From 40e09fe2949d2e3136c74a96fb722aa829caf3f3 Mon Sep 17 00:00:00 2001 From: amfl Date: Tue, 14 Jan 2020 14:29:23 +1300 Subject: [PATCH 01/63] bugfix: Correct reference to websocket room name When inside the onmessage function, `this` no longer refers to the websocket node. It needs to be accessed with `that`, which is where we stored it previously. --- src/nodes/network.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nodes/network.js b/src/nodes/network.js index 07bd72c12..12d2c52ab 100644 --- a/src/nodes/network.js +++ b/src/nodes/network.js @@ -86,7 +86,7 @@ this._ws.onmessage = function(e) { that.boxcolor = "#AFA"; var data = JSON.parse(e.data); - if (data.room && data.room != this.properties.room) { + if (data.room && data.room != that.properties.room) { return; } if (e.data.type == 1) { From d134ab73f0d1b6cbb89c77f6e01745855a99022b Mon Sep 17 00:00:00 2001 From: amfl Date: Tue, 14 Jan 2020 14:02:32 +1300 Subject: [PATCH 02/63] bugfix: Websocket checks parsed payload data We parse the JSON payload, but then it wasn't being referred to when we were checking the data type, which could lead to looking at the `type` attribute of a string. --- src/nodes/network.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nodes/network.js b/src/nodes/network.js index 12d2c52ab..0deef5c79 100644 --- a/src/nodes/network.js +++ b/src/nodes/network.js @@ -89,7 +89,7 @@ if (data.room && data.room != that.properties.room) { return; } - if (e.data.type == 1) { + if (data.type == 1) { if ( data.data.object_class && LiteGraph[data.data.object_class] @@ -105,7 +105,7 @@ that.triggerSlot(0, data.data); } } else { - that._last_received_data[e.data.channel || 0] = data.data; + that._last_received_data[data.channel || 0] = data.data; } }; this._ws.onerror = function(e) { From 811721bb2c3aaef041676dc3a4e67eec72493b0a Mon Sep 17 00:00:00 2001 From: Javi Agenjo Date: Wed, 4 Mar 2020 18:26:15 +0100 Subject: [PATCH 03/63] updated --- imgs/node_graph_example.png | Bin 87397 -> 94310 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/imgs/node_graph_example.png b/imgs/node_graph_example.png index 313af60c20ba388b5c032ca8e91fc18bb118c8c3..1ff51fac9a068395e8865ec872e6bb5fe1cb2ee9 100644 GIT binary patch literal 94310 zcmeEuXH=8h)-FXsq$(&7N<F z0qMO&dJQ!|fV-mZefBgQqrm@&oo*<~5#@eiodq8bnO|1DOhoiPoDy$%7<{I% zSJHMOBBDVP{v&R%%QPV(I(VggH*={J=(3b^yK`OW)F_b>2w}{f!eb;p?a-v2}IB0(i$3` z?;gpCoVpTPA1x~p!6P6y5t)56QvvfNqKV?v8(xH$PBHCgNlP`ZB8VvR1nOHwkt5U; zr7py?Iwv>J^@i8j>*|-^z8KyPzF2GFMG-~sv3i;d{#o*ol}H8Co8MP@nhjQZn5Bu) z>iEC%NOAWqvi;M1Vo=`v)aOPdrkJZb%3U(O0qX zdL|K}#dwcLS(O=c-cgN6S+xU$Y4Q%}QFc15fVrNM&=|?cfmQC|y_JrwQ<^;Vw2MI+ zo;ws|k|@G+Tn7cIe5~N8uTvcUp`Is}-oqu;h5iw@fg}W{CYrm_oiQm5{2flbL9eYMY?c3)kPjOuH3~xJdMlT(ITS@){ScxK6 z0i}PHvsfT^NKyNLAL)O0>HjxdnmRe4kzZBKQ#5(4?aI$kDZ<0R*hJAv`KTSlO%a~2 zyeKS-xP1!qCHHsCLMBRw_U}9<{zCob7}CRI%#Zx^&c;0Bc#`6)=`=CR@8LEzp}kDw z$oJTLIOOBM!X4k7)bv2GN37SbvYP&_`e4&d$zPrt*9a_9M2Njfmvw;d%U5 zNV!B!L-RVeLPY-h!*9qEe-)Vv60z~(4yUcTqBf0d0w_A(sVB_yO$msMug%Gc$lkbF zkArw5uN;FU|8zmWc@`znnO}L8*EyldG>d%Dnewa1F)x|*c6q%u#<&#^7Y+!>u#CM> zCF)(ABJjWDA{MUxYQJ)M1NAs+HpwVanrplM)1rbMl!yb186NX}xj(%7v8@okfO?)E zN!;(b*$ryRU*%<%ESQY8jpxztjT_=Tm&-p{zfgy4S|+JBWa zsSYy9e1V;v9A7G34r_rr9-dB`-=I{*m6iNFdxasM4|IRoPYpGZ(#t&B85*d1M$YH- zVCUCyAoYCa@-;!pUev}APeu)?jXV76(wNt%Q=4?hdG?i0kV@w!h`!r0y*33({pTN~ zf8VdQnhLdS(9U=kzI>5Z3v0#qhYTQ6Cw}lj7V^uN6NDm$_T(bLXWhNXcog zH23D**UD#$zSHxD-8FHQF3qwbAyYAZ!kQ8z_Mi{e!*_O)n0q<5bZKsGBRxd#tK;eO z?ccf|S%6p;ignq7M?7!Gafx0H4?n+k>>f{R2op-c&oQn4$7z-1=Z&L-;-8<_Qg}VF zGLF_=82H%CclVud_cy-@w5Mk>a%Z-2$O|_f)G-==(MG7$Z8-dN!! zL432=5KZRvtuf-Qid<6_0^^rF>J`bbol zH;9gq_j!ddB&R}Xrr^)O*{J#N}$< zK&-I&+a_LkDIX=Oy($2?`kBY9`Najz#6?r<-ds_r_j-iK!2q zfyrPX@gg4bm`?1lSND9z=vicLQ8WFoH~I40?`EbNOrwyVG|Sn3lT6jZ%YAtj>~+h1 zyK+-WNy)wq4di&uBMYXph&{Q@$>xwWW|;zWeV@IJv)r|2dF1UUB|SGE6tL#O09BWDwTx$Krk}ofw4GiNE!@Gm)pb+-Y96!adWT|b>2d{&4a;tghW01MHaPO`CVs6= zai0HPL8g^7%P#&I)^p)~Lt2vN0@UFY``h!gFj6=|D^IT+V)>3etUpQGvjk!PnW}Q7 zhOz^TrqjuOa0k!NvX_^Zbp^fC(Q(?-hW*c+e9r7(uAN>3NA_vY=HQ~PZ#){5{C1a;+{n1QDFq;99JA>Ek zJBt0MF|>Bir~Uu+a`oBLfNfI;q${Pw@4z1 z{LvSteX;%#Xkoi0p(sP;wSDA^2Be9!w zd~aSjg2`~lz`mWoX49aj(015r#A~fJbKx^jawyVWVUWJuDU-C&0guDZxR-4XSa%?B z(8ag@0^!Ze*@?Uc9e4o$J01M#Gqmh_Y~-W|5Jqs$k$blM^rDWcHGS zZ3CBj!F;Q}8g}~PoaWWgKA9#oK@Q4j|^Y$6>TUoQweRNpi~PL5_>U;A6Q#9=OiL|FPI6q~bV&wKhr&XZB2y(oo@M2U1}CaLLBgr! z)%EDK_^(-c6_C9T4_Tm}Y|}X~LGekYR1V{(U$Xd@>cqX8kB(GQT8BHSkZbFw6Kl@--la94V#*lI>i}3z}IgZb@v?&`+ra zn{9EC2ABB}J3IX`oLO?pyk?vD1F-TlKZw){`YczpsV*&tK|hs^ITAx*%NCYhPn$g5 zmOQ%Q#g@V@%%m#Bo&c_DO$kmgq3MXHw~+fLTvOm2IWE5Q2D9Ztn`KqYgrY}`1f(X! zQ3$#8jeltWEPVY+%~(yaAzOjGrL>lLLy#*^DEkXTX;%G9yTLZqXX%*~cIsK|oMVlM zI8~-eu<03g-EVL2ghJ&rLD)F=5i3vPB=%m!UejUlGbwVmD#j*sx4xBzkspD#_cJ&` zlA)Z=Tpw;*>+TzSf8@awUz{;bg~L-(@7f}5OqyY`$U6O4v5c&=pKC7eNU_GxJh31G z+DSvGQvWGMr05tYx6Iv(Bs1HjLfN&(iAId4qy}52l#_J&a)fz=LS@_N+50aqpB~V) zI50?dSECN}7gmI0%(v1+(Ky3H5S3$yt_7C|25&XU@9|_|(ZWdB{r0a8z7F!tVqKB& zL2Lxp-|zLtyBv`Pc3oCGCN&jVQnot3q^qnv3k@S$cjQEFYyrs5vbN%y4c;DWFZMwM zrb9H`KUDaEBtIygwgsYcs>$Br5mnxUmsqsMopr0zG|t1*qN%8f_n7eaW^J!PAh=ym zTcq$bFaVBULEkPmrguyY_>s!|>g?&$rMN*61Pw!x%qbh2c-qk zP(M~Rx*kWGZElDDLQzXeuZ*VGjU-*A*(lfwLAW&FBHpJNzSu+@LVR9XVCS~4p608k zl}>=^1hdI2dOLKO*UCobx0e*yd1huvy*y#^nIgdn67~dhRP|)y^R$G-A67T@2p#AxC171#Bp?3C7P7lI->@dWor0#GbT1PbZ_*BOVG$Ja@kD`#%;?%`G}WG7e#sGhHyTlH zf#ad#DD>a6k1S0o4{QjCGsM$D!w%1RtBIchDXXaBfA0f9%xUcBO9*1tmyamyrE z;OSy{Rdo!Zs%-Ug^HsgTYbh)TPse=SN53PUW{pd=L1hh2W)_}RMdJ-R>jj1ZCq62J zNE|_9M!Lv^*FcFUyHA8PhK>z8w^tQu&@!&O+Yt5mOI&6-YI--&rIF5%>txFrq=uqI z`4{3rH0>q~-IvU!E*uK=)vO8M$>a?5uE4K0cfdO;{65Q34SnO^2t<5!ke6cEAQr!9 zah>>awDVoYh7;2y4dHX)dlAZkf(klkGq(0Y5I47x4>o?{(q3_eg?b)j0Et?99oI&+nKBMn znB!sVQQYy>kIwF03({UQf6pC`*%q@xB~vyzaH6qQ)X#+dhy(&YIc2e$TqJ{B$Z_y+gWX$!Av{IMZXLwZM5Vr5iUMHjA4$g~6fDx1J;0sBu|A-EE2Jki_{|3m2k-kcX%SqfTL(3d7Sb?;z;Y$jO0 zI-ob(%>w$bsy%Za%cC|PU)=#n^aiDHO) z`?go8Q?tpbrw=7~1oZpQHVc2g&Wsv;Ym;?1-8|n)AFo4ftA^X4K;Np|U6Sfqc;%=6 z3HCZ66v}*=sS3Hb<6%x_(^dEUk%lVs1Z8>diB~EHMQ4{3O^AxE&&-QXo_-TC5DlpW z#o}d_Z3=A>E4(`whO!$JAFnPVtJvzt(xvE2Q{0XX4^~jM>~&~@#l^J=4YT$y)jJ_)4<8B|2?#pW6=TUwavC_sninS zlP4A7z$VpX+ZsOak#l=_!`YJ?y;x-l+L#{@SA^kM}C0E#1Pc zi=Rf>{i2HoL9|I0^kh(B3zIV6d4SxWjBb=QzQGiuTF0To7BY;vlSg$^ZgGxH(8%S-gv*kJ^?U5K}7P?tLgROib53{AQ5t*w8_-&s}H}Q zba=hTUjK!5H9FgJdEK9PVTmf*WX2%dtL;cy5Vf=oOn-yhgW^#rl$;9MLOU^8kYg~g zZrNeYzGARq^J%!pbO|QR6KFz$M+ph}vzm}NJdnVc`;#XL1~5oi+JORy`Ev24Tacj5 zP2OvwMOy}^&+SyyE+h|q?CgBc;ymQ|YC2USwa7y4<;@*-8J3n?ha-bxgJ1CLT1|aP zY|hPS^bcw_tT0$va9gEj6@Pc0wwX>BUjh(y7IGQkjp1CR6vs67u$R_9{O5>OPT)hb<&O z@c6DLwB`9aG-t1=leNJxrN?_|lD5!$szJ%Ur@K>r@{An{$cH_=3O`t}n@2APWAiVQ zQb<3{rQo6`&v%&X;_X@QAKShjN8L3-LzS{ec}bCzzmZS&2Tos(<5o*(obpXuH?a?| z@h01XZ)sN|dT&AgbmS>6k2`+CMk_b2Ej_uxOFJxzylBD4G2L#gSRYQs`eJ-CH)b(X z7VmeS9VAlDN;9hz`FcW^1dEVeIIM=} zZy?RzQJt2Qm>|X{j`~s8JgPhjQBcIP94eWb`}!%2nZJHdZ?3L&u5M{PL}p^HJIidO z%;WYwCfVBQrL1F&CMQ#VJ2ERg!>g0)0a@6r@E=tMfdv8A{ayxBYsew3&Xe`D!tft! zw7)iElCkg|rksfrUtdTK3I}hb*AgBWn^SiP=BkoPh^LuqfDOGGqK-;V>yoRdmAN*) zuh_Od9{0vg^%V!fpZsbOGT+^_TTP@Y4B*~=9mk7(#TA=c81!?~v?R-n>J&RZ5=GWN z5vX92cCwm>QA>qdTM*deTs7^p*Sr?Z@H5hh?q`nbH9YM7U_UQPT86CizCell7O9>F z<-BM!P&*g3i55i`WmMt(etm+*cxSob)raQySH8M3$*fn24&sjW!6DkV0%O_Q`DqHG z=J={MnS%#mY*Hs@ZP;X)Kjn!)ba|l9Aj!GM>S>qLhDuzrB<#mzYr)2+xw%}#oVc)_ zq(-${ckpbvIsl6<<<<9G6kYpzUBCr96>q07MLVYZGU1M;zzUnl*}s=dmX@(#(0G=*3U@obBk^5sq$H&PzYT7 zp!Fj15adoDPpnuiaXTr%?4Tc}XuS^hw#>GchBa*?X%39<_i=v`h&JmEk-vfI1le!u zr$uZ7G<>8prvB~ith+X6J-3!b0b1g){zVb*^R4liYq=Q_#%*uMCEs?qB+e98a(5FH zJTc6|W?g(*1C*l3ehmzxFhHq<&A%kfFgr7Pxf!}z`MNO<+M8}1=~h~_u6}s9c#8+P zZSHWIY45!$(y8L;O2Itc{pQynwz27E)iq}xu)>QZHJQI~%=tp?D4NVgk%Fklc^AN2 z9l(eO=C$AkBnB}C_@$Eh+{|%*uj*|#&o+R3Wrb0lnm9!t#F3e$l@}J+>j2(%qgzXp zvkcS`x>LuDvEDs=&V=1 z_PsMCEzP-9wbEZtDIMb8LmOFsirdW_iV{IutS3skoD)jT%qmy0RR6K1p%9rzRc^!U zv%)Pm+w_c4_c%S~2YevIc}3!J#G%k!j}k`5UXma}2IfS`oS)_|Ab+XoiGYHy`98>< zbk-){FhCQeV#;I=w%d-ueB6c{6r;D(>uJXvFGIeu8Oetpf|$Y%SK?PGt@mNXEdY9F z%Q2sau`-xF8&p^4FloG4e^6SV%kIAaZ1B#V_IGmOaeGv3NrJU4NgR=70T3BC1$CB7 zZZW;S=jskc>Wstvp^sq`L|d1K&Odg9-g^9qX5-coM8Wu+*ugVVYm~`Wb#!Y>Y<{13 zTy@j)^QB9V5Hb&_z$GP86F*KgMMP~uMSBGe0m-&Bq5xc-&>OM}D98!bv4{F>2FS*h zVZ?mS?bqRmdW&mL{N(p2jGU-X=`F78%;)7#(GCIJ5_0lLPL4dyRu{|NVC?-8XI|?i zuN3na;Mwg*+S3aG%JF5LG3NVEXWR7Hpu%oZ2P>XtTp@G)Elgi%dGkUi(AY>7=S?>42|TuQ29AW>;^9}#xJ;Sz0$OM#u(E*o$OQ5oSiz|F6!WZA=pkqZB!W9 z9w8XO>=J!`@o;Few?OR{tM2uUTwcZ!vo#CfoX>ALRY$!2=%_#PKssJ4ksca@mUwsBL*_SH z!Nwd<6e}b0;yrsI$~)zltqgh0;TYPg3u_#2cyD@%%fLKMh-RaM5wB?h%PQjG;vrJB zt+CY6@)B2O-j>uwVX&feihD{{SDCbZ1uZ{7PRkxzRpcF2yi4_lp5QQ2BZUDvjuLgZ zbe_ICFOndf!-JyIN>bMA!tug^WAGoKZ?z@xeOLUoitMRPUzz(s;~fs%m)8y+f?M_t zJKPr6pKt033Jq9t(4cZky?|8i>kBC8sWo#JSuU~)*sMush4)jAA4hQpE-4_c)n0Yz ztHhxpk3x#iG8RBvCK8-X#VTmgerjzYzwNaekw0SD`w;_P*G7Nf)cL5g>=Av&O?H;POiqTwpRF&M4V5xiY|HA0s3$16Mz|3b?@W;T7umz%(lKcL<(Og=!5&M@x) z_9u%<_nzG;aw=+@(o(GHnOolz4`Q#5SJb;#+U~mP+sOugNo?&r+;+zx#J~)Wbmp0Ijm1toa z1k};pQS~@KjepV? zAOx+xGFg?Aady!cQu(0WO);N^K=F0}uQ{__alQ9|8Qdm{q~&;FH+B|9VFwg6*GCkI zUlsW~9pAOiF|tQ2Cu3!Nrw?*+x< zBrvGQ>VFj~nRcgW28}0k)YF1o{sdj>@&N&VxAKw{MLxdq1nkfcg#swfd-JQayjHwM zBO;gc&2l0V;0MNC`HgiQ54vz?M9#tgD)Wp|^k+Fave6JL1?5pom=QvDIRyu%Et4sU zl&W1R$7Iy2Yg%byqN#t#DgXw7xCpyaWT#WK8h$swM+q!d=8@xJ_YzVWpiq}lpO^Nd zu*t?MPhHW|wMbmGGW(M7Ij$68z^z=?35aW1TBC9duO^SiGVTrbX4JiZTmyjiBvFGi zm9Wm;o`mA_vbC+b+$-{KZ@;b=NY%s+|4DdOYfysxG6dpf-4nB}Oif*BU*Dty%;s%| z_Nqxp#pAD$u`olqbxyVAk@9qFCNetI$r#xs$+-iuISbF(2TSAUryD!aidyvBDir0VYgX8E5S+mV1A{nZV?Q1-x71J}Nw% z-Zg8BwIkHG^&Eb`x&d4wMg5?THaH%gk%Tz6v0WK2W*3JJqE&2&7a>BmAOw7Ab0XD) z`YDS8^8nOQ9}dj}H01BzVnYRZJU95Zbx*eF@%3~3It5WE`J=x&J7ueO@hhAi7af7D zV@2)wTC3RT6|oybmu%SJ81A@2Ab%tmfwP4|5qk_*l|gX*!@cHqudo}RxIGnAiBJAv zn9wPzv5BX0D1Eu~iJO0;>xO(CX9A9oc!)~|zarm*CsrZKH>!IaM`6%D=4|(J7!^)R z-vu~i1x*G@m2WTW^WZ<<%B(FZXI*xxCD!`l0|`D7#K~eNuIiU=!JyK^{QXMzgW{oK z8P?xcYPtaekN`3_kmPr82?9d7@TypsLhZDW?NCu;!nw!KV2E`KQ6zK9a+sMILARB5 znO2aR9;9N4iHUM5rdy*&yKtiXf8z?d-i$jJ9VVxHru+=#TNbdAwgbcCjAcao8OcbaR;W9GH`6)H z3u*Fvt2|bR4Rq&#*ZgN68Co}PgQ7;(6 zSV%MI7hBsJHK=v6K8&q-r0>Kl9$~Al%o)X#I{Fshs)!~e{VBfXrYAivpAfp@W72^*>U@gN5E=(HP z56F1TS7*+98iMXlLUAvD4Va$_e;u3`#RI-g7f)j zmvIwu{Fevk?51C2IiWBI5icVlS>DEkLa{&@64Su$$v2RclQB)?pyv(z%Ac$O1(A8h zlUkqW@HY+zG$o{OKI(^e^c7IE_7yp#Z?85BXk*OR074dbj8oy3C6@2he#7>pZOeB+ z@K5>3ta2vSTF2|JnTPU?&Kd92h>-ZVHe>Babl)32Dn@Lav)*~2=d<#CB zn7U|CYSH*r_1<3s12{BMBX(cG1plBojp1;Mmy}84)0v9EY10l^wWn~g ziv;-PeEV@Zr<(l`>|U_mtI5@;72%TEUC2M1WZV$; ze*WyWBQP(YioLd}EuWq8S>dr{TQ+ze*ArZ;CWm$n$rEwEh?Dko?Zw)Ev<#^2ZItZhBpA6RkEE1TYXAj(TG`^v zOfd2+rsw*v{o*XHov04g>rH(n$uI?gEl;**Gm_InjEh)GThkJsw0y3Vw$!ee^O+yj zX`Ay}7%3klz;hAXq_BrWh{w;!MNAA<$SyFO&>ZN~+}B3bKiF?>jUW!-{Z?>sMzsmk7dx6d zKd@In-F|aUZjp8X>g}3JmPT!9P^CM=-r{$6#%@S#-jAY?jui4i`_#>3^KXZce1n_l z!kM6aX`kYT?9!qzU3JYkVkA@PM$i59-Ivt49=6^zI$Gee(*LcOEjR}svT#^xpK)Rd zEh@GfF6q`OaSpM`j=&BU+OT*UfAW~JOMV~MhOa!cyS9PXEp|L3q;%emRQZu+ z6;5(;RVZ}vp67_mMhCm$?@3!d+)v!;&1iY%ZJ5pjei3I#%58_ospY#ZJ2=gC9d=ru zd18hROq>dnS`zWWW1!lJAannPlfN2F+dQM?$s%GzM$7)7&~B7H_JUQmsVH)uKRZ8; zXZB!o-@iON$rvb1rhwW{7-($rcH-}AX?_s0Xiu?lpT2ppm66?&z4vK**)|L!Q!%b8 zY}NvMa`M7sL_Ww@Ok9_SENNK9=~Q0?m=7I^8t?B1gpz`c9?+hizsK0ilAA-&4I$dN zfdvk7MSn4IB@m=SOHskK7IZs|NX)iKHU-#BkV+7$3mHVk}V)PH0|JlcZ_gn zWJ5bM)Waw0+AlEacTos$7~xJ<375|tu3K}5KwZGz^x+d(zHRa-cy``k6R)DF6EY1b;C^9p&T zFEd2ClaFA5oJgt9+XXt&fApUL(mTNFF#{B0ixUk)5D$~lLy#6R6UB*q^>$mM!#1L- zUk?)Z#WJ*|$&+UDJ-0Kr)Pn$w{**D{&8azQ%E#jYTQrruP1p8EBPu11i~1Ub7A+FR z@W)(YN+AT7Jk4#84!4HO>@)3I)YNHlul8U8J{E?oy1Wbxw8xaE$K1vNjf5Cb@LGx# zcPT=>8Ki?kjYD>6t3Do#crM2_M+?Nejd<`tQdfHJ+rGus1O$H$02*|zGt4v1dH_o$8Y*QzfW?6QLZS;TdQYDx?L1h-OG#;$r#VOKl`mbS=TS4J zhQ2p%>+C3huSa_kX>teC8-ue=r52?WLOOq7IBcO7MLT3*NMyJPQ$bOsP}_FRh>qDV z97>XZ&%$kau*xLUJEQ5Z&*Pz4LYidfX|W@J=g#VZ+Fn~mT0P26E-c9wAN8QUF(g_K z5z@*z@m?L8FVubz-g*9-TUD^{=-!gsREOfH_knFwe&jofHb-owh$3rJbp4>whD04- zPTia?ls~e;J_{1Cdk?3s6k}s$sfaPQOSOg2^ZE?T`_g-2>iGyp{D-g+_~5q=s$^Ke zW(ja__@@q@m4i9i-wqb{Mwd{4&43NL`a)#RP@3T9hD%-dN=vh;)(_$Z+YXEq(!L#D zp0AT$t_{(;n{N)<9QZm*x5^h>i3X?&v1lbgw2aSaw2jgn`qUCs&q)7U{STtB8aCA- zw2kStAB$Ug|I7YWiJ1=Z~ z;F7c;@@d4wdi}ikCpKU6$rZ1mZ=WMu31rvw9xNNUGM8gan~G>6K`uIZQmhVCFY+OW zKhBR-{)k}I)4&rGU>ihhpURi{(96?dNsk0_8CjeTJX>R7)Ob3O-{=SiD6P{tP)q~C z9AQjzxTf=YSA@5Wj&M;2)wU~9Mbv62`x-_v$!+iNE-T8P!yb(W`s=Im7X-f@mlqH` zL@zHW$VG1u%pGV&U`smMRhJ;Q-}+(E?{~l-q)!VK>yQlEn(Pu&ABY1>3JRg;QP`^2 zElqkWh~P&Dg^SW&dOCs$qA*%Wf?v{0lb6oN{HdJ_P)wl1WWao_Y8@#R5gj`y;}#2G8i2sBMQo$jc);8=tY1FE834sAo(!_q2AisZ{rK z_B?F5h-$uZCnU|x<|C5C9Ue-PTN-3?E(xz+J0x<*!Kd1rEV6OT9aGE^r(=d&8Sl`z zA7ee_596Rl7#J2A7JiO0UWLTfk{LL%s5tt6wuND-mw0Cqbw8LWJ6|w#WL0_Q|Jl*v zE9<53py|~WFLo-aq+X1QR1(+(yiRA>h`!LGyI%XnMe#d0mO;0*;JY4Hubw}eJ)qgo z_s`bTENM=38!??~(XNS;1`7Y_mgN2Q>8@SQT=gMJELsq;V@t5WOP z?kD`4$?Q9tC6wh&<5hk)vu8>D$*7-t=!C1 zQsh?9Xl&_n>7Z2V6Y3tciC|Y%s(xWdV=5UHa?-WhF}s1->55OMi#9D)Mooo{#Q|&Z z3G_Q6?@^~yCmE^s>i3UO*-eHfQ6fe&1(h_YSWR9m2qCsF`Q3eBQ08}^<^k!{X&{6g z`U+`vzm2|mV6&5@a8YlhagO1YCVp1k=kZcdyx8&^Aun}lm%?YCI@3)p%e`mbWXw~0 zgzX+#(kxnjgKZA``>tl!FoAIFJk5Fqe3a`*7iVAO$SeTBipTm-syrC;ObREhrwu$5 z7_0(?h^2g2V!YPet=T1UXmt#vorFmor!GLleJ3CyOx4pOQV+Ui1+BKxw zQY%}ge;tl2x*Q*|^rX>8hjGY7;X76R$I9WSS@D?S8Q4XUbyraOqD)AX`RGjD1}@a` z4VkX+T$Dm4nTK5c(?UNe+e*vnXyeCU&%vVD$mq;yw#1oAd2hbHl_N}O1aqEg&vc_L zUnzp7i9vlZ2*ogSL2p@<{-3+RzenK#2~cq70LVs=049Q)&j4kXfqZ#_@n@~UUY2KR zevsL~MVkt=JRR?eh8$@=9>}eX{(*v7DNV=8u(Gi7d^-+lTW95}dMB~#1Tg0tyJ;a$ho<4)|DgD@UF5!; zc8fq4F-{2Y1xM7a6aU7|{Baw9K)=BCe*x8=H6Vf?hx`u1zJ2dvvj2cI_)?8Snk$oR zSbuUp-P?!(#+E~>WuRH%X{f-h9}Q31^8pE0mre&oG@niPO!cP5r7Vi<{!p|2`){Vh zHu=Pc=<}iDGt%#6%sFmxeiRU5@V);9C@M35bkct430Zxy%G8Ts?<5B>@SmIgQTi&G zuM;shF}(nZJpmACf|wxkBy^JMGRmB!q3uGJVkVG6!03x?i9RLB6Qot&+v>l)iA+Ru z^pbCKI{Z-JU;is?t!(xE*jo)jchBv>T;?hf$K5+6;s4mEHyQaXZHK~GKszwuxd(9$ z%@PEO+xNzN#SuwO_8=W7KnitQFBGJjU9P9KZ+@N>GIgcRbfNG41GLsGyA^&QUNc?c zYTG%(IY+C;v<|tplCy#XuL=E*42-QI&IJw2pGd1OtfgY>Kx58t&Dg+31~9#>UAT{8 z1Hc}4mMk^}wh29C7G8okB!Hgyd#@IUHt5wlq?P_xRGSNUJ5Y;Eft0iSALuw?9Kgj@ z`T{kZGb@5Z<** z))9ngnyv6Xx;o}xUj@2AK-72- z?KbLd-jl73wrUFfvjYGEE+FHyNHqH8um@lh+M%QM=UFafY3_G*=d)pgJS$%+nny#(9xyg(pD6U zSlYqj>IZN{|KT~%K_S@|RhzS&4MW~r!@6C<$Z+rd7}Mpi2W;DU;qMTDumY0f^W49K zGlbC}f0E?=&7g`1j68ZH$#W^>9W}J0qoewwSIFLhdnMslzn4noPwX-YVuq|H1j(Ph z9%#A;3e4Z+c)xdnX(71d=qE|Uv_$SLm(24!0nty7Z+t6|^R%$ol^GV=4Az4+$`3m9 znnzw8G#lfKKI;;_aEKEkY)A?Zt4!$n(y$cdI5lbT{88?wrBjG=|Ks&$I8(8|x9hgC zq8ZK}TYlM}>?${Cs~dTu(j!obHv;{X871|0o!zQ0%C8@|;gX?d8~m2i$eBfX9UxH3 zI;RzNC*{xRXX3y&Meodg}jB)rTe z%l>lVsjdU(rUyF)^rcA1MyS4e!q`<_;D~=Bfd7V(0X=#7o~NC5t0P+ zig{Ug(Hjs-gmRqMdOa8v--`8ggYP~bf)weXKWSIt930-x-h2$8_n&+!VUENfO!{x* zf3~+FTWJt&J?-?-JR=+d)g?W=f*%3oRZD?oXNlQHK`T2XzwaXCJ)r80SqRMJp999f zH$NtQ>yfF2^MDz3GH69Muh^M8-?QLHPy}DvTkj;4G`f;P$o6#NCxKbz0t8l=GEnwc zn$o`w2B?8vUt&@f(0uup3R<;h4^SS{R(2dH^jfR^$vMlj>ejr(E0-#Qvm;A^=GUk; z_?Q)5=+y60-+ynU(Cku0drYl_J&!H346>})dG@&VW)<*&EfwCoX$o(0(609Ie32Wk zk^!{tYCxb^!(mo<{J&_x|J$)1CHVsQWk`h|DLG;k9TcJgaH|uRHf)HqLw8sDFXS^{ ze84ogayu%CYNH|&J3m_KW3pr4+Ex1c>(ef!#p^$shCy$7ItqU%=Vs_H#~RVGg-L|Q z*$fe;AGj{`9itxpMm73r6v@I2PdI@@v_pa~VU#$^W4imBe0H;Wilyz@?#ZRyQf z3P6&BnM!jJ&Qz~C6^ZT46;1G_3-;*UhPJL~#Y@9JU-@NTZAnhe= zExl+U?d$iYf#Fq!g4rl*qVw0(1Gw+j&^-`B7O8dNy3=fq{v5req!@(QcRdBvCL$Fv zpr8G>c~b~Us$=M;j}PX6EpF2Wq3Myuyv1I+R__61}cP#vRYQ^8plR{;o%|Auk0TOj@^;H_i&&(dLd#QO)KCmQ|l5g&($u+CmxFqr`25p%$;YZ~K_``hM#bgRxx4cSog*Ao>HDPAlM09rvs z>C=szR#c?xO2Y>>lp{+d*Cx$t`2vp+f8 zzurIT8XO1>{Bg?E`DEu>4Zprrj(GE0L8Uw}^n3MfAuB9uaq4Tn*ZPeOkmAJxNWO~+ zz5*oo3R4L!9KMEENTUXkkW?TW(^Tt|xD{SDPx=S*KaxX_EbuL!_}+WSDKhkN?D&tS z!!y5*X@tJ7btMPxtCsBG{)a+@H;|uS5XM{4QCqwqo9}B|tzIA3hUZ4p=2=u-ANb65 zTzq8a!neX@Fzt$?--i&xs4TQ%E=xO=s{hSAsew4ybY@Ag`6pXaw{} zi=E6(CIV@pw*2}PI;NY!jC$@I14%YJ_AT}cknj4}|K=iPV%K~}PKu0VsK(dx(;n_z z@ga;k0`pZCR)Omz$mbLv43|9D%_!BN!X@7yIcu+nNW)6Gubi@;Il{UN3W(I#_(8i$ zQJ{3n5%QXu%>OhTjQeo>Ek(zFGhq*kP7jA$Z7%d1(?H=vhpE1PFm3MJSep<=7S5ta zmsS;kATN=Q`V=){T}QqF3sRR`-pIv|y=lhA@o=ihtFfiYOSREK8mhs$jlZLC|CpF` zT3s)o_Lp;`tnhq5SEg%>)46Cj(qgst)C}K~r$1`Cv+}7t8%PAN^IRcKxx5dc5;p)w zvb!_y0u16URq|l-^b(Hk^riz z`=&egW%db0-o8tiTq*@fYp)y+Yh_aO&>P4r@@WFf6bq!Q;@#->rT?C!Y)b@0;7-&^`a}S!F?738I z5#LAK<2m~(x&=sSOM6n8HkXE3Y)Iz~;-mR>g}}h6w*z`^RUWgv^|Tv+jOGJvFJp@2 zbCc*@nqr$l!muOdPL~WtJi78RIgOKOKU?`NXGe+1+EXf zjiQfohY&hep*`wTOs=?h!e&q)0~)>6w5 zNF1_@v&cW?@I9SMLg>>dDgpZ5{O#1U z*XEfeoijaVQiIZVg_oL(^7?!2q>HTKlpK(qrN*_rf6>zmg2p?Gj?+DT-K5fwhBoIe zzkU+Rh!{5@kwB9*;tZLlz+Sz5kt#+TNFy9kBU`riMe5knih4}BmcF;BP+Ux6l##gO zH>GYw)yV5oJ^^t7pg!5#-O58SON9b&HhmX7aod3XpgQMEHQ5`Ox%F81RbKuW++lQ! z;l91y9SqO?UkrEybydOJ2HMxOXuL+DRrlM*C}O-#o_hTjd>iRq+JKuL#dzJl`H)H5r(8 zwvM=$uLY_AYxf?g$n`Gj^#Ws2{>K{2H>p%Ui01tiKT1IwV;*f>3e#aSvt%h#V6Mb8 zgii}0QSKg>zFdzDg-$fAJvPBr4HY}J_UikzLZqN#wnH%aKq|v6%+CjbS@TA-JWgXE zFPkFXGQE_SU9VpF%K@i*^?}qZRxOFNmna)-cqlFF!vl?LoDG?&&$L?M94~VY1^2k-qQ^Wdr+jCd!3hS;)s>4 zHzc9b?dAA6BQ6I}-tU6wnAnX}J~7qIot);A;GeN1&DTE#kEn1+-L7dNJ`A!toEE%x zoWFmAV%y`HdJ~((TSW_B%-03rfRKMiVl%QFEC8jWT2!~M4Y2PTkA8ib!p*-N2xF+L zlp@P*jmEN|vJ_NNgx#|fB@Z<&+I$N*e5SAiN4^SE(*CI4IzN+ddM~swv;t~PK$}z4 z@p!dvO~?tXHJ4(%-d1;zC8Q(r+uXgD<&U75G@GpfsF8ouf@-3Qt$6svyz*I1?7qgU zMXa*)&cKRWRH)2OKLl7#4Pbh|wS7VxPYz?j4LLIAAf~=p^(uVW9u+{Kdj+3~hI!&DNBD@$rF5 z0dxAAvD}un>)5-Tt%15n7RPlKHTJgrZZZ0|-JOD%hyQMTh&L5)nG6271(A%3F`Y;+ zK}_ueTtRTsMLJ$dxQ%xlkE}eK9X*^~ab1(JeDau{1V25@xbRb)-^qE@%$fA!gQ;yX z1eaGpd`mX3iT+dH(SAp$-3Bb^;*sE7mZ58Y#X>HDa%@ji$#A#i*WF*DFZsldCd9`O z+2mJCw>I56wtc<<-8!(|q1@CHz(gC)-#B4co@3WryDtz+W}@AV&|^83$EaR{bQB3` z0;1WjQ8?(wkLm*#WJ?LO8bS~)Zi5h{fm^1~`Y_qin6@PIqn8Rs(Y1H})sS@sWuqYq zkKLDXqw7EcmoWZidbvfSmFY556c<)` zRM8_(X2QKThE{J?`i!dyUi=nwm}LxVr&Hu~?jEG~QHZ?z^l(ZS!nfFf_1oP0q}Tw1 zTiCmd;Cg2Qb`FU_z%5K3Qz5>{5u0_m8_^AvKid~BOISX-;FZ3)BaZyhRHZ&ZEp9)E^bsy!R}Ph?j$Ecs*;cAQ0a zy3_rY4G>JK&FwWIHrM4@c4vK9jwgsdyg&xwUylK$+k1y~YegeW!gh1GjzpD|%U$H!qGITz@klvK|v7pzYMe@qlT_W{KTEcH;KMrlT{o@P`7( zF37@5KpBPbs@gC*!u~Qf~AU@&KO9X`Fel{VOZ3K$h9EI8K{eN0OJUF5x2Si z3HJ)L+KCSrTLI)pKhc^W=DYH)q3a0^zr)I!#tH`%Q23~J{C0iB3u%|b*?`5))ECdi zwcF4*v}#%}DjIL^c33SaK3w3&Et&*lNJ3@CwQw;R6LSm7Ser_rrD?TdfhQ{hHz}z6 zA}$N&wDCDtY=S55;UD(27u}+hm;>|$AO2+xxN@_?)$Ao6L+9_dJarfz3g*Q6a6z?% zaADLSYVlgTMB+Fx17V<4-?yTD!T!}50_Be9(HeEE^Lw%DBG@KjM7dq%Y*^|UV$bB% zwb;`d(qvQX`Rc`LW&6Yy9X@mCBKLHJNvxnv*@lw{HNce2BCa<^sPsx8R=&w*_9{-~?6)L5+Rzo3xf#-==;+50Un4opkOhS|3_N6Ml^J z0kdSDk;+Oz|9-er7i{Hfbra{6D>w>EOlm@FDcCG4{u7QFR7eC)iW1%>=J|*30w6ny$p_!Wm=T;czl=^^MFf0T^`o}Y-HEhg z5N`T0M$Az`efq%t$Qr#7y*|B(iGEjqhD5qLy_5_-WP3A)e$j}naF#_DQjS^XIOyBS_y?~Wb zG(|nk;Ef~YfFmu{6A63-asc4WGSY!JL`nXJ{^yKy0JBKE0Jn2e>b8l;(Jyy zKjMG-;6oKzWm6zA@DwIbL-lbWPH%)Fia$b17C0Cw;$; zO?m_rLH{_96zrx=QVxWQ28bR7dy#ag4w}Npm-&4{5~J-nAA=>;n)Gab!ak8Z0!x9y zs3D+cYWXgdOVc~N5-$=AwlM7?LN9@6N_FpUri5XgYqCKt8&HDoY~h_l3Tiz-3UTG; zWDx$UFv=weGS`o-=PX}Ta#^YCI7&0+ddo-+%i{;6+lN4V`wj2t_z?$6gG)+B1V}!G z(1KC+94?2v2X)$}>#i%58Hf=!FhN(lK zHFYV}S@-BgplafbLu{|up(%iM!+zh?rFqfiHNc0AJ>iMH6qCPOasS+ROooc4lA^E; zpZ-QM0Lhyr0fiUX!MB+FPivLrdlS-i-=?%-dC37*-@W%Fz|o^>6O^((I85MuG~Pzn zMqMSN>3{nhL0k{|pK$p=Y!9|fmm~4=k{Q?7SU_rkD<}e!{SLK}aNE(ZhlQPI`%ZHN zazW2pt9);GM`pfnkD^2Gi2pXBlDCn&$1BA=DC7n3(;VE|nP@gB3>+K8WH*+WELJFV zv`WY}(t-1qp%$;35&aKfe|O0@)-NE2)B=pt=kGTxR;Bw^{V6T1Bk;eezJ|@S1JPbmy-t_kms-dSd zLm#88q@?697c1lXfcn9s%{zy71@{aM2tN2;n|w~umOCRt5(l)WWQ{aZm_0#^H^xZn^OJ?aD?0C8^)!GM4ZbPsL; zm*4R1%ciyVpdzFp$Hq=z?4&+k78#Ol_Cwx#h~H&k*4vL$z@2spUG933HII)LdN8lw zNz;h*TDt`K2`DXY0NBUOC;B1K#!*v!4S&l7)X5O>!?Ps&qdW^^H~eX~Cl|7G1U1ho z3;f08V~NNE-HBoB!UeU3hjS+6O=wIu0EKL;NUxlBpPe(bE*L!`jOw>-bIlUo z<(WWF6WEqNRE*`$0A_;Pd3PYTYFS^A!1`^U!?>;N;)XGs&2F_Z&cEER?ob*ylQRH{ z!_E(z>w;3KzWoKY3+Ia^E(c z*y`9Q1z%bjHul!_?_0F+Y}^I7 zEk6dd9%&vI2MSiZ2@ig|aRXgbz_V~7|E|uVgip<}@&oVjSND*yk6^0|oh=?$5#DN8 zebs>7V$i;A#=C^WSo_$b2LazaK|c;uq29a3P|?E{PNc>kh}`a|Y=7fPDw@%ESLdKDkz3$1Gn3yb` zVe{>LElw?n$rxe28_u=ltKY@I7fzP^LUgbdZpzQS)9iuhK`dZ!W7yfa&NIJ?d||Bo z^#Y#6HdvR{ep9*Tl-Ly<`@A@k*f$)FxIosekP}2^4#ll(PpX_dt29}gR6PX61+?-u zdnf?RXmB8`VZm(tUGOuRus`OJJ;;jD*XMoS@VC!Cd`hF+-esMD9^xSM>A%v^G}n;X z9Gs|8QWExj(0jD`**2d&&Mx*xBX#>Xfe-l<;>VdIv1t85)FX?OGglJI39=i_kY-lk ztC)<{42^rCOtqAx z)avxezxx3&xi`>MLlz~wnpRufHl~Z04m=DQX7tFbBziaH4E5Vj_WBV z%$nk4VOCEy%W5CBmvQEI%oZ>5+1H)RyGX(ZdwvkfS~H3r0{)2Y5kw(l{m{^xkT484 z6R|>=zQSV`QK0XYAWd^B9k2Px{AB^Tc1|W|uJ3kG3St)o#@*z5HUQx`M&3>%fqM1* z_Hu41`c&?JH8W;4Tvo$GXOj@ivabYCmLby{$OsE=ApLrD&ZmsML~ zGVQD%FqsB*U>09>rt1ceoVcfd>l+dO2xP&4Pl~mC5hZXr%`U6EVD`}}M0Opf!4>wn zj65RC+()-et&5kN8t~d6Siz;tW8)3BdL})@!DOrn`O9~zbs-P}70soAGm3*tyNc~J zB^#D%ryC-&D=mASx}qiU0j^6esV{N1a<_Em+&==x5O^|QKUNe}xLz>q5kvCPMvAmE z(R>zlUJeHi`EO!b&ufUou@s@ZG4>;sd$Yl2c|uZ#C4tN(P(nPVE_3iRD?9La5v zPn@QJY;XTez=Xo{ret6r@DuO2Z3zd`n%xid%AcQq88P><|#aT>xx(;Kq@9tyC{|nUASjMYOYV1Kv^p&TetR z^M350XG2PCS#KbVNK4 z*kh5M-;`8dbN)%OQkrI!Iv*&F_t&*hV3)wW4W{ZB3O2u<$g4}%6okR{P2Y*p##D!~0cd$w{xDCRfIOyfioc61b0v0W! z0CG3?rG6GuuW}1gz(Bx3104qu(#t_8_kiMOQLZ=ETdHK?>lx#o95Xe@iTY$V*(rr( zzw>Xg`kl1bNdib9ijM%bQ!MR{2gln-)wYhUVWK6WoBz}PDR@(!sUPONxlPdxKjZm;#z(zgX&7s~BD`|KtdCFY1X zK34_lC>OGvw7+0>uEAsWj+v|kme$ZuKl+i?1fX@@_yjEJ7PsRvqdS(pm-UXc3?&Hl z*l;V4s)(3b27G3#(wgT-C;%S+g?&Vcjwr=H{K z+#C5n;uDU&Oj3#Yph1Ii08uu{oi>}KRW<%dtNK0k`j73tnv+o3NCOV2&XwLzhCXIK ztChB@r~y#gHX6$(3JUYQiZ^LOaHd66+f^gGjv$z;i1BT^`0aSGN`xbPb8HH#1*(sL z-Qd(viF35%@mSfV^XLMs%Vcws@zS$XqSfdG-8sK{{<)1guKumDu`d6DdtXMIp|si2JZiQxy_$bR!Gn2P{BsKPp{2A zQch4&Uz%(lywZfNM#PrC1RGQY?9SN_M!6SjWH&?lY2xAJW5<^cJ@vHyY?HRUrV``L zcEPcTn7aDT^l9UU*lXE=gP^2h9;gi$SZW?nyaT#m^hK4T7>2U9Rushs#~bMHi(bjU z)3#FO%?S@z0XYd+ucnDfc?b--meClhieR-`EEtb{I=fe_3#NPy$>&~!fzxQ<+LUu@ zg28%E-v1beiU%)jE5K|7%8l zTAa4$y&$XRg_KGz1WlH4)7WH^Zcr%<)npH}^PnIoF+hPf=$wYz>gr}cXrx{o+59%< zN4Kuw6ILB+b6ffD=+-P)TIf%Huh)jIJgY1g`3f5ArT_(;;YLiRZS^SQv}&Urxk1J( z{T>^|GOsWm8Be@w`+9Zyljp7bjaq4O8|%jP&5cm`SC2U?V&C_4FsEUx}1ZDX4k=p2~|DVQ&t)k&DH=N{01f0Ztn; z%a-=xyZhupMqf=NoK2bdaE@lsT^mfe2OU2Xjv)!nyiN>*FEZsD?2GP(N7;b&nWJV| zxVPa8M_v7tD^r7i1y!WjN3fF+Cf_t*E`3~btp%oI!FDF$hXy48h6_-XO~b?C`Awvj z>#BXhmSn1hRyS0BY47N2`n49N; zKkrnMLRxL|3<`qZjYH}kxv7=YC-~RbO6(O=70VU0c#G$<>M)_Qx^z|0V9PA?nV>@* zzB`Q$YHFI%A|}Z|8(X)zF}HQy>=L2|irB?e+~;_g9~{cS^ctzblVV7i9adu?4W`eP z7&nkEkCogE&a7@6t;Vg_)2-i$7|pJZqJ&g8we^B}Bc-z32vmlVMd)snUrIH9-FLq5 z(S!HRLW}g^T7C@z9wMvzrYNyb#;IoRDwjtauue@iZa&$l;GOLSm}fjj;08N{{!EqA zwBx($*cz~bt1N9NY79MX5|w+Do;24<*m}n*j-s(By}+_&>o?htLpW*ZG_Ny5Xh(3P zhM=1>ZTGiN!Nm-WwgT=m>9KB`K~Eol&45T->vhI`-4noHCs<*)H(Wx$`L%w^Fje>r z)8Ov>BL!Cb!KuT{A;y;dfG}ZtV2F|sCC@eGWplk&mEj(TDHyG^1`#m|bJ$xExJ?o6(EbS~WXkub{3 zIBc5H$UzUKrYQoOQXsdmNaWcVYY z%6vb_iR&&zAw_P53L`7U#|e>ei}7$j*axsD!;H(>23|-Tar|J~5aN)qLycBK#tFJz&8;*9T8aEVQRttWeN^5!+8{IX2 z{H;oP#}e428T!S)2shddCgj_6!0Py*Guw+r<;&!Y-=8gffXDzt6jy)ua=tO}&5t?7 z-uOd*dDz~&b0YIsg`7!K-ansq)V50pS?0E=Y38&xd*=<<1T#F$8T-jSrG-lekyEV< zlTE4nexQpp_bDu~V8ge4Z&zfjIjOnGkJGXEI)^a?PIO^pgwM@pdtD*?HvJ)U@KFHH ziLRhz3!5{!24fL+IBoH^Cj@xFvX)gbgoQzoUygPS)f}h43^U>#%qqotSV2v+y{(xL zF8wxp3v7#ruPrtZ(v!+8H^#*wktWXP#1njF*v+Od-%Hlln#=bCR|Ku^YY^=?{7&sP z0GRw7jnbuEwwVcjA$*EoqOae( zIS(nR7cGsDrk z7vrZ>P6@fih7j?x3oLNI{LucXl0;v1At*5xpJwo#BOd@3VtXFuJAEU53gVP(XWILv z;d_Do#$F&@=~+Zrgn#`Uw?!I{G#pMyNbrHDu}knswF~c@zUASE-w^?U$2^OH%#+R@G~h&O<~)i@(Itp3Q%CAkmc47 z=TU|&z=k+|oQ{s_9J#Qd^;>vYu!x5MUv9{;QqT3c&hS#t&AP27(yVit3B%2Z@^pbd z!Qt}kkUqQ7a!F7!_@ykgtK+dog3dm#&a;*pVhyNKR|w444UuV9$|~}@XFi_LPEfu% zkxBQhWzZDG#TWQu1UegVa38noMH?7=&=2RO$drty>jc$Zcq?(3u2EEY(In-Ap!-mP zJ1k4_z2*1GWqEloY4U^kQ=!G}1a$8s*!eF@-UB6bf*;4PiNwY9e~E;9-4$b3m*q!3 zrixIQ8eRl0#HFssQxi5q9=sUHh!&L&DCQFku{DfSGYKXd=SyJcB`z!!M-cWhlj8%w zaiL)yjt%t%32|A~HnkW-jf7i@w&x3LPNGxLuD?vZH&DndJce*k8M@pWf{zlv|6+B1 z37gVQH8Jcn?3QdmwB!gfjIFjJ-+g!70iJSvr75RKPeFC@8zV|4iV@a3;fSIY+nQbE*fV%xJFN`3*K6Xwh>wiJjQEbW{*)yO@}mO1G&Ib9Bft>i}(a2pRS z9TrW5SgND^MK9eS<=-6PHxIf7&9^PU;vW7wb*icgc`R{!hn9xO(}dg8c4inuNTji2 z_U4@rgxkR=sXrLRJo9uV^yPKHhEyo=Q1`_?TpQ|YR!$@irlm6=oh4pV0y9^9Wbt}k z5d!evAD}3tgpj$z+1FqjEEftMyFHEaT;B6#ksPvooE-UwjK;HpT<@3Olr>H|dg#Ft zXOM;UR>+6ngFt-iee77DU~a{)<2Na#+GaR#?->4Nc75scVndLqeM59(rl_xt-qt!e z1YG4Z1U6O*`8|fFxaMR>+)+pN*Jj^KwHE#2U5zN-A5*=K9q_ZD&N}#}0=H|AwWJ^L z#znrVQS$69@Wwr)hdrpZULP)Pt&Ls|As{F-VDEubZRcU8xl+Q)76=x~gPqKqtQLVN z(lEqR008@hfvg6-y4jgX^wGSl&7J# z{hd;>F1+A#6ywK!fv?;38~HAm5&B$Y@XCSpDYa^d;WzEYU7N#qw?0%8;U(CE7J;qh zDd7NiD!h$q1gY8ekLj4}$ue8_O|sP*Xo@vr=&UpDrfEHK5U?d%0}lghFWR~}XOkSp zx}onr4Oy=;VJTl6ZyFc#^jvF;>oTki8I_d%<uk!-rq2cvs5=NtfH-(~#q& zw2lbg&k(~_{yVs4y6FWhAHi$G8#WPvIPGko#(6CAESNpWE=2OX74n#HwQs2FQe(1E zhN+9VbAh{7&oX+l3Y7g^ZN5*vz5-#|!R1PAKy_YwT-jm!nv-m4%Him%&v)DtE;TiY z@knN0_v0M>cqjEkt5F49(QHUxXI7&3mz7wHoC35V_sO^c>Lgi1Havhz`;m=gg?ps@ z$5wVY=7?u&>*db9C4?2hvaOA|GYnaXm$*57`}G;;iv)WV%oZA!#Znf6@pI8l(_!#+ zx&xUbk^1U#ER0UrXC#a%B6nJO*XC`JL~N za3E{e2bGO%a=?L!bV|E_?BdNiM8!6OXJ;hT)#mK48?!P%4`Lwn9|bR(>D@TAw6Xz^ zel5%eCG>D_kn1cWOPcqIAQwD^7*l*4$x%g41q@S<1}Z_OVtVy0KJ|>FLT`YX4S6W8 zB!x5obw9Qeq$s?OG@Slr>ap7^h!AkiB_|Gz0fVxY840?#w9|GlR>Z8(b?SXK;4WX) zKC8p-2^;3DH?snYa+Nu4?fc)S@O3s@n6uWJf|w;-RB;Vt@MD+mD?_n)E;Lq*$7vk24%iqs>6H$b{+w`WLq2R*ZAj)6V5tkz9?zz#nmGpJb zH!8>ViG<)c@ewTMSU%vr_gp$7y6GHNU^!4MxmYOeH0|F32c^pfA^NuRbSx<2)VU9J z3KJykhCYu4Wi$Lz1p!+t!*Yiy(CIE>6){zRIh^gyTItTw#E_f(WzQied0&z@`z|oc9)pt9uUTFDeFpQ}>Mx!@7Li@L zei8Z{|CpSWu4yXY@Q_cj{G5#lIIJ3%wNu1i2IKrmzs)z6GS96w&%1`cs3E$;p=m>` z(kQdSI#6MUvE~5FUgueR?(~+1#q=^?9_GZSOD$91i4| zH35ZE0f&Aa-}U$n);cq7m^wrjbV+$%9;2yzxY3JSJKAPeBZqHYncip?xcwnp)6>~) zHuEx76JFnCd>GU8jRgUsnO}v2^!h4mpK=1%>^I_Uc?lG4*7vcc9J^C0T_MmKC`=QU zXBq&?zbn+|VsFb_V(!Dbz3)tbq(voo3XjzpwOyIu5v`sp65omx1ta|7Vfq3%OzVle-!mrh@W@_Jio&`oqbIT>>p5Py%m>7V`{sTt#0|?ayY6*q!tlD<*MHADiiSDEY-EZN? zU`tv$k@$pB;1BS)Uv)yjbpH_DppI{oKv-@FW~DU{b`B!wL5@jku)Bikd`9u}hOUQ6 z$iqS`#U!jZ+I1b3fu{mxF4uBVBwE0uuQV(2?5=yx?tDiAnD46_&$Xq=p5wV0P8-Y=pPXI<`ym@PS2g9*6$36eKcg5j z0_hr7YnjzVZFl?}`5w?y_>VR3Zzdg2P-&gX6o@h=J>O~3;;E>~>vtk=fQ_I2Gq=$(!qLTXEooxV9 z2DD4L3iGfp75kjzfVRJKmye&?DWdm5&q4d9e&g@W3x7hxBP z=o)Kq)4(VxM@ys{JfGFiJAbH~*y2pXtyMu_60{%Lvp68E`5}b-vKcq=bf ziJPvsPP(4S7Rq;|_h^qDRng`x#p@`IpuJwKQ{Qmbpo&t%DZRhH)14{K`wdjK@}X3# ztqMzNCWcci9j09qDLIdM{MDK7=4_T(x3W{*>j=jOdw(U5MRMLJ&40G*%#TLczN;o~ ztPObHH1wUa17^>50#X0-tF!WaU=3q;@3x2gu@pXW-rYlU5aZPBGBf}*$NOA^VY#E6 zNh0}JOP`;%$>41Wd6>~OiYlRnJSciULmVkQ?QynUi7k9JCTj;8bs#{)elmc~H>ATE zH}>$s*`|BqBt4fVI3cnjiV)7F)EIsO2Juj?-n1V3aOsm(e1$o zY%r(6JQroZg~uaP?tMCqWWbQUl~w0QiLFYKK+)6?L5EyR(=FpnEpx)2W2)LbhkIr3)4WOrFwVa*dNhF#?CjQSty1`E}Lh=?(l(PTmZ9RcW<>xFH6s|bLVbr?QAcL#*#3w2exXl?C4wLQxNpv z%{G~$rs6DalKJEqvO0NO)~=4MB19{uH9?>w%G^gX-z-Gq zlimkbNz|nV4{fMM67p|8HlV_KFUZgKYVM<;$IxuiT082=S~-6jF)L9Y?!pLxJG<yaFmOjbo_FGy87|FPu)B;^G-{z`mlHms?;X+00#p^F($%r3i8?m8^<3$? zwaDVTBI`&zRWg;Y=}281loC)g-Qc@=YdkV!Q-U|Rg7&x3PFyYH)bO}m{nJ^iGghRH z_zB9w$HL#KwPJMdJ}Cn>-6>`Tz9Wm=)NuV$cSnAJLMr0P3xALGy*)`$&*ht2xc-Gr z*LkXvYz_Ve14#ZD1*qOxX;G9O-CP^rvO(i}OV>_V1bl6r^W0jGG<-qh8~vcBo93rd zxUSm`V{FZ`#EQdPG_2Ik;+m#Sw!T`@>PR0SI4N5+*$C4m28lTju$DC*Yq?YA)z5vR zl&t9d>km&?iSbo7)NS2I3VG4YAM@OO?28NGfj+Qb>_JExhzN?r~$I$_h&_P(#*1F}CyX zRGAaVJGplQ!GgeO9J>t!GG~$P`zR#J<#zF^jgwryL(k$dVj1VA`l$pg>x_e*#<17X z7^gcB7MCYXeC`x{6}pVPv-g+t=?xTApOnEjixY53Gt(4wE6($Su=i&*%e0oQX`|YU zlKkq8l`^LzD)%Eed_|upmQMByFTcXYA1_q25263oOp?r$##8sIg~rIrsJ0u z(3?YXz@X4qCm1~_iYjp%G`!OgW18lMBwFjpn8tdbp0I*r*X?^=18|f{*ZOnwX26{% zzv;ga%_2F?NX?p~(1^_q+HlW>{<1Vd582`k-3(BsTLQA$vGBJcj^R9iDiQKj5{ISy zd}GUSLYD5cXyYMLm+(~{`oYZ9rwy+uzk%tj*MR{B(lh8J$LhB3Ad^R%-J zbCF%hJAoO*|1|;q;X#OB`XN^gE8w3(0@)>npbd5a%pL{wN}|w*W^84d7?@xYEXFD= zRwXWmiF^*QUm1c5n=J6=hV5U^awA@t<-frIXhqx8`d?heZ6H&TMa1+vy26z>>^;UW zyE><_=xpA!p7O+8e13GcqlnWrc81GVW&VCJd-)(}C;u+|%UmkW%VRC|aFH)3P_mZw zS{J3(5z#DtgO<9I7%f+)kE^4p@}Oyikj<-`Ru|14<3LgSK7AlBCI9bNgay^;aplIb zm$VvKT29T)Tj&sTo_Q@;&|%QA5}@~&_E;|W_(K?Z*r&h9If@*xMNVm{TlBIj0TO@h zIA9ku1Y#lspvzSM6nl^+XcQESULj@lNm5SE;llkS=Y=ghp*5AGSn;7vgz<2@+E_P` zyL@U3jcW<8F|uAz_s=`3=YC3(;HY_)5=sUKPU1l%Ziz#5x9f7(-4r}kor%py!CNyFI_*J=72m`e!at?(TRQ(- zmc2_4y-(6p)VrdmlAIE&rnbb|Os#tg%69C`|1{u!#&0yu*y+ANfYnkRLj{^2x*64Te$JbZCwudB z(V3@($OXn5*U}&&`|`=1SLe0mJe$E%?VWY(9%AACdEoviA zsKS;pC$L`U+%>rdE91bnX&I8zg`vT91xvl1X<(+Q2P$%kDYtEiopTGcU_C%FG5<?PH!sUaxYVqy`xUR9_6)RkyV-^M4Dw!A2!{?rhW8kd@ zwO%(zCyxEXR!IX7P-%IP7X^;hg|l{cX0PzQ#YO=U(yotKMTR_992tpx6%d#F(isy% zp9SK~{|O}|J6yufz(`qw>CqP*i8iI7+qB^#RifIdG@17Jhsd$?18~uwD-{9W78mxg zK!jMqfo;}At~YU`h!8^vjjiLBK& z#CiSO*~sU>-sL7XD;oE5Kl%?+^j5E^Sra`rc5BJJ*b?1)jjZuVy{Oqz zUYc;Bb(?#!#yx$GncBK@Q8uox+-++px@x}g@kHvA`Z^``ToAF{|A&_;n9uK2!+bnX z2MQwn#L49yG;1hDw$o=VJG*zok+PLdsm!~6TCd6sj#`nlFxa%a)L7cKYW?)5Wi$6@ zOIu%=bko`fQ(A=vg=(vaV2Jwd`xs^_OIU$)=k7;5HZ1Ab->x0td)Vh_XkNMPJ;R=kicxDPeZIg&kK)$v<1lb&W z)mwv6Xmu%uL_iP8=OZ2IOG1ZSpXcWW{x%!dEMkR z4GU_P=RTLsKm(%NeVlR(653~5cj4Cwu2uP^m5@aCh5JTn5{Bc@!;Sg*z1DB>*j#z; zu#r!=%5Rj{14}pLu~U|&#V#_ z$y7V^ly!M#HZbbo&*hRcG)6)cU^<|uMJCs}*d|g{@5Vjz9;OFLxuQAz&K$k3_2a;( zE+tN6_bw`~XVt~WBcGkm4(r8#)3o+f>7{kg4W{Z)aG!S6`sZuWrHcT%#r8VI^pJA9 z&&>RsA7};qlbHtI`Hj!^R7?~c_4q>uTt&f6U83h=!&$$le)pzQK?x@-^enBwaRDBq zw-kZsR30B6AtCszlNOiHvL%MfR5}NJ_F2g1qB&DtrxZYF>9VsIEOew;ahf_uySzfV zKfOc-=SJ$-EY9r(TAF`1wf}TBO%egsM>FpCf#t-FDqWHS2Cy-n{<|sgr~TKvh^33z zPWu1R;{Q|CNA(MxGJ8EcgQv2OZ~O;5?l(Kk-)*`9@g7+4f4*AYs^82Qe+MT00nK3Q zTTt`~OSdN@Gs<0S=)46ru7QBLq8&BG(@mBH;5K9OAJ$>Ok_R_4P*ec6c$(&JcjpO$ z-vv_@UcN}CNz-654@D)X=7;_tPIkxT)cmtOc8A6Av(ozp>OXoiJBpis@^+KM^}zM~ zvEKon(Er)N`P<9VRZ^mj+NKgI&jL%s`F}FJ@Axx+648O(J@8S#$7lbONBd7NsqJnG z?-e{~$n3WuO`vCEarREbqq;QHc-`EP<2^vJA9vA*G znt==t_@Ru@L}^*`2Hc49XUE z?VZ~vcdMk~Z92@qqTYUaCAOt_(*7_QINcvvT3Mdy6&FG+$)@RwdaS|az!~;0+rvK% zm}na>!XrD$Hvs%qE_wwcv%ZXDW|dW*ep9VlM(h8khUew)V8Z8)t8-BT3u^*qLZk>Xl%&>4bS2QmKvEZ*_6z{T&> z+W^}WqW#@r?$-|FkJ-))Ln}Y|TO8@+o&|9fM!3HxDkVBptLh%nW*2zt59O$(jD`XE z8hE&(h)$9|d};$DtVn@wNzqG&0?rA@FugmQkb<`Z9cLUh_Kjpq7jZ&5Vzwfm#_n8e ziR7F+D>2O#v?>FnRin2WrBqidb@Cj=C=u=t6*;xNp0TS#{VR9yrWDqXKaE@pP`ntj zyU8ZL#kZQav25dG)(#NW>un-EUQIp}8rz;f1G>B~&S=O%XztGXAQ1pV^9NITZP@?E z>s6el>pb$>B51aTI<3xFXOZt1bSHHDPes2M)v8H>HgM3|nU3USM?<1LMP!oWkL}Py zkG?;36V*QPXi`3{dHn2AzrWmT!;^61>T#XmgPk%5euAR?6ufZ7jO5ig+kH=Oexef@ z?HSERYE>Rr68hm4zFAG2j(#okp4K-FyeHFIHV_8)b|+^CytFpI;vq6nIPNV_fILW- zWc*@LDouE@6GZ&Coa)3EO+fa=SS{b6Yf&!w8t|2$!W(598J1V{3x0o+f%yNN#+le& z4Uwl4oVH*1{w3wS8k9~aHdr_O%aWQ6?CZ3Ctc21J&hzs~q3ZtvPkRF@s~06Z?biJI ze9KcyX(_3CGB5Kk14Z}+xw!1(?@t+^)R~*`Xim5kQZU1USi3abRg?(1L{5b#pOwF| z{5P;#D-sA(WnWifcQJkxxL5g#vuD08m8Ze;q@rRfi_HMKEGs`sdN3S%=+h4G2 z0hQYvdAOBn_%VhJ>ZRwV;fK7+7J*|sj5y6q)msxS&GWx|Pn`OsJ-vYxEQhYp3!T*n zY0tTen#GScQvUJ17^41jn(id%o%+`AJ?U$up0#Z+&o^xNU;d4m)xlnh4|KQb^uOmc zy$~$@&#BpfJ+VUy;WiI!}AQ1&LKoejB=n7(2`IA3^i4$7~d&1`b9C;G?pzIg{#}5_J<*QQg1o z2%);4Yad6|Co8Nv+`vEXxP0S39XUyNMk`;W@&M37VU5jaU!HpZ_lp`ds?k-i*1foI zfwFir@gOIwEQUT(AXNL6?=4ikE?Oe^8|K(Q)yK3%C>S0XMa$ zHrUK>XV2am?cV~5MNo$ybj?)r?LCCB)#(qRWz=Z?_*#a-W~s|yYZWK9VAykPCnQ9K$3Uc%`uCl* z@<-^Z#@?NTMbJ;Z_mTrlOw;A7GkDSX^Pr=gF$LX zx)y#%bRqlThg0M%5y%&*Pp84?x^4Or&j!-JC;OHkM!2ml@+FKL4lL?Mnz=PbhOjc| z{a^Z#$9T4Ey^;t=(v_E)@^b?@?&;xAaFD3I7Ap}?aEy`f4=4Cp_23#1o8GLV&W;Va z+J#Z<8s0m*m}cUC|2hE-&dkRZC-i^^A%eYQDUM0v(-ZaKt{37OPfZmH5RP?e3ydsy zp&d72{lIAV&fIzdY4wV# zXxN!W(@EkM0f!aPp zr7j*QM!MyyKdW3#dbgpXvZneczSskS%A-#{8WZd9zjU7#w~sC0d|B1BztmE(N&EJ@ ziv4Z)k4_4T4;tUA7LNE>PsN7SPwd8h=*VL&GP)T?vp&{wj9b=8<{l(av*e@HfirPx zL00-v5_g!y;jG`NGOx>o=E5}v>%QSTF$fP=-v9c2+yKhN%Pu-L?a?o%k$kQyIaZ|k zeS4g%uZNdq(uq5y%9_tYg%>BJZ{zay#9A6>lDY?~j9JQp4)-<$VlvNoZ01LdNECT8 zkZe^gVQdHIw3MV6wu56zh(E^d|0C|rGQ zk;pD0`xdhA#=e)GVeCtmDP!NqIv6~+zUw*Xd(Qd(&hxxpPk-Eh^)mN;U7zc-yg#4o z{qEINabA<0%5dDMwm4Gb8{amJtwws9vv0Ugzs~>mB6Ag*`Zd^m^Rq;MO|v<#7(7{p zJtR&;;N3IAmG{rxo0PyF0)n1p4BZ5(@5@^{@KnD=b?9OJ5%atdujbsFleXl2qF zFujs{1~`}2GLwV=LUU-e2+Nh|P=lIsEBa6Z>RaEVIYGZwRH=!5bx$q<UUrt%rA~35!tQJ$94%+;~<%guJzVaIY)*fa#_5& zI`&Un$5QvNt~4oqt^3wZZ;QJWTSx9V^0WYNPy$neR%3VuY3p|xi=}@z{rNieueIEt zf4QHk4Y_`jM|cr%RDzz$jLQ<9C2^REZO?OZ^wuK^-YQg;hd|>3FL!V?fW5l zG(7Qa=JSy;J=rL8s<}NOy92bED#M7Af3_$zo1ue#YjyOdGf;FZFp$t&o)3Y$I zJKLbejM4qMW}->828+66d_h^SL{sm~lSg(M>Bl~ZmDe2s2)u|a{9Hzs&kOG%G8>$T zEXJV2|9j{OplPI4%$-JY7~}Xo1`OoKN)Vpw6hh|xZG~o*L?Uk8C53A<_IK?4Fu~?r z<*!BiLXvC0A#xZX`BK}96CZNsM{a#m$cqX4-;w~JRJ>t~dt=*Wv)v+4VbZpF2mh*S z*Ou%kgrw*5+GpBE2_%dTlmg*nO`C2HGX4s6g&|=eSzX!jHj?^xPp3C^KwJ(k+~ax~ zaPKy1Frl#J?v~mt!LO;ZD*b`L+Erix7?6L8rx2hseEWOHGgC7Z2Z*l+G5sckUe?;5p&|SeE`C0n7fmXyFzcxA8J+0OnS2KYYGg}EP+!~>2Gy^OX zQ%eXC1a%MoOmzN_K{{LzmTu0wCa9+a`FwFbaJ>R!4cr6g4=D>3MpM@dn#}dyNg@^2 zL}D4MDQR`=^uK~&I-LJ6oF-X_etqQ%=rugd%Sc{RiKH?)^yfF-Ez|)OXscDhpA^qw|g#eJ+bdx`;XfDp-m8+c?!0;bV@`M zdVw!F1-2<$mTWCM_zB(4v;Du@6I&?r6&dJ3-^4?CNUzXnk8k-=Pk<${hDh^5zUUxX z{q*qjM?m-|7ou{az5QldoaTvGDt|LOt@3KEx#+-BFxP||Natj1P-J769%Nr0=O*mt z&$3AiZlarcjEJlNk2;ZD1$X6+7&pNtp=STP4|qfm#)T~mnKOlQ+-B*^BcKqmM5_rI z_cT~snjZo|@}St75zuk<(&T~1PF-^SF`}(VHuSvb-r!<6Ie#j}D6=S#5p^TnWdz~} zuhh|jvKH;ynHqvlAzH8dSWhgEkPk~tXxYo+j`VOYAT;5;%u!&Rx&Tz1Dw0`>TzOWu z8_;F}CoHn=$o}uvSHKSg*6{&>Wp7Sc4T&|`TQO$MT*M`|~2c#&()_0*`nv^~A*$-EFOsn&2czC037?s2Zejr0Wez>(iZcF{HmoUE12u->gCVLBaOUQX*?m!w%I!27`(|XX@rbJ zPsnrk;+0v`G?PSmo=FRKd$6rJ==h#C8SP!Z?})6T``z+9)7J{R?a*S9`^mwtO>tFD zdIZXewr$)f^Grr)`ihQBtVry$opUC!oLO*MVPmbQ{+5J>8{Y_)1<)N6=0j=9{jXZ& z+UyZqYno6Gd5Kscm@n!yU{+Ed`Ne4=I7qD8m+SilpHFbVRJ&joHz3rX_=`$=7f}x} zFv9vo4=6@nL~jIq8$PSB=GHLXmy)>c;>Z+~UG~j@I|d`_?jZV3>`Q_9gIffTT7O-X zzl3@Jjhy>gx_f0eAK+L_)dVO7yR-i1a?4i>pjPBd`5*Ibpl;Q@4#ub|I5$oGNSqvqK?B->z_|Gev!U)!rJy=#rXJPn~H#sWTyNCt*D#Ia>Mc; zlfi#ky8UMe#M0cOISU~o$Du{{=(+}>&t6lw!1Zm>wn;;QSI10DCWkxBPh+OZq#3^O zD5j&D<+%JDo|<0rSB~##Yr7{=0y~n~M)xGl12RWzGv7-z^4VvxBHOXGXZJ+@wJiVk z72EF@j}OEL9%S<>#VDexoUm5q{XoO_w2PC18IUvKMhg|;=!b2}LU{7Z!emcF^cx@g zCAc#~KX;eC6MQ*lA(ZwajTR-*tdsykDaA?-hA@bAN(*{~R2P{rNRU2EIq~T^mXp_< zNoZ+PxxviacG7a=gzuA0{aktraz-1{+gW}5nZPczP+T#%a)ECa`|IR+>XGm34V8ba z(tmMV_t1(@(9{&U|AKYpP&4~k&m2fdJNmQ4dlHhoCwV#6li)0pzdz?`sx8y7!^6?d|Ozwk>^whvZaK|5awjEmUrfG+D2jg3d zCMefl8TmLW1!MSioD?8`wn#Ghl>(YnykGNmuc8_MwQ$b;^FO=hEVL+AbY{Z6DB?(< zv->;3=eKat#Z$`yE3(1zD-4(Fq>iT6xNdl>l2A3)4mc#HOmw1cJM`jZZT#e>ILS;U zvAhxcAn3PgkCn9!8wd9M;U?GhX|FyK2ARxUCfAXxNan9&o<~!m*`r0{zmGKoC!$P6 z0C?&Foe6@(MS&!EPgQT6xyQn5A%`wuA>*#6(U?fd-!ab!OlX2|hOA7|@2_ej;l09T zjQjjp2Qs(ORX(a#P4G}DnPT^fQNS~$0wS9hV0!Ua1P;LGLQx(UPJdCPs95) z>)k9vRuaic`#rX>3fp^9F+F5wjOqFEsX;9q<$Q6=Q6&{f`m@ZB{ z6*cx3CrigxyP(`rb*_76j@U&3xaEp7+-T|XpoDcYt(0Xvtu!>z&8Tm)P<&tpkL(H) zc|n*#yQL#lYIah^J~Yt1kQAj{e#}3hN@b>{oTU@6-Lmx9I_*^|LEr0Gap*=h^d&40ike zi!Q6e4>`9V3(!j1>Fy*8?wtFI`w{%9R%}22(PU(R;7AUjaUx`AGSWrpA7VmE-W0Av zQ_}9dD$1AQB~m3uR^u}5$wwzyZFW>>QPKM)U2@FxEtKF2a$-vCWo&h$31IRSVS&@_ z`%U5f=1unSfV4_MUS`@j9cllLo;y|>#GrtcO6g=P3$KH@7>k?nE6koY)6gSEN7Vh9>PmvXBMfoS9ex2q22cu`;fbI-c+cc}IeVh?=*rF85P9G#;VkSFn zxhUw?b8&H1)du|vH zIR?Va3cy|G=!860?>mDT_#v)-n+^9^S+@yZ9Ckd@6C1pl-c_&b-QkEUE?6|fM6K-~ z+uqJ2_InM(%xT*kPgmpYgeM)Bav!EUVbZVIJ~v!=k^*3qzy#U~W%@5v>fH`$94c{L zqTJUXaEh{S$}XW0%myYPi?8-@f5A~Vx~XH?j>pOF5z74{EH%!eFn%_&uw1oL~w*Zd<1F3iH<={lXkiHHAq`|h7sINtin9bg!c zT`3+2?eF~TvS`IC;9D8|pybVW)xCrNu*T()7`ZT-kRiF|Hu+{2TM9WN?EH|WChDWUmv&2}!O2lW@-pvN^w@4}<<_t|37tYu$65QE`y$@8rb(|4+L0a7uNbq41tVm$z& z3=#9zc0ljJ!l3qLzs*$11Q3%Wr)0cSPTB9K(mzzNL5}7AKl3G_Hsp-Zg*5I^~YRrspo1vXE^=l`3Q+QpCP1B zuin6nH-J?&MHtWT{j#93DA^S{yXXSwko5Md<@TI5?lYUEhwHBH&f|CZirTS+Otfuw z%sWe9acJPtIT9TzY4(DPpzT-^|My=s+Euj}rD*2*DKdfAKAd#^_)2b{sMGzOy3Mbp zR0#8eanRd6?6Grcm6`tQUZKd>Ce#lst$|8Ja;3$n0g}=h7l%_C9&ck_lBkQRlW~>pUe4$S1>{ zP4-lj)@4Cjysjgpa$=aSxA)LlgrjF&o?Oe320tYwzp5)VqFo-l!=sw*+i?rY8@lfo zT&rz;TSgI*)juyD*Qh?)UpM`SCLO@`b=gb+7AZ9hF!f6b*$h4VnJ;?>9mo{{)0;D+ z3SC&`_{vF-qa@T0d93Glv$aRj4MJ!J|Ewd@ySd+&dtXMpxI25y=ozc|L|IBdt85rB zn-zET_2~SPc!5D>$uf2td*beR%G#V}wy$}zu)~58sZ|mEr;P`W)!h9&K&-KXLl7n> z|Ddf19AY?Eqm9;Hl@tM_YK@FvQPo}hB1$oZzwt@R=`chGvk2YZEbiCO#BXU9MMKUr zGYCq?ceIE=c~jd})dvxtKit5Qu>DDJlLw|XMzh_0X}Mg#yLy39ZpQjo0)Y^qt5=|L z_S6R4WOtxlF(v^ZnFknRD&l179VpstPz_TafWF2&7K0Y)254Pg0L0de+vAXwIE^fo z3|=ol>bPehL$N2pzJbpv(lv^ahhBTp{p<*!+Bp zRLc+m_6-0hPbdWSov3q`+my9V0KU;dEYo^(XUJ!JO$s9$(nhXw8LR|&`-q>bhP(@DvJkt7$q$AxSuK16Lao=!{e z7(%g3BPbCk2dR5P$ip3!vPXu8SKsH){)_od;Nm=Fb?o0fED8UB9*Fu`_Dz{!@PF z-WG5#?P0z5(f&r!og#l<%%H)83^fu)9cvsgSN)>f6X@hhwH0-*O7LCe@N-cT4BMD| zQD|_Q-F4se2O!fOP;swW)OIr$ziyOFPF&!=08fLoFB4cHw58!CuQ85^R-#SssHiAT z>|~gJ9@i+teB8J=^LTLM153Zi`Z6#Un@jQY3i&?-F>a0;dO!pwKY58&)b?A89V-{j z{6YUi<_DnQr+(AmoeOD%WX<;giCHwAkIOuQcH}A`y^=DZ(uAD?TL4-H!D*uaWxdaM z^TBRh{aWpQT=zyjlCCDd4l|!&;jW2X0US(E6D>%rt403NFE4Af?8m*xuX;k(j-Z`K zX`G@3giMxZ>GU6Ys5@Qw%cX7xxHZFs+$^~b>SCuheIuGoO@6jt(#{lIHYS5WZzBP6{y`D5G1NF5(!-JBajn38 zY2em279EyACWF24jjw>v=yeIh%6S{+qtvlt8R$)-Afj%7K8fBovdmS!f8@>?VY_aS zK^Au#8M6DwgZZ6O`%3C7Mfzjq8+U~n!i5}IzR#@!WtYG{8IvV}laffY(EQbKX;=hZ zpeHGfh|KU#KRC8gZIJru(!bpd$v$jqWsivS?pUcAacu!Nw{F>OK{>wq9d&Km5Do;b z5{nW!f|7(v=|=P*+snC10$@Hs0rTq}G$!tgIv(gyu^L32 zEzTmLFGbwpD}%Hvzo7l?X2E=)WJMCy*1f)M{}*!YloRe}0_>i5gC*|~M=BgWp`;gq zl~dNM05Ze~CPa+w*0~VF_K!>ENN>6R?tTgM{HyJ(l(AP=>Q?M`hQuL3FkBZEYRg4T zmx}Kg6>mL}`x+Qc{*|}opN>7F1&&`J(0X>@-c6xYXf z$>$QtoIJy+gm0BPGh)WcAF584q5rTL5RiK0A83@Vqy}6Z8dl$t9B<=;nj@FT9cf1; zSfSd}!wef^Qu|$QaH}bK$J3_N`3y%s_)1m&L`5Z6LmG4ABOhGv7b6Dy%t=DwRLMf= zP|wqGE<(}~m3hrfR0Maw(;d`*Cok?jY#QZ)eY|AUv)ukQ4t0mrhAjt!?vwULGQ zH*Z5tFP=YXdqs1bhBF_KaOUk3^((~4711Zt&l*s83ksU;JiatAzEWUNFR2S$-L8ke zbuYh2ayz=;F#z~~Va(9;#{9;@Q-L;=w*b-Bvu!$ls4Tco)+Xd$H!g={^3#XyU^wy> z9M8zsdWiXwgt$iS_{m7W?w#ji$a0dp(r38 zrA<$evN33}Otv54hlj1w(0Tsv^A?Os=NShART5$*u_R zg9N`y3swv&>6djeNVt8ggJL4wj_Sz}#6@*>drT5`>4pzE?=N7kl6FkUb1@2MG)NI>6+`wl{O-R%PZg7o_xg`dzpS4C?0hRWo{~I z-S&7!Hcv&jLuvAiGkSdG&Se_GS0PJB6U1f4I07xY1D`wWtaJmd`ujmNWMOgF>Ytq& z#1^BVsJo`PJ7;j&>Noc$kIf&NBa_vDq%SM9eL8I}58;VNplu5$j;+7Pnx=ObNkpw( z%P;W#D2H*}_0|MHmjis>htBJMp$R5TOR-v(t171j9L12S!M+ZTvr*;rU)t&&SO=!e z+u4|I*-scefPCJR)PRoPZqm8*!l2Kz^v;a&yt4aE>I;5X%`b!OvCM!1kl-lx%S&TM zyJwlUT^@3vHR=|$5b17N3ceC+54QS~3kC9cyjkmdE07kH(2NNTOY^^03LR(l3rn&9 z@(oC#$0m#Qx?aQ4y43dSl92hAf$KTu8`B-Dp~8USb1S1=w?4kw->POfWq$vhs%Vcn z+OeiosO*LMYgKpC$ucI$2{78A9i;fPACfY4tW9*v?JH`Mi86PL5u(^|6>p+) z^j*Ik-yFbNoF*8naWv&S#y8_#UGBcuv01aLMk(tL9l5EWl^SUtcb>I9;fP>Mj`!jF z6oz^zOYL~AakA$LKK$iDuEkp_8$1H!H2ZYPeqXVG`ICo`Wozo&GG~1c(yW9riKL!g zK_UC?x~JRzuecXpn?bfrcMhH8gu0dhw*l6Tqd=Dp8X#Axt_}7>ciuu+W0fIR*&Y5Kc7L z1mI7Uk+P7AyrTlWiyfu_#g#E72l`SKIp3v?UUGz8_6GtYo3QK2V(JgZOtS4|eE!#n zi*Vb~X~f2OGrvk~{R1v7mpBkTP2<>VXy|lF%p9(wS-{1KHLKTbyDqEvM?LBMcd(R2 zG7b!)Jq|-oLP3H3XpT4l{tiT-iWT7L3n!B}d$V41mis{V`zNU2Ra&hjK(dmfkq%kz zi!`H`iT8as=zAeOkR_R4FHGj}cI_2EyRT1J2x=1no{)N9`}$oi(?X-MUO=PA(iTm) zc>&7ZEcuL@uI?El0TRFxApF^j^=Dt)c8#K8to{v_9`0)LTsVY6ez)TTtNuJ7fc$Zs zUF&Bc8Q>;GOgHsMB96magdhV?6a^sw{1Dp{w!t4WU2XGIPV)QsedKDATS+bi`3^Dt zMlDh>MI(*zeRW7S^njB(i`Dy_;D1Om(9bU z8;BZ_7+)0#iF{so8<1$q%lHt_3)hxtE9CT9a<|xg33ncRG5@tytOp4`MF4#MK+fA> zvNvO#XbE?4-dzngavxS$NH8QItf&yQ=_V z`p+c?7uRCHoNDT^9SC6fsDHH+;rhT5-wJ27R7lWn|2?)7;QMveo?hNf=;>ZCH0wzs zo`(A;gX*B?q3cdp635|01sYuM4YjOh@`e(+9Ujn1=CVQ=9}lc(ALko(zY=BrvkU!M z92B(iQSdv{R0Jv*Zmv#E*Sq%s_xTy(WTaOhWQ>}WmVna-=w?H_P4^CU{X$${ZeD`h z?CNe0RBI#1uCun&N+%=$#Ie2gTe&(7=K8~m!vG%z5cxAh+UwW>?VZP$S=-(R7hc~v zBxMrgphBq6!nbs+8=bMVX++NP+N?dzf%O%AJ8-?3KTJ#T^LzTG~H?ITHO=ILeGQ z;HGD=s!yAV=^jB?{_=Rbf-ZmO<>W@Gr^9Ki=2T=zoEad_x?g& z@q5{+h0eNd3rNW6>M6&M!*!}`G9;W1mCWJ$Kgw6kN2l@3XR8!e_iFqcPR#dKU5Bu( zLv2|g*kxo#2^xWCifhwacklHqbdpF2{TK84{ZD^lf%~`JgzT-;i??t;tGFZR?rJOv zS=TExXtdj75Vw(6T@9)D(a@W^@rETXd0YeN8_*j9q(cVYXBwmd@<`!O;}e>#4IhJT zAE&8NJQ1H$FZT^5n}-eyZg*;I#mM6P4Hg}oP#ZDpiZwM*O!VDL3X6ePWiPSu-(V#X zHnPDJ6}>d^ri2d98EqHxr6_4Jba5=y?4~(m$zV64cMq}GCso_Bv)R7RTw4PCJ6&CG ztUl{RBg7wH+>Q(>9}qzA__0JaSv~}z9NMPp2Rc}adHp^ z8Qyn;l?O9VR|oBPu%oz$H>ZaIoic-v94aoDYt<-F5%Ikx^d5KeY~9$Ku-69o^z=F@ zGHz`eJez%+xLRxW0gves0Z~hH=gX8D{YENGEvAjW$K7&pI%DGYeT<^?INZ%vlvzfA zvXN$l^X&{Zy6p*WE72l|nI$+mgBO-D1r1GaEh6yS~kE)QOSPb`7vIjI(ID?o#1hS1xm z<6gz*IVx#Oy0?HJHvTq$>1?Uz(O#VYhx(Jl72*pz!Ps~4UtV)kHx6h^3K#XvH*@&S zP&Z3H09JG|*34Nh^lK1vc(iu&9&yaj*a@7NgM+5#htif=wJ*0-4WZv~ZYYR&W>iKn zU9ugbd`flXN!*vZQrX~z7@}82m&NGmj+8mYx$h+l#yHGJ*1PO99enX;QcDm;M%j`X znQzn-e!(Bl!^dF=21+xO!k7?>w$fw(8HE3B=(WnpSHV@Gb(G3D{@R%GFd(&6uRq?K zSpNRSXMXKn z;^>DRX^pe+>k0*JCbG0E9r7r8%VL3SkQX!Mo6Zx-^ z$@WQ`4{PJ?ZM-*1wnHPk;MuA$7(iy;sC=zXI3fmg_#_Ye{%sB(mk%vuLhk@kI<+&p;)x6Pbr?TM&%>Rs+&5o6 zn)hY+z!m)hmF*MSYL;15Hq#)yxUp*Wnpu_hm1~DUMbGz$Vx80%2k6s3tv_7OGY8FDLsr-} zH>vb`1F_7w?}W?VgiQQL3X#0pxZ>#-VIcFF5ACdB(k5HQ`uR<~n zI>_fb$W}Z7qAigld2S{_A9rGa0dT$a_~ykejy6MWe~w({ts6g6TMwDYKX zRR{3aBKmmMrhE$1lGRUeH@mjCDZmKsTN%7$<G-=v| zeRb70?$m2GNj-RP3DkUwZ{6y<-gw4=bbMDB^NFUG>GKr{BPstYjDlu%p7Znb2n6$9 z=s1`@*rQnaQ94f^eo#u?d&Lf%$BUrkl&8rLIwaMA6GIbZm`qUs=5J?bAI4(ESxi5a zF1D&J1ag>|+^vMZP>uU|4=1`qk}Y+GNe$(;RJ)8u;p9I`#Q?o8)g<>>0TECo+$O>3 z2grT9RC`oX^UVykT5?JX_FfuWb@xqcf`V&WxPjzl%aldSQRN_Yx^St0h$5`c<~eFa zP5sV*qWoAmhv{Yl3YSSPk&g~y9%}VnLDevv4 zWi_ZOQ@;i;{G#Rr0RPLisH28MWtDOFBgjTo&nKFK24V})dHtnf}b80HbW@! zVU{--ioohPbln6Fv=qUCugyIw7T2p~x~+Uf<_DWkRoy!?>j{Bf;FfTD_f*&A^yGLR zcGrf&NMhO7$u-Q-R~2|KKc}Ei*w!k*e@b)cdKtY(BI08vuE|ny(7O{*zr#8$gP(QS zC-qWXU=P$mEq<1SZFnxvka>MRV|@#JweU737Y%an)aGO3BkPH%XCpmIy#5LM;N!vq z2dt`HIii-^!|d|VO<1k-njqX^FG2}NX8ARrb|g;=_rxV1uRkIhZ&j*#&8mfL|FYIK z@S8X=qRs>~X>YOOw}16pT$}DEZ0AaqF9Io+xp<&3r{kMX(aP5rnu?v1=m$`!KSoKSd>;l&-B@x7)XC&p^#J`>Ba zh5U1^d}-2PFvjQE_pQeYn`|mclDF(Q_!?&X$BQ7zZf$y0fzlECE!9HSH^Q*@*f2Mu zX5A0`bpwWjdV0Acg71_r9(9~sE>*uNu_r~R2hDKSv=R+04`U{ zAt-Zv5%%8B<_*dYy{(+ItFlMe_F@cS<8{dw=Ju}jJyyL@eqc1hfAEs(jjgfNScPZk zJ1$rh)A|LqRtjmgJ%IE)cUI~=OY&7|gb6!jZ&RpiYc_@O-yc9_46AAXpbe(^d)^Dz z#|102*WQtoEivq`N_|R(%}eZRdmWFaO;+RfDjPLXUUQmGi(^E?BEsx;qg_l)S0bYg z4Ln|W%-n{N&m9~DGj}+4LJIio34ygnVa`#u$d>Xe5^Y{42%s(AXoHvBZjKaK%cF>x zsJJ_FLuZVajdbG8s^^9~N(k;WCO^`weRmpKd;D%%wm30 zr01bbSu1RSTkl@(m3uhcHvNcgs#^{-t4eZG(*|k9z1e_d0&b}gsdN69q$ZHL3W)gK zOnVo|zpPw}h|wC6<(cuP74Kl~Dkcq}{Y*Mw#kW=#BX5#P^7fMVTiQ#cI*AMz?z8@EvJvM=&TMQVXzKD4*<5D}n^@Jz(Y7h6g z;>=MTXB6bailf#~uV}b76LT@bj4y#LnEt<+!X=DQ8iW_cz+0=0?4mhgj*nII4@13} zF4|v7hXl&3Em-!AB9x_1o*UX+s&9Ua5^-e+2QcOTYv{HsRJH z&BV?5@jKtc#KNO_%%D3)26Q_Vg8b5Sp1n?_(H_0V=eWx=w3=WloA9^K;)mJ&_7jC? zI3$$RzK5W2oe|MIO|)mOXt)=Wb>L*%J_1^uVI42q z{yC}uZ{eDg&NbHx|d*#VaL22Ft>(v{Ixy)R$3^V9cXqBmzs4yMS07J zY`27*2dA%g&1nY-@LG@9KhZXTm)^&XIPK+*CyC{v_ouT?aMcUL6JYm;AFHHs*if+^ zPiVNj^d!Fe!gL;_y3o3*ZZ8#5ii2~ELL%<=|K=;zZ~-L$KA*_Mk`D-L6+Ab)xY4vh zmJLbry4_qFm8TU{qEG`p8eYs)a9T;87_RmQw!_d}*>m#?;j&ocL1^`cN<=mHwA(&t z7b}*0ZDSDr4ATtqi5+fkHo2q!HEu;n;nSjkjKj||=r-)W-wa0)`kEjZ6`Que5d|SH z1qvh@F~6CZh5H(~OO}_Xrl#Zpl=gVZcRlLltf}LaVf*X~vrImd+bsDm&=<`aE!#OG zztjvwk$LUB*dFSga#_``XQeQUh`G4G{NoF^YXAv>{>;MT%hfWG7lWA46c5kx>#&)J zrnmn&Yrl^Pc6f0c=B6Ut8Wx^eTw_~KCoLJnwPP#U{!%%qjk-P>zO~?&X`G^P;XxQCF#Rks`tK&L zQMH=eJv80;E#Zmu0W;Iy+bqw=6UCDy_9>DtH^XaeHyEuJNX7C>a(JwI+6u45DdO!f z#u1j1s74_cHPin5V{>9~eTE&10WonUOgG?9{vx*qLi=aV{H15W-YHtyoQ}VXq#&7k z*_Ju`g^lHNiJn_eRIZC*5fpdoA)V^p5B}(c8Yw8OTD~kG+UZ39ZFo3uG%w0_%E}g5 zY{eX>mV}09dIcXR3o{q!s>Opa*wH3PRDtp2`>BRadjSRubg&s^IBmU{Za!Wo9VK z-RNA+895R)HYQ}j>s>nI?_UOziJCqX%TIMTu#nK;rUuLP#*v-(GMhs#%H-74)+(GA2ZYb$TI}VtQ*_5i3PxU&<~n)4$hCFVEf2F3 zIDWpm?@Rfy*I^oZ*HjWpn2xOO^twOiyufhyVdDK(ct|Jx0>ukVU`Yyv z8<@-Em13K)WMPLu6Gtyc+YH5EzNo;f3BUyuHnMr6o~~$!wzVm&RE@3G3e*ibp7<(s zQ(5$7BuBgnpTCRYx)OQ~Y_O~YSeNOkt77-Sr8vWm*a2~aWd&0pulpw;L?lmnlL<`P zTEq}!hylKoFo^l|`}4nbK+2^l8)xof(99mhf9Gg7Mgu0#a2tm6u}}RwXJnyuo7yY$ zWD1N+Kb#5Xqr!%rVp4{XU>wA31*d&FV;GO}+7>&_I7~TNKn~}{oIs5kV^mLT8^r_y zqJX)|TQC5=`b?hQoDU6r~-a?-w0h@E5kjJ;PTbBc4^9nw$@Y*f6ve#4C!hE1(v zc(w%0TQuUOcvfDZ1fP7V{974tUVMIB?dPiNI=K9%Y9R-#etZ)ASzvd3lFxTz#(eAU z;s&1WDHpE)!{X<9cWBm_QdG@w5V77z8Y~Amo%S>yGk3L+VVsj@LvA)z$Q&`B`W#Z9 z_GFpIxmR_Ol%MQf|bHFD#n>JI|}p>V0i=}9}HQ; zyT9Z4Qm7RU;8j9SIx`S-KJnx5ZJ;ZeT{r&A0 z!CQKK`BPLeH^vci%H8RXD^__`fqaW14?Zox-ohWqNS@*Fb|UwA7Gx;;H@5iS0`(!v zoy?^&+z&ADXZ>*5m|L*hku79%3K+%9X4klUA6#wxJfwyzwfu;+U0m1B6W!wuFg)rA zS!PHPIrD;_mwv4=YdGeAaWqCd5~Cu|wj}(|7Y{}2o-UIN+%Sc)ym*WTz z2N>2K8$!*sQX5jfkiGYx5(G`W&aZRPT8rF;i)Mk$uzk1Otm~j!WkU>JxWFp1x9-R3 z;~3o<0p~qlP8++pW02cH7P8^_`9GYXh&Jwq%vqcH1Na>I#}RAId~EfAb$0@%*MF)d z7v5dx?FeqI=Y~gM&v&GRw%52#qal@qR^(tKe1ypZ#08z^RR#jErN97pQdE7u)19^2 z>#4sRx}jt1MvoXu_^Ur(>J7=OOqTUpx0G?`-jlxUmiv#N%cB~N{ZRY_hb2VGo@EwA zZZiOrz0B?xLgrYVf?4E1O~K{XBEh-8Y-9>)&5 z-F~b1>Wrz|qyJPn70spoLlhBF#p(6?;J&A_53FkEnY1E3i-{jSb`bhcu;xF9ictnk zq7hYuJ;(=rhc<2ynU@b{Bt1lT^%9F@&Dyh z-cdK^$JKL_TRpf9i{iA0A8ZDuVq1$<$p2Kd>H@i!!yhio&=u1i!N~Qlr>;eSEvls` z4xaMw?WX4Z%zmGoF78pPxXU^N=OxO3Tj-8=D%TApRjYOy#RhhMAmAL`SK-E$pXU~e1-vE z(a{yoQ|7*mQB*nAyl@`qm1!Ni=5Vllj@wj6o@M>G{=#{6#;byj;R2mi4RQ0*!;zFJ z?;BVa)K!lq+7&_Fm;Tg`$}%q}nnq^<<&bR9x!)4xAM=Emgz35mHVdYfgCjyucwvW* z?`O0h|7C?b#fE7f?0;EAv0l^^dF4#`%4w#BNS5ll>l5N{n+28Axz20fGJtX1I~Du# zzvuT=2lsI7=C@&=6IUeN7h3HR0Vzikqxqr<)G0tuiDZTH-fuv{>${lRAAa;hNiNft zc1-P&>;PhV&*^Z-(g|orX9MaTIe^IKwbJZ2xFkuZ$4L^7=0AvX!|w%r8X}~_|Kp4N z#}hs*&3kxXVCUc%UR=zkqoY$#5Vcho5)zt$7F<~d$;jBf$9qrsKDf*P&}LW89$o`k z+3tV(*2f%d$lO{K@|S?M{igJ|iFfq5Yv-{Cadg;f> z@lmUcn*D9qFE!RBF>rt(YRI`HNh}^fgL2KfqoZDqN5#Z7@D!kONv>SUq?%Ks#*O30{D(`N`>0tnL z>(oU3jW8%-8MzcgNK>_anZ^(i@f<9&>C_TcHUJP}4uD^FzcH*sJZwp~;Mr~YSq`=kF!e;Za+MuBIDc>4(Ng{*E;Nr}K9A%IJ9 z?|p#Y`@IaDgS6OZ(GIC9b+6S==WiGkX@wPPAv1AOdz}KTa#gEpLH5_7P^c1!40m!j zZ7<{WW#Y@a&sbm-8)U@09kRYbuesK&k!$UtwX0$)w(rY6F(50|rI1MbOzqscmyZ?h z-+QY6%|^w;1n2|iYh3)jc}02+tT!E~C`HB~e&b32kL7V z#DBQ{s8X27=Frd>JcKJNqS}~*CMpXd;tdGL6QN$2{=te2Q>iYw`foN&fAwC{`S@gZ z9|*qsa*Mpo*#<^fi_9VPLbWQtty#dql5=o3NMOZZ@sOY`bI3RPT+Z6)n6}tm^4!Gv>Xw(BsaSllzQ^337qD-KQ_>UQT{q8^XI}i0TWdDXect?9eVX zL!O_G;cQUR78QIBaap#Cx7KbGI$6Ud{>68T-Nl{V&oCF?yh?3(b+x%OlB4yNEFC`6 zBetlcxBm63^e4)YNsR`{0QX)&kC>jRZK?ic67G2Hy=BXf%O})I| za(a9}ZDb3~z{gN&>OtrBs#($}KKYCB@U&F6md)Z*F0t3NKbjZ5)i51bj}R^;gsiU( zuC*qu;VIkx^;d1O$?47OgmJjYVo&DnPvp<$GG<9TV9>z_`+mR#X(m+|W49wFQ29)U4xjGmpNl)yKyMVrg>^2^1 zpFRd1?+ngtS$)Z8HvgI-qrvr?jor6`g*PreI(Z4Eo-OjU7(g86Z!q?&_t`V8*?+4i zh4Ds%R04+Zbw;F>A3H#>vUwVOh(&##Od@qcDEVYLrl&Wcl4; zYH}x3@w8^|bq-fieFd9LsFh=LwO)D2!-5g89QI|Q*iKkNT*ITo9Y{ibW!MbIUt`O_ zR`bIh+8JE}d{A}Ie!zWB9?t-I=0*L++)D_jVt+Y((4n zA$noFaHC;`s0CTk2)%UUkih&aJto5CH@M>_7kJys-|e6o!Ax-C>P4TtTvY)XLtH4> zpci=+?k|#rXJ$5*j^F5(N0$rcrRsULG9J(yxvl|oh;<~S`HUDul7nBaPgX~mQG&g00GD{1Ns@V*k>NF_ zaovD&y9FC*DLW4mu8;B@jr@$&vATYDhg5n^YgwwiI92K}dNSxQYLO2#LQP*v=$i9U z=H(6mUSm9O|MF|u?(Q*q+k$AvsI}(G?p$|czNGz<(xUQGBjIVYkGbe-9#;`Xr5fMJ z-h18~fC_as^K#%J`$`H{F2FzJ>VX#P1ON5+Kze*^sboQGFGg)OZiWZUZd?tuE}ZyOSaqn&DjiOTu+ZuJ z(#Y!N-okw}pxvN;?KzQ)q1zb$Co|`(>QbD%F-@@CDQzthV42dUJJD}w)j9;Cc^aL| zCG1?YlFLHbhME0!Sogz0i3`)6Yln#&{BV!?PK0m1F2?8e__$oH-W^9TLM*M>7?a7WbHUE7?Pg@B zhmF#!B=hs$j(i1WTT#xPw|F;N@)O-pBKG&^#_f`w=#-TG%VS`9^oNM8$In+3;Cwr|8Lw zXpu%tQO#sPuUU6d5F1-b!DF`lsF+aJgwVbVmem*cG}anQO`agT8=>Vn`*V4P{O!hb z-yb^y6PIq~k{+|!x>F;T@9CR7UH7gj5v8E}yyHNjAL<#tLOAsjoc<+3tdI>tgHXGp zhW0H*MMbI%SlzA5DYwF~#^xk9qd)R!FK>D=VH68})=Z8%$HktTL(xwKW;r-1)BF~Qct}d@J;Tr(9ztj3%rZ!KjP+-@7Vyw`vc1?7*gEcI+u|Ql! z6?~-EiFrOXVz%BKS)qCCFjgL(a)7&_PJi|G>x5x!i?DTg3iTh&I-ty_{D~b-HeSRu zck5?(^I_Y>8d6WzH%?R-?q+?^#^s0WeA&9*_<43*kjw4vwMH?N2V7}v_2eay&8uU! zPFuwTkzi;S^~ghpnQa>}q-1C&9R}}o34ELt5vLr8ZV$_=-Dr5eDpd{un*h3A>Q$Fv zX6cLIGgS)kG@idi=Btx&%Phl9|8ABpFmuK$eNc8dwE@%qx{ZPv8z)(hmM4rquuRaw z4si~>DO6V~lF`}|*B|XqH}G8ZH)~&XN1b{vGsZm=S2?FT7jlvRWD+BBMaJm@%M01H z;NNE}e@=?&Ljd_%&sy@v&%zmMq37nWgK>5buFO540DHW+V_Z3yJL7+}wHhh&%V)3y zlU_gMj$$ezFHW>bI3Fn&{En=L_L6-a9Rb_jCx~I!RT!^3hVB-gtR5e4co}{@+Bj7& zUpg@zeU57694omUggt*L5kw%)v{CmQmJd5#G0Z#b`D)p(8a$)Uw*n5pRFhePO#7EC zl^do?*`B~$+n;A^SGZv^s=El*H^TwUO>>gr)c~MYlrsb2+d#x)6xG=E6 z+TF=YKA=9O`vL!CzdBW0nU5L*kb+-&4*l7<&G2wi&-7bD9#sKE|Ci&B^k_=(BLnrmC((KjzcF=146p|p zsx$F)+A2(xPbf^IwJ9+^Lr;$$<8%H$oV|BE)&2iJZWPHlqRcpC<+3uOtYeg{B8AK& zGBZN9bC9w}C?l&vvXV{4Np@Cbk8sTFV;&s*o=08RRoC_Ye7^6`@2~52yRN#<>$x9~ z`{RDUKS($&SJ!O&Eyp&wuj9NQLntUI88M0mj(h5VFTrM8{{5KP>I2~t{nh|JHql|% zEPB+Q%uU0ON=AWVac0KddNnbSC&m4> zIzi4En29+*uS;oL2;DLA*V;lDHC3+CxRH0Gx=vEAN?$^Zqv|_5-VY{7?0NNN#M)DQ zzQCZYYM8P#qnFuLj-KW7j%{fH6#*%?W&VX=~|X~S^R(l&CX=tPIHOk
Zqw2?=@ms;j5=%Qs_I!* z)>`*=%rTc|YX0NW-F*;|5FQ*%=15>DK>d*))U;2@;pW-B>10cIS_^HrE~k+Ob8;OEyJ05`VcMz9p})GLOmr$?=tCMO>o$>Vgq`59TMCZT+cbn9 zNg!nAW~sy8F^21NYm1T1FiSqRP__A0Z64zwow3C#ZRn(8MU!OUg~U{ld|zY;H=Yxm zcG+hM?ERjPFp^ncitBL}^6tciiC7!ar%sN0;r^X@QNw%#-N#2^-30MZGa5_nhTG!U z-8(aS;5JW=rndN5!%Ar#Pj8Ci_FDmo725SW0+eDXtA)=94{$@Zx>TqtwWJSifK9t!E}$jWR}gXS$Z>%^cd&bi-0L@gPh^R*ckyWVMF0BfHFJE5d#8 zE4CHr#kVOHhAiE_z3p?|hz&|THszdrkh{P#mRoZW4x=w47nEMF8Y*@!yF(wk;sC>eFHX04)Emsakg5DNWAdWf3^TWgJ9*MH*JPei(292s1FA?g=f@+so z&O48w8{sR}fGnnZ?fOa8D*sY~u%c1ff=sc(?gYY5i8+v;(6<4gjOJKTaey^88a=nkNh%-4Y z@fPV$H=A@QKW0y^pRnjr`F2i`Pr5CfZlsbFv&baGQoff2x{5afVs>#fR*#xP=J~~N z@qInR~Vel#yD{YK(>*~(2s7IO=8l=ZB zmDwA7QZ(LTy_CzkvwM7V*{j!xB#A`uVgs@1s%P+iU?iSh$YrNagbry{l%NPpBpoSt zQS(RB7cs2;L!v<(no3U6JY41=5m$GY?&;Hp`i^9&-jR`{fl|dE7*=*B*4#*GCJ`>u zXjpSuL>_LQ?fABS})sj-D{b8_=XG?u)=$C^7l1m zHkv(LCR3_y5ZiKt2YK;mHAwUQre^hqJz|e6&;z&mAG!wnLV)et{GiI**1)vBxW3vB z(Fm|(X3)LAAveN;)eqpdXyfv5XO~Q=nxlH1QvvUM_Fdk>w%E;;z*81HpOw&#j2bG3VRvehtrXBs}& zm(^Wo)NQfE`#ZRLqzpJpS~EceWnnzD+jJ(y^*ohr3;M(i=eoNY0KV#GQ8AVe@6m20r$h?S1H5 z2n?rbC;@S&Zl9RketS^R5`9qc#opzxHjcac)~@KKDA;nz^X0nZ<=1rQ>wbR~kK}1T z2tc=F8uDY+WE_L_n^vD$!&+){&Y(K{ZaN>*T5Mk6V5fZi6^oTXWzCmRxVx*ZU@g|) zGq`Nj#r;Bj9b zEXtf%Spa((kClM6&e?bnS9v3a*C2vXtSw1dL(g`FSzMF}aOv}GUm65UY{>16a71Pq zl`Y=9FO{aYu~ySP@iBN$&8hR#wjsDQ*tmOY0F9-0YP*Ezz|dB0d}N2`b?epH6_$s? zmmgS`mMxacvsBMM^IYC>(B1bWD@I8Ba#zTZTM*bK0|{H0+Fm!wKa`5ZA<%n}M}e7a z=%{V?PR8^V!1WeuwXc|_0~MpPF=XW_SnbTf42jqS zKI_XUOQA3tSUheW;GW53{OADFv;)r#XOxjXg8|C0-hkz9JwtSl$K}J>g-|@#0)s~bRR`rQ6vZCoFga1A0mRp`YOzfe_*AvAP z5^Re%_f}AF(AyBcNV^&EV{sfns-+Sa#RVUa7=&y)ZpJ^aW4pMr*eG;3pWjo|>7&LX z?ODb&B4$*WH^s)Zr#B{LyJbyU$?z10SuE?hpMO~2%zDsdf^c65C*PULn49g#>ljZ- zqU`rQtf8F#d#W*ZL*`~HPfyjZRAHkBV9`~Ft#CZq*^>2y|D9pjdm&zWApiWW(&pa) z>Ap6>p%$J_fOfLTa_FHw3IcCcH*}seQWAG-ttsT;FumY8RbO54c%?nH$~?s;i1c21 zbqC?}eaYR;m#&4`dX!_6^ddhtPJ0I=PM*i|c+5)nG3racLf7p-EKzOFcFG8CF}HU5 z9FUjnMeK9I!{kKvoV;ak{grnYwb{SDc?Uz>y=F6joQP%`_HmtiQI$srrlB;Ldg;*= zDe+?IE8eetx}`3m`>(tn+?IK;e1F~_B1^qT`;mXbWvPqSx%NeeUm>F6{dMZNUjs8h zg0^~!ooMKLCaYk&Qx0%>%Q&Cgigb4wdWV@jPOxo`c_dH(z8YRwGlLR zuv?bjmbN|vuv~8j!JT&VX-+)-T=c#hU+nc;ov4wuW>at0BG%Zim+>hd(VG`4`2_w8 zc#ZK&1~nJCi+i;h_gY%JT}P{~?a%9?Xh_pQd=}*igq7K8u#f^rEEMp-jXbS|-rS&B z!#)WkD-i;LAc9P{S#qb5KNjbiS(>P(kdZmbD(U)ZL!b;*y}h2AQ~Is;$>sx(;!23x zTX8~#zvMnPI*H>lNT!g~Ht>`(PN7REyV(W;oo73Oryq-*^mY`MmgX1CiA_jg6kQ0^ zl~qQ((fLud3_lIyLjcU%)tPK)1t{DK3Fq>C_U~_g;mG)8saI0aU&Sxwz)Zv*3CMby zdw@mC--uf+He6rjoSnGfPSBnB>8l(j``OffA$9ZA7XR;;{v1gVl+`@4|6-5X7mA6! zsThqOP(u|E)DXFNG2@&2B;_WkZU|7TGGGo$LCq(jbtio~-8IE?o_BL+HZ>=dUMQq> zfzr5h6>_)=nU@;dxnVRUVr=BJI3Qfm`4b4rx{2-=rh!=(Rr}mQ96r}=B}pFd?MGTI zj}f1 zhP6GDI{;+}A zwtEr1kNoivD+R$UyShXB&= zIem7RG~?+wOw^2Qau!C-(shb~VT*k>Ns7z_RPVJ`~?|SYv6Z zMC~k2=hGQkwOFjVN}N&OTmmcvK56|1t9P)EI^6z76r?LHhqEuAASoE@A^H&ppF)?t zb90xUp&CPIBy8+A48v}jeJvuJSkSTiP;K&b-ahBg$t%`E#_t#H+a1Es-k3A~j>*T;_TOaCz6urIwEU_zDKI zS;Go$XMM(}GJ@~X^Q}=EZ|p?ie54PcR)RubyGQ+HrOSdII8YL#>kx_^0~ATF0-cRT zNgr$-^o1aj4obMVvLb;l^V+umdr05VfpMB<4=Nlzm5mO2SU3AnNVR<&{J?O38PZja zSgF}|E_vl;a+mspQWu&GNVauO0R>uZhm`qPShy?U@5ApfhAc zsO+=?3K=mva6RzQF5$T>pC17%jiPn6^0_Q?-06mML8%$~g!R?=z@UzP?@z8X$iT|4 zS&i)l($qC$#|#c8M?~h{d!Bqh)JueZ@H|m1QBoF{2DJ z(3tYf zRH5O@N?bAw^=U^V4QnUf212AiCB!WGDFDHaV{@slu10^PH`#$5&K?>96#pg!<7_UV z!X>Y$C(L1~$ zg?riDoi_btV%*%4k%{dUEXfg^eL;&G9dql==ZZr)uih_Gp{BKggN6H{Fk0x)(f9%B zS4z{|3Mc5B)auN2iCNXb?(%O zcX6lxC}3qrbT$2Px%DZ1J+|H@taBbVV$fi2oxU`?2$O!nAo=ESczan`*!a`-9c8cz ztf**)@7^a*SGNmZ#W45GI%uiY7Sjdka1TO?4p=<)f*8hZY+nrJdh(Pd>NbsAf0_w? zb7t4+=1}f2)1#`yPalfWE|8EKl-dd_SPI3oDY6o7Aj;L8F3Z|R7tXvhYt2~Viu1?s zz*f;op66vmy+F@R*BS=D_{mb<>tkKO&u_11I19N@eT#B#a{Ude8 zzbB#Hc4NMp;!H_X(-^LxX==N&1ZJ!=Ea++(mFd6$xG>D4Rf^Jad7l?M^fosSrx$|1 z-8onpfV0w?86+PB3uNDyruk^K6^V{Jj{kH$3yGfdxmZGLkBdh_f+bkx1E)u?m^%iZ zNyl#-hrBQU{wA@$)`69iu#E;r;HWAqPkSdz^>gf*;`I6n%UnxK-lNk+(h5LvaB(+3 z(b(jthk3PFIQ2mmxS&|A`b^K=lc~pN8SI!La?!ij|3TmD@lj4ooO^;qU13OgaZ!Pd zQd~c{C^h9e1@!A7i|4{qhOrPJpVVP7GuU9WKA2#5BXqhFW+G=9661b?@f;n7AlKc>zr7UjGo#F0@#0j>w+DX3R&^(A-5d%V5oA)S!LDKjPom) z9BnK=-Uvm~W&YWKr48`Xm=4n@IY21J4yRVg2tQUolyF-}Vi?O-S9j1Er8Srp;u?NM z8neEA`{WW=_)|MN;tJ}qsleQr)BcR^-@OISY)KbfV>z65bm??hxf%FPI^FeK$t^<% zUf~PkS&~X`+S{PZ`*pU}!@7xs7JPmn);hU)nJ^j%DF9zNrj`xb9k92bPWYh2#K5CI z@~I~gCMR&!ueMg+1@sZfAtt2?ItuAB_#6DDgW;*l=Z$wzQZ+Yn!+M*>6XH z5@7X%%vEponU2vG_Po!(Tmhuu4F_6)RipI=bKd>#gRc3rxGs5@iNo@zU#cy8`wuYp zZMOT}LKlS2NCt41;Oumk(h?^L&Ceh>t|T5;Q^0LF#&w)xcW<)V^M;2nZ}Tl>UIz>) zxgYhyA3hkPY?$F9%SgsKU@;m(d{-g%WV~?IY>F4t+JP*PcaWr7cE2>`b*u|!?h%-^ z7v4Ut(i+E#UuT_g0^{TxpH>x>ziRm`;Zc`gq>WoAD zPILfz7LbyEC@dfP*F(+%_Yyp2D6lt9qwt6Dg$pJYsLm9b9D878R*>3590G6aY3r^i zi<~mkI;8yM>!f(-{t(iIOPgHZh@`gbD?DBA;O=QXYN@*R^_x_B-*))ht%QB4&J#E z8kgmfo+4Tits`JOr`&8xy1gpD1-Ns~&?}lYhvC34Nm#n*`dLltZnOG)n2R`jxKi05 z=V=tqcJzbxs8g%JJTPUFfW*|%J9K;2c!O%PvHO@k6P{`VU&dPsli>L6a*SVg!1spw zdqAvewz*un^5X0PTIfKbdi4|e%*qWXqp1LM_a~Z#Rn1pGsm3?%iCQ5qQt+=Qo|Fzo z@BMTMqzi^rfqHTZbhMyedK>V6Ooyq+8iXF9uXWEcp1le3e=SSeZmWlh*A*~16yG4; z*wwoj@^VnL?H|z)XwW9Fhd6EGc?B|uxb<_`*ZFjq#DoOl%P?Tgf?nff;6WVjjY$-4 z$E4!wrvlF+@nN&{7~ZCOIt}kE<*yC{XtISNw}8WG7JXb_Y|vQJavEl=;7##h(r;_~ zC*qme<&uGo67wVp?6na9F*%kAVxHR2vD#Hr6omA&~r+BZaY=VW(!50z6c#)@m zGAsUn8ZaQ#ULT&g(YLkcNI3&1(3}RT zKk6}?j9ydf${>B7GrWhE75Px{0C9|1GPKwmBbph&T^2N(NrM$pnvb#W+0D3;hYl1{ ztJ$tzNw9mF{}MW(c4mX4ZQyRDYXTRar8kucbNo(BY^8E4^HwX%hT|e+e|I_#Zj3c; z>f=g?d9LNHB)#4yN`Cop(Gn&KKiRv`t^dBj@}oDXrFq@i?h4+K&EESIy(^`Y@SmPh z6K-4>ltj1EZAAxfsKax8b!z~xV%^igIHT8mlQ0tg0J6MOWeIx55CPN{!)i+ zpU(~g=rW%}ByDKTHZYZ99;}OFIqz>fu|mSCWqd1Zja9k(WNWCcP`wN66qmaiB64;H?>879`B{xJ#9P-p|kxehwMf*0*3R z*MXhp=$SpH+_ z>6j+1BkUAxOyJ^iR`TSE}+5p(%X9=#F2*8ZUoR_92KX^e5hWJ2=kK~ zGs|kuFA)VPKyG$UnW<*vhCQv*aZF)Bui-q|g#`}``f3qnVqmg|;+VSX%i+$&I4SC` zDNpzAU1o=>yQSopuO#eM_;pIzk{5_m-Eow0UA&`GA#@t$jkj1kq55pU z$)GgYEe5D$w^Y{&y~VJ|$V4IG)$sFIsK;=sh}Ynf75nfG_|Oo(5PP6LIa1*~Q?54r zSuqc-`(30kSFM!uJ;at{#r$40R{KL3MY+GV)Ez2EM#csq?t96qrW*xRyhsKw<>Al7 zLx1dE8JZs0(y~>-b<07On77Zs$Snp|r+3tZs_*Td+@)~ZpOHXWTP(f5vF1!{eurKp z1f{XOwc0>c>Mw-GNUf=aKuuSrK3Y=jdWzH-1E-0<1PaB=ngE#cUPTi#6mg`brJ+p{ zp6aBbP-BLl{YxWZRa#?jc}~N^-{s_EClaw!zQPc_ICE_xVm{rO?gzkm%KV;noNdK$ z>1{8@1U@AG2zoXXDg?@N&p$cqc-c6pwHaJB>PBp^R$*yMH&=L_=3qlOI00>eD z`q$mQK-GI5kY-ZFE^(*#CL|_`A_!KJqj#0TTWidf|C($xsP=3#IHMc>kMF-SgRnq8jSS~ z$!pM-=2W7{mIGhch}P7u##F?X-mQR6>SC9TG1a1&IY|0^O|?fcu`{`p)%*^b-atzZ!cFi0H7GDyqm}CRE|RB_@pE; zVww{#dq5U}f6u_RL18jdYeuKz>z#EKw+2>zEhrVtY;RC1*scapwKV({^5@io_v`J3 zEX;c%tn&(QDU&x)TfwQ>mFX-tguNn!#24d0qhb!`y*>kngpnACf>W*gNyNyONByf# zQ`b}b$A+(am4}9FR6^)6It+IFeRVQrSSK=~piYL|Km>Xn;_~Rixj4@taiEO?_1ZsE z_*VdZ!mfUTu73Mw>_}}WP=199>Z&*X-I0TX!_@oYcr>ZB$_;%brq){;G8uS%vFO`| z73^VAr>y9hINA+&2!ze?^0p%O1->}HUk2{m8apC?kEz6PhRnMm|3z!F94ltMB8;CG ziQg4|gZ`dyB_&KKnOBzeXSUHH5H9yUQ`*`iP}fS;oY!U%wvQitsZr z{_rpu-Op)=arWClfc77uI=BnxeZ|Hj^N-Sce7tfM_W*J;#cTf$%46%}d`NJlNiJyC z?LJSD3qi1ngR4~qQ?~;C?VqM(iblVTd4V07ei3-R{bMm0&W=Y`0VW&Z8ug|3HaOVY zwgnN_Tz(Ex?xPDG>=NIp@osM2Eds&tPclkch9%e#_{F%W!8^|e)k?c9I`jy;BHGA zCn>$w%t7u*u=jr96pYOV?u~gus6bkvCRp~`vVW%zdcbBpA6(1#Sm?fitJj@FdKC6* zi!1Qxt&2veVD2=%gLF?7(XHCUP8_okNk;<8HMr34=b9lSF&HHMgm!&*UY_JRc=cH$U!iv$?D2hSD!7u#`WtW0=L} zejhVMIjC$vYg_ajL2ebtdRqZUx7$27^J()G#T_N(~I1F@?~cfw!1Y$ z;E1;m6FBShK=1Z&BD>R&${(gAAe8!XaDE0yZ$NHz9=Y?ky`sD9a?`R!c zQk$3fFDFR=>F#@+l|zscx!}i#l%u)4$3`!eGut?^ojwOP11YBf?~QjRgJVm%(7w9e zR}BypN8r?oIK7)vq0X0OHl#a2^q&_QKGFLtOu7gl0C`L+>EDp3Vy^*ibveVc<5{^f znlu-u66Z9eCLLpHWqER`baCZQXR-`uFY!&aZ=!LhMNF=?3u<^prt(F0mqxe7v})?8 z;0QQ7X9(v7KzyVp^%G^ozmNaL&tBm0PoNxG;i2>;)3d?rI{_JBtm?7BJf3k3BA4H` zlIJ~}9Czdi>lNm7>@=|b`}K_@h|FsW&CZa=#~OPtV(>Q2i$eeUf5=0Vlg@NP*#8l4 zbJRNkT*%_3l!OEi)nlM%3=0oW*fZ8hy2QS96hfJ3s$e*HL5Qxb=!k3$&KA=_IQ(PyYs&Vy5}%ZH4KiFwQq^a@-*0(sUK z-vp5XrwVHlMUfsyI~Fg|zN&@$52Q~go?LpJgVl$PMwJgqmsjXXKR+%eGAOgxCWWwy z*&P65UeWDs6~G>}{o->$*KSdd8nqS0*n3&_{%+a8ib3?{-XiNz5BNED!38PLFs#5v zR9E|x1&$WNWRUQtk*?8(@4Zgow+9s5>|U1mP3AttL(`f_dn40-UVVT8@P#t73r+eU zVK|E88XZZUd-W$4w~<$X?h^ZC`^CiblOSbd^KdsIU$$&0d!=WTrX zsdzsT_RupUD7TgB?H5AgY(DPE!982dWI~%gb04TW7O5j4?95)Iz{N`XM`CeNXp_?# z=*PVu?ELdKmgQN)w!sV@%Pr06ak=l>tK&3U{DIIyx*>VJOp5dIlN{`+;m3j9qS75{ZuetkcG-n$B+W7j1{ z(_L4-*`>?}H+`Fw7ji#&j&)0+*^~(KP@ef)nM0~ap=H`we;i4JM%sev(Ext#`TIu} zU%w?BC%KSMtATCX&TR&J{#Qe1;zd4{ZF^+m zdhg-G+h_S{yKOeMw{wgD?ot}*wx@zf18mv<;t5a!nvmV}T4DC9=1%%$W7VX+$=6mc zj(9f78akGi zo$dvj&g5(L17f+j31hjee1KN}7ki->eIt5@VouiaO zrdIZSKAx`9sv;|kTUFcy($*|sL=fCs+Yn06_wH6O077o!1MYde`Zdn~)kIXAUX56a z=Xc7B($On5+R_`xi;(Uo--2wz{C_s(5@-R<8;Y+=%RbIsnb!txHCGVnKHkAwQ5K>( zo=t<-g4yic$3|U6wUGAx5mMUR&mli8OQc?(hsShK#(*F$$Zdup|BIP;u{uL%~>Q z%%1b7TAp_p;o;k3_slGAx1dSk5hjhDTTu}O?PvCsU6%4sr!r8yTa?)8cA`%mi5B*l z^fs0A7!mJJ}6&0!IVT0(YT@N!~jU0niFS&Epu$IDoiK|Q|XWCEzAKE9g_gog3=7}JFq&A7hA4lWJt z-}zqWi5+beyAR?nHf5E;M|Zf6w}L47#HdT|23H2fr`WIpB2r%e+*j&DZfkCC%&xHW zO)EOPBX*{+XsjLTz~bhV_s7zarONS9BsEILYb$8~4c*a8e{|*2|D5JqM^W`}IH61f zL_?r{-1Y05>uB(uAc6;qFZjm}0)yG3k`AyD`Ab_se;>N^E$v@nUddtsINuQ|w= zeZ}+=3c(*HzcW$j^u%0chiJLfD42M;GzQJHL_h8ZT9s=Izu`oZuGGMvU4p(BaNDDC zFXUO?j5#v!`!6m^&yc^^DtV;uY%_v&v`)m(#2fNlCO@xoR<4Q@*|NmbUnZ>O5v8Jf zyU>v4eLAovj{{Q1utY9iyb#0%VgU7CvO5W6#qmBdD4L*_M6y%65;Y z{BU!wE6o(|2KZlGjM^HQx_vcjl#)yrZF+DjpUbQI&TqrBg$xZsyw)n&r(ntqob8RK zMg}^eSgv&y(>mp*5mjy0GV#tjE<)BY;+JNVk~;iB|z#?uR-%(8a->@bOvVKD5O#%ioCWg z!vjWmO+)5(DS}KId&2I<{~_PmirRc9-Ak+crTZ8^ub0YQAQWccL%tmH!bNLYnRRbz zKkT0RgZK9H`p$ze1~=yTWVMh~z694{ad9x=m-M3-#w{g$DzB^HfZwzRSa$oGrH;ZS zwU^iN+KVHB?0@l~f!vM&^8O>9TmcpycvGEuveVtIT!0Em>P_n54c_HTjG@-!m;kt$AB@twmcw*F$es>i;FA!TRw%!|rXT26%f3$Jsxr5>x&+-fq@u7He2K0rH#<>A*mze^~D_v?%GdNM3hu#V!0rdj|@f9m);$UhBylV5G z7o~kER^I-b&mn?11uc;G_4CVv=nZ{#+c&tI|EQP%h6W=HB!9MH8tOSxkx62Y+zu*M zm{-?9xT&4Uo!J07>upSYt5Is-BE(;RC4=au!N=$h?!Qx1ix4R5!4p0YHg7yT?t9K$ z5%6YH1*}0c|H){62q;efqtZcKek}r|t>-{GDxVHH`goA8P-I_9)NC6(_E%Ojst#&s zSN5(*sUPs?4QHos-UK>#aP{877z>_a}nxTR&!zF5N;|EB`m+H!V?_CyjTvo^B zzs@KsLyw-}L|$al_48XVqtSn@%Pm~=zQ`av!^Plvv22LbS;*PABS*O3uo$IK=s>ks znIr(77%3)+Nk#qe70nORu?BP`jqabh*+a>czX2nK{H?{oG(d=dx%?x2gc*l;&kG@zBztBPFI)7d1VCe3f9zppc z)Uy*RFR*@7ak|Te)c?pl@v0(;BV4wqKC+1H)YX*h04exc^l7I?<<`V=Dde31-hDqs z%0Fl{pnUhQzsY)A>n|Y0A_%TiS*$hCCd8$nl@%$zz8>;>QR+4JRAKome8iQ=4wgCy z_}Sqbbj?RrQ%I>i6|L<-N%)0@}CF{!xYHwo|0PtQLwCo6fXG%~uO z?!^`mT~WQ*yj*a;Ug+|^F4-}_^}O?aG9yJ8EKxVG?Zjwl0NlNZby*<=AKni3a6}y$ZOzGcH7IV60{*u z*Dve+zOtfgWMpJh7eG1VgA>f752<-|s^pDgC>&Dn5JQJ*KaD#57rO+s5ul+6ffFBI zn*U(>^g@wGsC!s*_wl%>ycCpA5SqU9hCM~SgNe-zX^rsoqM}>--&2t)h-p5bH#+&o z_ks0Poe1w6?TVQf_j^D?gdSaB<6?7v;kK^PnSJC^319uD;``FHYoYK|{V~y8ajNTY zI||yf-JGNvH2#JkL*RF@cs&F#v%1*`GMYEQ)P}q99%dpCnhkUhg187f6R7*HiIWTs zRS4Yd@RF(K<*xL>V_Js5NH4y%`5cCseg&$U)@3K&(oKyr%678}WJ;y#*AOluEl7wT zcnxUxreyez(;N+@Om7#S>ms!eStN~|&VqZ*I+l?4UDO67JiFKZQo;dqX^q2`5{O3D zJB;4wjbgyD+9)o47WmUL;?Ld#`4{9q%K}F&5n)A-lC^DMD4Ye_T~=Meb(d;U% zgkGbd3CK)b?~!X`%GEDXHF6!Cp6@(Z6e)3<{==E^d9gj8=;g~7YX<}|-Y4S1EmxXWMGd2Cx+}73> zLDQx3$ovlJg6go<8FBj&E7`=3Bo9MrDuSk0IXMPl4#zmm5{oxnuElwvS9I4{N3vLE zLLc>%KJ)+An9_q8IjPWrfH|b6i3=fX$S?3YyJV{{7kJIkWBR$p$DV!(Tau_mi4{$M zf3tZFU;)v4M%PfU6qzF;CU)_$EP>1y?QzkRcd==}Vguc2H?L3 z9@)T_kG(qsLGffz2?sTZYo=7)-oPs=^elV=sXq)CwvHrRH53dPfZ-+}rqWqIHWw4- z>n3YYAv;T^5@_CiQdIL(hSoCMd@X6J7|p|8ZQamHA30xP;8~Crmo}!tr z>W)3FKy_|JzN_u|bGty7$@}n!M$OcK ztgSrla6(ii+lOXkum3IM?)UH39Nx?yJlm&}3pOYd2i*+=M(r&2KJQtI4%>45#*e>+ zJE`~r`|2!_g-<>w5-ZagmTJ`0IN%b!7PzmlwJ7Nd(@IGf0T;=kW6vhw98rczSg<7= zn?3EH^)(YENcEB**r`3)8Hp?H`a{n5n$ny4U4yN~%l9I5-}3OHRs1X-40c(j&;gS) z_k)9)w?3Rg9BOGtY|B%yuNt$K`yY5pUpPNX7}!LlWvBVvcG-gDEc>166}<#OcF5}< zYU(i#F^t77XPZc)ycpRlfY{A?^l`@Z^q`iIJKKcPD?##E&g z$-BuBvioIOIb+^rMNW!wz){}(z+$2r5&vDZTS9~d!5bsOJk}#_K=>P}^Mlz#hmK)_+avGr#_)&Ouop=-BN_MjbaHRY z><*~;XL9z#eG>I+yerDXbv|}%B}?yIEI0tI!LCZH!iR2D$0xn$Te~18gs8R=Fg}@= zVTw2`pJy75-V@QFe%-Dg8-^uBEILvxa;3UEFbl5N$r#5cH0;MeB}$T7AB^?aUWc4e zahlf67cXkpGEjfy5({^^u2*$zivu|5$qMrgeJJ6)s@%$zj(b=Au6?7+c{YV+A@$zQ zT29+;3sx*UkG6w{weaye`9$4Lz=py1P(jQy)H7?(#CC!omMDKl8-}M*rfk6s?~Ckp z(DUrbDW$TvH)NWdG7vI#i9OBO^cP7f;*nc%5w9Tb znxCeok}kQcK5CUi;iiQ`hpmp|jS*h1Ex->W?i_OF=)x^6NHdmtK7sCnOiV1g$>qhj zAWFhTz6r7vVwiL}3c+C#p4T-K(qiXA+3Z|XxVVr^NyL0UmxRj8$cf$?w|*Q-Q9enb zKO_);MxFT69Nq`*xqgLH_yApu2}|xmqnZhT8#O@{4sjORTIJ>#8v!z}GN$?=q4v7vguJ6QGt%|IFV{7)YgrRjQy6Zci z7XwHoc~DIObN}pV)CHfj!Vl)^I9o?q7;XE$f#)rRRNQD)Sld?h z-f4w*HKEq_6$2q-nFkwc@IQi$(x#6=SX5%QUk~93OuZ-5or1L&hTBarpPp=xTHSe9 zXp%zAq`&e!E=5im;8ljeP7w%3aX`G04N(uo#^5b%`J{78jRV1pC^~Pa zL-glv!8c7g396#{#+1WChFWHOoq|b8CUXIz;#?4J&`@FLD+Q)#fk!?;AQRggrxZL! zB5+|QI?)4DM?jDE2kO_oSW=(UW!Vu0dcCqA^dOui*zc2tH`G4rBf?e%HkrI*sI2D* zqWH&Y(rwDUceE3TRa*thMnLZz+~?>4ZGA1dLfe8uQ^ib1O`Q+@r^a3ATXl2k$|n6~ z+-9WF-e~ft?+CxrU&!$#6eUTRqh;}YIhaLy{pu1u!xz2uX@OQ88{O<1MIZYKX_q-< zXieyP?7hj=hrNcCg1e?f+b_{h_uABnf#D9AI4#?v!I#Ge&I#nx^_&?;8tlEK$3=qK zI!VGGgSM%a3A~hl2{j|V&}o(AmUNi0x1B8sf<(ZrPd^1~xf?=ciJQ@g1t>3L2lmX9 z^mwk;y!64r%iFS5_Y%&Os-k)JsYdK>;pWLLaZcXcWbO@f@>ezpf~~mOKSZAY@Qgy# zccvSL$X(SW^mTZLS%vJ`d9c3IFX^i)eAOMZ2EwRJF3f!Nz(pf{Ima3r^XG5HVZIDw zm$D2^5o7VYA*^B%k)1)CWhM`T{id@^m!AxHPFr?PDk-xHvNNZM6nPs>8q5f-jM$UJ zTgOFZoGNTLWur$Y;-0!>|Dlx1ENXyKfs!Wi?K1{+3U5qE%Q0m*`HDOCmgLaaH#vcS zw2AO?Q*vZ7?y1)CG%81*N*6#o1*`7l4SmseJxeg9s}Bp!UP$lEJSF3dq!snPylc)r zyeJ(+R+9fqV()Ywc>w&nr^rP}cQ6GH~X3U$C5aE=$-MNF68S8IgCNGyaSq>|r(-QA zVxahgwkG!F;DWof>!Lj7lJwREX@rqE+T5iTe7F^*8n zpyk}XM58sd1jJ&|?+lUiWh*M+KD)+QKHJ|xGdU|QEVy-6rL}N1STFvR|9P@juC=Ma zPWcg^GAG5%9VjX=`<7Udgb(t`da6>vu<`N(RaqPIYcz2$M90HO&QgE0BKgw%@gC^>TFc_(HSPz|h|c&+3}7o{oywKJS^BfxUVG6>mYgwvPV2nch(a z6O=@2Ut%v}AP2Z{7_Xloc^p*RZ(saisD!y)Q4Jr5CX!Z+TS{|jh7c~Lb?plPGeVmd z4gJ_B4ithJ)wmqpoYwq1?|{4SL48#X;kLqS#G8g58CcTTj&t%N@l}VS)2I_O^oqZ$ zVEcSdgFZlyfl~4%46AdJCSHy3$r^u~xf&}NsUR3B`#L$}t!;$Ltii1faWzb+2~rp& zL%%k3-oF-&zNXA@)S9H=FM~mf^-$1l^a2X7et#9F7PB(g4^?TORo|5Zc~mkeIZ6S8 z?W5j2wK#xKoN5#LyD9S#W7y6DcOHD<%bV!HZS2I8UckNi-~Ms8NBeon3e^qyX%3os-@l;yi80JKL6g22 zu09Y@;M*Ggd+Ppkn~bwo(gxZq<yaR41h97`Uu&_pd%?N=>38 zO?v!vMp#BBZ#VE-uM3OJj7rK=*^%M0S3c@46dGCKOu!QQ|7-7Bqnf(1FainzUUP26D~!r9FOWkNxQu^q1& zEA*PEZREPa+AR_Z8z^H{oGm2MwUX^Z%E|k1)#JwJTHNeHe+<*@!w&vjf{p>(y=l&b-4>5W$fvb&Vp?MBHPjS#4u%KF$9$AYbQ zn^x%2^TVQ1CV8emR;|y3l(D=yG9a7Xd2m-Ruq^x6%dbknn(~QvyLF{jQX8qA)c)1L zeya;>Cl}{M#%6Z-Bp7{6p+RP4Ns9X8p=TLIbN! z@a15zZ(UVoJ^6`|t6hr4&g7K_2ug>JystQ7`)Y1`wOn6*5%OX>Q%EkrHr@=TRM&uU zkW{_epPLJAV;Dq6Ld*aqV=BXm^zR(^hN%M2iUd>9wl{k_-G3(zFAp)QWoMuM-Pb!W zKE|(qLCS^)r`mCgT;0RMwNd@PNllzPVQ17)=o*|Sz5RSEnCLAMja2w^^1p1oh9qfE z8jlYTP|~LKdxpCHkqWJ0=>&2z^i8KxUzRKT%tRq{rGHkCk_f8~ir)5hu1~2fZpeIX@#2vyxA6HM zsr4?F%@d*5LAI3P3k4DHQ;%XI!kUd`k&3%nIS$$z2M^LBL`+C?%yJQW|QO|GD zckk>t`ebYV#EQ#6D)z^Yn$0#Lfhd-g3FGW88@8V~fRC>?P}&vJ$iH=Q+#Z}|pI)Ai z_QEC6xk)Wvmqm`IFDxOG%C)Q*&OPzfTE{Gq;$04*zN`BJSZK31p8S=Yq-Dhi;yv_~ zV#@bxfH9o{8{g|XgVBtU`?BB+_lwG%IF&3@D3gH|@em;Gd`su;f)q;Du+nBW+%f_w zsTUk^Y5=X+)}&t<$`{s{V3ZIn4}^@F4!#o}x!ytnKny~%tWNx{g2_(v-n|+omN~ui z3l?nzPZ+4Aq*@>eW5Gx8kZCmV;aw$d(Dv|&R-J6<@9+Q92hJWNo08ghwwb$6=e_p< zI8dsUJIW7N!1*|^yVpsKM|sh-t2t!QbM?81^&GVwsLDm~L=Xk#z5Jmv+)bVowp=R= z1UL!~x*_7pab##gTu4335F2dNk}$v;j|<+wg$FQ@a%icHhw$H^EH?m zm~HHBN3DsgI_|3-#{D_yN9ztHpZ3tVg4o{dFN@lQxCz{7nGp>#SB_GIOWc)CvIQ_k{mVm*!L{k8-mQE5g;fI?Jh}#4z zSY-E+7Zk$!Bf<1Eki10;BsN1r%Lg)BY&Fbut;#Xo|9d`l1$Zqon?*oF+8^$4Runv< zQxBY{_~T3H#^sKJ_iPaD`Y$B~f~Y8|EhKGfraDQ|&|5#3YRzgxtTD7pU@Qg?0$LtOb@=@6hhHm!Rxu-9~___y!=+;OkzH*nTA^1=hK>wlT zr<`90uCSklT%*6WPjxUj|C)}u)p^5F-e&qUxp8ov>ArWGudBaQVT7&9`Y3v7lZ%8v z)wDZr_QAQWqXgg^SPYI#c%2Z8H6<|mmQ!|wjui#4RgefuC)`S!=otuMyKKlDS?oNr z`hc4D&=LToY02ySTue_6k>@4>1(f#A9s~_-|CY`hchUAct`~^l48ThRUfQBVzBga zhGg+AweF}21sj>^k=-;uil#yZNDCcZevGmiw?DJ@QMrT`1?M)Z+Lur*ab+lQyN<^J z(#1!VM_71BGy+Ey9GXP;YnlsKnfsp;Fz5vNeB*lWz|FY`pq}i`SVZfgan(~8QiOJ;CxjE`y4$8NI31b=tLybKOJ^-d9{P)Ozm6tUS zeJ{onQb?qcY?tjG{4~M(QW#+kOJ5+v0=R$i$GRW5MSw)GN=-fp2~nP< zx+&yLlOq-VW8$|zM#hN@2`dGFLz~UeyyhXG(fGoXuv<=L?kB!%a|)%GqLoCFWm}_W ziz+(GLvO|38E$scM5F-Ogx)o|A%u`~NuV<;o5bjLRTA5bXX;x6)m=^qALsnb{gtfj zBciMziWwlrDt3c~$Zum-k$50K8*)(umQr&8>$8{(*#A1i5mvJ$V=J<_+DbR6|{Qs++^ft-oTQ_C{0mxI=94T^X1Vb0NxqF` zw4M|o^ggYqFTJ<0=RvSEAzWcPlga~AEigvF$ZMD)T)P$DlBjDyPp`wLD5JRTDB|$p*zt@2xX4+@m#wt|-A=zpTHtcs{Ux`mhOw?lD=zX0&mUVU{9hM*dmsOO@&6xQN5>}E zEP0K*+;Or+*Bg5mF^eDd+la8ipE4Uiuv=mZ#YYik<-$Bg^?%gPRD@Kls+>=>fcWaJ63q6th|5c(DEel?!|=cy7dYX0YOIGPgt|M*%! zJQ~rhv1>y3m9RK;`gUm=+d9SotA2Y+iI-1$R<2zGd)peIDGy4 z3ziexf8tBv`Ie}^Pp5WxV2QPMVfg*2q~M=P==RfYP|xycHzjAg@wI!cN#8}XB&f1m zq$zCWiC~P`zql){>5+n!CS${?;L&2C2PTMjeA4SjcByFKNoH^E86)_TpbQZ-(Fl4* z#IZ~~dSzsBA`&>~ZPyi(a5`cx z5n2TusTYbY$!(R7_w&y-y;?a?vCDKV@!Q$x~o2KPO5fj>xUxz{&Wz`EcxX*+SA|iw{vn;)w z;#8}>BC}roCON&*u77B|7OvhCB77U^Sm=X26%l$t(;V*7Gjj0~dW`KwYw+iM0<}*K z4?NZ*dp5wo?XBgsJtun+zeZcRAo_l*@W4VAZCI2&Ik_#52WA+3|EVzTGE^eL0zsZ6 z*~Rto!NPNuM2m8$-Ka#;iW$QAU0CCM-g0dZRBWy2U&Q>cOjw@Uc4ylYsP9?Ql>Bsd z)hH}HV7oRn=}OO(oP2h~KhKq@gd%q-tx2xoFkDtw#%DVdZaLqVZ}vOgZ56%up^)XU zx_XaU>`WG#bdOUY%$MO5%_&VwiU%Ft6e~%n*AZqy0mEWZN^2El`_l! zk#!@VmfW_E8;QSMu$f-SghO=EkUQ$6`30XQhBi}(5$_f2+`SLx*BAhYyf(#ZFz>a%Ea1T5~Z z_4cBR9U6bksBFzVsb*8V--+iZsDzLTAzSzM_inz$S{iTNz@OG(qU$H4L{SsIjd|8T zdrXCz=V)U_lVuhTJbtY@SN{x2BGVOr!X4b6`3x-_LVS3EjfrNJswtm6737@Tj%noE zu>4O6VE$Ya_<6#ARpf8MqNFOtkg7nIwwFuk%BW4xEOxB*;#P2_`-y5`R*4fj=h36L`utB$nyOB zi-ESW>?efTo7&IV|0;pAwSZF^Cz;NG`6gM-Qv*vAYwP~`AVW?O8k&!9o?MF3Sz==H z4|Gn$2GchI24LBeD2@-Gu!YZUc#GK?prnKQ|i@;9O;?#FJHeR8;)hmG|%?o zG)$SIx%GTSJ{yPDbGX5~Hk5lZ-%E#>{!@yl=pHcJC*$7FBO_b4abR9PK~d_xz$2^D zVNxq#FFUHK86BOAmVPT^S+yK4d56*FWd#j8JEN{%SZD_bA@zs9$6hB|)FY-<7_nVS zW+mitX*Ifd75f@K6A}OC5ef@|cfhTOcU}r-8^=Dvz})i^{t>=tPgw<}8s#mZ#OPHB zLB!A{=Xkj zq>O76=E>ISm)eFlX^o!l*_J`iymcl$dHH(|+uM13c>cA0&?lO#1e|bVpOKti!fE0+ zc=Nrz)~%pV*AKE~h*oS8R}x9zk&%6oVUmRs!S3AEc-ovavHRXBu1lfYx5P#VQdY{H zEH|7b^P4bg@yIJ%e{Liiih!&-U(&Ulc4hh|*-`aZvLL+%A;wiNKUnOU+ehP<8Cgn= z)rycYB1Dh5jeDR=tW$JwPiOL_2`tTg=9g|_yyE^@Pc=0(glUJOosuk|PNqL0xW1ud z^-`yHw>=X8n&^2ye#+cJYoX?y!@+`#az8&;{s4MeqCVYAH>Z)*gfG=fyVU4~jN4X+ zj=xb)r|4GO(UuQ9glUCom1p71n6DY8iE)ml0La>Jj6|@NHMZL#iivi*x3(Z+H)lim z1=8~|YZfy~lu-iqJ@zdPkD|$E%C-Zbhz43S1mEftEX5AkYpkgdi8w}Wd8fA|!P{_O zla>pEEhjxeKJovBW<(;jEe!e$n|@U`ZreF9_`q&|r!GJ@dT;Uo7zhn$;?u+LvFw^s zc!nKi+~qm*zoAYIVRPuK$E{rvk0>Z8oFv0bkuweMF%HdVt0@=dYQ=?xc=-6E^YcT= zMt-thSm-+}G;r#Iok| z@USp!t>4&`#82$+-^PIl+}gomZLupNGCG^d7ORVOjFu zzb11&2Z}hs?WJ`cp}~x@=s~aN{4Sv97Z-b8O)~KxEp$LlCXxJmyo~?{t*fhR<>1hR zI^iD}X)v~oHkj#SXa5z@>g4`rS2(ucq%85{Z4SJCpg2lSLt|lXj^1Bc>vtuHi;L^g zWQc)*A>g`6VrFJ0?05NPWyJ!XAtVhUGqbS?XI9SqYE2mg>gVw>_lp-@P2MMW1bokV z@7=qHKC!cxs~XnZE4#Y7N={3=+Ly@o6J!h)x(BkP=5#TF)8qW`+11rmWMm`(9!3Q; z&9vfIiC)c2%4}&V2Ru_G0@6<&5-_yZv9uSflwaqLqTSx!j*N=B%f-*%1Kxhzh5R z`le=QKS2w)lVnlR*f=nl#y>kWj6n3Sud|3|;mgMVcFia#ct$JYrLNVtCc8G(RYHno zjIDOBjqs}tC8pM(Hb6c~7=InVG&hU#DlWAJVd^)yMRj#aHX3rKD1>N3qy#ha^f_{@ zDc`(#^WN36ay2p&gYC^a1xQo=p%e9`NUnPEvAmKq`4gM#jqKc+vM!>yZuKHskVAJ_G_`5O8|B zXND7ura~MGF4CO47gUp`rlw`*mrP7d;4DGZadC0I;z&00@u8NDUteE0e*gZnO|=|f zn(HVUI3Ms#?D*s%WI0Vu8Nqqdg*-(K>;udvK~b6AZg8ta|) zXg@iCc2`37&xGA~W=!ntB4IE-K_Q_ENJL5sh5dZ z2+ABYS;UimZZnc z=#u4Ge`D#J>j90S%|%=^D_@(m46^~zK6pha(9&s z-nPsu^HR?Ke#eo#l-zRp6oi9*opW$Nz+DjO4vl*l;BkOEwv!jr)Y2MurP0tB<{cai zJMwB`2@kZ;DMe1dek@Dm*OFz-_3QAEci!)U{Z3iaG36spLq>AEk-52@aK!8<3HnlU zkRPj@H~X9$;Awzytd}gHq>we>)g5pF)w*442T%H)OGH~tYO|`;SwSsrDLQF6CisRF zr1ROm3u?`zk$=YZwI2wOvc`R!IGK<5Vwb-a?|B-&By%dVrVRgPidZ!%TpR%6M!wQmB_9?hr8Rey_+}*l_6h2ayAo~((c*4DQX3?3=~6h0T*H9L3} zPy{Gd<~nZtso>#DTcvh`zEtP<7Y!u6qX6W#He#+aX(|^Ftu6H>(m8L8P`!6Q`O3nY zVj8e_X5F0b)0&ZMZh+eJ@)vW+dilo2BG}(|2}54U!vZ%`*&NaRLtWd~hsNir08N+A zOAHsBL}t&vBG1LdR#;UnrfaD~Hw+3gL$`$1s)7F4(G1^qMqzZud5NR~lNgB=;MKET zdvQ@*F;sbIhd53`eb+7F78O;ZkNeutkd}3Qm`{cN(}a9rV89VM!LBg+U4_LMaU5dd zA{)<=C})4+&~n8GVy+W)(ol8}Br-_rKXwz_{>sYu{^$AreW7%IAj6-;$?O6kvkyM( z*O#KG_(2l=gu(1Rc8fKOzT|C0*O`!0q5nOv9h~nP8zTW-3$*X7t3O$FSqRxjU((WC z5N^@W@j5ajtDy)Qd;|ETqOV=J?x#I^^vGQpm6eR#mf3a> zY9mPIY-zhd-DFhf3*L|YbpK5w_xq&Sp%`usCpM85q<4B)*LqA37ecJ`2tc579ktkL8$LM|m5Ui@ji43$O;8qfy1#S_`Q!kos!Qwi^7fuRZn@l- zXOvy+dSt{%{jdQl;&U1cfHp=4*O%1P+b~Q@BGXzhX!1%NAJ;JSJ<>3(AcsK{K(pHI zz)$(|rL_`r;y#e_+Rd+J(x`|wrUy#w2BU$46S`azTgw-_DrktqUD#Xd0nIG2uI}YD z0x|2Berc+q!T+V%}rv!AXNF-%kV3CfQ?Ph`Opr zgMLl20P`Nd4MRjn+h4E8T;IKSFCQl2Ru{We73Oeu)XZPx{7UmAZP(wR0qTL0F^0X5gYenfB@Gx*}2TJ_6Qt%VP7` zhkxT8>{zbq#0YA8(0`HUw68W9GP4H|kyDSAr)LT%8!ROB)DLqC3x#+#mzTt$_XWuD z>Z`uhmjbK-0-ikN@5INbo+U78I0IIMl>qrx;}-rE7J>}Y-4%q|rb-D=cY`UQe+6Jj zd)*L#X5^lNAOeFB=yTry(XYsn+<`n=-17j%6(|z5FDql{#8%IbkULwh*(#5-h8lQ= zK#f;nSuW*B&65Rpz`uBQ<)<|zXr|ARtYcs>>m+r<)wS(cT9ZRiBc1AkJhEXV5)o>q z-NUrP-m+gOIQFo@$9N8j2s`s^H{`r3DLD)gA6PxWoLvc_AD-@eC5=g|@$_e_7v{pmB0HX1&B)qr@Z)cX(gJtsG0Zot>>r#S}@<4 zZ-KYBM>cd!EbV=@PHiZ74aNn^2l=Y4N?X#rZxnx(mX_}5C>5#}ls7guS~h@El9-rC zb77py1VACxbTu^1N3#3QO_kvAN zCJ5`&TJGh~9=!+zC~D*VH0f4a@7e*PzVM|0s+#WZsj$=cMa?9O$pUjBb)DCV?(@BK z6j%gD7rjWqr2>*L$1iHz+j!MK(>0Px!w?9G__QnF$=GHLT!v zL~|*X#`~PgZQF+i!z_TDD}>}c!+Bkn?7{WRQICpFS(#3%=6=sFx!m^cE=l_`@A`RM zo%mSB`>R^U`58Yg5?*yb79jtYZ}}lJ^X~b%5)YqS%AY?y!d~x_rDK{ctgQpYvlrmY zIB~ZOf1sC+5zo}~NKlE*K)@PNskqx}l_xTg*8H1XrPIZ3_F-j~`<|;>HJn5b2oAh9 zK!s$%DO%y?Q{l!_Eh>87Wr`3JNVk&8ptWt-mQYVR@)2NqQ zS3t_zi|l%o@p4CnowUuwnN&0-M>yqg0bkd^fSI76*QXwYm9@2{@h+sJgL8NHFAF~H zj5#={f-yDFnsiHc*4-Gu<%Y`Y>7}I^=_nVrxxK83qDHDy|FBGHB0SeqE;f8&wLzVo zyF?=DK;X>|E4p6~-FPNOT3XGA8-`X@m?|q5J)}(&)cq6EZS-D*Mx-5{+u0v z>O`-@qGGdnm(Wf^jYRj&6O6?@_A-i)wwZdxhYu^iJ?>eGbqKw=68+L@j4sUm8Sm8m z;dKkP=DP>Yeb=dWZgHdeLkTj6gfZ>}5!GA*0^*jIbN~dc%{4jHdY@3m_fn`9=<1MX z-3kls1Yq8zrU;WTeRIbAdMUxM&w1}i%y>C>{t0>Hj_<3C%TWKldE!Go50U+V`A`5X zX9wRaX?*JV!Rh;jx}cUlSsv8ud!xJp-pBVB_xL^=e`?gRaP|!FKHw3avsi_R(e|MB zc3Co*s*|h?o-6L>5|NHBD@bD+!3Vx3%b6Y@f8;WPaEm=}<78$!c1@So{PaGiTu|)p z&B49(<3Q6^pNH1jku_L0*zsM@;(MQoUY|Zn7r7t;WP+vL7I#L5@5hd}AtCUQ@!6rN zDYlvBtNU7I*X-co?w`>Y>Za-Z$gdP%Q(NB|9GZ!BOpBHH)Jz^ea#MKd{rdSimEpb1x%e$C~D=X1t6|8aa z@7P9E3%Z|Ak&pk{AtjwiNDersP0zhv)5UXs<;C+9zR9oZvujGZL&! zOEB9RvdcUkaWgKkOR`8%95SX53@^KuBfs#^N_`SEdhJ(e&x@v*C~)eqGxO&db@m|A}K>7c`PAb`g6u}aJUY?q3i zp103MMKj8Rjwy(R{g%x`b0SM9;8kl$lx~6garzz*7B6B*c|~DRHI8I!lrKfhs#t9Z zdqQ0b;zwbpNhSwCGdHPDi>tr3-R~m~PK>;x+&T0g)6O-=qmQANypd;Y_dm;_+r#%i z%%NFA4GrwJ`0n1Vc|`W`XW3x58lHaA$^)uL)ag;CpgS8(dhRdZRa3)1JsdUKhYN`< z4f6YoLnZXRdw=)~H=wr;BOV-L+e zYV4c;_5zse+>%47d3w8k`Bcj$e*)T%lZvFrLpwf^;%5y?j+bvx`cMqr&12L~>YC}) zC})!yfuE!Xd1M`(6wON5GZ;EZ+q5(o6n_OA_%GX=W0wK@;dsVjpCP@_&?ifEfl=?)St**X}Mko&P27yLU_gb89E9 zz!wDtg6pZe{IrrRcoh)`3Yu1uM8nAgEZ-u`4-7ne0}?H`xoAN zy1FuqM4doNYWpfQY5*bA0YhDig1znhGlJqC&ku!~MWwc*ZqINo2<<|j?mR5QkJ|l% zEU zd?@YtH<(R={<)VI#@T9Gj7;u3g8=06Pd*|z;37HTf}AKZXxh9V+T*tJ@nb7#y15+{o|@_U7@NoEAo-g@ za*Ir*xmRjbX7|hEUOGD!6|(yJ=C32E9+O8tuBm>TWEWotN$8IlAcc{wxLfU=)qaur zO%e80KoU3R6S9cyXTpFoAd46t9eu-*jL^JQ(*>wk2U2+6ySq1_ zY8o5$lWR3dg4Obs2qU_*_4UpD{N@Jr<|2ERvdhWDFpu=4WyKDi2+PUMQZ9@Sk%2G6yJ zo`02?1SEt3`%GA(&ieet(FfZ23T4@+~Qb@OHFO* zbAMC<_aauCmp|82Supr-SzVChVSumzslg>A1bAx(ay+7#9#Ev_)P9KK=2>&UeAzqO z;BJ8U1!yloEdS;md{IC^jSt@{DK91ixq#>{w`-4k=o2v2wt~>?Gf#RK`@Q?mmziGx zLLFV)OBWS^Eg?(d$eSworryky=9{K2_gN6Zw)0qllO~|B@JFFswQjCHhvQN=GbsDT zrSE_!0G*x|1kg4=H+N8ki6s2@q@9Sdh{6bPf_FWH(I@znLpn?V8=kH52@t1!eSI*h zKNI=#fZQdK%!UrOGNcVw*4MMXgT`Q2?5XTF8td@!;e-X?{%RmY&d{yM1&ob4H40ts z;P5$vK}SPlaW9F+2;YM@Fx6qm&4(6$JDZ>*5y#*!SH>{Q0UMjbiW_{A6zkzVOMVY_ z{F=_KEr#pOvg`Y7Z2gzB^EaHk7if0PWhaMtqw`|zr>BbGAeZ)dJ7dQEk|S*89rPq4 z0250V0ek}5IyxpMgA(6C?nOsG3~XC~p=pfr8v5)>f-=Q7ppY);8V+(5u$7*tyHAXK z_PSsN1cimpJ2NkV0*e_=tXq)?;#-%2R)7EcxurnD3C| z*;xz~`B8on13%XhoG)Ydmxap>;Z>*KEfl0vxk zp?BiI<-oI^4tM&fqOh=@C#p_AgoptnKzo;DuE)tk8VJ_paRr+B_%*n6>wR_15u38%%(aIG~$sFA|S?5MYARM z`&^%`-F^V>v;oyD56TZdt?&YP)WyXbZ~knAmZ}yw9;_tAw*%n$HygkDP3=1uB(Dd0@O^2a!^kJZLv4y*HVV4$a>h9`jCF{_!ralaB8cD+!37qhy;%pr~p{QBluC(S^ca6 zz^{c%tv{(SS6s`GPn)mLiGfZ&Q}@W)7WsqSAW2>!FTscBD)wSyITKZp`L*YbHzP2g zE%&bw8$5eW&MhotlZ%#zZTsj)<&OX4f$xy30q5R!O;%N7<10PAK<(m$iHV2P2yZac z?|Rsz!5C$~<>p3B8r?kq>xkQOHaG;}JZ+aLAyDOp){1Pk>JE4fl>H}r?62dv6%#<9 z>;vHvD5~xmG{7_&TU7?$=OMuRRik)D-be3UOgsw?Eb-X zAILtBjC|s%EQji^R5cQfxn!z!pQb4A4HUN@d}Of8Kn|W^4*6a0NLK5*7Nu;t($Hwq zJ!H`QG~eA_X{WCI1%8c-Ic@)}W?*sD`$ymK3Zm4XMT5j*vgt{bN7MKV7o^WGuQ}ry z?fyz_L#2bWwU)m-vuPf}l+L{2v=0)h*vaJYJC8SwGM?+{oi(&Q7AsU&F{g-uxVYO# z3*q9bE0@=j3JbZ3;*S@yZB5*uyqZyU_Bu?UjFLonzI#Xe&DxKoD+v1LF+pCwW$f^9 zG6gyLQ*u1Qm>Q>z5qw!juhWGf|9&ul)+^~MJ#=Wg`VpF83Pt3BBmvqfP^5r80BGD= z=4rQ98>WO_5O=_0utD|)UGgmeff6~x)q}28Kqo#qQef)X-Q@zO2Ow$t-`(iN2`;=+ zgNAODJ*w>672 zQC;XEs%F^zx%F=J+#THq1ds%lc^kEre2#QNAq?Fo%#N=gth^58=e|h z4kv0xINbk(sZ-+K9&hh5L|*l+ zh?PM9&}`h1+-`GCSy{=Lodn`c!M)}Xz-9syJLYuWtKXjhK^!MN_jQazCc56`bUYHq57 zPtm%|f-mI@3*-AOz~ExWog&F$Am{wIz39E$@$XrYR)Bp!0~#OH5C;mn7D>@8U?^)e zbm;gZ3b@6bd~Jxm-ja^v+4}&Qyv9a%eT?7>vB;*x(98`~=P(-@GRs#56}Qm3VdqpB zCtdDAJYzOsWtaGM^nLokxCA5?hf#!(gz#M7O1}efL^aXFo?6cXm!xF1Qr`l#8(K|NfB?v|#bOfY=*ayD&tXeAdrMK+U%(EF{3INL<7p z$dLVqS%q>@t7mrh9b&4Be)&KH`BO8glMtq4)9NHf4g60%OHMr%`Wy!<$#k&_@ZDS5{LK&$ z@s~!G{Xr_7{91DXu9bO||LO^#2PEj@?p;ctwH=Syk}!g1g(>;wlisP)f*`-QWhhLo zzK2&)>+@D3Zl%!^AMJ|JC!1gS-fTdibkcN`FWee#G)cOyn$zQ%dY;+wXT7O}0H+a{ zQvpr|1eE{vT0-N|1Zsb$!9RI!P7Tlwe!D>@8z`~oEy42*DT&w7`ft3A@#WKf zJQb1QSF`=XCD98Z5P5J}e*POPOY!lkD6cJLWixgE{OqP&T3($=$FHd=i#h)r{FnaMn5S2FziL(;A6XTIq*i@n!5^FrwLb@CS96dFaTSURjS*}EP*hlu*dEPpmg!) zI&g+oN;S-ldHFQs_hlDnP7NldMgM-GSjEwkClw}AO!4=KC*GT8)UIkIl#s2zQpoK(Cwl4El-pCxn7=q0KJm1sVRT=;+}rpz8>HQRyy!+ z0+q$Clu2;dc34Bz-kLO=XwMt%88Zs7rXLLUx*KItnv+Y-;%{DrxvD;KYnaQ-on4BC zlKg!vPzUNgYa$M?g8;Q!Yib7;S4UV@6Cx|`IMw*GaW1IQ@X0V%#kkFpibQ-u-mn$^ z(I1DeJ0FT1X&RxIbDls!a z^48e<_HHAL0E0NIen0;~MT+!qmaRnbpYwc~!P>=9lZF@1iHU1G_Loa9yuD(59W|*k zn&^+NOC6$tx73t2UTiyPe)D4hSAyt+B!v)rQi@YZsBqJb*BDycJ1~U<@tU7MhEeus zjXPn4E0BPG^~p_C!=QPxad~wIG!yv744pXW$T-GcgXR*=BG6E~d&=Ij6z?8xFap{4 zjr+-+JrA}uc$2vD(6jDt4ggrR>ilnj!LJI8O(1O&s_Apo^*hvOGb;p4Na$zt>|lWl+@C6B zPl4?%e9y-6!@v;Hu2IaZQ2anv8KOfSVT%2I#?oClXC+I5_ySHdU7q#+p>?=1SKs`F zw?8OYfsDQCMWm$$7vTWdd{lj=%tN|qMF|!$gq4fS@*ekdgW*TyDmF3kDHC&#=SreG zIc@VRTU*R93C=i~^vujBQqo4IK9^*GDNV`r`(Q$@&YGB7HHa^$?bk#c!k{1(WXu`~ z*R;NYeXwj4MQ%C@3;^q1B?PcS?7$iIE9{Y7E3YCMZa;*gXxWS?n3lAXkhPaP#E?<_ zxPHR`9!ap(_}9+20cjb{hZ=Nrl=zWwulAa}r6p<9u!wn9$^GP+l$4>V>Ec5m-k6%0 zkabMi#4r48nPrU~jhTK^@JR6h=#zUMyy~u~)UohI$b}8SPL=u>4lbQNmj2`+YH7tA zP2BH)Ilk=d%i(4hpTTLy3^9ovECsF(|6NS~fvG7^;Y<{*_?v7j81!JTr$v0ywNcUZ z=hM!TSHD|-RYp$g5#OsXP3VPG{`^dzVQB2Qi(zJ#OhG9qEiW$wGRg|7_B)r^r7byqeHkJn8)dj(%JZe= zX%CbU_uy0(-yy2hci~qjA=Nng%2LQ?YFoE6Jw2~ZA}N_ZM1X-qJXk5h!vKOl@ZAApPD@Lc_sI^}kxPYYV|pKoYU}Hc*@^Sq5VAl8zZ&AndvCy87BW| z?lC!D$>0YY+n0-(B5Wfvs)|z+Nqe@F9p@HfmXh&|XaSGGqSCAp^y~E%afpd=YPaqY zks14m`-vH(U;o0=L}yhOUf%odC;C%k=CY)4MRl>d$&3Xn$5NDdcKK4BZi(iz#8kE` zN#F+j+8b*@(K3&MD`^yI(26zp3=A;X&uuMj{YeG`y|9P~uvt0L$C01PKSnt+9RB)U zc`;G|h#p{cV8H>_L_l|gAJvkt`k*U@t8tDYk5miP zx$uJi{E;4ji!LT8UjrNTTi`@{!J+T*K#p`5Vb`!@{-@j(e-Hz>9rrw_!GIC(kz_Ia z=TGnDWg|fiGhPQ5IUSv)kB^U0k8C_TDe(YJ-Uqv{c3;yhoa+*+ql_l_#NX!!JzWrT z!nPwyvUuP0u@ni$oe-1S#Zle|B$Bx;EwC^_fH8Xvx7$m#8df>ZfG`A_@l^zh7H|(dA3zs30fRx zSN-%OEH`nDTmR7LXw~Ie)(y|~Y0S-&VJm1?H}8W|!>dRDIbI}L0Hqct7FjNLk{5S#fvScsxV4GOINaVuZ}md|CK` z&rqF$`uzG3iN<;Jj&#Mzg`E_ak{IX=+uQGKY@+@5m9LqJ!ob2t@O#0*{eAinlZ7L` zpyYuxX;~66Tp@%npM^?6xb~_$_p%yt-vx4 zTW*C94mtrsEH5t)Mm;$(A!YMKexGjsP7Jyj?fFhufySwW|Gi!fd&}m9hcBL-WJq26 zo#iNr(FD}a`5` zhQ4u=iEixZh&AZBKrj>^c&}aOE;YqVNnnF?s?fyFS1njL@&f93!(78e{^ppAe^JS& zu^PfN9WyGpAJ{dHHd|utE~zs`R?Lg6iwd|k-+{ImT5w<9@teSXWJ)k@JpOTyi1TKf zm7Q(}V$TqV1ss#A1)YnF--O=iu_kgUCV+5x_wL=tT9MZPwA#-#_BMIzxg!CXM?Tn6 zZ+t|yDszo*)X_p&wz5$A*+dvO!SkDin9VCEO9-<1qub_8Y0T`709ZOvXPts=ujUGY z)ebwvLR(Op&(S!hu%A!z__&37HB7ML5%Qw^OoZQs<+x!!_aQENsJNaU6{s-~GT{ES zeG>g*WG$jkdnk-%WhMSeRnBv$8| zuk~;y^nzy#%qmoM`bx`lnFQ2AaIbeJd`m|j6wyb+y4u`% ziyElY3oqN?n&mDp@V)CKUq_9*pAEs`@q_PXXXhU#S={H zNt;04kc6J@H+B7j#I41K`l|A#rAb4mKOHM+2Eaznb)wRu0k3|iW8H26yFJYsLVa-t zdJ&L`q-S!1%p4tKUDn`0P;-j}dW+Mjkd3D`!{r{`&0h50AG;*b^q#+b>G^vW%4*|k zYm0$-1h1v#gF&%P*a*gY&kwWSfq63E)$Yv5$VfY|=7vdpEA;@Gwzc)Ws>-G3i#fL} zYogaqSINRcuQ@kjVty8Tt=g+omZ4LS?adJ+m|xsq+1ai@eR{G`F6Rj-O1B%eimjWQ zg-9%Io{}FceJDo96E&~j)8H0Y-UVB!U8R0Ls@*O>JEe?=V`}_N)LO%Bb?tmQ^oh0Dd2boz>>#}!w z>!4>$YQB#d!;`Q&U{}gtH5Gle%*!uce2}s!Yi9lNI&(~W?Lv#Bt3+VLj;D4gs#Zf| zyyWJrQ3XQA|GCr}mZ0`nzz$G1{Pr`CNv89zQIu=&jGVGE{H!7ATvZc$dwT?t&cidG zOm$XQ@>xNBHD%(O)Qo71_Jlhu369N* zzH<;kviG?9^zm2I?t5SE(#6|9SyN!y(r48BM^=c=Ya+W$D?XJX6_H9wO8K(+qYd3a z+XsIiAYgj%Ns7Y$UJo6ebKz*wPg7A9N1r!rB{Hd; z5XJoSsE+fWACEXh0+*H^Z@Xu>cpcPjU)~(y^H(aH9J_mi2J#my>xh)wj4M$4HHlyH zv&oEaml3wi58-k)wg%%qinaJ7Qf98R3UE#W-qO~fGwqi(b>*n-D=;vm zIfVMeV4nI6Huv+R)CNj62FmvqxjQq35{+qxSA27skxVBE&amoq5|XXJv~IE0Noptn zn?5z$NB%}J*Z5L@TV7)NK5^tY`Gi*Xv37WArSkqTeq}?mB~!v*AUWKYU=0mVu$pzd zapL4eXfTO{$&d&e6^rptqC(sS@Yvx0^G^aGT+CV;bb#XAiMpCYWm`{YZkMb9M=$tM zC!-$fM{tF(gqEKOrk_$YH3>u)kmCjz)QmjDyDc>@449(%Vg;&1kd|G*-Gw??I=GL8 zPJilicjtP|?Dy)8(o}uhD}~iGe)`6m&|25IAsAF))ZE^F=f)B>n2L}Y^=YB|Ai<=G zhJM#^{+Z1)_UfND8=GpQRT&%DF(RVJ=}TBA!3^-Ij=wHzSo5cSS*=;EZc~KOx14k&uf5cK`9CUCga5RlHjD|y;C;3vy@R1sU_!hKwpJ(OH+xNw^K_->d%isU}K_( ztcKZD{!GJh3ToSnT>3f`BZLMvfv8{OW?U#?$;b4(F)|9gz9y;A2LCheo!+U?j`t9L z%}dEl5)MS|Juk%K#k@g1#_OvE+53%LevL_BD+KJH1Y78U5n^qDDUQ0^B&oK^`0YjI z>f;_-|1GEW;FmI932N;2%4q+T?nTf`+jtfIP8#Ol7L5b@7--#psw}}4$kx`@t%sa1 zO{4;bTVh==LZbfZy1Sy zP|yH6ndjZ7U=J)l^L01^^{Hp+2?@?~ZtlQRq>_wihLX!lA8B__4{%tk7mX8h)`v;Z zgS|czatvErTjqNEUU%v5q@U(~>|DCJ+W>PcQ=AMS6*o}IgfaL8F~Ft~L5L%Xjlu9y zoDd8tsbrukit^wL&y@ySr+&$cHd$A>!;63VkRUthL0yNp1rAW`yuH&Dn1aA{#K`~$ zq70}xE-u6dViLmgj9_&OEQKr{KLwn5t_|g=?y!6LzWO7oO^@RfWOF%)o(O1OD(1AJ4Q9z1uL|R}bw6G8}TpIx! zjuNc43DzwUuh#u$_^3te$9Uhm7fnC6HSd61-z%)s1I9zTSU(l(_{M`vT+UJ6KQlAt zCMI{mV-U<(tr`+#NR+FnsX3VlM}S5ZH?#(DjCPVFQ-bHLnwObJrbsb(^G3Yy0D6G& z5O^wpMILMw34Mfur5e-;SaL=ER0lh1pjNJ(Xcl#W-7PR207n5RAw3|vXb!OZD?$8& zdG|-o4|ec`ff>QbD2u@!ig*WwMyVDY*3U)Pqy`C+Vwqf&u;;B=v%Zekw)^1W^C2MM zwe-~SNq(5rQfO)fFC|&EE&%lsb^2d5+0@Fk3dRegW7(5&$B-6<m8^|GU6vFQ?I;M2nhRQkL->|dq{ zinH!XlJ!tH{uIkV4Dk?)ixMC~;L>eLkUCe;dlM!EtsEtus2f<`DZDBfg4BRx#vAuv z*MxKS{`^F%Gs+GZ!`P_k{bz!;2=}$zD`+m&-4MGhI|y$SFMUWGUbOV-Ps784jpJ04 ze*{ynxF@sN6zX+Mo&34*v;BN8U15MijDcN{w{NVCTaf87+=uVP_f#0=jn$Eg#{WPB zvE}YwZRw#zp_&b!kp4Q>&kJW212Sq$!Q`Ln;cQ=Xuh>yvy*CD?fp6*Y*@6>Ssh|^bcpphcqV#Qsz{VW<77fORoBw8qr6=IQPXzTJ?$Z$r9R(IsyeeKg=yKR zb$&HbsP!wj{6}pMkev*x_on$4eJ#{_xlF*9L3u$G!xSc!ciQt$@WR4EvBaUXzXxF3 z^;{#;2po1eMBkWmCX~2eAvhIIWSDsXeXzsJ38N``s`E)nwnR7^2M0NGwZaAl3{jvb zbk~iDwOKO2tY>UYxIg#l6E&!P%QzSm)W%=wSlRpx5Z)O|Uo3gxn;_+{#V z)5o8>9)EgL;#6$oO|9O6fwWF*>9h`xa7G@jKj}=>G*J!-M&)+!+;+Pvq<(R>KhQnp zN^v~<@7xzrxTkS4qthA}L4rcxsov=e*yXwx9KG&!Ou1GqkJ3n-I(1AnI#TX$aDM(V zaH#az)IG|+86Us~-aL1l4!~d7P7Yk{rUKsL^S`dwcZs{J$ z>Sr~#LV~D#KJ!VObVEmnY;?56J1w@aY?_3v@@0&|J?s zWPzX_Uf=5KbMHcLEs1VDC=1yLQ`p6}gHl)V+Te14eWalGR|)sj`pb)CL#aijNy;_1 z->TY60Rc|ty1-d%h_}ql{P9X^N5r7tvaz=>$j^6t=Gr;sYF$V%r^Dq1$ozp-+~?NT z_U`WUr3T8~uHvd&`=SPGYR*D0j!(%ObwRTD;hs7Ny(@)!x!3Hgr$`3e+iXwy`W`= z*#c^O#X3i^;$9!6QMxMq`}n>z?Ioh)hrRpM9T$I<+t}wtHNJe{&?9WSH}SwrtHcaGDjLXlZ~GEKRYE=I0~8_n=H)i zybB&&!%(=ge|Y$w_4S#J!NVkaQ&`qeA2sy##hG>7&}BkjV)hH~djn`xFc!GVq0C{M zfBws1gI$Gs!i$TGNKwP1)75qS&g(fG`o|x-pyI0kF2DV5Jy9ZYteHPP{soy{fl7d_F2TM2_s@uO8Yo2+(em@W5$-!xGw)m+-~2r)EMV|$eyvDvrM;I&Ri4*N zcSA*MCBAREgmB!Nw40GUA{!>PyKF1X$>!bXtep}uDso66LR6%KBc++;eSJv$w{SyxN=2`n@FS` z`uaIo&C$(v*Mz<$%Q;=oF`tNml!ljqS~999{n_H5;|g!VD#mVBia&MsO6BK#lBwEZ zyGS`LZ}m-wZE(sTnYbRNf6<=zeh#gHos9lfKIX7J|EWdFp1jC%%XD;jv zN5A(HiF~m{69NI}y|7a$$D3YTr&8MnzOxBW>P8&Si|XcelWT2DJn81-OO*Pr+UwGo ztV%>MD$%D!E9JEoYkvrBZM!Oe4#lL?L$nQ(2NZ8I^$1u7Jh3HqWdzsvX%&q|UEX^_ zOzeU;eY{_Ut}KP^%41Lyw>EqunY*cGtntsy%+! zO@0?eFmfnI(3kq?I=f$MND7>JpvW_+suo@XFLh?dYZZq_rYYSaG7Z<>(mGD~W?TQ~ zE~{#63@^Nzu`yg5X3yHwpS7Dk+j*YKvc_lX?a}?JG%m9CEYk7uO$OELm!N52efl|@ z_dBS4SRd#_)9Pxh!X-nNzDY(Ai#?9AH{VIo{!Tn%cBr!Msz^(}#Nc_|p(d881<%NY zu|b;o$7aNmB{qlgE<@4iYrJ-hN>UFye-Q5rGW^`$?=$;eJ<90|iEsF<=E=W*>F?Ls z$`Efl1xH1JLgQy)Pmul6qE&K7GY3#GG^GmNw?8)<9%Q$Z3GAFY857IpFx{t)L<|Bd z!OTOkme=^I9@$^ZhPO6q(~In%f0SY`81a|AlBu*ao(Ldi(G@?`4l0j)DMNpxj5T;) zRYt&6tmJWp z*udd^q%E)QiOx^e?X{N;5_gEFwfWw<)pA0Hycf60(*#-n*34h}L&YA&s;qlZJH7w; za|><~3y&3e%42fwhO#EAboArjE?j8w-B@$rJPflfwPX zR_A-|D{mMJ{X24In%Id=lU5IlY3SZL5iWUfjCP$ke%v%UQ2%wl&V$s{)b7*a+Mi&% z#VG=Uqu*2hut_IJxGUlK5BNgxTN}s6b#L7g5j#bQ!u`NdzwvpWPovEq+akM%W?VEZ~V!ALy_e}MoZq`dL$|>cm9=baPu1iH9#R=XZydEuS}}@flX;JQgiZjDBkjC#hctG zs6sYuq-nD*{CKNCJYE&GM)R%ql{*{~gNZwdhO>w6=a4OOIw63GRtbNo7I|D!Qd#Jv z0y;cDKWe49^laAb?9^pVan1U%$yEnx8Dev@z{@;&6I zNObS3@jpB+@2AFb-u=sEsQ=f1qQ2pkX4xB*7!AU!PHZ>ICSO0Yn`c3tyRxPiJ7WcT+`*-iS z0e_s~hPzKubL+M(2d`_&yyhwHwRzZNi(&38o){8Z~%Nup9U;fE|6m2*yK z(%|2HgwN$hdDl)&z3ez{)9OB4P2Ob6>Mnal;`PSQW8aKzXg~fC)Y7L7wcX}_)imF? zc7!Eg2WK=4&zX6m_$O|)RI9TG^QCw1P?2nc+1`0 zFXw_~CX=wRaOImD6i9|O9EKB008MXiZ`8z_?sr*C#XAr3WGM8o297A+D3?!BNU(}q z{G4U{sATkVzbkqv?M(}IFVf51x^FK}^Ckx#e*4hzEdJG{ncC`v zp9Yc#wy@D}bPWG`Clwg@VU}E&S-J1reFc*(cfVZQ%yy8LYX9!Qk1SDJUsR()Ie}UAp3_u+Qij$;mZQrq@vV(u67HQxNKkIBFpv`D z)a>577ynD*d^H{;90;YQA!&jL(71cTq6IQ4G=@l``u+RO{*H$g-p@*0M;1KW#KX^U z-@%tpdrs=ua`)q5y2HDJd@Spyun_5V2ntmuENCW&W(d^Jk{-Vn4Xse*OrZ@pNxb8n zzgEjMTq*7?dD*F@vw2ho%dy*9!Gh8p%_P;N_42NScJ!nSL;KB1foJ^0W8S8dobwnkwV^3fMQ9zvG;Tu6E3oG7~*qvJijfgIa z6*Z=$m)J-gW(s2otUdvN8R$AVZ;t{Hh$u1C)lJ2I$hywL!SSs&fTqLxsc}Zh6|pLRi0nSy3U1Vs9U&aPWM(iFA}Uoe!!fB&}r5HWk@k*L+-TPPx?|Jygq-f{Gv6HHnEFSU+(Y zQAi~7To6Lkk6|~*)u*i!z%5~bk>!;CDghkG!NFn3Lr2lYxfJ{IDc~{KRm#iD`RI2c z1_uOfW#wu73|18^YIukUkU;JcB7o5FB1%bmYR?Y;U(?fVHWEN1&EfxwNSZo{4(7i=RW~+s00^+YQyq3)UVzn$ZeuZI*}bjSZ90imGxJ#kBR>#TE^e#iWwqF*N!QpJh}nEE=cC#_V@MmR~S zXFc+ZV~(Ht81BmKgsww?tA`_m(U#C}wVj7qmj^g*CWxAbFJCADOOR|(5}$tl6r!L$ z!#m|~Am#}fbw`_cI3v-}Qfv()bwSu6zbj{aB6W@~_D=(GLZ;T!Ouor&?i_Kv`^`S)@b%c)l?s5a zbuU~$Hwc^7s}uYT2vfqEn$ImMRbbz%Zrd{L;w|^Z9$U{euG{$SHM~%Cnm$f=*?8uivGCVVQ;)*+ z8p@RZ-dQ$$Iip*e|K@pP81FP;=;*UGQoPKf)wJDC zp43Y9Fo$E}HK(e5FV7pVA2Q(Fl)RIP`n3`7l||aQz^iGY`%`@eedPt^b-sj=OS&6E zKdzmfJd$U<*Xh?>1E)px=_wiBhN`lBg=Zhvl!FemPN5TbF>LskH;AA|P+3vfmT;(m zdNIxa@wVeKe>B$c!=`VjD{esQ_x?LH&tS@tQwN~@VRLQgx#nti za=FM3*+`Un%=05`B||GGzp}EKL=pi`1)TKz_lyF-5*#2$Aj-!BlDvDkT^t?NcS1FJ zAb*fq>FP3~bDkt8PZU?LP0Qu4O7O_V1KBzJU+iLe-hrrFpiL87cJVO(&~4MEtK1Wy zu+u12C#KWH|43iTyW&bGrPLVOp7Wiy%m0t4y-SW~LHrxv6U)P|s&>};HwxP6Ljkv7NPP~cgKh{TAe zH73Cx9rWuG2F%@2+!%S_8Tho(#`4oI5EHJ&(e}J!sKvm zfsjKAA?mv)h_?8iaQx}+jX&xe<9{AcqnUo^$G4NzpU&hL-fgbRVHR;)LuvdVn`oCz$NObl*C0HL57HBkxOb@4%?N} zDro*G$GwchtmvvjuG=0Ec0UYD7Fcp(vh#nGiG@9Nd1Sgs6g5Zn@4)dxEG?AtWT6w#|9%33Ng_~&cafLROeVc2+F^Upd!U~JcxKWFmO*!p==U3E)>Q7 z7>!E+VFA2x_*P@D7@xs{E`ctO4k6ov?#Y^;`6msLG>RLr!N|_UJ0?{;dq^M@85NF7g_(n_Q&i&__K`pcPd>oH%b^+qT@IK(o&k5Kdw2$%);ZV@PsD< zK#|MH%NZ%-7H2=daU?2%PcGTsI8{>=Sy|xNc2+I&e>SdlE|0Dd@-$Do_n*J@pT$ge`pO56cD#$RX=f6kkD(b&cWZ&AL z3vRw;{;E{bNl{!?=C$T?MW?T_Q>4Wl#ZV%%VsMW?XIj}CtsIm{91SPhMuuMpDSkzW z6v%_Q7MU3btWDmB=P_E?PN*9%UjJ)Z*V%>@|%Vo0^< zr!fMERJh8JnVa}y#6~x!y-0*U#AQ59TZl2!CviC;Y_MfiXz`##5=YAKc^zKwAA$;ZO!mZ<$v2F2fU8A|C-g{FxeKVJLi z&s*mvQkZDfCT^s))p4!=hfvvvjeIDgyyetoIVbSF_hnrV zMgOhniUUBN+}n!uxr(JhFJ%1{6&01>uz|=4+2sh`O!odAGL~P3!?^HvH=qgp?6;;^ zDFP|M?9yrQ+<+va8)w6eSHbluV#N&*hRl%*U{!oArxP(Ra@~2xnVr_Dg4Ei1M&`F} z1)kv^e%l-6-DDE*?EgDIo7j&;RS3PGn0ECep(voE%i1yONWbE1%E9yV&;wpIjq%Vw zRFMs1ij#ZOnU{$1#~01N`3EQ*{9i)x%e*{-tY8kH^?HU&w5~tge174~~pz@71}6h}YX;jUR{DV5~{B z+NEzGgtIMQXSb#C`|t-3W^qS3to?PRv^Bt18;bmExKVT4*x0}}GCc$Zf*L&Y3vHNFakE%?4@S)L^j9>AV zvyf{IEkb0;pFG*aQAgNK`#q|6w&jqgRS%Hg2z6^}!ng>g1^o|MtpkNWB`9@818d2y zzocfGSe)3}ipqItV|@wcAUxoNABuIqann4!%)jy@JrV@E_u4`?g?*j;3crH@LS4{1 zAc)ygBA${~jPf=$Tc~=;s~Y_pcjL`B>6+>YX9Q!53giUxk(a8LPJ_^eX%xTJk-TV| z8K4OP0CIA1hhPgLPw=v;YN{3MJ}$X&TutarWo9bskSlx1BYB7q@0HuD$>@#umFP`?#r{OV@{{U#NK5l`y%#&pJg|xz*SmuS#rY3J8*+U~IAb>1sw|ysQ}}Ky^#Jv?={Fxv2mcAD{*+xI!{gzYMDX_11be#4 zZf;US>yCYWpbYDg_ZEa_J!=)@lLxOL5pmmFFByeR`Y}qV@HI7`MtZ5_AEl`iqXZcE zpJh<0DY6xa6Vljrxt))Jnl3~U>J!Sk#tuX|g{uo%d8?uNr(^{chC3k>?}xT5b-E)) zkAd3tr2xRGtlhObXI|EctFnd%nw{`LKmf`##QEa#LAnYTIct8EKu;7S>9hIt%v9VtU+Y3b}22>@r ziTYDa2?J?b33rkYN$v|PDU02ohAv%-=t?l6jT)PH&Uj%^?S7^QP88^O@F6HsvL8O| z=ze90HsqMRJme1B`{+96<{WXCQ1VA?ZJSG}$Rf<=!w6?{_H;4aTfwSrzew_{j4B+{$-@?TH5 zqrBs%W<+fG`|reTA4{D$>O-|;WK1If@D3q0Z;nnX@=MW;KdlXr3H=#iP&#j!e2Afc zrDWi$=-hmYT=D1#Q>mTF07w4AElYopsV5^NgHLf>LV^-%3U=7pNL!OpQ}dW=yHz^I z4I%3HnZ{wc)md;m_fLFR%IdU6^e;jj*i#QfP=K;`;{K7<;US+TLmbyoRbY#c?+$aJ zNU$Cs(N4JVdb#CslQ|yvlZ1quZf-F;N53bmj$0Tz_0a}Mjgk-kCJo^1;T$+8qd^`( z`Hijv!P#&%q9Es#^9C&lP8-fWBwd4M#SMXe5vMs2Ef<%L#_&_B(YW8yhd}~@a|6PW zXtJ@;WB1kNW3VP*-zc9ypXefZpz^Ud^Rp74P}-tVZXSoQK7Wm^Wn^MmeXdTVS81KE z{EL}f8G+*RpV~>|>N`;jW7B}CJfGf%=I0(^$fw(*veyPkS0kGA`1p7TXOIO9!X0t= zCnP05{%hUqC;g7_ylgnz?Fx+nEZsMpoW4#PeX$hBdib?08){UP)_(6temMkb2y3vn zZqO(ICx#c)GH;ZRh?^OBjKT_ZvoPKxj9B2{@_ zh%xN?k2;&rpStnCO=5pBka5+PU1%*BNP5>1rrzynQj5h5JC(kK(^bgbvhQ^o&Cy{2M? zKino$B0FR(4=g<{$j$~@g99kdB8?8{9ljn+%g$+Xg7ilw84l%}z(hc!cTasoR;ikuO-?5~WuUkKjGXIsbuqMn zICNm#avo>xsy2m4Odo<4a9%m)DjA|)fIjK7Ky*VCP>xjZL z1Ih!I|2Z;#jvrSLXT={a$@~m{4G5}G46JnK`6@}o!{RA_6Fc2oZib~ETaWU_@y`C2 zC)`0-jgM|)XZPtv?zjwZy)Zj#zNV(DvA!VT5t9Qu1_!WU(culEI95l6io;%z8izf! z%1HXGs=!Jz&F^V@^Fdf=z^}Y`B4U`c(ERauC(4MGW_w>})2aPi=DMrb7fv()>2h;( zL;o}B?rUqCJ2ry=>xgQ=coa6iXAYw6hrEj0cjoGIzZZV%H9LsvRpt2`djpC1L-EZd zuPT{H0$x)Wx$< zuglA)o^iADIVf#tyqlC4(!YPv6Y4|&Ozq<4#8TfgbkQ%I_x8mDTxH#Q3`ieHz%J|K~m9JY%iF=att!C}NG9dt%M2 z$^3{5F!o(JgulN{C4JHl2MluJ7eT2k-YSj*W40ns%x&_Mh#gnIwPX3F(5|>GJ2`6g zC4Mk16@6N*bBejmru535sqyEYI3CUE$ty!28>t#h^Dp4AgewB%60iWYAG-k0uKu!+ zH-n}UvMgLSSU+m(RS;>!H&dtqEJPhWz0sZW%bS7rB97~$gm31yf8V^@MkMLNQFalV z`n}c?@d>BBV#1h3L7jq}N`>)eekH-64hSY6z2h$pZEbDTJSYxO>7x0+c$b#AxZ;f( zVXjdi8S*L0i*P-51k^24F3*-Uq6B6uu_lMOdl|NAYZD=JCkC6=c z^1F?K)?P`*{+nUJLFnTNjh*+WTX(mTmzXyd)E`K?`DetTbX z>ChU9aP#;99Q0@q044lWy4848Bd{BHz4v0TMa!EZ@A*9dS`xrJe6j7Ca>Z{(S$F;_ z1CoN(Q?D$yY=n(>0+Kc6C21Yma*(#3ZHy^0#vDagD@qoOa8Ntpg??R}^$=&|36u^Q zRuO`K>qUKQ&=ev?86ELc`M>G%6y+FjEV^Axh-|s>AH=H&EWn3A!?LGc9O7WI^x={` zHN}?Km(|@XU5tY-@QKnueD~WsRw}sMl1JqT={q>JS9_J0$0qsY6Mjr+2q_$Wap&jO z>V5A$UBY(iD^=-;I{DsbZdSxQI65?!dce^LqcoA*up)#x{ZR- zB=%th_&>53GW@L>-?9iuSa`kU_wVMXx{nvTdg}mpRPr)Me zMOPczWN}rDlEAso5_lHn2`%&Xyja1G)v@8L#;;G7(WDToH*y=S6u|AjbqAg6@TqjhJ>XlWrKZ zUXb0ubp_F=7z+}voX$J_&XzbOa)s8Tl5$*ntOS>PW?1>&e)Glp7{|WfqRQmu8l8lF zL8h1Mw+bF)TpcV*DYO-fll}B)#W3R|^X=`N-?DF7?{NxL$~7_b>TB>&>I~lQqc*G5 zqrv4uXwPZQTut#}(&_m`(_VqHDSn=Ii}(ECgyi@j%BvBF3scImX}dnkzq zmh=c-qI7)pk5rydw5S9A%T(*_*#3p(w!B5G8P^ zo;%uYeG-x1p8Y?|NY75;{Z6<&OcUfNDhG&h`gF5EvbO?UykK;Iqn$4)JGV~yk^3>z zXF&TBzV+V{yb)tOV$ekd3v$YFhR=;$cp(4ehlLR54Kda`cfP90yxu%uYE}H{6!B11 zpNONpzD4e{0*&J@PU@?-?-sw;SG>T4><;?_s_tJ8E9-~v;7s19ekk}9|I`+~%%E$B z?FQ6LUvF&UdG!&LJxW3ZAmwVMqhPU$S=+xkd?x?Qrvq-=u4>fr!D3@MY%&{pMHCcq&e@jEqIeecw;_RwpI# z5>1M}&Ge(va{X`L)%1JhM1IPObY-3VASwZ9>Jc1g9+kl469IMd`bIwpv^d6ju7WhcAZO*d=U~1-WREGjsU7C0O+lPoyztB zHU7cMP1e@|S7JTyt^@ZhU7T@9l$q`a?b+S!;s?Sk6S#tZ*W7rNQ|Du#qRhfUb#^u@ zPxAT|sMVadDDW)h~zdU&wSGD*R>FIbJot zENj%IbF4UB(qXZdB6%ULY2|c!*53GeJuB6Ya4cm^rFTFYb_MCR+cW`N0!`qL_=rq< zGd7_<1FC#0;$l^W;awTmGr13RD%qI~AID4Z^!Jkd%4YG3hQ?^)xbh)T+U71tEi%yb7Un?1=G zyn01QN=k7r7T6xLP0RZ&mle~!{hmdtH<(t)Gt{*+TN0!#d#{}6J#s+yllDsH0)6%1 z=XuQxg%%sBhN&}WPQSjlYEHbUoudh8+S!-xuq}z{5~)10b<8}Ru_i73`o|@ZS>N*; z7>gDBs8n4O9>vAhWYHIYIV@dX|KekTtwcPCT|W9$yo`$rAP6AMl`PJD|9hbG?f_5U zTCvxGC3h1}lv9aJ$FZj%7pFA4IyybwQX?tZhEL@kmy3z&xHc3 z5huBricKUlmx5lKp~nT6>9b?=?{1peqr75sv?bHbamW5tH)Fj!%KOW2NglDr0K?x}Pz<+ z;(Nf3ikw-*z=68yeNeNZ4u97{IRlM;l|d$`yBVmZPA4ga{x>uu@K^;XBPKzhH>8X; zL5vy5OQFYm89!iV+P`cDHO|WH?DKDSERfeIIL`KulxByOQ(eYFqog1!J2b-zhZ8u{ zV!HvS{=~Gl7tUKO&znyL*%c356L0wVahv?#Houlq=NZYoX%cBR)^EjcuKCoEn`RU{ zSKI0H!FqWEd>QU6TvWf$U)#Dm`DYW?t&=QC+e{9cR&Iq3#v^O29Z25Elnya|1;EkK!qa|{2jM{{5*hn<~ z&{-u0=n22p`@!fhDiONs7rD5+qLL(VsFGJj3a7Da zC&UguGHPH~99D@q{kNxXk$AO!u4%ZVcc!e~n9!j$&Wn|Sa?wBpI+HKr2vIdj8Hms8 zwIN+POe>>}(9WS5$;WVjAg*G`aF=VC30*E@lyTD=z&fy;0fb6)GYfjo&(3G}e2tU? zMA!Md8ZXpScr%o#Ab#soD3vG@GMsmqwr2bfOy^pk4Wh)v0lfs=o`tD$AhQfa2tPcl z*ZsnCj-vR88q8)5v^6Iz%g0_=a8T*Tv_xZ+~vk?msUi6(nd2=7ca4xvewl^kvjhw^_9`I+U4NjQXnII_Ie}rSOAnd z+1VW0+Xtiy?b@g@YwjHdliwt;(ZX?>X+_@d?k_jqe{)3>gKr;ydehal&}=a1xjf8i zTaE0A@v9&L)vBN@1iqKaI1~m=p}xL8fHe$IgtUYqte&?SksAGt)BjT}HRxtxHb`Y9 zG~QgmJAgs@a9qPQLPc0HfvvosmTYgZZG^d_F0z7XHMV`?ggqv7sY z{|@i?2I_v>3J}RNcXRwr*1aX_lUp;NM#!Jkr;RCTW>Pkqke3@AvroRcGJPu~!S1S9 z-0(u$_H$}~d*;>3Dre;?tjr?MiGDu&H9kSwaY{M(!a+Td1LCaUcK_^P(0s*XO-y9n zlqe;@^M(x?mNZY2-7eFY?nW=q@%^Q_E8Hded)J|d?f0$X0QJ17sv;Ta=*1`j7|Y9> z3FwZ8oJWxdLOz^#bMC88vvzL+=mQd?%~E_BZ32*WsO@S(uHUaS0Z{@71OEayn_Qb- zPUrIXczY~S1g&C0f`@pL;c-Jx0V%_5Z2AV}izmc|p|7r(*OMr3@LS#|pNfGCyLZTl zyi%9p$-VT91Hh(fcI-ZZ(N#<78b43|#w$8?`!&vb2whp&q3$NMT4x<${A#>3HYZ>6 z4USRi}7vacbJLeq)%L*9*Eh>FN z_qO6IOho1=>^+|@%}m-!`bMH6V$kgfAs>IjY>GiQ;7BWHJ>fTtj*iB@!oV(M>Y@qB zbpInJu8Oc{id+%mM8qV+DzN$qWyq>;bwLXbKcrg~R8UlgRlpT71ryzJ&5hYFX?p;U z`}rj-EC{Sl53Qe!&UbZ={XZ>0^aTL z$y391`OtFZoMihPRH3;k&*oh-KDTQBUY*wpIlsa>#ke(WQMk*YXXcq}qtPgRV|-1P z91pc)ANcBJ2C1T2r>hTK%N?o8l#cwGNhhBEkoc9GreTv;#0#)x zqLah7K-t{_d(hUI8Q;C@uylz(8Yd)17_=w%g+m`HoeZk1puC(i>Wm@{`}`S0RO-%6 zU_1jrGdQ>9mWQ`acx73!0z1MUDkv-a1h5lfO$hsj{rQqyGNH>Pj{kqi68t_m-yuFk z36@F>SLSuEKPb#SA>;uHAnL*1D9{L&4$2bW)$fZOC{3@A0h1^v(@#R_huqL zJY}OgtLPk)bu*{G|JCgLV|HWP*wO-h6McI*)u1*i`Dq!FU7jHC*uSU3R7~nJAZ-zC zGdUv*O~SvaNJ~$Tu`|?9+wF0?BHHh7#GDthC{Frlo3A6AFCj^PF%P#8Mzi8>08D=A z(xoRE7i91L$g;L*8ewE*h^4g=chiioeFDm+nt zL^4Ulsj7gX#2D`TIWl{YY6=k!gs4COEiMyUa%G05Q$D_2CJF}+tl5nacMvfP3%9=O zfQK!C&`tMcz@utlBU$k`zbU1)O=LS|C)tWIH882}Wd|ifIXMz2z)PZpH%zQML*h^l$1SpcL2!xt2KqN2&3xydD)j1!x)KaY;Xm;|C^|05r-HR{Xz+BkDOJ z=I;76%S#eWbMp;j$CT@QKWotxw7nM-{(LJ;IVYg=jZ{0=H~p*%D**>qEIHKYF(|et-Nu6nN*);u3 zmt|&X@Zj4~$LqYjKp?)Syql+axOe|u`@SC1Ei#YOR77p)$J@n7-SAtxy*|5d1J1`| z4SM;+PfOe*Ka<)`>Tji$-^1DU6o*gOu(qFA!Gl%?61?mzO5}v zJa~LTjdiYfsw&+%q4j27Bx4{A(vYkQsf3JTXrzzSOULfrUmVV~)J?tGy7Bh?P~Lc3 z{IuS7R3FdWTy`>fuK7r+nnf0TZb~>OTEUDDIQGD=cV081k8173qD7^&1j}yDAAFss zTMQYem{M@(uhz-0Zk1o3i-eGcBe(31PwDIP!V>$5kSG6-_J;B!;(V!~FP$^N{@hFT zFVinO4f*C}Gb8_}1{~8_RN)&6uoI|DFS%TGF?cM-VgG)E%gW2$7CTqwWX?UImtyP_P9epE@RD8<(7m-gyjTK78)qSt%S-Q`7d>~u* zc69$d>OJ@T^X=0(lP;t1OKz1}b1zSP<2mi+n2=vv@&WD)F$@)+6;&QvopQEX&zY}N zOxGj2)DYe+H3yCw1#FB=g!MOd?h2@KrT6MG@SOIMY&I*OQLKuP`k3FqMGPaWIxfx1 zJWx)b>))z~S6izdtX+IWr}zS$08P^yc9R}x;2EeX18q(smLEom+L71oM-ASt(-G;X z_RH71l{EwhL*##ai8KJH8C&&Vby3N7ANdOOBNKBkeFk#Kxv>qQ z-qwfhlKppjXLFTr${}#3#O9C#w~=`s7|NdKqyKhZ{x2PlWC-p!`*@nh=AaZsPOLiU zla>;&{hac?-h46x+L)7#WCzEU-{z1T!}1_mRrp~7dj!6nVpke?&-Kx@_~~$^29Py^h1@~{f(_MwSISIyrvu9EiL+>c6aIdWMusr2ScAnkL6|-~5 zqk0M45;P!BpB__I7GemSl2pT-Kxx~YvgGKXV}g1^OEQ67u7-~^cGq8Czu^?@=V^Q2 z&&lJ7d%rb7PRy-UH@~;&v{NsOqUx19Q}gnxPInJAaK~M6Q=!{?`>}*zq`&3lXTN1B z*&kz!w((~ZPVL`IsBg`lkhSXj@-i@wY@C!f&Mb^I|#84TarlP>1ZkeUz=mXyeO!kkgBo_*i zZ&LR;%2iohB<3>j6>z8-wENQYN^aql@RypaEE&oY(V#g`&pURChl8nwC;G{7Q>-Ya z?e55)xlOwbr_1a@w=oX6q@2aqI{gammwjiJd_RS=*}GYPy>QQI?ShWs8D^E$PJ><(mD97|H(Ni*XJ_(nJUAHthXSwE3#L&&5dhbsDZn0y>RVEq*sN-EB!69wu=hR z%EqABkoov(=U)`%T?;>ZNtUBQ^k~NDJX7;ZUToh<7X>U^CRj_fj&S|7;j%S zLbR_oM_!6*J0w}#&YsD-{_|y@TyLwByfbmiI-NU# ChQ7F(wJko%g@7SqRAS1qc z4Usv~>Vf-ZZ-7e%z%$4f_ap>d^e!b(lA!2ADA6=o*Ljj#F$knB%fk)k#&P8g7LsJ z^J_C3PrR$XtPp?vkSw#C1y-@~0JNfMzEA713->GQI2vkP4P{$-_aAe=EnXkVAGAEO zvU`ZFN$gR`S?I;DIaYoWWolYotCdK$?|3p~W%5M2ZgSg9a_d~1#$!L9yw63K8mMUQ zIk*99E+W1QEXOoQW`%3Fb+s(UluJI{j=Lprn(}}X-`u5O2VfMy(`SY$6r1dA&NSCe zYzl%TfWp=zA`pcupaG=QV7_w`WlQ5Jsps}_zKyyp5=N65?k?K`r61e`&;Xmmj_OWV zCk8zQh+wzRXauJC0s_7-;#qEkZW(JGB5XWFkUIshU(bG?1NTIsg1NXb(s_g|hX4VE z!}XYt4lo@Z1xCtW5k!BTMG1Sqh`w9e*jv=7;v=dLu=c<$C~`VTSE?LqpmIkw_^qjX zk%c(x3)(dr!3*CfxLmB4c1DSDmX;^E@tDlDTnOY!YW*GAGU0BZ8 ztj0FiSEkOJ%jxHd6`QqF^|3h!*Nw*8#wH72UydEJJ*Mw!yfDD9&EyOcH<^0OuAiG< z85gp2pA^Nso5=*)^+88=N}+*j6oq@m+9DCvJcw|Fd3lYp)jjS4y#d$_Bsv6fQ1Po^ zI3M4?xDG&K_-Q!w7ITevrQfZa3;af9Y8TM1T#{A>-DL5QF9zsf&f3sTZ|?+jQg9MJ zNlLnfgi;*2m@<#+tFW-p;zO^Ujg1%>dY&k5KoX5NXMK_>p7w_8xms|TplnQ^k_P+% zC{1o-)tU0AJbgM+*lkp|~-X zP2DUwx3%}7i1!f9ZL8C*{_iGKL|U3h`2569zF+$Awc=#Vy_*a!dsa7P3zO_mE?cM9 z4zfO@$CHXA;J^VS4W%jchnN7MHX!`AU?IB(3KP^yeDsqQMM**b{4Lx}VH6F>@u!HP zVb}RUix|B|Hqb*v|>e__m6@{n9uYSF@BTs7?<+F5=`U!qfLzbR4;ycq9{^O?duX`$9JX=z1 zL)dlJy;0ua*!24FSMgjkhq|wUSGcxq#+#=ID)E&5C)#z99ynGrB9w2g3*pb`oFwG? z5G~Pe!>$ET9Wizz?)$aGJqppQ`MmvJcr1cl>zCbdG$o&qiXYV~CLc$kf-j%B-uQp<&m(J4j}tGZHA3eF6+i49dxZ&r|-`8@t7)y-wFIPp{5^>O84w`#W6QJ<0 z_`JsI&J!NKWQB<92UnKg#RM7J&n?^vk7PQsy5Jqs+MV5#UHJthx{SFg2Fp_^pi~f^U0}J(x>dJ26{zb7ZrW3CKEQ;iU#-| zhgLsay6aG_u*9FgZL^tUSbW`KU+;rWT+Iybwmf_C2kN&xPx&@hY~Y*!w%J(r=cIj_B)#HdH=`ND0iOJ3Y((gN0*y6s<3Wyh(h!VyA z9@`!AqSHEYy(G{?*0oVs@o?uPZ}(}tV@0(gXYLMJn}pXtT~F-Z8{@H=^udgv5C0S! zo7v5q&)Vr{P&_m&`N*w|M`uP3OMA)SXQ37(npuzB2p>Pux#NI=U zfg1t@69U-a<^WStMJCBmo**YD#Aa>^M!3qSi--7=DU-*NujX~bkb0}2bSB#tBLk?W zrs657xcWF2s(BMz@XJ6KGCJRcC@)ooFR_)iM;4Jc>K-Y#I#PKj-+vzcMtt4D^NF9Q z`IBTL&JUmK3HwqE)u*;^D{9*Cv5%H7FK!$XiRrZ~OmC^%e&+(8)9{nO2khn9C(QW! zqEFe^Ha7fP&`_6;vi5&5Fyk*$F*Fb>TOMwsb8q%${A-63aT6XK(QKr?z1l`~eeXz? zm%Cl_ZOhwjdfvQ@nB24Hx34o@#*^NnfvlaR!gC5Gsk}!c-MfzYOWF%49SPCfIYyQ% z-dM{F`iZ(Oy3t{QD{|{J>mSU245UbF)@H!;P`p%V&CpWg zMWD2y#D!VPpQjiVeK(f;k|{&z(R}0fGWJC?;r8FF{5H{sZd9MkK{S{I_9W&%Se7} zneoV{tD;#P#(rxjlO|lXB9hI6ce)O$g-Rs159Fd}o%L1`9t3k2U zw1s_bC1rbAQy7{xqBrb|K5opXd>j&fl)p7#dIw94(#`HKZpL%cp>tcjM%$A+!_$W5 zLl@l(kJ}4elL?-8``$#apfJYF+VN#P(0ps^;XU#%%481TZ*cK@5}1)G!!X*IY?0d~ zcl$wW^db+Hz+H!HT1(Y+o`3!yV}AkF)%L}YqDYBINr^OqfPi#Kij*LsB3;s5(y64< zBGL^4(%mU3(j~%|Mx;x+_kZt=@x~qZo^g(YgPZT(ti9G;bI$b%rW<~*BCCPb zrAZ2BbISm`KMx{5X&wZHFz7M?i5mJ(fWiT6E+k^W-sozck+nu3Iby)My2|4>Ixl(| zkS#?wQxJ81Carz7*^l_|Ijyev%5)^t`AEImH|n=jV|J{iRxbA?FR~mpA%J#=KlXdvW@$%o zQ``N1=g0`&RcT}Yy9#M)YeeCg)+eg8M*>{^%xksdTvdO>7_r(M?*4X(8~Q1BnnOW8 zY|BIOHz2BUhT=he`0JWr-2Uxoab;>IthRk3^lLHl%R{ME%8TcJ9jRPj+}K!pY(x{Q zfG&X|@#y=A`>{@}YxhSl&eUZOx*lUjdEQ&@I?5ZTKeI9Q3)N4Ys=hO^lKgf_6k+DG zH?I*5>oD?^FGrUk(8DyeZNS1ct?9uHw{hi|-u0&&qjpMVQa5kFEh5u~3rJ_Y%_Zq6 ze(bVl4}CHeG_`N~@XJ)JN89NBEVUd@bZGQ|)K_kVbEU7JW$1OdTao&yoBO+lWg*vU za_dR4RiS!|sMU-CeFF4_S^~!g{PUtk7Ygn4{h&)W$A+{7?~!jIWhCeea-Uv+j-vV8 zahz6>3^2B&Q5zd>&=RIm0y9w$@lr6B`fG<`(nTFA z`PUC`%RZHE{mJ*)*Ds`BYi40s$$wBS->XVL&&+7qg`1`i;38_qr&Uvw=)R7bCw1kWwCD;a*s2 zu-$eG{)v_VdKEBaATeo%TH%sr?rQIN{<~vkJSh7lmc2RrAkg*EIEyr}wr5$#k0Mae z4ktH(T2Z31pxyVyJm!%3^DAVD`6 zB5*{|8)^ksgcNVUg8?!@{yt#KAZrBnGngGrS}zKM=_p}nC!9+Ia0&7yEL13z6BKf& z3hF6DZJ^kW5Zc6`(6huJL^RzWb=D;h@8Y2duu6m?W3`Z$JZNvs4d|aMd{zkl;tiL_ z=;=*mY?~p_-_LuQU(qfehK6TU1?S$mHt|b>aOUnc*If>-dQwbN2xnc(Dy>`rJ0L{x zDuPnn3in@!T^fCs-MPzOeC#;)Xz#dd&11};&U2p92WI18)&2?267+?(o?Zt6xiVt)VpjQUy7y$Lnae`N==T6ha4RPqc z16K^`+>5sH>>3>Xf&C8N($ZEyg?%F&fn5BFYcYm9f>s<#Z@Im09DD%O&Y+uT;Nej} zYIM95DGm~EF`(Is|KJii|O! z|0kT`paID;1ELxb8<2=XuqWws)`e4_mn>Ahjk+9A=44on3p|T_bMZF2NGPrFqIoX~QJQ6b76wwk?=+ZrxPRVDLFUsmwh+ zjZt7kvZEodH2?q*vI5v!>_j_+ln!!ZXEmiaM-^$nl04~A_8GbV6bU*6EE2#UKR|to zWYP5}2?#G^LlXY|#i48fZ7(aYxfNvUm!h-} zWNsvLTiNFik4xw9-QM{dp~2QgVb@q@XWkLDgi*=D zuV1Ko+AAyV!>_*Rk|)vbJzXO(O=g;C+WpgIKc8Ogd>GG=@{762tWtps!Ad_4{oS9m? zeVT*sevQubqhV~~2OO=3Bp`K6v_KK7AK-L#MJwfXL~$M4N$u*ocVngv{k{u#738^Q z`D_`&+ycD82BFD%;U?l!dRflr!$GWp3np#;tIc6<8albOBB7=ga!q=3qX$>geO?+% zAr7M20;xzx(X1vixbgX;MPiUeVzRJn1R%UWStg0L zb3)G#Nl-*GP%n<`u3T~t<^jA0$M%09k3&iW>@vOi!-Rr_z<{0!^uYl=Gok+`1-hYG z7UVEVk!pT%8)I~?SCqGDO4RCf56hZR5b9rMI1F+uZavlNaNT)RLPPCO(C;x~s3VPN zsiwg+z-fE+I;Zb}$hI=#a?9|88VWxB<`6+~2Xzjvc%$C;Y~_WQK)Q^2VywK8dx>?P zPaU-4?v^_7P}v-%K|fl#OZ!-Owp7WfWiLqIzP$`ta$fzt99ZVfCI)gmyjXD0lmOLV zkb~yp`U=NakWv;F743l(JVadhm5~NErWKQuY&EsD$P{#izrn~xpEP2`+?>>Onql1J zRr@jd+_9(6+%0qoLAm7njuhMJ{LqGejcB58h+ERH;xDvXGwFT}xmDGs1VJj?3c5zX z{R67`03&lYajXfF?noL6aP-B!lZ+LsDy^T@Gj%WZi`1Y8(p_FS;I1D4`v$a@!zOr< z4VhFSJ0K-ZS`&uLzygF0GZGR81qVz?Xk$@i69beBdF;zf3-6+t@! zzfvGl7Zi~c7(o+!jqDretTR;iKzl`=Qxl*gXBf2X$}|{Klz0GU40<>~odd=uoE!-) z;17as3Y_`_i2@EkuBX-j+@-M*j{0%H?Svo5X+UN9{oB;c`4rd`kd;8imt#?0RtBoG zMxaImcj1QK6B{XD!U1CleJlt{Q0IZEDE?cQ!8yRb!PX4tHcKFzz5sg=`c%?kM8V3A zMG3}jc$@EeOl%EGzHaGpZFOEy+wQ zqBB6iLjevT6|?~1+hrP1>dVo29<1I1$j+$Y#%z-(vX|z`HsK5sgdqL`-O825#2km6 zSyA{=sHX|Ooiip!4{D;M1c44G2`ZExgCBP^@l&Xyyt8#J0pCk?LjET_`T z$*(3r%L}AkQj}=EBqSz29_59EtRnaK;b)jiz+>odrWk;|9yFhSHV9GtSb8@&^O6N5 z#~<$UV{Y*|ebVZ75b`AQm~6Vt?`ECi_vf}NsXuR(8$A-X8+jl>fNl_ntQl`t)4;Gr zW0EGeL_>B3K8o7se~UEOfLf6?Ak(tVQGe<6{-z&ovRcP9wx9i7q=p6X?C0S>w{b~W zkOY`yTs407=mC83mq*4V0X{c4TBtf`rM}?#A%ja}=2n^nnLlT|Ib;PY+zU+aN@dMT zirNF>Bn3GPQ1#+{ZWEI0B~(OrrVnVxqZ@0gDJMN0B>yAk&K>10oPe{~n38ZZGnxrP z>7Hj3cxP^dA^bR0SV>FbcODI;d~*Jz%P!^FI3vmJ5Ypwh*)f2&|thZ}23A`FcL z>T!#4sK@0r{b(s~)fcjvdu=4Pu&V(}6vfM~e&gN(H^Mo4sSXx-)kmTu+XUMHd zPi$CD%Z&z|MQ4hx_I~fBlx)$0!D!+@)h|K?)C*#7&fEl;Aa=-*d<_Lc;Wtp8n* zOb&0Q?dfOXDg{BUvtZF}Rf=_wl$s?nfAchD1in9~-tf`jztjlwp#ZM;T&`^k-x^aC zjAxY?p%^DhsEO{O9Z2ffN#!hpKNM^P?V7rXn%DP2suf%IkMfN;r4gnCOOK>k_g%D* z+u@5?S}cxR79DTO1%th9r4i!)KezZ5twsRz0ai61jKsgM2$XPP{E*)x2vG5lhawO8 z?S(Pq5d8mNI%xaEB^gIC{eEE1+QCSMgGS&=I{ z3ByAC`D#K3wGs<4@!E<4@#mLU^_Tef96tXy!f3%3QJUV5{$^b*sYZS_QyAR$P&!|4p+X^@){gS*OvCP&128 z6@p;NiQgzQHU@kJtB2f)8;xzxvH!c!zjZ!Qdz9h5nT7GYJX4uc6^!?t5~HrJ;`i;T zzSwf9>9rRU2fwVw`fOMH->gkA;p{K6SjfdLOlb)b;KoQr3um!b=k&%`xoe<=+$WGyj>j66FsPO-d&v0f3LeW zw%U1AhS*4MIld#|P2rVB8`VXfl)>wA=i{1-cgarM5u;ILb6er3A+H)9Ob*e3sRN1+ zRE+gUAEG>4W_c=UvCMImOU1m2`p8bRcb>4#%J8sFPcvKDU7%Yc!sf1sg$2GJ{lsB# z-14_V;FO3ml*90QHfk1MCfapRAO68CUD*?D*UH9}K%6&L@inEXS9TY~K+x8Lu#%1I zVx$xE;^gl_DlDhtLi3mxwZ0!uW>z9#+3fO6x~KK}&oGGr0ETWM{=)ye@C><8S>aZp05(@Qiu5 zJ4Hpu?H;m6j1$gtI6q9YO(K)|20v9j>ID}(r9y8iY!QF8yH(qt%cq7;nH2R-pOCd==Px&Hsnto{ zE&guy$Bc8^&824_G)zKK0!=lnBL|-))G8ddsb>0wc68BK{t&Av!~ZJAsQ<3p1b>gX zgDAdJy6eXZSCXp8=w~io3HL>oUow-a9{v~pgU==G=JXyzG_S3KCD}Gizti3p%JuBG znzUhMCL9?oDiJMHH8;+PJqav`=$i)l8W zkJfDTpZq!5FtXMlwIx(P)3lhso8_R@LpOxOJ$`pMC0x{(z!!TciSN(Ge&OdJx`fW~ zTrSIO`ub7!)5W_oSvVO9>epl{WSl&yuvg%32O>V0t3~|g|1Vl7-A6OA^=vEsvMf_3 zn{)g*CeW?pZWw13Jx|G)9O%j=7H9c2 znZDfDG7jyP+Qj^7JCk2*!CS<9^zGMICVbRFZx?;c`eECv<#PhJ)$1=$F`E&1(L=nb z^nN3$O>!A#)1>t)nOha|MR{-IS6--uB$8y%IzRa@&JG%<_Z6*Ga%w9!I{BJ1NW79B z(o!C)7J5*4_^|M>ZlHu$mno*NOpq5-wkorpM&9>@h%KfII=Vy}Mc)6Spub>aOWV^9 zr$pl<)o0k^scrb4&D1M4`6!?8WrS-bsD|!J3iP>Xa}qzF`}(ZOnSF?_Nmtg3K*so( z|M-5;5U)r(x*|f?IkxM^{~pOOz3+iisCgB7IJs2x%XhiY)ILcf{Kzm_2oYOm<8v%u zUMJ31x0dc2-RI>gkCbm${*1VEMWdOqxOGA;e?POqgh=Vx<3+}7-y^ecc-=tm`tj^k*cgJ%ny8Xp5JMD;%JRY5-u9iwjp@h{j`eS6+;mZR9@7i>g>)S!g0hbMGGYO*Sh2GF zhR>T$f=dRnquecB5v-}dYK@Ukk%`xVU&jtM$M;vAJE&vdrQ&D^1n7AO^^NwwpMRh* zGF;HEw7fBA=SuP{F(@QEFI+#<<8M{<9uDdv#lCrrBI?97Vq3Dv{MMVOVY&P>@0Zk;poO0d6DzI785FlRFCOv_wqPbIeXhk> z#`Pk3azMq7ZR8Z%lB+9MvQSoNO`ahCa$eH2+Ez;+R8$%r zR{_!q6#5Z7iD!>&lJ;cviSCzVYT@$gN{)tgLr&8+b}aL&ng%EG^#4JA5Q>oi+0J zSYofC`FeBrQK!IUYW;8oCVyBpof`IctFQ+%lPUDNi>|IwC%VJUIRA2?e@Rit4qlGh zqH82}c588zX5CXus{~WqFvA5(21kOYm80E$C^{4sm3`lSu&9|3&Ddn>#68OUD@1TY zop<=~Zn%vl{pi?hqAd-b%Ib__Ywof$l&r>}-<7L65o*{4*kQ)ovf=9g@^6_)?Q+6^5-S?3Zk18ql%k8X8eZ0iUd5AHgdL5 zivAH}Yb5ixu+6a9O(w11_+Y=D%v$u8WU}bpI7;C5`TRq3CA5C$kqnV^qe@AD7W@v6*d_NYgv~N@@@V?-&ohr35 z5C8sZYnSN1z;EzAj_|*T^M8}-bDr?kcwO8rte# zW7*|mk!GJh^Gu)7Rz3Wwt@^*xfXqP!t<~Yd6iiK7Q52|*&@*ns4(;|Flf&ou&U=2| z48j&f3A}vi^Sj8DBM)Y@rdIGAl>*c=4Z`iFsT zr>HHIZm)sq8O8cNA6J6A&XH!bnD;&Q3auo57wOw*@3^)Zd_q;s^Nx&lC$f2x{#ZZ# znf#*bb!o)Yf4>T0%Q@=QyhC3~$4;}Q%^QdGAw(mt*1@9YkME75X-Dtu=6?Ct*nSu^ zz4miz_V*G;%kn0=q!)isabZP`@E%aGyl%Ci;6l$qbJl*z>e)VQ(#e_Q`KU;E>v%j$%~t9Ayq^c zrGbOTMJ0YVOVN0K#z#6Ka2l%bB~O5^lPWZbKkGxq+qp6)D){$0)FeFN$VxDP=zR{V z&K$kHj>z2CW04EB&_`7q4=p7Sh1E~Zh|#fW0;@e^nlC+Z6>nN?Bi`)t7TI{Xwpf#! zGvn$gc9*i@@c7*+?KZ{*1!9ss&_g? zJJX;>C@vjle+2ozirQa^U><|rcVod5&v$2`fn6TsL;|HblI8?3mwlyA7+x?e%6$w5 z=dNU95ozw!yRlCQf;&oaZt4nff^if#7Y9(3^DI$G(u^6iT*^o5yDFjM9v2YBXgr z8cc2tilV~Ixc%QiO1j^G4 z)b>CwY{{%&ju_>WbcVw}RdUtkhhI}otKFP%v`>aZg?TqTFb@{QD_)teW0yyh92KVc z#tX^KuuaK&m!aIpJ@jCy5aYYS7FwT2?~JVBM6cx3z23uAiRPJZKIS_%+j#*p>c`&P zd4hXS0)l3?L032)$E?D5^aeG1(V}2eO)O-=tGEA6tnT+O{@Na+2LAC9sgdrrt+p~1 zy*|n*$27`NeyWtR`NSZsEb4}g>zhAy2ARXFDl2$?Z%r$FXUfaAF#FXUYt*VGn$)s~ zzdW~k`DLQurR8|>mD(4+ja71upg@C!_@F?OM2(;uL2_ffJ->tQaD;J*h;)71C0jcn zD0>oS%&o{B!w6@zM-(lcRlRoh=d*Sv{Q@nY_3Stk6?tNNOI3>eGq}V)EnN!e(Z|V^ z)z_fG_yb1`o~asR^9w@1w94KKMg1zFkRBcK6r2KVgx{DP zM7vXtGQIzl$R9>~IG$C?mmwl@kN1yWZ~F`&($q3663x$twneu~-8~PNL=&cbQd?xN zJd6Zzd-rrias!P7ZX}cX5WFJCj2+)LtlE2z}0@jmVR~c9-ls&HLVrGJ^)1xAYp$ zgld{AGxMbG{a*JTo|Rz2seYi>u8sS5xC`gb_~WmOh2p zmGLa5^2YY5E=M<2__ll1)XB+7ZwtRMS>fgFE77d9zb&g(TX8=7uxrUmdycAyw%RUM zTBA;^d1>-G`Df_(PLwo%U4AN1%e_y&GN@>nsLXWfB&)%n1QL@pyXFoNld{JNy0zV= z7SXM5bPDn<$Te;$v|Aphh0Ac^67`21+<8AT=zX}Sg{yVQk~$+LfHM%DypndAd*$ak zdyyWkbkI4`vN(9PK3ID`PZ1{d(YX4p&y&|t02K`uy~OO73d(4?oG9^N7ByOLNWw~$ zuJs$a*<{X_b;)RExy8@hv{`_wt{owXm?^zbwaBOmU>`ho74S9}?bj|@A=%oqyqux> z1s%?(4Rek6NeBelAple-y7e<&!?rnYl&nJ?jporwat{w3Rj=c8h;kDF$9wZ2-&3i! zxQ%*i(urDe*Q7dGd<~6MBWI%o+TA@7{K4aEHgo_WA>YE4hQ^4X)S z*guZf_xdK|fYx1BMn%zWv2Z`oM7q>aj;)u0JQP;&P(+vL>eKzFo_#p}#=MW&655Kl zo)xH{&RuQl-*OYPoApY2$=w+fTH;`Lrp;gdV^{aOkwDk6Nti&gfaK`>2crlp9Q13e zTOKRz&sctXM>NWB?Xi73$i>AXZkU~)jNEU%)2dQf#hVga>|ER@61-8rwGvrOWByjs zYi?)FaaX#^RPe;?R{fryZTju4=r?OcOhzaD^9L4uMQQskG{@H7;wxLq6k==Ahys@# zhj;ym0-BwKv2`KINs+6I+^b|u?{{14c%4PK)$&PT(pFJbCDUU|P|LpO_A#O74&$Ky zuZud1fpFz0`Ok6;A3KcpL`|+s%&QcQYiMd48tl>Cd|F}siT{hL%NIV&v81YMix4T! zBIYtT!qJe-1kAHyu3;Q1MbC>2hoOdzUavmjPrYAT4T@M1{PbPg=X6q5L0OM3MuAJr zCzWDic5!%7lHc`vZ4s%O$li-Cip)wEtBNckJMpPUD(2>*76_R|RV(jLKN{48F>THh ziabVWCdi~3T7F~-s7!CD{`Ha1O_gPpe%4^lvg)4HRxthShpxs9BB;Fm=KlG_wom&` z8FLejP10yChEQBl;Ma}{n|xuD*K2g_VfsI9O2#{?lXy2Vt983wv`(63$3i~zlRjbM z`TXJz;a*3oz`RzId|Bn!vor3e1^b1C?9U6-nRhC#_lam$4( zeIrDT;N+nE&k@VB#0XpIC%=Qf5Kp*sdL~_u-2Tw>>&ZQpJ_Wsnkpn({@hc45g40jP zBLYD@K=y`v;y&}#o)fv5P0U=B8=0^)(q)lk-|qTwv$wA-LY>G)d@4V8X@flR#g^K0 zjt{i=C~+f=Y;{p5EcCT1l0>v>2b1g?WrbqsQof5ggstnWZ|PmaLFA{J8s^vuFs?Ey zs%=wqNcnR_s=fRmzn_JSG04K8i;zqXtv4)P+tiaNJ}2epN=XP)Y=cc8(Yc$okx{zzL5DxQs ziBmkBV*0>C%?wU;EjcWv;MdH8k1U+dX5IN@Z@K4nI~xPtCx8}f_Yg%ldK{7UIL{?* z@{#+At?|G@&Bt??8G zHmGS*x|V91XtC_QZ_hbf@KUg`iyOQA6-(XtGb(;&_F2V3XJTQy-R=Yt`S)YP z{;?cxxSTDl0$+#f7d zD*x`wz_iboKaY=e#6z{RiG7z|32fB5M7QF)E?Om1h;~y5G}s$m?ELYsYmf zb#}q6jJJ|MDxrQyyE{mL{wq0-tj?!zxrrZBSPH>eVMM{3~D((+4e8^H_9zAdw!7DZE z-mSR4MU|(PhZ5nY4Cc^Cv>1|C4O|Vd7DVdE1Dp?-JhSdA;G4>>w+H$^D4B8M8V$18d|Mo$rxvMi_kUNDkkX=TVRmzHe^>P_Nw7iZBH1u=C`D)= zyZEo@k~f`}7DmdP?ayZg(6S~56Tg}b2MwDy3>?X6iLoV@A4fm(#ct&ty4F85?Ap$c z`G=2pUMpJ%_0a^M)8*vt*ncRA0t@okVgt?dmg_A|wP=cVSIEU{}^( z`qK&1Q;#-50d&WMHKIgIZcaGIr@U$1yzu`0d#AjTTNzZxmVdhWyoEw8k2m-UC68fM z+Hw8B<|5nv)5xFG?1bqa5!gHLMyt!Jc0&oZt5+8IPV2B=f0XeIo@m6HA5cs{ z<9iKi;y)%PPLB|F4!UumsdU_O#v5 z0ffx8E!8=Yc>qxk2?8MV_n4)51AOhDfei>bO5H^wyFf_>sbU4XNdPmI6-wEjo(29R zun5O2tKtrau+0Kmop&iIx~maaJ9Px;pGP&>^_qJ+^LiC@;|;uii9i`Hbytp;aAx16 z@WqUQfkkX*HV8(F8e7;z$z;hiO}6fN9lV!jNVsb%GxXpl?73&NF$WEi+vO?+^QU6J z>D<>i+ALVE>UIUBEpJ5s*!jKi8ubO^m}tCBYYPX@sX9f8$9vfjY}Cfso5M#Wa*`7W zbt1jPR`i81Ky1H%E)R&!@FhXu=Yg>Cc2_JLOboz?^Cy7=J$i3B__YB0X_7>MQA zHSS>o6L7UY;zbBJQ~;O?{t8HJBGj@I&Y&a%yiz~~!U_kjuhiX0t2MwAHSZvm#_sD9 z=wW4hc@1_tQeMo&>iVuMc(P{&K4(`@aQr(>48DDS)F==rIv*_2++5<1?Qv^zTSVb zL=Z6Q8;>wbS1VYkXD`zzR7nIyEdYxHS}jU}5lm@dEZ{do0p?jAehw&afxHZWZqX%> zap);nqJn0cM&aex;te5SbpQ)D3IMJk8v(#%pnQT_v2dJObPogQwgNV4A0%L5M#EA@ z$`b>=_XSH#d{PoP(no=N9BC+ECGio}b+4DPQNTUR)YG#;MNp3=#zkQJ#^W^fj0~HF z?Z4`E)DrVtTq0jPRzYokfXm%Bad_dz>pFvx1h(e|1(Lom3hfHZpX(Udcn2*zezO4fMUS-Iv=lZ#v)cUbLF1yPEQTxpWWVah2Rr+iXpKvA3}EI@_^i z*;8u=EA|I2(L4CGPh7;E2RxNrRWCQ((^gK!MQg(unV}^p-K<9YlO4;U0Rt>!0TcJn zzHFp#wz#MW*g`U%p2A>V342K9u*|WuSXxU_*N=_E|Iqd0Am*XoU$#U2+eL6w!->gqaURkr1kkDw`}NtOex*{ z@%7OTc$_i?Yh3^OjBG15hcnCcLr-DU0HbCv18hAK+*KVfUI!ay2cVhf2d&?nA9o^F zm3%Hh&La5nT)~+gLOB@LVF)-PJbW zkFZUi`wwUv5fOpw=%1UL%Y}m`{d{$O{Vou4dzWF8oqE}Qcvc3QBA|s2YuCWO zmuXu?P5c4Drg1>+3=nF`C#_>az2I(?Ie&}^H?$df>vSR@Yyuo}!JcfC7v^eBQ+jWC z*u(}v;laHSY};##pJqHWcVUwM*m2eYHWvVCMF9*e02)U>JysnzO=t(b8IW7*-~U;y z2s)2(4c=19*)I$%EwSZ3CR*ungo6tEb{!A&Pk$2EEWZX9AuuRvbJhigF{CR1ST_I>TrpT%cVcaG z(-^iP6lH~JY;?b^cLX)s9Ui!#ekT2#7{(CdBw$yShd9nwl~bg*r0UgbQxW|Cu0+@2nb)e{0EjPSr-+BHoPJvoe+|0 zka0XaJHx_68ZLld;KMh6MPS})yV7F_V^28S+fW>vPC}cuz5c$Xx0v{YuUHGOZZ?Uc zZLU*gHhA9N+EeoLoqy16e?KPV6Duq)!kes)eFKVIE%yF_;~4ql*dH^&`mFic&HQ5e zJo5Y;x*5v$gF@^nVF;I?`qyI&CtGSPB;XnB=(?iDt7vtEQK(X+2KmdAk5VA6b{&rr zGzh?$V0Cj7`EvOqN5ESP>}u`oIA9&>u)hPpge8a6H-FuMDU;Bb!o>WoT!D?$%IA^( z)hm5A(kPRVO*dWax)gRgjIW(HKjpM&ct85!8x1Mj@lCh0#F8E_F168fL7 z!xM&)>4?IYEdAmT!)v_Xn46ooUxDLfMMX4dcoE^fOVT_5#PL9yc;rSsPzpw7XYpEhcR7KN zj^vqw3^XpyM^K&3c=qL?^zV*fBG8Zn`a)3m5*P{#$0`)Yy`&WPNp=&7VC(%^-47c- z@D?$^O82v7()9Q^(D)aa#7}PkIC=9YlII?hIWLIxcLTSKRA7`mm%m3Kf(CI~8i?;W z?X3>Uzmq^z4dN0o`5}CO7~)3#ul>`85Cr%TxNJ=!qc5;;8HbqZHQuy4P`(3a6p&~D z7KI34nl~paz*fPJL9YMyA7FceR2wYXM%dmdg`BWzCqGrxe@BX7;+s!q;KMNMouKq_3an8U^QBK!1f*?mh6xSu4CXi zuIugTh)m{_%fGm7qrCmXwpXxxkG2pnmw_gf{u~uNHP+VF=%RZ-Qf0p?6Wnf#)OMK3 z6-Pm)JDnLD<{~8ZoPclV@YxIfz0vPs>Ov*=5!4AAkEX0Z;# z@iIT(P%|7~HY9U*F_dFuY6@6);QyvG0UlFe3WJ>bI_%=V8yb|BW{b5+Zc?Y_T8O01 z_t0*-3Y;EZ7gYjN!ss3#<$syj0G@#<(|&!cGv)Pm&P3;e#2lN_GVR-)OiIb~MK!Z+ z|NOxrgn*%$sWsTKfcyoBg#+^?$Kv(+*nKb=O4@hAZJ+-h!wLyz6uoRBpIeZD{bEP( z?_d6%ozhcNQ|~(sA;xnj`~r7huf0%xYyOsN;MM8?K3wIKn`kp7W_PZ)x|F7*ShZ}_ zeb8ik5G` z`kdvLWz>da7)10ytCL3en689^6{XnI41p+W#^1;mk36p}-dMDDpa;$J%9@(}a62!f z_KCqX@j=M^?Lxt4=;9z(97C*Z(PueEo$SsX@HC2&&xX?=EB;Jf(9KoJRhrqG24WBp z6+oVW63_AC#36!4Ji>tN;$lU-#Yiq16( zGOZ}U1A zjjXXMES;N4Eo<{}jV?os`y2zE0c^9Do4e=SiW}w*Mst-Np*VV#HO~>(n=I(yhjh-R z(CG*IFi1=A-lUtRiw?BkZtg+7V)k)wd`O}%DMd0&R)JFtlE z+&P{r^P2Z59BeCAD`>p<(+m4WI^8Q^6)1vwouf8S;^2x^tO$?@fmW52jRE2rAin+^ zJy97fcHnH+4LfM}pW1+UL6c1R&DD|db&?S1Z#pRF5K_LSa} z95o<#)PC-4+q3n0q~jd6Q`_l2^EnH2(N*v{k^#Do$=|>JjqrmOx3SAb*a43 zFX^0gWhX=xZy6&2r_@eFhvz$)QmkqJyxMKgX`f?raciZyAMn`Y*T zGR(w_NcD)FbRBS(P^AEd)UPmdfPrC=+pbr7%z*=V3VAsy5} zSP9>m>R9${Y|ihU?6l<9EV_;@6-ZzfrmC#^-D@`PT?e~mNU5QFA&nb7pZcWO-?`#W zA9sJ!GA4h-Ryqr|jm_49EB??N&l6X5nqUnkAAOXP!N~3n=_s)T{lxEu<+XzCeHX+P z`zpU~K9rB)jxF z7UY?zeZ1Mn(O247|ETx_>`>41i6usdoH=G=nZL2*7iX~K6I3ET9M(;4sJy_V5(+TU zROCreUfOd5heOcsbpTckBIh2gZm_nmsfmMZCAn`1wgUz^3XfYqG&vqX)(CAlWT1iv zzQGP`MMzF_YE@NZA|mi+Vab3N$|`J41X#S%6@x47JI6p2t}abbpe3+oO0{s}j}lU!TJ0FBw__kr|PSMK#PuJ^XU!ty^BC-w1!E=~_H# zr?Q?=_}W!X&Q}>>v!KDxO@J5lUV5prb8790)99_8%ql)5ZC<6Pwb>6Pe^lFik0DIZ zNd$3oEuqe~Ka_b@Brn!fM<%Oe{1fsszp%{O6RS={*-g#OPBrcpYRrjYL%wQOCx(?) z9I9VZ)#S8BIBt1ZjEM<(6F7|#OIw&KaOl>LQSO_OAzeP9K7Fa04dUitvJ1YS5lc|h zz$Z`>4w^kN zQ-O22@>-GJk#$1Wyn8I;*yWkDtk@sB{U?^BM-P%TwQN*oYW;*O#Jbj>gZdfhw>4-j z6-O)O+Eq;Jf_b2WgG2j-n0N9^ma063FBXYN3T{`d^?`sI_w`>L4DE^P>Kx^?icUBL2v; zF*-HEr(Vc&ZH=6Egs>?)p}Vtu{nK}F(Yrfo9deqjX-aC#$@9kFi80V(A}XfqC%qkK zSfQ19{&hJ}(^kdO@&y9}>ze^EPSp>KH)v-lKZI}aubXW!n$C0-kC$#R3gU%iF7BV~ zR!d=>_*HfauWoH-V6{80&NX(XNf&y<9u5Q7F7WL-*0N zE_3NE!@?7#tZSRU`!sub^CqkW4~0H5WU|;3xi8K?JL-;$o-{QF#g8|ATTi*}c9S~XIU4KhfYFj0 zuR+UsEkPD5M&x%Ms}|Ne_QBHX+g_N05$*D?NqX~D*glu(+|ZmT#nwUuw{+#vVw*Cp1>D9DP_ZE9HEqRwowi%%WU^ZPoqWz}jU^7nUA z=lVKfbj6Z_lJ4@%zL_>Rh7K zLC=`DDaDreUi%C7`Aj!4>!xds88W>x-mEU0gBOJY6o1uTou1irLMH<|R%WjF0Tm6c z>=$ec(Wqa_!#4O+d5Tr9Pv;bE35k_NMrbg3i=rJkZDrix~FBz{^IA9I5ParN>_I0>DwcRD$%#6@P`os3Jiqz zh{NWGot$QXsOxsnW&S{Mbo(T)%2pftsUNkp856Wj9@agvv`ilb&W}Rv#XU*9X8qBLQCcYJZi;S}4m%jHF{_4rVv)AwL(@i8 z+^5|T(kr2n9)I={Lp}DIg=C&DK?N!-TQ~Asi)yrPVYS6KL(msI6-al#f1@Lb?3U#u zvowCD8)tYQ9JkUn2A;E!Fca?+%e#kkqIMca7}h;%{y?w)9oC9O>~mv`xz zeU?wk{W6&;xGM~e;~*$trD7nX0yX_d20Bgl!trhDm*vcb#0f0=?M_sdRV*>DgS{Rq z*J&{5H{Q2>3atJx?Ad$Pr4l3a9FG3n`998FXIMQP!_iawaV-9AI?I#9 z5Rl`oTSH|m&}zS@@Z90vsc*eL@)K#@ND2Op0wjwJ1dL@b7*6BN7OqgWM8b2*^D*Yq z-nzz?6|mgNeXsq14y~T!EgLJdmSm-6Vo>4Qmk?}MGKqWT<;c$=n7%`2i{aNDrhLdv zu-ZYL#&pTVL{3ChYEX=ICyDl!*H^5>eEPZM8xQi*J`umSE<_FyI{qL8vA{t8R|Ov! zg3~!#9aCpOYqanwsbj%IHsXt6bl+#_(xV@UrLi=8=kp|f?ufAmTo%tizjf4r`jwQf zmwRh#3&mI=?)8jxl&W=in}coDTvN)HaTBv)%@3am7vq4UKBdl1XB!As@rHqy+ecxL z?I$7s@H4x0=TX#iV-jh#?Zmnn+aIBWuOUc~OjE$Mf=2(Yoe4f_7__dRPiV6L7w50R zlHH@*&X^x2AXi2{Kk}W7(S|7P_~++?L(mX7$Y)M_YQNasL@GtV;S#95t!4kajQ;nnRLPGro8ivW(!xH44?4*{BfV}6#L@1+jjHy&raFmfqU=%3P=NS$|KPig6>Qfcu*{?z|P(^p4Dxqn|Id%Ja(ng%uJ8YT`c66tY8C$PFG4(b9GL(2sj1v8-A%1*-NJ$bS!8bsQ7q`( zZqkXDFS0diU`x))>g9|ds-=-v=I9eVP!#Yi{rKAF|Fi%rh=81Y{-|(^kuu8crc9>Z zNSC}w3!Lf=B~DAXC^a>J)kQL}&H1|*9R=iutOcKW2vMB-iUQQa6nD+s!s7Yp0Wu1b zpulSbd*L$!e4J=(x3=lnwYkkix5SN5cm+aC&<3aAVulIgfLM{fd`HKn}4hR{f)^E?c#PXv*HiTY7|HkBvPBd z+V9=P3t_mIUUT+e9Vs%{;{M_+wVM{XylA;P@&@-yiC9{|vya1Djxnde!{i_8vfSxm zF4;!5Tp)qcxjZeoB6%x%F(kV9H9`M$L9;xMevgn{-`je$LXH1g^&>dO z4gXt1#Dt?RaFo6Y`@i1OmJ=FG+o*JRW`hgJwTbFAxLOhZ9fWgsyI!Et?!AV=SleR$ z37{{ki{IN>D0OS2EdKI!T#O76+;=prjn<_7akg9>+nsQHE_-%T#7?%@MVZ>cA$lUd zwXwF?5u*VR=Zxsp@>QQCrB6F5Rc8CFU9sy#zo~zJi`-a4@#$7ZQ>PeHTkEXI z{fK>>eGdn{8Vj+^0*sq!4$b!Fl-FC(->x~yrV^**bZpI^OkOxwR$VKNXWtp@aESk1 z_q2Fu>)^q@CnGUESFhD4rrSX(K|kH!Y07?Bl%qh}-q~nQrvOwE-oOINVHi46(X$HC z7en0815TeW@turbof_OMEieC#w^D8MiiIF$*&CdoA?|#Ju;myHI*XJ`Rn!X92`QcS z@qX{c=+2$4<NM$O`X;Ptxc<#uIfS};{5OPA?(RNCx*#UL-clleA7wHT;}7;|RG`#3yw zz1(HL!jT{PVOnusb9FckroRgi@3H{CRw&Sv4}g(k}H^6Z?yw6f_| zy0Y_~+qK_Iw#l*6C+W-3^x}k?Cyv-Z7-glYV~azyIx=S7pjsRVpKB@S0WQrJAv~2=-0#C%T06OX{1a zW2Mv4Yhq=pUv-O=i%E!bLbDj%g(=nK5p7`=1cbnD(nu{r>`h=RrIJg@jf zkS-LAXQ1o{mi&Bj`hpzr)RkY<_bEmCz&EGPdG)vOw6%-gGC(u{C@G`~qI$S}53~TY zqyrYSDs8mS4Gk&a<3!GCJNmya4O;oP!5^jpUZZqEC z#tu$i(2p4sb^44l!1D)+2J#KT%p0snKr;XloIt=|Mk~m%0Q?&qVY-cXqj>6W_|LdPi2(Gm-YCSHLc;TZ zapjN*_c&pz`DP`q5?+ud%Lv}Tuq<%J9U+sY#nQhOV1`X?Tl1t_*Z=5e#7^9sfxXTc zpZ=1P9L&^uPU&h@`k8|dKNB9uV;R$`{Dq^@OdxH3cY)0J0=Kz>M`e5h>|>%Xz|8`Ru)FT{bW^fA&?y0z+Rq7QO)h zf3P>w!?d6K8y3WvBpGr@(ZqaPJJ|h>$$stL^*jp_z&EXV8?tM*tg1-h3q4FbHWY~8eq>fV-h4_qd2p6nGC8wfMcRMm$Ibiq{OBZ9 z;HP5WpyBl_XDRzEm zNH^iBBrY?sH17*8!N)*rKAYUyo!s(0@*kmpAk+(MEvHo2=ad`8zfqAy?!xf~n+?JQ zd=R$`fl}kZQdMi(dUnzk2shQl9k`pbY1ZA)0tW6FwKO=raRZbdq7_LZRZ2!7)5pel zk^>9s7~lbw7%S&HHtNI4o`GNhpg%@Dk-%i6&bk{pl(~5*zQGICgUZ7ndaV`jp-c8C zdphaMM+D&rFO%=7uYrdV2#gF3--0>-j;FG!8aPVdbVWJ9N%ncrke7$219-y|Z{P3% z-|&lKmUTBup8qNaP55Mi58lwAI=Y|W-Jc|P8(Ga~oUS*z2!V!xT0-UTi;Ii@E4kgN;Pd2>enDtqDB$M~2S$d+Ke2cmZ*d^GZPhH#gK5XN z>0$FV&Bq4z;vlNsg62>qqQ12}#*|(?h_61A>(_CoOws~-{EFrd{#8{L<=&D)OpGJ0vf0_BXjo zj4NBd^Qq>@>pmJuXDN3ZnbCyGo3o2Sv>_xoM||}FDh5!;fP((eBI%k`a9*1tYvNb+y!kTTH$f%o8`_4URISL%-l-H!3L+8HWR_8mqMliJ@9K3nDzg;!l%F$ z)-9*M7=*@YU4q={$7KFE=Yu+g}*fM&F-% z-ung0bb#~EkGn)s^39Y}XPU7S7(+18>ihX~7v!Fx4c=yHAH5BO-(DoWh= z((w*_)MU4gUhEg$`R~jTxf4rUW;MzNmkP-Byz)A;fO~QVR|^@NRt&ThI+92lX`Cdv zC4UMHIB}sIha+u$yb8iQ9{saJi$)`U7EemdtoOooIK|}EJu>9;Skb@#nh9$P#<%OO{y0@l@j2~V+Vm%L*hWE5AzGyWpULjMqY=$j~%Ad7MT=x7fta7FDSZLF-A zQB?~d3ZPrr&87sS%%c~X4^V>a>-=7aEWjE(d-ST&Y=DBSYZkf*=whEhUggXT6_~by zPE6>J1JuYT*>A)N^k5~hg=(P-ifrc-=29M&?+%6nXftrj;l+QS38tY zVW0#JC@0Z81S)OM@2tkJH>~8s5E-g z=h3k>Hu=B-2fd#0>wkjkRRLLum8qu#4L)RRE?*^Z+I?n(pZh<9;?Tj1(pW3!>sPei za*7|0JRmypIU31VeVYm*THx$zK3`-9${Kp{D|el9S@x#(bYZVr_Z^!5M9Dlr z)j&FJ7TwD&CqpMiLfeVrwqJ{9~d{qih@*AxZ3&GC|H3uZy?no=b(YY8E<=`!i?8*duGgFn-%s|I+7V5GTXDA6 zCmw6P!doiYhA-mrg`WKyl5ntzJCzFxWN&(@AxT+Ow9zh>Mx+*K#Wm_+Oe{V8Yahu~ zn<27!F)}0byT3U`r`dN}5)^a;IR69500e`mjbNh*E-S`fPGGtFca*_U(b?5?ZpVGo z<37?B&lVdS8)CK+6BBDUYT3z(8uXcQW9Y z4xxx=TP=z-G5_Txxt}a12zcx+1k0I&pEF4$xLNk0Fd~P4fAW`(pk+8a+{kWe$+)_l zzIuxCF9ugjRM`ZI7&r{u+S)+F?vIKOJ|8c+AZa;CY3T;d0_5O9{=3NbfFn6HU7(l) zavHd7Uar58018qfFJ27D%8gG*xO+Pg0(}5-)%h9tv%ebaW`#C28P?AC%QiCaO~#mb z#$?3K;K|Wr)v%T3YssQ-3#~UfV~rM093iT+Ce`G9S&-O0Mb6-Fq3}~2KT(~C?tPG zpnkL8g$oFLp~Hr56*j68cE+I7LMVK>Pp{G<2#UjD{?SuV3kaSA_Y4Ca)tXa~_2vT}I3Z++18x8B}r18mJ@^ zP=Km3xpd_~hB@R89eW>5_u$_Vk)<7NEga5E*j*{_*~?~kT>s@TSLPa#+}d|eYxeU3 z1D(*1G2-9OrhDd+3}^jPEAE}w3tXv!ZUL9g)VDp+@1nhEJU)NIQt(sKY}$Jh!#6NPaTgX8an{EiEEKpuJR){X1B|JCo%_Wv3k1ruS&IpPUg7ld^R z+SNvVcJ`)yoUUj}c(%J2TYeevG~xV%T%>+4untX$#taezVm@E>dpQfBbiiPfey3n4 zXBqnYR~icS0pTMuIP&MOyYa_Kk5#i7aF=X8Cwx zkdJ7Eva{M|WJLXBvE(voxJpgv@*0KTXXX3bcvR9jB7WB@RA>Gz;ENoukkd+MT}n0;uOqD_uW7mivtA8%UtR zCpM?0?{}wxWA|2zvLfc@G-jZ74$n(Xm1Cj?(H7f{dl;=ePqIwKW>@_NYrKR-g>^ zP1DHC*^9=LlbW%HvuhwD?KP`)+z`qg@OwwY>@N0@R@}!@_p@&P6N!GpP?dfYkE6hv zC-Q*fh#beXK3~26E%pKsiPb8OjgY;-ci^;LrXR6+Z_19koq^gM%RjG!Jq5?S?tX zm<>6`Dt*-O7KwQyof)Jq2hDQubmZwBGO@Xh&2Gs*cES->= z691N$Oo}|n6+QbqaQt_`cjVj;Fh4lk0nZT>6a)>rv$Hd|yy!@Wb-wBil782YkSy zf`+ttzjKs` zgh+hUWgOXXtYLS~9uc*^92k8WB&#u?R2-lG3G+vCR2SvTgOfqkB7rXTPpm@3>G9b5 zilixS7T!W#o}b1T-n1Y!ZS(bg2mmqw^CV^i>B8Oz1qLRdI6wAo zMklC+dme=m!gB(Xc-w*&JqEJ-^GDMjmHiiBc@GCPtuzf07H>t>uLmU%{5lI|8t9)C zO=VE}EE_miVMWGBaZ{3F1fdkhMJ{?Diggl-ubw9Kf^)MXl!z6FuHdu}3gNYSs0l%M zBm>(N(6NrjaqS{7Mg*gAi!YJ1Os>Kz~1-|g{Qz0Ixp zFttYgbJilf3M+5#&6^mDGzQ)mqwc<2)HnR8fc2VuaYjTu(YxPw%Z%N(ebtGwJXt^U zlU`sW9J86AIIPE3Yw?L~SE$8)`ae)&%;jvd!J7Mm2v_pQLSdKfiv z-J0^zSgUhk>74_a8%j?GI5BGFg3$Nq*&+tCdN9F*!ez|N%wBO6E84)AMV5NO;MCMq zUycYP1a_yVQ;20g0Npa2r$5t$Q4}CRjr}`1#cPfrCxzl;`fTdx8VpZB2oPLH=2lkO z@B)<>+hNKGU@6j{dz6=Lely2A^PuCLo=s>ur-R}+2M!9r^&Jn_wLokOyNFD{@g2k) zz|{rQ3^4ZwqKymtxq;2fRGO`%v{9`TkQ*BCg23tMoacK8@4z_L==kD8%hl0 zOi&|nfByWriDbt-n@l5Y7(Q&k#sz7v!sM6O0i}XZ%Ct#7kI;|PrY}WT%8opNkX0uHJ#mxRs zj7i1Y@OOd#J{+8lm#1s}^jTg$oB)EuwNzy_iYk-fN^c9ncAEL-FP}Exq6c@j1^nYM zjoxxpcLOSc8CioUS~f0ZQWIlBX`BhSbv^3L#x~%u||E+nkmn4cAr&QP@rA@E4d5<5CH&Mk&I|N z-@OzvVNX2BG@CkV?t%b;;~#SnqvQmB?h5Hb-5^hdl$BR|;dIiLz8om{@MgG<|BSLj zq2Pf`sAb)IlaV_f7w6*%Ev>yFQA6V-P=15#kU_*Z0k`GUjI)k=%J7aKe|^4Ix#CUG z0C`^A!n>ENpRLO3bogD8g>As`_~cEa`?i{A!>N)9+Hm%V_ii}%B|U7XsZ9xO?QPCE zUvbTYvErLytU~Qb&$4Uf5rv4n=KOy?;)ydxWA{741JWY?jE*jgg!XOhk6wM+ z91?w=zmy@hr0z(5`v%S~efo=Xmc6(V?;unJCah!LH8sAr(?UqD*9QazF0ip}A2l_Y zrGdl*ILMN@?4VJF%tlbF-@XZ(sbA&R5H|#W32ZV$yTXXa7$`0iefoYpj z7r23cnJ1UCgg>Kt3tk`}yO-K;07D5&qT+9XdI5~~LDvLDp0>6&>^?xqaj|a01Oi|; zfK5UH0%?q7`0)eFkeUsNg_#JZY8$>cFd4Kr-cVl;g)b~TXk(L)_+q4fA(0?H>UF-3gw}`&yDjg~!4Eo%(rHcuygdD=W5k2P?%97FOEDIW^ zhMTYZJ?8^IAs7|8otSSPB!_CTsc^~Ug~`}SVwbd>;@_v>myoS}GT>?A5)-X(74T|V z)R-;}ZjPNGZKY30E;i)srS0NCzl$EXR%h|(lSs}ei@7~_bGwb1Z&QRsE7SeO%@5&_ zEN7W!d0wDW{E0C^)_Bh%K7Kh-FLkN2kKZbZwtAjQnsc{*xtLb=M_5?l8VIzr{xK$p zW#x_W$(FhH2qW*8#jABWOfp=0k10My#kSIDQG$)@usIgCBk z-4hs4H#xxA3>;aI8MFfhV2gm$!Sii*A^GbJ?vCzF_d>GJA6ET3CWqGfd}`~00_3VY zJ3Ht-s2}BeQxjNQlyW;>xdWljVX3D5;^F-WiiNL%>|8>%(MqMtht4*;oGs_u%MsvF zqQmsr!{=Cx^#|fIso3pl`_!q^N7}oi)unwT!;W`H5kqs>I2IP!1r;Iw>C~s5=&ROS z7E`jLF8N4;MU@z_$BJc%k5UFc{49*=&XF=xm;U-{!O_ApK9r@qHY|dcnX!E0$F2-0 zbhGkE!Vo;S%ChWpecBGJFptk`pB+$?D8dWCa5%H>*nH{0eE5pjf2|%>6HiV31Q9!g zxw+Zw*OR7HZQxt8Vl!Ee0_=g>=X#@%m_zu}IAGvbfqE+=K_00Zx3rPQ02RBiTLj^K zvYaNE0NKpcee7^>iY9V=EZj-Y_FCIz-;l_F%2~tcjD=d7GZvBkn5jHxK9Pq>EpTFv zCA_x+tfkmSt>;z2F$t>IPtxYSLEsme^JDlMU+ zYOGeLHTU@jP8i|=cFoSj%JaFRCv3Vc-zIj+-X=CNKMwihh#Cq`nv{WnoY_6}pwcFGPDD;% zPPV5|;uMohMX(m1mJC}(tq^wYN__|j&o?$EVLURlhW<{l_8l~xJ80i;-82HQKGX2M z630typMEYPovLs%a3%e+|V;p1wFrIlm274Q_M0B%CAF0_59j{1>g)CYxJ;G00! z4&zQiwUe_yPy?bcI|J$0K)u4W5EMZ0@Dx2g0&*XS$FC|lJMDinDy;9WvRdvYbgZnd z{5{08eN}N}I^d9MmcIE>XM_3e_vVLG&jM}{#?a99d}94;=_T|?abRCyHl3evh;X_5 z?DVBUx~LHiJ-zAX&rss7{kfr`!H1L8bRID;yJ^K8@HMX8df$Yh#`vds(UlEDjppSC z#t+xZd=ziB5uBJVX~bzIwRpE2dHFS-bUsZLyrj=GB%0*bl%`Aa-hDs7eifhMR+5>= zWLeZaGOKA*#b|y@YuNq==lvQ&BnQUo{`%mX^iRj`IpFvqASy${e)NCMGSlAbHDY$)AWl0x? zcW)aL8$D*>|8d9R43)VGs&|-x0GJEazr%3ihO6BerbcN)zCfHwg%-ewC~J;xV-^0T zJJt6xy1EPpu4PLq#PomR_u0og^4~LS?yBQs`#m)hnj^rN(A$oq++63ERxm2^#?y#I z&1r5I-QLst2O`)cRC_xC)aAw@xE-_Y&#MwjEmjg2ae|BAsL2ooOFIj6ioJfgx~VZ% z9XYV^wIVxVGfID)ChaAS4L4ua+@DnQJY9S&A63dq zO`k*|Eccxcf1iF7&21`5k@Y)IjB^f?`TFv!N>1K#DGM&H68=3cwHCeF90a2_j~9C{ zXDmy$RMohC%qspm$hpAu_+Ah9MGjHOS6=>M~ty$((AIfM#-8v{+S6+ z!U%%Djw0jp@1rByg0d{k5jVA7m1%UaDbn*ert>=;{@;9Zb{dpQ8(!oV7LR`aR-ZNy{5ZFkVz9ePDQaqZdAB@g7UP}$ z$+=YQd4&9`?eS077cAC09v^AB6VUH{)9?vStbhJh<@@j6Vc+^V&Ul+A4IFLj-}{pL zh1sw-Z1kL~T!u(izp(z6_)?b^AMZK=`sS>{>L=P)9_y~2M8p&(#>YA@sM-v2qag>g$K3}~-cWjO~$MGNW&liU#)9o8hm8E+QW z#qVQB6q1@D?Jo{ZM9y|`b8@;E<7M4%JWsu(LM-8p{=^)M+N;(t%wo(d*0vp$C~qyt zDhzwB#cs4Jm{Xj+W+D@Zqi9OW^;A^6|BhH3LfGL#pHkH0N>SjbQyfTJr)Yz!tBb=H zH8q&NoIgSr_#^txLVIjXGbhMaRf-m3z52_D(o94IyTqJ>zrMr*2i9J3rZ^nV;Z64P ztj~g}=4^b3B{3(#8G%hJq-Bd0vY}^FyPemx#i}HoZ*bW)#A6uoNILdk!79Y~?3`V| zT?&KMD|F9F&q_D`rC5MX3dBXr(W4^7*xAD^I9ubDddwJ2hE#h@rhMmb*hQo>toW(- z0k1tbCv>$tc-7VSox8lvpMmVTgMhM$c;Sa>y0O?s-T@|m7G|liVC3wx*t`VhvG^e6 z$8W$*(&9A8@{qS;g@_w z*t&g|Kw92?!tzr~E16)=rygeucgTxa3o-^4V75TF;p)AN_*fEVb<|B}TZ14_y`jdT zhE{1QqMaZc2@WwZILWHJqL5U^13?SSG*3-~K@mjNCuU?!ug=~*q^hah zQ72`Llw>OAouUEFj;ZMNE^Wu0sMhqM)GweWrj!@NDb~}BV`bw$SCP}KML2+`i zVj|&2$%b%>MHQ18CXM}9MyC~fGIJ&jMIskFH+COj@D7sMQcwI5lVud+pOE`TDKvnNmZ8J3aS8~=q%KM2T6e?-*!)tD4gl6wMmhy@4BV_J=$TKHN9%?#WKB2?9 z<{KT-)}(hVM6oHFzO>nn=PAw*ubEj<75XOdqK~b!djV6E<~i-y4JcJ_0WSnp66)zp zHn;&#i+A7T46C;s8c3iEfMEjM5UO?#AjbgZtuKc0sivVeymeQWK$(2GuZdm1d*4my zM`1-eZtkOb*54n*vwf~sWy$dFb&NN)Y;$6+%Hu3j0{Yu=!#Kp!YJ=QWOqRFKV#_V; z?RG_iEn*)V!)3(N_5(w~;95Ut87pRY_22m?%*4Usv`f*oLZ^A29y(C#!gpC$qgog!Nis}L*0{!xw% zS1l5ULiU9HW7jDap-eX%J;779w5X$ZnM_CMSz@dvsKV%9*lqZKazFf)w( z1C;IXh(IXN7^x|d_@%U}e^LGJ80saqWpK-MsCcsJgEB~4RczOkW{}Fk>H8^cWNVcCwVj4J32#y5VMtq0+oG7ODl%_) z+f&yZAw!==oz_2mHC4%CN|2DKRrpixy%)l;;K5w=b>IvDor5e)hz`}#9zVy+>|GR# zH^B({&Ksfhi?J%d_H|ip{suUG01`VhR`%w9BxG=L_$fzg4N=Z@Pqb{!_Fui+Cjk*y z84DAn(3wAlSqwC)BwbTi+gP2W5~t;RP2CGtc@eAoi*@&OXniDwyvj z$JonX2Ka3oHffbLE;l|1mWy!!-9PRZ|Y-uKGPRspw`V+N5`JD^UKp$@l#7 zLF+TXHXla^y7Trvp{ywIcR$0#$F9b?f7}h-&vb`a~p5%Sd%q)v@ zJtJhv@a>pmtn<&js)Pr3^zXtU4UZJ=1bKORkh#V7o05+ITy@65x($vq%Y8{)zK6_ZKlgs$P znulkZhnqq1NGY$rM6I+RBi_RBAHLiqRlM$N>argid{^%gd06|{PkwnNzL8WI92|)p zCH=POJox92*sE8V3{h#b`=K=_r%Qwr^-hs51^ty-I7Rssy+0>x9`Xw6=)9-X;JAkw z-P@h=sEv$xWmY4@fKG?eFiZzwh^fTdJ6_6OV8bU8_e-kqBF&UH<%#MTVoYNC9`K@d zbxG!^nAVBy3aC!t@xyyS*?B>*;@nY*UBMl-9q+mJyQnE~xI92y%LE=tfL`v7=-9#N z5v9-m!+4WfXF(VkkSgHu0RHbkTOqj9V7JcZiO}{gI!;b0$9~~?lR$encSxF_thj6B zQcoFAmrcV*6ufi>mJPJpVfM&d#Gt9w2^>38`a$0CZ>TEXWH=~?R@%J@z;SSIxsx$) zlr?D=X2|iQ&S{z7_naT(Bi@%M%OIEmJ@ON*`MTvX7(reHb^9K8TRGN|0c!kFvSyT) zexyOd2C2UhUAN=boK~iGUjo!!FU6jV)Y`}AJbT-6(!Zq@LAULpd%m{S(x1{5ZAe0o zox8j?onfP+8b%kE&#y_~n-H><<`wP6e)%r_XY=VntY*|s$MEz{2)k&GCL*3rKv-m= zl>@Pqw`IlWjeSOUxN5^&Kjqfu?k1?Ng2$bJEA8l0fX;&1r2CRR;a4wRgkw@jG}v^{ zi5Tc#q-MY9;&+*ozkL=3PCm2F{J*%-120}+9b!8i|GHzKdqN(a5mCl3+S?Xl1osvh z>52&*9N5giCJAQ0lV(TA%U8tqXOf!wOw2WI6l?ZQ1s*m;jxE~e>m_I1;H@_?v^BMK z<9ann+ByCvSbOJ*=*eHf3 zCTMSg%?7L(HAVp%V!(@0<1~tXr>|i1CCLR(UtJ!d5`SUUKz6e@JbECDJ7?`tDXZ`= znwpvz!@e(P`2B>P>~ERh4m$0V3Yb&zTrcsvumUeV7*{x7krbT+$_L7K0Q~=B9dF-+6=I(H7jR5M+GW>vzKLz1z*epQhk@ zxtFoG?02~g|IL}y7oYM5x(OIAK%vy2>_R15gX>)McTDvS?f4EDs-TF@r5^pngH*B6 zW@E;8UMGBdVw3alb;E1y2NVh6QSlr_^3=_>JNI5WVG`v?7ppETwLSTn!7t(UB`GvX zU>BcUF5Hn?(ugjK_AzwtyCovWHH{8)HZWveZ3F%s?)<(X(V}OBo5HndQj7`=#n0~a zFSd$h(a7jj$7y4-w#sG+Dyg9(_>^3<3qEmRps71D>t3EZHP;_Vo;I8c_$}!vQ22e^ zES{>*9{xLI$0fx7ebFjoFGRLwwwKvJnFa6UucBYy)k%xrd#shBnp(AKOG;=_SVH6xgES7@ zcpzI@Hf0CbB(NkxMID3uX3n0UX%eI?OZtGk!K)->H~D@{z+c^ONqD&AVjvZ)|sbl!a<$0m!EKGsIL?XTRcJ$Kv$va1pzfG@UX`o`}AKspR-Co>Rs(K5&SP z=r(Ee#{QY9Fx>L4P771RCk`39dr1}N_^?7#jD0Z)EIHXu`Mh0Ea22A{mnUodUfL-h zXwC1Txo@3ag~cxs_+Fg(%^hB#%gGJQ-Q}6xcQ3P1FAC^C-MR;>!lkVI9gFn9Fb|In z?MBvK4gyg>8ysw8y+U~IypGAGQkq;B_aR?og>e}zUpg`D1qvTLdg9YKUq#uQ6vaO z;EevCRskv~kVpVg!U>b2Kp8-D=JEviCkO>L1bGqID#1#U1bteZ3|dHAE0p4J=Km<~ z1DYKrgmZT05D=)VW8ZuVAS;YDfnX3afx-I=_+ap5`J+?Vbhg<*_S7{(idKvbk~hIL zwZZis{3bYn3|5cXv(&#o;pdkEM=s#sW?+(RqTVIkU8tE}ee@t4SJd&`KgYdi-n za0PP@p+xluO}beseIMmm_ZH#mv0vw%_FHCX z^nnnXMiDYZXIZx0XGW$Lm-8^>k)0CLn)?fR{>I^#;a`3*s`fguG%zXQnPl&&`%v`W zn>c^DUeI9~8$l;KFzjDntbxmVkCp|Tx?;ZkE9gX&mN6Aycs)YTdwfLLA#%nZY5dKy zE=nUh?u%u?CyD&D$}Onk>fE*{0K`QJ`e3jQ#8)I!cK5`U;U$_USKGR(D&x__NHqly zaA5uC0_b}{|5?@Q4CgM3RTWy_zbtOO(K7W73M-gj2k7>n1H5DTA)xgS#wWo&kuW>n zB-5f~>9PJ(Tc$XgGzFM|+3{?GcfO5co#BT-M*t)?46uOMpqvGm77){w^pEq$NZFqq z+Q4vG*ZKK*{a+n%W&O;wez%g#gH!GCfeXHe4Z>xdRI;4OIdiUpQnEPlO|AxuuVYk9 z(K@PEMV&vnv7dkV*!A-vMsN&m=xWm4;;G{RFKpYg2PN@5wcgJsL_}i|G}1koU8Q;% zHZASru}!CIfBPc3-I~6QOggo_8EYOJZ?ds#mr)tk)>QlRukhVaq;kH}kK81!)Y(zL zV+MM&PWQb(Xi@jmU^wRqw>z1Zy1k`B@noGGbwj{EXPS~S^!T^qeQOf)9HbGrWjgrC zyxZ3A`f1d5%{e*kxzo_|vf6zXbg3cS@eeruK$*UMSv9n}c$!$k>)?N^n%Z5;oy zs$H>ihw4^WkCu(JMv{|ZYD!B}bH?vH`bzoA8q@@A2`7WmzN!Z1)stYl!cDRW;t(jq zP$Ao#NxC(i+OaT^J%CA=e)o`~!!c-4HMq?OU{mq;thxVQ3F-A41)q9_iZ6Q{GfLVm zQ&0IF<&qH>y1uw}d^$pTGC!>7A;cxYmLNVz=I-r3tZ=8~@-^dwb#-V4f8%khwHD-m zB3FP5a^if6nSju&d*L@+wX9;T93j||N^A-5OIV)p@QDL(v538~FZp2o1%M#ez#4eb z5mt{7pO*#EZP2@lEQ$ySbc1+AGKwq{gqx8eM{Zy(I=eI9g_l(F0i7P4eIgkp`mv?Nx@?4SFTxXqB?)>4sFf-j8zYQ}P%^~O zm*Cz69DmQ^Zas0I2AE%hy*0{=42EYQa(*Y*?=%-amOY?*AmQ}!@j;C#z`qASC;c&( zITPqQK_iX2=-|);DIL&jgr@K5q-k(MA4EzR$kJst zc*{qBieE&|%qdZYt$(&T+k0z+l9SULBN`cKk$k>cVX$-1&km0{3XQ)>gp7?ndFZa= zo2sK5t@@I&c-)dMto}o*If?S{AVYUfO6&zEQw3cKN=79O2B;V$6u1P=62;g8mv*uk z=g^c&t3WDvy-MvoS0U~Ds#W^fbFaicob7L4w0q_lkX(N+>jDVR_;K1@5 z_@A&s;VJ-Y)!d2X5;L|14HYOGz`fwJKNMi9_x0N#Pk zAcU>Y{fK#lvbKiVEO7XOdqT&O{(310kfAC;4RfSTo~?z9R#n8O9XsL#>Ti@ss`F-?NJnFdwYAa7Cw`^3sbN(OUy#& zg~Jq?@OLk}bovzb8Ao$&bZ-`x_Ft!Y&K>`!1;C@+O7#nm$^7U(K>=nK8NNHDzHY0d zw6A)pdTVb!Po<^x;ew+8EPA+RHCfb4 z+9JE=0o|aM#xd}_;89E$qRtoq?;@};WlPM>$vHkRT5q!hQ)wXXD=^bCP}i)ktW;vetA)PP!1shMnnKue z3`a{1WnqP~e0AHJn*8Hd{;9`M^Rqb#0zu7;@!QFlp~{vrviJdhnGUYb6RdcZ$F!sY zzLd$6v$~cR0CO+jwb7EOwJQ z*lq2ol=8KFfnb4(4?&YA_eSu}W|t<6_?V#eg3m7wzs_XdBawKou=b~zeKiS**m!05 zcMUr3R;%h!2JqW8u&QHM8d@q$aJh{}7WKdGDW90R@w2mYo2g92&aR|s(2(Tc+tN@? zY4ya@qbou^gDaA+iMA_|{oIkkY{wFB=#W{{%siDny0Nq<2QxV8GrV{2!c!u(+b0L- zz2uxG#}>`jf`=GEA87NZ6y3Q%*&}kk{Q^!Q)a-+*zz@DxPIxY)AzgvJi_Zruhi&f; z=)<*Id@04(JR-i%+o64Xb&)^LI||y*lN~Qg$MqkNg>2bRyrb+Wre?M-N3VJv=|g)r zI&iT9vv|Fu$GN84C?bnih7ZsYS%t36W0Uif17#h$$&InO$7U}yMMZDi#NiMBtiz%6 zmiaNwmCAQ6omR#b&1%-4NVafRXUebZJ44Q^cJMeQ;+oVIsQu z+vFsaZU7F!1Cg>6@DaUZ)PMR{-2|U7U{176$-I0I8+cAI@jcb zuT!2=PPSj!5HT|%Xwh^+FiNJ`Z1{w3@LJKQNYR3q>LM6}W~t=cUtRjZ@y(3kb z(MTleH$q#S@XlKSgw)LT)o8a33^OmIu=g@l#xM^N2b&7h27FHEX%uT~ZwCHu@oL-t zVvOuiM_0lRiM-oh6cJ~g(qEhll`;h=8kbOpwBY*U4U#>e!bV&A(ypFkREKGAC~y!h zfEz;N!H5oQQ3#GW`F&P-O%8L2v$W81$WdDRilj(iHd1em#&zwrO>b7BOqNCu*+24` z4W}(!3kz_qrHGiEt;x9Ln&-lW=cn8HAjc-q!n$^PgQVoWkHfl~m-kjXkv?5DZxz$u z!(_j*>kZG@Hzps*7Tl)p+Pi8(K36WYALos=d817b+qAPjcIaDudjKg|rsy{AH!kd0 zqi;sqFM`*&d2wTduS!p|;cm3zOu4Ajn~BV5*!{Wh$A4RJ1&DWO$-MswTTVC!%X z-h3*!csMQpd;&(605YK5sb>F-7+mm09ORZa-jX&q4vKY5(r`{4OjDz*YSwO zeaAE9{!DDfYa)3EW$|TB$YO78HD6rspG6J8@a!du>YqiFb%(fnS49+!|MGQfGka=Y zmZSDO_RBCyW0O`xzBYcNr)`_f(T$3GcZL<1qhBut1DTby4fTq@4Su48STO#i>ckMFY_{r&x@jv5d$K)~AA zuLn$q*dSeSLtmF^);umIwt3%NeMX9`&{J zB$TQF{3RvZ?u+-6#I7eyN6)!#>?@%K2zPwDAp2;CH{0&yS?*UCkos4&`zEq{u$PWj z4NY8Uqo>kgO}TXhlrqdpxX%f-a7?eLWtjDS{H$yI$1A+R{ma#7kNy(W z7iSy2>E*2cN+qk}QWX857KPl`5K%`RvdvA9>tbw>G*Z5u-6~vBcUk!`b75NUL@+xL z{*>!ktN)v(@*x;!ns>CZUHWV)Qkl)7VSd5=+&o9Bc}TpcV20EB#^akusuZgZ6o)ah z*%rvLV1K%T#CSFKz{&>~8U}=76Si;v^Vb~$%N!-;iWx%~HhC|RoduhOS07Eqsp+px zS$^pa$%%pKl@I8r-cl;7bzNgqJLQpO0Zn2C29&La_OFnzbGI66(Z1A1aixF1%e}F0 zsDHECu&SsiEp_9q^S}n8WC>HufwZvF^-j2F*$bho;?R79^h2K z+t!M$M_tkYH-icYHV~{wV3(o5y0>8vZvt0{68^}56(wCSCsedxqWNhUO#?*KFjGeV zF~8<@#7i3R(Vm#lZ977aSNpd3K|DA+GDf--?3IxJ(P(=Eq6{hhoBK+)p1yCJoskqh zeK>3Ho*PomY)^`|;A4+#mtU*-IC;i5>RXYf64U&Xy%IM=q*V7G4pnqcnW~`s1g0leRp)wtJBiYa z|Ay1a+*2)XrsEkc2(ZZva$d-u6<7t^kRd;63o_6)sEy>5cBVgRp8^+ax7N zn*O7}HkbWb%uP^KY#dMw-e`?_v=JC#bB|6tK(_bS#_gJ0|1570Rc0E0?`OG`Jj>4X zy*HU>*u%@oiNREDTqMX8y1t?CDh+JY(Tc$!2wFR^Wat9umCL6Q@)9!sc5WS^bn_tj zSGHRLThg_pDU@;?tPie183toKy7kVXCnqO7XKwstPzQo+7}Rd(hczu7)ix-Q2)?gE z?p__{GL(!k!I9h2LYX@h`rp{IUhlwx*x?2Fo4i7YWcf0UVYKGS7WNvNbLH;z}zCU zFW-R8G1vOdRV~`7@Nmuc35|F2_in!^YDGL(=Lnt1eA1OfDc1uRgBvVbe}_gF+}<3O zUlf}(rBA=hQteeqzf4bO*hk5iVRTCw?<)ns-#}M^f65=f-m2=bs{hy6m&Zf(hiyw* z38f@!3aL;bM3z)UKSkMP&6Y8iY#|brg!)xdnM9kiGcuM5NeD@jC1VJM8f!ub?{(^Z z-{mAXgz0s4y;ZJojIl6iR_`X zv(#Bl@5ZnBsWJj1OGs(1CK{$0Lw)5D%UDmCY<@K$=gG>LsZ2x%J}$F;eIs2=pDHKR!G9~XDWO1Vsvys~*YL!hv zS3a3@L!cGeRZg3Ah>muh6FRo;KONVXjr8;jKJYSHS0>#H^6%YqR$4f z0i%ri0jHs$=JJ}F#X|Q59wSNvkYT#1;PMV>cv0ZE6v!M6!gN|89n(YVs06x-(}NXq zU87dv}Gn2@Sf#T99JK83{7sWYk z&@(k8B^2R&=-hRH84ciBEPgsT^!NS9Wr9 zkVxAdT8Nhz2R%;bb#;qyzeV}6!)G$LOuF5rKX@Px?G)|9hdGZty?MGQhHRz~obRGn z|INEZ#w+vPB*6!t?OM=4cM3))q@03k_tNavI{2Pe_v3s8fZ35$A?XJMirwzxqX09E zGR4}1sn?pQ8A0`)fAsAYcJ9;y-KaPV0zQz1`aydqEFzJaOW~b*zBmln`p6XO3rK0X zq1yvP77FF^KOj5L!YI$$l?_R!A%biueSyqCTWUo&W)?oOIqkDQeGf$8EI9USHPL&}w@Q#r7^*u~j)s_cCK~IGF*? zJ=Bhfc`IvIdjA@=M^PVmBS8!Y=l?If0&K?z#SCK;R2Q)ES6tL4BqZQzK1R=TUStQw zmcWB>#9x-ox2H+yNZTMR42(k{yEVF^8@B;vGh&XBy}kX@GX8S8gC-`pXCOHROXXPw z|C^eozmYc?PTaax`5t$AV{5CK?UT7py9Q4e5k*a~m#_vT^wKyjAp-{1U`TU8S{ljJ z-tzz^QGBVniSY|FKc8uh7XK9r_FEsP9xI^-_Z<`*jBFm-xq0Z!-pG3fwR$>Gz65cVnnz20B@|Dw2^-b@tUE5$BFXrHY?Yh z%b*Ehm*g#dVg!k>@E5d)U~@xu86FUoK8XLQ;pV7-wuOdz49I}HgEz#x!Ri)*gbN^j zj3?iHJQjL1%t`gXBv~^vvy#29c5-3wz_&iYyq`|`@z#kMjsCvA`+?F*%F3Y9Mv)nN z&;Eio`+g`60G=Zrvx35S$G5j&%Ayerd>iq#&xl8u%J33+>N@Mk*XGU<$ouP!~YP`*4VO6uL6rs7rQibt5J z0NX(Nf%4cZL_A1_iL3}cC$J;g**pi0P^>#`Er4k5G#m9V?4{zRB8-oL-3A}+ zj82DJf~l=A<;YW_g!7k3Ui>H*em&5%d;X&QSNVAkZN zrmny|D2Nc;($6gNhn2v>0UUw=h1CFAcIc%8$pTw&BBrq3Ij^H6n4l6O<~ahdfeP&( z2?aZ>ir9*TzfBNGpRqe|-H7pvUjnbd#>$>Oy}gYAHHoiH)~MNT49{00Ea;cq(&V;l#eXoQQEdk_BoI_rgEN5BVL?o?HPoLprLIwc8EIc6xLLh>>KT{Rb zg!X(~BMd8zdc;sX)CBN{iL50@%ih+%dk0N{YxV*dTLiTZ=@viIW52Z_{~1MhH(c76YP_Z?E!+S%5lvW- zQ~0up_#T*;PuzjW1q_-W_~&^Ku=$e?O z)Y_Gh-vEH=fXOmQ>A-Zk@{o|6gjNSM`3^~ZS3j_18AUB%_{9$m^zB1BpmM$Ki=A>e zYes=vXH3&$`R!V0vp?!~C<{koe?lh+wBwFDh`eVwdzw&w$z;fJm0F)MB^{nrgbw`0 zcP`;`5xfPDryL(3MPTqO%ERrc(Bd_v9#i2yQ6j{;z3 zuP|hUmHsvfDFZe+b~EBa_yej8ViyPo7BYu-w>;VHmN|_@2WgmI49Zav<_6>pywVP# zg2#?+5RD064-r`&05r|4hi>_X@Q4%6?S}O?{9tAp`o9efJY$Gy3<&_eX3hGlCjJp; zdjb^*zAZ=4Mz$aFmlifQ&OqnN%gaTkUxd${&tjbg_`j;T15?^C3r0Xh1e?tZDkL6V zrS&`rnSzx36h3}Gy=BXmK!ovE9(w&8FamWsy6!9LEPFS5j8E_Vdb+4{!(%B2p7xGI~8G_5JQyU-oQjt)fBReebk(o`hx8GYcERlaK(g zm2wKc9)<}DZbhOKyVLC~;#=!4-&gT|IRDTeDKq>iCLdvl2%K6A(Y=_oweO?fr( zYdCH+V9F(-ub3ftDYuV2lI%=g!hq7c9mZ;EZPdcoi+6%D38AS4c!KcaC{DrnppzRa ziN#<0gOnSfW3+fQq_gKy(eIfV}bX8loqOX%^#+q>VAYUR{MJg*RgHkGX#uOKX zki{TR(~o=^BlUo>+^$36)ZE6#W-~4CQ-=9J5K-lzXkE5~K$R1Qd&%C_P&UXbeLoh6 z^(u8UFooN%L?gLN~0PO2AXdBu#AP74}8B zJgNs_ ze$MlB-G9?IY5Q!-EH1qpAN9JErkdG4l1A?)n~dJTQXLYjsH?JEbJ^@#$rVUY+J@zH zSt|@==6Bt4=iJX;L%lAnBrQ*SXjk(2wRwDrn{GijFHP1|K5fg<>rP6?3h4GPHt{W(te|zCb!m?_=1=tqt~`dI%(*N(?q?Vi4y+|ad!XREs92t8z71ASX6AVSXtn_ zme=UBPa`WQHG1UA6ok#aIh$iFcQ4)+L4M@^uR*~-e0uRhwRWCnGE{E!XK9zH=T+KQ z^{&e`akQ?^a=GE?=s~a`x+l^yBPTP9)x^R?fco^IpcQ^ZW(72bYozC14l=19tL%`mGci(VocM zX|tMM`#msEa)sV51z;2 zb-9$>BE~ABY}Xw5zC35K^;8#3)O{Vun=c69$bI0}l@fZY&hM>AWptO5%J$ofgsMmu zRcRqzN2-Qj-0HR|SZvLMBc}d7OBnSs@xg$kY8JI?oo15tOMk!1?W;0hDm@LQZ6{sheNC@Z^gkqcax69*gQzKpdMEN`P zw?G&|yL(6axv%|`!~EZ~15m#=A;!5Q1V_#aF{-sI$%Q>3JDe%U@3XP3eXe(4pYb34 zQM-gbH@a?lsPlq#`QIzIxhzu$0VzaGL~7gX4J!b944pH?x!=S~35{-OXdpVo_tn}> zv6A5rIO7;Tq(c}w+)QqPKQayH0W`!r-8!aTob3|q>k?wZime#LQoU3VM;}Xlo*LZf@mNZ#RBeuA!T`j|fT(C6@_)pHkjJ6+KV7%@JR5)`DJu$U9}jl8%um_-b?+p(HHZh9%0$?dCdb9>GMm>pQ`D1_C0 zMbdk>sHkX2bCiS-^wKgOQS{Xy4Cux@L1Hi{yd)OQWG^IQn7cY#$z_;pPfXmHFtuPS z&cYZmS2wp!!rUX|d{gb*WE_w~|H6Y>?bD81kADrcNM(e~FW(i2KiCmjf4*={$!4P zd1!*jq^o-|RiJF2QH!%4GDK(|H?*$dRQj_%b4ZZBL=KG2gt(D-K4@ebsV^dgABa@# z*tZ4GkN_n*AKl8Z2l(J zSeA14`SGWxD0g4vG%4mkRY**2KaiUkRUO)$E7K}|(1K%8&$30l1@sVaHc3kk&ucxa z3#$7S9x3-ke3dKX$WiHJ7F^SQz_wMAX#2IPF-ZMjc``V7_Dwb(VPH|JNM4J>P60az zq_iJ@u(hWz{<1tNFmOY&+dAAnRGIffqzzz|oOQA9d+#?cbD?4rK_Z!^oRto%(XPm1 zv0RlX&{o_VbohWTdq>9_BPl!|NR6_yS!VLj%xPSz5RciljMC|x_ibWn4%4F-Hl?kj z1M$o&1aDr5y)ja8aB%QUiPDdXUkyK7EDvop+ICCIM&%1-Z+7g}>U!DLPRH^s58~gW z#+TJ%=VBn?gDn6Jrk-17HiQy=E}c%Htg`c^Y)l-rsXMTR?~-8Rk!4Je^_31A^j8cH z4LxXOJvtUOuita}RuKQG%tnuXAM%$=6st|Ok1;A7B{fBgfiK;ft&w7y-MU}k62@)+ z8q8~WQJVe|$_1F*zDq$N0*&05kcPuV^Pd>wByRaRbVhmfj?S00`m6TwCO5nF3^UbS zm4wVsJjacrOd9hwS)L3~wq$Vb=y>`4yJ~oHm}z>{j8NIX^W4F$RvGyN6Em)g zmW6@iI|vK);Dnsm*|`Xw(CDeo*>wr^+1Cai-gfE@ZW|sP?CGmF8|$NJzQS!TZ`J%g zssw2x5kNa_cBTrI>f}L7l!n3Ey+$ZLMYFiDr!~KjeT5S4`6{VrtvRzSHe^psTzEfKWcg~B7UcVznV+*Y-gxaBp&t=)i4+Z4ibed)UWQ-gOY714K+p|}1 z)|#X%Qg=_s>rwtC-ZlB{%~5ytofbu}kAFX;X#Z{|d(%;`w;Q=0Wm)F8H}OsFzE#UE zcfwqUUUIXueel_R_p)FKT9>-ww{HbE>JQ-jNB>O>-M+tzabEOmM*H7S5gJ!dgX7;- z*d(~Aqf56?^QD(cShJuhiEWLks(X9v#oszDT>GW@dW!B;PcEDNtIzr1ykCGx(=PR9h+Mib`Gzc$*1kKJC273w#C6@Em2OLx4U72R6WhkS)WXUtF zebhNQHhd*BT;Gt)bOzJo5o73;z+!O@*qyZI@6Vt5ZBtF;R!ux2*Q&E+Jy}DBi-T>2 z_&P_!3;UMKejJ!+@7`@_+2-kZ@%r+}{b%!c6%;8-!E^D)xAn_g&Efvoi~GwkJs}4LRGaz`&zzK_!b$NYFbCWx0a35 z{?Vftf8Uab(|gvW{k(2*lIx?xI{WPCuFxO4Z@Z?2wO4X24RMuzQ*`i|gLltJ=VR|{ z?{tK;ZfEebw^(xMi(lTO?DwItO2E#3HQvQY`n~#=TGD>~^W}DItG?wZO-)xhWeSL# zU*9MaH8418f=BSQ>09zKi`NsZ9U|>*?3l_>4khE5Cr_dWd{sM{y)?$q)fWHW_f`C7 z3Ta=zMlb8{`4A{&fAPWv0g+W_&z!&J_e*)}_PFrnE0_1}*!WE5p)7}ByPcuTemNQK zTXnqplA==s8HZH52DG?^Z(MrQr&~Xtsz>hH%E9$Hu3@JA_sp9_O>D2mDD{C2gFG`i_LpH_8U<$x zAH`Z$tlSW+VO5qF`NSijN1Z!1cfen*?NmUcXh}>_40Z)uas_Wr*bR2WU9GD0tjy+$ z4-Nr6b8?ya1O9YZ|Kfo*M^c_tv4QakQ}!_45nOToye0n0O#y!2KC(HKv3c1Xds6W*m8j6dv>16-* zqL#$GkiXL?ThEmweq_v`hC5P#7GS8j^tNeB&Y5oZVkWl|O({q7qhYhjo1Vu0%=`SA zVyTR+o$;*3{LHM34zh)8cJhZ+BEj5KTR14QsO6<`{4mKXoUcTz( zi@%+fq@MqGO&(`-j?ehAg&j{gbUe>4D_io4Ez2|cgG$*;PVtQ!@BPp&ytduRS5@~^ z=xT+$>TKD}?M>Ad_pfLj2&_QRmUve}ET+mreC+|R-$wTw*YB5{wfl0~V_2_hNk@y$ zfde(8o7Gn1^>wLo!2!JDgs^P!esBUo!G7SUv*@J<ApQAvZ~)R8IvZ~%$v(f@rTb5CX}=^Xv+{jgR4{$a=k3IFO1}Th{LvfeIO(GH z*;CZ8sMF`eUdz~8_4Ue1(xP%_Uo(|ow;zP>|s()6E8@4u2N^B`|t#^3DzQs=%D z^~BeOMUtEFVhg)V4`a6H4P@+H75AIbMnv4iJz|UN&kdg5<7=9|sPe~i7axbezfUBw z(~NayYdKvrLpY`5Qq&)8StD@LVZ_QU)8OMVSvgWQvxZGNMop%u=A?hej8aXZ+{rB) zlxr=kGYocmP~9cYeX6(?ozD2#UpLr$&7S$YMq@^xr9Nr!kw~q1L0;WTaeEhiS53dg zvF&+bK`$&Kaz$xfey45+rwx5DW_zhk;q_PX6V#&C9nkf@U_aKiB%%X-`of>=NKwT7C6PanfGSeIIpp&%ene znQgnBVQ6HfE-4wOT3Tsi=`*@p==(Ft`)hmNviw7-VTYGJm#`o|JX$;Kr#83ei~9CO z=8>vfYNRr)U_?Ba^LGsH8(0=7_PuofA;a>Yw6Myu>;JxrE9=Eh?~|MdcHez&tMI5e zimO?6dHBZ3^B6%h$$2EsWD(n_`i7 z$uFl|_*!IZgsGznKJ}I&>{}gUSLl|ojw(|gdz`8-7f=t%b7|R48sc^K-Km}0JGHv8 zFpPI>*&FJOCA_n@2;?Pz}nynffxMxKYj|-0p{^4R*`c~&g3;C=~ex2|ggB?O{t4_+Y zuRB+~kH4?Z{es`Pf?Af9k-k>dy(&AdnBKy-8i7g!!>=`^_NP#b*0pT>^;?PelA(T) z$aiOcHai`&?ffg3yt%XG-2ZtuX=+M5(}}sa?kX!K?n=p03C?jRCHX9h*E1`DOVX-W zDT-GA%1=(ebEi%3ArX7%;pYEpV073VpIQs%4O`r%)tqV&&;InhRP(bmN zGJn@e1^z>P+>h$Vdm?3i%bcx~s(GqzYn0yXZaEe5xXC4EPoaf8tB;3R%9JE?WzAfV z&{_v(_{QZ9*_Sx^P7J+qFaCtcN&Mjv@eGQlMN56d;QV~W5i>b6&!fSmferfA=><1i z>iteQH`P-;LY?!>mG8p}uB~Wgbbm3h)V=s&IR15pt>5N_v2~Qoc%6e)0G^$3yVyAe&I+9YfU+*5+@Le#)LT5?6 zweCOM*I3+H?8IlA`#=0{|Ie2n3Q(`dhfoKfmn{h3Q*XfvpUcw|y-BR~-@7baH`fi; z{BSQj9O;-_FH(tz^tyOD%5R*H)%R4U1^!gMjvAYuMBHgyv-5VAkD8&gMZt^zduJn% pbCwq<|2#hxkYEMjI!aScksx8EN#2+{{=`ICbIwl From 585692b3ae48717d8e1fa3717802acfcca3c4619 Mon Sep 17 00:00:00 2001 From: Javi Agenjo Date: Wed, 4 Mar 2020 18:38:39 +0100 Subject: [PATCH 04/63] Update README.md --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 782a0bbe2..d30e078ac 100755 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # litegraph.js -A library in Javascript to create graphs in the browser similar to Unreal Blueprints. Nodes can be programmed easily and it includes an editor to construct the graphs. +A library in Javascript to create graphs in the browser similar to Unreal Blueprints. Nodes can be programmed easily and it includes an editor to construct and tests the graphs. It can be integrated easily in any existing web applications and graphs can be run without the need of the editor. @@ -9,7 +9,7 @@ Try it in the [demo site](https://tamats.com/projects/litegraph/demo). ![Node Graph](imgs/node_graph_example.png "WebGLStudio") ## Features -- Renders on Canvas2D (zoom in, zoom out, panning, can be used inside a WebGLTexture) +- Renders on Canvas2D (zoom in/out and panning, easy to render complex interfaces, can be used inside a WebGLTexture) - Easy to use editor (searchbox, keyboard shortcuts, multiple selection, context menu, ...) - Optimized to support hundreds of nodes per graph (on editor but also on execution) - Customizable theme (colors, shapes, background) @@ -35,7 +35,7 @@ You can install it using npm npm install litegraph.js ``` -Or downloading the ```build/litegraph.js``` version from this repository. +Or downloading the ```build/litegraph.js``` and ```css/litegraph.css``` version from this repository. ## First project ## @@ -179,6 +179,7 @@ You can write any feedback to javi.agenjo@gmail.com - rappestad - InventivetalentDev - NateScarlet +- coderofsalvation From 540d3474d0140890124f6f584c49c500fba7d115 Mon Sep 17 00:00:00 2001 From: Javi Agenjo Date: Wed, 18 Mar 2020 14:14:02 +0100 Subject: [PATCH 05/63] Update README.md --- guides/README.md | 60 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/guides/README.md b/guides/README.md index 625ca2200..7f607bbfd 100644 --- a/guides/README.md +++ b/guides/README.md @@ -244,3 +244,63 @@ If you want to start the graph then: ```js graph.start(); ``` + +## Events + +When we run a step in a graph (using ```graph.runStep()```) every node onExecute method will be called. +But sometimes you want that actions are only performed when some trigger is activated, for this situations you can use Events. + +Events allow to trigger executions in nodes only when an event is dispatched from one node. + +To define slots for nodes you must use the type LiteGraph.ACTION for inputs, and LIteGraph.EVENT for outputs: + +```js +function MyNode() +{ + this.addInput("play", LiteGraph.ACTION ); + this.addInput("onFinish", LiteGraph.EVENT ); +} +``` + +Now to execute some code when an event is received from an input, you must define the method onAction: + +```js +MyNode.prototype.onAction = function(action, data) +{ + if(action == "play") + { + //do your action... + } + +} +``` + +And the last thing is to trigger events when something in your node happens. You could trigger them from inside the onExecute or from any other interaction: + +```js +MyNode.prototype.onAction = function(action, data) +{ + if( this.button_was_clicked ) + this.triggerSlot(0); //triggers event in slot 0 +} +``` + +There are some nodes already available to handle events, like delaying, counting, etc. + + + + + + + +``` + + + + + + + + + + From d9cdf4ec139139390dfea630bc4caad4955f58f5 Mon Sep 17 00:00:00 2001 From: smarthug Date: Thu, 19 Mar 2020 15:41:19 +0900 Subject: [PATCH 06/63] resolve "TypeError: Cannot redefine property: shape" issue When same node registered twice. It gives error. ex: LiteGraph.registerNodeType("basic/test", MyAddNode); LiteGraph.registerNodeType("basic/test", MyAddNode); To redefine an object property with "Object.defineProperty" method, configurable property should be set on true ( default is false) ex : Object.defineProperty( '...' , '...' , {configurable: true}) --- src/litegraph.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/litegraph.js b/src/litegraph.js index e61fc95fb..f773f63cc 100755 --- a/src/litegraph.js +++ b/src/litegraph.js @@ -159,7 +159,8 @@ get: function(v) { return this._shape; }, - enumerable: true + enumerable: true, + configurable: true }); } From 7f4e5b8d79ed9b60b2286f47a1d689292d284569 Mon Sep 17 00:00:00 2001 From: tamat Date: Thu, 19 Mar 2020 08:57:36 +0100 Subject: [PATCH 07/63] fix --- build/litegraph.js | 471 +- build/litegraph.min.js | 1273 +-- css/litegraph-editor.css | 138 +- css/litegraph.css | 1 + demo/index.html | 1 + demo/js/code.js | 84 +- demo/js/demos.js | 1 - demo/js/libs/gl-matrix-min.js | 28 + demo/js/libs/litegl.js | 13432 ++++++++++++++++++++++++++++++++ src/litegraph-editor.js | 333 +- src/litegraph.js | 259 +- src/nodes/.gltextures.js.swp | Bin 40960 -> 0 bytes src/nodes/audio.js | 2 +- src/nodes/base.js | 58 +- src/nodes/events.js | 20 +- src/nodes/geometry.js | 76 +- src/nodes/gltextures.js | 33 +- src/nodes/midi.js | 23 +- 18 files changed, 15244 insertions(+), 989 deletions(-) create mode 100644 demo/js/libs/gl-matrix-min.js create mode 100644 demo/js/libs/litegl.js delete mode 100644 src/nodes/.gltextures.js.swp diff --git a/build/litegraph.js b/build/litegraph.js index 5c6655b76..1e2be9d25 100644 --- a/build/litegraph.js +++ b/build/litegraph.js @@ -134,8 +134,12 @@ } } - if( !Object.hasOwnProperty( base_class.prototype, "shape") ) + var prev = this.registered_node_types[type]; + if(prev) + console.log("replacing node type: " + type); + else { + if( !Object.hasOwnProperty( base_class.prototype, "shape") ) Object.defineProperty(base_class.prototype, "shape", { set: function(v) { switch (v) { @@ -163,11 +167,25 @@ }, enumerable: true }); - } - var prev = this.registered_node_types[type]; - if(prev) - console.log("replacing node type: " + type); + //warnings + if (base_class.prototype.onPropertyChange) { + console.warn( + "LiteGraph node class " + + type + + " has onPropertyChange method, it must be called onPropertyChanged with d at the end" + ); + } + + //used to know which nodes create when dragging files to the canvas + if (base_class.supported_extensions) { + for (var i in base_class.supported_extensions) { + var ext = base_class.supported_extensions[i]; + if(ext && ext.constructor === String) + this.node_types_by_file_extension[ ext.toLowerCase() ] = base_class; + } + } + } this.registered_node_types[type] = base_class; if (base_class.constructor.name) { @@ -179,26 +197,22 @@ if (prev && LiteGraph.onNodeTypeReplaced) { LiteGraph.onNodeTypeReplaced(type, base_class, prev); } - - //warnings - if (base_class.prototype.onPropertyChange) { - console.warn( - "LiteGraph node class " + - type + - " has onPropertyChange method, it must be called onPropertyChanged with d at the end" - ); - } - - //used to know which nodes create when dragging files to the canvas - if (base_class.supported_extensions) { - for (var i in base_class.supported_extensions) { - var ext = base_class.supported_extensions[i]; - if(ext && ext.constructor === String) - this.node_types_by_file_extension[ ext.toLowerCase() ] = base_class; - } - } }, + /** + * removes a node type from the system + * @method unregisterNodeType + * @param {String|Object} type name of the node or the node constructor itself + */ + unregisterNodeType: function(type) { + var base_class = type.constructor === String ? this.registered_node_types[type] : type; + if(!base_class) + throw("node type not found: " + type ); + delete this.registered_node_types[base_class.type]; + if(base_class.constructor.name) + delete this.Nodes[base_class.constructor.name]; + }, + /** * Create a new nodetype by passing a function, it wraps it with a proper class and generates inputs according to the parameters of the function. * Useful to wrap simple methods that do not require properties, and that only process some input to generate an output. @@ -683,24 +697,29 @@ interval = interval || 0; var that = this; - if ( - interval == 0 && - typeof window != "undefined" && - window.requestAnimationFrame - ) { + //execute once per frame + if ( interval == 0 && typeof window != "undefined" && window.requestAnimationFrame ) { function on_frame() { if (that.execution_timer_id != -1) { return; } window.requestAnimationFrame(on_frame); + if(that.onBeforeStep) + that.onBeforeStep(); that.runStep(1, !this.catch_errors); + if(that.onAfterStep) + that.onAfterStep(); } this.execution_timer_id = -1; on_frame(); - } else { + } else { //execute every 'interval' ms this.execution_timer_id = setInterval(function() { //execute + if(that.onBeforeStep) + that.onBeforeStep(); that.runStep(1, !this.catch_errors); + if(that.onAfterStep) + that.onAfterStep(); }, interval); } }; @@ -1895,7 +1914,7 @@ //copy all stored fields for (var i in data) { - if(i == "nodes" || i == "groups") + if(i == "nodes" || i == "groups" ) //links must be accepted continue; this[i] = data[i]; } @@ -2205,6 +2224,8 @@ for (var i = 0; i < this.widgets.length; ++i) { var w = this.widgets[i]; + if(!w) + continue; if(w.options && w.options.property && this.properties[ w.options.property ]) w.value = JSON.parse( JSON.stringify( this.properties[ w.options.property ] ) ); } @@ -2267,7 +2288,10 @@ if (this.widgets && this.serialize_widgets) { o.widgets_values = []; for (var i = 0; i < this.widgets.length; ++i) { - o.widgets_values[i] = this.widgets[i].value; + if(this.widgets[i]) + o.widgets_values[i] = this.widgets[i].value; + else + o.widgets_values[i] = null; } } @@ -2368,6 +2392,18 @@ if( this.onPropertyChanged(name, value, prev_value) === false ) //abort change this.properties[name] = prev_value; } + if(this.widgets) //widgets could be linked to properties + for(var i = 0; i < this.widgets.length; ++i) + { + var w = this.widgets[i]; + if(!w) + continue; + if(w.options.property == name) + { + w.value = value; + break; + } + } }; // Execution ************************* @@ -3132,13 +3168,51 @@ }; /** - * Allows to pass + * returns all the info available about a property of this node. + * + * @method getPropertyInfo + * @param {String} property name of the property + * @return {Object} the object with all the available info + */ + LGraphNode.prototype.getPropertyInfo = function( property ) + { + var info = null; + + //there are several ways to define info about a property + //legacy mode + if (this.properties_info) { + for (var i = 0; i < this.properties_info.length; ++i) { + if (this.properties_info[i].name == property) { + info = this.properties_info[i]; + break; + } + } + } + //litescene mode using the constructor + if(this.constructor["@" + property]) + info = this.constructor["@" + property]; + + //litescene mode using the constructor + if (this.onGetPropertyInfo) { + info = this.onGetPropertyInfo(property); + } + + if (!info) + info = {}; + if(!info.type) + info.type = typeof this.properties[property]; + + return info; + } + + /** + * Defines a widget inside the node, it will be rendered on top of the node, you can control lots of properties * * @method addWidget * @param {String} type the widget type (could be "number","string","combo" * @param {String} name the text to show on the widget * @param {String} value the default value - * @param {Function} callback function to call when it changes (optionally, it can be the name of the property to modify) + * @param {Function|String} callback function to call when it changes (optionally, it can be the name of the property to modify) * @param {Object} options the object that contains special properties of this widget * @return {Object} the created widget object */ @@ -5454,16 +5528,8 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.active_canvas = this; //restore the mousemove event back to the canvas - document.removeEventListener( - "mousemove", - this._mousemove_callback, - true - ); - this.canvas.addEventListener( - "mousemove", - this._mousemove_callback, - true - ); + document.removeEventListener("mousemove",this._mousemove_callback,true); + this.canvas.addEventListener("mousemove",this._mousemove_callback,true); document.removeEventListener("mouseup", this._mouseup_callback, true); this.adjustMouseEvent(e); @@ -5472,6 +5538,12 @@ LGraphNode.prototype.executeAction = function(action) this.last_mouse_dragging = false; if (e.which == 1) { + + if( this.node_widget ) + { + this.processNodeWidgets( this.node_widget[0], this.canvas_mouse, e ); + } + //left button this.node_widget = null; @@ -8282,7 +8354,7 @@ LGraphNode.prototype.executeAction = function(action) for (var i = 0; i < node.widgets.length; ++i) { var w = node.widgets[i]; - if(w.disabled) + if(!w || w.disabled) continue; if ( w == active_widget || (x > 6 && x < width - 12 && y > w.last_y && y < w.last_y + LiteGraph.NODE_WIDGET_HEIGHT) ) { //inside widget @@ -8316,18 +8388,11 @@ LGraphNode.prototype.executeAction = function(action) case "combo": var old_value = w.value; if (event.type == "mousemove" && w.type == "number") { - w.value += - event.deltaX * 0.1 * (w.options.step || 1); - if ( - w.options.min != null && - w.value < w.options.min - ) { + w.value += event.deltaX * 0.1 * (w.options.step || 1); + if ( w.options.min != null && w.value < w.options.min ) { w.value = w.options.min; } - if ( - w.options.max != null && - w.value > w.options.max - ) { + if ( w.options.max != null && w.value > w.options.max ) { w.value = w.options.max; } } else if (event.type == "mousedown") { @@ -8339,38 +8404,33 @@ LGraphNode.prototype.executeAction = function(action) var delta = x < 40 ? -1 : x > width - 40 ? 1 : 0; if (w.type == "number") { w.value += delta * 0.1 * (w.options.step || 1); - if ( - w.options.min != null && - w.value < w.options.min - ) { + if ( w.options.min != null && w.value < w.options.min ) { w.value = w.options.min; } - if ( - w.options.max != null && - w.value > w.options.max - ) { + if ( w.options.max != null && w.value > w.options.max ) { w.value = w.options.max; } - } else if (delta) { - var index = values.indexOf(w.value) + delta; + } else if (delta) { //used for combos + var values_list = values.constructor === Array ? values : Object.keys(values); + var index = values_list.indexOf(w.value) + delta; if (index >= values.length) { index = 0; } if (index < 0) { - index = values.length - 1; + index = values_list.length - 1; } - w.value = values[index]; - } else { - var menu = new LiteGraph.ContextMenu( - values, - { + if( values.constructor === Array ) + w.value = values[index]; + else + w.value = values[ values_list[index] ]; + } else { //combo + var menu = new LiteGraph.ContextMenu(values,{ scale: Math.max(1, this.ds.scale), event: event, className: "dark", callback: inner_clicked.bind(w) }, - ref_window - ); + ref_window); function inner_clicked(v, option, event) { this.value = v; inner_value_change(this, v); @@ -8378,7 +8438,18 @@ LGraphNode.prototype.executeAction = function(action) return false; } } - } //mousedown + } //end mousedown + else if(event.type == "mouseup" && w.type == "number") + { + var delta = x < 40 ? -1 : x > width - 40 ? 1 : 0; + if (event.click_time < 200 && delta == 0) { + this.prompt("Value",w.value,function(v) { + this.value = Number(v); + inner_value_change(this, this.value); + }.bind(w), + event); + } + } if( old_value != w.value ) setTimeout( @@ -8400,15 +8471,11 @@ LGraphNode.prototype.executeAction = function(action) case "string": case "text": if (event.type == "mousedown") { - this.prompt( - "Value", - w.value, - function(v) { + this.prompt("Value",w.value,function(v) { this.value = v; inner_value_change(this, v); }.bind(w), - event - ); + event); } break; default: @@ -9423,11 +9490,7 @@ LGraphNode.prototype.executeAction = function(action) return dialog; }; - LGraphCanvas.prototype.showEditPropertyValue = function( - node, - property, - options - ) { + LGraphCanvas.prototype.showEditPropertyValue = function( node, property, options ) { if (!node || node.properties[property] === undefined) { return; } @@ -9435,28 +9498,8 @@ LGraphNode.prototype.executeAction = function(action) options = options || {}; var that = this; - var type = "string"; - - if (node.properties[property] !== null) { - type = typeof node.properties[property]; - } - - var info = null; - if (node.getPropertyInfo) { - info = node.getPropertyInfo(property); - } - if (node.properties_info) { - for (var i = 0; i < node.properties_info.length; ++i) { - if (node.properties_info[i].name == property) { - info = node.properties_info[i]; - break; - } - } - } - - if (info !== undefined && info !== null && info.type) { - type = info.type; - } + var info = node.getPropertyInfo(property); + var type = info.type; var input_html = ""; @@ -9550,9 +9593,13 @@ LGraphNode.prototype.executeAction = function(action) if (node.onPropertyChanged) { node.onPropertyChanged(property, value); } + if(options.onclose) + options.onclose(); dialog.close(); node.setDirtyCanvas(true, true); } + + return dialog; }; LGraphCanvas.prototype.createDialog = function(html, options) { @@ -11182,12 +11229,17 @@ if (typeof exports != "undefined") { { var type = this.properties.type; this.type_widget.value = type; + if(this.outputs[0].type != type) + { + this.outputs[0].type = type; + this.disconnectOutput(0); + } if(type == "number") { this.value_widget.type = "number"; this.value_widget.value = 0; } - else if(type == "bool") + else if(type == "boolean") { this.value_widget.type = "toggle"; this.value_widget.value = true; @@ -11253,8 +11305,10 @@ if (typeof exports != "undefined") { var data = this.graph.inputs[name]; if (!data) { this.setOutputData(0, this.properties.value ); + return; } - this.setOutputData(0, data.value === undefined ? this.properties.value : data.value); + + this.setOutputData(0, data.value !== undefined ? data.value : this.properties.value ); }; GraphInput.prototype.onRemoved = function() { @@ -11374,6 +11428,14 @@ if (typeof exports != "undefined") { function ConstantNumber() { this.addOutput("value", "number"); this.addProperty("value", 1.0); + this.widget = this.addWidget( + "number", + "value", + 1, + "value" + ); + this.widgets_up = true; + this.size = [180, 30]; } ConstantNumber.title = "Const Number"; @@ -11390,10 +11452,6 @@ if (typeof exports != "undefined") { return this.title; }; - ConstantNumber.prototype.setValue = function(v) { - this.properties.value = v; - }; - ConstantNumber.prototype.onDrawBackground = function(ctx) { //show the current value this.outputs[0].label = this.properties["value"].toFixed(3); @@ -11401,6 +11459,29 @@ if (typeof exports != "undefined") { LiteGraph.registerNodeType("basic/const", ConstantNumber); + function ConstantBoolean() { + this.addOutput("", "boolean"); + this.addProperty("value", true); + this.widget = this.addWidget( + "toggle", + "value", + true, + "value" + ); + this.widgets_up = true; + this.size = [140, 30]; + } + + ConstantBoolean.title = "Const Boolean"; + ConstantBoolean.desc = "Constant boolean"; + ConstantBoolean.prototype.getTitle = ConstantNumber.prototype.getTitle; + + ConstantBoolean.prototype.onExecute = function() { + this.setOutputData(0, this.properties["value"]); + }; + + LiteGraph.registerNodeType("basic/boolean", ConstantBoolean); + function ConstantString() { this.addOutput("", "string"); this.addProperty("value", ""); @@ -11408,23 +11489,15 @@ if (typeof exports != "undefined") { "text", "value", "", - this.setValue.bind(this) + "value" //link to property value ); this.widgets_up = true; - this.size = [100, 30]; + this.size = [180, 30]; } ConstantString.title = "Const String"; ConstantString.desc = "Constant string"; - ConstantString.prototype.setValue = function(v) { - this.properties.value = v; - }; - - ConstantString.prototype.onPropertyChanged = function(name, value) { - this.widget.value = value; - }; - ConstantString.prototype.getTitle = ConstantNumber.prototype.getTitle; ConstantString.prototype.onExecute = function() { @@ -11896,22 +11969,30 @@ if (typeof exports != "undefined") { //convert to Event if the value is true function TriggerEvent() { this.size = [60, 30]; - this.addInput("in", ""); + this.addInput("if", ""); this.addOutput("true", LiteGraph.EVENT); this.addOutput("change", LiteGraph.EVENT); - this.was_true = false; + this.addOutput("false", LiteGraph.EVENT); + this.properties = { only_on_change: true }; + this.prev = 0; } TriggerEvent.title = "TriggerEvent"; - TriggerEvent.desc = "Triggers event if value is true"; + TriggerEvent.desc = "Triggers event if input evaluates to true"; TriggerEvent.prototype.onExecute = function(action, param) { var v = this.getInputData(0); - if(v) + var changed = (v != this.prev); + if(this.prev === 0) + changed = false; + var must_resend = (changed && this.properties.only_on_change) || (!changed && !this.properties.only_on_change); + if(v && must_resend ) this.triggerSlot(0, param); - if(v && !this.was_true) + if(!v && must_resend) + this.triggerSlot(2, param); + if(changed) this.triggerSlot(1, param); - this.was_true = v; + this.prev = v; }; LiteGraph.registerNodeType("events/trigger", TriggerEvent); @@ -17053,15 +17134,14 @@ if (typeof exports != "undefined") { precision: LGraphTexture.DEFAULT }; - this.properties.code = - "//time: time in seconds\n//texSize: vec2 with res\nuniform float u_value;\nuniform vec4 u_color;\n\nvoid main() {\n vec2 uv = v_coord;\n vec3 color = vec3(0.0);\n //your code here\n color.xy=uv;\n\ngl_FragColor = vec4(color, 1.0);\n}\n"; + this.properties.code = LGraphTextureShader.pixel_shader; this._uniforms = { u_value: 1, u_color: vec4.create(), in_texture: 0, texSize: vec2.create(), time: 0 }; } LGraphTextureShader.title = "Shader"; LGraphTextureShader.desc = "Texture shader"; LGraphTextureShader.widgets_info = { - code: { type: "code" }, + code: { type: "code", lang: "glsl" }, precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } }; @@ -17164,10 +17244,7 @@ if (typeof exports != "undefined") { } this._shader_code = this.properties.code; - this._shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureShader.pixel_shader + this.properties.code - ); + this._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, this.properties.code ); if (!this._shader) { this.boxcolor = "red"; return null; @@ -17240,10 +17317,20 @@ if (typeof exports != "undefined") { }; LGraphTextureShader.pixel_shader = - "precision highp float;\n\ - \n\ - varying vec2 v_coord;\n\ - uniform float time;\n\ +"precision highp float;\n\ +\n\ +varying vec2 v_coord;\n\ +uniform float time; //time in seconds\n\ +uniform vec2 texSize; //tex resolution\n\ +uniform float u_value;\n\ +uniform vec4 u_color;\n\n\ +void main() {\n\ + vec2 uv = v_coord;\n\ + vec3 color = vec3(0.0);\n\ + //your code here\n\ + color.xy=uv;\n\n\ + gl_FragColor = vec4(color, 1.0);\n\ +}\n\ "; LiteGraph.registerNodeType("texture/shader", LGraphTextureShader); @@ -21204,7 +21291,7 @@ void main(void){\n\ this.addInput("v"); this.addOutput("out", "Texture"); this.properties = { - code: "", + code: LGraphTextureCanvas2D.default_code, width: 512, height: 512, clear: true, @@ -21213,12 +21300,15 @@ void main(void){\n\ }; this._func = null; this._temp_texture = null; + this.compileCode(); } LGraphTextureCanvas2D.title = "Canvas2D"; LGraphTextureCanvas2D.desc = "Executes Canvas2D code inside a texture or the viewport."; LGraphTextureCanvas2D.help = "Set width and height to 0 to match viewport size."; + LGraphTextureCanvas2D.default_code = "//vars: canvas,ctx,time\nctx.fillStyle='red';\nctx.fillRect(0,0,50,50);\n"; + LGraphTextureCanvas2D.widgets_info = { precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }, code: { type: "code" }, @@ -21530,8 +21620,9 @@ void main(void){\n\ LGraphPoints3D.OBJECT = 20; LGraphPoints3D.OBJECT_UNIFORMLY = 21; + LGraphPoints3D.OBJECT_INSIDE = 22; - LGraphPoints3D.MODE_VALUES = { "rectangle":LGraphPoints3D.RECTANGLE, "circle":LGraphPoints3D.CIRCLE, "cube":LGraphPoints3D.CUBE, "sphere":LGraphPoints3D.SPHERE, "hemisphere":LGraphPoints3D.HEMISPHERE, "inside_sphere":LGraphPoints3D.INSIDE_SPHERE, "object":LGraphPoints3D.OBJECT, "object_uniformly":LGraphPoints3D.OBJECT_UNIFORMLY }; + LGraphPoints3D.MODE_VALUES = { "rectangle":LGraphPoints3D.RECTANGLE, "circle":LGraphPoints3D.CIRCLE, "cube":LGraphPoints3D.CUBE, "sphere":LGraphPoints3D.SPHERE, "hemisphere":LGraphPoints3D.HEMISPHERE, "inside_sphere":LGraphPoints3D.INSIDE_SPHERE, "object":LGraphPoints3D.OBJECT, "object_uniformly":LGraphPoints3D.OBJECT_UNIFORMLY, "object_inside":LGraphPoints3D.OBJECT_INSIDE }; LGraphPoints3D.widgets_info = { mode: { widget: "combo", values: LGraphPoints3D.MODE_VALUES } @@ -21629,7 +21720,7 @@ void main(void){\n\ if(normals) { for(var i = 0; i < normals.length; i+=3) - normals.set(i, UP); + normals.set(UP, i); } } else if( mode == LGraphPoints3D.SPHERE) @@ -21660,7 +21751,7 @@ void main(void){\n\ if(normals) { for(var i = 0; i < normals.length; i+=3) - normals.set(i, UP); + normals.set(UP, i); } } } @@ -21677,7 +21768,7 @@ void main(void){\n\ if(normals) { for(var i = 0; i < normals.length; i+=3) - normals.set(i, UP); + normals.set(UP, i); } } else if( mode == LGraphPoints3D.CUBE) @@ -21691,7 +21782,7 @@ void main(void){\n\ if(normals) { for(var i = 0; i < normals.length; i+=3) - normals.set(i, UP); + normals.set(UP, i); } } else if( mode == LGraphPoints3D.SPHERE) @@ -21726,6 +21817,12 @@ void main(void){\n\ { LGraphPoints3D.generateFromObject( points, normals, size, obj, true ); } + else if( mode == LGraphPoints3D.OBJECT_INSIDE) + { + LGraphPoints3D.generateFromInsideObject( points, size, obj ); + //if(normals) + // LGraphPoints3D.generateSphericalNormals( points, normals ); + } else console.warn("wrong mode in LGraphPoints3D"); } @@ -21931,6 +22028,36 @@ void main(void){\n\ } } + LGraphPoints3D.generateFromInsideObject = function( points, size, mesh ) + { + if(!mesh || mesh.constructor !== GL.Mesh) + return; + + var aabb = mesh.getBoundingBox(); + if(!mesh.octree) + mesh.octree = new GL.Octree( mesh ); + var octree = mesh.octree; + var origin = vec3.create(); + var direction = vec3.fromValues(1,0,0); + var temp = vec3.create(); + var i = 0; + var tries = 0; + while(i < size && tries < points.length * 10) //limit to avoid problems + { + tries += 1 + var r = vec3.random(temp); //random point inside the aabb + r[0] = (r[0] * 2 - 1) * aabb[3] + aabb[0]; + r[1] = (r[1] * 2 - 1) * aabb[4] + aabb[1]; + r[2] = (r[2] * 2 - 1) * aabb[5] + aabb[2]; + origin.set(r); + var hit = octree.testRay( origin, direction, 0, 10000, true, GL.Octree.ALL ); + if(!hit || hit.length % 2 == 0) //not inside + continue; + points.set( r, i ); + i+=3; + } + } + LiteGraph.registerNodeType( "geometry/points3D", LGraphPoints3D ); @@ -21939,11 +22066,13 @@ void main(void){\n\ this.addInput("points", "geometry"); this.addOutput("instances", "[mat4]"); this.properties = { - mode: 1 + mode: 1, + autoupdate: true }; this.must_update = true; this.matrices = []; + this.first_time = true; } LGraphPointsToInstances.NORMAL = 0; @@ -21971,8 +22100,13 @@ void main(void){\n\ if( !this.isOutputConnected(0) ) return; - if( geo._version != this._version || geo._id != this._geometry_id ) + var has_changed = (geo._version != this._version || geo._id != this._geometry_id); + + if( has_changed && this.properties.autoupdate || this.first_time ) + { + this.first_time = false; this.updateInstances( geo ); + } this.setOutputData( 0, this.matrices ); } @@ -22076,7 +22210,8 @@ void main(void){\n\ this.geometry = { type: "triangles", vertices: null, - _id: generateGeometryId() + _id: generateGeometryId(), + _version: 0 }; this._last_geometry_id = -1; @@ -22195,7 +22330,7 @@ void main(void){\n\ this.addInput("sides", "number"); this.addInput("radius", "number"); this.addOutput("out", "geometry"); - this.properties = { sides: 6, radius: 1 } + this.properties = { sides: 6, radius: 1, uvs: false } this.geometry = { type: "line_loop", @@ -22233,6 +22368,13 @@ void main(void){\n\ if( !vertices || vertices.length != num ) vertices = this.geometry.vertices = new Float32Array( 3*sides ); var delta = (Math.PI * 2) / sides; + var gen_uvs = this.properties.uvs; + if(gen_uvs) + { + uvs = this.geometry.coords = new Float32Array( 3*sides ); + } + + for(var i = 0; i < sides; ++i) { var angle = delta * -i; @@ -22242,6 +22384,12 @@ void main(void){\n\ vertices[i*3] = x; vertices[i*3+1] = y; vertices[i*3+2] = z; + + if(gen_uvs) + { + + + } } this.geometry._id = ++this.geometry_id; this.geometry._version = ++this.version; @@ -24747,6 +24895,8 @@ function LGraphGeometryDisplace() { new MIDIInterface(function(midi) { that._midi = midi; }); + + this.addWidget("combo","Device",this.properties.port,{ property: "port", values: this.getMIDIOutputs.bind(this) }); } LGMIDIOut.MIDIInterface = MIDIInterface; @@ -24755,22 +24905,28 @@ function LGraphGeometryDisplace() { LGMIDIOut.desc = "Sends MIDI to output channel"; LGMIDIOut.color = MIDI_COLOR; - LGMIDIOut.prototype.getPropertyInfo = function(name) { + LGMIDIOut.prototype.onGetPropertyInfo = function(name) { if (!this._midi) { return; } if (name == "port") { - var values = {}; - for (var i = 0; i < this._midi.output_ports.size; ++i) { - var output = this._midi.output_ports.get(i); - values[i] = - i + ".- " + output.name + " version:" + output.version; - } + var values = this.getMIDIOutputs(); return { type: "enum", values: values }; } }; + LGMIDIOut.prototype.getMIDIOutputs = function() + { + var values = {}; + for (var i = 0; i < this._midi.output_ports.size; ++i) { + var output = this._midi.output_ports.get(i); + if(output) + values[i] = i + ".- " + output.name + " version:" + output.version; + } + return values; + } + LGMIDIOut.prototype.onAction = function(event, midi_event) { //console.log(midi_event); if (!this._midi) { @@ -24792,6 +24948,7 @@ function LGraphGeometryDisplace() { LiteGraph.registerNodeType("midi/output", LGMIDIOut); + function LGMIDIShow() { this.addInput("on_midi", LiteGraph.EVENT); this._str = ""; @@ -26943,7 +27100,7 @@ LiteGraph.registerNodeType("audio/waveShaper", LGAudioWaveShaper); } }; - LGAudioScript["@code"] = { widget: "code" }; + LGAudioScript["@code"] = { widget: "code", type: "code" }; LGAudioScript.prototype.onStart = function() { this.audionode.onaudioprocess = this._callback; diff --git a/build/litegraph.min.js b/build/litegraph.min.js index 8a7fe4940..515983274 100755 --- a/build/litegraph.min.js +++ b/build/litegraph.min.js @@ -1,702 +1,705 @@ -(function(y){function c(a){g.debug&&console.log("Graph created");this.list_of_graphcanvas=null;this.clear();a&&this.configure(a)}function m(a,b,d,k,f,g){this.id=a;this.type=b;this.origin_id=d;this.origin_slot=k;this.target_id=f;this.target_slot=g;this._data=null;this._pos=new Float32Array(2)}function n(a){this._ctor(a)}function l(a){this._ctor(a)}function x(a,b){this.offset=new Float32Array([0,0]);this.scale=1;this.max_scale=10;this.min_scale=0.1;this.onredraw=null;this.enabled=!0;this.last_mouse= -[0,0];this.element=null;this.visible_area=new Float32Array(4);a&&(this.element=a,b||this.bindEvents(a))}function e(a,b,d){d=d||{};this.background_image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; -a&&a.constructor===String&&(a=document.querySelector(a));this.ds=new x;this.zoom_modify_alpha=!0;this.title_text_font=""+g.NODE_TEXT_SIZE+"px Arial";this.inner_text_font="normal "+g.NODE_SUBTEXT_SIZE+"px Arial";this.node_title_color=g.NODE_TITLE_COLOR;this.default_link_color=g.LINK_COLOR;this.default_connection_color={input_off:"#778",input_on:"#7F7",output_off:"#778",output_on:"#7F7"};this.highquality_render=!0;this.use_gradients=!1;this.editor_alpha=1;this.pause_rendering=!1;this.clear_background= +(function(y){function c(a){e.debug&&console.log("Graph created");this.list_of_graphcanvas=null;this.clear();a&&this.configure(a)}function m(a,b,d,h,f,e){this.id=a;this.type=b;this.origin_id=d;this.origin_slot=h;this.target_id=f;this.target_slot=e;this._data=null;this._pos=new Float32Array(2)}function n(a){this._ctor(a)}function k(a){this._ctor(a)}function x(a,b){this.offset=new Float32Array([0,0]);this.scale=1;this.max_scale=10;this.min_scale=0.1;this.onredraw=null;this.enabled=!0;this.last_mouse= +[0,0];this.element=null;this.visible_area=new Float32Array(4);a&&(this.element=a,b||this.bindEvents(a))}function g(a,b,d){d=d||{};this.background_image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; +a&&a.constructor===String&&(a=document.querySelector(a));this.ds=new x;this.zoom_modify_alpha=!0;this.title_text_font=""+e.NODE_TEXT_SIZE+"px Arial";this.inner_text_font="normal "+e.NODE_SUBTEXT_SIZE+"px Arial";this.node_title_color=e.NODE_TITLE_COLOR;this.default_link_color=e.LINK_COLOR;this.default_connection_color={input_off:"#778",input_on:"#7F7",output_off:"#778",output_on:"#7F7"};this.highquality_render=!0;this.use_gradients=!1;this.editor_alpha=1;this.pause_rendering=!1;this.clear_background= !0;this.read_only=!1;this.render_only_selected=!0;this.live_mode=!1;this.allow_searchbox=this.allow_interaction=this.allow_dragnodes=this.allow_dragcanvas=this.show_info=!0;this.drag_mode=this.allow_reconnect_links=!1;this.filter=this.dragging_rectangle=null;this.always_render_background=!1;this.render_canvas_border=this.render_shadows=!0;this.render_connections_shadows=!1;this.render_connections_border=!0;this.render_connection_arrows=this.render_curved_connections=!1;this.render_collapsed_slots= -!0;this.render_execution_order=!1;this.render_link_tooltip=this.render_title_colored=!0;this.links_render_mode=g.SPLINE_LINK;this.canvas_mouse=[0,0];this.onSelectionChange=this.onNodeMoved=this.onDrawLinkTooltip=this.onDrawOverlay=this.onDrawForeground=this.onDrawBackground=this.onMouse=this.onSearchBoxSelection=this.onSearchBox=null;this.connections_width=3;this.round_radius=8;this.over_link_center=this.node_widget=this.current_node=null;this.last_mouse_position=[0,0];this.visible_area=this.ds.visible_area; -this.visible_links=[];b&&b.attachCanvas(this);this.setCanvas(a);this.clear();d.skip_render||this.startRendering();this.autoresize=d.autoresize}function C(a,b){return Math.sqrt((b[0]-a[0])*(b[0]-a[0])+(b[1]-a[1])*(b[1]-a[1]))}function z(a,b,d,k,f,g){return da&&kb?!0:!1}function v(a,b){var d=a[0]+a[2],k=a[1]+a[3],f=b[1]+b[3];return a[0]>b[0]+b[2]||a[1]>f||da&&hb?!0:!1}function v(a,b){var d=a[0]+a[2],h=a[1]+a[3],f=b[1]+b[3];return a[0]>b[0]+b[2]||a[1]>f||dh.width-c.width-10&&(g=h.width-c.width-10);w>h.height-c.height-10&&(w=h.height-c.height-10)}f.style.left=g+"px";f.style.top=w+"px";b.scale&&(f.style.transform="scale("+b.scale+")")}function B(a){this.points=a;this.nearest=this.selected=-1;this.size=null;this.must_update=!0;this.margin=5}var g=y.LiteGraph={VERSION:0.4,CANVAS_GRID_SIZE:10,NODE_TITLE_HEIGHT:30,NODE_TITLE_TEXT_Y:20,NODE_SLOT_HEIGHT:20,NODE_WIDGET_HEIGHT:20,NODE_WIDTH:140,NODE_MIN_WIDTH:50,NODE_COLLAPSED_RADIUS:10, +a.button)return h.close(),a.preventDefault(),!0},!0);b.scroll_speed||(b.scroll_speed=0.1);f.addEventListener("wheel",d,!0);f.addEventListener("mousewheel",d,!0);this.root=f;if(b.title){var e=document.createElement("div");e.className="litemenu-title";e.innerHTML=b.title;f.appendChild(e)}var e=0,r;for(r in a){var c=a.constructor==Array?a[r]:r;null!=c&&c.constructor!==String&&(c=void 0===c.content?String(c):c.content);this.addItem(c,a[r],b);e++}f.addEventListener("mouseleave",function(a){h.lock||(f.closing_timer&& +clearTimeout(f.closing_timer),f.closing_timer=setTimeout(h.close.bind(h,a),500))});f.addEventListener("mouseenter",function(a){f.closing_timer&&clearTimeout(f.closing_timer)});r=document;b.event&&(r=b.event.target.ownerDocument);r||(r=document);r.fullscreenElement?r.fullscreenElement.appendChild(f):r.body.appendChild(f);e=b.left||0;r=b.top||0;if(b.event){e=b.event.clientX-10;r=b.event.clientY-10;b.title&&(r-=20);b.parentMenu&&(e=b.parentMenu.root.getBoundingClientRect(),e=e.left+e.width);var c=document.body.getBoundingClientRect(), +l=f.getBoundingClientRect();e>c.width-l.width-10&&(e=c.width-l.width-10);r>c.height-l.height-10&&(r=c.height-l.height-10)}f.style.left=e+"px";f.style.top=r+"px";b.scale&&(f.style.transform="scale("+b.scale+")")}function B(a){this.points=a;this.nearest=this.selected=-1;this.size=null;this.must_update=!0;this.margin=5}var e=y.LiteGraph={VERSION:0.4,CANVAS_GRID_SIZE:10,NODE_TITLE_HEIGHT:30,NODE_TITLE_TEXT_Y:20,NODE_SLOT_HEIGHT:20,NODE_WIDGET_HEIGHT:20,NODE_WIDTH:140,NODE_MIN_WIDTH:50,NODE_COLLAPSED_RADIUS:10, NODE_COLLAPSED_WIDTH:80,NODE_TITLE_COLOR:"#999",NODE_TEXT_SIZE:14,NODE_TEXT_COLOR:"#AAA",NODE_SUBTEXT_SIZE:12,NODE_DEFAULT_COLOR:"#333",NODE_DEFAULT_BGCOLOR:"#353535",NODE_DEFAULT_BOXCOLOR:"#666",NODE_DEFAULT_SHAPE:"box",DEFAULT_SHADOW_COLOR:"rgba(0,0,0,0.5)",DEFAULT_GROUP_FONT:24,WIDGET_BGCOLOR:"#222",WIDGET_OUTLINE_COLOR:"#666",WIDGET_TEXT_COLOR:"#DDD",WIDGET_SECONDARY_TEXT_COLOR:"#999",LINK_COLOR:"#9A9",EVENT_LINK_COLOR:"#A86",CONNECTING_LINK_COLOR:"#AFA",MAX_NUMBER_OF_NODES:1E3,DEFAULT_POSITION:[100, 100],VALID_SHAPES:["default","box","round","card"],BOX_SHAPE:1,ROUND_SHAPE:2,CIRCLE_SHAPE:3,CARD_SHAPE:4,ARROW_SHAPE:5,INPUT:1,OUTPUT:2,EVENT:-1,ACTION:-1,ALWAYS:0,ON_EVENT:1,NEVER:2,ON_TRIGGER:3,UP:1,DOWN:2,LEFT:3,RIGHT:4,CENTER:5,STRAIGHT_LINK:0,LINEAR_LINK:1,SPLINE_LINK:2,NORMAL_TITLE:0,NO_TITLE:1,TRANSPARENT_TITLE:2,AUTOHIDE_TITLE:3,proxy:null,node_images_path:"",debug:!1,catch_exceptions:!0,throw_errors:!0,allow_scripts:!1,registered_node_types:{},node_types_by_file_extension:{},Nodes:{},searchbox_extras:{}, -registerNodeType:function(a,b){if(!b.prototype)throw"Cannot register a simple object, it must be a class with a prototype";b.type=a;g.debug&&console.log("Node registered: "+a);a.split("/");var d=b.name,k=a.lastIndexOf("/");b.category=a.substr(0,k);b.title||(b.title=d);if(b.prototype)for(var f in n.prototype)b.prototype[f]||(b.prototype[f]=n.prototype[f]);Object.hasOwnProperty(b.prototype,"shape")||Object.defineProperty(b.prototype,"shape",{set:function(a){switch(a){case "default":delete this._shape; -break;case "box":this._shape=g.BOX_SHAPE;break;case "round":this._shape=g.ROUND_SHAPE;break;case "circle":this._shape=g.CIRCLE_SHAPE;break;case "card":this._shape=g.CARD_SHAPE;break;default:this._shape=a}},get:function(a){return this._shape},enumerable:!0});(k=this.registered_node_types[a])&&console.log("replacing node type: "+a);this.registered_node_types[a]=b;b.constructor.name&&(this.Nodes[d]=b);if(g.onNodeTypeRegistered)g.onNodeTypeRegistered(a,b);if(k&&g.onNodeTypeReplaced)g.onNodeTypeReplaced(a, -b,k);b.prototype.onPropertyChange&&console.warn("LiteGraph node class "+a+" has onPropertyChange method, it must be called onPropertyChanged with d at the end");if(b.supported_extensions)for(f in b.supported_extensions)(d=b.supported_extensions[f])&&d.constructor===String&&(this.node_types_by_file_extension[d.toLowerCase()]=b)},wrapFunctionAsNode:function(a,b,d,k,f){for(var h=Array(b.length),w="",c=g.getParameterNames(b),e=0;ew&&(w=f.size[0]),c+=f.size[1]+a+g.NODE_TITLE_HEIGHT;b+=w+a}this.setDirtyCanvas(!0,!0)};c.prototype.getTime=function(){return this.globaltime};c.prototype.getFixedTime=function(){return this.fixedtime};c.prototype.getElapsedTime=function(){return this.elapsed_time};c.prototype.sendEventToAllNodes=function(a,b,d){d=d||g.ALWAYS;var k=this._nodes_in_order? -this._nodes_in_order:this._nodes;if(k)for(var f=0,h=k.length;f=g.MAX_NUMBER_OF_NODES)throw"LiteGraph: max number of nodes in a graph reached";null==a.id||-1==a.id?a.id=++this.last_node_id:this.last_node_ida.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos}, -enumerable:!0});this.id=-1;this.type=null;this.inputs=[];this.outputs=[];this.connections=[];this.properties={};this.properties_info=[];this.flags={}};n.prototype.configure=function(a){this.graph&&this.graph._version++;for(var b in a)if("properties"==b)for(var d in a.properties){if(this.properties[d]=a.properties[d],this.onPropertyChanged)this.onPropertyChanged(d,a.properties[d])}else null!=a[b]&&("object"==typeof a[b]?this[b]&&this[b].configure?this[b].configure(a[b]):this[b]=g.cloneObject(a[b], -this[b]):this[b]=a[b]);a.title||(this.title=this.constructor.title);if(this.onConnectionsChange){if(this.inputs)for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d._data=b,this.outputs[a].links))for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d.type=b,this.outputs[a].links))for(d=0;d=this.inputs.length||null==this.inputs[a].link)){var d=this.graph.links[this.inputs[a].link];if(!d)return null;if(!b)return d.data;var k=this.graph.getNodeById(d.origin_id);if(!k)return d.data;if(k.updateOutputData)k.updateOutputData(d.origin_slot);else if(k.onExecute)k.onExecute();return d.data}};n.prototype.getInputDataType=function(a){if(!this.inputs||a>=this.inputs.length||null==this.inputs[a].link)return null;a=this.graph.links[this.inputs[a].link]; -if(!a)return null;var b=this.graph.getNodeById(a.origin_id);return b?(a=b.outputs[a.origin_slot])?a.type:null:a.type};n.prototype.getInputDataByName=function(a,b){var d=this.findInputSlot(a);return-1==d?null:this.getInputData(d,b)};n.prototype.isInputConnected=function(a){return this.inputs?a=this.inputs.length)return null;a=this.inputs[a];return a&&null!==a.link?(a=this.graph.links[a.link])?this.graph.getNodeById(a.origin_id):null:null};n.prototype.getInputOrProperty=function(a){if(!this.inputs||!this.inputs.length)return this.properties?this.properties[a]:null;for(var b=0,d=this.inputs.length;b= -this.outputs.length?null:this.outputs[a]._data};n.prototype.getOutputInfo=function(a){return this.outputs?a=this.outputs.length)return null;a=this.outputs[a];if(!a.links||0==a.links.length)return null;for(var b=[],d=0;da&&this.pos[1]-f-db)return!0;return!1};n.prototype.getSlotInPosition=function(a,b){var d=new Float32Array(2);if(this.inputs)for(var k=0,f=this.inputs.length;k=this.outputs.length)return g.debug&&console.log("Connect: Error, slot number not found"),null;b&&b.constructor===Number&&(b=this.graph.getNodeById(b)); -if(!b)throw"target node is null";if(b==this)return null;if(d.constructor===String){if(d=b.findInputSlot(d),-1==d)return g.debug&&console.log("Connect: Error, no slot of name "+d),null}else{if(d===g.EVENT)return null;if(!b.inputs||d>=b.inputs.length)return g.debug&&console.log("Connect: Error, slot number not found"),null}null!=b.inputs[d].link&&b.disconnectInput(d);var k=this.outputs[a];if(b.onConnectInput&&!1===b.onConnectInput(d,k.type,k))return null;var f=b.inputs[d],h=null;if(g.isValidConnection(k.type, -f.type)){h=new m(++this.graph.last_link_id,f.type,this.id,a,b.id,d);this.graph.links[h.id]=h;null==k.links&&(k.links=[]);k.links.push(h.id);b.inputs[d].link=h.id;this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(g.OUTPUT,a,!0,h,k);if(b.onConnectionsChange)b.onConnectionsChange(g.INPUT,d,!0,h,f);this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(g.INPUT,b,d,this,a),this.graph.onNodeConnectionChange(g.OUTPUT,this,a,b,d))}this.setDirtyCanvas(!1, -!0);this.graph.connectionChange(this,h);return h};n.prototype.disconnectOutput=function(a,b){if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return g.debug&&console.log("Connect: Error, no slot of name "+a),!1}else if(!this.outputs||a>=this.outputs.length)return g.debug&&console.log("Connect: Error, slot number not found"),!1;var d=this.outputs[a];if(!d||!d.links||0==d.links.length)return!1;if(b){b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"Target Node not found"; -for(var k=0,f=d.links.length;k=this.inputs.length)return g.debug&&console.log("Connect: Error, slot number not found"),!1;var b=this.inputs[a];if(!b)return!1;var d=this.inputs[a].link;this.inputs[a].link=null;var k=this.graph.links[d];if(k){var f=this.graph.getNodeById(k.origin_id);if(!f)return!1;var h=f.outputs[k.origin_slot];if(!h||!h.links||0==h.links.length)return!1;for(var w=0,c=h.links.length;wb&&this.inputs[b].pos)return d[0]=this.pos[0]+this.inputs[b].pos[0],d[1]=this.pos[1]+this.inputs[b].pos[1],d;if(!a&&k>b&&this.outputs[b].pos)return d[0]=this.pos[0]+this.outputs[b].pos[0], -d[1]=this.pos[1]+this.outputs[b].pos[1],d;if(this.horizontal)return d[0]=this.pos[0]+this.size[0]/k*(b+0.5),d[1]=a?this.pos[1]-g.NODE_TITLE_HEIGHT:this.pos[1]+this.size[1],d;d[0]=a?this.pos[0]+f:this.pos[0]+this.size[0]+1-f;d[1]=this.pos[1]+(b+0.7)*g.NODE_SLOT_HEIGHT+(this.constructor.slot_start_y||0);return d};n.prototype.alignToGrid=function(){this.pos[0]=g.CANVAS_GRID_SIZE*Math.round(this.pos[0]/g.CANVAS_GRID_SIZE);this.pos[1]=g.CANVAS_GRID_SIZE*Math.round(this.pos[1]/g.CANVAS_GRID_SIZE)};n.prototype.trace= -function(a){this.console||(this.console=[]);this.console.push(a);this.console.length>n.MAX_CONSOLE&&this.console.shift();this.graph.onNodeTrace(this,a)};n.prototype.setDirtyCanvas=function(a,b){this.graph&&this.graph.sendActionToCanvas("setDirty",[a,b])};n.prototype.loadImage=function(a){var b=new Image;b.src=g.node_images_path+a;b.ready=!1;var d=this;b.onload=function(){this.ready=!0;d.setDirtyCanvas(!0)};return b};n.prototype.captureInput=function(a){if(this.graph&&this.graph.list_of_graphcanvas)for(var b= -this.graph.list_of_graphcanvas,d=0;da.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0}); -Object.defineProperty(this,"size",{set:function(a){!a||2>a.length||(this._size[0]=Math.max(140,a[0]),this._size[1]=Math.max(80,a[1]))},get:function(){return this._size},enumerable:!0})};l.prototype.configure=function(a){this.title=a.title;this._bounding.set(a.bounding);this.color=a.color;this.font=a.font};l.prototype.serialize=function(){var a=this._bounding;return{title:this.title,bounding:[Math.round(a[0]),Math.round(a[1]),Math.round(a[2]),Math.round(a[3])],color:this.color,font:this.font}};l.prototype.move= -function(a,b,d){this._pos[0]+=a;this._pos[1]+=b;if(!d)for(d=0;dthis.max_scale&&(a=this.max_scale);if(a!=this.scale&&this.element){var d=this.element.getBoundingClientRect(); -if(d){b=b||[0.5*d.width,0.5*d.height];d=this.convertCanvasToOffset(b);this.scale=a;0.01>Math.abs(this.scale-1)&&(this.scale=1);var k=this.convertCanvasToOffset(b),d=[k[0]-d[0],k[1]-d[1]];this.offset[0]+=d[0];this.offset[1]+=d[1];if(this.onredraw)this.onredraw(this)}}};x.prototype.changeDeltaScale=function(a,b){this.changeScale(this.scale*a,b)};x.prototype.reset=function(){this.scale=1;this.offset[0]=0;this.offset[1]=0};y.LGraphCanvas=g.LGraphCanvas=e;e.link_type_colors={"-1":g.EVENT_LINK_COLOR,number:"#AAA", -node:"#DCA"};e.gradients={};e.prototype.clear=function(){this.fps=this.render_time=this.last_draw_time=this.frame=0;this.dragging_rectangle=null;this.selected_nodes={};this.selected_group=null;this.visible_nodes=[];this.connecting_node=this.node_capturing_input=this.node_over=this.node_dragged=null;this.highlighted_links={};this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_widget=this.node_in_panel=this.dirty_area=null;this.last_mouse=[0,0];this.last_mouseclick=0;this.visible_area.set([0,0,0,0]); -if(this.onClear)this.onClear()};e.prototype.setGraph=function(a,b){this.graph!=a&&(b||this.clear(),!a&&this.graph?this.graph.detachCanvas(this):(a.attachCanvas(this),this.setDirty(!0,!0)))};e.prototype.openSubgraph=function(a){if(!a)throw"graph cannot be null";if(this.graph==a)throw"graph cannot be the same";this.clear();this.graph&&(this._graph_stack||(this._graph_stack=[]),this._graph_stack.push(this.graph));a.attachCanvas(this);this.setDirty(!0,!0)};e.prototype.closeSubgraph=function(){if(this._graph_stack&& -0!=this._graph_stack.length){var a=this.graph._subgraph_node,b=this._graph_stack.pop();this.selected_nodes={};this.highlighted_links={};b.attachCanvas(this);this.setDirty(!0,!0);a&&(this.centerOnNode(a),this.selectNodes([a]))}};e.prototype.getCurrentGraph=function(){return this.graph};e.prototype.setCanvas=function(a,b){if(a&&a.constructor===String&&(a=document.getElementById(a),!a))throw"Error creating LiteGraph canvas: Canvas not found";if(a!==this.canvas&&(!a&&this.canvas&&(b||this.unbindEvents()), -this.canvas=a,this.ds.element=a)){a.className+=" lgraphcanvas";a.data=this;a.tabindex="1";this.bgcanvas=null;this.bgcanvas||(this.bgcanvas=document.createElement("canvas"),this.bgcanvas.width=this.canvas.width,this.bgcanvas.height=this.canvas.height);if(null==a.getContext){if("canvas"!=a.localName)throw"Element supplied for LGraphCanvas must be a element, you passed a "+a.localName;throw"This browser doesn't support Canvas";}null==(this.ctx=a.getContext("2d"))&&(a.webgl_enabled||console.warn("This canvas seems to be WebGL, enabling WebGL renderer"), -this.enableWebGL());this._mousemove_callback=this.processMouseMove.bind(this);this._mouseup_callback=this.processMouseUp.bind(this);b||this.bindEvents()}};e.prototype._doNothing=function(a){a.preventDefault();return!1};e.prototype._doReturnTrue=function(a){a.preventDefault();return!0};e.prototype.bindEvents=function(){if(this._events_binded)console.warn("LGraphCanvas: events already binded");else{var a=this.canvas,b=this.getCanvasWindow().document;this._mousedown_callback=this.processMouseDown.bind(this); -this._mousewheel_callback=this.processMouseWheel.bind(this);a.addEventListener("mousedown",this._mousedown_callback,!0);a.addEventListener("mousemove",this._mousemove_callback);a.addEventListener("mousewheel",this._mousewheel_callback,!1);a.addEventListener("contextmenu",this._doNothing);a.addEventListener("DOMMouseScroll",this._mousewheel_callback,!1);a.addEventListener("touchstart",this.touchHandler,!0);a.addEventListener("touchmove",this.touchHandler,!0);a.addEventListener("touchend",this.touchHandler, -!0);a.addEventListener("touchcancel",this.touchHandler,!0);this._key_callback=this.processKey.bind(this);a.addEventListener("keydown",this._key_callback,!0);b.addEventListener("keyup",this._key_callback,!0);this._ondrop_callback=this.processDrop.bind(this);a.addEventListener("dragover",this._doNothing,!1);a.addEventListener("dragend",this._doNothing,!1);a.addEventListener("drop",this._ondrop_callback,!1);a.addEventListener("dragenter",this._doReturnTrue,!1);this._events_binded=!0}};e.prototype.unbindEvents= -function(){if(this._events_binded){var a=this.getCanvasWindow().document;this.canvas.removeEventListener("mousedown",this._mousedown_callback);this.canvas.removeEventListener("mousewheel",this._mousewheel_callback);this.canvas.removeEventListener("DOMMouseScroll",this._mousewheel_callback);this.canvas.removeEventListener("keydown",this._key_callback);a.removeEventListener("keyup",this._key_callback);this.canvas.removeEventListener("contextmenu",this._doNothing);this.canvas.removeEventListener("drop", -this._ondrop_callback);this.canvas.removeEventListener("dragenter",this._doReturnTrue);this.canvas.removeEventListener("touchstart",this.touchHandler);this.canvas.removeEventListener("touchmove",this.touchHandler);this.canvas.removeEventListener("touchend",this.touchHandler);this.canvas.removeEventListener("touchcancel",this.touchHandler);this._ondrop_callback=this._key_callback=this._mousewheel_callback=this._mousedown_callback=null;this._events_binded=!1}else console.warn("LGraphCanvas: no events binded")}; -e.getFileExtension=function(a){var b=a.indexOf("?");-1!=b&&(a=a.substr(0,b));b=a.lastIndexOf(".");return-1==b?"":a.substr(b+1).toLowerCase()};e.prototype.enableWebGL=function(){if(void 0===typeof GL)throw"litegl.js must be included to use a WebGL canvas";if(void 0===typeof enableWebGLCanvas)throw"webglCanvas.js must be included to use this feature";this.gl=this.ctx=enableWebGLCanvas(this.canvas);this.ctx.webgl=!0;this.bgcanvas=this.canvas;this.bgctx=this.gl;this.canvas.webgl_enabled=!0};e.prototype.setDirty= -function(a,b){a&&(this.dirty_canvas=!0);b&&(this.dirty_bgcanvas=!0)};e.prototype.getCanvasWindow=function(){if(!this.canvas)return window;var a=this.canvas.ownerDocument;return a.defaultView||a.parentWindow};e.prototype.startRendering=function(){function a(){this.pause_rendering||this.draw();var b=this.getCanvasWindow();this.is_rendering&&b.requestAnimationFrame(a.bind(this))}this.is_rendering||(this.is_rendering=!0,a.call(this))};e.prototype.stopRendering=function(){this.is_rendering=!1};e.prototype.processMouseDown= -function(a){if(this.graph){this.adjustMouseEvent(a);var b=this.getCanvasWindow();e.active_canvas=this;this.canvas.removeEventListener("mousemove",this._mousemove_callback);b.document.addEventListener("mousemove",this._mousemove_callback,!0);b.document.addEventListener("mouseup",this._mouseup_callback,!0);var d=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes,5),k=!1,f=300>g.getTime()-this.last_mouseclick;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;this.canvas.focus(); -g.closeAllContextMenus(b);if(!this.onMouse||!0!=this.onMouse(a)){if(1==a.which){a.ctrlKey&&(this.dragging_rectangle=new Float32Array(4),this.dragging_rectangle[0]=a.canvasX,this.dragging_rectangle[1]=a.canvasY,this.dragging_rectangle[2]=1,this.dragging_rectangle[3]=1,k=!0);var h=!1;if(d&&this.allow_interaction&&!k&&!this.read_only){this.live_mode||d.flags.pinned||this.bringToFront(d);if(!this.connecting_node&&!d.flags.collapsed&&!this.live_mode)if(!k&&!1!==d.resizable&&z(a.canvasX,a.canvasY,d.pos[0]+ -d.size[0]-5,d.pos[1]+d.size[1]-5,10,10))this.resizing_node=d,this.canvas.style.cursor="se-resize",k=!0;else{if(d.outputs)for(var w=0,c=d.outputs.length;wh[0]+4||a.canvasYh[1]+4)){this.showLinkMenu(d,a);this.over_link_center=null;break}this.selected_group=this.graph.getGroupOnPos(a.canvasX,a.canvasY);this.selected_group_resizing=!1;this.selected_group&&!this.read_only&&(a.ctrlKey&&(this.dragging_rectangle=null),10>C([a.canvasX, -a.canvasY],[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])*this.ds.scale?this.selected_group_resizing=!0:this.selected_group.recomputeInsideNodes());f&&!this.read_only&&this.allow_searchbox&&this.showSearchBox(a);h=!0}!k&&h&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}else 2!=a.which&&3==a.which&&(this.read_only||this.processContextMenu(d,a));this.last_mouse[0]=a.localX;this.last_mouse[1]=a.localY;this.last_mouseclick=g.getTime(); -this.last_mouse_dragging=!0;this.graph.change();(!b.document.activeElement||"input"!=b.document.activeElement.nodeName.toLowerCase()&&"textarea"!=b.document.activeElement.nodeName.toLowerCase())&&a.preventDefault();a.stopPropagation();if(this.onMouseDown)this.onMouseDown(a);return!1}}};e.prototype.processMouseMove=function(a){this.autoresize&&this.resize();if(this.graph){e.active_canvas=this;this.adjustMouseEvent(a);var b=[a.localX,a.localY],d=[b[0]-this.last_mouse[0],b[1]-this.last_mouse[1]];this.last_mouse= -b;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;a.dragging=this.last_mouse_dragging;this.node_widget&&(this.processNodeWidgets(this.node_widget[0],this.canvas_mouse,a,this.node_widget[1]),this.dirty_canvas=!0);if(this.dragging_rectangle)this.dragging_rectangle[2]=a.canvasX-this.dragging_rectangle[0],this.dragging_rectangle[3]=a.canvasY-this.dragging_rectangle[1],this.dirty_canvas=!0;else if(this.selected_group&&!this.read_only)this.selected_group_resizing?this.selected_group.size= -[a.canvasX-this.selected_group.pos[0],a.canvasY-this.selected_group.pos[1]]:(this.selected_group.move(d[0]/this.ds.scale,d[1]/this.ds.scale,a.ctrlKey),this.selected_group._nodes.length&&(this.dirty_canvas=!0)),this.dirty_bgcanvas=!0;else if(this.dragging_canvas)this.ds.offset[0]+=d[0]/this.ds.scale,this.ds.offset[1]+=d[1]/this.ds.scale,this.dirty_bgcanvas=this.dirty_canvas=!0;else if(this.allow_interaction&&!this.read_only){this.connecting_node&&(this.dirty_canvas=!0);for(var k=this.graph.getNodeOnPos(a.canvasX, -a.canvasY,this.visible_nodes),b=0,f=this.graph._nodes.length;bc[0]+4||a.canvasYc[1]+4)){f=h;break}}f!=this.over_link_center&&(this.over_link_center=f,this.dirty_canvas=!0);this.canvas&&(this.canvas.style.cursor="")}if(this.node_capturing_input&&this.node_capturing_input!=k&&this.node_capturing_input.onMouseMove)this.node_capturing_input.onMouseMove(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]],this);if(this.node_dragged&&!this.live_mode){for(b in this.selected_nodes)k=this.selected_nodes[b], -k.pos[0]+=d[0]/this.ds.scale,k.pos[1]+=d[1]/this.ds.scale;this.dirty_bgcanvas=this.dirty_canvas=!0}this.resizing_node&&!this.live_mode&&(this.resizing_node.size[0]=a.canvasX-this.resizing_node.pos[0],this.resizing_node.size[1]=a.canvasY-this.resizing_node.pos[1],d=Math.max(this.resizing_node.inputs?this.resizing_node.inputs.length:0,this.resizing_node.outputs?this.resizing_node.outputs.length:0)*g.NODE_SLOT_HEIGHT+(this.resizing_node.widgets?this.resizing_node.widgets.length:0)*(g.NODE_WIDGET_HEIGHT+ -4)+4,this.resizing_node.size[1]this.dragging_rectangle[3]?this.dragging_rectangle[1]-f:this.dragging_rectangle[1];this.dragging_rectangle[0]=0>this.dragging_rectangle[2]?this.dragging_rectangle[0]-k:this.dragging_rectangle[0]; -this.dragging_rectangle[1]=h;this.dragging_rectangle[2]=k;this.dragging_rectangle[3]=f;f=[];for(h=0;ha.click_time&&z(a.canvasX,a.canvasY,k.pos[0],k.pos[1]-g.NODE_TITLE_HEIGHT,g.NODE_TITLE_HEIGHT,g.NODE_TITLE_HEIGHT)&&k.collapse();this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_dragged.pos[0]=Math.round(this.node_dragged.pos[0]);this.node_dragged.pos[1]=Math.round(this.node_dragged.pos[1]);this.graph.config.align_to_grid&&this.node_dragged.alignToGrid();if(this.onNodeMoved)this.onNodeMoved(this.node_dragged); -this.node_dragged=null}else{k=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes);!k&&300>a.click_time&&this.deselectAllNodes();this.dirty_canvas=!0;this.dragging_canvas=!1;if(this.node_over&&this.node_over.onMouseUp)this.node_over.onMouseUp(a,[a.canvasX-this.node_over.pos[0],a.canvasY-this.node_over.pos[1]],this);if(this.node_capturing_input&&this.node_capturing_input.onMouseUp)this.node_capturing_input.onMouseUp(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]])}}else 2== -a.which?(this.dirty_canvas=!0,this.dragging_canvas=!1):3==a.which&&(this.dirty_canvas=!0,this.dragging_canvas=!1);this.graph.change();a.stopPropagation();a.preventDefault();return!1}};e.prototype.processMouseWheel=function(a){if(this.graph&&this.allow_dragcanvas){var b=null!=a.wheelDeltaY?a.wheelDeltaY:-60*a.detail;this.adjustMouseEvent(a);var d=this.ds.scale;0b&&(d*=1/1.1);this.ds.changeScale(d,[a.localX,a.localY]);this.graph.change();a.preventDefault();return!1}};e.prototype.isOverNodeBox= -function(a,b,d){var k=g.NODE_TITLE_HEIGHT;return z(b,d,a.pos[0]+2,a.pos[1]+2-k,k-4,k-4)?!0:!1};e.prototype.isOverNodeInput=function(a,b,d,k){if(a.inputs)for(var f=0,g=a.inputs.length;fd- -this.graph._last_trigger_time)&&this.drawBackCanvas();(this.dirty_canvas||a)&&this.drawFrontCanvas();this.fps=this.render_time?1/this.render_time:0;this.frame+=1}};e.prototype.drawFrontCanvas=function(){this.dirty_canvas=!1;this.ctx||(this.ctx=this.bgcanvas.getContext("2d"));var a=this.ctx;if(a){a.start2D&&a.start2D();var b=this.canvas;a.restore();a.setTransform(1,0,0,1,0,0);this.dirty_area&&(a.save(),a.beginPath(),a.rect(this.dirty_area[0],this.dirty_area[1],this.dirty_area[2],this.dirty_area[3]), -a.clip());this.clear_background&&a.clearRect(0,0,b.width,b.height);this.bgcanvas==this.canvas?this.drawBackCanvas():a.drawImage(this.bgcanvas,0,0);if(this.onRender)this.onRender(b,a);this.show_info&&this.renderInfo(a);if(this.graph){a.save();this.ds.toCanvasContext(a);for(var b=this.computeVisibleNodes(null,this.visible_nodes),d=0;d> ";b.fillText(k+d.getTitle(),0.5*a.width,40);b.restore()}d=!1;this.onRenderBackground&&(d=this.onRenderBackground(a,b));b.restore(); -b.setTransform(1,0,0,1,0,0);this.visible_links.length=0;if(this.graph){b.save();this.ds.toCanvasContext(b);if(this.background_image&&0.5this.ds.scale;if(this.live_mode){if(!a.flags.collapsed&&(b.shadowColor="transparent",a.onDrawForeground))a.onDrawForeground(b,this,this.canvas)}else{var h=this.editor_alpha;b.globalAlpha=h;this.render_shadows&&!f?(b.shadowColor= -g.DEFAULT_SHADOW_COLOR,b.shadowOffsetX=2*this.ds.scale,b.shadowOffsetY=2*this.ds.scale,b.shadowBlur=3*this.ds.scale):b.shadowColor="transparent";if(!a.flags.collapsed||!a.onDrawCollapsed||!0!=a.onDrawCollapsed(b,this)){var c=a._shape||g.BOX_SHAPE;q.set(a.size);var e=a.horizontal;if(a.flags.collapsed){b.font=this.inner_text_font;var r=a.getTitle?a.getTitle():a.title;null!=r&&(a._collapsed_width=Math.min(a.size[0],b.measureText(r).width+2*g.NODE_TITLE_HEIGHT),q[0]=a._collapsed_width,q[1]=0)}a.clip_area&& -(b.save(),b.beginPath(),c==g.BOX_SHAPE?b.rect(0,0,q[0],q[1]):c==g.ROUND_SHAPE?b.roundRect(0,0,q[0],q[1],10):c==g.CIRCLE_SHAPE&&b.arc(0.5*q[0],0.5*q[1],0.5*q[0],0,2*Math.PI),b.clip());a.has_errors&&(k="red");this.drawNodeShape(a,b,q,d,k,a.is_selected,a.mouseOver);b.shadowColor="transparent";if(a.onDrawForeground)a.onDrawForeground(b,this,this.canvas);b.textAlign=e?"center":"left";b.font=this.inner_text_font;k=!f;c=this.connecting_output;b.lineWidth=1;var r=0,s=new Float32Array(2);if(!a.flags.collapsed){if(a.inputs)for(d= -0;dthis.ds.scale,p=a._shape||a.constructor.shape||g.ROUND_SHAPE,s=a.constructor.title_mode,l=!0;s==g.TRANSPARENT_TITLE?l=!1:s==g.AUTOHIDE_TITLE&&c&&(l=!0);A[0]=0;A[1]=l?-f:0;A[2]=d[0]+1;A[3]=l?d[1]+f:d[1];c=b.globalAlpha;b.beginPath();p==g.BOX_SHAPE|| -r?b.fillRect(A[0],A[1],A[2],A[3]):p==g.ROUND_SHAPE||p==g.CARD_SHAPE?b.roundRect(A[0],A[1],A[2],A[3],this.round_radius,p==g.CARD_SHAPE?0:this.round_radius):p==g.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0],0,2*Math.PI);b.fill();a.flags.collapsed||(b.shadowColor="transparent",b.fillStyle="rgba(0,0,0,0.2)",b.fillRect(0,-1,A[2],2));b.shadowColor="transparent";if(a.onDrawBackground)a.onDrawBackground(b,this,this.canvas);if(l||s==g.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,f,d,this.ds.scale, -k);else if(s!=g.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){l=a.constructor.title_color||k;a.flags.collapsed&&(b.shadowColor=g.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var u=e.gradients[l];u||(u=e.gradients[l]=b.createLinearGradient(0,0,400,0),u.addColorStop(0,l),u.addColorStop(1,"#000"));b.fillStyle=u}else b.fillStyle=l;b.beginPath();p==g.BOX_SHAPE||r?b.rect(0,-f,d[0]+1,f):p!=g.ROUND_SHAPE&&p!=g.CARD_SHAPE||b.roundRect(0,-f,d[0]+1,f,this.round_radius,a.flags.collapsed? -this.round_radius:0);b.fill();b.shadowColor="transparent"}if(a.onDrawTitleBox)a.onDrawTitleBox(b,f,d,this.ds.scale);else p==g.ROUND_SHAPE||p==g.CIRCLE_SHAPE||p==g.CARD_SHAPE?(r&&(b.fillStyle="black",b.beginPath(),b.arc(0.5*f,-0.5*f,6,0,2*Math.PI),b.fill()),b.fillStyle=a.boxcolor||g.NODE_DEFAULT_BOXCOLOR,r?b.fillRect(0.5*f-5,-0.5*f-5,10,10):(b.beginPath(),b.arc(0.5*f,-0.5*f,5,0,2*Math.PI),b.fill())):(r&&(b.fillStyle="black",b.fillRect(0.5*(f-10)-1,-0.5*(f+10)-1,12,12)),b.fillStyle=a.boxcolor||g.NODE_DEFAULT_BOXCOLOR, -b.fillRect(0.5*(f-10),-0.5*(f+10),10,10));b.globalAlpha=c;if(a.onDrawTitleText)a.onDrawTitleText(b,f,d,this.ds.scale,this.title_text_font,h);!r&&(b.font=this.title_text_font,r=a.getTitle())&&(b.fillStyle=h?"white":a.constructor.title_text_color||this.node_title_color,a.flags.collapsed?(b.textAlign="center",c=b.measureText(r),b.fillText(r,f+0.5*c.width,g.NODE_TITLE_TEXT_Y-f),b.textAlign="left"):(b.textAlign="left",b.fillText(r,f,g.NODE_TITLE_TEXT_Y-f)));if(a.onDrawTitle)a.onDrawTitle(b)}if(h){if(a.onBounding)a.onBounding(A); -s==g.TRANSPARENT_TITLE&&(A[1]-=f,A[3]+=f);b.lineWidth=1;b.globalAlpha=0.8;b.beginPath();p==g.BOX_SHAPE?b.rect(-6+A[0],-6+A[1],12+A[2],12+A[3]):p==g.ROUND_SHAPE||p==g.CARD_SHAPE&&a.flags.collapsed?b.roundRect(-6+A[0],-6+A[1],12+A[2],12+A[3],2*this.round_radius):p==g.CARD_SHAPE?b.roundRect(-6+A[0],-6+A[1],12+A[2],12+A[3],2*this.round_radius,2):p==g.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0]+6,0,2*Math.PI);b.strokeStyle="#FFF";b.stroke();b.strokeStyle=k;b.globalAlpha=1}};var s=new Float32Array(4), -p=new Float32Array(4),t=new Float32Array(2),h=new Float32Array(2);e.prototype.drawConnections=function(a){var b=g.getTime(),d=this.visible_area;s[0]=d[0]-20;s[1]=d[1]-20;s[2]=d[2]+40;s[3]=d[3]+40;a.lineWidth=this.connections_width;a.fillStyle="#AAA";a.strokeStyle="#AAA";a.globalAlpha=this.editor_alpha;for(var d=this.graph._nodes,k=0,f=d.length;kp[2]&&(p[0]+=p[2],p[2]=Math.abs(p[2]));0>p[3]&&(p[1]+=p[3],p[3]=Math.abs(p[3]));if(v(p,s)){var n=r.outputs[l],l=c.inputs[w];if(n&&l&&(r=n.dir||(r.horizontal?g.DOWN:g.RIGHT),l=l.dir||(c.horizontal?g.UP:g.LEFT),this.renderLink(a,q,u,e,!1,0,null,r,l),e&&e._last_time&&1E3>b-e._last_time)){var n= -2-0.002*(b-e._last_time),J=a.globalAlpha;a.globalAlpha=J*n;this.renderLink(a,q,u,e,!0,n,"white",r,l);a.globalAlpha=J}}}}}}a.globalAlpha=1};e.prototype.renderLink=function(a,b,d,k,f,h,c,r,p,s){k&&this.visible_links.push(k);!c&&k&&(c=k.color||e.link_type_colors[k.type]);c||(c=this.default_link_color);null!=k&&this.highlighted_links[k.id]&&(c="#FFF");r=r||g.RIGHT;p=p||g.LEFT;var l=C(b,d);this.render_connections_border&&0.6b[1]?0:Math.PI,a.save(),a.translate(u[0],u[1]),a.rotate(q),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore(),a.save(),a.translate(s[0],s[1]),a.rotate(n),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill());if(h)for(a.fillStyle= -c,u=0;5>u;++u)h=(0.001*g.getTime()+0.2*u)%1,f=this.computeConnectionPoint(b,d,h,r,p),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill()};e.prototype.computeConnectionPoint=function(a,b,d,k,f){k=k||g.RIGHT;f=f||g.LEFT;var h=C(a,b),c=[a[0],a[1]],e=[b[0],b[1]];switch(k){case g.LEFT:c[0]+=-0.25*h;break;case g.RIGHT:c[0]+=0.25*h;break;case g.UP:c[1]+=-0.25*h;break;case g.DOWN:c[1]+=0.25*h}switch(f){case g.LEFT:e[0]+=-0.25*h;break;case g.RIGHT:e[0]+=0.25*h;break;case g.UP:e[1]+=-0.25*h;break;case g.DOWN:e[1]+= -0.25*h}k=(1-d)*(1-d)*(1-d);f=3*(1-d)*(1-d)*d;h=3*(1-d)*d*d;d*=d*d;return[k*a[0]+f*c[0]+h*e[0]+d*b[0],k*a[1]+f*c[1]+h*e[1]+d*b[1]]};e.prototype.drawExecutionOrder=function(a){a.shadowColor="transparent";a.globalAlpha=0.25;a.textAlign="center";a.strokeStyle="white";a.globalAlpha=0.75;for(var b=this.visible_nodes,d=0;du.last_y&&cu.options.max&&(u.value=u.options.max);else if("mousedown"==d.type)if((c=u.options.values)&&c.constructor===Function&&(c=u.options.values(u,a)),h=40>h?-1:h>e-40?1:0,"number"==u.type)u.value+=0.1*h*(u.options.step||1),null!=u.options.min&&u.valueu.options.max&&(u.value=u.options.max);else if(h)p=c.indexOf(u.value)+h,p>=c.length&&(p=0),0>p&&(p=c.length-1),u.value=c[p];else{new g.ContextMenu(c,{scale:Math.max(1,this.ds.scale),event:d,className:"dark",callback:q.bind(u)},p);var q=function(a,b,d){this.value=a;f(this,a);r.dirty_canvas=!0;return!1}}k!=u.value&&setTimeout(function(){f(this,this.value)}.bind(u),20);this.dirty_canvas=!0;break;case "toggle":"mousedown"==d.type&&(u.value=!u.value,setTimeout(function(){f(u, -u.value)},20));break;case "string":case "text":"mousedown"==d.type&&this.prompt("Value",u.value,function(a){this.value=a;f(this,a)}.bind(u),d);break;default:u.mouse&&u.mouse(ctx,d,[h,c],a)}return u}}return null};e.prototype.drawGroups=function(a,b){if(this.graph){var d=this.graph._groups;b.save();b.globalAlpha=0.5*this.editor_alpha;for(var k=0;k -d&&0.01>b.editor_alpha&&(clearInterval(k),1>d&&(b.live_mode=!0));1"+p+""+a+"",value:p});if(r.length)return new g.ContextMenu(r,{event:d,callback:h,parentMenu:k,allow_html:!0,node:f},b),!1}};e.decodeHTML=function(a){var b= -document.createElement("div");b.innerText=a;return b.innerHTML};e.onResizeNode=function(a,b,d,k,f){f&&(f.size=f.computeSize(),f.setDirtyCanvas(!0,!0))};e.prototype.showLinkMenu=function(a,b){var d=this;console.log(a);var k=new g.ContextMenu(["Add Node",null,"Delete"],{event:b,title:null!=a.data?a.data.constructor.name:null,callback:function(b,g,h){switch(b){case "Add Node":e.onMenuAdd(null,null,h,k,function(b){console.log("node autoconnect");var f=d.graph.getNodeById(a.origin_id),k=d.graph.getNodeById(a.target_id); -b.inputs&&b.inputs.length&&b.outputs&&b.outputs.length&&f.outputs[a.origin_slot].type==b.inputs[0].type&&b.outputs[0].type==k.inputs[0].type&&(f.connect(a.origin_slot,b,0),b.connect(0,k,a.target_slot),b.pos[0]-=0.5*b.size[0])});break;case "Delete":d.graph.removeLink(a.id)}}});return!1};e.onShowPropertyEditor=function(a,b,d,k,f){function h(){var b=r.value;"Number"==a.type?b=Number(b):"Boolean"==a.type&&(b=Boolean(b));f[g]=b;c.parentNode&&c.parentNode.removeChild(c);f.setDirtyCanvas(!0,!0)}var g=a.property|| -"title";b=f[g];var c=document.createElement("div");c.className="graphdialog";c.innerHTML="";c.querySelector(".name").innerText=g;var r=c.querySelector("input");r&&(r.value=b,r.addEventListener("blur",function(a){this.focus()}),r.addEventListener("keydown",function(a){13==a.keyCode&&(h(),a.preventDefault(),a.stopPropagation())}));b=e.active_canvas.canvas;d=b.getBoundingClientRect();var p=k=-20;d&&(k-=d.left,p-= -d.top);event?(c.style.left=event.clientX+k+"px",c.style.top=event.clientY+p+"px"):(c.style.left=0.5*b.width+k+"px",c.style.top=0.5*b.height+p+"px");c.querySelector("button").addEventListener("click",h);b.parentNode.appendChild(c)};e.prototype.prompt=function(a,b,d,k){var f=this;a=a||"";var g=!1,h=document.createElement("div");h.className="graphdialog rounded";h.innerHTML=" ";h.close=function(){f.prompt_box= -null;h.parentNode&&h.parentNode.removeChild(h)};1e.search_limit)break}}p=null;if(Array.prototype.filter)p=Object.keys(g.registered_node_types).filter(w);else for(c in p= -[],g.registered_node_types)w(c)&&p.push(c);for(c=0;ce.search_limit);c++);var w=function(a){var b=g.registered_node_types[a];return r&&b.filter!=r?!1:-1!==a.toLowerCase().indexOf(d)}}}var f=this,h=e.active_canvas,c=h.canvas,r=c.ownerDocument||document,p=document.createElement("div");p.className="litegraph litesearchbox graphdialog rounded";p.innerHTML="Search
"; -p.close=function(){f.search_box=null;r.body.focus();r.body.style.overflow="";setTimeout(function(){f.canvas.focus()},20);p.parentNode&&p.parentNode.removeChild(p)};var s=null;1c.height-200&&(q.style.maxHeight=c.height-a.layerY-20+"px");t.focus();return p};e.prototype.showEditPropertyValue=function(a,b,d){function k(){f(u.value)}function f(d){"number"==typeof a.properties[b]&&(d=Number(d));if("array"==h||"object"==h)d=JSON.parse(d);a.properties[b]=d;a._graph&&a._graph._version++;if(a.onPropertyChanged)a.onPropertyChanged(b, -d);e.close();a.setDirtyCanvas(!0,!0)}if(a&&void 0!==a.properties[b]){d=d||{};var h="string";null!==a.properties[b]&&(h=typeof a.properties[b]);var g=null;a.getPropertyInfo&&(g=a.getPropertyInfo(b));if(a.properties_info)for(var c=0;c";else if("enum"==h&&g.values){r= -""}else if("boolean"==h)r="";else{console.warn("unknown type: "+h);return}var e=this.createDialog(""+b+""+r+"",d);if("enum"==h&&g.values){var u=e.querySelector("select"); -u.addEventListener("change",function(a){f(a.target.value)})}else if("boolean"==h)(u=e.querySelector("input"))&&u.addEventListener("click",function(a){f(!!u.checked)});else if(u=e.querySelector("input"))u.addEventListener("blur",function(a){this.focus()}),p=void 0!==a.properties[b]?a.properties[b]:"",p=JSON.stringify(p),u.value=p,u.addEventListener("keydown",function(a){13==a.keyCode&&(k(),a.preventDefault(),a.stopPropagation())});e.querySelector("button").addEventListener("click",k)}};e.prototype.createDialog= -function(a,b){b=b||{};var d=document.createElement("div");d.className="graphdialog";d.innerHTML=a;var k=this.canvas.getBoundingClientRect(),f=-20,h=-20;k&&(f-=k.left,h-=k.top);b.position?(f+=b.position[0],h+=b.position[1]):b.event?(f+=b.event.clientX,h+=b.event.clientY):(f+=0.5*this.canvas.width,h+=0.5*this.canvas.height);d.style.left=f+"px";d.style.top=h+"px";this.canvas.parentNode.appendChild(d);d.close=function(){this.parentNode&&this.parentNode.removeChild(this)};return d};e.onMenuNodeCollapse= -function(a,b,d,k,f){f.collapse()};e.onMenuNodePin=function(a,b,d,k,f){f.pin()};e.onMenuNodeMode=function(a,b,d,k,f){new g.ContextMenu(["Always","On Event","On Trigger","Never"],{event:d,callback:function(a){if(f)switch(a){case "On Event":f.mode=g.ON_EVENT;break;case "On Trigger":f.mode=g.ON_TRIGGER;break;case "Never":f.mode=g.NEVER;break;default:f.mode=g.ALWAYS}},parentMenu:k,node:f});return!1};e.onMenuNodeColors=function(a,b,d,k,f){if(!f)throw"no node for color";b=[];b.push({value:null,content:"No color"}); -for(var h in e.node_colors)a=e.node_colors[h],a={value:h,content:""+h+""},b.push(a);new g.ContextMenu(b,{event:d,callback:function(a){f&&((a=a.value?e.node_colors[a.value]:null)?f.constructor===g.LGraphGroup?f.color=a.groupcolor:(f.color=a.color,f.bgcolor=a.bgcolor):(delete f.color,delete f.bgcolor),f.setDirtyCanvas(!0,!0))},parentMenu:k,node:f});return!1};e.onMenuNodeShapes= -function(a,b,d,k,f){if(!f)throw"no node passed";new g.ContextMenu(g.VALID_SHAPES,{event:d,callback:function(a){f&&(f.shape=a,f.setDirtyCanvas(!0))},parentMenu:k,node:f});return!1};e.onMenuNodeRemove=function(a,b,d,k,f){if(!f)throw"no node passed";!1!==f.removable&&(f.graph.remove(f),f.setDirtyCanvas(!0,!0))};e.onMenuNodeClone=function(a,b,d,k,f){!1!=f.clonable&&(a=f.clone())&&(a.pos=[f.pos[0]+5,f.pos[1]+5],f.graph.add(a),f.setDirtyCanvas(!0,!0))};e.node_colors={red:{color:"#322",bgcolor:"#533",groupcolor:"#A88"}, -brown:{color:"#332922",bgcolor:"#593930",groupcolor:"#b06634"},green:{color:"#232",bgcolor:"#353",groupcolor:"#8A8"},blue:{color:"#223",bgcolor:"#335",groupcolor:"#88A"},pale_blue:{color:"#2a363b",bgcolor:"#3f5159",groupcolor:"#3f789e"},cyan:{color:"#233",bgcolor:"#355",groupcolor:"#8AA"},purple:{color:"#323",bgcolor:"#535",groupcolor:"#a1309b"},yellow:{color:"#432",bgcolor:"#653",groupcolor:"#b58b2a"},black:{color:"#222",bgcolor:"#000",groupcolor:"#444"}};e.prototype.getCanvasMenuOptions=function(){var a= -null;this.getMenuOptions?a=this.getMenuOptions():(a=[{content:"Add Node",has_submenu:!0,callback:e.onMenuAdd},{content:"Add Group",callback:e.onGroupAdd}],this._graph_stack&&0Name",f),g=c.querySelector("input");g&&h&&(g.value=h.label||"");c.querySelector("button").addEventListener("click",function(a){g.value&& -(h&&(h.label=g.value),d.setDirty(!0));c.close()})}},extra:a};a&&(h.title=a.type);var c=null;a&&(c=a.getSlotInPosition(b.canvasX,b.canvasY),e.active_node=a);if(c){f=[];c&&c.output&&c.output.links&&c.output.links.length&&f.push({content:"Disconnect Links",slot:c});var r=c.input||c.output;f.push(r.locked?"Cannot remove":{content:"Remove Slot",slot:c});f.push(r.nameLocked?"Cannot rename":{content:"Rename Slot",slot:c});h.title=(c.input?c.input.type:c.output.type)||"*";c.input&&c.input.type==g.ACTION&& -(h.title="Action");c.output&&c.output.type==g.EVENT&&(h.title="Event")}else a?f=this.getNodeMenuOptions(a):(f=this.getCanvasMenuOptions(),(c=this.graph.getGroupOnPos(b.canvasX,b.canvasY))&&f.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:c,options:this.getGroupMenuOptions(c)}}));f&&new g.ContextMenu(f,h,k)};this.CanvasRenderingContext2D&&(CanvasRenderingContext2D.prototype.roundRect=function(a,b,d,k,f,h){void 0===f&&(f=5);void 0===h&&(h=f);this.moveTo(a+f,b);this.lineTo(a+ -d-f,b);this.quadraticCurveTo(a+d,b,a+d,b+f);this.lineTo(a+d,b+k-h);this.quadraticCurveTo(a+d,b+k,a+d-h,b+k);this.lineTo(a+h,b+k);this.quadraticCurveTo(a,b+k,a,b+k-h);this.lineTo(a,b+f);this.quadraticCurveTo(a,b,a+f,b)});g.compareObjects=function(a,b){for(var d in a)if(a[d]!=b[d])return!1;return!0};g.distance=C;g.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+","+Math.round(255*a[2]).toFixed()+","+(4==a.length?a[3].toFixed(2):"1.0")+")"};g.isInsideRectangle= -z;g.growBounding=function(a,b,d){ba[2]&&(a[2]=b);da[3]&&(a[3]=d)};g.isInsideBounding=function(a,b){return a[0]b[1][0]||a[1]>b[1][1]?!1:!0};g.overlapBounding=v;g.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),d=0,k,f,h=0;6>h;h+=2)k="0123456789ABCDEF".indexOf(a.charAt(h)),f="0123456789ABCDEF".indexOf(a.charAt(h+1)),b[d]=16*k+f,d++;return b};g.num2hex=function(a){for(var b="#",d,k,f=0;3>f;f++)d=a[f]/ -16,k=a[f]%16,b+="0123456789ABCDEF".charAt(d)+"0123456789ABCDEF".charAt(k);return b};D.prototype.addItem=function(a,b,d){function k(a){var b=this.value;b&&b.has_submenu&&f.call(this,a)}function f(a){var b=this.value,f=!0;h.current_submenu&&h.current_submenu.close(a);if(d.callback){var k=d.callback.call(this,b,d,a,h,d.node);!0===k&&(f=!1)}if(b&&(b.callback&&!d.ignore_item_callbacks&&!0!==b.disabled&&(k=b.callback.call(this,b,d,a,h,d.extra),!0===k&&(f=!1)),b.submenu)){if(!b.submenu.options)throw"ContextMenu submenu needs options"; -new h.constructor(b.submenu.options,{callback:b.submenu.callback,event:a,parentMenu:h,ignore_item_callbacks:b.submenu.ignore_item_callbacks,title:b.submenu.title,extra:b.submenu.extra,autoopen:d.autoopen});f=!1}f&&!h.lock&&h.close()}var h=this;d=d||{};var c=document.createElement("div");c.className="litemenu-entry submenu";var g=!1;if(null===b)c.classList.add("separator");else{c.innerHTML=b&&b.title?b.title:a;if(c.value=b)b.disabled&&(g=!0,c.classList.add("disabled")),(b.submenu||b.has_submenu)&& -c.classList.add("has_submenu");"function"==typeof b?(c.dataset.value=a,c.onclick_callback=b):c.dataset.value=b;b.className&&(c.className+=" "+b.className)}this.root.appendChild(c);g||c.addEventListener("click",f);d.autoopen&&c.addEventListener("mouseenter",k);return c};D.prototype.close=function(a,b){this.root.parentNode&&this.root.parentNode.removeChild(this.root);this.parentMenu&&!b&&(this.parentMenu.lock=!1,this.parentMenu.current_submenu=null,void 0===a?this.parentMenu.close():a&&!D.isCursorOverElement(a, -this.parentMenu.root)&&D.trigger(this.parentMenu.root,"mouseleave",a));this.current_submenu&&this.current_submenu.close(a,!0);this.root.closing_timer&&clearTimeout(this.root.closing_timer)};D.trigger=function(a,b,d,k){var f=document.createEvent("CustomEvent");f.initCustomEvent(b,!0,!0,d);f.srcElement=k;a.dispatchEvent?a.dispatchEvent(f):a.__events&&a.__events.dispatchEvent(f);return f};D.prototype.getTopMenu=function(){return this.options.parentMenu?this.options.parentMenu.getTopMenu():this};D.prototype.getFirstEvent= -function(){return this.options.parentMenu?this.options.parentMenu.getFirstEvent():this.options.event};D.isCursorOverElement=function(a,b){var d=a.clientX,k=a.clientY,f=b.getBoundingClientRect();return f?k>f.top&&kf.left&&dMath.abs(d))return k[1];d=(a-k[0])/d;return k[1]*(1-d)+f[1]*d}}return 0}};B.prototype.draw=function(a,b,d,k,f,h){if(d=this.points){this.size=b;var c=b[0]-2*this.margin;b=b[1]-2*this.margin;f=f||"#666";a.save();a.translate(this.margin,this.margin);k&&(a.fillStyle="#111",a.fillRect(0,0,c,b),a.fillStyle="#222",a.fillRect(0.5*c,0,1,b),a.strokeStyle="#333",a.strokeRect(0,0,c,b));a.strokeStyle=f;h&&(a.globalAlpha=0.5);a.beginPath();for(k=0;ka[1])){var k=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,h=a[0]-this.margin,c=a[1]-this.margin;this.selected=this.getCloserPoint([h,c],30/b.ds.scale);-1==this.selected&&(k=[h/k,1-c/f],d.push(k),d.sort(function(a,b){return a[0]- -b[0]}),this.selected=d.indexOf(k),this.must_update=!0);if(-1!=this.selected)return!0}};B.prototype.onMouseMove=function(a,b){var d=this.points;if(d){var k=this.selected;if(!(0>k)){var f=(a[0]-this.margin)/(this.size[0]-2*this.margin),h=(a[1]-this.margin)/(this.size[1]-2*this.margin);this._nearest=this.getCloserPoint([a[0]-this.margin,a[1]-this.margin],30/b.ds.scale);var c=d[k];if(c){var g=0==k||k==d.length-1;!g&&(-10>a[0]||a[0]>this.size[0]+10||-10>a[1]||a[1]>this.size[1]+10)?(d.splice(k,1),this.selected= --1):(c[0]=g?0==k?0:1:Math.clamp(f,0,1),c[1]=1-Math.clamp(h,0,1),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(c),this.must_update=!0)}}}};B.prototype.onMouseUp=function(a,b){this.selected=-1;return!1};B.prototype.getCloserPoint=function(a,b){var d=this.points;if(!d)return-1;b=b||30;for(var k=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,h=d.length,c=[0,0],g=1E6,r=-1,p=0;pg||e>b||(r=p,g=e)}return r};g.CurveEditor= -B;g.getParameterNames=function(a){return(a+"").replace(/[/][/].*$/gm,"").replace(/\s+/g,"").replace(/[/][*][^/*]*[*][/]/g,"").split("){",1)[0].replace(/^[^(]*[(]/,"").replace(/=[^,]+/g,"").split(",").filter(Boolean)};Math.clamp=function(a,b,d){return b>a?b:dthis.size[0]-t.NODE_TITLE_HEIGHT&&0>a[1]){var d=this;setTimeout(function(){b.openSubgraph(d.subgraph)}, -10)}};m.prototype.onAction=function(h,a){this.subgraph.onAction(h,a)};m.prototype.onExecute=function(){if(this.enabled=this.getInputOrProperty("enabled")){if(this.inputs)for(var h=0;h=g?this.trigger(null,e):this._pending.push([g,e])};e.prototype.onExecute=function(){var c=1E3*this.graph.elapsed_time;this.isInputConnected(1)&&(this.properties.time_in_ms=this.getInputData(1));for(var e=0;er&&(r=f.size[0]),l+=f.size[1]+a+e.NODE_TITLE_HEIGHT;b+=r+a}this.setDirtyCanvas(!0,!0)};c.prototype.getTime=function(){return this.globaltime}; +c.prototype.getFixedTime=function(){return this.fixedtime};c.prototype.getElapsedTime=function(){return this.elapsed_time};c.prototype.sendEventToAllNodes=function(a,b,d){d=d||e.ALWAYS;var h=this._nodes_in_order?this._nodes_in_order:this._nodes;if(h)for(var f=0,c=h.length;f=e.MAX_NUMBER_OF_NODES)throw"LiteGraph: max number of nodes in a graph reached"; +null==a.id||-1==a.id?a.id=++this.last_node_id:this.last_node_ida.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});this.id=-1;this.type=null;this.inputs=[];this.outputs=[];this.connections=[];this.properties={};this.properties_info=[];this.flags={}};n.prototype.configure=function(a){this.graph&&this.graph._version++; +for(var b in a)if("properties"==b)for(var d in a.properties){if(this.properties[d]=a.properties[d],this.onPropertyChanged)this.onPropertyChanged(d,a.properties[d])}else null!=a[b]&&("object"==typeof a[b]?this[b]&&this[b].configure?this[b].configure(a[b]):this[b]=e.cloneObject(a[b],this[b]):this[b]=a[b]);a.title||(this.title=this.constructor.title);if(this.onConnectionsChange){if(this.inputs)for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d._data=b,this.outputs[a].links))for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d.type=b,this.outputs[a].links))for(d=0;d=this.inputs.length||null==this.inputs[a].link)){var d=this.graph.links[this.inputs[a].link];if(!d)return null;if(!b)return d.data; +var h=this.graph.getNodeById(d.origin_id);if(!h)return d.data;if(h.updateOutputData)h.updateOutputData(d.origin_slot);else if(h.onExecute)h.onExecute();return d.data}};n.prototype.getInputDataType=function(a){if(!this.inputs||a>=this.inputs.length||null==this.inputs[a].link)return null;a=this.graph.links[this.inputs[a].link];if(!a)return null;var b=this.graph.getNodeById(a.origin_id);return b?(a=b.outputs[a.origin_slot])?a.type:null:a.type};n.prototype.getInputDataByName=function(a,b){var d=this.findInputSlot(a); +return-1==d?null:this.getInputData(d,b)};n.prototype.isInputConnected=function(a){return this.inputs?a=this.inputs.length)return null;a=this.inputs[a];return a&&null!==a.link?(a=this.graph.links[a.link])?this.graph.getNodeById(a.origin_id):null:null};n.prototype.getInputOrProperty=function(a){if(!this.inputs|| +!this.inputs.length)return this.properties?this.properties[a]:null;for(var b=0,d=this.inputs.length;b=this.outputs.length?null:this.outputs[a]._data};n.prototype.getOutputInfo=function(a){return this.outputs?a=this.outputs.length)return null;a=this.outputs[a];if(!a.links||0==a.links.length)return null;for(var b=[],d=0;da&&this.pos[1]-f-db)return!0;return!1};n.prototype.getSlotInPosition=function(a,b){var d=new Float32Array(2); +if(this.inputs)for(var h=0,f=this.inputs.length;h=this.outputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),null;b&&b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"target node is null";if(b==this)return null;if(d.constructor===String){if(d=b.findInputSlot(d),-1==d)return e.debug&&console.log("Connect: Error, no slot of name "+d),null}else{if(d===e.EVENT)return null;if(!b.inputs||d>=b.inputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),null}null!=b.inputs[d].link&& +b.disconnectInput(d);var h=this.outputs[a];if(b.onConnectInput&&!1===b.onConnectInput(d,h.type,h))return null;var f=b.inputs[d],c=null;if(e.isValidConnection(h.type,f.type)){c=new m(++this.graph.last_link_id,f.type,this.id,a,b.id,d);this.graph.links[c.id]=c;null==h.links&&(h.links=[]);h.links.push(c.id);b.inputs[d].link=c.id;this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(e.OUTPUT,a,!0,c,h);if(b.onConnectionsChange)b.onConnectionsChange(e.INPUT,d,!0,c,f);this.graph&& +this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(e.INPUT,b,d,this,a),this.graph.onNodeConnectionChange(e.OUTPUT,this,a,b,d))}this.setDirtyCanvas(!1,!0);this.graph.connectionChange(this,c);return c};n.prototype.disconnectOutput=function(a,b){if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return e.debug&&console.log("Connect: Error, no slot of name "+a),!1}else if(!this.outputs||a>=this.outputs.length)return e.debug&&console.log("Connect: Error, slot number not found"), +!1;var d=this.outputs[a];if(!d||!d.links||0==d.links.length)return!1;if(b){b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"Target Node not found";for(var h=0,f=d.links.length;h=this.inputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),!1;var b=this.inputs[a];if(!b)return!1;var d=this.inputs[a].link;this.inputs[a].link=null;var h=this.graph.links[d];if(h){var f=this.graph.getNodeById(h.origin_id);if(!f)return!1;var c=f.outputs[h.origin_slot];if(!c||!c.links||0==c.links.length)return!1;for(var r=0,l=c.links.length;r< +l;r++)if(c.links[r]==d){c.links.splice(r,1);break}delete this.graph.links[d];this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(e.INPUT,a,!1,h,b);if(f.onConnectionsChange)f.onConnectionsChange(e.OUTPUT,r,!1,h,c);this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(e.OUTPUT,f,r),this.graph.onNodeConnectionChange(e.INPUT,this,a))}this.setDirtyCanvas(!1,!0);this.graph.connectionChange(this);return!0};n.prototype.getConnectionPos=function(a, +b,d){d=d||new Float32Array(2);var h=0;a&&this.inputs&&(h=this.inputs.length);!a&&this.outputs&&(h=this.outputs.length);var f=0.5*e.NODE_SLOT_HEIGHT;if(this.flags.collapsed)return b=this._collapsed_width||e.NODE_COLLAPSED_WIDTH,this.horizontal?(d[0]=this.pos[0]+0.5*b,d[1]=a?this.pos[1]-e.NODE_TITLE_HEIGHT:this.pos[1]):(d[0]=a?this.pos[0]:this.pos[0]+b,d[1]=this.pos[1]-0.5*e.NODE_TITLE_HEIGHT),d;if(a&&-1==b)return d[0]=this.pos[0]+0.5*e.NODE_TITLE_HEIGHT,d[1]=this.pos[1]+0.5*e.NODE_TITLE_HEIGHT,d;if(a&& +h>b&&this.inputs[b].pos)return d[0]=this.pos[0]+this.inputs[b].pos[0],d[1]=this.pos[1]+this.inputs[b].pos[1],d;if(!a&&h>b&&this.outputs[b].pos)return d[0]=this.pos[0]+this.outputs[b].pos[0],d[1]=this.pos[1]+this.outputs[b].pos[1],d;if(this.horizontal)return d[0]=this.pos[0]+this.size[0]/h*(b+0.5),d[1]=a?this.pos[1]-e.NODE_TITLE_HEIGHT:this.pos[1]+this.size[1],d;d[0]=a?this.pos[0]+f:this.pos[0]+this.size[0]+1-f;d[1]=this.pos[1]+(b+0.7)*e.NODE_SLOT_HEIGHT+(this.constructor.slot_start_y||0);return d}; +n.prototype.alignToGrid=function(){this.pos[0]=e.CANVAS_GRID_SIZE*Math.round(this.pos[0]/e.CANVAS_GRID_SIZE);this.pos[1]=e.CANVAS_GRID_SIZE*Math.round(this.pos[1]/e.CANVAS_GRID_SIZE)};n.prototype.trace=function(a){this.console||(this.console=[]);this.console.push(a);this.console.length>n.MAX_CONSOLE&&this.console.shift();this.graph.onNodeTrace(this,a)};n.prototype.setDirtyCanvas=function(a,b){this.graph&&this.graph.sendActionToCanvas("setDirty",[a,b])};n.prototype.loadImage=function(a){var b=new Image; +b.src=e.node_images_path+a;b.ready=!1;var d=this;b.onload=function(){this.ready=!0;d.setDirtyCanvas(!0)};return b};n.prototype.captureInput=function(a){if(this.graph&&this.graph.list_of_graphcanvas)for(var b=this.graph.list_of_graphcanvas,d=0;da.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});Object.defineProperty(this,"size",{set:function(a){!a||2>a.length||(this._size[0]=Math.max(140,a[0]),this._size[1]=Math.max(80,a[1]))},get:function(){return this._size},enumerable:!0})};k.prototype.configure=function(a){this.title=a.title;this._bounding.set(a.bounding);this.color=a.color; +this.font=a.font};k.prototype.serialize=function(){var a=this._bounding;return{title:this.title,bounding:[Math.round(a[0]),Math.round(a[1]),Math.round(a[2]),Math.round(a[3])],color:this.color,font:this.font}};k.prototype.move=function(a,b,d){this._pos[0]+=a;this._pos[1]+=b;if(!d)for(d=0;dthis.max_scale&&(a=this.max_scale);if(a!=this.scale&&this.element){var d=this.element.getBoundingClientRect();if(d){b=b||[0.5*d.width,0.5*d.height];d=this.convertCanvasToOffset(b);this.scale=a;0.01>Math.abs(this.scale-1)&&(this.scale=1);var h=this.convertCanvasToOffset(b),d=[h[0]-d[0],h[1]-d[1]];this.offset[0]+=d[0];this.offset[1]+=d[1];if(this.onredraw)this.onredraw(this)}}}; +x.prototype.changeDeltaScale=function(a,b){this.changeScale(this.scale*a,b)};x.prototype.reset=function(){this.scale=1;this.offset[0]=0;this.offset[1]=0};y.LGraphCanvas=e.LGraphCanvas=g;g.link_type_colors={"-1":e.EVENT_LINK_COLOR,number:"#AAA",node:"#DCA"};g.gradients={};g.prototype.clear=function(){this.fps=this.render_time=this.last_draw_time=this.frame=0;this.dragging_rectangle=null;this.selected_nodes={};this.selected_group=null;this.visible_nodes=[];this.connecting_node=this.node_capturing_input= +this.node_over=this.node_dragged=null;this.highlighted_links={};this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_widget=this.node_in_panel=this.dirty_area=null;this.last_mouse=[0,0];this.last_mouseclick=0;this.visible_area.set([0,0,0,0]);if(this.onClear)this.onClear()};g.prototype.setGraph=function(a,b){this.graph!=a&&(b||this.clear(),!a&&this.graph?this.graph.detachCanvas(this):(a.attachCanvas(this),this.setDirty(!0,!0)))};g.prototype.openSubgraph=function(a){if(!a)throw"graph cannot be null"; +if(this.graph==a)throw"graph cannot be the same";this.clear();this.graph&&(this._graph_stack||(this._graph_stack=[]),this._graph_stack.push(this.graph));a.attachCanvas(this);this.setDirty(!0,!0)};g.prototype.closeSubgraph=function(){if(this._graph_stack&&0!=this._graph_stack.length){var a=this.graph._subgraph_node,b=this._graph_stack.pop();this.selected_nodes={};this.highlighted_links={};b.attachCanvas(this);this.setDirty(!0,!0);a&&(this.centerOnNode(a),this.selectNodes([a]))}};g.prototype.getCurrentGraph= +function(){return this.graph};g.prototype.setCanvas=function(a,b){if(a&&a.constructor===String&&(a=document.getElementById(a),!a))throw"Error creating LiteGraph canvas: Canvas not found";if(a!==this.canvas&&(!a&&this.canvas&&(b||this.unbindEvents()),this.canvas=a,this.ds.element=a)){a.className+=" lgraphcanvas";a.data=this;a.tabindex="1";this.bgcanvas=null;this.bgcanvas||(this.bgcanvas=document.createElement("canvas"),this.bgcanvas.width=this.canvas.width,this.bgcanvas.height=this.canvas.height); +if(null==a.getContext){if("canvas"!=a.localName)throw"Element supplied for LGraphCanvas must be a element, you passed a "+a.localName;throw"This browser doesn't support Canvas";}null==(this.ctx=a.getContext("2d"))&&(a.webgl_enabled||console.warn("This canvas seems to be WebGL, enabling WebGL renderer"),this.enableWebGL());this._mousemove_callback=this.processMouseMove.bind(this);this._mouseup_callback=this.processMouseUp.bind(this);b||this.bindEvents()}};g.prototype._doNothing=function(a){a.preventDefault(); +return!1};g.prototype._doReturnTrue=function(a){a.preventDefault();return!0};g.prototype.bindEvents=function(){if(this._events_binded)console.warn("LGraphCanvas: events already binded");else{var a=this.canvas,b=this.getCanvasWindow().document;this._mousedown_callback=this.processMouseDown.bind(this);this._mousewheel_callback=this.processMouseWheel.bind(this);a.addEventListener("mousedown",this._mousedown_callback,!0);a.addEventListener("mousemove",this._mousemove_callback);a.addEventListener("mousewheel", +this._mousewheel_callback,!1);a.addEventListener("contextmenu",this._doNothing);a.addEventListener("DOMMouseScroll",this._mousewheel_callback,!1);a.addEventListener("touchstart",this.touchHandler,!0);a.addEventListener("touchmove",this.touchHandler,!0);a.addEventListener("touchend",this.touchHandler,!0);a.addEventListener("touchcancel",this.touchHandler,!0);this._key_callback=this.processKey.bind(this);a.addEventListener("keydown",this._key_callback,!0);b.addEventListener("keyup",this._key_callback, +!0);this._ondrop_callback=this.processDrop.bind(this);a.addEventListener("dragover",this._doNothing,!1);a.addEventListener("dragend",this._doNothing,!1);a.addEventListener("drop",this._ondrop_callback,!1);a.addEventListener("dragenter",this._doReturnTrue,!1);this._events_binded=!0}};g.prototype.unbindEvents=function(){if(this._events_binded){var a=this.getCanvasWindow().document;this.canvas.removeEventListener("mousedown",this._mousedown_callback);this.canvas.removeEventListener("mousewheel",this._mousewheel_callback); +this.canvas.removeEventListener("DOMMouseScroll",this._mousewheel_callback);this.canvas.removeEventListener("keydown",this._key_callback);a.removeEventListener("keyup",this._key_callback);this.canvas.removeEventListener("contextmenu",this._doNothing);this.canvas.removeEventListener("drop",this._ondrop_callback);this.canvas.removeEventListener("dragenter",this._doReturnTrue);this.canvas.removeEventListener("touchstart",this.touchHandler);this.canvas.removeEventListener("touchmove",this.touchHandler); +this.canvas.removeEventListener("touchend",this.touchHandler);this.canvas.removeEventListener("touchcancel",this.touchHandler);this._ondrop_callback=this._key_callback=this._mousewheel_callback=this._mousedown_callback=null;this._events_binded=!1}else console.warn("LGraphCanvas: no events binded")};g.getFileExtension=function(a){var b=a.indexOf("?");-1!=b&&(a=a.substr(0,b));b=a.lastIndexOf(".");return-1==b?"":a.substr(b+1).toLowerCase()};g.prototype.enableWebGL=function(){if(void 0===typeof GL)throw"litegl.js must be included to use a WebGL canvas"; +if(void 0===typeof enableWebGLCanvas)throw"webglCanvas.js must be included to use this feature";this.gl=this.ctx=enableWebGLCanvas(this.canvas);this.ctx.webgl=!0;this.bgcanvas=this.canvas;this.bgctx=this.gl;this.canvas.webgl_enabled=!0};g.prototype.setDirty=function(a,b){a&&(this.dirty_canvas=!0);b&&(this.dirty_bgcanvas=!0)};g.prototype.getCanvasWindow=function(){if(!this.canvas)return window;var a=this.canvas.ownerDocument;return a.defaultView||a.parentWindow};g.prototype.startRendering=function(){function a(){this.pause_rendering|| +this.draw();var b=this.getCanvasWindow();this.is_rendering&&b.requestAnimationFrame(a.bind(this))}this.is_rendering||(this.is_rendering=!0,a.call(this))};g.prototype.stopRendering=function(){this.is_rendering=!1};g.prototype.processMouseDown=function(a){if(this.graph){this.adjustMouseEvent(a);var b=this.getCanvasWindow();g.active_canvas=this;this.canvas.removeEventListener("mousemove",this._mousemove_callback);b.document.addEventListener("mousemove",this._mousemove_callback,!0);b.document.addEventListener("mouseup", +this._mouseup_callback,!0);var d=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes,5),h=!1,f=300>e.getTime()-this.last_mouseclick;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;this.canvas.focus();e.closeAllContextMenus(b);if(!this.onMouse||!0!=this.onMouse(a)){if(1==a.which){a.ctrlKey&&(this.dragging_rectangle=new Float32Array(4),this.dragging_rectangle[0]=a.canvasX,this.dragging_rectangle[1]=a.canvasY,this.dragging_rectangle[2]=1,this.dragging_rectangle[3]=1,h=!0);var c= +!1;if(d&&this.allow_interaction&&!h&&!this.read_only){this.live_mode||d.flags.pinned||this.bringToFront(d);if(!this.connecting_node&&!d.flags.collapsed&&!this.live_mode)if(!h&&!1!==d.resizable&&z(a.canvasX,a.canvasY,d.pos[0]+d.size[0]-5,d.pos[1]+d.size[1]-5,10,10))this.resizing_node=d,this.canvas.style.cursor="se-resize",h=!0;else{if(d.outputs)for(var r=0,l=d.outputs.length;rc[0]+4||a.canvasYc[1]+4)){this.showLinkMenu(d, +a);this.over_link_center=null;break}this.selected_group=this.graph.getGroupOnPos(a.canvasX,a.canvasY);this.selected_group_resizing=!1;this.selected_group&&!this.read_only&&(a.ctrlKey&&(this.dragging_rectangle=null),10>C([a.canvasX,a.canvasY],[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])*this.ds.scale?this.selected_group_resizing=!0:this.selected_group.recomputeInsideNodes());f&&!this.read_only&&this.allow_searchbox&&this.showSearchBox(a); +c=!0}!h&&c&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}else 2!=a.which&&3==a.which&&(this.read_only||this.processContextMenu(d,a));this.last_mouse[0]=a.localX;this.last_mouse[1]=a.localY;this.last_mouseclick=e.getTime();this.last_mouse_dragging=!0;this.graph.change();(!b.document.activeElement||"input"!=b.document.activeElement.nodeName.toLowerCase()&&"textarea"!=b.document.activeElement.nodeName.toLowerCase())&&a.preventDefault();a.stopPropagation();if(this.onMouseDown)this.onMouseDown(a); +return!1}}};g.prototype.processMouseMove=function(a){this.autoresize&&this.resize();if(this.graph){g.active_canvas=this;this.adjustMouseEvent(a);var b=[a.localX,a.localY],d=[b[0]-this.last_mouse[0],b[1]-this.last_mouse[1]];this.last_mouse=b;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;a.dragging=this.last_mouse_dragging;this.node_widget&&(this.processNodeWidgets(this.node_widget[0],this.canvas_mouse,a,this.node_widget[1]),this.dirty_canvas=!0);if(this.dragging_rectangle)this.dragging_rectangle[2]= +a.canvasX-this.dragging_rectangle[0],this.dragging_rectangle[3]=a.canvasY-this.dragging_rectangle[1],this.dirty_canvas=!0;else if(this.selected_group&&!this.read_only)this.selected_group_resizing?this.selected_group.size=[a.canvasX-this.selected_group.pos[0],a.canvasY-this.selected_group.pos[1]]:(this.selected_group.move(d[0]/this.ds.scale,d[1]/this.ds.scale,a.ctrlKey),this.selected_group._nodes.length&&(this.dirty_canvas=!0)),this.dirty_bgcanvas=!0;else if(this.dragging_canvas)this.ds.offset[0]+= +d[0]/this.ds.scale,this.ds.offset[1]+=d[1]/this.ds.scale,this.dirty_bgcanvas=this.dirty_canvas=!0;else if(this.allow_interaction&&!this.read_only){this.connecting_node&&(this.dirty_canvas=!0);for(var h=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes),b=0,f=this.graph._nodes.length;br[0]+4||a.canvasYr[1]+4)){f=c;break}}f!=this.over_link_center&&(this.over_link_center=f,this.dirty_canvas=!0);this.canvas&&(this.canvas.style.cursor="")}if(this.node_capturing_input&&this.node_capturing_input!=h&&this.node_capturing_input.onMouseMove)this.node_capturing_input.onMouseMove(a, +[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]],this);if(this.node_dragged&&!this.live_mode){for(b in this.selected_nodes)h=this.selected_nodes[b],h.pos[0]+=d[0]/this.ds.scale,h.pos[1]+=d[1]/this.ds.scale;this.dirty_bgcanvas=this.dirty_canvas=!0}this.resizing_node&&!this.live_mode&&(this.resizing_node.size[0]=a.canvasX-this.resizing_node.pos[0],this.resizing_node.size[1]=a.canvasY-this.resizing_node.pos[1],d=Math.max(this.resizing_node.inputs?this.resizing_node.inputs.length: +0,this.resizing_node.outputs?this.resizing_node.outputs.length:0)*e.NODE_SLOT_HEIGHT+(this.resizing_node.widgets?this.resizing_node.widgets.length:0)*(e.NODE_WIDGET_HEIGHT+4)+4,this.resizing_node.size[1]this.dragging_rectangle[3]?this.dragging_rectangle[1]-f:this.dragging_rectangle[1];this.dragging_rectangle[0]=0>this.dragging_rectangle[2]?this.dragging_rectangle[0]-h:this.dragging_rectangle[0];this.dragging_rectangle[1]=c;this.dragging_rectangle[2]=h;this.dragging_rectangle[3]=f;f=[];for(c=0;ca.click_time&&z(a.canvasX,a.canvasY,h.pos[0],h.pos[1]-e.NODE_TITLE_HEIGHT,e.NODE_TITLE_HEIGHT,e.NODE_TITLE_HEIGHT)&&h.collapse();this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_dragged.pos[0]=Math.round(this.node_dragged.pos[0]); +this.node_dragged.pos[1]=Math.round(this.node_dragged.pos[1]);this.graph.config.align_to_grid&&this.node_dragged.alignToGrid();if(this.onNodeMoved)this.onNodeMoved(this.node_dragged);this.node_dragged=null}else{h=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes);!h&&300>a.click_time&&this.deselectAllNodes();this.dirty_canvas=!0;this.dragging_canvas=!1;if(this.node_over&&this.node_over.onMouseUp)this.node_over.onMouseUp(a,[a.canvasX-this.node_over.pos[0],a.canvasY-this.node_over.pos[1]], +this);if(this.node_capturing_input&&this.node_capturing_input.onMouseUp)this.node_capturing_input.onMouseUp(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]])}}else 2==a.which?(this.dirty_canvas=!0,this.dragging_canvas=!1):3==a.which&&(this.dirty_canvas=!0,this.dragging_canvas=!1);this.graph.change();a.stopPropagation();a.preventDefault();return!1}};g.prototype.processMouseWheel=function(a){if(this.graph&&this.allow_dragcanvas){var b=null!=a.wheelDeltaY?a.wheelDeltaY: +-60*a.detail;this.adjustMouseEvent(a);var d=this.ds.scale;0b&&(d*=1/1.1);this.ds.changeScale(d,[a.localX,a.localY]);this.graph.change();a.preventDefault();return!1}};g.prototype.isOverNodeBox=function(a,b,d){var h=e.NODE_TITLE_HEIGHT;return z(b,d,a.pos[0]+2,a.pos[1]+2-h,h-4,h-4)?!0:!1};g.prototype.isOverNodeInput=function(a,b,d,h){if(a.inputs)for(var f=0,e=a.inputs.length;fd-this.graph._last_trigger_time)&&this.drawBackCanvas();(this.dirty_canvas||a)&&this.drawFrontCanvas();this.fps=this.render_time?1/this.render_time:0;this.frame+=1}};g.prototype.drawFrontCanvas=function(){this.dirty_canvas=!1;this.ctx||(this.ctx=this.bgcanvas.getContext("2d"));var a=this.ctx;if(a){a.start2D&&a.start2D();var b=this.canvas;a.restore();a.setTransform(1,0,0,1,0,0);this.dirty_area&&(a.save(), +a.beginPath(),a.rect(this.dirty_area[0],this.dirty_area[1],this.dirty_area[2],this.dirty_area[3]),a.clip());this.clear_background&&a.clearRect(0,0,b.width,b.height);this.bgcanvas==this.canvas?this.drawBackCanvas():a.drawImage(this.bgcanvas,0,0);if(this.onRender)this.onRender(b,a);this.show_info&&this.renderInfo(a);if(this.graph){a.save();this.ds.toCanvasContext(a);for(var b=this.computeVisibleNodes(null,this.visible_nodes),d=0;d> ";b.fillText(h+d.getTitle(), +0.5*a.width,40);b.restore()}d=!1;this.onRenderBackground&&(d=this.onRenderBackground(a,b));b.restore();b.setTransform(1,0,0,1,0,0);this.visible_links.length=0;if(this.graph){b.save();this.ds.toCanvasContext(b);if(this.background_image&&0.5this.ds.scale;if(this.live_mode){if(!a.flags.collapsed&&(b.shadowColor="transparent",a.onDrawForeground))a.onDrawForeground(b,this,this.canvas)}else{var c= +this.editor_alpha;b.globalAlpha=c;this.render_shadows&&!f?(b.shadowColor=e.DEFAULT_SHADOW_COLOR,b.shadowOffsetX=2*this.ds.scale,b.shadowOffsetY=2*this.ds.scale,b.shadowBlur=3*this.ds.scale):b.shadowColor="transparent";if(!a.flags.collapsed||!a.onDrawCollapsed||!0!=a.onDrawCollapsed(b,this)){var r=a._shape||e.BOX_SHAPE;t.set(a.size);var l=a.horizontal;if(a.flags.collapsed){b.font=this.inner_text_font;var g=a.getTitle?a.getTitle():a.title;null!=g&&(a._collapsed_width=Math.min(a.size[0],b.measureText(g).width+ +2*e.NODE_TITLE_HEIGHT),t[0]=a._collapsed_width,t[1]=0)}a.clip_area&&(b.save(),b.beginPath(),r==e.BOX_SHAPE?b.rect(0,0,t[0],t[1]):r==e.ROUND_SHAPE?b.roundRect(0,0,t[0],t[1],10):r==e.CIRCLE_SHAPE&&b.arc(0.5*t[0],0.5*t[1],0.5*t[0],0,2*Math.PI),b.clip());a.has_errors&&(h="red");this.drawNodeShape(a,b,t,d,h,a.is_selected,a.mouseOver);b.shadowColor="transparent";if(a.onDrawForeground)a.onDrawForeground(b,this,this.canvas);b.textAlign=l?"center":"left";b.font=this.inner_text_font;h=!f;r=this.connecting_output; +b.lineWidth=1;var g=0,q=new Float32Array(2);if(!a.flags.collapsed){if(a.inputs)for(d=0;dthis.ds.scale,p=a._shape||a.constructor.shape||e.ROUND_SHAPE,q=a.constructor.title_mode,s=!0;q==e.TRANSPARENT_TITLE?s=!1:q==e.AUTOHIDE_TITLE&&r&&(s= +!0);A[0]=0;A[1]=s?-f:0;A[2]=d[0]+1;A[3]=s?d[1]+f:d[1];r=b.globalAlpha;b.beginPath();p==e.BOX_SHAPE||l?b.fillRect(A[0],A[1],A[2],A[3]):p==e.ROUND_SHAPE||p==e.CARD_SHAPE?b.roundRect(A[0],A[1],A[2],A[3],this.round_radius,p==e.CARD_SHAPE?0:this.round_radius):p==e.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0],0,2*Math.PI);b.fill();a.flags.collapsed||(b.shadowColor="transparent",b.fillStyle="rgba(0,0,0,0.2)",b.fillRect(0,-1,A[2],2));b.shadowColor="transparent";if(a.onDrawBackground)a.onDrawBackground(b, +this,this.canvas);if(s||q==e.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,f,d,this.ds.scale,h);else if(q!=e.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){s=a.constructor.title_color||h;a.flags.collapsed&&(b.shadowColor=e.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var u=g.gradients[s];u||(u=g.gradients[s]=b.createLinearGradient(0,0,400,0),u.addColorStop(0,s),u.addColorStop(1,"#000"));b.fillStyle=u}else b.fillStyle=s;b.beginPath();p==e.BOX_SHAPE||l?b.rect(0, +-f,d[0]+1,f):p!=e.ROUND_SHAPE&&p!=e.CARD_SHAPE||b.roundRect(0,-f,d[0]+1,f,this.round_radius,a.flags.collapsed?this.round_radius:0);b.fill();b.shadowColor="transparent"}if(a.onDrawTitleBox)a.onDrawTitleBox(b,f,d,this.ds.scale);else p==e.ROUND_SHAPE||p==e.CIRCLE_SHAPE||p==e.CARD_SHAPE?(l&&(b.fillStyle="black",b.beginPath(),b.arc(0.5*f,-0.5*f,6,0,2*Math.PI),b.fill()),b.fillStyle=a.boxcolor||e.NODE_DEFAULT_BOXCOLOR,l?b.fillRect(0.5*f-5,-0.5*f-5,10,10):(b.beginPath(),b.arc(0.5*f,-0.5*f,5,0,2*Math.PI), +b.fill())):(l&&(b.fillStyle="black",b.fillRect(0.5*(f-10)-1,-0.5*(f+10)-1,12,12)),b.fillStyle=a.boxcolor||e.NODE_DEFAULT_BOXCOLOR,b.fillRect(0.5*(f-10),-0.5*(f+10),10,10));b.globalAlpha=r;if(a.onDrawTitleText)a.onDrawTitleText(b,f,d,this.ds.scale,this.title_text_font,c);!l&&(b.font=this.title_text_font,l=a.getTitle())&&(b.fillStyle=c?"white":a.constructor.title_text_color||this.node_title_color,a.flags.collapsed?(b.textAlign="center",r=b.measureText(l),b.fillText(l,f+0.5*r.width,e.NODE_TITLE_TEXT_Y- +f),b.textAlign="left"):(b.textAlign="left",b.fillText(l,f,e.NODE_TITLE_TEXT_Y-f)));if(a.onDrawTitle)a.onDrawTitle(b)}if(c){if(a.onBounding)a.onBounding(A);q==e.TRANSPARENT_TITLE&&(A[1]-=f,A[3]+=f);b.lineWidth=1;b.globalAlpha=0.8;b.beginPath();p==e.BOX_SHAPE?b.rect(-6+A[0],-6+A[1],12+A[2],12+A[3]):p==e.ROUND_SHAPE||p==e.CARD_SHAPE&&a.flags.collapsed?b.roundRect(-6+A[0],-6+A[1],12+A[2],12+A[3],2*this.round_radius):p==e.CARD_SHAPE?b.roundRect(-6+A[0],-6+A[1],12+A[2],12+A[3],2*this.round_radius,2):p== +e.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0]+6,0,2*Math.PI);b.strokeStyle="#FFF";b.stroke();b.strokeStyle=h;b.globalAlpha=1}};var p=new Float32Array(4),s=new Float32Array(4),w=new Float32Array(2),l=new Float32Array(2);g.prototype.drawConnections=function(a){var b=e.getTime(),d=this.visible_area;p[0]=d[0]-20;p[1]=d[1]-20;p[2]=d[2]+40;p[3]=d[3]+40;a.lineWidth=this.connections_width;a.fillStyle="#AAA";a.strokeStyle="#AAA";a.globalAlpha=this.editor_alpha;for(var d=this.graph._nodes,h=0,f=d.length;h< +f;++h){var c=d[h];if(c.inputs&&c.inputs.length)for(var r=0;rs[2]&&(s[0]+=s[2],s[2]=Math.abs(s[2]));0>s[3]&&(s[1]+=s[3],s[3]=Math.abs(s[3]));if(v(s,p)){var n=q.outputs[t],t=c.inputs[r];if(n&& +t&&(q=n.dir||(q.horizontal?e.DOWN:e.RIGHT),t=t.dir||(c.horizontal?e.UP:e.LEFT),this.renderLink(a,k,u,g,!1,0,null,q,t),g&&g._last_time&&1E3>b-g._last_time)){var n=2-0.002*(b-g._last_time),J=a.globalAlpha;a.globalAlpha=J*n;this.renderLink(a,k,u,g,!0,n,"white",q,t);a.globalAlpha=J}}}}}}a.globalAlpha=1};g.prototype.renderLink=function(a,b,d,h,f,c,r,l,p,q){h&&this.visible_links.push(h);!r&&h&&(r=h.color||g.link_type_colors[h.type]);r||(r=this.default_link_color);null!=h&&this.highlighted_links[h.id]&& +(r="#FFF");l=l||e.RIGHT;p=p||e.LEFT;var s=C(b,d);this.render_connections_border&&0.6b[1]?0:Math.PI,a.save(),a.translate(u[0],u[1]),a.rotate(t),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore(),a.save(),a.translate(q[0],q[1]),a.rotate(k), +a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill());if(c)for(a.fillStyle=r,u=0;5>u;++u)c=(0.001*e.getTime()+0.2*u)%1,f=this.computeConnectionPoint(b,d,c,l,p),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill()};g.prototype.computeConnectionPoint=function(a,b,d,h,f){h=h||e.RIGHT;f=f||e.LEFT;var c=C(a,b),r=[a[0],a[1]],l=[b[0],b[1]];switch(h){case e.LEFT:r[0]+=-0.25*c;break;case e.RIGHT:r[0]+=0.25*c;break;case e.UP:r[1]+= +-0.25*c;break;case e.DOWN:r[1]+=0.25*c}switch(f){case e.LEFT:l[0]+=-0.25*c;break;case e.RIGHT:l[0]+=0.25*c;break;case e.UP:l[1]+=-0.25*c;break;case e.DOWN:l[1]+=0.25*c}h=(1-d)*(1-d)*(1-d);f=3*(1-d)*(1-d)*d;c=3*(1-d)*d*d;d*=d*d;return[h*a[0]+f*r[0]+c*l[0]+d*b[0],h*a[1]+f*r[1]+c*l[1]+d*b[1]]};g.prototype.drawExecutionOrder=function(a){a.shadowColor="transparent";a.globalAlpha=0.25;a.textAlign="center";a.strokeStyle="white";a.globalAlpha=0.75;for(var b=this.visible_nodes,d=0;du.last_y&&ru.options.max&&(u.value=u.options.max);else if("mousedown"==d.type)if((r=u.options.values)&&r.constructor===Function&&(r= +u.options.values(u,a)),c=40>c?-1:c>l-40?1:0,"number"==u.type)u.value+=0.1*c*(u.options.step||1),null!=u.options.min&&u.valueu.options.max&&(u.value=u.options.max);else if(c){var g=r.constructor===Array?r:Object.keys(r),s=g.indexOf(u.value)+c;s>=r.length&&(s=0);0>s&&(s=g.length-1);u.value=r.constructor===Array?r[s]:r[g[s]]}else new e.ContextMenu(r,{scale:Math.max(1,this.ds.scale),event:d,className:"dark",callback:s.bind(u)},g),s= +function(a,b,d){this.value=a;f(this,a);p.dirty_canvas=!0;return!1};else"mouseup"==d.type&&"number"==u.type&&(c=40>c?-1:c>l-40?1:0,200>d.click_time&&0==c&&this.prompt("Value",u.value,function(a){this.value=Number(a);f(this,this.value)}.bind(u),d));h!=u.value&&setTimeout(function(){f(this,this.value)}.bind(u),20);this.dirty_canvas=!0;break;case "toggle":"mousedown"==d.type&&(u.value=!u.value,setTimeout(function(){f(u,u.value)},20));break;case "string":case "text":"mousedown"==d.type&&this.prompt("Value", +u.value,function(a){this.value=a;f(this,a)}.bind(u),d);break;default:u.mouse&&u.mouse(ctx,d,[c,r],a)}return u}}return null};g.prototype.drawGroups=function(a,b){if(this.graph){var d=this.graph._groups;b.save();b.globalAlpha=0.5*this.editor_alpha;for(var h=0;hd&&0.01>b.editor_alpha&&(clearInterval(h),1>d&&(b.live_mode=!0));1"+p+""+a+"",value:p});if(l.length)return new e.ContextMenu(l,{event:d,callback:c,parentMenu:h,allow_html:!0,node:f},b),!1}};g.decodeHTML=function(a){var b=document.createElement("div");b.innerText=a;return b.innerHTML};g.onResizeNode=function(a, +b,d,h,f){f&&(f.size=f.computeSize(),f.setDirtyCanvas(!0,!0))};g.prototype.showLinkMenu=function(a,b){var d=this;console.log(a);var h=new e.ContextMenu(["Add Node",null,"Delete"],{event:b,title:null!=a.data?a.data.constructor.name:null,callback:function(b,c,e){switch(b){case "Add Node":g.onMenuAdd(null,null,e,h,function(b){console.log("node autoconnect");var f=d.graph.getNodeById(a.origin_id),h=d.graph.getNodeById(a.target_id);b.inputs&&b.inputs.length&&b.outputs&&b.outputs.length&&f.outputs[a.origin_slot].type== +b.inputs[0].type&&b.outputs[0].type==h.inputs[0].type&&(f.connect(a.origin_slot,b,0),b.connect(0,h,a.target_slot),b.pos[0]-=0.5*b.size[0])});break;case "Delete":d.graph.removeLink(a.id)}}});return!1};g.onShowPropertyEditor=function(a,b,d,h,f){function e(){var b=p.value;"Number"==a.type?b=Number(b):"Boolean"==a.type&&(b=Boolean(b));f[c]=b;l.parentNode&&l.parentNode.removeChild(l);f.setDirtyCanvas(!0,!0)}var c=a.property||"title";b=f[c];var l=document.createElement("div");l.className="graphdialog"; +l.innerHTML="";l.querySelector(".name").innerText=c;var p=l.querySelector("input");p&&(p.value=b,p.addEventListener("blur",function(a){this.focus()}),p.addEventListener("keydown",function(a){13==a.keyCode&&(e(),a.preventDefault(),a.stopPropagation())}));b=g.active_canvas.canvas;d=b.getBoundingClientRect();var q=h=-20;d&&(h-=d.left,q-=d.top);event?(l.style.left=event.clientX+h+"px",l.style.top=event.clientY+q+ +"px"):(l.style.left=0.5*b.width+h+"px",l.style.top=0.5*b.height+q+"px");l.querySelector("button").addEventListener("click",e);b.parentNode.appendChild(l)};g.prototype.prompt=function(a,b,d,h){var f=this;a=a||"";var c=!1,e=document.createElement("div");e.className="graphdialog rounded";e.innerHTML=" ";e.close=function(){f.prompt_box=null;e.parentNode&&e.parentNode.removeChild(e)};1g.search_limit)break}}q=null;if(Array.prototype.filter)q=Object.keys(e.registered_node_types).filter(s);else for(l in q= +[],e.registered_node_types)s(l)&&q.push(l);for(l=0;lg.search_limit);l++);var s=function(a){var b=e.registered_node_types[a];return r&&b.filter!=r?!1:-1!==a.toLowerCase().indexOf(d)}}}var f=this,c=g.active_canvas,l=c.canvas,p=l.ownerDocument||document,q=document.createElement("div");q.className="litegraph litesearchbox graphdialog rounded";q.innerHTML="Search
"; +q.close=function(){f.search_box=null;p.body.focus();p.body.style.overflow="";setTimeout(function(){f.canvas.focus()},20);q.parentNode&&q.parentNode.removeChild(q)};var s=null;1l.height-200&&(k.style.maxHeight=l.height-a.layerY-20+"px");v.focus();return q};g.prototype.showEditPropertyValue=function(a,b,d){function h(){f(u.value)}function f(f){"number"==typeof a.properties[b]&&(f=Number(f));if("array"==c||"object"==c)f=JSON.parse(f);a.properties[b]=f;a._graph&&a._graph._version++;if(a.onPropertyChanged)a.onPropertyChanged(b, +f);if(d.onclose)d.onclose();g.close();a.setDirtyCanvas(!0,!0)}if(a&&void 0!==a.properties[b]){d=d||{};var e=a.getPropertyInfo(b),c=e.type,l="";if("string"==c||"number"==c||"array"==c||"object"==c)l="";else if("enum"==c&&e.values){var l=""}else if("boolean"== +c)l="";else{console.warn("unknown type: "+c);return}var g=this.createDialog(""+b+""+l+"",d);if("enum"==c&&e.values){var u=g.querySelector("select");u.addEventListener("change",function(a){f(a.target.value)})}else if("boolean"==c)(u=g.querySelector("input"))&&u.addEventListener("click",function(a){f(!!u.checked)});else if(u=g.querySelector("input"))u.addEventListener("blur", +function(a){this.focus()}),p=void 0!==a.properties[b]?a.properties[b]:"",p=JSON.stringify(p),u.value=p,u.addEventListener("keydown",function(a){13==a.keyCode&&(h(),a.preventDefault(),a.stopPropagation())});g.querySelector("button").addEventListener("click",h);return g}};g.prototype.createDialog=function(a,b){b=b||{};var d=document.createElement("div");d.className="graphdialog";d.innerHTML=a;var h=this.canvas.getBoundingClientRect(),f=-20,e=-20;h&&(f-=h.left,e-=h.top);b.position?(f+=b.position[0], +e+=b.position[1]):b.event?(f+=b.event.clientX,e+=b.event.clientY):(f+=0.5*this.canvas.width,e+=0.5*this.canvas.height);d.style.left=f+"px";d.style.top=e+"px";this.canvas.parentNode.appendChild(d);d.close=function(){this.parentNode&&this.parentNode.removeChild(this)};return d};g.onMenuNodeCollapse=function(a,b,d,h,f){f.collapse()};g.onMenuNodePin=function(a,b,d,h,f){f.pin()};g.onMenuNodeMode=function(a,b,d,h,f){new e.ContextMenu(["Always","On Event","On Trigger","Never"],{event:d,callback:function(a){if(f)switch(a){case "On Event":f.mode= +e.ON_EVENT;break;case "On Trigger":f.mode=e.ON_TRIGGER;break;case "Never":f.mode=e.NEVER;break;default:f.mode=e.ALWAYS}},parentMenu:h,node:f});return!1};g.onMenuNodeColors=function(a,b,d,h,f){if(!f)throw"no node for color";b=[];b.push({value:null,content:"No color"});for(var c in g.node_colors)a=g.node_colors[c],a={value:c,content:""+c+""},b.push(a);new e.ContextMenu(b,{event:d,callback:function(a){f&&((a=a.value?g.node_colors[a.value]:null)?f.constructor===e.LGraphGroup?f.color=a.groupcolor:(f.color=a.color,f.bgcolor=a.bgcolor):(delete f.color,delete f.bgcolor),f.setDirtyCanvas(!0,!0))},parentMenu:h,node:f});return!1};g.onMenuNodeShapes=function(a,b,d,h,f){if(!f)throw"no node passed";new e.ContextMenu(e.VALID_SHAPES,{event:d,callback:function(a){f&&(f.shape=a,f.setDirtyCanvas(!0))},parentMenu:h,node:f}); +return!1};g.onMenuNodeRemove=function(a,b,d,h,f){if(!f)throw"no node passed";!1!==f.removable&&(f.graph.remove(f),f.setDirtyCanvas(!0,!0))};g.onMenuNodeClone=function(a,b,d,h,f){!1!=f.clonable&&(a=f.clone())&&(a.pos=[f.pos[0]+5,f.pos[1]+5],f.graph.add(a),f.setDirtyCanvas(!0,!0))};g.node_colors={red:{color:"#322",bgcolor:"#533",groupcolor:"#A88"},brown:{color:"#332922",bgcolor:"#593930",groupcolor:"#b06634"},green:{color:"#232",bgcolor:"#353",groupcolor:"#8A8"},blue:{color:"#223",bgcolor:"#335",groupcolor:"#88A"}, +pale_blue:{color:"#2a363b",bgcolor:"#3f5159",groupcolor:"#3f789e"},cyan:{color:"#233",bgcolor:"#355",groupcolor:"#8AA"},purple:{color:"#323",bgcolor:"#535",groupcolor:"#a1309b"},yellow:{color:"#432",bgcolor:"#653",groupcolor:"#b58b2a"},black:{color:"#222",bgcolor:"#000",groupcolor:"#444"}};g.prototype.getCanvasMenuOptions=function(){var a=null;this.getMenuOptions?a=this.getMenuOptions():(a=[{content:"Add Node",has_submenu:!0,callback:g.onMenuAdd},{content:"Add Group",callback:g.onGroupAdd}],this._graph_stack&& +0Name",f),l=c.querySelector("input");l&&e&&(l.value=e.label||"");c.querySelector("button").addEventListener("click",function(a){l.value&&(e&&(e.label=l.value),d.setDirty(!0));c.close()})}},extra:a};a&&(c.title=a.type);var l=null;a&&(l=a.getSlotInPosition(b.canvasX,b.canvasY),g.active_node= +a);if(l){f=[];l&&l.output&&l.output.links&&l.output.links.length&&f.push({content:"Disconnect Links",slot:l});var q=l.input||l.output;f.push(q.locked?"Cannot remove":{content:"Remove Slot",slot:l});f.push(q.nameLocked?"Cannot rename":{content:"Rename Slot",slot:l});c.title=(l.input?l.input.type:l.output.type)||"*";l.input&&l.input.type==e.ACTION&&(c.title="Action");l.output&&l.output.type==e.EVENT&&(c.title="Event")}else a?f=this.getNodeMenuOptions(a):(f=this.getCanvasMenuOptions(),(l=this.graph.getGroupOnPos(b.canvasX, +b.canvasY))&&f.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:l,options:this.getGroupMenuOptions(l)}}));f&&new e.ContextMenu(f,c,h)};this.CanvasRenderingContext2D&&(CanvasRenderingContext2D.prototype.roundRect=function(a,b,d,h,f,e){void 0===f&&(f=5);void 0===e&&(e=f);this.moveTo(a+f,b);this.lineTo(a+d-f,b);this.quadraticCurveTo(a+d,b,a+d,b+f);this.lineTo(a+d,b+h-e);this.quadraticCurveTo(a+d,b+h,a+d-e,b+h);this.lineTo(a+e,b+h);this.quadraticCurveTo(a,b+h,a,b+h-e);this.lineTo(a, +b+f);this.quadraticCurveTo(a,b,a+f,b)});e.compareObjects=function(a,b){for(var d in a)if(a[d]!=b[d])return!1;return!0};e.distance=C;e.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+","+Math.round(255*a[2]).toFixed()+","+(4==a.length?a[3].toFixed(2):"1.0")+")"};e.isInsideRectangle=z;e.growBounding=function(a,b,d){ba[2]&&(a[2]=b);da[3]&&(a[3]=d)};e.isInsideBounding=function(a,b){return a[0]b[1][0]||a[1]>b[1][1]?!1:!0};e.overlapBounding=v;e.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),d=0,h,f,e=0;6>e;e+=2)h="0123456789ABCDEF".indexOf(a.charAt(e)),f="0123456789ABCDEF".indexOf(a.charAt(e+1)),b[d]=16*h+f,d++;return b};e.num2hex=function(a){for(var b="#",d,h,f=0;3>f;f++)d=a[f]/16,h=a[f]%16,b+="0123456789ABCDEF".charAt(d)+"0123456789ABCDEF".charAt(h);return b};E.prototype.addItem=function(a,b,d){function h(a){var b=this.value;b&&b.has_submenu&& +f.call(this,a)}function f(a){var b=this.value,f=!0;e.current_submenu&&e.current_submenu.close(a);if(d.callback){var h=d.callback.call(this,b,d,a,e,d.node);!0===h&&(f=!1)}if(b&&(b.callback&&!d.ignore_item_callbacks&&!0!==b.disabled&&(h=b.callback.call(this,b,d,a,e,d.extra),!0===h&&(f=!1)),b.submenu)){if(!b.submenu.options)throw"ContextMenu submenu needs options";new e.constructor(b.submenu.options,{callback:b.submenu.callback,event:a,parentMenu:e,ignore_item_callbacks:b.submenu.ignore_item_callbacks, +title:b.submenu.title,extra:b.submenu.extra,autoopen:d.autoopen});f=!1}f&&!e.lock&&e.close()}var e=this;d=d||{};var c=document.createElement("div");c.className="litemenu-entry submenu";var l=!1;if(null===b)c.classList.add("separator");else{c.innerHTML=b&&b.title?b.title:a;if(c.value=b)b.disabled&&(l=!0,c.classList.add("disabled")),(b.submenu||b.has_submenu)&&c.classList.add("has_submenu");"function"==typeof b?(c.dataset.value=a,c.onclick_callback=b):c.dataset.value=b;b.className&&(c.className+=" "+ +b.className)}this.root.appendChild(c);l||c.addEventListener("click",f);d.autoopen&&c.addEventListener("mouseenter",h);return c};E.prototype.close=function(a,b){this.root.parentNode&&this.root.parentNode.removeChild(this.root);this.parentMenu&&!b&&(this.parentMenu.lock=!1,this.parentMenu.current_submenu=null,void 0===a?this.parentMenu.close():a&&!E.isCursorOverElement(a,this.parentMenu.root)&&E.trigger(this.parentMenu.root,"mouseleave",a));this.current_submenu&&this.current_submenu.close(a,!0);this.root.closing_timer&& +clearTimeout(this.root.closing_timer)};E.trigger=function(a,b,d,h){var f=document.createEvent("CustomEvent");f.initCustomEvent(b,!0,!0,d);f.srcElement=h;a.dispatchEvent?a.dispatchEvent(f):a.__events&&a.__events.dispatchEvent(f);return f};E.prototype.getTopMenu=function(){return this.options.parentMenu?this.options.parentMenu.getTopMenu():this};E.prototype.getFirstEvent=function(){return this.options.parentMenu?this.options.parentMenu.getFirstEvent():this.options.event};E.isCursorOverElement=function(a, +b){var d=a.clientX,h=a.clientY,f=b.getBoundingClientRect();return f?h>f.top&&hf.left&&dMath.abs(d))return h[1];d=(a-h[0])/d;return h[1]*(1-d)+f[1]*d}}return 0}};B.prototype.draw=function(a,b,d,h,f,e){if(d=this.points){this.size= +b;var c=b[0]-2*this.margin;b=b[1]-2*this.margin;f=f||"#666";a.save();a.translate(this.margin,this.margin);h&&(a.fillStyle="#111",a.fillRect(0,0,c,b),a.fillStyle="#222",a.fillRect(0.5*c,0,1,b),a.strokeStyle="#333",a.strokeRect(0,0,c,b));a.strokeStyle=f;e&&(a.globalAlpha=0.5);a.beginPath();for(h=0;ha[1])){var h=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,e=a[0]-this.margin,c=a[1]-this.margin;this.selected=this.getCloserPoint([e,c],30/b.ds.scale);-1==this.selected&&(h=[e/h,1-c/f],d.push(h),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(h),this.must_update=!0);if(-1!=this.selected)return!0}};B.prototype.onMouseMove=function(a,b){var d=this.points;if(d){var h= +this.selected;if(!(0>h)){var f=(a[0]-this.margin)/(this.size[0]-2*this.margin),e=(a[1]-this.margin)/(this.size[1]-2*this.margin);this._nearest=this.getCloserPoint([a[0]-this.margin,a[1]-this.margin],30/b.ds.scale);var c=d[h];if(c){var l=0==h||h==d.length-1;!l&&(-10>a[0]||a[0]>this.size[0]+10||-10>a[1]||a[1]>this.size[1]+10)?(d.splice(h,1),this.selected=-1):(c[0]=l?0==h?0:1:Math.clamp(f,0,1),c[1]=1-Math.clamp(e,0,1),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(c),this.must_update= +!0)}}}};B.prototype.onMouseUp=function(a,b){this.selected=-1;return!1};B.prototype.getCloserPoint=function(a,b){var d=this.points;if(!d)return-1;b=b||30;for(var h=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,e=d.length,c=[0,0],l=1E6,q=-1,p=0;pl||g>b||(q=p,l=g)}return q};e.CurveEditor=B;e.getParameterNames=function(a){return(a+"").replace(/[/][/].*$/gm,"").replace(/\s+/g,"").replace(/[/][*][^/*]*[*][/]/g,"").split("){", +1)[0].replace(/^[^(]*[(]/,"").replace(/=[^,]+/g,"").split(",").filter(Boolean)};Math.clamp=function(a,b,d){return b>a?b:dthis.size[0]-l.NODE_TITLE_HEIGHT&&0>b[1]){var h=this;setTimeout(function(){d.openSubgraph(h.subgraph)},10)}};m.prototype.onAction=function(a,b){this.subgraph.onAction(a,b)};m.prototype.onExecute=function(){if(this.enabled=this.getInputOrProperty("enabled")){if(this.inputs)for(var a=0;a=e?this.trigger(null,g):this._pending.push([e,g])};g.prototype.onExecute=function(){var c=1E3* +this.graph.elapsed_time;this.isInputConnected(1)&&(this.properties.time_in_ms=this.getInputData(1));for(var g=0;ge[1]))return this.old_y=c.canvasY,this.captureInput(!0),this.mouse_captured=!0};n.prototype.onMouseMove=function(c){if(this.mouse_captured){var e=this.old_y-c.canvasY;c.shiftKey&&(e*=10);if(c.metaKey||c.altKey)e*=0.1;this.old_y=c.canvasY;c=this._remainder+e/n.pixels_threshold;this._remainder=c%1;c=Math.clamp(this.properties.value+ -(c|0)*this.properties.step,this.properties.min,this.properties.max);this.properties.value=c;this.graph._version++;this.setDirtyCanvas(!0)}};n.prototype.onMouseUp=function(c,e){200>c.click_time&&(this.properties.value=Math.clamp(this.properties.value+(e[1]>0.5*this.size[1]?-1:1)*this.properties.step,this.properties.min,this.properties.max),this.graph._version++,this.setDirtyCanvas(!0));this.mouse_captured&&(this.mouse_captured=!1,this.captureInput(!1))};B.registerNodeType("widget/number",n);l.title= -"Combo";l.desc="Widget to select from a list";l.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};l.prototype.onPropertyChanged=function(c,e){"values"==c?(this._values=e.split(";"),this.widget.options.values=this._values):"value"==c&&(this.widget.value=e)};B.registerNodeType("widget/combo",l);x.title="Knob";x.desc="Circular controller";x.size=[80,100];x.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/ -(this.properties.max-this.properties.min));var e=0.5*this.size[0],q=0.5*this.size[1],l=0.5*Math.min(this.size[0],this.size[1])-5;c.globalAlpha=1;c.save();c.translate(e,q);c.rotate(0.75*Math.PI);c.fillStyle="rgba(0,0,0,0.5)";c.beginPath();c.moveTo(0,0);c.arc(0,0,l,0,1.5*Math.PI);c.fill();c.strokeStyle="black";c.fillStyle=this.properties.color;c.lineWidth=2;c.beginPath();c.moveTo(0,0);c.arc(0,0,l-4,0,1.5*Math.PI*Math.max(0.01,this.value));c.closePath();c.fill();c.lineWidth=1;c.globalAlpha=1;c.restore(); -c.fillStyle="black";c.beginPath();c.arc(e,q,0.75*l,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":this.properties.color;c.beginPath();var s=this.value*Math.PI*1.5+0.75*Math.PI;c.arc(e+Math.cos(s)*l*0.65,q+Math.sin(s)*l*0.65,0.05*l,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":"#AAA";c.font=Math.floor(0.5*l)+"px Arial";c.textAlign="center";c.fillText(this.properties.value.toFixed(this.properties.precision),e,q+0.15*l)}};x.prototype.onExecute=function(){this.setOutputData(0, +this.old_y=-1;this._precision=this._remainder=0;this.mouse_captured=!1}function k(){this.addOutput("","string");this.addOutput("change",B.EVENT);this.size=[80,60];this.properties={value:"A",values:"A;B;C"};this.old_y=-1;this.mouse_captured=!1;this._values=this.properties.values.split(";");var c=this;this.widgets_up=!0;this.widget=this.addWidget("combo","",this.properties.value,function(q){c.properties.value=q;c.triggerSlot(1,q)},{property:"value",values:this._values})}function x(){this.addOutput("", +"number");this.size=[64,84];this.properties={min:0,max:1,value:0.5,color:"#7AF",precision:2};this.value=-1}function g(){this.addOutput("","number");this.properties={value:0.5,min:0,max:1,text:"V"};var c=this;this.size=[140,40];this.slider=this.addWidget("slider","V",this.properties.value,function(q){c.properties.value=q},this.properties);this.widgets_up=!0}function C(){this.size=[160,26];this.addOutput("","number");this.properties={color:"#7AF",min:0,max:1,value:0.5};this.value=-1}function z(){this.size= +[160,26];this.addInput("","number");this.properties={min:0,max:1,value:0,color:"#AAF"}}function v(){this.addInputs("",0);this.properties={value:"...",font:"Arial",fontsize:18,color:"#AAA",align:"left",glowSize:0,decimals:1}}function E(){this.size=[200,100];this.properties={borderColor:"#ffffff",bgcolorTop:"#f0f0f0",bgcolorBottom:"#e0e0e0",shadowSize:2,borderRadius:3}}var B=y.LiteGraph;c.title="Button";c.desc="Triggers an event";c.font="Arial";c.prototype.onDrawForeground=function(e){if(!this.flags.collapsed&& +(e.fillStyle="black",e.fillRect(11,11,this.size[0]-20,this.size[1]-20),e.fillStyle="#AAF",e.fillRect(9,9,this.size[0]-20,this.size[1]-20),e.fillStyle=this.clicked?"white":this.mouseOver?"#668":"#334",e.fillRect(10,10,this.size[0]-20,this.size[1]-20),this.properties.text||0===this.properties.text)){var q=this.properties.font_size||30;e.textAlign="center";e.fillStyle=this.clicked?"black":"white";e.font=q+"px "+c.font;e.fillText(this.properties.text,0.5*this.size[0],0.5*this.size[1]+0.3*q);e.textAlign= +"left"}};c.prototype.onMouseDown=function(c,q){if(1q[1]))return this.old_y=c.canvasY,this.captureInput(!0),this.mouse_captured=!0};n.prototype.onMouseMove=function(c){if(this.mouse_captured){var q=this.old_y-c.canvasY;c.shiftKey&&(q*=10);if(c.metaKey||c.altKey)q*=0.1;this.old_y=c.canvasY;c=this._remainder+q/n.pixels_threshold;this._remainder=c%1;c=Math.clamp(this.properties.value+ +(c|0)*this.properties.step,this.properties.min,this.properties.max);this.properties.value=c;this.graph._version++;this.setDirtyCanvas(!0)}};n.prototype.onMouseUp=function(c,q){200>c.click_time&&(this.properties.value=Math.clamp(this.properties.value+(q[1]>0.5*this.size[1]?-1:1)*this.properties.step,this.properties.min,this.properties.max),this.graph._version++,this.setDirtyCanvas(!0));this.mouse_captured&&(this.mouse_captured=!1,this.captureInput(!1))};B.registerNodeType("widget/number",n);k.title= +"Combo";k.desc="Widget to select from a list";k.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};k.prototype.onPropertyChanged=function(c,q){"values"==c?(this._values=q.split(";"),this.widget.options.values=this._values):"value"==c&&(this.widget.value=q)};B.registerNodeType("widget/combo",k);x.title="Knob";x.desc="Circular controller";x.size=[80,100];x.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/ +(this.properties.max-this.properties.min));var q=0.5*this.size[0],g=0.5*this.size[1],k=0.5*Math.min(this.size[0],this.size[1])-5;c.globalAlpha=1;c.save();c.translate(q,g);c.rotate(0.75*Math.PI);c.fillStyle="rgba(0,0,0,0.5)";c.beginPath();c.moveTo(0,0);c.arc(0,0,k,0,1.5*Math.PI);c.fill();c.strokeStyle="black";c.fillStyle=this.properties.color;c.lineWidth=2;c.beginPath();c.moveTo(0,0);c.arc(0,0,k-4,0,1.5*Math.PI*Math.max(0.01,this.value));c.closePath();c.fill();c.lineWidth=1;c.globalAlpha=1;c.restore(); +c.fillStyle="black";c.beginPath();c.arc(q,g,0.75*k,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":this.properties.color;c.beginPath();var p=this.value*Math.PI*1.5+0.75*Math.PI;c.arc(q+Math.cos(p)*k*0.65,g+Math.sin(p)*k*0.65,0.05*k,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":"#AAA";c.font=Math.floor(0.5*k)+"px Arial";c.textAlign="center";c.fillText(this.properties.value.toFixed(this.properties.precision),q,g+0.15*k)}};x.prototype.onExecute=function(){this.setOutputData(0, this.properties.value);this.boxcolor=B.colorToString([this.value,this.value,this.value])};x.prototype.onMouseDown=function(c){this.center=[0.5*this.size[0],0.5*this.size[1]+20];this.radius=0.5*this.size[0];if(20>c.canvasY-this.pos[1]||B.distance([c.canvasX,c.canvasY],[this.pos[0]+this.center[0],this.pos[1]+this.center[1]])>this.radius)return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];this.captureInput(!0);return!0};x.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX- -this.pos[0],c.canvasY-this.pos[1]];var e=this.value,e=e-0.01*(c[1]-this.oldmouse[1]);1e&&(e=0);this.value=e;this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.oldmouse=c;this.setDirtyCanvas(!0)}};x.prototype.onMouseUp=function(c){this.oldmouse&&(this.oldmouse=null,this.captureInput(!1))};x.prototype.onPropertyChanged=function(c,e){if("min"==c||"max"==c||"value"==c)return this.properties[c]=parseFloat(e),!0};B.registerNodeType("widget/knob", -x);e.title="Inner Slider";e.prototype.onPropertyChanged=function(c,e){"value"==c&&(this.slider.value=e)};e.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};B.registerNodeType("widget/internal_slider",e);C.title="H.Slider";C.desc="Linear slider controller";C.prototype.onDrawForeground=function(c){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min));c.globalAlpha=1;c.lineWidth=1;c.fillStyle="#000";c.fillRect(2, +this.pos[0],c.canvasY-this.pos[1]];var g=this.value,g=g-0.01*(c[1]-this.oldmouse[1]);1g&&(g=0);this.value=g;this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.oldmouse=c;this.setDirtyCanvas(!0)}};x.prototype.onMouseUp=function(c){this.oldmouse&&(this.oldmouse=null,this.captureInput(!1))};x.prototype.onPropertyChanged=function(c,g){if("min"==c||"max"==c||"value"==c)return this.properties[c]=parseFloat(g),!0};B.registerNodeType("widget/knob", +x);g.title="Inner Slider";g.prototype.onPropertyChanged=function(c,g){"value"==c&&(this.slider.value=g)};g.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};B.registerNodeType("widget/internal_slider",g);C.title="H.Slider";C.desc="Linear slider controller";C.prototype.onDrawForeground=function(c){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min));c.globalAlpha=1;c.lineWidth=1;c.fillStyle="#000";c.fillRect(2, 2,this.size[0]-4,this.size[1]-4);c.fillStyle=this.properties.color;c.beginPath();c.rect(4,4,(this.size[0]-8)*this.value,this.size[1]-8);c.fill()};C.prototype.onExecute=function(){this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.setOutputData(0,this.properties.value);this.boxcolor=B.colorToString([this.value,this.value,this.value])};C.prototype.onMouseDown=function(c){if(0>c.canvasY-this.pos[1])return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY- -this.pos[1]];this.captureInput(!0);return!0};C.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];var e=this.value,e=e+(c[0]-this.oldmouse[0])/this.size[0];1e&&(e=0);this.value=e;this.oldmouse=c;this.setDirtyCanvas(!0)}};C.prototype.onMouseUp=function(c){this.oldmouse=null;this.captureInput(!1)};C.prototype.onMouseLeave=function(c){};B.registerNodeType("widget/hslider",C);z.title="Progress";z.desc="Shows data in linear progress";z.prototype.onExecute= -function(){var c=this.getInputData(0);void 0!=c&&(this.properties.value=c)};z.prototype.onDrawForeground=function(c){c.lineWidth=1;c.fillStyle=this.properties.color;var e=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min),e=Math.min(1,e),e=Math.max(0,e);c.fillRect(2,2,(this.size[0]-4)*e,this.size[1]-4)};B.registerNodeType("widget/progress",z);v.title="Text";v.desc="Shows the input value";v.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"led_text", -text:"LED",type:"minibutton"},{name:"normal_text",text:"Normal",type:"minibutton"}];v.prototype.onDrawForeground=function(c){c.fillStyle=this.properties.color;var e=this.properties.value;this.properties.glowSize?(c.shadowColor=this.properties.color,c.shadowOffsetX=0,c.shadowOffsetY=0,c.shadowBlur=this.properties.glowSize):c.shadowColor="transparent";var q=this.properties.fontsize;c.textAlign=this.properties.align;c.font=q.toString()+"px "+this.properties.font;this.str="number"==typeof e?e.toFixed(this.properties.decimals): -e;if("string"==typeof this.str){var e=this.str.split("\\n"),l;for(l in e)c.fillText(e[l],"left"==this.properties.align?15:this.size[0]-15,-0.15*q+q*(parseInt(l)+1))}c.shadowColor="transparent";this.last_ctx=c;c.textAlign="left"};v.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(this.properties.value=c)};v.prototype.resize=function(){if(this.last_ctx){var c=this.str.split("\\n");this.last_ctx.font=this.properties.fontsize+"px "+this.properties.font;var e=0,q;for(q in c){var l=this.last_ctx.measureText(c[q]).width; -eg&&(g=0);this.value=g;this.oldmouse=c;this.setDirtyCanvas(!0)}};C.prototype.onMouseUp=function(c){this.oldmouse=null;this.captureInput(!1)};C.prototype.onMouseLeave=function(c){};B.registerNodeType("widget/hslider",C);z.title="Progress";z.desc="Shows data in linear progress";z.prototype.onExecute= +function(){var c=this.getInputData(0);void 0!=c&&(this.properties.value=c)};z.prototype.onDrawForeground=function(c){c.lineWidth=1;c.fillStyle=this.properties.color;var g=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min),g=Math.min(1,g),g=Math.max(0,g);c.fillRect(2,2,(this.size[0]-4)*g,this.size[1]-4)};B.registerNodeType("widget/progress",z);v.title="Text";v.desc="Shows the input value";v.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"led_text", +text:"LED",type:"minibutton"},{name:"normal_text",text:"Normal",type:"minibutton"}];v.prototype.onDrawForeground=function(c){c.fillStyle=this.properties.color;var g=this.properties.value;this.properties.glowSize?(c.shadowColor=this.properties.color,c.shadowOffsetX=0,c.shadowOffsetY=0,c.shadowBlur=this.properties.glowSize):c.shadowColor="transparent";var k=this.properties.fontsize;c.textAlign=this.properties.align;c.font=k.toString()+"px "+this.properties.font;this.str="number"==typeof g?g.toFixed(this.properties.decimals): +g;if("string"==typeof this.str){var g=this.str.split("\\n"),n;for(n in g)c.fillText(g[n],"left"==this.properties.align?15:this.size[0]-15,-0.15*k+k*(parseInt(n)+1))}c.shadowColor="transparent";this.last_ctx=c;c.textAlign="left"};v.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(this.properties.value=c)};v.prototype.resize=function(){if(this.last_ctx){var c=this.str.split("\\n");this.last_ctx.font=this.properties.fontsize+"px "+this.properties.font;var g=0,k;for(k in c){var n=this.last_ctx.measureText(c[k]).width; +gl?n.xbox.axes.lx:0,this._left_axis[1]=Math.abs(n.xbox.axes.ly)>l?n.xbox.axes.ly:0,this._right_axis[0]=Math.abs(n.xbox.axes.rx)>l?n.xbox.axes.rx:0,this._right_axis[1]=Math.abs(n.xbox.axes.ry)>l?n.xbox.axes.ry:0,this._triggers[0]=Math.abs(n.xbox.axes.ltrigger)>l?n.xbox.axes.ltrigger: -0,this._triggers[1]=Math.abs(n.xbox.axes.rtrigger)>l?n.xbox.axes.rtrigger:0);if(this.outputs)for(l=0;ln;n++)if(l[n]){n=l[n];l=this.xbox_mapping;l||(l=this.xbox_mapping={axes:[], -buttons:{},hat:"",hatmap:c.CENTER});l.axes.lx=n.axes[0];l.axes.ly=n.axes[1];l.axes.rx=n.axes[2];l.axes.ry=n.axes[3];l.axes.ltrigger=n.buttons[6].value;l.axes.rtrigger=n.buttons[7].value;l.hat="";l.hatmap=c.CENTER;for(var m=0;mm)l.buttons[c.mapping_array[m]]=n.buttons[m].pressed,n.buttons[m].was_pressed&&this.trigger(c.mapping_array[m]+"_button_event");else switch(m){case 12:n.buttons[m].pressed&&(l.hat+="up",l.hatmap|=c.UP); -break;case 13:n.buttons[m].pressed&&(l.hat+="down",l.hatmap|=c.DOWN);break;case 14:n.buttons[m].pressed&&(l.hat+="left",l.hatmap|=c.LEFT);break;case 15:n.buttons[m].pressed&&(l.hat+="right",l.hatmap|=c.RIGHT);break;case 16:l.buttons.home=n.buttons[m].pressed}n.xbox=l;return n}};c.prototype.onDrawBackground=function(c){if(!this.flags.collapsed){var l=this._left_axis,m=this._right_axis;c.strokeStyle="#88A";c.strokeRect(0.5*(l[0]+1)*this.size[0]-4,0.5*(l[1]+1)*this.size[1]-4,8,8);c.strokeStyle="#8A8"; -c.strokeRect(0.5*(m[0]+1)*this.size[0]-4,0.5*(m[1]+1)*this.size[1]-4,8,8);l=this.size[1]/this._current_buttons.length;c.fillStyle="#AEB";for(m=0;mk?n.xbox.axes.lx:0,this._left_axis[1]=Math.abs(n.xbox.axes.ly)>k?n.xbox.axes.ly:0,this._right_axis[0]=Math.abs(n.xbox.axes.rx)>k?n.xbox.axes.rx:0,this._right_axis[1]=Math.abs(n.xbox.axes.ry)>k?n.xbox.axes.ry:0,this._triggers[0]=Math.abs(n.xbox.axes.ltrigger)>k?n.xbox.axes.ltrigger: +0,this._triggers[1]=Math.abs(n.xbox.axes.rtrigger)>k?n.xbox.axes.rtrigger:0);if(this.outputs)for(k=0;kn;n++)if(k[n]){n=k[n];k=this.xbox_mapping;k||(k=this.xbox_mapping={axes:[], +buttons:{},hat:"",hatmap:c.CENTER});k.axes.lx=n.axes[0];k.axes.ly=n.axes[1];k.axes.rx=n.axes[2];k.axes.ry=n.axes[3];k.axes.ltrigger=n.buttons[6].value;k.axes.rtrigger=n.buttons[7].value;k.hat="";k.hatmap=c.CENTER;for(var m=0;mm)k.buttons[c.mapping_array[m]]=n.buttons[m].pressed,n.buttons[m].was_pressed&&this.trigger(c.mapping_array[m]+"_button_event");else switch(m){case 12:n.buttons[m].pressed&&(k.hat+="up",k.hatmap|=c.UP); +break;case 13:n.buttons[m].pressed&&(k.hat+="down",k.hatmap|=c.DOWN);break;case 14:n.buttons[m].pressed&&(k.hat+="left",k.hatmap|=c.LEFT);break;case 15:n.buttons[m].pressed&&(k.hat+="right",k.hatmap|=c.RIGHT);break;case 16:k.buttons.home=n.buttons[m].pressed}n.xbox=k;return n}};c.prototype.onDrawBackground=function(c){if(!this.flags.collapsed){var k=this._left_axis,m=this._right_axis;c.strokeStyle="#88A";c.strokeRect(0.5*(k[0]+1)*this.size[0]-4,0.5*(k[1]+1)*this.size[1]-4,8,8);c.strokeStyle="#8A8"; +c.strokeRect(0.5*(m[0]+1)*this.size[0]-4,0.5*(m[1]+1)*this.size[1]-4,8,8);k=this.size[1]/this._current_buttons.length;c.fillStyle="#AEB";for(m=0;m","enum",{values:a.values});this.size=[80,60]}function b(){this.addInput("inc","number");this.addOutput("total","number");this.addProperty("increment",1);this.addProperty("value",0)}function d(){this.addInput("v","number");this.addOutput("sin","number");this.addProperty("amplitude",1);this.addProperty("offset",0);this.bgImageUrl= -"nodes/imgs/icon-sin.png"}function k(){this.addInput("x","number");this.addInput("y","number");this.addOutput("","number");this.properties={x:1,y:1,formula:"x+y"};this.code_widget=this.addWidget("text","F(x,y)",this.properties.formula,function(a,b,d){d.properties.formula=a});this.addWidget("toggle","allow",E.allow_scripts,function(a){E.allow_scripts=a});this._func=null}function f(){this.addInput("vec2","vec2");this.addOutput("x","number");this.addOutput("y","number")}function K(){this.addInputs([["x", -"number"],["y","number"]]);this.addOutput("vec2","vec2");this.properties={x:0,y:0};this._data=new Float32Array(2)}function w(){this.addInput("vec3","vec3");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number")}function H(){this.addInputs([["x","number"],["y","number"],["z","number"]]);this.addOutput("vec3","vec3");this.properties={x:0,y:0,z:0};this._data=new Float32Array(3)}function I(){this.addInput("vec4","vec4");this.addOutput("x","number");this.addOutput("y","number"); -this.addOutput("z","number");this.addOutput("w","number")}function G(){this.addInputs([["x","number"],["y","number"],["z","number"],["w","number"]]);this.addOutput("vec4","vec4");this.properties={x:0,y:0,z:0,w:0};this._data=new Float32Array(4)}var E=y.LiteGraph;c.title="Converter";c.desc="type A to type B";c.prototype.onExecute=function(){var a=this.getInputData(0);if(null!=a&&this.outputs)for(var b=0;ba&&(a+=1024);var c=Math.floor(a);a-=c;d=e.data[c];c=e.data[1023==c?0:c+1];b&&(a=a*a*a*(a*(6*a-15)+10));return d*(1-a)+c*a};e.prototype.onExecute=function(){var a=this.getInputData(0)||0,a=e.getValue(a,this.properties.smooth), -b=this.properties.min;this._last_v=a*(this.properties.max-b)+b;this.setOutputData(0,this._last_v)};e.prototype.onDrawBackground=function(a){this.outputs[0].label=(this._last_v||0).toFixed(3)};E.registerNodeType("math/noise",e);C.title="Spikes";C.desc="spike every random time";C.prototype.onExecute=function(){var a=this.graph.elapsed_time;this._remaining_time-=a;this._blink_time-=a;a=0;0this._remaining_time?(this._remaining_time= -Math.random()*(this.properties.max_time-this.properties.min_time)+this.properties.min_time,this._blink_time=this.properties.duration,this.boxcolor="#FFF"):this.boxcolor="#000";this.setOutputData(0,a)};E.registerNodeType("math/spikes",C);z.title="Clamp";z.desc="Clamp number between min and max";z.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(a=Math.max(this.properties.min,a),a=Math.min(this.properties.max,a),this.setOutputData(0,a))};z.prototype.getCode=function(a){a="";this.isInputConnected(0)&& -(a+="clamp({{0}},"+this.properties.min+","+this.properties.max+")");return a};E.registerNodeType("math/clamp",z);v.title="Lerp";v.desc="Linear Interpolation";v.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.getInputData(1);null==b&&(b=0);var d=this.properties.f,c=this.getInputData(2);void 0!==c&&(d=c);this.setOutputData(0,a*(1-d)+b*d)};v.prototype.onGetInputs=function(){return[["f","number"]]};E.registerNodeType("math/lerp",v);D.title="Abs";D.desc="Absolute";D.prototype.onExecute= -function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.abs(a))};E.registerNodeType("math/abs",D);B.title="Floor";B.desc="Floor number to remove fractional part";B.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.floor(a))};E.registerNodeType("math/floor",B);g.title="Frac";g.desc="Returns fractional part";g.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a%1)};E.registerNodeType("math/frac",g);r.title= -"Smoothstep";r.desc="Smoothstep";r.prototype.onExecute=function(){var a=this.getInputData(0);if(void 0!==a){var b=this.properties.A,a=Math.clamp((a-b)/(this.properties.B-b),0,1);this.setOutputData(0,a*a*(3-2*a))}};E.registerNodeType("math/smoothstep",r);q.title="Scale";q.desc="v * factor";q.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a*this.properties.factor)};E.registerNodeType("math/scale",q);A.title="Gate";A.desc="if v is true, then outputs A, otherwise B"; -A.prototype.onExecute=function(){var a=this.getInputData(0);this.setOutputData(0,this.getInputData(a?1:2))};E.registerNodeType("math/gate",A);s.title="Average";s.desc="Average Filter";s.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this._values.length;this._values[this._current%b]=a;this._current+=1;this._current>b&&(this._current=0);for(var d=a=0;db&&(b=1);this.properties.samples= -Math.round(b);var d=this._values;this._values=new Float32Array(this.properties.samples);d.length<=this._values.length?this._values.set(d):this._values.set(d.subarray(0,this._values.length))};E.registerNodeType("math/average",s);p.title="TendTo";p.desc="moves the output value always closer to the input";p.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.factor;this._value=null==this._value?a:this._value*(1-b)+a*b;this.setOutputData(0,this._value)};E.registerNodeType("math/tendTo", -p);t.values="+ - * / % ^ max min".split(" ");t.title="Operation";t.desc="Easy math operators";t["@OP"]={type:"enum",title:"operation",values:t.values};t.size=[100,60];t.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"};t.prototype.setValue=function(a){"string"==typeof a&&(a=parseFloat(a));this.properties.value=a};t.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);null!=a? -this.properties.A=a:a=this.properties.A;null!=b?this.properties.B=b:b=this.properties.B;var d=0;switch(this.properties.OP){case "+":d=a+b;break;case "-":d=a-b;break;case "x":case "X":case "*":d=a*b;break;case "/":d=a/b;break;case "%":d=a%b;break;case "^":d=Math.pow(a,b);break;case "max":d=Math.max(a,b);break;case "min":d=Math.min(a,b);break;default:console.warn("Unknown operation: "+this.properties.OP)}this.setOutputData(0,d)};t.prototype.onDrawBackground=function(a){this.flags.collapsed||(a.font= -"40px Arial",a.fillStyle="#666",a.textAlign="center",a.fillText(this.properties.OP,0.5*this.size[0],0.5*(this.size[1]+E.NODE_TITLE_HEIGHT)),a.textAlign="left")};E.registerNodeType("math/operation",t);E.registerSearchboxExtra("math/operation","MAX",{properties:{OP:"max"},title:"MAX()"});E.registerSearchboxExtra("math/operation","MIN",{properties:{OP:"min"},title:"MIN()"});h.title="Compare";h.desc="compares between two values";h.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1); -void 0!==a?this.properties.A=a:a=this.properties.A;void 0!==b?this.properties.B=b:b=this.properties.B;for(var d=0,c=this.outputs.length;dB":h=a>b;break;case "A=B":h=a>=b}this.setOutputData(d,h)}}};h.prototype.onGetOutputs=function(){return[["A==B","boolean"],["A!=B","boolean"],["A>B","boolean"],["A=B", -"boolean"],["A<=B","boolean"]]};E.registerNodeType("math/compare",h);E.registerSearchboxExtra("math/compare","==",{outputs:[["A==B","boolean"]],title:"A==B"});E.registerSearchboxExtra("math/compare","!=",{outputs:[["A!=B","boolean"]],title:"A!=B"});E.registerSearchboxExtra("math/compare",">",{outputs:[["A>B","boolean"]],title:"A>B"});E.registerSearchboxExtra("math/compare","<",{outputs:[["A=",{outputs:[["A>=B","boolean"]],title:"A>=B"}); -E.registerSearchboxExtra("math/compare","<=",{outputs:[["A<=B","boolean"]],title:"A<=B"});a.values="> < == != <= >= || &&".split(" ");a["@OP"]={type:"enum",title:"operation",values:a.values};a.title="Condition";a.desc="evaluates condition between A and B";a.prototype.getTitle=function(){return"A "+this.properties.OP+" B"};a.prototype.onExecute=function(){var a=this.getInputData(0);void 0===a?a=this.properties.A:this.properties.A=a;var b=this.getInputData(1);void 0===b?b=this.properties.B:this.properties.B= -b;var d=!0;switch(this.properties.OP){case ">":d=a>b;break;case "<":d=a=":d=a>=b;break;case "||":d=a||b;break;case "&&":d=a&&b}this.setOutputData(0,d);this.setOutputData(1,!d)};E.registerNodeType("math/condition",a);b.title="Accumulate";b.desc="Increments a value every time";b.prototype.onExecute=function(){null===this.properties.value&&(this.properties.value=0);var a=this.getInputData(0);this.properties.value=null!== -a?this.properties.value+a:this.properties.value+this.properties.increment;this.setOutputData(0,this.properties.value)};E.registerNodeType("math/accumulate",b);d.title="Trigonometry";d.desc="Sin Cos Tan";d.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.amplitude,d=this.findInputSlot("amplitude");-1!=d&&(b=this.getInputData(d));var c=this.properties.offset,d=this.findInputSlot("offset");-1!=d&&(c=this.getInputData(d));for(var d=0,f=this.outputs.length;d< +"nodes/imgs/icon-sin.png"}function h(){this.addInput("x","number");this.addInput("y","number");this.addOutput("","number");this.properties={x:1,y:1,formula:"x+y"};this.code_widget=this.addWidget("text","F(x,y)",this.properties.formula,function(a,b,d){d.properties.formula=a});this.addWidget("toggle","allow",D.allow_scripts,function(a){D.allow_scripts=a});this._func=null}function f(){this.addInput("vec2","vec2");this.addOutput("x","number");this.addOutput("y","number")}function K(){this.addInputs([["x", +"number"],["y","number"]]);this.addOutput("vec2","vec2");this.properties={x:0,y:0};this._data=new Float32Array(2)}function r(){this.addInput("vec3","vec3");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number")}function H(){this.addInputs([["x","number"],["y","number"],["z","number"]]);this.addOutput("vec3","vec3");this.properties={x:0,y:0,z:0};this._data=new Float32Array(3)}function I(){this.addInput("vec4","vec4");this.addOutput("x","number");this.addOutput("y","number"); +this.addOutput("z","number");this.addOutput("w","number")}function G(){this.addInputs([["x","number"],["y","number"],["z","number"],["w","number"]]);this.addOutput("vec4","vec4");this.properties={x:0,y:0,z:0,w:0};this._data=new Float32Array(4)}var D=y.LiteGraph;c.title="Converter";c.desc="type A to type B";c.prototype.onExecute=function(){var a=this.getInputData(0);if(null!=a&&this.outputs)for(var b=0;ba&&(a+=1024);var c=Math.floor(a);a-=c;d=g.data[c];c=g.data[1023==c?0:c+1];b&&(a=a*a*a*(a*(6*a-15)+10));return d*(1-a)+c*a};g.prototype.onExecute=function(){var a=this.getInputData(0)||0,a=g.getValue(a,this.properties.smooth), +b=this.properties.min;this._last_v=a*(this.properties.max-b)+b;this.setOutputData(0,this._last_v)};g.prototype.onDrawBackground=function(a){this.outputs[0].label=(this._last_v||0).toFixed(3)};D.registerNodeType("math/noise",g);C.title="Spikes";C.desc="spike every random time";C.prototype.onExecute=function(){var a=this.graph.elapsed_time;this._remaining_time-=a;this._blink_time-=a;a=0;0this._remaining_time?(this._remaining_time= +Math.random()*(this.properties.max_time-this.properties.min_time)+this.properties.min_time,this._blink_time=this.properties.duration,this.boxcolor="#FFF"):this.boxcolor="#000";this.setOutputData(0,a)};D.registerNodeType("math/spikes",C);z.title="Clamp";z.desc="Clamp number between min and max";z.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(a=Math.max(this.properties.min,a),a=Math.min(this.properties.max,a),this.setOutputData(0,a))};z.prototype.getCode=function(a){a="";this.isInputConnected(0)&& +(a+="clamp({{0}},"+this.properties.min+","+this.properties.max+")");return a};D.registerNodeType("math/clamp",z);v.title="Lerp";v.desc="Linear Interpolation";v.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.getInputData(1);null==b&&(b=0);var d=this.properties.f,c=this.getInputData(2);void 0!==c&&(d=c);this.setOutputData(0,a*(1-d)+b*d)};v.prototype.onGetInputs=function(){return[["f","number"]]};D.registerNodeType("math/lerp",v);E.title="Abs";E.desc="Absolute";E.prototype.onExecute= +function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.abs(a))};D.registerNodeType("math/abs",E);B.title="Floor";B.desc="Floor number to remove fractional part";B.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.floor(a))};D.registerNodeType("math/floor",B);e.title="Frac";e.desc="Returns fractional part";e.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a%1)};D.registerNodeType("math/frac",e);q.title= +"Smoothstep";q.desc="Smoothstep";q.prototype.onExecute=function(){var a=this.getInputData(0);if(void 0!==a){var b=this.properties.A,a=Math.clamp((a-b)/(this.properties.B-b),0,1);this.setOutputData(0,a*a*(3-2*a))}};D.registerNodeType("math/smoothstep",q);t.title="Scale";t.desc="v * factor";t.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a*this.properties.factor)};D.registerNodeType("math/scale",t);A.title="Gate";A.desc="if v is true, then outputs A, otherwise B"; +A.prototype.onExecute=function(){var a=this.getInputData(0);this.setOutputData(0,this.getInputData(a?1:2))};D.registerNodeType("math/gate",A);p.title="Average";p.desc="Average Filter";p.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this._values.length;this._values[this._current%b]=a;this._current+=1;this._current>b&&(this._current=0);for(var d=a=0;db&&(b=1);this.properties.samples= +Math.round(b);var d=this._values;this._values=new Float32Array(this.properties.samples);d.length<=this._values.length?this._values.set(d):this._values.set(d.subarray(0,this._values.length))};D.registerNodeType("math/average",p);s.title="TendTo";s.desc="moves the output value always closer to the input";s.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.factor;this._value=null==this._value?a:this._value*(1-b)+a*b;this.setOutputData(0,this._value)};D.registerNodeType("math/tendTo", +s);w.values="+ - * / % ^ max min".split(" ");w.title="Operation";w.desc="Easy math operators";w["@OP"]={type:"enum",title:"operation",values:w.values};w.size=[100,60];w.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"};w.prototype.setValue=function(a){"string"==typeof a&&(a=parseFloat(a));this.properties.value=a};w.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);null!=a? +this.properties.A=a:a=this.properties.A;null!=b?this.properties.B=b:b=this.properties.B;var d=0;switch(this.properties.OP){case "+":d=a+b;break;case "-":d=a-b;break;case "x":case "X":case "*":d=a*b;break;case "/":d=a/b;break;case "%":d=a%b;break;case "^":d=Math.pow(a,b);break;case "max":d=Math.max(a,b);break;case "min":d=Math.min(a,b);break;default:console.warn("Unknown operation: "+this.properties.OP)}this.setOutputData(0,d)};w.prototype.onDrawBackground=function(a){this.flags.collapsed||(a.font= +"40px Arial",a.fillStyle="#666",a.textAlign="center",a.fillText(this.properties.OP,0.5*this.size[0],0.5*(this.size[1]+D.NODE_TITLE_HEIGHT)),a.textAlign="left")};D.registerNodeType("math/operation",w);D.registerSearchboxExtra("math/operation","MAX",{properties:{OP:"max"},title:"MAX()"});D.registerSearchboxExtra("math/operation","MIN",{properties:{OP:"min"},title:"MIN()"});l.title="Compare";l.desc="compares between two values";l.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1); +void 0!==a?this.properties.A=a:a=this.properties.A;void 0!==b?this.properties.B=b:b=this.properties.B;for(var d=0,c=this.outputs.length;dB":h=a>b;break;case "A=B":h=a>=b}this.setOutputData(d,h)}}};l.prototype.onGetOutputs=function(){return[["A==B","boolean"],["A!=B","boolean"],["A>B","boolean"],["A=B", +"boolean"],["A<=B","boolean"]]};D.registerNodeType("math/compare",l);D.registerSearchboxExtra("math/compare","==",{outputs:[["A==B","boolean"]],title:"A==B"});D.registerSearchboxExtra("math/compare","!=",{outputs:[["A!=B","boolean"]],title:"A!=B"});D.registerSearchboxExtra("math/compare",">",{outputs:[["A>B","boolean"]],title:"A>B"});D.registerSearchboxExtra("math/compare","<",{outputs:[["A=",{outputs:[["A>=B","boolean"]],title:"A>=B"}); +D.registerSearchboxExtra("math/compare","<=",{outputs:[["A<=B","boolean"]],title:"A<=B"});a.values="> < == != <= >= || &&".split(" ");a["@OP"]={type:"enum",title:"operation",values:a.values};a.title="Condition";a.desc="evaluates condition between A and B";a.prototype.getTitle=function(){return"A "+this.properties.OP+" B"};a.prototype.onExecute=function(){var a=this.getInputData(0);void 0===a?a=this.properties.A:this.properties.A=a;var b=this.getInputData(1);void 0===b?b=this.properties.B:this.properties.B= +b;var d=!0;switch(this.properties.OP){case ">":d=a>b;break;case "<":d=a=":d=a>=b;break;case "||":d=a||b;break;case "&&":d=a&&b}this.setOutputData(0,d);this.setOutputData(1,!d)};D.registerNodeType("math/condition",a);b.title="Accumulate";b.desc="Increments a value every time";b.prototype.onExecute=function(){null===this.properties.value&&(this.properties.value=0);var a=this.getInputData(0);this.properties.value=null!== +a?this.properties.value+a:this.properties.value+this.properties.increment;this.setOutputData(0,this.properties.value)};D.registerNodeType("math/accumulate",b);d.title="Trigonometry";d.desc="Sin Cos Tan";d.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.amplitude,d=this.findInputSlot("amplitude");-1!=d&&(b=this.getInputData(d));var c=this.properties.offset,d=this.findInputSlot("offset");-1!=d&&(c=this.getInputData(d));for(var d=0,f=this.outputs.length;d< f;++d){var h;switch(this.outputs[d].name){case "sin":h=Math.sin(a);break;case "cos":h=Math.cos(a);break;case "tan":h=Math.tan(a);break;case "asin":h=Math.asin(a);break;case "acos":h=Math.acos(a);break;case "atan":h=Math.atan(a)}this.setOutputData(d,b*h+c)}};d.prototype.onGetInputs=function(){return[["v","number"],["amplitude","number"],["offset","number"]]};d.prototype.onGetOutputs=function(){return[["sin","number"],["cos","number"],["tan","number"],["asin","number"],["acos","number"],["atan","number"]]}; -E.registerNodeType("math/trigonometry",d);E.registerSearchboxExtra("math/trigonometry","SIN()",{outputs:[["sin","number"]],title:"SIN()"});E.registerSearchboxExtra("math/trigonometry","COS()",{outputs:[["cos","number"]],title:"COS()"});E.registerSearchboxExtra("math/trigonometry","TAN()",{outputs:[["tan","number"]],title:"TAN()"});k.title="Formula";k.desc="Compute formula";k.size=[160,100];s.prototype.onPropertyChanged=function(a,b){"formula"==a&&(this.code_widget.value=b)};k.prototype.onExecute= -function(){if(E.allow_scripts){var a=this.getInputData(0),b=this.getInputData(1);null!=a?this.properties.x=a:a=this.properties.x;null!=b?this.properties.y=b:b=this.properties.y;var d;try{this._func&&this._func_code==this.properties.formula||(this._func=new Function("x","y","TIME","return "+this.properties.formula),this._func_code=this.properties.formula),d=this._func(a,b,this.graph.globaltime),this.boxcolor=null}catch(c){this.boxcolor="red"}this.setOutputData(0,d)}};k.prototype.getTitle=function(){return this._func_code|| -"Formula"};k.prototype.onDrawBackground=function(){var a=this.properties.formula;this.outputs&&this.outputs.length&&(this.outputs[0].label=a)};E.registerNodeType("math/formula",k);f.title="Vec2->XY";f.desc="vector 2 to components";f.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]))};E.registerNodeType("math3d/vec2-to-xy",f);K.title="XY->Vec2";K.desc="components to vector2";K.prototype.onExecute=function(){var a=this.getInputData(0); -null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this._data;d[0]=a;d[1]=b;this.setOutputData(0,d)};E.registerNodeType("math3d/xy-to-vec2",K);w.title="Vec3->XYZ";w.desc="vector 3 to components";w.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]))};E.registerNodeType("math3d/vec3-to-xyz",w);H.title="XYZ->Vec3";H.desc="components to vector3";H.prototype.onExecute= -function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this._data;c[0]=a;c[1]=b;c[2]=d;this.setOutputData(0,c)};E.registerNodeType("math3d/xyz-to-vec3",H);I.title="Vec4->XYZW";I.desc="vector 4 to components";I.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]),this.setOutputData(3, -a[3]))};E.registerNodeType("math3d/vec4-to-xyzw",I);G.title="XYZW->Vec4";G.desc="components to vector4";G.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this.getInputData(3);null==c&&(c=this.properties.w);var f=this._data;f[0]=a;f[1]=b;f[2]=d;f[3]=c;this.setOutputData(0,f)};E.registerNodeType("math3d/xyzw-to-vec4",G);y.glMatrix&&(y=function(){this.addOutput("quat", -"quat");this.properties={x:0,y:0,z:0,w:1};this._value=quat.create()},y.title="Quaternion",y.desc="quaternion",y.prototype.onExecute=function(){this._value[0]=this.properties.x;this._value[1]=this.properties.y;this._value[2]=this.properties.z;this._value[3]=this.properties.w;this.setOutputData(0,this._value)},E.registerNodeType("math3d/quaternion",y),y=function(){this.addInputs([["degrees","number"],["axis","vec3"]]);this.addOutput("quat","quat");this.properties={angle:90,axis:vec3.fromValues(0,1, -0)};this._value=quat.create()},y.title="Rotation",y.desc="quaternion rotation",y.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.angle);var b=this.getInputData(1);null==b&&(b=this.properties.axis);a=quat.setAxisAngle(this._value,b,0.0174532925*a);this.setOutputData(0,a)},E.registerNodeType("math3d/rotation",y),y=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},y.title="Rot. Vec3",y.desc= -"rotate a point",y.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.vec);var b=this.getInputData(1);null==b?this.setOutputData(a):this.setOutputData(0,vec3.transformQuat(vec3.create(),a,b))},E.registerNodeType("math3d/rotate_vec3",y),y=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},y.title="Mult. Quat",y.desc="rotate quaternion",y.prototype.onExecute=function(){var a=this.getInputData(0);if(null!= -a){var b=this.getInputData(1);null!=b&&(a=quat.multiply(this._value,a,b),this.setOutputData(0,a))}},E.registerNodeType("math3d/mult-quat",y),y=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp","quat");this.addProperty("factor",0.5);this._value=quat.create()},y.title="Quat Slerp",y.desc="quaternion spherical interpolation",y.prototype.onExecute=function(){var a=this.getInputData(0);if(null!=a){var b=this.getInputData(1);if(null!=b){var d=this.properties.factor; -null!=this.getInputData(2)&&(d=this.getInputData(2));a=quat.slerp(this._value,a,b,d);this.setOutputData(0,a)}}},E.registerNodeType("math3d/quat-slerp",y))})(this); +D.registerNodeType("math/trigonometry",d);D.registerSearchboxExtra("math/trigonometry","SIN()",{outputs:[["sin","number"]],title:"SIN()"});D.registerSearchboxExtra("math/trigonometry","COS()",{outputs:[["cos","number"]],title:"COS()"});D.registerSearchboxExtra("math/trigonometry","TAN()",{outputs:[["tan","number"]],title:"TAN()"});h.title="Formula";h.desc="Compute formula";h.size=[160,100];p.prototype.onPropertyChanged=function(a,b){"formula"==a&&(this.code_widget.value=b)};h.prototype.onExecute= +function(){if(D.allow_scripts){var a=this.getInputData(0),b=this.getInputData(1);null!=a?this.properties.x=a:a=this.properties.x;null!=b?this.properties.y=b:b=this.properties.y;var d;try{this._func&&this._func_code==this.properties.formula||(this._func=new Function("x","y","TIME","return "+this.properties.formula),this._func_code=this.properties.formula),d=this._func(a,b,this.graph.globaltime),this.boxcolor=null}catch(c){this.boxcolor="red"}this.setOutputData(0,d)}};h.prototype.getTitle=function(){return this._func_code|| +"Formula"};h.prototype.onDrawBackground=function(){var a=this.properties.formula;this.outputs&&this.outputs.length&&(this.outputs[0].label=a)};D.registerNodeType("math/formula",h);f.title="Vec2->XY";f.desc="vector 2 to components";f.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]))};D.registerNodeType("math3d/vec2-to-xy",f);K.title="XY->Vec2";K.desc="components to vector2";K.prototype.onExecute=function(){var a=this.getInputData(0); +null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this._data;d[0]=a;d[1]=b;this.setOutputData(0,d)};D.registerNodeType("math3d/xy-to-vec2",K);r.title="Vec3->XYZ";r.desc="vector 3 to components";r.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]))};D.registerNodeType("math3d/vec3-to-xyz",r);H.title="XYZ->Vec3";H.desc="components to vector3";H.prototype.onExecute= +function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this._data;c[0]=a;c[1]=b;c[2]=d;this.setOutputData(0,c)};D.registerNodeType("math3d/xyz-to-vec3",H);I.title="Vec4->XYZW";I.desc="vector 4 to components";I.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]),this.setOutputData(3, +a[3]))};D.registerNodeType("math3d/vec4-to-xyzw",I);G.title="XYZW->Vec4";G.desc="components to vector4";G.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this.getInputData(3);null==c&&(c=this.properties.w);var f=this._data;f[0]=a;f[1]=b;f[2]=d;f[3]=c;this.setOutputData(0,f)};D.registerNodeType("math3d/xyzw-to-vec4",G);y.glMatrix&&(y=function(){this.addOutput("quat", +"quat");this.properties={x:0,y:0,z:0,w:1};this._value=quat.create()},y.title="Quaternion",y.desc="quaternion",y.prototype.onExecute=function(){this._value[0]=this.properties.x;this._value[1]=this.properties.y;this._value[2]=this.properties.z;this._value[3]=this.properties.w;this.setOutputData(0,this._value)},D.registerNodeType("math3d/quaternion",y),y=function(){this.addInputs([["degrees","number"],["axis","vec3"]]);this.addOutput("quat","quat");this.properties={angle:90,axis:vec3.fromValues(0,1, +0)};this._value=quat.create()},y.title="Rotation",y.desc="quaternion rotation",y.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.angle);var b=this.getInputData(1);null==b&&(b=this.properties.axis);a=quat.setAxisAngle(this._value,b,0.0174532925*a);this.setOutputData(0,a)},D.registerNodeType("math3d/rotation",y),y=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},y.title="Rot. Vec3",y.desc= +"rotate a point",y.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.vec);var b=this.getInputData(1);null==b?this.setOutputData(a):this.setOutputData(0,vec3.transformQuat(vec3.create(),a,b))},D.registerNodeType("math3d/rotate_vec3",y),y=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},y.title="Mult. Quat",y.desc="rotate quaternion",y.prototype.onExecute=function(){var a=this.getInputData(0);if(null!= +a){var b=this.getInputData(1);null!=b&&(a=quat.multiply(this._value,a,b),this.setOutputData(0,a))}},D.registerNodeType("math3d/mult-quat",y),y=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp","quat");this.addProperty("factor",0.5);this._value=quat.create()},y.title="Quat Slerp",y.desc="quaternion spherical interpolation",y.prototype.onExecute=function(){var a=this.getInputData(0);if(null!=a){var b=this.getInputData(1);if(null!=b){var d=this.properties.factor; +null!=this.getInputData(2)&&(d=this.getInputData(2));a=quat.slerp(this._value,a,b,d);this.setOutputData(0,a)}}},D.registerNodeType("math3d/quat-slerp",y))})(this); (function(y){function c(){this.addInput("T","vec3");this.addInput("R","vec3");this.addInput("S","vec3");this.addOutput("mat4","mat4");this.properties={T:[0,0,0],R:[0,0,0],S:[1,1,1],R_in_degrees:!0};this._result=mat4.create();this._must_update=!0}function m(){this.addInput("A","number,vec3");this.addInput("B","number,vec3");this.addOutput("=","vec3");this.addProperty("OP","+","enum",{values:m.values});this._result=vec3.create()}function n(){this.addInput("in","vec3");this.addInput("f","number");this.addOutput("out", -"vec3");this.properties={f:1};this._data=new Float32Array(3)}function l(){this.addInput("in","vec3");this.addOutput("out","number")}function x(){this.addInput("in","vec3");this.addOutput("out","vec3");this._data=new Float32Array(3)}function e(){this.addInput("A","vec3");this.addInput("B","vec3");this.addInput("f","vec3");this.addOutput("out","vec3");this.properties={f:0.5};this._data=new Float32Array(3)}function C(){this.addInput("A","vec3");this.addInput("B","vec3");this.addOutput("out","number")} -var z=y.LiteGraph;c.title="mat4";c.temp_quat=new Float32Array([0,0,0,1]);c.temp_mat4=new Float32Array(16);c.temp_vec3=new Float32Array(3);c.prototype.onPropertyChanged=function(c,e){this._must_update=!0};c.prototype.onExecute=function(){var e=this._result,l=c.temp_quat,n=c.temp_mat4,g=c.temp_vec3,r=this.getInputData(0),q=this.getInputData(1),m=this.getInputData(2);if(this._must_update||r||q||m)r=r||this.properties.T,q=q||this.properties.R,m=m||this.properties.S,mat4.identity(e),mat4.translate(e,e, -r),this.properties.R_in_degrees?(g.set(q),vec3.scale(g,g,DEG2RAD),quat.fromEuler(l,g)):quat.fromEuler(l,q),mat4.fromQuat(n,l),mat4.multiply(e,e,n),mat4.scale(e,e,m);this.setOutputData(0,e)};z.registerNodeType("math3d/mat4",c);m.values="+ - * / % ^ max min".split(" ");m.title="Operation";m.desc="Easy math 3D operators";m["@OP"]={type:"enum",title:"operation",values:m.values};m.size=[100,60];m.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+ -"(A,B)":"A "+this.properties.OP+" B"};m.prototype.onExecute=function(){var c=this.getInputData(0),e=this.getInputData(1);if(null!=c&&null!=e){c.constructor===Number&&(c=[c,c,c]);e.constructor===Number&&(e=[e,e,e]);var l=this._result;switch(this.properties.OP){case "+":l=vec3.add(l,c,e);break;case "-":l=vec3.sub(l,c,e);break;case "x":case "X":case "*":l=vec3.mul(l,c,e);break;case "/":l=vec3.div(l,c,e);break;case "%":l[0]=c[0]%e[0];l[1]=c[1]%e[1];l[2]=c[2]%e[2];break;case "^":l[0]=Math.pow(c[0],e[0]); -l[1]=Math.pow(c[1],e[1]);l[2]=Math.pow(c[2],e[2]);break;case "max":l[0]=Math.max(c[0],e[0]);l[1]=Math.max(c[1],e[1]);l[2]=Math.max(c[2],e[2]);break;case "min":l[0]=Math.min(c[0],e[0]);l[1]=Math.min(c[1],e[1]);l[2]=Math.min(c[2],e[2]);break;default:console.warn("Unknown operation: "+this.properties.OP)}this.setOutputData(0,l)}};m.prototype.onDrawBackground=function(c){this.flags.collapsed||(c.font="40px Arial",c.fillStyle="#666",c.textAlign="center",c.fillText(this.properties.OP,0.5*this.size[0],0.5* -(this.size[1]+z.NODE_TITLE_HEIGHT)),c.textAlign="left")};z.registerNodeType("math3d/operation",m);n.title="vec3_scale";n.desc="scales the components of a vec3";n.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null==e&&(e=this.properties.f);var l=this._data;l[0]=c[0]*e;l[1]=c[1]*e;l[2]=c[2]*e;this.setOutputData(0,l)}};z.registerNodeType("math3d/vec3-scale",n);l.title="vec3_length";l.desc="returns the module of a vector";l.prototype.onExecute=function(){var c= -this.getInputData(0);null!=c&&(c=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),this.setOutputData(0,c))};z.registerNodeType("math3d/vec3-length",l);x.title="vec3_normalize";x.desc="returns the vector normalized";x.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),l=this._data;l[0]=c[0]/e;l[1]=c[1]/e;l[2]=c[2]/e;this.setOutputData(0,l)}};z.registerNodeType("math3d/vec3-normalize",x);e.title="vec3_lerp";e.desc="returns the interpolated vector"; -e.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);if(null!=e){var l=this.getInputOrProperty("f"),g=this._data;g[0]=c[0]*(1-l)+e[0]*l;g[1]=c[1]*(1-l)+e[1]*l;g[2]=c[2]*(1-l)+e[2]*l;this.setOutputData(0,g)}}};z.registerNodeType("math3d/vec3-lerp",e);C.title="vec3_dot";C.desc="returns the dot product";C.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null!=e&&this.setOutputData(0,c[0]*e[0]+c[1]*e[1]+c[2]* -e[2])}};z.registerNodeType("math3d/vec3-dot",C);y.glMatrix?(y=function(){this.addOutput("quat","quat");this.properties={x:0,y:0,z:0,w:1,normalize:!1};this._value=quat.create()},y.title="Quaternion",y.desc="quaternion",y.prototype.onExecute=function(){this._value[0]=this.getInputOrProperty("x");this._value[1]=this.getInputOrProperty("y");this._value[2]=this.getInputOrProperty("z");this._value[3]=this.getInputOrProperty("w");this.properties.normalize&&quat.normalize(this._value,this._value);this.setOutputData(0, -this._value)},y.prototype.onGetInputs=function(){return[["x","number"],["y","number"],["z","number"],["w","number"]]},z.registerNodeType("math3d/quaternion",y),y=function(){this.addInputs([["degrees","number"],["axis","vec3"]]);this.addOutput("quat","quat");this.properties={angle:90,axis:vec3.fromValues(0,1,0)};this._value=quat.create()},y.title="Rotation",y.desc="quaternion rotation",y.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.angle);var e=this.getInputData(1); -null==e&&(e=this.properties.axis);c=quat.setAxisAngle(this._value,e,0.0174532925*c);this.setOutputData(0,c)},z.registerNodeType("math3d/rotation",y),y=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},y.title="Rot. Vec3",y.desc="rotate a point",y.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.vec);var e=this.getInputData(1);null==e?this.setOutputData(c):this.setOutputData(0,vec3.transformQuat(vec3.create(), -c,e))},z.registerNodeType("math3d/rotate_vec3",y),y=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},y.title="Mult. Quat",y.desc="rotate quaternion",y.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null!=e&&(c=quat.multiply(this._value,c,e),this.setOutputData(0,c))}},z.registerNodeType("math3d/mult-quat",y),y=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp", -"quat");this.addProperty("factor",0.5);this._value=quat.create()},y.title="Quat Slerp",y.desc="quaternion spherical interpolation",y.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);if(null!=e){var l=this.properties.factor;null!=this.getInputData(2)&&(l=this.getInputData(2));c=quat.slerp(this._value,c,e,l);this.setOutputData(0,c)}}},z.registerNodeType("math3d/quat-slerp",y),y=function(){this.addInput("vec3","vec3");this.addOutput("remap","vec3");this.addOutput("clamped", -"vec3");this.properties={clamp:!0,range_min:[-1,-1,0],range_max:[1,1,0],target_min:[-1,-1,0],target_max:[1,1,0]};this._value=vec3.create();this._clamped=vec3.create()},y.title="Remap Range",y.desc="remap a 3D range",y.prototype.onExecute=function(){var c=this.getInputData(0);c&&this._value.set(c);for(var c=this.properties.range_min,e=this.properties.range_max,l=this.properties.target_min,g=this.properties.target_max,r=0;3>r;++r){var q=e[r]-c[r];this._clamped[r]=Math.clamp(this._value[r],c[r],e[r]); -0==q?this._value[r]=0.5*(l[r]+g[r]):(q=(this._value[r]-c[r])/q,this.properties.clamp&&(q=Math.clamp(q,0,1)),this._value[r]=l[r]+q*(g[r]-l[r]))}this.setOutputData(0,this._value);this.setOutputData(1,this._clamped)},z.registerNodeType("math3d/remap_range",y)):console.warn("No glmatrix found, some Math3D nodes may not work")})(this); -(function(y){function c(c,l){return c==l}function m(c){return null!=c&&c.constructor===String?c.toUpperCase():c}y=y.LiteGraph;y.wrapFunctionAsNode("string/toString",c,["*"],"String");y.wrapFunctionAsNode("string/compare",c,["String","String"],"Boolean");y.wrapFunctionAsNode("string/concatenate",function(c,l){return void 0===c?l:void 0===l?c:c+l},["String","String"],"String");y.wrapFunctionAsNode("string/contains",function(c,l){return void 0===c||void 0===l?!1:-1!=c.indexOf(l)},["String","String"], +"vec3");this.properties={f:1};this._data=new Float32Array(3)}function k(){this.addInput("in","vec3");this.addOutput("out","number")}function x(){this.addInput("in","vec3");this.addOutput("out","vec3");this._data=new Float32Array(3)}function g(){this.addInput("A","vec3");this.addInput("B","vec3");this.addInput("f","vec3");this.addOutput("out","vec3");this.properties={f:0.5};this._data=new Float32Array(3)}function C(){this.addInput("A","vec3");this.addInput("B","vec3");this.addOutput("out","number")} +var z=y.LiteGraph;c.title="mat4";c.temp_quat=new Float32Array([0,0,0,1]);c.temp_mat4=new Float32Array(16);c.temp_vec3=new Float32Array(3);c.prototype.onPropertyChanged=function(c,g){this._must_update=!0};c.prototype.onExecute=function(){var g=this._result,k=c.temp_quat,n=c.temp_mat4,e=c.temp_vec3,q=this.getInputData(0),t=this.getInputData(1),m=this.getInputData(2);if(this._must_update||q||t||m)q=q||this.properties.T,t=t||this.properties.R,m=m||this.properties.S,mat4.identity(g),mat4.translate(g,g, +q),this.properties.R_in_degrees?(e.set(t),vec3.scale(e,e,DEG2RAD),quat.fromEuler(k,e)):quat.fromEuler(k,t),mat4.fromQuat(n,k),mat4.multiply(g,g,n),mat4.scale(g,g,m);this.setOutputData(0,g)};z.registerNodeType("math3d/mat4",c);m.values="+ - * / % ^ max min".split(" ");m.title="Operation";m.desc="Easy math 3D operators";m["@OP"]={type:"enum",title:"operation",values:m.values};m.size=[100,60];m.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+ +"(A,B)":"A "+this.properties.OP+" B"};m.prototype.onExecute=function(){var c=this.getInputData(0),g=this.getInputData(1);if(null!=c&&null!=g){c.constructor===Number&&(c=[c,c,c]);g.constructor===Number&&(g=[g,g,g]);var k=this._result;switch(this.properties.OP){case "+":k=vec3.add(k,c,g);break;case "-":k=vec3.sub(k,c,g);break;case "x":case "X":case "*":k=vec3.mul(k,c,g);break;case "/":k=vec3.div(k,c,g);break;case "%":k[0]=c[0]%g[0];k[1]=c[1]%g[1];k[2]=c[2]%g[2];break;case "^":k[0]=Math.pow(c[0],g[0]); +k[1]=Math.pow(c[1],g[1]);k[2]=Math.pow(c[2],g[2]);break;case "max":k[0]=Math.max(c[0],g[0]);k[1]=Math.max(c[1],g[1]);k[2]=Math.max(c[2],g[2]);break;case "min":k[0]=Math.min(c[0],g[0]);k[1]=Math.min(c[1],g[1]);k[2]=Math.min(c[2],g[2]);break;default:console.warn("Unknown operation: "+this.properties.OP)}this.setOutputData(0,k)}};m.prototype.onDrawBackground=function(c){this.flags.collapsed||(c.font="40px Arial",c.fillStyle="#666",c.textAlign="center",c.fillText(this.properties.OP,0.5*this.size[0],0.5* +(this.size[1]+z.NODE_TITLE_HEIGHT)),c.textAlign="left")};z.registerNodeType("math3d/operation",m);n.title="vec3_scale";n.desc="scales the components of a vec3";n.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var g=this.getInputData(1);null==g&&(g=this.properties.f);var k=this._data;k[0]=c[0]*g;k[1]=c[1]*g;k[2]=c[2]*g;this.setOutputData(0,k)}};z.registerNodeType("math3d/vec3-scale",n);k.title="vec3_length";k.desc="returns the module of a vector";k.prototype.onExecute=function(){var c= +this.getInputData(0);null!=c&&(c=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),this.setOutputData(0,c))};z.registerNodeType("math3d/vec3-length",k);x.title="vec3_normalize";x.desc="returns the vector normalized";x.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var g=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),k=this._data;k[0]=c[0]/g;k[1]=c[1]/g;k[2]=c[2]/g;this.setOutputData(0,k)}};z.registerNodeType("math3d/vec3-normalize",x);g.title="vec3_lerp";g.desc="returns the interpolated vector"; +g.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var g=this.getInputData(1);if(null!=g){var k=this.getInputOrProperty("f"),e=this._data;e[0]=c[0]*(1-k)+g[0]*k;e[1]=c[1]*(1-k)+g[1]*k;e[2]=c[2]*(1-k)+g[2]*k;this.setOutputData(0,e)}}};z.registerNodeType("math3d/vec3-lerp",g);C.title="vec3_dot";C.desc="returns the dot product";C.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var g=this.getInputData(1);null!=g&&this.setOutputData(0,c[0]*g[0]+c[1]*g[1]+c[2]* +g[2])}};z.registerNodeType("math3d/vec3-dot",C);y.glMatrix?(y=function(){this.addOutput("quat","quat");this.properties={x:0,y:0,z:0,w:1,normalize:!1};this._value=quat.create()},y.title="Quaternion",y.desc="quaternion",y.prototype.onExecute=function(){this._value[0]=this.getInputOrProperty("x");this._value[1]=this.getInputOrProperty("y");this._value[2]=this.getInputOrProperty("z");this._value[3]=this.getInputOrProperty("w");this.properties.normalize&&quat.normalize(this._value,this._value);this.setOutputData(0, +this._value)},y.prototype.onGetInputs=function(){return[["x","number"],["y","number"],["z","number"],["w","number"]]},z.registerNodeType("math3d/quaternion",y),y=function(){this.addInputs([["degrees","number"],["axis","vec3"]]);this.addOutput("quat","quat");this.properties={angle:90,axis:vec3.fromValues(0,1,0)};this._value=quat.create()},y.title="Rotation",y.desc="quaternion rotation",y.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.angle);var g=this.getInputData(1); +null==g&&(g=this.properties.axis);c=quat.setAxisAngle(this._value,g,0.0174532925*c);this.setOutputData(0,c)},z.registerNodeType("math3d/rotation",y),y=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},y.title="Rot. Vec3",y.desc="rotate a point",y.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.vec);var g=this.getInputData(1);null==g?this.setOutputData(c):this.setOutputData(0,vec3.transformQuat(vec3.create(), +c,g))},z.registerNodeType("math3d/rotate_vec3",y),y=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},y.title="Mult. Quat",y.desc="rotate quaternion",y.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var g=this.getInputData(1);null!=g&&(c=quat.multiply(this._value,c,g),this.setOutputData(0,c))}},z.registerNodeType("math3d/mult-quat",y),y=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp", +"quat");this.addProperty("factor",0.5);this._value=quat.create()},y.title="Quat Slerp",y.desc="quaternion spherical interpolation",y.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var g=this.getInputData(1);if(null!=g){var k=this.properties.factor;null!=this.getInputData(2)&&(k=this.getInputData(2));c=quat.slerp(this._value,c,g,k);this.setOutputData(0,c)}}},z.registerNodeType("math3d/quat-slerp",y),y=function(){this.addInput("vec3","vec3");this.addOutput("remap","vec3");this.addOutput("clamped", +"vec3");this.properties={clamp:!0,range_min:[-1,-1,0],range_max:[1,1,0],target_min:[-1,-1,0],target_max:[1,1,0]};this._value=vec3.create();this._clamped=vec3.create()},y.title="Remap Range",y.desc="remap a 3D range",y.prototype.onExecute=function(){var c=this.getInputData(0);c&&this._value.set(c);for(var c=this.properties.range_min,g=this.properties.range_max,k=this.properties.target_min,e=this.properties.target_max,q=0;3>q;++q){var n=g[q]-c[q];this._clamped[q]=Math.clamp(this._value[q],c[q],g[q]); +0==n?this._value[q]=0.5*(k[q]+e[q]):(n=(this._value[q]-c[q])/n,this.properties.clamp&&(n=Math.clamp(n,0,1)),this._value[q]=k[q]+n*(e[q]-k[q]))}this.setOutputData(0,this._value);this.setOutputData(1,this._clamped)},z.registerNodeType("math3d/remap_range",y)):console.warn("No glmatrix found, some Math3D nodes may not work")})(this); +(function(y){function c(c,k){return c==k}function m(c){return null!=c&&c.constructor===String?c.toUpperCase():c}y=y.LiteGraph;y.wrapFunctionAsNode("string/toString",c,["*"],"String");y.wrapFunctionAsNode("string/compare",c,["String","String"],"Boolean");y.wrapFunctionAsNode("string/concatenate",function(c,k){return void 0===c?k:void 0===k?c:c+k},["String","String"],"String");y.wrapFunctionAsNode("string/contains",function(c,k){return void 0===c||void 0===k?!1:-1!=c.indexOf(k)},["String","String"], "Boolean");y.wrapFunctionAsNode("string/toUpperCase",m,["String"],"String");y.wrapFunctionAsNode("string/split",m,["String","String"],"Array");y.wrapFunctionAsNode("string/toFixed",function(c){return null!=c&&c.constructor===Number?c.toFixed(this.properties.precision):c},["Number"],"String",{precision:0})})(this); (function(y){function c(){this.addInput("sel","number");this.addInput("A");this.addInput("B");this.addInput("C");this.addInput("D");this.addOutput("out");this.selected=0}function m(){this.properties={sequence:"A,B,C"};this.addInput("index","number");this.addInput("seq");this.addOutput("out");this.index=0;this.values=this.properties.sequence.split(",")}var n=y.LiteGraph;c.title="Selector";c.desc="selects an output";c.prototype.onDrawBackground=function(c){if(!this.flags.collapsed){c.fillStyle="#AFB"; var m=(this.selected+1)*n.NODE_SLOT_HEIGHT+6;c.beginPath();c.moveTo(50,m);c.lineTo(50,m+n.NODE_SLOT_HEIGHT);c.lineTo(34,m+0.5*n.NODE_SLOT_HEIGHT);c.fill()}};c.prototype.onExecute=function(){var c=this.getInputData(0);if(null==c||c.constructor!==Number)c=0;this.selected=c=Math.round(c)%(this.inputs.length-1);c=this.getInputData(c+1);void 0!==c&&this.setOutputData(0,c)};c.prototype.onGetInputs=function(){return[["E",0],["F",0],["G",0],["H",0]]};n.registerNodeType("logic/selector",c);m.title="Sequence"; m.desc="select one element from a sequence from a string";m.prototype.onPropertyChanged=function(c,n){"sequence"==c&&(this.values=n.split(","))};m.prototype.onExecute=function(){var c=this.getInputData(1);c&&c!=this.current_sequence&&(this.values=c.split(","),this.current_sequence=c);c=this.getInputData(0);null==c&&(c=0);this.index=c=Math.round(c)%this.values.length;this.setOutputData(0,this.values[c])};n.registerNodeType("logic/sequence",m)})(this); -(function(y){function c(){this.addInput("A","Number");this.addInput("B","Number");this.addInput("C","Number");this.addInput("D","Number");this.values=[[],[],[],[]];this.properties={scale:2}}function m(){this.addOutput("frame","image");this.properties={url:""}}function n(){this.addInput("f","number");this.addOutput("Color","color");this.properties={colorA:"#444444",colorB:"#44AAFF",colorC:"#44FFAA",colorD:"#FFFFFF"}}function l(){this.addInput("","image,canvas");this.size=[200,200]}function x(){this.addInputs([["img1", -"image"],["img2","image"],["fade","number"]]);this.addOutput("","image");this.properties={fade:0.5,width:512,height:512}}function e(){this.addInput("","image");this.addOutput("","image");this.properties={width:256,height:256,x:0,y:0,scale:1};this.size=[50,20]}function C(){this.addInput("clear",g.ACTION);this.addOutput("","canvas");this.properties={width:512,height:512,autoclear:!0};this.canvas=document.createElement("canvas");this.ctx=this.canvas.getContext("2d")}function z(){this.addInput("canvas", -"canvas");this.addInput("img","image,canvas");this.addInput("x","number");this.addInput("y","number");this.properties={x:0,y:0,opacity:1}}function v(){this.addInput("canvas","canvas");this.addInput("x","number");this.addInput("y","number");this.addInput("w","number");this.addInput("h","number");this.properties={x:0,y:0,w:10,h:10,color:"white",opacity:1}}function D(){this.addInput("t","number");this.addOutputs([["frame","image"],["t","number"],["d","number"]]);this.properties={url:"",use_proxy:!0}} -function B(){this.addOutput("Webcam","image");this.properties={facingMode:"user"};this.boxcolor="black";this.frame=0}var g=y.LiteGraph;c.title="Plot";c.desc="Plots data over time";c.colors=["#FFF","#F99","#9F9","#99F"];c.prototype.onExecute=function(c){if(!this.flags.collapsed){c=this.size;for(var e=0;4>e;++e){var g=this.getInputData(e);if(null!=g){var s=this.values[e];s.push(g);s.length>c[0]&&s.shift()}}}};c.prototype.onDrawBackground=function(e){if(!this.flags.collapsed){var g=this.size,l=0.5*g[1]/ -this.properties.scale,s=c.colors,p=0.5*g[1];e.fillStyle="#000";e.fillRect(0,0,g[0],g[1]);e.strokeStyle="#555";e.beginPath();e.moveTo(0,p);e.lineTo(g[0],p);e.stroke();if(this.inputs)for(var n=0;4>n;++n){var h=this.values[n];if(this.inputs[n]&&this.inputs[n].link){e.strokeStyle=s[n];e.beginPath();var a=h[0]*l*-1+p;e.moveTo(0,Math.clamp(a,0,g[1]));for(var b=1;be;++e){var g=this.getInputData(e);if(null!=g){var p=this.values[e];p.push(g);p.length>c[0]&&p.shift()}}}};c.prototype.onDrawBackground=function(e){if(!this.flags.collapsed){var g=this.size,k=0.5*g[1]/ +this.properties.scale,p=c.colors,s=0.5*g[1];e.fillStyle="#000";e.fillRect(0,0,g[0],g[1]);e.strokeStyle="#555";e.beginPath();e.moveTo(0,s);e.lineTo(g[0],s);e.stroke();if(this.inputs)for(var n=0;4>n;++n){var l=this.values[n];if(this.inputs[n]&&this.inputs[n].link){e.strokeStyle=p[n];e.beginPath();var a=l[0]*k*-1+s;e.moveTo(0,Math.clamp(a,0,g[1]));for(var b=1;be&&(e=0);if(0!=c.length){var g=[0,0,0];if(0==e)g=c[0];else if(1==e)g=c[c.length-1];else{var s=(c.length-1)* -e,e=c[Math.floor(s)],c=c[Math.floor(s)+1],s=s-Math.floor(s);g[0]=e[0]*(1-s)+c[0]*s;g[1]=e[1]*(1-s)+c[1]*s;g[2]=e[2]*(1-s)+c[2]*s}for(var p in g)g[p]/=255;this.boxcolor=colorToString(g);this.setOutputData(0,g)}};g.registerNodeType("color/palette",n);l.title="Frame";l.desc="Frame viewerew";l.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}];l.prototype.onDrawBackground=function(c){this.frame&&!this.flags.collapsed&&c.drawImage(this.frame,0,0,this.size[0], -this.size[1])};l.prototype.onExecute=function(){this.frame=this.getInputData(0);this.setDirtyCanvas(!0)};l.prototype.onWidget=function(c,e){if("resize"==e.name&&this.frame){var g=this.frame.width,s=this.frame.height;g||null==this.frame.videoWidth||(g=this.frame.videoWidth,s=this.frame.videoHeight);g&&s&&(this.size=[g,s]);this.setDirtyCanvas(!0,!0)}else"view"==e.name&&this.show()};l.prototype.show=function(){showElement&&this.frame&&showElement(this.frame)};g.registerNodeType("graphics/frame",l);x.title= +this.img):this.setOutputData(0,null);this.img&&this.img.dirty&&(this.img.dirty=!1)};m.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"url"==c&&""!=e&&this.loadImage(e);return!0};m.prototype.loadImage=function(c,g){if(""==c)this.img=null;else{this.img=document.createElement("img");"http"==c.substr(0,4)&&e.proxy&&(c=e.proxy+c.substr(c.indexOf(":")+3));this.img.src=c;this.boxcolor="#F95";var k=this;this.img.onload=function(){g&&g(this);k.trace("Image loaded, size: "+k.img.width+"x"+k.img.height); +this.dirty=!0;k.boxcolor="#9F9";k.setDirtyCanvas(!0)};this.img.onerror=function(){console.log("error loading the image:"+c)}}};m.prototype.onWidget=function(c,e){"load"==e.name&&this.loadImage(this.properties.url)};m.prototype.onDropFile=function(c){var e=this;this._url&&URL.revokeObjectURL(this._url);this._url=URL.createObjectURL(c);this.properties.url=this._url;this.loadImage(this._url,function(c){e.size[1]=c.height/c.width*e.size[0]})};e.registerNodeType("graphics/image",m);n.title="Palette";n.desc= +"Generates a color";n.prototype.onExecute=function(){var c=[];null!=this.properties.colorA&&c.push(hex2num(this.properties.colorA));null!=this.properties.colorB&&c.push(hex2num(this.properties.colorB));null!=this.properties.colorC&&c.push(hex2num(this.properties.colorC));null!=this.properties.colorD&&c.push(hex2num(this.properties.colorD));var e=this.getInputData(0);null==e&&(e=0.5);1e&&(e=0);if(0!=c.length){var g=[0,0,0];if(0==e)g=c[0];else if(1==e)g=c[c.length-1];else{var p=(c.length-1)* +e,e=c[Math.floor(p)],c=c[Math.floor(p)+1],p=p-Math.floor(p);g[0]=e[0]*(1-p)+c[0]*p;g[1]=e[1]*(1-p)+c[1]*p;g[2]=e[2]*(1-p)+c[2]*p}for(var k in g)g[k]/=255;this.boxcolor=colorToString(g);this.setOutputData(0,g)}};e.registerNodeType("color/palette",n);k.title="Frame";k.desc="Frame viewerew";k.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}];k.prototype.onDrawBackground=function(c){this.frame&&!this.flags.collapsed&&c.drawImage(this.frame,0,0,this.size[0], +this.size[1])};k.prototype.onExecute=function(){this.frame=this.getInputData(0);this.setDirtyCanvas(!0)};k.prototype.onWidget=function(c,e){if("resize"==e.name&&this.frame){var g=this.frame.width,p=this.frame.height;g||null==this.frame.videoWidth||(g=this.frame.videoWidth,p=this.frame.videoHeight);g&&p&&(this.size=[g,p]);this.setDirtyCanvas(!0,!0)}else"view"==e.name&&this.show()};k.prototype.show=function(){showElement&&this.frame&&showElement(this.frame)};e.registerNodeType("graphics/frame",k);x.title= "Image fade";x.desc="Fades between images";x.widgets=[{name:"resizeA",text:"Resize to A",type:"button"},{name:"resizeB",text:"Resize to B",type:"button"}];x.prototype.onAdded=function(){this.createCanvas();var c=this.canvas.getContext("2d");c.fillStyle="#000";c.fillRect(0,0,this.properties.width,this.properties.height)};x.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};x.prototype.onExecute= -function(){var c=this.canvas.getContext("2d");this.canvas.width=this.canvas.width;var e=this.getInputData(0);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);e=this.getInputData(2);null==e?e=this.properties.fade:this.properties.fade=e;c.globalAlpha=e;e=this.getInputData(1);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);c.globalAlpha=1;this.setOutputData(0,this.canvas);this.setDirtyCanvas(!0)};g.registerNodeType("graphics/imagefade",x);e.title="Crop";e.desc="Crop Image"; -e.prototype.onAdded=function(){this.createCanvas()};e.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};e.prototype.onExecute=function(){var c=this.getInputData(0);c&&(c.width?(this.canvas.getContext("2d").drawImage(c,-this.properties.x,-this.properties.y,c.width*this.properties.scale,c.height*this.properties.scale),this.setOutputData(0,this.canvas)):this.setOutputData(0,null))};e.prototype.onDrawBackground= -function(c){this.flags.collapsed||this.canvas&&c.drawImage(this.canvas,0,0,this.canvas.width,this.canvas.height,0,0,this.size[0],this.size[1])};e.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"scale"==c?(this.properties[c]=parseFloat(e),0==this.properties[c]&&(this.trace("Error in scale"),this.properties[c]=1)):this.properties[c]=parseInt(e);this.createCanvas();return!0};g.registerNodeType("graphics/cropImage",e);C.title="Canvas";C.desc="Canvas to render stuff";C.prototype.onExecute= -function(){var c=this.canvas,e=this.properties.width|0,g=this.properties.height|0;c.width!=e&&(c.width=e);c.height!=g&&(c.height=g);this.properties.autoclear&&this.ctx.clearRect(0,0,c.width,c.height);this.setOutputData(0,c)};C.prototype.onAction=function(c,e){"clear"==c&&this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)};g.registerNodeType("graphics/canvas",C);z.title="DrawImage";z.desc="Draws image into a canvas";z.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e= -this.getInputOrProperty("img");if(e){var g=this.getInputOrProperty("x"),s=this.getInputOrProperty("y");c.getContext("2d").drawImage(e,g,s)}}};g.registerNodeType("graphics/drawImage",z);v.title="DrawRectangle";v.desc="Draws rectangle in canvas";v.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e=this.getInputOrProperty("x"),g=this.getInputOrProperty("y"),s=this.getInputOrProperty("w"),p=this.getInputOrProperty("h");c.getContext("2d").fillRect(e,g,s,p)}};g.registerNodeType("graphics/drawRectangle", -v);D.title="Video";D.desc="Video playback";D.widgets=[{name:"play",text:"PLAY",type:"minibutton"},{name:"stop",text:"STOP",type:"minibutton"},{name:"demo",text:"Demo video",type:"button"},{name:"mute",text:"Mute video",type:"button"}];D.prototype.onExecute=function(){if(this.properties.url&&(this.properties.url!=this._video_url&&this.loadVideo(this.properties.url),this._video&&0!=this._video.width)){var c=this.getInputData(0);c&&0<=c&&1>=c&&(this._video.currentTime=c*this._video.duration,this._video.pause()); -this._video.dirty=!0;this.setOutputData(0,this._video);this.setOutputData(1,this._video.currentTime);this.setOutputData(2,this._video.duration);this.setDirtyCanvas(!0)}};D.prototype.onStart=function(){this.play()};D.prototype.onStop=function(){this.stop()};D.prototype.loadVideo=function(c){this._video_url=c;this.properties.use_proxy&&"http"==c.substr(0,4)&&g.proxy&&(c=g.proxy+c.substr(c.indexOf(":")+3));this._video=document.createElement("video");this._video.src=c;this._video.type="type=video/mp4"; -this._video.muted=!0;this._video.autoplay=!0;var e=this;this._video.addEventListener("loadedmetadata",function(c){e.trace("Duration: "+this.duration+" seconds");e.trace("Size: "+this.videoWidth+","+this.videoHeight);e.setDirtyCanvas(!0);this.width=this.videoWidth;this.height=this.videoHeight});this._video.addEventListener("progress",function(c){});this._video.addEventListener("error",function(c){console.log("Error loading video: "+this.src);e.trace("Error loading video: "+this.src);if(this.error)switch(this.error.code){case this.error.MEDIA_ERR_ABORTED:e.trace("You stopped the video."); -break;case this.error.MEDIA_ERR_NETWORK:e.trace("Network error - please try again later.");break;case this.error.MEDIA_ERR_DECODE:e.trace("Video is broken..");break;case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED:e.trace("Sorry, your browser can't play this video.")}});this._video.addEventListener("ended",function(c){e.trace("Ended.");this.play()})};D.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"url"==c&&""!=e&&this.loadVideo(e);return!0};D.prototype.play=function(){this._video&&this._video.play()}; -D.prototype.playPause=function(){this._video&&(this._video.paused?this.play():this.pause())};D.prototype.stop=function(){this._video&&(this._video.pause(),this._video.currentTime=0)};D.prototype.pause=function(){this._video&&(this.trace("Video paused"),this._video.pause())};D.prototype.onWidget=function(c,e){};g.registerNodeType("graphics/video",D);B.title="Webcam";B.desc="Webcam image";B.is_webcam_open=!1;B.prototype.openStream=function(){function c(g){console.log("Webcam rejected",g);e._webcam_stream= +function(){var c=this.canvas.getContext("2d");this.canvas.width=this.canvas.width;var e=this.getInputData(0);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);e=this.getInputData(2);null==e?e=this.properties.fade:this.properties.fade=e;c.globalAlpha=e;e=this.getInputData(1);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);c.globalAlpha=1;this.setOutputData(0,this.canvas);this.setDirtyCanvas(!0)};e.registerNodeType("graphics/imagefade",x);g.title="Crop";g.desc="Crop Image"; +g.prototype.onAdded=function(){this.createCanvas()};g.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};g.prototype.onExecute=function(){var c=this.getInputData(0);c&&(c.width?(this.canvas.getContext("2d").drawImage(c,-this.properties.x,-this.properties.y,c.width*this.properties.scale,c.height*this.properties.scale),this.setOutputData(0,this.canvas)):this.setOutputData(0,null))};g.prototype.onDrawBackground= +function(c){this.flags.collapsed||this.canvas&&c.drawImage(this.canvas,0,0,this.canvas.width,this.canvas.height,0,0,this.size[0],this.size[1])};g.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"scale"==c?(this.properties[c]=parseFloat(e),0==this.properties[c]&&(this.trace("Error in scale"),this.properties[c]=1)):this.properties[c]=parseInt(e);this.createCanvas();return!0};e.registerNodeType("graphics/cropImage",g);C.title="Canvas";C.desc="Canvas to render stuff";C.prototype.onExecute= +function(){var c=this.canvas,e=this.properties.width|0,g=this.properties.height|0;c.width!=e&&(c.width=e);c.height!=g&&(c.height=g);this.properties.autoclear&&this.ctx.clearRect(0,0,c.width,c.height);this.setOutputData(0,c)};C.prototype.onAction=function(c,e){"clear"==c&&this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)};e.registerNodeType("graphics/canvas",C);z.title="DrawImage";z.desc="Draws image into a canvas";z.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e= +this.getInputOrProperty("img");if(e){var g=this.getInputOrProperty("x"),p=this.getInputOrProperty("y");c.getContext("2d").drawImage(e,g,p)}}};e.registerNodeType("graphics/drawImage",z);v.title="DrawRectangle";v.desc="Draws rectangle in canvas";v.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e=this.getInputOrProperty("x"),g=this.getInputOrProperty("y"),p=this.getInputOrProperty("w"),k=this.getInputOrProperty("h");c.getContext("2d").fillRect(e,g,p,k)}};e.registerNodeType("graphics/drawRectangle", +v);E.title="Video";E.desc="Video playback";E.widgets=[{name:"play",text:"PLAY",type:"minibutton"},{name:"stop",text:"STOP",type:"minibutton"},{name:"demo",text:"Demo video",type:"button"},{name:"mute",text:"Mute video",type:"button"}];E.prototype.onExecute=function(){if(this.properties.url&&(this.properties.url!=this._video_url&&this.loadVideo(this.properties.url),this._video&&0!=this._video.width)){var c=this.getInputData(0);c&&0<=c&&1>=c&&(this._video.currentTime=c*this._video.duration,this._video.pause()); +this._video.dirty=!0;this.setOutputData(0,this._video);this.setOutputData(1,this._video.currentTime);this.setOutputData(2,this._video.duration);this.setDirtyCanvas(!0)}};E.prototype.onStart=function(){this.play()};E.prototype.onStop=function(){this.stop()};E.prototype.loadVideo=function(c){this._video_url=c;this.properties.use_proxy&&"http"==c.substr(0,4)&&e.proxy&&(c=e.proxy+c.substr(c.indexOf(":")+3));this._video=document.createElement("video");this._video.src=c;this._video.type="type=video/mp4"; +this._video.muted=!0;this._video.autoplay=!0;var g=this;this._video.addEventListener("loadedmetadata",function(c){g.trace("Duration: "+this.duration+" seconds");g.trace("Size: "+this.videoWidth+","+this.videoHeight);g.setDirtyCanvas(!0);this.width=this.videoWidth;this.height=this.videoHeight});this._video.addEventListener("progress",function(c){});this._video.addEventListener("error",function(c){console.log("Error loading video: "+this.src);g.trace("Error loading video: "+this.src);if(this.error)switch(this.error.code){case this.error.MEDIA_ERR_ABORTED:g.trace("You stopped the video."); +break;case this.error.MEDIA_ERR_NETWORK:g.trace("Network error - please try again later.");break;case this.error.MEDIA_ERR_DECODE:g.trace("Video is broken..");break;case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED:g.trace("Sorry, your browser can't play this video.")}});this._video.addEventListener("ended",function(c){g.trace("Ended.");this.play()})};E.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"url"==c&&""!=e&&this.loadVideo(e);return!0};E.prototype.play=function(){this._video&&this._video.play()}; +E.prototype.playPause=function(){this._video&&(this._video.paused?this.play():this.pause())};E.prototype.stop=function(){this._video&&(this._video.pause(),this._video.currentTime=0)};E.prototype.pause=function(){this._video&&(this.trace("Video paused"),this._video.pause())};E.prototype.onWidget=function(c,e){};e.registerNodeType("graphics/video",E);B.title="Webcam";B.desc="Webcam image";B.is_webcam_open=!1;B.prototype.openStream=function(){function c(g){console.log("Webcam rejected",g);e._webcam_stream= !1;B.is_webcam_open=!1;e.boxcolor="red";e.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](c);var e=this}};B.prototype.closeStream=function(){if(this._webcam_stream){var c=this._webcam_stream.getTracks();if(c.length)for(var e=0;e=this.size[1]||!this.properties.show||!this._video||(c.save(),c.drawImage(this._video,0,0,this.size[0],this.size[1]),c.restore())};B.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["stream_ready",g.EVENT],["stream_closed",g.EVENT],["stream_error",g.EVENT]]}; -g.registerNodeType("graphics/webcam",B)})(this); -(function(y){function c(){this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",filter:!0};this.size=[c.image_preview_size,c.image_preview_size]}function m(){this.addInput("Texture","Texture");this.properties={flipY:!1};this.size=[c.image_preview_size,c.image_preview_size]}function n(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",generate_mipmaps:!1}}function l(){this.addInput("Texture", +function(c){var e=this;return[{content:e.properties.show?"Hide Frame":"Show Frame",callback:function(){e.properties.show=!e.properties.show}}]};B.prototype.onDrawBackground=function(c){this.flags.collapsed||20>=this.size[1]||!this.properties.show||!this._video||(c.save(),c.drawImage(this._video,0,0,this.size[0],this.size[1]),c.restore())};B.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["stream_ready",e.EVENT],["stream_closed",e.EVENT],["stream_error",e.EVENT]]}; +e.registerNodeType("graphics/webcam",B)})(this); +(function(y){function c(){this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",filter:!0};this.size=[c.image_preview_size,c.image_preview_size]}function m(){this.addInput("Texture","Texture");this.properties={flipY:!1};this.size=[c.image_preview_size,c.image_preview_size]}function n(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",generate_mipmaps:!1}}function k(){this.addInput("Texture", "Texture");this.addInput("TextureB","Texture");this.addInput("value","number");this.addOutput("Texture","Texture");this.help="

pixelcode must be vec3, uvcode must be vec2, is optional

\t\t

uv: tex. coords

color: texture colorB: textureB

time: scene time value: input value

For multiline you must type: result = ...

";this.properties={value:1,pixelcode:"color + colorB * value",uvcode:"",precision:c.DEFAULT}; -this.has_error=!1}function x(){this.addOutput("out","Texture");this.properties={code:"",u_value:1,u_color:[1,1,1,1],width:512,height:512,precision:c.DEFAULT};this.properties.code="//time: time in seconds\n//texSize: vec2 with res\nuniform float u_value;\nuniform vec4 u_color;\n\nvoid main() {\n vec2 uv = v_coord;\n vec3 color = vec3(0.0);\n\t//your code here\n\tcolor.xy=uv;\n\ngl_FragColor = vec4(color, 1.0);\n}\n";this._uniforms={u_value:1,u_color:vec4.create(),in_texture:0,texSize:vec2.create(), -time:0}}function e(){this.addInput("in","Texture");this.addInput("scale","vec2");this.addInput("offset","vec2");this.addOutput("out","Texture");this.properties={offset:vec2.fromValues(0,0),scale:vec2.fromValues(1,1),precision:c.DEFAULT}}function C(){this.addInput("in","Texture");this.addInput("warp","Texture");this.addInput("factor","number");this.addOutput("out","Texture");this.properties={factor:0.01,scale:[1,1],offset:[0,0],precision:c.DEFAULT};this._uniforms={u_texture:0,u_textureB:1,u_factor:1, -u_scale:vec2.create(),u_offset:vec2.create()}}function z(){this.addInput("Texture","Texture");this.properties={additive:!1,antialiasing:!1,filter:!0,disable_alpha:!1,gamma:1,viewport:[0,0,1,1]};this.size[0]=130}function v(){this.addInput("Texture","Texture");this.addOutput("","Texture");this.properties={size:0,generate_mipmaps:!1,precision:c.DEFAULT}}function D(){this.addInput("Texture","Texture");this.addOutput("","Texture");this.properties={iterations:1,generate_mipmaps:!1,precision:c.DEFAULT}} -function B(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("avg","vec4");this.addOutput("lum","number");this.properties={use_previous_frame:!0,high_quality:!1};this._uniforms={u_texture:0,u_mipmap_offset:0};this._luminance=new Float32Array(4)}function g(){this.addInput("Texture","Texture");this.addOutput("min_t","Texture");this.addOutput("max_t","Texture");this.addOutput("min","vec4");this.addOutput("max","vec4");this.properties={mode:"max",use_previous_frame:!0}; -this._uniforms={u_texture:0};this._max=new Float32Array(4);this._min=new Float32Array(4);this._textures_chain=[]}function r(){this.addInput("in","Texture");this.addInput("factor","Number");this.addOutput("out","Texture");this.properties={factor:0.5};this._uniforms={u_texture:0,u_textureB:1,u_factor:this.properties.factor}}function q(){this.addInput("in","Texture");this.addOutput("avg","Texture");this.addOutput("array","Texture");this.properties={samples:64,frames_interval:1};this._uniforms={u_texture:0, -u_textureB:1,u_samples:this.properties.samples,u_isamples:1/this.properties.samples};this.frame=0}function A(){this.addInput("Image","image");this.addOutput("","Texture");this.properties={}}function s(){this.addInput("Texture","Texture");this.addInput("LUT","Texture");this.addInput("Intensity","number");this.addOutput("","Texture");this.properties={enabled:!0,intensity:1,precision:c.DEFAULT,texture:null};s._shader||(s._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,s.pixel_shader))}function p(){this.addInput("Texture", -"Texture");this.addOutput("R","Texture");this.addOutput("G","Texture");this.addOutput("B","Texture");this.addOutput("A","Texture");p._shader||(p._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,p.pixel_shader))}function t(){this.addInput("R","Texture");this.addInput("G","Texture");this.addInput("B","Texture");this.addInput("A","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,R:1,G:1,B:1,A:1};this._color=vec4.create();this._uniforms={u_textureR:0,u_textureG:1,u_textureB:2, -u_textureA:3,u_color:this._color}}function h(){this.addOutput("Texture","Texture");this._tex_color=vec4.create();this.properties={color:vec4.create(),precision:c.DEFAULT}}function a(){this.addInput("A","color");this.addInput("B","color");this.addOutput("Texture","Texture");this.properties={angle:0,scale:1,A:[0,0,0],B:[1,1,1],texture_size:32};a._shader||(a._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,a.pixel_shader));this._uniforms={u_angle:0,u_colorA:vec3.create(),u_colorB:vec3.create()}}function b(){this.addInput("A", -"Texture");this.addInput("B","Texture");this.addInput("Mixer","Texture");this.addOutput("Texture","Texture");this.properties={factor:0.5,size_from_biggest:!0,invert:!1,precision:c.DEFAULT};this._uniforms={u_textureA:0,u_textureB:1,u_textureMix:2,u_mix:vec4.create()}}function d(){this.addInput("Tex.","Texture");this.addOutput("Edges","Texture");this.properties={invert:!0,threshold:!1,factor:1,precision:c.DEFAULT};d._shader||(d._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,d.pixel_shader))}function k(){this.addInput("Texture", -"Texture");this.addInput("Distance","number");this.addInput("Range","number");this.addOutput("Texture","Texture");this.properties={distance:100,range:50,only_depth:!1,high_precision:!1};this._uniforms={u_texture:0,u_distance:100,u_range:50,u_camera_planes:null}}function f(){this.addInput("Texture","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,invert:!1};this._uniforms={u_texture:0,u_near:0.1,u_far:1E4}}function K(){this.addInput("Texture","Texture");this.addInput("Iterations", -"number");this.addInput("Intensity","number");this.addOutput("Blurred","Texture");this.properties={intensity:1,iterations:1,preserve_aspect:!1,scale:[1,1],precision:c.DEFAULT}}function w(){this.addInput("in","Texture");this.addInput("dirt","Texture");this.addOutput("out","Texture");this.addOutput("glow","Texture");this.properties={enabled:!0,intensity:1,persistence:0.99,iterations:16,threshold:0,scale:1,dirt_factor:0.5,precision:c.DEFAULT};this._textures=[];this._uniforms={u_intensity:1,u_texture:0, -u_glow_texture:1,u_threshold:0,u_texel_size:vec2.create()}}function H(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={intensity:1,radius:5}}function I(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={sigma:1.4,k:1.6,p:21.7,epsilon:79,phi:0.017}}function G(){this.addOutput("Webcam","Texture");this.properties={texture_name:"",facingMode:"user"};this.boxcolor="black";this.version=0}function E(){this.addInput("in","Texture"); -this.addInput("f","number");this.addOutput("out","Texture");this.properties={enabled:!0,factor:1,precision:c.LOW};this._uniforms={u_texture:0,u_factor:1}}function u(){this.addInput("in","Texture");this.addOutput("out","Texture");this.properties={precision:c.LOW,split_channels:!1};this._values=new Uint8Array(1024);this._values.fill(255);this._curve_texture=null;this._uniforms={u_texture:0,u_curve:1,u_range:1};this._must_update=!0;this._points={RGB:[[0,0],[1,1]],R:[[0,0],[1,1]],G:[[0,0],[1,1]],B:[[0, -0],[1,1]]};this.curve_editor=null;this.addWidget("toggle","Split Channels",!1,"split_channels");this.addWidget("combo","Channel","RGB",{values:["RGB","R","G","B"]});this.curve_offset=68;this.size=[240,160]}function M(){this.addInput("in","Texture");this.addInput("exp","number");this.addOutput("out","Texture");this.properties={exposition:1,precision:c.LOW};this._uniforms={u_texture:0,u_exposition:1}}function J(){this.addInput("in","Texture");this.addInput("avg","number,Texture");this.addOutput("out", -"Texture");this.properties={enabled:!0,scale:1,gamma:1,average_lum:1,lum_white:1,precision:c.LOW};this._uniforms={u_texture:0,u_lumwhite2:1,u_igamma:1,u_scale:1,u_average_lum:1}}function L(){this.addOutput("out","Texture");this.properties={width:512,height:512,seed:0,persistence:0.1,octaves:8,scale:1,offset:[0,0],amplitude:1,precision:c.DEFAULT};this._key=0;this._texture=null;this._uniforms={u_persistence:0.1,u_seed:0,u_offset:vec2.create(),u_scale:1,u_viewport:vec2.create()}}function N(){this.addInput("v"); -this.addOutput("out","Texture");this.properties={code:"",width:512,height:512,clear:!0,precision:c.DEFAULT,use_html_canvas:!1};this._temp_texture=this._func=null}function O(){this.addInput("in","Texture");this.addOutput("out","Texture");this.properties={key_color:vec3.fromValues(0,1,0),threshold:0.8,slope:0.2,precision:c.DEFAULT}}function P(){this.addInput("in","texture");this.addInput("yaw","number");this.addOutput("out","texture");this.properties={yaw:0}}var F=y.LiteGraph;y.LGraphTexture=null;"undefined"!= -typeof GL&&(LGraphCanvas.link_type_colors.Texture="#987",y.LGraphTexture=c,c.title="Texture",c.desc="Texture",c.widgets_info={name:{widget:"texture"},filter:{widget:"checkbox"}},c.loadTextureCallback=null,c.image_preview_size=256,c.PASS_THROUGH=1,c.COPY=2,c.LOW=3,c.HIGH=4,c.REUSE=5,c.DEFAULT=2,c.MODE_VALUES={"pass through":c.PASS_THROUGH,copy:c.COPY,low:c.LOW,high:c.HIGH,reuse:c.REUSE,"default":c.DEFAULT},c.getTexturesContainer=function(){return gl.textures},c.loadTexture=function(a,b){b=b||{};var d= -a;"http://"==d.substr(0,7)&&F.proxy&&(d=F.proxy+d.substr(7));return c.getTexturesContainer()[a]=GL.Texture.fromURL(d,b)},c.getTexture=function(a){var b=this.getTexturesContainer();if(!b)throw"Cannot load texture, container of textures not found";b=b[a];return!b&&a&&":"!=a[0]?this.loadTexture(a):b},c.getTargetTexture=function(a,b,d){if(!a)throw"LGraphTexture.getTargetTexture expects a reference texture";var f=null;switch(d){case c.LOW:f=gl.UNSIGNED_BYTE;break;case c.HIGH:f=gl.HIGH_PRECISION_FORMAT; -break;case c.REUSE:return a;default:f=a?a.type:gl.UNSIGNED_BYTE}b&&b.width==a.width&&b.height==a.height&&b.type==f||(b=new GL.Texture(a.width,a.height,{type:f,format:gl.RGBA,filter:gl.LINEAR}));return b},c.getTextureType=function(a,b){var d=b?b.type:gl.UNSIGNED_BYTE;switch(a){case c.HIGH:d=gl.HIGH_PRECISION_FORMAT;break;case c.LOW:d=gl.UNSIGNED_BYTE}return d},c.getWhiteTexture=function(){return this._white_texture?this._white_texture:this._white_texture=GL.Texture.fromMemory(1,1,[255,255,255,255], -{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.getNoiseTexture=function(){if(this._noise_texture)return this._noise_texture;for(var a=new Uint8Array(1048576),b=0;1048576>b;++b)a[b]=255*Math.random();return this._noise_texture=a=GL.Texture.fromMemory(512,512,a,{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.prototype.onDropFile=function(a,b,d){if(a){var c=null;"string"==typeof a?c=GL.Texture.fromURL(a):-1!=b.toLowerCase().indexOf(".dds")?c=GL.Texture.fromDDSInMemory(a):(a=new Blob([d]), -a=URL.createObjectURL(a),c=GL.Texture.fromURL(a));this._drop_texture=c;this.properties.name=b}else this._drop_texture=null,this.properties.name=""},c.prototype.getExtraMenuOptions=function(a){var b=this;if(this._drop_texture)return[{content:"Clear",callback:function(){b._drop_texture=null;b.properties.name=""}}]},c.prototype.onExecute=function(){var a=null;this.isOutputConnected(1)&&(a=this.getInputData(0));!a&&this._drop_texture&&(a=this._drop_texture);!a&&this.properties.name&&(a=c.getTexture(this.properties.name)); -if(a){this._last_tex=a;!1===this.properties.filter?a.setParameter(gl.TEXTURE_MAG_FILTER,gl.NEAREST):a.setParameter(gl.TEXTURE_MAG_FILTER,gl.LINEAR);this.setOutputData(0,a);this.setOutputData(1,a.fullpath||a.filename);for(var b=2;b=this.size[1]))if(this._drop_texture&&a.webgl)a.drawImage(this._drop_texture,0,0,this.size[0],this.size[1]);else{if(this._last_preview_tex!=this._last_tex)if(a.webgl)this._canvas=this._last_tex;else{var b=c.generateLowResTexturePreview(this._last_tex);if(!b)return;this._last_preview_tex=this._last_tex;this._canvas=cloneCanvas(b)}this._canvas&&(a.save(),a.webgl||(a.translate(0, -this.size[1]),a.scale(1,-1)),a.drawImage(this._canvas,0,0,this.size[0],this.size[1]),a.restore())}},c.generateLowResTexturePreview=function(a){if(!a)return null;var b=c.image_preview_size,d=a;if(a.format==gl.DEPTH_COMPONENT)return null;if(a.width>b||a.height>b)d=this._preview_temp_tex,this._preview_temp_tex||(this._preview_temp_tex=d=new GL.Texture(b,b,{minFilter:gl.NEAREST})),a.copyTo(d);a=this._preview_canvas;a||(this._preview_canvas=a=createCanvas(b,b));d&&d.toCanvas(a);return a},c.prototype.getResources= -function(a){this.properties.name&&(a[this.properties.name]=GL.Texture);return a},c.prototype.onGetInputs=function(){return[["in","Texture"]]},c.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["aspect","number"]]},c.replaceCode=function(a,b){return a.replace(/\{\{[a-zA-Z0-9_]*\}\}/g,function(a){a=a.replace(/[\{\}]/g,"");return b[a]||""})},F.registerNodeType("texture/texture",c),m.title="Preview",m.desc="Show a texture in the graph canvas",m.allow_preview=!1,m.prototype.onDrawBackground= -function(a){if(!this.flags.collapsed&&(a.webgl||m.allow_preview)){var b=this.getInputData(0);if(b){var d=null,d=!b.handle&&a.webgl?b:c.generateLowResTexturePreview(b);a.save();this.properties.flipY&&(a.translate(0,this.size[1]),a.scale(1,-1));a.drawImage(d,0,0,this.size[0],this.size[1]);a.restore()}}},F.registerNodeType("texture/preview",m),n.title="Save",n.desc="Save a texture in the repository",n.prototype.getPreviewTexture=function(){return this._texture},n.prototype.onExecute=function(){var a= -this.getInputData(0);a&&(this.properties.generate_mipmaps&&(a.bind(0),a.setParameter(gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_LINEAR),gl.generateMipmap(a.texture_type),a.unbind(0)),this.properties.name&&(c.storeTexture?c.storeTexture(this.properties.name,a):c.getTexturesContainer()[this.properties.name]=a),this._texture=a,this.setOutputData(0,a),this.setOutputData(1,this.properties.name))},F.registerNodeType("texture/save",n),l.widgets_info={uvcode:{widget:"code"},pixelcode:{widget:"code"},precision:{widget:"combo", -values:c.MODE_VALUES}},l.title="Operation",l.desc="Texture shader operation",l.presets={},l.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:b.properties.show?"Hide Texture":"Show Texture",callback:function(){b.properties.show=!b.properties.show}}]},l.prototype.onPropertyChanged=function(){this.has_error=!1},l.prototype.onDrawBackground=function(a){this.flags.collapsed||20>=this.size[1]||!this.properties.show||!this._tex||this._tex.gl!=a||(a.save(),a.drawImage(this._tex,0,0,this.size[0], -this.size[1]),a.restore())},l.prototype.onExecute=function(){var a=this.getInputData(0);if(this.isOutputConnected(0))if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else{var b=this.getInputData(1);if(this.properties.uvcode||this.properties.pixelcode){var d=512,f=512;a?(d=a.width,f=a.height):b&&(d=b.width,f=b.height);b||(b=GL.Texture.getWhiteTexture());var h=c.getTextureType(this.properties.precision,a);this._tex=a||this._tex?c.getTargetTexture(a||this._tex,this._tex,this.properties.precision): -new GL.Texture(d,f,{type:h,format:gl.RGBA,filter:gl.LINEAR});h="";this.properties.uvcode&&(h="uv = "+this.properties.uvcode,-1!=this.properties.uvcode.indexOf(";")&&(h=this.properties.uvcode));var k="";this.properties.pixelcode&&(k="result = "+this.properties.pixelcode,-1!=this.properties.pixelcode.indexOf(";")&&(k=this.properties.pixelcode));var e=this._shader;if(!(this.has_error||e&&this._shader_code==h+"|"+k)){var g=c.replaceCode(l.pixel_shader,{UV_CODE:h,PIXEL_CODE:k});try{e=new GL.Shader(Shader.SCREEN_VERTEX_SHADER, -g),this.boxcolor="#00FF00"}catch(p){GL.Shader.dumpErrorToConsole(p,Shader.SCREEN_VERTEX_SHADER,g);this.boxcolor="#FF0000";this.has_error=!0;return}this._shader=e;this._shader_code=h+"|"+k}if(this._shader){var s=this.getInputData(2);null!=s?this.properties.value=s:s=parseFloat(this.properties.value);var n=this.graph.getTime();this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);gl.disable(gl.BLEND);a&&a.bind(0);b&&b.bind(1);var c=Mesh.getScreenQuad();e.uniforms({u_texture:0, -u_textureB:1,value:s,texSize:[d,f],time:n}).draw(c)});this.setOutputData(0,this._tex)}}}},l.pixel_shader="precision highp float;\n\t\t\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec2 texSize;\n\t\tuniform float time;\n\t\tuniform float value;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord;\n\t\t\t{{UV_CODE}};\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\tvec3 color = color4.rgb;\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\tvec3 colorB = color4B.rgb;\n\t\t\tvec3 result = color;\n\t\t\tfloat alpha = 1.0;\n\t\t\t{{PIXEL_CODE}};\n\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t}\n\t\t", -l.registerPreset=function(a,b){l.presets[a]=b},l.registerPreset("",""),l.registerPreset("bypass","color"),l.registerPreset("add","color + colorB * value"),l.registerPreset("substract","(color - colorB) * value"),l.registerPreset("mate","mix( color, colorB, color4B.a * value)"),l.registerPreset("invert","vec3(1.0) - color"),l.registerPreset("multiply","color * colorB * value"),l.registerPreset("divide","(color / colorB) / value"),l.registerPreset("difference","abs(color - colorB) * value"),l.registerPreset("max", -"max(color, colorB) * value"),l.registerPreset("min","min(color, colorB) * value"),l.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"),l.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"),l.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"),l.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"), -l.prototype.onInspect=function(a){var b=this;a.addCombo("Presets","",{values:Object.keys(l.presets),callback:function(d){var c=l.presets[d];c&&(b.setProperty("pixelcode",c),b.title=d,a.refresh())}})},F.registerNodeType("texture/operation",l),x.title="Shader",x.desc="Texture shader",x.widgets_info={code:{type:"code"},precision:{widget:"combo",values:c.MODE_VALUES}},x.prototype.onPropertyChanged=function(a,b){if("code"==a){var d=this.getShader();if(d){var c=d.uniformInfo;if(this.inputs)for(var f={}, -h=0;hb;++b)a[b]=255*Math.random();return this._noise_texture=a=GL.Texture.fromMemory(512, +512,a,{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.prototype.onDropFile=function(a,b,d){if(a){var c=null;"string"==typeof a?c=GL.Texture.fromURL(a):-1!=b.toLowerCase().indexOf(".dds")?c=GL.Texture.fromDDSInMemory(a):(a=new Blob([d]),a=URL.createObjectURL(a),c=GL.Texture.fromURL(a));this._drop_texture=c;this.properties.name=b}else this._drop_texture=null,this.properties.name=""},c.prototype.getExtraMenuOptions=function(a){var b=this;if(this._drop_texture)return[{content:"Clear",callback:function(){b._drop_texture= +null;b.properties.name=""}}]},c.prototype.onExecute=function(){var a=null;this.isOutputConnected(1)&&(a=this.getInputData(0));!a&&this._drop_texture&&(a=this._drop_texture);!a&&this.properties.name&&(a=c.getTexture(this.properties.name));if(a){this._last_tex=a;!1===this.properties.filter?a.setParameter(gl.TEXTURE_MAG_FILTER,gl.NEAREST):a.setParameter(gl.TEXTURE_MAG_FILTER,gl.LINEAR);this.setOutputData(0,a);this.setOutputData(1,a.fullpath||a.filename);for(var b=2;b=this.size[1]))if(this._drop_texture&&a.webgl)a.drawImage(this._drop_texture,0,0,this.size[0],this.size[1]);else{if(this._last_preview_tex!=this._last_tex)if(a.webgl)this._canvas= +this._last_tex;else{var b=c.generateLowResTexturePreview(this._last_tex);if(!b)return;this._last_preview_tex=this._last_tex;this._canvas=cloneCanvas(b)}this._canvas&&(a.save(),a.webgl||(a.translate(0,this.size[1]),a.scale(1,-1)),a.drawImage(this._canvas,0,0,this.size[0],this.size[1]),a.restore())}},c.generateLowResTexturePreview=function(a){if(!a)return null;var b=c.image_preview_size,d=a;if(a.format==gl.DEPTH_COMPONENT)return null;if(a.width>b||a.height>b)d=this._preview_temp_tex,this._preview_temp_tex|| +(this._preview_temp_tex=d=new GL.Texture(b,b,{minFilter:gl.NEAREST})),a.copyTo(d);a=this._preview_canvas;a||(this._preview_canvas=a=createCanvas(b,b));d&&d.toCanvas(a);return a},c.prototype.getResources=function(a){this.properties.name&&(a[this.properties.name]=GL.Texture);return a},c.prototype.onGetInputs=function(){return[["in","Texture"]]},c.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["aspect","number"]]},c.replaceCode=function(a,b){return a.replace(/\{\{[a-zA-Z0-9_]*\}\}/g, +function(a){a=a.replace(/[\{\}]/g,"");return b[a]||""})},F.registerNodeType("texture/texture",c),m.title="Preview",m.desc="Show a texture in the graph canvas",m.allow_preview=!1,m.prototype.onDrawBackground=function(a){if(!this.flags.collapsed&&(a.webgl||m.allow_preview)){var b=this.getInputData(0);if(b){var d=null,d=!b.handle&&a.webgl?b:c.generateLowResTexturePreview(b);a.save();this.properties.flipY&&(a.translate(0,this.size[1]),a.scale(1,-1));a.drawImage(d,0,0,this.size[0],this.size[1]);a.restore()}}}, +F.registerNodeType("texture/preview",m),n.title="Save",n.desc="Save a texture in the repository",n.prototype.getPreviewTexture=function(){return this._texture},n.prototype.onExecute=function(){var a=this.getInputData(0);a&&(this.properties.generate_mipmaps&&(a.bind(0),a.setParameter(gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_LINEAR),gl.generateMipmap(a.texture_type),a.unbind(0)),this.properties.name&&(c.storeTexture?c.storeTexture(this.properties.name,a):c.getTexturesContainer()[this.properties.name]= +a),this._texture=a,this.setOutputData(0,a),this.setOutputData(1,this.properties.name))},F.registerNodeType("texture/save",n),k.widgets_info={uvcode:{widget:"code"},pixelcode:{widget:"code"},precision:{widget:"combo",values:c.MODE_VALUES}},k.title="Operation",k.desc="Texture shader operation",k.presets={},k.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:b.properties.show?"Hide Texture":"Show Texture",callback:function(){b.properties.show=!b.properties.show}}]},k.prototype.onPropertyChanged= +function(){this.has_error=!1},k.prototype.onDrawBackground=function(a){this.flags.collapsed||20>=this.size[1]||!this.properties.show||!this._tex||this._tex.gl!=a||(a.save(),a.drawImage(this._tex,0,0,this.size[0],this.size[1]),a.restore())},k.prototype.onExecute=function(){var a=this.getInputData(0);if(this.isOutputConnected(0))if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else{var b=this.getInputData(1);if(this.properties.uvcode||this.properties.pixelcode){var d=512,f=512; +a?(d=a.width,f=a.height):b&&(d=b.width,f=b.height);b||(b=GL.Texture.getWhiteTexture());var h=c.getTextureType(this.properties.precision,a);this._tex=a||this._tex?c.getTargetTexture(a||this._tex,this._tex,this.properties.precision):new GL.Texture(d,f,{type:h,format:gl.RGBA,filter:gl.LINEAR});h="";this.properties.uvcode&&(h="uv = "+this.properties.uvcode,-1!=this.properties.uvcode.indexOf(";")&&(h=this.properties.uvcode));var e="";this.properties.pixelcode&&(e="result = "+this.properties.pixelcode, +-1!=this.properties.pixelcode.indexOf(";")&&(e=this.properties.pixelcode));var g=this._shader;if(!(this.has_error||g&&this._shader_code==h+"|"+e)){var l=c.replaceCode(k.pixel_shader,{UV_CODE:h,PIXEL_CODE:e});try{g=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,l),this.boxcolor="#00FF00"}catch(p){GL.Shader.dumpErrorToConsole(p,Shader.SCREEN_VERTEX_SHADER,l);this.boxcolor="#FF0000";this.has_error=!0;return}this._shader=g;this._shader_code=h+"|"+e}if(this._shader){var r=this.getInputData(2);null!=r?this.properties.value= +r:r=parseFloat(this.properties.value);var s=this.graph.getTime();this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);gl.disable(gl.BLEND);a&&a.bind(0);b&&b.bind(1);var c=Mesh.getScreenQuad();g.uniforms({u_texture:0,u_textureB:1,value:r,texSize:[d,f],time:s}).draw(c)});this.setOutputData(0,this._tex)}}}},k.pixel_shader="precision highp float;\n\t\t\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec2 texSize;\n\t\tuniform float time;\n\t\tuniform float value;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord;\n\t\t\t{{UV_CODE}};\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\tvec3 color = color4.rgb;\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\tvec3 colorB = color4B.rgb;\n\t\t\tvec3 result = color;\n\t\t\tfloat alpha = 1.0;\n\t\t\t{{PIXEL_CODE}};\n\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t}\n\t\t", +k.registerPreset=function(a,b){k.presets[a]=b},k.registerPreset("",""),k.registerPreset("bypass","color"),k.registerPreset("add","color + colorB * value"),k.registerPreset("substract","(color - colorB) * value"),k.registerPreset("mate","mix( color, colorB, color4B.a * value)"),k.registerPreset("invert","vec3(1.0) - color"),k.registerPreset("multiply","color * colorB * value"),k.registerPreset("divide","(color / colorB) / value"),k.registerPreset("difference","abs(color - colorB) * value"),k.registerPreset("max", +"max(color, colorB) * value"),k.registerPreset("min","min(color, colorB) * value"),k.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"),k.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"),k.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"),k.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"), +k.prototype.onInspect=function(a){var b=this;a.addCombo("Presets","",{values:Object.keys(k.presets),callback:function(d){var c=k.presets[d];c&&(b.setProperty("pixelcode",c),b.title=d,a.refresh())}})},F.registerNodeType("texture/operation",k),x.title="Shader",x.desc="Texture shader",x.widgets_info={code:{type:"code",lang:"glsl"},precision:{widget:"combo",values:c.MODE_VALUES}},x.prototype.onPropertyChanged=function(a,b){if("code"==a){var d=this.getShader();if(d){var c=d.uniformInfo;if(this.inputs)for(var f= +{},h=0;h lumaMax))\n\t\t\t\tcolor = vec4(rgbA, 1.0);\n\t\t\telse\n\t\t\t\tcolor = vec4(rgbB, 1.0);\n\t\t\tif(u_igamma != 1.0)\n\t\t\t\tcolor.xyz = pow( color.xyz, vec3(u_igamma) );\n\t\t\treturn color;\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\t\t}\n\t\t", z.gamma_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_igamma;\n\t\tvoid main() {\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\t\t gl_FragColor = color;\n\t\t}\n\t\t",F.registerNodeType("texture/toviewport",z),v.title="Copy",v.desc="Copy Texture",v.widgets_info={size:{widget:"combo",values:[0,32,64,128,256,512,1024,2048]},precision:{widget:"combo", values:c.MODE_VALUES}},v.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)){if(a){var b=a.width,d=a.height;0!=this.properties.size&&(d=b=this.properties.size);var f=this._temp_texture,h=a.type;this.properties.precision===c.LOW?h=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(h=gl.HIGH_PRECISION_FORMAT);f&&f.width==b&&f.height==d&&f.type==h||(f=gl.LINEAR,this.properties.generate_mipmaps&&isPowerOfTwo(b)&&isPowerOfTwo(d)&&(f=gl.LINEAR_MIPMAP_LINEAR), -this._temp_texture=new GL.Texture(b,d,{type:h,format:gl.RGBA,minFilter:f,magFilter:gl.LINEAR}));a.copyTo(this._temp_texture);this.properties.generate_mipmaps&&(this._temp_texture.bind(0),gl.generateMipmap(this._temp_texture.texture_type),this._temp_texture.unbind(0))}this.setOutputData(0,this._temp_texture)}},F.registerNodeType("texture/copy",v),D.title="Downsample",D.desc="Downsample Texture",D.widgets_info={iterations:{type:"number",step:1,precision:0,min:0},precision:{widget:"combo",values:c.MODE_VALUES}}, -D.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)&&a&&a.texture_type===GL.TEXTURE_2D)if(1>this.properties.iterations)this.setOutputData(0,a);else{var b=D._shader;b||(D._shader=b=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,D.pixel_shader));var d=a.width|0,f=a.height|0,h=a.type;this.properties.precision===c.LOW?h=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(h=gl.HIGH_PRECISION_FORMAT);var k=this.properties.iterations||1,e=a,g= -null,p=[],a={type:h,format:a.format},h=vec2.create(),s={u_offset:h};this._texture&&GL.Texture.releaseTemporary(this._texture);for(var l=0;l>1||0;f=f>>1||0;g=GL.Texture.getTemporary(d,f,a);p.push(g);e.setParameter(GL.TEXTURE_MAG_FILTER,GL.NEAREST);e.copyTo(g,b,s);if(1==d&&1==f)break;e=g}this._texture=p.pop();for(l=0;lthis.properties.iterations)this.setOutputData(0,a);else{var b=E._shader;b||(E._shader=b=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,E.pixel_shader));var d=a.width|0,f=a.height|0,h=a.type;this.properties.precision===c.LOW?h=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(h=gl.HIGH_PRECISION_FORMAT);var e=this.properties.iterations||1,g=a,l= +null,p=[],a={type:h,format:a.format},h=vec2.create(),r={u_offset:h};this._texture&&GL.Texture.releaseTemporary(this._texture);for(var k=0;k>1||0;f=f>>1||0;l=GL.Texture.getTemporary(d,f,a);p.push(l);g.setParameter(GL.TEXTURE_MAG_FILTER,GL.NEAREST);g.copyTo(l,b,r);if(1==d&&1==f)break;g=l}this._texture=p.pop();for(k=0;k>=2,c++,1!=d););a.copyTo(this._textures_chain[0]);for(var c=1;c<=this._textures_chain.length;++c)a=this._textures_chain[c];var f=g._shader,h=this._uniforms;h.u_mipmap_offset=this.properties.mipmap_offset;gl.disable(gl.DEPTH_TEST); -gl.disable(gl.BLEND);this._temp_texture.drawTo(function(){a.toViewport(f,h)})}},g.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform mat4 u_samples_a;\n\t\tuniform mat4 u_samples_b;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_mipmap_offset;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t//random average\n\t\t\tfor(int i = 0; i < 4; ++i)\n\t\t\t\tfor(int j = 0; j < 4; ++j)\n\t\t\t\t{\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t}\n\t\t gl_FragColor = color * 0.03125;\n\t\t}\n\t\t", -r.title="Smooth",r.desc="Smooth texture over time",r.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){r._shader||(r._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,r.pixel_shader));var b=this._temp_texture;b&&b.type==a.type&&b.width==a.width&&b.height==a.height||(b={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(a.width,a.height,b),this._temp_texture2=new GL.Texture(a.width,a.height,b),a.copyTo(this._temp_texture2));var b= -this._temp_texture,d=this._temp_texture2,c=r._shader,f=this._uniforms;f.u_factor=1-this.getInputOrProperty("factor");gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);b.drawTo(function(){d.bind(1);a.toViewport(c,f)});this.setOutputData(0,b);this._temp_texture=d;this._temp_texture2=b}},r.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_factor;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tgl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\t\t}\n\t\t", -F.registerNodeType("texture/temporal_smooth",r),q.title="Lineal Avg Smooth",q.desc="Smooth texture linearly over time",q["@samples"]={type:"number",min:1,max:64,step:1,precision:1},q.prototype.getPreviewTexture=function(){return this._temp_texture2},q.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){q._shader||(q._shader_copy=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,q.pixel_shader_copy),q._shader_avg=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,q.pixel_shader_avg)); -var b=Math.clamp(this.properties.samples,0,64),d=this.frame,c=this.properties.frames_interval;if(0==c||0==d%c){d=this._temp_texture;d&&d.type==a.type&&d.width==b||(d={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(b,1,d),this._temp_texture2=new GL.Texture(b,1,d),this._temp_texture_out=new GL.Texture(1,1,d));var f=this._temp_texture,h=this._temp_texture2,k=q._shader_copy,e=q._shader_avg,g=this._uniforms;g.u_samples=b;g.u_isamples=1/b;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST); -f.drawTo(function(){h.bind(1);a.toViewport(k,g)});this._temp_texture_out.drawTo(function(){f.toViewport(e,g)});this.setOutputData(0,this._temp_texture_out);this._temp_texture=h;this._temp_texture2=f}else this.setOutputData(0,this._temp_texture_out);this.setOutputData(1,this._temp_texture2);this.frame++}},q.pixel_shader_copy="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tif( v_coord.x <= u_isamples )\n\t\t\t\tgl_FragColor = texture2D( u_texture, vec2(0.5) );\n\t\t\telse\n\t\t\t\tgl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\t\t}\n\t\t", -q.pixel_shader_avg="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform int u_samples;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\tfor(int i = 0; i < 64; ++i)\n\t\t\t{\n\t\t\t\tcolor += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\t\t\t\tif(i == (u_samples - 1))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tgl_FragColor = color * u_isamples;\n\t\t}\n\t\t",F.registerNodeType("texture/linear_avg_smooth", -q),A.title="Image to Texture",A.desc="Uploads an image to the GPU",A.prototype.onExecute=function(){var a=this.getInputData(0);if(a){var b=a.videoWidth||a.width,d=a.videoHeight||a.height;if(a.gltexture)this.setOutputData(0,a.gltexture);else{var c=this._temp_texture;c&&c.width==b&&c.height==d||(this._temp_texture=new GL.Texture(b,d,{format:gl.RGBA,filter:gl.LINEAR}));try{this._temp_texture.uploadImage(a)}catch(f){console.error("image comes from an unsafe location, cannot be uploaded to webgl: "+f); -return}this.setOutputData(0,this._temp_texture)}}},F.registerNodeType("texture/imageToTexture",A),s.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},s.title="LUT",s.desc="Apply LUT to Texture",s.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture)); -if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.bindTexture(gl.TEXTURE_2D,null);var d=this.properties.intensity;this.isInputConnected(2)&&(this.properties.intensity=d=this.getInputData(2));this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);this._tex.drawTo(function(){b.bind(1);a.toViewport(s._shader,{u_texture:0,u_textureB:1, -u_amount:d})});this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}},s.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_amount;\n\t\t\n\t\tvoid main() {\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\t\t\t mediump vec2 quad1;\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\t\t\t mediump vec2 quad2;\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\t\t\t highp vec2 texPos1;\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t highp vec2 texPos2;\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\t\t}\n\t\t", -F.registerNodeType("texture/LUT",s),p.title="Texture to Channels",p.desc="Split texture channels",p.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this._channels||(this._channels=Array(4));for(var b=gl.RGB,d=0,c=0;4>c;c++)this.isOutputConnected(c)?(this._channels[c]&&this._channels[c].width==a.width&&this._channels[c].height==a.height&&this._channels[c].type==a.type&&this._channels[c].format==b||(this._channels[c]=new GL.Texture(a.width,a.height,{type:a.type,format:b,filter:gl.LINEAR})), -d++):this._channels[c]=null;if(d){gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);for(var f=Mesh.getScreenQuad(),h=p._shader,k=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],c=0;4>c;c++)this._channels[c]&&(this._channels[c].drawTo(function(){a.bind(0);h.uniforms({u_texture:0,u_mask:k[c]}).draw(f)}),this.setOutputData(c,this._channels[c]))}}},p.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec4 u_mask;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\t\t}\n\t\t", -F.registerNodeType("texture/textureChannels",p),t.title="Channels to Texture",t.desc="Split texture channels",t.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},t.prototype.onExecute=function(){var a=c.getWhiteTexture(),b=this.getInputData(0)||a,d=this.getInputData(1)||a,f=this.getInputData(2)||a,h=this.getInputData(3)||a;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var k=Mesh.getScreenQuad();t._shader||(t._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,t.pixel_shader));var e=t._shader, -a=Math.max(b.width,d.width,f.width,h.width),g=Math.max(b.height,d.height,f.height,h.height),p=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._texture&&this._texture.width==a&&this._texture.height==g&&this._texture.type==p||(this._texture=new GL.Texture(a,g,{type:p,format:gl.RGBA,filter:gl.LINEAR}));a=this._color;a[0]=this.properties.R;a[1]=this.properties.G;a[2]=this.properties.B;a[3]=this.properties.A;var s=this._uniforms;this._texture.drawTo(function(){b.bind(0); -d.bind(1);f.bind(2);h.bind(3);e.uniforms(s).draw(k)});this.setOutputData(0,this._texture)},t.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureR;\n\t\tuniform sampler2D u_textureG;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform vec4 u_color;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = u_color * vec4( \t\t\t\t\ttexture2D(u_textureR, v_coord).r,\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\t\t}\n\t\t", -F.registerNodeType("texture/channelsTexture",t),h.title="Color",h.desc="Generates a 1x1 texture with a constant color",h.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},h.prototype.onDrawBackground=function(a){var b=this.properties.color;a.fillStyle="rgb("+Math.floor(255*Math.clamp(b[0],0,1))+","+Math.floor(255*Math.clamp(b[1],0,1))+","+Math.floor(255*Math.clamp(b[2],0,1))+")";this.flags.collapsed?this.boxcolor=a.fillStyle:a.fillRect(0,0,this.size[0],this.size[1])},h.prototype.onExecute= +F.registerNodeType("texture/average",B),e.widgets_info={mode:{widget:"combo",values:["min","max","avg"]}},e.title="MinMax",e.desc="Compute the scene min max",e.prototype.onExecute=function(){this.properties.use_previous_frame||this.update();this.setOutputData(0,this._temp_texture);this.setOutputData(1,this._luminance)},e.prototype.onPreRenderExecute=function(){this.update()},e.prototype.update=function(){var a=this.getInputData(0);if(a&&(this.isOutputConnected(0)||this.isOutputConnected(1))){e._shader|| +(e._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,e.pixel_shader));var b=gl.UNSIGNED_BYTE;a.type!=b&&(b=gl.FLOAT);var d=512;if(!this._textures_chain.length||this._textures_chain[0].type!=b)for(;c&&(this._textures_chain[c]=new GL.Texture(d,d,{type:b,format:gl.RGBA,filter:gl.NEAREST}),d>>=2,c++,1!=d););a.copyTo(this._textures_chain[0]);for(var c=1;c<=this._textures_chain.length;++c)a=this._textures_chain[c];var f=e._shader,h=this._uniforms;h.u_mipmap_offset=this.properties.mipmap_offset;gl.disable(gl.DEPTH_TEST); +gl.disable(gl.BLEND);this._temp_texture.drawTo(function(){a.toViewport(f,h)})}},e.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform mat4 u_samples_a;\n\t\tuniform mat4 u_samples_b;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_mipmap_offset;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t//random average\n\t\t\tfor(int i = 0; i < 4; ++i)\n\t\t\t\tfor(int j = 0; j < 4; ++j)\n\t\t\t\t{\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t}\n\t\t gl_FragColor = color * 0.03125;\n\t\t}\n\t\t", +q.title="Smooth",q.desc="Smooth texture over time",q.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){q._shader||(q._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,q.pixel_shader));var b=this._temp_texture;b&&b.type==a.type&&b.width==a.width&&b.height==a.height||(b={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(a.width,a.height,b),this._temp_texture2=new GL.Texture(a.width,a.height,b),a.copyTo(this._temp_texture2));var b= +this._temp_texture,d=this._temp_texture2,c=q._shader,f=this._uniforms;f.u_factor=1-this.getInputOrProperty("factor");gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);b.drawTo(function(){d.bind(1);a.toViewport(c,f)});this.setOutputData(0,b);this._temp_texture=d;this._temp_texture2=b}},q.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_factor;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tgl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\t\t}\n\t\t", +F.registerNodeType("texture/temporal_smooth",q),t.title="Lineal Avg Smooth",t.desc="Smooth texture linearly over time",t["@samples"]={type:"number",min:1,max:64,step:1,precision:1},t.prototype.getPreviewTexture=function(){return this._temp_texture2},t.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){t._shader||(t._shader_copy=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,t.pixel_shader_copy),t._shader_avg=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,t.pixel_shader_avg)); +var b=Math.clamp(this.properties.samples,0,64),d=this.frame,c=this.properties.frames_interval;if(0==c||0==d%c){d=this._temp_texture;d&&d.type==a.type&&d.width==b||(d={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(b,1,d),this._temp_texture2=new GL.Texture(b,1,d),this._temp_texture_out=new GL.Texture(1,1,d));var f=this._temp_texture,h=this._temp_texture2,e=t._shader_copy,g=t._shader_avg,l=this._uniforms;l.u_samples=b;l.u_isamples=1/b;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST); +f.drawTo(function(){h.bind(1);a.toViewport(e,l)});this._temp_texture_out.drawTo(function(){f.toViewport(g,l)});this.setOutputData(0,this._temp_texture_out);this._temp_texture=h;this._temp_texture2=f}else this.setOutputData(0,this._temp_texture_out);this.setOutputData(1,this._temp_texture2);this.frame++}},t.pixel_shader_copy="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tif( v_coord.x <= u_isamples )\n\t\t\t\tgl_FragColor = texture2D( u_texture, vec2(0.5) );\n\t\t\telse\n\t\t\t\tgl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\t\t}\n\t\t", +t.pixel_shader_avg="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform int u_samples;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\tfor(int i = 0; i < 64; ++i)\n\t\t\t{\n\t\t\t\tcolor += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\t\t\t\tif(i == (u_samples - 1))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tgl_FragColor = color * u_isamples;\n\t\t}\n\t\t",F.registerNodeType("texture/linear_avg_smooth", +t),A.title="Image to Texture",A.desc="Uploads an image to the GPU",A.prototype.onExecute=function(){var a=this.getInputData(0);if(a){var b=a.videoWidth||a.width,d=a.videoHeight||a.height;if(a.gltexture)this.setOutputData(0,a.gltexture);else{var c=this._temp_texture;c&&c.width==b&&c.height==d||(this._temp_texture=new GL.Texture(b,d,{format:gl.RGBA,filter:gl.LINEAR}));try{this._temp_texture.uploadImage(a)}catch(f){console.error("image comes from an unsafe location, cannot be uploaded to webgl: "+f); +return}this.setOutputData(0,this._temp_texture)}}},F.registerNodeType("texture/imageToTexture",A),p.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},p.title="LUT",p.desc="Apply LUT to Texture",p.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture)); +if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.bindTexture(gl.TEXTURE_2D,null);var d=this.properties.intensity;this.isInputConnected(2)&&(this.properties.intensity=d=this.getInputData(2));this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);this._tex.drawTo(function(){b.bind(1);a.toViewport(p._shader,{u_texture:0,u_textureB:1, +u_amount:d})});this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}},p.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_amount;\n\t\t\n\t\tvoid main() {\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\t\t\t mediump vec2 quad1;\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\t\t\t mediump vec2 quad2;\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\t\t\t highp vec2 texPos1;\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t highp vec2 texPos2;\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\t\t}\n\t\t", +F.registerNodeType("texture/LUT",p),s.title="Texture to Channels",s.desc="Split texture channels",s.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this._channels||(this._channels=Array(4));for(var b=gl.RGB,d=0,c=0;4>c;c++)this.isOutputConnected(c)?(this._channels[c]&&this._channels[c].width==a.width&&this._channels[c].height==a.height&&this._channels[c].type==a.type&&this._channels[c].format==b||(this._channels[c]=new GL.Texture(a.width,a.height,{type:a.type,format:b,filter:gl.LINEAR})), +d++):this._channels[c]=null;if(d){gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);for(var f=Mesh.getScreenQuad(),h=s._shader,e=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],c=0;4>c;c++)this._channels[c]&&(this._channels[c].drawTo(function(){a.bind(0);h.uniforms({u_texture:0,u_mask:e[c]}).draw(f)}),this.setOutputData(c,this._channels[c]))}}},s.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec4 u_mask;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\t\t}\n\t\t", +F.registerNodeType("texture/textureChannels",s),w.title="Channels to Texture",w.desc="Split texture channels",w.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},w.prototype.onExecute=function(){var a=c.getWhiteTexture(),b=this.getInputData(0)||a,d=this.getInputData(1)||a,f=this.getInputData(2)||a,h=this.getInputData(3)||a;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad();w._shader||(w._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,w.pixel_shader));var g=w._shader, +a=Math.max(b.width,d.width,f.width,h.width),l=Math.max(b.height,d.height,f.height,h.height),p=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._texture&&this._texture.width==a&&this._texture.height==l&&this._texture.type==p||(this._texture=new GL.Texture(a,l,{type:p,format:gl.RGBA,filter:gl.LINEAR}));a=this._color;a[0]=this.properties.R;a[1]=this.properties.G;a[2]=this.properties.B;a[3]=this.properties.A;var r=this._uniforms;this._texture.drawTo(function(){b.bind(0); +d.bind(1);f.bind(2);h.bind(3);g.uniforms(r).draw(e)});this.setOutputData(0,this._texture)},w.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureR;\n\t\tuniform sampler2D u_textureG;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform vec4 u_color;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = u_color * vec4( \t\t\t\t\ttexture2D(u_textureR, v_coord).r,\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\t\t}\n\t\t", +F.registerNodeType("texture/channelsTexture",w),l.title="Color",l.desc="Generates a 1x1 texture with a constant color",l.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},l.prototype.onDrawBackground=function(a){var b=this.properties.color;a.fillStyle="rgb("+Math.floor(255*Math.clamp(b[0],0,1))+","+Math.floor(255*Math.clamp(b[1],0,1))+","+Math.floor(255*Math.clamp(b[2],0,1))+")";this.flags.collapsed?this.boxcolor=a.fillStyle:a.fillRect(0,0,this.size[0],this.size[1])},l.prototype.onExecute= function(){var a=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._tex&&this._tex.type==a||(this._tex=new GL.Texture(1,1,{format:gl.RGBA,type:a,minFilter:gl.NEAREST}));a=this.properties.color;if(this.inputs)for(var b=0;ba.width?d: -a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var k=Mesh.getScreenQuad(),e=null,g=this._uniforms;f?(e=b._shader_tex,e||(e=b._shader_tex=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader,{MIX_TEX:""}))):(e=b._shader_factor,e||(e=b._shader_factor=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader)),h=null==h?this.properties.factor:h,g.u_mix.set([h,h,h,h]));var p=this.properties.invert;this._tex.drawTo(function(){a.bind(p?1:0);d.bind(p?0:1);f&&f.bind(2); -e.uniforms(g).draw(k)});this.setOutputData(0,this._tex)}}},b.prototype.onGetInputs=function(){return[["factor","number"]]},b.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform sampler2D u_textureB;\n\t\t#ifdef MIX_TEX\n\t\t\tuniform sampler2D u_textureMix;\n\t\t#else\n\t\t\tuniform vec4 u_mix;\n\t\t#endif\n\t\t\n\t\tvoid main() {\n\t\t\t#ifdef MIX_TEX\n\t\t\t vec4 f = texture2D(u_textureMix, v_coord);\n\t\t\t#else\n\t\t\t vec4 f = u_mix;\n\t\t\t#endif\n\t\t gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\t\t}\n\t\t", -F.registerNodeType("texture/mix",b),d.title="Edges",d.desc="Detects edges",d.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},d.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else if(a){this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var b=Mesh.getScreenQuad(),f=d._shader,h=this.properties.invert,k=this.properties.factor, -e=this.properties.threshold?1:0;this._tex.drawTo(function(){a.bind(0);f.uniforms({u_texture:0,u_isize:[1/a.width,1/a.height],u_factor:k,u_threshold:e,u_invert:h?1:0}).draw(b)});this.setOutputData(0,this._tex)}}},d.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_isize;\n\t\tuniform int u_invert;\n\t\tuniform float u_factor;\n\t\tuniform float u_threshold;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 center = texture2D(u_texture, v_coord);\n\t\t\tvec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\t\t\tvec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\t\t\tvec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\t\t\tvec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\t\t\tvec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\t\t\tdiff *= u_factor;\n\t\t\tif(u_invert == 1)\n\t\t\t\tdiff.xyz = vec3(1.0) - diff.xyz;\n\t\t\tif( u_threshold == 0.0 )\n\t\t\t\tgl_FragColor = vec4( diff.xyz, center.a );\n\t\t\telse\n\t\t\t\tgl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\t\t}\n\t\t", -F.registerNodeType("texture/edges",d),k.title="Depth Range",k.desc="Generates a texture with a depth range",k.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a){var b=gl.UNSIGNED_BYTE;this.properties.high_precision&&(b=gl.half_float_ext?gl.HALF_FLOAT_OES:gl.FLOAT);this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&&this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGBA, -filter:gl.LINEAR}));var d=this._uniforms,b=this.properties.distance;this.isInputConnected(1)&&(b=this.getInputData(1),this.properties.distance=b);var c=this.properties.range;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.range=c);d.u_distance=b;d.u_range=c;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var f=Mesh.getScreenQuad();k._shader||(k._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,k.pixel_shader),k._shader_onlydepth=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,k.pixel_shader, -{ONLY_DEPTH:""}));var h=this.properties.only_depth?k._shader_onlydepth:k._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1,1E3];d.u_camera_planes=b;this._temp_texture.drawTo(function(){a.bind(0);h.uniforms(d).draw(f)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},k.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform float u_distance;\n\t\tuniform float u_range;\n\t\t\n\t\tfloat LinearDepth()\n\t\t{\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\t\t\tdepth = depth * 2.0 - 1.0;\n\t\t\treturn zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t\tfloat depth = LinearDepth();\n\t\t\t#ifdef ONLY_DEPTH\n\t\t\t gl_FragColor = vec4(depth);\n\t\t\t#else\n\t\t\t\tfloat diff = abs(depth * u_camera_planes.y - u_distance);\n\t\t\t\tfloat dof = 1.0;\n\t\t\t\tif(diff <= u_range)\n\t\t\t\t\tdof = diff / u_range;\n\t\t\t gl_FragColor = vec4(dof);\n\t\t\t#endif\n\t\t}\n\t\t", -F.registerNodeType("texture/depth_range",k),f.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},f.title="Linear Depth",f.desc="Creates a color texture with linear depth",f.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a&&(a.format==gl.DEPTH_COMPONENT||a.format==gl.DEPTH_STENCIL)){var b=this.properties.precision==c.HIGH?gl.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&& -this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGB,filter:gl.LINEAR}));var d=this._uniforms;d.u_near=a.near_far_planes[0];d.u_far=a.near_far_planes[1];d.u_invert=this.properties.invert?1:0;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var h=Mesh.getScreenQuad();f._shader||(f._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,f.pixel_shader));var k=f._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera? -LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1,1E3];d.u_camera_planes=b;this._temp_texture.drawTo(function(){a.bind(0);k.uniforms(d).draw(h)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},f.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_near;\n\t\tuniform float u_far;\n\t\tuniform int u_invert;\n\t\t\n\t\tvoid main() {\n\t\t\tfloat zNear = u_near;\n\t\t\tfloat zFar = u_far;\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\t\t\tdepth = depth * 2.0 - 1.0;\n\t\t\tfloat f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t\tif( u_invert == 1 )\n\t\t\t\tf = 1.0 - f;\n\t\t\tgl_FragColor = vec4(vec3(f),1.0);\n\t\t}\n\t\t", +a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad(),g=null,l=this._uniforms;f?(g=b._shader_tex,g||(g=b._shader_tex=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader,{MIX_TEX:""}))):(g=b._shader_factor,g||(g=b._shader_factor=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader)),h=null==h?this.properties.factor:h,l.u_mix.set([h,h,h,h]));var p=this.properties.invert;this._tex.drawTo(function(){a.bind(p?1:0);d.bind(p?0:1);f&&f.bind(2); +g.uniforms(l).draw(e)});this.setOutputData(0,this._tex)}}},b.prototype.onGetInputs=function(){return[["factor","number"]]},b.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform sampler2D u_textureB;\n\t\t#ifdef MIX_TEX\n\t\t\tuniform sampler2D u_textureMix;\n\t\t#else\n\t\t\tuniform vec4 u_mix;\n\t\t#endif\n\t\t\n\t\tvoid main() {\n\t\t\t#ifdef MIX_TEX\n\t\t\t vec4 f = texture2D(u_textureMix, v_coord);\n\t\t\t#else\n\t\t\t vec4 f = u_mix;\n\t\t\t#endif\n\t\t gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\t\t}\n\t\t", +F.registerNodeType("texture/mix",b),d.title="Edges",d.desc="Detects edges",d.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},d.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else if(a){this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var b=Mesh.getScreenQuad(),f=d._shader,h=this.properties.invert,e=this.properties.factor, +g=this.properties.threshold?1:0;this._tex.drawTo(function(){a.bind(0);f.uniforms({u_texture:0,u_isize:[1/a.width,1/a.height],u_factor:e,u_threshold:g,u_invert:h?1:0}).draw(b)});this.setOutputData(0,this._tex)}}},d.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_isize;\n\t\tuniform int u_invert;\n\t\tuniform float u_factor;\n\t\tuniform float u_threshold;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 center = texture2D(u_texture, v_coord);\n\t\t\tvec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\t\t\tvec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\t\t\tvec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\t\t\tvec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\t\t\tvec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\t\t\tdiff *= u_factor;\n\t\t\tif(u_invert == 1)\n\t\t\t\tdiff.xyz = vec3(1.0) - diff.xyz;\n\t\t\tif( u_threshold == 0.0 )\n\t\t\t\tgl_FragColor = vec4( diff.xyz, center.a );\n\t\t\telse\n\t\t\t\tgl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\t\t}\n\t\t", +F.registerNodeType("texture/edges",d),h.title="Depth Range",h.desc="Generates a texture with a depth range",h.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a){var b=gl.UNSIGNED_BYTE;this.properties.high_precision&&(b=gl.half_float_ext?gl.HALF_FLOAT_OES:gl.FLOAT);this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&&this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGBA, +filter:gl.LINEAR}));var d=this._uniforms,b=this.properties.distance;this.isInputConnected(1)&&(b=this.getInputData(1),this.properties.distance=b);var c=this.properties.range;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.range=c);d.u_distance=b;d.u_range=c;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var f=Mesh.getScreenQuad();h._shader||(h._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,h.pixel_shader),h._shader_onlydepth=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,h.pixel_shader, +{ONLY_DEPTH:""}));var e=this.properties.only_depth?h._shader_onlydepth:h._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1,1E3];d.u_camera_planes=b;this._temp_texture.drawTo(function(){a.bind(0);e.uniforms(d).draw(f)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},h.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform float u_distance;\n\t\tuniform float u_range;\n\t\t\n\t\tfloat LinearDepth()\n\t\t{\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\t\t\tdepth = depth * 2.0 - 1.0;\n\t\t\treturn zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t\tfloat depth = LinearDepth();\n\t\t\t#ifdef ONLY_DEPTH\n\t\t\t gl_FragColor = vec4(depth);\n\t\t\t#else\n\t\t\t\tfloat diff = abs(depth * u_camera_planes.y - u_distance);\n\t\t\t\tfloat dof = 1.0;\n\t\t\t\tif(diff <= u_range)\n\t\t\t\t\tdof = diff / u_range;\n\t\t\t gl_FragColor = vec4(dof);\n\t\t\t#endif\n\t\t}\n\t\t", +F.registerNodeType("texture/depth_range",h),f.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},f.title="Linear Depth",f.desc="Creates a color texture with linear depth",f.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a&&(a.format==gl.DEPTH_COMPONENT||a.format==gl.DEPTH_STENCIL)){var b=this.properties.precision==c.HIGH?gl.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&& +this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGB,filter:gl.LINEAR}));var d=this._uniforms;d.u_near=a.near_far_planes[0];d.u_far=a.near_far_planes[1];d.u_invert=this.properties.invert?1:0;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var h=Mesh.getScreenQuad();f._shader||(f._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,f.pixel_shader));var e=f._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera? +LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1,1E3];d.u_camera_planes=b;this._temp_texture.drawTo(function(){a.bind(0);e.uniforms(d).draw(h)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},f.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_near;\n\t\tuniform float u_far;\n\t\tuniform int u_invert;\n\t\t\n\t\tvoid main() {\n\t\t\tfloat zNear = u_near;\n\t\t\tfloat zFar = u_far;\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\t\t\tdepth = depth * 2.0 - 1.0;\n\t\t\tfloat f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t\tif( u_invert == 1 )\n\t\t\t\tf = 1.0 - f;\n\t\t\tgl_FragColor = vec4(vec3(f),1.0);\n\t\t}\n\t\t", F.registerNodeType("texture/linear_depth",f),K.title="Blur",K.desc="Blur a texture",K.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},K.max_iterations=20,K.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._final_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(b=this._final_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));var d=this.properties.iterations;this.isInputConnected(1)&& (d=this.getInputData(1),this.properties.iterations=d);d=Math.min(Math.floor(d),K.max_iterations);if(0==d)this.setOutputData(0,a);else{var c=this.properties.intensity;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.intensity=c);var f=F.camera_aspect;f||void 0===window.gl||(f=gl.canvas.height/gl.canvas.width);f||(f=1);var f=this.properties.preserve_aspect?f:1,h=this.properties.scale||[1,1];a.applyBlur(f*h[0],h[1],c,b);for(a=1;a>=1;1<(d|0)&&(d>>=1);if(2>b)break;p=e[t]=GL.Texture.getTemporary(b,d,f);n[0]=1/s.width;n[1]=1/s.height;s.blit(p,g.uniforms(k));s=p}this.isOutputConnected(2)&&(b=this._average_texture,b&&b.type==a.type&&b.format==a.format||(b=this._average_texture=new GL.Texture(1,1,{type:a.type,format:a.format,filter:gl.LINEAR})),n[0]=1/s.width,n[1]=1/s.height,k.u_intensity=m,k.u_delta=1,s.blit(b,g.uniforms(k)),this.setOutputData(2,b));gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE);k.u_intensity= -this.getInputOrProperty("persistence");k.u_delta=0.5;for(t-=2;0<=t;t--)p=e[t],e[t]=null,n[0]=1/s.width,n[1]=1/s.height,s.blit(p,g.uniforms(k)),GL.Texture.releaseTemporary(s),s=p;gl.disable(gl.BLEND);this.isOutputConnected(1)&&(e=this._glow_texture,e&&e.width==a.width&&e.height==a.height&&e.type==h&&e.format==a.format||(e=this._glow_texture=new GL.Texture(a.width,a.height,{type:h,format:a.format,filter:gl.LINEAR})),s.blit(e),this.setOutputData(1,e));if(this.isOutputConnected(0)){e=this._final_texture; -e&&e.width==a.width&&e.height==a.height&&e.type==h&&e.format==a.format||(e=this._final_texture=new GL.Texture(a.width,a.height,{type:h,format:a.format,filter:gl.LINEAR}));var q=this.getInputData(1),u=this.getInputOrProperty("dirt_factor");k.u_intensity=m;g=q?w._dirt_final_shader:w._final_shader;g||(g=q?w._dirt_final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,w.final_pixel_shader,{USE_DIRT:""}):w._final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,w.final_pixel_shader));e.drawTo(function(){a.bind(0); -s.bind(1);q&&(g.setUniform("u_dirt_factor",u),g.setUniform("u_dirt_texture",q.bind(2)));g.toViewport(k)});this.setOutputData(0,e)}GL.Texture.releaseTemporary(s)}},w.cut_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform float u_threshold;\n\tvoid main() {\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\t}",w.scale_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\t}", -w.final_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform sampler2D u_glow_texture;\n\t#ifdef USE_DIRT\n\t\tuniform sampler2D u_dirt_texture;\n\t#endif\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\tuniform float u_dirt_factor;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tvec4 glow = sampleBox( v_coord );\n\t\t#ifdef USE_DIRT\n\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\t\t#endif\n\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\t}", -F.registerNodeType("texture/glow",w),H.title="Kuwahara Filter",H.desc="Filters a texture giving an artistic oil canvas painting",H.max_radius=10,H._shaders=[],H.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));b=this.properties.radius;b=Math.min(Math.floor(b),H.max_radius);if(0== +b)}}},F.registerNodeType("texture/blur",K),r.title="Glow",r.desc="Filters a texture giving it a glow effect",r.weights=new Float32Array([0.5,0.4,0.3,0.2]),r.widgets_info={iterations:{type:"number",min:0,max:16,step:1,precision:0},threshold:{type:"number",min:0,max:10,step:0.01,precision:2},precision:{widget:"combo",values:c.MODE_VALUES}},r.prototype.onGetInputs=function(){return[["enabled","boolean"],["threshold","number"],["intensity","number"],["persistence","number"],["iterations","number"],["dirt_factor", +"number"]]},r.prototype.onGetOutputs=function(){return[["average","Texture"]]},r.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isAnyOutputConnected())if(this.properties.precision===c.PASS_THROUGH||!1===this.getInputOrProperty("enabled"))this.setOutputData(0,a);else{var b=a.width,d=a.height,f={format:a.format,type:a.type,minFilter:GL.LINEAR,magFilter:GL.LINEAR,wrap:gl.CLAMP_TO_EDGE},h=c.getTextureType(this.properties.precision,a),e=this._uniforms,g=this._textures,l=r._cut_shader; +l||(l=r._cut_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,r.cut_pixel_shader));gl.disable(gl.DEPTH_TEST);gl.disable(gl.BLEND);e.u_threshold=this.getInputOrProperty("threshold");var p=g[0]=GL.Texture.getTemporary(b,d,f);a.blit(p,l.uniforms(e));var k=p,s=this.getInputOrProperty("iterations"),s=Math.clamp(s,1,16)|0,n=e.u_texel_size,q=this.getInputOrProperty("intensity");e.u_intensity=1;e.u_delta=this.properties.scale;l=r._shader;l||(l=r._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,r.scale_pixel_shader)); +for(var m=1;m>=1;1<(d|0)&&(d>>=1);if(2>b)break;p=g[m]=GL.Texture.getTemporary(b,d,f);n[0]=1/k.width;n[1]=1/k.height;k.blit(p,l.uniforms(e));k=p}this.isOutputConnected(2)&&(b=this._average_texture,b&&b.type==a.type&&b.format==a.format||(b=this._average_texture=new GL.Texture(1,1,{type:a.type,format:a.format,filter:gl.LINEAR})),n[0]=1/k.width,n[1]=1/k.height,e.u_intensity=q,e.u_delta=1,k.blit(b,l.uniforms(e)),this.setOutputData(2,b));gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE);e.u_intensity= +this.getInputOrProperty("persistence");e.u_delta=0.5;for(m-=2;0<=m;m--)p=g[m],g[m]=null,n[0]=1/k.width,n[1]=1/k.height,k.blit(p,l.uniforms(e)),GL.Texture.releaseTemporary(k),k=p;gl.disable(gl.BLEND);this.isOutputConnected(1)&&(g=this._glow_texture,g&&g.width==a.width&&g.height==a.height&&g.type==h&&g.format==a.format||(g=this._glow_texture=new GL.Texture(a.width,a.height,{type:h,format:a.format,filter:gl.LINEAR})),k.blit(g),this.setOutputData(1,g));if(this.isOutputConnected(0)){g=this._final_texture; +g&&g.width==a.width&&g.height==a.height&&g.type==h&&g.format==a.format||(g=this._final_texture=new GL.Texture(a.width,a.height,{type:h,format:a.format,filter:gl.LINEAR}));var u=this.getInputData(1),w=this.getInputOrProperty("dirt_factor");e.u_intensity=q;l=u?r._dirt_final_shader:r._final_shader;l||(l=u?r._dirt_final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,r.final_pixel_shader,{USE_DIRT:""}):r._final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,r.final_pixel_shader));g.drawTo(function(){a.bind(0); +k.bind(1);u&&(l.setUniform("u_dirt_factor",w),l.setUniform("u_dirt_texture",u.bind(2)));l.toViewport(e)});this.setOutputData(0,g)}GL.Texture.releaseTemporary(k)}},r.cut_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform float u_threshold;\n\tvoid main() {\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\t}",r.scale_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\t}", +r.final_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform sampler2D u_glow_texture;\n\t#ifdef USE_DIRT\n\t\tuniform sampler2D u_dirt_texture;\n\t#endif\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\tuniform float u_dirt_factor;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tvec4 glow = sampleBox( v_coord );\n\t\t#ifdef USE_DIRT\n\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\t\t#endif\n\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\t}", +F.registerNodeType("texture/glow",r),H.title="Kuwahara Filter",H.desc="Filters a texture giving an artistic oil canvas painting",H.max_radius=10,H._shaders=[],H.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));b=this.properties.radius;b=Math.min(Math.floor(b),H.max_radius);if(0== b)this.setOutputData(0,a);else{var d=this.properties.intensity,c=F.camera_aspect;c||void 0===window.gl||(c=gl.canvas.height/gl.canvas.width);c||(c=1);c=this.properties.preserve_aspect?c:1;H._shaders[b]||(H._shaders[b]=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,H.pixel_shader,{RADIUS:b.toFixed(0)}));var f=H._shaders[b],h=GL.Mesh.getScreenQuad();a.bind(0);this._temp_texture.drawTo(function(){f.uniforms({u_texture:0,u_intensity:d,u_resolution:[a.width,a.height],u_iResolution:[1/a.width,1/a.height]}).draw(h)}); this.setOutputData(0,this._temp_texture)}}},H.pixel_shader="\nprecision highp float;\nvarying vec2 v_coord;\nuniform sampler2D u_texture;\nuniform float u_intensity;\nuniform vec2 u_resolution;\nuniform vec2 u_iResolution;\n#ifndef RADIUS\n\t#define RADIUS 7\n#endif\nvoid main() {\n\n\tconst int radius = RADIUS;\n\tvec2 fragCoord = v_coord;\n\tvec2 src_size = u_iResolution;\n\tvec2 uv = v_coord;\n\tfloat n = float((radius + 1) * (radius + 1));\n\tint i;\n\tint j;\n\tvec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\tvec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\tvec3 c;\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm0 += c;\n\t\t\ts0 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm1 += c;\n\t\t\ts1 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm2 += c;\n\t\t\ts2 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm3 += c;\n\t\t\ts3 += c * c;\n\t\t}\n\t}\n\t\n\tfloat min_sigma2 = 1e+2;\n\tm0 /= n;\n\ts0 = abs(s0 / n - m0 * m0);\n\t\n\tfloat sigma2 = s0.r + s0.g + s0.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m0, 1.0);\n\t}\n\t\n\tm1 /= n;\n\ts1 = abs(s1 / n - m1 * m1);\n\t\n\tsigma2 = s1.r + s1.g + s1.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m1, 1.0);\n\t}\n\t\n\tm2 /= n;\n\ts2 = abs(s2 / n - m2 * m2);\n\t\n\tsigma2 = s2.r + s2.g + s2.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m2, 1.0);\n\t}\n\t\n\tm3 /= n;\n\ts3 = abs(s3 / n - m3 * m3);\n\t\n\tsigma2 = s3.r + s3.g + s3.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m3, 1.0);\n\t}\n}\n", F.registerNodeType("texture/kuwahara",H),I.title="XDoG Filter",I.desc="Filters a texture giving an artistic ink style",I.max_radius=10,I._shaders=[],I.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));I._xdog_shader||(I._xdog_shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,I.xdog_pixel_shader)); -var d=I._xdog_shader,c=GL.Mesh.getScreenQuad(),f=this.properties.sigma,h=this.properties.k,k=this.properties.p,e=this.properties.epsilon,g=this.properties.phi;a.bind(0);this._temp_texture.drawTo(function(){d.uniforms({src:0,sigma:f,k:h,p:k,epsilon:e,phi:g,cvsWidth:a.width,cvsHeight:a.height}).draw(c)});this.setOutputData(0,this._temp_texture)}},I.xdog_pixel_shader="\nprecision highp float;\nuniform sampler2D src;\n\nuniform float cvsHeight;\nuniform float cvsWidth;\n\nuniform float sigma;\nuniform float k;\nuniform float p;\nuniform float epsilon;\nuniform float phi;\nvarying vec2 v_coord;\n\nfloat cosh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat cosH = (tmp + 1.0 / tmp) / 2.0;\n\treturn cosH;\n}\n\nfloat tanh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\treturn tanH;\n}\n\nfloat sinh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat sinH = (tmp - 1.0 / tmp) / 2.0;\n\treturn sinH;\n}\n\nvoid main(void){\n\tvec3 destColor = vec3(0.0);\n\tfloat tFrag = 1.0 / cvsHeight;\n\tfloat sFrag = 1.0 / cvsWidth;\n\tvec2 Frag = vec2(sFrag,tFrag);\n\tvec2 uv = gl_FragCoord.st;\n\tfloat twoSigmaESquared = 2.0 * sigma * sigma;\n\tfloat twoSigmaRSquared = twoSigmaESquared * k * k;\n\tint halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\tconst int MAX_NUM_ITERATION = 99999;\n\tvec2 sum = vec2(0.0);\n\tvec2 norm = vec2(0.0);\n\n\tfor(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\t\tint i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\t\tint j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\t\tfloat d = length(vec2(i,j));\n\t\tvec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\t\t\t\t\t\t\texp( -d * d / twoSigmaRSquared ));\n\n\t\tvec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\t\tnorm += kernel;\n\t\tsum += kernel * L;\n\t}\n\n\tsum /= norm;\n\n\tfloat H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\tfloat edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\tdestColor = vec3(edge);\n\tgl_FragColor = vec4(destColor, 1.0);\n}", +var d=I._xdog_shader,c=GL.Mesh.getScreenQuad(),f=this.properties.sigma,h=this.properties.k,e=this.properties.p,g=this.properties.epsilon,l=this.properties.phi;a.bind(0);this._temp_texture.drawTo(function(){d.uniforms({src:0,sigma:f,k:h,p:e,epsilon:g,phi:l,cvsWidth:a.width,cvsHeight:a.height}).draw(c)});this.setOutputData(0,this._temp_texture)}},I.xdog_pixel_shader="\nprecision highp float;\nuniform sampler2D src;\n\nuniform float cvsHeight;\nuniform float cvsWidth;\n\nuniform float sigma;\nuniform float k;\nuniform float p;\nuniform float epsilon;\nuniform float phi;\nvarying vec2 v_coord;\n\nfloat cosh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat cosH = (tmp + 1.0 / tmp) / 2.0;\n\treturn cosH;\n}\n\nfloat tanh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\treturn tanH;\n}\n\nfloat sinh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat sinH = (tmp - 1.0 / tmp) / 2.0;\n\treturn sinH;\n}\n\nvoid main(void){\n\tvec3 destColor = vec3(0.0);\n\tfloat tFrag = 1.0 / cvsHeight;\n\tfloat sFrag = 1.0 / cvsWidth;\n\tvec2 Frag = vec2(sFrag,tFrag);\n\tvec2 uv = gl_FragCoord.st;\n\tfloat twoSigmaESquared = 2.0 * sigma * sigma;\n\tfloat twoSigmaRSquared = twoSigmaESquared * k * k;\n\tint halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\tconst int MAX_NUM_ITERATION = 99999;\n\tvec2 sum = vec2(0.0);\n\tvec2 norm = vec2(0.0);\n\n\tfor(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\t\tint i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\t\tint j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\t\tfloat d = length(vec2(i,j));\n\t\tvec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\t\t\t\t\t\t\texp( -d * d / twoSigmaRSquared ));\n\n\t\tvec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\t\tnorm += kernel;\n\t\tsum += kernel * L;\n\t}\n\n\tsum /= norm;\n\n\tfloat H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\tfloat edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\tdestColor = vec3(edge);\n\tgl_FragColor = vec4(destColor, 1.0);\n}", F.registerNodeType("texture/xDoG",I),G.title="Webcam",G.desc="Webcam texture",G.is_webcam_open=!1,G.prototype.openStream=function(){function a(d){G.is_webcam_open=!1;console.log("Webcam rejected",d);b._webcam_stream=!1;b.boxcolor="red";b.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](a);var b=this}},G.prototype.closeStream=function(){if(this._webcam_stream){var a= this._webcam_stream.getTracks();if(a.length)for(var b=0;b=this.size[1]||!this._video||(a.save(),a.webgl?this._video_texture&&a.drawImage(this._video_texture,0,0,this.size[0],this.size[1]):a.drawImage(this._video, 0,0,this.size[0],this.size[1]),a.restore())},G.prototype.onExecute=function(){null!=this._webcam_stream||this._waiting_confirmation||this.openStream();if(this._video&&this._video.videoWidth){var a=this._video.videoWidth,b=this._video.videoHeight,d=this._video_texture;d&&d.width==a&&d.height==b||(this._video_texture=new GL.Texture(a,b,{format:gl.RGB,filter:gl.LINEAR}));this._video_texture.uploadImage(this._video);this._video_texture.version=++this.version;this.properties.texture_name&&(c.getTexturesContainer()[this.properties.texture_name]= -this._video_texture);this.setOutputData(0,this._video_texture);for(a=1;aMath.abs(d))return c[1];d=(a-c[0])/d;return c[1]*(1-d)+f[1]*d}}return 0}},u.prototype.updateCurve=function(){for(var a=this._values,b=a.length/4,d=this.properties.split_channels,c=0;c=f;){h=0.5*(e+f)|0;c=a[h];if(c==d)break;if(f==e-1)return f;c -a&&(a=1);this.points&&this.points.length==3*a||(this.points=new Float32Array(3*a));this.properties.generate_normals?this.normals&&this.normals.length==this.points.length||(this.normals=new Float32Array(this.points.length)):this.normals=null;var d=this._last_radius||this.properties.radius,c=this.properties.mode,f=this.getInputData(0);this._old_obj_version=f?f._version:null;this.points=m.generatePoints(d,a,c,this.points,this.normals,this.properties.regular,f);this.version++};m.generatePoints=function(a, -d,c,f,h,e,g){var p=3*d;f&&f.length==p||(f=new Float32Array(p));var s=new Float32Array(3),l=new Float32Array([0,1,0]);if(e)if(c==m.RECTANGLE){p=Math.floor(Math.sqrt(d));for(d=0;de||rg&&gp))break}this.geometry.indices=this.indices=new Uint32Array(s)}this.indices&&this.indices.length? -(this.geometry.indices=this.indices,this.setOutputData(0,this.geometry)):this.setOutputData(0,null)}};A.registerNodeType("geometry/connectPoints",v);"undefined"!=typeof GL&&(D.title="to geometry",D.desc="converts a mesh to geometry",D.prototype.onExecute=function(){var a=this.getInputData(0);if(a){if(a!=this.last_mesh){this.last_mesh=a;for(i in a.vertexBuffers)this.geometry[i]=a.vertexBuffers[i].data;a.indexBuffers.triangles&&(this.geometry.indices=a.indexBuffers.triangles.data);this.geometry._id= -c();this.geometry._version=0}this.setOutputData(0,this.geometry);this.geometry&&this.setOutputData(1,this.geometry.vertices)}},A.registerNodeType("geometry/toGeometry",D),B.title="Geo to Mesh",B.prototype.updateMesh=function(a){this.mesh||(this.mesh=new GL.Mesh);for(var d in a)if("_"!=d[0]){var c=a[d],h=GL.Mesh.common_buffers[d];if(h||"indices"==d){var h=h?h.spacing:3,e=this.mesh.vertexBuffers[d];e&&e.data.length==c.length?(e.data.set(c),e.upload(GL.DYNAMIC_DRAW)):e=new GL.Buffer("indices"==d?GL.ELEMENT_ARRAY_BUFFER: -GL.ARRAY_BUFFER,c,h,GL.DYNAMIC_DRAW);this.mesh.addBuffer(d,e)}}if(this.mesh.vertexBuffers.normals&&this.mesh.vertexBuffers.normals.data.length!=this.mesh.vertexBuffers.vertices.data.length){c=new Float32Array([0,1,0]);h=new Float32Array(this.mesh.vertexBuffers.vertices.data.length);for(d=0;d=f;){e=0.5*(g+f)|0;c=a[e];if(c==d)break;if(f==g-1)return f;ca&&(a=1);this.points&&this.points.length==3*a||(this.points=new Float32Array(3*a));this.properties.generate_normals?this.normals&&this.normals.length==this.points.length||(this.normals=new Float32Array(this.points.length)):this.normals=null;var d=this._last_radius||this.properties.radius,c=this.properties.mode,f=this.getInputData(0);this._old_obj_version=f?f._version:null;this.points=m.generatePoints(d,a,c,this.points, +this.normals,this.properties.regular,f);this.version++};m.generatePoints=function(a,d,c,f,e,g,l){var p=3*d;f&&f.length==p||(f=new Float32Array(p));var k=new Float32Array(3),s=new Float32Array([0,1,0]);if(g)if(c==m.RECTANGLE){p=Math.floor(Math.sqrt(d));for(d=0;dg||tl&&lp))break}this.geometry.indices=this.indices=new Uint32Array(k)}this.indices&&this.indices.length?(this.geometry.indices=this.indices,this.setOutputData(0,this.geometry)):this.setOutputData(0,null)}};A.registerNodeType("geometry/connectPoints",v);"undefined"!=typeof GL&&(E.title="to geometry",E.desc="converts a mesh to geometry",E.prototype.onExecute= +function(){var a=this.getInputData(0);if(a){if(a!=this.last_mesh){this.last_mesh=a;for(i in a.vertexBuffers)this.geometry[i]=a.vertexBuffers[i].data;a.indexBuffers.triangles&&(this.geometry.indices=a.indexBuffers.triangles.data);this.geometry._id=c();this.geometry._version=0}this.setOutputData(0,this.geometry);this.geometry&&this.setOutputData(1,this.geometry.vertices)}},A.registerNodeType("geometry/toGeometry",E),B.title="Geo to Mesh",B.prototype.updateMesh=function(a){this.mesh||(this.mesh=new GL.Mesh); +for(var d in a)if("_"!=d[0]){var c=a[d],f=GL.Mesh.common_buffers[d];if(f||"indices"==d){var f=f?f.spacing:3,e=this.mesh.vertexBuffers[d];e&&e.data.length==c.length?(e.data.set(c),e.upload(GL.DYNAMIC_DRAW)):e=new GL.Buffer("indices"==d?GL.ELEMENT_ARRAY_BUFFER:GL.ARRAY_BUFFER,c,f,GL.DYNAMIC_DRAW);this.mesh.addBuffer(d,e)}}if(this.mesh.vertexBuffers.normals&&this.mesh.vertexBuffers.normals.data.length!=this.mesh.vertexBuffers.vertices.data.length){c=new Float32Array([0,1,0]);f=new Float32Array(this.mesh.vertexBuffers.vertices.data.length); +for(d=0;d=c.NOTEON||g<=c.NOTEOFF)this.channel= -e&15};Object.defineProperty(c.prototype,"velocity",{get:function(){return this.cmd==c.NOTEON?this.data[2]:-1},set:function(c){this.data[2]=c},enumerable:!0});c.notes="A A# B C C# D D# E F F# G G#".split(" ");c.note_to_index={A:0,"A#":1,B:2,C:3,"C#":4,D:5,"D#":6,E:7,F:8,"F#":9,G:10,"G#":11};Object.defineProperty(c.prototype,"note",{get:function(){return this.cmd!=c.NOTEON?-1:c.toNoteString(this.data[1],!0)},set:function(c){throw"notes cannot be assigned this way, must modify the data[1]";},enumerable:!0}); -Object.defineProperty(c.prototype,"octave",{get:function(){return this.cmd!=c.NOTEON?-1:Math.floor((this.data[1]-24)/12+1)},set:function(c){throw"octave cannot be assigned this way, must modify the data[1]";},enumerable:!0});c.prototype.getPitch=function(){return 440*Math.pow(2,(this.data[1]-69)/12)};c.computePitch=function(c){return 440*Math.pow(2,(c-69)/12)};c.prototype.getCC=function(){return this.data[1]};c.prototype.getCCValue=function(){return this.data[2]};c.prototype.getPitchBend=function(){return this.data[1]+ -(this.data[2]<<7)-8192};c.computePitchBend=function(c,e){return c+(e<<7)-8192};c.prototype.setCommandFromString=function(e){this.cmd=c.computeCommandFromString(e)};c.computeCommandFromString=function(e){if(!e)return 0;if(e&&e.constructor===Number)return e;e=e.toUpperCase();switch(e){case "NOTE ON":case "NOTEON":return c.NOTEON;case "NOTE OFF":case "NOTEOFF":return c.NOTEON;case "KEY PRESSURE":case "KEYPRESSURE":return c.KEYPRESSURE;case "CONTROLLER CHANGE":case "CONTROLLERCHANGE":case "CC":return c.CONTROLLERCHANGE; -case "PROGRAM CHANGE":case "PROGRAMCHANGE":case "PC":return c.PROGRAMCHANGE;case "CHANNEL PRESSURE":case "CHANNELPRESSURE":return c.CHANNELPRESSURE;case "PITCH BEND":case "PITCHBEND":return c.PITCHBEND;case "TIME TICK":case "TIMETICK":return c.TIMETICK;default:return Number(e)}};c.toNoteString=function(e,g){e=Math.round(e);var l,h=Math.floor((e-24)/12+1);l=(e-21)%12;0>l&&(l=12+l);return c.notes[l]+(g?"":h)};c.NoteStringToPitch=function(e){e=e.toUpperCase();var g=e[0],l=4;"#"==e[1]?(g+="#",2this.properties.max_value)return;this.trigger("on_midi",g)}};q.registerNodeType("midi/filter",e);C.title="MIDIEvent";C.desc="Create a MIDI Event";C.color="#243";C.prototype.onAction=function(e,g){"assign"== -e?(this.properties.channel=g.channel,this.properties.cmd=g.cmd,this.properties.value1=g.data[1],this.properties.value2=g.data[2],g.cmd==c.NOTEON?this.gate=!0:g.cmd==c.NOTEOFF&&(this.gate=!1)):(g=this.midi_event,g.channel=this.properties.channel,this.properties.cmd&&this.properties.cmd.constructor===String?g.setCommandFromString(this.properties.cmd):g.cmd=this.properties.cmd,g.data[0]=g.cmd|g.channel,g.data[1]=Number(this.properties.value1),g.data[2]=Number(this.properties.value2),this.trigger("on_midi", -g))};C.prototype.onExecute=function(){var e=this.properties;if(this.inputs)for(var g=0;gc;++c)this.valid_notes[c]=-1!=this.notes_pitches.indexOf(c);for(c=0;12>c;++c)if(this.valid_notes[c])this.offset_notes[c]=0;else for(var e= -1;12>e;++e){if(this.valid_notes[(c-e)%12]){this.offset_notes[c]=-e;break}if(this.valid_notes[(c+e)%12]){this.offset_notes[c]=e;break}}};B.prototype.onAction=function(e,g){g&&g.constructor===c&&(g.data[0]==c.NOTEON||g.data[0]==c.NOTEOFF?(this.midi_event=new c,this.midi_event.setup(g.data),this.midi_event.data[1]+=this.offset_notes[c.note_to_index[g.note]],this.trigger("out",this.midi_event)):this.trigger("out",g))};B.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&c!=this._current_scale&& -this.processScale(c)};q.registerNodeType("midi/quantize",B);g.title="MIDI Play";g.desc="Plays a MIDI note";g.color="#243";g.prototype.onAction=function(e,g){if(g&&g.constructor===c){if(this.instrument&&g.data[0]==c.NOTEON){var l=g.note;if(!l||"undefined"==l||l.constructor!==String)return;this.instrument.play(l,g.octave,this.properties.duration,this.properties.volume)}this.trigger("note",g)}};g.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&(this.properties.volume=c);c=this.getInputData(2); -null!=c&&(this.properties.duration=c)};q.registerNodeType("midi/play",g);r.title="MIDI Keys";r.desc="Keyboard to play notes";r.color="#243";r.keys=[{x:0,w:1,h:1,t:0},{x:0.75,w:0.5,h:0.6,t:1},{x:1,w:1,h:1,t:0},{x:1.75,w:0.5,h:0.6,t:1},{x:2,w:1,h:1,t:0},{x:2.75,w:0.5,h:0.6,t:1},{x:3,w:1,h:1,t:0},{x:4,w:1,h:1,t:0},{x:4.75,w:0.5,h:0.6,t:1},{x:5,w:1,h:1,t:0},{x:5.75,w:0.5,h:0.6,t:1},{x:6,w:1,h:1,t:0}];r.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){var e=12*this.properties.num_octaves; -this.keys.length=e;var g=this.size[0]/(7*this.properties.num_octaves),h=this.size[1];c.globalAlpha=1;for(var a=0;2>a;a++)for(var b=0;bd+k||c[1]>b))return a}}return-1};r.prototype.onAction=function(e,g){if("reset"==e)for(var l=0;lg[1])){var l=this.getKeyIndex(g);this.keys[l]=!0;this._last_key= -l;var l=12*(this.properties.start_octave-1)+29+l,h=new c;h.setup([c.NOTEON,l,100]);this.trigger("note",h);return!0}};r.prototype.onMouseMove=function(e,g){if(!(0>g[1]||-1==this._last_key)){this.setDirtyCanvas(!0);var l=this.getKeyIndex(g);if(this._last_key==l)return!0;this.keys[this._last_key]=!1;var h=12*(this.properties.start_octave-1)+29+this._last_key,a=new c;a.setup([c.NOTEOFF,h,100]);this.trigger("note",a);this.keys[l]=!0;h=12*(this.properties.start_octave-1)+29+l;a=new c;a.setup([c.NOTEON, -h,100]);this.trigger("note",a);this._last_key=l;return!0}};r.prototype.onMouseUp=function(e,g){if(!(0>g[1])){var l=this.getKeyIndex(g);this.keys[l]=!1;this._last_key=-1;var l=12*(this.properties.start_octave-1)+29+l,h=new c;h.setup([c.NOTEOFF,l,100]);this.trigger("note",h);return!0}};q.registerNodeType("midi/keys",r)})(this); -(function(y){function c(){this.properties={src:"",gain:0.5,loop:!0,autoplay:!0,playbackRate:1};this._loading_audio=!1;this._audiobuffer=null;this._audionodes=[];this._last_sourcenode=null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=t.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain;this.properties.src&&this.loadSound(this.properties.src)}function m(){this.properties={gain:0.5};this._audionodes=[];this._media_stream= -null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=t.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain}function n(){this.properties={fftSize:2048,minDecibels:-100,maxDecibels:-10,smoothingTimeConstant:0.5};this.audionode=t.getAudioContext().createAnalyser();this.audionode.graphnode=this;this.audionode.fftSize=this.properties.fftSize;this.audionode.minDecibels=this.properties.minDecibels;this.audionode.maxDecibels= -this.properties.maxDecibels;this.audionode.smoothingTimeConstant=this.properties.smoothingTimeConstant;this.addInput("in","audio");this.addOutput("freqs","array");this.addOutput("samples","array");this._time_bin=this._freq_bin=null}function l(){this.properties={gain:1};this.audionode=t.getAudioContext().createGain();this.addInput("in","audio");this.addInput("gain","number");this.addOutput("out","audio")}function x(){this.properties={impulse_src:"",normalize:!0};this.audionode=t.getAudioContext().createConvolver(); -this.addInput("in","audio");this.addOutput("out","audio")}function e(){this.properties={threshold:-50,knee:40,ratio:12,reduction:-20,attack:0,release:0.25};this.audionode=t.getAudioContext().createDynamicsCompressor();this.addInput("in","audio");this.addOutput("out","audio")}function C(){this.properties={};this.audionode=t.getAudioContext().createWaveShaper();this.addInput("in","audio");this.addInput("shape","waveshape");this.addOutput("out","audio")}function z(){this.properties={gain1:0.5,gain2:0.5}; -this.audionode=t.getAudioContext().createGain();this.audionode1=t.getAudioContext().createGain();this.audionode1.gain.value=this.properties.gain1;this.audionode2=t.getAudioContext().createGain();this.audionode2.gain.value=this.properties.gain2;this.audionode1.connect(this.audionode);this.audionode2.connect(this.audionode);this.addInput("in1","audio");this.addInput("in1 gain","number");this.addInput("in2","audio");this.addInput("in2 gain","number");this.addOutput("out","audio")}function v(){this.properties= -{A:0.1,D:0.1,S:0.1,R:0.1};this.audionode=t.getAudioContext().createGain();this.audionode.gain.value=0;this.addInput("in","audio");this.addInput("gate","bool");this.addOutput("out","audio");this.gate=!1}function D(){this.properties={delayTime:0.5};this.audionode=t.getAudioContext().createDelay(10);this.audionode.delayTime.value=this.properties.delayTime;this.addInput("in","audio");this.addInput("time","number");this.addOutput("out","audio")}function B(){this.properties={frequency:350,detune:0,Q:1}; -this.addProperty("type","lowpass","enum",{values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")});this.audionode=t.getAudioContext().createBiquadFilter();this.addInput("in","audio");this.addOutput("out","audio")}function g(){this.properties={frequency:440,detune:0,type:"sine"};this.addProperty("type","sine","enum",{values:["sine","square","sawtooth","triangle","custom"]});this.audionode=t.getAudioContext().createOscillator();this.addOutput("out","audio")}function r(){this.properties= -{continuous:!0,mark:-1};this.addInput("data","array");this.addInput("mark","number");this.size=[300,200];this._last_buffer=null}function q(){this.properties={band:440,amplitude:1};this.addInput("freqs","array");this.addOutput("signal","number")}function A(){if(!A.default_code){var c=A.default_function.toString(),a=c.indexOf("{")+1,b=c.lastIndexOf("}");A.default_code=c.substr(a,b-a)}this.properties={code:A.default_code};c=t.getAudioContext();c.createScriptProcessor?this.audionode=c.createScriptProcessor(4096, -1,1):(console.warn("ScriptProcessorNode deprecated"),this.audionode=c.createGain());this.processCode();A._bypass_function||(A._bypass_function=this.audionode.onaudioprocess);this.addInput("in","audio");this.addOutput("out","audio")}function s(){this.audionode=t.getAudioContext().destination;this.addInput("in","audio")}var p=y.LiteGraph,t={};y.LGAudio=t;t.getAudioContext=function(){if(!this._audio_context){window.AudioContext=window.AudioContext||window.webkitAudioContext;if(!window.AudioContext)return console.error("AudioContext not supported by browser"), -null;this._audio_context=new AudioContext;this._audio_context.onmessage=function(c){console.log("msg",c)};this._audio_context.onended=function(c){console.log("ended",c)};this._audio_context.oncomplete=function(c){console.log("complete",c)}}return this._audio_context};t.connect=function(c,a){try{c.connect(a)}catch(b){console.warn("LGraphAudio:",b)}};t.disconnect=function(c,a){try{c.disconnect(a)}catch(b){console.warn("LGraphAudio:",b)}};t.changeAllAudiosConnections=function(c,a){if(c.inputs)for(var b= -0;b=c.NOTEON||g<=c.NOTEOFF)this.channel=e&15};Object.defineProperty(c.prototype,"velocity",{get:function(){return this.cmd==c.NOTEON?this.data[2]:-1},set:function(c){this.data[2]=c},enumerable:!0});c.notes="A A# B C C# D D# E F F# G G#".split(" ");c.note_to_index={A:0,"A#":1,B:2,C:3,"C#":4,D:5,"D#":6,E:7,F:8,"F#":9,G:10,"G#":11};Object.defineProperty(c.prototype,"note",{get:function(){return this.cmd!=c.NOTEON?-1:c.toNoteString(this.data[1], +!0)},set:function(c){throw"notes cannot be assigned this way, must modify the data[1]";},enumerable:!0});Object.defineProperty(c.prototype,"octave",{get:function(){return this.cmd!=c.NOTEON?-1:Math.floor((this.data[1]-24)/12+1)},set:function(c){throw"octave cannot be assigned this way, must modify the data[1]";},enumerable:!0});c.prototype.getPitch=function(){return 440*Math.pow(2,(this.data[1]-69)/12)};c.computePitch=function(c){return 440*Math.pow(2,(c-69)/12)};c.prototype.getCC=function(){return this.data[1]}; +c.prototype.getCCValue=function(){return this.data[2]};c.prototype.getPitchBend=function(){return this.data[1]+(this.data[2]<<7)-8192};c.computePitchBend=function(c,e){return c+(e<<7)-8192};c.prototype.setCommandFromString=function(e){this.cmd=c.computeCommandFromString(e)};c.computeCommandFromString=function(e){if(!e)return 0;if(e&&e.constructor===Number)return e;e=e.toUpperCase();switch(e){case "NOTE ON":case "NOTEON":return c.NOTEON;case "NOTE OFF":case "NOTEOFF":return c.NOTEON;case "KEY PRESSURE":case "KEYPRESSURE":return c.KEYPRESSURE; +case "CONTROLLER CHANGE":case "CONTROLLERCHANGE":case "CC":return c.CONTROLLERCHANGE;case "PROGRAM CHANGE":case "PROGRAMCHANGE":case "PC":return c.PROGRAMCHANGE;case "CHANNEL PRESSURE":case "CHANNELPRESSURE":return c.CHANNELPRESSURE;case "PITCH BEND":case "PITCHBEND":return c.PITCHBEND;case "TIME TICK":case "TIMETICK":return c.TIMETICK;default:return Number(e)}};c.toNoteString=function(e,g){e=Math.round(e);var k,l=Math.floor((e-24)/12+1);k=(e-21)%12;0>k&&(k=12+k);return c.notes[k]+(g?"":l)};c.NoteStringToPitch= +function(e){e=e.toUpperCase();var g=e[0],k=4;"#"==e[1]?(g+="#",2this.properties.max_value)return;this.trigger("on_midi", +g)}};t.registerNodeType("midi/filter",g);C.title="MIDIEvent";C.desc="Create a MIDI Event";C.color="#243";C.prototype.onAction=function(e,g){"assign"==e?(this.properties.channel=g.channel,this.properties.cmd=g.cmd,this.properties.value1=g.data[1],this.properties.value2=g.data[2],g.cmd==c.NOTEON?this.gate=!0:g.cmd==c.NOTEOFF&&(this.gate=!1)):(g=this.midi_event,g.channel=this.properties.channel,this.properties.cmd&&this.properties.cmd.constructor===String?g.setCommandFromString(this.properties.cmd): +g.cmd=this.properties.cmd,g.data[0]=g.cmd|g.channel,g.data[1]=Number(this.properties.value1),g.data[2]=Number(this.properties.value2),this.trigger("on_midi",g))};C.prototype.onExecute=function(){var e=this.properties;if(this.inputs)for(var g=0;gc;++c)this.valid_notes[c]=-1!= +this.notes_pitches.indexOf(c);for(c=0;12>c;++c)if(this.valid_notes[c])this.offset_notes[c]=0;else for(var e=1;12>e;++e){if(this.valid_notes[(c-e)%12]){this.offset_notes[c]=-e;break}if(this.valid_notes[(c+e)%12]){this.offset_notes[c]=e;break}}};B.prototype.onAction=function(e,g){g&&g.constructor===c&&(g.data[0]==c.NOTEON||g.data[0]==c.NOTEOFF?(this.midi_event=new c,this.midi_event.setup(g.data),this.midi_event.data[1]+=this.offset_notes[c.note_to_index[g.note]],this.trigger("out",this.midi_event)): +this.trigger("out",g))};B.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&c!=this._current_scale&&this.processScale(c)};t.registerNodeType("midi/quantize",B);e.title="MIDI Play";e.desc="Plays a MIDI note";e.color="#243";e.prototype.onAction=function(e,g){if(g&&g.constructor===c){if(this.instrument&&g.data[0]==c.NOTEON){var k=g.note;if(!k||"undefined"==k||k.constructor!==String)return;this.instrument.play(k,g.octave,this.properties.duration,this.properties.volume)}this.trigger("note", +g)}};e.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&(this.properties.volume=c);c=this.getInputData(2);null!=c&&(this.properties.duration=c)};t.registerNodeType("midi/play",e);q.title="MIDI Keys";q.desc="Keyboard to play notes";q.color="#243";q.keys=[{x:0,w:1,h:1,t:0},{x:0.75,w:0.5,h:0.6,t:1},{x:1,w:1,h:1,t:0},{x:1.75,w:0.5,h:0.6,t:1},{x:2,w:1,h:1,t:0},{x:2.75,w:0.5,h:0.6,t:1},{x:3,w:1,h:1,t:0},{x:4,w:1,h:1,t:0},{x:4.75,w:0.5,h:0.6,t:1},{x:5,w:1,h:1,t:0},{x:5.75,w:0.5,h:0.6,t:1}, +{x:6,w:1,h:1,t:0}];q.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){var e=12*this.properties.num_octaves;this.keys.length=e;var g=this.size[0]/(7*this.properties.num_octaves),l=this.size[1];c.globalAlpha=1;for(var a=0;2>a;a++)for(var b=0;bd+h||c[1]>b))return a}}return-1};q.prototype.onAction=function(e,g){if("reset"==e)for(var k=0;kg[1])){var k=this.getKeyIndex(g);this.keys[k]=!0;this._last_key=k;var k=12*(this.properties.start_octave-1)+29+k,l=new c;l.setup([c.NOTEON,k,100]);this.trigger("note",l);return!0}};q.prototype.onMouseMove=function(e,g){if(!(0>g[1]||-1==this._last_key)){this.setDirtyCanvas(!0);var k=this.getKeyIndex(g);if(this._last_key==k)return!0;this.keys[this._last_key]=!1;var l=12*(this.properties.start_octave-1)+29+this._last_key,a=new c;a.setup([c.NOTEOFF,l,100]); +this.trigger("note",a);this.keys[k]=!0;l=12*(this.properties.start_octave-1)+29+k;a=new c;a.setup([c.NOTEON,l,100]);this.trigger("note",a);this._last_key=k;return!0}};q.prototype.onMouseUp=function(e,g){if(!(0>g[1])){var k=this.getKeyIndex(g);this.keys[k]=!1;this._last_key=-1;var k=12*(this.properties.start_octave-1)+29+k,l=new c;l.setup([c.NOTEOFF,k,100]);this.trigger("note",l);return!0}};t.registerNodeType("midi/keys",q)})(this); +(function(y){function c(){this.properties={src:"",gain:0.5,loop:!0,autoplay:!0,playbackRate:1};this._loading_audio=!1;this._audiobuffer=null;this._audionodes=[];this._last_sourcenode=null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=w.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain;this.properties.src&&this.loadSound(this.properties.src)}function m(){this.properties={gain:0.5};this._audionodes=[];this._media_stream= +null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=w.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain}function n(){this.properties={fftSize:2048,minDecibels:-100,maxDecibels:-10,smoothingTimeConstant:0.5};this.audionode=w.getAudioContext().createAnalyser();this.audionode.graphnode=this;this.audionode.fftSize=this.properties.fftSize;this.audionode.minDecibels=this.properties.minDecibels;this.audionode.maxDecibels= +this.properties.maxDecibels;this.audionode.smoothingTimeConstant=this.properties.smoothingTimeConstant;this.addInput("in","audio");this.addOutput("freqs","array");this.addOutput("samples","array");this._time_bin=this._freq_bin=null}function k(){this.properties={gain:1};this.audionode=w.getAudioContext().createGain();this.addInput("in","audio");this.addInput("gain","number");this.addOutput("out","audio")}function x(){this.properties={impulse_src:"",normalize:!0};this.audionode=w.getAudioContext().createConvolver(); +this.addInput("in","audio");this.addOutput("out","audio")}function g(){this.properties={threshold:-50,knee:40,ratio:12,reduction:-20,attack:0,release:0.25};this.audionode=w.getAudioContext().createDynamicsCompressor();this.addInput("in","audio");this.addOutput("out","audio")}function C(){this.properties={};this.audionode=w.getAudioContext().createWaveShaper();this.addInput("in","audio");this.addInput("shape","waveshape");this.addOutput("out","audio")}function z(){this.properties={gain1:0.5,gain2:0.5}; +this.audionode=w.getAudioContext().createGain();this.audionode1=w.getAudioContext().createGain();this.audionode1.gain.value=this.properties.gain1;this.audionode2=w.getAudioContext().createGain();this.audionode2.gain.value=this.properties.gain2;this.audionode1.connect(this.audionode);this.audionode2.connect(this.audionode);this.addInput("in1","audio");this.addInput("in1 gain","number");this.addInput("in2","audio");this.addInput("in2 gain","number");this.addOutput("out","audio")}function v(){this.properties= +{A:0.1,D:0.1,S:0.1,R:0.1};this.audionode=w.getAudioContext().createGain();this.audionode.gain.value=0;this.addInput("in","audio");this.addInput("gate","bool");this.addOutput("out","audio");this.gate=!1}function E(){this.properties={delayTime:0.5};this.audionode=w.getAudioContext().createDelay(10);this.audionode.delayTime.value=this.properties.delayTime;this.addInput("in","audio");this.addInput("time","number");this.addOutput("out","audio")}function B(){this.properties={frequency:350,detune:0,Q:1}; +this.addProperty("type","lowpass","enum",{values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")});this.audionode=w.getAudioContext().createBiquadFilter();this.addInput("in","audio");this.addOutput("out","audio")}function e(){this.properties={frequency:440,detune:0,type:"sine"};this.addProperty("type","sine","enum",{values:["sine","square","sawtooth","triangle","custom"]});this.audionode=w.getAudioContext().createOscillator();this.addOutput("out","audio")}function q(){this.properties= +{continuous:!0,mark:-1};this.addInput("data","array");this.addInput("mark","number");this.size=[300,200];this._last_buffer=null}function t(){this.properties={band:440,amplitude:1};this.addInput("freqs","array");this.addOutput("signal","number")}function A(){if(!A.default_code){var c=A.default_function.toString(),a=c.indexOf("{")+1,b=c.lastIndexOf("}");A.default_code=c.substr(a,b-a)}this.properties={code:A.default_code};c=w.getAudioContext();c.createScriptProcessor?this.audionode=c.createScriptProcessor(4096, +1,1):(console.warn("ScriptProcessorNode deprecated"),this.audionode=c.createGain());this.processCode();A._bypass_function||(A._bypass_function=this.audionode.onaudioprocess);this.addInput("in","audio");this.addOutput("out","audio")}function p(){this.audionode=w.getAudioContext().destination;this.addInput("in","audio")}var s=y.LiteGraph,w={};y.LGAudio=w;w.getAudioContext=function(){if(!this._audio_context){window.AudioContext=window.AudioContext||window.webkitAudioContext;if(!window.AudioContext)return console.error("AudioContext not supported by browser"), +null;this._audio_context=new AudioContext;this._audio_context.onmessage=function(c){console.log("msg",c)};this._audio_context.onended=function(c){console.log("ended",c)};this._audio_context.oncomplete=function(c){console.log("complete",c)}}return this._audio_context};w.connect=function(c,a){try{c.connect(a)}catch(b){console.warn("LGraphAudio:",b)}};w.disconnect=function(c,a){try{c.disconnect(a)}catch(b){console.warn("LGraphAudio:",b)}};w.changeAllAudiosConnections=function(c,a){if(c.inputs)for(var b= +0;b=this.size[0]&&(e=this.size[0]-1),c.strokeStyle="red",c.beginPath(),c.moveTo(e,d),c.lineTo(e,0),c.stroke())}};r.title="Visualization";r.desc="Audio Visualization";p.registerNodeType("audio/visualization",r);q.prototype.onExecute=function(){if(this._freqs=this.getInputData(0)){var c=this.properties.band,a=this.getInputData(1);void 0!==a&&(c=a);a=t.getAudioContext().sampleRate/this._freqs.length;a=c/a*2;a>=this._freqs.length?a=this._freqs[this._freqs.length-1]:(c=a|0, -a-=c,a=this._freqs[c]*(1-a)+this._freqs[c+1]*a);this.setOutputData(0,a/255*this.properties.amplitude)}};q.prototype.onGetInputs=function(){return[["band","number"]]};q.title="Signal";q.desc="extract the signal of some frequency";p.registerNodeType("audio/signal",q);A.prototype.onAdded=function(c){c.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback)};A["@code"]={widget:"code"};A.prototype.onStart=function(){this.audionode.onaudioprocess=this._callback};A.prototype.onStop= +b&&(this.audionode[a.name].value=b)}}};n.prototype.onGetInputs=function(){return[["minDecibels","number"],["maxDecibels","number"],["smoothingTimeConstant","number"]]};n.prototype.onGetOutputs=function(){return[["freqs","array"],["samples","array"]]};n.title="Analyser";n.desc="Audio Analyser";s.registerNodeType("audio/analyser",n);k.prototype.onExecute=function(){if(this.inputs&&this.inputs.length)for(var c=1;c=this.size[0]&&(e=this.size[0]-1),c.strokeStyle="red",c.beginPath(),c.moveTo(e,d),c.lineTo(e,0),c.stroke())}};q.title="Visualization";q.desc="Audio Visualization";s.registerNodeType("audio/visualization",q);t.prototype.onExecute=function(){if(this._freqs=this.getInputData(0)){var c=this.properties.band,a=this.getInputData(1);void 0!==a&&(c=a);a=w.getAudioContext().sampleRate/this._freqs.length;a=c/a*2;a>=this._freqs.length?a=this._freqs[this._freqs.length-1]:(c=a|0, +a-=c,a=this._freqs[c]*(1-a)+this._freqs[c+1]*a);this.setOutputData(0,a/255*this.properties.amplitude)}};t.prototype.onGetInputs=function(){return[["band","number"]]};t.title="Signal";t.desc="extract the signal of some frequency";s.registerNodeType("audio/signal",t);A.prototype.onAdded=function(c){c.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback)};A["@code"]={widget:"code",type:"code"};A.prototype.onStart=function(){this.audionode.onaudioprocess=this._callback};A.prototype.onStop= function(){this.audionode.onaudioprocess=A._bypass_function};A.prototype.onPause=function(){this.audionode.onaudioprocess=A._bypass_function};A.prototype.onUnpause=function(){this.audionode.onaudioprocess=this._callback};A.prototype.onExecute=function(){};A.prototype.onRemoved=function(){this.audionode.onaudioprocess=A._bypass_function};A.prototype.processCode=function(){try{this._script=new new Function("properties",this.properties.code)(this.properties),this._old_code=this.properties.code,this._callback= this._script.onaudioprocess}catch(c){console.error("Error in onaudioprocess code",c),this._callback=A._bypass_function,this.audionode.onaudioprocess=this._callback}};A.prototype.onPropertyChanged=function(c,a){"code"==c&&(this.properties.code=a,this.processCode(),this.graph&&this.graph.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback))};A.default_function=function(){this.onaudioprocess=function(c){var a=c.inputBuffer;c=c.outputBuffer;for(var b=0;b + diff --git a/demo/js/code.js b/demo/js/code.js index c1b7972bb..cf7fd00bf 100644 --- a/demo/js/code.js +++ b/demo/js/code.js @@ -1,3 +1,4 @@ +var webgl_canvas = null; LiteGraph.node_images_path = "../nodes_data/"; var editor = new LiteGraph.Editor("main"); @@ -10,10 +11,13 @@ window.onbeforeunload = function(){ localStorage.setItem("litegraphg demo backup", data ); } +//enable scripting +LiteGraph.allow_scripts = true; + //create scene selector var elem = document.createElement("span"); elem.className = "selector"; -elem.innerHTML = "Demo "; +elem.innerHTML = "Demo | "; editor.tools.appendChild(elem); var select = elem.querySelector("select"); select.addEventListener("change", function(e){ @@ -54,6 +58,9 @@ elem.querySelector("#download").addEventListener("click",function(){ setTimeout( function(){ URL.revokeObjectURL( url ); }, 1000*60 ); //wait one minute to revoke url }); +elem.querySelector("#webgl").addEventListener("click", enableWebGL ); + + function addDemo( name, url ) { var option = document.createElement("option"); @@ -81,5 +88,80 @@ addDemo("autobackup", function(){ graph.configure( graph_data ); }); +//allows to use the WebGL nodes like textures +function enableWebGL() +{ + if( webgl_canvas ) + { + webgl_canvas.style.display = (webgl_canvas.style.display == "none" ? "block" : "none"); + return; + } + + var libs = [ + "js/libs/gl-matrix-min.js", + "js/libs/litegl.js", + "../src/nodes/gltextures.js", + "../src/nodes/glfx.js", + "../src/nodes/geometry.js" + ]; + + function fetchJS() + { + if(libs.length == 0) + return on_ready(); + + var script = null; + script = document.createElement("script"); + script.onload = fetchJS; + script.src = libs.shift(); + document.head.appendChild(script); + } + + fetchJS(); + + function on_ready() + { + console.log(this.src); + if(!window.GL) + return; + webgl_canvas = document.createElement("canvas"); + webgl_canvas.width = 400; + webgl_canvas.height = 300; + webgl_canvas.style.position = "absolute"; + webgl_canvas.style.top = "0px"; + webgl_canvas.style.right = "0px"; + webgl_canvas.style.border = "1px solid #AAA"; + + webgl_canvas.addEventListener("click", function(){ + var rect = webgl_canvas.parentNode.getBoundingClientRect(); + if( webgl_canvas.width != rect.width ) + { + webgl_canvas.width = rect.width; + webgl_canvas.height = rect.height; + } + else + { + webgl_canvas.width = 400; + webgl_canvas.height = 300; + } + }); + + var parent = document.querySelector(".editor-area"); + parent.appendChild( webgl_canvas ); + var gl = GL.create({ canvas: webgl_canvas }); + if(!gl) + return; + + editor.graph.onBeforeStep = ondraw; + + console.log("webgl ready"); + function ondraw () + { + gl.clearColor(0,0,0,0); + gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT ); + gl.viewport(0,0,gl.canvas.width, gl.canvas.height ); + } + } +} diff --git a/demo/js/demos.js b/demo/js/demos.js index 921704f66..ea1e06a3c 100644 --- a/demo/js/demos.js +++ b/demo/js/demos.js @@ -99,7 +99,6 @@ TestWidgetsNode.title = "Widgets"; LiteGraph.registerNodeType("features/widgets", TestWidgetsNode ); - //Show value inside the debug console function TestSpecialNode() { diff --git a/demo/js/libs/gl-matrix-min.js b/demo/js/libs/gl-matrix-min.js new file mode 100644 index 000000000..747b3a77e --- /dev/null +++ b/demo/js/libs/gl-matrix-min.js @@ -0,0 +1,28 @@ +/*! +@fileoverview gl-matrix - High performance matrix and vector operations +@author Brandon Jones +@author Colin MacKenzie IV +@version 2.7.0 + +Copyright (c) 2015-2018, Brandon Jones, Colin MacKenzie IV. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ +!function(t,n){if("object"==typeof exports&&"object"==typeof module)module.exports=n();else if("function"==typeof define&&define.amd)define([],n);else{var r=n();for(var a in r)("object"==typeof exports?exports:t)[a]=r[a]}}("undefined"!=typeof self?self:this,function(){return function(t){var n={};function r(a){if(n[a])return n[a].exports;var e=n[a]={i:a,l:!1,exports:{}};return t[a].call(e.exports,e,e.exports,r),e.l=!0,e.exports}return r.m=t,r.c=n,r.d=function(t,n,a){r.o(t,n)||Object.defineProperty(t,n,{enumerable:!0,get:a})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,n){if(1&n&&(t=r(t)),8&n)return t;if(4&n&&"object"==typeof t&&t&&t.__esModule)return t;var a=Object.create(null);if(r.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:t}),2&n&&"string"!=typeof t)for(var e in t)r.d(a,e,function(n){return t[n]}.bind(null,e));return a},r.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(n,"a",n),n},r.o=function(t,n){return Object.prototype.hasOwnProperty.call(t,n)},r.p="",r(r.s=10)}([function(t,n,r){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.setMatrixArrayType=function(t){n.ARRAY_TYPE=t},n.toRadian=function(t){return t*e},n.equals=function(t,n){return Math.abs(t-n)<=a*Math.max(1,Math.abs(t),Math.abs(n))};var a=n.EPSILON=1e-6;n.ARRAY_TYPE="undefined"!=typeof Float32Array?Float32Array:Array,n.RANDOM=Math.random;var e=Math.PI/180},function(t,n,r){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.forEach=n.sqrLen=n.len=n.sqrDist=n.dist=n.div=n.mul=n.sub=void 0,n.create=e,n.clone=function(t){var n=new a.ARRAY_TYPE(4);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n},n.fromValues=function(t,n,r,e){var u=new a.ARRAY_TYPE(4);return u[0]=t,u[1]=n,u[2]=r,u[3]=e,u},n.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t},n.set=function(t,n,r,a,e){return t[0]=n,t[1]=r,t[2]=a,t[3]=e,t},n.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t[3]=n[3]+r[3],t},n.subtract=u,n.multiply=o,n.divide=i,n.ceil=function(t,n){return t[0]=Math.ceil(n[0]),t[1]=Math.ceil(n[1]),t[2]=Math.ceil(n[2]),t[3]=Math.ceil(n[3]),t},n.floor=function(t,n){return t[0]=Math.floor(n[0]),t[1]=Math.floor(n[1]),t[2]=Math.floor(n[2]),t[3]=Math.floor(n[3]),t},n.min=function(t,n,r){return t[0]=Math.min(n[0],r[0]),t[1]=Math.min(n[1],r[1]),t[2]=Math.min(n[2],r[2]),t[3]=Math.min(n[3],r[3]),t},n.max=function(t,n,r){return t[0]=Math.max(n[0],r[0]),t[1]=Math.max(n[1],r[1]),t[2]=Math.max(n[2],r[2]),t[3]=Math.max(n[3],r[3]),t},n.round=function(t,n){return t[0]=Math.round(n[0]),t[1]=Math.round(n[1]),t[2]=Math.round(n[2]),t[3]=Math.round(n[3]),t},n.scale=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t[3]=n[3]*r,t},n.scaleAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t[3]=n[3]+r[3]*a,t},n.distance=s,n.squaredDistance=c,n.length=f,n.squaredLength=M,n.negate=function(t,n){return t[0]=-n[0],t[1]=-n[1],t[2]=-n[2],t[3]=-n[3],t},n.inverse=function(t,n){return t[0]=1/n[0],t[1]=1/n[1],t[2]=1/n[2],t[3]=1/n[3],t},n.normalize=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r*r+a*a+e*e+u*u;o>0&&(o=1/Math.sqrt(o),t[0]=r*o,t[1]=a*o,t[2]=e*o,t[3]=u*o);return t},n.dot=function(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]+t[3]*n[3]},n.lerp=function(t,n,r,a){var e=n[0],u=n[1],o=n[2],i=n[3];return t[0]=e+a*(r[0]-e),t[1]=u+a*(r[1]-u),t[2]=o+a*(r[2]-o),t[3]=i+a*(r[3]-i),t},n.random=function(t,n){var r,e,u,o,i,s;n=n||1;do{r=2*a.RANDOM()-1,e=2*a.RANDOM()-1,i=r*r+e*e}while(i>=1);do{u=2*a.RANDOM()-1,o=2*a.RANDOM()-1,s=u*u+o*o}while(s>=1);var c=Math.sqrt((1-i)/s);return t[0]=n*r,t[1]=n*e,t[2]=n*u*c,t[3]=n*o*c,t},n.transformMat4=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3];return t[0]=r[0]*a+r[4]*e+r[8]*u+r[12]*o,t[1]=r[1]*a+r[5]*e+r[9]*u+r[13]*o,t[2]=r[2]*a+r[6]*e+r[10]*u+r[14]*o,t[3]=r[3]*a+r[7]*e+r[11]*u+r[15]*o,t},n.transformQuat=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=r[0],i=r[1],s=r[2],c=r[3],f=c*a+i*u-s*e,M=c*e+s*a-o*u,h=c*u+o*e-i*a,l=-o*a-i*e-s*u;return t[0]=f*c+l*-o+M*-s-h*-i,t[1]=M*c+l*-i+h*-o-f*-s,t[2]=h*c+l*-s+f*-i-M*-o,t[3]=n[3],t},n.str=function(t){return"vec4("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]},n.equals=function(t,n){var r=t[0],e=t[1],u=t[2],o=t[3],i=n[0],s=n[1],c=n[2],f=n[3];return Math.abs(r-i)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(i))&&Math.abs(e-s)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(s))&&Math.abs(u-c)<=a.EPSILON*Math.max(1,Math.abs(u),Math.abs(c))&&Math.abs(o-f)<=a.EPSILON*Math.max(1,Math.abs(o),Math.abs(f))};var a=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(r(0));function e(){var t=new a.ARRAY_TYPE(4);return a.ARRAY_TYPE!=Float32Array&&(t[0]=0,t[1]=0,t[2]=0,t[3]=0),t}function u(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t[3]=n[3]-r[3],t}function o(t,n,r){return t[0]=n[0]*r[0],t[1]=n[1]*r[1],t[2]=n[2]*r[2],t[3]=n[3]*r[3],t}function i(t,n,r){return t[0]=n[0]/r[0],t[1]=n[1]/r[1],t[2]=n[2]/r[2],t[3]=n[3]/r[3],t}function s(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2],u=n[3]-t[3];return Math.sqrt(r*r+a*a+e*e+u*u)}function c(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2],u=n[3]-t[3];return r*r+a*a+e*e+u*u}function f(t){var n=t[0],r=t[1],a=t[2],e=t[3];return Math.sqrt(n*n+r*r+a*a+e*e)}function M(t){var n=t[0],r=t[1],a=t[2],e=t[3];return n*n+r*r+a*a+e*e}n.sub=u,n.mul=o,n.div=i,n.dist=s,n.sqrDist=c,n.len=f,n.sqrLen=M,n.forEach=function(){var t=e();return function(n,r,a,e,u,o){var i=void 0,s=void 0;for(r||(r=4),a||(a=0),s=e?Math.min(e*r+a,n.length):n.length,i=a;i1?0:e<-1?Math.PI:Math.acos(e)},n.str=function(t){return"vec3("+t[0]+", "+t[1]+", "+t[2]+")"},n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]},n.equals=function(t,n){var r=t[0],e=t[1],u=t[2],o=n[0],i=n[1],s=n[2];return Math.abs(r-o)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(o))&&Math.abs(e-i)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(i))&&Math.abs(u-s)<=a.EPSILON*Math.max(1,Math.abs(u),Math.abs(s))};var a=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(r(0));function e(){var t=new a.ARRAY_TYPE(3);return a.ARRAY_TYPE!=Float32Array&&(t[0]=0,t[1]=0,t[2]=0),t}function u(t){var n=t[0],r=t[1],a=t[2];return Math.sqrt(n*n+r*r+a*a)}function o(t,n,r){var e=new a.ARRAY_TYPE(3);return e[0]=t,e[1]=n,e[2]=r,e}function i(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t}function s(t,n,r){return t[0]=n[0]*r[0],t[1]=n[1]*r[1],t[2]=n[2]*r[2],t}function c(t,n,r){return t[0]=n[0]/r[0],t[1]=n[1]/r[1],t[2]=n[2]/r[2],t}function f(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2];return Math.sqrt(r*r+a*a+e*e)}function M(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2];return r*r+a*a+e*e}function h(t){var n=t[0],r=t[1],a=t[2];return n*n+r*r+a*a}function l(t,n){var r=n[0],a=n[1],e=n[2],u=r*r+a*a+e*e;return u>0&&(u=1/Math.sqrt(u),t[0]=n[0]*u,t[1]=n[1]*u,t[2]=n[2]*u),t}function v(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]}n.sub=i,n.mul=s,n.div=c,n.dist=f,n.sqrDist=M,n.len=u,n.sqrLen=h,n.forEach=function(){var t=e();return function(n,r,a,e,u,o){var i=void 0,s=void 0;for(r||(r=3),a||(a=0),s=e?Math.min(e*r+a,n.length):n.length,i=a;ia.EPSILON?(t[0]=n[0]/e,t[1]=n[1]/e,t[2]=n[2]/e):(t[0]=1,t[1]=0,t[2]=0);return r},n.multiply=f,n.rotateX=function(t,n,r){r*=.5;var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),s=Math.cos(r);return t[0]=a*s+o*i,t[1]=e*s+u*i,t[2]=u*s-e*i,t[3]=o*s-a*i,t},n.rotateY=function(t,n,r){r*=.5;var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),s=Math.cos(r);return t[0]=a*s-u*i,t[1]=e*s+o*i,t[2]=u*s+a*i,t[3]=o*s-e*i,t},n.rotateZ=function(t,n,r){r*=.5;var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),s=Math.cos(r);return t[0]=a*s+e*i,t[1]=e*s-a*i,t[2]=u*s+o*i,t[3]=o*s-u*i,t},n.calculateW=function(t,n){var r=n[0],a=n[1],e=n[2];return t[0]=r,t[1]=a,t[2]=e,t[3]=Math.sqrt(Math.abs(1-r*r-a*a-e*e)),t},n.slerp=M,n.random=function(t){var n=a.RANDOM(),r=a.RANDOM(),e=a.RANDOM(),u=Math.sqrt(1-n),o=Math.sqrt(n);return t[0]=u*Math.sin(2*Math.PI*r),t[1]=u*Math.cos(2*Math.PI*r),t[2]=o*Math.sin(2*Math.PI*e),t[3]=o*Math.cos(2*Math.PI*e),t},n.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r*r+a*a+e*e+u*u,i=o?1/o:0;return t[0]=-r*i,t[1]=-a*i,t[2]=-e*i,t[3]=u*i,t},n.conjugate=function(t,n){return t[0]=-n[0],t[1]=-n[1],t[2]=-n[2],t[3]=n[3],t},n.fromMat3=h,n.fromEuler=function(t,n,r,a){var e=.5*Math.PI/180;n*=e,r*=e,a*=e;var u=Math.sin(n),o=Math.cos(n),i=Math.sin(r),s=Math.cos(r),c=Math.sin(a),f=Math.cos(a);return t[0]=u*s*f-o*i*c,t[1]=o*i*f+u*s*c,t[2]=o*s*c-u*i*f,t[3]=o*s*f+u*i*c,t},n.str=function(t){return"quat("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"};var a=i(r(0)),e=i(r(5)),u=i(r(2)),o=i(r(1));function i(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}function s(){var t=new a.ARRAY_TYPE(4);return a.ARRAY_TYPE!=Float32Array&&(t[0]=0,t[1]=0,t[2]=0),t[3]=1,t}function c(t,n,r){r*=.5;var a=Math.sin(r);return t[0]=a*n[0],t[1]=a*n[1],t[2]=a*n[2],t[3]=Math.cos(r),t}function f(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=r[0],s=r[1],c=r[2],f=r[3];return t[0]=a*f+o*i+e*c-u*s,t[1]=e*f+o*s+u*i-a*c,t[2]=u*f+o*c+a*s-e*i,t[3]=o*f-a*i-e*s-u*c,t}function M(t,n,r,e){var u=n[0],o=n[1],i=n[2],s=n[3],c=r[0],f=r[1],M=r[2],h=r[3],l=void 0,v=void 0,d=void 0,b=void 0,m=void 0;return(v=u*c+o*f+i*M+s*h)<0&&(v=-v,c=-c,f=-f,M=-M,h=-h),1-v>a.EPSILON?(l=Math.acos(v),d=Math.sin(l),b=Math.sin((1-e)*l)/d,m=Math.sin(e*l)/d):(b=1-e,m=e),t[0]=b*u+m*c,t[1]=b*o+m*f,t[2]=b*i+m*M,t[3]=b*s+m*h,t}function h(t,n){var r=n[0]+n[4]+n[8],a=void 0;if(r>0)a=Math.sqrt(r+1),t[3]=.5*a,a=.5/a,t[0]=(n[5]-n[7])*a,t[1]=(n[6]-n[2])*a,t[2]=(n[1]-n[3])*a;else{var e=0;n[4]>n[0]&&(e=1),n[8]>n[3*e+e]&&(e=2);var u=(e+1)%3,o=(e+2)%3;a=Math.sqrt(n[3*e+e]-n[3*u+u]-n[3*o+o]+1),t[e]=.5*a,a=.5/a,t[3]=(n[3*u+o]-n[3*o+u])*a,t[u]=(n[3*u+e]+n[3*e+u])*a,t[o]=(n[3*o+e]+n[3*e+o])*a}return t}n.clone=o.clone,n.fromValues=o.fromValues,n.copy=o.copy,n.set=o.set,n.add=o.add,n.mul=f,n.scale=o.scale,n.dot=o.dot,n.lerp=o.lerp;var l=n.length=o.length,v=(n.len=l,n.squaredLength=o.squaredLength),d=(n.sqrLen=v,n.normalize=o.normalize);n.exactEquals=o.exactEquals,n.equals=o.equals,n.rotationTo=function(){var t=u.create(),n=u.fromValues(1,0,0),r=u.fromValues(0,1,0);return function(a,e,o){var i=u.dot(e,o);return i<-.999999?(u.cross(t,n,e),u.len(t)<1e-6&&u.cross(t,r,e),u.normalize(t,t),c(a,t,Math.PI),a):i>.999999?(a[0]=0,a[1]=0,a[2]=0,a[3]=1,a):(u.cross(t,e,o),a[0]=t[0],a[1]=t[1],a[2]=t[2],a[3]=1+i,d(a,a))}}(),n.sqlerp=function(){var t=s(),n=s();return function(r,a,e,u,o,i){return M(t,a,o,i),M(n,e,u,i),M(r,t,n,2*i*(1-i)),r}}(),n.setAxes=function(){var t=e.create();return function(n,r,a,e){return t[0]=a[0],t[3]=a[1],t[6]=a[2],t[1]=e[0],t[4]=e[1],t[7]=e[2],t[2]=-r[0],t[5]=-r[1],t[8]=-r[2],d(n,h(n,t))}}()},function(t,n,r){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.sub=n.mul=void 0,n.create=function(){var t=new a.ARRAY_TYPE(16);a.ARRAY_TYPE!=Float32Array&&(t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[11]=0,t[12]=0,t[13]=0,t[14]=0);return t[0]=1,t[5]=1,t[10]=1,t[15]=1,t},n.clone=function(t){var n=new a.ARRAY_TYPE(16);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n[6]=t[6],n[7]=t[7],n[8]=t[8],n[9]=t[9],n[10]=t[10],n[11]=t[11],n[12]=t[12],n[13]=t[13],n[14]=t[14],n[15]=t[15],n},n.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t[9]=n[9],t[10]=n[10],t[11]=n[11],t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15],t},n.fromValues=function(t,n,r,e,u,o,i,s,c,f,M,h,l,v,d,b){var m=new a.ARRAY_TYPE(16);return m[0]=t,m[1]=n,m[2]=r,m[3]=e,m[4]=u,m[5]=o,m[6]=i,m[7]=s,m[8]=c,m[9]=f,m[10]=M,m[11]=h,m[12]=l,m[13]=v,m[14]=d,m[15]=b,m},n.set=function(t,n,r,a,e,u,o,i,s,c,f,M,h,l,v,d,b){return t[0]=n,t[1]=r,t[2]=a,t[3]=e,t[4]=u,t[5]=o,t[6]=i,t[7]=s,t[8]=c,t[9]=f,t[10]=M,t[11]=h,t[12]=l,t[13]=v,t[14]=d,t[15]=b,t},n.identity=e,n.transpose=function(t,n){if(t===n){var r=n[1],a=n[2],e=n[3],u=n[6],o=n[7],i=n[11];t[1]=n[4],t[2]=n[8],t[3]=n[12],t[4]=r,t[6]=n[9],t[7]=n[13],t[8]=a,t[9]=u,t[11]=n[14],t[12]=e,t[13]=o,t[14]=i}else t[0]=n[0],t[1]=n[4],t[2]=n[8],t[3]=n[12],t[4]=n[1],t[5]=n[5],t[6]=n[9],t[7]=n[13],t[8]=n[2],t[9]=n[6],t[10]=n[10],t[11]=n[14],t[12]=n[3],t[13]=n[7],t[14]=n[11],t[15]=n[15];return t},n.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],s=n[6],c=n[7],f=n[8],M=n[9],h=n[10],l=n[11],v=n[12],d=n[13],b=n[14],m=n[15],p=r*i-a*o,P=r*s-e*o,A=r*c-u*o,E=a*s-e*i,O=a*c-u*i,R=e*c-u*s,y=f*d-M*v,q=f*b-h*v,x=f*m-l*v,_=M*b-h*d,Y=M*m-l*d,L=h*m-l*b,S=p*L-P*Y+A*_+E*x-O*q+R*y;if(!S)return null;return S=1/S,t[0]=(i*L-s*Y+c*_)*S,t[1]=(e*Y-a*L-u*_)*S,t[2]=(d*R-b*O+m*E)*S,t[3]=(h*O-M*R-l*E)*S,t[4]=(s*x-o*L-c*q)*S,t[5]=(r*L-e*x+u*q)*S,t[6]=(b*A-v*R-m*P)*S,t[7]=(f*R-h*A+l*P)*S,t[8]=(o*Y-i*x+c*y)*S,t[9]=(a*x-r*Y-u*y)*S,t[10]=(v*O-d*A+m*p)*S,t[11]=(M*A-f*O-l*p)*S,t[12]=(i*q-o*_-s*y)*S,t[13]=(r*_-a*q+e*y)*S,t[14]=(d*P-v*E-b*p)*S,t[15]=(f*E-M*P+h*p)*S,t},n.adjoint=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],s=n[6],c=n[7],f=n[8],M=n[9],h=n[10],l=n[11],v=n[12],d=n[13],b=n[14],m=n[15];return t[0]=i*(h*m-l*b)-M*(s*m-c*b)+d*(s*l-c*h),t[1]=-(a*(h*m-l*b)-M*(e*m-u*b)+d*(e*l-u*h)),t[2]=a*(s*m-c*b)-i*(e*m-u*b)+d*(e*c-u*s),t[3]=-(a*(s*l-c*h)-i*(e*l-u*h)+M*(e*c-u*s)),t[4]=-(o*(h*m-l*b)-f*(s*m-c*b)+v*(s*l-c*h)),t[5]=r*(h*m-l*b)-f*(e*m-u*b)+v*(e*l-u*h),t[6]=-(r*(s*m-c*b)-o*(e*m-u*b)+v*(e*c-u*s)),t[7]=r*(s*l-c*h)-o*(e*l-u*h)+f*(e*c-u*s),t[8]=o*(M*m-l*d)-f*(i*m-c*d)+v*(i*l-c*M),t[9]=-(r*(M*m-l*d)-f*(a*m-u*d)+v*(a*l-u*M)),t[10]=r*(i*m-c*d)-o*(a*m-u*d)+v*(a*c-u*i),t[11]=-(r*(i*l-c*M)-o*(a*l-u*M)+f*(a*c-u*i)),t[12]=-(o*(M*b-h*d)-f*(i*b-s*d)+v*(i*h-s*M)),t[13]=r*(M*b-h*d)-f*(a*b-e*d)+v*(a*h-e*M),t[14]=-(r*(i*b-s*d)-o*(a*b-e*d)+v*(a*s-e*i)),t[15]=r*(i*h-s*M)-o*(a*h-e*M)+f*(a*s-e*i),t},n.determinant=function(t){var n=t[0],r=t[1],a=t[2],e=t[3],u=t[4],o=t[5],i=t[6],s=t[7],c=t[8],f=t[9],M=t[10],h=t[11],l=t[12],v=t[13],d=t[14],b=t[15];return(n*o-r*u)*(M*b-h*d)-(n*i-a*u)*(f*b-h*v)+(n*s-e*u)*(f*d-M*v)+(r*i-a*o)*(c*b-h*l)-(r*s-e*o)*(c*d-M*l)+(a*s-e*i)*(c*v-f*l)},n.multiply=u,n.translate=function(t,n,r){var a=r[0],e=r[1],u=r[2],o=void 0,i=void 0,s=void 0,c=void 0,f=void 0,M=void 0,h=void 0,l=void 0,v=void 0,d=void 0,b=void 0,m=void 0;n===t?(t[12]=n[0]*a+n[4]*e+n[8]*u+n[12],t[13]=n[1]*a+n[5]*e+n[9]*u+n[13],t[14]=n[2]*a+n[6]*e+n[10]*u+n[14],t[15]=n[3]*a+n[7]*e+n[11]*u+n[15]):(o=n[0],i=n[1],s=n[2],c=n[3],f=n[4],M=n[5],h=n[6],l=n[7],v=n[8],d=n[9],b=n[10],m=n[11],t[0]=o,t[1]=i,t[2]=s,t[3]=c,t[4]=f,t[5]=M,t[6]=h,t[7]=l,t[8]=v,t[9]=d,t[10]=b,t[11]=m,t[12]=o*a+f*e+v*u+n[12],t[13]=i*a+M*e+d*u+n[13],t[14]=s*a+h*e+b*u+n[14],t[15]=c*a+l*e+m*u+n[15]);return t},n.scale=function(t,n,r){var a=r[0],e=r[1],u=r[2];return t[0]=n[0]*a,t[1]=n[1]*a,t[2]=n[2]*a,t[3]=n[3]*a,t[4]=n[4]*e,t[5]=n[5]*e,t[6]=n[6]*e,t[7]=n[7]*e,t[8]=n[8]*u,t[9]=n[9]*u,t[10]=n[10]*u,t[11]=n[11]*u,t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15],t},n.rotate=function(t,n,r,e){var u=e[0],o=e[1],i=e[2],s=Math.sqrt(u*u+o*o+i*i),c=void 0,f=void 0,M=void 0,h=void 0,l=void 0,v=void 0,d=void 0,b=void 0,m=void 0,p=void 0,P=void 0,A=void 0,E=void 0,O=void 0,R=void 0,y=void 0,q=void 0,x=void 0,_=void 0,Y=void 0,L=void 0,S=void 0,w=void 0,I=void 0;if(s0?(r[0]=2*(c*s+h*e+f*i-M*u)/l,r[1]=2*(f*s+h*u+M*e-c*i)/l,r[2]=2*(M*s+h*i+c*u-f*e)/l):(r[0]=2*(c*s+h*e+f*i-M*u),r[1]=2*(f*s+h*u+M*e-c*i),r[2]=2*(M*s+h*i+c*u-f*e));return o(t,n,r),t},n.getTranslation=function(t,n){return t[0]=n[12],t[1]=n[13],t[2]=n[14],t},n.getScaling=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[4],o=n[5],i=n[6],s=n[8],c=n[9],f=n[10];return t[0]=Math.sqrt(r*r+a*a+e*e),t[1]=Math.sqrt(u*u+o*o+i*i),t[2]=Math.sqrt(s*s+c*c+f*f),t},n.getRotation=function(t,n){var r=n[0]+n[5]+n[10],a=0;r>0?(a=2*Math.sqrt(r+1),t[3]=.25*a,t[0]=(n[6]-n[9])/a,t[1]=(n[8]-n[2])/a,t[2]=(n[1]-n[4])/a):n[0]>n[5]&&n[0]>n[10]?(a=2*Math.sqrt(1+n[0]-n[5]-n[10]),t[3]=(n[6]-n[9])/a,t[0]=.25*a,t[1]=(n[1]+n[4])/a,t[2]=(n[8]+n[2])/a):n[5]>n[10]?(a=2*Math.sqrt(1+n[5]-n[0]-n[10]),t[3]=(n[8]-n[2])/a,t[0]=(n[1]+n[4])/a,t[1]=.25*a,t[2]=(n[6]+n[9])/a):(a=2*Math.sqrt(1+n[10]-n[0]-n[5]),t[3]=(n[1]-n[4])/a,t[0]=(n[8]+n[2])/a,t[1]=(n[6]+n[9])/a,t[2]=.25*a);return t},n.fromRotationTranslationScale=function(t,n,r,a){var e=n[0],u=n[1],o=n[2],i=n[3],s=e+e,c=u+u,f=o+o,M=e*s,h=e*c,l=e*f,v=u*c,d=u*f,b=o*f,m=i*s,p=i*c,P=i*f,A=a[0],E=a[1],O=a[2];return t[0]=(1-(v+b))*A,t[1]=(h+P)*A,t[2]=(l-p)*A,t[3]=0,t[4]=(h-P)*E,t[5]=(1-(M+b))*E,t[6]=(d+m)*E,t[7]=0,t[8]=(l+p)*O,t[9]=(d-m)*O,t[10]=(1-(M+v))*O,t[11]=0,t[12]=r[0],t[13]=r[1],t[14]=r[2],t[15]=1,t},n.fromRotationTranslationScaleOrigin=function(t,n,r,a,e){var u=n[0],o=n[1],i=n[2],s=n[3],c=u+u,f=o+o,M=i+i,h=u*c,l=u*f,v=u*M,d=o*f,b=o*M,m=i*M,p=s*c,P=s*f,A=s*M,E=a[0],O=a[1],R=a[2],y=e[0],q=e[1],x=e[2],_=(1-(d+m))*E,Y=(l+A)*E,L=(v-P)*E,S=(l-A)*O,w=(1-(h+m))*O,I=(b+p)*O,N=(v+P)*R,g=(b-p)*R,T=(1-(h+d))*R;return t[0]=_,t[1]=Y,t[2]=L,t[3]=0,t[4]=S,t[5]=w,t[6]=I,t[7]=0,t[8]=N,t[9]=g,t[10]=T,t[11]=0,t[12]=r[0]+y-(_*y+S*q+N*x),t[13]=r[1]+q-(Y*y+w*q+g*x),t[14]=r[2]+x-(L*y+I*q+T*x),t[15]=1,t},n.fromQuat=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r+r,i=a+a,s=e+e,c=r*o,f=a*o,M=a*i,h=e*o,l=e*i,v=e*s,d=u*o,b=u*i,m=u*s;return t[0]=1-M-v,t[1]=f+m,t[2]=h-b,t[3]=0,t[4]=f-m,t[5]=1-c-v,t[6]=l+d,t[7]=0,t[8]=h+b,t[9]=l-d,t[10]=1-c-M,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},n.frustum=function(t,n,r,a,e,u,o){var i=1/(r-n),s=1/(e-a),c=1/(u-o);return t[0]=2*u*i,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=2*u*s,t[6]=0,t[7]=0,t[8]=(r+n)*i,t[9]=(e+a)*s,t[10]=(o+u)*c,t[11]=-1,t[12]=0,t[13]=0,t[14]=o*u*2*c,t[15]=0,t},n.perspective=function(t,n,r,a,e){var u=1/Math.tan(n/2),o=void 0;t[0]=u/r,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=u,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[11]=-1,t[12]=0,t[13]=0,t[15]=0,null!=e&&e!==1/0?(o=1/(a-e),t[10]=(e+a)*o,t[14]=2*e*a*o):(t[10]=-1,t[14]=-2*a);return t},n.perspectiveFromFieldOfView=function(t,n,r,a){var e=Math.tan(n.upDegrees*Math.PI/180),u=Math.tan(n.downDegrees*Math.PI/180),o=Math.tan(n.leftDegrees*Math.PI/180),i=Math.tan(n.rightDegrees*Math.PI/180),s=2/(o+i),c=2/(e+u);return t[0]=s,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=c,t[6]=0,t[7]=0,t[8]=-(o-i)*s*.5,t[9]=(e-u)*c*.5,t[10]=a/(r-a),t[11]=-1,t[12]=0,t[13]=0,t[14]=a*r/(r-a),t[15]=0,t},n.ortho=function(t,n,r,a,e,u,o){var i=1/(n-r),s=1/(a-e),c=1/(u-o);return t[0]=-2*i,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=-2*s,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=2*c,t[11]=0,t[12]=(n+r)*i,t[13]=(e+a)*s,t[14]=(o+u)*c,t[15]=1,t},n.lookAt=function(t,n,r,u){var o=void 0,i=void 0,s=void 0,c=void 0,f=void 0,M=void 0,h=void 0,l=void 0,v=void 0,d=void 0,b=n[0],m=n[1],p=n[2],P=u[0],A=u[1],E=u[2],O=r[0],R=r[1],y=r[2];if(Math.abs(b-O)0&&(l=1/Math.sqrt(l),f*=l,M*=l,h*=l);var v=s*h-c*M,d=c*f-i*h,b=i*M-s*f;(l=v*v+d*d+b*b)>0&&(l=1/Math.sqrt(l),v*=l,d*=l,b*=l);return t[0]=v,t[1]=d,t[2]=b,t[3]=0,t[4]=M*b-h*d,t[5]=h*v-f*b,t[6]=f*d-M*v,t[7]=0,t[8]=f,t[9]=M,t[10]=h,t[11]=0,t[12]=e,t[13]=u,t[14]=o,t[15]=1,t},n.str=function(t){return"mat4("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+", "+t[6]+", "+t[7]+", "+t[8]+", "+t[9]+", "+t[10]+", "+t[11]+", "+t[12]+", "+t[13]+", "+t[14]+", "+t[15]+")"},n.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+Math.pow(t[6],2)+Math.pow(t[7],2)+Math.pow(t[8],2)+Math.pow(t[9],2)+Math.pow(t[10],2)+Math.pow(t[11],2)+Math.pow(t[12],2)+Math.pow(t[13],2)+Math.pow(t[14],2)+Math.pow(t[15],2))},n.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t[3]=n[3]+r[3],t[4]=n[4]+r[4],t[5]=n[5]+r[5],t[6]=n[6]+r[6],t[7]=n[7]+r[7],t[8]=n[8]+r[8],t[9]=n[9]+r[9],t[10]=n[10]+r[10],t[11]=n[11]+r[11],t[12]=n[12]+r[12],t[13]=n[13]+r[13],t[14]=n[14]+r[14],t[15]=n[15]+r[15],t},n.subtract=i,n.multiplyScalar=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t[3]=n[3]*r,t[4]=n[4]*r,t[5]=n[5]*r,t[6]=n[6]*r,t[7]=n[7]*r,t[8]=n[8]*r,t[9]=n[9]*r,t[10]=n[10]*r,t[11]=n[11]*r,t[12]=n[12]*r,t[13]=n[13]*r,t[14]=n[14]*r,t[15]=n[15]*r,t},n.multiplyScalarAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t[3]=n[3]+r[3]*a,t[4]=n[4]+r[4]*a,t[5]=n[5]+r[5]*a,t[6]=n[6]+r[6]*a,t[7]=n[7]+r[7]*a,t[8]=n[8]+r[8]*a,t[9]=n[9]+r[9]*a,t[10]=n[10]+r[10]*a,t[11]=n[11]+r[11]*a,t[12]=n[12]+r[12]*a,t[13]=n[13]+r[13]*a,t[14]=n[14]+r[14]*a,t[15]=n[15]+r[15]*a,t},n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]&&t[4]===n[4]&&t[5]===n[5]&&t[6]===n[6]&&t[7]===n[7]&&t[8]===n[8]&&t[9]===n[9]&&t[10]===n[10]&&t[11]===n[11]&&t[12]===n[12]&&t[13]===n[13]&&t[14]===n[14]&&t[15]===n[15]},n.equals=function(t,n){var r=t[0],e=t[1],u=t[2],o=t[3],i=t[4],s=t[5],c=t[6],f=t[7],M=t[8],h=t[9],l=t[10],v=t[11],d=t[12],b=t[13],m=t[14],p=t[15],P=n[0],A=n[1],E=n[2],O=n[3],R=n[4],y=n[5],q=n[6],x=n[7],_=n[8],Y=n[9],L=n[10],S=n[11],w=n[12],I=n[13],N=n[14],g=n[15];return Math.abs(r-P)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(P))&&Math.abs(e-A)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(A))&&Math.abs(u-E)<=a.EPSILON*Math.max(1,Math.abs(u),Math.abs(E))&&Math.abs(o-O)<=a.EPSILON*Math.max(1,Math.abs(o),Math.abs(O))&&Math.abs(i-R)<=a.EPSILON*Math.max(1,Math.abs(i),Math.abs(R))&&Math.abs(s-y)<=a.EPSILON*Math.max(1,Math.abs(s),Math.abs(y))&&Math.abs(c-q)<=a.EPSILON*Math.max(1,Math.abs(c),Math.abs(q))&&Math.abs(f-x)<=a.EPSILON*Math.max(1,Math.abs(f),Math.abs(x))&&Math.abs(M-_)<=a.EPSILON*Math.max(1,Math.abs(M),Math.abs(_))&&Math.abs(h-Y)<=a.EPSILON*Math.max(1,Math.abs(h),Math.abs(Y))&&Math.abs(l-L)<=a.EPSILON*Math.max(1,Math.abs(l),Math.abs(L))&&Math.abs(v-S)<=a.EPSILON*Math.max(1,Math.abs(v),Math.abs(S))&&Math.abs(d-w)<=a.EPSILON*Math.max(1,Math.abs(d),Math.abs(w))&&Math.abs(b-I)<=a.EPSILON*Math.max(1,Math.abs(b),Math.abs(I))&&Math.abs(m-N)<=a.EPSILON*Math.max(1,Math.abs(m),Math.abs(N))&&Math.abs(p-g)<=a.EPSILON*Math.max(1,Math.abs(p),Math.abs(g))};var a=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(r(0));function e(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t}function u(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=n[6],f=n[7],M=n[8],h=n[9],l=n[10],v=n[11],d=n[12],b=n[13],m=n[14],p=n[15],P=r[0],A=r[1],E=r[2],O=r[3];return t[0]=P*a+A*i+E*M+O*d,t[1]=P*e+A*s+E*h+O*b,t[2]=P*u+A*c+E*l+O*m,t[3]=P*o+A*f+E*v+O*p,P=r[4],A=r[5],E=r[6],O=r[7],t[4]=P*a+A*i+E*M+O*d,t[5]=P*e+A*s+E*h+O*b,t[6]=P*u+A*c+E*l+O*m,t[7]=P*o+A*f+E*v+O*p,P=r[8],A=r[9],E=r[10],O=r[11],t[8]=P*a+A*i+E*M+O*d,t[9]=P*e+A*s+E*h+O*b,t[10]=P*u+A*c+E*l+O*m,t[11]=P*o+A*f+E*v+O*p,P=r[12],A=r[13],E=r[14],O=r[15],t[12]=P*a+A*i+E*M+O*d,t[13]=P*e+A*s+E*h+O*b,t[14]=P*u+A*c+E*l+O*m,t[15]=P*o+A*f+E*v+O*p,t}function o(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=a+a,s=e+e,c=u+u,f=a*i,M=a*s,h=a*c,l=e*s,v=e*c,d=u*c,b=o*i,m=o*s,p=o*c;return t[0]=1-(l+d),t[1]=M+p,t[2]=h-m,t[3]=0,t[4]=M-p,t[5]=1-(f+d),t[6]=v+b,t[7]=0,t[8]=h+m,t[9]=v-b,t[10]=1-(f+l),t[11]=0,t[12]=r[0],t[13]=r[1],t[14]=r[2],t[15]=1,t}function i(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t[3]=n[3]-r[3],t[4]=n[4]-r[4],t[5]=n[5]-r[5],t[6]=n[6]-r[6],t[7]=n[7]-r[7],t[8]=n[8]-r[8],t[9]=n[9]-r[9],t[10]=n[10]-r[10],t[11]=n[11]-r[11],t[12]=n[12]-r[12],t[13]=n[13]-r[13],t[14]=n[14]-r[14],t[15]=n[15]-r[15],t}n.mul=u,n.sub=i},function(t,n,r){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.sub=n.mul=void 0,n.create=function(){var t=new a.ARRAY_TYPE(9);a.ARRAY_TYPE!=Float32Array&&(t[1]=0,t[2]=0,t[3]=0,t[5]=0,t[6]=0,t[7]=0);return t[0]=1,t[4]=1,t[8]=1,t},n.fromMat4=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[4],t[4]=n[5],t[5]=n[6],t[6]=n[8],t[7]=n[9],t[8]=n[10],t},n.clone=function(t){var n=new a.ARRAY_TYPE(9);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n[6]=t[6],n[7]=t[7],n[8]=t[8],n},n.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t},n.fromValues=function(t,n,r,e,u,o,i,s,c){var f=new a.ARRAY_TYPE(9);return f[0]=t,f[1]=n,f[2]=r,f[3]=e,f[4]=u,f[5]=o,f[6]=i,f[7]=s,f[8]=c,f},n.set=function(t,n,r,a,e,u,o,i,s,c){return t[0]=n,t[1]=r,t[2]=a,t[3]=e,t[4]=u,t[5]=o,t[6]=i,t[7]=s,t[8]=c,t},n.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},n.transpose=function(t,n){if(t===n){var r=n[1],a=n[2],e=n[5];t[1]=n[3],t[2]=n[6],t[3]=r,t[5]=n[7],t[6]=a,t[7]=e}else t[0]=n[0],t[1]=n[3],t[2]=n[6],t[3]=n[1],t[4]=n[4],t[5]=n[7],t[6]=n[2],t[7]=n[5],t[8]=n[8];return t},n.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],s=n[6],c=n[7],f=n[8],M=f*o-i*c,h=-f*u+i*s,l=c*u-o*s,v=r*M+a*h+e*l;if(!v)return null;return v=1/v,t[0]=M*v,t[1]=(-f*a+e*c)*v,t[2]=(i*a-e*o)*v,t[3]=h*v,t[4]=(f*r-e*s)*v,t[5]=(-i*r+e*u)*v,t[6]=l*v,t[7]=(-c*r+a*s)*v,t[8]=(o*r-a*u)*v,t},n.adjoint=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],s=n[6],c=n[7],f=n[8];return t[0]=o*f-i*c,t[1]=e*c-a*f,t[2]=a*i-e*o,t[3]=i*s-u*f,t[4]=r*f-e*s,t[5]=e*u-r*i,t[6]=u*c-o*s,t[7]=a*s-r*c,t[8]=r*o-a*u,t},n.determinant=function(t){var n=t[0],r=t[1],a=t[2],e=t[3],u=t[4],o=t[5],i=t[6],s=t[7],c=t[8];return n*(c*u-o*s)+r*(-c*e+o*i)+a*(s*e-u*i)},n.multiply=e,n.translate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=n[6],f=n[7],M=n[8],h=r[0],l=r[1];return t[0]=a,t[1]=e,t[2]=u,t[3]=o,t[4]=i,t[5]=s,t[6]=h*a+l*o+c,t[7]=h*e+l*i+f,t[8]=h*u+l*s+M,t},n.rotate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=n[6],f=n[7],M=n[8],h=Math.sin(r),l=Math.cos(r);return t[0]=l*a+h*o,t[1]=l*e+h*i,t[2]=l*u+h*s,t[3]=l*o-h*a,t[4]=l*i-h*e,t[5]=l*s-h*u,t[6]=c,t[7]=f,t[8]=M,t},n.scale=function(t,n,r){var a=r[0],e=r[1];return t[0]=a*n[0],t[1]=a*n[1],t[2]=a*n[2],t[3]=e*n[3],t[4]=e*n[4],t[5]=e*n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t},n.fromTranslation=function(t,n){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=n[0],t[7]=n[1],t[8]=1,t},n.fromRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=r,t[2]=0,t[3]=-r,t[4]=a,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},n.fromScaling=function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=0,t[4]=n[1],t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},n.fromMat2d=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=0,t[3]=n[2],t[4]=n[3],t[5]=0,t[6]=n[4],t[7]=n[5],t[8]=1,t},n.fromQuat=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r+r,i=a+a,s=e+e,c=r*o,f=a*o,M=a*i,h=e*o,l=e*i,v=e*s,d=u*o,b=u*i,m=u*s;return t[0]=1-M-v,t[3]=f-m,t[6]=h+b,t[1]=f+m,t[4]=1-c-v,t[7]=l-d,t[2]=h-b,t[5]=l+d,t[8]=1-c-M,t},n.normalFromMat4=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],s=n[6],c=n[7],f=n[8],M=n[9],h=n[10],l=n[11],v=n[12],d=n[13],b=n[14],m=n[15],p=r*i-a*o,P=r*s-e*o,A=r*c-u*o,E=a*s-e*i,O=a*c-u*i,R=e*c-u*s,y=f*d-M*v,q=f*b-h*v,x=f*m-l*v,_=M*b-h*d,Y=M*m-l*d,L=h*m-l*b,S=p*L-P*Y+A*_+E*x-O*q+R*y;if(!S)return null;return S=1/S,t[0]=(i*L-s*Y+c*_)*S,t[1]=(s*x-o*L-c*q)*S,t[2]=(o*Y-i*x+c*y)*S,t[3]=(e*Y-a*L-u*_)*S,t[4]=(r*L-e*x+u*q)*S,t[5]=(a*x-r*Y-u*y)*S,t[6]=(d*R-b*O+m*E)*S,t[7]=(b*A-v*R-m*P)*S,t[8]=(v*O-d*A+m*p)*S,t},n.projection=function(t,n,r){return t[0]=2/n,t[1]=0,t[2]=0,t[3]=0,t[4]=-2/r,t[5]=0,t[6]=-1,t[7]=1,t[8]=1,t},n.str=function(t){return"mat3("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+", "+t[6]+", "+t[7]+", "+t[8]+")"},n.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+Math.pow(t[6],2)+Math.pow(t[7],2)+Math.pow(t[8],2))},n.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t[3]=n[3]+r[3],t[4]=n[4]+r[4],t[5]=n[5]+r[5],t[6]=n[6]+r[6],t[7]=n[7]+r[7],t[8]=n[8]+r[8],t},n.subtract=u,n.multiplyScalar=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t[3]=n[3]*r,t[4]=n[4]*r,t[5]=n[5]*r,t[6]=n[6]*r,t[7]=n[7]*r,t[8]=n[8]*r,t},n.multiplyScalarAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t[3]=n[3]+r[3]*a,t[4]=n[4]+r[4]*a,t[5]=n[5]+r[5]*a,t[6]=n[6]+r[6]*a,t[7]=n[7]+r[7]*a,t[8]=n[8]+r[8]*a,t},n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]&&t[4]===n[4]&&t[5]===n[5]&&t[6]===n[6]&&t[7]===n[7]&&t[8]===n[8]},n.equals=function(t,n){var r=t[0],e=t[1],u=t[2],o=t[3],i=t[4],s=t[5],c=t[6],f=t[7],M=t[8],h=n[0],l=n[1],v=n[2],d=n[3],b=n[4],m=n[5],p=n[6],P=n[7],A=n[8];return Math.abs(r-h)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(h))&&Math.abs(e-l)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(l))&&Math.abs(u-v)<=a.EPSILON*Math.max(1,Math.abs(u),Math.abs(v))&&Math.abs(o-d)<=a.EPSILON*Math.max(1,Math.abs(o),Math.abs(d))&&Math.abs(i-b)<=a.EPSILON*Math.max(1,Math.abs(i),Math.abs(b))&&Math.abs(s-m)<=a.EPSILON*Math.max(1,Math.abs(s),Math.abs(m))&&Math.abs(c-p)<=a.EPSILON*Math.max(1,Math.abs(c),Math.abs(p))&&Math.abs(f-P)<=a.EPSILON*Math.max(1,Math.abs(f),Math.abs(P))&&Math.abs(M-A)<=a.EPSILON*Math.max(1,Math.abs(M),Math.abs(A))};var a=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(r(0));function e(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=n[6],f=n[7],M=n[8],h=r[0],l=r[1],v=r[2],d=r[3],b=r[4],m=r[5],p=r[6],P=r[7],A=r[8];return t[0]=h*a+l*o+v*c,t[1]=h*e+l*i+v*f,t[2]=h*u+l*s+v*M,t[3]=d*a+b*o+m*c,t[4]=d*e+b*i+m*f,t[5]=d*u+b*s+m*M,t[6]=p*a+P*o+A*c,t[7]=p*e+P*i+A*f,t[8]=p*u+P*s+A*M,t}function u(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t[3]=n[3]-r[3],t[4]=n[4]-r[4],t[5]=n[5]-r[5],t[6]=n[6]-r[6],t[7]=n[7]-r[7],t[8]=n[8]-r[8],t}n.mul=e,n.sub=u},function(t,n,r){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.forEach=n.sqrLen=n.sqrDist=n.dist=n.div=n.mul=n.sub=n.len=void 0,n.create=e,n.clone=function(t){var n=new a.ARRAY_TYPE(2);return n[0]=t[0],n[1]=t[1],n},n.fromValues=function(t,n){var r=new a.ARRAY_TYPE(2);return r[0]=t,r[1]=n,r},n.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t},n.set=function(t,n,r){return t[0]=n,t[1]=r,t},n.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t},n.subtract=u,n.multiply=o,n.divide=i,n.ceil=function(t,n){return t[0]=Math.ceil(n[0]),t[1]=Math.ceil(n[1]),t},n.floor=function(t,n){return t[0]=Math.floor(n[0]),t[1]=Math.floor(n[1]),t},n.min=function(t,n,r){return t[0]=Math.min(n[0],r[0]),t[1]=Math.min(n[1],r[1]),t},n.max=function(t,n,r){return t[0]=Math.max(n[0],r[0]),t[1]=Math.max(n[1],r[1]),t},n.round=function(t,n){return t[0]=Math.round(n[0]),t[1]=Math.round(n[1]),t},n.scale=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t},n.scaleAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t},n.distance=s,n.squaredDistance=c,n.length=f,n.squaredLength=M,n.negate=function(t,n){return t[0]=-n[0],t[1]=-n[1],t},n.inverse=function(t,n){return t[0]=1/n[0],t[1]=1/n[1],t},n.normalize=function(t,n){var r=n[0],a=n[1],e=r*r+a*a;e>0&&(e=1/Math.sqrt(e),t[0]=n[0]*e,t[1]=n[1]*e);return t},n.dot=function(t,n){return t[0]*n[0]+t[1]*n[1]},n.cross=function(t,n,r){var a=n[0]*r[1]-n[1]*r[0];return t[0]=t[1]=0,t[2]=a,t},n.lerp=function(t,n,r,a){var e=n[0],u=n[1];return t[0]=e+a*(r[0]-e),t[1]=u+a*(r[1]-u),t},n.random=function(t,n){n=n||1;var r=2*a.RANDOM()*Math.PI;return t[0]=Math.cos(r)*n,t[1]=Math.sin(r)*n,t},n.transformMat2=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[2]*e,t[1]=r[1]*a+r[3]*e,t},n.transformMat2d=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[2]*e+r[4],t[1]=r[1]*a+r[3]*e+r[5],t},n.transformMat3=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[3]*e+r[6],t[1]=r[1]*a+r[4]*e+r[7],t},n.transformMat4=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[4]*e+r[12],t[1]=r[1]*a+r[5]*e+r[13],t},n.rotate=function(t,n,r,a){var e=n[0]-r[0],u=n[1]-r[1],o=Math.sin(a),i=Math.cos(a);return t[0]=e*i-u*o+r[0],t[1]=e*o+u*i+r[1],t},n.angle=function(t,n){var r=t[0],a=t[1],e=n[0],u=n[1],o=r*r+a*a;o>0&&(o=1/Math.sqrt(o));var i=e*e+u*u;i>0&&(i=1/Math.sqrt(i));var s=(r*e+a*u)*o*i;return s>1?0:s<-1?Math.PI:Math.acos(s)},n.str=function(t){return"vec2("+t[0]+", "+t[1]+")"},n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]},n.equals=function(t,n){var r=t[0],e=t[1],u=n[0],o=n[1];return Math.abs(r-u)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(u))&&Math.abs(e-o)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(o))};var a=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(r(0));function e(){var t=new a.ARRAY_TYPE(2);return a.ARRAY_TYPE!=Float32Array&&(t[0]=0,t[1]=0),t}function u(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t}function o(t,n,r){return t[0]=n[0]*r[0],t[1]=n[1]*r[1],t}function i(t,n,r){return t[0]=n[0]/r[0],t[1]=n[1]/r[1],t}function s(t,n){var r=n[0]-t[0],a=n[1]-t[1];return Math.sqrt(r*r+a*a)}function c(t,n){var r=n[0]-t[0],a=n[1]-t[1];return r*r+a*a}function f(t){var n=t[0],r=t[1];return Math.sqrt(n*n+r*r)}function M(t){var n=t[0],r=t[1];return n*n+r*r}n.len=f,n.sub=u,n.mul=o,n.div=i,n.dist=s,n.sqrDist=c,n.sqrLen=M,n.forEach=function(){var t=e();return function(n,r,a,e,u,o){var i=void 0,s=void 0;for(r||(r=2),a||(a=0),s=e?Math.min(e*r+a,n.length):n.length,i=a;i0){r=Math.sqrt(r);var a=n[0]/r,e=n[1]/r,u=n[2]/r,o=n[3]/r,i=n[4],s=n[5],c=n[6],f=n[7],M=a*i+e*s+u*c+o*f;t[0]=a,t[1]=e,t[2]=u,t[3]=o,t[4]=(i-a*M)/r,t[5]=(s-e*M)/r,t[6]=(c-u*M)/r,t[7]=(f-o*M)/r}return t},n.str=function(t){return"quat2("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+", "+t[6]+", "+t[7]+")"},n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]&&t[4]===n[4]&&t[5]===n[5]&&t[6]===n[6]&&t[7]===n[7]},n.equals=function(t,n){var r=t[0],e=t[1],u=t[2],o=t[3],i=t[4],s=t[5],c=t[6],f=t[7],M=n[0],h=n[1],l=n[2],v=n[3],d=n[4],b=n[5],m=n[6],p=n[7];return Math.abs(r-M)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(M))&&Math.abs(e-h)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(h))&&Math.abs(u-l)<=a.EPSILON*Math.max(1,Math.abs(u),Math.abs(l))&&Math.abs(o-v)<=a.EPSILON*Math.max(1,Math.abs(o),Math.abs(v))&&Math.abs(i-d)<=a.EPSILON*Math.max(1,Math.abs(i),Math.abs(d))&&Math.abs(s-b)<=a.EPSILON*Math.max(1,Math.abs(s),Math.abs(b))&&Math.abs(c-m)<=a.EPSILON*Math.max(1,Math.abs(c),Math.abs(m))&&Math.abs(f-p)<=a.EPSILON*Math.max(1,Math.abs(f),Math.abs(p))};var a=o(r(0)),e=o(r(3)),u=o(r(4));function o(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}function i(t,n,r){var a=.5*r[0],e=.5*r[1],u=.5*r[2],o=n[0],i=n[1],s=n[2],c=n[3];return t[0]=o,t[1]=i,t[2]=s,t[3]=c,t[4]=a*c+e*s-u*i,t[5]=e*c+u*o-a*s,t[6]=u*c+a*i-e*o,t[7]=-a*o-e*i-u*s,t}function s(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t}n.getReal=e.copy;n.setReal=e.copy;function c(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=r[4],s=r[5],c=r[6],f=r[7],M=n[4],h=n[5],l=n[6],v=n[7],d=r[0],b=r[1],m=r[2],p=r[3];return t[0]=a*p+o*d+e*m-u*b,t[1]=e*p+o*b+u*d-a*m,t[2]=u*p+o*m+a*b-e*d,t[3]=o*p-a*d-e*b-u*m,t[4]=a*f+o*i+e*c-u*s+M*p+v*d+h*m-l*b,t[5]=e*f+o*s+u*i-a*c+h*p+v*b+l*d-M*m,t[6]=u*f+o*c+a*s-e*i+l*p+v*m+M*b-h*d,t[7]=o*f-a*i-e*s-u*c+v*p-M*d-h*b-l*m,t}n.mul=c;var f=n.dot=e.dot;var M=n.length=e.length,h=(n.len=M,n.squaredLength=e.squaredLength);n.sqrLen=h},function(t,n,r){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.sub=n.mul=void 0,n.create=function(){var t=new a.ARRAY_TYPE(6);a.ARRAY_TYPE!=Float32Array&&(t[1]=0,t[2]=0,t[4]=0,t[5]=0);return t[0]=1,t[3]=1,t},n.clone=function(t){var n=new a.ARRAY_TYPE(6);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n},n.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t},n.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=0,t[5]=0,t},n.fromValues=function(t,n,r,e,u,o){var i=new a.ARRAY_TYPE(6);return i[0]=t,i[1]=n,i[2]=r,i[3]=e,i[4]=u,i[5]=o,i},n.set=function(t,n,r,a,e,u,o){return t[0]=n,t[1]=r,t[2]=a,t[3]=e,t[4]=u,t[5]=o,t},n.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],s=r*u-a*e;if(!s)return null;return s=1/s,t[0]=u*s,t[1]=-a*s,t[2]=-e*s,t[3]=r*s,t[4]=(e*i-u*o)*s,t[5]=(a*o-r*i)*s,t},n.determinant=function(t){return t[0]*t[3]-t[1]*t[2]},n.multiply=e,n.rotate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=Math.sin(r),f=Math.cos(r);return t[0]=a*f+u*c,t[1]=e*f+o*c,t[2]=a*-c+u*f,t[3]=e*-c+o*f,t[4]=i,t[5]=s,t},n.scale=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=r[0],f=r[1];return t[0]=a*c,t[1]=e*c,t[2]=u*f,t[3]=o*f,t[4]=i,t[5]=s,t},n.translate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=r[0],f=r[1];return t[0]=a,t[1]=e,t[2]=u,t[3]=o,t[4]=a*c+u*f+i,t[5]=e*c+o*f+s,t},n.fromRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=r,t[2]=-r,t[3]=a,t[4]=0,t[5]=0,t},n.fromScaling=function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=n[1],t[4]=0,t[5]=0,t},n.fromTranslation=function(t,n){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=n[0],t[5]=n[1],t},n.str=function(t){return"mat2d("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+")"},n.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+1)},n.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t[3]=n[3]+r[3],t[4]=n[4]+r[4],t[5]=n[5]+r[5],t},n.subtract=u,n.multiplyScalar=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t[3]=n[3]*r,t[4]=n[4]*r,t[5]=n[5]*r,t},n.multiplyScalarAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t[3]=n[3]+r[3]*a,t[4]=n[4]+r[4]*a,t[5]=n[5]+r[5]*a,t},n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]&&t[4]===n[4]&&t[5]===n[5]},n.equals=function(t,n){var r=t[0],e=t[1],u=t[2],o=t[3],i=t[4],s=t[5],c=n[0],f=n[1],M=n[2],h=n[3],l=n[4],v=n[5];return Math.abs(r-c)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(c))&&Math.abs(e-f)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(f))&&Math.abs(u-M)<=a.EPSILON*Math.max(1,Math.abs(u),Math.abs(M))&&Math.abs(o-h)<=a.EPSILON*Math.max(1,Math.abs(o),Math.abs(h))&&Math.abs(i-l)<=a.EPSILON*Math.max(1,Math.abs(i),Math.abs(l))&&Math.abs(s-v)<=a.EPSILON*Math.max(1,Math.abs(s),Math.abs(v))};var a=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(r(0));function e(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=r[0],f=r[1],M=r[2],h=r[3],l=r[4],v=r[5];return t[0]=a*c+u*f,t[1]=e*c+o*f,t[2]=a*M+u*h,t[3]=e*M+o*h,t[4]=a*l+u*v+i,t[5]=e*l+o*v+s,t}function u(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t[3]=n[3]-r[3],t[4]=n[4]-r[4],t[5]=n[5]-r[5],t}n.mul=e,n.sub=u},function(t,n,r){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.sub=n.mul=void 0,n.create=function(){var t=new a.ARRAY_TYPE(4);a.ARRAY_TYPE!=Float32Array&&(t[1]=0,t[2]=0);return t[0]=1,t[3]=1,t},n.clone=function(t){var n=new a.ARRAY_TYPE(4);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n},n.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t},n.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t},n.fromValues=function(t,n,r,e){var u=new a.ARRAY_TYPE(4);return u[0]=t,u[1]=n,u[2]=r,u[3]=e,u},n.set=function(t,n,r,a,e){return t[0]=n,t[1]=r,t[2]=a,t[3]=e,t},n.transpose=function(t,n){if(t===n){var r=n[1];t[1]=n[2],t[2]=r}else t[0]=n[0],t[1]=n[2],t[2]=n[1],t[3]=n[3];return t},n.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r*u-e*a;if(!o)return null;return o=1/o,t[0]=u*o,t[1]=-a*o,t[2]=-e*o,t[3]=r*o,t},n.adjoint=function(t,n){var r=n[0];return t[0]=n[3],t[1]=-n[1],t[2]=-n[2],t[3]=r,t},n.determinant=function(t){return t[0]*t[3]-t[2]*t[1]},n.multiply=e,n.rotate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),s=Math.cos(r);return t[0]=a*s+u*i,t[1]=e*s+o*i,t[2]=a*-i+u*s,t[3]=e*-i+o*s,t},n.scale=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=r[0],s=r[1];return t[0]=a*i,t[1]=e*i,t[2]=u*s,t[3]=o*s,t},n.fromRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=r,t[2]=-r,t[3]=a,t},n.fromScaling=function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=n[1],t},n.str=function(t){return"mat2("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},n.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2))},n.LDU=function(t,n,r,a){return t[2]=a[2]/a[0],r[0]=a[0],r[1]=a[1],r[3]=a[3]-t[2]*r[1],[t,n,r]},n.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t[3]=n[3]+r[3],t},n.subtract=u,n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]},n.equals=function(t,n){var r=t[0],e=t[1],u=t[2],o=t[3],i=n[0],s=n[1],c=n[2],f=n[3];return Math.abs(r-i)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(i))&&Math.abs(e-s)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(s))&&Math.abs(u-c)<=a.EPSILON*Math.max(1,Math.abs(u),Math.abs(c))&&Math.abs(o-f)<=a.EPSILON*Math.max(1,Math.abs(o),Math.abs(f))},n.multiplyScalar=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t[3]=n[3]*r,t},n.multiplyScalarAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t[3]=n[3]+r[3]*a,t};var a=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(r(0));function e(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=r[0],s=r[1],c=r[2],f=r[3];return t[0]=a*i+u*s,t[1]=e*i+o*s,t[2]=a*c+u*f,t[3]=e*c+o*f,t}function u(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t[3]=n[3]-r[3],t}n.mul=e,n.sub=u},function(t,n,r){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.vec4=n.vec3=n.vec2=n.quat2=n.quat=n.mat4=n.mat3=n.mat2d=n.mat2=n.glMatrix=void 0;var a=l(r(0)),e=l(r(9)),u=l(r(8)),o=l(r(5)),i=l(r(4)),s=l(r(3)),c=l(r(7)),f=l(r(6)),M=l(r(2)),h=l(r(1));function l(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}n.glMatrix=a,n.mat2=e,n.mat2d=u,n.mat3=o,n.mat4=i,n.quat=s,n.quat2=c,n.vec2=f,n.vec3=M,n.vec4=h}])}); \ No newline at end of file diff --git a/demo/js/libs/litegl.js b/demo/js/libs/litegl.js new file mode 100644 index 000000000..f9ae7f822 --- /dev/null +++ b/demo/js/libs/litegl.js @@ -0,0 +1,13432 @@ +//packer version +//litegl.js by Javi Agenjo 2014 @tamat (tamats.com) +//forked from lightgl.js by Evan Wallace (madebyevan.com) +"use strict"; + +(function(global){ + +var GL = global.GL = {}; + +if(typeof(glMatrix) == "undefined") + throw("litegl.js requires gl-matrix to work. It must be included before litegl."); +else +{ + if(!global.vec2) + throw("litegl.js does not support gl-matrix 3.0, download 2.8 https://github.com/toji/gl-matrix/releases/tag/v2.8.1"); +} + +//polyfill +global.requestAnimationFrame = global.requestAnimationFrame || global.mozRequestAnimationFrame || global.webkitRequestAnimationFrame || function(callback) { setTimeout(callback, 1000 / 60); }; + +GL.blockable_keys = {"Up":true,"Down":true,"Left":true,"Right":true}; + +GL.reverse = null; + +//some consts +//https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button +GL.LEFT_MOUSE_BUTTON = 0; +GL.MIDDLE_MOUSE_BUTTON = 1; +GL.RIGHT_MOUSE_BUTTON = 2; + +GL.LEFT_MOUSE_BUTTON_MASK = 1; +GL.RIGHT_MOUSE_BUTTON_MASK = 2; +GL.MIDDLE_MOUSE_BUTTON_MASK = 4; + +GL.last_context_id = 0; + + +//Define WEBGL ENUMS as statics (more to come in WebGL 2) +//sometimes we need some gl enums before having the gl context, solution: define them globally because the specs says they are constant) + +GL.COLOR_BUFFER_BIT = 16384; +GL.DEPTH_BUFFER_BIT = 256; +GL.STENCIL_BUFFER_BIT = 1024; + +GL.TEXTURE_2D = 3553; +GL.TEXTURE_CUBE_MAP = 34067; +GL.TEXTURE_3D = 32879; + +GL.TEXTURE_MAG_FILTER = 10240; +GL.TEXTURE_MIN_FILTER = 10241; +GL.TEXTURE_WRAP_S = 10242; +GL.TEXTURE_WRAP_T = 10243; + +GL.BYTE = 5120; +GL.UNSIGNED_BYTE = 5121; +GL.SHORT = 5122; +GL.UNSIGNED_SHORT = 5123; +GL.INT = 5124; +GL.UNSIGNED_INT = 5125; +GL.FLOAT = 5126; +GL.HALF_FLOAT_OES = 36193; //webgl 1.0 only + +//webgl2 formats +GL.HALF_FLOAT = 5131; +GL.DEPTH_COMPONENT16 = 33189; +GL.DEPTH_COMPONENT24 = 33190; +GL.DEPTH_COMPONENT32F = 36012; + +GL.FLOAT_VEC2 = 35664; +GL.FLOAT_VEC3 = 35665; +GL.FLOAT_VEC4 = 35666; +GL.INT_VEC2 = 35667; +GL.INT_VEC3 = 35668; +GL.INT_VEC4 = 35669; +GL.BOOL = 35670; +GL.BOOL_VEC2 = 35671; +GL.BOOL_VEC3 = 35672; +GL.BOOL_VEC4 = 35673; +GL.FLOAT_MAT2 = 35674; +GL.FLOAT_MAT3 = 35675; +GL.FLOAT_MAT4 = 35676; + +//used to know the amount of data to reserve per uniform +GL.TYPE_LENGTH = {}; +GL.TYPE_LENGTH[ GL.FLOAT ] = GL.TYPE_LENGTH[ GL.INT ] = GL.TYPE_LENGTH[ GL.BYTE ] = GL.TYPE_LENGTH[ GL.BOOL ] = 1; +GL.TYPE_LENGTH[ GL.FLOAT_VEC2 ] = GL.TYPE_LENGTH[ GL.INT_VEC2 ] = GL.TYPE_LENGTH[ GL.BOOL_VEC2 ] = 2; +GL.TYPE_LENGTH[ GL.FLOAT_VEC3 ] = GL.TYPE_LENGTH[ GL.INT_VEC3 ] = GL.TYPE_LENGTH[ GL.BOOL_VEC3 ] = 3; +GL.TYPE_LENGTH[ GL.FLOAT_VEC4 ] = GL.TYPE_LENGTH[ GL.INT_VEC4 ] = GL.TYPE_LENGTH[ GL.BOOL_VEC4 ] = 4; +GL.TYPE_LENGTH[ GL.FLOAT_MAT3 ] = 9; +GL.TYPE_LENGTH[ GL.FLOAT_MAT4 ] = 16; + + +GL.SAMPLER_2D = 35678; +GL.SAMPLER_3D = 35679; +GL.SAMPLER_CUBE = 35680; + +GL.DEPTH_COMPONENT = 6402; +GL.ALPHA = 6406; +GL.RGB = 6407; +GL.RGBA = 6408; +GL.LUMINANCE = 6409; +GL.LUMINANCE_ALPHA = 6410; +GL.DEPTH_STENCIL = 34041; +GL.UNSIGNED_INT_24_8_WEBGL = 34042; + +//webgl2 formats +GL.R8 = 33321; +GL.R16F = 33325; +GL.R32F = 33326; +GL.R8UI = 33330; +GL.RG8 = 33323; +GL.RG16F = 33327; +GL.RG32F = 33328; +GL.RGB8 = 32849; +GL.SRGB8 = 35905; +GL.RGB565 = 36194; +GL.R11F_G11F_B10F = 35898; +GL.RGB9_E5 = 35901; +GL.RGB16F = 34843; +GL.RGB32F = 34837; +GL.RGB8UI = 36221; +GL.RGBA8 = 32856; +GL.RGB5_A1 = 32855; +GL.RGBA16F = 34842; +GL.RGBA32F = 34836; +GL.RGBA8UI = 36220; +GL.RGBA16I = 36232; +GL.RGBA16UI = 36214; +GL.RGBA32I = 36226; +GL.RGBA32UI = 36208; + +GL.NEAREST = 9728; +GL.LINEAR = 9729; +GL.NEAREST_MIPMAP_NEAREST = 9984; +GL.LINEAR_MIPMAP_NEAREST = 9985; +GL.NEAREST_MIPMAP_LINEAR = 9986; +GL.LINEAR_MIPMAP_LINEAR = 9987; + +GL.REPEAT = 10497; +GL.CLAMP_TO_EDGE = 33071; +GL.MIRRORED_REPEAT = 33648; + +GL.ZERO = 0; +GL.ONE = 1; +GL.SRC_COLOR = 768; +GL.ONE_MINUS_SRC_COLOR = 769; +GL.SRC_ALPHA = 770; +GL.ONE_MINUS_SRC_ALPHA = 771; +GL.DST_ALPHA = 772; +GL.ONE_MINUS_DST_ALPHA = 773; +GL.DST_COLOR = 774; +GL.ONE_MINUS_DST_COLOR = 775; +GL.SRC_ALPHA_SATURATE = 776; +GL.CONSTANT_COLOR = 32769; +GL.ONE_MINUS_CONSTANT_COLOR = 32770; +GL.CONSTANT_ALPHA = 32771; +GL.ONE_MINUS_CONSTANT_ALPHA = 32772; + +GL.VERTEX_SHADER = 35633; +GL.FRAGMENT_SHADER = 35632; + +GL.FRONT = 1028; +GL.BACK = 1029; +GL.FRONT_AND_BACK = 1032; + +GL.NEVER = 512; +GL.LESS = 513; +GL.EQUAL = 514; +GL.LEQUAL = 515; +GL.GREATER = 516; +GL.NOTEQUAL = 517; +GL.GEQUAL = 518; +GL.ALWAYS = 519; + +GL.KEEP = 7680; +GL.REPLACE = 7681; +GL.INCR = 7682; +GL.DECR = 7683; +GL.INCR_WRAP = 34055; +GL.DECR_WRAP = 34056; +GL.INVERT = 5386; + +GL.STREAM_DRAW = 35040; +GL.STATIC_DRAW = 35044; +GL.DYNAMIC_DRAW = 35048; + +GL.ARRAY_BUFFER = 34962; +GL.ELEMENT_ARRAY_BUFFER = 34963; + +GL.POINTS = 0; +GL.LINES = 1; +GL.LINE_LOOP = 2; +GL.LINE_STRIP = 3; +GL.TRIANGLES = 4; +GL.TRIANGLE_STRIP = 5; +GL.TRIANGLE_FAN = 6; + +GL.CW = 2304; +GL.CCW = 2305; + +GL.CULL_FACE = 2884; +GL.DEPTH_TEST = 2929; +GL.BLEND = 3042; + +GL.temp_vec3 = vec3.create(); +GL.temp2_vec3 = vec3.create(); +GL.temp_vec4 = vec4.create(); +GL.temp_quat = quat.create(); +GL.temp_mat3 = mat3.create(); +GL.temp_mat4 = mat4.create(); + + +global.DEG2RAD = 0.0174532925; +global.RAD2DEG = 57.295779578552306; +global.EPSILON = 0.000001; + +/** +* Tells if one number is power of two (used for textures) +* @method isPowerOfTwo +* @param {v} number +* @return {boolean} +*/ +global.isPowerOfTwo = GL.isPowerOfTwo = function isPowerOfTwo(v) +{ + return ((Math.log(v) / Math.log(2)) % 1) == 0; +} + +/** +* Tells if one number is power of two (used for textures) +* @method isPowerOfTwo +* @param {v} number +* @return {boolean} +*/ +global.nearestPowerOfTwo = GL.nearestPowerOfTwo = function nearestPowerOfTwo(v) +{ + return Math.pow(2, Math.round( Math.log( v ) / Math.log(2) ) ) +} + + +/** +* converts from polar to cartesian +* @method polarToCartesian +* @param {vec3} out +* @param {number} azimuth orientation from 0 to 2PI +* @param {number} inclianation from -PI to PI +* @param {number} radius +* @return {vec3} returns out +*/ +global.polarToCartesian = function( out, azimuth, inclination, radius ) +{ + out = out || vec3.create(); + out[0] = radius * Math.sin(inclination) * Math.cos(azimuth); + out[1] = radius * Math.cos(inclination); + out[2] = radius * Math.sin(inclination) * Math.sin(azimuth); + return out; +} + +/** +* converts from cartesian to polar +* @method cartesianToPolar +* @param {vec3} out +* @param {number} x +* @param {number} y +* @param {number} z +* @return {vec3} returns [azimuth,inclination,radius] +*/ +global.cartesianToPolar = function( out, x,y,z ) +{ + out = out || vec3.create(); + out[2] = Math.sqrt(x*x+y*y+z*z); + out[0] = Math.atan2(x,z); + out[1] = Math.acos(z/out[2]); + return out; +} + +//Global Scope +//better array conversion to string for serializing +var typed_arrays = [ Uint8Array, Int8Array, Uint16Array, Int16Array, Uint32Array, Int32Array, Float32Array, Float64Array ]; +function typedToArray(){ + return Array.prototype.slice.call(this); +} +typed_arrays.forEach( function(v) { + if(!v.prototype.toJSON) + Object.defineProperty( v.prototype, "toJSON", { + value: typedToArray, + enumerable: false + }); +}); + + + +/** +* Get current time in milliseconds +* @method getTime +* @return {number} +*/ +if(typeof(performance) != "undefined") + global.getTime = performance.now.bind(performance); +else + global.getTime = Date.now.bind( Date ); +GL.getTime = global.getTime; + + +global.isFunction = function isFunction(obj) { + return !!(obj && obj.constructor && obj.call && obj.apply); +} + +global.isArray = function isArray(obj) { + return (obj && obj.constructor === Array ); + //var str = Object.prototype.toString.call(obj); + //return str == '[object Array]' || str == '[object Float32Array]'; +} + +global.isNumber = function isNumber(obj) { + return (obj != null && obj.constructor === Number ); +} + +global.getClassName = function getClassName(obj) +{ + if (!obj) + return; + + //from function info, but not standard + if(obj.name) + return obj.name; + + //from sourcecode + if(obj.toString) { + var arr = obj.toString().match( + /function\s*(\w+)/); + if (arr && arr.length == 2) { + return arr[1]; + } + } +} + +/** +* clone one object recursively, only allows objects containing number,strings,typed-arrays or other objects +* @method cloneObject +* @param {Object} object +* @param {Object} target if omited an empty object is created +* @return {Object} +*/ +global.cloneObject = GL.cloneObject = function(o, t) +{ + if(o.constructor !== Object) + throw("cloneObject only can clone pure javascript objects, not classes"); + + t = t || {}; + + for(var i in o) + { + var v = o[i]; + if(v === null) + { + t[i] = null; + continue; + } + + switch(v.constructor) + { + case Int8Array: + case Uint8Array: + case Int16Array: + case Uint16Array: + case Int32Array: + case Uint32Array: + case Float32Array: + case Float64Array: + t[i] = new v.constructor(v); + break; + case Boolean: + case Number: + case String: + t[i] = v; + break; + case Array: + t[i] = v.concat(); //content is not cloned + break; + case Object: + t[i] = GL.cloneObject(v); + break; + } + } + + return t; +} + + +/* SLOW because accepts booleans +function isNumber(obj) { + var str = Object.prototype.toString.call(obj); + return str == '[object Number]' || str == '[object Boolean]'; +} +*/ + +//given a regular expression, a text and a callback, it calls the function every time it finds it +global.regexMap = function regexMap(regex, text, callback) { + var result; + while ((result = regex.exec(text)) != null) { + callback(result); + } +} + +global.createCanvas = GL.createCanvas = function createCanvas(width, height) { + var canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + return canvas; +} + +global.cloneCanvas = GL.cloneCanvas = function cloneCanvas(c) { + var canvas = document.createElement('canvas'); + canvas.width = c.width; + canvas.height = c.height; + var ctx = canvas.getContext("2d"); + ctx.drawImage(c,0,0); + return canvas; +} + +if(typeof(Image) != "undefined") //not existing inside workers +{ + Image.prototype.getPixels = function() + { + var canvas = document.createElement('canvas'); + canvas.width = this.width; + canvas.height = this.height; + var ctx = canvas.getContext("2d"); + ctx.drawImage(this,0,0); + return ctx.getImageData(0, 0, this.width, this.height).data; + } +} + +//you must pass an object with characters to replace and replace with what {"a":"A","c":"C"} +if(!String.prototype.hasOwnProperty("replaceAll")) + Object.defineProperty(String.prototype, "replaceAll", { + value: function(words){ + var str = this; + for(var i in words) + str = str.split(i).join(words[i]); + return str; + }, + enumerable: false + }); + +/* +String.prototype.replaceAll = function(words){ + var str = this; + for(var i in words) + str = str.split(i).join(words[i]); + return str; +}; +*/ + +//used for hashing keys +if(!String.prototype.hasOwnProperty("hashCode")) + Object.defineProperty(String.prototype, "hashCode", { + value: function(){ + var hash = 0, i, c, l; + if (this.length == 0) return hash; + for (i = 0, l = this.length; i < l; ++i) { + c = this.charCodeAt(i); + hash = ((hash<<5)-hash)+c; + hash |= 0; // Convert to 32bit integer + } + return hash; + }, + enumerable: false + }); + +//avoid errors when Typed array is expected and regular array is found +//Array.prototype.subarray = Array.prototype.slice; +//if(!Array.prototype.hasOwnProperty("subarray")) +// Object.defineProperty(Array.prototype, "subarray", { value: Array.prototype.slice, enumerable: false }); + +if(!Array.prototype.hasOwnProperty("clone")) + Object.defineProperty(Array.prototype, "clone", { value: Array.prototype.concat, enumerable: false }); +if(!Float32Array.prototype.hasOwnProperty("clone")) + Object.defineProperty(Float32Array.prototype, "clone", { value: function() { return new Float32Array(this); }, enumerable: false }); + + +// remove all properties on obj, effectively reverting it to a new object (to reduce garbage) +global.wipeObject = function wipeObject(obj) +{ + for (var p in obj) + { + if (obj.hasOwnProperty(p)) + delete obj[p]; + } +}; + +//copy methods from origin to target +global.extendClass = GL.extendClass = function extendClass( target, origin ) { + for(var i in origin) //copy class properties + { + if(target.hasOwnProperty(i)) + continue; + target[i] = origin[i]; + } + + if(origin.prototype) //copy prototype properties + { + var prop_names = Object.getOwnPropertyNames( origin.prototype ); + for(var i = 0; i < prop_names.length; ++i) //only enumerables + { + var name = prop_names[i]; + //if(!origin.prototype.hasOwnProperty(name)) + // continue; + + if(target.prototype.hasOwnProperty(name)) //avoid overwritting existing ones + continue; + + //copy getters + if(origin.prototype.__lookupGetter__(name)) + target.prototype.__defineGetter__(name, origin.prototype.__lookupGetter__(name)); + else + target.prototype[name] = origin.prototype[name]; + + //and setters + if(origin.prototype.__lookupSetter__(name)) + target.prototype.__defineSetter__(name, origin.prototype.__lookupSetter__(name)); + } + } + + if(!target.hasOwnProperty("superclass")) + Object.defineProperty(target, "superclass", { + get: function() { return origin }, + enumerable: false + }); +} + + + +//simple http request +global.HttpRequest = GL.request = function HttpRequest( url, params, callback, error, options ) +{ + var async = true; + if(options && options.async !== undefined) + async = options.async; + + if(params) + { + var params_str = null; + var params_arr = []; + for(var i in params) + params_arr.push(i + "=" + params[i]); + params_str = params_arr.join("&"); + url = url + "?" + params_str; + } + + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, async); + xhr.onload = function(e) + { + var response = this.response; + var type = this.getResponseHeader("Content-Type"); + if(this.status != 200) + { + LEvent.trigger(xhr,"fail",this.status); + if(error) + error(this.status); + return; + } + + LEvent.trigger(xhr,"done",this.response); + if(callback) + callback(this.response); + return; + } + + xhr.onerror = function(err) + { + LEvent.trigger(xhr,"fail",err); + } + + if(options) + { + for(var i in options) + xhr[i] = options[i]; + if(options.binary) + xhr.responseType = "arraybuffer"; + } + + xhr.send(); + + return xhr; +} + +//cheap simple promises +if( global.XMLHttpRequest ) +{ + if( !XMLHttpRequest.prototype.hasOwnProperty("done") ) + Object.defineProperty( XMLHttpRequest.prototype, "done", { enumerable: false, value: function(callback) + { + LEvent.bind(this,"done", function(e,err) { callback(err); } ); + return this; + }}); + + if( !XMLHttpRequest.prototype.hasOwnProperty("fail") ) + Object.defineProperty( XMLHttpRequest.prototype, "fail", { enumerable: false, value: function(callback) + { + LEvent.bind(this,"fail", function(e,err) { callback(err); } ); + return this; + }}); +} + +global.getFileExtension = function getFileExtension(url) +{ + var question = url.indexOf("?"); + if(question != -1) + url = url.substr(0,question); + var point = url.lastIndexOf("."); + if(point == -1) + return ""; + return url.substr(point+1).toLowerCase(); +} + + +//allows to pack several (text)files inside one single file (useful for shaders) +//every file must start with \filename.ext or /filename.ext +global.loadFileAtlas = GL.loadFileAtlas = function loadFileAtlas(url, callback, sync) +{ + var deferred_callback = null; + + HttpRequest(url, null, function(data) { + var files = GL.processFileAtlas(data); + if(callback) + callback(files); + if(deferred_callback) + deferred_callback(files); + }, alert, sync); + + return { done: function(callback) { deferred_callback = callback; } }; +} + +//This parses a text file that contains several text files (they are separated by "\filename"), and returns an object with every file separatly +global.processFileAtlas = GL.processFileAtlas = function(data, skip_trim) +{ + var lines = data.split("\n"); + var files = {}; + + var current_file_lines = []; + var current_file_name = ""; + for(var i = 0, l = lines.length; i < l; i++) + { + var line = skip_trim ? lines[i] : lines[i].trim(); + if(!line.length) + continue; + if( line[0] != "\\") + { + current_file_lines.push(line); + continue; + } + + if( current_file_lines.length ) + files[ current_file_name ] = current_file_lines.join("\n"); + current_file_lines.length = 0; + current_file_name = line.substr(1); + } + + if( current_file_lines.length ) + files[ current_file_name ] = current_file_lines.join("\n"); + + return files; +} + + +/* +global.halfFloatToFloat = function( h ) +{ + function convertMantissa(i) { + if (i == 0) + return 0 + else if (i < 1024) + { + var m = i << 13; + var e = 0; + while (!(m & 0x00800000)) + { + e -= 0x00800000 + m = m << 1 + } + m &= ~0x00800000 + e += 0x38800000 + return m | e; + } + return 0x38000000 + ((i - 1024) << 13); + } + + function convertExponent(i) { + if (i == 0) + return 0; + else if (i >= 1 && i <= 31) + return i << 23; + else if (i == 31) + return 0x47800000; + else if (i == 32) + return 0x80000000; + else if (i >= 33 && i <= 63) + return 0x80000000 + ((i - 32) << 23); + return 0xC7800000; + } + + function convertOffset(i) { + if (i == 0 || i == 32) + return 0 + return 1024; + } + + var v = convertMantissa( convertOffset( h >> 10) + (h & 0x3ff) ) + convertExponent(h >> 10); + var a = new Uint32Array([v]); + return (new Float32Array(a.buffer))[0]; +} +*/ + +global.typedArrayToArray = function(array) +{ + var r = []; + r.length = array.length; + for(var i = 0; i < array.length; i++) + r[i] = array[i]; + return r; +} + +global.RGBToHex = function(r, g, b) { + r = Math.min(255, r*255)|0; + g = Math.min(255, g*255)|0; + b = Math.min(255, b*255)|0; + return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); +} + +global.HUEToRGB = function ( p, q, t ){ + if(t < 0) t += 1; + if(t > 1) t -= 1; + if(t < 1/6) return p + (q - p) * 6 * t; + if(t < 1/2) return q; + if(t < 2/3) return p + (q - p) * (2/3 - t) * 6; + return p; +} + +global.HSLToRGB = function( h, s, l, out ){ + var r, g, b; + out = out || vec3.create(); + if(s == 0){ + r = g = b = l; // achromatic + }else{ + var q = l < 0.5 ? l * (1 + s) : l + s - l * s; + var p = 2 * l - q; + r = HUEToRGB(p, q, h + 1/3); + g = HUEToRGB(p, q, h); + b = HUEToRGB(p, q, h - 1/3); + } + out[0] = r; + out[1] = g; + out[2] = b; + return out; +} + +global.hexColorToRGBA = (function() { + //to change the color: from http://www.w3schools.com/cssref/css_colorsfull.asp + var string_colors = { + white: [1,1,1], + black: [0,0,0], + gray: [0.501960813999176, 0.501960813999176, 0.501960813999176], + red: [1,0,0], + orange: [1, 0.6470588445663452, 0], + pink: [1, 0.7529411911964417, 0.7960784435272217], + green: [0, 0.501960813999176, 0], + lime: [0,1,0], + blue: [0,0,1], + violet: [0.9333333373069763, 0.5098039507865906, 0.9333333373069763], + magenta: [1,0,1], + cyan: [0,1,1], + yellow: [1,1,0], + brown: [0.6470588445663452, 0.16470588743686676, 0.16470588743686676], + silver: [0.7529411911964417, 0.7529411911964417, 0.7529411911964417], + gold: [1, 0.843137264251709, 0], + transparent: [0,0,0,0] + }; + + return function( hex, color, alpha ) + { + alpha = (alpha === undefined ? 1 : alpha); + color = color || new Float32Array(4); + color[3] = alpha; + + if(typeof(hex) != "string") + return color; + + + //for those hardcoded colors + var col = string_colors[hex]; + if( col !== undefined ) + { + color.set( col ); + if(color.length == 3) + color[3] = alpha; + else + color[3] *= alpha; + return color; + } + + //rgba colors + var pos = hex.indexOf("rgba("); + if(pos != -1) + { + var str = hex.substr(5,hex.length-2); + str = str.split(","); + color[0] = parseInt( str[0] ) / 255; + color[1] = parseInt( str[1] ) / 255; + color[2] = parseInt( str[2] ) / 255; + color[3] = parseFloat( str[3] ) * alpha; + return color; + } + + var pos = hex.indexOf("hsla("); + if(pos != -1) + { + var str = hex.substr(5,hex.length-2); + str = str.split(","); + HSLToRGB( parseInt( str[0] ) / 360, parseInt( str[1] ) / 100, parseInt( str[2] ) / 100, color ); + color[3] = parseFloat( str[3] ) * alpha; + return color; + } + + color[3] = alpha; + + //rgb colors + var pos = hex.indexOf("rgb("); + if(pos != -1) + { + var str = hex.substr(4,hex.length-2); + str = str.split(","); + color[0] = parseInt( str[0] ) / 255; + color[1] = parseInt( str[1] ) / 255; + color[2] = parseInt( str[2] ) / 255; + return color; + } + + var pos = hex.indexOf("hsl("); + if(pos != -1) + { + var str = hex.substr(4,hex.length-2); + str = str.split(","); + HSLToRGB( parseInt( str[0] ) / 360, parseInt( str[1] ) / 100, parseInt( str[2] ) / 100, color ); + return color; + } + + + //the rest + // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") + var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; + hex = hex.replace( shorthandRegex, function(m, r, g, b) { + return r + r + g + g + b + b; + }); + + var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + if(!result) + return color; + + color[0] = parseInt(result[1], 16) / 255; + color[1] = parseInt(result[2], 16) / 255; + color[2] = parseInt(result[3], 16) / 255; + return color; + } +})(); +/** + * @fileoverview dds - Utilities for loading DDS texture files + * @author Brandon Jones + * @version 0.1 + */ + +/* + * Copyright (c) 2012 Brandon Jones + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + */ + +var DDS = (function () { + + "use strict"; + + // All values and structures referenced from: + // http://msdn.microsoft.com/en-us/library/bb943991.aspx/ + var DDS_MAGIC = 0x20534444; + + var DDSD_CAPS = 0x1, + DDSD_HEIGHT = 0x2, + DDSD_WIDTH = 0x4, + DDSD_PITCH = 0x8, + DDSD_PIXELFORMAT = 0x1000, + DDSD_MIPMAPCOUNT = 0x20000, + DDSD_LINEARSIZE = 0x80000, + DDSD_DEPTH = 0x800000; + + var DDSCAPS_COMPLEX = 0x8, + DDSCAPS_MIPMAP = 0x400000, + DDSCAPS_TEXTURE = 0x1000; + + var DDSCAPS2_CUBEMAP = 0x200, + DDSCAPS2_CUBEMAP_POSITIVEX = 0x400, + DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800, + DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000, + DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000, + DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000, + DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000, + DDSCAPS2_VOLUME = 0x200000; + + var DDPF_ALPHAPIXELS = 0x1, + DDPF_ALPHA = 0x2, + DDPF_FOURCC = 0x4, + DDPF_RGB = 0x40, + DDPF_YUV = 0x200, + DDPF_LUMINANCE = 0x20000; + + function fourCCToInt32(value) { + return value.charCodeAt(0) + + (value.charCodeAt(1) << 8) + + (value.charCodeAt(2) << 16) + + (value.charCodeAt(3) << 24); + } + + function int32ToFourCC(value) { + return String.fromCharCode( + value & 0xff, + (value >> 8) & 0xff, + (value >> 16) & 0xff, + (value >> 24) & 0xff + ); + } + + var FOURCC_DXT1 = fourCCToInt32("DXT1"); + var FOURCC_DXT3 = fourCCToInt32("DXT3"); + var FOURCC_DXT5 = fourCCToInt32("DXT5"); + + var headerLengthInt = 31; // The header length in 32 bit ints + + // Offsets into the header array + var off_magic = 0; + + var off_size = 1; + var off_flags = 2; + var off_height = 3; + var off_width = 4; + + var off_mipmapCount = 7; + + var off_pfFlags = 20; + var off_pfFourCC = 21; + var off_caps = 27; + + // Little reminder for myself where the above values come from + /*DDS_PIXELFORMAT { + int32 dwSize; // offset: 19 + int32 dwFlags; + char[4] dwFourCC; + int32 dwRGBBitCount; + int32 dwRBitMask; + int32 dwGBitMask; + int32 dwBBitMask; + int32 dwABitMask; // offset: 26 + }; + + DDS_HEADER { + int32 dwSize; // 1 + int32 dwFlags; + int32 dwHeight; + int32 dwWidth; + int32 dwPitchOrLinearSize; + int32 dwDepth; + int32 dwMipMapCount; // offset: 7 + int32[11] dwReserved1; + DDS_PIXELFORMAT ddspf; // offset 19 + int32 dwCaps; // offset: 27 + int32 dwCaps2; + int32 dwCaps3; + int32 dwCaps4; + int32 dwReserved2; // offset 31 + };*/ + + /** + * Transcodes DXT into RGB565. + * Optimizations: + * 1. Use integer math to compute c2 and c3 instead of floating point + * math. Specifically: + * c2 = 5/8 * c0 + 3/8 * c1 + * c3 = 3/8 * c0 + 5/8 * c1 + * This is about a 40% performance improvement. It also appears to + * match what hardware DXT decoders do, as the colors produced + * by this integer math match what hardware produces, while the + * floating point in dxtToRgb565Unoptimized() produce slightly + * different colors (for one GPU this was tested on). + * 2. Unroll the inner loop. Another ~10% improvement. + * 3. Compute r0, g0, b0, r1, g1, b1 only once instead of twice. + * Another 10% improvement. + * 4. Use a Uint16Array instead of a Uint8Array. Another 10% improvement. + * @author Evan Parker + * @param {Uint16Array} src The src DXT bits as a Uint16Array. + * @param {number} srcByteOffset + * @param {number} width + * @param {number} height + * @return {Uint16Array} dst + */ + function dxtToRgb565(src, src16Offset, width, height) { + var c = new Uint16Array(4); + var dst = new Uint16Array(width * height); + var nWords = (width * height) / 4; + var m = 0; + var dstI = 0; + var i = 0; + var r0 = 0, g0 = 0, b0 = 0, r1 = 0, g1 = 0, b1 = 0; + + var blockWidth = width / 4; + var blockHeight = height / 4; + for (var blockY = 0; blockY < blockHeight; blockY++) { + for (var blockX = 0; blockX < blockWidth; blockX++) { + i = src16Offset + 4 * (blockY * blockWidth + blockX); + c[0] = src[i]; + c[1] = src[i + 1]; + r0 = c[0] & 0x1f; + g0 = c[0] & 0x7e0; + b0 = c[0] & 0xf800; + r1 = c[1] & 0x1f; + g1 = c[1] & 0x7e0; + b1 = c[1] & 0xf800; + // Interpolate between c0 and c1 to get c2 and c3. + // Note that we approximate 1/3 as 3/8 and 2/3 as 5/8 for + // speed. This also appears to be what the hardware DXT + // decoder in many GPUs does :) + c[2] = ((5 * r0 + 3 * r1) >> 3) + | (((5 * g0 + 3 * g1) >> 3) & 0x7e0) + | (((5 * b0 + 3 * b1) >> 3) & 0xf800); + c[3] = ((5 * r1 + 3 * r0) >> 3) + | (((5 * g1 + 3 * g0) >> 3) & 0x7e0) + | (((5 * b1 + 3 * b0) >> 3) & 0xf800); + m = src[i + 2]; + dstI = (blockY * 4) * width + blockX * 4; + dst[dstI] = c[m & 0x3]; + dst[dstI + 1] = c[(m >> 2) & 0x3]; + dst[dstI + 2] = c[(m >> 4) & 0x3]; + dst[dstI + 3] = c[(m >> 6) & 0x3]; + dstI += width; + dst[dstI] = c[(m >> 8) & 0x3]; + dst[dstI + 1] = c[(m >> 10) & 0x3]; + dst[dstI + 2] = c[(m >> 12) & 0x3]; + dst[dstI + 3] = c[(m >> 14)]; + m = src[i + 3]; + dstI += width; + dst[dstI] = c[m & 0x3]; + dst[dstI + 1] = c[(m >> 2) & 0x3]; + dst[dstI + 2] = c[(m >> 4) & 0x3]; + dst[dstI + 3] = c[(m >> 6) & 0x3]; + dstI += width; + dst[dstI] = c[(m >> 8) & 0x3]; + dst[dstI + 1] = c[(m >> 10) & 0x3]; + dst[dstI + 2] = c[(m >> 12) & 0x3]; + dst[dstI + 3] = c[(m >> 14)]; + } + } + return dst; + } + + function BGRtoRGB( byteArray ) + { + for(var j = 0, l = byteArray.length, tmp = 0; j < l; j+=4) //BGR fix + { + tmp = byteArray[j]; + byteArray[j] = byteArray[j+2]; + byteArray[j+2] = tmp; + } + } + + function flipDXT( width, blockBytes, byteArray ) + { + //TODO + //var row = Uint8Array(width); + } + + + /** + * Parses a DDS file from the given arrayBuffer and uploads it into the currently bound texture + * + * @param {WebGLRenderingContext} gl WebGL rendering context + * @param {WebGLCompressedTextureS3TC} ext WEBGL_compressed_texture_s3tc extension object + * @param {TypedArray} arrayBuffer Array Buffer containing the DDS files data + * @param {boolean} [loadMipmaps] If false only the top mipmap level will be loaded, otherwise all available mipmaps will be uploaded + * + * @returns {number} Number of mipmaps uploaded, 0 if there was an error + */ + function uploadDDSLevels(gl, ext, arrayBuffer, loadMipmaps) { + var header = new Int32Array(arrayBuffer, 0, headerLengthInt), + fourCC, blockBytes, internalFormat, + width, height, dataLength, dataOffset, is_cubemap, + rgb565Data, byteArray, mipmapCount, i, face; + + if(header[off_magic] != DDS_MAGIC) { + console.error("Invalid magic number in DDS header"); + return 0; + } + + if(!header[off_pfFlags] & DDPF_FOURCC) { + console.error("Unsupported format, must contain a FourCC code"); + return 0; + } + + fourCC = header[off_pfFourCC]; + switch(fourCC) { + case FOURCC_DXT1: + blockBytes = 8; + internalFormat = ext ? ext.COMPRESSED_RGB_S3TC_DXT1_EXT : null; + break; + + /* + case FOURCC_DXT1: + blockBytes = 8; + internalFormat = ext ? ext.COMPRESSED_RGBA_S3TC_DXT1_EXT : null; + break; + */ + + case FOURCC_DXT3: + blockBytes = 16; + internalFormat = ext ? ext.COMPRESSED_RGBA_S3TC_DXT3_EXT : null; + break; + + case FOURCC_DXT5: + blockBytes = 16; + internalFormat = ext ? ext.COMPRESSED_RGBA_S3TC_DXT5_EXT : null; + break; + + default: + blockBytes = 4; + fourCC = null; + internalFormat = gl.RGBA; + //console.error("Unsupported FourCC code:", int32ToFourCC(fourCC), fourCC); + //return null; + } + + mipmapCount = 1; + if(header[off_flags] & DDSD_MIPMAPCOUNT && loadMipmaps !== false) { + mipmapCount = Math.max(1, header[off_mipmapCount]); + } + + width = header[off_width]; + height = header[off_height]; + dataOffset = header[off_size] + 4; + is_cubemap = !!(header[off_caps+1] & DDSCAPS2_CUBEMAP); + + if(is_cubemap) + { + //console.error("Cubemaps not supported in DDS"); + //return null; + + for(face = 0; face < 6; ++face) + { + width = header[off_width]; + height = header[off_height]; + for(var i = 0; i < mipmapCount; ++i) { + if(fourCC) + { + dataLength = Math.max( 4, width )/4 * Math.max( 4, height )/4 * blockBytes; + byteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength); + flipDXT( width, blockBytes, byteArray ); + gl.compressedTexImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + face, i, internalFormat, width, height, 0, byteArray); + } + else + { + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false ); + dataLength = width * height * blockBytes; + byteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength); + BGRtoRGB(byteArray); + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + face, i, internalFormat, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, byteArray); + } + dataOffset += dataLength; + width *= 0.5; + height *= 0.5; + } + } + } + else //2d texture + { + if(ext) { + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true ); + for(var i = 0; i < mipmapCount; ++i) { + if(fourCC) + { + dataLength = Math.max( 4, width )/4 * Math.max( 4, height )/4 * blockBytes; + byteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength); + gl.compressedTexImage2D(gl.TEXTURE_2D, i, internalFormat, width, height, 0, byteArray); + } + else + { + dataLength = width * height * blockBytes; + byteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength); + BGRtoRGB(byteArray); + gl.texImage2D(gl.TEXTURE_2D, i, internalFormat, width, height, 0, internalFormat, gl.UNSIGNED_BYTE, byteArray); + } + dataOffset += dataLength; + width *= 0.5; + height *= 0.5; + } + } else { + if(fourCC == FOURCC_DXT1) { + dataLength = Math.max( 4, width )/4 * Math.max( 4, height )/4 * blockBytes; + byteArray = new Uint16Array(arrayBuffer); + //Decompress + rgb565Data = dxtToRgb565(byteArray, dataOffset / 2, width, height); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, width, height, 0, gl.RGB, gl.UNSIGNED_SHORT_5_6_5, rgb565Data); + if(loadMipmaps) { + gl.generateMipmap(gl.TEXTURE_2D); + } + } else { + console.error("No manual decoder for", int32ToFourCC(fourCC), "and no native support"); + return 0; + } + } + } + + return mipmapCount; + } + + /** + * Parses a DDS file from the given arrayBuffer and uploads it into the currently bound texture + * + * @param {WebGLRenderingContext} gl WebGL rendering context + * @param {WebGLCompressedTextureS3TC} ext WEBGL_compressed_texture_s3tc extension object + * @param {TypedArray} arrayBuffer Array Buffer containing the DDS files data + * @param {boolean} [loadMipmaps] If false only the top mipmap level will be loaded, otherwise all available mipmaps will be uploaded + * + * @returns {number} Number of mipmaps uploaded, 0 if there was an error + */ + function getDDSLevels( arrayBuffer, compressed_not_supported ) + { + var header = new Int32Array(arrayBuffer, 0, headerLengthInt), + fourCC, blockBytes, internalFormat, + width, height, dataLength, dataOffset, is_cubemap, + rgb565Data, byteArray, mipmapCount, i, face; + + if(header[off_magic] != DDS_MAGIC) { + console.error("Invalid magic number in DDS header"); + return 0; + } + + if(!header[off_pfFlags] & DDPF_FOURCC) { + console.error("Unsupported format, must contain a FourCC code"); + return 0; + } + + fourCC = header[off_pfFourCC]; + switch(fourCC) { + case FOURCC_DXT1: + blockBytes = 8; + internalFormat = "COMPRESSED_RGB_S3TC_DXT1_EXT"; + break; + + case FOURCC_DXT3: + blockBytes = 16; + internalFormat = "COMPRESSED_RGBA_S3TC_DXT3_EXT"; + break; + + case FOURCC_DXT5: + blockBytes = 16; + internalFormat = "COMPRESSED_RGBA_S3TC_DXT5_EXT"; + break; + + default: + blockBytes = 4; + internalFormat = "RGBA"; + //console.error("Unsupported FourCC code:", int32ToFourCC(fourCC), fourCC); + //return null; + } + + mipmapCount = 1; + if(header[off_flags] & DDSD_MIPMAPCOUNT && loadMipmaps !== false) { + mipmapCount = Math.max(1, header[off_mipmapCount]); + } + + width = header[off_width]; + height = header[off_height]; + dataOffset = header[off_size] + 4; + is_cubemap = !!(header[off_caps+1] & DDSCAPS2_CUBEMAP); + + var buffers = []; + + if(is_cubemap) + { + for(var face = 0; face < 6; ++face) + { + width = header[off_width]; + height = header[off_height]; + for(var i = 0; i < mipmapCount; ++i) + { + if(fourCC) + { + dataLength = Math.max( 4, width )/4 * Math.max( 4, height )/4 * blockBytes; + byteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength); + buffers.push({ tex: "TEXTURE_CUBE_MAP", face: face, mipmap: i, internalFormat: internalFormat, width: width, height: height, offset: 0, dataOffset: dataOffset, dataLength: dataLength }); + } + else + { + dataLength = width * height * blockBytes; + byteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength); + BGRtoRGB(byteArray); + buffers.push({ tex: "TEXTURE_CUBE_MAP", face: face, mipmap: i, internalFormat: internalFormat, width: width, height: height, offset: 0, type: "UNSIGNED_BYTE", dataOffset: dataOffset, dataLength: dataLength }); + } + dataOffset += dataLength; + width *= 0.5; + height *= 0.5; + } + } + } + else //2d texture + { + if(!compressed_not_supported) + { + for(var i = 0; i < mipmapCount; ++i) { + dataLength = Math.max( 4, width )/4 * Math.max( 4, height )/4 * blockBytes; + byteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength); + //gl.compressedTexImage2D(gl.TEXTURE_2D, i, internalFormat, width, height, 0, byteArray); + buffers.push({ tex: "TEXTURE_2D", mipmap: i, internalFormat: internalFormat, width: width, height: height, offset: 0, type: "UNSIGNED_BYTE", dataOffset: dataOffset, dataLength: dataLength }); + dataOffset += dataLength; + width *= 0.5; + height *= 0.5; + } + } else { + if(fourCC == FOURCC_DXT1) + { + dataLength = Math.max( 4, width )/4 * Math.max( 4, height )/4 * blockBytes; + byteArray = new Uint16Array(arrayBuffer); + rgb565Data = dxtToRgb565(byteArray, dataOffset / 2, width, height); + //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, width, height, 0, gl.RGB, gl.UNSIGNED_SHORT_5_6_5, rgb565Data); + buffers.push({ tex: "TEXTURE_2D", mipmap: 0, internalFormat: "RGB", width: width, height: height, offset: 0, format:"RGB", type: "UNSIGNED_SHORT_5_6_5", data: rgb565Data }); + } else { + console.error("No manual decoder for", int32ToFourCC(fourCC), "and no native support"); + return 0; + } + } + } + + return buffers; + } + + /** + * Creates a texture from the DDS file at the given URL. Simple shortcut for the most common use case + * + * @param {WebGLRenderingContext} gl WebGL rendering context + * @param {WebGLCompressedTextureS3TC} ext WEBGL_compressed_texture_s3tc extension object + * @param {string} src URL to DDS file to be loaded + * @param {function} [callback] callback to be fired when the texture has finished loading + * + * @returns {WebGLTexture} New texture that will receive the DDS image data + */ + function loadDDSTextureEx(gl, ext, src, texture, loadMipmaps, callback) { + var xhr = new XMLHttpRequest(); + + xhr.open('GET', src, true); + xhr.responseType = "arraybuffer"; + xhr.onload = function() { + if(this.status == 200) { + var header = new Int32Array(this.response, 0, headerLengthInt) + var is_cubemap = !!(header[off_caps+1] & DDSCAPS2_CUBEMAP); + var tex_type = is_cubemap ? gl.TEXTURE_CUBE_MAP : gl.TEXTURE_2D; + gl.bindTexture(tex_type, texture); + var mipmaps = uploadDDSLevels(gl, ext, this.response, loadMipmaps); + gl.texParameteri(tex_type, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(tex_type, gl.TEXTURE_MIN_FILTER, mipmaps > 1 ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR); + gl.bindTexture(tex_type, null); + texture.texture_type = tex_type; + texture.width = header[off_width]; + texture.height = header[off_height]; + } + + if(callback) { + callback(texture); + } + }; + xhr.send(null); + + return texture; + } + + /** + * Creates a texture from the DDS file at the given ArrayBuffer. + * + * @param {WebGLRenderingContext} gl WebGL rendering context + * @param {WebGLCompressedTextureS3TC} ext WEBGL_compressed_texture_s3tc extension object + * @param {ArrayBuffer} data containing the DDS file + * @param {Texture} texture from GL.Texture + * @returns {WebGLTexture} New texture that will receive the DDS image data + */ + function loadDDSTextureFromMemoryEx(gl, ext, data, texture, loadMipmaps) { + var header = new Int32Array(data, 0, headerLengthInt) + var is_cubemap = !!(header[off_caps+1] & DDSCAPS2_CUBEMAP); + var tex_type = is_cubemap ? gl.TEXTURE_CUBE_MAP : gl.TEXTURE_2D; + + var handler = texture.handler || texture; + + gl.bindTexture(tex_type, texture.handler); + + //upload data + var mipmaps = uploadDDSLevels(gl, ext, data, loadMipmaps); + + gl.texParameteri(tex_type, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(tex_type, gl.TEXTURE_MIN_FILTER, mipmaps > 1 ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR); + if(is_cubemap) + { + gl.texParameteri(tex_type, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE ); + gl.texParameteri(tex_type, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE ); + } + + gl.bindTexture(tex_type, null); //unbind + if(texture.handler) + { + texture.texture_type = tex_type; + texture.width = header[off_width]; + texture.height = header[off_height]; + } + return texture; + } + + /** + * Extracts the texture info from a DDS file at the given ArrayBuffer. + * + * @param {ArrayBuffer} data containing the DDS file + * + * @returns {Object} contains mipmaps and properties + */ + function getDDSTextureFromMemoryEx(data) { + var header = new Int32Array(data, 0, headerLengthInt) + var is_cubemap = !!(header[off_caps+1] & DDSCAPS2_CUBEMAP); + var tex_type = is_cubemap ? "TEXTURE_CUBE_MAP" : "TEXTURE_2D"; + var buffers = getDDSLevels(data); + + var texture = { + type: tex_type, + buffers: buffers, + data: data, + width: header[off_width], + height: header[off_height] + }; + + return texture; + } + + /** + * Creates a texture from the DDS file at the given URL. Simple shortcut for the most common use case + * + * @param {WebGLRenderingContext} gl WebGL rendering context + * @param {WebGLCompressedTextureS3TC} ext WEBGL_compressed_texture_s3tc extension object + * @param {string} src URL to DDS file to be loaded + * @param {function} [callback] callback to be fired when the texture has finished loading + * + * @returns {WebGLTexture} New texture that will receive the DDS image data + */ + function loadDDSTexture(gl, ext, src, callback) { + var texture = gl.createTexture(); + var ext = gl.getExtension("WEBGL_compressed_texture_s3tc"); + loadDDSTextureEx(gl, ext, src, texture, true, callback); + return texture; + } + + return { + dxtToRgb565: dxtToRgb565, + uploadDDSLevels: uploadDDSLevels, + loadDDSTextureEx: loadDDSTextureEx, + loadDDSTexture: loadDDSTexture, + loadDDSTextureFromMemoryEx: loadDDSTextureFromMemoryEx, + getDDSTextureFromMemoryEx: getDDSTextureFromMemoryEx + }; + +})(); + +if(typeof(global) != "undefined") + global.DDS = DDS; + +/* this file adds some extra functions to gl-matrix library */ +if(typeof(glMatrix) == "undefined") + throw("You must include glMatrix on your project"); + +Math.clamp = function(v,a,b) { return (a > v ? a : (b < v ? b : v)); } + +var V3 = vec3.create; +var M4 = vec3.create; + + +vec3.ZERO = vec3.fromValues(0,0,0); +vec3.FRONT = vec3.fromValues(0,0,-1); +vec3.UP = vec3.fromValues(0,1,0); +vec3.RIGHT = vec3.fromValues(1,0,0); + +vec2.rotate = function(out,vec,angle_in_rad) +{ + var x = vec[0], y = vec[1]; + var cos = Math.cos(angle_in_rad); + var sin = Math.sin(angle_in_rad); + out[0] = x * cos - y * sin; + out[1] = x * sin + y * cos; + return out; +} + +vec3.zero = function(a) +{ + a[0] = a[1] = 0.0; + return a; +} + +//for signed angles +vec2.perpdot = function(a,b) +{ + return a[1] * b[0] + -a[0] * b[1]; +} + +vec2.computeSignedAngle = function( a, b ) +{ + return Math.atan2( vec2.perpdot(a,b), vec2.dot(a,b) ); +} + +vec2.random = function( vec, scale ) +{ + scale = scale || 1.0; + vec[0] = Math.random() * scale; + vec[1] = Math.random() * scale; + return vec; +} + +vec3.zero = function(a) +{ + a[0] = a[1] = a[2] = 0.0; + return a; +} + +vec3.minValue = function(a) +{ + if(a[0] < a[1] && a[0] < a[2]) return a[0]; + if(a[1] < a[2]) return a[1]; + return a[2]; +} + +vec3.maxValue = function(a) +{ + if(a[0] > a[1] && a[0] > a[2]) return a[0]; + if(a[1] > a[2]) return a[1]; + return a[2]; +} + +vec3.minValue = function(a) +{ + if(a[0] < a[1] && a[0] < a[2]) return a[0]; + if(a[1] < a[2]) return a[1]; + return a[2]; +} + +vec3.addValue = function(out,a,v) +{ + out[0] = a[0] + v; + out[1] = a[1] + v; + out[2] = a[2] + v; +} + +vec3.subValue = function(out,a,v) +{ + out[0] = a[0] - v; + out[1] = a[1] - v; + out[2] = a[2] - v; +} + +vec3.toArray = function(vec) +{ + return [vec[0],vec[1],vec[2]]; +} + +vec3.rotateX = function(out,vec,angle_in_rad) +{ + var y = vec[1], z = vec[2]; + var cos = Math.cos(angle_in_rad); + var sin = Math.sin(angle_in_rad); + + out[0] = vec[0]; + out[1] = y * cos - z * sin; + out[2] = y * sin + z * cos; + return out; +} + +vec3.rotateY = function(out,vec,angle_in_rad) +{ + var x = vec[0], z = vec[2]; + var cos = Math.cos(angle_in_rad); + var sin = Math.sin(angle_in_rad); + + out[0] = x * cos - z * sin; + out[1] = vec[1]; + out[2] = x * sin + z * cos; + return out; +} + +vec3.rotateZ = function(out,vec,angle_in_rad) +{ + var x = vec[0], y = vec[1]; + var cos = Math.cos(angle_in_rad); + var sin = Math.sin(angle_in_rad); + + out[0] = x * cos - y * sin; + out[1] = x * sin + y * cos; + out[2] = vec[2]; + return out; +} + +vec3.angle = function( a, b ) +{ + return Math.acos( vec3.dot(a,b) ); +} + +vec3.signedAngle = function(from, to, axis) +{ + var unsignedAngle = vec3.angle( from, to ); + var cross_x = from[1] * to[2] - from[2] * to[1]; + var cross_y = from[2] * to[0] - from[0] * to[2]; + var cross_z = from[0] * to[1] - from[1] * to[0]; + var sign = Math.sign(axis[0] * cross_x + axis[1] * cross_y + axis[2] * cross_z); + return unsignedAngle * sign; +} + +vec3.random = function(vec, scale) +{ + scale = scale || 1.0; + vec[0] = Math.random() * scale; + vec[1] = Math.random() * scale; + vec[2] = Math.random() * scale; + return vec; +} + +//converts a polar coordinate (radius, lat, long) to (x,y,z) +vec3.polarToCartesian = function(out, v) +{ + var r = v[0]; + var lat = v[1]; + var lon = v[2]; + out[0] = r * Math.cos(lat) * Math.sin(lon); + out[1] = r * Math.sin(lat); + out[2] = r * Math.cos(lat) * Math.cos(lon); + return out; +} + +vec3.reflect = function(out, v, n) +{ + var x = v[0]; var y = v[1]; var z = v[2]; + vec3.scale( out, n, -2 * vec3.dot(v,n) ); + out[0] += x; + out[1] += y; + out[2] += z; + return out; +} + +/* VEC4 */ +vec4.random = function(vec, scale) +{ + scale = scale || 1.0; + vec[0] = Math.random() * scale; + vec[1] = Math.random() * scale; + vec[2] = Math.random() * scale; + vec[3] = Math.random() * scale; + return vec; +} + +vec4.toArray = function(vec) +{ + return [vec[0],vec[1],vec[2],vec[3]]; +} + + +/** MATRIX ********************/ +mat3.IDENTITY = mat3.create(); +mat4.IDENTITY = mat4.create(); + +mat4.toArray = function(mat) +{ + return [mat[0],mat[1],mat[2],mat[3],mat[4],mat[5],mat[6],mat[7],mat[8],mat[9],mat[10],mat[11],mat[12],mat[13],mat[14],mat[15]]; +} + +mat4.setUpAndOrthonormalize = function(out, m, up) +{ + if(m != out) + mat4.copy(out,m); + var right = out.subarray(0,3); + vec3.normalize(out.subarray(4,7),up); + var front = out.subarray(8,11); + vec3.cross( right, up, front ); + vec3.normalize( right, right ); + vec3.cross( front, right, up ); + vec3.normalize( front, front ); +} + +mat4.multiplyVec3 = function(out, m, a) { + var x = a[0], y = a[1], z = a[2]; + out[0] = m[0] * x + m[4] * y + m[8] * z + m[12]; + out[1] = m[1] * x + m[5] * y + m[9] * z + m[13]; + out[2] = m[2] * x + m[6] * y + m[10] * z + m[14]; + return out; +}; + +//from https://github.com/hughsk/from-3d-to-2d/blob/master/index.js +//m should be a projection matrix (or a VP or MVP) +//projects vector from 3D to 2D and returns the value in normalized screen space +mat4.projectVec3 = function(out, m, a) +{ + var ix = a[0]; + var iy = a[1]; + var iz = a[2]; + + var ox = m[0] * ix + m[4] * iy + m[8] * iz + m[12]; + var oy = m[1] * ix + m[5] * iy + m[9] * iz + m[13]; + var oz = m[2] * ix + m[6] * iy + m[10] * iz + m[14]; + var ow = m[3] * ix + m[7] * iy + m[11] * iz + m[15]; + + out[0] = (ox / ow + 1) / 2; + out[1] = (oy / ow + 1) / 2; + out[2] = (oz / ow + 1) / 2; + return out; +}; + + +//from https://github.com/hughsk/from-3d-to-2d/blob/master/index.js +vec3.project = function(out, vec, mvp, viewport) { + viewport = viewport || gl.viewport_data; + + var m = mvp; + + var ix = vec[0]; + var iy = vec[1]; + var iz = vec[2]; + + var ox = m[0] * ix + m[4] * iy + m[8] * iz + m[12]; + var oy = m[1] * ix + m[5] * iy + m[9] * iz + m[13]; + var oz = m[2] * ix + m[6] * iy + m[10] * iz + m[14]; + var ow = m[3] * ix + m[7] * iy + m[11] * iz + m[15]; + + var projx = (ox / ow + 1) / 2; + var projy = 1 - (oy / ow + 1) / 2; + var projz = (oz / ow + 1) / 2; + + out[0] = projx * viewport[2] + viewport[0]; + out[1] = projy * viewport[3] + viewport[1]; + out[2] = projz; //ow + return out; +}; + +var unprojectMat = mat4.create(); +var unprojectVec = vec4.create(); + +vec3.unproject = function (out, vec, viewprojection, viewport) { + + var m = unprojectMat; + var v = unprojectVec; + + v[0] = (vec[0] - viewport[0]) * 2.0 / viewport[2] - 1.0; + v[1] = (vec[1] - viewport[1]) * 2.0 / viewport[3] - 1.0; + v[2] = 2.0 * vec[2] - 1.0; + v[3] = 1.0; + + if(!mat4.invert(m,viewprojection)) + return null; + + vec4.transformMat4(v, v, m); + if(v[3] === 0.0) + return null; + + out[0] = v[0] / v[3]; + out[1] = v[1] / v[3]; + out[2] = v[2] / v[3]; + + return out; +}; + +//without translation +mat4.rotateVec3 = function(out, m, a) { + var x = a[0], y = a[1], z = a[2]; + out[0] = m[0] * x + m[4] * y + m[8] * z; + out[1] = m[1] * x + m[5] * y + m[9] * z; + out[2] = m[2] * x + m[6] * y + m[10] * z; + return out; +}; + +mat4.fromTranslationFrontTop = function (out, pos, front, top) +{ + vec3.cross(out.subarray(0,3), front, top); + out.set(top,4); + out.set(front,8); + out.set(pos,12); + return out; +} + + +mat4.translationMatrix = function (v) +{ + var out = mat4.create(); + out[12] = v[0]; + out[13] = v[1]; + out[14] = v[2]; + return out; +} + +mat4.setTranslation = function (out, v) +{ + out[12] = v[0]; + out[13] = v[1]; + out[14] = v[2]; + return out; +} + + +mat4.getTranslation = function (out, matrix) +{ + out[0] = matrix[12]; + out[1] = matrix[13]; + out[2] = matrix[14]; + return out; +} + +//returns the matrix without rotation +mat4.toRotationMat4 = function (out, mat) { + mat4.copy(out,mat); + out[12] = out[13] = out[14] = 0.0; + return out; +}; + +mat4.swapRows = function(out, mat, row, row2) +{ + if(out != mat) + { + mat4.copy(out, mat); + out[4*row] = mat[4*row2]; + out[4*row+1] = mat[4*row2+1]; + out[4*row+2] = mat[4*row2+2]; + out[4*row+3] = mat[4*row2+3]; + out[4*row2] = mat[4*row]; + out[4*row2+1] = mat[4*row+1]; + out[4*row2+2] = mat[4*row+2]; + out[4*row2+3] = mat[4*row+3]; + return out; + } + + var temp = new Float32Array(matrix.subarray(row*4,row*5)); + matrix.set( matrix.subarray(row2*4,row2*5), row*4 ); + matrix.set( temp, row2*4 ); + return out; +} + +//used in skinning +mat4.scaleAndAdd = function(out, mat, mat2, v) +{ + out[0] = mat[0] + mat2[0] * v; out[1] = mat[1] + mat2[1] * v; out[2] = mat[2] + mat2[2] * v; out[3] = mat[3] + mat2[3] * v; + out[4] = mat[4] + mat2[4] * v; out[5] = mat[5] + mat2[5] * v; out[6] = mat[6] + mat2[6] * v; out[7] = mat[7] + mat2[7] * v; + out[8] = mat[8] + mat2[8] * v; out[9] = mat[9] + mat2[9] * v; out[10] = mat[10] + mat2[10] * v; out[11] = mat[11] + mat2[11] * v; + out[12] = mat[12] + mat2[12] * v; out[13] = mat[13] + mat2[13] * v; out[14] = mat[14] + mat2[14] * v; out[15] = mat[15] + mat2[15] * v; + return out; +} + +quat.fromAxisAngle = function(axis, rad) +{ + var out = quat.create(); + rad = rad * 0.5; + var s = Math.sin(rad); + out[0] = s * axis[0]; + out[1] = s * axis[1]; + out[2] = s * axis[2]; + out[3] = Math.cos(rad); + return out; +} + +//from https://answers.unity.com/questions/467614/what-is-the-source-code-of-quaternionlookrotation.html +quat.lookRotation = (function(){ + var vector = vec3.create(); + var vector2 = vec3.create(); + var vector3 = vec3.create(); + + return function( q, front, up ) + { + vec3.normalize(vector,front); + vec3.cross( vector2, up, vector ); + vec3.normalize(vector2,vector2); + vec3.cross( vector3, vector, vector2 ); + + var m00 = vector2[0]; + var m01 = vector2[1]; + var m02 = vector2[2]; + var m10 = vector3[0]; + var m11 = vector3[1]; + var m12 = vector3[2]; + var m20 = vector[0]; + var m21 = vector[1]; + var m22 = vector[2]; + + var num8 = (m00 + m11) + m22; + + if (num8 > 0) + { + var num = Math.sqrt(num8 + 1); + q[3] = num * 0.5; + num = 0.5 / num; + q[0] = (m12 - m21) * num; + q[1] = (m20 - m02) * num; + q[2] = (m01 - m10) * num; + return q; + } + if ((m00 >= m11) && (m00 >= m22)) + { + var num7 = Math.sqrt(((1 + m00) - m11) - m22); + var num4 = 0.5 / num7; + q[0] = 0.5 * num7; + q[1] = (m01 + m10) * num4; + q[2] = (m02 + m20) * num4; + q[3] = (m12 - m21) * num4; + return q; + } + if (m11 > m22) + { + var num6 = Math.sqrt(((1 + m11) - m00) - m22); + var num3 = 0.5 / num6; + q[0] = (m10+ m01) * num3; + q[1] = 0.5 * num6; + q[2] = (m21 + m12) * num3; + q[3] = (m20 - m02) * num3; + return q; + } + var num5 = Math.sqrt(((1 + m22) - m00) - m11); + var num2 = 0.5 / num5; + q[0] = (m20 + m02) * num2; + q[1] = (m21 + m12) * num2; + q[2] = 0.5 * num5; + q[3] = (m01 - m10) * num2; + return q; + }; +})(); + +/* +quat.toEuler = function(out, quat) { + var q = quat; + var heading, attitude, bank; + + if( (q[0]*q[1] + q[2]*q[3]) == 0.5 ) + { + heading = 2 * Math.atan2(q[0],q[3]); + bank = 0; + attitude = 0; //¿? + } + else if( (q[0]*q[1] + q[2]*q[3]) == 0.5 ) + { + heading = -2 * Math.atan2(q[0],q[3]); + bank = 0; + attitude = 0; //¿? + } + else + { + heading = Math.atan2( 2*(q[1]*q[3] - q[0]*q[2]) , 1 - 2 * (q[1]*q[1] - q[2]*q[2]) ); + attitude = Math.asin( 2*(q[0]*q[1] - q[2]*q[3]) ); + bank = Math.atan2( 2*(q[0]*q[3] - q[1]*q[2]), 1 - 2*(q[0]*q[0] - q[2]*q[2]) ); + } + + if(!out) + out = vec3.create(); + vec3.set(out, heading, attitude, bank); + return out; +} +*/ + +/* +//FROM https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles +//doesnt work well +quat.toEuler = function(out, q) +{ + var yaw = Math.atan2(2*q[0]*q[3] + 2*q[1]*q[2], 1 - 2*q[2]*q[2] - 2*q[3]*q[3]); + var pitch = Math.asin(2*q[0]*q[2] - 2*q[3]*q[1]); + var roll = Math.atan2(2*q[0]*q[1] + 2*q[2]*q[3], 1 - 2*q[1]*q[1] - 2*q[2]*q[2]); + if(!out) + out = vec3.create(); + vec3.set(out, yaw, pitch, roll); + return out; +} + +quat.fromEuler = function(out, vec) { + var yaw = vec[0]; + var pitch = vec[1]; + var roll = vec[2]; + + var C1 = Math.cos(yaw*0.5); + var C2 = Math.cos(pitch*0.5); + var C3 = Math.cos(roll*0.5); + var S1 = Math.sin(yaw*0.5); + var S2 = Math.sin(pitch*0.5); + var S3 = Math.sin(roll*0.5); + + var x = C1*C2*C3 + S1*S2*S3; + var y = S1*C2*C3 - C1*S2*S3; + var z = C1*S2*C3 + S1*C2*S3; + var w = C1*C2*S3 - S1*S2*C3; + + quat.set(out, x,y,z,w ); + quat.normalize(out,out); //necessary? + return out; +} +*/ + +quat.toEuler = function(out, q) +{ + var heading = Math.atan2(2*q[1]*q[3] - 2*q[0]*q[2], 1 - 2*q[1]*q[1] - 2*q[2]*q[2]); + var attitude = Math.asin(2*q[0]*q[1] + 2*q[2]*q[3]); + var bank = Math.atan2(2*q[0]*q[3] - 2*q[1]*q[2], 1 - 2*q[0]*q[0] - 2*q[2]*q[2]); + if(!out) + out = vec3.create(); + vec3.set(out, heading, attitude, bank); + return out; +} + +quat.fromEuler = function(out, vec) { + var heading = vec[0]; + var attitude = vec[1]; + var bank = vec[2]; + + var C1 = Math.cos(heading); //yaw + var C2 = Math.cos(attitude); //pitch + var C3 = Math.cos(bank); //roll + var S1 = Math.sin(heading); + var S2 = Math.sin(attitude); + var S3 = Math.sin(bank); + + var w = Math.sqrt(1.0 + C1 * C2 + C1*C3 - S1 * S2 * S3 + C2*C3) * 0.5; + if(w == 0.0) + { + w = 0.000001; + //quat.set(out, 0,0,0,1 ); + //return out; + } + + var x = (C2 * S3 + C1 * S3 + S1 * S2 * C3) / (4.0 * w); + var y = (S1 * C2 + S1 * C3 + C1 * S2 * S3) / (4.0 * w); + var z = (-S1 * S3 + C1 * S2 * C3 + S2) /(4.0 * w); + quat.set(out, x,y,z,w ); + quat.normalize(out,out); + return out; +}; + + +//not tested +quat.fromMat4 = function(out,m) +{ + var trace = m[0] + m[5] + m[10]; + if ( trace > 0.0 ) + { + var s = Math.sqrt( trace + 1.0 ); + out[3] = s * 0.5;//w + var recip = 0.5 / s; + out[0] = ( m[9] - m[6] ) * recip; //2,1 1,2 + out[1] = ( m[2] - m[8] ) * recip; //0,2 2,0 + out[2] = ( m[4] - m[1] ) * recip; //1,0 0,1 + } + else + { + var i = 0; + if( m[5] > m[0] ) + i = 1; + if( m[10] > m[i*4+i] ) + i = 2; + var j = ( i + 1 ) % 3; + var k = ( j + 1 ) % 3; + var s = Math.sqrt( m[i*4+i] - m[j*4+j] - m[k*4+k] + 1.0 ); + out[i] = 0.5 * s; + var recip = 0.5 / s; + out[3] = ( m[k*4+j] - m[j*4+k] ) * recip;//w + out[j] = ( m[j*4+i] + m[i*4+j] ) * recip; + out[k] = ( m[k*4+i] + m[i*4+k] ) * recip; + } + quat.normalize(out,out); +} + +//col according to common matrix notation, here are stored as rows +vec3.getMat3Column = function(out, m, index ) +{ + out[0] = m[index*3]; + out[1] = m[index*3 + 1]; + out[2] = m[index*3 + 2]; + return out; +} + +mat3.setColumn = function(out, v, index ) +{ + out[index*3] = v[0]; + out[index*3+1] = v[1]; + out[index*3+2] = v[2]; + return out; +} + + +//http://matthias-mueller-fischer.ch/publications/stablePolarDecomp.pdf +//reusing the previous quaternion as an indicator to keep perpendicularity +quat.fromMat3AndQuat = (function(){ + var temp_mat3 = mat3.create(); + var temp_quat = quat.create(); + var Rcol0 = vec3.create(); + var Rcol1 = vec3.create(); + var Rcol2 = vec3.create(); + var Acol0 = vec3.create(); + var Acol1 = vec3.create(); + var Acol2 = vec3.create(); + var RAcross0 = vec3.create(); + var RAcross1 = vec3.create(); + var RAcross2 = vec3.create(); + var omega = vec3.create(); + var axis = mat3.create(); + + return function( q, A, max_iter ) + { + max_iter = max_iter || 25; + for (var iter = 0; iter < max_iter; ++iter) + { + var R = mat3.fromQuat( temp_mat3, q ); + vec3.getMat3Column(Rcol0,R,0); + vec3.getMat3Column(Rcol1,R,1); + vec3.getMat3Column(Rcol2,R,2); + vec3.getMat3Column(Acol0,A,0); + vec3.getMat3Column(Acol1,A,1); + vec3.getMat3Column(Acol2,A,2); + vec3.cross( RAcross0, Rcol0, Acol0 ); + vec3.cross( RAcross1, Rcol1, Acol1 ); + vec3.cross( RAcross2, Rcol2, Acol2 ); + vec3.add( omega, RAcross0, RAcross1 ); + vec3.add( omega, omega, RAcross2 ); + var d = 1.0 / Math.abs( vec3.dot(Rcol0,Acol0) + vec3.dot(Rcol1,Acol1) + vec3.dot(Rcol2,Acol2) ) + 1.0e-9; + vec3.scale( omega, omega, d ); + var w = vec3.length(omega); + if (w < 1.0e-9) + break; + vec3.scale(omega,omega,1/w); //normalize + quat.setAxisAngle( temp_quat, omega, w ); + quat.mul( q, temp_quat, q ); + quat.normalize(q,q); + } + return q; + }; +})(); + +//http://number-none.com/product/IK%20with%20Quaternion%20Joint%20Limits/ +quat.rotateToFrom = (function(){ + var tmp = vec3.create(); + return function(out, v1, v2) + { + out = out || quat.create(); + var axis = vec3.cross(tmp, v1, v2); + var dot = vec3.dot(v1, v2); + if( dot < -1 + 0.01){ + out[0] = 0; + out[1] = 1; + out[2] = 0; + out[3] = 0; + return out; + } + out[0] = axis[0] * 0.5; + out[1] = axis[1] * 0.5; + out[2] = axis[2] * 0.5; + out[3] = (1 + dot) * 0.5; + quat.normalize(out, out); + return out; + } +})(); + +quat.lookAt = (function(){ + var axis = vec3.create(); + + return function( out, forwardVector, up ) + { + var dot = vec3.dot( vec3.FRONT, forwardVector ); + + if ( Math.abs( dot - (-1.0)) < 0.000001 ) + { + out.set( vec3.UP ); + out[3] = Math.PI; + return out; + } + if ( Math.abs(dot - 1.0) < 0.000001 ) + { + return quat.identity( out ); + } + + var rotAngle = Math.acos( dot ); + vec3.cross( axis, vec3.FRONT, forwardVector ); + vec3.normalize( axis, axis ); + quat.setAxisAngle( out, axis, rotAngle ); + return out; + } +})(); + + + + +/** +* @namespace GL +*/ + +/** +* Indexer used to reuse vertices among a mesh +* @class Indexer +* @constructor +*/ +GL.Indexer = function Indexer() { + this.unique = []; + this.indices = []; + this.map = {}; +} +GL.Indexer.prototype = { + add: function(obj) { + var key = JSON.stringify(obj); + if (!(key in this.map)) { + this.map[key] = this.unique.length; + this.unique.push(obj); + } + return this.map[key]; + } +}; + +/** +* A data buffer to be stored in the GPU +* @class Buffer +* @constructor +* @param {Number} target gl.ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER +* @param {ArrayBufferView} data the data in typed-array format +* @param {number} spacing number of numbers per component (3 per vertex, 2 per uvs...), default 3 +* @param {enum} stream_type default gl.STATIC_DRAW (other: gl.DYNAMIC_DRAW, gl.STREAM_DRAW +*/ +GL.Buffer = function Buffer( target, data, spacing, stream_type, gl ) { + if(GL.debug) + console.log("GL.Buffer created"); + + if(gl !== null) + gl = gl || global.gl; + this.gl = gl; + + this.buffer = null; //webgl buffer + this.target = target; //GL.ARRAY_BUFFER, GL.ELEMENT_ARRAY_BUFFER + this.attribute = null; //name of the attribute in the shader ("a_vertex","a_normal","a_coord",...) + + //optional + this.data = data; + this.spacing = spacing || 3; + + if(this.data && this.gl) + this.upload(stream_type); +} + +/** +* binds the buffer to a attrib location +* @method bind +* @param {number} location the location of the shader (from shader.attributes[ name ]) +*/ +GL.Buffer.prototype.bind = function( location, gl ) +{ + gl = gl || this.gl; + + gl.bindBuffer( gl.ARRAY_BUFFER, this.buffer ); + gl.enableVertexAttribArray( location ); + gl.vertexAttribPointer( location, this.spacing, this.buffer.gl_type, false, 0, 0); +} + +/** +* unbinds the buffer from an attrib location +* @method unbind +* @param {number} location the location of the shader +*/ +GL.Buffer.prototype.unbind = function( location, gl ) +{ + gl = gl || this.gl; + gl.disableVertexAttribArray( location ); +} + +/** +* Applies an action to every vertex in this buffer +* @method forEach +* @param {function} callback to be called for every vertex (or whatever is contained in the buffer) +*/ +GL.Buffer.prototype.forEach = function(callback) +{ + var d = this.data; + for (var i = 0, s = this.spacing, l = d.length; i < l; i += s) + { + callback(d.subarray(i,i+s),i); + } + return this; //to concatenate +} + +/** +* Applies a mat4 transform to every triplets in the buffer (assuming they are points) +* No upload is performed (to ensure efficiency in case there are several operations performed) +* @method applyTransform +* @param {mat4} mat +*/ +GL.Buffer.prototype.applyTransform = function(mat) +{ + var d = this.data; + for (var i = 0, s = this.spacing, l = d.length; i < l; i += s) + { + var v = d.subarray(i,i+s); + vec3.transformMat4(v,v,mat); + } + return this; //to concatenate +} + +/** +* Uploads the buffer data (stored in this.data) to the GPU +* @method upload +* @param {number} stream_type default gl.STATIC_DRAW (other: gl.DYNAMIC_DRAW, gl.STREAM_DRAW +*/ +GL.Buffer.prototype.upload = function( stream_type ) { //default gl.STATIC_DRAW (other: gl.DYNAMIC_DRAW, gl.STREAM_DRAW ) + var spacing = this.spacing || 3; //default spacing + var gl = this.gl; + if(!gl) + return; + + if(!this.data) + throw("No data supplied"); + + var data = this.data; + if(!data.buffer) + throw("Buffers must be typed arrays"); + + //I store some stuff inside the WebGL buffer instance, it is supported + this.buffer = this.buffer || gl.createBuffer(); + if(!this.buffer) + return; //if the context is lost... + + this.buffer.length = data.length; + this.buffer.spacing = spacing; + + //store the data format + switch( data.constructor ) + { + case Int8Array: this.buffer.gl_type = gl.BYTE; break; + case Uint8ClampedArray: + case Uint8Array: this.buffer.gl_type = gl.UNSIGNED_BYTE; break; + case Int16Array: this.buffer.gl_type = gl.SHORT; break; + case Uint16Array: this.buffer.gl_type = gl.UNSIGNED_SHORT; break; + case Int32Array: this.buffer.gl_type = gl.INT; break; + case Uint32Array: this.buffer.gl_type = gl.UNSIGNED_INT; break; + case Float32Array: this.buffer.gl_type = gl.FLOAT; break; + default: throw("unsupported buffer type"); + } + + if(this.target == gl.ARRAY_BUFFER && ( this.buffer.gl_type == gl.INT || this.buffer.gl_type == gl.UNSIGNED_INT )) + { + console.warn("WebGL does not support UINT32 or INT32 as vertex buffer types, converting to FLOAT"); + this.buffer.gl_type = gl.FLOAT; + data = new Float32Array(data); + } + + gl.bindBuffer(this.target, this.buffer); + gl.bufferData(this.target, data , stream_type || this.stream_type || gl.STATIC_DRAW); +}; +//legacy +GL.Buffer.prototype.compile = GL.Buffer.prototype.upload; + + +/** +* Assign data to buffer and uploads it (it allows range) +* @method setData +* @param {ArrayBufferView} data in Float32Array format usually +* @param {number} offset offset in bytes +*/ +GL.Buffer.prototype.setData = function( data, offset ) +{ + if(!data.buffer) + throw("Data must be typed array"); + offset = offset || 0; + + if(!this.data) + { + this.data = data; + this.upload(); + return; + } + else if( this.data.length < data.length ) + throw("buffer is not big enough, you cannot set data to a smaller buffer"); + + if(this.data != data) + { + if(this.data.length == data.length) + { + this.data.set( data ); + this.upload(); + return; + } + + //upload just part of it + var new_data_view = new Uint8Array( data.buffer, data.buffer.byteOffset, data.buffer.byteLength ); + var data_view = new Uint8Array( this.data.buffer ); + data_view.set( new_data_view, offset ); + this.uploadRange( offset, new_data_view.length ); + } + +}; + + +/** +* Uploads part of the buffer data (stored in this.data) to the GPU +* @method uploadRange +* @param {number} start offset in bytes +* @param {number} size sizes in bytes +*/ +GL.Buffer.prototype.uploadRange = function(start, size) +{ + if(!this.data) + throw("No data stored in this buffer"); + + var data = this.data; + if(!data.buffer) + throw("Buffers must be typed arrays"); + + //cut fragment to upload (no way to avoid GC here, no function to specify the size in WebGL 1.0, but there is one in WebGL 2.0) + var view = new Uint8Array( this.data.buffer, start, size ); + + var gl = this.gl; + gl.bindBuffer(this.target, this.buffer); + gl.bufferSubData(this.target, start, view ); +}; + +/** +* Clones one buffer (it allows to share the same data between both buffers) +* @method clone +* @param {boolean} share if you want that both buffers share the same data (default false) +* return {GL.Buffer} buffer cloned +*/ +GL.Buffer.prototype.clone = function(share) +{ + var buffer = new GL.Buffer(); + if(share) + { + for(var i in this) + buffer[i] = this[i]; + } + else + { + if(this.target) + buffer.target = this.target; + if(this.gl) + buffer.gl = this.gl; + if(this.spacing) + buffer.spacing = this.spacing; + if(this.data) //clone data + { + buffer.data = new global[ this.data.constructor ]( this.data ); + buffer.upload(); + } + } + return buffer; +} + + +GL.Buffer.prototype.toJSON = function() +{ + if(!this.data) + { + console.error("cannot serialize a mesh without data"); + return null; + } + + return { + data_type: getClassName(this.data), + data: this.data.toJSON(), + target: this.target, + attribute: this.attribute, + spacing: this.spacing + }; +} + +GL.Buffer.prototype.fromJSON = function(o) +{ + var data_type = global[ o.data_type ] || Float32Array; + this.data = new data_type( o.data ); //cloned + this.target = o.target; + this.spacing = o.spacing || 3; + this.attribute = o.attribute; + this.upload( GL.STATIC_DRAW ); +} + +/** +* Deletes the content from the GPU and destroys the handler +* @method delete +*/ +GL.Buffer.prototype.delete = function() +{ + var gl = this.gl; + gl.deleteBuffer( this.buffer ); + this.buffer = null; +} + +/** +* Base class for meshes, it wraps several buffers and some global info like the bounding box +* @class Mesh +* @param {Object} vertexBuffers object with all the vertex streams +* @param {Object} indexBuffers object with all the indices streams +* @param {Object} options +* @param {WebGLContext} gl [Optional] gl context where to create the mesh +* @constructor +*/ +global.Mesh = GL.Mesh = function Mesh( vertexbuffers, indexbuffers, options, gl ) +{ + if(GL.debug) + console.log("GL.Mesh created"); + + if( gl !== null ) + { + gl = gl || global.gl; + this.gl = gl; + } + + //used to avoid problems with resources moving between different webgl context + this._context_id = gl.context_id; + + this.vertexBuffers = {}; + this.indexBuffers = {}; + + //here you can store extra info, like groups, which is an array of { name, start, length, material } + this.info = { + groups: [] + }; + this._bounding = BBox.create(); //here you can store a AABB in BBox format + + if(vertexbuffers || indexbuffers) + this.addBuffers( vertexbuffers, indexbuffers, options ? options.stream_type : null ); + + if(options) + for(var i in options) + this[i] = options[i]; +}; + +Mesh.common_buffers = { + "vertices": { spacing:3, attribute: "a_vertex"}, + "vertices2D": { spacing:2, attribute: "a_vertex2D"}, + "normals": { spacing:3, attribute: "a_normal"}, + "coords": { spacing:2, attribute: "a_coord"}, + "coords1": { spacing:2, attribute: "a_coord1"}, + "coords2": { spacing:2, attribute: "a_coord2"}, + "colors": { spacing:4, attribute: "a_color"}, + "tangents": { spacing:3, attribute: "a_tangent"}, + "bone_indices": { spacing:4, attribute: "a_bone_indices", type: Uint8Array }, + "weights": { spacing:4, attribute: "a_weights"}, + "extra": { spacing:1, attribute: "a_extra"}, + "extra2": { spacing:2, attribute: "a_extra2"}, + "extra3": { spacing:3, attribute: "a_extra3"}, + "extra4": { spacing:4, attribute: "a_extra4"} +}; + +Mesh.default_datatype = Float32Array; + +Object.defineProperty( Mesh.prototype, "bounding", { + set: function(v) + { + if(!v) + return; + if(v.length < 13) + throw("Bounding must use the BBox bounding format of 13 floats: center, halfsize, min, max, radius"); + this._bounding.set(v); + }, + get: function() + { + return this._bounding; + } +}); + +/** +* Adds buffer to mesh +* @method addBuffer +* @param {string} name +* @param {Buffer} buffer +*/ + +Mesh.prototype.addBuffer = function(name, buffer) +{ + if(buffer.target == gl.ARRAY_BUFFER) + this.vertexBuffers[name] = buffer; + else + this.indexBuffers[name] = buffer; + + if(!buffer.attribute) + { + var info = GL.Mesh.common_buffers[name]; + if(info) + buffer.attribute = info.attribute; + } +} + + +/** +* Adds vertex and indices buffers to a mesh +* @method addBuffers +* @param {Object} vertexBuffers object with all the vertex streams +* @param {Object} indexBuffers object with all the indices streams +* @param {enum} stream_type default gl.STATIC_DRAW (other: gl.DYNAMIC_DRAW, gl.STREAM_DRAW ) +*/ +Mesh.prototype.addBuffers = function( vertexbuffers, indexbuffers, stream_type ) +{ + var num_vertices = 0; + + if(this.vertexBuffers["vertices"]) + num_vertices = this.vertexBuffers["vertices"].data.length / 3; + + for(var i in vertexbuffers) + { + var data = vertexbuffers[i]; + if(!data) + continue; + + if( data.constructor == GL.Buffer ) + { + data = data.data; + } + else if( typeof(data[0]) != "number") //linearize: (transform Arrays in typed arrays) + { + var newdata = []; + for (var j = 0, chunk = 10000; j < data.length; j += chunk) { + newdata = Array.prototype.concat.apply(newdata, data.slice(j, j + chunk)); + } + data = newdata; + } + + var stream_info = GL.Mesh.common_buffers[i]; + + //cast to typed float32 if no type is specified + if(data.constructor === Array) + { + var datatype = GL.Mesh.default_datatype; + if(stream_info && stream_info.type) + datatype = stream_info.type; + data = new datatype( data ); + } + + //compute spacing + if(i == "vertices") + num_vertices = data.length / 3; + var spacing = data.length / num_vertices; + if(stream_info && stream_info.spacing) + spacing = stream_info.spacing; + + //add and upload + var attribute = "a_" + i; + if(stream_info && stream_info.attribute) + attribute = stream_info.attribute; + + if( this.vertexBuffers[i] ) + this.updateVertexBuffer( i, attribute, spacing, data, stream_type ); + else + this.createVertexBuffer( i, attribute, spacing, data, stream_type ); + } + + if(indexbuffers) + for(var i in indexbuffers) + { + var data = indexbuffers[i]; + if(!data) continue; + + if( data.constructor == GL.Buffer ) + { + data = data.data; + } + if( typeof(data[0]) != "number") //linearize + { + newdata = []; + for (var i = 0, chunk = 10000; i < data.length; i += chunk) { + newdata = Array.prototype.concat.apply(newdata, data.slice(i, i + chunk)); + } + data = newdata; + } + + //cast to typed + if(data.constructor === Array) + { + var datatype = Uint16Array; + if(num_vertices > 256*256) + datatype = Uint32Array; + data = new datatype( data ); + } + + this.createIndexBuffer( i, data ); + } +} + +/** +* Creates a new empty buffer and attachs it to this mesh +* @method createVertexBuffer +* @param {String} name "vertices","normals"... +* @param {String} attribute name of the stream in the shader "a_vertex","a_normal",... [optional, if omitted is used the common_buffers] +* @param {number} spacing components per vertex [optional, if ommited is used the common_buffers, if not found then uses 3 ] +* @param {ArrayBufferView} buffer_data the data in typed array format [optional, if ommited it created an empty array of getNumVertices() * spacing] +* @param {enum} stream_type [optional, default = gl.STATIC_DRAW (other: gl.DYNAMIC_DRAW, gl.STREAM_DRAW ) ] +*/ + +Mesh.prototype.createVertexBuffer = function( name, attribute, buffer_spacing, buffer_data, stream_type ) { + + var common = GL.Mesh.common_buffers[name]; //generic info about a buffer with the same name + + if (!attribute && common) + attribute = common.attribute; + + if (!attribute) + throw("Buffer added to mesh without attribute name"); + + if (!buffer_spacing && common) + { + if(common && common.spacing) + buffer_spacing = common.spacing; + else + buffer_spacing = 3; + } + + if(!buffer_data) + { + var num = this.getNumVertices(); + if(!num) + throw("Cannot create an empty buffer in a mesh without vertices (vertices are needed to know the size)"); + buffer_data = new (GL.Mesh.default_datatype)(num * buffer_spacing); + } + + if(!buffer_data.buffer) + throw("Buffer data MUST be typed array"); + + //used to ensure the buffers are held in the same gl context as the mesh + var buffer = this.vertexBuffers[name] = new GL.Buffer( gl.ARRAY_BUFFER, buffer_data, buffer_spacing, stream_type, this.gl ); + buffer.name = name; + buffer.attribute = attribute; + + return buffer; +} + +/** +* Updates a vertex buffer +* @method updateVertexBuffer +* @param {String} name the name of the buffer +* @param {String} attribute the name of the attribute in the shader +* @param {number} spacing number of numbers per component (3 per vertex, 2 per uvs...), default 3 +* @param {*} data the array with all the data +* @param {enum} stream_type default gl.STATIC_DRAW (other: gl.DYNAMIC_DRAW, gl.STREAM_DRAW +*/ +Mesh.prototype.updateVertexBuffer = function( name, attribute, buffer_spacing, buffer_data, stream_type ) { + var buffer = this.vertexBuffers[name]; + if(!buffer) + { + console.log("buffer not found: ",name); + return; + } + + if(!buffer_data.length) + return; + + buffer.attribute = attribute; + buffer.spacing = buffer_spacing; + buffer.data = buffer_data; + buffer.upload( stream_type ); +} + + +/** +* Removes a vertex buffer from the mesh +* @method removeVertexBuffer +* @param {String} name "vertices","normals"... +* @param {Boolean} free if you want to remove the data from the GPU +*/ +Mesh.prototype.removeVertexBuffer = function(name, free) { + var buffer = this.vertexBuffers[name]; + if(!buffer) + return; + if(free) + buffer.delete(); + delete this.vertexBuffers[name]; +} + +/** +* Returns a vertex buffer +* @method getVertexBuffer +* @param {String} name of vertex buffer +* @return {Buffer} the buffer +*/ +Mesh.prototype.getVertexBuffer = function(name) +{ + return this.vertexBuffers[name]; +} + + +/** +* Creates a new empty index buffer and attachs it to this mesh +* @method createIndexBuffer +* @param {String} name +* @param {Typed array} data +* @param {enum} stream_type gl.STATIC_DRAW, gl.DYNAMIC_DRAW, gl.STREAM_DRAW +*/ +Mesh.prototype.createIndexBuffer = function(name, buffer_data, stream_type) { + //(target, data, spacing, stream_type, gl) + + //cast to typed + if(buffer_data.constructor === Array) + { + var datatype = Uint16Array; + var vertices = this.vertexBuffers["vertices"]; + if(vertices) + { + var num_vertices = vertices.data.length / 3; + if(num_vertices > 256*256) + datatype = Uint32Array; + buffer_data = new datatype( buffer_data ); + } + } + + var buffer = this.indexBuffers[name] = new GL.Buffer(gl.ELEMENT_ARRAY_BUFFER, buffer_data, 0, stream_type, this.gl ); + return buffer; +} + +/** +* Returns a vertex buffer +* @method getBuffer +* @param {String} name of vertex buffer +* @return {Buffer} the buffer +*/ +Mesh.prototype.getBuffer = function(name) +{ + return this.vertexBuffers[name]; +} + +/** +* Returns a index buffer +* @method getIndexBuffer +* @param {String} name of index buffer +* @return {Buffer} the buffer +*/ +Mesh.prototype.getIndexBuffer = function(name) +{ + return this.indexBuffers[name]; +} + +/** +* Removes an index buffer from the mesh +* @method removeIndexBuffer +* @param {String} name "vertices","normals"... +* @param {Boolean} free if you want to remove the data from the GPU +*/ +Mesh.prototype.removeIndexBuffer = function(name, free) { + var buffer = this.indexBuffers[name]; + if(!buffer) + return; + if(free) + buffer.delete(); + delete this.indexBuffers[name]; +} + + +/** +* Uploads data inside buffers to VRAM. +* @method upload +* @param {number} buffer_type gl.STATIC_DRAW, gl.DYNAMIC_DRAW, gl.STREAM_DRAW +*/ +Mesh.prototype.upload = function(buffer_type) { + for (var attribute in this.vertexBuffers) { + var buffer = this.vertexBuffers[attribute]; + //buffer.data = this[buffer.name]; + buffer.upload(buffer_type); + } + + for (var name in this.indexBuffers) { + var buffer = this.indexBuffers[name]; + //buffer.data = this[name]; + buffer.upload(); + } +} + +//LEGACY, plz remove +Mesh.prototype.compile = Mesh.prototype.upload; + + +Mesh.prototype.deleteBuffers = function() +{ + for(var i in this.vertexBuffers) + { + var buffer = this.vertexBuffers[i]; + buffer.delete(); + } + this.vertexBuffers = {}; + + for(var i in this.indexBuffers) + { + var buffer = this.indexBuffers[i]; + buffer.delete(); + } + this.indexBuffers = {}; +} + +Mesh.prototype.delete = Mesh.prototype.deleteBuffers; + +Mesh.prototype.bindBuffers = function( shader ) +{ + // enable attributes as necessary. + for (var name in this.vertexBuffers) + { + var buffer = this.vertexBuffers[ name ]; + var attribute = buffer.attribute || name; + var location = shader.attributes[ attribute ]; + if (location == null || !buffer.buffer) + continue; + gl.bindBuffer(gl.ARRAY_BUFFER, buffer.buffer); + gl.enableVertexAttribArray(location); + gl.vertexAttribPointer(location, buffer.buffer.spacing, buffer.buffer.gl_type, false, 0, 0); + } +} + +Mesh.prototype.unbindBuffers = function( shader ) +{ + // disable attributes + for (var name in this.vertexBuffers) + { + var buffer = this.vertexBuffers[ name ]; + var attribute = buffer.attribute || name; + var location = shader.attributes[ attribute ]; + if (location == null || !buffer.buffer) + continue; //ignore this buffer + gl.disableVertexAttribArray( shader.attributes[attribute] ); + } +} + +/** +* Creates a clone of the mesh, the datarrays are cloned too +* @method clone +*/ +Mesh.prototype.clone = function( gl ) +{ + var gl = gl || global.gl; + var vbs = {}; + var ibs = {}; + + for(var i in this.vertexBuffers) + { + var b = this.vertexBuffers[i]; + vbs[i] = new b.data.constructor( b.data ); //clone + } + for(var i in this.indexBuffers) + { + var b = this.indexBuffers[i]; + ibs[i] = new b.data.constructor( b.data ); //clone + } + + return new GL.Mesh( vbs, ibs, undefined, gl ); +} + +/** +* Creates a clone of the mesh, but the data-arrays are shared between both meshes (useful for sharing a mesh between contexts) +* @method clone +*/ +Mesh.prototype.cloneShared = function( gl ) +{ + var gl = gl || global.gl; + return new GL.Mesh( this.vertexBuffers, this.indexBuffers, undefined, gl ); +} + + +/** +* Creates an object with the info of the mesh (useful to transfer to workers) +* @method toObject +*/ +Mesh.prototype.toObject = function() +{ + var vbs = {}; + var ibs = {}; + + for(var i in this.vertexBuffers) + { + var b = this.vertexBuffers[i]; + vbs[i] = { + spacing: b.spacing, + data: new b.data.constructor( b.data ) //clone + }; + } + for(var i in this.indexBuffers) + { + var b = this.indexBuffers[i]; + ibs[i] = { + data: new b.data.constructor( b.data ) //clone + } + } + + return { + vertexBuffers: vbs, + indexBuffers: ibs, + info: this.info ? cloneObject( this.info ) : null, + bounding: this._bounding.toJSON() + }; +} + + +Mesh.prototype.toJSON = function() +{ + var r = { + vertexBuffers: {}, + indexBuffers: {}, + info: this.info ? cloneObject( this.info ) : null, + bounding: this._bounding.toJSON() + }; + + for(var i in this.vertexBuffers) + r.vertexBuffers[i] = this.vertexBuffers[i].toJSON(); + + for(var i in this.indexBuffers) + r.indexBuffers[i] = this.indexBuffers[i].toJSON(); + + return r; +} + +Mesh.prototype.fromJSON = function(o) +{ + this.vertexBuffers = {}; + this.indexBuffers = {}; + + for(var i in o.vertexBuffers) + { + if(!o.vertexBuffers[i]) + continue; + var buffer = new GL.Buffer(); + buffer.fromJSON( o.vertexBuffers[i] ); + if(!buffer.attribute && GL.Mesh.common_buffers[i]) + buffer.attribute = GL.Mesh.common_buffers[i].attribute; + this.vertexBuffers[i] = buffer; + } + + for(var i in o.indexBuffers) + { + if(!o.indexBuffers[i]) + continue; + var buffer = new GL.Buffer(); + buffer.fromJSON( o.indexBuffers[i] ); + this.indexBuffers[i] = buffer; + } + + if(o.info) + this.info = cloneObject( o.info ); + if(o.bounding) + this.bounding = o.bounding; //setter does the job +} + + +/** +* Computes some data about the mesh +* @method generateMetadata +*/ +Mesh.prototype.generateMetadata = function() +{ + var metadata = {}; + + var vertices = this.vertexBuffers["vertices"].data; + var triangles = this.indexBuffers["triangles"].data; + + metadata.vertices = vertices.length / 3; + if(triangles) + metadata.faces = triangles.length / 3; + else + metadata.faces = vertices.length / 9; + + metadata.indexed = !!this.metadata.faces; + this.metadata = metadata; +} + +//never tested +/* +Mesh.prototype.draw = function(shader, mode, range_start, range_length) +{ + if(range_length == 0) return; + + // Create and enable attribute pointers as necessary. + var length = 0; + for (var attribute in this.vertexBuffers) { + var buffer = this.vertexBuffers[attribute]; + var location = shader.attributes[attribute] || + gl.getAttribLocation(shader.program, attribute); + if (location == -1 || !buffer.buffer) continue; + shader.attributes[attribute] = location; + gl.bindBuffer(gl.ARRAY_BUFFER, buffer.buffer); + gl.enableVertexAttribArray(location); + gl.vertexAttribPointer(location, buffer.buffer.spacing, gl.FLOAT, false, 0, 0); + length = buffer.buffer.length / buffer.buffer.spacing; + } + + //range rendering + var offset = 0; + if(arguments.length > 3) //render a polygon range + offset = range_start * (this.indexBuffer ? this.indexBuffer.constructor.BYTES_PER_ELEMENT : 1); //in bytes (Uint16 == 2 bytes) + + if(arguments.length > 4) + length = range_length; + else if (this.indexBuffer) + length = this.indexBuffer.buffer.length - offset; + + // Disable unused attribute pointers. + for (var attribute in shader.attributes) { + if (!(attribute in this.vertexBuffers)) { + gl.disableVertexAttribArray(shader.attributes[attribute]); + } + } + + // Draw the geometry. + if (length && (!this.indexBuffer || indexBuffer.buffer)) { + if (this.indexBuffer) { + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer.buffer); + gl.drawElements(mode, length, gl.UNSIGNED_SHORT, offset); + } else { + gl.drawArrays(mode, offset, length); + } + } + + return this; +} +*/ + +/** +* Creates a new index stream with wireframe +* @method computeWireframe +*/ +Mesh.prototype.computeWireframe = function() { + var index_buffer = this.indexBuffers["triangles"]; + + var vertices = this.vertexBuffers["vertices"].data; + var num_vertices = (vertices.length/3); + + if(!index_buffer) //unindexed + { + var num_triangles = num_vertices / 3; + var buffer = num_vertices > 256*256 ? new Uint32Array( num_triangles * 6 ) : new Uint16Array( num_triangles * 6 ); + for(var i = 0; i < num_vertices; i += 3) + { + buffer[i*2] = i; + buffer[i*2+1] = i+1; + buffer[i*2+2] = i+1; + buffer[i*2+3] = i+2; + buffer[i*2+4] = i+2; + buffer[i*2+5] = i; + } + + } + else //indexed + { + var data = index_buffer.data; + + var indexer = new GL.Indexer(); + for (var i = 0; i < data.length; i+=3) { + var t = data.subarray(i,i+3); + for (var j = 0; j < t.length; j++) { + var a = t[j], b = t[(j + 1) % t.length]; + indexer.add([Math.min(a, b), Math.max(a, b)]); + } + } + + //linearize + var unique = indexer.unique; + var buffer = num_vertices > 256*256 ? new Uint32Array( unique.length * 2 ) : new Uint16Array( unique.length * 2 ); + for(var i = 0, l = unique.length; i < l; ++i) + buffer.set(unique[i],i*2); + } + + //create stream + this.createIndexBuffer('wireframe', buffer); + return this; +} + + +/** +* Multiplies every normal by -1 and uploads it +* @method flipNormals +* @param {enum} stream_type default gl.STATIC_DRAW (other: gl.DYNAMIC_DRAW, gl.STREAM_DRAW) +*/ +Mesh.prototype.flipNormals = function( stream_type ) { + var normals_buffer = this.vertexBuffers["normals"]; + if(!normals_buffer) + return; + var data = normals_buffer.data; + var l = data.length; + for(var i = 0; i < l; ++i) + data[i] *= -1; + normals_buffer.upload( stream_type ); + + //reverse indices too + if( !this.indexBuffers["triangles"] ) + this.computeIndices(); //create indices + + var triangles_buffer = this.indexBuffers["triangles"]; + var data = triangles_buffer.data; + var l = data.length; + for(var i = 0; i < l; i += 3) + { + var tmp = data[i]; + data[i] = data[i+1]; + data[i+1] = tmp; + //the [i+2] stays the same + } + triangles_buffer.upload( stream_type ); +} + + +/** +* Compute indices for a mesh where vertices are shared +* @method computeIndices +*/ +Mesh.prototype.computeIndices = function() { + + //cluster by distance + var new_vertices = []; + var new_normals = []; + var new_coords = []; + + var indices = []; + + var old_vertices_buffer = this.vertexBuffers["vertices"]; + var old_normals_buffer = this.vertexBuffers["normals"]; + var old_coords_buffer = this.vertexBuffers["coords"]; + + var old_vertices_data = old_vertices_buffer.data; + + var old_normals_data = null; + if( old_normals_buffer ) + old_normals_data = old_normals_buffer.data; + + var old_coords_data = null; + if( old_coords_buffer ) + old_coords_data = old_coords_buffer.data; + + + var indexer = {}; + + var l = old_vertices_data.length / 3; + for(var i = 0; i < l; ++i) + { + var v = old_vertices_data.subarray( i*3,(i+1)*3 ); + var key = (v[0] * 1000)|0; + + //search in new_vertices + var j = 0; + var candidates = indexer[key]; + if(candidates) + { + var l2 = candidates.length; + for(; j < l2; j++) + { + var v2 = new_vertices[ candidates[j] ]; + //same vertex + if( vec3.sqrDist( v, v2 ) < 0.01 ) + { + indices.push(j); + break; + } + } + } + + /* + var l2 = new_vertices.length; + for(var j = 0; j < l2; j++) + { + //same vertex + if( vec3.sqrDist( v, new_vertices[j] ) < 0.001 ) + { + indices.push(j); + break; + } + } + */ + + if(candidates && j != l2) + continue; + + var index = j; + new_vertices.push(v); + if( indexer[ key ] ) + indexer[ key ].push( index ); + else + indexer[ key ] = [ index ]; + + if(old_normals_data) + new_normals.push( old_normals_data.subarray(i*3, (i+1)*3) ); + if(old_coords_data) + new_coords.push( old_coords_data.subarray(i*2, (i+1)*2) ); + indices.push(index); + } + + this.vertexBuffers = {}; //erase all + + //new buffers + this.createVertexBuffer( 'vertices', GL.Mesh.common_buffers["vertices"].attribute, 3, linearizeArray( new_vertices ) ); + if(old_normals_data) + this.createVertexBuffer( 'normals', GL.Mesh.common_buffers["normals"].attribute, 3, linearizeArray( new_normals ) ); + if(old_coords_data) + this.createVertexBuffer( 'coords', GL.Mesh.common_buffers["coords"].attribute, 2, linearizeArray( new_coords ) ); + + this.createIndexBuffer( "triangles", indices ); +} + +/** +* Breaks the indices +* @method explodeIndices +*/ +Mesh.prototype.explodeIndices = function( buffer_name ) { + + buffer_name = buffer_name || "triangles"; + + var indices_buffer = this.getIndexBuffer( buffer_name ); + if(!indices_buffer) + return; + + var indices = indices_buffer.data; + + var new_buffers = {}; + for(var i in this.vertexBuffers) + { + var info = GL.Mesh.common_buffers[i]; + new_buffers[i] = new (info.type || Float32Array)( info.spacing * indices.length ); + } + + for(var i = 0, l = indices.length; i < l; ++i) + { + var index = indices[i]; + for(var j in this.vertexBuffers) + { + var buffer = this.vertexBuffers[j]; + var info = GL.Mesh.common_buffers[j]; + var spacing = buffer.spacing || info.spacing; + var new_buffer = new_buffers[j]; + new_buffer.set( buffer.data.subarray( index*spacing, index*spacing + spacing ), i*spacing ); + } + } + + /* + //cluster by distance + var new_vertices = new Float32Array(indices.length * 3); + var new_normals = null; + var new_coords = null; + + var old_vertices_buffer = this.vertexBuffers["vertices"]; + var old_vertices = old_vertices_buffer.data; + + var old_normals_buffer = this.vertexBuffers["normals"]; + var old_normals = null; + if(old_normals_buffer) + { + old_normals = old_normals_buffer.data; + new_normals = new Float32Array(indices.length * 3); + } + + var old_coords_buffer = this.vertexBuffers["coords"]; + var old_coords = null; + if( old_coords_buffer ) + { + old_coords = old_coords_buffer.data; + new_coords = new Float32Array(indices.length * 2); + } + + for(var i = 0, l = indices.length; i < l; ++i) + { + var index = indices[i]; + new_vertices.set( old_vertices.subarray( index*3, index*3 + 3 ), i*3 ); + if(old_normals) + new_normals.set( old_normals.subarray( index*3, index*3 + 3 ), i*3 ); + if(old_coords) + new_coords.set( old_coords.subarray( index*2, index*2 + 2 ), i*2 ); + } + + //erase all + this.vertexBuffers = {}; + + //new buffers + this.createVertexBuffer( 'vertices', GL.Mesh.common_buffers["vertices"].attribute, 3, new_vertices ); + if(new_normals) + this.createVertexBuffer( 'normals', GL.Mesh.common_buffers["normals"].attribute, 3, new_normals ); + if(new_coords) + this.createVertexBuffer( 'coords', GL.Mesh.common_buffers["coords"].attribute, 2, new_coords ); + */ + + for(var i in new_buffers) + { + var old = this.vertexBuffers[i]; + this.createVertexBuffer( i, old.attribute, old.spacing, new_buffers[i] ); + } + + delete this.indexBuffers[ buffer_name ]; +} + + + +/** +* Creates a stream with the normals +* @method computeNormals +* @param {enum} stream_type default gl.STATIC_DRAW (other: gl.DYNAMIC_DRAW, gl.STREAM_DRAW) +*/ +Mesh.prototype.computeNormals = function( stream_type ) { + var vertices_buffer = this.vertexBuffers["vertices"]; + if(!vertices_buffer) + return console.error("Cannot compute normals of a mesh without vertices"); + + var vertices = this.vertexBuffers["vertices"].data; + var num_vertices = vertices.length / 3; + + //create because it is faster than filling it with zeros + var normals = new Float32Array( vertices.length ); + + var triangles = null; + if(this.indexBuffers["triangles"]) + triangles = this.indexBuffers["triangles"].data; + + var temp = GL.temp_vec3; + var temp2 = GL.temp2_vec3; + + var i1,i2,i3,v1,v2,v3,n1,n2,n3; + + //compute the plane normal + var l = triangles ? triangles.length : vertices.length; + for (var a = 0; a < l; a+=3) + { + if(triangles) + { + i1 = triangles[a]; + i2 = triangles[a+1]; + i3 = triangles[a+2]; + + v1 = vertices.subarray(i1*3,i1*3+3); + v2 = vertices.subarray(i2*3,i2*3+3); + v3 = vertices.subarray(i3*3,i3*3+3); + + n1 = normals.subarray(i1*3,i1*3+3); + n2 = normals.subarray(i2*3,i2*3+3); + n3 = normals.subarray(i3*3,i3*3+3); + } + else + { + v1 = vertices.subarray(a*3,a*3+3); + v2 = vertices.subarray(a*3+3,a*3+6); + v3 = vertices.subarray(a*3+6,a*3+9); + + n1 = normals.subarray(a*3,a*3+3); + n2 = normals.subarray(a*3+3,a*3+6); + n3 = normals.subarray(a*3+6,a*3+9); + } + + vec3.sub( temp, v2, v1 ); + vec3.sub( temp2, v3, v1 ); + vec3.cross( temp, temp, temp2 ); + vec3.normalize(temp,temp); + + //save + vec3.add( n1, n1, temp ); + vec3.add( n2, n2, temp ); + vec3.add( n3, n3, temp ); + } + + //normalize if vertices are shared + if(triangles) + for (var a = 0, l = normals.length; a < l; a+=3) + { + var n = normals.subarray(a,a+3); + vec3.normalize(n,n); + } + + var normals_buffer = this.vertexBuffers["normals"]; + + if(normals_buffer) + { + normals_buffer.data = normals; + normals_buffer.upload( stream_type ); + } + else + return this.createVertexBuffer('normals', GL.Mesh.common_buffers["normals"].attribute, 3, normals ); + return normals_buffer; +} + + +/** +* Creates a new stream with the tangents +* @method computeTangents +*/ +Mesh.prototype.computeTangents = function() +{ + var vertices_buffer = this.vertexBuffers["vertices"]; + if(!vertices_buffer) + return console.error("Cannot compute tangents of a mesh without vertices"); + + var normals_buffer = this.vertexBuffers["normals"]; + if(!normals_buffer) + return console.error("Cannot compute tangents of a mesh without normals"); + + var uvs_buffer = this.vertexBuffers["coords"]; + if(!uvs_buffer) + return console.error("Cannot compute tangents of a mesh without uvs"); + + var triangles_buffer = this.indexBuffers["triangles"]; + if(!triangles_buffer) + return console.error("Cannot compute tangents of a mesh without indices"); + + var vertices = vertices_buffer.data; + var normals = normals_buffer.data; + var uvs = uvs_buffer.data; + var triangles = triangles_buffer.data; + + if(!vertices || !normals || !uvs) return; + + var num_vertices = vertices.length / 3; + + var tangents = new Float32Array(num_vertices * 4); + + //temporary (shared) + var tan1 = new Float32Array(num_vertices*3*2); + var tan2 = tan1.subarray(num_vertices*3); + + var a,l; + var sdir = vec3.create(); + var tdir = vec3.create(); + var temp = vec3.create(); + var temp2 = vec3.create(); + + for (a = 0, l = triangles.length; a < l; a+=3) + { + var i1 = triangles[a]; + var i2 = triangles[a+1]; + var i3 = triangles[a+2]; + + var v1 = vertices.subarray(i1*3,i1*3+3); + var v2 = vertices.subarray(i2*3,i2*3+3); + var v3 = vertices.subarray(i3*3,i3*3+3); + + var w1 = uvs.subarray(i1*2,i1*2+2); + var w2 = uvs.subarray(i2*2,i2*2+2); + var w3 = uvs.subarray(i3*2,i3*2+2); + + var x1 = v2[0] - v1[0]; + var x2 = v3[0] - v1[0]; + var y1 = v2[1] - v1[1]; + var y2 = v3[1] - v1[1]; + var z1 = v2[2] - v1[2]; + var z2 = v3[2] - v1[2]; + + var s1 = w2[0] - w1[0]; + var s2 = w3[0] - w1[0]; + var t1 = w2[1] - w1[1]; + var t2 = w3[1] - w1[1]; + + var r; + var den = (s1 * t2 - s2 * t1); + if ( Math.abs(den) < 0.000000001 ) + r = 0.0; + else + r = 1.0 / den; + + vec3.copy(sdir, [(t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r] ); + vec3.copy(tdir, [(s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r] ); + + vec3.add( tan1.subarray( i1*3, i1*3+3), tan1.subarray( i1*3, i1*3+3), sdir); + vec3.add( tan1.subarray( i2*3, i2*3+3), tan1.subarray( i2*3, i2*3+3), sdir); + vec3.add( tan1.subarray( i3*3, i3*3+3), tan1.subarray( i3*3, i3*3+3), sdir); + + vec3.add( tan2.subarray( i1*3, i1*3+3), tan2.subarray( i1*3, i1*3+3), tdir); + vec3.add( tan2.subarray( i2*3, i2*3+3), tan2.subarray( i2*3, i2*3+3), tdir); + vec3.add( tan2.subarray( i3*3, i3*3+3), tan2.subarray( i3*3, i3*3+3), tdir); + } + + for (a = 0, l = vertices.length; a < l; a+=3) + { + var n = normals.subarray(a,a+3); + var t = tan1.subarray(a,a+3); + + // Gram-Schmidt orthogonalize + vec3.subtract(temp, t, vec3.scale(temp, n, vec3.dot(n, t) ) ); + vec3.normalize(temp,temp); + + // Calculate handedness + var w = ( vec3.dot( vec3.cross(temp2, n, t), tan2.subarray(a,a+3) ) < 0.0) ? -1.0 : 1.0; + tangents.set([temp[0], temp[1], temp[2], w],(a/3)*4); + } + + this.createVertexBuffer('tangents', Mesh.common_buffers["tangents"].attribute, 4, tangents ); +} + +/** +* Creates texture coordinates using a triplanar aproximation +* @method computeTextureCoordinates +*/ +Mesh.prototype.computeTextureCoordinates = function( stream_type ) +{ + var vertices_buffer = this.vertexBuffers["vertices"]; + if(!vertices_buffer) + return console.error("Cannot compute uvs of a mesh without vertices"); + + this.explodeIndices( "triangles" ); + + vertices_buffer = this.vertexBuffers["vertices"]; + var vertices = vertices_buffer.data; + var num_vertices = vertices.length / 3; + + var uvs_buffer = this.vertexBuffers["coords"]; + var uvs = new Float32Array( num_vertices * 2 ); + + var triangles_buffer = this.indexBuffers["triangles"]; + var triangles = null; + if( triangles_buffer ) + triangles = triangles_buffer.data; + + var plane_normal = vec3.create(); + var side1 = vec3.create(); + var side2 = vec3.create(); + + var bbox = this.getBoundingBox(); + var bboxcenter = BBox.getCenter( bbox ); + var bboxhs = vec3.create(); + bboxhs.set( BBox.getHalfsize( bbox ) ); //careful, this is a reference + vec3.scale( bboxhs, bboxhs, 2 ); + + var num = triangles ? triangles.length : vertices.length/3; + + for (var a = 0; a < num; a+=3) + { + if(triangles) + { + var i1 = triangles[a]; + var i2 = triangles[a+1]; + var i3 = triangles[a+2]; + + var v1 = vertices.subarray(i1*3,i1*3+3); + var v2 = vertices.subarray(i2*3,i2*3+3); + var v3 = vertices.subarray(i3*3,i3*3+3); + + var uv1 = uvs.subarray(i1*2,i1*2+2); + var uv2 = uvs.subarray(i2*2,i2*2+2); + var uv3 = uvs.subarray(i3*2,i3*2+2); + } + else + { + var v1 = vertices.subarray((a)*3,(a)*3+3); + var v2 = vertices.subarray((a+1)*3,(a+1)*3+3); + var v3 = vertices.subarray((a+2)*3,(a+2)*3+3); + + var uv1 = uvs.subarray((a)*2,(a)*2+2); + var uv2 = uvs.subarray((a+1)*2,(a+1)*2+2); + var uv3 = uvs.subarray((a+2)*2,(a+2)*2+2); + } + + vec3.sub(side1, v1, v2 ); + vec3.sub(side2, v1, v3 ); + vec3.cross( plane_normal, side1, side2 ); + //vec3.normalize( plane_normal, plane_normal ); //not necessary + + plane_normal[0] = Math.abs( plane_normal[0] ); + plane_normal[1] = Math.abs( plane_normal[1] ); + plane_normal[2] = Math.abs( plane_normal[2] ); + + if( plane_normal[0] > plane_normal[1] && plane_normal[0] > plane_normal[2]) + { + //X + uv1[0] = (v1[2] - bboxcenter[2]) / bboxhs[2]; + uv1[1] = (v1[1] - bboxcenter[1]) / bboxhs[1]; + uv2[0] = (v2[2] - bboxcenter[2]) / bboxhs[2]; + uv2[1] = (v2[1] - bboxcenter[1]) / bboxhs[1]; + uv3[0] = (v3[2] - bboxcenter[2]) / bboxhs[2]; + uv3[1] = (v3[1] - bboxcenter[1]) / bboxhs[1]; + } + else if ( plane_normal[1] > plane_normal[2]) + { + //Y + uv1[0] = (v1[0] - bboxcenter[0]) / bboxhs[0]; + uv1[1] = (v1[2] - bboxcenter[2]) / bboxhs[2]; + uv2[0] = (v2[0] - bboxcenter[0]) / bboxhs[0]; + uv2[1] = (v2[2] - bboxcenter[2]) / bboxhs[2]; + uv3[0] = (v3[0] - bboxcenter[0]) / bboxhs[0]; + uv3[1] = (v3[2] - bboxcenter[2]) / bboxhs[2]; + } + else + { + //Z + uv1[0] = (v1[0] - bboxcenter[0]) / bboxhs[0]; + uv1[1] = (v1[1] - bboxcenter[1]) / bboxhs[1]; + uv2[0] = (v2[0] - bboxcenter[0]) / bboxhs[0]; + uv2[1] = (v2[1] - bboxcenter[1]) / bboxhs[1]; + uv3[0] = (v3[0] - bboxcenter[0]) / bboxhs[0]; + uv3[1] = (v3[1] - bboxcenter[1]) / bboxhs[1]; + } + } + + if(uvs_buffer) + { + uvs_buffer.data = uvs; + uvs_buffer.upload( stream_type ); + } + else + this.createVertexBuffer('coords', Mesh.common_buffers["coords"].attribute, 2, uvs ); +} + + +/** +* Computes the number of vertices +* @method getVertexNumber +*/ +Mesh.prototype.getNumVertices = function() { + var b = this.vertexBuffers["vertices"]; + if(!b) + return 0; + return b.data.length / b.spacing; +} + +/** +* Computes the number of triangles (takes into account indices) +* @method getNumTriangles +*/ +Mesh.prototype.getNumTriangles = function() { + var indices_buffer = this.getIndexBuffer("triangles"); + if(!indices_buffer) + return this.getNumVertices() / 3; + return indices_buffer.data.length / 3; +} + + +/** +* Computes bounding information +* @method Mesh.computeBoundingBox +* @param {typed Array} vertices array containing all the vertices +* @param {BBox} bb where to store the bounding box +* @param {Array} mask [optional] to specify which vertices must be considered when creating the bbox, used to create BBox of a submesh +*/ +Mesh.computeBoundingBox = function( vertices, bb, mask ) { + + if(!vertices) + return; + + var start = 0; + + if(mask) + { + for(var i = 0; i < mask.length; ++i) + if( mask[i] ) + { + start = i; + break; + } + if(start == mask.length) + { + console.warn("mask contains only zeros, no vertices marked"); + return; + } + } + + var min = vec3.clone( vertices.subarray( start*3, start*3 + 3) ); + var max = vec3.clone( vertices.subarray( start*3, start*3 + 3) ); + var v; + + for(var i = start*3; i < vertices.length; i+=3) + { + if( mask && !mask[i/3] ) + continue; + v = vertices.subarray(i,i+3); + vec3.min( min,v, min); + vec3.max( max,v, max); + } + + if( isNaN(min[0]) || isNaN(min[1]) || isNaN(min[2]) || + isNaN(max[0]) || isNaN(max[1]) || isNaN(max[2]) ) + { + min[0] = min[1] = min[2] = 0; + max[0] = max[1] = max[2] = 0; + console.warn("Warning: GL.Mesh has NaN values in vertices"); + } + + var center = vec3.add( vec3.create(), min,max ); + vec3.scale( center, center, 0.5); + var half_size = vec3.subtract( vec3.create(), max, center ); + + return BBox.setCenterHalfsize( bb || BBox.create(), center, half_size ); +} + +/** +* returns the bounding box, if it is not computed, then computes it +* @method getBoundingBox +* @return {BBox} bounding box +*/ +Mesh.prototype.getBoundingBox = function() +{ + if(this._bounding) + return this._bounding; + + this.updateBoundingBox(); + return this._bounding; +} + +/** +* Update bounding information of this mesh +* @method updateBoundingBox +*/ +Mesh.prototype.updateBoundingBox = function() { + var vertices = this.vertexBuffers["vertices"]; + if(!vertices) + return; + GL.Mesh.computeBoundingBox( vertices.data, this._bounding ); + if(this.info && this.info.groups && this.info.groups.length) + this.computeGroupsBoundingBoxes(); +} + +/** +* Update bounding information for every group submesh +* @method computeGroupsBoundingBoxes +*/ +Mesh.prototype.computeGroupsBoundingBoxes = function() +{ + var indices = null; + var indices_buffer = this.getIndexBuffer("triangles"); + if( indices_buffer ) + indices = indices_buffer.data; + + var vertices_buffer = this.getVertexBuffer("vertices"); + if(!vertices_buffer) + return false; + var vertices = vertices_buffer.data; + if(!vertices.length) + return false; + + var groups = this.info.groups; + for(var i = 0; i < groups.length; ++i) + { + var group = groups[i]; + group.bounding = group.bounding || BBox.create(); + var submesh_vertices = null; + if( indices ) + { + var mask = new Uint8Array( vertices.length / 3 ); + var s = group.start; + for( var j = 0, l = group.length; j < l; j += 3 ) + { + mask[ indices[s+j] ] = 1; + mask[ indices[s+j+1] ] = 1; + mask[ indices[s+j+2] ] = 1; + } + GL.Mesh.computeBoundingBox( vertices, group.bounding, mask ); + } + else + { + submesh_vertices = vertices.subarray( group.start * 3, ( group.start + group.length) * 3 ); + GL.Mesh.computeBoundingBox( submesh_vertices, group.bounding ); + } + } + return true; +} + + + +/** +* forces a bounding box to be set +* @method setBoundingBox +* @param {vec3} center center of the bounding box +* @param {vec3} half_size vector from the center to positive corner +*/ +Mesh.prototype.setBoundingBox = function( center, half_size ) { + BBox.setCenterHalfsize( this._bounding, center, half_size ); +} + + +/** +* Remove all local memory from the streams (leaving it only in the VRAM) to save RAM +* @method freeData +*/ +Mesh.prototype.freeData = function() +{ + for (var attribute in this.vertexBuffers) + { + this.vertexBuffers[attribute].data = null; + delete this[ this.vertexBuffers[attribute].name ]; //delete from the mesh itself + } + for (var name in this.indexBuffers) + { + this.indexBuffers[name].data = null; + delete this[ this.indexBuffers[name].name ]; //delete from the mesh itself + } +} + +Mesh.prototype.configure = function( o, options ) +{ + var vertex_buffers = {}; + var index_buffers = {}; + options = options || {}; + + for(var j in o) + { + if(!o[j]) + continue; + + if(j == "vertexBuffers" || j == "vertex_buffers") //HACK: legacy code + { + for(i in o[j]) + vertex_buffers[i] = o[j][i]; + continue; + } + + if(j == "indexBuffers" || j == "index_buffers") + { + for(i in o[j]) + index_buffers[i] = o[j][i]; + continue; + } + + if(j == "indices" || j == "lines" || j == "wireframe" || j == "triangles") + index_buffers[j] = o[j]; + else if( GL.Mesh.common_buffers[j]) + vertex_buffers[j] = o[j]; + else //global data like bounding, info of groups, etc + { + options[j] = o[j]; + } + } + + this.addBuffers( vertex_buffers, index_buffers, options.stream_type ); + + for(var i in options) + this[i] = options[i]; + + if(!options.bounding) + this.updateBoundingBox(); +} + +/** +* Returns the amount of memory used by this mesh in bytes (sum of all buffers) +* @method getMemory +* @return {number} bytes +*/ +Mesh.prototype.totalMemory = function() +{ + var num = 0|0; + + for (var name in this.vertexBuffers) + num += this.vertexBuffers[name].data.buffer.byteLength; + for (var name in this.indexBuffers) + num += this.indexBuffers[name].data.buffer.byteLength; + + return num; +} + +Mesh.prototype.slice = function(start, length) +{ + var new_vertex_buffers = {}; + + var indices_buffer = this.indexBuffers["triangles"]; + if(!indices_buffer) + { + console.warn("splice in not indexed not supported yet"); + return null; + } + + var indices = indices_buffer.data; + + var new_triangles = []; + var reindex = new Int32Array( indices.length ); + reindex.fill(-1); + + var end = start + length; + if(end >= indices.length) + end = indices.length; + + var last_index = 0; + for(var j = start; j < end; ++j) + { + var index = indices[j]; + if( reindex[index] != -1 ) + { + new_triangles.push(reindex[index]); + continue; + } + + //new vertex + var new_index = last_index++; + reindex[index] = new_index; + new_triangles.push(new_index); + + for( var i in this.vertexBuffers ) + { + var buffer = this.vertexBuffers[i]; + var data = buffer.data; + var spacing = buffer.spacing; + if(!new_vertex_buffers[i]) + new_vertex_buffers[i] = []; + var new_buffer = new_vertex_buffers[i]; + for(var k = 0; k < spacing; ++k) + new_buffer.push( data[k + index*spacing] ); + } + } + + var new_mesh = new GL.Mesh( new_vertex_buffers, {triangles: new_triangles}, null,gl); + new_mesh.updateBoundingBox(); + return new_mesh; +} + + +/** +* returns a low poly version of the mesh that takes much less memory (but breaks tiling of uvs and smoothing groups) +* @method simplify +* @return {Mesh} simplified mesh +*/ +Mesh.prototype.simplify = function() +{ + //compute bounding box + var bb = this.getBoundingBox(); + var min = BBox.getMin( bb ); + var halfsize = BBox.getHalfsize( bb ); + var range = vec3.scale( vec3.create(), halfsize, 2 ); + + var newmesh = new GL.Mesh(); + var temp = vec3.create(); + + for(var i in this.vertexBuffers) + { + //take every vertex and normalize it to the bounding box + var buffer = this.vertexBuffers[i]; + var data = buffer.data; + + var new_data = new Float32Array( data.length ); + + if(i == "vertices") + { + for(var j = 0, l = data.length; j < l; j+=3 ) + { + var v = data.subarray(j,j+3); + vec3.sub( temp, v, min ); + vec3.div( temp, temp, range ); + temp[0] = Math.round(temp[0] * 256) / 256; + temp[1] = Math.round(temp[1] * 256) / 256; + temp[2] = Math.round(temp[2] * 256) / 256; + vec3.mul( temp, temp, range ); + vec3.add( temp, temp, min ); + new_data.set( temp, j ); + } + } + else + { + } + + newmesh.addBuffer(); + } + + //search for repeated vertices + //compute the average normal and coord + //reindex the triangles + //return simplified mesh +} + +/** +* Static method for the class Mesh to create a mesh from a list of common streams +* @method Mesh.load +* @param {Object} buffers object will all the buffers +* @param {Object} options [optional] +* @param {Mesh} output_mesh [optional] mesh to store the mesh, otherwise is created +* @param {WebGLContext} gl [optional] if omitted, the global.gl is used +*/ +Mesh.load = function( buffers, options, output_mesh, gl ) { + options = options || {}; + if(options.no_gl) + gl = null; + var mesh = output_mesh || new GL.Mesh(null,null,null,gl); + mesh.configure( buffers, options ); + return mesh; +} + +/** +* Returns a mesh with all the meshes merged (you can apply transforms individually to every buffer) +* @method Mesh.mergeMeshes +* @param {Array} meshes array containing object like { mesh:, matrix:, texture_matrix: } +* @param {Object} options { only_data: to get the mesh data without uploading it } +* @return {GL.Mesh|Object} the mesh in GL.Mesh format or Object format (if options.only_data is true) +*/ +Mesh.mergeMeshes = function( meshes, options ) +{ + options = options || {}; + + var vertex_buffers = {}; + var index_buffers = {}; + var offsets = {}; //tells how many positions indices must be offseted + var vertex_offsets = []; + var current_vertex_offset = 0; + var groups = []; + + //vertex buffers + //compute size + for(var i = 0; i < meshes.length; ++i) + { + var mesh_info = meshes[i]; + var mesh = mesh_info.mesh; + var offset = current_vertex_offset; + vertex_offsets.push( offset ); + var length = mesh.vertexBuffers["vertices"].data.length / 3; + current_vertex_offset += length; + + for(var j in mesh.vertexBuffers) + { + if(!vertex_buffers[j]) + vertex_buffers[j] = mesh.vertexBuffers[j].data.length; + else + vertex_buffers[j] += mesh.vertexBuffers[j].data.length; + } + + for(var j in mesh.indexBuffers) + { + if(!index_buffers[j]) + index_buffers[j] = mesh.indexBuffers[j].data.length; + else + index_buffers[j] += mesh.indexBuffers[j].data.length; + } + + //groups + var group = { + name: "mesh_" + i, + start: offset, + length: length, + material: "" + }; + + groups.push( group ); + } + + //allocate + for(var j in vertex_buffers) + { + var datatype = options[j]; + if(datatype === null) + { + delete vertex_buffers[j]; + continue; + } + + if(!datatype) + datatype = Float32Array; + + vertex_buffers[j] = new datatype( vertex_buffers[j] ); + offsets[j] = 0; + } + + for(var j in index_buffers) + { + index_buffers[j] = new Uint32Array( index_buffers[j] ); + offsets[j] = 0; + } + + //store + for(var i = 0; i < meshes.length; ++i) + { + var mesh_info = meshes[i]; + var mesh = mesh_info.mesh; + var offset = 0; + var length = 0; + + for(var j in mesh.vertexBuffers) + { + if(!vertex_buffers[j]) + continue; + + if(j == "vertices") + length = mesh.vertexBuffers[j].data.length / 3; + + vertex_buffers[j].set( mesh.vertexBuffers[j].data, offsets[j] ); + + //apply transform + if(mesh_info[ j + "_matrix"] ) + { + var matrix = mesh_info[ j + "_matrix" ]; + if(matrix.length == 16) + apply_transform( vertex_buffers[j], offsets[j], mesh.vertexBuffers[j].data.length, matrix ) + else if(matrix.length == 9) + apply_transform2D( vertex_buffers[j], offsets[j], mesh.vertexBuffers[j].data.length, matrix ) + } + + offsets[j] += mesh.vertexBuffers[j].data.length; + } + + for(var j in mesh.indexBuffers) + { + index_buffers[j].set( mesh.indexBuffers[j].data, offsets[j] ); + apply_offset( index_buffers[j], offsets[j], mesh.indexBuffers[j].data.length, vertex_offsets[i] ); + offsets[j] += mesh.indexBuffers[j].data.length; + } + } + + //useful functions + function apply_transform( array, start, length, matrix ) + { + var l = start + length; + for(var i = start; i < l; i+=3) + { + var v = array.subarray(i,i+3); + vec3.transformMat4( v, v, matrix ); + } + } + + function apply_transform2D( array, start, length, matrix ) + { + var l = start + length; + for(var i = start; i < l; i+=2) + { + var v = array.subarray(i,i+2); + vec2.transformMat3( v, v, matrix ); + } + } + + function apply_offset( array, start, length, offset ) + { + var l = start + length; + for(var i = start; i < l; ++i) + array[i] += offset; + } + + var extra = { info: { groups: groups } }; + + //return + if( typeof(gl) != "undefined" || options.only_data ) + return new GL.Mesh( vertex_buffers,index_buffers, extra ); + return { + vertexBuffers: vertex_buffers, + indexBuffers: index_buffers, + info: { groups: groups } + }; +} + + + +//Here we store all basic mesh parsers (OBJ, STL) and encoders +Mesh.parsers = {}; +Mesh.encoders = {}; +Mesh.binary_file_formats = {}; //extensions that must be downloaded in binary +Mesh.compressors = {}; //used to compress binary meshes +Mesh.decompressors = {}; //used to decompress binary meshes + +/** +* Returns am empty mesh and loads a mesh and parses it using the Mesh.parsers, by default only OBJ is supported +* @method Mesh.fromOBJ +* @param {Array} meshes array containing all the meshes +*/ +Mesh.fromURL = function(url, on_complete, gl, options) +{ + options = options || {}; + gl = gl || global.gl; + + var mesh = new GL.Mesh(undefined,undefined,undefined,gl); + mesh.ready = false; + + var pos = url.lastIndexOf("."); + var extension = url.substr(pos+1).toLowerCase(); + options.binary = Mesh.binary_file_formats[ extension ]; + + HttpRequest( url, null, function(data) { + mesh.parse( data, extension ); + delete mesh["ready"]; + if(on_complete) + on_complete.call(mesh,mesh, url); + }, function(err){ + if(on_complete) + on_complete(null); + }, options ); + return mesh; +} + +/** +* given some data an information about the format, it search for a parser in Mesh.parsers and tries to extract the mesh information +* Only obj supported now +* @method parse +* @param {*} data could be string or ArrayBuffer +* @param {String} format parser file format name (p.e. "obj") +* @return {?} depending on the parser +*/ +Mesh.prototype.parse = function( data, format ) +{ + format = format.toLowerCase(); + var parser = GL.Mesh.parsers[ format ]; + if(parser) + return parser.call(null, data, {mesh: this}); + throw("GL.Mesh.parse: no parser found for format " + format ); +} + +/** +* It returns the mesh data encoded in the format specified +* Only obj supported now +* @method encode +* @param {String} format to encode the data to (p.e. "obj") +* @return {?} String with the info +*/ +Mesh.prototype.encode = function( format, options ) +{ + format = format.toLowerCase(); + var encoder = GL.Mesh.encoders[ format ]; + if(encoder) + return encoder.call(null, this, options ); + throw("GL.Mesh.encode: no encoder found for format " + format ); +} + +/** +* Returns a shared mesh containing a quad to be used when rendering to the screen +* Reusing the same quad helps not filling the memory +* @method getScreenQuad +* @return {GL.Mesh} the screen quad +*/ +Mesh.getScreenQuad = function(gl) +{ + gl = gl || global.gl; + var mesh = gl.meshes[":screen_quad"]; + if(mesh) + return mesh; + + var vertices = new Float32Array([0,0,0, 1,1,0, 0,1,0, 0,0,0, 1,0,0, 1,1,0 ]); + var coords = new Float32Array([0,0, 1,1, 0,1, 0,0, 1,0, 1,1 ]); + mesh = new GL.Mesh({ vertices: vertices, coords: coords}, undefined, undefined, gl); + return gl.meshes[":screen_quad"] = mesh; +} + +function linearizeArray( array, typed_array_class ) +{ + if(array.constructor === typed_array_class) + return array; + if(array.constructor !== Array) + { + typed_array_class = typed_array_class || Float32Array; + return new typed_array_class(array); + } + + typed_array_class = typed_array_class || Float32Array; + var components = array[0].length; + var size = array.length * components; + var buffer = new typed_array_class(size); + + for (var i=0; i < array.length;++i) + for(var j=0; j < components; ++j) + buffer[i*components + j] = array[i][j]; + return buffer; +} + +GL.linearizeArray = linearizeArray; + +/* BINARY MESHES */ +//Add some functions to the classes in LiteGL to allow store in binary +GL.Mesh.EXTENSION = "wbin"; +GL.Mesh.enable_wbin_compression = true; + +//this is used when a mesh is dynamic and constantly changes +function DynamicMesh( size, normals, coords, colors, gl ) +{ + size = size || 1024; + + if(GL.debug) + console.log("GL.Mesh created"); + + if( gl !== null ) + { + gl = gl || global.gl; + this.gl = gl; + } + + //used to avoid problems with resources moving between different webgl context + this._context_id = gl.context_id; + + this.vertexBuffers = {}; + this.indexBuffers = {}; + + //here you can store extra info, like groups, which is an array of { name, start, length, material } + this.info = { + groups: [] + }; + this._bounding = BBox.create(); //here you can store a AABB in BBox format + + this.resize( size ); +} + +DynamicMesh.DEFAULT_NORMAL = vec3.fromValues(0,1,0); +DynamicMesh.DEFAULT_COORD = vec2.fromValues(0.5,0.5); +DynamicMesh.DEFAULT_COLOR = vec4.fromValues(1,1,1,1); + +DynamicMesh.prototype.resize = function( size ) +{ + var buffers = {}; + + this._vertex_data = new Float32Array( size * 3 ); + buffers.vertices = this._vertex_data; + + if( normals ) + buffers.normals = this._normal_data = new Float32Array( size * 3 ); + if( coords ) + buffers.coords = this._coord_data = new Float32Array( size * 2 ); + if( colors ) + buffers.colors = this._color_data = new Float32Array( size * 4 ); + + this.addBuffers( buffers ); + + this.current_pos = 0; + this.max_size = size; + this._must_update = true; +} + +DynamicMesh.prototype.clear = function() +{ + this.current_pos = 0; +} + +DynamicMesh.prototype.addPoint = function( vertex, normal, coord, color ) +{ + if (pos >= this.max_size) + { + console.warn("DynamicMesh: not enough space, reserve more"); + return false; + } + var pos = this.current_pos++; + + this._vertex_data.set( vertex, pos*3 ); + + if(this._normal_data) + this._normal_data.set( normal || DynamicMesh.DEFAULT_NORMAL, pos*3 ); + if(this._coord_data) + this._coord_data.set( coord || DynamicMesh.DEFAULT_COORD, pos*2 ); + if(this._color_data) + this._color_data.set( color || DynamicMesh.DEFAULT_COLOR, pos*4 ); + + this._must_update = true; + return true; +} + +DynamicMesh.prototype.update = function( force ) +{ + if(!this._must_update && !force) + return this.current_pos; + this._must_update = false; + + this.getBuffer("vertices").upload( gl.STREAM_DRAW ); + if(this._normal_data) + this.getBuffer("normal").upload( gl.STREAM_DRAW ); + if(this._coord_data) + this.getBuffer("coord").upload( gl.STREAM_DRAW ); + if(this._color_data) + this.getBuffer("color").upload( gl.STREAM_DRAW ); + return this.current_pos; +} + +extendClass( DynamicMesh, Mesh ); + +/** +* @class Mesh +*/ + +/** +* Returns a planar mesh (you can choose how many subdivisions) +* @method Mesh.plane +* @param {Object} options valid options: detail, detailX, detailY, size, width, heigth, xz (horizontal plane) +*/ +Mesh.plane = function(options, gl) { + options = options || {}; + options.triangles = []; + var mesh = {}; + var detailX = options.detailX || options.detail || 1; + var detailY = options.detailY || options.detail || 1; + var width = options.width || options.size || 1; + var height = options.height || options.size || 1; + var xz = options.xz; + width *= 0.5; + height *= 0.5; + + var triangles = []; + var vertices = []; + var coords = []; + var normals = []; + + var N = vec3.fromValues(0,0,1); + if(xz) + N.set([0,1,0]); + + for (var y = 0; y <= detailY; y++) { + var t = y / detailY; + for (var x = 0; x <= detailX; x++) { + var s = x / detailX; + if(xz) + vertices.push((2 * s - 1) * width, 0, -(2 * t - 1) * height); + else + vertices.push((2 * s - 1) * width, (2 * t - 1) * height, 0); + coords.push(s, t); + normals.push(N[0],N[1],N[2]); + if (x < detailX && y < detailY) { + var i = x + y * (detailX + 1); + if(xz) //horizontal + { + triangles.push(i + 1, i + detailX + 1, i); + triangles.push(i + 1, i + detailX + 2, i + detailX + 1); + } + else //vertical + { + triangles.push(i, i + 1, i + detailX + 1); + triangles.push(i + detailX + 1, i + 1, i + detailX + 2); + } + } + } + } + + var bounding = BBox.fromCenterHalfsize( [0,0,0], xz ? [width,0,height] : [width,height,0] ); + var mesh_info = {vertices:vertices, normals: normals, coords: coords, triangles: triangles }; + return GL.Mesh.load( mesh_info, { bounding: bounding }, gl); +}; + +/** +* Returns a 2D Mesh (be careful, stream is vertices2D, used for 2D engines ) +* @method Mesh.plane2D +*/ +Mesh.plane2D = function(options, gl) { + var vertices = new Float32Array([-1,1, 1,-1, 1,1, -1,1, -1,-1, 1,-1]); + var coords = new Float32Array([0,1, 1,0, 1,1, 0,1, 0,0, 1,0]); + + if(options && options.size) + { + var s = options.size * 0.5; + for(var i = 0; i < vertices.length; ++i) + vertices[i] *= s; + } + return new GL.Mesh( {vertices2D: vertices, coords: coords },null,gl ); +}; + +/** +* Returns a point mesh +* @method Mesh.point +* @param {Object} options no options +*/ +Mesh.point = function(options) { + return new GL.Mesh( {vertices: [0,0,0]} ); +} + +/** +* Returns a cube mesh +* @method Mesh.cube +* @param {Object} options valid options: size +*/ +Mesh.cube = function(options, gl) { + options = options || {}; + var halfsize = (options.size || 1) * 0.5; + + var buffers = {}; + //[[-1,1,-1],[-1,-1,+1],[-1,1,1],[-1,1,-1],[-1,-1,-1],[-1,-1,+1],[1,1,-1],[1,1,1],[1,-1,+1],[1,1,-1],[1,-1,+1],[1,-1,-1],[-1,1,1],[1,-1,1],[1,1,1],[-1,1,1],[-1,-1,1],[1,-1,1],[-1,1,-1],[1,1,-1],[1,-1,-1],[-1,1,-1],[1,-1,-1],[-1,-1,-1],[-1,1,-1],[1,1,1],[1,1,-1],[-1,1,-1],[-1,1,1],[1,1,1],[-1,-1,-1],[1,-1,-1],[1,-1,1],[-1,-1,-1],[1,-1,1],[-1,-1,1]] + buffers.vertices = new Float32Array([-1,1,-1,-1,-1,+1, -1,1,1,-1,1,-1, -1,-1,-1,-1,-1,+1, 1,1,-1,1,1,1,1,-1,+1,1,1,-1,1,-1,+1,1,-1,-1,-1,1,1,1,-1,1,1,1,1,-1,1,1,-1,-1,1,1,-1,1,-1,1,-1,1,1,-1,1,-1,-1,-1,1,-1,1,-1,-1,-1,-1,-1,-1,1,-1,1,1,1,1,1,-1,-1,1,-1,-1,1,1,1,1,1,-1,-1,-1,1,-1,-1,1,-1,1,-1,-1,-1,1,-1,1,-1,-1,1]); + for(var i = 0, l = buffers.vertices.length; i < l; ++i) + buffers.vertices[i] *= halfsize; + + //[[-1,0,0],[-1,0,0],[-1,0,0],[-1,0,0],[-1,0,0],[-1,0,0],[1,0,0],[1,0,0],[1,0,0],[1,0,0],[1,0,0],[1,0,0],[0,0,1],[0,0,1],[0,0,1],[0,0,1],[0,0,1],[0,0,1],[0,0,-1],[0,0,-1],[0,0,-1],[0,0,-1],[0,0,-1],[0,0,-1],[0,1,0],[0,1,0],[0,1,0],[0,1,0],[0,1,0],[0,1,0],[0,-1,0],[0,-1,0],[0,-1,0],[0,-1,0],[0,-1,0],[0,-1,0]] + //[[0,1],[1,0],[1,1],[0,1],[0,0],[1,0],[1,1],[0,1],[0,0],[1,1],[0,0],[1,0],[0,1],[1,0],[1,1],[0,1],[0,0],[1,0],[1,1],[0,1],[0,0],[1,1],[0,0],[1,0],[0,1],[1,0],[1,1],[0,1],[0,0],[1,0],[1,1],[0,1],[0,0],[1,1],[0,0],[1,0]]; + buffers.normals = new Float32Array([-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0]); + buffers.coords = new Float32Array([0,1,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0]); + + if(options.wireframe) + buffers.wireframe = new Uint16Array([0,2, 2,5, 5,4, 4,0, 6,7, 7,10, 10,11, 11,6, 0,6, 2,7, 5,10, 4,11 ]); + options.bounding = BBox.fromCenterHalfsize( [0,0,0], [halfsize,halfsize,halfsize] ); + return GL.Mesh.load(buffers, options, gl); +} + + +/** +* Returns a cube mesh of a given size +* @method Mesh.cube +* @param {Object} options valid options: size, sizex, sizey, sizez +*/ +Mesh.box = function(options, gl) { + options = options || {}; + var sizex = options.sizex || 1; + var sizey = options.sizey || 1; + var sizez = options.sizez || 1; + sizex *= 0.5; + sizey *= 0.5; + sizez *= 0.5; + + var buffers = {}; + //[[-1,1,-1],[-1,-1,+1],[-1,1,1],[-1,1,-1],[-1,-1,-1],[-1,-1,+1],[1,1,-1],[1,1,1],[1,-1,+1],[1,1,-1],[1,-1,+1],[1,-1,-1],[-1,1,1],[1,-1,1],[1,1,1],[-1,1,1],[-1,-1,1],[1,-1,1],[-1,1,-1],[1,1,-1],[1,-1,-1],[-1,1,-1],[1,-1,-1],[-1,-1,-1],[-1,1,-1],[1,1,1],[1,1,-1],[-1,1,-1],[-1,1,1],[1,1,1],[-1,-1,-1],[1,-1,-1],[1,-1,1],[-1,-1,-1],[1,-1,1],[-1,-1,1]] + buffers.vertices = new Float32Array([-1,1,-1,-1,-1,+1,-1,1,1,-1,1,-1,-1,-1,-1,-1,-1,+1,1,1,-1,1,1,1,1,-1,+1,1,1,-1,1,-1,+1,1,-1,-1,-1,1,1,1,-1,1,1,1,1,-1,1,1,-1,-1,1,1,-1,1,-1,1,-1,1,1,-1,1,-1,-1,-1,1,-1,1,-1,-1,-1,-1,-1,-1,1,-1,1,1,1,1,1,-1,-1,1,-1,-1,1,1,1,1,1,-1,-1,-1,1,-1,-1,1,-1,1,-1,-1,-1,1,-1,1,-1,-1,1]); + //for(var i in options.vertices) for(var j in options.vertices[i]) options.vertices[i][j] *= size; + for(var i = 0, l = buffers.vertices.length; i < l; i+=3) + { + buffers.vertices[i] *= sizex; + buffers.vertices[i+1] *= sizey; + buffers.vertices[i+2] *= sizez; + } + + //[[-1,0,0],[-1,0,0],[-1,0,0],[-1,0,0],[-1,0,0],[-1,0,0],[1,0,0],[1,0,0],[1,0,0],[1,0,0],[1,0,0],[1,0,0],[0,0,1],[0,0,1],[0,0,1],[0,0,1],[0,0,1],[0,0,1],[0,0,-1],[0,0,-1],[0,0,-1],[0,0,-1],[0,0,-1],[0,0,-1],[0,1,0],[0,1,0],[0,1,0],[0,1,0],[0,1,0],[0,1,0],[0,-1,0],[0,-1,0],[0,-1,0],[0,-1,0],[0,-1,0],[0,-1,0]] + //[[0,1],[1,0],[1,1],[0,1],[0,0],[1,0],[1,1],[0,1],[0,0],[1,1],[0,0],[1,0],[0,1],[1,0],[1,1],[0,1],[0,0],[1,0],[1,1],[0,1],[0,0],[1,1],[0,0],[1,0],[0,1],[1,0],[1,1],[0,1],[0,0],[1,0],[1,1],[0,1],[0,0],[1,1],[0,0],[1,0]]; + buffers.normals = new Float32Array([-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0]); + buffers.coords = new Float32Array([0,1,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0]); + + if(options.wireframe) + buffers.wireframe = new Uint16Array([0,2, 2,5, 5,4, 4,0, 6,7, 7,10, 10,11, 11,6, 0,6, 2,7, 5,10, 4,11 ]); + + options.bounding = BBox.fromCenterHalfsize( [0,0,0], [sizex,sizey,sizez] ); + + return GL.Mesh.load(buffers, options, gl); +} + +/** +* Returns a circle mesh +* @method Mesh.circle +* @param {Object} options valid options: size,radius, xz = in xz plane, otherwise xy plane +*/ +Mesh.circle = function( options, gl ) { + options = options || {}; + var size = options.size || options.radius || 1; + var slices = Math.ceil(options.slices || 24); + var xz = options.xz || false; + var empty = options.empty || false; + if(slices < 3) slices = 3; + var delta = (2 * Math.PI) / slices; + + var center = vec3.create(); + var A = vec3.create(); + var N = vec3.fromValues(0,0,1); + var uv_center = vec2.fromValues(0.5,0.5); + var uv = vec2.create(); + + if(xz) N.set([0,1,0]); + + var index = xz ? 2 : 1; + + var vertices = new Float32Array(3 * (slices + 1)); + var normals = new Float32Array(3 * (slices + 1)); + var coords = new Float32Array(2 * (slices + 1)); + var triangles = null; + + //the center is always the same + vertices.set(center, 0); + normals.set(N, 0); + coords.set(uv_center, 0); + + var sin = 0; + var cos = 0; + + //compute vertices + for(var i = 0; i < slices; ++i ) + { + sin = Math.sin( delta * i ); + cos = Math.cos( delta * i ); + + A[0] = sin * size; + A[index] = cos * size; + uv[0] = sin * 0.5 + 0.5; + uv[1] = cos * 0.5 + 0.5; + vertices.set(A, i * 3 + 3); + normals.set(N, i * 3 + 3); + coords.set(uv, i * 2 + 2); + } + + if(empty) + { + vertices = vertices.subarray(3); + normals = vertices.subarray(3); + coords = vertices.subarray(2); + triangles = null; + } + else + { + var triangles = new Uint16Array(3 * slices); + var offset = 2; + var offset2 = 1; + if(xz) + { + offset = 1; + offset2 = 2; + } + + //compute indices + for(var i = 0; i < slices-1; ++i ) + { + triangles[i*3] = 0; + triangles[i*3+1] = i+offset; + triangles[i*3+2] = i+offset2; + } + + triangles[i*3] = 0; + if(xz) + { + triangles[i*3+1] = i+1; + triangles[i*3+2] = 1; + } + else + { + triangles[i*3+1] = 1; + triangles[i*3+2] = i+1; + } + } + + options.bounding = BBox.fromCenterHalfsize( [0,0,0], xz ? [size,0,size] : [size,size,0] ); + + var buffers = {vertices: vertices, normals: normals, coords: coords, triangles: triangles}; + + if(options.wireframe) + { + var wireframe = new Uint16Array(slices*2); + for(var i = 0; i < slices; i++) + { + wireframe[i*2] = i; + wireframe[i*2+1] = i+1; + } + wireframe[0] = slices; + buffers.wireframe = wireframe; + } + + return GL.Mesh.load( buffers, options, gl ); +} + +/** +* Returns a cube mesh +* @method Mesh.cylinder +* @param {Object} options valid options: radius, height, subdivisions +*/ +Mesh.cylinder = function( options, gl ) { + options = options || {}; + var radius = options.radius || options.size || 1; + var height = options.height || options.size || 2; + var subdivisions = options.subdivisions || 64; + + var vertices = new Float32Array(subdivisions * 6 * 3 * 2 ); + var normals = new Float32Array(subdivisions * 6 * 3 * 2 ); + var coords = new Float32Array(subdivisions * 6 * 2 * 2 ); + //not indexed because caps have different normals and uvs so... + + var delta = 2*Math.PI / subdivisions; + var normal = null; + for(var i = 0; i < subdivisions; ++i) + { + var angle = i * delta; + + normal = [ Math.sin(angle), 0, Math.cos(angle)]; + vertices.set([ normal[0]*radius, height*0.5, normal[2]*radius], i*6*3); + normals.set(normal, i*6*3 ); + coords.set([i/subdivisions,1], i*6*2 ); + + normal = [ Math.sin(angle), 0, Math.cos(angle)]; + vertices.set([ normal[0]*radius, height*-0.5, normal[2]*radius], i*6*3 + 3); + normals.set(normal, i*6*3 + 3); + coords.set([i/subdivisions,0], i*6*2 + 2); + + normal = [ Math.sin(angle+delta), 0, Math.cos(angle+delta)]; + vertices.set([ normal[0]*radius, height*-0.5, normal[2]*radius], i*6*3 + 6); + normals.set(normal, i*6*3 + 6); + coords.set([(i+1)/subdivisions,0], i*6*2 + 4); + + normal = [ Math.sin(angle+delta), 0, Math.cos(angle+delta)]; + vertices.set([ normal[0]*radius, height*0.5, normal[2]*radius], i*6*3 + 9); + normals.set(normal, i*6*3 + 9); + coords.set([(i+1)/subdivisions,1], i*6*2 + 6); + + normal = [ Math.sin(angle), 0, Math.cos(angle)]; + vertices.set([ normal[0]*radius, height*0.5, normal[2]*radius], i*6*3 + 12); + normals.set(normal, i*6*3 + 12); + coords.set([i/subdivisions,1], i*6*2 + 8); + + normal = [ Math.sin(angle+delta), 0, Math.cos(angle+delta)]; + vertices.set([ normal[0]*radius, height*-0.5, normal[2]*radius], i*6*3 + 15); + normals.set(normal, i*6*3 + 15); + coords.set([(i+1)/subdivisions,0], i*6*2 + 10); + } + + var pos = i*6*3; + var pos_uv = i*6*2; + var caps_start = pos; + + //caps + if( options.caps === false ) + { + //finalize arrays + vertices = vertices.subarray(0,pos); + normals = normals.subarray(0,pos); + coords = coords.subarray(0,pos_uv); + } + else + { + var top_center = vec3.fromValues(0,height*0.5,0); + var bottom_center = vec3.fromValues(0,height*-0.5,0); + var up = vec3.fromValues(0,1,0); + var down = vec3.fromValues(0,-1,0); + for(var i = 0; i < subdivisions; ++i) + { + var angle = i * delta; + + var uv = vec3.fromValues( Math.sin(angle), 0, Math.cos(angle) ); + var uv2 = vec3.fromValues( Math.sin(angle+delta), 0, Math.cos(angle+delta) ); + + vertices.set([ uv[0]*radius, height*0.5, uv[2]*radius], pos + i*6*3); + normals.set(up, pos + i*6*3 ); + coords.set( [ -uv[0] * 0.5 + 0.5,uv[2] * 0.5 + 0.5], pos_uv + i*6*2 ); + + vertices.set([ uv2[0]*radius, height*0.5, uv2[2]*radius], pos + i*6*3 + 3); + normals.set(up, pos + i*6*3 + 3 ); + coords.set( [ -uv2[0] * 0.5 + 0.5,uv2[2] * 0.5 + 0.5], pos_uv + i*6*2 + 2 ); + + vertices.set( top_center, pos + i*6*3 + 6 ); + normals.set(up, pos + i*6*3 + 6); + coords.set([0.5,0.5], pos_uv + i*6*2 + 4); + + //bottom + vertices.set([ uv2[0]*radius, height*-0.5, uv2[2]*radius], pos + i*6*3 + 9); + normals.set(down, pos + i*6*3 + 9); + coords.set( [ uv2[0] * 0.5 + 0.5,uv2[2] * 0.5 + 0.5], pos_uv + i*6*2 + 6); + + vertices.set([ uv[0]*radius, height*-0.5, uv[2]*radius], pos + i*6*3 + 12); + normals.set(down, pos + i*6*3 + 12 ); + coords.set( [ uv[0] * 0.5 + 0.5,uv[2] * 0.5 + 0.5], pos_uv + i*6*2 + 8 ); + + vertices.set( bottom_center, pos + i*6*3 + 15 ); + normals.set( down, pos + i*6*3 + 15); + coords.set( [0.5,0.5], pos_uv + i*6*2 + 10); + } + } + + var buffers = { + vertices: vertices, + normals: normals, + coords: coords + } + options.bounding = BBox.fromCenterHalfsize( [0,0,0], [radius,height*0.5,radius] ); + options.info = { groups: [] }; + + if(options.caps !== false) + { + options.info.groups.push({ name:"side", start: 0, length: caps_start / 3}); + options.info.groups.push({ name:"caps", start: caps_start / 3, length: (vertices.length - caps_start) / 3}); + } + + return Mesh.load( buffers, options, gl ); +} + +/** +* Returns a cone mesh +* @method Mesh.cone +* @param {Object} options valid options: radius, height, subdivisions +*/ +Mesh.cone = function( options, gl ) { + options = options || {}; + var radius = options.radius || options.size || 1; + var height = options.height || options.size || 2; + var subdivisions = options.subdivisions || 64; + + var vertices = new Float32Array(subdivisions * 3 * 3 * 2); + var normals = new Float32Array(subdivisions * 3 * 3 * 2); + var coords = new Float32Array(subdivisions * 2 * 3 * 2); + //not indexed because caps have different normals and uvs so... + + var delta = 2*Math.PI / subdivisions; + var normal = null; + var normal_y = radius / height; + var up = [0,1,0]; + + for(var i = 0; i < subdivisions; ++i) + { + var angle = i * delta; + + normal = [ Math.sin(angle+delta*0.5), normal_y, Math.cos(angle+delta*0.5)]; + vec3.normalize(normal,normal); + //normal = up; + vertices.set([ 0, height, 0] , i*6*3); + normals.set(normal, i*6*3 ); + coords.set([i/subdivisions,1], i*6*2 ); + + normal = [ Math.sin(angle), normal_y, Math.cos(angle)]; + vertices.set([ normal[0]*radius, 0, normal[2]*radius], i*6*3 + 3); + vec3.normalize(normal,normal); + normals.set(normal, i*6*3 + 3); + coords.set([i/subdivisions,0], i*6*2 + 2); + + normal = [ Math.sin(angle+delta), normal_y, Math.cos(angle+delta)]; + vertices.set([ normal[0]*radius, 0, normal[2]*radius], i*6*3 + 6); + vec3.normalize(normal,normal); + normals.set(normal, i*6*3 + 6); + coords.set([(i+1)/subdivisions,0], i*6*2 + 4); + } + + var pos = 0;//i*3*3; + var pos_uv = 0;//i*3*2; + + //cap + var bottom_center = vec3.fromValues(0,0,0); + var down = vec3.fromValues(0,-1,0); + for(var i = 0; i < subdivisions; ++i) + { + var angle = i * delta; + + var uv = vec3.fromValues( Math.sin(angle), 0, Math.cos(angle) ); + var uv2 = vec3.fromValues( Math.sin(angle+delta), 0, Math.cos(angle+delta) ); + + //bottom + vertices.set([ uv2[0]*radius, 0, uv2[2]*radius], pos + i*6*3 + 9); + normals.set(down, pos + i*6*3 + 9); + coords.set( [ uv2[0] * 0.5 + 0.5,uv2[2] * 0.5 + 0.5], pos_uv + i*6*2 + 6); + + vertices.set([ uv[0]*radius, 0, uv[2]*radius], pos + i*6*3 + 12); + normals.set(down, pos + i*6*3 + 12 ); + coords.set( [ uv[0] * 0.5 + 0.5,uv[2] * 0.5 + 0.5], pos_uv + i*6*2 + 8 ); + + vertices.set( bottom_center, pos + i*6*3 + 15 ); + normals.set( down, pos + i*6*3 + 15); + coords.set( [0.5,0.5], pos_uv + i*6*2 + 10); + } + + var buffers = { + vertices: vertices, + normals: normals, + coords: coords + } + options.bounding = BBox.fromCenterHalfsize( [0,height*0.5,0], [radius,height*0.5,radius] ); + + return Mesh.load( buffers, options, gl ); +} + +/** +* Returns a sphere mesh +* @method Mesh.sphere +* @param {Object} options valid options: radius, lat, long, subdivisions, hemi +*/ +Mesh.sphere = function( options, gl ) { + options = options || {}; + var radius = options.radius || options.size || 1; + var latitudeBands = options.lat || options.subdivisions || 16; + var longitudeBands = options["long"] || options.subdivisions || 16; + + var vertexPositionData = new Float32Array( (latitudeBands+1)*(longitudeBands+1)*3 ); + var normalData = new Float32Array( (latitudeBands+1)*(longitudeBands+1)*3 ); + var textureCoordData = new Float32Array( (latitudeBands+1)*(longitudeBands+1)*2 ); + var indexData = new Uint16Array( latitudeBands*longitudeBands*6 ); + var latRange = options.hemi ? Math.PI * 0.5 : Math.PI; + + var i = 0, iuv = 0; + for (var latNumber = 0; latNumber <= latitudeBands; latNumber++) + { + var theta = latNumber * latRange / latitudeBands; + var sinTheta = Math.sin(theta); + var cosTheta = Math.cos(theta); + + for (var longNumber = 0; longNumber <= longitudeBands; longNumber++) + { + var phi = longNumber * 2 * Math.PI / longitudeBands; + var sinPhi = Math.sin(phi); + var cosPhi = Math.cos(phi); + + var x = cosPhi * sinTheta; + var y = cosTheta; + var z = sinPhi * sinTheta; + var u = 1- (longNumber / longitudeBands); + var v = (1 - latNumber / latitudeBands); + + vertexPositionData.set([radius * x,radius * y,radius * z],i); + normalData.set([x,y,z],i); + textureCoordData.set([u,v], iuv ); + i += 3; + iuv += 2; + } + } + + i=0; + for (var latNumber = 0; latNumber < latitudeBands; latNumber++) + { + for (var longNumber = 0; longNumber < longitudeBands; longNumber++) + { + var first = (latNumber * (longitudeBands + 1)) + longNumber; + var second = first + longitudeBands + 1; + + indexData.set([second,first,first + 1], i); + indexData.set([second + 1,second,first + 1], i+3); + i += 6; + } + } + + var buffers = { + vertices: vertexPositionData, + normals: normalData, + coords: textureCoordData, + triangles: indexData + }; + + if(options.wireframe) + { + var wireframe = new Uint16Array(longitudeBands*latitudeBands*4); + var pos = 0; + for(var i = 0; i < latitudeBands; i++) + { + for(var j = 0; j < longitudeBands; j++) + { + wireframe[pos] = i*(longitudeBands+1) + j; + wireframe[pos + 1] = i*(longitudeBands+1) + j + 1; + pos += 2; + } + wireframe[pos - longitudeBands*2] = i*(longitudeBands+1) + j; + } + + for(var i = 0; i < longitudeBands; i++) + for(var j = 0; j < latitudeBands; j++) + { + wireframe[pos] = j*(longitudeBands+1) + i; + wireframe[pos + 1] = (j+1)*(longitudeBands+1) + i; + pos += 2; + } + buffers.wireframe = wireframe; + } + + if(options.hemi) + options.bounding = BBox.fromCenterHalfsize( [0,radius*0.5,0], [radius,radius*0.5,radius], radius ); + else + options.bounding = BBox.fromCenterHalfsize( [0,0,0], [radius,radius,radius], radius ); + return GL.Mesh.load( buffers, options, gl ); +} + +/** +* Returns a grid mesh (must be rendered using gl.LINES) +* @method Mesh.grid +* @param {Object} options valid options: size, lines +*/ +Mesh.grid = function( options, gl ) +{ + options = options || {}; + var num_lines = options.lines || 11; + if(num_lines < 0) + num_lines = 1; + var size = options.size || 10; + + var vertexPositionData = new Float32Array( num_lines*2*2*3 ); + var hsize = size * 0.5; + var pos = 0; + var x = -hsize; + var delta = size / (num_lines-1); + + for(var i = 0; i < num_lines; i++) + { + vertexPositionData[ pos ] = x; + vertexPositionData[ pos+2 ] = -hsize; + vertexPositionData[ pos+3 ] = x; + vertexPositionData[ pos+5 ] = hsize; + + vertexPositionData[ pos+6 ] = hsize; + vertexPositionData[ pos+8 ] = x + vertexPositionData[ pos+9 ] = -hsize; + vertexPositionData[ pos+11 ] = x + + x += delta; + pos += 12; + } + + return new GL.Mesh({vertices: vertexPositionData}, options, gl ); +} + + +/** +* Returns a icosahedron mesh (useful to create spheres by subdivision) +* @method Mesh.icosahedron +* @param {Object} options valid options: radius, subdivisions (max: 6) +*/ +Mesh.icosahedron = function( options, gl ) { + options = options || {}; + var radius = options.radius || options.size || 1; + var subdivisions = options.subdivisions === undefined ? 0 : options.subdivisions; + if(subdivisions > 6) //dangerous + subdivisions = 6; + + var t = (1.0 + Math.sqrt(5)) / 2.0; + var vertices = [-1,t,0, 1,t,0, -1,-t,0, 1,-t,0, + 0,-1,t, 0,1,t, 0,-1,-t, 0,1,-t, + t,0,-1, t,0,1, -t,0,-1, -t,0,1]; + var normals = []; + var coords = []; + var indices = [0,11,5, 0,5,1, 0,1,7, 0,7,10, 0,10,11, 1,5,9, 5,11,4, 11,10,2, 10,7,6, 7,1,8, 3,9,4, 3,4,2, 3,2,6, 3,6,8, 3,8,9, 4,9,5, 2,4,11, 6,2,10, 8,6,7, 9,8,1 ]; + + //normalize + var l = vertices.length; + for(var i = 0; i < l; i+=3) + { + var mod = Math.sqrt( vertices[i]*vertices[i] + vertices[i+1]*vertices[i+1] + vertices[i+2]*vertices[i+2] ); + var normalx = vertices[i] / mod; + var normaly = vertices[i+1] / mod; + var normalz = vertices[i+2] / mod; + normals.push( normalx, normaly, normalz ); + coords.push( Math.atan2( normalx, normalz ), Math.acos( normaly ) ); + vertices[i] *= radius/mod; + vertices[i+1] *= radius/mod; + vertices[i+2] *= radius/mod; + } + + var middles = {}; + + //A,B = index of vertex in vertex array + function middlePoint( A, B ) + { + var key = indices[A] < indices[B] ? indices[A] + ":"+indices[B] : indices[B]+":"+indices[A]; + var r = middles[key]; + if(r) + return r; + var index = vertices.length / 3; + vertices.push(( vertices[ indices[A]*3] + vertices[ indices[B]*3 ]) * 0.5, + (vertices[ indices[A]*3+1] + vertices[ indices[B]*3+1 ]) * 0.5, + (vertices[ indices[A]*3+2] + vertices[ indices[B]*3+2 ]) * 0.5); + + var mod = Math.sqrt( vertices[index*3]*vertices[index*3] + vertices[index*3+1]*vertices[index*3+1] + vertices[index*3+2]*vertices[index*3+2] ); + var normalx = vertices[index*3] / mod; + var normaly = vertices[index*3+1] / mod; + var normalz = vertices[index*3+2] / mod; + normals.push( normalx, normaly, normalz ); + coords.push( (Math.atan2( normalx, normalz ) / Math.PI) * 0.5, (Math.acos( normaly ) / Math.PI) ); + vertices[index*3] *= radius/mod; + vertices[index*3+1] *= radius/mod; + vertices[index*3+2] *= radius/mod; + + middles[key] = index; + return index; + } + + for (var iR = 0; iR < subdivisions; ++iR ) + { + var new_indices = []; + var l = indices.length; + for(var i = 0; i < l; i+=3) + { + var MA = middlePoint( i, i+1 ); + var MB = middlePoint( i+1, i+2); + var MC = middlePoint( i+2, i); + new_indices.push(indices[i], MA, MC); + new_indices.push(indices[i+1], MB, MA); + new_indices.push(indices[i+2], MC, MB); + new_indices.push(MA, MB, MC); + } + indices = new_indices; + } + + options.bounding = BBox.fromCenterHalfsize( [0,0,0], [radius,radius,radius], radius ); + + return new GL.Mesh.load({vertices: vertices, coords: coords, normals: normals, triangles: indices},options,gl); +} +/** +* @namespace GL +*/ + +/** +* Texture class to upload images to the GPU, default is gl.TEXTURE_2D, gl.RGBA of gl.UNSIGNED_BYTE with filters set to gl.LINEAR and wrap to gl.CLAMP_TO_EDGE
+ There is a list of options
+ ==========================
+ - texture_type: gl.TEXTURE_2D, gl.TEXTURE_CUBE_MAP, default gl.TEXTURE_2D
+ - format: gl.RGB, gl.RGBA, gl.DEPTH_COMPONENT, default gl.RGBA
+ - type: gl.UNSIGNED_BYTE, gl.UNSIGNED_SHORT, gl.HALF_FLOAT_OES, gl.FLOAT, default gl.UNSIGNED_BYTE
+ - filter: filtering for mag and min: gl.NEAREST or gl.LINEAR, default gl.NEAREST
+ - magFilter: magnifying filter: gl.NEAREST, gl.LINEAR, default gl.NEAREST
+ - minFilter: minifying filter: gl.NEAREST, gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR, default gl.NEAREST
+ - wrap: texture wrapping: gl.CLAMP_TO_EDGE, gl.REPEAT, gl.MIRROR, default gl.CLAMP_TO_EDGE (also accepts wrapT and wrapS for separate settings)
+ - pixel_data: ArrayBufferView with the pixel data to upload to the texture, otherwise the texture will be black (if cubemaps then pass an array[6] with the data for every face)
+ - premultiply_alpha : multiply the color by the alpha value when uploading, default FALSE
+ - no_flip : do not flip in Y, default TRUE
+ - anisotropic : number of anisotropic fetches, default 0
+ + check for more info about formats: https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/texImage2D + +* @class Texture +* @param {number} width texture width (any supported but Power of Two allows to have mipmaps), 0 means no memory reserved till its filled +* @param {number} height texture height (any supported but Power of Two allows to have mipmaps), 0 means no memory reserved till its filled +* @param {Object} options Check the list in the description +* @constructor +*/ + +global.Texture = GL.Texture = function Texture( width, height, options, gl ) { + options = options || {}; + + //used to avoid problems with resources moving between different webgl context + gl = gl || global.gl; + this.gl = gl; + this._context_id = gl.context_id; + + //round sizes + width = parseInt(width); + height = parseInt(height); + + if(GL.debug) + console.log("GL.Texture created: ",width,height); + + //create texture handler + this.handler = gl.createTexture(); + + //set settings + this.width = width; + this.height = height; + if(options.depth) //for texture_3d + this.depth = options.depth; + this.texture_type = options.texture_type || gl.TEXTURE_2D; //or gl.TEXTURE_CUBE_MAP + this.format = options.format || Texture.DEFAULT_FORMAT; //gl.RGBA (if gl.DEPTH_COMPONENT remember type: gl.UNSIGNED_SHORT) + this.internalFormat = options.internalFormat; //LUMINANCE, and weird formats with bits + this.type = options.type || Texture.DEFAULT_TYPE; //gl.UNSIGNED_BYTE, gl.UNSIGNED_SHORT, gl.FLOAT or gl.HALF_FLOAT_OES (or gl.HIGH_PRECISION_FORMAT which could be half or float) + this.magFilter = options.magFilter || options.filter || Texture.DEFAULT_MAG_FILTER; + this.minFilter = options.minFilter || options.filter || Texture.DEFAULT_MIN_FILTER; + this.wrapS = options.wrap || options.wrapS || Texture.DEFAULT_WRAP_S; + this.wrapT = options.wrap || options.wrapT || Texture.DEFAULT_WRAP_T; + this.data = null; //where the data came from + + //precompute the max amount of texture units + if(!Texture.MAX_TEXTURE_IMAGE_UNITS) + Texture.MAX_TEXTURE_IMAGE_UNITS = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS ); + + this.has_mipmaps = false; + + if( this.format == gl.DEPTH_COMPONENT && gl.webgl_version == 1 && !gl.extensions["WEBGL_depth_texture"] ) + throw("Depth Texture not supported"); + if( this.type == gl.FLOAT && !gl.extensions["OES_texture_float"] && gl.webgl_version == 1 ) + throw("Float Texture not supported"); + if( this.type == gl.HALF_FLOAT_OES) + { + if( !gl.extensions["OES_texture_half_float"] && gl.webgl_version == 1 ) + throw("Half Float Texture extension not supported."); + else if( gl.webgl_version > 1 ) + { + console.warn("using HALF_FLOAT_OES in WebGL2 is deprecated, suing HALF_FLOAT instead"); + this.type = this.format == gl.RGB ? gl.RGB16F : gl.RGBA16F; + } + } + if( (!isPowerOfTwo(this.width) || !isPowerOfTwo(this.height)) && //non power of two + ( (this.minFilter != gl.NEAREST && this.minFilter != gl.LINEAR) || //uses mipmaps + (this.wrapS != gl.CLAMP_TO_EDGE || this.wrapT != gl.CLAMP_TO_EDGE) ) ) //uses wrap + { + if(!options.ignore_pot) + throw("Cannot use texture-wrap or mipmaps in Non-Power-of-Two textures"); + else + { + this.minFilter = this.magFilter = gl.LINEAR; + this.wrapS = this.wrapT = gl.CLAMP_TO_EDGE; + } + } + + //empty textures are allowed to be created + if(!width || !height) + return; + + //because sometimes the internal format is not so obvious + if(!this.internalFormat) + this.computeInternalFormat(); + + //this is done because in some cases the user binds a texture to slot 0 and then creates a new one, which overrides slot 0 + gl.activeTexture( gl.TEXTURE0 + Texture.MAX_TEXTURE_IMAGE_UNITS - 1); + //I use an invalid gl enum to say this texture is a depth texture, ugly, I know... + gl.bindTexture( this.texture_type, this.handler); + gl.texParameteri( this.texture_type, gl.TEXTURE_MAG_FILTER, this.magFilter ); + gl.texParameteri( this.texture_type, gl.TEXTURE_MIN_FILTER, this.minFilter ); + gl.texParameteri( this.texture_type, gl.TEXTURE_WRAP_S, this.wrapS ); + gl.texParameteri( this.texture_type, gl.TEXTURE_WRAP_T, this.wrapT ); + + if(options.anisotropic && gl.extensions["EXT_texture_filter_anisotropic"]) + gl.texParameterf( GL.TEXTURE_2D, gl.extensions["EXT_texture_filter_anisotropic"].TEXTURE_MAX_ANISOTROPY_EXT, options.anisotropic); + + var type = this.type; + var pixel_data = options.pixel_data; + if(pixel_data && !pixel_data.buffer) + { + if( this.texture_type == GL.TEXTURE_CUBE_MAP ) + { + if(pixel_data[0].constructor === Number) //special case, specify just one face and copy it + { + pixel_data = toTypedArray( pixel_data ); + pixel_data = [pixel_data,pixel_data,pixel_data,pixel_data,pixel_data,pixel_data]; + } + else + for(var i = 0; i < pixel_data.length; ++i) + pixel_data[i] = toTypedArray( pixel_data[i] ); + } + else + pixel_data = toTypedArray( pixel_data ); + this.data = pixel_data; + } + + function toTypedArray( data ) + { + if(data.constructor !== Array) + return data; + if( type == GL.FLOAT) + return new Float32Array( data ); + if( type == GL.HALF_FLOAT_OES) + return new Uint16Array( data ); + return new Uint8Array( data ); + } + + //gl.TEXTURE_1D is not supported by WebGL... + + //here we create all ********************************** + if(this.texture_type == GL.TEXTURE_2D) + { + //create the texture + gl.texImage2D( GL.TEXTURE_2D, 0, this.internalFormat, width, height, 0, this.format, this.type, pixel_data || null ); + + //generate empty mipmaps (necessary?) + if ( GL.isPowerOfTwo(width) && GL.isPowerOfTwo(height) && options.minFilter && options.minFilter != gl.NEAREST && options.minFilter != gl.LINEAR) + { + gl.generateMipmap( this.texture_type ); + this.has_mipmaps = true; + } + } + else if(this.texture_type == GL.TEXTURE_CUBE_MAP) + { + for(var i = 0; i < 6; ++i) + gl.texImage2D( gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, this.internalFormat, this.width, this.height, 0, this.format, this.type, pixel_data ? pixel_data[i] : null ); + } + else if(this.texture_type == GL.TEXTURE_3D) + { + if(this.gl.webgl_version == 1) + throw("TEXTURE_3D not supported in WebGL 1. Enable WebGL 2 in the context by passing webgl2:true to the context"); + if(!options.depth) + throw("3d texture depth must be set in the options.depth"); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false ); //standard does not allow this flags for 3D textures + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false ); + gl.texImage3D( GL.TEXTURE_3D, 0, this.internalFormat, width, height, options.depth, 0, this.format, this.type, pixel_data || null ); + } + gl.bindTexture(this.texture_type, null); //disable + gl.activeTexture(gl.TEXTURE0); +} + +Texture.DEFAULT_TYPE = GL.UNSIGNED_BYTE; +Texture.DEFAULT_FORMAT = GL.RGBA; +Texture.DEFAULT_MAG_FILTER = GL.LINEAR; +Texture.DEFAULT_MIN_FILTER = GL.LINEAR; +Texture.DEFAULT_WRAP_S = GL.CLAMP_TO_EDGE; +Texture.DEFAULT_WRAP_T = GL.CLAMP_TO_EDGE; +Texture.EXTENSION = "png"; //used when saving it to file + +//used for render to FBOs +Texture.framebuffer = null; +Texture.renderbuffer = null; +Texture.loading_color = new Uint8Array([0,0,0,0]); +Texture.use_renderbuffer_pool = true; //should improve performance + +//because usually you dont want to specify the internalFormat, this tries to guess it from its format +//check https://webgl2fundamentals.org/webgl/lessons/webgl-data-textures.html for more info +Texture.prototype.computeInternalFormat = function() +{ + this.internalFormat = this.format; //default + + //automatic selection of internal format for depth textures to avoid problems between webgl1 and 2 + if( this.format == GL.DEPTH_COMPONENT ) + { + this.minFilter = this.magFilter = GL.NEAREST; + + if( gl.webgl_version == 2 ) + { + if( this.type == GL.UNSIGNED_SHORT ) + this.internalFormat = GL.DEPTH_COMPONENT16; + else if( this.type == GL.UNSIGNED_INT ) + this.internalFormat = GL.DEPTH_COMPONENT24; + else if( this.type == GL.FLOAT ) + this.internalFormat = GL.DEPTH_COMPONENT32F; + else + throw("unsupported type for a depth texture"); + } + else if( gl.webgl_version == 1 ) + { + if( this.type == GL.FLOAT ) + throw("WebGL 1.0 does not support float depth textures"); + this.internalFormat = GL.DEPTH_COMPONENT; + } + } + else if( this.format == gl.RGBA ) + { + if( gl.webgl_version == 2 ) + { + if( this.type == GL.FLOAT ) + this.internalFormat = GL.RGBA32F; + else if( this.type == GL.HALF_FLOAT ) + this.internalFormat = GL.RGBA16F; + else if( this.type == GL.HALF_FLOAT_OES ) + { + console.warn("webgl 2 does not use HALF_FLOAT_OES, converting to HALF_FLOAT") + this.type = GL.HALF_FLOAT; + this.internalFormat = GL.RGBA16F; + } + /* + else if( this.type == GL.UNSIGNED_SHORT ) + { + this.internalFormat = GL.RGBA16UI; + this.format = gl.RGBA_INTEGER; + } + else if( this.type == GL.UNSIGNED_INT ) + { + this.internalFormat = GL.RGBA32UI; + this.format = gl.RGBA_INTEGER; + } + */ + } + else if( gl.webgl_version == 1 ) + { + if( this.type == GL.HALF_FLOAT ) + { + console.warn("webgl 1 does not use HALF_FLOAT, converting to HALF_FLOAT_OES") + this.type = GL.HALF_FLOAT_OES; + } + } + } +} + +/** +* Free the texture memory from the GPU, sets the texture handler to null +* @method delete +*/ +Texture.prototype.delete = function() +{ + gl.deleteTexture( this.handler ); + this.handler = null; +} + +Texture.prototype.getProperties = function() +{ + return { + width: this.width, + height: this.height, + type: this.type, + format: this.format, + texture_type: this.texture_type, + magFilter: this.magFilter, + minFilter: this.minFilter, + wrapS: this.wrapS, + wrapT: this.wrapT + }; +} + +Texture.prototype.hasSameProperties = function(t) +{ + if(!t) + return false; + return t.width == this.width && + t.height == this.height && + t.type == this.type && + t.format == this.format && + t.texture_type == this.texture_type; +} + +Texture.prototype.hasSameSize = function(t) +{ + if(!t) + return false; + return t.width == this.width && t.height == this.height; +} +//textures cannot be stored in JSON +Texture.prototype.toJSON = function() +{ + return ""; +} + + +/** +* Returns if depth texture is supported by the GPU +* @method isDepthSupported +* @return {Boolean} true if supported +*/ +Texture.isDepthSupported = function() +{ + return gl.extensions["WEBGL_depth_texture"] != null; +} + +/** +* Binds the texture to one texture unit +* @method bind +* @param {number} unit texture unit +* @return {number} returns the texture unit +*/ +Texture.prototype.bind = function( unit ) { + if(unit == undefined) + unit = 0; + var gl = this.gl; + + //TODO: if the texture is not uploaded, must be upload now + + //bind + gl.activeTexture(gl.TEXTURE0 + unit); + gl.bindTexture( this.texture_type, this.handler ); + return unit; +} + +/** +* Unbinds the texture +* @method unbind +* @param {number} unit texture unit +* @return {number} returns the texture unit +*/ +Texture.prototype.unbind = function(unit) { + if(unit === undefined) + unit = 0; + var gl = this.gl; + gl.activeTexture(gl.TEXTURE0 + unit ); + gl.bindTexture(this.texture_type, null); +} + + +Texture.prototype.setParameter = function(param,value) { + this.bind(0); + this.gl.texParameteri( this.texture_type, param, value ); + switch(param) + { + case this.gl.TEXTURE_MAG_FILTER: this.magFilter = value; break; + case this.gl.TEXTURE_MIN_FILTER: this.minFilter = value; break; + case this.gl.TEXTURE_WRAP_S: this.wrapS = value; break; + case this.gl.TEXTURE_WRAP_T: this.wrapT = value; break; + } +} + +/** +* Unbinds the texture +* @method Texture.setUploadOptions +* @param {Object} options a list of options to upload the texture +* - premultiply_alpha : multiply the color by the alpha value, default FALSE +* - no_flip : do not flip in Y, default TRUE +*/ +Texture.setUploadOptions = function(options, gl) +{ + gl = gl || global.gl; + + if(options) //options that are not stored in the texture should be passed again to avoid reusing unknown state + { + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, !!(options.premultiply_alpha) ); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !(options.no_flip) ); + } + else + { + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false ); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true ); + } + gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1); +} + +/** +* Given an Image/Canvas/Video it uploads it to the GPU +* @method uploadImage +* @param {Image} img +* @param {Object} options [optional] upload options (premultiply_alpha, no_flip) +*/ +Texture.prototype.uploadImage = function( image, options ) +{ + this.bind(); + var gl = this.gl; + if(!image) + throw("uploadImage parameter must be Image"); + + Texture.setUploadOptions(options, gl); + + try { + gl.texImage2D( gl.TEXTURE_2D, 0, this.format, this.format, this.type, image ); + this.width = image.videoWidth || image.width; + this.height = image.videoHeight || image.height; + this.data = image; + } catch (e) { + if (location.protocol == 'file:') { + throw 'image not loaded for security reasons (serve this page over "http://" instead)'; + } else { + throw 'image not loaded for security reasons (image must originate from the same ' + + 'domain as this page or use Cross-Origin Resource Sharing)'; + } + } + + //TODO: add expand transparent pixels option + + //generate mipmaps + if (this.minFilter && this.minFilter != gl.NEAREST && this.minFilter != gl.LINEAR) { + gl.generateMipmap(this.texture_type); + this.has_mipmaps = true; + } + gl.bindTexture(this.texture_type, null); //disable +} + +/** +* Uploads data to the GPU (data must have the appropiate size) +* @method uploadData +* @param {ArrayBuffer} data +* @param {Object} options [optional] upload options (premultiply_alpha, no_flip, cubemap_face, mipmap_level) +*/ +Texture.prototype.uploadData = function( data, options, skip_mipmaps ) +{ + options = options || {}; + if(!data) + throw("no data passed"); + var gl = this.gl; + this.bind(); + Texture.setUploadOptions(options, gl); + var mipmap_level = options.mipmap_level || 0; + var width = this.width; + var height = this.height; + width = width >> mipmap_level; + height = height >> mipmap_level; + var internal_format = this.internalFormat || this.format; + + if( this.type == GL.HALF_FLOAT_OES && data.constructor === Float32Array ) + console.warn("cannot uploadData to a HALF_FLOAT texture from a Float32Array, must be Uint16Array. To upload it we recomment to create a FLOAT texture, upload data there and copy to your HALF_FLOAT."); + + if( this.texture_type == GL.TEXTURE_2D ) + { + if(gl.webgl_version == 1) + { + if(data.buffer && data.buffer.constructor == ArrayBuffer) + gl.texImage2D(this.texture_type, mipmap_level, internal_format, width, height, 0, this.format, this.type, data); + else + gl.texImage2D(this.texture_type, mipmap_level, internal_format, this.format, this.type, data); + } + else if(gl.webgl_version == 2) //webgl forces to use width and height + { + if(data.buffer && data.buffer.constructor == ArrayBuffer) + gl.texImage2D(this.texture_type, mipmap_level, internal_format, width, height, 0, this.format, this.type, data); + else + gl.texImage2D(this.texture_type, mipmap_level, internal_format, width, height, 0, this.format, this.type, data); + } + } + else if( this.texture_type == GL.TEXTURE_3D ) + { + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false ); //standard does not allow this flags for 3D textures + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false ); + gl.texImage3D( this.texture_type, mipmap_level, internal_format, width, height, this.depth >> mipmap_level, 0, this.format, this.type, data); + } + else if( this.texture_type == GL.TEXTURE_CUBE_MAP ) + gl.texImage2D( gl.TEXTURE_CUBE_MAP_POSITIVE_X + (options.cubemap_face || 0), mipmap_level, internal_format, width, height, 0, this.format, this.type, data); + else + throw("cannot uploadData for this texture type"); + + this.data = data; //should I clone it? + + if (!skip_mipmaps && this.minFilter && this.minFilter != gl.NEAREST && this.minFilter != gl.LINEAR) { + gl.generateMipmap(this.texture_type); + this.has_mipmaps = true; + } + gl.bindTexture(this.texture_type, null); //disable +} + +//When creating cubemaps this is helpful + +/*THIS WORKS old +Texture.cubemap_camera_parameters = [ + { type:"posX", dir: vec3.fromValues(-1,0,0), up: vec3.fromValues(0,1,0), right: vec3.fromValues(0,0,-1) }, + { type:"negX", dir: vec3.fromValues(1,0,0), up: vec3.fromValues(0,1,0), right: vec3.fromValues(0,0,1) }, + { type:"posY", dir: vec3.fromValues(0,-1,0), up: vec3.fromValues(0,0,-1), right: vec3.fromValues(1,0,0) }, + { type:"negY", dir: vec3.fromValues(0,1,0), up: vec3.fromValues(0,0,1), right: vec3.fromValues(-1,0,0) }, + { type:"posZ", dir: vec3.fromValues(0,0,-1), up: vec3.fromValues(0,1,0), right: vec3.fromValues(1,0,0) }, + { type:"negZ", dir: vec3.fromValues(0,0,1), up: vec3.fromValues(0,1,0), right: vec3.fromValues(-1,0,0) } +]; +*/ + +//THIS works +Texture.cubemap_camera_parameters = [ + { type:"posX", dir: vec3.fromValues(1,0,0), up: vec3.fromValues(0,1,0), right: vec3.fromValues(0,0,-1) }, + { type:"negX", dir: vec3.fromValues(-1,0,0), up: vec3.fromValues(0,1,0), right: vec3.fromValues(0,0,1) }, + { type:"posY", dir: vec3.fromValues(0,1,0), up: vec3.fromValues(0,0,-1), right: vec3.fromValues(1,0,0) }, + { type:"negY", dir: vec3.fromValues(0,-1,0), up: vec3.fromValues(0,0,1), right: vec3.fromValues(1,0,0) }, + { type:"posZ", dir: vec3.fromValues(0,0,1), up: vec3.fromValues(0,1,0), right: vec3.fromValues(1,0,0) }, + { type:"negZ", dir: vec3.fromValues(0,0,-1), up: vec3.fromValues(0,1,0), right: vec3.fromValues(-1,0,0) } +]; + + + +/** +* Render to texture using FBO, just pass the callback to a rendering function and the content of the texture will be updated +* If the texture is a cubemap, the callback will be called six times, once per face, the number of the face is passed as a second parameter +* for further info about how to set up the propper cubemap camera, check the GL.Texture.cubemap_camera_parameters with the direction and up vector for every face. +* +* Keep in mind that it tries to reuse the last renderbuffer for the depth, and if it cannot (different size) it creates a new one (throwing the old) +* @method drawTo +* @param {Function} callback function that does all the rendering inside this texture +*/ +Texture.prototype.drawTo = function(callback, params) +{ + var gl = this.gl; + + //if(this.format == gl.DEPTH_COMPONENT) + // throw("cannot use drawTo in depth textures, use Texture.drawToColorAndDepth"); + + var v = gl.getViewport(); + var now = GL.getTime(); + + var old_fbo = gl.getParameter( gl.FRAMEBUFFER_BINDING ); + + var framebuffer = gl._framebuffer = gl._framebuffer || gl.createFramebuffer(); + gl.bindFramebuffer( gl.FRAMEBUFFER, framebuffer ); + + //this code allows to reuse old renderbuffers instead of creating and destroying them for every frame + var renderbuffer = null; + + if( Texture.use_renderbuffer_pool ) //create a renderbuffer pool + { + if(!gl._renderbuffers_pool) + gl._renderbuffers_pool = {}; + //generate unique key for this renderbuffer + var key = this.width + ":" + this.height; + + //reuse or create new one + if( gl._renderbuffers_pool[ key ] ) //Reuse old + { + renderbuffer = gl._renderbuffers_pool[ key ]; + renderbuffer.time = now; + gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer ); + } + else + { + //create temporary buffer + gl._renderbuffers_pool[ key ] = renderbuffer = gl.createRenderbuffer(); + renderbuffer.time = now; + renderbuffer.width = this.width; + renderbuffer.height = this.height; + gl.bindRenderbuffer( gl.RENDERBUFFER, renderbuffer ); + + //destroy after one minute + setTimeout( inner_check_destroy.bind(renderbuffer), 1000*60 ); + } + } + else + { + renderbuffer = gl._renderbuffer = gl._renderbuffer || gl.createRenderbuffer(); + renderbuffer.width = this.width; + renderbuffer.height = this.height; + gl.bindRenderbuffer( gl.RENDERBUFFER, renderbuffer ); + } + + + //bind render buffer for depth or color + if( this.format === gl.DEPTH_COMPONENT ) + gl.renderbufferStorage( gl.RENDERBUFFER, gl.RGBA4, this.width, this.height); + else + gl.renderbufferStorage( gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, this.width, this.height); + + + //clears memory from unused buffer + function inner_check_destroy() + { + if( GL.getTime() - this.time >= 1000*60 ) + { + //console.log("Buffer cleared"); + gl.deleteRenderbuffer( gl._renderbuffers_pool[ key ] ); + delete gl._renderbuffers_pool[ key ]; + } + else + setTimeout( inner_check_destroy.bind(this), 1000*60 ); + } + + + //create to store depth + /* + if (this.width != renderbuffer.width || this.height != renderbuffer.height ) { + renderbuffer.width = this.width; + renderbuffer.height = this.height; + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, this.width, this.height); + } + */ + + gl.viewport(0, 0, this.width, this.height); + + //if(gl._current_texture_drawto) + // throw("Texture.drawTo: Cannot use drawTo from inside another drawTo"); + + gl._current_texture_drawto = this; + gl._current_fbo_color = framebuffer; + gl._current_fbo_depth = renderbuffer; + + if(this.texture_type == gl.TEXTURE_2D) + { + if( this.format !== gl.DEPTH_COMPONENT ) + { + gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.handler, 0 ); + gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuffer ); + } + else + { + gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, renderbuffer ); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, this.handler, 0); + } + callback(this, params); + } + else if(this.texture_type == gl.TEXTURE_CUBE_MAP) + { + //bind the fixed ones out of the loop to save calls + if( this.format !== gl.DEPTH_COMPONENT ) + gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuffer ); + else + gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, renderbuffer ); + + //for every face of the cubemap + for(var i = 0; i < 6; i++) + { + if( this.format !== gl.DEPTH_COMPONENT ) + gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, this.handler, 0); + else + gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, this.handler, 0 ); + callback(this,i, params); + } + } + + this.data = null; + + gl._current_texture_drawto = null; + gl._current_fbo_color = null; + gl._current_fbo_depth = null; + + gl.bindFramebuffer( gl.FRAMEBUFFER, old_fbo ); + gl.bindRenderbuffer(gl.RENDERBUFFER, null); + gl.viewport(v[0], v[1], v[2], v[3]); + + return this; +} + +/** +* Static version of drawTo meant to be used with several buffers +* @method drawToColorAndDepth +* @param {Texture} color_texture +* @param {Texture} depth_texture +* @param {Function} callback +*/ +Texture.drawTo = function( color_textures, callback, depth_texture ) +{ + var w = -1, + h = -1, + type = null; + + if(!color_textures && !depth_texture) + throw("Textures missing in drawTo"); + + if(color_textures && color_textures.length) + { + for(var i = 0; i < color_textures.length; i++) + { + var t = color_textures[i]; + if(w == -1) + w = t.width; + else if(w != t.width) + throw("Cannot use Texture.drawTo if textures have different dimensions"); + if(h == -1) + h = t.height; + else if(h != t.height) + throw("Cannot use Texture.drawTo if textures have different dimensions"); + if(type == null) //first one defines the type + type = t.type; + else if (type != t.type) + throw("Cannot use Texture.drawTo if textures have different data type, all must have the same type"); + } + } + else + { + w = depth_texture.width; + h = depth_texture.height; + } + + var ext = gl.extensions["WEBGL_draw_buffers"]; + if(!ext && color_textures && color_textures.length > 1) + throw("Rendering to several textures not supported"); + + var v = gl.getViewport(); + gl._framebuffer = gl._framebuffer || gl.createFramebuffer(); + gl.bindFramebuffer( gl.FRAMEBUFFER, gl._framebuffer ); + + gl.viewport( 0, 0, w, h ); + + var renderbuffer = null; + if( depth_texture && depth_texture.format !== gl.DEPTH_COMPONENT || depth_texture.type != gl.UNSIGNED_INT ) + throw("Depth texture must be of format: gl.DEPTH_COMPONENT and type: gl.UNSIGNED_INT"); + + if( depth_texture ) + { + gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depth_texture.handler, 0); + } + else //create a temporary depth renderbuffer + { + //create renderbuffer for depth + renderbuffer = gl._renderbuffer = gl._renderbuffer || gl.createRenderbuffer(); + renderbuffer.width = w; + renderbuffer.height = h; + gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer ); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, w, h); + + gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuffer ); + } + + if( color_textures ) + { + var order = []; //draw_buffers request the use of an array with the order of the attachments + for(var i = 0; i < color_textures.length; i++) + { + var t = color_textures[i]; + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i, gl.TEXTURE_2D, t.handler, 0); + order.push( gl.COLOR_ATTACHMENT0 + i ); + } + + if(color_textures.length > 1) + ext.drawBuffersWEBGL( order ); + } + else //create temporary color render buffer + { + var color_renderbuffer = this._color_renderbuffer = this._color_renderbuffer || gl.createRenderbuffer(); + color_renderbuffer.width = w; + color_renderbuffer.height = h; + + gl.bindRenderbuffer( gl.RENDERBUFFER, color_renderbuffer ); + gl.renderbufferStorage( gl.RENDERBUFFER, gl.RGBA4, w, h ); + + gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, color_renderbuffer ); + } + + var complete = gl.checkFramebufferStatus( gl.FRAMEBUFFER ); + if(complete !== gl.FRAMEBUFFER_COMPLETE) + throw("FBO not complete: " + complete); + + callback(); + + //clear data + if(color_textures.length) + for(var i = 0; i < color_textures.length; ++i) + color_textures[i].data = null; + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + gl.viewport(v[0], v[1], v[2], v[3]); +} + +/** +* Similar to drawTo but it also stores the depth in a depth texture +* @method drawToColorAndDepth +* @param {Texture} color_texture +* @param {Texture} depth_texture +* @param {Function} callback +*/ +Texture.drawToColorAndDepth = function( color_texture, depth_texture, callback ) { + var gl = color_texture.gl; //static function + + if(depth_texture.width != color_texture.width || depth_texture.height != color_texture.height) + throw("Different size between color texture and depth texture"); + + var v = gl.getViewport(); + + gl._framebuffer = gl._framebuffer || gl.createFramebuffer(); + + gl.bindFramebuffer( gl.FRAMEBUFFER, gl._framebuffer); + + gl.viewport(0, 0, color_texture.width, color_texture.height); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, color_texture.handler, 0); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depth_texture.handler, 0); + + callback(); + + color_texture.data = null; + depth_texture.data = null; + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + gl.viewport(v[0], v[1], v[2], v[3]); +} + + + +/** +* Copy content of one texture into another +* TODO: check using copyTexImage2D +* @method copyTo +* @param {GL.Texture} target_texture +* @param {GL.Shader} [shader=null] optional shader to apply while copying +* @param {Object} [uniforms=null] optional uniforms for the shader +*/ +Texture.prototype.copyTo = function( target_texture, shader, uniforms ) { + var that = this; + var gl = this.gl; + if(!target_texture) + throw("target_texture required"); + + //save state + var previous_fbo = gl.getParameter( gl.FRAMEBUFFER_BINDING ); + var viewport = gl.getViewport(); + + if(!shader) + shader = this.texture_type == gl.TEXTURE_2D ? GL.Shader.getScreenShader() : GL.Shader.getCubemapCopyShader(); + + //render + gl.disable( gl.BLEND ); + gl.disable( gl.DEPTH_TEST ); + if(shader && uniforms) + shader.uniforms( uniforms ); + + //reuse fbo + var fbo = gl.__copy_fbo; + if(!fbo) + fbo = gl.__copy_fbo = gl.createFramebuffer(); + gl.bindFramebuffer( gl.FRAMEBUFFER, fbo ); + + gl.viewport(0,0,target_texture.width, target_texture.height); + if(this.texture_type == gl.TEXTURE_2D) + { + //regular color texture + if(this.format !== gl.DEPTH_COMPONENT && this.format !== gl.DEPTH_STENCIL ) + { + /* doesnt work + if( this.width == target_texture.width && this.height == target_texture.height && this.format == target_texture.format) + { + gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.handler, 0); + gl.bindTexture( target_texture.texture_type, target_texture.handler ); + gl.copyTexImage2D( target_texture.texture_type, 0, this.format, 0, 0, target_texture.width, target_texture.height, 0); + } + else + */ + { + gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, target_texture.handler, 0); + this.toViewport( shader ); + } + } + else //copying a depth texture is harder + { + var color_renderbuffer = gl._color_renderbuffer = gl._color_renderbuffer || gl.createRenderbuffer(); + var w = color_renderbuffer.width = target_texture.width; + var h = color_renderbuffer.height = target_texture.height; + + //attach color render buffer + gl.bindRenderbuffer( gl.RENDERBUFFER, color_renderbuffer ); + gl.renderbufferStorage( gl.RENDERBUFFER, gl.RGBA4, w, h ); + gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, color_renderbuffer ); + + //attach depth texture + var attachment_point = target_texture.format == gl.DEPTH_STENCIL ? gl.DEPTH_STENCIL_ATTACHMENT : gl.DEPTH_ATTACHMENT; + gl.framebufferTexture2D( gl.FRAMEBUFFER, attachment_point, gl.TEXTURE_2D, target_texture.handler, 0); + + var complete = gl.checkFramebufferStatus( gl.FRAMEBUFFER ); + if(complete !== gl.FRAMEBUFFER_COMPLETE) + throw("FBO not complete: " + complete); + + //enable depth test? + gl.enable( gl.DEPTH_TEST ); + gl.depthFunc( gl.ALWAYS ); + gl.colorMask( false,false,false,false ); + //call shader that overwrites depth values + shader = GL.Shader.getCopyDepthShader(); + this.toViewport( shader ); + gl.colorMask( true,true,true,true ); + gl.disable( gl.DEPTH_TEST ); + gl.depthFunc( gl.LEQUAL ); + gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, null ); + gl.framebufferTexture2D( gl.FRAMEBUFFER, attachment_point, gl.TEXTURE_2D, null, 0); + } + } + else if(this.texture_type == gl.TEXTURE_CUBE_MAP) + { + shader.uniforms({u_texture: 0}); + var rot_matrix = GL.temp_mat3; + for(var i = 0; i < 6; i++) + { + gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, target_texture.handler, 0); + var face_info = GL.Texture.cubemap_camera_parameters[ i ]; + mat3.identity( rot_matrix ); + rot_matrix.set( face_info.right, 0 ); + rot_matrix.set( face_info.up, 3 ); + rot_matrix.set( face_info.dir, 6 ); + //mat3.invert(rot_matrix,rot_matrix); + this.toViewport( shader,{ u_rotation: rot_matrix }); + } + } + + //restore previous state + gl.setViewport(viewport); //restore viewport + gl.bindFramebuffer( gl.FRAMEBUFFER, previous_fbo ); //restore fbo + + //generate mipmaps when needed + if (target_texture.minFilter && target_texture.minFilter != gl.NEAREST && target_texture.minFilter != gl.LINEAR) { + target_texture.bind(); + gl.generateMipmap(target_texture.texture_type); + target_texture.has_mipmaps = true; + } + + target_texture.data = null; + gl.bindTexture( target_texture.texture_type, null ); //disable + return this; +} + + +/** +* Similar to CopyTo, but more specific, only for color texture_2D. It doesnt change the blend flag +* @method blit +* @param {GL.Texture} target_texture +* @param {GL.Shader} [shader=null] optional shader to apply while copying +* @param {Object} [uniforms=null] optional uniforms for the shader +*/ +Texture.prototype.blit = (function(){ + var viewport = new Float32Array(4); + + return function( target_texture, shader, uniforms ) { + var that = this; + var gl = this.gl; + + if ( this.texture_type != gl.TEXTURE_2D || this.format === gl.DEPTH_COMPONENT || this.format === gl.DEPTH_STENCIL ) + throw("blit only support TEXTURE_2D of RGB or RGBA. use copyTo instead"); + + //save state + var previous_fbo = gl.getParameter( gl.FRAMEBUFFER_BINDING ); + viewport.set( gl.viewport_data ); + + shader = shader || GL.Shader.getScreenShader(); + if(shader && uniforms) + shader.uniforms( uniforms ); + + //reuse fbo + var fbo = gl.__copy_fbo; + if(!fbo) + fbo = gl.__copy_fbo = gl.createFramebuffer(); + gl.bindFramebuffer( gl.FRAMEBUFFER, fbo ); + + gl.viewport(0,0,target_texture.width, target_texture.height); + gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, target_texture.handler, 0); + + this.bind(0); + shader.draw( GL.Mesh.getScreenQuad(), gl.TRIANGLES ); + + //restore previous state + gl.setViewport(viewport); //restore viewport + gl.bindFramebuffer( gl.FRAMEBUFFER, previous_fbo ); //restore fbo + + target_texture.data = null; + gl.bindTexture( target_texture.texture_type, null ); //disable + return this; + } +})(); + +/** +* Render texture in a quad to full viewport size +* @method toViewport +* @param {Shader} shader to apply, otherwise a default textured shader is applied [optional] +* @param {Object} uniforms for the shader if needed [optional] +*/ +Texture.prototype.toViewport = function(shader, uniforms) +{ + shader = shader || Shader.getScreenShader(); + var mesh = Mesh.getScreenQuad(); + this.bind(0); + //shader.uniforms({u_texture: 0}); //never changes + if(uniforms) + shader.uniforms(uniforms); + shader.draw( mesh, gl.TRIANGLES ); +} + +/** +* Fills the texture with a constant color (uses gl.clear) +* @method fill +* @param {vec4} color rgba +* @param {boolean} skip_mipmaps if true the mipmaps wont be updated +*/ +Texture.prototype.fill = function(color, skip_mipmaps ) +{ + var old_color = gl.getParameter( gl.COLOR_CLEAR_VALUE ); + gl.clearColor( color[0], color[1], color[2], color[3] ); + this.drawTo( function() { + gl.clear( gl.COLOR_BUFFER_BIT ); + }); + gl.clearColor( old_color[0], old_color[1], old_color[2], old_color[3] ); + + if (!skip_mipmaps && this.minFilter && this.minFilter != gl.NEAREST && this.minFilter != gl.LINEAR ) { + this.bind(); + gl.generateMipmap( this.texture_type ); + this.has_mipmaps = true; + } +} + +/** +* Render texture in a quad of specified area +* @method renderQuad +* @param {number} x +* @param {number} y +* @param {number} width +* @param {number} height +*/ +Texture.prototype.renderQuad = (function() { + //static variables: less garbage + var identity = mat3.create(); + var pos = vec2.create(); + var size = vec2.create(); + var white = vec4.fromValues(1,1,1,1); + + return (function(x,y,w,h, shader, uniforms) + { + pos[0] = x; pos[1] = y; + size[0] = w; size[1] = h; + + shader = shader || Shader.getQuadShader(this.gl); + var mesh = Mesh.getScreenQuad(this.gl); + this.bind(0); + shader.uniforms({u_texture: 0, u_position: pos, u_color: white, u_size: size, u_viewport: gl.viewport_data.subarray(2,4), u_transform: identity }); + if(uniforms) + shader.uniforms(uniforms); + shader.draw( mesh, gl.TRIANGLES ); + }); +})(); + + +/** +* Applies a blur filter of 5x5 pixels to the texture (be careful using it, it is slow) +* @method applyBlur +* @param {Number} offsetx scalar that multiplies the offset when fetching pixels horizontally (default 1) +* @param {Number} offsety scalar that multiplies the offset when fetching pixels vertically (default 1) +* @param {Number} intensity scalar that multiplies the result (default 1) +* @param {Texture} output_texture [optional] if not passed the output is the own texture +* @param {Texture} temp_texture blur needs a temp texture, if not supplied it will use the temporary textures pool +* @return {Texture} returns the temp_texture in case you want to reuse it +*/ +Texture.prototype.applyBlur = function( offsetx, offsety, intensity, output_texture, temp_texture ) +{ + var that = this; + var gl = this.gl; + if(offsetx === undefined) + offsetx = 1; + if(offsety === undefined) + offsety = 1; + gl.disable( gl.DEPTH_TEST ); + gl.disable( gl.BLEND ); + output_texture = output_texture || this; + var is_temp = !temp_texture; + + //if(this === output_texture && this.texture_type === gl.TEXTURE_CUBE_MAP ) + // throw("cannot use applyBlur in a texture with itself when blurring a CUBE_MAP"); + if(temp_texture === output_texture) + throw("cannot use applyBlur in a texture using as temporary itself"); + + if(output_texture && this.texture_type !== output_texture.texture_type ) + throw("cannot use applyBlur with textures of different texture_type"); + + //if(this.width != output_texture.width || this.height != output_texture.height) + // throw("cannot use applyBlur with an output texture of different size, it doesnt work"); + + //save state + var current_fbo = gl.getParameter( gl.FRAMEBUFFER_BINDING ); + var viewport = gl.getViewport(); + + //reuse fbo + var fbo = gl.__copy_fbo; + if(!fbo) + fbo = gl.__copy_fbo = gl.createFramebuffer(); + gl.bindFramebuffer( gl.FRAMEBUFFER, fbo ); + gl.viewport(0,0, this.width, this.height); + + if( this.texture_type === gl.TEXTURE_2D ) + { + var shader = GL.Shader.getBlurShader(); + + if(!temp_texture) + temp_texture = GL.Texture.getTemporary( this.width, this.height, this ); + + //horizontal blur + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, temp_texture.handler, 0); + this.toViewport( shader, {u_texture: 0, u_intensity: intensity, u_offset: [0, offsety / this.height ] }); + + //vertical blur + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, output_texture.handler, 0); + gl.viewport(0,0,output_texture.width, output_texture.height); + temp_texture.toViewport( shader, {u_intensity: intensity, u_offset: [offsetx / temp_texture.width, 0] }); + + if(is_temp) + GL.Texture.releaseTemporary( temp_texture ); + } + else if( this.texture_type === gl.TEXTURE_CUBE_MAP ) + { + //var weights = new Float32Array([ 0.16/0.98, 0.15/0.98, 0.12/0.98, 0.09/0.98, 0.05/0.98 ]); + //var weights = new Float32Array([ 0.05/0.98, 0.09/0.98, 0.12/0.98, 0.15/0.98, 0.16/0.98, 0.15/0.98, 0.12/0.98, 0.09/0.98, 0.05/0.98, 0.0 ]); //extra 0 to avoid mat3 + var shader = GL.Shader.getCubemapBlurShader(); + shader.uniforms({u_texture: 0, u_intensity: intensity, u_offset: [ offsetx / this.width, offsety / this.height ] }); + this.bind(0); + var mesh = Mesh.getScreenQuad(); + mesh.bindBuffers( shader ); + shader.bind(); + + var destination = null; + + if(!temp_texture && output_texture == this) //we need a temporary texture + destination = temp_texture = GL.Texture.getTemporary( output_texture.width, output_texture.height, output_texture ); + else + destination = output_texture; //blur directly to output texture + + var rot_matrix = GL.temp_mat3; + for(var i = 0; i < 6; ++i) + { + gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, destination.handler, 0); + var face_info = GL.Texture.cubemap_camera_parameters[ i ]; + mat3.identity(rot_matrix); + rot_matrix.set( face_info.right, 0 ); + rot_matrix.set( face_info.up, 3 ); + rot_matrix.set( face_info.dir, 6 ); + //mat3.invert(rot_matrix,rot_matrix); + shader._setUniform( "u_rotation", rot_matrix ); + gl.drawArrays( gl.TRIANGLES, 0, 6 ); + } + + mesh.unbindBuffers( shader ); + + if(temp_texture) //copy back + temp_texture.copyTo( output_texture ); + + if(temp_texture && is_temp) //release temp + GL.Texture.releaseTemporary( temp_texture ); + } + + //restore previous state + gl.setViewport(viewport); //restore viewport + gl.bindFramebuffer( gl.FRAMEBUFFER, current_fbo ); //restore fbo + + output_texture.data = null; + + //generate mipmaps when needed + if (output_texture.minFilter && output_texture.minFilter != gl.NEAREST && output_texture.minFilter != gl.LINEAR) { + output_texture.bind(); + gl.generateMipmap(output_texture.texture_type); + output_texture.has_mipmaps = true; + } + + gl.bindTexture( output_texture.texture_type, null ); //disable +} + + +/** +* Loads and uploads a texture from a url +* @method Texture.fromURL +* @param {String} url +* @param {Object} options +* @param {Function} on_complete +* @return {Texture} the texture +*/ +Texture.fromURL = function( url, options, on_complete, gl ) { + gl = gl || global.gl; + + options = options || {}; + options = Object.create(options); //creates a new options using the old one as prototype + + var texture = options.texture || new GL.Texture(1, 1, options, gl); + + if(url.length < 64) + texture.url = url; + texture.bind(); + var default_color = options.temp_color || Texture.loading_color; + //Texture.setUploadOptions(options); + gl.pixelStorei(gl.UNPACK_ALIGNMENT, 4); + var temp_color = options.type == gl.FLOAT ? new Float32Array(default_color) : new Uint8Array(default_color); + gl.texImage2D( gl.TEXTURE_2D, 0, texture.format, texture.width, texture.height, 0, texture.format, texture.type, temp_color ); + gl.bindTexture( texture.texture_type, null ); //disable + texture.ready = false; + + var ext = null; + if( options.extension ) //to force format + ext = options.extension; + + if(!ext && url.length < 512) //avoid base64 urls + { + var base = url; + var pos = url.indexOf("?"); + if(pos != -1) + base = url.substr(0,pos); + pos = base.lastIndexOf("."); + if(pos != -1) + ext = base.substr(pos+1).toLowerCase(); + } + + if( ext == "dds") + { + var ext = gl.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc") || gl.getExtension("WEBGL_compressed_texture_s3tc"); + var new_texture = new GL.Texture(0,0, options, gl); + DDS.loadDDSTextureEx(gl, ext, url, new_texture.handler, true, function(t) { + texture.texture_type = t.texture_type; + texture.handler = t; + delete texture["ready"]; //texture.ready = true; + if(on_complete) + on_complete(texture, url); + }); + } + else if( ext == "tga" ) + { + HttpRequest( url, null, function(data) { + var img_data = GL.Texture.parseTGA(data); + if(!img_data) + return; + options.texture = texture; + if(img_data.format == "RGB") + texture.format = gl.RGB; + texture = GL.Texture.fromMemory( img_data.width, img_data.height, img_data.pixels, options ); + delete texture["ready"]; //texture.ready = true; + if(on_complete) + on_complete( texture, url ); + },null,{ binary: true }); + } + else //png,jpg,webp,... + { + var image = new Image(); + image.src = url; + var that = this; + image.onload = function() + { + options.texture = texture; + GL.Texture.fromImage(this, options); + delete texture["ready"]; //texture.ready = true; + if(on_complete) + on_complete(texture, url); + } + image.onerror = function() + { + if(on_complete) + on_complete(null); + } + } + + return texture; +}; + +Texture.parseTGA = function(data) +{ + if(!data || data.constructor !== ArrayBuffer) + throw( "TGA: data must be ArrayBuffer"); + data = new Uint8Array(data); + var TGAheader = new Uint8Array( [0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0] ); + var TGAcompare = data.subarray(0,12); + for(var i = 0; i < TGAcompare.length; i++) + if(TGAheader[i] != TGAcompare[i]) + { + console.error("TGA header is not valid"); + return null; //not a TGA + } + + var header = data.subarray(12,18); + var img = {}; + img.width = header[1] * 256 + header[0]; + img.height = header[3] * 256 + header[2]; + img.bpp = header[4]; + img.bytesPerPixel = img.bpp / 8; + img.imageSize = img.width * img.height * img.bytesPerPixel; + img.pixels = data.subarray(18,18+img.imageSize); + img.pixels = new Uint8Array( img.pixels ); //clone + if( (header[5] & (1<<4)) == 0) //hack, needs swap + { + //TGA comes in BGR format so we swap it, this is slooooow + for(var i = 0; i < img.imageSize; i+= img.bytesPerPixel) + { + var temp = img.pixels[i]; + img.pixels[i] = img.pixels[i+2]; + img.pixels[i+2] = temp; + } + header[5] |= 1<<4; //mark as swaped + img.format = img.bpp == 32 ? "RGBA" : "RGB"; + } + else + img.format = img.bpp == 32 ? "RGBA" : "RGB"; + //some extra bytes to avoid alignment problems + //img.pixels = new Uint8Array( img.imageSize + 14); + //img.pixels.set( data.subarray(18,18+img.imageSize), 0); + img.flipY = true; + //img.format = img.bpp == 32 ? "BGRA" : "BGR"; + //trace("TGA info: " + img.width + "x" + img.height ); + return img; +} + +/** +* Create a texture from an Image +* @method Texture.fromImage +* @param {Image} image +* @param {Object} options +* @return {Texture} the texture +*/ +Texture.fromImage = function( image, options ) { + options = options || {}; + + var texture = options.texture || new GL.Texture( image.width, image.height, options); + texture.uploadImage( image, options ); + + texture.bind(); + gl.texParameteri(texture.texture_type, gl.TEXTURE_MAG_FILTER, texture.magFilter ); + gl.texParameteri(texture.texture_type, gl.TEXTURE_MIN_FILTER, texture.minFilter ); + gl.texParameteri(texture.texture_type, gl.TEXTURE_WRAP_S, texture.wrapS ); + gl.texParameteri(texture.texture_type, gl.TEXTURE_WRAP_T, texture.wrapT ); + + if (GL.isPowerOfTwo(texture.width) && GL.isPowerOfTwo(texture.height) ) + { + if( options.minFilter && options.minFilter != gl.NEAREST && options.minFilter != gl.LINEAR) + { + texture.bind(); + gl.generateMipmap(texture.texture_type); + texture.has_mipmaps = true; + } + } + else + { + //no mipmaps supported + gl.texParameteri(texture.texture_type, gl.TEXTURE_MIN_FILTER, GL.LINEAR ); + gl.texParameteri(texture.texture_type, gl.TEXTURE_WRAP_S, GL.CLAMP_TO_EDGE ); + gl.texParameteri(texture.texture_type, gl.TEXTURE_WRAP_T, GL.CLAMP_TO_EDGE ); + texture.has_mipmaps = false; + } + gl.bindTexture(texture.texture_type, null); //disable + texture.data = image; + if(options.keep_image) + texture.img = image; + return texture; +}; + +/** +* Create a texture from a Video +* @method Texture.fromVideo +* @param {Video} video +* @param {Object} options +* @return {Texture} the texture +*/ +Texture.fromVideo = function(video, options) { + options = options || {}; + + var texture = options.texture || new GL.Texture(video.videoWidth, video.videoHeight, options); + texture.bind(); + texture.uploadImage( video, options ); + if (options.minFilter && options.minFilter != gl.NEAREST && options.minFilter != gl.LINEAR) { + texture.bind(); + gl.generateMipmap(texture.texture_type); + texture.has_mipmaps = true; + texture.data = video; + } + gl.bindTexture(texture.texture_type, null); //disable + return texture; +}; + +/** +* Create a clone of a texture +* @method Texture.fromTexture +* @param {Texture} old_texture +* @param {Object} options +* @return {Texture} the texture +*/ +Texture.fromTexture = function( old_texture, options) { + options = options || {}; + var texture = new GL.Texture( old_texture.width, old_texture.height, options ); + old_texture.copyTo( texture ); + return texture; +}; + +Texture.prototype.clone = function( options ) +{ + var old_options = this.getProperties(); + if(options) + for(var i in options) + old_options[i] = options[i]; + return Texture.fromTexture( this, old_options); +} + +/** +* Create a texture from an ArrayBuffer containing the pixels +* @method Texture.fromTexture +* @param {number} width +* @param {number} height +* @param {ArrayBuffer} pixels +* @param {Object} options +* @return {Texture} the texture +*/ +Texture.fromMemory = function( width, height, pixels, options) //format in options as format +{ + options = options || {}; + + var texture = options.texture || new GL.Texture(width, height, options); + Texture.setUploadOptions(options); + texture.bind(); + + if(pixels.constructor === Array) + { + if(options.type == gl.FLOAT) + pixels = new Float32Array( pixels ); + else if(options.type == GL.HALF_FLOAT || options.type == GL.HALF_FLOAT_OES) + pixels = new Uint16Array( pixels ); //gl.UNSIGNED_SHORT_4_4_4_4 is only for texture that are SHORT per pixel, not per channel! + else + pixels = new Uint8Array( pixels ); + } + + gl.texImage2D( gl.TEXTURE_2D, 0, texture.format, width, height, 0, texture.format, texture.type, pixels ); + texture.width = width; + texture.height = height; + texture.data = pixels; + if (options.minFilter && options.minFilter != gl.NEAREST && options.minFilter != gl.LINEAR) { + gl.generateMipmap(gl.TEXTURE_2D); + texture.has_mipmaps = true; + } + gl.bindTexture(texture.texture_type, null); //disable + return texture; +}; + +/** +* Create a texture from an ArrayBuffer containing the pixels +* @method Texture.fromDDSInMemory +* @param {ArrayBuffer} DDS data +* @param {Object} options +* @return {Texture} the texture +*/ +Texture.fromDDSInMemory = function(data, options) //format in options as format +{ + options = options || {}; + + var texture = options.texture || new GL.Texture(0, 0, options); + GL.Texture.setUploadOptions(options); + texture.bind(); + + var ext = gl.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc") || gl.getExtension("WEBGL_compressed_texture_s3tc"); + DDS.loadDDSTextureFromMemoryEx(gl, ext, data, texture, true ); + + gl.bindTexture(texture.texture_type, null); //disable + return texture; +}; + +/** +* Create a generative texture from a shader ( must GL.Shader.getScreenShader as reference for the shader ) +* @method Texture.fromShader +* @param {number} width +* @param {number} height +* @param {Shader} shader +* @param {Object} options +* @return {Texture} the texture +*/ +Texture.fromShader = function(width, height, shader, options) { + options = options || {}; + + var texture = new GL.Texture( width, height, options ); + //copy content + texture.drawTo(function() { + gl.disable( gl.BLEND ); + gl.disable( gl.DEPTH_TEST ); + gl.disable( gl.CULL_FACE ); + var mesh = Mesh.getScreenQuad(); + shader.draw( mesh ); + }); + + return texture; +}; + +/** +* Create a cubemap texture from a set of 6 images +* @method Texture.cubemapFromImages +* @param {Array} images +* @param {Object} options +* @return {Texture} the texture +*/ +Texture.cubemapFromImages = function(images, options) { + options = options || {}; + if(images.length != 6) + throw "missing images to create cubemap"; + + var width = images[0].width; + var height = images[0].height; + options.texture_type = gl.TEXTURE_CUBE_MAP; + + var texture = null; + + if(options.texture) + { + texture = options.texture; + texture.width = width; + texture.height = height; + } + else + texture = new GL.Texture( width, height, options ); + + Texture.setUploadOptions(options); + texture.bind(); + + try { + + for(var i = 0; i < 6; i++) + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X+i, 0, texture.format, texture.format, texture.type, images[i]); + texture.data = images; + } catch (e) { + if (location.protocol == 'file:') { + throw 'image not loaded for security reasons (serve this page over "http://" instead)'; + } else { + throw 'image not loaded for security reasons (image must originate from the same ' + + 'domain as this page or use Cross-Origin Resource Sharing)'; + } + } + if (options.minFilter && options.minFilter != gl.NEAREST && options.minFilter != gl.LINEAR) { + gl.generateMipmap(gl.TEXTURE_CUBE_MAP); + texture.has_mipmaps = true; + } + + texture.unbind(); + return texture; +}; + +/** +* Create a cubemap texture from a single image that contains all six images +* If it is a cross, it must be horizontally aligned, and options.is_cross must be equal to the column where the top and bottom are located (usually 1 or 2) +* otherwise it assumes the 6 images are arranged vertically, in the order of OpenGL: +X, -X, +Y, -Y, +Z, -Z +* @method Texture.cubemapFromImage +* @param {Image} image +* @param {Object} options +* @return {Texture} the texture +*/ +Texture.cubemapFromImage = function( image, options ) { + options = options || {}; + + if(image.width != (image.height / 6) && image.height % 6 != 0 && !options.faces && !options.is_polar ) + { + console.error( "Cubemap image not valid, only 1x6 (vertical) or 6x3 (cross) formats. Check size:", image.width, image.height ); + return null; + } + + var width = image.width; + var height = image.height; + + if(options.is_polar) + { + var size = options.size || GL.nearestPowerOfTwo( image.height ); + var temp_tex = GL.Texture.fromImage( image, { ignore_pot:true, wrap: gl.REPEAT, filter: gl.LINEAR } ); + var cubemap = new GL.Texture( size, size, { texture_type: gl.TEXTURE_CUBE_MAP, format: gl.RGBA }); + if(options.texture) + { + var old_tex = options.texture; + for(var i in cubemap) + old_tex[i] = cubemap[i]; + cubemap = old_tex; + } + var rot_matrix = mat3.create(); + var uniforms = { u_texture:0, u_rotation: rot_matrix }; + gl.disable( gl.DEPTH_TEST ); + gl.disable( gl.BLEND ); + var shader = GL.Shader.getPolarToCubemapShader(); + cubemap.drawTo(function(t,i){ + var face_info = GL.Texture.cubemap_camera_parameters[ i ]; + mat3.identity( rot_matrix ); + rot_matrix.set( face_info.right, 0 ); + rot_matrix.set( face_info.up, 3 ); + rot_matrix.set( face_info.dir, 6 ); + temp_tex.toViewport( shader, uniforms ); + }); + if(options.keep_image) + cubemap.img = image; + return cubemap; + } + else if(options.is_cross !== undefined) + { + options.faces = Texture.generateCubemapCrossFacesInfo(image.width, options.is_cross); + width = height = image.width / 4; + } + else if(options.faces) + { + width = options.width || options.faces[0].width; + height = options.height || options.faces[0].height; + } + else + height /= 6; + + if(width != height) + { + console.log("Texture not valid, width and height for every face must be square"); + return null; + } + + var size = width; + options.no_flip = true; + + var images = []; + for(var i = 0; i < 6; i++) + { + var canvas = createCanvas( size, size ); + var ctx = canvas.getContext("2d"); + if(options.faces) + ctx.drawImage(image, options.faces[i].x, options.faces[i].y, options.faces[i].width || size, options.faces[i].height || size, 0,0, size, size ); + else + ctx.drawImage(image, 0, height*i, width, height, 0,0, size, size ); + images.push(canvas); + //document.body.appendChild(canvas); //debug + } + + var texture = Texture.cubemapFromImages(images, options); + if(options.keep_image) + texture.img = image; + return texture; +}; + +/** +* Given the width and the height of an image, and in which column is the top and bottom sides of the cubemap, it gets the info to pass to Texture.cubemapFromImage in options.faces +* @method Texture.generateCubemapCrossFaces +* @param {number} width of the CROSS image (not the side image) +* @param {number} column the column where the top and the bottom is located +* @return {Object} object to pass to Texture.cubemapFromImage in options.faces +*/ +Texture.generateCubemapCrossFacesInfo = function(width, column) +{ + if(column === undefined) + column = 1; + var s = width / 4; + + return [ + { x: 2*s, y: s, width: s, height: s }, //+x + { x: 0, y: s, width: s, height: s }, //-x + { x: column*s, y: 0, width: s, height: s }, //+y + { x: column*s, y: 2*s, width: s, height: s }, //-y + { x: s, y: s, width: s, height: s }, //+z + { x: 3*s, y: s, width: s, height: s } //-z + ]; +} + +/** +* Create a cubemap texture from a single image url that contains the six images +* if it is a cross, it must be horizontally aligned, and options.is_cross must be equal to the column where the top and bottom are located (usually 1 or 2) +* otherwise it assumes the 6 images are arranged vertically. +* @method Texture.cubemapFromURL +* @param {Image} image +* @param {Object} options +* @param {Function} on_complete callback +* @return {Texture} the texture +*/ +Texture.cubemapFromURL = function( url, options, on_complete ) { + options = options || {}; + options = Object.create(options); //creates a new options using the old one as prototype + options.texture_type = gl.TEXTURE_CUBE_MAP; + var texture = options.texture || new GL.Texture(1, 1, options); + + texture.bind(); + Texture.setUploadOptions(options); + var default_color = options.temp_color || [0,0,0,255]; + var temp_color = options.type == gl.FLOAT ? new Float32Array(default_color) : new Uint8Array(default_color); + + for(var i = 0; i < 6; i++) + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X+i, 0, texture.format, 1, 1, 0, texture.format, texture.type, temp_color); + gl.bindTexture(texture.texture_type, null); //disable + texture.ready = false; + + var image = new Image(); + image.src = url; + var that = this; + image.onload = function() + { + options.texture = texture; + texture = GL.Texture.cubemapFromImage(this, options); + if(texture) + delete texture["ready"]; //texture.ready = true; + if(on_complete) + on_complete(texture); + } + + return texture; +}; + +/** +* returns an ArrayBuffer with the pixels in the texture, they are fliped in Y +* Warn: If cubemap it only returns the pixels of the first face! use getCubemapPixels instead +* @method getPixels +* @param {number} cubemap_face [optional] the index of the cubemap face to read (ignore if texture_2D) +* @param {number} mipmap level [optional, default is 0] +* @return {ArrayBuffer} the data ( Uint8Array, Uint16Array or Float32Array ) +*/ +Texture.prototype.getPixels = function( cubemap_face, mipmap_level ) +{ + mipmap_level = mipmap_level || 0; + var gl = this.gl; + var v = gl.getViewport(); + var old_fbo = gl.getParameter( gl.FRAMEBUFFER_BINDING ); + + if(this.format == gl.DEPTH_COMPONENT) + throw("cannot use getPixels in depth textures"); + + gl.disable( gl.DEPTH_TEST ); + + //reuse fbo + var fbo = gl.__copy_fbo; + if(!fbo) + fbo = gl.__copy_fbo = gl.createFramebuffer(); + gl.bindFramebuffer( gl.FRAMEBUFFER, fbo ); + + var buffer = null; + + var width = this.width >> mipmap_level; + var height = this.height >> mipmap_level; + gl.viewport(0, 0, width, height); + + if(this.texture_type == gl.TEXTURE_2D) + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.handler, mipmap_level); + else if(this.texture_type == gl.TEXTURE_CUBE_MAP) + gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + (cubemap_face || 0), this.handler, mipmap_level); + + var channels = this.format == gl.RGB ? 3 : 4; + channels = 4; //WEBGL DOES NOT SUPPORT READING 3 CHANNELS ONLY, YET... + var type = this.type; + //type = gl.UNSIGNED_BYTE; //WEBGL DOES NOT SUPPORT READING FLOAT seems, YET... 23/5/18 now it seems it does now + + if(type == gl.UNSIGNED_BYTE) + buffer = new Uint8Array( width * height * channels ); + else if(type == GL.HALF_FLOAT || type == GL.HALF_FLOAT_OES) //previously half float couldnot be read + buffer = new Uint16Array( width * height * channels ); //gl.UNSIGNED_SHORT_4_4_4_4 is only for texture that are SHORT per pixel, not per channel! + else + buffer = new Float32Array( width * height * channels ); + + gl.readPixels( 0,0, width, height, channels == 3 ? gl.RGB : gl.RGBA, type, buffer ); //NOT SUPPORTED FLOAT or RGB BY WEBGL YET + + //restore + gl.bindFramebuffer(gl.FRAMEBUFFER, old_fbo ); + gl.viewport(v[0], v[1], v[2], v[3]); + return buffer; +} + +/** +* uploads some pixels to the texture (see uploadData method for more options) +* @method setPixels +* @param {ArrayBuffer} data gl.UNSIGNED_BYTE or gl.FLOAT data +* @param {Boolean} no_flip do not flip in Y +* @param {Boolean} skip_mipmaps do not update mipmaps when possible +* @param {Number} cubemap_face if the texture is a cubemap, which face +*/ +Texture.prototype.setPixels = function( data, no_flip, skip_mipmaps, cubemap_face ) +{ + var options = { no_flip: no_flip }; + if(cubemap_face) + options.cubemap_face = cubemap_face; + this.uploadData( data, options, skip_mipmaps ); +} + +/** +* returns an array with six arrays containing the pixels of every cubemap face +* @method getCubemapPixels +* @return {Array} the array that has 6 typed arrays containing the pixels +*/ +Texture.prototype.getCubemapPixels = function() +{ + if(this.texture_type !== gl.TEXTURE_CUBE_MAP) + throw("this texture is not a cubemap"); + return [ this.getPixels(0), this.getPixels(1), this.getPixels(2), this.getPixels(3), this.getPixels(4), this.getPixels(5) ]; +} + +/** +* fills a cubemap given an array with typed arrays containing the pixels of 6 faces +* @method setCubemapPixels +* @param {Array} data array that has 6 typed arrays containing the pixels +* @param {bool} noflip if pixels should not be flipped according to Y +*/ +Texture.prototype.setCubemapPixels = function( data_array, no_flip ) +{ + if(this.texture_type !== gl.TEXTURE_CUBE_MAP) + throw("this texture is not a cubemap, it should be created with { texture_type: gl.TEXTURE_CUBE_MAP }"); + for(var i = 0; i < 6; ++i) + this.setPixels( data_array[i], no_flip, i != 5, i ); +} + +/** +* Copy texture content to a canvas +* @method toCanvas +* @param {Canvas} canvas must have the same size, if different the canvas will be resized +* @param {boolean} flip_y optional, flip vertically +* @param {Number} max_size optional, if it is supplied the canvas wont be bigger of max_size (the image will be scaled down) +*/ +Texture.prototype.toCanvas = function( canvas, flip_y, max_size ) +{ + max_size = max_size || 8192; + var gl = this.gl; + + var w = Math.min( this.width, max_size ); + var h = Math.min( this.height, max_size ); + + //cross + if(this.texture_type == gl.TEXTURE_CUBE_MAP) + { + w = w * 4; + h = h * 3; + } + + canvas = canvas || createCanvas( w, h ); + if(canvas.width != w) + canvas.width = w; + if(canvas.height != h) + canvas.height = h; + + var buffer = null; + if(this.texture_type == gl.TEXTURE_2D ) + { + if(this.width != w || this.height != h || this.type != gl.UNSIGNED_BYTE) //resize image to fit the canvas + { + //create a temporary texture + var temp = new GL.Texture(w,h,{ format: gl.RGBA, filter: gl.NEAREST }); + this.copyTo( temp ); + buffer = temp.getPixels(); + } + else + buffer = this.getPixels(); + + var ctx = canvas.getContext("2d"); + var pixels = ctx.getImageData(0,0,w,h); + pixels.data.set( buffer ); + ctx.putImageData(pixels,0,0); + + if(flip_y) + { + var temp = createCanvas(w,h); + var temp_ctx = temp.getContext("2d"); + temp_ctx.translate(0,temp.height); + temp_ctx.scale(1,-1); + temp_ctx.drawImage( canvas, 0, 0, temp.width, temp.height ); + ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height); + ctx.drawImage( temp, 0, 0 ); + } + } + else if(this.texture_type == gl.TEXTURE_CUBE_MAP ) + { + var temp_canvas = createCanvas( this.width, this.height ); + var temp_ctx = temp_canvas.getContext("2d"); + var info = GL.Texture.generateCubemapCrossFacesInfo( canvas.width, 1 ); + var ctx = canvas.getContext("2d"); + ctx.fillStyle = "black"; + ctx.fillRect(0,0,canvas.width, canvas.height ); + + var cubemap = this; + if(this.type != gl.UNSIGNED_BYTE) //convert pixels to uint8 as it is the only supported format by the canvas + { + //create a temporary texture + cubemap = new GL.Texture( this.width, this.height, { format: gl.RGBA, texture_type: gl.TEXTURE_CUBE_MAP, filter: gl.NEAREST, type: gl.UNSIGNED_BYTE }); + this.copyTo( cubemap ); + } + + for(var i = 0; i < 6; i++) + { + var pixels = temp_ctx.getImageData(0,0, temp_canvas.width, temp_canvas.height ); + buffer = cubemap.getPixels(i); + pixels.data.set( buffer ); + temp_ctx.putImageData(pixels,0,0); + ctx.drawImage( temp_canvas, info[i].x, info[i].y, temp_canvas.width, temp_canvas.height ); + } + } + + return canvas; +} + + +/** +* returns the texture file in binary format +* @method toBinary +* @param {Boolean} flip_y +* @return {ArrayBuffer} the arraybuffer of the file containing the image +*/ +Texture.binary_extension = "png"; +Texture.prototype.toBinary = function(flip_y, type) +{ + //dump to canvas + var canvas = this.toCanvas(null,flip_y); + //use the slow method (because its sync) + var data = canvas.toDataURL( type ); + var index = data.indexOf(","); + var base64_data = data.substr(index+1); + var binStr = atob( base64_data ); + var len = binStr.length, + arr = new Uint8Array(len); + for (var i=0; i 0 ) + console.warn("this texture is already in the textures pool"); + + var pool = gl._texture_pool; + if(!pool) + pool = gl._texture_pool = []; + tex._pool = getTime(); + pool.push( tex ); + + //do not store too much textures in the textures pool + if( pool.length > 20 ) + { + pool.sort( function(a,b) { return b._pool - a._pool } ); //sort by time + //pool.sort( function(a,b) { return a._key - b._key } ); //sort by size + var tex = pool.pop(); //free the last one + tex._pool = 0; + tex.delete(); + } +} + +//returns the next power of two bigger than size +Texture.nextPOT = function( size ) +{ + return Math.pow( 2, Math.ceil( Math.log(size) / Math.log(2) ) ); +} + +/** +* FBO for FrameBufferObjects, FBOs are used to store the render inside one or several textures +* Supports multibuffer and depthbuffer texture, useful for deferred rendering +* @namespace GL +* @class FBO +* @param {Array} color_textures an array containing the color textures, if not supplied a render buffer will be used +* @param {GL.Texture} depth_texture the depth texture, if not supplied a render buffer will be used +* @param {Bool} stencil create a stencil buffer? +* @constructor +*/ +function FBO( textures, depth_texture, stencil, gl ) +{ + gl = gl || global.gl; + this.gl = gl; + this._context_id = gl.context_id; + + if(textures && textures.constructor !== Array) + throw("FBO textures must be an Array"); + + this.handler = null; + this.width = -1; + this.height = -1; + this.color_textures = []; + this.depth_texture = null; + this.stencil = !!stencil; + + this._stencil_enabled = false; + this._num_binded_textures = 0; + + //assign textures + if((textures && textures.length) || depth_texture) + this.setTextures( textures, depth_texture ); + + //save state + this._old_fbo_handler = null; + this._old_viewport = new Float32Array(4); + this.order = null; +} + +GL.FBO = FBO; + +/** +* Changes the textures binded to this FBO +* @method setTextures +* @param {Array} color_textures an array containing the color textures, if not supplied a render buffer will be used +* @param {GL.Texture} depth_texture the depth texture, if not supplied a render buffer will be used +* @param {Boolean} skip_disable it doenst try to go back to the previous FBO enabled in case there was one +*/ +FBO.prototype.setTextures = function( color_textures, depth_texture, skip_disable ) +{ + //test depth + if( depth_texture && depth_texture.constructor === GL.Texture ) + { + if( depth_texture.format !== GL.DEPTH_COMPONENT && + depth_texture.format !== GL.DEPTH_STENCIL && + depth_texture.format !== GL.DEPTH_COMPONENT16 && + depth_texture.format !== GL.DEPTH_COMPONENT24 && + depth_texture.format !== GL.DEPTH_COMPONENT32F ) + throw("FBO Depth texture must be of format: gl.DEPTH_COMPONENT, gl.DEPTH_STENCIL or gl.DEPTH_COMPONENT16/24/32F (only in webgl2)"); + + if( depth_texture.type != GL.UNSIGNED_SHORT && + depth_texture.type != GL.UNSIGNED_INT && + depth_texture.type != GL.UNSIGNED_INT_24_8_WEBGL && + depth_texture.type != GL.FLOAT) + throw("FBO Depth texture must be of type: gl.UNSIGNED_SHORT, gl.UNSIGNED_INT, gl.UNSIGNED_INT_24_8_WEBGL"); + } + + //test if is already binded + var same = this.depth_texture == depth_texture; + if( same && color_textures ) + { + if( color_textures.constructor !== Array ) + throw("FBO: color_textures parameter must be an array containing all the textures to be binded in the color"); + if( color_textures.length == this.color_textures.length ) + { + for(var i = 0; i < color_textures.length; ++i) + if( color_textures[i] != this.color_textures[i] ) + { + same = false; + break; + } + } + else + same = false; + } + + if(this._stencil_enabled !== this.stencil) + same = false; + + if(same) + return; + + //copy textures in place + this.color_textures.length = color_textures ? color_textures.length : 0; + if(color_textures) + for(var i = 0; i < color_textures.length; ++i) + this.color_textures[i] = color_textures[i]; + this.depth_texture = depth_texture; + + //update GPU FBO + this.update( skip_disable ); +} + +/** +* Updates the FBO with the new set of textures and buffers +* @method update +* @param {Boolean} skip_disable it doenst try to go back to the previous FBO enabled in case there was one +*/ +FBO.prototype.update = function( skip_disable ) +{ + //save state to restore afterwards + this._old_fbo_handler = gl.getParameter( gl.FRAMEBUFFER_BINDING ); + + if(!this.handler) + this.handler = gl.createFramebuffer(); + + var w = -1, + h = -1, + type = null; + + var color_textures = this.color_textures; + var depth_texture = this.depth_texture; + + //compute the W and H (and check they have the same size) + if(color_textures && color_textures.length) + for(var i = 0; i < color_textures.length; i++) + { + var t = color_textures[i]; + if(t.constructor !== GL.Texture) + throw("FBO can only bind instances of GL.Texture"); + if(w == -1) + w = t.width; + else if(w != t.width) + throw("Cannot bind textures with different dimensions"); + if(h == -1) + h = t.height; + else if(h != t.height) + throw("Cannot bind textures with different dimensions"); + if(type == null) //first one defines the type + type = t.type; + else if (type != t.type) + throw("Cannot bind textures to a FBO with different pixel formats"); + if (t.texture_type != gl.TEXTURE_2D) + throw("Cannot bind a Cubemap to a FBO"); + } + else + { + w = depth_texture.width; + h = depth_texture.height; + } + + this.width = w; + this.height = h; + + gl.bindFramebuffer( gl.FRAMEBUFFER, this.handler ); + + //draw_buffers allow to have more than one color texture binded in a FBO + var ext = gl.extensions["WEBGL_draw_buffers"]; + if( gl.webgl_version == 1 && !ext && color_textures && color_textures.length > 1) + throw("Rendering to several textures not supported by your browser"); + + var target = gl.webgl_version == 1 ? gl.FRAMEBUFFER : gl.DRAW_FRAMEBUFFER; + + //detach anything bindede + gl.framebufferRenderbuffer( target, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, null ); + gl.framebufferRenderbuffer( target, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, null ); + //detach color too? + + //bind a buffer for the depth + if( depth_texture && depth_texture.constructor === GL.Texture ) + { + if(gl.webgl_version == 1 && !gl.extensions["WEBGL_depth_texture"] ) + throw("Rendering to depth texture not supported by your browser"); + + if(this.stencil && depth_texture.format !== gl.DEPTH_STENCIL ) + console.warn("Stencil cannot be enabled if there is a depth texture with a DEPTH_STENCIL format"); + + if( depth_texture.format == gl.DEPTH_STENCIL ) + gl.framebufferTexture2D( target, gl.DEPTH_STENCIL_ATTACHMENT, gl.TEXTURE_2D, depth_texture.handler, 0); + else + gl.framebufferTexture2D( target, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depth_texture.handler, 0); + } + else //create a renderbuffer to store depth + { + var depth_renderbuffer = null; + + //allows to reuse a renderbuffer between FBOs + if( depth_texture && depth_texture.constructor === WebGLRenderbuffer && depth_texture.width == w && depth_texture.height == h ) + depth_renderbuffer = this._depth_renderbuffer = depth_texture; + else + { + //create one + depth_renderbuffer = this._depth_renderbuffer = this._depth_renderbuffer || gl.createRenderbuffer(); + depth_renderbuffer.width = w; + depth_renderbuffer.height = h; + } + + gl.bindRenderbuffer( gl.RENDERBUFFER, depth_renderbuffer ); + if(this.stencil) + { + gl.renderbufferStorage( gl.RENDERBUFFER, gl.DEPTH_STENCIL, w, h ); + gl.framebufferRenderbuffer( target, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, depth_renderbuffer ); + } + else + { + gl.renderbufferStorage( gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, w, h ); + gl.framebufferRenderbuffer( target, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depth_renderbuffer ); + } + } + + //bind buffers for the colors + if(color_textures && color_textures.length) + { + this.order = []; //draw_buffers request the use of an array with the order of the attachments + for(var i = 0; i < color_textures.length; i++) + { + var t = color_textures[i]; + + //not a bug, gl.COLOR_ATTACHMENT0 + i because COLOR_ATTACHMENT is sequential numbers + gl.framebufferTexture2D( target, gl.COLOR_ATTACHMENT0 + i, gl.TEXTURE_2D, t.handler, 0 ); + this.order.push( gl.COLOR_ATTACHMENT0 + i ); + } + } + else //create renderbuffer to store color + { + var color_renderbuffer = this._color_renderbuffer = this._color_renderbuffer || gl.createRenderbuffer(); + color_renderbuffer.width = w; + color_renderbuffer.height = h; + gl.bindRenderbuffer( gl.RENDERBUFFER, color_renderbuffer ); + gl.renderbufferStorage( gl.RENDERBUFFER, gl.RGBA4, w, h ); + gl.framebufferRenderbuffer( target, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, color_renderbuffer ); + } + + //detach old ones (only if is reusing a FBO with a different set of textures) + var num = color_textures ? color_textures.length : 0; + for(var i = num; i < this._num_binded_textures; ++i) + gl.framebufferTexture2D( target, gl.COLOR_ATTACHMENT0 + i, gl.TEXTURE_2D, null, 0); + this._num_binded_textures = num; + + this._stencil_enabled = this.stencil; + + /* does not work, must be used with the depth_stencil + if(this.stencil && !depth_texture) + { + var stencil_buffer = this._stencil_buffer = this._stencil_buffer || gl.createRenderbuffer(); + stencil_buffer.width = w; + stencil_buffer.height = h; + gl.bindRenderbuffer( gl.RENDERBUFFER, stencil_buffer ); + gl.renderbufferStorage( gl.RENDERBUFFER, gl.STENCIL_INDEX8, w, h); + gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, stencil_buffer ); + this._stencil_enabled = true; + } + else + { + this._stencil_buffer = null; + this._stencil_enabled = false; + } + */ + + //when using more than one texture you need to use the multidraw extension + if(color_textures && color_textures.length > 1) + { + if( ext ) + ext.drawBuffersWEBGL( this.order ); + else + gl.drawBuffers( this.order ); + } + + //check completion + var complete = gl.checkFramebufferStatus( target ); + if(complete !== gl.FRAMEBUFFER_COMPLETE) //36054: GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT + throw("FBO not complete: " + complete); + + //restore state + gl.bindTexture(gl.TEXTURE_2D, null); + gl.bindRenderbuffer(gl.RENDERBUFFER, null); + if(!skip_disable) + gl.bindFramebuffer( target, this._old_fbo_handler ); +} + +/** +* Enables this FBO (from now on all the render will be stored in the textures attached to this FBO) +* It stores the previous viewport to restore it afterwards, and changes it to full FBO size +* @method bind +* @param {boolean} keep_old keeps the previous FBO is one was attached to restore it afterwards +*/ +FBO.prototype.bind = function( keep_old ) +{ + if(!this.color_textures.length && !this.depth_texture) + throw("FBO: no textures attached to FBO"); + this._old_viewport.set( gl.viewport_data ); + + if(keep_old) + this._old_fbo_handler = gl.getParameter( gl.FRAMEBUFFER_BINDING ); + else + this._old_fbo_handler = null; + + if(this._old_fbo_handler != this.handler ) + gl.bindFramebuffer( gl.FRAMEBUFFER, this.handler ); + + //mark them as in use in the FBO + for(var i = 0; i < this.color_textures.length; ++i) + this.color_textures[i]._in_current_fbo = true; + if(this.depth_texture) + this.depth_texture._in_current_fbo = true; + + gl.viewport( 0,0, this.width, this.height ); + FBO.current = this; +} + +/** +* Disables this FBO, if it was binded with keep_old then the old FBO is enabled, otherwise it will render to the screen +* Restores viewport to previous +* @method unbind +*/ +FBO.prototype.unbind = function() +{ + gl.bindFramebuffer( gl.FRAMEBUFFER, this._old_fbo_handler ); + this._old_fbo_handler = null; + gl.setViewport( this._old_viewport ); + + //mark the textures as no longer in use + for(var i = 0; i < this.color_textures.length; ++i) + this.color_textures[i]._in_current_fbo = false; + if(this.depth_texture) + this.depth_texture._in_current_fbo = false; + FBO.current = null; +} + +//binds another FBO without switch back to previous (faster) +FBO.prototype.switchTo = function( next_fbo ) +{ + next_fbo._old_fbo_handler = this._old_fbo_handler; + next_fbo._old_viewport.set( this._old_viewport ); + gl.bindFramebuffer( gl.FRAMEBUFFER, next_fbo.handler ); + this._old_fbo_handler = null; + gl.viewport( 0,0, this.width, this.height ); + + //mark the textures as no longer in use + for(var i = 0; i < this.color_textures.length; ++i) + this.color_textures[i]._in_current_fbo = false; + if(this.depth_texture) + this.depth_texture._in_current_fbo = false; + + //mark them as in use in the FBO + for(var i = 0; i < next_fbo.color_textures.length; ++i) + next_fbo.color_textures[i]._in_current_fbo = true; + if(next_fbo.depth_texture) + next_fbo.depth_texture._in_current_fbo = true; + + FBO.current = next_fbo; +} + +FBO.prototype.delete = function() +{ + gl.deleteFramebuffer( this.handler ); + this.handler = null; +} + +//WebGL 1.0 support for certaing FBOs is not very clear and can crash sometimes +FBO.supported = {}; +//type: gl.FLOAT, format: gl.RGBA +FBO.testSupport = function( type, format ) { + var name = type +":" + format; + if( FBO.supported[ name ] != null ) + return FBO.supported[ name ]; + + var tex = new GL.Texture(1,1,{ format: format, type: type }); + try + { + var fbo = new GL.FBO([tex]); + } + catch (err) + { + console.warn("This browser WEBGL implementation doesn't support this FBO format: " + GL.reverse[type] + " " + GL.reverse[format] ); + return FBO.supported[ name ] = false; + } + FBO.supported[ name ] = true; + return true; +} + +FBO.prototype.toSingle = function() +{ + if( this.color_textures.length < 2 ) + return; //nothing to do + var ext = gl.extensions.WEBGL_draw_buffers; + if( ext ) + ext.drawBuffersWEBGL( [ this.order[0] ] ); + else + gl.drawBuffers( [ this.order[0] ] ); +} + +FBO.prototype.toMulti = function() +{ + if( this.color_textures.length < 2 ) + return; //nothing to do + var ext = gl.extensions.WEBGL_draw_buffers; + if( ext ) + ext.drawBuffersWEBGL( this.order ); + else + gl.drawBuffers( this.order ); +} + +//clears only the secondary buffers (not the main one) +FBO.prototype.clearSecondary = function( color ) +{ + if(!this.order || this.order.length < 2) + return; + + var ext = gl.extensions.WEBGL_draw_buffers; + var new_order = [gl.NONE]; + for(var i = 1; i < this.order.length; ++i) + new_order.push(this.order[i]); + if(ext) + ext.drawBuffersWEBGL( new_order ); + else + gl.drawBuffers( new_order ); + gl.clearColor( color[0],color[1],color[2],color[3] ); + gl.clear( gl.COLOR_BUFFER_BIT ); + + if(ext) + ext.drawBuffersWEBGL( this.order ); + else + gl.drawBuffers( this.order ); +} + + + +/** +* @namespace GL +*/ + +/** +* Shader class to upload programs to the GPU +* @class Shader +* @constructor +* @param {String} vertexSource (it also allows to pass a compiled vertex shader) +* @param {String} fragmentSource (it also allows to pass a compiled fragment shader) +* @param {Object} macros (optional) precompiler macros to be applied when compiling +*/ +global.Shader = GL.Shader = function Shader( vertexSource, fragmentSource, macros ) +{ + if(GL.debug) + console.log("GL.Shader created"); + + if( !vertexSource || !fragmentSource ) + throw("GL.Shader source code parameter missing"); + + //used to avoid problems with resources moving between different webgl context + this._context_id = global.gl.context_id; + var gl = this.gl = global.gl; + + //expand macros + var extra_code = Shader.expandMacros( macros ); + + var final_vertexSource = vertexSource.constructor === String ? Shader.injectCode( extra_code, vertexSource, gl ) : vertexSource; + var final_fragmentSource = fragmentSource.constructor === String ? Shader.injectCode( extra_code, fragmentSource, gl ) : fragmentSource; + + this.program = gl.createProgram(); + + var vs = vertexSource.constructor === String ? GL.Shader.compileSource( gl.VERTEX_SHADER, final_vertexSource ) : vertexSource; + var fs = fragmentSource.constructor === String ? GL.Shader.compileSource( gl.FRAGMENT_SHADER, final_fragmentSource ) : fragmentSource; + + gl.attachShader( this.program, vs, gl ); + gl.attachShader( this.program, fs, gl ); + gl.linkProgram(this.program); + if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) { + throw 'link error: ' + gl.getProgramInfoLog(this.program); + } + + this.vs_shader = vs; + this.fs_shader = fs; + + //Extract info from the shader + this.attributes = {}; + this.uniformInfo = {}; + this.samplers = {}; + + //extract info about the shader to speed up future processes + this.extractShaderInfo(); +} + +Shader.expandMacros = function(macros) +{ + var extra_code = ""; //add here preprocessor directives that should be above everything + if(macros) + for(var i in macros) + extra_code += "#define " + i + " " + (macros[i] ? macros[i] : "") + "\n"; + return extra_code; +} + +//this is done to avoid problems with the #version which must be in the first line +Shader.injectCode = function( inject_code, code, gl ) +{ + var index = code.indexOf("\n"); + var version = ( gl ? "#define WEBGL" + gl.webgl_version + "\n" : ""); + var first_line = code.substr(0,index).trim(); + if( first_line.indexOf("#version") == -1 ) + return version + inject_code + code; + return first_line + "\n" + version + inject_code + code.substr(index); +} + + +/** +* Compiles one single shader source (could be gl.VERTEX_SHADER or gl.FRAGMENT_SHADER) and returns the webgl shader handler +* Used internaly to compile the vertex and fragment shader. +* It throws an exception if there is any error in the code +* @method Shader.compileSource +* @param {Number} type could be gl.VERTEX_SHADER or gl.FRAGMENT_SHADER +* @param {String} source the source file to compile +* @return {WebGLShader} the handler from webgl +*/ +Shader.compileSource = function( type, source, gl, shader ) +{ + gl = gl || global.gl; + shader = shader || gl.createShader(type); + gl.shaderSource(shader, source); + gl.compileShader(shader); + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + throw (type == gl.VERTEX_SHADER ? "Vertex" : "Fragment") + ' shader compile error: ' + gl.getShaderInfoLog(shader); + } + return shader; +} + +Shader.parseError = function( error_str, vs_code, fs_code ) +{ + if(!error_str) + return null; + + var t = error_str.split(" "); + var nums = t[5].split(":"); + + return { + type: t[0], + line_number: parseInt( nums[1] ), + line_pos: parseInt( nums[0] ), + line_code: ( t[0] == "Fragment" ? fs_code : vs_code ).split("\n")[ parseInt( nums[1] ) ], + err: error_str + }; +} + +/** +* It updates the code inside one shader +* @method updateShader +* @param {String} vertexSource +* @param {String} fragmentSource +* @param {Object} macros [optional] +*/ +Shader.prototype.updateShader = function( vertexSource, fragmentSource, macros ) +{ + var gl = this.gl || global.gl; + + //expand macros + var extra_code = Shader.expandMacros( macros ); + + if(!this.program) + this.program = gl.createProgram(); + else + { + gl.detachShader( this.program, this.vs_shader ); + gl.detachShader( this.program, this.fs_shader ); + } + + var extra_code = Shader.expandMacros( macros ); + + var final_vertexSource = vertexSource.constructor === String ? Shader.injectCode( extra_code, vertexSource, gl ) : vertexSource; + var final_fragmentSource = fragmentSource.constructor === String ? Shader.injectCode( extra_code, fragmentSource, gl ) : fragmentSource; + + var vs = vertexSource.constructor === String ? GL.Shader.compileSource( gl.VERTEX_SHADER, final_vertexSource ) : vertexSource; + var fs = fragmentSource.constructor === String ? GL.Shader.compileSource( gl.FRAGMENT_SHADER, final_fragmentSource ) : fragmentSource; + + gl.attachShader( this.program, vs, gl ); + gl.attachShader( this.program, fs, gl ); + gl.linkProgram( this.program ); + if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) { + throw 'link error: ' + gl.getProgramInfoLog( this.program ); + } + + //store shaders separated + this.vs_shader = vs; + this.fs_shader = fs; + + //Extract info from the shader + this.attributes = {}; + this.uniformInfo = {}; + this.samplers = {}; + + //extract info about the shader to speed up future processes + this.extractShaderInfo(); +} + +/** +* It extract all the info about the compiled shader program, all the info about uniforms and attributes. +* This info is stored so it works faster during rendering. +* @method extractShaderInfo +*/ + + +Shader.prototype.extractShaderInfo = function() +{ + var gl = this.gl; + + var l = gl.getProgramParameter( this.program, gl.ACTIVE_UNIFORMS ); + + //extract uniforms info + for(var i = 0; i < l; ++i) + { + var data = gl.getActiveUniform( this.program, i); + if(!data) break; + + var uniformName = data.name; + + //arrays have uniformName[0], strip the [] (also data.size tells you if it is an array) + var pos = uniformName.indexOf("["); + if(pos != -1) + { + var pos2 = uniformName.indexOf("]."); //leave array of structs though + if(pos2 == -1) + uniformName = uniformName.substr(0,pos); + } + + //store texture samplers + if(data.type == gl.SAMPLER_2D || data.type == gl.SAMPLER_CUBE || data.type == GL.SAMPLER_3D) + this.samplers[ uniformName ] = data.type; + + //get which function to call when uploading this uniform + var func = Shader.getUniformFunc(data); + var is_matrix = false; + if(data.type == gl.FLOAT_MAT2 || data.type == gl.FLOAT_MAT3 || data.type == gl.FLOAT_MAT4) + is_matrix = true; + var type_length = GL.TYPE_LENGTH[ data.type ] || 1; + + //save the info so the user doesnt have to specify types when uploading data to the shader + this.uniformInfo[ uniformName ] = { + type: data.type, + func: func, + size: data.size, + type_length: type_length, + is_matrix: is_matrix, + loc: gl.getUniformLocation(this.program, uniformName), + data: new Float32Array( type_length * data.size ) //prealloc space to assign uniforms that are not typed + }; + } + + //extract attributes info + for(var i = 0, l = gl.getProgramParameter(this.program, gl.ACTIVE_ATTRIBUTES); i < l; ++i) + { + var data = gl.getActiveAttrib( this.program, i); + if(!data) + break; + var func = Shader.getUniformFunc(data); + var type_length = GL.TYPE_LENGTH[ data.type ] || 1; + this.uniformInfo[ data.name ] = { + type: data.type, + func: func, + type_length: type_length, + size: data.size, + loc: null + }; //gl.getAttribLocation( this.program, data.name ) + this.attributes[ data.name ] = gl.getAttribLocation(this.program, data.name ); + } +} + +/** +* Returns if this shader has a uniform with the given name +* @method hasUniform +* @param {String} name name of the uniform +* @return {Boolean} +*/ +Shader.prototype.hasUniform = function(name) +{ + return this.uniformInfo[name]; +} + +/** +* Returns if this shader has an attribute with the given name +* @method hasAttribute +* @param {String} name name of the attribute +* @return {Boolean} +*/ +Shader.prototype.hasAttribute = function(name) +{ + return this.attributes[name]; +} + + +/** +* Tells you which function to call when uploading a uniform according to the data type in the shader +* Used internally from extractShaderInfo to optimize calls +* @method Shader.getUniformFunc +* @param {Object} data info about the uniform +* @return {Function} +*/ +Shader.getUniformFunc = function( data ) +{ + var func = null; + switch (data.type) + { + case GL.FLOAT: + if(data.size == 1) + func = gl.uniform1f; + else + func = gl.uniform1fv; + break; + case GL.FLOAT_MAT2: func = gl.uniformMatrix2fv; break; + case GL.FLOAT_MAT3: func = gl.uniformMatrix3fv; break; + case GL.FLOAT_MAT4: func = gl.uniformMatrix4fv; break; + case GL.FLOAT_VEC2: func = gl.uniform2fv; break; + case GL.FLOAT_VEC3: func = gl.uniform3fv; break; + case GL.FLOAT_VEC4: func = gl.uniform4fv; break; + + case GL.UNSIGNED_INT: + case GL.INT: + if(data.size == 1) + func = gl.uniform1i; + else + func = gl.uniform1iv; + break; + case GL.INT_VEC2: func = gl.uniform2iv; break; + case GL.INT_VEC3: func = gl.uniform3iv; break; + case GL.INT_VEC4: func = gl.uniform4iv; break; + + case GL.SAMPLER_2D: + case GL.SAMPLER_3D: + case GL.SAMPLER_CUBE: + func = gl.uniform1i; break; + default: func = gl.uniform1f; break; + } + return func; +} + +/** +* Create a shader from two urls. While the system is fetching the two urls, the shader contains a dummy shader that renders black. +* @method Shader.fromURL +* @param {String} vs_path the url to the vertex shader +* @param {String} fs_path the url to the fragment shader +* @param {Function} on_complete [Optional] a callback to call once the shader is ready. +* @return {Shader} +*/ +Shader.fromURL = function( vs_path, fs_path, on_complete ) +{ + //create simple shader first + var vs_code = "\n\ + precision highp float;\n\ + attribute vec3 a_vertex;\n\ + attribute mat4 u_mvp;\n\ + void main() { \n\ + gl_Position = u_mvp * vec4(a_vertex,1.0); \n\ + }\n\ + "; + var fs_code = "\n\ + precision highp float;\n\ + void main() {\n\ + gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);\n\ + }\n\ + "; + + var shader = new GL.Shader(vs_code, fs_code); + shader.ready = false; + + var true_vs = null; + var true_fs = null; + + HttpRequest( vs_path, null, function(vs_code) { + true_vs = vs_code; + if(true_fs) + compileShader(); + }); + + HttpRequest( fs_path, null, function(fs_code) { + true_fs = fs_code; + if(true_vs) + compileShader(); + }); + + function compileShader() + { + var true_shader = new GL.Shader(true_vs, true_fs); + for(var i in true_shader) + shader[i] = true_shader[i]; + shader.ready = true; + } + + return shader; +} + +/** +* enables the shader (calls useProgram) +* @method bind +*/ +Shader.prototype.bind = function() +{ + var gl = this.gl; + gl.useProgram( this.program ); + gl._current_shader = this; +} + +/** +* Returns the location of a uniform or attribute +* @method getLocation +* @param {String} name +* @return {WebGLUniformLocation} location +*/ +Shader.prototype.getLocation = function( name ) +{ + var info = this.uniformInfo[name]; + if(info) + return this.uniformInfo[name].loc; + return null; +} + +/** +* Uploads a set of uniforms to the Shader. You dont need to specify types, they are infered from the shader info. +* @method uniforms +* @param {Object} uniforms +*/ +Shader._temp_uniform = new Float32Array(16); + +Shader.prototype.uniforms = function(uniforms) { + var gl = this.gl; + gl.useProgram(this.program); + gl._current_shader = this; + + for (var name in uniforms) + { + var info = this.uniformInfo[ name ]; + if (!info) + continue; + this._setUniform( name, uniforms[name] ); + //this.setUniform( name, uniforms[name] ); + //this._assing_uniform(uniforms, name, gl ); + } + + return this; +}//uniforms + +Shader.prototype.uniformsArray = function(array) { + var gl = this.gl; + gl.useProgram( this.program ); + gl._current_shader = this; + + for(var i = 0, l = array.length; i < l; ++i) + { + var uniforms = array[i]; + for (var name in uniforms) + this._setUniform( name, uniforms[name] ); + //this._assing_uniform(uniforms, name, gl ); + } + + return this; +} + +/** +* Uploads a uniform to the Shader. You dont need to specify types, they are infered from the shader info. Shader must be binded! +* @method setUniform +* @param {string} name +* @param {*} value +*/ +Shader.prototype.setUniform = (function(){ + + return (function(name, value) + { + if( this.gl._current_shader != this ) + this.bind(); + + var info = this.uniformInfo[name]; + if (!info) + return; + + if(info.loc === null) + return; + + if(value == null) //strict? + return; + + if(value.constructor === Array) + { + info.data.set( value ); + value = info.data; + } + + if(info.is_matrix) + info.func.call( this.gl, info.loc, false, value ); + else + info.func.call( this.gl, info.loc, value ); + }); +})(); + +//skips enabling shader +Shader.prototype._setUniform = (function(){ + + return (function(name, value) + { + var info = this.uniformInfo[ name ]; + if (!info) + return; + + if(info.loc === null) + return; + + //if(info.loc.constructor !== Function) + // return; + + if(value == null) + return; + + if(value.constructor === Array) + { + info.data.set( value ); + value = info.data; + } + + if(info.is_matrix) + info.func.call( this.gl, info.loc, false, value ); + else + info.func.call( this.gl, info.loc, value ); + }); +})(); + +/** +* Renders a mesh using this shader, remember to use the function uniforms before to enable the shader +* @method draw +* @param {Mesh} mesh +* @param {number} mode could be gl.LINES, gl.POINTS, gl.TRIANGLES, gl.TRIANGLE_STRIP, gl.TRIANGLE_FAN +* @param {String} index_buffer_name the name of the index buffer, if not provided triangles will be assumed +*/ +Shader.prototype.draw = function( mesh, mode, index_buffer_name ) { + index_buffer_name = index_buffer_name === undefined ? (mode == gl.LINES ? 'lines' : 'triangles') : index_buffer_name; + this.drawBuffers( mesh.vertexBuffers, + index_buffer_name ? mesh.indexBuffers[ index_buffer_name ] : null, + arguments.length < 2 ? gl.TRIANGLES : mode); +} + +/** +* Renders a range of a mesh using this shader +* @method drawRange +* @param {Mesh} mesh +* @param {number} mode could be gl.LINES, gl.POINTS, gl.TRIANGLES, gl.TRIANGLE_STRIP, gl.TRIANGLE_FAN +* @param {number} start first primitive to render +* @param {number} length number of primitives to render +* @param {String} index_buffer_name the name of the index buffer, if not provided triangles will be assumed +*/ +Shader.prototype.drawRange = function(mesh, mode, start, length, index_buffer_name ) +{ + index_buffer_name = index_buffer_name === undefined ? (mode == gl.LINES ? 'lines' : 'triangles') : index_buffer_name; + + this.drawBuffers( mesh.vertexBuffers, + index_buffer_name ? mesh.indexBuffers[ index_buffer_name ] : null, + mode, start, length); +} + +/** +* render several buffers with a given index buffer +* @method drawBuffers +* @param {Object} vertexBuffers an object containing all the buffers +* @param {IndexBuffer} indexBuffer +* @param {number} mode could be gl.LINES, gl.POINTS, gl.TRIANGLES, gl.TRIANGLE_STRIP, gl.TRIANGLE_FAN +* @param {number} range_start first primitive to render +* @param {number} range_length number of primitives to render +*/ + +//this two variables are a hack to avoid memory allocation on drawCalls +var temp_attribs_array = new Uint8Array(16); +var temp_attribs_array_zero = new Uint8Array(16); //should be filled with zeros always + +Shader.prototype.drawBuffers = function( vertexBuffers, indexBuffer, mode, range_start, range_length ) +{ + if(range_length == 0) + return; + + var gl = this.gl; + + gl.useProgram(this.program); //this could be removed assuming every shader is called with some uniforms + + // enable attributes as necessary. + var length = 0; + var attribs_in_use = temp_attribs_array; //hack to avoid garbage + attribs_in_use.set( temp_attribs_array_zero ); //reset + + for (var name in vertexBuffers) + { + var buffer = vertexBuffers[name]; + var attribute = buffer.attribute || name; + //precompute attribute locations in shader + var location = this.attributes[attribute];// || gl.getAttribLocation(this.program, attribute); + + if (location == null || !buffer.buffer) //-1 changed for null + continue; //ignore this buffer + + attribs_in_use[location] = 1; //mark it as used + + //this.attributes[attribute] = location; + gl.bindBuffer(gl.ARRAY_BUFFER, buffer.buffer); + gl.enableVertexAttribArray(location); + + gl.vertexAttribPointer(location, buffer.buffer.spacing, buffer.buffer.gl_type, false, 0, 0); + length = buffer.buffer.length / buffer.buffer.spacing; + } + + //range rendering + var offset = 0; //in bytes + if(range_start > 0) //render a polygon range + offset = range_start; //in bytes (Uint16 == 2 bytes) + + if (indexBuffer) + length = indexBuffer.buffer.length - offset; + + if(range_length > 0 && range_length < length) //to avoid problems + length = range_length; + + var BYTES_PER_ELEMENT = (indexBuffer && indexBuffer.data) ? indexBuffer.data.constructor.BYTES_PER_ELEMENT : 1; + offset *= BYTES_PER_ELEMENT; + + // Force to disable buffers in this shader that are not in this mesh + for (var attribute in this.attributes) + { + var location = this.attributes[attribute]; + if (!(attribs_in_use[location])) { + gl.disableVertexAttribArray(this.attributes[attribute]); + } + } + + // Draw the geometry. + if (length && (!indexBuffer || indexBuffer.buffer)) { + if (indexBuffer) { + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer.buffer); + gl.drawElements( mode, length, indexBuffer.buffer.gl_type, offset); //gl.UNSIGNED_SHORT + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); + } else { + gl.drawArrays(mode, offset, length); + } + } + + return this; +} + +Shader._instancing_arrays = []; + +Shader.prototype.drawInstanced = function( mesh, primitive, indices, instanced_uniforms, range_start, range_length, num_instances ) +{ + if(range_length === 0) + return; + + //bind buffers + var gl = this.gl; + + if( gl.webgl_version == 1 && !gl.extensions.ANGLE_instanced_arrays ) + throw("instancing not supported"); + + gl.useProgram(this.program); //this could be removed assuming every shader is called with some uniforms + + // enable attributes as necessary. + var length = 0; + var attribs_in_use = temp_attribs_array; //hack to avoid garbage + attribs_in_use.set( temp_attribs_array_zero ); //reset + + var vertexBuffers = mesh.vertexBuffers; + + for (var name in vertexBuffers) + { + var buffer = vertexBuffers[name]; + var attribute = buffer.attribute || name; + //precompute attribute locations in shader + var location = this.attributes[attribute];// || gl.getAttribLocation(this.program, attribute); + + if (location == null || !buffer.buffer) //-1 changed for null + continue; //ignore this buffer + + attribs_in_use[location] = 1; //mark it as used + + //this.attributes[attribute] = location; + gl.bindBuffer(gl.ARRAY_BUFFER, buffer.buffer); + gl.enableVertexAttribArray(location); + + gl.vertexAttribPointer(location, buffer.buffer.spacing, buffer.buffer.gl_type, false, 0, 0); + length = buffer.buffer.length / buffer.buffer.spacing; + } + + var indexBuffer = null; + if(indices) + { + if(indices.constructor === GL.Buffer) + indexBuffer = indices; + else + indexBuffer = mesh.getIndexBuffer( indices ); + } + + //range rendering + var offset = 0; //in bytes + if(range_start > 0) //render a polygon range + offset = range_start; + + if (indexBuffer) + length = indexBuffer.buffer.length - offset; + + if(range_length > 0 && range_length < length) //to avoid problems + length = range_length; + + var BYTES_PER_ELEMENT = (indexBuffer && indexBuffer.data) ? indexBuffer.data.constructor.BYTES_PER_ELEMENT : 1; + offset *= BYTES_PER_ELEMENT; + + // Force to disable buffers in this shader that are not in this mesh + for (var attribute in this.attributes) + { + var location = this.attributes[attribute]; + if (!(attribs_in_use[location])) { + gl.disableVertexAttribArray(this.attributes[attribute]); + } + } + + var ext = gl.extensions.ANGLE_instanced_arrays; + var batch_length = 0; + + //pack the instanced uniforms + var index = 0; + for(var uniform in instanced_uniforms) + { + var values = instanced_uniforms[ uniform ]; + batch_length = values.length; + var uniformLocation = this.attributes[ uniform ]; + if( uniformLocation == null ) + return; //not found + var element_size = 0; + var total_size = 0; + if( values.constructor === Array ) + { + element_size = values[0].constructor === Number ? 1 : values[0].length; + total_size = element_size * values.length; + } + else //typed array + { + element_size = this.uniformInfo[ uniform ].type_length; + total_size = values.length; + batch_length = total_size / element_size; + } + + var data_array = Shader._instancing_arrays[ index ]; + if( !data_array || data_array.data.length < total_size ) + data_array = Shader._instancing_arrays[ index ] = { data: new Float32Array( total_size ), buffer: gl.createBuffer() }; + data_array.uniform = uniform; + data_array.element_size = element_size; + if( values.constructor === Array ) + for(var j = 0; j < values.length; ++j) + data_array.data.set( values[j], j*element_size ); //flatten array + else + data_array.data.set( values ); //copy + gl.bindBuffer( gl.ARRAY_BUFFER, data_array.buffer ); + gl.bufferData( gl.ARRAY_BUFFER, data_array.data, gl.STREAM_DRAW ); + + if(element_size == 16) //mat4 + { + for(var k = 0; k < 4; ++k) + { + gl.enableVertexAttribArray( uniformLocation+k ); + gl.vertexAttribPointer( uniformLocation+k, 4, gl.FLOAT , false, 16*4, k*4*4 ); //4 bytes per float + if( ext ) //webgl 1 + ext.vertexAttribDivisorANGLE( uniformLocation+k, 1 ); // This makes it instanced! + else + gl.vertexAttribDivisor( uniformLocation+k, 1 ); // This makes it instanced! + } + } + else //others + { + gl.enableVertexAttribArray( uniformLocation ); + gl.vertexAttribPointer( uniformLocation, element_size, gl.FLOAT, false, element_size*4, 0 ); //4 bytes per float, 0 offset + if( ext ) //webgl 1 + ext.vertexAttribDivisorANGLE( uniformLocation, 1 ); // This makes it instanced! + else + gl.vertexAttribDivisor( uniformLocation, 1 ); // This makes it instanced! + } + index+=1; + } + + if( num_instances ) + batch_length = num_instances; + + if( ext ) //webgl 1.0 + { + if(indexBuffer) + { + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer.buffer); + ext.drawElementsInstancedANGLE( primitive, length, indexBuffer.buffer.gl_type, offset, batch_length ); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null ); + } + else + ext.drawArraysInstancedANGLE( primitive, offset, length, batch_length); + } + else + { + if(indexBuffer) + { + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer.buffer); + gl.drawElementsInstanced( primitive, length, indexBuffer.buffer.gl_type, offset, batch_length ); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null ); + } + else + gl.drawArraysInstanced( primitive, offset, length, batch_length); + } + + //disable instancing buffers + for(var i = 0; i < index; ++i) + { + var info = Shader._instancing_arrays[ i ]; + var uniformLocation = this.attributes[ info.uniform ]; + var element_size = info.element_size; + if( element_size == 16) //mat4 + { + for(var k = 0; k < 4; ++k) + { + gl.disableVertexAttribArray( uniformLocation+k ); + if( ext ) //webgl 1 + ext.vertexAttribDivisorANGLE( uniformLocation+k, 0 ); + else + gl.vertexAttribDivisor( uniformLocation+k, 0 ); + } + } + else //others + { + gl.enableVertexAttribArray( uniformLocation ); + if( ext ) //webgl 1 + ext.vertexAttribDivisorANGLE( uniformLocation, 0 ); + else + gl.vertexAttribDivisor( uniformLocation, 0 ); + } + } + + return this; +} + + + +/** +* Given a source code with the directive #import it expands it inserting the code using Shader.files to fetch for import files. +* Warning: Imports are evaluated only the first inclusion, the rest are ignored to avoid double inclusion of functions +* Also, imports cannot have other imports inside. +* @method Shader.expandImports +* @param {String} code the source code +* @param {Object} files [Optional] object with files to import from (otherwise Shader.files is used) +* @return {String} the code with the lines #import removed and replaced by the code +*/ +Shader.expandImports = function(code, files) +{ + files = files || Shader.files; + + var already_imported = {}; //avoid to import two times the same code + if( !files ) + throw("Shader.files not initialized, assign files there"); + + var replace_import = function(v) + { + var token = v.split("\""); + var id = token[1]; + if( already_imported[id] ) + return "//already imported: " + id + "\n"; + var file = files[id]; + already_imported[ id ] = true; + if(file) + return file + "\n"; + return "//import code not found: " + id + "\n"; + } + + //return code.replace(/#import\s+\"(\w+)\"\s*\n/g, replace_import ); + return code.replace(/#import\s+\"([a-zA-Z0-9_\.]+)\"\s*\n/g, replace_import ); +} + +Shader.dumpErrorToConsole = function(err, vscode, fscode) +{ + console.error(err); + var msg = err.msg; + var code = null; + if(err.indexOf("Fragment") != -1) + code = fscode; + else + code = vscode; + + var lines = code.split("\n"); + for(var i in lines) + lines[i] = i + "| " + lines[i]; + + console.groupCollapsed("Shader code"); + console.log( lines.join("\n") ); + console.groupEnd(); +} + +Shader.convertTo100 = function(code,type) +{ + //in VERTEX + //change in for attribute + //change out for varying + //add #extension GL_OES_standard_derivatives + //in FRAGMENT + //change in for varying + //remove out vec4 _gl_FragColor + //rename _gl_FragColor for gl_FragColor + //in both + //change #version 300 es for #version 100 + //replace 'texture(' for 'texture2D(' +} + + +Shader.convertTo300 = function(code,type) +{ + //in VERTEX + //change attribute for in + //change varying for out + //remove #extension GL_OES_standard_derivatives + //in FRAGMENT + //change varying for in + //rename gl_FragColor for _gl_FragColor + //rename gl_FragData[0] for _gl_FragColor + //add out vec4 _gl_FragColor + //in both + //replace texture2D for texture +} + +//helps to check if a variable value is valid to an specific uniform in a shader +Shader.validateValue = function( value, uniform_info ) +{ + if(value === null || value === undefined) + return false; + + switch (uniform_info.type) + { + //used to validate shaders + case GL.INT: + case GL.FLOAT: + case GL.SAMPLER_2D: + case GL.SAMPLER_CUBE: + return isNumber(value); + case GL.INT_VEC2: + case GL.FLOAT_VEC2: + return value.length === 2; + case GL.INT_VEC3: + case GL.FLOAT_VEC3: + return value.length === 3; + case GL.INT_VEC4: + case GL.FLOAT_VEC4: + case GL.FLOAT_MAT2: + return value.length === 4; + case GL.FLOAT_MAT3: + return value.length === 8; + case GL.FLOAT_MAT4: + return value.length === 16; + } + return true; +} + +//**************** SHADERS *********************************** + +Shader.DEFAULT_VERTEX_SHADER = "\n\ + precision highp float;\n\ + attribute vec3 a_vertex;\n\ + attribute vec3 a_normal;\n\ + attribute vec2 a_coord;\n\ + varying vec3 v_position;\n\ + varying vec3 v_normal;\n\ + varying vec2 v_coord;\n\ + uniform mat4 u_model;\n\ + uniform mat4 u_mvp;\n\ + void main() {\n\ + v_position = (u_model * vec4(a_vertex,1.0)).xyz;\n\ + v_normal = (u_model * vec4(a_normal,0.0)).xyz;\n\ + v_coord = a_coord;\n\ + gl_Position = u_mvp * vec4(a_vertex,1.0);\n\ + }\n\ + "; + +Shader.SCREEN_VERTEX_SHADER = "\n\ + precision highp float;\n\ + attribute vec3 a_vertex;\n\ + attribute vec2 a_coord;\n\ + varying vec2 v_coord;\n\ + void main() { \n\ + v_coord = a_coord; \n\ + gl_Position = vec4(a_coord * 2.0 - 1.0, 0.0, 1.0); \n\ + }\n\ + "; + +Shader.SCREEN_FRAGMENT_SHADER = "\n\ + precision highp float;\n\ + uniform sampler2D u_texture;\n\ + varying vec2 v_coord;\n\ + void main() {\n\ + gl_FragColor = texture2D(u_texture, v_coord);\n\ + }\n\ + "; + +//used in createFX +Shader.SCREEN_FRAGMENT_FX = "\n\ + precision highp float;\n\ + uniform sampler2D u_texture;\n\ + varying vec2 v_coord;\n\ + #ifdef FX_UNIFORMS\n\ + FX_UNIFORMS\n\ + #endif\n\ + void main() {\n\ + vec2 uv = v_coord;\n\ + vec4 color = texture2D(u_texture, uv);\n\ + #ifdef FX_CODE\n\ + FX_CODE ;\n\ + #endif\n\ + gl_FragColor = color;\n\ + }\n\ + "; + +Shader.SCREEN_COLORED_FRAGMENT_SHADER = "\n\ + precision highp float;\n\ + uniform sampler2D u_texture;\n\ + uniform vec4 u_color;\n\ + varying vec2 v_coord;\n\ + void main() {\n\ + gl_FragColor = u_color * texture2D(u_texture, v_coord);\n\ + }\n\ + "; + +Shader.BLEND_FRAGMENT_SHADER = "\n\ + precision highp float;\n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_texture2;\n\ + uniform float u_factor;\n\ + varying vec2 v_coord;\n\ + void main() {\n\ + gl_FragColor = mix( texture2D(u_texture, v_coord), texture2D(u_texture2, v_coord), u_factor);\n\ + }\n\ + "; + +//used to paint quads +Shader.QUAD_VERTEX_SHADER = "\n\ + precision highp float;\n\ + attribute vec3 a_vertex;\n\ + attribute vec2 a_coord;\n\ + varying vec2 v_coord;\n\ + uniform vec2 u_position;\n\ + uniform vec2 u_size;\n\ + uniform vec2 u_viewport;\n\ + uniform mat3 u_transform;\n\ + void main() { \n\ + vec3 pos = vec3(u_position + vec2(a_coord.x,1.0 - a_coord.y) * u_size, 1.0);\n\ + v_coord = a_coord; \n\ + pos = u_transform * pos;\n\ + pos.z = 0.0;\n\ + //normalize\n\ + pos.x = (2.0 * pos.x / u_viewport.x) - 1.0;\n\ + pos.y = -((2.0 * pos.y / u_viewport.y) - 1.0);\n\ + gl_Position = vec4(pos, 1.0); \n\ + }\n\ + "; + +Shader.QUAD_FRAGMENT_SHADER = "\n\ + precision highp float;\n\ + uniform sampler2D u_texture;\n\ + uniform vec4 u_color;\n\ + varying vec2 v_coord;\n\ + void main() {\n\ + gl_FragColor = u_color * texture2D(u_texture, v_coord);\n\ + }\n\ + "; + +//used to render partially a texture +Shader.QUAD2_FRAGMENT_SHADER = "\n\ + precision highp float;\n\ + uniform sampler2D u_texture;\n\ + uniform vec4 u_color;\n\ + uniform vec4 u_texture_area;\n\ + varying vec2 v_coord;\n\ + void main() {\n\ + vec2 uv = vec2( mix(u_texture_area.x, u_texture_area.z, v_coord.x), 1.0 - mix(u_texture_area.w, u_texture_area.y, v_coord.y) );\n\ + gl_FragColor = u_color * texture2D(u_texture, uv);\n\ + }\n\ + "; + +Shader.PRIMITIVE2D_VERTEX_SHADER = "\n\ + precision highp float;\n\ + attribute vec3 a_vertex;\n\ + uniform vec2 u_viewport;\n\ + uniform mat3 u_transform;\n\ + void main() { \n\ + vec3 pos = a_vertex;\n\ + pos = u_transform * pos;\n\ + pos.z = 0.0;\n\ + //normalize\n\ + pos.x = (2.0 * pos.x / u_viewport.x) - 1.0;\n\ + pos.y = -((2.0 * pos.y / u_viewport.y) - 1.0);\n\ + gl_Position = vec4(pos, 1.0); \n\ + }\n\ + "; + +Shader.FLAT_VERTEX_SHADER = "\n\ + precision highp float;\n\ + attribute vec3 a_vertex;\n\ + uniform mat4 u_mvp;\n\ + void main() { \n\ + gl_Position = u_mvp * vec4(a_vertex,1.0); \n\ + }\n\ + "; + +Shader.FLAT_FRAGMENT_SHADER = "\n\ + precision highp float;\n\ + uniform vec4 u_color;\n\ + void main() {\n\ + gl_FragColor = u_color;\n\ + }\n\ + "; +Shader.SCREEN_FLAT_FRAGMENT_SHADER = Shader.FLAT_FRAGMENT_SHADER; //legacy + + +/** +* Allows to create a simple shader meant to be used to process a texture, instead of having to define the generic Vertex & Fragment Shader code +* @method Shader.createFX +* @param {string} code string containg code, like "color = color * 2.0;" +* @param {string} [uniforms=null] string containg extra uniforms, like "uniform vec3 u_pos;" +*/ +Shader.createFX = function(code, uniforms, shader) +{ + //remove comments + code = GL.Shader.removeComments( code, true ); //remove comments and breaklines to avoid problems with the macros + var macros = { + FX_CODE: code, + FX_UNIFORMS: uniforms || "" + } + if(!shader) + return new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, GL.Shader.SCREEN_FRAGMENT_FX, macros ); + shader.updateShader( GL.Shader.SCREEN_VERTEX_SHADER, GL.Shader.SCREEN_FRAGMENT_FX, macros ); + return shader; +} + +/** +* Given a shader code with some vars inside (like {{varname}}) and an object with the variable values, it will replace them. +* @method Shader.replaceCodeUsingContext +* @param {string} code string containg code and vars in {{varname}} format +* @param {object} context object containing all var values +*/ +Shader.replaceCodeUsingContext = function( code_template, context ) +{ + return code_template.replace(/\{\{[a-zA-Z0-9_]*\}\}/g, function(v){ + v = v.replace( /[\{\}]/g, "" ); + return context[v] || ""; + }); +} + +Shader.removeComments = function(code, one_line) +{ + if(!code) + return ""; + + var rx = /(\/\*([^*]|[\r\n]|(\*+([^*\/]|[\r\n])))*\*+\/)|(\/\/.*)/g; + var code = code.replace( rx ,""); + var lines = code.split("\n"); + var result = []; + for(var i = 0; i < lines.length; ++i) + { + var line = lines[i]; + var pos = line.indexOf("//"); + if(pos != -1) + line = lines[i].substr(0,pos); + line = line.trim(); + if(line.length) + result.push(line); + } + return result.join( one_line ? "" : "\n" ); +} + +/** +* Renders a fullscreen quad with this shader applied +* @method toViewport +* @param {object} uniforms +*/ +Shader.prototype.toViewport = function(uniforms) +{ + var mesh = GL.Mesh.getScreenQuad(); + if(uniforms) + this.uniforms(uniforms); + this.draw( mesh ); +} + +//Now some common shaders everybody needs + +/** +* Returns a shader ready to render a textured quad in fullscreen, use with Mesh.getScreenQuad() mesh +* shader params: sampler2D u_texture +* @method Shader.getScreenShader +*/ +Shader.getScreenShader = function(gl) +{ + gl = gl || global.gl; + var shader = gl.shaders[":screen"]; + if(shader) + return shader; + shader = gl.shaders[":screen"] = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, Shader.SCREEN_FRAGMENT_SHADER ); + return shader.uniforms({u_texture:0}); //do it the first time so I dont have to do it every time +} + +/** +* Returns a shader ready to render a flat color quad in fullscreen, use with Mesh.getScreenQuad() mesh +* shader params: vec4 u_color +* @method Shader.getFlatScreenShader +*/ +Shader.getFlatScreenShader = function(gl) +{ + gl = gl || global.gl; + var shader = gl.shaders[":flat_screen"]; + if(shader) + return shader; + shader = gl.shaders[":flat_screen"] = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, Shader.FLAT_FRAGMENT_SHADER ); + return shader.uniforms({u_color:[1,1,1,1]}); //do it the first time so I dont have to do it every time +} + +/** +* Returns a shader ready to render a colored textured quad in fullscreen, use with Mesh.getScreenQuad() mesh +* shader params vec4 u_color and sampler2D u_texture +* @method Shader.getColoredScreenShader +*/ +Shader.getColoredScreenShader = function(gl) +{ + gl = gl || global.gl; + var shader = gl.shaders[":colored_screen"]; + if(shader) + return shader; + shader = gl.shaders[":colored_screen"] = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, Shader.SCREEN_COLORED_FRAGMENT_SHADER ); + return shader.uniforms({u_texture:0, u_color: vec4.fromValues(1,1,1,1) }); //do it the first time so I dont have to do it every time +} + +/** +* Returns a shader ready to render a quad with transform, use with Mesh.getScreenQuad() mesh +* shader must have: u_position, u_size, u_viewport, u_transform (mat3) +* @method Shader.getQuadShader +*/ +Shader.getQuadShader = function(gl) +{ + gl = gl || global.gl; + var shader = gl.shaders[":quad"]; + if(shader) + return shader; + return gl.shaders[":quad"] = new GL.Shader( Shader.QUAD_VERTEX_SHADER, Shader.QUAD_FRAGMENT_SHADER ); +} + +/** +* Returns a shader ready to render part of a texture into the viewport +* shader must have: u_position, u_size, u_viewport, u_transform, u_texture_area (vec4) +* @method Shader.getPartialQuadShader +*/ +Shader.getPartialQuadShader = function(gl) +{ + gl = gl || global.gl; + var shader = gl.shaders[":quad2"]; + if(shader) + return shader; + return gl.shaders[":quad2"] = new GL.Shader( Shader.QUAD_VERTEX_SHADER, Shader.QUAD2_FRAGMENT_SHADER ); +} + +/** +* Returns a shader that blends two textures +* shader must have: u_factor, u_texture, u_texture2 +* @method Shader.getBlendShader +*/ +Shader.getBlendShader = function(gl) +{ + gl = gl || global.gl; + var shader = gl.shaders[":blend"]; + if(shader) + return shader; + return gl.shaders[":blend"] = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, Shader.BLEND_FRAGMENT_SHADER ); +} + +/** +* Returns a shader used to apply gaussian blur to one texture in one axis (you should use it twice to get a gaussian blur) +* shader params are: vec2 u_offset, float u_intensity +* @method Shader.getBlurShader +*/ +Shader.getBlurShader = function(gl) +{ + gl = gl || global.gl; + var shader = gl.shaders[":blur"]; + if(shader) + return shader; + + var shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER,"\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 u_offset;\n\ + uniform float u_intensity;\n\ + void main() {\n\ + vec4 sum = vec4(0.0);\n\ + sum += texture2D(u_texture, v_coord + u_offset * -4.0) * 0.05/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * -3.0) * 0.09/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * -2.0) * 0.12/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * -1.0) * 0.15/0.98;\n\ + sum += texture2D(u_texture, v_coord) * 0.16/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * 4.0) * 0.05/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * 3.0) * 0.09/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * 2.0) * 0.12/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * 1.0) * 0.15/0.98;\n\ + gl_FragColor = u_intensity * sum;\n\ + }\n\ + "); + return gl.shaders[":blur"] = shader; +} + +//shader to copy a depth texture into another one +Shader.getCopyDepthShader = function(gl) +{ + gl = gl || global.gl; + var shader = gl.shaders[":copy_depth"]; + if(shader) + return shader; + + var shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER,"\n\ + #extension GL_EXT_frag_depth : enable\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + void main() {\n\ + gl_FragDepthEXT = texture2D( u_texture, v_coord ).x;\n\ + gl_FragColor = vec4(1.0);\n\ + }\n\ + "); + return gl.shaders[":copy_depth"] = shader; +} + +Shader.getCubemapShowShader = function(gl) +{ + gl = gl || global.gl; + var shader = gl.shaders[":show_cubemap"]; + if(shader) + return shader; + + var shader = new GL.Shader( Shader.DEFAULT_VERTEX_SHADER,"\n\ + precision highp float;\n\ + varying vec3 v_normal;\n\ + uniform samplerCube u_texture;\n\ + void main() {\n\ + gl_FragColor = textureCube( u_texture, v_normal );\n\ + }\n\ + "); + shader.uniforms({u_texture:0}); + return gl.shaders[":show_cubemap"] = shader; +} + +//shader to copy a cubemap into another +Shader.getPolarToCubemapShader = function(gl) +{ + gl = gl || global.gl; + var shader = gl.shaders[":polar_to_cubemap"]; + if(shader) + return shader; + + var shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER,"\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform mat3 u_rotation;\n\ + void main() {\n\ + vec2 uv = vec2( v_coord.x, 1.0 - v_coord.y );\n\ + vec3 dir = normalize( vec3( uv - vec2(0.5), 0.5 ));\n\ + dir = u_rotation * dir;\n\ + float u = atan(dir.x,dir.z) / 6.28318531;\n\ + float v = (asin(dir.y) / 1.57079633) * 0.5 + 0.5;\n\ + u = mod(u,1.0);\n\ + v = mod(v,1.0);\n\ + gl_FragColor = texture2D( u_texture, vec2(u,v) );\n\ + }\n\ + "); + return gl.shaders[":polar_to_cubemap"] = shader; +} + + +//shader to copy a cubemap into another +Shader.getCubemapCopyShader = function(gl) +{ + gl = gl || global.gl; + var shader = gl.shaders[":copy_cubemap"]; + if(shader) + return shader; + + var shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER,"\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform samplerCube u_texture;\n\ + uniform mat3 u_rotation;\n\ + void main() {\n\ + vec2 uv = vec2( v_coord.x, 1.0 - v_coord.y );\n\ + vec3 dir = vec3( uv - vec2(0.5), 0.5 );\n\ + dir = u_rotation * dir;\n\ + gl_FragColor = textureCube( u_texture, dir );\n\ + }\n\ + "); + return gl.shaders[":copy_cubemap"] = shader; +} + +//shader to blur a cubemap +Shader.getCubemapBlurShader = function(gl) +{ + gl = gl || global.gl; + var shader = gl.shaders[":blur_cubemap"]; + if(shader) + return shader; + + var shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER,"\n\ + #ifndef NUM_SAMPLES\n\ + #define NUM_SAMPLES 4\n\ + #endif\n\ + \n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform samplerCube u_texture;\n\ + uniform mat3 u_rotation;\n\ + uniform vec2 u_offset;\n\ + uniform float u_intensity;\n\ + void main() {\n\ + vec4 sum = vec4(0.0);\n\ + vec2 uv = vec2( v_coord.x, 1.0 - v_coord.y ) - vec2(0.5);\n\ + vec3 dir = vec3(0.0);\n\ + vec4 color = vec4(0.0);\n\ + for( int x = -2; x <= 2; x++ )\n\ + {\n\ + for( int y = -2; y <= 2; y++ )\n\ + {\n\ + dir.xy = uv + vec2( u_offset.x * float(x), u_offset.y * float(y)) * 0.5;\n\ + dir.z = 0.5;\n\ + dir = u_rotation * dir;\n\ + color = textureCube( u_texture, dir );\n\ + color.xyz = color.xyz * color.xyz;/*linearize*/\n\ + sum += color;\n\ + }\n\ + }\n\ + sum /= 25.0;\n\ + gl_FragColor = vec4( sqrt( sum.xyz ), sum.w ) ;\n\ + }\n\ + "); + return gl.shaders[":blur_cubemap"] = shader; +} + +//shader to do FXAA (antialiasing) +Shader.FXAA_FUNC = "\n\ + uniform vec2 u_viewportSize;\n\ + uniform vec2 u_iViewportSize;\n\ + #define FXAA_REDUCE_MIN (1.0/ 128.0)\n\ + #define FXAA_REDUCE_MUL (1.0 / 8.0)\n\ + #define FXAA_SPAN_MAX 8.0\n\ + \n\ + /* from mitsuhiko/webgl-meincraft based on the code on geeks3d.com */\n\ + vec4 applyFXAA(sampler2D tex, vec2 fragCoord)\n\ + {\n\ + vec4 color = vec4(0.0);\n\ + /*vec2 u_iViewportSize = vec2(1.0 / u_viewportSize.x, 1.0 / u_viewportSize.y);*/\n\ + vec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * u_iViewportSize).xyz;\n\ + vec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * u_iViewportSize).xyz;\n\ + vec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * u_iViewportSize).xyz;\n\ + vec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * u_iViewportSize).xyz;\n\ + vec3 rgbM = texture2D(tex, fragCoord * u_iViewportSize).xyz;\n\ + vec3 luma = vec3(0.299, 0.587, 0.114);\n\ + float lumaNW = dot(rgbNW, luma);\n\ + float lumaNE = dot(rgbNE, luma);\n\ + float lumaSW = dot(rgbSW, luma);\n\ + float lumaSE = dot(rgbSE, luma);\n\ + float lumaM = dot(rgbM, luma);\n\ + float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n\ + float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\ + \n\ + vec2 dir;\n\ + dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n\ + dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\ + \n\ + float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\ + \n\ + float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n\ + dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * u_iViewportSize;\n\ + \n\ + vec3 rgbA = 0.5 * (texture2D(tex, fragCoord * u_iViewportSize + dir * (1.0 / 3.0 - 0.5)).xyz + \n\ + texture2D(tex, fragCoord * u_iViewportSize + dir * (2.0 / 3.0 - 0.5)).xyz);\n\ + vec3 rgbB = rgbA * 0.5 + 0.25 * (texture2D(tex, fragCoord * u_iViewportSize + dir * -0.5).xyz + \n\ + texture2D(tex, fragCoord * u_iViewportSize + dir * 0.5).xyz);\n\ + \n\ + return vec4(rgbA,1.0);\n\ + float lumaB = dot(rgbB, luma);\n\ + if ((lumaB < lumaMin) || (lumaB > lumaMax))\n\ + color = vec4(rgbA, 1.0);\n\ + else\n\ + color = vec4(rgbB, 1.0);\n\ + return color;\n\ + }\n\ +"; + +/** +* Returns a shader to apply FXAA antialiasing +* params are vec2 u_viewportSize, vec2 u_iViewportSize or you can call shader.setup() +* @method Shader.getFXAAShader +*/ +Shader.getFXAAShader = function(gl) +{ + gl = gl || global.gl; + var shader = gl.shaders[":fxaa"]; + if(shader) + return shader; + + var shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER,"\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + " + Shader.FXAA_FUNC + "\n\ + \n\ + void main() {\n\ + gl_FragColor = applyFXAA( u_texture, v_coord * u_viewportSize) ;\n\ + }\n\ + "); + + var viewport = vec2.fromValues( gl.viewport_data[2], gl.viewport_data[3] ); + var iviewport = vec2.fromValues( 1/gl.viewport_data[2], 1/gl.viewport_data[3] ); + + shader.setup = function() { + viewport[0] = gl.viewport_data[2]; + viewport[1] = gl.viewport_data[3]; + iviewport[0] = 1/gl.viewport_data[2]; + iviewport[1] = 1/gl.viewport_data[3]; + this.uniforms({ u_viewportSize: viewport, u_iViewportSize: iviewport }); + } + return gl.shaders[":fxaa"] = shader; +} + +/** +* Returns a flat shader (useful to render lines) +* @method Shader.getFlatShader +*/ +Shader.getFlatShader = function(gl) +{ + gl = gl || global.gl; + var shader = gl.shaders[":flat"]; + if(shader) + return shader; + + var shader = new GL.Shader( Shader.FLAT_VERTEX_SHADER,Shader.FLAT_FRAGMENT_SHADER); + shader.uniforms({u_color:[1,1,1,1]}); + return gl.shaders[":flat"] = shader; +} + +/** +* The global scope that contains all the classes from LiteGL and also all the enums of WebGL so you dont need to create a context to use the values. +* @class GL +*/ + +/** +* creates a new WebGL context (it can create the canvas or use an existing one) +* @method create +* @param {Object} options supported are: +* - width +* - height +* - canvas +* - container (string or element) +* @return {WebGLRenderingContext} webgl context with all the extra functions (check gl in the doc for more info) +*/ +GL.create = function(options) { + options = options || {}; + var canvas = null; + if(options.canvas) + { + if(typeof(options.canvas) == "string") + { + canvas = document.getElementById( options.canvas ); + if(!canvas) throw("Canvas element not found: " + options.canvas ); + } + else + canvas = options.canvas; + } + else + { + var root = null; + if(options.container) + root = options.container.constructor === String ? document.querySelector( options.container ) : options.container; + if(root && !options.width) + { + var rect = root.getBoundingClientRect(); + options.width = rect.width; + options.height = rect.height; + } + + canvas = createCanvas( options.width || 800, options.height || 600 ); + if(root) + root.appendChild(canvas); + } + + if (!('alpha' in options)) options.alpha = false; + + + /** + * the webgl context returned by GL.create, its a WebGLRenderingContext with some extra methods added + * @class gl + */ + var gl = null; + + var seq = null; + if(options.version == 2) + seq = ['webgl2','experimental-webgl2']; + else if(options.version == 1 || options.version === undefined) //default + seq = ['webgl','experimental-webgl']; + else if(options.version === 0) //latest + seq = ['webgl2','experimental-webgl2','webgl','experimental-webgl']; + + if(!seq) + throw 'Incorrect WebGL version, must be 1 or 2'; + + var context_options = { + alpha: options.alpha === undefined ? true : options.alpha, + depth: options.depth === undefined ? true : options.depth, + stencil: options.stencil === undefined ? true : options.stencil, + antialias: options.antialias === undefined ? true : options.antialias, + premultipliedAlpha: options.premultipliedAlpha === undefined ? true : options.premultipliedAlpha, + preserveDrawingBuffer: options.preserveDrawingBuffer === undefined ? true : options.preserveDrawingBuffer + }; + + for(var i = 0; i < seq.length; ++i) + { + try { gl = canvas.getContext( seq[i], context_options ); } catch (e) {} + if(gl) + break; + } + + if (!gl) + { + if( canvas.getContext( "webgl" ) ) + throw 'WebGL supported but not with those parameters'; + throw 'WebGL not supported'; + } + + //context globals + gl.webgl_version = gl.constructor.name === "WebGL2RenderingContext" ? 2 : 1; + global.gl = gl; + canvas.is_webgl = true; + canvas.gl = gl; + gl.context_id = this.last_context_id++; + + //get all supported extensions + var supported_extensions = gl.getSupportedExtensions(); + gl.extensions = {}; + for(var i in supported_extensions) + gl.extensions[ supported_extensions[i] ] = gl.getExtension( supported_extensions[i] ); + gl.derivatives_supported = gl.extensions['OES_standard_derivatives'] != null || gl.webgl_version > 1; + + /* + gl.extensions["OES_standard_derivatives"] = gl.derivatives_supported = gl.getExtension('OES_standard_derivatives') || false; + gl.extensions["WEBGL_depth_texture"] = gl.getExtension("WEBGL_depth_texture") || gl.getExtension("WEBKIT_WEBGL_depth_texture") || gl.getExtension("MOZ_WEBGL_depth_texture"); + gl.extensions["OES_element_index_uint"] = gl.getExtension("OES_element_index_uint"); + gl.extensions["WEBGL_draw_buffers"] = gl.getExtension("WEBGL_draw_buffers"); + gl.extensions["EXT_shader_texture_lod"] = gl.getExtension("EXT_shader_texture_lod"); + gl.extensions["EXT_sRGB"] = gl.getExtension("EXT_sRGB"); + gl.extensions["EXT_texture_filter_anisotropic"] = gl.getExtension("EXT_texture_filter_anisotropic") || gl.getExtension("WEBKIT_EXT_texture_filter_anisotropic") || gl.getExtension("MOZ_EXT_texture_filter_anisotropic"); + gl.extensions["EXT_frag_depth"] = gl.getExtension("EXT_frag_depth") || gl.getExtension("WEBKIT_EXT_frag_depth") || gl.getExtension("MOZ_EXT_frag_depth"); + gl.extensions["WEBGL_lose_context"] = gl.getExtension("WEBGL_lose_context") || gl.getExtension("WEBKIT_WEBGL_lose_context") || gl.getExtension("MOZ_WEBGL_lose_context"); + gl.extensions["ANGLE_instanced_arrays"] = gl.getExtension("ANGLE_instanced_arrays"); + gl.extensions["disjoint_timer_query"] = gl.getExtension("EXT_disjoint_timer_query"); + + //for float textures + gl.extensions["OES_texture_float_linear"] = gl.getExtension("OES_texture_float_linear"); + if(gl.extensions["OES_texture_float_linear"]) + gl.extensions["OES_texture_float"] = gl.getExtension("OES_texture_float"); + gl.extensions["EXT_color_buffer_float"] = gl.getExtension("EXT_color_buffer_float"); + + //for half float textures in webgl 1 require extension + gl.extensions["OES_texture_half_float_linear"] = gl.getExtension("OES_texture_half_float_linear"); + if(gl.extensions["OES_texture_half_float_linear"]) + gl.extensions["OES_texture_half_float"] = gl.getExtension("OES_texture_half_float"); + */ + + if( gl.webgl_version == 1 ) + gl.HIGH_PRECISION_FORMAT = gl.extensions["OES_texture_half_float"] ? GL.HALF_FLOAT_OES : (gl.extensions["OES_texture_float"] ? GL.FLOAT : GL.UNSIGNED_BYTE); //because Firefox dont support half float + else + gl.HIGH_PRECISION_FORMAT = GL.HALF_FLOAT_OES; + + gl.max_texture_units = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); + + //viewport hack to retrieve it without using getParameter (which is slow and generates garbage) + if(!gl._viewport_func) + { + gl._viewport_func = gl.viewport; + gl.viewport_data = new Float32Array([0,0,gl.canvas.width,gl.canvas.height]); //32000 max viewport, I guess its fine + gl.viewport = function(a,b,c,d) { var v = this.viewport_data; v[0] = a|0; v[1] = b|0; v[2] = c|0; v[3] = d|0; this._viewport_func(a,b,c,d); } + gl.getViewport = function(v) { + if(v) { v[0] = gl.viewport_data[0]; v[1] = gl.viewport_data[1]; v[2] = gl.viewport_data[2]; v[3] = gl.viewport_data[3]; return v; } + return new Float32Array( gl.viewport_data ); + }; + gl.setViewport = function( v, flip_y ) { + gl.viewport_data.set(v); + if(flip_y) + gl.viewport_data[1] = this.drawingBufferHeight-v[1]-v[3]; + this._viewport_func(v[0],gl.viewport_data[1],v[2],v[3]); + }; + } + else + console.warn("Creating LiteGL context over the same canvas twice"); + + //reverse names helper (assuming no names repeated) + if(!GL.reverse) + { + GL.reverse = {}; + for(var i in gl) + if( gl[i] && gl[i].constructor === Number ) + GL.reverse[ gl[i] ] = i; + } + + //just some checks + if(typeof(glMatrix) == "undefined") + throw("glMatrix not found, LiteGL requires glMatrix to be included"); + + var last_click_time = 0; + + //some global containers, use them to reuse assets + gl.shaders = {}; + gl.textures = {}; + gl.meshes = {}; + + /** + * sets this context as the current global gl context (in case you have more than one) + * @method makeCurrent + */ + gl.makeCurrent = function() + { + global.gl = this; + } + + /** + * executes callback inside this webgl context + * @method execute + * @param {Function} callback + */ + gl.execute = function(callback) + { + var old_gl = global.gl; + global.gl = this; + callback(); + global.gl = old_gl; + } + + + /** + * Launch animation loop (calls gl.onupdate and gl.ondraw every frame) + * example: gl.ondraw = function(){ ... } or gl.onupdate = function(dt) { ... } + * @method animate + */ + gl.animate = function(v) { + if(v === false) + { + global.cancelAnimationFrame( this._requestFrame_id ); + this._requestFrame_id = null; + return; + } + + var post = global.requestAnimationFrame; + var time = getTime(); + var context = this; + + //loop only if browser tab visible + function loop() { + if(gl.destroyed) //to stop rendering once it is destroyed + return; + + context._requestFrame_id = post(loop); //do it first, in case it crashes + + var now = getTime(); + var dt = (now - time) * 0.001; + if(context.mouse) + context.mouse.last_buttons = context.mouse.buttons; + if (context.onupdate) + context.onupdate(dt); + LEvent.trigger( context, "update", dt); + if (context.ondraw) + { + //make sure the ondraw is called using this gl context (in case there is more than one) + var old_gl = global.gl; + global.gl = context; + //call ondraw + context.ondraw(); + LEvent.trigger(context,"draw"); + //restore old context + global.gl = old_gl; + } + time = now; + } + this._requestFrame_id = post(loop); //launch main loop + } + + //store binded to be able to remove them if destroyed + /* + var _binded_events = []; + function addEvent(object, type, callback) + { + _binded_events.push(object,type,callback); + } + */ + + /** + * Destroy this WebGL context (removes also the Canvas from the DOM) + * @method destroy + */ + gl.destroy = function() { + //unbind global events + if(onkey_handler) + { + document.removeEventListener("keydown", onkey_handler ); + document.removeEventListener("keyup", onkey_handler ); + } + + if(this.canvas.parentNode) + this.canvas.parentNode.removeChild(this.canvas); + this.destroyed = true; + if(global.gl == this) + global.gl = null; + } + + var mouse = gl.mouse = { + buttons: 0, //this should always be up-to-date with mouse state + last_buttons: 0, //button state in the previous frame + left_button: false, + middle_button: false, + right_button: false, + position: new Float32Array(2), + x:0, //in canvas coordinates + y:0, + deltax: 0, + deltay: 0, + clientx:0, //in client coordinates + clienty:0, + isInsideRect: function(x,y,w,h, flip_y ) + { + var mouse_y = this.y; + if(flip_y) + mouse_y = gl.canvas.height - mouse_y; + if( this.x > x && this.x < x + w && + mouse_y > y && mouse_y < y + h) + return true; + return false; + }, + + /** + * returns true if button num is pressed (where num could be GL.LEFT_MOUSE_BUTTON, GL.RIGHT_MOUSE_BUTTON, GL.MIDDLE_MOUSE_BUTTON + * @method captureMouse + * @param {boolean} capture_wheel capture also the mouse wheel + */ + isButtonPressed: function(num) + { + if(num == GL.LEFT_MOUSE_BUTTON) + return this.buttons & GL.LEFT_MOUSE_BUTTON_MASK; + if(num == GL.MIDDLE_MOUSE_BUTTON) + return this.buttons & GL.MIDDLE_MOUSE_BUTTON_MASK; + if(num == GL.RIGHT_MOUSE_BUTTON) + return this.buttons & GL.RIGHT_MOUSE_BUTTON_MASK; + }, + + wasButtonPressed: function(num) + { + var mask = 0; + if(num == GL.LEFT_MOUSE_BUTTON) + mask = GL.LEFT_MOUSE_BUTTON_MASK; + else if(num == GL.MIDDLE_MOUSE_BUTTON) + mask = GL.MIDDLE_MOUSE_BUTTON_MASK; + else if(num == GL.RIGHT_MOUSE_BUTTON) + mask = GL.RIGHT_MOUSE_BUTTON_MASK; + return (this.buttons & mask) && !(this.last_buttons & mask); + } + }; + + /** + * Tells the system to capture mouse events on the canvas. + * This will trigger onmousedown, onmousemove, onmouseup, onmousewheel callbacks assigned in the gl context + * example: gl.onmousedown = function(e){ ... } + * The event is a regular MouseEvent with some extra parameters + * @method captureMouse + * @param {boolean} capture_wheel capture also the mouse wheel + */ + gl.captureMouse = function(capture_wheel, translate_touchs ) { + + canvas.addEventListener("mousedown", onmouse); + canvas.addEventListener("mousemove", onmouse); + canvas.addEventListener("dragstart", onmouse); + //canvas.addEventListener("mouseup", onmouse); ?? + if(capture_wheel) + { + canvas.addEventListener("mousewheel", onmouse, false); + canvas.addEventListener("wheel", onmouse, false); + //canvas.addEventListener("DOMMouseScroll", onmouse, false); //deprecated or non-standard + } + //prevent right click context menu + canvas.addEventListener("contextmenu", function(e) { e.preventDefault(); return false; }); + + if( translate_touchs ) + this.captureTouch( true ); + } + + function onmouse(e) { + + if(gl.ignore_events) + return; + //console.log(e.type); //debug + var old_mouse_mask = gl.mouse.buttons; + GL.augmentEvent(e, canvas); + e.eventType = e.eventType || e.type; //type cannot be overwritten, so I make a clone to allow me to overwrite + var now = getTime(); + + //gl.mouse info + mouse.dragging = e.dragging; + mouse.position[0] = e.canvasx; + mouse.position[1] = e.canvasy; + mouse.x = e.canvasx; + mouse.y = e.canvasy; + mouse.mousex = e.mousex; + mouse.mousey = e.mousey; + mouse.canvasx = e.canvasx; + mouse.canvasy = e.canvasy; + mouse.clientx = e.mousex; + mouse.clienty = e.mousey; + mouse.buttons = e.buttons; + mouse.left_button = !!(mouse.buttons & GL.LEFT_MOUSE_BUTTON_MASK); + mouse.middle_button = !!(mouse.buttons & GL.MIDDLE_MOUSE_BUTTON_MASK); + mouse.right_button = !!(mouse.buttons & GL.RIGHT_MOUSE_BUTTON_MASK); + + if(e.eventType == "mousedown") + { + if(old_mouse_mask == 0) //no mouse button was pressed till now + { + canvas.removeEventListener("mousemove", onmouse); + var doc = canvas.ownerDocument; + doc.addEventListener("mousemove", onmouse); + doc.addEventListener("mouseup", onmouse); + } + last_click_time = now; + + if(gl.onmousedown) + gl.onmousedown(e); + LEvent.trigger(gl,"mousedown"); + } + else if(e.eventType == "mousemove") + { + if(gl.onmousemove) + gl.onmousemove(e); + LEvent.trigger(gl,"mousemove",e); + } + else if(e.eventType == "mouseup") + { + //console.log("mouseup"); + if(gl.mouse.buttons == 0) //no more buttons pressed + { + canvas.addEventListener("mousemove", onmouse); + var doc = canvas.ownerDocument; + doc.removeEventListener("mousemove", onmouse); + doc.removeEventListener("mouseup", onmouse); + } + e.click_time = now - last_click_time; + //last_click_time = now; //commented to avoid reseting click time when unclicking two mouse buttons + + if(gl.onmouseup) + gl.onmouseup(e); + LEvent.trigger(gl,"mouseup",e); + } + else if((e.eventType == "mousewheel" || e.eventType == "wheel" || e.eventType == "DOMMouseScroll")) + { + e.eventType = "mousewheel"; + if(e.type == "wheel") + e.wheel = -e.deltaY; //in firefox deltaY is 1 while in Chrome is 120 + else + e.wheel = (e.wheelDeltaY != null ? e.wheelDeltaY : e.detail * -60); + + //from stack overflow + //firefox doesnt have wheelDelta + e.delta = e.wheelDelta !== undefined ? (e.wheelDelta/40) : (e.deltaY ? -e.deltaY/3 : 0); + //console.log(e.delta); + if(gl.onmousewheel) + gl.onmousewheel(e); + + LEvent.trigger(gl, "mousewheel", e); + } + else if(e.eventType == "dragstart") + { + if(gl.ondragstart) + gl.ondragstart(e); + LEvent.trigger(gl, "dragstart", e); + } + + if(gl.onmouse) + gl.onmouse(e); + + if(!e.skip_preventDefault) + { + if(e.eventType != "mousemove") + e.stopPropagation(); + e.preventDefault(); + return false; + } + } + + var translate_touches = false; + + gl.captureTouch = function( translate_to_mouse_events ) + { + translate_touches = translate_to_mouse_events; + + canvas.addEventListener("touchstart", ontouch, true); + canvas.addEventListener("touchmove", ontouch, true); + canvas.addEventListener("touchend", ontouch, true); + canvas.addEventListener("touchcancel", ontouch, true); + + canvas.addEventListener('gesturestart', ongesture ); + canvas.addEventListener('gesturechange', ongesture ); + canvas.addEventListener('gestureend', ongesture ); + } + + //translates touch events in mouseevents + function ontouch( e ) + { + var touches = e.changedTouches, + first = touches[0], + type = ""; + + if( gl.ontouch && gl.ontouch(e) === true ) + return; + + if( LEvent.trigger( gl, e.type, e ) === true ) + return; + + if(!translate_touches) + return; + + //ignore secondary touches + if(e.touches.length && e.changedTouches[0].identifier !== e.touches[0].identifier) + return; + + if(touches > 1) + return; + + switch(e.type) + { + case "touchstart": type = "mousedown"; break; + case "touchmove": type = "mousemove"; break; + case "touchend": type = "mouseup"; break; + default: return; + } + + var simulatedEvent = document.createEvent("MouseEvent"); + simulatedEvent.initMouseEvent(type, true, true, window, 1, + first.screenX, first.screenY, + first.clientX, first.clientY, false, + false, false, false, 0/*left*/, null); + simulatedEvent.originalEvent = simulatedEvent; + simulatedEvent.is_touch = true; + first.target.dispatchEvent( simulatedEvent ); + e.preventDefault(); + } + + function ongesture(e) + { + e.eventType = e.type; + + if(gl.ongesture && gl.ongesture(e) === false ) + return; + + if( LEvent.trigger( gl, e.type, e ) === false ) + return; + + e.preventDefault(); + } + + var keys = gl.keys = {}; + + /** + * Tells the system to capture key events on the canvas. This will trigger onkey + * @method captureKeys + * @param {boolean} prevent_default prevent default behaviour (like scroll on the web, etc) + * @param {boolean} only_canvas only caches keyboard events if they happen when the canvas is in focus + */ + var onkey_handler = null; + gl.captureKeys = function( prevent_default, only_canvas ) { + if(onkey_handler) + return; + gl.keys = {}; + + var target = only_canvas ? gl.canvas : document; + + document.addEventListener("keydown", inner ); + document.addEventListener("keyup", inner ); + function inner(e) { onkey(e, prevent_default); } + onkey_handler = inner; + } + + + + function onkey(e, prevent_default) + { + e.eventType = e.type; //type cannot be overwritten, so I make a clone to allow me to overwrite + + var target_element = e.target.nodeName.toLowerCase(); + if(target_element === "input" || target_element === "textarea" || target_element === "select") + return; + + e.character = String.fromCharCode(e.keyCode).toLowerCase(); + var prev_state = false; + var key = GL.mapKeyCode(e.keyCode); + if(!key) //this key doesnt look like an special key + key = e.character; + + //regular key + if (!e.altKey && !e.ctrlKey && !e.metaKey) { + if (key) + gl.keys[key] = e.type == "keydown"; + prev_state = gl.keys[e.keyCode]; + gl.keys[e.keyCode] = e.type == "keydown"; + } + + //avoid repetition if key stays pressed + if(prev_state != gl.keys[e.keyCode]) + { + if(e.type == "keydown" && gl.onkeydown) + gl.onkeydown(e); + else if(e.type == "keyup" && gl.onkeyup) + gl.onkeyup(e); + LEvent.trigger(gl, e.type, e); + } + + if(gl.onkey) + gl.onkey(e); + + if(prevent_default && (e.isChar || GL.blockable_keys[e.keyIdentifier || e.key ]) ) + e.preventDefault(); + } + + //gamepads + gl.gamepads = null; + /* + function onButton(e, pressed) + { + console.log(e); + if(pressed && gl.onbuttondown) + gl.onbuttondown(e); + else if(!pressed && gl.onbuttonup) + gl.onbuttonup(e); + if(gl.onbutton) + gl.onbutton(e); + LEvent.trigger(gl, pressed ? "buttondown" : "buttonup", e ); + } + function onGamepad(e) + { + console.log(e); + if(gl.ongamepad) + gl.ongamepad(e); + } + */ + + /** + * Tells the system to capture gamepad events on the canvas. + * @method captureGamepads + */ + gl.captureGamepads = function() + { + var getGamepads = navigator.getGamepads || navigator.webkitGetGamepads || navigator.mozGetGamepads; + if(!getGamepads) return; + this.gamepads = getGamepads.call(navigator); + + //only in firefox, so I cannot rely on this + /* + window.addEventListener("gamepadButtonDown", function(e) { onButton(e, true); }, false); + window.addEventListener("MozGamepadButtonDown", function(e) { onButton(e, true); }, false); + window.addEventListener("WebkitGamepadButtonDown", function(e) { onButton(e, true); }, false); + window.addEventListener("gamepadButtonUp", function(e) { onButton(e, false); }, false); + window.addEventListener("MozGamepadButtonUp", function(e) { onButton(e, false); }, false); + window.addEventListener("WebkitGamepadButtonUp", function(e) { onButton(e, false); }, false); + window.addEventListener("gamepadconnected", onGamepad, false); + window.addEventListener("gamepaddisconnected", onGamepad, false); + */ + + } + + /** + * returns the detected gamepads on the system + * @method getGamepads + * @param {bool} skip_mapping if set to true it returns the basic gamepad, otherwise it returns a class with mapping info to XBOX controller + */ + gl.getGamepads = function(skip_mapping) + { + //gamepads + var getGamepads = navigator.getGamepads || navigator.webkitGetGamepads || navigator.mozGetGamepads; + if(!getGamepads) + return; + var gamepads = getGamepads.call(navigator); + if(!this.gamepads) + this.gamepads = []; + + //iterate to generate events + for(var i = 0; i < 4; i++) + { + var gamepad = gamepads[i]; //current state + + if(gamepad && !skip_mapping) + addGamepadXBOXmapping(gamepad); + + //launch connected gamepads events + if(gamepad && !gamepad.prev_buttons) + { + gamepad.prev_buttons = new Uint8Array(32); + var event = new CustomEvent("gamepadconnected"); + event.eventType = event.type; + event.gamepad = gamepad; + if(this.ongamepadconnected) + this.ongamepadconnected(event); + LEvent.trigger(gl,"gamepadconnected",event); + } + /* + else if(old_gamepad && !gamepad) + { + var event = new CustomEvent("gamepaddisconnected"); + event.eventType = event.type; + event.gamepad = old_gamepad; + if(this.ongamepaddisconnected) + this.ongamepaddisconnected(event); + LEvent.trigger(gl,"gamepaddisconnected",event); + } + */ + + //seek buttons changes to trigger events + if(gamepad) + { + for(var j = 0; j < gamepad.buttons.length; ++j) + { + var button = gamepad.buttons[j]; + button.was_pressed = false; + if( button.pressed && !gamepad.prev_buttons[j] ) + { + button.was_pressed = true; + var event = new CustomEvent("gamepadButtonDown"); + event.eventType = event.type; + event.button = button; + event.which = j; + event.gamepad = gamepad; + if(gl.onbuttondown) + gl.onbuttondown(event); + LEvent.trigger(gl,"buttondown",event); + } + else if( !button.pressed && gamepad.prev_buttons[j] ) + { + var event = new CustomEvent("gamepadButtonUp"); + event.eventType = event.type; + event.button = button; + event.which = j; + event.gamepad = gamepad; + if(gl.onbuttondown) + gl.onbuttondown(event); + LEvent.trigger(gl,"buttonup",event); + } + + gamepad.prev_buttons[j] = button.pressed ? 1 : 0; + } + } + } + this.gamepads = gamepads; + return gamepads; + } + + function addGamepadXBOXmapping(gamepad) + { + //xbox controller mapping + var xbox = gamepad.xbox || { axes:[], buttons:{}, hat: ""}; + xbox.axes["lx"] = gamepad.axes[0]; + xbox.axes["ly"] = gamepad.axes[1]; + xbox.axes["rx"] = gamepad.axes[2]; + xbox.axes["ry"] = gamepad.axes[3]; + xbox.axes["triggers"] = gamepad.axes[4]; + + for(var i = 0; i < gamepad.buttons.length; i++) + { + switch(i) //I use a switch to ensure that a player with another gamepad could play + { + case 0: xbox.buttons["a"] = gamepad.buttons[i].pressed; break; + case 1: xbox.buttons["b"] = gamepad.buttons[i].pressed; break; + case 2: xbox.buttons["x"] = gamepad.buttons[i].pressed; break; + case 3: xbox.buttons["y"] = gamepad.buttons[i].pressed; break; + case 4: xbox.buttons["lb"] = gamepad.buttons[i].pressed; break; + case 5: xbox.buttons["rb"] = gamepad.buttons[i].pressed; break; + case 6: xbox.buttons["lt"] = gamepad.buttons[i].pressed; break; + case 7: xbox.buttons["rt"] = gamepad.buttons[i].pressed; break; + case 8: xbox.buttons["back"] = gamepad.buttons[i].pressed; break; + case 9: xbox.buttons["start"] = gamepad.buttons[i].pressed; break; + case 10: xbox.buttons["ls"] = gamepad.buttons[i].pressed; break; + case 11: xbox.buttons["rs"] = gamepad.buttons[i].pressed; break; + case 12: if( gamepad.buttons[i].pressed) xbox.hat += "up"; break; + case 13: if( gamepad.buttons[i].pressed) xbox.hat += "down"; break; + case 14: if( gamepad.buttons[i].pressed) xbox.hat += "left"; break; + case 15: if( gamepad.buttons[i].pressed) xbox.hat += "right"; break; + case 16: xbox.buttons["home"] = gamepad.buttons[i].pressed; break; + default: + } + } + gamepad.xbox = xbox; + } + + /** + * launches de canvas in fullscreen mode + * @method fullscreen + */ + gl.fullscreen = function() + { + var canvas = this.canvas; + if(canvas.requestFullScreen) + canvas.requestFullScreen(); + else if(canvas.webkitRequestFullScreen) + canvas.webkitRequestFullScreen(); + else if(canvas.mozRequestFullScreen) + canvas.mozRequestFullScreen(); + else + console.error("Fullscreen not supported"); + } + + /** + * returns a canvas with a snapshot of an area + * this is safer than using the canvas itself due to internals of webgl + * @method snapshot + * @param {Number} startx viewport x coordinate + * @param {Number} starty viewport y coordinate from bottom + * @param {Number} areax viewport area width + * @param {Number} areay viewport area height + * @return {Canvas} canvas + */ + gl.snapshot = function(startx, starty, areax, areay, skip_reverse) + { + var canvas = createCanvas(areax,areay); + var ctx = canvas.getContext("2d"); + var pixels = ctx.getImageData(0,0,canvas.width,canvas.height); + + var buffer = new Uint8Array(areax * areay * 4); + gl.readPixels(startx, starty, canvas.width, canvas.height, gl.RGBA,gl.UNSIGNED_BYTE, buffer); + + pixels.data.set( buffer ); + ctx.putImageData(pixels,0,0); + + if(skip_reverse) + return canvas; + + //flip image + var final_canvas = createCanvas(areax,areay); + var ctx = final_canvas.getContext("2d"); + ctx.translate(0,areay); + ctx.scale(1,-1); + ctx.drawImage(canvas,0,0); + + return final_canvas; + } + + //from https://webgl2fundamentals.org/webgl/lessons/webgl1-to-webgl2.html + function getAndApplyExtension( gl, name ) { + var ext = gl.getExtension(name); + if (!ext) { + return false; + } + var suffix = name.split("_")[0]; + var prefix = suffix = '_'; + var suffixRE = new RegExp(suffix + '$'); + var prefixRE = new RegExp('^' + prefix); + for (var key in ext) { + var val = ext[key]; + if (typeof(val) === 'function') { + // remove suffix (eg: bindVertexArrayOES -> bindVertexArray) + var unsuffixedKey = key.replace(suffixRE, ''); + if (key.substing) + gl[unprefixedKey] = ext[key].bind(ext); + } else { + var unprefixedKey = key.replace(prefixRE, ''); + gl[unprefixedKey] = ext[key]; + } + } + } + + + //mini textures manager + var loading_textures = {}; + /** + * returns a texture and caches it inside gl.textures[] + * @method loadTexture + * @param {String} url + * @param {Object} options (same options as when creating a texture) + * @param {Function} callback function called once the texture is loaded + * @return {Texture} texture + */ + gl.loadTexture = function(url, options, on_load) + { + if(this.textures[ url ]) + return this.textures[url]; + + if( loading_textures[url] ) + return null; + + var img = new Image(); + img.url = url; + img.onload = function() + { + var texture = GL.Texture.fromImage(this, options); + texture.img = this; + gl.textures[this.url] = texture; + delete loading_textures[this.url]; + if(on_load) + on_load(texture); + } + img.src = url; + loading_textures[url] = true; + return null; + } + + /** + * draws a texture to the viewport + * @method drawTexture + * @param {Texture} texture + * @param {number} x in viewport coordinates + * @param {number} y in viewport coordinates + * @param {number} w in viewport coordinates + * @param {number} h in viewport coordinates + * @param {number} tx texture x in texture coordinates + * @param {number} ty texture y in texture coordinates + * @param {number} tw texture width in texture coordinates + * @param {number} th texture height in texture coordinates + */ + gl.drawTexture = (function() { + //static variables: less garbage + var identity = mat3.create(); + var pos = vec2.create(); + var size = vec2.create(); + var area = vec4.create(); + var white = vec4.fromValues(1,1,1,1); + var viewport = vec2.create(); + var _uniforms = {u_texture: 0, u_position: pos, u_color: white, u_size: size, u_texture_area: area, u_viewport: viewport, u_transform: identity }; + + return (function(texture, x,y, w,h, tx,ty, tw,th, shader, uniforms) + { + pos[0] = x; pos[1] = y; + if(w === undefined) + w = texture.width; + if(h === undefined) + h = texture.height; + size[0] = w; + size[1] = h; + + if(tx === undefined) tx = 0; + if(ty === undefined) ty = 0; + if(tw === undefined) tw = texture.width; + if(th === undefined) th = texture.height; + + area[0] = tx / texture.width; + area[1] = ty / texture.height; + area[2] = (tx + tw) / texture.width; + area[3] = (ty + th) / texture.height; + + viewport[0] = this.viewport_data[2]; + viewport[1] = this.viewport_data[3]; + + shader = shader || Shader.getPartialQuadShader(this); + var mesh = Mesh.getScreenQuad(this); + texture.bind(0); + shader.uniforms( _uniforms ); + if( uniforms ) + shader.uniforms( uniforms ); + shader.draw( mesh, gl.TRIANGLES ); + }); + })(); + + gl.canvas.addEventListener("webglcontextlost", function(e) { + e.preventDefault(); + gl.context_lost = true; + if(gl.onlosecontext) + gl.onlosecontext(e); + }, false); + + /** + * use it to reset the the initial gl state + * @method gl.reset + */ + gl.reset = function() + { + //viewport + gl.viewport(0,0, this.canvas.width, this.canvas.height ); + + //flags + gl.disable( gl.BLEND ); + gl.disable( gl.CULL_FACE ); + gl.disable( gl.DEPTH_TEST ); + gl.frontFace( gl.CCW ); + + //texture + gl._current_texture_drawto = null; + gl._current_fbo_color = null; + gl._current_fbo_depth = null; + } + + gl.dump = function() + { + console.log("userAgent: ", navigator.userAgent ); + console.log("Supported extensions:"); + var extensions = gl.getSupportedExtensions(); + console.log( extensions.join(",") ); + var info = [ "VENDOR", "VERSION", "MAX_VERTEX_ATTRIBS", "MAX_VARYING_VECTORS", "MAX_VERTEX_UNIFORM_VECTORS", "MAX_VERTEX_TEXTURE_IMAGE_UNITS", "MAX_FRAGMENT_UNIFORM_VECTORS", "MAX_TEXTURE_SIZE", "MAX_TEXTURE_IMAGE_UNITS" ]; + console.log("WebGL info:"); + for(var i in info) + console.log(" * " + info[i] + ": " + gl.getParameter( gl[info[i]] )); + console.log("*************************************************") + } + + //Reset state + gl.reset(); + + //Return + return gl; +} + +GL.mapKeyCode = function(code) +{ + var named = { + 8: 'BACKSPACE', + 9: 'TAB', + 13: 'ENTER', + 16: 'SHIFT', + 17: 'CTRL', + 27: 'ESCAPE', + 32: 'SPACE', + 37: 'LEFT', + 38: 'UP', + 39: 'RIGHT', + 40: 'DOWN' + }; + return named[code] || (code >= 65 && code <= 90 ? String.fromCharCode(code) : null); +} + +//add useful info to the event +GL.dragging = false; +GL.last_pos = [0,0]; + +//adds extra info to the MouseEvent (coordinates in canvas axis, deltas and button state) +GL.augmentEvent = function(e, root_element) +{ + var offset_left = 0; + var offset_top = 0; + var b = null; + + root_element = root_element || e.target || gl.canvas; + b = root_element.getBoundingClientRect(); + + e.mousex = e.clientX - b.left; + e.mousey = e.clientY - b.top; + e.canvasx = e.mousex; + e.canvasy = b.height - e.mousey; + e.deltax = 0; + e.deltay = 0; + + if(e.type == "mousedown") + { + this.dragging = true; + //gl.mouse.buttons |= (1 << e.which); //enable + } + else if (e.type == "mousemove") + { + } + else if (e.type == "mouseup") + { + //gl.mouse.buttons = gl.mouse.buttons & ~(1 << e.which); + if(e.buttons == 0) + this.dragging = false; + } + + if( e.movementX !== undefined && !GL.isMobile() ) //pointer lock (mobile gives always zero) + { + //console.log( e.movementX ) + e.deltax = e.movementX; + e.deltay = e.movementY; + } + else + { + e.deltax = e.mousex - this.last_pos[0]; + e.deltay = e.mousey - this.last_pos[1]; + } + this.last_pos[0] = e.mousex; + this.last_pos[1] = e.mousey; + + //insert info in event + e.dragging = this.dragging; + e.leftButton = !!(gl.mouse.buttons & GL.LEFT_MOUSE_BUTTON_MASK); + e.middleButton = !!(gl.mouse.buttons & GL.MIDDLE_MOUSE_BUTTON_MASK); + e.rightButton = !!(gl.mouse.buttons & GL.RIGHT_MOUSE_BUTTON_MASK); + //shitty non-coherent API, e.buttons use 1:left,2:right,4:middle) but e.button uses (0:left,1:middle,2:right) + e.buttons_mask = 0; + if( e.leftButton ) e.buttons_mask = 1; + if( e.middleButton ) e.buttons_mask |= 2; + if( e.rightButton ) e.buttons_mask |= 4; + e.isButtonPressed = function(num) { return this.buttons_mask & (1<= 0; --j) //iterate backwards to avoid problems after removing + { + if( array[j][1] != target_instance || (callback && callback !== array[j][0]) ) + continue; + + array.splice(j,1);//remove + } + } + }, + + /** + * Unbinds all callbacks associated to one specific event from this instance + * @method LEvent.unbindAll + * @param {Object} instance where the events are binded + * @param {String} event name of the event you want to remove all binds + **/ + unbindAllEvent: function( instance, event_type ) + { + if(!instance) + throw("cannot unbind events in null"); + + var events = instance.__levents; + if(!events) + return; + delete events[ event_type ]; + if( instance.onLEventUnbindAll ) + instance.onLEventUnbindAll( event_type, target_instance, callback ); + return; + }, + + /** + * Tells if there is a binded callback that matches the criteria + * @method LEvent.isBind + * @param {Object} instance where the are the events binded + * @param {String} event_name string defining the event name + * @param {function} callback the callback + * @param {Object} target_instance [Optional] instance binded to callback + **/ + isBind: function( instance, event_type, callback, target_instance ) + { + if(!instance) + throw("LEvent cannot have null as instance"); + + var events = instance.__levents; + if( !events ) + return; + + if( !events.hasOwnProperty(event_type) ) + return false; + + for(var i = 0, l = events[event_type].length; i < l; ++i) + { + var v = events[event_type][i]; + if(v[0] === callback && v[1] === target_instance) + return true; + } + return false; + }, + + /** + * Tells if there is any callback binded to this event + * @method LEvent.hasBind + * @param {Object} instance where the are the events binded + * @param {String} event_name string defining the event name + * @return {boolean} true is there is at least one + **/ + hasBind: function( instance, event_type ) + { + if(!instance) + throw("LEvent cannot have null as instance"); + var events = instance.__levents; + if(!events || !events.hasOwnProperty( event_type ) || !events[event_type].length) + return false; + return true; + }, + + /** + * Tells if there is any callback binded to this object pointing to a method in the target object + * @method LEvent.hasBindTo + * @param {Object} instance where there are the events binded + * @param {Object} target instance to check to + * @return {boolean} true is there is at least one + **/ + hasBindTo: function( instance, target ) + { + if(!instance) + throw("LEvent cannot have null as instance"); + var events = instance.__levents; + + //no events binded + if(!events) + return false; + + for(var j in events) + { + var binds = events[j]; + for(var i = 0; i < binds.length; ++i) + { + if(binds[i][1] === target) //one found + return true; + } + } + + return false; + }, + + /** + * Triggers and event in an instance + * If the callback returns true then it will stop the propagation and return true + * @method LEvent.trigger + * @param {Object} instance that triggers the event + * @param {String} event_name string defining the event name + * @param {*} parameters that will be received by the binded function + * @param {bool} reverse_order trigger in reverse order (binded last get called first) + * @param {bool} expand_parameters parameters are passed not as one single parameter, but as many + * return {bool} true if the event passed was blocked by any binded callback + **/ + trigger: function( instance, event_type, params, reverse_order, expand_parameters ) + { + if(!instance) + throw("cannot trigger event from null"); + if(instance.constructor === String ) + throw("cannot bind event to a string"); + + var events = instance.__levents; + if( !events || !events.hasOwnProperty(event_type) ) + return false; + + var inst = events[event_type]; + if( reverse_order ) + { + for(var i = inst.length - 1; i >= 0; --i) + { + var v = inst[i]; + if(expand_parameters) + { + if( v && v[0].apply( v[1], params ) === true)// || event.stop) + return true; //stopPropagation + } + else + { + if( v && v[0].call( v[1], event_type, params) === true)// || event.stop) + return true; //stopPropagation + } + } + } + else + { + for(var i = 0, l = inst.length; i < l; ++i) + { + var v = inst[i]; + if( expand_parameters ) + { + if( v && v[0].apply( v[1], params ) === true)// || event.stop) + return true; //stopPropagation + } + else + { + if( v && v[0].call(v[1], event_type, params) === true)// || event.stop) + return true; //stopPropagation + } + } + } + + return false; + }, + + /** + * Triggers and event to every element in an array. + * If the event returns true, it must be intercepted + * @method LEvent.triggerArray + * @param {Array} array contains all instances to triggers the event + * @param {String} event_name string defining the event name + * @param {*} parameters that will be received by the binded function + * @param {bool} reverse_order trigger in reverse order (binded last get called first) + * @param {bool} expand_parameters parameters are passed not as one single parameter, but as many + * return {bool} false + **/ + triggerArray: function( instances, event_type, params, reverse_order, expand_parameters ) + { + var blocked = false; + for(var i = 0, l = instances.length; i < l; ++i) + { + var instance = instances[i]; + if(!instance) + throw("cannot trigger event from null"); + if(instance.constructor === String ) + throw("cannot bind event to a string"); + + var events = instance.__levents; + if( !events || !events.hasOwnProperty( event_type ) ) + continue; + + if( reverse_order ) + { + for(var j = events[event_type].length - 1; j >= 0; --j) + { + var v = events[event_type][j]; + if(expand_parameters) + { + if( v[0].apply(v[1], params ) === true)// || event.stop) + { + blocked = true; + break; //stopPropagation + } + } + else + { + if( v[0].call(v[1], event_type, params) === true)// || event.stop) + { + blocked = true; + break; //stopPropagation + } + } + } + } + else + { + for(var j = 0, ll = events[event_type].length; j < ll; ++j) + { + var v = events[event_type][j]; + if(expand_parameters) + { + if( v[0].apply(v[1], params ) === true)// || event.stop) + { + blocked = true; + break; //stopPropagation + } + } + else + { + if( v[0].call(v[1], event_type, params) === true)// || event.stop) + { + blocked = true; + break; //stopPropagation + } + } + } + } + } + + return blocked; + }, + + extendObject: function( object ) + { + object.bind = function( event_type, callback, instance ){ + return LEvent.bind( this, event_type, callback, instance ); + }; + + object.trigger = function( event_type, params ){ + return LEvent.trigger( this, event_type, params ); + }; + + object.unbind = function( event_type, callback, target_instance ) + { + return LEvent.unbind( this, event_type, callback, instance ); + }; + + object.unbindAll = function( target_instance, callback ) + { + return LEvent.unbindAll( this, target_instance, callback ); + }; + }, + + /** + * Adds the methods to bind, trigger and unbind to this class prototype + * @method LEvent.extendClass + * @param {Object} constructor + **/ + extendClass: function( constructor ) + { + this.extendObject( constructor.prototype ); + } +}; +/* geometric utilities */ +global.CLIP_INSIDE = GL.CLIP_INSIDE = 0; +global.CLIP_OUTSIDE = GL.CLIP_OUTSIDE = 1; +global.CLIP_OVERLAP = GL.CLIP_OVERLAP = 2; + +/** +* @namespace +*/ + + +/** +* Computational geometry algorithms, is a static class +* @class geo +*/ + +global.geo = { + + /** + * Returns a float4 containing the info about a plane with normal N and that passes through point P + * @method createPlane + * @param {vec3} P + * @param {vec3} N + * @return {vec4} plane values + */ + createPlane: function(P,N) + { + return new Float32Array([N[0],N[1],N[2],-vec3.dot(P,N)]); + }, + + /** + * Computes the distance between the point and the plane + * @method distancePointToPlane + * @param {vec3} point + * @param {vec4} plane + * @return {Number} distance + */ + distancePointToPlane: function(point, plane) + { + return (vec3.dot(point,plane) + plane[3])/Math.sqrt(plane[0]*plane[0] + plane[1]*plane[1] + plane[2]*plane[2]); + }, + + /** + * Computes the square distance between the point and the plane + * @method distance2PointToPlane + * @param {vec3} point + * @param {vec4} plane + * @return {Number} distance*distance + */ + distance2PointToPlane: function(point, plane) + { + return (vec3.dot(point,plane) + plane[3])/(plane[0]*plane[0] + plane[1]*plane[1] + plane[2]*plane[2]); + }, + + /** + * Projects a 3D point on a 3D line + * @method projectPointOnLine + * @param {vec3} P + * @param {vec3} A line start + * @param {vec3} B line end + * @param {vec3} result to store result (optional) + * @return {vec3} projectec point + */ + projectPointOnLine: function( P, A, B, result ) + { + result = result || vec3.create(); + //A + dot(AP,AB) / dot(AB,AB) * AB + var AP = vec3.fromValues( P[0] - A[0], P[1] - A[1], P[2] - A[2]); + var AB = vec3.fromValues( B[0] - A[0], B[1] - A[1], B[2] - A[2]); + var div = vec3.dot(AP,AB) / vec3.dot(AB,AB); + result[0] = A[0] + div[0] * AB[0]; + result[1] = A[1] + div[1] * AB[1]; + result[2] = A[2] + div[2] * AB[2]; + return result; + }, + + /** + * Projects a 2D point on a 2D line + * @method project2DPointOnLine + * @param {vec2} P + * @param {vec2} A line start + * @param {vec2} B line end + * @param {vec2} result to store result (optional) + * @return {vec2} projectec point + */ + project2DPointOnLine: function( P, A, B, result ) + { + result = result || vec2.create(); + //A + dot(AP,AB) / dot(AB,AB) * AB + var AP = vec2.fromValues(P[0] - A[0], P[1] - A[1]); + var AB = vec2.fromValues(B[0] - A[0], B[1] - A[1]); + var div = vec2.dot(AP,AB) / vec2.dot(AB,AB); + result[0] = A[0] + div[0] * AB[0]; + result[1] = A[1] + div[1] * AB[1]; + return result; + }, + + /** + * Projects point on plane + * @method projectPointOnPlane + * @param {vec3} point + * @param {vec3} P plane point + * @param {vec3} N plane normal + * @param {vec3} result to store result (optional) + * @return {vec3} projectec point + */ + projectPointOnPlane: function(point, P, N, result) + { + result = result || vec3.create(); + var v = vec3.subtract( vec3.create(), point, P ); + var dist = vec3.dot(v,N); + return vec3.subtract( result, point , vec3.scale( vec3.create(), N, dist ) ); + }, + + /** + * Finds the reflected point over a plane (useful for reflecting camera position when rendering reflections) + * @method reflectPointInPlane + * @param {vec3} point point to reflect + * @param {vec3} P point where the plane passes + * @param {vec3} N normal of the plane + * @return {vec3} reflected point + */ + reflectPointInPlane: function(point, P, N) + { + var d = -1 * (P[0] * N[0] + P[1] * N[1] + P[2] * N[2]); + var t = -(d + N[0]*point[0] + N[1]*point[1] + N[2]*point[2]) / (N[0]*N[0] + N[1]*N[1] + N[2]*N[2]); + //trace("T:" + t); + //var closest = [ point[0]+t*N[0], point[1]+t*N[1], point[2]+t*N[2] ]; + //trace("Closest:" + closest); + return vec3.fromValues( point[0]+t*N[0]*2, point[1]+t*N[1]*2, point[2]+t*N[2]*2 ); + }, + + /** + * test a ray plane collision and retrieves the collision point + * @method testRayPlane + * @param {vec3} start ray start + * @param {vec3} direction ray direction + * @param {vec3} P point where the plane passes + * @param {vec3} N normal of the plane + * @param {vec3} result collision position + * @return {boolean} returns if the ray collides the plane or the ray is parallel to the plane + */ + testRayPlane: function(start, direction, P, N, result) + { + var D = vec3.dot( P, N ); + var numer = D - vec3.dot(N, start); + var denom = vec3.dot(N, direction); + if( Math.abs(denom) < EPSILON) return false; + var t = (numer / denom); + if(t < 0.0) return false; //behind the ray + if(result) + vec3.add( result, start, vec3.scale( result, direction, t) ); + + return true; + }, + + /** + * test collision between segment and plane and retrieves the collision point + * @method testSegmentPlane + * @param {vec3} start segment start + * @param {vec3} end segment end + * @param {vec3} P point where the plane passes + * @param {vec3} N normal of the plane + * @param {vec3} result collision position + * @return {boolean} returns if the segment collides the plane or it is parallel to the plane + */ + testSegmentPlane: (function() { + var temp = vec3.create(); + return function(start, end, P, N, result) + { + var D = vec3.dot( P, N ); + var numer = D - vec3.dot(N, start); + var direction = vec3.sub( temp, end, start ); + var denom = vec3.dot(N, direction); + if( Math.abs(denom) < EPSILON) + return false; //parallel + var t = (numer / denom); + if(t < 0.0) + return false; //behind the start + if(t > 1.0) + return false; //after the end + if(result) + vec3.add( result, start, vec3.scale( result, direction, t) ); + return true; + }; + })(), + + /** + * test a ray sphere collision and retrieves the collision point + * @method testRaySphere + * @param {vec3} start ray start + * @param {vec3} direction ray direction (normalized) + * @param {vec3} center center of the sphere + * @param {number} radius radius of the sphere + * @param {vec3} result [optional] collision position + * @param {number} max_dist not fully tested + * @return {boolean} returns if the ray collides the sphere + */ + testRaySphere: (function() { + var temp = vec3.create(); + return function(start, direction, center, radius, result, max_dist) + { + // sphere equation (centered at origin) x2+y2+z2=r2 + // ray equation x(t) = p0 + t*dir + // substitute x(t) into sphere equation + // solution below: + + // transform ray origin into sphere local coordinates + var orig = vec3.subtract( temp , start, center); + + var a = direction[0]*direction[0] + direction[1]*direction[1] + direction[2]*direction[2]; + var b = 2*orig[0]*direction[0] + 2*orig[1]*direction[1] + 2*orig[2]*direction[2]; + var c = orig[0]*orig[0] + orig[1]*orig[1] + orig[2]*orig[2] - radius*radius; + //return quadraticFormula(a,b,c,t0,t1) ? 2 : 0; + + var q = b*b - 4*a*c; + if( q < 0.0 ) + return false; + + if(result) + { + var sq = Math.sqrt(q); + var d = 1 / (2*a); + var r1 = ( -b + sq ) * d; + var r2 = ( -b - sq ) * d; + var t = r1 < r2 ? r1 : r2; + if(max_dist !== undefined && t > max_dist) + return false; + vec3.add(result, start, vec3.scale( result, direction, t ) ); + } + return true;//real roots + }; + })(), + + /** + * test a ray cylinder collision (only vertical cylinders) and retrieves the collision point [not fully tested] + * @method testRayCylinder + * @param {vec3} start ray start + * @param {vec3} direction ray direction + * @param {vec3} p center of the cylinder + * @param {number} q height of the cylinder + * @param {number} r radius of the cylinder + * @param {vec3} result collision position + * @return {boolean} returns if the ray collides the cylinder + */ + testRayCylinder: function(start, direction, p, q, r, result) + { + var sa = vec3.clone(start); + var sb = vec3.add(vec3.create(), start, vec3.scale( vec3.create(), direction, 100000) ); + var t = 0; + var d = vec3.subtract(vec3.create(),q,p); + var m = vec3.subtract(vec3.create(),sa,p); + var n = vec3.subtract(vec3.create(),sb,sa); + //var n = vec3.create(direction); + + var md = vec3.dot(m, d); + var nd = vec3.dot(n, d); + var dd = vec3.dot(d, d); + + // Test if segment fully outside either endcap of cylinder + if (md < 0.0 && md + nd < 0.0) return false; // Segment outside ’p’ side of cylinder + if (md > dd && md + nd > dd) return false; // Segment outside ’q’ side of cylinder + + var nn = vec3.dot(n, n); + var mn = vec3.dot(m, n); + var a = dd * nn - nd * nd; + var k = vec3.dot(m,m) - r*r; + var c = dd * k - md * md; + + if (Math.abs(a) < EPSILON) + { + // Segment runs parallel to cylinder axis + if (c > 0.0) return false; + // ’a’ and thus the segment lie outside cylinder + // Now known that segment intersects cylinder; figure out how it intersects + if (md < 0.0) t = -mn/nn; + // Intersect segment against ’p’ endcap + else if (md > dd) + t=(nd-mn)/nn; + // Intersect segment against ’q’ endcap + else t = 0.0; + // ’a’ lies inside cylinder + if(result) + vec3.add(result, sa, vec3.scale(result, n,t) ); + return true; + } + var b = dd * mn - nd * md; + var discr = b*b - a*c; + if (discr < 0.0) + return false; + // No real roots; no intersection + t = (-b - Math.sqrt(discr)) / a; + if (t < 0.0 || t > 1.0) + return false; + // Intersection lies outside segment + if(md+t*nd < 0.0) + { + // Intersection outside cylinder on ’p’ side + if (nd <= 0.0) + return false; + // Segment pointing away from endcap + t = -md / nd; + // Keep intersection if Dot(S(t) - p, S(t) - p) <= r^2 + if(result) + vec3.add(result, sa, vec3.scale(result, n,t) ); + return k+2*t*(mn+t*nn) <= 0.0; + } else if (md+t*nd>dd) + { + // Intersection outside cylinder on ’q’ side + if (nd >= 0.0) return false; //Segment pointing away from endcap + t = (dd - md) / nd; + // Keep intersection if Dot(S(t) - q, S(t) - q) <= r^2 + if(result) + vec3.add(result, sa, vec3.scale(result, n,t) ); + return k+dd - 2*md+t*(2*(mn - nd)+t*nn) <= 0.0; + } + // Segment intersects cylinder between the endcaps; t is correct + if(result) + vec3.add(result, sa, vec3.scale(result, n,t) ); + return true; + }, + + + /** + * test a ray bounding-box collision and retrieves the collision point, the BB must be Axis Aligned + * @method testRayBox + * @param {vec3} start ray start + * @param {vec3} direction ray direction + * @param {vec3} minB minimum position of the bounding box + * @param {vec3} maxB maximim position of the bounding box + * @param {vec3} result collision position + * @return {boolean} returns if the ray collides the box + */ + testRayBox: (function() { + + var quadrant = new Float32Array(3); + var candidatePlane = new Float32Array(3); + var maxT = new Float32Array(3); + + return function(start, direction, minB, maxB, result, max_dist) + { + //#define NUMDIM 3 + //#define RIGHT 0 + //#define LEFT 1 + //#define MIDDLE 2 + + max_dist = max_dist || Number.MAX_VALUE; + + var inside = true; + var i = 0|0; + var whichPlane; + + quadrant.fill(0); + maxT.fill(0); + candidatePlane.fill(0); + + /* Find candidate planes; this loop can be avoided if + rays cast all from the eye(assume perpsective view) */ + for (i=0; i < 3; ++i) + if(start[i] < minB[i]) { + quadrant[i] = 1; + candidatePlane[i] = minB[i]; + inside = false; + }else if (start[i] > maxB[i]) { + quadrant[i] = 0; + candidatePlane[i] = maxB[i]; + inside = false; + }else { + quadrant[i] = 2; + } + + /* Ray origin inside bounding box */ + if(inside) { + if(result) + vec3.copy(result, start); + return true; + } + + + /* Calculate T distances to candidate planes */ + for (i = 0; i < 3; ++i) + if (quadrant[i] != 2 && direction[i] != 0.) + maxT[i] = (candidatePlane[i] - start[i]) / direction[i]; + else + maxT[i] = -1.; + + /* Get largest of the maxT's for final choice of intersection */ + whichPlane = 0; + for (i = 1; i < 3; i++) + if (maxT[whichPlane] < maxT[i]) + whichPlane = i; + + /* Check final candidate actually inside box */ + if (maxT[whichPlane] < 0.) return false; + if (maxT[whichPlane] > max_dist) return false; //NOT TESTED + + for (i = 0; i < 3; ++i) + if (whichPlane != i) { + var res = start[i] + maxT[whichPlane] * direction[i]; + if (res < minB[i] || res > maxB[i]) + return false; + if(result) + result[i] = res; + } else { + if(result) + result[i] = candidatePlane[i]; + } + return true; /* ray hits box */ + } + })(), + + /** + * test a ray bounding-box collision, it uses the BBox class and allows to use non-axis aligned bbox + * @method testRayBBox + * @param {vec3} origin ray origin + * @param {vec3} direction ray direction + * @param {BBox} box in BBox format + * @param {mat4} model transformation of the BBox [optional] + * @param {vec3} result collision position in world space unless in_local is true + * @return {boolean} returns if the ray collides the box + */ + testRayBBox: (function(){ + var inv = mat4.create(); + var end = vec3.create(); + var origin2 = vec3.create(); + return function( origin, direction, box, model, result, max_dist, in_local ) + { + if(!origin || !direction || !box) + throw("parameters missing"); + if(model) + { + mat4.invert( inv, model ); + vec3.add( end, origin, direction ); + origin = vec3.transformMat4( origin2, origin, inv); + vec3.transformMat4( end, end, inv ); + vec3.sub( end, end, origin ); + direction = vec3.normalize( end, end ); + } + var r = this.testRayBox( origin, direction, box.subarray(6,9), box.subarray(9,12), result, max_dist ); + if(!in_local && model && result) + vec3.transformMat4(result, result, model); + return r; + } + })(), + + /** + * test if a 3d point is inside a BBox + * @method testPointBBox + * @param {vec3} point + * @param {BBox} bbox + * @return {boolean} true if it is inside + */ + testPointBBox: function(point, bbox) { + if(point[0] < bbox[6] || point[0] > bbox[9] || + point[1] < bbox[7] || point[0] > bbox[10] || + point[2] < bbox[8] || point[0] > bbox[11]) + return false; + return true; + }, + + /** + * test if a BBox overlaps another BBox + * @method testBBoxBBox + * @param {BBox} a + * @param {BBox} b + * @return {boolean} true if it overlaps + */ + testBBoxBBox: function(a, b) + { + var tx = Math.abs( b[0] - a[0]); + if (tx > (a[3] + b[3])) + return false; //outside + var ty = Math.abs(b[1] - a[1]); + if (ty > (a[4] + b[4])) + return false; //outside + var tz = Math.abs( b[2] - a[2]); + if (tz > (a[5] + b[5]) ) + return false; //outside + + var vmin = BBox.getMin(b); + if ( geo.testPointBBox(vmin, a) ) + { + var vmax = BBox.getMax(b); + if (geo.testPointBBox(vmax, a)) + { + return true;// INSIDE;// this instance contains b + } + } + + return true; //OVERLAP; // this instance overlaps with b + }, + + /** + * test if a sphere overlaps a BBox + * @method testSphereBBox + * @param {vec3} point + * @param {float} radius + * @param {BBox} bounding_box + * @return {boolean} true if it overlaps + */ + testSphereBBox: function(center, radius, bbox) + { + // arvo's algorithm from gamasutra + // http://www.gamasutra.com/features/19991018/Gomez_4.htm + + var s, d = 0.0; + //find the square of the distance + //from the sphere to the box + var vmin = BBox.getMin( bbox ); + var vmax = BBox.getMax( bbox ); + for(var i = 0; i < 3; ++i) + { + if( center[i] < vmin[i] ) + { + s = center[i] - vmin[i]; + d += s*s; + } + else if( center[i] > vmax[i] ) + { + s = center[i] - vmax[i]; + d += s*s; + } + } + //return d <= r*r + + var radiusSquared = radius * radius; + if (d <= radiusSquared) + { + return true; + /* + // this is used just to know if it overlaps or is just inside, but I dont care + // make an aabb aabb test with the sphere aabb to test inside state + var halfsize = vec3.fromValues( radius, radius, radius ); + var sphere_bbox = BBox.fromCenterHalfsize( center, halfsize ); + if ( geo.testBBoxBBox(bbox, sphere_bbox) ) + return INSIDE; + return OVERLAP; + */ + } + + return false; //OUTSIDE; + }, + + closestPointBetweenLines: function(a0,a1, b0,b1, p_a, p_b) + { + var u = vec3.subtract( vec3.create(), a1, a0 ); + var v = vec3.subtract( vec3.create(), b1, b0 ); + var w = vec3.subtract( vec3.create(), a0, b0 ); + + var a = vec3.dot(u,u); // always >= 0 + var b = vec3.dot(u,v); + var c = vec3.dot(v,v); // always >= 0 + var d = vec3.dot(u,w); + var e = vec3.dot(v,w); + var D = a*c - b*b; // always >= 0 + var sc, tc; + + // compute the line parameters of the two closest points + if (D < EPSILON) { // the lines are almost parallel + sc = 0.0; + tc = (b>c ? d/b : e/c); // use the largest denominator + } + else { + sc = (b*e - c*d) / D; + tc = (a*e - b*d) / D; + } + + // get the difference of the two closest points + if(p_a) vec3.add(p_a, a0, vec3.scale(vec3.create(),u,sc)); + if(p_b) vec3.add(p_b, b0, vec3.scale(vec3.create(),v,tc)); + + var dP = vec3.add( vec3.create(), w, vec3.subtract( vec3.create(), vec3.scale(vec3.create(),u,sc) , vec3.scale(vec3.create(),v,tc)) ); // = L1(sc) - L2(tc) + return vec3.length(dP); // return the closest distance + }, + + /** + * extract frustum planes given a view-projection matrix + * @method extractPlanes + * @param {mat4} viewprojection matrix + * @return {Float32Array} returns all 6 planes in a float32array[24] + */ + extractPlanes: function(vp, planes) + { + var planes = planes || new Float32Array(4*6); + + //right + planes.set( [vp[3] - vp[0], vp[7] - vp[4], vp[11] - vp[8], vp[15] - vp[12] ], 0); + normalize(0); + + //left + planes.set( [vp[3] + vp[0], vp[ 7] + vp[ 4], vp[11] + vp[ 8], vp[15] + vp[12] ], 4); + normalize(4); + + //bottom + planes.set( [ vp[ 3] + vp[ 1], vp[ 7] + vp[ 5], vp[11] + vp[ 9], vp[15] + vp[13] ], 8); + normalize(8); + + //top + planes.set( [ vp[ 3] - vp[ 1], vp[ 7] - vp[ 5], vp[11] - vp[ 9], vp[15] - vp[13] ],12); + normalize(12); + + //back + planes.set( [ vp[ 3] - vp[ 2], vp[ 7] - vp[ 6], vp[11] - vp[10], vp[15] - vp[14] ],16); + normalize(16); + + //front + planes.set( [ vp[ 3] + vp[ 2], vp[ 7] + vp[ 6], vp[11] + vp[10], vp[15] + vp[14] ],20); + normalize(20); + + return planes; + + function normalize(pos) + { + var N = planes.subarray(pos,pos+3); + var l = vec3.length(N); + if(l === 0) return; + l = 1.0 / l; + planes[pos] *= l; + planes[pos+1] *= l; + planes[pos+2] *= l; + planes[pos+3] *= l; + } + }, + + /** + * test a BBox against the frustum + * @method frustumTestBox + * @param {Float32Array} planes frustum planes + * @param {BBox} boundindbox in BBox format + * @return {enum} CLIP_INSIDE, CLIP_OVERLAP, CLIP_OUTSIDE + */ + frustumTestBox: function(planes, box) + { + var flag = 0, o = 0; + + flag = planeBoxOverlap(planes.subarray(0,4),box); + if (flag == CLIP_OUTSIDE) return CLIP_OUTSIDE; o+= flag; + flag = planeBoxOverlap(planes.subarray(4,8),box); + if (flag == CLIP_OUTSIDE) return CLIP_OUTSIDE; o+= flag; + flag = planeBoxOverlap(planes.subarray(8,12),box); + if (flag == CLIP_OUTSIDE) return CLIP_OUTSIDE; o+= flag; + flag = planeBoxOverlap(planes.subarray(12,16),box); + if (flag == CLIP_OUTSIDE) return CLIP_OUTSIDE; o+= flag; + flag = planeBoxOverlap(planes.subarray(16,20),box); + if (flag == CLIP_OUTSIDE) return CLIP_OUTSIDE; o+= flag; + flag = planeBoxOverlap(planes.subarray(20,24),box); + if (flag == CLIP_OUTSIDE) return CLIP_OUTSIDE; o+= flag; + + return o == 0 ? CLIP_INSIDE : CLIP_OVERLAP; + }, + + /** + * test a Sphere against the frustum + * @method frustumTestSphere + * @param {vec3} center sphere center + * @param {number} radius sphere radius + * @return {enum} CLIP_INSIDE, CLIP_OVERLAP, CLIP_OUTSIDE + */ + + frustumTestSphere: function(planes, center, radius) + { + var dist; + var overlap = false; + + dist = distanceToPlane( planes.subarray(0,4), center ); + if( dist < -radius ) return CLIP_OUTSIDE; + else if(dist >= -radius && dist <= radius) overlap = true; + dist = distanceToPlane( planes.subarray(4,8), center ); + if( dist < -radius ) return CLIP_OUTSIDE; + else if(dist >= -radius && dist <= radius) overlap = true; + dist = distanceToPlane( planes.subarray(8,12), center ); + if( dist < -radius ) return CLIP_OUTSIDE; + else if(dist >= -radius && dist <= radius) overlap = true; + dist = distanceToPlane( planes.subarray(12,16), center ); + if( dist < -radius ) return CLIP_OUTSIDE; + else if(dist >= -radius && dist <= radius) overlap = true; + dist = distanceToPlane( planes.subarray(16,20), center ); + if( dist < -radius ) return CLIP_OUTSIDE; + else if(dist >= -radius && dist <= radius) overlap = true; + dist = distanceToPlane( planes.subarray(20,24), center ); + if( dist < -radius ) return CLIP_OUTSIDE; + else if(dist >= -radius && dist <= radius) overlap = true; + return overlap ? CLIP_OVERLAP : CLIP_INSIDE; + }, + + + /** + * test if a 2d point is inside a 2d polygon + * @method testPoint2DInPolygon + * @param {Array} poly array of 2d points + * @param {vec2} point + * @return {boolean} true if it is inside + */ + testPoint2DInPolygon: function(poly, pt) { + for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i) + ((poly[i][1] <= pt[1] && pt[1] < poly[j][1]) || (poly[j][1] <= pt[1] && pt[1] < poly[i][1])) + && (pt[0] < (poly[j][0] - poly[i][0]) * (pt[1] - poly[i][1]) / (poly[j][1] - poly[i][1]) + poly[i][0]) + && (c = !c); + return c; + } +}; + +/** +* BBox is a class to create BoundingBoxes but it works as glMatrix, creating Float32Array with the info inside instead of objects +* The bounding box is stored as center,halfsize,min,max,radius (total of 13 floats) +* @class BBox +*/ +global.BBox = GL.BBox = { + center:0, + halfsize:3, + min:6, + max:9, + radius:12, + data_length: 13, + + //corners: new Float32Array([1,1,1, 1,1,-1, 1,-1,1, 1,-1,-1, -1,1,1, -1,1,-1, -1,-1,1, -1,-1,-1 ]), + corners: [ vec3.fromValues(1,1,1), vec3.fromValues(1,1,-1), vec3.fromValues(1,-1,1), vec3.fromValues(1,-1,-1), vec3.fromValues(-1,1,1), vec3.fromValues(-1,1,-1), vec3.fromValues(-1,-1,1), vec3.fromValues(-1,-1,-1) ] , + + /** + * create an empty bbox + * @method create + * @return {BBox} returns a float32array with the bbox + */ + create: function() + { + return new Float32Array(13); + }, + + /** + * create an bbox copy from another one + * @method clone + * @return {BBox} returns a float32array with the bbox + */ + clone: function(bb) + { + return new Float32Array(bb); + }, + + /** + * copy one bbox into another + * @method copy + * @param {BBox} out where to store the result + * @param {BBox} where to read the bbox + * @return {BBox} returns out + */ + copy: function(out,bb) + { + out.set(bb); + return out; + }, + + /** + * create a bbox from one point + * @method fromPoint + * @param {vec3} point + * @return {BBox} returns a float32array with the bbox + */ + fromPoint: function(point) + { + var bb = this.create(); + bb.set(point, 0); //center + bb.set(point, 6); //min + bb.set(point, 9); //max + return bb; + }, + + /** + * create a bbox from min and max points + * @method fromMinMax + * @param {vec3} min + * @param {vec3} max + * @return {BBox} returns a float32array with the bbox + */ + fromMinMax: function(min,max) + { + var bb = this.create(); + this.setMinMax(bb, min, max); + return bb; + }, + + /** + * create a bbox from center and halfsize + * @method fromCenterHalfsize + * @param {vec3} center + * @param {vec3} halfsize + * @return {BBox} returns a float32array with the bbox + */ + fromCenterHalfsize: function(center, halfsize) + { + var bb = this.create(); + this.setCenterHalfsize(bb, center, halfsize); + return bb; + }, + + /** + * create a bbox from a typed-array containing points + * @method fromPoints + * @param {Float32Array} points + * @return {BBox} returns a float32array with the bbox + */ + fromPoints: function(points) + { + var bb = this.create(); + this.setFromPoints(bb, points); + return bb; + }, + + /** + * set the values to a BB from a set of points + * @method setFromPoints + * @param {BBox} out where to store the result + * @param {Float32Array} points + * @return {BBox} returns a float32array with the bbox + */ + setFromPoints: function(bb, points) + { + var min = bb.subarray(6,9); + var max = bb.subarray(9,12); + + min[0] = points[0]; //min.set( points.subarray(0,3) ); + min[1] = points[1]; + min[2] = points[2]; + max.set( min ); + + var v = 0; + for(var i = 3, l = points.length; i < l; i+=3) + { + var x = points[i]; + var y = points[i+1]; + var z = points[i+2]; + if( x < min[0] ) min[0] = x; + else if( x > max[0] ) max[0] = x; + if( y < min[1] ) min[1] = y; + else if( y > max[1] ) max[1] = y; + if( z < min[2] ) min[2] = z; + else if( z > max[2] ) max[2] = z; + /* + v = points.subarray(i,i+3); + vec3.min( min, v, min); + vec3.max( max, v, max); + */ + } + + //center + bb[0] = (min[0] + max[0]) * 0.5; + bb[1] = (min[1] + max[1]) * 0.5; + bb[2] = (min[2] + max[2]) * 0.5; + //halfsize + bb[3] = max[0] - bb[0]; + bb[4] = max[1] - bb[1]; + bb[5] = max[2] - bb[2]; + bb[12] = Math.sqrt( bb[3]*bb[3] + bb[4]*bb[4] + bb[5]*bb[5] ); + + /* + var center = vec3.add( bb.subarray(0,3), min, max ); + vec3.scale( center, center, 0.5); + vec3.subtract( bb.subarray(3,6), max, center ); + bb[12] = vec3.length(bb.subarray(3,6)); //radius + */ + return bb; + }, + + /** + * set the values to a BB from min and max + * @method setMinMax + * @param {BBox} out where to store the result + * @param {vec3} min + * @param {vec3} max + * @return {BBox} returns out + */ + setMinMax: function(bb, min, max) + { + bb[6] = min[0]; + bb[7] = min[1]; + bb[8] = min[2]; + bb[9] = max[0]; + bb[10] = max[1]; + bb[11] = max[2]; + + //halfsize + var halfsize = bb.subarray(3,6); + vec3.sub( halfsize, max, min ); //range + vec3.scale( halfsize, halfsize, 0.5 ); + + //center + bb[0] = max[0] - halfsize[0]; + bb[1] = max[1] - halfsize[1]; + bb[2] = max[2] - halfsize[2]; + + bb[12] = vec3.length(bb.subarray(3,6)); //radius + return bb; + }, + + /** + * set the values to a BB from center and halfsize + * @method setCenterHalfsize + * @param {BBox} out where to store the result + * @param {vec3} min + * @param {vec3} max + * @param {number} radius [optional] (the minimum distance from the center to the further point) + * @return {BBox} returns out + */ + setCenterHalfsize: function(bb, center, halfsize, radius) + { + bb[0] = center[0]; + bb[1] = center[1]; + bb[2] = center[2]; + bb[3] = halfsize[0]; + bb[4] = halfsize[1]; + bb[5] = halfsize[2]; + bb[6] = bb[0] - bb[3]; + bb[7] = bb[1] - bb[4]; + bb[8] = bb[2] - bb[5]; + bb[9] = bb[0] + bb[3]; + bb[10] = bb[1] + bb[4]; + bb[11] = bb[2] + bb[5]; + if(radius) + bb[12] = radius; + else + bb[12] = vec3.length(halfsize); + return bb; + }, + + /** + * Apply a matrix transformation to the BBox (applies to every corner and recomputes the BB) + * @method transformMat4 + * @param {BBox} out where to store the result + * @param {BBox} bb bbox you want to transform + * @param {mat4} mat transformation + * @return {BBox} returns out + */ + transformMat4: (function(){ + var hsx = 0; + var hsy = 0; + var hsz = 0; + var points_buffer = new Float32Array(8*3); + var points = []; + for(var i = 0; i < 24; i += 3 ) + points.push( points_buffer.subarray( i, i+3 ) ); + + return function( out, bb, mat ) + { + var centerx = bb[0]; + var centery = bb[1]; + var centerz = bb[2]; + hsx = bb[3]; + hsy = bb[4]; + hsz = bb[5]; + + var corners = this.corners; + + for(var i = 0; i < 8; ++i) + { + var corner = corners[i]; + var result = points[i]; + result[0] = hsx * corner[0] + centerx; + result[1] = hsy * corner[1] + centery; + result[2] = hsz * corner[2] + centerz; + mat4.multiplyVec3( result, mat, result ); + } + + return this.setFromPoints( out, points_buffer ); + } + })(), + + + /** + * Computes the eight corners of the BBox and returns it + * @method getCorners + * @param {BBox} bb the bounding box + * @param {Float32Array} result optional, should be 8 * 3 + * @return {Float32Array} returns the 8 corners + */ + getCorners: function( bb, result ) + { + var center = bb; //.subarray(0,3); AVOID GC + var halfsize = bb.subarray(3,6); + + var corners = null; + if(result) + { + result.set(this.corners); + corners = result; + } + else + corners = new Float32Array( this.corners ); + + for(var i = 0; i < 8; ++i) + { + var corner = corners.subarray(i*3, i*3+3); + vec3.multiply( corner, halfsize, corner ); + vec3.add( corner, corner, center ); + } + + return corners; + }, + + merge: function( out, a, b ) + { + var min = out.subarray(6,9); + var max = out.subarray(9,12); + vec3.min( min, a.subarray(6,9), b.subarray(6,9) ); + vec3.max( max, a.subarray(9,12), b.subarray(9,12) ); + return BBox.setMinMax( out, min, max ); + }, + + extendToPoint: function( out, p ) + { + if( p[0] < out[6] ) out[6] = p[0]; + else if( p[0] > out[9] ) out[9] = p[0]; + + if( p[1] < out[7] ) out[7] = p[1]; + else if( p[1] > out[10] ) out[10] = p[1]; + + + if( p[2] < out[8] ) out[8] = p[2]; + else if( p[2] > out[11] ) out[11] = p[2]; + + //recompute + var min = out.subarray(6,9); + var max = out.subarray(9,12); + var center = vec3.add( out.subarray(0,3), min, max ); + vec3.scale( center, center, 0.5); + vec3.subtract( out.subarray(3,6), max, center ); + out[12] = vec3.length( out.subarray(3,6) ); //radius + return out; + }, + + clampPoint: function(out, box, point) + { + out[0] = Math.clamp( point[0], box[0] - box[3], box[0] + box[3]); + out[1] = Math.clamp( point[1], box[1] - box[4], box[1] + box[4]); + out[2] = Math.clamp( point[2], box[2] - box[5], box[2] + box[5]); + }, + + isPointInside: function( bbox, point ) + { + if( (bbox[0] - bbox[3]) > point[0] || + (bbox[1] - bbox[4]) > point[1] || + (bbox[2] - bbox[5]) > point[2] || + (bbox[0] + bbox[3]) < point[0] || + (bbox[1] + bbox[4]) < point[1] || + (bbox[2] + bbox[5]) < point[2] ) + return false; + return true; + }, + + getCenter: function(bb) { return bb.subarray(0,3); }, + getHalfsize: function(bb) { return bb.subarray(3,6); }, + getMin: function(bb) { return bb.subarray(6,9); }, + getMax: function(bb) { return bb.subarray(9,12); }, + getRadius: function(bb) { return bb[12]; } + //setCenter,setHalfsize not coded, too much work to update all +} + +global.distanceToPlane = GL.distanceToPlane = function distanceToPlane(plane, point) +{ + return vec3.dot(plane,point) + plane[3]; +} + +global.planeBoxOverlap = GL.planeBoxOverlap = function planeBoxOverlap(plane, box) +{ + var n = plane; //.subarray(0,3); + var d = plane[3]; + //hack, to avoif GC I use indices directly + var center = box; //.subarray(0,3); + var halfsize = box; //.subarray(3,6); + + var radius = Math.abs( halfsize[3] * n[0] ) + Math.abs( halfsize[4] * n[1] ) + Math.abs( halfsize[5] * n[2] ); + var distance = vec3.dot(n,center) + d; + + if (distance <= -radius) + return CLIP_OUTSIDE; + else if (distance <= radius) + return CLIP_OVERLAP; + return CLIP_INSIDE; +} + +/** +* @namespace GL +*/ + +/** +* Octree generator for fast ray triangle collision with meshes +* Dependencies: glmatrix.js (for vector and matrix operations) +* @class Octree +* @constructor +* @param {Mesh} mesh object containing vertices buffer (indices buffer optional) +*/ + +global.Octree = GL.Octree = function Octree( mesh ) +{ + this.root = null; + this.total_depth = 0; + this.total_nodes = 0; + if(mesh) + { + this.buildFromMesh(mesh); + this.total_nodes = this.trim(); + } +} + +Octree.MAX_NODE_TRIANGLES_RATIO = 0.1; +Octree.MAX_OCTREE_DEPTH = 8; +Octree.OCTREE_MARGIN_RATIO = 0.01; +Octree.OCTREE_MIN_MARGIN = 0.1; + +var octree_tested_boxes = 0; +var octree_tested_triangles = 0; + +Octree.prototype.buildFromMesh = function( mesh ) +{ + this.total_depth = 0; + this.total_nodes = 0; + + var vertices = mesh.getBuffer("vertices").data; + var triangles = mesh.getIndexBuffer("triangles"); + if(triangles) + triangles = triangles.data; //get the internal data + + var root = this.computeAABB(vertices); + this.root = root; + this.total_nodes = 1; + this.total_triangles = triangles ? triangles.length / 3 : vertices.length / 9; + this.max_node_triangles = this.total_triangles * Octree.MAX_NODE_TRIANGLES_RATIO; + + var margin = vec3.create(); + vec3.scale( margin, root.size, Octree.OCTREE_MARGIN_RATIO ); + if(margin[0] < Octree.OCTREE_MIN_MARGIN) margin[0] = Octree.OCTREE_MIN_MARGIN; + if(margin[1] < Octree.OCTREE_MIN_MARGIN) margin[1] = Octree.OCTREE_MIN_MARGIN; + if(margin[2] < Octree.OCTREE_MIN_MARGIN) margin[2] = Octree.OCTREE_MIN_MARGIN; + + vec3.sub(root.min, root.min, margin); + vec3.add(root.max, root.max, margin); + + root.faces = []; + root.inside = 0; + + + //indexed + if(triangles) + { + for(var i = 0; i < triangles.length; i+=3) + { + var face = new Float32Array([vertices[triangles[i]*3], vertices[triangles[i]*3+1],vertices[triangles[i]*3+2], + vertices[triangles[i+1]*3], vertices[triangles[i+1]*3+1],vertices[triangles[i+1]*3+2], + vertices[triangles[i+2]*3], vertices[triangles[i+2]*3+1],vertices[triangles[i+2]*3+2],i/3]); + this.addToNode( face,root,0); + } + } + else + { + for(var i = 0; i < vertices.length; i+=9) + { + var face = new Float32Array( 10 ); + face.set( vertices.subarray(i,i+9) ); + face[9] = i/9; + this.addToNode(face,root,0); + } + } + + return root; +} + +Octree.prototype.addToNode = function( face, node, depth ) +{ + node.inside += 1; + + //has children + if(node.c) + { + var aabb = this.computeAABB(face); + var added = false; + for(var i in node.c) + { + var child = node.c[i]; + if (Octree.isInsideAABB(aabb,child)) + { + this.addToNode(face,child, depth+1); + added = true; + break; + } + } + if(!added) + { + if(node.faces == null) + node.faces = []; + node.faces.push(face); + } + } + else //add till full, then split + { + if(node.faces == null) + node.faces = []; + node.faces.push(face); + + //split + if(node.faces.length > this.max_node_triangles && depth < Octree.MAX_OCTREE_DEPTH) + { + this.splitNode(node); + if(this.total_depth < depth + 1) + this.total_depth = depth + 1; + + var faces = node.faces.concat(); + node.faces = null; + + //redistribute all nodes + for(var i in faces) + { + var face = faces[i]; + var aabb = this.computeAABB(face); + var added = false; + for(var j in node.c) + { + var child = node.c[j]; + if (Octree.isInsideAABB(aabb,child)) + { + this.addToNode(face,child, depth+1); + added = true; + break; + } + } + if (!added) + { + if(node.faces == null) + node.faces = []; + node.faces.push(face); + } + } + } + } +}; + +Octree.prototype.octree_pos_ref = [[0,0,0],[0,0,1],[0,1,0],[0,1,1],[1,0,0],[1,0,1],[1,1,0],[1,1,1]]; + +Octree.prototype.splitNode = function(node) +{ + node.c = []; + var half = [(node.max[0] - node.min[0]) * 0.5, (node.max[1] - node.min[1]) * 0.5, (node.max[2] - node.min[2]) * 0.5]; + + for(var i in this.octree_pos_ref) + { + var ref = this.octree_pos_ref[i]; + + var newnode = {}; + this.total_nodes += 1; + + newnode.min = [ node.min[0] + half[0] * ref[0], node.min[1] + half[1] * ref[1], node.min[2] + half[2] * ref[2]]; + newnode.max = [newnode.min[0] + half[0], newnode.min[1] + half[1], newnode.min[2] + half[2]]; + newnode.faces = null; + newnode.inside = 0; + node.c.push(newnode); + } +} + +Octree.prototype.computeAABB = function(vertices) +{ + var min = new Float32Array([ vertices[0], vertices[1], vertices[2] ]); + var max = new Float32Array([ vertices[0], vertices[1], vertices[2] ]); + + for(var i = 0; i < vertices.length; i+=3) + { + for(var j = 0; j < 3; j++) + { + if(min[j] > vertices[i+j]) + min[j] = vertices[i+j]; + if(max[j] < vertices[i+j]) + max[j] = vertices[i+j]; + } + } + + return {min: min, max: max, size: vec3.sub( vec3.create(), max, min) }; +} + +//remove empty nodes +Octree.prototype.trim = function(node) +{ + node = node || this.root; + if(!node.c) + return 1; + + var num = 1; + var valid = []; + var c = node.c; + for(var i = 0; i < c.length; ++i) + { + if(c[i].inside) + { + valid.push(c[i]); + num += this.trim(c[i]); + } + } + node.c = valid; + return num; +} + +/** +* Test collision between ray and triangles in the octree +* @method testRay +* @param {vec3} origin ray origin position +* @param {vec3} direction ray direction position +* @param {number} dist_min +* @param {number} dist_max +* @return {HitTest} object containing pos and normal +*/ +Octree.prototype.testRay = (function(){ + var origin_temp = vec3.create(); + var direction_temp = vec3.create(); + var min_temp = vec3.create(); + var max_temp = vec3.create(); + + return function(origin, direction, dist_min, dist_max, test_backfaces ) + { + octree_tested_boxes = 0; + octree_tested_triangles = 0; + + if(!this.root) + { + throw("Error: octree not build"); + } + + origin_temp.set( origin ); + direction_temp.set( direction ); + min_temp.set( this.root.min ); + max_temp.set( this.root.max ); + + var test = Octree.hitTestBox( origin_temp, direction_temp, min_temp, max_temp ); + if(!test) //no collision with mesh bounding box + return null; + + var test = Octree.testRayInNode( this.root, origin_temp, direction_temp, test_backfaces ); + if(test != null) + { + var pos = vec3.scale( vec3.create(), direction, test.t ); + vec3.add( pos, pos, origin ); + test.pos = pos; + return test; + } + + return null; + } +})(); + +/** +* test collision between sphere and the triangles in the octree (only test if there is any vertex inside the sphere) +* @method testSphere +* @param {vec3} origin sphere center +* @param {number} radius +* @return {Boolean} true if the sphere collided with the mesh +*/ +Octree.prototype.testSphere = function( origin, radius ) +{ + origin = vec3.clone(origin); + octree_tested_boxes = 0; + octree_tested_triangles = 0; + + if(!this.root) + throw("Error: octree not build"); + + //better to use always the radius squared, because all the calculations are going to do that + var rr = radius * radius; + + if( !Octree.testSphereBox( origin, rr, vec3.clone(this.root.min), vec3.clone(this.root.max) ) ) + return false; //out of the box + + return Octree.testSphereInNode( this.root, origin, rr ); +} + +//WARNING: cannot use static here, it uses recursion +Octree.testRayInNode = function( node, origin, direction, test_backfaces ) +{ + var test = null; + var prev_test = null; + octree_tested_boxes += 1; + + //test faces + if(node.faces) + for(var i = 0, l = node.faces.length; i < l; ++i) + { + var face = node.faces[i]; + octree_tested_triangles += 1; + test = Octree.hitTestTriangle( origin, direction, face.subarray(0,3) , face.subarray(3,6), face.subarray(6,9), test_backfaces ); + if (test==null) + continue; + test.face = face; + if(prev_test) + prev_test.mergeWith( test ); + else + prev_test = test; + } + + //WARNING: cannot use statics here, this function uses recursion + var child_min = vec3.create(); + var child_max = vec3.create(); + + //test children nodes faces + var child; + if(node.c) + for(var i = 0; i < node.c.length; ++i) + { + child = node.c[i]; + child_min.set( child.min ); + child_max.set( child.max ); + + //test with node box + test = Octree.hitTestBox( origin, direction, child_min, child_max ); + if( test == null ) + continue; + + //nodebox behind current collision, then ignore node + if(prev_test && test.t > prev_test.t) + continue; + + //test collision with node + test = Octree.testRayInNode( child, origin, direction, test_backfaces ); + if(test == null) + continue; + + if(prev_test) + prev_test.mergeWith( test ); + else + prev_test = test; + } + + return prev_test; +} + +//WARNING: cannot use static here, it uses recursion +Octree.testSphereInNode = function( node, origin, radius2 ) +{ + var test = null; + var prev_test = null; + octree_tested_boxes += 1; + + //test faces + if(node.faces) + for(var i = 0, l = node.faces.length; i < l; ++i) + { + var face = node.faces[i]; + octree_tested_triangles += 1; + if( Octree.testSphereTriangle( origin, radius2, face.subarray(0,3) , face.subarray(3,6), face.subarray(6,9) ) ) + return true; + } + + //WARNING: cannot use statics here, this function uses recursion + var child_min = vec3.create(); + var child_max = vec3.create(); + + //test children nodes faces + var child; + if(node.c) + for(var i = 0; i < node.c.length; ++i) + { + child = node.c[i]; + child_min.set( child.min ); + child_max.set( child.max ); + + //test with node box + if( !Octree.testSphereBox( origin, radius2, child_min, child_max ) ) + continue; + + //test collision with node content + if( Octree.testSphereInNode( child, origin, radius2 ) ) + return true; + } + + return false; +} + +//test if one bounding is inside or overlapping another bounding +Octree.isInsideAABB = function(a,b) +{ + if(a.min[0] < b.min[0] || a.min[1] < b.min[1] || a.min[2] < b.min[2] || + a.max[0] > b.max[0] || a.max[1] > b.max[1] || a.max[2] > b.max[2]) + return false; + return true; +} + + +Octree.hitTestBox = (function(){ + var tMin = vec3.create(); + var tMax = vec3.create(); + var inv = vec3.create(); + var t1 = vec3.create(); + var t2 = vec3.create(); + var tmp = vec3.create(); + var epsilon = 1.0e-6; + var eps = vec3.fromValues( epsilon,epsilon,epsilon ); + + return function( origin, ray, box_min, box_max ) { + vec3.subtract( tMin, box_min, origin ); + vec3.subtract( tMax, box_max, origin ); + + if( vec3.maxValue(tMin) < 0 && vec3.minValue(tMax) > 0) + return new HitTest(0,origin,ray); + + inv[0] = 1/ray[0]; inv[1] = 1/ray[1]; inv[2] = 1/ray[2]; + vec3.multiply(tMin, tMin, inv); + vec3.multiply(tMax, tMax, inv); + vec3.min(t1, tMin, tMax); + vec3.max(t2, tMin, tMax); + var tNear = vec3.maxValue(t1); + var tFar = vec3.minValue(t2); + + if (tNear > 0 && tNear < tFar) { + var hit = vec3.add( vec3.create(), vec3.scale(tmp, ray, tNear ), origin); + vec3.add( box_min, box_min, eps); + vec3.subtract(box_min, box_min, eps); + return new HitTest(tNear, hit, vec3.fromValues( + (hit[0] > box_max[0]) - (hit[0] < box_min[0]), + (hit[1] > box_max[1]) - (hit[1] < box_min[1]), + (hit[2] > box_max[2]) - (hit[2] < box_min[2]) )); + } + + return null; + } +})(); + +Octree.hitTestTriangle = (function(){ + + var AB = vec3.create(); + var AC = vec3.create(); + var toHit = vec3.create(); + var tmp = vec3.create(); + + return function( origin, ray, A, B, C, test_backfaces ) { + vec3.subtract( AB, B, A ); + vec3.subtract( AC, C, A ); + var normal = vec3.cross( vec3.create(), AB, AC ); //returned + vec3.normalize( normal, normal ); + if( !test_backfaces && vec3.dot(normal,ray) > 0) + return null; //ignore backface + + var t = vec3.dot(normal, vec3.subtract( tmp, A, origin )) / vec3.dot(normal,ray); + + if (t > 0) + { + var hit = vec3.scale(vec3.create(), ray, t); //returned + vec3.add(hit, hit, origin); + vec3.subtract( toHit, hit, A ); + var dot00 = vec3.dot(AC,AC); + var dot01 = vec3.dot(AC,AB); + var dot02 = vec3.dot(AC,toHit); + var dot11 = vec3.dot(AB,AB); + var dot12 = vec3.dot(AB,toHit); + var divide = dot00 * dot11 - dot01 * dot01; + var u = (dot11 * dot02 - dot01 * dot12) / divide; + var v = (dot00 * dot12 - dot01 * dot02) / divide; + if (u >= 0 && v >= 0 && u + v <= 1) + return new HitTest(t, hit, normal); + } + return null; + }; +})(); + +//from http://realtimecollisiondetection.net/blog/?p=103 +//radius must be squared +Octree.testSphereTriangle = (function(){ + + var A = vec3.create(); + var B = vec3.create(); + var C = vec3.create(); + var AB = vec3.create(); + var AC = vec3.create(); + var BC = vec3.create(); + var CA = vec3.create(); + var V = vec3.create(); + + return function( P, rr, A_, B_, C_ ) { + vec3.sub( A, A_, P ); + vec3.sub( B, B_, P ); + vec3.sub( C, C_, P ); + + vec3.sub( AB, B, A ); + vec3.sub( AC, C, A ); + + vec3.cross( V, AB, AC ); + var d = vec3.dot( A, V ); + var e = vec3.dot( V, V ); + var sep1 = d * d > rr * e; + var aa = vec3.dot(A, A); + var ab = vec3.dot(A, B); + var ac = vec3.dot(A, C); + var bb = vec3.dot(B, B); + var bc = vec3.dot(B, C); + var cc = vec3.dot(C, C); + var sep2 = (aa > rr) & (ab > aa) & (ac > aa); + var sep3 = (bb > rr) & (ab > bb) & (bc > bb); + var sep4 = (cc > rr) & (ac > cc) & (bc > cc); + + var d1 = ab - aa; + var d2 = bc - bb; + var d3 = ac - cc; + + vec3.sub( BC, C, B ); + vec3.sub( CA, A, C ); + + var e1 = vec3.dot(AB, AB); + var e2 = vec3.dot(BC, BC); + var e3 = vec3.dot(CA, CA); + + var Q1 = vec3.scale(vec3.create(), A, e1); vec3.sub( Q1, Q1, vec3.scale(vec3.create(), AB, d1) ); + var Q2 = vec3.scale(vec3.create(), B, e2); vec3.sub( Q2, Q2, vec3.scale(vec3.create(), BC, d2) ); + var Q3 = vec3.scale(vec3.create(), C, e3); vec3.sub( Q3, Q3, vec3.scale(vec3.create(), CA, d3) ); + + var QC = vec3.scale( vec3.create(), C, e1 ); QC = vec3.sub( QC, QC, Q1 ); + var QA = vec3.scale( vec3.create(), A, e2 ); QA = vec3.sub( QA, QA, Q2 ); + var QB = vec3.scale( vec3.create(), B, e3 ); QB = vec3.sub( QB, QB, Q3 ); + + var sep5 = ( vec3.dot(Q1, Q1) > rr * e1 * e1) & (vec3.dot(Q1, QC) > 0 ); + var sep6 = ( vec3.dot(Q2, Q2) > rr * e2 * e2) & (vec3.dot(Q2, QA) > 0 ); + var sep7 = ( vec3.dot(Q3, Q3) > rr * e3 * e3) & (vec3.dot(Q3, QB) > 0 ); + + var separated = sep1 | sep2 | sep3 | sep4 | sep5 | sep6 | sep7 + return !separated; + }; +})(); + +Octree.testSphereBox = function( center, radius2, box_min, box_max ) { + + // arvo's algorithm from gamasutra + // http://www.gamasutra.com/features/19991018/Gomez_4.htm + var s, d = 0.0; + //find the square of the distance + //from the sphere to the box + for(var i = 0; i < 3; ++i) + { + if( center[i] < box_min[i] ) + { + s = center[i] - box_min[i]; + d += s*s; + } + else if( center[i] > box_max[i] ) + { + s = center[i] - box_max[i]; + d += s*s; + } + } + //return d <= r*r + + if (d <= radius2) + { + return true; + /* + // this is used just to know if it overlaps or is just inside, but I dont care + // make an aabb aabb test with the sphere aabb to test inside state + var halfsize = vec3.fromValues( radius, radius, radius ); + var sphere_bbox = BBox.fromCenterHalfsize( center, halfsize ); + if ( geo.testBBoxBBox(bbox, sphere_bbox) ) + return INSIDE; + return OVERLAP; + */ + } + + return false; //OUTSIDE; +}; +// Provides a convenient raytracing interface. + +// ### new GL.HitTest([t, hit, normal]) +// +// This is the object used to return hit test results. If there are no +// arguments, the constructed argument represents a hit infinitely far +// away. +global.HitTest = GL.HitTest = function HitTest(t, hit, normal) { + this.t = arguments.length ? t : Number.MAX_VALUE; + this.hit = hit; + this.normal = normal; + this.face = null; +} + +// ### .mergeWith(other) +// +// Changes this object to be the closer of the two hit test results. +HitTest.prototype = { + mergeWith: function(other) { + if (other.t > 0 && other.t < this.t) { + this.t = other.t; + this.hit = other.hit; + this.normal = other.normal; + this.face = other.face; + } + } +}; + +// ### new GL.Ray( origin, direction ) +global.Ray = GL.Ray = function Ray( origin, direction ) +{ + this.origin = vec3.create(); + this.direction = vec3.create(); + this.collision_point = vec3.create(); + + if(origin) + this.origin.set( origin ); + if(direction) + this.direction.set( direction ); +} + +Ray.prototype.testPlane = function( P, N ) +{ + return geo.testRayPlane( this.origin, this.direction, P, N, this.collision_point ); +} + +Ray.prototype.testSphere = function( center, radius, max_dist ) +{ + return geo.testRaySphere( this.origin, this.direction, center, radius, this.collision_point, max_dist ); +} + +// ### new GL.Raytracer() +// +// This will read the current modelview matrix, projection matrix, and viewport, +// reconstruct the eye position, and store enough information to later generate +// per-pixel rays using `getRayForPixel()`. +// +// Example usage: +// +// var tracer = new GL.Raytracer(); +// var ray = tracer.getRayForPixel( +// gl.canvas.width / 2, +// gl.canvas.height / 2); +// var result = GL.Raytracer.hitTestSphere( +// tracer.eye, ray, new GL.Vector(0, 0, 0), 1); + +global.Raytracer = GL.Raytracer = function Raytracer( viewprojection_matrix, viewport ) { + this.viewport = vec4.create(); + this.ray00 = vec3.create(); + this.ray10 = vec3.create(); + this.ray01 = vec3.create(); + this.ray11 = vec3.create(); + this.eye = vec3.create(); + this.setup( viewprojection_matrix, viewport ); +} + +Raytracer.prototype.setup = function( viewprojection_matrix, viewport ) +{ + viewport = viewport || gl.viewport_data; + this.viewport.set( viewport ); + + var minX = viewport[0], maxX = minX + viewport[2]; + var minY = viewport[1], maxY = minY + viewport[3]; + + vec3.set( this.ray00, minX, minY, 1 ); + vec3.set( this.ray10, maxX, minY, 1 ); + vec3.set( this.ray01, minX, maxY, 1 ); + vec3.set( this.ray11, maxX, maxY, 1 ); + vec3.unproject( this.ray00, this.ray00, viewprojection_matrix, viewport); + vec3.unproject( this.ray10, this.ray10, viewprojection_matrix, viewport); + vec3.unproject( this.ray01, this.ray01, viewprojection_matrix, viewport); + vec3.unproject( this.ray11, this.ray11, viewprojection_matrix, viewport); + var eye = this.eye; + vec3.unproject(eye, eye, viewprojection_matrix, viewport); + vec3.subtract(this.ray00, this.ray00, eye); + vec3.subtract(this.ray10, this.ray10, eye); + vec3.subtract(this.ray01, this.ray01, eye); + vec3.subtract(this.ray11, this.ray11, eye); +} + + // ### .getRayForPixel(x, y) + // + // Returns the ray direction originating from the camera and traveling through the pixel `x, y`. +Raytracer.prototype.getRayForPixel = (function(){ + var ray0 = vec3.create(); + var ray1 = vec3.create(); + return function(x, y, out) { + out = out || vec3.create(); + x = (x - this.viewport[0]) / this.viewport[2]; + y = 1 - (y - this.viewport[1]) / this.viewport[3]; + vec3.lerp(ray0, this.ray00, this.ray10, x); + vec3.lerp(ray1, this.ray01, this.ray11, x); + vec3.lerp( out, ray0, ray1, y) + return vec3.normalize( out, out ); + } +})(); + +// ### GL.Raytracer.hitTestBox(origin, ray, min, max) +// +// Traces the ray starting from `origin` along `ray` against the axis-aligned box +// whose coordinates extend from `min` to `max`. Returns a `HitTest` with the +// information or `null` for no intersection. +// +// This implementation uses the [slab intersection method](http://www.siggraph.org/education/materials/HyperGraph/raytrace/rtinter3.htm). +var _hittest_inv = mat4.create(); +Raytracer.hitTestBox = function(origin, ray, min, max, model) { + var _hittest_v3 = new Float32Array(10*3); //reuse memory to speedup + + if(model) + { + var inv = mat4.invert( _hittest_inv, model ); + origin = mat4.multiplyVec3( _hittest_v3.subarray(3,6), inv, origin ); + ray = mat4.rotateVec3( _hittest_v3.subarray(6,9), inv, ray ); + } + + var tMin = vec3.subtract( _hittest_v3.subarray(9,12), min, origin ); + vec3.divide( tMin, tMin, ray ); + + var tMax = vec3.subtract( _hittest_v3.subarray(12,15), max, origin ); + vec3.divide( tMax, tMax, ray ); + + var t1 = vec3.min( _hittest_v3.subarray(15,18), tMin, tMax); + var t2 = vec3.max( _hittest_v3.subarray(18,21), tMin, tMax); + + var tNear = vec3.maxValue(t1); + var tFar = vec3.minValue(t2); + + if (tNear > 0 && tNear <= tFar) { + var epsilon = 1.0e-6; + var hit = vec3.scale( _hittest_v3.subarray(21,24), ray, tNear); + vec3.add( hit, origin, hit ); + + vec3.addValue(_hittest_v3.subarray(24,27), min, epsilon); + vec3.subValue(_hittest_v3.subarray(27,30), max, epsilon); + + return new HitTest(tNear, hit, vec3.fromValues( + (hit[0] > max[0]) - (hit[0] < min[0]), + (hit[1] > max[1]) - (hit[1] < min[1]), + (hit[2] > max[2]) - (hit[2] < min[2]) + )); + } + + return null; +}; + + + + +// ### GL.Raytracer.hitTestSphere(origin, ray, center, radius) +// +// Traces the ray starting from `origin` along `ray` against the sphere defined +// by `center` and `radius`. Returns a `HitTest` with the information or `null` +// for no intersection. +Raytracer.hitTestSphere = function(origin, ray, center, radius) { + var offset = vec3.subtract( vec3.create(), origin,center); + var a = vec3.dot(ray,ray); + var b = 2 * vec3.dot(ray,offset); + var c = vec3.dot(offset,offset) - radius * radius; + var discriminant = b * b - 4 * a * c; + + if (discriminant > 0) { + var t = (-b - Math.sqrt(discriminant)) / (2 * a), hit = vec3.add(vec3.create(),origin, vec3.scale(vec3.create(), ray, t)); + return new HitTest(t, hit, vec3.scale( vec3.create(), vec3.subtract(vec3.create(), hit,center), 1.0/radius)); + } + + return null; +}; + + +// ### GL.Raytracer.hitTestTriangle(origin, ray, a, b, c) +// +// Traces the ray starting from `origin` along `ray` against the triangle defined +// by the points `a`, `b`, and `c`. Returns a `HitTest` with the information or +// `null` for no intersection. +Raytracer.hitTestTriangle = function(origin, ray, a, b, c) { + var ab = vec3.subtract(vec3.create(), b,a ); + var ac = vec3.subtract(vec3.create(), c,a ); + var normal = vec3.cross( vec3.create(), ab,ac); + vec3.normalize( normal, normal ); + var t = vec3.dot(normal, vec3.subtract( vec3.create(), a,origin)) / vec3.dot(normal,ray); + + if (t > 0) { + var hit = vec3.add( vec3.create(), origin, vec3.scale(vec3.create(), ray,t)); + var toHit = vec3.subtract( vec3.create(), hit, a); + var dot00 = vec3.dot(ac,ac); + var dot01 = vec3.dot(ac,ab); + var dot02 = vec3.dot(ac,toHit); + var dot11 = vec3.dot(ab,ab); + var dot12 = vec3.dot(ab,toHit); + var divide = dot00 * dot11 - dot01 * dot01; + var u = (dot11 * dot02 - dot01 * dot12) / divide; + var v = (dot00 * dot12 - dot01 * dot02) / divide; + if (u >= 0 && v >= 0 && u + v <= 1) return new HitTest(t, hit, normal); + } + + return null; +}; +//***** OBJ parser adapted from SpiderGL implementation ***************** +/** +* Parses a OBJ string and returns an object with the info ready to be passed to GL.Mesh.load +* @method Mesh.parseOBJ +* @param {String} data all the OBJ info to be parsed +* @param {Object} options +* @return {Object} mesh information (vertices, coords, normals, indices) +*/ + +Mesh.parseOBJ = function( text, options ) +{ + options = options || {}; + + //final arrays (packed, lineal [ax,ay,az, bx,by,bz ...]) + var positionsArray = [ ]; + var texcoordsArray = [ ]; + var normalsArray = [ ]; + var indicesArray = [ ]; + + //unique arrays (not packed, lineal) + var positions = [ ]; + var texcoords = [ ]; + var normals = [ ]; + var facemap = { }; + var index = 0; + + var line = null; + var f = null; + var pos = 0; + var tex = 0; + var nor = 0; + var x = 0.0; + var y = 0.0; + var z = 0.0; + var tokens = null; + + var hasPos = false; + var hasTex = false; + var hasNor = false; + + var parsingFaces = false; + var indices_offset = 0; + var negative_offset = -1; //used for weird objs with negative indices + var max_index = 0; + + var skip_indices = options.noindex ? options.noindex : (text.length > 10000000 ? true : false); + //trace("SKIP INDICES: " + skip_indices); + var flip_axis = options.flipAxis; + var flip_normals = (flip_axis || options.flipNormals); + + //used for mesh groups (submeshes) + var group = null; + var groups = []; + var materials_found = {}; + + var V_CODE = 1; + var VT_CODE = 2; + var VN_CODE = 3; + var F_CODE = 4; + var G_CODE = 5; + var O_CODE = 6; + var codes = { v: V_CODE, vt: VT_CODE, vn: VN_CODE, f: F_CODE, g: G_CODE, o: O_CODE }; + + + var lines = text.split("\n"); + var length = lines.length; + for (var lineIndex = 0; lineIndex < length; ++lineIndex) { + line = lines[lineIndex].replace(/[ \t]+/g, " ").replace(/\s\s*$/, ""); //trim + + if (line[0] == "#") continue; + if(line == "") continue; + + tokens = line.split(" "); + var code = codes[ tokens[0] ]; + + if(parsingFaces && code == V_CODE) //another mesh? + { + indices_offset = index; + parsingFaces = false; + //trace("multiple meshes: " + indices_offset); + } + + //read and parse numbers + if( code <= VN_CODE ) //v,vt,vn + { + x = parseFloat(tokens[1]); + y = parseFloat(tokens[2]); + if( code != VT_CODE ) + { + if(tokens[3] == '\\') //super weird case, OBJ allows to break lines with slashes... + { + //HACK! only works if the var is the thirth position... + ++lineIndex; + line = lines[lineIndex].replace(/[ \t]+/g, " ").replace(/\s\s*$/, ""); //better than trim + z = parseFloat(line); + } + else + z = parseFloat(tokens[3]); + } + } + + if (code == V_CODE) { + if(flip_axis) //maya and max notation style + positions.push(-1*x,z,y); + else + positions.push(x,y,z); + } + else if (code == VT_CODE) { + texcoords.push(x,y); + } + else if (code == VN_CODE) { + + if(flip_normals) //maya and max notation style + normals.push(-y,-z,x); + else + normals.push(x,y,z); + } + else if (code == F_CODE) { + parsingFaces = true; + + if (tokens.length < 4) continue; //faces with less that 3 vertices? nevermind + + //for every corner of this polygon + var polygon_indices = []; + for (var i=1; i < tokens.length; ++i) + { + if (!(tokens[i] in facemap) || skip_indices) + { + f = tokens[i].split("/"); + + if (f.length == 1) { //unpacked + pos = parseInt(f[0]) - 1; + tex = pos; + nor = pos; + } + else if (f.length == 2) { //no normals + pos = parseInt(f[0]) - 1; + tex = parseInt(f[1]) - 1; + nor = -1; + } + else if (f.length == 3) { //all three indexed + pos = parseInt(f[0]) - 1; + tex = parseInt(f[1]) - 1; + nor = parseInt(f[2]) - 1; + } + else { + console.err("Problem parsing: unknown number of values per face"); + return false; + } + + if(i > 3 && skip_indices) //break polygon in triangles + { + //first + var pl = positionsArray.length; + positionsArray.push( positionsArray[pl - (i-3)*9], positionsArray[pl - (i-3)*9 + 1], positionsArray[pl - (i-3)*9 + 2]); + positionsArray.push( positionsArray[pl - 3], positionsArray[pl - 2], positionsArray[pl - 1]); + pl = texcoordsArray.length; + texcoordsArray.push( texcoordsArray[pl - (i-3)*6], texcoordsArray[pl - (i-3)*6 + 1]); + texcoordsArray.push( texcoordsArray[pl - 2], texcoordsArray[pl - 1]); + pl = normalsArray.length; + normalsArray.push( normalsArray[pl - (i-3)*9], normalsArray[pl - (i-3)*9 + 1], normalsArray[pl - (i-3)*9 + 2]); + normalsArray.push( normalsArray[pl - 3], normalsArray[pl - 2], normalsArray[pl - 1]); + } + + //add new vertex + x = 0.0; + y = 0.0; + z = 0.0; + if ((pos * 3 + 2) < positions.length) { + hasPos = true; + x = positions[pos*3+0]; + y = positions[pos*3+1]; + z = positions[pos*3+2]; + } + positionsArray.push(x,y,z); + + //add new texture coordinate + x = 0.0; + y = 0.0; + if ((tex * 2 + 1) < texcoords.length) { + hasTex = true; + x = texcoords[tex*2+0]; + y = texcoords[tex*2+1]; + } + texcoordsArray.push(x,y); + + //add new normal + x = 0.0; + y = 0.0; + z = 1.0; + if(nor != -1) + { + if ((nor * 3 + 2) < normals.length) { + hasNor = true; + x = normals[nor*3+0]; + y = normals[nor*3+1]; + z = normals[nor*3+2]; + } + + normalsArray.push(x,y,z); + } + + //Save the string "10/10/10" and tells which index represents it in the arrays + if(!skip_indices) + facemap[tokens[i]] = index++; + }//end of 'if this token is new (store and index for later reuse)' + + //store key for this triplet + if(!skip_indices) + { + var final_index = facemap[tokens[i]]; + polygon_indices.push(final_index); + if(max_index < final_index) + max_index = final_index; + } + } //end of for every token on a 'f' line + + //polygons (not just triangles) + if(!skip_indices) + { + for(var iP = 2; iP < polygon_indices.length; iP++) + { + indicesArray.push( polygon_indices[0], polygon_indices[iP-1], polygon_indices[iP] ); + //indicesArray.push( [polygon_indices[0], polygon_indices[iP-1], polygon_indices[iP]] ); + } + } + } + else if (code == G_CODE) { //tokens[0] == "usemtl" + negative_offset = positions.length / 3 - 1; + + if(tokens.length > 1) + { + if(group != null) + { + group.length = indicesArray.length - group.start; + if(group.length > 0) + groups.push(group); + } + + group = { + name: tokens[1], + start: indicesArray.length, + length: -1, + material: "" + }; + } + } + else if (tokens[0] == "usemtl") { + if(group) + group.material = tokens[1]; + } + } + + if(!positions.length) + { + console.error("OBJ doesnt have vertices, maybe the file is not a OBJ"); + return null; + } + + if(group && (indicesArray.length - group.start) > 1) + { + group.length = indicesArray.length - group.start; + groups.push(group); + } + + //deindex streams + if((max_index > 256*256 || skip_indices ) && indicesArray.length > 0) + { + console.log("Deindexing mesh...") + var finalVertices = new Float32Array(indicesArray.length * 3); + var finalNormals = normalsArray && normalsArray.length ? new Float32Array(indicesArray.length * 3) : null; + var finalTexCoords = texcoordsArray && texcoordsArray.length ? new Float32Array(indicesArray.length * 2) : null; + for(var i = 0; i < indicesArray.length; i += 1) + { + finalVertices.set( positionsArray.slice( indicesArray[i]*3,indicesArray[i]*3 + 3), i*3 ); + if(finalNormals) + finalNormals.set( normalsArray.slice( indicesArray[i]*3,indicesArray[i]*3 + 3 ), i*3 ); + if(finalTexCoords) + finalTexCoords.set( texcoordsArray.slice(indicesArray[i]*2,indicesArray[i]*2 + 2 ), i*2 ); + } + positionsArray = finalVertices; + if(finalNormals) + normalsArray = finalNormals; + if(finalTexCoords) + texcoordsArray = finalTexCoords; + indicesArray = null; + } + + //Create final mesh object + var mesh = {}; + + //create typed arrays + if (hasPos) + mesh.vertices = new Float32Array(positionsArray); + if (hasNor && normalsArray.length > 0) + mesh.normals = new Float32Array(normalsArray); + if (hasTex && texcoordsArray.length > 0) + mesh.coords = new Float32Array(texcoordsArray); + if (indicesArray && indicesArray.length > 0) + mesh.triangles = new Uint16Array(indicesArray); + + var info = {}; + if(groups.length > 1) + info.groups = groups; + mesh.info = info; + + if(options.only_data) + return mesh; + + //creates and returns a GL.Mesh + var final_mesh = null; + final_mesh = Mesh.load( mesh, null, options.mesh ); + final_mesh.updateBoundingBox(); + return final_mesh; +} + +Mesh.parsers["obj"] = Mesh.parseOBJ; + +Mesh.encoders["obj"] = function( mesh, options ) +{ + //store vertices + var verticesBuffer = mesh.getBuffer("vertices"); + if(!verticesBuffer) + return null; + + var lines = []; + lines.push("# Generated with liteGL.js by Javi Agenjo\n"); + + var vertices = verticesBuffer.data; + for (var i = 0; i < vertices.length; i+=3) + lines.push("v " + vertices[i].toFixed(4) + " " + vertices[i+1].toFixed(4) + " " + vertices[i+2].toFixed(4)); + + //store normals + var normalsBuffer = mesh.getBuffer("normals"); + if(normalsBuffer) + { + lines.push(""); + var normals = normalsBuffer.data; + for (var i = 0; i < normals.length; i+=3) + lines.push("vn " + normals[i].toFixed(4) + " " + normals[i+1].toFixed(4) + " " + normals[i+2].toFixed(4) ); + } + + //store uvs + var coordsBuffer = mesh.getBuffer("coords"); + if(coordsBuffer) + { + lines.push(""); + var coords = coordsBuffer.data; + for (var i = 0; i < coords.length; i+=2) + lines.push("vt " + coords[i].toFixed(4) + " " + coords[i+1].toFixed(4) + " " + " 0.0000"); + } + + var groups = mesh.info.groups; + + + //store faces + var indicesBuffer = mesh.getIndexBuffer("triangles"); + if(indicesBuffer) + { + var indices = indicesBuffer.data; + if(!groups || !groups.length) + groups = [{start:0, length: indices.length, name:"mesh"}]; + for(var j = 0; j < groups.length; ++j) + { + var group = groups[j]; + lines.push("g " + group.name ); + lines.push("usemtl " + (group.material || ("mat_"+j))); + var start = group.start; + var end = start + group.length; + for (var i = start; i < end; i+=3) + lines.push("f " + (indices[i]+1) + "/" + (indices[i]+1) + "/" + (indices[i]+1) + " " + (indices[i+1]+1) + "/" + (indices[i+1]+1) + "/" + (indices[i+1]+1) + " " + (indices[i+2]+1) + "/" + (indices[i+2]+1) + "/" + (indices[i+2]+1) ); + } + } + else //no indices + { + if(!groups || !groups.length) + groups = [{start:0, length: (vertices.length / 3), name:"mesh"}]; + for(var j = 0; j < groups.length; ++j) + { + var group = groups[j]; + lines.push("g " + group.name); + lines.push("usemtl " + (group.material || ("mat_"+j))); + var start = group.start; + var end = start + group.length; + for (var i = start; i < end; i+=3) + lines.push( "f " + (i+1) + "/" + (i+1) + "/" + (i+1) + " " + (i+2) + "/" + (i+2) + "/" + (i+2) + " " + (i+3) + "/" + (i+3) + "/" + (i+3) ); + } + } + + return lines.join("\n"); +} + +//simple format to output meshes in ASCII +Mesh.parsers["mesh"] = function( text, options ) +{ + var mesh = {}; + + var lines = text.split("\n"); + for(var i = 0; i < lines.length; ++i) + { + var line = lines[i]; + var type = line[0]; + var t = line.substr(1).split(","); + var name = t[0]; + + if(type == "-") //buffer + { + var data = new Float32Array( Number(t[1]) ); + for(var j = 0; j < data.length; ++j) + data[j] = Number(t[j+2]); + mesh[name] = data; + } + else if(type == "*") //index + { + var data = Number(t[1]) > 256*256 ? new Uint32Array( Number(t[1]) ) : new Uint16Array( Number(t[1]) ); + for(var j = 0; j < data.length; ++j) + data[j] = Number(t[j+2]); + mesh[name] = data; + } + else if(type == "@") //info + { + if(name == "bones") + { + var bones = []; + var num_bones = Number(t[1]); + for(var j = 0; j < num_bones; ++j) + { + var m = (t.slice(3 + j*17, 3 + (j+1)*17 - 1)).map(Number); + bones.push( [ t[2 + j*17], m ] ); + } + mesh.bones = bones; + } + else if(name == "bind_matrix") + mesh.bind_matrix = t.slice(1,17).map(Number); + else if(name == "groups") + { + mesh.info = { groups: [] }; + var num_groups = Number(t[1]); + for(var j = 0; j < num_groups; ++j) + { + var group = { name: t[2+j*4], material: t[2+j*4+1], start: Number(t[2+j*4+2]), length: Number(t[2+j*4+3]) }; + mesh.info.groups.push(group); + } + } + } + else + console.warn("type unknown: " + t[0] ); + } + + if(options.only_data) + return mesh; + + //creates and returns a GL.Mesh + var final_mesh = null; + final_mesh = Mesh.load( mesh, null, options.mesh ); + final_mesh.updateBoundingBox(); + return final_mesh; +} + +Mesh.encoders["mesh"] = function( mesh, options ) +{ + var lines = []; + for(var i in mesh.vertexBuffers ) + { + var buffer = mesh.vertexBuffers[i]; + var line = ["-"+i, buffer.data.length, buffer.data, typedArrayToArray( buffer.data ) ]; + lines.push(line.join(",")); + } + + for(var i in mesh.indexBuffers ) + { + var buffer = mesh.indexBuffers[i]; + var line = [ "*" + i, buffer.data.length, buffer.data, typedArrayToArray( buffer.data ) ]; + lines.push(line.join(",")); + } + + if(mesh.bounding) + lines.push( ["@bounding", typedArrayToArray(mesh.bounding.subarray(0,6))].join(",") ); + if(mesh.info && mesh.info.groups) + { + var groups_info = []; + for(var j = 0; j < mesh.info.groups.length; ++j) + { + var group = mesh.info.groups[j]; + groups_info.push( group.name, group.material, group.start, group.length ); + } + lines.push( ["@groups", mesh.info.groups.length ].concat( groups_info ).join(",") ); + } + + if(mesh.bones) + lines.push( ["@bones", mesh.bones.length, mesh.bones.flat()].join(",") ); + if(mesh.bind_matrix) + lines.push( ["@bind_matrix", typedArrayToArray(mesh.bind_matrix) ].join(",") ); + + return lines.join("\n"); +} + +/* BINARY FORMAT ************************************/ + +if(global.WBin) + global.WBin.classes["Mesh"] = Mesh; + +Mesh.binary_file_formats["wbin"] = true; + +Mesh.parsers["wbin"] = Mesh.fromBinary = function( data_array, options ) +{ + if(!global.WBin) + throw("To use binary meshes you need to install WBin.js from https://github.com/jagenjo/litescene.js/blob/master/src/utils/wbin.js "); + + options = options || {}; + + var o = null; + if( data_array.constructor == ArrayBuffer ) + o = WBin.load( data_array, true ); + else + o = data_array; + + if(!o.info) + console.warn("This WBin doesn't seem to contain a mesh. Classname: ", o["@classname"] ); + + if( o.format ) + GL.Mesh.decompress( o ); + + var vertex_buffers = {}; + if(o.vertex_buffers) + { + for(var i in o.vertex_buffers) + vertex_buffers[ o.vertex_buffers[i] ] = o[ o.vertex_buffers[i] ]; + } + else + { + if(o.vertices) vertex_buffers.vertices = o.vertices; + if(o.normals) vertex_buffers.normals = o.normals; + if(o.coords) vertex_buffers.coords = o.coords; + if(o.weights) vertex_buffers.weights = o.weights; + if(o.bone_indices) vertex_buffers.bone_indices = o.bone_indices; + } + + var index_buffers = {}; + if( o.index_buffers ) + { + for(var i in o.index_buffers) + index_buffers[ o.index_buffers[i] ] = o[ o.index_buffers[i] ]; + } + else + { + if(o.triangles) index_buffers.triangles = o.triangles; + if(o.wireframe) index_buffers.wireframe = o.wireframe; + } + + var mesh = { + vertex_buffers: vertex_buffers, + index_buffers: index_buffers, + bounding: o.bounding, + info: o.info + }; + + if(o.bones) + { + mesh.bones = o.bones; + //restore Float32array + for(var i = 0; i < mesh.bones.length; ++i) + mesh.bones[i][1] = mat4.clone(mesh.bones[i][1]); + if(o.bind_matrix) + mesh.bind_matrix = mat4.clone( o.bind_matrix ); + } + + if(o.morph_targets) + mesh.morph_targets = o.morph_targets; + + if(options.only_data) + return mesh; + + //build mesh object + var final_mesh = options.mesh || new GL.Mesh(); + final_mesh.configure( mesh ); + return final_mesh; +} + +Mesh.encoders["wbin"] = function( mesh, options ) +{ + return mesh.toBinary( options ); +} + +Mesh.prototype.toBinary = function( options ) +{ + if(!global.WBin) + throw("to use Mesh.toBinary you need to have WBin included. Check the repository for wbin.js"); + + if(!this.info) + this.info = {}; + + //clean data + var o = { + object_class: "Mesh", + info: this.info, + groups: this.groups + }; + + if(this.bones) + { + var bones = []; + //convert to array + for(var i = 0; i < this.bones.length; ++i) + bones.push([ this.bones[i][0], mat4.toArray( this.bones[i][1] ) ]); + o.bones = bones; + if(this.bind_matrix) + o.bind_matrix = this.bind_matrix; + } + + //bounding box + if(!this.bounding) + this.updateBoundingBox(); + o.bounding = this.bounding; + + var vertex_buffers = []; + var index_buffers = []; + + for(var i in this.vertexBuffers) + { + var stream = this.vertexBuffers[i]; + o[ stream.name ] = stream.data; + vertex_buffers.push( stream.name ); + + if(stream.name == "vertices") + o.info.num_vertices = stream.data.length / 3; + } + + for(var i in this.indexBuffers) + { + var stream = this.indexBuffers[i]; + o[i] = stream.data; + index_buffers.push( i ); + } + + o.vertex_buffers = vertex_buffers; + o.index_buffers = index_buffers; + + //compress wbin using the bounding + if( GL.Mesh.enable_wbin_compression ) //apply compression + GL.Mesh.compress( o ); + + //create pack file + var bin = WBin.create( o, "Mesh" ); + return bin; +} + +Mesh.compress = function( o, format ) +{ + format = format || "bounding_compressed"; + o.format = { + type: format + }; + + var func = Mesh.compressors[ format ]; + if(!func) + throw("compression format not supported:" + format ); + return func( o ); +} + +Mesh.decompress = function( o ) +{ + if(!o.format) + return; + var func = Mesh.decompressors[ o.format.type ]; + if(!func) + throw("decompression format not supported:" + o.format.type ); + return func( o ); +} + +Mesh.compressors["bounding_compressed"] = function(o) +{ + if(!o.vertex_buffers) + throw("buffers not found"); + + var min = BBox.getMin( o.bounding ); + var max = BBox.getMax( o.bounding ); + var range = vec3.sub( vec3.create(), max, min ); + + var vertices = o.vertices; + var new_vertices = new Uint16Array( vertices.length ); + for(var i = 0; i < vertices.length; i+=3) + { + new_vertices[i] = ((vertices[i] - min[0]) / range[0]) * 65535; + new_vertices[i+1] = ((vertices[i+1] - min[1]) / range[1]) * 65535; + new_vertices[i+2] = ((vertices[i+2] - min[2]) / range[2]) * 65535; + } + o.vertices = new_vertices; + + if( o.normals ) + { + var normals = o.normals; + var new_normals = new Uint8Array( normals.length ); + var normals_range = new_normals.constructor == Uint8Array ? 255 : 65535; + for(var i = 0; i < normals.length; i+=3) + { + new_normals[i] = (normals[i] * 0.5 + 0.5) * normals_range; + new_normals[i+1] = (normals[i+1] * 0.5 + 0.5) * normals_range; + new_normals[i+2] = (normals[i+2] * 0.5 + 0.5) * normals_range; + } + o.normals = new_normals; + } + + if( o.coords ) + { + //compute uv bounding: [minu,minv,maxu,maxv] + var coords = o.coords; + var uvs_bounding = [10000,10000,-10000,-10000]; + for(var i = 0; i < coords.length; i+=2) + { + var u = coords[i]; + if( uvs_bounding[0] > u ) uvs_bounding[0] = u; + else if( uvs_bounding[2] < u ) uvs_bounding[2] = u; + var v = coords[i+1]; + if( uvs_bounding[1] > v ) uvs_bounding[1] = v; + else if( uvs_bounding[3] < v ) uvs_bounding[3] = v; + } + o.format.uvs_bounding = uvs_bounding; + + var new_coords = new Uint16Array( coords.length ); + var range = [ uvs_bounding[2] - uvs_bounding[0], uvs_bounding[3] - uvs_bounding[1] ]; + for(var i = 0; i < coords.length; i+=2) + { + new_coords[i] = ((coords[i] - uvs_bounding[0]) / range[0]) * 65535; + new_coords[i+1] = ((coords[i+1] - uvs_bounding[1]) / range[1]) * 65535; + } + o.coords = new_coords; + } + + if( o.weights ) + { + var weights = o.weights; + var new_weights = new Uint16Array( weights.length ); //using only one byte distorts the meshes a lot + var weights_range = new_weights.constructor == Uint8Array ? 255 : 65535; + for(var i = 0; i < weights.length; i+=4) + { + new_weights[i] = weights[i] * weights_range; + new_weights[i+1] = weights[i+1] * weights_range; + new_weights[i+2] = weights[i+2] * weights_range; + new_weights[i+3] = weights[i+3] * weights_range; + } + o.weights = new_weights; + } +} + + +Mesh.decompressors["bounding_compressed"] = function(o) +{ + var bounding = o.bounding; + if(!bounding) + throw("error in mesh decompressing data: bounding not found, cannot use the bounding decompression."); + + var min = BBox.getMin( bounding ); + var max = BBox.getMax( bounding ); + var range = vec3.sub( vec3.create(), max, min ); + + var format = o.format; + + var inv8 = 1 / 255; + var inv16 = 1 / 65535; + var vertices = o.vertices; + var new_vertices = new Float32Array( vertices.length ); + for( var i = 0, l = vertices.length; i < l; i += 3 ) + { + new_vertices[i] = ((vertices[i] * inv16) * range[0]) + min[0]; + new_vertices[i+1] = ((vertices[i+1] * inv16) * range[1]) + min[1]; + new_vertices[i+2] = ((vertices[i+2] * inv16) * range[2]) + min[2]; + } + o.vertices = new_vertices; + + if( o.normals && o.normals.constructor != Float32Array ) + { + var normals = o.normals; + var new_normals = new Float32Array( normals.length ); + var inormals_range = normals.constructor == Uint8Array ? inv8 : inv16; + for( var i = 0, l = normals.length; i < l; i += 3 ) + { + new_normals[i] = (normals[i] * inormals_range) * 2.0 - 1.0; + new_normals[i+1] = (normals[i+1] * inormals_range) * 2.0 - 1.0; + new_normals[i+2] = (normals[i+2] * inormals_range) * 2.0 - 1.0; + var N = new_normals.subarray(i,i+3); + vec3.normalize(N,N); + } + o.normals = new_normals; + } + + if( o.coords && format.uvs_bounding && o.coords.constructor != Float32Array ) + { + var coords = o.coords; + var uvs_bounding = format.uvs_bounding; + var range = [ uvs_bounding[2] - uvs_bounding[0], uvs_bounding[3] - uvs_bounding[1] ]; + var new_coords = new Float32Array( coords.length ); + for( var i = 0, l = coords.length; i < l; i += 2 ) + { + new_coords[i] = (coords[i] * inv16) * range[0] + uvs_bounding[0]; + new_coords[i+1] = (coords[i+1] * inv16) * range[1] + uvs_bounding[1]; + } + o.coords = new_coords; + } + + //bones are already in Uint8 format so dont need to compress them further, but weights yes + if( o.weights && o.weights.constructor != Float32Array ) //do we really need to unpack them? what if we use them like this? + { + var weights = o.weights; + var new_weights = new Float32Array( weights.length ); + var iweights_range = weights.constructor == Uint8Array ? inv8 : inv16; + for(var i = 0, l = weights.length; i < l; i += 4 ) + { + new_weights[i] = weights[i] * iweights_range; + new_weights[i+1] = weights[i+1] * iweights_range; + new_weights[i+2] = weights[i+2] * iweights_range; + new_weights[i+3] = weights[i+3] * iweights_range; + } + o.weights = new_weights; + } +} + +//footer.js +})( typeof(window) != "undefined" ? window : (typeof(self) != "undefined" ? self : global ) ); diff --git a/src/litegraph-editor.js b/src/litegraph-editor.js index abf77fcbf..4b6042860 100755 --- a/src/litegraph-editor.js +++ b/src/litegraph-editor.js @@ -3,12 +3,9 @@ function Editor(container_id, options) { options = options || {}; //fill container - var html = - "
"; - html += - "
"; - html += - ""; + var html = "
"; + html += "
"; + html += ""; var root = document.createElement("div"); this.root = root; @@ -16,6 +13,7 @@ function Editor(container_id, options) { root.innerHTML = html; this.tools = root.querySelector(".tools"); + this.content = root.querySelector(".content"); this.footer = root.querySelector(".footer"); var canvas = root.querySelector(".graphcanvas"); @@ -28,6 +26,9 @@ function Editor(container_id, options) { graphcanvas.draw(true); }; + graphcanvas.onDropItem = this.onDropItem.bind(this); + graphcanvas.onShowNodePanel = this.onShowNodePanel.bind(this); + //add stuff //this.addToolsButton("loadsession_button","Load","imgs/icon-load.png", this.onLoadButton.bind(this), ".tools-left" ); //this.addToolsButton("savesession_button","Save","imgs/icon-save.png", this.onSaveButton.bind(this), ".tools-left" ); @@ -104,56 +105,159 @@ Editor.prototype.addLoadCounter = function() { }, 200); }; -Editor.prototype.addToolsButton = function( - id, - name, - icon_url, - callback, - container -) { +Editor.prototype.addToolsButton = function( id, name, icon_url, callback, container ) { if (!container) { container = ".tools"; } - var button = this.createButton(name, icon_url); + var button = this.createButton(name, icon_url, callback); button.id = id; - button.addEventListener("click", callback); - this.root.querySelector(container).appendChild(button); }; Editor.prototype.createPanel = function(title, options) { + options = options || {}; + + var ref_window = options.window || window; var root = document.createElement("div"); root.className = "dialog"; - root.innerHTML = - "
" + - title + - "
"; + root.innerHTML = "
"; root.header = root.querySelector(".dialog-header"); + if(options.closable) + { + var close = document.createElement("span"); + close.innerHTML = "✕"; + close.classList.add("close"); + close.addEventListener("click",function(){ + root.close(); + }); + root.header.appendChild(close); + } + root.title_element = root.querySelector(".dialog-title"); + root.title_element.innerText = title; root.content = root.querySelector(".dialog-content"); root.footer = root.querySelector(".dialog-footer"); + root.close = function() + { + this.parentNode.removeChild(this); + } + + root.addButton = function( name, callback, options ) + { + var elem = document.createElement("button"); + elem.innerText = name; + elem.options = options; + elem.addEventListener("click",callback); + root.footer.appendChild(elem); + return elem; + } + + root.addWidget = function( type, name, value, options, callback ) + { + options = options || {}; + var str_value = String(value); + if(type == "number") + str_value = value.toFixed(3); + + var elem = document.createElement("div"); + elem.className = "property"; + elem.innerHTML = ""; + elem.querySelector(".property_name").innerText = name; + var value_element = elem.querySelector(".property_value"); + value_element.innerText = str_value; + elem.dataset["property"] = name; + elem.dataset["type"] = options.type || type; + elem.options = options; + elem.value = value; + + //if( type == "code" ) + // elem.addEventListener("click", function(){ inner_showCodePad( node, this.dataset["property"] ); }); + if (type == "boolean") + { + elem.classList.add("boolean"); + if(value) + elem.classList.add("bool-on"); + elem.addEventListener("click", function(){ + //var v = node.properties[this.dataset["property"]]; + //node.setProperty(this.dataset["property"],!v); this.innerText = v ? "true" : "false"; + var propname = this.dataset["property"]; + this.value = !this.value; + this.classList.toggle("bool-on"); + this.querySelector(".property_value").innerText = this.value ? "true" : "false"; + innerChange(propname, this.value ); + }); + } + else if (type == "string" || type == "number") + { + value_element.setAttribute("contenteditable",true); + value_element.addEventListener("keydown", function(e){ + if(e.code == "Enter") + { + e.preventDefault(); + this.blur(); + } + }); + value_element.addEventListener("blur", function(){ + var v = this.innerText; + var propname = this.parentNode.dataset["property"]; + var proptype = this.parentNode.dataset["type"]; + if( proptype == "number") + v = Number(v); + innerChange(propname, v); + }); + } + else if (type == "enum") + value_element.addEventListener("click", function(event){ + var values = options.values || []; + var propname = this.parentNode.dataset["property"]; + var elem_that = this; + var menu = new LiteGraph.ContextMenu(values,{ + event: event, + className: "dark", + callback: inner_clicked + }, + ref_window); + function inner_clicked(v, option, event) { + //node.setProperty(propname,v); + //graphcanvas.dirty_canvas = true; + elem_that.innerText = v; + innerChange(propname,v); + return false; + } + }); + + root.content.appendChild(elem); + + function innerChange(name, value) + { + console.log("change",name,value); + //that.dirty_canvas = true; + if(options.callback) + options.callback(name,value); + if(callback) + callback(name,value); + } + + return elem; + } return root; }; -Editor.prototype.createButton = function(name, icon_url) { +Editor.prototype.createButton = function(name, icon_url, callback) { var button = document.createElement("button"); if (icon_url) { button.innerHTML = " "; } button.innerHTML += name; + if(callback) + button.addEventListener("click", callback ); return button; }; Editor.prototype.onLoadButton = function() { - var panel = this.createPanel("Load session"); - var close = this.createButton("Close"); - close.style.float = "right"; - close.addEventListener("click", function() { - panel.parentNode.removeChild(panel); - }); - panel.header.appendChild(close); - panel.content.innerHTML = "test"; + var panel = this.createPanel("Load session",{closable:true}); + //TO DO this.root.appendChild(panel); }; @@ -192,6 +296,177 @@ Editor.prototype.onLiveButton = function() { : " Edit"; }; +Editor.prototype.onDropItem = function(e) +{ + var that = this; + for(var i = 0; i < e.dataTransfer.files.length; ++i) + { + var file = e.dataTransfer.files[i]; + var ext = LGraphCanvas.getFileExtension(file.name); + var reader = new FileReader(); + if(ext == "json") + { + reader.onload = function(event) { + var data = JSON.parse( event.target.result ); + that.graph.configure(data); + }; + reader.readAsText(file); + } + } +} + +//shows the left side panel with the node info +Editor.prototype.onShowNodePanel = function(node) +{ + var panel = document.querySelector("#node-panel"); + if(panel) + panel.close(); + var ref_window = this.graphcanvas.getCanvasWindow(); + panel = this.createPanel(node.title || "",{closable: true, window: ref_window }); + panel.id = "node-panel"; + panel.classList.add("settings"); + var that = this; + var graphcanvas = this.graphcanvas; + + function inner_refresh() + { + panel.content.innerHTML = ""; //clear + var elem = document.createElement("div"); + elem.innerHTML = ""+node.type+""+(node.constructor.desc || "")+""; + panel.content.appendChild(elem); + + for(var i in node.properties) + { + var value = node.properties[i]; + var info = node.getPropertyInfo(i); + var type = info.type || "string"; + panel.addWidget( info.widget || info.type, i, value, info, function(name,value){ + node.setProperty(name,value); + graphcanvas.dirty_canvas = true; + }); + } + + panel.addButton("Delete",function(){ + node.graph.remove(node); + panel.close(); + }).classList.add("delete"); + + /* + for(var i in node.properties) + { + var value = node.properties[i]; + var type = "string"; + var info = node.getPropertyInfo(i); + var type = info.type; + + var str_value = String(value); + if(type == "number") + str_value = value.toFixed(3); + + var elem = document.createElement("div"); + elem.className = "property"; + elem.innerHTML = ""; + elem.querySelector(".property_name").innerText = i; + var value_element = elem.querySelector(".property_value"); + value_element.innerText = str_value; + elem.dataset["property"] = i; + elem.dataset["type"] = type; + elem.datainfo = info; + + if( type == "code" ) + elem.addEventListener("click", function(){ inner_showCodePad( node, this.dataset["property"] ); }); + else if (type == "boolean") + elem.addEventListener("click", function(){ + var v = node.properties[this.dataset["property"]]; + node.setProperty(this.dataset["property"],!v); this.innerText = v ? "true" : "false"; + }); + else if (type == "string" || type == "number") + { + value_element.setAttribute("contenteditable",true); + value_element.addEventListener("change", function(){ + var v = this.innerText; + var propname = this.parentNode.dataset["property"]; + if( propname == "number") + v = Number(v); + node.setProperty(propname,v); + that.dirty_canvas = true; + }); + } + else if (type == "enum") + value_element.addEventListener("click", function(event){ + var values = this.parentNode.datainfo.values || []; + var propname = this.parentNode.dataset["property"]; + var elem_that = this; + var menu = new LiteGraph.ContextMenu(values,{ + event: event, + className: "dark", + callback: inner_clicked + }, + ref_window); + function inner_clicked(v, option, event) { + node.setProperty(propname,v); + elem_that.innerText = v; + graphcanvas.dirty_canvas = true; + return false; + } + }); + else //generic + elem.addEventListener("click", function(){ + that.graphcanvas.showEditPropertyValue( node, this.dataset["property"], {onclose: inner_refresh} ); + }); + panel.content.appendChild(elem); + } + */ + } + + function inner_showCodePad( node, propname ) + { + panel.style.top = "calc( 50% - 250px)"; + panel.style.left = "calc( 50% - 400px)"; + panel.style.width = "800px"; + panel.style.height = "500px"; + + if(window.CodeFlask) //disabled for now + { + panel.content.innerHTML = "
"; + var flask = new CodeFlask( "div.code", { language: 'js' }); + flask.updateCode(node.properties[propname]); + flask.onUpdate( function(code) { + node.setProperty(propname, code); + }); + } + else + { + panel.content.innerHTML = ""; + var textarea = panel.content.querySelector("textarea"); + textarea.value = node.properties[propname]; + textarea.addEventListener("keydown", function(e){ + //console.log(e); + if(e.code == "Enter" && e.ctrlKey ) + { + console.log("Assigned"); + node.setProperty(propname, textarea.value); + } + }); + textarea.style.height = "calc(100% - 40px)"; + } + var assign = that.createButton( "Assign", null, function(){ + node.setProperty(propname, textarea.value); + }); + panel.content.appendChild(assign); + var button = that.createButton( "Close", null, function(){ + panel.style.height = ""; + inner_refresh(); + }); + button.style.float = "right"; + panel.content.appendChild(button); + } + + inner_refresh(); + + this.content.appendChild( panel ); +} + Editor.prototype.goFullscreen = function() { if (this.root.requestFullscreen) { this.root.requestFullscreen(Element.ALLOW_KEYBOARD_INPUT); diff --git a/src/litegraph.js b/src/litegraph.js index e61fc95fb..04ca903cd 100755 --- a/src/litegraph.js +++ b/src/litegraph.js @@ -132,8 +132,12 @@ } } - if( !Object.hasOwnProperty( base_class.prototype, "shape") ) + var prev = this.registered_node_types[type]; + if(prev) + console.log("replacing node type: " + type); + else { + if( !Object.hasOwnProperty( base_class.prototype, "shape") ) Object.defineProperty(base_class.prototype, "shape", { set: function(v) { switch (v) { @@ -161,11 +165,25 @@ }, enumerable: true }); - } - var prev = this.registered_node_types[type]; - if(prev) - console.log("replacing node type: " + type); + //warnings + if (base_class.prototype.onPropertyChange) { + console.warn( + "LiteGraph node class " + + type + + " has onPropertyChange method, it must be called onPropertyChanged with d at the end" + ); + } + + //used to know which nodes create when dragging files to the canvas + if (base_class.supported_extensions) { + for (var i in base_class.supported_extensions) { + var ext = base_class.supported_extensions[i]; + if(ext && ext.constructor === String) + this.node_types_by_file_extension[ ext.toLowerCase() ] = base_class; + } + } + } this.registered_node_types[type] = base_class; if (base_class.constructor.name) { @@ -177,26 +195,22 @@ if (prev && LiteGraph.onNodeTypeReplaced) { LiteGraph.onNodeTypeReplaced(type, base_class, prev); } - - //warnings - if (base_class.prototype.onPropertyChange) { - console.warn( - "LiteGraph node class " + - type + - " has onPropertyChange method, it must be called onPropertyChanged with d at the end" - ); - } - - //used to know which nodes create when dragging files to the canvas - if (base_class.supported_extensions) { - for (var i in base_class.supported_extensions) { - var ext = base_class.supported_extensions[i]; - if(ext && ext.constructor === String) - this.node_types_by_file_extension[ ext.toLowerCase() ] = base_class; - } - } }, + /** + * removes a node type from the system + * @method unregisterNodeType + * @param {String|Object} type name of the node or the node constructor itself + */ + unregisterNodeType: function(type) { + var base_class = type.constructor === String ? this.registered_node_types[type] : type; + if(!base_class) + throw("node type not found: " + type ); + delete this.registered_node_types[base_class.type]; + if(base_class.constructor.name) + delete this.Nodes[base_class.constructor.name]; + }, + /** * Create a new nodetype by passing a function, it wraps it with a proper class and generates inputs according to the parameters of the function. * Useful to wrap simple methods that do not require properties, and that only process some input to generate an output. @@ -681,24 +695,29 @@ interval = interval || 0; var that = this; - if ( - interval == 0 && - typeof window != "undefined" && - window.requestAnimationFrame - ) { + //execute once per frame + if ( interval == 0 && typeof window != "undefined" && window.requestAnimationFrame ) { function on_frame() { if (that.execution_timer_id != -1) { return; } window.requestAnimationFrame(on_frame); + if(that.onBeforeStep) + that.onBeforeStep(); that.runStep(1, !this.catch_errors); + if(that.onAfterStep) + that.onAfterStep(); } this.execution_timer_id = -1; on_frame(); - } else { + } else { //execute every 'interval' ms this.execution_timer_id = setInterval(function() { //execute + if(that.onBeforeStep) + that.onBeforeStep(); that.runStep(1, !this.catch_errors); + if(that.onAfterStep) + that.onAfterStep(); }, interval); } }; @@ -1893,7 +1912,7 @@ //copy all stored fields for (var i in data) { - if(i == "nodes" || i == "groups") + if(i == "nodes" || i == "groups" ) //links must be accepted continue; this[i] = data[i]; } @@ -2203,6 +2222,8 @@ for (var i = 0; i < this.widgets.length; ++i) { var w = this.widgets[i]; + if(!w) + continue; if(w.options && w.options.property && this.properties[ w.options.property ]) w.value = JSON.parse( JSON.stringify( this.properties[ w.options.property ] ) ); } @@ -2265,7 +2286,10 @@ if (this.widgets && this.serialize_widgets) { o.widgets_values = []; for (var i = 0; i < this.widgets.length; ++i) { - o.widgets_values[i] = this.widgets[i].value; + if(this.widgets[i]) + o.widgets_values[i] = this.widgets[i].value; + else + o.widgets_values[i] = null; } } @@ -2366,6 +2390,18 @@ if( this.onPropertyChanged(name, value, prev_value) === false ) //abort change this.properties[name] = prev_value; } + if(this.widgets) //widgets could be linked to properties + for(var i = 0; i < this.widgets.length; ++i) + { + var w = this.widgets[i]; + if(!w) + continue; + if(w.options.property == name) + { + w.value = value; + break; + } + } }; // Execution ************************* @@ -3130,13 +3166,51 @@ }; /** - * Allows to pass + * returns all the info available about a property of this node. + * + * @method getPropertyInfo + * @param {String} property name of the property + * @return {Object} the object with all the available info + */ + LGraphNode.prototype.getPropertyInfo = function( property ) + { + var info = null; + + //there are several ways to define info about a property + //legacy mode + if (this.properties_info) { + for (var i = 0; i < this.properties_info.length; ++i) { + if (this.properties_info[i].name == property) { + info = this.properties_info[i]; + break; + } + } + } + //litescene mode using the constructor + if(this.constructor["@" + property]) + info = this.constructor["@" + property]; + + //litescene mode using the constructor + if (this.onGetPropertyInfo) { + info = this.onGetPropertyInfo(property); + } + + if (!info) + info = {}; + if(!info.type) + info.type = typeof this.properties[property]; + + return info; + } + + /** + * Defines a widget inside the node, it will be rendered on top of the node, you can control lots of properties * * @method addWidget * @param {String} type the widget type (could be "number","string","combo" * @param {String} name the text to show on the widget * @param {String} value the default value - * @param {Function} callback function to call when it changes (optionally, it can be the name of the property to modify) + * @param {Function|String} callback function to call when it changes (optionally, it can be the name of the property to modify) * @param {Object} options the object that contains special properties of this widget * @return {Object} the created widget object */ @@ -5452,16 +5526,8 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.active_canvas = this; //restore the mousemove event back to the canvas - document.removeEventListener( - "mousemove", - this._mousemove_callback, - true - ); - this.canvas.addEventListener( - "mousemove", - this._mousemove_callback, - true - ); + document.removeEventListener("mousemove",this._mousemove_callback,true); + this.canvas.addEventListener("mousemove",this._mousemove_callback,true); document.removeEventListener("mouseup", this._mouseup_callback, true); this.adjustMouseEvent(e); @@ -5470,6 +5536,12 @@ LGraphNode.prototype.executeAction = function(action) this.last_mouse_dragging = false; if (e.which == 1) { + + if( this.node_widget ) + { + this.processNodeWidgets( this.node_widget[0], this.canvas_mouse, e ); + } + //left button this.node_widget = null; @@ -8280,7 +8352,7 @@ LGraphNode.prototype.executeAction = function(action) for (var i = 0; i < node.widgets.length; ++i) { var w = node.widgets[i]; - if(w.disabled) + if(!w || w.disabled) continue; if ( w == active_widget || (x > 6 && x < width - 12 && y > w.last_y && y < w.last_y + LiteGraph.NODE_WIDGET_HEIGHT) ) { //inside widget @@ -8314,18 +8386,11 @@ LGraphNode.prototype.executeAction = function(action) case "combo": var old_value = w.value; if (event.type == "mousemove" && w.type == "number") { - w.value += - event.deltaX * 0.1 * (w.options.step || 1); - if ( - w.options.min != null && - w.value < w.options.min - ) { + w.value += event.deltaX * 0.1 * (w.options.step || 1); + if ( w.options.min != null && w.value < w.options.min ) { w.value = w.options.min; } - if ( - w.options.max != null && - w.value > w.options.max - ) { + if ( w.options.max != null && w.value > w.options.max ) { w.value = w.options.max; } } else if (event.type == "mousedown") { @@ -8337,38 +8402,33 @@ LGraphNode.prototype.executeAction = function(action) var delta = x < 40 ? -1 : x > width - 40 ? 1 : 0; if (w.type == "number") { w.value += delta * 0.1 * (w.options.step || 1); - if ( - w.options.min != null && - w.value < w.options.min - ) { + if ( w.options.min != null && w.value < w.options.min ) { w.value = w.options.min; } - if ( - w.options.max != null && - w.value > w.options.max - ) { + if ( w.options.max != null && w.value > w.options.max ) { w.value = w.options.max; } - } else if (delta) { - var index = values.indexOf(w.value) + delta; + } else if (delta) { //used for combos + var values_list = values.constructor === Array ? values : Object.keys(values); + var index = values_list.indexOf(w.value) + delta; if (index >= values.length) { index = 0; } if (index < 0) { - index = values.length - 1; + index = values_list.length - 1; } - w.value = values[index]; - } else { - var menu = new LiteGraph.ContextMenu( - values, - { + if( values.constructor === Array ) + w.value = values[index]; + else + w.value = values[ values_list[index] ]; + } else { //combo + var menu = new LiteGraph.ContextMenu(values,{ scale: Math.max(1, this.ds.scale), event: event, className: "dark", callback: inner_clicked.bind(w) }, - ref_window - ); + ref_window); function inner_clicked(v, option, event) { this.value = v; inner_value_change(this, v); @@ -8376,7 +8436,18 @@ LGraphNode.prototype.executeAction = function(action) return false; } } - } //mousedown + } //end mousedown + else if(event.type == "mouseup" && w.type == "number") + { + var delta = x < 40 ? -1 : x > width - 40 ? 1 : 0; + if (event.click_time < 200 && delta == 0) { + this.prompt("Value",w.value,function(v) { + this.value = Number(v); + inner_value_change(this, this.value); + }.bind(w), + event); + } + } if( old_value != w.value ) setTimeout( @@ -8398,15 +8469,11 @@ LGraphNode.prototype.executeAction = function(action) case "string": case "text": if (event.type == "mousedown") { - this.prompt( - "Value", - w.value, - function(v) { + this.prompt("Value",w.value,function(v) { this.value = v; inner_value_change(this, v); }.bind(w), - event - ); + event); } break; default: @@ -9421,11 +9488,7 @@ LGraphNode.prototype.executeAction = function(action) return dialog; }; - LGraphCanvas.prototype.showEditPropertyValue = function( - node, - property, - options - ) { + LGraphCanvas.prototype.showEditPropertyValue = function( node, property, options ) { if (!node || node.properties[property] === undefined) { return; } @@ -9433,28 +9496,8 @@ LGraphNode.prototype.executeAction = function(action) options = options || {}; var that = this; - var type = "string"; - - if (node.properties[property] !== null) { - type = typeof node.properties[property]; - } - - var info = null; - if (node.getPropertyInfo) { - info = node.getPropertyInfo(property); - } - if (node.properties_info) { - for (var i = 0; i < node.properties_info.length; ++i) { - if (node.properties_info[i].name == property) { - info = node.properties_info[i]; - break; - } - } - } - - if (info !== undefined && info !== null && info.type) { - type = info.type; - } + var info = node.getPropertyInfo(property); + var type = info.type; var input_html = ""; @@ -9548,9 +9591,13 @@ LGraphNode.prototype.executeAction = function(action) if (node.onPropertyChanged) { node.onPropertyChanged(property, value); } + if(options.onclose) + options.onclose(); dialog.close(); node.setDirtyCanvas(true, true); } + + return dialog; }; LGraphCanvas.prototype.createDialog = function(html, options) { diff --git a/src/nodes/.gltextures.js.swp b/src/nodes/.gltextures.js.swp deleted file mode 100644 index 5e286edeb1d9e899a3603182e7bbc1dbb89579f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40960 zcmeI53z%e8b?;ju#0~-hjPYU&XBwl`Gd)$^Jum2C2=nN9gn6aAhXKc?I9+|ZyJo8D zl=bMIVHl0^({S~Mh=|7A+=Q#B*CbwV)C=|R$r^lsZz0!pKFv$xoXWn z=oK3ET&dXbW@~PBj#!0U#h>=-x!F>~JKSj2yn6OvebMZ?1YHSqC154cY|N~@@Qi({ zhKB~_)dhXaoO93FXG6PxyAtS1peuo{1iBLFN}wx&t^~Rg=t|)K0|_*$13hn`oIhvE znm2#vEa65;PmgnvB|{#+vb zYbLyC(w{y>KiKsDIuZYYMEF-tc-6#TV+3O3et-t@A2!F(cA2jJdkqCd-gcnTs*lF?fzi7g3{eLVG z{+A})*6+)IJ|6!e6FzIwe^Vm-!9@ITC&IsA!b_IF7sb>6yb1SA_#KJx2TZt??*+Z_ z`1hM|E1x$f!vC)ccTM`=O@!ZP!mWJv{z5$c&zW%CYU=axM7XK$9-IDIiT-D*t4I0i zNa1yfa8n&Umzngxln6KC>?xb@oiEl8wtaur@b`KXetRPPE)#y#gkN|@JpN})xaI%W zM7R-GkFB4VoEeXAs;kHLziSfVpEl_iP5w_N!tXHQHvNgS;^}|NgxmVLEfM}n6J9at zpN}t8K2|=TFyXd+|864ub`x&-dpZ$r)J>pguR1%PzW&zzzbk>R1iBLFN}wx&t^~Rg z=t`g~fuF1d-07a4L(GM5l=(1oR_h0l`SBl__wSIf?x!n(t^~Rg=t`g~fvyC)66i{x zD}k;Ax)SJ0peuo{1Wu*|j`e36bH#f9`ZIbQKF6G#-V9%2)4Mb6%@ix%v<{Nso$Qm} zCi~>GHE*_9Z+Nxc?3JH5QuQ)@X19E<(46whZnbZ~*{Mi3>+h0-*#CWSf4eMnVgK9p z<0rBCKMOts-VQ3D1h#?GK`(fmHMoz01@KzX51wJ&?_1!j;IF`+f)9Z=fXhJ^ybyee z^~L+aec%RgF8Bs(mVX2O9L$5W!Dm<}Jp@*Pv%neP?^#UzFn9}a!3a1TJkBEGm%(Ge z1+N09f*-K>`3(3&FbURy^S~1z zwzq<}f%V`b;DDEabHJBzq+Aag;C%23@Iw{>e*nG;ZUt`y9#{`v03N3fq#kawi;f*z zl~jK8Q*i5^sox2GN+LTpx^H}RBy1~D@~N8V9x_cV`d*%CH~9uF$l2h`ma^M-Y}=mS zJ2tv`$M}vtyYpN3jP2SukrFe70g3v&^WV37e8;xkqg(QuuACTMTE08?>`!D{Unn*T zb55pA=jeBip3xJR%Dh{Pmb%g`mB=Yhqa-s|^B0^x-Hoi6XT8RRTa*8_+#T<5RnD+D zu2b`7yqZ@jc#^uDT`pg7}Xmie{fY_ZZGX17TC8=RSDrO+t)m5h!$;4~Bi15Qvr zTc^2Pv+hkh4d3CoO|{v8lR3|Es~ohc7G1vj%?7V6f6+o8OYmCrC_zOiTV$h(L4fnB zS2-28?AgBu1}B|m8=St8zJ6q(qodohCEuMk(#iU zIZ`W|-Acu8IFhz4&OjL7@n`JYx+8QmezP*2S|354R7qGVl9TWiguTFo^_05*s zOteI;)oB_^q#~6R;tjK-{H>%cS0elkP9|VZi;Gv=c4m#Fvokfnyl-r0rdcZuIDS80p>T9i6#7b$MMAZhJB zTB4X3b2*)|E=1VC7Uocpj$0}Pg%fRMvUXg;>J7ifyJ!)gNBNjQxy@2zB+;(7jBeey zZ|6k3+G`%VGfAli)qyzSTyb_TNhf1pf~@2(B&isw5<~^RdL&7;dC%S}6J&i=S2c*5 z-<+N6OGencaeO>Kv3+dMz8Dkn`rEZ<%V_@cjXU>^j#F(k`LIxNLL(fAhTAp#Hpt2(7hZA25DI@0jkCMOl3ir;}t8CUCcViNVMUae82$GN(>>rvVk zs-2jll}2Vm!u+(a#wy0^(h+Ct6=Bp|Zl>hUk~{|t0h$(Y-r2ZXR_ew(l5sCiXZxc2A6M8{0TB>g?Y!vE8|H&%QBd>)6I! zqx<)aT^22AQl&9aFl3B4M`=ydD6J8vuQ2Bo4o&%o`<%o`C0aN}X?TY)(gEXbO{ZGV z7b`P9GNB`>cHYv|Il^2Zyl+Blx4yQrZWJ3OsgEdSnCNWDpK?pt7!MG~zLRtqAyLH1 zNWSTewleRjTyDQ#J5<-YPo^>q#+d%n)3t)!?@f_|Iumc} zS!O7~tOw3)E_SL#oo8(EGdfTeTkQXrV`qNC*#EAvIsXUtz3dCffM>DmzXm=FUI$i# z?_t9~3hn}Tg6lyETnc^}Jb*p_-$4yr3SJ4$0AInTzX$vt_$}~yFbXaL>p(B~TWtKh z!F$0QK^dG0ejc0(9>LZZ`~L<|0lUB;I19W8Jb}&s0q_gpd)WEk29E-<|8D{}f&y3% z&Iiw7?>`Lg05^f_z%2L;&=15P@MCQLuY>;v{t^5B5%2)G4ZIy30n5O-;9hL~JHe;H zF>n+d0qemz;7sr>?EO2ye*xElw}4&XJJiFsfz-$U1RnnhP$9*WId= zUAF8V&exbIHtRHop+Th&I!3U{Nyiu4)o4Cvwm2{IO}D~qtbvEASaAGe$tkD~;#4ut z8iY$P=$JvE)GVit&PpN@6(!Z%o2Cj#-mBD$4JJiH1CB1!h_goN0uv*m0d$()bl*VG zQAGoUggazX644ZKvZIjNZW7n1mTR3shM7WdF6S6kZi?b8U+PDSObS7d?cltlvn~Xl zJColwnY&22q-4kXt9=WtxD?xuQFYnvEN9V>^3;%;^B;<8&LlfDVE7}vDQe*X@HDW(2QGPCJ);- z-3y8{=tl=1ResBku?hQn-Ycxu8ElaGRJG)7^2s=0-Nv8yi_=b-<|hi=L_TH{F=k91 zCI_>_YiyiemALeY80%8NkoD#~(($v{nj)sf39mV_G`hnJOQXAHA(WBgS7sHe61|d| zs=*q}Eq4eU&a6bdUa8?KIm<(T_VD2&M{J38fdes06sI{KO3VnXSt-u=wX&mT)$tm$ zDJ4mQ*3*tav6T$e42ZBzVJtzs&DqR!1E>veu{@dT2x6tSp|sf1I<~RX=n@$)upSZh zr}#E)P)C=c+J5>%>qLx|s+MD%E~>k|ZM3*7u6(zYuNIXk=GWfo3PX5l{TBahBk1vxD7XhyHe{7QxQ=EN218* zSlUYos4Fd#ZTOdC_QO>pW27G}Hwsnslfsk@ZkLG-k4^}(Q^m@3W;o(tu~gB&eP*g) ziq@AfBBP|HBz3Q`PY+$0xK-Ji)@G#VBuuX&Gn97468kP%BxOHEv$4;yYSslGYL_S_ z)f9ti6tRlVPS@Op2|qOB6a%fLejYWplp(4$LtAe7OY6qgJoFjh0qp+|fOmm+f+F}lyx$A%2JZ$p0`UzLz%Y=qy$n1_Iqw610{#F@ zfeqjl;7R-d4}mX$zW}mFpbz{I+y4>pzrYlDHFzobE`ESJ!A;;OD1uqA9$W;T!4L2> zcocjHxF8Q!fTywlp8|gZUJot-7lR@2G(Lbk!S8`da2|LWco09p=fS<;(-EIQv>m2B zYdeU^WHK;)WI-LZ>l-JKQZmoQllW=GV6i*#S)4EBUsS_yB5M5W@g&4Jeq4;ibFvF{ zC!=P~n%`ncod^TTI46(oj-pOx+m_f8wk2rWO>E5YZ>KauD*MF3X5!N}e#iPrB+`+# zo?S9^oUj)EdR(>mJfg~6w}VEKl-mm}lJAm;mX?&sF_O4?Y@0*d_LHV&bGiDVV%3={ zVJF~oh%0TGep-+21_z_+Xgoa0lKI}2v-af;cupqSJ}phTSh%8UOB7_Qm2EQfQ{_kvs!t7n7|u4Rxc~-7kccru=tDXek)BD%ym1} zR9;in5117NM^;6Z0FN(Li#g(qcQAkA#!ZLDGr$_?*v2h8_KlAi2C><<;<-Gzmf%! zNXjf(>IEXxbetS6efvr^ER|juUv>;G;3L2-I$L7(6py!EJyl6d0|+t?*G&qn*VkOb zuSznP6aDGb{j%36(qu3fy=m67SiaI_DfoC{)b2?w4M{oZ2q{7d!b}TM-Lp4PFO)i= zxr~>_j9k6FM@2N0G^V{y?%85gEO+@`TTd)diQW7qo)0ZJRcR z3;Vp-xnuX}#<6I#77gdHjZXgR^kqR^Qd4aw2-3>NhM8|Ro@8u;)U#>+A`_K?=>Rc( zYAV^*FN^VYw$ZT|&yu{U$7M<~#ky9_oBc|K=A?S2$-qVq=&`EgS!XQ!G*t>HFtr+~ z-Fm9Nq^WeOU;1jpm$GF2%IIMa*QD$du`8CcD!E7kR__t(fT%pN|G$Yny8;_n?ElNm zV)NtJ^>=~yfj5CEup6ucE5OUZIpANg^B)3#25tm-Fa}1!li2q+fH#3_!Cr6y_y)H9 zL*SF(R`93b7H}Ad{eL=m7F+*u@Kx|IcmTW?$hm+u;FUmp0;ho=;RARE+zxz>ko}GvHxxD|k1kfrFp`CcrQdx&0#e z8oq-2!S919a5b0!zXIf}K`;0ga{dDN9k2~pKf>qn7knK2HuxI3K(MoC(eVi+_51&2nIUmKQ_E zP6d~Ld6*T*@^G+J$@d(-IqS549x-D)EG5em%Ikz8spWCuZ)vfYMx_!>h|kD?_3Y(rfoV=&p+r z=>EJ!G8xl!yFKm*fu1muJZVidy=E5YuZ7~oYDRCM37I(2R9e_sg5qtB)B0u+^$oir z?~DM^zSbdVk+Hxe8T6jSRwcDgTxN6E5NpM@=l3%IWwXm{*_EM4KiDvFg_xj=*_CAJ zFdi-QLA;@CjXgZ|q3o>wGiB4TltVJmNim1)xHcr?8Vh!YqD(+!Tw79Eu(MBES@g1a zD2YK`qCs7v!E8&325pH3ZHWe@M4@QQ1Vl=tGYG{<^dWD>uq}VGz(Fnc)>k_h5XU3#%RsSPyi z)gx*w?WO3)7Rptzm!`3+lt-MudwO_P$sSrs$Wvmxu zg)=05KKeCKx$r5Q6eT0QU)3h20<4-K7-weajf@e^sI9QF)wLxN4x7~F?v}O^jmyUxi_q75fnTzif=ezs9!zB)AzI1$7|j|0ltEuo|2VWZnN^@DcDq za5MO?;5}dm*apr5-^T9$d+=%SF7O**E7$_g1%H9fe+Re`90YSf_5k#Q(}4H`{t0{s z+y~^2fRBLR1zGShZ2McmTfiycer)*ngZF`BAP+WzUj|QOughHlw}T-d`yVnu?g@Ar zJO$nec7tK?67a9s>yLp?fe(TkKod-YOTn*!Uj?UtN3q@S0=I)d2EPxk1)D$)yb7ES zWr2vVEN=tm*Z zK`mS~+$0zBrVSU0GX^{*JmKtkc_Qs4@Ko^Y3CYMC%TqM}6O#<7wQ|vJXi;?*AGxY? zMN{zjd#_qAmVE0Gh=)~EW7vKXr;WD$cu2ItrZ7futgYI};eg7VLl{wUJFE@IefL*v z@we&YLfOO9{%m3su~{AF6j5w>c$Ndt%_+4mJmwxL`<3Zkg>B7Z&C6}uyKm(t_7hL9 z+#!c|SMII(awz~C1y)Y@eyPs7mp_#&yK)B*HwI1R7rd$2Qm$U86|4N9uAG$N7@=Ar zIIb1u@SsuKC1%7kg3YTcU_}2Ja1M^sW>x2TEGVZhC&K`JBe^Cfja0@0S*tPprcl`?Ybiykw4@ZIBPXSkp>p?*S+7<@ zMJt7F&fyy6^IHCnw8gw?jC%cBG10cwMky&meBbSHEObXQZ|w-WQMIgPN8&`;uq$jz zoUw(wkim)nR~IMk3ZWXKT1!Y}Nt>~&i$Ck7$w&hq z+SR&ByINPZx?05-qMXO8m{F-k(N)aa%d3J39^R6ut5r`RdOMe8_$N}9VO^GCU6x^6 z7P0^LU@)!_yAk`pYK+If#J0Z&$Q^)h16P4x2OGc-vFCpPz6JgO$lZUJfnNouf`7%H z|33IX;N##AK^4gU|CfUogD+sye*|0)Hi0Y{0MB8|%RPXy)?WsffaM?q{tdfcemCuZ zgX=&ZOoENzKY^#P?d5KOw}ETGVXzx)0+)i9gQu|b9|a!;H-brU3HTN8ee8TW2k=dB zFSrA|2fQ6z2WG%puoC%J!CS!9;8Gy= z|8v;*{{X%Y?g8%yZwB)FfaBnm;5qF5C%~J4+#@g!E(1Hj3&APipQgDZg4<+Gd- z_(9~HK&ww6x}Q>wHJqY1hvPAi=%Ia;W_il1^~nGoC6?#oijW{KRwQvVNXJsQ`&f)} z7(HC|IruNv+{JdgvL&Y|$HK*oGXw+96x;f%C3bmnO$P7X%Cy{s#g}@sDhJ&-;WeY~ z;%hjwZn^9_oYG=nj|{1{aN(}9_E%|^xQk%saG<|-@^$MKA|W+0A5$ueAv&qRYm$(iW~@pgba#=KO$@T`L-g+a9L% z`{fP3H}m$eeR%W|t|WJHmq%F82jb;)zuFvEYnJo=%nbWZ+1eG0YixW7)l-HAisfvf zEfn)Kle}ym%g2ptSTwl=o*{OB?!3Po<(aM$Iw><217;x)2i5+zta zGvPYzNJT+3oDHcgC&AcYrg=`)U&zQ_WpkbuUUI~XoT&}UCo!ySO8x+p17c$hWu>Z= zOnJ3(VZh;^c_J*VG|i&41#Z2eLaUN5_r}r`RD|4GQgqh901w=a&=OS{RFZPZPo`Ru zI}ET)tC_~YV0KL;s;YQ64`QenEv->AN1F^*@Pcx;h^+3?d@~K4L9RG~GE@2~%v2|4 z3TIMgN21&)=D7A@YUz1VX32tOWbruB1+^P0Dk7bDAXt;>lCmh2xV}ME0)0Z&L`3vK zPtz%?q6dlFMrHrDNJ`%Zvcf6IDz0b{{wWE(EY9Y{`!`1}aZu1TWWhSDTc;tk5G$ZUqPwpv?#|p@d&(iE!H{Y(k zwhgu7-tJV_U(#rosnR4vq)y}MN?o=scuPH|To<5MS`xidl})ZYPSI&ChlXSGb%`k- z)sb!2+Ec@SE!Sio$@F;@(Jj+`atp8Mgw(V9Dt4qRhi-U@6mutJV>fB03oI@&)iG2K zsoVPPS+Ru6EiDbU`djLFu&$L3oBy?t?ER=#fh@A&Urqsr`6S06U6Ia#1A&OKyrx(z z@Z3t@q}~5NjB)r4u@$lZJ!2?-3_Je=;1(e3|E~eE|9>Yq1$+uS{}bSy;2od@#=%%sT2>u&)!0kQXA1LVB_cd*|-0c5}aUN8t=4DP~a ze;*KA{*6G^`0HR0oCm&-&Hey*510l+Kz;=2Q+2AxFw)|JH-QNvv0u^BQ@JszX3Lfcb(_1S)+LWTt)wz^rwapwukz3KM z1)jVUQxXX}TeJZF=?OJ;c!~h9in5HguuzPzurOQ^N)wBeX7EW9iCO zz+&bXxRalwMO;hh;;ztVn8XfGEqLI=6+v*Jt`LT#5f&wOeCC08S`Sm|c>|7J<+lw} zlIY3aDDNUdjH@gzERM$(>LB%$q0cBy*2uUSH4&cBmBM*EaYYchHqmc3+Bs!}4@++R zLo-5)v76={N{}wu*`pg~($UjyX|!I&$TE8Q=XiI~ ze8_D2$Wr6znFrLix+YVTR&$OHRCXK*%Mxq6T6UZs4G*WGRP^z4 z*YfG~3nmh-T4H>%f;U9H&CeI7JwL1d->!D4w42EmeU){xBtTdCqa-gj*LiE#80zZ2IX%`1Hf5leiB5b5SbHV3dlb5ycAjP zXHLl>r3*``f;5R0Yuk(ex`%KnqU^+snh%G)N1~+gmgPqr)D8C=*w?PY&5H!nX;GV1 zxoutT4AGqEHXc`KlCOMH{S6W@W3EaM4&}yq783c;7$S#xQ&dU{hX~e~rq<$8EqTDv zor^QW@w{~FOg#szBBq=H)+r@Sw-#$i^?KXjkZe3#A=l}|+fP(kB&tKAXqI9vPRH0U zJ-Q{Auz;jrRj%{5XZ z!jIf?m+Uw4KAw7ns{y$J#HwB;E#wfBU7C#L2R%d+@#8YG^DNG-YRZx!Eo`=wU{K!6 z`T3R_mL%CJkE6nOX65JW%zfg~79%A|c9gcgArP4QSq%2il}c_^e)&pn15j=Y-8SSz z&@gvswD>p{YvEg{txf#u#Vo(Lg4GeRl_+FFzbD&zqy|I{>R%U=;@OWTNc2%l7?*cS e--wOP`YaJv0lf121<_Gizmquu-L9(Y Date: Tue, 24 Mar 2020 23:39:17 +0100 Subject: [PATCH 08/63] Documentation mismatch Some method names didn't match the actual method names, rendering the automatically generated documentation misleading --- src/litegraph.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/litegraph.js b/src/litegraph.js index cfa3c7679..05fcf5fb7 100755 --- a/src/litegraph.js +++ b/src/litegraph.js @@ -1477,7 +1477,7 @@ /** * Tell this graph it has a global graph input of this type - * @method addGlobalInput + * @method addInput * @param {String} name * @param {String} type * @param {*} value [optional] @@ -1503,7 +1503,7 @@ /** * Assign a data to the global graph input - * @method setGlobalInputData + * @method setInputData * @param {String} name * @param {*} data */ From e8eda9d30fd8a36db43ae90910a62ecbd99bd780 Mon Sep 17 00:00:00 2001 From: tamat Date: Wed, 25 Mar 2020 13:13:58 +0100 Subject: [PATCH 09/63] fixes in audio --- build/litegraph.js | 82 +-- build/litegraph.min.js | 1092 ++++++++++++++++++++-------------------- package.json | 4 +- src/litegraph.js | 26 +- src/nodes/audio.js | 5 +- src/nodes/graphics.js | 48 +- 6 files changed, 648 insertions(+), 609 deletions(-) diff --git a/build/litegraph.js b/build/litegraph.js index 1e2be9d25..0a3385f9a 100644 --- a/build/litegraph.js +++ b/build/litegraph.js @@ -165,7 +165,8 @@ get: function(v) { return this._shape; }, - enumerable: true + enumerable: true, + configurable: true }); //warnings @@ -7539,7 +7540,7 @@ LGraphNode.prototype.executeAction = function(action) } if (!low_quality) { ctx.font = this.title_text_font; - var title = node.getTitle(); + var title = String(node.getTitle()); if (title) { if (selected) { ctx.fillStyle = "white"; @@ -7549,11 +7550,11 @@ LGraphNode.prototype.executeAction = function(action) this.node_title_color; } if (node.flags.collapsed) { - ctx.textAlign = "center"; + ctx.textAlign = "left"; var measure = ctx.measureText(title); ctx.fillText( - title, - title_height + measure.width * 0.5, + title.substr(0,20), //avoid urls too long + title_height,// + measure.width * 0.5, LiteGraph.NODE_TITLE_TEXT_Y - title_height ); ctx.textAlign = "left"; @@ -8308,6 +8309,11 @@ LGraphNode.prototype.executeAction = function(action) ctx.rect( margin, posY, width - margin * 2, H ); ctx.fill(); if (show_text) { + ctx.save(); + ctx.beginPath(); + ctx.rect(margin, posY, width - margin * 2, H); + ctx.clip(); + ctx.stroke(); ctx.fillStyle = secondary_text_color; if (w.name != null) { @@ -8316,6 +8322,8 @@ LGraphNode.prototype.executeAction = function(action) ctx.fillStyle = text_color; ctx.textAlign = "right"; ctx.fillText(w.value, width - margin * 2, y + H * 0.7); + + ctx.restore(); } break; default: @@ -10290,11 +10298,12 @@ LGraphNode.prototype.executeAction = function(action) } } - if ( - options.event && - options.event.constructor !== MouseEvent && - options.event.constructor !== CustomEvent && - options.event.constructor !== PointerEvent + var eventClass = null; + if(options.event) //use strings because comparing classes between windows doesnt work + eventClass = options.event.constructor.name; + if ( eventClass !== "MouseEvent" && + eventClass !== "CustomEvent" && + eventClass !== "PointerEvent" ) { console.error( "Event passed to ContextMenu is not of type MouseEvent or CustomEvent. Ignoring it." @@ -15646,9 +15655,7 @@ if (typeof exports != "undefined") { if (callback) { callback(this); } - that.trace( - "Image loaded, size: " + that.img.width + "x" + that.img.height - ); + console.log( "Image loaded, size: " + that.img.width + "x" + that.img.height ); this.dirty = true; that.boxcolor = "#9F9"; that.setDirtyCanvas(true); @@ -15930,7 +15937,7 @@ if (typeof exports != "undefined") { if (name == "scale") { this.properties[name] = parseFloat(value); if (this.properties[name] == 0) { - this.trace("Error in scale"); + console.error("Error in scale"); this.properties[name] = 1.0; } } else { @@ -16100,10 +16107,23 @@ if (typeof exports != "undefined") { ImageVideo.prototype.loadVideo = function(url) { this._video_url = url; + var pos = url.substr(0,10).indexOf(":"); + var protocol = ""; + if(pos != -1) + protocol = url.substr(0,pos); + + var host = ""; + if(protocol) + { + host = url.substr(0,url.indexOf("/",protocol.length + 3)); + host = host.substr(protocol.length+3); + } + if ( this.properties.use_proxy && - url.substr(0, 4) == "http" && - LiteGraph.proxy + protocol && + LiteGraph.proxy && + host != location.host ) { url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); } @@ -16118,41 +16138,38 @@ if (typeof exports != "undefined") { var that = this; this._video.addEventListener("loadedmetadata", function(e) { //onload - that.trace("Duration: " + this.duration + " seconds"); - that.trace("Size: " + this.videoWidth + "," + this.videoHeight); + console.log("Duration: " + this.duration + " seconds"); + console.log("Size: " + this.videoWidth + "," + this.videoHeight); that.setDirtyCanvas(true); this.width = this.videoWidth; this.height = this.videoHeight; }); this._video.addEventListener("progress", function(e) { //onload - //that.trace("loading..."); + console.log("video loading..."); }); this._video.addEventListener("error", function(e) { - console.log("Error loading video: " + this.src); - that.trace("Error loading video: " + this.src); + console.error("Error loading video: " + this.src); if (this.error) { switch (this.error.code) { case this.error.MEDIA_ERR_ABORTED: - that.trace("You stopped the video."); + console.error("You stopped the video."); break; case this.error.MEDIA_ERR_NETWORK: - that.trace("Network error - please try again later."); + console.error("Network error - please try again later."); break; case this.error.MEDIA_ERR_DECODE: - that.trace("Video is broken.."); + console.error("Video is broken.."); break; case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED: - that.trace( - "Sorry, your browser can't play this video." - ); + console.error("Sorry, your browser can't play this video."); break; } } }); this._video.addEventListener("ended", function(e) { - that.trace("Ended."); + console.log("Video Ended."); this.play(); //loop }); @@ -16169,7 +16186,7 @@ if (typeof exports != "undefined") { }; ImageVideo.prototype.play = function() { - if (this._video) { + if (this._video && this._video.videoWidth ) { //is loaded this._video.play(); } }; @@ -16197,7 +16214,7 @@ if (typeof exports != "undefined") { if (!this._video) { return; } - this.trace("Video paused"); + console.log("Video paused"); this._video.pause(); }; @@ -26088,8 +26105,10 @@ function LGraphGeometryDisplace() { if (v === undefined) { continue; } - if (input.name == "gain") { + if (input.name == "gain") this.audionode.gain.value = v; + else if (input.name == "src") { + this.setProperty("src",v); } else if (input.name == "playbackRate") { this.properties.playbackRate = v; for (var j = 0; j < this._audionodes.length; ++j) { @@ -26201,6 +26220,7 @@ function LGraphGeometryDisplace() { LGAudioSource.prototype.onGetInputs = function() { return [ ["playbackRate", "number"], + ["src","string"], ["Play", LiteGraph.ACTION], ["Stop", LiteGraph.ACTION] ]; diff --git a/build/litegraph.min.js b/build/litegraph.min.js index 515983274..0b2a46791 100755 --- a/build/litegraph.min.js +++ b/build/litegraph.min.js @@ -1,426 +1,426 @@ -(function(y){function c(a){e.debug&&console.log("Graph created");this.list_of_graphcanvas=null;this.clear();a&&this.configure(a)}function m(a,b,d,h,f,e){this.id=a;this.type=b;this.origin_id=d;this.origin_slot=h;this.target_id=f;this.target_slot=e;this._data=null;this._pos=new Float32Array(2)}function n(a){this._ctor(a)}function k(a){this._ctor(a)}function x(a,b){this.offset=new Float32Array([0,0]);this.scale=1;this.max_scale=10;this.min_scale=0.1;this.onredraw=null;this.enabled=!0;this.last_mouse= -[0,0];this.element=null;this.visible_area=new Float32Array(4);a&&(this.element=a,b||this.bindEvents(a))}function g(a,b,d){d=d||{};this.background_image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; -a&&a.constructor===String&&(a=document.querySelector(a));this.ds=new x;this.zoom_modify_alpha=!0;this.title_text_font=""+e.NODE_TEXT_SIZE+"px Arial";this.inner_text_font="normal "+e.NODE_SUBTEXT_SIZE+"px Arial";this.node_title_color=e.NODE_TITLE_COLOR;this.default_link_color=e.LINK_COLOR;this.default_connection_color={input_off:"#778",input_on:"#7F7",output_off:"#778",output_on:"#7F7"};this.highquality_render=!0;this.use_gradients=!1;this.editor_alpha=1;this.pause_rendering=!1;this.clear_background= +(function(y){function c(a){g.debug&&console.log("Graph created");this.list_of_graphcanvas=null;this.clear();a&&this.configure(a)}function m(a,b,d,h,f,g){this.id=a;this.type=b;this.origin_id=d;this.origin_slot=h;this.target_id=f;this.target_slot=g;this._data=null;this._pos=new Float32Array(2)}function q(a){this._ctor(a)}function k(a){this._ctor(a)}function w(a,b){this.offset=new Float32Array([0,0]);this.scale=1;this.max_scale=10;this.min_scale=0.1;this.onredraw=null;this.enabled=!0;this.last_mouse= +[0,0];this.element=null;this.visible_area=new Float32Array(4);a&&(this.element=a,b||this.bindEvents(a))}function e(a,b,d){d=d||{};this.background_image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; +a&&a.constructor===String&&(a=document.querySelector(a));this.ds=new w;this.zoom_modify_alpha=!0;this.title_text_font=""+g.NODE_TEXT_SIZE+"px Arial";this.inner_text_font="normal "+g.NODE_SUBTEXT_SIZE+"px Arial";this.node_title_color=g.NODE_TITLE_COLOR;this.default_link_color=g.LINK_COLOR;this.default_connection_color={input_off:"#778",input_on:"#7F7",output_off:"#778",output_on:"#7F7"};this.highquality_render=!0;this.use_gradients=!1;this.editor_alpha=1;this.pause_rendering=!1;this.clear_background= !0;this.read_only=!1;this.render_only_selected=!0;this.live_mode=!1;this.allow_searchbox=this.allow_interaction=this.allow_dragnodes=this.allow_dragcanvas=this.show_info=!0;this.drag_mode=this.allow_reconnect_links=!1;this.filter=this.dragging_rectangle=null;this.always_render_background=!1;this.render_canvas_border=this.render_shadows=!0;this.render_connections_shadows=!1;this.render_connections_border=!0;this.render_connection_arrows=this.render_curved_connections=!1;this.render_collapsed_slots= -!0;this.render_execution_order=!1;this.render_link_tooltip=this.render_title_colored=!0;this.links_render_mode=e.SPLINE_LINK;this.canvas_mouse=[0,0];this.onSelectionChange=this.onNodeMoved=this.onDrawLinkTooltip=this.onDrawOverlay=this.onDrawForeground=this.onDrawBackground=this.onMouse=this.onSearchBoxSelection=this.onSearchBox=null;this.connections_width=3;this.round_radius=8;this.over_link_center=this.node_widget=this.current_node=null;this.last_mouse_position=[0,0];this.visible_area=this.ds.visible_area; -this.visible_links=[];b&&b.attachCanvas(this);this.setCanvas(a);this.clear();d.skip_render||this.startRendering();this.autoresize=d.autoresize}function C(a,b){return Math.sqrt((b[0]-a[0])*(b[0]-a[0])+(b[1]-a[1])*(b[1]-a[1]))}function z(a,b,d,h,f,e){return da&&hb?!0:!1}function v(a,b){var d=a[0]+a[2],h=a[1]+a[3],f=b[1]+b[3];return a[0]>b[0]+b[2]||a[1]>f||dc.width-l.width-10&&(e=c.width-l.width-10);r>c.height-l.height-10&&(r=c.height-l.height-10)}f.style.left=e+"px";f.style.top=r+"px";b.scale&&(f.style.transform="scale("+b.scale+")")}function B(a){this.points=a;this.nearest=this.selected=-1;this.size=null;this.must_update=!0;this.margin=5}var e=y.LiteGraph={VERSION:0.4,CANVAS_GRID_SIZE:10,NODE_TITLE_HEIGHT:30,NODE_TITLE_TEXT_Y:20,NODE_SLOT_HEIGHT:20,NODE_WIDGET_HEIGHT:20,NODE_WIDTH:140,NODE_MIN_WIDTH:50,NODE_COLLAPSED_RADIUS:10, +!0;this.render_execution_order=!1;this.render_link_tooltip=this.render_title_colored=!0;this.links_render_mode=g.SPLINE_LINK;this.canvas_mouse=[0,0];this.onSelectionChange=this.onNodeMoved=this.onDrawLinkTooltip=this.onDrawOverlay=this.onDrawForeground=this.onDrawBackground=this.onMouse=this.onSearchBoxSelection=this.onSearchBox=null;this.connections_width=3;this.round_radius=8;this.over_link_center=this.node_widget=this.current_node=null;this.last_mouse_position=[0,0];this.visible_area=this.ds.visible_area; +this.visible_links=[];b&&b.attachCanvas(this);this.setCanvas(a);this.clear();d.skip_render||this.startRendering();this.autoresize=d.autoresize}function C(a,b){return Math.sqrt((b[0]-a[0])*(b[0]-a[0])+(b[1]-a[1])*(b[1]-a[1]))}function z(a,b,d,h,f,g){return da&&hb?!0:!1}function u(a,b){var d=a[0]+a[2],h=a[1]+a[3],f=b[1]+b[3];return a[0]>b[0]+b[2]||a[1]>f||dc.width-l.width-10&&(f=c.width-l.width-10);r>c.height-l.height-10&&(r=c.height-l.height-10)}g.style.left=f+"px";g.style.top=r+"px";b.scale&&(g.style.transform="scale("+b.scale+")")}function B(a){this.points=a;this.nearest=this.selected=-1;this.size=null;this.must_update=!0;this.margin=5}var g=y.LiteGraph={VERSION:0.4,CANVAS_GRID_SIZE:10,NODE_TITLE_HEIGHT:30,NODE_TITLE_TEXT_Y:20,NODE_SLOT_HEIGHT:20,NODE_WIDGET_HEIGHT:20,NODE_WIDTH:140,NODE_MIN_WIDTH:50,NODE_COLLAPSED_RADIUS:10, NODE_COLLAPSED_WIDTH:80,NODE_TITLE_COLOR:"#999",NODE_TEXT_SIZE:14,NODE_TEXT_COLOR:"#AAA",NODE_SUBTEXT_SIZE:12,NODE_DEFAULT_COLOR:"#333",NODE_DEFAULT_BGCOLOR:"#353535",NODE_DEFAULT_BOXCOLOR:"#666",NODE_DEFAULT_SHAPE:"box",DEFAULT_SHADOW_COLOR:"rgba(0,0,0,0.5)",DEFAULT_GROUP_FONT:24,WIDGET_BGCOLOR:"#222",WIDGET_OUTLINE_COLOR:"#666",WIDGET_TEXT_COLOR:"#DDD",WIDGET_SECONDARY_TEXT_COLOR:"#999",LINK_COLOR:"#9A9",EVENT_LINK_COLOR:"#A86",CONNECTING_LINK_COLOR:"#AFA",MAX_NUMBER_OF_NODES:1E3,DEFAULT_POSITION:[100, 100],VALID_SHAPES:["default","box","round","card"],BOX_SHAPE:1,ROUND_SHAPE:2,CIRCLE_SHAPE:3,CARD_SHAPE:4,ARROW_SHAPE:5,INPUT:1,OUTPUT:2,EVENT:-1,ACTION:-1,ALWAYS:0,ON_EVENT:1,NEVER:2,ON_TRIGGER:3,UP:1,DOWN:2,LEFT:3,RIGHT:4,CENTER:5,STRAIGHT_LINK:0,LINEAR_LINK:1,SPLINE_LINK:2,NORMAL_TITLE:0,NO_TITLE:1,TRANSPARENT_TITLE:2,AUTOHIDE_TITLE:3,proxy:null,node_images_path:"",debug:!1,catch_exceptions:!0,throw_errors:!0,allow_scripts:!1,registered_node_types:{},node_types_by_file_extension:{},Nodes:{},searchbox_extras:{}, -registerNodeType:function(a,b){if(!b.prototype)throw"Cannot register a simple object, it must be a class with a prototype";b.type=a;e.debug&&console.log("Node registered: "+a);a.split("/");var d=b.name,h=a.lastIndexOf("/");b.category=a.substr(0,h);b.title||(b.title=d);if(b.prototype)for(var f in n.prototype)b.prototype[f]||(b.prototype[f]=n.prototype[f]);if(h=this.registered_node_types[a])console.log("replacing node type: "+a);else if(Object.hasOwnProperty(b.prototype,"shape")||Object.defineProperty(b.prototype, -"shape",{set:function(a){switch(a){case "default":delete this._shape;break;case "box":this._shape=e.BOX_SHAPE;break;case "round":this._shape=e.ROUND_SHAPE;break;case "circle":this._shape=e.CIRCLE_SHAPE;break;case "card":this._shape=e.CARD_SHAPE;break;default:this._shape=a}},get:function(a){return this._shape},enumerable:!0}),b.prototype.onPropertyChange&&console.warn("LiteGraph node class "+a+" has onPropertyChange method, it must be called onPropertyChanged with d at the end"),b.supported_extensions)for(f in b.supported_extensions){var c= -b.supported_extensions[f];c&&c.constructor===String&&(this.node_types_by_file_extension[c.toLowerCase()]=b)}this.registered_node_types[a]=b;b.constructor.name&&(this.Nodes[d]=b);if(e.onNodeTypeRegistered)e.onNodeTypeRegistered(a,b);if(h&&e.onNodeTypeReplaced)e.onNodeTypeReplaced(a,b,h)},unregisterNodeType:function(a){var b=a.constructor===String?this.registered_node_types[a]:a;if(!b)throw"node type not found: "+a;delete this.registered_node_types[b.type];b.constructor.name&&delete this.Nodes[b.constructor.name]}, -wrapFunctionAsNode:function(a,b,d,h,f){for(var c=Array(b.length),r="",l=e.getParameterNames(b),g=0;gr&&(r=f.size[0]),l+=f.size[1]+a+e.NODE_TITLE_HEIGHT;b+=r+a}this.setDirtyCanvas(!0,!0)};c.prototype.getTime=function(){return this.globaltime}; -c.prototype.getFixedTime=function(){return this.fixedtime};c.prototype.getElapsedTime=function(){return this.elapsed_time};c.prototype.sendEventToAllNodes=function(a,b,d){d=d||e.ALWAYS;var h=this._nodes_in_order?this._nodes_in_order:this._nodes;if(h)for(var f=0,c=h.length;f=e.MAX_NUMBER_OF_NODES)throw"LiteGraph: max number of nodes in a graph reached"; -null==a.id||-1==a.id?a.id=++this.last_node_id:this.last_node_idr&&(r=f.size[0]),l+=f.size[1]+a+g.NODE_TITLE_HEIGHT;b+=r+a}this.setDirtyCanvas(!0,!0)};c.prototype.getTime=function(){return this.globaltime}; +c.prototype.getFixedTime=function(){return this.fixedtime};c.prototype.getElapsedTime=function(){return this.elapsed_time};c.prototype.sendEventToAllNodes=function(a,b,d){d=d||g.ALWAYS;var h=this._nodes_in_order?this._nodes_in_order:this._nodes;if(h)for(var f=0,c=h.length;f=g.MAX_NUMBER_OF_NODES)throw"LiteGraph: max number of nodes in a graph reached"; +null==a.id||-1==a.id?a.id=++this.last_node_id:this.last_node_ida.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});this.id=-1;this.type=null;this.inputs=[];this.outputs=[];this.connections=[];this.properties={};this.properties_info=[];this.flags={}};n.prototype.configure=function(a){this.graph&&this.graph._version++; -for(var b in a)if("properties"==b)for(var d in a.properties){if(this.properties[d]=a.properties[d],this.onPropertyChanged)this.onPropertyChanged(d,a.properties[d])}else null!=a[b]&&("object"==typeof a[b]?this[b]&&this[b].configure?this[b].configure(a[b]):this[b]=e.cloneObject(a[b],this[b]):this[b]=a[b]);a.title||(this.title=this.constructor.title);if(this.onConnectionsChange){if(this.inputs)for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d._data=b,this.outputs[a].links))for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d.type=b,this.outputs[a].links))for(d=0;d=this.inputs.length||null==this.inputs[a].link)){var d=this.graph.links[this.inputs[a].link];if(!d)return null;if(!b)return d.data; -var h=this.graph.getNodeById(d.origin_id);if(!h)return d.data;if(h.updateOutputData)h.updateOutputData(d.origin_slot);else if(h.onExecute)h.onExecute();return d.data}};n.prototype.getInputDataType=function(a){if(!this.inputs||a>=this.inputs.length||null==this.inputs[a].link)return null;a=this.graph.links[this.inputs[a].link];if(!a)return null;var b=this.graph.getNodeById(a.origin_id);return b?(a=b.outputs[a.origin_slot])?a.type:null:a.type};n.prototype.getInputDataByName=function(a,b){var d=this.findInputSlot(a); -return-1==d?null:this.getInputData(d,b)};n.prototype.isInputConnected=function(a){return this.inputs?a=this.inputs.length)return null;a=this.inputs[a];return a&&null!==a.link?(a=this.graph.links[a.link])?this.graph.getNodeById(a.origin_id):null:null};n.prototype.getInputOrProperty=function(a){if(!this.inputs|| -!this.inputs.length)return this.properties?this.properties[a]:null;for(var b=0,d=this.inputs.length;b=this.outputs.length?null:this.outputs[a]._data};n.prototype.getOutputInfo=function(a){return this.outputs?a=this.outputs.length)return null;a=this.outputs[a];if(!a.links||0==a.links.length)return null;for(var b=[],d=0;da&&this.pos[1]-f-db)return!0;return!1};n.prototype.getSlotInPosition=function(a,b){var d=new Float32Array(2); -if(this.inputs)for(var h=0,f=this.inputs.length;h=this.outputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),null;b&&b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"target node is null";if(b==this)return null;if(d.constructor===String){if(d=b.findInputSlot(d),-1==d)return e.debug&&console.log("Connect: Error, no slot of name "+d),null}else{if(d===e.EVENT)return null;if(!b.inputs||d>=b.inputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),null}null!=b.inputs[d].link&& -b.disconnectInput(d);var h=this.outputs[a];if(b.onConnectInput&&!1===b.onConnectInput(d,h.type,h))return null;var f=b.inputs[d],c=null;if(e.isValidConnection(h.type,f.type)){c=new m(++this.graph.last_link_id,f.type,this.id,a,b.id,d);this.graph.links[c.id]=c;null==h.links&&(h.links=[]);h.links.push(c.id);b.inputs[d].link=c.id;this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(e.OUTPUT,a,!0,c,h);if(b.onConnectionsChange)b.onConnectionsChange(e.INPUT,d,!0,c,f);this.graph&& -this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(e.INPUT,b,d,this,a),this.graph.onNodeConnectionChange(e.OUTPUT,this,a,b,d))}this.setDirtyCanvas(!1,!0);this.graph.connectionChange(this,c);return c};n.prototype.disconnectOutput=function(a,b){if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return e.debug&&console.log("Connect: Error, no slot of name "+a),!1}else if(!this.outputs||a>=this.outputs.length)return e.debug&&console.log("Connect: Error, slot number not found"), -!1;var d=this.outputs[a];if(!d||!d.links||0==d.links.length)return!1;if(b){b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"Target Node not found";for(var h=0,f=d.links.length;h=this.inputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),!1;var b=this.inputs[a];if(!b)return!1;var d=this.inputs[a].link;this.inputs[a].link=null;var h=this.graph.links[d];if(h){var f=this.graph.getNodeById(h.origin_id);if(!f)return!1;var c=f.outputs[h.origin_slot];if(!c||!c.links||0==c.links.length)return!1;for(var r=0,l=c.links.length;r< -l;r++)if(c.links[r]==d){c.links.splice(r,1);break}delete this.graph.links[d];this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(e.INPUT,a,!1,h,b);if(f.onConnectionsChange)f.onConnectionsChange(e.OUTPUT,r,!1,h,c);this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(e.OUTPUT,f,r),this.graph.onNodeConnectionChange(e.INPUT,this,a))}this.setDirtyCanvas(!1,!0);this.graph.connectionChange(this);return!0};n.prototype.getConnectionPos=function(a, -b,d){d=d||new Float32Array(2);var h=0;a&&this.inputs&&(h=this.inputs.length);!a&&this.outputs&&(h=this.outputs.length);var f=0.5*e.NODE_SLOT_HEIGHT;if(this.flags.collapsed)return b=this._collapsed_width||e.NODE_COLLAPSED_WIDTH,this.horizontal?(d[0]=this.pos[0]+0.5*b,d[1]=a?this.pos[1]-e.NODE_TITLE_HEIGHT:this.pos[1]):(d[0]=a?this.pos[0]:this.pos[0]+b,d[1]=this.pos[1]-0.5*e.NODE_TITLE_HEIGHT),d;if(a&&-1==b)return d[0]=this.pos[0]+0.5*e.NODE_TITLE_HEIGHT,d[1]=this.pos[1]+0.5*e.NODE_TITLE_HEIGHT,d;if(a&& -h>b&&this.inputs[b].pos)return d[0]=this.pos[0]+this.inputs[b].pos[0],d[1]=this.pos[1]+this.inputs[b].pos[1],d;if(!a&&h>b&&this.outputs[b].pos)return d[0]=this.pos[0]+this.outputs[b].pos[0],d[1]=this.pos[1]+this.outputs[b].pos[1],d;if(this.horizontal)return d[0]=this.pos[0]+this.size[0]/h*(b+0.5),d[1]=a?this.pos[1]-e.NODE_TITLE_HEIGHT:this.pos[1]+this.size[1],d;d[0]=a?this.pos[0]+f:this.pos[0]+this.size[0]+1-f;d[1]=this.pos[1]+(b+0.7)*e.NODE_SLOT_HEIGHT+(this.constructor.slot_start_y||0);return d}; -n.prototype.alignToGrid=function(){this.pos[0]=e.CANVAS_GRID_SIZE*Math.round(this.pos[0]/e.CANVAS_GRID_SIZE);this.pos[1]=e.CANVAS_GRID_SIZE*Math.round(this.pos[1]/e.CANVAS_GRID_SIZE)};n.prototype.trace=function(a){this.console||(this.console=[]);this.console.push(a);this.console.length>n.MAX_CONSOLE&&this.console.shift();this.graph.onNodeTrace(this,a)};n.prototype.setDirtyCanvas=function(a,b){this.graph&&this.graph.sendActionToCanvas("setDirty",[a,b])};n.prototype.loadImage=function(a){var b=new Image; -b.src=e.node_images_path+a;b.ready=!1;var d=this;b.onload=function(){this.ready=!0;d.setDirtyCanvas(!0)};return b};n.prototype.captureInput=function(a){if(this.graph&&this.graph.list_of_graphcanvas)for(var b=this.graph.list_of_graphcanvas,d=0;da.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});this.id=-1;this.type=null;this.inputs=[];this.outputs=[];this.connections=[];this.properties={};this.properties_info=[];this.flags={}};q.prototype.configure=function(a){this.graph&&this.graph._version++; +for(var b in a)if("properties"==b)for(var d in a.properties){if(this.properties[d]=a.properties[d],this.onPropertyChanged)this.onPropertyChanged(d,a.properties[d])}else null!=a[b]&&("object"==typeof a[b]?this[b]&&this[b].configure?this[b].configure(a[b]):this[b]=g.cloneObject(a[b],this[b]):this[b]=a[b]);a.title||(this.title=this.constructor.title);if(this.onConnectionsChange){if(this.inputs)for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d._data=b,this.outputs[a].links))for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d.type=b,this.outputs[a].links))for(d=0;d=this.inputs.length||null==this.inputs[a].link)){var d=this.graph.links[this.inputs[a].link];if(!d)return null;if(!b)return d.data; +var h=this.graph.getNodeById(d.origin_id);if(!h)return d.data;if(h.updateOutputData)h.updateOutputData(d.origin_slot);else if(h.onExecute)h.onExecute();return d.data}};q.prototype.getInputDataType=function(a){if(!this.inputs||a>=this.inputs.length||null==this.inputs[a].link)return null;a=this.graph.links[this.inputs[a].link];if(!a)return null;var b=this.graph.getNodeById(a.origin_id);return b?(a=b.outputs[a.origin_slot])?a.type:null:a.type};q.prototype.getInputDataByName=function(a,b){var d=this.findInputSlot(a); +return-1==d?null:this.getInputData(d,b)};q.prototype.isInputConnected=function(a){return this.inputs?a=this.inputs.length)return null;a=this.inputs[a];return a&&null!==a.link?(a=this.graph.links[a.link])?this.graph.getNodeById(a.origin_id):null:null};q.prototype.getInputOrProperty=function(a){if(!this.inputs|| +!this.inputs.length)return this.properties?this.properties[a]:null;for(var b=0,d=this.inputs.length;b=this.outputs.length?null:this.outputs[a]._data};q.prototype.getOutputInfo=function(a){return this.outputs?a=this.outputs.length)return null;a=this.outputs[a];if(!a.links||0==a.links.length)return null;for(var b=[],d=0;da&&this.pos[1]-f-db)return!0;return!1};q.prototype.getSlotInPosition=function(a,b){var d=new Float32Array(2); +if(this.inputs)for(var h=0,f=this.inputs.length;h=this.outputs.length)return g.debug&&console.log("Connect: Error, slot number not found"),null;b&&b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"target node is null";if(b==this)return null;if(d.constructor===String){if(d=b.findInputSlot(d),-1==d)return g.debug&&console.log("Connect: Error, no slot of name "+d),null}else{if(d===g.EVENT)return null;if(!b.inputs||d>=b.inputs.length)return g.debug&&console.log("Connect: Error, slot number not found"),null}null!=b.inputs[d].link&& +b.disconnectInput(d);var h=this.outputs[a];if(b.onConnectInput&&!1===b.onConnectInput(d,h.type,h))return null;var f=b.inputs[d],c=null;if(g.isValidConnection(h.type,f.type)){c=new m(++this.graph.last_link_id,f.type,this.id,a,b.id,d);this.graph.links[c.id]=c;null==h.links&&(h.links=[]);h.links.push(c.id);b.inputs[d].link=c.id;this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(g.OUTPUT,a,!0,c,h);if(b.onConnectionsChange)b.onConnectionsChange(g.INPUT,d,!0,c,f);this.graph&& +this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(g.INPUT,b,d,this,a),this.graph.onNodeConnectionChange(g.OUTPUT,this,a,b,d))}this.setDirtyCanvas(!1,!0);this.graph.connectionChange(this,c);return c};q.prototype.disconnectOutput=function(a,b){if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return g.debug&&console.log("Connect: Error, no slot of name "+a),!1}else if(!this.outputs||a>=this.outputs.length)return g.debug&&console.log("Connect: Error, slot number not found"), +!1;var d=this.outputs[a];if(!d||!d.links||0==d.links.length)return!1;if(b){b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"Target Node not found";for(var h=0,f=d.links.length;h=this.inputs.length)return g.debug&&console.log("Connect: Error, slot number not found"),!1;var b=this.inputs[a];if(!b)return!1;var d=this.inputs[a].link;this.inputs[a].link=null;var h=this.graph.links[d];if(h){var f=this.graph.getNodeById(h.origin_id);if(!f)return!1;var c=f.outputs[h.origin_slot];if(!c||!c.links||0==c.links.length)return!1;for(var r=0,l=c.links.length;r< +l;r++)if(c.links[r]==d){c.links.splice(r,1);break}delete this.graph.links[d];this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(g.INPUT,a,!1,h,b);if(f.onConnectionsChange)f.onConnectionsChange(g.OUTPUT,r,!1,h,c);this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(g.OUTPUT,f,r),this.graph.onNodeConnectionChange(g.INPUT,this,a))}this.setDirtyCanvas(!1,!0);this.graph.connectionChange(this);return!0};q.prototype.getConnectionPos=function(a, +b,d){d=d||new Float32Array(2);var h=0;a&&this.inputs&&(h=this.inputs.length);!a&&this.outputs&&(h=this.outputs.length);var f=0.5*g.NODE_SLOT_HEIGHT;if(this.flags.collapsed)return b=this._collapsed_width||g.NODE_COLLAPSED_WIDTH,this.horizontal?(d[0]=this.pos[0]+0.5*b,d[1]=a?this.pos[1]-g.NODE_TITLE_HEIGHT:this.pos[1]):(d[0]=a?this.pos[0]:this.pos[0]+b,d[1]=this.pos[1]-0.5*g.NODE_TITLE_HEIGHT),d;if(a&&-1==b)return d[0]=this.pos[0]+0.5*g.NODE_TITLE_HEIGHT,d[1]=this.pos[1]+0.5*g.NODE_TITLE_HEIGHT,d;if(a&& +h>b&&this.inputs[b].pos)return d[0]=this.pos[0]+this.inputs[b].pos[0],d[1]=this.pos[1]+this.inputs[b].pos[1],d;if(!a&&h>b&&this.outputs[b].pos)return d[0]=this.pos[0]+this.outputs[b].pos[0],d[1]=this.pos[1]+this.outputs[b].pos[1],d;if(this.horizontal)return d[0]=this.pos[0]+this.size[0]/h*(b+0.5),d[1]=a?this.pos[1]-g.NODE_TITLE_HEIGHT:this.pos[1]+this.size[1],d;d[0]=a?this.pos[0]+f:this.pos[0]+this.size[0]+1-f;d[1]=this.pos[1]+(b+0.7)*g.NODE_SLOT_HEIGHT+(this.constructor.slot_start_y||0);return d}; +q.prototype.alignToGrid=function(){this.pos[0]=g.CANVAS_GRID_SIZE*Math.round(this.pos[0]/g.CANVAS_GRID_SIZE);this.pos[1]=g.CANVAS_GRID_SIZE*Math.round(this.pos[1]/g.CANVAS_GRID_SIZE)};q.prototype.trace=function(a){this.console||(this.console=[]);this.console.push(a);this.console.length>q.MAX_CONSOLE&&this.console.shift();this.graph.onNodeTrace(this,a)};q.prototype.setDirtyCanvas=function(a,b){this.graph&&this.graph.sendActionToCanvas("setDirty",[a,b])};q.prototype.loadImage=function(a){var b=new Image; +b.src=g.node_images_path+a;b.ready=!1;var d=this;b.onload=function(){this.ready=!0;d.setDirtyCanvas(!0)};return b};q.prototype.captureInput=function(a){if(this.graph&&this.graph.list_of_graphcanvas)for(var b=this.graph.list_of_graphcanvas,d=0;da.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});Object.defineProperty(this,"size",{set:function(a){!a||2>a.length||(this._size[0]=Math.max(140,a[0]),this._size[1]=Math.max(80,a[1]))},get:function(){return this._size},enumerable:!0})};k.prototype.configure=function(a){this.title=a.title;this._bounding.set(a.bounding);this.color=a.color; this.font=a.font};k.prototype.serialize=function(){var a=this._bounding;return{title:this.title,bounding:[Math.round(a[0]),Math.round(a[1]),Math.round(a[2]),Math.round(a[3])],color:this.color,font:this.font}};k.prototype.move=function(a,b,d){this._pos[0]+=a;this._pos[1]+=b;if(!d)for(d=0;dthis.max_scale&&(a=this.max_scale);if(a!=this.scale&&this.element){var d=this.element.getBoundingClientRect();if(d){b=b||[0.5*d.width,0.5*d.height];d=this.convertCanvasToOffset(b);this.scale=a;0.01>Math.abs(this.scale-1)&&(this.scale=1);var h=this.convertCanvasToOffset(b),d=[h[0]-d[0],h[1]-d[1]];this.offset[0]+=d[0];this.offset[1]+=d[1];if(this.onredraw)this.onredraw(this)}}}; -x.prototype.changeDeltaScale=function(a,b){this.changeScale(this.scale*a,b)};x.prototype.reset=function(){this.scale=1;this.offset[0]=0;this.offset[1]=0};y.LGraphCanvas=e.LGraphCanvas=g;g.link_type_colors={"-1":e.EVENT_LINK_COLOR,number:"#AAA",node:"#DCA"};g.gradients={};g.prototype.clear=function(){this.fps=this.render_time=this.last_draw_time=this.frame=0;this.dragging_rectangle=null;this.selected_nodes={};this.selected_group=null;this.visible_nodes=[];this.connecting_node=this.node_capturing_input= -this.node_over=this.node_dragged=null;this.highlighted_links={};this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_widget=this.node_in_panel=this.dirty_area=null;this.last_mouse=[0,0];this.last_mouseclick=0;this.visible_area.set([0,0,0,0]);if(this.onClear)this.onClear()};g.prototype.setGraph=function(a,b){this.graph!=a&&(b||this.clear(),!a&&this.graph?this.graph.detachCanvas(this):(a.attachCanvas(this),this.setDirty(!0,!0)))};g.prototype.openSubgraph=function(a){if(!a)throw"graph cannot be null"; -if(this.graph==a)throw"graph cannot be the same";this.clear();this.graph&&(this._graph_stack||(this._graph_stack=[]),this._graph_stack.push(this.graph));a.attachCanvas(this);this.setDirty(!0,!0)};g.prototype.closeSubgraph=function(){if(this._graph_stack&&0!=this._graph_stack.length){var a=this.graph._subgraph_node,b=this._graph_stack.pop();this.selected_nodes={};this.highlighted_links={};b.attachCanvas(this);this.setDirty(!0,!0);a&&(this.centerOnNode(a),this.selectNodes([a]))}};g.prototype.getCurrentGraph= -function(){return this.graph};g.prototype.setCanvas=function(a,b){if(a&&a.constructor===String&&(a=document.getElementById(a),!a))throw"Error creating LiteGraph canvas: Canvas not found";if(a!==this.canvas&&(!a&&this.canvas&&(b||this.unbindEvents()),this.canvas=a,this.ds.element=a)){a.className+=" lgraphcanvas";a.data=this;a.tabindex="1";this.bgcanvas=null;this.bgcanvas||(this.bgcanvas=document.createElement("canvas"),this.bgcanvas.width=this.canvas.width,this.bgcanvas.height=this.canvas.height); -if(null==a.getContext){if("canvas"!=a.localName)throw"Element supplied for LGraphCanvas must be a element, you passed a "+a.localName;throw"This browser doesn't support Canvas";}null==(this.ctx=a.getContext("2d"))&&(a.webgl_enabled||console.warn("This canvas seems to be WebGL, enabling WebGL renderer"),this.enableWebGL());this._mousemove_callback=this.processMouseMove.bind(this);this._mouseup_callback=this.processMouseUp.bind(this);b||this.bindEvents()}};g.prototype._doNothing=function(a){a.preventDefault(); -return!1};g.prototype._doReturnTrue=function(a){a.preventDefault();return!0};g.prototype.bindEvents=function(){if(this._events_binded)console.warn("LGraphCanvas: events already binded");else{var a=this.canvas,b=this.getCanvasWindow().document;this._mousedown_callback=this.processMouseDown.bind(this);this._mousewheel_callback=this.processMouseWheel.bind(this);a.addEventListener("mousedown",this._mousedown_callback,!0);a.addEventListener("mousemove",this._mousemove_callback);a.addEventListener("mousewheel", +h;this.last_mouse[1]=d;a.preventDefault();a.stopPropagation();return!1}};w.prototype.toCanvasContext=function(a){a.scale(this.scale,this.scale);a.translate(this.offset[0],this.offset[1])};w.prototype.convertOffsetToCanvas=function(a){return[(a[0]+this.offset[0])*this.scale,(a[1]+this.offset[1])*this.scale]};w.prototype.convertCanvasToOffset=function(a,b){b=b||[0,0];b[0]=a[0]/this.scale-this.offset[0];b[1]=a[1]/this.scale-this.offset[1];return b};w.prototype.mouseDrag=function(a,b){this.offset[0]+= +a/this.scale;this.offset[1]+=b/this.scale;if(this.onredraw)this.onredraw(this)};w.prototype.changeScale=function(a,b){athis.max_scale&&(a=this.max_scale);if(a!=this.scale&&this.element){var d=this.element.getBoundingClientRect();if(d){b=b||[0.5*d.width,0.5*d.height];d=this.convertCanvasToOffset(b);this.scale=a;0.01>Math.abs(this.scale-1)&&(this.scale=1);var h=this.convertCanvasToOffset(b),d=[h[0]-d[0],h[1]-d[1]];this.offset[0]+=d[0];this.offset[1]+=d[1];if(this.onredraw)this.onredraw(this)}}}; +w.prototype.changeDeltaScale=function(a,b){this.changeScale(this.scale*a,b)};w.prototype.reset=function(){this.scale=1;this.offset[0]=0;this.offset[1]=0};y.LGraphCanvas=g.LGraphCanvas=e;e.link_type_colors={"-1":g.EVENT_LINK_COLOR,number:"#AAA",node:"#DCA"};e.gradients={};e.prototype.clear=function(){this.fps=this.render_time=this.last_draw_time=this.frame=0;this.dragging_rectangle=null;this.selected_nodes={};this.selected_group=null;this.visible_nodes=[];this.connecting_node=this.node_capturing_input= +this.node_over=this.node_dragged=null;this.highlighted_links={};this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_widget=this.node_in_panel=this.dirty_area=null;this.last_mouse=[0,0];this.last_mouseclick=0;this.visible_area.set([0,0,0,0]);if(this.onClear)this.onClear()};e.prototype.setGraph=function(a,b){this.graph!=a&&(b||this.clear(),!a&&this.graph?this.graph.detachCanvas(this):(a.attachCanvas(this),this.setDirty(!0,!0)))};e.prototype.openSubgraph=function(a){if(!a)throw"graph cannot be null"; +if(this.graph==a)throw"graph cannot be the same";this.clear();this.graph&&(this._graph_stack||(this._graph_stack=[]),this._graph_stack.push(this.graph));a.attachCanvas(this);this.setDirty(!0,!0)};e.prototype.closeSubgraph=function(){if(this._graph_stack&&0!=this._graph_stack.length){var a=this.graph._subgraph_node,b=this._graph_stack.pop();this.selected_nodes={};this.highlighted_links={};b.attachCanvas(this);this.setDirty(!0,!0);a&&(this.centerOnNode(a),this.selectNodes([a]))}};e.prototype.getCurrentGraph= +function(){return this.graph};e.prototype.setCanvas=function(a,b){if(a&&a.constructor===String&&(a=document.getElementById(a),!a))throw"Error creating LiteGraph canvas: Canvas not found";if(a!==this.canvas&&(!a&&this.canvas&&(b||this.unbindEvents()),this.canvas=a,this.ds.element=a)){a.className+=" lgraphcanvas";a.data=this;a.tabindex="1";this.bgcanvas=null;this.bgcanvas||(this.bgcanvas=document.createElement("canvas"),this.bgcanvas.width=this.canvas.width,this.bgcanvas.height=this.canvas.height); +if(null==a.getContext){if("canvas"!=a.localName)throw"Element supplied for LGraphCanvas must be a element, you passed a "+a.localName;throw"This browser doesn't support Canvas";}null==(this.ctx=a.getContext("2d"))&&(a.webgl_enabled||console.warn("This canvas seems to be WebGL, enabling WebGL renderer"),this.enableWebGL());this._mousemove_callback=this.processMouseMove.bind(this);this._mouseup_callback=this.processMouseUp.bind(this);b||this.bindEvents()}};e.prototype._doNothing=function(a){a.preventDefault(); +return!1};e.prototype._doReturnTrue=function(a){a.preventDefault();return!0};e.prototype.bindEvents=function(){if(this._events_binded)console.warn("LGraphCanvas: events already binded");else{var a=this.canvas,b=this.getCanvasWindow().document;this._mousedown_callback=this.processMouseDown.bind(this);this._mousewheel_callback=this.processMouseWheel.bind(this);a.addEventListener("mousedown",this._mousedown_callback,!0);a.addEventListener("mousemove",this._mousemove_callback);a.addEventListener("mousewheel", this._mousewheel_callback,!1);a.addEventListener("contextmenu",this._doNothing);a.addEventListener("DOMMouseScroll",this._mousewheel_callback,!1);a.addEventListener("touchstart",this.touchHandler,!0);a.addEventListener("touchmove",this.touchHandler,!0);a.addEventListener("touchend",this.touchHandler,!0);a.addEventListener("touchcancel",this.touchHandler,!0);this._key_callback=this.processKey.bind(this);a.addEventListener("keydown",this._key_callback,!0);b.addEventListener("keyup",this._key_callback, -!0);this._ondrop_callback=this.processDrop.bind(this);a.addEventListener("dragover",this._doNothing,!1);a.addEventListener("dragend",this._doNothing,!1);a.addEventListener("drop",this._ondrop_callback,!1);a.addEventListener("dragenter",this._doReturnTrue,!1);this._events_binded=!0}};g.prototype.unbindEvents=function(){if(this._events_binded){var a=this.getCanvasWindow().document;this.canvas.removeEventListener("mousedown",this._mousedown_callback);this.canvas.removeEventListener("mousewheel",this._mousewheel_callback); +!0);this._ondrop_callback=this.processDrop.bind(this);a.addEventListener("dragover",this._doNothing,!1);a.addEventListener("dragend",this._doNothing,!1);a.addEventListener("drop",this._ondrop_callback,!1);a.addEventListener("dragenter",this._doReturnTrue,!1);this._events_binded=!0}};e.prototype.unbindEvents=function(){if(this._events_binded){var a=this.getCanvasWindow().document;this.canvas.removeEventListener("mousedown",this._mousedown_callback);this.canvas.removeEventListener("mousewheel",this._mousewheel_callback); this.canvas.removeEventListener("DOMMouseScroll",this._mousewheel_callback);this.canvas.removeEventListener("keydown",this._key_callback);a.removeEventListener("keyup",this._key_callback);this.canvas.removeEventListener("contextmenu",this._doNothing);this.canvas.removeEventListener("drop",this._ondrop_callback);this.canvas.removeEventListener("dragenter",this._doReturnTrue);this.canvas.removeEventListener("touchstart",this.touchHandler);this.canvas.removeEventListener("touchmove",this.touchHandler); -this.canvas.removeEventListener("touchend",this.touchHandler);this.canvas.removeEventListener("touchcancel",this.touchHandler);this._ondrop_callback=this._key_callback=this._mousewheel_callback=this._mousedown_callback=null;this._events_binded=!1}else console.warn("LGraphCanvas: no events binded")};g.getFileExtension=function(a){var b=a.indexOf("?");-1!=b&&(a=a.substr(0,b));b=a.lastIndexOf(".");return-1==b?"":a.substr(b+1).toLowerCase()};g.prototype.enableWebGL=function(){if(void 0===typeof GL)throw"litegl.js must be included to use a WebGL canvas"; -if(void 0===typeof enableWebGLCanvas)throw"webglCanvas.js must be included to use this feature";this.gl=this.ctx=enableWebGLCanvas(this.canvas);this.ctx.webgl=!0;this.bgcanvas=this.canvas;this.bgctx=this.gl;this.canvas.webgl_enabled=!0};g.prototype.setDirty=function(a,b){a&&(this.dirty_canvas=!0);b&&(this.dirty_bgcanvas=!0)};g.prototype.getCanvasWindow=function(){if(!this.canvas)return window;var a=this.canvas.ownerDocument;return a.defaultView||a.parentWindow};g.prototype.startRendering=function(){function a(){this.pause_rendering|| -this.draw();var b=this.getCanvasWindow();this.is_rendering&&b.requestAnimationFrame(a.bind(this))}this.is_rendering||(this.is_rendering=!0,a.call(this))};g.prototype.stopRendering=function(){this.is_rendering=!1};g.prototype.processMouseDown=function(a){if(this.graph){this.adjustMouseEvent(a);var b=this.getCanvasWindow();g.active_canvas=this;this.canvas.removeEventListener("mousemove",this._mousemove_callback);b.document.addEventListener("mousemove",this._mousemove_callback,!0);b.document.addEventListener("mouseup", -this._mouseup_callback,!0);var d=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes,5),h=!1,f=300>e.getTime()-this.last_mouseclick;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;this.canvas.focus();e.closeAllContextMenus(b);if(!this.onMouse||!0!=this.onMouse(a)){if(1==a.which){a.ctrlKey&&(this.dragging_rectangle=new Float32Array(4),this.dragging_rectangle[0]=a.canvasX,this.dragging_rectangle[1]=a.canvasY,this.dragging_rectangle[2]=1,this.dragging_rectangle[3]=1,h=!0);var c= -!1;if(d&&this.allow_interaction&&!h&&!this.read_only){this.live_mode||d.flags.pinned||this.bringToFront(d);if(!this.connecting_node&&!d.flags.collapsed&&!this.live_mode)if(!h&&!1!==d.resizable&&z(a.canvasX,a.canvasY,d.pos[0]+d.size[0]-5,d.pos[1]+d.size[1]-5,10,10))this.resizing_node=d,this.canvas.style.cursor="se-resize",h=!0;else{if(d.outputs)for(var r=0,l=d.outputs.length;rg.getTime()-this.last_mouseclick;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;this.canvas.focus();g.closeAllContextMenus(b);if(!this.onMouse||!0!=this.onMouse(a)){if(1==a.which){a.ctrlKey&&(this.dragging_rectangle=new Float32Array(4),this.dragging_rectangle[0]=a.canvasX,this.dragging_rectangle[1]=a.canvasY,this.dragging_rectangle[2]=1,this.dragging_rectangle[3]=1,h=!0);var c= +!1;if(d&&this.allow_interaction&&!h&&!this.read_only){this.live_mode||d.flags.pinned||this.bringToFront(d);if(!this.connecting_node&&!d.flags.collapsed&&!this.live_mode)if(!h&&!1!==d.resizable&&z(a.canvasX,a.canvasY,d.pos[0]+d.size[0]-5,d.pos[1]+d.size[1]-5,10,10))this.resizing_node=d,this.canvas.style.cursor="se-resize",h=!0;else{if(d.outputs)for(var r=0,l=d.outputs.length;rc[0]+4||a.canvasYc[1]+4)){this.showLinkMenu(d, a);this.over_link_center=null;break}this.selected_group=this.graph.getGroupOnPos(a.canvasX,a.canvasY);this.selected_group_resizing=!1;this.selected_group&&!this.read_only&&(a.ctrlKey&&(this.dragging_rectangle=null),10>C([a.canvasX,a.canvasY],[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])*this.ds.scale?this.selected_group_resizing=!0:this.selected_group.recomputeInsideNodes());f&&!this.read_only&&this.allow_searchbox&&this.showSearchBox(a); -c=!0}!h&&c&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}else 2!=a.which&&3==a.which&&(this.read_only||this.processContextMenu(d,a));this.last_mouse[0]=a.localX;this.last_mouse[1]=a.localY;this.last_mouseclick=e.getTime();this.last_mouse_dragging=!0;this.graph.change();(!b.document.activeElement||"input"!=b.document.activeElement.nodeName.toLowerCase()&&"textarea"!=b.document.activeElement.nodeName.toLowerCase())&&a.preventDefault();a.stopPropagation();if(this.onMouseDown)this.onMouseDown(a); -return!1}}};g.prototype.processMouseMove=function(a){this.autoresize&&this.resize();if(this.graph){g.active_canvas=this;this.adjustMouseEvent(a);var b=[a.localX,a.localY],d=[b[0]-this.last_mouse[0],b[1]-this.last_mouse[1]];this.last_mouse=b;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;a.dragging=this.last_mouse_dragging;this.node_widget&&(this.processNodeWidgets(this.node_widget[0],this.canvas_mouse,a,this.node_widget[1]),this.dirty_canvas=!0);if(this.dragging_rectangle)this.dragging_rectangle[2]= +c=!0}!h&&c&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}else 2!=a.which&&3==a.which&&(this.read_only||this.processContextMenu(d,a));this.last_mouse[0]=a.localX;this.last_mouse[1]=a.localY;this.last_mouseclick=g.getTime();this.last_mouse_dragging=!0;this.graph.change();(!b.document.activeElement||"input"!=b.document.activeElement.nodeName.toLowerCase()&&"textarea"!=b.document.activeElement.nodeName.toLowerCase())&&a.preventDefault();a.stopPropagation();if(this.onMouseDown)this.onMouseDown(a); +return!1}}};e.prototype.processMouseMove=function(a){this.autoresize&&this.resize();if(this.graph){e.active_canvas=this;this.adjustMouseEvent(a);var b=[a.localX,a.localY],d=[b[0]-this.last_mouse[0],b[1]-this.last_mouse[1]];this.last_mouse=b;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;a.dragging=this.last_mouse_dragging;this.node_widget&&(this.processNodeWidgets(this.node_widget[0],this.canvas_mouse,a,this.node_widget[1]),this.dirty_canvas=!0);if(this.dragging_rectangle)this.dragging_rectangle[2]= a.canvasX-this.dragging_rectangle[0],this.dragging_rectangle[3]=a.canvasY-this.dragging_rectangle[1],this.dirty_canvas=!0;else if(this.selected_group&&!this.read_only)this.selected_group_resizing?this.selected_group.size=[a.canvasX-this.selected_group.pos[0],a.canvasY-this.selected_group.pos[1]]:(this.selected_group.move(d[0]/this.ds.scale,d[1]/this.ds.scale,a.ctrlKey),this.selected_group._nodes.length&&(this.dirty_canvas=!0)),this.dirty_bgcanvas=!0;else if(this.dragging_canvas)this.ds.offset[0]+= d[0]/this.ds.scale,this.ds.offset[1]+=d[1]/this.ds.scale,this.dirty_bgcanvas=this.dirty_canvas=!0;else if(this.allow_interaction&&!this.read_only){this.connecting_node&&(this.dirty_canvas=!0);for(var h=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes),b=0,f=this.graph._nodes.length;br[0]+4||a.canvasYr[1]+4)){f=c;break}}f!=this.over_link_center&&(this.over_link_center=f,this.dirty_canvas=!0);this.canvas&&(this.canvas.style.cursor="")}if(this.node_capturing_input&&this.node_capturing_input!=h&&this.node_capturing_input.onMouseMove)this.node_capturing_input.onMouseMove(a, [a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]],this);if(this.node_dragged&&!this.live_mode){for(b in this.selected_nodes)h=this.selected_nodes[b],h.pos[0]+=d[0]/this.ds.scale,h.pos[1]+=d[1]/this.ds.scale;this.dirty_bgcanvas=this.dirty_canvas=!0}this.resizing_node&&!this.live_mode&&(this.resizing_node.size[0]=a.canvasX-this.resizing_node.pos[0],this.resizing_node.size[1]=a.canvasY-this.resizing_node.pos[1],d=Math.max(this.resizing_node.inputs?this.resizing_node.inputs.length: -0,this.resizing_node.outputs?this.resizing_node.outputs.length:0)*e.NODE_SLOT_HEIGHT+(this.resizing_node.widgets?this.resizing_node.widgets.length:0)*(e.NODE_WIDGET_HEIGHT+4)+4,this.resizing_node.size[1]this.dragging_rectangle[3]?this.dragging_rectangle[1]-f:this.dragging_rectangle[1];this.dragging_rectangle[0]=0>this.dragging_rectangle[2]?this.dragging_rectangle[0]-h:this.dragging_rectangle[0];this.dragging_rectangle[1]=c;this.dragging_rectangle[2]=h;this.dragging_rectangle[3]=f;f=[];for(c=0;ca.click_time&&z(a.canvasX,a.canvasY,h.pos[0],h.pos[1]-e.NODE_TITLE_HEIGHT,e.NODE_TITLE_HEIGHT,e.NODE_TITLE_HEIGHT)&&h.collapse();this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_dragged.pos[0]=Math.round(this.node_dragged.pos[0]); +f=Math.abs(this.dragging_rectangle[3]),c=0>this.dragging_rectangle[3]?this.dragging_rectangle[1]-f:this.dragging_rectangle[1];this.dragging_rectangle[0]=0>this.dragging_rectangle[2]?this.dragging_rectangle[0]-h:this.dragging_rectangle[0];this.dragging_rectangle[1]=c;this.dragging_rectangle[2]=h;this.dragging_rectangle[3]=f;f=[];for(c=0;ca.click_time&&z(a.canvasX,a.canvasY,h.pos[0],h.pos[1]-g.NODE_TITLE_HEIGHT,g.NODE_TITLE_HEIGHT,g.NODE_TITLE_HEIGHT)&&h.collapse();this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_dragged.pos[0]=Math.round(this.node_dragged.pos[0]); this.node_dragged.pos[1]=Math.round(this.node_dragged.pos[1]);this.graph.config.align_to_grid&&this.node_dragged.alignToGrid();if(this.onNodeMoved)this.onNodeMoved(this.node_dragged);this.node_dragged=null}else{h=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes);!h&&300>a.click_time&&this.deselectAllNodes();this.dirty_canvas=!0;this.dragging_canvas=!1;if(this.node_over&&this.node_over.onMouseUp)this.node_over.onMouseUp(a,[a.canvasX-this.node_over.pos[0],a.canvasY-this.node_over.pos[1]], -this);if(this.node_capturing_input&&this.node_capturing_input.onMouseUp)this.node_capturing_input.onMouseUp(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]])}}else 2==a.which?(this.dirty_canvas=!0,this.dragging_canvas=!1):3==a.which&&(this.dirty_canvas=!0,this.dragging_canvas=!1);this.graph.change();a.stopPropagation();a.preventDefault();return!1}};g.prototype.processMouseWheel=function(a){if(this.graph&&this.allow_dragcanvas){var b=null!=a.wheelDeltaY?a.wheelDeltaY: --60*a.detail;this.adjustMouseEvent(a);var d=this.ds.scale;0b&&(d*=1/1.1);this.ds.changeScale(d,[a.localX,a.localY]);this.graph.change();a.preventDefault();return!1}};g.prototype.isOverNodeBox=function(a,b,d){var h=e.NODE_TITLE_HEIGHT;return z(b,d,a.pos[0]+2,a.pos[1]+2-h,h-4,h-4)?!0:!1};g.prototype.isOverNodeInput=function(a,b,d,h){if(a.inputs)for(var f=0,e=a.inputs.length;fd-this.graph._last_trigger_time)&&this.drawBackCanvas();(this.dirty_canvas||a)&&this.drawFrontCanvas();this.fps=this.render_time?1/this.render_time:0;this.frame+=1}};g.prototype.drawFrontCanvas=function(){this.dirty_canvas=!1;this.ctx||(this.ctx=this.bgcanvas.getContext("2d"));var a=this.ctx;if(a){a.start2D&&a.start2D();var b=this.canvas;a.restore();a.setTransform(1,0,0,1,0,0);this.dirty_area&&(a.save(), +this);if(this.node_capturing_input&&this.node_capturing_input.onMouseUp)this.node_capturing_input.onMouseUp(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]])}}else 2==a.which?(this.dirty_canvas=!0,this.dragging_canvas=!1):3==a.which&&(this.dirty_canvas=!0,this.dragging_canvas=!1);this.graph.change();a.stopPropagation();a.preventDefault();return!1}};e.prototype.processMouseWheel=function(a){if(this.graph&&this.allow_dragcanvas){var b=null!=a.wheelDeltaY?a.wheelDeltaY: +-60*a.detail;this.adjustMouseEvent(a);var d=this.ds.scale;0b&&(d*=1/1.1);this.ds.changeScale(d,[a.localX,a.localY]);this.graph.change();a.preventDefault();return!1}};e.prototype.isOverNodeBox=function(a,b,d){var h=g.NODE_TITLE_HEIGHT;return z(b,d,a.pos[0]+2,a.pos[1]+2-h,h-4,h-4)?!0:!1};e.prototype.isOverNodeInput=function(a,b,d,h){if(a.inputs)for(var f=0,g=a.inputs.length;fd-this.graph._last_trigger_time)&&this.drawBackCanvas();(this.dirty_canvas||a)&&this.drawFrontCanvas();this.fps=this.render_time?1/this.render_time:0;this.frame+=1}};e.prototype.drawFrontCanvas=function(){this.dirty_canvas=!1;this.ctx||(this.ctx=this.bgcanvas.getContext("2d"));var a=this.ctx;if(a){a.start2D&&a.start2D();var b=this.canvas;a.restore();a.setTransform(1,0,0,1,0,0);this.dirty_area&&(a.save(), a.beginPath(),a.rect(this.dirty_area[0],this.dirty_area[1],this.dirty_area[2],this.dirty_area[3]),a.clip());this.clear_background&&a.clearRect(0,0,b.width,b.height);this.bgcanvas==this.canvas?this.drawBackCanvas():a.drawImage(this.bgcanvas,0,0);if(this.onRender)this.onRender(b,a);this.show_info&&this.renderInfo(a);if(this.graph){a.save();this.ds.toCanvasContext(a);for(var b=this.computeVisibleNodes(null,this.visible_nodes),d=0;d> ";b.fillText(h+d.getTitle(), 0.5*a.width,40);b.restore()}d=!1;this.onRenderBackground&&(d=this.onRenderBackground(a,b));b.restore();b.setTransform(1,0,0,1,0,0);this.visible_links.length=0;if(this.graph){b.save();this.ds.toCanvasContext(b);if(this.background_image&&0.5this.ds.scale;if(this.live_mode){if(!a.flags.collapsed&&(b.shadowColor="transparent",a.onDrawForeground))a.onDrawForeground(b,this,this.canvas)}else{var c= -this.editor_alpha;b.globalAlpha=c;this.render_shadows&&!f?(b.shadowColor=e.DEFAULT_SHADOW_COLOR,b.shadowOffsetX=2*this.ds.scale,b.shadowOffsetY=2*this.ds.scale,b.shadowBlur=3*this.ds.scale):b.shadowColor="transparent";if(!a.flags.collapsed||!a.onDrawCollapsed||!0!=a.onDrawCollapsed(b,this)){var r=a._shape||e.BOX_SHAPE;t.set(a.size);var l=a.horizontal;if(a.flags.collapsed){b.font=this.inner_text_font;var g=a.getTitle?a.getTitle():a.title;null!=g&&(a._collapsed_width=Math.min(a.size[0],b.measureText(g).width+ -2*e.NODE_TITLE_HEIGHT),t[0]=a._collapsed_width,t[1]=0)}a.clip_area&&(b.save(),b.beginPath(),r==e.BOX_SHAPE?b.rect(0,0,t[0],t[1]):r==e.ROUND_SHAPE?b.roundRect(0,0,t[0],t[1],10):r==e.CIRCLE_SHAPE&&b.arc(0.5*t[0],0.5*t[1],0.5*t[0],0,2*Math.PI),b.clip());a.has_errors&&(h="red");this.drawNodeShape(a,b,t,d,h,a.is_selected,a.mouseOver);b.shadowColor="transparent";if(a.onDrawForeground)a.onDrawForeground(b,this,this.canvas);b.textAlign=l?"center":"left";b.font=this.inner_text_font;h=!f;r=this.connecting_output; -b.lineWidth=1;var g=0,q=new Float32Array(2);if(!a.flags.collapsed){if(a.inputs)for(d=0;dthis.ds.scale;if(this.live_mode){if(!a.flags.collapsed&&(b.shadowColor="transparent",a.onDrawForeground))a.onDrawForeground(b,this,this.canvas)}else{var c= +this.editor_alpha;b.globalAlpha=c;this.render_shadows&&!f?(b.shadowColor=g.DEFAULT_SHADOW_COLOR,b.shadowOffsetX=2*this.ds.scale,b.shadowOffsetY=2*this.ds.scale,b.shadowBlur=3*this.ds.scale):b.shadowColor="transparent";if(!a.flags.collapsed||!a.onDrawCollapsed||!0!=a.onDrawCollapsed(b,this)){var r=a._shape||g.BOX_SHAPE;s.set(a.size);var l=a.horizontal;if(a.flags.collapsed){b.font=this.inner_text_font;var e=a.getTitle?a.getTitle():a.title;null!=e&&(a._collapsed_width=Math.min(a.size[0],b.measureText(e).width+ +2*g.NODE_TITLE_HEIGHT),s[0]=a._collapsed_width,s[1]=0)}a.clip_area&&(b.save(),b.beginPath(),r==g.BOX_SHAPE?b.rect(0,0,s[0],s[1]):r==g.ROUND_SHAPE?b.roundRect(0,0,s[0],s[1],10):r==g.CIRCLE_SHAPE&&b.arc(0.5*s[0],0.5*s[1],0.5*s[0],0,2*Math.PI),b.clip());a.has_errors&&(h="red");this.drawNodeShape(a,b,s,d,h,a.is_selected,a.mouseOver);b.shadowColor="transparent";if(a.onDrawForeground)a.onDrawForeground(b,this,this.canvas);b.textAlign=l?"center":"left";b.font=this.inner_text_font;h=!f;r=this.connecting_output; +b.lineWidth=1;var e=0,p=new Float32Array(2);if(!a.flags.collapsed){if(a.inputs)for(d=0;dthis.ds.scale,p=a._shape||a.constructor.shape||e.ROUND_SHAPE,q=a.constructor.title_mode,s=!0;q==e.TRANSPARENT_TITLE?s=!1:q==e.AUTOHIDE_TITLE&&r&&(s= -!0);A[0]=0;A[1]=s?-f:0;A[2]=d[0]+1;A[3]=s?d[1]+f:d[1];r=b.globalAlpha;b.beginPath();p==e.BOX_SHAPE||l?b.fillRect(A[0],A[1],A[2],A[3]):p==e.ROUND_SHAPE||p==e.CARD_SHAPE?b.roundRect(A[0],A[1],A[2],A[3],this.round_radius,p==e.CARD_SHAPE?0:this.round_radius):p==e.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0],0,2*Math.PI);b.fill();a.flags.collapsed||(b.shadowColor="transparent",b.fillStyle="rgba(0,0,0,0.2)",b.fillRect(0,-1,A[2],2));b.shadowColor="transparent";if(a.onDrawBackground)a.onDrawBackground(b, -this,this.canvas);if(s||q==e.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,f,d,this.ds.scale,h);else if(q!=e.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){s=a.constructor.title_color||h;a.flags.collapsed&&(b.shadowColor=e.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var u=g.gradients[s];u||(u=g.gradients[s]=b.createLinearGradient(0,0,400,0),u.addColorStop(0,s),u.addColorStop(1,"#000"));b.fillStyle=u}else b.fillStyle=s;b.beginPath();p==e.BOX_SHAPE||l?b.rect(0, --f,d[0]+1,f):p!=e.ROUND_SHAPE&&p!=e.CARD_SHAPE||b.roundRect(0,-f,d[0]+1,f,this.round_radius,a.flags.collapsed?this.round_radius:0);b.fill();b.shadowColor="transparent"}if(a.onDrawTitleBox)a.onDrawTitleBox(b,f,d,this.ds.scale);else p==e.ROUND_SHAPE||p==e.CIRCLE_SHAPE||p==e.CARD_SHAPE?(l&&(b.fillStyle="black",b.beginPath(),b.arc(0.5*f,-0.5*f,6,0,2*Math.PI),b.fill()),b.fillStyle=a.boxcolor||e.NODE_DEFAULT_BOXCOLOR,l?b.fillRect(0.5*f-5,-0.5*f-5,10,10):(b.beginPath(),b.arc(0.5*f,-0.5*f,5,0,2*Math.PI), -b.fill())):(l&&(b.fillStyle="black",b.fillRect(0.5*(f-10)-1,-0.5*(f+10)-1,12,12)),b.fillStyle=a.boxcolor||e.NODE_DEFAULT_BOXCOLOR,b.fillRect(0.5*(f-10),-0.5*(f+10),10,10));b.globalAlpha=r;if(a.onDrawTitleText)a.onDrawTitleText(b,f,d,this.ds.scale,this.title_text_font,c);!l&&(b.font=this.title_text_font,l=a.getTitle())&&(b.fillStyle=c?"white":a.constructor.title_text_color||this.node_title_color,a.flags.collapsed?(b.textAlign="center",r=b.measureText(l),b.fillText(l,f+0.5*r.width,e.NODE_TITLE_TEXT_Y- -f),b.textAlign="left"):(b.textAlign="left",b.fillText(l,f,e.NODE_TITLE_TEXT_Y-f)));if(a.onDrawTitle)a.onDrawTitle(b)}if(c){if(a.onBounding)a.onBounding(A);q==e.TRANSPARENT_TITLE&&(A[1]-=f,A[3]+=f);b.lineWidth=1;b.globalAlpha=0.8;b.beginPath();p==e.BOX_SHAPE?b.rect(-6+A[0],-6+A[1],12+A[2],12+A[3]):p==e.ROUND_SHAPE||p==e.CARD_SHAPE&&a.flags.collapsed?b.roundRect(-6+A[0],-6+A[1],12+A[2],12+A[3],2*this.round_radius):p==e.CARD_SHAPE?b.roundRect(-6+A[0],-6+A[1],12+A[2],12+A[3],2*this.round_radius,2):p== -e.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0]+6,0,2*Math.PI);b.strokeStyle="#FFF";b.stroke();b.strokeStyle=h;b.globalAlpha=1}};var p=new Float32Array(4),s=new Float32Array(4),w=new Float32Array(2),l=new Float32Array(2);g.prototype.drawConnections=function(a){var b=e.getTime(),d=this.visible_area;p[0]=d[0]-20;p[1]=d[1]-20;p[2]=d[2]+40;p[3]=d[3]+40;a.lineWidth=this.connections_width;a.fillStyle="#AAA";a.strokeStyle="#AAA";a.globalAlpha=this.editor_alpha;for(var d=this.graph._nodes,h=0,f=d.length;h< -f;++h){var c=d[h];if(c.inputs&&c.inputs.length)for(var r=0;rs[2]&&(s[0]+=s[2],s[2]=Math.abs(s[2]));0>s[3]&&(s[1]+=s[3],s[3]=Math.abs(s[3]));if(v(s,p)){var n=q.outputs[t],t=c.inputs[r];if(n&& -t&&(q=n.dir||(q.horizontal?e.DOWN:e.RIGHT),t=t.dir||(c.horizontal?e.UP:e.LEFT),this.renderLink(a,k,u,g,!1,0,null,q,t),g&&g._last_time&&1E3>b-g._last_time)){var n=2-0.002*(b-g._last_time),J=a.globalAlpha;a.globalAlpha=J*n;this.renderLink(a,k,u,g,!0,n,"white",q,t);a.globalAlpha=J}}}}}}a.globalAlpha=1};g.prototype.renderLink=function(a,b,d,h,f,c,r,l,p,q){h&&this.visible_links.push(h);!r&&h&&(r=h.color||g.link_type_colors[h.type]);r||(r=this.default_link_color);null!=h&&this.highlighted_links[h.id]&& -(r="#FFF");l=l||e.RIGHT;p=p||e.LEFT;var s=C(b,d);this.render_connections_border&&0.6b[1]?0:Math.PI,a.save(),a.translate(u[0],u[1]),a.rotate(t),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore(),a.save(),a.translate(q[0],q[1]),a.rotate(k), -a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill());if(c)for(a.fillStyle=r,u=0;5>u;++u)c=(0.001*e.getTime()+0.2*u)%1,f=this.computeConnectionPoint(b,d,c,l,p),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill()};g.prototype.computeConnectionPoint=function(a,b,d,h,f){h=h||e.RIGHT;f=f||e.LEFT;var c=C(a,b),r=[a[0],a[1]],l=[b[0],b[1]];switch(h){case e.LEFT:r[0]+=-0.25*c;break;case e.RIGHT:r[0]+=0.25*c;break;case e.UP:r[1]+= --0.25*c;break;case e.DOWN:r[1]+=0.25*c}switch(f){case e.LEFT:l[0]+=-0.25*c;break;case e.RIGHT:l[0]+=0.25*c;break;case e.UP:l[1]+=-0.25*c;break;case e.DOWN:l[1]+=0.25*c}h=(1-d)*(1-d)*(1-d);f=3*(1-d)*(1-d)*d;c=3*(1-d)*d*d;d*=d*d;return[h*a[0]+f*r[0]+c*l[0]+d*b[0],h*a[1]+f*r[1]+c*l[1]+d*b[1]]};g.prototype.drawExecutionOrder=function(a){a.shadowColor="transparent";a.globalAlpha=0.25;a.textAlign="center";a.strokeStyle="white";a.globalAlpha=0.75;for(var b=this.visible_nodes,d=0;du.last_y&&ru.options.max&&(u.value=u.options.max);else if("mousedown"==d.type)if((r=u.options.values)&&r.constructor===Function&&(r= -u.options.values(u,a)),c=40>c?-1:c>l-40?1:0,"number"==u.type)u.value+=0.1*c*(u.options.step||1),null!=u.options.min&&u.valueu.options.max&&(u.value=u.options.max);else if(c){var g=r.constructor===Array?r:Object.keys(r),s=g.indexOf(u.value)+c;s>=r.length&&(s=0);0>s&&(s=g.length-1);u.value=r.constructor===Array?r[s]:r[g[s]]}else new e.ContextMenu(r,{scale:Math.max(1,this.ds.scale),event:d,className:"dark",callback:s.bind(u)},g),s= -function(a,b,d){this.value=a;f(this,a);p.dirty_canvas=!0;return!1};else"mouseup"==d.type&&"number"==u.type&&(c=40>c?-1:c>l-40?1:0,200>d.click_time&&0==c&&this.prompt("Value",u.value,function(a){this.value=Number(a);f(this,this.value)}.bind(u),d));h!=u.value&&setTimeout(function(){f(this,this.value)}.bind(u),20);this.dirty_canvas=!0;break;case "toggle":"mousedown"==d.type&&(u.value=!u.value,setTimeout(function(){f(u,u.value)},20));break;case "string":case "text":"mousedown"==d.type&&this.prompt("Value", -u.value,function(a){this.value=a;f(this,a)}.bind(u),d);break;default:u.mouse&&u.mouse(ctx,d,[c,r],a)}return u}}return null};g.prototype.drawGroups=function(a,b){if(this.graph){var d=this.graph._groups;b.save();b.globalAlpha=0.5*this.editor_alpha;for(var h=0;hd&&0.01>b.editor_alpha&&(clearInterval(h),1>d&&(b.live_mode=!0));1"+p+""+a+"",value:p});if(l.length)return new e.ContextMenu(l,{event:d,callback:c,parentMenu:h,allow_html:!0,node:f},b),!1}};g.decodeHTML=function(a){var b=document.createElement("div");b.innerText=a;return b.innerHTML};g.onResizeNode=function(a, -b,d,h,f){f&&(f.size=f.computeSize(),f.setDirtyCanvas(!0,!0))};g.prototype.showLinkMenu=function(a,b){var d=this;console.log(a);var h=new e.ContextMenu(["Add Node",null,"Delete"],{event:b,title:null!=a.data?a.data.constructor.name:null,callback:function(b,c,e){switch(b){case "Add Node":g.onMenuAdd(null,null,e,h,function(b){console.log("node autoconnect");var f=d.graph.getNodeById(a.origin_id),h=d.graph.getNodeById(a.target_id);b.inputs&&b.inputs.length&&b.outputs&&b.outputs.length&&f.outputs[a.origin_slot].type== -b.inputs[0].type&&b.outputs[0].type==h.inputs[0].type&&(f.connect(a.origin_slot,b,0),b.connect(0,h,a.target_slot),b.pos[0]-=0.5*b.size[0])});break;case "Delete":d.graph.removeLink(a.id)}}});return!1};g.onShowPropertyEditor=function(a,b,d,h,f){function e(){var b=p.value;"Number"==a.type?b=Number(b):"Boolean"==a.type&&(b=Boolean(b));f[c]=b;l.parentNode&&l.parentNode.removeChild(l);f.setDirtyCanvas(!0,!0)}var c=a.property||"title";b=f[c];var l=document.createElement("div");l.className="graphdialog"; -l.innerHTML="";l.querySelector(".name").innerText=c;var p=l.querySelector("input");p&&(p.value=b,p.addEventListener("blur",function(a){this.focus()}),p.addEventListener("keydown",function(a){13==a.keyCode&&(e(),a.preventDefault(),a.stopPropagation())}));b=g.active_canvas.canvas;d=b.getBoundingClientRect();var q=h=-20;d&&(h-=d.left,q-=d.top);event?(l.style.left=event.clientX+h+"px",l.style.top=event.clientY+q+ -"px"):(l.style.left=0.5*b.width+h+"px",l.style.top=0.5*b.height+q+"px");l.querySelector("button").addEventListener("click",e);b.parentNode.appendChild(l)};g.prototype.prompt=function(a,b,d,h){var f=this;a=a||"";var c=!1,e=document.createElement("div");e.className="graphdialog rounded";e.innerHTML=" ";e.close=function(){f.prompt_box=null;e.parentNode&&e.parentNode.removeChild(e)};1g.search_limit)break}}q=null;if(Array.prototype.filter)q=Object.keys(e.registered_node_types).filter(s);else for(l in q= -[],e.registered_node_types)s(l)&&q.push(l);for(l=0;lg.search_limit);l++);var s=function(a){var b=e.registered_node_types[a];return r&&b.filter!=r?!1:-1!==a.toLowerCase().indexOf(d)}}}var f=this,c=g.active_canvas,l=c.canvas,p=l.ownerDocument||document,q=document.createElement("div");q.className="litegraph litesearchbox graphdialog rounded";q.innerHTML="Search
"; -q.close=function(){f.search_box=null;p.body.focus();p.body.style.overflow="";setTimeout(function(){f.canvas.focus()},20);q.parentNode&&q.parentNode.removeChild(q)};var s=null;1l.height-200&&(k.style.maxHeight=l.height-a.layerY-20+"px");v.focus();return q};g.prototype.showEditPropertyValue=function(a,b,d){function h(){f(u.value)}function f(f){"number"==typeof a.properties[b]&&(f=Number(f));if("array"==c||"object"==c)f=JSON.parse(f);a.properties[b]=f;a._graph&&a._graph._version++;if(a.onPropertyChanged)a.onPropertyChanged(b, -f);if(d.onclose)d.onclose();g.close();a.setDirtyCanvas(!0,!0)}if(a&&void 0!==a.properties[b]){d=d||{};var e=a.getPropertyInfo(b),c=e.type,l="";if("string"==c||"number"==c||"array"==c||"object"==c)l="";else if("enum"==c&&e.values){var l=""}else if("boolean"== -c)l="";else{console.warn("unknown type: "+c);return}var g=this.createDialog(""+b+""+l+"",d);if("enum"==c&&e.values){var u=g.querySelector("select");u.addEventListener("change",function(a){f(a.target.value)})}else if("boolean"==c)(u=g.querySelector("input"))&&u.addEventListener("click",function(a){f(!!u.checked)});else if(u=g.querySelector("input"))u.addEventListener("blur", -function(a){this.focus()}),p=void 0!==a.properties[b]?a.properties[b]:"",p=JSON.stringify(p),u.value=p,u.addEventListener("keydown",function(a){13==a.keyCode&&(h(),a.preventDefault(),a.stopPropagation())});g.querySelector("button").addEventListener("click",h);return g}};g.prototype.createDialog=function(a,b){b=b||{};var d=document.createElement("div");d.className="graphdialog";d.innerHTML=a;var h=this.canvas.getBoundingClientRect(),f=-20,e=-20;h&&(f-=h.left,e-=h.top);b.position?(f+=b.position[0], -e+=b.position[1]):b.event?(f+=b.event.clientX,e+=b.event.clientY):(f+=0.5*this.canvas.width,e+=0.5*this.canvas.height);d.style.left=f+"px";d.style.top=e+"px";this.canvas.parentNode.appendChild(d);d.close=function(){this.parentNode&&this.parentNode.removeChild(this)};return d};g.onMenuNodeCollapse=function(a,b,d,h,f){f.collapse()};g.onMenuNodePin=function(a,b,d,h,f){f.pin()};g.onMenuNodeMode=function(a,b,d,h,f){new e.ContextMenu(["Always","On Event","On Trigger","Never"],{event:d,callback:function(a){if(f)switch(a){case "On Event":f.mode= -e.ON_EVENT;break;case "On Trigger":f.mode=e.ON_TRIGGER;break;case "Never":f.mode=e.NEVER;break;default:f.mode=e.ALWAYS}},parentMenu:h,node:f});return!1};g.onMenuNodeColors=function(a,b,d,h,f){if(!f)throw"no node for color";b=[];b.push({value:null,content:"No color"});for(var c in g.node_colors)a=g.node_colors[c],a={value:c,content:""+c+""},b.push(a);new e.ContextMenu(b,{event:d,callback:function(a){f&&((a=a.value?g.node_colors[a.value]:null)?f.constructor===e.LGraphGroup?f.color=a.groupcolor:(f.color=a.color,f.bgcolor=a.bgcolor):(delete f.color,delete f.bgcolor),f.setDirtyCanvas(!0,!0))},parentMenu:h,node:f});return!1};g.onMenuNodeShapes=function(a,b,d,h,f){if(!f)throw"no node passed";new e.ContextMenu(e.VALID_SHAPES,{event:d,callback:function(a){f&&(f.shape=a,f.setDirtyCanvas(!0))},parentMenu:h,node:f}); -return!1};g.onMenuNodeRemove=function(a,b,d,h,f){if(!f)throw"no node passed";!1!==f.removable&&(f.graph.remove(f),f.setDirtyCanvas(!0,!0))};g.onMenuNodeClone=function(a,b,d,h,f){!1!=f.clonable&&(a=f.clone())&&(a.pos=[f.pos[0]+5,f.pos[1]+5],f.graph.add(a),f.setDirtyCanvas(!0,!0))};g.node_colors={red:{color:"#322",bgcolor:"#533",groupcolor:"#A88"},brown:{color:"#332922",bgcolor:"#593930",groupcolor:"#b06634"},green:{color:"#232",bgcolor:"#353",groupcolor:"#8A8"},blue:{color:"#223",bgcolor:"#335",groupcolor:"#88A"}, -pale_blue:{color:"#2a363b",bgcolor:"#3f5159",groupcolor:"#3f789e"},cyan:{color:"#233",bgcolor:"#355",groupcolor:"#8AA"},purple:{color:"#323",bgcolor:"#535",groupcolor:"#a1309b"},yellow:{color:"#432",bgcolor:"#653",groupcolor:"#b58b2a"},black:{color:"#222",bgcolor:"#000",groupcolor:"#444"}};g.prototype.getCanvasMenuOptions=function(){var a=null;this.getMenuOptions?a=this.getMenuOptions():(a=[{content:"Add Node",has_submenu:!0,callback:g.onMenuAdd},{content:"Add Group",callback:g.onGroupAdd}],this._graph_stack&& -0Name",f),l=c.querySelector("input");l&&e&&(l.value=e.label||"");c.querySelector("button").addEventListener("click",function(a){l.value&&(e&&(e.label=l.value),d.setDirty(!0));c.close()})}},extra:a};a&&(c.title=a.type);var l=null;a&&(l=a.getSlotInPosition(b.canvasX,b.canvasY),g.active_node= -a);if(l){f=[];l&&l.output&&l.output.links&&l.output.links.length&&f.push({content:"Disconnect Links",slot:l});var q=l.input||l.output;f.push(q.locked?"Cannot remove":{content:"Remove Slot",slot:l});f.push(q.nameLocked?"Cannot rename":{content:"Rename Slot",slot:l});c.title=(l.input?l.input.type:l.output.type)||"*";l.input&&l.input.type==e.ACTION&&(c.title="Action");l.output&&l.output.type==e.EVENT&&(c.title="Event")}else a?f=this.getNodeMenuOptions(a):(f=this.getCanvasMenuOptions(),(l=this.graph.getGroupOnPos(b.canvasX, -b.canvasY))&&f.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:l,options:this.getGroupMenuOptions(l)}}));f&&new e.ContextMenu(f,c,h)};this.CanvasRenderingContext2D&&(CanvasRenderingContext2D.prototype.roundRect=function(a,b,d,h,f,e){void 0===f&&(f=5);void 0===e&&(e=f);this.moveTo(a+f,b);this.lineTo(a+d-f,b);this.quadraticCurveTo(a+d,b,a+d,b+f);this.lineTo(a+d,b+h-e);this.quadraticCurveTo(a+d,b+h,a+d-e,b+h);this.lineTo(a+e,b+h);this.quadraticCurveTo(a,b+h,a,b+h-e);this.lineTo(a, -b+f);this.quadraticCurveTo(a,b,a+f,b)});e.compareObjects=function(a,b){for(var d in a)if(a[d]!=b[d])return!1;return!0};e.distance=C;e.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+","+Math.round(255*a[2]).toFixed()+","+(4==a.length?a[3].toFixed(2):"1.0")+")"};e.isInsideRectangle=z;e.growBounding=function(a,b,d){ba[2]&&(a[2]=b);da[3]&&(a[3]=d)};e.isInsideBounding=function(a,b){return a[0]b[1][0]||a[1]>b[1][1]?!1:!0};e.overlapBounding=v;e.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),d=0,h,f,e=0;6>e;e+=2)h="0123456789ABCDEF".indexOf(a.charAt(e)),f="0123456789ABCDEF".indexOf(a.charAt(e+1)),b[d]=16*h+f,d++;return b};e.num2hex=function(a){for(var b="#",d,h,f=0;3>f;f++)d=a[f]/16,h=a[f]%16,b+="0123456789ABCDEF".charAt(d)+"0123456789ABCDEF".charAt(h);return b};E.prototype.addItem=function(a,b,d){function h(a){var b=this.value;b&&b.has_submenu&& -f.call(this,a)}function f(a){var b=this.value,f=!0;e.current_submenu&&e.current_submenu.close(a);if(d.callback){var h=d.callback.call(this,b,d,a,e,d.node);!0===h&&(f=!1)}if(b&&(b.callback&&!d.ignore_item_callbacks&&!0!==b.disabled&&(h=b.callback.call(this,b,d,a,e,d.extra),!0===h&&(f=!1)),b.submenu)){if(!b.submenu.options)throw"ContextMenu submenu needs options";new e.constructor(b.submenu.options,{callback:b.submenu.callback,event:a,parentMenu:e,ignore_item_callbacks:b.submenu.ignore_item_callbacks, -title:b.submenu.title,extra:b.submenu.extra,autoopen:d.autoopen});f=!1}f&&!e.lock&&e.close()}var e=this;d=d||{};var c=document.createElement("div");c.className="litemenu-entry submenu";var l=!1;if(null===b)c.classList.add("separator");else{c.innerHTML=b&&b.title?b.title:a;if(c.value=b)b.disabled&&(l=!0,c.classList.add("disabled")),(b.submenu||b.has_submenu)&&c.classList.add("has_submenu");"function"==typeof b?(c.dataset.value=a,c.onclick_callback=b):c.dataset.value=b;b.className&&(c.className+=" "+ -b.className)}this.root.appendChild(c);l||c.addEventListener("click",f);d.autoopen&&c.addEventListener("mouseenter",h);return c};E.prototype.close=function(a,b){this.root.parentNode&&this.root.parentNode.removeChild(this.root);this.parentMenu&&!b&&(this.parentMenu.lock=!1,this.parentMenu.current_submenu=null,void 0===a?this.parentMenu.close():a&&!E.isCursorOverElement(a,this.parentMenu.root)&&E.trigger(this.parentMenu.root,"mouseleave",a));this.current_submenu&&this.current_submenu.close(a,!0);this.root.closing_timer&& -clearTimeout(this.root.closing_timer)};E.trigger=function(a,b,d,h){var f=document.createEvent("CustomEvent");f.initCustomEvent(b,!0,!0,d);f.srcElement=h;a.dispatchEvent?a.dispatchEvent(f):a.__events&&a.__events.dispatchEvent(f);return f};E.prototype.getTopMenu=function(){return this.options.parentMenu?this.options.parentMenu.getTopMenu():this};E.prototype.getFirstEvent=function(){return this.options.parentMenu?this.options.parentMenu.getFirstEvent():this.options.event};E.isCursorOverElement=function(a, -b){var d=a.clientX,h=a.clientY,f=b.getBoundingClientRect();return f?h>f.top&&hf.left&&dMath.abs(d))return h[1];d=(a-h[0])/d;return h[1]*(1-d)+f[1]*d}}return 0}};B.prototype.draw=function(a,b,d,h,f,e){if(d=this.points){this.size= -b;var c=b[0]-2*this.margin;b=b[1]-2*this.margin;f=f||"#666";a.save();a.translate(this.margin,this.margin);h&&(a.fillStyle="#111",a.fillRect(0,0,c,b),a.fillStyle="#222",a.fillRect(0.5*c,0,1,b),a.strokeStyle="#333",a.strokeRect(0,0,c,b));a.strokeStyle=f;e&&(a.globalAlpha=0.5);a.beginPath();for(h=0;ha[1])){var h=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,e=a[0]-this.margin,c=a[1]-this.margin;this.selected=this.getCloserPoint([e,c],30/b.ds.scale);-1==this.selected&&(h=[e/h,1-c/f],d.push(h),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(h),this.must_update=!0);if(-1!=this.selected)return!0}};B.prototype.onMouseMove=function(a,b){var d=this.points;if(d){var h= -this.selected;if(!(0>h)){var f=(a[0]-this.margin)/(this.size[0]-2*this.margin),e=(a[1]-this.margin)/(this.size[1]-2*this.margin);this._nearest=this.getCloserPoint([a[0]-this.margin,a[1]-this.margin],30/b.ds.scale);var c=d[h];if(c){var l=0==h||h==d.length-1;!l&&(-10>a[0]||a[0]>this.size[0]+10||-10>a[1]||a[1]>this.size[1]+10)?(d.splice(h,1),this.selected=-1):(c[0]=l?0==h?0:1:Math.clamp(f,0,1),c[1]=1-Math.clamp(e,0,1),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(c),this.must_update= -!0)}}}};B.prototype.onMouseUp=function(a,b){this.selected=-1;return!1};B.prototype.getCloserPoint=function(a,b){var d=this.points;if(!d)return-1;b=b||30;for(var h=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,e=d.length,c=[0,0],l=1E6,q=-1,p=0;pl||g>b||(q=p,l=g)}return q};e.CurveEditor=B;e.getParameterNames=function(a){return(a+"").replace(/[/][/].*$/gm,"").replace(/\s+/g,"").replace(/[/][*][^/*]*[*][/]/g,"").split("){", +0.5*h,d[1]-15-24,h,24,3,3),a.moveTo(d[0]-10,d[1]-15),a.lineTo(d[0]+10,d[1]-15),a.lineTo(d[0],d[1]-5),a.fill(),a.shadowColor="transparent",a.textAlign="center",a.fillStyle="#CEC",a.fillText(f,d[0],d[1]-15-24*0.3))}};var A=new Float32Array(4);e.prototype.drawNodeShape=function(a,b,d,h,f,c,r){b.strokeStyle=h;b.fillStyle=f;f=g.NODE_TITLE_HEIGHT;var l=0.5>this.ds.scale,n=a._shape||a.constructor.shape||g.ROUND_SHAPE,p=a.constructor.title_mode,v=!0;p==g.TRANSPARENT_TITLE?v=!1:p==g.AUTOHIDE_TITLE&&r&&(v= +!0);A[0]=0;A[1]=v?-f:0;A[2]=d[0]+1;A[3]=v?d[1]+f:d[1];r=b.globalAlpha;b.beginPath();n==g.BOX_SHAPE||l?b.fillRect(A[0],A[1],A[2],A[3]):n==g.ROUND_SHAPE||n==g.CARD_SHAPE?b.roundRect(A[0],A[1],A[2],A[3],this.round_radius,n==g.CARD_SHAPE?0:this.round_radius):n==g.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0],0,2*Math.PI);b.fill();a.flags.collapsed||(b.shadowColor="transparent",b.fillStyle="rgba(0,0,0,0.2)",b.fillRect(0,-1,A[2],2));b.shadowColor="transparent";if(a.onDrawBackground)a.onDrawBackground(b, +this,this.canvas);if(v||p==g.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,f,d,this.ds.scale,h);else if(p!=g.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){v=a.constructor.title_color||h;a.flags.collapsed&&(b.shadowColor=g.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var t=e.gradients[v];t||(t=e.gradients[v]=b.createLinearGradient(0,0,400,0),t.addColorStop(0,v),t.addColorStop(1,"#000"));b.fillStyle=t}else b.fillStyle=v;b.beginPath();n==g.BOX_SHAPE||l?b.rect(0, +-f,d[0]+1,f):n!=g.ROUND_SHAPE&&n!=g.CARD_SHAPE||b.roundRect(0,-f,d[0]+1,f,this.round_radius,a.flags.collapsed?this.round_radius:0);b.fill();b.shadowColor="transparent"}if(a.onDrawTitleBox)a.onDrawTitleBox(b,f,d,this.ds.scale);else n==g.ROUND_SHAPE||n==g.CIRCLE_SHAPE||n==g.CARD_SHAPE?(l&&(b.fillStyle="black",b.beginPath(),b.arc(0.5*f,-0.5*f,6,0,2*Math.PI),b.fill()),b.fillStyle=a.boxcolor||g.NODE_DEFAULT_BOXCOLOR,l?b.fillRect(0.5*f-5,-0.5*f-5,10,10):(b.beginPath(),b.arc(0.5*f,-0.5*f,5,0,2*Math.PI), +b.fill())):(l&&(b.fillStyle="black",b.fillRect(0.5*(f-10)-1,-0.5*(f+10)-1,12,12)),b.fillStyle=a.boxcolor||g.NODE_DEFAULT_BOXCOLOR,b.fillRect(0.5*(f-10),-0.5*(f+10),10,10));b.globalAlpha=r;if(a.onDrawTitleText)a.onDrawTitleText(b,f,d,this.ds.scale,this.title_text_font,c);!l&&(b.font=this.title_text_font,l=String(a.getTitle()))&&(b.fillStyle=c?"white":a.constructor.title_text_color||this.node_title_color,a.flags.collapsed?(b.textAlign="left",b.measureText(l),b.fillText(l.substr(0,20),f,g.NODE_TITLE_TEXT_Y- +f),b.textAlign="left"):(b.textAlign="left",b.fillText(l,f,g.NODE_TITLE_TEXT_Y-f)));if(a.onDrawTitle)a.onDrawTitle(b)}if(c){if(a.onBounding)a.onBounding(A);p==g.TRANSPARENT_TITLE&&(A[1]-=f,A[3]+=f);b.lineWidth=1;b.globalAlpha=0.8;b.beginPath();n==g.BOX_SHAPE?b.rect(-6+A[0],-6+A[1],12+A[2],12+A[3]):n==g.ROUND_SHAPE||n==g.CARD_SHAPE&&a.flags.collapsed?b.roundRect(-6+A[0],-6+A[1],12+A[2],12+A[3],2*this.round_radius):n==g.CARD_SHAPE?b.roundRect(-6+A[0],-6+A[1],12+A[2],12+A[3],2*this.round_radius,2):n== +g.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0]+6,0,2*Math.PI);b.strokeStyle="#FFF";b.stroke();b.strokeStyle=h;b.globalAlpha=1}};var n=new Float32Array(4),v=new Float32Array(4),x=new Float32Array(2),l=new Float32Array(2);e.prototype.drawConnections=function(a){var b=g.getTime(),d=this.visible_area;n[0]=d[0]-20;n[1]=d[1]-20;n[2]=d[2]+40;n[3]=d[3]+40;a.lineWidth=this.connections_width;a.fillStyle="#AAA";a.strokeStyle="#AAA";a.globalAlpha=this.editor_alpha;for(var d=this.graph._nodes,h=0,f=d.length;h< +f;++h){var c=d[h];if(c.inputs&&c.inputs.length)for(var r=0;rv[2]&&(v[0]+=v[2],v[2]=Math.abs(v[2]));0>v[3]&&(v[1]+=v[3],v[3]=Math.abs(v[3]));if(u(v,n)){var q=p.outputs[s],s=c.inputs[r];if(q&& +s&&(p=q.dir||(p.horizontal?g.DOWN:g.RIGHT),s=s.dir||(c.horizontal?g.UP:g.LEFT),this.renderLink(a,k,t,e,!1,0,null,p,s),e&&e._last_time&&1E3>b-e._last_time)){var q=2-0.002*(b-e._last_time),J=a.globalAlpha;a.globalAlpha=J*q;this.renderLink(a,k,t,e,!0,q,"white",p,s);a.globalAlpha=J}}}}}}a.globalAlpha=1};e.prototype.renderLink=function(a,b,d,h,f,c,r,l,n,p){h&&this.visible_links.push(h);!r&&h&&(r=h.color||e.link_type_colors[h.type]);r||(r=this.default_link_color);null!=h&&this.highlighted_links[h.id]&& +(r="#FFF");l=l||g.RIGHT;n=n||g.LEFT;var v=C(b,d);this.render_connections_border&&0.6b[1]?0:Math.PI,a.save(),a.translate(t[0],t[1]),a.rotate(s),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore(),a.save(),a.translate(p[0],p[1]),a.rotate(k), +a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill());if(c)for(a.fillStyle=r,t=0;5>t;++t)c=(0.001*g.getTime()+0.2*t)%1,f=this.computeConnectionPoint(b,d,c,l,n),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill()};e.prototype.computeConnectionPoint=function(a,b,d,h,f){h=h||g.RIGHT;f=f||g.LEFT;var c=C(a,b),r=[a[0],a[1]],l=[b[0],b[1]];switch(h){case g.LEFT:r[0]+=-0.25*c;break;case g.RIGHT:r[0]+=0.25*c;break;case g.UP:r[1]+= +-0.25*c;break;case g.DOWN:r[1]+=0.25*c}switch(f){case g.LEFT:l[0]+=-0.25*c;break;case g.RIGHT:l[0]+=0.25*c;break;case g.UP:l[1]+=-0.25*c;break;case g.DOWN:l[1]+=0.25*c}h=(1-d)*(1-d)*(1-d);f=3*(1-d)*(1-d)*d;c=3*(1-d)*d*d;d*=d*d;return[h*a[0]+f*r[0]+c*l[0]+d*b[0],h*a[1]+f*r[1]+c*l[1]+d*b[1]]};e.prototype.drawExecutionOrder=function(a){a.shadowColor="transparent";a.globalAlpha=0.25;a.textAlign="center";a.strokeStyle="white";a.globalAlpha=0.75;for(var b=this.visible_nodes,d=0;dt.last_y&&rt.options.max&&(t.value=t.options.max);else if("mousedown"== +d.type)if((r=t.options.values)&&r.constructor===Function&&(r=t.options.values(t,a)),c=40>c?-1:c>l-40?1:0,"number"==t.type)t.value+=0.1*c*(t.options.step||1),null!=t.options.min&&t.valuet.options.max&&(t.value=t.options.max);else if(c){var e=r.constructor===Array?r:Object.keys(r),s=e.indexOf(t.value)+c;s>=r.length&&(s=0);0>s&&(s=e.length-1);t.value=r.constructor===Array?r[s]:r[e[s]]}else new g.ContextMenu(r,{scale:Math.max(1,this.ds.scale), +event:d,className:"dark",callback:s.bind(t)},e),s=function(a,b,d){this.value=a;f(this,a);n.dirty_canvas=!0;return!1};else"mouseup"==d.type&&"number"==t.type&&(c=40>c?-1:c>l-40?1:0,200>d.click_time&&0==c&&this.prompt("Value",t.value,function(a){this.value=Number(a);f(this,this.value)}.bind(t),d));h!=t.value&&setTimeout(function(){f(this,this.value)}.bind(t),20);this.dirty_canvas=!0;break;case "toggle":"mousedown"==d.type&&(t.value=!t.value,setTimeout(function(){f(t,t.value)},20));break;case "string":case "text":"mousedown"== +d.type&&this.prompt("Value",t.value,function(a){this.value=a;f(this,a)}.bind(t),d);break;default:t.mouse&&t.mouse(ctx,d,[c,r],a)}return t}}return null};e.prototype.drawGroups=function(a,b){if(this.graph){var d=this.graph._groups;b.save();b.globalAlpha=0.5*this.editor_alpha;for(var h=0;hd&&0.01>b.editor_alpha&&(clearInterval(h),1>d&&(b.live_mode= +!0));1"+n+""+a+"",value:n});if(l.length)return new g.ContextMenu(l,{event:d,callback:c,parentMenu:h,allow_html:!0,node:f},b),!1}};e.decodeHTML=function(a){var b=document.createElement("div"); +b.innerText=a;return b.innerHTML};e.onResizeNode=function(a,b,d,h,f){f&&(f.size=f.computeSize(),f.setDirtyCanvas(!0,!0))};e.prototype.showLinkMenu=function(a,b){var d=this;console.log(a);var h=new g.ContextMenu(["Add Node",null,"Delete"],{event:b,title:null!=a.data?a.data.constructor.name:null,callback:function(b,g,c){switch(b){case "Add Node":e.onMenuAdd(null,null,c,h,function(b){console.log("node autoconnect");var f=d.graph.getNodeById(a.origin_id),h=d.graph.getNodeById(a.target_id);b.inputs&&b.inputs.length&& +b.outputs&&b.outputs.length&&f.outputs[a.origin_slot].type==b.inputs[0].type&&b.outputs[0].type==h.inputs[0].type&&(f.connect(a.origin_slot,b,0),b.connect(0,h,a.target_slot),b.pos[0]-=0.5*b.size[0])});break;case "Delete":d.graph.removeLink(a.id)}}});return!1};e.onShowPropertyEditor=function(a,b,d,h,f){function g(){var b=n.value;"Number"==a.type?b=Number(b):"Boolean"==a.type&&(b=Boolean(b));f[c]=b;l.parentNode&&l.parentNode.removeChild(l);f.setDirtyCanvas(!0,!0)}var c=a.property||"title";b=f[c];var l= +document.createElement("div");l.className="graphdialog";l.innerHTML="";l.querySelector(".name").innerText=c;var n=l.querySelector("input");n&&(n.value=b,n.addEventListener("blur",function(a){this.focus()}),n.addEventListener("keydown",function(a){13==a.keyCode&&(g(),a.preventDefault(),a.stopPropagation())}));b=e.active_canvas.canvas;d=b.getBoundingClientRect();var p=h=-20;d&&(h-=d.left,p-=d.top);event?(l.style.left= +event.clientX+h+"px",l.style.top=event.clientY+p+"px"):(l.style.left=0.5*b.width+h+"px",l.style.top=0.5*b.height+p+"px");l.querySelector("button").addEventListener("click",g);b.parentNode.appendChild(l)};e.prototype.prompt=function(a,b,d,h){var f=this;a=a||"";var g=!1,c=document.createElement("div");c.className="graphdialog rounded";c.innerHTML=" ";c.close=function(){f.prompt_box=null;c.parentNode&& +c.parentNode.removeChild(c)};1e.search_limit)break}}p=null;if(Array.prototype.filter)p=Object.keys(g.registered_node_types).filter(s);else for(l in p= +[],g.registered_node_types)s(l)&&p.push(l);for(l=0;le.search_limit);l++);var s=function(a){var b=g.registered_node_types[a];return r&&b.filter!=r?!1:-1!==a.toLowerCase().indexOf(d)}}}var f=this,c=e.active_canvas,l=c.canvas,n=l.ownerDocument||document,p=document.createElement("div");p.className="litegraph litesearchbox graphdialog rounded";p.innerHTML="Search
"; +p.close=function(){f.search_box=null;n.body.focus();n.body.style.overflow="";setTimeout(function(){f.canvas.focus()},20);p.parentNode&&p.parentNode.removeChild(p)};var s=null;1l.height-200&&(v.style.maxHeight=l.height-a.layerY-20+"px");u.focus();return p};e.prototype.showEditPropertyValue=function(a,b,d){function h(){f(t.value)}function f(f){"number"==typeof a.properties[b]&&(f=Number(f));if("array"==g||"object"==g)f=JSON.parse(f);a.properties[b]=f;a._graph&&a._graph._version++;if(a.onPropertyChanged)a.onPropertyChanged(b, +f);if(d.onclose)d.onclose();e.close();a.setDirtyCanvas(!0,!0)}if(a&&void 0!==a.properties[b]){d=d||{};var c=a.getPropertyInfo(b),g=c.type,l="";if("string"==g||"number"==g||"array"==g||"object"==g)l="";else if("enum"==g&&c.values){var l=""}else if("boolean"== +g)l="";else{console.warn("unknown type: "+g);return}var e=this.createDialog(""+b+""+l+"",d);if("enum"==g&&c.values){var t=e.querySelector("select");t.addEventListener("change",function(a){f(a.target.value)})}else if("boolean"==g)(t=e.querySelector("input"))&&t.addEventListener("click",function(a){f(!!t.checked)});else if(t=e.querySelector("input"))t.addEventListener("blur", +function(a){this.focus()}),n=void 0!==a.properties[b]?a.properties[b]:"",n=JSON.stringify(n),t.value=n,t.addEventListener("keydown",function(a){13==a.keyCode&&(h(),a.preventDefault(),a.stopPropagation())});e.querySelector("button").addEventListener("click",h);return e}};e.prototype.createDialog=function(a,b){b=b||{};var d=document.createElement("div");d.className="graphdialog";d.innerHTML=a;var h=this.canvas.getBoundingClientRect(),f=-20,c=-20;h&&(f-=h.left,c-=h.top);b.position?(f+=b.position[0], +c+=b.position[1]):b.event?(f+=b.event.clientX,c+=b.event.clientY):(f+=0.5*this.canvas.width,c+=0.5*this.canvas.height);d.style.left=f+"px";d.style.top=c+"px";this.canvas.parentNode.appendChild(d);d.close=function(){this.parentNode&&this.parentNode.removeChild(this)};return d};e.onMenuNodeCollapse=function(a,b,d,h,f){f.collapse()};e.onMenuNodePin=function(a,b,d,h,f){f.pin()};e.onMenuNodeMode=function(a,b,d,h,f){new g.ContextMenu(["Always","On Event","On Trigger","Never"],{event:d,callback:function(a){if(f)switch(a){case "On Event":f.mode= +g.ON_EVENT;break;case "On Trigger":f.mode=g.ON_TRIGGER;break;case "Never":f.mode=g.NEVER;break;default:f.mode=g.ALWAYS}},parentMenu:h,node:f});return!1};e.onMenuNodeColors=function(a,b,d,h,f){if(!f)throw"no node for color";b=[];b.push({value:null,content:"No color"});for(var c in e.node_colors)a=e.node_colors[c],a={value:c,content:""+c+""},b.push(a);new g.ContextMenu(b,{event:d,callback:function(a){f&&((a=a.value?e.node_colors[a.value]:null)?f.constructor===g.LGraphGroup?f.color=a.groupcolor:(f.color=a.color,f.bgcolor=a.bgcolor):(delete f.color,delete f.bgcolor),f.setDirtyCanvas(!0,!0))},parentMenu:h,node:f});return!1};e.onMenuNodeShapes=function(a,b,d,h,f){if(!f)throw"no node passed";new g.ContextMenu(g.VALID_SHAPES,{event:d,callback:function(a){f&&(f.shape=a,f.setDirtyCanvas(!0))},parentMenu:h,node:f}); +return!1};e.onMenuNodeRemove=function(a,b,d,h,f){if(!f)throw"no node passed";!1!==f.removable&&(f.graph.remove(f),f.setDirtyCanvas(!0,!0))};e.onMenuNodeClone=function(a,b,d,h,f){!1!=f.clonable&&(a=f.clone())&&(a.pos=[f.pos[0]+5,f.pos[1]+5],f.graph.add(a),f.setDirtyCanvas(!0,!0))};e.node_colors={red:{color:"#322",bgcolor:"#533",groupcolor:"#A88"},brown:{color:"#332922",bgcolor:"#593930",groupcolor:"#b06634"},green:{color:"#232",bgcolor:"#353",groupcolor:"#8A8"},blue:{color:"#223",bgcolor:"#335",groupcolor:"#88A"}, +pale_blue:{color:"#2a363b",bgcolor:"#3f5159",groupcolor:"#3f789e"},cyan:{color:"#233",bgcolor:"#355",groupcolor:"#8AA"},purple:{color:"#323",bgcolor:"#535",groupcolor:"#a1309b"},yellow:{color:"#432",bgcolor:"#653",groupcolor:"#b58b2a"},black:{color:"#222",bgcolor:"#000",groupcolor:"#444"}};e.prototype.getCanvasMenuOptions=function(){var a=null;this.getMenuOptions?a=this.getMenuOptions():(a=[{content:"Add Node",has_submenu:!0,callback:e.onMenuAdd},{content:"Add Group",callback:e.onGroupAdd}],this._graph_stack&& +0Name",f),l=g.querySelector("input");l&&c&&(l.value=c.label||"");g.querySelector("button").addEventListener("click",function(a){l.value&&(c&&(c.label=l.value),d.setDirty(!0));g.close()})}},extra:a};a&&(c.title=a.type);var l=null;a&&(l=a.getSlotInPosition(b.canvasX,b.canvasY),e.active_node= +a);if(l){f=[];l&&l.output&&l.output.links&&l.output.links.length&&f.push({content:"Disconnect Links",slot:l});var p=l.input||l.output;f.push(p.locked?"Cannot remove":{content:"Remove Slot",slot:l});f.push(p.nameLocked?"Cannot rename":{content:"Rename Slot",slot:l});c.title=(l.input?l.input.type:l.output.type)||"*";l.input&&l.input.type==g.ACTION&&(c.title="Action");l.output&&l.output.type==g.EVENT&&(c.title="Event")}else a?f=this.getNodeMenuOptions(a):(f=this.getCanvasMenuOptions(),(l=this.graph.getGroupOnPos(b.canvasX, +b.canvasY))&&f.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:l,options:this.getGroupMenuOptions(l)}}));f&&new g.ContextMenu(f,c,h)};this.CanvasRenderingContext2D&&(CanvasRenderingContext2D.prototype.roundRect=function(a,b,d,h,f,c){void 0===f&&(f=5);void 0===c&&(c=f);this.moveTo(a+f,b);this.lineTo(a+d-f,b);this.quadraticCurveTo(a+d,b,a+d,b+f);this.lineTo(a+d,b+h-c);this.quadraticCurveTo(a+d,b+h,a+d-c,b+h);this.lineTo(a+c,b+h);this.quadraticCurveTo(a,b+h,a,b+h-c);this.lineTo(a, +b+f);this.quadraticCurveTo(a,b,a+f,b)});g.compareObjects=function(a,b){for(var d in a)if(a[d]!=b[d])return!1;return!0};g.distance=C;g.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+","+Math.round(255*a[2]).toFixed()+","+(4==a.length?a[3].toFixed(2):"1.0")+")"};g.isInsideRectangle=z;g.growBounding=function(a,b,d){ba[2]&&(a[2]=b);da[3]&&(a[3]=d)};g.isInsideBounding=function(a,b){return a[0]b[1][0]||a[1]>b[1][1]?!1:!0};g.overlapBounding=u;g.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),d=0,h,f,c=0;6>c;c+=2)h="0123456789ABCDEF".indexOf(a.charAt(c)),f="0123456789ABCDEF".indexOf(a.charAt(c+1)),b[d]=16*h+f,d++;return b};g.num2hex=function(a){for(var b="#",d,h,f=0;3>f;f++)d=a[f]/16,h=a[f]%16,b+="0123456789ABCDEF".charAt(d)+"0123456789ABCDEF".charAt(h);return b};D.prototype.addItem=function(a,b,d){function h(a){var b=this.value;b&&b.has_submenu&& +f.call(this,a)}function f(a){var b=this.value,f=!0;c.current_submenu&&c.current_submenu.close(a);if(d.callback){var h=d.callback.call(this,b,d,a,c,d.node);!0===h&&(f=!1)}if(b&&(b.callback&&!d.ignore_item_callbacks&&!0!==b.disabled&&(h=b.callback.call(this,b,d,a,c,d.extra),!0===h&&(f=!1)),b.submenu)){if(!b.submenu.options)throw"ContextMenu submenu needs options";new c.constructor(b.submenu.options,{callback:b.submenu.callback,event:a,parentMenu:c,ignore_item_callbacks:b.submenu.ignore_item_callbacks, +title:b.submenu.title,extra:b.submenu.extra,autoopen:d.autoopen});f=!1}f&&!c.lock&&c.close()}var c=this;d=d||{};var g=document.createElement("div");g.className="litemenu-entry submenu";var l=!1;if(null===b)g.classList.add("separator");else{g.innerHTML=b&&b.title?b.title:a;if(g.value=b)b.disabled&&(l=!0,g.classList.add("disabled")),(b.submenu||b.has_submenu)&&g.classList.add("has_submenu");"function"==typeof b?(g.dataset.value=a,g.onclick_callback=b):g.dataset.value=b;b.className&&(g.className+=" "+ +b.className)}this.root.appendChild(g);l||g.addEventListener("click",f);d.autoopen&&g.addEventListener("mouseenter",h);return g};D.prototype.close=function(a,b){this.root.parentNode&&this.root.parentNode.removeChild(this.root);this.parentMenu&&!b&&(this.parentMenu.lock=!1,this.parentMenu.current_submenu=null,void 0===a?this.parentMenu.close():a&&!D.isCursorOverElement(a,this.parentMenu.root)&&D.trigger(this.parentMenu.root,"mouseleave",a));this.current_submenu&&this.current_submenu.close(a,!0);this.root.closing_timer&& +clearTimeout(this.root.closing_timer)};D.trigger=function(a,b,d,h){var f=document.createEvent("CustomEvent");f.initCustomEvent(b,!0,!0,d);f.srcElement=h;a.dispatchEvent?a.dispatchEvent(f):a.__events&&a.__events.dispatchEvent(f);return f};D.prototype.getTopMenu=function(){return this.options.parentMenu?this.options.parentMenu.getTopMenu():this};D.prototype.getFirstEvent=function(){return this.options.parentMenu?this.options.parentMenu.getFirstEvent():this.options.event};D.isCursorOverElement=function(a, +b){var d=a.clientX,h=a.clientY,f=b.getBoundingClientRect();return f?h>f.top&&hf.left&&dMath.abs(d))return h[1];d=(a-h[0])/d;return h[1]*(1-d)+f[1]*d}}return 0}};B.prototype.draw=function(a,b,d,h,f,c){if(d=this.points){this.size= +b;var g=b[0]-2*this.margin;b=b[1]-2*this.margin;f=f||"#666";a.save();a.translate(this.margin,this.margin);h&&(a.fillStyle="#111",a.fillRect(0,0,g,b),a.fillStyle="#222",a.fillRect(0.5*g,0,1,b),a.strokeStyle="#333",a.strokeRect(0,0,g,b));a.strokeStyle=f;c&&(a.globalAlpha=0.5);a.beginPath();for(h=0;ha[1])){var h=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=a[0]-this.margin,g=a[1]-this.margin;this.selected=this.getCloserPoint([c,g],30/b.ds.scale);-1==this.selected&&(h=[c/h,1-g/f],d.push(h),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(h),this.must_update=!0);if(-1!=this.selected)return!0}};B.prototype.onMouseMove=function(a,b){var d=this.points;if(d){var h= +this.selected;if(!(0>h)){var f=(a[0]-this.margin)/(this.size[0]-2*this.margin),c=(a[1]-this.margin)/(this.size[1]-2*this.margin);this._nearest=this.getCloserPoint([a[0]-this.margin,a[1]-this.margin],30/b.ds.scale);var g=d[h];if(g){var l=0==h||h==d.length-1;!l&&(-10>a[0]||a[0]>this.size[0]+10||-10>a[1]||a[1]>this.size[1]+10)?(d.splice(h,1),this.selected=-1):(g[0]=l?0==h?0:1:Math.clamp(f,0,1),g[1]=1-Math.clamp(c,0,1),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(g),this.must_update= +!0)}}}};B.prototype.onMouseUp=function(a,b){this.selected=-1;return!1};B.prototype.getCloserPoint=function(a,b){var d=this.points;if(!d)return-1;b=b||30;for(var h=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=d.length,g=[0,0],l=1E6,p=-1,n=0;nl||e>b||(p=n,l=e)}return p};g.CurveEditor=B;g.getParameterNames=function(a){return(a+"").replace(/[/][/].*$/gm,"").replace(/\s+/g,"").replace(/[/][*][^/*]*[*][/]/g,"").split("){", 1)[0].replace(/^[^(]*[(]/,"").replace(/=[^,]+/g,"").split(",").filter(Boolean)};Math.clamp=function(a,b,d){return b>a?b:dthis.size[0]-l.NODE_TITLE_HEIGHT&&0>b[1]){var h=this;setTimeout(function(){d.openSubgraph(h.subgraph)},10)}};m.prototype.onAction=function(a,b){this.subgraph.onAction(a,b)};m.prototype.onExecute=function(){if(this.enabled=this.getInputOrProperty("enabled")){if(this.inputs)for(var a=0;a=e?this.trigger(null,g):this._pending.push([e,g])};g.prototype.onExecute=function(){var c=1E3* -this.graph.elapsed_time;this.isInputConnected(1)&&(this.properties.time_in_ms=this.getInputData(1));for(var g=0;g=g?this.trigger(null,e):this._pending.push([g,e])};e.prototype.onExecute=function(){var c=1E3* +this.graph.elapsed_time;this.isInputConnected(1)&&(this.properties.time_in_ms=this.getInputData(1));for(var e=0;eq[1]))return this.old_y=c.canvasY,this.captureInput(!0),this.mouse_captured=!0};n.prototype.onMouseMove=function(c){if(this.mouse_captured){var q=this.old_y-c.canvasY;c.shiftKey&&(q*=10);if(c.metaKey||c.altKey)q*=0.1;this.old_y=c.canvasY;c=this._remainder+q/n.pixels_threshold;this._remainder=c%1;c=Math.clamp(this.properties.value+ -(c|0)*this.properties.step,this.properties.min,this.properties.max);this.properties.value=c;this.graph._version++;this.setDirtyCanvas(!0)}};n.prototype.onMouseUp=function(c,q){200>c.click_time&&(this.properties.value=Math.clamp(this.properties.value+(q[1]>0.5*this.size[1]?-1:1)*this.properties.step,this.properties.min,this.properties.max),this.graph._version++,this.setDirtyCanvas(!0));this.mouse_captured&&(this.mouse_captured=!1,this.captureInput(!1))};B.registerNodeType("widget/number",n);k.title= -"Combo";k.desc="Widget to select from a list";k.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};k.prototype.onPropertyChanged=function(c,q){"values"==c?(this._values=q.split(";"),this.widget.options.values=this._values):"value"==c&&(this.widget.value=q)};B.registerNodeType("widget/combo",k);x.title="Knob";x.desc="Circular controller";x.size=[80,100];x.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/ -(this.properties.max-this.properties.min));var q=0.5*this.size[0],g=0.5*this.size[1],k=0.5*Math.min(this.size[0],this.size[1])-5;c.globalAlpha=1;c.save();c.translate(q,g);c.rotate(0.75*Math.PI);c.fillStyle="rgba(0,0,0,0.5)";c.beginPath();c.moveTo(0,0);c.arc(0,0,k,0,1.5*Math.PI);c.fill();c.strokeStyle="black";c.fillStyle=this.properties.color;c.lineWidth=2;c.beginPath();c.moveTo(0,0);c.arc(0,0,k-4,0,1.5*Math.PI*Math.max(0.01,this.value));c.closePath();c.fill();c.lineWidth=1;c.globalAlpha=1;c.restore(); -c.fillStyle="black";c.beginPath();c.arc(q,g,0.75*k,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":this.properties.color;c.beginPath();var p=this.value*Math.PI*1.5+0.75*Math.PI;c.arc(q+Math.cos(p)*k*0.65,g+Math.sin(p)*k*0.65,0.05*k,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":"#AAA";c.font=Math.floor(0.5*k)+"px Arial";c.textAlign="center";c.fillText(this.properties.value.toFixed(this.properties.precision),q,g+0.15*k)}};x.prototype.onExecute=function(){this.setOutputData(0, -this.properties.value);this.boxcolor=B.colorToString([this.value,this.value,this.value])};x.prototype.onMouseDown=function(c){this.center=[0.5*this.size[0],0.5*this.size[1]+20];this.radius=0.5*this.size[0];if(20>c.canvasY-this.pos[1]||B.distance([c.canvasX,c.canvasY],[this.pos[0]+this.center[0],this.pos[1]+this.center[1]])>this.radius)return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];this.captureInput(!0);return!0};x.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX- -this.pos[0],c.canvasY-this.pos[1]];var g=this.value,g=g-0.01*(c[1]-this.oldmouse[1]);1g&&(g=0);this.value=g;this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.oldmouse=c;this.setDirtyCanvas(!0)}};x.prototype.onMouseUp=function(c){this.oldmouse&&(this.oldmouse=null,this.captureInput(!1))};x.prototype.onPropertyChanged=function(c,g){if("min"==c||"max"==c||"value"==c)return this.properties[c]=parseFloat(g),!0};B.registerNodeType("widget/knob", -x);g.title="Inner Slider";g.prototype.onPropertyChanged=function(c,g){"value"==c&&(this.slider.value=g)};g.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};B.registerNodeType("widget/internal_slider",g);C.title="H.Slider";C.desc="Linear slider controller";C.prototype.onDrawForeground=function(c){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min));c.globalAlpha=1;c.lineWidth=1;c.fillStyle="#000";c.fillRect(2, +this.time%=this.last_interval,this.trigger("on_tick",this.properties.event),this.inputs&&1p[1]))return this.old_y=c.canvasY,this.captureInput(!0),this.mouse_captured=!0};q.prototype.onMouseMove=function(c){if(this.mouse_captured){var p=this.old_y-c.canvasY;c.shiftKey&&(p*=10);if(c.metaKey||c.altKey)p*=0.1;this.old_y=c.canvasY;c=this._remainder+p/q.pixels_threshold;this._remainder=c%1;c=Math.clamp(this.properties.value+ +(c|0)*this.properties.step,this.properties.min,this.properties.max);this.properties.value=c;this.graph._version++;this.setDirtyCanvas(!0)}};q.prototype.onMouseUp=function(c,p){200>c.click_time&&(this.properties.value=Math.clamp(this.properties.value+(p[1]>0.5*this.size[1]?-1:1)*this.properties.step,this.properties.min,this.properties.max),this.graph._version++,this.setDirtyCanvas(!0));this.mouse_captured&&(this.mouse_captured=!1,this.captureInput(!1))};B.registerNodeType("widget/number",q);k.title= +"Combo";k.desc="Widget to select from a list";k.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};k.prototype.onPropertyChanged=function(c,p){"values"==c?(this._values=p.split(";"),this.widget.options.values=this._values):"value"==c&&(this.widget.value=p)};B.registerNodeType("widget/combo",k);w.title="Knob";w.desc="Circular controller";w.size=[80,100];w.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/ +(this.properties.max-this.properties.min));var p=0.5*this.size[0],e=0.5*this.size[1],k=0.5*Math.min(this.size[0],this.size[1])-5;c.globalAlpha=1;c.save();c.translate(p,e);c.rotate(0.75*Math.PI);c.fillStyle="rgba(0,0,0,0.5)";c.beginPath();c.moveTo(0,0);c.arc(0,0,k,0,1.5*Math.PI);c.fill();c.strokeStyle="black";c.fillStyle=this.properties.color;c.lineWidth=2;c.beginPath();c.moveTo(0,0);c.arc(0,0,k-4,0,1.5*Math.PI*Math.max(0.01,this.value));c.closePath();c.fill();c.lineWidth=1;c.globalAlpha=1;c.restore(); +c.fillStyle="black";c.beginPath();c.arc(p,e,0.75*k,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":this.properties.color;c.beginPath();var n=this.value*Math.PI*1.5+0.75*Math.PI;c.arc(p+Math.cos(n)*k*0.65,e+Math.sin(n)*k*0.65,0.05*k,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":"#AAA";c.font=Math.floor(0.5*k)+"px Arial";c.textAlign="center";c.fillText(this.properties.value.toFixed(this.properties.precision),p,e+0.15*k)}};w.prototype.onExecute=function(){this.setOutputData(0, +this.properties.value);this.boxcolor=B.colorToString([this.value,this.value,this.value])};w.prototype.onMouseDown=function(c){this.center=[0.5*this.size[0],0.5*this.size[1]+20];this.radius=0.5*this.size[0];if(20>c.canvasY-this.pos[1]||B.distance([c.canvasX,c.canvasY],[this.pos[0]+this.center[0],this.pos[1]+this.center[1]])>this.radius)return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];this.captureInput(!0);return!0};w.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX- +this.pos[0],c.canvasY-this.pos[1]];var e=this.value,e=e-0.01*(c[1]-this.oldmouse[1]);1e&&(e=0);this.value=e;this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.oldmouse=c;this.setDirtyCanvas(!0)}};w.prototype.onMouseUp=function(c){this.oldmouse&&(this.oldmouse=null,this.captureInput(!1))};w.prototype.onPropertyChanged=function(c,e){if("min"==c||"max"==c||"value"==c)return this.properties[c]=parseFloat(e),!0};B.registerNodeType("widget/knob", +w);e.title="Inner Slider";e.prototype.onPropertyChanged=function(c,e){"value"==c&&(this.slider.value=e)};e.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};B.registerNodeType("widget/internal_slider",e);C.title="H.Slider";C.desc="Linear slider controller";C.prototype.onDrawForeground=function(c){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min));c.globalAlpha=1;c.lineWidth=1;c.fillStyle="#000";c.fillRect(2, 2,this.size[0]-4,this.size[1]-4);c.fillStyle=this.properties.color;c.beginPath();c.rect(4,4,(this.size[0]-8)*this.value,this.size[1]-8);c.fill()};C.prototype.onExecute=function(){this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.setOutputData(0,this.properties.value);this.boxcolor=B.colorToString([this.value,this.value,this.value])};C.prototype.onMouseDown=function(c){if(0>c.canvasY-this.pos[1])return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY- -this.pos[1]];this.captureInput(!0);return!0};C.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];var g=this.value,g=g+(c[0]-this.oldmouse[0])/this.size[0];1g&&(g=0);this.value=g;this.oldmouse=c;this.setDirtyCanvas(!0)}};C.prototype.onMouseUp=function(c){this.oldmouse=null;this.captureInput(!1)};C.prototype.onMouseLeave=function(c){};B.registerNodeType("widget/hslider",C);z.title="Progress";z.desc="Shows data in linear progress";z.prototype.onExecute= -function(){var c=this.getInputData(0);void 0!=c&&(this.properties.value=c)};z.prototype.onDrawForeground=function(c){c.lineWidth=1;c.fillStyle=this.properties.color;var g=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min),g=Math.min(1,g),g=Math.max(0,g);c.fillRect(2,2,(this.size[0]-4)*g,this.size[1]-4)};B.registerNodeType("widget/progress",z);v.title="Text";v.desc="Shows the input value";v.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"led_text", -text:"LED",type:"minibutton"},{name:"normal_text",text:"Normal",type:"minibutton"}];v.prototype.onDrawForeground=function(c){c.fillStyle=this.properties.color;var g=this.properties.value;this.properties.glowSize?(c.shadowColor=this.properties.color,c.shadowOffsetX=0,c.shadowOffsetY=0,c.shadowBlur=this.properties.glowSize):c.shadowColor="transparent";var k=this.properties.fontsize;c.textAlign=this.properties.align;c.font=k.toString()+"px "+this.properties.font;this.str="number"==typeof g?g.toFixed(this.properties.decimals): -g;if("string"==typeof this.str){var g=this.str.split("\\n"),n;for(n in g)c.fillText(g[n],"left"==this.properties.align?15:this.size[0]-15,-0.15*k+k*(parseInt(n)+1))}c.shadowColor="transparent";this.last_ctx=c;c.textAlign="left"};v.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(this.properties.value=c)};v.prototype.resize=function(){if(this.last_ctx){var c=this.str.split("\\n");this.last_ctx.font=this.properties.fontsize+"px "+this.properties.font;var g=0,k;for(k in c){var n=this.last_ctx.measureText(c[k]).width; -ge&&(e=0);this.value=e;this.oldmouse=c;this.setDirtyCanvas(!0)}};C.prototype.onMouseUp=function(c){this.oldmouse=null;this.captureInput(!1)};C.prototype.onMouseLeave=function(c){};B.registerNodeType("widget/hslider",C);z.title="Progress";z.desc="Shows data in linear progress";z.prototype.onExecute= +function(){var c=this.getInputData(0);void 0!=c&&(this.properties.value=c)};z.prototype.onDrawForeground=function(c){c.lineWidth=1;c.fillStyle=this.properties.color;var e=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min),e=Math.min(1,e),e=Math.max(0,e);c.fillRect(2,2,(this.size[0]-4)*e,this.size[1]-4)};B.registerNodeType("widget/progress",z);u.title="Text";u.desc="Shows the input value";u.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"led_text", +text:"LED",type:"minibutton"},{name:"normal_text",text:"Normal",type:"minibutton"}];u.prototype.onDrawForeground=function(c){c.fillStyle=this.properties.color;var e=this.properties.value;this.properties.glowSize?(c.shadowColor=this.properties.color,c.shadowOffsetX=0,c.shadowOffsetY=0,c.shadowBlur=this.properties.glowSize):c.shadowColor="transparent";var s=this.properties.fontsize;c.textAlign=this.properties.align;c.font=s.toString()+"px "+this.properties.font;this.str="number"==typeof e?e.toFixed(this.properties.decimals): +e;if("string"==typeof this.str){var e=this.str.split("\\n"),k;for(k in e)c.fillText(e[k],"left"==this.properties.align?15:this.size[0]-15,-0.15*s+s*(parseInt(k)+1))}c.shadowColor="transparent";this.last_ctx=c;c.textAlign="left"};u.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(this.properties.value=c)};u.prototype.resize=function(){if(this.last_ctx){var c=this.str.split("\\n");this.last_ctx.font=this.properties.fontsize+"px "+this.properties.font;var e=0,s;for(s in c){var k=this.last_ctx.measureText(c[s]).width; +ek?n.xbox.axes.lx:0,this._left_axis[1]=Math.abs(n.xbox.axes.ly)>k?n.xbox.axes.ly:0,this._right_axis[0]=Math.abs(n.xbox.axes.rx)>k?n.xbox.axes.rx:0,this._right_axis[1]=Math.abs(n.xbox.axes.ry)>k?n.xbox.axes.ry:0,this._triggers[0]=Math.abs(n.xbox.axes.ltrigger)>k?n.xbox.axes.ltrigger: -0,this._triggers[1]=Math.abs(n.xbox.axes.rtrigger)>k?n.xbox.axes.rtrigger:0);if(this.outputs)for(k=0;kn;n++)if(k[n]){n=k[n];k=this.xbox_mapping;k||(k=this.xbox_mapping={axes:[], -buttons:{},hat:"",hatmap:c.CENTER});k.axes.lx=n.axes[0];k.axes.ly=n.axes[1];k.axes.rx=n.axes[2];k.axes.ry=n.axes[3];k.axes.ltrigger=n.buttons[6].value;k.axes.rtrigger=n.buttons[7].value;k.hat="";k.hatmap=c.CENTER;for(var m=0;mm)k.buttons[c.mapping_array[m]]=n.buttons[m].pressed,n.buttons[m].was_pressed&&this.trigger(c.mapping_array[m]+"_button_event");else switch(m){case 12:n.buttons[m].pressed&&(k.hat+="up",k.hatmap|=c.UP); -break;case 13:n.buttons[m].pressed&&(k.hat+="down",k.hatmap|=c.DOWN);break;case 14:n.buttons[m].pressed&&(k.hat+="left",k.hatmap|=c.LEFT);break;case 15:n.buttons[m].pressed&&(k.hat+="right",k.hatmap|=c.RIGHT);break;case 16:k.buttons.home=n.buttons[m].pressed}n.xbox=k;return n}};c.prototype.onDrawBackground=function(c){if(!this.flags.collapsed){var k=this._left_axis,m=this._right_axis;c.strokeStyle="#88A";c.strokeRect(0.5*(k[0]+1)*this.size[0]-4,0.5*(k[1]+1)*this.size[1]-4,8,8);c.strokeStyle="#8A8"; +8;c.zero=new Float32Array(2);c.buttons="a b x y lb rb lt rt back start ls rs home".split(" ");c.prototype.onExecute=function(){var q=this.getGamepad(),k=this.properties.threshold||0;q&&(this._left_axis[0]=Math.abs(q.xbox.axes.lx)>k?q.xbox.axes.lx:0,this._left_axis[1]=Math.abs(q.xbox.axes.ly)>k?q.xbox.axes.ly:0,this._right_axis[0]=Math.abs(q.xbox.axes.rx)>k?q.xbox.axes.rx:0,this._right_axis[1]=Math.abs(q.xbox.axes.ry)>k?q.xbox.axes.ry:0,this._triggers[0]=Math.abs(q.xbox.axes.ltrigger)>k?q.xbox.axes.ltrigger: +0,this._triggers[1]=Math.abs(q.xbox.axes.rtrigger)>k?q.xbox.axes.rtrigger:0);if(this.outputs)for(k=0;kq;q++)if(k[q]){q=k[q];k=this.xbox_mapping;k||(k=this.xbox_mapping={axes:[], +buttons:{},hat:"",hatmap:c.CENTER});k.axes.lx=q.axes[0];k.axes.ly=q.axes[1];k.axes.rx=q.axes[2];k.axes.ry=q.axes[3];k.axes.ltrigger=q.buttons[6].value;k.axes.rtrigger=q.buttons[7].value;k.hat="";k.hatmap=c.CENTER;for(var m=0;mm)k.buttons[c.mapping_array[m]]=q.buttons[m].pressed,q.buttons[m].was_pressed&&this.trigger(c.mapping_array[m]+"_button_event");else switch(m){case 12:q.buttons[m].pressed&&(k.hat+="up",k.hatmap|=c.UP); +break;case 13:q.buttons[m].pressed&&(k.hat+="down",k.hatmap|=c.DOWN);break;case 14:q.buttons[m].pressed&&(k.hat+="left",k.hatmap|=c.LEFT);break;case 15:q.buttons[m].pressed&&(k.hat+="right",k.hatmap|=c.RIGHT);break;case 16:k.buttons.home=q.buttons[m].pressed}q.xbox=k;return q}};c.prototype.onDrawBackground=function(c){if(!this.flags.collapsed){var k=this._left_axis,m=this._right_axis;c.strokeStyle="#88A";c.strokeRect(0.5*(k[0]+1)*this.size[0]-4,0.5*(k[1]+1)*this.size[1]-4,8,8);c.strokeStyle="#8A8"; c.strokeRect(0.5*(m[0]+1)*this.size[0]-4,0.5*(m[1]+1)*this.size[1]-4,8,8);k=this.size[1]/this._current_buttons.length;c.fillStyle="#AEB";for(m=0;m","enum",{values:a.values});this.size=[80,60]}function b(){this.addInput("inc","number");this.addOutput("total","number");this.addProperty("increment",1);this.addProperty("value",0)}function d(){this.addInput("v","number");this.addOutput("sin","number");this.addProperty("amplitude",1);this.addProperty("offset",0);this.bgImageUrl= -"nodes/imgs/icon-sin.png"}function h(){this.addInput("x","number");this.addInput("y","number");this.addOutput("","number");this.properties={x:1,y:1,formula:"x+y"};this.code_widget=this.addWidget("text","F(x,y)",this.properties.formula,function(a,b,d){d.properties.formula=a});this.addWidget("toggle","allow",D.allow_scripts,function(a){D.allow_scripts=a});this._func=null}function f(){this.addInput("vec2","vec2");this.addOutput("x","number");this.addOutput("y","number")}function K(){this.addInputs([["x", +"nodes/imgs/icon-sin.png"}function h(){this.addInput("x","number");this.addInput("y","number");this.addOutput("","number");this.properties={x:1,y:1,formula:"x+y"};this.code_widget=this.addWidget("text","F(x,y)",this.properties.formula,function(a,b,d){d.properties.formula=a});this.addWidget("toggle","allow",E.allow_scripts,function(a){E.allow_scripts=a});this._func=null}function f(){this.addInput("vec2","vec2");this.addOutput("x","number");this.addOutput("y","number")}function K(){this.addInputs([["x", "number"],["y","number"]]);this.addOutput("vec2","vec2");this.properties={x:0,y:0};this._data=new Float32Array(2)}function r(){this.addInput("vec3","vec3");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number")}function H(){this.addInputs([["x","number"],["y","number"],["z","number"]]);this.addOutput("vec3","vec3");this.properties={x:0,y:0,z:0};this._data=new Float32Array(3)}function I(){this.addInput("vec4","vec4");this.addOutput("x","number");this.addOutput("y","number"); -this.addOutput("z","number");this.addOutput("w","number")}function G(){this.addInputs([["x","number"],["y","number"],["z","number"],["w","number"]]);this.addOutput("vec4","vec4");this.properties={x:0,y:0,z:0,w:0};this._data=new Float32Array(4)}var D=y.LiteGraph;c.title="Converter";c.desc="type A to type B";c.prototype.onExecute=function(){var a=this.getInputData(0);if(null!=a&&this.outputs)for(var b=0;ba&&(a+=1024);var c=Math.floor(a);a-=c;d=g.data[c];c=g.data[1023==c?0:c+1];b&&(a=a*a*a*(a*(6*a-15)+10));return d*(1-a)+c*a};g.prototype.onExecute=function(){var a=this.getInputData(0)||0,a=g.getValue(a,this.properties.smooth), -b=this.properties.min;this._last_v=a*(this.properties.max-b)+b;this.setOutputData(0,this._last_v)};g.prototype.onDrawBackground=function(a){this.outputs[0].label=(this._last_v||0).toFixed(3)};D.registerNodeType("math/noise",g);C.title="Spikes";C.desc="spike every random time";C.prototype.onExecute=function(){var a=this.graph.elapsed_time;this._remaining_time-=a;this._blink_time-=a;a=0;0this._remaining_time?(this._remaining_time= -Math.random()*(this.properties.max_time-this.properties.min_time)+this.properties.min_time,this._blink_time=this.properties.duration,this.boxcolor="#FFF"):this.boxcolor="#000";this.setOutputData(0,a)};D.registerNodeType("math/spikes",C);z.title="Clamp";z.desc="Clamp number between min and max";z.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(a=Math.max(this.properties.min,a),a=Math.min(this.properties.max,a),this.setOutputData(0,a))};z.prototype.getCode=function(a){a="";this.isInputConnected(0)&& -(a+="clamp({{0}},"+this.properties.min+","+this.properties.max+")");return a};D.registerNodeType("math/clamp",z);v.title="Lerp";v.desc="Linear Interpolation";v.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.getInputData(1);null==b&&(b=0);var d=this.properties.f,c=this.getInputData(2);void 0!==c&&(d=c);this.setOutputData(0,a*(1-d)+b*d)};v.prototype.onGetInputs=function(){return[["f","number"]]};D.registerNodeType("math/lerp",v);E.title="Abs";E.desc="Absolute";E.prototype.onExecute= -function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.abs(a))};D.registerNodeType("math/abs",E);B.title="Floor";B.desc="Floor number to remove fractional part";B.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.floor(a))};D.registerNodeType("math/floor",B);e.title="Frac";e.desc="Returns fractional part";e.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a%1)};D.registerNodeType("math/frac",e);q.title= -"Smoothstep";q.desc="Smoothstep";q.prototype.onExecute=function(){var a=this.getInputData(0);if(void 0!==a){var b=this.properties.A,a=Math.clamp((a-b)/(this.properties.B-b),0,1);this.setOutputData(0,a*a*(3-2*a))}};D.registerNodeType("math/smoothstep",q);t.title="Scale";t.desc="v * factor";t.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a*this.properties.factor)};D.registerNodeType("math/scale",t);A.title="Gate";A.desc="if v is true, then outputs A, otherwise B"; -A.prototype.onExecute=function(){var a=this.getInputData(0);this.setOutputData(0,this.getInputData(a?1:2))};D.registerNodeType("math/gate",A);p.title="Average";p.desc="Average Filter";p.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this._values.length;this._values[this._current%b]=a;this._current+=1;this._current>b&&(this._current=0);for(var d=a=0;db&&(b=1);this.properties.samples= -Math.round(b);var d=this._values;this._values=new Float32Array(this.properties.samples);d.length<=this._values.length?this._values.set(d):this._values.set(d.subarray(0,this._values.length))};D.registerNodeType("math/average",p);s.title="TendTo";s.desc="moves the output value always closer to the input";s.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.factor;this._value=null==this._value?a:this._value*(1-b)+a*b;this.setOutputData(0,this._value)};D.registerNodeType("math/tendTo", -s);w.values="+ - * / % ^ max min".split(" ");w.title="Operation";w.desc="Easy math operators";w["@OP"]={type:"enum",title:"operation",values:w.values};w.size=[100,60];w.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"};w.prototype.setValue=function(a){"string"==typeof a&&(a=parseFloat(a));this.properties.value=a};w.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);null!=a? -this.properties.A=a:a=this.properties.A;null!=b?this.properties.B=b:b=this.properties.B;var d=0;switch(this.properties.OP){case "+":d=a+b;break;case "-":d=a-b;break;case "x":case "X":case "*":d=a*b;break;case "/":d=a/b;break;case "%":d=a%b;break;case "^":d=Math.pow(a,b);break;case "max":d=Math.max(a,b);break;case "min":d=Math.min(a,b);break;default:console.warn("Unknown operation: "+this.properties.OP)}this.setOutputData(0,d)};w.prototype.onDrawBackground=function(a){this.flags.collapsed||(a.font= -"40px Arial",a.fillStyle="#666",a.textAlign="center",a.fillText(this.properties.OP,0.5*this.size[0],0.5*(this.size[1]+D.NODE_TITLE_HEIGHT)),a.textAlign="left")};D.registerNodeType("math/operation",w);D.registerSearchboxExtra("math/operation","MAX",{properties:{OP:"max"},title:"MAX()"});D.registerSearchboxExtra("math/operation","MIN",{properties:{OP:"min"},title:"MIN()"});l.title="Compare";l.desc="compares between two values";l.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1); +["out_min","number"],["out_max","number"]]};E.registerNodeType("math/range",k);w.title="Rand";w.desc="Random number";w.prototype.onExecute=function(){if(this.inputs)for(var a=0;aa&&(a+=1024);var c=Math.floor(a);a-=c;d=e.data[c];c=e.data[1023==c?0:c+1];b&&(a=a*a*a*(a*(6*a-15)+10));return d*(1-a)+c*a};e.prototype.onExecute=function(){var a=this.getInputData(0)||0,a=e.getValue(a,this.properties.smooth), +b=this.properties.min;this._last_v=a*(this.properties.max-b)+b;this.setOutputData(0,this._last_v)};e.prototype.onDrawBackground=function(a){this.outputs[0].label=(this._last_v||0).toFixed(3)};E.registerNodeType("math/noise",e);C.title="Spikes";C.desc="spike every random time";C.prototype.onExecute=function(){var a=this.graph.elapsed_time;this._remaining_time-=a;this._blink_time-=a;a=0;0this._remaining_time?(this._remaining_time= +Math.random()*(this.properties.max_time-this.properties.min_time)+this.properties.min_time,this._blink_time=this.properties.duration,this.boxcolor="#FFF"):this.boxcolor="#000";this.setOutputData(0,a)};E.registerNodeType("math/spikes",C);z.title="Clamp";z.desc="Clamp number between min and max";z.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(a=Math.max(this.properties.min,a),a=Math.min(this.properties.max,a),this.setOutputData(0,a))};z.prototype.getCode=function(a){a="";this.isInputConnected(0)&& +(a+="clamp({{0}},"+this.properties.min+","+this.properties.max+")");return a};E.registerNodeType("math/clamp",z);u.title="Lerp";u.desc="Linear Interpolation";u.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.getInputData(1);null==b&&(b=0);var d=this.properties.f,c=this.getInputData(2);void 0!==c&&(d=c);this.setOutputData(0,a*(1-d)+b*d)};u.prototype.onGetInputs=function(){return[["f","number"]]};E.registerNodeType("math/lerp",u);D.title="Abs";D.desc="Absolute";D.prototype.onExecute= +function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.abs(a))};E.registerNodeType("math/abs",D);B.title="Floor";B.desc="Floor number to remove fractional part";B.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.floor(a))};E.registerNodeType("math/floor",B);g.title="Frac";g.desc="Returns fractional part";g.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a%1)};E.registerNodeType("math/frac",g);p.title= +"Smoothstep";p.desc="Smoothstep";p.prototype.onExecute=function(){var a=this.getInputData(0);if(void 0!==a){var b=this.properties.A,a=Math.clamp((a-b)/(this.properties.B-b),0,1);this.setOutputData(0,a*a*(3-2*a))}};E.registerNodeType("math/smoothstep",p);s.title="Scale";s.desc="v * factor";s.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a*this.properties.factor)};E.registerNodeType("math/scale",s);A.title="Gate";A.desc="if v is true, then outputs A, otherwise B"; +A.prototype.onExecute=function(){var a=this.getInputData(0);this.setOutputData(0,this.getInputData(a?1:2))};E.registerNodeType("math/gate",A);n.title="Average";n.desc="Average Filter";n.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this._values.length;this._values[this._current%b]=a;this._current+=1;this._current>b&&(this._current=0);for(var d=a=0;db&&(b=1);this.properties.samples= +Math.round(b);var d=this._values;this._values=new Float32Array(this.properties.samples);d.length<=this._values.length?this._values.set(d):this._values.set(d.subarray(0,this._values.length))};E.registerNodeType("math/average",n);v.title="TendTo";v.desc="moves the output value always closer to the input";v.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.factor;this._value=null==this._value?a:this._value*(1-b)+a*b;this.setOutputData(0,this._value)};E.registerNodeType("math/tendTo", +v);x.values="+ - * / % ^ max min".split(" ");x.title="Operation";x.desc="Easy math operators";x["@OP"]={type:"enum",title:"operation",values:x.values};x.size=[100,60];x.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"};x.prototype.setValue=function(a){"string"==typeof a&&(a=parseFloat(a));this.properties.value=a};x.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);null!=a? +this.properties.A=a:a=this.properties.A;null!=b?this.properties.B=b:b=this.properties.B;var d=0;switch(this.properties.OP){case "+":d=a+b;break;case "-":d=a-b;break;case "x":case "X":case "*":d=a*b;break;case "/":d=a/b;break;case "%":d=a%b;break;case "^":d=Math.pow(a,b);break;case "max":d=Math.max(a,b);break;case "min":d=Math.min(a,b);break;default:console.warn("Unknown operation: "+this.properties.OP)}this.setOutputData(0,d)};x.prototype.onDrawBackground=function(a){this.flags.collapsed||(a.font= +"40px Arial",a.fillStyle="#666",a.textAlign="center",a.fillText(this.properties.OP,0.5*this.size[0],0.5*(this.size[1]+E.NODE_TITLE_HEIGHT)),a.textAlign="left")};E.registerNodeType("math/operation",x);E.registerSearchboxExtra("math/operation","MAX",{properties:{OP:"max"},title:"MAX()"});E.registerSearchboxExtra("math/operation","MIN",{properties:{OP:"min"},title:"MIN()"});l.title="Compare";l.desc="compares between two values";l.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1); void 0!==a?this.properties.A=a:a=this.properties.A;void 0!==b?this.properties.B=b:b=this.properties.B;for(var d=0,c=this.outputs.length;dB":h=a>b;break;case "A=B":h=a>=b}this.setOutputData(d,h)}}};l.prototype.onGetOutputs=function(){return[["A==B","boolean"],["A!=B","boolean"],["A>B","boolean"],["A=B", -"boolean"],["A<=B","boolean"]]};D.registerNodeType("math/compare",l);D.registerSearchboxExtra("math/compare","==",{outputs:[["A==B","boolean"]],title:"A==B"});D.registerSearchboxExtra("math/compare","!=",{outputs:[["A!=B","boolean"]],title:"A!=B"});D.registerSearchboxExtra("math/compare",">",{outputs:[["A>B","boolean"]],title:"A>B"});D.registerSearchboxExtra("math/compare","<",{outputs:[["A=",{outputs:[["A>=B","boolean"]],title:"A>=B"}); -D.registerSearchboxExtra("math/compare","<=",{outputs:[["A<=B","boolean"]],title:"A<=B"});a.values="> < == != <= >= || &&".split(" ");a["@OP"]={type:"enum",title:"operation",values:a.values};a.title="Condition";a.desc="evaluates condition between A and B";a.prototype.getTitle=function(){return"A "+this.properties.OP+" B"};a.prototype.onExecute=function(){var a=this.getInputData(0);void 0===a?a=this.properties.A:this.properties.A=a;var b=this.getInputData(1);void 0===b?b=this.properties.B:this.properties.B= -b;var d=!0;switch(this.properties.OP){case ">":d=a>b;break;case "<":d=a=":d=a>=b;break;case "||":d=a||b;break;case "&&":d=a&&b}this.setOutputData(0,d);this.setOutputData(1,!d)};D.registerNodeType("math/condition",a);b.title="Accumulate";b.desc="Increments a value every time";b.prototype.onExecute=function(){null===this.properties.value&&(this.properties.value=0);var a=this.getInputData(0);this.properties.value=null!== -a?this.properties.value+a:this.properties.value+this.properties.increment;this.setOutputData(0,this.properties.value)};D.registerNodeType("math/accumulate",b);d.title="Trigonometry";d.desc="Sin Cos Tan";d.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.amplitude,d=this.findInputSlot("amplitude");-1!=d&&(b=this.getInputData(d));var c=this.properties.offset,d=this.findInputSlot("offset");-1!=d&&(c=this.getInputData(d));for(var d=0,f=this.outputs.length;d< +"boolean"],["A<=B","boolean"]]};E.registerNodeType("math/compare",l);E.registerSearchboxExtra("math/compare","==",{outputs:[["A==B","boolean"]],title:"A==B"});E.registerSearchboxExtra("math/compare","!=",{outputs:[["A!=B","boolean"]],title:"A!=B"});E.registerSearchboxExtra("math/compare",">",{outputs:[["A>B","boolean"]],title:"A>B"});E.registerSearchboxExtra("math/compare","<",{outputs:[["A=",{outputs:[["A>=B","boolean"]],title:"A>=B"}); +E.registerSearchboxExtra("math/compare","<=",{outputs:[["A<=B","boolean"]],title:"A<=B"});a.values="> < == != <= >= || &&".split(" ");a["@OP"]={type:"enum",title:"operation",values:a.values};a.title="Condition";a.desc="evaluates condition between A and B";a.prototype.getTitle=function(){return"A "+this.properties.OP+" B"};a.prototype.onExecute=function(){var a=this.getInputData(0);void 0===a?a=this.properties.A:this.properties.A=a;var b=this.getInputData(1);void 0===b?b=this.properties.B:this.properties.B= +b;var d=!0;switch(this.properties.OP){case ">":d=a>b;break;case "<":d=a=":d=a>=b;break;case "||":d=a||b;break;case "&&":d=a&&b}this.setOutputData(0,d);this.setOutputData(1,!d)};E.registerNodeType("math/condition",a);b.title="Accumulate";b.desc="Increments a value every time";b.prototype.onExecute=function(){null===this.properties.value&&(this.properties.value=0);var a=this.getInputData(0);this.properties.value=null!== +a?this.properties.value+a:this.properties.value+this.properties.increment;this.setOutputData(0,this.properties.value)};E.registerNodeType("math/accumulate",b);d.title="Trigonometry";d.desc="Sin Cos Tan";d.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.amplitude,d=this.findInputSlot("amplitude");-1!=d&&(b=this.getInputData(d));var c=this.properties.offset,d=this.findInputSlot("offset");-1!=d&&(c=this.getInputData(d));for(var d=0,f=this.outputs.length;d< f;++d){var h;switch(this.outputs[d].name){case "sin":h=Math.sin(a);break;case "cos":h=Math.cos(a);break;case "tan":h=Math.tan(a);break;case "asin":h=Math.asin(a);break;case "acos":h=Math.acos(a);break;case "atan":h=Math.atan(a)}this.setOutputData(d,b*h+c)}};d.prototype.onGetInputs=function(){return[["v","number"],["amplitude","number"],["offset","number"]]};d.prototype.onGetOutputs=function(){return[["sin","number"],["cos","number"],["tan","number"],["asin","number"],["acos","number"],["atan","number"]]}; -D.registerNodeType("math/trigonometry",d);D.registerSearchboxExtra("math/trigonometry","SIN()",{outputs:[["sin","number"]],title:"SIN()"});D.registerSearchboxExtra("math/trigonometry","COS()",{outputs:[["cos","number"]],title:"COS()"});D.registerSearchboxExtra("math/trigonometry","TAN()",{outputs:[["tan","number"]],title:"TAN()"});h.title="Formula";h.desc="Compute formula";h.size=[160,100];p.prototype.onPropertyChanged=function(a,b){"formula"==a&&(this.code_widget.value=b)};h.prototype.onExecute= -function(){if(D.allow_scripts){var a=this.getInputData(0),b=this.getInputData(1);null!=a?this.properties.x=a:a=this.properties.x;null!=b?this.properties.y=b:b=this.properties.y;var d;try{this._func&&this._func_code==this.properties.formula||(this._func=new Function("x","y","TIME","return "+this.properties.formula),this._func_code=this.properties.formula),d=this._func(a,b,this.graph.globaltime),this.boxcolor=null}catch(c){this.boxcolor="red"}this.setOutputData(0,d)}};h.prototype.getTitle=function(){return this._func_code|| -"Formula"};h.prototype.onDrawBackground=function(){var a=this.properties.formula;this.outputs&&this.outputs.length&&(this.outputs[0].label=a)};D.registerNodeType("math/formula",h);f.title="Vec2->XY";f.desc="vector 2 to components";f.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]))};D.registerNodeType("math3d/vec2-to-xy",f);K.title="XY->Vec2";K.desc="components to vector2";K.prototype.onExecute=function(){var a=this.getInputData(0); -null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this._data;d[0]=a;d[1]=b;this.setOutputData(0,d)};D.registerNodeType("math3d/xy-to-vec2",K);r.title="Vec3->XYZ";r.desc="vector 3 to components";r.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]))};D.registerNodeType("math3d/vec3-to-xyz",r);H.title="XYZ->Vec3";H.desc="components to vector3";H.prototype.onExecute= -function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this._data;c[0]=a;c[1]=b;c[2]=d;this.setOutputData(0,c)};D.registerNodeType("math3d/xyz-to-vec3",H);I.title="Vec4->XYZW";I.desc="vector 4 to components";I.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]),this.setOutputData(3, -a[3]))};D.registerNodeType("math3d/vec4-to-xyzw",I);G.title="XYZW->Vec4";G.desc="components to vector4";G.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this.getInputData(3);null==c&&(c=this.properties.w);var f=this._data;f[0]=a;f[1]=b;f[2]=d;f[3]=c;this.setOutputData(0,f)};D.registerNodeType("math3d/xyzw-to-vec4",G);y.glMatrix&&(y=function(){this.addOutput("quat", -"quat");this.properties={x:0,y:0,z:0,w:1};this._value=quat.create()},y.title="Quaternion",y.desc="quaternion",y.prototype.onExecute=function(){this._value[0]=this.properties.x;this._value[1]=this.properties.y;this._value[2]=this.properties.z;this._value[3]=this.properties.w;this.setOutputData(0,this._value)},D.registerNodeType("math3d/quaternion",y),y=function(){this.addInputs([["degrees","number"],["axis","vec3"]]);this.addOutput("quat","quat");this.properties={angle:90,axis:vec3.fromValues(0,1, -0)};this._value=quat.create()},y.title="Rotation",y.desc="quaternion rotation",y.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.angle);var b=this.getInputData(1);null==b&&(b=this.properties.axis);a=quat.setAxisAngle(this._value,b,0.0174532925*a);this.setOutputData(0,a)},D.registerNodeType("math3d/rotation",y),y=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},y.title="Rot. Vec3",y.desc= -"rotate a point",y.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.vec);var b=this.getInputData(1);null==b?this.setOutputData(a):this.setOutputData(0,vec3.transformQuat(vec3.create(),a,b))},D.registerNodeType("math3d/rotate_vec3",y),y=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},y.title="Mult. Quat",y.desc="rotate quaternion",y.prototype.onExecute=function(){var a=this.getInputData(0);if(null!= -a){var b=this.getInputData(1);null!=b&&(a=quat.multiply(this._value,a,b),this.setOutputData(0,a))}},D.registerNodeType("math3d/mult-quat",y),y=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp","quat");this.addProperty("factor",0.5);this._value=quat.create()},y.title="Quat Slerp",y.desc="quaternion spherical interpolation",y.prototype.onExecute=function(){var a=this.getInputData(0);if(null!=a){var b=this.getInputData(1);if(null!=b){var d=this.properties.factor; -null!=this.getInputData(2)&&(d=this.getInputData(2));a=quat.slerp(this._value,a,b,d);this.setOutputData(0,a)}}},D.registerNodeType("math3d/quat-slerp",y))})(this); -(function(y){function c(){this.addInput("T","vec3");this.addInput("R","vec3");this.addInput("S","vec3");this.addOutput("mat4","mat4");this.properties={T:[0,0,0],R:[0,0,0],S:[1,1,1],R_in_degrees:!0};this._result=mat4.create();this._must_update=!0}function m(){this.addInput("A","number,vec3");this.addInput("B","number,vec3");this.addOutput("=","vec3");this.addProperty("OP","+","enum",{values:m.values});this._result=vec3.create()}function n(){this.addInput("in","vec3");this.addInput("f","number");this.addOutput("out", -"vec3");this.properties={f:1};this._data=new Float32Array(3)}function k(){this.addInput("in","vec3");this.addOutput("out","number")}function x(){this.addInput("in","vec3");this.addOutput("out","vec3");this._data=new Float32Array(3)}function g(){this.addInput("A","vec3");this.addInput("B","vec3");this.addInput("f","vec3");this.addOutput("out","vec3");this.properties={f:0.5};this._data=new Float32Array(3)}function C(){this.addInput("A","vec3");this.addInput("B","vec3");this.addOutput("out","number")} -var z=y.LiteGraph;c.title="mat4";c.temp_quat=new Float32Array([0,0,0,1]);c.temp_mat4=new Float32Array(16);c.temp_vec3=new Float32Array(3);c.prototype.onPropertyChanged=function(c,g){this._must_update=!0};c.prototype.onExecute=function(){var g=this._result,k=c.temp_quat,n=c.temp_mat4,e=c.temp_vec3,q=this.getInputData(0),t=this.getInputData(1),m=this.getInputData(2);if(this._must_update||q||t||m)q=q||this.properties.T,t=t||this.properties.R,m=m||this.properties.S,mat4.identity(g),mat4.translate(g,g, -q),this.properties.R_in_degrees?(e.set(t),vec3.scale(e,e,DEG2RAD),quat.fromEuler(k,e)):quat.fromEuler(k,t),mat4.fromQuat(n,k),mat4.multiply(g,g,n),mat4.scale(g,g,m);this.setOutputData(0,g)};z.registerNodeType("math3d/mat4",c);m.values="+ - * / % ^ max min".split(" ");m.title="Operation";m.desc="Easy math 3D operators";m["@OP"]={type:"enum",title:"operation",values:m.values};m.size=[100,60];m.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+ -"(A,B)":"A "+this.properties.OP+" B"};m.prototype.onExecute=function(){var c=this.getInputData(0),g=this.getInputData(1);if(null!=c&&null!=g){c.constructor===Number&&(c=[c,c,c]);g.constructor===Number&&(g=[g,g,g]);var k=this._result;switch(this.properties.OP){case "+":k=vec3.add(k,c,g);break;case "-":k=vec3.sub(k,c,g);break;case "x":case "X":case "*":k=vec3.mul(k,c,g);break;case "/":k=vec3.div(k,c,g);break;case "%":k[0]=c[0]%g[0];k[1]=c[1]%g[1];k[2]=c[2]%g[2];break;case "^":k[0]=Math.pow(c[0],g[0]); -k[1]=Math.pow(c[1],g[1]);k[2]=Math.pow(c[2],g[2]);break;case "max":k[0]=Math.max(c[0],g[0]);k[1]=Math.max(c[1],g[1]);k[2]=Math.max(c[2],g[2]);break;case "min":k[0]=Math.min(c[0],g[0]);k[1]=Math.min(c[1],g[1]);k[2]=Math.min(c[2],g[2]);break;default:console.warn("Unknown operation: "+this.properties.OP)}this.setOutputData(0,k)}};m.prototype.onDrawBackground=function(c){this.flags.collapsed||(c.font="40px Arial",c.fillStyle="#666",c.textAlign="center",c.fillText(this.properties.OP,0.5*this.size[0],0.5* -(this.size[1]+z.NODE_TITLE_HEIGHT)),c.textAlign="left")};z.registerNodeType("math3d/operation",m);n.title="vec3_scale";n.desc="scales the components of a vec3";n.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var g=this.getInputData(1);null==g&&(g=this.properties.f);var k=this._data;k[0]=c[0]*g;k[1]=c[1]*g;k[2]=c[2]*g;this.setOutputData(0,k)}};z.registerNodeType("math3d/vec3-scale",n);k.title="vec3_length";k.desc="returns the module of a vector";k.prototype.onExecute=function(){var c= -this.getInputData(0);null!=c&&(c=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),this.setOutputData(0,c))};z.registerNodeType("math3d/vec3-length",k);x.title="vec3_normalize";x.desc="returns the vector normalized";x.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var g=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),k=this._data;k[0]=c[0]/g;k[1]=c[1]/g;k[2]=c[2]/g;this.setOutputData(0,k)}};z.registerNodeType("math3d/vec3-normalize",x);g.title="vec3_lerp";g.desc="returns the interpolated vector"; -g.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var g=this.getInputData(1);if(null!=g){var k=this.getInputOrProperty("f"),e=this._data;e[0]=c[0]*(1-k)+g[0]*k;e[1]=c[1]*(1-k)+g[1]*k;e[2]=c[2]*(1-k)+g[2]*k;this.setOutputData(0,e)}}};z.registerNodeType("math3d/vec3-lerp",g);C.title="vec3_dot";C.desc="returns the dot product";C.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var g=this.getInputData(1);null!=g&&this.setOutputData(0,c[0]*g[0]+c[1]*g[1]+c[2]* -g[2])}};z.registerNodeType("math3d/vec3-dot",C);y.glMatrix?(y=function(){this.addOutput("quat","quat");this.properties={x:0,y:0,z:0,w:1,normalize:!1};this._value=quat.create()},y.title="Quaternion",y.desc="quaternion",y.prototype.onExecute=function(){this._value[0]=this.getInputOrProperty("x");this._value[1]=this.getInputOrProperty("y");this._value[2]=this.getInputOrProperty("z");this._value[3]=this.getInputOrProperty("w");this.properties.normalize&&quat.normalize(this._value,this._value);this.setOutputData(0, -this._value)},y.prototype.onGetInputs=function(){return[["x","number"],["y","number"],["z","number"],["w","number"]]},z.registerNodeType("math3d/quaternion",y),y=function(){this.addInputs([["degrees","number"],["axis","vec3"]]);this.addOutput("quat","quat");this.properties={angle:90,axis:vec3.fromValues(0,1,0)};this._value=quat.create()},y.title="Rotation",y.desc="quaternion rotation",y.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.angle);var g=this.getInputData(1); -null==g&&(g=this.properties.axis);c=quat.setAxisAngle(this._value,g,0.0174532925*c);this.setOutputData(0,c)},z.registerNodeType("math3d/rotation",y),y=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},y.title="Rot. Vec3",y.desc="rotate a point",y.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.vec);var g=this.getInputData(1);null==g?this.setOutputData(c):this.setOutputData(0,vec3.transformQuat(vec3.create(), -c,g))},z.registerNodeType("math3d/rotate_vec3",y),y=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},y.title="Mult. Quat",y.desc="rotate quaternion",y.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var g=this.getInputData(1);null!=g&&(c=quat.multiply(this._value,c,g),this.setOutputData(0,c))}},z.registerNodeType("math3d/mult-quat",y),y=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp", -"quat");this.addProperty("factor",0.5);this._value=quat.create()},y.title="Quat Slerp",y.desc="quaternion spherical interpolation",y.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var g=this.getInputData(1);if(null!=g){var k=this.properties.factor;null!=this.getInputData(2)&&(k=this.getInputData(2));c=quat.slerp(this._value,c,g,k);this.setOutputData(0,c)}}},z.registerNodeType("math3d/quat-slerp",y),y=function(){this.addInput("vec3","vec3");this.addOutput("remap","vec3");this.addOutput("clamped", -"vec3");this.properties={clamp:!0,range_min:[-1,-1,0],range_max:[1,1,0],target_min:[-1,-1,0],target_max:[1,1,0]};this._value=vec3.create();this._clamped=vec3.create()},y.title="Remap Range",y.desc="remap a 3D range",y.prototype.onExecute=function(){var c=this.getInputData(0);c&&this._value.set(c);for(var c=this.properties.range_min,g=this.properties.range_max,k=this.properties.target_min,e=this.properties.target_max,q=0;3>q;++q){var n=g[q]-c[q];this._clamped[q]=Math.clamp(this._value[q],c[q],g[q]); -0==n?this._value[q]=0.5*(k[q]+e[q]):(n=(this._value[q]-c[q])/n,this.properties.clamp&&(n=Math.clamp(n,0,1)),this._value[q]=k[q]+n*(e[q]-k[q]))}this.setOutputData(0,this._value);this.setOutputData(1,this._clamped)},z.registerNodeType("math3d/remap_range",y)):console.warn("No glmatrix found, some Math3D nodes may not work")})(this); +E.registerNodeType("math/trigonometry",d);E.registerSearchboxExtra("math/trigonometry","SIN()",{outputs:[["sin","number"]],title:"SIN()"});E.registerSearchboxExtra("math/trigonometry","COS()",{outputs:[["cos","number"]],title:"COS()"});E.registerSearchboxExtra("math/trigonometry","TAN()",{outputs:[["tan","number"]],title:"TAN()"});h.title="Formula";h.desc="Compute formula";h.size=[160,100];n.prototype.onPropertyChanged=function(a,b){"formula"==a&&(this.code_widget.value=b)};h.prototype.onExecute= +function(){if(E.allow_scripts){var a=this.getInputData(0),b=this.getInputData(1);null!=a?this.properties.x=a:a=this.properties.x;null!=b?this.properties.y=b:b=this.properties.y;var d;try{this._func&&this._func_code==this.properties.formula||(this._func=new Function("x","y","TIME","return "+this.properties.formula),this._func_code=this.properties.formula),d=this._func(a,b,this.graph.globaltime),this.boxcolor=null}catch(c){this.boxcolor="red"}this.setOutputData(0,d)}};h.prototype.getTitle=function(){return this._func_code|| +"Formula"};h.prototype.onDrawBackground=function(){var a=this.properties.formula;this.outputs&&this.outputs.length&&(this.outputs[0].label=a)};E.registerNodeType("math/formula",h);f.title="Vec2->XY";f.desc="vector 2 to components";f.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]))};E.registerNodeType("math3d/vec2-to-xy",f);K.title="XY->Vec2";K.desc="components to vector2";K.prototype.onExecute=function(){var a=this.getInputData(0); +null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this._data;d[0]=a;d[1]=b;this.setOutputData(0,d)};E.registerNodeType("math3d/xy-to-vec2",K);r.title="Vec3->XYZ";r.desc="vector 3 to components";r.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]))};E.registerNodeType("math3d/vec3-to-xyz",r);H.title="XYZ->Vec3";H.desc="components to vector3";H.prototype.onExecute= +function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this._data;c[0]=a;c[1]=b;c[2]=d;this.setOutputData(0,c)};E.registerNodeType("math3d/xyz-to-vec3",H);I.title="Vec4->XYZW";I.desc="vector 4 to components";I.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]),this.setOutputData(3, +a[3]))};E.registerNodeType("math3d/vec4-to-xyzw",I);G.title="XYZW->Vec4";G.desc="components to vector4";G.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this.getInputData(3);null==c&&(c=this.properties.w);var f=this._data;f[0]=a;f[1]=b;f[2]=d;f[3]=c;this.setOutputData(0,f)};E.registerNodeType("math3d/xyzw-to-vec4",G);y.glMatrix&&(y=function(){this.addOutput("quat", +"quat");this.properties={x:0,y:0,z:0,w:1};this._value=quat.create()},y.title="Quaternion",y.desc="quaternion",y.prototype.onExecute=function(){this._value[0]=this.properties.x;this._value[1]=this.properties.y;this._value[2]=this.properties.z;this._value[3]=this.properties.w;this.setOutputData(0,this._value)},E.registerNodeType("math3d/quaternion",y),y=function(){this.addInputs([["degrees","number"],["axis","vec3"]]);this.addOutput("quat","quat");this.properties={angle:90,axis:vec3.fromValues(0,1, +0)};this._value=quat.create()},y.title="Rotation",y.desc="quaternion rotation",y.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.angle);var b=this.getInputData(1);null==b&&(b=this.properties.axis);a=quat.setAxisAngle(this._value,b,0.0174532925*a);this.setOutputData(0,a)},E.registerNodeType("math3d/rotation",y),y=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},y.title="Rot. Vec3",y.desc= +"rotate a point",y.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.vec);var b=this.getInputData(1);null==b?this.setOutputData(a):this.setOutputData(0,vec3.transformQuat(vec3.create(),a,b))},E.registerNodeType("math3d/rotate_vec3",y),y=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},y.title="Mult. Quat",y.desc="rotate quaternion",y.prototype.onExecute=function(){var a=this.getInputData(0);if(null!= +a){var b=this.getInputData(1);null!=b&&(a=quat.multiply(this._value,a,b),this.setOutputData(0,a))}},E.registerNodeType("math3d/mult-quat",y),y=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp","quat");this.addProperty("factor",0.5);this._value=quat.create()},y.title="Quat Slerp",y.desc="quaternion spherical interpolation",y.prototype.onExecute=function(){var a=this.getInputData(0);if(null!=a){var b=this.getInputData(1);if(null!=b){var d=this.properties.factor; +null!=this.getInputData(2)&&(d=this.getInputData(2));a=quat.slerp(this._value,a,b,d);this.setOutputData(0,a)}}},E.registerNodeType("math3d/quat-slerp",y))})(this); +(function(y){function c(){this.addInput("T","vec3");this.addInput("R","vec3");this.addInput("S","vec3");this.addOutput("mat4","mat4");this.properties={T:[0,0,0],R:[0,0,0],S:[1,1,1],R_in_degrees:!0};this._result=mat4.create();this._must_update=!0}function m(){this.addInput("A","number,vec3");this.addInput("B","number,vec3");this.addOutput("=","vec3");this.addProperty("OP","+","enum",{values:m.values});this._result=vec3.create()}function q(){this.addInput("in","vec3");this.addInput("f","number");this.addOutput("out", +"vec3");this.properties={f:1};this._data=new Float32Array(3)}function k(){this.addInput("in","vec3");this.addOutput("out","number")}function w(){this.addInput("in","vec3");this.addOutput("out","vec3");this._data=new Float32Array(3)}function e(){this.addInput("A","vec3");this.addInput("B","vec3");this.addInput("f","vec3");this.addOutput("out","vec3");this.properties={f:0.5};this._data=new Float32Array(3)}function C(){this.addInput("A","vec3");this.addInput("B","vec3");this.addOutput("out","number")} +var z=y.LiteGraph;c.title="mat4";c.temp_quat=new Float32Array([0,0,0,1]);c.temp_mat4=new Float32Array(16);c.temp_vec3=new Float32Array(3);c.prototype.onPropertyChanged=function(c,e){this._must_update=!0};c.prototype.onExecute=function(){var e=this._result,k=c.temp_quat,q=c.temp_mat4,g=c.temp_vec3,p=this.getInputData(0),s=this.getInputData(1),m=this.getInputData(2);if(this._must_update||p||s||m)p=p||this.properties.T,s=s||this.properties.R,m=m||this.properties.S,mat4.identity(e),mat4.translate(e,e, +p),this.properties.R_in_degrees?(g.set(s),vec3.scale(g,g,DEG2RAD),quat.fromEuler(k,g)):quat.fromEuler(k,s),mat4.fromQuat(q,k),mat4.multiply(e,e,q),mat4.scale(e,e,m);this.setOutputData(0,e)};z.registerNodeType("math3d/mat4",c);m.values="+ - * / % ^ max min".split(" ");m.title="Operation";m.desc="Easy math 3D operators";m["@OP"]={type:"enum",title:"operation",values:m.values};m.size=[100,60];m.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+ +"(A,B)":"A "+this.properties.OP+" B"};m.prototype.onExecute=function(){var c=this.getInputData(0),e=this.getInputData(1);if(null!=c&&null!=e){c.constructor===Number&&(c=[c,c,c]);e.constructor===Number&&(e=[e,e,e]);var k=this._result;switch(this.properties.OP){case "+":k=vec3.add(k,c,e);break;case "-":k=vec3.sub(k,c,e);break;case "x":case "X":case "*":k=vec3.mul(k,c,e);break;case "/":k=vec3.div(k,c,e);break;case "%":k[0]=c[0]%e[0];k[1]=c[1]%e[1];k[2]=c[2]%e[2];break;case "^":k[0]=Math.pow(c[0],e[0]); +k[1]=Math.pow(c[1],e[1]);k[2]=Math.pow(c[2],e[2]);break;case "max":k[0]=Math.max(c[0],e[0]);k[1]=Math.max(c[1],e[1]);k[2]=Math.max(c[2],e[2]);break;case "min":k[0]=Math.min(c[0],e[0]);k[1]=Math.min(c[1],e[1]);k[2]=Math.min(c[2],e[2]);break;default:console.warn("Unknown operation: "+this.properties.OP)}this.setOutputData(0,k)}};m.prototype.onDrawBackground=function(c){this.flags.collapsed||(c.font="40px Arial",c.fillStyle="#666",c.textAlign="center",c.fillText(this.properties.OP,0.5*this.size[0],0.5* +(this.size[1]+z.NODE_TITLE_HEIGHT)),c.textAlign="left")};z.registerNodeType("math3d/operation",m);q.title="vec3_scale";q.desc="scales the components of a vec3";q.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null==e&&(e=this.properties.f);var k=this._data;k[0]=c[0]*e;k[1]=c[1]*e;k[2]=c[2]*e;this.setOutputData(0,k)}};z.registerNodeType("math3d/vec3-scale",q);k.title="vec3_length";k.desc="returns the module of a vector";k.prototype.onExecute=function(){var c= +this.getInputData(0);null!=c&&(c=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),this.setOutputData(0,c))};z.registerNodeType("math3d/vec3-length",k);w.title="vec3_normalize";w.desc="returns the vector normalized";w.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),k=this._data;k[0]=c[0]/e;k[1]=c[1]/e;k[2]=c[2]/e;this.setOutputData(0,k)}};z.registerNodeType("math3d/vec3-normalize",w);e.title="vec3_lerp";e.desc="returns the interpolated vector"; +e.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);if(null!=e){var k=this.getInputOrProperty("f"),g=this._data;g[0]=c[0]*(1-k)+e[0]*k;g[1]=c[1]*(1-k)+e[1]*k;g[2]=c[2]*(1-k)+e[2]*k;this.setOutputData(0,g)}}};z.registerNodeType("math3d/vec3-lerp",e);C.title="vec3_dot";C.desc="returns the dot product";C.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null!=e&&this.setOutputData(0,c[0]*e[0]+c[1]*e[1]+c[2]* +e[2])}};z.registerNodeType("math3d/vec3-dot",C);y.glMatrix?(y=function(){this.addOutput("quat","quat");this.properties={x:0,y:0,z:0,w:1,normalize:!1};this._value=quat.create()},y.title="Quaternion",y.desc="quaternion",y.prototype.onExecute=function(){this._value[0]=this.getInputOrProperty("x");this._value[1]=this.getInputOrProperty("y");this._value[2]=this.getInputOrProperty("z");this._value[3]=this.getInputOrProperty("w");this.properties.normalize&&quat.normalize(this._value,this._value);this.setOutputData(0, +this._value)},y.prototype.onGetInputs=function(){return[["x","number"],["y","number"],["z","number"],["w","number"]]},z.registerNodeType("math3d/quaternion",y),y=function(){this.addInputs([["degrees","number"],["axis","vec3"]]);this.addOutput("quat","quat");this.properties={angle:90,axis:vec3.fromValues(0,1,0)};this._value=quat.create()},y.title="Rotation",y.desc="quaternion rotation",y.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.angle);var e=this.getInputData(1); +null==e&&(e=this.properties.axis);c=quat.setAxisAngle(this._value,e,0.0174532925*c);this.setOutputData(0,c)},z.registerNodeType("math3d/rotation",y),y=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},y.title="Rot. Vec3",y.desc="rotate a point",y.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.vec);var e=this.getInputData(1);null==e?this.setOutputData(c):this.setOutputData(0,vec3.transformQuat(vec3.create(), +c,e))},z.registerNodeType("math3d/rotate_vec3",y),y=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},y.title="Mult. Quat",y.desc="rotate quaternion",y.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null!=e&&(c=quat.multiply(this._value,c,e),this.setOutputData(0,c))}},z.registerNodeType("math3d/mult-quat",y),y=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp", +"quat");this.addProperty("factor",0.5);this._value=quat.create()},y.title="Quat Slerp",y.desc="quaternion spherical interpolation",y.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);if(null!=e){var k=this.properties.factor;null!=this.getInputData(2)&&(k=this.getInputData(2));c=quat.slerp(this._value,c,e,k);this.setOutputData(0,c)}}},z.registerNodeType("math3d/quat-slerp",y),y=function(){this.addInput("vec3","vec3");this.addOutput("remap","vec3");this.addOutput("clamped", +"vec3");this.properties={clamp:!0,range_min:[-1,-1,0],range_max:[1,1,0],target_min:[-1,-1,0],target_max:[1,1,0]};this._value=vec3.create();this._clamped=vec3.create()},y.title="Remap Range",y.desc="remap a 3D range",y.prototype.onExecute=function(){var c=this.getInputData(0);c&&this._value.set(c);for(var c=this.properties.range_min,e=this.properties.range_max,k=this.properties.target_min,g=this.properties.target_max,p=0;3>p;++p){var s=e[p]-c[p];this._clamped[p]=Math.clamp(this._value[p],c[p],e[p]); +0==s?this._value[p]=0.5*(k[p]+g[p]):(s=(this._value[p]-c[p])/s,this.properties.clamp&&(s=Math.clamp(s,0,1)),this._value[p]=k[p]+s*(g[p]-k[p]))}this.setOutputData(0,this._value);this.setOutputData(1,this._clamped)},z.registerNodeType("math3d/remap_range",y)):console.warn("No glmatrix found, some Math3D nodes may not work")})(this); (function(y){function c(c,k){return c==k}function m(c){return null!=c&&c.constructor===String?c.toUpperCase():c}y=y.LiteGraph;y.wrapFunctionAsNode("string/toString",c,["*"],"String");y.wrapFunctionAsNode("string/compare",c,["String","String"],"Boolean");y.wrapFunctionAsNode("string/concatenate",function(c,k){return void 0===c?k:void 0===k?c:c+k},["String","String"],"String");y.wrapFunctionAsNode("string/contains",function(c,k){return void 0===c||void 0===k?!1:-1!=c.indexOf(k)},["String","String"], "Boolean");y.wrapFunctionAsNode("string/toUpperCase",m,["String"],"String");y.wrapFunctionAsNode("string/split",m,["String","String"],"Array");y.wrapFunctionAsNode("string/toFixed",function(c){return null!=c&&c.constructor===Number?c.toFixed(this.properties.precision):c},["Number"],"String",{precision:0})})(this); -(function(y){function c(){this.addInput("sel","number");this.addInput("A");this.addInput("B");this.addInput("C");this.addInput("D");this.addOutput("out");this.selected=0}function m(){this.properties={sequence:"A,B,C"};this.addInput("index","number");this.addInput("seq");this.addOutput("out");this.index=0;this.values=this.properties.sequence.split(",")}var n=y.LiteGraph;c.title="Selector";c.desc="selects an output";c.prototype.onDrawBackground=function(c){if(!this.flags.collapsed){c.fillStyle="#AFB"; -var m=(this.selected+1)*n.NODE_SLOT_HEIGHT+6;c.beginPath();c.moveTo(50,m);c.lineTo(50,m+n.NODE_SLOT_HEIGHT);c.lineTo(34,m+0.5*n.NODE_SLOT_HEIGHT);c.fill()}};c.prototype.onExecute=function(){var c=this.getInputData(0);if(null==c||c.constructor!==Number)c=0;this.selected=c=Math.round(c)%(this.inputs.length-1);c=this.getInputData(c+1);void 0!==c&&this.setOutputData(0,c)};c.prototype.onGetInputs=function(){return[["E",0],["F",0],["G",0],["H",0]]};n.registerNodeType("logic/selector",c);m.title="Sequence"; -m.desc="select one element from a sequence from a string";m.prototype.onPropertyChanged=function(c,n){"sequence"==c&&(this.values=n.split(","))};m.prototype.onExecute=function(){var c=this.getInputData(1);c&&c!=this.current_sequence&&(this.values=c.split(","),this.current_sequence=c);c=this.getInputData(0);null==c&&(c=0);this.index=c=Math.round(c)%this.values.length;this.setOutputData(0,this.values[c])};n.registerNodeType("logic/sequence",m)})(this); -(function(y){function c(){this.addInput("A","Number");this.addInput("B","Number");this.addInput("C","Number");this.addInput("D","Number");this.values=[[],[],[],[]];this.properties={scale:2}}function m(){this.addOutput("frame","image");this.properties={url:""}}function n(){this.addInput("f","number");this.addOutput("Color","color");this.properties={colorA:"#444444",colorB:"#44AAFF",colorC:"#44FFAA",colorD:"#FFFFFF"}}function k(){this.addInput("","image,canvas");this.size=[200,200]}function x(){this.addInputs([["img1", -"image"],["img2","image"],["fade","number"]]);this.addOutput("","image");this.properties={fade:0.5,width:512,height:512}}function g(){this.addInput("","image");this.addOutput("","image");this.properties={width:256,height:256,x:0,y:0,scale:1};this.size=[50,20]}function C(){this.addInput("clear",e.ACTION);this.addOutput("","canvas");this.properties={width:512,height:512,autoclear:!0};this.canvas=document.createElement("canvas");this.ctx=this.canvas.getContext("2d")}function z(){this.addInput("canvas", -"canvas");this.addInput("img","image,canvas");this.addInput("x","number");this.addInput("y","number");this.properties={x:0,y:0,opacity:1}}function v(){this.addInput("canvas","canvas");this.addInput("x","number");this.addInput("y","number");this.addInput("w","number");this.addInput("h","number");this.properties={x:0,y:0,w:10,h:10,color:"white",opacity:1}}function E(){this.addInput("t","number");this.addOutputs([["frame","image"],["t","number"],["d","number"]]);this.properties={url:"",use_proxy:!0}} -function B(){this.addOutput("Webcam","image");this.properties={facingMode:"user"};this.boxcolor="black";this.frame=0}var e=y.LiteGraph;c.title="Plot";c.desc="Plots data over time";c.colors=["#FFF","#F99","#9F9","#99F"];c.prototype.onExecute=function(c){if(!this.flags.collapsed){c=this.size;for(var e=0;4>e;++e){var g=this.getInputData(e);if(null!=g){var p=this.values[e];p.push(g);p.length>c[0]&&p.shift()}}}};c.prototype.onDrawBackground=function(e){if(!this.flags.collapsed){var g=this.size,k=0.5*g[1]/ -this.properties.scale,p=c.colors,s=0.5*g[1];e.fillStyle="#000";e.fillRect(0,0,g[0],g[1]);e.strokeStyle="#555";e.beginPath();e.moveTo(0,s);e.lineTo(g[0],s);e.stroke();if(this.inputs)for(var n=0;4>n;++n){var l=this.values[n];if(this.inputs[n]&&this.inputs[n].link){e.strokeStyle=p[n];e.beginPath();var a=l[0]*k*-1+s;e.moveTo(0,Math.clamp(a,0,g[1]));for(var b=1;be;++e){var g=this.getInputData(e);if(null!=g){var n=this.values[e];n.push(g);n.length>c[0]&&n.shift()}}}};c.prototype.onDrawBackground=function(e){if(!this.flags.collapsed){var g=this.size,k=0.5*g[1]/ +this.properties.scale,n=c.colors,v=0.5*g[1];e.fillStyle="#000";e.fillRect(0,0,g[0],g[1]);e.strokeStyle="#555";e.beginPath();e.moveTo(0,v);e.lineTo(g[0],v);e.stroke();if(this.inputs)for(var m=0;4>m;++m){var l=this.values[m];if(this.inputs[m]&&this.inputs[m].link){e.strokeStyle=n[m];e.beginPath();var a=l[0]*k*-1+v;e.moveTo(0,Math.clamp(a,0,g[1]));for(var b=1;be&&(e=0);if(0!=c.length){var g=[0,0,0];if(0==e)g=c[0];else if(1==e)g=c[c.length-1];else{var p=(c.length-1)* -e,e=c[Math.floor(p)],c=c[Math.floor(p)+1],p=p-Math.floor(p);g[0]=e[0]*(1-p)+c[0]*p;g[1]=e[1]*(1-p)+c[1]*p;g[2]=e[2]*(1-p)+c[2]*p}for(var k in g)g[k]/=255;this.boxcolor=colorToString(g);this.setOutputData(0,g)}};e.registerNodeType("color/palette",n);k.title="Frame";k.desc="Frame viewerew";k.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}];k.prototype.onDrawBackground=function(c){this.frame&&!this.flags.collapsed&&c.drawImage(this.frame,0,0,this.size[0], -this.size[1])};k.prototype.onExecute=function(){this.frame=this.getInputData(0);this.setDirtyCanvas(!0)};k.prototype.onWidget=function(c,e){if("resize"==e.name&&this.frame){var g=this.frame.width,p=this.frame.height;g||null==this.frame.videoWidth||(g=this.frame.videoWidth,p=this.frame.videoHeight);g&&p&&(this.size=[g,p]);this.setDirtyCanvas(!0,!0)}else"view"==e.name&&this.show()};k.prototype.show=function(){showElement&&this.frame&&showElement(this.frame)};e.registerNodeType("graphics/frame",k);x.title= -"Image fade";x.desc="Fades between images";x.widgets=[{name:"resizeA",text:"Resize to A",type:"button"},{name:"resizeB",text:"Resize to B",type:"button"}];x.prototype.onAdded=function(){this.createCanvas();var c=this.canvas.getContext("2d");c.fillStyle="#000";c.fillRect(0,0,this.properties.width,this.properties.height)};x.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};x.prototype.onExecute= -function(){var c=this.canvas.getContext("2d");this.canvas.width=this.canvas.width;var e=this.getInputData(0);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);e=this.getInputData(2);null==e?e=this.properties.fade:this.properties.fade=e;c.globalAlpha=e;e=this.getInputData(1);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);c.globalAlpha=1;this.setOutputData(0,this.canvas);this.setDirtyCanvas(!0)};e.registerNodeType("graphics/imagefade",x);g.title="Crop";g.desc="Crop Image"; -g.prototype.onAdded=function(){this.createCanvas()};g.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};g.prototype.onExecute=function(){var c=this.getInputData(0);c&&(c.width?(this.canvas.getContext("2d").drawImage(c,-this.properties.x,-this.properties.y,c.width*this.properties.scale,c.height*this.properties.scale),this.setOutputData(0,this.canvas)):this.setOutputData(0,null))};g.prototype.onDrawBackground= -function(c){this.flags.collapsed||this.canvas&&c.drawImage(this.canvas,0,0,this.canvas.width,this.canvas.height,0,0,this.size[0],this.size[1])};g.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"scale"==c?(this.properties[c]=parseFloat(e),0==this.properties[c]&&(this.trace("Error in scale"),this.properties[c]=1)):this.properties[c]=parseInt(e);this.createCanvas();return!0};e.registerNodeType("graphics/cropImage",g);C.title="Canvas";C.desc="Canvas to render stuff";C.prototype.onExecute= -function(){var c=this.canvas,e=this.properties.width|0,g=this.properties.height|0;c.width!=e&&(c.width=e);c.height!=g&&(c.height=g);this.properties.autoclear&&this.ctx.clearRect(0,0,c.width,c.height);this.setOutputData(0,c)};C.prototype.onAction=function(c,e){"clear"==c&&this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)};e.registerNodeType("graphics/canvas",C);z.title="DrawImage";z.desc="Draws image into a canvas";z.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e= -this.getInputOrProperty("img");if(e){var g=this.getInputOrProperty("x"),p=this.getInputOrProperty("y");c.getContext("2d").drawImage(e,g,p)}}};e.registerNodeType("graphics/drawImage",z);v.title="DrawRectangle";v.desc="Draws rectangle in canvas";v.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e=this.getInputOrProperty("x"),g=this.getInputOrProperty("y"),p=this.getInputOrProperty("w"),k=this.getInputOrProperty("h");c.getContext("2d").fillRect(e,g,p,k)}};e.registerNodeType("graphics/drawRectangle", -v);E.title="Video";E.desc="Video playback";E.widgets=[{name:"play",text:"PLAY",type:"minibutton"},{name:"stop",text:"STOP",type:"minibutton"},{name:"demo",text:"Demo video",type:"button"},{name:"mute",text:"Mute video",type:"button"}];E.prototype.onExecute=function(){if(this.properties.url&&(this.properties.url!=this._video_url&&this.loadVideo(this.properties.url),this._video&&0!=this._video.width)){var c=this.getInputData(0);c&&0<=c&&1>=c&&(this._video.currentTime=c*this._video.duration,this._video.pause()); -this._video.dirty=!0;this.setOutputData(0,this._video);this.setOutputData(1,this._video.currentTime);this.setOutputData(2,this._video.duration);this.setDirtyCanvas(!0)}};E.prototype.onStart=function(){this.play()};E.prototype.onStop=function(){this.stop()};E.prototype.loadVideo=function(c){this._video_url=c;this.properties.use_proxy&&"http"==c.substr(0,4)&&e.proxy&&(c=e.proxy+c.substr(c.indexOf(":")+3));this._video=document.createElement("video");this._video.src=c;this._video.type="type=video/mp4"; -this._video.muted=!0;this._video.autoplay=!0;var g=this;this._video.addEventListener("loadedmetadata",function(c){g.trace("Duration: "+this.duration+" seconds");g.trace("Size: "+this.videoWidth+","+this.videoHeight);g.setDirtyCanvas(!0);this.width=this.videoWidth;this.height=this.videoHeight});this._video.addEventListener("progress",function(c){});this._video.addEventListener("error",function(c){console.log("Error loading video: "+this.src);g.trace("Error loading video: "+this.src);if(this.error)switch(this.error.code){case this.error.MEDIA_ERR_ABORTED:g.trace("You stopped the video."); -break;case this.error.MEDIA_ERR_NETWORK:g.trace("Network error - please try again later.");break;case this.error.MEDIA_ERR_DECODE:g.trace("Video is broken..");break;case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED:g.trace("Sorry, your browser can't play this video.")}});this._video.addEventListener("ended",function(c){g.trace("Ended.");this.play()})};E.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"url"==c&&""!=e&&this.loadVideo(e);return!0};E.prototype.play=function(){this._video&&this._video.play()}; -E.prototype.playPause=function(){this._video&&(this._video.paused?this.play():this.pause())};E.prototype.stop=function(){this._video&&(this._video.pause(),this._video.currentTime=0)};E.prototype.pause=function(){this._video&&(this.trace("Video paused"),this._video.pause())};E.prototype.onWidget=function(c,e){};e.registerNodeType("graphics/video",E);B.title="Webcam";B.desc="Webcam image";B.is_webcam_open=!1;B.prototype.openStream=function(){function c(g){console.log("Webcam rejected",g);e._webcam_stream= -!1;B.is_webcam_open=!1;e.boxcolor="red";e.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](c);var e=this}};B.prototype.closeStream=function(){if(this._webcam_stream){var c=this._webcam_stream.getTracks();if(c.length)for(var e=0;e=this.size[1]||!this.properties.show||!this._video||(c.save(),c.drawImage(this._video,0,0,this.size[0],this.size[1]),c.restore())};B.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["stream_ready",e.EVENT],["stream_closed",e.EVENT],["stream_error",e.EVENT]]}; -e.registerNodeType("graphics/webcam",B)})(this); -(function(y){function c(){this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",filter:!0};this.size=[c.image_preview_size,c.image_preview_size]}function m(){this.addInput("Texture","Texture");this.properties={flipY:!1};this.size=[c.image_preview_size,c.image_preview_size]}function n(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",generate_mipmaps:!1}}function k(){this.addInput("Texture", +this.img):this.setOutputData(0,null);this.img&&this.img.dirty&&(this.img.dirty=!1)};m.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"url"==c&&""!=e&&this.loadImage(e);return!0};m.prototype.loadImage=function(c,e){if(""==c)this.img=null;else{this.img=document.createElement("img");"http"==c.substr(0,4)&&g.proxy&&(c=g.proxy+c.substr(c.indexOf(":")+3));this.img.src=c;this.boxcolor="#F95";var k=this;this.img.onload=function(){e&&e(this);console.log("Image loaded, size: "+k.img.width+"x"+ +k.img.height);this.dirty=!0;k.boxcolor="#9F9";k.setDirtyCanvas(!0)};this.img.onerror=function(){console.log("error loading the image:"+c)}}};m.prototype.onWidget=function(c,e){"load"==e.name&&this.loadImage(this.properties.url)};m.prototype.onDropFile=function(c){var e=this;this._url&&URL.revokeObjectURL(this._url);this._url=URL.createObjectURL(c);this.properties.url=this._url;this.loadImage(this._url,function(c){e.size[1]=c.height/c.width*e.size[0]})};g.registerNodeType("graphics/image",m);q.title= +"Palette";q.desc="Generates a color";q.prototype.onExecute=function(){var c=[];null!=this.properties.colorA&&c.push(hex2num(this.properties.colorA));null!=this.properties.colorB&&c.push(hex2num(this.properties.colorB));null!=this.properties.colorC&&c.push(hex2num(this.properties.colorC));null!=this.properties.colorD&&c.push(hex2num(this.properties.colorD));var e=this.getInputData(0);null==e&&(e=0.5);1e&&(e=0);if(0!=c.length){var g=[0,0,0];if(0==e)g=c[0];else if(1==e)g=c[c.length-1];else{var n= +(c.length-1)*e,e=c[Math.floor(n)],c=c[Math.floor(n)+1],n=n-Math.floor(n);g[0]=e[0]*(1-n)+c[0]*n;g[1]=e[1]*(1-n)+c[1]*n;g[2]=e[2]*(1-n)+c[2]*n}for(var k in g)g[k]/=255;this.boxcolor=colorToString(g);this.setOutputData(0,g)}};g.registerNodeType("color/palette",q);k.title="Frame";k.desc="Frame viewerew";k.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}];k.prototype.onDrawBackground=function(c){this.frame&&!this.flags.collapsed&&c.drawImage(this.frame, +0,0,this.size[0],this.size[1])};k.prototype.onExecute=function(){this.frame=this.getInputData(0);this.setDirtyCanvas(!0)};k.prototype.onWidget=function(c,e){if("resize"==e.name&&this.frame){var g=this.frame.width,n=this.frame.height;g||null==this.frame.videoWidth||(g=this.frame.videoWidth,n=this.frame.videoHeight);g&&n&&(this.size=[g,n]);this.setDirtyCanvas(!0,!0)}else"view"==e.name&&this.show()};k.prototype.show=function(){showElement&&this.frame&&showElement(this.frame)};g.registerNodeType("graphics/frame", +k);w.title="Image fade";w.desc="Fades between images";w.widgets=[{name:"resizeA",text:"Resize to A",type:"button"},{name:"resizeB",text:"Resize to B",type:"button"}];w.prototype.onAdded=function(){this.createCanvas();var c=this.canvas.getContext("2d");c.fillStyle="#000";c.fillRect(0,0,this.properties.width,this.properties.height)};w.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};w.prototype.onExecute= +function(){var c=this.canvas.getContext("2d");this.canvas.width=this.canvas.width;var e=this.getInputData(0);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);e=this.getInputData(2);null==e?e=this.properties.fade:this.properties.fade=e;c.globalAlpha=e;e=this.getInputData(1);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);c.globalAlpha=1;this.setOutputData(0,this.canvas);this.setDirtyCanvas(!0)};g.registerNodeType("graphics/imagefade",w);e.title="Crop";e.desc="Crop Image"; +e.prototype.onAdded=function(){this.createCanvas()};e.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};e.prototype.onExecute=function(){var c=this.getInputData(0);c&&(c.width?(this.canvas.getContext("2d").drawImage(c,-this.properties.x,-this.properties.y,c.width*this.properties.scale,c.height*this.properties.scale),this.setOutputData(0,this.canvas)):this.setOutputData(0,null))};e.prototype.onDrawBackground= +function(c){this.flags.collapsed||this.canvas&&c.drawImage(this.canvas,0,0,this.canvas.width,this.canvas.height,0,0,this.size[0],this.size[1])};e.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"scale"==c?(this.properties[c]=parseFloat(e),0==this.properties[c]&&(console.error("Error in scale"),this.properties[c]=1)):this.properties[c]=parseInt(e);this.createCanvas();return!0};g.registerNodeType("graphics/cropImage",e);C.title="Canvas";C.desc="Canvas to render stuff";C.prototype.onExecute= +function(){var c=this.canvas,e=this.properties.width|0,g=this.properties.height|0;c.width!=e&&(c.width=e);c.height!=g&&(c.height=g);this.properties.autoclear&&this.ctx.clearRect(0,0,c.width,c.height);this.setOutputData(0,c)};C.prototype.onAction=function(c,e){"clear"==c&&this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)};g.registerNodeType("graphics/canvas",C);z.title="DrawImage";z.desc="Draws image into a canvas";z.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e= +this.getInputOrProperty("img");if(e){var g=this.getInputOrProperty("x"),n=this.getInputOrProperty("y");c.getContext("2d").drawImage(e,g,n)}}};g.registerNodeType("graphics/drawImage",z);u.title="DrawRectangle";u.desc="Draws rectangle in canvas";u.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e=this.getInputOrProperty("x"),g=this.getInputOrProperty("y"),n=this.getInputOrProperty("w"),k=this.getInputOrProperty("h");c.getContext("2d").fillRect(e,g,n,k)}};g.registerNodeType("graphics/drawRectangle", +u);D.title="Video";D.desc="Video playback";D.widgets=[{name:"play",text:"PLAY",type:"minibutton"},{name:"stop",text:"STOP",type:"minibutton"},{name:"demo",text:"Demo video",type:"button"},{name:"mute",text:"Mute video",type:"button"}];D.prototype.onExecute=function(){if(this.properties.url&&(this.properties.url!=this._video_url&&this.loadVideo(this.properties.url),this._video&&0!=this._video.width)){var c=this.getInputData(0);c&&0<=c&&1>=c&&(this._video.currentTime=c*this._video.duration,this._video.pause()); +this._video.dirty=!0;this.setOutputData(0,this._video);this.setOutputData(1,this._video.currentTime);this.setOutputData(2,this._video.duration);this.setDirtyCanvas(!0)}};D.prototype.onStart=function(){this.play()};D.prototype.onStop=function(){this.stop()};D.prototype.loadVideo=function(c){this._video_url=c;var e=c.substr(0,10).indexOf(":"),k="";-1!=e&&(k=c.substr(0,e));e="";k&&(e=c.substr(0,c.indexOf("/",k.length+3)),e=e.substr(k.length+3));this.properties.use_proxy&&k&&g.proxy&&e!=location.host&& +(c=g.proxy+c.substr(c.indexOf(":")+3));this._video=document.createElement("video");this._video.src=c;this._video.type="type=video/mp4";this._video.muted=!0;this._video.autoplay=!0;var n=this;this._video.addEventListener("loadedmetadata",function(c){console.log("Duration: "+this.duration+" seconds");console.log("Size: "+this.videoWidth+","+this.videoHeight);n.setDirtyCanvas(!0);this.width=this.videoWidth;this.height=this.videoHeight});this._video.addEventListener("progress",function(c){console.log("video loading...")}); +this._video.addEventListener("error",function(c){console.error("Error loading video: "+this.src);if(this.error)switch(this.error.code){case this.error.MEDIA_ERR_ABORTED:console.error("You stopped the video.");break;case this.error.MEDIA_ERR_NETWORK:console.error("Network error - please try again later.");break;case this.error.MEDIA_ERR_DECODE:console.error("Video is broken..");break;case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED:console.error("Sorry, your browser can't play this video.")}});this._video.addEventListener("ended", +function(c){console.log("Video Ended.");this.play()})};D.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"url"==c&&""!=e&&this.loadVideo(e);return!0};D.prototype.play=function(){this._video&&this._video.videoWidth&&this._video.play()};D.prototype.playPause=function(){this._video&&(this._video.paused?this.play():this.pause())};D.prototype.stop=function(){this._video&&(this._video.pause(),this._video.currentTime=0)};D.prototype.pause=function(){this._video&&(console.log("Video paused"), +this._video.pause())};D.prototype.onWidget=function(c,e){};g.registerNodeType("graphics/video",D);B.title="Webcam";B.desc="Webcam image";B.is_webcam_open=!1;B.prototype.openStream=function(){function c(g){console.log("Webcam rejected",g);e._webcam_stream=!1;B.is_webcam_open=!1;e.boxcolor="red";e.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](c); +var e=this}};B.prototype.closeStream=function(){if(this._webcam_stream){var c=this._webcam_stream.getTracks();if(c.length)for(var e=0;e=this.size[1]||!this.properties.show|| +!this._video||(c.save(),c.drawImage(this._video,0,0,this.size[0],this.size[1]),c.restore())};B.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["stream_ready",g.EVENT],["stream_closed",g.EVENT],["stream_error",g.EVENT]]};g.registerNodeType("graphics/webcam",B)})(this); +(function(y){function c(){this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",filter:!0};this.size=[c.image_preview_size,c.image_preview_size]}function m(){this.addInput("Texture","Texture");this.properties={flipY:!1};this.size=[c.image_preview_size,c.image_preview_size]}function q(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",generate_mipmaps:!1}}function k(){this.addInput("Texture", "Texture");this.addInput("TextureB","Texture");this.addInput("value","number");this.addOutput("Texture","Texture");this.help="

pixelcode must be vec3, uvcode must be vec2, is optional

\t\t

uv: tex. coords

color: texture colorB: textureB

time: scene time value: input value

For multiline you must type: result = ...

";this.properties={value:1,pixelcode:"color + colorB * value",uvcode:"",precision:c.DEFAULT}; -this.has_error=!1}function x(){this.addOutput("out","Texture");this.properties={code:"",u_value:1,u_color:[1,1,1,1],width:512,height:512,precision:c.DEFAULT};this.properties.code=x.pixel_shader;this._uniforms={u_value:1,u_color:vec4.create(),in_texture:0,texSize:vec2.create(),time:0}}function g(){this.addInput("in","Texture");this.addInput("scale","vec2");this.addInput("offset","vec2");this.addOutput("out","Texture");this.properties={offset:vec2.fromValues(0,0),scale:vec2.fromValues(1,1),precision:c.DEFAULT}} -function C(){this.addInput("in","Texture");this.addInput("warp","Texture");this.addInput("factor","number");this.addOutput("out","Texture");this.properties={factor:0.01,scale:[1,1],offset:[0,0],precision:c.DEFAULT};this._uniforms={u_texture:0,u_textureB:1,u_factor:1,u_scale:vec2.create(),u_offset:vec2.create()}}function z(){this.addInput("Texture","Texture");this.properties={additive:!1,antialiasing:!1,filter:!0,disable_alpha:!1,gamma:1,viewport:[0,0,1,1]};this.size[0]=130}function v(){this.addInput("Texture", -"Texture");this.addOutput("","Texture");this.properties={size:0,generate_mipmaps:!1,precision:c.DEFAULT}}function E(){this.addInput("Texture","Texture");this.addOutput("","Texture");this.properties={iterations:1,generate_mipmaps:!1,precision:c.DEFAULT}}function B(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("avg","vec4");this.addOutput("lum","number");this.properties={use_previous_frame:!0,high_quality:!1};this._uniforms={u_texture:0,u_mipmap_offset:0};this._luminance= -new Float32Array(4)}function e(){this.addInput("Texture","Texture");this.addOutput("min_t","Texture");this.addOutput("max_t","Texture");this.addOutput("min","vec4");this.addOutput("max","vec4");this.properties={mode:"max",use_previous_frame:!0};this._uniforms={u_texture:0};this._max=new Float32Array(4);this._min=new Float32Array(4);this._textures_chain=[]}function q(){this.addInput("in","Texture");this.addInput("factor","Number");this.addOutput("out","Texture");this.properties={factor:0.5};this._uniforms= -{u_texture:0,u_textureB:1,u_factor:this.properties.factor}}function t(){this.addInput("in","Texture");this.addOutput("avg","Texture");this.addOutput("array","Texture");this.properties={samples:64,frames_interval:1};this._uniforms={u_texture:0,u_textureB:1,u_samples:this.properties.samples,u_isamples:1/this.properties.samples};this.frame=0}function A(){this.addInput("Image","image");this.addOutput("","Texture");this.properties={}}function p(){this.addInput("Texture","Texture");this.addInput("LUT", -"Texture");this.addInput("Intensity","number");this.addOutput("","Texture");this.properties={enabled:!0,intensity:1,precision:c.DEFAULT,texture:null};p._shader||(p._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,p.pixel_shader))}function s(){this.addInput("Texture","Texture");this.addOutput("R","Texture");this.addOutput("G","Texture");this.addOutput("B","Texture");this.addOutput("A","Texture");s._shader||(s._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,s.pixel_shader))}function w(){this.addInput("R", +this.has_error=!1}function w(){this.addOutput("out","Texture");this.properties={code:"",u_value:1,u_color:[1,1,1,1],width:512,height:512,precision:c.DEFAULT};this.properties.code=w.pixel_shader;this._uniforms={u_value:1,u_color:vec4.create(),in_texture:0,texSize:vec2.create(),time:0}}function e(){this.addInput("in","Texture");this.addInput("scale","vec2");this.addInput("offset","vec2");this.addOutput("out","Texture");this.properties={offset:vec2.fromValues(0,0),scale:vec2.fromValues(1,1),precision:c.DEFAULT}} +function C(){this.addInput("in","Texture");this.addInput("warp","Texture");this.addInput("factor","number");this.addOutput("out","Texture");this.properties={factor:0.01,scale:[1,1],offset:[0,0],precision:c.DEFAULT};this._uniforms={u_texture:0,u_textureB:1,u_factor:1,u_scale:vec2.create(),u_offset:vec2.create()}}function z(){this.addInput("Texture","Texture");this.properties={additive:!1,antialiasing:!1,filter:!0,disable_alpha:!1,gamma:1,viewport:[0,0,1,1]};this.size[0]=130}function u(){this.addInput("Texture", +"Texture");this.addOutput("","Texture");this.properties={size:0,generate_mipmaps:!1,precision:c.DEFAULT}}function D(){this.addInput("Texture","Texture");this.addOutput("","Texture");this.properties={iterations:1,generate_mipmaps:!1,precision:c.DEFAULT}}function B(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("avg","vec4");this.addOutput("lum","number");this.properties={use_previous_frame:!0,high_quality:!1};this._uniforms={u_texture:0,u_mipmap_offset:0};this._luminance= +new Float32Array(4)}function g(){this.addInput("Texture","Texture");this.addOutput("min_t","Texture");this.addOutput("max_t","Texture");this.addOutput("min","vec4");this.addOutput("max","vec4");this.properties={mode:"max",use_previous_frame:!0};this._uniforms={u_texture:0};this._max=new Float32Array(4);this._min=new Float32Array(4);this._textures_chain=[]}function p(){this.addInput("in","Texture");this.addInput("factor","Number");this.addOutput("out","Texture");this.properties={factor:0.5};this._uniforms= +{u_texture:0,u_textureB:1,u_factor:this.properties.factor}}function s(){this.addInput("in","Texture");this.addOutput("avg","Texture");this.addOutput("array","Texture");this.properties={samples:64,frames_interval:1};this._uniforms={u_texture:0,u_textureB:1,u_samples:this.properties.samples,u_isamples:1/this.properties.samples};this.frame=0}function A(){this.addInput("Image","image");this.addOutput("","Texture");this.properties={}}function n(){this.addInput("Texture","Texture");this.addInput("LUT", +"Texture");this.addInput("Intensity","number");this.addOutput("","Texture");this.properties={enabled:!0,intensity:1,precision:c.DEFAULT,texture:null};n._shader||(n._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,n.pixel_shader))}function v(){this.addInput("Texture","Texture");this.addOutput("R","Texture");this.addOutput("G","Texture");this.addOutput("B","Texture");this.addOutput("A","Texture");v._shader||(v._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,v.pixel_shader))}function x(){this.addInput("R", "Texture");this.addInput("G","Texture");this.addInput("B","Texture");this.addInput("A","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,R:1,G:1,B:1,A:1};this._color=vec4.create();this._uniforms={u_textureR:0,u_textureG:1,u_textureB:2,u_textureA:3,u_color:this._color}}function l(){this.addOutput("Texture","Texture");this._tex_color=vec4.create();this.properties={color:vec4.create(),precision:c.DEFAULT}}function a(){this.addInput("A","color");this.addInput("B","color"); this.addOutput("Texture","Texture");this.properties={angle:0,scale:1,A:[0,0,0],B:[1,1,1],texture_size:32};a._shader||(a._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,a.pixel_shader));this._uniforms={u_angle:0,u_colorA:vec3.create(),u_colorB:vec3.create()}}function b(){this.addInput("A","Texture");this.addInput("B","Texture");this.addInput("Mixer","Texture");this.addOutput("Texture","Texture");this.properties={factor:0.5,size_from_biggest:!0,invert:!1,precision:c.DEFAULT};this._uniforms={u_textureA:0, u_textureB:1,u_textureMix:2,u_mix:vec4.create()}}function d(){this.addInput("Tex.","Texture");this.addOutput("Edges","Texture");this.properties={invert:!0,threshold:!1,factor:1,precision:c.DEFAULT};d._shader||(d._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,d.pixel_shader))}function h(){this.addInput("Texture","Texture");this.addInput("Distance","number");this.addInput("Range","number");this.addOutput("Texture","Texture");this.properties={distance:100,range:50,only_depth:!1,high_precision:!1}; this._uniforms={u_texture:0,u_distance:100,u_range:50,u_camera_planes:null}}function f(){this.addInput("Texture","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,invert:!1};this._uniforms={u_texture:0,u_near:0.1,u_far:1E4}}function K(){this.addInput("Texture","Texture");this.addInput("Iterations","number");this.addInput("Intensity","number");this.addOutput("Blurred","Texture");this.properties={intensity:1,iterations:1,preserve_aspect:!1,scale:[1,1],precision:c.DEFAULT}} function r(){this.addInput("in","Texture");this.addInput("dirt","Texture");this.addOutput("out","Texture");this.addOutput("glow","Texture");this.properties={enabled:!0,intensity:1,persistence:0.99,iterations:16,threshold:0,scale:1,dirt_factor:0.5,precision:c.DEFAULT};this._textures=[];this._uniforms={u_intensity:1,u_texture:0,u_glow_texture:1,u_threshold:0,u_texel_size:vec2.create()}}function H(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={intensity:1, -radius:5}}function I(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={sigma:1.4,k:1.6,p:21.7,epsilon:79,phi:0.017}}function G(){this.addOutput("Webcam","Texture");this.properties={texture_name:"",facingMode:"user"};this.boxcolor="black";this.version=0}function D(){this.addInput("in","Texture");this.addInput("f","number");this.addOutput("out","Texture");this.properties={enabled:!0,factor:1,precision:c.LOW};this._uniforms={u_texture:0,u_factor:1}}function u(){this.addInput("in", +radius:5}}function I(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={sigma:1.4,k:1.6,p:21.7,epsilon:79,phi:0.017}}function G(){this.addOutput("Webcam","Texture");this.properties={texture_name:"",facingMode:"user"};this.boxcolor="black";this.version=0}function E(){this.addInput("in","Texture");this.addInput("f","number");this.addOutput("out","Texture");this.properties={enabled:!0,factor:1,precision:c.LOW};this._uniforms={u_texture:0,u_factor:1}}function t(){this.addInput("in", "Texture");this.addOutput("out","Texture");this.properties={precision:c.LOW,split_channels:!1};this._values=new Uint8Array(1024);this._values.fill(255);this._curve_texture=null;this._uniforms={u_texture:0,u_curve:1,u_range:1};this._must_update=!0;this._points={RGB:[[0,0],[1,1]],R:[[0,0],[1,1]],G:[[0,0],[1,1]],B:[[0,0],[1,1]]};this.curve_editor=null;this.addWidget("toggle","Split Channels",!1,"split_channels");this.addWidget("combo","Channel","RGB",{values:["RGB","R","G","B"]});this.curve_offset=68; this.size=[240,160]}function N(){this.addInput("in","Texture");this.addInput("exp","number");this.addOutput("out","Texture");this.properties={exposition:1,precision:c.LOW};this._uniforms={u_texture:0,u_exposition:1}}function J(){this.addInput("in","Texture");this.addInput("avg","number,Texture");this.addOutput("out","Texture");this.properties={enabled:!0,scale:1,gamma:1,average_lum:1,lum_white:1,precision:c.LOW};this._uniforms={u_texture:0,u_lumwhite2:1,u_igamma:1,u_scale:1,u_average_lum:1}}function M(){this.addOutput("out", "Texture");this.properties={width:512,height:512,seed:0,persistence:0.1,octaves:8,scale:1,offset:[0,0],amplitude:1,precision:c.DEFAULT};this._key=0;this._texture=null;this._uniforms={u_persistence:0.1,u_seed:0,u_offset:vec2.create(),u_scale:1,u_viewport:vec2.create()}}function L(){this.addInput("v");this.addOutput("out","Texture");this.properties={code:L.default_code,width:512,height:512,clear:!0,precision:c.DEFAULT,use_html_canvas:!1};this._temp_texture=this._func=null;this.compileCode()}function O(){this.addInput("in", @@ -434,63 +434,63 @@ if(d){var f=null;"width"==d.name?f=a.width:"height"==d.name?f=a.height:"aspect"= this._last_tex;else{var b=c.generateLowResTexturePreview(this._last_tex);if(!b)return;this._last_preview_tex=this._last_tex;this._canvas=cloneCanvas(b)}this._canvas&&(a.save(),a.webgl||(a.translate(0,this.size[1]),a.scale(1,-1)),a.drawImage(this._canvas,0,0,this.size[0],this.size[1]),a.restore())}},c.generateLowResTexturePreview=function(a){if(!a)return null;var b=c.image_preview_size,d=a;if(a.format==gl.DEPTH_COMPONENT)return null;if(a.width>b||a.height>b)d=this._preview_temp_tex,this._preview_temp_tex|| (this._preview_temp_tex=d=new GL.Texture(b,b,{minFilter:gl.NEAREST})),a.copyTo(d);a=this._preview_canvas;a||(this._preview_canvas=a=createCanvas(b,b));d&&d.toCanvas(a);return a},c.prototype.getResources=function(a){this.properties.name&&(a[this.properties.name]=GL.Texture);return a},c.prototype.onGetInputs=function(){return[["in","Texture"]]},c.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["aspect","number"]]},c.replaceCode=function(a,b){return a.replace(/\{\{[a-zA-Z0-9_]*\}\}/g, function(a){a=a.replace(/[\{\}]/g,"");return b[a]||""})},F.registerNodeType("texture/texture",c),m.title="Preview",m.desc="Show a texture in the graph canvas",m.allow_preview=!1,m.prototype.onDrawBackground=function(a){if(!this.flags.collapsed&&(a.webgl||m.allow_preview)){var b=this.getInputData(0);if(b){var d=null,d=!b.handle&&a.webgl?b:c.generateLowResTexturePreview(b);a.save();this.properties.flipY&&(a.translate(0,this.size[1]),a.scale(1,-1));a.drawImage(d,0,0,this.size[0],this.size[1]);a.restore()}}}, -F.registerNodeType("texture/preview",m),n.title="Save",n.desc="Save a texture in the repository",n.prototype.getPreviewTexture=function(){return this._texture},n.prototype.onExecute=function(){var a=this.getInputData(0);a&&(this.properties.generate_mipmaps&&(a.bind(0),a.setParameter(gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_LINEAR),gl.generateMipmap(a.texture_type),a.unbind(0)),this.properties.name&&(c.storeTexture?c.storeTexture(this.properties.name,a):c.getTexturesContainer()[this.properties.name]= -a),this._texture=a,this.setOutputData(0,a),this.setOutputData(1,this.properties.name))},F.registerNodeType("texture/save",n),k.widgets_info={uvcode:{widget:"code"},pixelcode:{widget:"code"},precision:{widget:"combo",values:c.MODE_VALUES}},k.title="Operation",k.desc="Texture shader operation",k.presets={},k.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:b.properties.show?"Hide Texture":"Show Texture",callback:function(){b.properties.show=!b.properties.show}}]},k.prototype.onPropertyChanged= +F.registerNodeType("texture/preview",m),q.title="Save",q.desc="Save a texture in the repository",q.prototype.getPreviewTexture=function(){return this._texture},q.prototype.onExecute=function(){var a=this.getInputData(0);a&&(this.properties.generate_mipmaps&&(a.bind(0),a.setParameter(gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_LINEAR),gl.generateMipmap(a.texture_type),a.unbind(0)),this.properties.name&&(c.storeTexture?c.storeTexture(this.properties.name,a):c.getTexturesContainer()[this.properties.name]= +a),this._texture=a,this.setOutputData(0,a),this.setOutputData(1,this.properties.name))},F.registerNodeType("texture/save",q),k.widgets_info={uvcode:{widget:"code"},pixelcode:{widget:"code"},precision:{widget:"combo",values:c.MODE_VALUES}},k.title="Operation",k.desc="Texture shader operation",k.presets={},k.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:b.properties.show?"Hide Texture":"Show Texture",callback:function(){b.properties.show=!b.properties.show}}]},k.prototype.onPropertyChanged= function(){this.has_error=!1},k.prototype.onDrawBackground=function(a){this.flags.collapsed||20>=this.size[1]||!this.properties.show||!this._tex||this._tex.gl!=a||(a.save(),a.drawImage(this._tex,0,0,this.size[0],this.size[1]),a.restore())},k.prototype.onExecute=function(){var a=this.getInputData(0);if(this.isOutputConnected(0))if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else{var b=this.getInputData(1);if(this.properties.uvcode||this.properties.pixelcode){var d=512,f=512; a?(d=a.width,f=a.height):b&&(d=b.width,f=b.height);b||(b=GL.Texture.getWhiteTexture());var h=c.getTextureType(this.properties.precision,a);this._tex=a||this._tex?c.getTargetTexture(a||this._tex,this._tex,this.properties.precision):new GL.Texture(d,f,{type:h,format:gl.RGBA,filter:gl.LINEAR});h="";this.properties.uvcode&&(h="uv = "+this.properties.uvcode,-1!=this.properties.uvcode.indexOf(";")&&(h=this.properties.uvcode));var e="";this.properties.pixelcode&&(e="result = "+this.properties.pixelcode, --1!=this.properties.pixelcode.indexOf(";")&&(e=this.properties.pixelcode));var g=this._shader;if(!(this.has_error||g&&this._shader_code==h+"|"+e)){var l=c.replaceCode(k.pixel_shader,{UV_CODE:h,PIXEL_CODE:e});try{g=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,l),this.boxcolor="#00FF00"}catch(p){GL.Shader.dumpErrorToConsole(p,Shader.SCREEN_VERTEX_SHADER,l);this.boxcolor="#FF0000";this.has_error=!0;return}this._shader=g;this._shader_code=h+"|"+e}if(this._shader){var r=this.getInputData(2);null!=r?this.properties.value= -r:r=parseFloat(this.properties.value);var s=this.graph.getTime();this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);gl.disable(gl.BLEND);a&&a.bind(0);b&&b.bind(1);var c=Mesh.getScreenQuad();g.uniforms({u_texture:0,u_textureB:1,value:r,texSize:[d,f],time:s}).draw(c)});this.setOutputData(0,this._tex)}}}},k.pixel_shader="precision highp float;\n\t\t\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec2 texSize;\n\t\tuniform float time;\n\t\tuniform float value;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord;\n\t\t\t{{UV_CODE}};\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\tvec3 color = color4.rgb;\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\tvec3 colorB = color4B.rgb;\n\t\t\tvec3 result = color;\n\t\t\tfloat alpha = 1.0;\n\t\t\t{{PIXEL_CODE}};\n\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t}\n\t\t", +-1!=this.properties.pixelcode.indexOf(";")&&(e=this.properties.pixelcode));var g=this._shader;if(!(this.has_error||g&&this._shader_code==h+"|"+e)){var l=c.replaceCode(k.pixel_shader,{UV_CODE:h,PIXEL_CODE:e});try{g=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,l),this.boxcolor="#00FF00"}catch(n){GL.Shader.dumpErrorToConsole(n,Shader.SCREEN_VERTEX_SHADER,l);this.boxcolor="#FF0000";this.has_error=!0;return}this._shader=g;this._shader_code=h+"|"+e}if(this._shader){var r=this.getInputData(2);null!=r?this.properties.value= +r:r=parseFloat(this.properties.value);var v=this.graph.getTime();this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);gl.disable(gl.BLEND);a&&a.bind(0);b&&b.bind(1);var c=Mesh.getScreenQuad();g.uniforms({u_texture:0,u_textureB:1,value:r,texSize:[d,f],time:v}).draw(c)});this.setOutputData(0,this._tex)}}}},k.pixel_shader="precision highp float;\n\t\t\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec2 texSize;\n\t\tuniform float time;\n\t\tuniform float value;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord;\n\t\t\t{{UV_CODE}};\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\tvec3 color = color4.rgb;\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\tvec3 colorB = color4B.rgb;\n\t\t\tvec3 result = color;\n\t\t\tfloat alpha = 1.0;\n\t\t\t{{PIXEL_CODE}};\n\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t}\n\t\t", k.registerPreset=function(a,b){k.presets[a]=b},k.registerPreset("",""),k.registerPreset("bypass","color"),k.registerPreset("add","color + colorB * value"),k.registerPreset("substract","(color - colorB) * value"),k.registerPreset("mate","mix( color, colorB, color4B.a * value)"),k.registerPreset("invert","vec3(1.0) - color"),k.registerPreset("multiply","color * colorB * value"),k.registerPreset("divide","(color / colorB) / value"),k.registerPreset("difference","abs(color - colorB) * value"),k.registerPreset("max", "max(color, colorB) * value"),k.registerPreset("min","min(color, colorB) * value"),k.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"),k.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"),k.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"),k.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"), -k.prototype.onInspect=function(a){var b=this;a.addCombo("Presets","",{values:Object.keys(k.presets),callback:function(d){var c=k.presets[d];c&&(b.setProperty("pixelcode",c),b.title=d,a.refresh())}})},F.registerNodeType("texture/operation",k),x.title="Shader",x.desc="Texture shader",x.widgets_info={code:{type:"code",lang:"glsl"},precision:{widget:"combo",values:c.MODE_VALUES}},x.prototype.onPropertyChanged=function(a,b){if("code"==a){var d=this.getShader();if(d){var c=d.uniformInfo;if(this.inputs)for(var f= +k.prototype.onInspect=function(a){var b=this;a.addCombo("Presets","",{values:Object.keys(k.presets),callback:function(d){var c=k.presets[d];c&&(b.setProperty("pixelcode",c),b.title=d,a.refresh())}})},F.registerNodeType("texture/operation",k),w.title="Shader",w.desc="Texture shader",w.widgets_info={code:{type:"code",lang:"glsl"},precision:{widget:"combo",values:c.MODE_VALUES}},w.prototype.onPropertyChanged=function(a,b){if("code"==a){var d=this.getShader();if(d){var c=d.uniformInfo;if(this.inputs)for(var f= {},h=0;h lumaMax))\n\t\t\t\tcolor = vec4(rgbA, 1.0);\n\t\t\telse\n\t\t\t\tcolor = vec4(rgbB, 1.0);\n\t\t\tif(u_igamma != 1.0)\n\t\t\t\tcolor.xyz = pow( color.xyz, vec3(u_igamma) );\n\t\t\treturn color;\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\t\t}\n\t\t", -z.gamma_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_igamma;\n\t\tvoid main() {\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\t\t gl_FragColor = color;\n\t\t}\n\t\t",F.registerNodeType("texture/toviewport",z),v.title="Copy",v.desc="Copy Texture",v.widgets_info={size:{widget:"combo",values:[0,32,64,128,256,512,1024,2048]},precision:{widget:"combo", -values:c.MODE_VALUES}},v.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)){if(a){var b=a.width,d=a.height;0!=this.properties.size&&(d=b=this.properties.size);var f=this._temp_texture,h=a.type;this.properties.precision===c.LOW?h=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(h=gl.HIGH_PRECISION_FORMAT);f&&f.width==b&&f.height==d&&f.type==h||(f=gl.LINEAR,this.properties.generate_mipmaps&&isPowerOfTwo(b)&&isPowerOfTwo(d)&&(f=gl.LINEAR_MIPMAP_LINEAR), -this._temp_texture=new GL.Texture(b,d,{type:h,format:gl.RGBA,minFilter:f,magFilter:gl.LINEAR}));a.copyTo(this._temp_texture);this.properties.generate_mipmaps&&(this._temp_texture.bind(0),gl.generateMipmap(this._temp_texture.texture_type),this._temp_texture.unbind(0))}this.setOutputData(0,this._temp_texture)}},F.registerNodeType("texture/copy",v),E.title="Downsample",E.desc="Downsample Texture",E.widgets_info={iterations:{type:"number",step:1,precision:0,min:0},precision:{widget:"combo",values:c.MODE_VALUES}}, -E.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)&&a&&a.texture_type===GL.TEXTURE_2D)if(1>this.properties.iterations)this.setOutputData(0,a);else{var b=E._shader;b||(E._shader=b=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,E.pixel_shader));var d=a.width|0,f=a.height|0,h=a.type;this.properties.precision===c.LOW?h=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(h=gl.HIGH_PRECISION_FORMAT);var e=this.properties.iterations||1,g=a,l= -null,p=[],a={type:h,format:a.format},h=vec2.create(),r={u_offset:h};this._texture&&GL.Texture.releaseTemporary(this._texture);for(var k=0;k>1||0;f=f>>1||0;l=GL.Texture.getTemporary(d,f,a);p.push(l);g.setParameter(GL.TEXTURE_MAG_FILTER,GL.NEAREST);g.copyTo(l,b,r);if(1==d&&1==f)break;g=l}this._texture=p.pop();for(k=0;kthis.properties.iterations)this.setOutputData(0,a);else{var b=D._shader;b||(D._shader=b=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,D.pixel_shader));var d=a.width|0,f=a.height|0,h=a.type;this.properties.precision===c.LOW?h=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(h=gl.HIGH_PRECISION_FORMAT);var e=this.properties.iterations||1,g=a,l= +null,n=[],a={type:h,format:a.format},h=vec2.create(),r={u_offset:h};this._texture&&GL.Texture.releaseTemporary(this._texture);for(var k=0;k>1||0;f=f>>1||0;l=GL.Texture.getTemporary(d,f,a);n.push(l);g.setParameter(GL.TEXTURE_MAG_FILTER,GL.NEAREST);g.copyTo(l,b,r);if(1==d&&1==f)break;g=l}this._texture=n.pop();for(k=0;k>=2,c++,1!=d););a.copyTo(this._textures_chain[0]);for(var c=1;c<=this._textures_chain.length;++c)a=this._textures_chain[c];var f=e._shader,h=this._uniforms;h.u_mipmap_offset=this.properties.mipmap_offset;gl.disable(gl.DEPTH_TEST); -gl.disable(gl.BLEND);this._temp_texture.drawTo(function(){a.toViewport(f,h)})}},e.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform mat4 u_samples_a;\n\t\tuniform mat4 u_samples_b;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_mipmap_offset;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t//random average\n\t\t\tfor(int i = 0; i < 4; ++i)\n\t\t\t\tfor(int j = 0; j < 4; ++j)\n\t\t\t\t{\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t}\n\t\t gl_FragColor = color * 0.03125;\n\t\t}\n\t\t", -q.title="Smooth",q.desc="Smooth texture over time",q.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){q._shader||(q._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,q.pixel_shader));var b=this._temp_texture;b&&b.type==a.type&&b.width==a.width&&b.height==a.height||(b={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(a.width,a.height,b),this._temp_texture2=new GL.Texture(a.width,a.height,b),a.copyTo(this._temp_texture2));var b= -this._temp_texture,d=this._temp_texture2,c=q._shader,f=this._uniforms;f.u_factor=1-this.getInputOrProperty("factor");gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);b.drawTo(function(){d.bind(1);a.toViewport(c,f)});this.setOutputData(0,b);this._temp_texture=d;this._temp_texture2=b}},q.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_factor;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tgl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\t\t}\n\t\t", -F.registerNodeType("texture/temporal_smooth",q),t.title="Lineal Avg Smooth",t.desc="Smooth texture linearly over time",t["@samples"]={type:"number",min:1,max:64,step:1,precision:1},t.prototype.getPreviewTexture=function(){return this._temp_texture2},t.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){t._shader||(t._shader_copy=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,t.pixel_shader_copy),t._shader_avg=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,t.pixel_shader_avg)); -var b=Math.clamp(this.properties.samples,0,64),d=this.frame,c=this.properties.frames_interval;if(0==c||0==d%c){d=this._temp_texture;d&&d.type==a.type&&d.width==b||(d={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(b,1,d),this._temp_texture2=new GL.Texture(b,1,d),this._temp_texture_out=new GL.Texture(1,1,d));var f=this._temp_texture,h=this._temp_texture2,e=t._shader_copy,g=t._shader_avg,l=this._uniforms;l.u_samples=b;l.u_isamples=1/b;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST); -f.drawTo(function(){h.bind(1);a.toViewport(e,l)});this._temp_texture_out.drawTo(function(){f.toViewport(g,l)});this.setOutputData(0,this._temp_texture_out);this._temp_texture=h;this._temp_texture2=f}else this.setOutputData(0,this._temp_texture_out);this.setOutputData(1,this._temp_texture2);this.frame++}},t.pixel_shader_copy="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tif( v_coord.x <= u_isamples )\n\t\t\t\tgl_FragColor = texture2D( u_texture, vec2(0.5) );\n\t\t\telse\n\t\t\t\tgl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\t\t}\n\t\t", -t.pixel_shader_avg="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform int u_samples;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\tfor(int i = 0; i < 64; ++i)\n\t\t\t{\n\t\t\t\tcolor += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\t\t\t\tif(i == (u_samples - 1))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tgl_FragColor = color * u_isamples;\n\t\t}\n\t\t",F.registerNodeType("texture/linear_avg_smooth", -t),A.title="Image to Texture",A.desc="Uploads an image to the GPU",A.prototype.onExecute=function(){var a=this.getInputData(0);if(a){var b=a.videoWidth||a.width,d=a.videoHeight||a.height;if(a.gltexture)this.setOutputData(0,a.gltexture);else{var c=this._temp_texture;c&&c.width==b&&c.height==d||(this._temp_texture=new GL.Texture(b,d,{format:gl.RGBA,filter:gl.LINEAR}));try{this._temp_texture.uploadImage(a)}catch(f){console.error("image comes from an unsafe location, cannot be uploaded to webgl: "+f); -return}this.setOutputData(0,this._temp_texture)}}},F.registerNodeType("texture/imageToTexture",A),p.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},p.title="LUT",p.desc="Apply LUT to Texture",p.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture)); -if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.bindTexture(gl.TEXTURE_2D,null);var d=this.properties.intensity;this.isInputConnected(2)&&(this.properties.intensity=d=this.getInputData(2));this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);this._tex.drawTo(function(){b.bind(1);a.toViewport(p._shader,{u_texture:0,u_textureB:1, -u_amount:d})});this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}},p.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_amount;\n\t\t\n\t\tvoid main() {\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\t\t\t mediump vec2 quad1;\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\t\t\t mediump vec2 quad2;\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\t\t\t highp vec2 texPos1;\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t highp vec2 texPos2;\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\t\t}\n\t\t", -F.registerNodeType("texture/LUT",p),s.title="Texture to Channels",s.desc="Split texture channels",s.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this._channels||(this._channels=Array(4));for(var b=gl.RGB,d=0,c=0;4>c;c++)this.isOutputConnected(c)?(this._channels[c]&&this._channels[c].width==a.width&&this._channels[c].height==a.height&&this._channels[c].type==a.type&&this._channels[c].format==b||(this._channels[c]=new GL.Texture(a.width,a.height,{type:a.type,format:b,filter:gl.LINEAR})), -d++):this._channels[c]=null;if(d){gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);for(var f=Mesh.getScreenQuad(),h=s._shader,e=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],c=0;4>c;c++)this._channels[c]&&(this._channels[c].drawTo(function(){a.bind(0);h.uniforms({u_texture:0,u_mask:e[c]}).draw(f)}),this.setOutputData(c,this._channels[c]))}}},s.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec4 u_mask;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\t\t}\n\t\t", -F.registerNodeType("texture/textureChannels",s),w.title="Channels to Texture",w.desc="Split texture channels",w.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},w.prototype.onExecute=function(){var a=c.getWhiteTexture(),b=this.getInputData(0)||a,d=this.getInputData(1)||a,f=this.getInputData(2)||a,h=this.getInputData(3)||a;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad();w._shader||(w._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,w.pixel_shader));var g=w._shader, -a=Math.max(b.width,d.width,f.width,h.width),l=Math.max(b.height,d.height,f.height,h.height),p=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._texture&&this._texture.width==a&&this._texture.height==l&&this._texture.type==p||(this._texture=new GL.Texture(a,l,{type:p,format:gl.RGBA,filter:gl.LINEAR}));a=this._color;a[0]=this.properties.R;a[1]=this.properties.G;a[2]=this.properties.B;a[3]=this.properties.A;var r=this._uniforms;this._texture.drawTo(function(){b.bind(0); -d.bind(1);f.bind(2);h.bind(3);g.uniforms(r).draw(e)});this.setOutputData(0,this._texture)},w.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureR;\n\t\tuniform sampler2D u_textureG;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform vec4 u_color;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = u_color * vec4( \t\t\t\t\ttexture2D(u_textureR, v_coord).r,\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\t\t}\n\t\t", -F.registerNodeType("texture/channelsTexture",w),l.title="Color",l.desc="Generates a 1x1 texture with a constant color",l.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},l.prototype.onDrawBackground=function(a){var b=this.properties.color;a.fillStyle="rgb("+Math.floor(255*Math.clamp(b[0],0,1))+","+Math.floor(255*Math.clamp(b[1],0,1))+","+Math.floor(255*Math.clamp(b[2],0,1))+")";this.flags.collapsed?this.boxcolor=a.fillStyle:a.fillRect(0,0,this.size[0],this.size[1])},l.prototype.onExecute= +F.registerNodeType("texture/average",B),g.widgets_info={mode:{widget:"combo",values:["min","max","avg"]}},g.title="MinMax",g.desc="Compute the scene min max",g.prototype.onExecute=function(){this.properties.use_previous_frame||this.update();this.setOutputData(0,this._temp_texture);this.setOutputData(1,this._luminance)},g.prototype.onPreRenderExecute=function(){this.update()},g.prototype.update=function(){var a=this.getInputData(0);if(a&&(this.isOutputConnected(0)||this.isOutputConnected(1))){g._shader|| +(g._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,g.pixel_shader));var b=gl.UNSIGNED_BYTE;a.type!=b&&(b=gl.FLOAT);var d=512;if(!this._textures_chain.length||this._textures_chain[0].type!=b)for(;c&&(this._textures_chain[c]=new GL.Texture(d,d,{type:b,format:gl.RGBA,filter:gl.NEAREST}),d>>=2,c++,1!=d););a.copyTo(this._textures_chain[0]);for(var c=1;c<=this._textures_chain.length;++c)a=this._textures_chain[c];var f=g._shader,h=this._uniforms;h.u_mipmap_offset=this.properties.mipmap_offset;gl.disable(gl.DEPTH_TEST); +gl.disable(gl.BLEND);this._temp_texture.drawTo(function(){a.toViewport(f,h)})}},g.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform mat4 u_samples_a;\n\t\tuniform mat4 u_samples_b;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_mipmap_offset;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t//random average\n\t\t\tfor(int i = 0; i < 4; ++i)\n\t\t\t\tfor(int j = 0; j < 4; ++j)\n\t\t\t\t{\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t}\n\t\t gl_FragColor = color * 0.03125;\n\t\t}\n\t\t", +p.title="Smooth",p.desc="Smooth texture over time",p.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){p._shader||(p._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,p.pixel_shader));var b=this._temp_texture;b&&b.type==a.type&&b.width==a.width&&b.height==a.height||(b={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(a.width,a.height,b),this._temp_texture2=new GL.Texture(a.width,a.height,b),a.copyTo(this._temp_texture2));var b= +this._temp_texture,d=this._temp_texture2,c=p._shader,f=this._uniforms;f.u_factor=1-this.getInputOrProperty("factor");gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);b.drawTo(function(){d.bind(1);a.toViewport(c,f)});this.setOutputData(0,b);this._temp_texture=d;this._temp_texture2=b}},p.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_factor;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tgl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\t\t}\n\t\t", +F.registerNodeType("texture/temporal_smooth",p),s.title="Lineal Avg Smooth",s.desc="Smooth texture linearly over time",s["@samples"]={type:"number",min:1,max:64,step:1,precision:1},s.prototype.getPreviewTexture=function(){return this._temp_texture2},s.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){s._shader||(s._shader_copy=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,s.pixel_shader_copy),s._shader_avg=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,s.pixel_shader_avg)); +var b=Math.clamp(this.properties.samples,0,64),d=this.frame,c=this.properties.frames_interval;if(0==c||0==d%c){d=this._temp_texture;d&&d.type==a.type&&d.width==b||(d={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(b,1,d),this._temp_texture2=new GL.Texture(b,1,d),this._temp_texture_out=new GL.Texture(1,1,d));var f=this._temp_texture,h=this._temp_texture2,e=s._shader_copy,g=s._shader_avg,l=this._uniforms;l.u_samples=b;l.u_isamples=1/b;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST); +f.drawTo(function(){h.bind(1);a.toViewport(e,l)});this._temp_texture_out.drawTo(function(){f.toViewport(g,l)});this.setOutputData(0,this._temp_texture_out);this._temp_texture=h;this._temp_texture2=f}else this.setOutputData(0,this._temp_texture_out);this.setOutputData(1,this._temp_texture2);this.frame++}},s.pixel_shader_copy="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tif( v_coord.x <= u_isamples )\n\t\t\t\tgl_FragColor = texture2D( u_texture, vec2(0.5) );\n\t\t\telse\n\t\t\t\tgl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\t\t}\n\t\t", +s.pixel_shader_avg="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform int u_samples;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\tfor(int i = 0; i < 64; ++i)\n\t\t\t{\n\t\t\t\tcolor += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\t\t\t\tif(i == (u_samples - 1))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tgl_FragColor = color * u_isamples;\n\t\t}\n\t\t",F.registerNodeType("texture/linear_avg_smooth", +s),A.title="Image to Texture",A.desc="Uploads an image to the GPU",A.prototype.onExecute=function(){var a=this.getInputData(0);if(a){var b=a.videoWidth||a.width,d=a.videoHeight||a.height;if(a.gltexture)this.setOutputData(0,a.gltexture);else{var c=this._temp_texture;c&&c.width==b&&c.height==d||(this._temp_texture=new GL.Texture(b,d,{format:gl.RGBA,filter:gl.LINEAR}));try{this._temp_texture.uploadImage(a)}catch(f){console.error("image comes from an unsafe location, cannot be uploaded to webgl: "+f); +return}this.setOutputData(0,this._temp_texture)}}},F.registerNodeType("texture/imageToTexture",A),n.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},n.title="LUT",n.desc="Apply LUT to Texture",n.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture)); +if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.bindTexture(gl.TEXTURE_2D,null);var d=this.properties.intensity;this.isInputConnected(2)&&(this.properties.intensity=d=this.getInputData(2));this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);this._tex.drawTo(function(){b.bind(1);a.toViewport(n._shader,{u_texture:0,u_textureB:1, +u_amount:d})});this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}},n.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_amount;\n\t\t\n\t\tvoid main() {\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\t\t\t mediump vec2 quad1;\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\t\t\t mediump vec2 quad2;\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\t\t\t highp vec2 texPos1;\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t highp vec2 texPos2;\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\t\t}\n\t\t", +F.registerNodeType("texture/LUT",n),v.title="Texture to Channels",v.desc="Split texture channels",v.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this._channels||(this._channels=Array(4));for(var b=gl.RGB,d=0,c=0;4>c;c++)this.isOutputConnected(c)?(this._channels[c]&&this._channels[c].width==a.width&&this._channels[c].height==a.height&&this._channels[c].type==a.type&&this._channels[c].format==b||(this._channels[c]=new GL.Texture(a.width,a.height,{type:a.type,format:b,filter:gl.LINEAR})), +d++):this._channels[c]=null;if(d){gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);for(var f=Mesh.getScreenQuad(),h=v._shader,e=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],c=0;4>c;c++)this._channels[c]&&(this._channels[c].drawTo(function(){a.bind(0);h.uniforms({u_texture:0,u_mask:e[c]}).draw(f)}),this.setOutputData(c,this._channels[c]))}}},v.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec4 u_mask;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\t\t}\n\t\t", +F.registerNodeType("texture/textureChannels",v),x.title="Channels to Texture",x.desc="Split texture channels",x.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},x.prototype.onExecute=function(){var a=c.getWhiteTexture(),b=this.getInputData(0)||a,d=this.getInputData(1)||a,f=this.getInputData(2)||a,h=this.getInputData(3)||a;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad();x._shader||(x._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,x.pixel_shader));var g=x._shader, +a=Math.max(b.width,d.width,f.width,h.width),l=Math.max(b.height,d.height,f.height,h.height),n=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._texture&&this._texture.width==a&&this._texture.height==l&&this._texture.type==n||(this._texture=new GL.Texture(a,l,{type:n,format:gl.RGBA,filter:gl.LINEAR}));a=this._color;a[0]=this.properties.R;a[1]=this.properties.G;a[2]=this.properties.B;a[3]=this.properties.A;var r=this._uniforms;this._texture.drawTo(function(){b.bind(0); +d.bind(1);f.bind(2);h.bind(3);g.uniforms(r).draw(e)});this.setOutputData(0,this._texture)},x.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureR;\n\t\tuniform sampler2D u_textureG;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform vec4 u_color;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = u_color * vec4( \t\t\t\t\ttexture2D(u_textureR, v_coord).r,\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\t\t}\n\t\t", +F.registerNodeType("texture/channelsTexture",x),l.title="Color",l.desc="Generates a 1x1 texture with a constant color",l.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},l.prototype.onDrawBackground=function(a){var b=this.properties.color;a.fillStyle="rgb("+Math.floor(255*Math.clamp(b[0],0,1))+","+Math.floor(255*Math.clamp(b[1],0,1))+","+Math.floor(255*Math.clamp(b[2],0,1))+")";this.flags.collapsed?this.boxcolor=a.fillStyle:a.fillRect(0,0,this.size[0],this.size[1])},l.prototype.onExecute= function(){var a=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._tex&&this._tex.type==a||(this._tex=new GL.Texture(1,1,{format:gl.RGBA,type:a,minFilter:gl.NEAREST}));a=this.properties.color;if(this.inputs)for(var b=0;ba.width?d: -a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad(),g=null,l=this._uniforms;f?(g=b._shader_tex,g||(g=b._shader_tex=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader,{MIX_TEX:""}))):(g=b._shader_factor,g||(g=b._shader_factor=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader)),h=null==h?this.properties.factor:h,l.u_mix.set([h,h,h,h]));var p=this.properties.invert;this._tex.drawTo(function(){a.bind(p?1:0);d.bind(p?0:1);f&&f.bind(2); +a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad(),g=null,l=this._uniforms;f?(g=b._shader_tex,g||(g=b._shader_tex=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader,{MIX_TEX:""}))):(g=b._shader_factor,g||(g=b._shader_factor=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader)),h=null==h?this.properties.factor:h,l.u_mix.set([h,h,h,h]));var n=this.properties.invert;this._tex.drawTo(function(){a.bind(n?1:0);d.bind(n?0:1);f&&f.bind(2); g.uniforms(l).draw(e)});this.setOutputData(0,this._tex)}}},b.prototype.onGetInputs=function(){return[["factor","number"]]},b.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform sampler2D u_textureB;\n\t\t#ifdef MIX_TEX\n\t\t\tuniform sampler2D u_textureMix;\n\t\t#else\n\t\t\tuniform vec4 u_mix;\n\t\t#endif\n\t\t\n\t\tvoid main() {\n\t\t\t#ifdef MIX_TEX\n\t\t\t vec4 f = texture2D(u_textureMix, v_coord);\n\t\t\t#else\n\t\t\t vec4 f = u_mix;\n\t\t\t#endif\n\t\t gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\t\t}\n\t\t", F.registerNodeType("texture/mix",b),d.title="Edges",d.desc="Detects edges",d.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},d.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else if(a){this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var b=Mesh.getScreenQuad(),f=d._shader,h=this.properties.invert,e=this.properties.factor, g=this.properties.threshold?1:0;this._tex.drawTo(function(){a.bind(0);f.uniforms({u_texture:0,u_isize:[1/a.width,1/a.height],u_factor:e,u_threshold:g,u_invert:h?1:0}).draw(b)});this.setOutputData(0,this._tex)}}},d.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_isize;\n\t\tuniform int u_invert;\n\t\tuniform float u_factor;\n\t\tuniform float u_threshold;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 center = texture2D(u_texture, v_coord);\n\t\t\tvec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\t\t\tvec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\t\t\tvec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\t\t\tvec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\t\t\tvec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\t\t\tdiff *= u_factor;\n\t\t\tif(u_invert == 1)\n\t\t\t\tdiff.xyz = vec3(1.0) - diff.xyz;\n\t\t\tif( u_threshold == 0.0 )\n\t\t\t\tgl_FragColor = vec4( diff.xyz, center.a );\n\t\t\telse\n\t\t\t\tgl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\t\t}\n\t\t", @@ -504,11 +504,11 @@ F.registerNodeType("texture/linear_depth",f),K.title="Blur",K.desc="Blur a textu (d=this.getInputData(1),this.properties.iterations=d);d=Math.min(Math.floor(d),K.max_iterations);if(0==d)this.setOutputData(0,a);else{var c=this.properties.intensity;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.intensity=c);var f=F.camera_aspect;f||void 0===window.gl||(f=gl.canvas.height/gl.canvas.width);f||(f=1);var f=this.properties.preserve_aspect?f:1,h=this.properties.scale||[1,1];a.applyBlur(f*h[0],h[1],c,b);for(a=1;a>=1;1<(d|0)&&(d>>=1);if(2>b)break;p=g[m]=GL.Texture.getTemporary(b,d,f);n[0]=1/k.width;n[1]=1/k.height;k.blit(p,l.uniforms(e));k=p}this.isOutputConnected(2)&&(b=this._average_texture,b&&b.type==a.type&&b.format==a.format||(b=this._average_texture=new GL.Texture(1,1,{type:a.type,format:a.format,filter:gl.LINEAR})),n[0]=1/k.width,n[1]=1/k.height,e.u_intensity=q,e.u_delta=1,k.blit(b,l.uniforms(e)),this.setOutputData(2,b));gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE);e.u_intensity= -this.getInputOrProperty("persistence");e.u_delta=0.5;for(m-=2;0<=m;m--)p=g[m],g[m]=null,n[0]=1/k.width,n[1]=1/k.height,k.blit(p,l.uniforms(e)),GL.Texture.releaseTemporary(k),k=p;gl.disable(gl.BLEND);this.isOutputConnected(1)&&(g=this._glow_texture,g&&g.width==a.width&&g.height==a.height&&g.type==h&&g.format==a.format||(g=this._glow_texture=new GL.Texture(a.width,a.height,{type:h,format:a.format,filter:gl.LINEAR})),k.blit(g),this.setOutputData(1,g));if(this.isOutputConnected(0)){g=this._final_texture; -g&&g.width==a.width&&g.height==a.height&&g.type==h&&g.format==a.format||(g=this._final_texture=new GL.Texture(a.width,a.height,{type:h,format:a.format,filter:gl.LINEAR}));var u=this.getInputData(1),w=this.getInputOrProperty("dirt_factor");e.u_intensity=q;l=u?r._dirt_final_shader:r._final_shader;l||(l=u?r._dirt_final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,r.final_pixel_shader,{USE_DIRT:""}):r._final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,r.final_pixel_shader));g.drawTo(function(){a.bind(0); -k.bind(1);u&&(l.setUniform("u_dirt_factor",w),l.setUniform("u_dirt_texture",u.bind(2)));l.toViewport(e)});this.setOutputData(0,g)}GL.Texture.releaseTemporary(k)}},r.cut_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform float u_threshold;\n\tvoid main() {\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\t}",r.scale_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\t}", +l||(l=r._cut_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,r.cut_pixel_shader));gl.disable(gl.DEPTH_TEST);gl.disable(gl.BLEND);e.u_threshold=this.getInputOrProperty("threshold");var n=g[0]=GL.Texture.getTemporary(b,d,f);a.blit(n,l.uniforms(e));var k=n,v=this.getInputOrProperty("iterations"),v=Math.clamp(v,1,16)|0,m=e.u_texel_size,p=this.getInputOrProperty("intensity");e.u_intensity=1;e.u_delta=this.properties.scale;l=r._shader;l||(l=r._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,r.scale_pixel_shader)); +for(var q=1;q>=1;1<(d|0)&&(d>>=1);if(2>b)break;n=g[q]=GL.Texture.getTemporary(b,d,f);m[0]=1/k.width;m[1]=1/k.height;k.blit(n,l.uniforms(e));k=n}this.isOutputConnected(2)&&(b=this._average_texture,b&&b.type==a.type&&b.format==a.format||(b=this._average_texture=new GL.Texture(1,1,{type:a.type,format:a.format,filter:gl.LINEAR})),m[0]=1/k.width,m[1]=1/k.height,e.u_intensity=p,e.u_delta=1,k.blit(b,l.uniforms(e)),this.setOutputData(2,b));gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE);e.u_intensity= +this.getInputOrProperty("persistence");e.u_delta=0.5;for(q-=2;0<=q;q--)n=g[q],g[q]=null,m[0]=1/k.width,m[1]=1/k.height,k.blit(n,l.uniforms(e)),GL.Texture.releaseTemporary(k),k=n;gl.disable(gl.BLEND);this.isOutputConnected(1)&&(g=this._glow_texture,g&&g.width==a.width&&g.height==a.height&&g.type==h&&g.format==a.format||(g=this._glow_texture=new GL.Texture(a.width,a.height,{type:h,format:a.format,filter:gl.LINEAR})),k.blit(g),this.setOutputData(1,g));if(this.isOutputConnected(0)){g=this._final_texture; +g&&g.width==a.width&&g.height==a.height&&g.type==h&&g.format==a.format||(g=this._final_texture=new GL.Texture(a.width,a.height,{type:h,format:a.format,filter:gl.LINEAR}));var s=this.getInputData(1),t=this.getInputOrProperty("dirt_factor");e.u_intensity=p;l=s?r._dirt_final_shader:r._final_shader;l||(l=s?r._dirt_final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,r.final_pixel_shader,{USE_DIRT:""}):r._final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,r.final_pixel_shader));g.drawTo(function(){a.bind(0); +k.bind(1);s&&(l.setUniform("u_dirt_factor",t),l.setUniform("u_dirt_texture",s.bind(2)));l.toViewport(e)});this.setOutputData(0,g)}GL.Texture.releaseTemporary(k)}},r.cut_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform float u_threshold;\n\tvoid main() {\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\t}",r.scale_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\t}", r.final_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform sampler2D u_glow_texture;\n\t#ifdef USE_DIRT\n\t\tuniform sampler2D u_dirt_texture;\n\t#endif\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\tuniform float u_dirt_factor;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tvec4 glow = sampleBox( v_coord );\n\t\t#ifdef USE_DIRT\n\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\t\t#endif\n\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\t}", F.registerNodeType("texture/glow",r),H.title="Kuwahara Filter",H.desc="Filters a texture giving an artistic oil canvas painting",H.max_radius=10,H._shaders=[],H.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));b=this.properties.radius;b=Math.min(Math.floor(b),H.max_radius);if(0== b)this.setOutputData(0,a);else{var d=this.properties.intensity,c=F.camera_aspect;c||void 0===window.gl||(c=gl.canvas.height/gl.canvas.width);c||(c=1);c=this.properties.preserve_aspect?c:1;H._shaders[b]||(H._shaders[b]=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,H.pixel_shader,{RADIUS:b.toFixed(0)}));var f=H._shaders[b],h=GL.Mesh.getScreenQuad();a.bind(0);this._temp_texture.drawTo(function(){f.uniforms({u_texture:0,u_intensity:d,u_resolution:[a.width,a.height],u_iResolution:[1/a.width,1/a.height]}).draw(h)}); @@ -519,105 +519,105 @@ F.registerNodeType("texture/xDoG",I),G.title="Webcam",G.desc="Webcam texture",G. this._webcam_stream.getTracks();if(a.length)for(var b=0;b=this.size[1]||!this._video||(a.save(),a.webgl?this._video_texture&&a.drawImage(this._video_texture,0,0,this.size[0],this.size[1]):a.drawImage(this._video, 0,0,this.size[0],this.size[1]),a.restore())},G.prototype.onExecute=function(){null!=this._webcam_stream||this._waiting_confirmation||this.openStream();if(this._video&&this._video.videoWidth){var a=this._video.videoWidth,b=this._video.videoHeight,d=this._video_texture;d&&d.width==a&&d.height==b||(this._video_texture=new GL.Texture(a,b,{format:gl.RGB,filter:gl.LINEAR}));this._video_texture.uploadImage(this._video);this._video_texture.version=++this.version;this.properties.texture_name&&(c.getTexturesContainer()[this.properties.texture_name]= -this._video_texture);this.setOutputData(0,this._video_texture);for(a=1;aMath.abs(d))return c[1];d=(a-c[0])/d;return c[1]*(1-d)+f[1]*d}}return 0}},u.prototype.updateCurve=function(){for(var a=this._values,b=a.length/4,d=this.properties.split_channels,c=0;cMath.abs(d))return c[1];d=(a-c[0])/d;return c[1]*(1-d)+f[1]*d}}return 0}},t.prototype.updateCurve=function(){for(var a=this._values,b=a.length/4,d=this.properties.split_channels,c=0;c=f;){e=0.5*(g+f)|0;c=a[e];if(c==d)break;if(f==g-1)return f;c=f;){e=0.5*(g+f)|0;c=a[e];if(c==d)break;if(f==g-1)return f;ca&&(a=1);this.points&&this.points.length==3*a||(this.points=new Float32Array(3*a));this.properties.generate_normals?this.normals&&this.normals.length==this.points.length||(this.normals=new Float32Array(this.points.length)):this.normals=null;var d=this._last_radius||this.properties.radius,c=this.properties.mode,f=this.getInputData(0);this._old_obj_version=f?f._version:null;this.points=m.generatePoints(d,a,c,this.points, -this.normals,this.properties.regular,f);this.version++};m.generatePoints=function(a,d,c,f,e,g,l){var p=3*d;f&&f.length==p||(f=new Float32Array(p));var k=new Float32Array(3),s=new Float32Array([0,1,0]);if(g)if(c==m.RECTANGLE){p=Math.floor(Math.sqrt(d));for(d=0;dg||tl&&lp))break}this.geometry.indices=this.indices=new Uint32Array(k)}this.indices&&this.indices.length?(this.geometry.indices=this.indices,this.setOutputData(0,this.geometry)):this.setOutputData(0,null)}};A.registerNodeType("geometry/connectPoints",v);"undefined"!=typeof GL&&(E.title="to geometry",E.desc="converts a mesh to geometry",E.prototype.onExecute= -function(){var a=this.getInputData(0);if(a){if(a!=this.last_mesh){this.last_mesh=a;for(i in a.vertexBuffers)this.geometry[i]=a.vertexBuffers[i].data;a.indexBuffers.triangles&&(this.geometry.indices=a.indexBuffers.triangles.data);this.geometry._id=c();this.geometry._version=0}this.setOutputData(0,this.geometry);this.geometry&&this.setOutputData(1,this.geometry.vertices)}},A.registerNodeType("geometry/toGeometry",E),B.title="Geo to Mesh",B.prototype.updateMesh=function(a){this.mesh||(this.mesh=new GL.Mesh); +g=this.vertices;g&&this.vertices.length==a.vertices.length?g.set(a.vertices):g=this.vertices=new Float32Array(a.vertices);for(f=0;fg||xl&&ln))break}this.geometry.indices=this.indices=new Uint32Array(k)}this.indices&&this.indices.length?(this.geometry.indices=this.indices,this.setOutputData(0,this.geometry)):this.setOutputData(0,null)}};A.registerNodeType("geometry/connectPoints",u);"undefined"!=typeof GL&&(D.title="to geometry",D.desc="converts a mesh to geometry",D.prototype.onExecute= +function(){var a=this.getInputData(0);if(a){if(a!=this.last_mesh){this.last_mesh=a;for(i in a.vertexBuffers)this.geometry[i]=a.vertexBuffers[i].data;a.indexBuffers.triangles&&(this.geometry.indices=a.indexBuffers.triangles.data);this.geometry._id=c();this.geometry._version=0}this.setOutputData(0,this.geometry);this.geometry&&this.setOutputData(1,this.geometry.vertices)}},A.registerNodeType("geometry/toGeometry",D),B.title="Geo to Mesh",B.prototype.updateMesh=function(a){this.mesh||(this.mesh=new GL.Mesh); for(var d in a)if("_"!=d[0]){var c=a[d],f=GL.Mesh.common_buffers[d];if(f||"indices"==d){var f=f?f.spacing:3,e=this.mesh.vertexBuffers[d];e&&e.data.length==c.length?(e.data.set(c),e.upload(GL.DYNAMIC_DRAW)):e=new GL.Buffer("indices"==d?GL.ELEMENT_ARRAY_BUFFER:GL.ARRAY_BUFFER,c,f,GL.DYNAMIC_DRAW);this.mesh.addBuffer(d,e)}}if(this.mesh.vertexBuffers.normals&&this.mesh.vertexBuffers.normals.data.length!=this.mesh.vertexBuffers.vertices.data.length){c=new Float32Array([0,1,0]);f=new Float32Array(this.mesh.vertexBuffers.vertices.data.length); -for(d=0;d=c.NOTEON||g<=c.NOTEOFF)this.channel=e&15};Object.defineProperty(c.prototype,"velocity",{get:function(){return this.cmd==c.NOTEON?this.data[2]:-1},set:function(c){this.data[2]=c},enumerable:!0});c.notes="A A# B C C# D D# E F F# G G#".split(" ");c.note_to_index={A:0,"A#":1,B:2,C:3,"C#":4,D:5,"D#":6,E:7,F:8,"F#":9,G:10,"G#":11};Object.defineProperty(c.prototype,"note",{get:function(){return this.cmd!=c.NOTEON?-1:c.toNoteString(this.data[1], !0)},set:function(c){throw"notes cannot be assigned this way, must modify the data[1]";},enumerable:!0});Object.defineProperty(c.prototype,"octave",{get:function(){return this.cmd!=c.NOTEON?-1:Math.floor((this.data[1]-24)/12+1)},set:function(c){throw"octave cannot be assigned this way, must modify the data[1]";},enumerable:!0});c.prototype.getPitch=function(){return 440*Math.pow(2,(this.data[1]-69)/12)};c.computePitch=function(c){return 440*Math.pow(2,(c-69)/12)};c.prototype.getCC=function(){return this.data[1]}; c.prototype.getCCValue=function(){return this.data[2]};c.prototype.getPitchBend=function(){return this.data[1]+(this.data[2]<<7)-8192};c.computePitchBend=function(c,e){return c+(e<<7)-8192};c.prototype.setCommandFromString=function(e){this.cmd=c.computeCommandFromString(e)};c.computeCommandFromString=function(e){if(!e)return 0;if(e&&e.constructor===Number)return e;e=e.toUpperCase();switch(e){case "NOTE ON":case "NOTEON":return c.NOTEON;case "NOTE OFF":case "NOTEOFF":return c.NOTEON;case "KEY PRESSURE":case "KEYPRESSURE":return c.KEYPRESSURE; @@ -628,78 +628,78 @@ function(e){e=e.toUpperCase();var g=e[0],k=4;"#"==e[1]?(g+="#",2this.properties.max_value)return;this.trigger("on_midi", -g)}};t.registerNodeType("midi/filter",g);C.title="MIDIEvent";C.desc="Create a MIDI Event";C.color="#243";C.prototype.onAction=function(e,g){"assign"==e?(this.properties.channel=g.channel,this.properties.cmd=g.cmd,this.properties.value1=g.data[1],this.properties.value2=g.data[2],g.cmd==c.NOTEON?this.gate=!0:g.cmd==c.NOTEOFF&&(this.gate=!1)):(g=this.midi_event,g.channel=this.properties.channel,this.properties.cmd&&this.properties.cmd.constructor===String?g.setCommandFromString(this.properties.cmd): +g)}};s.registerNodeType("midi/filter",e);C.title="MIDIEvent";C.desc="Create a MIDI Event";C.color="#243";C.prototype.onAction=function(e,g){"assign"==e?(this.properties.channel=g.channel,this.properties.cmd=g.cmd,this.properties.value1=g.data[1],this.properties.value2=g.data[2],g.cmd==c.NOTEON?this.gate=!0:g.cmd==c.NOTEOFF&&(this.gate=!1)):(g=this.midi_event,g.channel=this.properties.channel,this.properties.cmd&&this.properties.cmd.constructor===String?g.setCommandFromString(this.properties.cmd): g.cmd=this.properties.cmd,g.data[0]=g.cmd|g.channel,g.data[1]=Number(this.properties.value1),g.data[2]=Number(this.properties.value2),this.trigger("on_midi",g))};C.prototype.onExecute=function(){var e=this.properties;if(this.inputs)for(var g=0;gc;++c)this.valid_notes[c]=-1!= +k)}};C.prototype.onPropertyChanged=function(e,g){"cmd"==e&&(this.properties.cmd=c.computeCommandFromString(g))};C.prototype.onGetInputs=function(){return[["note","number"]]};C.prototype.onGetOutputs=function(){return[["midi","midi"],["on_midi",s.EVENT],["command","number"],["note","number"],["velocity","number"],["cc","number"],["cc_value","number"],["pitch","number"],["gate","bool"],["pitchbend","number"]]};s.registerNodeType("midi/event",C);z.title="MIDICC";z.desc="gets a Controller Change";z.color= +"#243";z.prototype.onExecute=function(){m.input&&(this.properties.value=m.input.state.cc[this.properties.cc]);this.setOutputData(0,this.properties.value)};s.registerNodeType("midi/cc",z);u.title="MIDI Generator";u.desc="Generates a random MIDI note";u.color="#243";u.processScale=function(e){e=e.split(",");for(var g=0;gc;++c)this.valid_notes[c]=-1!= this.notes_pitches.indexOf(c);for(c=0;12>c;++c)if(this.valid_notes[c])this.offset_notes[c]=0;else for(var e=1;12>e;++e){if(this.valid_notes[(c-e)%12]){this.offset_notes[c]=-e;break}if(this.valid_notes[(c+e)%12]){this.offset_notes[c]=e;break}}};B.prototype.onAction=function(e,g){g&&g.constructor===c&&(g.data[0]==c.NOTEON||g.data[0]==c.NOTEOFF?(this.midi_event=new c,this.midi_event.setup(g.data),this.midi_event.data[1]+=this.offset_notes[c.note_to_index[g.note]],this.trigger("out",this.midi_event)): -this.trigger("out",g))};B.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&c!=this._current_scale&&this.processScale(c)};t.registerNodeType("midi/quantize",B);e.title="MIDI Play";e.desc="Plays a MIDI note";e.color="#243";e.prototype.onAction=function(e,g){if(g&&g.constructor===c){if(this.instrument&&g.data[0]==c.NOTEON){var k=g.note;if(!k||"undefined"==k||k.constructor!==String)return;this.instrument.play(k,g.octave,this.properties.duration,this.properties.volume)}this.trigger("note", -g)}};e.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&(this.properties.volume=c);c=this.getInputData(2);null!=c&&(this.properties.duration=c)};t.registerNodeType("midi/play",e);q.title="MIDI Keys";q.desc="Keyboard to play notes";q.color="#243";q.keys=[{x:0,w:1,h:1,t:0},{x:0.75,w:0.5,h:0.6,t:1},{x:1,w:1,h:1,t:0},{x:1.75,w:0.5,h:0.6,t:1},{x:2,w:1,h:1,t:0},{x:2.75,w:0.5,h:0.6,t:1},{x:3,w:1,h:1,t:0},{x:4,w:1,h:1,t:0},{x:4.75,w:0.5,h:0.6,t:1},{x:5,w:1,h:1,t:0},{x:5.75,w:0.5,h:0.6,t:1}, -{x:6,w:1,h:1,t:0}];q.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){var e=12*this.properties.num_octaves;this.keys.length=e;var g=this.size[0]/(7*this.properties.num_octaves),l=this.size[1];c.globalAlpha=1;for(var a=0;2>a;a++)for(var b=0;bd+h||c[1]>b))return a}}return-1};q.prototype.onAction=function(e,g){if("reset"==e)for(var k=0;kg[1])){var k=this.getKeyIndex(g);this.keys[k]=!0;this._last_key=k;var k=12*(this.properties.start_octave-1)+29+k,l=new c;l.setup([c.NOTEON,k,100]);this.trigger("note",l);return!0}};q.prototype.onMouseMove=function(e,g){if(!(0>g[1]||-1==this._last_key)){this.setDirtyCanvas(!0);var k=this.getKeyIndex(g);if(this._last_key==k)return!0;this.keys[this._last_key]=!1;var l=12*(this.properties.start_octave-1)+29+this._last_key,a=new c;a.setup([c.NOTEOFF,l,100]); -this.trigger("note",a);this.keys[k]=!0;l=12*(this.properties.start_octave-1)+29+k;a=new c;a.setup([c.NOTEON,l,100]);this.trigger("note",a);this._last_key=k;return!0}};q.prototype.onMouseUp=function(e,g){if(!(0>g[1])){var k=this.getKeyIndex(g);this.keys[k]=!1;this._last_key=-1;var k=12*(this.properties.start_octave-1)+29+k,l=new c;l.setup([c.NOTEOFF,k,100]);this.trigger("note",l);return!0}};t.registerNodeType("midi/keys",q)})(this); -(function(y){function c(){this.properties={src:"",gain:0.5,loop:!0,autoplay:!0,playbackRate:1};this._loading_audio=!1;this._audiobuffer=null;this._audionodes=[];this._last_sourcenode=null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=w.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain;this.properties.src&&this.loadSound(this.properties.src)}function m(){this.properties={gain:0.5};this._audionodes=[];this._media_stream= -null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=w.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain}function n(){this.properties={fftSize:2048,minDecibels:-100,maxDecibels:-10,smoothingTimeConstant:0.5};this.audionode=w.getAudioContext().createAnalyser();this.audionode.graphnode=this;this.audionode.fftSize=this.properties.fftSize;this.audionode.minDecibels=this.properties.minDecibels;this.audionode.maxDecibels= -this.properties.maxDecibels;this.audionode.smoothingTimeConstant=this.properties.smoothingTimeConstant;this.addInput("in","audio");this.addOutput("freqs","array");this.addOutput("samples","array");this._time_bin=this._freq_bin=null}function k(){this.properties={gain:1};this.audionode=w.getAudioContext().createGain();this.addInput("in","audio");this.addInput("gain","number");this.addOutput("out","audio")}function x(){this.properties={impulse_src:"",normalize:!0};this.audionode=w.getAudioContext().createConvolver(); -this.addInput("in","audio");this.addOutput("out","audio")}function g(){this.properties={threshold:-50,knee:40,ratio:12,reduction:-20,attack:0,release:0.25};this.audionode=w.getAudioContext().createDynamicsCompressor();this.addInput("in","audio");this.addOutput("out","audio")}function C(){this.properties={};this.audionode=w.getAudioContext().createWaveShaper();this.addInput("in","audio");this.addInput("shape","waveshape");this.addOutput("out","audio")}function z(){this.properties={gain1:0.5,gain2:0.5}; -this.audionode=w.getAudioContext().createGain();this.audionode1=w.getAudioContext().createGain();this.audionode1.gain.value=this.properties.gain1;this.audionode2=w.getAudioContext().createGain();this.audionode2.gain.value=this.properties.gain2;this.audionode1.connect(this.audionode);this.audionode2.connect(this.audionode);this.addInput("in1","audio");this.addInput("in1 gain","number");this.addInput("in2","audio");this.addInput("in2 gain","number");this.addOutput("out","audio")}function v(){this.properties= -{A:0.1,D:0.1,S:0.1,R:0.1};this.audionode=w.getAudioContext().createGain();this.audionode.gain.value=0;this.addInput("in","audio");this.addInput("gate","bool");this.addOutput("out","audio");this.gate=!1}function E(){this.properties={delayTime:0.5};this.audionode=w.getAudioContext().createDelay(10);this.audionode.delayTime.value=this.properties.delayTime;this.addInput("in","audio");this.addInput("time","number");this.addOutput("out","audio")}function B(){this.properties={frequency:350,detune:0,Q:1}; -this.addProperty("type","lowpass","enum",{values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")});this.audionode=w.getAudioContext().createBiquadFilter();this.addInput("in","audio");this.addOutput("out","audio")}function e(){this.properties={frequency:440,detune:0,type:"sine"};this.addProperty("type","sine","enum",{values:["sine","square","sawtooth","triangle","custom"]});this.audionode=w.getAudioContext().createOscillator();this.addOutput("out","audio")}function q(){this.properties= -{continuous:!0,mark:-1};this.addInput("data","array");this.addInput("mark","number");this.size=[300,200];this._last_buffer=null}function t(){this.properties={band:440,amplitude:1};this.addInput("freqs","array");this.addOutput("signal","number")}function A(){if(!A.default_code){var c=A.default_function.toString(),a=c.indexOf("{")+1,b=c.lastIndexOf("}");A.default_code=c.substr(a,b-a)}this.properties={code:A.default_code};c=w.getAudioContext();c.createScriptProcessor?this.audionode=c.createScriptProcessor(4096, -1,1):(console.warn("ScriptProcessorNode deprecated"),this.audionode=c.createGain());this.processCode();A._bypass_function||(A._bypass_function=this.audionode.onaudioprocess);this.addInput("in","audio");this.addOutput("out","audio")}function p(){this.audionode=w.getAudioContext().destination;this.addInput("in","audio")}var s=y.LiteGraph,w={};y.LGAudio=w;w.getAudioContext=function(){if(!this._audio_context){window.AudioContext=window.AudioContext||window.webkitAudioContext;if(!window.AudioContext)return console.error("AudioContext not supported by browser"), -null;this._audio_context=new AudioContext;this._audio_context.onmessage=function(c){console.log("msg",c)};this._audio_context.onended=function(c){console.log("ended",c)};this._audio_context.oncomplete=function(c){console.log("complete",c)}}return this._audio_context};w.connect=function(c,a){try{c.connect(a)}catch(b){console.warn("LGraphAudio:",b)}};w.disconnect=function(c,a){try{c.disconnect(a)}catch(b){console.warn("LGraphAudio:",b)}};w.changeAllAudiosConnections=function(c,a){if(c.inputs)for(var b= -0;ba;a++)for(var b=0;bd+h||c[1]>b))return a}}return-1};p.prototype.onAction=function(e,g){if("reset"==e)for(var k=0;kg[1])){var k=this.getKeyIndex(g);this.keys[k]=!0;this._last_key=k;var k=12*(this.properties.start_octave-1)+29+k,l=new c;l.setup([c.NOTEON,k,100]);this.trigger("note",l);return!0}};p.prototype.onMouseMove=function(e,g){if(!(0>g[1]||-1==this._last_key)){this.setDirtyCanvas(!0);var k=this.getKeyIndex(g);if(this._last_key==k)return!0;this.keys[this._last_key]=!1;var l=12*(this.properties.start_octave-1)+29+this._last_key,a=new c;a.setup([c.NOTEOFF,l,100]); +this.trigger("note",a);this.keys[k]=!0;l=12*(this.properties.start_octave-1)+29+k;a=new c;a.setup([c.NOTEON,l,100]);this.trigger("note",a);this._last_key=k;return!0}};p.prototype.onMouseUp=function(e,g){if(!(0>g[1])){var k=this.getKeyIndex(g);this.keys[k]=!1;this._last_key=-1;var k=12*(this.properties.start_octave-1)+29+k,l=new c;l.setup([c.NOTEOFF,k,100]);this.trigger("note",l);return!0}};s.registerNodeType("midi/keys",p)})(this); +(function(y){function c(){this.properties={src:"",gain:0.5,loop:!0,autoplay:!0,playbackRate:1};this._loading_audio=!1;this._audiobuffer=null;this._audionodes=[];this._last_sourcenode=null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=x.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain;this.properties.src&&this.loadSound(this.properties.src)}function m(){this.properties={gain:0.5};this._audionodes=[];this._media_stream= +null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=x.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain}function q(){this.properties={fftSize:2048,minDecibels:-100,maxDecibels:-10,smoothingTimeConstant:0.5};this.audionode=x.getAudioContext().createAnalyser();this.audionode.graphnode=this;this.audionode.fftSize=this.properties.fftSize;this.audionode.minDecibels=this.properties.minDecibels;this.audionode.maxDecibels= +this.properties.maxDecibels;this.audionode.smoothingTimeConstant=this.properties.smoothingTimeConstant;this.addInput("in","audio");this.addOutput("freqs","array");this.addOutput("samples","array");this._time_bin=this._freq_bin=null}function k(){this.properties={gain:1};this.audionode=x.getAudioContext().createGain();this.addInput("in","audio");this.addInput("gain","number");this.addOutput("out","audio")}function w(){this.properties={impulse_src:"",normalize:!0};this.audionode=x.getAudioContext().createConvolver(); +this.addInput("in","audio");this.addOutput("out","audio")}function e(){this.properties={threshold:-50,knee:40,ratio:12,reduction:-20,attack:0,release:0.25};this.audionode=x.getAudioContext().createDynamicsCompressor();this.addInput("in","audio");this.addOutput("out","audio")}function C(){this.properties={};this.audionode=x.getAudioContext().createWaveShaper();this.addInput("in","audio");this.addInput("shape","waveshape");this.addOutput("out","audio")}function z(){this.properties={gain1:0.5,gain2:0.5}; +this.audionode=x.getAudioContext().createGain();this.audionode1=x.getAudioContext().createGain();this.audionode1.gain.value=this.properties.gain1;this.audionode2=x.getAudioContext().createGain();this.audionode2.gain.value=this.properties.gain2;this.audionode1.connect(this.audionode);this.audionode2.connect(this.audionode);this.addInput("in1","audio");this.addInput("in1 gain","number");this.addInput("in2","audio");this.addInput("in2 gain","number");this.addOutput("out","audio")}function u(){this.properties= +{A:0.1,D:0.1,S:0.1,R:0.1};this.audionode=x.getAudioContext().createGain();this.audionode.gain.value=0;this.addInput("in","audio");this.addInput("gate","bool");this.addOutput("out","audio");this.gate=!1}function D(){this.properties={delayTime:0.5};this.audionode=x.getAudioContext().createDelay(10);this.audionode.delayTime.value=this.properties.delayTime;this.addInput("in","audio");this.addInput("time","number");this.addOutput("out","audio")}function B(){this.properties={frequency:350,detune:0,Q:1}; +this.addProperty("type","lowpass","enum",{values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")});this.audionode=x.getAudioContext().createBiquadFilter();this.addInput("in","audio");this.addOutput("out","audio")}function g(){this.properties={frequency:440,detune:0,type:"sine"};this.addProperty("type","sine","enum",{values:["sine","square","sawtooth","triangle","custom"]});this.audionode=x.getAudioContext().createOscillator();this.addOutput("out","audio")}function p(){this.properties= +{continuous:!0,mark:-1};this.addInput("data","array");this.addInput("mark","number");this.size=[300,200];this._last_buffer=null}function s(){this.properties={band:440,amplitude:1};this.addInput("freqs","array");this.addOutput("signal","number")}function A(){if(!A.default_code){var c=A.default_function.toString(),a=c.indexOf("{")+1,b=c.lastIndexOf("}");A.default_code=c.substr(a,b-a)}this.properties={code:A.default_code};c=x.getAudioContext();c.createScriptProcessor?this.audionode=c.createScriptProcessor(4096, +1,1):(console.warn("ScriptProcessorNode deprecated"),this.audionode=c.createGain());this.processCode();A._bypass_function||(A._bypass_function=this.audionode.onaudioprocess);this.addInput("in","audio");this.addOutput("out","audio")}function n(){this.audionode=x.getAudioContext().destination;this.addInput("in","audio")}var v=y.LiteGraph,x={};y.LGAudio=x;x.getAudioContext=function(){if(!this._audio_context){window.AudioContext=window.AudioContext||window.webkitAudioContext;if(!window.AudioContext)return console.error("AudioContext not supported by browser"), +null;this._audio_context=new AudioContext;this._audio_context.onmessage=function(c){console.log("msg",c)};this._audio_context.onended=function(c){console.log("ended",c)};this._audio_context.oncomplete=function(c){console.log("complete",c)}}return this._audio_context};x.connect=function(c,a){try{c.connect(a)}catch(b){console.warn("LGraphAudio:",b)}};x.disconnect=function(c,a){try{c.disconnect(a)}catch(b){console.warn("LGraphAudio:",b)}};x.changeAllAudiosConnections=function(c,a){if(c.inputs)for(var b= +0;b=this.size[0]&&(e=this.size[0]-1),c.strokeStyle="red",c.beginPath(),c.moveTo(e,d),c.lineTo(e,0),c.stroke())}};q.title="Visualization";q.desc="Audio Visualization";s.registerNodeType("audio/visualization",q);t.prototype.onExecute=function(){if(this._freqs=this.getInputData(0)){var c=this.properties.band,a=this.getInputData(1);void 0!==a&&(c=a);a=w.getAudioContext().sampleRate/this._freqs.length;a=c/a*2;a>=this._freqs.length?a=this._freqs[this._freqs.length-1]:(c=a|0, -a-=c,a=this._freqs[c]*(1-a)+this._freqs[c+1]*a);this.setOutputData(0,a/255*this.properties.amplitude)}};t.prototype.onGetInputs=function(){return[["band","number"]]};t.title="Signal";t.desc="extract the signal of some frequency";s.registerNodeType("audio/signal",t);A.prototype.onAdded=function(c){c.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback)};A["@code"]={widget:"code",type:"code"};A.prototype.onStart=function(){this.audionode.onaudioprocess=this._callback};A.prototype.onStop= -function(){this.audionode.onaudioprocess=A._bypass_function};A.prototype.onPause=function(){this.audionode.onaudioprocess=A._bypass_function};A.prototype.onUnpause=function(){this.audionode.onaudioprocess=this._callback};A.prototype.onExecute=function(){};A.prototype.onRemoved=function(){this.audionode.onaudioprocess=A._bypass_function};A.prototype.processCode=function(){try{this._script=new new Function("properties",this.properties.code)(this.properties),this._old_code=this.properties.code,this._callback= -this._script.onaudioprocess}catch(c){console.error("Error in onaudioprocess code",c),this._callback=A._bypass_function,this.audionode.onaudioprocess=this._callback}};A.prototype.onPropertyChanged=function(c,a){"code"==c&&(this.properties.code=a,this.processCode(),this.graph&&this.graph.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback))};A.default_function=function(){this.onaudioprocess=function(c){var a=c.inputBuffer;c=c.outputBuffer;for(var b=0;b=this.size[0]&&(e=this.size[0]-1),c.strokeStyle="red",c.beginPath(),c.moveTo(e,d),c.lineTo(e,0),c.stroke())}};p.title="Visualization";p.desc="Audio Visualization";v.registerNodeType("audio/visualization",p);s.prototype.onExecute=function(){if(this._freqs=this.getInputData(0)){var c=this.properties.band,a=this.getInputData(1);void 0!==a&&(c=a);a=x.getAudioContext().sampleRate/this._freqs.length;a=c/a*2;a>=this._freqs.length? +a=this._freqs[this._freqs.length-1]:(c=a|0,a-=c,a=this._freqs[c]*(1-a)+this._freqs[c+1]*a);this.setOutputData(0,a/255*this.properties.amplitude)}};s.prototype.onGetInputs=function(){return[["band","number"]]};s.title="Signal";s.desc="extract the signal of some frequency";v.registerNodeType("audio/signal",s);A.prototype.onAdded=function(c){c.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback)};A["@code"]={widget:"code",type:"code"};A.prototype.onStart=function(){this.audionode.onaudioprocess= +this._callback};A.prototype.onStop=function(){this.audionode.onaudioprocess=A._bypass_function};A.prototype.onPause=function(){this.audionode.onaudioprocess=A._bypass_function};A.prototype.onUnpause=function(){this.audionode.onaudioprocess=this._callback};A.prototype.onExecute=function(){};A.prototype.onRemoved=function(){this.audionode.onaudioprocess=A._bypass_function};A.prototype.processCode=function(){try{this._script=new new Function("properties",this.properties.code)(this.properties),this._old_code= +this.properties.code,this._callback=this._script.onaudioprocess}catch(c){console.error("Error in onaudioprocess code",c),this._callback=A._bypass_function,this.audionode.onaudioprocess=this._callback}};A.prototype.onPropertyChanged=function(c,a){"code"==c&&(this.properties.code=a,this.processCode(),this.graph&&this.graph.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback))};A.default_function=function(){this.onaudioprocess=function(c){var a=c.inputBuffer;c=c.outputBuffer; +for(var b=0;b Date: Sat, 28 Mar 2020 10:59:57 +0300 Subject: [PATCH 10/63] fixes type error in function wrapFunctionAsNode --- src/litegraph.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/litegraph.d.ts b/src/litegraph.d.ts index 8c2a0e852..ba94e91d6 100644 --- a/src/litegraph.d.ts +++ b/src/litegraph.d.ts @@ -240,7 +240,7 @@ export const LiteGraph: { wrapFunctionAsNode( name: string, func: (...args: any[]) => any, - param_types?: [], + param_types?: string[], return_type?: string, properties?: object ): void; From e9e7c8df219e3e21c3d8efa84ea4d585a2a24856 Mon Sep 17 00:00:00 2001 From: Javi Agenjo Date: Sun, 29 Mar 2020 20:50:20 +0200 Subject: [PATCH 11/63] Update README.md --- guides/README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/guides/README.md b/guides/README.md index 7f607bbfd..4059a7290 100644 --- a/guides/README.md +++ b/guides/README.md @@ -186,7 +186,14 @@ function MyNodeType() This is the list of supported widgets: * **"number"** to change a value of a number, the syntax is ```this.addWidget("number","Number", current_value, callback, { min: 0, max: 100, step: 1} );``` * **"slider"** to change a number by draging the mouse, the syntax is the same as number. -* **"combo"** to select between multiple choices, the syntax is: ```this.addWidget("combo","Combo", "red", callback, { values:["red","green","blue"]} );``` +* **"combo"** to select between multiple choices, the syntax is: + + ```this.addWidget("combo","Combo", "red", callback, { values:["red","green","blue"]} );``` + + or if you want to use objects: + + ```this.addWidget("combo","Combo", value1, callback, { values: { "title1":value1, "title2":value2 } );``` + * **"text"** to edit a short string * **"toggle"** like a checkbox * **"button"** From 6ef31c8c3ae01a5870e5311abeb3d0d5f983f8bb Mon Sep 17 00:00:00 2001 From: Shan M Date: Tue, 31 Mar 2020 17:21:53 +0300 Subject: [PATCH 12/63] Adds missing type for showMenuNodeOptionalOutputs --- src/litegraph.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/litegraph.d.ts b/src/litegraph.d.ts index 8c2a0e852..34315f36f 100644 --- a/src/litegraph.d.ts +++ b/src/litegraph.d.ts @@ -1017,6 +1017,7 @@ export declare class LGraphCanvas { /** Create menu for `Add Node` */ static onMenuAdd: ContextMenuEventListener; static showMenuNodeOptionalInputs: ContextMenuEventListener; + static showMenuNodeOptionalOutputs: ContextMenuEventListener; static onShowMenuNodeProperties: ContextMenuEventListener; static onResizeNode: ContextMenuEventListener; static onMenuNodeCollapse: ContextMenuEventListener; From 2158aff0b0e833cc3014f4dab4a5ca6729088895 Mon Sep 17 00:00:00 2001 From: tamat Date: Fri, 3 Apr 2020 17:45:05 +0200 Subject: [PATCH 13/63] added setValue to some nodes --- build/litegraph.js | 480 ++++++++++++------- build/litegraph.min.js | 996 ++++++++++++++++++++------------------- css/litegraph-editor.css | 1 + src/litegraph-editor.js | 1 + src/litegraph.js | 40 +- src/nodes/audio.js | 10 +- src/nodes/base.js | 249 +++++++--- src/nodes/midi.js | 103 ++-- src/nodes/strings.js | 78 ++- 9 files changed, 1147 insertions(+), 811 deletions(-) diff --git a/build/litegraph.js b/build/litegraph.js index 0a3385f9a..f5861d749 100644 --- a/build/litegraph.js +++ b/build/litegraph.js @@ -7310,6 +7310,7 @@ LGraphNode.prototype.executeAction = function(action) if(text == null) return; + text = text.substr(0,30); //avoid weird ctx.font = "14px Courier New"; var info = ctx.measureText(text); @@ -8289,8 +8290,17 @@ LGraphNode.prototype.executeAction = function(action) y + H * 0.7 ); } else { + var v = w.value; + if( w.options.values ) + { + var values = w.options.values; + if( values.constructor === Function ) + values = values(); + if(values && values.constructor !== Array) + v = values[ w.value ]; + } ctx.fillText( - w.value, + v, width - margin * 2 - 20, y + H * 0.7 ); @@ -8321,8 +8331,7 @@ LGraphNode.prototype.executeAction = function(action) } ctx.fillStyle = text_color; ctx.textAlign = "right"; - ctx.fillText(w.value, width - margin * 2, y + H * 0.7); - + ctx.fillText(String(w.value).substr(0,30), width - margin * 2, y + H * 0.7); //30 chars max ctx.restore(); } break; @@ -8408,6 +8417,7 @@ LGraphNode.prototype.executeAction = function(action) if (values && values.constructor === Function) { values = w.options.values(w, node); } + var values_list = values.constructor === Array ? values : Object.keys(values); var delta = x < 40 ? -1 : x > width - 40 ? 1 : 0; if (w.type == "number") { @@ -8418,21 +8428,25 @@ LGraphNode.prototype.executeAction = function(action) if ( w.options.max != null && w.value > w.options.max ) { w.value = w.options.max; } - } else if (delta) { //used for combos - var values_list = values.constructor === Array ? values : Object.keys(values); - var index = values_list.indexOf(w.value) + delta; - if (index >= values.length) { - index = 0; + } else if (delta) { //clicked in arrow, used for combos + var index = -1; + if(values.constructor === Object) + index = values_list.indexOf( String( w.value ) ) + delta; + else + index = values_list.indexOf( w.value ) + delta; + if (index >= values_list.length) { + index = values_list.length - 1; } if (index < 0) { - index = values_list.length - 1; + index = 0; } if( values.constructor === Array ) w.value = values[index]; else - w.value = values[ values_list[index] ]; - } else { //combo - var menu = new LiteGraph.ContextMenu(values,{ + w.value = index; + } else { //combo clicked + var text_values = values != values_list ? Object.values(values) : values; + var menu = new LiteGraph.ContextMenu(text_values, { scale: Math.max(1, this.ds.scale), event: event, className: "dark", @@ -8440,6 +8454,8 @@ LGraphNode.prototype.executeAction = function(action) }, ref_window); function inner_clicked(v, option, event) { + if(values != values_list) + v = text_values.indexOf(v); this.value = v; inner_value_change(this, v); that.dirty_canvas = true; @@ -11378,27 +11394,8 @@ if (typeof exports != "undefined") { enumerable: true }); - this.name_widget = this.addWidget( - "text", - "Name", - this.properties.name, - function(v) { - if (!v) { - return; - } - that.properties.name = v; - } - ); - this.type_widget = this.addWidget( - "text", - "Type", - this.properties.type, - function(v) { - v = v || ""; - that.properties.type = v; - } - ); - + this.name_widget = this.addWidget("text","Name",this.properties.name,"name"); + this.type_widget = this.addWidget("text","Type",this.properties.type,"type"); this.widgets_up = true; this.size = [180, 60]; } @@ -11437,12 +11434,7 @@ if (typeof exports != "undefined") { function ConstantNumber() { this.addOutput("value", "number"); this.addProperty("value", 1.0); - this.widget = this.addWidget( - "number", - "value", - 1, - "value" - ); + this.widget = this.addWidget("number","value",1,"value"); this.widgets_up = true; this.size = [180, 30]; } @@ -11461,6 +11453,11 @@ if (typeof exports != "undefined") { return this.title; }; + ConstantNumber.prototype.setValue = function(v) + { + this.setProperty("value",v); + } + ConstantNumber.prototype.onDrawBackground = function(ctx) { //show the current value this.outputs[0].label = this.properties["value"].toFixed(3); @@ -11471,12 +11468,7 @@ if (typeof exports != "undefined") { function ConstantBoolean() { this.addOutput("", "boolean"); this.addProperty("value", true); - this.widget = this.addWidget( - "toggle", - "value", - true, - "value" - ); + this.widget = this.addWidget("toggle","value",true,"value"); this.widgets_up = true; this.size = [140, 30]; } @@ -11489,17 +11481,14 @@ if (typeof exports != "undefined") { this.setOutputData(0, this.properties["value"]); }; + ConstantBoolean.prototype.setValue = ConstantNumber.prototype.setValue; + LiteGraph.registerNodeType("basic/boolean", ConstantBoolean); function ConstantString() { this.addOutput("", "string"); this.addProperty("value", ""); - this.widget = this.addWidget( - "text", - "value", - "", - "value" //link to property value - ); + this.widget = this.addWidget("text","value","","value"); //link to property value this.widgets_up = true; this.size = [180, 30]; } @@ -11513,17 +11502,124 @@ if (typeof exports != "undefined") { this.setOutputData(0, this.properties["value"]); }; + ConstantString.prototype.setValue = ConstantNumber.prototype.setValue; + + ConstantString.prototype.onDropFile = function(file) + { + var that = this; + var reader = new FileReader(); + reader.onload = function(e) + { + that.setProperty("value",e.target.result); + } + reader.readAsText(file); + } + LiteGraph.registerNodeType("basic/string", ConstantString); + function ConstantFile() { + this.addInput("url", ""); + this.addOutput("", ""); + this.addProperty("url", ""); + this.addProperty("type", "text"); + this.widget = this.addWidget("text","url","","url"); + this._data = null; + } + + ConstantFile.title = "Const File"; + ConstantFile.desc = "Fetches a file from an url"; + ConstantFile["@type"] = { type: "enum", values: ["text","arraybuffer","blob","json"] }; + + ConstantFile.prototype.onPropertyChanged = function(name, value) { + if (name == "url") + { + if( value == null || value == "") + this._data = null; + else + { + this.fetchFile(value); + } + } + } + + ConstantFile.prototype.onExecute = function() { + var url = this.getInputData(0) || this.properties.url; + if(url && (url != this._url || this._type != this.properties.type)) + this.fetchFile(url); + this.setOutputData(0, this._data ); + }; + + ConstantFile.prototype.setValue = ConstantNumber.prototype.setValue; + + ConstantFile.prototype.fetchFile = function(url) { + var that = this; + if(!url || url.constructor !== String) + { + that._data = null; + that.boxcolor = null; + return; + } + + this._url = url; + this._type = this.properties.type; + if (url.substr(0, 4) == "http" && LiteGraph.proxy) { + url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); + } + fetch(url) + .then(function(response) { + if(!response.ok) + throw new Error("File not found"); + + if(that.properties.type == "arraybuffer") + return response.arrayBuffer(); + else if(that.properties.type == "text") + return response.text(); + else if(that.properties.type == "json") + return response.json(); + else if(that.properties.type == "blob") + return response.blob(); + }) + .then(function(data) { + that._data = data; + that.boxcolor = "#AEA"; + }) + .catch(function(error) { + that._data = null; + that.boxcolor = "red"; + console.error("error fetching file:",url); + }); + }; + + ConstantFile.prototype.onDropFile = function(file) + { + var that = this; + this._url = file.name; + this._type = this.properties.type; + this.properties.url = file.name; + var reader = new FileReader(); + reader.onload = function(e) + { + that.boxcolor = "#AEA"; + var v = e.target.result; + if( that.properties.type == "json" ) + v = JSON.parse(v); + that._data = v; + } + if(that.properties.type == "arraybuffer") + reader.readAsArrayBuffer(file); + else if(that.properties.type == "text" || that.properties.type == "json") + reader.readAsText(file); + else if(that.properties.type == "blob") + return reader.readAsBinaryString(file); + } + + LiteGraph.registerNodeType("basic/file", ConstantFile); + + //to store json objects function ConstantData() { this.addOutput("", ""); this.addProperty("value", ""); - this.widget = this.addWidget( - "text", - "json", - "", - this.setValue.bind(this) - ); + this.widget = this.addWidget("text","json","","value"); this.widgets_up = true; this.size = [140, 30]; this._value = null; @@ -11532,11 +11628,6 @@ if (typeof exports != "undefined") { ConstantData.title = "Const Data"; ConstantData.desc = "Constant Data"; - ConstantData.prototype.setValue = function(v) { - this.properties.value = v; - this.onPropertyChanged("value", v); - }; - ConstantData.prototype.onPropertyChanged = function(name, value) { this.widget.value = value; if (value == null || value == "") { @@ -11555,18 +11646,68 @@ if (typeof exports != "undefined") { this.setOutputData(0, this._value); }; + ConstantData.prototype.setValue = ConstantNumber.prototype.setValue; + LiteGraph.registerNodeType("basic/data", ConstantData); + function ArrayElement() { + this.addInput("array", "array,table,string"); + this.addInput("index", "number"); + this.addOutput("value", ""); + this.addProperty("index",0); + } + + ArrayElement.title = "Array[i]"; + ArrayElement.desc = "Returns an element from an array"; + + ArrayElement.prototype.onExecute = function() { + var array = this.getInputData(0); + var index = this.getInputData(1); + if(index == null) + index = this.properties.index; + if(array == null || index == null ) + return; + this.setOutputData(0, array[Math.floor(Number(index))] ); + }; + + LiteGraph.registerNodeType("basic/array[]", ArrayElement); + + function TableElement() { + this.addInput("table", "table"); + this.addInput("row", "number"); + this.addInput("col", "number"); + this.addOutput("value", ""); + this.addProperty("row",0); + this.addProperty("column",0); + } + + TableElement.title = "Table[row][col]"; + TableElement.desc = "Returns an element from a table"; + + TableElement.prototype.onExecute = function() { + var table = this.getInputData(0); + var row = this.getInputData(1); + var col = this.getInputData(2); + if(row == null) + row = this.properties.row; + if(col == null) + col = this.properties.column; + if(table == null || row == null || col == null) + return; + var row = table[Math.floor(Number(row))]; + if(row) + this.setOutputData(0, row[Math.floor(Number(col))] ); + else + this.setOutputData(0, null ); + }; + + LiteGraph.registerNodeType("basic/table[][]", TableElement); + function ObjectProperty() { this.addInput("obj", ""); this.addOutput("", ""); this.addProperty("value", ""); - this.widget = this.addWidget( - "text", - "prop.", - "", - this.setValue.bind(this) - ); + this.widget = this.addWidget("text","prop.","",this.setValue.bind(this) ); this.widgets_up = true; this.size = [140, 30]; this._value = null; @@ -11675,6 +11816,18 @@ if (typeof exports != "undefined") { LiteGraph.registerNodeType("basic/variable", Variable); + function length(v) { + if(v && v.length != null) + return Number(v.length); + return 0; + } + + LiteGraph.wrapFunctionAsNode( + "basic/length", + length, + ["*"], + "number" + ); function DownloadData() { this.size = [60, 30]; @@ -15345,8 +15498,8 @@ if (typeof exports != "undefined") { LiteGraph.wrapFunctionAsNode( "string/compare", compare, - ["String", "String"], - "Boolean" + ["string", "string"], + "boolean" ); function concatenate(a, b) { @@ -15362,8 +15515,8 @@ if (typeof exports != "undefined") { LiteGraph.wrapFunctionAsNode( "string/concatenate", concatenate, - ["String", "String"], - "String" + ["string", "string"], + "string" ); function contains(a, b) { @@ -15376,8 +15529,8 @@ if (typeof exports != "undefined") { LiteGraph.wrapFunctionAsNode( "string/contains", contains, - ["String", "String"], - "Boolean" + ["string", "string"], + "boolean" ); function toUpperCase(a) { @@ -15390,22 +15543,33 @@ if (typeof exports != "undefined") { LiteGraph.wrapFunctionAsNode( "string/toUpperCase", toUpperCase, - ["String"], - "String" + ["string"], + "string" ); - function split(a, b) { - if (a != null && a.constructor === String) { - return a.split(b || " "); - } - return [a]; + function split(str, separator) { + if(separator == null) + separator = this.properties.separator; + if (str == null ) + return []; + if( str.constructor === String ) + return str.split(separator || " "); + else if( str.constructor === Array ) + { + var r = []; + for(var i = 0; i < str.length; ++i) + r[i] = str[i].split(separator || " "); + return r; + } + return null; } LiteGraph.wrapFunctionAsNode( "string/split", - toUpperCase, - ["String", "String"], - "Array" + split, + ["string,array", "string"], + "array", + { separator: "," } ); function toFixed(a) { @@ -15418,10 +15582,41 @@ if (typeof exports != "undefined") { LiteGraph.wrapFunctionAsNode( "string/toFixed", toFixed, - ["Number"], - "String", + ["number"], + "string", { precision: 0 } ); + + + function StringToTable() { + this.addInput("", "string"); + this.addOutput("table", "table"); + this.addOutput("rows", "number"); + this.addProperty("value", ""); + this.addProperty("separator", ","); + this._table = null; + } + + StringToTable.title = "toTable"; + StringToTable.desc = "Splits a string to table"; + + StringToTable.prototype.onExecute = function() { + var input = this.getInputData(0); + if(!input) + return; + var separator = this.properties.separator || ","; + if(input != this._str || separator != this._last_separator ) + { + this._last_separator = separator; + this._str = input; + this._table = input.split("\n").map(function(a){ return a.trim().split(separator)}); + } + this.setOutputData(0, this._table ); + this.setOutputData(1, this._table ? this._table.length : 0 ); + }; + + LiteGraph.registerNodeType("string/toTable", StringToTable); + })(this); (function(global) { @@ -24598,7 +24793,7 @@ function LGraphGeometryDisplace() { MIDIEvent.commands_reversed[MIDIEvent.commands[i]] = i; } - //MIDI wrapper + //MIDI wrapper, instantiate by MIDIIn and MIDIOut function MIDIInterface(on_ready, on_error) { if (!navigator.requestMIDIAccess) { this.error = "not suppoorted"; @@ -24617,9 +24812,12 @@ function LGraphGeometryDisplace() { cc: [] }; - navigator - .requestMIDIAccess() - .then(this.onMIDISuccess.bind(this), this.onMIDIFailure.bind(this)); + this.input_ports = null; + this.input_ports_info = []; + this.output_ports = null; + this.output_ports_info = []; + + navigator.requestMIDIAccess().then(this.onMIDISuccess.bind(this), this.onMIDIFailure.bind(this)); } MIDIInterface.input = null; @@ -24640,80 +24838,34 @@ function LGraphGeometryDisplace() { MIDIInterface.prototype.updatePorts = function() { var midi = this.midi; this.input_ports = midi.inputs; + this.input_ports_info = []; + this.output_ports = midi.outputs; + this.output_ports_info = []; + var num = 0; var it = this.input_ports.values(); var it_value = it.next(); while (it_value && it_value.done === false) { var port_info = it_value.value; - console.log( - "Input port [type:'" + - port_info.type + - "'] id:'" + - port_info.id + - "' manufacturer:'" + - port_info.manufacturer + - "' name:'" + - port_info.name + - "' version:'" + - port_info.version + - "'" - ); + this.input_ports_info.push(port_info); + console.log( "Input port [type:'" + port_info.type + "'] id:'" + port_info.id + "' manufacturer:'" + port_info.manufacturer + "' name:'" + port_info.name + "' version:'" + port_info.version + "'" ); num++; it_value = it.next(); } this.num_input_ports = num; num = 0; - this.output_ports = midi.outputs; var it = this.output_ports.values(); var it_value = it.next(); while (it_value && it_value.done === false) { var port_info = it_value.value; - console.log( - "Output port [type:'" + - port_info.type + - "'] id:'" + - port_info.id + - "' manufacturer:'" + - port_info.manufacturer + - "' name:'" + - port_info.name + - "' version:'" + - port_info.version + - "'" - ); + this.output_ports_info.push(port_info); + console.log( "Output port [type:'" + port_info.type + "'] id:'" + port_info.id + "' manufacturer:'" + port_info.manufacturer + "' name:'" + port_info.name + "' version:'" + port_info.version + "'" ); num++; it_value = it.next(); } this.num_output_ports = num; - - /* OLD WAY - for (var i = 0; i < this.input_ports.size; ++i) { - var input = this.input_ports.get(i); - if(!input) - continue; //sometimes it is null?! - console.log( "Input port [type:'" + input.type + "'] id:'" + input.id + - "' manufacturer:'" + input.manufacturer + "' name:'" + input.name + - "' version:'" + input.version + "'" ); - num++; - } - this.num_input_ports = num; - - - num = 0; - this.output_ports = midi.outputs; - for (var i = 0; i < this.output_ports.size; ++i) { - var output = this.output_ports.get(i); - if(!output) - continue; - console.log( "Output port [type:'" + output.type + "'] id:'" + output.id + - "' manufacturer:'" + output.manufacturer + "' name:'" + output.name + - "' version:'" + output.version + "'" ); - num++; - } - this.num_output_ports = num; - */ }; MIDIInterface.prototype.onMIDIFailure = function(msg) { @@ -24763,7 +24915,7 @@ function LGraphGeometryDisplace() { return; } - var output_port = this.output_ports.get("output-" + port); + var output_port = this.output_ports_info[port];//this.output_ports.get("output-" + port); if (!output_port) { return; } @@ -24810,10 +24962,9 @@ function LGraphGeometryDisplace() { if (name == "port") { var values = {}; - for (var i = 0; i < this._midi.input_ports.size; ++i) { - var input = this._midi.input_ports.get("input-" + i); - values[i] = - i + ".- " + input.name + " version:" + input.version; + for (var i = 0; i < this._midi.input_ports_info.length; ++i) { + var input = this._midi.input_ports_info[i]; + values[i] = i + ".- " + input.name + " version:" + input.version; } return { type: "enum", values: values }; } @@ -24911,9 +25062,10 @@ function LGraphGeometryDisplace() { var that = this; new MIDIInterface(function(midi) { that._midi = midi; + that.widget.options.values = that.getMIDIOutputs(); }); - - this.addWidget("combo","Device",this.properties.port,{ property: "port", values: this.getMIDIOutputs.bind(this) }); + this.widget = this.addWidget("combo","Device",this.properties.port,{ property: "port", values: this.getMIDIOutputs.bind(this) }); + this.size = [340,60]; } LGMIDIOut.MIDIInterface = MIDIInterface; @@ -24932,14 +25084,20 @@ function LGraphGeometryDisplace() { return { type: "enum", values: values }; } }; + LGMIDIOut.default_ports = {0:"unknown"}; LGMIDIOut.prototype.getMIDIOutputs = function() { var values = {}; - for (var i = 0; i < this._midi.output_ports.size; ++i) { - var output = this._midi.output_ports.get(i); - if(output) - values[i] = i + ".- " + output.name + " version:" + output.version; + if(!this._midi) + return LGMIDIOut.default_ports; + if(this._midi.output_ports_info) + for (var i = 0; i < this._midi.output_ports_info.length; ++i) { + var output = this._midi.output_ports_info[i]; + if(!output) + continue; + var name = i + ".- " + output.name + " version:" + output.version; + values[i] = name; } return values; } @@ -24950,7 +25108,7 @@ function LGraphGeometryDisplace() { return; } if (event == "send") { - this._midi.sendMIDI(this.port, midi_event); + this._midi.sendMIDI(this.properties.port, midi_event); } this.trigger("midi", midi_event); }; @@ -26035,6 +26193,7 @@ function LGraphGeometryDisplace() { } } + LGAudioSource.desc = "Plays an audio file"; LGAudioSource["@src"] = { widget: "resource" }; LGAudioSource.supported_extensions = ["wav", "ogg", "mp3"]; @@ -26050,7 +26209,7 @@ function LGraphGeometryDisplace() { } if (this.properties.autoplay) { - this.playBuffer(this._audiobuffer); + this.playBuffer(this._audiobuffer); } }; @@ -26163,7 +26322,10 @@ function LGraphGeometryDisplace() { audionode.playbackRate.value = this.properties.playbackRate; this._audionodes.push(audionode); audionode.connect(this.audionode); //connect to gain - this._audionodes.push(audionode); + + this._audionodes.push(audionode); + + this.trigger("start"); audionode.onended = function() { //console.log("ended!"); @@ -26227,7 +26389,7 @@ function LGraphGeometryDisplace() { }; LGAudioSource.prototype.onGetOutputs = function() { - return [["buffer", "audiobuffer"], ["ended", LiteGraph.EVENT]]; + return [["buffer", "audiobuffer"], ["start", LiteGraph.EVENT], ["ended", LiteGraph.EVENT]]; }; LGAudioSource.prototype.onDropFile = function(file) { diff --git a/build/litegraph.min.js b/build/litegraph.min.js index 0b2a46791..e597b5473 100755 --- a/build/litegraph.min.js +++ b/build/litegraph.min.js @@ -1,4 +1,4 @@ -(function(y){function c(a){g.debug&&console.log("Graph created");this.list_of_graphcanvas=null;this.clear();a&&this.configure(a)}function m(a,b,d,h,f,g){this.id=a;this.type=b;this.origin_id=d;this.origin_slot=h;this.target_id=f;this.target_slot=g;this._data=null;this._pos=new Float32Array(2)}function q(a){this._ctor(a)}function k(a){this._ctor(a)}function w(a,b){this.offset=new Float32Array([0,0]);this.scale=1;this.max_scale=10;this.min_scale=0.1;this.onredraw=null;this.enabled=!0;this.last_mouse= +(function(y){function c(a){g.debug&&console.log("Graph created");this.list_of_graphcanvas=null;this.clear();a&&this.configure(a)}function l(a,b,d,h,f,g){this.id=a;this.type=b;this.origin_id=d;this.origin_slot=h;this.target_id=f;this.target_slot=g;this._data=null;this._pos=new Float32Array(2)}function p(a){this._ctor(a)}function k(a){this._ctor(a)}function w(a,b){this.offset=new Float32Array([0,0]);this.scale=1;this.max_scale=10;this.min_scale=0.1;this.onredraw=null;this.enabled=!0;this.last_mouse= [0,0];this.element=null;this.visible_area=new Float32Array(4);a&&(this.element=a,b||this.bindEvents(a))}function e(a,b,d){d=d||{};this.background_image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; a&&a.constructor===String&&(a=document.querySelector(a));this.ds=new w;this.zoom_modify_alpha=!0;this.title_text_font=""+g.NODE_TEXT_SIZE+"px Arial";this.inner_text_font="normal "+g.NODE_SUBTEXT_SIZE+"px Arial";this.node_title_color=g.NODE_TITLE_COLOR;this.default_link_color=g.LINK_COLOR;this.default_connection_color={input_off:"#778",input_on:"#7F7",output_off:"#778",output_on:"#7F7"};this.highquality_render=!0;this.use_gradients=!1;this.editor_alpha=1;this.pause_rendering=!1;this.clear_background= !0;this.read_only=!1;this.render_only_selected=!0;this.live_mode=!1;this.allow_searchbox=this.allow_interaction=this.allow_dragnodes=this.allow_dragcanvas=this.show_info=!0;this.drag_mode=this.allow_reconnect_links=!1;this.filter=this.dragging_rectangle=null;this.always_render_background=!1;this.render_canvas_border=this.render_shadows=!0;this.render_connections_shadows=!1;this.render_connections_border=!0;this.render_connection_arrows=this.render_curved_connections=!1;this.render_collapsed_slots= @@ -6,31 +6,31 @@ a&&a.constructor===String&&(a=document.querySelector(a));this.ds=new w;this.zoom this.visible_links=[];b&&b.attachCanvas(this);this.setCanvas(a);this.clear();d.skip_render||this.startRendering();this.autoresize=d.autoresize}function C(a,b){return Math.sqrt((b[0]-a[0])*(b[0]-a[0])+(b[1]-a[1])*(b[1]-a[1]))}function z(a,b,d,h,f,g){return da&&hb?!0:!1}function u(a,b){var d=a[0]+a[2],h=a[1]+a[3],f=b[1]+b[3];return a[0]>b[0]+b[2]||a[1]>f||dc.width-l.width-10&&(f=c.width-l.width-10);r>c.height-l.height-10&&(r=c.height-l.height-10)}g.style.left=f+"px";g.style.top=r+"px";b.scale&&(g.style.transform="scale("+b.scale+")")}function B(a){this.points=a;this.nearest=this.selected=-1;this.size=null;this.must_update=!0;this.margin=5}var g=y.LiteGraph={VERSION:0.4,CANVAS_GRID_SIZE:10,NODE_TITLE_HEIGHT:30,NODE_TITLE_TEXT_Y:20,NODE_SLOT_HEIGHT:20,NODE_WIDGET_HEIGHT:20,NODE_WIDTH:140,NODE_MIN_WIDTH:50,NODE_COLLAPSED_RADIUS:10, +a.button)return h.close(),a.preventDefault(),!0},!0);b.scroll_speed||(b.scroll_speed=0.1);g.addEventListener("wheel",d,!0);g.addEventListener("mousewheel",d,!0);this.root=g;b.title&&(f=document.createElement("div"),f.className="litemenu-title",f.innerHTML=b.title,g.appendChild(f));var f=0,q;for(q in a){var c=a.constructor==Array?a[q]:q;null!=c&&c.constructor!==String&&(c=void 0===c.content?String(c):c.content);this.addItem(c,a[q],b);f++}g.addEventListener("mouseleave",function(a){h.lock||(g.closing_timer&& +clearTimeout(g.closing_timer),g.closing_timer=setTimeout(h.close.bind(h,a),500))});g.addEventListener("mouseenter",function(a){g.closing_timer&&clearTimeout(g.closing_timer)});q=document;b.event&&(q=b.event.target.ownerDocument);q||(q=document);q.fullscreenElement?q.fullscreenElement.appendChild(g):q.body.appendChild(g);f=b.left||0;q=b.top||0;if(b.event){f=b.event.clientX-10;q=b.event.clientY-10;b.title&&(q-=20);b.parentMenu&&(f=b.parentMenu.root.getBoundingClientRect(),f=f.left+f.width);var c=document.body.getBoundingClientRect(), +e=g.getBoundingClientRect();f>c.width-e.width-10&&(f=c.width-e.width-10);q>c.height-e.height-10&&(q=c.height-e.height-10)}g.style.left=f+"px";g.style.top=q+"px";b.scale&&(g.style.transform="scale("+b.scale+")")}function B(a){this.points=a;this.nearest=this.selected=-1;this.size=null;this.must_update=!0;this.margin=5}var g=y.LiteGraph={VERSION:0.4,CANVAS_GRID_SIZE:10,NODE_TITLE_HEIGHT:30,NODE_TITLE_TEXT_Y:20,NODE_SLOT_HEIGHT:20,NODE_WIDGET_HEIGHT:20,NODE_WIDTH:140,NODE_MIN_WIDTH:50,NODE_COLLAPSED_RADIUS:10, NODE_COLLAPSED_WIDTH:80,NODE_TITLE_COLOR:"#999",NODE_TEXT_SIZE:14,NODE_TEXT_COLOR:"#AAA",NODE_SUBTEXT_SIZE:12,NODE_DEFAULT_COLOR:"#333",NODE_DEFAULT_BGCOLOR:"#353535",NODE_DEFAULT_BOXCOLOR:"#666",NODE_DEFAULT_SHAPE:"box",DEFAULT_SHADOW_COLOR:"rgba(0,0,0,0.5)",DEFAULT_GROUP_FONT:24,WIDGET_BGCOLOR:"#222",WIDGET_OUTLINE_COLOR:"#666",WIDGET_TEXT_COLOR:"#DDD",WIDGET_SECONDARY_TEXT_COLOR:"#999",LINK_COLOR:"#9A9",EVENT_LINK_COLOR:"#A86",CONNECTING_LINK_COLOR:"#AFA",MAX_NUMBER_OF_NODES:1E3,DEFAULT_POSITION:[100, 100],VALID_SHAPES:["default","box","round","card"],BOX_SHAPE:1,ROUND_SHAPE:2,CIRCLE_SHAPE:3,CARD_SHAPE:4,ARROW_SHAPE:5,INPUT:1,OUTPUT:2,EVENT:-1,ACTION:-1,ALWAYS:0,ON_EVENT:1,NEVER:2,ON_TRIGGER:3,UP:1,DOWN:2,LEFT:3,RIGHT:4,CENTER:5,STRAIGHT_LINK:0,LINEAR_LINK:1,SPLINE_LINK:2,NORMAL_TITLE:0,NO_TITLE:1,TRANSPARENT_TITLE:2,AUTOHIDE_TITLE:3,proxy:null,node_images_path:"",debug:!1,catch_exceptions:!0,throw_errors:!0,allow_scripts:!1,registered_node_types:{},node_types_by_file_extension:{},Nodes:{},searchbox_extras:{}, -registerNodeType:function(a,b){if(!b.prototype)throw"Cannot register a simple object, it must be a class with a prototype";b.type=a;g.debug&&console.log("Node registered: "+a);a.split("/");var d=b.name,h=a.lastIndexOf("/");b.category=a.substr(0,h);b.title||(b.title=d);if(b.prototype)for(var f in q.prototype)b.prototype[f]||(b.prototype[f]=q.prototype[f]);if(h=this.registered_node_types[a])console.log("replacing node type: "+a);else if(Object.hasOwnProperty(b.prototype,"shape")||Object.defineProperty(b.prototype, -"shape",{set:function(a){switch(a){case "default":delete this._shape;break;case "box":this._shape=g.BOX_SHAPE;break;case "round":this._shape=g.ROUND_SHAPE;break;case "circle":this._shape=g.CIRCLE_SHAPE;break;case "card":this._shape=g.CARD_SHAPE;break;default:this._shape=a}},get:function(a){return this._shape},enumerable:!0,configurable:!0}),b.prototype.onPropertyChange&&console.warn("LiteGraph node class "+a+" has onPropertyChange method, it must be called onPropertyChanged with d at the end"),b.supported_extensions)for(f in b.supported_extensions){var c= -b.supported_extensions[f];c&&c.constructor===String&&(this.node_types_by_file_extension[c.toLowerCase()]=b)}this.registered_node_types[a]=b;b.constructor.name&&(this.Nodes[d]=b);if(g.onNodeTypeRegistered)g.onNodeTypeRegistered(a,b);if(h&&g.onNodeTypeReplaced)g.onNodeTypeReplaced(a,b,h)},unregisterNodeType:function(a){var b=a.constructor===String?this.registered_node_types[a]:a;if(!b)throw"node type not found: "+a;delete this.registered_node_types[b.type];b.constructor.name&&delete this.Nodes[b.constructor.name]}, -wrapFunctionAsNode:function(a,b,d,h,f){for(var c=Array(b.length),r="",l=g.getParameterNames(b),e=0;er&&(r=f.size[0]),l+=f.size[1]+a+g.NODE_TITLE_HEIGHT;b+=r+a}this.setDirtyCanvas(!0,!0)};c.prototype.getTime=function(){return this.globaltime}; -c.prototype.getFixedTime=function(){return this.fixedtime};c.prototype.getElapsedTime=function(){return this.elapsed_time};c.prototype.sendEventToAllNodes=function(a,b,d){d=d||g.ALWAYS;var h=this._nodes_in_order?this._nodes_in_order:this._nodes;if(h)for(var f=0,c=h.length;fq&&(q=f.size[0]),c+=f.size[1]+a+g.NODE_TITLE_HEIGHT;b+=q+a}this.setDirtyCanvas(!0,!0)};c.prototype.getTime=function(){return this.globaltime}; +c.prototype.getFixedTime=function(){return this.fixedtime};c.prototype.getElapsedTime=function(){return this.elapsed_time};c.prototype.sendEventToAllNodes=function(a,b,d){d=d||g.ALWAYS;var h=this._nodes_in_order?this._nodes_in_order:this._nodes;if(h)for(var f=0,E=h.length;f=g.MAX_NUMBER_OF_NODES)throw"LiteGraph: max number of nodes in a graph reached"; null==a.id||-1==a.id?a.id=++this.last_node_id:this.last_node_ida.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});this.id=-1;this.type=null;this.inputs=[];this.outputs=[];this.connections=[];this.properties={};this.properties_info=[];this.flags={}};q.prototype.configure=function(a){this.graph&&this.graph._version++; +if(!h.serialize){console.warn("weird LLink bug, link info is not a LLink but a regular object");var f=new l;for(b in h)f[b]=h[b];h=this.links[b]=f}d.push(h.serialize())}h=[];for(b=0;ba.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});this.id=-1;this.type=null;this.inputs=[];this.outputs=[];this.connections=[];this.properties={};this.properties_info=[];this.flags={}};p.prototype.configure=function(a){this.graph&&this.graph._version++; for(var b in a)if("properties"==b)for(var d in a.properties){if(this.properties[d]=a.properties[d],this.onPropertyChanged)this.onPropertyChanged(d,a.properties[d])}else null!=a[b]&&("object"==typeof a[b]?this[b]&&this[b].configure?this[b].configure(a[b]):this[b]=g.cloneObject(a[b],this[b]):this[b]=a[b]);a.title||(this.title=this.constructor.title);if(this.onConnectionsChange){if(this.inputs)for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d._data=b,this.outputs[a].links))for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d.type=b,this.outputs[a].links))for(d=0;d=this.inputs.length||null==this.inputs[a].link)){var d=this.graph.links[this.inputs[a].link];if(!d)return null;if(!b)return d.data; -var h=this.graph.getNodeById(d.origin_id);if(!h)return d.data;if(h.updateOutputData)h.updateOutputData(d.origin_slot);else if(h.onExecute)h.onExecute();return d.data}};q.prototype.getInputDataType=function(a){if(!this.inputs||a>=this.inputs.length||null==this.inputs[a].link)return null;a=this.graph.links[this.inputs[a].link];if(!a)return null;var b=this.graph.getNodeById(a.origin_id);return b?(a=b.outputs[a.origin_slot])?a.type:null:a.type};q.prototype.getInputDataByName=function(a,b){var d=this.findInputSlot(a); -return-1==d?null:this.getInputData(d,b)};q.prototype.isInputConnected=function(a){return this.inputs?a=this.inputs.length)return null;a=this.inputs[a];return a&&null!==a.link?(a=this.graph.links[a.link])?this.graph.getNodeById(a.origin_id):null:null};q.prototype.getInputOrProperty=function(a){if(!this.inputs|| -!this.inputs.length)return this.properties?this.properties[a]:null;for(var b=0,d=this.inputs.length;b=this.outputs.length?null:this.outputs[a]._data};q.prototype.getOutputInfo=function(a){return this.outputs?a=this.outputs.length)return null;a=this.outputs[a];if(!a.links||0==a.links.length)return null;for(var b=[],d=0;da&&this.pos[1]-f-db)return!0;return!1};q.prototype.getSlotInPosition=function(a,b){var d=new Float32Array(2); -if(this.inputs)for(var h=0,f=this.inputs.length;h=this.outputs.length)){var d=this.outputs[a];if(d&&(d._data=b,this.outputs[a].links))for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d.type=b,this.outputs[a].links))for(d=0;d=this.inputs.length||null==this.inputs[a].link)){var d=this.graph.links[this.inputs[a].link];if(!d)return null;if(!b)return d.data; +var h=this.graph.getNodeById(d.origin_id);if(!h)return d.data;if(h.updateOutputData)h.updateOutputData(d.origin_slot);else if(h.onExecute)h.onExecute();return d.data}};p.prototype.getInputDataType=function(a){if(!this.inputs||a>=this.inputs.length||null==this.inputs[a].link)return null;a=this.graph.links[this.inputs[a].link];if(!a)return null;var b=this.graph.getNodeById(a.origin_id);return b?(a=b.outputs[a.origin_slot])?a.type:null:a.type};p.prototype.getInputDataByName=function(a,b){var d=this.findInputSlot(a); +return-1==d?null:this.getInputData(d,b)};p.prototype.isInputConnected=function(a){return this.inputs?a=this.inputs.length)return null;a=this.inputs[a];return a&&null!==a.link?(a=this.graph.links[a.link])?this.graph.getNodeById(a.origin_id):null:null};p.prototype.getInputOrProperty=function(a){if(!this.inputs|| +!this.inputs.length)return this.properties?this.properties[a]:null;for(var b=0,d=this.inputs.length;b=this.outputs.length?null:this.outputs[a]._data};p.prototype.getOutputInfo=function(a){return this.outputs?a=this.outputs.length)return null;a=this.outputs[a];if(!a.links||0==a.links.length)return null;for(var b=[],d=0;da&&this.pos[1]-f-db)return!0;return!1};p.prototype.getSlotInPosition=function(a,b){var d=new Float32Array(2); +if(this.inputs)for(var h=0,f=this.inputs.length;h=this.outputs.length)return g.debug&&console.log("Connect: Error, slot number not found"),null;b&&b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"target node is null";if(b==this)return null;if(d.constructor===String){if(d=b.findInputSlot(d),-1==d)return g.debug&&console.log("Connect: Error, no slot of name "+d),null}else{if(d===g.EVENT)return null;if(!b.inputs||d>=b.inputs.length)return g.debug&&console.log("Connect: Error, slot number not found"),null}null!=b.inputs[d].link&& -b.disconnectInput(d);var h=this.outputs[a];if(b.onConnectInput&&!1===b.onConnectInput(d,h.type,h))return null;var f=b.inputs[d],c=null;if(g.isValidConnection(h.type,f.type)){c=new m(++this.graph.last_link_id,f.type,this.id,a,b.id,d);this.graph.links[c.id]=c;null==h.links&&(h.links=[]);h.links.push(c.id);b.inputs[d].link=c.id;this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(g.OUTPUT,a,!0,c,h);if(b.onConnectionsChange)b.onConnectionsChange(g.INPUT,d,!0,c,f);this.graph&& -this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(g.INPUT,b,d,this,a),this.graph.onNodeConnectionChange(g.OUTPUT,this,a,b,d))}this.setDirtyCanvas(!1,!0);this.graph.connectionChange(this,c);return c};q.prototype.disconnectOutput=function(a,b){if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return g.debug&&console.log("Connect: Error, no slot of name "+a),!1}else if(!this.outputs||a>=this.outputs.length)return g.debug&&console.log("Connect: Error, slot number not found"), -!1;var d=this.outputs[a];if(!d||!d.links||0==d.links.length)return!1;if(b){b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"Target Node not found";for(var h=0,f=d.links.length;h=this.inputs.length)return g.debug&&console.log("Connect: Error, slot number not found"),!1;var b=this.inputs[a];if(!b)return!1;var d=this.inputs[a].link;this.inputs[a].link=null;var h=this.graph.links[d];if(h){var f=this.graph.getNodeById(h.origin_id);if(!f)return!1;var c=f.outputs[h.origin_slot];if(!c||!c.links||0==c.links.length)return!1;for(var r=0,l=c.links.length;r< -l;r++)if(c.links[r]==d){c.links.splice(r,1);break}delete this.graph.links[d];this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(g.INPUT,a,!1,h,b);if(f.onConnectionsChange)f.onConnectionsChange(g.OUTPUT,r,!1,h,c);this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(g.OUTPUT,f,r),this.graph.onNodeConnectionChange(g.INPUT,this,a))}this.setDirtyCanvas(!1,!0);this.graph.connectionChange(this);return!0};q.prototype.getConnectionPos=function(a, +b.disconnectInput(d);var h=this.outputs[a];if(b.onConnectInput&&!1===b.onConnectInput(d,h.type,h))return null;var f=b.inputs[d],c=null;if(g.isValidConnection(h.type,f.type)){c=new l(++this.graph.last_link_id,f.type,this.id,a,b.id,d);this.graph.links[c.id]=c;null==h.links&&(h.links=[]);h.links.push(c.id);b.inputs[d].link=c.id;this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(g.OUTPUT,a,!0,c,h);if(b.onConnectionsChange)b.onConnectionsChange(g.INPUT,d,!0,c,f);this.graph&& +this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(g.INPUT,b,d,this,a),this.graph.onNodeConnectionChange(g.OUTPUT,this,a,b,d))}this.setDirtyCanvas(!1,!0);this.graph.connectionChange(this,c);return c};p.prototype.disconnectOutput=function(a,b){if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return g.debug&&console.log("Connect: Error, no slot of name "+a),!1}else if(!this.outputs||a>=this.outputs.length)return g.debug&&console.log("Connect: Error, slot number not found"), +!1;var d=this.outputs[a];if(!d||!d.links||0==d.links.length)return!1;if(b){b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"Target Node not found";for(var h=0,f=d.links.length;h=this.inputs.length)return g.debug&&console.log("Connect: Error, slot number not found"),!1;var b=this.inputs[a];if(!b)return!1;var d=this.inputs[a].link;this.inputs[a].link=null;var h=this.graph.links[d];if(h){var f=this.graph.getNodeById(h.origin_id);if(!f)return!1;var c=f.outputs[h.origin_slot];if(!c||!c.links||0==c.links.length)return!1;for(var q=0,e=c.links.length;q< +e;q++)if(c.links[q]==d){c.links.splice(q,1);break}delete this.graph.links[d];this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(g.INPUT,a,!1,h,b);if(f.onConnectionsChange)f.onConnectionsChange(g.OUTPUT,q,!1,h,c);this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(g.OUTPUT,f,q),this.graph.onNodeConnectionChange(g.INPUT,this,a))}this.setDirtyCanvas(!1,!0);this.graph.connectionChange(this);return!0};p.prototype.getConnectionPos=function(a, b,d){d=d||new Float32Array(2);var h=0;a&&this.inputs&&(h=this.inputs.length);!a&&this.outputs&&(h=this.outputs.length);var f=0.5*g.NODE_SLOT_HEIGHT;if(this.flags.collapsed)return b=this._collapsed_width||g.NODE_COLLAPSED_WIDTH,this.horizontal?(d[0]=this.pos[0]+0.5*b,d[1]=a?this.pos[1]-g.NODE_TITLE_HEIGHT:this.pos[1]):(d[0]=a?this.pos[0]:this.pos[0]+b,d[1]=this.pos[1]-0.5*g.NODE_TITLE_HEIGHT),d;if(a&&-1==b)return d[0]=this.pos[0]+0.5*g.NODE_TITLE_HEIGHT,d[1]=this.pos[1]+0.5*g.NODE_TITLE_HEIGHT,d;if(a&& h>b&&this.inputs[b].pos)return d[0]=this.pos[0]+this.inputs[b].pos[0],d[1]=this.pos[1]+this.inputs[b].pos[1],d;if(!a&&h>b&&this.outputs[b].pos)return d[0]=this.pos[0]+this.outputs[b].pos[0],d[1]=this.pos[1]+this.outputs[b].pos[1],d;if(this.horizontal)return d[0]=this.pos[0]+this.size[0]/h*(b+0.5),d[1]=a?this.pos[1]-g.NODE_TITLE_HEIGHT:this.pos[1]+this.size[1],d;d[0]=a?this.pos[0]+f:this.pos[0]+this.size[0]+1-f;d[1]=this.pos[1]+(b+0.7)*g.NODE_SLOT_HEIGHT+(this.constructor.slot_start_y||0);return d}; -q.prototype.alignToGrid=function(){this.pos[0]=g.CANVAS_GRID_SIZE*Math.round(this.pos[0]/g.CANVAS_GRID_SIZE);this.pos[1]=g.CANVAS_GRID_SIZE*Math.round(this.pos[1]/g.CANVAS_GRID_SIZE)};q.prototype.trace=function(a){this.console||(this.console=[]);this.console.push(a);this.console.length>q.MAX_CONSOLE&&this.console.shift();this.graph.onNodeTrace(this,a)};q.prototype.setDirtyCanvas=function(a,b){this.graph&&this.graph.sendActionToCanvas("setDirty",[a,b])};q.prototype.loadImage=function(a){var b=new Image; -b.src=g.node_images_path+a;b.ready=!1;var d=this;b.onload=function(){this.ready=!0;d.setDirtyCanvas(!0)};return b};q.prototype.captureInput=function(a){if(this.graph&&this.graph.list_of_graphcanvas)for(var b=this.graph.list_of_graphcanvas,d=0;dp.MAX_CONSOLE&&this.console.shift();this.graph.onNodeTrace(this,a)};p.prototype.setDirtyCanvas=function(a,b){this.graph&&this.graph.sendActionToCanvas("setDirty",[a,b])};p.prototype.loadImage=function(a){var b=new Image; +b.src=g.node_images_path+a;b.ready=!1;var d=this;b.onload=function(){this.ready=!0;d.setDirtyCanvas(!0)};return b};p.prototype.captureInput=function(a){if(this.graph&&this.graph.list_of_graphcanvas)for(var b=this.graph.list_of_graphcanvas,d=0;da.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});Object.defineProperty(this,"size",{set:function(a){!a||2>a.length||(this._size[0]=Math.max(140,a[0]),this._size[1]=Math.max(80,a[1]))},get:function(){return this._size},enumerable:!0})};k.prototype.configure=function(a){this.title=a.title;this._bounding.set(a.bounding);this.color=a.color; this.font=a.font};k.prototype.serialize=function(){var a=this._bounding;return{title:this.title,bounding:[Math.round(a[0]),Math.round(a[1]),Math.round(a[2]),Math.round(a[3])],color:this.color,font:this.font}};k.prototype.move=function(a,b,d){this._pos[0]+=a;this._pos[1]+=b;if(!d)for(d=0;dg.getTime()-this.last_mouseclick;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;this.canvas.focus();g.closeAllContextMenus(b);if(!this.onMouse||!0!=this.onMouse(a)){if(1==a.which){a.ctrlKey&&(this.dragging_rectangle=new Float32Array(4),this.dragging_rectangle[0]=a.canvasX,this.dragging_rectangle[1]=a.canvasY,this.dragging_rectangle[2]=1,this.dragging_rectangle[3]=1,h=!0);var c= -!1;if(d&&this.allow_interaction&&!h&&!this.read_only){this.live_mode||d.flags.pinned||this.bringToFront(d);if(!this.connecting_node&&!d.flags.collapsed&&!this.live_mode)if(!h&&!1!==d.resizable&&z(a.canvasX,a.canvasY,d.pos[0]+d.size[0]-5,d.pos[1]+d.size[1]-5,10,10))this.resizing_node=d,this.canvas.style.cursor="se-resize",h=!0;else{if(d.outputs)for(var r=0,l=d.outputs.length;rc[0]+4||a.canvasYc[1]+4)){this.showLinkMenu(d, +!1;if(d&&this.allow_interaction&&!h&&!this.read_only){this.live_mode||d.flags.pinned||this.bringToFront(d);if(!this.connecting_node&&!d.flags.collapsed&&!this.live_mode)if(!h&&!1!==d.resizable&&z(a.canvasX,a.canvasY,d.pos[0]+d.size[0]-5,d.pos[1]+d.size[1]-5,10,10))this.resizing_node=d,this.canvas.style.cursor="se-resize",h=!0;else{if(d.outputs)for(var q=0,m=d.outputs.length;qc[0]+4||a.canvasYc[1]+4)){this.showLinkMenu(d, a);this.over_link_center=null;break}this.selected_group=this.graph.getGroupOnPos(a.canvasX,a.canvasY);this.selected_group_resizing=!1;this.selected_group&&!this.read_only&&(a.ctrlKey&&(this.dragging_rectangle=null),10>C([a.canvasX,a.canvasY],[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])*this.ds.scale?this.selected_group_resizing=!0:this.selected_group.recomputeInsideNodes());f&&!this.read_only&&this.allow_searchbox&&this.showSearchBox(a); c=!0}!h&&c&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}else 2!=a.which&&3==a.which&&(this.read_only||this.processContextMenu(d,a));this.last_mouse[0]=a.localX;this.last_mouse[1]=a.localY;this.last_mouseclick=g.getTime();this.last_mouse_dragging=!0;this.graph.change();(!b.document.activeElement||"input"!=b.document.activeElement.nodeName.toLowerCase()&&"textarea"!=b.document.activeElement.nodeName.toLowerCase())&&a.preventDefault();a.stopPropagation();if(this.onMouseDown)this.onMouseDown(a); return!1}}};e.prototype.processMouseMove=function(a){this.autoresize&&this.resize();if(this.graph){e.active_canvas=this;this.adjustMouseEvent(a);var b=[a.localX,a.localY],d=[b[0]-this.last_mouse[0],b[1]-this.last_mouse[1]];this.last_mouse=b;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;a.dragging=this.last_mouse_dragging;this.node_widget&&(this.processNodeWidgets(this.node_widget[0],this.canvas_mouse,a,this.node_widget[1]),this.dirty_canvas=!0);if(this.dragging_rectangle)this.dragging_rectangle[2]= a.canvasX-this.dragging_rectangle[0],this.dragging_rectangle[3]=a.canvasY-this.dragging_rectangle[1],this.dirty_canvas=!0;else if(this.selected_group&&!this.read_only)this.selected_group_resizing?this.selected_group.size=[a.canvasX-this.selected_group.pos[0],a.canvasY-this.selected_group.pos[1]]:(this.selected_group.move(d[0]/this.ds.scale,d[1]/this.ds.scale,a.ctrlKey),this.selected_group._nodes.length&&(this.dirty_canvas=!0)),this.dirty_bgcanvas=!0;else if(this.dragging_canvas)this.ds.offset[0]+= d[0]/this.ds.scale,this.ds.offset[1]+=d[1]/this.ds.scale,this.dirty_bgcanvas=this.dirty_canvas=!0;else if(this.allow_interaction&&!this.read_only){this.connecting_node&&(this.dirty_canvas=!0);for(var h=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes),b=0,f=this.graph._nodes.length;br[0]+4||a.canvasYr[1]+4)){f=c;break}}f!=this.over_link_center&&(this.over_link_center=f,this.dirty_canvas=!0);this.canvas&&(this.canvas.style.cursor="")}if(this.node_capturing_input&&this.node_capturing_input!=h&&this.node_capturing_input.onMouseMove)this.node_capturing_input.onMouseMove(a, +a.canvasY,h.pos[0]+h.size[0]-5,h.pos[1]+h.size[1]-5,5,5)?this.canvas.style.cursor="se-resize":this.canvas.style.cursor="crosshair")}else{f=null;for(b=0;bq[0]+4||a.canvasYq[1]+4)){f=c;break}}f!=this.over_link_center&&(this.over_link_center=f,this.dirty_canvas=!0);this.canvas&&(this.canvas.style.cursor="")}if(this.node_capturing_input&&this.node_capturing_input!=h&&this.node_capturing_input.onMouseMove)this.node_capturing_input.onMouseMove(a, [a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]],this);if(this.node_dragged&&!this.live_mode){for(b in this.selected_nodes)h=this.selected_nodes[b],h.pos[0]+=d[0]/this.ds.scale,h.pos[1]+=d[1]/this.ds.scale;this.dirty_bgcanvas=this.dirty_canvas=!0}this.resizing_node&&!this.live_mode&&(this.resizing_node.size[0]=a.canvasX-this.resizing_node.pos[0],this.resizing_node.size[1]=a.canvasY-this.resizing_node.pos[1],d=Math.max(this.resizing_node.inputs?this.resizing_node.inputs.length: 0,this.resizing_node.outputs?this.resizing_node.outputs.length:0)*g.NODE_SLOT_HEIGHT+(this.resizing_node.widgets?this.resizing_node.widgets.length:0)*(g.NODE_WIDGET_HEIGHT+4)+4,this.resizing_node.size[1]a.click_time&&z(a.canvasX,a.canvasY,h.pos[0],h.pos[1]-g.NODE_TITLE_HEIGHT,g.NODE_TITLE_HEIGHT,g.NODE_TITLE_HEIGHT)&&h.collapse();this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_dragged.pos[0]=Math.round(this.node_dragged.pos[0]); this.node_dragged.pos[1]=Math.round(this.node_dragged.pos[1]);this.graph.config.align_to_grid&&this.node_dragged.alignToGrid();if(this.onNodeMoved)this.onNodeMoved(this.node_dragged);this.node_dragged=null}else{h=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes);!h&&300>a.click_time&&this.deselectAllNodes();this.dirty_canvas=!0;this.dragging_canvas=!1;if(this.node_over&&this.node_over.onMouseUp)this.node_over.onMouseUp(a,[a.canvasX-this.node_over.pos[0],a.canvasY-this.node_over.pos[1]], this);if(this.node_capturing_input&&this.node_capturing_input.onMouseUp)this.node_capturing_input.onMouseUp(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]])}}else 2==a.which?(this.dirty_canvas=!0,this.dragging_canvas=!1):3==a.which&&(this.dirty_canvas=!0,this.dragging_canvas=!1);this.graph.change();a.stopPropagation();a.preventDefault();return!1}};e.prototype.processMouseWheel=function(a){if(this.graph&&this.allow_dragcanvas){var b=null!=a.wheelDeltaY?a.wheelDeltaY: --60*a.detail;this.adjustMouseEvent(a);var d=this.ds.scale;0b&&(d*=1/1.1);this.ds.changeScale(d,[a.localX,a.localY]);this.graph.change();a.preventDefault();return!1}};e.prototype.isOverNodeBox=function(a,b,d){var h=g.NODE_TITLE_HEIGHT;return z(b,d,a.pos[0]+2,a.pos[1]+2-h,h-4,h-4)?!0:!1};e.prototype.isOverNodeInput=function(a,b,d,h){if(a.inputs)for(var f=0,g=a.inputs.length;fb&&(d*=1/1.1);this.ds.changeScale(d,[a.localX,a.localY]);this.graph.change();a.preventDefault();return!1}};e.prototype.isOverNodeBox=function(a,b,d){var h=g.NODE_TITLE_HEIGHT;return z(b,d,a.pos[0]+2,a.pos[1]+2-h,h-4,h-4)?!0:!1};e.prototype.isOverNodeInput=function(a,b,d,h){if(a.inputs)for(var f=0,g=a.inputs.length;fd-this.graph._last_trigger_time)&&this.drawBackCanvas();(this.dirty_canvas||a)&&this.drawFrontCanvas();this.fps=this.render_time?1/this.render_time:0;this.frame+=1}};e.prototype.drawFrontCanvas=function(){this.dirty_canvas=!1;this.ctx||(this.ctx=this.bgcanvas.getContext("2d"));var a=this.ctx;if(a){a.start2D&&a.start2D();var b=this.canvas;a.restore();a.setTransform(1,0,0,1,0,0);this.dirty_area&&(a.save(), a.beginPath(),a.rect(this.dirty_area[0],this.dirty_area[1],this.dirty_area[2],this.dirty_area[3]),a.clip());this.clear_background&&a.clearRect(0,0,b.width,b.height);this.bgcanvas==this.canvas?this.drawBackCanvas():a.drawImage(this.bgcanvas,0,0);if(this.onRender)this.onRender(b,a);this.show_info&&this.renderInfo(a);if(this.graph){a.save();this.ds.toCanvasContext(a);for(var b=this.computeVisibleNodes(null,this.visible_nodes),d=0;dthis.ds.scale;if(this.live_mode){if(!a.flags.collapsed&&(b.shadowColor="transparent",a.onDrawForeground))a.onDrawForeground(b,this,this.canvas)}else{var c= -this.editor_alpha;b.globalAlpha=c;this.render_shadows&&!f?(b.shadowColor=g.DEFAULT_SHADOW_COLOR,b.shadowOffsetX=2*this.ds.scale,b.shadowOffsetY=2*this.ds.scale,b.shadowBlur=3*this.ds.scale):b.shadowColor="transparent";if(!a.flags.collapsed||!a.onDrawCollapsed||!0!=a.onDrawCollapsed(b,this)){var r=a._shape||g.BOX_SHAPE;s.set(a.size);var l=a.horizontal;if(a.flags.collapsed){b.font=this.inner_text_font;var e=a.getTitle?a.getTitle():a.title;null!=e&&(a._collapsed_width=Math.min(a.size[0],b.measureText(e).width+ -2*g.NODE_TITLE_HEIGHT),s[0]=a._collapsed_width,s[1]=0)}a.clip_area&&(b.save(),b.beginPath(),r==g.BOX_SHAPE?b.rect(0,0,s[0],s[1]):r==g.ROUND_SHAPE?b.roundRect(0,0,s[0],s[1],10):r==g.CIRCLE_SHAPE&&b.arc(0.5*s[0],0.5*s[1],0.5*s[0],0,2*Math.PI),b.clip());a.has_errors&&(h="red");this.drawNodeShape(a,b,s,d,h,a.is_selected,a.mouseOver);b.shadowColor="transparent";if(a.onDrawForeground)a.onDrawForeground(b,this,this.canvas);b.textAlign=l?"center":"left";b.font=this.inner_text_font;h=!f;r=this.connecting_output; -b.lineWidth=1;var e=0,p=new Float32Array(2);if(!a.flags.collapsed){if(a.inputs)for(d=0;dthis.ds.scale,n=a._shape||a.constructor.shape||g.ROUND_SHAPE,p=a.constructor.title_mode,v=!0;p==g.TRANSPARENT_TITLE?v=!1:p==g.AUTOHIDE_TITLE&&r&&(v= -!0);A[0]=0;A[1]=v?-f:0;A[2]=d[0]+1;A[3]=v?d[1]+f:d[1];r=b.globalAlpha;b.beginPath();n==g.BOX_SHAPE||l?b.fillRect(A[0],A[1],A[2],A[3]):n==g.ROUND_SHAPE||n==g.CARD_SHAPE?b.roundRect(A[0],A[1],A[2],A[3],this.round_radius,n==g.CARD_SHAPE?0:this.round_radius):n==g.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0],0,2*Math.PI);b.fill();a.flags.collapsed||(b.shadowColor="transparent",b.fillStyle="rgba(0,0,0,0.2)",b.fillRect(0,-1,A[2],2));b.shadowColor="transparent";if(a.onDrawBackground)a.onDrawBackground(b, -this,this.canvas);if(v||p==g.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,f,d,this.ds.scale,h);else if(p!=g.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){v=a.constructor.title_color||h;a.flags.collapsed&&(b.shadowColor=g.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var t=e.gradients[v];t||(t=e.gradients[v]=b.createLinearGradient(0,0,400,0),t.addColorStop(0,v),t.addColorStop(1,"#000"));b.fillStyle=t}else b.fillStyle=v;b.beginPath();n==g.BOX_SHAPE||l?b.rect(0, --f,d[0]+1,f):n!=g.ROUND_SHAPE&&n!=g.CARD_SHAPE||b.roundRect(0,-f,d[0]+1,f,this.round_radius,a.flags.collapsed?this.round_radius:0);b.fill();b.shadowColor="transparent"}if(a.onDrawTitleBox)a.onDrawTitleBox(b,f,d,this.ds.scale);else n==g.ROUND_SHAPE||n==g.CIRCLE_SHAPE||n==g.CARD_SHAPE?(l&&(b.fillStyle="black",b.beginPath(),b.arc(0.5*f,-0.5*f,6,0,2*Math.PI),b.fill()),b.fillStyle=a.boxcolor||g.NODE_DEFAULT_BOXCOLOR,l?b.fillRect(0.5*f-5,-0.5*f-5,10,10):(b.beginPath(),b.arc(0.5*f,-0.5*f,5,0,2*Math.PI), -b.fill())):(l&&(b.fillStyle="black",b.fillRect(0.5*(f-10)-1,-0.5*(f+10)-1,12,12)),b.fillStyle=a.boxcolor||g.NODE_DEFAULT_BOXCOLOR,b.fillRect(0.5*(f-10),-0.5*(f+10),10,10));b.globalAlpha=r;if(a.onDrawTitleText)a.onDrawTitleText(b,f,d,this.ds.scale,this.title_text_font,c);!l&&(b.font=this.title_text_font,l=String(a.getTitle()))&&(b.fillStyle=c?"white":a.constructor.title_text_color||this.node_title_color,a.flags.collapsed?(b.textAlign="left",b.measureText(l),b.fillText(l.substr(0,20),f,g.NODE_TITLE_TEXT_Y- -f),b.textAlign="left"):(b.textAlign="left",b.fillText(l,f,g.NODE_TITLE_TEXT_Y-f)));if(a.onDrawTitle)a.onDrawTitle(b)}if(c){if(a.onBounding)a.onBounding(A);p==g.TRANSPARENT_TITLE&&(A[1]-=f,A[3]+=f);b.lineWidth=1;b.globalAlpha=0.8;b.beginPath();n==g.BOX_SHAPE?b.rect(-6+A[0],-6+A[1],12+A[2],12+A[3]):n==g.ROUND_SHAPE||n==g.CARD_SHAPE&&a.flags.collapsed?b.roundRect(-6+A[0],-6+A[1],12+A[2],12+A[3],2*this.round_radius):n==g.CARD_SHAPE?b.roundRect(-6+A[0],-6+A[1],12+A[2],12+A[3],2*this.round_radius,2):n== -g.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0]+6,0,2*Math.PI);b.strokeStyle="#FFF";b.stroke();b.strokeStyle=h;b.globalAlpha=1}};var n=new Float32Array(4),v=new Float32Array(4),x=new Float32Array(2),l=new Float32Array(2);e.prototype.drawConnections=function(a){var b=g.getTime(),d=this.visible_area;n[0]=d[0]-20;n[1]=d[1]-20;n[2]=d[2]+40;n[3]=d[3]+40;a.lineWidth=this.connections_width;a.fillStyle="#AAA";a.strokeStyle="#AAA";a.globalAlpha=this.editor_alpha;for(var d=this.graph._nodes,h=0,f=d.length;h< -f;++h){var c=d[h];if(c.inputs&&c.inputs.length)for(var r=0;rv[2]&&(v[0]+=v[2],v[2]=Math.abs(v[2]));0>v[3]&&(v[1]+=v[3],v[3]=Math.abs(v[3]));if(u(v,n)){var q=p.outputs[s],s=c.inputs[r];if(q&& -s&&(p=q.dir||(p.horizontal?g.DOWN:g.RIGHT),s=s.dir||(c.horizontal?g.UP:g.LEFT),this.renderLink(a,k,t,e,!1,0,null,p,s),e&&e._last_time&&1E3>b-e._last_time)){var q=2-0.002*(b-e._last_time),J=a.globalAlpha;a.globalAlpha=J*q;this.renderLink(a,k,t,e,!0,q,"white",p,s);a.globalAlpha=J}}}}}}a.globalAlpha=1};e.prototype.renderLink=function(a,b,d,h,f,c,r,l,n,p){h&&this.visible_links.push(h);!r&&h&&(r=h.color||e.link_type_colors[h.type]);r||(r=this.default_link_color);null!=h&&this.highlighted_links[h.id]&& -(r="#FFF");l=l||g.RIGHT;n=n||g.LEFT;var v=C(b,d);this.render_connections_border&&0.6b[1]?0:Math.PI,a.save(),a.translate(t[0],t[1]),a.rotate(s),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore(),a.save(),a.translate(p[0],p[1]),a.rotate(k), -a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill());if(c)for(a.fillStyle=r,t=0;5>t;++t)c=(0.001*g.getTime()+0.2*t)%1,f=this.computeConnectionPoint(b,d,c,l,n),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill()};e.prototype.computeConnectionPoint=function(a,b,d,h,f){h=h||g.RIGHT;f=f||g.LEFT;var c=C(a,b),r=[a[0],a[1]],l=[b[0],b[1]];switch(h){case g.LEFT:r[0]+=-0.25*c;break;case g.RIGHT:r[0]+=0.25*c;break;case g.UP:r[1]+= --0.25*c;break;case g.DOWN:r[1]+=0.25*c}switch(f){case g.LEFT:l[0]+=-0.25*c;break;case g.RIGHT:l[0]+=0.25*c;break;case g.UP:l[1]+=-0.25*c;break;case g.DOWN:l[1]+=0.25*c}h=(1-d)*(1-d)*(1-d);f=3*(1-d)*(1-d)*d;c=3*(1-d)*d*d;d*=d*d;return[h*a[0]+f*r[0]+c*l[0]+d*b[0],h*a[1]+f*r[1]+c*l[1]+d*b[1]]};e.prototype.drawExecutionOrder=function(a){a.shadowColor="transparent";a.globalAlpha=0.25;a.textAlign="center";a.strokeStyle="white";a.globalAlpha=0.75;for(var b=this.visible_nodes,d=0;dt.last_y&&rt.options.max&&(t.value=t.options.max);else if("mousedown"== -d.type)if((r=t.options.values)&&r.constructor===Function&&(r=t.options.values(t,a)),c=40>c?-1:c>l-40?1:0,"number"==t.type)t.value+=0.1*c*(t.options.step||1),null!=t.options.min&&t.valuet.options.max&&(t.value=t.options.max);else if(c){var e=r.constructor===Array?r:Object.keys(r),s=e.indexOf(t.value)+c;s>=r.length&&(s=0);0>s&&(s=e.length-1);t.value=r.constructor===Array?r[s]:r[e[s]]}else new g.ContextMenu(r,{scale:Math.max(1,this.ds.scale), -event:d,className:"dark",callback:s.bind(t)},e),s=function(a,b,d){this.value=a;f(this,a);n.dirty_canvas=!0;return!1};else"mouseup"==d.type&&"number"==t.type&&(c=40>c?-1:c>l-40?1:0,200>d.click_time&&0==c&&this.prompt("Value",t.value,function(a){this.value=Number(a);f(this,this.value)}.bind(t),d));h!=t.value&&setTimeout(function(){f(this,this.value)}.bind(t),20);this.dirty_canvas=!0;break;case "toggle":"mousedown"==d.type&&(t.value=!t.value,setTimeout(function(){f(t,t.value)},20));break;case "string":case "text":"mousedown"== -d.type&&this.prompt("Value",t.value,function(a){this.value=a;f(this,a)}.bind(t),d);break;default:t.mouse&&t.mouse(ctx,d,[c,r],a)}return t}}return null};e.prototype.drawGroups=function(a,b){if(this.graph){var d=this.graph._groups;b.save();b.globalAlpha=0.5*this.editor_alpha;for(var h=0;hd&&0.01>b.editor_alpha&&(clearInterval(h),1>d&&(b.live_mode= -!0));1"+n+""+a+"",value:n});if(l.length)return new g.ContextMenu(l,{event:d,callback:c,parentMenu:h,allow_html:!0,node:f},b),!1}};e.decodeHTML=function(a){var b=document.createElement("div"); -b.innerText=a;return b.innerHTML};e.onResizeNode=function(a,b,d,h,f){f&&(f.size=f.computeSize(),f.setDirtyCanvas(!0,!0))};e.prototype.showLinkMenu=function(a,b){var d=this;console.log(a);var h=new g.ContextMenu(["Add Node",null,"Delete"],{event:b,title:null!=a.data?a.data.constructor.name:null,callback:function(b,g,c){switch(b){case "Add Node":e.onMenuAdd(null,null,c,h,function(b){console.log("node autoconnect");var f=d.graph.getNodeById(a.origin_id),h=d.graph.getNodeById(a.target_id);b.inputs&&b.inputs.length&& -b.outputs&&b.outputs.length&&f.outputs[a.origin_slot].type==b.inputs[0].type&&b.outputs[0].type==h.inputs[0].type&&(f.connect(a.origin_slot,b,0),b.connect(0,h,a.target_slot),b.pos[0]-=0.5*b.size[0])});break;case "Delete":d.graph.removeLink(a.id)}}});return!1};e.onShowPropertyEditor=function(a,b,d,h,f){function g(){var b=n.value;"Number"==a.type?b=Number(b):"Boolean"==a.type&&(b=Boolean(b));f[c]=b;l.parentNode&&l.parentNode.removeChild(l);f.setDirtyCanvas(!0,!0)}var c=a.property||"title";b=f[c];var l= -document.createElement("div");l.className="graphdialog";l.innerHTML="";l.querySelector(".name").innerText=c;var n=l.querySelector("input");n&&(n.value=b,n.addEventListener("blur",function(a){this.focus()}),n.addEventListener("keydown",function(a){13==a.keyCode&&(g(),a.preventDefault(),a.stopPropagation())}));b=e.active_canvas.canvas;d=b.getBoundingClientRect();var p=h=-20;d&&(h-=d.left,p-=d.top);event?(l.style.left= -event.clientX+h+"px",l.style.top=event.clientY+p+"px"):(l.style.left=0.5*b.width+h+"px",l.style.top=0.5*b.height+p+"px");l.querySelector("button").addEventListener("click",g);b.parentNode.appendChild(l)};e.prototype.prompt=function(a,b,d,h){var f=this;a=a||"";var g=!1,c=document.createElement("div");c.className="graphdialog rounded";c.innerHTML=" ";c.close=function(){f.prompt_box=null;c.parentNode&& -c.parentNode.removeChild(c)};1e.search_limit)break}}p=null;if(Array.prototype.filter)p=Object.keys(g.registered_node_types).filter(s);else for(l in p= -[],g.registered_node_types)s(l)&&p.push(l);for(l=0;le.search_limit);l++);var s=function(a){var b=g.registered_node_types[a];return r&&b.filter!=r?!1:-1!==a.toLowerCase().indexOf(d)}}}var f=this,c=e.active_canvas,l=c.canvas,n=l.ownerDocument||document,p=document.createElement("div");p.className="litegraph litesearchbox graphdialog rounded";p.innerHTML="Search
"; -p.close=function(){f.search_box=null;n.body.focus();n.body.style.overflow="";setTimeout(function(){f.canvas.focus()},20);p.parentNode&&p.parentNode.removeChild(p)};var s=null;1l.height-200&&(v.style.maxHeight=l.height-a.layerY-20+"px");u.focus();return p};e.prototype.showEditPropertyValue=function(a,b,d){function h(){f(t.value)}function f(f){"number"==typeof a.properties[b]&&(f=Number(f));if("array"==g||"object"==g)f=JSON.parse(f);a.properties[b]=f;a._graph&&a._graph._version++;if(a.onPropertyChanged)a.onPropertyChanged(b, -f);if(d.onclose)d.onclose();e.close();a.setDirtyCanvas(!0,!0)}if(a&&void 0!==a.properties[b]){d=d||{};var c=a.getPropertyInfo(b),g=c.type,l="";if("string"==g||"number"==g||"array"==g||"object"==g)l="";else if("enum"==g&&c.values){var l=""}else if("boolean"== -g)l="";else{console.warn("unknown type: "+g);return}var e=this.createDialog(""+b+""+l+"",d);if("enum"==g&&c.values){var t=e.querySelector("select");t.addEventListener("change",function(a){f(a.target.value)})}else if("boolean"==g)(t=e.querySelector("input"))&&t.addEventListener("click",function(a){f(!!t.checked)});else if(t=e.querySelector("input"))t.addEventListener("blur", -function(a){this.focus()}),n=void 0!==a.properties[b]?a.properties[b]:"",n=JSON.stringify(n),t.value=n,t.addEventListener("keydown",function(a){13==a.keyCode&&(h(),a.preventDefault(),a.stopPropagation())});e.querySelector("button").addEventListener("click",h);return e}};e.prototype.createDialog=function(a,b){b=b||{};var d=document.createElement("div");d.className="graphdialog";d.innerHTML=a;var h=this.canvas.getBoundingClientRect(),f=-20,c=-20;h&&(f-=h.left,c-=h.top);b.position?(f+=b.position[0], -c+=b.position[1]):b.event?(f+=b.event.clientX,c+=b.event.clientY):(f+=0.5*this.canvas.width,c+=0.5*this.canvas.height);d.style.left=f+"px";d.style.top=c+"px";this.canvas.parentNode.appendChild(d);d.close=function(){this.parentNode&&this.parentNode.removeChild(this)};return d};e.onMenuNodeCollapse=function(a,b,d,h,f){f.collapse()};e.onMenuNodePin=function(a,b,d,h,f){f.pin()};e.onMenuNodeMode=function(a,b,d,h,f){new g.ContextMenu(["Always","On Event","On Trigger","Never"],{event:d,callback:function(a){if(f)switch(a){case "On Event":f.mode= -g.ON_EVENT;break;case "On Trigger":f.mode=g.ON_TRIGGER;break;case "Never":f.mode=g.NEVER;break;default:f.mode=g.ALWAYS}},parentMenu:h,node:f});return!1};e.onMenuNodeColors=function(a,b,d,h,f){if(!f)throw"no node for color";b=[];b.push({value:null,content:"No color"});for(var c in e.node_colors)a=e.node_colors[c],a={value:c,content:""+c+""},b.push(a);new g.ContextMenu(b,{event:d,callback:function(a){f&&((a=a.value?e.node_colors[a.value]:null)?f.constructor===g.LGraphGroup?f.color=a.groupcolor:(f.color=a.color,f.bgcolor=a.bgcolor):(delete f.color,delete f.bgcolor),f.setDirtyCanvas(!0,!0))},parentMenu:h,node:f});return!1};e.onMenuNodeShapes=function(a,b,d,h,f){if(!f)throw"no node passed";new g.ContextMenu(g.VALID_SHAPES,{event:d,callback:function(a){f&&(f.shape=a,f.setDirtyCanvas(!0))},parentMenu:h,node:f}); -return!1};e.onMenuNodeRemove=function(a,b,d,h,f){if(!f)throw"no node passed";!1!==f.removable&&(f.graph.remove(f),f.setDirtyCanvas(!0,!0))};e.onMenuNodeClone=function(a,b,d,h,f){!1!=f.clonable&&(a=f.clone())&&(a.pos=[f.pos[0]+5,f.pos[1]+5],f.graph.add(a),f.setDirtyCanvas(!0,!0))};e.node_colors={red:{color:"#322",bgcolor:"#533",groupcolor:"#A88"},brown:{color:"#332922",bgcolor:"#593930",groupcolor:"#b06634"},green:{color:"#232",bgcolor:"#353",groupcolor:"#8A8"},blue:{color:"#223",bgcolor:"#335",groupcolor:"#88A"}, -pale_blue:{color:"#2a363b",bgcolor:"#3f5159",groupcolor:"#3f789e"},cyan:{color:"#233",bgcolor:"#355",groupcolor:"#8AA"},purple:{color:"#323",bgcolor:"#535",groupcolor:"#a1309b"},yellow:{color:"#432",bgcolor:"#653",groupcolor:"#b58b2a"},black:{color:"#222",bgcolor:"#000",groupcolor:"#444"}};e.prototype.getCanvasMenuOptions=function(){var a=null;this.getMenuOptions?a=this.getMenuOptions():(a=[{content:"Add Node",has_submenu:!0,callback:e.onMenuAdd},{content:"Add Group",callback:e.onGroupAdd}],this._graph_stack&& -0Name",f),l=g.querySelector("input");l&&c&&(l.value=c.label||"");g.querySelector("button").addEventListener("click",function(a){l.value&&(c&&(c.label=l.value),d.setDirty(!0));g.close()})}},extra:a};a&&(c.title=a.type);var l=null;a&&(l=a.getSlotInPosition(b.canvasX,b.canvasY),e.active_node= -a);if(l){f=[];l&&l.output&&l.output.links&&l.output.links.length&&f.push({content:"Disconnect Links",slot:l});var p=l.input||l.output;f.push(p.locked?"Cannot remove":{content:"Remove Slot",slot:l});f.push(p.nameLocked?"Cannot rename":{content:"Rename Slot",slot:l});c.title=(l.input?l.input.type:l.output.type)||"*";l.input&&l.input.type==g.ACTION&&(c.title="Action");l.output&&l.output.type==g.EVENT&&(c.title="Event")}else a?f=this.getNodeMenuOptions(a):(f=this.getCanvasMenuOptions(),(l=this.graph.getGroupOnPos(b.canvasX, -b.canvasY))&&f.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:l,options:this.getGroupMenuOptions(l)}}));f&&new g.ContextMenu(f,c,h)};this.CanvasRenderingContext2D&&(CanvasRenderingContext2D.prototype.roundRect=function(a,b,d,h,f,c){void 0===f&&(f=5);void 0===c&&(c=f);this.moveTo(a+f,b);this.lineTo(a+d-f,b);this.quadraticCurveTo(a+d,b,a+d,b+f);this.lineTo(a+d,b+h-c);this.quadraticCurveTo(a+d,b+h,a+d-c,b+h);this.lineTo(a+c,b+h);this.quadraticCurveTo(a,b+h,a,b+h-c);this.lineTo(a, -b+f);this.quadraticCurveTo(a,b,a+f,b)});g.compareObjects=function(a,b){for(var d in a)if(a[d]!=b[d])return!1;return!0};g.distance=C;g.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+","+Math.round(255*a[2]).toFixed()+","+(4==a.length?a[3].toFixed(2):"1.0")+")"};g.isInsideRectangle=z;g.growBounding=function(a,b,d){ba[2]&&(a[2]=b);da[3]&&(a[3]=d)};g.isInsideBounding=function(a,b){return a[0]b[1][0]||a[1]>b[1][1]?!1:!0};g.overlapBounding=u;g.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),d=0,h,f,c=0;6>c;c+=2)h="0123456789ABCDEF".indexOf(a.charAt(c)),f="0123456789ABCDEF".indexOf(a.charAt(c+1)),b[d]=16*h+f,d++;return b};g.num2hex=function(a){for(var b="#",d,h,f=0;3>f;f++)d=a[f]/16,h=a[f]%16,b+="0123456789ABCDEF".charAt(d)+"0123456789ABCDEF".charAt(h);return b};D.prototype.addItem=function(a,b,d){function h(a){var b=this.value;b&&b.has_submenu&& -f.call(this,a)}function f(a){var b=this.value,f=!0;c.current_submenu&&c.current_submenu.close(a);if(d.callback){var h=d.callback.call(this,b,d,a,c,d.node);!0===h&&(f=!1)}if(b&&(b.callback&&!d.ignore_item_callbacks&&!0!==b.disabled&&(h=b.callback.call(this,b,d,a,c,d.extra),!0===h&&(f=!1)),b.submenu)){if(!b.submenu.options)throw"ContextMenu submenu needs options";new c.constructor(b.submenu.options,{callback:b.submenu.callback,event:a,parentMenu:c,ignore_item_callbacks:b.submenu.ignore_item_callbacks, -title:b.submenu.title,extra:b.submenu.extra,autoopen:d.autoopen});f=!1}f&&!c.lock&&c.close()}var c=this;d=d||{};var g=document.createElement("div");g.className="litemenu-entry submenu";var l=!1;if(null===b)g.classList.add("separator");else{g.innerHTML=b&&b.title?b.title:a;if(g.value=b)b.disabled&&(l=!0,g.classList.add("disabled")),(b.submenu||b.has_submenu)&&g.classList.add("has_submenu");"function"==typeof b?(g.dataset.value=a,g.onclick_callback=b):g.dataset.value=b;b.className&&(g.className+=" "+ -b.className)}this.root.appendChild(g);l||g.addEventListener("click",f);d.autoopen&&g.addEventListener("mouseenter",h);return g};D.prototype.close=function(a,b){this.root.parentNode&&this.root.parentNode.removeChild(this.root);this.parentMenu&&!b&&(this.parentMenu.lock=!1,this.parentMenu.current_submenu=null,void 0===a?this.parentMenu.close():a&&!D.isCursorOverElement(a,this.parentMenu.root)&&D.trigger(this.parentMenu.root,"mouseleave",a));this.current_submenu&&this.current_submenu.close(a,!0);this.root.closing_timer&& -clearTimeout(this.root.closing_timer)};D.trigger=function(a,b,d,h){var f=document.createEvent("CustomEvent");f.initCustomEvent(b,!0,!0,d);f.srcElement=h;a.dispatchEvent?a.dispatchEvent(f):a.__events&&a.__events.dispatchEvent(f);return f};D.prototype.getTopMenu=function(){return this.options.parentMenu?this.options.parentMenu.getTopMenu():this};D.prototype.getFirstEvent=function(){return this.options.parentMenu?this.options.parentMenu.getFirstEvent():this.options.event};D.isCursorOverElement=function(a, -b){var d=a.clientX,h=a.clientY,f=b.getBoundingClientRect();return f?h>f.top&&hf.left&&dMath.abs(d))return h[1];d=(a-h[0])/d;return h[1]*(1-d)+f[1]*d}}return 0}};B.prototype.draw=function(a,b,d,h,f,c){if(d=this.points){this.size= -b;var g=b[0]-2*this.margin;b=b[1]-2*this.margin;f=f||"#666";a.save();a.translate(this.margin,this.margin);h&&(a.fillStyle="#111",a.fillRect(0,0,g,b),a.fillStyle="#222",a.fillRect(0.5*g,0,1,b),a.strokeStyle="#333",a.strokeRect(0,0,g,b));a.strokeStyle=f;c&&(a.globalAlpha=0.5);a.beginPath();for(h=0;ha[1])){var h=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=a[0]-this.margin,g=a[1]-this.margin;this.selected=this.getCloserPoint([c,g],30/b.ds.scale);-1==this.selected&&(h=[c/h,1-g/f],d.push(h),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(h),this.must_update=!0);if(-1!=this.selected)return!0}};B.prototype.onMouseMove=function(a,b){var d=this.points;if(d){var h= -this.selected;if(!(0>h)){var f=(a[0]-this.margin)/(this.size[0]-2*this.margin),c=(a[1]-this.margin)/(this.size[1]-2*this.margin);this._nearest=this.getCloserPoint([a[0]-this.margin,a[1]-this.margin],30/b.ds.scale);var g=d[h];if(g){var l=0==h||h==d.length-1;!l&&(-10>a[0]||a[0]>this.size[0]+10||-10>a[1]||a[1]>this.size[1]+10)?(d.splice(h,1),this.selected=-1):(g[0]=l?0==h?0:1:Math.clamp(f,0,1),g[1]=1-Math.clamp(c,0,1),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(g),this.must_update= -!0)}}}};B.prototype.onMouseUp=function(a,b){this.selected=-1;return!1};B.prototype.getCloserPoint=function(a,b){var d=this.points;if(!d)return-1;b=b||30;for(var h=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=d.length,g=[0,0],l=1E6,p=-1,n=0;nl||e>b||(p=n,l=e)}return p};g.CurveEditor=B;g.getParameterNames=function(a){return(a+"").replace(/[/][/].*$/gm,"").replace(/\s+/g,"").replace(/[/][*][^/*]*[*][/]/g,"").split("){", -1)[0].replace(/^[^(]*[(]/,"").replace(/=[^,]+/g,"").split(",").filter(Boolean)};Math.clamp=function(a,b,d){return b>a?b:dthis.ds.scale,n=a._shape||a.constructor.shape||g.ROUND_SHAPE,r=a.constructor.title_mode,k=!0;r==g.TRANSPARENT_TITLE?k=!1:r==g.AUTOHIDE_TITLE&& +q&&(k=!0);A[0]=0;A[1]=k?-f:0;A[2]=d[0]+1;A[3]=k?d[1]+f:d[1];q=b.globalAlpha;b.beginPath();n==g.BOX_SHAPE||m?b.fillRect(A[0],A[1],A[2],A[3]):n==g.ROUND_SHAPE||n==g.CARD_SHAPE?b.roundRect(A[0],A[1],A[2],A[3],this.round_radius,n==g.CARD_SHAPE?0:this.round_radius):n==g.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0],0,2*Math.PI);b.fill();a.flags.collapsed||(b.shadowColor="transparent",b.fillStyle="rgba(0,0,0,0.2)",b.fillRect(0,-1,A[2],2));b.shadowColor="transparent";if(a.onDrawBackground)a.onDrawBackground(b, +this,this.canvas);if(k||r==g.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,f,d,this.ds.scale,h);else if(r!=g.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){k=a.constructor.title_color||h;a.flags.collapsed&&(b.shadowColor=g.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var t=e.gradients[k];t||(t=e.gradients[k]=b.createLinearGradient(0,0,400,0),t.addColorStop(0,k),t.addColorStop(1,"#000"));b.fillStyle=t}else b.fillStyle=k;b.beginPath();n==g.BOX_SHAPE||m?b.rect(0, +-f,d[0]+1,f):n!=g.ROUND_SHAPE&&n!=g.CARD_SHAPE||b.roundRect(0,-f,d[0]+1,f,this.round_radius,a.flags.collapsed?this.round_radius:0);b.fill();b.shadowColor="transparent"}if(a.onDrawTitleBox)a.onDrawTitleBox(b,f,d,this.ds.scale);else n==g.ROUND_SHAPE||n==g.CIRCLE_SHAPE||n==g.CARD_SHAPE?(m&&(b.fillStyle="black",b.beginPath(),b.arc(0.5*f,-0.5*f,6,0,2*Math.PI),b.fill()),b.fillStyle=a.boxcolor||g.NODE_DEFAULT_BOXCOLOR,m?b.fillRect(0.5*f-5,-0.5*f-5,10,10):(b.beginPath(),b.arc(0.5*f,-0.5*f,5,0,2*Math.PI), +b.fill())):(m&&(b.fillStyle="black",b.fillRect(0.5*(f-10)-1,-0.5*(f+10)-1,12,12)),b.fillStyle=a.boxcolor||g.NODE_DEFAULT_BOXCOLOR,b.fillRect(0.5*(f-10),-0.5*(f+10),10,10));b.globalAlpha=q;if(a.onDrawTitleText)a.onDrawTitleText(b,f,d,this.ds.scale,this.title_text_font,c);!m&&(b.font=this.title_text_font,m=String(a.getTitle()))&&(b.fillStyle=c?"white":a.constructor.title_text_color||this.node_title_color,a.flags.collapsed?(b.textAlign="left",b.measureText(m),b.fillText(m.substr(0,20),f,g.NODE_TITLE_TEXT_Y- +f),b.textAlign="left"):(b.textAlign="left",b.fillText(m,f,g.NODE_TITLE_TEXT_Y-f)));if(a.onDrawTitle)a.onDrawTitle(b)}if(c){if(a.onBounding)a.onBounding(A);r==g.TRANSPARENT_TITLE&&(A[1]-=f,A[3]+=f);b.lineWidth=1;b.globalAlpha=0.8;b.beginPath();n==g.BOX_SHAPE?b.rect(-6+A[0],-6+A[1],12+A[2],12+A[3]):n==g.ROUND_SHAPE||n==g.CARD_SHAPE&&a.flags.collapsed?b.roundRect(-6+A[0],-6+A[1],12+A[2],12+A[3],2*this.round_radius):n==g.CARD_SHAPE?b.roundRect(-6+A[0],-6+A[1],12+A[2],12+A[3],2*this.round_radius,2):n== +g.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0]+6,0,2*Math.PI);b.strokeStyle="#FFF";b.stroke();b.strokeStyle=h;b.globalAlpha=1}};var r=new Float32Array(4),n=new Float32Array(4),x=new Float32Array(2),v=new Float32Array(2);e.prototype.drawConnections=function(a){var b=g.getTime(),d=this.visible_area;r[0]=d[0]-20;r[1]=d[1]-20;r[2]=d[2]+40;r[3]=d[3]+40;a.lineWidth=this.connections_width;a.fillStyle="#AAA";a.strokeStyle="#AAA";a.globalAlpha=this.editor_alpha;for(var d=this.graph._nodes,h=0,f=d.length;h< +f;++h){var c=d[h];if(c.inputs&&c.inputs.length)for(var q=0;qn[2]&&(n[0]+=n[2],n[2]=Math.abs(n[2]));0>n[3]&&(n[1]+=n[3],n[3]=Math.abs(n[3]));if(u(n,r)){var p=m.outputs[k],k=c.inputs[q];if(p&& +k&&(m=p.dir||(m.horizontal?g.DOWN:g.RIGHT),k=k.dir||(c.horizontal?g.UP:g.LEFT),this.renderLink(a,s,t,e,!1,0,null,m,k),e&&e._last_time&&1E3>b-e._last_time)){var p=2-0.002*(b-e._last_time),K=a.globalAlpha;a.globalAlpha=K*p;this.renderLink(a,s,t,e,!0,p,"white",m,k);a.globalAlpha=K}}}}}}a.globalAlpha=1};e.prototype.renderLink=function(a,b,d,h,f,c,q,m,n,r){h&&this.visible_links.push(h);!q&&h&&(q=h.color||e.link_type_colors[h.type]);q||(q=this.default_link_color);null!=h&&this.highlighted_links[h.id]&& +(q="#FFF");m=m||g.RIGHT;n=n||g.LEFT;var k=C(b,d);this.render_connections_border&&0.6b[1]?0:Math.PI,a.save(),a.translate(t[0],t[1]),a.rotate(s),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore(),a.save(),a.translate(r[0],r[1]),a.rotate(v), +a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill());if(c)for(a.fillStyle=q,t=0;5>t;++t)c=(0.001*g.getTime()+0.2*t)%1,f=this.computeConnectionPoint(b,d,c,m,n),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill()};e.prototype.computeConnectionPoint=function(a,b,d,h,f){h=h||g.RIGHT;f=f||g.LEFT;var c=C(a,b),q=[a[0],a[1]],e=[b[0],b[1]];switch(h){case g.LEFT:q[0]+=-0.25*c;break;case g.RIGHT:q[0]+=0.25*c;break;case g.UP:q[1]+= +-0.25*c;break;case g.DOWN:q[1]+=0.25*c}switch(f){case g.LEFT:e[0]+=-0.25*c;break;case g.RIGHT:e[0]+=0.25*c;break;case g.UP:e[1]+=-0.25*c;break;case g.DOWN:e[1]+=0.25*c}h=(1-d)*(1-d)*(1-d);f=3*(1-d)*(1-d)*d;c=3*(1-d)*d*d;d*=d*d;return[h*a[0]+f*q[0]+c*e[0]+d*b[0],h*a[1]+f*q[1]+c*e[1]+d*b[1]]};e.prototype.drawExecutionOrder=function(a){a.shadowColor="transparent";a.globalAlpha=0.25;a.textAlign="center";a.strokeStyle="white";a.globalAlpha=0.75;for(var b=this.visible_nodes,d=0;dt.last_y&&q +t.options.max&&(t.value=t.options.max);else if("mousedown"==d.type){var s=t.options.values;s&&s.constructor===Function&&(s=t.options.values(t,a));var k=s.constructor===Array?s:Object.keys(s),c=40>c?-1:c>e-40?1:0;if("number"==t.type)t.value+=0.1*c*(t.options.step||1),null!=t.options.min&&t.valuet.options.max&&(t.value=t.options.max);else if(c)n=-1,n=s.constructor===Object?k.indexOf(String(t.value))+c:k.indexOf(t.value)+c,n>=k.length&& +(n=k.length-1),0>n&&(n=0),t.value=s.constructor===Array?s[n]:n;else{var v=s!=k?Object.values(s):s;new g.ContextMenu(v,{scale:Math.max(1,this.ds.scale),event:d,className:"dark",callback:p.bind(t)},n);var p=function(a,b,d){s!=k&&(a=v.indexOf(a));this.value=a;f(this,a);m.dirty_canvas=!0;return!1}}}else"mouseup"==d.type&&"number"==t.type&&(c=40>c?-1:c>e-40?1:0,200>d.click_time&&0==c&&this.prompt("Value",t.value,function(a){this.value=Number(a);f(this,this.value)}.bind(t),d));h!=t.value&&setTimeout(function(){f(this, +this.value)}.bind(t),20);this.dirty_canvas=!0;break;case "toggle":"mousedown"==d.type&&(t.value=!t.value,setTimeout(function(){f(t,t.value)},20));break;case "string":case "text":"mousedown"==d.type&&this.prompt("Value",t.value,function(a){this.value=a;f(this,a)}.bind(t),d);break;default:t.mouse&&t.mouse(ctx,d,[c,q],a)}return t}}return null};e.prototype.drawGroups=function(a,b){if(this.graph){var d=this.graph._groups;b.save();b.globalAlpha=0.5*this.editor_alpha;for(var h=0;hd&&0.01>b.editor_alpha&&(clearInterval(h),1>d&&(b.live_mode=!0));1"+n+""+ +a+"",value:n});if(m.length)return new g.ContextMenu(m,{event:d,callback:c,parentMenu:h,allow_html:!0,node:f},b),!1}};e.decodeHTML=function(a){var b=document.createElement("div");b.innerText=a;return b.innerHTML};e.onResizeNode=function(a,b,d,h,f){f&&(f.size=f.computeSize(),f.setDirtyCanvas(!0,!0))};e.prototype.showLinkMenu=function(a,b){var d=this;console.log(a);var h=new g.ContextMenu(["Add Node",null,"Delete"],{event:b,title:null!=a.data?a.data.constructor.name:null,callback:function(b,g, +c){switch(b){case "Add Node":e.onMenuAdd(null,null,c,h,function(b){console.log("node autoconnect");var h=d.graph.getNodeById(a.origin_id),f=d.graph.getNodeById(a.target_id);b.inputs&&b.inputs.length&&b.outputs&&b.outputs.length&&h.outputs[a.origin_slot].type==b.inputs[0].type&&b.outputs[0].type==f.inputs[0].type&&(h.connect(a.origin_slot,b,0),b.connect(0,f,a.target_slot),b.pos[0]-=0.5*b.size[0])});break;case "Delete":d.graph.removeLink(a.id)}}});return!1};e.onShowPropertyEditor=function(a,b,d,h,f){function g(){var b= +n.value;"Number"==a.type?b=Number(b):"Boolean"==a.type&&(b=Boolean(b));f[c]=b;m.parentNode&&m.parentNode.removeChild(m);f.setDirtyCanvas(!0,!0)}var c=a.property||"title";b=f[c];var m=document.createElement("div");m.className="graphdialog";m.innerHTML="";m.querySelector(".name").innerText=c;var n=m.querySelector("input");n&&(n.value=b,n.addEventListener("blur",function(a){this.focus()}),n.addEventListener("keydown", +function(a){13==a.keyCode&&(g(),a.preventDefault(),a.stopPropagation())}));b=e.active_canvas.canvas;d=b.getBoundingClientRect();var r=h=-20;d&&(h-=d.left,r-=d.top);event?(m.style.left=event.clientX+h+"px",m.style.top=event.clientY+r+"px"):(m.style.left=0.5*b.width+h+"px",m.style.top=0.5*b.height+r+"px");m.querySelector("button").addEventListener("click",g);b.parentNode.appendChild(m)};e.prototype.prompt=function(a,b,d,h){var f=this;a=a||"";var g=!1,c=document.createElement("div");c.className="graphdialog rounded"; +c.innerHTML=" ";c.close=function(){f.prompt_box=null;c.parentNode&&c.parentNode.removeChild(c)};1e.search_limit)break}}m=null;if(Array.prototype.filter)m=Object.keys(g.registered_node_types).filter(r);else for(L in m=[],g.registered_node_types)r(L)&&m.push(L);for(L=0;Le.search_limit);L++);var r=function(a){var b=g.registered_node_types[a];return q&&b.filter!=q?!1:-1!==a.toLowerCase().indexOf(d)}}}var f=this,c=e.active_canvas,q=c.canvas,m=q.ownerDocument||document,n=document.createElement("div"); +n.className="litegraph litesearchbox graphdialog rounded";n.innerHTML="Search
";n.close=function(){f.search_box=null;m.body.focus();m.body.style.overflow="";setTimeout(function(){f.canvas.focus()},20);n.parentNode&&n.parentNode.removeChild(n)};var r=null;1q.height-200&&(s.style.maxHeight=q.height-a.layerY-20+"px");p.focus();return n};e.prototype.showEditPropertyValue=function(a,b,d){function h(){f(t.value)}function f(h){"number"==typeof a.properties[b]&& +(h=Number(h));if("array"==g||"object"==g)h=JSON.parse(h);a.properties[b]=h;a._graph&&a._graph._version++;if(a.onPropertyChanged)a.onPropertyChanged(b,h);if(d.onclose)d.onclose();r.close();a.setDirtyCanvas(!0,!0)}if(a&&void 0!==a.properties[b]){d=d||{};var c=a.getPropertyInfo(b),g=c.type,m="";if("string"==g||"number"==g||"array"==g||"object"==g)m="";else if("enum"==g&&c.values){var m=""}else if("boolean"==g)m="";else{console.warn("unknown type: "+g);return}var r=this.createDialog(""+b+""+m+"",d);if("enum"==g&&c.values){var t=r.querySelector("select");t.addEventListener("change",function(a){f(a.target.value)})}else if("boolean"== +g)(t=r.querySelector("input"))&&t.addEventListener("click",function(a){f(!!t.checked)});else if(t=r.querySelector("input"))t.addEventListener("blur",function(a){this.focus()}),n=void 0!==a.properties[b]?a.properties[b]:"",n=JSON.stringify(n),t.value=n,t.addEventListener("keydown",function(a){13==a.keyCode&&(h(),a.preventDefault(),a.stopPropagation())});r.querySelector("button").addEventListener("click",h);return r}};e.prototype.createDialog=function(a,b){b=b||{};var d=document.createElement("div"); +d.className="graphdialog";d.innerHTML=a;var h=this.canvas.getBoundingClientRect(),f=-20,c=-20;h&&(f-=h.left,c-=h.top);b.position?(f+=b.position[0],c+=b.position[1]):b.event?(f+=b.event.clientX,c+=b.event.clientY):(f+=0.5*this.canvas.width,c+=0.5*this.canvas.height);d.style.left=f+"px";d.style.top=c+"px";this.canvas.parentNode.appendChild(d);d.close=function(){this.parentNode&&this.parentNode.removeChild(this)};return d};e.onMenuNodeCollapse=function(a,b,d,h,f){f.collapse()};e.onMenuNodePin=function(a, +b,d,h,f){f.pin()};e.onMenuNodeMode=function(a,b,d,h,f){new g.ContextMenu(["Always","On Event","On Trigger","Never"],{event:d,callback:function(a){if(f)switch(a){case "On Event":f.mode=g.ON_EVENT;break;case "On Trigger":f.mode=g.ON_TRIGGER;break;case "Never":f.mode=g.NEVER;break;default:f.mode=g.ALWAYS}},parentMenu:h,node:f});return!1};e.onMenuNodeColors=function(a,b,d,h,f){if(!f)throw"no node for color";b=[];b.push({value:null,content:"No color"}); +for(var c in e.node_colors)a=e.node_colors[c],a={value:c,content:""+c+""},b.push(a);new g.ContextMenu(b,{event:d,callback:function(a){f&&((a=a.value?e.node_colors[a.value]:null)?f.constructor===g.LGraphGroup?f.color=a.groupcolor:(f.color=a.color,f.bgcolor=a.bgcolor):(delete f.color,delete f.bgcolor),f.setDirtyCanvas(!0,!0))},parentMenu:h,node:f});return!1};e.onMenuNodeShapes= +function(a,b,d,h,f){if(!f)throw"no node passed";new g.ContextMenu(g.VALID_SHAPES,{event:d,callback:function(a){f&&(f.shape=a,f.setDirtyCanvas(!0))},parentMenu:h,node:f});return!1};e.onMenuNodeRemove=function(a,b,d,h,f){if(!f)throw"no node passed";!1!==f.removable&&(f.graph.remove(f),f.setDirtyCanvas(!0,!0))};e.onMenuNodeClone=function(a,b,d,h,f){!1!=f.clonable&&(a=f.clone())&&(a.pos=[f.pos[0]+5,f.pos[1]+5],f.graph.add(a),f.setDirtyCanvas(!0,!0))};e.node_colors={red:{color:"#322",bgcolor:"#533",groupcolor:"#A88"}, +brown:{color:"#332922",bgcolor:"#593930",groupcolor:"#b06634"},green:{color:"#232",bgcolor:"#353",groupcolor:"#8A8"},blue:{color:"#223",bgcolor:"#335",groupcolor:"#88A"},pale_blue:{color:"#2a363b",bgcolor:"#3f5159",groupcolor:"#3f789e"},cyan:{color:"#233",bgcolor:"#355",groupcolor:"#8AA"},purple:{color:"#323",bgcolor:"#535",groupcolor:"#a1309b"},yellow:{color:"#432",bgcolor:"#653",groupcolor:"#b58b2a"},black:{color:"#222",bgcolor:"#000",groupcolor:"#444"}};e.prototype.getCanvasMenuOptions=function(){var a= +null;this.getMenuOptions?a=this.getMenuOptions():(a=[{content:"Add Node",has_submenu:!0,callback:e.onMenuAdd},{content:"Add Group",callback:e.onGroupAdd}],this._graph_stack&&0Name",h),m=g.querySelector("input");m&&c&&(m.value=c.label||"");g.querySelector("button").addEventListener("click",function(a){m.value&& +(c&&(c.label=m.value),d.setDirty(!0));g.close()})}},extra:a};a&&(c.title=a.type);var m=null;a&&(m=a.getSlotInPosition(b.canvasX,b.canvasY),e.active_node=a);if(m){f=[];m&&m.output&&m.output.links&&m.output.links.length&&f.push({content:"Disconnect Links",slot:m});var n=m.input||m.output;f.push(n.locked?"Cannot remove":{content:"Remove Slot",slot:m});f.push(n.nameLocked?"Cannot rename":{content:"Rename Slot",slot:m});c.title=(m.input?m.input.type:m.output.type)||"*";m.input&&m.input.type==g.ACTION&& +(c.title="Action");m.output&&m.output.type==g.EVENT&&(c.title="Event")}else a?f=this.getNodeMenuOptions(a):(f=this.getCanvasMenuOptions(),(m=this.graph.getGroupOnPos(b.canvasX,b.canvasY))&&f.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:m,options:this.getGroupMenuOptions(m)}}));f&&new g.ContextMenu(f,c,h)};this.CanvasRenderingContext2D&&(CanvasRenderingContext2D.prototype.roundRect=function(a,b,d,h,f,c){void 0===f&&(f=5);void 0===c&&(c=f);this.moveTo(a+f,b);this.lineTo(a+ +d-f,b);this.quadraticCurveTo(a+d,b,a+d,b+f);this.lineTo(a+d,b+h-c);this.quadraticCurveTo(a+d,b+h,a+d-c,b+h);this.lineTo(a+c,b+h);this.quadraticCurveTo(a,b+h,a,b+h-c);this.lineTo(a,b+f);this.quadraticCurveTo(a,b,a+f,b)});g.compareObjects=function(a,b){for(var d in a)if(a[d]!=b[d])return!1;return!0};g.distance=C;g.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+","+Math.round(255*a[2]).toFixed()+","+(4==a.length?a[3].toFixed(2):"1.0")+")"};g.isInsideRectangle= +z;g.growBounding=function(a,b,d){ba[2]&&(a[2]=b);da[3]&&(a[3]=d)};g.isInsideBounding=function(a,b){return a[0]b[1][0]||a[1]>b[1][1]?!1:!0};g.overlapBounding=u;g.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),d=0,h,f,c=0;6>c;c+=2)h="0123456789ABCDEF".indexOf(a.charAt(c)),f="0123456789ABCDEF".indexOf(a.charAt(c+1)),b[d]=16*h+f,d++;return b};g.num2hex=function(a){for(var b="#",d,h,f=0;3>f;f++)d=a[f]/ +16,h=a[f]%16,b+="0123456789ABCDEF".charAt(d)+"0123456789ABCDEF".charAt(h);return b};D.prototype.addItem=function(a,b,d){function h(a){var b=this.value;b&&b.has_submenu&&f.call(this,a)}function f(a){var b=this.value,h=!0;c.current_submenu&&c.current_submenu.close(a);if(d.callback){var f=d.callback.call(this,b,d,a,c,d.node);!0===f&&(h=!1)}if(b&&(b.callback&&!d.ignore_item_callbacks&&!0!==b.disabled&&(f=b.callback.call(this,b,d,a,c,d.extra),!0===f&&(h=!1)),b.submenu)){if(!b.submenu.options)throw"ContextMenu submenu needs options"; +new c.constructor(b.submenu.options,{callback:b.submenu.callback,event:a,parentMenu:c,ignore_item_callbacks:b.submenu.ignore_item_callbacks,title:b.submenu.title,extra:b.submenu.extra,autoopen:d.autoopen});h=!1}h&&!c.lock&&c.close()}var c=this;d=d||{};var g=document.createElement("div");g.className="litemenu-entry submenu";var m=!1;if(null===b)g.classList.add("separator");else{g.innerHTML=b&&b.title?b.title:a;if(g.value=b)b.disabled&&(m=!0,g.classList.add("disabled")),(b.submenu||b.has_submenu)&& +g.classList.add("has_submenu");"function"==typeof b?(g.dataset.value=a,g.onclick_callback=b):g.dataset.value=b;b.className&&(g.className+=" "+b.className)}this.root.appendChild(g);m||g.addEventListener("click",f);d.autoopen&&g.addEventListener("mouseenter",h);return g};D.prototype.close=function(a,b){this.root.parentNode&&this.root.parentNode.removeChild(this.root);this.parentMenu&&!b&&(this.parentMenu.lock=!1,this.parentMenu.current_submenu=null,void 0===a?this.parentMenu.close():a&&!D.isCursorOverElement(a, +this.parentMenu.root)&&D.trigger(this.parentMenu.root,"mouseleave",a));this.current_submenu&&this.current_submenu.close(a,!0);this.root.closing_timer&&clearTimeout(this.root.closing_timer)};D.trigger=function(a,b,d,h){var f=document.createEvent("CustomEvent");f.initCustomEvent(b,!0,!0,d);f.srcElement=h;a.dispatchEvent?a.dispatchEvent(f):a.__events&&a.__events.dispatchEvent(f);return f};D.prototype.getTopMenu=function(){return this.options.parentMenu?this.options.parentMenu.getTopMenu():this};D.prototype.getFirstEvent= +function(){return this.options.parentMenu?this.options.parentMenu.getFirstEvent():this.options.event};D.isCursorOverElement=function(a,b){var d=a.clientX,h=a.clientY,f=b.getBoundingClientRect();return f?h>f.top&&hf.left&&dMath.abs(d))return h[1];d=(a-h[0])/d;return h[1]*(1-d)+f[1]*d}}return 0}};B.prototype.draw=function(a,b,d,h,f,c){if(d=this.points){this.size=b;var g=b[0]-2*this.margin;b=b[1]-2*this.margin;f=f||"#666";a.save();a.translate(this.margin,this.margin);h&&(a.fillStyle="#111",a.fillRect(0,0,g,b),a.fillStyle="#222",a.fillRect(0.5*g,0,1,b),a.strokeStyle="#333",a.strokeRect(0,0,g,b));a.strokeStyle=f;c&&(a.globalAlpha=0.5);a.beginPath();for(h=0;ha[1])){var h=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=a[0]-this.margin,g=a[1]-this.margin;this.selected=this.getCloserPoint([c,g],30/b.ds.scale);-1==this.selected&&(h=[c/h,1-g/f],d.push(h),d.sort(function(a,b){return a[0]- +b[0]}),this.selected=d.indexOf(h),this.must_update=!0);if(-1!=this.selected)return!0}};B.prototype.onMouseMove=function(a,b){var d=this.points;if(d){var h=this.selected;if(!(0>h)){var f=(a[0]-this.margin)/(this.size[0]-2*this.margin),c=(a[1]-this.margin)/(this.size[1]-2*this.margin);this._nearest=this.getCloserPoint([a[0]-this.margin,a[1]-this.margin],30/b.ds.scale);var g=d[h];if(g){var m=0==h||h==d.length-1;!m&&(-10>a[0]||a[0]>this.size[0]+10||-10>a[1]||a[1]>this.size[1]+10)?(d.splice(h,1),this.selected= +-1):(g[0]=m?0==h?0:1:Math.clamp(f,0,1),g[1]=1-Math.clamp(c,0,1),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(g),this.must_update=!0)}}}};B.prototype.onMouseUp=function(a,b){this.selected=-1;return!1};B.prototype.getCloserPoint=function(a,b){var d=this.points;if(!d)return-1;b=b||30;for(var h=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=d.length,g=[0,0],m=1E6,n=-1,e=0;em||r>b||(n=e,m=r)}return n};g.CurveEditor= +B;g.getParameterNames=function(a){return(a+"").replace(/[/][/].*$/gm,"").replace(/\s+/g,"").replace(/[/][*][^/*]*[*][/]/g,"").split("){",1)[0].replace(/^[^(]*[(]/,"").replace(/=[^,]+/g,"").split(",").filter(Boolean)};Math.clamp=function(a,b,d){return b>a?b:dthis.size[0]-l.NODE_TITLE_HEIGHT&&0>b[1]){var h=this;setTimeout(function(){d.openSubgraph(h.subgraph)},10)}};m.prototype.onAction=function(a,b){this.subgraph.onAction(a,b)};m.prototype.onExecute=function(){if(this.enabled=this.getInputOrProperty("enabled")){if(this.inputs)for(var a=0;athis.size[0]-d.NODE_TITLE_HEIGHT&&0>b[1]){var g=this;setTimeout(function(){c.openSubgraph(g.subgraph)},10)}};l.prototype.onAction=function(a,b){this.subgraph.onAction(a,b)};l.prototype.onExecute=function(){if(this.enabled=this.getInputOrProperty("enabled")){if(this.inputs)for(var a=0;a=g?this.trigger(null,e):this._pending.push([g,e])};e.prototype.onExecute=function(){var c=1E3* this.graph.elapsed_time;this.isInputConnected(1)&&(this.properties.time_in_ms=this.getInputData(1));for(var e=0;ep[1]))return this.old_y=c.canvasY,this.captureInput(!0),this.mouse_captured=!0};q.prototype.onMouseMove=function(c){if(this.mouse_captured){var p=this.old_y-c.canvasY;c.shiftKey&&(p*=10);if(c.metaKey||c.altKey)p*=0.1;this.old_y=c.canvasY;c=this._remainder+p/q.pixels_threshold;this._remainder=c%1;c=Math.clamp(this.properties.value+ -(c|0)*this.properties.step,this.properties.min,this.properties.max);this.properties.value=c;this.graph._version++;this.setDirtyCanvas(!0)}};q.prototype.onMouseUp=function(c,p){200>c.click_time&&(this.properties.value=Math.clamp(this.properties.value+(p[1]>0.5*this.size[1]?-1:1)*this.properties.step,this.properties.min,this.properties.max),this.graph._version++,this.setDirtyCanvas(!0));this.mouse_captured&&(this.mouse_captured=!1,this.captureInput(!1))};B.registerNodeType("widget/number",q);k.title= -"Combo";k.desc="Widget to select from a list";k.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};k.prototype.onPropertyChanged=function(c,p){"values"==c?(this._values=p.split(";"),this.widget.options.values=this._values):"value"==c&&(this.widget.value=p)};B.registerNodeType("widget/combo",k);w.title="Knob";w.desc="Circular controller";w.size=[80,100];w.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/ -(this.properties.max-this.properties.min));var p=0.5*this.size[0],e=0.5*this.size[1],k=0.5*Math.min(this.size[0],this.size[1])-5;c.globalAlpha=1;c.save();c.translate(p,e);c.rotate(0.75*Math.PI);c.fillStyle="rgba(0,0,0,0.5)";c.beginPath();c.moveTo(0,0);c.arc(0,0,k,0,1.5*Math.PI);c.fill();c.strokeStyle="black";c.fillStyle=this.properties.color;c.lineWidth=2;c.beginPath();c.moveTo(0,0);c.arc(0,0,k-4,0,1.5*Math.PI*Math.max(0.01,this.value));c.closePath();c.fill();c.lineWidth=1;c.globalAlpha=1;c.restore(); -c.fillStyle="black";c.beginPath();c.arc(p,e,0.75*k,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":this.properties.color;c.beginPath();var n=this.value*Math.PI*1.5+0.75*Math.PI;c.arc(p+Math.cos(n)*k*0.65,e+Math.sin(n)*k*0.65,0.05*k,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":"#AAA";c.font=Math.floor(0.5*k)+"px Arial";c.textAlign="center";c.fillText(this.properties.value.toFixed(this.properties.precision),p,e+0.15*k)}};w.prototype.onExecute=function(){this.setOutputData(0, +(g.fillStyle="black",g.fillRect(11,11,this.size[0]-20,this.size[1]-20),g.fillStyle="#AAF",g.fillRect(9,9,this.size[0]-20,this.size[1]-20),g.fillStyle=this.clicked?"white":this.mouseOver?"#668":"#334",g.fillRect(10,10,this.size[0]-20,this.size[1]-20),this.properties.text||0===this.properties.text)){var m=this.properties.font_size||30;g.textAlign="center";g.fillStyle=this.clicked?"black":"white";g.font=m+"px "+c.font;g.fillText(this.properties.text,0.5*this.size[0],0.5*this.size[1]+0.3*m);g.textAlign= +"left"}};c.prototype.onMouseDown=function(c,m){if(1m[1]))return this.old_y=c.canvasY,this.captureInput(!0),this.mouse_captured=!0};p.prototype.onMouseMove=function(c){if(this.mouse_captured){var m=this.old_y-c.canvasY;c.shiftKey&&(m*=10);if(c.metaKey||c.altKey)m*=0.1;this.old_y=c.canvasY;c=this._remainder+m/p.pixels_threshold;this._remainder=c%1;c=Math.clamp(this.properties.value+ +(c|0)*this.properties.step,this.properties.min,this.properties.max);this.properties.value=c;this.graph._version++;this.setDirtyCanvas(!0)}};p.prototype.onMouseUp=function(c,m){200>c.click_time&&(this.properties.value=Math.clamp(this.properties.value+(m[1]>0.5*this.size[1]?-1:1)*this.properties.step,this.properties.min,this.properties.max),this.graph._version++,this.setDirtyCanvas(!0));this.mouse_captured&&(this.mouse_captured=!1,this.captureInput(!1))};B.registerNodeType("widget/number",p);k.title= +"Combo";k.desc="Widget to select from a list";k.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};k.prototype.onPropertyChanged=function(c,m){"values"==c?(this._values=m.split(";"),this.widget.options.values=this._values):"value"==c&&(this.widget.value=m)};B.registerNodeType("widget/combo",k);w.title="Knob";w.desc="Circular controller";w.size=[80,100];w.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/ +(this.properties.max-this.properties.min));var m=0.5*this.size[0],e=0.5*this.size[1],k=0.5*Math.min(this.size[0],this.size[1])-5;c.globalAlpha=1;c.save();c.translate(m,e);c.rotate(0.75*Math.PI);c.fillStyle="rgba(0,0,0,0.5)";c.beginPath();c.moveTo(0,0);c.arc(0,0,k,0,1.5*Math.PI);c.fill();c.strokeStyle="black";c.fillStyle=this.properties.color;c.lineWidth=2;c.beginPath();c.moveTo(0,0);c.arc(0,0,k-4,0,1.5*Math.PI*Math.max(0.01,this.value));c.closePath();c.fill();c.lineWidth=1;c.globalAlpha=1;c.restore(); +c.fillStyle="black";c.beginPath();c.arc(m,e,0.75*k,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":this.properties.color;c.beginPath();var r=this.value*Math.PI*1.5+0.75*Math.PI;c.arc(m+Math.cos(r)*k*0.65,e+Math.sin(r)*k*0.65,0.05*k,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":"#AAA";c.font=Math.floor(0.5*k)+"px Arial";c.textAlign="center";c.fillText(this.properties.value.toFixed(this.properties.precision),m,e+0.15*k)}};w.prototype.onExecute=function(){this.setOutputData(0, this.properties.value);this.boxcolor=B.colorToString([this.value,this.value,this.value])};w.prototype.onMouseDown=function(c){this.center=[0.5*this.size[0],0.5*this.size[1]+20];this.radius=0.5*this.size[0];if(20>c.canvasY-this.pos[1]||B.distance([c.canvasX,c.canvasY],[this.pos[0]+this.center[0],this.pos[1]+this.center[1]])>this.radius)return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];this.captureInput(!0);return!0};w.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX- this.pos[0],c.canvasY-this.pos[1]];var e=this.value,e=e-0.01*(c[1]-this.oldmouse[1]);1e&&(e=0);this.value=e;this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.oldmouse=c;this.setDirtyCanvas(!0)}};w.prototype.onMouseUp=function(c){this.oldmouse&&(this.oldmouse=null,this.captureInput(!1))};w.prototype.onPropertyChanged=function(c,e){if("min"==c||"max"==c||"value"==c)return this.properties[c]=parseFloat(e),!0};B.registerNodeType("widget/knob", w);e.title="Inner Slider";e.prototype.onPropertyChanged=function(c,e){"value"==c&&(this.slider.value=e)};e.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};B.registerNodeType("widget/internal_slider",e);C.title="H.Slider";C.desc="Linear slider controller";C.prototype.onDrawForeground=function(c){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min));c.globalAlpha=1;c.lineWidth=1;c.fillStyle="#000";c.fillRect(2, 2,this.size[0]-4,this.size[1]-4);c.fillStyle=this.properties.color;c.beginPath();c.rect(4,4,(this.size[0]-8)*this.value,this.size[1]-8);c.fill()};C.prototype.onExecute=function(){this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.setOutputData(0,this.properties.value);this.boxcolor=B.colorToString([this.value,this.value,this.value])};C.prototype.onMouseDown=function(c){if(0>c.canvasY-this.pos[1])return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY- this.pos[1]];this.captureInput(!0);return!0};C.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];var e=this.value,e=e+(c[0]-this.oldmouse[0])/this.size[0];1e&&(e=0);this.value=e;this.oldmouse=c;this.setDirtyCanvas(!0)}};C.prototype.onMouseUp=function(c){this.oldmouse=null;this.captureInput(!1)};C.prototype.onMouseLeave=function(c){};B.registerNodeType("widget/hslider",C);z.title="Progress";z.desc="Shows data in linear progress";z.prototype.onExecute= function(){var c=this.getInputData(0);void 0!=c&&(this.properties.value=c)};z.prototype.onDrawForeground=function(c){c.lineWidth=1;c.fillStyle=this.properties.color;var e=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min),e=Math.min(1,e),e=Math.max(0,e);c.fillRect(2,2,(this.size[0]-4)*e,this.size[1]-4)};B.registerNodeType("widget/progress",z);u.title="Text";u.desc="Shows the input value";u.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"led_text", -text:"LED",type:"minibutton"},{name:"normal_text",text:"Normal",type:"minibutton"}];u.prototype.onDrawForeground=function(c){c.fillStyle=this.properties.color;var e=this.properties.value;this.properties.glowSize?(c.shadowColor=this.properties.color,c.shadowOffsetX=0,c.shadowOffsetY=0,c.shadowBlur=this.properties.glowSize):c.shadowColor="transparent";var s=this.properties.fontsize;c.textAlign=this.properties.align;c.font=s.toString()+"px "+this.properties.font;this.str="number"==typeof e?e.toFixed(this.properties.decimals): -e;if("string"==typeof this.str){var e=this.str.split("\\n"),k;for(k in e)c.fillText(e[k],"left"==this.properties.align?15:this.size[0]-15,-0.15*s+s*(parseInt(k)+1))}c.shadowColor="transparent";this.last_ctx=c;c.textAlign="left"};u.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(this.properties.value=c)};u.prototype.resize=function(){if(this.last_ctx){var c=this.str.split("\\n");this.last_ctx.font=this.properties.fontsize+"px "+this.properties.font;var e=0,s;for(s in c){var k=this.last_ctx.measureText(c[s]).width; -ek?q.xbox.axes.lx:0,this._left_axis[1]=Math.abs(q.xbox.axes.ly)>k?q.xbox.axes.ly:0,this._right_axis[0]=Math.abs(q.xbox.axes.rx)>k?q.xbox.axes.rx:0,this._right_axis[1]=Math.abs(q.xbox.axes.ry)>k?q.xbox.axes.ry:0,this._triggers[0]=Math.abs(q.xbox.axes.ltrigger)>k?q.xbox.axes.ltrigger: -0,this._triggers[1]=Math.abs(q.xbox.axes.rtrigger)>k?q.xbox.axes.rtrigger:0);if(this.outputs)for(k=0;kq;q++)if(k[q]){q=k[q];k=this.xbox_mapping;k||(k=this.xbox_mapping={axes:[], -buttons:{},hat:"",hatmap:c.CENTER});k.axes.lx=q.axes[0];k.axes.ly=q.axes[1];k.axes.rx=q.axes[2];k.axes.ry=q.axes[3];k.axes.ltrigger=q.buttons[6].value;k.axes.rtrigger=q.buttons[7].value;k.hat="";k.hatmap=c.CENTER;for(var m=0;mm)k.buttons[c.mapping_array[m]]=q.buttons[m].pressed,q.buttons[m].was_pressed&&this.trigger(c.mapping_array[m]+"_button_event");else switch(m){case 12:q.buttons[m].pressed&&(k.hat+="up",k.hatmap|=c.UP); -break;case 13:q.buttons[m].pressed&&(k.hat+="down",k.hatmap|=c.DOWN);break;case 14:q.buttons[m].pressed&&(k.hat+="left",k.hatmap|=c.LEFT);break;case 15:q.buttons[m].pressed&&(k.hat+="right",k.hatmap|=c.RIGHT);break;case 16:k.buttons.home=q.buttons[m].pressed}q.xbox=k;return q}};c.prototype.onDrawBackground=function(c){if(!this.flags.collapsed){var k=this._left_axis,m=this._right_axis;c.strokeStyle="#88A";c.strokeRect(0.5*(k[0]+1)*this.size[0]-4,0.5*(k[1]+1)*this.size[1]-4,8,8);c.strokeStyle="#8A8"; -c.strokeRect(0.5*(m[0]+1)*this.size[0]-4,0.5*(m[1]+1)*this.size[1]-4,8,8);k=this.size[1]/this._current_buttons.length;c.fillStyle="#AEB";for(m=0;mk?p.xbox.axes.lx:0,this._left_axis[1]=Math.abs(p.xbox.axes.ly)>k?p.xbox.axes.ly:0,this._right_axis[0]=Math.abs(p.xbox.axes.rx)>k?p.xbox.axes.rx:0,this._right_axis[1]=Math.abs(p.xbox.axes.ry)>k?p.xbox.axes.ry:0,this._triggers[0]=Math.abs(p.xbox.axes.ltrigger)>k?p.xbox.axes.ltrigger: +0,this._triggers[1]=Math.abs(p.xbox.axes.rtrigger)>k?p.xbox.axes.rtrigger:0);if(this.outputs)for(k=0;kp;p++)if(k[p]){p=k[p];k=this.xbox_mapping;k||(k=this.xbox_mapping={axes:[], +buttons:{},hat:"",hatmap:c.CENTER});k.axes.lx=p.axes[0];k.axes.ly=p.axes[1];k.axes.rx=p.axes[2];k.axes.ry=p.axes[3];k.axes.ltrigger=p.buttons[6].value;k.axes.rtrigger=p.buttons[7].value;k.hat="";k.hatmap=c.CENTER;for(var l=0;ll)k.buttons[c.mapping_array[l]]=p.buttons[l].pressed,p.buttons[l].was_pressed&&this.trigger(c.mapping_array[l]+"_button_event");else switch(l){case 12:p.buttons[l].pressed&&(k.hat+="up",k.hatmap|=c.UP); +break;case 13:p.buttons[l].pressed&&(k.hat+="down",k.hatmap|=c.DOWN);break;case 14:p.buttons[l].pressed&&(k.hat+="left",k.hatmap|=c.LEFT);break;case 15:p.buttons[l].pressed&&(k.hat+="right",k.hatmap|=c.RIGHT);break;case 16:k.buttons.home=p.buttons[l].pressed}p.xbox=k;return p}};c.prototype.onDrawBackground=function(c){if(!this.flags.collapsed){var k=this._left_axis,l=this._right_axis;c.strokeStyle="#88A";c.strokeRect(0.5*(k[0]+1)*this.size[0]-4,0.5*(k[1]+1)*this.size[1]-4,8,8);c.strokeStyle="#8A8"; +c.strokeRect(0.5*(l[0]+1)*this.size[0]-4,0.5*(l[1]+1)*this.size[1]-4,8,8);k=this.size[1]/this._current_buttons.length;c.fillStyle="#AEB";for(l=0;l","enum",{values:a.values});this.size=[80,60]}function b(){this.addInput("inc","number");this.addOutput("total","number");this.addProperty("increment",1);this.addProperty("value",0)}function d(){this.addInput("v","number");this.addOutput("sin","number");this.addProperty("amplitude",1);this.addProperty("offset",0);this.bgImageUrl= -"nodes/imgs/icon-sin.png"}function h(){this.addInput("x","number");this.addInput("y","number");this.addOutput("","number");this.properties={x:1,y:1,formula:"x+y"};this.code_widget=this.addWidget("text","F(x,y)",this.properties.formula,function(a,b,d){d.properties.formula=a});this.addWidget("toggle","allow",E.allow_scripts,function(a){E.allow_scripts=a});this._func=null}function f(){this.addInput("vec2","vec2");this.addOutput("x","number");this.addOutput("y","number")}function K(){this.addInputs([["x", -"number"],["y","number"]]);this.addOutput("vec2","vec2");this.properties={x:0,y:0};this._data=new Float32Array(2)}function r(){this.addInput("vec3","vec3");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number")}function H(){this.addInputs([["x","number"],["y","number"],["z","number"]]);this.addOutput("vec3","vec3");this.properties={x:0,y:0,z:0};this._data=new Float32Array(3)}function I(){this.addInput("vec4","vec4");this.addOutput("x","number");this.addOutput("y","number"); -this.addOutput("z","number");this.addOutput("w","number")}function G(){this.addInputs([["x","number"],["y","number"],["z","number"],["w","number"]]);this.addOutput("vec4","vec4");this.properties={x:0,y:0,z:0,w:0};this._data=new Float32Array(4)}var E=y.LiteGraph;c.title="Converter";c.desc="type A to type B";c.prototype.onExecute=function(){var a=this.getInputData(0);if(null!=a&&this.outputs)for(var b=0;ba&&(a+=1024);var c=Math.floor(a);a-=c;d=e.data[c];c=e.data[1023==c?0:c+1];b&&(a=a*a*a*(a*(6*a-15)+10));return d*(1-a)+c*a};e.prototype.onExecute=function(){var a=this.getInputData(0)||0,a=e.getValue(a,this.properties.smooth), -b=this.properties.min;this._last_v=a*(this.properties.max-b)+b;this.setOutputData(0,this._last_v)};e.prototype.onDrawBackground=function(a){this.outputs[0].label=(this._last_v||0).toFixed(3)};E.registerNodeType("math/noise",e);C.title="Spikes";C.desc="spike every random time";C.prototype.onExecute=function(){var a=this.graph.elapsed_time;this._remaining_time-=a;this._blink_time-=a;a=0;0this._remaining_time?(this._remaining_time= -Math.random()*(this.properties.max_time-this.properties.min_time)+this.properties.min_time,this._blink_time=this.properties.duration,this.boxcolor="#FFF"):this.boxcolor="#000";this.setOutputData(0,a)};E.registerNodeType("math/spikes",C);z.title="Clamp";z.desc="Clamp number between min and max";z.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(a=Math.max(this.properties.min,a),a=Math.min(this.properties.max,a),this.setOutputData(0,a))};z.prototype.getCode=function(a){a="";this.isInputConnected(0)&& -(a+="clamp({{0}},"+this.properties.min+","+this.properties.max+")");return a};E.registerNodeType("math/clamp",z);u.title="Lerp";u.desc="Linear Interpolation";u.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.getInputData(1);null==b&&(b=0);var d=this.properties.f,c=this.getInputData(2);void 0!==c&&(d=c);this.setOutputData(0,a*(1-d)+b*d)};u.prototype.onGetInputs=function(){return[["f","number"]]};E.registerNodeType("math/lerp",u);D.title="Abs";D.desc="Absolute";D.prototype.onExecute= -function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.abs(a))};E.registerNodeType("math/abs",D);B.title="Floor";B.desc="Floor number to remove fractional part";B.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.floor(a))};E.registerNodeType("math/floor",B);g.title="Frac";g.desc="Returns fractional part";g.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a%1)};E.registerNodeType("math/frac",g);p.title= -"Smoothstep";p.desc="Smoothstep";p.prototype.onExecute=function(){var a=this.getInputData(0);if(void 0!==a){var b=this.properties.A,a=Math.clamp((a-b)/(this.properties.B-b),0,1);this.setOutputData(0,a*a*(3-2*a))}};E.registerNodeType("math/smoothstep",p);s.title="Scale";s.desc="v * factor";s.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a*this.properties.factor)};E.registerNodeType("math/scale",s);A.title="Gate";A.desc="if v is true, then outputs A, otherwise B"; -A.prototype.onExecute=function(){var a=this.getInputData(0);this.setOutputData(0,this.getInputData(a?1:2))};E.registerNodeType("math/gate",A);n.title="Average";n.desc="Average Filter";n.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this._values.length;this._values[this._current%b]=a;this._current+=1;this._current>b&&(this._current=0);for(var d=a=0;db&&(b=1);this.properties.samples= -Math.round(b);var d=this._values;this._values=new Float32Array(this.properties.samples);d.length<=this._values.length?this._values.set(d):this._values.set(d.subarray(0,this._values.length))};E.registerNodeType("math/average",n);v.title="TendTo";v.desc="moves the output value always closer to the input";v.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.factor;this._value=null==this._value?a:this._value*(1-b)+a*b;this.setOutputData(0,this._value)};E.registerNodeType("math/tendTo", -v);x.values="+ - * / % ^ max min".split(" ");x.title="Operation";x.desc="Easy math operators";x["@OP"]={type:"enum",title:"operation",values:x.values};x.size=[100,60];x.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"};x.prototype.setValue=function(a){"string"==typeof a&&(a=parseFloat(a));this.properties.value=a};x.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);null!=a? +["out_min","number"],["out_max","number"]]};F.registerNodeType("math/range",k);w.title="Rand";w.desc="Random number";w.prototype.onExecute=function(){if(this.inputs)for(var a=0;aa&&(a+=1024);var c=Math.floor(a);a-=c;d=e.data[c];c=e.data[1023==c?0:c+1];b&&(a=a*a*a*(a*(6*a-15)+10));return d*(1-a)+c*a};e.prototype.onExecute=function(){var a=this.getInputData(0)||0,a=e.getValue(a,this.properties.smooth), +b=this.properties.min;this._last_v=a*(this.properties.max-b)+b;this.setOutputData(0,this._last_v)};e.prototype.onDrawBackground=function(a){this.outputs[0].label=(this._last_v||0).toFixed(3)};F.registerNodeType("math/noise",e);C.title="Spikes";C.desc="spike every random time";C.prototype.onExecute=function(){var a=this.graph.elapsed_time;this._remaining_time-=a;this._blink_time-=a;a=0;0this._remaining_time?(this._remaining_time= +Math.random()*(this.properties.max_time-this.properties.min_time)+this.properties.min_time,this._blink_time=this.properties.duration,this.boxcolor="#FFF"):this.boxcolor="#000";this.setOutputData(0,a)};F.registerNodeType("math/spikes",C);z.title="Clamp";z.desc="Clamp number between min and max";z.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(a=Math.max(this.properties.min,a),a=Math.min(this.properties.max,a),this.setOutputData(0,a))};z.prototype.getCode=function(a){a="";this.isInputConnected(0)&& +(a+="clamp({{0}},"+this.properties.min+","+this.properties.max+")");return a};F.registerNodeType("math/clamp",z);u.title="Lerp";u.desc="Linear Interpolation";u.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.getInputData(1);null==b&&(b=0);var d=this.properties.f,c=this.getInputData(2);void 0!==c&&(d=c);this.setOutputData(0,a*(1-d)+b*d)};u.prototype.onGetInputs=function(){return[["f","number"]]};F.registerNodeType("math/lerp",u);D.title="Abs";D.desc="Absolute";D.prototype.onExecute= +function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.abs(a))};F.registerNodeType("math/abs",D);B.title="Floor";B.desc="Floor number to remove fractional part";B.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.floor(a))};F.registerNodeType("math/floor",B);g.title="Frac";g.desc="Returns fractional part";g.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a%1)};F.registerNodeType("math/frac",g);m.title= +"Smoothstep";m.desc="Smoothstep";m.prototype.onExecute=function(){var a=this.getInputData(0);if(void 0!==a){var b=this.properties.A,a=Math.clamp((a-b)/(this.properties.B-b),0,1);this.setOutputData(0,a*a*(3-2*a))}};F.registerNodeType("math/smoothstep",m);s.title="Scale";s.desc="v * factor";s.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a*this.properties.factor)};F.registerNodeType("math/scale",s);A.title="Gate";A.desc="if v is true, then outputs A, otherwise B"; +A.prototype.onExecute=function(){var a=this.getInputData(0);this.setOutputData(0,this.getInputData(a?1:2))};F.registerNodeType("math/gate",A);r.title="Average";r.desc="Average Filter";r.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this._values.length;this._values[this._current%b]=a;this._current+=1;this._current>b&&(this._current=0);for(var d=a=0;db&&(b=1);this.properties.samples= +Math.round(b);var d=this._values;this._values=new Float32Array(this.properties.samples);d.length<=this._values.length?this._values.set(d):this._values.set(d.subarray(0,this._values.length))};F.registerNodeType("math/average",r);n.title="TendTo";n.desc="moves the output value always closer to the input";n.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.factor;this._value=null==this._value?a:this._value*(1-b)+a*b;this.setOutputData(0,this._value)};F.registerNodeType("math/tendTo", +n);x.values="+ - * / % ^ max min".split(" ");x.title="Operation";x.desc="Easy math operators";x["@OP"]={type:"enum",title:"operation",values:x.values};x.size=[100,60];x.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"};x.prototype.setValue=function(a){"string"==typeof a&&(a=parseFloat(a));this.properties.value=a};x.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);null!=a? this.properties.A=a:a=this.properties.A;null!=b?this.properties.B=b:b=this.properties.B;var d=0;switch(this.properties.OP){case "+":d=a+b;break;case "-":d=a-b;break;case "x":case "X":case "*":d=a*b;break;case "/":d=a/b;break;case "%":d=a%b;break;case "^":d=Math.pow(a,b);break;case "max":d=Math.max(a,b);break;case "min":d=Math.min(a,b);break;default:console.warn("Unknown operation: "+this.properties.OP)}this.setOutputData(0,d)};x.prototype.onDrawBackground=function(a){this.flags.collapsed||(a.font= -"40px Arial",a.fillStyle="#666",a.textAlign="center",a.fillText(this.properties.OP,0.5*this.size[0],0.5*(this.size[1]+E.NODE_TITLE_HEIGHT)),a.textAlign="left")};E.registerNodeType("math/operation",x);E.registerSearchboxExtra("math/operation","MAX",{properties:{OP:"max"},title:"MAX()"});E.registerSearchboxExtra("math/operation","MIN",{properties:{OP:"min"},title:"MIN()"});l.title="Compare";l.desc="compares between two values";l.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1); -void 0!==a?this.properties.A=a:a=this.properties.A;void 0!==b?this.properties.B=b:b=this.properties.B;for(var d=0,c=this.outputs.length;dB":h=a>b;break;case "A=B":h=a>=b}this.setOutputData(d,h)}}};l.prototype.onGetOutputs=function(){return[["A==B","boolean"],["A!=B","boolean"],["A>B","boolean"],["A=B", -"boolean"],["A<=B","boolean"]]};E.registerNodeType("math/compare",l);E.registerSearchboxExtra("math/compare","==",{outputs:[["A==B","boolean"]],title:"A==B"});E.registerSearchboxExtra("math/compare","!=",{outputs:[["A!=B","boolean"]],title:"A!=B"});E.registerSearchboxExtra("math/compare",">",{outputs:[["A>B","boolean"]],title:"A>B"});E.registerSearchboxExtra("math/compare","<",{outputs:[["A=",{outputs:[["A>=B","boolean"]],title:"A>=B"}); -E.registerSearchboxExtra("math/compare","<=",{outputs:[["A<=B","boolean"]],title:"A<=B"});a.values="> < == != <= >= || &&".split(" ");a["@OP"]={type:"enum",title:"operation",values:a.values};a.title="Condition";a.desc="evaluates condition between A and B";a.prototype.getTitle=function(){return"A "+this.properties.OP+" B"};a.prototype.onExecute=function(){var a=this.getInputData(0);void 0===a?a=this.properties.A:this.properties.A=a;var b=this.getInputData(1);void 0===b?b=this.properties.B:this.properties.B= -b;var d=!0;switch(this.properties.OP){case ">":d=a>b;break;case "<":d=a=":d=a>=b;break;case "||":d=a||b;break;case "&&":d=a&&b}this.setOutputData(0,d);this.setOutputData(1,!d)};E.registerNodeType("math/condition",a);b.title="Accumulate";b.desc="Increments a value every time";b.prototype.onExecute=function(){null===this.properties.value&&(this.properties.value=0);var a=this.getInputData(0);this.properties.value=null!== -a?this.properties.value+a:this.properties.value+this.properties.increment;this.setOutputData(0,this.properties.value)};E.registerNodeType("math/accumulate",b);d.title="Trigonometry";d.desc="Sin Cos Tan";d.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.amplitude,d=this.findInputSlot("amplitude");-1!=d&&(b=this.getInputData(d));var c=this.properties.offset,d=this.findInputSlot("offset");-1!=d&&(c=this.getInputData(d));for(var d=0,f=this.outputs.length;d< -f;++d){var h;switch(this.outputs[d].name){case "sin":h=Math.sin(a);break;case "cos":h=Math.cos(a);break;case "tan":h=Math.tan(a);break;case "asin":h=Math.asin(a);break;case "acos":h=Math.acos(a);break;case "atan":h=Math.atan(a)}this.setOutputData(d,b*h+c)}};d.prototype.onGetInputs=function(){return[["v","number"],["amplitude","number"],["offset","number"]]};d.prototype.onGetOutputs=function(){return[["sin","number"],["cos","number"],["tan","number"],["asin","number"],["acos","number"],["atan","number"]]}; -E.registerNodeType("math/trigonometry",d);E.registerSearchboxExtra("math/trigonometry","SIN()",{outputs:[["sin","number"]],title:"SIN()"});E.registerSearchboxExtra("math/trigonometry","COS()",{outputs:[["cos","number"]],title:"COS()"});E.registerSearchboxExtra("math/trigonometry","TAN()",{outputs:[["tan","number"]],title:"TAN()"});h.title="Formula";h.desc="Compute formula";h.size=[160,100];n.prototype.onPropertyChanged=function(a,b){"formula"==a&&(this.code_widget.value=b)};h.prototype.onExecute= -function(){if(E.allow_scripts){var a=this.getInputData(0),b=this.getInputData(1);null!=a?this.properties.x=a:a=this.properties.x;null!=b?this.properties.y=b:b=this.properties.y;var d;try{this._func&&this._func_code==this.properties.formula||(this._func=new Function("x","y","TIME","return "+this.properties.formula),this._func_code=this.properties.formula),d=this._func(a,b,this.graph.globaltime),this.boxcolor=null}catch(c){this.boxcolor="red"}this.setOutputData(0,d)}};h.prototype.getTitle=function(){return this._func_code|| -"Formula"};h.prototype.onDrawBackground=function(){var a=this.properties.formula;this.outputs&&this.outputs.length&&(this.outputs[0].label=a)};E.registerNodeType("math/formula",h);f.title="Vec2->XY";f.desc="vector 2 to components";f.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]))};E.registerNodeType("math3d/vec2-to-xy",f);K.title="XY->Vec2";K.desc="components to vector2";K.prototype.onExecute=function(){var a=this.getInputData(0); -null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this._data;d[0]=a;d[1]=b;this.setOutputData(0,d)};E.registerNodeType("math3d/xy-to-vec2",K);r.title="Vec3->XYZ";r.desc="vector 3 to components";r.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]))};E.registerNodeType("math3d/vec3-to-xyz",r);H.title="XYZ->Vec3";H.desc="components to vector3";H.prototype.onExecute= -function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this._data;c[0]=a;c[1]=b;c[2]=d;this.setOutputData(0,c)};E.registerNodeType("math3d/xyz-to-vec3",H);I.title="Vec4->XYZW";I.desc="vector 4 to components";I.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]),this.setOutputData(3, -a[3]))};E.registerNodeType("math3d/vec4-to-xyzw",I);G.title="XYZW->Vec4";G.desc="components to vector4";G.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this.getInputData(3);null==c&&(c=this.properties.w);var f=this._data;f[0]=a;f[1]=b;f[2]=d;f[3]=c;this.setOutputData(0,f)};E.registerNodeType("math3d/xyzw-to-vec4",G);y.glMatrix&&(y=function(){this.addOutput("quat", -"quat");this.properties={x:0,y:0,z:0,w:1};this._value=quat.create()},y.title="Quaternion",y.desc="quaternion",y.prototype.onExecute=function(){this._value[0]=this.properties.x;this._value[1]=this.properties.y;this._value[2]=this.properties.z;this._value[3]=this.properties.w;this.setOutputData(0,this._value)},E.registerNodeType("math3d/quaternion",y),y=function(){this.addInputs([["degrees","number"],["axis","vec3"]]);this.addOutput("quat","quat");this.properties={angle:90,axis:vec3.fromValues(0,1, -0)};this._value=quat.create()},y.title="Rotation",y.desc="quaternion rotation",y.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.angle);var b=this.getInputData(1);null==b&&(b=this.properties.axis);a=quat.setAxisAngle(this._value,b,0.0174532925*a);this.setOutputData(0,a)},E.registerNodeType("math3d/rotation",y),y=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},y.title="Rot. Vec3",y.desc= -"rotate a point",y.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.vec);var b=this.getInputData(1);null==b?this.setOutputData(a):this.setOutputData(0,vec3.transformQuat(vec3.create(),a,b))},E.registerNodeType("math3d/rotate_vec3",y),y=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},y.title="Mult. Quat",y.desc="rotate quaternion",y.prototype.onExecute=function(){var a=this.getInputData(0);if(null!= -a){var b=this.getInputData(1);null!=b&&(a=quat.multiply(this._value,a,b),this.setOutputData(0,a))}},E.registerNodeType("math3d/mult-quat",y),y=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp","quat");this.addProperty("factor",0.5);this._value=quat.create()},y.title="Quat Slerp",y.desc="quaternion spherical interpolation",y.prototype.onExecute=function(){var a=this.getInputData(0);if(null!=a){var b=this.getInputData(1);if(null!=b){var d=this.properties.factor; -null!=this.getInputData(2)&&(d=this.getInputData(2));a=quat.slerp(this._value,a,b,d);this.setOutputData(0,a)}}},E.registerNodeType("math3d/quat-slerp",y))})(this); -(function(y){function c(){this.addInput("T","vec3");this.addInput("R","vec3");this.addInput("S","vec3");this.addOutput("mat4","mat4");this.properties={T:[0,0,0],R:[0,0,0],S:[1,1,1],R_in_degrees:!0};this._result=mat4.create();this._must_update=!0}function m(){this.addInput("A","number,vec3");this.addInput("B","number,vec3");this.addOutput("=","vec3");this.addProperty("OP","+","enum",{values:m.values});this._result=vec3.create()}function q(){this.addInput("in","vec3");this.addInput("f","number");this.addOutput("out", +"40px Arial",a.fillStyle="#666",a.textAlign="center",a.fillText(this.properties.OP,0.5*this.size[0],0.5*(this.size[1]+F.NODE_TITLE_HEIGHT)),a.textAlign="left")};F.registerNodeType("math/operation",x);F.registerSearchboxExtra("math/operation","MAX",{properties:{OP:"max"},title:"MAX()"});F.registerSearchboxExtra("math/operation","MIN",{properties:{OP:"min"},title:"MIN()"});v.title="Compare";v.desc="compares between two values";v.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1); +void 0!==a?this.properties.A=a:a=this.properties.A;void 0!==b?this.properties.B=b:b=this.properties.B;for(var d=0,c=this.outputs.length;dB":f=a>b;break;case "A=B":f=a>=b}this.setOutputData(d,f)}}};v.prototype.onGetOutputs=function(){return[["A==B","boolean"],["A!=B","boolean"],["A>B","boolean"],["A=B", +"boolean"],["A<=B","boolean"]]};F.registerNodeType("math/compare",v);F.registerSearchboxExtra("math/compare","==",{outputs:[["A==B","boolean"]],title:"A==B"});F.registerSearchboxExtra("math/compare","!=",{outputs:[["A!=B","boolean"]],title:"A!=B"});F.registerSearchboxExtra("math/compare",">",{outputs:[["A>B","boolean"]],title:"A>B"});F.registerSearchboxExtra("math/compare","<",{outputs:[["A=",{outputs:[["A>=B","boolean"]],title:"A>=B"}); +F.registerSearchboxExtra("math/compare","<=",{outputs:[["A<=B","boolean"]],title:"A<=B"});a.values="> < == != <= >= || &&".split(" ");a["@OP"]={type:"enum",title:"operation",values:a.values};a.title="Condition";a.desc="evaluates condition between A and B";a.prototype.getTitle=function(){return"A "+this.properties.OP+" B"};a.prototype.onExecute=function(){var a=this.getInputData(0);void 0===a?a=this.properties.A:this.properties.A=a;var b=this.getInputData(1);void 0===b?b=this.properties.B:this.properties.B= +b;var d=!0;switch(this.properties.OP){case ">":d=a>b;break;case "<":d=a=":d=a>=b;break;case "||":d=a||b;break;case "&&":d=a&&b}this.setOutputData(0,d);this.setOutputData(1,!d)};F.registerNodeType("math/condition",a);b.title="Accumulate";b.desc="Increments a value every time";b.prototype.onExecute=function(){null===this.properties.value&&(this.properties.value=0);var a=this.getInputData(0);this.properties.value=null!== +a?this.properties.value+a:this.properties.value+this.properties.increment;this.setOutputData(0,this.properties.value)};F.registerNodeType("math/accumulate",b);d.title="Trigonometry";d.desc="Sin Cos Tan";d.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.amplitude,d=this.findInputSlot("amplitude");-1!=d&&(b=this.getInputData(d));var c=this.properties.offset,d=this.findInputSlot("offset");-1!=d&&(c=this.getInputData(d));for(var d=0,h=this.outputs.length;d< +h;++d){var f;switch(this.outputs[d].name){case "sin":f=Math.sin(a);break;case "cos":f=Math.cos(a);break;case "tan":f=Math.tan(a);break;case "asin":f=Math.asin(a);break;case "acos":f=Math.acos(a);break;case "atan":f=Math.atan(a)}this.setOutputData(d,b*f+c)}};d.prototype.onGetInputs=function(){return[["v","number"],["amplitude","number"],["offset","number"]]};d.prototype.onGetOutputs=function(){return[["sin","number"],["cos","number"],["tan","number"],["asin","number"],["acos","number"],["atan","number"]]}; +F.registerNodeType("math/trigonometry",d);F.registerSearchboxExtra("math/trigonometry","SIN()",{outputs:[["sin","number"]],title:"SIN()"});F.registerSearchboxExtra("math/trigonometry","COS()",{outputs:[["cos","number"]],title:"COS()"});F.registerSearchboxExtra("math/trigonometry","TAN()",{outputs:[["tan","number"]],title:"TAN()"});h.title="Formula";h.desc="Compute formula";h.size=[160,100];r.prototype.onPropertyChanged=function(a,b){"formula"==a&&(this.code_widget.value=b)};h.prototype.onExecute= +function(){if(F.allow_scripts){var a=this.getInputData(0),b=this.getInputData(1);null!=a?this.properties.x=a:a=this.properties.x;null!=b?this.properties.y=b:b=this.properties.y;var d;try{this._func&&this._func_code==this.properties.formula||(this._func=new Function("x","y","TIME","return "+this.properties.formula),this._func_code=this.properties.formula),d=this._func(a,b,this.graph.globaltime),this.boxcolor=null}catch(c){this.boxcolor="red"}this.setOutputData(0,d)}};h.prototype.getTitle=function(){return this._func_code|| +"Formula"};h.prototype.onDrawBackground=function(){var a=this.properties.formula;this.outputs&&this.outputs.length&&(this.outputs[0].label=a)};F.registerNodeType("math/formula",h);f.title="Vec2->XY";f.desc="vector 2 to components";f.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]))};F.registerNodeType("math3d/vec2-to-xy",f);E.title="XY->Vec2";E.desc="components to vector2";E.prototype.onExecute=function(){var a=this.getInputData(0); +null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this._data;d[0]=a;d[1]=b;this.setOutputData(0,d)};F.registerNodeType("math3d/xy-to-vec2",E);q.title="Vec3->XYZ";q.desc="vector 3 to components";q.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]))};F.registerNodeType("math3d/vec3-to-xyz",q);I.title="XYZ->Vec3";I.desc="components to vector3";I.prototype.onExecute= +function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this._data;c[0]=a;c[1]=b;c[2]=d;this.setOutputData(0,c)};F.registerNodeType("math3d/xyz-to-vec3",I);J.title="Vec4->XYZW";J.desc="vector 4 to components";J.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]),this.setOutputData(3, +a[3]))};F.registerNodeType("math3d/vec4-to-xyzw",J);H.title="XYZW->Vec4";H.desc="components to vector4";H.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this.getInputData(3);null==c&&(c=this.properties.w);var f=this._data;f[0]=a;f[1]=b;f[2]=d;f[3]=c;this.setOutputData(0,f)};F.registerNodeType("math3d/xyzw-to-vec4",H);y.glMatrix&&(y=function(){this.addOutput("quat", +"quat");this.properties={x:0,y:0,z:0,w:1};this._value=quat.create()},y.title="Quaternion",y.desc="quaternion",y.prototype.onExecute=function(){this._value[0]=this.properties.x;this._value[1]=this.properties.y;this._value[2]=this.properties.z;this._value[3]=this.properties.w;this.setOutputData(0,this._value)},F.registerNodeType("math3d/quaternion",y),y=function(){this.addInputs([["degrees","number"],["axis","vec3"]]);this.addOutput("quat","quat");this.properties={angle:90,axis:vec3.fromValues(0,1, +0)};this._value=quat.create()},y.title="Rotation",y.desc="quaternion rotation",y.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.angle);var b=this.getInputData(1);null==b&&(b=this.properties.axis);a=quat.setAxisAngle(this._value,b,0.0174532925*a);this.setOutputData(0,a)},F.registerNodeType("math3d/rotation",y),y=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},y.title="Rot. Vec3",y.desc= +"rotate a point",y.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.vec);var b=this.getInputData(1);null==b?this.setOutputData(a):this.setOutputData(0,vec3.transformQuat(vec3.create(),a,b))},F.registerNodeType("math3d/rotate_vec3",y),y=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},y.title="Mult. Quat",y.desc="rotate quaternion",y.prototype.onExecute=function(){var a=this.getInputData(0);if(null!= +a){var b=this.getInputData(1);null!=b&&(a=quat.multiply(this._value,a,b),this.setOutputData(0,a))}},F.registerNodeType("math3d/mult-quat",y),y=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp","quat");this.addProperty("factor",0.5);this._value=quat.create()},y.title="Quat Slerp",y.desc="quaternion spherical interpolation",y.prototype.onExecute=function(){var a=this.getInputData(0);if(null!=a){var b=this.getInputData(1);if(null!=b){var d=this.properties.factor; +null!=this.getInputData(2)&&(d=this.getInputData(2));a=quat.slerp(this._value,a,b,d);this.setOutputData(0,a)}}},F.registerNodeType("math3d/quat-slerp",y))})(this); +(function(y){function c(){this.addInput("T","vec3");this.addInput("R","vec3");this.addInput("S","vec3");this.addOutput("mat4","mat4");this.properties={T:[0,0,0],R:[0,0,0],S:[1,1,1],R_in_degrees:!0};this._result=mat4.create();this._must_update=!0}function l(){this.addInput("A","number,vec3");this.addInput("B","number,vec3");this.addOutput("=","vec3");this.addProperty("OP","+","enum",{values:l.values});this._result=vec3.create()}function p(){this.addInput("in","vec3");this.addInput("f","number");this.addOutput("out", "vec3");this.properties={f:1};this._data=new Float32Array(3)}function k(){this.addInput("in","vec3");this.addOutput("out","number")}function w(){this.addInput("in","vec3");this.addOutput("out","vec3");this._data=new Float32Array(3)}function e(){this.addInput("A","vec3");this.addInput("B","vec3");this.addInput("f","vec3");this.addOutput("out","vec3");this.properties={f:0.5};this._data=new Float32Array(3)}function C(){this.addInput("A","vec3");this.addInput("B","vec3");this.addOutput("out","number")} -var z=y.LiteGraph;c.title="mat4";c.temp_quat=new Float32Array([0,0,0,1]);c.temp_mat4=new Float32Array(16);c.temp_vec3=new Float32Array(3);c.prototype.onPropertyChanged=function(c,e){this._must_update=!0};c.prototype.onExecute=function(){var e=this._result,k=c.temp_quat,q=c.temp_mat4,g=c.temp_vec3,p=this.getInputData(0),s=this.getInputData(1),m=this.getInputData(2);if(this._must_update||p||s||m)p=p||this.properties.T,s=s||this.properties.R,m=m||this.properties.S,mat4.identity(e),mat4.translate(e,e, -p),this.properties.R_in_degrees?(g.set(s),vec3.scale(g,g,DEG2RAD),quat.fromEuler(k,g)):quat.fromEuler(k,s),mat4.fromQuat(q,k),mat4.multiply(e,e,q),mat4.scale(e,e,m);this.setOutputData(0,e)};z.registerNodeType("math3d/mat4",c);m.values="+ - * / % ^ max min".split(" ");m.title="Operation";m.desc="Easy math 3D operators";m["@OP"]={type:"enum",title:"operation",values:m.values};m.size=[100,60];m.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+ -"(A,B)":"A "+this.properties.OP+" B"};m.prototype.onExecute=function(){var c=this.getInputData(0),e=this.getInputData(1);if(null!=c&&null!=e){c.constructor===Number&&(c=[c,c,c]);e.constructor===Number&&(e=[e,e,e]);var k=this._result;switch(this.properties.OP){case "+":k=vec3.add(k,c,e);break;case "-":k=vec3.sub(k,c,e);break;case "x":case "X":case "*":k=vec3.mul(k,c,e);break;case "/":k=vec3.div(k,c,e);break;case "%":k[0]=c[0]%e[0];k[1]=c[1]%e[1];k[2]=c[2]%e[2];break;case "^":k[0]=Math.pow(c[0],e[0]); -k[1]=Math.pow(c[1],e[1]);k[2]=Math.pow(c[2],e[2]);break;case "max":k[0]=Math.max(c[0],e[0]);k[1]=Math.max(c[1],e[1]);k[2]=Math.max(c[2],e[2]);break;case "min":k[0]=Math.min(c[0],e[0]);k[1]=Math.min(c[1],e[1]);k[2]=Math.min(c[2],e[2]);break;default:console.warn("Unknown operation: "+this.properties.OP)}this.setOutputData(0,k)}};m.prototype.onDrawBackground=function(c){this.flags.collapsed||(c.font="40px Arial",c.fillStyle="#666",c.textAlign="center",c.fillText(this.properties.OP,0.5*this.size[0],0.5* -(this.size[1]+z.NODE_TITLE_HEIGHT)),c.textAlign="left")};z.registerNodeType("math3d/operation",m);q.title="vec3_scale";q.desc="scales the components of a vec3";q.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null==e&&(e=this.properties.f);var k=this._data;k[0]=c[0]*e;k[1]=c[1]*e;k[2]=c[2]*e;this.setOutputData(0,k)}};z.registerNodeType("math3d/vec3-scale",q);k.title="vec3_length";k.desc="returns the module of a vector";k.prototype.onExecute=function(){var c= +var z=y.LiteGraph;c.title="mat4";c.temp_quat=new Float32Array([0,0,0,1]);c.temp_mat4=new Float32Array(16);c.temp_vec3=new Float32Array(3);c.prototype.onPropertyChanged=function(c,e){this._must_update=!0};c.prototype.onExecute=function(){var e=this._result,k=c.temp_quat,p=c.temp_mat4,g=c.temp_vec3,m=this.getInputData(0),s=this.getInputData(1),l=this.getInputData(2);if(this._must_update||m||s||l)m=m||this.properties.T,s=s||this.properties.R,l=l||this.properties.S,mat4.identity(e),mat4.translate(e,e, +m),this.properties.R_in_degrees?(g.set(s),vec3.scale(g,g,DEG2RAD),quat.fromEuler(k,g)):quat.fromEuler(k,s),mat4.fromQuat(p,k),mat4.multiply(e,e,p),mat4.scale(e,e,l);this.setOutputData(0,e)};z.registerNodeType("math3d/mat4",c);l.values="+ - * / % ^ max min".split(" ");l.title="Operation";l.desc="Easy math 3D operators";l["@OP"]={type:"enum",title:"operation",values:l.values};l.size=[100,60];l.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+ +"(A,B)":"A "+this.properties.OP+" B"};l.prototype.onExecute=function(){var c=this.getInputData(0),e=this.getInputData(1);if(null!=c&&null!=e){c.constructor===Number&&(c=[c,c,c]);e.constructor===Number&&(e=[e,e,e]);var k=this._result;switch(this.properties.OP){case "+":k=vec3.add(k,c,e);break;case "-":k=vec3.sub(k,c,e);break;case "x":case "X":case "*":k=vec3.mul(k,c,e);break;case "/":k=vec3.div(k,c,e);break;case "%":k[0]=c[0]%e[0];k[1]=c[1]%e[1];k[2]=c[2]%e[2];break;case "^":k[0]=Math.pow(c[0],e[0]); +k[1]=Math.pow(c[1],e[1]);k[2]=Math.pow(c[2],e[2]);break;case "max":k[0]=Math.max(c[0],e[0]);k[1]=Math.max(c[1],e[1]);k[2]=Math.max(c[2],e[2]);break;case "min":k[0]=Math.min(c[0],e[0]);k[1]=Math.min(c[1],e[1]);k[2]=Math.min(c[2],e[2]);break;default:console.warn("Unknown operation: "+this.properties.OP)}this.setOutputData(0,k)}};l.prototype.onDrawBackground=function(c){this.flags.collapsed||(c.font="40px Arial",c.fillStyle="#666",c.textAlign="center",c.fillText(this.properties.OP,0.5*this.size[0],0.5* +(this.size[1]+z.NODE_TITLE_HEIGHT)),c.textAlign="left")};z.registerNodeType("math3d/operation",l);p.title="vec3_scale";p.desc="scales the components of a vec3";p.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null==e&&(e=this.properties.f);var k=this._data;k[0]=c[0]*e;k[1]=c[1]*e;k[2]=c[2]*e;this.setOutputData(0,k)}};z.registerNodeType("math3d/vec3-scale",p);k.title="vec3_length";k.desc="returns the module of a vector";k.prototype.onExecute=function(){var c= this.getInputData(0);null!=c&&(c=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),this.setOutputData(0,c))};z.registerNodeType("math3d/vec3-length",k);w.title="vec3_normalize";w.desc="returns the vector normalized";w.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),k=this._data;k[0]=c[0]/e;k[1]=c[1]/e;k[2]=c[2]/e;this.setOutputData(0,k)}};z.registerNodeType("math3d/vec3-normalize",w);e.title="vec3_lerp";e.desc="returns the interpolated vector"; e.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);if(null!=e){var k=this.getInputOrProperty("f"),g=this._data;g[0]=c[0]*(1-k)+e[0]*k;g[1]=c[1]*(1-k)+e[1]*k;g[2]=c[2]*(1-k)+e[2]*k;this.setOutputData(0,g)}}};z.registerNodeType("math3d/vec3-lerp",e);C.title="vec3_dot";C.desc="returns the dot product";C.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null!=e&&this.setOutputData(0,c[0]*e[0]+c[1]*e[1]+c[2]* e[2])}};z.registerNodeType("math3d/vec3-dot",C);y.glMatrix?(y=function(){this.addOutput("quat","quat");this.properties={x:0,y:0,z:0,w:1,normalize:!1};this._value=quat.create()},y.title="Quaternion",y.desc="quaternion",y.prototype.onExecute=function(){this._value[0]=this.getInputOrProperty("x");this._value[1]=this.getInputOrProperty("y");this._value[2]=this.getInputOrProperty("z");this._value[3]=this.getInputOrProperty("w");this.properties.normalize&&quat.normalize(this._value,this._value);this.setOutputData(0, @@ -373,33 +380,35 @@ this._value)},y.prototype.onGetInputs=function(){return[["x","number"],["y","num null==e&&(e=this.properties.axis);c=quat.setAxisAngle(this._value,e,0.0174532925*c);this.setOutputData(0,c)},z.registerNodeType("math3d/rotation",y),y=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},y.title="Rot. Vec3",y.desc="rotate a point",y.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.vec);var e=this.getInputData(1);null==e?this.setOutputData(c):this.setOutputData(0,vec3.transformQuat(vec3.create(), c,e))},z.registerNodeType("math3d/rotate_vec3",y),y=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},y.title="Mult. Quat",y.desc="rotate quaternion",y.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null!=e&&(c=quat.multiply(this._value,c,e),this.setOutputData(0,c))}},z.registerNodeType("math3d/mult-quat",y),y=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp", "quat");this.addProperty("factor",0.5);this._value=quat.create()},y.title="Quat Slerp",y.desc="quaternion spherical interpolation",y.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);if(null!=e){var k=this.properties.factor;null!=this.getInputData(2)&&(k=this.getInputData(2));c=quat.slerp(this._value,c,e,k);this.setOutputData(0,c)}}},z.registerNodeType("math3d/quat-slerp",y),y=function(){this.addInput("vec3","vec3");this.addOutput("remap","vec3");this.addOutput("clamped", -"vec3");this.properties={clamp:!0,range_min:[-1,-1,0],range_max:[1,1,0],target_min:[-1,-1,0],target_max:[1,1,0]};this._value=vec3.create();this._clamped=vec3.create()},y.title="Remap Range",y.desc="remap a 3D range",y.prototype.onExecute=function(){var c=this.getInputData(0);c&&this._value.set(c);for(var c=this.properties.range_min,e=this.properties.range_max,k=this.properties.target_min,g=this.properties.target_max,p=0;3>p;++p){var s=e[p]-c[p];this._clamped[p]=Math.clamp(this._value[p],c[p],e[p]); -0==s?this._value[p]=0.5*(k[p]+g[p]):(s=(this._value[p]-c[p])/s,this.properties.clamp&&(s=Math.clamp(s,0,1)),this._value[p]=k[p]+s*(g[p]-k[p]))}this.setOutputData(0,this._value);this.setOutputData(1,this._clamped)},z.registerNodeType("math3d/remap_range",y)):console.warn("No glmatrix found, some Math3D nodes may not work")})(this); -(function(y){function c(c,k){return c==k}function m(c){return null!=c&&c.constructor===String?c.toUpperCase():c}y=y.LiteGraph;y.wrapFunctionAsNode("string/toString",c,["*"],"String");y.wrapFunctionAsNode("string/compare",c,["String","String"],"Boolean");y.wrapFunctionAsNode("string/concatenate",function(c,k){return void 0===c?k:void 0===k?c:c+k},["String","String"],"String");y.wrapFunctionAsNode("string/contains",function(c,k){return void 0===c||void 0===k?!1:-1!=c.indexOf(k)},["String","String"], -"Boolean");y.wrapFunctionAsNode("string/toUpperCase",m,["String"],"String");y.wrapFunctionAsNode("string/split",m,["String","String"],"Array");y.wrapFunctionAsNode("string/toFixed",function(c){return null!=c&&c.constructor===Number?c.toFixed(this.properties.precision):c},["Number"],"String",{precision:0})})(this); -(function(y){function c(){this.addInput("sel","number");this.addInput("A");this.addInput("B");this.addInput("C");this.addInput("D");this.addOutput("out");this.selected=0}function m(){this.properties={sequence:"A,B,C"};this.addInput("index","number");this.addInput("seq");this.addOutput("out");this.index=0;this.values=this.properties.sequence.split(",")}var q=y.LiteGraph;c.title="Selector";c.desc="selects an output";c.prototype.onDrawBackground=function(c){if(!this.flags.collapsed){c.fillStyle="#AFB"; -var m=(this.selected+1)*q.NODE_SLOT_HEIGHT+6;c.beginPath();c.moveTo(50,m);c.lineTo(50,m+q.NODE_SLOT_HEIGHT);c.lineTo(34,m+0.5*q.NODE_SLOT_HEIGHT);c.fill()}};c.prototype.onExecute=function(){var c=this.getInputData(0);if(null==c||c.constructor!==Number)c=0;this.selected=c=Math.round(c)%(this.inputs.length-1);c=this.getInputData(c+1);void 0!==c&&this.setOutputData(0,c)};c.prototype.onGetInputs=function(){return[["E",0],["F",0],["G",0],["H",0]]};q.registerNodeType("logic/selector",c);m.title="Sequence"; -m.desc="select one element from a sequence from a string";m.prototype.onPropertyChanged=function(c,m){"sequence"==c&&(this.values=m.split(","))};m.prototype.onExecute=function(){var c=this.getInputData(1);c&&c!=this.current_sequence&&(this.values=c.split(","),this.current_sequence=c);c=this.getInputData(0);null==c&&(c=0);this.index=c=Math.round(c)%this.values.length;this.setOutputData(0,this.values[c])};q.registerNodeType("logic/sequence",m)})(this); -(function(y){function c(){this.addInput("A","Number");this.addInput("B","Number");this.addInput("C","Number");this.addInput("D","Number");this.values=[[],[],[],[]];this.properties={scale:2}}function m(){this.addOutput("frame","image");this.properties={url:""}}function q(){this.addInput("f","number");this.addOutput("Color","color");this.properties={colorA:"#444444",colorB:"#44AAFF",colorC:"#44FFAA",colorD:"#FFFFFF"}}function k(){this.addInput("","image,canvas");this.size=[200,200]}function w(){this.addInputs([["img1", +"vec3");this.properties={clamp:!0,range_min:[-1,-1,0],range_max:[1,1,0],target_min:[-1,-1,0],target_max:[1,1,0]};this._value=vec3.create();this._clamped=vec3.create()},y.title="Remap Range",y.desc="remap a 3D range",y.prototype.onExecute=function(){var c=this.getInputData(0);c&&this._value.set(c);for(var c=this.properties.range_min,e=this.properties.range_max,k=this.properties.target_min,g=this.properties.target_max,m=0;3>m;++m){var s=e[m]-c[m];this._clamped[m]=Math.clamp(this._value[m],c[m],e[m]); +0==s?this._value[m]=0.5*(k[m]+g[m]):(s=(this._value[m]-c[m])/s,this.properties.clamp&&(s=Math.clamp(s,0,1)),this._value[m]=k[m]+s*(g[m]-k[m]))}this.setOutputData(0,this._value);this.setOutputData(1,this._clamped)},z.registerNodeType("math3d/remap_range",y)):console.warn("No glmatrix found, some Math3D nodes may not work")})(this); +(function(y){function c(c,k){return c==k}function l(){this.addInput("","string");this.addOutput("table","table");this.addOutput("rows","number");this.addProperty("value","");this.addProperty("separator",",");this._table=null}y=y.LiteGraph;y.wrapFunctionAsNode("string/toString",c,["*"],"String");y.wrapFunctionAsNode("string/compare",c,["string","string"],"boolean");y.wrapFunctionAsNode("string/concatenate",function(c,k){return void 0===c?k:void 0===k?c:c+k},["string","string"],"string");y.wrapFunctionAsNode("string/contains", +function(c,k){return void 0===c||void 0===k?!1:-1!=c.indexOf(k)},["string","string"],"boolean");y.wrapFunctionAsNode("string/toUpperCase",function(c){return null!=c&&c.constructor===String?c.toUpperCase():c},["string"],"string");y.wrapFunctionAsNode("string/split",function(c,k){null==k&&(k=this.properties.separator);if(null==c)return[];if(c.constructor===String)return c.split(k||" ");if(c.constructor===Array){for(var l=[],e=0;ee;++e){var g=this.getInputData(e);if(null!=g){var n=this.values[e];n.push(g);n.length>c[0]&&n.shift()}}}};c.prototype.onDrawBackground=function(e){if(!this.flags.collapsed){var g=this.size,k=0.5*g[1]/ -this.properties.scale,n=c.colors,v=0.5*g[1];e.fillStyle="#000";e.fillRect(0,0,g[0],g[1]);e.strokeStyle="#555";e.beginPath();e.moveTo(0,v);e.lineTo(g[0],v);e.stroke();if(this.inputs)for(var m=0;4>m;++m){var l=this.values[m];if(this.inputs[m]&&this.inputs[m].link){e.strokeStyle=n[m];e.beginPath();var a=l[0]*k*-1+v;e.moveTo(0,Math.clamp(a,0,g[1]));for(var b=1;be&&(e=0);if(0!=c.length){var g=[0,0,0];if(0==e)g=c[0];else if(1==e)g=c[c.length-1];else{var n= -(c.length-1)*e,e=c[Math.floor(n)],c=c[Math.floor(n)+1],n=n-Math.floor(n);g[0]=e[0]*(1-n)+c[0]*n;g[1]=e[1]*(1-n)+c[1]*n;g[2]=e[2]*(1-n)+c[2]*n}for(var k in g)g[k]/=255;this.boxcolor=colorToString(g);this.setOutputData(0,g)}};g.registerNodeType("color/palette",q);k.title="Frame";k.desc="Frame viewerew";k.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}];k.prototype.onDrawBackground=function(c){this.frame&&!this.flags.collapsed&&c.drawImage(this.frame, -0,0,this.size[0],this.size[1])};k.prototype.onExecute=function(){this.frame=this.getInputData(0);this.setDirtyCanvas(!0)};k.prototype.onWidget=function(c,e){if("resize"==e.name&&this.frame){var g=this.frame.width,n=this.frame.height;g||null==this.frame.videoWidth||(g=this.frame.videoWidth,n=this.frame.videoHeight);g&&n&&(this.size=[g,n]);this.setDirtyCanvas(!0,!0)}else"view"==e.name&&this.show()};k.prototype.show=function(){showElement&&this.frame&&showElement(this.frame)};g.registerNodeType("graphics/frame", +function B(){this.addOutput("Webcam","image");this.properties={facingMode:"user"};this.boxcolor="black";this.frame=0}var g=y.LiteGraph;c.title="Plot";c.desc="Plots data over time";c.colors=["#FFF","#F99","#9F9","#99F"];c.prototype.onExecute=function(c){if(!this.flags.collapsed){c=this.size;for(var e=0;4>e;++e){var g=this.getInputData(e);if(null!=g){var r=this.values[e];r.push(g);r.length>c[0]&&r.shift()}}}};c.prototype.onDrawBackground=function(e){if(!this.flags.collapsed){var g=this.size,k=0.5*g[1]/ +this.properties.scale,r=c.colors,n=0.5*g[1];e.fillStyle="#000";e.fillRect(0,0,g[0],g[1]);e.strokeStyle="#555";e.beginPath();e.moveTo(0,n);e.lineTo(g[0],n);e.stroke();if(this.inputs)for(var l=0;4>l;++l){var v=this.values[l];if(this.inputs[l]&&this.inputs[l].link){e.strokeStyle=r[l];e.beginPath();var a=v[0]*k*-1+n;e.moveTo(0,Math.clamp(a,0,g[1]));for(var b=1;be&&(e=0);if(0!=c.length){var g=[0,0,0];if(0==e)g=c[0];else if(1==e)g=c[c.length-1];else{var r= +(c.length-1)*e,e=c[Math.floor(r)],c=c[Math.floor(r)+1],r=r-Math.floor(r);g[0]=e[0]*(1-r)+c[0]*r;g[1]=e[1]*(1-r)+c[1]*r;g[2]=e[2]*(1-r)+c[2]*r}for(var n in g)g[n]/=255;this.boxcolor=colorToString(g);this.setOutputData(0,g)}};g.registerNodeType("color/palette",p);k.title="Frame";k.desc="Frame viewerew";k.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}];k.prototype.onDrawBackground=function(c){this.frame&&!this.flags.collapsed&&c.drawImage(this.frame, +0,0,this.size[0],this.size[1])};k.prototype.onExecute=function(){this.frame=this.getInputData(0);this.setDirtyCanvas(!0)};k.prototype.onWidget=function(c,e){if("resize"==e.name&&this.frame){var g=this.frame.width,r=this.frame.height;g||null==this.frame.videoWidth||(g=this.frame.videoWidth,r=this.frame.videoHeight);g&&r&&(this.size=[g,r]);this.setDirtyCanvas(!0,!0)}else"view"==e.name&&this.show()};k.prototype.show=function(){showElement&&this.frame&&showElement(this.frame)};g.registerNodeType("graphics/frame", k);w.title="Image fade";w.desc="Fades between images";w.widgets=[{name:"resizeA",text:"Resize to A",type:"button"},{name:"resizeB",text:"Resize to B",type:"button"}];w.prototype.onAdded=function(){this.createCanvas();var c=this.canvas.getContext("2d");c.fillStyle="#000";c.fillRect(0,0,this.properties.width,this.properties.height)};w.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};w.prototype.onExecute= function(){var c=this.canvas.getContext("2d");this.canvas.width=this.canvas.width;var e=this.getInputData(0);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);e=this.getInputData(2);null==e?e=this.properties.fade:this.properties.fade=e;c.globalAlpha=e;e=this.getInputData(1);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);c.globalAlpha=1;this.setOutputData(0,this.canvas);this.setDirtyCanvas(!0)};g.registerNodeType("graphics/imagefade",w);e.title="Crop";e.desc="Crop Image"; e.prototype.onAdded=function(){this.createCanvas()};e.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};e.prototype.onExecute=function(){var c=this.getInputData(0);c&&(c.width?(this.canvas.getContext("2d").drawImage(c,-this.properties.x,-this.properties.y,c.width*this.properties.scale,c.height*this.properties.scale),this.setOutputData(0,this.canvas)):this.setOutputData(0,null))};e.prototype.onDrawBackground= function(c){this.flags.collapsed||this.canvas&&c.drawImage(this.canvas,0,0,this.canvas.width,this.canvas.height,0,0,this.size[0],this.size[1])};e.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"scale"==c?(this.properties[c]=parseFloat(e),0==this.properties[c]&&(console.error("Error in scale"),this.properties[c]=1)):this.properties[c]=parseInt(e);this.createCanvas();return!0};g.registerNodeType("graphics/cropImage",e);C.title="Canvas";C.desc="Canvas to render stuff";C.prototype.onExecute= function(){var c=this.canvas,e=this.properties.width|0,g=this.properties.height|0;c.width!=e&&(c.width=e);c.height!=g&&(c.height=g);this.properties.autoclear&&this.ctx.clearRect(0,0,c.width,c.height);this.setOutputData(0,c)};C.prototype.onAction=function(c,e){"clear"==c&&this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)};g.registerNodeType("graphics/canvas",C);z.title="DrawImage";z.desc="Draws image into a canvas";z.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e= -this.getInputOrProperty("img");if(e){var g=this.getInputOrProperty("x"),n=this.getInputOrProperty("y");c.getContext("2d").drawImage(e,g,n)}}};g.registerNodeType("graphics/drawImage",z);u.title="DrawRectangle";u.desc="Draws rectangle in canvas";u.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e=this.getInputOrProperty("x"),g=this.getInputOrProperty("y"),n=this.getInputOrProperty("w"),k=this.getInputOrProperty("h");c.getContext("2d").fillRect(e,g,n,k)}};g.registerNodeType("graphics/drawRectangle", +this.getInputOrProperty("img");if(e){var g=this.getInputOrProperty("x"),r=this.getInputOrProperty("y");c.getContext("2d").drawImage(e,g,r)}}};g.registerNodeType("graphics/drawImage",z);u.title="DrawRectangle";u.desc="Draws rectangle in canvas";u.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e=this.getInputOrProperty("x"),g=this.getInputOrProperty("y"),r=this.getInputOrProperty("w"),n=this.getInputOrProperty("h");c.getContext("2d").fillRect(e,g,r,n)}};g.registerNodeType("graphics/drawRectangle", u);D.title="Video";D.desc="Video playback";D.widgets=[{name:"play",text:"PLAY",type:"minibutton"},{name:"stop",text:"STOP",type:"minibutton"},{name:"demo",text:"Demo video",type:"button"},{name:"mute",text:"Mute video",type:"button"}];D.prototype.onExecute=function(){if(this.properties.url&&(this.properties.url!=this._video_url&&this.loadVideo(this.properties.url),this._video&&0!=this._video.width)){var c=this.getInputData(0);c&&0<=c&&1>=c&&(this._video.currentTime=c*this._video.duration,this._video.pause()); this._video.dirty=!0;this.setOutputData(0,this._video);this.setOutputData(1,this._video.currentTime);this.setOutputData(2,this._video.duration);this.setDirtyCanvas(!0)}};D.prototype.onStart=function(){this.play()};D.prototype.onStop=function(){this.stop()};D.prototype.loadVideo=function(c){this._video_url=c;var e=c.substr(0,10).indexOf(":"),k="";-1!=e&&(k=c.substr(0,e));e="";k&&(e=c.substr(0,c.indexOf("/",k.length+3)),e=e.substr(k.length+3));this.properties.use_proxy&&k&&g.proxy&&e!=location.host&& -(c=g.proxy+c.substr(c.indexOf(":")+3));this._video=document.createElement("video");this._video.src=c;this._video.type="type=video/mp4";this._video.muted=!0;this._video.autoplay=!0;var n=this;this._video.addEventListener("loadedmetadata",function(c){console.log("Duration: "+this.duration+" seconds");console.log("Size: "+this.videoWidth+","+this.videoHeight);n.setDirtyCanvas(!0);this.width=this.videoWidth;this.height=this.videoHeight});this._video.addEventListener("progress",function(c){console.log("video loading...")}); +(c=g.proxy+c.substr(c.indexOf(":")+3));this._video=document.createElement("video");this._video.src=c;this._video.type="type=video/mp4";this._video.muted=!0;this._video.autoplay=!0;var r=this;this._video.addEventListener("loadedmetadata",function(c){console.log("Duration: "+this.duration+" seconds");console.log("Size: "+this.videoWidth+","+this.videoHeight);r.setDirtyCanvas(!0);this.width=this.videoWidth;this.height=this.videoHeight});this._video.addEventListener("progress",function(c){console.log("video loading...")}); this._video.addEventListener("error",function(c){console.error("Error loading video: "+this.src);if(this.error)switch(this.error.code){case this.error.MEDIA_ERR_ABORTED:console.error("You stopped the video.");break;case this.error.MEDIA_ERR_NETWORK:console.error("Network error - please try again later.");break;case this.error.MEDIA_ERR_DECODE:console.error("Video is broken..");break;case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED:console.error("Sorry, your browser can't play this video.")}});this._video.addEventListener("ended", function(c){console.log("Video Ended.");this.play()})};D.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"url"==c&&""!=e&&this.loadVideo(e);return!0};D.prototype.play=function(){this._video&&this._video.videoWidth&&this._video.play()};D.prototype.playPause=function(){this._video&&(this._video.paused?this.play():this.pause())};D.prototype.stop=function(){this._video&&(this._video.pause(),this._video.currentTime=0)};D.prototype.pause=function(){this._video&&(console.log("Video paused"), this._video.pause())};D.prototype.onWidget=function(c,e){};g.registerNodeType("graphics/video",D);B.title="Webcam";B.desc="Webcam image";B.is_webcam_open=!1;B.prototype.openStream=function(){function c(g){console.log("Webcam rejected",g);e._webcam_stream=!1;B.is_webcam_open=!1;e.boxcolor="red";e.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](c); @@ -407,25 +416,25 @@ var e=this}};B.prototype.closeStream=function(){if(this._webcam_stream){var c=th c;this.boxcolor="green";var e=this._video;e||(e=document.createElement("video"),e.autoplay=!0,e.srcObject=c,this._video=e,e.onloadedmetadata=function(c){console.log(c);B.is_webcam_open=!0});this.trigger("stream_ready",e)};B.prototype.onExecute=function(){null!=this._webcam_stream||this._waiting_confirmation||this.openStream();if(this._video&&this._video.videoWidth){this._video.frame=++this.frame;this._video.width=this._video.videoWidth;this._video.height=this._video.videoHeight;this.setOutputData(0, this._video);for(var c=1;c=this.size[1]||!this.properties.show|| !this._video||(c.save(),c.drawImage(this._video,0,0,this.size[0],this.size[1]),c.restore())};B.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["stream_ready",g.EVENT],["stream_closed",g.EVENT],["stream_error",g.EVENT]]};g.registerNodeType("graphics/webcam",B)})(this); -(function(y){function c(){this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",filter:!0};this.size=[c.image_preview_size,c.image_preview_size]}function m(){this.addInput("Texture","Texture");this.properties={flipY:!1};this.size=[c.image_preview_size,c.image_preview_size]}function q(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",generate_mipmaps:!1}}function k(){this.addInput("Texture", +(function(y){function c(){this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",filter:!0};this.size=[c.image_preview_size,c.image_preview_size]}function l(){this.addInput("Texture","Texture");this.properties={flipY:!1};this.size=[c.image_preview_size,c.image_preview_size]}function p(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",generate_mipmaps:!1}}function k(){this.addInput("Texture", "Texture");this.addInput("TextureB","Texture");this.addInput("value","number");this.addOutput("Texture","Texture");this.help="

pixelcode must be vec3, uvcode must be vec2, is optional

\t\t

uv: tex. coords

color: texture colorB: textureB

time: scene time value: input value

For multiline you must type: result = ...

";this.properties={value:1,pixelcode:"color + colorB * value",uvcode:"",precision:c.DEFAULT}; this.has_error=!1}function w(){this.addOutput("out","Texture");this.properties={code:"",u_value:1,u_color:[1,1,1,1],width:512,height:512,precision:c.DEFAULT};this.properties.code=w.pixel_shader;this._uniforms={u_value:1,u_color:vec4.create(),in_texture:0,texSize:vec2.create(),time:0}}function e(){this.addInput("in","Texture");this.addInput("scale","vec2");this.addInput("offset","vec2");this.addOutput("out","Texture");this.properties={offset:vec2.fromValues(0,0),scale:vec2.fromValues(1,1),precision:c.DEFAULT}} function C(){this.addInput("in","Texture");this.addInput("warp","Texture");this.addInput("factor","number");this.addOutput("out","Texture");this.properties={factor:0.01,scale:[1,1],offset:[0,0],precision:c.DEFAULT};this._uniforms={u_texture:0,u_textureB:1,u_factor:1,u_scale:vec2.create(),u_offset:vec2.create()}}function z(){this.addInput("Texture","Texture");this.properties={additive:!1,antialiasing:!1,filter:!0,disable_alpha:!1,gamma:1,viewport:[0,0,1,1]};this.size[0]=130}function u(){this.addInput("Texture", "Texture");this.addOutput("","Texture");this.properties={size:0,generate_mipmaps:!1,precision:c.DEFAULT}}function D(){this.addInput("Texture","Texture");this.addOutput("","Texture");this.properties={iterations:1,generate_mipmaps:!1,precision:c.DEFAULT}}function B(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("avg","vec4");this.addOutput("lum","number");this.properties={use_previous_frame:!0,high_quality:!1};this._uniforms={u_texture:0,u_mipmap_offset:0};this._luminance= -new Float32Array(4)}function g(){this.addInput("Texture","Texture");this.addOutput("min_t","Texture");this.addOutput("max_t","Texture");this.addOutput("min","vec4");this.addOutput("max","vec4");this.properties={mode:"max",use_previous_frame:!0};this._uniforms={u_texture:0};this._max=new Float32Array(4);this._min=new Float32Array(4);this._textures_chain=[]}function p(){this.addInput("in","Texture");this.addInput("factor","Number");this.addOutput("out","Texture");this.properties={factor:0.5};this._uniforms= -{u_texture:0,u_textureB:1,u_factor:this.properties.factor}}function s(){this.addInput("in","Texture");this.addOutput("avg","Texture");this.addOutput("array","Texture");this.properties={samples:64,frames_interval:1};this._uniforms={u_texture:0,u_textureB:1,u_samples:this.properties.samples,u_isamples:1/this.properties.samples};this.frame=0}function A(){this.addInput("Image","image");this.addOutput("","Texture");this.properties={}}function n(){this.addInput("Texture","Texture");this.addInput("LUT", -"Texture");this.addInput("Intensity","number");this.addOutput("","Texture");this.properties={enabled:!0,intensity:1,precision:c.DEFAULT,texture:null};n._shader||(n._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,n.pixel_shader))}function v(){this.addInput("Texture","Texture");this.addOutput("R","Texture");this.addOutput("G","Texture");this.addOutput("B","Texture");this.addOutput("A","Texture");v._shader||(v._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,v.pixel_shader))}function x(){this.addInput("R", -"Texture");this.addInput("G","Texture");this.addInput("B","Texture");this.addInput("A","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,R:1,G:1,B:1,A:1};this._color=vec4.create();this._uniforms={u_textureR:0,u_textureG:1,u_textureB:2,u_textureA:3,u_color:this._color}}function l(){this.addOutput("Texture","Texture");this._tex_color=vec4.create();this.properties={color:vec4.create(),precision:c.DEFAULT}}function a(){this.addInput("A","color");this.addInput("B","color"); +new Float32Array(4)}function g(){this.addInput("Texture","Texture");this.addOutput("min_t","Texture");this.addOutput("max_t","Texture");this.addOutput("min","vec4");this.addOutput("max","vec4");this.properties={mode:"max",use_previous_frame:!0};this._uniforms={u_texture:0};this._max=new Float32Array(4);this._min=new Float32Array(4);this._textures_chain=[]}function m(){this.addInput("in","Texture");this.addInput("factor","Number");this.addOutput("out","Texture");this.properties={factor:0.5};this._uniforms= +{u_texture:0,u_textureB:1,u_factor:this.properties.factor}}function s(){this.addInput("in","Texture");this.addOutput("avg","Texture");this.addOutput("array","Texture");this.properties={samples:64,frames_interval:1};this._uniforms={u_texture:0,u_textureB:1,u_samples:this.properties.samples,u_isamples:1/this.properties.samples};this.frame=0}function A(){this.addInput("Image","image");this.addOutput("","Texture");this.properties={}}function r(){this.addInput("Texture","Texture");this.addInput("LUT", +"Texture");this.addInput("Intensity","number");this.addOutput("","Texture");this.properties={enabled:!0,intensity:1,precision:c.DEFAULT,texture:null};r._shader||(r._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,r.pixel_shader))}function n(){this.addInput("Texture","Texture");this.addOutput("R","Texture");this.addOutput("G","Texture");this.addOutput("B","Texture");this.addOutput("A","Texture");n._shader||(n._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,n.pixel_shader))}function x(){this.addInput("R", +"Texture");this.addInput("G","Texture");this.addInput("B","Texture");this.addInput("A","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,R:1,G:1,B:1,A:1};this._color=vec4.create();this._uniforms={u_textureR:0,u_textureG:1,u_textureB:2,u_textureA:3,u_color:this._color}}function v(){this.addOutput("Texture","Texture");this._tex_color=vec4.create();this.properties={color:vec4.create(),precision:c.DEFAULT}}function a(){this.addInput("A","color");this.addInput("B","color"); this.addOutput("Texture","Texture");this.properties={angle:0,scale:1,A:[0,0,0],B:[1,1,1],texture_size:32};a._shader||(a._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,a.pixel_shader));this._uniforms={u_angle:0,u_colorA:vec3.create(),u_colorB:vec3.create()}}function b(){this.addInput("A","Texture");this.addInput("B","Texture");this.addInput("Mixer","Texture");this.addOutput("Texture","Texture");this.properties={factor:0.5,size_from_biggest:!0,invert:!1,precision:c.DEFAULT};this._uniforms={u_textureA:0, u_textureB:1,u_textureMix:2,u_mix:vec4.create()}}function d(){this.addInput("Tex.","Texture");this.addOutput("Edges","Texture");this.properties={invert:!0,threshold:!1,factor:1,precision:c.DEFAULT};d._shader||(d._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,d.pixel_shader))}function h(){this.addInput("Texture","Texture");this.addInput("Distance","number");this.addInput("Range","number");this.addOutput("Texture","Texture");this.properties={distance:100,range:50,only_depth:!1,high_precision:!1}; -this._uniforms={u_texture:0,u_distance:100,u_range:50,u_camera_planes:null}}function f(){this.addInput("Texture","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,invert:!1};this._uniforms={u_texture:0,u_near:0.1,u_far:1E4}}function K(){this.addInput("Texture","Texture");this.addInput("Iterations","number");this.addInput("Intensity","number");this.addOutput("Blurred","Texture");this.properties={intensity:1,iterations:1,preserve_aspect:!1,scale:[1,1],precision:c.DEFAULT}} -function r(){this.addInput("in","Texture");this.addInput("dirt","Texture");this.addOutput("out","Texture");this.addOutput("glow","Texture");this.properties={enabled:!0,intensity:1,persistence:0.99,iterations:16,threshold:0,scale:1,dirt_factor:0.5,precision:c.DEFAULT};this._textures=[];this._uniforms={u_intensity:1,u_texture:0,u_glow_texture:1,u_threshold:0,u_texel_size:vec2.create()}}function H(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={intensity:1, -radius:5}}function I(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={sigma:1.4,k:1.6,p:21.7,epsilon:79,phi:0.017}}function G(){this.addOutput("Webcam","Texture");this.properties={texture_name:"",facingMode:"user"};this.boxcolor="black";this.version=0}function E(){this.addInput("in","Texture");this.addInput("f","number");this.addOutput("out","Texture");this.properties={enabled:!0,factor:1,precision:c.LOW};this._uniforms={u_texture:0,u_factor:1}}function t(){this.addInput("in", +this._uniforms={u_texture:0,u_distance:100,u_range:50,u_camera_planes:null}}function f(){this.addInput("Texture","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,invert:!1};this._uniforms={u_texture:0,u_near:0.1,u_far:1E4}}function E(){this.addInput("Texture","Texture");this.addInput("Iterations","number");this.addInput("Intensity","number");this.addOutput("Blurred","Texture");this.properties={intensity:1,iterations:1,preserve_aspect:!1,scale:[1,1],precision:c.DEFAULT}} +function q(){this.addInput("in","Texture");this.addInput("dirt","Texture");this.addOutput("out","Texture");this.addOutput("glow","Texture");this.properties={enabled:!0,intensity:1,persistence:0.99,iterations:16,threshold:0,scale:1,dirt_factor:0.5,precision:c.DEFAULT};this._textures=[];this._uniforms={u_intensity:1,u_texture:0,u_glow_texture:1,u_threshold:0,u_texel_size:vec2.create()}}function I(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={intensity:1, +radius:5}}function J(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={sigma:1.4,k:1.6,p:21.7,epsilon:79,phi:0.017}}function H(){this.addOutput("Webcam","Texture");this.properties={texture_name:"",facingMode:"user"};this.boxcolor="black";this.version=0}function F(){this.addInput("in","Texture");this.addInput("f","number");this.addOutput("out","Texture");this.properties={enabled:!0,factor:1,precision:c.LOW};this._uniforms={u_texture:0,u_factor:1}}function t(){this.addInput("in", "Texture");this.addOutput("out","Texture");this.properties={precision:c.LOW,split_channels:!1};this._values=new Uint8Array(1024);this._values.fill(255);this._curve_texture=null;this._uniforms={u_texture:0,u_curve:1,u_range:1};this._must_update=!0;this._points={RGB:[[0,0],[1,1]],R:[[0,0],[1,1]],G:[[0,0],[1,1]],B:[[0,0],[1,1]]};this.curve_editor=null;this.addWidget("toggle","Split Channels",!1,"split_channels");this.addWidget("combo","Channel","RGB",{values:["RGB","R","G","B"]});this.curve_offset=68; -this.size=[240,160]}function N(){this.addInput("in","Texture");this.addInput("exp","number");this.addOutput("out","Texture");this.properties={exposition:1,precision:c.LOW};this._uniforms={u_texture:0,u_exposition:1}}function J(){this.addInput("in","Texture");this.addInput("avg","number,Texture");this.addOutput("out","Texture");this.properties={enabled:!0,scale:1,gamma:1,average_lum:1,lum_white:1,precision:c.LOW};this._uniforms={u_texture:0,u_lumwhite2:1,u_igamma:1,u_scale:1,u_average_lum:1}}function M(){this.addOutput("out", -"Texture");this.properties={width:512,height:512,seed:0,persistence:0.1,octaves:8,scale:1,offset:[0,0],amplitude:1,precision:c.DEFAULT};this._key=0;this._texture=null;this._uniforms={u_persistence:0.1,u_seed:0,u_offset:vec2.create(),u_scale:1,u_viewport:vec2.create()}}function L(){this.addInput("v");this.addOutput("out","Texture");this.properties={code:L.default_code,width:512,height:512,clear:!0,precision:c.DEFAULT,use_html_canvas:!1};this._temp_texture=this._func=null;this.compileCode()}function O(){this.addInput("in", -"Texture");this.addOutput("out","Texture");this.properties={key_color:vec3.fromValues(0,1,0),threshold:0.8,slope:0.2,precision:c.DEFAULT}}function P(){this.addInput("in","texture");this.addInput("yaw","number");this.addOutput("out","texture");this.properties={yaw:0}}var F=y.LiteGraph;y.LGraphTexture=null;"undefined"!=typeof GL&&(LGraphCanvas.link_type_colors.Texture="#987",y.LGraphTexture=c,c.title="Texture",c.desc="Texture",c.widgets_info={name:{widget:"texture"},filter:{widget:"checkbox"}},c.loadTextureCallback= -null,c.image_preview_size=256,c.PASS_THROUGH=1,c.COPY=2,c.LOW=3,c.HIGH=4,c.REUSE=5,c.DEFAULT=2,c.MODE_VALUES={"pass through":c.PASS_THROUGH,copy:c.COPY,low:c.LOW,high:c.HIGH,reuse:c.REUSE,"default":c.DEFAULT},c.getTexturesContainer=function(){return gl.textures},c.loadTexture=function(a,b){b=b||{};var d=a;"http://"==d.substr(0,7)&&F.proxy&&(d=F.proxy+d.substr(7));return c.getTexturesContainer()[a]=GL.Texture.fromURL(d,b)},c.getTexture=function(a){var b=this.getTexturesContainer();if(!b)throw"Cannot load texture, container of textures not found"; +this.size=[240,160]}function O(){this.addInput("in","Texture");this.addInput("exp","number");this.addOutput("out","Texture");this.properties={exposition:1,precision:c.LOW};this._uniforms={u_texture:0,u_exposition:1}}function K(){this.addInput("in","Texture");this.addInput("avg","number,Texture");this.addOutput("out","Texture");this.properties={enabled:!0,scale:1,gamma:1,average_lum:1,lum_white:1,precision:c.LOW};this._uniforms={u_texture:0,u_lumwhite2:1,u_igamma:1,u_scale:1,u_average_lum:1}}function N(){this.addOutput("out", +"Texture");this.properties={width:512,height:512,seed:0,persistence:0.1,octaves:8,scale:1,offset:[0,0],amplitude:1,precision:c.DEFAULT};this._key=0;this._texture=null;this._uniforms={u_persistence:0.1,u_seed:0,u_offset:vec2.create(),u_scale:1,u_viewport:vec2.create()}}function M(){this.addInput("v");this.addOutput("out","Texture");this.properties={code:M.default_code,width:512,height:512,clear:!0,precision:c.DEFAULT,use_html_canvas:!1};this._temp_texture=this._func=null;this.compileCode()}function P(){this.addInput("in", +"Texture");this.addOutput("out","Texture");this.properties={key_color:vec3.fromValues(0,1,0),threshold:0.8,slope:0.2,precision:c.DEFAULT}}function Q(){this.addInput("in","texture");this.addInput("yaw","number");this.addOutput("out","texture");this.properties={yaw:0}}var G=y.LiteGraph;y.LGraphTexture=null;"undefined"!=typeof GL&&(LGraphCanvas.link_type_colors.Texture="#987",y.LGraphTexture=c,c.title="Texture",c.desc="Texture",c.widgets_info={name:{widget:"texture"},filter:{widget:"checkbox"}},c.loadTextureCallback= +null,c.image_preview_size=256,c.PASS_THROUGH=1,c.COPY=2,c.LOW=3,c.HIGH=4,c.REUSE=5,c.DEFAULT=2,c.MODE_VALUES={"pass through":c.PASS_THROUGH,copy:c.COPY,low:c.LOW,high:c.HIGH,reuse:c.REUSE,"default":c.DEFAULT},c.getTexturesContainer=function(){return gl.textures},c.loadTexture=function(a,b){b=b||{};var d=a;"http://"==d.substr(0,7)&&G.proxy&&(d=G.proxy+d.substr(7));return c.getTexturesContainer()[a]=GL.Texture.fromURL(d,b)},c.getTexture=function(a){var b=this.getTexturesContainer();if(!b)throw"Cannot load texture, container of textures not found"; b=b[a];return!b&&a&&":"!=a[0]?this.loadTexture(a):b},c.getTargetTexture=function(a,b,d){if(!a)throw"LGraphTexture.getTargetTexture expects a reference texture";var f=null;switch(d){case c.LOW:f=gl.UNSIGNED_BYTE;break;case c.HIGH:f=gl.HIGH_PRECISION_FORMAT;break;case c.REUSE:return a;default:f=a?a.type:gl.UNSIGNED_BYTE}b&&b.width==a.width&&b.height==a.height&&b.type==f||(b=new GL.Texture(a.width,a.height,{type:f,format:gl.RGBA,filter:gl.LINEAR}));return b},c.getTextureType=function(a,b){var d=b?b.type: gl.UNSIGNED_BYTE;switch(a){case c.HIGH:d=gl.HIGH_PRECISION_FORMAT;break;case c.LOW:d=gl.UNSIGNED_BYTE}return d},c.getWhiteTexture=function(){return this._white_texture?this._white_texture:this._white_texture=GL.Texture.fromMemory(1,1,[255,255,255,255],{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.getNoiseTexture=function(){if(this._noise_texture)return this._noise_texture;for(var a=new Uint8Array(1048576),b=0;1048576>b;++b)a[b]=255*Math.random();return this._noise_texture=a=GL.Texture.fromMemory(512, 512,a,{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.prototype.onDropFile=function(a,b,d){if(a){var c=null;"string"==typeof a?c=GL.Texture.fromURL(a):-1!=b.toLowerCase().indexOf(".dds")?c=GL.Texture.fromDDSInMemory(a):(a=new Blob([d]),a=URL.createObjectURL(a),c=GL.Texture.fromURL(a));this._drop_texture=c;this.properties.name=b}else this._drop_texture=null,this.properties.name=""},c.prototype.getExtraMenuOptions=function(a){var b=this;if(this._drop_texture)return[{content:"Clear",callback:function(){b._drop_texture= @@ -433,273 +442,274 @@ null;b.properties.name=""}}]},c.prototype.onExecute=function(){var a=null;this.i if(d){var f=null;"width"==d.name?f=a.width:"height"==d.name?f=a.height:"aspect"==d.name&&(f=a.width/a.height);this.setOutputData(b,f)}}}else this.setOutputData(0,null),this.setOutputData(1,"")},c.prototype.onResourceRenamed=function(a,b){this.properties.name==a&&(this.properties.name=b)},c.prototype.onDrawBackground=function(a){if(!(this.flags.collapsed||20>=this.size[1]))if(this._drop_texture&&a.webgl)a.drawImage(this._drop_texture,0,0,this.size[0],this.size[1]);else{if(this._last_preview_tex!=this._last_tex)if(a.webgl)this._canvas= this._last_tex;else{var b=c.generateLowResTexturePreview(this._last_tex);if(!b)return;this._last_preview_tex=this._last_tex;this._canvas=cloneCanvas(b)}this._canvas&&(a.save(),a.webgl||(a.translate(0,this.size[1]),a.scale(1,-1)),a.drawImage(this._canvas,0,0,this.size[0],this.size[1]),a.restore())}},c.generateLowResTexturePreview=function(a){if(!a)return null;var b=c.image_preview_size,d=a;if(a.format==gl.DEPTH_COMPONENT)return null;if(a.width>b||a.height>b)d=this._preview_temp_tex,this._preview_temp_tex|| (this._preview_temp_tex=d=new GL.Texture(b,b,{minFilter:gl.NEAREST})),a.copyTo(d);a=this._preview_canvas;a||(this._preview_canvas=a=createCanvas(b,b));d&&d.toCanvas(a);return a},c.prototype.getResources=function(a){this.properties.name&&(a[this.properties.name]=GL.Texture);return a},c.prototype.onGetInputs=function(){return[["in","Texture"]]},c.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["aspect","number"]]},c.replaceCode=function(a,b){return a.replace(/\{\{[a-zA-Z0-9_]*\}\}/g, -function(a){a=a.replace(/[\{\}]/g,"");return b[a]||""})},F.registerNodeType("texture/texture",c),m.title="Preview",m.desc="Show a texture in the graph canvas",m.allow_preview=!1,m.prototype.onDrawBackground=function(a){if(!this.flags.collapsed&&(a.webgl||m.allow_preview)){var b=this.getInputData(0);if(b){var d=null,d=!b.handle&&a.webgl?b:c.generateLowResTexturePreview(b);a.save();this.properties.flipY&&(a.translate(0,this.size[1]),a.scale(1,-1));a.drawImage(d,0,0,this.size[0],this.size[1]);a.restore()}}}, -F.registerNodeType("texture/preview",m),q.title="Save",q.desc="Save a texture in the repository",q.prototype.getPreviewTexture=function(){return this._texture},q.prototype.onExecute=function(){var a=this.getInputData(0);a&&(this.properties.generate_mipmaps&&(a.bind(0),a.setParameter(gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_LINEAR),gl.generateMipmap(a.texture_type),a.unbind(0)),this.properties.name&&(c.storeTexture?c.storeTexture(this.properties.name,a):c.getTexturesContainer()[this.properties.name]= -a),this._texture=a,this.setOutputData(0,a),this.setOutputData(1,this.properties.name))},F.registerNodeType("texture/save",q),k.widgets_info={uvcode:{widget:"code"},pixelcode:{widget:"code"},precision:{widget:"combo",values:c.MODE_VALUES}},k.title="Operation",k.desc="Texture shader operation",k.presets={},k.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:b.properties.show?"Hide Texture":"Show Texture",callback:function(){b.properties.show=!b.properties.show}}]},k.prototype.onPropertyChanged= +function(a){a=a.replace(/[\{\}]/g,"");return b[a]||""})},G.registerNodeType("texture/texture",c),l.title="Preview",l.desc="Show a texture in the graph canvas",l.allow_preview=!1,l.prototype.onDrawBackground=function(a){if(!this.flags.collapsed&&(a.webgl||l.allow_preview)){var b=this.getInputData(0);if(b){var d=null,d=!b.handle&&a.webgl?b:c.generateLowResTexturePreview(b);a.save();this.properties.flipY&&(a.translate(0,this.size[1]),a.scale(1,-1));a.drawImage(d,0,0,this.size[0],this.size[1]);a.restore()}}}, +G.registerNodeType("texture/preview",l),p.title="Save",p.desc="Save a texture in the repository",p.prototype.getPreviewTexture=function(){return this._texture},p.prototype.onExecute=function(){var a=this.getInputData(0);a&&(this.properties.generate_mipmaps&&(a.bind(0),a.setParameter(gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_LINEAR),gl.generateMipmap(a.texture_type),a.unbind(0)),this.properties.name&&(c.storeTexture?c.storeTexture(this.properties.name,a):c.getTexturesContainer()[this.properties.name]= +a),this._texture=a,this.setOutputData(0,a),this.setOutputData(1,this.properties.name))},G.registerNodeType("texture/save",p),k.widgets_info={uvcode:{widget:"code"},pixelcode:{widget:"code"},precision:{widget:"combo",values:c.MODE_VALUES}},k.title="Operation",k.desc="Texture shader operation",k.presets={},k.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:b.properties.show?"Hide Texture":"Show Texture",callback:function(){b.properties.show=!b.properties.show}}]},k.prototype.onPropertyChanged= function(){this.has_error=!1},k.prototype.onDrawBackground=function(a){this.flags.collapsed||20>=this.size[1]||!this.properties.show||!this._tex||this._tex.gl!=a||(a.save(),a.drawImage(this._tex,0,0,this.size[0],this.size[1]),a.restore())},k.prototype.onExecute=function(){var a=this.getInputData(0);if(this.isOutputConnected(0))if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else{var b=this.getInputData(1);if(this.properties.uvcode||this.properties.pixelcode){var d=512,f=512; a?(d=a.width,f=a.height):b&&(d=b.width,f=b.height);b||(b=GL.Texture.getWhiteTexture());var h=c.getTextureType(this.properties.precision,a);this._tex=a||this._tex?c.getTargetTexture(a||this._tex,this._tex,this.properties.precision):new GL.Texture(d,f,{type:h,format:gl.RGBA,filter:gl.LINEAR});h="";this.properties.uvcode&&(h="uv = "+this.properties.uvcode,-1!=this.properties.uvcode.indexOf(";")&&(h=this.properties.uvcode));var e="";this.properties.pixelcode&&(e="result = "+this.properties.pixelcode, --1!=this.properties.pixelcode.indexOf(";")&&(e=this.properties.pixelcode));var g=this._shader;if(!(this.has_error||g&&this._shader_code==h+"|"+e)){var l=c.replaceCode(k.pixel_shader,{UV_CODE:h,PIXEL_CODE:e});try{g=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,l),this.boxcolor="#00FF00"}catch(n){GL.Shader.dumpErrorToConsole(n,Shader.SCREEN_VERTEX_SHADER,l);this.boxcolor="#FF0000";this.has_error=!0;return}this._shader=g;this._shader_code=h+"|"+e}if(this._shader){var r=this.getInputData(2);null!=r?this.properties.value= -r:r=parseFloat(this.properties.value);var v=this.graph.getTime();this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);gl.disable(gl.BLEND);a&&a.bind(0);b&&b.bind(1);var c=Mesh.getScreenQuad();g.uniforms({u_texture:0,u_textureB:1,value:r,texSize:[d,f],time:v}).draw(c)});this.setOutputData(0,this._tex)}}}},k.pixel_shader="precision highp float;\n\t\t\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec2 texSize;\n\t\tuniform float time;\n\t\tuniform float value;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord;\n\t\t\t{{UV_CODE}};\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\tvec3 color = color4.rgb;\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\tvec3 colorB = color4B.rgb;\n\t\t\tvec3 result = color;\n\t\t\tfloat alpha = 1.0;\n\t\t\t{{PIXEL_CODE}};\n\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t}\n\t\t", +-1!=this.properties.pixelcode.indexOf(";")&&(e=this.properties.pixelcode));var g=this._shader;if(!(this.has_error||g&&this._shader_code==h+"|"+e)){var n=c.replaceCode(k.pixel_shader,{UV_CODE:h,PIXEL_CODE:e});try{g=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,n),this.boxcolor="#00FF00"}catch(r){GL.Shader.dumpErrorToConsole(r,Shader.SCREEN_VERTEX_SHADER,n);this.boxcolor="#FF0000";this.has_error=!0;return}this._shader=g;this._shader_code=h+"|"+e}if(this._shader){var q=this.getInputData(2);null!=q?this.properties.value= +q:q=parseFloat(this.properties.value);var v=this.graph.getTime();this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);gl.disable(gl.BLEND);a&&a.bind(0);b&&b.bind(1);var c=Mesh.getScreenQuad();g.uniforms({u_texture:0,u_textureB:1,value:q,texSize:[d,f],time:v}).draw(c)});this.setOutputData(0,this._tex)}}}},k.pixel_shader="precision highp float;\n\t\t\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec2 texSize;\n\t\tuniform float time;\n\t\tuniform float value;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord;\n\t\t\t{{UV_CODE}};\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\tvec3 color = color4.rgb;\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\tvec3 colorB = color4B.rgb;\n\t\t\tvec3 result = color;\n\t\t\tfloat alpha = 1.0;\n\t\t\t{{PIXEL_CODE}};\n\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t}\n\t\t", k.registerPreset=function(a,b){k.presets[a]=b},k.registerPreset("",""),k.registerPreset("bypass","color"),k.registerPreset("add","color + colorB * value"),k.registerPreset("substract","(color - colorB) * value"),k.registerPreset("mate","mix( color, colorB, color4B.a * value)"),k.registerPreset("invert","vec3(1.0) - color"),k.registerPreset("multiply","color * colorB * value"),k.registerPreset("divide","(color / colorB) / value"),k.registerPreset("difference","abs(color - colorB) * value"),k.registerPreset("max", "max(color, colorB) * value"),k.registerPreset("min","min(color, colorB) * value"),k.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"),k.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"),k.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"),k.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"), -k.prototype.onInspect=function(a){var b=this;a.addCombo("Presets","",{values:Object.keys(k.presets),callback:function(d){var c=k.presets[d];c&&(b.setProperty("pixelcode",c),b.title=d,a.refresh())}})},F.registerNodeType("texture/operation",k),w.title="Shader",w.desc="Texture shader",w.widgets_info={code:{type:"code",lang:"glsl"},precision:{widget:"combo",values:c.MODE_VALUES}},w.prototype.onPropertyChanged=function(a,b){if("code"==a){var d=this.getShader();if(d){var c=d.uniformInfo;if(this.inputs)for(var f= +k.prototype.onInspect=function(a){var b=this;a.addCombo("Presets","",{values:Object.keys(k.presets),callback:function(d){var c=k.presets[d];c&&(b.setProperty("pixelcode",c),b.title=d,a.refresh())}})},G.registerNodeType("texture/operation",k),w.title="Shader",w.desc="Texture shader",w.widgets_info={code:{type:"code",lang:"glsl"},precision:{widget:"combo",values:c.MODE_VALUES}},w.prototype.onPropertyChanged=function(a,b){if("code"==a){var d=this.getShader();if(d){var c=d.uniformInfo;if(this.inputs)for(var f= {},h=0;h lumaMax))\n\t\t\t\tcolor = vec4(rgbA, 1.0);\n\t\t\telse\n\t\t\t\tcolor = vec4(rgbB, 1.0);\n\t\t\tif(u_igamma != 1.0)\n\t\t\t\tcolor.xyz = pow( color.xyz, vec3(u_igamma) );\n\t\t\treturn color;\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\t\t}\n\t\t", -z.gamma_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_igamma;\n\t\tvoid main() {\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\t\t gl_FragColor = color;\n\t\t}\n\t\t",F.registerNodeType("texture/toviewport",z),u.title="Copy",u.desc="Copy Texture",u.widgets_info={size:{widget:"combo",values:[0,32,64,128,256,512,1024,2048]},precision:{widget:"combo", +z.gamma_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_igamma;\n\t\tvoid main() {\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\t\t gl_FragColor = color;\n\t\t}\n\t\t",G.registerNodeType("texture/toviewport",z),u.title="Copy",u.desc="Copy Texture",u.widgets_info={size:{widget:"combo",values:[0,32,64,128,256,512,1024,2048]},precision:{widget:"combo", values:c.MODE_VALUES}},u.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)){if(a){var b=a.width,d=a.height;0!=this.properties.size&&(d=b=this.properties.size);var f=this._temp_texture,h=a.type;this.properties.precision===c.LOW?h=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(h=gl.HIGH_PRECISION_FORMAT);f&&f.width==b&&f.height==d&&f.type==h||(f=gl.LINEAR,this.properties.generate_mipmaps&&isPowerOfTwo(b)&&isPowerOfTwo(d)&&(f=gl.LINEAR_MIPMAP_LINEAR), -this._temp_texture=new GL.Texture(b,d,{type:h,format:gl.RGBA,minFilter:f,magFilter:gl.LINEAR}));a.copyTo(this._temp_texture);this.properties.generate_mipmaps&&(this._temp_texture.bind(0),gl.generateMipmap(this._temp_texture.texture_type),this._temp_texture.unbind(0))}this.setOutputData(0,this._temp_texture)}},F.registerNodeType("texture/copy",u),D.title="Downsample",D.desc="Downsample Texture",D.widgets_info={iterations:{type:"number",step:1,precision:0,min:0},precision:{widget:"combo",values:c.MODE_VALUES}}, -D.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)&&a&&a.texture_type===GL.TEXTURE_2D)if(1>this.properties.iterations)this.setOutputData(0,a);else{var b=D._shader;b||(D._shader=b=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,D.pixel_shader));var d=a.width|0,f=a.height|0,h=a.type;this.properties.precision===c.LOW?h=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(h=gl.HIGH_PRECISION_FORMAT);var e=this.properties.iterations||1,g=a,l= -null,n=[],a={type:h,format:a.format},h=vec2.create(),r={u_offset:h};this._texture&&GL.Texture.releaseTemporary(this._texture);for(var k=0;k>1||0;f=f>>1||0;l=GL.Texture.getTemporary(d,f,a);n.push(l);g.setParameter(GL.TEXTURE_MAG_FILTER,GL.NEAREST);g.copyTo(l,b,r);if(1==d&&1==f)break;g=l}this._texture=n.pop();for(k=0;kthis.properties.iterations)this.setOutputData(0,a);else{var b=D._shader;b||(D._shader=b=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,D.pixel_shader));var d=a.width|0,f=a.height|0,h=a.type;this.properties.precision===c.LOW?h=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(h=gl.HIGH_PRECISION_FORMAT);var e=this.properties.iterations||1,g=a,n= +null,r=[],a={type:h,format:a.format},h=vec2.create(),k={u_offset:h};this._texture&&GL.Texture.releaseTemporary(this._texture);for(var q=0;q>1||0;f=f>>1||0;n=GL.Texture.getTemporary(d,f,a);r.push(n);g.setParameter(GL.TEXTURE_MAG_FILTER,GL.NEAREST);g.copyTo(n,b,k);if(1==d&&1==f)break;g=n}this._texture=r.pop();for(q=0;q>=2,c++,1!=d););a.copyTo(this._textures_chain[0]);for(var c=1;c<=this._textures_chain.length;++c)a=this._textures_chain[c];var f=g._shader,h=this._uniforms;h.u_mipmap_offset=this.properties.mipmap_offset;gl.disable(gl.DEPTH_TEST); gl.disable(gl.BLEND);this._temp_texture.drawTo(function(){a.toViewport(f,h)})}},g.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform mat4 u_samples_a;\n\t\tuniform mat4 u_samples_b;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_mipmap_offset;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t//random average\n\t\t\tfor(int i = 0; i < 4; ++i)\n\t\t\t\tfor(int j = 0; j < 4; ++j)\n\t\t\t\t{\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t}\n\t\t gl_FragColor = color * 0.03125;\n\t\t}\n\t\t", -p.title="Smooth",p.desc="Smooth texture over time",p.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){p._shader||(p._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,p.pixel_shader));var b=this._temp_texture;b&&b.type==a.type&&b.width==a.width&&b.height==a.height||(b={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(a.width,a.height,b),this._temp_texture2=new GL.Texture(a.width,a.height,b),a.copyTo(this._temp_texture2));var b= -this._temp_texture,d=this._temp_texture2,c=p._shader,f=this._uniforms;f.u_factor=1-this.getInputOrProperty("factor");gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);b.drawTo(function(){d.bind(1);a.toViewport(c,f)});this.setOutputData(0,b);this._temp_texture=d;this._temp_texture2=b}},p.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_factor;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tgl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\t\t}\n\t\t", -F.registerNodeType("texture/temporal_smooth",p),s.title="Lineal Avg Smooth",s.desc="Smooth texture linearly over time",s["@samples"]={type:"number",min:1,max:64,step:1,precision:1},s.prototype.getPreviewTexture=function(){return this._temp_texture2},s.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){s._shader||(s._shader_copy=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,s.pixel_shader_copy),s._shader_avg=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,s.pixel_shader_avg)); -var b=Math.clamp(this.properties.samples,0,64),d=this.frame,c=this.properties.frames_interval;if(0==c||0==d%c){d=this._temp_texture;d&&d.type==a.type&&d.width==b||(d={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(b,1,d),this._temp_texture2=new GL.Texture(b,1,d),this._temp_texture_out=new GL.Texture(1,1,d));var f=this._temp_texture,h=this._temp_texture2,e=s._shader_copy,g=s._shader_avg,l=this._uniforms;l.u_samples=b;l.u_isamples=1/b;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST); -f.drawTo(function(){h.bind(1);a.toViewport(e,l)});this._temp_texture_out.drawTo(function(){f.toViewport(g,l)});this.setOutputData(0,this._temp_texture_out);this._temp_texture=h;this._temp_texture2=f}else this.setOutputData(0,this._temp_texture_out);this.setOutputData(1,this._temp_texture2);this.frame++}},s.pixel_shader_copy="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tif( v_coord.x <= u_isamples )\n\t\t\t\tgl_FragColor = texture2D( u_texture, vec2(0.5) );\n\t\t\telse\n\t\t\t\tgl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\t\t}\n\t\t", -s.pixel_shader_avg="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform int u_samples;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\tfor(int i = 0; i < 64; ++i)\n\t\t\t{\n\t\t\t\tcolor += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\t\t\t\tif(i == (u_samples - 1))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tgl_FragColor = color * u_isamples;\n\t\t}\n\t\t",F.registerNodeType("texture/linear_avg_smooth", +m.title="Smooth",m.desc="Smooth texture over time",m.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){m._shader||(m._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,m.pixel_shader));var b=this._temp_texture;b&&b.type==a.type&&b.width==a.width&&b.height==a.height||(b={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(a.width,a.height,b),this._temp_texture2=new GL.Texture(a.width,a.height,b),a.copyTo(this._temp_texture2));var b= +this._temp_texture,d=this._temp_texture2,c=m._shader,f=this._uniforms;f.u_factor=1-this.getInputOrProperty("factor");gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);b.drawTo(function(){d.bind(1);a.toViewport(c,f)});this.setOutputData(0,b);this._temp_texture=d;this._temp_texture2=b}},m.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_factor;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tgl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\t\t}\n\t\t", +G.registerNodeType("texture/temporal_smooth",m),s.title="Lineal Avg Smooth",s.desc="Smooth texture linearly over time",s["@samples"]={type:"number",min:1,max:64,step:1,precision:1},s.prototype.getPreviewTexture=function(){return this._temp_texture2},s.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){s._shader||(s._shader_copy=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,s.pixel_shader_copy),s._shader_avg=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,s.pixel_shader_avg)); +var b=Math.clamp(this.properties.samples,0,64),d=this.frame,c=this.properties.frames_interval;if(0==c||0==d%c){d=this._temp_texture;d&&d.type==a.type&&d.width==b||(d={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(b,1,d),this._temp_texture2=new GL.Texture(b,1,d),this._temp_texture_out=new GL.Texture(1,1,d));var f=this._temp_texture,h=this._temp_texture2,e=s._shader_copy,g=s._shader_avg,n=this._uniforms;n.u_samples=b;n.u_isamples=1/b;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST); +f.drawTo(function(){h.bind(1);a.toViewport(e,n)});this._temp_texture_out.drawTo(function(){f.toViewport(g,n)});this.setOutputData(0,this._temp_texture_out);this._temp_texture=h;this._temp_texture2=f}else this.setOutputData(0,this._temp_texture_out);this.setOutputData(1,this._temp_texture2);this.frame++}},s.pixel_shader_copy="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tif( v_coord.x <= u_isamples )\n\t\t\t\tgl_FragColor = texture2D( u_texture, vec2(0.5) );\n\t\t\telse\n\t\t\t\tgl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\t\t}\n\t\t", +s.pixel_shader_avg="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform int u_samples;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\tfor(int i = 0; i < 64; ++i)\n\t\t\t{\n\t\t\t\tcolor += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\t\t\t\tif(i == (u_samples - 1))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tgl_FragColor = color * u_isamples;\n\t\t}\n\t\t",G.registerNodeType("texture/linear_avg_smooth", s),A.title="Image to Texture",A.desc="Uploads an image to the GPU",A.prototype.onExecute=function(){var a=this.getInputData(0);if(a){var b=a.videoWidth||a.width,d=a.videoHeight||a.height;if(a.gltexture)this.setOutputData(0,a.gltexture);else{var c=this._temp_texture;c&&c.width==b&&c.height==d||(this._temp_texture=new GL.Texture(b,d,{format:gl.RGBA,filter:gl.LINEAR}));try{this._temp_texture.uploadImage(a)}catch(f){console.error("image comes from an unsafe location, cannot be uploaded to webgl: "+f); -return}this.setOutputData(0,this._temp_texture)}}},F.registerNodeType("texture/imageToTexture",A),n.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},n.title="LUT",n.desc="Apply LUT to Texture",n.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture)); -if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.bindTexture(gl.TEXTURE_2D,null);var d=this.properties.intensity;this.isInputConnected(2)&&(this.properties.intensity=d=this.getInputData(2));this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);this._tex.drawTo(function(){b.bind(1);a.toViewport(n._shader,{u_texture:0,u_textureB:1, -u_amount:d})});this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}},n.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_amount;\n\t\t\n\t\tvoid main() {\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\t\t\t mediump vec2 quad1;\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\t\t\t mediump vec2 quad2;\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\t\t\t highp vec2 texPos1;\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t highp vec2 texPos2;\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\t\t}\n\t\t", -F.registerNodeType("texture/LUT",n),v.title="Texture to Channels",v.desc="Split texture channels",v.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this._channels||(this._channels=Array(4));for(var b=gl.RGB,d=0,c=0;4>c;c++)this.isOutputConnected(c)?(this._channels[c]&&this._channels[c].width==a.width&&this._channels[c].height==a.height&&this._channels[c].type==a.type&&this._channels[c].format==b||(this._channels[c]=new GL.Texture(a.width,a.height,{type:a.type,format:b,filter:gl.LINEAR})), -d++):this._channels[c]=null;if(d){gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);for(var f=Mesh.getScreenQuad(),h=v._shader,e=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],c=0;4>c;c++)this._channels[c]&&(this._channels[c].drawTo(function(){a.bind(0);h.uniforms({u_texture:0,u_mask:e[c]}).draw(f)}),this.setOutputData(c,this._channels[c]))}}},v.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec4 u_mask;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\t\t}\n\t\t", -F.registerNodeType("texture/textureChannels",v),x.title="Channels to Texture",x.desc="Split texture channels",x.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},x.prototype.onExecute=function(){var a=c.getWhiteTexture(),b=this.getInputData(0)||a,d=this.getInputData(1)||a,f=this.getInputData(2)||a,h=this.getInputData(3)||a;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad();x._shader||(x._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,x.pixel_shader));var g=x._shader, -a=Math.max(b.width,d.width,f.width,h.width),l=Math.max(b.height,d.height,f.height,h.height),n=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._texture&&this._texture.width==a&&this._texture.height==l&&this._texture.type==n||(this._texture=new GL.Texture(a,l,{type:n,format:gl.RGBA,filter:gl.LINEAR}));a=this._color;a[0]=this.properties.R;a[1]=this.properties.G;a[2]=this.properties.B;a[3]=this.properties.A;var r=this._uniforms;this._texture.drawTo(function(){b.bind(0); -d.bind(1);f.bind(2);h.bind(3);g.uniforms(r).draw(e)});this.setOutputData(0,this._texture)},x.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureR;\n\t\tuniform sampler2D u_textureG;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform vec4 u_color;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = u_color * vec4( \t\t\t\t\ttexture2D(u_textureR, v_coord).r,\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\t\t}\n\t\t", -F.registerNodeType("texture/channelsTexture",x),l.title="Color",l.desc="Generates a 1x1 texture with a constant color",l.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},l.prototype.onDrawBackground=function(a){var b=this.properties.color;a.fillStyle="rgb("+Math.floor(255*Math.clamp(b[0],0,1))+","+Math.floor(255*Math.clamp(b[1],0,1))+","+Math.floor(255*Math.clamp(b[2],0,1))+")";this.flags.collapsed?this.boxcolor=a.fillStyle:a.fillRect(0,0,this.size[0],this.size[1])},l.prototype.onExecute= +return}this.setOutputData(0,this._temp_texture)}}},G.registerNodeType("texture/imageToTexture",A),r.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},r.title="LUT",r.desc="Apply LUT to Texture",r.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture)); +if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.bindTexture(gl.TEXTURE_2D,null);var d=this.properties.intensity;this.isInputConnected(2)&&(this.properties.intensity=d=this.getInputData(2));this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);this._tex.drawTo(function(){b.bind(1);a.toViewport(r._shader,{u_texture:0,u_textureB:1, +u_amount:d})});this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}},r.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_amount;\n\t\t\n\t\tvoid main() {\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\t\t\t mediump vec2 quad1;\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\t\t\t mediump vec2 quad2;\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\t\t\t highp vec2 texPos1;\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t highp vec2 texPos2;\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\t\t}\n\t\t", +G.registerNodeType("texture/LUT",r),n.title="Texture to Channels",n.desc="Split texture channels",n.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this._channels||(this._channels=Array(4));for(var b=gl.RGB,d=0,c=0;4>c;c++)this.isOutputConnected(c)?(this._channels[c]&&this._channels[c].width==a.width&&this._channels[c].height==a.height&&this._channels[c].type==a.type&&this._channels[c].format==b||(this._channels[c]=new GL.Texture(a.width,a.height,{type:a.type,format:b,filter:gl.LINEAR})), +d++):this._channels[c]=null;if(d){gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);for(var f=Mesh.getScreenQuad(),h=n._shader,e=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],c=0;4>c;c++)this._channels[c]&&(this._channels[c].drawTo(function(){a.bind(0);h.uniforms({u_texture:0,u_mask:e[c]}).draw(f)}),this.setOutputData(c,this._channels[c]))}}},n.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec4 u_mask;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\t\t}\n\t\t", +G.registerNodeType("texture/textureChannels",n),x.title="Channels to Texture",x.desc="Split texture channels",x.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},x.prototype.onExecute=function(){var a=c.getWhiteTexture(),b=this.getInputData(0)||a,d=this.getInputData(1)||a,f=this.getInputData(2)||a,h=this.getInputData(3)||a;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad();x._shader||(x._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,x.pixel_shader));var g=x._shader, +a=Math.max(b.width,d.width,f.width,h.width),n=Math.max(b.height,d.height,f.height,h.height),r=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._texture&&this._texture.width==a&&this._texture.height==n&&this._texture.type==r||(this._texture=new GL.Texture(a,n,{type:r,format:gl.RGBA,filter:gl.LINEAR}));a=this._color;a[0]=this.properties.R;a[1]=this.properties.G;a[2]=this.properties.B;a[3]=this.properties.A;var q=this._uniforms;this._texture.drawTo(function(){b.bind(0); +d.bind(1);f.bind(2);h.bind(3);g.uniforms(q).draw(e)});this.setOutputData(0,this._texture)},x.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureR;\n\t\tuniform sampler2D u_textureG;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform vec4 u_color;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = u_color * vec4( \t\t\t\t\ttexture2D(u_textureR, v_coord).r,\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\t\t}\n\t\t", +G.registerNodeType("texture/channelsTexture",x),v.title="Color",v.desc="Generates a 1x1 texture with a constant color",v.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},v.prototype.onDrawBackground=function(a){var b=this.properties.color;a.fillStyle="rgb("+Math.floor(255*Math.clamp(b[0],0,1))+","+Math.floor(255*Math.clamp(b[1],0,1))+","+Math.floor(255*Math.clamp(b[2],0,1))+")";this.flags.collapsed?this.boxcolor=a.fillStyle:a.fillRect(0,0,this.size[0],this.size[1])},v.prototype.onExecute= function(){var a=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._tex&&this._tex.type==a||(this._tex=new GL.Texture(1,1,{format:gl.RGBA,type:a,minFilter:gl.NEAREST}));a=this.properties.color;if(this.inputs)for(var b=0;ba.width?d: -a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad(),g=null,l=this._uniforms;f?(g=b._shader_tex,g||(g=b._shader_tex=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader,{MIX_TEX:""}))):(g=b._shader_factor,g||(g=b._shader_factor=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader)),h=null==h?this.properties.factor:h,l.u_mix.set([h,h,h,h]));var n=this.properties.invert;this._tex.drawTo(function(){a.bind(n?1:0);d.bind(n?0:1);f&&f.bind(2); -g.uniforms(l).draw(e)});this.setOutputData(0,this._tex)}}},b.prototype.onGetInputs=function(){return[["factor","number"]]},b.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform sampler2D u_textureB;\n\t\t#ifdef MIX_TEX\n\t\t\tuniform sampler2D u_textureMix;\n\t\t#else\n\t\t\tuniform vec4 u_mix;\n\t\t#endif\n\t\t\n\t\tvoid main() {\n\t\t\t#ifdef MIX_TEX\n\t\t\t vec4 f = texture2D(u_textureMix, v_coord);\n\t\t\t#else\n\t\t\t vec4 f = u_mix;\n\t\t\t#endif\n\t\t gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\t\t}\n\t\t", -F.registerNodeType("texture/mix",b),d.title="Edges",d.desc="Detects edges",d.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},d.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else if(a){this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var b=Mesh.getScreenQuad(),f=d._shader,h=this.properties.invert,e=this.properties.factor, +a)&&(this._tex_color.set(a),this._tex.fill(a));this.setOutputData(0,this._tex)},v.prototype.onGetInputs=function(){return[["RGB","vec3"],["RGBA","vec4"],["R","number"],["G","number"],["B","number"],["A","number"]]},G.registerNodeType("texture/color",v),a.title="Gradient",a.desc="Generates a gradient",a["@A"]={type:"color"},a["@B"]={type:"color"},a["@texture_size"]={type:"enum",values:[32,64,128,256,512]},a.prototype.onExecute=function(){gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var b=GL.Mesh.getScreenQuad(), +d=a._shader,c=this.getInputData(0);c||(c=this.properties.A);var f=this.getInputData(1);f||(f=this.properties.B);for(var h=2;ha.width?d: +a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad(),g=null,n=this._uniforms;f?(g=b._shader_tex,g||(g=b._shader_tex=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader,{MIX_TEX:""}))):(g=b._shader_factor,g||(g=b._shader_factor=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader)),h=null==h?this.properties.factor:h,n.u_mix.set([h,h,h,h]));var r=this.properties.invert;this._tex.drawTo(function(){a.bind(r?1:0);d.bind(r?0:1);f&&f.bind(2); +g.uniforms(n).draw(e)});this.setOutputData(0,this._tex)}}},b.prototype.onGetInputs=function(){return[["factor","number"]]},b.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform sampler2D u_textureB;\n\t\t#ifdef MIX_TEX\n\t\t\tuniform sampler2D u_textureMix;\n\t\t#else\n\t\t\tuniform vec4 u_mix;\n\t\t#endif\n\t\t\n\t\tvoid main() {\n\t\t\t#ifdef MIX_TEX\n\t\t\t vec4 f = texture2D(u_textureMix, v_coord);\n\t\t\t#else\n\t\t\t vec4 f = u_mix;\n\t\t\t#endif\n\t\t gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\t\t}\n\t\t", +G.registerNodeType("texture/mix",b),d.title="Edges",d.desc="Detects edges",d.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},d.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else if(a){this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var b=Mesh.getScreenQuad(),f=d._shader,h=this.properties.invert,e=this.properties.factor, g=this.properties.threshold?1:0;this._tex.drawTo(function(){a.bind(0);f.uniforms({u_texture:0,u_isize:[1/a.width,1/a.height],u_factor:e,u_threshold:g,u_invert:h?1:0}).draw(b)});this.setOutputData(0,this._tex)}}},d.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_isize;\n\t\tuniform int u_invert;\n\t\tuniform float u_factor;\n\t\tuniform float u_threshold;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 center = texture2D(u_texture, v_coord);\n\t\t\tvec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\t\t\tvec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\t\t\tvec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\t\t\tvec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\t\t\tvec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\t\t\tdiff *= u_factor;\n\t\t\tif(u_invert == 1)\n\t\t\t\tdiff.xyz = vec3(1.0) - diff.xyz;\n\t\t\tif( u_threshold == 0.0 )\n\t\t\t\tgl_FragColor = vec4( diff.xyz, center.a );\n\t\t\telse\n\t\t\t\tgl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\t\t}\n\t\t", -F.registerNodeType("texture/edges",d),h.title="Depth Range",h.desc="Generates a texture with a depth range",h.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a){var b=gl.UNSIGNED_BYTE;this.properties.high_precision&&(b=gl.half_float_ext?gl.HALF_FLOAT_OES:gl.FLOAT);this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&&this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGBA, +G.registerNodeType("texture/edges",d),h.title="Depth Range",h.desc="Generates a texture with a depth range",h.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a){var b=gl.UNSIGNED_BYTE;this.properties.high_precision&&(b=gl.half_float_ext?gl.HALF_FLOAT_OES:gl.FLOAT);this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&&this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGBA, filter:gl.LINEAR}));var d=this._uniforms,b=this.properties.distance;this.isInputConnected(1)&&(b=this.getInputData(1),this.properties.distance=b);var c=this.properties.range;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.range=c);d.u_distance=b;d.u_range=c;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var f=Mesh.getScreenQuad();h._shader||(h._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,h.pixel_shader),h._shader_onlydepth=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,h.pixel_shader, {ONLY_DEPTH:""}));var e=this.properties.only_depth?h._shader_onlydepth:h._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1,1E3];d.u_camera_planes=b;this._temp_texture.drawTo(function(){a.bind(0);e.uniforms(d).draw(f)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},h.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform float u_distance;\n\t\tuniform float u_range;\n\t\t\n\t\tfloat LinearDepth()\n\t\t{\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\t\t\tdepth = depth * 2.0 - 1.0;\n\t\t\treturn zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t\tfloat depth = LinearDepth();\n\t\t\t#ifdef ONLY_DEPTH\n\t\t\t gl_FragColor = vec4(depth);\n\t\t\t#else\n\t\t\t\tfloat diff = abs(depth * u_camera_planes.y - u_distance);\n\t\t\t\tfloat dof = 1.0;\n\t\t\t\tif(diff <= u_range)\n\t\t\t\t\tdof = diff / u_range;\n\t\t\t gl_FragColor = vec4(dof);\n\t\t\t#endif\n\t\t}\n\t\t", -F.registerNodeType("texture/depth_range",h),f.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},f.title="Linear Depth",f.desc="Creates a color texture with linear depth",f.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a&&(a.format==gl.DEPTH_COMPONENT||a.format==gl.DEPTH_STENCIL)){var b=this.properties.precision==c.HIGH?gl.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&& +G.registerNodeType("texture/depth_range",h),f.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},f.title="Linear Depth",f.desc="Creates a color texture with linear depth",f.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a&&(a.format==gl.DEPTH_COMPONENT||a.format==gl.DEPTH_STENCIL)){var b=this.properties.precision==c.HIGH?gl.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&& this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGB,filter:gl.LINEAR}));var d=this._uniforms;d.u_near=a.near_far_planes[0];d.u_far=a.near_far_planes[1];d.u_invert=this.properties.invert?1:0;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var h=Mesh.getScreenQuad();f._shader||(f._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,f.pixel_shader));var e=f._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera? LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1,1E3];d.u_camera_planes=b;this._temp_texture.drawTo(function(){a.bind(0);e.uniforms(d).draw(h)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},f.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_near;\n\t\tuniform float u_far;\n\t\tuniform int u_invert;\n\t\t\n\t\tvoid main() {\n\t\t\tfloat zNear = u_near;\n\t\t\tfloat zFar = u_far;\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\t\t\tdepth = depth * 2.0 - 1.0;\n\t\t\tfloat f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t\tif( u_invert == 1 )\n\t\t\t\tf = 1.0 - f;\n\t\t\tgl_FragColor = vec4(vec3(f),1.0);\n\t\t}\n\t\t", -F.registerNodeType("texture/linear_depth",f),K.title="Blur",K.desc="Blur a texture",K.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},K.max_iterations=20,K.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._final_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(b=this._final_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));var d=this.properties.iterations;this.isInputConnected(1)&& -(d=this.getInputData(1),this.properties.iterations=d);d=Math.min(Math.floor(d),K.max_iterations);if(0==d)this.setOutputData(0,a);else{var c=this.properties.intensity;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.intensity=c);var f=F.camera_aspect;f||void 0===window.gl||(f=gl.canvas.height/gl.canvas.width);f||(f=1);var f=this.properties.preserve_aspect?f:1,h=this.properties.scale||[1,1];a.applyBlur(f*h[0],h[1],c,b);for(a=1;a>=1;1<(d|0)&&(d>>=1);if(2>b)break;n=g[q]=GL.Texture.getTemporary(b,d,f);m[0]=1/k.width;m[1]=1/k.height;k.blit(n,l.uniforms(e));k=n}this.isOutputConnected(2)&&(b=this._average_texture,b&&b.type==a.type&&b.format==a.format||(b=this._average_texture=new GL.Texture(1,1,{type:a.type,format:a.format,filter:gl.LINEAR})),m[0]=1/k.width,m[1]=1/k.height,e.u_intensity=p,e.u_delta=1,k.blit(b,l.uniforms(e)),this.setOutputData(2,b));gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE);e.u_intensity= -this.getInputOrProperty("persistence");e.u_delta=0.5;for(q-=2;0<=q;q--)n=g[q],g[q]=null,m[0]=1/k.width,m[1]=1/k.height,k.blit(n,l.uniforms(e)),GL.Texture.releaseTemporary(k),k=n;gl.disable(gl.BLEND);this.isOutputConnected(1)&&(g=this._glow_texture,g&&g.width==a.width&&g.height==a.height&&g.type==h&&g.format==a.format||(g=this._glow_texture=new GL.Texture(a.width,a.height,{type:h,format:a.format,filter:gl.LINEAR})),k.blit(g),this.setOutputData(1,g));if(this.isOutputConnected(0)){g=this._final_texture; -g&&g.width==a.width&&g.height==a.height&&g.type==h&&g.format==a.format||(g=this._final_texture=new GL.Texture(a.width,a.height,{type:h,format:a.format,filter:gl.LINEAR}));var s=this.getInputData(1),t=this.getInputOrProperty("dirt_factor");e.u_intensity=p;l=s?r._dirt_final_shader:r._final_shader;l||(l=s?r._dirt_final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,r.final_pixel_shader,{USE_DIRT:""}):r._final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,r.final_pixel_shader));g.drawTo(function(){a.bind(0); -k.bind(1);s&&(l.setUniform("u_dirt_factor",t),l.setUniform("u_dirt_texture",s.bind(2)));l.toViewport(e)});this.setOutputData(0,g)}GL.Texture.releaseTemporary(k)}},r.cut_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform float u_threshold;\n\tvoid main() {\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\t}",r.scale_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\t}", -r.final_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform sampler2D u_glow_texture;\n\t#ifdef USE_DIRT\n\t\tuniform sampler2D u_dirt_texture;\n\t#endif\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\tuniform float u_dirt_factor;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tvec4 glow = sampleBox( v_coord );\n\t\t#ifdef USE_DIRT\n\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\t\t#endif\n\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\t}", -F.registerNodeType("texture/glow",r),H.title="Kuwahara Filter",H.desc="Filters a texture giving an artistic oil canvas painting",H.max_radius=10,H._shaders=[],H.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));b=this.properties.radius;b=Math.min(Math.floor(b),H.max_radius);if(0== -b)this.setOutputData(0,a);else{var d=this.properties.intensity,c=F.camera_aspect;c||void 0===window.gl||(c=gl.canvas.height/gl.canvas.width);c||(c=1);c=this.properties.preserve_aspect?c:1;H._shaders[b]||(H._shaders[b]=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,H.pixel_shader,{RADIUS:b.toFixed(0)}));var f=H._shaders[b],h=GL.Mesh.getScreenQuad();a.bind(0);this._temp_texture.drawTo(function(){f.uniforms({u_texture:0,u_intensity:d,u_resolution:[a.width,a.height],u_iResolution:[1/a.width,1/a.height]}).draw(h)}); -this.setOutputData(0,this._temp_texture)}}},H.pixel_shader="\nprecision highp float;\nvarying vec2 v_coord;\nuniform sampler2D u_texture;\nuniform float u_intensity;\nuniform vec2 u_resolution;\nuniform vec2 u_iResolution;\n#ifndef RADIUS\n\t#define RADIUS 7\n#endif\nvoid main() {\n\n\tconst int radius = RADIUS;\n\tvec2 fragCoord = v_coord;\n\tvec2 src_size = u_iResolution;\n\tvec2 uv = v_coord;\n\tfloat n = float((radius + 1) * (radius + 1));\n\tint i;\n\tint j;\n\tvec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\tvec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\tvec3 c;\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm0 += c;\n\t\t\ts0 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm1 += c;\n\t\t\ts1 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm2 += c;\n\t\t\ts2 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm3 += c;\n\t\t\ts3 += c * c;\n\t\t}\n\t}\n\t\n\tfloat min_sigma2 = 1e+2;\n\tm0 /= n;\n\ts0 = abs(s0 / n - m0 * m0);\n\t\n\tfloat sigma2 = s0.r + s0.g + s0.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m0, 1.0);\n\t}\n\t\n\tm1 /= n;\n\ts1 = abs(s1 / n - m1 * m1);\n\t\n\tsigma2 = s1.r + s1.g + s1.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m1, 1.0);\n\t}\n\t\n\tm2 /= n;\n\ts2 = abs(s2 / n - m2 * m2);\n\t\n\tsigma2 = s2.r + s2.g + s2.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m2, 1.0);\n\t}\n\t\n\tm3 /= n;\n\ts3 = abs(s3 / n - m3 * m3);\n\t\n\tsigma2 = s3.r + s3.g + s3.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m3, 1.0);\n\t}\n}\n", -F.registerNodeType("texture/kuwahara",H),I.title="XDoG Filter",I.desc="Filters a texture giving an artistic ink style",I.max_radius=10,I._shaders=[],I.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));I._xdog_shader||(I._xdog_shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,I.xdog_pixel_shader)); -var d=I._xdog_shader,c=GL.Mesh.getScreenQuad(),f=this.properties.sigma,h=this.properties.k,e=this.properties.p,g=this.properties.epsilon,l=this.properties.phi;a.bind(0);this._temp_texture.drawTo(function(){d.uniforms({src:0,sigma:f,k:h,p:e,epsilon:g,phi:l,cvsWidth:a.width,cvsHeight:a.height}).draw(c)});this.setOutputData(0,this._temp_texture)}},I.xdog_pixel_shader="\nprecision highp float;\nuniform sampler2D src;\n\nuniform float cvsHeight;\nuniform float cvsWidth;\n\nuniform float sigma;\nuniform float k;\nuniform float p;\nuniform float epsilon;\nuniform float phi;\nvarying vec2 v_coord;\n\nfloat cosh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat cosH = (tmp + 1.0 / tmp) / 2.0;\n\treturn cosH;\n}\n\nfloat tanh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\treturn tanH;\n}\n\nfloat sinh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat sinH = (tmp - 1.0 / tmp) / 2.0;\n\treturn sinH;\n}\n\nvoid main(void){\n\tvec3 destColor = vec3(0.0);\n\tfloat tFrag = 1.0 / cvsHeight;\n\tfloat sFrag = 1.0 / cvsWidth;\n\tvec2 Frag = vec2(sFrag,tFrag);\n\tvec2 uv = gl_FragCoord.st;\n\tfloat twoSigmaESquared = 2.0 * sigma * sigma;\n\tfloat twoSigmaRSquared = twoSigmaESquared * k * k;\n\tint halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\tconst int MAX_NUM_ITERATION = 99999;\n\tvec2 sum = vec2(0.0);\n\tvec2 norm = vec2(0.0);\n\n\tfor(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\t\tint i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\t\tint j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\t\tfloat d = length(vec2(i,j));\n\t\tvec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\t\t\t\t\t\t\texp( -d * d / twoSigmaRSquared ));\n\n\t\tvec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\t\tnorm += kernel;\n\t\tsum += kernel * L;\n\t}\n\n\tsum /= norm;\n\n\tfloat H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\tfloat edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\tdestColor = vec3(edge);\n\tgl_FragColor = vec4(destColor, 1.0);\n}", -F.registerNodeType("texture/xDoG",I),G.title="Webcam",G.desc="Webcam texture",G.is_webcam_open=!1,G.prototype.openStream=function(){function a(d){G.is_webcam_open=!1;console.log("Webcam rejected",d);b._webcam_stream=!1;b.boxcolor="red";b.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](a);var b=this}},G.prototype.closeStream=function(){if(this._webcam_stream){var a= -this._webcam_stream.getTracks();if(a.length)for(var b=0;b=this.size[1]||!this._video||(a.save(),a.webgl?this._video_texture&&a.drawImage(this._video_texture,0,0,this.size[0],this.size[1]):a.drawImage(this._video, -0,0,this.size[0],this.size[1]),a.restore())},G.prototype.onExecute=function(){null!=this._webcam_stream||this._waiting_confirmation||this.openStream();if(this._video&&this._video.videoWidth){var a=this._video.videoWidth,b=this._video.videoHeight,d=this._video_texture;d&&d.width==a&&d.height==b||(this._video_texture=new GL.Texture(a,b,{format:gl.RGB,filter:gl.LINEAR}));this._video_texture.uploadImage(this._video);this._video_texture.version=++this.version;this.properties.texture_name&&(c.getTexturesContainer()[this.properties.texture_name]= -this._video_texture);this.setOutputData(0,this._video_texture);for(a=1;a>=1;1<(d|0)&&(d>>=1);if(2>b)break;r=g[p]=GL.Texture.getTemporary(b,d,f);m[0]=1/k.width;m[1]=1/k.height;k.blit(r,n.uniforms(e));k=r}this.isOutputConnected(2)&&(b=this._average_texture,b&&b.type==a.type&&b.format==a.format||(b=this._average_texture=new GL.Texture(1,1,{type:a.type,format:a.format,filter:gl.LINEAR})),m[0]=1/k.width,m[1]=1/k.height,e.u_intensity=l,e.u_delta=1,k.blit(b,n.uniforms(e)),this.setOutputData(2,b));gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE);e.u_intensity= +this.getInputOrProperty("persistence");e.u_delta=0.5;for(p-=2;0<=p;p--)r=g[p],g[p]=null,m[0]=1/k.width,m[1]=1/k.height,k.blit(r,n.uniforms(e)),GL.Texture.releaseTemporary(k),k=r;gl.disable(gl.BLEND);this.isOutputConnected(1)&&(g=this._glow_texture,g&&g.width==a.width&&g.height==a.height&&g.type==h&&g.format==a.format||(g=this._glow_texture=new GL.Texture(a.width,a.height,{type:h,format:a.format,filter:gl.LINEAR})),k.blit(g),this.setOutputData(1,g));if(this.isOutputConnected(0)){g=this._final_texture; +g&&g.width==a.width&&g.height==a.height&&g.type==h&&g.format==a.format||(g=this._final_texture=new GL.Texture(a.width,a.height,{type:h,format:a.format,filter:gl.LINEAR}));var t=this.getInputData(1),s=this.getInputOrProperty("dirt_factor");e.u_intensity=l;n=t?q._dirt_final_shader:q._final_shader;n||(n=t?q._dirt_final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,q.final_pixel_shader,{USE_DIRT:""}):q._final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,q.final_pixel_shader));g.drawTo(function(){a.bind(0); +k.bind(1);t&&(n.setUniform("u_dirt_factor",s),n.setUniform("u_dirt_texture",t.bind(2)));n.toViewport(e)});this.setOutputData(0,g)}GL.Texture.releaseTemporary(k)}},q.cut_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform float u_threshold;\n\tvoid main() {\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\t}",q.scale_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\t}", +q.final_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform sampler2D u_glow_texture;\n\t#ifdef USE_DIRT\n\t\tuniform sampler2D u_dirt_texture;\n\t#endif\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\tuniform float u_dirt_factor;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tvec4 glow = sampleBox( v_coord );\n\t\t#ifdef USE_DIRT\n\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\t\t#endif\n\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\t}", +G.registerNodeType("texture/glow",q),I.title="Kuwahara Filter",I.desc="Filters a texture giving an artistic oil canvas painting",I.max_radius=10,I._shaders=[],I.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));b=this.properties.radius;b=Math.min(Math.floor(b),I.max_radius);if(0== +b)this.setOutputData(0,a);else{var d=this.properties.intensity,c=G.camera_aspect;c||void 0===window.gl||(c=gl.canvas.height/gl.canvas.width);c||(c=1);c=this.properties.preserve_aspect?c:1;I._shaders[b]||(I._shaders[b]=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,I.pixel_shader,{RADIUS:b.toFixed(0)}));var f=I._shaders[b],h=GL.Mesh.getScreenQuad();a.bind(0);this._temp_texture.drawTo(function(){f.uniforms({u_texture:0,u_intensity:d,u_resolution:[a.width,a.height],u_iResolution:[1/a.width,1/a.height]}).draw(h)}); +this.setOutputData(0,this._temp_texture)}}},I.pixel_shader="\nprecision highp float;\nvarying vec2 v_coord;\nuniform sampler2D u_texture;\nuniform float u_intensity;\nuniform vec2 u_resolution;\nuniform vec2 u_iResolution;\n#ifndef RADIUS\n\t#define RADIUS 7\n#endif\nvoid main() {\n\n\tconst int radius = RADIUS;\n\tvec2 fragCoord = v_coord;\n\tvec2 src_size = u_iResolution;\n\tvec2 uv = v_coord;\n\tfloat n = float((radius + 1) * (radius + 1));\n\tint i;\n\tint j;\n\tvec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\tvec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\tvec3 c;\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm0 += c;\n\t\t\ts0 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm1 += c;\n\t\t\ts1 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm2 += c;\n\t\t\ts2 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm3 += c;\n\t\t\ts3 += c * c;\n\t\t}\n\t}\n\t\n\tfloat min_sigma2 = 1e+2;\n\tm0 /= n;\n\ts0 = abs(s0 / n - m0 * m0);\n\t\n\tfloat sigma2 = s0.r + s0.g + s0.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m0, 1.0);\n\t}\n\t\n\tm1 /= n;\n\ts1 = abs(s1 / n - m1 * m1);\n\t\n\tsigma2 = s1.r + s1.g + s1.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m1, 1.0);\n\t}\n\t\n\tm2 /= n;\n\ts2 = abs(s2 / n - m2 * m2);\n\t\n\tsigma2 = s2.r + s2.g + s2.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m2, 1.0);\n\t}\n\t\n\tm3 /= n;\n\ts3 = abs(s3 / n - m3 * m3);\n\t\n\tsigma2 = s3.r + s3.g + s3.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m3, 1.0);\n\t}\n}\n", +G.registerNodeType("texture/kuwahara",I),J.title="XDoG Filter",J.desc="Filters a texture giving an artistic ink style",J.max_radius=10,J._shaders=[],J.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));J._xdog_shader||(J._xdog_shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,J.xdog_pixel_shader)); +var d=J._xdog_shader,c=GL.Mesh.getScreenQuad(),f=this.properties.sigma,h=this.properties.k,e=this.properties.p,g=this.properties.epsilon,n=this.properties.phi;a.bind(0);this._temp_texture.drawTo(function(){d.uniforms({src:0,sigma:f,k:h,p:e,epsilon:g,phi:n,cvsWidth:a.width,cvsHeight:a.height}).draw(c)});this.setOutputData(0,this._temp_texture)}},J.xdog_pixel_shader="\nprecision highp float;\nuniform sampler2D src;\n\nuniform float cvsHeight;\nuniform float cvsWidth;\n\nuniform float sigma;\nuniform float k;\nuniform float p;\nuniform float epsilon;\nuniform float phi;\nvarying vec2 v_coord;\n\nfloat cosh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat cosH = (tmp + 1.0 / tmp) / 2.0;\n\treturn cosH;\n}\n\nfloat tanh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\treturn tanH;\n}\n\nfloat sinh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat sinH = (tmp - 1.0 / tmp) / 2.0;\n\treturn sinH;\n}\n\nvoid main(void){\n\tvec3 destColor = vec3(0.0);\n\tfloat tFrag = 1.0 / cvsHeight;\n\tfloat sFrag = 1.0 / cvsWidth;\n\tvec2 Frag = vec2(sFrag,tFrag);\n\tvec2 uv = gl_FragCoord.st;\n\tfloat twoSigmaESquared = 2.0 * sigma * sigma;\n\tfloat twoSigmaRSquared = twoSigmaESquared * k * k;\n\tint halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\tconst int MAX_NUM_ITERATION = 99999;\n\tvec2 sum = vec2(0.0);\n\tvec2 norm = vec2(0.0);\n\n\tfor(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\t\tint i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\t\tint j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\t\tfloat d = length(vec2(i,j));\n\t\tvec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\t\t\t\t\t\t\texp( -d * d / twoSigmaRSquared ));\n\n\t\tvec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\t\tnorm += kernel;\n\t\tsum += kernel * L;\n\t}\n\n\tsum /= norm;\n\n\tfloat H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\tfloat edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\tdestColor = vec3(edge);\n\tgl_FragColor = vec4(destColor, 1.0);\n}", +G.registerNodeType("texture/xDoG",J),H.title="Webcam",H.desc="Webcam texture",H.is_webcam_open=!1,H.prototype.openStream=function(){function a(d){H.is_webcam_open=!1;console.log("Webcam rejected",d);b._webcam_stream=!1;b.boxcolor="red";b.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](a);var b=this}},H.prototype.closeStream=function(){if(this._webcam_stream){var a= +this._webcam_stream.getTracks();if(a.length)for(var b=0;b=this.size[1]||!this._video||(a.save(),a.webgl?this._video_texture&&a.drawImage(this._video_texture,0,0,this.size[0],this.size[1]):a.drawImage(this._video, +0,0,this.size[0],this.size[1]),a.restore())},H.prototype.onExecute=function(){null!=this._webcam_stream||this._waiting_confirmation||this.openStream();if(this._video&&this._video.videoWidth){var a=this._video.videoWidth,b=this._video.videoHeight,d=this._video_texture;d&&d.width==a&&d.height==b||(this._video_texture=new GL.Texture(a,b,{format:gl.RGB,filter:gl.LINEAR}));this._video_texture.uploadImage(this._video);this._video_texture.version=++this.version;this.properties.texture_name&&(c.getTexturesContainer()[this.properties.texture_name]= +this._video_texture);this.setOutputData(0,this._video_texture);for(a=1;aMath.abs(d))return c[1];d=(a-c[0])/d;return c[1]*(1-d)+f[1]*d}}return 0}},t.prototype.updateCurve=function(){for(var a=this._values,b=a.length/4,d=this.properties.split_channels,c=0;c=f;){e=0.5*(g+f)|0;c=a[e];if(c==d)break;if(f==g-1)return f;c=f;){e=0.5*(g+f)|0;c=a[e];if(c==d)break;if(f==g-1)return f;ca&&(a=1);this.points&&this.points.length==3*a||(this.points=new Float32Array(3*a));this.properties.generate_normals?this.normals&&this.normals.length==this.points.length||(this.normals=new Float32Array(this.points.length)):this.normals=null;var d=this._last_radius||this.properties.radius,c=this.properties.mode,f=this.getInputData(0);this._old_obj_version=f?f._version:null;this.points=m.generatePoints(d,a,c,this.points, -this.normals,this.properties.regular,f);this.version++};m.generatePoints=function(a,d,c,f,e,g,l){var n=3*d;f&&f.length==n||(f=new Float32Array(n));var k=new Float32Array(3),v=new Float32Array([0,1,0]);if(g)if(c==m.RECTANGLE){n=Math.floor(Math.sqrt(d));for(d=0;da&&(a=1);this.points&&this.points.length==3*a||(this.points=new Float32Array(3*a));this.properties.generate_normals?this.normals&&this.normals.length==this.points.length||(this.normals=new Float32Array(this.points.length)):this.normals=null;var d=this._last_radius||this.properties.radius,c=this.properties.mode,f=this.getInputData(0);this._old_obj_version=f?f._version:null;this.points=l.generatePoints(d,a,c,this.points, +this.normals,this.properties.regular,f);this.version++};l.generatePoints=function(a,d,c,f,e,g,n){var r=3*d;f&&f.length==r||(f=new Float32Array(r));var k=new Float32Array(3),v=new Float32Array([0,1,0]);if(g)if(c==l.RECTANGLE){r=Math.floor(Math.sqrt(d));for(d=0;dg||xl&&ln))break}this.geometry.indices=this.indices=new Uint32Array(k)}this.indices&&this.indices.length?(this.geometry.indices=this.indices,this.setOutputData(0,this.geometry)):this.setOutputData(0,null)}};A.registerNodeType("geometry/connectPoints",u);"undefined"!=typeof GL&&(D.title="to geometry",D.desc="converts a mesh to geometry",D.prototype.onExecute= +function(){var a=this.getInputData(0);if(a){if(this.geometry_id!=a._id||this.version!=a._version||this.must_update){this.must_update=!1;this.geometry_id=a._id;this.version=a._version;this.geometry={};for(var d in a)this.geometry[d]=a[d];this.geometry._id=c();this.geometry._version=this.my_version++;var a=a.vertices,e=a.length,f=this.properties.min_dist,g=this.properties.max_dist,n=this.properties.probability,r=this.properties.max_connections,k=[];for(d=0;dg||xn&&nr))break}this.geometry.indices=this.indices=new Uint32Array(k)}this.indices&&this.indices.length?(this.geometry.indices=this.indices,this.setOutputData(0,this.geometry)):this.setOutputData(0,null)}};A.registerNodeType("geometry/connectPoints",u);"undefined"!=typeof GL&&(D.title="to geometry",D.desc="converts a mesh to geometry",D.prototype.onExecute= function(){var a=this.getInputData(0);if(a){if(a!=this.last_mesh){this.last_mesh=a;for(i in a.vertexBuffers)this.geometry[i]=a.vertexBuffers[i].data;a.indexBuffers.triangles&&(this.geometry.indices=a.indexBuffers.triangles.data);this.geometry._id=c();this.geometry._version=0}this.setOutputData(0,this.geometry);this.geometry&&this.setOutputData(1,this.geometry.vertices)}},A.registerNodeType("geometry/toGeometry",D),B.title="Geo to Mesh",B.prototype.updateMesh=function(a){this.mesh||(this.mesh=new GL.Mesh); for(var d in a)if("_"!=d[0]){var c=a[d],f=GL.Mesh.common_buffers[d];if(f||"indices"==d){var f=f?f.spacing:3,e=this.mesh.vertexBuffers[d];e&&e.data.length==c.length?(e.data.set(c),e.upload(GL.DYNAMIC_DRAW)):e=new GL.Buffer("indices"==d?GL.ELEMENT_ARRAY_BUFFER:GL.ARRAY_BUFFER,c,f,GL.DYNAMIC_DRAW);this.mesh.addBuffer(d,e)}}if(this.mesh.vertexBuffers.normals&&this.mesh.vertexBuffers.normals.data.length!=this.mesh.vertexBuffers.vertices.data.length){c=new Float32Array([0,1,0]);f=new Float32Array(this.mesh.vertexBuffers.vertices.data.length); for(d=0;d=c.NOTEON||g<=c.NOTEOFF)this.channel=e&15};Object.defineProperty(c.prototype,"velocity",{get:function(){return this.cmd==c.NOTEON?this.data[2]:-1},set:function(c){this.data[2]=c},enumerable:!0});c.notes="A A# B C C# D D# E F F# G G#".split(" ");c.note_to_index={A:0,"A#":1,B:2,C:3,"C#":4,D:5,"D#":6,E:7,F:8,"F#":9,G:10,"G#":11};Object.defineProperty(c.prototype,"note",{get:function(){return this.cmd!=c.NOTEON?-1:c.toNoteString(this.data[1], -!0)},set:function(c){throw"notes cannot be assigned this way, must modify the data[1]";},enumerable:!0});Object.defineProperty(c.prototype,"octave",{get:function(){return this.cmd!=c.NOTEON?-1:Math.floor((this.data[1]-24)/12+1)},set:function(c){throw"octave cannot be assigned this way, must modify the data[1]";},enumerable:!0});c.prototype.getPitch=function(){return 440*Math.pow(2,(this.data[1]-69)/12)};c.computePitch=function(c){return 440*Math.pow(2,(c-69)/12)};c.prototype.getCC=function(){return this.data[1]}; -c.prototype.getCCValue=function(){return this.data[2]};c.prototype.getPitchBend=function(){return this.data[1]+(this.data[2]<<7)-8192};c.computePitchBend=function(c,e){return c+(e<<7)-8192};c.prototype.setCommandFromString=function(e){this.cmd=c.computeCommandFromString(e)};c.computeCommandFromString=function(e){if(!e)return 0;if(e&&e.constructor===Number)return e;e=e.toUpperCase();switch(e){case "NOTE ON":case "NOTEON":return c.NOTEON;case "NOTE OFF":case "NOTEOFF":return c.NOTEON;case "KEY PRESSURE":case "KEYPRESSURE":return c.KEYPRESSURE; -case "CONTROLLER CHANGE":case "CONTROLLERCHANGE":case "CC":return c.CONTROLLERCHANGE;case "PROGRAM CHANGE":case "PROGRAMCHANGE":case "PC":return c.PROGRAMCHANGE;case "CHANNEL PRESSURE":case "CHANNELPRESSURE":return c.CHANNELPRESSURE;case "PITCH BEND":case "PITCHBEND":return c.PITCHBEND;case "TIME TICK":case "TIMETICK":return c.TIMETICK;default:return Number(e)}};c.toNoteString=function(e,g){e=Math.round(e);var k,l=Math.floor((e-24)/12+1);k=(e-21)%12;0>k&&(k=12+k);return c.notes[k]+(g?"":l)};c.NoteStringToPitch= -function(e){e=e.toUpperCase();var g=e[0],k=4;"#"==e[1]?(g+="#",2this.properties.max_value)return;this.trigger("on_midi", -g)}};s.registerNodeType("midi/filter",e);C.title="MIDIEvent";C.desc="Create a MIDI Event";C.color="#243";C.prototype.onAction=function(e,g){"assign"==e?(this.properties.channel=g.channel,this.properties.cmd=g.cmd,this.properties.value1=g.data[1],this.properties.value2=g.data[2],g.cmd==c.NOTEON?this.gate=!0:g.cmd==c.NOTEOFF&&(this.gate=!1)):(g=this.midi_event,g.channel=this.properties.channel,this.properties.cmd&&this.properties.cmd.constructor===String?g.setCommandFromString(this.properties.cmd): -g.cmd=this.properties.cmd,g.data[0]=g.cmd|g.channel,g.data[1]=Number(this.properties.value1),g.data[2]=Number(this.properties.value2),this.trigger("on_midi",g))};C.prototype.onExecute=function(){var e=this.properties;if(this.inputs)for(var g=0;gc;++c)this.valid_notes[c]=-1!= -this.notes_pitches.indexOf(c);for(c=0;12>c;++c)if(this.valid_notes[c])this.offset_notes[c]=0;else for(var e=1;12>e;++e){if(this.valid_notes[(c-e)%12]){this.offset_notes[c]=-e;break}if(this.valid_notes[(c+e)%12]){this.offset_notes[c]=e;break}}};B.prototype.onAction=function(e,g){g&&g.constructor===c&&(g.data[0]==c.NOTEON||g.data[0]==c.NOTEOFF?(this.midi_event=new c,this.midi_event.setup(g.data),this.midi_event.data[1]+=this.offset_notes[c.note_to_index[g.note]],this.trigger("out",this.midi_event)): -this.trigger("out",g))};B.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&c!=this._current_scale&&this.processScale(c)};s.registerNodeType("midi/quantize",B);g.title="MIDI Play";g.desc="Plays a MIDI note";g.color="#243";g.prototype.onAction=function(e,g){if(g&&g.constructor===c){if(this.instrument&&g.data[0]==c.NOTEON){var k=g.note;if(!k||"undefined"==k||k.constructor!==String)return;this.instrument.play(k,g.octave,this.properties.duration,this.properties.volume)}this.trigger("note", -g)}};g.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&(this.properties.volume=c);c=this.getInputData(2);null!=c&&(this.properties.duration=c)};s.registerNodeType("midi/play",g);p.title="MIDI Keys";p.desc="Keyboard to play notes";p.color="#243";p.keys=[{x:0,w:1,h:1,t:0},{x:0.75,w:0.5,h:0.6,t:1},{x:1,w:1,h:1,t:0},{x:1.75,w:0.5,h:0.6,t:1},{x:2,w:1,h:1,t:0},{x:2.75,w:0.5,h:0.6,t:1},{x:3,w:1,h:1,t:0},{x:4,w:1,h:1,t:0},{x:4.75,w:0.5,h:0.6,t:1},{x:5,w:1,h:1,t:0},{x:5.75,w:0.5,h:0.6,t:1}, -{x:6,w:1,h:1,t:0}];p.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){var e=12*this.properties.num_octaves;this.keys.length=e;var g=this.size[0]/(7*this.properties.num_octaves),l=this.size[1];c.globalAlpha=1;for(var a=0;2>a;a++)for(var b=0;bd+h||c[1]>b))return a}}return-1};p.prototype.onAction=function(e,g){if("reset"==e)for(var k=0;kg[1])){var k=this.getKeyIndex(g);this.keys[k]=!0;this._last_key=k;var k=12*(this.properties.start_octave-1)+29+k,l=new c;l.setup([c.NOTEON,k,100]);this.trigger("note",l);return!0}};p.prototype.onMouseMove=function(e,g){if(!(0>g[1]||-1==this._last_key)){this.setDirtyCanvas(!0);var k=this.getKeyIndex(g);if(this._last_key==k)return!0;this.keys[this._last_key]=!1;var l=12*(this.properties.start_octave-1)+29+this._last_key,a=new c;a.setup([c.NOTEOFF,l,100]); -this.trigger("note",a);this.keys[k]=!0;l=12*(this.properties.start_octave-1)+29+k;a=new c;a.setup([c.NOTEON,l,100]);this.trigger("note",a);this._last_key=k;return!0}};p.prototype.onMouseUp=function(e,g){if(!(0>g[1])){var k=this.getKeyIndex(g);this.keys[k]=!1;this._last_key=-1;var k=12*(this.properties.start_octave-1)+29+k,l=new c;l.setup([c.NOTEOFF,k,100]);this.trigger("note",l);return!0}};s.registerNodeType("midi/keys",p)})(this); -(function(y){function c(){this.properties={src:"",gain:0.5,loop:!0,autoplay:!0,playbackRate:1};this._loading_audio=!1;this._audiobuffer=null;this._audionodes=[];this._last_sourcenode=null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=x.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain;this.properties.src&&this.loadSound(this.properties.src)}function m(){this.properties={gain:0.5};this._audionodes=[];this._media_stream= -null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=x.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain}function q(){this.properties={fftSize:2048,minDecibels:-100,maxDecibels:-10,smoothingTimeConstant:0.5};this.audionode=x.getAudioContext().createAnalyser();this.audionode.graphnode=this;this.audionode.fftSize=this.properties.fftSize;this.audionode.minDecibels=this.properties.minDecibels;this.audionode.maxDecibels= +(function(y){function c(c){this.cmd=this.channel=0;this.data=new Uint32Array(3);c&&this.setup(c)}function l(c,e){navigator.requestMIDIAccess?(this.on_ready=c,this.state={note:[],cc:[]},this.input_ports=null,this.input_ports_info=[],this.output_ports=null,this.output_ports_info=[],navigator.requestMIDIAccess().then(this.onMIDISuccess.bind(this),this.onMIDIFailure.bind(this))):(this.error="not suppoorted",e?e("Not supported"):console.error("MIDI NOT SUPPORTED, enable by chrome://flags"))}function p(){this.addOutput("on_midi", +s.EVENT);this.addOutput("out","midi");this.properties={port:0};this._current_midi_event=this._last_midi_event=null;this.boxcolor="#AAA";this._last_time=0;var c=this;new l(function(e){c._midi=e;if(c._waiting)c.onStart();c._waiting=!1})}function k(){this.addInput("send",s.EVENT);this.properties={port:0};var c=this;new l(function(e){c._midi=e;c.widget.options.values=c.getMIDIOutputs()});this.widget=this.addWidget("combo","Device",this.properties.port,{property:"port",values:this.getMIDIOutputs.bind(this)}); +this.size=[340,60]}function w(){this.addInput("on_midi",s.EVENT);this._str="";this.size=[200,40]}function e(){this.properties={channel:-1,cmd:-1,min_value:-1,max_value:-1};var c=this;this._learning=!1;this.addWidget("button","Learn","",function(){c._learning=!0;c.boxcolor="#FA3"});this.addInput("in",s.EVENT);this.addOutput("on_midi",s.EVENT);this.boxcolor="#AAA"}function C(){this.properties={channel:0,cmd:144,value1:1,value2:1};this.addInput("send",s.EVENT);this.addInput("assign",s.EVENT);this.addOutput("on_midi", +s.EVENT);this.midi_event=new c;this.gate=!1}function z(){this.properties={cc:1,value:0};this.addOutput("value","number")}function u(){this.addInput("generate",s.ACTION);this.addInput("scale","string");this.addInput("octave","number");this.addOutput("note",s.EVENT);this.properties={notes:"A,A#,B,C,C#,D,D#,E,F,F#,G,G#",octave:2,duration:0.5,mode:"sequence"};this.notes_pitches=u.processScale(this.properties.notes);this.sequence_index=0}function D(){this.properties={amount:0};this.addInput("in",s.ACTION); +this.addInput("amount","number");this.addOutput("out",s.EVENT);this.midi_event=new c}function B(){this.properties={scale:"A,A#,B,C,C#,D,D#,E,F,F#,G,G#"};this.addInput("note",s.ACTION);this.addInput("scale","string");this.addOutput("out",s.EVENT);this.valid_notes=Array(12);this.offset_notes=Array(12);this.processScale(this.properties.scale)}function g(){this.properties={volume:0.5,duration:1};this.addInput("note",s.ACTION);this.addInput("volume","number");this.addInput("duration","number");this.addOutput("note", +s.EVENT);"undefined"==typeof AudioSynth?(console.error("Audiosynth.js not included, LGMidiPlay requires that library"),this.boxcolor="red"):this.instrument=(this.synth=new AudioSynth).createInstrument("piano")}function m(){this.properties={num_octaves:2,start_octave:2};this.addInput("note",s.ACTION);this.addInput("reset",s.ACTION);this.addOutput("note",s.EVENT);this.size=[400,100];this.keys=[];this._last_key=-1}var s=y.LiteGraph;s.MIDIEvent=c;c.prototype.fromJSON=function(c){this.setup(c.data)};c.prototype.setup= +function(e){var g=e;e.constructor===Object&&(g=e.data);this.data.set(g);this.status=e=g[0];g=e&240;this.cmd=240<=e?e:g;this.cmd==c.NOTEON&&0==this.velocity&&(this.cmd=c.NOTEOFF);this.cmd_str=c.commands[this.cmd]||"";if(g>=c.NOTEON||g<=c.NOTEOFF)this.channel=e&15};Object.defineProperty(c.prototype,"velocity",{get:function(){return this.cmd==c.NOTEON?this.data[2]:-1},set:function(c){this.data[2]=c},enumerable:!0});c.notes="A A# B C C# D D# E F F# G G#".split(" ");c.note_to_index={A:0,"A#":1,B:2,C:3, +"C#":4,D:5,"D#":6,E:7,F:8,"F#":9,G:10,"G#":11};Object.defineProperty(c.prototype,"note",{get:function(){return this.cmd!=c.NOTEON?-1:c.toNoteString(this.data[1],!0)},set:function(c){throw"notes cannot be assigned this way, must modify the data[1]";},enumerable:!0});Object.defineProperty(c.prototype,"octave",{get:function(){return this.cmd!=c.NOTEON?-1:Math.floor((this.data[1]-24)/12+1)},set:function(c){throw"octave cannot be assigned this way, must modify the data[1]";},enumerable:!0});c.prototype.getPitch= +function(){return 440*Math.pow(2,(this.data[1]-69)/12)};c.computePitch=function(c){return 440*Math.pow(2,(c-69)/12)};c.prototype.getCC=function(){return this.data[1]};c.prototype.getCCValue=function(){return this.data[2]};c.prototype.getPitchBend=function(){return this.data[1]+(this.data[2]<<7)-8192};c.computePitchBend=function(c,e){return c+(e<<7)-8192};c.prototype.setCommandFromString=function(e){this.cmd=c.computeCommandFromString(e)};c.computeCommandFromString=function(e){if(!e)return 0;if(e&& +e.constructor===Number)return e;e=e.toUpperCase();switch(e){case "NOTE ON":case "NOTEON":return c.NOTEON;case "NOTE OFF":case "NOTEOFF":return c.NOTEON;case "KEY PRESSURE":case "KEYPRESSURE":return c.KEYPRESSURE;case "CONTROLLER CHANGE":case "CONTROLLERCHANGE":case "CC":return c.CONTROLLERCHANGE;case "PROGRAM CHANGE":case "PROGRAMCHANGE":case "PC":return c.PROGRAMCHANGE;case "CHANNEL PRESSURE":case "CHANNELPRESSURE":return c.CHANNELPRESSURE;case "PITCH BEND":case "PITCHBEND":return c.PITCHBEND;case "TIME TICK":case "TIMETICK":return c.TIMETICK; +default:return Number(e)}};c.toNoteString=function(e,g){e=Math.round(e);var k,v=Math.floor((e-24)/12+1);k=(e-21)%12;0>k&&(k=12+k);return c.notes[k]+(g?"":v)};c.NoteStringToPitch=function(e){e=e.toUpperCase();var g=e[0],k=4;"#"==e[1]?(g+="#",2this.properties.max_value)return;this.trigger("on_midi",g)}};s.registerNodeType("midi/filter",e);C.title="MIDIEvent";C.desc="Create a MIDI Event";C.color="#243";C.prototype.onAction= +function(e,g){"assign"==e?(this.properties.channel=g.channel,this.properties.cmd=g.cmd,this.properties.value1=g.data[1],this.properties.value2=g.data[2],g.cmd==c.NOTEON?this.gate=!0:g.cmd==c.NOTEOFF&&(this.gate=!1)):(g=this.midi_event,g.channel=this.properties.channel,this.properties.cmd&&this.properties.cmd.constructor===String?g.setCommandFromString(this.properties.cmd):g.cmd=this.properties.cmd,g.data[0]=g.cmd|g.channel,g.data[1]=Number(this.properties.value1),g.data[2]=Number(this.properties.value2), +this.trigger("on_midi",g))};C.prototype.onExecute=function(){var e=this.properties;if(this.inputs)for(var g=0;gc;++c)this.valid_notes[c]=-1!=this.notes_pitches.indexOf(c);for(c=0;12>c;++c)if(this.valid_notes[c])this.offset_notes[c]=0;else for(var e= +1;12>e;++e){if(this.valid_notes[(c-e)%12]){this.offset_notes[c]=-e;break}if(this.valid_notes[(c+e)%12]){this.offset_notes[c]=e;break}}};B.prototype.onAction=function(e,g){g&&g.constructor===c&&(g.data[0]==c.NOTEON||g.data[0]==c.NOTEOFF?(this.midi_event=new c,this.midi_event.setup(g.data),this.midi_event.data[1]+=this.offset_notes[c.note_to_index[g.note]],this.trigger("out",this.midi_event)):this.trigger("out",g))};B.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&c!=this._current_scale&& +this.processScale(c)};s.registerNodeType("midi/quantize",B);g.title="MIDI Play";g.desc="Plays a MIDI note";g.color="#243";g.prototype.onAction=function(e,g){if(g&&g.constructor===c){if(this.instrument&&g.data[0]==c.NOTEON){var k=g.note;if(!k||"undefined"==k||k.constructor!==String)return;this.instrument.play(k,g.octave,this.properties.duration,this.properties.volume)}this.trigger("note",g)}};g.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&(this.properties.volume=c);c=this.getInputData(2); +null!=c&&(this.properties.duration=c)};s.registerNodeType("midi/play",g);m.title="MIDI Keys";m.desc="Keyboard to play notes";m.color="#243";m.keys=[{x:0,w:1,h:1,t:0},{x:0.75,w:0.5,h:0.6,t:1},{x:1,w:1,h:1,t:0},{x:1.75,w:0.5,h:0.6,t:1},{x:2,w:1,h:1,t:0},{x:2.75,w:0.5,h:0.6,t:1},{x:3,w:1,h:1,t:0},{x:4,w:1,h:1,t:0},{x:4.75,w:0.5,h:0.6,t:1},{x:5,w:1,h:1,t:0},{x:5.75,w:0.5,h:0.6,t:1},{x:6,w:1,h:1,t:0}];m.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){var e=12*this.properties.num_octaves; +this.keys.length=e;var g=this.size[0]/(7*this.properties.num_octaves),k=this.size[1];c.globalAlpha=1;for(var a=0;2>a;a++)for(var b=0;bd+h||c[1]>b))return a}}return-1};m.prototype.onAction=function(e,g){if("reset"==e)for(var k=0;kg[1])){var k=this.getKeyIndex(g);this.keys[k]=!0;this._last_key= +k;var k=12*(this.properties.start_octave-1)+29+k,v=new c;v.setup([c.NOTEON,k,100]);this.trigger("note",v);return!0}};m.prototype.onMouseMove=function(e,g){if(!(0>g[1]||-1==this._last_key)){this.setDirtyCanvas(!0);var k=this.getKeyIndex(g);if(this._last_key==k)return!0;this.keys[this._last_key]=!1;var v=12*(this.properties.start_octave-1)+29+this._last_key,a=new c;a.setup([c.NOTEOFF,v,100]);this.trigger("note",a);this.keys[k]=!0;v=12*(this.properties.start_octave-1)+29+k;a=new c;a.setup([c.NOTEON, +v,100]);this.trigger("note",a);this._last_key=k;return!0}};m.prototype.onMouseUp=function(e,g){if(!(0>g[1])){var k=this.getKeyIndex(g);this.keys[k]=!1;this._last_key=-1;var k=12*(this.properties.start_octave-1)+29+k,v=new c;v.setup([c.NOTEOFF,k,100]);this.trigger("note",v);return!0}};s.registerNodeType("midi/keys",m)})(this); +(function(y){function c(){this.properties={src:"",gain:0.5,loop:!0,autoplay:!0,playbackRate:1};this._loading_audio=!1;this._audiobuffer=null;this._audionodes=[];this._last_sourcenode=null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=x.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain;this.properties.src&&this.loadSound(this.properties.src)}function l(){this.properties={gain:0.5};this._audionodes=[];this._media_stream= +null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=x.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain}function p(){this.properties={fftSize:2048,minDecibels:-100,maxDecibels:-10,smoothingTimeConstant:0.5};this.audionode=x.getAudioContext().createAnalyser();this.audionode.graphnode=this;this.audionode.fftSize=this.properties.fftSize;this.audionode.minDecibels=this.properties.minDecibels;this.audionode.maxDecibels= this.properties.maxDecibels;this.audionode.smoothingTimeConstant=this.properties.smoothingTimeConstant;this.addInput("in","audio");this.addOutput("freqs","array");this.addOutput("samples","array");this._time_bin=this._freq_bin=null}function k(){this.properties={gain:1};this.audionode=x.getAudioContext().createGain();this.addInput("in","audio");this.addInput("gain","number");this.addOutput("out","audio")}function w(){this.properties={impulse_src:"",normalize:!0};this.audionode=x.getAudioContext().createConvolver(); this.addInput("in","audio");this.addOutput("out","audio")}function e(){this.properties={threshold:-50,knee:40,ratio:12,reduction:-20,attack:0,release:0.25};this.audionode=x.getAudioContext().createDynamicsCompressor();this.addInput("in","audio");this.addOutput("out","audio")}function C(){this.properties={};this.audionode=x.getAudioContext().createWaveShaper();this.addInput("in","audio");this.addInput("shape","waveshape");this.addOutput("out","audio")}function z(){this.properties={gain1:0.5,gain2:0.5}; this.audionode=x.getAudioContext().createGain();this.audionode1=x.getAudioContext().createGain();this.audionode1.gain.value=this.properties.gain1;this.audionode2=x.getAudioContext().createGain();this.audionode2.gain.value=this.properties.gain2;this.audionode1.connect(this.audionode);this.audionode2.connect(this.audionode);this.addInput("in1","audio");this.addInput("in1 gain","number");this.addInput("in2","audio");this.addInput("in2 gain","number");this.addOutput("out","audio")}function u(){this.properties= {A:0.1,D:0.1,S:0.1,R:0.1};this.audionode=x.getAudioContext().createGain();this.audionode.gain.value=0;this.addInput("in","audio");this.addInput("gate","bool");this.addOutput("out","audio");this.gate=!1}function D(){this.properties={delayTime:0.5};this.audionode=x.getAudioContext().createDelay(10);this.audionode.delayTime.value=this.properties.delayTime;this.addInput("in","audio");this.addInput("time","number");this.addOutput("out","audio")}function B(){this.properties={frequency:350,detune:0,Q:1}; -this.addProperty("type","lowpass","enum",{values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")});this.audionode=x.getAudioContext().createBiquadFilter();this.addInput("in","audio");this.addOutput("out","audio")}function g(){this.properties={frequency:440,detune:0,type:"sine"};this.addProperty("type","sine","enum",{values:["sine","square","sawtooth","triangle","custom"]});this.audionode=x.getAudioContext().createOscillator();this.addOutput("out","audio")}function p(){this.properties= +this.addProperty("type","lowpass","enum",{values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")});this.audionode=x.getAudioContext().createBiquadFilter();this.addInput("in","audio");this.addOutput("out","audio")}function g(){this.properties={frequency:440,detune:0,type:"sine"};this.addProperty("type","sine","enum",{values:["sine","square","sawtooth","triangle","custom"]});this.audionode=x.getAudioContext().createOscillator();this.addOutput("out","audio")}function m(){this.properties= {continuous:!0,mark:-1};this.addInput("data","array");this.addInput("mark","number");this.size=[300,200];this._last_buffer=null}function s(){this.properties={band:440,amplitude:1};this.addInput("freqs","array");this.addOutput("signal","number")}function A(){if(!A.default_code){var c=A.default_function.toString(),a=c.indexOf("{")+1,b=c.lastIndexOf("}");A.default_code=c.substr(a,b-a)}this.properties={code:A.default_code};c=x.getAudioContext();c.createScriptProcessor?this.audionode=c.createScriptProcessor(4096, -1,1):(console.warn("ScriptProcessorNode deprecated"),this.audionode=c.createGain());this.processCode();A._bypass_function||(A._bypass_function=this.audionode.onaudioprocess);this.addInput("in","audio");this.addOutput("out","audio")}function n(){this.audionode=x.getAudioContext().destination;this.addInput("in","audio")}var v=y.LiteGraph,x={};y.LGAudio=x;x.getAudioContext=function(){if(!this._audio_context){window.AudioContext=window.AudioContext||window.webkitAudioContext;if(!window.AudioContext)return console.error("AudioContext not supported by browser"), +1,1):(console.warn("ScriptProcessorNode deprecated"),this.audionode=c.createGain());this.processCode();A._bypass_function||(A._bypass_function=this.audionode.onaudioprocess);this.addInput("in","audio");this.addOutput("out","audio")}function r(){this.audionode=x.getAudioContext().destination;this.addInput("in","audio")}var n=y.LiteGraph,x={};y.LGAudio=x;x.getAudioContext=function(){if(!this._audio_context){window.AudioContext=window.AudioContext||window.webkitAudioContext;if(!window.AudioContext)return console.error("AudioContext not supported by browser"), null;this._audio_context=new AudioContext;this._audio_context.onmessage=function(c){console.log("msg",c)};this._audio_context.onended=function(c){console.log("ended",c)};this._audio_context.oncomplete=function(c){console.log("complete",c)}}return this._audio_context};x.connect=function(c,a){try{c.connect(a)}catch(b){console.warn("LGraphAudio:",b)}};x.disconnect=function(c,a){try{c.disconnect(a)}catch(b){console.warn("LGraphAudio:",b)}};x.changeAllAudiosConnections=function(c,a){if(c.inputs)for(var b= 0;b=this.size[0]&&(e=this.size[0]-1),c.strokeStyle="red",c.beginPath(),c.moveTo(e,d),c.lineTo(e,0),c.stroke())}};p.title="Visualization";p.desc="Audio Visualization";v.registerNodeType("audio/visualization",p);s.prototype.onExecute=function(){if(this._freqs=this.getInputData(0)){var c=this.properties.band,a=this.getInputData(1);void 0!==a&&(c=a);a=x.getAudioContext().sampleRate/this._freqs.length;a=c/a*2;a>=this._freqs.length? -a=this._freqs[this._freqs.length-1]:(c=a|0,a-=c,a=this._freqs[c]*(1-a)+this._freqs[c+1]*a);this.setOutputData(0,a/255*this.properties.amplitude)}};s.prototype.onGetInputs=function(){return[["band","number"]]};s.title="Signal";s.desc="extract the signal of some frequency";v.registerNodeType("audio/signal",s);A.prototype.onAdded=function(c){c.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback)};A["@code"]={widget:"code",type:"code"};A.prototype.onStart=function(){this.audionode.onaudioprocess= -this._callback};A.prototype.onStop=function(){this.audionode.onaudioprocess=A._bypass_function};A.prototype.onPause=function(){this.audionode.onaudioprocess=A._bypass_function};A.prototype.onUnpause=function(){this.audionode.onaudioprocess=this._callback};A.prototype.onExecute=function(){};A.prototype.onRemoved=function(){this.audionode.onaudioprocess=A._bypass_function};A.prototype.processCode=function(){try{this._script=new new Function("properties",this.properties.code)(this.properties),this._old_code= -this.properties.code,this._callback=this._script.onaudioprocess}catch(c){console.error("Error in onaudioprocess code",c),this._callback=A._bypass_function,this.audionode.onaudioprocess=this._callback}};A.prototype.onPropertyChanged=function(c,a){"code"==c&&(this.properties.code=a,this.processCode(),this.graph&&this.graph.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback))};A.default_function=function(){this.onaudioprocess=function(c){var a=c.inputBuffer;c=c.outputBuffer; -for(var b=0;b=this.size[0]&&(e=this.size[0]-1),c.strokeStyle="red",c.beginPath(),c.moveTo(e,d),c.lineTo(e,0),c.stroke())}};m.title="Visualization";m.desc="Audio Visualization";n.registerNodeType("audio/visualization",m);s.prototype.onExecute=function(){if(this._freqs=this.getInputData(0)){var c=this.properties.band,a=this.getInputData(1); +void 0!==a&&(c=a);a=x.getAudioContext().sampleRate/this._freqs.length;a=c/a*2;a>=this._freqs.length?a=this._freqs[this._freqs.length-1]:(c=a|0,a-=c,a=this._freqs[c]*(1-a)+this._freqs[c+1]*a);this.setOutputData(0,a/255*this.properties.amplitude)}};s.prototype.onGetInputs=function(){return[["band","number"]]};s.title="Signal";s.desc="extract the signal of some frequency";n.registerNodeType("audio/signal",s);A.prototype.onAdded=function(c){c.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess= +this._callback)};A["@code"]={widget:"code",type:"code"};A.prototype.onStart=function(){this.audionode.onaudioprocess=this._callback};A.prototype.onStop=function(){this.audionode.onaudioprocess=A._bypass_function};A.prototype.onPause=function(){this.audionode.onaudioprocess=A._bypass_function};A.prototype.onUnpause=function(){this.audionode.onaudioprocess=this._callback};A.prototype.onExecute=function(){};A.prototype.onRemoved=function(){this.audionode.onaudioprocess=A._bypass_function};A.prototype.processCode= +function(){try{this._script=new new Function("properties",this.properties.code)(this.properties),this._old_code=this.properties.code,this._callback=this._script.onaudioprocess}catch(c){console.error("Error in onaudioprocess code",c),this._callback=A._bypass_function,this.audionode.onaudioprocess=this._callback}};A.prototype.onPropertyChanged=function(c,a){"code"==c&&(this.properties.code=a,this.processCode(),this.graph&&this.graph.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback))}; +A.default_function=function(){this.onaudioprocess=function(c){var a=c.inputBuffer;c=c.outputBuffer;for(var b=0;b width - 40 ? 1 : 0; if (w.type == "number") { @@ -8416,21 +8426,25 @@ LGraphNode.prototype.executeAction = function(action) if ( w.options.max != null && w.value > w.options.max ) { w.value = w.options.max; } - } else if (delta) { //used for combos - var values_list = values.constructor === Array ? values : Object.keys(values); - var index = values_list.indexOf(w.value) + delta; - if (index >= values.length) { - index = 0; + } else if (delta) { //clicked in arrow, used for combos + var index = -1; + if(values.constructor === Object) + index = values_list.indexOf( String( w.value ) ) + delta; + else + index = values_list.indexOf( w.value ) + delta; + if (index >= values_list.length) { + index = values_list.length - 1; } if (index < 0) { - index = values_list.length - 1; + index = 0; } if( values.constructor === Array ) w.value = values[index]; else - w.value = values[ values_list[index] ]; - } else { //combo - var menu = new LiteGraph.ContextMenu(values,{ + w.value = index; + } else { //combo clicked + var text_values = values != values_list ? Object.values(values) : values; + var menu = new LiteGraph.ContextMenu(text_values, { scale: Math.max(1, this.ds.scale), event: event, className: "dark", @@ -8438,6 +8452,8 @@ LGraphNode.prototype.executeAction = function(action) }, ref_window); function inner_clicked(v, option, event) { + if(values != values_list) + v = text_values.indexOf(v); this.value = v; inner_value_change(this, v); that.dirty_canvas = true; diff --git a/src/nodes/audio.js b/src/nodes/audio.js index ee0a4f14e..ca35c6990 100644 --- a/src/nodes/audio.js +++ b/src/nodes/audio.js @@ -275,6 +275,7 @@ } } + LGAudioSource.desc = "Plays an audio file"; LGAudioSource["@src"] = { widget: "resource" }; LGAudioSource.supported_extensions = ["wav", "ogg", "mp3"]; @@ -290,7 +291,7 @@ } if (this.properties.autoplay) { - this.playBuffer(this._audiobuffer); + this.playBuffer(this._audiobuffer); } }; @@ -403,7 +404,10 @@ audionode.playbackRate.value = this.properties.playbackRate; this._audionodes.push(audionode); audionode.connect(this.audionode); //connect to gain - this._audionodes.push(audionode); + + this._audionodes.push(audionode); + + this.trigger("start"); audionode.onended = function() { //console.log("ended!"); @@ -467,7 +471,7 @@ }; LGAudioSource.prototype.onGetOutputs = function() { - return [["buffer", "audiobuffer"], ["ended", LiteGraph.EVENT]]; + return [["buffer", "audiobuffer"], ["start", LiteGraph.EVENT], ["ended", LiteGraph.EVENT]]; }; LGAudioSource.prototype.onDropFile = function(file) { diff --git a/src/nodes/base.js b/src/nodes/base.js index 0e14ef587..f273d0bd2 100755 --- a/src/nodes/base.js +++ b/src/nodes/base.js @@ -441,27 +441,8 @@ enumerable: true }); - this.name_widget = this.addWidget( - "text", - "Name", - this.properties.name, - function(v) { - if (!v) { - return; - } - that.properties.name = v; - } - ); - this.type_widget = this.addWidget( - "text", - "Type", - this.properties.type, - function(v) { - v = v || ""; - that.properties.type = v; - } - ); - + this.name_widget = this.addWidget("text","Name",this.properties.name,"name"); + this.type_widget = this.addWidget("text","Type",this.properties.type,"type"); this.widgets_up = true; this.size = [180, 60]; } @@ -500,12 +481,7 @@ function ConstantNumber() { this.addOutput("value", "number"); this.addProperty("value", 1.0); - this.widget = this.addWidget( - "number", - "value", - 1, - "value" - ); + this.widget = this.addWidget("number","value",1,"value"); this.widgets_up = true; this.size = [180, 30]; } @@ -524,6 +500,11 @@ return this.title; }; + ConstantNumber.prototype.setValue = function(v) + { + this.setProperty("value",v); + } + ConstantNumber.prototype.onDrawBackground = function(ctx) { //show the current value this.outputs[0].label = this.properties["value"].toFixed(3); @@ -534,12 +515,7 @@ function ConstantBoolean() { this.addOutput("", "boolean"); this.addProperty("value", true); - this.widget = this.addWidget( - "toggle", - "value", - true, - "value" - ); + this.widget = this.addWidget("toggle","value",true,"value"); this.widgets_up = true; this.size = [140, 30]; } @@ -552,17 +528,14 @@ this.setOutputData(0, this.properties["value"]); }; + ConstantBoolean.prototype.setValue = ConstantNumber.prototype.setValue; + LiteGraph.registerNodeType("basic/boolean", ConstantBoolean); function ConstantString() { this.addOutput("", "string"); this.addProperty("value", ""); - this.widget = this.addWidget( - "text", - "value", - "", - "value" //link to property value - ); + this.widget = this.addWidget("text","value","","value"); //link to property value this.widgets_up = true; this.size = [180, 30]; } @@ -576,17 +549,124 @@ this.setOutputData(0, this.properties["value"]); }; + ConstantString.prototype.setValue = ConstantNumber.prototype.setValue; + + ConstantString.prototype.onDropFile = function(file) + { + var that = this; + var reader = new FileReader(); + reader.onload = function(e) + { + that.setProperty("value",e.target.result); + } + reader.readAsText(file); + } + LiteGraph.registerNodeType("basic/string", ConstantString); + function ConstantFile() { + this.addInput("url", ""); + this.addOutput("", ""); + this.addProperty("url", ""); + this.addProperty("type", "text"); + this.widget = this.addWidget("text","url","","url"); + this._data = null; + } + + ConstantFile.title = "Const File"; + ConstantFile.desc = "Fetches a file from an url"; + ConstantFile["@type"] = { type: "enum", values: ["text","arraybuffer","blob","json"] }; + + ConstantFile.prototype.onPropertyChanged = function(name, value) { + if (name == "url") + { + if( value == null || value == "") + this._data = null; + else + { + this.fetchFile(value); + } + } + } + + ConstantFile.prototype.onExecute = function() { + var url = this.getInputData(0) || this.properties.url; + if(url && (url != this._url || this._type != this.properties.type)) + this.fetchFile(url); + this.setOutputData(0, this._data ); + }; + + ConstantFile.prototype.setValue = ConstantNumber.prototype.setValue; + + ConstantFile.prototype.fetchFile = function(url) { + var that = this; + if(!url || url.constructor !== String) + { + that._data = null; + that.boxcolor = null; + return; + } + + this._url = url; + this._type = this.properties.type; + if (url.substr(0, 4) == "http" && LiteGraph.proxy) { + url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); + } + fetch(url) + .then(function(response) { + if(!response.ok) + throw new Error("File not found"); + + if(that.properties.type == "arraybuffer") + return response.arrayBuffer(); + else if(that.properties.type == "text") + return response.text(); + else if(that.properties.type == "json") + return response.json(); + else if(that.properties.type == "blob") + return response.blob(); + }) + .then(function(data) { + that._data = data; + that.boxcolor = "#AEA"; + }) + .catch(function(error) { + that._data = null; + that.boxcolor = "red"; + console.error("error fetching file:",url); + }); + }; + + ConstantFile.prototype.onDropFile = function(file) + { + var that = this; + this._url = file.name; + this._type = this.properties.type; + this.properties.url = file.name; + var reader = new FileReader(); + reader.onload = function(e) + { + that.boxcolor = "#AEA"; + var v = e.target.result; + if( that.properties.type == "json" ) + v = JSON.parse(v); + that._data = v; + } + if(that.properties.type == "arraybuffer") + reader.readAsArrayBuffer(file); + else if(that.properties.type == "text" || that.properties.type == "json") + reader.readAsText(file); + else if(that.properties.type == "blob") + return reader.readAsBinaryString(file); + } + + LiteGraph.registerNodeType("basic/file", ConstantFile); + + //to store json objects function ConstantData() { this.addOutput("", ""); this.addProperty("value", ""); - this.widget = this.addWidget( - "text", - "json", - "", - this.setValue.bind(this) - ); + this.widget = this.addWidget("text","json","","value"); this.widgets_up = true; this.size = [140, 30]; this._value = null; @@ -595,11 +675,6 @@ ConstantData.title = "Const Data"; ConstantData.desc = "Constant Data"; - ConstantData.prototype.setValue = function(v) { - this.properties.value = v; - this.onPropertyChanged("value", v); - }; - ConstantData.prototype.onPropertyChanged = function(name, value) { this.widget.value = value; if (value == null || value == "") { @@ -618,18 +693,68 @@ this.setOutputData(0, this._value); }; + ConstantData.prototype.setValue = ConstantNumber.prototype.setValue; + LiteGraph.registerNodeType("basic/data", ConstantData); + function ArrayElement() { + this.addInput("array", "array,table,string"); + this.addInput("index", "number"); + this.addOutput("value", ""); + this.addProperty("index",0); + } + + ArrayElement.title = "Array[i]"; + ArrayElement.desc = "Returns an element from an array"; + + ArrayElement.prototype.onExecute = function() { + var array = this.getInputData(0); + var index = this.getInputData(1); + if(index == null) + index = this.properties.index; + if(array == null || index == null ) + return; + this.setOutputData(0, array[Math.floor(Number(index))] ); + }; + + LiteGraph.registerNodeType("basic/array[]", ArrayElement); + + function TableElement() { + this.addInput("table", "table"); + this.addInput("row", "number"); + this.addInput("col", "number"); + this.addOutput("value", ""); + this.addProperty("row",0); + this.addProperty("column",0); + } + + TableElement.title = "Table[row][col]"; + TableElement.desc = "Returns an element from a table"; + + TableElement.prototype.onExecute = function() { + var table = this.getInputData(0); + var row = this.getInputData(1); + var col = this.getInputData(2); + if(row == null) + row = this.properties.row; + if(col == null) + col = this.properties.column; + if(table == null || row == null || col == null) + return; + var row = table[Math.floor(Number(row))]; + if(row) + this.setOutputData(0, row[Math.floor(Number(col))] ); + else + this.setOutputData(0, null ); + }; + + LiteGraph.registerNodeType("basic/table[][]", TableElement); + function ObjectProperty() { this.addInput("obj", ""); this.addOutput("", ""); this.addProperty("value", ""); - this.widget = this.addWidget( - "text", - "prop.", - "", - this.setValue.bind(this) - ); + this.widget = this.addWidget("text","prop.","",this.setValue.bind(this) ); this.widgets_up = true; this.size = [140, 30]; this._value = null; @@ -738,6 +863,18 @@ LiteGraph.registerNodeType("basic/variable", Variable); + function length(v) { + if(v && v.length != null) + return Number(v.length); + return 0; + } + + LiteGraph.wrapFunctionAsNode( + "basic/length", + length, + ["*"], + "number" + ); function DownloadData() { this.size = [60, 30]; diff --git a/src/nodes/midi.js b/src/nodes/midi.js index f34678b9d..9d0908e4c 100644 --- a/src/nodes/midi.js +++ b/src/nodes/midi.js @@ -328,7 +328,7 @@ MIDIEvent.commands_reversed[MIDIEvent.commands[i]] = i; } - //MIDI wrapper + //MIDI wrapper, instantiate by MIDIIn and MIDIOut function MIDIInterface(on_ready, on_error) { if (!navigator.requestMIDIAccess) { this.error = "not suppoorted"; @@ -347,9 +347,12 @@ cc: [] }; - navigator - .requestMIDIAccess() - .then(this.onMIDISuccess.bind(this), this.onMIDIFailure.bind(this)); + this.input_ports = null; + this.input_ports_info = []; + this.output_ports = null; + this.output_ports_info = []; + + navigator.requestMIDIAccess().then(this.onMIDISuccess.bind(this), this.onMIDIFailure.bind(this)); } MIDIInterface.input = null; @@ -370,80 +373,34 @@ MIDIInterface.prototype.updatePorts = function() { var midi = this.midi; this.input_ports = midi.inputs; + this.input_ports_info = []; + this.output_ports = midi.outputs; + this.output_ports_info = []; + var num = 0; var it = this.input_ports.values(); var it_value = it.next(); while (it_value && it_value.done === false) { var port_info = it_value.value; - console.log( - "Input port [type:'" + - port_info.type + - "'] id:'" + - port_info.id + - "' manufacturer:'" + - port_info.manufacturer + - "' name:'" + - port_info.name + - "' version:'" + - port_info.version + - "'" - ); + this.input_ports_info.push(port_info); + console.log( "Input port [type:'" + port_info.type + "'] id:'" + port_info.id + "' manufacturer:'" + port_info.manufacturer + "' name:'" + port_info.name + "' version:'" + port_info.version + "'" ); num++; it_value = it.next(); } this.num_input_ports = num; num = 0; - this.output_ports = midi.outputs; var it = this.output_ports.values(); var it_value = it.next(); while (it_value && it_value.done === false) { var port_info = it_value.value; - console.log( - "Output port [type:'" + - port_info.type + - "'] id:'" + - port_info.id + - "' manufacturer:'" + - port_info.manufacturer + - "' name:'" + - port_info.name + - "' version:'" + - port_info.version + - "'" - ); + this.output_ports_info.push(port_info); + console.log( "Output port [type:'" + port_info.type + "'] id:'" + port_info.id + "' manufacturer:'" + port_info.manufacturer + "' name:'" + port_info.name + "' version:'" + port_info.version + "'" ); num++; it_value = it.next(); } this.num_output_ports = num; - - /* OLD WAY - for (var i = 0; i < this.input_ports.size; ++i) { - var input = this.input_ports.get(i); - if(!input) - continue; //sometimes it is null?! - console.log( "Input port [type:'" + input.type + "'] id:'" + input.id + - "' manufacturer:'" + input.manufacturer + "' name:'" + input.name + - "' version:'" + input.version + "'" ); - num++; - } - this.num_input_ports = num; - - - num = 0; - this.output_ports = midi.outputs; - for (var i = 0; i < this.output_ports.size; ++i) { - var output = this.output_ports.get(i); - if(!output) - continue; - console.log( "Output port [type:'" + output.type + "'] id:'" + output.id + - "' manufacturer:'" + output.manufacturer + "' name:'" + output.name + - "' version:'" + output.version + "'" ); - num++; - } - this.num_output_ports = num; - */ }; MIDIInterface.prototype.onMIDIFailure = function(msg) { @@ -493,7 +450,7 @@ return; } - var output_port = this.output_ports.get("output-" + port); + var output_port = this.output_ports_info[port];//this.output_ports.get("output-" + port); if (!output_port) { return; } @@ -540,10 +497,9 @@ if (name == "port") { var values = {}; - for (var i = 0; i < this._midi.input_ports.size; ++i) { - var input = this._midi.input_ports.get("input-" + i); - values[i] = - i + ".- " + input.name + " version:" + input.version; + for (var i = 0; i < this._midi.input_ports_info.length; ++i) { + var input = this._midi.input_ports_info[i]; + values[i] = i + ".- " + input.name + " version:" + input.version; } return { type: "enum", values: values }; } @@ -641,9 +597,10 @@ var that = this; new MIDIInterface(function(midi) { that._midi = midi; + that.widget.options.values = that.getMIDIOutputs(); }); - - this.addWidget("combo","Device",this.properties.port,{ property: "port", values: this.getMIDIOutputs.bind(this) }); + this.widget = this.addWidget("combo","Device",this.properties.port,{ property: "port", values: this.getMIDIOutputs.bind(this) }); + this.size = [340,60]; } LGMIDIOut.MIDIInterface = MIDIInterface; @@ -662,14 +619,20 @@ return { type: "enum", values: values }; } }; + LGMIDIOut.default_ports = {0:"unknown"}; LGMIDIOut.prototype.getMIDIOutputs = function() { var values = {}; - for (var i = 0; i < this._midi.output_ports.size; ++i) { - var output = this._midi.output_ports.get(i); - if(output) - values[i] = i + ".- " + output.name + " version:" + output.version; + if(!this._midi) + return LGMIDIOut.default_ports; + if(this._midi.output_ports_info) + for (var i = 0; i < this._midi.output_ports_info.length; ++i) { + var output = this._midi.output_ports_info[i]; + if(!output) + continue; + var name = i + ".- " + output.name + " version:" + output.version; + values[i] = name; } return values; } @@ -680,7 +643,7 @@ return; } if (event == "send") { - this._midi.sendMIDI(this.port, midi_event); + this._midi.sendMIDI(this.properties.port, midi_event); } this.trigger("midi", midi_event); }; diff --git a/src/nodes/strings.js b/src/nodes/strings.js index b84b3af0e..20b4cb9b8 100644 --- a/src/nodes/strings.js +++ b/src/nodes/strings.js @@ -15,8 +15,8 @@ LiteGraph.wrapFunctionAsNode( "string/compare", compare, - ["String", "String"], - "Boolean" + ["string", "string"], + "boolean" ); function concatenate(a, b) { @@ -32,8 +32,8 @@ LiteGraph.wrapFunctionAsNode( "string/concatenate", concatenate, - ["String", "String"], - "String" + ["string", "string"], + "string" ); function contains(a, b) { @@ -46,8 +46,8 @@ LiteGraph.wrapFunctionAsNode( "string/contains", contains, - ["String", "String"], - "Boolean" + ["string", "string"], + "boolean" ); function toUpperCase(a) { @@ -60,22 +60,33 @@ LiteGraph.wrapFunctionAsNode( "string/toUpperCase", toUpperCase, - ["String"], - "String" + ["string"], + "string" ); - function split(a, b) { - if (a != null && a.constructor === String) { - return a.split(b || " "); - } - return [a]; + function split(str, separator) { + if(separator == null) + separator = this.properties.separator; + if (str == null ) + return []; + if( str.constructor === String ) + return str.split(separator || " "); + else if( str.constructor === Array ) + { + var r = []; + for(var i = 0; i < str.length; ++i) + r[i] = str[i].split(separator || " "); + return r; + } + return null; } LiteGraph.wrapFunctionAsNode( "string/split", - toUpperCase, - ["String", "String"], - "Array" + split, + ["string,array", "string"], + "array", + { separator: "," } ); function toFixed(a) { @@ -88,8 +99,39 @@ LiteGraph.wrapFunctionAsNode( "string/toFixed", toFixed, - ["Number"], - "String", + ["number"], + "string", { precision: 0 } ); + + + function StringToTable() { + this.addInput("", "string"); + this.addOutput("table", "table"); + this.addOutput("rows", "number"); + this.addProperty("value", ""); + this.addProperty("separator", ","); + this._table = null; + } + + StringToTable.title = "toTable"; + StringToTable.desc = "Splits a string to table"; + + StringToTable.prototype.onExecute = function() { + var input = this.getInputData(0); + if(!input) + return; + var separator = this.properties.separator || ","; + if(input != this._str || separator != this._last_separator ) + { + this._last_separator = separator; + this._str = input; + this._table = input.split("\n").map(function(a){ return a.trim().split(separator)}); + } + this.setOutputData(0, this._table ); + this.setOutputData(1, this._table ? this._table.length : 0 ); + }; + + LiteGraph.registerNodeType("string/toTable", StringToTable); + })(this); From 03408f13df518436334901dc679cdeffe1ce8b6e Mon Sep 17 00:00:00 2001 From: Shan M Date: Mon, 6 Apr 2020 12:06:36 +0300 Subject: [PATCH 14/63] adds missing type unregisterNodeType --- src/litegraph.d.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/litegraph.d.ts b/src/litegraph.d.ts index 34315f36f..551601a11 100644 --- a/src/litegraph.d.ts +++ b/src/litegraph.d.ts @@ -228,6 +228,8 @@ export const LiteGraph: { createNode(type: string): T; /** Register a node class so it can be listed when the user wants to create a new one */ registerNodeType(type: string, base: { new (): LGraphNode }): void; + /** removes a node type from the system */ + unregisterNodeType(type: string): void; /** * Create a new node type by passing a function, it wraps it with a proper class and generates inputs according to the parameters of the function. * Useful to wrap simple methods that do not require properties, and that only process some input to generate an output. From 99e1d85f4f56905d07d51f68f2326a1f068a0dae Mon Sep 17 00:00:00 2001 From: tamat Date: Mon, 6 Apr 2020 12:03:37 +0200 Subject: [PATCH 15/63] 0.7.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5273c2a9e..573bfbf84 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "litegraph.js", - "version": "0.7.4", + "version": "0.7.5", "description": "A graph node editor similar to PD or UDK Blueprints, it works in a HTML5 Canvas and allow to exported graphs to be included in applications.", "main": "build/litegraph.js", "types": "src/litegraph.d.ts", From 28c0eec6c20b3c347c6ff294c7bdb6b0b620d550 Mon Sep 17 00:00:00 2001 From: tamat Date: Wed, 8 Apr 2020 17:14:32 +0200 Subject: [PATCH 16/63] fix --- build/litegraph.js | 196 ++------ build/litegraph.min.js | 1054 ++++++++++++++++++++-------------------- src/litegraph.js | 12 +- src/nodes/base.js | 47 +- src/nodes/math.js | 137 ------ 5 files changed, 627 insertions(+), 819 deletions(-) diff --git a/build/litegraph.js b/build/litegraph.js index f5861d749..743dbef0f 100644 --- a/build/litegraph.js +++ b/build/litegraph.js @@ -4585,14 +4585,12 @@ LGraphNode.prototype.executeAction = function(action) return; } - /* - if(this.graph) - this.graph.canvas = null; //remove old graph link to the canvas - this.graph = graph; - if(this.graph) - this.graph.canvas = this; - */ graph.attachCanvas(this); + + //remove the graph stack in case a subgraph was open + if (this._graph_stack) + this._graph_stack = null; + this.setDirty(true, true); }; @@ -11177,7 +11175,7 @@ if (typeof exports != "undefined") { }; Subgraph.prototype.serialize = function() { - var data = LGraphNode.prototype.serialize.call(this); + var data = LiteGraph.LGraphNode.prototype.serialize.call(this); data.subgraph = this.subgraph.serialize(); return data; }; @@ -11650,6 +11648,51 @@ if (typeof exports != "undefined") { LiteGraph.registerNodeType("basic/data", ConstantData); + //to store json objects + function ConstantArray() { + this.addInput("", ""); + this.addOutput("", "array"); + this.addProperty("value", ""); + this.widget = this.addWidget("text","array","","value"); + this.widgets_up = true; + this.size = [140, 30]; + this._value = null; + } + + ConstantArray.title = "Const Array"; + ConstantArray.desc = "Constant Array"; + + ConstantArray.prototype.onPropertyChanged = function(name, value) { + this.widget.value = value; + if (value == null || value == "") { + return; + } + + try { + this._value = JSON.parse(value); + this.boxcolor = "#AEA"; + } catch (err) { + this.boxcolor = "red"; + } + }; + + ConstantArray.prototype.onExecute = function() { + var v = this.getInputData(0); + if(v && v.length) + { + if(!this._value) + this._value = new Array(); + this._value.length = v.length; + for(var i = 0; i < v.length; ++i) + this._value[i] = v[i]; + } + this.setOutputData(0, this._value); + }; + + ConstantArray.prototype.setValue = ConstantNumber.prototype.setValue; + + LiteGraph.registerNodeType("basic/array", ConstantArray); + function ArrayElement() { this.addInput("array", "array,table,string"); this.addInput("index", "number"); @@ -14845,143 +14888,6 @@ if (typeof exports != "undefined") { LiteGraph.registerNodeType("math3d/xyzw-to-vec4", Math3DXYZWToVec4); - //if glMatrix is installed... - if (global.glMatrix) { - function Math3DQuaternion() { - this.addOutput("quat", "quat"); - this.properties = { x: 0, y: 0, z: 0, w: 1 }; - this._value = quat.create(); - } - - Math3DQuaternion.title = "Quaternion"; - Math3DQuaternion.desc = "quaternion"; - - Math3DQuaternion.prototype.onExecute = function() { - this._value[0] = this.properties.x; - this._value[1] = this.properties.y; - this._value[2] = this.properties.z; - this._value[3] = this.properties.w; - this.setOutputData(0, this._value); - }; - - LiteGraph.registerNodeType("math3d/quaternion", Math3DQuaternion); - - function Math3DRotation() { - this.addInputs([["degrees", "number"], ["axis", "vec3"]]); - this.addOutput("quat", "quat"); - this.properties = { angle: 90.0, axis: vec3.fromValues(0, 1, 0) }; - - this._value = quat.create(); - } - - Math3DRotation.title = "Rotation"; - Math3DRotation.desc = "quaternion rotation"; - - Math3DRotation.prototype.onExecute = function() { - var angle = this.getInputData(0); - if (angle == null) { - angle = this.properties.angle; - } - var axis = this.getInputData(1); - if (axis == null) { - axis = this.properties.axis; - } - - var R = quat.setAxisAngle(this._value, axis, angle * 0.0174532925); - this.setOutputData(0, R); - }; - - LiteGraph.registerNodeType("math3d/rotation", Math3DRotation); - - //Math3D rotate vec3 - function Math3DRotateVec3() { - this.addInputs([["vec3", "vec3"], ["quat", "quat"]]); - this.addOutput("result", "vec3"); - this.properties = { vec: [0, 0, 1] }; - } - - Math3DRotateVec3.title = "Rot. Vec3"; - Math3DRotateVec3.desc = "rotate a point"; - - Math3DRotateVec3.prototype.onExecute = function() { - var vec = this.getInputData(0); - if (vec == null) { - vec = this.properties.vec; - } - var quat = this.getInputData(1); - if (quat == null) { - this.setOutputData(vec); - } else { - this.setOutputData( - 0, - vec3.transformQuat(vec3.create(), vec, quat) - ); - } - }; - - LiteGraph.registerNodeType("math3d/rotate_vec3", Math3DRotateVec3); - - function Math3DMultQuat() { - this.addInputs([["A", "quat"], ["B", "quat"]]); - this.addOutput("A*B", "quat"); - - this._value = quat.create(); - } - - Math3DMultQuat.title = "Mult. Quat"; - Math3DMultQuat.desc = "rotate quaternion"; - - Math3DMultQuat.prototype.onExecute = function() { - var A = this.getInputData(0); - if (A == null) { - return; - } - var B = this.getInputData(1); - if (B == null) { - return; - } - - var R = quat.multiply(this._value, A, B); - this.setOutputData(0, R); - }; - - LiteGraph.registerNodeType("math3d/mult-quat", Math3DMultQuat); - - function Math3DQuatSlerp() { - this.addInputs([ - ["A", "quat"], - ["B", "quat"], - ["factor", "number"] - ]); - this.addOutput("slerp", "quat"); - this.addProperty("factor", 0.5); - - this._value = quat.create(); - } - - Math3DQuatSlerp.title = "Quat Slerp"; - Math3DQuatSlerp.desc = "quaternion spherical interpolation"; - - Math3DQuatSlerp.prototype.onExecute = function() { - var A = this.getInputData(0); - if (A == null) { - return; - } - var B = this.getInputData(1); - if (B == null) { - return; - } - var factor = this.properties.factor; - if (this.getInputData(2) != null) { - factor = this.getInputData(2); - } - - var R = quat.slerp(this._value, A, B, factor); - this.setOutputData(0, R); - }; - - LiteGraph.registerNodeType("math3d/quat-slerp", Math3DQuatSlerp); - } //glMatrix })(this); (function(global) { diff --git a/build/litegraph.min.js b/build/litegraph.min.js index e597b5473..d26101b08 100755 --- a/build/litegraph.min.js +++ b/build/litegraph.min.js @@ -1,36 +1,36 @@ -(function(y){function c(a){g.debug&&console.log("Graph created");this.list_of_graphcanvas=null;this.clear();a&&this.configure(a)}function l(a,b,d,h,f,g){this.id=a;this.type=b;this.origin_id=d;this.origin_slot=h;this.target_id=f;this.target_slot=g;this._data=null;this._pos=new Float32Array(2)}function p(a){this._ctor(a)}function k(a){this._ctor(a)}function w(a,b){this.offset=new Float32Array([0,0]);this.scale=1;this.max_scale=10;this.min_scale=0.1;this.onredraw=null;this.enabled=!0;this.last_mouse= +(function(C){function c(a){g.debug&&console.log("Graph created");this.list_of_graphcanvas=null;this.clear();a&&this.configure(a)}function m(a,b,d,h,f,s){this.id=a;this.type=b;this.origin_id=d;this.origin_slot=h;this.target_id=f;this.target_slot=s;this._data=null;this._pos=new Float32Array(2)}function p(a){this._ctor(a)}function k(a){this._ctor(a)}function v(a,b){this.offset=new Float32Array([0,0]);this.scale=1;this.max_scale=10;this.min_scale=0.1;this.onredraw=null;this.enabled=!0;this.last_mouse= [0,0];this.element=null;this.visible_area=new Float32Array(4);a&&(this.element=a,b||this.bindEvents(a))}function e(a,b,d){d=d||{};this.background_image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; -a&&a.constructor===String&&(a=document.querySelector(a));this.ds=new w;this.zoom_modify_alpha=!0;this.title_text_font=""+g.NODE_TEXT_SIZE+"px Arial";this.inner_text_font="normal "+g.NODE_SUBTEXT_SIZE+"px Arial";this.node_title_color=g.NODE_TITLE_COLOR;this.default_link_color=g.LINK_COLOR;this.default_connection_color={input_off:"#778",input_on:"#7F7",output_off:"#778",output_on:"#7F7"};this.highquality_render=!0;this.use_gradients=!1;this.editor_alpha=1;this.pause_rendering=!1;this.clear_background= +a&&a.constructor===String&&(a=document.querySelector(a));this.ds=new v;this.zoom_modify_alpha=!0;this.title_text_font=""+g.NODE_TEXT_SIZE+"px Arial";this.inner_text_font="normal "+g.NODE_SUBTEXT_SIZE+"px Arial";this.node_title_color=g.NODE_TITLE_COLOR;this.default_link_color=g.LINK_COLOR;this.default_connection_color={input_off:"#778",input_on:"#7F7",output_off:"#778",output_on:"#7F7"};this.highquality_render=!0;this.use_gradients=!1;this.editor_alpha=1;this.pause_rendering=!1;this.clear_background= !0;this.read_only=!1;this.render_only_selected=!0;this.live_mode=!1;this.allow_searchbox=this.allow_interaction=this.allow_dragnodes=this.allow_dragcanvas=this.show_info=!0;this.drag_mode=this.allow_reconnect_links=!1;this.filter=this.dragging_rectangle=null;this.always_render_background=!1;this.render_canvas_border=this.render_shadows=!0;this.render_connections_shadows=!1;this.render_connections_border=!0;this.render_connection_arrows=this.render_curved_connections=!1;this.render_collapsed_slots= !0;this.render_execution_order=!1;this.render_link_tooltip=this.render_title_colored=!0;this.links_render_mode=g.SPLINE_LINK;this.canvas_mouse=[0,0];this.onSelectionChange=this.onNodeMoved=this.onDrawLinkTooltip=this.onDrawOverlay=this.onDrawForeground=this.onDrawBackground=this.onMouse=this.onSearchBoxSelection=this.onSearchBox=null;this.connections_width=3;this.round_radius=8;this.over_link_center=this.node_widget=this.current_node=null;this.last_mouse_position=[0,0];this.visible_area=this.ds.visible_area; -this.visible_links=[];b&&b.attachCanvas(this);this.setCanvas(a);this.clear();d.skip_render||this.startRendering();this.autoresize=d.autoresize}function C(a,b){return Math.sqrt((b[0]-a[0])*(b[0]-a[0])+(b[1]-a[1])*(b[1]-a[1]))}function z(a,b,d,h,f,g){return da&&hb?!0:!1}function u(a,b){var d=a[0]+a[2],h=a[1]+a[3],f=b[1]+b[3];return a[0]>b[0]+b[2]||a[1]>f||da&&hb?!0:!1}function y(a,b){var d=a[0]+a[2],h=a[1]+a[3],f=b[1]+b[3];return a[0]>b[0]+b[2]||a[1]>f||dc.width-e.width-10&&(f=c.width-e.width-10);q>c.height-e.height-10&&(q=c.height-e.height-10)}g.style.left=f+"px";g.style.top=q+"px";b.scale&&(g.style.transform="scale("+b.scale+")")}function B(a){this.points=a;this.nearest=this.selected=-1;this.size=null;this.must_update=!0;this.margin=5}var g=y.LiteGraph={VERSION:0.4,CANVAS_GRID_SIZE:10,NODE_TITLE_HEIGHT:30,NODE_TITLE_TEXT_Y:20,NODE_SLOT_HEIGHT:20,NODE_WIDGET_HEIGHT:20,NODE_WIDTH:140,NODE_MIN_WIDTH:50,NODE_COLLAPSED_RADIUS:10, +b.event=null);var s=document.createElement("div");s.className="litegraph litecontextmenu litemenubar-panel";b.className&&(s.className+=" "+b.className);s.style.minWidth=100;s.style.minHeight=100;s.style.pointerEvents="none";setTimeout(function(){s.style.pointerEvents="auto"},100);s.addEventListener("mouseup",function(a){a.preventDefault();return!0},!0);s.addEventListener("contextmenu",function(a){if(2!=a.button)return!1;a.preventDefault();return!1},!0);s.addEventListener("mousedown",function(a){if(2== +a.button)return h.close(),a.preventDefault(),!0},!0);b.scroll_speed||(b.scroll_speed=0.1);s.addEventListener("wheel",d,!0);s.addEventListener("mousewheel",d,!0);this.root=s;b.title&&(f=document.createElement("div"),f.className="litemenu-title",f.innerHTML=b.title,s.appendChild(f));var f=0,g;for(g in a){var c=a.constructor==Array?a[g]:g;null!=c&&c.constructor!==String&&(c=void 0===c.content?String(c):c.content);this.addItem(c,a[g],b);f++}s.addEventListener("mouseleave",function(a){h.lock||(s.closing_timer&& +clearTimeout(s.closing_timer),s.closing_timer=setTimeout(h.close.bind(h,a),500))});s.addEventListener("mouseenter",function(a){s.closing_timer&&clearTimeout(s.closing_timer)});g=document;b.event&&(g=b.event.target.ownerDocument);g||(g=document);g.fullscreenElement?g.fullscreenElement.appendChild(s):g.body.appendChild(s);f=b.left||0;g=b.top||0;if(b.event){f=b.event.clientX-10;g=b.event.clientY-10;b.title&&(g-=20);b.parentMenu&&(f=b.parentMenu.root.getBoundingClientRect(),f=f.left+f.width);var c=document.body.getBoundingClientRect(), +e=s.getBoundingClientRect();f>c.width-e.width-10&&(f=c.width-e.width-10);g>c.height-e.height-10&&(g=c.height-e.height-10)}s.style.left=f+"px";s.style.top=g+"px";b.scale&&(s.style.transform="scale("+b.scale+")")}function A(a){this.points=a;this.nearest=this.selected=-1;this.size=null;this.must_update=!0;this.margin=5}var g=C.LiteGraph={VERSION:0.4,CANVAS_GRID_SIZE:10,NODE_TITLE_HEIGHT:30,NODE_TITLE_TEXT_Y:20,NODE_SLOT_HEIGHT:20,NODE_WIDGET_HEIGHT:20,NODE_WIDTH:140,NODE_MIN_WIDTH:50,NODE_COLLAPSED_RADIUS:10, NODE_COLLAPSED_WIDTH:80,NODE_TITLE_COLOR:"#999",NODE_TEXT_SIZE:14,NODE_TEXT_COLOR:"#AAA",NODE_SUBTEXT_SIZE:12,NODE_DEFAULT_COLOR:"#333",NODE_DEFAULT_BGCOLOR:"#353535",NODE_DEFAULT_BOXCOLOR:"#666",NODE_DEFAULT_SHAPE:"box",DEFAULT_SHADOW_COLOR:"rgba(0,0,0,0.5)",DEFAULT_GROUP_FONT:24,WIDGET_BGCOLOR:"#222",WIDGET_OUTLINE_COLOR:"#666",WIDGET_TEXT_COLOR:"#DDD",WIDGET_SECONDARY_TEXT_COLOR:"#999",LINK_COLOR:"#9A9",EVENT_LINK_COLOR:"#A86",CONNECTING_LINK_COLOR:"#AFA",MAX_NUMBER_OF_NODES:1E3,DEFAULT_POSITION:[100, 100],VALID_SHAPES:["default","box","round","card"],BOX_SHAPE:1,ROUND_SHAPE:2,CIRCLE_SHAPE:3,CARD_SHAPE:4,ARROW_SHAPE:5,INPUT:1,OUTPUT:2,EVENT:-1,ACTION:-1,ALWAYS:0,ON_EVENT:1,NEVER:2,ON_TRIGGER:3,UP:1,DOWN:2,LEFT:3,RIGHT:4,CENTER:5,STRAIGHT_LINK:0,LINEAR_LINK:1,SPLINE_LINK:2,NORMAL_TITLE:0,NO_TITLE:1,TRANSPARENT_TITLE:2,AUTOHIDE_TITLE:3,proxy:null,node_images_path:"",debug:!1,catch_exceptions:!0,throw_errors:!0,allow_scripts:!1,registered_node_types:{},node_types_by_file_extension:{},Nodes:{},searchbox_extras:{}, registerNodeType:function(a,b){if(!b.prototype)throw"Cannot register a simple object, it must be a class with a prototype";b.type=a;g.debug&&console.log("Node registered: "+a);a.split("/");var d=b.name,h=a.lastIndexOf("/");b.category=a.substr(0,h);b.title||(b.title=d);if(b.prototype)for(var f in p.prototype)b.prototype[f]||(b.prototype[f]=p.prototype[f]);if(h=this.registered_node_types[a])console.log("replacing node type: "+a);else if(Object.hasOwnProperty(b.prototype,"shape")||Object.defineProperty(b.prototype, -"shape",{set:function(a){switch(a){case "default":delete this._shape;break;case "box":this._shape=g.BOX_SHAPE;break;case "round":this._shape=g.ROUND_SHAPE;break;case "circle":this._shape=g.CIRCLE_SHAPE;break;case "card":this._shape=g.CARD_SHAPE;break;default:this._shape=a}},get:function(a){return this._shape},enumerable:!0,configurable:!0}),b.prototype.onPropertyChange&&console.warn("LiteGraph node class "+a+" has onPropertyChange method, it must be called onPropertyChanged with d at the end"),b.supported_extensions)for(f in b.supported_extensions){var E= -b.supported_extensions[f];E&&E.constructor===String&&(this.node_types_by_file_extension[E.toLowerCase()]=b)}this.registered_node_types[a]=b;b.constructor.name&&(this.Nodes[d]=b);if(g.onNodeTypeRegistered)g.onNodeTypeRegistered(a,b);if(h&&g.onNodeTypeReplaced)g.onNodeTypeReplaced(a,b,h)},unregisterNodeType:function(a){var b=a.constructor===String?this.registered_node_types[a]:a;if(!b)throw"node type not found: "+a;delete this.registered_node_types[b.type];b.constructor.name&&delete this.Nodes[b.constructor.name]}, -wrapFunctionAsNode:function(a,b,d,h,f){for(var E=Array(b.length),q="",c=g.getParameterNames(b),e=0;eq&&(q=f.size[0]),c+=f.size[1]+a+g.NODE_TITLE_HEIGHT;b+=q+a}this.setDirtyCanvas(!0,!0)};c.prototype.getTime=function(){return this.globaltime}; -c.prototype.getFixedTime=function(){return this.fixedtime};c.prototype.getElapsedTime=function(){return this.elapsed_time};c.prototype.sendEventToAllNodes=function(a,b,d){d=d||g.ALWAYS;var h=this._nodes_in_order?this._nodes_in_order:this._nodes;if(h)for(var f=0,E=h.length;fr&&(r=f.size[0]),c+=f.size[1]+a+g.NODE_TITLE_HEIGHT;b+=r+a}this.setDirtyCanvas(!0,!0)};c.prototype.getTime=function(){return this.globaltime}; +c.prototype.getFixedTime=function(){return this.fixedtime};c.prototype.getElapsedTime=function(){return this.elapsed_time};c.prototype.sendEventToAllNodes=function(a,b,d){d=d||g.ALWAYS;var h=this._nodes_in_order?this._nodes_in_order:this._nodes;if(h)for(var f=0,s=h.length;f=g.MAX_NUMBER_OF_NODES)throw"LiteGraph: max number of nodes in a graph reached"; null==a.id||-1==a.id?a.id=++this.last_node_id:this.last_node_ida.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});this.id=-1;this.type=null;this.inputs=[];this.outputs=[];this.connections=[];this.properties={};this.properties_info=[];this.flags={}};p.prototype.configure=function(a){this.graph&&this.graph._version++; for(var b in a)if("properties"==b)for(var d in a.properties){if(this.properties[d]=a.properties[d],this.onPropertyChanged)this.onPropertyChanged(d,a.properties[d])}else null!=a[b]&&("object"==typeof a[b]?this[b]&&this[b].configure?this[b].configure(a[b]):this[b]=g.cloneObject(a[b],this[b]):this[b]=a[b]);a.title||(this.title=this.constructor.title);if(this.onConnectionsChange){if(this.inputs)for(d=0;d=this.outputs.length?null:this.outputs[a]._data};p.prototype.getOutputInfo=function(a){return this.outputs?a=this.outputs.length)return null;a=this.outputs[a];if(!a.links||0==a.links.length)return null;for(var b=[],d=0;d=this.outputs.length)return g.debug&&console.log("Connect: Error, slot number not found"),null;b&&b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"target node is null";if(b==this)return null;if(d.constructor===String){if(d=b.findInputSlot(d),-1==d)return g.debug&&console.log("Connect: Error, no slot of name "+d),null}else{if(d===g.EVENT)return null;if(!b.inputs||d>=b.inputs.length)return g.debug&&console.log("Connect: Error, slot number not found"),null}null!=b.inputs[d].link&& -b.disconnectInput(d);var h=this.outputs[a];if(b.onConnectInput&&!1===b.onConnectInput(d,h.type,h))return null;var f=b.inputs[d],c=null;if(g.isValidConnection(h.type,f.type)){c=new l(++this.graph.last_link_id,f.type,this.id,a,b.id,d);this.graph.links[c.id]=c;null==h.links&&(h.links=[]);h.links.push(c.id);b.inputs[d].link=c.id;this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(g.OUTPUT,a,!0,c,h);if(b.onConnectionsChange)b.onConnectionsChange(g.INPUT,d,!0,c,f);this.graph&& -this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(g.INPUT,b,d,this,a),this.graph.onNodeConnectionChange(g.OUTPUT,this,a,b,d))}this.setDirtyCanvas(!1,!0);this.graph.connectionChange(this,c);return c};p.prototype.disconnectOutput=function(a,b){if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return g.debug&&console.log("Connect: Error, no slot of name "+a),!1}else if(!this.outputs||a>=this.outputs.length)return g.debug&&console.log("Connect: Error, slot number not found"), -!1;var d=this.outputs[a];if(!d||!d.links||0==d.links.length)return!1;if(b){b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"Target Node not found";for(var h=0,f=d.links.length;h=this.inputs.length)return g.debug&&console.log("Connect: Error, slot number not found"),!1;var b=this.inputs[a];if(!b)return!1;var d=this.inputs[a].link;this.inputs[a].link=null;var h=this.graph.links[d];if(h){var f=this.graph.getNodeById(h.origin_id);if(!f)return!1;var c=f.outputs[h.origin_slot];if(!c||!c.links||0==c.links.length)return!1;for(var q=0,e=c.links.length;q< -e;q++)if(c.links[q]==d){c.links.splice(q,1);break}delete this.graph.links[d];this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(g.INPUT,a,!1,h,b);if(f.onConnectionsChange)f.onConnectionsChange(g.OUTPUT,q,!1,h,c);this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(g.OUTPUT,f,q),this.graph.onNodeConnectionChange(g.INPUT,this,a))}this.setDirtyCanvas(!1,!0);this.graph.connectionChange(this);return!0};p.prototype.getConnectionPos=function(a, +b.disconnectInput(d);var h=this.outputs[a];if(b.onConnectInput&&!1===b.onConnectInput(d,h.type,h))return null;var f=b.inputs[d],s=null;if(g.isValidConnection(h.type,f.type)){s=new m(++this.graph.last_link_id,f.type,this.id,a,b.id,d);this.graph.links[s.id]=s;null==h.links&&(h.links=[]);h.links.push(s.id);b.inputs[d].link=s.id;this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(g.OUTPUT,a,!0,s,h);if(b.onConnectionsChange)b.onConnectionsChange(g.INPUT,d,!0,s,f);this.graph&& +this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(g.INPUT,b,d,this,a),this.graph.onNodeConnectionChange(g.OUTPUT,this,a,b,d))}this.setDirtyCanvas(!1,!0);this.graph.connectionChange(this,s);return s};p.prototype.disconnectOutput=function(a,b){if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return g.debug&&console.log("Connect: Error, no slot of name "+a),!1}else if(!this.outputs||a>=this.outputs.length)return g.debug&&console.log("Connect: Error, slot number not found"), +!1;var d=this.outputs[a];if(!d||!d.links||0==d.links.length)return!1;if(b){b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"Target Node not found";for(var h=0,f=d.links.length;h=this.inputs.length)return g.debug&&console.log("Connect: Error, slot number not found"),!1;var b=this.inputs[a];if(!b)return!1;var d=this.inputs[a].link;this.inputs[a].link=null;var h=this.graph.links[d];if(h){var f=this.graph.getNodeById(h.origin_id);if(!f)return!1;var s=f.outputs[h.origin_slot];if(!s||!s.links||0==s.links.length)return!1;for(var r=0,c=s.links.length;r< +c;r++)if(s.links[r]==d){s.links.splice(r,1);break}delete this.graph.links[d];this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(g.INPUT,a,!1,h,b);if(f.onConnectionsChange)f.onConnectionsChange(g.OUTPUT,r,!1,h,s);this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(g.OUTPUT,f,r),this.graph.onNodeConnectionChange(g.INPUT,this,a))}this.setDirtyCanvas(!1,!0);this.graph.connectionChange(this);return!0};p.prototype.getConnectionPos=function(a, b,d){d=d||new Float32Array(2);var h=0;a&&this.inputs&&(h=this.inputs.length);!a&&this.outputs&&(h=this.outputs.length);var f=0.5*g.NODE_SLOT_HEIGHT;if(this.flags.collapsed)return b=this._collapsed_width||g.NODE_COLLAPSED_WIDTH,this.horizontal?(d[0]=this.pos[0]+0.5*b,d[1]=a?this.pos[1]-g.NODE_TITLE_HEIGHT:this.pos[1]):(d[0]=a?this.pos[0]:this.pos[0]+b,d[1]=this.pos[1]-0.5*g.NODE_TITLE_HEIGHT),d;if(a&&-1==b)return d[0]=this.pos[0]+0.5*g.NODE_TITLE_HEIGHT,d[1]=this.pos[1]+0.5*g.NODE_TITLE_HEIGHT,d;if(a&& h>b&&this.inputs[b].pos)return d[0]=this.pos[0]+this.inputs[b].pos[0],d[1]=this.pos[1]+this.inputs[b].pos[1],d;if(!a&&h>b&&this.outputs[b].pos)return d[0]=this.pos[0]+this.outputs[b].pos[0],d[1]=this.pos[1]+this.outputs[b].pos[1],d;if(this.horizontal)return d[0]=this.pos[0]+this.size[0]/h*(b+0.5),d[1]=a?this.pos[1]-g.NODE_TITLE_HEIGHT:this.pos[1]+this.size[1],d;d[0]=a?this.pos[0]+f:this.pos[0]+this.size[0]+1-f;d[1]=this.pos[1]+(b+0.7)*g.NODE_SLOT_HEIGHT+(this.constructor.slot_start_y||0);return d}; p.prototype.alignToGrid=function(){this.pos[0]=g.CANVAS_GRID_SIZE*Math.round(this.pos[0]/g.CANVAS_GRID_SIZE);this.pos[1]=g.CANVAS_GRID_SIZE*Math.round(this.pos[1]/g.CANVAS_GRID_SIZE)};p.prototype.trace=function(a){this.console||(this.console=[]);this.console.push(a);this.console.length>p.MAX_CONSOLE&&this.console.shift();this.graph.onNodeTrace(this,a)};p.prototype.setDirtyCanvas=function(a,b){this.graph&&this.graph.sendActionToCanvas("setDirty",[a,b])};p.prototype.loadImage=function(a){var b=new Image; b.src=g.node_images_path+a;b.ready=!1;var d=this;b.onload=function(){this.ready=!0;d.setDirtyCanvas(!0)};return b};p.prototype.captureInput=function(a){if(this.graph&&this.graph.list_of_graphcanvas)for(var b=this.graph.list_of_graphcanvas,d=0;da.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});Object.defineProperty(this,"size",{set:function(a){!a||2>a.length||(this._size[0]=Math.max(140,a[0]),this._size[1]=Math.max(80,a[1]))},get:function(){return this._size},enumerable:!0})};k.prototype.configure=function(a){this.title=a.title;this._bounding.set(a.bounding);this.color=a.color; this.font=a.font};k.prototype.serialize=function(){var a=this._bounding;return{title:this.title,bounding:[Math.round(a[0]),Math.round(a[1]),Math.round(a[2]),Math.round(a[3])],color:this.color,font:this.font}};k.prototype.move=function(a,b,d){this._pos[0]+=a;this._pos[1]+=b;if(!d)for(d=0;dthis.max_scale&&(a=this.max_scale);if(a!=this.scale&&this.element){var d=this.element.getBoundingClientRect();if(d){b=b||[0.5*d.width,0.5*d.height];d=this.convertCanvasToOffset(b);this.scale=a;0.01>Math.abs(this.scale-1)&&(this.scale=1);var h=this.convertCanvasToOffset(b),d=[h[0]-d[0],h[1]-d[1]];this.offset[0]+=d[0];this.offset[1]+=d[1];if(this.onredraw)this.onredraw(this)}}}; -w.prototype.changeDeltaScale=function(a,b){this.changeScale(this.scale*a,b)};w.prototype.reset=function(){this.scale=1;this.offset[0]=0;this.offset[1]=0};y.LGraphCanvas=g.LGraphCanvas=e;e.link_type_colors={"-1":g.EVENT_LINK_COLOR,number:"#AAA",node:"#DCA"};e.gradients={};e.prototype.clear=function(){this.fps=this.render_time=this.last_draw_time=this.frame=0;this.dragging_rectangle=null;this.selected_nodes={};this.selected_group=null;this.visible_nodes=[];this.connecting_node=this.node_capturing_input= -this.node_over=this.node_dragged=null;this.highlighted_links={};this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_widget=this.node_in_panel=this.dirty_area=null;this.last_mouse=[0,0];this.last_mouseclick=0;this.visible_area.set([0,0,0,0]);if(this.onClear)this.onClear()};e.prototype.setGraph=function(a,b){this.graph!=a&&(b||this.clear(),!a&&this.graph?this.graph.detachCanvas(this):(a.attachCanvas(this),this.setDirty(!0,!0)))};e.prototype.openSubgraph=function(a){if(!a)throw"graph cannot be null"; +h;this.last_mouse[1]=d;a.preventDefault();a.stopPropagation();return!1}};v.prototype.toCanvasContext=function(a){a.scale(this.scale,this.scale);a.translate(this.offset[0],this.offset[1])};v.prototype.convertOffsetToCanvas=function(a){return[(a[0]+this.offset[0])*this.scale,(a[1]+this.offset[1])*this.scale]};v.prototype.convertCanvasToOffset=function(a,b){b=b||[0,0];b[0]=a[0]/this.scale-this.offset[0];b[1]=a[1]/this.scale-this.offset[1];return b};v.prototype.mouseDrag=function(a,b){this.offset[0]+= +a/this.scale;this.offset[1]+=b/this.scale;if(this.onredraw)this.onredraw(this)};v.prototype.changeScale=function(a,b){athis.max_scale&&(a=this.max_scale);if(a!=this.scale&&this.element){var d=this.element.getBoundingClientRect();if(d){b=b||[0.5*d.width,0.5*d.height];d=this.convertCanvasToOffset(b);this.scale=a;0.01>Math.abs(this.scale-1)&&(this.scale=1);var h=this.convertCanvasToOffset(b),d=[h[0]-d[0],h[1]-d[1]];this.offset[0]+=d[0];this.offset[1]+=d[1];if(this.onredraw)this.onredraw(this)}}}; +v.prototype.changeDeltaScale=function(a,b){this.changeScale(this.scale*a,b)};v.prototype.reset=function(){this.scale=1;this.offset[0]=0;this.offset[1]=0};C.LGraphCanvas=g.LGraphCanvas=e;e.link_type_colors={"-1":g.EVENT_LINK_COLOR,number:"#AAA",node:"#DCA"};e.gradients={};e.prototype.clear=function(){this.fps=this.render_time=this.last_draw_time=this.frame=0;this.dragging_rectangle=null;this.selected_nodes={};this.selected_group=null;this.visible_nodes=[];this.connecting_node=this.node_capturing_input= +this.node_over=this.node_dragged=null;this.highlighted_links={};this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_widget=this.node_in_panel=this.dirty_area=null;this.last_mouse=[0,0];this.last_mouseclick=0;this.visible_area.set([0,0,0,0]);if(this.onClear)this.onClear()};e.prototype.setGraph=function(a,b){this.graph!=a&&(b||this.clear(),!a&&this.graph?this.graph.detachCanvas(this):(a.attachCanvas(this),this._graph_stack&&(this._graph_stack=null),this.setDirty(!0,!0)))};e.prototype.openSubgraph=function(a){if(!a)throw"graph cannot be null"; if(this.graph==a)throw"graph cannot be the same";this.clear();this.graph&&(this._graph_stack||(this._graph_stack=[]),this._graph_stack.push(this.graph));a.attachCanvas(this);this.setDirty(!0,!0)};e.prototype.closeSubgraph=function(){if(this._graph_stack&&0!=this._graph_stack.length){var a=this.graph._subgraph_node,b=this._graph_stack.pop();this.selected_nodes={};this.highlighted_links={};b.attachCanvas(this);this.setDirty(!0,!0);a&&(this.centerOnNode(a),this.selectNodes([a]))}};e.prototype.getCurrentGraph= function(){return this.graph};e.prototype.setCanvas=function(a,b){if(a&&a.constructor===String&&(a=document.getElementById(a),!a))throw"Error creating LiteGraph canvas: Canvas not found";if(a!==this.canvas&&(!a&&this.canvas&&(b||this.unbindEvents()),this.canvas=a,this.ds.element=a)){a.className+=" lgraphcanvas";a.data=this;a.tabindex="1";this.bgcanvas=null;this.bgcanvas||(this.bgcanvas=document.createElement("canvas"),this.bgcanvas.width=this.canvas.width,this.bgcanvas.height=this.canvas.height); if(null==a.getContext){if("canvas"!=a.localName)throw"Element supplied for LGraphCanvas must be a element, you passed a "+a.localName;throw"This browser doesn't support Canvas";}null==(this.ctx=a.getContext("2d"))&&(a.webgl_enabled||console.warn("This canvas seems to be WebGL, enabling WebGL renderer"),this.enableWebGL());this._mousemove_callback=this.processMouseMove.bind(this);this._mouseup_callback=this.processMouseUp.bind(this);b||this.bindEvents()}};e.prototype._doNothing=function(a){a.preventDefault(); @@ -108,42 +108,42 @@ this.canvas.removeEventListener("DOMMouseScroll",this._mousewheel_callback);this this.canvas.removeEventListener("touchend",this.touchHandler);this.canvas.removeEventListener("touchcancel",this.touchHandler);this._ondrop_callback=this._key_callback=this._mousewheel_callback=this._mousedown_callback=null;this._events_binded=!1}else console.warn("LGraphCanvas: no events binded")};e.getFileExtension=function(a){var b=a.indexOf("?");-1!=b&&(a=a.substr(0,b));b=a.lastIndexOf(".");return-1==b?"":a.substr(b+1).toLowerCase()};e.prototype.enableWebGL=function(){if(void 0===typeof GL)throw"litegl.js must be included to use a WebGL canvas"; if(void 0===typeof enableWebGLCanvas)throw"webglCanvas.js must be included to use this feature";this.gl=this.ctx=enableWebGLCanvas(this.canvas);this.ctx.webgl=!0;this.bgcanvas=this.canvas;this.bgctx=this.gl;this.canvas.webgl_enabled=!0};e.prototype.setDirty=function(a,b){a&&(this.dirty_canvas=!0);b&&(this.dirty_bgcanvas=!0)};e.prototype.getCanvasWindow=function(){if(!this.canvas)return window;var a=this.canvas.ownerDocument;return a.defaultView||a.parentWindow};e.prototype.startRendering=function(){function a(){this.pause_rendering|| this.draw();var b=this.getCanvasWindow();this.is_rendering&&b.requestAnimationFrame(a.bind(this))}this.is_rendering||(this.is_rendering=!0,a.call(this))};e.prototype.stopRendering=function(){this.is_rendering=!1};e.prototype.processMouseDown=function(a){if(this.graph){this.adjustMouseEvent(a);var b=this.getCanvasWindow();e.active_canvas=this;this.canvas.removeEventListener("mousemove",this._mousemove_callback);b.document.addEventListener("mousemove",this._mousemove_callback,!0);b.document.addEventListener("mouseup", -this._mouseup_callback,!0);var d=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes,5),h=!1,f=300>g.getTime()-this.last_mouseclick;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;this.canvas.focus();g.closeAllContextMenus(b);if(!this.onMouse||!0!=this.onMouse(a)){if(1==a.which){a.ctrlKey&&(this.dragging_rectangle=new Float32Array(4),this.dragging_rectangle[0]=a.canvasX,this.dragging_rectangle[1]=a.canvasY,this.dragging_rectangle[2]=1,this.dragging_rectangle[3]=1,h=!0);var c= -!1;if(d&&this.allow_interaction&&!h&&!this.read_only){this.live_mode||d.flags.pinned||this.bringToFront(d);if(!this.connecting_node&&!d.flags.collapsed&&!this.live_mode)if(!h&&!1!==d.resizable&&z(a.canvasX,a.canvasY,d.pos[0]+d.size[0]-5,d.pos[1]+d.size[1]-5,10,10))this.resizing_node=d,this.canvas.style.cursor="se-resize",h=!0;else{if(d.outputs)for(var q=0,m=d.outputs.length;qc[0]+4||a.canvasYc[1]+4)){this.showLinkMenu(d, -a);this.over_link_center=null;break}this.selected_group=this.graph.getGroupOnPos(a.canvasX,a.canvasY);this.selected_group_resizing=!1;this.selected_group&&!this.read_only&&(a.ctrlKey&&(this.dragging_rectangle=null),10>C([a.canvasX,a.canvasY],[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])*this.ds.scale?this.selected_group_resizing=!0:this.selected_group.recomputeInsideNodes());f&&!this.read_only&&this.allow_searchbox&&this.showSearchBox(a); -c=!0}!h&&c&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}else 2!=a.which&&3==a.which&&(this.read_only||this.processContextMenu(d,a));this.last_mouse[0]=a.localX;this.last_mouse[1]=a.localY;this.last_mouseclick=g.getTime();this.last_mouse_dragging=!0;this.graph.change();(!b.document.activeElement||"input"!=b.document.activeElement.nodeName.toLowerCase()&&"textarea"!=b.document.activeElement.nodeName.toLowerCase())&&a.preventDefault();a.stopPropagation();if(this.onMouseDown)this.onMouseDown(a); +this._mouseup_callback,!0);var d=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes,5),h=!1,f=300>g.getTime()-this.last_mouseclick;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;this.canvas.focus();g.closeAllContextMenus(b);if(!this.onMouse||!0!=this.onMouse(a)){if(1==a.which){a.ctrlKey&&(this.dragging_rectangle=new Float32Array(4),this.dragging_rectangle[0]=a.canvasX,this.dragging_rectangle[1]=a.canvasY,this.dragging_rectangle[2]=1,this.dragging_rectangle[3]=1,h=!0);var s= +!1;if(d&&this.allow_interaction&&!h&&!this.read_only){this.live_mode||d.flags.pinned||this.bringToFront(d);if(!this.connecting_node&&!d.flags.collapsed&&!this.live_mode)if(!h&&!1!==d.resizable&&z(a.canvasX,a.canvasY,d.pos[0]+d.size[0]-5,d.pos[1]+d.size[1]-5,10,10))this.resizing_node=d,this.canvas.style.cursor="se-resize",h=!0;else{if(d.outputs)for(var r=0,c=d.outputs.length;rs[0]+4||a.canvasYs[1]+4)){this.showLinkMenu(d, +a);this.over_link_center=null;break}this.selected_group=this.graph.getGroupOnPos(a.canvasX,a.canvasY);this.selected_group_resizing=!1;this.selected_group&&!this.read_only&&(a.ctrlKey&&(this.dragging_rectangle=null),10>E([a.canvasX,a.canvasY],[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])*this.ds.scale?this.selected_group_resizing=!0:this.selected_group.recomputeInsideNodes());f&&!this.read_only&&this.allow_searchbox&&this.showSearchBox(a); +s=!0}!h&&s&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}else 2!=a.which&&3==a.which&&(this.read_only||this.processContextMenu(d,a));this.last_mouse[0]=a.localX;this.last_mouse[1]=a.localY;this.last_mouseclick=g.getTime();this.last_mouse_dragging=!0;this.graph.change();(!b.document.activeElement||"input"!=b.document.activeElement.nodeName.toLowerCase()&&"textarea"!=b.document.activeElement.nodeName.toLowerCase())&&a.preventDefault();a.stopPropagation();if(this.onMouseDown)this.onMouseDown(a); return!1}}};e.prototype.processMouseMove=function(a){this.autoresize&&this.resize();if(this.graph){e.active_canvas=this;this.adjustMouseEvent(a);var b=[a.localX,a.localY],d=[b[0]-this.last_mouse[0],b[1]-this.last_mouse[1]];this.last_mouse=b;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;a.dragging=this.last_mouse_dragging;this.node_widget&&(this.processNodeWidgets(this.node_widget[0],this.canvas_mouse,a,this.node_widget[1]),this.dirty_canvas=!0);if(this.dragging_rectangle)this.dragging_rectangle[2]= a.canvasX-this.dragging_rectangle[0],this.dragging_rectangle[3]=a.canvasY-this.dragging_rectangle[1],this.dirty_canvas=!0;else if(this.selected_group&&!this.read_only)this.selected_group_resizing?this.selected_group.size=[a.canvasX-this.selected_group.pos[0],a.canvasY-this.selected_group.pos[1]]:(this.selected_group.move(d[0]/this.ds.scale,d[1]/this.ds.scale,a.ctrlKey),this.selected_group._nodes.length&&(this.dirty_canvas=!0)),this.dirty_bgcanvas=!0;else if(this.dragging_canvas)this.ds.offset[0]+= d[0]/this.ds.scale,this.ds.offset[1]+=d[1]/this.ds.scale,this.dirty_bgcanvas=this.dirty_canvas=!0;else if(this.allow_interaction&&!this.read_only){this.connecting_node&&(this.dirty_canvas=!0);for(var h=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes),b=0,f=this.graph._nodes.length;bq[0]+4||a.canvasYq[1]+4)){f=c;break}}f!=this.over_link_center&&(this.over_link_center=f,this.dirty_canvas=!0);this.canvas&&(this.canvas.style.cursor="")}if(this.node_capturing_input&&this.node_capturing_input!=h&&this.node_capturing_input.onMouseMove)this.node_capturing_input.onMouseMove(a, +!0}if(h){if(!h.mouseOver&&(h.mouseOver=!0,this.node_over=h,this.dirty_canvas=!0,h.onMouseEnter))h.onMouseEnter(a);if(h.onMouseMove)h.onMouseMove(a,[a.canvasX-h.pos[0],a.canvasY-h.pos[1]],this);if(this.connecting_node&&(f=this._highlight_input||[0,0],!this.isOverNodeBox(h,a.canvasX,a.canvasY))){var s=this.isOverNodeInput(h,a.canvasX,a.canvasY,f);-1!=s&&h.inputs[s]?g.isValidConnection(this.connecting_output.type,h.inputs[s].type)&&(this._highlight_input=f):this._highlight_input=null}this.canvas&&(z(a.canvasX, +a.canvasY,h.pos[0]+h.size[0]-5,h.pos[1]+h.size[1]-5,5,5)?this.canvas.style.cursor="se-resize":this.canvas.style.cursor="crosshair")}else{f=null;for(b=0;br[0]+4||a.canvasYr[1]+4)){f=s;break}}f!=this.over_link_center&&(this.over_link_center=f,this.dirty_canvas=!0);this.canvas&&(this.canvas.style.cursor="")}if(this.node_capturing_input&&this.node_capturing_input!=h&&this.node_capturing_input.onMouseMove)this.node_capturing_input.onMouseMove(a, [a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]],this);if(this.node_dragged&&!this.live_mode){for(b in this.selected_nodes)h=this.selected_nodes[b],h.pos[0]+=d[0]/this.ds.scale,h.pos[1]+=d[1]/this.ds.scale;this.dirty_bgcanvas=this.dirty_canvas=!0}this.resizing_node&&!this.live_mode&&(this.resizing_node.size[0]=a.canvasX-this.resizing_node.pos[0],this.resizing_node.size[1]=a.canvasY-this.resizing_node.pos[1],d=Math.max(this.resizing_node.inputs?this.resizing_node.inputs.length: 0,this.resizing_node.outputs?this.resizing_node.outputs.length:0)*g.NODE_SLOT_HEIGHT+(this.resizing_node.widgets?this.resizing_node.widgets.length:0)*(g.NODE_WIDGET_HEIGHT+4)+4,this.resizing_node.size[1]this.dragging_rectangle[3]?this.dragging_rectangle[1]-f:this.dragging_rectangle[1];this.dragging_rectangle[0]=0>this.dragging_rectangle[2]?this.dragging_rectangle[0]-h:this.dragging_rectangle[0];this.dragging_rectangle[1]=c;this.dragging_rectangle[2]=h;this.dragging_rectangle[3]=f;f=[];for(c=0;cthis.dragging_rectangle[3]?this.dragging_rectangle[1]-f:this.dragging_rectangle[1];this.dragging_rectangle[0]=0>this.dragging_rectangle[2]?this.dragging_rectangle[0]-h:this.dragging_rectangle[0];this.dragging_rectangle[1]=s;this.dragging_rectangle[2]=h;this.dragging_rectangle[3]=f;f=[];for(s=0;sa.click_time&&z(a.canvasX,a.canvasY,h.pos[0],h.pos[1]-g.NODE_TITLE_HEIGHT,g.NODE_TITLE_HEIGHT,g.NODE_TITLE_HEIGHT)&&h.collapse();this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_dragged.pos[0]=Math.round(this.node_dragged.pos[0]); this.node_dragged.pos[1]=Math.round(this.node_dragged.pos[1]);this.graph.config.align_to_grid&&this.node_dragged.alignToGrid();if(this.onNodeMoved)this.onNodeMoved(this.node_dragged);this.node_dragged=null}else{h=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes);!h&&300>a.click_time&&this.deselectAllNodes();this.dirty_canvas=!0;this.dragging_canvas=!1;if(this.node_over&&this.node_over.onMouseUp)this.node_over.onMouseUp(a,[a.canvasX-this.node_over.pos[0],a.canvasY-this.node_over.pos[1]], this);if(this.node_capturing_input&&this.node_capturing_input.onMouseUp)this.node_capturing_input.onMouseUp(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]])}}else 2==a.which?(this.dirty_canvas=!0,this.dragging_canvas=!1):3==a.which&&(this.dirty_canvas=!0,this.dragging_canvas=!1);this.graph.change();a.stopPropagation();a.preventDefault();return!1}};e.prototype.processMouseWheel=function(a){if(this.graph&&this.allow_dragcanvas){var b=null!=a.wheelDeltaY?a.wheelDeltaY: --60*a.detail;this.adjustMouseEvent(a);var d=this.ds.scale;0b&&(d*=1/1.1);this.ds.changeScale(d,[a.localX,a.localY]);this.graph.change();a.preventDefault();return!1}};e.prototype.isOverNodeBox=function(a,b,d){var h=g.NODE_TITLE_HEIGHT;return z(b,d,a.pos[0]+2,a.pos[1]+2-h,h-4,h-4)?!0:!1};e.prototype.isOverNodeInput=function(a,b,d,h){if(a.inputs)for(var f=0,g=a.inputs.length;fb&&(d*=1/1.1);this.ds.changeScale(d,[a.localX,a.localY]);this.graph.change();a.preventDefault();return!1}};e.prototype.isOverNodeBox=function(a,b,d){var h=g.NODE_TITLE_HEIGHT;return z(b,d,a.pos[0]+2,a.pos[1]+2-h,h-4,h-4)?!0:!1};e.prototype.isOverNodeInput=function(a,b,d,h){if(a.inputs)for(var f=0,g=a.inputs.length;fd-this.graph._last_trigger_time)&&this.drawBackCanvas();(this.dirty_canvas||a)&&this.drawFrontCanvas();this.fps=this.render_time?1/this.render_time:0;this.frame+=1}};e.prototype.drawFrontCanvas=function(){this.dirty_canvas=!1;this.ctx||(this.ctx=this.bgcanvas.getContext("2d"));var a=this.ctx;if(a){a.start2D&&a.start2D();var b=this.canvas;a.restore();a.setTransform(1,0,0,1,0,0);this.dirty_area&&(a.save(), a.beginPath(),a.rect(this.dirty_area[0],this.dirty_area[1],this.dirty_area[2],this.dirty_area[3]),a.clip());this.clear_background&&a.clearRect(0,0,b.width,b.height);this.bgcanvas==this.canvas?this.drawBackCanvas():a.drawImage(this.bgcanvas,0,0);if(this.onRender)this.onRender(b,a);this.show_info&&this.renderInfo(a);if(this.graph){a.save();this.ds.toCanvasContext(a);for(var b=this.computeVisibleNodes(null,this.visible_nodes),d=0;dthis.ds.scale;if(this.live_mode){if(!a.flags.collapsed&&(b.shadowColor="transparent",a.onDrawForeground))a.onDrawForeground(b,this,this.canvas)}else{var c= -this.editor_alpha;b.globalAlpha=c;this.render_shadows&&!f?(b.shadowColor=g.DEFAULT_SHADOW_COLOR,b.shadowOffsetX=2*this.ds.scale,b.shadowOffsetY=2*this.ds.scale,b.shadowBlur=3*this.ds.scale):b.shadowColor="transparent";if(!a.flags.collapsed||!a.onDrawCollapsed||!0!=a.onDrawCollapsed(b,this)){var q=a._shape||g.BOX_SHAPE;s.set(a.size);var e=a.horizontal;if(a.flags.collapsed){b.font=this.inner_text_font;var m=a.getTitle?a.getTitle():a.title;null!=m&&(a._collapsed_width=Math.min(a.size[0],b.measureText(m).width+ -2*g.NODE_TITLE_HEIGHT),s[0]=a._collapsed_width,s[1]=0)}a.clip_area&&(b.save(),b.beginPath(),q==g.BOX_SHAPE?b.rect(0,0,s[0],s[1]):q==g.ROUND_SHAPE?b.roundRect(0,0,s[0],s[1],10):q==g.CIRCLE_SHAPE&&b.arc(0.5*s[0],0.5*s[1],0.5*s[0],0,2*Math.PI),b.clip());a.has_errors&&(h="red");this.drawNodeShape(a,b,s,d,h,a.is_selected,a.mouseOver);b.shadowColor="transparent";if(a.onDrawForeground)a.onDrawForeground(b,this,this.canvas);b.textAlign=e?"center":"left";b.font=this.inner_text_font;h=!f;q=this.connecting_output; -b.lineWidth=1;var m=0,r=new Float32Array(2);if(!a.flags.collapsed){if(a.inputs)for(d=0;dthis.ds.scale;if(this.live_mode){if(!a.flags.collapsed&&(b.shadowColor="transparent",a.onDrawForeground))a.onDrawForeground(b,this,this.canvas)}else{var s= +this.editor_alpha;b.globalAlpha=s;this.render_shadows&&!f?(b.shadowColor=g.DEFAULT_SHADOW_COLOR,b.shadowOffsetX=2*this.ds.scale,b.shadowOffsetY=2*this.ds.scale,b.shadowBlur=3*this.ds.scale):b.shadowColor="transparent";if(!a.flags.collapsed||!a.onDrawCollapsed||!0!=a.onDrawCollapsed(b,this)){var r=a._shape||g.BOX_SHAPE;t.set(a.size);var c=a.horizontal;if(a.flags.collapsed){b.font=this.inner_text_font;var e=a.getTitle?a.getTitle():a.title;null!=e&&(a._collapsed_width=Math.min(a.size[0],b.measureText(e).width+ +2*g.NODE_TITLE_HEIGHT),t[0]=a._collapsed_width,t[1]=0)}a.clip_area&&(b.save(),b.beginPath(),r==g.BOX_SHAPE?b.rect(0,0,t[0],t[1]):r==g.ROUND_SHAPE?b.roundRect(0,0,t[0],t[1],10):r==g.CIRCLE_SHAPE&&b.arc(0.5*t[0],0.5*t[1],0.5*t[0],0,2*Math.PI),b.clip());a.has_errors&&(h="red");this.drawNodeShape(a,b,t,d,h,a.is_selected,a.mouseOver);b.shadowColor="transparent";if(a.onDrawForeground)a.onDrawForeground(b,this,this.canvas);b.textAlign=c?"center":"left";b.font=this.inner_text_font;h=!f;r=this.connecting_output; +b.lineWidth=1;var e=0,n=new Float32Array(2);if(!a.flags.collapsed){if(a.inputs)for(d=0;dthis.ds.scale,n=a._shape||a.constructor.shape||g.ROUND_SHAPE,r=a.constructor.title_mode,k=!0;r==g.TRANSPARENT_TITLE?k=!1:r==g.AUTOHIDE_TITLE&& -q&&(k=!0);A[0]=0;A[1]=k?-f:0;A[2]=d[0]+1;A[3]=k?d[1]+f:d[1];q=b.globalAlpha;b.beginPath();n==g.BOX_SHAPE||m?b.fillRect(A[0],A[1],A[2],A[3]):n==g.ROUND_SHAPE||n==g.CARD_SHAPE?b.roundRect(A[0],A[1],A[2],A[3],this.round_radius,n==g.CARD_SHAPE?0:this.round_radius):n==g.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0],0,2*Math.PI);b.fill();a.flags.collapsed||(b.shadowColor="transparent",b.fillStyle="rgba(0,0,0,0.2)",b.fillRect(0,-1,A[2],2));b.shadowColor="transparent";if(a.onDrawBackground)a.onDrawBackground(b, -this,this.canvas);if(k||r==g.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,f,d,this.ds.scale,h);else if(r!=g.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){k=a.constructor.title_color||h;a.flags.collapsed&&(b.shadowColor=g.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var t=e.gradients[k];t||(t=e.gradients[k]=b.createLinearGradient(0,0,400,0),t.addColorStop(0,k),t.addColorStop(1,"#000"));b.fillStyle=t}else b.fillStyle=k;b.beginPath();n==g.BOX_SHAPE||m?b.rect(0, --f,d[0]+1,f):n!=g.ROUND_SHAPE&&n!=g.CARD_SHAPE||b.roundRect(0,-f,d[0]+1,f,this.round_radius,a.flags.collapsed?this.round_radius:0);b.fill();b.shadowColor="transparent"}if(a.onDrawTitleBox)a.onDrawTitleBox(b,f,d,this.ds.scale);else n==g.ROUND_SHAPE||n==g.CIRCLE_SHAPE||n==g.CARD_SHAPE?(m&&(b.fillStyle="black",b.beginPath(),b.arc(0.5*f,-0.5*f,6,0,2*Math.PI),b.fill()),b.fillStyle=a.boxcolor||g.NODE_DEFAULT_BOXCOLOR,m?b.fillRect(0.5*f-5,-0.5*f-5,10,10):(b.beginPath(),b.arc(0.5*f,-0.5*f,5,0,2*Math.PI), -b.fill())):(m&&(b.fillStyle="black",b.fillRect(0.5*(f-10)-1,-0.5*(f+10)-1,12,12)),b.fillStyle=a.boxcolor||g.NODE_DEFAULT_BOXCOLOR,b.fillRect(0.5*(f-10),-0.5*(f+10),10,10));b.globalAlpha=q;if(a.onDrawTitleText)a.onDrawTitleText(b,f,d,this.ds.scale,this.title_text_font,c);!m&&(b.font=this.title_text_font,m=String(a.getTitle()))&&(b.fillStyle=c?"white":a.constructor.title_text_color||this.node_title_color,a.flags.collapsed?(b.textAlign="left",b.measureText(m),b.fillText(m.substr(0,20),f,g.NODE_TITLE_TEXT_Y- -f),b.textAlign="left"):(b.textAlign="left",b.fillText(m,f,g.NODE_TITLE_TEXT_Y-f)));if(a.onDrawTitle)a.onDrawTitle(b)}if(c){if(a.onBounding)a.onBounding(A);r==g.TRANSPARENT_TITLE&&(A[1]-=f,A[3]+=f);b.lineWidth=1;b.globalAlpha=0.8;b.beginPath();n==g.BOX_SHAPE?b.rect(-6+A[0],-6+A[1],12+A[2],12+A[3]):n==g.ROUND_SHAPE||n==g.CARD_SHAPE&&a.flags.collapsed?b.roundRect(-6+A[0],-6+A[1],12+A[2],12+A[3],2*this.round_radius):n==g.CARD_SHAPE?b.roundRect(-6+A[0],-6+A[1],12+A[2],12+A[3],2*this.round_radius,2):n== -g.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0]+6,0,2*Math.PI);b.strokeStyle="#FFF";b.stroke();b.strokeStyle=h;b.globalAlpha=1}};var r=new Float32Array(4),n=new Float32Array(4),x=new Float32Array(2),v=new Float32Array(2);e.prototype.drawConnections=function(a){var b=g.getTime(),d=this.visible_area;r[0]=d[0]-20;r[1]=d[1]-20;r[2]=d[2]+40;r[3]=d[3]+40;a.lineWidth=this.connections_width;a.fillStyle="#AAA";a.strokeStyle="#AAA";a.globalAlpha=this.editor_alpha;for(var d=this.graph._nodes,h=0,f=d.length;h< -f;++h){var c=d[h];if(c.inputs&&c.inputs.length)for(var q=0;qn[2]&&(n[0]+=n[2],n[2]=Math.abs(n[2]));0>n[3]&&(n[1]+=n[3],n[3]=Math.abs(n[3]));if(u(n,r)){var p=m.outputs[k],k=c.inputs[q];if(p&& -k&&(m=p.dir||(m.horizontal?g.DOWN:g.RIGHT),k=k.dir||(c.horizontal?g.UP:g.LEFT),this.renderLink(a,s,t,e,!1,0,null,m,k),e&&e._last_time&&1E3>b-e._last_time)){var p=2-0.002*(b-e._last_time),K=a.globalAlpha;a.globalAlpha=K*p;this.renderLink(a,s,t,e,!0,p,"white",m,k);a.globalAlpha=K}}}}}}a.globalAlpha=1};e.prototype.renderLink=function(a,b,d,h,f,c,q,m,n,r){h&&this.visible_links.push(h);!q&&h&&(q=h.color||e.link_type_colors[h.type]);q||(q=this.default_link_color);null!=h&&this.highlighted_links[h.id]&& -(q="#FFF");m=m||g.RIGHT;n=n||g.LEFT;var k=C(b,d);this.render_connections_border&&0.6b[1]?0:Math.PI,a.save(),a.translate(t[0],t[1]),a.rotate(s),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore(),a.save(),a.translate(r[0],r[1]),a.rotate(v), -a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill());if(c)for(a.fillStyle=q,t=0;5>t;++t)c=(0.001*g.getTime()+0.2*t)%1,f=this.computeConnectionPoint(b,d,c,m,n),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill()};e.prototype.computeConnectionPoint=function(a,b,d,h,f){h=h||g.RIGHT;f=f||g.LEFT;var c=C(a,b),q=[a[0],a[1]],e=[b[0],b[1]];switch(h){case g.LEFT:q[0]+=-0.25*c;break;case g.RIGHT:q[0]+=0.25*c;break;case g.UP:q[1]+= --0.25*c;break;case g.DOWN:q[1]+=0.25*c}switch(f){case g.LEFT:e[0]+=-0.25*c;break;case g.RIGHT:e[0]+=0.25*c;break;case g.UP:e[1]+=-0.25*c;break;case g.DOWN:e[1]+=0.25*c}h=(1-d)*(1-d)*(1-d);f=3*(1-d)*(1-d)*d;c=3*(1-d)*d*d;d*=d*d;return[h*a[0]+f*q[0]+c*e[0]+d*b[0],h*a[1]+f*q[1]+c*e[1]+d*b[1]]};e.prototype.drawExecutionOrder=function(a){a.shadowColor="transparent";a.globalAlpha=0.25;a.textAlign="center";a.strokeStyle="white";a.globalAlpha=0.75;for(var b=this.visible_nodes,d=0;dt.last_y&&q -t.options.max&&(t.value=t.options.max);else if("mousedown"==d.type){var s=t.options.values;s&&s.constructor===Function&&(s=t.options.values(t,a));var k=s.constructor===Array?s:Object.keys(s),c=40>c?-1:c>e-40?1:0;if("number"==t.type)t.value+=0.1*c*(t.options.step||1),null!=t.options.min&&t.valuet.options.max&&(t.value=t.options.max);else if(c)n=-1,n=s.constructor===Object?k.indexOf(String(t.value))+c:k.indexOf(t.value)+c,n>=k.length&& -(n=k.length-1),0>n&&(n=0),t.value=s.constructor===Array?s[n]:n;else{var v=s!=k?Object.values(s):s;new g.ContextMenu(v,{scale:Math.max(1,this.ds.scale),event:d,className:"dark",callback:p.bind(t)},n);var p=function(a,b,d){s!=k&&(a=v.indexOf(a));this.value=a;f(this,a);m.dirty_canvas=!0;return!1}}}else"mouseup"==d.type&&"number"==t.type&&(c=40>c?-1:c>e-40?1:0,200>d.click_time&&0==c&&this.prompt("Value",t.value,function(a){this.value=Number(a);f(this,this.value)}.bind(t),d));h!=t.value&&setTimeout(function(){f(this, -this.value)}.bind(t),20);this.dirty_canvas=!0;break;case "toggle":"mousedown"==d.type&&(t.value=!t.value,setTimeout(function(){f(t,t.value)},20));break;case "string":case "text":"mousedown"==d.type&&this.prompt("Value",t.value,function(a){this.value=a;f(this,a)}.bind(t),d);break;default:t.mouse&&t.mouse(ctx,d,[c,q],a)}return t}}return null};e.prototype.drawGroups=function(a,b){if(this.graph){var d=this.graph._groups;b.save();b.globalAlpha=0.5*this.editor_alpha;for(var h=0;hthis.ds.scale,l=a._shape||a.constructor.shape||g.ROUND_SHAPE,n=a.constructor.title_mode,k=!0;n==g.TRANSPARENT_TITLE?k=!1:n==g.AUTOHIDE_TITLE&& +r&&(k=!0);B[0]=0;B[1]=k?-f:0;B[2]=d[0]+1;B[3]=k?d[1]+f:d[1];r=b.globalAlpha;b.beginPath();l==g.BOX_SHAPE||c?b.fillRect(B[0],B[1],B[2],B[3]):l==g.ROUND_SHAPE||l==g.CARD_SHAPE?b.roundRect(B[0],B[1],B[2],B[3],this.round_radius,l==g.CARD_SHAPE?0:this.round_radius):l==g.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0],0,2*Math.PI);b.fill();a.flags.collapsed||(b.shadowColor="transparent",b.fillStyle="rgba(0,0,0,0.2)",b.fillRect(0,-1,B[2],2));b.shadowColor="transparent";if(a.onDrawBackground)a.onDrawBackground(b, +this,this.canvas);if(k||n==g.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,f,d,this.ds.scale,h);else if(n!=g.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){k=a.constructor.title_color||h;a.flags.collapsed&&(b.shadowColor=g.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var u=e.gradients[k];u||(u=e.gradients[k]=b.createLinearGradient(0,0,400,0),u.addColorStop(0,k),u.addColorStop(1,"#000"));b.fillStyle=u}else b.fillStyle=k;b.beginPath();l==g.BOX_SHAPE||c?b.rect(0, +-f,d[0]+1,f):l!=g.ROUND_SHAPE&&l!=g.CARD_SHAPE||b.roundRect(0,-f,d[0]+1,f,this.round_radius,a.flags.collapsed?this.round_radius:0);b.fill();b.shadowColor="transparent"}if(a.onDrawTitleBox)a.onDrawTitleBox(b,f,d,this.ds.scale);else l==g.ROUND_SHAPE||l==g.CIRCLE_SHAPE||l==g.CARD_SHAPE?(c&&(b.fillStyle="black",b.beginPath(),b.arc(0.5*f,-0.5*f,6,0,2*Math.PI),b.fill()),b.fillStyle=a.boxcolor||g.NODE_DEFAULT_BOXCOLOR,c?b.fillRect(0.5*f-5,-0.5*f-5,10,10):(b.beginPath(),b.arc(0.5*f,-0.5*f,5,0,2*Math.PI), +b.fill())):(c&&(b.fillStyle="black",b.fillRect(0.5*(f-10)-1,-0.5*(f+10)-1,12,12)),b.fillStyle=a.boxcolor||g.NODE_DEFAULT_BOXCOLOR,b.fillRect(0.5*(f-10),-0.5*(f+10),10,10));b.globalAlpha=r;if(a.onDrawTitleText)a.onDrawTitleText(b,f,d,this.ds.scale,this.title_text_font,s);!c&&(b.font=this.title_text_font,c=String(a.getTitle()))&&(b.fillStyle=s?"white":a.constructor.title_text_color||this.node_title_color,a.flags.collapsed?(b.textAlign="left",b.measureText(c),b.fillText(c.substr(0,20),f,g.NODE_TITLE_TEXT_Y- +f),b.textAlign="left"):(b.textAlign="left",b.fillText(c,f,g.NODE_TITLE_TEXT_Y-f)));if(a.onDrawTitle)a.onDrawTitle(b)}if(s){if(a.onBounding)a.onBounding(B);n==g.TRANSPARENT_TITLE&&(B[1]-=f,B[3]+=f);b.lineWidth=1;b.globalAlpha=0.8;b.beginPath();l==g.BOX_SHAPE?b.rect(-6+B[0],-6+B[1],12+B[2],12+B[3]):l==g.ROUND_SHAPE||l==g.CARD_SHAPE&&a.flags.collapsed?b.roundRect(-6+B[0],-6+B[1],12+B[2],12+B[3],2*this.round_radius):l==g.CARD_SHAPE?b.roundRect(-6+B[0],-6+B[1],12+B[2],12+B[3],2*this.round_radius,2):l== +g.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0]+6,0,2*Math.PI);b.strokeStyle="#FFF";b.stroke();b.strokeStyle=h;b.globalAlpha=1}};var l=new Float32Array(4),q=new Float32Array(4),w=new Float32Array(2),x=new Float32Array(2);e.prototype.drawConnections=function(a){var b=g.getTime(),d=this.visible_area;l[0]=d[0]-20;l[1]=d[1]-20;l[2]=d[2]+40;l[3]=d[3]+40;a.lineWidth=this.connections_width;a.fillStyle="#AAA";a.strokeStyle="#AAA";a.globalAlpha=this.editor_alpha;for(var d=this.graph._nodes,h=0,f=d.length;h< +f;++h){var s=d[h];if(s.inputs&&s.inputs.length)for(var r=0;rq[2]&&(q[0]+=q[2],q[2]=Math.abs(q[2]));0>q[3]&&(q[1]+=q[3],q[3]=Math.abs(q[3]));if(y(q,l)){var t=e.outputs[n],n=s.inputs[r];if(t&& +n&&(e=t.dir||(e.horizontal?g.DOWN:g.RIGHT),n=n.dir||(s.horizontal?g.UP:g.LEFT),this.renderLink(a,k,u,c,!1,0,null,e,n),c&&c._last_time&&1E3>b-c._last_time)){var t=2-0.002*(b-c._last_time),p=a.globalAlpha;a.globalAlpha=p*t;this.renderLink(a,k,u,c,!0,t,"white",e,n);a.globalAlpha=p}}}}}}a.globalAlpha=1};e.prototype.renderLink=function(a,b,d,h,f,s,c,l,n,k){h&&this.visible_links.push(h);!c&&h&&(c=h.color||e.link_type_colors[h.type]);c||(c=this.default_link_color);null!=h&&this.highlighted_links[h.id]&& +(c="#FFF");l=l||g.RIGHT;n=n||g.LEFT;var q=E(b,d);this.render_connections_border&&0.6b[1]?0:Math.PI,a.save(),a.translate(u[0],u[1]),a.rotate(t),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore(),a.save(),a.translate(k[0],k[1]),a.rotate(x), +a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill());if(s)for(a.fillStyle=c,u=0;5>u;++u)s=(0.001*g.getTime()+0.2*u)%1,f=this.computeConnectionPoint(b,d,s,l,n),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill()};e.prototype.computeConnectionPoint=function(a,b,d,h,f){h=h||g.RIGHT;f=f||g.LEFT;var s=E(a,b),c=[a[0],a[1]],l=[b[0],b[1]];switch(h){case g.LEFT:c[0]+=-0.25*s;break;case g.RIGHT:c[0]+=0.25*s;break;case g.UP:c[1]+= +-0.25*s;break;case g.DOWN:c[1]+=0.25*s}switch(f){case g.LEFT:l[0]+=-0.25*s;break;case g.RIGHT:l[0]+=0.25*s;break;case g.UP:l[1]+=-0.25*s;break;case g.DOWN:l[1]+=0.25*s}h=(1-d)*(1-d)*(1-d);f=3*(1-d)*(1-d)*d;s=3*(1-d)*d*d;d*=d*d;return[h*a[0]+f*c[0]+s*l[0]+d*b[0],h*a[1]+f*c[1]+s*l[1]+d*b[1]]};e.prototype.drawExecutionOrder=function(a){a.shadowColor="transparent";a.globalAlpha=0.25;a.textAlign="center";a.strokeStyle="white";a.globalAlpha=0.75;for(var b=this.visible_nodes,d=0;du.last_y&&r +u.options.max&&(u.value=u.options.max);else if("mousedown"==d.type){var q=u.options.values;q&&q.constructor===Function&&(q=u.options.values(u,a));var x=q.constructor===Array?q:Object.keys(q),c=40>c?-1:c>l-40?1:0;if("number"==u.type)u.value+=0.1*c*(u.options.step||1),null!=u.options.min&&u.valueu.options.max&&(u.value=u.options.max);else if(c)n=-1,n=q.constructor===Object?x.indexOf(String(u.value))+c:x.indexOf(u.value)+c,n>=x.length&& +(n=x.length-1),0>n&&(n=0),u.value=q.constructor===Array?q[n]:n;else{var t=q!=x?Object.values(q):q;new g.ContextMenu(t,{scale:Math.max(1,this.ds.scale),event:d,className:"dark",callback:p.bind(u)},n);var p=function(a,b,d){q!=x&&(a=t.indexOf(a));this.value=a;f(this,a);e.dirty_canvas=!0;return!1}}}else"mouseup"==d.type&&"number"==u.type&&(c=40>c?-1:c>l-40?1:0,200>d.click_time&&0==c&&this.prompt("Value",u.value,function(a){this.value=Number(a);f(this,this.value)}.bind(u),d));h!=u.value&&setTimeout(function(){f(this, +this.value)}.bind(u),20);this.dirty_canvas=!0;break;case "toggle":"mousedown"==d.type&&(u.value=!u.value,setTimeout(function(){f(u,u.value)},20));break;case "string":case "text":"mousedown"==d.type&&this.prompt("Value",u.value,function(a){this.value=a;f(this,a)}.bind(u),d);break;default:u.mouse&&u.mouse(ctx,d,[c,r],a)}return u}}return null};e.prototype.drawGroups=function(a,b){if(this.graph){var d=this.graph._groups;b.save();b.globalAlpha=0.5*this.editor_alpha;for(var h=0;hd&&0.01>b.editor_alpha&&(clearInterval(h),1>d&&(b.live_mode=!0));1"+n+""+ -a+"",value:n});if(m.length)return new g.ContextMenu(m,{event:d,callback:c,parentMenu:h,allow_html:!0,node:f},b),!1}};e.decodeHTML=function(a){var b=document.createElement("div");b.innerText=a;return b.innerHTML};e.onResizeNode=function(a,b,d,h,f){f&&(f.size=f.computeSize(),f.setDirtyCanvas(!0,!0))};e.prototype.showLinkMenu=function(a,b){var d=this;console.log(a);var h=new g.ContextMenu(["Add Node",null,"Delete"],{event:b,title:null!=a.data?a.data.constructor.name:null,callback:function(b,g, -c){switch(b){case "Add Node":e.onMenuAdd(null,null,c,h,function(b){console.log("node autoconnect");var h=d.graph.getNodeById(a.origin_id),f=d.graph.getNodeById(a.target_id);b.inputs&&b.inputs.length&&b.outputs&&b.outputs.length&&h.outputs[a.origin_slot].type==b.inputs[0].type&&b.outputs[0].type==f.inputs[0].type&&(h.connect(a.origin_slot,b,0),b.connect(0,f,a.target_slot),b.pos[0]-=0.5*b.size[0])});break;case "Delete":d.graph.removeLink(a.id)}}});return!1};e.onShowPropertyEditor=function(a,b,d,h,f){function g(){var b= -n.value;"Number"==a.type?b=Number(b):"Boolean"==a.type&&(b=Boolean(b));f[c]=b;m.parentNode&&m.parentNode.removeChild(m);f.setDirtyCanvas(!0,!0)}var c=a.property||"title";b=f[c];var m=document.createElement("div");m.className="graphdialog";m.innerHTML="";m.querySelector(".name").innerText=c;var n=m.querySelector("input");n&&(n.value=b,n.addEventListener("blur",function(a){this.focus()}),n.addEventListener("keydown", -function(a){13==a.keyCode&&(g(),a.preventDefault(),a.stopPropagation())}));b=e.active_canvas.canvas;d=b.getBoundingClientRect();var r=h=-20;d&&(h-=d.left,r-=d.top);event?(m.style.left=event.clientX+h+"px",m.style.top=event.clientY+r+"px"):(m.style.left=0.5*b.width+h+"px",m.style.top=0.5*b.height+r+"px");m.querySelector("button").addEventListener("click",g);b.parentNode.appendChild(m)};e.prototype.prompt=function(a,b,d,h){var f=this;a=a||"";var g=!1,c=document.createElement("div");c.className="graphdialog rounded"; -c.innerHTML=" ";c.close=function(){f.prompt_box=null;c.parentNode&&c.parentNode.removeChild(c)};1"+n+""+ +a+"",value:n});if(l.length)return new g.ContextMenu(l,{event:d,callback:c,parentMenu:h,allow_html:!0,node:f},b),!1}};e.decodeHTML=function(a){var b=document.createElement("div");b.innerText=a;return b.innerHTML};e.onResizeNode=function(a,b,d,h,f){f&&(f.size=f.computeSize(),f.setDirtyCanvas(!0,!0))};e.prototype.showLinkMenu=function(a,b){var d=this;console.log(a);var h=new g.ContextMenu(["Add Node",null,"Delete"],{event:b,title:null!=a.data?a.data.constructor.name:null,callback:function(b,g, +c){switch(b){case "Add Node":e.onMenuAdd(null,null,c,h,function(b){console.log("node autoconnect");var f=d.graph.getNodeById(a.origin_id),h=d.graph.getNodeById(a.target_id);b.inputs&&b.inputs.length&&b.outputs&&b.outputs.length&&f.outputs[a.origin_slot].type==b.inputs[0].type&&b.outputs[0].type==h.inputs[0].type&&(f.connect(a.origin_slot,b,0),b.connect(0,h,a.target_slot),b.pos[0]-=0.5*b.size[0])});break;case "Delete":d.graph.removeLink(a.id)}}});return!1};e.onShowPropertyEditor=function(a,b,d,h,f){function g(){var b= +n.value;"Number"==a.type?b=Number(b):"Boolean"==a.type&&(b=Boolean(b));f[c]=b;l.parentNode&&l.parentNode.removeChild(l);f.setDirtyCanvas(!0,!0)}var c=a.property||"title";b=f[c];var l=document.createElement("div");l.className="graphdialog";l.innerHTML="";l.querySelector(".name").innerText=c;var n=l.querySelector("input");n&&(n.value=b,n.addEventListener("blur",function(a){this.focus()}),n.addEventListener("keydown", +function(a){13==a.keyCode&&(g(),a.preventDefault(),a.stopPropagation())}));b=e.active_canvas.canvas;d=b.getBoundingClientRect();var q=h=-20;d&&(h-=d.left,q-=d.top);event?(l.style.left=event.clientX+h+"px",l.style.top=event.clientY+q+"px"):(l.style.left=0.5*b.width+h+"px",l.style.top=0.5*b.height+q+"px");l.querySelector("button").addEventListener("click",g);b.parentNode.appendChild(l)};e.prototype.prompt=function(a,b,d,h){var f=this;a=a||"";var g=!1,c=document.createElement("div");c.className="graphdialog rounded"; +c.innerHTML=" ";c.close=function(){f.prompt_box=null;c.parentNode&&c.parentNode.removeChild(c)};1e.search_limit)break}}m=null;if(Array.prototype.filter)m=Object.keys(g.registered_node_types).filter(r);else for(L in m=[],g.registered_node_types)r(L)&&m.push(L);for(L=0;Le.search_limit);L++);var r=function(a){var b=g.registered_node_types[a];return q&&b.filter!=q?!1:-1!==a.toLowerCase().indexOf(d)}}}var f=this,c=e.active_canvas,q=c.canvas,m=q.ownerDocument||document,n=document.createElement("div"); -n.className="litegraph litesearchbox graphdialog rounded";n.innerHTML="Search
";n.close=function(){f.search_box=null;m.body.focus();m.body.style.overflow="";setTimeout(function(){f.canvas.focus()},20);n.parentNode&&n.parentNode.removeChild(n)};var r=null;1q.height-200&&(s.style.maxHeight=q.height-a.layerY-20+"px");p.focus();return n};e.prototype.showEditPropertyValue=function(a,b,d){function h(){f(t.value)}function f(h){"number"==typeof a.properties[b]&& -(h=Number(h));if("array"==g||"object"==g)h=JSON.parse(h);a.properties[b]=h;a._graph&&a._graph._version++;if(a.onPropertyChanged)a.onPropertyChanged(b,h);if(d.onclose)d.onclose();r.close();a.setDirtyCanvas(!0,!0)}if(a&&void 0!==a.properties[b]){d=d||{};var c=a.getPropertyInfo(b),g=c.type,m="";if("string"==g||"number"==g||"array"==g||"object"==g)m="";else if("enum"==g&&c.values){var m=""}else if("boolean"==g)m="";else{console.warn("unknown type: "+g);return}var r=this.createDialog(""+b+""+m+"",d);if("enum"==g&&c.values){var t=r.querySelector("select");t.addEventListener("change",function(a){f(a.target.value)})}else if("boolean"== -g)(t=r.querySelector("input"))&&t.addEventListener("click",function(a){f(!!t.checked)});else if(t=r.querySelector("input"))t.addEventListener("blur",function(a){this.focus()}),n=void 0!==a.properties[b]?a.properties[b]:"",n=JSON.stringify(n),t.value=n,t.addEventListener("keydown",function(a){13==a.keyCode&&(h(),a.preventDefault(),a.stopPropagation())});r.querySelector("button").addEventListener("click",h);return r}};e.prototype.createDialog=function(a,b){b=b||{};var d=document.createElement("div"); +[],d.data.outputs)b.addOutput(d.data.outputs[h][0],d.data.outputs[h][1]);d.data.title&&(b.title=d.data.title);d.data.json&&b.configure(d.data.json)}}n.close()}function d(a){var b=t;t&&t.classList.remove("selected");t?(t=a?t.nextSibling:t.previousSibling)||(t=b):t=a?k.childNodes[0]:k.childNodes[k.childNodes.length];t&&(t.classList.add("selected"),t.scrollIntoView({block:"end",behavior:"smooth"}))}function h(){function a(d,f){var h=document.createElement("div");u||(u=d);h.innerText=d;h.dataset.type= +escape(d);h.className="litegraph lite-search-item";f&&(h.className+=" "+f);h.addEventListener("click",function(a){b(unescape(this.dataset.type))});k.appendChild(h)}x=null;var d=p.value;u=null;k.innerHTML="";if(d)if(f.onSearchBox){var h=f.onSearchBox(k,d,c);if(h)for(var L=0;Le.search_limit)break}}r=null;if(Array.prototype.filter)r=Object.keys(g.registered_node_types).filter(q);else for(L in r=[],g.registered_node_types)q(L)&&r.push(L);for(L=0;Le.search_limit);L++);var q=function(a){var b=g.registered_node_types[a];return l&&b.filter!=l?!1:-1!==a.toLowerCase().indexOf(d)}}}var f=this,c=e.active_canvas,r=c.canvas,l=r.ownerDocument||document,n=document.createElement("div"); +n.className="litegraph litesearchbox graphdialog rounded";n.innerHTML="Search
";n.close=function(){f.search_box=null;l.body.focus();l.body.style.overflow="";setTimeout(function(){f.canvas.focus()},20);n.parentNode&&n.parentNode.removeChild(n)};var q=null;1r.height-200&&(k.style.maxHeight=r.height-a.layerY-20+"px");p.focus();return n};e.prototype.showEditPropertyValue=function(a,b,d){function h(){f(u.value)}function f(f){"number"==typeof a.properties[b]&& +(f=Number(f));if("array"==g||"object"==g)f=JSON.parse(f);a.properties[b]=f;a._graph&&a._graph._version++;if(a.onPropertyChanged)a.onPropertyChanged(b,f);if(d.onclose)d.onclose();q.close();a.setDirtyCanvas(!0,!0)}if(a&&void 0!==a.properties[b]){d=d||{};var c=a.getPropertyInfo(b),g=c.type,l="";if("string"==g||"number"==g||"array"==g||"object"==g)l="";else if("enum"==g&&c.values){var l=""}else if("boolean"==g)l="";else{console.warn("unknown type: "+g);return}var q=this.createDialog(""+b+""+l+"",d);if("enum"==g&&c.values){var u=q.querySelector("select");u.addEventListener("change",function(a){f(a.target.value)})}else if("boolean"== +g)(u=q.querySelector("input"))&&u.addEventListener("click",function(a){f(!!u.checked)});else if(u=q.querySelector("input"))u.addEventListener("blur",function(a){this.focus()}),e=void 0!==a.properties[b]?a.properties[b]:"",e=JSON.stringify(e),u.value=e,u.addEventListener("keydown",function(a){13==a.keyCode&&(h(),a.preventDefault(),a.stopPropagation())});q.querySelector("button").addEventListener("click",h);return q}};e.prototype.createDialog=function(a,b){b=b||{};var d=document.createElement("div"); d.className="graphdialog";d.innerHTML=a;var h=this.canvas.getBoundingClientRect(),f=-20,c=-20;h&&(f-=h.left,c-=h.top);b.position?(f+=b.position[0],c+=b.position[1]):b.event?(f+=b.event.clientX,c+=b.event.clientY):(f+=0.5*this.canvas.width,c+=0.5*this.canvas.height);d.style.left=f+"px";d.style.top=c+"px";this.canvas.parentNode.appendChild(d);d.close=function(){this.parentNode&&this.parentNode.removeChild(this)};return d};e.onMenuNodeCollapse=function(a,b,d,h,f){f.collapse()};e.onMenuNodePin=function(a, b,d,h,f){f.pin()};e.onMenuNodeMode=function(a,b,d,h,f){new g.ContextMenu(["Always","On Event","On Trigger","Never"],{event:d,callback:function(a){if(f)switch(a){case "On Event":f.mode=g.ON_EVENT;break;case "On Trigger":f.mode=g.ON_TRIGGER;break;case "Never":f.mode=g.NEVER;break;default:f.mode=g.ALWAYS}},parentMenu:h,node:f});return!1};e.onMenuNodeColors=function(a,b,d,h,f){if(!f)throw"no node for color";b=[];b.push({value:null,content:"No color"}); for(var c in e.node_colors)a=e.node_colors[c],a={value:c,content:""+c+""},b.push(a);new g.ContextMenu(b,{event:d,callback:function(a){f&&((a=a.value?e.node_colors[a.value]:null)?f.constructor===g.LGraphGroup?f.color=a.groupcolor:(f.color=a.color,f.bgcolor=a.bgcolor):(delete f.color,delete f.bgcolor),f.setDirtyCanvas(!0,!0))},parentMenu:h,node:f});return!1};e.onMenuNodeShapes= @@ -223,493 +223,489 @@ brown:{color:"#332922",bgcolor:"#593930",groupcolor:"#b06634"},green:{color:"#23 null;this.getMenuOptions?a=this.getMenuOptions():(a=[{content:"Add Node",has_submenu:!0,callback:e.onMenuAdd},{content:"Add Group",callback:e.onGroupAdd}],this._graph_stack&&0Name",h),m=g.querySelector("input");m&&c&&(m.value=c.label||"");g.querySelector("button").addEventListener("click",function(a){m.value&& -(c&&(c.label=m.value),d.setDirty(!0));g.close()})}},extra:a};a&&(c.title=a.type);var m=null;a&&(m=a.getSlotInPosition(b.canvasX,b.canvasY),e.active_node=a);if(m){f=[];m&&m.output&&m.output.links&&m.output.links.length&&f.push({content:"Disconnect Links",slot:m});var n=m.input||m.output;f.push(n.locked?"Cannot remove":{content:"Remove Slot",slot:m});f.push(n.nameLocked?"Cannot rename":{content:"Rename Slot",slot:m});c.title=(m.input?m.input.type:m.output.type)||"*";m.input&&m.input.type==g.ACTION&& -(c.title="Action");m.output&&m.output.type==g.EVENT&&(c.title="Event")}else a?f=this.getNodeMenuOptions(a):(f=this.getCanvasMenuOptions(),(m=this.graph.getGroupOnPos(b.canvasX,b.canvasY))&&f.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:m,options:this.getGroupMenuOptions(m)}}));f&&new g.ContextMenu(f,c,h)};this.CanvasRenderingContext2D&&(CanvasRenderingContext2D.prototype.roundRect=function(a,b,d,h,f,c){void 0===f&&(f=5);void 0===c&&(c=f);this.moveTo(a+f,b);this.lineTo(a+ -d-f,b);this.quadraticCurveTo(a+d,b,a+d,b+f);this.lineTo(a+d,b+h-c);this.quadraticCurveTo(a+d,b+h,a+d-c,b+h);this.lineTo(a+c,b+h);this.quadraticCurveTo(a,b+h,a,b+h-c);this.lineTo(a,b+f);this.quadraticCurveTo(a,b,a+f,b)});g.compareObjects=function(a,b){for(var d in a)if(a[d]!=b[d])return!1;return!0};g.distance=C;g.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+","+Math.round(255*a[2]).toFixed()+","+(4==a.length?a[3].toFixed(2):"1.0")+")"};g.isInsideRectangle= -z;g.growBounding=function(a,b,d){ba[2]&&(a[2]=b);da[3]&&(a[3]=d)};g.isInsideBounding=function(a,b){return a[0]b[1][0]||a[1]>b[1][1]?!1:!0};g.overlapBounding=u;g.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),d=0,h,f,c=0;6>c;c+=2)h="0123456789ABCDEF".indexOf(a.charAt(c)),f="0123456789ABCDEF".indexOf(a.charAt(c+1)),b[d]=16*h+f,d++;return b};g.num2hex=function(a){for(var b="#",d,h,f=0;3>f;f++)d=a[f]/ -16,h=a[f]%16,b+="0123456789ABCDEF".charAt(d)+"0123456789ABCDEF".charAt(h);return b};D.prototype.addItem=function(a,b,d){function h(a){var b=this.value;b&&b.has_submenu&&f.call(this,a)}function f(a){var b=this.value,h=!0;c.current_submenu&&c.current_submenu.close(a);if(d.callback){var f=d.callback.call(this,b,d,a,c,d.node);!0===f&&(h=!1)}if(b&&(b.callback&&!d.ignore_item_callbacks&&!0!==b.disabled&&(f=b.callback.call(this,b,d,a,c,d.extra),!0===f&&(h=!1)),b.submenu)){if(!b.submenu.options)throw"ContextMenu submenu needs options"; -new c.constructor(b.submenu.options,{callback:b.submenu.callback,event:a,parentMenu:c,ignore_item_callbacks:b.submenu.ignore_item_callbacks,title:b.submenu.title,extra:b.submenu.extra,autoopen:d.autoopen});h=!1}h&&!c.lock&&c.close()}var c=this;d=d||{};var g=document.createElement("div");g.className="litemenu-entry submenu";var m=!1;if(null===b)g.classList.add("separator");else{g.innerHTML=b&&b.title?b.title:a;if(g.value=b)b.disabled&&(m=!0,g.classList.add("disabled")),(b.submenu||b.has_submenu)&& -g.classList.add("has_submenu");"function"==typeof b?(g.dataset.value=a,g.onclick_callback=b):g.dataset.value=b;b.className&&(g.className+=" "+b.className)}this.root.appendChild(g);m||g.addEventListener("click",f);d.autoopen&&g.addEventListener("mouseenter",h);return g};D.prototype.close=function(a,b){this.root.parentNode&&this.root.parentNode.removeChild(this.root);this.parentMenu&&!b&&(this.parentMenu.lock=!1,this.parentMenu.current_submenu=null,void 0===a?this.parentMenu.close():a&&!D.isCursorOverElement(a, +a);return b};e.prototype.getGroupMenuOptions=function(a){return[{content:"Title",callback:e.onShowPropertyEditor},{content:"Color",has_submenu:!0,callback:e.onMenuNodeColors},{content:"Font size",property:"font_size",type:"Number",callback:e.onShowPropertyEditor},null,{content:"Remove",callback:e.onMenuNodeRemove}]};e.prototype.processContextMenu=function(a,b){var d=this,h=e.active_canvas.getCanvasWindow(),f=null,c={event:b,callback:function(b,f,h){if(b)if("Remove Slot"==b.content)b=b.slot,b.input? +a.removeInput(b.slot):b.output&&a.removeOutput(b.slot);else if("Disconnect Links"==b.content)b=b.slot,b.output?a.disconnectOutput(b.slot):b.input&&a.disconnectInput(b.slot);else if("Rename Slot"==b.content){b=b.slot;var c=b.input?a.getInputInfo(b.slot):a.getOutputInfo(b.slot),g=d.createDialog("Name",f),s=g.querySelector("input");s&&c&&(s.value=c.label||"");g.querySelector("button").addEventListener("click",function(a){s.value&& +(c&&(c.label=s.value),d.setDirty(!0));g.close()})}},extra:a};a&&(c.title=a.type);var l=null;a&&(l=a.getSlotInPosition(b.canvasX,b.canvasY),e.active_node=a);if(l){f=[];l&&l.output&&l.output.links&&l.output.links.length&&f.push({content:"Disconnect Links",slot:l});var n=l.input||l.output;f.push(n.locked?"Cannot remove":{content:"Remove Slot",slot:l});f.push(n.nameLocked?"Cannot rename":{content:"Rename Slot",slot:l});c.title=(l.input?l.input.type:l.output.type)||"*";l.input&&l.input.type==g.ACTION&& +(c.title="Action");l.output&&l.output.type==g.EVENT&&(c.title="Event")}else a?f=this.getNodeMenuOptions(a):(f=this.getCanvasMenuOptions(),(l=this.graph.getGroupOnPos(b.canvasX,b.canvasY))&&f.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:l,options:this.getGroupMenuOptions(l)}}));f&&new g.ContextMenu(f,c,h)};this.CanvasRenderingContext2D&&(CanvasRenderingContext2D.prototype.roundRect=function(a,b,d,h,f,c){void 0===f&&(f=5);void 0===c&&(c=f);this.moveTo(a+f,b);this.lineTo(a+ +d-f,b);this.quadraticCurveTo(a+d,b,a+d,b+f);this.lineTo(a+d,b+h-c);this.quadraticCurveTo(a+d,b+h,a+d-c,b+h);this.lineTo(a+c,b+h);this.quadraticCurveTo(a,b+h,a,b+h-c);this.lineTo(a,b+f);this.quadraticCurveTo(a,b,a+f,b)});g.compareObjects=function(a,b){for(var d in a)if(a[d]!=b[d])return!1;return!0};g.distance=E;g.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+","+Math.round(255*a[2]).toFixed()+","+(4==a.length?a[3].toFixed(2):"1.0")+")"};g.isInsideRectangle= +z;g.growBounding=function(a,b,d){ba[2]&&(a[2]=b);da[3]&&(a[3]=d)};g.isInsideBounding=function(a,b){return a[0]b[1][0]||a[1]>b[1][1]?!1:!0};g.overlapBounding=y;g.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),d=0,h,f,c=0;6>c;c+=2)h="0123456789ABCDEF".indexOf(a.charAt(c)),f="0123456789ABCDEF".indexOf(a.charAt(c+1)),b[d]=16*h+f,d++;return b};g.num2hex=function(a){for(var b="#",d,h,f=0;3>f;f++)d=a[f]/ +16,h=a[f]%16,b+="0123456789ABCDEF".charAt(d)+"0123456789ABCDEF".charAt(h);return b};D.prototype.addItem=function(a,b,d){function h(a){var b=this.value;b&&b.has_submenu&&f.call(this,a)}function f(a){var b=this.value,f=!0;c.current_submenu&&c.current_submenu.close(a);if(d.callback){var h=d.callback.call(this,b,d,a,c,d.node);!0===h&&(f=!1)}if(b&&(b.callback&&!d.ignore_item_callbacks&&!0!==b.disabled&&(h=b.callback.call(this,b,d,a,c,d.extra),!0===h&&(f=!1)),b.submenu)){if(!b.submenu.options)throw"ContextMenu submenu needs options"; +new c.constructor(b.submenu.options,{callback:b.submenu.callback,event:a,parentMenu:c,ignore_item_callbacks:b.submenu.ignore_item_callbacks,title:b.submenu.title,extra:b.submenu.extra,autoopen:d.autoopen});f=!1}f&&!c.lock&&c.close()}var c=this;d=d||{};var g=document.createElement("div");g.className="litemenu-entry submenu";var l=!1;if(null===b)g.classList.add("separator");else{g.innerHTML=b&&b.title?b.title:a;if(g.value=b)b.disabled&&(l=!0,g.classList.add("disabled")),(b.submenu||b.has_submenu)&& +g.classList.add("has_submenu");"function"==typeof b?(g.dataset.value=a,g.onclick_callback=b):g.dataset.value=b;b.className&&(g.className+=" "+b.className)}this.root.appendChild(g);l||g.addEventListener("click",f);d.autoopen&&g.addEventListener("mouseenter",h);return g};D.prototype.close=function(a,b){this.root.parentNode&&this.root.parentNode.removeChild(this.root);this.parentMenu&&!b&&(this.parentMenu.lock=!1,this.parentMenu.current_submenu=null,void 0===a?this.parentMenu.close():a&&!D.isCursorOverElement(a, this.parentMenu.root)&&D.trigger(this.parentMenu.root,"mouseleave",a));this.current_submenu&&this.current_submenu.close(a,!0);this.root.closing_timer&&clearTimeout(this.root.closing_timer)};D.trigger=function(a,b,d,h){var f=document.createEvent("CustomEvent");f.initCustomEvent(b,!0,!0,d);f.srcElement=h;a.dispatchEvent?a.dispatchEvent(f):a.__events&&a.__events.dispatchEvent(f);return f};D.prototype.getTopMenu=function(){return this.options.parentMenu?this.options.parentMenu.getTopMenu():this};D.prototype.getFirstEvent= function(){return this.options.parentMenu?this.options.parentMenu.getFirstEvent():this.options.event};D.isCursorOverElement=function(a,b){var d=a.clientX,h=a.clientY,f=b.getBoundingClientRect();return f?h>f.top&&hf.left&&dMath.abs(d))return h[1];d=(a-h[0])/d;return h[1]*(1-d)+f[1]*d}}return 0}};B.prototype.draw=function(a,b,d,h,f,c){if(d=this.points){this.size=b;var g=b[0]-2*this.margin;b=b[1]-2*this.margin;f=f||"#666";a.save();a.translate(this.margin,this.margin);h&&(a.fillStyle="#111",a.fillRect(0,0,g,b),a.fillStyle="#222",a.fillRect(0.5*g,0,1,b),a.strokeStyle="#333",a.strokeRect(0,0,g,b));a.strokeStyle=f;c&&(a.globalAlpha=0.5);a.beginPath();for(h=0;ha[1])){var h=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=a[0]-this.margin,g=a[1]-this.margin;this.selected=this.getCloserPoint([c,g],30/b.ds.scale);-1==this.selected&&(h=[c/h,1-g/f],d.push(h),d.sort(function(a,b){return a[0]- -b[0]}),this.selected=d.indexOf(h),this.must_update=!0);if(-1!=this.selected)return!0}};B.prototype.onMouseMove=function(a,b){var d=this.points;if(d){var h=this.selected;if(!(0>h)){var f=(a[0]-this.margin)/(this.size[0]-2*this.margin),c=(a[1]-this.margin)/(this.size[1]-2*this.margin);this._nearest=this.getCloserPoint([a[0]-this.margin,a[1]-this.margin],30/b.ds.scale);var g=d[h];if(g){var m=0==h||h==d.length-1;!m&&(-10>a[0]||a[0]>this.size[0]+10||-10>a[1]||a[1]>this.size[1]+10)?(d.splice(h,1),this.selected= --1):(g[0]=m?0==h?0:1:Math.clamp(f,0,1),g[1]=1-Math.clamp(c,0,1),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(g),this.must_update=!0)}}}};B.prototype.onMouseUp=function(a,b){this.selected=-1;return!1};B.prototype.getCloserPoint=function(a,b){var d=this.points;if(!d)return-1;b=b||30;for(var h=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=d.length,g=[0,0],m=1E6,n=-1,e=0;em||r>b||(n=e,m=r)}return n};g.CurveEditor= -B;g.getParameterNames=function(a){return(a+"").replace(/[/][/].*$/gm,"").replace(/\s+/g,"").replace(/[/][*][^/*]*[*][/]/g,"").split("){",1)[0].replace(/^[^(]*[(]/,"").replace(/=[^,]+/g,"").split(",").filter(Boolean)};Math.clamp=function(a,b,d){return b>a?b:dMath.abs(d))return h[1];d=(a-h[0])/d;return h[1]*(1-d)+f[1]*d}}return 0}};A.prototype.draw=function(a,b,d,h,f,c){if(d=this.points){this.size=b;var g=b[0]-2*this.margin;b=b[1]-2*this.margin;f=f||"#666";a.save();a.translate(this.margin,this.margin);h&&(a.fillStyle="#111",a.fillRect(0,0,g,b),a.fillStyle="#222",a.fillRect(0.5*g,0,1,b),a.strokeStyle="#333",a.strokeRect(0,0,g,b));a.strokeStyle=f;c&&(a.globalAlpha=0.5);a.beginPath();for(h=0;ha[1])){var h=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=a[0]-this.margin,g=a[1]-this.margin;this.selected=this.getCloserPoint([c,g],30/b.ds.scale);-1==this.selected&&(h=[c/h,1-g/f],d.push(h),d.sort(function(a,b){return a[0]- +b[0]}),this.selected=d.indexOf(h),this.must_update=!0);if(-1!=this.selected)return!0}};A.prototype.onMouseMove=function(a,b){var d=this.points;if(d){var h=this.selected;if(!(0>h)){var f=(a[0]-this.margin)/(this.size[0]-2*this.margin),c=(a[1]-this.margin)/(this.size[1]-2*this.margin);this._nearest=this.getCloserPoint([a[0]-this.margin,a[1]-this.margin],30/b.ds.scale);var g=d[h];if(g){var l=0==h||h==d.length-1;!l&&(-10>a[0]||a[0]>this.size[0]+10||-10>a[1]||a[1]>this.size[1]+10)?(d.splice(h,1),this.selected= +-1):(g[0]=l?0==h?0:1:Math.clamp(f,0,1),g[1]=1-Math.clamp(c,0,1),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(g),this.must_update=!0)}}}};A.prototype.onMouseUp=function(a,b){this.selected=-1;return!1};A.prototype.getCloserPoint=function(a,b){var d=this.points;if(!d)return-1;b=b||30;for(var h=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=d.length,g=[0,0],l=1E6,n=-1,e=0;el||q>b||(n=e,l=q)}return n};g.CurveEditor= +A;g.getParameterNames=function(a){return(a+"").replace(/[/][/].*$/gm,"").replace(/\s+/g,"").replace(/[/][*][^/*]*[*][/]/g,"").split("){",1)[0].replace(/^[^(]*[(]/,"").replace(/=[^,]+/g,"").split(",").filter(Boolean)};Math.clamp=function(a,b,d){return b>a?b:dthis.size[0]-d.NODE_TITLE_HEIGHT&&0>b[1]){var g=this;setTimeout(function(){c.openSubgraph(g.subgraph)},10)}};l.prototype.onAction=function(a,b){this.subgraph.onAction(a,b)};l.prototype.onExecute=function(){if(this.enabled=this.getInputOrProperty("enabled")){if(this.inputs)for(var a=0;a=g?this.trigger(null,e):this._pending.push([g,e])};e.prototype.onExecute=function(){var c=1E3* -this.graph.elapsed_time;this.isInputConnected(1)&&(this.properties.time_in_ms=this.getInputData(1));for(var e=0;em[1]))return this.old_y=c.canvasY,this.captureInput(!0),this.mouse_captured=!0};p.prototype.onMouseMove=function(c){if(this.mouse_captured){var m=this.old_y-c.canvasY;c.shiftKey&&(m*=10);if(c.metaKey||c.altKey)m*=0.1;this.old_y=c.canvasY;c=this._remainder+m/p.pixels_threshold;this._remainder=c%1;c=Math.clamp(this.properties.value+ -(c|0)*this.properties.step,this.properties.min,this.properties.max);this.properties.value=c;this.graph._version++;this.setDirtyCanvas(!0)}};p.prototype.onMouseUp=function(c,m){200>c.click_time&&(this.properties.value=Math.clamp(this.properties.value+(m[1]>0.5*this.size[1]?-1:1)*this.properties.step,this.properties.min,this.properties.max),this.graph._version++,this.setDirtyCanvas(!0));this.mouse_captured&&(this.mouse_captured=!1,this.captureInput(!1))};B.registerNodeType("widget/number",p);k.title= -"Combo";k.desc="Widget to select from a list";k.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};k.prototype.onPropertyChanged=function(c,m){"values"==c?(this._values=m.split(";"),this.widget.options.values=this._values):"value"==c&&(this.widget.value=m)};B.registerNodeType("widget/combo",k);w.title="Knob";w.desc="Circular controller";w.size=[80,100];w.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/ -(this.properties.max-this.properties.min));var m=0.5*this.size[0],e=0.5*this.size[1],k=0.5*Math.min(this.size[0],this.size[1])-5;c.globalAlpha=1;c.save();c.translate(m,e);c.rotate(0.75*Math.PI);c.fillStyle="rgba(0,0,0,0.5)";c.beginPath();c.moveTo(0,0);c.arc(0,0,k,0,1.5*Math.PI);c.fill();c.strokeStyle="black";c.fillStyle=this.properties.color;c.lineWidth=2;c.beginPath();c.moveTo(0,0);c.arc(0,0,k-4,0,1.5*Math.PI*Math.max(0.01,this.value));c.closePath();c.fill();c.lineWidth=1;c.globalAlpha=1;c.restore(); -c.fillStyle="black";c.beginPath();c.arc(m,e,0.75*k,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":this.properties.color;c.beginPath();var r=this.value*Math.PI*1.5+0.75*Math.PI;c.arc(m+Math.cos(r)*k*0.65,e+Math.sin(r)*k*0.65,0.05*k,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":"#AAA";c.font=Math.floor(0.5*k)+"px Arial";c.textAlign="center";c.fillText(this.properties.value.toFixed(this.properties.precision),m,e+0.15*k)}};w.prototype.onExecute=function(){this.setOutputData(0, -this.properties.value);this.boxcolor=B.colorToString([this.value,this.value,this.value])};w.prototype.onMouseDown=function(c){this.center=[0.5*this.size[0],0.5*this.size[1]+20];this.radius=0.5*this.size[0];if(20>c.canvasY-this.pos[1]||B.distance([c.canvasX,c.canvasY],[this.pos[0]+this.center[0],this.pos[1]+this.center[1]])>this.radius)return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];this.captureInput(!0);return!0};w.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX- -this.pos[0],c.canvasY-this.pos[1]];var e=this.value,e=e-0.01*(c[1]-this.oldmouse[1]);1e&&(e=0);this.value=e;this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.oldmouse=c;this.setDirtyCanvas(!0)}};w.prototype.onMouseUp=function(c){this.oldmouse&&(this.oldmouse=null,this.captureInput(!1))};w.prototype.onPropertyChanged=function(c,e){if("min"==c||"max"==c||"value"==c)return this.properties[c]=parseFloat(e),!0};B.registerNodeType("widget/knob", -w);e.title="Inner Slider";e.prototype.onPropertyChanged=function(c,e){"value"==c&&(this.slider.value=e)};e.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};B.registerNodeType("widget/internal_slider",e);C.title="H.Slider";C.desc="Linear slider controller";C.prototype.onDrawForeground=function(c){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min));c.globalAlpha=1;c.lineWidth=1;c.fillStyle="#000";c.fillRect(2, -2,this.size[0]-4,this.size[1]-4);c.fillStyle=this.properties.color;c.beginPath();c.rect(4,4,(this.size[0]-8)*this.value,this.size[1]-8);c.fill()};C.prototype.onExecute=function(){this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.setOutputData(0,this.properties.value);this.boxcolor=B.colorToString([this.value,this.value,this.value])};C.prototype.onMouseDown=function(c){if(0>c.canvasY-this.pos[1])return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY- -this.pos[1]];this.captureInput(!0);return!0};C.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];var e=this.value,e=e+(c[0]-this.oldmouse[0])/this.size[0];1e&&(e=0);this.value=e;this.oldmouse=c;this.setDirtyCanvas(!0)}};C.prototype.onMouseUp=function(c){this.oldmouse=null;this.captureInput(!1)};C.prototype.onMouseLeave=function(c){};B.registerNodeType("widget/hslider",C);z.title="Progress";z.desc="Shows data in linear progress";z.prototype.onExecute= -function(){var c=this.getInputData(0);void 0!=c&&(this.properties.value=c)};z.prototype.onDrawForeground=function(c){c.lineWidth=1;c.fillStyle=this.properties.color;var e=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min),e=Math.min(1,e),e=Math.max(0,e);c.fillRect(2,2,(this.size[0]-4)*e,this.size[1]-4)};B.registerNodeType("widget/progress",z);u.title="Text";u.desc="Shows the input value";u.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"led_text", -text:"LED",type:"minibutton"},{name:"normal_text",text:"Normal",type:"minibutton"}];u.prototype.onDrawForeground=function(c){c.fillStyle=this.properties.color;var e=this.properties.value;this.properties.glowSize?(c.shadowColor=this.properties.color,c.shadowOffsetX=0,c.shadowOffsetY=0,c.shadowBlur=this.properties.glowSize):c.shadowColor="transparent";var k=this.properties.fontsize;c.textAlign=this.properties.align;c.font=k.toString()+"px "+this.properties.font;this.str="number"==typeof e?e.toFixed(this.properties.decimals): -e;if("string"==typeof this.str){var e=this.str.split("\\n"),p;for(p in e)c.fillText(e[p],"left"==this.properties.align?15:this.size[0]-15,-0.15*k+k*(parseInt(p)+1))}c.shadowColor="transparent";this.last_ctx=c;c.textAlign="left"};u.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(this.properties.value=c)};u.prototype.resize=function(){if(this.last_ctx){var c=this.str.split("\\n");this.last_ctx.font=this.properties.fontsize+"px "+this.properties.font;var e=0,k;for(k in c){var p=this.last_ctx.measureText(c[k]).width; -ethis.size[0]-h.NODE_TITLE_HEIGHT&&0>b[1]){var c=this;setTimeout(function(){d.openSubgraph(c.subgraph)},10)}};m.prototype.onAction=function(a,b){this.subgraph.onAction(a,b)};m.prototype.onExecute=function(){if(this.enabled= +this.getInputOrProperty("enabled")){if(this.inputs)for(var a=0;a=g?this.trigger(null,e):this._pending.push([g,e])};e.prototype.onExecute=function(){var c=1E3* +this.graph.elapsed_time;this.isInputConnected(1)&&(this.properties.time_in_ms=this.getInputData(1));for(var e=0;en[1]))return this.old_y=c.canvasY,this.captureInput(!0),this.mouse_captured=!0};p.prototype.onMouseMove=function(c){if(this.mouse_captured){var n=this.old_y-c.canvasY;c.shiftKey&&(n*=10);if(c.metaKey||c.altKey)n*=0.1;this.old_y=c.canvasY;c=this._remainder+n/p.pixels_threshold;this._remainder=c%1;c=Math.clamp(this.properties.value+ +(c|0)*this.properties.step,this.properties.min,this.properties.max);this.properties.value=c;this.graph._version++;this.setDirtyCanvas(!0)}};p.prototype.onMouseUp=function(c,n){200>c.click_time&&(this.properties.value=Math.clamp(this.properties.value+(n[1]>0.5*this.size[1]?-1:1)*this.properties.step,this.properties.min,this.properties.max),this.graph._version++,this.setDirtyCanvas(!0));this.mouse_captured&&(this.mouse_captured=!1,this.captureInput(!1))};A.registerNodeType("widget/number",p);k.title= +"Combo";k.desc="Widget to select from a list";k.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};k.prototype.onPropertyChanged=function(c,n){"values"==c?(this._values=n.split(";"),this.widget.options.values=this._values):"value"==c&&(this.widget.value=n)};A.registerNodeType("widget/combo",k);v.title="Knob";v.desc="Circular controller";v.size=[80,100];v.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/ +(this.properties.max-this.properties.min));var n=0.5*this.size[0],e=0.5*this.size[1],k=0.5*Math.min(this.size[0],this.size[1])-5;c.globalAlpha=1;c.save();c.translate(n,e);c.rotate(0.75*Math.PI);c.fillStyle="rgba(0,0,0,0.5)";c.beginPath();c.moveTo(0,0);c.arc(0,0,k,0,1.5*Math.PI);c.fill();c.strokeStyle="black";c.fillStyle=this.properties.color;c.lineWidth=2;c.beginPath();c.moveTo(0,0);c.arc(0,0,k-4,0,1.5*Math.PI*Math.max(0.01,this.value));c.closePath();c.fill();c.lineWidth=1;c.globalAlpha=1;c.restore(); +c.fillStyle="black";c.beginPath();c.arc(n,e,0.75*k,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":this.properties.color;c.beginPath();var l=this.value*Math.PI*1.5+0.75*Math.PI;c.arc(n+Math.cos(l)*k*0.65,e+Math.sin(l)*k*0.65,0.05*k,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":"#AAA";c.font=Math.floor(0.5*k)+"px Arial";c.textAlign="center";c.fillText(this.properties.value.toFixed(this.properties.precision),n,e+0.15*k)}};v.prototype.onExecute=function(){this.setOutputData(0, +this.properties.value);this.boxcolor=A.colorToString([this.value,this.value,this.value])};v.prototype.onMouseDown=function(c){this.center=[0.5*this.size[0],0.5*this.size[1]+20];this.radius=0.5*this.size[0];if(20>c.canvasY-this.pos[1]||A.distance([c.canvasX,c.canvasY],[this.pos[0]+this.center[0],this.pos[1]+this.center[1]])>this.radius)return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];this.captureInput(!0);return!0};v.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX- +this.pos[0],c.canvasY-this.pos[1]];var e=this.value,e=e-0.01*(c[1]-this.oldmouse[1]);1e&&(e=0);this.value=e;this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.oldmouse=c;this.setDirtyCanvas(!0)}};v.prototype.onMouseUp=function(c){this.oldmouse&&(this.oldmouse=null,this.captureInput(!1))};v.prototype.onPropertyChanged=function(c,e){if("min"==c||"max"==c||"value"==c)return this.properties[c]=parseFloat(e),!0};A.registerNodeType("widget/knob", +v);e.title="Inner Slider";e.prototype.onPropertyChanged=function(c,e){"value"==c&&(this.slider.value=e)};e.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};A.registerNodeType("widget/internal_slider",e);E.title="H.Slider";E.desc="Linear slider controller";E.prototype.onDrawForeground=function(c){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min));c.globalAlpha=1;c.lineWidth=1;c.fillStyle="#000";c.fillRect(2, +2,this.size[0]-4,this.size[1]-4);c.fillStyle=this.properties.color;c.beginPath();c.rect(4,4,(this.size[0]-8)*this.value,this.size[1]-8);c.fill()};E.prototype.onExecute=function(){this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.setOutputData(0,this.properties.value);this.boxcolor=A.colorToString([this.value,this.value,this.value])};E.prototype.onMouseDown=function(c){if(0>c.canvasY-this.pos[1])return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY- +this.pos[1]];this.captureInput(!0);return!0};E.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];var e=this.value,e=e+(c[0]-this.oldmouse[0])/this.size[0];1e&&(e=0);this.value=e;this.oldmouse=c;this.setDirtyCanvas(!0)}};E.prototype.onMouseUp=function(c){this.oldmouse=null;this.captureInput(!1)};E.prototype.onMouseLeave=function(c){};A.registerNodeType("widget/hslider",E);z.title="Progress";z.desc="Shows data in linear progress";z.prototype.onExecute= +function(){var c=this.getInputData(0);void 0!=c&&(this.properties.value=c)};z.prototype.onDrawForeground=function(c){c.lineWidth=1;c.fillStyle=this.properties.color;var e=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min),e=Math.min(1,e),e=Math.max(0,e);c.fillRect(2,2,(this.size[0]-4)*e,this.size[1]-4)};A.registerNodeType("widget/progress",z);y.title="Text";y.desc="Shows the input value";y.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"led_text", +text:"LED",type:"minibutton"},{name:"normal_text",text:"Normal",type:"minibutton"}];y.prototype.onDrawForeground=function(c){c.fillStyle=this.properties.color;var e=this.properties.value;this.properties.glowSize?(c.shadowColor=this.properties.color,c.shadowOffsetX=0,c.shadowOffsetY=0,c.shadowBlur=this.properties.glowSize):c.shadowColor="transparent";var k=this.properties.fontsize;c.textAlign=this.properties.align;c.font=k.toString()+"px "+this.properties.font;this.str="number"==typeof e?e.toFixed(this.properties.decimals): +e;if("string"==typeof this.str){var e=this.str.split("\\n"),p;for(p in e)c.fillText(e[p],"left"==this.properties.align?15:this.size[0]-15,-0.15*k+k*(parseInt(p)+1))}c.shadowColor="transparent";this.last_ctx=c;c.textAlign="left"};y.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(this.properties.value=c)};y.prototype.resize=function(){if(this.last_ctx){var c=this.str.split("\\n");this.last_ctx.font=this.properties.fontsize+"px "+this.properties.font;var e=0,k;for(k in c){var p=this.last_ctx.measureText(c[k]).width; +ek?p.xbox.axes.lx:0,this._left_axis[1]=Math.abs(p.xbox.axes.ly)>k?p.xbox.axes.ly:0,this._right_axis[0]=Math.abs(p.xbox.axes.rx)>k?p.xbox.axes.rx:0,this._right_axis[1]=Math.abs(p.xbox.axes.ry)>k?p.xbox.axes.ry:0,this._triggers[0]=Math.abs(p.xbox.axes.ltrigger)>k?p.xbox.axes.ltrigger: -0,this._triggers[1]=Math.abs(p.xbox.axes.rtrigger)>k?p.xbox.axes.rtrigger:0);if(this.outputs)for(k=0;kk?p.xbox.axes.rtrigger:0);if(this.outputs)for(k=0;kp;p++)if(k[p]){p=k[p];k=this.xbox_mapping;k||(k=this.xbox_mapping={axes:[], -buttons:{},hat:"",hatmap:c.CENTER});k.axes.lx=p.axes[0];k.axes.ly=p.axes[1];k.axes.rx=p.axes[2];k.axes.ry=p.axes[3];k.axes.ltrigger=p.buttons[6].value;k.axes.rtrigger=p.buttons[7].value;k.hat="";k.hatmap=c.CENTER;for(var l=0;ll)k.buttons[c.mapping_array[l]]=p.buttons[l].pressed,p.buttons[l].was_pressed&&this.trigger(c.mapping_array[l]+"_button_event");else switch(l){case 12:p.buttons[l].pressed&&(k.hat+="up",k.hatmap|=c.UP); -break;case 13:p.buttons[l].pressed&&(k.hat+="down",k.hatmap|=c.DOWN);break;case 14:p.buttons[l].pressed&&(k.hat+="left",k.hatmap|=c.LEFT);break;case 15:p.buttons[l].pressed&&(k.hat+="right",k.hatmap|=c.RIGHT);break;case 16:k.buttons.home=p.buttons[l].pressed}p.xbox=k;return p}};c.prototype.onDrawBackground=function(c){if(!this.flags.collapsed){var k=this._left_axis,l=this._right_axis;c.strokeStyle="#88A";c.strokeRect(0.5*(k[0]+1)*this.size[0]-4,0.5*(k[1]+1)*this.size[1]-4,8,8);c.strokeStyle="#8A8"; -c.strokeRect(0.5*(l[0]+1)*this.size[0]-4,0.5*(l[1]+1)*this.size[1]-4,8,8);k=this.size[1]/this._current_buttons.length;c.fillStyle="#AEB";for(l=0;lm)k.buttons[c.mapping_array[m]]=p.buttons[m].pressed,p.buttons[m].was_pressed&&this.trigger(c.mapping_array[m]+"_button_event");else switch(m){case 12:p.buttons[m].pressed&&(k.hat+="up",k.hatmap|=c.UP); +break;case 13:p.buttons[m].pressed&&(k.hat+="down",k.hatmap|=c.DOWN);break;case 14:p.buttons[m].pressed&&(k.hat+="left",k.hatmap|=c.LEFT);break;case 15:p.buttons[m].pressed&&(k.hat+="right",k.hatmap|=c.RIGHT);break;case 16:k.buttons.home=p.buttons[m].pressed}p.xbox=k;return p}};c.prototype.onDrawBackground=function(c){if(!this.flags.collapsed){var k=this._left_axis,m=this._right_axis;c.strokeStyle="#88A";c.strokeRect(0.5*(k[0]+1)*this.size[0]-4,0.5*(k[1]+1)*this.size[1]-4,8,8);c.strokeStyle="#8A8"; +c.strokeRect(0.5*(m[0]+1)*this.size[0]-4,0.5*(m[1]+1)*this.size[1]-4,8,8);k=this.size[1]/this._current_buttons.length;c.fillStyle="#AEB";for(m=0;m","enum",{values:a.values});this.size=[80,60]}function b(){this.addInput("inc","number");this.addOutput("total","number");this.addProperty("increment",1);this.addProperty("value",0)}function d(){this.addInput("v","number");this.addOutput("sin","number");this.addProperty("amplitude",1);this.addProperty("offset",0);this.bgImageUrl= -"nodes/imgs/icon-sin.png"}function h(){this.addInput("x","number");this.addInput("y","number");this.addOutput("","number");this.properties={x:1,y:1,formula:"x+y"};this.code_widget=this.addWidget("text","F(x,y)",this.properties.formula,function(a,b,d){d.properties.formula=a});this.addWidget("toggle","allow",F.allow_scripts,function(a){F.allow_scripts=a});this._func=null}function f(){this.addInput("vec2","vec2");this.addOutput("x","number");this.addOutput("y","number")}function E(){this.addInputs([["x", -"number"],["y","number"]]);this.addOutput("vec2","vec2");this.properties={x:0,y:0};this._data=new Float32Array(2)}function q(){this.addInput("vec3","vec3");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number")}function I(){this.addInputs([["x","number"],["y","number"],["z","number"]]);this.addOutput("vec3","vec3");this.properties={x:0,y:0,z:0};this._data=new Float32Array(3)}function J(){this.addInput("vec4","vec4");this.addOutput("x","number");this.addOutput("y","number"); -this.addOutput("z","number");this.addOutput("w","number")}function H(){this.addInputs([["x","number"],["y","number"],["z","number"],["w","number"]]);this.addOutput("vec4","vec4");this.properties={x:0,y:0,z:0,w:0};this._data=new Float32Array(4)}var F=y.LiteGraph;c.title="Converter";c.desc="type A to type B";c.prototype.onExecute=function(){var a=this.getInputData(0);if(null!=a&&this.outputs)for(var b=0;ba&&(a+=1024);var c=Math.floor(a);a-=c;d=e.data[c];c=e.data[1023==c?0:c+1];b&&(a=a*a*a*(a*(6*a-15)+10));return d*(1-a)+c*a};e.prototype.onExecute=function(){var a=this.getInputData(0)||0,a=e.getValue(a,this.properties.smooth), -b=this.properties.min;this._last_v=a*(this.properties.max-b)+b;this.setOutputData(0,this._last_v)};e.prototype.onDrawBackground=function(a){this.outputs[0].label=(this._last_v||0).toFixed(3)};F.registerNodeType("math/noise",e);C.title="Spikes";C.desc="spike every random time";C.prototype.onExecute=function(){var a=this.graph.elapsed_time;this._remaining_time-=a;this._blink_time-=a;a=0;0this._remaining_time?(this._remaining_time= -Math.random()*(this.properties.max_time-this.properties.min_time)+this.properties.min_time,this._blink_time=this.properties.duration,this.boxcolor="#FFF"):this.boxcolor="#000";this.setOutputData(0,a)};F.registerNodeType("math/spikes",C);z.title="Clamp";z.desc="Clamp number between min and max";z.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(a=Math.max(this.properties.min,a),a=Math.min(this.properties.max,a),this.setOutputData(0,a))};z.prototype.getCode=function(a){a="";this.isInputConnected(0)&& -(a+="clamp({{0}},"+this.properties.min+","+this.properties.max+")");return a};F.registerNodeType("math/clamp",z);u.title="Lerp";u.desc="Linear Interpolation";u.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.getInputData(1);null==b&&(b=0);var d=this.properties.f,c=this.getInputData(2);void 0!==c&&(d=c);this.setOutputData(0,a*(1-d)+b*d)};u.prototype.onGetInputs=function(){return[["f","number"]]};F.registerNodeType("math/lerp",u);D.title="Abs";D.desc="Absolute";D.prototype.onExecute= -function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.abs(a))};F.registerNodeType("math/abs",D);B.title="Floor";B.desc="Floor number to remove fractional part";B.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.floor(a))};F.registerNodeType("math/floor",B);g.title="Frac";g.desc="Returns fractional part";g.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a%1)};F.registerNodeType("math/frac",g);m.title= -"Smoothstep";m.desc="Smoothstep";m.prototype.onExecute=function(){var a=this.getInputData(0);if(void 0!==a){var b=this.properties.A,a=Math.clamp((a-b)/(this.properties.B-b),0,1);this.setOutputData(0,a*a*(3-2*a))}};F.registerNodeType("math/smoothstep",m);s.title="Scale";s.desc="v * factor";s.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a*this.properties.factor)};F.registerNodeType("math/scale",s);A.title="Gate";A.desc="if v is true, then outputs A, otherwise B"; -A.prototype.onExecute=function(){var a=this.getInputData(0);this.setOutputData(0,this.getInputData(a?1:2))};F.registerNodeType("math/gate",A);r.title="Average";r.desc="Average Filter";r.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this._values.length;this._values[this._current%b]=a;this._current+=1;this._current>b&&(this._current=0);for(var d=a=0;db&&(b=1);this.properties.samples= -Math.round(b);var d=this._values;this._values=new Float32Array(this.properties.samples);d.length<=this._values.length?this._values.set(d):this._values.set(d.subarray(0,this._values.length))};F.registerNodeType("math/average",r);n.title="TendTo";n.desc="moves the output value always closer to the input";n.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.factor;this._value=null==this._value?a:this._value*(1-b)+a*b;this.setOutputData(0,this._value)};F.registerNodeType("math/tendTo", -n);x.values="+ - * / % ^ max min".split(" ");x.title="Operation";x.desc="Easy math operators";x["@OP"]={type:"enum",title:"operation",values:x.values};x.size=[100,60];x.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"};x.prototype.setValue=function(a){"string"==typeof a&&(a=parseFloat(a));this.properties.value=a};x.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);null!=a? -this.properties.A=a:a=this.properties.A;null!=b?this.properties.B=b:b=this.properties.B;var d=0;switch(this.properties.OP){case "+":d=a+b;break;case "-":d=a-b;break;case "x":case "X":case "*":d=a*b;break;case "/":d=a/b;break;case "%":d=a%b;break;case "^":d=Math.pow(a,b);break;case "max":d=Math.max(a,b);break;case "min":d=Math.min(a,b);break;default:console.warn("Unknown operation: "+this.properties.OP)}this.setOutputData(0,d)};x.prototype.onDrawBackground=function(a){this.flags.collapsed||(a.font= -"40px Arial",a.fillStyle="#666",a.textAlign="center",a.fillText(this.properties.OP,0.5*this.size[0],0.5*(this.size[1]+F.NODE_TITLE_HEIGHT)),a.textAlign="left")};F.registerNodeType("math/operation",x);F.registerSearchboxExtra("math/operation","MAX",{properties:{OP:"max"},title:"MAX()"});F.registerSearchboxExtra("math/operation","MIN",{properties:{OP:"min"},title:"MIN()"});v.title="Compare";v.desc="compares between two values";v.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1); -void 0!==a?this.properties.A=a:a=this.properties.A;void 0!==b?this.properties.B=b:b=this.properties.B;for(var d=0,c=this.outputs.length;dB":f=a>b;break;case "A=B":f=a>=b}this.setOutputData(d,f)}}};v.prototype.onGetOutputs=function(){return[["A==B","boolean"],["A!=B","boolean"],["A>B","boolean"],["A=B", -"boolean"],["A<=B","boolean"]]};F.registerNodeType("math/compare",v);F.registerSearchboxExtra("math/compare","==",{outputs:[["A==B","boolean"]],title:"A==B"});F.registerSearchboxExtra("math/compare","!=",{outputs:[["A!=B","boolean"]],title:"A!=B"});F.registerSearchboxExtra("math/compare",">",{outputs:[["A>B","boolean"]],title:"A>B"});F.registerSearchboxExtra("math/compare","<",{outputs:[["A=",{outputs:[["A>=B","boolean"]],title:"A>=B"}); +["out_min","number"],["out_max","number"]]};F.registerNodeType("math/range",k);v.title="Rand";v.desc="Random number";v.prototype.onExecute=function(){if(this.inputs)for(var a=0;aa&&(a+=1024);var c=Math.floor(a);a-=c;d=e.data[c];c=e.data[1023==c?0:c+1];b&&(a=a*a*a*(a*(6*a-15)+10));return d*(1-a)+c*a};e.prototype.onExecute=function(){var a=this.getInputData(0)||0,a=e.getValue(a,this.properties.smooth), +b=this.properties.min;this._last_v=a*(this.properties.max-b)+b;this.setOutputData(0,this._last_v)};e.prototype.onDrawBackground=function(a){this.outputs[0].label=(this._last_v||0).toFixed(3)};F.registerNodeType("math/noise",e);E.title="Spikes";E.desc="spike every random time";E.prototype.onExecute=function(){var a=this.graph.elapsed_time;this._remaining_time-=a;this._blink_time-=a;a=0;0this._remaining_time?(this._remaining_time= +Math.random()*(this.properties.max_time-this.properties.min_time)+this.properties.min_time,this._blink_time=this.properties.duration,this.boxcolor="#FFF"):this.boxcolor="#000";this.setOutputData(0,a)};F.registerNodeType("math/spikes",E);z.title="Clamp";z.desc="Clamp number between min and max";z.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(a=Math.max(this.properties.min,a),a=Math.min(this.properties.max,a),this.setOutputData(0,a))};z.prototype.getCode=function(a){a="";this.isInputConnected(0)&& +(a+="clamp({{0}},"+this.properties.min+","+this.properties.max+")");return a};F.registerNodeType("math/clamp",z);y.title="Lerp";y.desc="Linear Interpolation";y.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.getInputData(1);null==b&&(b=0);var d=this.properties.f,c=this.getInputData(2);void 0!==c&&(d=c);this.setOutputData(0,a*(1-d)+b*d)};y.prototype.onGetInputs=function(){return[["f","number"]]};F.registerNodeType("math/lerp",y);D.title="Abs";D.desc="Absolute";D.prototype.onExecute= +function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.abs(a))};F.registerNodeType("math/abs",D);A.title="Floor";A.desc="Floor number to remove fractional part";A.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.floor(a))};F.registerNodeType("math/floor",A);g.title="Frac";g.desc="Returns fractional part";g.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a%1)};F.registerNodeType("math/frac",g);n.title= +"Smoothstep";n.desc="Smoothstep";n.prototype.onExecute=function(){var a=this.getInputData(0);if(void 0!==a){var b=this.properties.A,a=Math.clamp((a-b)/(this.properties.B-b),0,1);this.setOutputData(0,a*a*(3-2*a))}};F.registerNodeType("math/smoothstep",n);t.title="Scale";t.desc="v * factor";t.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a*this.properties.factor)};F.registerNodeType("math/scale",t);B.title="Gate";B.desc="if v is true, then outputs A, otherwise B"; +B.prototype.onExecute=function(){var a=this.getInputData(0);this.setOutputData(0,this.getInputData(a?1:2))};F.registerNodeType("math/gate",B);l.title="Average";l.desc="Average Filter";l.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this._values.length;this._values[this._current%b]=a;this._current+=1;this._current>b&&(this._current=0);for(var d=a=0;db&&(b=1);this.properties.samples= +Math.round(b);var d=this._values;this._values=new Float32Array(this.properties.samples);d.length<=this._values.length?this._values.set(d):this._values.set(d.subarray(0,this._values.length))};F.registerNodeType("math/average",l);q.title="TendTo";q.desc="moves the output value always closer to the input";q.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.factor;this._value=null==this._value?a:this._value*(1-b)+a*b;this.setOutputData(0,this._value)};F.registerNodeType("math/tendTo", +q);w.values="+ - * / % ^ max min".split(" ");w.title="Operation";w.desc="Easy math operators";w["@OP"]={type:"enum",title:"operation",values:w.values};w.size=[100,60];w.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"};w.prototype.setValue=function(a){"string"==typeof a&&(a=parseFloat(a));this.properties.value=a};w.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);null!=a? +this.properties.A=a:a=this.properties.A;null!=b?this.properties.B=b:b=this.properties.B;var d=0;switch(this.properties.OP){case "+":d=a+b;break;case "-":d=a-b;break;case "x":case "X":case "*":d=a*b;break;case "/":d=a/b;break;case "%":d=a%b;break;case "^":d=Math.pow(a,b);break;case "max":d=Math.max(a,b);break;case "min":d=Math.min(a,b);break;default:console.warn("Unknown operation: "+this.properties.OP)}this.setOutputData(0,d)};w.prototype.onDrawBackground=function(a){this.flags.collapsed||(a.font= +"40px Arial",a.fillStyle="#666",a.textAlign="center",a.fillText(this.properties.OP,0.5*this.size[0],0.5*(this.size[1]+F.NODE_TITLE_HEIGHT)),a.textAlign="left")};F.registerNodeType("math/operation",w);F.registerSearchboxExtra("math/operation","MAX",{properties:{OP:"max"},title:"MAX()"});F.registerSearchboxExtra("math/operation","MIN",{properties:{OP:"min"},title:"MIN()"});x.title="Compare";x.desc="compares between two values";x.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1); +void 0!==a?this.properties.A=a:a=this.properties.A;void 0!==b?this.properties.B=b:b=this.properties.B;for(var d=0,c=this.outputs.length;dB":f=a>b;break;case "A=B":f=a>=b}this.setOutputData(d,f)}}};x.prototype.onGetOutputs=function(){return[["A==B","boolean"],["A!=B","boolean"],["A>B","boolean"],["A=B", +"boolean"],["A<=B","boolean"]]};F.registerNodeType("math/compare",x);F.registerSearchboxExtra("math/compare","==",{outputs:[["A==B","boolean"]],title:"A==B"});F.registerSearchboxExtra("math/compare","!=",{outputs:[["A!=B","boolean"]],title:"A!=B"});F.registerSearchboxExtra("math/compare",">",{outputs:[["A>B","boolean"]],title:"A>B"});F.registerSearchboxExtra("math/compare","<",{outputs:[["A=",{outputs:[["A>=B","boolean"]],title:"A>=B"}); F.registerSearchboxExtra("math/compare","<=",{outputs:[["A<=B","boolean"]],title:"A<=B"});a.values="> < == != <= >= || &&".split(" ");a["@OP"]={type:"enum",title:"operation",values:a.values};a.title="Condition";a.desc="evaluates condition between A and B";a.prototype.getTitle=function(){return"A "+this.properties.OP+" B"};a.prototype.onExecute=function(){var a=this.getInputData(0);void 0===a?a=this.properties.A:this.properties.A=a;var b=this.getInputData(1);void 0===b?b=this.properties.B:this.properties.B= b;var d=!0;switch(this.properties.OP){case ">":d=a>b;break;case "<":d=a=":d=a>=b;break;case "||":d=a||b;break;case "&&":d=a&&b}this.setOutputData(0,d);this.setOutputData(1,!d)};F.registerNodeType("math/condition",a);b.title="Accumulate";b.desc="Increments a value every time";b.prototype.onExecute=function(){null===this.properties.value&&(this.properties.value=0);var a=this.getInputData(0);this.properties.value=null!== a?this.properties.value+a:this.properties.value+this.properties.increment;this.setOutputData(0,this.properties.value)};F.registerNodeType("math/accumulate",b);d.title="Trigonometry";d.desc="Sin Cos Tan";d.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.amplitude,d=this.findInputSlot("amplitude");-1!=d&&(b=this.getInputData(d));var c=this.properties.offset,d=this.findInputSlot("offset");-1!=d&&(c=this.getInputData(d));for(var d=0,h=this.outputs.length;d< h;++d){var f;switch(this.outputs[d].name){case "sin":f=Math.sin(a);break;case "cos":f=Math.cos(a);break;case "tan":f=Math.tan(a);break;case "asin":f=Math.asin(a);break;case "acos":f=Math.acos(a);break;case "atan":f=Math.atan(a)}this.setOutputData(d,b*f+c)}};d.prototype.onGetInputs=function(){return[["v","number"],["amplitude","number"],["offset","number"]]};d.prototype.onGetOutputs=function(){return[["sin","number"],["cos","number"],["tan","number"],["asin","number"],["acos","number"],["atan","number"]]}; -F.registerNodeType("math/trigonometry",d);F.registerSearchboxExtra("math/trigonometry","SIN()",{outputs:[["sin","number"]],title:"SIN()"});F.registerSearchboxExtra("math/trigonometry","COS()",{outputs:[["cos","number"]],title:"COS()"});F.registerSearchboxExtra("math/trigonometry","TAN()",{outputs:[["tan","number"]],title:"TAN()"});h.title="Formula";h.desc="Compute formula";h.size=[160,100];r.prototype.onPropertyChanged=function(a,b){"formula"==a&&(this.code_widget.value=b)};h.prototype.onExecute= +F.registerNodeType("math/trigonometry",d);F.registerSearchboxExtra("math/trigonometry","SIN()",{outputs:[["sin","number"]],title:"SIN()"});F.registerSearchboxExtra("math/trigonometry","COS()",{outputs:[["cos","number"]],title:"COS()"});F.registerSearchboxExtra("math/trigonometry","TAN()",{outputs:[["tan","number"]],title:"TAN()"});h.title="Formula";h.desc="Compute formula";h.size=[160,100];l.prototype.onPropertyChanged=function(a,b){"formula"==a&&(this.code_widget.value=b)};h.prototype.onExecute= function(){if(F.allow_scripts){var a=this.getInputData(0),b=this.getInputData(1);null!=a?this.properties.x=a:a=this.properties.x;null!=b?this.properties.y=b:b=this.properties.y;var d;try{this._func&&this._func_code==this.properties.formula||(this._func=new Function("x","y","TIME","return "+this.properties.formula),this._func_code=this.properties.formula),d=this._func(a,b,this.graph.globaltime),this.boxcolor=null}catch(c){this.boxcolor="red"}this.setOutputData(0,d)}};h.prototype.getTitle=function(){return this._func_code|| -"Formula"};h.prototype.onDrawBackground=function(){var a=this.properties.formula;this.outputs&&this.outputs.length&&(this.outputs[0].label=a)};F.registerNodeType("math/formula",h);f.title="Vec2->XY";f.desc="vector 2 to components";f.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]))};F.registerNodeType("math3d/vec2-to-xy",f);E.title="XY->Vec2";E.desc="components to vector2";E.prototype.onExecute=function(){var a=this.getInputData(0); -null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this._data;d[0]=a;d[1]=b;this.setOutputData(0,d)};F.registerNodeType("math3d/xy-to-vec2",E);q.title="Vec3->XYZ";q.desc="vector 3 to components";q.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]))};F.registerNodeType("math3d/vec3-to-xyz",q);I.title="XYZ->Vec3";I.desc="components to vector3";I.prototype.onExecute= +"Formula"};h.prototype.onDrawBackground=function(){var a=this.properties.formula;this.outputs&&this.outputs.length&&(this.outputs[0].label=a)};F.registerNodeType("math/formula",h);f.title="Vec2->XY";f.desc="vector 2 to components";f.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]))};F.registerNodeType("math3d/vec2-to-xy",f);s.title="XY->Vec2";s.desc="components to vector2";s.prototype.onExecute=function(){var a=this.getInputData(0); +null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this._data;d[0]=a;d[1]=b;this.setOutputData(0,d)};F.registerNodeType("math3d/xy-to-vec2",s);r.title="Vec3->XYZ";r.desc="vector 3 to components";r.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]))};F.registerNodeType("math3d/vec3-to-xyz",r);I.title="XYZ->Vec3";I.desc="components to vector3";I.prototype.onExecute= function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this._data;c[0]=a;c[1]=b;c[2]=d;this.setOutputData(0,c)};F.registerNodeType("math3d/xyz-to-vec3",I);J.title="Vec4->XYZW";J.desc="vector 4 to components";J.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]),this.setOutputData(3, -a[3]))};F.registerNodeType("math3d/vec4-to-xyzw",J);H.title="XYZW->Vec4";H.desc="components to vector4";H.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this.getInputData(3);null==c&&(c=this.properties.w);var f=this._data;f[0]=a;f[1]=b;f[2]=d;f[3]=c;this.setOutputData(0,f)};F.registerNodeType("math3d/xyzw-to-vec4",H);y.glMatrix&&(y=function(){this.addOutput("quat", -"quat");this.properties={x:0,y:0,z:0,w:1};this._value=quat.create()},y.title="Quaternion",y.desc="quaternion",y.prototype.onExecute=function(){this._value[0]=this.properties.x;this._value[1]=this.properties.y;this._value[2]=this.properties.z;this._value[3]=this.properties.w;this.setOutputData(0,this._value)},F.registerNodeType("math3d/quaternion",y),y=function(){this.addInputs([["degrees","number"],["axis","vec3"]]);this.addOutput("quat","quat");this.properties={angle:90,axis:vec3.fromValues(0,1, -0)};this._value=quat.create()},y.title="Rotation",y.desc="quaternion rotation",y.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.angle);var b=this.getInputData(1);null==b&&(b=this.properties.axis);a=quat.setAxisAngle(this._value,b,0.0174532925*a);this.setOutputData(0,a)},F.registerNodeType("math3d/rotation",y),y=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},y.title="Rot. Vec3",y.desc= -"rotate a point",y.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.vec);var b=this.getInputData(1);null==b?this.setOutputData(a):this.setOutputData(0,vec3.transformQuat(vec3.create(),a,b))},F.registerNodeType("math3d/rotate_vec3",y),y=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},y.title="Mult. Quat",y.desc="rotate quaternion",y.prototype.onExecute=function(){var a=this.getInputData(0);if(null!= -a){var b=this.getInputData(1);null!=b&&(a=quat.multiply(this._value,a,b),this.setOutputData(0,a))}},F.registerNodeType("math3d/mult-quat",y),y=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp","quat");this.addProperty("factor",0.5);this._value=quat.create()},y.title="Quat Slerp",y.desc="quaternion spherical interpolation",y.prototype.onExecute=function(){var a=this.getInputData(0);if(null!=a){var b=this.getInputData(1);if(null!=b){var d=this.properties.factor; -null!=this.getInputData(2)&&(d=this.getInputData(2));a=quat.slerp(this._value,a,b,d);this.setOutputData(0,a)}}},F.registerNodeType("math3d/quat-slerp",y))})(this); -(function(y){function c(){this.addInput("T","vec3");this.addInput("R","vec3");this.addInput("S","vec3");this.addOutput("mat4","mat4");this.properties={T:[0,0,0],R:[0,0,0],S:[1,1,1],R_in_degrees:!0};this._result=mat4.create();this._must_update=!0}function l(){this.addInput("A","number,vec3");this.addInput("B","number,vec3");this.addOutput("=","vec3");this.addProperty("OP","+","enum",{values:l.values});this._result=vec3.create()}function p(){this.addInput("in","vec3");this.addInput("f","number");this.addOutput("out", -"vec3");this.properties={f:1};this._data=new Float32Array(3)}function k(){this.addInput("in","vec3");this.addOutput("out","number")}function w(){this.addInput("in","vec3");this.addOutput("out","vec3");this._data=new Float32Array(3)}function e(){this.addInput("A","vec3");this.addInput("B","vec3");this.addInput("f","vec3");this.addOutput("out","vec3");this.properties={f:0.5};this._data=new Float32Array(3)}function C(){this.addInput("A","vec3");this.addInput("B","vec3");this.addOutput("out","number")} -var z=y.LiteGraph;c.title="mat4";c.temp_quat=new Float32Array([0,0,0,1]);c.temp_mat4=new Float32Array(16);c.temp_vec3=new Float32Array(3);c.prototype.onPropertyChanged=function(c,e){this._must_update=!0};c.prototype.onExecute=function(){var e=this._result,k=c.temp_quat,p=c.temp_mat4,g=c.temp_vec3,m=this.getInputData(0),s=this.getInputData(1),l=this.getInputData(2);if(this._must_update||m||s||l)m=m||this.properties.T,s=s||this.properties.R,l=l||this.properties.S,mat4.identity(e),mat4.translate(e,e, -m),this.properties.R_in_degrees?(g.set(s),vec3.scale(g,g,DEG2RAD),quat.fromEuler(k,g)):quat.fromEuler(k,s),mat4.fromQuat(p,k),mat4.multiply(e,e,p),mat4.scale(e,e,l);this.setOutputData(0,e)};z.registerNodeType("math3d/mat4",c);l.values="+ - * / % ^ max min".split(" ");l.title="Operation";l.desc="Easy math 3D operators";l["@OP"]={type:"enum",title:"operation",values:l.values};l.size=[100,60];l.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+ -"(A,B)":"A "+this.properties.OP+" B"};l.prototype.onExecute=function(){var c=this.getInputData(0),e=this.getInputData(1);if(null!=c&&null!=e){c.constructor===Number&&(c=[c,c,c]);e.constructor===Number&&(e=[e,e,e]);var k=this._result;switch(this.properties.OP){case "+":k=vec3.add(k,c,e);break;case "-":k=vec3.sub(k,c,e);break;case "x":case "X":case "*":k=vec3.mul(k,c,e);break;case "/":k=vec3.div(k,c,e);break;case "%":k[0]=c[0]%e[0];k[1]=c[1]%e[1];k[2]=c[2]%e[2];break;case "^":k[0]=Math.pow(c[0],e[0]); -k[1]=Math.pow(c[1],e[1]);k[2]=Math.pow(c[2],e[2]);break;case "max":k[0]=Math.max(c[0],e[0]);k[1]=Math.max(c[1],e[1]);k[2]=Math.max(c[2],e[2]);break;case "min":k[0]=Math.min(c[0],e[0]);k[1]=Math.min(c[1],e[1]);k[2]=Math.min(c[2],e[2]);break;default:console.warn("Unknown operation: "+this.properties.OP)}this.setOutputData(0,k)}};l.prototype.onDrawBackground=function(c){this.flags.collapsed||(c.font="40px Arial",c.fillStyle="#666",c.textAlign="center",c.fillText(this.properties.OP,0.5*this.size[0],0.5* -(this.size[1]+z.NODE_TITLE_HEIGHT)),c.textAlign="left")};z.registerNodeType("math3d/operation",l);p.title="vec3_scale";p.desc="scales the components of a vec3";p.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null==e&&(e=this.properties.f);var k=this._data;k[0]=c[0]*e;k[1]=c[1]*e;k[2]=c[2]*e;this.setOutputData(0,k)}};z.registerNodeType("math3d/vec3-scale",p);k.title="vec3_length";k.desc="returns the module of a vector";k.prototype.onExecute=function(){var c= -this.getInputData(0);null!=c&&(c=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),this.setOutputData(0,c))};z.registerNodeType("math3d/vec3-length",k);w.title="vec3_normalize";w.desc="returns the vector normalized";w.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),k=this._data;k[0]=c[0]/e;k[1]=c[1]/e;k[2]=c[2]/e;this.setOutputData(0,k)}};z.registerNodeType("math3d/vec3-normalize",w);e.title="vec3_lerp";e.desc="returns the interpolated vector"; -e.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);if(null!=e){var k=this.getInputOrProperty("f"),g=this._data;g[0]=c[0]*(1-k)+e[0]*k;g[1]=c[1]*(1-k)+e[1]*k;g[2]=c[2]*(1-k)+e[2]*k;this.setOutputData(0,g)}}};z.registerNodeType("math3d/vec3-lerp",e);C.title="vec3_dot";C.desc="returns the dot product";C.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null!=e&&this.setOutputData(0,c[0]*e[0]+c[1]*e[1]+c[2]* -e[2])}};z.registerNodeType("math3d/vec3-dot",C);y.glMatrix?(y=function(){this.addOutput("quat","quat");this.properties={x:0,y:0,z:0,w:1,normalize:!1};this._value=quat.create()},y.title="Quaternion",y.desc="quaternion",y.prototype.onExecute=function(){this._value[0]=this.getInputOrProperty("x");this._value[1]=this.getInputOrProperty("y");this._value[2]=this.getInputOrProperty("z");this._value[3]=this.getInputOrProperty("w");this.properties.normalize&&quat.normalize(this._value,this._value);this.setOutputData(0, -this._value)},y.prototype.onGetInputs=function(){return[["x","number"],["y","number"],["z","number"],["w","number"]]},z.registerNodeType("math3d/quaternion",y),y=function(){this.addInputs([["degrees","number"],["axis","vec3"]]);this.addOutput("quat","quat");this.properties={angle:90,axis:vec3.fromValues(0,1,0)};this._value=quat.create()},y.title="Rotation",y.desc="quaternion rotation",y.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.angle);var e=this.getInputData(1); -null==e&&(e=this.properties.axis);c=quat.setAxisAngle(this._value,e,0.0174532925*c);this.setOutputData(0,c)},z.registerNodeType("math3d/rotation",y),y=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},y.title="Rot. Vec3",y.desc="rotate a point",y.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.vec);var e=this.getInputData(1);null==e?this.setOutputData(c):this.setOutputData(0,vec3.transformQuat(vec3.create(), -c,e))},z.registerNodeType("math3d/rotate_vec3",y),y=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},y.title="Mult. Quat",y.desc="rotate quaternion",y.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null!=e&&(c=quat.multiply(this._value,c,e),this.setOutputData(0,c))}},z.registerNodeType("math3d/mult-quat",y),y=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp", -"quat");this.addProperty("factor",0.5);this._value=quat.create()},y.title="Quat Slerp",y.desc="quaternion spherical interpolation",y.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);if(null!=e){var k=this.properties.factor;null!=this.getInputData(2)&&(k=this.getInputData(2));c=quat.slerp(this._value,c,e,k);this.setOutputData(0,c)}}},z.registerNodeType("math3d/quat-slerp",y),y=function(){this.addInput("vec3","vec3");this.addOutput("remap","vec3");this.addOutput("clamped", -"vec3");this.properties={clamp:!0,range_min:[-1,-1,0],range_max:[1,1,0],target_min:[-1,-1,0],target_max:[1,1,0]};this._value=vec3.create();this._clamped=vec3.create()},y.title="Remap Range",y.desc="remap a 3D range",y.prototype.onExecute=function(){var c=this.getInputData(0);c&&this._value.set(c);for(var c=this.properties.range_min,e=this.properties.range_max,k=this.properties.target_min,g=this.properties.target_max,m=0;3>m;++m){var s=e[m]-c[m];this._clamped[m]=Math.clamp(this._value[m],c[m],e[m]); -0==s?this._value[m]=0.5*(k[m]+g[m]):(s=(this._value[m]-c[m])/s,this.properties.clamp&&(s=Math.clamp(s,0,1)),this._value[m]=k[m]+s*(g[m]-k[m]))}this.setOutputData(0,this._value);this.setOutputData(1,this._clamped)},z.registerNodeType("math3d/remap_range",y)):console.warn("No glmatrix found, some Math3D nodes may not work")})(this); -(function(y){function c(c,k){return c==k}function l(){this.addInput("","string");this.addOutput("table","table");this.addOutput("rows","number");this.addProperty("value","");this.addProperty("separator",",");this._table=null}y=y.LiteGraph;y.wrapFunctionAsNode("string/toString",c,["*"],"String");y.wrapFunctionAsNode("string/compare",c,["string","string"],"boolean");y.wrapFunctionAsNode("string/concatenate",function(c,k){return void 0===c?k:void 0===k?c:c+k},["string","string"],"string");y.wrapFunctionAsNode("string/contains", -function(c,k){return void 0===c||void 0===k?!1:-1!=c.indexOf(k)},["string","string"],"boolean");y.wrapFunctionAsNode("string/toUpperCase",function(c){return null!=c&&c.constructor===String?c.toUpperCase():c},["string"],"string");y.wrapFunctionAsNode("string/split",function(c,k){null==k&&(k=this.properties.separator);if(null==c)return[];if(c.constructor===String)return c.split(k||" ");if(c.constructor===Array){for(var l=[],e=0;ee;++e){var g=this.getInputData(e);if(null!=g){var r=this.values[e];r.push(g);r.length>c[0]&&r.shift()}}}};c.prototype.onDrawBackground=function(e){if(!this.flags.collapsed){var g=this.size,k=0.5*g[1]/ -this.properties.scale,r=c.colors,n=0.5*g[1];e.fillStyle="#000";e.fillRect(0,0,g[0],g[1]);e.strokeStyle="#555";e.beginPath();e.moveTo(0,n);e.lineTo(g[0],n);e.stroke();if(this.inputs)for(var l=0;4>l;++l){var v=this.values[l];if(this.inputs[l]&&this.inputs[l].link){e.strokeStyle=r[l];e.beginPath();var a=v[0]*k*-1+n;e.moveTo(0,Math.clamp(a,0,g[1]));for(var b=1;be&&(e=0);if(0!=c.length){var g=[0,0,0];if(0==e)g=c[0];else if(1==e)g=c[c.length-1];else{var r= -(c.length-1)*e,e=c[Math.floor(r)],c=c[Math.floor(r)+1],r=r-Math.floor(r);g[0]=e[0]*(1-r)+c[0]*r;g[1]=e[1]*(1-r)+c[1]*r;g[2]=e[2]*(1-r)+c[2]*r}for(var n in g)g[n]/=255;this.boxcolor=colorToString(g);this.setOutputData(0,g)}};g.registerNodeType("color/palette",p);k.title="Frame";k.desc="Frame viewerew";k.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}];k.prototype.onDrawBackground=function(c){this.frame&&!this.flags.collapsed&&c.drawImage(this.frame, -0,0,this.size[0],this.size[1])};k.prototype.onExecute=function(){this.frame=this.getInputData(0);this.setDirtyCanvas(!0)};k.prototype.onWidget=function(c,e){if("resize"==e.name&&this.frame){var g=this.frame.width,r=this.frame.height;g||null==this.frame.videoWidth||(g=this.frame.videoWidth,r=this.frame.videoHeight);g&&r&&(this.size=[g,r]);this.setDirtyCanvas(!0,!0)}else"view"==e.name&&this.show()};k.prototype.show=function(){showElement&&this.frame&&showElement(this.frame)};g.registerNodeType("graphics/frame", -k);w.title="Image fade";w.desc="Fades between images";w.widgets=[{name:"resizeA",text:"Resize to A",type:"button"},{name:"resizeB",text:"Resize to B",type:"button"}];w.prototype.onAdded=function(){this.createCanvas();var c=this.canvas.getContext("2d");c.fillStyle="#000";c.fillRect(0,0,this.properties.width,this.properties.height)};w.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};w.prototype.onExecute= -function(){var c=this.canvas.getContext("2d");this.canvas.width=this.canvas.width;var e=this.getInputData(0);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);e=this.getInputData(2);null==e?e=this.properties.fade:this.properties.fade=e;c.globalAlpha=e;e=this.getInputData(1);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);c.globalAlpha=1;this.setOutputData(0,this.canvas);this.setDirtyCanvas(!0)};g.registerNodeType("graphics/imagefade",w);e.title="Crop";e.desc="Crop Image"; +a[3]))};F.registerNodeType("math3d/vec4-to-xyzw",J);H.title="XYZW->Vec4";H.desc="components to vector4";H.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this.getInputData(3);null==c&&(c=this.properties.w);var h=this._data;h[0]=a;h[1]=b;h[2]=d;h[3]=c;this.setOutputData(0,h)};F.registerNodeType("math3d/xyzw-to-vec4",H)})(this); +(function(C){function c(){this.addInput("T","vec3");this.addInput("R","vec3");this.addInput("S","vec3");this.addOutput("mat4","mat4");this.properties={T:[0,0,0],R:[0,0,0],S:[1,1,1],R_in_degrees:!0};this._result=mat4.create();this._must_update=!0}function m(){this.addInput("A","number,vec3");this.addInput("B","number,vec3");this.addOutput("=","vec3");this.addProperty("OP","+","enum",{values:m.values});this._result=vec3.create()}function p(){this.addInput("in","vec3");this.addInput("f","number");this.addOutput("out", +"vec3");this.properties={f:1};this._data=new Float32Array(3)}function k(){this.addInput("in","vec3");this.addOutput("out","number")}function v(){this.addInput("in","vec3");this.addOutput("out","vec3");this._data=new Float32Array(3)}function e(){this.addInput("A","vec3");this.addInput("B","vec3");this.addInput("f","vec3");this.addOutput("out","vec3");this.properties={f:0.5};this._data=new Float32Array(3)}function E(){this.addInput("A","vec3");this.addInput("B","vec3");this.addOutput("out","number")} +var z=C.LiteGraph;c.title="mat4";c.temp_quat=new Float32Array([0,0,0,1]);c.temp_mat4=new Float32Array(16);c.temp_vec3=new Float32Array(3);c.prototype.onPropertyChanged=function(c,e){this._must_update=!0};c.prototype.onExecute=function(){var e=this._result,k=c.temp_quat,p=c.temp_mat4,g=c.temp_vec3,n=this.getInputData(0),t=this.getInputData(1),m=this.getInputData(2);if(this._must_update||n||t||m)n=n||this.properties.T,t=t||this.properties.R,m=m||this.properties.S,mat4.identity(e),mat4.translate(e,e, +n),this.properties.R_in_degrees?(g.set(t),vec3.scale(g,g,DEG2RAD),quat.fromEuler(k,g)):quat.fromEuler(k,t),mat4.fromQuat(p,k),mat4.multiply(e,e,p),mat4.scale(e,e,m);this.setOutputData(0,e)};z.registerNodeType("math3d/mat4",c);m.values="+ - * / % ^ max min".split(" ");m.title="Operation";m.desc="Easy math 3D operators";m["@OP"]={type:"enum",title:"operation",values:m.values};m.size=[100,60];m.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+ +"(A,B)":"A "+this.properties.OP+" B"};m.prototype.onExecute=function(){var c=this.getInputData(0),e=this.getInputData(1);if(null!=c&&null!=e){c.constructor===Number&&(c=[c,c,c]);e.constructor===Number&&(e=[e,e,e]);var k=this._result;switch(this.properties.OP){case "+":k=vec3.add(k,c,e);break;case "-":k=vec3.sub(k,c,e);break;case "x":case "X":case "*":k=vec3.mul(k,c,e);break;case "/":k=vec3.div(k,c,e);break;case "%":k[0]=c[0]%e[0];k[1]=c[1]%e[1];k[2]=c[2]%e[2];break;case "^":k[0]=Math.pow(c[0],e[0]); +k[1]=Math.pow(c[1],e[1]);k[2]=Math.pow(c[2],e[2]);break;case "max":k[0]=Math.max(c[0],e[0]);k[1]=Math.max(c[1],e[1]);k[2]=Math.max(c[2],e[2]);break;case "min":k[0]=Math.min(c[0],e[0]);k[1]=Math.min(c[1],e[1]);k[2]=Math.min(c[2],e[2]);break;default:console.warn("Unknown operation: "+this.properties.OP)}this.setOutputData(0,k)}};m.prototype.onDrawBackground=function(c){this.flags.collapsed||(c.font="40px Arial",c.fillStyle="#666",c.textAlign="center",c.fillText(this.properties.OP,0.5*this.size[0],0.5* +(this.size[1]+z.NODE_TITLE_HEIGHT)),c.textAlign="left")};z.registerNodeType("math3d/operation",m);p.title="vec3_scale";p.desc="scales the components of a vec3";p.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null==e&&(e=this.properties.f);var k=this._data;k[0]=c[0]*e;k[1]=c[1]*e;k[2]=c[2]*e;this.setOutputData(0,k)}};z.registerNodeType("math3d/vec3-scale",p);k.title="vec3_length";k.desc="returns the module of a vector";k.prototype.onExecute=function(){var c= +this.getInputData(0);null!=c&&(c=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),this.setOutputData(0,c))};z.registerNodeType("math3d/vec3-length",k);v.title="vec3_normalize";v.desc="returns the vector normalized";v.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),k=this._data;k[0]=c[0]/e;k[1]=c[1]/e;k[2]=c[2]/e;this.setOutputData(0,k)}};z.registerNodeType("math3d/vec3-normalize",v);e.title="vec3_lerp";e.desc="returns the interpolated vector"; +e.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);if(null!=e){var k=this.getInputOrProperty("f"),g=this._data;g[0]=c[0]*(1-k)+e[0]*k;g[1]=c[1]*(1-k)+e[1]*k;g[2]=c[2]*(1-k)+e[2]*k;this.setOutputData(0,g)}}};z.registerNodeType("math3d/vec3-lerp",e);E.title="vec3_dot";E.desc="returns the dot product";E.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null!=e&&this.setOutputData(0,c[0]*e[0]+c[1]*e[1]+c[2]* +e[2])}};z.registerNodeType("math3d/vec3-dot",E);C.glMatrix?(C=function(){this.addOutput("quat","quat");this.properties={x:0,y:0,z:0,w:1,normalize:!1};this._value=quat.create()},C.title="Quaternion",C.desc="quaternion",C.prototype.onExecute=function(){this._value[0]=this.getInputOrProperty("x");this._value[1]=this.getInputOrProperty("y");this._value[2]=this.getInputOrProperty("z");this._value[3]=this.getInputOrProperty("w");this.properties.normalize&&quat.normalize(this._value,this._value);this.setOutputData(0, +this._value)},C.prototype.onGetInputs=function(){return[["x","number"],["y","number"],["z","number"],["w","number"]]},z.registerNodeType("math3d/quaternion",C),C=function(){this.addInputs([["degrees","number"],["axis","vec3"]]);this.addOutput("quat","quat");this.properties={angle:90,axis:vec3.fromValues(0,1,0)};this._value=quat.create()},C.title="Rotation",C.desc="quaternion rotation",C.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.angle);var e=this.getInputData(1); +null==e&&(e=this.properties.axis);c=quat.setAxisAngle(this._value,e,0.0174532925*c);this.setOutputData(0,c)},z.registerNodeType("math3d/rotation",C),C=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},C.title="Rot. Vec3",C.desc="rotate a point",C.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.vec);var e=this.getInputData(1);null==e?this.setOutputData(c):this.setOutputData(0,vec3.transformQuat(vec3.create(), +c,e))},z.registerNodeType("math3d/rotate_vec3",C),C=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},C.title="Mult. Quat",C.desc="rotate quaternion",C.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null!=e&&(c=quat.multiply(this._value,c,e),this.setOutputData(0,c))}},z.registerNodeType("math3d/mult-quat",C),C=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp", +"quat");this.addProperty("factor",0.5);this._value=quat.create()},C.title="Quat Slerp",C.desc="quaternion spherical interpolation",C.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);if(null!=e){var k=this.properties.factor;null!=this.getInputData(2)&&(k=this.getInputData(2));c=quat.slerp(this._value,c,e,k);this.setOutputData(0,c)}}},z.registerNodeType("math3d/quat-slerp",C),C=function(){this.addInput("vec3","vec3");this.addOutput("remap","vec3");this.addOutput("clamped", +"vec3");this.properties={clamp:!0,range_min:[-1,-1,0],range_max:[1,1,0],target_min:[-1,-1,0],target_max:[1,1,0]};this._value=vec3.create();this._clamped=vec3.create()},C.title="Remap Range",C.desc="remap a 3D range",C.prototype.onExecute=function(){var c=this.getInputData(0);c&&this._value.set(c);for(var c=this.properties.range_min,e=this.properties.range_max,k=this.properties.target_min,g=this.properties.target_max,n=0;3>n;++n){var p=e[n]-c[n];this._clamped[n]=Math.clamp(this._value[n],c[n],e[n]); +0==p?this._value[n]=0.5*(k[n]+g[n]):(p=(this._value[n]-c[n])/p,this.properties.clamp&&(p=Math.clamp(p,0,1)),this._value[n]=k[n]+p*(g[n]-k[n]))}this.setOutputData(0,this._value);this.setOutputData(1,this._clamped)},z.registerNodeType("math3d/remap_range",C)):console.warn("No glmatrix found, some Math3D nodes may not work")})(this); +(function(C){function c(c,k){return c==k}function m(){this.addInput("","string");this.addOutput("table","table");this.addOutput("rows","number");this.addProperty("value","");this.addProperty("separator",",");this._table=null}C=C.LiteGraph;C.wrapFunctionAsNode("string/toString",c,["*"],"String");C.wrapFunctionAsNode("string/compare",c,["string","string"],"boolean");C.wrapFunctionAsNode("string/concatenate",function(c,k){return void 0===c?k:void 0===k?c:c+k},["string","string"],"string");C.wrapFunctionAsNode("string/contains", +function(c,k){return void 0===c||void 0===k?!1:-1!=c.indexOf(k)},["string","string"],"boolean");C.wrapFunctionAsNode("string/toUpperCase",function(c){return null!=c&&c.constructor===String?c.toUpperCase():c},["string"],"string");C.wrapFunctionAsNode("string/split",function(c,k){null==k&&(k=this.properties.separator);if(null==c)return[];if(c.constructor===String)return c.split(k||" ");if(c.constructor===Array){for(var m=[],e=0;ee;++e){var g=this.getInputData(e);if(null!=g){var l=this.values[e];l.push(g);l.length>c[0]&&l.shift()}}}};c.prototype.onDrawBackground=function(e){if(!this.flags.collapsed){var g=this.size,k=0.5*g[1]/ +this.properties.scale,l=c.colors,q=0.5*g[1];e.fillStyle="#000";e.fillRect(0,0,g[0],g[1]);e.strokeStyle="#555";e.beginPath();e.moveTo(0,q);e.lineTo(g[0],q);e.stroke();if(this.inputs)for(var m=0;4>m;++m){var x=this.values[m];if(this.inputs[m]&&this.inputs[m].link){e.strokeStyle=l[m];e.beginPath();var a=x[0]*k*-1+q;e.moveTo(0,Math.clamp(a,0,g[1]));for(var b=1;be&&(e=0);if(0!=c.length){var g=[0,0,0];if(0==e)g=c[0];else if(1==e)g=c[c.length-1];else{var l= +(c.length-1)*e,e=c[Math.floor(l)],c=c[Math.floor(l)+1],l=l-Math.floor(l);g[0]=e[0]*(1-l)+c[0]*l;g[1]=e[1]*(1-l)+c[1]*l;g[2]=e[2]*(1-l)+c[2]*l}for(var k in g)g[k]/=255;this.boxcolor=colorToString(g);this.setOutputData(0,g)}};g.registerNodeType("color/palette",p);k.title="Frame";k.desc="Frame viewerew";k.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}];k.prototype.onDrawBackground=function(c){this.frame&&!this.flags.collapsed&&c.drawImage(this.frame, +0,0,this.size[0],this.size[1])};k.prototype.onExecute=function(){this.frame=this.getInputData(0);this.setDirtyCanvas(!0)};k.prototype.onWidget=function(c,e){if("resize"==e.name&&this.frame){var g=this.frame.width,l=this.frame.height;g||null==this.frame.videoWidth||(g=this.frame.videoWidth,l=this.frame.videoHeight);g&&l&&(this.size=[g,l]);this.setDirtyCanvas(!0,!0)}else"view"==e.name&&this.show()};k.prototype.show=function(){showElement&&this.frame&&showElement(this.frame)};g.registerNodeType("graphics/frame", +k);v.title="Image fade";v.desc="Fades between images";v.widgets=[{name:"resizeA",text:"Resize to A",type:"button"},{name:"resizeB",text:"Resize to B",type:"button"}];v.prototype.onAdded=function(){this.createCanvas();var c=this.canvas.getContext("2d");c.fillStyle="#000";c.fillRect(0,0,this.properties.width,this.properties.height)};v.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};v.prototype.onExecute= +function(){var c=this.canvas.getContext("2d");this.canvas.width=this.canvas.width;var e=this.getInputData(0);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);e=this.getInputData(2);null==e?e=this.properties.fade:this.properties.fade=e;c.globalAlpha=e;e=this.getInputData(1);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);c.globalAlpha=1;this.setOutputData(0,this.canvas);this.setDirtyCanvas(!0)};g.registerNodeType("graphics/imagefade",v);e.title="Crop";e.desc="Crop Image"; e.prototype.onAdded=function(){this.createCanvas()};e.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};e.prototype.onExecute=function(){var c=this.getInputData(0);c&&(c.width?(this.canvas.getContext("2d").drawImage(c,-this.properties.x,-this.properties.y,c.width*this.properties.scale,c.height*this.properties.scale),this.setOutputData(0,this.canvas)):this.setOutputData(0,null))};e.prototype.onDrawBackground= -function(c){this.flags.collapsed||this.canvas&&c.drawImage(this.canvas,0,0,this.canvas.width,this.canvas.height,0,0,this.size[0],this.size[1])};e.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"scale"==c?(this.properties[c]=parseFloat(e),0==this.properties[c]&&(console.error("Error in scale"),this.properties[c]=1)):this.properties[c]=parseInt(e);this.createCanvas();return!0};g.registerNodeType("graphics/cropImage",e);C.title="Canvas";C.desc="Canvas to render stuff";C.prototype.onExecute= -function(){var c=this.canvas,e=this.properties.width|0,g=this.properties.height|0;c.width!=e&&(c.width=e);c.height!=g&&(c.height=g);this.properties.autoclear&&this.ctx.clearRect(0,0,c.width,c.height);this.setOutputData(0,c)};C.prototype.onAction=function(c,e){"clear"==c&&this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)};g.registerNodeType("graphics/canvas",C);z.title="DrawImage";z.desc="Draws image into a canvas";z.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e= -this.getInputOrProperty("img");if(e){var g=this.getInputOrProperty("x"),r=this.getInputOrProperty("y");c.getContext("2d").drawImage(e,g,r)}}};g.registerNodeType("graphics/drawImage",z);u.title="DrawRectangle";u.desc="Draws rectangle in canvas";u.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e=this.getInputOrProperty("x"),g=this.getInputOrProperty("y"),r=this.getInputOrProperty("w"),n=this.getInputOrProperty("h");c.getContext("2d").fillRect(e,g,r,n)}};g.registerNodeType("graphics/drawRectangle", -u);D.title="Video";D.desc="Video playback";D.widgets=[{name:"play",text:"PLAY",type:"minibutton"},{name:"stop",text:"STOP",type:"minibutton"},{name:"demo",text:"Demo video",type:"button"},{name:"mute",text:"Mute video",type:"button"}];D.prototype.onExecute=function(){if(this.properties.url&&(this.properties.url!=this._video_url&&this.loadVideo(this.properties.url),this._video&&0!=this._video.width)){var c=this.getInputData(0);c&&0<=c&&1>=c&&(this._video.currentTime=c*this._video.duration,this._video.pause()); +function(c){this.flags.collapsed||this.canvas&&c.drawImage(this.canvas,0,0,this.canvas.width,this.canvas.height,0,0,this.size[0],this.size[1])};e.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"scale"==c?(this.properties[c]=parseFloat(e),0==this.properties[c]&&(console.error("Error in scale"),this.properties[c]=1)):this.properties[c]=parseInt(e);this.createCanvas();return!0};g.registerNodeType("graphics/cropImage",e);E.title="Canvas";E.desc="Canvas to render stuff";E.prototype.onExecute= +function(){var c=this.canvas,e=this.properties.width|0,g=this.properties.height|0;c.width!=e&&(c.width=e);c.height!=g&&(c.height=g);this.properties.autoclear&&this.ctx.clearRect(0,0,c.width,c.height);this.setOutputData(0,c)};E.prototype.onAction=function(c,e){"clear"==c&&this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)};g.registerNodeType("graphics/canvas",E);z.title="DrawImage";z.desc="Draws image into a canvas";z.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e= +this.getInputOrProperty("img");if(e){var g=this.getInputOrProperty("x"),l=this.getInputOrProperty("y");c.getContext("2d").drawImage(e,g,l)}}};g.registerNodeType("graphics/drawImage",z);y.title="DrawRectangle";y.desc="Draws rectangle in canvas";y.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e=this.getInputOrProperty("x"),g=this.getInputOrProperty("y"),l=this.getInputOrProperty("w"),k=this.getInputOrProperty("h");c.getContext("2d").fillRect(e,g,l,k)}};g.registerNodeType("graphics/drawRectangle", +y);D.title="Video";D.desc="Video playback";D.widgets=[{name:"play",text:"PLAY",type:"minibutton"},{name:"stop",text:"STOP",type:"minibutton"},{name:"demo",text:"Demo video",type:"button"},{name:"mute",text:"Mute video",type:"button"}];D.prototype.onExecute=function(){if(this.properties.url&&(this.properties.url!=this._video_url&&this.loadVideo(this.properties.url),this._video&&0!=this._video.width)){var c=this.getInputData(0);c&&0<=c&&1>=c&&(this._video.currentTime=c*this._video.duration,this._video.pause()); this._video.dirty=!0;this.setOutputData(0,this._video);this.setOutputData(1,this._video.currentTime);this.setOutputData(2,this._video.duration);this.setDirtyCanvas(!0)}};D.prototype.onStart=function(){this.play()};D.prototype.onStop=function(){this.stop()};D.prototype.loadVideo=function(c){this._video_url=c;var e=c.substr(0,10).indexOf(":"),k="";-1!=e&&(k=c.substr(0,e));e="";k&&(e=c.substr(0,c.indexOf("/",k.length+3)),e=e.substr(k.length+3));this.properties.use_proxy&&k&&g.proxy&&e!=location.host&& -(c=g.proxy+c.substr(c.indexOf(":")+3));this._video=document.createElement("video");this._video.src=c;this._video.type="type=video/mp4";this._video.muted=!0;this._video.autoplay=!0;var r=this;this._video.addEventListener("loadedmetadata",function(c){console.log("Duration: "+this.duration+" seconds");console.log("Size: "+this.videoWidth+","+this.videoHeight);r.setDirtyCanvas(!0);this.width=this.videoWidth;this.height=this.videoHeight});this._video.addEventListener("progress",function(c){console.log("video loading...")}); +(c=g.proxy+c.substr(c.indexOf(":")+3));this._video=document.createElement("video");this._video.src=c;this._video.type="type=video/mp4";this._video.muted=!0;this._video.autoplay=!0;var l=this;this._video.addEventListener("loadedmetadata",function(c){console.log("Duration: "+this.duration+" seconds");console.log("Size: "+this.videoWidth+","+this.videoHeight);l.setDirtyCanvas(!0);this.width=this.videoWidth;this.height=this.videoHeight});this._video.addEventListener("progress",function(c){console.log("video loading...")}); this._video.addEventListener("error",function(c){console.error("Error loading video: "+this.src);if(this.error)switch(this.error.code){case this.error.MEDIA_ERR_ABORTED:console.error("You stopped the video.");break;case this.error.MEDIA_ERR_NETWORK:console.error("Network error - please try again later.");break;case this.error.MEDIA_ERR_DECODE:console.error("Video is broken..");break;case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED:console.error("Sorry, your browser can't play this video.")}});this._video.addEventListener("ended", function(c){console.log("Video Ended.");this.play()})};D.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"url"==c&&""!=e&&this.loadVideo(e);return!0};D.prototype.play=function(){this._video&&this._video.videoWidth&&this._video.play()};D.prototype.playPause=function(){this._video&&(this._video.paused?this.play():this.pause())};D.prototype.stop=function(){this._video&&(this._video.pause(),this._video.currentTime=0)};D.prototype.pause=function(){this._video&&(console.log("Video paused"), -this._video.pause())};D.prototype.onWidget=function(c,e){};g.registerNodeType("graphics/video",D);B.title="Webcam";B.desc="Webcam image";B.is_webcam_open=!1;B.prototype.openStream=function(){function c(g){console.log("Webcam rejected",g);e._webcam_stream=!1;B.is_webcam_open=!1;e.boxcolor="red";e.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](c); -var e=this}};B.prototype.closeStream=function(){if(this._webcam_stream){var c=this._webcam_stream.getTracks();if(c.length)for(var e=0;e=this.size[1]||!this.properties.show|| -!this._video||(c.save(),c.drawImage(this._video,0,0,this.size[0],this.size[1]),c.restore())};B.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["stream_ready",g.EVENT],["stream_closed",g.EVENT],["stream_error",g.EVENT]]};g.registerNodeType("graphics/webcam",B)})(this); -(function(y){function c(){this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",filter:!0};this.size=[c.image_preview_size,c.image_preview_size]}function l(){this.addInput("Texture","Texture");this.properties={flipY:!1};this.size=[c.image_preview_size,c.image_preview_size]}function p(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",generate_mipmaps:!1}}function k(){this.addInput("Texture", +this._video.pause())};D.prototype.onWidget=function(c,e){};g.registerNodeType("graphics/video",D);A.title="Webcam";A.desc="Webcam image";A.is_webcam_open=!1;A.prototype.openStream=function(){function c(g){console.log("Webcam rejected",g);e._webcam_stream=!1;A.is_webcam_open=!1;e.boxcolor="red";e.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](c); +var e=this}};A.prototype.closeStream=function(){if(this._webcam_stream){var c=this._webcam_stream.getTracks();if(c.length)for(var e=0;e=this.size[1]||!this.properties.show|| +!this._video||(c.save(),c.drawImage(this._video,0,0,this.size[0],this.size[1]),c.restore())};A.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["stream_ready",g.EVENT],["stream_closed",g.EVENT],["stream_error",g.EVENT]]};g.registerNodeType("graphics/webcam",A)})(this); +(function(C){function c(){this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",filter:!0};this.size=[c.image_preview_size,c.image_preview_size]}function m(){this.addInput("Texture","Texture");this.properties={flipY:!1};this.size=[c.image_preview_size,c.image_preview_size]}function p(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",generate_mipmaps:!1}}function k(){this.addInput("Texture", "Texture");this.addInput("TextureB","Texture");this.addInput("value","number");this.addOutput("Texture","Texture");this.help="

pixelcode must be vec3, uvcode must be vec2, is optional

\t\t

uv: tex. coords

color: texture colorB: textureB

time: scene time value: input value

For multiline you must type: result = ...

";this.properties={value:1,pixelcode:"color + colorB * value",uvcode:"",precision:c.DEFAULT}; -this.has_error=!1}function w(){this.addOutput("out","Texture");this.properties={code:"",u_value:1,u_color:[1,1,1,1],width:512,height:512,precision:c.DEFAULT};this.properties.code=w.pixel_shader;this._uniforms={u_value:1,u_color:vec4.create(),in_texture:0,texSize:vec2.create(),time:0}}function e(){this.addInput("in","Texture");this.addInput("scale","vec2");this.addInput("offset","vec2");this.addOutput("out","Texture");this.properties={offset:vec2.fromValues(0,0),scale:vec2.fromValues(1,1),precision:c.DEFAULT}} -function C(){this.addInput("in","Texture");this.addInput("warp","Texture");this.addInput("factor","number");this.addOutput("out","Texture");this.properties={factor:0.01,scale:[1,1],offset:[0,0],precision:c.DEFAULT};this._uniforms={u_texture:0,u_textureB:1,u_factor:1,u_scale:vec2.create(),u_offset:vec2.create()}}function z(){this.addInput("Texture","Texture");this.properties={additive:!1,antialiasing:!1,filter:!0,disable_alpha:!1,gamma:1,viewport:[0,0,1,1]};this.size[0]=130}function u(){this.addInput("Texture", -"Texture");this.addOutput("","Texture");this.properties={size:0,generate_mipmaps:!1,precision:c.DEFAULT}}function D(){this.addInput("Texture","Texture");this.addOutput("","Texture");this.properties={iterations:1,generate_mipmaps:!1,precision:c.DEFAULT}}function B(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("avg","vec4");this.addOutput("lum","number");this.properties={use_previous_frame:!0,high_quality:!1};this._uniforms={u_texture:0,u_mipmap_offset:0};this._luminance= -new Float32Array(4)}function g(){this.addInput("Texture","Texture");this.addOutput("min_t","Texture");this.addOutput("max_t","Texture");this.addOutput("min","vec4");this.addOutput("max","vec4");this.properties={mode:"max",use_previous_frame:!0};this._uniforms={u_texture:0};this._max=new Float32Array(4);this._min=new Float32Array(4);this._textures_chain=[]}function m(){this.addInput("in","Texture");this.addInput("factor","Number");this.addOutput("out","Texture");this.properties={factor:0.5};this._uniforms= -{u_texture:0,u_textureB:1,u_factor:this.properties.factor}}function s(){this.addInput("in","Texture");this.addOutput("avg","Texture");this.addOutput("array","Texture");this.properties={samples:64,frames_interval:1};this._uniforms={u_texture:0,u_textureB:1,u_samples:this.properties.samples,u_isamples:1/this.properties.samples};this.frame=0}function A(){this.addInput("Image","image");this.addOutput("","Texture");this.properties={}}function r(){this.addInput("Texture","Texture");this.addInput("LUT", -"Texture");this.addInput("Intensity","number");this.addOutput("","Texture");this.properties={enabled:!0,intensity:1,precision:c.DEFAULT,texture:null};r._shader||(r._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,r.pixel_shader))}function n(){this.addInput("Texture","Texture");this.addOutput("R","Texture");this.addOutput("G","Texture");this.addOutput("B","Texture");this.addOutput("A","Texture");n._shader||(n._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,n.pixel_shader))}function x(){this.addInput("R", -"Texture");this.addInput("G","Texture");this.addInput("B","Texture");this.addInput("A","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,R:1,G:1,B:1,A:1};this._color=vec4.create();this._uniforms={u_textureR:0,u_textureG:1,u_textureB:2,u_textureA:3,u_color:this._color}}function v(){this.addOutput("Texture","Texture");this._tex_color=vec4.create();this.properties={color:vec4.create(),precision:c.DEFAULT}}function a(){this.addInput("A","color");this.addInput("B","color"); +this.has_error=!1}function v(){this.addOutput("out","Texture");this.properties={code:"",u_value:1,u_color:[1,1,1,1],width:512,height:512,precision:c.DEFAULT};this.properties.code=v.pixel_shader;this._uniforms={u_value:1,u_color:vec4.create(),in_texture:0,texSize:vec2.create(),time:0}}function e(){this.addInput("in","Texture");this.addInput("scale","vec2");this.addInput("offset","vec2");this.addOutput("out","Texture");this.properties={offset:vec2.fromValues(0,0),scale:vec2.fromValues(1,1),precision:c.DEFAULT}} +function E(){this.addInput("in","Texture");this.addInput("warp","Texture");this.addInput("factor","number");this.addOutput("out","Texture");this.properties={factor:0.01,scale:[1,1],offset:[0,0],precision:c.DEFAULT};this._uniforms={u_texture:0,u_textureB:1,u_factor:1,u_scale:vec2.create(),u_offset:vec2.create()}}function z(){this.addInput("Texture","Texture");this.properties={additive:!1,antialiasing:!1,filter:!0,disable_alpha:!1,gamma:1,viewport:[0,0,1,1]};this.size[0]=130}function y(){this.addInput("Texture", +"Texture");this.addOutput("","Texture");this.properties={size:0,generate_mipmaps:!1,precision:c.DEFAULT}}function D(){this.addInput("Texture","Texture");this.addOutput("","Texture");this.properties={iterations:1,generate_mipmaps:!1,precision:c.DEFAULT}}function A(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("avg","vec4");this.addOutput("lum","number");this.properties={use_previous_frame:!0,high_quality:!1};this._uniforms={u_texture:0,u_mipmap_offset:0};this._luminance= +new Float32Array(4)}function g(){this.addInput("Texture","Texture");this.addOutput("min_t","Texture");this.addOutput("max_t","Texture");this.addOutput("min","vec4");this.addOutput("max","vec4");this.properties={mode:"max",use_previous_frame:!0};this._uniforms={u_texture:0};this._max=new Float32Array(4);this._min=new Float32Array(4);this._textures_chain=[]}function n(){this.addInput("in","Texture");this.addInput("factor","Number");this.addOutput("out","Texture");this.properties={factor:0.5};this._uniforms= +{u_texture:0,u_textureB:1,u_factor:this.properties.factor}}function t(){this.addInput("in","Texture");this.addOutput("avg","Texture");this.addOutput("array","Texture");this.properties={samples:64,frames_interval:1};this._uniforms={u_texture:0,u_textureB:1,u_samples:this.properties.samples,u_isamples:1/this.properties.samples};this.frame=0}function B(){this.addInput("Image","image");this.addOutput("","Texture");this.properties={}}function l(){this.addInput("Texture","Texture");this.addInput("LUT", +"Texture");this.addInput("Intensity","number");this.addOutput("","Texture");this.properties={enabled:!0,intensity:1,precision:c.DEFAULT,texture:null};l._shader||(l._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,l.pixel_shader))}function q(){this.addInput("Texture","Texture");this.addOutput("R","Texture");this.addOutput("G","Texture");this.addOutput("B","Texture");this.addOutput("A","Texture");q._shader||(q._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,q.pixel_shader))}function w(){this.addInput("R", +"Texture");this.addInput("G","Texture");this.addInput("B","Texture");this.addInput("A","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,R:1,G:1,B:1,A:1};this._color=vec4.create();this._uniforms={u_textureR:0,u_textureG:1,u_textureB:2,u_textureA:3,u_color:this._color}}function x(){this.addOutput("Texture","Texture");this._tex_color=vec4.create();this.properties={color:vec4.create(),precision:c.DEFAULT}}function a(){this.addInput("A","color");this.addInput("B","color"); this.addOutput("Texture","Texture");this.properties={angle:0,scale:1,A:[0,0,0],B:[1,1,1],texture_size:32};a._shader||(a._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,a.pixel_shader));this._uniforms={u_angle:0,u_colorA:vec3.create(),u_colorB:vec3.create()}}function b(){this.addInput("A","Texture");this.addInput("B","Texture");this.addInput("Mixer","Texture");this.addOutput("Texture","Texture");this.properties={factor:0.5,size_from_biggest:!0,invert:!1,precision:c.DEFAULT};this._uniforms={u_textureA:0, u_textureB:1,u_textureMix:2,u_mix:vec4.create()}}function d(){this.addInput("Tex.","Texture");this.addOutput("Edges","Texture");this.properties={invert:!0,threshold:!1,factor:1,precision:c.DEFAULT};d._shader||(d._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,d.pixel_shader))}function h(){this.addInput("Texture","Texture");this.addInput("Distance","number");this.addInput("Range","number");this.addOutput("Texture","Texture");this.properties={distance:100,range:50,only_depth:!1,high_precision:!1}; -this._uniforms={u_texture:0,u_distance:100,u_range:50,u_camera_planes:null}}function f(){this.addInput("Texture","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,invert:!1};this._uniforms={u_texture:0,u_near:0.1,u_far:1E4}}function E(){this.addInput("Texture","Texture");this.addInput("Iterations","number");this.addInput("Intensity","number");this.addOutput("Blurred","Texture");this.properties={intensity:1,iterations:1,preserve_aspect:!1,scale:[1,1],precision:c.DEFAULT}} -function q(){this.addInput("in","Texture");this.addInput("dirt","Texture");this.addOutput("out","Texture");this.addOutput("glow","Texture");this.properties={enabled:!0,intensity:1,persistence:0.99,iterations:16,threshold:0,scale:1,dirt_factor:0.5,precision:c.DEFAULT};this._textures=[];this._uniforms={u_intensity:1,u_texture:0,u_glow_texture:1,u_threshold:0,u_texel_size:vec2.create()}}function I(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={intensity:1, -radius:5}}function J(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={sigma:1.4,k:1.6,p:21.7,epsilon:79,phi:0.017}}function H(){this.addOutput("Webcam","Texture");this.properties={texture_name:"",facingMode:"user"};this.boxcolor="black";this.version=0}function F(){this.addInput("in","Texture");this.addInput("f","number");this.addOutput("out","Texture");this.properties={enabled:!0,factor:1,precision:c.LOW};this._uniforms={u_texture:0,u_factor:1}}function t(){this.addInput("in", +this._uniforms={u_texture:0,u_distance:100,u_range:50,u_camera_planes:null}}function f(){this.addInput("Texture","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,invert:!1};this._uniforms={u_texture:0,u_near:0.1,u_far:1E4}}function s(){this.addInput("Texture","Texture");this.addInput("Iterations","number");this.addInput("Intensity","number");this.addOutput("Blurred","Texture");this.properties={intensity:1,iterations:1,preserve_aspect:!1,scale:[1,1],precision:c.DEFAULT}} +function r(){this.addInput("in","Texture");this.addInput("dirt","Texture");this.addOutput("out","Texture");this.addOutput("glow","Texture");this.properties={enabled:!0,intensity:1,persistence:0.99,iterations:16,threshold:0,scale:1,dirt_factor:0.5,precision:c.DEFAULT};this._textures=[];this._uniforms={u_intensity:1,u_texture:0,u_glow_texture:1,u_threshold:0,u_texel_size:vec2.create()}}function I(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={intensity:1, +radius:5}}function J(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={sigma:1.4,k:1.6,p:21.7,epsilon:79,phi:0.017}}function H(){this.addOutput("Webcam","Texture");this.properties={texture_name:"",facingMode:"user"};this.boxcolor="black";this.version=0}function F(){this.addInput("in","Texture");this.addInput("f","number");this.addOutput("out","Texture");this.properties={enabled:!0,factor:1,precision:c.LOW};this._uniforms={u_texture:0,u_factor:1}}function u(){this.addInput("in", "Texture");this.addOutput("out","Texture");this.properties={precision:c.LOW,split_channels:!1};this._values=new Uint8Array(1024);this._values.fill(255);this._curve_texture=null;this._uniforms={u_texture:0,u_curve:1,u_range:1};this._must_update=!0;this._points={RGB:[[0,0],[1,1]],R:[[0,0],[1,1]],G:[[0,0],[1,1]],B:[[0,0],[1,1]]};this.curve_editor=null;this.addWidget("toggle","Split Channels",!1,"split_channels");this.addWidget("combo","Channel","RGB",{values:["RGB","R","G","B"]});this.curve_offset=68; this.size=[240,160]}function O(){this.addInput("in","Texture");this.addInput("exp","number");this.addOutput("out","Texture");this.properties={exposition:1,precision:c.LOW};this._uniforms={u_texture:0,u_exposition:1}}function K(){this.addInput("in","Texture");this.addInput("avg","number,Texture");this.addOutput("out","Texture");this.properties={enabled:!0,scale:1,gamma:1,average_lum:1,lum_white:1,precision:c.LOW};this._uniforms={u_texture:0,u_lumwhite2:1,u_igamma:1,u_scale:1,u_average_lum:1}}function N(){this.addOutput("out", "Texture");this.properties={width:512,height:512,seed:0,persistence:0.1,octaves:8,scale:1,offset:[0,0],amplitude:1,precision:c.DEFAULT};this._key=0;this._texture=null;this._uniforms={u_persistence:0.1,u_seed:0,u_offset:vec2.create(),u_scale:1,u_viewport:vec2.create()}}function M(){this.addInput("v");this.addOutput("out","Texture");this.properties={code:M.default_code,width:512,height:512,clear:!0,precision:c.DEFAULT,use_html_canvas:!1};this._temp_texture=this._func=null;this.compileCode()}function P(){this.addInput("in", -"Texture");this.addOutput("out","Texture");this.properties={key_color:vec3.fromValues(0,1,0),threshold:0.8,slope:0.2,precision:c.DEFAULT}}function Q(){this.addInput("in","texture");this.addInput("yaw","number");this.addOutput("out","texture");this.properties={yaw:0}}var G=y.LiteGraph;y.LGraphTexture=null;"undefined"!=typeof GL&&(LGraphCanvas.link_type_colors.Texture="#987",y.LGraphTexture=c,c.title="Texture",c.desc="Texture",c.widgets_info={name:{widget:"texture"},filter:{widget:"checkbox"}},c.loadTextureCallback= +"Texture");this.addOutput("out","Texture");this.properties={key_color:vec3.fromValues(0,1,0),threshold:0.8,slope:0.2,precision:c.DEFAULT}}function Q(){this.addInput("in","texture");this.addInput("yaw","number");this.addOutput("out","texture");this.properties={yaw:0}}var G=C.LiteGraph;C.LGraphTexture=null;"undefined"!=typeof GL&&(LGraphCanvas.link_type_colors.Texture="#987",C.LGraphTexture=c,c.title="Texture",c.desc="Texture",c.widgets_info={name:{widget:"texture"},filter:{widget:"checkbox"}},c.loadTextureCallback= null,c.image_preview_size=256,c.PASS_THROUGH=1,c.COPY=2,c.LOW=3,c.HIGH=4,c.REUSE=5,c.DEFAULT=2,c.MODE_VALUES={"pass through":c.PASS_THROUGH,copy:c.COPY,low:c.LOW,high:c.HIGH,reuse:c.REUSE,"default":c.DEFAULT},c.getTexturesContainer=function(){return gl.textures},c.loadTexture=function(a,b){b=b||{};var d=a;"http://"==d.substr(0,7)&&G.proxy&&(d=G.proxy+d.substr(7));return c.getTexturesContainer()[a]=GL.Texture.fromURL(d,b)},c.getTexture=function(a){var b=this.getTexturesContainer();if(!b)throw"Cannot load texture, container of textures not found"; -b=b[a];return!b&&a&&":"!=a[0]?this.loadTexture(a):b},c.getTargetTexture=function(a,b,d){if(!a)throw"LGraphTexture.getTargetTexture expects a reference texture";var f=null;switch(d){case c.LOW:f=gl.UNSIGNED_BYTE;break;case c.HIGH:f=gl.HIGH_PRECISION_FORMAT;break;case c.REUSE:return a;default:f=a?a.type:gl.UNSIGNED_BYTE}b&&b.width==a.width&&b.height==a.height&&b.type==f||(b=new GL.Texture(a.width,a.height,{type:f,format:gl.RGBA,filter:gl.LINEAR}));return b},c.getTextureType=function(a,b){var d=b?b.type: +b=b[a];return!b&&a&&":"!=a[0]?this.loadTexture(a):b},c.getTargetTexture=function(a,b,d){if(!a)throw"LGraphTexture.getTargetTexture expects a reference texture";var h=null;switch(d){case c.LOW:h=gl.UNSIGNED_BYTE;break;case c.HIGH:h=gl.HIGH_PRECISION_FORMAT;break;case c.REUSE:return a;default:h=a?a.type:gl.UNSIGNED_BYTE}b&&b.width==a.width&&b.height==a.height&&b.type==h||(b=new GL.Texture(a.width,a.height,{type:h,format:gl.RGBA,filter:gl.LINEAR}));return b},c.getTextureType=function(a,b){var d=b?b.type: gl.UNSIGNED_BYTE;switch(a){case c.HIGH:d=gl.HIGH_PRECISION_FORMAT;break;case c.LOW:d=gl.UNSIGNED_BYTE}return d},c.getWhiteTexture=function(){return this._white_texture?this._white_texture:this._white_texture=GL.Texture.fromMemory(1,1,[255,255,255,255],{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.getNoiseTexture=function(){if(this._noise_texture)return this._noise_texture;for(var a=new Uint8Array(1048576),b=0;1048576>b;++b)a[b]=255*Math.random();return this._noise_texture=a=GL.Texture.fromMemory(512, 512,a,{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.prototype.onDropFile=function(a,b,d){if(a){var c=null;"string"==typeof a?c=GL.Texture.fromURL(a):-1!=b.toLowerCase().indexOf(".dds")?c=GL.Texture.fromDDSInMemory(a):(a=new Blob([d]),a=URL.createObjectURL(a),c=GL.Texture.fromURL(a));this._drop_texture=c;this.properties.name=b}else this._drop_texture=null,this.properties.name=""},c.prototype.getExtraMenuOptions=function(a){var b=this;if(this._drop_texture)return[{content:"Clear",callback:function(){b._drop_texture= null;b.properties.name=""}}]},c.prototype.onExecute=function(){var a=null;this.isOutputConnected(1)&&(a=this.getInputData(0));!a&&this._drop_texture&&(a=this._drop_texture);!a&&this.properties.name&&(a=c.getTexture(this.properties.name));if(a){this._last_tex=a;!1===this.properties.filter?a.setParameter(gl.TEXTURE_MAG_FILTER,gl.NEAREST):a.setParameter(gl.TEXTURE_MAG_FILTER,gl.LINEAR);this.setOutputData(0,a);this.setOutputData(1,a.fullpath||a.filename);for(var b=2;b=this.size[1]))if(this._drop_texture&&a.webgl)a.drawImage(this._drop_texture,0,0,this.size[0],this.size[1]);else{if(this._last_preview_tex!=this._last_tex)if(a.webgl)this._canvas= +if(d){var h=null;"width"==d.name?h=a.width:"height"==d.name?h=a.height:"aspect"==d.name&&(h=a.width/a.height);this.setOutputData(b,h)}}}else this.setOutputData(0,null),this.setOutputData(1,"")},c.prototype.onResourceRenamed=function(a,b){this.properties.name==a&&(this.properties.name=b)},c.prototype.onDrawBackground=function(a){if(!(this.flags.collapsed||20>=this.size[1]))if(this._drop_texture&&a.webgl)a.drawImage(this._drop_texture,0,0,this.size[0],this.size[1]);else{if(this._last_preview_tex!=this._last_tex)if(a.webgl)this._canvas= this._last_tex;else{var b=c.generateLowResTexturePreview(this._last_tex);if(!b)return;this._last_preview_tex=this._last_tex;this._canvas=cloneCanvas(b)}this._canvas&&(a.save(),a.webgl||(a.translate(0,this.size[1]),a.scale(1,-1)),a.drawImage(this._canvas,0,0,this.size[0],this.size[1]),a.restore())}},c.generateLowResTexturePreview=function(a){if(!a)return null;var b=c.image_preview_size,d=a;if(a.format==gl.DEPTH_COMPONENT)return null;if(a.width>b||a.height>b)d=this._preview_temp_tex,this._preview_temp_tex|| (this._preview_temp_tex=d=new GL.Texture(b,b,{minFilter:gl.NEAREST})),a.copyTo(d);a=this._preview_canvas;a||(this._preview_canvas=a=createCanvas(b,b));d&&d.toCanvas(a);return a},c.prototype.getResources=function(a){this.properties.name&&(a[this.properties.name]=GL.Texture);return a},c.prototype.onGetInputs=function(){return[["in","Texture"]]},c.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["aspect","number"]]},c.replaceCode=function(a,b){return a.replace(/\{\{[a-zA-Z0-9_]*\}\}/g, -function(a){a=a.replace(/[\{\}]/g,"");return b[a]||""})},G.registerNodeType("texture/texture",c),l.title="Preview",l.desc="Show a texture in the graph canvas",l.allow_preview=!1,l.prototype.onDrawBackground=function(a){if(!this.flags.collapsed&&(a.webgl||l.allow_preview)){var b=this.getInputData(0);if(b){var d=null,d=!b.handle&&a.webgl?b:c.generateLowResTexturePreview(b);a.save();this.properties.flipY&&(a.translate(0,this.size[1]),a.scale(1,-1));a.drawImage(d,0,0,this.size[0],this.size[1]);a.restore()}}}, -G.registerNodeType("texture/preview",l),p.title="Save",p.desc="Save a texture in the repository",p.prototype.getPreviewTexture=function(){return this._texture},p.prototype.onExecute=function(){var a=this.getInputData(0);a&&(this.properties.generate_mipmaps&&(a.bind(0),a.setParameter(gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_LINEAR),gl.generateMipmap(a.texture_type),a.unbind(0)),this.properties.name&&(c.storeTexture?c.storeTexture(this.properties.name,a):c.getTexturesContainer()[this.properties.name]= +function(a){a=a.replace(/[\{\}]/g,"");return b[a]||""})},G.registerNodeType("texture/texture",c),m.title="Preview",m.desc="Show a texture in the graph canvas",m.allow_preview=!1,m.prototype.onDrawBackground=function(a){if(!this.flags.collapsed&&(a.webgl||m.allow_preview)){var b=this.getInputData(0);if(b){var d=null,d=!b.handle&&a.webgl?b:c.generateLowResTexturePreview(b);a.save();this.properties.flipY&&(a.translate(0,this.size[1]),a.scale(1,-1));a.drawImage(d,0,0,this.size[0],this.size[1]);a.restore()}}}, +G.registerNodeType("texture/preview",m),p.title="Save",p.desc="Save a texture in the repository",p.prototype.getPreviewTexture=function(){return this._texture},p.prototype.onExecute=function(){var a=this.getInputData(0);a&&(this.properties.generate_mipmaps&&(a.bind(0),a.setParameter(gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_LINEAR),gl.generateMipmap(a.texture_type),a.unbind(0)),this.properties.name&&(c.storeTexture?c.storeTexture(this.properties.name,a):c.getTexturesContainer()[this.properties.name]= a),this._texture=a,this.setOutputData(0,a),this.setOutputData(1,this.properties.name))},G.registerNodeType("texture/save",p),k.widgets_info={uvcode:{widget:"code"},pixelcode:{widget:"code"},precision:{widget:"combo",values:c.MODE_VALUES}},k.title="Operation",k.desc="Texture shader operation",k.presets={},k.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:b.properties.show?"Hide Texture":"Show Texture",callback:function(){b.properties.show=!b.properties.show}}]},k.prototype.onPropertyChanged= -function(){this.has_error=!1},k.prototype.onDrawBackground=function(a){this.flags.collapsed||20>=this.size[1]||!this.properties.show||!this._tex||this._tex.gl!=a||(a.save(),a.drawImage(this._tex,0,0,this.size[0],this.size[1]),a.restore())},k.prototype.onExecute=function(){var a=this.getInputData(0);if(this.isOutputConnected(0))if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else{var b=this.getInputData(1);if(this.properties.uvcode||this.properties.pixelcode){var d=512,f=512; -a?(d=a.width,f=a.height):b&&(d=b.width,f=b.height);b||(b=GL.Texture.getWhiteTexture());var h=c.getTextureType(this.properties.precision,a);this._tex=a||this._tex?c.getTargetTexture(a||this._tex,this._tex,this.properties.precision):new GL.Texture(d,f,{type:h,format:gl.RGBA,filter:gl.LINEAR});h="";this.properties.uvcode&&(h="uv = "+this.properties.uvcode,-1!=this.properties.uvcode.indexOf(";")&&(h=this.properties.uvcode));var e="";this.properties.pixelcode&&(e="result = "+this.properties.pixelcode, --1!=this.properties.pixelcode.indexOf(";")&&(e=this.properties.pixelcode));var g=this._shader;if(!(this.has_error||g&&this._shader_code==h+"|"+e)){var n=c.replaceCode(k.pixel_shader,{UV_CODE:h,PIXEL_CODE:e});try{g=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,n),this.boxcolor="#00FF00"}catch(r){GL.Shader.dumpErrorToConsole(r,Shader.SCREEN_VERTEX_SHADER,n);this.boxcolor="#FF0000";this.has_error=!0;return}this._shader=g;this._shader_code=h+"|"+e}if(this._shader){var q=this.getInputData(2);null!=q?this.properties.value= -q:q=parseFloat(this.properties.value);var v=this.graph.getTime();this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);gl.disable(gl.BLEND);a&&a.bind(0);b&&b.bind(1);var c=Mesh.getScreenQuad();g.uniforms({u_texture:0,u_textureB:1,value:q,texSize:[d,f],time:v}).draw(c)});this.setOutputData(0,this._tex)}}}},k.pixel_shader="precision highp float;\n\t\t\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec2 texSize;\n\t\tuniform float time;\n\t\tuniform float value;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord;\n\t\t\t{{UV_CODE}};\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\tvec3 color = color4.rgb;\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\tvec3 colorB = color4B.rgb;\n\t\t\tvec3 result = color;\n\t\t\tfloat alpha = 1.0;\n\t\t\t{{PIXEL_CODE}};\n\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t}\n\t\t", +function(){this.has_error=!1},k.prototype.onDrawBackground=function(a){this.flags.collapsed||20>=this.size[1]||!this.properties.show||!this._tex||this._tex.gl!=a||(a.save(),a.drawImage(this._tex,0,0,this.size[0],this.size[1]),a.restore())},k.prototype.onExecute=function(){var a=this.getInputData(0);if(this.isOutputConnected(0))if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else{var b=this.getInputData(1);if(this.properties.uvcode||this.properties.pixelcode){var d=512,h=512; +a?(d=a.width,h=a.height):b&&(d=b.width,h=b.height);b||(b=GL.Texture.getWhiteTexture());var f=c.getTextureType(this.properties.precision,a);this._tex=a||this._tex?c.getTargetTexture(a||this._tex,this._tex,this.properties.precision):new GL.Texture(d,h,{type:f,format:gl.RGBA,filter:gl.LINEAR});f="";this.properties.uvcode&&(f="uv = "+this.properties.uvcode,-1!=this.properties.uvcode.indexOf(";")&&(f=this.properties.uvcode));var e="";this.properties.pixelcode&&(e="result = "+this.properties.pixelcode, +-1!=this.properties.pixelcode.indexOf(";")&&(e=this.properties.pixelcode));var g=this._shader;if(!(this.has_error||g&&this._shader_code==f+"|"+e)){var l=c.replaceCode(k.pixel_shader,{UV_CODE:f,PIXEL_CODE:e});try{g=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,l),this.boxcolor="#00FF00"}catch(q){GL.Shader.dumpErrorToConsole(q,Shader.SCREEN_VERTEX_SHADER,l);this.boxcolor="#FF0000";this.has_error=!0;return}this._shader=g;this._shader_code=f+"|"+e}if(this._shader){var r=this.getInputData(2);null!=r?this.properties.value= +r:r=parseFloat(this.properties.value);var s=this.graph.getTime();this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);gl.disable(gl.BLEND);a&&a.bind(0);b&&b.bind(1);var c=Mesh.getScreenQuad();g.uniforms({u_texture:0,u_textureB:1,value:r,texSize:[d,h],time:s}).draw(c)});this.setOutputData(0,this._tex)}}}},k.pixel_shader="precision highp float;\n\t\t\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec2 texSize;\n\t\tuniform float time;\n\t\tuniform float value;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord;\n\t\t\t{{UV_CODE}};\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\tvec3 color = color4.rgb;\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\tvec3 colorB = color4B.rgb;\n\t\t\tvec3 result = color;\n\t\t\tfloat alpha = 1.0;\n\t\t\t{{PIXEL_CODE}};\n\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t}\n\t\t", k.registerPreset=function(a,b){k.presets[a]=b},k.registerPreset("",""),k.registerPreset("bypass","color"),k.registerPreset("add","color + colorB * value"),k.registerPreset("substract","(color - colorB) * value"),k.registerPreset("mate","mix( color, colorB, color4B.a * value)"),k.registerPreset("invert","vec3(1.0) - color"),k.registerPreset("multiply","color * colorB * value"),k.registerPreset("divide","(color / colorB) / value"),k.registerPreset("difference","abs(color - colorB) * value"),k.registerPreset("max", "max(color, colorB) * value"),k.registerPreset("min","min(color, colorB) * value"),k.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"),k.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"),k.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"),k.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"), -k.prototype.onInspect=function(a){var b=this;a.addCombo("Presets","",{values:Object.keys(k.presets),callback:function(d){var c=k.presets[d];c&&(b.setProperty("pixelcode",c),b.title=d,a.refresh())}})},G.registerNodeType("texture/operation",k),w.title="Shader",w.desc="Texture shader",w.widgets_info={code:{type:"code",lang:"glsl"},precision:{widget:"combo",values:c.MODE_VALUES}},w.prototype.onPropertyChanged=function(a,b){if("code"==a){var d=this.getShader();if(d){var c=d.uniformInfo;if(this.inputs)for(var f= -{},h=0;h lumaMax))\n\t\t\t\tcolor = vec4(rgbA, 1.0);\n\t\t\telse\n\t\t\t\tcolor = vec4(rgbB, 1.0);\n\t\t\tif(u_igamma != 1.0)\n\t\t\t\tcolor.xyz = pow( color.xyz, vec3(u_igamma) );\n\t\t\treturn color;\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\t\t}\n\t\t", -z.gamma_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_igamma;\n\t\tvoid main() {\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\t\t gl_FragColor = color;\n\t\t}\n\t\t",G.registerNodeType("texture/toviewport",z),u.title="Copy",u.desc="Copy Texture",u.widgets_info={size:{widget:"combo",values:[0,32,64,128,256,512,1024,2048]},precision:{widget:"combo", -values:c.MODE_VALUES}},u.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)){if(a){var b=a.width,d=a.height;0!=this.properties.size&&(d=b=this.properties.size);var f=this._temp_texture,h=a.type;this.properties.precision===c.LOW?h=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(h=gl.HIGH_PRECISION_FORMAT);f&&f.width==b&&f.height==d&&f.type==h||(f=gl.LINEAR,this.properties.generate_mipmaps&&isPowerOfTwo(b)&&isPowerOfTwo(d)&&(f=gl.LINEAR_MIPMAP_LINEAR), -this._temp_texture=new GL.Texture(b,d,{type:h,format:gl.RGBA,minFilter:f,magFilter:gl.LINEAR}));a.copyTo(this._temp_texture);this.properties.generate_mipmaps&&(this._temp_texture.bind(0),gl.generateMipmap(this._temp_texture.texture_type),this._temp_texture.unbind(0))}this.setOutputData(0,this._temp_texture)}},G.registerNodeType("texture/copy",u),D.title="Downsample",D.desc="Downsample Texture",D.widgets_info={iterations:{type:"number",step:1,precision:0,min:0},precision:{widget:"combo",values:c.MODE_VALUES}}, -D.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)&&a&&a.texture_type===GL.TEXTURE_2D)if(1>this.properties.iterations)this.setOutputData(0,a);else{var b=D._shader;b||(D._shader=b=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,D.pixel_shader));var d=a.width|0,f=a.height|0,h=a.type;this.properties.precision===c.LOW?h=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(h=gl.HIGH_PRECISION_FORMAT);var e=this.properties.iterations||1,g=a,n= -null,r=[],a={type:h,format:a.format},h=vec2.create(),k={u_offset:h};this._texture&&GL.Texture.releaseTemporary(this._texture);for(var q=0;q>1||0;f=f>>1||0;n=GL.Texture.getTemporary(d,f,a);r.push(n);g.setParameter(GL.TEXTURE_MAG_FILTER,GL.NEAREST);g.copyTo(n,b,k);if(1==d&&1==f)break;g=n}this._texture=r.pop();for(q=0;qthis.properties.iterations)this.setOutputData(0,a);else{var b=D._shader;b||(D._shader=b=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,D.pixel_shader));var d=a.width|0,h=a.height|0,f=a.type;this.properties.precision===c.LOW?f=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(f=gl.HIGH_PRECISION_FORMAT);var e=this.properties.iterations||1,g=a,l= +null,k=[],a={type:f,format:a.format},f=vec2.create(),q={u_offset:f};this._texture&&GL.Texture.releaseTemporary(this._texture);for(var r=0;r>1||0;h=h>>1||0;l=GL.Texture.getTemporary(d,h,a);k.push(l);g.setParameter(GL.TEXTURE_MAG_FILTER,GL.NEAREST);g.copyTo(l,b,q);if(1==d&&1==h)break;g=l}this._texture=k.pop();for(r=0;r>=2,c++,1!=d););a.copyTo(this._textures_chain[0]);for(var c=1;c<=this._textures_chain.length;++c)a=this._textures_chain[c];var f=g._shader,h=this._uniforms;h.u_mipmap_offset=this.properties.mipmap_offset;gl.disable(gl.DEPTH_TEST); -gl.disable(gl.BLEND);this._temp_texture.drawTo(function(){a.toViewport(f,h)})}},g.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform mat4 u_samples_a;\n\t\tuniform mat4 u_samples_b;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_mipmap_offset;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t//random average\n\t\t\tfor(int i = 0; i < 4; ++i)\n\t\t\t\tfor(int j = 0; j < 4; ++j)\n\t\t\t\t{\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t}\n\t\t gl_FragColor = color * 0.03125;\n\t\t}\n\t\t", -m.title="Smooth",m.desc="Smooth texture over time",m.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){m._shader||(m._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,m.pixel_shader));var b=this._temp_texture;b&&b.type==a.type&&b.width==a.width&&b.height==a.height||(b={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(a.width,a.height,b),this._temp_texture2=new GL.Texture(a.width,a.height,b),a.copyTo(this._temp_texture2));var b= -this._temp_texture,d=this._temp_texture2,c=m._shader,f=this._uniforms;f.u_factor=1-this.getInputOrProperty("factor");gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);b.drawTo(function(){d.bind(1);a.toViewport(c,f)});this.setOutputData(0,b);this._temp_texture=d;this._temp_texture2=b}},m.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_factor;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tgl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\t\t}\n\t\t", -G.registerNodeType("texture/temporal_smooth",m),s.title="Lineal Avg Smooth",s.desc="Smooth texture linearly over time",s["@samples"]={type:"number",min:1,max:64,step:1,precision:1},s.prototype.getPreviewTexture=function(){return this._temp_texture2},s.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){s._shader||(s._shader_copy=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,s.pixel_shader_copy),s._shader_avg=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,s.pixel_shader_avg)); -var b=Math.clamp(this.properties.samples,0,64),d=this.frame,c=this.properties.frames_interval;if(0==c||0==d%c){d=this._temp_texture;d&&d.type==a.type&&d.width==b||(d={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(b,1,d),this._temp_texture2=new GL.Texture(b,1,d),this._temp_texture_out=new GL.Texture(1,1,d));var f=this._temp_texture,h=this._temp_texture2,e=s._shader_copy,g=s._shader_avg,n=this._uniforms;n.u_samples=b;n.u_isamples=1/b;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST); -f.drawTo(function(){h.bind(1);a.toViewport(e,n)});this._temp_texture_out.drawTo(function(){f.toViewport(g,n)});this.setOutputData(0,this._temp_texture_out);this._temp_texture=h;this._temp_texture2=f}else this.setOutputData(0,this._temp_texture_out);this.setOutputData(1,this._temp_texture2);this.frame++}},s.pixel_shader_copy="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tif( v_coord.x <= u_isamples )\n\t\t\t\tgl_FragColor = texture2D( u_texture, vec2(0.5) );\n\t\t\telse\n\t\t\t\tgl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\t\t}\n\t\t", -s.pixel_shader_avg="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform int u_samples;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\tfor(int i = 0; i < 64; ++i)\n\t\t\t{\n\t\t\t\tcolor += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\t\t\t\tif(i == (u_samples - 1))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tgl_FragColor = color * u_isamples;\n\t\t}\n\t\t",G.registerNodeType("texture/linear_avg_smooth", -s),A.title="Image to Texture",A.desc="Uploads an image to the GPU",A.prototype.onExecute=function(){var a=this.getInputData(0);if(a){var b=a.videoWidth||a.width,d=a.videoHeight||a.height;if(a.gltexture)this.setOutputData(0,a.gltexture);else{var c=this._temp_texture;c&&c.width==b&&c.height==d||(this._temp_texture=new GL.Texture(b,d,{format:gl.RGBA,filter:gl.LINEAR}));try{this._temp_texture.uploadImage(a)}catch(f){console.error("image comes from an unsafe location, cannot be uploaded to webgl: "+f); -return}this.setOutputData(0,this._temp_texture)}}},G.registerNodeType("texture/imageToTexture",A),r.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},r.title="LUT",r.desc="Apply LUT to Texture",r.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture)); -if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.bindTexture(gl.TEXTURE_2D,null);var d=this.properties.intensity;this.isInputConnected(2)&&(this.properties.intensity=d=this.getInputData(2));this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);this._tex.drawTo(function(){b.bind(1);a.toViewport(r._shader,{u_texture:0,u_textureB:1, -u_amount:d})});this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}},r.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_amount;\n\t\t\n\t\tvoid main() {\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\t\t\t mediump vec2 quad1;\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\t\t\t mediump vec2 quad2;\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\t\t\t highp vec2 texPos1;\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t highp vec2 texPos2;\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\t\t}\n\t\t", -G.registerNodeType("texture/LUT",r),n.title="Texture to Channels",n.desc="Split texture channels",n.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this._channels||(this._channels=Array(4));for(var b=gl.RGB,d=0,c=0;4>c;c++)this.isOutputConnected(c)?(this._channels[c]&&this._channels[c].width==a.width&&this._channels[c].height==a.height&&this._channels[c].type==a.type&&this._channels[c].format==b||(this._channels[c]=new GL.Texture(a.width,a.height,{type:a.type,format:b,filter:gl.LINEAR})), -d++):this._channels[c]=null;if(d){gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);for(var f=Mesh.getScreenQuad(),h=n._shader,e=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],c=0;4>c;c++)this._channels[c]&&(this._channels[c].drawTo(function(){a.bind(0);h.uniforms({u_texture:0,u_mask:e[c]}).draw(f)}),this.setOutputData(c,this._channels[c]))}}},n.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec4 u_mask;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\t\t}\n\t\t", -G.registerNodeType("texture/textureChannels",n),x.title="Channels to Texture",x.desc="Split texture channels",x.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},x.prototype.onExecute=function(){var a=c.getWhiteTexture(),b=this.getInputData(0)||a,d=this.getInputData(1)||a,f=this.getInputData(2)||a,h=this.getInputData(3)||a;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad();x._shader||(x._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,x.pixel_shader));var g=x._shader, -a=Math.max(b.width,d.width,f.width,h.width),n=Math.max(b.height,d.height,f.height,h.height),r=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._texture&&this._texture.width==a&&this._texture.height==n&&this._texture.type==r||(this._texture=new GL.Texture(a,n,{type:r,format:gl.RGBA,filter:gl.LINEAR}));a=this._color;a[0]=this.properties.R;a[1]=this.properties.G;a[2]=this.properties.B;a[3]=this.properties.A;var q=this._uniforms;this._texture.drawTo(function(){b.bind(0); -d.bind(1);f.bind(2);h.bind(3);g.uniforms(q).draw(e)});this.setOutputData(0,this._texture)},x.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureR;\n\t\tuniform sampler2D u_textureG;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform vec4 u_color;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = u_color * vec4( \t\t\t\t\ttexture2D(u_textureR, v_coord).r,\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\t\t}\n\t\t", -G.registerNodeType("texture/channelsTexture",x),v.title="Color",v.desc="Generates a 1x1 texture with a constant color",v.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},v.prototype.onDrawBackground=function(a){var b=this.properties.color;a.fillStyle="rgb("+Math.floor(255*Math.clamp(b[0],0,1))+","+Math.floor(255*Math.clamp(b[1],0,1))+","+Math.floor(255*Math.clamp(b[2],0,1))+")";this.flags.collapsed?this.boxcolor=a.fillStyle:a.fillRect(0,0,this.size[0],this.size[1])},v.prototype.onExecute= -function(){var a=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._tex&&this._tex.type==a||(this._tex=new GL.Texture(1,1,{format:gl.RGBA,type:a,minFilter:gl.NEAREST}));a=this.properties.color;if(this.inputs)for(var b=0;ba.width?d: -a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad(),g=null,n=this._uniforms;f?(g=b._shader_tex,g||(g=b._shader_tex=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader,{MIX_TEX:""}))):(g=b._shader_factor,g||(g=b._shader_factor=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader)),h=null==h?this.properties.factor:h,n.u_mix.set([h,h,h,h]));var r=this.properties.invert;this._tex.drawTo(function(){a.bind(r?1:0);d.bind(r?0:1);f&&f.bind(2); -g.uniforms(n).draw(e)});this.setOutputData(0,this._tex)}}},b.prototype.onGetInputs=function(){return[["factor","number"]]},b.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform sampler2D u_textureB;\n\t\t#ifdef MIX_TEX\n\t\t\tuniform sampler2D u_textureMix;\n\t\t#else\n\t\t\tuniform vec4 u_mix;\n\t\t#endif\n\t\t\n\t\tvoid main() {\n\t\t\t#ifdef MIX_TEX\n\t\t\t vec4 f = texture2D(u_textureMix, v_coord);\n\t\t\t#else\n\t\t\t vec4 f = u_mix;\n\t\t\t#endif\n\t\t gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\t\t}\n\t\t", -G.registerNodeType("texture/mix",b),d.title="Edges",d.desc="Detects edges",d.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},d.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else if(a){this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var b=Mesh.getScreenQuad(),f=d._shader,h=this.properties.invert,e=this.properties.factor, -g=this.properties.threshold?1:0;this._tex.drawTo(function(){a.bind(0);f.uniforms({u_texture:0,u_isize:[1/a.width,1/a.height],u_factor:e,u_threshold:g,u_invert:h?1:0}).draw(b)});this.setOutputData(0,this._tex)}}},d.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_isize;\n\t\tuniform int u_invert;\n\t\tuniform float u_factor;\n\t\tuniform float u_threshold;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 center = texture2D(u_texture, v_coord);\n\t\t\tvec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\t\t\tvec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\t\t\tvec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\t\t\tvec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\t\t\tvec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\t\t\tdiff *= u_factor;\n\t\t\tif(u_invert == 1)\n\t\t\t\tdiff.xyz = vec3(1.0) - diff.xyz;\n\t\t\tif( u_threshold == 0.0 )\n\t\t\t\tgl_FragColor = vec4( diff.xyz, center.a );\n\t\t\telse\n\t\t\t\tgl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\t\t}\n\t\t", +G.registerNodeType("texture/downsample",D),A.title="Average",A.desc="Compute a partial average (32 random samples) of a texture and stores it as a 1x1 pixel texture.\n If high_quality is true, then it generates the mipmaps first and reads from the lower one.",A.prototype.onExecute=function(){this.properties.use_previous_frame||this.updateAverage();var a=this._luminance;this.setOutputData(0,this._temp_texture);this.setOutputData(1,a);this.setOutputData(2,(a[0]+a[1]+a[2])/3)},A.prototype.onPreRenderExecute= +function(){this.updateAverage()},A.prototype.updateAverage=function(){var a=this.getInputData(0);if(a&&(this.isOutputConnected(0)||this.isOutputConnected(1)||this.isOutputConnected(2))){if(!A._shader){A._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,A.pixel_shader);for(var b=new Float32Array(16),d=0;d>=2,c++,1!=d););a.copyTo(this._textures_chain[0]);for(var c=1;c<=this._textures_chain.length;++c)a=this._textures_chain[c];var h=g._shader,f=this._uniforms;f.u_mipmap_offset=this.properties.mipmap_offset;gl.disable(gl.DEPTH_TEST); +gl.disable(gl.BLEND);this._temp_texture.drawTo(function(){a.toViewport(h,f)})}},g.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform mat4 u_samples_a;\n\t\tuniform mat4 u_samples_b;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_mipmap_offset;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t//random average\n\t\t\tfor(int i = 0; i < 4; ++i)\n\t\t\t\tfor(int j = 0; j < 4; ++j)\n\t\t\t\t{\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t}\n\t\t gl_FragColor = color * 0.03125;\n\t\t}\n\t\t", +n.title="Smooth",n.desc="Smooth texture over time",n.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){n._shader||(n._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,n.pixel_shader));var b=this._temp_texture;b&&b.type==a.type&&b.width==a.width&&b.height==a.height||(b={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(a.width,a.height,b),this._temp_texture2=new GL.Texture(a.width,a.height,b),a.copyTo(this._temp_texture2));var b= +this._temp_texture,d=this._temp_texture2,c=n._shader,h=this._uniforms;h.u_factor=1-this.getInputOrProperty("factor");gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);b.drawTo(function(){d.bind(1);a.toViewport(c,h)});this.setOutputData(0,b);this._temp_texture=d;this._temp_texture2=b}},n.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_factor;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tgl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\t\t}\n\t\t", +G.registerNodeType("texture/temporal_smooth",n),t.title="Lineal Avg Smooth",t.desc="Smooth texture linearly over time",t["@samples"]={type:"number",min:1,max:64,step:1,precision:1},t.prototype.getPreviewTexture=function(){return this._temp_texture2},t.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){t._shader||(t._shader_copy=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,t.pixel_shader_copy),t._shader_avg=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,t.pixel_shader_avg)); +var b=Math.clamp(this.properties.samples,0,64),d=this.frame,c=this.properties.frames_interval;if(0==c||0==d%c){d=this._temp_texture;d&&d.type==a.type&&d.width==b||(d={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(b,1,d),this._temp_texture2=new GL.Texture(b,1,d),this._temp_texture_out=new GL.Texture(1,1,d));var h=this._temp_texture,f=this._temp_texture2,e=t._shader_copy,g=t._shader_avg,l=this._uniforms;l.u_samples=b;l.u_isamples=1/b;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST); +h.drawTo(function(){f.bind(1);a.toViewport(e,l)});this._temp_texture_out.drawTo(function(){h.toViewport(g,l)});this.setOutputData(0,this._temp_texture_out);this._temp_texture=f;this._temp_texture2=h}else this.setOutputData(0,this._temp_texture_out);this.setOutputData(1,this._temp_texture2);this.frame++}},t.pixel_shader_copy="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tif( v_coord.x <= u_isamples )\n\t\t\t\tgl_FragColor = texture2D( u_texture, vec2(0.5) );\n\t\t\telse\n\t\t\t\tgl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\t\t}\n\t\t", +t.pixel_shader_avg="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform int u_samples;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\tfor(int i = 0; i < 64; ++i)\n\t\t\t{\n\t\t\t\tcolor += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\t\t\t\tif(i == (u_samples - 1))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tgl_FragColor = color * u_isamples;\n\t\t}\n\t\t",G.registerNodeType("texture/linear_avg_smooth", +t),B.title="Image to Texture",B.desc="Uploads an image to the GPU",B.prototype.onExecute=function(){var a=this.getInputData(0);if(a){var b=a.videoWidth||a.width,d=a.videoHeight||a.height;if(a.gltexture)this.setOutputData(0,a.gltexture);else{var c=this._temp_texture;c&&c.width==b&&c.height==d||(this._temp_texture=new GL.Texture(b,d,{format:gl.RGBA,filter:gl.LINEAR}));try{this._temp_texture.uploadImage(a)}catch(h){console.error("image comes from an unsafe location, cannot be uploaded to webgl: "+h); +return}this.setOutputData(0,this._temp_texture)}}},G.registerNodeType("texture/imageToTexture",B),l.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},l.title="LUT",l.desc="Apply LUT to Texture",l.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture)); +if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.bindTexture(gl.TEXTURE_2D,null);var d=this.properties.intensity;this.isInputConnected(2)&&(this.properties.intensity=d=this.getInputData(2));this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);this._tex.drawTo(function(){b.bind(1);a.toViewport(l._shader,{u_texture:0,u_textureB:1, +u_amount:d})});this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}},l.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_amount;\n\t\t\n\t\tvoid main() {\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\t\t\t mediump vec2 quad1;\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\t\t\t mediump vec2 quad2;\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\t\t\t highp vec2 texPos1;\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t highp vec2 texPos2;\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\t\t}\n\t\t", +G.registerNodeType("texture/LUT",l),q.title="Texture to Channels",q.desc="Split texture channels",q.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this._channels||(this._channels=Array(4));for(var b=gl.RGB,d=0,c=0;4>c;c++)this.isOutputConnected(c)?(this._channels[c]&&this._channels[c].width==a.width&&this._channels[c].height==a.height&&this._channels[c].type==a.type&&this._channels[c].format==b||(this._channels[c]=new GL.Texture(a.width,a.height,{type:a.type,format:b,filter:gl.LINEAR})), +d++):this._channels[c]=null;if(d){gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);for(var h=Mesh.getScreenQuad(),f=q._shader,e=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],c=0;4>c;c++)this._channels[c]&&(this._channels[c].drawTo(function(){a.bind(0);f.uniforms({u_texture:0,u_mask:e[c]}).draw(h)}),this.setOutputData(c,this._channels[c]))}}},q.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec4 u_mask;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\t\t}\n\t\t", +G.registerNodeType("texture/textureChannels",q),w.title="Channels to Texture",w.desc="Split texture channels",w.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},w.prototype.onExecute=function(){var a=c.getWhiteTexture(),b=this.getInputData(0)||a,d=this.getInputData(1)||a,h=this.getInputData(2)||a,f=this.getInputData(3)||a;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad();w._shader||(w._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,w.pixel_shader));var g=w._shader, +a=Math.max(b.width,d.width,h.width,f.width),l=Math.max(b.height,d.height,h.height,f.height),k=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._texture&&this._texture.width==a&&this._texture.height==l&&this._texture.type==k||(this._texture=new GL.Texture(a,l,{type:k,format:gl.RGBA,filter:gl.LINEAR}));a=this._color;a[0]=this.properties.R;a[1]=this.properties.G;a[2]=this.properties.B;a[3]=this.properties.A;var q=this._uniforms;this._texture.drawTo(function(){b.bind(0); +d.bind(1);h.bind(2);f.bind(3);g.uniforms(q).draw(e)});this.setOutputData(0,this._texture)},w.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureR;\n\t\tuniform sampler2D u_textureG;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform vec4 u_color;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = u_color * vec4( \t\t\t\t\ttexture2D(u_textureR, v_coord).r,\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\t\t}\n\t\t", +G.registerNodeType("texture/channelsTexture",w),x.title="Color",x.desc="Generates a 1x1 texture with a constant color",x.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},x.prototype.onDrawBackground=function(a){var b=this.properties.color;a.fillStyle="rgb("+Math.floor(255*Math.clamp(b[0],0,1))+","+Math.floor(255*Math.clamp(b[1],0,1))+","+Math.floor(255*Math.clamp(b[2],0,1))+")";this.flags.collapsed?this.boxcolor=a.fillStyle:a.fillRect(0,0,this.size[0],this.size[1])},x.prototype.onExecute= +function(){var a=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._tex&&this._tex.type==a||(this._tex=new GL.Texture(1,1,{format:gl.RGBA,type:a,minFilter:gl.NEAREST}));a=this.properties.color;if(this.inputs)for(var b=0;ba.width?d: +a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad(),g=null,l=this._uniforms;h?(g=b._shader_tex,g||(g=b._shader_tex=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader,{MIX_TEX:""}))):(g=b._shader_factor,g||(g=b._shader_factor=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader)),f=null==f?this.properties.factor:f,l.u_mix.set([f,f,f,f]));var k=this.properties.invert;this._tex.drawTo(function(){a.bind(k?1:0);d.bind(k?0:1);h&&h.bind(2); +g.uniforms(l).draw(e)});this.setOutputData(0,this._tex)}}},b.prototype.onGetInputs=function(){return[["factor","number"]]},b.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform sampler2D u_textureB;\n\t\t#ifdef MIX_TEX\n\t\t\tuniform sampler2D u_textureMix;\n\t\t#else\n\t\t\tuniform vec4 u_mix;\n\t\t#endif\n\t\t\n\t\tvoid main() {\n\t\t\t#ifdef MIX_TEX\n\t\t\t vec4 f = texture2D(u_textureMix, v_coord);\n\t\t\t#else\n\t\t\t vec4 f = u_mix;\n\t\t\t#endif\n\t\t gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\t\t}\n\t\t", +G.registerNodeType("texture/mix",b),d.title="Edges",d.desc="Detects edges",d.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},d.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else if(a){this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var b=Mesh.getScreenQuad(),h=d._shader,f=this.properties.invert,e=this.properties.factor, +g=this.properties.threshold?1:0;this._tex.drawTo(function(){a.bind(0);h.uniforms({u_texture:0,u_isize:[1/a.width,1/a.height],u_factor:e,u_threshold:g,u_invert:f?1:0}).draw(b)});this.setOutputData(0,this._tex)}}},d.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_isize;\n\t\tuniform int u_invert;\n\t\tuniform float u_factor;\n\t\tuniform float u_threshold;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 center = texture2D(u_texture, v_coord);\n\t\t\tvec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\t\t\tvec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\t\t\tvec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\t\t\tvec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\t\t\tvec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\t\t\tdiff *= u_factor;\n\t\t\tif(u_invert == 1)\n\t\t\t\tdiff.xyz = vec3(1.0) - diff.xyz;\n\t\t\tif( u_threshold == 0.0 )\n\t\t\t\tgl_FragColor = vec4( diff.xyz, center.a );\n\t\t\telse\n\t\t\t\tgl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\t\t}\n\t\t", G.registerNodeType("texture/edges",d),h.title="Depth Range",h.desc="Generates a texture with a depth range",h.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a){var b=gl.UNSIGNED_BYTE;this.properties.high_precision&&(b=gl.half_float_ext?gl.HALF_FLOAT_OES:gl.FLOAT);this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&&this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGBA, filter:gl.LINEAR}));var d=this._uniforms,b=this.properties.distance;this.isInputConnected(1)&&(b=this.getInputData(1),this.properties.distance=b);var c=this.properties.range;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.range=c);d.u_distance=b;d.u_range=c;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var f=Mesh.getScreenQuad();h._shader||(h._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,h.pixel_shader),h._shader_onlydepth=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,h.pixel_shader, {ONLY_DEPTH:""}));var e=this.properties.only_depth?h._shader_onlydepth:h._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1,1E3];d.u_camera_planes=b;this._temp_texture.drawTo(function(){a.bind(0);e.uniforms(d).draw(f)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},h.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform float u_distance;\n\t\tuniform float u_range;\n\t\t\n\t\tfloat LinearDepth()\n\t\t{\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\t\t\tdepth = depth * 2.0 - 1.0;\n\t\t\treturn zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t\tfloat depth = LinearDepth();\n\t\t\t#ifdef ONLY_DEPTH\n\t\t\t gl_FragColor = vec4(depth);\n\t\t\t#else\n\t\t\t\tfloat diff = abs(depth * u_camera_planes.y - u_distance);\n\t\t\t\tfloat dof = 1.0;\n\t\t\t\tif(diff <= u_range)\n\t\t\t\t\tdof = diff / u_range;\n\t\t\t gl_FragColor = vec4(dof);\n\t\t\t#endif\n\t\t}\n\t\t", G.registerNodeType("texture/depth_range",h),f.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},f.title="Linear Depth",f.desc="Creates a color texture with linear depth",f.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a&&(a.format==gl.DEPTH_COMPONENT||a.format==gl.DEPTH_STENCIL)){var b=this.properties.precision==c.HIGH?gl.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&& this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGB,filter:gl.LINEAR}));var d=this._uniforms;d.u_near=a.near_far_planes[0];d.u_far=a.near_far_planes[1];d.u_invert=this.properties.invert?1:0;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var h=Mesh.getScreenQuad();f._shader||(f._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,f.pixel_shader));var e=f._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera? LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1,1E3];d.u_camera_planes=b;this._temp_texture.drawTo(function(){a.bind(0);e.uniforms(d).draw(h)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},f.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_near;\n\t\tuniform float u_far;\n\t\tuniform int u_invert;\n\t\t\n\t\tvoid main() {\n\t\t\tfloat zNear = u_near;\n\t\t\tfloat zFar = u_far;\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\t\t\tdepth = depth * 2.0 - 1.0;\n\t\t\tfloat f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t\tif( u_invert == 1 )\n\t\t\t\tf = 1.0 - f;\n\t\t\tgl_FragColor = vec4(vec3(f),1.0);\n\t\t}\n\t\t", -G.registerNodeType("texture/linear_depth",f),E.title="Blur",E.desc="Blur a texture",E.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},E.max_iterations=20,E.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._final_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(b=this._final_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));var d=this.properties.iterations;this.isInputConnected(1)&& -(d=this.getInputData(1),this.properties.iterations=d);d=Math.min(Math.floor(d),E.max_iterations);if(0==d)this.setOutputData(0,a);else{var c=this.properties.intensity;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.intensity=c);var f=G.camera_aspect;f||void 0===window.gl||(f=gl.canvas.height/gl.canvas.width);f||(f=1);var f=this.properties.preserve_aspect?f:1,h=this.properties.scale||[1,1];a.applyBlur(f*h[0],h[1],c,b);for(a=1;a>=1;1<(d|0)&&(d>>=1);if(2>b)break;r=g[p]=GL.Texture.getTemporary(b,d,f);m[0]=1/k.width;m[1]=1/k.height;k.blit(r,n.uniforms(e));k=r}this.isOutputConnected(2)&&(b=this._average_texture,b&&b.type==a.type&&b.format==a.format||(b=this._average_texture=new GL.Texture(1,1,{type:a.type,format:a.format,filter:gl.LINEAR})),m[0]=1/k.width,m[1]=1/k.height,e.u_intensity=l,e.u_delta=1,k.blit(b,n.uniforms(e)),this.setOutputData(2,b));gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE);e.u_intensity= -this.getInputOrProperty("persistence");e.u_delta=0.5;for(p-=2;0<=p;p--)r=g[p],g[p]=null,m[0]=1/k.width,m[1]=1/k.height,k.blit(r,n.uniforms(e)),GL.Texture.releaseTemporary(k),k=r;gl.disable(gl.BLEND);this.isOutputConnected(1)&&(g=this._glow_texture,g&&g.width==a.width&&g.height==a.height&&g.type==h&&g.format==a.format||(g=this._glow_texture=new GL.Texture(a.width,a.height,{type:h,format:a.format,filter:gl.LINEAR})),k.blit(g),this.setOutputData(1,g));if(this.isOutputConnected(0)){g=this._final_texture; -g&&g.width==a.width&&g.height==a.height&&g.type==h&&g.format==a.format||(g=this._final_texture=new GL.Texture(a.width,a.height,{type:h,format:a.format,filter:gl.LINEAR}));var t=this.getInputData(1),s=this.getInputOrProperty("dirt_factor");e.u_intensity=l;n=t?q._dirt_final_shader:q._final_shader;n||(n=t?q._dirt_final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,q.final_pixel_shader,{USE_DIRT:""}):q._final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,q.final_pixel_shader));g.drawTo(function(){a.bind(0); -k.bind(1);t&&(n.setUniform("u_dirt_factor",s),n.setUniform("u_dirt_texture",t.bind(2)));n.toViewport(e)});this.setOutputData(0,g)}GL.Texture.releaseTemporary(k)}},q.cut_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform float u_threshold;\n\tvoid main() {\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\t}",q.scale_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\t}", -q.final_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform sampler2D u_glow_texture;\n\t#ifdef USE_DIRT\n\t\tuniform sampler2D u_dirt_texture;\n\t#endif\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\tuniform float u_dirt_factor;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tvec4 glow = sampleBox( v_coord );\n\t\t#ifdef USE_DIRT\n\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\t\t#endif\n\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\t}", -G.registerNodeType("texture/glow",q),I.title="Kuwahara Filter",I.desc="Filters a texture giving an artistic oil canvas painting",I.max_radius=10,I._shaders=[],I.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));b=this.properties.radius;b=Math.min(Math.floor(b),I.max_radius);if(0== -b)this.setOutputData(0,a);else{var d=this.properties.intensity,c=G.camera_aspect;c||void 0===window.gl||(c=gl.canvas.height/gl.canvas.width);c||(c=1);c=this.properties.preserve_aspect?c:1;I._shaders[b]||(I._shaders[b]=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,I.pixel_shader,{RADIUS:b.toFixed(0)}));var f=I._shaders[b],h=GL.Mesh.getScreenQuad();a.bind(0);this._temp_texture.drawTo(function(){f.uniforms({u_texture:0,u_intensity:d,u_resolution:[a.width,a.height],u_iResolution:[1/a.width,1/a.height]}).draw(h)}); +G.registerNodeType("texture/linear_depth",f),s.title="Blur",s.desc="Blur a texture",s.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},s.max_iterations=20,s.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._final_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(b=this._final_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));var d=this.properties.iterations;this.isInputConnected(1)&& +(d=this.getInputData(1),this.properties.iterations=d);d=Math.min(Math.floor(d),s.max_iterations);if(0==d)this.setOutputData(0,a);else{var c=this.properties.intensity;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.intensity=c);var h=G.camera_aspect;h||void 0===window.gl||(h=gl.canvas.height/gl.canvas.width);h||(h=1);var h=this.properties.preserve_aspect?h:1,f=this.properties.scale||[1,1];a.applyBlur(h*f[0],f[1],c,b);for(a=1;a>=1;1<(d|0)&&(d>>=1);if(2>b)break;k=g[n]=GL.Texture.getTemporary(b,d,h);x[0]=1/q.width;x[1]=1/q.height;q.blit(k,l.uniforms(e));q=k}this.isOutputConnected(2)&&(b=this._average_texture,b&&b.type==a.type&&b.format==a.format||(b=this._average_texture=new GL.Texture(1,1,{type:a.type,format:a.format,filter:gl.LINEAR})),x[0]=1/q.width,x[1]=1/q.height,e.u_intensity=m,e.u_delta=1,q.blit(b,l.uniforms(e)),this.setOutputData(2,b));gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE);e.u_intensity= +this.getInputOrProperty("persistence");e.u_delta=0.5;for(n-=2;0<=n;n--)k=g[n],g[n]=null,x[0]=1/q.width,x[1]=1/q.height,q.blit(k,l.uniforms(e)),GL.Texture.releaseTemporary(q),q=k;gl.disable(gl.BLEND);this.isOutputConnected(1)&&(g=this._glow_texture,g&&g.width==a.width&&g.height==a.height&&g.type==f&&g.format==a.format||(g=this._glow_texture=new GL.Texture(a.width,a.height,{type:f,format:a.format,filter:gl.LINEAR})),q.blit(g),this.setOutputData(1,g));if(this.isOutputConnected(0)){g=this._final_texture; +g&&g.width==a.width&&g.height==a.height&&g.type==f&&g.format==a.format||(g=this._final_texture=new GL.Texture(a.width,a.height,{type:f,format:a.format,filter:gl.LINEAR}));var p=this.getInputData(1),w=this.getInputOrProperty("dirt_factor");e.u_intensity=m;l=p?r._dirt_final_shader:r._final_shader;l||(l=p?r._dirt_final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,r.final_pixel_shader,{USE_DIRT:""}):r._final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,r.final_pixel_shader));g.drawTo(function(){a.bind(0); +q.bind(1);p&&(l.setUniform("u_dirt_factor",w),l.setUniform("u_dirt_texture",p.bind(2)));l.toViewport(e)});this.setOutputData(0,g)}GL.Texture.releaseTemporary(q)}},r.cut_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform float u_threshold;\n\tvoid main() {\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\t}",r.scale_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\t}", +r.final_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform sampler2D u_glow_texture;\n\t#ifdef USE_DIRT\n\t\tuniform sampler2D u_dirt_texture;\n\t#endif\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\tuniform float u_dirt_factor;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tvec4 glow = sampleBox( v_coord );\n\t\t#ifdef USE_DIRT\n\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\t\t#endif\n\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\t}", +G.registerNodeType("texture/glow",r),I.title="Kuwahara Filter",I.desc="Filters a texture giving an artistic oil canvas painting",I.max_radius=10,I._shaders=[],I.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));b=this.properties.radius;b=Math.min(Math.floor(b),I.max_radius);if(0== +b)this.setOutputData(0,a);else{var d=this.properties.intensity,c=G.camera_aspect;c||void 0===window.gl||(c=gl.canvas.height/gl.canvas.width);c||(c=1);c=this.properties.preserve_aspect?c:1;I._shaders[b]||(I._shaders[b]=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,I.pixel_shader,{RADIUS:b.toFixed(0)}));var h=I._shaders[b],f=GL.Mesh.getScreenQuad();a.bind(0);this._temp_texture.drawTo(function(){h.uniforms({u_texture:0,u_intensity:d,u_resolution:[a.width,a.height],u_iResolution:[1/a.width,1/a.height]}).draw(f)}); this.setOutputData(0,this._temp_texture)}}},I.pixel_shader="\nprecision highp float;\nvarying vec2 v_coord;\nuniform sampler2D u_texture;\nuniform float u_intensity;\nuniform vec2 u_resolution;\nuniform vec2 u_iResolution;\n#ifndef RADIUS\n\t#define RADIUS 7\n#endif\nvoid main() {\n\n\tconst int radius = RADIUS;\n\tvec2 fragCoord = v_coord;\n\tvec2 src_size = u_iResolution;\n\tvec2 uv = v_coord;\n\tfloat n = float((radius + 1) * (radius + 1));\n\tint i;\n\tint j;\n\tvec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\tvec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\tvec3 c;\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm0 += c;\n\t\t\ts0 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm1 += c;\n\t\t\ts1 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm2 += c;\n\t\t\ts2 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm3 += c;\n\t\t\ts3 += c * c;\n\t\t}\n\t}\n\t\n\tfloat min_sigma2 = 1e+2;\n\tm0 /= n;\n\ts0 = abs(s0 / n - m0 * m0);\n\t\n\tfloat sigma2 = s0.r + s0.g + s0.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m0, 1.0);\n\t}\n\t\n\tm1 /= n;\n\ts1 = abs(s1 / n - m1 * m1);\n\t\n\tsigma2 = s1.r + s1.g + s1.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m1, 1.0);\n\t}\n\t\n\tm2 /= n;\n\ts2 = abs(s2 / n - m2 * m2);\n\t\n\tsigma2 = s2.r + s2.g + s2.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m2, 1.0);\n\t}\n\t\n\tm3 /= n;\n\ts3 = abs(s3 / n - m3 * m3);\n\t\n\tsigma2 = s3.r + s3.g + s3.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m3, 1.0);\n\t}\n}\n", G.registerNodeType("texture/kuwahara",I),J.title="XDoG Filter",J.desc="Filters a texture giving an artistic ink style",J.max_radius=10,J._shaders=[],J.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));J._xdog_shader||(J._xdog_shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,J.xdog_pixel_shader)); -var d=J._xdog_shader,c=GL.Mesh.getScreenQuad(),f=this.properties.sigma,h=this.properties.k,e=this.properties.p,g=this.properties.epsilon,n=this.properties.phi;a.bind(0);this._temp_texture.drawTo(function(){d.uniforms({src:0,sigma:f,k:h,p:e,epsilon:g,phi:n,cvsWidth:a.width,cvsHeight:a.height}).draw(c)});this.setOutputData(0,this._temp_texture)}},J.xdog_pixel_shader="\nprecision highp float;\nuniform sampler2D src;\n\nuniform float cvsHeight;\nuniform float cvsWidth;\n\nuniform float sigma;\nuniform float k;\nuniform float p;\nuniform float epsilon;\nuniform float phi;\nvarying vec2 v_coord;\n\nfloat cosh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat cosH = (tmp + 1.0 / tmp) / 2.0;\n\treturn cosH;\n}\n\nfloat tanh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\treturn tanH;\n}\n\nfloat sinh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat sinH = (tmp - 1.0 / tmp) / 2.0;\n\treturn sinH;\n}\n\nvoid main(void){\n\tvec3 destColor = vec3(0.0);\n\tfloat tFrag = 1.0 / cvsHeight;\n\tfloat sFrag = 1.0 / cvsWidth;\n\tvec2 Frag = vec2(sFrag,tFrag);\n\tvec2 uv = gl_FragCoord.st;\n\tfloat twoSigmaESquared = 2.0 * sigma * sigma;\n\tfloat twoSigmaRSquared = twoSigmaESquared * k * k;\n\tint halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\tconst int MAX_NUM_ITERATION = 99999;\n\tvec2 sum = vec2(0.0);\n\tvec2 norm = vec2(0.0);\n\n\tfor(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\t\tint i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\t\tint j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\t\tfloat d = length(vec2(i,j));\n\t\tvec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\t\t\t\t\t\t\texp( -d * d / twoSigmaRSquared ));\n\n\t\tvec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\t\tnorm += kernel;\n\t\tsum += kernel * L;\n\t}\n\n\tsum /= norm;\n\n\tfloat H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\tfloat edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\tdestColor = vec3(edge);\n\tgl_FragColor = vec4(destColor, 1.0);\n}", +var d=J._xdog_shader,c=GL.Mesh.getScreenQuad(),h=this.properties.sigma,f=this.properties.k,e=this.properties.p,g=this.properties.epsilon,l=this.properties.phi;a.bind(0);this._temp_texture.drawTo(function(){d.uniforms({src:0,sigma:h,k:f,p:e,epsilon:g,phi:l,cvsWidth:a.width,cvsHeight:a.height}).draw(c)});this.setOutputData(0,this._temp_texture)}},J.xdog_pixel_shader="\nprecision highp float;\nuniform sampler2D src;\n\nuniform float cvsHeight;\nuniform float cvsWidth;\n\nuniform float sigma;\nuniform float k;\nuniform float p;\nuniform float epsilon;\nuniform float phi;\nvarying vec2 v_coord;\n\nfloat cosh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat cosH = (tmp + 1.0 / tmp) / 2.0;\n\treturn cosH;\n}\n\nfloat tanh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\treturn tanH;\n}\n\nfloat sinh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat sinH = (tmp - 1.0 / tmp) / 2.0;\n\treturn sinH;\n}\n\nvoid main(void){\n\tvec3 destColor = vec3(0.0);\n\tfloat tFrag = 1.0 / cvsHeight;\n\tfloat sFrag = 1.0 / cvsWidth;\n\tvec2 Frag = vec2(sFrag,tFrag);\n\tvec2 uv = gl_FragCoord.st;\n\tfloat twoSigmaESquared = 2.0 * sigma * sigma;\n\tfloat twoSigmaRSquared = twoSigmaESquared * k * k;\n\tint halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\tconst int MAX_NUM_ITERATION = 99999;\n\tvec2 sum = vec2(0.0);\n\tvec2 norm = vec2(0.0);\n\n\tfor(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\t\tint i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\t\tint j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\t\tfloat d = length(vec2(i,j));\n\t\tvec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\t\t\t\t\t\t\texp( -d * d / twoSigmaRSquared ));\n\n\t\tvec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\t\tnorm += kernel;\n\t\tsum += kernel * L;\n\t}\n\n\tsum /= norm;\n\n\tfloat H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\tfloat edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\tdestColor = vec3(edge);\n\tgl_FragColor = vec4(destColor, 1.0);\n}", G.registerNodeType("texture/xDoG",J),H.title="Webcam",H.desc="Webcam texture",H.is_webcam_open=!1,H.prototype.openStream=function(){function a(d){H.is_webcam_open=!1;console.log("Webcam rejected",d);b._webcam_stream=!1;b.boxcolor="red";b.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](a);var b=this}},H.prototype.closeStream=function(){if(this._webcam_stream){var a= this._webcam_stream.getTracks();if(a.length)for(var b=0;b=this.size[1]||!this._video||(a.save(),a.webgl?this._video_texture&&a.drawImage(this._video_texture,0,0,this.size[0],this.size[1]):a.drawImage(this._video, 0,0,this.size[0],this.size[1]),a.restore())},H.prototype.onExecute=function(){null!=this._webcam_stream||this._waiting_confirmation||this.openStream();if(this._video&&this._video.videoWidth){var a=this._video.videoWidth,b=this._video.videoHeight,d=this._video_texture;d&&d.width==a&&d.height==b||(this._video_texture=new GL.Texture(a,b,{format:gl.RGB,filter:gl.LINEAR}));this._video_texture.uploadImage(this._video);this._video_texture.version=++this.version;this.properties.texture_name&&(c.getTexturesContainer()[this.properties.texture_name]= this._video_texture);this.setOutputData(0,this._video_texture);for(a=1;aMath.abs(d))return c[1];d=(a-c[0])/d;return c[1]*(1-d)+f[1]*d}}return 0}},t.prototype.updateCurve=function(){for(var a=this._values,b=a.length/4,d=this.properties.split_channels,c=0;cMath.abs(d))return c[1];d=(a-c[0])/d;return c[1]*(1-d)+h[1]*d}}return 0}},u.prototype.updateCurve=function(){for(var a=this._values,b=a.length/4,d=this.properties.split_channels,c=0;c=f;){e=0.5*(g+f)|0;c=a[e];if(c==d)break;if(f==g-1)return f;ca&&(a=1);this.points&&this.points.length==3*a||(this.points=new Float32Array(3*a));this.properties.generate_normals?this.normals&&this.normals.length==this.points.length||(this.normals=new Float32Array(this.points.length)):this.normals=null;var d=this._last_radius||this.properties.radius,c=this.properties.mode,f=this.getInputData(0);this._old_obj_version=f?f._version:null;this.points=l.generatePoints(d,a,c,this.points, -this.normals,this.properties.regular,f);this.version++};l.generatePoints=function(a,d,c,f,e,g,n){var r=3*d;f&&f.length==r||(f=new Float32Array(r));var k=new Float32Array(3),v=new Float32Array([0,1,0]);if(g)if(c==l.RECTANGLE){r=Math.floor(Math.sqrt(d));for(d=0;d=f;){e=0.5*(g+f)|0;c=a[e];if(c==d)break;if(f==g-1)return f;ca&&(a=1);this.points&&this.points.length==3*a||(this.points=new Float32Array(3*a));this.properties.generate_normals?this.normals&&this.normals.length==this.points.length||(this.normals=new Float32Array(this.points.length)):this.normals=null;var d=this._last_radius||this.properties.radius,c=this.properties.mode,f=this.getInputData(0);this._old_obj_version=f?f._version:null;this.points=m.generatePoints(d,a,c,this.points, +this.normals,this.properties.regular,f);this.version++};m.generatePoints=function(a,d,c,f,e,g,l){var k=3*d;f&&f.length==k||(f=new Float32Array(k));var q=new Float32Array(3),x=new Float32Array([0,1,0]);if(g)if(c==m.RECTANGLE){k=Math.floor(Math.sqrt(d));for(d=0;dg||xn&&nr))break}this.geometry.indices=this.indices=new Uint32Array(k)}this.indices&&this.indices.length?(this.geometry.indices=this.indices,this.setOutputData(0,this.geometry)):this.setOutputData(0,null)}};A.registerNodeType("geometry/connectPoints",u);"undefined"!=typeof GL&&(D.title="to geometry",D.desc="converts a mesh to geometry",D.prototype.onExecute= -function(){var a=this.getInputData(0);if(a){if(a!=this.last_mesh){this.last_mesh=a;for(i in a.vertexBuffers)this.geometry[i]=a.vertexBuffers[i].data;a.indexBuffers.triangles&&(this.geometry.indices=a.indexBuffers.triangles.data);this.geometry._id=c();this.geometry._version=0}this.setOutputData(0,this.geometry);this.geometry&&this.setOutputData(1,this.geometry.vertices)}},A.registerNodeType("geometry/toGeometry",D),B.title="Geo to Mesh",B.prototype.updateMesh=function(a){this.mesh||(this.mesh=new GL.Mesh); +g=this.vertices;g&&this.vertices.length==a.vertices.length?g.set(a.vertices):g=this.vertices=new Float32Array(a.vertices);for(f=0;fe||wg&&gl))break}this.geometry.indices=this.indices=new Uint32Array(k)}this.indices&&this.indices.length?(this.geometry.indices=this.indices,this.setOutputData(0,this.geometry)):this.setOutputData(0,null)}};B.registerNodeType("geometry/connectPoints",y);"undefined"!=typeof GL&&(D.title="to geometry",D.desc="converts a mesh to geometry",D.prototype.onExecute= +function(){var a=this.getInputData(0);if(a){if(a!=this.last_mesh){this.last_mesh=a;for(i in a.vertexBuffers)this.geometry[i]=a.vertexBuffers[i].data;a.indexBuffers.triangles&&(this.geometry.indices=a.indexBuffers.triangles.data);this.geometry._id=c();this.geometry._version=0}this.setOutputData(0,this.geometry);this.geometry&&this.setOutputData(1,this.geometry.vertices)}},B.registerNodeType("geometry/toGeometry",D),A.title="Geo to Mesh",A.prototype.updateMesh=function(a){this.mesh||(this.mesh=new GL.Mesh); for(var d in a)if("_"!=d[0]){var c=a[d],f=GL.Mesh.common_buffers[d];if(f||"indices"==d){var f=f?f.spacing:3,e=this.mesh.vertexBuffers[d];e&&e.data.length==c.length?(e.data.set(c),e.upload(GL.DYNAMIC_DRAW)):e=new GL.Buffer("indices"==d?GL.ELEMENT_ARRAY_BUFFER:GL.ARRAY_BUFFER,c,f,GL.DYNAMIC_DRAW);this.mesh.addBuffer(d,e)}}if(this.mesh.vertexBuffers.normals&&this.mesh.vertexBuffers.normals.data.length!=this.mesh.vertexBuffers.vertices.data.length){c=new Float32Array([0,1,0]);f=new Float32Array(this.mesh.vertexBuffers.vertices.data.length); -for(d=0;d=c.NOTEON||g<=c.NOTEOFF)this.channel=e&15};Object.defineProperty(c.prototype,"velocity",{get:function(){return this.cmd==c.NOTEON?this.data[2]:-1},set:function(c){this.data[2]=c},enumerable:!0});c.notes="A A# B C C# D D# E F F# G G#".split(" ");c.note_to_index={A:0,"A#":1,B:2,C:3, "C#":4,D:5,"D#":6,E:7,F:8,"F#":9,G:10,"G#":11};Object.defineProperty(c.prototype,"note",{get:function(){return this.cmd!=c.NOTEON?-1:c.toNoteString(this.data[1],!0)},set:function(c){throw"notes cannot be assigned this way, must modify the data[1]";},enumerable:!0});Object.defineProperty(c.prototype,"octave",{get:function(){return this.cmd!=c.NOTEON?-1:Math.floor((this.data[1]-24)/12+1)},set:function(c){throw"octave cannot be assigned this way, must modify the data[1]";},enumerable:!0});c.prototype.getPitch= function(){return 440*Math.pow(2,(this.data[1]-69)/12)};c.computePitch=function(c){return 440*Math.pow(2,(c-69)/12)};c.prototype.getCC=function(){return this.data[1]};c.prototype.getCCValue=function(){return this.data[2]};c.prototype.getPitchBend=function(){return this.data[1]+(this.data[2]<<7)-8192};c.computePitchBend=function(c,e){return c+(e<<7)-8192};c.prototype.setCommandFromString=function(e){this.cmd=c.computeCommandFromString(e)};c.computeCommandFromString=function(e){if(!e)return 0;if(e&& e.constructor===Number)return e;e=e.toUpperCase();switch(e){case "NOTE ON":case "NOTEON":return c.NOTEON;case "NOTE OFF":case "NOTEOFF":return c.NOTEON;case "KEY PRESSURE":case "KEYPRESSURE":return c.KEYPRESSURE;case "CONTROLLER CHANGE":case "CONTROLLERCHANGE":case "CC":return c.CONTROLLERCHANGE;case "PROGRAM CHANGE":case "PROGRAMCHANGE":case "PC":return c.PROGRAMCHANGE;case "CHANNEL PRESSURE":case "CHANNELPRESSURE":return c.CHANNELPRESSURE;case "PITCH BEND":case "PITCHBEND":return c.PITCHBEND;case "TIME TICK":case "TIMETICK":return c.TIMETICK; -default:return Number(e)}};c.toNoteString=function(e,g){e=Math.round(e);var k,v=Math.floor((e-24)/12+1);k=(e-21)%12;0>k&&(k=12+k);return c.notes[k]+(g?"":v)};c.NoteStringToPitch=function(e){e=e.toUpperCase();var g=e[0],k=4;"#"==e[1]?(g+="#",2k&&(k=12+k);return c.notes[k]+(g?"":x)};c.NoteStringToPitch=function(e){e=e.toUpperCase();var g=e[0],k=4;"#"==e[1]?(g+="#",2this.properties.max_value)return;this.trigger("on_midi",g)}};s.registerNodeType("midi/filter",e);C.title="MIDIEvent";C.desc="Create a MIDI Event";C.color="#243";C.prototype.onAction= +g.channel,this.properties.cmd=g.cmd,this.properties.min_value=this.properties.max_value=g.data[1];else if(-1!=this.properties.channel&&g.channel!=this.properties.channel||-1!=this.properties.cmd&&g.cmd!=this.properties.cmd||-1!=this.properties.min_value&&g.data[1]this.properties.max_value)return;this.trigger("on_midi",g)}};t.registerNodeType("midi/filter",e);E.title="MIDIEvent";E.desc="Create a MIDI Event";E.color="#243";E.prototype.onAction= function(e,g){"assign"==e?(this.properties.channel=g.channel,this.properties.cmd=g.cmd,this.properties.value1=g.data[1],this.properties.value2=g.data[2],g.cmd==c.NOTEON?this.gate=!0:g.cmd==c.NOTEOFF&&(this.gate=!1)):(g=this.midi_event,g.channel=this.properties.channel,this.properties.cmd&&this.properties.cmd.constructor===String?g.setCommandFromString(this.properties.cmd):g.cmd=this.properties.cmd,g.data[0]=g.cmd|g.channel,g.data[1]=Number(this.properties.value1),g.data[2]=Number(this.properties.value2), -this.trigger("on_midi",g))};C.prototype.onExecute=function(){var e=this.properties;if(this.inputs)for(var g=0;gc;++c)this.valid_notes[c]=-1!=this.notes_pitches.indexOf(c);for(c=0;12>c;++c)if(this.valid_notes[c])this.offset_notes[c]=0;else for(var e= -1;12>e;++e){if(this.valid_notes[(c-e)%12]){this.offset_notes[c]=-e;break}if(this.valid_notes[(c+e)%12]){this.offset_notes[c]=e;break}}};B.prototype.onAction=function(e,g){g&&g.constructor===c&&(g.data[0]==c.NOTEON||g.data[0]==c.NOTEOFF?(this.midi_event=new c,this.midi_event.setup(g.data),this.midi_event.data[1]+=this.offset_notes[c.note_to_index[g.note]],this.trigger("out",this.midi_event)):this.trigger("out",g))};B.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&c!=this._current_scale&& -this.processScale(c)};s.registerNodeType("midi/quantize",B);g.title="MIDI Play";g.desc="Plays a MIDI note";g.color="#243";g.prototype.onAction=function(e,g){if(g&&g.constructor===c){if(this.instrument&&g.data[0]==c.NOTEON){var k=g.note;if(!k||"undefined"==k||k.constructor!==String)return;this.instrument.play(k,g.octave,this.properties.duration,this.properties.volume)}this.trigger("note",g)}};g.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&(this.properties.volume=c);c=this.getInputData(2); -null!=c&&(this.properties.duration=c)};s.registerNodeType("midi/play",g);m.title="MIDI Keys";m.desc="Keyboard to play notes";m.color="#243";m.keys=[{x:0,w:1,h:1,t:0},{x:0.75,w:0.5,h:0.6,t:1},{x:1,w:1,h:1,t:0},{x:1.75,w:0.5,h:0.6,t:1},{x:2,w:1,h:1,t:0},{x:2.75,w:0.5,h:0.6,t:1},{x:3,w:1,h:1,t:0},{x:4,w:1,h:1,t:0},{x:4.75,w:0.5,h:0.6,t:1},{x:5,w:1,h:1,t:0},{x:5.75,w:0.5,h:0.6,t:1},{x:6,w:1,h:1,t:0}];m.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){var e=12*this.properties.num_octaves; -this.keys.length=e;var g=this.size[0]/(7*this.properties.num_octaves),k=this.size[1];c.globalAlpha=1;for(var a=0;2>a;a++)for(var b=0;bd+h||c[1]>b))return a}}return-1};m.prototype.onAction=function(e,g){if("reset"==e)for(var k=0;kg[1])){var k=this.getKeyIndex(g);this.keys[k]=!0;this._last_key= -k;var k=12*(this.properties.start_octave-1)+29+k,v=new c;v.setup([c.NOTEON,k,100]);this.trigger("note",v);return!0}};m.prototype.onMouseMove=function(e,g){if(!(0>g[1]||-1==this._last_key)){this.setDirtyCanvas(!0);var k=this.getKeyIndex(g);if(this._last_key==k)return!0;this.keys[this._last_key]=!1;var v=12*(this.properties.start_octave-1)+29+this._last_key,a=new c;a.setup([c.NOTEOFF,v,100]);this.trigger("note",a);this.keys[k]=!0;v=12*(this.properties.start_octave-1)+29+k;a=new c;a.setup([c.NOTEON, -v,100]);this.trigger("note",a);this._last_key=k;return!0}};m.prototype.onMouseUp=function(e,g){if(!(0>g[1])){var k=this.getKeyIndex(g);this.keys[k]=!1;this._last_key=-1;var k=12*(this.properties.start_octave-1)+29+k,v=new c;v.setup([c.NOTEOFF,k,100]);this.trigger("note",v);return!0}};s.registerNodeType("midi/keys",m)})(this); -(function(y){function c(){this.properties={src:"",gain:0.5,loop:!0,autoplay:!0,playbackRate:1};this._loading_audio=!1;this._audiobuffer=null;this._audionodes=[];this._last_sourcenode=null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=x.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain;this.properties.src&&this.loadSound(this.properties.src)}function l(){this.properties={gain:0.5};this._audionodes=[];this._media_stream= -null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=x.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain}function p(){this.properties={fftSize:2048,minDecibels:-100,maxDecibels:-10,smoothingTimeConstant:0.5};this.audionode=x.getAudioContext().createAnalyser();this.audionode.graphnode=this;this.audionode.fftSize=this.properties.fftSize;this.audionode.minDecibels=this.properties.minDecibels;this.audionode.maxDecibels= -this.properties.maxDecibels;this.audionode.smoothingTimeConstant=this.properties.smoothingTimeConstant;this.addInput("in","audio");this.addOutput("freqs","array");this.addOutput("samples","array");this._time_bin=this._freq_bin=null}function k(){this.properties={gain:1};this.audionode=x.getAudioContext().createGain();this.addInput("in","audio");this.addInput("gain","number");this.addOutput("out","audio")}function w(){this.properties={impulse_src:"",normalize:!0};this.audionode=x.getAudioContext().createConvolver(); -this.addInput("in","audio");this.addOutput("out","audio")}function e(){this.properties={threshold:-50,knee:40,ratio:12,reduction:-20,attack:0,release:0.25};this.audionode=x.getAudioContext().createDynamicsCompressor();this.addInput("in","audio");this.addOutput("out","audio")}function C(){this.properties={};this.audionode=x.getAudioContext().createWaveShaper();this.addInput("in","audio");this.addInput("shape","waveshape");this.addOutput("out","audio")}function z(){this.properties={gain1:0.5,gain2:0.5}; -this.audionode=x.getAudioContext().createGain();this.audionode1=x.getAudioContext().createGain();this.audionode1.gain.value=this.properties.gain1;this.audionode2=x.getAudioContext().createGain();this.audionode2.gain.value=this.properties.gain2;this.audionode1.connect(this.audionode);this.audionode2.connect(this.audionode);this.addInput("in1","audio");this.addInput("in1 gain","number");this.addInput("in2","audio");this.addInput("in2 gain","number");this.addOutput("out","audio")}function u(){this.properties= -{A:0.1,D:0.1,S:0.1,R:0.1};this.audionode=x.getAudioContext().createGain();this.audionode.gain.value=0;this.addInput("in","audio");this.addInput("gate","bool");this.addOutput("out","audio");this.gate=!1}function D(){this.properties={delayTime:0.5};this.audionode=x.getAudioContext().createDelay(10);this.audionode.delayTime.value=this.properties.delayTime;this.addInput("in","audio");this.addInput("time","number");this.addOutput("out","audio")}function B(){this.properties={frequency:350,detune:0,Q:1}; -this.addProperty("type","lowpass","enum",{values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")});this.audionode=x.getAudioContext().createBiquadFilter();this.addInput("in","audio");this.addOutput("out","audio")}function g(){this.properties={frequency:440,detune:0,type:"sine"};this.addProperty("type","sine","enum",{values:["sine","square","sawtooth","triangle","custom"]});this.audionode=x.getAudioContext().createOscillator();this.addOutput("out","audio")}function m(){this.properties= -{continuous:!0,mark:-1};this.addInput("data","array");this.addInput("mark","number");this.size=[300,200];this._last_buffer=null}function s(){this.properties={band:440,amplitude:1};this.addInput("freqs","array");this.addOutput("signal","number")}function A(){if(!A.default_code){var c=A.default_function.toString(),a=c.indexOf("{")+1,b=c.lastIndexOf("}");A.default_code=c.substr(a,b-a)}this.properties={code:A.default_code};c=x.getAudioContext();c.createScriptProcessor?this.audionode=c.createScriptProcessor(4096, -1,1):(console.warn("ScriptProcessorNode deprecated"),this.audionode=c.createGain());this.processCode();A._bypass_function||(A._bypass_function=this.audionode.onaudioprocess);this.addInput("in","audio");this.addOutput("out","audio")}function r(){this.audionode=x.getAudioContext().destination;this.addInput("in","audio")}var n=y.LiteGraph,x={};y.LGAudio=x;x.getAudioContext=function(){if(!this._audio_context){window.AudioContext=window.AudioContext||window.webkitAudioContext;if(!window.AudioContext)return console.error("AudioContext not supported by browser"), -null;this._audio_context=new AudioContext;this._audio_context.onmessage=function(c){console.log("msg",c)};this._audio_context.onended=function(c){console.log("ended",c)};this._audio_context.oncomplete=function(c){console.log("complete",c)}}return this._audio_context};x.connect=function(c,a){try{c.connect(a)}catch(b){console.warn("LGraphAudio:",b)}};x.disconnect=function(c,a){try{c.disconnect(a)}catch(b){console.warn("LGraphAudio:",b)}};x.changeAllAudiosConnections=function(c,a){if(c.inputs)for(var b= -0;bc;++c)this.valid_notes[c]=-1!=this.notes_pitches.indexOf(c);for(c=0;12>c;++c)if(this.valid_notes[c])this.offset_notes[c]=0;else for(var e= +1;12>e;++e){if(this.valid_notes[(c-e)%12]){this.offset_notes[c]=-e;break}if(this.valid_notes[(c+e)%12]){this.offset_notes[c]=e;break}}};A.prototype.onAction=function(e,g){g&&g.constructor===c&&(g.data[0]==c.NOTEON||g.data[0]==c.NOTEOFF?(this.midi_event=new c,this.midi_event.setup(g.data),this.midi_event.data[1]+=this.offset_notes[c.note_to_index[g.note]],this.trigger("out",this.midi_event)):this.trigger("out",g))};A.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&c!=this._current_scale&& +this.processScale(c)};t.registerNodeType("midi/quantize",A);g.title="MIDI Play";g.desc="Plays a MIDI note";g.color="#243";g.prototype.onAction=function(e,g){if(g&&g.constructor===c){if(this.instrument&&g.data[0]==c.NOTEON){var k=g.note;if(!k||"undefined"==k||k.constructor!==String)return;this.instrument.play(k,g.octave,this.properties.duration,this.properties.volume)}this.trigger("note",g)}};g.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&(this.properties.volume=c);c=this.getInputData(2); +null!=c&&(this.properties.duration=c)};t.registerNodeType("midi/play",g);n.title="MIDI Keys";n.desc="Keyboard to play notes";n.color="#243";n.keys=[{x:0,w:1,h:1,t:0},{x:0.75,w:0.5,h:0.6,t:1},{x:1,w:1,h:1,t:0},{x:1.75,w:0.5,h:0.6,t:1},{x:2,w:1,h:1,t:0},{x:2.75,w:0.5,h:0.6,t:1},{x:3,w:1,h:1,t:0},{x:4,w:1,h:1,t:0},{x:4.75,w:0.5,h:0.6,t:1},{x:5,w:1,h:1,t:0},{x:5.75,w:0.5,h:0.6,t:1},{x:6,w:1,h:1,t:0}];n.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){var e=12*this.properties.num_octaves; +this.keys.length=e;var g=this.size[0]/(7*this.properties.num_octaves),k=this.size[1];c.globalAlpha=1;for(var a=0;2>a;a++)for(var b=0;bd+h||c[1]>b))return a}}return-1};n.prototype.onAction=function(e,g){if("reset"==e)for(var k=0;kg[1])){var k=this.getKeyIndex(g);this.keys[k]=!0;this._last_key= +k;var k=12*(this.properties.start_octave-1)+29+k,x=new c;x.setup([c.NOTEON,k,100]);this.trigger("note",x);return!0}};n.prototype.onMouseMove=function(e,g){if(!(0>g[1]||-1==this._last_key)){this.setDirtyCanvas(!0);var k=this.getKeyIndex(g);if(this._last_key==k)return!0;this.keys[this._last_key]=!1;var x=12*(this.properties.start_octave-1)+29+this._last_key,a=new c;a.setup([c.NOTEOFF,x,100]);this.trigger("note",a);this.keys[k]=!0;x=12*(this.properties.start_octave-1)+29+k;a=new c;a.setup([c.NOTEON, +x,100]);this.trigger("note",a);this._last_key=k;return!0}};n.prototype.onMouseUp=function(e,g){if(!(0>g[1])){var k=this.getKeyIndex(g);this.keys[k]=!1;this._last_key=-1;var k=12*(this.properties.start_octave-1)+29+k,x=new c;x.setup([c.NOTEOFF,k,100]);this.trigger("note",x);return!0}};t.registerNodeType("midi/keys",n)})(this); +(function(C){function c(){this.properties={src:"",gain:0.5,loop:!0,autoplay:!0,playbackRate:1};this._loading_audio=!1;this._audiobuffer=null;this._audionodes=[];this._last_sourcenode=null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=w.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain;this.properties.src&&this.loadSound(this.properties.src)}function m(){this.properties={gain:0.5};this._audionodes=[];this._media_stream= +null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=w.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain}function p(){this.properties={fftSize:2048,minDecibels:-100,maxDecibels:-10,smoothingTimeConstant:0.5};this.audionode=w.getAudioContext().createAnalyser();this.audionode.graphnode=this;this.audionode.fftSize=this.properties.fftSize;this.audionode.minDecibels=this.properties.minDecibels;this.audionode.maxDecibels= +this.properties.maxDecibels;this.audionode.smoothingTimeConstant=this.properties.smoothingTimeConstant;this.addInput("in","audio");this.addOutput("freqs","array");this.addOutput("samples","array");this._time_bin=this._freq_bin=null}function k(){this.properties={gain:1};this.audionode=w.getAudioContext().createGain();this.addInput("in","audio");this.addInput("gain","number");this.addOutput("out","audio")}function v(){this.properties={impulse_src:"",normalize:!0};this.audionode=w.getAudioContext().createConvolver(); +this.addInput("in","audio");this.addOutput("out","audio")}function e(){this.properties={threshold:-50,knee:40,ratio:12,reduction:-20,attack:0,release:0.25};this.audionode=w.getAudioContext().createDynamicsCompressor();this.addInput("in","audio");this.addOutput("out","audio")}function E(){this.properties={};this.audionode=w.getAudioContext().createWaveShaper();this.addInput("in","audio");this.addInput("shape","waveshape");this.addOutput("out","audio")}function z(){this.properties={gain1:0.5,gain2:0.5}; +this.audionode=w.getAudioContext().createGain();this.audionode1=w.getAudioContext().createGain();this.audionode1.gain.value=this.properties.gain1;this.audionode2=w.getAudioContext().createGain();this.audionode2.gain.value=this.properties.gain2;this.audionode1.connect(this.audionode);this.audionode2.connect(this.audionode);this.addInput("in1","audio");this.addInput("in1 gain","number");this.addInput("in2","audio");this.addInput("in2 gain","number");this.addOutput("out","audio")}function y(){this.properties= +{A:0.1,D:0.1,S:0.1,R:0.1};this.audionode=w.getAudioContext().createGain();this.audionode.gain.value=0;this.addInput("in","audio");this.addInput("gate","bool");this.addOutput("out","audio");this.gate=!1}function D(){this.properties={delayTime:0.5};this.audionode=w.getAudioContext().createDelay(10);this.audionode.delayTime.value=this.properties.delayTime;this.addInput("in","audio");this.addInput("time","number");this.addOutput("out","audio")}function A(){this.properties={frequency:350,detune:0,Q:1}; +this.addProperty("type","lowpass","enum",{values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")});this.audionode=w.getAudioContext().createBiquadFilter();this.addInput("in","audio");this.addOutput("out","audio")}function g(){this.properties={frequency:440,detune:0,type:"sine"};this.addProperty("type","sine","enum",{values:["sine","square","sawtooth","triangle","custom"]});this.audionode=w.getAudioContext().createOscillator();this.addOutput("out","audio")}function n(){this.properties= +{continuous:!0,mark:-1};this.addInput("data","array");this.addInput("mark","number");this.size=[300,200];this._last_buffer=null}function t(){this.properties={band:440,amplitude:1};this.addInput("freqs","array");this.addOutput("signal","number")}function B(){if(!B.default_code){var c=B.default_function.toString(),a=c.indexOf("{")+1,b=c.lastIndexOf("}");B.default_code=c.substr(a,b-a)}this.properties={code:B.default_code};c=w.getAudioContext();c.createScriptProcessor?this.audionode=c.createScriptProcessor(4096, +1,1):(console.warn("ScriptProcessorNode deprecated"),this.audionode=c.createGain());this.processCode();B._bypass_function||(B._bypass_function=this.audionode.onaudioprocess);this.addInput("in","audio");this.addOutput("out","audio")}function l(){this.audionode=w.getAudioContext().destination;this.addInput("in","audio")}var q=C.LiteGraph,w={};C.LGAudio=w;w.getAudioContext=function(){if(!this._audio_context){window.AudioContext=window.AudioContext||window.webkitAudioContext;if(!window.AudioContext)return console.error("AudioContext not supported by browser"), +null;this._audio_context=new AudioContext;this._audio_context.onmessage=function(c){console.log("msg",c)};this._audio_context.onended=function(c){console.log("ended",c)};this._audio_context.oncomplete=function(c){console.log("complete",c)}}return this._audio_context};w.connect=function(c,a){try{c.connect(a)}catch(b){console.warn("LGraphAudio:",b)}};w.disconnect=function(c,a){try{c.disconnect(a)}catch(b){console.warn("LGraphAudio:",b)}};w.changeAllAudiosConnections=function(c,a){if(c.inputs)for(var b= +0;b=this.size[0]&&(e=this.size[0]-1),c.strokeStyle="red",c.beginPath(),c.moveTo(e,d),c.lineTo(e,0),c.stroke())}};m.title="Visualization";m.desc="Audio Visualization";n.registerNodeType("audio/visualization",m);s.prototype.onExecute=function(){if(this._freqs=this.getInputData(0)){var c=this.properties.band,a=this.getInputData(1); -void 0!==a&&(c=a);a=x.getAudioContext().sampleRate/this._freqs.length;a=c/a*2;a>=this._freqs.length?a=this._freqs[this._freqs.length-1]:(c=a|0,a-=c,a=this._freqs[c]*(1-a)+this._freqs[c+1]*a);this.setOutputData(0,a/255*this.properties.amplitude)}};s.prototype.onGetInputs=function(){return[["band","number"]]};s.title="Signal";s.desc="extract the signal of some frequency";n.registerNodeType("audio/signal",s);A.prototype.onAdded=function(c){c.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess= -this._callback)};A["@code"]={widget:"code",type:"code"};A.prototype.onStart=function(){this.audionode.onaudioprocess=this._callback};A.prototype.onStop=function(){this.audionode.onaudioprocess=A._bypass_function};A.prototype.onPause=function(){this.audionode.onaudioprocess=A._bypass_function};A.prototype.onUnpause=function(){this.audionode.onaudioprocess=this._callback};A.prototype.onExecute=function(){};A.prototype.onRemoved=function(){this.audionode.onaudioprocess=A._bypass_function};A.prototype.processCode= -function(){try{this._script=new new Function("properties",this.properties.code)(this.properties),this._old_code=this.properties.code,this._callback=this._script.onaudioprocess}catch(c){console.error("Error in onaudioprocess code",c),this._callback=A._bypass_function,this.audionode.onaudioprocess=this._callback}};A.prototype.onPropertyChanged=function(c,a){"code"==c&&(this.properties.code=a,this.processCode(),this.graph&&this.graph.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback))}; -A.default_function=function(){this.onaudioprocess=function(c){var a=c.inputBuffer;c=c.outputBuffer;for(var b=0;b=this.size[0]&&(e=this.size[0]-1),c.strokeStyle="red",c.beginPath(),c.moveTo(e,d),c.lineTo(e,0),c.stroke())}};n.title="Visualization";n.desc="Audio Visualization";q.registerNodeType("audio/visualization",n);t.prototype.onExecute=function(){if(this._freqs=this.getInputData(0)){var c=this.properties.band,a=this.getInputData(1); +void 0!==a&&(c=a);a=w.getAudioContext().sampleRate/this._freqs.length;a=c/a*2;a>=this._freqs.length?a=this._freqs[this._freqs.length-1]:(c=a|0,a-=c,a=this._freqs[c]*(1-a)+this._freqs[c+1]*a);this.setOutputData(0,a/255*this.properties.amplitude)}};t.prototype.onGetInputs=function(){return[["band","number"]]};t.title="Signal";t.desc="extract the signal of some frequency";q.registerNodeType("audio/signal",t);B.prototype.onAdded=function(c){c.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess= +this._callback)};B["@code"]={widget:"code",type:"code"};B.prototype.onStart=function(){this.audionode.onaudioprocess=this._callback};B.prototype.onStop=function(){this.audionode.onaudioprocess=B._bypass_function};B.prototype.onPause=function(){this.audionode.onaudioprocess=B._bypass_function};B.prototype.onUnpause=function(){this.audionode.onaudioprocess=this._callback};B.prototype.onExecute=function(){};B.prototype.onRemoved=function(){this.audionode.onaudioprocess=B._bypass_function};B.prototype.processCode= +function(){try{this._script=new new Function("properties",this.properties.code)(this.properties),this._old_code=this.properties.code,this._callback=this._script.onaudioprocess}catch(c){console.error("Error in onaudioprocess code",c),this._callback=B._bypass_function,this.audionode.onaudioprocess=this._callback}};B.prototype.onPropertyChanged=function(c,a){"code"==c&&(this.properties.code=a,this.processCode(),this.graph&&this.graph.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback))}; +B.default_function=function(){this.onaudioprocess=function(c){var a=c.inputBuffer;c=c.outputBuffer;for(var b=0;b Date: Wed, 15 Apr 2020 11:41:43 +0300 Subject: [PATCH 17/63] Fixes type definition for renderInfo() --- src/litegraph.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/litegraph.d.ts b/src/litegraph.d.ts index 551601a11..fecd6e201 100644 --- a/src/litegraph.d.ts +++ b/src/litegraph.d.ts @@ -1239,7 +1239,7 @@ export declare class LGraphCanvas { /** draws the front canvas (the one containing all the nodes) */ drawFrontCanvas(): void; /** draws some useful stats in the corner of the canvas */ - renderInfo(): void; + renderInfo(ctx: CanvasRenderingContext2D, x: number, y: nymber): void; /** draws the back canvas (the one containing the background and the connections) */ drawBackCanvas(): void; /** draws the given node inside the canvas */ From 6607593471265e50b004662c4187c9b6c401b576 Mon Sep 17 00:00:00 2001 From: Shan M Date: Wed, 15 Apr 2020 11:45:25 +0300 Subject: [PATCH 18/63] fix typo --- src/litegraph.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/litegraph.d.ts b/src/litegraph.d.ts index fecd6e201..70fd06e0a 100644 --- a/src/litegraph.d.ts +++ b/src/litegraph.d.ts @@ -1239,7 +1239,7 @@ export declare class LGraphCanvas { /** draws the front canvas (the one containing all the nodes) */ drawFrontCanvas(): void; /** draws some useful stats in the corner of the canvas */ - renderInfo(ctx: CanvasRenderingContext2D, x: number, y: nymber): void; + renderInfo(ctx: CanvasRenderingContext2D, x: number, y: number): void; /** draws the back canvas (the one containing the background and the connections) */ drawBackCanvas(): void; /** draws the given node inside the canvas */ From df5a0420997c2eeeaee43ccf30ab902f54fe6800 Mon Sep 17 00:00:00 2001 From: Shan M Date: Mon, 20 Apr 2020 16:50:53 +0300 Subject: [PATCH 19/63] nodes attribute in selectNodes function is optional --- src/litegraph.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/litegraph.d.ts b/src/litegraph.d.ts index d1161672c..69220a8df 100644 --- a/src/litegraph.d.ts +++ b/src/litegraph.d.ts @@ -1216,7 +1216,7 @@ export declare class LGraphCanvas { /** selects a given node (or adds it to the current selection) */ selectNode(node: LGraphNode, add?: boolean): void; /** selects several nodes (or adds them to the current selection) */ - selectNodes(nodes: LGraphNode[], add?: boolean): void; + selectNodes(nodes?: LGraphNode[], add?: boolean): void; /** removes a node from the current selection */ deselectNode(node: LGraphNode): void; /** removes all nodes from the current selection */ From 78e55db0b1f30717edb8a89fd542a52d618e9789 Mon Sep 17 00:00:00 2001 From: tamat Date: Tue, 21 Apr 2020 12:35:02 +0200 Subject: [PATCH 20/63] fix in widgets --- build/litegraph.js | 263 ++++++- build/litegraph.min.js | 1340 ++++++++++++++++++----------------- css/litegraph-editor.css | 15 + demo/index.html | 2 + demo/js/libs/midi-parser.js | 356 ++++++++++ src/litegraph-editor.js | 105 +-- src/litegraph.js | 87 ++- src/nodes/base.js | 9 + src/nodes/math.js | 19 +- src/nodes/math3d.js | 15 + src/nodes/midi.js | 133 +++- 11 files changed, 1593 insertions(+), 751 deletions(-) create mode 100644 demo/js/libs/midi-parser.js diff --git a/build/litegraph.js b/build/litegraph.js index 743dbef0f..6116c5c4b 100644 --- a/build/litegraph.js +++ b/build/litegraph.js @@ -394,7 +394,6 @@ * @method getNodeTypesCategories * @return {Array} array with all the names of the categories */ - getNodeTypesCategories: function( filter ) { var categories = { "": 1 }; for (var i in this.registered_node_types) { @@ -474,6 +473,13 @@ return target; }, + /** + * Returns if the types of two slots are compatible (taking into account wildcards, etc) + * @method isValidConnection + * @param {String} type_a + * @param {String} type_b + * @return {Boolean} true if they can be connected + */ isValidConnection: function(type_a, type_b) { if ( !type_a || //generic output @@ -509,13 +515,85 @@ return false; }, + /** + * Register a string in the search box so when the user types it it will recommend this node + * @method registerSearchboxExtra + * @param {String} node_type the node recommended + * @param {String} description text to show next to it + * @param {Object} data it could contain info of how the node should be configured + * @return {Boolean} true if they can be connected + */ registerSearchboxExtra: function(node_type, description, data) { this.searchbox_extras[description.toLowerCase()] = { type: node_type, desc: description, data: data }; - } + }, + + /** + * Wrapper to load files (from url using fetch or from file using FileReader) + * @method fetchFile + * @param {String|File|Blob} url the url of the file (or the file itself) + * @param {String} type an string to know how to fetch it: "text","arraybuffer","json","blob" + * @param {Function} on_complete callback(data) + * @param {Function} on_error in case of an error + * @return {FileReader|Promise} returns the object used to + */ + fetchFile: function( url, type, on_complete, on_error ) { + var that = this; + if(!url) + return null; + + type = type || "text"; + if( url.constructor === String ) + { + if (url.substr(0, 4) == "http" && LiteGraph.proxy) { + url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); + } + return fetch(url) + .then(function(response) { + if(!response.ok) + throw new Error("File not found"); //it will be catch below + if(type == "arraybuffer") + return response.arrayBuffer(); + else if(type == "text" || type == "string") + return response.text(); + else if(type == "json") + return response.json(); + else if(type == "blob") + return response.blob(); + }) + .then(function(data) { + if(on_complete) + on_complete(data); + }) + .catch(function(error) { + console.error("error fetching file:",url); + if(on_error) + on_error(error); + }); + } + else if( url.constructor === File || url.constructor === Blob) + { + var reader = new FileReader(); + reader.onload = function(e) + { + var v = e.target.result; + if( type == "json" ) + v = JSON.parse(v); + if(on_complete) + on_complete(v); + } + if(type == "arraybuffer") + return reader.readAsArrayBuffer(url); + else if(type == "text" || type == "json") + return reader.readAsText(url); + else if(type == "blob") + return reader.readAsBinaryString(url); + } + return null; + } }); //timer that works everywhere @@ -8415,7 +8493,10 @@ LGraphNode.prototype.executeAction = function(action) if (values && values.constructor === Function) { values = w.options.values(w, node); } - var values_list = values.constructor === Array ? values : Object.keys(values); + var values_list = null; + + if( w.type != "number") + values_list = values.constructor === Array ? values : Object.keys(values); var delta = x < 40 ? -1 : x > width - 40 ? 1 : 0; if (w.type == "number") { @@ -11481,6 +11562,15 @@ if (typeof exports != "undefined") { ConstantBoolean.prototype.setValue = ConstantNumber.prototype.setValue; + ConstantBoolean.prototype.onGetInputs = function() { + return [["toggle", LiteGraph.ACTION]]; + }; + + ConstantBoolean.prototype.onAction = function(action) + { + this.setValue( !this.properties.value ); + } + LiteGraph.registerNodeType("basic/boolean", ConstantBoolean); function ConstantString() { @@ -13751,6 +13841,7 @@ if (typeof exports != "undefined") { function MathRange() { this.addInput("in", "number", { locked: true }); this.addOutput("out", "number", { locked: true }); + this.addOutput("clamped", "number", { locked: true }); this.addProperty("in", 0); this.addProperty("in_min", 0); @@ -13758,7 +13849,7 @@ if (typeof exports != "undefined") { this.addProperty("out_min", 0); this.addProperty("out_max", 1); - this.size = [80, 30]; + this.size = [120, 50]; } MathRange.title = "Range"; @@ -13792,10 +13883,22 @@ if (typeof exports != "undefined") { var in_max = this.properties.in_max; var out_min = this.properties.out_min; var out_max = this.properties.out_max; + /* + if( in_min > in_max ) + { + in_min = in_max; + in_max = this.properties.in_min; + } + if( out_min > out_max ) + { + out_min = out_max; + out_max = this.properties.out_min; + } + */ - this._last_v = - ((v - in_min) / (in_max - in_min)) * (out_max - out_min) + out_min; + this._last_v = ((v - in_min) / (in_max - in_min)) * (out_max - out_min) + out_min; this.setOutputData(0, this._last_v); + this.setOutputData(1, Math.clamp( this._last_v, out_min, out_max )); }; MathRange.prototype.onDrawBackground = function(ctx) { @@ -15356,6 +15459,21 @@ if (typeof exports != "undefined") { var target_min = this.properties.target_min; var target_max = this.properties.target_max; + //swap to avoid errors + /* + if(range_min > range_max) + { + range_min = range_max; + range_max = this.properties.range_min; + } + + if(target_min > target_max) + { + target_min = target_max; + target_max = this.properties.target_min; + } + */ + for(var i = 0; i < 3; ++i) { var r = range_max[i] - range_min[i]; @@ -25253,6 +25371,24 @@ function LGraphGeometryDisplace() { this.properties.value1 = (v | 0) % 255; } break; + case "cmd": + var v = this.getInputData(i); + if (v != null) { + this.properties.cmd = v; + } + break; + case "value1": + var v = this.getInputData(i); + if (v != null) { + this.properties.value1 = Math.clamp(v|0,0,127); + } + break; + case "value2": + var v = this.getInputData(i); + if (v != null) { + this.properties.value2 = Math.clamp(v|0,0,127); + } + break; } } } @@ -25321,7 +25457,7 @@ function LGraphGeometryDisplace() { }; LGMIDIEvent.prototype.onGetInputs = function() { - return [["note", "number"]]; + return [["cmd", "number"],["note", "number"],["value1", "number"],["value2", "number"]]; }; LGMIDIEvent.prototype.onGetOutputs = function() { @@ -25578,6 +25714,119 @@ function LGraphGeometryDisplace() { LiteGraph.registerNodeType("midi/quantize", LGMIDIQuantize); + function LGMIDIFromFile() { + this.properties = { + url: "", + autoplay: true + }; + + this.addInput("play", LiteGraph.ACTION); + this.addInput("pause", LiteGraph.ACTION); + this.addOutput("note", LiteGraph.EVENT); + this._midi = null; + this._current_time = 0; + this._playing = false; + + if (typeof MidiParser == "undefined") { + console.error( + "midi-parser.js not included, LGMidiPlay requires that library: https://raw.githubusercontent.com/colxi/midi-parser-js/master/src/main.js" + ); + this.boxcolor = "red"; + } + + } + + LGMIDIFromFile.title = "MIDI fromFile"; + LGMIDIFromFile.desc = "Plays a MIDI file"; + LGMIDIFromFile.color = MIDI_COLOR; + + LGMIDIFromFile.prototype.onAction = function( name ) + { + if(name == "play") + this.play(); + else if(name == "pause") + this._playing = !this._playing; + } + + LGMIDIFromFile.prototype.onPropertyChanged = function(name,value) + { + if(name == "url") + this.loadMIDIFile(value); + } + + LGMIDIFromFile.prototype.onExecute = function() { + if(!this._midi) + return; + + if(!this._playing) + return; + + this._current_time += this.graph.elapsed_time; + var current_time = this._current_time * 100; + + for(var i = 0; i < this._midi.tracks; ++i) + { + var track = this._midi.track[i]; + if(!track._last_pos) + { + track._last_pos = 0; + track._time = 0; + } + + var elem = track.event[ track._last_pos ]; + if(elem && (track._time + elem.deltaTime) <= current_time ) + { + track._last_pos++; + track._time += elem.deltaTime; + + if(elem.data) + { + var midi_cmd = elem.type << 4 + elem.channel; + var midi_event = new MIDIEvent(); + midi_event.setup([midi_cmd, elem.data[0], elem.data[1]]); + this.trigger("note", midi_event); + } + } + + } + }; + + LGMIDIFromFile.prototype.play = function() + { + this._playing = true; + this._current_time = 0; + for(var i = 0; i < this._midi.tracks; ++i) + { + var track = this._midi.track[i]; + track._last_pos = 0; + track._time = 0; + } + } + + LGMIDIFromFile.prototype.loadMIDIFile = function(url) + { + var that = this; + LiteGraph.fetchFile( url, "arraybuffer", function(data) + { + that.boxcolor = "#AFA"; + that._midi = MidiParser.parse( new Uint8Array(data) ); + if(that.properties.autoplay) + that.play(); + }, function(err){ + that.boxcolor = "#FAA"; + that._midi = null; + }); + } + + LGMIDIFromFile.prototype.onDropFile = function(file) + { + this.properties.url = ""; + this.loadMIDIFile( file ); + } + + LiteGraph.registerNodeType("midi/fromFile", LGMIDIFromFile); + + function LGMIDIPlay() { this.properties = { volume: 0.5, diff --git a/build/litegraph.min.js b/build/litegraph.min.js index d26101b08..cfe4fef25 100755 --- a/build/litegraph.min.js +++ b/build/litegraph.min.js @@ -1,711 +1,717 @@ -(function(C){function c(a){g.debug&&console.log("Graph created");this.list_of_graphcanvas=null;this.clear();a&&this.configure(a)}function m(a,b,d,h,f,s){this.id=a;this.type=b;this.origin_id=d;this.origin_slot=h;this.target_id=f;this.target_slot=s;this._data=null;this._pos=new Float32Array(2)}function p(a){this._ctor(a)}function k(a){this._ctor(a)}function v(a,b){this.offset=new Float32Array([0,0]);this.scale=1;this.max_scale=10;this.min_scale=0.1;this.onredraw=null;this.enabled=!0;this.last_mouse= +(function(B){function c(a){h.debug&&console.log("Graph created");this.list_of_graphcanvas=null;this.clear();a&&this.configure(a)}function m(a,b,d,g,f,r){this.id=a;this.type=b;this.origin_id=d;this.origin_slot=g;this.target_id=f;this.target_slot=r;this._data=null;this._pos=new Float32Array(2)}function n(a){this._ctor(a)}function l(a){this._ctor(a)}function x(a,b){this.offset=new Float32Array([0,0]);this.scale=1;this.max_scale=10;this.min_scale=0.1;this.onredraw=null;this.enabled=!0;this.last_mouse= [0,0];this.element=null;this.visible_area=new Float32Array(4);a&&(this.element=a,b||this.bindEvents(a))}function e(a,b,d){d=d||{};this.background_image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; -a&&a.constructor===String&&(a=document.querySelector(a));this.ds=new v;this.zoom_modify_alpha=!0;this.title_text_font=""+g.NODE_TEXT_SIZE+"px Arial";this.inner_text_font="normal "+g.NODE_SUBTEXT_SIZE+"px Arial";this.node_title_color=g.NODE_TITLE_COLOR;this.default_link_color=g.LINK_COLOR;this.default_connection_color={input_off:"#778",input_on:"#7F7",output_off:"#778",output_on:"#7F7"};this.highquality_render=!0;this.use_gradients=!1;this.editor_alpha=1;this.pause_rendering=!1;this.clear_background= +a&&a.constructor===String&&(a=document.querySelector(a));this.ds=new x;this.zoom_modify_alpha=!0;this.title_text_font=""+h.NODE_TEXT_SIZE+"px Arial";this.inner_text_font="normal "+h.NODE_SUBTEXT_SIZE+"px Arial";this.node_title_color=h.NODE_TITLE_COLOR;this.default_link_color=h.LINK_COLOR;this.default_connection_color={input_off:"#778",input_on:"#7F7",output_off:"#778",output_on:"#7F7"};this.highquality_render=!0;this.use_gradients=!1;this.editor_alpha=1;this.pause_rendering=!1;this.clear_background= !0;this.read_only=!1;this.render_only_selected=!0;this.live_mode=!1;this.allow_searchbox=this.allow_interaction=this.allow_dragnodes=this.allow_dragcanvas=this.show_info=!0;this.drag_mode=this.allow_reconnect_links=!1;this.filter=this.dragging_rectangle=null;this.always_render_background=!1;this.render_canvas_border=this.render_shadows=!0;this.render_connections_shadows=!1;this.render_connections_border=!0;this.render_connection_arrows=this.render_curved_connections=!1;this.render_collapsed_slots= -!0;this.render_execution_order=!1;this.render_link_tooltip=this.render_title_colored=!0;this.links_render_mode=g.SPLINE_LINK;this.canvas_mouse=[0,0];this.onSelectionChange=this.onNodeMoved=this.onDrawLinkTooltip=this.onDrawOverlay=this.onDrawForeground=this.onDrawBackground=this.onMouse=this.onSearchBoxSelection=this.onSearchBox=null;this.connections_width=3;this.round_radius=8;this.over_link_center=this.node_widget=this.current_node=null;this.last_mouse_position=[0,0];this.visible_area=this.ds.visible_area; -this.visible_links=[];b&&b.attachCanvas(this);this.setCanvas(a);this.clear();d.skip_render||this.startRendering();this.autoresize=d.autoresize}function E(a,b){return Math.sqrt((b[0]-a[0])*(b[0]-a[0])+(b[1]-a[1])*(b[1]-a[1]))}function z(a,b,d,h,f,s){return da&&hb?!0:!1}function y(a,b){var d=a[0]+a[2],h=a[1]+a[3],f=b[1]+b[3];return a[0]>b[0]+b[2]||a[1]>f||dc.width-e.width-10&&(f=c.width-e.width-10);g>c.height-e.height-10&&(g=c.height-e.height-10)}s.style.left=f+"px";s.style.top=g+"px";b.scale&&(s.style.transform="scale("+b.scale+")")}function A(a){this.points=a;this.nearest=this.selected=-1;this.size=null;this.must_update=!0;this.margin=5}var g=C.LiteGraph={VERSION:0.4,CANVAS_GRID_SIZE:10,NODE_TITLE_HEIGHT:30,NODE_TITLE_TEXT_Y:20,NODE_SLOT_HEIGHT:20,NODE_WIDGET_HEIGHT:20,NODE_WIDTH:140,NODE_MIN_WIDTH:50,NODE_COLLAPSED_RADIUS:10, +!0;this.render_execution_order=!1;this.render_link_tooltip=this.render_title_colored=!0;this.links_render_mode=h.SPLINE_LINK;this.canvas_mouse=[0,0];this.onSelectionChange=this.onNodeMoved=this.onDrawLinkTooltip=this.onDrawOverlay=this.onDrawForeground=this.onDrawBackground=this.onMouse=this.onSearchBoxSelection=this.onSearchBox=null;this.connections_width=3;this.round_radius=8;this.over_link_center=this.node_widget=this.current_node=null;this.last_mouse_position=[0,0];this.visible_area=this.ds.visible_area; +this.visible_links=[];b&&b.attachCanvas(this);this.setCanvas(a);this.clear();d.skip_render||this.startRendering();this.autoresize=d.autoresize}function C(a,b){return Math.sqrt((b[0]-a[0])*(b[0]-a[0])+(b[1]-a[1])*(b[1]-a[1]))}function w(a,b,d,g,f,r){return da&&gb?!0:!1}function u(a,b){var d=a[0]+a[2],g=a[1]+a[3],f=b[1]+b[3];return a[0]>b[0]+b[2]||a[1]>f||dc.width-k.width-10&&(f=c.width-k.width-10);h>c.height-k.height-10&&(h=c.height-k.height-10)}r.style.left=f+"px";r.style.top=h+"px";b.scale&&(r.style.transform="scale("+b.scale+")")}function A(a){this.points=a;this.nearest=this.selected=-1;this.size=null;this.must_update=!0;this.margin=5}var h=B.LiteGraph={VERSION:0.4,CANVAS_GRID_SIZE:10,NODE_TITLE_HEIGHT:30,NODE_TITLE_TEXT_Y:20,NODE_SLOT_HEIGHT:20,NODE_WIDGET_HEIGHT:20,NODE_WIDTH:140,NODE_MIN_WIDTH:50,NODE_COLLAPSED_RADIUS:10, NODE_COLLAPSED_WIDTH:80,NODE_TITLE_COLOR:"#999",NODE_TEXT_SIZE:14,NODE_TEXT_COLOR:"#AAA",NODE_SUBTEXT_SIZE:12,NODE_DEFAULT_COLOR:"#333",NODE_DEFAULT_BGCOLOR:"#353535",NODE_DEFAULT_BOXCOLOR:"#666",NODE_DEFAULT_SHAPE:"box",DEFAULT_SHADOW_COLOR:"rgba(0,0,0,0.5)",DEFAULT_GROUP_FONT:24,WIDGET_BGCOLOR:"#222",WIDGET_OUTLINE_COLOR:"#666",WIDGET_TEXT_COLOR:"#DDD",WIDGET_SECONDARY_TEXT_COLOR:"#999",LINK_COLOR:"#9A9",EVENT_LINK_COLOR:"#A86",CONNECTING_LINK_COLOR:"#AFA",MAX_NUMBER_OF_NODES:1E3,DEFAULT_POSITION:[100, 100],VALID_SHAPES:["default","box","round","card"],BOX_SHAPE:1,ROUND_SHAPE:2,CIRCLE_SHAPE:3,CARD_SHAPE:4,ARROW_SHAPE:5,INPUT:1,OUTPUT:2,EVENT:-1,ACTION:-1,ALWAYS:0,ON_EVENT:1,NEVER:2,ON_TRIGGER:3,UP:1,DOWN:2,LEFT:3,RIGHT:4,CENTER:5,STRAIGHT_LINK:0,LINEAR_LINK:1,SPLINE_LINK:2,NORMAL_TITLE:0,NO_TITLE:1,TRANSPARENT_TITLE:2,AUTOHIDE_TITLE:3,proxy:null,node_images_path:"",debug:!1,catch_exceptions:!0,throw_errors:!0,allow_scripts:!1,registered_node_types:{},node_types_by_file_extension:{},Nodes:{},searchbox_extras:{}, -registerNodeType:function(a,b){if(!b.prototype)throw"Cannot register a simple object, it must be a class with a prototype";b.type=a;g.debug&&console.log("Node registered: "+a);a.split("/");var d=b.name,h=a.lastIndexOf("/");b.category=a.substr(0,h);b.title||(b.title=d);if(b.prototype)for(var f in p.prototype)b.prototype[f]||(b.prototype[f]=p.prototype[f]);if(h=this.registered_node_types[a])console.log("replacing node type: "+a);else if(Object.hasOwnProperty(b.prototype,"shape")||Object.defineProperty(b.prototype, -"shape",{set:function(a){switch(a){case "default":delete this._shape;break;case "box":this._shape=g.BOX_SHAPE;break;case "round":this._shape=g.ROUND_SHAPE;break;case "circle":this._shape=g.CIRCLE_SHAPE;break;case "card":this._shape=g.CARD_SHAPE;break;default:this._shape=a}},get:function(a){return this._shape},enumerable:!0,configurable:!0}),b.prototype.onPropertyChange&&console.warn("LiteGraph node class "+a+" has onPropertyChange method, it must be called onPropertyChanged with d at the end"),b.supported_extensions)for(f in b.supported_extensions){var s= -b.supported_extensions[f];s&&s.constructor===String&&(this.node_types_by_file_extension[s.toLowerCase()]=b)}this.registered_node_types[a]=b;b.constructor.name&&(this.Nodes[d]=b);if(g.onNodeTypeRegistered)g.onNodeTypeRegistered(a,b);if(h&&g.onNodeTypeReplaced)g.onNodeTypeReplaced(a,b,h)},unregisterNodeType:function(a){var b=a.constructor===String?this.registered_node_types[a]:a;if(!b)throw"node type not found: "+a;delete this.registered_node_types[b.type];b.constructor.name&&delete this.Nodes[b.constructor.name]}, -wrapFunctionAsNode:function(a,b,d,h,f){for(var s=Array(b.length),r="",c=g.getParameterNames(b),e=0;er&&(r=f.size[0]),c+=f.size[1]+a+g.NODE_TITLE_HEIGHT;b+=r+a}this.setDirtyCanvas(!0,!0)};c.prototype.getTime=function(){return this.globaltime}; -c.prototype.getFixedTime=function(){return this.fixedtime};c.prototype.getElapsedTime=function(){return this.elapsed_time};c.prototype.sendEventToAllNodes=function(a,b,d){d=d||g.ALWAYS;var h=this._nodes_in_order?this._nodes_in_order:this._nodes;if(h)for(var f=0,s=h.length;f=g.MAX_NUMBER_OF_NODES)throw"LiteGraph: max number of nodes in a graph reached"; -null==a.id||-1==a.id?a.id=++this.last_node_id:this.last_node_ida.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});this.id=-1;this.type=null;this.inputs=[];this.outputs=[];this.connections=[];this.properties={};this.properties_info=[];this.flags={}};p.prototype.configure=function(a){this.graph&&this.graph._version++; -for(var b in a)if("properties"==b)for(var d in a.properties){if(this.properties[d]=a.properties[d],this.onPropertyChanged)this.onPropertyChanged(d,a.properties[d])}else null!=a[b]&&("object"==typeof a[b]?this[b]&&this[b].configure?this[b].configure(a[b]):this[b]=g.cloneObject(a[b],this[b]):this[b]=a[b]);a.title||(this.title=this.constructor.title);if(this.onConnectionsChange){if(this.inputs)for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d._data=b,this.outputs[a].links))for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d.type=b,this.outputs[a].links))for(d=0;d=this.inputs.length||null==this.inputs[a].link)){var d=this.graph.links[this.inputs[a].link];if(!d)return null;if(!b)return d.data; -var h=this.graph.getNodeById(d.origin_id);if(!h)return d.data;if(h.updateOutputData)h.updateOutputData(d.origin_slot);else if(h.onExecute)h.onExecute();return d.data}};p.prototype.getInputDataType=function(a){if(!this.inputs||a>=this.inputs.length||null==this.inputs[a].link)return null;a=this.graph.links[this.inputs[a].link];if(!a)return null;var b=this.graph.getNodeById(a.origin_id);return b?(a=b.outputs[a.origin_slot])?a.type:null:a.type};p.prototype.getInputDataByName=function(a,b){var d=this.findInputSlot(a); -return-1==d?null:this.getInputData(d,b)};p.prototype.isInputConnected=function(a){return this.inputs?a=this.inputs.length)return null;a=this.inputs[a];return a&&null!==a.link?(a=this.graph.links[a.link])?this.graph.getNodeById(a.origin_id):null:null};p.prototype.getInputOrProperty=function(a){if(!this.inputs|| -!this.inputs.length)return this.properties?this.properties[a]:null;for(var b=0,d=this.inputs.length;b=this.outputs.length?null:this.outputs[a]._data};p.prototype.getOutputInfo=function(a){return this.outputs?a=this.outputs.length)return null;a=this.outputs[a];if(!a.links||0==a.links.length)return null;for(var b=[],d=0;da&&this.pos[1]-f-db)return!0;return!1};p.prototype.getSlotInPosition=function(a,b){var d=new Float32Array(2); -if(this.inputs)for(var h=0,f=this.inputs.length;h=this.outputs.length)return g.debug&&console.log("Connect: Error, slot number not found"),null;b&&b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"target node is null";if(b==this)return null;if(d.constructor===String){if(d=b.findInputSlot(d),-1==d)return g.debug&&console.log("Connect: Error, no slot of name "+d),null}else{if(d===g.EVENT)return null;if(!b.inputs||d>=b.inputs.length)return g.debug&&console.log("Connect: Error, slot number not found"),null}null!=b.inputs[d].link&& -b.disconnectInput(d);var h=this.outputs[a];if(b.onConnectInput&&!1===b.onConnectInput(d,h.type,h))return null;var f=b.inputs[d],s=null;if(g.isValidConnection(h.type,f.type)){s=new m(++this.graph.last_link_id,f.type,this.id,a,b.id,d);this.graph.links[s.id]=s;null==h.links&&(h.links=[]);h.links.push(s.id);b.inputs[d].link=s.id;this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(g.OUTPUT,a,!0,s,h);if(b.onConnectionsChange)b.onConnectionsChange(g.INPUT,d,!0,s,f);this.graph&& -this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(g.INPUT,b,d,this,a),this.graph.onNodeConnectionChange(g.OUTPUT,this,a,b,d))}this.setDirtyCanvas(!1,!0);this.graph.connectionChange(this,s);return s};p.prototype.disconnectOutput=function(a,b){if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return g.debug&&console.log("Connect: Error, no slot of name "+a),!1}else if(!this.outputs||a>=this.outputs.length)return g.debug&&console.log("Connect: Error, slot number not found"), -!1;var d=this.outputs[a];if(!d||!d.links||0==d.links.length)return!1;if(b){b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"Target Node not found";for(var h=0,f=d.links.length;h=this.inputs.length)return g.debug&&console.log("Connect: Error, slot number not found"),!1;var b=this.inputs[a];if(!b)return!1;var d=this.inputs[a].link;this.inputs[a].link=null;var h=this.graph.links[d];if(h){var f=this.graph.getNodeById(h.origin_id);if(!f)return!1;var s=f.outputs[h.origin_slot];if(!s||!s.links||0==s.links.length)return!1;for(var r=0,c=s.links.length;r< -c;r++)if(s.links[r]==d){s.links.splice(r,1);break}delete this.graph.links[d];this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(g.INPUT,a,!1,h,b);if(f.onConnectionsChange)f.onConnectionsChange(g.OUTPUT,r,!1,h,s);this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(g.OUTPUT,f,r),this.graph.onNodeConnectionChange(g.INPUT,this,a))}this.setDirtyCanvas(!1,!0);this.graph.connectionChange(this);return!0};p.prototype.getConnectionPos=function(a, -b,d){d=d||new Float32Array(2);var h=0;a&&this.inputs&&(h=this.inputs.length);!a&&this.outputs&&(h=this.outputs.length);var f=0.5*g.NODE_SLOT_HEIGHT;if(this.flags.collapsed)return b=this._collapsed_width||g.NODE_COLLAPSED_WIDTH,this.horizontal?(d[0]=this.pos[0]+0.5*b,d[1]=a?this.pos[1]-g.NODE_TITLE_HEIGHT:this.pos[1]):(d[0]=a?this.pos[0]:this.pos[0]+b,d[1]=this.pos[1]-0.5*g.NODE_TITLE_HEIGHT),d;if(a&&-1==b)return d[0]=this.pos[0]+0.5*g.NODE_TITLE_HEIGHT,d[1]=this.pos[1]+0.5*g.NODE_TITLE_HEIGHT,d;if(a&& -h>b&&this.inputs[b].pos)return d[0]=this.pos[0]+this.inputs[b].pos[0],d[1]=this.pos[1]+this.inputs[b].pos[1],d;if(!a&&h>b&&this.outputs[b].pos)return d[0]=this.pos[0]+this.outputs[b].pos[0],d[1]=this.pos[1]+this.outputs[b].pos[1],d;if(this.horizontal)return d[0]=this.pos[0]+this.size[0]/h*(b+0.5),d[1]=a?this.pos[1]-g.NODE_TITLE_HEIGHT:this.pos[1]+this.size[1],d;d[0]=a?this.pos[0]+f:this.pos[0]+this.size[0]+1-f;d[1]=this.pos[1]+(b+0.7)*g.NODE_SLOT_HEIGHT+(this.constructor.slot_start_y||0);return d}; -p.prototype.alignToGrid=function(){this.pos[0]=g.CANVAS_GRID_SIZE*Math.round(this.pos[0]/g.CANVAS_GRID_SIZE);this.pos[1]=g.CANVAS_GRID_SIZE*Math.round(this.pos[1]/g.CANVAS_GRID_SIZE)};p.prototype.trace=function(a){this.console||(this.console=[]);this.console.push(a);this.console.length>p.MAX_CONSOLE&&this.console.shift();this.graph.onNodeTrace(this,a)};p.prototype.setDirtyCanvas=function(a,b){this.graph&&this.graph.sendActionToCanvas("setDirty",[a,b])};p.prototype.loadImage=function(a){var b=new Image; -b.src=g.node_images_path+a;b.ready=!1;var d=this;b.onload=function(){this.ready=!0;d.setDirtyCanvas(!0)};return b};p.prototype.captureInput=function(a){if(this.graph&&this.graph.list_of_graphcanvas)for(var b=this.graph.list_of_graphcanvas,d=0;da.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});Object.defineProperty(this,"size",{set:function(a){!a||2>a.length||(this._size[0]=Math.max(140,a[0]),this._size[1]=Math.max(80,a[1]))},get:function(){return this._size},enumerable:!0})};k.prototype.configure=function(a){this.title=a.title;this._bounding.set(a.bounding);this.color=a.color; -this.font=a.font};k.prototype.serialize=function(){var a=this._bounding;return{title:this.title,bounding:[Math.round(a[0]),Math.round(a[1]),Math.round(a[2]),Math.round(a[3])],color:this.color,font:this.font}};k.prototype.move=function(a,b,d){this._pos[0]+=a;this._pos[1]+=b;if(!d)for(d=0;dthis.max_scale&&(a=this.max_scale);if(a!=this.scale&&this.element){var d=this.element.getBoundingClientRect();if(d){b=b||[0.5*d.width,0.5*d.height];d=this.convertCanvasToOffset(b);this.scale=a;0.01>Math.abs(this.scale-1)&&(this.scale=1);var h=this.convertCanvasToOffset(b),d=[h[0]-d[0],h[1]-d[1]];this.offset[0]+=d[0];this.offset[1]+=d[1];if(this.onredraw)this.onredraw(this)}}}; -v.prototype.changeDeltaScale=function(a,b){this.changeScale(this.scale*a,b)};v.prototype.reset=function(){this.scale=1;this.offset[0]=0;this.offset[1]=0};C.LGraphCanvas=g.LGraphCanvas=e;e.link_type_colors={"-1":g.EVENT_LINK_COLOR,number:"#AAA",node:"#DCA"};e.gradients={};e.prototype.clear=function(){this.fps=this.render_time=this.last_draw_time=this.frame=0;this.dragging_rectangle=null;this.selected_nodes={};this.selected_group=null;this.visible_nodes=[];this.connecting_node=this.node_capturing_input= -this.node_over=this.node_dragged=null;this.highlighted_links={};this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_widget=this.node_in_panel=this.dirty_area=null;this.last_mouse=[0,0];this.last_mouseclick=0;this.visible_area.set([0,0,0,0]);if(this.onClear)this.onClear()};e.prototype.setGraph=function(a,b){this.graph!=a&&(b||this.clear(),!a&&this.graph?this.graph.detachCanvas(this):(a.attachCanvas(this),this._graph_stack&&(this._graph_stack=null),this.setDirty(!0,!0)))};e.prototype.openSubgraph=function(a){if(!a)throw"graph cannot be null"; -if(this.graph==a)throw"graph cannot be the same";this.clear();this.graph&&(this._graph_stack||(this._graph_stack=[]),this._graph_stack.push(this.graph));a.attachCanvas(this);this.setDirty(!0,!0)};e.prototype.closeSubgraph=function(){if(this._graph_stack&&0!=this._graph_stack.length){var a=this.graph._subgraph_node,b=this._graph_stack.pop();this.selected_nodes={};this.highlighted_links={};b.attachCanvas(this);this.setDirty(!0,!0);a&&(this.centerOnNode(a),this.selectNodes([a]))}};e.prototype.getCurrentGraph= -function(){return this.graph};e.prototype.setCanvas=function(a,b){if(a&&a.constructor===String&&(a=document.getElementById(a),!a))throw"Error creating LiteGraph canvas: Canvas not found";if(a!==this.canvas&&(!a&&this.canvas&&(b||this.unbindEvents()),this.canvas=a,this.ds.element=a)){a.className+=" lgraphcanvas";a.data=this;a.tabindex="1";this.bgcanvas=null;this.bgcanvas||(this.bgcanvas=document.createElement("canvas"),this.bgcanvas.width=this.canvas.width,this.bgcanvas.height=this.canvas.height); -if(null==a.getContext){if("canvas"!=a.localName)throw"Element supplied for LGraphCanvas must be a element, you passed a "+a.localName;throw"This browser doesn't support Canvas";}null==(this.ctx=a.getContext("2d"))&&(a.webgl_enabled||console.warn("This canvas seems to be WebGL, enabling WebGL renderer"),this.enableWebGL());this._mousemove_callback=this.processMouseMove.bind(this);this._mouseup_callback=this.processMouseUp.bind(this);b||this.bindEvents()}};e.prototype._doNothing=function(a){a.preventDefault(); -return!1};e.prototype._doReturnTrue=function(a){a.preventDefault();return!0};e.prototype.bindEvents=function(){if(this._events_binded)console.warn("LGraphCanvas: events already binded");else{var a=this.canvas,b=this.getCanvasWindow().document;this._mousedown_callback=this.processMouseDown.bind(this);this._mousewheel_callback=this.processMouseWheel.bind(this);a.addEventListener("mousedown",this._mousedown_callback,!0);a.addEventListener("mousemove",this._mousemove_callback);a.addEventListener("mousewheel", -this._mousewheel_callback,!1);a.addEventListener("contextmenu",this._doNothing);a.addEventListener("DOMMouseScroll",this._mousewheel_callback,!1);a.addEventListener("touchstart",this.touchHandler,!0);a.addEventListener("touchmove",this.touchHandler,!0);a.addEventListener("touchend",this.touchHandler,!0);a.addEventListener("touchcancel",this.touchHandler,!0);this._key_callback=this.processKey.bind(this);a.addEventListener("keydown",this._key_callback,!0);b.addEventListener("keyup",this._key_callback, -!0);this._ondrop_callback=this.processDrop.bind(this);a.addEventListener("dragover",this._doNothing,!1);a.addEventListener("dragend",this._doNothing,!1);a.addEventListener("drop",this._ondrop_callback,!1);a.addEventListener("dragenter",this._doReturnTrue,!1);this._events_binded=!0}};e.prototype.unbindEvents=function(){if(this._events_binded){var a=this.getCanvasWindow().document;this.canvas.removeEventListener("mousedown",this._mousedown_callback);this.canvas.removeEventListener("mousewheel",this._mousewheel_callback); -this.canvas.removeEventListener("DOMMouseScroll",this._mousewheel_callback);this.canvas.removeEventListener("keydown",this._key_callback);a.removeEventListener("keyup",this._key_callback);this.canvas.removeEventListener("contextmenu",this._doNothing);this.canvas.removeEventListener("drop",this._ondrop_callback);this.canvas.removeEventListener("dragenter",this._doReturnTrue);this.canvas.removeEventListener("touchstart",this.touchHandler);this.canvas.removeEventListener("touchmove",this.touchHandler); -this.canvas.removeEventListener("touchend",this.touchHandler);this.canvas.removeEventListener("touchcancel",this.touchHandler);this._ondrop_callback=this._key_callback=this._mousewheel_callback=this._mousedown_callback=null;this._events_binded=!1}else console.warn("LGraphCanvas: no events binded")};e.getFileExtension=function(a){var b=a.indexOf("?");-1!=b&&(a=a.substr(0,b));b=a.lastIndexOf(".");return-1==b?"":a.substr(b+1).toLowerCase()};e.prototype.enableWebGL=function(){if(void 0===typeof GL)throw"litegl.js must be included to use a WebGL canvas"; -if(void 0===typeof enableWebGLCanvas)throw"webglCanvas.js must be included to use this feature";this.gl=this.ctx=enableWebGLCanvas(this.canvas);this.ctx.webgl=!0;this.bgcanvas=this.canvas;this.bgctx=this.gl;this.canvas.webgl_enabled=!0};e.prototype.setDirty=function(a,b){a&&(this.dirty_canvas=!0);b&&(this.dirty_bgcanvas=!0)};e.prototype.getCanvasWindow=function(){if(!this.canvas)return window;var a=this.canvas.ownerDocument;return a.defaultView||a.parentWindow};e.prototype.startRendering=function(){function a(){this.pause_rendering|| -this.draw();var b=this.getCanvasWindow();this.is_rendering&&b.requestAnimationFrame(a.bind(this))}this.is_rendering||(this.is_rendering=!0,a.call(this))};e.prototype.stopRendering=function(){this.is_rendering=!1};e.prototype.processMouseDown=function(a){if(this.graph){this.adjustMouseEvent(a);var b=this.getCanvasWindow();e.active_canvas=this;this.canvas.removeEventListener("mousemove",this._mousemove_callback);b.document.addEventListener("mousemove",this._mousemove_callback,!0);b.document.addEventListener("mouseup", -this._mouseup_callback,!0);var d=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes,5),h=!1,f=300>g.getTime()-this.last_mouseclick;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;this.canvas.focus();g.closeAllContextMenus(b);if(!this.onMouse||!0!=this.onMouse(a)){if(1==a.which){a.ctrlKey&&(this.dragging_rectangle=new Float32Array(4),this.dragging_rectangle[0]=a.canvasX,this.dragging_rectangle[1]=a.canvasY,this.dragging_rectangle[2]=1,this.dragging_rectangle[3]=1,h=!0);var s= -!1;if(d&&this.allow_interaction&&!h&&!this.read_only){this.live_mode||d.flags.pinned||this.bringToFront(d);if(!this.connecting_node&&!d.flags.collapsed&&!this.live_mode)if(!h&&!1!==d.resizable&&z(a.canvasX,a.canvasY,d.pos[0]+d.size[0]-5,d.pos[1]+d.size[1]-5,10,10))this.resizing_node=d,this.canvas.style.cursor="se-resize",h=!0;else{if(d.outputs)for(var r=0,c=d.outputs.length;rs[0]+4||a.canvasYs[1]+4)){this.showLinkMenu(d, -a);this.over_link_center=null;break}this.selected_group=this.graph.getGroupOnPos(a.canvasX,a.canvasY);this.selected_group_resizing=!1;this.selected_group&&!this.read_only&&(a.ctrlKey&&(this.dragging_rectangle=null),10>E([a.canvasX,a.canvasY],[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])*this.ds.scale?this.selected_group_resizing=!0:this.selected_group.recomputeInsideNodes());f&&!this.read_only&&this.allow_searchbox&&this.showSearchBox(a); -s=!0}!h&&s&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}else 2!=a.which&&3==a.which&&(this.read_only||this.processContextMenu(d,a));this.last_mouse[0]=a.localX;this.last_mouse[1]=a.localY;this.last_mouseclick=g.getTime();this.last_mouse_dragging=!0;this.graph.change();(!b.document.activeElement||"input"!=b.document.activeElement.nodeName.toLowerCase()&&"textarea"!=b.document.activeElement.nodeName.toLowerCase())&&a.preventDefault();a.stopPropagation();if(this.onMouseDown)this.onMouseDown(a); -return!1}}};e.prototype.processMouseMove=function(a){this.autoresize&&this.resize();if(this.graph){e.active_canvas=this;this.adjustMouseEvent(a);var b=[a.localX,a.localY],d=[b[0]-this.last_mouse[0],b[1]-this.last_mouse[1]];this.last_mouse=b;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;a.dragging=this.last_mouse_dragging;this.node_widget&&(this.processNodeWidgets(this.node_widget[0],this.canvas_mouse,a,this.node_widget[1]),this.dirty_canvas=!0);if(this.dragging_rectangle)this.dragging_rectangle[2]= -a.canvasX-this.dragging_rectangle[0],this.dragging_rectangle[3]=a.canvasY-this.dragging_rectangle[1],this.dirty_canvas=!0;else if(this.selected_group&&!this.read_only)this.selected_group_resizing?this.selected_group.size=[a.canvasX-this.selected_group.pos[0],a.canvasY-this.selected_group.pos[1]]:(this.selected_group.move(d[0]/this.ds.scale,d[1]/this.ds.scale,a.ctrlKey),this.selected_group._nodes.length&&(this.dirty_canvas=!0)),this.dirty_bgcanvas=!0;else if(this.dragging_canvas)this.ds.offset[0]+= -d[0]/this.ds.scale,this.ds.offset[1]+=d[1]/this.ds.scale,this.dirty_bgcanvas=this.dirty_canvas=!0;else if(this.allow_interaction&&!this.read_only){this.connecting_node&&(this.dirty_canvas=!0);for(var h=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes),b=0,f=this.graph._nodes.length;br[0]+4||a.canvasYr[1]+4)){f=s;break}}f!=this.over_link_center&&(this.over_link_center=f,this.dirty_canvas=!0);this.canvas&&(this.canvas.style.cursor="")}if(this.node_capturing_input&&this.node_capturing_input!=h&&this.node_capturing_input.onMouseMove)this.node_capturing_input.onMouseMove(a, -[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]],this);if(this.node_dragged&&!this.live_mode){for(b in this.selected_nodes)h=this.selected_nodes[b],h.pos[0]+=d[0]/this.ds.scale,h.pos[1]+=d[1]/this.ds.scale;this.dirty_bgcanvas=this.dirty_canvas=!0}this.resizing_node&&!this.live_mode&&(this.resizing_node.size[0]=a.canvasX-this.resizing_node.pos[0],this.resizing_node.size[1]=a.canvasY-this.resizing_node.pos[1],d=Math.max(this.resizing_node.inputs?this.resizing_node.inputs.length: -0,this.resizing_node.outputs?this.resizing_node.outputs.length:0)*g.NODE_SLOT_HEIGHT+(this.resizing_node.widgets?this.resizing_node.widgets.length:0)*(g.NODE_WIDGET_HEIGHT+4)+4,this.resizing_node.size[1]this.dragging_rectangle[3]?this.dragging_rectangle[1]-f:this.dragging_rectangle[1];this.dragging_rectangle[0]=0>this.dragging_rectangle[2]?this.dragging_rectangle[0]-h:this.dragging_rectangle[0];this.dragging_rectangle[1]=s;this.dragging_rectangle[2]=h;this.dragging_rectangle[3]=f;f=[];for(s=0;sa.click_time&&z(a.canvasX,a.canvasY,h.pos[0],h.pos[1]-g.NODE_TITLE_HEIGHT,g.NODE_TITLE_HEIGHT,g.NODE_TITLE_HEIGHT)&&h.collapse();this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_dragged.pos[0]=Math.round(this.node_dragged.pos[0]); -this.node_dragged.pos[1]=Math.round(this.node_dragged.pos[1]);this.graph.config.align_to_grid&&this.node_dragged.alignToGrid();if(this.onNodeMoved)this.onNodeMoved(this.node_dragged);this.node_dragged=null}else{h=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes);!h&&300>a.click_time&&this.deselectAllNodes();this.dirty_canvas=!0;this.dragging_canvas=!1;if(this.node_over&&this.node_over.onMouseUp)this.node_over.onMouseUp(a,[a.canvasX-this.node_over.pos[0],a.canvasY-this.node_over.pos[1]], -this);if(this.node_capturing_input&&this.node_capturing_input.onMouseUp)this.node_capturing_input.onMouseUp(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]])}}else 2==a.which?(this.dirty_canvas=!0,this.dragging_canvas=!1):3==a.which&&(this.dirty_canvas=!0,this.dragging_canvas=!1);this.graph.change();a.stopPropagation();a.preventDefault();return!1}};e.prototype.processMouseWheel=function(a){if(this.graph&&this.allow_dragcanvas){var b=null!=a.wheelDeltaY?a.wheelDeltaY: --60*a.detail;this.adjustMouseEvent(a);var d=this.ds.scale;0b&&(d*=1/1.1);this.ds.changeScale(d,[a.localX,a.localY]);this.graph.change();a.preventDefault();return!1}};e.prototype.isOverNodeBox=function(a,b,d){var h=g.NODE_TITLE_HEIGHT;return z(b,d,a.pos[0]+2,a.pos[1]+2-h,h-4,h-4)?!0:!1};e.prototype.isOverNodeInput=function(a,b,d,h){if(a.inputs)for(var f=0,g=a.inputs.length;fd-this.graph._last_trigger_time)&&this.drawBackCanvas();(this.dirty_canvas||a)&&this.drawFrontCanvas();this.fps=this.render_time?1/this.render_time:0;this.frame+=1}};e.prototype.drawFrontCanvas=function(){this.dirty_canvas=!1;this.ctx||(this.ctx=this.bgcanvas.getContext("2d"));var a=this.ctx;if(a){a.start2D&&a.start2D();var b=this.canvas;a.restore();a.setTransform(1,0,0,1,0,0);this.dirty_area&&(a.save(), -a.beginPath(),a.rect(this.dirty_area[0],this.dirty_area[1],this.dirty_area[2],this.dirty_area[3]),a.clip());this.clear_background&&a.clearRect(0,0,b.width,b.height);this.bgcanvas==this.canvas?this.drawBackCanvas():a.drawImage(this.bgcanvas,0,0);if(this.onRender)this.onRender(b,a);this.show_info&&this.renderInfo(a);if(this.graph){a.save();this.ds.toCanvasContext(a);for(var b=this.computeVisibleNodes(null,this.visible_nodes),d=0;d> ";b.fillText(h+d.getTitle(), -0.5*a.width,40);b.restore()}d=!1;this.onRenderBackground&&(d=this.onRenderBackground(a,b));b.restore();b.setTransform(1,0,0,1,0,0);this.visible_links.length=0;if(this.graph){b.save();this.ds.toCanvasContext(b);if(this.background_image&&0.5this.ds.scale;if(this.live_mode){if(!a.flags.collapsed&&(b.shadowColor="transparent",a.onDrawForeground))a.onDrawForeground(b,this,this.canvas)}else{var s= -this.editor_alpha;b.globalAlpha=s;this.render_shadows&&!f?(b.shadowColor=g.DEFAULT_SHADOW_COLOR,b.shadowOffsetX=2*this.ds.scale,b.shadowOffsetY=2*this.ds.scale,b.shadowBlur=3*this.ds.scale):b.shadowColor="transparent";if(!a.flags.collapsed||!a.onDrawCollapsed||!0!=a.onDrawCollapsed(b,this)){var r=a._shape||g.BOX_SHAPE;t.set(a.size);var c=a.horizontal;if(a.flags.collapsed){b.font=this.inner_text_font;var e=a.getTitle?a.getTitle():a.title;null!=e&&(a._collapsed_width=Math.min(a.size[0],b.measureText(e).width+ -2*g.NODE_TITLE_HEIGHT),t[0]=a._collapsed_width,t[1]=0)}a.clip_area&&(b.save(),b.beginPath(),r==g.BOX_SHAPE?b.rect(0,0,t[0],t[1]):r==g.ROUND_SHAPE?b.roundRect(0,0,t[0],t[1],10):r==g.CIRCLE_SHAPE&&b.arc(0.5*t[0],0.5*t[1],0.5*t[0],0,2*Math.PI),b.clip());a.has_errors&&(h="red");this.drawNodeShape(a,b,t,d,h,a.is_selected,a.mouseOver);b.shadowColor="transparent";if(a.onDrawForeground)a.onDrawForeground(b,this,this.canvas);b.textAlign=c?"center":"left";b.font=this.inner_text_font;h=!f;r=this.connecting_output; -b.lineWidth=1;var e=0,n=new Float32Array(2);if(!a.flags.collapsed){if(a.inputs)for(d=0;dthis.ds.scale,l=a._shape||a.constructor.shape||g.ROUND_SHAPE,n=a.constructor.title_mode,k=!0;n==g.TRANSPARENT_TITLE?k=!1:n==g.AUTOHIDE_TITLE&& -r&&(k=!0);B[0]=0;B[1]=k?-f:0;B[2]=d[0]+1;B[3]=k?d[1]+f:d[1];r=b.globalAlpha;b.beginPath();l==g.BOX_SHAPE||c?b.fillRect(B[0],B[1],B[2],B[3]):l==g.ROUND_SHAPE||l==g.CARD_SHAPE?b.roundRect(B[0],B[1],B[2],B[3],this.round_radius,l==g.CARD_SHAPE?0:this.round_radius):l==g.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0],0,2*Math.PI);b.fill();a.flags.collapsed||(b.shadowColor="transparent",b.fillStyle="rgba(0,0,0,0.2)",b.fillRect(0,-1,B[2],2));b.shadowColor="transparent";if(a.onDrawBackground)a.onDrawBackground(b, -this,this.canvas);if(k||n==g.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,f,d,this.ds.scale,h);else if(n!=g.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){k=a.constructor.title_color||h;a.flags.collapsed&&(b.shadowColor=g.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var u=e.gradients[k];u||(u=e.gradients[k]=b.createLinearGradient(0,0,400,0),u.addColorStop(0,k),u.addColorStop(1,"#000"));b.fillStyle=u}else b.fillStyle=k;b.beginPath();l==g.BOX_SHAPE||c?b.rect(0, --f,d[0]+1,f):l!=g.ROUND_SHAPE&&l!=g.CARD_SHAPE||b.roundRect(0,-f,d[0]+1,f,this.round_radius,a.flags.collapsed?this.round_radius:0);b.fill();b.shadowColor="transparent"}if(a.onDrawTitleBox)a.onDrawTitleBox(b,f,d,this.ds.scale);else l==g.ROUND_SHAPE||l==g.CIRCLE_SHAPE||l==g.CARD_SHAPE?(c&&(b.fillStyle="black",b.beginPath(),b.arc(0.5*f,-0.5*f,6,0,2*Math.PI),b.fill()),b.fillStyle=a.boxcolor||g.NODE_DEFAULT_BOXCOLOR,c?b.fillRect(0.5*f-5,-0.5*f-5,10,10):(b.beginPath(),b.arc(0.5*f,-0.5*f,5,0,2*Math.PI), -b.fill())):(c&&(b.fillStyle="black",b.fillRect(0.5*(f-10)-1,-0.5*(f+10)-1,12,12)),b.fillStyle=a.boxcolor||g.NODE_DEFAULT_BOXCOLOR,b.fillRect(0.5*(f-10),-0.5*(f+10),10,10));b.globalAlpha=r;if(a.onDrawTitleText)a.onDrawTitleText(b,f,d,this.ds.scale,this.title_text_font,s);!c&&(b.font=this.title_text_font,c=String(a.getTitle()))&&(b.fillStyle=s?"white":a.constructor.title_text_color||this.node_title_color,a.flags.collapsed?(b.textAlign="left",b.measureText(c),b.fillText(c.substr(0,20),f,g.NODE_TITLE_TEXT_Y- -f),b.textAlign="left"):(b.textAlign="left",b.fillText(c,f,g.NODE_TITLE_TEXT_Y-f)));if(a.onDrawTitle)a.onDrawTitle(b)}if(s){if(a.onBounding)a.onBounding(B);n==g.TRANSPARENT_TITLE&&(B[1]-=f,B[3]+=f);b.lineWidth=1;b.globalAlpha=0.8;b.beginPath();l==g.BOX_SHAPE?b.rect(-6+B[0],-6+B[1],12+B[2],12+B[3]):l==g.ROUND_SHAPE||l==g.CARD_SHAPE&&a.flags.collapsed?b.roundRect(-6+B[0],-6+B[1],12+B[2],12+B[3],2*this.round_radius):l==g.CARD_SHAPE?b.roundRect(-6+B[0],-6+B[1],12+B[2],12+B[3],2*this.round_radius,2):l== -g.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0]+6,0,2*Math.PI);b.strokeStyle="#FFF";b.stroke();b.strokeStyle=h;b.globalAlpha=1}};var l=new Float32Array(4),q=new Float32Array(4),w=new Float32Array(2),x=new Float32Array(2);e.prototype.drawConnections=function(a){var b=g.getTime(),d=this.visible_area;l[0]=d[0]-20;l[1]=d[1]-20;l[2]=d[2]+40;l[3]=d[3]+40;a.lineWidth=this.connections_width;a.fillStyle="#AAA";a.strokeStyle="#AAA";a.globalAlpha=this.editor_alpha;for(var d=this.graph._nodes,h=0,f=d.length;h< -f;++h){var s=d[h];if(s.inputs&&s.inputs.length)for(var r=0;rq[2]&&(q[0]+=q[2],q[2]=Math.abs(q[2]));0>q[3]&&(q[1]+=q[3],q[3]=Math.abs(q[3]));if(y(q,l)){var t=e.outputs[n],n=s.inputs[r];if(t&& -n&&(e=t.dir||(e.horizontal?g.DOWN:g.RIGHT),n=n.dir||(s.horizontal?g.UP:g.LEFT),this.renderLink(a,k,u,c,!1,0,null,e,n),c&&c._last_time&&1E3>b-c._last_time)){var t=2-0.002*(b-c._last_time),p=a.globalAlpha;a.globalAlpha=p*t;this.renderLink(a,k,u,c,!0,t,"white",e,n);a.globalAlpha=p}}}}}}a.globalAlpha=1};e.prototype.renderLink=function(a,b,d,h,f,s,c,l,n,k){h&&this.visible_links.push(h);!c&&h&&(c=h.color||e.link_type_colors[h.type]);c||(c=this.default_link_color);null!=h&&this.highlighted_links[h.id]&& -(c="#FFF");l=l||g.RIGHT;n=n||g.LEFT;var q=E(b,d);this.render_connections_border&&0.6b[1]?0:Math.PI,a.save(),a.translate(u[0],u[1]),a.rotate(t),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore(),a.save(),a.translate(k[0],k[1]),a.rotate(x), -a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill());if(s)for(a.fillStyle=c,u=0;5>u;++u)s=(0.001*g.getTime()+0.2*u)%1,f=this.computeConnectionPoint(b,d,s,l,n),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill()};e.prototype.computeConnectionPoint=function(a,b,d,h,f){h=h||g.RIGHT;f=f||g.LEFT;var s=E(a,b),c=[a[0],a[1]],l=[b[0],b[1]];switch(h){case g.LEFT:c[0]+=-0.25*s;break;case g.RIGHT:c[0]+=0.25*s;break;case g.UP:c[1]+= --0.25*s;break;case g.DOWN:c[1]+=0.25*s}switch(f){case g.LEFT:l[0]+=-0.25*s;break;case g.RIGHT:l[0]+=0.25*s;break;case g.UP:l[1]+=-0.25*s;break;case g.DOWN:l[1]+=0.25*s}h=(1-d)*(1-d)*(1-d);f=3*(1-d)*(1-d)*d;s=3*(1-d)*d*d;d*=d*d;return[h*a[0]+f*c[0]+s*l[0]+d*b[0],h*a[1]+f*c[1]+s*l[1]+d*b[1]]};e.prototype.drawExecutionOrder=function(a){a.shadowColor="transparent";a.globalAlpha=0.25;a.textAlign="center";a.strokeStyle="white";a.globalAlpha=0.75;for(var b=this.visible_nodes,d=0;du.last_y&&r -u.options.max&&(u.value=u.options.max);else if("mousedown"==d.type){var q=u.options.values;q&&q.constructor===Function&&(q=u.options.values(u,a));var x=q.constructor===Array?q:Object.keys(q),c=40>c?-1:c>l-40?1:0;if("number"==u.type)u.value+=0.1*c*(u.options.step||1),null!=u.options.min&&u.valueu.options.max&&(u.value=u.options.max);else if(c)n=-1,n=q.constructor===Object?x.indexOf(String(u.value))+c:x.indexOf(u.value)+c,n>=x.length&& -(n=x.length-1),0>n&&(n=0),u.value=q.constructor===Array?q[n]:n;else{var t=q!=x?Object.values(q):q;new g.ContextMenu(t,{scale:Math.max(1,this.ds.scale),event:d,className:"dark",callback:p.bind(u)},n);var p=function(a,b,d){q!=x&&(a=t.indexOf(a));this.value=a;f(this,a);e.dirty_canvas=!0;return!1}}}else"mouseup"==d.type&&"number"==u.type&&(c=40>c?-1:c>l-40?1:0,200>d.click_time&&0==c&&this.prompt("Value",u.value,function(a){this.value=Number(a);f(this,this.value)}.bind(u),d));h!=u.value&&setTimeout(function(){f(this, -this.value)}.bind(u),20);this.dirty_canvas=!0;break;case "toggle":"mousedown"==d.type&&(u.value=!u.value,setTimeout(function(){f(u,u.value)},20));break;case "string":case "text":"mousedown"==d.type&&this.prompt("Value",u.value,function(a){this.value=a;f(this,a)}.bind(u),d);break;default:u.mouse&&u.mouse(ctx,d,[c,r],a)}return u}}return null};e.prototype.drawGroups=function(a,b){if(this.graph){var d=this.graph._groups;b.save();b.globalAlpha=0.5*this.editor_alpha;for(var h=0;hd&&0.01>b.editor_alpha&&(clearInterval(h),1>d&&(b.live_mode=!0));1"+n+""+ -a+"",value:n});if(l.length)return new g.ContextMenu(l,{event:d,callback:c,parentMenu:h,allow_html:!0,node:f},b),!1}};e.decodeHTML=function(a){var b=document.createElement("div");b.innerText=a;return b.innerHTML};e.onResizeNode=function(a,b,d,h,f){f&&(f.size=f.computeSize(),f.setDirtyCanvas(!0,!0))};e.prototype.showLinkMenu=function(a,b){var d=this;console.log(a);var h=new g.ContextMenu(["Add Node",null,"Delete"],{event:b,title:null!=a.data?a.data.constructor.name:null,callback:function(b,g, -c){switch(b){case "Add Node":e.onMenuAdd(null,null,c,h,function(b){console.log("node autoconnect");var f=d.graph.getNodeById(a.origin_id),h=d.graph.getNodeById(a.target_id);b.inputs&&b.inputs.length&&b.outputs&&b.outputs.length&&f.outputs[a.origin_slot].type==b.inputs[0].type&&b.outputs[0].type==h.inputs[0].type&&(f.connect(a.origin_slot,b,0),b.connect(0,h,a.target_slot),b.pos[0]-=0.5*b.size[0])});break;case "Delete":d.graph.removeLink(a.id)}}});return!1};e.onShowPropertyEditor=function(a,b,d,h,f){function g(){var b= -n.value;"Number"==a.type?b=Number(b):"Boolean"==a.type&&(b=Boolean(b));f[c]=b;l.parentNode&&l.parentNode.removeChild(l);f.setDirtyCanvas(!0,!0)}var c=a.property||"title";b=f[c];var l=document.createElement("div");l.className="graphdialog";l.innerHTML="";l.querySelector(".name").innerText=c;var n=l.querySelector("input");n&&(n.value=b,n.addEventListener("blur",function(a){this.focus()}),n.addEventListener("keydown", -function(a){13==a.keyCode&&(g(),a.preventDefault(),a.stopPropagation())}));b=e.active_canvas.canvas;d=b.getBoundingClientRect();var q=h=-20;d&&(h-=d.left,q-=d.top);event?(l.style.left=event.clientX+h+"px",l.style.top=event.clientY+q+"px"):(l.style.left=0.5*b.width+h+"px",l.style.top=0.5*b.height+q+"px");l.querySelector("button").addEventListener("click",g);b.parentNode.appendChild(l)};e.prototype.prompt=function(a,b,d,h){var f=this;a=a||"";var g=!1,c=document.createElement("div");c.className="graphdialog rounded"; -c.innerHTML=" ";c.close=function(){f.prompt_box=null;c.parentNode&&c.parentNode.removeChild(c)};1e.search_limit)break}}r=null;if(Array.prototype.filter)r=Object.keys(g.registered_node_types).filter(q);else for(L in r=[],g.registered_node_types)q(L)&&r.push(L);for(L=0;Le.search_limit);L++);var q=function(a){var b=g.registered_node_types[a];return l&&b.filter!=l?!1:-1!==a.toLowerCase().indexOf(d)}}}var f=this,c=e.active_canvas,r=c.canvas,l=r.ownerDocument||document,n=document.createElement("div"); -n.className="litegraph litesearchbox graphdialog rounded";n.innerHTML="Search
";n.close=function(){f.search_box=null;l.body.focus();l.body.style.overflow="";setTimeout(function(){f.canvas.focus()},20);n.parentNode&&n.parentNode.removeChild(n)};var q=null;1r.height-200&&(k.style.maxHeight=r.height-a.layerY-20+"px");p.focus();return n};e.prototype.showEditPropertyValue=function(a,b,d){function h(){f(u.value)}function f(f){"number"==typeof a.properties[b]&& -(f=Number(f));if("array"==g||"object"==g)f=JSON.parse(f);a.properties[b]=f;a._graph&&a._graph._version++;if(a.onPropertyChanged)a.onPropertyChanged(b,f);if(d.onclose)d.onclose();q.close();a.setDirtyCanvas(!0,!0)}if(a&&void 0!==a.properties[b]){d=d||{};var c=a.getPropertyInfo(b),g=c.type,l="";if("string"==g||"number"==g||"array"==g||"object"==g)l="";else if("enum"==g&&c.values){var l=""}else if("boolean"==g)l="";else{console.warn("unknown type: "+g);return}var q=this.createDialog(""+b+""+l+"",d);if("enum"==g&&c.values){var u=q.querySelector("select");u.addEventListener("change",function(a){f(a.target.value)})}else if("boolean"== -g)(u=q.querySelector("input"))&&u.addEventListener("click",function(a){f(!!u.checked)});else if(u=q.querySelector("input"))u.addEventListener("blur",function(a){this.focus()}),e=void 0!==a.properties[b]?a.properties[b]:"",e=JSON.stringify(e),u.value=e,u.addEventListener("keydown",function(a){13==a.keyCode&&(h(),a.preventDefault(),a.stopPropagation())});q.querySelector("button").addEventListener("click",h);return q}};e.prototype.createDialog=function(a,b){b=b||{};var d=document.createElement("div"); -d.className="graphdialog";d.innerHTML=a;var h=this.canvas.getBoundingClientRect(),f=-20,c=-20;h&&(f-=h.left,c-=h.top);b.position?(f+=b.position[0],c+=b.position[1]):b.event?(f+=b.event.clientX,c+=b.event.clientY):(f+=0.5*this.canvas.width,c+=0.5*this.canvas.height);d.style.left=f+"px";d.style.top=c+"px";this.canvas.parentNode.appendChild(d);d.close=function(){this.parentNode&&this.parentNode.removeChild(this)};return d};e.onMenuNodeCollapse=function(a,b,d,h,f){f.collapse()};e.onMenuNodePin=function(a, -b,d,h,f){f.pin()};e.onMenuNodeMode=function(a,b,d,h,f){new g.ContextMenu(["Always","On Event","On Trigger","Never"],{event:d,callback:function(a){if(f)switch(a){case "On Event":f.mode=g.ON_EVENT;break;case "On Trigger":f.mode=g.ON_TRIGGER;break;case "Never":f.mode=g.NEVER;break;default:f.mode=g.ALWAYS}},parentMenu:h,node:f});return!1};e.onMenuNodeColors=function(a,b,d,h,f){if(!f)throw"no node for color";b=[];b.push({value:null,content:"No color"}); -for(var c in e.node_colors)a=e.node_colors[c],a={value:c,content:""+c+""},b.push(a);new g.ContextMenu(b,{event:d,callback:function(a){f&&((a=a.value?e.node_colors[a.value]:null)?f.constructor===g.LGraphGroup?f.color=a.groupcolor:(f.color=a.color,f.bgcolor=a.bgcolor):(delete f.color,delete f.bgcolor),f.setDirtyCanvas(!0,!0))},parentMenu:h,node:f});return!1};e.onMenuNodeShapes= -function(a,b,d,h,f){if(!f)throw"no node passed";new g.ContextMenu(g.VALID_SHAPES,{event:d,callback:function(a){f&&(f.shape=a,f.setDirtyCanvas(!0))},parentMenu:h,node:f});return!1};e.onMenuNodeRemove=function(a,b,d,h,f){if(!f)throw"no node passed";!1!==f.removable&&(f.graph.remove(f),f.setDirtyCanvas(!0,!0))};e.onMenuNodeClone=function(a,b,d,h,f){!1!=f.clonable&&(a=f.clone())&&(a.pos=[f.pos[0]+5,f.pos[1]+5],f.graph.add(a),f.setDirtyCanvas(!0,!0))};e.node_colors={red:{color:"#322",bgcolor:"#533",groupcolor:"#A88"}, -brown:{color:"#332922",bgcolor:"#593930",groupcolor:"#b06634"},green:{color:"#232",bgcolor:"#353",groupcolor:"#8A8"},blue:{color:"#223",bgcolor:"#335",groupcolor:"#88A"},pale_blue:{color:"#2a363b",bgcolor:"#3f5159",groupcolor:"#3f789e"},cyan:{color:"#233",bgcolor:"#355",groupcolor:"#8AA"},purple:{color:"#323",bgcolor:"#535",groupcolor:"#a1309b"},yellow:{color:"#432",bgcolor:"#653",groupcolor:"#b58b2a"},black:{color:"#222",bgcolor:"#000",groupcolor:"#444"}};e.prototype.getCanvasMenuOptions=function(){var a= -null;this.getMenuOptions?a=this.getMenuOptions():(a=[{content:"Add Node",has_submenu:!0,callback:e.onMenuAdd},{content:"Add Group",callback:e.onGroupAdd}],this._graph_stack&&0Name",f),s=g.querySelector("input");s&&c&&(s.value=c.label||"");g.querySelector("button").addEventListener("click",function(a){s.value&& -(c&&(c.label=s.value),d.setDirty(!0));g.close()})}},extra:a};a&&(c.title=a.type);var l=null;a&&(l=a.getSlotInPosition(b.canvasX,b.canvasY),e.active_node=a);if(l){f=[];l&&l.output&&l.output.links&&l.output.links.length&&f.push({content:"Disconnect Links",slot:l});var n=l.input||l.output;f.push(n.locked?"Cannot remove":{content:"Remove Slot",slot:l});f.push(n.nameLocked?"Cannot rename":{content:"Rename Slot",slot:l});c.title=(l.input?l.input.type:l.output.type)||"*";l.input&&l.input.type==g.ACTION&& -(c.title="Action");l.output&&l.output.type==g.EVENT&&(c.title="Event")}else a?f=this.getNodeMenuOptions(a):(f=this.getCanvasMenuOptions(),(l=this.graph.getGroupOnPos(b.canvasX,b.canvasY))&&f.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:l,options:this.getGroupMenuOptions(l)}}));f&&new g.ContextMenu(f,c,h)};this.CanvasRenderingContext2D&&(CanvasRenderingContext2D.prototype.roundRect=function(a,b,d,h,f,c){void 0===f&&(f=5);void 0===c&&(c=f);this.moveTo(a+f,b);this.lineTo(a+ -d-f,b);this.quadraticCurveTo(a+d,b,a+d,b+f);this.lineTo(a+d,b+h-c);this.quadraticCurveTo(a+d,b+h,a+d-c,b+h);this.lineTo(a+c,b+h);this.quadraticCurveTo(a,b+h,a,b+h-c);this.lineTo(a,b+f);this.quadraticCurveTo(a,b,a+f,b)});g.compareObjects=function(a,b){for(var d in a)if(a[d]!=b[d])return!1;return!0};g.distance=E;g.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+","+Math.round(255*a[2]).toFixed()+","+(4==a.length?a[3].toFixed(2):"1.0")+")"};g.isInsideRectangle= -z;g.growBounding=function(a,b,d){ba[2]&&(a[2]=b);da[3]&&(a[3]=d)};g.isInsideBounding=function(a,b){return a[0]b[1][0]||a[1]>b[1][1]?!1:!0};g.overlapBounding=y;g.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),d=0,h,f,c=0;6>c;c+=2)h="0123456789ABCDEF".indexOf(a.charAt(c)),f="0123456789ABCDEF".indexOf(a.charAt(c+1)),b[d]=16*h+f,d++;return b};g.num2hex=function(a){for(var b="#",d,h,f=0;3>f;f++)d=a[f]/ -16,h=a[f]%16,b+="0123456789ABCDEF".charAt(d)+"0123456789ABCDEF".charAt(h);return b};D.prototype.addItem=function(a,b,d){function h(a){var b=this.value;b&&b.has_submenu&&f.call(this,a)}function f(a){var b=this.value,f=!0;c.current_submenu&&c.current_submenu.close(a);if(d.callback){var h=d.callback.call(this,b,d,a,c,d.node);!0===h&&(f=!1)}if(b&&(b.callback&&!d.ignore_item_callbacks&&!0!==b.disabled&&(h=b.callback.call(this,b,d,a,c,d.extra),!0===h&&(f=!1)),b.submenu)){if(!b.submenu.options)throw"ContextMenu submenu needs options"; -new c.constructor(b.submenu.options,{callback:b.submenu.callback,event:a,parentMenu:c,ignore_item_callbacks:b.submenu.ignore_item_callbacks,title:b.submenu.title,extra:b.submenu.extra,autoopen:d.autoopen});f=!1}f&&!c.lock&&c.close()}var c=this;d=d||{};var g=document.createElement("div");g.className="litemenu-entry submenu";var l=!1;if(null===b)g.classList.add("separator");else{g.innerHTML=b&&b.title?b.title:a;if(g.value=b)b.disabled&&(l=!0,g.classList.add("disabled")),(b.submenu||b.has_submenu)&& -g.classList.add("has_submenu");"function"==typeof b?(g.dataset.value=a,g.onclick_callback=b):g.dataset.value=b;b.className&&(g.className+=" "+b.className)}this.root.appendChild(g);l||g.addEventListener("click",f);d.autoopen&&g.addEventListener("mouseenter",h);return g};D.prototype.close=function(a,b){this.root.parentNode&&this.root.parentNode.removeChild(this.root);this.parentMenu&&!b&&(this.parentMenu.lock=!1,this.parentMenu.current_submenu=null,void 0===a?this.parentMenu.close():a&&!D.isCursorOverElement(a, -this.parentMenu.root)&&D.trigger(this.parentMenu.root,"mouseleave",a));this.current_submenu&&this.current_submenu.close(a,!0);this.root.closing_timer&&clearTimeout(this.root.closing_timer)};D.trigger=function(a,b,d,h){var f=document.createEvent("CustomEvent");f.initCustomEvent(b,!0,!0,d);f.srcElement=h;a.dispatchEvent?a.dispatchEvent(f):a.__events&&a.__events.dispatchEvent(f);return f};D.prototype.getTopMenu=function(){return this.options.parentMenu?this.options.parentMenu.getTopMenu():this};D.prototype.getFirstEvent= -function(){return this.options.parentMenu?this.options.parentMenu.getFirstEvent():this.options.event};D.isCursorOverElement=function(a,b){var d=a.clientX,h=a.clientY,f=b.getBoundingClientRect();return f?h>f.top&&hf.left&&dMath.abs(d))return h[1];d=(a-h[0])/d;return h[1]*(1-d)+f[1]*d}}return 0}};A.prototype.draw=function(a,b,d,h,f,c){if(d=this.points){this.size=b;var g=b[0]-2*this.margin;b=b[1]-2*this.margin;f=f||"#666";a.save();a.translate(this.margin,this.margin);h&&(a.fillStyle="#111",a.fillRect(0,0,g,b),a.fillStyle="#222",a.fillRect(0.5*g,0,1,b),a.strokeStyle="#333",a.strokeRect(0,0,g,b));a.strokeStyle=f;c&&(a.globalAlpha=0.5);a.beginPath();for(h=0;ha[1])){var h=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=a[0]-this.margin,g=a[1]-this.margin;this.selected=this.getCloserPoint([c,g],30/b.ds.scale);-1==this.selected&&(h=[c/h,1-g/f],d.push(h),d.sort(function(a,b){return a[0]- -b[0]}),this.selected=d.indexOf(h),this.must_update=!0);if(-1!=this.selected)return!0}};A.prototype.onMouseMove=function(a,b){var d=this.points;if(d){var h=this.selected;if(!(0>h)){var f=(a[0]-this.margin)/(this.size[0]-2*this.margin),c=(a[1]-this.margin)/(this.size[1]-2*this.margin);this._nearest=this.getCloserPoint([a[0]-this.margin,a[1]-this.margin],30/b.ds.scale);var g=d[h];if(g){var l=0==h||h==d.length-1;!l&&(-10>a[0]||a[0]>this.size[0]+10||-10>a[1]||a[1]>this.size[1]+10)?(d.splice(h,1),this.selected= --1):(g[0]=l?0==h?0:1:Math.clamp(f,0,1),g[1]=1-Math.clamp(c,0,1),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(g),this.must_update=!0)}}}};A.prototype.onMouseUp=function(a,b){this.selected=-1;return!1};A.prototype.getCloserPoint=function(a,b){var d=this.points;if(!d)return-1;b=b||30;for(var h=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=d.length,g=[0,0],l=1E6,n=-1,e=0;el||q>b||(n=e,l=q)}return n};g.CurveEditor= -A;g.getParameterNames=function(a){return(a+"").replace(/[/][/].*$/gm,"").replace(/\s+/g,"").replace(/[/][*][^/*]*[*][/]/g,"").split("){",1)[0].replace(/^[^(]*[(]/,"").replace(/=[^,]+/g,"").split(",").filter(Boolean)};Math.clamp=function(a,b,d){return b>a?b:dthis.size[0]-h.NODE_TITLE_HEIGHT&&0>b[1]){var c=this;setTimeout(function(){d.openSubgraph(c.subgraph)},10)}};m.prototype.onAction=function(a,b){this.subgraph.onAction(a,b)};m.prototype.onExecute=function(){if(this.enabled= +registerNodeType:function(a,b){if(!b.prototype)throw"Cannot register a simple object, it must be a class with a prototype";b.type=a;h.debug&&console.log("Node registered: "+a);a.split("/");var d=b.name,g=a.lastIndexOf("/");b.category=a.substr(0,g);b.title||(b.title=d);if(b.prototype)for(var f in n.prototype)b.prototype[f]||(b.prototype[f]=n.prototype[f]);if(g=this.registered_node_types[a])console.log("replacing node type: "+a);else if(Object.hasOwnProperty(b.prototype,"shape")||Object.defineProperty(b.prototype, +"shape",{set:function(a){switch(a){case "default":delete this._shape;break;case "box":this._shape=h.BOX_SHAPE;break;case "round":this._shape=h.ROUND_SHAPE;break;case "circle":this._shape=h.CIRCLE_SHAPE;break;case "card":this._shape=h.CARD_SHAPE;break;default:this._shape=a}},get:function(a){return this._shape},enumerable:!0,configurable:!0}),b.prototype.onPropertyChange&&console.warn("LiteGraph node class "+a+" has onPropertyChange method, it must be called onPropertyChanged with d at the end"),b.supported_extensions)for(f in b.supported_extensions){var r= +b.supported_extensions[f];r&&r.constructor===String&&(this.node_types_by_file_extension[r.toLowerCase()]=b)}this.registered_node_types[a]=b;b.constructor.name&&(this.Nodes[d]=b);if(h.onNodeTypeRegistered)h.onNodeTypeRegistered(a,b);if(g&&h.onNodeTypeReplaced)h.onNodeTypeReplaced(a,b,g)},unregisterNodeType:function(a){var b=a.constructor===String?this.registered_node_types[a]:a;if(!b)throw"node type not found: "+a;delete this.registered_node_types[b.type];b.constructor.name&&delete this.Nodes[b.constructor.name]}, +wrapFunctionAsNode:function(a,b,d,g,f){for(var r=Array(b.length),s="",c=h.getParameterNames(b),k=0;ks&&(s=f.size[0]),c+=f.size[1]+a+h.NODE_TITLE_HEIGHT;b+=s+a}this.setDirtyCanvas(!0,!0)};c.prototype.getTime=function(){return this.globaltime};c.prototype.getFixedTime=function(){return this.fixedtime};c.prototype.getElapsedTime=function(){return this.elapsed_time};c.prototype.sendEventToAllNodes=function(a,b,d){d=d||h.ALWAYS;var g=this._nodes_in_order? +this._nodes_in_order:this._nodes;if(g)for(var f=0,r=g.length;f=h.MAX_NUMBER_OF_NODES)throw"LiteGraph: max number of nodes in a graph reached";null==a.id||-1==a.id?a.id=++this.last_node_id:this.last_node_ida.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos}, +enumerable:!0});this.id=-1;this.type=null;this.inputs=[];this.outputs=[];this.connections=[];this.properties={};this.properties_info=[];this.flags={}};n.prototype.configure=function(a){this.graph&&this.graph._version++;for(var b in a)if("properties"==b)for(var d in a.properties){if(this.properties[d]=a.properties[d],this.onPropertyChanged)this.onPropertyChanged(d,a.properties[d])}else null!=a[b]&&("object"==typeof a[b]?this[b]&&this[b].configure?this[b].configure(a[b]):this[b]=h.cloneObject(a[b], +this[b]):this[b]=a[b]);a.title||(this.title=this.constructor.title);if(this.onConnectionsChange){if(this.inputs)for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d._data=b,this.outputs[a].links))for(d=0;d=this.outputs.length)){var d=this.outputs[a]; +if(d&&(d.type=b,this.outputs[a].links))for(d=0;d=this.inputs.length||null==this.inputs[a].link)){var d=this.graph.links[this.inputs[a].link];if(!d)return null;if(!b)return d.data;var g=this.graph.getNodeById(d.origin_id);if(!g)return d.data;if(g.updateOutputData)g.updateOutputData(d.origin_slot);else if(g.onExecute)g.onExecute();return d.data}};n.prototype.getInputDataType= +function(a){if(!this.inputs||a>=this.inputs.length||null==this.inputs[a].link)return null;a=this.graph.links[this.inputs[a].link];if(!a)return null;var b=this.graph.getNodeById(a.origin_id);return b?(a=b.outputs[a.origin_slot])?a.type:null:a.type};n.prototype.getInputDataByName=function(a,b){var d=this.findInputSlot(a);return-1==d?null:this.getInputData(d,b)};n.prototype.isInputConnected=function(a){return this.inputs?a=this.inputs.length)return null;a=this.inputs[a];return a&&null!==a.link?(a=this.graph.links[a.link])?this.graph.getNodeById(a.origin_id):null:null};n.prototype.getInputOrProperty=function(a){if(!this.inputs||!this.inputs.length)return this.properties?this.properties[a]:null;for(var b=0,d=this.inputs.length;b=this.outputs.length?null:this.outputs[a]._data};n.prototype.getOutputInfo=function(a){return this.outputs?a=this.outputs.length)return null;a=this.outputs[a];if(!a.links||0==a.links.length)return null;for(var b=[],d=0;da&&this.pos[1]-f-db)return!0;return!1};n.prototype.getSlotInPosition=function(a,b){var d=new Float32Array(2);if(this.inputs)for(var g=0,f=this.inputs.length;g=this.outputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),null;b&&b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"target node is null";if(b==this)return null;if(d.constructor===String){if(d=b.findInputSlot(d),-1==d)return h.debug&&console.log("Connect: Error, no slot of name "+d),null}else{if(d=== +h.EVENT)return null;if(!b.inputs||d>=b.inputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),null}null!=b.inputs[d].link&&b.disconnectInput(d);var g=this.outputs[a];if(b.onConnectInput&&!1===b.onConnectInput(d,g.type,g))return null;var f=b.inputs[d],r=null;if(h.isValidConnection(g.type,f.type)){r=new m(++this.graph.last_link_id,f.type,this.id,a,b.id,d);this.graph.links[r.id]=r;null==g.links&&(g.links=[]);g.links.push(r.id);b.inputs[d].link=r.id;this.graph&&this.graph._version++; +if(this.onConnectionsChange)this.onConnectionsChange(h.OUTPUT,a,!0,r,g);if(b.onConnectionsChange)b.onConnectionsChange(h.INPUT,d,!0,r,f);this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(h.INPUT,b,d,this,a),this.graph.onNodeConnectionChange(h.OUTPUT,this,a,b,d))}this.setDirtyCanvas(!1,!0);this.graph.connectionChange(this,r);return r};n.prototype.disconnectOutput=function(a,b){if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return h.debug&&console.log("Connect: Error, no slot of name "+ +a),!1}else if(!this.outputs||a>=this.outputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),!1;var d=this.outputs[a];if(!d||!d.links||0==d.links.length)return!1;if(b){b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"Target Node not found";for(var g=0,f=d.links.length;g=this.inputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),!1;var b=this.inputs[a];if(!b)return!1;var d=this.inputs[a].link;this.inputs[a].link=null;var g=this.graph.links[d];if(g){var f= +this.graph.getNodeById(g.origin_id);if(!f)return!1;var r=f.outputs[g.origin_slot];if(!r||!r.links||0==r.links.length)return!1;for(var s=0,c=r.links.length;sb&&this.inputs[b].pos)return d[0]=this.pos[0]+this.inputs[b].pos[0],d[1]=this.pos[1]+this.inputs[b].pos[1],d;if(!a&&g>b&&this.outputs[b].pos)return d[0]=this.pos[0]+this.outputs[b].pos[0],d[1]=this.pos[1]+this.outputs[b].pos[1],d;if(this.horizontal)return d[0]=this.pos[0]+this.size[0]/g*(b+0.5),d[1]=a?this.pos[1]-h.NODE_TITLE_HEIGHT: +this.pos[1]+this.size[1],d;d[0]=a?this.pos[0]+f:this.pos[0]+this.size[0]+1-f;d[1]=this.pos[1]+(b+0.7)*h.NODE_SLOT_HEIGHT+(this.constructor.slot_start_y||0);return d};n.prototype.alignToGrid=function(){this.pos[0]=h.CANVAS_GRID_SIZE*Math.round(this.pos[0]/h.CANVAS_GRID_SIZE);this.pos[1]=h.CANVAS_GRID_SIZE*Math.round(this.pos[1]/h.CANVAS_GRID_SIZE)};n.prototype.trace=function(a){this.console||(this.console=[]);this.console.push(a);this.console.length>n.MAX_CONSOLE&&this.console.shift();this.graph.onNodeTrace(this, +a)};n.prototype.setDirtyCanvas=function(a,b){this.graph&&this.graph.sendActionToCanvas("setDirty",[a,b])};n.prototype.loadImage=function(a){var b=new Image;b.src=h.node_images_path+a;b.ready=!1;var d=this;b.onload=function(){this.ready=!0;d.setDirtyCanvas(!0)};return b};n.prototype.captureInput=function(a){if(this.graph&&this.graph.list_of_graphcanvas)for(var b=this.graph.list_of_graphcanvas,d=0;da.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});Object.defineProperty(this,"size",{set:function(a){!a||2>a.length||(this._size[0]=Math.max(140,a[0]),this._size[1]=Math.max(80,a[1]))},get:function(){return this._size}, +enumerable:!0})};l.prototype.configure=function(a){this.title=a.title;this._bounding.set(a.bounding);this.color=a.color;this.font=a.font};l.prototype.serialize=function(){var a=this._bounding;return{title:this.title,bounding:[Math.round(a[0]),Math.round(a[1]),Math.round(a[2]),Math.round(a[3])],color:this.color,font:this.font}};l.prototype.move=function(a,b,d){this._pos[0]+=a;this._pos[1]+=b;if(!d)for(d=0;dthis.max_scale&&(a=this.max_scale);if(a!=this.scale&&this.element){var d=this.element.getBoundingClientRect();if(d){b=b||[0.5*d.width,0.5*d.height];d=this.convertCanvasToOffset(b);this.scale=a;0.01>Math.abs(this.scale-1)&&(this.scale= +1);var g=this.convertCanvasToOffset(b),d=[g[0]-d[0],g[1]-d[1]];this.offset[0]+=d[0];this.offset[1]+=d[1];if(this.onredraw)this.onredraw(this)}}};x.prototype.changeDeltaScale=function(a,b){this.changeScale(this.scale*a,b)};x.prototype.reset=function(){this.scale=1;this.offset[0]=0;this.offset[1]=0};B.LGraphCanvas=h.LGraphCanvas=e;e.link_type_colors={"-1":h.EVENT_LINK_COLOR,number:"#AAA",node:"#DCA"};e.gradients={};e.prototype.clear=function(){this.fps=this.render_time=this.last_draw_time=this.frame= +0;this.dragging_rectangle=null;this.selected_nodes={};this.selected_group=null;this.visible_nodes=[];this.connecting_node=this.node_capturing_input=this.node_over=this.node_dragged=null;this.highlighted_links={};this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_widget=this.node_in_panel=this.dirty_area=null;this.last_mouse=[0,0];this.last_mouseclick=0;this.visible_area.set([0,0,0,0]);if(this.onClear)this.onClear()};e.prototype.setGraph=function(a,b){this.graph!=a&&(b||this.clear(),!a&&this.graph? +this.graph.detachCanvas(this):(a.attachCanvas(this),this._graph_stack&&(this._graph_stack=null),this.setDirty(!0,!0)))};e.prototype.openSubgraph=function(a){if(!a)throw"graph cannot be null";if(this.graph==a)throw"graph cannot be the same";this.clear();this.graph&&(this._graph_stack||(this._graph_stack=[]),this._graph_stack.push(this.graph));a.attachCanvas(this);this.setDirty(!0,!0)};e.prototype.closeSubgraph=function(){if(this._graph_stack&&0!=this._graph_stack.length){var a=this.graph._subgraph_node, +b=this._graph_stack.pop();this.selected_nodes={};this.highlighted_links={};b.attachCanvas(this);this.setDirty(!0,!0);a&&(this.centerOnNode(a),this.selectNodes([a]))}};e.prototype.getCurrentGraph=function(){return this.graph};e.prototype.setCanvas=function(a,b){if(a&&a.constructor===String&&(a=document.getElementById(a),!a))throw"Error creating LiteGraph canvas: Canvas not found";if(a!==this.canvas&&(!a&&this.canvas&&(b||this.unbindEvents()),this.canvas=a,this.ds.element=a)){a.className+=" lgraphcanvas"; +a.data=this;a.tabindex="1";this.bgcanvas=null;this.bgcanvas||(this.bgcanvas=document.createElement("canvas"),this.bgcanvas.width=this.canvas.width,this.bgcanvas.height=this.canvas.height);if(null==a.getContext){if("canvas"!=a.localName)throw"Element supplied for LGraphCanvas must be a element, you passed a "+a.localName;throw"This browser doesn't support Canvas";}null==(this.ctx=a.getContext("2d"))&&(a.webgl_enabled||console.warn("This canvas seems to be WebGL, enabling WebGL renderer"), +this.enableWebGL());this._mousemove_callback=this.processMouseMove.bind(this);this._mouseup_callback=this.processMouseUp.bind(this);b||this.bindEvents()}};e.prototype._doNothing=function(a){a.preventDefault();return!1};e.prototype._doReturnTrue=function(a){a.preventDefault();return!0};e.prototype.bindEvents=function(){if(this._events_binded)console.warn("LGraphCanvas: events already binded");else{var a=this.canvas,b=this.getCanvasWindow().document;this._mousedown_callback=this.processMouseDown.bind(this); +this._mousewheel_callback=this.processMouseWheel.bind(this);a.addEventListener("mousedown",this._mousedown_callback,!0);a.addEventListener("mousemove",this._mousemove_callback);a.addEventListener("mousewheel",this._mousewheel_callback,!1);a.addEventListener("contextmenu",this._doNothing);a.addEventListener("DOMMouseScroll",this._mousewheel_callback,!1);a.addEventListener("touchstart",this.touchHandler,!0);a.addEventListener("touchmove",this.touchHandler,!0);a.addEventListener("touchend",this.touchHandler, +!0);a.addEventListener("touchcancel",this.touchHandler,!0);this._key_callback=this.processKey.bind(this);a.addEventListener("keydown",this._key_callback,!0);b.addEventListener("keyup",this._key_callback,!0);this._ondrop_callback=this.processDrop.bind(this);a.addEventListener("dragover",this._doNothing,!1);a.addEventListener("dragend",this._doNothing,!1);a.addEventListener("drop",this._ondrop_callback,!1);a.addEventListener("dragenter",this._doReturnTrue,!1);this._events_binded=!0}};e.prototype.unbindEvents= +function(){if(this._events_binded){var a=this.getCanvasWindow().document;this.canvas.removeEventListener("mousedown",this._mousedown_callback);this.canvas.removeEventListener("mousewheel",this._mousewheel_callback);this.canvas.removeEventListener("DOMMouseScroll",this._mousewheel_callback);this.canvas.removeEventListener("keydown",this._key_callback);a.removeEventListener("keyup",this._key_callback);this.canvas.removeEventListener("contextmenu",this._doNothing);this.canvas.removeEventListener("drop", +this._ondrop_callback);this.canvas.removeEventListener("dragenter",this._doReturnTrue);this.canvas.removeEventListener("touchstart",this.touchHandler);this.canvas.removeEventListener("touchmove",this.touchHandler);this.canvas.removeEventListener("touchend",this.touchHandler);this.canvas.removeEventListener("touchcancel",this.touchHandler);this._ondrop_callback=this._key_callback=this._mousewheel_callback=this._mousedown_callback=null;this._events_binded=!1}else console.warn("LGraphCanvas: no events binded")}; +e.getFileExtension=function(a){var b=a.indexOf("?");-1!=b&&(a=a.substr(0,b));b=a.lastIndexOf(".");return-1==b?"":a.substr(b+1).toLowerCase()};e.prototype.enableWebGL=function(){if(void 0===typeof GL)throw"litegl.js must be included to use a WebGL canvas";if(void 0===typeof enableWebGLCanvas)throw"webglCanvas.js must be included to use this feature";this.gl=this.ctx=enableWebGLCanvas(this.canvas);this.ctx.webgl=!0;this.bgcanvas=this.canvas;this.bgctx=this.gl;this.canvas.webgl_enabled=!0};e.prototype.setDirty= +function(a,b){a&&(this.dirty_canvas=!0);b&&(this.dirty_bgcanvas=!0)};e.prototype.getCanvasWindow=function(){if(!this.canvas)return window;var a=this.canvas.ownerDocument;return a.defaultView||a.parentWindow};e.prototype.startRendering=function(){function a(){this.pause_rendering||this.draw();var b=this.getCanvasWindow();this.is_rendering&&b.requestAnimationFrame(a.bind(this))}this.is_rendering||(this.is_rendering=!0,a.call(this))};e.prototype.stopRendering=function(){this.is_rendering=!1};e.prototype.processMouseDown= +function(a){if(this.graph){this.adjustMouseEvent(a);var b=this.getCanvasWindow();e.active_canvas=this;this.canvas.removeEventListener("mousemove",this._mousemove_callback);b.document.addEventListener("mousemove",this._mousemove_callback,!0);b.document.addEventListener("mouseup",this._mouseup_callback,!0);var d=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes,5),g=!1,f=300>h.getTime()-this.last_mouseclick;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;this.canvas.focus(); +h.closeAllContextMenus(b);if(!this.onMouse||!0!=this.onMouse(a)){if(1==a.which){a.ctrlKey&&(this.dragging_rectangle=new Float32Array(4),this.dragging_rectangle[0]=a.canvasX,this.dragging_rectangle[1]=a.canvasY,this.dragging_rectangle[2]=1,this.dragging_rectangle[3]=1,g=!0);var r=!1;if(d&&this.allow_interaction&&!g&&!this.read_only){this.live_mode||d.flags.pinned||this.bringToFront(d);if(!this.connecting_node&&!d.flags.collapsed&&!this.live_mode)if(!g&&!1!==d.resizable&&w(a.canvasX,a.canvasY,d.pos[0]+ +d.size[0]-5,d.pos[1]+d.size[1]-5,10,10))this.resizing_node=d,this.canvas.style.cursor="se-resize",g=!0;else{if(d.outputs)for(var s=0,c=d.outputs.length;sr[0]+4||a.canvasYr[1]+4)){this.showLinkMenu(d,a);this.over_link_center=null;break}this.selected_group=this.graph.getGroupOnPos(a.canvasX,a.canvasY);this.selected_group_resizing=!1;this.selected_group&&!this.read_only&&(a.ctrlKey&&(this.dragging_rectangle=null),10>C([a.canvasX, +a.canvasY],[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])*this.ds.scale?this.selected_group_resizing=!0:this.selected_group.recomputeInsideNodes());f&&!this.read_only&&this.allow_searchbox&&this.showSearchBox(a);r=!0}!g&&r&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}else 2!=a.which&&3==a.which&&(this.read_only||this.processContextMenu(d,a));this.last_mouse[0]=a.localX;this.last_mouse[1]=a.localY;this.last_mouseclick=h.getTime(); +this.last_mouse_dragging=!0;this.graph.change();(!b.document.activeElement||"input"!=b.document.activeElement.nodeName.toLowerCase()&&"textarea"!=b.document.activeElement.nodeName.toLowerCase())&&a.preventDefault();a.stopPropagation();if(this.onMouseDown)this.onMouseDown(a);return!1}}};e.prototype.processMouseMove=function(a){this.autoresize&&this.resize();if(this.graph){e.active_canvas=this;this.adjustMouseEvent(a);var b=[a.localX,a.localY],d=[b[0]-this.last_mouse[0],b[1]-this.last_mouse[1]];this.last_mouse= +b;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;a.dragging=this.last_mouse_dragging;this.node_widget&&(this.processNodeWidgets(this.node_widget[0],this.canvas_mouse,a,this.node_widget[1]),this.dirty_canvas=!0);if(this.dragging_rectangle)this.dragging_rectangle[2]=a.canvasX-this.dragging_rectangle[0],this.dragging_rectangle[3]=a.canvasY-this.dragging_rectangle[1],this.dirty_canvas=!0;else if(this.selected_group&&!this.read_only)this.selected_group_resizing?this.selected_group.size= +[a.canvasX-this.selected_group.pos[0],a.canvasY-this.selected_group.pos[1]]:(this.selected_group.move(d[0]/this.ds.scale,d[1]/this.ds.scale,a.ctrlKey),this.selected_group._nodes.length&&(this.dirty_canvas=!0)),this.dirty_bgcanvas=!0;else if(this.dragging_canvas)this.ds.offset[0]+=d[0]/this.ds.scale,this.ds.offset[1]+=d[1]/this.ds.scale,this.dirty_bgcanvas=this.dirty_canvas=!0;else if(this.allow_interaction&&!this.read_only){this.connecting_node&&(this.dirty_canvas=!0);for(var g=this.graph.getNodeOnPos(a.canvasX, +a.canvasY,this.visible_nodes),b=0,f=this.graph._nodes.length;bs[0]+4||a.canvasYs[1]+4)){f=r;break}}f!=this.over_link_center&&(this.over_link_center=f,this.dirty_canvas=!0);this.canvas&&(this.canvas.style.cursor="")}if(this.node_capturing_input&&this.node_capturing_input!=g&&this.node_capturing_input.onMouseMove)this.node_capturing_input.onMouseMove(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]],this);if(this.node_dragged&&!this.live_mode){for(b in this.selected_nodes)g=this.selected_nodes[b], +g.pos[0]+=d[0]/this.ds.scale,g.pos[1]+=d[1]/this.ds.scale;this.dirty_bgcanvas=this.dirty_canvas=!0}this.resizing_node&&!this.live_mode&&(this.resizing_node.size[0]=a.canvasX-this.resizing_node.pos[0],this.resizing_node.size[1]=a.canvasY-this.resizing_node.pos[1],d=Math.max(this.resizing_node.inputs?this.resizing_node.inputs.length:0,this.resizing_node.outputs?this.resizing_node.outputs.length:0)*h.NODE_SLOT_HEIGHT+(this.resizing_node.widgets?this.resizing_node.widgets.length:0)*(h.NODE_WIDGET_HEIGHT+ +4)+4,this.resizing_node.size[1]this.dragging_rectangle[3]?this.dragging_rectangle[1]-f:this.dragging_rectangle[1];this.dragging_rectangle[0]= +0>this.dragging_rectangle[2]?this.dragging_rectangle[0]-g:this.dragging_rectangle[0];this.dragging_rectangle[1]=r;this.dragging_rectangle[2]=g;this.dragging_rectangle[3]=f;f=[];for(r=0;ra.click_time&&w(a.canvasX,a.canvasY,g.pos[0],g.pos[1]-h.NODE_TITLE_HEIGHT,h.NODE_TITLE_HEIGHT,h.NODE_TITLE_HEIGHT)&&g.collapse();this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_dragged.pos[0]=Math.round(this.node_dragged.pos[0]);this.node_dragged.pos[1]=Math.round(this.node_dragged.pos[1]);this.graph.config.align_to_grid&& +this.node_dragged.alignToGrid();if(this.onNodeMoved)this.onNodeMoved(this.node_dragged);this.node_dragged=null}else{g=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes);!g&&300>a.click_time&&this.deselectAllNodes();this.dirty_canvas=!0;this.dragging_canvas=!1;if(this.node_over&&this.node_over.onMouseUp)this.node_over.onMouseUp(a,[a.canvasX-this.node_over.pos[0],a.canvasY-this.node_over.pos[1]],this);if(this.node_capturing_input&&this.node_capturing_input.onMouseUp)this.node_capturing_input.onMouseUp(a, +[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]])}}else 2==a.which?(this.dirty_canvas=!0,this.dragging_canvas=!1):3==a.which&&(this.dirty_canvas=!0,this.dragging_canvas=!1);this.graph.change();a.stopPropagation();a.preventDefault();return!1}};e.prototype.processMouseWheel=function(a){if(this.graph&&this.allow_dragcanvas){var b=null!=a.wheelDeltaY?a.wheelDeltaY:-60*a.detail;this.adjustMouseEvent(a);var d=this.ds.scale;0b&&(d*=1/1.1);this.ds.changeScale(d, +[a.localX,a.localY]);this.graph.change();a.preventDefault();return!1}};e.prototype.isOverNodeBox=function(a,b,d){var g=h.NODE_TITLE_HEIGHT;return w(b,d,a.pos[0]+2,a.pos[1]+2-g,g-4,g-4)?!0:!1};e.prototype.isOverNodeInput=function(a,b,d,g){if(a.inputs)for(var f=0,h=a.inputs.length;fd- +this.graph._last_trigger_time)&&this.drawBackCanvas();(this.dirty_canvas||a)&&this.drawFrontCanvas();this.fps=this.render_time?1/this.render_time:0;this.frame+=1}};e.prototype.drawFrontCanvas=function(){this.dirty_canvas=!1;this.ctx||(this.ctx=this.bgcanvas.getContext("2d"));var a=this.ctx;if(a){a.start2D&&a.start2D();var b=this.canvas;a.restore();a.setTransform(1,0,0,1,0,0);this.dirty_area&&(a.save(),a.beginPath(),a.rect(this.dirty_area[0],this.dirty_area[1],this.dirty_area[2],this.dirty_area[3]), +a.clip());this.clear_background&&a.clearRect(0,0,b.width,b.height);this.bgcanvas==this.canvas?this.drawBackCanvas():a.drawImage(this.bgcanvas,0,0);if(this.onRender)this.onRender(b,a);this.show_info&&this.renderInfo(a);if(this.graph){a.save();this.ds.toCanvasContext(a);for(var b=this.computeVisibleNodes(null,this.visible_nodes),d=0;d> ";b.fillText(g+d.getTitle(),0.5*a.width,40);b.restore()}d=!1;this.onRenderBackground&&(d=this.onRenderBackground(a,b));b.restore(); +b.setTransform(1,0,0,1,0,0);this.visible_links.length=0;if(this.graph){b.save();this.ds.toCanvasContext(b);if(this.background_image&&0.5this.ds.scale;if(this.live_mode){if(!a.flags.collapsed&&(b.shadowColor="transparent",a.onDrawForeground))a.onDrawForeground(b,this,this.canvas)}else{var r=this.editor_alpha;b.globalAlpha=r;this.render_shadows&&!f?(b.shadowColor= +h.DEFAULT_SHADOW_COLOR,b.shadowOffsetX=2*this.ds.scale,b.shadowOffsetY=2*this.ds.scale,b.shadowBlur=3*this.ds.scale):b.shadowColor="transparent";if(!a.flags.collapsed||!a.onDrawCollapsed||!0!=a.onDrawCollapsed(b,this)){var s=a._shape||h.BOX_SHAPE;y.set(a.size);var c=a.horizontal;if(a.flags.collapsed){b.font=this.inner_text_font;var k=a.getTitle?a.getTitle():a.title;null!=k&&(a._collapsed_width=Math.min(a.size[0],b.measureText(k).width+2*h.NODE_TITLE_HEIGHT),y[0]=a._collapsed_width,y[1]=0)}a.clip_area&& +(b.save(),b.beginPath(),s==h.BOX_SHAPE?b.rect(0,0,y[0],y[1]):s==h.ROUND_SHAPE?b.roundRect(0,0,y[0],y[1],10):s==h.CIRCLE_SHAPE&&b.arc(0.5*y[0],0.5*y[1],0.5*y[0],0,2*Math.PI),b.clip());a.has_errors&&(g="red");this.drawNodeShape(a,b,y,d,g,a.is_selected,a.mouseOver);b.shadowColor="transparent";if(a.onDrawForeground)a.onDrawForeground(b,this,this.canvas);b.textAlign=c?"center":"left";b.font=this.inner_text_font;g=!f;s=this.connecting_output;b.lineWidth=1;var k=0,q=new Float32Array(2);if(!a.flags.collapsed){if(a.inputs)for(d= +0;dthis.ds.scale,k=a._shape||a.constructor.shape||h.ROUND_SHAPE,q=a.constructor.title_mode,p=!0;q==h.TRANSPARENT_TITLE?p=!1:q==h.AUTOHIDE_TITLE&&s&&(p=!0);v[0]=0;v[1]=p?-f:0;v[2]=d[0]+1;v[3]=p?d[1]+f:d[1];s=b.globalAlpha;b.beginPath();k==h.BOX_SHAPE|| +c?b.fillRect(v[0],v[1],v[2],v[3]):k==h.ROUND_SHAPE||k==h.CARD_SHAPE?b.roundRect(v[0],v[1],v[2],v[3],this.round_radius,k==h.CARD_SHAPE?0:this.round_radius):k==h.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0],0,2*Math.PI);b.fill();a.flags.collapsed||(b.shadowColor="transparent",b.fillStyle="rgba(0,0,0,0.2)",b.fillRect(0,-1,v[2],2));b.shadowColor="transparent";if(a.onDrawBackground)a.onDrawBackground(b,this,this.canvas);if(p||q==h.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,f,d,this.ds.scale, +g);else if(q!=h.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){p=a.constructor.title_color||g;a.flags.collapsed&&(b.shadowColor=h.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var t=e.gradients[p];t||(t=e.gradients[p]=b.createLinearGradient(0,0,400,0),t.addColorStop(0,p),t.addColorStop(1,"#000"));b.fillStyle=t}else b.fillStyle=p;b.beginPath();k==h.BOX_SHAPE||c?b.rect(0,-f,d[0]+1,f):k!=h.ROUND_SHAPE&&k!=h.CARD_SHAPE||b.roundRect(0,-f,d[0]+1,f,this.round_radius,a.flags.collapsed? +this.round_radius:0);b.fill();b.shadowColor="transparent"}if(a.onDrawTitleBox)a.onDrawTitleBox(b,f,d,this.ds.scale);else k==h.ROUND_SHAPE||k==h.CIRCLE_SHAPE||k==h.CARD_SHAPE?(c&&(b.fillStyle="black",b.beginPath(),b.arc(0.5*f,-0.5*f,6,0,2*Math.PI),b.fill()),b.fillStyle=a.boxcolor||h.NODE_DEFAULT_BOXCOLOR,c?b.fillRect(0.5*f-5,-0.5*f-5,10,10):(b.beginPath(),b.arc(0.5*f,-0.5*f,5,0,2*Math.PI),b.fill())):(c&&(b.fillStyle="black",b.fillRect(0.5*(f-10)-1,-0.5*(f+10)-1,12,12)),b.fillStyle=a.boxcolor||h.NODE_DEFAULT_BOXCOLOR, +b.fillRect(0.5*(f-10),-0.5*(f+10),10,10));b.globalAlpha=s;if(a.onDrawTitleText)a.onDrawTitleText(b,f,d,this.ds.scale,this.title_text_font,r);!c&&(b.font=this.title_text_font,c=String(a.getTitle()))&&(b.fillStyle=r?"white":a.constructor.title_text_color||this.node_title_color,a.flags.collapsed?(b.textAlign="left",b.measureText(c),b.fillText(c.substr(0,20),f,h.NODE_TITLE_TEXT_Y-f),b.textAlign="left"):(b.textAlign="left",b.fillText(c,f,h.NODE_TITLE_TEXT_Y-f)));if(a.onDrawTitle)a.onDrawTitle(b)}if(r){if(a.onBounding)a.onBounding(v); +q==h.TRANSPARENT_TITLE&&(v[1]-=f,v[3]+=f);b.lineWidth=1;b.globalAlpha=0.8;b.beginPath();k==h.BOX_SHAPE?b.rect(-6+v[0],-6+v[1],12+v[2],12+v[3]):k==h.ROUND_SHAPE||k==h.CARD_SHAPE&&a.flags.collapsed?b.roundRect(-6+v[0],-6+v[1],12+v[2],12+v[3],2*this.round_radius):k==h.CARD_SHAPE?b.roundRect(-6+v[0],-6+v[1],12+v[2],12+v[3],2*this.round_radius,2):k==h.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0]+6,0,2*Math.PI);b.strokeStyle="#FFF";b.stroke();b.strokeStyle=g;b.globalAlpha=1}};var G=new Float32Array(4), +q=new Float32Array(4),p=new Float32Array(2),k=new Float32Array(2);e.prototype.drawConnections=function(a){var b=h.getTime(),d=this.visible_area;G[0]=d[0]-20;G[1]=d[1]-20;G[2]=d[2]+40;G[3]=d[3]+40;a.lineWidth=this.connections_width;a.fillStyle="#AAA";a.strokeStyle="#AAA";a.globalAlpha=this.editor_alpha;for(var d=this.graph._nodes,g=0,f=d.length;gq[2]&&(q[0]+=q[2],q[2]=Math.abs(q[2]));0>q[3]&&(q[1]+=q[3],q[3]=Math.abs(q[3]));if(u(q,G)){var n=e.outputs[z],z=r.inputs[s];if(n&&z&&(e=n.dir||(e.horizontal?h.DOWN:h.RIGHT),z=z.dir||(r.horizontal?h.UP:h.LEFT),this.renderLink(a,l,t,c,!1,0,null,e,z),c&&c._last_time&&1E3>b-c._last_time)){var n= +2-0.002*(b-c._last_time),K=a.globalAlpha;a.globalAlpha=K*n;this.renderLink(a,l,t,c,!0,n,"white",e,z);a.globalAlpha=K}}}}}}a.globalAlpha=1};e.prototype.renderLink=function(a,b,d,g,f,r,c,k,q,p){g&&this.visible_links.push(g);!c&&g&&(c=g.color||e.link_type_colors[g.type]);c||(c=this.default_link_color);null!=g&&this.highlighted_links[g.id]&&(c="#FFF");k=k||h.RIGHT;q=q||h.LEFT;var z=C(b,d);this.render_connections_border&&0.6b[1]?0:Math.PI,a.save(),a.translate(t[0],t[1]),a.rotate(l),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore(),a.save(),a.translate(p[0],p[1]),a.rotate(n),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill());if(r)for(a.fillStyle= +c,t=0;5>t;++t)r=(0.001*h.getTime()+0.2*t)%1,f=this.computeConnectionPoint(b,d,r,k,q),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill()};e.prototype.computeConnectionPoint=function(a,b,d,g,f){g=g||h.RIGHT;f=f||h.LEFT;var r=C(a,b),c=[a[0],a[1]],k=[b[0],b[1]];switch(g){case h.LEFT:c[0]+=-0.25*r;break;case h.RIGHT:c[0]+=0.25*r;break;case h.UP:c[1]+=-0.25*r;break;case h.DOWN:c[1]+=0.25*r}switch(f){case h.LEFT:k[0]+=-0.25*r;break;case h.RIGHT:k[0]+=0.25*r;break;case h.UP:k[1]+=-0.25*r;break;case h.DOWN:k[1]+= +0.25*r}g=(1-d)*(1-d)*(1-d);f=3*(1-d)*(1-d)*d;r=3*(1-d)*d*d;d*=d*d;return[g*a[0]+f*c[0]+r*k[0]+d*b[0],g*a[1]+f*c[1]+r*k[1]+d*b[1]]};e.prototype.drawExecutionOrder=function(a){a.shadowColor="transparent";a.globalAlpha=0.25;a.textAlign="center";a.strokeStyle="white";a.globalAlpha=0.75;for(var b=this.visible_nodes,d=0;dt.last_y&&st.options.max&&(t.value=t.options.max);else if("mousedown"==d.type){var l=t.options.values; +l&&l.constructor===Function&&(l=t.options.values(t,a));var z=null;"number"!=t.type&&(z=l.constructor===Array?l:Object.keys(l));c=40>c?-1:c>k-40?1:0;if("number"==t.type)t.value+=0.1*c*(t.options.step||1),null!=t.options.min&&t.valuet.options.max&&(t.value=t.options.max);else if(c)q=-1,q=l.constructor===Object?z.indexOf(String(t.value))+c:z.indexOf(t.value)+c,q>=z.length&&(q=z.length-1),0>q&&(q=0),t.value=l.constructor===Array?l[q]: +q;else{var n=l!=z?Object.values(l):l;new h.ContextMenu(n,{scale:Math.max(1,this.ds.scale),event:d,className:"dark",callback:y.bind(t)},q);var y=function(a,b,d){l!=z&&(a=n.indexOf(a));this.value=a;f(this,a);e.dirty_canvas=!0;return!1}}}else"mouseup"==d.type&&"number"==t.type&&(c=40>c?-1:c>k-40?1:0,200>d.click_time&&0==c&&this.prompt("Value",t.value,function(a){this.value=Number(a);f(this,this.value)}.bind(t),d));g!=t.value&&setTimeout(function(){f(this,this.value)}.bind(t),20);this.dirty_canvas=!0; +break;case "toggle":"mousedown"==d.type&&(t.value=!t.value,setTimeout(function(){f(t,t.value)},20));break;case "string":case "text":"mousedown"==d.type&&this.prompt("Value",t.value,function(a){this.value=a;f(this,a)}.bind(t),d);break;default:t.mouse&&t.mouse(ctx,d,[c,s],a)}return t}}return null};e.prototype.drawGroups=function(a,b){if(this.graph){var d=this.graph._groups;b.save();b.globalAlpha=0.5*this.editor_alpha;for(var g=0;gd&&0.01>b.editor_alpha&&(clearInterval(g),1>d&&(b.live_mode=!0));1"+p+""+a+"",value:p});if(q.length)return new h.ContextMenu(q, +{event:d,callback:c,parentMenu:g,allow_html:!0,node:f},b),!1}};e.decodeHTML=function(a){var b=document.createElement("div");b.innerText=a;return b.innerHTML};e.onResizeNode=function(a,b,d,g,f){f&&(f.size=f.computeSize(),f.setDirtyCanvas(!0,!0))};e.prototype.showLinkMenu=function(a,b){var d=this;console.log(a);var g=new h.ContextMenu(["Add Node",null,"Delete"],{event:b,title:null!=a.data?a.data.constructor.name:null,callback:function(b,h,c){switch(b){case "Add Node":e.onMenuAdd(null,null,c,g,function(b){console.log("node autoconnect"); +var f=d.graph.getNodeById(a.origin_id),g=d.graph.getNodeById(a.target_id);b.inputs&&b.inputs.length&&b.outputs&&b.outputs.length&&f.outputs[a.origin_slot].type==b.inputs[0].type&&b.outputs[0].type==g.inputs[0].type&&(f.connect(a.origin_slot,b,0),b.connect(0,g,a.target_slot),b.pos[0]-=0.5*b.size[0])});break;case "Delete":d.graph.removeLink(a.id)}}});return!1};e.onShowPropertyEditor=function(a,b,d,g,f){function h(){var b=q.value;"Number"==a.type?b=Number(b):"Boolean"==a.type&&(b=Boolean(b));f[c]=b; +k.parentNode&&k.parentNode.removeChild(k);f.setDirtyCanvas(!0,!0)}var c=a.property||"title";b=f[c];var k=document.createElement("div");k.className="graphdialog";k.innerHTML="";k.querySelector(".name").innerText=c;var q=k.querySelector("input");q&&(q.value=b,q.addEventListener("blur",function(a){this.focus()}),q.addEventListener("keydown",function(a){13==a.keyCode&&(h(),a.preventDefault(),a.stopPropagation())})); +b=e.active_canvas.canvas;d=b.getBoundingClientRect();var p=g=-20;d&&(g-=d.left,p-=d.top);event?(k.style.left=event.clientX+g+"px",k.style.top=event.clientY+p+"px"):(k.style.left=0.5*b.width+g+"px",k.style.top=0.5*b.height+p+"px");k.querySelector("button").addEventListener("click",h);b.parentNode.appendChild(k)};e.prototype.prompt=function(a,b,d,g){var f=this;a=a||"";var h=!1,c=document.createElement("div");c.className="graphdialog rounded";c.innerHTML=" "; +c.close=function(){f.prompt_box=null;c.parentNode&&c.parentNode.removeChild(c)};1e.search_limit)break}}q=null;if(Array.prototype.filter)q=Object.keys(h.registered_node_types).filter(l); +else for(k in q=[],h.registered_node_types)l(k)&&q.push(k);for(k=0;ke.search_limit);k++);var l=function(a){var b=h.registered_node_types[a];return s&&b.filter!=s?!1:-1!==a.toLowerCase().indexOf(d)}}}var f=this,c=e.active_canvas,k=c.canvas,q=k.ownerDocument||document,p=document.createElement("div");p.className="litegraph litesearchbox graphdialog rounded";p.innerHTML="Search
"; +p.close=function(){f.search_box=null;q.body.focus();q.body.style.overflow="";setTimeout(function(){f.canvas.focus()},20);p.parentNode&&p.parentNode.removeChild(p)};var l=null;1k.height-200&&(z.style.maxHeight=k.height-a.layerY-20+"px");u.focus();return p};e.prototype.showEditPropertyValue=function(a,b,d){function g(){f(t.value)}function f(f){"number"==typeof a.properties[b]&&(f=Number(f));if("array"==h||"object"==h)f=JSON.parse(f);a.properties[b]=f;a._graph&&a._graph._version++;if(a.onPropertyChanged)a.onPropertyChanged(b, +f);if(d.onclose)d.onclose();p.close();a.setDirtyCanvas(!0,!0)}if(a&&void 0!==a.properties[b]){d=d||{};var c=a.getPropertyInfo(b),h=c.type,k="";if("string"==h||"number"==h||"array"==h||"object"==h)k="";else if("enum"==h&&c.values){var k=""}else if("boolean"== +h)k="";else{console.warn("unknown type: "+h);return}var p=this.createDialog(""+b+""+k+"",d);if("enum"==h&&c.values){var t=p.querySelector("select");t.addEventListener("change",function(a){f(a.target.value)})}else if("boolean"==h)(t=p.querySelector("input"))&&t.addEventListener("click",function(a){f(!!t.checked)});else if(t=p.querySelector("input"))t.addEventListener("blur", +function(a){this.focus()}),e=void 0!==a.properties[b]?a.properties[b]:"",e=JSON.stringify(e),t.value=e,t.addEventListener("keydown",function(a){13==a.keyCode&&(g(),a.preventDefault(),a.stopPropagation())});p.querySelector("button").addEventListener("click",g);return p}};e.prototype.createDialog=function(a,b){b=b||{};var d=document.createElement("div");d.className="graphdialog";d.innerHTML=a;var g=this.canvas.getBoundingClientRect(),f=-20,c=-20;g&&(f-=g.left,c-=g.top);b.position?(f+=b.position[0], +c+=b.position[1]):b.event?(f+=b.event.clientX,c+=b.event.clientY):(f+=0.5*this.canvas.width,c+=0.5*this.canvas.height);d.style.left=f+"px";d.style.top=c+"px";this.canvas.parentNode.appendChild(d);d.close=function(){this.parentNode&&this.parentNode.removeChild(this)};return d};e.onMenuNodeCollapse=function(a,b,d,g,f){f.collapse()};e.onMenuNodePin=function(a,b,d,g,f){f.pin()};e.onMenuNodeMode=function(a,b,d,g,f){new h.ContextMenu(["Always","On Event","On Trigger","Never"],{event:d,callback:function(a){if(f)switch(a){case "On Event":f.mode= +h.ON_EVENT;break;case "On Trigger":f.mode=h.ON_TRIGGER;break;case "Never":f.mode=h.NEVER;break;default:f.mode=h.ALWAYS}},parentMenu:g,node:f});return!1};e.onMenuNodeColors=function(a,b,d,g,f){if(!f)throw"no node for color";b=[];b.push({value:null,content:"No color"});for(var c in e.node_colors)a=e.node_colors[c],a={value:c,content:""+c+""},b.push(a);new h.ContextMenu(b,{event:d,callback:function(a){f&&((a=a.value?e.node_colors[a.value]:null)?f.constructor===h.LGraphGroup?f.color=a.groupcolor:(f.color=a.color,f.bgcolor=a.bgcolor):(delete f.color,delete f.bgcolor),f.setDirtyCanvas(!0,!0))},parentMenu:g,node:f});return!1};e.onMenuNodeShapes=function(a,b,d,g,f){if(!f)throw"no node passed";new h.ContextMenu(h.VALID_SHAPES,{event:d,callback:function(a){f&&(f.shape=a,f.setDirtyCanvas(!0))},parentMenu:g,node:f}); +return!1};e.onMenuNodeRemove=function(a,b,d,g,f){if(!f)throw"no node passed";!1!==f.removable&&(f.graph.remove(f),f.setDirtyCanvas(!0,!0))};e.onMenuNodeClone=function(a,b,d,g,f){!1!=f.clonable&&(a=f.clone())&&(a.pos=[f.pos[0]+5,f.pos[1]+5],f.graph.add(a),f.setDirtyCanvas(!0,!0))};e.node_colors={red:{color:"#322",bgcolor:"#533",groupcolor:"#A88"},brown:{color:"#332922",bgcolor:"#593930",groupcolor:"#b06634"},green:{color:"#232",bgcolor:"#353",groupcolor:"#8A8"},blue:{color:"#223",bgcolor:"#335",groupcolor:"#88A"}, +pale_blue:{color:"#2a363b",bgcolor:"#3f5159",groupcolor:"#3f789e"},cyan:{color:"#233",bgcolor:"#355",groupcolor:"#8AA"},purple:{color:"#323",bgcolor:"#535",groupcolor:"#a1309b"},yellow:{color:"#432",bgcolor:"#653",groupcolor:"#b58b2a"},black:{color:"#222",bgcolor:"#000",groupcolor:"#444"}};e.prototype.getCanvasMenuOptions=function(){var a=null;this.getMenuOptions?a=this.getMenuOptions():(a=[{content:"Add Node",has_submenu:!0,callback:e.onMenuAdd},{content:"Add Group",callback:e.onGroupAdd}],this._graph_stack&& +0Name",f),k=h.querySelector("input");k&&c&&(k.value=c.label||"");h.querySelector("button").addEventListener("click",function(a){k.value&&(c&&(c.label=k.value),d.setDirty(!0));h.close()})}},extra:a};a&&(c.title=a.type);var k=null;a&&(k=a.getSlotInPosition(b.canvasX,b.canvasY),e.active_node= +a);if(k){f=[];k&&k.output&&k.output.links&&k.output.links.length&&f.push({content:"Disconnect Links",slot:k});var q=k.input||k.output;f.push(q.locked?"Cannot remove":{content:"Remove Slot",slot:k});f.push(q.nameLocked?"Cannot rename":{content:"Rename Slot",slot:k});c.title=(k.input?k.input.type:k.output.type)||"*";k.input&&k.input.type==h.ACTION&&(c.title="Action");k.output&&k.output.type==h.EVENT&&(c.title="Event")}else a?f=this.getNodeMenuOptions(a):(f=this.getCanvasMenuOptions(),(k=this.graph.getGroupOnPos(b.canvasX, +b.canvasY))&&f.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:k,options:this.getGroupMenuOptions(k)}}));f&&new h.ContextMenu(f,c,g)};this.CanvasRenderingContext2D&&(CanvasRenderingContext2D.prototype.roundRect=function(a,b,d,g,f,c){void 0===f&&(f=5);void 0===c&&(c=f);this.moveTo(a+f,b);this.lineTo(a+d-f,b);this.quadraticCurveTo(a+d,b,a+d,b+f);this.lineTo(a+d,b+g-c);this.quadraticCurveTo(a+d,b+g,a+d-c,b+g);this.lineTo(a+c,b+g);this.quadraticCurveTo(a,b+g,a,b+g-c);this.lineTo(a, +b+f);this.quadraticCurveTo(a,b,a+f,b)});h.compareObjects=function(a,b){for(var d in a)if(a[d]!=b[d])return!1;return!0};h.distance=C;h.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+","+Math.round(255*a[2]).toFixed()+","+(4==a.length?a[3].toFixed(2):"1.0")+")"};h.isInsideRectangle=w;h.growBounding=function(a,b,d){ba[2]&&(a[2]=b);da[3]&&(a[3]=d)};h.isInsideBounding=function(a,b){return a[0]b[1][0]||a[1]>b[1][1]?!1:!0};h.overlapBounding=u;h.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),d=0,g,f,c=0;6>c;c+=2)g="0123456789ABCDEF".indexOf(a.charAt(c)),f="0123456789ABCDEF".indexOf(a.charAt(c+1)),b[d]=16*g+f,d++;return b};h.num2hex=function(a){for(var b="#",d,g,f=0;3>f;f++)d=a[f]/16,g=a[f]%16,b+="0123456789ABCDEF".charAt(d)+"0123456789ABCDEF".charAt(g);return b};D.prototype.addItem=function(a,b,d){function g(a){var b=this.value;b&&b.has_submenu&& +f.call(this,a)}function f(a){var b=this.value,f=!0;c.current_submenu&&c.current_submenu.close(a);if(d.callback){var g=d.callback.call(this,b,d,a,c,d.node);!0===g&&(f=!1)}if(b&&(b.callback&&!d.ignore_item_callbacks&&!0!==b.disabled&&(g=b.callback.call(this,b,d,a,c,d.extra),!0===g&&(f=!1)),b.submenu)){if(!b.submenu.options)throw"ContextMenu submenu needs options";new c.constructor(b.submenu.options,{callback:b.submenu.callback,event:a,parentMenu:c,ignore_item_callbacks:b.submenu.ignore_item_callbacks, +title:b.submenu.title,extra:b.submenu.extra,autoopen:d.autoopen});f=!1}f&&!c.lock&&c.close()}var c=this;d=d||{};var h=document.createElement("div");h.className="litemenu-entry submenu";var k=!1;if(null===b)h.classList.add("separator");else{h.innerHTML=b&&b.title?b.title:a;if(h.value=b)b.disabled&&(k=!0,h.classList.add("disabled")),(b.submenu||b.has_submenu)&&h.classList.add("has_submenu");"function"==typeof b?(h.dataset.value=a,h.onclick_callback=b):h.dataset.value=b;b.className&&(h.className+=" "+ +b.className)}this.root.appendChild(h);k||h.addEventListener("click",f);d.autoopen&&h.addEventListener("mouseenter",g);return h};D.prototype.close=function(a,b){this.root.parentNode&&this.root.parentNode.removeChild(this.root);this.parentMenu&&!b&&(this.parentMenu.lock=!1,this.parentMenu.current_submenu=null,void 0===a?this.parentMenu.close():a&&!D.isCursorOverElement(a,this.parentMenu.root)&&D.trigger(this.parentMenu.root,"mouseleave",a));this.current_submenu&&this.current_submenu.close(a,!0);this.root.closing_timer&& +clearTimeout(this.root.closing_timer)};D.trigger=function(a,b,d,g){var f=document.createEvent("CustomEvent");f.initCustomEvent(b,!0,!0,d);f.srcElement=g;a.dispatchEvent?a.dispatchEvent(f):a.__events&&a.__events.dispatchEvent(f);return f};D.prototype.getTopMenu=function(){return this.options.parentMenu?this.options.parentMenu.getTopMenu():this};D.prototype.getFirstEvent=function(){return this.options.parentMenu?this.options.parentMenu.getFirstEvent():this.options.event};D.isCursorOverElement=function(a, +b){var d=a.clientX,g=a.clientY,f=b.getBoundingClientRect();return f?g>f.top&&gf.left&&dMath.abs(d))return g[1];d=(a-g[0])/d;return g[1]*(1-d)+f[1]*d}}return 0}};A.prototype.draw=function(a,b,d,g,f,c){if(d=this.points){this.size= +b;var h=b[0]-2*this.margin;b=b[1]-2*this.margin;f=f||"#666";a.save();a.translate(this.margin,this.margin);g&&(a.fillStyle="#111",a.fillRect(0,0,h,b),a.fillStyle="#222",a.fillRect(0.5*h,0,1,b),a.strokeStyle="#333",a.strokeRect(0,0,h,b));a.strokeStyle=f;c&&(a.globalAlpha=0.5);a.beginPath();for(g=0;ga[1])){var g=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=a[0]-this.margin,h=a[1]-this.margin;this.selected=this.getCloserPoint([c,h],30/b.ds.scale);-1==this.selected&&(g=[c/g,1-h/f],d.push(g),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(g),this.must_update=!0);if(-1!=this.selected)return!0}};A.prototype.onMouseMove=function(a,b){var d=this.points;if(d){var g= +this.selected;if(!(0>g)){var f=(a[0]-this.margin)/(this.size[0]-2*this.margin),c=(a[1]-this.margin)/(this.size[1]-2*this.margin);this._nearest=this.getCloserPoint([a[0]-this.margin,a[1]-this.margin],30/b.ds.scale);var h=d[g];if(h){var k=0==g||g==d.length-1;!k&&(-10>a[0]||a[0]>this.size[0]+10||-10>a[1]||a[1]>this.size[1]+10)?(d.splice(g,1),this.selected=-1):(h[0]=k?0==g?0:1:Math.clamp(f,0,1),h[1]=1-Math.clamp(c,0,1),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(h),this.must_update= +!0)}}}};A.prototype.onMouseUp=function(a,b){this.selected=-1;return!1};A.prototype.getCloserPoint=function(a,b){var d=this.points;if(!d)return-1;b=b||30;for(var g=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=d.length,h=[0,0],k=1E6,q=-1,e=0;ek||p>b||(q=e,k=p)}return q};h.CurveEditor=A;h.getParameterNames=function(a){return(a+"").replace(/[/][/].*$/gm,"").replace(/\s+/g,"").replace(/[/][*][^/*]*[*][/]/g,"").split("){", +1)[0].replace(/^[^(]*[(]/,"").replace(/=[^,]+/g,"").split(",").filter(Boolean)};Math.clamp=function(a,b,d){return b>a?b:dthis.size[0]-g.NODE_TITLE_HEIGHT&&0>b[1]){var c=this;setTimeout(function(){d.openSubgraph(c.subgraph)},10)}};m.prototype.onAction=function(a,b){this.subgraph.onAction(a,b)};m.prototype.onExecute=function(){if(this.enabled= this.getInputOrProperty("enabled")){if(this.inputs)for(var a=0;a=g?this.trigger(null,e):this._pending.push([g,e])};e.prototype.onExecute=function(){var c=1E3* -this.graph.elapsed_time;this.isInputConnected(1)&&(this.properties.time_in_ms=this.getInputData(1));for(var e=0;en[1]))return this.old_y=c.canvasY,this.captureInput(!0),this.mouse_captured=!0};p.prototype.onMouseMove=function(c){if(this.mouse_captured){var n=this.old_y-c.canvasY;c.shiftKey&&(n*=10);if(c.metaKey||c.altKey)n*=0.1;this.old_y=c.canvasY;c=this._remainder+n/p.pixels_threshold;this._remainder=c%1;c=Math.clamp(this.properties.value+ -(c|0)*this.properties.step,this.properties.min,this.properties.max);this.properties.value=c;this.graph._version++;this.setDirtyCanvas(!0)}};p.prototype.onMouseUp=function(c,n){200>c.click_time&&(this.properties.value=Math.clamp(this.properties.value+(n[1]>0.5*this.size[1]?-1:1)*this.properties.step,this.properties.min,this.properties.max),this.graph._version++,this.setDirtyCanvas(!0));this.mouse_captured&&(this.mouse_captured=!1,this.captureInput(!1))};A.registerNodeType("widget/number",p);k.title= -"Combo";k.desc="Widget to select from a list";k.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};k.prototype.onPropertyChanged=function(c,n){"values"==c?(this._values=n.split(";"),this.widget.options.values=this._values):"value"==c&&(this.widget.value=n)};A.registerNodeType("widget/combo",k);v.title="Knob";v.desc="Circular controller";v.size=[80,100];v.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/ -(this.properties.max-this.properties.min));var n=0.5*this.size[0],e=0.5*this.size[1],k=0.5*Math.min(this.size[0],this.size[1])-5;c.globalAlpha=1;c.save();c.translate(n,e);c.rotate(0.75*Math.PI);c.fillStyle="rgba(0,0,0,0.5)";c.beginPath();c.moveTo(0,0);c.arc(0,0,k,0,1.5*Math.PI);c.fill();c.strokeStyle="black";c.fillStyle=this.properties.color;c.lineWidth=2;c.beginPath();c.moveTo(0,0);c.arc(0,0,k-4,0,1.5*Math.PI*Math.max(0.01,this.value));c.closePath();c.fill();c.lineWidth=1;c.globalAlpha=1;c.restore(); -c.fillStyle="black";c.beginPath();c.arc(n,e,0.75*k,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":this.properties.color;c.beginPath();var l=this.value*Math.PI*1.5+0.75*Math.PI;c.arc(n+Math.cos(l)*k*0.65,e+Math.sin(l)*k*0.65,0.05*k,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":"#AAA";c.font=Math.floor(0.5*k)+"px Arial";c.textAlign="center";c.fillText(this.properties.value.toFixed(this.properties.precision),n,e+0.15*k)}};v.prototype.onExecute=function(){this.setOutputData(0, -this.properties.value);this.boxcolor=A.colorToString([this.value,this.value,this.value])};v.prototype.onMouseDown=function(c){this.center=[0.5*this.size[0],0.5*this.size[1]+20];this.radius=0.5*this.size[0];if(20>c.canvasY-this.pos[1]||A.distance([c.canvasX,c.canvasY],[this.pos[0]+this.center[0],this.pos[1]+this.center[1]])>this.radius)return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];this.captureInput(!0);return!0};v.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX- -this.pos[0],c.canvasY-this.pos[1]];var e=this.value,e=e-0.01*(c[1]-this.oldmouse[1]);1e&&(e=0);this.value=e;this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.oldmouse=c;this.setDirtyCanvas(!0)}};v.prototype.onMouseUp=function(c){this.oldmouse&&(this.oldmouse=null,this.captureInput(!1))};v.prototype.onPropertyChanged=function(c,e){if("min"==c||"max"==c||"value"==c)return this.properties[c]=parseFloat(e),!0};A.registerNodeType("widget/knob", -v);e.title="Inner Slider";e.prototype.onPropertyChanged=function(c,e){"value"==c&&(this.slider.value=e)};e.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};A.registerNodeType("widget/internal_slider",e);E.title="H.Slider";E.desc="Linear slider controller";E.prototype.onDrawForeground=function(c){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min));c.globalAlpha=1;c.lineWidth=1;c.fillStyle="#000";c.fillRect(2, -2,this.size[0]-4,this.size[1]-4);c.fillStyle=this.properties.color;c.beginPath();c.rect(4,4,(this.size[0]-8)*this.value,this.size[1]-8);c.fill()};E.prototype.onExecute=function(){this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.setOutputData(0,this.properties.value);this.boxcolor=A.colorToString([this.value,this.value,this.value])};E.prototype.onMouseDown=function(c){if(0>c.canvasY-this.pos[1])return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY- -this.pos[1]];this.captureInput(!0);return!0};E.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];var e=this.value,e=e+(c[0]-this.oldmouse[0])/this.size[0];1e&&(e=0);this.value=e;this.oldmouse=c;this.setDirtyCanvas(!0)}};E.prototype.onMouseUp=function(c){this.oldmouse=null;this.captureInput(!1)};E.prototype.onMouseLeave=function(c){};A.registerNodeType("widget/hslider",E);z.title="Progress";z.desc="Shows data in linear progress";z.prototype.onExecute= -function(){var c=this.getInputData(0);void 0!=c&&(this.properties.value=c)};z.prototype.onDrawForeground=function(c){c.lineWidth=1;c.fillStyle=this.properties.color;var e=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min),e=Math.min(1,e),e=Math.max(0,e);c.fillRect(2,2,(this.size[0]-4)*e,this.size[1]-4)};A.registerNodeType("widget/progress",z);y.title="Text";y.desc="Shows the input value";y.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"led_text", -text:"LED",type:"minibutton"},{name:"normal_text",text:"Normal",type:"minibutton"}];y.prototype.onDrawForeground=function(c){c.fillStyle=this.properties.color;var e=this.properties.value;this.properties.glowSize?(c.shadowColor=this.properties.color,c.shadowOffsetX=0,c.shadowOffsetY=0,c.shadowBlur=this.properties.glowSize):c.shadowColor="transparent";var k=this.properties.fontsize;c.textAlign=this.properties.align;c.font=k.toString()+"px "+this.properties.font;this.str="number"==typeof e?e.toFixed(this.properties.decimals): -e;if("string"==typeof this.str){var e=this.str.split("\\n"),p;for(p in e)c.fillText(e[p],"left"==this.properties.align?15:this.size[0]-15,-0.15*k+k*(parseInt(p)+1))}c.shadowColor="transparent";this.last_ctx=c;c.textAlign="left"};y.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(this.properties.value=c)};y.prototype.resize=function(){if(this.last_ctx){var c=this.str.split("\\n");this.last_ctx.font=this.properties.fontsize+"px "+this.properties.font;var e=0,k;for(k in c){var p=this.last_ctx.measureText(c[k]).width; -e=h?this.trigger(null,e):this._pending.push([h,e])};e.prototype.onExecute=function(){var c=1E3* +this.graph.elapsed_time;this.isInputConnected(1)&&(this.properties.time_in_ms=this.getInputData(1));for(var e=0;ee[1]))return this.old_y=c.canvasY,this.captureInput(!0),this.mouse_captured=!0};n.prototype.onMouseMove=function(c){if(this.mouse_captured){var e=this.old_y-c.canvasY;c.shiftKey&&(e*=10);if(c.metaKey||c.altKey)e*=0.1;this.old_y=c.canvasY;c=this._remainder+e/n.pixels_threshold;this._remainder=c%1;c=Math.clamp(this.properties.value+ +(c|0)*this.properties.step,this.properties.min,this.properties.max);this.properties.value=c;this.graph._version++;this.setDirtyCanvas(!0)}};n.prototype.onMouseUp=function(c,e){200>c.click_time&&(this.properties.value=Math.clamp(this.properties.value+(e[1]>0.5*this.size[1]?-1:1)*this.properties.step,this.properties.min,this.properties.max),this.graph._version++,this.setDirtyCanvas(!0));this.mouse_captured&&(this.mouse_captured=!1,this.captureInput(!1))};A.registerNodeType("widget/number",n);l.title= +"Combo";l.desc="Widget to select from a list";l.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};l.prototype.onPropertyChanged=function(c,e){"values"==c?(this._values=e.split(";"),this.widget.options.values=this._values):"value"==c&&(this.widget.value=e)};A.registerNodeType("widget/combo",l);x.title="Knob";x.desc="Circular controller";x.size=[80,100];x.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/ +(this.properties.max-this.properties.min));var e=0.5*this.size[0],l=0.5*this.size[1],n=0.5*Math.min(this.size[0],this.size[1])-5;c.globalAlpha=1;c.save();c.translate(e,l);c.rotate(0.75*Math.PI);c.fillStyle="rgba(0,0,0,0.5)";c.beginPath();c.moveTo(0,0);c.arc(0,0,n,0,1.5*Math.PI);c.fill();c.strokeStyle="black";c.fillStyle=this.properties.color;c.lineWidth=2;c.beginPath();c.moveTo(0,0);c.arc(0,0,n-4,0,1.5*Math.PI*Math.max(0.01,this.value));c.closePath();c.fill();c.lineWidth=1;c.globalAlpha=1;c.restore(); +c.fillStyle="black";c.beginPath();c.arc(e,l,0.75*n,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":this.properties.color;c.beginPath();var u=this.value*Math.PI*1.5+0.75*Math.PI;c.arc(e+Math.cos(u)*n*0.65,l+Math.sin(u)*n*0.65,0.05*n,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":"#AAA";c.font=Math.floor(0.5*n)+"px Arial";c.textAlign="center";c.fillText(this.properties.value.toFixed(this.properties.precision),e,l+0.15*n)}};x.prototype.onExecute=function(){this.setOutputData(0, +this.properties.value);this.boxcolor=A.colorToString([this.value,this.value,this.value])};x.prototype.onMouseDown=function(c){this.center=[0.5*this.size[0],0.5*this.size[1]+20];this.radius=0.5*this.size[0];if(20>c.canvasY-this.pos[1]||A.distance([c.canvasX,c.canvasY],[this.pos[0]+this.center[0],this.pos[1]+this.center[1]])>this.radius)return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];this.captureInput(!0);return!0};x.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX- +this.pos[0],c.canvasY-this.pos[1]];var e=this.value,e=e-0.01*(c[1]-this.oldmouse[1]);1e&&(e=0);this.value=e;this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.oldmouse=c;this.setDirtyCanvas(!0)}};x.prototype.onMouseUp=function(c){this.oldmouse&&(this.oldmouse=null,this.captureInput(!1))};x.prototype.onPropertyChanged=function(c,e){if("min"==c||"max"==c||"value"==c)return this.properties[c]=parseFloat(e),!0};A.registerNodeType("widget/knob", +x);e.title="Inner Slider";e.prototype.onPropertyChanged=function(c,e){"value"==c&&(this.slider.value=e)};e.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};A.registerNodeType("widget/internal_slider",e);C.title="H.Slider";C.desc="Linear slider controller";C.prototype.onDrawForeground=function(c){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min));c.globalAlpha=1;c.lineWidth=1;c.fillStyle="#000";c.fillRect(2, +2,this.size[0]-4,this.size[1]-4);c.fillStyle=this.properties.color;c.beginPath();c.rect(4,4,(this.size[0]-8)*this.value,this.size[1]-8);c.fill()};C.prototype.onExecute=function(){this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.setOutputData(0,this.properties.value);this.boxcolor=A.colorToString([this.value,this.value,this.value])};C.prototype.onMouseDown=function(c){if(0>c.canvasY-this.pos[1])return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY- +this.pos[1]];this.captureInput(!0);return!0};C.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];var e=this.value,e=e+(c[0]-this.oldmouse[0])/this.size[0];1e&&(e=0);this.value=e;this.oldmouse=c;this.setDirtyCanvas(!0)}};C.prototype.onMouseUp=function(c){this.oldmouse=null;this.captureInput(!1)};C.prototype.onMouseLeave=function(c){};A.registerNodeType("widget/hslider",C);w.title="Progress";w.desc="Shows data in linear progress";w.prototype.onExecute= +function(){var c=this.getInputData(0);void 0!=c&&(this.properties.value=c)};w.prototype.onDrawForeground=function(c){c.lineWidth=1;c.fillStyle=this.properties.color;var e=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min),e=Math.min(1,e),e=Math.max(0,e);c.fillRect(2,2,(this.size[0]-4)*e,this.size[1]-4)};A.registerNodeType("widget/progress",w);u.title="Text";u.desc="Shows the input value";u.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"led_text", +text:"LED",type:"minibutton"},{name:"normal_text",text:"Normal",type:"minibutton"}];u.prototype.onDrawForeground=function(c){c.fillStyle=this.properties.color;var e=this.properties.value;this.properties.glowSize?(c.shadowColor=this.properties.color,c.shadowOffsetX=0,c.shadowOffsetY=0,c.shadowBlur=this.properties.glowSize):c.shadowColor="transparent";var l=this.properties.fontsize;c.textAlign=this.properties.align;c.font=l.toString()+"px "+this.properties.font;this.str="number"==typeof e?e.toFixed(this.properties.decimals): +e;if("string"==typeof this.str){var e=this.str.split("\\n"),n;for(n in e)c.fillText(e[n],"left"==this.properties.align?15:this.size[0]-15,-0.15*l+l*(parseInt(n)+1))}c.shadowColor="transparent";this.last_ctx=c;c.textAlign="left"};u.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(this.properties.value=c)};u.prototype.resize=function(){if(this.last_ctx){var c=this.str.split("\\n");this.last_ctx.font=this.properties.fontsize+"px "+this.properties.font;var e=0,l;for(l in c){var n=this.last_ctx.measureText(c[l]).width; +ek?p.xbox.axes.lx:0,this._left_axis[1]=Math.abs(p.xbox.axes.ly)>k?p.xbox.axes.ly:0,this._right_axis[0]=Math.abs(p.xbox.axes.rx)>k?p.xbox.axes.rx:0,this._right_axis[1]=Math.abs(p.xbox.axes.ry)>k?p.xbox.axes.ry:0,this._triggers[0]=Math.abs(p.xbox.axes.ltrigger)>k?p.xbox.axes.ltrigger: -0,this._triggers[1]=Math.abs(p.xbox.axes.rtrigger)>k?p.xbox.axes.rtrigger:0);if(this.outputs)for(k=0;kp;p++)if(k[p]){p=k[p];k=this.xbox_mapping;k||(k=this.xbox_mapping={axes:[], -buttons:{},hat:"",hatmap:c.CENTER});k.axes.lx=p.axes[0];k.axes.ly=p.axes[1];k.axes.rx=p.axes[2];k.axes.ry=p.axes[3];k.axes.ltrigger=p.buttons[6].value;k.axes.rtrigger=p.buttons[7].value;k.hat="";k.hatmap=c.CENTER;for(var m=0;mm)k.buttons[c.mapping_array[m]]=p.buttons[m].pressed,p.buttons[m].was_pressed&&this.trigger(c.mapping_array[m]+"_button_event");else switch(m){case 12:p.buttons[m].pressed&&(k.hat+="up",k.hatmap|=c.UP); -break;case 13:p.buttons[m].pressed&&(k.hat+="down",k.hatmap|=c.DOWN);break;case 14:p.buttons[m].pressed&&(k.hat+="left",k.hatmap|=c.LEFT);break;case 15:p.buttons[m].pressed&&(k.hat+="right",k.hatmap|=c.RIGHT);break;case 16:k.buttons.home=p.buttons[m].pressed}p.xbox=k;return p}};c.prototype.onDrawBackground=function(c){if(!this.flags.collapsed){var k=this._left_axis,m=this._right_axis;c.strokeStyle="#88A";c.strokeRect(0.5*(k[0]+1)*this.size[0]-4,0.5*(k[1]+1)*this.size[1]-4,8,8);c.strokeStyle="#8A8"; -c.strokeRect(0.5*(m[0]+1)*this.size[0]-4,0.5*(m[1]+1)*this.size[1]-4,8,8);k=this.size[1]/this._current_buttons.length;c.fillStyle="#AEB";for(m=0;ml?n.xbox.axes.lx:0,this._left_axis[1]=Math.abs(n.xbox.axes.ly)>l?n.xbox.axes.ly:0,this._right_axis[0]=Math.abs(n.xbox.axes.rx)>l?n.xbox.axes.rx:0,this._right_axis[1]=Math.abs(n.xbox.axes.ry)>l?n.xbox.axes.ry:0,this._triggers[0]=Math.abs(n.xbox.axes.ltrigger)>l?n.xbox.axes.ltrigger: +0,this._triggers[1]=Math.abs(n.xbox.axes.rtrigger)>l?n.xbox.axes.rtrigger:0);if(this.outputs)for(l=0;ln;n++)if(l[n]){n=l[n];l=this.xbox_mapping;l||(l=this.xbox_mapping={axes:[], +buttons:{},hat:"",hatmap:c.CENTER});l.axes.lx=n.axes[0];l.axes.ly=n.axes[1];l.axes.rx=n.axes[2];l.axes.ry=n.axes[3];l.axes.ltrigger=n.buttons[6].value;l.axes.rtrigger=n.buttons[7].value;l.hat="";l.hatmap=c.CENTER;for(var m=0;mm)l.buttons[c.mapping_array[m]]=n.buttons[m].pressed,n.buttons[m].was_pressed&&this.trigger(c.mapping_array[m]+"_button_event");else switch(m){case 12:n.buttons[m].pressed&&(l.hat+="up",l.hatmap|=c.UP); +break;case 13:n.buttons[m].pressed&&(l.hat+="down",l.hatmap|=c.DOWN);break;case 14:n.buttons[m].pressed&&(l.hat+="left",l.hatmap|=c.LEFT);break;case 15:n.buttons[m].pressed&&(l.hat+="right",l.hatmap|=c.RIGHT);break;case 16:l.buttons.home=n.buttons[m].pressed}n.xbox=l;return n}};c.prototype.onDrawBackground=function(c){if(!this.flags.collapsed){var l=this._left_axis,m=this._right_axis;c.strokeStyle="#88A";c.strokeRect(0.5*(l[0]+1)*this.size[0]-4,0.5*(l[1]+1)*this.size[1]-4,8,8);c.strokeStyle="#8A8"; +c.strokeRect(0.5*(m[0]+1)*this.size[0]-4,0.5*(m[1]+1)*this.size[1]-4,8,8);l=this.size[1]/this._current_buttons.length;c.fillStyle="#AEB";for(m=0;m","enum",{values:a.values});this.size=[80,60]}function b(){this.addInput("inc","number");this.addOutput("total","number");this.addProperty("increment",1);this.addProperty("value",0)}function d(){this.addInput("v","number");this.addOutput("sin","number");this.addProperty("amplitude",1);this.addProperty("offset",0);this.bgImageUrl= -"nodes/imgs/icon-sin.png"}function h(){this.addInput("x","number");this.addInput("y","number");this.addOutput("","number");this.properties={x:1,y:1,formula:"x+y"};this.code_widget=this.addWidget("text","F(x,y)",this.properties.formula,function(a,b,d){d.properties.formula=a});this.addWidget("toggle","allow",F.allow_scripts,function(a){F.allow_scripts=a});this._func=null}function f(){this.addInput("vec2","vec2");this.addOutput("x","number");this.addOutput("y","number")}function s(){this.addInputs([["x", -"number"],["y","number"]]);this.addOutput("vec2","vec2");this.properties={x:0,y:0};this._data=new Float32Array(2)}function r(){this.addInput("vec3","vec3");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number")}function I(){this.addInputs([["x","number"],["y","number"],["z","number"]]);this.addOutput("vec3","vec3");this.properties={x:0,y:0,z:0};this._data=new Float32Array(3)}function J(){this.addInput("vec4","vec4");this.addOutput("x","number");this.addOutput("y","number"); -this.addOutput("z","number");this.addOutput("w","number")}function H(){this.addInputs([["x","number"],["y","number"],["z","number"],["w","number"]]);this.addOutput("vec4","vec4");this.properties={x:0,y:0,z:0,w:0};this._data=new Float32Array(4)}var F=C.LiteGraph;c.title="Converter";c.desc="type A to type B";c.prototype.onExecute=function(){var a=this.getInputData(0);if(null!=a&&this.outputs)for(var b=0;ba&&(a+=1024);var c=Math.floor(a);a-=c;d=e.data[c];c=e.data[1023==c?0:c+1];b&&(a=a*a*a*(a*(6*a-15)+10));return d*(1-a)+c*a};e.prototype.onExecute=function(){var a=this.getInputData(0)||0,a=e.getValue(a,this.properties.smooth), -b=this.properties.min;this._last_v=a*(this.properties.max-b)+b;this.setOutputData(0,this._last_v)};e.prototype.onDrawBackground=function(a){this.outputs[0].label=(this._last_v||0).toFixed(3)};F.registerNodeType("math/noise",e);E.title="Spikes";E.desc="spike every random time";E.prototype.onExecute=function(){var a=this.graph.elapsed_time;this._remaining_time-=a;this._blink_time-=a;a=0;0this._remaining_time?(this._remaining_time= -Math.random()*(this.properties.max_time-this.properties.min_time)+this.properties.min_time,this._blink_time=this.properties.duration,this.boxcolor="#FFF"):this.boxcolor="#000";this.setOutputData(0,a)};F.registerNodeType("math/spikes",E);z.title="Clamp";z.desc="Clamp number between min and max";z.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(a=Math.max(this.properties.min,a),a=Math.min(this.properties.max,a),this.setOutputData(0,a))};z.prototype.getCode=function(a){a="";this.isInputConnected(0)&& -(a+="clamp({{0}},"+this.properties.min+","+this.properties.max+")");return a};F.registerNodeType("math/clamp",z);y.title="Lerp";y.desc="Linear Interpolation";y.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.getInputData(1);null==b&&(b=0);var d=this.properties.f,c=this.getInputData(2);void 0!==c&&(d=c);this.setOutputData(0,a*(1-d)+b*d)};y.prototype.onGetInputs=function(){return[["f","number"]]};F.registerNodeType("math/lerp",y);D.title="Abs";D.desc="Absolute";D.prototype.onExecute= -function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.abs(a))};F.registerNodeType("math/abs",D);A.title="Floor";A.desc="Floor number to remove fractional part";A.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.floor(a))};F.registerNodeType("math/floor",A);g.title="Frac";g.desc="Returns fractional part";g.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a%1)};F.registerNodeType("math/frac",g);n.title= -"Smoothstep";n.desc="Smoothstep";n.prototype.onExecute=function(){var a=this.getInputData(0);if(void 0!==a){var b=this.properties.A,a=Math.clamp((a-b)/(this.properties.B-b),0,1);this.setOutputData(0,a*a*(3-2*a))}};F.registerNodeType("math/smoothstep",n);t.title="Scale";t.desc="v * factor";t.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a*this.properties.factor)};F.registerNodeType("math/scale",t);B.title="Gate";B.desc="if v is true, then outputs A, otherwise B"; -B.prototype.onExecute=function(){var a=this.getInputData(0);this.setOutputData(0,this.getInputData(a?1:2))};F.registerNodeType("math/gate",B);l.title="Average";l.desc="Average Filter";l.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this._values.length;this._values[this._current%b]=a;this._current+=1;this._current>b&&(this._current=0);for(var d=a=0;db&&(b=1);this.properties.samples= -Math.round(b);var d=this._values;this._values=new Float32Array(this.properties.samples);d.length<=this._values.length?this._values.set(d):this._values.set(d.subarray(0,this._values.length))};F.registerNodeType("math/average",l);q.title="TendTo";q.desc="moves the output value always closer to the input";q.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.factor;this._value=null==this._value?a:this._value*(1-b)+a*b;this.setOutputData(0,this._value)};F.registerNodeType("math/tendTo", -q);w.values="+ - * / % ^ max min".split(" ");w.title="Operation";w.desc="Easy math operators";w["@OP"]={type:"enum",title:"operation",values:w.values};w.size=[100,60];w.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"};w.prototype.setValue=function(a){"string"==typeof a&&(a=parseFloat(a));this.properties.value=a};w.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);null!=a? -this.properties.A=a:a=this.properties.A;null!=b?this.properties.B=b:b=this.properties.B;var d=0;switch(this.properties.OP){case "+":d=a+b;break;case "-":d=a-b;break;case "x":case "X":case "*":d=a*b;break;case "/":d=a/b;break;case "%":d=a%b;break;case "^":d=Math.pow(a,b);break;case "max":d=Math.max(a,b);break;case "min":d=Math.min(a,b);break;default:console.warn("Unknown operation: "+this.properties.OP)}this.setOutputData(0,d)};w.prototype.onDrawBackground=function(a){this.flags.collapsed||(a.font= -"40px Arial",a.fillStyle="#666",a.textAlign="center",a.fillText(this.properties.OP,0.5*this.size[0],0.5*(this.size[1]+F.NODE_TITLE_HEIGHT)),a.textAlign="left")};F.registerNodeType("math/operation",w);F.registerSearchboxExtra("math/operation","MAX",{properties:{OP:"max"},title:"MAX()"});F.registerSearchboxExtra("math/operation","MIN",{properties:{OP:"min"},title:"MIN()"});x.title="Compare";x.desc="compares between two values";x.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1); -void 0!==a?this.properties.A=a:a=this.properties.A;void 0!==b?this.properties.B=b:b=this.properties.B;for(var d=0,c=this.outputs.length;dB":f=a>b;break;case "A=B":f=a>=b}this.setOutputData(d,f)}}};x.prototype.onGetOutputs=function(){return[["A==B","boolean"],["A!=B","boolean"],["A>B","boolean"],["A=B", -"boolean"],["A<=B","boolean"]]};F.registerNodeType("math/compare",x);F.registerSearchboxExtra("math/compare","==",{outputs:[["A==B","boolean"]],title:"A==B"});F.registerSearchboxExtra("math/compare","!=",{outputs:[["A!=B","boolean"]],title:"A!=B"});F.registerSearchboxExtra("math/compare",">",{outputs:[["A>B","boolean"]],title:"A>B"});F.registerSearchboxExtra("math/compare","<",{outputs:[["A=",{outputs:[["A>=B","boolean"]],title:"A>=B"}); -F.registerSearchboxExtra("math/compare","<=",{outputs:[["A<=B","boolean"]],title:"A<=B"});a.values="> < == != <= >= || &&".split(" ");a["@OP"]={type:"enum",title:"operation",values:a.values};a.title="Condition";a.desc="evaluates condition between A and B";a.prototype.getTitle=function(){return"A "+this.properties.OP+" B"};a.prototype.onExecute=function(){var a=this.getInputData(0);void 0===a?a=this.properties.A:this.properties.A=a;var b=this.getInputData(1);void 0===b?b=this.properties.B:this.properties.B= -b;var d=!0;switch(this.properties.OP){case ">":d=a>b;break;case "<":d=a=":d=a>=b;break;case "||":d=a||b;break;case "&&":d=a&&b}this.setOutputData(0,d);this.setOutputData(1,!d)};F.registerNodeType("math/condition",a);b.title="Accumulate";b.desc="Increments a value every time";b.prototype.onExecute=function(){null===this.properties.value&&(this.properties.value=0);var a=this.getInputData(0);this.properties.value=null!== -a?this.properties.value+a:this.properties.value+this.properties.increment;this.setOutputData(0,this.properties.value)};F.registerNodeType("math/accumulate",b);d.title="Trigonometry";d.desc="Sin Cos Tan";d.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.amplitude,d=this.findInputSlot("amplitude");-1!=d&&(b=this.getInputData(d));var c=this.properties.offset,d=this.findInputSlot("offset");-1!=d&&(c=this.getInputData(d));for(var d=0,h=this.outputs.length;d< -h;++d){var f;switch(this.outputs[d].name){case "sin":f=Math.sin(a);break;case "cos":f=Math.cos(a);break;case "tan":f=Math.tan(a);break;case "asin":f=Math.asin(a);break;case "acos":f=Math.acos(a);break;case "atan":f=Math.atan(a)}this.setOutputData(d,b*f+c)}};d.prototype.onGetInputs=function(){return[["v","number"],["amplitude","number"],["offset","number"]]};d.prototype.onGetOutputs=function(){return[["sin","number"],["cos","number"],["tan","number"],["asin","number"],["acos","number"],["atan","number"]]}; -F.registerNodeType("math/trigonometry",d);F.registerSearchboxExtra("math/trigonometry","SIN()",{outputs:[["sin","number"]],title:"SIN()"});F.registerSearchboxExtra("math/trigonometry","COS()",{outputs:[["cos","number"]],title:"COS()"});F.registerSearchboxExtra("math/trigonometry","TAN()",{outputs:[["tan","number"]],title:"TAN()"});h.title="Formula";h.desc="Compute formula";h.size=[160,100];l.prototype.onPropertyChanged=function(a,b){"formula"==a&&(this.code_widget.value=b)};h.prototype.onExecute= -function(){if(F.allow_scripts){var a=this.getInputData(0),b=this.getInputData(1);null!=a?this.properties.x=a:a=this.properties.x;null!=b?this.properties.y=b:b=this.properties.y;var d;try{this._func&&this._func_code==this.properties.formula||(this._func=new Function("x","y","TIME","return "+this.properties.formula),this._func_code=this.properties.formula),d=this._func(a,b,this.graph.globaltime),this.boxcolor=null}catch(c){this.boxcolor="red"}this.setOutputData(0,d)}};h.prototype.getTitle=function(){return this._func_code|| -"Formula"};h.prototype.onDrawBackground=function(){var a=this.properties.formula;this.outputs&&this.outputs.length&&(this.outputs[0].label=a)};F.registerNodeType("math/formula",h);f.title="Vec2->XY";f.desc="vector 2 to components";f.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]))};F.registerNodeType("math3d/vec2-to-xy",f);s.title="XY->Vec2";s.desc="components to vector2";s.prototype.onExecute=function(){var a=this.getInputData(0); -null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this._data;d[0]=a;d[1]=b;this.setOutputData(0,d)};F.registerNodeType("math3d/xy-to-vec2",s);r.title="Vec3->XYZ";r.desc="vector 3 to components";r.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]))};F.registerNodeType("math3d/vec3-to-xyz",r);I.title="XYZ->Vec3";I.desc="components to vector3";I.prototype.onExecute= -function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this._data;c[0]=a;c[1]=b;c[2]=d;this.setOutputData(0,c)};F.registerNodeType("math3d/xyz-to-vec3",I);J.title="Vec4->XYZW";J.desc="vector 4 to components";J.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]),this.setOutputData(3, -a[3]))};F.registerNodeType("math3d/vec4-to-xyzw",J);H.title="XYZW->Vec4";H.desc="components to vector4";H.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this.getInputData(3);null==c&&(c=this.properties.w);var h=this._data;h[0]=a;h[1]=b;h[2]=d;h[3]=c;this.setOutputData(0,h)};F.registerNodeType("math3d/xyzw-to-vec4",H)})(this); -(function(C){function c(){this.addInput("T","vec3");this.addInput("R","vec3");this.addInput("S","vec3");this.addOutput("mat4","mat4");this.properties={T:[0,0,0],R:[0,0,0],S:[1,1,1],R_in_degrees:!0};this._result=mat4.create();this._must_update=!0}function m(){this.addInput("A","number,vec3");this.addInput("B","number,vec3");this.addOutput("=","vec3");this.addProperty("OP","+","enum",{values:m.values});this._result=vec3.create()}function p(){this.addInput("in","vec3");this.addInput("f","number");this.addOutput("out", -"vec3");this.properties={f:1};this._data=new Float32Array(3)}function k(){this.addInput("in","vec3");this.addOutput("out","number")}function v(){this.addInput("in","vec3");this.addOutput("out","vec3");this._data=new Float32Array(3)}function e(){this.addInput("A","vec3");this.addInput("B","vec3");this.addInput("f","vec3");this.addOutput("out","vec3");this.properties={f:0.5};this._data=new Float32Array(3)}function E(){this.addInput("A","vec3");this.addInput("B","vec3");this.addOutput("out","number")} -var z=C.LiteGraph;c.title="mat4";c.temp_quat=new Float32Array([0,0,0,1]);c.temp_mat4=new Float32Array(16);c.temp_vec3=new Float32Array(3);c.prototype.onPropertyChanged=function(c,e){this._must_update=!0};c.prototype.onExecute=function(){var e=this._result,k=c.temp_quat,p=c.temp_mat4,g=c.temp_vec3,n=this.getInputData(0),t=this.getInputData(1),m=this.getInputData(2);if(this._must_update||n||t||m)n=n||this.properties.T,t=t||this.properties.R,m=m||this.properties.S,mat4.identity(e),mat4.translate(e,e, -n),this.properties.R_in_degrees?(g.set(t),vec3.scale(g,g,DEG2RAD),quat.fromEuler(k,g)):quat.fromEuler(k,t),mat4.fromQuat(p,k),mat4.multiply(e,e,p),mat4.scale(e,e,m);this.setOutputData(0,e)};z.registerNodeType("math3d/mat4",c);m.values="+ - * / % ^ max min".split(" ");m.title="Operation";m.desc="Easy math 3D operators";m["@OP"]={type:"enum",title:"operation",values:m.values};m.size=[100,60];m.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+ -"(A,B)":"A "+this.properties.OP+" B"};m.prototype.onExecute=function(){var c=this.getInputData(0),e=this.getInputData(1);if(null!=c&&null!=e){c.constructor===Number&&(c=[c,c,c]);e.constructor===Number&&(e=[e,e,e]);var k=this._result;switch(this.properties.OP){case "+":k=vec3.add(k,c,e);break;case "-":k=vec3.sub(k,c,e);break;case "x":case "X":case "*":k=vec3.mul(k,c,e);break;case "/":k=vec3.div(k,c,e);break;case "%":k[0]=c[0]%e[0];k[1]=c[1]%e[1];k[2]=c[2]%e[2];break;case "^":k[0]=Math.pow(c[0],e[0]); -k[1]=Math.pow(c[1],e[1]);k[2]=Math.pow(c[2],e[2]);break;case "max":k[0]=Math.max(c[0],e[0]);k[1]=Math.max(c[1],e[1]);k[2]=Math.max(c[2],e[2]);break;case "min":k[0]=Math.min(c[0],e[0]);k[1]=Math.min(c[1],e[1]);k[2]=Math.min(c[2],e[2]);break;default:console.warn("Unknown operation: "+this.properties.OP)}this.setOutputData(0,k)}};m.prototype.onDrawBackground=function(c){this.flags.collapsed||(c.font="40px Arial",c.fillStyle="#666",c.textAlign="center",c.fillText(this.properties.OP,0.5*this.size[0],0.5* -(this.size[1]+z.NODE_TITLE_HEIGHT)),c.textAlign="left")};z.registerNodeType("math3d/operation",m);p.title="vec3_scale";p.desc="scales the components of a vec3";p.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null==e&&(e=this.properties.f);var k=this._data;k[0]=c[0]*e;k[1]=c[1]*e;k[2]=c[2]*e;this.setOutputData(0,k)}};z.registerNodeType("math3d/vec3-scale",p);k.title="vec3_length";k.desc="returns the module of a vector";k.prototype.onExecute=function(){var c= -this.getInputData(0);null!=c&&(c=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),this.setOutputData(0,c))};z.registerNodeType("math3d/vec3-length",k);v.title="vec3_normalize";v.desc="returns the vector normalized";v.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),k=this._data;k[0]=c[0]/e;k[1]=c[1]/e;k[2]=c[2]/e;this.setOutputData(0,k)}};z.registerNodeType("math3d/vec3-normalize",v);e.title="vec3_lerp";e.desc="returns the interpolated vector"; -e.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);if(null!=e){var k=this.getInputOrProperty("f"),g=this._data;g[0]=c[0]*(1-k)+e[0]*k;g[1]=c[1]*(1-k)+e[1]*k;g[2]=c[2]*(1-k)+e[2]*k;this.setOutputData(0,g)}}};z.registerNodeType("math3d/vec3-lerp",e);E.title="vec3_dot";E.desc="returns the dot product";E.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null!=e&&this.setOutputData(0,c[0]*e[0]+c[1]*e[1]+c[2]* -e[2])}};z.registerNodeType("math3d/vec3-dot",E);C.glMatrix?(C=function(){this.addOutput("quat","quat");this.properties={x:0,y:0,z:0,w:1,normalize:!1};this._value=quat.create()},C.title="Quaternion",C.desc="quaternion",C.prototype.onExecute=function(){this._value[0]=this.getInputOrProperty("x");this._value[1]=this.getInputOrProperty("y");this._value[2]=this.getInputOrProperty("z");this._value[3]=this.getInputOrProperty("w");this.properties.normalize&&quat.normalize(this._value,this._value);this.setOutputData(0, -this._value)},C.prototype.onGetInputs=function(){return[["x","number"],["y","number"],["z","number"],["w","number"]]},z.registerNodeType("math3d/quaternion",C),C=function(){this.addInputs([["degrees","number"],["axis","vec3"]]);this.addOutput("quat","quat");this.properties={angle:90,axis:vec3.fromValues(0,1,0)};this._value=quat.create()},C.title="Rotation",C.desc="quaternion rotation",C.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.angle);var e=this.getInputData(1); -null==e&&(e=this.properties.axis);c=quat.setAxisAngle(this._value,e,0.0174532925*c);this.setOutputData(0,c)},z.registerNodeType("math3d/rotation",C),C=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},C.title="Rot. Vec3",C.desc="rotate a point",C.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.vec);var e=this.getInputData(1);null==e?this.setOutputData(c):this.setOutputData(0,vec3.transformQuat(vec3.create(), -c,e))},z.registerNodeType("math3d/rotate_vec3",C),C=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},C.title="Mult. Quat",C.desc="rotate quaternion",C.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null!=e&&(c=quat.multiply(this._value,c,e),this.setOutputData(0,c))}},z.registerNodeType("math3d/mult-quat",C),C=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp", -"quat");this.addProperty("factor",0.5);this._value=quat.create()},C.title="Quat Slerp",C.desc="quaternion spherical interpolation",C.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);if(null!=e){var k=this.properties.factor;null!=this.getInputData(2)&&(k=this.getInputData(2));c=quat.slerp(this._value,c,e,k);this.setOutputData(0,c)}}},z.registerNodeType("math3d/quat-slerp",C),C=function(){this.addInput("vec3","vec3");this.addOutput("remap","vec3");this.addOutput("clamped", -"vec3");this.properties={clamp:!0,range_min:[-1,-1,0],range_max:[1,1,0],target_min:[-1,-1,0],target_max:[1,1,0]};this._value=vec3.create();this._clamped=vec3.create()},C.title="Remap Range",C.desc="remap a 3D range",C.prototype.onExecute=function(){var c=this.getInputData(0);c&&this._value.set(c);for(var c=this.properties.range_min,e=this.properties.range_max,k=this.properties.target_min,g=this.properties.target_max,n=0;3>n;++n){var p=e[n]-c[n];this._clamped[n]=Math.clamp(this._value[n],c[n],e[n]); -0==p?this._value[n]=0.5*(k[n]+g[n]):(p=(this._value[n]-c[n])/p,this.properties.clamp&&(p=Math.clamp(p,0,1)),this._value[n]=k[n]+p*(g[n]-k[n]))}this.setOutputData(0,this._value);this.setOutputData(1,this._clamped)},z.registerNodeType("math3d/remap_range",C)):console.warn("No glmatrix found, some Math3D nodes may not work")})(this); -(function(C){function c(c,k){return c==k}function m(){this.addInput("","string");this.addOutput("table","table");this.addOutput("rows","number");this.addProperty("value","");this.addProperty("separator",",");this._table=null}C=C.LiteGraph;C.wrapFunctionAsNode("string/toString",c,["*"],"String");C.wrapFunctionAsNode("string/compare",c,["string","string"],"boolean");C.wrapFunctionAsNode("string/concatenate",function(c,k){return void 0===c?k:void 0===k?c:c+k},["string","string"],"string");C.wrapFunctionAsNode("string/contains", -function(c,k){return void 0===c||void 0===k?!1:-1!=c.indexOf(k)},["string","string"],"boolean");C.wrapFunctionAsNode("string/toUpperCase",function(c){return null!=c&&c.constructor===String?c.toUpperCase():c},["string"],"string");C.wrapFunctionAsNode("string/split",function(c,k){null==k&&(k=this.properties.separator);if(null==c)return[];if(c.constructor===String)return c.split(k||" ");if(c.constructor===Array){for(var m=[],e=0;ee;++e){var g=this.getInputData(e);if(null!=g){var l=this.values[e];l.push(g);l.length>c[0]&&l.shift()}}}};c.prototype.onDrawBackground=function(e){if(!this.flags.collapsed){var g=this.size,k=0.5*g[1]/ -this.properties.scale,l=c.colors,q=0.5*g[1];e.fillStyle="#000";e.fillRect(0,0,g[0],g[1]);e.strokeStyle="#555";e.beginPath();e.moveTo(0,q);e.lineTo(g[0],q);e.stroke();if(this.inputs)for(var m=0;4>m;++m){var x=this.values[m];if(this.inputs[m]&&this.inputs[m].link){e.strokeStyle=l[m];e.beginPath();var a=x[0]*k*-1+q;e.moveTo(0,Math.clamp(a,0,g[1]));for(var b=1;b","enum",{values:a.values});this.size=[80,60]}function b(){this.addInput("inc","number");this.addOutput("total","number");this.addProperty("increment",1);this.addProperty("value",0)}function d(){this.addInput("v","number");this.addOutput("sin","number");this.addProperty("amplitude", +1);this.addProperty("offset",0);this.bgImageUrl="nodes/imgs/icon-sin.png"}function g(){this.addInput("x","number");this.addInput("y","number");this.addOutput("","number");this.properties={x:1,y:1,formula:"x+y"};this.code_widget=this.addWidget("text","F(x,y)",this.properties.formula,function(a,b,d){d.properties.formula=a});this.addWidget("toggle","allow",E.allow_scripts,function(a){E.allow_scripts=a});this._func=null}function f(){this.addInput("vec2","vec2");this.addOutput("x","number");this.addOutput("y", +"number")}function r(){this.addInputs([["x","number"],["y","number"]]);this.addOutput("vec2","vec2");this.properties={x:0,y:0};this._data=new Float32Array(2)}function s(){this.addInput("vec3","vec3");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number")}function I(){this.addInputs([["x","number"],["y","number"],["z","number"]]);this.addOutput("vec3","vec3");this.properties={x:0,y:0,z:0};this._data=new Float32Array(3)}function J(){this.addInput("vec4","vec4");this.addOutput("x", +"number");this.addOutput("y","number");this.addOutput("z","number");this.addOutput("w","number")}function H(){this.addInputs([["x","number"],["y","number"],["z","number"],["w","number"]]);this.addOutput("vec4","vec4");this.properties={x:0,y:0,z:0,w:0};this._data=new Float32Array(4)}var E=B.LiteGraph;c.title="Converter";c.desc="type A to type B";c.prototype.onExecute=function(){var a=this.getInputData(0);if(null!=a&&this.outputs)for(var b=0;ba&&(a+=1024);var c=Math.floor(a);a-=c;d=e.data[c];c=e.data[1023==c?0:c+1]; +b&&(a=a*a*a*(a*(6*a-15)+10));return d*(1-a)+c*a};e.prototype.onExecute=function(){var a=this.getInputData(0)||0,a=e.getValue(a,this.properties.smooth),b=this.properties.min;this._last_v=a*(this.properties.max-b)+b;this.setOutputData(0,this._last_v)};e.prototype.onDrawBackground=function(a){this.outputs[0].label=(this._last_v||0).toFixed(3)};E.registerNodeType("math/noise",e);C.title="Spikes";C.desc="spike every random time";C.prototype.onExecute=function(){var a=this.graph.elapsed_time;this._remaining_time-= +a;this._blink_time-=a;a=0;0this._remaining_time?(this._remaining_time=Math.random()*(this.properties.max_time-this.properties.min_time)+this.properties.min_time,this._blink_time=this.properties.duration,this.boxcolor="#FFF"):this.boxcolor="#000";this.setOutputData(0,a)};E.registerNodeType("math/spikes",C);w.title="Clamp";w.desc="Clamp number between min and max";w.prototype.onExecute=function(){var a=this.getInputData(0); +null!=a&&(a=Math.max(this.properties.min,a),a=Math.min(this.properties.max,a),this.setOutputData(0,a))};w.prototype.getCode=function(a){a="";this.isInputConnected(0)&&(a+="clamp({{0}},"+this.properties.min+","+this.properties.max+")");return a};E.registerNodeType("math/clamp",w);u.title="Lerp";u.desc="Linear Interpolation";u.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.getInputData(1);null==b&&(b=0);var d=this.properties.f,c=this.getInputData(2);void 0!==c&& +(d=c);this.setOutputData(0,a*(1-d)+b*d)};u.prototype.onGetInputs=function(){return[["f","number"]]};E.registerNodeType("math/lerp",u);D.title="Abs";D.desc="Absolute";D.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.abs(a))};E.registerNodeType("math/abs",D);A.title="Floor";A.desc="Floor number to remove fractional part";A.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.floor(a))};E.registerNodeType("math/floor", +A);h.title="Frac";h.desc="Returns fractional part";h.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a%1)};E.registerNodeType("math/frac",h);z.title="Smoothstep";z.desc="Smoothstep";z.prototype.onExecute=function(){var a=this.getInputData(0);if(void 0!==a){var b=this.properties.A,a=Math.clamp((a-b)/(this.properties.B-b),0,1);this.setOutputData(0,a*a*(3-2*a))}};E.registerNodeType("math/smoothstep",z);y.title="Scale";y.desc="v * factor";y.prototype.onExecute= +function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a*this.properties.factor)};E.registerNodeType("math/scale",y);v.title="Gate";v.desc="if v is true, then outputs A, otherwise B";v.prototype.onExecute=function(){var a=this.getInputData(0);this.setOutputData(0,this.getInputData(a?1:2))};E.registerNodeType("math/gate",v);G.title="Average";G.desc="Average Filter";G.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this._values.length;this._values[this._current% +b]=a;this._current+=1;this._current>b&&(this._current=0);for(var d=a=0;db&&(b=1);this.properties.samples=Math.round(b);var d=this._values;this._values=new Float32Array(this.properties.samples);d.length<=this._values.length?this._values.set(d):this._values.set(d.subarray(0,this._values.length))};E.registerNodeType("math/average",G);q.title="TendTo";q.desc="moves the output value always closer to the input"; +q.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.factor;this._value=null==this._value?a:this._value*(1-b)+a*b;this.setOutputData(0,this._value)};E.registerNodeType("math/tendTo",q);p.values="+ - * / % ^ max min".split(" ");p.title="Operation";p.desc="Easy math operators";p["@OP"]={type:"enum",title:"operation",values:p.values};p.size=[100,60];p.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+ +"(A,B)":"A "+this.properties.OP+" B"};p.prototype.setValue=function(a){"string"==typeof a&&(a=parseFloat(a));this.properties.value=a};p.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);null!=a?this.properties.A=a:a=this.properties.A;null!=b?this.properties.B=b:b=this.properties.B;var d=0;switch(this.properties.OP){case "+":d=a+b;break;case "-":d=a-b;break;case "x":case "X":case "*":d=a*b;break;case "/":d=a/b;break;case "%":d=a%b;break;case "^":d=Math.pow(a,b);break; +case "max":d=Math.max(a,b);break;case "min":d=Math.min(a,b);break;default:console.warn("Unknown operation: "+this.properties.OP)}this.setOutputData(0,d)};p.prototype.onDrawBackground=function(a){this.flags.collapsed||(a.font="40px Arial",a.fillStyle="#666",a.textAlign="center",a.fillText(this.properties.OP,0.5*this.size[0],0.5*(this.size[1]+E.NODE_TITLE_HEIGHT)),a.textAlign="left")};E.registerNodeType("math/operation",p);E.registerSearchboxExtra("math/operation","MAX",{properties:{OP:"max"},title:"MAX()"}); +E.registerSearchboxExtra("math/operation","MIN",{properties:{OP:"min"},title:"MIN()"});k.title="Compare";k.desc="compares between two values";k.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);void 0!==a?this.properties.A=a:a=this.properties.A;void 0!==b?this.properties.B=b:b=this.properties.B;for(var d=0,c=this.outputs.length;dB":f= +a>b;break;case "A=B":f=a>=b}this.setOutputData(d,f)}}};k.prototype.onGetOutputs=function(){return[["A==B","boolean"],["A!=B","boolean"],["A>B","boolean"],["A=B","boolean"],["A<=B","boolean"]]};E.registerNodeType("math/compare",k);E.registerSearchboxExtra("math/compare","==",{outputs:[["A==B","boolean"]],title:"A==B"});E.registerSearchboxExtra("math/compare","!=",{outputs:[["A!=B","boolean"]],title:"A!=B"});E.registerSearchboxExtra("math/compare", +">",{outputs:[["A>B","boolean"]],title:"A>B"});E.registerSearchboxExtra("math/compare","<",{outputs:[["A=",{outputs:[["A>=B","boolean"]],title:"A>=B"});E.registerSearchboxExtra("math/compare","<=",{outputs:[["A<=B","boolean"]],title:"A<=B"});a.values="> < == != <= >= || &&".split(" ");a["@OP"]={type:"enum",title:"operation",values:a.values};a.title="Condition";a.desc="evaluates condition between A and B";a.prototype.getTitle= +function(){return"A "+this.properties.OP+" B"};a.prototype.onExecute=function(){var a=this.getInputData(0);void 0===a?a=this.properties.A:this.properties.A=a;var b=this.getInputData(1);void 0===b?b=this.properties.B:this.properties.B=b;var d=!0;switch(this.properties.OP){case ">":d=a>b;break;case "<":d=a=":d=a>=b;break;case "||":d=a||b;break;case "&&":d=a&&b}this.setOutputData(0,d);this.setOutputData(1,!d)};E.registerNodeType("math/condition", +a);b.title="Accumulate";b.desc="Increments a value every time";b.prototype.onExecute=function(){null===this.properties.value&&(this.properties.value=0);var a=this.getInputData(0);this.properties.value=null!==a?this.properties.value+a:this.properties.value+this.properties.increment;this.setOutputData(0,this.properties.value)};E.registerNodeType("math/accumulate",b);d.title="Trigonometry";d.desc="Sin Cos Tan";d.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.amplitude, +d=this.findInputSlot("amplitude");-1!=d&&(b=this.getInputData(d));var c=this.properties.offset,d=this.findInputSlot("offset");-1!=d&&(c=this.getInputData(d));for(var d=0,g=this.outputs.length;dXY";f.desc="vector 2 to components";f.prototype.onExecute=function(){var a=this.getInputData(0); +null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]))};E.registerNodeType("math3d/vec2-to-xy",f);r.title="XY->Vec2";r.desc="components to vector2";r.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this._data;d[0]=a;d[1]=b;this.setOutputData(0,d)};E.registerNodeType("math3d/xy-to-vec2",r);s.title="Vec3->XYZ";s.desc="vector 3 to components";s.prototype.onExecute=function(){var a=this.getInputData(0); +null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]))};E.registerNodeType("math3d/vec3-to-xyz",s);I.title="XYZ->Vec3";I.desc="components to vector3";I.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this._data;c[0]=a;c[1]=b;c[2]=d;this.setOutputData(0,c)};E.registerNodeType("math3d/xyz-to-vec3",I);J.title= +"Vec4->XYZW";J.desc="vector 4 to components";J.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]),this.setOutputData(3,a[3]))};E.registerNodeType("math3d/vec4-to-xyzw",J);H.title="XYZW->Vec4";H.desc="components to vector4";H.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null== +d&&(d=this.properties.z);var c=this.getInputData(3);null==c&&(c=this.properties.w);var g=this._data;g[0]=a;g[1]=b;g[2]=d;g[3]=c;this.setOutputData(0,g)};E.registerNodeType("math3d/xyzw-to-vec4",H)})(this); +(function(B){function c(){this.addInput("T","vec3");this.addInput("R","vec3");this.addInput("S","vec3");this.addOutput("mat4","mat4");this.properties={T:[0,0,0],R:[0,0,0],S:[1,1,1],R_in_degrees:!0};this._result=mat4.create();this._must_update=!0}function m(){this.addInput("A","number,vec3");this.addInput("B","number,vec3");this.addOutput("=","vec3");this.addProperty("OP","+","enum",{values:m.values});this._result=vec3.create()}function n(){this.addInput("in","vec3");this.addInput("f","number");this.addOutput("out", +"vec3");this.properties={f:1};this._data=new Float32Array(3)}function l(){this.addInput("in","vec3");this.addOutput("out","number")}function x(){this.addInput("in","vec3");this.addOutput("out","vec3");this._data=new Float32Array(3)}function e(){this.addInput("A","vec3");this.addInput("B","vec3");this.addInput("f","vec3");this.addOutput("out","vec3");this.properties={f:0.5};this._data=new Float32Array(3)}function C(){this.addInput("A","vec3");this.addInput("B","vec3");this.addOutput("out","number")} +var w=B.LiteGraph;c.title="mat4";c.temp_quat=new Float32Array([0,0,0,1]);c.temp_mat4=new Float32Array(16);c.temp_vec3=new Float32Array(3);c.prototype.onPropertyChanged=function(c,e){this._must_update=!0};c.prototype.onExecute=function(){var e=this._result,l=c.temp_quat,n=c.temp_mat4,h=c.temp_vec3,m=this.getInputData(0),y=this.getInputData(1),v=this.getInputData(2);if(this._must_update||m||y||v)m=m||this.properties.T,y=y||this.properties.R,v=v||this.properties.S,mat4.identity(e),mat4.translate(e,e, +m),this.properties.R_in_degrees?(h.set(y),vec3.scale(h,h,DEG2RAD),quat.fromEuler(l,h)):quat.fromEuler(l,y),mat4.fromQuat(n,l),mat4.multiply(e,e,n),mat4.scale(e,e,v);this.setOutputData(0,e)};w.registerNodeType("math3d/mat4",c);m.values="+ - * / % ^ max min".split(" ");m.title="Operation";m.desc="Easy math 3D operators";m["@OP"]={type:"enum",title:"operation",values:m.values};m.size=[100,60];m.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+ +"(A,B)":"A "+this.properties.OP+" B"};m.prototype.onExecute=function(){var c=this.getInputData(0),e=this.getInputData(1);if(null!=c&&null!=e){c.constructor===Number&&(c=[c,c,c]);e.constructor===Number&&(e=[e,e,e]);var l=this._result;switch(this.properties.OP){case "+":l=vec3.add(l,c,e);break;case "-":l=vec3.sub(l,c,e);break;case "x":case "X":case "*":l=vec3.mul(l,c,e);break;case "/":l=vec3.div(l,c,e);break;case "%":l[0]=c[0]%e[0];l[1]=c[1]%e[1];l[2]=c[2]%e[2];break;case "^":l[0]=Math.pow(c[0],e[0]); +l[1]=Math.pow(c[1],e[1]);l[2]=Math.pow(c[2],e[2]);break;case "max":l[0]=Math.max(c[0],e[0]);l[1]=Math.max(c[1],e[1]);l[2]=Math.max(c[2],e[2]);break;case "min":l[0]=Math.min(c[0],e[0]);l[1]=Math.min(c[1],e[1]);l[2]=Math.min(c[2],e[2]);break;default:console.warn("Unknown operation: "+this.properties.OP)}this.setOutputData(0,l)}};m.prototype.onDrawBackground=function(c){this.flags.collapsed||(c.font="40px Arial",c.fillStyle="#666",c.textAlign="center",c.fillText(this.properties.OP,0.5*this.size[0],0.5* +(this.size[1]+w.NODE_TITLE_HEIGHT)),c.textAlign="left")};w.registerNodeType("math3d/operation",m);n.title="vec3_scale";n.desc="scales the components of a vec3";n.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null==e&&(e=this.properties.f);var l=this._data;l[0]=c[0]*e;l[1]=c[1]*e;l[2]=c[2]*e;this.setOutputData(0,l)}};w.registerNodeType("math3d/vec3-scale",n);l.title="vec3_length";l.desc="returns the module of a vector";l.prototype.onExecute=function(){var c= +this.getInputData(0);null!=c&&(c=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),this.setOutputData(0,c))};w.registerNodeType("math3d/vec3-length",l);x.title="vec3_normalize";x.desc="returns the vector normalized";x.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),l=this._data;l[0]=c[0]/e;l[1]=c[1]/e;l[2]=c[2]/e;this.setOutputData(0,l)}};w.registerNodeType("math3d/vec3-normalize",x);e.title="vec3_lerp";e.desc="returns the interpolated vector"; +e.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);if(null!=e){var l=this.getInputOrProperty("f"),h=this._data;h[0]=c[0]*(1-l)+e[0]*l;h[1]=c[1]*(1-l)+e[1]*l;h[2]=c[2]*(1-l)+e[2]*l;this.setOutputData(0,h)}}};w.registerNodeType("math3d/vec3-lerp",e);C.title="vec3_dot";C.desc="returns the dot product";C.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null!=e&&this.setOutputData(0,c[0]*e[0]+c[1]*e[1]+c[2]* +e[2])}};w.registerNodeType("math3d/vec3-dot",C);B.glMatrix?(B=function(){this.addOutput("quat","quat");this.properties={x:0,y:0,z:0,w:1,normalize:!1};this._value=quat.create()},B.title="Quaternion",B.desc="quaternion",B.prototype.onExecute=function(){this._value[0]=this.getInputOrProperty("x");this._value[1]=this.getInputOrProperty("y");this._value[2]=this.getInputOrProperty("z");this._value[3]=this.getInputOrProperty("w");this.properties.normalize&&quat.normalize(this._value,this._value);this.setOutputData(0, +this._value)},B.prototype.onGetInputs=function(){return[["x","number"],["y","number"],["z","number"],["w","number"]]},w.registerNodeType("math3d/quaternion",B),B=function(){this.addInputs([["degrees","number"],["axis","vec3"]]);this.addOutput("quat","quat");this.properties={angle:90,axis:vec3.fromValues(0,1,0)};this._value=quat.create()},B.title="Rotation",B.desc="quaternion rotation",B.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.angle);var e=this.getInputData(1); +null==e&&(e=this.properties.axis);c=quat.setAxisAngle(this._value,e,0.0174532925*c);this.setOutputData(0,c)},w.registerNodeType("math3d/rotation",B),B=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},B.title="Rot. Vec3",B.desc="rotate a point",B.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.vec);var e=this.getInputData(1);null==e?this.setOutputData(c):this.setOutputData(0,vec3.transformQuat(vec3.create(), +c,e))},w.registerNodeType("math3d/rotate_vec3",B),B=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},B.title="Mult. Quat",B.desc="rotate quaternion",B.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null!=e&&(c=quat.multiply(this._value,c,e),this.setOutputData(0,c))}},w.registerNodeType("math3d/mult-quat",B),B=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp", +"quat");this.addProperty("factor",0.5);this._value=quat.create()},B.title="Quat Slerp",B.desc="quaternion spherical interpolation",B.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);if(null!=e){var l=this.properties.factor;null!=this.getInputData(2)&&(l=this.getInputData(2));c=quat.slerp(this._value,c,e,l);this.setOutputData(0,c)}}},w.registerNodeType("math3d/quat-slerp",B),B=function(){this.addInput("vec3","vec3");this.addOutput("remap","vec3");this.addOutput("clamped", +"vec3");this.properties={clamp:!0,range_min:[-1,-1,0],range_max:[1,1,0],target_min:[-1,-1,0],target_max:[1,1,0]};this._value=vec3.create();this._clamped=vec3.create()},B.title="Remap Range",B.desc="remap a 3D range",B.prototype.onExecute=function(){var c=this.getInputData(0);c&&this._value.set(c);for(var c=this.properties.range_min,e=this.properties.range_max,l=this.properties.target_min,h=this.properties.target_max,n=0;3>n;++n){var m=e[n]-c[n];this._clamped[n]=Math.clamp(this._value[n],c[n],e[n]); +0==m?this._value[n]=0.5*(l[n]+h[n]):(m=(this._value[n]-c[n])/m,this.properties.clamp&&(m=Math.clamp(m,0,1)),this._value[n]=l[n]+m*(h[n]-l[n]))}this.setOutputData(0,this._value);this.setOutputData(1,this._clamped)},w.registerNodeType("math3d/remap_range",B)):console.warn("No glmatrix found, some Math3D nodes may not work")})(this); +(function(B){function c(c,l){return c==l}function m(){this.addInput("","string");this.addOutput("table","table");this.addOutput("rows","number");this.addProperty("value","");this.addProperty("separator",",");this._table=null}B=B.LiteGraph;B.wrapFunctionAsNode("string/toString",c,["*"],"String");B.wrapFunctionAsNode("string/compare",c,["string","string"],"boolean");B.wrapFunctionAsNode("string/concatenate",function(c,l){return void 0===c?l:void 0===l?c:c+l},["string","string"],"string");B.wrapFunctionAsNode("string/contains", +function(c,l){return void 0===c||void 0===l?!1:-1!=c.indexOf(l)},["string","string"],"boolean");B.wrapFunctionAsNode("string/toUpperCase",function(c){return null!=c&&c.constructor===String?c.toUpperCase():c},["string"],"string");B.wrapFunctionAsNode("string/split",function(c,l){null==l&&(l=this.properties.separator);if(null==c)return[];if(c.constructor===String)return c.split(l||" ");if(c.constructor===Array){for(var m=[],e=0;ee;++e){var h=this.getInputData(e);if(null!=h){var l=this.values[e];l.push(h);l.length>c[0]&&l.shift()}}}};c.prototype.onDrawBackground=function(e){if(!this.flags.collapsed){var h=this.size,l=0.5*h[1]/ +this.properties.scale,n=c.colors,q=0.5*h[1];e.fillStyle="#000";e.fillRect(0,0,h[0],h[1]);e.strokeStyle="#555";e.beginPath();e.moveTo(0,q);e.lineTo(h[0],q);e.stroke();if(this.inputs)for(var p=0;4>p;++p){var k=this.values[p];if(this.inputs[p]&&this.inputs[p].link){e.strokeStyle=n[p];e.beginPath();var a=k[0]*l*-1+q;e.moveTo(0,Math.clamp(a,0,h[1]));for(var b=1;be&&(e=0);if(0!=c.length){var g=[0,0,0];if(0==e)g=c[0];else if(1==e)g=c[c.length-1];else{var l= -(c.length-1)*e,e=c[Math.floor(l)],c=c[Math.floor(l)+1],l=l-Math.floor(l);g[0]=e[0]*(1-l)+c[0]*l;g[1]=e[1]*(1-l)+c[1]*l;g[2]=e[2]*(1-l)+c[2]*l}for(var k in g)g[k]/=255;this.boxcolor=colorToString(g);this.setOutputData(0,g)}};g.registerNodeType("color/palette",p);k.title="Frame";k.desc="Frame viewerew";k.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}];k.prototype.onDrawBackground=function(c){this.frame&&!this.flags.collapsed&&c.drawImage(this.frame, -0,0,this.size[0],this.size[1])};k.prototype.onExecute=function(){this.frame=this.getInputData(0);this.setDirtyCanvas(!0)};k.prototype.onWidget=function(c,e){if("resize"==e.name&&this.frame){var g=this.frame.width,l=this.frame.height;g||null==this.frame.videoWidth||(g=this.frame.videoWidth,l=this.frame.videoHeight);g&&l&&(this.size=[g,l]);this.setDirtyCanvas(!0,!0)}else"view"==e.name&&this.show()};k.prototype.show=function(){showElement&&this.frame&&showElement(this.frame)};g.registerNodeType("graphics/frame", -k);v.title="Image fade";v.desc="Fades between images";v.widgets=[{name:"resizeA",text:"Resize to A",type:"button"},{name:"resizeB",text:"Resize to B",type:"button"}];v.prototype.onAdded=function(){this.createCanvas();var c=this.canvas.getContext("2d");c.fillStyle="#000";c.fillRect(0,0,this.properties.width,this.properties.height)};v.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};v.prototype.onExecute= -function(){var c=this.canvas.getContext("2d");this.canvas.width=this.canvas.width;var e=this.getInputData(0);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);e=this.getInputData(2);null==e?e=this.properties.fade:this.properties.fade=e;c.globalAlpha=e;e=this.getInputData(1);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);c.globalAlpha=1;this.setOutputData(0,this.canvas);this.setDirtyCanvas(!0)};g.registerNodeType("graphics/imagefade",v);e.title="Crop";e.desc="Crop Image"; +this.img):this.setOutputData(0,null);this.img&&this.img.dirty&&(this.img.dirty=!1)};m.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"url"==c&&""!=e&&this.loadImage(e);return!0};m.prototype.loadImage=function(c,e){if(""==c)this.img=null;else{this.img=document.createElement("img");"http"==c.substr(0,4)&&h.proxy&&(c=h.proxy+c.substr(c.indexOf(":")+3));this.img.src=c;this.boxcolor="#F95";var l=this;this.img.onload=function(){e&&e(this);console.log("Image loaded, size: "+l.img.width+"x"+ +l.img.height);this.dirty=!0;l.boxcolor="#9F9";l.setDirtyCanvas(!0)};this.img.onerror=function(){console.log("error loading the image:"+c)}}};m.prototype.onWidget=function(c,e){"load"==e.name&&this.loadImage(this.properties.url)};m.prototype.onDropFile=function(c){var e=this;this._url&&URL.revokeObjectURL(this._url);this._url=URL.createObjectURL(c);this.properties.url=this._url;this.loadImage(this._url,function(c){e.size[1]=c.height/c.width*e.size[0]})};h.registerNodeType("graphics/image",m);n.title= +"Palette";n.desc="Generates a color";n.prototype.onExecute=function(){var c=[];null!=this.properties.colorA&&c.push(hex2num(this.properties.colorA));null!=this.properties.colorB&&c.push(hex2num(this.properties.colorB));null!=this.properties.colorC&&c.push(hex2num(this.properties.colorC));null!=this.properties.colorD&&c.push(hex2num(this.properties.colorD));var e=this.getInputData(0);null==e&&(e=0.5);1e&&(e=0);if(0!=c.length){var h=[0,0,0];if(0==e)h=c[0];else if(1==e)h=c[c.length-1];else{var l= +(c.length-1)*e,e=c[Math.floor(l)],c=c[Math.floor(l)+1],l=l-Math.floor(l);h[0]=e[0]*(1-l)+c[0]*l;h[1]=e[1]*(1-l)+c[1]*l;h[2]=e[2]*(1-l)+c[2]*l}for(var q in h)h[q]/=255;this.boxcolor=colorToString(h);this.setOutputData(0,h)}};h.registerNodeType("color/palette",n);l.title="Frame";l.desc="Frame viewerew";l.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}];l.prototype.onDrawBackground=function(c){this.frame&&!this.flags.collapsed&&c.drawImage(this.frame, +0,0,this.size[0],this.size[1])};l.prototype.onExecute=function(){this.frame=this.getInputData(0);this.setDirtyCanvas(!0)};l.prototype.onWidget=function(c,e){if("resize"==e.name&&this.frame){var h=this.frame.width,l=this.frame.height;h||null==this.frame.videoWidth||(h=this.frame.videoWidth,l=this.frame.videoHeight);h&&l&&(this.size=[h,l]);this.setDirtyCanvas(!0,!0)}else"view"==e.name&&this.show()};l.prototype.show=function(){showElement&&this.frame&&showElement(this.frame)};h.registerNodeType("graphics/frame", +l);x.title="Image fade";x.desc="Fades between images";x.widgets=[{name:"resizeA",text:"Resize to A",type:"button"},{name:"resizeB",text:"Resize to B",type:"button"}];x.prototype.onAdded=function(){this.createCanvas();var c=this.canvas.getContext("2d");c.fillStyle="#000";c.fillRect(0,0,this.properties.width,this.properties.height)};x.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};x.prototype.onExecute= +function(){var c=this.canvas.getContext("2d");this.canvas.width=this.canvas.width;var e=this.getInputData(0);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);e=this.getInputData(2);null==e?e=this.properties.fade:this.properties.fade=e;c.globalAlpha=e;e=this.getInputData(1);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);c.globalAlpha=1;this.setOutputData(0,this.canvas);this.setDirtyCanvas(!0)};h.registerNodeType("graphics/imagefade",x);e.title="Crop";e.desc="Crop Image"; e.prototype.onAdded=function(){this.createCanvas()};e.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};e.prototype.onExecute=function(){var c=this.getInputData(0);c&&(c.width?(this.canvas.getContext("2d").drawImage(c,-this.properties.x,-this.properties.y,c.width*this.properties.scale,c.height*this.properties.scale),this.setOutputData(0,this.canvas)):this.setOutputData(0,null))};e.prototype.onDrawBackground= -function(c){this.flags.collapsed||this.canvas&&c.drawImage(this.canvas,0,0,this.canvas.width,this.canvas.height,0,0,this.size[0],this.size[1])};e.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"scale"==c?(this.properties[c]=parseFloat(e),0==this.properties[c]&&(console.error("Error in scale"),this.properties[c]=1)):this.properties[c]=parseInt(e);this.createCanvas();return!0};g.registerNodeType("graphics/cropImage",e);E.title="Canvas";E.desc="Canvas to render stuff";E.prototype.onExecute= -function(){var c=this.canvas,e=this.properties.width|0,g=this.properties.height|0;c.width!=e&&(c.width=e);c.height!=g&&(c.height=g);this.properties.autoclear&&this.ctx.clearRect(0,0,c.width,c.height);this.setOutputData(0,c)};E.prototype.onAction=function(c,e){"clear"==c&&this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)};g.registerNodeType("graphics/canvas",E);z.title="DrawImage";z.desc="Draws image into a canvas";z.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e= -this.getInputOrProperty("img");if(e){var g=this.getInputOrProperty("x"),l=this.getInputOrProperty("y");c.getContext("2d").drawImage(e,g,l)}}};g.registerNodeType("graphics/drawImage",z);y.title="DrawRectangle";y.desc="Draws rectangle in canvas";y.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e=this.getInputOrProperty("x"),g=this.getInputOrProperty("y"),l=this.getInputOrProperty("w"),k=this.getInputOrProperty("h");c.getContext("2d").fillRect(e,g,l,k)}};g.registerNodeType("graphics/drawRectangle", -y);D.title="Video";D.desc="Video playback";D.widgets=[{name:"play",text:"PLAY",type:"minibutton"},{name:"stop",text:"STOP",type:"minibutton"},{name:"demo",text:"Demo video",type:"button"},{name:"mute",text:"Mute video",type:"button"}];D.prototype.onExecute=function(){if(this.properties.url&&(this.properties.url!=this._video_url&&this.loadVideo(this.properties.url),this._video&&0!=this._video.width)){var c=this.getInputData(0);c&&0<=c&&1>=c&&(this._video.currentTime=c*this._video.duration,this._video.pause()); -this._video.dirty=!0;this.setOutputData(0,this._video);this.setOutputData(1,this._video.currentTime);this.setOutputData(2,this._video.duration);this.setDirtyCanvas(!0)}};D.prototype.onStart=function(){this.play()};D.prototype.onStop=function(){this.stop()};D.prototype.loadVideo=function(c){this._video_url=c;var e=c.substr(0,10).indexOf(":"),k="";-1!=e&&(k=c.substr(0,e));e="";k&&(e=c.substr(0,c.indexOf("/",k.length+3)),e=e.substr(k.length+3));this.properties.use_proxy&&k&&g.proxy&&e!=location.host&& -(c=g.proxy+c.substr(c.indexOf(":")+3));this._video=document.createElement("video");this._video.src=c;this._video.type="type=video/mp4";this._video.muted=!0;this._video.autoplay=!0;var l=this;this._video.addEventListener("loadedmetadata",function(c){console.log("Duration: "+this.duration+" seconds");console.log("Size: "+this.videoWidth+","+this.videoHeight);l.setDirtyCanvas(!0);this.width=this.videoWidth;this.height=this.videoHeight});this._video.addEventListener("progress",function(c){console.log("video loading...")}); +function(c){this.flags.collapsed||this.canvas&&c.drawImage(this.canvas,0,0,this.canvas.width,this.canvas.height,0,0,this.size[0],this.size[1])};e.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"scale"==c?(this.properties[c]=parseFloat(e),0==this.properties[c]&&(console.error("Error in scale"),this.properties[c]=1)):this.properties[c]=parseInt(e);this.createCanvas();return!0};h.registerNodeType("graphics/cropImage",e);C.title="Canvas";C.desc="Canvas to render stuff";C.prototype.onExecute= +function(){var c=this.canvas,e=this.properties.width|0,h=this.properties.height|0;c.width!=e&&(c.width=e);c.height!=h&&(c.height=h);this.properties.autoclear&&this.ctx.clearRect(0,0,c.width,c.height);this.setOutputData(0,c)};C.prototype.onAction=function(c,e){"clear"==c&&this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)};h.registerNodeType("graphics/canvas",C);w.title="DrawImage";w.desc="Draws image into a canvas";w.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e= +this.getInputOrProperty("img");if(e){var h=this.getInputOrProperty("x"),l=this.getInputOrProperty("y");c.getContext("2d").drawImage(e,h,l)}}};h.registerNodeType("graphics/drawImage",w);u.title="DrawRectangle";u.desc="Draws rectangle in canvas";u.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e=this.getInputOrProperty("x"),h=this.getInputOrProperty("y"),l=this.getInputOrProperty("w"),q=this.getInputOrProperty("h");c.getContext("2d").fillRect(e,h,l,q)}};h.registerNodeType("graphics/drawRectangle", +u);D.title="Video";D.desc="Video playback";D.widgets=[{name:"play",text:"PLAY",type:"minibutton"},{name:"stop",text:"STOP",type:"minibutton"},{name:"demo",text:"Demo video",type:"button"},{name:"mute",text:"Mute video",type:"button"}];D.prototype.onExecute=function(){if(this.properties.url&&(this.properties.url!=this._video_url&&this.loadVideo(this.properties.url),this._video&&0!=this._video.width)){var c=this.getInputData(0);c&&0<=c&&1>=c&&(this._video.currentTime=c*this._video.duration,this._video.pause()); +this._video.dirty=!0;this.setOutputData(0,this._video);this.setOutputData(1,this._video.currentTime);this.setOutputData(2,this._video.duration);this.setDirtyCanvas(!0)}};D.prototype.onStart=function(){this.play()};D.prototype.onStop=function(){this.stop()};D.prototype.loadVideo=function(c){this._video_url=c;var e=c.substr(0,10).indexOf(":"),l="";-1!=e&&(l=c.substr(0,e));e="";l&&(e=c.substr(0,c.indexOf("/",l.length+3)),e=e.substr(l.length+3));this.properties.use_proxy&&l&&h.proxy&&e!=location.host&& +(c=h.proxy+c.substr(c.indexOf(":")+3));this._video=document.createElement("video");this._video.src=c;this._video.type="type=video/mp4";this._video.muted=!0;this._video.autoplay=!0;var n=this;this._video.addEventListener("loadedmetadata",function(c){console.log("Duration: "+this.duration+" seconds");console.log("Size: "+this.videoWidth+","+this.videoHeight);n.setDirtyCanvas(!0);this.width=this.videoWidth;this.height=this.videoHeight});this._video.addEventListener("progress",function(c){console.log("video loading...")}); this._video.addEventListener("error",function(c){console.error("Error loading video: "+this.src);if(this.error)switch(this.error.code){case this.error.MEDIA_ERR_ABORTED:console.error("You stopped the video.");break;case this.error.MEDIA_ERR_NETWORK:console.error("Network error - please try again later.");break;case this.error.MEDIA_ERR_DECODE:console.error("Video is broken..");break;case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED:console.error("Sorry, your browser can't play this video.")}});this._video.addEventListener("ended", function(c){console.log("Video Ended.");this.play()})};D.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"url"==c&&""!=e&&this.loadVideo(e);return!0};D.prototype.play=function(){this._video&&this._video.videoWidth&&this._video.play()};D.prototype.playPause=function(){this._video&&(this._video.paused?this.play():this.pause())};D.prototype.stop=function(){this._video&&(this._video.pause(),this._video.currentTime=0)};D.prototype.pause=function(){this._video&&(console.log("Video paused"), -this._video.pause())};D.prototype.onWidget=function(c,e){};g.registerNodeType("graphics/video",D);A.title="Webcam";A.desc="Webcam image";A.is_webcam_open=!1;A.prototype.openStream=function(){function c(g){console.log("Webcam rejected",g);e._webcam_stream=!1;A.is_webcam_open=!1;e.boxcolor="red";e.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](c); +this._video.pause())};D.prototype.onWidget=function(c,e){};h.registerNodeType("graphics/video",D);A.title="Webcam";A.desc="Webcam image";A.is_webcam_open=!1;A.prototype.openStream=function(){function c(h){console.log("Webcam rejected",h);e._webcam_stream=!1;A.is_webcam_open=!1;e.boxcolor="red";e.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](c); var e=this}};A.prototype.closeStream=function(){if(this._webcam_stream){var c=this._webcam_stream.getTracks();if(c.length)for(var e=0;e=this.size[1]||!this.properties.show|| -!this._video||(c.save(),c.drawImage(this._video,0,0,this.size[0],this.size[1]),c.restore())};A.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["stream_ready",g.EVENT],["stream_closed",g.EVENT],["stream_error",g.EVENT]]};g.registerNodeType("graphics/webcam",A)})(this); -(function(C){function c(){this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",filter:!0};this.size=[c.image_preview_size,c.image_preview_size]}function m(){this.addInput("Texture","Texture");this.properties={flipY:!1};this.size=[c.image_preview_size,c.image_preview_size]}function p(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",generate_mipmaps:!1}}function k(){this.addInput("Texture", +!this._video||(c.save(),c.drawImage(this._video,0,0,this.size[0],this.size[1]),c.restore())};A.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["stream_ready",h.EVENT],["stream_closed",h.EVENT],["stream_error",h.EVENT]]};h.registerNodeType("graphics/webcam",A)})(this); +(function(B){function c(){this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",filter:!0};this.size=[c.image_preview_size,c.image_preview_size]}function m(){this.addInput("Texture","Texture");this.properties={flipY:!1};this.size=[c.image_preview_size,c.image_preview_size]}function n(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",generate_mipmaps:!1}}function l(){this.addInput("Texture", "Texture");this.addInput("TextureB","Texture");this.addInput("value","number");this.addOutput("Texture","Texture");this.help="

pixelcode must be vec3, uvcode must be vec2, is optional

\t\t

uv: tex. coords

color: texture colorB: textureB

time: scene time value: input value

For multiline you must type: result = ...

";this.properties={value:1,pixelcode:"color + colorB * value",uvcode:"",precision:c.DEFAULT}; -this.has_error=!1}function v(){this.addOutput("out","Texture");this.properties={code:"",u_value:1,u_color:[1,1,1,1],width:512,height:512,precision:c.DEFAULT};this.properties.code=v.pixel_shader;this._uniforms={u_value:1,u_color:vec4.create(),in_texture:0,texSize:vec2.create(),time:0}}function e(){this.addInput("in","Texture");this.addInput("scale","vec2");this.addInput("offset","vec2");this.addOutput("out","Texture");this.properties={offset:vec2.fromValues(0,0),scale:vec2.fromValues(1,1),precision:c.DEFAULT}} -function E(){this.addInput("in","Texture");this.addInput("warp","Texture");this.addInput("factor","number");this.addOutput("out","Texture");this.properties={factor:0.01,scale:[1,1],offset:[0,0],precision:c.DEFAULT};this._uniforms={u_texture:0,u_textureB:1,u_factor:1,u_scale:vec2.create(),u_offset:vec2.create()}}function z(){this.addInput("Texture","Texture");this.properties={additive:!1,antialiasing:!1,filter:!0,disable_alpha:!1,gamma:1,viewport:[0,0,1,1]};this.size[0]=130}function y(){this.addInput("Texture", +this.has_error=!1}function x(){this.addOutput("out","Texture");this.properties={code:"",u_value:1,u_color:[1,1,1,1],width:512,height:512,precision:c.DEFAULT};this.properties.code=x.pixel_shader;this._uniforms={u_value:1,u_color:vec4.create(),in_texture:0,texSize:vec2.create(),time:0}}function e(){this.addInput("in","Texture");this.addInput("scale","vec2");this.addInput("offset","vec2");this.addOutput("out","Texture");this.properties={offset:vec2.fromValues(0,0),scale:vec2.fromValues(1,1),precision:c.DEFAULT}} +function C(){this.addInput("in","Texture");this.addInput("warp","Texture");this.addInput("factor","number");this.addOutput("out","Texture");this.properties={factor:0.01,scale:[1,1],offset:[0,0],precision:c.DEFAULT};this._uniforms={u_texture:0,u_textureB:1,u_factor:1,u_scale:vec2.create(),u_offset:vec2.create()}}function w(){this.addInput("Texture","Texture");this.properties={additive:!1,antialiasing:!1,filter:!0,disable_alpha:!1,gamma:1,viewport:[0,0,1,1]};this.size[0]=130}function u(){this.addInput("Texture", "Texture");this.addOutput("","Texture");this.properties={size:0,generate_mipmaps:!1,precision:c.DEFAULT}}function D(){this.addInput("Texture","Texture");this.addOutput("","Texture");this.properties={iterations:1,generate_mipmaps:!1,precision:c.DEFAULT}}function A(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("avg","vec4");this.addOutput("lum","number");this.properties={use_previous_frame:!0,high_quality:!1};this._uniforms={u_texture:0,u_mipmap_offset:0};this._luminance= -new Float32Array(4)}function g(){this.addInput("Texture","Texture");this.addOutput("min_t","Texture");this.addOutput("max_t","Texture");this.addOutput("min","vec4");this.addOutput("max","vec4");this.properties={mode:"max",use_previous_frame:!0};this._uniforms={u_texture:0};this._max=new Float32Array(4);this._min=new Float32Array(4);this._textures_chain=[]}function n(){this.addInput("in","Texture");this.addInput("factor","Number");this.addOutput("out","Texture");this.properties={factor:0.5};this._uniforms= -{u_texture:0,u_textureB:1,u_factor:this.properties.factor}}function t(){this.addInput("in","Texture");this.addOutput("avg","Texture");this.addOutput("array","Texture");this.properties={samples:64,frames_interval:1};this._uniforms={u_texture:0,u_textureB:1,u_samples:this.properties.samples,u_isamples:1/this.properties.samples};this.frame=0}function B(){this.addInput("Image","image");this.addOutput("","Texture");this.properties={}}function l(){this.addInput("Texture","Texture");this.addInput("LUT", -"Texture");this.addInput("Intensity","number");this.addOutput("","Texture");this.properties={enabled:!0,intensity:1,precision:c.DEFAULT,texture:null};l._shader||(l._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,l.pixel_shader))}function q(){this.addInput("Texture","Texture");this.addOutput("R","Texture");this.addOutput("G","Texture");this.addOutput("B","Texture");this.addOutput("A","Texture");q._shader||(q._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,q.pixel_shader))}function w(){this.addInput("R", -"Texture");this.addInput("G","Texture");this.addInput("B","Texture");this.addInput("A","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,R:1,G:1,B:1,A:1};this._color=vec4.create();this._uniforms={u_textureR:0,u_textureG:1,u_textureB:2,u_textureA:3,u_color:this._color}}function x(){this.addOutput("Texture","Texture");this._tex_color=vec4.create();this.properties={color:vec4.create(),precision:c.DEFAULT}}function a(){this.addInput("A","color");this.addInput("B","color"); +new Float32Array(4)}function h(){this.addInput("Texture","Texture");this.addOutput("min_t","Texture");this.addOutput("max_t","Texture");this.addOutput("min","vec4");this.addOutput("max","vec4");this.properties={mode:"max",use_previous_frame:!0};this._uniforms={u_texture:0};this._max=new Float32Array(4);this._min=new Float32Array(4);this._textures_chain=[]}function z(){this.addInput("in","Texture");this.addInput("factor","Number");this.addOutput("out","Texture");this.properties={factor:0.5};this._uniforms= +{u_texture:0,u_textureB:1,u_factor:this.properties.factor}}function y(){this.addInput("in","Texture");this.addOutput("avg","Texture");this.addOutput("array","Texture");this.properties={samples:64,frames_interval:1};this._uniforms={u_texture:0,u_textureB:1,u_samples:this.properties.samples,u_isamples:1/this.properties.samples};this.frame=0}function v(){this.addInput("Image","image");this.addOutput("","Texture");this.properties={}}function G(){this.addInput("Texture","Texture");this.addInput("LUT", +"Texture");this.addInput("Intensity","number");this.addOutput("","Texture");this.properties={enabled:!0,intensity:1,precision:c.DEFAULT,texture:null};G._shader||(G._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,G.pixel_shader))}function q(){this.addInput("Texture","Texture");this.addOutput("R","Texture");this.addOutput("G","Texture");this.addOutput("B","Texture");this.addOutput("A","Texture");q._shader||(q._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,q.pixel_shader))}function p(){this.addInput("R", +"Texture");this.addInput("G","Texture");this.addInput("B","Texture");this.addInput("A","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,R:1,G:1,B:1,A:1};this._color=vec4.create();this._uniforms={u_textureR:0,u_textureG:1,u_textureB:2,u_textureA:3,u_color:this._color}}function k(){this.addOutput("Texture","Texture");this._tex_color=vec4.create();this.properties={color:vec4.create(),precision:c.DEFAULT}}function a(){this.addInput("A","color");this.addInput("B","color"); this.addOutput("Texture","Texture");this.properties={angle:0,scale:1,A:[0,0,0],B:[1,1,1],texture_size:32};a._shader||(a._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,a.pixel_shader));this._uniforms={u_angle:0,u_colorA:vec3.create(),u_colorB:vec3.create()}}function b(){this.addInput("A","Texture");this.addInput("B","Texture");this.addInput("Mixer","Texture");this.addOutput("Texture","Texture");this.properties={factor:0.5,size_from_biggest:!0,invert:!1,precision:c.DEFAULT};this._uniforms={u_textureA:0, -u_textureB:1,u_textureMix:2,u_mix:vec4.create()}}function d(){this.addInput("Tex.","Texture");this.addOutput("Edges","Texture");this.properties={invert:!0,threshold:!1,factor:1,precision:c.DEFAULT};d._shader||(d._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,d.pixel_shader))}function h(){this.addInput("Texture","Texture");this.addInput("Distance","number");this.addInput("Range","number");this.addOutput("Texture","Texture");this.properties={distance:100,range:50,only_depth:!1,high_precision:!1}; -this._uniforms={u_texture:0,u_distance:100,u_range:50,u_camera_planes:null}}function f(){this.addInput("Texture","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,invert:!1};this._uniforms={u_texture:0,u_near:0.1,u_far:1E4}}function s(){this.addInput("Texture","Texture");this.addInput("Iterations","number");this.addInput("Intensity","number");this.addOutput("Blurred","Texture");this.properties={intensity:1,iterations:1,preserve_aspect:!1,scale:[1,1],precision:c.DEFAULT}} -function r(){this.addInput("in","Texture");this.addInput("dirt","Texture");this.addOutput("out","Texture");this.addOutput("glow","Texture");this.properties={enabled:!0,intensity:1,persistence:0.99,iterations:16,threshold:0,scale:1,dirt_factor:0.5,precision:c.DEFAULT};this._textures=[];this._uniforms={u_intensity:1,u_texture:0,u_glow_texture:1,u_threshold:0,u_texel_size:vec2.create()}}function I(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={intensity:1, -radius:5}}function J(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={sigma:1.4,k:1.6,p:21.7,epsilon:79,phi:0.017}}function H(){this.addOutput("Webcam","Texture");this.properties={texture_name:"",facingMode:"user"};this.boxcolor="black";this.version=0}function F(){this.addInput("in","Texture");this.addInput("f","number");this.addOutput("out","Texture");this.properties={enabled:!0,factor:1,precision:c.LOW};this._uniforms={u_texture:0,u_factor:1}}function u(){this.addInput("in", +u_textureB:1,u_textureMix:2,u_mix:vec4.create()}}function d(){this.addInput("Tex.","Texture");this.addOutput("Edges","Texture");this.properties={invert:!0,threshold:!1,factor:1,precision:c.DEFAULT};d._shader||(d._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,d.pixel_shader))}function g(){this.addInput("Texture","Texture");this.addInput("Distance","number");this.addInput("Range","number");this.addOutput("Texture","Texture");this.properties={distance:100,range:50,only_depth:!1,high_precision:!1}; +this._uniforms={u_texture:0,u_distance:100,u_range:50,u_camera_planes:null}}function f(){this.addInput("Texture","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,invert:!1};this._uniforms={u_texture:0,u_near:0.1,u_far:1E4}}function r(){this.addInput("Texture","Texture");this.addInput("Iterations","number");this.addInput("Intensity","number");this.addOutput("Blurred","Texture");this.properties={intensity:1,iterations:1,preserve_aspect:!1,scale:[1,1],precision:c.DEFAULT}} +function s(){this.addInput("in","Texture");this.addInput("dirt","Texture");this.addOutput("out","Texture");this.addOutput("glow","Texture");this.properties={enabled:!0,intensity:1,persistence:0.99,iterations:16,threshold:0,scale:1,dirt_factor:0.5,precision:c.DEFAULT};this._textures=[];this._uniforms={u_intensity:1,u_texture:0,u_glow_texture:1,u_threshold:0,u_texel_size:vec2.create()}}function I(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={intensity:1, +radius:5}}function J(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={sigma:1.4,k:1.6,p:21.7,epsilon:79,phi:0.017}}function H(){this.addOutput("Webcam","Texture");this.properties={texture_name:"",facingMode:"user"};this.boxcolor="black";this.version=0}function E(){this.addInput("in","Texture");this.addInput("f","number");this.addOutput("out","Texture");this.properties={enabled:!0,factor:1,precision:c.LOW};this._uniforms={u_texture:0,u_factor:1}}function t(){this.addInput("in", "Texture");this.addOutput("out","Texture");this.properties={precision:c.LOW,split_channels:!1};this._values=new Uint8Array(1024);this._values.fill(255);this._curve_texture=null;this._uniforms={u_texture:0,u_curve:1,u_range:1};this._must_update=!0;this._points={RGB:[[0,0],[1,1]],R:[[0,0],[1,1]],G:[[0,0],[1,1]],B:[[0,0],[1,1]]};this.curve_editor=null;this.addWidget("toggle","Split Channels",!1,"split_channels");this.addWidget("combo","Channel","RGB",{values:["RGB","R","G","B"]});this.curve_offset=68; -this.size=[240,160]}function O(){this.addInput("in","Texture");this.addInput("exp","number");this.addOutput("out","Texture");this.properties={exposition:1,precision:c.LOW};this._uniforms={u_texture:0,u_exposition:1}}function K(){this.addInput("in","Texture");this.addInput("avg","number,Texture");this.addOutput("out","Texture");this.properties={enabled:!0,scale:1,gamma:1,average_lum:1,lum_white:1,precision:c.LOW};this._uniforms={u_texture:0,u_lumwhite2:1,u_igamma:1,u_scale:1,u_average_lum:1}}function N(){this.addOutput("out", -"Texture");this.properties={width:512,height:512,seed:0,persistence:0.1,octaves:8,scale:1,offset:[0,0],amplitude:1,precision:c.DEFAULT};this._key=0;this._texture=null;this._uniforms={u_persistence:0.1,u_seed:0,u_offset:vec2.create(),u_scale:1,u_viewport:vec2.create()}}function M(){this.addInput("v");this.addOutput("out","Texture");this.properties={code:M.default_code,width:512,height:512,clear:!0,precision:c.DEFAULT,use_html_canvas:!1};this._temp_texture=this._func=null;this.compileCode()}function P(){this.addInput("in", -"Texture");this.addOutput("out","Texture");this.properties={key_color:vec3.fromValues(0,1,0),threshold:0.8,slope:0.2,precision:c.DEFAULT}}function Q(){this.addInput("in","texture");this.addInput("yaw","number");this.addOutput("out","texture");this.properties={yaw:0}}var G=C.LiteGraph;C.LGraphTexture=null;"undefined"!=typeof GL&&(LGraphCanvas.link_type_colors.Texture="#987",C.LGraphTexture=c,c.title="Texture",c.desc="Texture",c.widgets_info={name:{widget:"texture"},filter:{widget:"checkbox"}},c.loadTextureCallback= -null,c.image_preview_size=256,c.PASS_THROUGH=1,c.COPY=2,c.LOW=3,c.HIGH=4,c.REUSE=5,c.DEFAULT=2,c.MODE_VALUES={"pass through":c.PASS_THROUGH,copy:c.COPY,low:c.LOW,high:c.HIGH,reuse:c.REUSE,"default":c.DEFAULT},c.getTexturesContainer=function(){return gl.textures},c.loadTexture=function(a,b){b=b||{};var d=a;"http://"==d.substr(0,7)&&G.proxy&&(d=G.proxy+d.substr(7));return c.getTexturesContainer()[a]=GL.Texture.fromURL(d,b)},c.getTexture=function(a){var b=this.getTexturesContainer();if(!b)throw"Cannot load texture, container of textures not found"; -b=b[a];return!b&&a&&":"!=a[0]?this.loadTexture(a):b},c.getTargetTexture=function(a,b,d){if(!a)throw"LGraphTexture.getTargetTexture expects a reference texture";var h=null;switch(d){case c.LOW:h=gl.UNSIGNED_BYTE;break;case c.HIGH:h=gl.HIGH_PRECISION_FORMAT;break;case c.REUSE:return a;default:h=a?a.type:gl.UNSIGNED_BYTE}b&&b.width==a.width&&b.height==a.height&&b.type==h||(b=new GL.Texture(a.width,a.height,{type:h,format:gl.RGBA,filter:gl.LINEAR}));return b},c.getTextureType=function(a,b){var d=b?b.type: +this.size=[240,160]}function N(){this.addInput("in","Texture");this.addInput("exp","number");this.addOutput("out","Texture");this.properties={exposition:1,precision:c.LOW};this._uniforms={u_texture:0,u_exposition:1}}function K(){this.addInput("in","Texture");this.addInput("avg","number,Texture");this.addOutput("out","Texture");this.properties={enabled:!0,scale:1,gamma:1,average_lum:1,lum_white:1,precision:c.LOW};this._uniforms={u_texture:0,u_lumwhite2:1,u_igamma:1,u_scale:1,u_average_lum:1}}function M(){this.addOutput("out", +"Texture");this.properties={width:512,height:512,seed:0,persistence:0.1,octaves:8,scale:1,offset:[0,0],amplitude:1,precision:c.DEFAULT};this._key=0;this._texture=null;this._uniforms={u_persistence:0.1,u_seed:0,u_offset:vec2.create(),u_scale:1,u_viewport:vec2.create()}}function L(){this.addInput("v");this.addOutput("out","Texture");this.properties={code:L.default_code,width:512,height:512,clear:!0,precision:c.DEFAULT,use_html_canvas:!1};this._temp_texture=this._func=null;this.compileCode()}function O(){this.addInput("in", +"Texture");this.addOutput("out","Texture");this.properties={key_color:vec3.fromValues(0,1,0),threshold:0.8,slope:0.2,precision:c.DEFAULT}}function P(){this.addInput("in","texture");this.addInput("yaw","number");this.addOutput("out","texture");this.properties={yaw:0}}var F=B.LiteGraph;B.LGraphTexture=null;"undefined"!=typeof GL&&(LGraphCanvas.link_type_colors.Texture="#987",B.LGraphTexture=c,c.title="Texture",c.desc="Texture",c.widgets_info={name:{widget:"texture"},filter:{widget:"checkbox"}},c.loadTextureCallback= +null,c.image_preview_size=256,c.PASS_THROUGH=1,c.COPY=2,c.LOW=3,c.HIGH=4,c.REUSE=5,c.DEFAULT=2,c.MODE_VALUES={"pass through":c.PASS_THROUGH,copy:c.COPY,low:c.LOW,high:c.HIGH,reuse:c.REUSE,"default":c.DEFAULT},c.getTexturesContainer=function(){return gl.textures},c.loadTexture=function(a,b){b=b||{};var d=a;"http://"==d.substr(0,7)&&F.proxy&&(d=F.proxy+d.substr(7));return c.getTexturesContainer()[a]=GL.Texture.fromURL(d,b)},c.getTexture=function(a){var b=this.getTexturesContainer();if(!b)throw"Cannot load texture, container of textures not found"; +b=b[a];return!b&&a&&":"!=a[0]?this.loadTexture(a):b},c.getTargetTexture=function(a,b,d){if(!a)throw"LGraphTexture.getTargetTexture expects a reference texture";var g=null;switch(d){case c.LOW:g=gl.UNSIGNED_BYTE;break;case c.HIGH:g=gl.HIGH_PRECISION_FORMAT;break;case c.REUSE:return a;default:g=a?a.type:gl.UNSIGNED_BYTE}b&&b.width==a.width&&b.height==a.height&&b.type==g||(b=new GL.Texture(a.width,a.height,{type:g,format:gl.RGBA,filter:gl.LINEAR}));return b},c.getTextureType=function(a,b){var d=b?b.type: gl.UNSIGNED_BYTE;switch(a){case c.HIGH:d=gl.HIGH_PRECISION_FORMAT;break;case c.LOW:d=gl.UNSIGNED_BYTE}return d},c.getWhiteTexture=function(){return this._white_texture?this._white_texture:this._white_texture=GL.Texture.fromMemory(1,1,[255,255,255,255],{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.getNoiseTexture=function(){if(this._noise_texture)return this._noise_texture;for(var a=new Uint8Array(1048576),b=0;1048576>b;++b)a[b]=255*Math.random();return this._noise_texture=a=GL.Texture.fromMemory(512, 512,a,{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.prototype.onDropFile=function(a,b,d){if(a){var c=null;"string"==typeof a?c=GL.Texture.fromURL(a):-1!=b.toLowerCase().indexOf(".dds")?c=GL.Texture.fromDDSInMemory(a):(a=new Blob([d]),a=URL.createObjectURL(a),c=GL.Texture.fromURL(a));this._drop_texture=c;this.properties.name=b}else this._drop_texture=null,this.properties.name=""},c.prototype.getExtraMenuOptions=function(a){var b=this;if(this._drop_texture)return[{content:"Clear",callback:function(){b._drop_texture= null;b.properties.name=""}}]},c.prototype.onExecute=function(){var a=null;this.isOutputConnected(1)&&(a=this.getInputData(0));!a&&this._drop_texture&&(a=this._drop_texture);!a&&this.properties.name&&(a=c.getTexture(this.properties.name));if(a){this._last_tex=a;!1===this.properties.filter?a.setParameter(gl.TEXTURE_MAG_FILTER,gl.NEAREST):a.setParameter(gl.TEXTURE_MAG_FILTER,gl.LINEAR);this.setOutputData(0,a);this.setOutputData(1,a.fullpath||a.filename);for(var b=2;b=this.size[1]))if(this._drop_texture&&a.webgl)a.drawImage(this._drop_texture,0,0,this.size[0],this.size[1]);else{if(this._last_preview_tex!=this._last_tex)if(a.webgl)this._canvas= +if(d){var g=null;"width"==d.name?g=a.width:"height"==d.name?g=a.height:"aspect"==d.name&&(g=a.width/a.height);this.setOutputData(b,g)}}}else this.setOutputData(0,null),this.setOutputData(1,"")},c.prototype.onResourceRenamed=function(a,b){this.properties.name==a&&(this.properties.name=b)},c.prototype.onDrawBackground=function(a){if(!(this.flags.collapsed||20>=this.size[1]))if(this._drop_texture&&a.webgl)a.drawImage(this._drop_texture,0,0,this.size[0],this.size[1]);else{if(this._last_preview_tex!=this._last_tex)if(a.webgl)this._canvas= this._last_tex;else{var b=c.generateLowResTexturePreview(this._last_tex);if(!b)return;this._last_preview_tex=this._last_tex;this._canvas=cloneCanvas(b)}this._canvas&&(a.save(),a.webgl||(a.translate(0,this.size[1]),a.scale(1,-1)),a.drawImage(this._canvas,0,0,this.size[0],this.size[1]),a.restore())}},c.generateLowResTexturePreview=function(a){if(!a)return null;var b=c.image_preview_size,d=a;if(a.format==gl.DEPTH_COMPONENT)return null;if(a.width>b||a.height>b)d=this._preview_temp_tex,this._preview_temp_tex|| (this._preview_temp_tex=d=new GL.Texture(b,b,{minFilter:gl.NEAREST})),a.copyTo(d);a=this._preview_canvas;a||(this._preview_canvas=a=createCanvas(b,b));d&&d.toCanvas(a);return a},c.prototype.getResources=function(a){this.properties.name&&(a[this.properties.name]=GL.Texture);return a},c.prototype.onGetInputs=function(){return[["in","Texture"]]},c.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["aspect","number"]]},c.replaceCode=function(a,b){return a.replace(/\{\{[a-zA-Z0-9_]*\}\}/g, -function(a){a=a.replace(/[\{\}]/g,"");return b[a]||""})},G.registerNodeType("texture/texture",c),m.title="Preview",m.desc="Show a texture in the graph canvas",m.allow_preview=!1,m.prototype.onDrawBackground=function(a){if(!this.flags.collapsed&&(a.webgl||m.allow_preview)){var b=this.getInputData(0);if(b){var d=null,d=!b.handle&&a.webgl?b:c.generateLowResTexturePreview(b);a.save();this.properties.flipY&&(a.translate(0,this.size[1]),a.scale(1,-1));a.drawImage(d,0,0,this.size[0],this.size[1]);a.restore()}}}, -G.registerNodeType("texture/preview",m),p.title="Save",p.desc="Save a texture in the repository",p.prototype.getPreviewTexture=function(){return this._texture},p.prototype.onExecute=function(){var a=this.getInputData(0);a&&(this.properties.generate_mipmaps&&(a.bind(0),a.setParameter(gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_LINEAR),gl.generateMipmap(a.texture_type),a.unbind(0)),this.properties.name&&(c.storeTexture?c.storeTexture(this.properties.name,a):c.getTexturesContainer()[this.properties.name]= -a),this._texture=a,this.setOutputData(0,a),this.setOutputData(1,this.properties.name))},G.registerNodeType("texture/save",p),k.widgets_info={uvcode:{widget:"code"},pixelcode:{widget:"code"},precision:{widget:"combo",values:c.MODE_VALUES}},k.title="Operation",k.desc="Texture shader operation",k.presets={},k.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:b.properties.show?"Hide Texture":"Show Texture",callback:function(){b.properties.show=!b.properties.show}}]},k.prototype.onPropertyChanged= -function(){this.has_error=!1},k.prototype.onDrawBackground=function(a){this.flags.collapsed||20>=this.size[1]||!this.properties.show||!this._tex||this._tex.gl!=a||(a.save(),a.drawImage(this._tex,0,0,this.size[0],this.size[1]),a.restore())},k.prototype.onExecute=function(){var a=this.getInputData(0);if(this.isOutputConnected(0))if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else{var b=this.getInputData(1);if(this.properties.uvcode||this.properties.pixelcode){var d=512,h=512; -a?(d=a.width,h=a.height):b&&(d=b.width,h=b.height);b||(b=GL.Texture.getWhiteTexture());var f=c.getTextureType(this.properties.precision,a);this._tex=a||this._tex?c.getTargetTexture(a||this._tex,this._tex,this.properties.precision):new GL.Texture(d,h,{type:f,format:gl.RGBA,filter:gl.LINEAR});f="";this.properties.uvcode&&(f="uv = "+this.properties.uvcode,-1!=this.properties.uvcode.indexOf(";")&&(f=this.properties.uvcode));var e="";this.properties.pixelcode&&(e="result = "+this.properties.pixelcode, --1!=this.properties.pixelcode.indexOf(";")&&(e=this.properties.pixelcode));var g=this._shader;if(!(this.has_error||g&&this._shader_code==f+"|"+e)){var l=c.replaceCode(k.pixel_shader,{UV_CODE:f,PIXEL_CODE:e});try{g=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,l),this.boxcolor="#00FF00"}catch(q){GL.Shader.dumpErrorToConsole(q,Shader.SCREEN_VERTEX_SHADER,l);this.boxcolor="#FF0000";this.has_error=!0;return}this._shader=g;this._shader_code=f+"|"+e}if(this._shader){var r=this.getInputData(2);null!=r?this.properties.value= -r:r=parseFloat(this.properties.value);var s=this.graph.getTime();this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);gl.disable(gl.BLEND);a&&a.bind(0);b&&b.bind(1);var c=Mesh.getScreenQuad();g.uniforms({u_texture:0,u_textureB:1,value:r,texSize:[d,h],time:s}).draw(c)});this.setOutputData(0,this._tex)}}}},k.pixel_shader="precision highp float;\n\t\t\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec2 texSize;\n\t\tuniform float time;\n\t\tuniform float value;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord;\n\t\t\t{{UV_CODE}};\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\tvec3 color = color4.rgb;\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\tvec3 colorB = color4B.rgb;\n\t\t\tvec3 result = color;\n\t\t\tfloat alpha = 1.0;\n\t\t\t{{PIXEL_CODE}};\n\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t}\n\t\t", -k.registerPreset=function(a,b){k.presets[a]=b},k.registerPreset("",""),k.registerPreset("bypass","color"),k.registerPreset("add","color + colorB * value"),k.registerPreset("substract","(color - colorB) * value"),k.registerPreset("mate","mix( color, colorB, color4B.a * value)"),k.registerPreset("invert","vec3(1.0) - color"),k.registerPreset("multiply","color * colorB * value"),k.registerPreset("divide","(color / colorB) / value"),k.registerPreset("difference","abs(color - colorB) * value"),k.registerPreset("max", -"max(color, colorB) * value"),k.registerPreset("min","min(color, colorB) * value"),k.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"),k.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"),k.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"),k.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"), -k.prototype.onInspect=function(a){var b=this;a.addCombo("Presets","",{values:Object.keys(k.presets),callback:function(d){var c=k.presets[d];c&&(b.setProperty("pixelcode",c),b.title=d,a.refresh())}})},G.registerNodeType("texture/operation",k),v.title="Shader",v.desc="Texture shader",v.widgets_info={code:{type:"code",lang:"glsl"},precision:{widget:"combo",values:c.MODE_VALUES}},v.prototype.onPropertyChanged=function(a,b){if("code"==a){var d=this.getShader();if(d){var c=d.uniformInfo;if(this.inputs)for(var h= -{},f=0;f lumaMax))\n\t\t\t\tcolor = vec4(rgbA, 1.0);\n\t\t\telse\n\t\t\t\tcolor = vec4(rgbB, 1.0);\n\t\t\tif(u_igamma != 1.0)\n\t\t\t\tcolor.xyz = pow( color.xyz, vec3(u_igamma) );\n\t\t\treturn color;\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\t\t}\n\t\t", -z.gamma_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_igamma;\n\t\tvoid main() {\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\t\t gl_FragColor = color;\n\t\t}\n\t\t",G.registerNodeType("texture/toviewport",z),y.title="Copy",y.desc="Copy Texture",y.widgets_info={size:{widget:"combo",values:[0,32,64,128,256,512,1024,2048]},precision:{widget:"combo", -values:c.MODE_VALUES}},y.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)){if(a){var b=a.width,d=a.height;0!=this.properties.size&&(d=b=this.properties.size);var h=this._temp_texture,f=a.type;this.properties.precision===c.LOW?f=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(f=gl.HIGH_PRECISION_FORMAT);h&&h.width==b&&h.height==d&&h.type==f||(h=gl.LINEAR,this.properties.generate_mipmaps&&isPowerOfTwo(b)&&isPowerOfTwo(d)&&(h=gl.LINEAR_MIPMAP_LINEAR), -this._temp_texture=new GL.Texture(b,d,{type:f,format:gl.RGBA,minFilter:h,magFilter:gl.LINEAR}));a.copyTo(this._temp_texture);this.properties.generate_mipmaps&&(this._temp_texture.bind(0),gl.generateMipmap(this._temp_texture.texture_type),this._temp_texture.unbind(0))}this.setOutputData(0,this._temp_texture)}},G.registerNodeType("texture/copy",y),D.title="Downsample",D.desc="Downsample Texture",D.widgets_info={iterations:{type:"number",step:1,precision:0,min:0},precision:{widget:"combo",values:c.MODE_VALUES}}, -D.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)&&a&&a.texture_type===GL.TEXTURE_2D)if(1>this.properties.iterations)this.setOutputData(0,a);else{var b=D._shader;b||(D._shader=b=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,D.pixel_shader));var d=a.width|0,h=a.height|0,f=a.type;this.properties.precision===c.LOW?f=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(f=gl.HIGH_PRECISION_FORMAT);var e=this.properties.iterations||1,g=a,l= -null,k=[],a={type:f,format:a.format},f=vec2.create(),q={u_offset:f};this._texture&&GL.Texture.releaseTemporary(this._texture);for(var r=0;r>1||0;h=h>>1||0;l=GL.Texture.getTemporary(d,h,a);k.push(l);g.setParameter(GL.TEXTURE_MAG_FILTER,GL.NEAREST);g.copyTo(l,b,q);if(1==d&&1==h)break;g=l}this._texture=k.pop();for(r=0;r=this.size[1]||!this.properties.show||!this._tex||this._tex.gl!=a||(a.save(),a.drawImage(this._tex,0,0,this.size[0],this.size[1]),a.restore())},l.prototype.onExecute=function(){var a=this.getInputData(0);if(this.isOutputConnected(0))if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else{var b=this.getInputData(1);if(this.properties.uvcode||this.properties.pixelcode){var d=512,g=512; +a?(d=a.width,g=a.height):b&&(d=b.width,g=b.height);b||(b=GL.Texture.getWhiteTexture());var f=c.getTextureType(this.properties.precision,a);this._tex=a||this._tex?c.getTargetTexture(a||this._tex,this._tex,this.properties.precision):new GL.Texture(d,g,{type:f,format:gl.RGBA,filter:gl.LINEAR});f="";this.properties.uvcode&&(f="uv = "+this.properties.uvcode,-1!=this.properties.uvcode.indexOf(";")&&(f=this.properties.uvcode));var e="";this.properties.pixelcode&&(e="result = "+this.properties.pixelcode, +-1!=this.properties.pixelcode.indexOf(";")&&(e=this.properties.pixelcode));var k=this._shader;if(!(this.has_error||k&&this._shader_code==f+"|"+e)){var h=c.replaceCode(l.pixel_shader,{UV_CODE:f,PIXEL_CODE:e});try{k=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,h),this.boxcolor="#00FF00"}catch(q){GL.Shader.dumpErrorToConsole(q,Shader.SCREEN_VERTEX_SHADER,h);this.boxcolor="#FF0000";this.has_error=!0;return}this._shader=k;this._shader_code=f+"|"+e}if(this._shader){var p=this.getInputData(2);null!=p?this.properties.value= +p:p=parseFloat(this.properties.value);var r=this.graph.getTime();this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);gl.disable(gl.BLEND);a&&a.bind(0);b&&b.bind(1);var c=Mesh.getScreenQuad();k.uniforms({u_texture:0,u_textureB:1,value:p,texSize:[d,g],time:r}).draw(c)});this.setOutputData(0,this._tex)}}}},l.pixel_shader="precision highp float;\n\t\t\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec2 texSize;\n\t\tuniform float time;\n\t\tuniform float value;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord;\n\t\t\t{{UV_CODE}};\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\tvec3 color = color4.rgb;\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\tvec3 colorB = color4B.rgb;\n\t\t\tvec3 result = color;\n\t\t\tfloat alpha = 1.0;\n\t\t\t{{PIXEL_CODE}};\n\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t}\n\t\t", +l.registerPreset=function(a,b){l.presets[a]=b},l.registerPreset("",""),l.registerPreset("bypass","color"),l.registerPreset("add","color + colorB * value"),l.registerPreset("substract","(color - colorB) * value"),l.registerPreset("mate","mix( color, colorB, color4B.a * value)"),l.registerPreset("invert","vec3(1.0) - color"),l.registerPreset("multiply","color * colorB * value"),l.registerPreset("divide","(color / colorB) / value"),l.registerPreset("difference","abs(color - colorB) * value"),l.registerPreset("max", +"max(color, colorB) * value"),l.registerPreset("min","min(color, colorB) * value"),l.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"),l.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"),l.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"),l.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"), +l.prototype.onInspect=function(a){var b=this;a.addCombo("Presets","",{values:Object.keys(l.presets),callback:function(d){var c=l.presets[d];c&&(b.setProperty("pixelcode",c),b.title=d,a.refresh())}})},F.registerNodeType("texture/operation",l),x.title="Shader",x.desc="Texture shader",x.widgets_info={code:{type:"code",lang:"glsl"},precision:{widget:"combo",values:c.MODE_VALUES}},x.prototype.onPropertyChanged=function(a,b){if("code"==a){var d=this.getShader();if(d){var c=d.uniformInfo;if(this.inputs)for(var g= +{},f=0;f lumaMax))\n\t\t\t\tcolor = vec4(rgbA, 1.0);\n\t\t\telse\n\t\t\t\tcolor = vec4(rgbB, 1.0);\n\t\t\tif(u_igamma != 1.0)\n\t\t\t\tcolor.xyz = pow( color.xyz, vec3(u_igamma) );\n\t\t\treturn color;\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\t\t}\n\t\t", +w.gamma_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_igamma;\n\t\tvoid main() {\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\t\t gl_FragColor = color;\n\t\t}\n\t\t",F.registerNodeType("texture/toviewport",w),u.title="Copy",u.desc="Copy Texture",u.widgets_info={size:{widget:"combo",values:[0,32,64,128,256,512,1024,2048]},precision:{widget:"combo", +values:c.MODE_VALUES}},u.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)){if(a){var b=a.width,d=a.height;0!=this.properties.size&&(d=b=this.properties.size);var g=this._temp_texture,f=a.type;this.properties.precision===c.LOW?f=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(f=gl.HIGH_PRECISION_FORMAT);g&&g.width==b&&g.height==d&&g.type==f||(g=gl.LINEAR,this.properties.generate_mipmaps&&isPowerOfTwo(b)&&isPowerOfTwo(d)&&(g=gl.LINEAR_MIPMAP_LINEAR), +this._temp_texture=new GL.Texture(b,d,{type:f,format:gl.RGBA,minFilter:g,magFilter:gl.LINEAR}));a.copyTo(this._temp_texture);this.properties.generate_mipmaps&&(this._temp_texture.bind(0),gl.generateMipmap(this._temp_texture.texture_type),this._temp_texture.unbind(0))}this.setOutputData(0,this._temp_texture)}},F.registerNodeType("texture/copy",u),D.title="Downsample",D.desc="Downsample Texture",D.widgets_info={iterations:{type:"number",step:1,precision:0,min:0},precision:{widget:"combo",values:c.MODE_VALUES}}, +D.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)&&a&&a.texture_type===GL.TEXTURE_2D)if(1>this.properties.iterations)this.setOutputData(0,a);else{var b=D._shader;b||(D._shader=b=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,D.pixel_shader));var d=a.width|0,g=a.height|0,f=a.type;this.properties.precision===c.LOW?f=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(f=gl.HIGH_PRECISION_FORMAT);var e=this.properties.iterations||1,k=a,h= +null,l=[],a={type:f,format:a.format},f=vec2.create(),q={u_offset:f};this._texture&&GL.Texture.releaseTemporary(this._texture);for(var p=0;p>1||0;g=g>>1||0;h=GL.Texture.getTemporary(d,g,a);l.push(h);k.setParameter(GL.TEXTURE_MAG_FILTER,GL.NEAREST);k.copyTo(h,b,q);if(1==d&&1==g)break;k=h}this._texture=l.pop();for(p=0;p>=2,c++,1!=d););a.copyTo(this._textures_chain[0]);for(var c=1;c<=this._textures_chain.length;++c)a=this._textures_chain[c];var h=g._shader,f=this._uniforms;f.u_mipmap_offset=this.properties.mipmap_offset;gl.disable(gl.DEPTH_TEST); -gl.disable(gl.BLEND);this._temp_texture.drawTo(function(){a.toViewport(h,f)})}},g.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform mat4 u_samples_a;\n\t\tuniform mat4 u_samples_b;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_mipmap_offset;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t//random average\n\t\t\tfor(int i = 0; i < 4; ++i)\n\t\t\t\tfor(int j = 0; j < 4; ++j)\n\t\t\t\t{\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t}\n\t\t gl_FragColor = color * 0.03125;\n\t\t}\n\t\t", -n.title="Smooth",n.desc="Smooth texture over time",n.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){n._shader||(n._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,n.pixel_shader));var b=this._temp_texture;b&&b.type==a.type&&b.width==a.width&&b.height==a.height||(b={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(a.width,a.height,b),this._temp_texture2=new GL.Texture(a.width,a.height,b),a.copyTo(this._temp_texture2));var b= -this._temp_texture,d=this._temp_texture2,c=n._shader,h=this._uniforms;h.u_factor=1-this.getInputOrProperty("factor");gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);b.drawTo(function(){d.bind(1);a.toViewport(c,h)});this.setOutputData(0,b);this._temp_texture=d;this._temp_texture2=b}},n.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_factor;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tgl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\t\t}\n\t\t", -G.registerNodeType("texture/temporal_smooth",n),t.title="Lineal Avg Smooth",t.desc="Smooth texture linearly over time",t["@samples"]={type:"number",min:1,max:64,step:1,precision:1},t.prototype.getPreviewTexture=function(){return this._temp_texture2},t.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){t._shader||(t._shader_copy=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,t.pixel_shader_copy),t._shader_avg=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,t.pixel_shader_avg)); -var b=Math.clamp(this.properties.samples,0,64),d=this.frame,c=this.properties.frames_interval;if(0==c||0==d%c){d=this._temp_texture;d&&d.type==a.type&&d.width==b||(d={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(b,1,d),this._temp_texture2=new GL.Texture(b,1,d),this._temp_texture_out=new GL.Texture(1,1,d));var h=this._temp_texture,f=this._temp_texture2,e=t._shader_copy,g=t._shader_avg,l=this._uniforms;l.u_samples=b;l.u_isamples=1/b;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST); -h.drawTo(function(){f.bind(1);a.toViewport(e,l)});this._temp_texture_out.drawTo(function(){h.toViewport(g,l)});this.setOutputData(0,this._temp_texture_out);this._temp_texture=f;this._temp_texture2=h}else this.setOutputData(0,this._temp_texture_out);this.setOutputData(1,this._temp_texture2);this.frame++}},t.pixel_shader_copy="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tif( v_coord.x <= u_isamples )\n\t\t\t\tgl_FragColor = texture2D( u_texture, vec2(0.5) );\n\t\t\telse\n\t\t\t\tgl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\t\t}\n\t\t", -t.pixel_shader_avg="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform int u_samples;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\tfor(int i = 0; i < 64; ++i)\n\t\t\t{\n\t\t\t\tcolor += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\t\t\t\tif(i == (u_samples - 1))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tgl_FragColor = color * u_isamples;\n\t\t}\n\t\t",G.registerNodeType("texture/linear_avg_smooth", -t),B.title="Image to Texture",B.desc="Uploads an image to the GPU",B.prototype.onExecute=function(){var a=this.getInputData(0);if(a){var b=a.videoWidth||a.width,d=a.videoHeight||a.height;if(a.gltexture)this.setOutputData(0,a.gltexture);else{var c=this._temp_texture;c&&c.width==b&&c.height==d||(this._temp_texture=new GL.Texture(b,d,{format:gl.RGBA,filter:gl.LINEAR}));try{this._temp_texture.uploadImage(a)}catch(h){console.error("image comes from an unsafe location, cannot be uploaded to webgl: "+h); -return}this.setOutputData(0,this._temp_texture)}}},G.registerNodeType("texture/imageToTexture",B),l.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},l.title="LUT",l.desc="Apply LUT to Texture",l.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture)); -if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.bindTexture(gl.TEXTURE_2D,null);var d=this.properties.intensity;this.isInputConnected(2)&&(this.properties.intensity=d=this.getInputData(2));this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);this._tex.drawTo(function(){b.bind(1);a.toViewport(l._shader,{u_texture:0,u_textureB:1, -u_amount:d})});this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}},l.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_amount;\n\t\t\n\t\tvoid main() {\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\t\t\t mediump vec2 quad1;\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\t\t\t mediump vec2 quad2;\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\t\t\t highp vec2 texPos1;\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t highp vec2 texPos2;\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\t\t}\n\t\t", -G.registerNodeType("texture/LUT",l),q.title="Texture to Channels",q.desc="Split texture channels",q.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this._channels||(this._channels=Array(4));for(var b=gl.RGB,d=0,c=0;4>c;c++)this.isOutputConnected(c)?(this._channels[c]&&this._channels[c].width==a.width&&this._channels[c].height==a.height&&this._channels[c].type==a.type&&this._channels[c].format==b||(this._channels[c]=new GL.Texture(a.width,a.height,{type:a.type,format:b,filter:gl.LINEAR})), -d++):this._channels[c]=null;if(d){gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);for(var h=Mesh.getScreenQuad(),f=q._shader,e=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],c=0;4>c;c++)this._channels[c]&&(this._channels[c].drawTo(function(){a.bind(0);f.uniforms({u_texture:0,u_mask:e[c]}).draw(h)}),this.setOutputData(c,this._channels[c]))}}},q.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec4 u_mask;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\t\t}\n\t\t", -G.registerNodeType("texture/textureChannels",q),w.title="Channels to Texture",w.desc="Split texture channels",w.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},w.prototype.onExecute=function(){var a=c.getWhiteTexture(),b=this.getInputData(0)||a,d=this.getInputData(1)||a,h=this.getInputData(2)||a,f=this.getInputData(3)||a;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad();w._shader||(w._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,w.pixel_shader));var g=w._shader, -a=Math.max(b.width,d.width,h.width,f.width),l=Math.max(b.height,d.height,h.height,f.height),k=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._texture&&this._texture.width==a&&this._texture.height==l&&this._texture.type==k||(this._texture=new GL.Texture(a,l,{type:k,format:gl.RGBA,filter:gl.LINEAR}));a=this._color;a[0]=this.properties.R;a[1]=this.properties.G;a[2]=this.properties.B;a[3]=this.properties.A;var q=this._uniforms;this._texture.drawTo(function(){b.bind(0); -d.bind(1);h.bind(2);f.bind(3);g.uniforms(q).draw(e)});this.setOutputData(0,this._texture)},w.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureR;\n\t\tuniform sampler2D u_textureG;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform vec4 u_color;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = u_color * vec4( \t\t\t\t\ttexture2D(u_textureR, v_coord).r,\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\t\t}\n\t\t", -G.registerNodeType("texture/channelsTexture",w),x.title="Color",x.desc="Generates a 1x1 texture with a constant color",x.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},x.prototype.onDrawBackground=function(a){var b=this.properties.color;a.fillStyle="rgb("+Math.floor(255*Math.clamp(b[0],0,1))+","+Math.floor(255*Math.clamp(b[1],0,1))+","+Math.floor(255*Math.clamp(b[2],0,1))+")";this.flags.collapsed?this.boxcolor=a.fillStyle:a.fillRect(0,0,this.size[0],this.size[1])},x.prototype.onExecute= -function(){var a=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._tex&&this._tex.type==a||(this._tex=new GL.Texture(1,1,{format:gl.RGBA,type:a,minFilter:gl.NEAREST}));a=this.properties.color;if(this.inputs)for(var b=0;ba.width?d: -a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad(),g=null,l=this._uniforms;h?(g=b._shader_tex,g||(g=b._shader_tex=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader,{MIX_TEX:""}))):(g=b._shader_factor,g||(g=b._shader_factor=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader)),f=null==f?this.properties.factor:f,l.u_mix.set([f,f,f,f]));var k=this.properties.invert;this._tex.drawTo(function(){a.bind(k?1:0);d.bind(k?0:1);h&&h.bind(2); -g.uniforms(l).draw(e)});this.setOutputData(0,this._tex)}}},b.prototype.onGetInputs=function(){return[["factor","number"]]},b.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform sampler2D u_textureB;\n\t\t#ifdef MIX_TEX\n\t\t\tuniform sampler2D u_textureMix;\n\t\t#else\n\t\t\tuniform vec4 u_mix;\n\t\t#endif\n\t\t\n\t\tvoid main() {\n\t\t\t#ifdef MIX_TEX\n\t\t\t vec4 f = texture2D(u_textureMix, v_coord);\n\t\t\t#else\n\t\t\t vec4 f = u_mix;\n\t\t\t#endif\n\t\t gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\t\t}\n\t\t", -G.registerNodeType("texture/mix",b),d.title="Edges",d.desc="Detects edges",d.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},d.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else if(a){this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var b=Mesh.getScreenQuad(),h=d._shader,f=this.properties.invert,e=this.properties.factor, -g=this.properties.threshold?1:0;this._tex.drawTo(function(){a.bind(0);h.uniforms({u_texture:0,u_isize:[1/a.width,1/a.height],u_factor:e,u_threshold:g,u_invert:f?1:0}).draw(b)});this.setOutputData(0,this._tex)}}},d.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_isize;\n\t\tuniform int u_invert;\n\t\tuniform float u_factor;\n\t\tuniform float u_threshold;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 center = texture2D(u_texture, v_coord);\n\t\t\tvec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\t\t\tvec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\t\t\tvec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\t\t\tvec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\t\t\tvec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\t\t\tdiff *= u_factor;\n\t\t\tif(u_invert == 1)\n\t\t\t\tdiff.xyz = vec3(1.0) - diff.xyz;\n\t\t\tif( u_threshold == 0.0 )\n\t\t\t\tgl_FragColor = vec4( diff.xyz, center.a );\n\t\t\telse\n\t\t\t\tgl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\t\t}\n\t\t", -G.registerNodeType("texture/edges",d),h.title="Depth Range",h.desc="Generates a texture with a depth range",h.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a){var b=gl.UNSIGNED_BYTE;this.properties.high_precision&&(b=gl.half_float_ext?gl.HALF_FLOAT_OES:gl.FLOAT);this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&&this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGBA, -filter:gl.LINEAR}));var d=this._uniforms,b=this.properties.distance;this.isInputConnected(1)&&(b=this.getInputData(1),this.properties.distance=b);var c=this.properties.range;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.range=c);d.u_distance=b;d.u_range=c;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var f=Mesh.getScreenQuad();h._shader||(h._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,h.pixel_shader),h._shader_onlydepth=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,h.pixel_shader, -{ONLY_DEPTH:""}));var e=this.properties.only_depth?h._shader_onlydepth:h._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1,1E3];d.u_camera_planes=b;this._temp_texture.drawTo(function(){a.bind(0);e.uniforms(d).draw(f)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},h.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform float u_distance;\n\t\tuniform float u_range;\n\t\t\n\t\tfloat LinearDepth()\n\t\t{\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\t\t\tdepth = depth * 2.0 - 1.0;\n\t\t\treturn zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t\tfloat depth = LinearDepth();\n\t\t\t#ifdef ONLY_DEPTH\n\t\t\t gl_FragColor = vec4(depth);\n\t\t\t#else\n\t\t\t\tfloat diff = abs(depth * u_camera_planes.y - u_distance);\n\t\t\t\tfloat dof = 1.0;\n\t\t\t\tif(diff <= u_range)\n\t\t\t\t\tdof = diff / u_range;\n\t\t\t gl_FragColor = vec4(dof);\n\t\t\t#endif\n\t\t}\n\t\t", -G.registerNodeType("texture/depth_range",h),f.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},f.title="Linear Depth",f.desc="Creates a color texture with linear depth",f.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a&&(a.format==gl.DEPTH_COMPONENT||a.format==gl.DEPTH_STENCIL)){var b=this.properties.precision==c.HIGH?gl.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&& -this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGB,filter:gl.LINEAR}));var d=this._uniforms;d.u_near=a.near_far_planes[0];d.u_far=a.near_far_planes[1];d.u_invert=this.properties.invert?1:0;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var h=Mesh.getScreenQuad();f._shader||(f._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,f.pixel_shader));var e=f._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera? -LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1,1E3];d.u_camera_planes=b;this._temp_texture.drawTo(function(){a.bind(0);e.uniforms(d).draw(h)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},f.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_near;\n\t\tuniform float u_far;\n\t\tuniform int u_invert;\n\t\t\n\t\tvoid main() {\n\t\t\tfloat zNear = u_near;\n\t\t\tfloat zFar = u_far;\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\t\t\tdepth = depth * 2.0 - 1.0;\n\t\t\tfloat f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t\tif( u_invert == 1 )\n\t\t\t\tf = 1.0 - f;\n\t\t\tgl_FragColor = vec4(vec3(f),1.0);\n\t\t}\n\t\t", -G.registerNodeType("texture/linear_depth",f),s.title="Blur",s.desc="Blur a texture",s.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},s.max_iterations=20,s.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._final_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(b=this._final_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));var d=this.properties.iterations;this.isInputConnected(1)&& -(d=this.getInputData(1),this.properties.iterations=d);d=Math.min(Math.floor(d),s.max_iterations);if(0==d)this.setOutputData(0,a);else{var c=this.properties.intensity;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.intensity=c);var h=G.camera_aspect;h||void 0===window.gl||(h=gl.canvas.height/gl.canvas.width);h||(h=1);var h=this.properties.preserve_aspect?h:1,f=this.properties.scale||[1,1];a.applyBlur(h*f[0],f[1],c,b);for(a=1;a>=1;1<(d|0)&&(d>>=1);if(2>b)break;k=g[n]=GL.Texture.getTemporary(b,d,h);x[0]=1/q.width;x[1]=1/q.height;q.blit(k,l.uniforms(e));q=k}this.isOutputConnected(2)&&(b=this._average_texture,b&&b.type==a.type&&b.format==a.format||(b=this._average_texture=new GL.Texture(1,1,{type:a.type,format:a.format,filter:gl.LINEAR})),x[0]=1/q.width,x[1]=1/q.height,e.u_intensity=m,e.u_delta=1,q.blit(b,l.uniforms(e)),this.setOutputData(2,b));gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE);e.u_intensity= -this.getInputOrProperty("persistence");e.u_delta=0.5;for(n-=2;0<=n;n--)k=g[n],g[n]=null,x[0]=1/q.width,x[1]=1/q.height,q.blit(k,l.uniforms(e)),GL.Texture.releaseTemporary(q),q=k;gl.disable(gl.BLEND);this.isOutputConnected(1)&&(g=this._glow_texture,g&&g.width==a.width&&g.height==a.height&&g.type==f&&g.format==a.format||(g=this._glow_texture=new GL.Texture(a.width,a.height,{type:f,format:a.format,filter:gl.LINEAR})),q.blit(g),this.setOutputData(1,g));if(this.isOutputConnected(0)){g=this._final_texture; -g&&g.width==a.width&&g.height==a.height&&g.type==f&&g.format==a.format||(g=this._final_texture=new GL.Texture(a.width,a.height,{type:f,format:a.format,filter:gl.LINEAR}));var p=this.getInputData(1),w=this.getInputOrProperty("dirt_factor");e.u_intensity=m;l=p?r._dirt_final_shader:r._final_shader;l||(l=p?r._dirt_final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,r.final_pixel_shader,{USE_DIRT:""}):r._final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,r.final_pixel_shader));g.drawTo(function(){a.bind(0); -q.bind(1);p&&(l.setUniform("u_dirt_factor",w),l.setUniform("u_dirt_texture",p.bind(2)));l.toViewport(e)});this.setOutputData(0,g)}GL.Texture.releaseTemporary(q)}},r.cut_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform float u_threshold;\n\tvoid main() {\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\t}",r.scale_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\t}", -r.final_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform sampler2D u_glow_texture;\n\t#ifdef USE_DIRT\n\t\tuniform sampler2D u_dirt_texture;\n\t#endif\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\tuniform float u_dirt_factor;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tvec4 glow = sampleBox( v_coord );\n\t\t#ifdef USE_DIRT\n\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\t\t#endif\n\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\t}", -G.registerNodeType("texture/glow",r),I.title="Kuwahara Filter",I.desc="Filters a texture giving an artistic oil canvas painting",I.max_radius=10,I._shaders=[],I.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));b=this.properties.radius;b=Math.min(Math.floor(b),I.max_radius);if(0== -b)this.setOutputData(0,a);else{var d=this.properties.intensity,c=G.camera_aspect;c||void 0===window.gl||(c=gl.canvas.height/gl.canvas.width);c||(c=1);c=this.properties.preserve_aspect?c:1;I._shaders[b]||(I._shaders[b]=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,I.pixel_shader,{RADIUS:b.toFixed(0)}));var h=I._shaders[b],f=GL.Mesh.getScreenQuad();a.bind(0);this._temp_texture.drawTo(function(){h.uniforms({u_texture:0,u_intensity:d,u_resolution:[a.width,a.height],u_iResolution:[1/a.width,1/a.height]}).draw(f)}); +new GL.Texture(1,1,{type:b,format:gl.RGBA,filter:gl.NEAREST}));this._uniforms.u_mipmap_offset=0;this.properties.high_quality&&(this._temp_pot2_texture&&this._temp_pot2_texture.type==b||(this._temp_pot2_texture=new GL.Texture(512,512,{type:b,format:gl.RGBA,minFilter:gl.LINEAR_MIPMAP_LINEAR,magFilter:gl.LINEAR})),a.copyTo(this._temp_pot2_texture),a=this._temp_pot2_texture,a.bind(0),gl.generateMipmap(GL.TEXTURE_2D),this._uniforms.u_mipmap_offset=9);var c=A._shader,g=this._uniforms;g.u_mipmap_offset= +this.properties.mipmap_offset;gl.disable(gl.DEPTH_TEST);gl.disable(gl.BLEND);this._temp_texture.drawTo(function(){a.toViewport(c,g)});if(this.isOutputConnected(1)||this.isOutputConnected(2))if(d=this._temp_texture.getPixels()){var f=this._luminance,b=this._temp_texture.type;f.set(d);b==gl.UNSIGNED_BYTE&&vec4.scale(f,f,1/255)}}},A.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform mat4 u_samples_a;\n\t\tuniform mat4 u_samples_b;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_mipmap_offset;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t//random average\n\t\t\tfor(int i = 0; i < 4; ++i)\n\t\t\t\tfor(int j = 0; j < 4; ++j)\n\t\t\t\t{\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t}\n\t\t gl_FragColor = color * 0.03125;\n\t\t}\n\t\t", +F.registerNodeType("texture/average",A),h.widgets_info={mode:{widget:"combo",values:["min","max","avg"]}},h.title="MinMax",h.desc="Compute the scene min max",h.prototype.onExecute=function(){this.properties.use_previous_frame||this.update();this.setOutputData(0,this._temp_texture);this.setOutputData(1,this._luminance)},h.prototype.onPreRenderExecute=function(){this.update()},h.prototype.update=function(){var a=this.getInputData(0);if(a&&(this.isOutputConnected(0)||this.isOutputConnected(1))){h._shader|| +(h._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,h.pixel_shader));var b=gl.UNSIGNED_BYTE;a.type!=b&&(b=gl.FLOAT);var d=512;if(!this._textures_chain.length||this._textures_chain[0].type!=b)for(;c&&(this._textures_chain[c]=new GL.Texture(d,d,{type:b,format:gl.RGBA,filter:gl.NEAREST}),d>>=2,c++,1!=d););a.copyTo(this._textures_chain[0]);for(var c=1;c<=this._textures_chain.length;++c)a=this._textures_chain[c];var g=h._shader,f=this._uniforms;f.u_mipmap_offset=this.properties.mipmap_offset;gl.disable(gl.DEPTH_TEST); +gl.disable(gl.BLEND);this._temp_texture.drawTo(function(){a.toViewport(g,f)})}},h.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform mat4 u_samples_a;\n\t\tuniform mat4 u_samples_b;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_mipmap_offset;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t//random average\n\t\t\tfor(int i = 0; i < 4; ++i)\n\t\t\t\tfor(int j = 0; j < 4; ++j)\n\t\t\t\t{\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t}\n\t\t gl_FragColor = color * 0.03125;\n\t\t}\n\t\t", +z.title="Smooth",z.desc="Smooth texture over time",z.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){z._shader||(z._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,z.pixel_shader));var b=this._temp_texture;b&&b.type==a.type&&b.width==a.width&&b.height==a.height||(b={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(a.width,a.height,b),this._temp_texture2=new GL.Texture(a.width,a.height,b),a.copyTo(this._temp_texture2));var b= +this._temp_texture,d=this._temp_texture2,c=z._shader,g=this._uniforms;g.u_factor=1-this.getInputOrProperty("factor");gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);b.drawTo(function(){d.bind(1);a.toViewport(c,g)});this.setOutputData(0,b);this._temp_texture=d;this._temp_texture2=b}},z.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_factor;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tgl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\t\t}\n\t\t", +F.registerNodeType("texture/temporal_smooth",z),y.title="Lineal Avg Smooth",y.desc="Smooth texture linearly over time",y["@samples"]={type:"number",min:1,max:64,step:1,precision:1},y.prototype.getPreviewTexture=function(){return this._temp_texture2},y.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){y._shader||(y._shader_copy=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,y.pixel_shader_copy),y._shader_avg=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,y.pixel_shader_avg)); +var b=Math.clamp(this.properties.samples,0,64),d=this.frame,c=this.properties.frames_interval;if(0==c||0==d%c){d=this._temp_texture;d&&d.type==a.type&&d.width==b||(d={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(b,1,d),this._temp_texture2=new GL.Texture(b,1,d),this._temp_texture_out=new GL.Texture(1,1,d));var g=this._temp_texture,f=this._temp_texture2,e=y._shader_copy,k=y._shader_avg,h=this._uniforms;h.u_samples=b;h.u_isamples=1/b;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST); +g.drawTo(function(){f.bind(1);a.toViewport(e,h)});this._temp_texture_out.drawTo(function(){g.toViewport(k,h)});this.setOutputData(0,this._temp_texture_out);this._temp_texture=f;this._temp_texture2=g}else this.setOutputData(0,this._temp_texture_out);this.setOutputData(1,this._temp_texture2);this.frame++}},y.pixel_shader_copy="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tif( v_coord.x <= u_isamples )\n\t\t\t\tgl_FragColor = texture2D( u_texture, vec2(0.5) );\n\t\t\telse\n\t\t\t\tgl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\t\t}\n\t\t", +y.pixel_shader_avg="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform int u_samples;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\tfor(int i = 0; i < 64; ++i)\n\t\t\t{\n\t\t\t\tcolor += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\t\t\t\tif(i == (u_samples - 1))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tgl_FragColor = color * u_isamples;\n\t\t}\n\t\t",F.registerNodeType("texture/linear_avg_smooth", +y),v.title="Image to Texture",v.desc="Uploads an image to the GPU",v.prototype.onExecute=function(){var a=this.getInputData(0);if(a){var b=a.videoWidth||a.width,d=a.videoHeight||a.height;if(a.gltexture)this.setOutputData(0,a.gltexture);else{var c=this._temp_texture;c&&c.width==b&&c.height==d||(this._temp_texture=new GL.Texture(b,d,{format:gl.RGBA,filter:gl.LINEAR}));try{this._temp_texture.uploadImage(a)}catch(g){console.error("image comes from an unsafe location, cannot be uploaded to webgl: "+g); +return}this.setOutputData(0,this._temp_texture)}}},F.registerNodeType("texture/imageToTexture",v),G.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},G.title="LUT",G.desc="Apply LUT to Texture",G.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture)); +if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.bindTexture(gl.TEXTURE_2D,null);var d=this.properties.intensity;this.isInputConnected(2)&&(this.properties.intensity=d=this.getInputData(2));this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);this._tex.drawTo(function(){b.bind(1);a.toViewport(G._shader,{u_texture:0,u_textureB:1, +u_amount:d})});this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}},G.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_amount;\n\t\t\n\t\tvoid main() {\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\t\t\t mediump vec2 quad1;\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\t\t\t mediump vec2 quad2;\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\t\t\t highp vec2 texPos1;\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t highp vec2 texPos2;\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\t\t}\n\t\t", +F.registerNodeType("texture/LUT",G),q.title="Texture to Channels",q.desc="Split texture channels",q.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this._channels||(this._channels=Array(4));for(var b=gl.RGB,d=0,c=0;4>c;c++)this.isOutputConnected(c)?(this._channels[c]&&this._channels[c].width==a.width&&this._channels[c].height==a.height&&this._channels[c].type==a.type&&this._channels[c].format==b||(this._channels[c]=new GL.Texture(a.width,a.height,{type:a.type,format:b,filter:gl.LINEAR})), +d++):this._channels[c]=null;if(d){gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);for(var g=Mesh.getScreenQuad(),f=q._shader,e=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],c=0;4>c;c++)this._channels[c]&&(this._channels[c].drawTo(function(){a.bind(0);f.uniforms({u_texture:0,u_mask:e[c]}).draw(g)}),this.setOutputData(c,this._channels[c]))}}},q.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec4 u_mask;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\t\t}\n\t\t", +F.registerNodeType("texture/textureChannels",q),p.title="Channels to Texture",p.desc="Split texture channels",p.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},p.prototype.onExecute=function(){var a=c.getWhiteTexture(),b=this.getInputData(0)||a,d=this.getInputData(1)||a,g=this.getInputData(2)||a,f=this.getInputData(3)||a;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad();p._shader||(p._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,p.pixel_shader));var k=p._shader, +a=Math.max(b.width,d.width,g.width,f.width),h=Math.max(b.height,d.height,g.height,f.height),l=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._texture&&this._texture.width==a&&this._texture.height==h&&this._texture.type==l||(this._texture=new GL.Texture(a,h,{type:l,format:gl.RGBA,filter:gl.LINEAR}));a=this._color;a[0]=this.properties.R;a[1]=this.properties.G;a[2]=this.properties.B;a[3]=this.properties.A;var q=this._uniforms;this._texture.drawTo(function(){b.bind(0); +d.bind(1);g.bind(2);f.bind(3);k.uniforms(q).draw(e)});this.setOutputData(0,this._texture)},p.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureR;\n\t\tuniform sampler2D u_textureG;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform vec4 u_color;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = u_color * vec4( \t\t\t\t\ttexture2D(u_textureR, v_coord).r,\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\t\t}\n\t\t", +F.registerNodeType("texture/channelsTexture",p),k.title="Color",k.desc="Generates a 1x1 texture with a constant color",k.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},k.prototype.onDrawBackground=function(a){var b=this.properties.color;a.fillStyle="rgb("+Math.floor(255*Math.clamp(b[0],0,1))+","+Math.floor(255*Math.clamp(b[1],0,1))+","+Math.floor(255*Math.clamp(b[2],0,1))+")";this.flags.collapsed?this.boxcolor=a.fillStyle:a.fillRect(0,0,this.size[0],this.size[1])},k.prototype.onExecute= +function(){var a=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._tex&&this._tex.type==a||(this._tex=new GL.Texture(1,1,{format:gl.RGBA,type:a,minFilter:gl.NEAREST}));a=this.properties.color;if(this.inputs)for(var b=0;ba.width?d: +a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad(),k=null,h=this._uniforms;g?(k=b._shader_tex,k||(k=b._shader_tex=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader,{MIX_TEX:""}))):(k=b._shader_factor,k||(k=b._shader_factor=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader)),f=null==f?this.properties.factor:f,h.u_mix.set([f,f,f,f]));var l=this.properties.invert;this._tex.drawTo(function(){a.bind(l?1:0);d.bind(l?0:1);g&&g.bind(2); +k.uniforms(h).draw(e)});this.setOutputData(0,this._tex)}}},b.prototype.onGetInputs=function(){return[["factor","number"]]},b.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform sampler2D u_textureB;\n\t\t#ifdef MIX_TEX\n\t\t\tuniform sampler2D u_textureMix;\n\t\t#else\n\t\t\tuniform vec4 u_mix;\n\t\t#endif\n\t\t\n\t\tvoid main() {\n\t\t\t#ifdef MIX_TEX\n\t\t\t vec4 f = texture2D(u_textureMix, v_coord);\n\t\t\t#else\n\t\t\t vec4 f = u_mix;\n\t\t\t#endif\n\t\t gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\t\t}\n\t\t", +F.registerNodeType("texture/mix",b),d.title="Edges",d.desc="Detects edges",d.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},d.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else if(a){this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var b=Mesh.getScreenQuad(),g=d._shader,f=this.properties.invert,e=this.properties.factor, +k=this.properties.threshold?1:0;this._tex.drawTo(function(){a.bind(0);g.uniforms({u_texture:0,u_isize:[1/a.width,1/a.height],u_factor:e,u_threshold:k,u_invert:f?1:0}).draw(b)});this.setOutputData(0,this._tex)}}},d.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_isize;\n\t\tuniform int u_invert;\n\t\tuniform float u_factor;\n\t\tuniform float u_threshold;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 center = texture2D(u_texture, v_coord);\n\t\t\tvec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\t\t\tvec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\t\t\tvec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\t\t\tvec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\t\t\tvec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\t\t\tdiff *= u_factor;\n\t\t\tif(u_invert == 1)\n\t\t\t\tdiff.xyz = vec3(1.0) - diff.xyz;\n\t\t\tif( u_threshold == 0.0 )\n\t\t\t\tgl_FragColor = vec4( diff.xyz, center.a );\n\t\t\telse\n\t\t\t\tgl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\t\t}\n\t\t", +F.registerNodeType("texture/edges",d),g.title="Depth Range",g.desc="Generates a texture with a depth range",g.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a){var b=gl.UNSIGNED_BYTE;this.properties.high_precision&&(b=gl.half_float_ext?gl.HALF_FLOAT_OES:gl.FLOAT);this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&&this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGBA, +filter:gl.LINEAR}));var d=this._uniforms,b=this.properties.distance;this.isInputConnected(1)&&(b=this.getInputData(1),this.properties.distance=b);var c=this.properties.range;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.range=c);d.u_distance=b;d.u_range=c;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var f=Mesh.getScreenQuad();g._shader||(g._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,g.pixel_shader),g._shader_onlydepth=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,g.pixel_shader, +{ONLY_DEPTH:""}));var e=this.properties.only_depth?g._shader_onlydepth:g._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1,1E3];d.u_camera_planes=b;this._temp_texture.drawTo(function(){a.bind(0);e.uniforms(d).draw(f)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},g.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform float u_distance;\n\t\tuniform float u_range;\n\t\t\n\t\tfloat LinearDepth()\n\t\t{\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\t\t\tdepth = depth * 2.0 - 1.0;\n\t\t\treturn zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t\tfloat depth = LinearDepth();\n\t\t\t#ifdef ONLY_DEPTH\n\t\t\t gl_FragColor = vec4(depth);\n\t\t\t#else\n\t\t\t\tfloat diff = abs(depth * u_camera_planes.y - u_distance);\n\t\t\t\tfloat dof = 1.0;\n\t\t\t\tif(diff <= u_range)\n\t\t\t\t\tdof = diff / u_range;\n\t\t\t gl_FragColor = vec4(dof);\n\t\t\t#endif\n\t\t}\n\t\t", +F.registerNodeType("texture/depth_range",g),f.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},f.title="Linear Depth",f.desc="Creates a color texture with linear depth",f.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a&&(a.format==gl.DEPTH_COMPONENT||a.format==gl.DEPTH_STENCIL)){var b=this.properties.precision==c.HIGH?gl.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&& +this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGB,filter:gl.LINEAR}));var d=this._uniforms;d.u_near=a.near_far_planes[0];d.u_far=a.near_far_planes[1];d.u_invert=this.properties.invert?1:0;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var g=Mesh.getScreenQuad();f._shader||(f._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,f.pixel_shader));var e=f._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera? +LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1,1E3];d.u_camera_planes=b;this._temp_texture.drawTo(function(){a.bind(0);e.uniforms(d).draw(g)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},f.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_near;\n\t\tuniform float u_far;\n\t\tuniform int u_invert;\n\t\t\n\t\tvoid main() {\n\t\t\tfloat zNear = u_near;\n\t\t\tfloat zFar = u_far;\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\t\t\tdepth = depth * 2.0 - 1.0;\n\t\t\tfloat f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t\tif( u_invert == 1 )\n\t\t\t\tf = 1.0 - f;\n\t\t\tgl_FragColor = vec4(vec3(f),1.0);\n\t\t}\n\t\t", +F.registerNodeType("texture/linear_depth",f),r.title="Blur",r.desc="Blur a texture",r.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},r.max_iterations=20,r.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._final_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(b=this._final_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));var d=this.properties.iterations;this.isInputConnected(1)&& +(d=this.getInputData(1),this.properties.iterations=d);d=Math.min(Math.floor(d),r.max_iterations);if(0==d)this.setOutputData(0,a);else{var c=this.properties.intensity;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.intensity=c);var g=F.camera_aspect;g||void 0===window.gl||(g=gl.canvas.height/gl.canvas.width);g||(g=1);var g=this.properties.preserve_aspect?g:1,f=this.properties.scale||[1,1];a.applyBlur(g*f[0],f[1],c,b);for(a=1;a>=1;1<(d|0)&&(d>>=1);if(2>b)break;l=k[m]=GL.Texture.getTemporary(b,d,g);r[0]=1/q.width;r[1]=1/q.height;q.blit(l,h.uniforms(e));q=l}this.isOutputConnected(2)&&(b=this._average_texture,b&&b.type==a.type&&b.format==a.format||(b=this._average_texture=new GL.Texture(1,1,{type:a.type,format:a.format,filter:gl.LINEAR})),r[0]=1/q.width,r[1]=1/q.height,e.u_intensity=n,e.u_delta=1,q.blit(b,h.uniforms(e)),this.setOutputData(2,b));gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE);e.u_intensity= +this.getInputOrProperty("persistence");e.u_delta=0.5;for(m-=2;0<=m;m--)l=k[m],k[m]=null,r[0]=1/q.width,r[1]=1/q.height,q.blit(l,h.uniforms(e)),GL.Texture.releaseTemporary(q),q=l;gl.disable(gl.BLEND);this.isOutputConnected(1)&&(k=this._glow_texture,k&&k.width==a.width&&k.height==a.height&&k.type==f&&k.format==a.format||(k=this._glow_texture=new GL.Texture(a.width,a.height,{type:f,format:a.format,filter:gl.LINEAR})),q.blit(k),this.setOutputData(1,k));if(this.isOutputConnected(0)){k=this._final_texture; +k&&k.width==a.width&&k.height==a.height&&k.type==f&&k.format==a.format||(k=this._final_texture=new GL.Texture(a.width,a.height,{type:f,format:a.format,filter:gl.LINEAR}));var t=this.getInputData(1),u=this.getInputOrProperty("dirt_factor");e.u_intensity=n;h=t?s._dirt_final_shader:s._final_shader;h||(h=t?s._dirt_final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,s.final_pixel_shader,{USE_DIRT:""}):s._final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,s.final_pixel_shader));k.drawTo(function(){a.bind(0); +q.bind(1);t&&(h.setUniform("u_dirt_factor",u),h.setUniform("u_dirt_texture",t.bind(2)));h.toViewport(e)});this.setOutputData(0,k)}GL.Texture.releaseTemporary(q)}},s.cut_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform float u_threshold;\n\tvoid main() {\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\t}",s.scale_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\t}", +s.final_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform sampler2D u_glow_texture;\n\t#ifdef USE_DIRT\n\t\tuniform sampler2D u_dirt_texture;\n\t#endif\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\tuniform float u_dirt_factor;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tvec4 glow = sampleBox( v_coord );\n\t\t#ifdef USE_DIRT\n\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\t\t#endif\n\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\t}", +F.registerNodeType("texture/glow",s),I.title="Kuwahara Filter",I.desc="Filters a texture giving an artistic oil canvas painting",I.max_radius=10,I._shaders=[],I.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));b=this.properties.radius;b=Math.min(Math.floor(b),I.max_radius);if(0== +b)this.setOutputData(0,a);else{var d=this.properties.intensity,c=F.camera_aspect;c||void 0===window.gl||(c=gl.canvas.height/gl.canvas.width);c||(c=1);c=this.properties.preserve_aspect?c:1;I._shaders[b]||(I._shaders[b]=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,I.pixel_shader,{RADIUS:b.toFixed(0)}));var g=I._shaders[b],f=GL.Mesh.getScreenQuad();a.bind(0);this._temp_texture.drawTo(function(){g.uniforms({u_texture:0,u_intensity:d,u_resolution:[a.width,a.height],u_iResolution:[1/a.width,1/a.height]}).draw(f)}); this.setOutputData(0,this._temp_texture)}}},I.pixel_shader="\nprecision highp float;\nvarying vec2 v_coord;\nuniform sampler2D u_texture;\nuniform float u_intensity;\nuniform vec2 u_resolution;\nuniform vec2 u_iResolution;\n#ifndef RADIUS\n\t#define RADIUS 7\n#endif\nvoid main() {\n\n\tconst int radius = RADIUS;\n\tvec2 fragCoord = v_coord;\n\tvec2 src_size = u_iResolution;\n\tvec2 uv = v_coord;\n\tfloat n = float((radius + 1) * (radius + 1));\n\tint i;\n\tint j;\n\tvec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\tvec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\tvec3 c;\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm0 += c;\n\t\t\ts0 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm1 += c;\n\t\t\ts1 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm2 += c;\n\t\t\ts2 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm3 += c;\n\t\t\ts3 += c * c;\n\t\t}\n\t}\n\t\n\tfloat min_sigma2 = 1e+2;\n\tm0 /= n;\n\ts0 = abs(s0 / n - m0 * m0);\n\t\n\tfloat sigma2 = s0.r + s0.g + s0.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m0, 1.0);\n\t}\n\t\n\tm1 /= n;\n\ts1 = abs(s1 / n - m1 * m1);\n\t\n\tsigma2 = s1.r + s1.g + s1.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m1, 1.0);\n\t}\n\t\n\tm2 /= n;\n\ts2 = abs(s2 / n - m2 * m2);\n\t\n\tsigma2 = s2.r + s2.g + s2.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m2, 1.0);\n\t}\n\t\n\tm3 /= n;\n\ts3 = abs(s3 / n - m3 * m3);\n\t\n\tsigma2 = s3.r + s3.g + s3.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m3, 1.0);\n\t}\n}\n", -G.registerNodeType("texture/kuwahara",I),J.title="XDoG Filter",J.desc="Filters a texture giving an artistic ink style",J.max_radius=10,J._shaders=[],J.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));J._xdog_shader||(J._xdog_shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,J.xdog_pixel_shader)); -var d=J._xdog_shader,c=GL.Mesh.getScreenQuad(),h=this.properties.sigma,f=this.properties.k,e=this.properties.p,g=this.properties.epsilon,l=this.properties.phi;a.bind(0);this._temp_texture.drawTo(function(){d.uniforms({src:0,sigma:h,k:f,p:e,epsilon:g,phi:l,cvsWidth:a.width,cvsHeight:a.height}).draw(c)});this.setOutputData(0,this._temp_texture)}},J.xdog_pixel_shader="\nprecision highp float;\nuniform sampler2D src;\n\nuniform float cvsHeight;\nuniform float cvsWidth;\n\nuniform float sigma;\nuniform float k;\nuniform float p;\nuniform float epsilon;\nuniform float phi;\nvarying vec2 v_coord;\n\nfloat cosh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat cosH = (tmp + 1.0 / tmp) / 2.0;\n\treturn cosH;\n}\n\nfloat tanh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\treturn tanH;\n}\n\nfloat sinh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat sinH = (tmp - 1.0 / tmp) / 2.0;\n\treturn sinH;\n}\n\nvoid main(void){\n\tvec3 destColor = vec3(0.0);\n\tfloat tFrag = 1.0 / cvsHeight;\n\tfloat sFrag = 1.0 / cvsWidth;\n\tvec2 Frag = vec2(sFrag,tFrag);\n\tvec2 uv = gl_FragCoord.st;\n\tfloat twoSigmaESquared = 2.0 * sigma * sigma;\n\tfloat twoSigmaRSquared = twoSigmaESquared * k * k;\n\tint halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\tconst int MAX_NUM_ITERATION = 99999;\n\tvec2 sum = vec2(0.0);\n\tvec2 norm = vec2(0.0);\n\n\tfor(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\t\tint i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\t\tint j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\t\tfloat d = length(vec2(i,j));\n\t\tvec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\t\t\t\t\t\t\texp( -d * d / twoSigmaRSquared ));\n\n\t\tvec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\t\tnorm += kernel;\n\t\tsum += kernel * L;\n\t}\n\n\tsum /= norm;\n\n\tfloat H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\tfloat edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\tdestColor = vec3(edge);\n\tgl_FragColor = vec4(destColor, 1.0);\n}", -G.registerNodeType("texture/xDoG",J),H.title="Webcam",H.desc="Webcam texture",H.is_webcam_open=!1,H.prototype.openStream=function(){function a(d){H.is_webcam_open=!1;console.log("Webcam rejected",d);b._webcam_stream=!1;b.boxcolor="red";b.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](a);var b=this}},H.prototype.closeStream=function(){if(this._webcam_stream){var a= +F.registerNodeType("texture/kuwahara",I),J.title="XDoG Filter",J.desc="Filters a texture giving an artistic ink style",J.max_radius=10,J._shaders=[],J.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));J._xdog_shader||(J._xdog_shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,J.xdog_pixel_shader)); +var d=J._xdog_shader,c=GL.Mesh.getScreenQuad(),g=this.properties.sigma,f=this.properties.k,e=this.properties.p,k=this.properties.epsilon,h=this.properties.phi;a.bind(0);this._temp_texture.drawTo(function(){d.uniforms({src:0,sigma:g,k:f,p:e,epsilon:k,phi:h,cvsWidth:a.width,cvsHeight:a.height}).draw(c)});this.setOutputData(0,this._temp_texture)}},J.xdog_pixel_shader="\nprecision highp float;\nuniform sampler2D src;\n\nuniform float cvsHeight;\nuniform float cvsWidth;\n\nuniform float sigma;\nuniform float k;\nuniform float p;\nuniform float epsilon;\nuniform float phi;\nvarying vec2 v_coord;\n\nfloat cosh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat cosH = (tmp + 1.0 / tmp) / 2.0;\n\treturn cosH;\n}\n\nfloat tanh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\treturn tanH;\n}\n\nfloat sinh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat sinH = (tmp - 1.0 / tmp) / 2.0;\n\treturn sinH;\n}\n\nvoid main(void){\n\tvec3 destColor = vec3(0.0);\n\tfloat tFrag = 1.0 / cvsHeight;\n\tfloat sFrag = 1.0 / cvsWidth;\n\tvec2 Frag = vec2(sFrag,tFrag);\n\tvec2 uv = gl_FragCoord.st;\n\tfloat twoSigmaESquared = 2.0 * sigma * sigma;\n\tfloat twoSigmaRSquared = twoSigmaESquared * k * k;\n\tint halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\tconst int MAX_NUM_ITERATION = 99999;\n\tvec2 sum = vec2(0.0);\n\tvec2 norm = vec2(0.0);\n\n\tfor(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\t\tint i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\t\tint j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\t\tfloat d = length(vec2(i,j));\n\t\tvec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\t\t\t\t\t\t\texp( -d * d / twoSigmaRSquared ));\n\n\t\tvec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\t\tnorm += kernel;\n\t\tsum += kernel * L;\n\t}\n\n\tsum /= norm;\n\n\tfloat H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\tfloat edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\tdestColor = vec3(edge);\n\tgl_FragColor = vec4(destColor, 1.0);\n}", +F.registerNodeType("texture/xDoG",J),H.title="Webcam",H.desc="Webcam texture",H.is_webcam_open=!1,H.prototype.openStream=function(){function a(d){H.is_webcam_open=!1;console.log("Webcam rejected",d);b._webcam_stream=!1;b.boxcolor="red";b.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](a);var b=this}},H.prototype.closeStream=function(){if(this._webcam_stream){var a= this._webcam_stream.getTracks();if(a.length)for(var b=0;b=this.size[1]||!this._video||(a.save(),a.webgl?this._video_texture&&a.drawImage(this._video_texture,0,0,this.size[0],this.size[1]):a.drawImage(this._video, 0,0,this.size[0],this.size[1]),a.restore())},H.prototype.onExecute=function(){null!=this._webcam_stream||this._waiting_confirmation||this.openStream();if(this._video&&this._video.videoWidth){var a=this._video.videoWidth,b=this._video.videoHeight,d=this._video_texture;d&&d.width==a&&d.height==b||(this._video_texture=new GL.Texture(a,b,{format:gl.RGB,filter:gl.LINEAR}));this._video_texture.uploadImage(this._video);this._video_texture.version=++this.version;this.properties.texture_name&&(c.getTexturesContainer()[this.properties.texture_name]= -this._video_texture);this.setOutputData(0,this._video_texture);for(a=1;aMath.abs(d))return c[1];d=(a-c[0])/d;return c[1]*(1-d)+h[1]*d}}return 0}},u.prototype.updateCurve=function(){for(var a=this._values,b=a.length/4,d=this.properties.split_channels,c=0;c=f;){e=0.5*(g+f)|0;c=a[e];if(c==d)break;if(f==g-1)return f;cMath.abs(d))return c[1];d=(a-c[0])/d;return c[1]*(1-d)+g[1]*d}}return 0}},t.prototype.updateCurve=function(){for(var a=this._values,b=a.length/4,d=this.properties.split_channels,c=0;c=f;){e=0.5*(k+f)|0;c=a[e];if(c==d)break;if(f==k-1)return f;ca&&(a=1);this.points&&this.points.length==3*a||(this.points=new Float32Array(3*a));this.properties.generate_normals?this.normals&&this.normals.length==this.points.length||(this.normals=new Float32Array(this.points.length)):this.normals=null;var d=this._last_radius||this.properties.radius,c=this.properties.mode,f=this.getInputData(0);this._old_obj_version=f?f._version:null;this.points=m.generatePoints(d,a,c,this.points, -this.normals,this.properties.regular,f);this.version++};m.generatePoints=function(a,d,c,f,e,g,l){var k=3*d;f&&f.length==k||(f=new Float32Array(k));var q=new Float32Array(3),x=new Float32Array([0,1,0]);if(g)if(c==m.RECTANGLE){k=Math.floor(Math.sqrt(d));for(d=0;de||wg&&gl))break}this.geometry.indices=this.indices=new Uint32Array(k)}this.indices&&this.indices.length?(this.geometry.indices=this.indices,this.setOutputData(0,this.geometry)):this.setOutputData(0,null)}};B.registerNodeType("geometry/connectPoints",y);"undefined"!=typeof GL&&(D.title="to geometry",D.desc="converts a mesh to geometry",D.prototype.onExecute= -function(){var a=this.getInputData(0);if(a){if(a!=this.last_mesh){this.last_mesh=a;for(i in a.vertexBuffers)this.geometry[i]=a.vertexBuffers[i].data;a.indexBuffers.triangles&&(this.geometry.indices=a.indexBuffers.triangles.data);this.geometry._id=c();this.geometry._version=0}this.setOutputData(0,this.geometry);this.geometry&&this.setOutputData(1,this.geometry.vertices)}},B.registerNodeType("geometry/toGeometry",D),A.title="Geo to Mesh",A.prototype.updateMesh=function(a){this.mesh||(this.mesh=new GL.Mesh); +k=this.vertices;k&&this.vertices.length==a.vertices.length?k.set(a.vertices):k=this.vertices=new Float32Array(a.vertices);for(f=0;fe||wk&&kh))break}this.geometry.indices=this.indices=new Uint32Array(l)}this.indices&&this.indices.length?(this.geometry.indices=this.indices,this.setOutputData(0,this.geometry)):this.setOutputData(0,null)}};v.registerNodeType("geometry/connectPoints",u);"undefined"!=typeof GL&&(D.title="to geometry",D.desc="converts a mesh to geometry",D.prototype.onExecute= +function(){var a=this.getInputData(0);if(a){if(a!=this.last_mesh){this.last_mesh=a;for(i in a.vertexBuffers)this.geometry[i]=a.vertexBuffers[i].data;a.indexBuffers.triangles&&(this.geometry.indices=a.indexBuffers.triangles.data);this.geometry._id=c();this.geometry._version=0}this.setOutputData(0,this.geometry);this.geometry&&this.setOutputData(1,this.geometry.vertices)}},v.registerNodeType("geometry/toGeometry",D),A.title="Geo to Mesh",A.prototype.updateMesh=function(a){this.mesh||(this.mesh=new GL.Mesh); for(var d in a)if("_"!=d[0]){var c=a[d],f=GL.Mesh.common_buffers[d];if(f||"indices"==d){var f=f?f.spacing:3,e=this.mesh.vertexBuffers[d];e&&e.data.length==c.length?(e.data.set(c),e.upload(GL.DYNAMIC_DRAW)):e=new GL.Buffer("indices"==d?GL.ELEMENT_ARRAY_BUFFER:GL.ARRAY_BUFFER,c,f,GL.DYNAMIC_DRAW);this.mesh.addBuffer(d,e)}}if(this.mesh.vertexBuffers.normals&&this.mesh.vertexBuffers.normals.data.length!=this.mesh.vertexBuffers.vertices.data.length){c=new Float32Array([0,1,0]);f=new Float32Array(this.mesh.vertexBuffers.vertices.data.length); -for(d=0;d=c.NOTEON||g<=c.NOTEOFF)this.channel=e&15};Object.defineProperty(c.prototype,"velocity",{get:function(){return this.cmd==c.NOTEON?this.data[2]:-1},set:function(c){this.data[2]=c},enumerable:!0});c.notes="A A# B C C# D D# E F F# G G#".split(" ");c.note_to_index={A:0,"A#":1,B:2,C:3, -"C#":4,D:5,"D#":6,E:7,F:8,"F#":9,G:10,"G#":11};Object.defineProperty(c.prototype,"note",{get:function(){return this.cmd!=c.NOTEON?-1:c.toNoteString(this.data[1],!0)},set:function(c){throw"notes cannot be assigned this way, must modify the data[1]";},enumerable:!0});Object.defineProperty(c.prototype,"octave",{get:function(){return this.cmd!=c.NOTEON?-1:Math.floor((this.data[1]-24)/12+1)},set:function(c){throw"octave cannot be assigned this way, must modify the data[1]";},enumerable:!0});c.prototype.getPitch= -function(){return 440*Math.pow(2,(this.data[1]-69)/12)};c.computePitch=function(c){return 440*Math.pow(2,(c-69)/12)};c.prototype.getCC=function(){return this.data[1]};c.prototype.getCCValue=function(){return this.data[2]};c.prototype.getPitchBend=function(){return this.data[1]+(this.data[2]<<7)-8192};c.computePitchBend=function(c,e){return c+(e<<7)-8192};c.prototype.setCommandFromString=function(e){this.cmd=c.computeCommandFromString(e)};c.computeCommandFromString=function(e){if(!e)return 0;if(e&& -e.constructor===Number)return e;e=e.toUpperCase();switch(e){case "NOTE ON":case "NOTEON":return c.NOTEON;case "NOTE OFF":case "NOTEOFF":return c.NOTEON;case "KEY PRESSURE":case "KEYPRESSURE":return c.KEYPRESSURE;case "CONTROLLER CHANGE":case "CONTROLLERCHANGE":case "CC":return c.CONTROLLERCHANGE;case "PROGRAM CHANGE":case "PROGRAMCHANGE":case "PC":return c.PROGRAMCHANGE;case "CHANNEL PRESSURE":case "CHANNELPRESSURE":return c.CHANNELPRESSURE;case "PITCH BEND":case "PITCHBEND":return c.PITCHBEND;case "TIME TICK":case "TIMETICK":return c.TIMETICK; -default:return Number(e)}};c.toNoteString=function(e,g){e=Math.round(e);var k,x=Math.floor((e-24)/12+1);k=(e-21)%12;0>k&&(k=12+k);return c.notes[k]+(g?"":x)};c.NoteStringToPitch=function(e){e=e.toUpperCase();var g=e[0],k=4;"#"==e[1]?(g+="#",2this.properties.max_value)return;this.trigger("on_midi",g)}};t.registerNodeType("midi/filter",e);E.title="MIDIEvent";E.desc="Create a MIDI Event";E.color="#243";E.prototype.onAction= -function(e,g){"assign"==e?(this.properties.channel=g.channel,this.properties.cmd=g.cmd,this.properties.value1=g.data[1],this.properties.value2=g.data[2],g.cmd==c.NOTEON?this.gate=!0:g.cmd==c.NOTEOFF&&(this.gate=!1)):(g=this.midi_event,g.channel=this.properties.channel,this.properties.cmd&&this.properties.cmd.constructor===String?g.setCommandFromString(this.properties.cmd):g.cmd=this.properties.cmd,g.data[0]=g.cmd|g.channel,g.data[1]=Number(this.properties.value1),g.data[2]=Number(this.properties.value2), -this.trigger("on_midi",g))};E.prototype.onExecute=function(){var e=this.properties;if(this.inputs)for(var g=0;gc;++c)this.valid_notes[c]=-1!=this.notes_pitches.indexOf(c);for(c=0;12>c;++c)if(this.valid_notes[c])this.offset_notes[c]=0;else for(var e= -1;12>e;++e){if(this.valid_notes[(c-e)%12]){this.offset_notes[c]=-e;break}if(this.valid_notes[(c+e)%12]){this.offset_notes[c]=e;break}}};A.prototype.onAction=function(e,g){g&&g.constructor===c&&(g.data[0]==c.NOTEON||g.data[0]==c.NOTEOFF?(this.midi_event=new c,this.midi_event.setup(g.data),this.midi_event.data[1]+=this.offset_notes[c.note_to_index[g.note]],this.trigger("out",this.midi_event)):this.trigger("out",g))};A.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&c!=this._current_scale&& -this.processScale(c)};t.registerNodeType("midi/quantize",A);g.title="MIDI Play";g.desc="Plays a MIDI note";g.color="#243";g.prototype.onAction=function(e,g){if(g&&g.constructor===c){if(this.instrument&&g.data[0]==c.NOTEON){var k=g.note;if(!k||"undefined"==k||k.constructor!==String)return;this.instrument.play(k,g.octave,this.properties.duration,this.properties.volume)}this.trigger("note",g)}};g.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&(this.properties.volume=c);c=this.getInputData(2); -null!=c&&(this.properties.duration=c)};t.registerNodeType("midi/play",g);n.title="MIDI Keys";n.desc="Keyboard to play notes";n.color="#243";n.keys=[{x:0,w:1,h:1,t:0},{x:0.75,w:0.5,h:0.6,t:1},{x:1,w:1,h:1,t:0},{x:1.75,w:0.5,h:0.6,t:1},{x:2,w:1,h:1,t:0},{x:2.75,w:0.5,h:0.6,t:1},{x:3,w:1,h:1,t:0},{x:4,w:1,h:1,t:0},{x:4.75,w:0.5,h:0.6,t:1},{x:5,w:1,h:1,t:0},{x:5.75,w:0.5,h:0.6,t:1},{x:6,w:1,h:1,t:0}];n.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){var e=12*this.properties.num_octaves; -this.keys.length=e;var g=this.size[0]/(7*this.properties.num_octaves),k=this.size[1];c.globalAlpha=1;for(var a=0;2>a;a++)for(var b=0;bd+h||c[1]>b))return a}}return-1};n.prototype.onAction=function(e,g){if("reset"==e)for(var k=0;kg[1])){var k=this.getKeyIndex(g);this.keys[k]=!0;this._last_key= -k;var k=12*(this.properties.start_octave-1)+29+k,x=new c;x.setup([c.NOTEON,k,100]);this.trigger("note",x);return!0}};n.prototype.onMouseMove=function(e,g){if(!(0>g[1]||-1==this._last_key)){this.setDirtyCanvas(!0);var k=this.getKeyIndex(g);if(this._last_key==k)return!0;this.keys[this._last_key]=!1;var x=12*(this.properties.start_octave-1)+29+this._last_key,a=new c;a.setup([c.NOTEOFF,x,100]);this.trigger("note",a);this.keys[k]=!0;x=12*(this.properties.start_octave-1)+29+k;a=new c;a.setup([c.NOTEON, -x,100]);this.trigger("note",a);this._last_key=k;return!0}};n.prototype.onMouseUp=function(e,g){if(!(0>g[1])){var k=this.getKeyIndex(g);this.keys[k]=!1;this._last_key=-1;var k=12*(this.properties.start_octave-1)+29+k,x=new c;x.setup([c.NOTEOFF,k,100]);this.trigger("note",x);return!0}};t.registerNodeType("midi/keys",n)})(this); -(function(C){function c(){this.properties={src:"",gain:0.5,loop:!0,autoplay:!0,playbackRate:1};this._loading_audio=!1;this._audiobuffer=null;this._audionodes=[];this._last_sourcenode=null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=w.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain;this.properties.src&&this.loadSound(this.properties.src)}function m(){this.properties={gain:0.5};this._audionodes=[];this._media_stream= -null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=w.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain}function p(){this.properties={fftSize:2048,minDecibels:-100,maxDecibels:-10,smoothingTimeConstant:0.5};this.audionode=w.getAudioContext().createAnalyser();this.audionode.graphnode=this;this.audionode.fftSize=this.properties.fftSize;this.audionode.minDecibels=this.properties.minDecibels;this.audionode.maxDecibels= -this.properties.maxDecibels;this.audionode.smoothingTimeConstant=this.properties.smoothingTimeConstant;this.addInput("in","audio");this.addOutput("freqs","array");this.addOutput("samples","array");this._time_bin=this._freq_bin=null}function k(){this.properties={gain:1};this.audionode=w.getAudioContext().createGain();this.addInput("in","audio");this.addInput("gain","number");this.addOutput("out","audio")}function v(){this.properties={impulse_src:"",normalize:!0};this.audionode=w.getAudioContext().createConvolver(); -this.addInput("in","audio");this.addOutput("out","audio")}function e(){this.properties={threshold:-50,knee:40,ratio:12,reduction:-20,attack:0,release:0.25};this.audionode=w.getAudioContext().createDynamicsCompressor();this.addInput("in","audio");this.addOutput("out","audio")}function E(){this.properties={};this.audionode=w.getAudioContext().createWaveShaper();this.addInput("in","audio");this.addInput("shape","waveshape");this.addOutput("out","audio")}function z(){this.properties={gain1:0.5,gain2:0.5}; -this.audionode=w.getAudioContext().createGain();this.audionode1=w.getAudioContext().createGain();this.audionode1.gain.value=this.properties.gain1;this.audionode2=w.getAudioContext().createGain();this.audionode2.gain.value=this.properties.gain2;this.audionode1.connect(this.audionode);this.audionode2.connect(this.audionode);this.addInput("in1","audio");this.addInput("in1 gain","number");this.addInput("in2","audio");this.addInput("in2 gain","number");this.addOutput("out","audio")}function y(){this.properties= -{A:0.1,D:0.1,S:0.1,R:0.1};this.audionode=w.getAudioContext().createGain();this.audionode.gain.value=0;this.addInput("in","audio");this.addInput("gate","bool");this.addOutput("out","audio");this.gate=!1}function D(){this.properties={delayTime:0.5};this.audionode=w.getAudioContext().createDelay(10);this.audionode.delayTime.value=this.properties.delayTime;this.addInput("in","audio");this.addInput("time","number");this.addOutput("out","audio")}function A(){this.properties={frequency:350,detune:0,Q:1}; -this.addProperty("type","lowpass","enum",{values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")});this.audionode=w.getAudioContext().createBiquadFilter();this.addInput("in","audio");this.addOutput("out","audio")}function g(){this.properties={frequency:440,detune:0,type:"sine"};this.addProperty("type","sine","enum",{values:["sine","square","sawtooth","triangle","custom"]});this.audionode=w.getAudioContext().createOscillator();this.addOutput("out","audio")}function n(){this.properties= -{continuous:!0,mark:-1};this.addInput("data","array");this.addInput("mark","number");this.size=[300,200];this._last_buffer=null}function t(){this.properties={band:440,amplitude:1};this.addInput("freqs","array");this.addOutput("signal","number")}function B(){if(!B.default_code){var c=B.default_function.toString(),a=c.indexOf("{")+1,b=c.lastIndexOf("}");B.default_code=c.substr(a,b-a)}this.properties={code:B.default_code};c=w.getAudioContext();c.createScriptProcessor?this.audionode=c.createScriptProcessor(4096, -1,1):(console.warn("ScriptProcessorNode deprecated"),this.audionode=c.createGain());this.processCode();B._bypass_function||(B._bypass_function=this.audionode.onaudioprocess);this.addInput("in","audio");this.addOutput("out","audio")}function l(){this.audionode=w.getAudioContext().destination;this.addInput("in","audio")}var q=C.LiteGraph,w={};C.LGAudio=w;w.getAudioContext=function(){if(!this._audio_context){window.AudioContext=window.AudioContext||window.webkitAudioContext;if(!window.AudioContext)return console.error("AudioContext not supported by browser"), -null;this._audio_context=new AudioContext;this._audio_context.onmessage=function(c){console.log("msg",c)};this._audio_context.onended=function(c){console.log("ended",c)};this._audio_context.oncomplete=function(c){console.log("complete",c)}}return this._audio_context};w.connect=function(c,a){try{c.connect(a)}catch(b){console.warn("LGraphAudio:",b)}};w.disconnect=function(c,a){try{c.disconnect(a)}catch(b){console.warn("LGraphAudio:",b)}};w.changeAllAudiosConnections=function(c,a){if(c.inputs)for(var b= -0;b=c.NOTEON||h<=c.NOTEOFF)this.channel=e&15};Object.defineProperty(c.prototype,"velocity",{get:function(){return this.cmd==c.NOTEON?this.data[2]:-1},set:function(c){this.data[2]=c},enumerable:!0});c.notes="A A# B C C# D D# E F F# G G#".split(" ");c.note_to_index={A:0,"A#":1,B:2,C:3,"C#":4,D:5,"D#":6,E:7,F:8,"F#":9,G:10,"G#":11};Object.defineProperty(c.prototype,"note",{get:function(){return this.cmd!= +c.NOTEON?-1:c.toNoteString(this.data[1],!0)},set:function(c){throw"notes cannot be assigned this way, must modify the data[1]";},enumerable:!0});Object.defineProperty(c.prototype,"octave",{get:function(){return this.cmd!=c.NOTEON?-1:Math.floor((this.data[1]-24)/12+1)},set:function(c){throw"octave cannot be assigned this way, must modify the data[1]";},enumerable:!0});c.prototype.getPitch=function(){return 440*Math.pow(2,(this.data[1]-69)/12)};c.computePitch=function(c){return 440*Math.pow(2,(c-69)/ +12)};c.prototype.getCC=function(){return this.data[1]};c.prototype.getCCValue=function(){return this.data[2]};c.prototype.getPitchBend=function(){return this.data[1]+(this.data[2]<<7)-8192};c.computePitchBend=function(c,e){return c+(e<<7)-8192};c.prototype.setCommandFromString=function(e){this.cmd=c.computeCommandFromString(e)};c.computeCommandFromString=function(e){if(!e)return 0;if(e&&e.constructor===Number)return e;e=e.toUpperCase();switch(e){case "NOTE ON":case "NOTEON":return c.NOTEON;case "NOTE OFF":case "NOTEOFF":return c.NOTEON; +case "KEY PRESSURE":case "KEYPRESSURE":return c.KEYPRESSURE;case "CONTROLLER CHANGE":case "CONTROLLERCHANGE":case "CC":return c.CONTROLLERCHANGE;case "PROGRAM CHANGE":case "PROGRAMCHANGE":case "PC":return c.PROGRAMCHANGE;case "CHANNEL PRESSURE":case "CHANNELPRESSURE":return c.CHANNELPRESSURE;case "PITCH BEND":case "PITCHBEND":return c.PITCHBEND;case "TIME TICK":case "TIMETICK":return c.TIMETICK;default:return Number(e)}};c.toNoteString=function(e,h){e=Math.round(e);var k,a=Math.floor((e-24)/12+1); +k=(e-21)%12;0>k&&(k=12+k);return c.notes[k]+(h?"":a)};c.NoteStringToPitch=function(e){e=e.toUpperCase();var h=e[0],k=4;"#"==e[1]?(h+="#",2this.properties.max_value)return;this.trigger("on_midi",h)}};v.registerNodeType("midi/filter",e);C.title="MIDIEvent";C.desc="Create a MIDI Event";C.color="#243";C.prototype.onAction=function(e,h){"assign"==e?(this.properties.channel=h.channel,this.properties.cmd=h.cmd,this.properties.value1=h.data[1],this.properties.value2=h.data[2],h.cmd== +c.NOTEON?this.gate=!0:h.cmd==c.NOTEOFF&&(this.gate=!1)):(h=this.midi_event,h.channel=this.properties.channel,this.properties.cmd&&this.properties.cmd.constructor===String?h.setCommandFromString(this.properties.cmd):h.cmd=this.properties.cmd,h.data[0]=h.cmd|h.channel,h.data[1]=Number(this.properties.value1),h.data[2]=Number(this.properties.value2),this.trigger("on_midi",h))};C.prototype.onExecute=function(){var e=this.properties;if(this.inputs)for(var h=0;hc;++c)this.valid_notes[c]=-1!=this.notes_pitches.indexOf(c);for(c=0;12>c;++c)if(this.valid_notes[c])this.offset_notes[c]=0;else for(var e=1;12>e;++e){if(this.valid_notes[(c-e)%12]){this.offset_notes[c]=-e;break}if(this.valid_notes[(c+e)%12]){this.offset_notes[c]=e;break}}};A.prototype.onAction=function(e,h){h&&h.constructor===c&&(h.data[0]==c.NOTEON||h.data[0]==c.NOTEOFF?(this.midi_event=new c,this.midi_event.setup(h.data),this.midi_event.data[1]+=this.offset_notes[c.note_to_index[h.note]], +this.trigger("out",this.midi_event)):this.trigger("out",h))};A.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&c!=this._current_scale&&this.processScale(c)};v.registerNodeType("midi/quantize",A);h.title="MIDI fromFile";h.desc="Plays a MIDI file";h.color="#243";h.prototype.onAction=function(c){"play"==c?this.play():"pause"==c&&(this._playing=!this._playing)};h.prototype.onPropertyChanged=function(c,e){"url"==c&&this.loadMIDIFile(e)};h.prototype.onExecute=function(){if(this._midi&& +this._playing){this._current_time+=this.graph.elapsed_time;for(var e=100*this._current_time,h=0;hb;b++)for(var d=0;dg+f||c[1]>d))return b}}return-1};y.prototype.onAction=function(e,h){if("reset"==e)for(var k=0;kh[1])){var k=this.getKeyIndex(h);this.keys[k]=!0;this._last_key=k;var k=12*(this.properties.start_octave-1)+29+k,a=new c;a.setup([c.NOTEON,k,100]);this.trigger("note",a);return!0}};y.prototype.onMouseMove=function(e,h){if(!(0>h[1]||-1==this._last_key)){this.setDirtyCanvas(!0); +var k=this.getKeyIndex(h);if(this._last_key==k)return!0;this.keys[this._last_key]=!1;var a=12*(this.properties.start_octave-1)+29+this._last_key,b=new c;b.setup([c.NOTEOFF,a,100]);this.trigger("note",b);this.keys[k]=!0;a=12*(this.properties.start_octave-1)+29+k;b=new c;b.setup([c.NOTEON,a,100]);this.trigger("note",b);this._last_key=k;return!0}};y.prototype.onMouseUp=function(e,h){if(!(0>h[1])){var k=this.getKeyIndex(h);this.keys[k]=!1;this._last_key=-1;var k=12*(this.properties.start_octave-1)+29+ +k,a=new c;a.setup([c.NOTEOFF,k,100]);this.trigger("note",a);return!0}};v.registerNodeType("midi/keys",y)})(this); +(function(B){function c(){this.properties={src:"",gain:0.5,loop:!0,autoplay:!0,playbackRate:1};this._loading_audio=!1;this._audiobuffer=null;this._audionodes=[];this._last_sourcenode=null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=p.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain;this.properties.src&&this.loadSound(this.properties.src)}function m(){this.properties={gain:0.5};this._audionodes=[];this._media_stream= +null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=p.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain}function n(){this.properties={fftSize:2048,minDecibels:-100,maxDecibels:-10,smoothingTimeConstant:0.5};this.audionode=p.getAudioContext().createAnalyser();this.audionode.graphnode=this;this.audionode.fftSize=this.properties.fftSize;this.audionode.minDecibels=this.properties.minDecibels;this.audionode.maxDecibels= +this.properties.maxDecibels;this.audionode.smoothingTimeConstant=this.properties.smoothingTimeConstant;this.addInput("in","audio");this.addOutput("freqs","array");this.addOutput("samples","array");this._time_bin=this._freq_bin=null}function l(){this.properties={gain:1};this.audionode=p.getAudioContext().createGain();this.addInput("in","audio");this.addInput("gain","number");this.addOutput("out","audio")}function x(){this.properties={impulse_src:"",normalize:!0};this.audionode=p.getAudioContext().createConvolver(); +this.addInput("in","audio");this.addOutput("out","audio")}function e(){this.properties={threshold:-50,knee:40,ratio:12,reduction:-20,attack:0,release:0.25};this.audionode=p.getAudioContext().createDynamicsCompressor();this.addInput("in","audio");this.addOutput("out","audio")}function C(){this.properties={};this.audionode=p.getAudioContext().createWaveShaper();this.addInput("in","audio");this.addInput("shape","waveshape");this.addOutput("out","audio")}function w(){this.properties={gain1:0.5,gain2:0.5}; +this.audionode=p.getAudioContext().createGain();this.audionode1=p.getAudioContext().createGain();this.audionode1.gain.value=this.properties.gain1;this.audionode2=p.getAudioContext().createGain();this.audionode2.gain.value=this.properties.gain2;this.audionode1.connect(this.audionode);this.audionode2.connect(this.audionode);this.addInput("in1","audio");this.addInput("in1 gain","number");this.addInput("in2","audio");this.addInput("in2 gain","number");this.addOutput("out","audio")}function u(){this.properties= +{A:0.1,D:0.1,S:0.1,R:0.1};this.audionode=p.getAudioContext().createGain();this.audionode.gain.value=0;this.addInput("in","audio");this.addInput("gate","bool");this.addOutput("out","audio");this.gate=!1}function D(){this.properties={delayTime:0.5};this.audionode=p.getAudioContext().createDelay(10);this.audionode.delayTime.value=this.properties.delayTime;this.addInput("in","audio");this.addInput("time","number");this.addOutput("out","audio")}function A(){this.properties={frequency:350,detune:0,Q:1}; +this.addProperty("type","lowpass","enum",{values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")});this.audionode=p.getAudioContext().createBiquadFilter();this.addInput("in","audio");this.addOutput("out","audio")}function h(){this.properties={frequency:440,detune:0,type:"sine"};this.addProperty("type","sine","enum",{values:["sine","square","sawtooth","triangle","custom"]});this.audionode=p.getAudioContext().createOscillator();this.addOutput("out","audio")}function z(){this.properties= +{continuous:!0,mark:-1};this.addInput("data","array");this.addInput("mark","number");this.size=[300,200];this._last_buffer=null}function y(){this.properties={band:440,amplitude:1};this.addInput("freqs","array");this.addOutput("signal","number")}function v(){if(!v.default_code){var c=v.default_function.toString(),a=c.indexOf("{")+1,b=c.lastIndexOf("}");v.default_code=c.substr(a,b-a)}this.properties={code:v.default_code};c=p.getAudioContext();c.createScriptProcessor?this.audionode=c.createScriptProcessor(4096, +1,1):(console.warn("ScriptProcessorNode deprecated"),this.audionode=c.createGain());this.processCode();v._bypass_function||(v._bypass_function=this.audionode.onaudioprocess);this.addInput("in","audio");this.addOutput("out","audio")}function G(){this.audionode=p.getAudioContext().destination;this.addInput("in","audio")}var q=B.LiteGraph,p={};B.LGAudio=p;p.getAudioContext=function(){if(!this._audio_context){window.AudioContext=window.AudioContext||window.webkitAudioContext;if(!window.AudioContext)return console.error("AudioContext not supported by browser"), +null;this._audio_context=new AudioContext;this._audio_context.onmessage=function(c){console.log("msg",c)};this._audio_context.onended=function(c){console.log("ended",c)};this._audio_context.oncomplete=function(c){console.log("complete",c)}}return this._audio_context};p.connect=function(c,a){try{c.connect(a)}catch(b){console.warn("LGraphAudio:",b)}};p.disconnect=function(c,a){try{c.disconnect(a)}catch(b){console.warn("LGraphAudio:",b)}};p.changeAllAudiosConnections=function(c,a){if(c.inputs)for(var b= +0;b=this.size[0]&&(e=this.size[0]-1),c.strokeStyle="red",c.beginPath(),c.moveTo(e,d),c.lineTo(e,0),c.stroke())}};n.title="Visualization";n.desc="Audio Visualization";q.registerNodeType("audio/visualization",n);t.prototype.onExecute=function(){if(this._freqs=this.getInputData(0)){var c=this.properties.band,a=this.getInputData(1); -void 0!==a&&(c=a);a=w.getAudioContext().sampleRate/this._freqs.length;a=c/a*2;a>=this._freqs.length?a=this._freqs[this._freqs.length-1]:(c=a|0,a-=c,a=this._freqs[c]*(1-a)+this._freqs[c+1]*a);this.setOutputData(0,a/255*this.properties.amplitude)}};t.prototype.onGetInputs=function(){return[["band","number"]]};t.title="Signal";t.desc="extract the signal of some frequency";q.registerNodeType("audio/signal",t);B.prototype.onAdded=function(c){c.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess= -this._callback)};B["@code"]={widget:"code",type:"code"};B.prototype.onStart=function(){this.audionode.onaudioprocess=this._callback};B.prototype.onStop=function(){this.audionode.onaudioprocess=B._bypass_function};B.prototype.onPause=function(){this.audionode.onaudioprocess=B._bypass_function};B.prototype.onUnpause=function(){this.audionode.onaudioprocess=this._callback};B.prototype.onExecute=function(){};B.prototype.onRemoved=function(){this.audionode.onaudioprocess=B._bypass_function};B.prototype.processCode= -function(){try{this._script=new new Function("properties",this.properties.code)(this.properties),this._old_code=this.properties.code,this._callback=this._script.onaudioprocess}catch(c){console.error("Error in onaudioprocess code",c),this._callback=B._bypass_function,this.audionode.onaudioprocess=this._callback}};B.prototype.onPropertyChanged=function(c,a){"code"==c&&(this.properties.code=a,this.processCode(),this.graph&&this.graph.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback))}; -B.default_function=function(){this.onaudioprocess=function(c){var a=c.inputBuffer;c=c.outputBuffer;for(var b=0;b=this.size[0]&&(e=this.size[0]-1),c.strokeStyle="red",c.beginPath(),c.moveTo(e,d),c.lineTo(e,0),c.stroke())}};z.title="Visualization";z.desc="Audio Visualization";q.registerNodeType("audio/visualization",z);y.prototype.onExecute=function(){if(this._freqs=this.getInputData(0)){var c=this.properties.band,a=this.getInputData(1); +void 0!==a&&(c=a);a=p.getAudioContext().sampleRate/this._freqs.length;a=c/a*2;a>=this._freqs.length?a=this._freqs[this._freqs.length-1]:(c=a|0,a-=c,a=this._freqs[c]*(1-a)+this._freqs[c+1]*a);this.setOutputData(0,a/255*this.properties.amplitude)}};y.prototype.onGetInputs=function(){return[["band","number"]]};y.title="Signal";y.desc="extract the signal of some frequency";q.registerNodeType("audio/signal",y);v.prototype.onAdded=function(c){c.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess= +this._callback)};v["@code"]={widget:"code",type:"code"};v.prototype.onStart=function(){this.audionode.onaudioprocess=this._callback};v.prototype.onStop=function(){this.audionode.onaudioprocess=v._bypass_function};v.prototype.onPause=function(){this.audionode.onaudioprocess=v._bypass_function};v.prototype.onUnpause=function(){this.audionode.onaudioprocess=this._callback};v.prototype.onExecute=function(){};v.prototype.onRemoved=function(){this.audionode.onaudioprocess=v._bypass_function};v.prototype.processCode= +function(){try{this._script=new new Function("properties",this.properties.code)(this.properties),this._old_code=this.properties.code,this._callback=this._script.onaudioprocess}catch(c){console.error("Error in onaudioprocess code",c),this._callback=v._bypass_function,this.audionode.onaudioprocess=this._callback}};v.prototype.onPropertyChanged=function(c,a){"code"==c&&(this.properties.code=a,this.processCode(),this.graph&&this.graph.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback))}; +v.default_function=function(){this.onaudioprocess=function(c){var a=c.inputBuffer;c=c.outputBuffer;for(var b=0;b + + diff --git a/demo/js/libs/midi-parser.js b/demo/js/libs/midi-parser.js new file mode 100644 index 000000000..6d0d53de2 --- /dev/null +++ b/demo/js/libs/midi-parser.js @@ -0,0 +1,356 @@ +/* + Project Name : midi-parser-js + Project Url : https://github.com/colxi/midi-parser-js/ + Author : colxi + Author URL : http://www.colxi.info/ + Description : MidiParser library reads .MID binary files, Base64 encoded MIDI Data, + or UInt8 Arrays, and outputs as a readable and structured JS object. +*/ + +(function(){ + 'use strict'; + + /** + * CROSSBROWSER & NODEjs POLYFILL for ATOB() - + * By: https://github.com/MaxArt2501 (modified) + * @param {string} string [description] + * @return {[type]} [description] + */ + const _atob = function(string) { + // base64 character set, plus padding character (=) + let b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; + // Regular expression to check formal correctness of base64 encoded strings + let b64re = /^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/; + // remove data type signatures at the begining of the string + // eg : "data:audio/mid;base64," + string = string.replace( /^.*?base64,/ , ''); + // atob can work with strings with whitespaces, even inside the encoded part, + // but only \t, \n, \f, \r and ' ', which can be stripped. + string = String(string).replace(/[\t\n\f\r ]+/g, ''); + if (!b64re.test(string)) + throw new TypeError('Failed to execute _atob() : The string to be decoded is not correctly encoded.'); + + // Adding the padding if missing, for semplicity + string += '=='.slice(2 - (string.length & 3)); + let bitmap, result = ''; + let r1, r2, i = 0; + for (; i < string.length;) { + bitmap = b64.indexOf(string.charAt(i++)) << 18 | b64.indexOf(string.charAt(i++)) << 12 + | (r1 = b64.indexOf(string.charAt(i++))) << 6 | (r2 = b64.indexOf(string.charAt(i++))); + + result += r1 === 64 ? String.fromCharCode(bitmap >> 16 & 255) + : r2 === 64 ? String.fromCharCode(bitmap >> 16 & 255, bitmap >> 8 & 255) + : String.fromCharCode(bitmap >> 16 & 255, bitmap >> 8 & 255, bitmap & 255); + } + return result; + }; + + + /** + * [MidiParser description] + * @type {Object} + */ + const MidiParser = { + // debug (bool), when enabled will log in console unimplemented events + // warnings and internal handled errors. + debug: false, + + /** + * [parse description] + * @param {[type]} input [description] + * @param {[type]} _callback [description] + * @return {[type]} [description] + */ + parse: function(input, _callback){ + if(input instanceof Uint8Array) return MidiParser.Uint8(input); + else if(typeof input === 'string') return MidiParser.Base64(input); + else if(input instanceof HTMLElement && input.type === 'file') return MidiParser.addListener(input , _callback); + else throw new Error('MidiParser.parse() : Invalid input provided'); + }, + + /** + * addListener() should be called in order attach a listener to the INPUT HTML element + * that will provide the binary data automating the conversion, and returning + * the structured data to the provided callback function. + */ + addListener: function(_fileElement, _callback){ + if(!File || !FileReader) throw new Error('The File|FileReader APIs are not supported in this browser. Use instead MidiParser.Base64() or MidiParser.Uint8()'); + + // validate provided element + if( _fileElement === undefined || + !(_fileElement instanceof HTMLElement) || + _fileElement.tagName !== 'INPUT' || + _fileElement.type.toLowerCase() !== 'file' + ){ + console.warn('MidiParser.addListener() : Provided element is not a valid FILE INPUT element'); + return false; + } + _callback = _callback || function(){}; + + _fileElement.addEventListener('change', function(InputEvt){ // set the 'file selected' event handler + if (!InputEvt.target.files.length) return false; // return false if no elements where selected + console.log('MidiParser.addListener() : File detected in INPUT ELEMENT processing data..'); + let reader = new FileReader(); // prepare the file Reader + reader.readAsArrayBuffer(InputEvt.target.files[0]); // read the binary data + reader.onload = function(e){ + _callback( MidiParser.Uint8(new Uint8Array(e.target.result))); // encode data with Uint8Array and call the parser + }; + }); + }, + + /** + * Base64() : convert baset4 string into uint8 array buffer, before performing the + * parsing subroutine. + */ + Base64 : function(b64String){ + b64String = String(b64String); + + let raw = _atob(b64String); + let rawLength = raw.length; + let t_array = new Uint8Array(new ArrayBuffer(rawLength)); + + for(let i=0; i 1){ + for(let i=1; i<= (_bytes-1); i++){ + value += this.data.getUint8(this.pointer) * Math.pow(256, (_bytes - i)); + this.pointer++; + } + } + value += this.data.getUint8(this.pointer); + this.pointer++; + return value; + }, + readStr: function(_bytes){ // read as ASCII chars, the followoing _bytes + let text = ''; + for(let char=1; char <= _bytes; char++) text += String.fromCharCode(this.readInt(1)); + return text; + }, + readIntVLV: function(){ // read a variable length value + let value = 0; + if ( this.pointer >= this.data.byteLength ){ + return -1; // EOF + }else if(this.data.getUint8(this.pointer) < 128){ // ...value in a single byte + value = this.readInt(1); + }else{ // ...value in multiple bytes + let FirstBytes = []; + while(this.data.getUint8(this.pointer) >= 128){ + FirstBytes.push(this.readInt(1) - 128); + } + let lastByte = this.readInt(1); + for(let dt = 1; dt <= FirstBytes.length; dt++){ + value += FirstBytes[FirstBytes.length - dt] * Math.pow(128, dt); + } + value += lastByte; + } + return value; + } + }; + + file.data = new DataView(FileAsUint8Array.buffer, FileAsUint8Array.byteOffset, FileAsUint8Array.byteLength); // 8 bits bytes file data array + // ** read FILE HEADER + if(file.readInt(4) !== 0x4D546864){ + console.warn('Header validation failed (not MIDI standard or file corrupt.)'); + return false; // Header validation failed (not MIDI standard or file corrupt.) + } + let headerSize = file.readInt(4); // header size (unused var), getted just for read pointer movement + let MIDI = {}; // create new midi object + MIDI.formatType = file.readInt(2); // get MIDI Format Type + MIDI.tracks = file.readInt(2); // get ammount of track chunks + MIDI.track = []; // create array key for track data storing + let timeDivisionByte1 = file.readInt(1); // get Time Division first byte + let timeDivisionByte2 = file.readInt(1); // get Time Division second byte + if(timeDivisionByte1 >= 128){ // discover Time Division mode (fps or tpf) + MIDI.timeDivision = []; + MIDI.timeDivision[0] = timeDivisionByte1 - 128; // frames per second MODE (1st byte) + MIDI.timeDivision[1] = timeDivisionByte2; // ticks in each frame (2nd byte) + }else MIDI.timeDivision = (timeDivisionByte1 * 256) + timeDivisionByte2;// else... ticks per beat MODE (2 bytes value) + + // ** read TRACK CHUNK + for(let t=1; t <= MIDI.tracks; t++){ + MIDI.track[t-1] = {event: []}; // create new Track entry in Array + let headerValidation = file.readInt(4); + if ( headerValidation === -1 ) break; // EOF + if(headerValidation !== 0x4D54726B) return false; // Track chunk header validation failed. + file.readInt(4); // move pointer. get chunk size (bytes length) + let e = 0; // init event counter + let endOfTrack = false; // FLAG for track reading secuence breaking + // ** read EVENT CHUNK + let statusByte; + let laststatusByte; + while(!endOfTrack){ + e++; // increase by 1 event counter + MIDI.track[t-1].event[e-1] = {}; // create new event object, in events array + MIDI.track[t-1].event[e-1].deltaTime = file.readIntVLV(); // get DELTA TIME OF MIDI event (Variable Length Value) + statusByte = file.readInt(1); // read EVENT TYPE (STATUS BYTE) + if(statusByte === -1) break; // EOF + else if(statusByte >= 128) laststatusByte = statusByte; // NEW STATUS BYTE DETECTED + else{ // 'RUNNING STATUS' situation detected + statusByte = laststatusByte; // apply last loop, Status Byte + file.movePointer(-1); // move back the pointer (cause readed byte is not status byte) + } + + + // + // ** IS META EVENT + // + if(statusByte === 0xFF){ // Meta Event type + MIDI.track[t-1].event[e-1].type = 0xFF; // assign metaEvent code to array + MIDI.track[t-1].event[e-1].metaType = file.readInt(1); // assign metaEvent subtype + let metaEventLength = file.readIntVLV(); // get the metaEvent length + switch(MIDI.track[t-1].event[e-1].metaType){ + case 0x2F: // end of track, has no data byte + case -1: // EOF + endOfTrack = true; // change FLAG to force track reading loop breaking + break; + case 0x01: // Text Event + case 0x02: // Copyright Notice + case 0x03: + case 0x04: // Instrument Name + case 0x05: // Lyrics) + case 0x07: // Cue point // Sequence/Track Name (documentation: http://www.ta7.de/txt/musik/musi0006.htm) + case 0x06: // Marker + MIDI.track[t-1].event[e-1].data = file.readStr(metaEventLength); + break; + case 0x21: // MIDI PORT + case 0x59: // Key Signature + case 0x51: // Set Tempo + MIDI.track[t-1].event[e-1].data = file.readInt(metaEventLength); + break; + case 0x54: // SMPTE Offset + MIDI.track[t-1].event[e-1].data = []; + MIDI.track[t-1].event[e-1].data[0] = file.readInt(1); + MIDI.track[t-1].event[e-1].data[1] = file.readInt(1); + MIDI.track[t-1].event[e-1].data[2] = file.readInt(1); + MIDI.track[t-1].event[e-1].data[3] = file.readInt(1); + MIDI.track[t-1].event[e-1].data[4] = file.readInt(1); + break; + case 0x58: // Time Signature + MIDI.track[t-1].event[e-1].data = []; + MIDI.track[t-1].event[e-1].data[0] = file.readInt(1); + MIDI.track[t-1].event[e-1].data[1] = file.readInt(1); + MIDI.track[t-1].event[e-1].data[2] = file.readInt(1); + MIDI.track[t-1].event[e-1].data[3] = file.readInt(1); + break; + default : + // if user provided a custom interpreter, call it + // and assign to event the returned data + if( this.customInterpreter !== null){ + MIDI.track[t-1].event[e-1].data = this.customInterpreter( MIDI.track[t-1].event[e-1].metaType, file, metaEventLength); + } + // if no customInterpretr is provided, or returned + // false (=apply default), perform default action + if(this.customInterpreter === null || MIDI.track[t-1].event[e-1].data === false){ + file.readInt(metaEventLength); + MIDI.track[t-1].event[e-1].data = file.readInt(metaEventLength); + if (this.debug) console.info('Unimplemented 0xFF meta event! data block readed as Integer'); + } + } + } + + // + // IS REGULAR EVENT + // + else{ // MIDI Control Events OR System Exclusive Events + statusByte = statusByte.toString(16).split(''); // split the status byte HEX representation, to obtain 4 bits values + if(!statusByte[1]) statusByte.unshift('0'); // force 2 digits + MIDI.track[t-1].event[e-1].type = parseInt(statusByte[0], 16);// first byte is EVENT TYPE ID + MIDI.track[t-1].event[e-1].channel = parseInt(statusByte[1], 16);// second byte is channel + switch(MIDI.track[t-1].event[e-1].type){ + case 0xF:{ // System Exclusive Events + + // if user provided a custom interpreter, call it + // and assign to event the returned data + if( this.customInterpreter !== null){ + MIDI.track[t-1].event[e-1].data = this.customInterpreter( MIDI.track[t-1].event[e-1].type, file , false); + } + + // if no customInterpretr is provided, or returned + // false (=apply default), perform default action + if(this.customInterpreter === null || MIDI.track[t-1].event[e-1].data === false){ + let event_length = file.readIntVLV(); + MIDI.track[t-1].event[e-1].data = file.readInt(event_length); + if (this.debug) console.info('Unimplemented 0xF exclusive events! data block readed as Integer'); + } + break; + } + case 0xA: // Note Aftertouch + case 0xB: // Controller + case 0xE: // Pitch Bend Event + case 0x8: // Note off + case 0x9: // Note On + MIDI.track[t-1].event[e-1].data = []; + MIDI.track[t-1].event[e-1].data[0] = file.readInt(1); + MIDI.track[t-1].event[e-1].data[1] = file.readInt(1); + break; + case 0xC: // Program Change + case 0xD: // Channel Aftertouch + MIDI.track[t-1].event[e-1].data = file.readInt(1); + break; + case -1: // EOF + endOfTrack = true; // change FLAG to force track reading loop breaking + break; + default: + // if user provided a custom interpreter, call it + // and assign to event the returned data + if( this.customInterpreter !== null){ + MIDI.track[t-1].event[e-1].data = this.customInterpreter( MIDI.track[t-1].event[e-1].metaType, file , false); + } + + // if no customInterpretr is provided, or returned + // false (=apply default), perform default action + if(this.customInterpreter === null || MIDI.track[t-1].event[e-1].data === false){ + console.log('Unknown EVENT detected... reading cancelled!'); + return false; + } + } + } + } + } + return MIDI; + }, + + /** + * custom function to handle unimplemented, or custom midi messages. + * If message is a meta-event, the value of metaEventLength will be >0. + * Function must return the value to store, and pointer of dataView needs + * to be manually increased + * If you want default action to be performed, return false + */ + customInterpreter : null // function( e_type , arrayByffer, metaEventLength){ return e_data_int } + }; + + + // if running in NODE export module + if(typeof module !== 'undefined') module.exports = MidiParser; + else{ + // if running in Browser, set a global variable. + let _global = typeof window === 'object' && window.self === window && window || + typeof self === 'object' && self.self === self && self || + typeof global === 'object' && global.global === global && global; + + _global.MidiParser = MidiParser; + } + + + +})(); \ No newline at end of file diff --git a/src/litegraph-editor.js b/src/litegraph-editor.js index afd13000a..8f300e11e 100755 --- a/src/litegraph-editor.js +++ b/src/litegraph-editor.js @@ -142,6 +142,16 @@ Editor.prototype.createPanel = function(title, options) { this.parentNode.removeChild(this); } + root.addHTML = function(code, classname) + { + var elem = document.createElement("div"); + if(classname) + elem.className = classname; + elem.innerHTML = code; + root.content.appendChild(elem); + return elem; + } + root.addButton = function( name, callback, options ) { var elem = document.createElement("button"); @@ -152,6 +162,13 @@ Editor.prototype.createPanel = function(title, options) { return elem; } + root.addSeparator = function() + { + var elem = document.createElement("div"); + elem.className = "separator"; + root.content.appendChild(elem); + } + root.addWidget = function( type, name, value, options, callback ) { options = options || {}; @@ -332,92 +349,40 @@ Editor.prototype.onShowNodePanel = function(node) function inner_refresh() { panel.content.innerHTML = ""; //clear - var elem = document.createElement("div"); - elem.innerHTML = ""+node.type+""+(node.constructor.desc || "")+""; - panel.content.appendChild(elem); + panel.addHTML(""+node.type+""+(node.constructor.desc || "")+""); + + panel.addHTML("

Properties

"); for(var i in node.properties) { var value = node.properties[i]; var info = node.getPropertyInfo(i); var type = info.type || "string"; + + //in case the user wants control over the side panel widget + if( node.onAddPropertyToPanel && node.onAddPropertyToPanel(i,panel) ) + continue; + panel.addWidget( info.widget || info.type, i, value, info, function(name,value){ node.setProperty(name,value); graphcanvas.dirty_canvas = true; }); } + panel.addSeparator(); + + /* + panel.addHTML("

Connections

"); + var connection_containers = panel.addHTML("
","connections"); + var inputs = connection_containers.querySelector(".inputs"); + var outputs = connection_containers.querySelector(".outputs"); + */ + + panel.addButton("Delete",function(){ node.graph.remove(node); panel.close(); }).classList.add("delete"); - - /* - for(var i in node.properties) - { - var value = node.properties[i]; - var type = "string"; - var info = node.getPropertyInfo(i); - var type = info.type; - - var str_value = String(value); - if(type == "number") - str_value = value.toFixed(3); - - var elem = document.createElement("div"); - elem.className = "property"; - elem.innerHTML = ""; - elem.querySelector(".property_name").innerText = i; - var value_element = elem.querySelector(".property_value"); - value_element.innerText = str_value; - elem.dataset["property"] = i; - elem.dataset["type"] = type; - elem.datainfo = info; - - if( type == "code" ) - elem.addEventListener("click", function(){ inner_showCodePad( node, this.dataset["property"] ); }); - else if (type == "boolean") - elem.addEventListener("click", function(){ - var v = node.properties[this.dataset["property"]]; - node.setProperty(this.dataset["property"],!v); this.innerText = v ? "true" : "false"; - }); - else if (type == "string" || type == "number") - { - value_element.setAttribute("contenteditable",true); - value_element.addEventListener("change", function(){ - var v = this.innerText; - var propname = this.parentNode.dataset["property"]; - if( propname == "number") - v = Number(v); - node.setProperty(propname,v); - that.dirty_canvas = true; - }); - } - else if (type == "enum") - value_element.addEventListener("click", function(event){ - var values = this.parentNode.datainfo.values || []; - var propname = this.parentNode.dataset["property"]; - var elem_that = this; - var menu = new LiteGraph.ContextMenu(values,{ - event: event, - className: "dark", - callback: inner_clicked - }, - ref_window); - function inner_clicked(v, option, event) { - node.setProperty(propname,v); - elem_that.innerText = v; - graphcanvas.dirty_canvas = true; - return false; - } - }); - else //generic - elem.addEventListener("click", function(){ - that.graphcanvas.showEditPropertyValue( node, this.dataset["property"], {onclose: inner_refresh} ); - }); - panel.content.appendChild(elem); - } - */ } function inner_showCodePad( node, propname ) diff --git a/src/litegraph.js b/src/litegraph.js index 8ae24ca30..7de6f9ac0 100755 --- a/src/litegraph.js +++ b/src/litegraph.js @@ -392,7 +392,6 @@ * @method getNodeTypesCategories * @return {Array} array with all the names of the categories */ - getNodeTypesCategories: function( filter ) { var categories = { "": 1 }; for (var i in this.registered_node_types) { @@ -472,6 +471,13 @@ return target; }, + /** + * Returns if the types of two slots are compatible (taking into account wildcards, etc) + * @method isValidConnection + * @param {String} type_a + * @param {String} type_b + * @return {Boolean} true if they can be connected + */ isValidConnection: function(type_a, type_b) { if ( !type_a || //generic output @@ -507,13 +513,85 @@ return false; }, + /** + * Register a string in the search box so when the user types it it will recommend this node + * @method registerSearchboxExtra + * @param {String} node_type the node recommended + * @param {String} description text to show next to it + * @param {Object} data it could contain info of how the node should be configured + * @return {Boolean} true if they can be connected + */ registerSearchboxExtra: function(node_type, description, data) { this.searchbox_extras[description.toLowerCase()] = { type: node_type, desc: description, data: data }; - } + }, + + /** + * Wrapper to load files (from url using fetch or from file using FileReader) + * @method fetchFile + * @param {String|File|Blob} url the url of the file (or the file itself) + * @param {String} type an string to know how to fetch it: "text","arraybuffer","json","blob" + * @param {Function} on_complete callback(data) + * @param {Function} on_error in case of an error + * @return {FileReader|Promise} returns the object used to + */ + fetchFile: function( url, type, on_complete, on_error ) { + var that = this; + if(!url) + return null; + + type = type || "text"; + if( url.constructor === String ) + { + if (url.substr(0, 4) == "http" && LiteGraph.proxy) { + url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); + } + return fetch(url) + .then(function(response) { + if(!response.ok) + throw new Error("File not found"); //it will be catch below + if(type == "arraybuffer") + return response.arrayBuffer(); + else if(type == "text" || type == "string") + return response.text(); + else if(type == "json") + return response.json(); + else if(type == "blob") + return response.blob(); + }) + .then(function(data) { + if(on_complete) + on_complete(data); + }) + .catch(function(error) { + console.error("error fetching file:",url); + if(on_error) + on_error(error); + }); + } + else if( url.constructor === File || url.constructor === Blob) + { + var reader = new FileReader(); + reader.onload = function(e) + { + var v = e.target.result; + if( type == "json" ) + v = JSON.parse(v); + if(on_complete) + on_complete(v); + } + if(type == "arraybuffer") + return reader.readAsArrayBuffer(url); + else if(type == "text" || type == "json") + return reader.readAsText(url); + else if(type == "blob") + return reader.readAsBinaryString(url); + } + return null; + } }); //timer that works everywhere @@ -8413,7 +8491,10 @@ LGraphNode.prototype.executeAction = function(action) if (values && values.constructor === Function) { values = w.options.values(w, node); } - var values_list = values.constructor === Array ? values : Object.keys(values); + var values_list = null; + + if( w.type != "number") + values_list = values.constructor === Array ? values : Object.keys(values); var delta = x < 40 ? -1 : x > width - 40 ? 1 : 0; if (w.type == "number") { diff --git a/src/nodes/base.js b/src/nodes/base.js index 9608c0c63..cb2902eb7 100755 --- a/src/nodes/base.js +++ b/src/nodes/base.js @@ -530,6 +530,15 @@ ConstantBoolean.prototype.setValue = ConstantNumber.prototype.setValue; + ConstantBoolean.prototype.onGetInputs = function() { + return [["toggle", LiteGraph.ACTION]]; + }; + + ConstantBoolean.prototype.onAction = function(action) + { + this.setValue( !this.properties.value ); + } + LiteGraph.registerNodeType("basic/boolean", ConstantBoolean); function ConstantString() { diff --git a/src/nodes/math.js b/src/nodes/math.js index 58be04878..2761eafa8 100755 --- a/src/nodes/math.js +++ b/src/nodes/math.js @@ -110,6 +110,7 @@ function MathRange() { this.addInput("in", "number", { locked: true }); this.addOutput("out", "number", { locked: true }); + this.addOutput("clamped", "number", { locked: true }); this.addProperty("in", 0); this.addProperty("in_min", 0); @@ -117,7 +118,7 @@ this.addProperty("out_min", 0); this.addProperty("out_max", 1); - this.size = [80, 30]; + this.size = [120, 50]; } MathRange.title = "Range"; @@ -151,10 +152,22 @@ var in_max = this.properties.in_max; var out_min = this.properties.out_min; var out_max = this.properties.out_max; + /* + if( in_min > in_max ) + { + in_min = in_max; + in_max = this.properties.in_min; + } + if( out_min > out_max ) + { + out_min = out_max; + out_max = this.properties.out_min; + } + */ - this._last_v = - ((v - in_min) / (in_max - in_min)) * (out_max - out_min) + out_min; + this._last_v = ((v - in_min) / (in_max - in_min)) * (out_max - out_min) + out_min; this.setOutputData(0, this._last_v); + this.setOutputData(1, Math.clamp( this._last_v, out_min, out_max )); }; MathRange.prototype.onDrawBackground = function(ctx) { diff --git a/src/nodes/math3d.js b/src/nodes/math3d.js index 176588b68..ae11583c6 100644 --- a/src/nodes/math3d.js +++ b/src/nodes/math3d.js @@ -464,6 +464,21 @@ var target_min = this.properties.target_min; var target_max = this.properties.target_max; + //swap to avoid errors + /* + if(range_min > range_max) + { + range_min = range_max; + range_max = this.properties.range_min; + } + + if(target_min > target_max) + { + target_min = target_max; + target_max = this.properties.target_min; + } + */ + for(var i = 0; i < 3; ++i) { var r = range_max[i] - range_min[i]; diff --git a/src/nodes/midi.js b/src/nodes/midi.js index 9d0908e4c..1f4eb0cc6 100644 --- a/src/nodes/midi.js +++ b/src/nodes/midi.js @@ -882,6 +882,24 @@ this.properties.value1 = (v | 0) % 255; } break; + case "cmd": + var v = this.getInputData(i); + if (v != null) { + this.properties.cmd = v; + } + break; + case "value1": + var v = this.getInputData(i); + if (v != null) { + this.properties.value1 = Math.clamp(v|0,0,127); + } + break; + case "value2": + var v = this.getInputData(i); + if (v != null) { + this.properties.value2 = Math.clamp(v|0,0,127); + } + break; } } } @@ -950,7 +968,7 @@ }; LGMIDIEvent.prototype.onGetInputs = function() { - return [["note", "number"]]; + return [["cmd", "number"],["note", "number"],["value1", "number"],["value2", "number"]]; }; LGMIDIEvent.prototype.onGetOutputs = function() { @@ -1207,6 +1225,119 @@ LiteGraph.registerNodeType("midi/quantize", LGMIDIQuantize); + function LGMIDIFromFile() { + this.properties = { + url: "", + autoplay: true + }; + + this.addInput("play", LiteGraph.ACTION); + this.addInput("pause", LiteGraph.ACTION); + this.addOutput("note", LiteGraph.EVENT); + this._midi = null; + this._current_time = 0; + this._playing = false; + + if (typeof MidiParser == "undefined") { + console.error( + "midi-parser.js not included, LGMidiPlay requires that library: https://raw.githubusercontent.com/colxi/midi-parser-js/master/src/main.js" + ); + this.boxcolor = "red"; + } + + } + + LGMIDIFromFile.title = "MIDI fromFile"; + LGMIDIFromFile.desc = "Plays a MIDI file"; + LGMIDIFromFile.color = MIDI_COLOR; + + LGMIDIFromFile.prototype.onAction = function( name ) + { + if(name == "play") + this.play(); + else if(name == "pause") + this._playing = !this._playing; + } + + LGMIDIFromFile.prototype.onPropertyChanged = function(name,value) + { + if(name == "url") + this.loadMIDIFile(value); + } + + LGMIDIFromFile.prototype.onExecute = function() { + if(!this._midi) + return; + + if(!this._playing) + return; + + this._current_time += this.graph.elapsed_time; + var current_time = this._current_time * 100; + + for(var i = 0; i < this._midi.tracks; ++i) + { + var track = this._midi.track[i]; + if(!track._last_pos) + { + track._last_pos = 0; + track._time = 0; + } + + var elem = track.event[ track._last_pos ]; + if(elem && (track._time + elem.deltaTime) <= current_time ) + { + track._last_pos++; + track._time += elem.deltaTime; + + if(elem.data) + { + var midi_cmd = elem.type << 4 + elem.channel; + var midi_event = new MIDIEvent(); + midi_event.setup([midi_cmd, elem.data[0], elem.data[1]]); + this.trigger("note", midi_event); + } + } + + } + }; + + LGMIDIFromFile.prototype.play = function() + { + this._playing = true; + this._current_time = 0; + for(var i = 0; i < this._midi.tracks; ++i) + { + var track = this._midi.track[i]; + track._last_pos = 0; + track._time = 0; + } + } + + LGMIDIFromFile.prototype.loadMIDIFile = function(url) + { + var that = this; + LiteGraph.fetchFile( url, "arraybuffer", function(data) + { + that.boxcolor = "#AFA"; + that._midi = MidiParser.parse( new Uint8Array(data) ); + if(that.properties.autoplay) + that.play(); + }, function(err){ + that.boxcolor = "#FAA"; + that._midi = null; + }); + } + + LGMIDIFromFile.prototype.onDropFile = function(file) + { + this.properties.url = ""; + this.loadMIDIFile( file ); + } + + LiteGraph.registerNodeType("midi/fromFile", LGMIDIFromFile); + + function LGMIDIPlay() { this.properties = { volume: 0.5, From 6ff8bb700dff1c6e22551a4cea84b8ea4d98f8e0 Mon Sep 17 00:00:00 2001 From: tamat Date: Tue, 21 Apr 2020 12:38:54 +0200 Subject: [PATCH 21/63] fix in roundRect --- build/litegraph.js | 10 +- build/litegraph.min.js | 286 ++++++++++++++++++++--------------------- src/litegraph.js | 8 +- 3 files changed, 152 insertions(+), 152 deletions(-) diff --git a/build/litegraph.js b/build/litegraph.js index 6116c5c4b..926530b2f 100644 --- a/build/litegraph.js +++ b/build/litegraph.js @@ -10196,8 +10196,8 @@ LGraphNode.prototype.executeAction = function(action) //API ************************************************* //like rect but rounded corners - if (this.CanvasRenderingContext2D) { - CanvasRenderingContext2D.prototype.roundRect = function( + if (global.CanvasRenderingContext2D) { + global.CanvasRenderingContext2D.prototype.roundRect = function( x, y, width, @@ -27616,10 +27616,10 @@ LiteGraph.registerNodeType("audio/waveShaper", LGAudioWaveShaper); this._ws.onmessage = function(e) { that.boxcolor = "#AFA"; var data = JSON.parse(e.data); - if (data.room && data.room != this.properties.room) { + if (data.room && data.room != that.properties.room) { return; } - if (e.data.type == 1) { + if (data.type == 1) { if ( data.data.object_class && LiteGraph[data.data.object_class] @@ -27635,7 +27635,7 @@ LiteGraph.registerNodeType("audio/waveShaper", LGAudioWaveShaper); that.triggerSlot(0, data.data); } } else { - that._last_received_data[e.data.channel || 0] = data.data; + that._last_received_data[data.channel || 0] = data.data; } }; this._ws.onerror = function(e) { diff --git a/build/litegraph.min.js b/build/litegraph.min.js index cfe4fef25..0d7846dbd 100755 --- a/build/litegraph.min.js +++ b/build/litegraph.min.js @@ -28,8 +28,8 @@ c.STATUS_RUNNING){this.status=c.STATUS_RUNNING;if(this.onPlayEvent)this.onPlayEv b.runStep(1,!this.catch_errors);if(b.onAfterStep)b.onAfterStep()},a)}};c.prototype.stop=function(){if(this.status!=c.STATUS_STOPPED){this.status=c.STATUS_STOPPED;if(this.onStopEvent)this.onStopEvent();null!=this.execution_timer_id&&(-1!=this.execution_timer_id&&clearInterval(this.execution_timer_id),this.execution_timer_id=null);this.sendEventToAllNodes("onStop")}};c.prototype.runStep=function(a,b,d){a=a||1;var g=h.getTime();this.globaltime=0.001*(g-this.starttime);var f=this._nodes_executable?this._nodes_executable: this._nodes;if(f){d=d||f.length;if(b){for(var r=0;rs&&(s=f.size[0]),c+=f.size[1]+a+h.NODE_TITLE_HEIGHT;b+=s+a}this.setDirtyCanvas(!0,!0)};c.prototype.getTime=function(){return this.globaltime};c.prototype.getFixedTime=function(){return this.fixedtime};c.prototype.getElapsedTime=function(){return this.elapsed_time};c.prototype.sendEventToAllNodes=function(a,b,d){d=d||h.ALWAYS;var g=this._nodes_in_order? this._nodes_in_order:this._nodes;if(g)for(var f=0,r=g.length;fh.getTime()-this.last_mouseclick;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;this.canvas.focus(); h.closeAllContextMenus(b);if(!this.onMouse||!0!=this.onMouse(a)){if(1==a.which){a.ctrlKey&&(this.dragging_rectangle=new Float32Array(4),this.dragging_rectangle[0]=a.canvasX,this.dragging_rectangle[1]=a.canvasY,this.dragging_rectangle[2]=1,this.dragging_rectangle[3]=1,g=!0);var r=!1;if(d&&this.allow_interaction&&!g&&!this.read_only){this.live_mode||d.flags.pinned||this.bringToFront(d);if(!this.connecting_node&&!d.flags.collapsed&&!this.live_mode)if(!g&&!1!==d.resizable&&w(a.canvasX,a.canvasY,d.pos[0]+ -d.size[0]-5,d.pos[1]+d.size[1]-5,10,10))this.resizing_node=d,this.canvas.style.cursor="se-resize",g=!0;else{if(d.outputs)for(var s=0,c=d.outputs.length;sr[0]+4||a.canvasYr[1]+4)){this.showLinkMenu(d,a);this.over_link_center=null;break}this.selected_group=this.graph.getGroupOnPos(a.canvasX,a.canvasY);this.selected_group_resizing=!1;this.selected_group&&!this.read_only&&(a.ctrlKey&&(this.dragging_rectangle=null),10>C([a.canvasX, a.canvasY],[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])*this.ds.scale?this.selected_group_resizing=!0:this.selected_group.recomputeInsideNodes());f&&!this.read_only&&this.allow_searchbox&&this.showSearchBox(a);r=!0}!g&&r&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}else 2!=a.which&&3==a.which&&(this.read_only||this.processContextMenu(d,a));this.last_mouse[0]=a.localX;this.last_mouse[1]=a.localY;this.last_mouseclick=h.getTime(); @@ -158,65 +158,65 @@ this._bg_img.onload=function(){h.draw(!0,!0)}}d=null;null==this._pattern&&0this.ds.scale;if(this.live_mode){if(!a.flags.collapsed&&(b.shadowColor="transparent",a.onDrawForeground))a.onDrawForeground(b,this,this.canvas)}else{var r=this.editor_alpha;b.globalAlpha=r;this.render_shadows&&!f?(b.shadowColor= h.DEFAULT_SHADOW_COLOR,b.shadowOffsetX=2*this.ds.scale,b.shadowOffsetY=2*this.ds.scale,b.shadowBlur=3*this.ds.scale):b.shadowColor="transparent";if(!a.flags.collapsed||!a.onDrawCollapsed||!0!=a.onDrawCollapsed(b,this)){var s=a._shape||h.BOX_SHAPE;y.set(a.size);var c=a.horizontal;if(a.flags.collapsed){b.font=this.inner_text_font;var k=a.getTitle?a.getTitle():a.title;null!=k&&(a._collapsed_width=Math.min(a.size[0],b.measureText(k).width+2*h.NODE_TITLE_HEIGHT),y[0]=a._collapsed_width,y[1]=0)}a.clip_area&& -(b.save(),b.beginPath(),s==h.BOX_SHAPE?b.rect(0,0,y[0],y[1]):s==h.ROUND_SHAPE?b.roundRect(0,0,y[0],y[1],10):s==h.CIRCLE_SHAPE&&b.arc(0.5*y[0],0.5*y[1],0.5*y[0],0,2*Math.PI),b.clip());a.has_errors&&(g="red");this.drawNodeShape(a,b,y,d,g,a.is_selected,a.mouseOver);b.shadowColor="transparent";if(a.onDrawForeground)a.onDrawForeground(b,this,this.canvas);b.textAlign=c?"center":"left";b.font=this.inner_text_font;g=!f;s=this.connecting_output;b.lineWidth=1;var k=0,q=new Float32Array(2);if(!a.flags.collapsed){if(a.inputs)for(d= -0;dthis.ds.scale,k=a._shape||a.constructor.shape||h.ROUND_SHAPE,q=a.constructor.title_mode,p=!0;q==h.TRANSPARENT_TITLE?p=!1:q==h.AUTOHIDE_TITLE&&s&&(p=!0);v[0]=0;v[1]=p?-f:0;v[2]=d[0]+1;v[3]=p?d[1]+f:d[1];s=b.globalAlpha;b.beginPath();k==h.BOX_SHAPE|| -c?b.fillRect(v[0],v[1],v[2],v[3]):k==h.ROUND_SHAPE||k==h.CARD_SHAPE?b.roundRect(v[0],v[1],v[2],v[3],this.round_radius,k==h.CARD_SHAPE?0:this.round_radius):k==h.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0],0,2*Math.PI);b.fill();a.flags.collapsed||(b.shadowColor="transparent",b.fillStyle="rgba(0,0,0,0.2)",b.fillRect(0,-1,v[2],2));b.shadowColor="transparent";if(a.onDrawBackground)a.onDrawBackground(b,this,this.canvas);if(p||q==h.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,f,d,this.ds.scale, -g);else if(q!=h.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){p=a.constructor.title_color||g;a.flags.collapsed&&(b.shadowColor=h.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var t=e.gradients[p];t||(t=e.gradients[p]=b.createLinearGradient(0,0,400,0),t.addColorStop(0,p),t.addColorStop(1,"#000"));b.fillStyle=t}else b.fillStyle=p;b.beginPath();k==h.BOX_SHAPE||c?b.rect(0,-f,d[0]+1,f):k!=h.ROUND_SHAPE&&k!=h.CARD_SHAPE||b.roundRect(0,-f,d[0]+1,f,this.round_radius,a.flags.collapsed? +d[1]-5),a.fill(),a.shadowColor="transparent",a.textAlign="center",a.fillStyle="#CEC",a.fillText(f,d[0],d[1]-15-24*0.3))}};var v=new Float32Array(4);e.prototype.drawNodeShape=function(a,b,d,g,f,r,s){b.strokeStyle=g;b.fillStyle=f;f=h.NODE_TITLE_HEIGHT;var c=0.5>this.ds.scale,k=a._shape||a.constructor.shape||h.ROUND_SHAPE,p=a.constructor.title_mode,q=!0;p==h.TRANSPARENT_TITLE?q=!1:p==h.AUTOHIDE_TITLE&&s&&(q=!0);v[0]=0;v[1]=q?-f:0;v[2]=d[0]+1;v[3]=q?d[1]+f:d[1];s=b.globalAlpha;b.beginPath();k==h.BOX_SHAPE|| +c?b.fillRect(v[0],v[1],v[2],v[3]):k==h.ROUND_SHAPE||k==h.CARD_SHAPE?b.roundRect(v[0],v[1],v[2],v[3],this.round_radius,k==h.CARD_SHAPE?0:this.round_radius):k==h.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0],0,2*Math.PI);b.fill();a.flags.collapsed||(b.shadowColor="transparent",b.fillStyle="rgba(0,0,0,0.2)",b.fillRect(0,-1,v[2],2));b.shadowColor="transparent";if(a.onDrawBackground)a.onDrawBackground(b,this,this.canvas);if(q||p==h.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,f,d,this.ds.scale, +g);else if(p!=h.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){q=a.constructor.title_color||g;a.flags.collapsed&&(b.shadowColor=h.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var t=e.gradients[q];t||(t=e.gradients[q]=b.createLinearGradient(0,0,400,0),t.addColorStop(0,q),t.addColorStop(1,"#000"));b.fillStyle=t}else b.fillStyle=q;b.beginPath();k==h.BOX_SHAPE||c?b.rect(0,-f,d[0]+1,f):k!=h.ROUND_SHAPE&&k!=h.CARD_SHAPE||b.roundRect(0,-f,d[0]+1,f,this.round_radius,a.flags.collapsed? this.round_radius:0);b.fill();b.shadowColor="transparent"}if(a.onDrawTitleBox)a.onDrawTitleBox(b,f,d,this.ds.scale);else k==h.ROUND_SHAPE||k==h.CIRCLE_SHAPE||k==h.CARD_SHAPE?(c&&(b.fillStyle="black",b.beginPath(),b.arc(0.5*f,-0.5*f,6,0,2*Math.PI),b.fill()),b.fillStyle=a.boxcolor||h.NODE_DEFAULT_BOXCOLOR,c?b.fillRect(0.5*f-5,-0.5*f-5,10,10):(b.beginPath(),b.arc(0.5*f,-0.5*f,5,0,2*Math.PI),b.fill())):(c&&(b.fillStyle="black",b.fillRect(0.5*(f-10)-1,-0.5*(f+10)-1,12,12)),b.fillStyle=a.boxcolor||h.NODE_DEFAULT_BOXCOLOR, b.fillRect(0.5*(f-10),-0.5*(f+10),10,10));b.globalAlpha=s;if(a.onDrawTitleText)a.onDrawTitleText(b,f,d,this.ds.scale,this.title_text_font,r);!c&&(b.font=this.title_text_font,c=String(a.getTitle()))&&(b.fillStyle=r?"white":a.constructor.title_text_color||this.node_title_color,a.flags.collapsed?(b.textAlign="left",b.measureText(c),b.fillText(c.substr(0,20),f,h.NODE_TITLE_TEXT_Y-f),b.textAlign="left"):(b.textAlign="left",b.fillText(c,f,h.NODE_TITLE_TEXT_Y-f)));if(a.onDrawTitle)a.onDrawTitle(b)}if(r){if(a.onBounding)a.onBounding(v); -q==h.TRANSPARENT_TITLE&&(v[1]-=f,v[3]+=f);b.lineWidth=1;b.globalAlpha=0.8;b.beginPath();k==h.BOX_SHAPE?b.rect(-6+v[0],-6+v[1],12+v[2],12+v[3]):k==h.ROUND_SHAPE||k==h.CARD_SHAPE&&a.flags.collapsed?b.roundRect(-6+v[0],-6+v[1],12+v[2],12+v[3],2*this.round_radius):k==h.CARD_SHAPE?b.roundRect(-6+v[0],-6+v[1],12+v[2],12+v[3],2*this.round_radius,2):k==h.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0]+6,0,2*Math.PI);b.strokeStyle="#FFF";b.stroke();b.strokeStyle=g;b.globalAlpha=1}};var G=new Float32Array(4), -q=new Float32Array(4),p=new Float32Array(2),k=new Float32Array(2);e.prototype.drawConnections=function(a){var b=h.getTime(),d=this.visible_area;G[0]=d[0]-20;G[1]=d[1]-20;G[2]=d[2]+40;G[3]=d[3]+40;a.lineWidth=this.connections_width;a.fillStyle="#AAA";a.strokeStyle="#AAA";a.globalAlpha=this.editor_alpha;for(var d=this.graph._nodes,g=0,f=d.length;gq[2]&&(q[0]+=q[2],q[2]=Math.abs(q[2]));0>q[3]&&(q[1]+=q[3],q[3]=Math.abs(q[3]));if(u(q,G)){var n=e.outputs[z],z=r.inputs[s];if(n&&z&&(e=n.dir||(e.horizontal?h.DOWN:h.RIGHT),z=z.dir||(r.horizontal?h.UP:h.LEFT),this.renderLink(a,l,t,c,!1,0,null,e,z),c&&c._last_time&&1E3>b-c._last_time)){var n= -2-0.002*(b-c._last_time),K=a.globalAlpha;a.globalAlpha=K*n;this.renderLink(a,l,t,c,!0,n,"white",e,z);a.globalAlpha=K}}}}}}a.globalAlpha=1};e.prototype.renderLink=function(a,b,d,g,f,r,c,k,q,p){g&&this.visible_links.push(g);!c&&g&&(c=g.color||e.link_type_colors[g.type]);c||(c=this.default_link_color);null!=g&&this.highlighted_links[g.id]&&(c="#FFF");k=k||h.RIGHT;q=q||h.LEFT;var z=C(b,d);this.render_connections_border&&0.6b[1]?0:Math.PI,a.save(),a.translate(t[0],t[1]),a.rotate(l),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore(),a.save(),a.translate(p[0],p[1]),a.rotate(n),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill());if(r)for(a.fillStyle= -c,t=0;5>t;++t)r=(0.001*h.getTime()+0.2*t)%1,f=this.computeConnectionPoint(b,d,r,k,q),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill()};e.prototype.computeConnectionPoint=function(a,b,d,g,f){g=g||h.RIGHT;f=f||h.LEFT;var r=C(a,b),c=[a[0],a[1]],k=[b[0],b[1]];switch(g){case h.LEFT:c[0]+=-0.25*r;break;case h.RIGHT:c[0]+=0.25*r;break;case h.UP:c[1]+=-0.25*r;break;case h.DOWN:c[1]+=0.25*r}switch(f){case h.LEFT:k[0]+=-0.25*r;break;case h.RIGHT:k[0]+=0.25*r;break;case h.UP:k[1]+=-0.25*r;break;case h.DOWN:k[1]+= +p==h.TRANSPARENT_TITLE&&(v[1]-=f,v[3]+=f);b.lineWidth=1;b.globalAlpha=0.8;b.beginPath();k==h.BOX_SHAPE?b.rect(-6+v[0],-6+v[1],12+v[2],12+v[3]):k==h.ROUND_SHAPE||k==h.CARD_SHAPE&&a.flags.collapsed?b.roundRect(-6+v[0],-6+v[1],12+v[2],12+v[3],2*this.round_radius):k==h.CARD_SHAPE?b.roundRect(-6+v[0],-6+v[1],12+v[2],12+v[3],2*this.round_radius,2):k==h.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0]+6,0,2*Math.PI);b.strokeStyle="#FFF";b.stroke();b.strokeStyle=g;b.globalAlpha=1}};var G=new Float32Array(4), +p=new Float32Array(4),q=new Float32Array(2),k=new Float32Array(2);e.prototype.drawConnections=function(a){var b=h.getTime(),d=this.visible_area;G[0]=d[0]-20;G[1]=d[1]-20;G[2]=d[2]+40;G[3]=d[3]+40;a.lineWidth=this.connections_width;a.fillStyle="#AAA";a.strokeStyle="#AAA";a.globalAlpha=this.editor_alpha;for(var d=this.graph._nodes,g=0,f=d.length;gp[2]&&(p[0]+=p[2],p[2]=Math.abs(p[2]));0>p[3]&&(p[1]+=p[3],p[3]=Math.abs(p[3]));if(u(p,G)){var n=e.outputs[z],z=r.inputs[s];if(n&&z&&(e=n.dir||(e.horizontal?h.DOWN:h.RIGHT),z=z.dir||(r.horizontal?h.UP:h.LEFT),this.renderLink(a,l,t,c,!1,0,null,e,z),c&&c._last_time&&1E3>b-c._last_time)){var n= +2-0.002*(b-c._last_time),K=a.globalAlpha;a.globalAlpha=K*n;this.renderLink(a,l,t,c,!0,n,"white",e,z);a.globalAlpha=K}}}}}}a.globalAlpha=1};e.prototype.renderLink=function(a,b,d,g,f,r,c,k,p,q){g&&this.visible_links.push(g);!c&&g&&(c=g.color||e.link_type_colors[g.type]);c||(c=this.default_link_color);null!=g&&this.highlighted_links[g.id]&&(c="#FFF");k=k||h.RIGHT;p=p||h.LEFT;var z=C(b,d);this.render_connections_border&&0.6b[1]?0:Math.PI,a.save(),a.translate(t[0],t[1]),a.rotate(l),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore(),a.save(),a.translate(q[0],q[1]),a.rotate(n),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill());if(r)for(a.fillStyle= +c,t=0;5>t;++t)r=(0.001*h.getTime()+0.2*t)%1,f=this.computeConnectionPoint(b,d,r,k,p),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill()};e.prototype.computeConnectionPoint=function(a,b,d,g,f){g=g||h.RIGHT;f=f||h.LEFT;var r=C(a,b),c=[a[0],a[1]],k=[b[0],b[1]];switch(g){case h.LEFT:c[0]+=-0.25*r;break;case h.RIGHT:c[0]+=0.25*r;break;case h.UP:c[1]+=-0.25*r;break;case h.DOWN:c[1]+=0.25*r}switch(f){case h.LEFT:k[0]+=-0.25*r;break;case h.RIGHT:k[0]+=0.25*r;break;case h.UP:k[1]+=-0.25*r;break;case h.DOWN:k[1]+= 0.25*r}g=(1-d)*(1-d)*(1-d);f=3*(1-d)*(1-d)*d;r=3*(1-d)*d*d;d*=d*d;return[g*a[0]+f*c[0]+r*k[0]+d*b[0],g*a[1]+f*c[1]+r*k[1]+d*b[1]]};e.prototype.drawExecutionOrder=function(a){a.shadowColor="transparent";a.globalAlpha=0.25;a.textAlign="center";a.strokeStyle="white";a.globalAlpha=0.75;for(var b=this.visible_nodes,d=0;dt.last_y&&st.options.max&&(t.value=t.options.max);else if("mousedown"==d.type){var l=t.options.values; -l&&l.constructor===Function&&(l=t.options.values(t,a));var z=null;"number"!=t.type&&(z=l.constructor===Array?l:Object.keys(l));c=40>c?-1:c>k-40?1:0;if("number"==t.type)t.value+=0.1*c*(t.options.step||1),null!=t.options.min&&t.valuet.options.max&&(t.value=t.options.max);else if(c)q=-1,q=l.constructor===Object?z.indexOf(String(t.value))+c:z.indexOf(t.value)+c,q>=z.length&&(q=z.length-1),0>q&&(q=0),t.value=l.constructor===Array?l[q]: -q;else{var n=l!=z?Object.values(l):l;new h.ContextMenu(n,{scale:Math.max(1,this.ds.scale),event:d,className:"dark",callback:y.bind(t)},q);var y=function(a,b,d){l!=z&&(a=n.indexOf(a));this.value=a;f(this,a);e.dirty_canvas=!0;return!1}}}else"mouseup"==d.type&&"number"==t.type&&(c=40>c?-1:c>k-40?1:0,200>d.click_time&&0==c&&this.prompt("Value",t.value,function(a){this.value=Number(a);f(this,this.value)}.bind(t),d));g!=t.value&&setTimeout(function(){f(this,this.value)}.bind(t),20);this.dirty_canvas=!0; +h.NODE_TITLE_HEIGHT+0.5,g.pos[1]-h.NODE_TITLE_HEIGHT+0.5,h.NODE_TITLE_HEIGHT,h.NODE_TITLE_HEIGHT);a.fillStyle="#FFF";a.fillText(g.order,g.pos[0]+-0.5*h.NODE_TITLE_HEIGHT,g.pos[1]-6)}a.globalAlpha=1};e.prototype.drawNodeWidgets=function(a,b,d,g){if(!a.widgets||!a.widgets.length)return 0;var f=a.size[0],r=a.widgets;b+=2;var c=h.NODE_WIDGET_HEIGHT,k=0.5t.last_y&&st.options.max&&(t.value=t.options.max);else if("mousedown"==d.type){var l=t.options.values; +l&&l.constructor===Function&&(l=t.options.values(t,a));var z=null;"number"!=t.type&&(z=l.constructor===Array?l:Object.keys(l));c=40>c?-1:c>k-40?1:0;if("number"==t.type)t.value+=0.1*c*(t.options.step||1),null!=t.options.min&&t.valuet.options.max&&(t.value=t.options.max);else if(c)e=-1,e=l.constructor===Object?z.indexOf(String(t.value))+c:z.indexOf(t.value)+c,e>=z.length&&(e=z.length-1),0>e&&(e=0),t.value=l.constructor===Array?l[e]: +e;else{var n=l!=z?Object.values(l):l;new h.ContextMenu(n,{scale:Math.max(1,this.ds.scale),event:d,className:"dark",callback:y.bind(t)},e);var y=function(a,b,d){l!=z&&(a=n.indexOf(a));this.value=a;f(this,a);p.dirty_canvas=!0;return!1}}}else"mouseup"==d.type&&"number"==t.type&&(c=40>c?-1:c>k-40?1:0,200>d.click_time&&0==c&&this.prompt("Value",t.value,function(a){this.value=Number(a);f(this,this.value)}.bind(t),d));g!=t.value&&setTimeout(function(){f(this,this.value)}.bind(t),20);this.dirty_canvas=!0; break;case "toggle":"mousedown"==d.type&&(t.value=!t.value,setTimeout(function(){f(t,t.value)},20));break;case "string":case "text":"mousedown"==d.type&&this.prompt("Value",t.value,function(a){this.value=a;f(this,a)}.bind(t),d);break;default:t.mouse&&t.mouse(ctx,d,[c,s],a)}return t}}return null};e.prototype.drawGroups=function(a,b){if(this.graph){var d=this.graph._groups;b.save();b.globalAlpha=0.5*this.editor_alpha;for(var g=0;gd&&0.01>b.editor_alpha&&(clearInterval(g),1>d&&(b.live_mode=!0));1"+p+""+a+"",value:p});if(q.length)return new h.ContextMenu(q, +s.graph.add(r));f&&f(r)}var s=e.active_canvas,k=s.getCanvasWindow();a=h.getNodeTypesCategories(s.filter);b=[];for(var p in a)a[p]&&b.push({value:a[p],content:a[p],has_submenu:!0});var q=new h.ContextMenu(b,{event:d,callback:function(a,b,d){a=h.getNodeTypesInCategory(a.value,s.filter);b=[];for(var f in a)a[f].skip_list||b.push({content:a[f].title,value:a[f].type});new h.ContextMenu(b,{event:d,callback:c,parentMenu:q},k);return!1},parentMenu:g},k);return!1};e.onMenuCollapseAll=function(){};e.onMenuNodeEdit= +function(){};e.showMenuNodeOptionalInputs=function(a,b,d,g,f){function c(a,b,d){f&&(a.callback&&a.callback.call(s,f,a,b,d),a.value&&(f.addInput(a.value[0],a.value[1],a.value[2]),f.setDirtyCanvas(!0,!0)))}if(f){var s=this;a=e.active_canvas.getCanvasWindow();b=f.optional_inputs;f.onGetInputs&&(b=f.onGetInputs());var k=[];if(b)for(var p in b){var q=b[p];if(q){var l=q[0];q[2]&&q[2].label&&(l=q[2].label);l={content:l,value:q};q[1]==h.ACTION&&(l.className="event");k.push(l)}else k.push(null)}this.onMenuNodeInputs&& +(k=this.onMenuNodeInputs(k));if(k.length)return new h.ContextMenu(k,{event:d,callback:c,parentMenu:g,node:f},a),!1}};e.showMenuNodeOptionalOutputs=function(a,b,d,g,f){function c(a,b,d){if(f&&(a.callback&&a.callback.call(k,f,a,b,d),a.value))if(d=a.value[1],!d||d.constructor!==Object&&d.constructor!==Array)f.addOutput(a.value[0],a.value[1],a.value[2]),f.setDirtyCanvas(!0,!0);else{a=[];for(var p in d)a.push({content:p,value:d[p]});new h.ContextMenu(a,{event:b,callback:c,parentMenu:g,node:f});return!1}} +if(f){var k=this;a=e.active_canvas.getCanvasWindow();b=f.optional_outputs;f.onGetOutputs&&(b=f.onGetOutputs());var p=[];if(b)for(var q in b){var l=b[q];if(!l)p.push(null);else if(!f.flags||!f.flags.skip_repeated_outputs||-1==f.findOutputSlot(l[0])){var z=l[0];l[2]&&l[2].label&&(z=l[2].label);z={content:z,value:l};l[1]==h.EVENT&&(z.className="event");p.push(z)}}this.onMenuNodeOutputs&&(p=this.onMenuNodeOutputs(p));if(p.length)return new h.ContextMenu(p,{event:d,callback:c,parentMenu:g,node:f},a),!1}}; +e.onShowMenuNodeProperties=function(a,b,d,g,f){function c(a,b,d,g){f&&(b=this.getBoundingClientRect(),k.showEditPropertyValue(f,a.value,{position:[b.left,b.top]}))}if(f&&f.properties){var k=e.active_canvas;b=k.getCanvasWindow();var p=[],q;for(q in f.properties)a=void 0!==f.properties[q]?f.properties[q]:" ","object"==typeof a&&(a=JSON.stringify(a)),a=e.decodeHTML(a),p.push({content:""+q+""+a+"",value:q});if(p.length)return new h.ContextMenu(p, {event:d,callback:c,parentMenu:g,allow_html:!0,node:f},b),!1}};e.decodeHTML=function(a){var b=document.createElement("div");b.innerText=a;return b.innerHTML};e.onResizeNode=function(a,b,d,g,f){f&&(f.size=f.computeSize(),f.setDirtyCanvas(!0,!0))};e.prototype.showLinkMenu=function(a,b){var d=this;console.log(a);var g=new h.ContextMenu(["Add Node",null,"Delete"],{event:b,title:null!=a.data?a.data.constructor.name:null,callback:function(b,h,c){switch(b){case "Add Node":e.onMenuAdd(null,null,c,g,function(b){console.log("node autoconnect"); -var f=d.graph.getNodeById(a.origin_id),g=d.graph.getNodeById(a.target_id);b.inputs&&b.inputs.length&&b.outputs&&b.outputs.length&&f.outputs[a.origin_slot].type==b.inputs[0].type&&b.outputs[0].type==g.inputs[0].type&&(f.connect(a.origin_slot,b,0),b.connect(0,g,a.target_slot),b.pos[0]-=0.5*b.size[0])});break;case "Delete":d.graph.removeLink(a.id)}}});return!1};e.onShowPropertyEditor=function(a,b,d,g,f){function h(){var b=q.value;"Number"==a.type?b=Number(b):"Boolean"==a.type&&(b=Boolean(b));f[c]=b; -k.parentNode&&k.parentNode.removeChild(k);f.setDirtyCanvas(!0,!0)}var c=a.property||"title";b=f[c];var k=document.createElement("div");k.className="graphdialog";k.innerHTML="";k.querySelector(".name").innerText=c;var q=k.querySelector("input");q&&(q.value=b,q.addEventListener("blur",function(a){this.focus()}),q.addEventListener("keydown",function(a){13==a.keyCode&&(h(),a.preventDefault(),a.stopPropagation())})); -b=e.active_canvas.canvas;d=b.getBoundingClientRect();var p=g=-20;d&&(g-=d.left,p-=d.top);event?(k.style.left=event.clientX+g+"px",k.style.top=event.clientY+p+"px"):(k.style.left=0.5*b.width+g+"px",k.style.top=0.5*b.height+p+"px");k.querySelector("button").addEventListener("click",h);b.parentNode.appendChild(k)};e.prototype.prompt=function(a,b,d,g){var f=this;a=a||"";var h=!1,c=document.createElement("div");c.className="graphdialog rounded";c.innerHTML=" "; +var f=d.graph.getNodeById(a.origin_id),g=d.graph.getNodeById(a.target_id);b.inputs&&b.inputs.length&&b.outputs&&b.outputs.length&&f.outputs[a.origin_slot].type==b.inputs[0].type&&b.outputs[0].type==g.inputs[0].type&&(f.connect(a.origin_slot,b,0),b.connect(0,g,a.target_slot),b.pos[0]-=0.5*b.size[0])});break;case "Delete":d.graph.removeLink(a.id)}}});return!1};e.onShowPropertyEditor=function(a,b,d,g,f){function h(){var b=p.value;"Number"==a.type?b=Number(b):"Boolean"==a.type&&(b=Boolean(b));f[c]=b; +k.parentNode&&k.parentNode.removeChild(k);f.setDirtyCanvas(!0,!0)}var c=a.property||"title";b=f[c];var k=document.createElement("div");k.className="graphdialog";k.innerHTML="";k.querySelector(".name").innerText=c;var p=k.querySelector("input");p&&(p.value=b,p.addEventListener("blur",function(a){this.focus()}),p.addEventListener("keydown",function(a){13==a.keyCode&&(h(),a.preventDefault(),a.stopPropagation())})); +b=e.active_canvas.canvas;d=b.getBoundingClientRect();var q=g=-20;d&&(g-=d.left,q-=d.top);event?(k.style.left=event.clientX+g+"px",k.style.top=event.clientY+q+"px"):(k.style.left=0.5*b.width+g+"px",k.style.top=0.5*b.height+q+"px");k.querySelector("button").addEventListener("click",h);b.parentNode.appendChild(k)};e.prototype.prompt=function(a,b,d,g){var f=this;a=a||"";var h=!1,c=document.createElement("div");c.className="graphdialog rounded";c.innerHTML=" "; c.close=function(){f.prompt_box=null;c.parentNode&&c.parentNode.removeChild(c)};1e.search_limit)break}}q=null;if(Array.prototype.filter)q=Object.keys(h.registered_node_types).filter(l); -else for(k in q=[],h.registered_node_types)l(k)&&q.push(k);for(k=0;ke.search_limit);k++);var l=function(a){var b=h.registered_node_types[a];return s&&b.filter!=s?!1:-1!==a.toLowerCase().indexOf(d)}}}var f=this,c=e.active_canvas,k=c.canvas,q=k.ownerDocument||document,p=document.createElement("div");p.className="litegraph litesearchbox graphdialog rounded";p.innerHTML="Search
"; -p.close=function(){f.search_box=null;q.body.focus();q.body.style.overflow="";setTimeout(function(){f.canvas.focus()},20);p.parentNode&&p.parentNode.removeChild(p)};var l=null;1k.height-200&&(z.style.maxHeight=k.height-a.layerY-20+"px");u.focus();return p};e.prototype.showEditPropertyValue=function(a,b,d){function g(){f(t.value)}function f(f){"number"==typeof a.properties[b]&&(f=Number(f));if("array"==h||"object"==h)f=JSON.parse(f);a.properties[b]=f;a._graph&&a._graph._version++;if(a.onPropertyChanged)a.onPropertyChanged(b, -f);if(d.onclose)d.onclose();p.close();a.setDirtyCanvas(!0,!0)}if(a&&void 0!==a.properties[b]){d=d||{};var c=a.getPropertyInfo(b),h=c.type,k="";if("string"==h||"number"==h||"array"==h||"object"==h)k="";else if("enum"==h&&c.values){var k=""}else if("boolean"== -h)k="";else{console.warn("unknown type: "+h);return}var p=this.createDialog(""+b+""+k+"",d);if("enum"==h&&c.values){var t=p.querySelector("select");t.addEventListener("change",function(a){f(a.target.value)})}else if("boolean"==h)(t=p.querySelector("input"))&&t.addEventListener("click",function(a){f(!!t.checked)});else if(t=p.querySelector("input"))t.addEventListener("blur", -function(a){this.focus()}),e=void 0!==a.properties[b]?a.properties[b]:"",e=JSON.stringify(e),t.value=e,t.addEventListener("keydown",function(a){13==a.keyCode&&(g(),a.preventDefault(),a.stopPropagation())});p.querySelector("button").addEventListener("click",g);return p}};e.prototype.createDialog=function(a,b){b=b||{};var d=document.createElement("div");d.className="graphdialog";d.innerHTML=a;var g=this.canvas.getBoundingClientRect(),f=-20,c=-20;g&&(f-=g.left,c-=g.top);b.position?(f+=b.position[0], +b.configure(d.data.json)}}q.close()}function d(a){var b=y;y&&y.classList.remove("selected");y?(y=a?y.nextSibling:y.previousSibling)||(y=b):y=a?z.childNodes[0]:z.childNodes[z.childNodes.length];y&&(y.classList.add("selected"),y.scrollIntoView({block:"end",behavior:"smooth"}))}function g(){function a(d,f){var g=document.createElement("div");t||(t=d);g.innerText=d;g.dataset.type=escape(d);g.className="litegraph lite-search-item";f&&(g.className+=" "+f);g.addEventListener("click",function(a){b(unescape(this.dataset.type))}); +z.appendChild(g)}n=null;var d=u.value;t=null;z.innerHTML="";if(d)if(f.onSearchBox){var g=f.onSearchBox(z,d,c);if(g)for(var k=0;ke.search_limit)break}}s=null;if(Array.prototype.filter)s=Object.keys(h.registered_node_types).filter(l); +else for(k in s=[],h.registered_node_types)l(k)&&s.push(k);for(k=0;ke.search_limit);k++);var l=function(a){var b=h.registered_node_types[a];return p&&b.filter!=p?!1:-1!==a.toLowerCase().indexOf(d)}}}var f=this,c=e.active_canvas,k=c.canvas,p=k.ownerDocument||document,q=document.createElement("div");q.className="litegraph litesearchbox graphdialog rounded";q.innerHTML="Search
"; +q.close=function(){f.search_box=null;p.body.focus();p.body.style.overflow="";setTimeout(function(){f.canvas.focus()},20);q.parentNode&&q.parentNode.removeChild(q)};var l=null;1k.height-200&&(z.style.maxHeight=k.height-a.layerY-20+"px");u.focus();return q};e.prototype.showEditPropertyValue=function(a,b,d){function g(){f(t.value)}function f(f){"number"==typeof a.properties[b]&&(f=Number(f));if("array"==h||"object"==h)f=JSON.parse(f);a.properties[b]=f;a._graph&&a._graph._version++;if(a.onPropertyChanged)a.onPropertyChanged(b, +f);if(d.onclose)d.onclose();e.close();a.setDirtyCanvas(!0,!0)}if(a&&void 0!==a.properties[b]){d=d||{};var c=a.getPropertyInfo(b),h=c.type,k="";if("string"==h||"number"==h||"array"==h||"object"==h)k="";else if("enum"==h&&c.values){var k=""}else if("boolean"== +h)k="";else{console.warn("unknown type: "+h);return}var e=this.createDialog(""+b+""+k+"",d);if("enum"==h&&c.values){var t=e.querySelector("select");t.addEventListener("change",function(a){f(a.target.value)})}else if("boolean"==h)(t=e.querySelector("input"))&&t.addEventListener("click",function(a){f(!!t.checked)});else if(t=e.querySelector("input"))t.addEventListener("blur", +function(a){this.focus()}),q=void 0!==a.properties[b]?a.properties[b]:"",q=JSON.stringify(q),t.value=q,t.addEventListener("keydown",function(a){13==a.keyCode&&(g(),a.preventDefault(),a.stopPropagation())});e.querySelector("button").addEventListener("click",g);return e}};e.prototype.createDialog=function(a,b){b=b||{};var d=document.createElement("div");d.className="graphdialog";d.innerHTML=a;var g=this.canvas.getBoundingClientRect(),f=-20,c=-20;g&&(f-=g.left,c-=g.top);b.position?(f+=b.position[0], c+=b.position[1]):b.event?(f+=b.event.clientX,c+=b.event.clientY):(f+=0.5*this.canvas.width,c+=0.5*this.canvas.height);d.style.left=f+"px";d.style.top=c+"px";this.canvas.parentNode.appendChild(d);d.close=function(){this.parentNode&&this.parentNode.removeChild(this)};return d};e.onMenuNodeCollapse=function(a,b,d,g,f){f.collapse()};e.onMenuNodePin=function(a,b,d,g,f){f.pin()};e.onMenuNodeMode=function(a,b,d,g,f){new h.ContextMenu(["Always","On Event","On Trigger","Never"],{event:d,callback:function(a){if(f)switch(a){case "On Event":f.mode= h.ON_EVENT;break;case "On Trigger":f.mode=h.ON_TRIGGER;break;case "Never":f.mode=h.NEVER;break;default:f.mode=h.ALWAYS}},parentMenu:g,node:f});return!1};e.onMenuNodeColors=function(a,b,d,g,f){if(!f)throw"no node for color";b=[];b.push({value:null,content:"No color"});for(var c in e.node_colors)a=e.node_colors[c],a={value:c,content:""+c+""},b.push(a);new h.ContextMenu(b,{event:d,callback:function(a){f&&((a=a.value?e.node_colors[a.value]:null)?f.constructor===h.LGraphGroup?f.color=a.groupcolor:(f.color=a.color,f.bgcolor=a.bgcolor):(delete f.color,delete f.bgcolor),f.setDirtyCanvas(!0,!0))},parentMenu:g,node:f});return!1};e.onMenuNodeShapes=function(a,b,d,g,f){if(!f)throw"no node passed";new h.ContextMenu(h.VALID_SHAPES,{event:d,callback:function(a){f&&(f.shape=a,f.setDirtyCanvas(!0))},parentMenu:g,node:f}); @@ -227,8 +227,8 @@ callback:e.onShowMenuNodeProperties},null,{content:"Title",callback:e.onShowProp (d=a.onGetOutputs())&&d.length&&(b[1].disabled=!1);a.getExtraMenuOptions&&(d=a.getExtraMenuOptions(this))&&(d.push(null),b=d.concat(b));!1!==a.clonable&&b.push({content:"Clone",callback:e.onMenuNodeClone});!1!==a.removable&&b.push(null,{content:"Remove",callback:e.onMenuNodeRemove});if(a.graph&&a.graph.onGetNodeMenuOptions)a.graph.onGetNodeMenuOptions(b,a);return b};e.prototype.getGroupMenuOptions=function(a){return[{content:"Title",callback:e.onShowPropertyEditor},{content:"Color",has_submenu:!0, callback:e.onMenuNodeColors},{content:"Font size",property:"font_size",type:"Number",callback:e.onShowPropertyEditor},null,{content:"Remove",callback:e.onMenuNodeRemove}]};e.prototype.processContextMenu=function(a,b){var d=this,g=e.active_canvas.getCanvasWindow(),f=null,c={event:b,callback:function(b,f,g){if(b)if("Remove Slot"==b.content)b=b.slot,b.input?a.removeInput(b.slot):b.output&&a.removeOutput(b.slot);else if("Disconnect Links"==b.content)b=b.slot,b.output?a.disconnectOutput(b.slot):b.input&& a.disconnectInput(b.slot);else if("Rename Slot"==b.content){b=b.slot;var c=b.input?a.getInputInfo(b.slot):a.getOutputInfo(b.slot),h=d.createDialog("Name",f),k=h.querySelector("input");k&&c&&(k.value=c.label||"");h.querySelector("button").addEventListener("click",function(a){k.value&&(c&&(c.label=k.value),d.setDirty(!0));h.close()})}},extra:a};a&&(c.title=a.type);var k=null;a&&(k=a.getSlotInPosition(b.canvasX,b.canvasY),e.active_node= -a);if(k){f=[];k&&k.output&&k.output.links&&k.output.links.length&&f.push({content:"Disconnect Links",slot:k});var q=k.input||k.output;f.push(q.locked?"Cannot remove":{content:"Remove Slot",slot:k});f.push(q.nameLocked?"Cannot rename":{content:"Rename Slot",slot:k});c.title=(k.input?k.input.type:k.output.type)||"*";k.input&&k.input.type==h.ACTION&&(c.title="Action");k.output&&k.output.type==h.EVENT&&(c.title="Event")}else a?f=this.getNodeMenuOptions(a):(f=this.getCanvasMenuOptions(),(k=this.graph.getGroupOnPos(b.canvasX, -b.canvasY))&&f.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:k,options:this.getGroupMenuOptions(k)}}));f&&new h.ContextMenu(f,c,g)};this.CanvasRenderingContext2D&&(CanvasRenderingContext2D.prototype.roundRect=function(a,b,d,g,f,c){void 0===f&&(f=5);void 0===c&&(c=f);this.moveTo(a+f,b);this.lineTo(a+d-f,b);this.quadraticCurveTo(a+d,b,a+d,b+f);this.lineTo(a+d,b+g-c);this.quadraticCurveTo(a+d,b+g,a+d-c,b+g);this.lineTo(a+c,b+g);this.quadraticCurveTo(a,b+g,a,b+g-c);this.lineTo(a, +a);if(k){f=[];k&&k.output&&k.output.links&&k.output.links.length&&f.push({content:"Disconnect Links",slot:k});var p=k.input||k.output;f.push(p.locked?"Cannot remove":{content:"Remove Slot",slot:k});f.push(p.nameLocked?"Cannot rename":{content:"Rename Slot",slot:k});c.title=(k.input?k.input.type:k.output.type)||"*";k.input&&k.input.type==h.ACTION&&(c.title="Action");k.output&&k.output.type==h.EVENT&&(c.title="Event")}else a?f=this.getNodeMenuOptions(a):(f=this.getCanvasMenuOptions(),(k=this.graph.getGroupOnPos(b.canvasX, +b.canvasY))&&f.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:k,options:this.getGroupMenuOptions(k)}}));f&&new h.ContextMenu(f,c,g)};B.CanvasRenderingContext2D&&(B.CanvasRenderingContext2D.prototype.roundRect=function(a,b,d,g,f,c){void 0===f&&(f=5);void 0===c&&(c=f);this.moveTo(a+f,b);this.lineTo(a+d-f,b);this.quadraticCurveTo(a+d,b,a+d,b+f);this.lineTo(a+d,b+g-c);this.quadraticCurveTo(a+d,b+g,a+d-c,b+g);this.lineTo(a+c,b+g);this.quadraticCurveTo(a,b+g,a,b+g-c);this.lineTo(a, b+f);this.quadraticCurveTo(a,b,a+f,b)});h.compareObjects=function(a,b){for(var d in a)if(a[d]!=b[d])return!1;return!0};h.distance=C;h.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+","+Math.round(255*a[2]).toFixed()+","+(4==a.length?a[3].toFixed(2):"1.0")+")"};h.isInsideRectangle=w;h.growBounding=function(a,b,d){ba[2]&&(a[2]=b);da[3]&&(a[3]=d)};h.isInsideBounding=function(a,b){return a[0]b[1][0]||a[1]>b[1][1]?!1:!0};h.overlapBounding=u;h.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),d=0,g,f,c=0;6>c;c+=2)g="0123456789ABCDEF".indexOf(a.charAt(c)),f="0123456789ABCDEF".indexOf(a.charAt(c+1)),b[d]=16*g+f,d++;return b};h.num2hex=function(a){for(var b="#",d,g,f=0;3>f;f++)d=a[f]/16,g=a[f]%16,b+="0123456789ABCDEF".charAt(d)+"0123456789ABCDEF".charAt(g);return b};D.prototype.addItem=function(a,b,d){function g(a){var b=this.value;b&&b.has_submenu&& f.call(this,a)}function f(a){var b=this.value,f=!0;c.current_submenu&&c.current_submenu.close(a);if(d.callback){var g=d.callback.call(this,b,d,a,c,d.node);!0===g&&(f=!1)}if(b&&(b.callback&&!d.ignore_item_callbacks&&!0!==b.disabled&&(g=b.callback.call(this,b,d,a,c,d.extra),!0===g&&(f=!1)),b.submenu)){if(!b.submenu.options)throw"ContextMenu submenu needs options";new c.constructor(b.submenu.options,{callback:b.submenu.callback,event:a,parentMenu:c,ignore_item_callbacks:b.submenu.ignore_item_callbacks, @@ -240,7 +240,7 @@ b){var d=a.clientX,g=a.clientY,f=b.getBoundingClientRect();return f?g>f.top&&ga[1])){var g=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=a[0]-this.margin,h=a[1]-this.margin;this.selected=this.getCloserPoint([c,h],30/b.ds.scale);-1==this.selected&&(g=[c/g,1-h/f],d.push(g),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(g),this.must_update=!0);if(-1!=this.selected)return!0}};A.prototype.onMouseMove=function(a,b){var d=this.points;if(d){var g= this.selected;if(!(0>g)){var f=(a[0]-this.margin)/(this.size[0]-2*this.margin),c=(a[1]-this.margin)/(this.size[1]-2*this.margin);this._nearest=this.getCloserPoint([a[0]-this.margin,a[1]-this.margin],30/b.ds.scale);var h=d[g];if(h){var k=0==g||g==d.length-1;!k&&(-10>a[0]||a[0]>this.size[0]+10||-10>a[1]||a[1]>this.size[1]+10)?(d.splice(g,1),this.selected=-1):(h[0]=k?0==g?0:1:Math.clamp(f,0,1),h[1]=1-Math.clamp(c,0,1),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(h),this.must_update= -!0)}}}};A.prototype.onMouseUp=function(a,b){this.selected=-1;return!1};A.prototype.getCloserPoint=function(a,b){var d=this.points;if(!d)return-1;b=b||30;for(var g=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=d.length,h=[0,0],k=1E6,q=-1,e=0;ek||p>b||(q=e,k=p)}return q};h.CurveEditor=A;h.getParameterNames=function(a){return(a+"").replace(/[/][/].*$/gm,"").replace(/\s+/g,"").replace(/[/][*][^/*]*[*][/]/g,"").split("){", +!0)}}}};A.prototype.onMouseUp=function(a,b){this.selected=-1;return!1};A.prototype.getCloserPoint=function(a,b){var d=this.points;if(!d)return-1;b=b||30;for(var g=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=d.length,h=[0,0],k=1E6,p=-1,q=0;qk||e>b||(p=q,k=e)}return p};h.CurveEditor=A;h.getParameterNames=function(a){return(a+"").replace(/[/][/].*$/gm,"").replace(/\s+/g,"").replace(/[/][*][^/*]*[*][/]/g,"").split("){", 1)[0].replace(/^[^(]*[(]/,"").replace(/=[^,]+/g,"").split(",").filter(Boolean)};Math.clamp=function(a,b,d){return b>a?b:dthis.size[0]-g.NODE_TITLE_HEIGHT&&0>b[1]){var c=this;setTimeout(function(){d.openSubgraph(c.subgraph)},10)}};m.prototype.onAction=function(a,b){this.subgraph.onAction(a,b)};m.prototype.onExecute=function(){if(this.enabled= @@ -272,10 +272,10 @@ u.prototype.onPropertyChanged=function(a,b){this.widget.value=b;if(null!=b&&""!= a[Math.floor(Number(b))])};g.registerNodeType("basic/array[]",A);h.title="Table[row][col]";h.desc="Returns an element from a table";h.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1),d=this.getInputData(2);null==b&&(b=this.properties.row);null==d&&(d=this.properties.column);null!=a&&null!=b&&null!=d&&((b=a[Math.floor(Number(b))])?this.setOutputData(0,b[Math.floor(Number(d))]):this.setOutputData(0,null))};g.registerNodeType("basic/table[][]",h);z.title="Object property"; z.desc="Outputs the property of an object";z.prototype.setValue=function(a){this.properties.value=a;this.widget.value=a};z.prototype.getTitle=function(){return this.flags.collapsed?"in."+this.properties.value:this.title};z.prototype.onPropertyChanged=function(a,b){this.widget.value=b};z.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a[this.properties.value])};g.registerNodeType("basic/object_property",z);y.title="Object keys";y.desc="Outputs an array with the keys of an object"; y.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Object.keys(a))};g.registerNodeType("basic/object_keys",y);v.title="Merge Objects";v.desc="Creates an object copying properties from others";v.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1),d=this._result;if(a)for(var g in a)d[g]=a[g];if(b)for(g in b)d[g]=b[g];this.setOutputData(0,d)};g.registerNodeType("basic/merge_objects",v);G.title="Variable";G.desc="store/read variable value"; -G.prototype.onExecute=function(){this.value=this.getInputData(0);this.graph&&(this.graph.vars[this.properties.varname]=this.value);this.properties.global&&(B[this.properties.varname]=this.value);this.setOutputData(0,this.value)};G.prototype.getTitle=function(){return this.properties.varname};g.registerNodeType("basic/variable",G);g.wrapFunctionAsNode("basic/length",function(a){return a&&null!=a.length?Number(a.length):0},["*"],"number");q.title="Download";q.desc="Download some data";q.prototype.downloadAsFile= -function(){if(null!=this.value){var a=null,a=this.value.constructor===String?this.value:JSON.stringify(this.value),a=new Blob([a]),b=URL.createObjectURL(a),a=document.createElement("a");a.setAttribute("href",b);a.setAttribute("download",this.properties.filename);a.style.display="none";document.body.appendChild(a);a.click();document.body.removeChild(a);setTimeout(function(){URL.revokeObjectURL(b)},6E4)}};q.prototype.onAction=function(a,b){var d=this;setTimeout(function(){d.downloadAsFile()},100)}; -q.prototype.onExecute=function(){this.inputs[0]&&(this.value=this.getInputData(0))};q.prototype.getTitle=function(){return this.flags.collapsed?this.properties.filename:this.title};g.registerNodeType("basic/download",q);p.title="Watch";p.desc="Show value of input";p.prototype.onExecute=function(){this.inputs[0]&&(this.value=this.getInputData(0))};p.prototype.getTitle=function(){return this.flags.collapsed?this.inputs[0].label:this.title};p.toString=function(a){if(null==a)return"null";if(a.constructor=== -Number)return a.toFixed(3);if(a.constructor===Array){for(var b="[",d=0;d","enum",{values:a.values});this.size=[80,60]}function b(){this.addInput("inc","number");this.addOutput("total","number");this.addProperty("increment",1);this.addProperty("value",0)}function d(){this.addInput("v","number");this.addOutput("sin","number");this.addProperty("amplitude", 1);this.addProperty("offset",0);this.bgImageUrl="nodes/imgs/icon-sin.png"}function g(){this.addInput("x","number");this.addInput("y","number");this.addOutput("","number");this.properties={x:1,y:1,formula:"x+y"};this.code_widget=this.addWidget("text","F(x,y)",this.properties.formula,function(a,b,d){d.properties.formula=a});this.addWidget("toggle","allow",E.allow_scripts,function(a){E.allow_scripts=a});this._func=null}function f(){this.addInput("vec2","vec2");this.addOutput("x","number");this.addOutput("y", "number")}function r(){this.addInputs([["x","number"],["y","number"]]);this.addOutput("vec2","vec2");this.properties={x:0,y:0};this._data=new Float32Array(2)}function s(){this.addInput("vec3","vec3");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number")}function I(){this.addInputs([["x","number"],["y","number"],["z","number"]]);this.addOutput("vec3","vec3");this.properties={x:0,y:0,z:0};this._data=new Float32Array(3)}function J(){this.addInput("vec4","vec4");this.addOutput("x", @@ -347,10 +347,10 @@ null!=a&&(a=Math.max(this.properties.min,a),a=Math.min(this.properties.max,a),th (d=c);this.setOutputData(0,a*(1-d)+b*d)};u.prototype.onGetInputs=function(){return[["f","number"]]};E.registerNodeType("math/lerp",u);D.title="Abs";D.desc="Absolute";D.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.abs(a))};E.registerNodeType("math/abs",D);A.title="Floor";A.desc="Floor number to remove fractional part";A.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.floor(a))};E.registerNodeType("math/floor", A);h.title="Frac";h.desc="Returns fractional part";h.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a%1)};E.registerNodeType("math/frac",h);z.title="Smoothstep";z.desc="Smoothstep";z.prototype.onExecute=function(){var a=this.getInputData(0);if(void 0!==a){var b=this.properties.A,a=Math.clamp((a-b)/(this.properties.B-b),0,1);this.setOutputData(0,a*a*(3-2*a))}};E.registerNodeType("math/smoothstep",z);y.title="Scale";y.desc="v * factor";y.prototype.onExecute= function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a*this.properties.factor)};E.registerNodeType("math/scale",y);v.title="Gate";v.desc="if v is true, then outputs A, otherwise B";v.prototype.onExecute=function(){var a=this.getInputData(0);this.setOutputData(0,this.getInputData(a?1:2))};E.registerNodeType("math/gate",v);G.title="Average";G.desc="Average Filter";G.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this._values.length;this._values[this._current% -b]=a;this._current+=1;this._current>b&&(this._current=0);for(var d=a=0;db&&(b=1);this.properties.samples=Math.round(b);var d=this._values;this._values=new Float32Array(this.properties.samples);d.length<=this._values.length?this._values.set(d):this._values.set(d.subarray(0,this._values.length))};E.registerNodeType("math/average",G);q.title="TendTo";q.desc="moves the output value always closer to the input"; -q.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.factor;this._value=null==this._value?a:this._value*(1-b)+a*b;this.setOutputData(0,this._value)};E.registerNodeType("math/tendTo",q);p.values="+ - * / % ^ max min".split(" ");p.title="Operation";p.desc="Easy math operators";p["@OP"]={type:"enum",title:"operation",values:p.values};p.size=[100,60];p.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+ -"(A,B)":"A "+this.properties.OP+" B"};p.prototype.setValue=function(a){"string"==typeof a&&(a=parseFloat(a));this.properties.value=a};p.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);null!=a?this.properties.A=a:a=this.properties.A;null!=b?this.properties.B=b:b=this.properties.B;var d=0;switch(this.properties.OP){case "+":d=a+b;break;case "-":d=a-b;break;case "x":case "X":case "*":d=a*b;break;case "/":d=a/b;break;case "%":d=a%b;break;case "^":d=Math.pow(a,b);break; -case "max":d=Math.max(a,b);break;case "min":d=Math.min(a,b);break;default:console.warn("Unknown operation: "+this.properties.OP)}this.setOutputData(0,d)};p.prototype.onDrawBackground=function(a){this.flags.collapsed||(a.font="40px Arial",a.fillStyle="#666",a.textAlign="center",a.fillText(this.properties.OP,0.5*this.size[0],0.5*(this.size[1]+E.NODE_TITLE_HEIGHT)),a.textAlign="left")};E.registerNodeType("math/operation",p);E.registerSearchboxExtra("math/operation","MAX",{properties:{OP:"max"},title:"MAX()"}); +b]=a;this._current+=1;this._current>b&&(this._current=0);for(var d=a=0;db&&(b=1);this.properties.samples=Math.round(b);var d=this._values;this._values=new Float32Array(this.properties.samples);d.length<=this._values.length?this._values.set(d):this._values.set(d.subarray(0,this._values.length))};E.registerNodeType("math/average",G);p.title="TendTo";p.desc="moves the output value always closer to the input"; +p.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.factor;this._value=null==this._value?a:this._value*(1-b)+a*b;this.setOutputData(0,this._value)};E.registerNodeType("math/tendTo",p);q.values="+ - * / % ^ max min".split(" ");q.title="Operation";q.desc="Easy math operators";q["@OP"]={type:"enum",title:"operation",values:q.values};q.size=[100,60];q.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+ +"(A,B)":"A "+this.properties.OP+" B"};q.prototype.setValue=function(a){"string"==typeof a&&(a=parseFloat(a));this.properties.value=a};q.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);null!=a?this.properties.A=a:a=this.properties.A;null!=b?this.properties.B=b:b=this.properties.B;var d=0;switch(this.properties.OP){case "+":d=a+b;break;case "-":d=a-b;break;case "x":case "X":case "*":d=a*b;break;case "/":d=a/b;break;case "%":d=a%b;break;case "^":d=Math.pow(a,b);break; +case "max":d=Math.max(a,b);break;case "min":d=Math.min(a,b);break;default:console.warn("Unknown operation: "+this.properties.OP)}this.setOutputData(0,d)};q.prototype.onDrawBackground=function(a){this.flags.collapsed||(a.font="40px Arial",a.fillStyle="#666",a.textAlign="center",a.fillText(this.properties.OP,0.5*this.size[0],0.5*(this.size[1]+E.NODE_TITLE_HEIGHT)),a.textAlign="left")};E.registerNodeType("math/operation",q);E.registerSearchboxExtra("math/operation","MAX",{properties:{OP:"max"},title:"MAX()"}); E.registerSearchboxExtra("math/operation","MIN",{properties:{OP:"min"},title:"MIN()"});k.title="Compare";k.desc="compares between two values";k.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);void 0!==a?this.properties.A=a:a=this.properties.A;void 0!==b?this.properties.B=b:b=this.properties.B;for(var d=0,c=this.outputs.length;dB":f= a>b;break;case "A=B":f=a>=b}this.setOutputData(d,f)}}};k.prototype.onGetOutputs=function(){return[["A==B","boolean"],["A!=B","boolean"],["A>B","boolean"],["A=B","boolean"],["A<=B","boolean"]]};E.registerNodeType("math/compare",k);E.registerSearchboxExtra("math/compare","==",{outputs:[["A==B","boolean"]],title:"A==B"});E.registerSearchboxExtra("math/compare","!=",{outputs:[["A!=B","boolean"]],title:"A!=B"});E.registerSearchboxExtra("math/compare", ">",{outputs:[["A>B","boolean"]],title:"A>B"});E.registerSearchboxExtra("math/compare","<",{outputs:[["A=",{outputs:[["A>=B","boolean"]],title:"A>=B"});E.registerSearchboxExtra("math/compare","<=",{outputs:[["A<=B","boolean"]],title:"A<=B"});a.values="> < == != <= >= || &&".split(" ");a["@OP"]={type:"enum",title:"operation",values:a.values};a.title="Condition";a.desc="evaluates condition between A and B";a.prototype.getTitle= @@ -391,19 +391,19 @@ m.desc="select one element from a sequence from a string";m.prototype.onProperty "image"],["img2","image"],["fade","number"]]);this.addOutput("","image");this.properties={fade:0.5,width:512,height:512}}function e(){this.addInput("","image");this.addOutput("","image");this.properties={width:256,height:256,x:0,y:0,scale:1};this.size=[50,20]}function C(){this.addInput("clear",h.ACTION);this.addOutput("","canvas");this.properties={width:512,height:512,autoclear:!0};this.canvas=document.createElement("canvas");this.ctx=this.canvas.getContext("2d")}function w(){this.addInput("canvas", "canvas");this.addInput("img","image,canvas");this.addInput("x","number");this.addInput("y","number");this.properties={x:0,y:0,opacity:1}}function u(){this.addInput("canvas","canvas");this.addInput("x","number");this.addInput("y","number");this.addInput("w","number");this.addInput("h","number");this.properties={x:0,y:0,w:10,h:10,color:"white",opacity:1}}function D(){this.addInput("t","number");this.addOutputs([["frame","image"],["t","number"],["d","number"]]);this.properties={url:"",use_proxy:!0}} function A(){this.addOutput("Webcam","image");this.properties={facingMode:"user"};this.boxcolor="black";this.frame=0}var h=B.LiteGraph;c.title="Plot";c.desc="Plots data over time";c.colors=["#FFF","#F99","#9F9","#99F"];c.prototype.onExecute=function(c){if(!this.flags.collapsed){c=this.size;for(var e=0;4>e;++e){var h=this.getInputData(e);if(null!=h){var l=this.values[e];l.push(h);l.length>c[0]&&l.shift()}}}};c.prototype.onDrawBackground=function(e){if(!this.flags.collapsed){var h=this.size,l=0.5*h[1]/ -this.properties.scale,n=c.colors,q=0.5*h[1];e.fillStyle="#000";e.fillRect(0,0,h[0],h[1]);e.strokeStyle="#555";e.beginPath();e.moveTo(0,q);e.lineTo(h[0],q);e.stroke();if(this.inputs)for(var p=0;4>p;++p){var k=this.values[p];if(this.inputs[p]&&this.inputs[p].link){e.strokeStyle=n[p];e.beginPath();var a=k[0]*l*-1+q;e.moveTo(0,Math.clamp(a,0,h[1]));for(var b=1;bq;++q){var k=this.values[q];if(this.inputs[q]&&this.inputs[q].link){e.strokeStyle=n[q];e.beginPath();var a=k[0]*l*-1+p;e.moveTo(0,Math.clamp(a,0,h[1]));for(var b=1;be&&(e=0);if(0!=c.length){var h=[0,0,0];if(0==e)h=c[0];else if(1==e)h=c[c.length-1];else{var l= -(c.length-1)*e,e=c[Math.floor(l)],c=c[Math.floor(l)+1],l=l-Math.floor(l);h[0]=e[0]*(1-l)+c[0]*l;h[1]=e[1]*(1-l)+c[1]*l;h[2]=e[2]*(1-l)+c[2]*l}for(var q in h)h[q]/=255;this.boxcolor=colorToString(h);this.setOutputData(0,h)}};h.registerNodeType("color/palette",n);l.title="Frame";l.desc="Frame viewerew";l.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}];l.prototype.onDrawBackground=function(c){this.frame&&!this.flags.collapsed&&c.drawImage(this.frame, +(c.length-1)*e,e=c[Math.floor(l)],c=c[Math.floor(l)+1],l=l-Math.floor(l);h[0]=e[0]*(1-l)+c[0]*l;h[1]=e[1]*(1-l)+c[1]*l;h[2]=e[2]*(1-l)+c[2]*l}for(var p in h)h[p]/=255;this.boxcolor=colorToString(h);this.setOutputData(0,h)}};h.registerNodeType("color/palette",n);l.title="Frame";l.desc="Frame viewerew";l.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}];l.prototype.onDrawBackground=function(c){this.frame&&!this.flags.collapsed&&c.drawImage(this.frame, 0,0,this.size[0],this.size[1])};l.prototype.onExecute=function(){this.frame=this.getInputData(0);this.setDirtyCanvas(!0)};l.prototype.onWidget=function(c,e){if("resize"==e.name&&this.frame){var h=this.frame.width,l=this.frame.height;h||null==this.frame.videoWidth||(h=this.frame.videoWidth,l=this.frame.videoHeight);h&&l&&(this.size=[h,l]);this.setDirtyCanvas(!0,!0)}else"view"==e.name&&this.show()};l.prototype.show=function(){showElement&&this.frame&&showElement(this.frame)};h.registerNodeType("graphics/frame", l);x.title="Image fade";x.desc="Fades between images";x.widgets=[{name:"resizeA",text:"Resize to A",type:"button"},{name:"resizeB",text:"Resize to B",type:"button"}];x.prototype.onAdded=function(){this.createCanvas();var c=this.canvas.getContext("2d");c.fillStyle="#000";c.fillRect(0,0,this.properties.width,this.properties.height)};x.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};x.prototype.onExecute= function(){var c=this.canvas.getContext("2d");this.canvas.width=this.canvas.width;var e=this.getInputData(0);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);e=this.getInputData(2);null==e?e=this.properties.fade:this.properties.fade=e;c.globalAlpha=e;e=this.getInputData(1);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);c.globalAlpha=1;this.setOutputData(0,this.canvas);this.setDirtyCanvas(!0)};h.registerNodeType("graphics/imagefade",x);e.title="Crop";e.desc="Crop Image"; e.prototype.onAdded=function(){this.createCanvas()};e.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};e.prototype.onExecute=function(){var c=this.getInputData(0);c&&(c.width?(this.canvas.getContext("2d").drawImage(c,-this.properties.x,-this.properties.y,c.width*this.properties.scale,c.height*this.properties.scale),this.setOutputData(0,this.canvas)):this.setOutputData(0,null))};e.prototype.onDrawBackground= function(c){this.flags.collapsed||this.canvas&&c.drawImage(this.canvas,0,0,this.canvas.width,this.canvas.height,0,0,this.size[0],this.size[1])};e.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"scale"==c?(this.properties[c]=parseFloat(e),0==this.properties[c]&&(console.error("Error in scale"),this.properties[c]=1)):this.properties[c]=parseInt(e);this.createCanvas();return!0};h.registerNodeType("graphics/cropImage",e);C.title="Canvas";C.desc="Canvas to render stuff";C.prototype.onExecute= function(){var c=this.canvas,e=this.properties.width|0,h=this.properties.height|0;c.width!=e&&(c.width=e);c.height!=h&&(c.height=h);this.properties.autoclear&&this.ctx.clearRect(0,0,c.width,c.height);this.setOutputData(0,c)};C.prototype.onAction=function(c,e){"clear"==c&&this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)};h.registerNodeType("graphics/canvas",C);w.title="DrawImage";w.desc="Draws image into a canvas";w.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e= -this.getInputOrProperty("img");if(e){var h=this.getInputOrProperty("x"),l=this.getInputOrProperty("y");c.getContext("2d").drawImage(e,h,l)}}};h.registerNodeType("graphics/drawImage",w);u.title="DrawRectangle";u.desc="Draws rectangle in canvas";u.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e=this.getInputOrProperty("x"),h=this.getInputOrProperty("y"),l=this.getInputOrProperty("w"),q=this.getInputOrProperty("h");c.getContext("2d").fillRect(e,h,l,q)}};h.registerNodeType("graphics/drawRectangle", +this.getInputOrProperty("img");if(e){var h=this.getInputOrProperty("x"),l=this.getInputOrProperty("y");c.getContext("2d").drawImage(e,h,l)}}};h.registerNodeType("graphics/drawImage",w);u.title="DrawRectangle";u.desc="Draws rectangle in canvas";u.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e=this.getInputOrProperty("x"),h=this.getInputOrProperty("y"),l=this.getInputOrProperty("w"),p=this.getInputOrProperty("h");c.getContext("2d").fillRect(e,h,l,p)}};h.registerNodeType("graphics/drawRectangle", u);D.title="Video";D.desc="Video playback";D.widgets=[{name:"play",text:"PLAY",type:"minibutton"},{name:"stop",text:"STOP",type:"minibutton"},{name:"demo",text:"Demo video",type:"button"},{name:"mute",text:"Mute video",type:"button"}];D.prototype.onExecute=function(){if(this.properties.url&&(this.properties.url!=this._video_url&&this.loadVideo(this.properties.url),this._video&&0!=this._video.width)){var c=this.getInputData(0);c&&0<=c&&1>=c&&(this._video.currentTime=c*this._video.duration,this._video.pause()); this._video.dirty=!0;this.setOutputData(0,this._video);this.setOutputData(1,this._video.currentTime);this.setOutputData(2,this._video.duration);this.setDirtyCanvas(!0)}};D.prototype.onStart=function(){this.play()};D.prototype.onStop=function(){this.stop()};D.prototype.loadVideo=function(c){this._video_url=c;var e=c.substr(0,10).indexOf(":"),l="";-1!=e&&(l=c.substr(0,e));e="";l&&(e=c.substr(0,c.indexOf("/",l.length+3)),e=e.substr(l.length+3));this.properties.use_proxy&&l&&h.proxy&&e!=location.host&& (c=h.proxy+c.substr(c.indexOf(":")+3));this._video=document.createElement("video");this._video.src=c;this._video.type="type=video/mp4";this._video.muted=!0;this._video.autoplay=!0;var n=this;this._video.addEventListener("loadedmetadata",function(c){console.log("Duration: "+this.duration+" seconds");console.log("Size: "+this.videoWidth+","+this.videoHeight);n.setDirtyCanvas(!0);this.width=this.videoWidth;this.height=this.videoHeight});this._video.addEventListener("progress",function(c){console.log("video loading...")}); @@ -421,7 +421,7 @@ function C(){this.addInput("in","Texture");this.addInput("warp","Texture");this. "Texture");this.addOutput("","Texture");this.properties={size:0,generate_mipmaps:!1,precision:c.DEFAULT}}function D(){this.addInput("Texture","Texture");this.addOutput("","Texture");this.properties={iterations:1,generate_mipmaps:!1,precision:c.DEFAULT}}function A(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("avg","vec4");this.addOutput("lum","number");this.properties={use_previous_frame:!0,high_quality:!1};this._uniforms={u_texture:0,u_mipmap_offset:0};this._luminance= new Float32Array(4)}function h(){this.addInput("Texture","Texture");this.addOutput("min_t","Texture");this.addOutput("max_t","Texture");this.addOutput("min","vec4");this.addOutput("max","vec4");this.properties={mode:"max",use_previous_frame:!0};this._uniforms={u_texture:0};this._max=new Float32Array(4);this._min=new Float32Array(4);this._textures_chain=[]}function z(){this.addInput("in","Texture");this.addInput("factor","Number");this.addOutput("out","Texture");this.properties={factor:0.5};this._uniforms= {u_texture:0,u_textureB:1,u_factor:this.properties.factor}}function y(){this.addInput("in","Texture");this.addOutput("avg","Texture");this.addOutput("array","Texture");this.properties={samples:64,frames_interval:1};this._uniforms={u_texture:0,u_textureB:1,u_samples:this.properties.samples,u_isamples:1/this.properties.samples};this.frame=0}function v(){this.addInput("Image","image");this.addOutput("","Texture");this.properties={}}function G(){this.addInput("Texture","Texture");this.addInput("LUT", -"Texture");this.addInput("Intensity","number");this.addOutput("","Texture");this.properties={enabled:!0,intensity:1,precision:c.DEFAULT,texture:null};G._shader||(G._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,G.pixel_shader))}function q(){this.addInput("Texture","Texture");this.addOutput("R","Texture");this.addOutput("G","Texture");this.addOutput("B","Texture");this.addOutput("A","Texture");q._shader||(q._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,q.pixel_shader))}function p(){this.addInput("R", +"Texture");this.addInput("Intensity","number");this.addOutput("","Texture");this.properties={enabled:!0,intensity:1,precision:c.DEFAULT,texture:null};G._shader||(G._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,G.pixel_shader))}function p(){this.addInput("Texture","Texture");this.addOutput("R","Texture");this.addOutput("G","Texture");this.addOutput("B","Texture");this.addOutput("A","Texture");p._shader||(p._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,p.pixel_shader))}function q(){this.addInput("R", "Texture");this.addInput("G","Texture");this.addInput("B","Texture");this.addInput("A","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,R:1,G:1,B:1,A:1};this._color=vec4.create();this._uniforms={u_textureR:0,u_textureG:1,u_textureB:2,u_textureA:3,u_color:this._color}}function k(){this.addOutput("Texture","Texture");this._tex_color=vec4.create();this.properties={color:vec4.create(),precision:c.DEFAULT}}function a(){this.addInput("A","color");this.addInput("B","color"); this.addOutput("Texture","Texture");this.properties={angle:0,scale:1,A:[0,0,0],B:[1,1,1],texture_size:32};a._shader||(a._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,a.pixel_shader));this._uniforms={u_angle:0,u_colorA:vec3.create(),u_colorB:vec3.create()}}function b(){this.addInput("A","Texture");this.addInput("B","Texture");this.addInput("Mixer","Texture");this.addOutput("Texture","Texture");this.properties={factor:0.5,size_from_biggest:!0,invert:!1,precision:c.DEFAULT};this._uniforms={u_textureA:0, u_textureB:1,u_textureMix:2,u_mix:vec4.create()}}function d(){this.addInput("Tex.","Texture");this.addOutput("Edges","Texture");this.properties={invert:!0,threshold:!1,factor:1,precision:c.DEFAULT};d._shader||(d._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,d.pixel_shader))}function g(){this.addInput("Texture","Texture");this.addInput("Distance","number");this.addInput("Range","number");this.addOutput("Texture","Texture");this.properties={distance:100,range:50,only_depth:!1,high_precision:!1}; @@ -445,8 +445,8 @@ F.registerNodeType("texture/preview",m),n.title="Save",n.desc="Save a texture in a),this._texture=a,this.setOutputData(0,a),this.setOutputData(1,this.properties.name))},F.registerNodeType("texture/save",n),l.widgets_info={uvcode:{widget:"code"},pixelcode:{widget:"code"},precision:{widget:"combo",values:c.MODE_VALUES}},l.title="Operation",l.desc="Texture shader operation",l.presets={},l.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:b.properties.show?"Hide Texture":"Show Texture",callback:function(){b.properties.show=!b.properties.show}}]},l.prototype.onPropertyChanged= function(){this.has_error=!1},l.prototype.onDrawBackground=function(a){this.flags.collapsed||20>=this.size[1]||!this.properties.show||!this._tex||this._tex.gl!=a||(a.save(),a.drawImage(this._tex,0,0,this.size[0],this.size[1]),a.restore())},l.prototype.onExecute=function(){var a=this.getInputData(0);if(this.isOutputConnected(0))if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else{var b=this.getInputData(1);if(this.properties.uvcode||this.properties.pixelcode){var d=512,g=512; a?(d=a.width,g=a.height):b&&(d=b.width,g=b.height);b||(b=GL.Texture.getWhiteTexture());var f=c.getTextureType(this.properties.precision,a);this._tex=a||this._tex?c.getTargetTexture(a||this._tex,this._tex,this.properties.precision):new GL.Texture(d,g,{type:f,format:gl.RGBA,filter:gl.LINEAR});f="";this.properties.uvcode&&(f="uv = "+this.properties.uvcode,-1!=this.properties.uvcode.indexOf(";")&&(f=this.properties.uvcode));var e="";this.properties.pixelcode&&(e="result = "+this.properties.pixelcode, --1!=this.properties.pixelcode.indexOf(";")&&(e=this.properties.pixelcode));var k=this._shader;if(!(this.has_error||k&&this._shader_code==f+"|"+e)){var h=c.replaceCode(l.pixel_shader,{UV_CODE:f,PIXEL_CODE:e});try{k=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,h),this.boxcolor="#00FF00"}catch(q){GL.Shader.dumpErrorToConsole(q,Shader.SCREEN_VERTEX_SHADER,h);this.boxcolor="#FF0000";this.has_error=!0;return}this._shader=k;this._shader_code=f+"|"+e}if(this._shader){var p=this.getInputData(2);null!=p?this.properties.value= -p:p=parseFloat(this.properties.value);var r=this.graph.getTime();this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);gl.disable(gl.BLEND);a&&a.bind(0);b&&b.bind(1);var c=Mesh.getScreenQuad();k.uniforms({u_texture:0,u_textureB:1,value:p,texSize:[d,g],time:r}).draw(c)});this.setOutputData(0,this._tex)}}}},l.pixel_shader="precision highp float;\n\t\t\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec2 texSize;\n\t\tuniform float time;\n\t\tuniform float value;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord;\n\t\t\t{{UV_CODE}};\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\tvec3 color = color4.rgb;\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\tvec3 colorB = color4B.rgb;\n\t\t\tvec3 result = color;\n\t\t\tfloat alpha = 1.0;\n\t\t\t{{PIXEL_CODE}};\n\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t}\n\t\t", +-1!=this.properties.pixelcode.indexOf(";")&&(e=this.properties.pixelcode));var k=this._shader;if(!(this.has_error||k&&this._shader_code==f+"|"+e)){var h=c.replaceCode(l.pixel_shader,{UV_CODE:f,PIXEL_CODE:e});try{k=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,h),this.boxcolor="#00FF00"}catch(p){GL.Shader.dumpErrorToConsole(p,Shader.SCREEN_VERTEX_SHADER,h);this.boxcolor="#FF0000";this.has_error=!0;return}this._shader=k;this._shader_code=f+"|"+e}if(this._shader){var q=this.getInputData(2);null!=q?this.properties.value= +q:q=parseFloat(this.properties.value);var r=this.graph.getTime();this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);gl.disable(gl.BLEND);a&&a.bind(0);b&&b.bind(1);var c=Mesh.getScreenQuad();k.uniforms({u_texture:0,u_textureB:1,value:q,texSize:[d,g],time:r}).draw(c)});this.setOutputData(0,this._tex)}}}},l.pixel_shader="precision highp float;\n\t\t\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec2 texSize;\n\t\tuniform float time;\n\t\tuniform float value;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord;\n\t\t\t{{UV_CODE}};\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\tvec3 color = color4.rgb;\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\tvec3 colorB = color4B.rgb;\n\t\t\tvec3 result = color;\n\t\t\tfloat alpha = 1.0;\n\t\t\t{{PIXEL_CODE}};\n\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t}\n\t\t", l.registerPreset=function(a,b){l.presets[a]=b},l.registerPreset("",""),l.registerPreset("bypass","color"),l.registerPreset("add","color + colorB * value"),l.registerPreset("substract","(color - colorB) * value"),l.registerPreset("mate","mix( color, colorB, color4B.a * value)"),l.registerPreset("invert","vec3(1.0) - color"),l.registerPreset("multiply","color * colorB * value"),l.registerPreset("divide","(color / colorB) / value"),l.registerPreset("difference","abs(color - colorB) * value"),l.registerPreset("max", "max(color, colorB) * value"),l.registerPreset("min","min(color, colorB) * value"),l.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"),l.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"),l.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"),l.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"), l.prototype.onInspect=function(a){var b=this;a.addCombo("Presets","",{values:Object.keys(l.presets),callback:function(d){var c=l.presets[d];c&&(b.setProperty("pixelcode",c),b.title=d,a.refresh())}})},F.registerNodeType("texture/operation",l),x.title="Shader",x.desc="Texture shader",x.widgets_info={code:{type:"code",lang:"glsl"},precision:{widget:"combo",values:c.MODE_VALUES}},x.prototype.onPropertyChanged=function(a,b){if("code"==a){var d=this.getShader();if(d){var c=d.uniformInfo;if(this.inputs)for(var g= @@ -467,7 +467,7 @@ w.gamma_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tva values:c.MODE_VALUES}},u.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)){if(a){var b=a.width,d=a.height;0!=this.properties.size&&(d=b=this.properties.size);var g=this._temp_texture,f=a.type;this.properties.precision===c.LOW?f=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(f=gl.HIGH_PRECISION_FORMAT);g&&g.width==b&&g.height==d&&g.type==f||(g=gl.LINEAR,this.properties.generate_mipmaps&&isPowerOfTwo(b)&&isPowerOfTwo(d)&&(g=gl.LINEAR_MIPMAP_LINEAR), this._temp_texture=new GL.Texture(b,d,{type:f,format:gl.RGBA,minFilter:g,magFilter:gl.LINEAR}));a.copyTo(this._temp_texture);this.properties.generate_mipmaps&&(this._temp_texture.bind(0),gl.generateMipmap(this._temp_texture.texture_type),this._temp_texture.unbind(0))}this.setOutputData(0,this._temp_texture)}},F.registerNodeType("texture/copy",u),D.title="Downsample",D.desc="Downsample Texture",D.widgets_info={iterations:{type:"number",step:1,precision:0,min:0},precision:{widget:"combo",values:c.MODE_VALUES}}, D.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)&&a&&a.texture_type===GL.TEXTURE_2D)if(1>this.properties.iterations)this.setOutputData(0,a);else{var b=D._shader;b||(D._shader=b=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,D.pixel_shader));var d=a.width|0,g=a.height|0,f=a.type;this.properties.precision===c.LOW?f=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(f=gl.HIGH_PRECISION_FORMAT);var e=this.properties.iterations||1,k=a,h= -null,l=[],a={type:f,format:a.format},f=vec2.create(),q={u_offset:f};this._texture&&GL.Texture.releaseTemporary(this._texture);for(var p=0;p>1||0;g=g>>1||0;h=GL.Texture.getTemporary(d,g,a);l.push(h);k.setParameter(GL.TEXTURE_MAG_FILTER,GL.NEAREST);k.copyTo(h,b,q);if(1==d&&1==g)break;k=h}this._texture=l.pop();for(p=0;p>1||0;g=g>>1||0;h=GL.Texture.getTemporary(d,g,a);l.push(h);k.setParameter(GL.TEXTURE_MAG_FILTER,GL.NEAREST);k.copyTo(h,b,p);if(1==d&&1==g)break;k=h}this._texture=l.pop();for(q=0;qc;c++)this.isOutputConnected(c)?(this._channels[c]&&this._channels[c].width==a.width&&this._channels[c].height==a.height&&this._channels[c].type==a.type&&this._channels[c].format==b||(this._channels[c]=new GL.Texture(a.width,a.height,{type:a.type,format:b,filter:gl.LINEAR})), -d++):this._channels[c]=null;if(d){gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);for(var g=Mesh.getScreenQuad(),f=q._shader,e=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],c=0;4>c;c++)this._channels[c]&&(this._channels[c].drawTo(function(){a.bind(0);f.uniforms({u_texture:0,u_mask:e[c]}).draw(g)}),this.setOutputData(c,this._channels[c]))}}},q.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec4 u_mask;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\t\t}\n\t\t", -F.registerNodeType("texture/textureChannels",q),p.title="Channels to Texture",p.desc="Split texture channels",p.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},p.prototype.onExecute=function(){var a=c.getWhiteTexture(),b=this.getInputData(0)||a,d=this.getInputData(1)||a,g=this.getInputData(2)||a,f=this.getInputData(3)||a;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad();p._shader||(p._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,p.pixel_shader));var k=p._shader, -a=Math.max(b.width,d.width,g.width,f.width),h=Math.max(b.height,d.height,g.height,f.height),l=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._texture&&this._texture.width==a&&this._texture.height==h&&this._texture.type==l||(this._texture=new GL.Texture(a,h,{type:l,format:gl.RGBA,filter:gl.LINEAR}));a=this._color;a[0]=this.properties.R;a[1]=this.properties.G;a[2]=this.properties.B;a[3]=this.properties.A;var q=this._uniforms;this._texture.drawTo(function(){b.bind(0); -d.bind(1);g.bind(2);f.bind(3);k.uniforms(q).draw(e)});this.setOutputData(0,this._texture)},p.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureR;\n\t\tuniform sampler2D u_textureG;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform vec4 u_color;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = u_color * vec4( \t\t\t\t\ttexture2D(u_textureR, v_coord).r,\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\t\t}\n\t\t", -F.registerNodeType("texture/channelsTexture",p),k.title="Color",k.desc="Generates a 1x1 texture with a constant color",k.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},k.prototype.onDrawBackground=function(a){var b=this.properties.color;a.fillStyle="rgb("+Math.floor(255*Math.clamp(b[0],0,1))+","+Math.floor(255*Math.clamp(b[1],0,1))+","+Math.floor(255*Math.clamp(b[2],0,1))+")";this.flags.collapsed?this.boxcolor=a.fillStyle:a.fillRect(0,0,this.size[0],this.size[1])},k.prototype.onExecute= +F.registerNodeType("texture/LUT",G),p.title="Texture to Channels",p.desc="Split texture channels",p.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this._channels||(this._channels=Array(4));for(var b=gl.RGB,d=0,c=0;4>c;c++)this.isOutputConnected(c)?(this._channels[c]&&this._channels[c].width==a.width&&this._channels[c].height==a.height&&this._channels[c].type==a.type&&this._channels[c].format==b||(this._channels[c]=new GL.Texture(a.width,a.height,{type:a.type,format:b,filter:gl.LINEAR})), +d++):this._channels[c]=null;if(d){gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);for(var g=Mesh.getScreenQuad(),f=p._shader,e=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],c=0;4>c;c++)this._channels[c]&&(this._channels[c].drawTo(function(){a.bind(0);f.uniforms({u_texture:0,u_mask:e[c]}).draw(g)}),this.setOutputData(c,this._channels[c]))}}},p.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec4 u_mask;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\t\t}\n\t\t", +F.registerNodeType("texture/textureChannels",p),q.title="Channels to Texture",q.desc="Split texture channels",q.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},q.prototype.onExecute=function(){var a=c.getWhiteTexture(),b=this.getInputData(0)||a,d=this.getInputData(1)||a,g=this.getInputData(2)||a,f=this.getInputData(3)||a;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad();q._shader||(q._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,q.pixel_shader));var k=q._shader, +a=Math.max(b.width,d.width,g.width,f.width),h=Math.max(b.height,d.height,g.height,f.height),l=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._texture&&this._texture.width==a&&this._texture.height==h&&this._texture.type==l||(this._texture=new GL.Texture(a,h,{type:l,format:gl.RGBA,filter:gl.LINEAR}));a=this._color;a[0]=this.properties.R;a[1]=this.properties.G;a[2]=this.properties.B;a[3]=this.properties.A;var p=this._uniforms;this._texture.drawTo(function(){b.bind(0); +d.bind(1);g.bind(2);f.bind(3);k.uniforms(p).draw(e)});this.setOutputData(0,this._texture)},q.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureR;\n\t\tuniform sampler2D u_textureG;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform vec4 u_color;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = u_color * vec4( \t\t\t\t\ttexture2D(u_textureR, v_coord).r,\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\t\t}\n\t\t", +F.registerNodeType("texture/channelsTexture",q),k.title="Color",k.desc="Generates a 1x1 texture with a constant color",k.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},k.prototype.onDrawBackground=function(a){var b=this.properties.color;a.fillStyle="rgb("+Math.floor(255*Math.clamp(b[0],0,1))+","+Math.floor(255*Math.clamp(b[1],0,1))+","+Math.floor(255*Math.clamp(b[2],0,1))+")";this.flags.collapsed?this.boxcolor=a.fillStyle:a.fillRect(0,0,this.size[0],this.size[1])},k.prototype.onExecute= function(){var a=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._tex&&this._tex.type==a||(this._tex=new GL.Texture(1,1,{format:gl.RGBA,type:a,minFilter:gl.NEAREST}));a=this.properties.color;if(this.inputs)for(var b=0;b>=1;1<(d|0)&&(d>>=1);if(2>b)break;l=k[m]=GL.Texture.getTemporary(b,d,g);r[0]=1/q.width;r[1]=1/q.height;q.blit(l,h.uniforms(e));q=l}this.isOutputConnected(2)&&(b=this._average_texture,b&&b.type==a.type&&b.format==a.format||(b=this._average_texture=new GL.Texture(1,1,{type:a.type,format:a.format,filter:gl.LINEAR})),r[0]=1/q.width,r[1]=1/q.height,e.u_intensity=n,e.u_delta=1,q.blit(b,h.uniforms(e)),this.setOutputData(2,b));gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE);e.u_intensity= -this.getInputOrProperty("persistence");e.u_delta=0.5;for(m-=2;0<=m;m--)l=k[m],k[m]=null,r[0]=1/q.width,r[1]=1/q.height,q.blit(l,h.uniforms(e)),GL.Texture.releaseTemporary(q),q=l;gl.disable(gl.BLEND);this.isOutputConnected(1)&&(k=this._glow_texture,k&&k.width==a.width&&k.height==a.height&&k.type==f&&k.format==a.format||(k=this._glow_texture=new GL.Texture(a.width,a.height,{type:f,format:a.format,filter:gl.LINEAR})),q.blit(k),this.setOutputData(1,k));if(this.isOutputConnected(0)){k=this._final_texture; +h||(h=s._cut_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,s.cut_pixel_shader));gl.disable(gl.DEPTH_TEST);gl.disable(gl.BLEND);e.u_threshold=this.getInputOrProperty("threshold");var l=k[0]=GL.Texture.getTemporary(b,d,g);a.blit(l,h.uniforms(e));var p=l,q=this.getInputOrProperty("iterations"),q=Math.clamp(q,1,16)|0,r=e.u_texel_size,n=this.getInputOrProperty("intensity");e.u_intensity=1;e.u_delta=this.properties.scale;h=s._shader;h||(h=s._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,s.scale_pixel_shader)); +for(var m=1;m>=1;1<(d|0)&&(d>>=1);if(2>b)break;l=k[m]=GL.Texture.getTemporary(b,d,g);r[0]=1/p.width;r[1]=1/p.height;p.blit(l,h.uniforms(e));p=l}this.isOutputConnected(2)&&(b=this._average_texture,b&&b.type==a.type&&b.format==a.format||(b=this._average_texture=new GL.Texture(1,1,{type:a.type,format:a.format,filter:gl.LINEAR})),r[0]=1/p.width,r[1]=1/p.height,e.u_intensity=n,e.u_delta=1,p.blit(b,h.uniforms(e)),this.setOutputData(2,b));gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE);e.u_intensity= +this.getInputOrProperty("persistence");e.u_delta=0.5;for(m-=2;0<=m;m--)l=k[m],k[m]=null,r[0]=1/p.width,r[1]=1/p.height,p.blit(l,h.uniforms(e)),GL.Texture.releaseTemporary(p),p=l;gl.disable(gl.BLEND);this.isOutputConnected(1)&&(k=this._glow_texture,k&&k.width==a.width&&k.height==a.height&&k.type==f&&k.format==a.format||(k=this._glow_texture=new GL.Texture(a.width,a.height,{type:f,format:a.format,filter:gl.LINEAR})),p.blit(k),this.setOutputData(1,k));if(this.isOutputConnected(0)){k=this._final_texture; k&&k.width==a.width&&k.height==a.height&&k.type==f&&k.format==a.format||(k=this._final_texture=new GL.Texture(a.width,a.height,{type:f,format:a.format,filter:gl.LINEAR}));var t=this.getInputData(1),u=this.getInputOrProperty("dirt_factor");e.u_intensity=n;h=t?s._dirt_final_shader:s._final_shader;h||(h=t?s._dirt_final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,s.final_pixel_shader,{USE_DIRT:""}):s._final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,s.final_pixel_shader));k.drawTo(function(){a.bind(0); -q.bind(1);t&&(h.setUniform("u_dirt_factor",u),h.setUniform("u_dirt_texture",t.bind(2)));h.toViewport(e)});this.setOutputData(0,k)}GL.Texture.releaseTemporary(q)}},s.cut_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform float u_threshold;\n\tvoid main() {\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\t}",s.scale_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\t}", +p.bind(1);t&&(h.setUniform("u_dirt_factor",u),h.setUniform("u_dirt_texture",t.bind(2)));h.toViewport(e)});this.setOutputData(0,k)}GL.Texture.releaseTemporary(p)}},s.cut_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform float u_threshold;\n\tvoid main() {\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\t}",s.scale_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\t}", s.final_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform sampler2D u_glow_texture;\n\t#ifdef USE_DIRT\n\t\tuniform sampler2D u_dirt_texture;\n\t#endif\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\tuniform float u_dirt_factor;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tvec4 glow = sampleBox( v_coord );\n\t\t#ifdef USE_DIRT\n\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\t\t#endif\n\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\t}", F.registerNodeType("texture/glow",s),I.title="Kuwahara Filter",I.desc="Filters a texture giving an artistic oil canvas painting",I.max_radius=10,I._shaders=[],I.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));b=this.properties.radius;b=Math.min(Math.floor(b),I.max_radius);if(0== b)this.setOutputData(0,a);else{var d=this.properties.intensity,c=F.camera_aspect;c||void 0===window.gl||(c=gl.canvas.height/gl.canvas.width);c||(c=1);c=this.properties.preserve_aspect?c:1;I._shaders[b]||(I._shaders[b]=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,I.pixel_shader,{RADIUS:b.toFixed(0)}));var g=I._shaders[b],f=GL.Mesh.getScreenQuad();a.bind(0);this._temp_texture.drawTo(function(){g.uniforms({u_texture:0,u_intensity:d,u_resolution:[a.width,a.height],u_iResolution:[1/a.width,1/a.height]}).draw(f)}); @@ -542,12 +542,12 @@ F.registerNodeType("texture/exposition",N),K.title="Tone Mapping",K.desc="Applie b.height==a.height&&b.type==a.type||(b=this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));var d=this.getInputData(1);null==d&&(d=this.properties.average_lum);var g=this._uniforms,f=null;d.constructor===Number?(this.properties.average_lum=d,g.u_average_lum=this.properties.average_lum,f=K._shader,f||(f=K._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,K.pixel_shader))):d.constructor===GL.Texture&&(g.u_average_texture=d.bind(1),f=K._shader_texture, f||(f=K._shader_texture=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,K.pixel_shader,{AVG_TEXTURE:""})));g.u_lumwhite2=this.properties.lum_white*this.properties.lum_white;g.u_scale=this.properties.scale;g.u_igamma=1/this.properties.gamma;gl.disable(gl.DEPTH_TEST);b.drawTo(function(){a.bind(0);f.uniforms(g).draw(GL.Mesh.getScreenQuad())});this.setOutputData(0,this._temp_texture)}},K.pixel_shader="precision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_scale;\n\t\t#ifdef AVG_TEXTURE\n\t\t\tuniform sampler2D u_average_texture;\n\t\t#else\n\t\t\tuniform float u_average_lum;\n\t\t#endif\n\t\tuniform float u_lumwhite2;\n\t\tuniform float u_igamma;\n\t\tvec3 RGB2xyY (vec3 rgb)\n\t\t{\n\t\t\t const mat3 RGB2XYZ = mat3(0.4124, 0.3576, 0.1805,\n\t\t\t\t\t\t\t\t\t 0.2126, 0.7152, 0.0722,\n\t\t\t\t\t\t\t\t\t 0.0193, 0.1192, 0.9505);\n\t\t\tvec3 XYZ = RGB2XYZ * rgb;\n\t\t\t\n\t\t\tfloat f = (XYZ.x + XYZ.y + XYZ.z);\n\t\t\treturn vec3(XYZ.x / f,\n\t\t\t\t\t\tXYZ.y / f,\n\t\t\t\t\t\tXYZ.y);\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = texture2D( u_texture, v_coord );\n\t\t\tvec3 rgb = color.xyz;\n\t\t\tfloat average_lum = 0.0;\n\t\t\t#ifdef AVG_TEXTURE\n\t\t\t\tvec3 pixel = texture2D(u_average_texture,vec2(0.5)).xyz;\n\t\t\t\taverage_lum = (pixel.x + pixel.y + pixel.z) / 3.0;\n\t\t\t#else\n\t\t\t\taverage_lum = u_average_lum;\n\t\t\t#endif\n\t\t\t//Ld - this part of the code is the same for both versions\n\t\t\tfloat lum = dot(rgb, vec3(0.2126, 0.7152, 0.0722));\n\t\t\tfloat L = (u_scale / average_lum) * lum;\n\t\t\tfloat Ld = (L * (1.0 + L / u_lumwhite2)) / (1.0 + L);\n\t\t\t//first\n\t\t\t//vec3 xyY = RGB2xyY(rgb);\n\t\t\t//xyY.z *= Ld;\n\t\t\t//rgb = xyYtoRGB(xyY);\n\t\t\t//second\n\t\t\trgb = (rgb / lum) * Ld;\n\t\t\trgb = max(rgb,vec3(0.001));\n\t\t\trgb = pow( rgb, vec3( u_igamma ) );\n\t\t\tgl_FragColor = vec4( rgb, color.a );\n\t\t}", F.registerNodeType("texture/tonemapping",K),M.title="Perlin",M.desc="Generates a perlin noise texture",M.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES},width:{type:"Number",precision:0,step:1},height:{type:"Number",precision:0,step:1},octaves:{type:"Number",precision:0,step:1,min:1,max:50}},M.prototype.onGetInputs=function(){return[["seed","Number"],["persistence","Number"],["octaves","Number"],["scale","Number"],["amplitude","Number"],["offset","vec2"]]},M.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a= -this.properties.width|0,b=this.properties.height|0;0==a&&(a=gl.viewport_data[2]);0==b&&(b=gl.viewport_data[3]);var d=c.getTextureType(this.properties.precision),g=this._texture;g&&g.width==a&&g.height==b&&g.type==d||(g=this._texture=new GL.Texture(a,b,{type:d,format:gl.RGB,filter:gl.LINEAR}));var f=this.getInputOrProperty("persistence"),e=this.getInputOrProperty("octaves"),k=this.getInputOrProperty("offset"),h=this.getInputOrProperty("scale"),l=this.getInputOrProperty("amplitude"),q=this.getInputOrProperty("seed"), -d=""+a+b+d+f+e+h+q+k[0]+k[1]+l;if(d!=this._key){this._key=d;var p=this._uniforms;p.u_persistence=f;p.u_octaves=e;p.u_offset.set(k);p.u_scale=h;p.u_amplitude=l;p.u_seed=128*q;p.u_viewport[0]=a;p.u_viewport[1]=b;var r=M._shader;r||(r=M._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,M.pixel_shader));gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);g.drawTo(function(){r.uniforms(p).draw(GL.Mesh.getScreenQuad())})}this.setOutputData(0,g)}},M.pixel_shader="precision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec2 u_offset;\n\t\tuniform float u_scale;\n\t\tuniform float u_persistence;\n\t\tuniform int u_octaves;\n\t\tuniform float u_amplitude;\n\t\tuniform vec2 u_viewport;\n\t\tuniform float u_seed;\n\t\t#define M_PI 3.14159265358979323846\n\t\t\n\t\tfloat rand(vec2 c){\treturn fract(sin(dot(c.xy ,vec2( 12.9898 + u_seed,78.233 + u_seed))) * 43758.5453); }\n\t\t\n\t\tfloat noise(vec2 p, float freq ){\n\t\t\tfloat unit = u_viewport.x/freq;\n\t\t\tvec2 ij = floor(p/unit);\n\t\t\tvec2 xy = mod(p,unit)/unit;\n\t\t\t//xy = 3.*xy*xy-2.*xy*xy*xy;\n\t\t\txy = .5*(1.-cos(M_PI*xy));\n\t\t\tfloat a = rand((ij+vec2(0.,0.)));\n\t\t\tfloat b = rand((ij+vec2(1.,0.)));\n\t\t\tfloat c = rand((ij+vec2(0.,1.)));\n\t\t\tfloat d = rand((ij+vec2(1.,1.)));\n\t\t\tfloat x1 = mix(a, b, xy.x);\n\t\t\tfloat x2 = mix(c, d, xy.x);\n\t\t\treturn mix(x1, x2, xy.y);\n\t\t}\n\t\t\n\t\tfloat pNoise(vec2 p, int res){\n\t\t\tfloat persistance = u_persistence;\n\t\t\tfloat n = 0.;\n\t\t\tfloat normK = 0.;\n\t\t\tfloat f = 4.;\n\t\t\tfloat amp = 1.0;\n\t\t\tint iCount = 0;\n\t\t\tfor (int i = 0; i<50; i++){\n\t\t\t\tn+=amp*noise(p, f);\n\t\t\t\tf*=2.;\n\t\t\t\tnormK+=amp;\n\t\t\t\tamp*=persistance;\n\t\t\t\tif (iCount >= res)\n\t\t\t\t\tbreak;\n\t\t\t\tiCount++;\n\t\t\t}\n\t\t\tfloat nf = n/normK;\n\t\t\treturn nf*nf*nf*nf;\n\t\t}\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord * u_scale * u_viewport + u_offset * u_scale;\n\t\t\tvec4 color = vec4( pNoise( uv, u_octaves ) * u_amplitude );\n\t\t\tgl_FragColor = color;\n\t\t}", +this.properties.width|0,b=this.properties.height|0;0==a&&(a=gl.viewport_data[2]);0==b&&(b=gl.viewport_data[3]);var d=c.getTextureType(this.properties.precision),g=this._texture;g&&g.width==a&&g.height==b&&g.type==d||(g=this._texture=new GL.Texture(a,b,{type:d,format:gl.RGB,filter:gl.LINEAR}));var f=this.getInputOrProperty("persistence"),e=this.getInputOrProperty("octaves"),k=this.getInputOrProperty("offset"),h=this.getInputOrProperty("scale"),l=this.getInputOrProperty("amplitude"),p=this.getInputOrProperty("seed"), +d=""+a+b+d+f+e+h+p+k[0]+k[1]+l;if(d!=this._key){this._key=d;var q=this._uniforms;q.u_persistence=f;q.u_octaves=e;q.u_offset.set(k);q.u_scale=h;q.u_amplitude=l;q.u_seed=128*p;q.u_viewport[0]=a;q.u_viewport[1]=b;var r=M._shader;r||(r=M._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,M.pixel_shader));gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);g.drawTo(function(){r.uniforms(q).draw(GL.Mesh.getScreenQuad())})}this.setOutputData(0,g)}},M.pixel_shader="precision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec2 u_offset;\n\t\tuniform float u_scale;\n\t\tuniform float u_persistence;\n\t\tuniform int u_octaves;\n\t\tuniform float u_amplitude;\n\t\tuniform vec2 u_viewport;\n\t\tuniform float u_seed;\n\t\t#define M_PI 3.14159265358979323846\n\t\t\n\t\tfloat rand(vec2 c){\treturn fract(sin(dot(c.xy ,vec2( 12.9898 + u_seed,78.233 + u_seed))) * 43758.5453); }\n\t\t\n\t\tfloat noise(vec2 p, float freq ){\n\t\t\tfloat unit = u_viewport.x/freq;\n\t\t\tvec2 ij = floor(p/unit);\n\t\t\tvec2 xy = mod(p,unit)/unit;\n\t\t\t//xy = 3.*xy*xy-2.*xy*xy*xy;\n\t\t\txy = .5*(1.-cos(M_PI*xy));\n\t\t\tfloat a = rand((ij+vec2(0.,0.)));\n\t\t\tfloat b = rand((ij+vec2(1.,0.)));\n\t\t\tfloat c = rand((ij+vec2(0.,1.)));\n\t\t\tfloat d = rand((ij+vec2(1.,1.)));\n\t\t\tfloat x1 = mix(a, b, xy.x);\n\t\t\tfloat x2 = mix(c, d, xy.x);\n\t\t\treturn mix(x1, x2, xy.y);\n\t\t}\n\t\t\n\t\tfloat pNoise(vec2 p, int res){\n\t\t\tfloat persistance = u_persistence;\n\t\t\tfloat n = 0.;\n\t\t\tfloat normK = 0.;\n\t\t\tfloat f = 4.;\n\t\t\tfloat amp = 1.0;\n\t\t\tint iCount = 0;\n\t\t\tfor (int i = 0; i<50; i++){\n\t\t\t\tn+=amp*noise(p, f);\n\t\t\t\tf*=2.;\n\t\t\t\tnormK+=amp;\n\t\t\t\tamp*=persistance;\n\t\t\t\tif (iCount >= res)\n\t\t\t\t\tbreak;\n\t\t\t\tiCount++;\n\t\t\t}\n\t\t\tfloat nf = n/normK;\n\t\t\treturn nf*nf*nf*nf;\n\t\t}\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord * u_scale * u_viewport + u_offset * u_scale;\n\t\t\tvec4 color = vec4( pNoise( uv, u_octaves ) * u_amplitude );\n\t\t\tgl_FragColor = color;\n\t\t}", F.registerNodeType("texture/perlin",M),L.title="Canvas2D",L.desc="Executes Canvas2D code inside a texture or the viewport.",L.help="Set width and height to 0 to match viewport size.",L.default_code="//vars: canvas,ctx,time\nctx.fillStyle='red';\nctx.fillRect(0,0,50,50);\n",L.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES},code:{type:"code"},width:{type:"Number",precision:0,step:1},height:{type:"Number",precision:0,step:1}},L.prototype.onPropertyChanged=function(a,b){"code"==a&&this.compileCode(b)}, L.prototype.compileCode=function(a){this._func=null;if(F.allow_scripts)try{this._func=new Function("canvas","ctx","time","script","v",a),this.boxcolor="#00FF00"}catch(b){this.boxcolor="#FF0000",console.error("Error parsing script"),console.error(b)}},L.prototype.onExecute=function(){var a=this._func;a&&this.isOutputConnected(0)&&this.executeDraw(a)},L.prototype.executeDraw=function(a){var b=this.properties.width||gl.canvas.width,d=this.properties.height||gl.canvas.height,g=this._temp_texture,f=c.getTextureType(this.properties.precision); -g&&g.width==b&&g.height==d&&g.type==f||(g=this._temp_texture=new GL.Texture(b,d,{format:gl.RGBA,filter:gl.LINEAR,type:f}));var e=this.getInputData(0),k=this.properties,h=this,l=this.graph.getTime(),q=gl,p=gl.canvas;if(this.properties.use_html_canvas||!B.enableWebGLCanvas)this._canvas?(p=this._canvas,q=this._ctx):(p=this._canvas=createCanvas(b.height),q=this._ctx=p.getContext("2d")),p.width=b,p.height=d;if(q==gl)g.drawTo(function(){gl.start2D();k.clear&&(gl.clearColor(0,0,0,0),gl.clear(gl.COLOR_BUFFER_BIT)); -try{a.draw?a.draw.call(h,p,q,l,a,e):a.call(h,p,q,l,a,e),h.boxcolor="#00FF00"}catch(b){h.boxcolor="#FF0000",console.error("Error executing script"),console.error(b)}gl.finish2D()});else{k.clear&&q.clearRect(0,0,p.width,p.height);try{a.draw?a.draw.call(this,p,q,l,a,e):a.call(this,p,q,l,a,e),this.boxcolor="#00FF00"}catch(r){this.boxcolor="#FF0000",console.error("Error executing script"),console.error(r)}g.uploadImage(p)}this.setOutputData(0,g)},F.registerNodeType("texture/canvas2D",L),O.title="Matte", +g&&g.width==b&&g.height==d&&g.type==f||(g=this._temp_texture=new GL.Texture(b,d,{format:gl.RGBA,filter:gl.LINEAR,type:f}));var e=this.getInputData(0),k=this.properties,h=this,l=this.graph.getTime(),p=gl,q=gl.canvas;if(this.properties.use_html_canvas||!B.enableWebGLCanvas)this._canvas?(q=this._canvas,p=this._ctx):(q=this._canvas=createCanvas(b.height),p=this._ctx=q.getContext("2d")),q.width=b,q.height=d;if(p==gl)g.drawTo(function(){gl.start2D();k.clear&&(gl.clearColor(0,0,0,0),gl.clear(gl.COLOR_BUFFER_BIT)); +try{a.draw?a.draw.call(h,q,p,l,a,e):a.call(h,q,p,l,a,e),h.boxcolor="#00FF00"}catch(b){h.boxcolor="#FF0000",console.error("Error executing script"),console.error(b)}gl.finish2D()});else{k.clear&&p.clearRect(0,0,q.width,q.height);try{a.draw?a.draw.call(this,q,p,l,a,e):a.call(this,q,p,l,a,e),this.boxcolor="#00FF00"}catch(r){this.boxcolor="#FF0000",console.error("Error executing script"),console.error(r)}g.uploadImage(q)}this.setOutputData(0,g)},F.registerNodeType("texture/canvas2D",L),O.title="Matte", O.desc="Extracts background",O.widgets_info={key_color:{widget:"color"},precision:{widget:"combo",values:c.MODE_VALUES}},O.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else if(a){this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);this._uniforms||(this._uniforms={u_texture:0,u_key_color:this.properties.key_color,u_threshold:1, u_slope:1});var b=this._uniforms,d=Mesh.getScreenQuad(),g=O._shader;g||(g=O._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,O.pixel_shader));b.u_key_color=this.properties.key_color;b.u_threshold=this.properties.threshold;b.u_slope=this.properties.slope;this._tex.drawTo(function(){a.bind(0);g.uniforms(b).draw(d)});this.setOutputData(0,this._tex)}}},O.pixel_shader="precision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec3 u_key_color;\n\t\tuniform float u_threshold;\n\t\tuniform float u_slope;\n\t\t\n\t\tvoid main() {\n\t\t\tvec3 color = texture2D( u_texture, v_coord ).xyz;\n\t\t\tfloat diff = length( normalize(color) - normalize(u_key_color) );\n\t\t\tfloat edge = u_threshold * (1.0 - u_slope);\n\t\t\tfloat alpha = smoothstep( edge, u_threshold, diff);\n\t\t\tgl_FragColor = vec4( color, alpha );\n\t\t}", F.registerNodeType("texture/matte",O),P.title="CubemapToTexture2D",P.desc="Transforms a CUBEMAP texture into a TEXTURE2D in Polar Representation",P.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a&&a.texture_type==GL.TEXTURE_CUBE_MAP){!this._last_tex||this._last_tex.height==a.height&&this._last_tex.type==a.type||(this._last_tex=null);var b=this.getInputOrProperty("yaw");this._last_tex=GL.Texture.cubemapToTexture2D(a,a.height,this._last_tex,!0,b);this.setOutputData(0, @@ -558,46 +558,46 @@ this.addOutput("out","geometry");this.properties={};this.geometry={type:"triangl "geometry");this.addOutput("","geometry");this.properties={top_cap:!0,bottom_cap:!0,offset:[0,100,0]};this._last_geo_version=this.version=-1;this._must_update=!0}function w(){this.addInput("in","geometry");this.addOutput("out","geometry");this.properties={code:"V[1] += 0.01 * Math.sin(I + T*0.001);",execute_every_frame:!1};this.geometry=null;this.version=this.geometry_id=-1;this.must_update=!0;this.func=this.vertices=null}function u(){this.addInput("in","geometry");this.addOutput("out","geometry"); this.properties={min_dist:0.4,max_dist:0.5,max_connections:0,probability:1};this.version=this.geometry_id=-1;this.my_version=1;this.must_update=!0}function D(){this.addInput("mesh","mesh");this.addOutput("out","geometry");this.geometry={};this.last_mesh=null}function A(){this.addInput("in","geometry");this.addOutput("mesh","mesh");this.properties={};this.version=-1;this.mesh=null}function h(){this.addInput("mesh","mesh");this.addInput("mat4","mat4");this.addInput("tex","texture");this.properties= {enabled:!0,primitive:GL.TRIANGLES,additive:!1,color:[1,1,1],opacity:1};this.color=vec4.create([1,1,1,1]);this.model_matrix=mat4.create();this.uniforms={u_color:this.color,u_model:this.model_matrix}}function z(){this.addInput("size","number");this.addOutput("out","mesh");this.properties={type:1,size:1,subdivisions:32};this.version=1E5*Math.random()|0;this.last_info={type:-1,size:-1,subdivisions:-1}}function y(){this.addInput("in","geometry");this.addInput("mat4","mat4");this.addInput("tex","texture"); -this.properties={enabled:!0,point_size:0.1,fixed_size:!1,additive:!0,color:[1,1,1],opacity:1};this.color=vec4.create([1,1,1,1]);this.uniforms={u_point_size:1,u_perspective:1,u_point_perspective:1,u_color:this.color};this.version=this.geometry_id=-1;this.mesh=null}var v=B.LiteGraph,G=new Float32Array(16),q=new Float32Array(16),p=new Float32Array(16),k=new Float32Array(16),a={u_view:G,u_projection:q,u_viewprojection:p,u_model:k};v.LGraphRender={onRequestCameraMatrices:null};B.LGraphPoints3D=m;m.RECTANGLE= +this.properties={enabled:!0,point_size:0.1,fixed_size:!1,additive:!0,color:[1,1,1],opacity:1};this.color=vec4.create([1,1,1,1]);this.uniforms={u_point_size:1,u_perspective:1,u_point_perspective:1,u_color:this.color};this.version=this.geometry_id=-1;this.mesh=null}var v=B.LiteGraph,G=new Float32Array(16),p=new Float32Array(16),q=new Float32Array(16),k=new Float32Array(16),a={u_view:G,u_projection:p,u_viewprojection:q,u_model:k};v.LGraphRender={onRequestCameraMatrices:null};B.LGraphPoints3D=m;m.RECTANGLE= 1;m.CIRCLE=2;m.CUBE=10;m.SPHERE=11;m.HEMISPHERE=12;m.INSIDE_SPHERE=13;m.OBJECT=20;m.OBJECT_UNIFORMLY=21;m.OBJECT_INSIDE=22;m.MODE_VALUES={rectangle:m.RECTANGLE,circle:m.CIRCLE,cube:m.CUBE,sphere:m.SPHERE,hemisphere:m.HEMISPHERE,inside_sphere:m.INSIDE_SPHERE,object:m.OBJECT,object_uniformly:m.OBJECT_UNIFORMLY,object_inside:m.OBJECT_INSIDE};m.widgets_info={mode:{widget:"combo",values:m.MODE_VALUES}};m.title="list of points";m.desc="returns an array of points";m.prototype.onPropertyChanged=function(a, d){this.must_update=!0};m.prototype.onExecute=function(){var a=this.getInputData(0);if(a!=this._old_obj||a&&a._version!=this._old_obj_version)this._old_obj=a,this.must_update=!0;a=this.getInputData(1);null==a&&(a=this.properties.radius);this._last_radius!=a&&(this._last_radius=a,this.must_update=!0);if(this.must_update||this.properties.force_update)this.must_update=!1,this.updatePoints();this.geometry.vertices=this.points;this.geometry.normals=this.normals;this.geometry._version=this.version;this.setOutputData(0, this.geometry)};m.prototype.updatePoints=function(){var a=this.properties.num_points|0;1>a&&(a=1);this.points&&this.points.length==3*a||(this.points=new Float32Array(3*a));this.properties.generate_normals?this.normals&&this.normals.length==this.points.length||(this.normals=new Float32Array(this.points.length)):this.normals=null;var d=this._last_radius||this.properties.radius,c=this.properties.mode,f=this.getInputData(0);this._old_obj_version=f?f._version:null;this.points=m.generatePoints(d,a,c,this.points, -this.normals,this.properties.regular,f);this.version++};m.generatePoints=function(a,d,c,f,e,k,h){var l=3*d;f&&f.length==l||(f=new Float32Array(l));var q=new Float32Array(3),p=new Float32Array([0,1,0]);if(k)if(c==m.RECTANGLE){l=Math.floor(Math.sqrt(d));for(d=0;de||wk&&kh))break}this.geometry.indices=this.indices=new Uint32Array(l)}this.indices&&this.indices.length?(this.geometry.indices=this.indices,this.setOutputData(0,this.geometry)):this.setOutputData(0,null)}};v.registerNodeType("geometry/connectPoints",u);"undefined"!=typeof GL&&(D.title="to geometry",D.desc="converts a mesh to geometry",D.prototype.onExecute= +function(){var a=this.getInputData(0);if(a){if(this.geometry_id!=a._id||this.version!=a._version||this.must_update){this.must_update=!1;this.geometry_id=a._id;this.version=a._version;this.geometry={};for(var d in a)this.geometry[d]=a[d];this.geometry._id=c();this.geometry._version=this.my_version++;var a=a.vertices,g=a.length,f=this.properties.min_dist,e=this.properties.max_dist,k=this.properties.probability,h=this.properties.max_connections,p=[];for(d=0;de||wk&&kh))break}this.geometry.indices=this.indices=new Uint32Array(p)}this.indices&&this.indices.length?(this.geometry.indices=this.indices,this.setOutputData(0,this.geometry)):this.setOutputData(0,null)}};v.registerNodeType("geometry/connectPoints",u);"undefined"!=typeof GL&&(D.title="to geometry",D.desc="converts a mesh to geometry",D.prototype.onExecute= function(){var a=this.getInputData(0);if(a){if(a!=this.last_mesh){this.last_mesh=a;for(i in a.vertexBuffers)this.geometry[i]=a.vertexBuffers[i].data;a.indexBuffers.triangles&&(this.geometry.indices=a.indexBuffers.triangles.data);this.geometry._id=c();this.geometry._version=0}this.setOutputData(0,this.geometry);this.geometry&&this.setOutputData(1,this.geometry.vertices)}},v.registerNodeType("geometry/toGeometry",D),A.title="Geo to Mesh",A.prototype.updateMesh=function(a){this.mesh||(this.mesh=new GL.Mesh); for(var d in a)if("_"!=d[0]){var c=a[d],f=GL.Mesh.common_buffers[d];if(f||"indices"==d){var f=f?f.spacing:3,e=this.mesh.vertexBuffers[d];e&&e.data.length==c.length?(e.data.set(c),e.upload(GL.DYNAMIC_DRAW)):e=new GL.Buffer("indices"==d?GL.ELEMENT_ARRAY_BUFFER:GL.ARRAY_BUFFER,c,f,GL.DYNAMIC_DRAW);this.mesh.addBuffer(d,e)}}if(this.mesh.vertexBuffers.normals&&this.mesh.vertexBuffers.normals.data.length!=this.mesh.vertexBuffers.vertices.data.length){c=new Float32Array([0,1,0]);f=new Float32Array(this.mesh.vertexBuffers.vertices.data.length); for(d=0;dh[1])){var k=this.getKeyIndex(h);this.keys[k]=!0;this._last_key=k;var k=12*(this.properties.start_octave-1)+29+k,a=new c;a.setup([c.NOTEON,k,100]);this.trigger("note",a);return!0}};y.prototype.onMouseMove=function(e,h){if(!(0>h[1]||-1==this._last_key)){this.setDirtyCanvas(!0); var k=this.getKeyIndex(h);if(this._last_key==k)return!0;this.keys[this._last_key]=!1;var a=12*(this.properties.start_octave-1)+29+this._last_key,b=new c;b.setup([c.NOTEOFF,a,100]);this.trigger("note",b);this.keys[k]=!0;a=12*(this.properties.start_octave-1)+29+k;b=new c;b.setup([c.NOTEON,a,100]);this.trigger("note",b);this._last_key=k;return!0}};y.prototype.onMouseUp=function(e,h){if(!(0>h[1])){var k=this.getKeyIndex(h);this.keys[k]=!1;this._last_key=-1;var k=12*(this.properties.start_octave-1)+29+ k,a=new c;a.setup([c.NOTEOFF,k,100]);this.trigger("note",a);return!0}};v.registerNodeType("midi/keys",y)})(this); -(function(B){function c(){this.properties={src:"",gain:0.5,loop:!0,autoplay:!0,playbackRate:1};this._loading_audio=!1;this._audiobuffer=null;this._audionodes=[];this._last_sourcenode=null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=p.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain;this.properties.src&&this.loadSound(this.properties.src)}function m(){this.properties={gain:0.5};this._audionodes=[];this._media_stream= -null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=p.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain}function n(){this.properties={fftSize:2048,minDecibels:-100,maxDecibels:-10,smoothingTimeConstant:0.5};this.audionode=p.getAudioContext().createAnalyser();this.audionode.graphnode=this;this.audionode.fftSize=this.properties.fftSize;this.audionode.minDecibels=this.properties.minDecibels;this.audionode.maxDecibels= -this.properties.maxDecibels;this.audionode.smoothingTimeConstant=this.properties.smoothingTimeConstant;this.addInput("in","audio");this.addOutput("freqs","array");this.addOutput("samples","array");this._time_bin=this._freq_bin=null}function l(){this.properties={gain:1};this.audionode=p.getAudioContext().createGain();this.addInput("in","audio");this.addInput("gain","number");this.addOutput("out","audio")}function x(){this.properties={impulse_src:"",normalize:!0};this.audionode=p.getAudioContext().createConvolver(); -this.addInput("in","audio");this.addOutput("out","audio")}function e(){this.properties={threshold:-50,knee:40,ratio:12,reduction:-20,attack:0,release:0.25};this.audionode=p.getAudioContext().createDynamicsCompressor();this.addInput("in","audio");this.addOutput("out","audio")}function C(){this.properties={};this.audionode=p.getAudioContext().createWaveShaper();this.addInput("in","audio");this.addInput("shape","waveshape");this.addOutput("out","audio")}function w(){this.properties={gain1:0.5,gain2:0.5}; -this.audionode=p.getAudioContext().createGain();this.audionode1=p.getAudioContext().createGain();this.audionode1.gain.value=this.properties.gain1;this.audionode2=p.getAudioContext().createGain();this.audionode2.gain.value=this.properties.gain2;this.audionode1.connect(this.audionode);this.audionode2.connect(this.audionode);this.addInput("in1","audio");this.addInput("in1 gain","number");this.addInput("in2","audio");this.addInput("in2 gain","number");this.addOutput("out","audio")}function u(){this.properties= -{A:0.1,D:0.1,S:0.1,R:0.1};this.audionode=p.getAudioContext().createGain();this.audionode.gain.value=0;this.addInput("in","audio");this.addInput("gate","bool");this.addOutput("out","audio");this.gate=!1}function D(){this.properties={delayTime:0.5};this.audionode=p.getAudioContext().createDelay(10);this.audionode.delayTime.value=this.properties.delayTime;this.addInput("in","audio");this.addInput("time","number");this.addOutput("out","audio")}function A(){this.properties={frequency:350,detune:0,Q:1}; -this.addProperty("type","lowpass","enum",{values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")});this.audionode=p.getAudioContext().createBiquadFilter();this.addInput("in","audio");this.addOutput("out","audio")}function h(){this.properties={frequency:440,detune:0,type:"sine"};this.addProperty("type","sine","enum",{values:["sine","square","sawtooth","triangle","custom"]});this.audionode=p.getAudioContext().createOscillator();this.addOutput("out","audio")}function z(){this.properties= -{continuous:!0,mark:-1};this.addInput("data","array");this.addInput("mark","number");this.size=[300,200];this._last_buffer=null}function y(){this.properties={band:440,amplitude:1};this.addInput("freqs","array");this.addOutput("signal","number")}function v(){if(!v.default_code){var c=v.default_function.toString(),a=c.indexOf("{")+1,b=c.lastIndexOf("}");v.default_code=c.substr(a,b-a)}this.properties={code:v.default_code};c=p.getAudioContext();c.createScriptProcessor?this.audionode=c.createScriptProcessor(4096, -1,1):(console.warn("ScriptProcessorNode deprecated"),this.audionode=c.createGain());this.processCode();v._bypass_function||(v._bypass_function=this.audionode.onaudioprocess);this.addInput("in","audio");this.addOutput("out","audio")}function G(){this.audionode=p.getAudioContext().destination;this.addInput("in","audio")}var q=B.LiteGraph,p={};B.LGAudio=p;p.getAudioContext=function(){if(!this._audio_context){window.AudioContext=window.AudioContext||window.webkitAudioContext;if(!window.AudioContext)return console.error("AudioContext not supported by browser"), -null;this._audio_context=new AudioContext;this._audio_context.onmessage=function(c){console.log("msg",c)};this._audio_context.onended=function(c){console.log("ended",c)};this._audio_context.oncomplete=function(c){console.log("complete",c)}}return this._audio_context};p.connect=function(c,a){try{c.connect(a)}catch(b){console.warn("LGraphAudio:",b)}};p.disconnect=function(c,a){try{c.disconnect(a)}catch(b){console.warn("LGraphAudio:",b)}};p.changeAllAudiosConnections=function(c,a){if(c.inputs)for(var b= -0;b=this.size[0]&&(e=this.size[0]-1),c.strokeStyle="red",c.beginPath(),c.moveTo(e,d),c.lineTo(e,0),c.stroke())}};z.title="Visualization";z.desc="Audio Visualization";q.registerNodeType("audio/visualization",z);y.prototype.onExecute=function(){if(this._freqs=this.getInputData(0)){var c=this.properties.band,a=this.getInputData(1); -void 0!==a&&(c=a);a=p.getAudioContext().sampleRate/this._freqs.length;a=c/a*2;a>=this._freqs.length?a=this._freqs[this._freqs.length-1]:(c=a|0,a-=c,a=this._freqs[c]*(1-a)+this._freqs[c+1]*a);this.setOutputData(0,a/255*this.properties.amplitude)}};y.prototype.onGetInputs=function(){return[["band","number"]]};y.title="Signal";y.desc="extract the signal of some frequency";q.registerNodeType("audio/signal",y);v.prototype.onAdded=function(c){c.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess= +0;f=this.size[0]&&(e=this.size[0]-1),c.strokeStyle="red",c.beginPath(),c.moveTo(e,d),c.lineTo(e,0),c.stroke())}};z.title="Visualization";z.desc="Audio Visualization";p.registerNodeType("audio/visualization",z);y.prototype.onExecute=function(){if(this._freqs=this.getInputData(0)){var c=this.properties.band,a=this.getInputData(1); +void 0!==a&&(c=a);a=q.getAudioContext().sampleRate/this._freqs.length;a=c/a*2;a>=this._freqs.length?a=this._freqs[this._freqs.length-1]:(c=a|0,a-=c,a=this._freqs[c]*(1-a)+this._freqs[c+1]*a);this.setOutputData(0,a/255*this.properties.amplitude)}};y.prototype.onGetInputs=function(){return[["band","number"]]};y.title="Signal";y.desc="extract the signal of some frequency";p.registerNodeType("audio/signal",y);v.prototype.onAdded=function(c){c.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess= this._callback)};v["@code"]={widget:"code",type:"code"};v.prototype.onStart=function(){this.audionode.onaudioprocess=this._callback};v.prototype.onStop=function(){this.audionode.onaudioprocess=v._bypass_function};v.prototype.onPause=function(){this.audionode.onaudioprocess=v._bypass_function};v.prototype.onUnpause=function(){this.audionode.onaudioprocess=this._callback};v.prototype.onExecute=function(){};v.prototype.onRemoved=function(){this.audionode.onaudioprocess=v._bypass_function};v.prototype.processCode= function(){try{this._script=new new Function("properties",this.properties.code)(this.properties),this._old_code=this.properties.code,this._callback=this._script.onaudioprocess}catch(c){console.error("Error in onaudioprocess code",c),this._callback=v._bypass_function,this.audionode.onaudioprocess=this._callback}};v.prototype.onPropertyChanged=function(c,a){"code"==c&&(this.properties.code=a,this.processCode(),this.graph&&this.graph.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback))}; -v.default_function=function(){this.onaudioprocess=function(c){var a=c.inputBuffer;c=c.outputBuffer;for(var b=0;b Date: Thu, 23 Apr 2020 00:07:18 +0200 Subject: [PATCH 22/63] - added hook for slot context menu --- src/litegraph.d.ts | 1 + src/litegraph.js | 41 +++++++++++++++++++++++------------------ 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/litegraph.d.ts b/src/litegraph.d.ts index 69220a8df..e2c9474f5 100644 --- a/src/litegraph.d.ts +++ b/src/litegraph.d.ts @@ -932,6 +932,7 @@ export declare class LGraphNode { ): boolean; /** Called by `LGraphCanvas.processContextMenu` */ getMenuOptions?(graphCanvas: LGraphCanvas): ContextMenuItem[]; + getSlotMenuOptions?(slot: INodeSlot): ContextMenuItem[]; } export type LGraphNodeConstructor = { diff --git a/src/litegraph.js b/src/litegraph.js index 0487be1a8..895b58a46 100755 --- a/src/litegraph.js +++ b/src/litegraph.js @@ -10081,25 +10081,30 @@ LGraphNode.prototype.executeAction = function(action) if (slot) { //on slot menu_info = []; - if ( - slot && - slot.output && - slot.output.links && - slot.output.links.length - ) { - menu_info.push({ content: "Disconnect Links", slot: slot }); + if (node.getSlotMenuOptions) { + menu_info = node.getSlotMenuOptions(slot); + } else { + if ( + slot && + slot.output && + slot.output.links && + slot.output.links.length + ) { + menu_info.push({ content: "Disconnect Links", slot: slot }); + } + var _slot = slot.input || slot.output; + menu_info.push( + _slot.locked + ? "Cannot remove" + : { content: "Remove Slot", slot: slot } + ); + menu_info.push( + _slot.nameLocked + ? "Cannot rename" + : { content: "Rename Slot", slot: slot } + ); + } - var _slot = slot.input || slot.output; - menu_info.push( - _slot.locked - ? "Cannot remove" - : { content: "Remove Slot", slot: slot } - ); - menu_info.push( - _slot.nameLocked - ? "Cannot rename" - : { content: "Rename Slot", slot: slot } - ); options.title = (slot.input ? slot.input.type : slot.output.type) || "*"; if (slot.input && slot.input.type == LiteGraph.ACTION) { From c54e6ca3bb5ad13e95f4da11e3912b966afbdcc8 Mon Sep 17 00:00:00 2001 From: tamat Date: Sun, 26 Apr 2020 23:57:02 +0200 Subject: [PATCH 23/63] 0.7.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 573bfbf84..304f44dea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "litegraph.js", - "version": "0.7.5", + "version": "0.7.6", "description": "A graph node editor similar to PD or UDK Blueprints, it works in a HTML5 Canvas and allow to exported graphs to be included in applications.", "main": "build/litegraph.js", "types": "src/litegraph.d.ts", From 22190622b4bac2fd7fcab46a3cd61387c9dfebf3 Mon Sep 17 00:00:00 2001 From: tamat Date: Mon, 27 Apr 2020 00:22:24 +0200 Subject: [PATCH 24/63] fix in roundRect --- build/litegraph.js | 4 ++-- build/litegraph.min.js | 8 ++++---- src/litegraph.js | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/build/litegraph.js b/build/litegraph.js index 926530b2f..7cf799a16 100644 --- a/build/litegraph.js +++ b/build/litegraph.js @@ -10196,8 +10196,8 @@ LGraphNode.prototype.executeAction = function(action) //API ************************************************* //like rect but rounded corners - if (global.CanvasRenderingContext2D) { - global.CanvasRenderingContext2D.prototype.roundRect = function( + if (typeof(window) != "undefined" && window.CanvasRenderingContext2D) { + window.CanvasRenderingContext2D.prototype.roundRect = function( x, y, width, diff --git a/build/litegraph.min.js b/build/litegraph.min.js index 0d7846dbd..2054e16b3 100755 --- a/build/litegraph.min.js +++ b/build/litegraph.min.js @@ -228,10 +228,10 @@ callback:e.onShowMenuNodeProperties},null,{content:"Title",callback:e.onShowProp callback:e.onMenuNodeColors},{content:"Font size",property:"font_size",type:"Number",callback:e.onShowPropertyEditor},null,{content:"Remove",callback:e.onMenuNodeRemove}]};e.prototype.processContextMenu=function(a,b){var d=this,g=e.active_canvas.getCanvasWindow(),f=null,c={event:b,callback:function(b,f,g){if(b)if("Remove Slot"==b.content)b=b.slot,b.input?a.removeInput(b.slot):b.output&&a.removeOutput(b.slot);else if("Disconnect Links"==b.content)b=b.slot,b.output?a.disconnectOutput(b.slot):b.input&& a.disconnectInput(b.slot);else if("Rename Slot"==b.content){b=b.slot;var c=b.input?a.getInputInfo(b.slot):a.getOutputInfo(b.slot),h=d.createDialog("Name",f),k=h.querySelector("input");k&&c&&(k.value=c.label||"");h.querySelector("button").addEventListener("click",function(a){k.value&&(c&&(c.label=k.value),d.setDirty(!0));h.close()})}},extra:a};a&&(c.title=a.type);var k=null;a&&(k=a.getSlotInPosition(b.canvasX,b.canvasY),e.active_node= a);if(k){f=[];k&&k.output&&k.output.links&&k.output.links.length&&f.push({content:"Disconnect Links",slot:k});var p=k.input||k.output;f.push(p.locked?"Cannot remove":{content:"Remove Slot",slot:k});f.push(p.nameLocked?"Cannot rename":{content:"Rename Slot",slot:k});c.title=(k.input?k.input.type:k.output.type)||"*";k.input&&k.input.type==h.ACTION&&(c.title="Action");k.output&&k.output.type==h.EVENT&&(c.title="Event")}else a?f=this.getNodeMenuOptions(a):(f=this.getCanvasMenuOptions(),(k=this.graph.getGroupOnPos(b.canvasX, -b.canvasY))&&f.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:k,options:this.getGroupMenuOptions(k)}}));f&&new h.ContextMenu(f,c,g)};B.CanvasRenderingContext2D&&(B.CanvasRenderingContext2D.prototype.roundRect=function(a,b,d,g,f,c){void 0===f&&(f=5);void 0===c&&(c=f);this.moveTo(a+f,b);this.lineTo(a+d-f,b);this.quadraticCurveTo(a+d,b,a+d,b+f);this.lineTo(a+d,b+g-c);this.quadraticCurveTo(a+d,b+g,a+d-c,b+g);this.lineTo(a+c,b+g);this.quadraticCurveTo(a,b+g,a,b+g-c);this.lineTo(a, -b+f);this.quadraticCurveTo(a,b,a+f,b)});h.compareObjects=function(a,b){for(var d in a)if(a[d]!=b[d])return!1;return!0};h.distance=C;h.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+","+Math.round(255*a[2]).toFixed()+","+(4==a.length?a[3].toFixed(2):"1.0")+")"};h.isInsideRectangle=w;h.growBounding=function(a,b,d){ba[2]&&(a[2]=b);da[3]&&(a[3]=d)};h.isInsideBounding=function(a,b){return a[0]b[1][0]||a[1]>b[1][1]?!1:!0};h.overlapBounding=u;h.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),d=0,g,f,c=0;6>c;c+=2)g="0123456789ABCDEF".indexOf(a.charAt(c)),f="0123456789ABCDEF".indexOf(a.charAt(c+1)),b[d]=16*g+f,d++;return b};h.num2hex=function(a){for(var b="#",d,g,f=0;3>f;f++)d=a[f]/16,g=a[f]%16,b+="0123456789ABCDEF".charAt(d)+"0123456789ABCDEF".charAt(g);return b};D.prototype.addItem=function(a,b,d){function g(a){var b=this.value;b&&b.has_submenu&& -f.call(this,a)}function f(a){var b=this.value,f=!0;c.current_submenu&&c.current_submenu.close(a);if(d.callback){var g=d.callback.call(this,b,d,a,c,d.node);!0===g&&(f=!1)}if(b&&(b.callback&&!d.ignore_item_callbacks&&!0!==b.disabled&&(g=b.callback.call(this,b,d,a,c,d.extra),!0===g&&(f=!1)),b.submenu)){if(!b.submenu.options)throw"ContextMenu submenu needs options";new c.constructor(b.submenu.options,{callback:b.submenu.callback,event:a,parentMenu:c,ignore_item_callbacks:b.submenu.ignore_item_callbacks, +b.canvasY))&&f.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:k,options:this.getGroupMenuOptions(k)}}));f&&new h.ContextMenu(f,c,g)};"undefined"!=typeof window&&window.CanvasRenderingContext2D&&(window.CanvasRenderingContext2D.prototype.roundRect=function(a,b,d,g,f,c){void 0===f&&(f=5);void 0===c&&(c=f);this.moveTo(a+f,b);this.lineTo(a+d-f,b);this.quadraticCurveTo(a+d,b,a+d,b+f);this.lineTo(a+d,b+g-c);this.quadraticCurveTo(a+d,b+g,a+d-c,b+g);this.lineTo(a+c,b+g);this.quadraticCurveTo(a, +b+g,a,b+g-c);this.lineTo(a,b+f);this.quadraticCurveTo(a,b,a+f,b)});h.compareObjects=function(a,b){for(var d in a)if(a[d]!=b[d])return!1;return!0};h.distance=C;h.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+","+Math.round(255*a[2]).toFixed()+","+(4==a.length?a[3].toFixed(2):"1.0")+")"};h.isInsideRectangle=w;h.growBounding=function(a,b,d){ba[2]&&(a[2]=b);da[3]&&(a[3]=d)};h.isInsideBounding=function(a,b){return a[0]< +b[0][0]||a[1]b[1][0]||a[1]>b[1][1]?!1:!0};h.overlapBounding=u;h.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),d=0,g,f,c=0;6>c;c+=2)g="0123456789ABCDEF".indexOf(a.charAt(c)),f="0123456789ABCDEF".indexOf(a.charAt(c+1)),b[d]=16*g+f,d++;return b};h.num2hex=function(a){for(var b="#",d,g,f=0;3>f;f++)d=a[f]/16,g=a[f]%16,b+="0123456789ABCDEF".charAt(d)+"0123456789ABCDEF".charAt(g);return b};D.prototype.addItem=function(a,b,d){function g(a){var b= +this.value;b&&b.has_submenu&&f.call(this,a)}function f(a){var b=this.value,f=!0;c.current_submenu&&c.current_submenu.close(a);if(d.callback){var g=d.callback.call(this,b,d,a,c,d.node);!0===g&&(f=!1)}if(b&&(b.callback&&!d.ignore_item_callbacks&&!0!==b.disabled&&(g=b.callback.call(this,b,d,a,c,d.extra),!0===g&&(f=!1)),b.submenu)){if(!b.submenu.options)throw"ContextMenu submenu needs options";new c.constructor(b.submenu.options,{callback:b.submenu.callback,event:a,parentMenu:c,ignore_item_callbacks:b.submenu.ignore_item_callbacks, title:b.submenu.title,extra:b.submenu.extra,autoopen:d.autoopen});f=!1}f&&!c.lock&&c.close()}var c=this;d=d||{};var h=document.createElement("div");h.className="litemenu-entry submenu";var k=!1;if(null===b)h.classList.add("separator");else{h.innerHTML=b&&b.title?b.title:a;if(h.value=b)b.disabled&&(k=!0,h.classList.add("disabled")),(b.submenu||b.has_submenu)&&h.classList.add("has_submenu");"function"==typeof b?(h.dataset.value=a,h.onclick_callback=b):h.dataset.value=b;b.className&&(h.className+=" "+ b.className)}this.root.appendChild(h);k||h.addEventListener("click",f);d.autoopen&&h.addEventListener("mouseenter",g);return h};D.prototype.close=function(a,b){this.root.parentNode&&this.root.parentNode.removeChild(this.root);this.parentMenu&&!b&&(this.parentMenu.lock=!1,this.parentMenu.current_submenu=null,void 0===a?this.parentMenu.close():a&&!D.isCursorOverElement(a,this.parentMenu.root)&&D.trigger(this.parentMenu.root,"mouseleave",a));this.current_submenu&&this.current_submenu.close(a,!0);this.root.closing_timer&& clearTimeout(this.root.closing_timer)};D.trigger=function(a,b,d,g){var f=document.createEvent("CustomEvent");f.initCustomEvent(b,!0,!0,d);f.srcElement=g;a.dispatchEvent?a.dispatchEvent(f):a.__events&&a.__events.dispatchEvent(f);return f};D.prototype.getTopMenu=function(){return this.options.parentMenu?this.options.parentMenu.getTopMenu():this};D.prototype.getFirstEvent=function(){return this.options.parentMenu?this.options.parentMenu.getFirstEvent():this.options.event};D.isCursorOverElement=function(a, diff --git a/src/litegraph.js b/src/litegraph.js index 0487be1a8..12193f7a2 100755 --- a/src/litegraph.js +++ b/src/litegraph.js @@ -10194,8 +10194,8 @@ LGraphNode.prototype.executeAction = function(action) //API ************************************************* //like rect but rounded corners - if (global.CanvasRenderingContext2D) { - global.CanvasRenderingContext2D.prototype.roundRect = function( + if (typeof(window) != "undefined" && window.CanvasRenderingContext2D) { + window.CanvasRenderingContext2D.prototype.roundRect = function( x, y, width, From 30e78c5d10d9d1b147043f734d2d22630b33dcef Mon Sep 17 00:00:00 2001 From: tamat Date: Mon, 27 Apr 2020 00:22:46 +0200 Subject: [PATCH 25/63] 0.7.7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 304f44dea..58aa2554d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "litegraph.js", - "version": "0.7.6", + "version": "0.7.7", "description": "A graph node editor similar to PD or UDK Blueprints, it works in a HTML5 Canvas and allow to exported graphs to be included in applications.", "main": "build/litegraph.js", "types": "src/litegraph.d.ts", From 26a72a0a4481fc6bcf881658d06f7e28d9773211 Mon Sep 17 00:00:00 2001 From: tamat Date: Sun, 3 May 2020 13:47:12 +0200 Subject: [PATCH 26/63] added onResize event to nodes --- build/litegraph.js | 10 +- build/litegraph.min.js | 469 +++++++++++++++++++++-------------------- src/litegraph.js | 10 +- 3 files changed, 251 insertions(+), 238 deletions(-) diff --git a/build/litegraph.js b/build/litegraph.js index 7cf799a16..e4e226921 100644 --- a/build/litegraph.js +++ b/build/litegraph.js @@ -5581,6 +5581,10 @@ LGraphNode.prototype.executeAction = function(action) this.resizing_node.size[0] = LiteGraph.NODE_MIN_WIDTH; } + if (this.resizing_node.onResize) { + this.resizing_node.onResize(this.resizing_node.size); + } + this.canvas.style.cursor = "se-resize"; this.dirty_canvas = true; this.dirty_bgcanvas = true; @@ -10539,11 +10543,13 @@ LGraphNode.prototype.executeAction = function(action) var body_rect = document.body.getBoundingClientRect(); var root_rect = root.getBoundingClientRect(); + if(body_rect.height == 0) + console.error("document.body height is 0. That is dangerous, set html,body { height: 100%; }"); - if (left > body_rect.width - root_rect.width - 10) { + if (body_rect.width && left > body_rect.width - root_rect.width - 10) { left = body_rect.width - root_rect.width - 10; } - if (top > body_rect.height - root_rect.height - 10) { + if (body_rect.height && top > body_rect.height - root_rect.height - 10) { top = body_rect.height - root_rect.height - 10; } } diff --git a/build/litegraph.min.js b/build/litegraph.min.js index 2054e16b3..92bc038e6 100755 --- a/build/litegraph.min.js +++ b/build/litegraph.min.js @@ -8,240 +8,241 @@ this.visible_links=[];b&&b.attachCanvas(this);this.setCanvas(a);this.clear();d.s b.event=null);var r=document.createElement("div");r.className="litegraph litecontextmenu litemenubar-panel";b.className&&(r.className+=" "+b.className);r.style.minWidth=100;r.style.minHeight=100;r.style.pointerEvents="none";setTimeout(function(){r.style.pointerEvents="auto"},100);r.addEventListener("mouseup",function(a){a.preventDefault();return!0},!0);r.addEventListener("contextmenu",function(a){if(2!=a.button)return!1;a.preventDefault();return!1},!0);r.addEventListener("mousedown",function(a){if(2== a.button)return g.close(),a.preventDefault(),!0},!0);b.scroll_speed||(b.scroll_speed=0.1);r.addEventListener("wheel",d,!0);r.addEventListener("mousewheel",d,!0);this.root=r;b.title&&(f=document.createElement("div"),f.className="litemenu-title",f.innerHTML=b.title,r.appendChild(f));var f=0,h;for(h in a){var c=a.constructor==Array?a[h]:h;null!=c&&c.constructor!==String&&(c=void 0===c.content?String(c):c.content);this.addItem(c,a[h],b);f++}r.addEventListener("mouseleave",function(a){g.lock||(r.closing_timer&& clearTimeout(r.closing_timer),r.closing_timer=setTimeout(g.close.bind(g,a),500))});r.addEventListener("mouseenter",function(a){r.closing_timer&&clearTimeout(r.closing_timer)});h=document;b.event&&(h=b.event.target.ownerDocument);h||(h=document);h.fullscreenElement?h.fullscreenElement.appendChild(r):h.body.appendChild(r);f=b.left||0;h=b.top||0;if(b.event){f=b.event.clientX-10;h=b.event.clientY-10;b.title&&(h-=20);b.parentMenu&&(f=b.parentMenu.root.getBoundingClientRect(),f=f.left+f.width);var c=document.body.getBoundingClientRect(), -k=r.getBoundingClientRect();f>c.width-k.width-10&&(f=c.width-k.width-10);h>c.height-k.height-10&&(h=c.height-k.height-10)}r.style.left=f+"px";r.style.top=h+"px";b.scale&&(r.style.transform="scale("+b.scale+")")}function A(a){this.points=a;this.nearest=this.selected=-1;this.size=null;this.must_update=!0;this.margin=5}var h=B.LiteGraph={VERSION:0.4,CANVAS_GRID_SIZE:10,NODE_TITLE_HEIGHT:30,NODE_TITLE_TEXT_Y:20,NODE_SLOT_HEIGHT:20,NODE_WIDGET_HEIGHT:20,NODE_WIDTH:140,NODE_MIN_WIDTH:50,NODE_COLLAPSED_RADIUS:10, -NODE_COLLAPSED_WIDTH:80,NODE_TITLE_COLOR:"#999",NODE_TEXT_SIZE:14,NODE_TEXT_COLOR:"#AAA",NODE_SUBTEXT_SIZE:12,NODE_DEFAULT_COLOR:"#333",NODE_DEFAULT_BGCOLOR:"#353535",NODE_DEFAULT_BOXCOLOR:"#666",NODE_DEFAULT_SHAPE:"box",DEFAULT_SHADOW_COLOR:"rgba(0,0,0,0.5)",DEFAULT_GROUP_FONT:24,WIDGET_BGCOLOR:"#222",WIDGET_OUTLINE_COLOR:"#666",WIDGET_TEXT_COLOR:"#DDD",WIDGET_SECONDARY_TEXT_COLOR:"#999",LINK_COLOR:"#9A9",EVENT_LINK_COLOR:"#A86",CONNECTING_LINK_COLOR:"#AFA",MAX_NUMBER_OF_NODES:1E3,DEFAULT_POSITION:[100, -100],VALID_SHAPES:["default","box","round","card"],BOX_SHAPE:1,ROUND_SHAPE:2,CIRCLE_SHAPE:3,CARD_SHAPE:4,ARROW_SHAPE:5,INPUT:1,OUTPUT:2,EVENT:-1,ACTION:-1,ALWAYS:0,ON_EVENT:1,NEVER:2,ON_TRIGGER:3,UP:1,DOWN:2,LEFT:3,RIGHT:4,CENTER:5,STRAIGHT_LINK:0,LINEAR_LINK:1,SPLINE_LINK:2,NORMAL_TITLE:0,NO_TITLE:1,TRANSPARENT_TITLE:2,AUTOHIDE_TITLE:3,proxy:null,node_images_path:"",debug:!1,catch_exceptions:!0,throw_errors:!0,allow_scripts:!1,registered_node_types:{},node_types_by_file_extension:{},Nodes:{},searchbox_extras:{}, -registerNodeType:function(a,b){if(!b.prototype)throw"Cannot register a simple object, it must be a class with a prototype";b.type=a;h.debug&&console.log("Node registered: "+a);a.split("/");var d=b.name,g=a.lastIndexOf("/");b.category=a.substr(0,g);b.title||(b.title=d);if(b.prototype)for(var f in n.prototype)b.prototype[f]||(b.prototype[f]=n.prototype[f]);if(g=this.registered_node_types[a])console.log("replacing node type: "+a);else if(Object.hasOwnProperty(b.prototype,"shape")||Object.defineProperty(b.prototype, -"shape",{set:function(a){switch(a){case "default":delete this._shape;break;case "box":this._shape=h.BOX_SHAPE;break;case "round":this._shape=h.ROUND_SHAPE;break;case "circle":this._shape=h.CIRCLE_SHAPE;break;case "card":this._shape=h.CARD_SHAPE;break;default:this._shape=a}},get:function(a){return this._shape},enumerable:!0,configurable:!0}),b.prototype.onPropertyChange&&console.warn("LiteGraph node class "+a+" has onPropertyChange method, it must be called onPropertyChanged with d at the end"),b.supported_extensions)for(f in b.supported_extensions){var r= -b.supported_extensions[f];r&&r.constructor===String&&(this.node_types_by_file_extension[r.toLowerCase()]=b)}this.registered_node_types[a]=b;b.constructor.name&&(this.Nodes[d]=b);if(h.onNodeTypeRegistered)h.onNodeTypeRegistered(a,b);if(g&&h.onNodeTypeReplaced)h.onNodeTypeReplaced(a,b,g)},unregisterNodeType:function(a){var b=a.constructor===String?this.registered_node_types[a]:a;if(!b)throw"node type not found: "+a;delete this.registered_node_types[b.type];b.constructor.name&&delete this.Nodes[b.constructor.name]}, -wrapFunctionAsNode:function(a,b,d,g,f){for(var r=Array(b.length),s="",c=h.getParameterNames(b),k=0;ks&&(s=f.size[0]),c+=f.size[1]+a+h.NODE_TITLE_HEIGHT;b+=s+a}this.setDirtyCanvas(!0,!0)};c.prototype.getTime=function(){return this.globaltime};c.prototype.getFixedTime=function(){return this.fixedtime};c.prototype.getElapsedTime=function(){return this.elapsed_time};c.prototype.sendEventToAllNodes=function(a,b,d){d=d||h.ALWAYS;var g=this._nodes_in_order? -this._nodes_in_order:this._nodes;if(g)for(var f=0,r=g.length;f=h.MAX_NUMBER_OF_NODES)throw"LiteGraph: max number of nodes in a graph reached";null==a.id||-1==a.id?a.id=++this.last_node_id:this.last_node_ida.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos}, -enumerable:!0});this.id=-1;this.type=null;this.inputs=[];this.outputs=[];this.connections=[];this.properties={};this.properties_info=[];this.flags={}};n.prototype.configure=function(a){this.graph&&this.graph._version++;for(var b in a)if("properties"==b)for(var d in a.properties){if(this.properties[d]=a.properties[d],this.onPropertyChanged)this.onPropertyChanged(d,a.properties[d])}else null!=a[b]&&("object"==typeof a[b]?this[b]&&this[b].configure?this[b].configure(a[b]):this[b]=h.cloneObject(a[b], -this[b]):this[b]=a[b]);a.title||(this.title=this.constructor.title);if(this.onConnectionsChange){if(this.inputs)for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d._data=b,this.outputs[a].links))for(d=0;d=this.outputs.length)){var d=this.outputs[a]; -if(d&&(d.type=b,this.outputs[a].links))for(d=0;d=this.inputs.length||null==this.inputs[a].link)){var d=this.graph.links[this.inputs[a].link];if(!d)return null;if(!b)return d.data;var g=this.graph.getNodeById(d.origin_id);if(!g)return d.data;if(g.updateOutputData)g.updateOutputData(d.origin_slot);else if(g.onExecute)g.onExecute();return d.data}};n.prototype.getInputDataType= -function(a){if(!this.inputs||a>=this.inputs.length||null==this.inputs[a].link)return null;a=this.graph.links[this.inputs[a].link];if(!a)return null;var b=this.graph.getNodeById(a.origin_id);return b?(a=b.outputs[a.origin_slot])?a.type:null:a.type};n.prototype.getInputDataByName=function(a,b){var d=this.findInputSlot(a);return-1==d?null:this.getInputData(d,b)};n.prototype.isInputConnected=function(a){return this.inputs?a=this.inputs.length)return null;a=this.inputs[a];return a&&null!==a.link?(a=this.graph.links[a.link])?this.graph.getNodeById(a.origin_id):null:null};n.prototype.getInputOrProperty=function(a){if(!this.inputs||!this.inputs.length)return this.properties?this.properties[a]:null;for(var b=0,d=this.inputs.length;b=this.outputs.length?null:this.outputs[a]._data};n.prototype.getOutputInfo=function(a){return this.outputs?a=this.outputs.length)return null;a=this.outputs[a];if(!a.links||0==a.links.length)return null;for(var b=[],d=0;da&&this.pos[1]-f-db)return!0;return!1};n.prototype.getSlotInPosition=function(a,b){var d=new Float32Array(2);if(this.inputs)for(var g=0,f=this.inputs.length;g=this.outputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),null;b&&b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"target node is null";if(b==this)return null;if(d.constructor===String){if(d=b.findInputSlot(d),-1==d)return h.debug&&console.log("Connect: Error, no slot of name "+d),null}else{if(d=== -h.EVENT)return null;if(!b.inputs||d>=b.inputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),null}null!=b.inputs[d].link&&b.disconnectInput(d);var g=this.outputs[a];if(b.onConnectInput&&!1===b.onConnectInput(d,g.type,g))return null;var f=b.inputs[d],r=null;if(h.isValidConnection(g.type,f.type)){r=new m(++this.graph.last_link_id,f.type,this.id,a,b.id,d);this.graph.links[r.id]=r;null==g.links&&(g.links=[]);g.links.push(r.id);b.inputs[d].link=r.id;this.graph&&this.graph._version++; -if(this.onConnectionsChange)this.onConnectionsChange(h.OUTPUT,a,!0,r,g);if(b.onConnectionsChange)b.onConnectionsChange(h.INPUT,d,!0,r,f);this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(h.INPUT,b,d,this,a),this.graph.onNodeConnectionChange(h.OUTPUT,this,a,b,d))}this.setDirtyCanvas(!1,!0);this.graph.connectionChange(this,r);return r};n.prototype.disconnectOutput=function(a,b){if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return h.debug&&console.log("Connect: Error, no slot of name "+ -a),!1}else if(!this.outputs||a>=this.outputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),!1;var d=this.outputs[a];if(!d||!d.links||0==d.links.length)return!1;if(b){b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"Target Node not found";for(var g=0,f=d.links.length;g=this.inputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),!1;var b=this.inputs[a];if(!b)return!1;var d=this.inputs[a].link;this.inputs[a].link=null;var g=this.graph.links[d];if(g){var f= -this.graph.getNodeById(g.origin_id);if(!f)return!1;var r=f.outputs[g.origin_slot];if(!r||!r.links||0==r.links.length)return!1;for(var s=0,c=r.links.length;sb&&this.inputs[b].pos)return d[0]=this.pos[0]+this.inputs[b].pos[0],d[1]=this.pos[1]+this.inputs[b].pos[1],d;if(!a&&g>b&&this.outputs[b].pos)return d[0]=this.pos[0]+this.outputs[b].pos[0],d[1]=this.pos[1]+this.outputs[b].pos[1],d;if(this.horizontal)return d[0]=this.pos[0]+this.size[0]/g*(b+0.5),d[1]=a?this.pos[1]-h.NODE_TITLE_HEIGHT: -this.pos[1]+this.size[1],d;d[0]=a?this.pos[0]+f:this.pos[0]+this.size[0]+1-f;d[1]=this.pos[1]+(b+0.7)*h.NODE_SLOT_HEIGHT+(this.constructor.slot_start_y||0);return d};n.prototype.alignToGrid=function(){this.pos[0]=h.CANVAS_GRID_SIZE*Math.round(this.pos[0]/h.CANVAS_GRID_SIZE);this.pos[1]=h.CANVAS_GRID_SIZE*Math.round(this.pos[1]/h.CANVAS_GRID_SIZE)};n.prototype.trace=function(a){this.console||(this.console=[]);this.console.push(a);this.console.length>n.MAX_CONSOLE&&this.console.shift();this.graph.onNodeTrace(this, -a)};n.prototype.setDirtyCanvas=function(a,b){this.graph&&this.graph.sendActionToCanvas("setDirty",[a,b])};n.prototype.loadImage=function(a){var b=new Image;b.src=h.node_images_path+a;b.ready=!1;var d=this;b.onload=function(){this.ready=!0;d.setDirtyCanvas(!0)};return b};n.prototype.captureInput=function(a){if(this.graph&&this.graph.list_of_graphcanvas)for(var b=this.graph.list_of_graphcanvas,d=0;da.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});Object.defineProperty(this,"size",{set:function(a){!a||2>a.length||(this._size[0]=Math.max(140,a[0]),this._size[1]=Math.max(80,a[1]))},get:function(){return this._size}, -enumerable:!0})};l.prototype.configure=function(a){this.title=a.title;this._bounding.set(a.bounding);this.color=a.color;this.font=a.font};l.prototype.serialize=function(){var a=this._bounding;return{title:this.title,bounding:[Math.round(a[0]),Math.round(a[1]),Math.round(a[2]),Math.round(a[3])],color:this.color,font:this.font}};l.prototype.move=function(a,b,d){this._pos[0]+=a;this._pos[1]+=b;if(!d)for(d=0;dthis.max_scale&&(a=this.max_scale);if(a!=this.scale&&this.element){var d=this.element.getBoundingClientRect();if(d){b=b||[0.5*d.width,0.5*d.height];d=this.convertCanvasToOffset(b);this.scale=a;0.01>Math.abs(this.scale-1)&&(this.scale= -1);var g=this.convertCanvasToOffset(b),d=[g[0]-d[0],g[1]-d[1]];this.offset[0]+=d[0];this.offset[1]+=d[1];if(this.onredraw)this.onredraw(this)}}};x.prototype.changeDeltaScale=function(a,b){this.changeScale(this.scale*a,b)};x.prototype.reset=function(){this.scale=1;this.offset[0]=0;this.offset[1]=0};B.LGraphCanvas=h.LGraphCanvas=e;e.link_type_colors={"-1":h.EVENT_LINK_COLOR,number:"#AAA",node:"#DCA"};e.gradients={};e.prototype.clear=function(){this.fps=this.render_time=this.last_draw_time=this.frame= -0;this.dragging_rectangle=null;this.selected_nodes={};this.selected_group=null;this.visible_nodes=[];this.connecting_node=this.node_capturing_input=this.node_over=this.node_dragged=null;this.highlighted_links={};this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_widget=this.node_in_panel=this.dirty_area=null;this.last_mouse=[0,0];this.last_mouseclick=0;this.visible_area.set([0,0,0,0]);if(this.onClear)this.onClear()};e.prototype.setGraph=function(a,b){this.graph!=a&&(b||this.clear(),!a&&this.graph? -this.graph.detachCanvas(this):(a.attachCanvas(this),this._graph_stack&&(this._graph_stack=null),this.setDirty(!0,!0)))};e.prototype.openSubgraph=function(a){if(!a)throw"graph cannot be null";if(this.graph==a)throw"graph cannot be the same";this.clear();this.graph&&(this._graph_stack||(this._graph_stack=[]),this._graph_stack.push(this.graph));a.attachCanvas(this);this.setDirty(!0,!0)};e.prototype.closeSubgraph=function(){if(this._graph_stack&&0!=this._graph_stack.length){var a=this.graph._subgraph_node, -b=this._graph_stack.pop();this.selected_nodes={};this.highlighted_links={};b.attachCanvas(this);this.setDirty(!0,!0);a&&(this.centerOnNode(a),this.selectNodes([a]))}};e.prototype.getCurrentGraph=function(){return this.graph};e.prototype.setCanvas=function(a,b){if(a&&a.constructor===String&&(a=document.getElementById(a),!a))throw"Error creating LiteGraph canvas: Canvas not found";if(a!==this.canvas&&(!a&&this.canvas&&(b||this.unbindEvents()),this.canvas=a,this.ds.element=a)){a.className+=" lgraphcanvas"; -a.data=this;a.tabindex="1";this.bgcanvas=null;this.bgcanvas||(this.bgcanvas=document.createElement("canvas"),this.bgcanvas.width=this.canvas.width,this.bgcanvas.height=this.canvas.height);if(null==a.getContext){if("canvas"!=a.localName)throw"Element supplied for LGraphCanvas must be a element, you passed a "+a.localName;throw"This browser doesn't support Canvas";}null==(this.ctx=a.getContext("2d"))&&(a.webgl_enabled||console.warn("This canvas seems to be WebGL, enabling WebGL renderer"), -this.enableWebGL());this._mousemove_callback=this.processMouseMove.bind(this);this._mouseup_callback=this.processMouseUp.bind(this);b||this.bindEvents()}};e.prototype._doNothing=function(a){a.preventDefault();return!1};e.prototype._doReturnTrue=function(a){a.preventDefault();return!0};e.prototype.bindEvents=function(){if(this._events_binded)console.warn("LGraphCanvas: events already binded");else{var a=this.canvas,b=this.getCanvasWindow().document;this._mousedown_callback=this.processMouseDown.bind(this); -this._mousewheel_callback=this.processMouseWheel.bind(this);a.addEventListener("mousedown",this._mousedown_callback,!0);a.addEventListener("mousemove",this._mousemove_callback);a.addEventListener("mousewheel",this._mousewheel_callback,!1);a.addEventListener("contextmenu",this._doNothing);a.addEventListener("DOMMouseScroll",this._mousewheel_callback,!1);a.addEventListener("touchstart",this.touchHandler,!0);a.addEventListener("touchmove",this.touchHandler,!0);a.addEventListener("touchend",this.touchHandler, -!0);a.addEventListener("touchcancel",this.touchHandler,!0);this._key_callback=this.processKey.bind(this);a.addEventListener("keydown",this._key_callback,!0);b.addEventListener("keyup",this._key_callback,!0);this._ondrop_callback=this.processDrop.bind(this);a.addEventListener("dragover",this._doNothing,!1);a.addEventListener("dragend",this._doNothing,!1);a.addEventListener("drop",this._ondrop_callback,!1);a.addEventListener("dragenter",this._doReturnTrue,!1);this._events_binded=!0}};e.prototype.unbindEvents= -function(){if(this._events_binded){var a=this.getCanvasWindow().document;this.canvas.removeEventListener("mousedown",this._mousedown_callback);this.canvas.removeEventListener("mousewheel",this._mousewheel_callback);this.canvas.removeEventListener("DOMMouseScroll",this._mousewheel_callback);this.canvas.removeEventListener("keydown",this._key_callback);a.removeEventListener("keyup",this._key_callback);this.canvas.removeEventListener("contextmenu",this._doNothing);this.canvas.removeEventListener("drop", -this._ondrop_callback);this.canvas.removeEventListener("dragenter",this._doReturnTrue);this.canvas.removeEventListener("touchstart",this.touchHandler);this.canvas.removeEventListener("touchmove",this.touchHandler);this.canvas.removeEventListener("touchend",this.touchHandler);this.canvas.removeEventListener("touchcancel",this.touchHandler);this._ondrop_callback=this._key_callback=this._mousewheel_callback=this._mousedown_callback=null;this._events_binded=!1}else console.warn("LGraphCanvas: no events binded")}; -e.getFileExtension=function(a){var b=a.indexOf("?");-1!=b&&(a=a.substr(0,b));b=a.lastIndexOf(".");return-1==b?"":a.substr(b+1).toLowerCase()};e.prototype.enableWebGL=function(){if(void 0===typeof GL)throw"litegl.js must be included to use a WebGL canvas";if(void 0===typeof enableWebGLCanvas)throw"webglCanvas.js must be included to use this feature";this.gl=this.ctx=enableWebGLCanvas(this.canvas);this.ctx.webgl=!0;this.bgcanvas=this.canvas;this.bgctx=this.gl;this.canvas.webgl_enabled=!0};e.prototype.setDirty= -function(a,b){a&&(this.dirty_canvas=!0);b&&(this.dirty_bgcanvas=!0)};e.prototype.getCanvasWindow=function(){if(!this.canvas)return window;var a=this.canvas.ownerDocument;return a.defaultView||a.parentWindow};e.prototype.startRendering=function(){function a(){this.pause_rendering||this.draw();var b=this.getCanvasWindow();this.is_rendering&&b.requestAnimationFrame(a.bind(this))}this.is_rendering||(this.is_rendering=!0,a.call(this))};e.prototype.stopRendering=function(){this.is_rendering=!1};e.prototype.processMouseDown= -function(a){if(this.graph){this.adjustMouseEvent(a);var b=this.getCanvasWindow();e.active_canvas=this;this.canvas.removeEventListener("mousemove",this._mousemove_callback);b.document.addEventListener("mousemove",this._mousemove_callback,!0);b.document.addEventListener("mouseup",this._mouseup_callback,!0);var d=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes,5),g=!1,f=300>h.getTime()-this.last_mouseclick;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;this.canvas.focus(); -h.closeAllContextMenus(b);if(!this.onMouse||!0!=this.onMouse(a)){if(1==a.which){a.ctrlKey&&(this.dragging_rectangle=new Float32Array(4),this.dragging_rectangle[0]=a.canvasX,this.dragging_rectangle[1]=a.canvasY,this.dragging_rectangle[2]=1,this.dragging_rectangle[3]=1,g=!0);var r=!1;if(d&&this.allow_interaction&&!g&&!this.read_only){this.live_mode||d.flags.pinned||this.bringToFront(d);if(!this.connecting_node&&!d.flags.collapsed&&!this.live_mode)if(!g&&!1!==d.resizable&&w(a.canvasX,a.canvasY,d.pos[0]+ -d.size[0]-5,d.pos[1]+d.size[1]-5,10,10))this.resizing_node=d,this.canvas.style.cursor="se-resize",g=!0;else{if(d.outputs)for(var s=0,c=d.outputs.length;sr[0]+4||a.canvasYr[1]+4)){this.showLinkMenu(d,a);this.over_link_center=null;break}this.selected_group=this.graph.getGroupOnPos(a.canvasX,a.canvasY);this.selected_group_resizing=!1;this.selected_group&&!this.read_only&&(a.ctrlKey&&(this.dragging_rectangle=null),10>C([a.canvasX, -a.canvasY],[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])*this.ds.scale?this.selected_group_resizing=!0:this.selected_group.recomputeInsideNodes());f&&!this.read_only&&this.allow_searchbox&&this.showSearchBox(a);r=!0}!g&&r&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}else 2!=a.which&&3==a.which&&(this.read_only||this.processContextMenu(d,a));this.last_mouse[0]=a.localX;this.last_mouse[1]=a.localY;this.last_mouseclick=h.getTime(); -this.last_mouse_dragging=!0;this.graph.change();(!b.document.activeElement||"input"!=b.document.activeElement.nodeName.toLowerCase()&&"textarea"!=b.document.activeElement.nodeName.toLowerCase())&&a.preventDefault();a.stopPropagation();if(this.onMouseDown)this.onMouseDown(a);return!1}}};e.prototype.processMouseMove=function(a){this.autoresize&&this.resize();if(this.graph){e.active_canvas=this;this.adjustMouseEvent(a);var b=[a.localX,a.localY],d=[b[0]-this.last_mouse[0],b[1]-this.last_mouse[1]];this.last_mouse= -b;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;a.dragging=this.last_mouse_dragging;this.node_widget&&(this.processNodeWidgets(this.node_widget[0],this.canvas_mouse,a,this.node_widget[1]),this.dirty_canvas=!0);if(this.dragging_rectangle)this.dragging_rectangle[2]=a.canvasX-this.dragging_rectangle[0],this.dragging_rectangle[3]=a.canvasY-this.dragging_rectangle[1],this.dirty_canvas=!0;else if(this.selected_group&&!this.read_only)this.selected_group_resizing?this.selected_group.size= -[a.canvasX-this.selected_group.pos[0],a.canvasY-this.selected_group.pos[1]]:(this.selected_group.move(d[0]/this.ds.scale,d[1]/this.ds.scale,a.ctrlKey),this.selected_group._nodes.length&&(this.dirty_canvas=!0)),this.dirty_bgcanvas=!0;else if(this.dragging_canvas)this.ds.offset[0]+=d[0]/this.ds.scale,this.ds.offset[1]+=d[1]/this.ds.scale,this.dirty_bgcanvas=this.dirty_canvas=!0;else if(this.allow_interaction&&!this.read_only){this.connecting_node&&(this.dirty_canvas=!0);for(var g=this.graph.getNodeOnPos(a.canvasX, -a.canvasY,this.visible_nodes),b=0,f=this.graph._nodes.length;bs[0]+4||a.canvasYs[1]+4)){f=r;break}}f!=this.over_link_center&&(this.over_link_center=f,this.dirty_canvas=!0);this.canvas&&(this.canvas.style.cursor="")}if(this.node_capturing_input&&this.node_capturing_input!=g&&this.node_capturing_input.onMouseMove)this.node_capturing_input.onMouseMove(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]],this);if(this.node_dragged&&!this.live_mode){for(b in this.selected_nodes)g=this.selected_nodes[b], -g.pos[0]+=d[0]/this.ds.scale,g.pos[1]+=d[1]/this.ds.scale;this.dirty_bgcanvas=this.dirty_canvas=!0}this.resizing_node&&!this.live_mode&&(this.resizing_node.size[0]=a.canvasX-this.resizing_node.pos[0],this.resizing_node.size[1]=a.canvasY-this.resizing_node.pos[1],d=Math.max(this.resizing_node.inputs?this.resizing_node.inputs.length:0,this.resizing_node.outputs?this.resizing_node.outputs.length:0)*h.NODE_SLOT_HEIGHT+(this.resizing_node.widgets?this.resizing_node.widgets.length:0)*(h.NODE_WIDGET_HEIGHT+ -4)+4,this.resizing_node.size[1]this.dragging_rectangle[3]?this.dragging_rectangle[1]-f:this.dragging_rectangle[1];this.dragging_rectangle[0]= -0>this.dragging_rectangle[2]?this.dragging_rectangle[0]-g:this.dragging_rectangle[0];this.dragging_rectangle[1]=r;this.dragging_rectangle[2]=g;this.dragging_rectangle[3]=f;f=[];for(r=0;ra.click_time&&w(a.canvasX,a.canvasY,g.pos[0],g.pos[1]-h.NODE_TITLE_HEIGHT,h.NODE_TITLE_HEIGHT,h.NODE_TITLE_HEIGHT)&&g.collapse();this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_dragged.pos[0]=Math.round(this.node_dragged.pos[0]);this.node_dragged.pos[1]=Math.round(this.node_dragged.pos[1]);this.graph.config.align_to_grid&& -this.node_dragged.alignToGrid();if(this.onNodeMoved)this.onNodeMoved(this.node_dragged);this.node_dragged=null}else{g=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes);!g&&300>a.click_time&&this.deselectAllNodes();this.dirty_canvas=!0;this.dragging_canvas=!1;if(this.node_over&&this.node_over.onMouseUp)this.node_over.onMouseUp(a,[a.canvasX-this.node_over.pos[0],a.canvasY-this.node_over.pos[1]],this);if(this.node_capturing_input&&this.node_capturing_input.onMouseUp)this.node_capturing_input.onMouseUp(a, -[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]])}}else 2==a.which?(this.dirty_canvas=!0,this.dragging_canvas=!1):3==a.which&&(this.dirty_canvas=!0,this.dragging_canvas=!1);this.graph.change();a.stopPropagation();a.preventDefault();return!1}};e.prototype.processMouseWheel=function(a){if(this.graph&&this.allow_dragcanvas){var b=null!=a.wheelDeltaY?a.wheelDeltaY:-60*a.detail;this.adjustMouseEvent(a);var d=this.ds.scale;0b&&(d*=1/1.1);this.ds.changeScale(d, -[a.localX,a.localY]);this.graph.change();a.preventDefault();return!1}};e.prototype.isOverNodeBox=function(a,b,d){var g=h.NODE_TITLE_HEIGHT;return w(b,d,a.pos[0]+2,a.pos[1]+2-g,g-4,g-4)?!0:!1};e.prototype.isOverNodeInput=function(a,b,d,g){if(a.inputs)for(var f=0,h=a.inputs.length;fd- -this.graph._last_trigger_time)&&this.drawBackCanvas();(this.dirty_canvas||a)&&this.drawFrontCanvas();this.fps=this.render_time?1/this.render_time:0;this.frame+=1}};e.prototype.drawFrontCanvas=function(){this.dirty_canvas=!1;this.ctx||(this.ctx=this.bgcanvas.getContext("2d"));var a=this.ctx;if(a){a.start2D&&a.start2D();var b=this.canvas;a.restore();a.setTransform(1,0,0,1,0,0);this.dirty_area&&(a.save(),a.beginPath(),a.rect(this.dirty_area[0],this.dirty_area[1],this.dirty_area[2],this.dirty_area[3]), -a.clip());this.clear_background&&a.clearRect(0,0,b.width,b.height);this.bgcanvas==this.canvas?this.drawBackCanvas():a.drawImage(this.bgcanvas,0,0);if(this.onRender)this.onRender(b,a);this.show_info&&this.renderInfo(a);if(this.graph){a.save();this.ds.toCanvasContext(a);for(var b=this.computeVisibleNodes(null,this.visible_nodes),d=0;d> ";b.fillText(g+d.getTitle(),0.5*a.width,40);b.restore()}d=!1;this.onRenderBackground&&(d=this.onRenderBackground(a,b));b.restore(); -b.setTransform(1,0,0,1,0,0);this.visible_links.length=0;if(this.graph){b.save();this.ds.toCanvasContext(b);if(this.background_image&&0.5this.ds.scale;if(this.live_mode){if(!a.flags.collapsed&&(b.shadowColor="transparent",a.onDrawForeground))a.onDrawForeground(b,this,this.canvas)}else{var r=this.editor_alpha;b.globalAlpha=r;this.render_shadows&&!f?(b.shadowColor= -h.DEFAULT_SHADOW_COLOR,b.shadowOffsetX=2*this.ds.scale,b.shadowOffsetY=2*this.ds.scale,b.shadowBlur=3*this.ds.scale):b.shadowColor="transparent";if(!a.flags.collapsed||!a.onDrawCollapsed||!0!=a.onDrawCollapsed(b,this)){var s=a._shape||h.BOX_SHAPE;y.set(a.size);var c=a.horizontal;if(a.flags.collapsed){b.font=this.inner_text_font;var k=a.getTitle?a.getTitle():a.title;null!=k&&(a._collapsed_width=Math.min(a.size[0],b.measureText(k).width+2*h.NODE_TITLE_HEIGHT),y[0]=a._collapsed_width,y[1]=0)}a.clip_area&& -(b.save(),b.beginPath(),s==h.BOX_SHAPE?b.rect(0,0,y[0],y[1]):s==h.ROUND_SHAPE?b.roundRect(0,0,y[0],y[1],10):s==h.CIRCLE_SHAPE&&b.arc(0.5*y[0],0.5*y[1],0.5*y[0],0,2*Math.PI),b.clip());a.has_errors&&(g="red");this.drawNodeShape(a,b,y,d,g,a.is_selected,a.mouseOver);b.shadowColor="transparent";if(a.onDrawForeground)a.onDrawForeground(b,this,this.canvas);b.textAlign=c?"center":"left";b.font=this.inner_text_font;g=!f;s=this.connecting_output;b.lineWidth=1;var k=0,e=new Float32Array(2);if(!a.flags.collapsed){if(a.inputs)for(d= -0;dthis.ds.scale,k=a._shape||a.constructor.shape||h.ROUND_SHAPE,p=a.constructor.title_mode,q=!0;p==h.TRANSPARENT_TITLE?q=!1:p==h.AUTOHIDE_TITLE&&s&&(q=!0);v[0]=0;v[1]=q?-f:0;v[2]=d[0]+1;v[3]=q?d[1]+f:d[1];s=b.globalAlpha;b.beginPath();k==h.BOX_SHAPE|| -c?b.fillRect(v[0],v[1],v[2],v[3]):k==h.ROUND_SHAPE||k==h.CARD_SHAPE?b.roundRect(v[0],v[1],v[2],v[3],this.round_radius,k==h.CARD_SHAPE?0:this.round_radius):k==h.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0],0,2*Math.PI);b.fill();a.flags.collapsed||(b.shadowColor="transparent",b.fillStyle="rgba(0,0,0,0.2)",b.fillRect(0,-1,v[2],2));b.shadowColor="transparent";if(a.onDrawBackground)a.onDrawBackground(b,this,this.canvas);if(q||p==h.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,f,d,this.ds.scale, -g);else if(p!=h.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){q=a.constructor.title_color||g;a.flags.collapsed&&(b.shadowColor=h.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var t=e.gradients[q];t||(t=e.gradients[q]=b.createLinearGradient(0,0,400,0),t.addColorStop(0,q),t.addColorStop(1,"#000"));b.fillStyle=t}else b.fillStyle=q;b.beginPath();k==h.BOX_SHAPE||c?b.rect(0,-f,d[0]+1,f):k!=h.ROUND_SHAPE&&k!=h.CARD_SHAPE||b.roundRect(0,-f,d[0]+1,f,this.round_radius,a.flags.collapsed? -this.round_radius:0);b.fill();b.shadowColor="transparent"}if(a.onDrawTitleBox)a.onDrawTitleBox(b,f,d,this.ds.scale);else k==h.ROUND_SHAPE||k==h.CIRCLE_SHAPE||k==h.CARD_SHAPE?(c&&(b.fillStyle="black",b.beginPath(),b.arc(0.5*f,-0.5*f,6,0,2*Math.PI),b.fill()),b.fillStyle=a.boxcolor||h.NODE_DEFAULT_BOXCOLOR,c?b.fillRect(0.5*f-5,-0.5*f-5,10,10):(b.beginPath(),b.arc(0.5*f,-0.5*f,5,0,2*Math.PI),b.fill())):(c&&(b.fillStyle="black",b.fillRect(0.5*(f-10)-1,-0.5*(f+10)-1,12,12)),b.fillStyle=a.boxcolor||h.NODE_DEFAULT_BOXCOLOR, -b.fillRect(0.5*(f-10),-0.5*(f+10),10,10));b.globalAlpha=s;if(a.onDrawTitleText)a.onDrawTitleText(b,f,d,this.ds.scale,this.title_text_font,r);!c&&(b.font=this.title_text_font,c=String(a.getTitle()))&&(b.fillStyle=r?"white":a.constructor.title_text_color||this.node_title_color,a.flags.collapsed?(b.textAlign="left",b.measureText(c),b.fillText(c.substr(0,20),f,h.NODE_TITLE_TEXT_Y-f),b.textAlign="left"):(b.textAlign="left",b.fillText(c,f,h.NODE_TITLE_TEXT_Y-f)));if(a.onDrawTitle)a.onDrawTitle(b)}if(r){if(a.onBounding)a.onBounding(v); -p==h.TRANSPARENT_TITLE&&(v[1]-=f,v[3]+=f);b.lineWidth=1;b.globalAlpha=0.8;b.beginPath();k==h.BOX_SHAPE?b.rect(-6+v[0],-6+v[1],12+v[2],12+v[3]):k==h.ROUND_SHAPE||k==h.CARD_SHAPE&&a.flags.collapsed?b.roundRect(-6+v[0],-6+v[1],12+v[2],12+v[3],2*this.round_radius):k==h.CARD_SHAPE?b.roundRect(-6+v[0],-6+v[1],12+v[2],12+v[3],2*this.round_radius,2):k==h.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0]+6,0,2*Math.PI);b.strokeStyle="#FFF";b.stroke();b.strokeStyle=g;b.globalAlpha=1}};var G=new Float32Array(4), -p=new Float32Array(4),q=new Float32Array(2),k=new Float32Array(2);e.prototype.drawConnections=function(a){var b=h.getTime(),d=this.visible_area;G[0]=d[0]-20;G[1]=d[1]-20;G[2]=d[2]+40;G[3]=d[3]+40;a.lineWidth=this.connections_width;a.fillStyle="#AAA";a.strokeStyle="#AAA";a.globalAlpha=this.editor_alpha;for(var d=this.graph._nodes,g=0,f=d.length;gp[2]&&(p[0]+=p[2],p[2]=Math.abs(p[2]));0>p[3]&&(p[1]+=p[3],p[3]=Math.abs(p[3]));if(u(p,G)){var n=e.outputs[z],z=r.inputs[s];if(n&&z&&(e=n.dir||(e.horizontal?h.DOWN:h.RIGHT),z=z.dir||(r.horizontal?h.UP:h.LEFT),this.renderLink(a,l,t,c,!1,0,null,e,z),c&&c._last_time&&1E3>b-c._last_time)){var n= -2-0.002*(b-c._last_time),K=a.globalAlpha;a.globalAlpha=K*n;this.renderLink(a,l,t,c,!0,n,"white",e,z);a.globalAlpha=K}}}}}}a.globalAlpha=1};e.prototype.renderLink=function(a,b,d,g,f,r,c,k,p,q){g&&this.visible_links.push(g);!c&&g&&(c=g.color||e.link_type_colors[g.type]);c||(c=this.default_link_color);null!=g&&this.highlighted_links[g.id]&&(c="#FFF");k=k||h.RIGHT;p=p||h.LEFT;var z=C(b,d);this.render_connections_border&&0.6b[1]?0:Math.PI,a.save(),a.translate(t[0],t[1]),a.rotate(l),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore(),a.save(),a.translate(q[0],q[1]),a.rotate(n),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill());if(r)for(a.fillStyle= -c,t=0;5>t;++t)r=(0.001*h.getTime()+0.2*t)%1,f=this.computeConnectionPoint(b,d,r,k,p),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill()};e.prototype.computeConnectionPoint=function(a,b,d,g,f){g=g||h.RIGHT;f=f||h.LEFT;var r=C(a,b),c=[a[0],a[1]],k=[b[0],b[1]];switch(g){case h.LEFT:c[0]+=-0.25*r;break;case h.RIGHT:c[0]+=0.25*r;break;case h.UP:c[1]+=-0.25*r;break;case h.DOWN:c[1]+=0.25*r}switch(f){case h.LEFT:k[0]+=-0.25*r;break;case h.RIGHT:k[0]+=0.25*r;break;case h.UP:k[1]+=-0.25*r;break;case h.DOWN:k[1]+= -0.25*r}g=(1-d)*(1-d)*(1-d);f=3*(1-d)*(1-d)*d;r=3*(1-d)*d*d;d*=d*d;return[g*a[0]+f*c[0]+r*k[0]+d*b[0],g*a[1]+f*c[1]+r*k[1]+d*b[1]]};e.prototype.drawExecutionOrder=function(a){a.shadowColor="transparent";a.globalAlpha=0.25;a.textAlign="center";a.strokeStyle="white";a.globalAlpha=0.75;for(var b=this.visible_nodes,d=0;dt.last_y&&st.options.max&&(t.value=t.options.max);else if("mousedown"==d.type){var l=t.options.values; -l&&l.constructor===Function&&(l=t.options.values(t,a));var z=null;"number"!=t.type&&(z=l.constructor===Array?l:Object.keys(l));c=40>c?-1:c>k-40?1:0;if("number"==t.type)t.value+=0.1*c*(t.options.step||1),null!=t.options.min&&t.valuet.options.max&&(t.value=t.options.max);else if(c)e=-1,e=l.constructor===Object?z.indexOf(String(t.value))+c:z.indexOf(t.value)+c,e>=z.length&&(e=z.length-1),0>e&&(e=0),t.value=l.constructor===Array?l[e]: -e;else{var n=l!=z?Object.values(l):l;new h.ContextMenu(n,{scale:Math.max(1,this.ds.scale),event:d,className:"dark",callback:y.bind(t)},e);var y=function(a,b,d){l!=z&&(a=n.indexOf(a));this.value=a;f(this,a);p.dirty_canvas=!0;return!1}}}else"mouseup"==d.type&&"number"==t.type&&(c=40>c?-1:c>k-40?1:0,200>d.click_time&&0==c&&this.prompt("Value",t.value,function(a){this.value=Number(a);f(this,this.value)}.bind(t),d));g!=t.value&&setTimeout(function(){f(this,this.value)}.bind(t),20);this.dirty_canvas=!0; -break;case "toggle":"mousedown"==d.type&&(t.value=!t.value,setTimeout(function(){f(t,t.value)},20));break;case "string":case "text":"mousedown"==d.type&&this.prompt("Value",t.value,function(a){this.value=a;f(this,a)}.bind(t),d);break;default:t.mouse&&t.mouse(ctx,d,[c,s],a)}return t}}return null};e.prototype.drawGroups=function(a,b){if(this.graph){var d=this.graph._groups;b.save();b.globalAlpha=0.5*this.editor_alpha;for(var g=0;gd&&0.01>b.editor_alpha&&(clearInterval(g),1>d&&(b.live_mode=!0));1"+q+""+a+"",value:q});if(p.length)return new h.ContextMenu(p, -{event:d,callback:c,parentMenu:g,allow_html:!0,node:f},b),!1}};e.decodeHTML=function(a){var b=document.createElement("div");b.innerText=a;return b.innerHTML};e.onResizeNode=function(a,b,d,g,f){f&&(f.size=f.computeSize(),f.setDirtyCanvas(!0,!0))};e.prototype.showLinkMenu=function(a,b){var d=this;console.log(a);var g=new h.ContextMenu(["Add Node",null,"Delete"],{event:b,title:null!=a.data?a.data.constructor.name:null,callback:function(b,h,c){switch(b){case "Add Node":e.onMenuAdd(null,null,c,g,function(b){console.log("node autoconnect"); -var f=d.graph.getNodeById(a.origin_id),g=d.graph.getNodeById(a.target_id);b.inputs&&b.inputs.length&&b.outputs&&b.outputs.length&&f.outputs[a.origin_slot].type==b.inputs[0].type&&b.outputs[0].type==g.inputs[0].type&&(f.connect(a.origin_slot,b,0),b.connect(0,g,a.target_slot),b.pos[0]-=0.5*b.size[0])});break;case "Delete":d.graph.removeLink(a.id)}}});return!1};e.onShowPropertyEditor=function(a,b,d,g,f){function h(){var b=p.value;"Number"==a.type?b=Number(b):"Boolean"==a.type&&(b=Boolean(b));f[c]=b; -k.parentNode&&k.parentNode.removeChild(k);f.setDirtyCanvas(!0,!0)}var c=a.property||"title";b=f[c];var k=document.createElement("div");k.className="graphdialog";k.innerHTML="";k.querySelector(".name").innerText=c;var p=k.querySelector("input");p&&(p.value=b,p.addEventListener("blur",function(a){this.focus()}),p.addEventListener("keydown",function(a){13==a.keyCode&&(h(),a.preventDefault(),a.stopPropagation())})); -b=e.active_canvas.canvas;d=b.getBoundingClientRect();var q=g=-20;d&&(g-=d.left,q-=d.top);event?(k.style.left=event.clientX+g+"px",k.style.top=event.clientY+q+"px"):(k.style.left=0.5*b.width+g+"px",k.style.top=0.5*b.height+q+"px");k.querySelector("button").addEventListener("click",h);b.parentNode.appendChild(k)};e.prototype.prompt=function(a,b,d,g){var f=this;a=a||"";var h=!1,c=document.createElement("div");c.className="graphdialog rounded";c.innerHTML=" "; -c.close=function(){f.prompt_box=null;c.parentNode&&c.parentNode.removeChild(c)};1e.search_limit)break}}s=null;if(Array.prototype.filter)s=Object.keys(h.registered_node_types).filter(l); -else for(k in s=[],h.registered_node_types)l(k)&&s.push(k);for(k=0;ke.search_limit);k++);var l=function(a){var b=h.registered_node_types[a];return p&&b.filter!=p?!1:-1!==a.toLowerCase().indexOf(d)}}}var f=this,c=e.active_canvas,k=c.canvas,p=k.ownerDocument||document,q=document.createElement("div");q.className="litegraph litesearchbox graphdialog rounded";q.innerHTML="Search
"; -q.close=function(){f.search_box=null;p.body.focus();p.body.style.overflow="";setTimeout(function(){f.canvas.focus()},20);q.parentNode&&q.parentNode.removeChild(q)};var l=null;1k.height-200&&(z.style.maxHeight=k.height-a.layerY-20+"px");u.focus();return q};e.prototype.showEditPropertyValue=function(a,b,d){function g(){f(t.value)}function f(f){"number"==typeof a.properties[b]&&(f=Number(f));if("array"==h||"object"==h)f=JSON.parse(f);a.properties[b]=f;a._graph&&a._graph._version++;if(a.onPropertyChanged)a.onPropertyChanged(b, -f);if(d.onclose)d.onclose();e.close();a.setDirtyCanvas(!0,!0)}if(a&&void 0!==a.properties[b]){d=d||{};var c=a.getPropertyInfo(b),h=c.type,k="";if("string"==h||"number"==h||"array"==h||"object"==h)k="";else if("enum"==h&&c.values){var k=""}else if("boolean"== -h)k="";else{console.warn("unknown type: "+h);return}var e=this.createDialog(""+b+""+k+"",d);if("enum"==h&&c.values){var t=e.querySelector("select");t.addEventListener("change",function(a){f(a.target.value)})}else if("boolean"==h)(t=e.querySelector("input"))&&t.addEventListener("click",function(a){f(!!t.checked)});else if(t=e.querySelector("input"))t.addEventListener("blur", -function(a){this.focus()}),q=void 0!==a.properties[b]?a.properties[b]:"",q=JSON.stringify(q),t.value=q,t.addEventListener("keydown",function(a){13==a.keyCode&&(g(),a.preventDefault(),a.stopPropagation())});e.querySelector("button").addEventListener("click",g);return e}};e.prototype.createDialog=function(a,b){b=b||{};var d=document.createElement("div");d.className="graphdialog";d.innerHTML=a;var g=this.canvas.getBoundingClientRect(),f=-20,c=-20;g&&(f-=g.left,c-=g.top);b.position?(f+=b.position[0], -c+=b.position[1]):b.event?(f+=b.event.clientX,c+=b.event.clientY):(f+=0.5*this.canvas.width,c+=0.5*this.canvas.height);d.style.left=f+"px";d.style.top=c+"px";this.canvas.parentNode.appendChild(d);d.close=function(){this.parentNode&&this.parentNode.removeChild(this)};return d};e.onMenuNodeCollapse=function(a,b,d,g,f){f.collapse()};e.onMenuNodePin=function(a,b,d,g,f){f.pin()};e.onMenuNodeMode=function(a,b,d,g,f){new h.ContextMenu(["Always","On Event","On Trigger","Never"],{event:d,callback:function(a){if(f)switch(a){case "On Event":f.mode= -h.ON_EVENT;break;case "On Trigger":f.mode=h.ON_TRIGGER;break;case "Never":f.mode=h.NEVER;break;default:f.mode=h.ALWAYS}},parentMenu:g,node:f});return!1};e.onMenuNodeColors=function(a,b,d,g,f){if(!f)throw"no node for color";b=[];b.push({value:null,content:"No color"});for(var c in e.node_colors)a=e.node_colors[c],a={value:c,content:""+c+""},b.push(a);new h.ContextMenu(b,{event:d,callback:function(a){f&&((a=a.value?e.node_colors[a.value]:null)?f.constructor===h.LGraphGroup?f.color=a.groupcolor:(f.color=a.color,f.bgcolor=a.bgcolor):(delete f.color,delete f.bgcolor),f.setDirtyCanvas(!0,!0))},parentMenu:g,node:f});return!1};e.onMenuNodeShapes=function(a,b,d,g,f){if(!f)throw"no node passed";new h.ContextMenu(h.VALID_SHAPES,{event:d,callback:function(a){f&&(f.shape=a,f.setDirtyCanvas(!0))},parentMenu:g,node:f}); -return!1};e.onMenuNodeRemove=function(a,b,d,g,f){if(!f)throw"no node passed";!1!==f.removable&&(f.graph.remove(f),f.setDirtyCanvas(!0,!0))};e.onMenuNodeClone=function(a,b,d,g,f){!1!=f.clonable&&(a=f.clone())&&(a.pos=[f.pos[0]+5,f.pos[1]+5],f.graph.add(a),f.setDirtyCanvas(!0,!0))};e.node_colors={red:{color:"#322",bgcolor:"#533",groupcolor:"#A88"},brown:{color:"#332922",bgcolor:"#593930",groupcolor:"#b06634"},green:{color:"#232",bgcolor:"#353",groupcolor:"#8A8"},blue:{color:"#223",bgcolor:"#335",groupcolor:"#88A"}, -pale_blue:{color:"#2a363b",bgcolor:"#3f5159",groupcolor:"#3f789e"},cyan:{color:"#233",bgcolor:"#355",groupcolor:"#8AA"},purple:{color:"#323",bgcolor:"#535",groupcolor:"#a1309b"},yellow:{color:"#432",bgcolor:"#653",groupcolor:"#b58b2a"},black:{color:"#222",bgcolor:"#000",groupcolor:"#444"}};e.prototype.getCanvasMenuOptions=function(){var a=null;this.getMenuOptions?a=this.getMenuOptions():(a=[{content:"Add Node",has_submenu:!0,callback:e.onMenuAdd},{content:"Add Group",callback:e.onGroupAdd}],this._graph_stack&& -0Name",f),k=h.querySelector("input");k&&c&&(k.value=c.label||"");h.querySelector("button").addEventListener("click",function(a){k.value&&(c&&(c.label=k.value),d.setDirty(!0));h.close()})}},extra:a};a&&(c.title=a.type);var k=null;a&&(k=a.getSlotInPosition(b.canvasX,b.canvasY),e.active_node= -a);if(k){f=[];k&&k.output&&k.output.links&&k.output.links.length&&f.push({content:"Disconnect Links",slot:k});var p=k.input||k.output;f.push(p.locked?"Cannot remove":{content:"Remove Slot",slot:k});f.push(p.nameLocked?"Cannot rename":{content:"Rename Slot",slot:k});c.title=(k.input?k.input.type:k.output.type)||"*";k.input&&k.input.type==h.ACTION&&(c.title="Action");k.output&&k.output.type==h.EVENT&&(c.title="Event")}else a?f=this.getNodeMenuOptions(a):(f=this.getCanvasMenuOptions(),(k=this.graph.getGroupOnPos(b.canvasX, -b.canvasY))&&f.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:k,options:this.getGroupMenuOptions(k)}}));f&&new h.ContextMenu(f,c,g)};"undefined"!=typeof window&&window.CanvasRenderingContext2D&&(window.CanvasRenderingContext2D.prototype.roundRect=function(a,b,d,g,f,c){void 0===f&&(f=5);void 0===c&&(c=f);this.moveTo(a+f,b);this.lineTo(a+d-f,b);this.quadraticCurveTo(a+d,b,a+d,b+f);this.lineTo(a+d,b+g-c);this.quadraticCurveTo(a+d,b+g,a+d-c,b+g);this.lineTo(a+c,b+g);this.quadraticCurveTo(a, -b+g,a,b+g-c);this.lineTo(a,b+f);this.quadraticCurveTo(a,b,a+f,b)});h.compareObjects=function(a,b){for(var d in a)if(a[d]!=b[d])return!1;return!0};h.distance=C;h.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+","+Math.round(255*a[2]).toFixed()+","+(4==a.length?a[3].toFixed(2):"1.0")+")"};h.isInsideRectangle=w;h.growBounding=function(a,b,d){ba[2]&&(a[2]=b);da[3]&&(a[3]=d)};h.isInsideBounding=function(a,b){return a[0]< -b[0][0]||a[1]b[1][0]||a[1]>b[1][1]?!1:!0};h.overlapBounding=u;h.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),d=0,g,f,c=0;6>c;c+=2)g="0123456789ABCDEF".indexOf(a.charAt(c)),f="0123456789ABCDEF".indexOf(a.charAt(c+1)),b[d]=16*g+f,d++;return b};h.num2hex=function(a){for(var b="#",d,g,f=0;3>f;f++)d=a[f]/16,g=a[f]%16,b+="0123456789ABCDEF".charAt(d)+"0123456789ABCDEF".charAt(g);return b};D.prototype.addItem=function(a,b,d){function g(a){var b= -this.value;b&&b.has_submenu&&f.call(this,a)}function f(a){var b=this.value,f=!0;c.current_submenu&&c.current_submenu.close(a);if(d.callback){var g=d.callback.call(this,b,d,a,c,d.node);!0===g&&(f=!1)}if(b&&(b.callback&&!d.ignore_item_callbacks&&!0!==b.disabled&&(g=b.callback.call(this,b,d,a,c,d.extra),!0===g&&(f=!1)),b.submenu)){if(!b.submenu.options)throw"ContextMenu submenu needs options";new c.constructor(b.submenu.options,{callback:b.submenu.callback,event:a,parentMenu:c,ignore_item_callbacks:b.submenu.ignore_item_callbacks, -title:b.submenu.title,extra:b.submenu.extra,autoopen:d.autoopen});f=!1}f&&!c.lock&&c.close()}var c=this;d=d||{};var h=document.createElement("div");h.className="litemenu-entry submenu";var k=!1;if(null===b)h.classList.add("separator");else{h.innerHTML=b&&b.title?b.title:a;if(h.value=b)b.disabled&&(k=!0,h.classList.add("disabled")),(b.submenu||b.has_submenu)&&h.classList.add("has_submenu");"function"==typeof b?(h.dataset.value=a,h.onclick_callback=b):h.dataset.value=b;b.className&&(h.className+=" "+ -b.className)}this.root.appendChild(h);k||h.addEventListener("click",f);d.autoopen&&h.addEventListener("mouseenter",g);return h};D.prototype.close=function(a,b){this.root.parentNode&&this.root.parentNode.removeChild(this.root);this.parentMenu&&!b&&(this.parentMenu.lock=!1,this.parentMenu.current_submenu=null,void 0===a?this.parentMenu.close():a&&!D.isCursorOverElement(a,this.parentMenu.root)&&D.trigger(this.parentMenu.root,"mouseleave",a));this.current_submenu&&this.current_submenu.close(a,!0);this.root.closing_timer&& -clearTimeout(this.root.closing_timer)};D.trigger=function(a,b,d,g){var f=document.createEvent("CustomEvent");f.initCustomEvent(b,!0,!0,d);f.srcElement=g;a.dispatchEvent?a.dispatchEvent(f):a.__events&&a.__events.dispatchEvent(f);return f};D.prototype.getTopMenu=function(){return this.options.parentMenu?this.options.parentMenu.getTopMenu():this};D.prototype.getFirstEvent=function(){return this.options.parentMenu?this.options.parentMenu.getFirstEvent():this.options.event};D.isCursorOverElement=function(a, -b){var d=a.clientX,g=a.clientY,f=b.getBoundingClientRect();return f?g>f.top&&gf.left&&dMath.abs(d))return g[1];d=(a-g[0])/d;return g[1]*(1-d)+f[1]*d}}return 0}};A.prototype.draw=function(a,b,d,g,f,c){if(d=this.points){this.size= -b;var h=b[0]-2*this.margin;b=b[1]-2*this.margin;f=f||"#666";a.save();a.translate(this.margin,this.margin);g&&(a.fillStyle="#111",a.fillRect(0,0,h,b),a.fillStyle="#222",a.fillRect(0.5*h,0,1,b),a.strokeStyle="#333",a.strokeRect(0,0,h,b));a.strokeStyle=f;c&&(a.globalAlpha=0.5);a.beginPath();for(g=0;ga[1])){var g=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=a[0]-this.margin,h=a[1]-this.margin;this.selected=this.getCloserPoint([c,h],30/b.ds.scale);-1==this.selected&&(g=[c/g,1-h/f],d.push(g),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(g),this.must_update=!0);if(-1!=this.selected)return!0}};A.prototype.onMouseMove=function(a,b){var d=this.points;if(d){var g= -this.selected;if(!(0>g)){var f=(a[0]-this.margin)/(this.size[0]-2*this.margin),c=(a[1]-this.margin)/(this.size[1]-2*this.margin);this._nearest=this.getCloserPoint([a[0]-this.margin,a[1]-this.margin],30/b.ds.scale);var h=d[g];if(h){var k=0==g||g==d.length-1;!k&&(-10>a[0]||a[0]>this.size[0]+10||-10>a[1]||a[1]>this.size[1]+10)?(d.splice(g,1),this.selected=-1):(h[0]=k?0==g?0:1:Math.clamp(f,0,1),h[1]=1-Math.clamp(c,0,1),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(h),this.must_update= -!0)}}}};A.prototype.onMouseUp=function(a,b){this.selected=-1;return!1};A.prototype.getCloserPoint=function(a,b){var d=this.points;if(!d)return-1;b=b||30;for(var g=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=d.length,h=[0,0],k=1E6,p=-1,q=0;qk||e>b||(p=q,k=e)}return p};h.CurveEditor=A;h.getParameterNames=function(a){return(a+"").replace(/[/][/].*$/gm,"").replace(/\s+/g,"").replace(/[/][*][^/*]*[*][/]/g,"").split("){", -1)[0].replace(/^[^(]*[(]/,"").replace(/=[^,]+/g,"").split(",").filter(Boolean)};Math.clamp=function(a,b,d){return b>a?b:dc.width-k.width-10&&(f=c.width-k.width-10);c.height&&h>c.height-k.height-10&&(h=c.height-k.height-10)}r.style.left=f+"px";r.style.top=h+"px";b.scale&&(r.style.transform="scale("+b.scale+")")}function A(a){this.points=a;this.nearest=this.selected=-1;this.size=null;this.must_update=!0;this.margin=5}var h=B.LiteGraph={VERSION:0.4,CANVAS_GRID_SIZE:10,NODE_TITLE_HEIGHT:30, +NODE_TITLE_TEXT_Y:20,NODE_SLOT_HEIGHT:20,NODE_WIDGET_HEIGHT:20,NODE_WIDTH:140,NODE_MIN_WIDTH:50,NODE_COLLAPSED_RADIUS:10,NODE_COLLAPSED_WIDTH:80,NODE_TITLE_COLOR:"#999",NODE_TEXT_SIZE:14,NODE_TEXT_COLOR:"#AAA",NODE_SUBTEXT_SIZE:12,NODE_DEFAULT_COLOR:"#333",NODE_DEFAULT_BGCOLOR:"#353535",NODE_DEFAULT_BOXCOLOR:"#666",NODE_DEFAULT_SHAPE:"box",DEFAULT_SHADOW_COLOR:"rgba(0,0,0,0.5)",DEFAULT_GROUP_FONT:24,WIDGET_BGCOLOR:"#222",WIDGET_OUTLINE_COLOR:"#666",WIDGET_TEXT_COLOR:"#DDD",WIDGET_SECONDARY_TEXT_COLOR:"#999", +LINK_COLOR:"#9A9",EVENT_LINK_COLOR:"#A86",CONNECTING_LINK_COLOR:"#AFA",MAX_NUMBER_OF_NODES:1E3,DEFAULT_POSITION:[100,100],VALID_SHAPES:["default","box","round","card"],BOX_SHAPE:1,ROUND_SHAPE:2,CIRCLE_SHAPE:3,CARD_SHAPE:4,ARROW_SHAPE:5,INPUT:1,OUTPUT:2,EVENT:-1,ACTION:-1,ALWAYS:0,ON_EVENT:1,NEVER:2,ON_TRIGGER:3,UP:1,DOWN:2,LEFT:3,RIGHT:4,CENTER:5,STRAIGHT_LINK:0,LINEAR_LINK:1,SPLINE_LINK:2,NORMAL_TITLE:0,NO_TITLE:1,TRANSPARENT_TITLE:2,AUTOHIDE_TITLE:3,proxy:null,node_images_path:"",debug:!1,catch_exceptions:!0, +throw_errors:!0,allow_scripts:!1,registered_node_types:{},node_types_by_file_extension:{},Nodes:{},searchbox_extras:{},registerNodeType:function(a,b){if(!b.prototype)throw"Cannot register a simple object, it must be a class with a prototype";b.type=a;h.debug&&console.log("Node registered: "+a);a.split("/");var d=b.name,g=a.lastIndexOf("/");b.category=a.substr(0,g);b.title||(b.title=d);if(b.prototype)for(var f in n.prototype)b.prototype[f]||(b.prototype[f]=n.prototype[f]);if(g=this.registered_node_types[a])console.log("replacing node type: "+ +a);else if(Object.hasOwnProperty(b.prototype,"shape")||Object.defineProperty(b.prototype,"shape",{set:function(a){switch(a){case "default":delete this._shape;break;case "box":this._shape=h.BOX_SHAPE;break;case "round":this._shape=h.ROUND_SHAPE;break;case "circle":this._shape=h.CIRCLE_SHAPE;break;case "card":this._shape=h.CARD_SHAPE;break;default:this._shape=a}},get:function(a){return this._shape},enumerable:!0,configurable:!0}),b.prototype.onPropertyChange&&console.warn("LiteGraph node class "+a+ +" has onPropertyChange method, it must be called onPropertyChanged with d at the end"),b.supported_extensions)for(f in b.supported_extensions){var r=b.supported_extensions[f];r&&r.constructor===String&&(this.node_types_by_file_extension[r.toLowerCase()]=b)}this.registered_node_types[a]=b;b.constructor.name&&(this.Nodes[d]=b);if(h.onNodeTypeRegistered)h.onNodeTypeRegistered(a,b);if(g&&h.onNodeTypeReplaced)h.onNodeTypeReplaced(a,b,g)},unregisterNodeType:function(a){var b=a.constructor===String?this.registered_node_types[a]: +a;if(!b)throw"node type not found: "+a;delete this.registered_node_types[b.type];b.constructor.name&&delete this.Nodes[b.constructor.name]},wrapFunctionAsNode:function(a,b,d,g,f){for(var r=Array(b.length),s="",c=h.getParameterNames(b),k=0;ks&&(s=f.size[0]),c+=f.size[1]+a+h.NODE_TITLE_HEIGHT;b+=s+a}this.setDirtyCanvas(!0,!0)};c.prototype.getTime=function(){return this.globaltime};c.prototype.getFixedTime=function(){return this.fixedtime};c.prototype.getElapsedTime= +function(){return this.elapsed_time};c.prototype.sendEventToAllNodes=function(a,b,d){d=d||h.ALWAYS;var g=this._nodes_in_order?this._nodes_in_order:this._nodes;if(g)for(var f=0,r=g.length;f=h.MAX_NUMBER_OF_NODES)throw"LiteGraph: max number of nodes in a graph reached";null==a.id||-1==a.id?a.id=++this.last_node_id:this.last_node_id< +a.id&&(this.last_node_id=a.id);a.graph=this;this._version++;this._nodes.push(a);this._nodes_by_id[a.id]=a;if(a.onAdded)a.onAdded(this);this.config.align_to_grid&&a.alignToGrid();b||this.updateExecutionOrder();if(this.onNodeAdded)this.onNodeAdded(a);this.setDirtyCanvas(!0);this.change();return a}};c.prototype.remove=function(a){if(a.constructor===h.LGraphGroup){var b=this._groups.indexOf(a);-1!=b&&this._groups.splice(b,1);a.graph=null;this._version++;this.setDirtyCanvas(!0,!0);this.change()}else if(null!= +this._nodes_by_id[a.id]&&!a.ignore_remove){if(a.inputs)for(b=0;ba.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});this.id=-1;this.type=null;this.inputs=[];this.outputs=[];this.connections=[];this.properties={};this.properties_info=[];this.flags={}};n.prototype.configure=function(a){this.graph&&this.graph._version++;for(var b in a)if("properties"==b)for(var d in a.properties){if(this.properties[d]=a.properties[d],this.onPropertyChanged)this.onPropertyChanged(d, +a.properties[d])}else null!=a[b]&&("object"==typeof a[b]?this[b]&&this[b].configure?this[b].configure(a[b]):this[b]=h.cloneObject(a[b],this[b]):this[b]=a[b]);a.title||(this.title=this.constructor.title);if(this.onConnectionsChange){if(this.inputs)for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d._data=b,this.outputs[a].links))for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d.type=b,this.outputs[a].links))for(d=0;d=this.inputs.length||null==this.inputs[a].link)){var d=this.graph.links[this.inputs[a].link];if(!d)return null;if(!b)return d.data;var g=this.graph.getNodeById(d.origin_id);if(!g)return d.data; +if(g.updateOutputData)g.updateOutputData(d.origin_slot);else if(g.onExecute)g.onExecute();return d.data}};n.prototype.getInputDataType=function(a){if(!this.inputs||a>=this.inputs.length||null==this.inputs[a].link)return null;a=this.graph.links[this.inputs[a].link];if(!a)return null;var b=this.graph.getNodeById(a.origin_id);return b?(a=b.outputs[a.origin_slot])?a.type:null:a.type};n.prototype.getInputDataByName=function(a,b){var d=this.findInputSlot(a);return-1==d?null:this.getInputData(d,b)};n.prototype.isInputConnected= +function(a){return this.inputs?a=this.inputs.length)return null;a=this.inputs[a];return a&&null!==a.link?(a=this.graph.links[a.link])?this.graph.getNodeById(a.origin_id):null:null};n.prototype.getInputOrProperty=function(a){if(!this.inputs||!this.inputs.length)return this.properties?this.properties[a]: +null;for(var b=0,d=this.inputs.length;b=this.outputs.length?null:this.outputs[a]._data};n.prototype.getOutputInfo=function(a){return this.outputs?a=this.outputs.length)return null;a=this.outputs[a];if(!a.links||0==a.links.length)return null;for(var b=[],d=0;da&&this.pos[1]-f-db)return!0;return!1};n.prototype.getSlotInPosition=function(a,b){var d=new Float32Array(2);if(this.inputs)for(var g=0,f=this.inputs.length;g=this.outputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),null;b&&b.constructor===Number&&(b=this.graph.getNodeById(b)); +if(!b)throw"target node is null";if(b==this)return null;if(d.constructor===String){if(d=b.findInputSlot(d),-1==d)return h.debug&&console.log("Connect: Error, no slot of name "+d),null}else{if(d===h.EVENT)return null;if(!b.inputs||d>=b.inputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),null}null!=b.inputs[d].link&&b.disconnectInput(d);var g=this.outputs[a];if(b.onConnectInput&&!1===b.onConnectInput(d,g.type,g))return null;var f=b.inputs[d],r=null;if(h.isValidConnection(g.type, +f.type)){r=new m(++this.graph.last_link_id,f.type,this.id,a,b.id,d);this.graph.links[r.id]=r;null==g.links&&(g.links=[]);g.links.push(r.id);b.inputs[d].link=r.id;this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(h.OUTPUT,a,!0,r,g);if(b.onConnectionsChange)b.onConnectionsChange(h.INPUT,d,!0,r,f);this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(h.INPUT,b,d,this,a),this.graph.onNodeConnectionChange(h.OUTPUT,this,a,b,d))}this.setDirtyCanvas(!1, +!0);this.graph.connectionChange(this,r);return r};n.prototype.disconnectOutput=function(a,b){if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return h.debug&&console.log("Connect: Error, no slot of name "+a),!1}else if(!this.outputs||a>=this.outputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),!1;var d=this.outputs[a];if(!d||!d.links||0==d.links.length)return!1;if(b){b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"Target Node not found"; +for(var g=0,f=d.links.length;g=this.inputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),!1;var b=this.inputs[a];if(!b)return!1;var d=this.inputs[a].link;this.inputs[a].link=null;var g=this.graph.links[d];if(g){var f=this.graph.getNodeById(g.origin_id);if(!f)return!1;var r=f.outputs[g.origin_slot];if(!r||!r.links||0==r.links.length)return!1;for(var s=0,c=r.links.length;sb&&this.inputs[b].pos)return d[0]=this.pos[0]+this.inputs[b].pos[0],d[1]=this.pos[1]+this.inputs[b].pos[1],d;if(!a&&g>b&&this.outputs[b].pos)return d[0]=this.pos[0]+this.outputs[b].pos[0], +d[1]=this.pos[1]+this.outputs[b].pos[1],d;if(this.horizontal)return d[0]=this.pos[0]+this.size[0]/g*(b+0.5),d[1]=a?this.pos[1]-h.NODE_TITLE_HEIGHT:this.pos[1]+this.size[1],d;d[0]=a?this.pos[0]+f:this.pos[0]+this.size[0]+1-f;d[1]=this.pos[1]+(b+0.7)*h.NODE_SLOT_HEIGHT+(this.constructor.slot_start_y||0);return d};n.prototype.alignToGrid=function(){this.pos[0]=h.CANVAS_GRID_SIZE*Math.round(this.pos[0]/h.CANVAS_GRID_SIZE);this.pos[1]=h.CANVAS_GRID_SIZE*Math.round(this.pos[1]/h.CANVAS_GRID_SIZE)};n.prototype.trace= +function(a){this.console||(this.console=[]);this.console.push(a);this.console.length>n.MAX_CONSOLE&&this.console.shift();this.graph.onNodeTrace(this,a)};n.prototype.setDirtyCanvas=function(a,b){this.graph&&this.graph.sendActionToCanvas("setDirty",[a,b])};n.prototype.loadImage=function(a){var b=new Image;b.src=h.node_images_path+a;b.ready=!1;var d=this;b.onload=function(){this.ready=!0;d.setDirtyCanvas(!0)};return b};n.prototype.captureInput=function(a){if(this.graph&&this.graph.list_of_graphcanvas)for(var b= +this.graph.list_of_graphcanvas,d=0;da.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0}); +Object.defineProperty(this,"size",{set:function(a){!a||2>a.length||(this._size[0]=Math.max(140,a[0]),this._size[1]=Math.max(80,a[1]))},get:function(){return this._size},enumerable:!0})};l.prototype.configure=function(a){this.title=a.title;this._bounding.set(a.bounding);this.color=a.color;this.font=a.font};l.prototype.serialize=function(){var a=this._bounding;return{title:this.title,bounding:[Math.round(a[0]),Math.round(a[1]),Math.round(a[2]),Math.round(a[3])],color:this.color,font:this.font}};l.prototype.move= +function(a,b,d){this._pos[0]+=a;this._pos[1]+=b;if(!d)for(d=0;dthis.max_scale&&(a=this.max_scale);if(a!=this.scale&&this.element){var d=this.element.getBoundingClientRect(); +if(d){b=b||[0.5*d.width,0.5*d.height];d=this.convertCanvasToOffset(b);this.scale=a;0.01>Math.abs(this.scale-1)&&(this.scale=1);var g=this.convertCanvasToOffset(b),d=[g[0]-d[0],g[1]-d[1]];this.offset[0]+=d[0];this.offset[1]+=d[1];if(this.onredraw)this.onredraw(this)}}};x.prototype.changeDeltaScale=function(a,b){this.changeScale(this.scale*a,b)};x.prototype.reset=function(){this.scale=1;this.offset[0]=0;this.offset[1]=0};B.LGraphCanvas=h.LGraphCanvas=e;e.link_type_colors={"-1":h.EVENT_LINK_COLOR,number:"#AAA", +node:"#DCA"};e.gradients={};e.prototype.clear=function(){this.fps=this.render_time=this.last_draw_time=this.frame=0;this.dragging_rectangle=null;this.selected_nodes={};this.selected_group=null;this.visible_nodes=[];this.connecting_node=this.node_capturing_input=this.node_over=this.node_dragged=null;this.highlighted_links={};this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_widget=this.node_in_panel=this.dirty_area=null;this.last_mouse=[0,0];this.last_mouseclick=0;this.visible_area.set([0,0,0,0]); +if(this.onClear)this.onClear()};e.prototype.setGraph=function(a,b){this.graph!=a&&(b||this.clear(),!a&&this.graph?this.graph.detachCanvas(this):(a.attachCanvas(this),this._graph_stack&&(this._graph_stack=null),this.setDirty(!0,!0)))};e.prototype.openSubgraph=function(a){if(!a)throw"graph cannot be null";if(this.graph==a)throw"graph cannot be the same";this.clear();this.graph&&(this._graph_stack||(this._graph_stack=[]),this._graph_stack.push(this.graph));a.attachCanvas(this);this.setDirty(!0,!0)}; +e.prototype.closeSubgraph=function(){if(this._graph_stack&&0!=this._graph_stack.length){var a=this.graph._subgraph_node,b=this._graph_stack.pop();this.selected_nodes={};this.highlighted_links={};b.attachCanvas(this);this.setDirty(!0,!0);a&&(this.centerOnNode(a),this.selectNodes([a]))}};e.prototype.getCurrentGraph=function(){return this.graph};e.prototype.setCanvas=function(a,b){if(a&&a.constructor===String&&(a=document.getElementById(a),!a))throw"Error creating LiteGraph canvas: Canvas not found"; +if(a!==this.canvas&&(!a&&this.canvas&&(b||this.unbindEvents()),this.canvas=a,this.ds.element=a)){a.className+=" lgraphcanvas";a.data=this;a.tabindex="1";this.bgcanvas=null;this.bgcanvas||(this.bgcanvas=document.createElement("canvas"),this.bgcanvas.width=this.canvas.width,this.bgcanvas.height=this.canvas.height);if(null==a.getContext){if("canvas"!=a.localName)throw"Element supplied for LGraphCanvas must be a element, you passed a "+a.localName;throw"This browser doesn't support Canvas";}null== +(this.ctx=a.getContext("2d"))&&(a.webgl_enabled||console.warn("This canvas seems to be WebGL, enabling WebGL renderer"),this.enableWebGL());this._mousemove_callback=this.processMouseMove.bind(this);this._mouseup_callback=this.processMouseUp.bind(this);b||this.bindEvents()}};e.prototype._doNothing=function(a){a.preventDefault();return!1};e.prototype._doReturnTrue=function(a){a.preventDefault();return!0};e.prototype.bindEvents=function(){if(this._events_binded)console.warn("LGraphCanvas: events already binded"); +else{var a=this.canvas,b=this.getCanvasWindow().document;this._mousedown_callback=this.processMouseDown.bind(this);this._mousewheel_callback=this.processMouseWheel.bind(this);a.addEventListener("mousedown",this._mousedown_callback,!0);a.addEventListener("mousemove",this._mousemove_callback);a.addEventListener("mousewheel",this._mousewheel_callback,!1);a.addEventListener("contextmenu",this._doNothing);a.addEventListener("DOMMouseScroll",this._mousewheel_callback,!1);a.addEventListener("touchstart", +this.touchHandler,!0);a.addEventListener("touchmove",this.touchHandler,!0);a.addEventListener("touchend",this.touchHandler,!0);a.addEventListener("touchcancel",this.touchHandler,!0);this._key_callback=this.processKey.bind(this);a.addEventListener("keydown",this._key_callback,!0);b.addEventListener("keyup",this._key_callback,!0);this._ondrop_callback=this.processDrop.bind(this);a.addEventListener("dragover",this._doNothing,!1);a.addEventListener("dragend",this._doNothing,!1);a.addEventListener("drop", +this._ondrop_callback,!1);a.addEventListener("dragenter",this._doReturnTrue,!1);this._events_binded=!0}};e.prototype.unbindEvents=function(){if(this._events_binded){var a=this.getCanvasWindow().document;this.canvas.removeEventListener("mousedown",this._mousedown_callback);this.canvas.removeEventListener("mousewheel",this._mousewheel_callback);this.canvas.removeEventListener("DOMMouseScroll",this._mousewheel_callback);this.canvas.removeEventListener("keydown",this._key_callback);a.removeEventListener("keyup", +this._key_callback);this.canvas.removeEventListener("contextmenu",this._doNothing);this.canvas.removeEventListener("drop",this._ondrop_callback);this.canvas.removeEventListener("dragenter",this._doReturnTrue);this.canvas.removeEventListener("touchstart",this.touchHandler);this.canvas.removeEventListener("touchmove",this.touchHandler);this.canvas.removeEventListener("touchend",this.touchHandler);this.canvas.removeEventListener("touchcancel",this.touchHandler);this._ondrop_callback=this._key_callback= +this._mousewheel_callback=this._mousedown_callback=null;this._events_binded=!1}else console.warn("LGraphCanvas: no events binded")};e.getFileExtension=function(a){var b=a.indexOf("?");-1!=b&&(a=a.substr(0,b));b=a.lastIndexOf(".");return-1==b?"":a.substr(b+1).toLowerCase()};e.prototype.enableWebGL=function(){if(void 0===typeof GL)throw"litegl.js must be included to use a WebGL canvas";if(void 0===typeof enableWebGLCanvas)throw"webglCanvas.js must be included to use this feature";this.gl=this.ctx=enableWebGLCanvas(this.canvas); +this.ctx.webgl=!0;this.bgcanvas=this.canvas;this.bgctx=this.gl;this.canvas.webgl_enabled=!0};e.prototype.setDirty=function(a,b){a&&(this.dirty_canvas=!0);b&&(this.dirty_bgcanvas=!0)};e.prototype.getCanvasWindow=function(){if(!this.canvas)return window;var a=this.canvas.ownerDocument;return a.defaultView||a.parentWindow};e.prototype.startRendering=function(){function a(){this.pause_rendering||this.draw();var b=this.getCanvasWindow();this.is_rendering&&b.requestAnimationFrame(a.bind(this))}this.is_rendering|| +(this.is_rendering=!0,a.call(this))};e.prototype.stopRendering=function(){this.is_rendering=!1};e.prototype.processMouseDown=function(a){if(this.graph){this.adjustMouseEvent(a);var b=this.getCanvasWindow();e.active_canvas=this;this.canvas.removeEventListener("mousemove",this._mousemove_callback);b.document.addEventListener("mousemove",this._mousemove_callback,!0);b.document.addEventListener("mouseup",this._mouseup_callback,!0);var d=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes,5), +g=!1,f=300>h.getTime()-this.last_mouseclick;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;this.canvas.focus();h.closeAllContextMenus(b);if(!this.onMouse||!0!=this.onMouse(a)){if(1==a.which){a.ctrlKey&&(this.dragging_rectangle=new Float32Array(4),this.dragging_rectangle[0]=a.canvasX,this.dragging_rectangle[1]=a.canvasY,this.dragging_rectangle[2]=1,this.dragging_rectangle[3]=1,g=!0);var r=!1;if(d&&this.allow_interaction&&!g&&!this.read_only){this.live_mode||d.flags.pinned||this.bringToFront(d); +if(!this.connecting_node&&!d.flags.collapsed&&!this.live_mode)if(!g&&!1!==d.resizable&&w(a.canvasX,a.canvasY,d.pos[0]+d.size[0]-5,d.pos[1]+d.size[1]-5,10,10))this.resizing_node=d,this.canvas.style.cursor="se-resize",g=!0;else{if(d.outputs)for(var s=0,c=d.outputs.length;sr[0]+4||a.canvasYr[1]+4)){this.showLinkMenu(d,a);this.over_link_center=null;break}this.selected_group=this.graph.getGroupOnPos(a.canvasX,a.canvasY);this.selected_group_resizing= +!1;this.selected_group&&!this.read_only&&(a.ctrlKey&&(this.dragging_rectangle=null),10>C([a.canvasX,a.canvasY],[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])*this.ds.scale?this.selected_group_resizing=!0:this.selected_group.recomputeInsideNodes());f&&!this.read_only&&this.allow_searchbox&&this.showSearchBox(a);r=!0}!g&&r&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}else 2!=a.which&&3==a.which&&(this.read_only||this.processContextMenu(d, +a));this.last_mouse[0]=a.localX;this.last_mouse[1]=a.localY;this.last_mouseclick=h.getTime();this.last_mouse_dragging=!0;this.graph.change();(!b.document.activeElement||"input"!=b.document.activeElement.nodeName.toLowerCase()&&"textarea"!=b.document.activeElement.nodeName.toLowerCase())&&a.preventDefault();a.stopPropagation();if(this.onMouseDown)this.onMouseDown(a);return!1}}};e.prototype.processMouseMove=function(a){this.autoresize&&this.resize();if(this.graph){e.active_canvas=this;this.adjustMouseEvent(a); +var b=[a.localX,a.localY],d=[b[0]-this.last_mouse[0],b[1]-this.last_mouse[1]];this.last_mouse=b;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;a.dragging=this.last_mouse_dragging;this.node_widget&&(this.processNodeWidgets(this.node_widget[0],this.canvas_mouse,a,this.node_widget[1]),this.dirty_canvas=!0);if(this.dragging_rectangle)this.dragging_rectangle[2]=a.canvasX-this.dragging_rectangle[0],this.dragging_rectangle[3]=a.canvasY-this.dragging_rectangle[1],this.dirty_canvas=!0;else if(this.selected_group&& +!this.read_only)this.selected_group_resizing?this.selected_group.size=[a.canvasX-this.selected_group.pos[0],a.canvasY-this.selected_group.pos[1]]:(this.selected_group.move(d[0]/this.ds.scale,d[1]/this.ds.scale,a.ctrlKey),this.selected_group._nodes.length&&(this.dirty_canvas=!0)),this.dirty_bgcanvas=!0;else if(this.dragging_canvas)this.ds.offset[0]+=d[0]/this.ds.scale,this.ds.offset[1]+=d[1]/this.ds.scale,this.dirty_bgcanvas=this.dirty_canvas=!0;else if(this.allow_interaction&&!this.read_only){this.connecting_node&& +(this.dirty_canvas=!0);for(var g=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes),b=0,f=this.graph._nodes.length;bs[0]+4||a.canvasYs[1]+4)){f=r;break}}f!=this.over_link_center&&(this.over_link_center=f,this.dirty_canvas=!0);this.canvas&&(this.canvas.style.cursor="")}if(this.node_capturing_input&&this.node_capturing_input!=g&&this.node_capturing_input.onMouseMove)this.node_capturing_input.onMouseMove(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]], +this);if(this.node_dragged&&!this.live_mode){for(b in this.selected_nodes)g=this.selected_nodes[b],g.pos[0]+=d[0]/this.ds.scale,g.pos[1]+=d[1]/this.ds.scale;this.dirty_bgcanvas=this.dirty_canvas=!0}if(this.resizing_node&&!this.live_mode){this.resizing_node.size[0]=a.canvasX-this.resizing_node.pos[0];this.resizing_node.size[1]=a.canvasY-this.resizing_node.pos[1];d=Math.max(this.resizing_node.inputs?this.resizing_node.inputs.length:0,this.resizing_node.outputs?this.resizing_node.outputs.length:0)*h.NODE_SLOT_HEIGHT+ +(this.resizing_node.widgets?this.resizing_node.widgets.length:0)*(h.NODE_WIDGET_HEIGHT+4)+4;this.resizing_node.size[1]this.dragging_rectangle[3]?this.dragging_rectangle[1]-f:this.dragging_rectangle[1];this.dragging_rectangle[0]=0>this.dragging_rectangle[2]?this.dragging_rectangle[0]-g:this.dragging_rectangle[0];this.dragging_rectangle[1]=r;this.dragging_rectangle[2]=g;this.dragging_rectangle[3]=f;f=[];for(r=0;ra.click_time&&w(a.canvasX,a.canvasY,g.pos[0],g.pos[1]-h.NODE_TITLE_HEIGHT,h.NODE_TITLE_HEIGHT,h.NODE_TITLE_HEIGHT)&&g.collapse();this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_dragged.pos[0]=Math.round(this.node_dragged.pos[0]); +this.node_dragged.pos[1]=Math.round(this.node_dragged.pos[1]);this.graph.config.align_to_grid&&this.node_dragged.alignToGrid();if(this.onNodeMoved)this.onNodeMoved(this.node_dragged);this.node_dragged=null}else{g=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes);!g&&300>a.click_time&&this.deselectAllNodes();this.dirty_canvas=!0;this.dragging_canvas=!1;if(this.node_over&&this.node_over.onMouseUp)this.node_over.onMouseUp(a,[a.canvasX-this.node_over.pos[0],a.canvasY-this.node_over.pos[1]], +this);if(this.node_capturing_input&&this.node_capturing_input.onMouseUp)this.node_capturing_input.onMouseUp(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]])}}else 2==a.which?(this.dirty_canvas=!0,this.dragging_canvas=!1):3==a.which&&(this.dirty_canvas=!0,this.dragging_canvas=!1);this.graph.change();a.stopPropagation();a.preventDefault();return!1}};e.prototype.processMouseWheel=function(a){if(this.graph&&this.allow_dragcanvas){var b=null!=a.wheelDeltaY?a.wheelDeltaY: +-60*a.detail;this.adjustMouseEvent(a);var d=this.ds.scale;0b&&(d*=1/1.1);this.ds.changeScale(d,[a.localX,a.localY]);this.graph.change();a.preventDefault();return!1}};e.prototype.isOverNodeBox=function(a,b,d){var g=h.NODE_TITLE_HEIGHT;return w(b,d,a.pos[0]+2,a.pos[1]+2-g,g-4,g-4)?!0:!1};e.prototype.isOverNodeInput=function(a,b,d,g){if(a.inputs)for(var f=0,h=a.inputs.length;fd-this.graph._last_trigger_time)&&this.drawBackCanvas();(this.dirty_canvas||a)&&this.drawFrontCanvas();this.fps=this.render_time?1/this.render_time:0;this.frame+=1}};e.prototype.drawFrontCanvas=function(){this.dirty_canvas=!1;this.ctx||(this.ctx=this.bgcanvas.getContext("2d"));var a=this.ctx;if(a){a.start2D&&a.start2D();var b=this.canvas;a.restore();a.setTransform(1,0,0,1,0,0);this.dirty_area&&(a.save(), +a.beginPath(),a.rect(this.dirty_area[0],this.dirty_area[1],this.dirty_area[2],this.dirty_area[3]),a.clip());this.clear_background&&a.clearRect(0,0,b.width,b.height);this.bgcanvas==this.canvas?this.drawBackCanvas():a.drawImage(this.bgcanvas,0,0);if(this.onRender)this.onRender(b,a);this.show_info&&this.renderInfo(a);if(this.graph){a.save();this.ds.toCanvasContext(a);for(var b=this.computeVisibleNodes(null,this.visible_nodes),d=0;d> ";b.fillText(g+d.getTitle(), +0.5*a.width,40);b.restore()}d=!1;this.onRenderBackground&&(d=this.onRenderBackground(a,b));b.restore();b.setTransform(1,0,0,1,0,0);this.visible_links.length=0;if(this.graph){b.save();this.ds.toCanvasContext(b);if(this.background_image&&0.5this.ds.scale;if(this.live_mode){if(!a.flags.collapsed&&(b.shadowColor="transparent",a.onDrawForeground))a.onDrawForeground(b,this,this.canvas)}else{var r= +this.editor_alpha;b.globalAlpha=r;this.render_shadows&&!f?(b.shadowColor=h.DEFAULT_SHADOW_COLOR,b.shadowOffsetX=2*this.ds.scale,b.shadowOffsetY=2*this.ds.scale,b.shadowBlur=3*this.ds.scale):b.shadowColor="transparent";if(!a.flags.collapsed||!a.onDrawCollapsed||!0!=a.onDrawCollapsed(b,this)){var s=a._shape||h.BOX_SHAPE;y.set(a.size);var c=a.horizontal;if(a.flags.collapsed){b.font=this.inner_text_font;var k=a.getTitle?a.getTitle():a.title;null!=k&&(a._collapsed_width=Math.min(a.size[0],b.measureText(k).width+ +2*h.NODE_TITLE_HEIGHT),y[0]=a._collapsed_width,y[1]=0)}a.clip_area&&(b.save(),b.beginPath(),s==h.BOX_SHAPE?b.rect(0,0,y[0],y[1]):s==h.ROUND_SHAPE?b.roundRect(0,0,y[0],y[1],10):s==h.CIRCLE_SHAPE&&b.arc(0.5*y[0],0.5*y[1],0.5*y[0],0,2*Math.PI),b.clip());a.has_errors&&(g="red");this.drawNodeShape(a,b,y,d,g,a.is_selected,a.mouseOver);b.shadowColor="transparent";if(a.onDrawForeground)a.onDrawForeground(b,this,this.canvas);b.textAlign=c?"center":"left";b.font=this.inner_text_font;g=!f;s=this.connecting_output; +b.lineWidth=1;var k=0,e=new Float32Array(2);if(!a.flags.collapsed){if(a.inputs)for(d=0;dthis.ds.scale,k=a._shape||a.constructor.shape||h.ROUND_SHAPE,p=a.constructor.title_mode,q=!0;p==h.TRANSPARENT_TITLE?q=!1:p==h.AUTOHIDE_TITLE&& +s&&(q=!0);v[0]=0;v[1]=q?-f:0;v[2]=d[0]+1;v[3]=q?d[1]+f:d[1];s=b.globalAlpha;b.beginPath();k==h.BOX_SHAPE||c?b.fillRect(v[0],v[1],v[2],v[3]):k==h.ROUND_SHAPE||k==h.CARD_SHAPE?b.roundRect(v[0],v[1],v[2],v[3],this.round_radius,k==h.CARD_SHAPE?0:this.round_radius):k==h.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0],0,2*Math.PI);b.fill();a.flags.collapsed||(b.shadowColor="transparent",b.fillStyle="rgba(0,0,0,0.2)",b.fillRect(0,-1,v[2],2));b.shadowColor="transparent";if(a.onDrawBackground)a.onDrawBackground(b, +this,this.canvas);if(q||p==h.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,f,d,this.ds.scale,g);else if(p!=h.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){q=a.constructor.title_color||g;a.flags.collapsed&&(b.shadowColor=h.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var t=e.gradients[q];t||(t=e.gradients[q]=b.createLinearGradient(0,0,400,0),t.addColorStop(0,q),t.addColorStop(1,"#000"));b.fillStyle=t}else b.fillStyle=q;b.beginPath();k==h.BOX_SHAPE||c?b.rect(0, +-f,d[0]+1,f):k!=h.ROUND_SHAPE&&k!=h.CARD_SHAPE||b.roundRect(0,-f,d[0]+1,f,this.round_radius,a.flags.collapsed?this.round_radius:0);b.fill();b.shadowColor="transparent"}if(a.onDrawTitleBox)a.onDrawTitleBox(b,f,d,this.ds.scale);else k==h.ROUND_SHAPE||k==h.CIRCLE_SHAPE||k==h.CARD_SHAPE?(c&&(b.fillStyle="black",b.beginPath(),b.arc(0.5*f,-0.5*f,6,0,2*Math.PI),b.fill()),b.fillStyle=a.boxcolor||h.NODE_DEFAULT_BOXCOLOR,c?b.fillRect(0.5*f-5,-0.5*f-5,10,10):(b.beginPath(),b.arc(0.5*f,-0.5*f,5,0,2*Math.PI), +b.fill())):(c&&(b.fillStyle="black",b.fillRect(0.5*(f-10)-1,-0.5*(f+10)-1,12,12)),b.fillStyle=a.boxcolor||h.NODE_DEFAULT_BOXCOLOR,b.fillRect(0.5*(f-10),-0.5*(f+10),10,10));b.globalAlpha=s;if(a.onDrawTitleText)a.onDrawTitleText(b,f,d,this.ds.scale,this.title_text_font,r);!c&&(b.font=this.title_text_font,c=String(a.getTitle()))&&(b.fillStyle=r?"white":a.constructor.title_text_color||this.node_title_color,a.flags.collapsed?(b.textAlign="left",b.measureText(c),b.fillText(c.substr(0,20),f,h.NODE_TITLE_TEXT_Y- +f),b.textAlign="left"):(b.textAlign="left",b.fillText(c,f,h.NODE_TITLE_TEXT_Y-f)));if(a.onDrawTitle)a.onDrawTitle(b)}if(r){if(a.onBounding)a.onBounding(v);p==h.TRANSPARENT_TITLE&&(v[1]-=f,v[3]+=f);b.lineWidth=1;b.globalAlpha=0.8;b.beginPath();k==h.BOX_SHAPE?b.rect(-6+v[0],-6+v[1],12+v[2],12+v[3]):k==h.ROUND_SHAPE||k==h.CARD_SHAPE&&a.flags.collapsed?b.roundRect(-6+v[0],-6+v[1],12+v[2],12+v[3],2*this.round_radius):k==h.CARD_SHAPE?b.roundRect(-6+v[0],-6+v[1],12+v[2],12+v[3],2*this.round_radius,2):k== +h.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0]+6,0,2*Math.PI);b.strokeStyle="#FFF";b.stroke();b.strokeStyle=g;b.globalAlpha=1}};var G=new Float32Array(4),p=new Float32Array(4),q=new Float32Array(2),k=new Float32Array(2);e.prototype.drawConnections=function(a){var b=h.getTime(),d=this.visible_area;G[0]=d[0]-20;G[1]=d[1]-20;G[2]=d[2]+40;G[3]=d[3]+40;a.lineWidth=this.connections_width;a.fillStyle="#AAA";a.strokeStyle="#AAA";a.globalAlpha=this.editor_alpha;for(var d=this.graph._nodes,g=0,f=d.length;g< +f;++g){var r=d[g];if(r.inputs&&r.inputs.length)for(var s=0;sp[2]&&(p[0]+=p[2],p[2]=Math.abs(p[2]));0>p[3]&&(p[1]+=p[3],p[3]=Math.abs(p[3]));if(u(p,G)){var n=e.outputs[z],z=r.inputs[s];if(n&& +z&&(e=n.dir||(e.horizontal?h.DOWN:h.RIGHT),z=z.dir||(r.horizontal?h.UP:h.LEFT),this.renderLink(a,l,t,c,!1,0,null,e,z),c&&c._last_time&&1E3>b-c._last_time)){var n=2-0.002*(b-c._last_time),K=a.globalAlpha;a.globalAlpha=K*n;this.renderLink(a,l,t,c,!0,n,"white",e,z);a.globalAlpha=K}}}}}}a.globalAlpha=1};e.prototype.renderLink=function(a,b,d,g,f,r,c,k,p,q){g&&this.visible_links.push(g);!c&&g&&(c=g.color||e.link_type_colors[g.type]);c||(c=this.default_link_color);null!=g&&this.highlighted_links[g.id]&& +(c="#FFF");k=k||h.RIGHT;p=p||h.LEFT;var z=C(b,d);this.render_connections_border&&0.6b[1]?0:Math.PI,a.save(),a.translate(t[0],t[1]),a.rotate(l),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore(),a.save(),a.translate(q[0],q[1]),a.rotate(n), +a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill());if(r)for(a.fillStyle=c,t=0;5>t;++t)r=(0.001*h.getTime()+0.2*t)%1,f=this.computeConnectionPoint(b,d,r,k,p),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill()};e.prototype.computeConnectionPoint=function(a,b,d,g,f){g=g||h.RIGHT;f=f||h.LEFT;var r=C(a,b),c=[a[0],a[1]],k=[b[0],b[1]];switch(g){case h.LEFT:c[0]+=-0.25*r;break;case h.RIGHT:c[0]+=0.25*r;break;case h.UP:c[1]+= +-0.25*r;break;case h.DOWN:c[1]+=0.25*r}switch(f){case h.LEFT:k[0]+=-0.25*r;break;case h.RIGHT:k[0]+=0.25*r;break;case h.UP:k[1]+=-0.25*r;break;case h.DOWN:k[1]+=0.25*r}g=(1-d)*(1-d)*(1-d);f=3*(1-d)*(1-d)*d;r=3*(1-d)*d*d;d*=d*d;return[g*a[0]+f*c[0]+r*k[0]+d*b[0],g*a[1]+f*c[1]+r*k[1]+d*b[1]]};e.prototype.drawExecutionOrder=function(a){a.shadowColor="transparent";a.globalAlpha=0.25;a.textAlign="center";a.strokeStyle="white";a.globalAlpha=0.75;for(var b=this.visible_nodes,d=0;dt.last_y&&s +t.options.max&&(t.value=t.options.max);else if("mousedown"==d.type){var l=t.options.values;l&&l.constructor===Function&&(l=t.options.values(t,a));var z=null;"number"!=t.type&&(z=l.constructor===Array?l:Object.keys(l));c=40>c?-1:c>k-40?1:0;if("number"==t.type)t.value+=0.1*c*(t.options.step||1),null!=t.options.min&&t.valuet.options.max&&(t.value=t.options.max);else if(c)e=-1,e=l.constructor===Object?z.indexOf(String(t.value))+c:z.indexOf(t.value)+ +c,e>=z.length&&(e=z.length-1),0>e&&(e=0),t.value=l.constructor===Array?l[e]:e;else{var n=l!=z?Object.values(l):l;new h.ContextMenu(n,{scale:Math.max(1,this.ds.scale),event:d,className:"dark",callback:y.bind(t)},e);var y=function(a,b,d){l!=z&&(a=n.indexOf(a));this.value=a;f(this,a);p.dirty_canvas=!0;return!1}}}else"mouseup"==d.type&&"number"==t.type&&(c=40>c?-1:c>k-40?1:0,200>d.click_time&&0==c&&this.prompt("Value",t.value,function(a){this.value=Number(a);f(this,this.value)}.bind(t),d));g!=t.value&& +setTimeout(function(){f(this,this.value)}.bind(t),20);this.dirty_canvas=!0;break;case "toggle":"mousedown"==d.type&&(t.value=!t.value,setTimeout(function(){f(t,t.value)},20));break;case "string":case "text":"mousedown"==d.type&&this.prompt("Value",t.value,function(a){this.value=a;f(this,a)}.bind(t),d);break;default:t.mouse&&t.mouse(ctx,d,[c,s],a)}return t}}return null};e.prototype.drawGroups=function(a,b){if(this.graph){var d=this.graph._groups;b.save();b.globalAlpha=0.5*this.editor_alpha;for(var g= +0;gd&&0.01>b.editor_alpha&&(clearInterval(g),1>d&&(b.live_mode=!0));1"+ +q+""+a+"",value:q});if(p.length)return new h.ContextMenu(p,{event:d,callback:c,parentMenu:g,allow_html:!0,node:f},b),!1}};e.decodeHTML=function(a){var b=document.createElement("div");b.innerText=a;return b.innerHTML};e.onResizeNode=function(a,b,d,g,f){f&&(f.size=f.computeSize(),f.setDirtyCanvas(!0,!0))};e.prototype.showLinkMenu=function(a,b){var d=this;console.log(a);var g=new h.ContextMenu(["Add Node",null,"Delete"],{event:b,title:null!=a.data?a.data.constructor.name: +null,callback:function(b,h,c){switch(b){case "Add Node":e.onMenuAdd(null,null,c,g,function(b){console.log("node autoconnect");var f=d.graph.getNodeById(a.origin_id),g=d.graph.getNodeById(a.target_id);b.inputs&&b.inputs.length&&b.outputs&&b.outputs.length&&f.outputs[a.origin_slot].type==b.inputs[0].type&&b.outputs[0].type==g.inputs[0].type&&(f.connect(a.origin_slot,b,0),b.connect(0,g,a.target_slot),b.pos[0]-=0.5*b.size[0])});break;case "Delete":d.graph.removeLink(a.id)}}});return!1};e.onShowPropertyEditor= +function(a,b,d,g,f){function h(){var b=p.value;"Number"==a.type?b=Number(b):"Boolean"==a.type&&(b=Boolean(b));f[c]=b;k.parentNode&&k.parentNode.removeChild(k);f.setDirtyCanvas(!0,!0)}var c=a.property||"title";b=f[c];var k=document.createElement("div");k.className="graphdialog";k.innerHTML="";k.querySelector(".name").innerText=c;var p=k.querySelector("input");p&&(p.value=b,p.addEventListener("blur",function(a){this.focus()}), +p.addEventListener("keydown",function(a){13==a.keyCode&&(h(),a.preventDefault(),a.stopPropagation())}));b=e.active_canvas.canvas;d=b.getBoundingClientRect();var q=g=-20;d&&(g-=d.left,q-=d.top);event?(k.style.left=event.clientX+g+"px",k.style.top=event.clientY+q+"px"):(k.style.left=0.5*b.width+g+"px",k.style.top=0.5*b.height+q+"px");k.querySelector("button").addEventListener("click",h);b.parentNode.appendChild(k)};e.prototype.prompt=function(a,b,d,g){var f=this;a=a||"";var h=!1,c=document.createElement("div"); +c.className="graphdialog rounded";c.innerHTML=" ";c.close=function(){f.prompt_box=null;c.parentNode&&c.parentNode.removeChild(c)};1e.search_limit)break}}s=null;if(Array.prototype.filter)s=Object.keys(h.registered_node_types).filter(l);else for(k in s=[],h.registered_node_types)l(k)&&s.push(k);for(k=0;ke.search_limit);k++);var l=function(a){var b=h.registered_node_types[a];return p&&b.filter!=p?!1:-1!==a.toLowerCase().indexOf(d)}}}var f=this,c=e.active_canvas,k=c.canvas,p=k.ownerDocument||document, +q=document.createElement("div");q.className="litegraph litesearchbox graphdialog rounded";q.innerHTML="Search
";q.close=function(){f.search_box=null;p.body.focus();p.body.style.overflow="";setTimeout(function(){f.canvas.focus()},20);q.parentNode&&q.parentNode.removeChild(q)};var l=null;1k.height-200&&(z.style.maxHeight=k.height-a.layerY-20+"px");u.focus();return q};e.prototype.showEditPropertyValue=function(a,b,d){function g(){f(t.value)} +function f(f){"number"==typeof a.properties[b]&&(f=Number(f));if("array"==h||"object"==h)f=JSON.parse(f);a.properties[b]=f;a._graph&&a._graph._version++;if(a.onPropertyChanged)a.onPropertyChanged(b,f);if(d.onclose)d.onclose();e.close();a.setDirtyCanvas(!0,!0)}if(a&&void 0!==a.properties[b]){d=d||{};var c=a.getPropertyInfo(b),h=c.type,k="";if("string"==h||"number"==h||"array"==h||"object"==h)k="";else if("enum"==h&&c.values){var k=""}else if("boolean"==h)k="";else{console.warn("unknown type: "+h);return}var e=this.createDialog(""+b+""+k+"",d);if("enum"==h&&c.values){var t=e.querySelector("select");t.addEventListener("change", +function(a){f(a.target.value)})}else if("boolean"==h)(t=e.querySelector("input"))&&t.addEventListener("click",function(a){f(!!t.checked)});else if(t=e.querySelector("input"))t.addEventListener("blur",function(a){this.focus()}),q=void 0!==a.properties[b]?a.properties[b]:"",q=JSON.stringify(q),t.value=q,t.addEventListener("keydown",function(a){13==a.keyCode&&(g(),a.preventDefault(),a.stopPropagation())});e.querySelector("button").addEventListener("click",g);return e}};e.prototype.createDialog=function(a, +b){b=b||{};var d=document.createElement("div");d.className="graphdialog";d.innerHTML=a;var g=this.canvas.getBoundingClientRect(),f=-20,c=-20;g&&(f-=g.left,c-=g.top);b.position?(f+=b.position[0],c+=b.position[1]):b.event?(f+=b.event.clientX,c+=b.event.clientY):(f+=0.5*this.canvas.width,c+=0.5*this.canvas.height);d.style.left=f+"px";d.style.top=c+"px";this.canvas.parentNode.appendChild(d);d.close=function(){this.parentNode&&this.parentNode.removeChild(this)};return d};e.onMenuNodeCollapse=function(a, +b,d,g,f){f.collapse()};e.onMenuNodePin=function(a,b,d,g,f){f.pin()};e.onMenuNodeMode=function(a,b,d,g,f){new h.ContextMenu(["Always","On Event","On Trigger","Never"],{event:d,callback:function(a){if(f)switch(a){case "On Event":f.mode=h.ON_EVENT;break;case "On Trigger":f.mode=h.ON_TRIGGER;break;case "Never":f.mode=h.NEVER;break;default:f.mode=h.ALWAYS}},parentMenu:g,node:f});return!1};e.onMenuNodeColors=function(a,b,d,g,f){if(!f)throw"no node for color";b=[];b.push({value:null,content:"No color"}); +for(var c in e.node_colors)a=e.node_colors[c],a={value:c,content:""+c+""},b.push(a);new h.ContextMenu(b,{event:d,callback:function(a){f&&((a=a.value?e.node_colors[a.value]:null)?f.constructor===h.LGraphGroup?f.color=a.groupcolor:(f.color=a.color,f.bgcolor=a.bgcolor):(delete f.color,delete f.bgcolor),f.setDirtyCanvas(!0,!0))},parentMenu:g,node:f});return!1};e.onMenuNodeShapes= +function(a,b,d,g,f){if(!f)throw"no node passed";new h.ContextMenu(h.VALID_SHAPES,{event:d,callback:function(a){f&&(f.shape=a,f.setDirtyCanvas(!0))},parentMenu:g,node:f});return!1};e.onMenuNodeRemove=function(a,b,d,g,f){if(!f)throw"no node passed";!1!==f.removable&&(f.graph.remove(f),f.setDirtyCanvas(!0,!0))};e.onMenuNodeClone=function(a,b,d,g,f){!1!=f.clonable&&(a=f.clone())&&(a.pos=[f.pos[0]+5,f.pos[1]+5],f.graph.add(a),f.setDirtyCanvas(!0,!0))};e.node_colors={red:{color:"#322",bgcolor:"#533",groupcolor:"#A88"}, +brown:{color:"#332922",bgcolor:"#593930",groupcolor:"#b06634"},green:{color:"#232",bgcolor:"#353",groupcolor:"#8A8"},blue:{color:"#223",bgcolor:"#335",groupcolor:"#88A"},pale_blue:{color:"#2a363b",bgcolor:"#3f5159",groupcolor:"#3f789e"},cyan:{color:"#233",bgcolor:"#355",groupcolor:"#8AA"},purple:{color:"#323",bgcolor:"#535",groupcolor:"#a1309b"},yellow:{color:"#432",bgcolor:"#653",groupcolor:"#b58b2a"},black:{color:"#222",bgcolor:"#000",groupcolor:"#444"}};e.prototype.getCanvasMenuOptions=function(){var a= +null;this.getMenuOptions?a=this.getMenuOptions():(a=[{content:"Add Node",has_submenu:!0,callback:e.onMenuAdd},{content:"Add Group",callback:e.onGroupAdd}],this._graph_stack&&0Name",f),k=h.querySelector("input");k&&c&&(k.value=c.label||"");h.querySelector("button").addEventListener("click",function(a){k.value&& +(c&&(c.label=k.value),d.setDirty(!0));h.close()})}},extra:a};a&&(c.title=a.type);var k=null;a&&(k=a.getSlotInPosition(b.canvasX,b.canvasY),e.active_node=a);if(k){f=[];k&&k.output&&k.output.links&&k.output.links.length&&f.push({content:"Disconnect Links",slot:k});var p=k.input||k.output;f.push(p.locked?"Cannot remove":{content:"Remove Slot",slot:k});f.push(p.nameLocked?"Cannot rename":{content:"Rename Slot",slot:k});c.title=(k.input?k.input.type:k.output.type)||"*";k.input&&k.input.type==h.ACTION&& +(c.title="Action");k.output&&k.output.type==h.EVENT&&(c.title="Event")}else a?f=this.getNodeMenuOptions(a):(f=this.getCanvasMenuOptions(),(k=this.graph.getGroupOnPos(b.canvasX,b.canvasY))&&f.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:k,options:this.getGroupMenuOptions(k)}}));f&&new h.ContextMenu(f,c,g)};"undefined"!=typeof window&&window.CanvasRenderingContext2D&&(window.CanvasRenderingContext2D.prototype.roundRect=function(a,b,d,g,f,c){void 0===f&&(f=5);void 0=== +c&&(c=f);this.moveTo(a+f,b);this.lineTo(a+d-f,b);this.quadraticCurveTo(a+d,b,a+d,b+f);this.lineTo(a+d,b+g-c);this.quadraticCurveTo(a+d,b+g,a+d-c,b+g);this.lineTo(a+c,b+g);this.quadraticCurveTo(a,b+g,a,b+g-c);this.lineTo(a,b+f);this.quadraticCurveTo(a,b,a+f,b)});h.compareObjects=function(a,b){for(var d in a)if(a[d]!=b[d])return!1;return!0};h.distance=C;h.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+","+Math.round(255*a[2]).toFixed()+","+ +(4==a.length?a[3].toFixed(2):"1.0")+")"};h.isInsideRectangle=w;h.growBounding=function(a,b,d){ba[2]&&(a[2]=b);da[3]&&(a[3]=d)};h.isInsideBounding=function(a,b){return a[0]b[1][0]||a[1]>b[1][1]?!1:!0};h.overlapBounding=u;h.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),d=0,g,f,c=0;6>c;c+=2)g="0123456789ABCDEF".indexOf(a.charAt(c)),f="0123456789ABCDEF".indexOf(a.charAt(c+1)),b[d]=16*g+f,d++;return b}; +h.num2hex=function(a){for(var b="#",d,g,f=0;3>f;f++)d=a[f]/16,g=a[f]%16,b+="0123456789ABCDEF".charAt(d)+"0123456789ABCDEF".charAt(g);return b};D.prototype.addItem=function(a,b,d){function g(a){var b=this.value;b&&b.has_submenu&&f.call(this,a)}function f(a){var b=this.value,f=!0;c.current_submenu&&c.current_submenu.close(a);if(d.callback){var g=d.callback.call(this,b,d,a,c,d.node);!0===g&&(f=!1)}if(b&&(b.callback&&!d.ignore_item_callbacks&&!0!==b.disabled&&(g=b.callback.call(this,b,d,a,c,d.extra), +!0===g&&(f=!1)),b.submenu)){if(!b.submenu.options)throw"ContextMenu submenu needs options";new c.constructor(b.submenu.options,{callback:b.submenu.callback,event:a,parentMenu:c,ignore_item_callbacks:b.submenu.ignore_item_callbacks,title:b.submenu.title,extra:b.submenu.extra,autoopen:d.autoopen});f=!1}f&&!c.lock&&c.close()}var c=this;d=d||{};var h=document.createElement("div");h.className="litemenu-entry submenu";var k=!1;if(null===b)h.classList.add("separator");else{h.innerHTML=b&&b.title?b.title: +a;if(h.value=b)b.disabled&&(k=!0,h.classList.add("disabled")),(b.submenu||b.has_submenu)&&h.classList.add("has_submenu");"function"==typeof b?(h.dataset.value=a,h.onclick_callback=b):h.dataset.value=b;b.className&&(h.className+=" "+b.className)}this.root.appendChild(h);k||h.addEventListener("click",f);d.autoopen&&h.addEventListener("mouseenter",g);return h};D.prototype.close=function(a,b){this.root.parentNode&&this.root.parentNode.removeChild(this.root);this.parentMenu&&!b&&(this.parentMenu.lock= +!1,this.parentMenu.current_submenu=null,void 0===a?this.parentMenu.close():a&&!D.isCursorOverElement(a,this.parentMenu.root)&&D.trigger(this.parentMenu.root,"mouseleave",a));this.current_submenu&&this.current_submenu.close(a,!0);this.root.closing_timer&&clearTimeout(this.root.closing_timer)};D.trigger=function(a,b,d,g){var f=document.createEvent("CustomEvent");f.initCustomEvent(b,!0,!0,d);f.srcElement=g;a.dispatchEvent?a.dispatchEvent(f):a.__events&&a.__events.dispatchEvent(f);return f};D.prototype.getTopMenu= +function(){return this.options.parentMenu?this.options.parentMenu.getTopMenu():this};D.prototype.getFirstEvent=function(){return this.options.parentMenu?this.options.parentMenu.getFirstEvent():this.options.event};D.isCursorOverElement=function(a,b){var d=a.clientX,g=a.clientY,f=b.getBoundingClientRect();return f?g>f.top&&gf.left&&dMath.abs(d))return g[1];d=(a-g[0])/d;return g[1]*(1-d)+f[1]*d}}return 0}};A.prototype.draw=function(a,b,d,g,f,c){if(d=this.points){this.size=b;var h=b[0]-2*this.margin;b=b[1]-2*this.margin;f=f||"#666";a.save();a.translate(this.margin,this.margin);g&&(a.fillStyle="#111",a.fillRect(0,0,h,b),a.fillStyle="#222",a.fillRect(0.5*h,0,1,b),a.strokeStyle="#333",a.strokeRect(0,0,h,b));a.strokeStyle= +f;c&&(a.globalAlpha=0.5);a.beginPath();for(g=0;ga[1])){var g=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=a[0]-this.margin,h=a[1]-this.margin;this.selected=this.getCloserPoint([c, +h],30/b.ds.scale);-1==this.selected&&(g=[c/g,1-h/f],d.push(g),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(g),this.must_update=!0);if(-1!=this.selected)return!0}};A.prototype.onMouseMove=function(a,b){var d=this.points;if(d){var g=this.selected;if(!(0>g)){var f=(a[0]-this.margin)/(this.size[0]-2*this.margin),c=(a[1]-this.margin)/(this.size[1]-2*this.margin);this._nearest=this.getCloserPoint([a[0]-this.margin,a[1]-this.margin],30/b.ds.scale);var h=d[g];if(h){var k=0==g||g==d.length- +1;!k&&(-10>a[0]||a[0]>this.size[0]+10||-10>a[1]||a[1]>this.size[1]+10)?(d.splice(g,1),this.selected=-1):(h[0]=k?0==g?0:1:Math.clamp(f,0,1),h[1]=1-Math.clamp(c,0,1),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(h),this.must_update=!0)}}}};A.prototype.onMouseUp=function(a,b){this.selected=-1;return!1};A.prototype.getCloserPoint=function(a,b){var d=this.points;if(!d)return-1;b=b||30;for(var g=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=d.length,h=[0,0],k=1E6,p=-1,q=0;q< +c;++q){var e=d[q];h[0]=e[0]*g;h[1]=(1-e[1])*f;e=vec2.distance(a,h);e>k||e>b||(p=q,k=e)}return p};h.CurveEditor=A;h.getParameterNames=function(a){return(a+"").replace(/[/][/].*$/gm,"").replace(/\s+/g,"").replace(/[/][*][^/*]*[*][/]/g,"").split("){",1)[0].replace(/^[^(]*[(]/,"").replace(/=[^,]+/g,"").split(",").filter(Boolean)};Math.clamp=function(a,b,d){return b>a?b:d body_rect.width - root_rect.width - 10) { + if (body_rect.width && left > body_rect.width - root_rect.width - 10) { left = body_rect.width - root_rect.width - 10; } - if (top > body_rect.height - root_rect.height - 10) { + if (body_rect.height && top > body_rect.height - root_rect.height - 10) { top = body_rect.height - root_rect.height - 10; } } From 3087acad0096ed8e8b09aa2283c66ee53f773651 Mon Sep 17 00:00:00 2001 From: tamat Date: Sun, 3 May 2020 13:47:49 +0200 Subject: [PATCH 27/63] 0.7.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 58aa2554d..c21e75114 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "litegraph.js", - "version": "0.7.7", + "version": "0.7.8", "description": "A graph node editor similar to PD or UDK Blueprints, it works in a HTML5 Canvas and allow to exported graphs to be included in applications.", "main": "build/litegraph.js", "types": "src/litegraph.d.ts", From a1c312fab90baf2bbae8000b29b674bea430ec01 Mon Sep 17 00:00:00 2001 From: tamat Date: Tue, 5 May 2020 12:57:59 +0200 Subject: [PATCH 28/63] fix on onConnectInput to have more info --- build/litegraph.js | 25 +++++++++------------ build/litegraph.min.js | 50 ++++++++++++++++++++--------------------- src/litegraph.js | 5 +---- src/nodes/gltextures.js | 20 ++++++++--------- 4 files changed, 45 insertions(+), 55 deletions(-) diff --git a/build/litegraph.js b/build/litegraph.js index e4e226921..0fd7b38f4 100644 --- a/build/litegraph.js +++ b/build/litegraph.js @@ -3591,10 +3591,7 @@ //allows nodes to block connection if (target_node.onConnectInput) { - if ( - target_node.onConnectInput(target_slot, output.type, output) === - false - ) { + if ( target_node.onConnectInput(target_slot, output.type, output, this, slot) === false ) { return null; } } @@ -19684,8 +19681,8 @@ void main() {\n\ }; this._uniforms = { u_texture: 0, - u_near: 0.1, - u_far: 10000 + u_camera_planes: null, //filled later + u_ires: vec2.create() }; } @@ -19717,9 +19714,6 @@ void main() {\n\ } var uniforms = this._uniforms; - - uniforms.u_near = tex.near_far_planes[0]; - uniforms.u_far = tex.near_far_planes[1]; uniforms.u_invert = this.properties.invert ? 1 : 0; gl.disable(gl.BLEND); @@ -19739,6 +19733,8 @@ void main() {\n\ planes = [0.1, 1000]; } //hardcoded uniforms.u_camera_planes = planes; + //uniforms.u_ires.set([1/tex.width, 1/tex.height]); + uniforms.u_ires.set([0,0]); this._temp_texture.drawTo(function() { tex.bind(0); @@ -19754,15 +19750,14 @@ void main() {\n\ precision highp float;\n\ varying vec2 v_coord;\n\ uniform sampler2D u_texture;\n\ - uniform float u_near;\n\ - uniform float u_far;\n\ + uniform vec2 u_camera_planes;\n\ uniform int u_invert;\n\ + uniform vec2 u_ires;\n\ \n\ void main() {\n\ - float zNear = u_near;\n\ - float zFar = u_far;\n\ - float depth = texture2D(u_texture, v_coord).x;\n\ - depth = depth * 2.0 - 1.0;\n\ + float zNear = u_camera_planes.x;\n\ + float zFar = u_camera_planes.y;\n\ + float depth = texture2D(u_texture, v_coord + u_ires*0.5).x * 2.0 - 1.0;\n\ float f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\ if( u_invert == 1 )\n\ f = 1.0 - f;\n\ diff --git a/build/litegraph.min.js b/build/litegraph.min.js index 92bc038e6..b0d422061 100755 --- a/build/litegraph.min.js +++ b/build/litegraph.min.js @@ -77,7 +77,7 @@ this.widgets.push(b);this.size=this.computeSize();return b};n.prototype.addCusto 0);if(this.flags&&this.flags.collapsed){if(w(a,b,this.pos[0]-d,this.pos[1]-h.NODE_TITLE_HEIGHT-d,(this._collapsed_width||h.NODE_COLLAPSED_WIDTH)+2*d,h.NODE_TITLE_HEIGHT+2*d))return!0}else if(this.pos[0]-4-da&&this.pos[1]-f-db)return!0;return!1};n.prototype.getSlotInPosition=function(a,b){var d=new Float32Array(2);if(this.inputs)for(var g=0,f=this.inputs.length;g=this.outputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),null;b&&b.constructor===Number&&(b=this.graph.getNodeById(b)); -if(!b)throw"target node is null";if(b==this)return null;if(d.constructor===String){if(d=b.findInputSlot(d),-1==d)return h.debug&&console.log("Connect: Error, no slot of name "+d),null}else{if(d===h.EVENT)return null;if(!b.inputs||d>=b.inputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),null}null!=b.inputs[d].link&&b.disconnectInput(d);var g=this.outputs[a];if(b.onConnectInput&&!1===b.onConnectInput(d,g.type,g))return null;var f=b.inputs[d],r=null;if(h.isValidConnection(g.type, +if(!b)throw"target node is null";if(b==this)return null;if(d.constructor===String){if(d=b.findInputSlot(d),-1==d)return h.debug&&console.log("Connect: Error, no slot of name "+d),null}else{if(d===h.EVENT)return null;if(!b.inputs||d>=b.inputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),null}null!=b.inputs[d].link&&b.disconnectInput(d);var g=this.outputs[a];if(b.onConnectInput&&!1===b.onConnectInput(d,g.type,g,this,a))return null;var f=b.inputs[d],r=null;if(h.isValidConnection(g.type, f.type)){r=new m(++this.graph.last_link_id,f.type,this.id,a,b.id,d);this.graph.links[r.id]=r;null==g.links&&(g.links=[]);g.links.push(r.id);b.inputs[d].link=r.id;this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(h.OUTPUT,a,!0,r,g);if(b.onConnectionsChange)b.onConnectionsChange(h.INPUT,d,!0,r,f);this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(h.INPUT,b,d,this,a),this.graph.onNodeConnectionChange(h.OUTPUT,this,a,b,d))}this.setDirtyCanvas(!1, !0);this.graph.connectionChange(this,r);return r};n.prototype.disconnectOutput=function(a,b){if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return h.debug&&console.log("Connect: Error, no slot of name "+a),!1}else if(!this.outputs||a>=this.outputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),!1;var d=this.outputs[a];if(!d||!d.links||0==d.links.length)return!1;if(b){b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"Target Node not found"; for(var g=0,f=d.links.length;gb;++b)a[b]=255*Math.random();return this._noise_texture=a=GL.Texture.fromMemory(512, -512,a,{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.prototype.onDropFile=function(a,b,d){if(a){var c=null;"string"==typeof a?c=GL.Texture.fromURL(a):-1!=b.toLowerCase().indexOf(".dds")?c=GL.Texture.fromDDSInMemory(a):(a=new Blob([d]),a=URL.createObjectURL(a),c=GL.Texture.fromURL(a));this._drop_texture=c;this.properties.name=b}else this._drop_texture=null,this.properties.name=""},c.prototype.getExtraMenuOptions=function(a){var b=this;if(this._drop_texture)return[{content:"Clear",callback:function(){b._drop_texture= -null;b.properties.name=""}}]},c.prototype.onExecute=function(){var a=null;this.isOutputConnected(1)&&(a=this.getInputData(0));!a&&this._drop_texture&&(a=this._drop_texture);!a&&this.properties.name&&(a=c.getTexture(this.properties.name));if(a){this._last_tex=a;!1===this.properties.filter?a.setParameter(gl.TEXTURE_MAG_FILTER,gl.NEAREST):a.setParameter(gl.TEXTURE_MAG_FILTER,gl.LINEAR);this.setOutputData(0,a);this.setOutputData(1,a.fullpath||a.filename);for(var b=2;b=this.size[1]))if(this._drop_texture&&a.webgl)a.drawImage(this._drop_texture,0,0,this.size[0],this.size[1]);else{if(this._last_preview_tex!=this._last_tex)if(a.webgl)this._canvas= -this._last_tex;else{var b=c.generateLowResTexturePreview(this._last_tex);if(!b)return;this._last_preview_tex=this._last_tex;this._canvas=cloneCanvas(b)}this._canvas&&(a.save(),a.webgl||(a.translate(0,this.size[1]),a.scale(1,-1)),a.drawImage(this._canvas,0,0,this.size[0],this.size[1]),a.restore())}},c.generateLowResTexturePreview=function(a){if(!a)return null;var b=c.image_preview_size,d=a;if(a.format==gl.DEPTH_COMPONENT)return null;if(a.width>b||a.height>b)d=this._preview_temp_tex,this._preview_temp_tex|| -(this._preview_temp_tex=d=new GL.Texture(b,b,{minFilter:gl.NEAREST})),a.copyTo(d);a=this._preview_canvas;a||(this._preview_canvas=a=createCanvas(b,b));d&&d.toCanvas(a);return a},c.prototype.getResources=function(a){this.properties.name&&(a[this.properties.name]=GL.Texture);return a},c.prototype.onGetInputs=function(){return[["in","Texture"]]},c.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["aspect","number"]]},c.replaceCode=function(a,b){return a.replace(/\{\{[a-zA-Z0-9_]*\}\}/g, -function(a){a=a.replace(/[\{\}]/g,"");return b[a]||""})},F.registerNodeType("texture/texture",c),m.title="Preview",m.desc="Show a texture in the graph canvas",m.allow_preview=!1,m.prototype.onDrawBackground=function(a){if(!this.flags.collapsed&&(a.webgl||m.allow_preview)){var b=this.getInputData(0);if(b){var d=null,d=!b.handle&&a.webgl?b:c.generateLowResTexturePreview(b);a.save();this.properties.flipY&&(a.translate(0,this.size[1]),a.scale(1,-1));a.drawImage(d,0,0,this.size[0],this.size[1]);a.restore()}}}, -F.registerNodeType("texture/preview",m),n.title="Save",n.desc="Save a texture in the repository",n.prototype.getPreviewTexture=function(){return this._texture},n.prototype.onExecute=function(){var a=this.getInputData(0);a&&(this.properties.generate_mipmaps&&(a.bind(0),a.setParameter(gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_LINEAR),gl.generateMipmap(a.texture_type),a.unbind(0)),this.properties.name&&(c.storeTexture?c.storeTexture(this.properties.name,a):c.getTexturesContainer()[this.properties.name]= -a),this._texture=a,this.setOutputData(0,a),this.setOutputData(1,this.properties.name))},F.registerNodeType("texture/save",n),l.widgets_info={uvcode:{widget:"code"},pixelcode:{widget:"code"},precision:{widget:"combo",values:c.MODE_VALUES}},l.title="Operation",l.desc="Texture shader operation",l.presets={},l.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:b.properties.show?"Hide Texture":"Show Texture",callback:function(){b.properties.show=!b.properties.show}}]},l.prototype.onPropertyChanged= -function(){this.has_error=!1},l.prototype.onDrawBackground=function(a){this.flags.collapsed||20>=this.size[1]||!this.properties.show||!this._tex||this._tex.gl!=a||(a.save(),a.drawImage(this._tex,0,0,this.size[0],this.size[1]),a.restore())},l.prototype.onExecute=function(){var a=this.getInputData(0);if(this.isOutputConnected(0))if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else{var b=this.getInputData(1);if(this.properties.uvcode||this.properties.pixelcode){var d=512,g=512; -a?(d=a.width,g=a.height):b&&(d=b.width,g=b.height);b||(b=GL.Texture.getWhiteTexture());var f=c.getTextureType(this.properties.precision,a);this._tex=a||this._tex?c.getTargetTexture(a||this._tex,this._tex,this.properties.precision):new GL.Texture(d,g,{type:f,format:gl.RGBA,filter:gl.LINEAR});f="";this.properties.uvcode&&(f="uv = "+this.properties.uvcode,-1!=this.properties.uvcode.indexOf(";")&&(f=this.properties.uvcode));var e="";this.properties.pixelcode&&(e="result = "+this.properties.pixelcode, --1!=this.properties.pixelcode.indexOf(";")&&(e=this.properties.pixelcode));var k=this._shader;if(!(this.has_error||k&&this._shader_code==f+"|"+e)){var h=c.replaceCode(l.pixel_shader,{UV_CODE:f,PIXEL_CODE:e});try{k=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,h),this.boxcolor="#00FF00"}catch(p){GL.Shader.dumpErrorToConsole(p,Shader.SCREEN_VERTEX_SHADER,h);this.boxcolor="#FF0000";this.has_error=!0;return}this._shader=k;this._shader_code=f+"|"+e}if(this._shader){var q=this.getInputData(2);null!=q?this.properties.value= -q:q=parseFloat(this.properties.value);var r=this.graph.getTime();this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);gl.disable(gl.BLEND);a&&a.bind(0);b&&b.bind(1);var c=Mesh.getScreenQuad();k.uniforms({u_texture:0,u_textureB:1,value:q,texSize:[d,g],time:r}).draw(c)});this.setOutputData(0,this._tex)}}}},l.pixel_shader="precision highp float;\n\t\t\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec2 texSize;\n\t\tuniform float time;\n\t\tuniform float value;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord;\n\t\t\t{{UV_CODE}};\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\tvec3 color = color4.rgb;\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\tvec3 colorB = color4B.rgb;\n\t\t\tvec3 result = color;\n\t\t\tfloat alpha = 1.0;\n\t\t\t{{PIXEL_CODE}};\n\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t}\n\t\t", +this._uniforms={u_texture:0,u_distance:100,u_range:50,u_camera_planes:null}}function f(){this.addInput("Texture","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,invert:!1};this._uniforms={u_texture:0,u_camera_planes:null,u_ires:vec2.create()}}function r(){this.addInput("Texture","Texture");this.addInput("Iterations","number");this.addInput("Intensity","number");this.addOutput("Blurred","Texture");this.properties={intensity:1,iterations:1,preserve_aspect:!1,scale:[1, +1],precision:c.DEFAULT}}function s(){this.addInput("in","Texture");this.addInput("dirt","Texture");this.addOutput("out","Texture");this.addOutput("glow","Texture");this.properties={enabled:!0,intensity:1,persistence:0.99,iterations:16,threshold:0,scale:1,dirt_factor:0.5,precision:c.DEFAULT};this._textures=[];this._uniforms={u_intensity:1,u_texture:0,u_glow_texture:1,u_threshold:0,u_texel_size:vec2.create()}}function I(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties= +{intensity:1,radius:5}}function J(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={sigma:1.4,k:1.6,p:21.7,epsilon:79,phi:0.017}}function H(){this.addOutput("Webcam","Texture");this.properties={texture_name:"",facingMode:"user"};this.boxcolor="black";this.version=0}function E(){this.addInput("in","Texture");this.addInput("f","number");this.addOutput("out","Texture");this.properties={enabled:!0,factor:1,precision:c.LOW};this._uniforms={u_texture:0,u_factor:1}} +function t(){this.addInput("in","Texture");this.addOutput("out","Texture");this.properties={precision:c.LOW,split_channels:!1};this._values=new Uint8Array(1024);this._values.fill(255);this._curve_texture=null;this._uniforms={u_texture:0,u_curve:1,u_range:1};this._must_update=!0;this._points={RGB:[[0,0],[1,1]],R:[[0,0],[1,1]],G:[[0,0],[1,1]],B:[[0,0],[1,1]]};this.curve_editor=null;this.addWidget("toggle","Split Channels",!1,"split_channels");this.addWidget("combo","Channel","RGB",{values:["RGB","R", +"G","B"]});this.curve_offset=68;this.size=[240,160]}function N(){this.addInput("in","Texture");this.addInput("exp","number");this.addOutput("out","Texture");this.properties={exposition:1,precision:c.LOW};this._uniforms={u_texture:0,u_exposition:1}}function K(){this.addInput("in","Texture");this.addInput("avg","number,Texture");this.addOutput("out","Texture");this.properties={enabled:!0,scale:1,gamma:1,average_lum:1,lum_white:1,precision:c.LOW};this._uniforms={u_texture:0,u_lumwhite2:1,u_igamma:1, +u_scale:1,u_average_lum:1}}function M(){this.addOutput("out","Texture");this.properties={width:512,height:512,seed:0,persistence:0.1,octaves:8,scale:1,offset:[0,0],amplitude:1,precision:c.DEFAULT};this._key=0;this._texture=null;this._uniforms={u_persistence:0.1,u_seed:0,u_offset:vec2.create(),u_scale:1,u_viewport:vec2.create()}}function L(){this.addInput("v");this.addOutput("out","Texture");this.properties={code:L.default_code,width:512,height:512,clear:!0,precision:c.DEFAULT,use_html_canvas:!1}; +this._temp_texture=this._func=null;this.compileCode()}function O(){this.addInput("in","Texture");this.addOutput("out","Texture");this.properties={key_color:vec3.fromValues(0,1,0),threshold:0.8,slope:0.2,precision:c.DEFAULT}}function P(){this.addInput("in","texture");this.addInput("yaw","number");this.addOutput("out","texture");this.properties={yaw:0}}var F=B.LiteGraph;B.LGraphTexture=null;"undefined"!=typeof GL&&(LGraphCanvas.link_type_colors.Texture="#987",B.LGraphTexture=c,c.title="Texture",c.desc= +"Texture",c.widgets_info={name:{widget:"texture"},filter:{widget:"checkbox"}},c.loadTextureCallback=null,c.image_preview_size=256,c.PASS_THROUGH=1,c.COPY=2,c.LOW=3,c.HIGH=4,c.REUSE=5,c.DEFAULT=2,c.MODE_VALUES={"pass through":c.PASS_THROUGH,copy:c.COPY,low:c.LOW,high:c.HIGH,reuse:c.REUSE,"default":c.DEFAULT},c.getTexturesContainer=function(){return gl.textures},c.loadTexture=function(a,b){b=b||{};var d=a;"http://"==d.substr(0,7)&&F.proxy&&(d=F.proxy+d.substr(7));return c.getTexturesContainer()[a]= +GL.Texture.fromURL(d,b)},c.getTexture=function(a){var b=this.getTexturesContainer();if(!b)throw"Cannot load texture, container of textures not found";b=b[a];return!b&&a&&":"!=a[0]?this.loadTexture(a):b},c.getTargetTexture=function(a,b,d){if(!a)throw"LGraphTexture.getTargetTexture expects a reference texture";var g=null;switch(d){case c.LOW:g=gl.UNSIGNED_BYTE;break;case c.HIGH:g=gl.HIGH_PRECISION_FORMAT;break;case c.REUSE:return a;default:g=a?a.type:gl.UNSIGNED_BYTE}b&&b.width==a.width&&b.height== +a.height&&b.type==g||(b=new GL.Texture(a.width,a.height,{type:g,format:gl.RGBA,filter:gl.LINEAR}));return b},c.getTextureType=function(a,b){var d=b?b.type:gl.UNSIGNED_BYTE;switch(a){case c.HIGH:d=gl.HIGH_PRECISION_FORMAT;break;case c.LOW:d=gl.UNSIGNED_BYTE}return d},c.getWhiteTexture=function(){return this._white_texture?this._white_texture:this._white_texture=GL.Texture.fromMemory(1,1,[255,255,255,255],{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.getNoiseTexture=function(){if(this._noise_texture)return this._noise_texture; +for(var a=new Uint8Array(1048576),b=0;1048576>b;++b)a[b]=255*Math.random();return this._noise_texture=a=GL.Texture.fromMemory(512,512,a,{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.prototype.onDropFile=function(a,b,d){if(a){var c=null;"string"==typeof a?c=GL.Texture.fromURL(a):-1!=b.toLowerCase().indexOf(".dds")?c=GL.Texture.fromDDSInMemory(a):(a=new Blob([d]),a=URL.createObjectURL(a),c=GL.Texture.fromURL(a));this._drop_texture=c;this.properties.name=b}else this._drop_texture=null,this.properties.name= +""},c.prototype.getExtraMenuOptions=function(a){var b=this;if(this._drop_texture)return[{content:"Clear",callback:function(){b._drop_texture=null;b.properties.name=""}}]},c.prototype.onExecute=function(){var a=null;this.isOutputConnected(1)&&(a=this.getInputData(0));!a&&this._drop_texture&&(a=this._drop_texture);!a&&this.properties.name&&(a=c.getTexture(this.properties.name));if(a){this._last_tex=a;!1===this.properties.filter?a.setParameter(gl.TEXTURE_MAG_FILTER,gl.NEAREST):a.setParameter(gl.TEXTURE_MAG_FILTER, +gl.LINEAR);this.setOutputData(0,a);this.setOutputData(1,a.fullpath||a.filename);for(var b=2;b= +this.size[1]))if(this._drop_texture&&a.webgl)a.drawImage(this._drop_texture,0,0,this.size[0],this.size[1]);else{if(this._last_preview_tex!=this._last_tex)if(a.webgl)this._canvas=this._last_tex;else{var b=c.generateLowResTexturePreview(this._last_tex);if(!b)return;this._last_preview_tex=this._last_tex;this._canvas=cloneCanvas(b)}this._canvas&&(a.save(),a.webgl||(a.translate(0,this.size[1]),a.scale(1,-1)),a.drawImage(this._canvas,0,0,this.size[0],this.size[1]),a.restore())}},c.generateLowResTexturePreview= +function(a){if(!a)return null;var b=c.image_preview_size,d=a;if(a.format==gl.DEPTH_COMPONENT)return null;if(a.width>b||a.height>b)d=this._preview_temp_tex,this._preview_temp_tex||(this._preview_temp_tex=d=new GL.Texture(b,b,{minFilter:gl.NEAREST})),a.copyTo(d);a=this._preview_canvas;a||(this._preview_canvas=a=createCanvas(b,b));d&&d.toCanvas(a);return a},c.prototype.getResources=function(a){this.properties.name&&(a[this.properties.name]=GL.Texture);return a},c.prototype.onGetInputs=function(){return[["in", +"Texture"]]},c.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["aspect","number"]]},c.replaceCode=function(a,b){return a.replace(/\{\{[a-zA-Z0-9_]*\}\}/g,function(a){a=a.replace(/[\{\}]/g,"");return b[a]||""})},F.registerNodeType("texture/texture",c),m.title="Preview",m.desc="Show a texture in the graph canvas",m.allow_preview=!1,m.prototype.onDrawBackground=function(a){if(!this.flags.collapsed&&(a.webgl||m.allow_preview)){var b=this.getInputData(0);if(b){var d=null, +d=!b.handle&&a.webgl?b:c.generateLowResTexturePreview(b);a.save();this.properties.flipY&&(a.translate(0,this.size[1]),a.scale(1,-1));a.drawImage(d,0,0,this.size[0],this.size[1]);a.restore()}}},F.registerNodeType("texture/preview",m),n.title="Save",n.desc="Save a texture in the repository",n.prototype.getPreviewTexture=function(){return this._texture},n.prototype.onExecute=function(){var a=this.getInputData(0);a&&(this.properties.generate_mipmaps&&(a.bind(0),a.setParameter(gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_LINEAR), +gl.generateMipmap(a.texture_type),a.unbind(0)),this.properties.name&&(c.storeTexture?c.storeTexture(this.properties.name,a):c.getTexturesContainer()[this.properties.name]=a),this._texture=a,this.setOutputData(0,a),this.setOutputData(1,this.properties.name))},F.registerNodeType("texture/save",n),l.widgets_info={uvcode:{widget:"code"},pixelcode:{widget:"code"},precision:{widget:"combo",values:c.MODE_VALUES}},l.title="Operation",l.desc="Texture shader operation",l.presets={},l.prototype.getExtraMenuOptions= +function(a){var b=this;return[{content:b.properties.show?"Hide Texture":"Show Texture",callback:function(){b.properties.show=!b.properties.show}}]},l.prototype.onPropertyChanged=function(){this.has_error=!1},l.prototype.onDrawBackground=function(a){this.flags.collapsed||20>=this.size[1]||!this.properties.show||!this._tex||this._tex.gl!=a||(a.save(),a.drawImage(this._tex,0,0,this.size[0],this.size[1]),a.restore())},l.prototype.onExecute=function(){var a=this.getInputData(0);if(this.isOutputConnected(0))if(this.properties.precision=== +c.PASS_THROUGH)this.setOutputData(0,a);else{var b=this.getInputData(1);if(this.properties.uvcode||this.properties.pixelcode){var d=512,g=512;a?(d=a.width,g=a.height):b&&(d=b.width,g=b.height);b||(b=GL.Texture.getWhiteTexture());var f=c.getTextureType(this.properties.precision,a);this._tex=a||this._tex?c.getTargetTexture(a||this._tex,this._tex,this.properties.precision):new GL.Texture(d,g,{type:f,format:gl.RGBA,filter:gl.LINEAR});f="";this.properties.uvcode&&(f="uv = "+this.properties.uvcode,-1!=this.properties.uvcode.indexOf(";")&& +(f=this.properties.uvcode));var e="";this.properties.pixelcode&&(e="result = "+this.properties.pixelcode,-1!=this.properties.pixelcode.indexOf(";")&&(e=this.properties.pixelcode));var k=this._shader;if(!(this.has_error||k&&this._shader_code==f+"|"+e)){var h=c.replaceCode(l.pixel_shader,{UV_CODE:f,PIXEL_CODE:e});try{k=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,h),this.boxcolor="#00FF00"}catch(p){GL.Shader.dumpErrorToConsole(p,Shader.SCREEN_VERTEX_SHADER,h);this.boxcolor="#FF0000";this.has_error=!0; +return}this._shader=k;this._shader_code=f+"|"+e}if(this._shader){var q=this.getInputData(2);null!=q?this.properties.value=q:q=parseFloat(this.properties.value);var r=this.graph.getTime();this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);gl.disable(gl.BLEND);a&&a.bind(0);b&&b.bind(1);var c=Mesh.getScreenQuad();k.uniforms({u_texture:0,u_textureB:1,value:q,texSize:[d,g],time:r}).draw(c)});this.setOutputData(0,this._tex)}}}},l.pixel_shader="precision highp float;\n\t\t\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec2 texSize;\n\t\tuniform float time;\n\t\tuniform float value;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord;\n\t\t\t{{UV_CODE}};\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\tvec3 color = color4.rgb;\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\tvec3 colorB = color4B.rgb;\n\t\t\tvec3 result = color;\n\t\t\tfloat alpha = 1.0;\n\t\t\t{{PIXEL_CODE}};\n\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t}\n\t\t", l.registerPreset=function(a,b){l.presets[a]=b},l.registerPreset("",""),l.registerPreset("bypass","color"),l.registerPreset("add","color + colorB * value"),l.registerPreset("substract","(color - colorB) * value"),l.registerPreset("mate","mix( color, colorB, color4B.a * value)"),l.registerPreset("invert","vec3(1.0) - color"),l.registerPreset("multiply","color * colorB * value"),l.registerPreset("divide","(color / colorB) / value"),l.registerPreset("difference","abs(color - colorB) * value"),l.registerPreset("max", "max(color, colorB) * value"),l.registerPreset("min","min(color, colorB) * value"),l.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"),l.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"),l.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"),l.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"), l.prototype.onInspect=function(a){var b=this;a.addCombo("Presets","",{values:Object.keys(l.presets),callback:function(d){var c=l.presets[d];c&&(b.setProperty("pixelcode",c),b.title=d,a.refresh())}})},F.registerNodeType("texture/operation",l),x.title="Shader",x.desc="Texture shader",x.widgets_info={code:{type:"code",lang:"glsl"},precision:{widget:"combo",values:c.MODE_VALUES}},x.prototype.onPropertyChanged=function(a,b){if("code"==a){var d=this.getShader();if(d){var c=d.uniformInfo;if(this.inputs)for(var g= @@ -506,8 +506,8 @@ F.registerNodeType("texture/edges",d),g.title="Depth Range",g.desc="Generates a filter:gl.LINEAR}));var d=this._uniforms,b=this.properties.distance;this.isInputConnected(1)&&(b=this.getInputData(1),this.properties.distance=b);var c=this.properties.range;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.range=c);d.u_distance=b;d.u_range=c;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var f=Mesh.getScreenQuad();g._shader||(g._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,g.pixel_shader),g._shader_onlydepth=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,g.pixel_shader, {ONLY_DEPTH:""}));var e=this.properties.only_depth?g._shader_onlydepth:g._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1,1E3];d.u_camera_planes=b;this._temp_texture.drawTo(function(){a.bind(0);e.uniforms(d).draw(f)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},g.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform float u_distance;\n\t\tuniform float u_range;\n\t\t\n\t\tfloat LinearDepth()\n\t\t{\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\t\t\tdepth = depth * 2.0 - 1.0;\n\t\t\treturn zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t\tfloat depth = LinearDepth();\n\t\t\t#ifdef ONLY_DEPTH\n\t\t\t gl_FragColor = vec4(depth);\n\t\t\t#else\n\t\t\t\tfloat diff = abs(depth * u_camera_planes.y - u_distance);\n\t\t\t\tfloat dof = 1.0;\n\t\t\t\tif(diff <= u_range)\n\t\t\t\t\tdof = diff / u_range;\n\t\t\t gl_FragColor = vec4(dof);\n\t\t\t#endif\n\t\t}\n\t\t", F.registerNodeType("texture/depth_range",g),f.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},f.title="Linear Depth",f.desc="Creates a color texture with linear depth",f.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a&&(a.format==gl.DEPTH_COMPONENT||a.format==gl.DEPTH_STENCIL)){var b=this.properties.precision==c.HIGH?gl.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&& -this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGB,filter:gl.LINEAR}));var d=this._uniforms;d.u_near=a.near_far_planes[0];d.u_far=a.near_far_planes[1];d.u_invert=this.properties.invert?1:0;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var g=Mesh.getScreenQuad();f._shader||(f._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,f.pixel_shader));var e=f._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera? -LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1,1E3];d.u_camera_planes=b;this._temp_texture.drawTo(function(){a.bind(0);e.uniforms(d).draw(g)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},f.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_near;\n\t\tuniform float u_far;\n\t\tuniform int u_invert;\n\t\t\n\t\tvoid main() {\n\t\t\tfloat zNear = u_near;\n\t\t\tfloat zFar = u_far;\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\t\t\tdepth = depth * 2.0 - 1.0;\n\t\t\tfloat f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t\tif( u_invert == 1 )\n\t\t\t\tf = 1.0 - f;\n\t\t\tgl_FragColor = vec4(vec3(f),1.0);\n\t\t}\n\t\t", +this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGB,filter:gl.LINEAR}));var d=this._uniforms;d.u_invert=this.properties.invert?1:0;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var g=Mesh.getScreenQuad();f._shader||(f._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,f.pixel_shader));var e=f._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1, +1E3];d.u_camera_planes=b;d.u_ires.set([0,0]);this._temp_texture.drawTo(function(){a.bind(0);e.uniforms(d).draw(g)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},f.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform int u_invert;\n\t\tuniform vec2 u_ires;\n\t\t\n\t\tvoid main() {\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord + u_ires*0.5).x * 2.0 - 1.0;\n\t\t\tfloat f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t\tif( u_invert == 1 )\n\t\t\t\tf = 1.0 - f;\n\t\t\tgl_FragColor = vec4(vec3(f),1.0);\n\t\t}\n\t\t", F.registerNodeType("texture/linear_depth",f),r.title="Blur",r.desc="Blur a texture",r.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},r.max_iterations=20,r.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._final_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(b=this._final_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));var d=this.properties.iterations;this.isInputConnected(1)&& (d=this.getInputData(1),this.properties.iterations=d);d=Math.min(Math.floor(d),r.max_iterations);if(0==d)this.setOutputData(0,a);else{var c=this.properties.intensity;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.intensity=c);var g=F.camera_aspect;g||void 0===window.gl||(g=gl.canvas.height/gl.canvas.width);g||(g=1);var g=this.properties.preserve_aspect?g:1,f=this.properties.scale||[1,1];a.applyBlur(g*f[0],f[1],c,b);for(a=1;a Date: Tue, 5 May 2020 18:07:39 +0300 Subject: [PATCH 29/63] Update for missing type definitions Updated onConnectInput to match current function definition Added missing definitions for LGraphCanvas.onDrawLinkTooltip LGraphCanvas.onNodeMoved LGraphCanvas.onSelectionChange LGraphCanvas.onNodeSelected LGraphCanvas.onNodeDeselected LGraphCanvas.onShowNodePanel LGraphCanvas.onNodeDblClicked LGraphNode.onConnectionsChange Can close #127 --- src/litegraph.d.ts | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/litegraph.d.ts b/src/litegraph.d.ts index 69220a8df..0c6950bf2 100644 --- a/src/litegraph.d.ts +++ b/src/litegraph.d.ts @@ -928,8 +928,20 @@ export declare class LGraphNode { onConnectInput?( inputIndex: number, type: INodeOutputSlot["type"], - outputSlot: INodeOutputSlot + outputSlot: INodeOutputSlot, + this: this, + slotIndex: number ): boolean; + + /** a connection changed (new one or removed) (LiteGraph.INPUT or LiteGraph.OUTPUT, slot, true if connected, link_info, input_info or output_info ) */ + onConnectionsChange( + type: number, + slotIndex: number, + isConnected: boolean, + link: LLink, + ioSlot: (INodeOutputSlot | INodeInputSlot) + ): void; + /** Called by `LGraphCanvas.processContextMenu` */ getMenuOptions?(graphCanvas: LGraphCanvas): ContextMenuItem[]; } @@ -1114,6 +1126,20 @@ export declare class LGraphCanvas { onDrawOverlay: ((ctx: CanvasRenderingContext2D) => void) | null; /** Called by `LGraphCanvas.processMouseDown` */ onMouse: ((event: MouseEvent) => boolean) | null; + /** Called by `LGraphCanvas.drawFrontCanvas` and `LGraphCanvas.drawLinkTooltip` */ + onDrawLinkTooltip: ((ctx: CanvasRenderingContext2D, link: LLink, this: this) => void) | null; + /** Called by `LGraphCanvas.selectNodes` */ + onNodeMoved: ((node: LGraphNode) => void) | null; + /** Called by `LGraphCanvas.processNodeSelected` */ + onNodeSelected: ((node: LGraphNode) => void) | null; + /** Called by `LGraphCanvas.deselectNode` */ + onNodeDeselected: ((node: LGraphNode) => void) | null; + /** Called by `LGraphCanvas.processNodeDblClicked` */ + onShowNodePanel: ((node: LGraphNode) => void) | null; + /** Called by `LGraphCanvas.processNodeDblClicked` */ + onNodeDblClicked: ((node: LGraphNode) => void) | null; + /** Called by `LGraphCanvas.selectNodes` */ + onSelectionChange: ((nodes: Record) => void) | null; /** Called by `LGraphCanvas.showSearchBox` */ onSearchBox: | (( From 626a69fec434aad6b53260e9d800463e31c8a2a1 Mon Sep 17 00:00:00 2001 From: Shan M Date: Wed, 6 May 2020 09:33:55 +0300 Subject: [PATCH 30/63] Error correction in type definition --- src/litegraph.d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/litegraph.d.ts b/src/litegraph.d.ts index 0c6950bf2..07585975b 100644 --- a/src/litegraph.d.ts +++ b/src/litegraph.d.ts @@ -929,7 +929,7 @@ export declare class LGraphNode { inputIndex: number, type: INodeOutputSlot["type"], outputSlot: INodeOutputSlot, - this: this, + _this: this, slotIndex: number ): boolean; @@ -1127,7 +1127,7 @@ export declare class LGraphCanvas { /** Called by `LGraphCanvas.processMouseDown` */ onMouse: ((event: MouseEvent) => boolean) | null; /** Called by `LGraphCanvas.drawFrontCanvas` and `LGraphCanvas.drawLinkTooltip` */ - onDrawLinkTooltip: ((ctx: CanvasRenderingContext2D, link: LLink, this: this) => void) | null; + onDrawLinkTooltip: ((ctx: CanvasRenderingContext2D, link: LLink, _this: this) => void) | null; /** Called by `LGraphCanvas.selectNodes` */ onNodeMoved: ((node: LGraphNode) => void) | null; /** Called by `LGraphCanvas.processNodeSelected` */ @@ -1139,7 +1139,7 @@ export declare class LGraphCanvas { /** Called by `LGraphCanvas.processNodeDblClicked` */ onNodeDblClicked: ((node: LGraphNode) => void) | null; /** Called by `LGraphCanvas.selectNodes` */ - onSelectionChange: ((nodes: Record) => void) | null; + onSelectionChange: ((nodes) => void) | null; /** Called by `LGraphCanvas.showSearchBox` */ onSearchBox: | (( From 706b65a8f61d2b87237440dd554829eba43fe3f4 Mon Sep 17 00:00:00 2001 From: altarfinch Date: Thu, 7 May 2020 19:45:36 +0200 Subject: [PATCH 31/63] custom widget custom size support --- build/litegraph.js | 39612 ++++++++++++++++++--------------------- build/litegraph.min.js | 11052 ++++++++++- src/litegraph.d.ts | 4 +- src/litegraph.js | 64 +- 4 files changed, 28466 insertions(+), 22266 deletions(-) diff --git a/build/litegraph.js b/build/litegraph.js index 7cf799a16..6b449c569 100644 --- a/build/litegraph.js +++ b/build/litegraph.js @@ -1,12247 +1,12265 @@ -//packer version - -(function(global) { - // ************************************************************* - // LiteGraph CLASS ******* - // ************************************************************* - - /** - * The Global Scope. It contains all the registered node classes. - * - * @class LiteGraph - * @constructor - */ - - var LiteGraph = (global.LiteGraph = { - VERSION: 0.4, - - CANVAS_GRID_SIZE: 10, - - NODE_TITLE_HEIGHT: 30, - NODE_TITLE_TEXT_Y: 20, - NODE_SLOT_HEIGHT: 20, - NODE_WIDGET_HEIGHT: 20, - NODE_WIDTH: 140, - NODE_MIN_WIDTH: 50, - NODE_COLLAPSED_RADIUS: 10, - NODE_COLLAPSED_WIDTH: 80, - NODE_TITLE_COLOR: "#999", - NODE_TEXT_SIZE: 14, - NODE_TEXT_COLOR: "#AAA", - NODE_SUBTEXT_SIZE: 12, - NODE_DEFAULT_COLOR: "#333", - NODE_DEFAULT_BGCOLOR: "#353535", - NODE_DEFAULT_BOXCOLOR: "#666", - NODE_DEFAULT_SHAPE: "box", - DEFAULT_SHADOW_COLOR: "rgba(0,0,0,0.5)", - DEFAULT_GROUP_FONT: 24, - - WIDGET_BGCOLOR: "#222", - WIDGET_OUTLINE_COLOR: "#666", - WIDGET_TEXT_COLOR: "#DDD", - WIDGET_SECONDARY_TEXT_COLOR: "#999", - - LINK_COLOR: "#9A9", - EVENT_LINK_COLOR: "#A86", - CONNECTING_LINK_COLOR: "#AFA", - - MAX_NUMBER_OF_NODES: 1000, //avoid infinite loops - DEFAULT_POSITION: [100, 100], //default node position - VALID_SHAPES: ["default", "box", "round", "card"], //,"circle" - - //shapes are used for nodes but also for slots - BOX_SHAPE: 1, - ROUND_SHAPE: 2, - CIRCLE_SHAPE: 3, - CARD_SHAPE: 4, - ARROW_SHAPE: 5, - - //enums - INPUT: 1, - OUTPUT: 2, - - EVENT: -1, //for outputs - ACTION: -1, //for inputs - - ALWAYS: 0, - ON_EVENT: 1, - NEVER: 2, - ON_TRIGGER: 3, - - UP: 1, - DOWN: 2, - LEFT: 3, - RIGHT: 4, - CENTER: 5, - - STRAIGHT_LINK: 0, - LINEAR_LINK: 1, - SPLINE_LINK: 2, - - NORMAL_TITLE: 0, - NO_TITLE: 1, - TRANSPARENT_TITLE: 2, - AUTOHIDE_TITLE: 3, - - proxy: null, //used to redirect calls - node_images_path: "", - - debug: false, - catch_exceptions: true, - throw_errors: true, - allow_scripts: false, //if set to true some nodes like Formula would be allowed to evaluate code that comes from unsafe sources (like node configuration), which could lead to exploits - registered_node_types: {}, //nodetypes by string - node_types_by_file_extension: {}, //used for dropping files in the canvas - Nodes: {}, //node types by classname - - searchbox_extras: {}, //used to add extra features to the search box - - /** - * Register a node class so it can be listed when the user wants to create a new one - * @method registerNodeType - * @param {String} type name of the node and path - * @param {Class} base_class class containing the structure of a node - */ - - registerNodeType: function(type, base_class) { - if (!base_class.prototype) { - throw "Cannot register a simple object, it must be a class with a prototype"; - } - base_class.type = type; - - if (LiteGraph.debug) { - console.log("Node registered: " + type); - } - - var categories = type.split("/"); - var classname = base_class.name; - - var pos = type.lastIndexOf("/"); - base_class.category = type.substr(0, pos); - - if (!base_class.title) { - base_class.title = classname; - } - //info.name = name.substr(pos+1,name.length - pos); - - //extend class - if (base_class.prototype) { - //is a class - for (var i in LGraphNode.prototype) { - if (!base_class.prototype[i]) { - base_class.prototype[i] = LGraphNode.prototype[i]; - } - } - } - - var prev = this.registered_node_types[type]; - if(prev) - console.log("replacing node type: " + type); - else - { - if( !Object.hasOwnProperty( base_class.prototype, "shape") ) - Object.defineProperty(base_class.prototype, "shape", { - set: function(v) { - switch (v) { - case "default": - delete this._shape; - break; - case "box": - this._shape = LiteGraph.BOX_SHAPE; - break; - case "round": - this._shape = LiteGraph.ROUND_SHAPE; - break; - case "circle": - this._shape = LiteGraph.CIRCLE_SHAPE; - break; - case "card": - this._shape = LiteGraph.CARD_SHAPE; - break; - default: - this._shape = v; - } - }, - get: function(v) { - return this._shape; - }, - enumerable: true, - configurable: true - }); - - //warnings - if (base_class.prototype.onPropertyChange) { - console.warn( - "LiteGraph node class " + - type + - " has onPropertyChange method, it must be called onPropertyChanged with d at the end" - ); - } - - //used to know which nodes create when dragging files to the canvas - if (base_class.supported_extensions) { - for (var i in base_class.supported_extensions) { - var ext = base_class.supported_extensions[i]; - if(ext && ext.constructor === String) - this.node_types_by_file_extension[ ext.toLowerCase() ] = base_class; - } - } - } - - this.registered_node_types[type] = base_class; - if (base_class.constructor.name) { - this.Nodes[classname] = base_class; - } - if (LiteGraph.onNodeTypeRegistered) { - LiteGraph.onNodeTypeRegistered(type, base_class); - } - if (prev && LiteGraph.onNodeTypeReplaced) { - LiteGraph.onNodeTypeReplaced(type, base_class, prev); - } - }, - - /** - * removes a node type from the system - * @method unregisterNodeType - * @param {String|Object} type name of the node or the node constructor itself - */ - unregisterNodeType: function(type) { - var base_class = type.constructor === String ? this.registered_node_types[type] : type; - if(!base_class) - throw("node type not found: " + type ); - delete this.registered_node_types[base_class.type]; - if(base_class.constructor.name) - delete this.Nodes[base_class.constructor.name]; - }, - - /** - * Create a new nodetype by passing a function, it wraps it with a proper class and generates inputs according to the parameters of the function. - * Useful to wrap simple methods that do not require properties, and that only process some input to generate an output. - * @method wrapFunctionAsNode - * @param {String} name node name with namespace (p.e.: 'math/sum') - * @param {Function} func - * @param {Array} param_types [optional] an array containing the type of every parameter, otherwise parameters will accept any type - * @param {String} return_type [optional] string with the return type, otherwise it will be generic - * @param {Object} properties [optional] properties to be configurable - */ - wrapFunctionAsNode: function( - name, - func, - param_types, - return_type, - properties - ) { - var params = Array(func.length); - var code = ""; - var names = LiteGraph.getParameterNames(func); - for (var i = 0; i < names.length; ++i) { - code += - "this.addInput('" + - names[i] + - "'," + - (param_types && param_types[i] - ? "'" + param_types[i] + "'" - : "0") + - ");\n"; - } - code += - "this.addOutput('out'," + - (return_type ? "'" + return_type + "'" : 0) + - ");\n"; - if (properties) { - code += - "this.properties = " + JSON.stringify(properties) + ";\n"; - } - var classobj = Function(code); - classobj.title = name.split("/").pop(); - classobj.desc = "Generated from " + func.name; - classobj.prototype.onExecute = function onExecute() { - for (var i = 0; i < params.length; ++i) { - params[i] = this.getInputData(i); - } - var r = func.apply(this, params); - this.setOutputData(0, r); - }; - this.registerNodeType(name, classobj); - }, - - /** - * Adds this method to all nodetypes, existing and to be created - * (You can add it to LGraphNode.prototype but then existing node types wont have it) - * @method addNodeMethod - * @param {Function} func - */ - addNodeMethod: function(name, func) { - LGraphNode.prototype[name] = func; - for (var i in this.registered_node_types) { - var type = this.registered_node_types[i]; - if (type.prototype[name]) { - type.prototype["_" + name] = type.prototype[name]; - } //keep old in case of replacing - type.prototype[name] = func; - } - }, - - /** - * Create a node of a given type with a name. The node is not attached to any graph yet. - * @method createNode - * @param {String} type full name of the node class. p.e. "math/sin" - * @param {String} name a name to distinguish from other nodes - * @param {Object} options to set options - */ - - createNode: function(type, title, options) { - var base_class = this.registered_node_types[type]; - if (!base_class) { - if (LiteGraph.debug) { - console.log( - 'GraphNode type "' + type + '" not registered.' - ); - } - return null; - } - - var prototype = base_class.prototype || base_class; - - title = title || base_class.title || type; - - var node = null; - - if (LiteGraph.catch_exceptions) { - try { - node = new base_class(title); - } catch (err) { - console.error(err); - return null; - } - } else { - node = new base_class(title); - } - - node.type = type; - - if (!node.title && title) { - node.title = title; - } - if (!node.properties) { - node.properties = {}; - } - if (!node.properties_info) { - node.properties_info = []; - } - if (!node.flags) { - node.flags = {}; - } - if (!node.size) { - node.size = node.computeSize(); - } - if (!node.pos) { - node.pos = LiteGraph.DEFAULT_POSITION.concat(); - } - if (!node.mode) { - node.mode = LiteGraph.ALWAYS; - } - - //extra options - if (options) { - for (var i in options) { - node[i] = options[i]; - } - } - - return node; - }, - - /** - * Returns a registered node type with a given name - * @method getNodeType - * @param {String} type full name of the node class. p.e. "math/sin" - * @return {Class} the node class - */ - getNodeType: function(type) { - return this.registered_node_types[type]; - }, - - /** - * Returns a list of node types matching one category - * @method getNodeType - * @param {String} category category name - * @return {Array} array with all the node classes - */ - - getNodeTypesInCategory: function(category, filter) { - var r = []; - for (var i in this.registered_node_types) { - var type = this.registered_node_types[i]; - if (filter && type.filter && type.filter != filter) { - continue; - } - - if (category == "") { - if (type.category == null) { - r.push(type); - } - } else if (type.category == category) { - r.push(type); - } - } - - return r; - }, - - /** - * Returns a list with all the node type categories - * @method getNodeTypesCategories - * @return {Array} array with all the names of the categories - */ - getNodeTypesCategories: function( filter ) { - var categories = { "": 1 }; - for (var i in this.registered_node_types) { - var type = this.registered_node_types[i]; - if ( type.category && !type.skip_list ) - { - if(filter && type.filter != filter) - continue; - categories[type.category] = 1; - } - } - var result = []; - for (var i in categories) { - result.push(i); - } - return result; - }, - - //debug purposes: reloads all the js scripts that matches a wildcard - reloadNodes: function(folder_wildcard) { - var tmp = document.getElementsByTagName("script"); - //weird, this array changes by its own, so we use a copy - var script_files = []; - for (var i in tmp) { - script_files.push(tmp[i]); - } - - var docHeadObj = document.getElementsByTagName("head")[0]; - folder_wildcard = document.location.href + folder_wildcard; - - for (var i in script_files) { - var src = script_files[i].src; - if ( - !src || - src.substr(0, folder_wildcard.length) != folder_wildcard - ) { - continue; - } - - try { - if (LiteGraph.debug) { - console.log("Reloading: " + src); - } - var dynamicScript = document.createElement("script"); - dynamicScript.type = "text/javascript"; - dynamicScript.src = src; - docHeadObj.appendChild(dynamicScript); - docHeadObj.removeChild(script_files[i]); - } catch (err) { - if (LiteGraph.throw_errors) { - throw err; - } - if (LiteGraph.debug) { - console.log("Error while reloading " + src); - } - } - } - - if (LiteGraph.debug) { - console.log("Nodes reloaded"); - } - }, - - //separated just to improve if it doesn't work - cloneObject: function(obj, target) { - if (obj == null) { - return null; - } - var r = JSON.parse(JSON.stringify(obj)); - if (!target) { - return r; - } - - for (var i in r) { - target[i] = r[i]; - } - return target; - }, - - /** - * Returns if the types of two slots are compatible (taking into account wildcards, etc) - * @method isValidConnection - * @param {String} type_a - * @param {String} type_b - * @return {Boolean} true if they can be connected - */ - isValidConnection: function(type_a, type_b) { - if ( - !type_a || //generic output - !type_b || //generic input - type_a == type_b || //same type (is valid for triggers) - (type_a == LiteGraph.EVENT && type_b == LiteGraph.ACTION) - ) { - return true; - } - - // Enforce string type to handle toLowerCase call (-1 number not ok) - type_a = String(type_a); - type_b = String(type_b); - type_a = type_a.toLowerCase(); - type_b = type_b.toLowerCase(); - - // For nodes supporting multiple connection types - if (type_a.indexOf(",") == -1 && type_b.indexOf(",") == -1) { - return type_a == type_b; - } - - // Check all permutations to see if one is valid - var supported_types_a = type_a.split(","); - var supported_types_b = type_b.split(","); - for (var i = 0; i < supported_types_a.length; ++i) { - for (var j = 0; j < supported_types_b.length; ++j) { - if (supported_types_a[i] == supported_types_b[j]) { - return true; - } - } - } - - return false; - }, - - /** - * Register a string in the search box so when the user types it it will recommend this node - * @method registerSearchboxExtra - * @param {String} node_type the node recommended - * @param {String} description text to show next to it - * @param {Object} data it could contain info of how the node should be configured - * @return {Boolean} true if they can be connected - */ - registerSearchboxExtra: function(node_type, description, data) { - this.searchbox_extras[description.toLowerCase()] = { - type: node_type, - desc: description, - data: data - }; - }, - - /** - * Wrapper to load files (from url using fetch or from file using FileReader) - * @method fetchFile - * @param {String|File|Blob} url the url of the file (or the file itself) - * @param {String} type an string to know how to fetch it: "text","arraybuffer","json","blob" - * @param {Function} on_complete callback(data) - * @param {Function} on_error in case of an error - * @return {FileReader|Promise} returns the object used to - */ - fetchFile: function( url, type, on_complete, on_error ) { - var that = this; - if(!url) - return null; - - type = type || "text"; - if( url.constructor === String ) - { - if (url.substr(0, 4) == "http" && LiteGraph.proxy) { - url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); - } - return fetch(url) - .then(function(response) { - if(!response.ok) - throw new Error("File not found"); //it will be catch below - if(type == "arraybuffer") - return response.arrayBuffer(); - else if(type == "text" || type == "string") - return response.text(); - else if(type == "json") - return response.json(); - else if(type == "blob") - return response.blob(); - }) - .then(function(data) { - if(on_complete) - on_complete(data); - }) - .catch(function(error) { - console.error("error fetching file:",url); - if(on_error) - on_error(error); - }); - } - else if( url.constructor === File || url.constructor === Blob) - { - var reader = new FileReader(); - reader.onload = function(e) - { - var v = e.target.result; - if( type == "json" ) - v = JSON.parse(v); - if(on_complete) - on_complete(v); - } - if(type == "arraybuffer") - return reader.readAsArrayBuffer(url); - else if(type == "text" || type == "json") - return reader.readAsText(url); - else if(type == "blob") - return reader.readAsBinaryString(url); - } - return null; - } - }); - - //timer that works everywhere - if (typeof performance != "undefined") { - LiteGraph.getTime = performance.now.bind(performance); - } else if (typeof Date != "undefined" && Date.now) { - LiteGraph.getTime = Date.now.bind(Date); - } else if (typeof process != "undefined") { - LiteGraph.getTime = function() { - var t = process.hrtime(); - return t[0] * 0.001 + t[1] * 1e-6; - }; - } else { - LiteGraph.getTime = function getTime() { - return new Date().getTime(); - }; - } - - //********************************************************************************* - // LGraph CLASS - //********************************************************************************* - - /** - * LGraph is the class that contain a full graph. We instantiate one and add nodes to it, and then we can run the execution loop. - * - * @class LGraph - * @constructor - * @param {Object} o data from previous serialization [optional] - */ - - function LGraph(o) { - if (LiteGraph.debug) { - console.log("Graph created"); - } - this.list_of_graphcanvas = null; - this.clear(); - - if (o) { - this.configure(o); - } - } - - global.LGraph = LiteGraph.LGraph = LGraph; - - //default supported types - LGraph.supported_types = ["number", "string", "boolean"]; - - //used to know which types of connections support this graph (some graphs do not allow certain types) - LGraph.prototype.getSupportedTypes = function() { - return this.supported_types || LGraph.supported_types; - }; - - LGraph.STATUS_STOPPED = 1; - LGraph.STATUS_RUNNING = 2; - - /** - * Removes all nodes from this graph - * @method clear - */ - - LGraph.prototype.clear = function() { - this.stop(); - this.status = LGraph.STATUS_STOPPED; - - this.last_node_id = 0; - this.last_link_id = 0; - - this._version = -1; //used to detect changes - - //safe clear - if (this._nodes) { - for (var i = 0; i < this._nodes.length; ++i) { - var node = this._nodes[i]; - if (node.onRemoved) { - node.onRemoved(); - } - } - } - - //nodes - this._nodes = []; - this._nodes_by_id = {}; - this._nodes_in_order = []; //nodes that are executable sorted in execution order - this._nodes_executable = null; //nodes that contain onExecute - - //other scene stuff - this._groups = []; - - //links - this.links = {}; //container with all the links - - //iterations - this.iteration = 0; - - //custom data - this.config = {}; - this.vars = {}; - - //timing - this.globaltime = 0; - this.runningtime = 0; - this.fixedtime = 0; - this.fixedtime_lapse = 0.01; - this.elapsed_time = 0.01; - this.last_update_time = 0; - this.starttime = 0; - - this.catch_errors = true; - - //subgraph_data - this.inputs = {}; - this.outputs = {}; - - //notify canvas to redraw - this.change(); - - this.sendActionToCanvas("clear"); - }; - - /** - * Attach Canvas to this graph - * @method attachCanvas - * @param {GraphCanvas} graph_canvas - */ - - LGraph.prototype.attachCanvas = function(graphcanvas) { - if (graphcanvas.constructor != LGraphCanvas) { - throw "attachCanvas expects a LGraphCanvas instance"; - } - if (graphcanvas.graph && graphcanvas.graph != this) { - graphcanvas.graph.detachCanvas(graphcanvas); - } - - graphcanvas.graph = this; - if (!this.list_of_graphcanvas) { - this.list_of_graphcanvas = []; - } - this.list_of_graphcanvas.push(graphcanvas); - }; - - /** - * Detach Canvas from this graph - * @method detachCanvas - * @param {GraphCanvas} graph_canvas - */ - LGraph.prototype.detachCanvas = function(graphcanvas) { - if (!this.list_of_graphcanvas) { - return; - } - - var pos = this.list_of_graphcanvas.indexOf(graphcanvas); - if (pos == -1) { - return; - } - graphcanvas.graph = null; - this.list_of_graphcanvas.splice(pos, 1); - }; - - /** - * Starts running this graph every interval milliseconds. - * @method start - * @param {number} interval amount of milliseconds between executions, if 0 then it renders to the monitor refresh rate - */ - - LGraph.prototype.start = function(interval) { - if (this.status == LGraph.STATUS_RUNNING) { - return; - } - this.status = LGraph.STATUS_RUNNING; - - if (this.onPlayEvent) { - this.onPlayEvent(); - } - - this.sendEventToAllNodes("onStart"); - - //launch - this.starttime = LiteGraph.getTime(); - this.last_update_time = this.starttime; - interval = interval || 0; - var that = this; - - //execute once per frame - if ( interval == 0 && typeof window != "undefined" && window.requestAnimationFrame ) { - function on_frame() { - if (that.execution_timer_id != -1) { - return; - } - window.requestAnimationFrame(on_frame); - if(that.onBeforeStep) - that.onBeforeStep(); - that.runStep(1, !this.catch_errors); - if(that.onAfterStep) - that.onAfterStep(); - } - this.execution_timer_id = -1; - on_frame(); - } else { //execute every 'interval' ms - this.execution_timer_id = setInterval(function() { - //execute - if(that.onBeforeStep) - that.onBeforeStep(); - that.runStep(1, !this.catch_errors); - if(that.onAfterStep) - that.onAfterStep(); - }, interval); - } - }; - - /** - * Stops the execution loop of the graph - * @method stop execution - */ - - LGraph.prototype.stop = function() { - if (this.status == LGraph.STATUS_STOPPED) { - return; - } - - this.status = LGraph.STATUS_STOPPED; - - if (this.onStopEvent) { - this.onStopEvent(); - } - - if (this.execution_timer_id != null) { - if (this.execution_timer_id != -1) { - clearInterval(this.execution_timer_id); - } - this.execution_timer_id = null; - } - - this.sendEventToAllNodes("onStop"); - }; - - /** - * Run N steps (cycles) of the graph - * @method runStep - * @param {number} num number of steps to run, default is 1 - * @param {Boolean} do_not_catch_errors [optional] if you want to try/catch errors - * @param {number} limit max number of nodes to execute (used to execute from start to a node) - */ - - LGraph.prototype.runStep = function(num, do_not_catch_errors, limit ) { - num = num || 1; - - var start = LiteGraph.getTime(); - this.globaltime = 0.001 * (start - this.starttime); - - var nodes = this._nodes_executable - ? this._nodes_executable - : this._nodes; - if (!nodes) { - return; - } - - limit = limit || nodes.length; - - if (do_not_catch_errors) { - //iterations - for (var i = 0; i < num; i++) { - for (var j = 0; j < limit; ++j) { - var node = nodes[j]; - if (node.mode == LiteGraph.ALWAYS && node.onExecute) { - node.onExecute(); //hard to send elapsed time - } - } - - this.fixedtime += this.fixedtime_lapse; - if (this.onExecuteStep) { - this.onExecuteStep(); - } - } - - if (this.onAfterExecute) { - this.onAfterExecute(); - } - } else { - try { - //iterations - for (var i = 0; i < num; i++) { - for (var j = 0; j < limit; ++j) { - var node = nodes[j]; - if (node.mode == LiteGraph.ALWAYS && node.onExecute) { - node.onExecute(); - } - } - - this.fixedtime += this.fixedtime_lapse; - if (this.onExecuteStep) { - this.onExecuteStep(); - } - } - - if (this.onAfterExecute) { - this.onAfterExecute(); - } - this.errors_in_execution = false; - } catch (err) { - this.errors_in_execution = true; - if (LiteGraph.throw_errors) { - throw err; - } - if (LiteGraph.debug) { - console.log("Error during execution: " + err); - } - this.stop(); - } - } - - var now = LiteGraph.getTime(); - var elapsed = now - start; - if (elapsed == 0) { - elapsed = 1; - } - this.execution_time = 0.001 * elapsed; - this.globaltime += 0.001 * elapsed; - this.iteration += 1; - this.elapsed_time = (now - this.last_update_time) * 0.001; - this.last_update_time = now; - }; - - /** - * Updates the graph execution order according to relevance of the nodes (nodes with only outputs have more relevance than - * nodes with only inputs. - * @method updateExecutionOrder - */ - LGraph.prototype.updateExecutionOrder = function() { - this._nodes_in_order = this.computeExecutionOrder(false); - this._nodes_executable = []; - for (var i = 0; i < this._nodes_in_order.length; ++i) { - if (this._nodes_in_order[i].onExecute) { - this._nodes_executable.push(this._nodes_in_order[i]); - } - } - }; - - //This is more internal, it computes the executable nodes in order and returns it - LGraph.prototype.computeExecutionOrder = function( - only_onExecute, - set_level - ) { - var L = []; - var S = []; - var M = {}; - var visited_links = {}; //to avoid repeating links - var remaining_links = {}; //to a - - //search for the nodes without inputs (starting nodes) - for (var i = 0, l = this._nodes.length; i < l; ++i) { - var node = this._nodes[i]; - if (only_onExecute && !node.onExecute) { - continue; - } - - M[node.id] = node; //add to pending nodes - - var num = 0; //num of input connections - if (node.inputs) { - for (var j = 0, l2 = node.inputs.length; j < l2; j++) { - if (node.inputs[j] && node.inputs[j].link != null) { - num += 1; - } - } - } - - if (num == 0) { - //is a starting node - S.push(node); - if (set_level) { - node._level = 1; - } - } //num of input links - else { - if (set_level) { - node._level = 0; - } - remaining_links[node.id] = num; - } - } - - while (true) { - if (S.length == 0) { - break; - } - - //get an starting node - var node = S.shift(); - L.push(node); //add to ordered list - delete M[node.id]; //remove from the pending nodes - - if (!node.outputs) { - continue; - } - - //for every output - for (var i = 0; i < node.outputs.length; i++) { - var output = node.outputs[i]; - //not connected - if ( - output == null || - output.links == null || - output.links.length == 0 - ) { - continue; - } - - //for every connection - for (var j = 0; j < output.links.length; j++) { - var link_id = output.links[j]; - var link = this.links[link_id]; - if (!link) { - continue; - } - - //already visited link (ignore it) - if (visited_links[link.id]) { - continue; - } - - var target_node = this.getNodeById(link.target_id); - if (target_node == null) { - visited_links[link.id] = true; - continue; - } - - if ( - set_level && - (!target_node._level || - target_node._level <= node._level) - ) { - target_node._level = node._level + 1; - } - - visited_links[link.id] = true; //mark as visited - remaining_links[target_node.id] -= 1; //reduce the number of links remaining - if (remaining_links[target_node.id] == 0) { - S.push(target_node); - } //if no more links, then add to starters array - } - } - } - - //the remaining ones (loops) - for (var i in M) { - L.push(M[i]); - } - - if (L.length != this._nodes.length && LiteGraph.debug) { - console.warn("something went wrong, nodes missing"); - } - - var l = L.length; - - //save order number in the node - for (var i = 0; i < l; ++i) { - L[i].order = i; - } - - //sort now by priority - L = L.sort(function(A, B) { - var Ap = A.constructor.priority || A.priority || 0; - var Bp = B.constructor.priority || B.priority || 0; - if (Ap == Bp) { - //if same priority, sort by order - return A.order - B.order; - } - return Ap - Bp; //sort by priority - }); - - //save order number in the node, again... - for (var i = 0; i < l; ++i) { - L[i].order = i; - } - - return L; - }; - - /** - * Returns all the nodes that could affect this one (ancestors) by crawling all the inputs recursively. - * It doesn't include the node itself - * @method getAncestors - * @return {Array} an array with all the LGraphNodes that affect this node, in order of execution - */ - LGraph.prototype.getAncestors = function(node) { - var ancestors = []; - var pending = [node]; - var visited = {}; - - while (pending.length) { - var current = pending.shift(); - if (!current.inputs) { - continue; - } - if (!visited[current.id] && current != node) { - visited[current.id] = true; - ancestors.push(current); - } - - for (var i = 0; i < current.inputs.length; ++i) { - var input = current.getInputNode(i); - if (input && ancestors.indexOf(input) == -1) { - pending.push(input); - } - } - } - - ancestors.sort(function(a, b) { - return a.order - b.order; - }); - return ancestors; - }; - - /** - * Positions every node in a more readable manner - * @method arrange - */ - LGraph.prototype.arrange = function(margin) { - margin = margin || 100; - - var nodes = this.computeExecutionOrder(false, true); - var columns = []; - for (var i = 0; i < nodes.length; ++i) { - var node = nodes[i]; - var col = node._level || 1; - if (!columns[col]) { - columns[col] = []; - } - columns[col].push(node); - } - - var x = margin; - - for (var i = 0; i < columns.length; ++i) { - var column = columns[i]; - if (!column) { - continue; - } - var max_size = 100; - var y = margin + LiteGraph.NODE_TITLE_HEIGHT; - for (var j = 0; j < column.length; ++j) { - var node = column[j]; - node.pos[0] = x; - node.pos[1] = y; - if (node.size[0] > max_size) { - max_size = node.size[0]; - } - y += node.size[1] + margin + LiteGraph.NODE_TITLE_HEIGHT; - } - x += max_size + margin; - } - - this.setDirtyCanvas(true, true); - }; - - /** - * Returns the amount of time the graph has been running in milliseconds - * @method getTime - * @return {number} number of milliseconds the graph has been running - */ - LGraph.prototype.getTime = function() { - return this.globaltime; - }; - - /** - * Returns the amount of time accumulated using the fixedtime_lapse var. This is used in context where the time increments should be constant - * @method getFixedTime - * @return {number} number of milliseconds the graph has been running - */ - - LGraph.prototype.getFixedTime = function() { - return this.fixedtime; - }; - - /** - * Returns the amount of time it took to compute the latest iteration. Take into account that this number could be not correct - * if the nodes are using graphical actions - * @method getElapsedTime - * @return {number} number of milliseconds it took the last cycle - */ - - LGraph.prototype.getElapsedTime = function() { - return this.elapsed_time; - }; - - /** - * Sends an event to all the nodes, useful to trigger stuff - * @method sendEventToAllNodes - * @param {String} eventname the name of the event (function to be called) - * @param {Array} params parameters in array format - */ - LGraph.prototype.sendEventToAllNodes = function(eventname, params, mode) { - mode = mode || LiteGraph.ALWAYS; - - var nodes = this._nodes_in_order ? this._nodes_in_order : this._nodes; - if (!nodes) { - return; - } - - for (var j = 0, l = nodes.length; j < l; ++j) { - var node = nodes[j]; - - if ( - node.constructor === LiteGraph.Subgraph && - eventname != "onExecute" - ) { - if (node.mode == mode) { - node.sendEventToAllNodes(eventname, params, mode); - } - continue; - } - - if (!node[eventname] || node.mode != mode) { - continue; - } - if (params === undefined) { - node[eventname](); - } else if (params && params.constructor === Array) { - node[eventname].apply(node, params); - } else { - node[eventname](params); - } - } - }; - - LGraph.prototype.sendActionToCanvas = function(action, params) { - if (!this.list_of_graphcanvas) { - return; - } - - for (var i = 0; i < this.list_of_graphcanvas.length; ++i) { - var c = this.list_of_graphcanvas[i]; - if (c[action]) { - c[action].apply(c, params); - } - } - }; - - /** - * Adds a new node instance to this graph - * @method add - * @param {LGraphNode} node the instance of the node - */ - - LGraph.prototype.add = function(node, skip_compute_order) { - if (!node) { - return; - } - - //groups - if (node.constructor === LGraphGroup) { - this._groups.push(node); - this.setDirtyCanvas(true); - this.change(); - node.graph = this; - this._version++; - return; - } - - //nodes - if (node.id != -1 && this._nodes_by_id[node.id] != null) { - console.warn( - "LiteGraph: there is already a node with this ID, changing it" - ); - node.id = ++this.last_node_id; - } - - if (this._nodes.length >= LiteGraph.MAX_NUMBER_OF_NODES) { - throw "LiteGraph: max number of nodes in a graph reached"; - } - - //give him an id - if (node.id == null || node.id == -1) { - node.id = ++this.last_node_id; - } else if (this.last_node_id < node.id) { - this.last_node_id = node.id; - } - - node.graph = this; - this._version++; - - this._nodes.push(node); - this._nodes_by_id[node.id] = node; - - if (node.onAdded) { - node.onAdded(this); - } - - if (this.config.align_to_grid) { - node.alignToGrid(); - } - - if (!skip_compute_order) { - this.updateExecutionOrder(); - } - - if (this.onNodeAdded) { - this.onNodeAdded(node); - } - - this.setDirtyCanvas(true); - this.change(); - - return node; //to chain actions - }; - - /** - * Removes a node from the graph - * @method remove - * @param {LGraphNode} node the instance of the node - */ - - LGraph.prototype.remove = function(node) { - if (node.constructor === LiteGraph.LGraphGroup) { - var index = this._groups.indexOf(node); - if (index != -1) { - this._groups.splice(index, 1); - } - node.graph = null; - this._version++; - this.setDirtyCanvas(true, true); - this.change(); - return; - } - - if (this._nodes_by_id[node.id] == null) { - return; - } //not found - - if (node.ignore_remove) { - return; - } //cannot be removed - - //disconnect inputs - if (node.inputs) { - for (var i = 0; i < node.inputs.length; i++) { - var slot = node.inputs[i]; - if (slot.link != null) { - node.disconnectInput(i); - } - } - } - - //disconnect outputs - if (node.outputs) { - for (var i = 0; i < node.outputs.length; i++) { - var slot = node.outputs[i]; - if (slot.links != null && slot.links.length) { - node.disconnectOutput(i); - } - } - } - - //node.id = -1; //why? - - //callback - if (node.onRemoved) { - node.onRemoved(); - } - - node.graph = null; - this._version++; - - //remove from canvas render - if (this.list_of_graphcanvas) { - for (var i = 0; i < this.list_of_graphcanvas.length; ++i) { - var canvas = this.list_of_graphcanvas[i]; - if (canvas.selected_nodes[node.id]) { - delete canvas.selected_nodes[node.id]; - } - if (canvas.node_dragged == node) { - canvas.node_dragged = null; - } - } - } - - //remove from containers - var pos = this._nodes.indexOf(node); - if (pos != -1) { - this._nodes.splice(pos, 1); - } - delete this._nodes_by_id[node.id]; - - if (this.onNodeRemoved) { - this.onNodeRemoved(node); - } - - this.setDirtyCanvas(true, true); - this.change(); - - this.updateExecutionOrder(); - }; - - /** - * Returns a node by its id. - * @method getNodeById - * @param {Number} id - */ - - LGraph.prototype.getNodeById = function(id) { - if (id == null) { - return null; - } - return this._nodes_by_id[id]; - }; - - /** - * Returns a list of nodes that matches a class - * @method findNodesByClass - * @param {Class} classObject the class itself (not an string) - * @return {Array} a list with all the nodes of this type - */ - LGraph.prototype.findNodesByClass = function(classObject, result) { - result = result || []; - result.length = 0; - for (var i = 0, l = this._nodes.length; i < l; ++i) { - if (this._nodes[i].constructor === classObject) { - result.push(this._nodes[i]); - } - } - return result; - }; - - /** - * Returns a list of nodes that matches a type - * @method findNodesByType - * @param {String} type the name of the node type - * @return {Array} a list with all the nodes of this type - */ - LGraph.prototype.findNodesByType = function(type, result) { - var type = type.toLowerCase(); - result = result || []; - result.length = 0; - for (var i = 0, l = this._nodes.length; i < l; ++i) { - if (this._nodes[i].type.toLowerCase() == type) { - result.push(this._nodes[i]); - } - } - return result; - }; - - /** - * Returns the first node that matches a name in its title - * @method findNodeByTitle - * @param {String} name the name of the node to search - * @return {Node} the node or null - */ - LGraph.prototype.findNodeByTitle = function(title) { - for (var i = 0, l = this._nodes.length; i < l; ++i) { - if (this._nodes[i].title == title) { - return this._nodes[i]; - } - } - return null; - }; - - /** - * Returns a list of nodes that matches a name - * @method findNodesByTitle - * @param {String} name the name of the node to search - * @return {Array} a list with all the nodes with this name - */ - LGraph.prototype.findNodesByTitle = function(title) { - var result = []; - for (var i = 0, l = this._nodes.length; i < l; ++i) { - if (this._nodes[i].title == title) { - result.push(this._nodes[i]); - } - } - return result; - }; - - /** - * Returns the top-most node in this position of the canvas - * @method getNodeOnPos - * @param {number} x the x coordinate in canvas space - * @param {number} y the y coordinate in canvas space - * @param {Array} nodes_list a list with all the nodes to search from, by default is all the nodes in the graph - * @return {LGraphNode} the node at this position or null - */ - LGraph.prototype.getNodeOnPos = function(x, y, nodes_list, margin) { - nodes_list = nodes_list || this._nodes; - for (var i = nodes_list.length - 1; i >= 0; i--) { - var n = nodes_list[i]; - if (n.isPointInside(x, y, margin)) { - return n; - } - } - return null; - }; - - /** - * Returns the top-most group in that position - * @method getGroupOnPos - * @param {number} x the x coordinate in canvas space - * @param {number} y the y coordinate in canvas space - * @return {LGraphGroup} the group or null - */ - LGraph.prototype.getGroupOnPos = function(x, y) { - for (var i = this._groups.length - 1; i >= 0; i--) { - var g = this._groups[i]; - if (g.isPointInside(x, y, 2, true)) { - return g; - } - } - return null; - }; - - /** - * Checks that the node type matches the node type registered, used when replacing a nodetype by a newer version during execution - * this replaces the ones using the old version with the new version - * @method checkNodeTypes - */ - LGraph.prototype.checkNodeTypes = function() { - var changes = false; - for (var i = 0; i < this._nodes.length; i++) { - var node = this._nodes[i]; - var ctor = LiteGraph.registered_node_types[node.type]; - if (node.constructor == ctor) { - continue; - } - console.log("node being replaced by newer version: " + node.type); - var newnode = LiteGraph.createNode(node.type); - changes = true; - this._nodes[i] = newnode; - newnode.configure(node.serialize()); - newnode.graph = this; - this._nodes_by_id[newnode.id] = newnode; - if (node.inputs) { - newnode.inputs = node.inputs.concat(); - } - if (node.outputs) { - newnode.outputs = node.outputs.concat(); - } - } - this.updateExecutionOrder(); - }; - - // ********** GLOBALS ***************** - - LGraph.prototype.onAction = function(action, param) { - this._input_nodes = this.findNodesByClass( - LiteGraph.GraphInput, - this._input_nodes - ); - for (var i = 0; i < this._input_nodes.length; ++i) { - var node = this._input_nodes[i]; - if (node.properties.name != action) { - continue; - } - node.onAction(action, param); - break; - } - }; - - LGraph.prototype.trigger = function(action, param) { - if (this.onTrigger) { - this.onTrigger(action, param); - } - }; - - /** - * Tell this graph it has a global graph input of this type - * @method addGlobalInput - * @param {String} name - * @param {String} type - * @param {*} value [optional] - */ - LGraph.prototype.addInput = function(name, type, value) { - var input = this.inputs[name]; - if (input) { - //already exist - return; - } - - this.inputs[name] = { name: name, type: type, value: value }; - this._version++; - - if (this.onInputAdded) { - this.onInputAdded(name, type); - } - - if (this.onInputsOutputsChange) { - this.onInputsOutputsChange(); - } - }; - - /** - * Assign a data to the global graph input - * @method setGlobalInputData - * @param {String} name - * @param {*} data - */ - LGraph.prototype.setInputData = function(name, data) { - var input = this.inputs[name]; - if (!input) { - return; - } - input.value = data; - }; - - /** - * Returns the current value of a global graph input - * @method getInputData - * @param {String} name - * @return {*} the data - */ - LGraph.prototype.getInputData = function(name) { - var input = this.inputs[name]; - if (!input) { - return null; - } - return input.value; - }; - - /** - * Changes the name of a global graph input - * @method renameInput - * @param {String} old_name - * @param {String} new_name - */ - LGraph.prototype.renameInput = function(old_name, name) { - if (name == old_name) { - return; - } - - if (!this.inputs[old_name]) { - return false; - } - - if (this.inputs[name]) { - console.error("there is already one input with that name"); - return false; - } - - this.inputs[name] = this.inputs[old_name]; - delete this.inputs[old_name]; - this._version++; - - if (this.onInputRenamed) { - this.onInputRenamed(old_name, name); - } - - if (this.onInputsOutputsChange) { - this.onInputsOutputsChange(); - } - }; - - /** - * Changes the type of a global graph input - * @method changeInputType - * @param {String} name - * @param {String} type - */ - LGraph.prototype.changeInputType = function(name, type) { - if (!this.inputs[name]) { - return false; - } - - if ( - this.inputs[name].type && - String(this.inputs[name].type).toLowerCase() == - String(type).toLowerCase() - ) { - return; - } - - this.inputs[name].type = type; - this._version++; - if (this.onInputTypeChanged) { - this.onInputTypeChanged(name, type); - } - }; - - /** - * Removes a global graph input - * @method removeInput - * @param {String} name - * @param {String} type - */ - LGraph.prototype.removeInput = function(name) { - if (!this.inputs[name]) { - return false; - } - - delete this.inputs[name]; - this._version++; - - if (this.onInputRemoved) { - this.onInputRemoved(name); - } - - if (this.onInputsOutputsChange) { - this.onInputsOutputsChange(); - } - return true; - }; - - /** - * Creates a global graph output - * @method addOutput - * @param {String} name - * @param {String} type - * @param {*} value - */ - LGraph.prototype.addOutput = function(name, type, value) { - this.outputs[name] = { name: name, type: type, value: value }; - this._version++; - - if (this.onOutputAdded) { - this.onOutputAdded(name, type); - } - - if (this.onInputsOutputsChange) { - this.onInputsOutputsChange(); - } - }; - - /** - * Assign a data to the global output - * @method setOutputData - * @param {String} name - * @param {String} value - */ - LGraph.prototype.setOutputData = function(name, value) { - var output = this.outputs[name]; - if (!output) { - return; - } - output.value = value; - }; - - /** - * Returns the current value of a global graph output - * @method getOutputData - * @param {String} name - * @return {*} the data - */ - LGraph.prototype.getOutputData = function(name) { - var output = this.outputs[name]; - if (!output) { - return null; - } - return output.value; - }; - - /** - * Renames a global graph output - * @method renameOutput - * @param {String} old_name - * @param {String} new_name - */ - LGraph.prototype.renameOutput = function(old_name, name) { - if (!this.outputs[old_name]) { - return false; - } - - if (this.outputs[name]) { - console.error("there is already one output with that name"); - return false; - } - - this.outputs[name] = this.outputs[old_name]; - delete this.outputs[old_name]; - this._version++; - - if (this.onOutputRenamed) { - this.onOutputRenamed(old_name, name); - } - - if (this.onInputsOutputsChange) { - this.onInputsOutputsChange(); - } - }; - - /** - * Changes the type of a global graph output - * @method changeOutputType - * @param {String} name - * @param {String} type - */ - LGraph.prototype.changeOutputType = function(name, type) { - if (!this.outputs[name]) { - return false; - } - - if ( - this.outputs[name].type && - String(this.outputs[name].type).toLowerCase() == - String(type).toLowerCase() - ) { - return; - } - - this.outputs[name].type = type; - this._version++; - if (this.onOutputTypeChanged) { - this.onOutputTypeChanged(name, type); - } - }; - - /** - * Removes a global graph output - * @method removeOutput - * @param {String} name - */ - LGraph.prototype.removeOutput = function(name) { - if (!this.outputs[name]) { - return false; - } - delete this.outputs[name]; - this._version++; - - if (this.onOutputRemoved) { - this.onOutputRemoved(name); - } - - if (this.onInputsOutputsChange) { - this.onInputsOutputsChange(); - } - return true; - }; - - LGraph.prototype.triggerInput = function(name, value) { - var nodes = this.findNodesByTitle(name); - for (var i = 0; i < nodes.length; ++i) { - nodes[i].onTrigger(value); - } - }; - - LGraph.prototype.setCallback = function(name, func) { - var nodes = this.findNodesByTitle(name); - for (var i = 0; i < nodes.length; ++i) { - nodes[i].setTrigger(func); - } - }; - - LGraph.prototype.connectionChange = function(node, link_info) { - this.updateExecutionOrder(); - if (this.onConnectionChange) { - this.onConnectionChange(node); - } - this._version++; - this.sendActionToCanvas("onConnectionChange"); - }; - - /** - * returns if the graph is in live mode - * @method isLive - */ - - LGraph.prototype.isLive = function() { - if (!this.list_of_graphcanvas) { - return false; - } - - for (var i = 0; i < this.list_of_graphcanvas.length; ++i) { - var c = this.list_of_graphcanvas[i]; - if (c.live_mode) { - return true; - } - } - return false; - }; - - /** - * clears the triggered slot animation in all links (stop visual animation) - * @method clearTriggeredSlots - */ - LGraph.prototype.clearTriggeredSlots = function() { - for (var i in this.links) { - var link_info = this.links[i]; - if (!link_info) { - continue; - } - if (link_info._last_time) { - link_info._last_time = 0; - } - } - }; - - /* Called when something visually changed (not the graph!) */ - LGraph.prototype.change = function() { - if (LiteGraph.debug) { - console.log("Graph changed"); - } - this.sendActionToCanvas("setDirty", [true, true]); - if (this.on_change) { - this.on_change(this); - } - }; - - LGraph.prototype.setDirtyCanvas = function(fg, bg) { - this.sendActionToCanvas("setDirty", [fg, bg]); - }; - - /** - * Destroys a link - * @method removeLink - * @param {Number} link_id - */ - LGraph.prototype.removeLink = function(link_id) { - var link = this.links[link_id]; - if (!link) { - return; - } - var node = this.getNodeById(link.target_id); - if (node) { - node.disconnectInput(link.target_slot); - } - }; - - //save and recover app state *************************************** - /** - * Creates a Object containing all the info about this graph, it can be serialized - * @method serialize - * @return {Object} value of the node - */ - LGraph.prototype.serialize = function() { - var nodes_info = []; - for (var i = 0, l = this._nodes.length; i < l; ++i) { - nodes_info.push(this._nodes[i].serialize()); - } - - //pack link info into a non-verbose format - var links = []; - for (var i in this.links) { - //links is an OBJECT - var link = this.links[i]; - if (!link.serialize) { - //weird bug I havent solved yet - console.warn( - "weird LLink bug, link info is not a LLink but a regular object" - ); - var link2 = new LLink(); - for (var i in link) { - link2[i] = link[i]; - } - this.links[i] = link2; - link = link2; - } - - links.push(link.serialize()); - } - - var groups_info = []; - for (var i = 0; i < this._groups.length; ++i) { - groups_info.push(this._groups[i].serialize()); - } - - var data = { - last_node_id: this.last_node_id, - last_link_id: this.last_link_id, - nodes: nodes_info, - links: links, - groups: groups_info, - config: this.config, - version: LiteGraph.VERSION - }; - - return data; - }; - - /** - * Configure a graph from a JSON string - * @method configure - * @param {String} str configure a graph from a JSON string - * @param {Boolean} returns if there was any error parsing - */ - LGraph.prototype.configure = function(data, keep_old) { - if (!data) { - return; - } - - if (!keep_old) { - this.clear(); - } - - var nodes = data.nodes; - - //decode links info (they are very verbose) - if (data.links && data.links.constructor === Array) { - var links = []; - for (var i = 0; i < data.links.length; ++i) { - var link_data = data.links[i]; - if(!link_data) //weird bug - { - console.warn("serialized graph link data contains errors, skipping."); - continue; - } - var link = new LLink(); - link.configure(link_data); - links[link.id] = link; - } - data.links = links; - } - - //copy all stored fields - for (var i in data) { - if(i == "nodes" || i == "groups" ) //links must be accepted - continue; - this[i] = data[i]; - } - - var error = false; - - //create nodes - this._nodes = []; - if (nodes) { - for (var i = 0, l = nodes.length; i < l; ++i) { - var n_info = nodes[i]; //stored info - var node = LiteGraph.createNode(n_info.type, n_info.title); - if (!node) { - if (LiteGraph.debug) { - console.log( - "Node not found or has errors: " + n_info.type - ); - } - - //in case of error we create a replacement node to avoid losing info - node = new LGraphNode(); - node.last_serialization = n_info; - node.has_errors = true; - error = true; - //continue; - } - - node.id = n_info.id; //id it or it will create a new id - this.add(node, true); //add before configure, otherwise configure cannot create links - } - - //configure nodes afterwards so they can reach each other - for (var i = 0, l = nodes.length; i < l; ++i) { - var n_info = nodes[i]; - var node = this.getNodeById(n_info.id); - if (node) { - node.configure(n_info); - } - } - } - - //groups - this._groups.length = 0; - if (data.groups) { - for (var i = 0; i < data.groups.length; ++i) { - var group = new LiteGraph.LGraphGroup(); - group.configure(data.groups[i]); - this.add(group); - } - } - - this.updateExecutionOrder(); - this._version++; - this.setDirtyCanvas(true, true); - return error; - }; - - LGraph.prototype.load = function(url) { - var that = this; - var req = new XMLHttpRequest(); - req.open("GET", url, true); - req.send(null); - req.onload = function(oEvent) { - if (req.status !== 200) { - console.error("Error loading graph:", req.status, req.response); - return; - } - var data = JSON.parse(req.response); - that.configure(data); - }; - req.onerror = function(err) { - console.error("Error loading graph:", err); - }; - }; - - LGraph.prototype.onNodeTrace = function(node, msg, color) { - //TODO - }; - - //this is the class in charge of storing link information - function LLink(id, type, origin_id, origin_slot, target_id, target_slot) { - this.id = id; - this.type = type; - this.origin_id = origin_id; - this.origin_slot = origin_slot; - this.target_id = target_id; - this.target_slot = target_slot; - - this._data = null; - this._pos = new Float32Array(2); //center - } - - LLink.prototype.configure = function(o) { - if (o.constructor === Array) { - this.id = o[0]; - this.origin_id = o[1]; - this.origin_slot = o[2]; - this.target_id = o[3]; - this.target_slot = o[4]; - this.type = o[5]; - } else { - this.id = o.id; - this.type = o.type; - this.origin_id = o.origin_id; - this.origin_slot = o.origin_slot; - this.target_id = o.target_id; - this.target_slot = o.target_slot; - } - }; - - LLink.prototype.serialize = function() { - return [ - this.id, - this.origin_id, - this.origin_slot, - this.target_id, - this.target_slot, - this.type - ]; - }; - - LiteGraph.LLink = LLink; - - // ************************************************************* - // Node CLASS ******* - // ************************************************************* - - /* - title: string - pos: [x,y] - size: [x,y] - - input|output: every connection - + { name:string, type:string, pos: [x,y]=Optional, direction: "input"|"output", links: Array }); - - general properties: - + clip_area: if you render outside the node, it will be clipped - + unsafe_execution: not allowed for safe execution - + skip_repeated_outputs: when adding new outputs, it wont show if there is one already connected - + resizable: if set to false it wont be resizable with the mouse - + horizontal: slots are distributed horizontally - + widgets_start_y: widgets start at y distance from the top of the node - - flags object: - + collapsed: if it is collapsed - - supported callbacks: - + onAdded: when added to graph (warning: this is called BEFORE the node is configured when loading) - + onRemoved: when removed from graph - + onStart: when the graph starts playing - + onStop: when the graph stops playing - + onDrawForeground: render the inside widgets inside the node - + onDrawBackground: render the background area inside the node (only in edit mode) - + onMouseDown - + onMouseMove - + onMouseUp - + onMouseEnter - + onMouseLeave - + onExecute: execute the node - + onPropertyChanged: when a property is changed in the panel (return true to skip default behaviour) - + onGetInputs: returns an array of possible inputs - + onGetOutputs: returns an array of possible outputs - + onBounding: in case this node has a bigger bounding than the node itself (the callback receives the bounding as [x,y,w,h]) - + onDblClick: double clicked in the node - + onInputDblClick: input slot double clicked (can be used to automatically create a node connected) - + onOutputDblClick: output slot double clicked (can be used to automatically create a node connected) - + onConfigure: called after the node has been configured - + onSerialize: to add extra info when serializing (the callback receives the object that should be filled with the data) - + onSelected - + onDeselected - + onDropItem : DOM item dropped over the node - + onDropFile : file dropped over the node - + onConnectInput : if returns false the incoming connection will be canceled - + onConnectionsChange : a connection changed (new one or removed) (LiteGraph.INPUT or LiteGraph.OUTPUT, slot, true if connected, link_info, input_info ) - + onAction: action slot triggered - + getExtraMenuOptions: to add option to context menu -*/ - - /** - * Base Class for all the node type classes - * @class LGraphNode - * @param {String} name a name for the node - */ - - function LGraphNode(title) { - this._ctor(title); - } - - global.LGraphNode = LiteGraph.LGraphNode = LGraphNode; - - LGraphNode.prototype._ctor = function(title) { - this.title = title || "Unnamed"; - this.size = [LiteGraph.NODE_WIDTH, 60]; - this.graph = null; - - this._pos = new Float32Array(10, 10); - - Object.defineProperty(this, "pos", { - set: function(v) { - if (!v || v.length < 2) { - return; - } - this._pos[0] = v[0]; - this._pos[1] = v[1]; - }, - get: function() { - return this._pos; - }, - enumerable: true - }); - - this.id = -1; //not know till not added - this.type = null; - - //inputs available: array of inputs - this.inputs = []; - this.outputs = []; - this.connections = []; - - //local data - this.properties = {}; //for the values - this.properties_info = []; //for the info - - this.flags = {}; - }; - - /** - * configure a node from an object containing the serialized info - * @method configure - */ - LGraphNode.prototype.configure = function(info) { - if (this.graph) { - this.graph._version++; - } - for (var j in info) { - if (j == "properties") { - //i don't want to clone properties, I want to reuse the old container - for (var k in info.properties) { - this.properties[k] = info.properties[k]; - if (this.onPropertyChanged) { - this.onPropertyChanged( k, info.properties[k] ); - } - } - continue; - } - - if (info[j] == null) { - continue; - } else if (typeof info[j] == "object") { - //object - if (this[j] && this[j].configure) { - this[j].configure(info[j]); - } else { - this[j] = LiteGraph.cloneObject(info[j], this[j]); - } - } //value - else { - this[j] = info[j]; - } - } - - if (!info.title) { - this.title = this.constructor.title; - } - - if (this.onConnectionsChange) { - if (this.inputs) { - for (var i = 0; i < this.inputs.length; ++i) { - var input = this.inputs[i]; - var link_info = this.graph - ? this.graph.links[input.link] - : null; - this.onConnectionsChange( - LiteGraph.INPUT, - i, - true, - link_info, - input - ); //link_info has been created now, so its updated - } - } - - if (this.outputs) { - for (var i = 0; i < this.outputs.length; ++i) { - var output = this.outputs[i]; - if (!output.links) { - continue; - } - for (var j = 0; j < output.links.length; ++j) { - var link_info = this.graph - ? this.graph.links[output.links[j]] - : null; - this.onConnectionsChange( - LiteGraph.OUTPUT, - i, - true, - link_info, - output - ); //link_info has been created now, so its updated - } - } - } - } - - if( this.widgets ) - { - for (var i = 0; i < this.widgets.length; ++i) - { - var w = this.widgets[i]; - if(!w) - continue; - if(w.options && w.options.property && this.properties[ w.options.property ]) - w.value = JSON.parse( JSON.stringify( this.properties[ w.options.property ] ) ); - } - if (info.widgets_values) { - for (var i = 0; i < info.widgets_values.length; ++i) { - if (this.widgets[i]) { - this.widgets[i].value = info.widgets_values[i]; - } - } - } - } - - if (this.onConfigure) { - this.onConfigure(info); - } - }; - - /** - * serialize the content - * @method serialize - */ - - LGraphNode.prototype.serialize = function() { - //create serialization object - var o = { - id: this.id, - type: this.type, - pos: this.pos, - size: this.size, - flags: LiteGraph.cloneObject(this.flags), - order: this.order, - mode: this.mode - }; - - //special case for when there were errors - if (this.constructor === LGraphNode && this.last_serialization) { - return this.last_serialization; - } - - if (this.inputs) { - o.inputs = this.inputs; - } - - if (this.outputs) { - //clear outputs last data (because data in connections is never serialized but stored inside the outputs info) - for (var i = 0; i < this.outputs.length; i++) { - delete this.outputs[i]._data; - } - o.outputs = this.outputs; - } - - if (this.title && this.title != this.constructor.title) { - o.title = this.title; - } - - if (this.properties) { - o.properties = LiteGraph.cloneObject(this.properties); - } - - if (this.widgets && this.serialize_widgets) { - o.widgets_values = []; - for (var i = 0; i < this.widgets.length; ++i) { - if(this.widgets[i]) - o.widgets_values[i] = this.widgets[i].value; - else - o.widgets_values[i] = null; - } - } - - if (!o.type) { - o.type = this.constructor.type; - } - - if (this.color) { - o.color = this.color; - } - if (this.bgcolor) { - o.bgcolor = this.bgcolor; - } - if (this.boxcolor) { - o.boxcolor = this.boxcolor; - } - if (this.shape) { - o.shape = this.shape; - } - - if (this.onSerialize) { - if (this.onSerialize(o)) { - console.warn( - "node onSerialize shouldnt return anything, data should be stored in the object pass in the first parameter" - ); - } - } - - return o; - }; - - /* Creates a clone of this node */ - LGraphNode.prototype.clone = function() { - var node = LiteGraph.createNode(this.type); - if (!node) { - return null; - } - - //we clone it because serialize returns shared containers - var data = LiteGraph.cloneObject(this.serialize()); - - //remove links - if (data.inputs) { - for (var i = 0; i < data.inputs.length; ++i) { - data.inputs[i].link = null; - } - } - - if (data.outputs) { - for (var i = 0; i < data.outputs.length; ++i) { - if (data.outputs[i].links) { - data.outputs[i].links.length = 0; - } - } - } - - delete data["id"]; - //remove links - node.configure(data); - - return node; - }; - - /** - * serialize and stringify - * @method toString - */ - - LGraphNode.prototype.toString = function() { - return JSON.stringify(this.serialize()); - }; - //LGraphNode.prototype.deserialize = function(info) {} //this cannot be done from within, must be done in LiteGraph - - /** - * get the title string - * @method getTitle - */ - - LGraphNode.prototype.getTitle = function() { - return this.title || this.constructor.title; - }; - - /** - * sets the value of a property - * @method setProperty - * @param {String} name - * @param {*} value - */ - LGraphNode.prototype.setProperty = function(name, value) { - if (!this.properties) { - this.properties = {}; - } - if( value === this.properties[name] ) - return; - var prev_value = this.properties[name]; - this.properties[name] = value; - if (this.onPropertyChanged) { - if( this.onPropertyChanged(name, value, prev_value) === false ) //abort change - this.properties[name] = prev_value; - } - if(this.widgets) //widgets could be linked to properties - for(var i = 0; i < this.widgets.length; ++i) - { - var w = this.widgets[i]; - if(!w) - continue; - if(w.options.property == name) - { - w.value = value; - break; - } - } - }; - - // Execution ************************* - /** - * sets the output data - * @method setOutputData - * @param {number} slot - * @param {*} data - */ - LGraphNode.prototype.setOutputData = function(slot, data) { - if (!this.outputs) { - return; - } - - //this maybe slow and a niche case - //if(slot && slot.constructor === String) - // slot = this.findOutputSlot(slot); - - if (slot == -1 || slot >= this.outputs.length) { - return; - } - - var output_info = this.outputs[slot]; - if (!output_info) { - return; - } - - //store data in the output itself in case we want to debug - output_info._data = data; - - //if there are connections, pass the data to the connections - if (this.outputs[slot].links) { - for (var i = 0; i < this.outputs[slot].links.length; i++) { - var link_id = this.outputs[slot].links[i]; - var link = this.graph.links[link_id]; - if(link) - link.data = data; - } - } - }; - - /** - * sets the output data type, useful when you want to be able to overwrite the data type - * @method setOutputDataType - * @param {number} slot - * @param {String} datatype - */ - LGraphNode.prototype.setOutputDataType = function(slot, type) { - if (!this.outputs) { - return; - } - if (slot == -1 || slot >= this.outputs.length) { - return; - } - var output_info = this.outputs[slot]; - if (!output_info) { - return; - } - //store data in the output itself in case we want to debug - output_info.type = type; - - //if there are connections, pass the data to the connections - if (this.outputs[slot].links) { - for (var i = 0; i < this.outputs[slot].links.length; i++) { - var link_id = this.outputs[slot].links[i]; - this.graph.links[link_id].type = type; - } - } - }; - - /** - * Retrieves the input data (data traveling through the connection) from one slot - * @method getInputData - * @param {number} slot - * @param {boolean} force_update if set to true it will force the connected node of this slot to output data into this link - * @return {*} data or if it is not connected returns undefined - */ - LGraphNode.prototype.getInputData = function(slot, force_update) { - if (!this.inputs) { - return; - } //undefined; - - if (slot >= this.inputs.length || this.inputs[slot].link == null) { - return; - } - - var link_id = this.inputs[slot].link; - var link = this.graph.links[link_id]; - if (!link) { - //bug: weird case but it happens sometimes - return null; - } - - if (!force_update) { - return link.data; - } - - //special case: used to extract data from the incoming connection before the graph has been executed - var node = this.graph.getNodeById(link.origin_id); - if (!node) { - return link.data; - } - - if (node.updateOutputData) { - node.updateOutputData(link.origin_slot); - } else if (node.onExecute) { - node.onExecute(); - } - - return link.data; - }; - - /** - * Retrieves the input data type (in case this supports multiple input types) - * @method getInputDataType - * @param {number} slot - * @return {String} datatype in string format - */ - LGraphNode.prototype.getInputDataType = function(slot) { - if (!this.inputs) { - return null; - } //undefined; - - if (slot >= this.inputs.length || this.inputs[slot].link == null) { - return null; - } - var link_id = this.inputs[slot].link; - var link = this.graph.links[link_id]; - if (!link) { - //bug: weird case but it happens sometimes - return null; - } - var node = this.graph.getNodeById(link.origin_id); - if (!node) { - return link.type; - } - var output_info = node.outputs[link.origin_slot]; - if (output_info) { - return output_info.type; - } - return null; - }; - - /** - * Retrieves the input data from one slot using its name instead of slot number - * @method getInputDataByName - * @param {String} slot_name - * @param {boolean} force_update if set to true it will force the connected node of this slot to output data into this link - * @return {*} data or if it is not connected returns null - */ - LGraphNode.prototype.getInputDataByName = function( - slot_name, - force_update - ) { - var slot = this.findInputSlot(slot_name); - if (slot == -1) { - return null; - } - return this.getInputData(slot, force_update); - }; - - /** - * tells you if there is a connection in one input slot - * @method isInputConnected - * @param {number} slot - * @return {boolean} - */ - LGraphNode.prototype.isInputConnected = function(slot) { - if (!this.inputs) { - return false; - } - return slot < this.inputs.length && this.inputs[slot].link != null; - }; - - /** - * tells you info about an input connection (which node, type, etc) - * @method getInputInfo - * @param {number} slot - * @return {Object} object or null { link: id, name: string, type: string or 0 } - */ - LGraphNode.prototype.getInputInfo = function(slot) { - if (!this.inputs) { - return null; - } - if (slot < this.inputs.length) { - return this.inputs[slot]; - } - return null; - }; - - /** - * returns the node connected in the input slot - * @method getInputNode - * @param {number} slot - * @return {LGraphNode} node or null - */ - LGraphNode.prototype.getInputNode = function(slot) { - if (!this.inputs) { - return null; - } - if (slot >= this.inputs.length) { - return null; - } - var input = this.inputs[slot]; - if (!input || input.link === null) { - return null; - } - var link_info = this.graph.links[input.link]; - if (!link_info) { - return null; - } - return this.graph.getNodeById(link_info.origin_id); - }; - - /** - * returns the value of an input with this name, otherwise checks if there is a property with that name - * @method getInputOrProperty - * @param {string} name - * @return {*} value - */ - LGraphNode.prototype.getInputOrProperty = function(name) { - if (!this.inputs || !this.inputs.length) { - return this.properties ? this.properties[name] : null; - } - - for (var i = 0, l = this.inputs.length; i < l; ++i) { - var input_info = this.inputs[i]; - if (name == input_info.name && input_info.link != null) { - var link = this.graph.links[input_info.link]; - if (link) { - return link.data; - } - } - } - return this.properties[name]; - }; - - /** - * tells you the last output data that went in that slot - * @method getOutputData - * @param {number} slot - * @return {Object} object or null - */ - LGraphNode.prototype.getOutputData = function(slot) { - if (!this.outputs) { - return null; - } - if (slot >= this.outputs.length) { - return null; - } - - var info = this.outputs[slot]; - return info._data; - }; - - /** - * tells you info about an output connection (which node, type, etc) - * @method getOutputInfo - * @param {number} slot - * @return {Object} object or null { name: string, type: string, links: [ ids of links in number ] } - */ - LGraphNode.prototype.getOutputInfo = function(slot) { - if (!this.outputs) { - return null; - } - if (slot < this.outputs.length) { - return this.outputs[slot]; - } - return null; - }; - - /** - * tells you if there is a connection in one output slot - * @method isOutputConnected - * @param {number} slot - * @return {boolean} - */ - LGraphNode.prototype.isOutputConnected = function(slot) { - if (!this.outputs) { - return false; - } - return ( - slot < this.outputs.length && - this.outputs[slot].links && - this.outputs[slot].links.length - ); - }; - - /** - * tells you if there is any connection in the output slots - * @method isAnyOutputConnected - * @return {boolean} - */ - LGraphNode.prototype.isAnyOutputConnected = function() { - if (!this.outputs) { - return false; - } - for (var i = 0; i < this.outputs.length; ++i) { - if (this.outputs[i].links && this.outputs[i].links.length) { - return true; - } - } - return false; - }; - - /** - * retrieves all the nodes connected to this output slot - * @method getOutputNodes - * @param {number} slot - * @return {array} - */ - LGraphNode.prototype.getOutputNodes = function(slot) { - if (!this.outputs || this.outputs.length == 0) { - return null; - } - - if (slot >= this.outputs.length) { - return null; - } - - var output = this.outputs[slot]; - if (!output.links || output.links.length == 0) { - return null; - } - - var r = []; - for (var i = 0; i < output.links.length; i++) { - var link_id = output.links[i]; - var link = this.graph.links[link_id]; - if (link) { - var target_node = this.graph.getNodeById(link.target_id); - if (target_node) { - r.push(target_node); - } - } - } - return r; - }; - - /** - * Triggers an event in this node, this will trigger any output with the same name - * @method trigger - * @param {String} event name ( "on_play", ... ) if action is equivalent to false then the event is send to all - * @param {*} param - */ - LGraphNode.prototype.trigger = function(action, param) { - if (!this.outputs || !this.outputs.length) { - return; - } - - if (this.graph) - this.graph._last_trigger_time = LiteGraph.getTime(); - - for (var i = 0; i < this.outputs.length; ++i) { - var output = this.outputs[i]; - if ( !output || output.type !== LiteGraph.EVENT || (action && output.name != action) ) - continue; - this.triggerSlot(i, param); - } - }; - - /** - * Triggers an slot event in this node - * @method triggerSlot - * @param {Number} slot the index of the output slot - * @param {*} param - * @param {Number} link_id [optional] in case you want to trigger and specific output link in a slot - */ - LGraphNode.prototype.triggerSlot = function(slot, param, link_id) { - if (!this.outputs) { - return; - } - - var output = this.outputs[slot]; - if (!output) { - return; - } - - var links = output.links; - if (!links || !links.length) { - return; - } - - if (this.graph) { - this.graph._last_trigger_time = LiteGraph.getTime(); - } - - //for every link attached here - for (var k = 0; k < links.length; ++k) { - var id = links[k]; - if (link_id != null && link_id != id) { - //to skip links - continue; - } - var link_info = this.graph.links[links[k]]; - if (!link_info) { - //not connected - continue; - } - link_info._last_time = LiteGraph.getTime(); - var node = this.graph.getNodeById(link_info.target_id); - if (!node) { - //node not found? - continue; - } - - //used to mark events in graph - var target_connection = node.inputs[link_info.target_slot]; - - if (node.onAction) { - node.onAction(target_connection.name, param); - } else if (node.mode === LiteGraph.ON_TRIGGER) { - if (node.onExecute) { - node.onExecute(param); - } - } - } - }; - - /** - * clears the trigger slot animation - * @method clearTriggeredSlot - * @param {Number} slot the index of the output slot - * @param {Number} link_id [optional] in case you want to trigger and specific output link in a slot - */ - LGraphNode.prototype.clearTriggeredSlot = function(slot, link_id) { - if (!this.outputs) { - return; - } - - var output = this.outputs[slot]; - if (!output) { - return; - } - - var links = output.links; - if (!links || !links.length) { - return; - } - - //for every link attached here - for (var k = 0; k < links.length; ++k) { - var id = links[k]; - if (link_id != null && link_id != id) { - //to skip links - continue; - } - var link_info = this.graph.links[links[k]]; - if (!link_info) { - //not connected - continue; - } - link_info._last_time = 0; - } - }; - - /** - * add a new property to this node - * @method addProperty - * @param {string} name - * @param {*} default_value - * @param {string} type string defining the output type ("vec3","number",...) - * @param {Object} extra_info this can be used to have special properties of the property (like values, etc) - */ - LGraphNode.prototype.addProperty = function( - name, - default_value, - type, - extra_info - ) { - var o = { name: name, type: type, default_value: default_value }; - if (extra_info) { - for (var i in extra_info) { - o[i] = extra_info[i]; - } - } - if (!this.properties_info) { - this.properties_info = []; - } - this.properties_info.push(o); - if (!this.properties) { - this.properties = {}; - } - this.properties[name] = default_value; - return o; - }; - - //connections - - /** - * add a new output slot to use in this node - * @method addOutput - * @param {string} name - * @param {string} type string defining the output type ("vec3","number",...) - * @param {Object} extra_info this can be used to have special properties of an output (label, special color, position, etc) - */ - LGraphNode.prototype.addOutput = function(name, type, extra_info) { - var o = { name: name, type: type, links: null }; - if (extra_info) { - for (var i in extra_info) { - o[i] = extra_info[i]; - } - } - - if (!this.outputs) { - this.outputs = []; - } - this.outputs.push(o); - if (this.onOutputAdded) { - this.onOutputAdded(o); - } - this.size = this.computeSize(); - this.setDirtyCanvas(true, true); - return o; - }; - - /** - * add a new output slot to use in this node - * @method addOutputs - * @param {Array} array of triplets like [[name,type,extra_info],[...]] - */ - LGraphNode.prototype.addOutputs = function(array) { - for (var i = 0; i < array.length; ++i) { - var info = array[i]; - var o = { name: info[0], type: info[1], link: null }; - if (array[2]) { - for (var j in info[2]) { - o[j] = info[2][j]; - } - } - - if (!this.outputs) { - this.outputs = []; - } - this.outputs.push(o); - if (this.onOutputAdded) { - this.onOutputAdded(o); - } - } - - this.size = this.computeSize(); - this.setDirtyCanvas(true, true); - }; - - /** - * remove an existing output slot - * @method removeOutput - * @param {number} slot - */ - LGraphNode.prototype.removeOutput = function(slot) { - this.disconnectOutput(slot); - this.outputs.splice(slot, 1); - for (var i = slot; i < this.outputs.length; ++i) { - if (!this.outputs[i] || !this.outputs[i].links) { - continue; - } - var links = this.outputs[i].links; - for (var j = 0; j < links.length; ++j) { - var link = this.graph.links[links[j]]; - if (!link) { - continue; - } - link.origin_slot -= 1; - } - } - - this.size = this.computeSize(); - if (this.onOutputRemoved) { - this.onOutputRemoved(slot); - } - this.setDirtyCanvas(true, true); - }; - - /** - * add a new input slot to use in this node - * @method addInput - * @param {string} name - * @param {string} type string defining the input type ("vec3","number",...), it its a generic one use 0 - * @param {Object} extra_info this can be used to have special properties of an input (label, color, position, etc) - */ - LGraphNode.prototype.addInput = function(name, type, extra_info) { - type = type || 0; - var o = { name: name, type: type, link: null }; - if (extra_info) { - for (var i in extra_info) { - o[i] = extra_info[i]; - } - } - - if (!this.inputs) { - this.inputs = []; - } - this.inputs.push(o); - this.size = this.computeSize(); - if (this.onInputAdded) { - this.onInputAdded(o); - } - this.setDirtyCanvas(true, true); - return o; - }; - - /** - * add several new input slots in this node - * @method addInputs - * @param {Array} array of triplets like [[name,type,extra_info],[...]] - */ - LGraphNode.prototype.addInputs = function(array) { - for (var i = 0; i < array.length; ++i) { - var info = array[i]; - var o = { name: info[0], type: info[1], link: null }; - if (array[2]) { - for (var j in info[2]) { - o[j] = info[2][j]; - } - } - - if (!this.inputs) { - this.inputs = []; - } - this.inputs.push(o); - if (this.onInputAdded) { - this.onInputAdded(o); - } - } - - this.size = this.computeSize(); - this.setDirtyCanvas(true, true); - }; - - /** - * remove an existing input slot - * @method removeInput - * @param {number} slot - */ - LGraphNode.prototype.removeInput = function(slot) { - this.disconnectInput(slot); - this.inputs.splice(slot, 1); - for (var i = slot; i < this.inputs.length; ++i) { - if (!this.inputs[i]) { - continue; - } - var link = this.graph.links[this.inputs[i].link]; - if (!link) { - continue; - } - link.target_slot -= 1; - } - this.size = this.computeSize(); - if (this.onInputRemoved) { - this.onInputRemoved(slot); - } - this.setDirtyCanvas(true, true); - }; - - /** - * add an special connection to this node (used for special kinds of graphs) - * @method addConnection - * @param {string} name - * @param {string} type string defining the input type ("vec3","number",...) - * @param {[x,y]} pos position of the connection inside the node - * @param {string} direction if is input or output - */ - LGraphNode.prototype.addConnection = function(name, type, pos, direction) { - var o = { - name: name, - type: type, - pos: pos, - direction: direction, - links: null - }; - this.connections.push(o); - return o; - }; - - /** - * computes the size of a node according to its inputs and output slots - * @method computeSize - * @param {number} minHeight - * @return {number} the total size - */ - LGraphNode.prototype.computeSize = function(minHeight, out) { - if (this.constructor.size) { - return this.constructor.size.concat(); - } - - var rows = Math.max( - this.inputs ? this.inputs.length : 1, - this.outputs ? this.outputs.length : 1 - ); - var size = out || new Float32Array([0, 0]); - rows = Math.max(rows, 1); - var font_size = LiteGraph.NODE_TEXT_SIZE; //although it should be graphcanvas.inner_text_font size - size[1] = (this.constructor.slot_start_y || 0) + rows * LiteGraph.NODE_SLOT_HEIGHT; - - var widgets_height = 0; - if (this.widgets && this.widgets.length) { - widgets_height = this.widgets.length * (LiteGraph.NODE_WIDGET_HEIGHT + 4) + 8; - } - - //compute height using widgets height - if( this.widgets_up ) - size[1] = Math.max( size[1], widgets_height ); - else if( this.widgets_start_y != null ) - size[1] = Math.max( size[1], widgets_height + this.widgets_start_y ); - else - size[1] += widgets_height; - - var font_size = font_size; - var title_width = compute_text_size(this.title); - var input_width = 0; - var output_width = 0; - - if (this.inputs) { - for (var i = 0, l = this.inputs.length; i < l; ++i) { - var input = this.inputs[i]; - var text = input.label || input.name || ""; - var text_width = compute_text_size(text); - if (input_width < text_width) { - input_width = text_width; - } - } - } - - if (this.outputs) { - for (var i = 0, l = this.outputs.length; i < l; ++i) { - var output = this.outputs[i]; - var text = output.label || output.name || ""; - var text_width = compute_text_size(text); - if (output_width < text_width) { - output_width = text_width; - } - } - } - - size[0] = Math.max(input_width + output_width + 10, title_width); - size[0] = Math.max(size[0], LiteGraph.NODE_WIDTH); - if (this.widgets && this.widgets.length) { - size[0] = Math.max(size[0], LiteGraph.NODE_WIDTH * 1.5); - } - - if (this.onResize) { - this.onResize(size); - } - - function compute_text_size(text) { - if (!text) { - return 0; - } - return font_size * text.length * 0.6; - } - - if ( - this.constructor.min_height && - size[1] < this.constructor.min_height - ) { - size[1] = this.constructor.min_height; - } - - size[1] += 6; //margin - - return size; - }; - - /** - * returns all the info available about a property of this node. - * - * @method getPropertyInfo - * @param {String} property name of the property - * @return {Object} the object with all the available info - */ - LGraphNode.prototype.getPropertyInfo = function( property ) - { - var info = null; - - //there are several ways to define info about a property - //legacy mode - if (this.properties_info) { - for (var i = 0; i < this.properties_info.length; ++i) { - if (this.properties_info[i].name == property) { - info = this.properties_info[i]; - break; - } - } - } - //litescene mode using the constructor - if(this.constructor["@" + property]) - info = this.constructor["@" + property]; - - //litescene mode using the constructor - if (this.onGetPropertyInfo) { - info = this.onGetPropertyInfo(property); - } - - if (!info) - info = {}; - if(!info.type) - info.type = typeof this.properties[property]; - - return info; - } - - /** - * Defines a widget inside the node, it will be rendered on top of the node, you can control lots of properties - * - * @method addWidget - * @param {String} type the widget type (could be "number","string","combo" - * @param {String} name the text to show on the widget - * @param {String} value the default value - * @param {Function|String} callback function to call when it changes (optionally, it can be the name of the property to modify) - * @param {Object} options the object that contains special properties of this widget - * @return {Object} the created widget object - */ - LGraphNode.prototype.addWidget = function( type, name, value, callback, options ) - { - if (!this.widgets) { - this.widgets = []; - } - - if(!options && callback && callback.constructor === Object) - { - options = callback; - callback = null; - } - - if(options && options.constructor === String) //options can be the property name - options = { property: options }; - - if(callback && callback.constructor === String) //callback can be the property name - { - if(!options) - options = {}; - options.property = callback; - callback = null; - } - - if(callback && callback.constructor !== Function) - { - console.warn("addWidget: callback must be a function"); - callback = null; - } - - var w = { - type: type.toLowerCase(), - name: name, - value: value, - callback: callback, - options: options || {} - }; - - if (w.options.y !== undefined) { - w.y = w.options.y; - } - - if (!callback && !w.options.callback && !w.options.property) { - console.warn("LiteGraph addWidget(...) without a callback or property assigned"); - } - if (type == "combo" && !w.options.values) { - throw "LiteGraph addWidget('combo',...) requires to pass values in options: { values:['red','blue'] }"; - } - this.widgets.push(w); - this.size = this.computeSize(); - return w; - }; - - LGraphNode.prototype.addCustomWidget = function(custom_widget) { - if (!this.widgets) { - this.widgets = []; - } - this.widgets.push(custom_widget); - return custom_widget; - }; - - /** - * returns the bounding of the object, used for rendering purposes - * bounding is: [topleft_cornerx, topleft_cornery, width, height] - * @method getBounding - * @return {Float32Array[4]} the total size - */ - LGraphNode.prototype.getBounding = function(out) { - out = out || new Float32Array(4); - out[0] = this.pos[0] - 4; - out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT; - out[2] = this.size[0] + 4; - out[3] = this.size[1] + LiteGraph.NODE_TITLE_HEIGHT; - - if (this.onBounding) { - this.onBounding(out); - } - return out; - }; - - /** - * checks if a point is inside the shape of a node - * @method isPointInside - * @param {number} x - * @param {number} y - * @return {boolean} - */ - LGraphNode.prototype.isPointInside = function(x, y, margin, skip_title) { - margin = margin || 0; - - var margin_top = this.graph && this.graph.isLive() ? 0 : LiteGraph.NODE_TITLE_HEIGHT; - if (skip_title) { - margin_top = 0; - } - if (this.flags && this.flags.collapsed) { - //if ( distance([x,y], [this.pos[0] + this.size[0]*0.5, this.pos[1] + this.size[1]*0.5]) < LiteGraph.NODE_COLLAPSED_RADIUS) - if ( - isInsideRectangle( - x, - y, - this.pos[0] - margin, - this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT - margin, - (this._collapsed_width || LiteGraph.NODE_COLLAPSED_WIDTH) + - 2 * margin, - LiteGraph.NODE_TITLE_HEIGHT + 2 * margin - ) - ) { - return true; - } - } else if ( - this.pos[0] - 4 - margin < x && - this.pos[0] + this.size[0] + 4 + margin > x && - this.pos[1] - margin_top - margin < y && - this.pos[1] + this.size[1] + margin > y - ) { - return true; - } - return false; - }; - - /** - * checks if a point is inside a node slot, and returns info about which slot - * @method getSlotInPosition - * @param {number} x - * @param {number} y - * @return {Object} if found the object contains { input|output: slot object, slot: number, link_pos: [x,y] } - */ - LGraphNode.prototype.getSlotInPosition = function(x, y) { - //search for inputs - var link_pos = new Float32Array(2); - if (this.inputs) { - for (var i = 0, l = this.inputs.length; i < l; ++i) { - var input = this.inputs[i]; - this.getConnectionPos(true, i, link_pos); - if ( - isInsideRectangle( - x, - y, - link_pos[0] - 10, - link_pos[1] - 5, - 20, - 10 - ) - ) { - return { input: input, slot: i, link_pos: link_pos }; - } - } - } - - if (this.outputs) { - for (var i = 0, l = this.outputs.length; i < l; ++i) { - var output = this.outputs[i]; - this.getConnectionPos(false, i, link_pos); - if ( - isInsideRectangle( - x, - y, - link_pos[0] - 10, - link_pos[1] - 5, - 20, - 10 - ) - ) { - return { output: output, slot: i, link_pos: link_pos }; - } - } - } - - return null; - }; - - /** - * returns the input slot with a given name (used for dynamic slots), -1 if not found - * @method findInputSlot - * @param {string} name the name of the slot - * @return {number} the slot (-1 if not found) - */ - LGraphNode.prototype.findInputSlot = function(name) { - if (!this.inputs) { - return -1; - } - for (var i = 0, l = this.inputs.length; i < l; ++i) { - if (name == this.inputs[i].name) { - return i; - } - } - return -1; - }; - - /** - * returns the output slot with a given name (used for dynamic slots), -1 if not found - * @method findOutputSlot - * @param {string} name the name of the slot - * @return {number} the slot (-1 if not found) - */ - LGraphNode.prototype.findOutputSlot = function(name) { - if (!this.outputs) { - return -1; - } - for (var i = 0, l = this.outputs.length; i < l; ++i) { - if (name == this.outputs[i].name) { - return i; - } - } - return -1; - }; - - /** - * connect this node output to the input of another node - * @method connect - * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) - * @param {LGraphNode} node the target node - * @param {number_or_string} target_slot the input slot of the target node (could be the number of the slot or the string with the name of the slot, or -1 to connect a trigger) - * @return {Object} the link_info is created, otherwise null - */ - LGraphNode.prototype.connect = function(slot, target_node, target_slot) { - target_slot = target_slot || 0; - - if (!this.graph) { - //could be connected before adding it to a graph - console.log( - "Connect: Error, node doesn't belong to any graph. Nodes must be added first to a graph before connecting them." - ); //due to link ids being associated with graphs - return null; - } - - //seek for the output slot - if (slot.constructor === String) { - slot = this.findOutputSlot(slot); - if (slot == -1) { - if (LiteGraph.debug) { - console.log("Connect: Error, no slot of name " + slot); - } - return null; - } - } else if (!this.outputs || slot >= this.outputs.length) { - if (LiteGraph.debug) { - console.log("Connect: Error, slot number not found"); - } - return null; - } - - if (target_node && target_node.constructor === Number) { - target_node = this.graph.getNodeById(target_node); - } - if (!target_node) { - throw "target node is null"; - } - - //avoid loopback - if (target_node == this) { - return null; - } - - //you can specify the slot by name - if (target_slot.constructor === String) { - target_slot = target_node.findInputSlot(target_slot); - if (target_slot == -1) { - if (LiteGraph.debug) { - console.log( - "Connect: Error, no slot of name " + target_slot - ); - } - return null; - } - } else if (target_slot === LiteGraph.EVENT) { - //search for first slot with event? - /* - //create input for trigger - var input = target_node.addInput("onTrigger", LiteGraph.EVENT ); - target_slot = target_node.inputs.length - 1; //last one is the one created - target_node.mode = LiteGraph.ON_TRIGGER; - */ - return null; - } else if ( - !target_node.inputs || - target_slot >= target_node.inputs.length - ) { - if (LiteGraph.debug) { - console.log("Connect: Error, slot number not found"); - } - return null; - } - - //if there is something already plugged there, disconnect - if (target_node.inputs[target_slot].link != null) { - target_node.disconnectInput(target_slot); - } - - //why here?? - //this.setDirtyCanvas(false,true); - //this.graph.connectionChange( this ); - - var output = this.outputs[slot]; - - //allows nodes to block connection - if (target_node.onConnectInput) { - if ( - target_node.onConnectInput(target_slot, output.type, output) === - false - ) { - return null; - } - } - - var input = target_node.inputs[target_slot]; - var link_info = null; - - if (LiteGraph.isValidConnection(output.type, input.type)) { - link_info = new LLink( - ++this.graph.last_link_id, - input.type, - this.id, - slot, - target_node.id, - target_slot - ); - - //add to graph links list - this.graph.links[link_info.id] = link_info; - - //connect in output - if (output.links == null) { - output.links = []; - } - output.links.push(link_info.id); - //connect in input - target_node.inputs[target_slot].link = link_info.id; - if (this.graph) { - this.graph._version++; - } - if (this.onConnectionsChange) { - this.onConnectionsChange( - LiteGraph.OUTPUT, - slot, - true, - link_info, - output - ); - } //link_info has been created now, so its updated - if (target_node.onConnectionsChange) { - target_node.onConnectionsChange( - LiteGraph.INPUT, - target_slot, - true, - link_info, - input - ); - } - if (this.graph && this.graph.onNodeConnectionChange) { - this.graph.onNodeConnectionChange( - LiteGraph.INPUT, - target_node, - target_slot, - this, - slot - ); - this.graph.onNodeConnectionChange( - LiteGraph.OUTPUT, - this, - slot, - target_node, - target_slot - ); - } - } - - this.setDirtyCanvas(false, true); - this.graph.connectionChange(this, link_info); - - return link_info; - }; - - /** - * disconnect one output to an specific node - * @method disconnectOutput - * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) - * @param {LGraphNode} target_node the target node to which this slot is connected [Optional, if not target_node is specified all nodes will be disconnected] - * @return {boolean} if it was disconnected successfully - */ - LGraphNode.prototype.disconnectOutput = function(slot, target_node) { - if (slot.constructor === String) { - slot = this.findOutputSlot(slot); - if (slot == -1) { - if (LiteGraph.debug) { - console.log("Connect: Error, no slot of name " + slot); - } - return false; - } - } else if (!this.outputs || slot >= this.outputs.length) { - if (LiteGraph.debug) { - console.log("Connect: Error, slot number not found"); - } - return false; - } - - //get output slot - var output = this.outputs[slot]; - if (!output || !output.links || output.links.length == 0) { - return false; - } - - //one of the output links in this slot - if (target_node) { - if (target_node.constructor === Number) { - target_node = this.graph.getNodeById(target_node); - } - if (!target_node) { - throw "Target Node not found"; - } - - for (var i = 0, l = output.links.length; i < l; i++) { - var link_id = output.links[i]; - var link_info = this.graph.links[link_id]; - - //is the link we are searching for... - if (link_info.target_id == target_node.id) { - output.links.splice(i, 1); //remove here - var input = target_node.inputs[link_info.target_slot]; - input.link = null; //remove there - delete this.graph.links[link_id]; //remove the link from the links pool - if (this.graph) { - this.graph._version++; - } - if (target_node.onConnectionsChange) { - target_node.onConnectionsChange( - LiteGraph.INPUT, - link_info.target_slot, - false, - link_info, - input - ); - } //link_info hasn't been modified so its ok - if (this.onConnectionsChange) { - this.onConnectionsChange( - LiteGraph.OUTPUT, - slot, - false, - link_info, - output - ); - } - if (this.graph && this.graph.onNodeConnectionChange) { - this.graph.onNodeConnectionChange( - LiteGraph.OUTPUT, - this, - slot - ); - } - if (this.graph && this.graph.onNodeConnectionChange) { - this.graph.onNodeConnectionChange( - LiteGraph.OUTPUT, - this, - slot - ); - this.graph.onNodeConnectionChange( - LiteGraph.INPUT, - target_node, - link_info.target_slot - ); - } - break; - } - } - } //all the links in this output slot - else { - for (var i = 0, l = output.links.length; i < l; i++) { - var link_id = output.links[i]; - var link_info = this.graph.links[link_id]; - if (!link_info) { - //bug: it happens sometimes - continue; - } - - var target_node = this.graph.getNodeById(link_info.target_id); - var input = null; - if (this.graph) { - this.graph._version++; - } - if (target_node) { - input = target_node.inputs[link_info.target_slot]; - input.link = null; //remove other side link - if (target_node.onConnectionsChange) { - target_node.onConnectionsChange( - LiteGraph.INPUT, - link_info.target_slot, - false, - link_info, - input - ); - } //link_info hasn't been modified so its ok - if (this.graph && this.graph.onNodeConnectionChange) { - this.graph.onNodeConnectionChange( - LiteGraph.INPUT, - target_node, - link_info.target_slot - ); - } - } - delete this.graph.links[link_id]; //remove the link from the links pool - if (this.onConnectionsChange) { - this.onConnectionsChange( - LiteGraph.OUTPUT, - slot, - false, - link_info, - output - ); - } - if (this.graph && this.graph.onNodeConnectionChange) { - this.graph.onNodeConnectionChange( - LiteGraph.OUTPUT, - this, - slot - ); - this.graph.onNodeConnectionChange( - LiteGraph.INPUT, - target_node, - link_info.target_slot - ); - } - } - output.links = null; - } - - this.setDirtyCanvas(false, true); - this.graph.connectionChange(this); - return true; - }; - - /** - * disconnect one input - * @method disconnectInput - * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) - * @return {boolean} if it was disconnected successfully - */ - LGraphNode.prototype.disconnectInput = function(slot) { - //seek for the output slot - if (slot.constructor === String) { - slot = this.findInputSlot(slot); - if (slot == -1) { - if (LiteGraph.debug) { - console.log("Connect: Error, no slot of name " + slot); - } - return false; - } - } else if (!this.inputs || slot >= this.inputs.length) { - if (LiteGraph.debug) { - console.log("Connect: Error, slot number not found"); - } - return false; - } - - var input = this.inputs[slot]; - if (!input) { - return false; - } - - var link_id = this.inputs[slot].link; - this.inputs[slot].link = null; - - //remove other side - var link_info = this.graph.links[link_id]; - if (link_info) { - var target_node = this.graph.getNodeById(link_info.origin_id); - if (!target_node) { - return false; - } - - var output = target_node.outputs[link_info.origin_slot]; - if (!output || !output.links || output.links.length == 0) { - return false; - } - - //search in the inputs list for this link - for (var i = 0, l = output.links.length; i < l; i++) { - if (output.links[i] == link_id) { - output.links.splice(i, 1); - break; - } - } - - delete this.graph.links[link_id]; //remove from the pool - if (this.graph) { - this.graph._version++; - } - if (this.onConnectionsChange) { - this.onConnectionsChange( - LiteGraph.INPUT, - slot, - false, - link_info, - input - ); - } - if (target_node.onConnectionsChange) { - target_node.onConnectionsChange( - LiteGraph.OUTPUT, - i, - false, - link_info, - output - ); - } - if (this.graph && this.graph.onNodeConnectionChange) { - this.graph.onNodeConnectionChange( - LiteGraph.OUTPUT, - target_node, - i - ); - this.graph.onNodeConnectionChange(LiteGraph.INPUT, this, slot); - } - } - - this.setDirtyCanvas(false, true); - this.graph.connectionChange(this); - return true; - }; - - /** - * returns the center of a connection point in canvas coords - * @method getConnectionPos - * @param {boolean} is_input true if if a input slot, false if it is an output - * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) - * @param {vec2} out [optional] a place to store the output, to free garbage - * @return {[x,y]} the position - **/ - LGraphNode.prototype.getConnectionPos = function( - is_input, - slot_number, - out - ) { - out = out || new Float32Array(2); - var num_slots = 0; - if (is_input && this.inputs) { - num_slots = this.inputs.length; - } - if (!is_input && this.outputs) { - num_slots = this.outputs.length; - } - - var offset = LiteGraph.NODE_SLOT_HEIGHT * 0.5; - - if (this.flags.collapsed) { - var w = this._collapsed_width || LiteGraph.NODE_COLLAPSED_WIDTH; - if (this.horizontal) { - out[0] = this.pos[0] + w * 0.5; - if (is_input) { - out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT; - } else { - out[1] = this.pos[1]; - } - } else { - if (is_input) { - out[0] = this.pos[0]; - } else { - out[0] = this.pos[0] + w; - } - out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT * 0.5; - } - return out; - } - - //weird feature that never got finished - if (is_input && slot_number == -1) { - out[0] = this.pos[0] + LiteGraph.NODE_TITLE_HEIGHT * 0.5; - out[1] = this.pos[1] + LiteGraph.NODE_TITLE_HEIGHT * 0.5; - return out; - } - - //hard-coded pos - if ( - is_input && - num_slots > slot_number && - this.inputs[slot_number].pos - ) { - out[0] = this.pos[0] + this.inputs[slot_number].pos[0]; - out[1] = this.pos[1] + this.inputs[slot_number].pos[1]; - return out; - } else if ( - !is_input && - num_slots > slot_number && - this.outputs[slot_number].pos - ) { - out[0] = this.pos[0] + this.outputs[slot_number].pos[0]; - out[1] = this.pos[1] + this.outputs[slot_number].pos[1]; - return out; - } - - //horizontal distributed slots - if (this.horizontal) { - out[0] = - this.pos[0] + (slot_number + 0.5) * (this.size[0] / num_slots); - if (is_input) { - out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT; - } else { - out[1] = this.pos[1] + this.size[1]; - } - return out; - } - - //default vertical slots - if (is_input) { - out[0] = this.pos[0] + offset; - } else { - out[0] = this.pos[0] + this.size[0] + 1 - offset; - } - out[1] = - this.pos[1] + - (slot_number + 0.7) * LiteGraph.NODE_SLOT_HEIGHT + - (this.constructor.slot_start_y || 0); - return out; - }; - - /* Force align to grid */ - LGraphNode.prototype.alignToGrid = function() { - this.pos[0] = - LiteGraph.CANVAS_GRID_SIZE * - Math.round(this.pos[0] / LiteGraph.CANVAS_GRID_SIZE); - this.pos[1] = - LiteGraph.CANVAS_GRID_SIZE * - Math.round(this.pos[1] / LiteGraph.CANVAS_GRID_SIZE); - }; - - /* Console output */ - LGraphNode.prototype.trace = function(msg) { - if (!this.console) { - this.console = []; - } - this.console.push(msg); - if (this.console.length > LGraphNode.MAX_CONSOLE) { - this.console.shift(); - } - - this.graph.onNodeTrace(this, msg); - }; - - /* Forces to redraw or the main canvas (LGraphNode) or the bg canvas (links) */ - LGraphNode.prototype.setDirtyCanvas = function( - dirty_foreground, - dirty_background - ) { - if (!this.graph) { - return; - } - this.graph.sendActionToCanvas("setDirty", [ - dirty_foreground, - dirty_background - ]); - }; - - LGraphNode.prototype.loadImage = function(url) { - var img = new Image(); - img.src = LiteGraph.node_images_path + url; - img.ready = false; - - var that = this; - img.onload = function() { - this.ready = true; - that.setDirtyCanvas(true); - }; - return img; - }; - - //safe LGraphNode action execution (not sure if safe) - /* -LGraphNode.prototype.executeAction = function(action) -{ - if(action == "") return false; - - if( action.indexOf(";") != -1 || action.indexOf("}") != -1) - { - this.trace("Error: Action contains unsafe characters"); - return false; - } - - var tokens = action.split("("); - var func_name = tokens[0]; - if( typeof(this[func_name]) != "function") - { - this.trace("Error: Action not found on node: " + func_name); - return false; - } - - var code = action; - - try - { - var _foo = eval; - eval = null; - (new Function("with(this) { " + code + "}")).call(this); - eval = _foo; - } - catch (err) - { - this.trace("Error executing action {" + action + "} :" + err); - return false; - } - - return true; -} -*/ - - /* Allows to get onMouseMove and onMouseUp events even if the mouse is out of focus */ - LGraphNode.prototype.captureInput = function(v) { - if (!this.graph || !this.graph.list_of_graphcanvas) { - return; - } - - var list = this.graph.list_of_graphcanvas; - - for (var i = 0; i < list.length; ++i) { - var c = list[i]; - //releasing somebody elses capture?! - if (!v && c.node_capturing_input != this) { - continue; - } - - //change - c.node_capturing_input = v ? this : null; - } - }; - - /** - * Collapse the node to make it smaller on the canvas - * @method collapse - **/ - LGraphNode.prototype.collapse = function(force) { - this.graph._version++; - if (this.constructor.collapsable === false && !force) { - return; - } - if (!this.flags.collapsed) { - this.flags.collapsed = true; - } else { - this.flags.collapsed = false; - } - this.setDirtyCanvas(true, true); - }; - - /** - * Forces the node to do not move or realign on Z - * @method pin - **/ - - LGraphNode.prototype.pin = function(v) { - this.graph._version++; - if (v === undefined) { - this.flags.pinned = !this.flags.pinned; - } else { - this.flags.pinned = v; - } - }; - - LGraphNode.prototype.localToScreen = function(x, y, graphcanvas) { - return [ - (x + this.pos[0]) * graphcanvas.scale + graphcanvas.offset[0], - (y + this.pos[1]) * graphcanvas.scale + graphcanvas.offset[1] - ]; - }; - - function LGraphGroup(title) { - this._ctor(title); - } - - global.LGraphGroup = LiteGraph.LGraphGroup = LGraphGroup; - - LGraphGroup.prototype._ctor = function(title) { - this.title = title || "Group"; - this.font_size = 24; - this.color = LGraphCanvas.node_colors.pale_blue - ? LGraphCanvas.node_colors.pale_blue.groupcolor - : "#AAA"; - this._bounding = new Float32Array([10, 10, 140, 80]); - this._pos = this._bounding.subarray(0, 2); - this._size = this._bounding.subarray(2, 4); - this._nodes = []; - this.graph = null; - - Object.defineProperty(this, "pos", { - set: function(v) { - if (!v || v.length < 2) { - return; - } - this._pos[0] = v[0]; - this._pos[1] = v[1]; - }, - get: function() { - return this._pos; - }, - enumerable: true - }); - - Object.defineProperty(this, "size", { - set: function(v) { - if (!v || v.length < 2) { - return; - } - this._size[0] = Math.max(140, v[0]); - this._size[1] = Math.max(80, v[1]); - }, - get: function() { - return this._size; - }, - enumerable: true - }); - }; - - LGraphGroup.prototype.configure = function(o) { - this.title = o.title; - this._bounding.set(o.bounding); - this.color = o.color; - this.font = o.font; - }; - - LGraphGroup.prototype.serialize = function() { - var b = this._bounding; - return { - title: this.title, - bounding: [ - Math.round(b[0]), - Math.round(b[1]), - Math.round(b[2]), - Math.round(b[3]) - ], - color: this.color, - font: this.font - }; - }; - - LGraphGroup.prototype.move = function(deltax, deltay, ignore_nodes) { - this._pos[0] += deltax; - this._pos[1] += deltay; - if (ignore_nodes) { - return; - } - for (var i = 0; i < this._nodes.length; ++i) { - var node = this._nodes[i]; - node.pos[0] += deltax; - node.pos[1] += deltay; - } - }; - - LGraphGroup.prototype.recomputeInsideNodes = function() { - this._nodes.length = 0; - var nodes = this.graph._nodes; - var node_bounding = new Float32Array(4); - - for (var i = 0; i < nodes.length; ++i) { - var node = nodes[i]; - node.getBounding(node_bounding); - if (!overlapBounding(this._bounding, node_bounding)) { - continue; - } //out of the visible area - this._nodes.push(node); - } - }; - - LGraphGroup.prototype.isPointInside = LGraphNode.prototype.isPointInside; - LGraphGroup.prototype.setDirtyCanvas = LGraphNode.prototype.setDirtyCanvas; - - //**************************************** - - //Scale and Offset - function DragAndScale(element, skip_events) { - this.offset = new Float32Array([0, 0]); - this.scale = 1; - this.max_scale = 10; - this.min_scale = 0.1; - this.onredraw = null; - this.enabled = true; - this.last_mouse = [0, 0]; - this.element = null; - this.visible_area = new Float32Array(4); - - if (element) { - this.element = element; - if (!skip_events) { - this.bindEvents(element); - } - } - } - - LiteGraph.DragAndScale = DragAndScale; - - DragAndScale.prototype.bindEvents = function(element) { - this.last_mouse = new Float32Array(2); - - this._binded_mouse_callback = this.onMouse.bind(this); - - element.addEventListener("mousedown", this._binded_mouse_callback); - element.addEventListener("mousemove", this._binded_mouse_callback); - - element.addEventListener( - "mousewheel", - this._binded_mouse_callback, - false - ); - element.addEventListener("wheel", this._binded_mouse_callback, false); - }; - - DragAndScale.prototype.computeVisibleArea = function() { - if (!this.element) { - this.visible_area[0] = this.visible_area[1] = this.visible_area[2] = this.visible_area[3] = 0; - return; - } - var width = this.element.width; - var height = this.element.height; - var startx = -this.offset[0]; - var starty = -this.offset[1]; - var endx = startx + width / this.scale; - var endy = starty + height / this.scale; - this.visible_area[0] = startx; - this.visible_area[1] = starty; - this.visible_area[2] = endx - startx; - this.visible_area[3] = endy - starty; - }; - - DragAndScale.prototype.onMouse = function(e) { - if (!this.enabled) { - return; - } - - var canvas = this.element; - var rect = canvas.getBoundingClientRect(); - var x = e.clientX - rect.left; - var y = e.clientY - rect.top; - e.canvasx = x; - e.canvasy = y; - e.dragging = this.dragging; - - var ignore = false; - if (this.onmouse) { - ignore = this.onmouse(e); - } - - if (e.type == "mousedown") { - this.dragging = true; - canvas.removeEventListener( - "mousemove", - this._binded_mouse_callback - ); - document.body.addEventListener( - "mousemove", - this._binded_mouse_callback - ); - document.body.addEventListener( - "mouseup", - this._binded_mouse_callback - ); - } else if (e.type == "mousemove") { - if (!ignore) { - var deltax = x - this.last_mouse[0]; - var deltay = y - this.last_mouse[1]; - if (this.dragging) { - this.mouseDrag(deltax, deltay); - } - } - } else if (e.type == "mouseup") { - this.dragging = false; - document.body.removeEventListener( - "mousemove", - this._binded_mouse_callback - ); - document.body.removeEventListener( - "mouseup", - this._binded_mouse_callback - ); - canvas.addEventListener("mousemove", this._binded_mouse_callback); - } else if ( - e.type == "mousewheel" || - e.type == "wheel" || - e.type == "DOMMouseScroll" - ) { - e.eventType = "mousewheel"; - if (e.type == "wheel") { - e.wheel = -e.deltaY; - } else { - e.wheel = - e.wheelDeltaY != null ? e.wheelDeltaY : e.detail * -60; - } - - //from stack overflow - e.delta = e.wheelDelta - ? e.wheelDelta / 40 - : e.deltaY - ? -e.deltaY / 3 - : 0; - this.changeDeltaScale(1.0 + e.delta * 0.05); - } - - this.last_mouse[0] = x; - this.last_mouse[1] = y; - - e.preventDefault(); - e.stopPropagation(); - return false; - }; - - DragAndScale.prototype.toCanvasContext = function(ctx) { - ctx.scale(this.scale, this.scale); - ctx.translate(this.offset[0], this.offset[1]); - }; - - DragAndScale.prototype.convertOffsetToCanvas = function(pos) { - //return [pos[0] / this.scale - this.offset[0], pos[1] / this.scale - this.offset[1]]; - return [ - (pos[0] + this.offset[0]) * this.scale, - (pos[1] + this.offset[1]) * this.scale - ]; - }; - - DragAndScale.prototype.convertCanvasToOffset = function(pos, out) { - out = out || [0, 0]; - out[0] = pos[0] / this.scale - this.offset[0]; - out[1] = pos[1] / this.scale - this.offset[1]; - return out; - }; - - DragAndScale.prototype.mouseDrag = function(x, y) { - this.offset[0] += x / this.scale; - this.offset[1] += y / this.scale; - - if (this.onredraw) { - this.onredraw(this); - } - }; - - DragAndScale.prototype.changeScale = function(value, zooming_center) { - if (value < this.min_scale) { - value = this.min_scale; - } else if (value > this.max_scale) { - value = this.max_scale; - } - - if (value == this.scale) { - return; - } - - if (!this.element) { - return; - } - - var rect = this.element.getBoundingClientRect(); - if (!rect) { - return; - } - - zooming_center = zooming_center || [ - rect.width * 0.5, - rect.height * 0.5 - ]; - var center = this.convertCanvasToOffset(zooming_center); - this.scale = value; - if (Math.abs(this.scale - 1) < 0.01) { - this.scale = 1; - } - - var new_center = this.convertCanvasToOffset(zooming_center); - var delta_offset = [ - new_center[0] - center[0], - new_center[1] - center[1] - ]; - - this.offset[0] += delta_offset[0]; - this.offset[1] += delta_offset[1]; - - if (this.onredraw) { - this.onredraw(this); - } - }; - - DragAndScale.prototype.changeDeltaScale = function(value, zooming_center) { - this.changeScale(this.scale * value, zooming_center); - }; - - DragAndScale.prototype.reset = function() { - this.scale = 1; - this.offset[0] = 0; - this.offset[1] = 0; - }; - - //********************************************************************************* - // LGraphCanvas: LGraph renderer CLASS - //********************************************************************************* - - /** - * This class is in charge of rendering one graph inside a canvas. And provides all the interaction required. - * Valid callbacks are: onNodeSelected, onNodeDeselected, onShowNodePanel, onNodeDblClicked - * - * @class LGraphCanvas - * @constructor - * @param {HTMLCanvas} canvas the canvas where you want to render (it accepts a selector in string format or the canvas element itself) - * @param {LGraph} graph [optional] - * @param {Object} options [optional] { skip_rendering, autoresize } - */ - function LGraphCanvas(canvas, graph, options) { - options = options || {}; - - //if(graph === undefined) - // throw ("No graph assigned"); - this.background_image = - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; - - if (canvas && canvas.constructor === String) { - canvas = document.querySelector(canvas); - } - - this.ds = new DragAndScale(); - this.zoom_modify_alpha = true; //otherwise it generates ugly patterns when scaling down too much - - this.title_text_font = "" + LiteGraph.NODE_TEXT_SIZE + "px Arial"; - this.inner_text_font = - "normal " + LiteGraph.NODE_SUBTEXT_SIZE + "px Arial"; - this.node_title_color = LiteGraph.NODE_TITLE_COLOR; - this.default_link_color = LiteGraph.LINK_COLOR; - this.default_connection_color = { - input_off: "#778", - input_on: "#7F7", - output_off: "#778", - output_on: "#7F7" - }; - - this.highquality_render = true; - this.use_gradients = false; //set to true to render titlebar with gradients - this.editor_alpha = 1; //used for transition - this.pause_rendering = false; - this.clear_background = true; - - this.read_only = false; //if set to true users cannot modify the graph - this.render_only_selected = true; - this.live_mode = false; - this.show_info = true; - this.allow_dragcanvas = true; - this.allow_dragnodes = true; - this.allow_interaction = true; //allow to control widgets, buttons, collapse, etc - this.allow_searchbox = true; - this.allow_reconnect_links = false; //allows to change a connection with having to redo it again - - this.drag_mode = false; - this.dragging_rectangle = null; - - this.filter = null; //allows to filter to only accept some type of nodes in a graph - - this.always_render_background = false; - this.render_shadows = true; - this.render_canvas_border = true; - this.render_connections_shadows = false; //too much cpu - this.render_connections_border = true; - this.render_curved_connections = false; - this.render_connection_arrows = false; - this.render_collapsed_slots = true; - this.render_execution_order = false; - this.render_title_colored = true; - this.render_link_tooltip = true; - - this.links_render_mode = LiteGraph.SPLINE_LINK; - - this.canvas_mouse = [0, 0]; //mouse in canvas graph coordinates, where 0,0 is the top-left corner of the blue rectangle - - //to personalize the search box - this.onSearchBox = null; - this.onSearchBoxSelection = null; - - //callbacks - this.onMouse = null; - this.onDrawBackground = null; //to render background objects (behind nodes and connections) in the canvas affected by transform - this.onDrawForeground = null; //to render foreground objects (above nodes and connections) in the canvas affected by transform - this.onDrawOverlay = null; //to render foreground objects not affected by transform (for GUIs) - this.onDrawLinkTooltip = null; //called when rendering a tooltip - this.onNodeMoved = null; //called after moving a node - this.onSelectionChange = null; //called if the selection changes - - this.connections_width = 3; - this.round_radius = 8; - - this.current_node = null; - this.node_widget = null; //used for widgets - this.over_link_center = null; - this.last_mouse_position = [0, 0]; - this.visible_area = this.ds.visible_area; - this.visible_links = []; - - //link canvas and graph - if (graph) { - graph.attachCanvas(this); - } - - this.setCanvas(canvas); - this.clear(); - - if (!options.skip_render) { - this.startRendering(); - } - - this.autoresize = options.autoresize; - } - - global.LGraphCanvas = LiteGraph.LGraphCanvas = LGraphCanvas; - - LGraphCanvas.link_type_colors = { - "-1": LiteGraph.EVENT_LINK_COLOR, - number: "#AAA", - node: "#DCA" - }; - LGraphCanvas.gradients = {}; //cache of gradients - - /** - * clears all the data inside - * - * @method clear - */ - LGraphCanvas.prototype.clear = function() { - this.frame = 0; - this.last_draw_time = 0; - this.render_time = 0; - this.fps = 0; - - //this.scale = 1; - //this.offset = [0,0]; - - this.dragging_rectangle = null; - - this.selected_nodes = {}; - this.selected_group = null; - - this.visible_nodes = []; - this.node_dragged = null; - this.node_over = null; - this.node_capturing_input = null; - this.connecting_node = null; - this.highlighted_links = {}; - - this.dirty_canvas = true; - this.dirty_bgcanvas = true; - this.dirty_area = null; - - this.node_in_panel = null; - this.node_widget = null; - - this.last_mouse = [0, 0]; - this.last_mouseclick = 0; - this.visible_area.set([0, 0, 0, 0]); - - if (this.onClear) { - this.onClear(); - } - }; - - /** - * assigns a graph, you can reassign graphs to the same canvas - * - * @method setGraph - * @param {LGraph} graph - */ - LGraphCanvas.prototype.setGraph = function(graph, skip_clear) { - if (this.graph == graph) { - return; - } - - if (!skip_clear) { - this.clear(); - } - - if (!graph && this.graph) { - this.graph.detachCanvas(this); - return; - } - - graph.attachCanvas(this); - - //remove the graph stack in case a subgraph was open - if (this._graph_stack) - this._graph_stack = null; - - this.setDirty(true, true); - }; - - /** - * opens a graph contained inside a node in the current graph - * - * @method openSubgraph - * @param {LGraph} graph - */ - LGraphCanvas.prototype.openSubgraph = function(graph) { - if (!graph) { - throw "graph cannot be null"; - } - - if (this.graph == graph) { - throw "graph cannot be the same"; - } - - this.clear(); - - if (this.graph) { - if (!this._graph_stack) { - this._graph_stack = []; - } - this._graph_stack.push(this.graph); - } - - graph.attachCanvas(this); - this.setDirty(true, true); - }; - - /** - * closes a subgraph contained inside a node - * - * @method closeSubgraph - * @param {LGraph} assigns a graph - */ - LGraphCanvas.prototype.closeSubgraph = function() { - if (!this._graph_stack || this._graph_stack.length == 0) { - return; - } - var subgraph_node = this.graph._subgraph_node; - var graph = this._graph_stack.pop(); - this.selected_nodes = {}; - this.highlighted_links = {}; - graph.attachCanvas(this); - this.setDirty(true, true); - if (subgraph_node) { - this.centerOnNode(subgraph_node); - this.selectNodes([subgraph_node]); - } - }; - - /** - * returns the visualy active graph (in case there are more in the stack) - * @method getCurrentGraph - * @return {LGraph} the active graph - */ - LGraphCanvas.prototype.getCurrentGraph = function() { - return this.graph; - }; - - /** - * assigns a canvas - * - * @method setCanvas - * @param {Canvas} assigns a canvas (also accepts the ID of the element (not a selector) - */ - LGraphCanvas.prototype.setCanvas = function(canvas, skip_events) { - var that = this; - - if (canvas) { - if (canvas.constructor === String) { - canvas = document.getElementById(canvas); - if (!canvas) { - throw "Error creating LiteGraph canvas: Canvas not found"; - } - } - } - - if (canvas === this.canvas) { - return; - } - - if (!canvas && this.canvas) { - //maybe detach events from old_canvas - if (!skip_events) { - this.unbindEvents(); - } - } - - this.canvas = canvas; - this.ds.element = canvas; - - if (!canvas) { - return; - } - - //this.canvas.tabindex = "1000"; - canvas.className += " lgraphcanvas"; - canvas.data = this; - canvas.tabindex = "1"; //to allow key events - - //bg canvas: used for non changing stuff - this.bgcanvas = null; - if (!this.bgcanvas) { - this.bgcanvas = document.createElement("canvas"); - this.bgcanvas.width = this.canvas.width; - this.bgcanvas.height = this.canvas.height; - } - - if (canvas.getContext == null) { - if (canvas.localName != "canvas") { - throw "Element supplied for LGraphCanvas must be a element, you passed a " + - canvas.localName; - } - throw "This browser doesn't support Canvas"; - } - - var ctx = (this.ctx = canvas.getContext("2d")); - if (ctx == null) { - if (!canvas.webgl_enabled) { - console.warn( - "This canvas seems to be WebGL, enabling WebGL renderer" - ); - } - this.enableWebGL(); - } - - //input: (move and up could be unbinded) - this._mousemove_callback = this.processMouseMove.bind(this); - this._mouseup_callback = this.processMouseUp.bind(this); - - if (!skip_events) { - this.bindEvents(); - } - }; - - //used in some events to capture them - LGraphCanvas.prototype._doNothing = function doNothing(e) { - e.preventDefault(); - return false; - }; - LGraphCanvas.prototype._doReturnTrue = function doNothing(e) { - e.preventDefault(); - return true; - }; - - /** - * binds mouse, keyboard, touch and drag events to the canvas - * @method bindEvents - **/ - LGraphCanvas.prototype.bindEvents = function() { - if (this._events_binded) { - console.warn("LGraphCanvas: events already binded"); - return; - } - - var canvas = this.canvas; - var ref_window = this.getCanvasWindow(); - var document = ref_window.document; //hack used when moving canvas between windows - - this._mousedown_callback = this.processMouseDown.bind(this); - this._mousewheel_callback = this.processMouseWheel.bind(this); - - canvas.addEventListener("mousedown", this._mousedown_callback, true); //down do not need to store the binded - canvas.addEventListener("mousemove", this._mousemove_callback); - canvas.addEventListener("mousewheel", this._mousewheel_callback, false); - - canvas.addEventListener("contextmenu", this._doNothing); - canvas.addEventListener( - "DOMMouseScroll", - this._mousewheel_callback, - false - ); - - //touch events - //if( 'touchstart' in document.documentElement ) - { - canvas.addEventListener("touchstart", this.touchHandler, true); - canvas.addEventListener("touchmove", this.touchHandler, true); - canvas.addEventListener("touchend", this.touchHandler, true); - canvas.addEventListener("touchcancel", this.touchHandler, true); - } - - //Keyboard ****************** - this._key_callback = this.processKey.bind(this); - - canvas.addEventListener("keydown", this._key_callback, true); - document.addEventListener("keyup", this._key_callback, true); //in document, otherwise it doesn't fire keyup - - //Dropping Stuff over nodes ************************************ - this._ondrop_callback = this.processDrop.bind(this); - - canvas.addEventListener("dragover", this._doNothing, false); - canvas.addEventListener("dragend", this._doNothing, false); - canvas.addEventListener("drop", this._ondrop_callback, false); - canvas.addEventListener("dragenter", this._doReturnTrue, false); - - this._events_binded = true; - }; - - /** - * unbinds mouse events from the canvas - * @method unbindEvents - **/ - LGraphCanvas.prototype.unbindEvents = function() { - if (!this._events_binded) { - console.warn("LGraphCanvas: no events binded"); - return; - } - - var ref_window = this.getCanvasWindow(); - var document = ref_window.document; - - this.canvas.removeEventListener("mousedown", this._mousedown_callback); - this.canvas.removeEventListener( - "mousewheel", - this._mousewheel_callback - ); - this.canvas.removeEventListener( - "DOMMouseScroll", - this._mousewheel_callback - ); - this.canvas.removeEventListener("keydown", this._key_callback); - document.removeEventListener("keyup", this._key_callback); - this.canvas.removeEventListener("contextmenu", this._doNothing); - this.canvas.removeEventListener("drop", this._ondrop_callback); - this.canvas.removeEventListener("dragenter", this._doReturnTrue); - - this.canvas.removeEventListener("touchstart", this.touchHandler); - this.canvas.removeEventListener("touchmove", this.touchHandler); - this.canvas.removeEventListener("touchend", this.touchHandler); - this.canvas.removeEventListener("touchcancel", this.touchHandler); - - this._mousedown_callback = null; - this._mousewheel_callback = null; - this._key_callback = null; - this._ondrop_callback = null; - - this._events_binded = false; - }; - - LGraphCanvas.getFileExtension = function(url) { - var question = url.indexOf("?"); - if (question != -1) { - url = url.substr(0, question); - } - var point = url.lastIndexOf("."); - if (point == -1) { - return ""; - } - return url.substr(point + 1).toLowerCase(); - }; - - /** - * this function allows to render the canvas using WebGL instead of Canvas2D - * this is useful if you plant to render 3D objects inside your nodes, it uses litegl.js for webgl and canvas2DtoWebGL to emulate the Canvas2D calls in webGL - * @method enableWebGL - **/ - LGraphCanvas.prototype.enableWebGL = function() { - if (typeof GL === undefined) { - throw "litegl.js must be included to use a WebGL canvas"; - } - if (typeof enableWebGLCanvas === undefined) { - throw "webglCanvas.js must be included to use this feature"; - } - - this.gl = this.ctx = enableWebGLCanvas(this.canvas); - this.ctx.webgl = true; - this.bgcanvas = this.canvas; - this.bgctx = this.gl; - this.canvas.webgl_enabled = true; - - /* - GL.create({ canvas: this.bgcanvas }); - this.bgctx = enableWebGLCanvas( this.bgcanvas ); - window.gl = this.gl; - */ - }; - - /** - * marks as dirty the canvas, this way it will be rendered again - * - * @class LGraphCanvas - * @method setDirty - * @param {bool} fgcanvas if the foreground canvas is dirty (the one containing the nodes) - * @param {bool} bgcanvas if the background canvas is dirty (the one containing the wires) - */ - LGraphCanvas.prototype.setDirty = function(fgcanvas, bgcanvas) { - if (fgcanvas) { - this.dirty_canvas = true; - } - if (bgcanvas) { - this.dirty_bgcanvas = true; - } - }; - - /** - * Used to attach the canvas in a popup - * - * @method getCanvasWindow - * @return {window} returns the window where the canvas is attached (the DOM root node) - */ - LGraphCanvas.prototype.getCanvasWindow = function() { - if (!this.canvas) { - return window; - } - var doc = this.canvas.ownerDocument; - return doc.defaultView || doc.parentWindow; - }; - - /** - * starts rendering the content of the canvas when needed - * - * @method startRendering - */ - LGraphCanvas.prototype.startRendering = function() { - if (this.is_rendering) { - return; - } //already rendering - - this.is_rendering = true; - renderFrame.call(this); - - function renderFrame() { - if (!this.pause_rendering) { - this.draw(); - } - - var window = this.getCanvasWindow(); - if (this.is_rendering) { - window.requestAnimationFrame(renderFrame.bind(this)); - } - } - }; - - /** - * stops rendering the content of the canvas (to save resources) - * - * @method stopRendering - */ - LGraphCanvas.prototype.stopRendering = function() { - this.is_rendering = false; - /* - if(this.rendering_timer_id) - { - clearInterval(this.rendering_timer_id); - this.rendering_timer_id = null; - } - */ - }; - - /* LiteGraphCanvas input */ - - LGraphCanvas.prototype.processMouseDown = function(e) { - if (!this.graph) { - return; - } - - this.adjustMouseEvent(e); - - var ref_window = this.getCanvasWindow(); - var document = ref_window.document; - LGraphCanvas.active_canvas = this; - var that = this; - - //move mouse move event to the window in case it drags outside of the canvas - this.canvas.removeEventListener("mousemove", this._mousemove_callback); - ref_window.document.addEventListener( - "mousemove", - this._mousemove_callback, - true - ); //catch for the entire window - ref_window.document.addEventListener( - "mouseup", - this._mouseup_callback, - true - ); - - var node = this.graph.getNodeOnPos( - e.canvasX, - e.canvasY, - this.visible_nodes, - 5 - ); - var skip_dragging = false; - var skip_action = false; - var now = LiteGraph.getTime(); - var is_double_click = now - this.last_mouseclick < 300; - - this.canvas_mouse[0] = e.canvasX; - this.canvas_mouse[1] = e.canvasY; - this.canvas.focus(); - - LiteGraph.closeAllContextMenus(ref_window); - - if (this.onMouse) { - if (this.onMouse(e) == true) { - return; - } - } - - if (e.which == 1) { - //left button mouse - if (e.ctrlKey) { - this.dragging_rectangle = new Float32Array(4); - this.dragging_rectangle[0] = e.canvasX; - this.dragging_rectangle[1] = e.canvasY; - this.dragging_rectangle[2] = 1; - this.dragging_rectangle[3] = 1; - skip_action = true; - } - - var clicking_canvas_bg = false; - - //when clicked on top of a node - //and it is not interactive - if (node && this.allow_interaction && !skip_action && !this.read_only) { - if (!this.live_mode && !node.flags.pinned) { - this.bringToFront(node); - } //if it wasn't selected? - - //not dragging mouse to connect two slots - if ( - !this.connecting_node && - !node.flags.collapsed && - !this.live_mode - ) { - //Search for corner for resize - if ( - !skip_action && - node.resizable !== false && - isInsideRectangle( - e.canvasX, - e.canvasY, - node.pos[0] + node.size[0] - 5, - node.pos[1] + node.size[1] - 5, - 10, - 10 - ) - ) { - this.resizing_node = node; - this.canvas.style.cursor = "se-resize"; - skip_action = true; - } else { - //search for outputs - if (node.outputs) { - for ( - var i = 0, l = node.outputs.length; - i < l; - ++i - ) { - var output = node.outputs[i]; - var link_pos = node.getConnectionPos(false, i); - if ( - isInsideRectangle( - e.canvasX, - e.canvasY, - link_pos[0] - 15, - link_pos[1] - 10, - 30, - 20 - ) - ) { - this.connecting_node = node; - this.connecting_output = output; - this.connecting_pos = node.getConnectionPos( false, i ); - this.connecting_slot = i; - - if (e.shiftKey) { - node.disconnectOutput(i); - } - - if (is_double_click) { - if (node.onOutputDblClick) { - node.onOutputDblClick(i, e); - } - } else { - if (node.onOutputClick) { - node.onOutputClick(i, e); - } - } - - skip_action = true; - break; - } - } - } - - //search for inputs - if (node.inputs) { - for ( - var i = 0, l = node.inputs.length; - i < l; - ++i - ) { - var input = node.inputs[i]; - var link_pos = node.getConnectionPos(true, i); - if ( - isInsideRectangle( - e.canvasX, - e.canvasY, - link_pos[0] - 15, - link_pos[1] - 10, - 30, - 20 - ) - ) { - if (is_double_click) { - if (node.onInputDblClick) { - node.onInputDblClick(i, e); - } - } else { - if (node.onInputClick) { - node.onInputClick(i, e); - } - } - - if (input.link !== null) { - var link_info = this.graph.links[ - input.link - ]; //before disconnecting - node.disconnectInput(i); - - if ( - this.allow_reconnect_links || - e.shiftKey - ) { - this.connecting_node = this.graph._nodes_by_id[ - link_info.origin_id - ]; - this.connecting_slot = - link_info.origin_slot; - this.connecting_output = this.connecting_node.outputs[ - this.connecting_slot - ]; - this.connecting_pos = this.connecting_node.getConnectionPos( false, this.connecting_slot ); - } - - this.dirty_bgcanvas = true; - skip_action = true; - } - } - } - } - } //not resizing - } - - //Search for corner for collapsing - /* - if( !skip_action && isInsideRectangle( e.canvasX, e.canvasY, node.pos[0], node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT )) - { - node.collapse(); - skip_action = true; - } - */ - - //it wasn't clicked on the links boxes - if (!skip_action) { - var block_drag_node = false; - - //widgets - var widget = this.processNodeWidgets( node, this.canvas_mouse, e ); - if (widget) { - block_drag_node = true; - this.node_widget = [node, widget]; - } - - //double clicking - if (is_double_click && this.selected_nodes[node.id]) { - //double click node - if (node.onDblClick) { - node.onDblClick( e, [ e.canvasX - node.pos[0], e.canvasY - node.pos[1] ], this ); - } - this.processNodeDblClicked(node); - block_drag_node = true; - } - - //if do not capture mouse - if ( node.onMouseDown && node.onMouseDown( e, [e.canvasX - node.pos[0], e.canvasY - node.pos[1]], this ) ) { - block_drag_node = true; - } else if (this.live_mode) { - clicking_canvas_bg = true; - block_drag_node = true; - } - - if (!block_drag_node) { - if (this.allow_dragnodes) { - this.node_dragged = node; - } - if (!this.selected_nodes[node.id]) { - this.processNodeSelected(node, e); - } - } - - this.dirty_canvas = true; - } - } //clicked outside of nodes - else { - //search for link connector - if(!this.read_only) - for (var i = 0; i < this.visible_links.length; ++i) { - var link = this.visible_links[i]; - var center = link._pos; - if ( - !center || - e.canvasX < center[0] - 4 || - e.canvasX > center[0] + 4 || - e.canvasY < center[1] - 4 || - e.canvasY > center[1] + 4 - ) { - continue; - } - //link clicked - this.showLinkMenu(link, e); - this.over_link_center = null; //clear tooltip - break; - } - - this.selected_group = this.graph.getGroupOnPos( e.canvasX, e.canvasY ); - this.selected_group_resizing = false; - if (this.selected_group && !this.read_only ) { - if (e.ctrlKey) { - this.dragging_rectangle = null; - } - - var dist = distance( [e.canvasX, e.canvasY], [ this.selected_group.pos[0] + this.selected_group.size[0], this.selected_group.pos[1] + this.selected_group.size[1] ] ); - if (dist * this.ds.scale < 10) { - this.selected_group_resizing = true; - } else { - this.selected_group.recomputeInsideNodes(); - } - } - - if (is_double_click && !this.read_only && this.allow_searchbox) { - this.showSearchBox(e); - } - - clicking_canvas_bg = true; - } - - if (!skip_action && clicking_canvas_bg && this.allow_dragcanvas) { - this.dragging_canvas = true; - } - } else if (e.which == 2) { - //middle button - } else if (e.which == 3) { - //right button - if(!this.read_only) - this.processContextMenu(node, e); - } - - //TODO - //if(this.node_selected != prev_selected) - // this.onNodeSelectionChange(this.node_selected); - - this.last_mouse[0] = e.localX; - this.last_mouse[1] = e.localY; - this.last_mouseclick = LiteGraph.getTime(); - this.last_mouse_dragging = true; - - /* - if( (this.dirty_canvas || this.dirty_bgcanvas) && this.rendering_timer_id == null) - this.draw(); - */ - - this.graph.change(); - - //this is to ensure to defocus(blur) if a text input element is on focus - if ( - !ref_window.document.activeElement || - (ref_window.document.activeElement.nodeName.toLowerCase() != - "input" && - ref_window.document.activeElement.nodeName.toLowerCase() != - "textarea") - ) { - e.preventDefault(); - } - e.stopPropagation(); - - if (this.onMouseDown) { - this.onMouseDown(e); - } - - return false; - }; - - /** - * Called when a mouse move event has to be processed - * @method processMouseMove - **/ - LGraphCanvas.prototype.processMouseMove = function(e) { - if (this.autoresize) { - this.resize(); - } - - if (!this.graph) { - return; - } - - LGraphCanvas.active_canvas = this; - this.adjustMouseEvent(e); - var mouse = [e.localX, e.localY]; - var delta = [ - mouse[0] - this.last_mouse[0], - mouse[1] - this.last_mouse[1] - ]; - this.last_mouse = mouse; - this.canvas_mouse[0] = e.canvasX; - this.canvas_mouse[1] = e.canvasY; - e.dragging = this.last_mouse_dragging; - - if (this.node_widget) { - this.processNodeWidgets( - this.node_widget[0], - this.canvas_mouse, - e, - this.node_widget[1] - ); - this.dirty_canvas = true; - } - - if (this.dragging_rectangle) { - this.dragging_rectangle[2] = e.canvasX - this.dragging_rectangle[0]; - this.dragging_rectangle[3] = e.canvasY - this.dragging_rectangle[1]; - this.dirty_canvas = true; - } else if (this.selected_group && !this.read_only) { - //moving/resizing a group - if (this.selected_group_resizing) { - this.selected_group.size = [ - e.canvasX - this.selected_group.pos[0], - e.canvasY - this.selected_group.pos[1] - ]; - } else { - var deltax = delta[0] / this.ds.scale; - var deltay = delta[1] / this.ds.scale; - this.selected_group.move(deltax, deltay, e.ctrlKey); - if (this.selected_group._nodes.length) { - this.dirty_canvas = true; - } - } - this.dirty_bgcanvas = true; - } else if (this.dragging_canvas) { - this.ds.offset[0] += delta[0] / this.ds.scale; - this.ds.offset[1] += delta[1] / this.ds.scale; - this.dirty_canvas = true; - this.dirty_bgcanvas = true; - } else if (this.allow_interaction && !this.read_only) { - if (this.connecting_node) { - this.dirty_canvas = true; - } - - //get node over - var node = this.graph.getNodeOnPos( - e.canvasX, - e.canvasY, - this.visible_nodes - ); - - //remove mouseover flag - for (var i = 0, l = this.graph._nodes.length; i < l; ++i) { - if ( - this.graph._nodes[i].mouseOver && - node != this.graph._nodes[i] - ) { - //mouse leave - this.graph._nodes[i].mouseOver = false; - if (this.node_over && this.node_over.onMouseLeave) { - this.node_over.onMouseLeave(e); - } - this.node_over = null; - this.dirty_canvas = true; - } - } - - //mouse over a node - if (node) { - //this.canvas.style.cursor = "move"; - if (!node.mouseOver) { - //mouse enter - node.mouseOver = true; - this.node_over = node; - this.dirty_canvas = true; - - if (node.onMouseEnter) { - node.onMouseEnter(e); - } - } - - //in case the node wants to do something - if (node.onMouseMove) { - node.onMouseMove( - e, - [e.canvasX - node.pos[0], e.canvasY - node.pos[1]], - this - ); - } - - //if dragging a link - if (this.connecting_node) { - var pos = this._highlight_input || [0, 0]; //to store the output of isOverNodeInput - - //on top of input - if (this.isOverNodeBox(node, e.canvasX, e.canvasY)) { - //mouse on top of the corner box, don't know what to do - } else { - //check if I have a slot below de mouse - var slot = this.isOverNodeInput( - node, - e.canvasX, - e.canvasY, - pos - ); - if (slot != -1 && node.inputs[slot]) { - var slot_type = node.inputs[slot].type; - if ( - LiteGraph.isValidConnection( - this.connecting_output.type, - slot_type - ) - ) { - this._highlight_input = pos; - } - } else { - this._highlight_input = null; - } - } - } - - //Search for corner - if (this.canvas) { - if ( - isInsideRectangle( - e.canvasX, - e.canvasY, - node.pos[0] + node.size[0] - 5, - node.pos[1] + node.size[1] - 5, - 5, - 5 - ) - ) { - this.canvas.style.cursor = "se-resize"; - } else { - this.canvas.style.cursor = "crosshair"; - } - } - } else { //outside - - //search for link connector - var over_link = null; - for (var i = 0; i < this.visible_links.length; ++i) { - var link = this.visible_links[i]; - var center = link._pos; - if ( - !center || - e.canvasX < center[0] - 4 || - e.canvasX > center[0] + 4 || - e.canvasY < center[1] - 4 || - e.canvasY > center[1] + 4 - ) { - continue; - } - over_link = link; - break; - } - if( over_link != this.over_link_center ) - { - this.over_link_center = over_link; - this.dirty_canvas = true; - } - - if (this.canvas) { - this.canvas.style.cursor = ""; - } - } - - if ( this.node_capturing_input && this.node_capturing_input != node && this.node_capturing_input.onMouseMove ) { - this.node_capturing_input.onMouseMove(e,[e.canvasX - this.node_capturing_input.pos[0],e.canvasY - this.node_capturing_input.pos[1]], this); - } - - if (this.node_dragged && !this.live_mode) { - for (var i in this.selected_nodes) { - var n = this.selected_nodes[i]; - n.pos[0] += delta[0] / this.ds.scale; - n.pos[1] += delta[1] / this.ds.scale; - } - - this.dirty_canvas = true; - this.dirty_bgcanvas = true; - } - - if (this.resizing_node && !this.live_mode) { - //convert mouse to node space - this.resizing_node.size[0] = e.canvasX - this.resizing_node.pos[0]; - this.resizing_node.size[1] = e.canvasY - this.resizing_node.pos[1]; - - //constraint size - var max_slots = Math.max( - this.resizing_node.inputs ? this.resizing_node.inputs.length : 0, - this.resizing_node.outputs ? this.resizing_node.outputs.length : 0 - ); - var min_height = - max_slots * LiteGraph.NODE_SLOT_HEIGHT + - (this.resizing_node.widgets ? this.resizing_node.widgets.length : 0) * (LiteGraph.NODE_WIDGET_HEIGHT + 4) + 4; - if (this.resizing_node.size[1] < min_height) { - this.resizing_node.size[1] = min_height; - } - if (this.resizing_node.size[0] < LiteGraph.NODE_MIN_WIDTH) { - this.resizing_node.size[0] = LiteGraph.NODE_MIN_WIDTH; - } - - this.canvas.style.cursor = "se-resize"; - this.dirty_canvas = true; - this.dirty_bgcanvas = true; - } - } - - e.preventDefault(); - return false; - }; - - /** - * Called when a mouse up event has to be processed - * @method processMouseUp - **/ - LGraphCanvas.prototype.processMouseUp = function(e) { - if (!this.graph) { - return; - } - - var window = this.getCanvasWindow(); - var document = window.document; - LGraphCanvas.active_canvas = this; - - //restore the mousemove event back to the canvas - document.removeEventListener("mousemove",this._mousemove_callback,true); - this.canvas.addEventListener("mousemove",this._mousemove_callback,true); - document.removeEventListener("mouseup", this._mouseup_callback, true); - - this.adjustMouseEvent(e); - var now = LiteGraph.getTime(); - e.click_time = now - this.last_mouseclick; - this.last_mouse_dragging = false; - - if (e.which == 1) { - - if( this.node_widget ) - { - this.processNodeWidgets( this.node_widget[0], this.canvas_mouse, e ); - } - - //left button - this.node_widget = null; - - if (this.selected_group) { - var diffx = - this.selected_group.pos[0] - - Math.round(this.selected_group.pos[0]); - var diffy = - this.selected_group.pos[1] - - Math.round(this.selected_group.pos[1]); - this.selected_group.move(diffx, diffy, e.ctrlKey); - this.selected_group.pos[0] = Math.round( - this.selected_group.pos[0] - ); - this.selected_group.pos[1] = Math.round( - this.selected_group.pos[1] - ); - if (this.selected_group._nodes.length) { - this.dirty_canvas = true; - } - this.selected_group = null; - } - this.selected_group_resizing = false; - - if (this.dragging_rectangle) { - if (this.graph) { - var nodes = this.graph._nodes; - var node_bounding = new Float32Array(4); - this.deselectAllNodes(); - //compute bounding and flip if left to right - var w = Math.abs(this.dragging_rectangle[2]); - var h = Math.abs(this.dragging_rectangle[3]); - var startx = - this.dragging_rectangle[2] < 0 - ? this.dragging_rectangle[0] - w - : this.dragging_rectangle[0]; - var starty = - this.dragging_rectangle[3] < 0 - ? this.dragging_rectangle[1] - h - : this.dragging_rectangle[1]; - this.dragging_rectangle[0] = startx; - this.dragging_rectangle[1] = starty; - this.dragging_rectangle[2] = w; - this.dragging_rectangle[3] = h; - - //test against all nodes (not visible because the rectangle maybe start outside - var to_select = []; - for (var i = 0; i < nodes.length; ++i) { - var node = nodes[i]; - node.getBounding(node_bounding); - if ( - !overlapBounding( - this.dragging_rectangle, - node_bounding - ) - ) { - continue; - } //out of the visible area - to_select.push(node); - } - if (to_select.length) { - this.selectNodes(to_select); - } - } - this.dragging_rectangle = null; - } else if (this.connecting_node) { - //dragging a connection - this.dirty_canvas = true; - this.dirty_bgcanvas = true; - - var node = this.graph.getNodeOnPos( - e.canvasX, - e.canvasY, - this.visible_nodes - ); - - //node below mouse - if (node) { - if ( - this.connecting_output.type == LiteGraph.EVENT && - this.isOverNodeBox(node, e.canvasX, e.canvasY) - ) { - this.connecting_node.connect( - this.connecting_slot, - node, - LiteGraph.EVENT - ); - } else { - //slot below mouse? connect - var slot = this.isOverNodeInput( - node, - e.canvasX, - e.canvasY - ); - if (slot != -1) { - this.connecting_node.connect( - this.connecting_slot, - node, - slot - ); - } else { - //not on top of an input - var input = node.getInputInfo(0); - //auto connect - if ( - this.connecting_output.type == LiteGraph.EVENT - ) { - this.connecting_node.connect( - this.connecting_slot, - node, - LiteGraph.EVENT - ); - } else if ( - input && - !input.link && - LiteGraph.isValidConnection( - input.type && this.connecting_output.type - ) - ) { - this.connecting_node.connect( - this.connecting_slot, - node, - 0 - ); - } - } - } - } - - this.connecting_output = null; - this.connecting_pos = null; - this.connecting_node = null; - this.connecting_slot = -1; - } //not dragging connection - else if (this.resizing_node) { - this.dirty_canvas = true; - this.dirty_bgcanvas = true; - this.resizing_node = null; - } else if (this.node_dragged) { - //node being dragged? - var node = this.node_dragged; - if ( - node && - e.click_time < 300 && - isInsideRectangle( e.canvasX, e.canvasY, node.pos[0], node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT ) - ) { - node.collapse(); - } - - this.dirty_canvas = true; - this.dirty_bgcanvas = true; - this.node_dragged.pos[0] = Math.round(this.node_dragged.pos[0]); - this.node_dragged.pos[1] = Math.round(this.node_dragged.pos[1]); - if (this.graph.config.align_to_grid) { - this.node_dragged.alignToGrid(); - } - if( this.onNodeMoved ) - this.onNodeMoved( this.node_dragged ); - this.node_dragged = null; - } //no node being dragged - else { - //get node over - var node = this.graph.getNodeOnPos( - e.canvasX, - e.canvasY, - this.visible_nodes - ); - - if (!node && e.click_time < 300) { - this.deselectAllNodes(); - } - - this.dirty_canvas = true; - this.dragging_canvas = false; - - if (this.node_over && this.node_over.onMouseUp) { - this.node_over.onMouseUp( e, [ e.canvasX - this.node_over.pos[0], e.canvasY - this.node_over.pos[1] ], this ); - } - if ( - this.node_capturing_input && - this.node_capturing_input.onMouseUp - ) { - this.node_capturing_input.onMouseUp(e, [ - e.canvasX - this.node_capturing_input.pos[0], - e.canvasY - this.node_capturing_input.pos[1] - ]); - } - } - } else if (e.which == 2) { - //middle button - //trace("middle"); - this.dirty_canvas = true; - this.dragging_canvas = false; - } else if (e.which == 3) { - //right button - //trace("right"); - this.dirty_canvas = true; - this.dragging_canvas = false; - } - - /* - if((this.dirty_canvas || this.dirty_bgcanvas) && this.rendering_timer_id == null) - this.draw(); - */ - - this.graph.change(); - - e.stopPropagation(); - e.preventDefault(); - return false; - }; - - /** - * Called when a mouse wheel event has to be processed - * @method processMouseWheel - **/ - LGraphCanvas.prototype.processMouseWheel = function(e) { - if (!this.graph || !this.allow_dragcanvas) { - return; - } - - var delta = e.wheelDeltaY != null ? e.wheelDeltaY : e.detail * -60; - - this.adjustMouseEvent(e); - - var scale = this.ds.scale; - - if (delta > 0) { - scale *= 1.1; - } else if (delta < 0) { - scale *= 1 / 1.1; - } - - //this.setZoom( scale, [ e.localX, e.localY ] ); - this.ds.changeScale(scale, [e.localX, e.localY]); - - this.graph.change(); - - e.preventDefault(); - return false; // prevent default - }; - - /** - * returns true if a position (in graph space) is on top of a node little corner box - * @method isOverNodeBox - **/ - LGraphCanvas.prototype.isOverNodeBox = function(node, canvasx, canvasy) { - var title_height = LiteGraph.NODE_TITLE_HEIGHT; - if ( - isInsideRectangle( - canvasx, - canvasy, - node.pos[0] + 2, - node.pos[1] + 2 - title_height, - title_height - 4, - title_height - 4 - ) - ) { - return true; - } - return false; - }; - - /** - * returns true if a position (in graph space) is on top of a node input slot - * @method isOverNodeInput - **/ - LGraphCanvas.prototype.isOverNodeInput = function( - node, - canvasx, - canvasy, - slot_pos - ) { - if (node.inputs) { - for (var i = 0, l = node.inputs.length; i < l; ++i) { - var input = node.inputs[i]; - var link_pos = node.getConnectionPos(true, i); - var is_inside = false; - if (node.horizontal) { - is_inside = isInsideRectangle( - canvasx, - canvasy, - link_pos[0] - 5, - link_pos[1] - 10, - 10, - 20 - ); - } else { - is_inside = isInsideRectangle( - canvasx, - canvasy, - link_pos[0] - 10, - link_pos[1] - 5, - 40, - 10 - ); - } - if (is_inside) { - if (slot_pos) { - slot_pos[0] = link_pos[0]; - slot_pos[1] = link_pos[1]; - } - return i; - } - } - } - return -1; - }; - - /** - * process a key event - * @method processKey - **/ - LGraphCanvas.prototype.processKey = function(e) { - if (!this.graph) { - return; - } - - var block_default = false; - //console.log(e); //debug - - if (e.target.localName == "input") { - return; - } - - if (e.type == "keydown") { - if (e.keyCode == 32) { - //esc - this.dragging_canvas = true; - block_default = true; - } - - //select all Control A - if (e.keyCode == 65 && e.ctrlKey) { - this.selectNodes(); - block_default = true; - } - - if (e.code == "KeyC" && (e.metaKey || e.ctrlKey) && !e.shiftKey) { - //copy - if (this.selected_nodes) { - this.copyToClipboard(); - block_default = true; - } - } - - if (e.code == "KeyV" && (e.metaKey || e.ctrlKey) && !e.shiftKey) { - //paste - this.pasteFromClipboard(); - } - - //delete or backspace - if (e.keyCode == 46 || e.keyCode == 8) { - if ( - e.target.localName != "input" && - e.target.localName != "textarea" - ) { - this.deleteSelectedNodes(); - block_default = true; - } - } - - //collapse - //... - - //TODO - if (this.selected_nodes) { - for (var i in this.selected_nodes) { - if (this.selected_nodes[i].onKeyDown) { - this.selected_nodes[i].onKeyDown(e); - } - } - } - } else if (e.type == "keyup") { - if (e.keyCode == 32) { - this.dragging_canvas = false; - } - - if (this.selected_nodes) { - for (var i in this.selected_nodes) { - if (this.selected_nodes[i].onKeyUp) { - this.selected_nodes[i].onKeyUp(e); - } - } - } - } - - this.graph.change(); - - if (block_default) { - e.preventDefault(); - e.stopImmediatePropagation(); - return false; - } - }; - - LGraphCanvas.prototype.copyToClipboard = function() { - var clipboard_info = { - nodes: [], - links: [] - }; - var index = 0; - var selected_nodes_array = []; - for (var i in this.selected_nodes) { - var node = this.selected_nodes[i]; - node._relative_id = index; - selected_nodes_array.push(node); - index += 1; - } - - for (var i = 0; i < selected_nodes_array.length; ++i) { - var node = selected_nodes_array[i]; - var cloned = node.clone(); - if(!cloned) - { - console.warn("node type not found: " + node.type ); - continue; - } - clipboard_info.nodes.push(cloned.serialize()); - if (node.inputs && node.inputs.length) { - for (var j = 0; j < node.inputs.length; ++j) { - var input = node.inputs[j]; - if (!input || input.link == null) { - continue; - } - var link_info = this.graph.links[input.link]; - if (!link_info) { - continue; - } - var target_node = this.graph.getNodeById( - link_info.origin_id - ); - if (!target_node || !this.selected_nodes[target_node.id]) { - //improve this by allowing connections to non-selected nodes - continue; - } //not selected - clipboard_info.links.push([ - target_node._relative_id, - link_info.origin_slot, //j, - node._relative_id, - link_info.target_slot - ]); - } - } - } - localStorage.setItem( - "litegrapheditor_clipboard", - JSON.stringify(clipboard_info) - ); - }; - - LGraphCanvas.prototype.pasteFromClipboard = function() { - var data = localStorage.getItem("litegrapheditor_clipboard"); - if (!data) { - return; - } - - //create nodes - var clipboard_info = JSON.parse(data); - var nodes = []; - for (var i = 0; i < clipboard_info.nodes.length; ++i) { - var node_data = clipboard_info.nodes[i]; - var node = LiteGraph.createNode(node_data.type); - if (node) { - node.configure(node_data); - node.pos[0] += 5; - node.pos[1] += 5; - this.graph.add(node); - nodes.push(node); - } - } - - //create links - for (var i = 0; i < clipboard_info.links.length; ++i) { - var link_info = clipboard_info.links[i]; - var origin_node = nodes[link_info[0]]; - var target_node = nodes[link_info[2]]; - if( origin_node && target_node ) - origin_node.connect(link_info[1], target_node, link_info[3]); - else - console.warn("Warning, nodes missing on pasting"); - } - - this.selectNodes(nodes); - }; - - /** - * process a item drop event on top the canvas - * @method processDrop - **/ - LGraphCanvas.prototype.processDrop = function(e) { - e.preventDefault(); - this.adjustMouseEvent(e); - - var pos = [e.canvasX, e.canvasY]; - var node = this.graph.getNodeOnPos(pos[0], pos[1]); - - if (!node) { - var r = null; - if (this.onDropItem) { - r = this.onDropItem(event); - } - if (!r) { - this.checkDropItem(e); - } - return; - } - - if (node.onDropFile || node.onDropData) { - var files = e.dataTransfer.files; - if (files && files.length) { - for (var i = 0; i < files.length; i++) { - var file = e.dataTransfer.files[0]; - var filename = file.name; - var ext = LGraphCanvas.getFileExtension(filename); - //console.log(file); - - if (node.onDropFile) { - node.onDropFile(file); - } - - if (node.onDropData) { - //prepare reader - var reader = new FileReader(); - reader.onload = function(event) { - //console.log(event.target); - var data = event.target.result; - node.onDropData(data, filename, file); - }; - - //read data - var type = file.type.split("/")[0]; - if (type == "text" || type == "") { - reader.readAsText(file); - } else if (type == "image") { - reader.readAsDataURL(file); - } else { - reader.readAsArrayBuffer(file); - } - } - } - } - } - - if (node.onDropItem) { - if (node.onDropItem(event)) { - return true; - } - } - - if (this.onDropItem) { - return this.onDropItem(event); - } - - return false; - }; - - //called if the graph doesn't have a default drop item behaviour - LGraphCanvas.prototype.checkDropItem = function(e) { - if (e.dataTransfer.files.length) { - var file = e.dataTransfer.files[0]; - var ext = LGraphCanvas.getFileExtension(file.name).toLowerCase(); - var nodetype = LiteGraph.node_types_by_file_extension[ext]; - if (nodetype) { - var node = LiteGraph.createNode(nodetype.type); - node.pos = [e.canvasX, e.canvasY]; - this.graph.add(node); - if (node.onDropFile) { - node.onDropFile(file); - } - } - } - }; - - LGraphCanvas.prototype.processNodeDblClicked = function(n) { - if (this.onShowNodePanel) { - this.onShowNodePanel(n); - } - - if (this.onNodeDblClicked) { - this.onNodeDblClicked(n); - } - - this.setDirty(true); - }; - - LGraphCanvas.prototype.processNodeSelected = function(node, e) { - this.selectNode(node, e && e.shiftKey); - if (this.onNodeSelected) { - this.onNodeSelected(node); - } - }; - - /** - * selects a given node (or adds it to the current selection) - * @method selectNode - **/ - LGraphCanvas.prototype.selectNode = function( - node, - add_to_current_selection - ) { - if (node == null) { - this.deselectAllNodes(); - } else { - this.selectNodes([node], add_to_current_selection); - } - }; - - /** - * selects several nodes (or adds them to the current selection) - * @method selectNodes - **/ - LGraphCanvas.prototype.selectNodes = function( - nodes, - add_to_current_selection - ) { - if (!add_to_current_selection) { - this.deselectAllNodes(); - } - - nodes = nodes || this.graph._nodes; - for (var i = 0; i < nodes.length; ++i) { - var node = nodes[i]; - if (node.is_selected) { - continue; - } - - if (!node.is_selected && node.onSelected) { - node.onSelected(); - } - node.is_selected = true; - this.selected_nodes[node.id] = node; - - if (node.inputs) { - for (var j = 0; j < node.inputs.length; ++j) { - this.highlighted_links[node.inputs[j].link] = true; - } - } - if (node.outputs) { - for (var j = 0; j < node.outputs.length; ++j) { - var out = node.outputs[j]; - if (out.links) { - for (var k = 0; k < out.links.length; ++k) { - this.highlighted_links[out.links[k]] = true; - } - } - } - } - } - - if( this.onSelectionChange ) - this.onSelectionChange( this.selected_nodes ); - - this.setDirty(true); - }; - - /** - * removes a node from the current selection - * @method deselectNode - **/ - LGraphCanvas.prototype.deselectNode = function(node) { - if (!node.is_selected) { - return; - } - if (node.onDeselected) { - node.onDeselected(); - } - node.is_selected = false; - - if (this.onNodeDeselected) { - this.onNodeDeselected(node); - } - - //remove highlighted - if (node.inputs) { - for (var i = 0; i < node.inputs.length; ++i) { - delete this.highlighted_links[node.inputs[i].link]; - } - } - if (node.outputs) { - for (var i = 0; i < node.outputs.length; ++i) { - var out = node.outputs[i]; - if (out.links) { - for (var j = 0; j < out.links.length; ++j) { - delete this.highlighted_links[out.links[j]]; - } - } - } - } - }; - - /** - * removes all nodes from the current selection - * @method deselectAllNodes - **/ - LGraphCanvas.prototype.deselectAllNodes = function() { - if (!this.graph) { - return; - } - var nodes = this.graph._nodes; - for (var i = 0, l = nodes.length; i < l; ++i) { - var node = nodes[i]; - if (!node.is_selected) { - continue; - } - if (node.onDeselected) { - node.onDeselected(); - } - node.is_selected = false; - if (this.onNodeDeselected) { - this.onNodeDeselected(node); - } - } - this.selected_nodes = {}; - this.current_node = null; - this.highlighted_links = {}; - if( this.onSelectionChange ) - this.onSelectionChange( this.selected_nodes ); - this.setDirty(true); - }; - - /** - * deletes all nodes in the current selection from the graph - * @method deleteSelectedNodes - **/ - LGraphCanvas.prototype.deleteSelectedNodes = function() { - for (var i in this.selected_nodes) { - var node = this.selected_nodes[i]; - //autoconnect when possible (very basic, only takes into account first input-output) - if(node.inputs && node.inputs.length && node.outputs && node.outputs.length && LiteGraph.isValidConnection( node.inputs[0].type, node.outputs[0].type ) && node.inputs[0].link && node.outputs[0].links && node.outputs[0].links.length ) - { - var input_link = node.graph.links[ node.inputs[0].link ]; - var output_link = node.graph.links[ node.outputs[0].links[0] ]; - var input_node = node.getInputNode(0); - var output_node = node.getOutputNodes(0)[0]; - if(input_node && output_node) - input_node.connect( input_link.origin_slot, output_node, output_link.target_slot ); - } - this.graph.remove(node); - if (this.onNodeDeselected) { - this.onNodeDeselected(node); - } - } - this.selected_nodes = {}; - this.current_node = null; - this.highlighted_links = {}; - this.setDirty(true); - }; - - /** - * centers the camera on a given node - * @method centerOnNode - **/ - LGraphCanvas.prototype.centerOnNode = function(node) { - this.ds.offset[0] = - -node.pos[0] - - node.size[0] * 0.5 + - (this.canvas.width * 0.5) / this.ds.scale; - this.ds.offset[1] = - -node.pos[1] - - node.size[1] * 0.5 + - (this.canvas.height * 0.5) / this.ds.scale; - this.setDirty(true, true); - }; - - /** - * adds some useful properties to a mouse event, like the position in graph coordinates - * @method adjustMouseEvent - **/ - LGraphCanvas.prototype.adjustMouseEvent = function(e) { - if (this.canvas) { - var b = this.canvas.getBoundingClientRect(); - e.localX = e.clientX - b.left; - e.localY = e.clientY - b.top; - } else { - e.localX = e.clientX; - e.localY = e.clientY; - } - - e.deltaX = e.localX - this.last_mouse_position[0]; - e.deltaY = e.localY - this.last_mouse_position[1]; - - this.last_mouse_position[0] = e.localX; - this.last_mouse_position[1] = e.localY; - - e.canvasX = e.localX / this.ds.scale - this.ds.offset[0]; - e.canvasY = e.localY / this.ds.scale - this.ds.offset[1]; - }; - - /** - * changes the zoom level of the graph (default is 1), you can pass also a place used to pivot the zoom - * @method setZoom - **/ - LGraphCanvas.prototype.setZoom = function(value, zooming_center) { - this.ds.changeScale(value, zooming_center); - /* - if(!zooming_center && this.canvas) - zooming_center = [this.canvas.width * 0.5,this.canvas.height * 0.5]; - - var center = this.convertOffsetToCanvas( zooming_center ); - - this.ds.scale = value; - - if(this.scale > this.max_zoom) - this.scale = this.max_zoom; - else if(this.scale < this.min_zoom) - this.scale = this.min_zoom; - - var new_center = this.convertOffsetToCanvas( zooming_center ); - var delta_offset = [new_center[0] - center[0], new_center[1] - center[1]]; - - this.offset[0] += delta_offset[0]; - this.offset[1] += delta_offset[1]; - */ - - this.dirty_canvas = true; - this.dirty_bgcanvas = true; - }; - - /** - * converts a coordinate from graph coordinates to canvas2D coordinates - * @method convertOffsetToCanvas - **/ - LGraphCanvas.prototype.convertOffsetToCanvas = function(pos, out) { - return this.ds.convertOffsetToCanvas(pos, out); - }; - - /** - * converts a coordinate from Canvas2D coordinates to graph space - * @method convertCanvasToOffset - **/ - LGraphCanvas.prototype.convertCanvasToOffset = function(pos, out) { - return this.ds.convertCanvasToOffset(pos, out); - }; - - //converts event coordinates from canvas2D to graph coordinates - LGraphCanvas.prototype.convertEventToCanvasOffset = function(e) { - var rect = this.canvas.getBoundingClientRect(); - return this.convertCanvasToOffset([ - e.clientX - rect.left, - e.clientY - rect.top - ]); - }; - - /** - * brings a node to front (above all other nodes) - * @method bringToFront - **/ - LGraphCanvas.prototype.bringToFront = function(node) { - var i = this.graph._nodes.indexOf(node); - if (i == -1) { - return; - } - - this.graph._nodes.splice(i, 1); - this.graph._nodes.push(node); - }; - - /** - * sends a node to the back (below all other nodes) - * @method sendToBack - **/ - LGraphCanvas.prototype.sendToBack = function(node) { - var i = this.graph._nodes.indexOf(node); - if (i == -1) { - return; - } - - this.graph._nodes.splice(i, 1); - this.graph._nodes.unshift(node); - }; - - /* Interaction */ - - /* LGraphCanvas render */ - var temp = new Float32Array(4); - - /** - * checks which nodes are visible (inside the camera area) - * @method computeVisibleNodes - **/ - LGraphCanvas.prototype.computeVisibleNodes = function(nodes, out) { - var visible_nodes = out || []; - visible_nodes.length = 0; - nodes = nodes || this.graph._nodes; - for (var i = 0, l = nodes.length; i < l; ++i) { - var n = nodes[i]; - - //skip rendering nodes in live mode - if (this.live_mode && !n.onDrawBackground && !n.onDrawForeground) { - continue; - } - - if (!overlapBounding(this.visible_area, n.getBounding(temp))) { - continue; - } //out of the visible area - - visible_nodes.push(n); - } - return visible_nodes; - }; - - /** - * renders the whole canvas content, by rendering in two separated canvas, one containing the background grid and the connections, and one containing the nodes) - * @method draw - **/ - LGraphCanvas.prototype.draw = function(force_canvas, force_bgcanvas) { - if (!this.canvas) { - return; - } - - //fps counting - var now = LiteGraph.getTime(); - this.render_time = (now - this.last_draw_time) * 0.001; - this.last_draw_time = now; - - if (this.graph) { - this.ds.computeVisibleArea(); - } - - if ( - this.dirty_bgcanvas || - force_bgcanvas || - this.always_render_background || - (this.graph && - this.graph._last_trigger_time && - now - this.graph._last_trigger_time < 1000) - ) { - this.drawBackCanvas(); - } - - if (this.dirty_canvas || force_canvas) { - this.drawFrontCanvas(); - } - - this.fps = this.render_time ? 1.0 / this.render_time : 0; - this.frame += 1; - }; - - /** - * draws the front canvas (the one containing all the nodes) - * @method drawFrontCanvas - **/ - LGraphCanvas.prototype.drawFrontCanvas = function() { - this.dirty_canvas = false; - - if (!this.ctx) { - this.ctx = this.bgcanvas.getContext("2d"); - } - var ctx = this.ctx; - if (!ctx) { - //maybe is using webgl... - return; - } - - if (ctx.start2D) { - ctx.start2D(); - } - - var canvas = this.canvas; - - //reset in case of error - ctx.restore(); - ctx.setTransform(1, 0, 0, 1, 0, 0); - - //clip dirty area if there is one, otherwise work in full canvas - if (this.dirty_area) { - ctx.save(); - ctx.beginPath(); - ctx.rect( - this.dirty_area[0], - this.dirty_area[1], - this.dirty_area[2], - this.dirty_area[3] - ); - ctx.clip(); - } - - //clear - //canvas.width = canvas.width; - if (this.clear_background) { - ctx.clearRect(0, 0, canvas.width, canvas.height); - } - - //draw bg canvas - if (this.bgcanvas == this.canvas) { - this.drawBackCanvas(); - } else { - ctx.drawImage(this.bgcanvas, 0, 0); - } - - //rendering - if (this.onRender) { - this.onRender(canvas, ctx); - } - - //info widget - if (this.show_info) { - this.renderInfo(ctx); - } - - if (this.graph) { - //apply transformations - ctx.save(); - this.ds.toCanvasContext(ctx); - - //draw nodes - var drawn_nodes = 0; - var visible_nodes = this.computeVisibleNodes( - null, - this.visible_nodes - ); - - for (var i = 0; i < visible_nodes.length; ++i) { - var node = visible_nodes[i]; - - //transform coords system - ctx.save(); - ctx.translate(node.pos[0], node.pos[1]); - - //Draw - this.drawNode(node, ctx); - drawn_nodes += 1; - - //Restore - ctx.restore(); - } - - //on top (debug) - if (this.render_execution_order) { - this.drawExecutionOrder(ctx); - } - - //connections ontop? - if (this.graph.config.links_ontop) { - if (!this.live_mode) { - this.drawConnections(ctx); - } - } - - //current connection (the one being dragged by the mouse) - if (this.connecting_pos != null) { - ctx.lineWidth = this.connections_width; - var link_color = null; - - switch (this.connecting_output.type) { - case LiteGraph.EVENT: - link_color = LiteGraph.EVENT_LINK_COLOR; - break; - default: - link_color = LiteGraph.CONNECTING_LINK_COLOR; - } - - //the connection being dragged by the mouse - this.renderLink( - ctx, - this.connecting_pos, - [this.canvas_mouse[0], this.canvas_mouse[1]], - null, - false, - null, - link_color, - this.connecting_output.dir || - (this.connecting_node.horizontal - ? LiteGraph.DOWN - : LiteGraph.RIGHT), - LiteGraph.CENTER - ); - - ctx.beginPath(); - if ( - this.connecting_output.type === LiteGraph.EVENT || - this.connecting_output.shape === LiteGraph.BOX_SHAPE - ) { - ctx.rect( - this.connecting_pos[0] - 6 + 0.5, - this.connecting_pos[1] - 5 + 0.5, - 14, - 10 - ); - } else { - ctx.arc( - this.connecting_pos[0], - this.connecting_pos[1], - 4, - 0, - Math.PI * 2 - ); - } - ctx.fill(); - - ctx.fillStyle = "#ffcc00"; - if (this._highlight_input) { - ctx.beginPath(); - ctx.arc( - this._highlight_input[0], - this._highlight_input[1], - 6, - 0, - Math.PI * 2 - ); - ctx.fill(); - } - } - - //the selection rectangle - if (this.dragging_rectangle) { - ctx.strokeStyle = "#FFF"; - ctx.strokeRect( - this.dragging_rectangle[0], - this.dragging_rectangle[1], - this.dragging_rectangle[2], - this.dragging_rectangle[3] - ); - } - - //on top of link center - if(this.over_link_center && this.render_link_tooltip) - this.drawLinkTooltip( ctx, this.over_link_center ); - else - if(this.onDrawLinkTooltip) //to remove - this.onDrawLinkTooltip(ctx,null); - - //custom info - if (this.onDrawForeground) { - this.onDrawForeground(ctx, this.visible_rect); - } - - ctx.restore(); - } - - if (this.onDrawOverlay) { - this.onDrawOverlay(ctx); - } - - if (this.dirty_area) { - ctx.restore(); - //this.dirty_area = null; - } - - if (ctx.finish2D) { - //this is a function I use in webgl renderer - ctx.finish2D(); - } - }; - - /** - * draws some useful stats in the corner of the canvas - * @method renderInfo - **/ - LGraphCanvas.prototype.renderInfo = function(ctx, x, y) { - x = x || 0; - y = y || 0; - - ctx.save(); - ctx.translate(x, y); - - ctx.font = "10px Arial"; - ctx.fillStyle = "#888"; - if (this.graph) { - ctx.fillText( "T: " + this.graph.globaltime.toFixed(2) + "s", 5, 13 * 1 ); - ctx.fillText("I: " + this.graph.iteration, 5, 13 * 2 ); - ctx.fillText("N: " + this.graph._nodes.length + " [" + this.visible_nodes.length + "]", 5, 13 * 3 ); - ctx.fillText("V: " + this.graph._version, 5, 13 * 4); - ctx.fillText("FPS:" + this.fps.toFixed(2), 5, 13 * 5); - } else { - ctx.fillText("No graph selected", 5, 13 * 1); - } - ctx.restore(); - }; - - /** - * draws the back canvas (the one containing the background and the connections) - * @method drawBackCanvas - **/ - LGraphCanvas.prototype.drawBackCanvas = function() { - var canvas = this.bgcanvas; - if ( - canvas.width != this.canvas.width || - canvas.height != this.canvas.height - ) { - canvas.width = this.canvas.width; - canvas.height = this.canvas.height; - } - - if (!this.bgctx) { - this.bgctx = this.bgcanvas.getContext("2d"); - } - var ctx = this.bgctx; - if (ctx.start) { - ctx.start(); - } - - //clear - if (this.clear_background) { - ctx.clearRect(0, 0, canvas.width, canvas.height); - } - - if (this._graph_stack && this._graph_stack.length) { - ctx.save(); - var parent_graph = this._graph_stack[this._graph_stack.length - 1]; - var subgraph_node = this.graph._subgraph_node; - ctx.strokeStyle = subgraph_node.bgcolor; - ctx.lineWidth = 10; - ctx.strokeRect(1, 1, canvas.width - 2, canvas.height - 2); - ctx.lineWidth = 1; - ctx.font = "40px Arial"; - ctx.textAlign = "center"; - ctx.fillStyle = subgraph_node.bgcolor || "#AAA"; - var title = ""; - for (var i = 1; i < this._graph_stack.length; ++i) { - title += - this._graph_stack[i]._subgraph_node.getTitle() + " >> "; - } - ctx.fillText( - title + subgraph_node.getTitle(), - canvas.width * 0.5, - 40 - ); - ctx.restore(); - } - - var bg_already_painted = false; - if (this.onRenderBackground) { - bg_already_painted = this.onRenderBackground(canvas, ctx); - } - - //reset in case of error - ctx.restore(); - ctx.setTransform(1, 0, 0, 1, 0, 0); - this.visible_links.length = 0; - - if (this.graph) { - //apply transformations - ctx.save(); - this.ds.toCanvasContext(ctx); - - //render BG - if ( - this.background_image && - this.ds.scale > 0.5 && - !bg_already_painted - ) { - if (this.zoom_modify_alpha) { - ctx.globalAlpha = - (1.0 - 0.5 / this.ds.scale) * this.editor_alpha; - } else { - ctx.globalAlpha = this.editor_alpha; - } - ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.imageSmoothingEnabled = false; - if ( - !this._bg_img || - this._bg_img.name != this.background_image - ) { - this._bg_img = new Image(); - this._bg_img.name = this.background_image; - this._bg_img.src = this.background_image; - var that = this; - this._bg_img.onload = function() { - that.draw(true, true); - }; - } - - var pattern = null; - if (this._pattern == null && this._bg_img.width > 0) { - pattern = ctx.createPattern(this._bg_img, "repeat"); - this._pattern_img = this._bg_img; - this._pattern = pattern; - } else { - pattern = this._pattern; - } - if (pattern) { - ctx.fillStyle = pattern; - ctx.fillRect( - this.visible_area[0], - this.visible_area[1], - this.visible_area[2], - this.visible_area[3] - ); - ctx.fillStyle = "transparent"; - } - - ctx.globalAlpha = 1.0; - ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.imageSmoothingEnabled = true; - } - - //groups - if (this.graph._groups.length && !this.live_mode) { - this.drawGroups(canvas, ctx); - } - - if (this.onDrawBackground) { - this.onDrawBackground(ctx, this.visible_area); - } - if (this.onBackgroundRender) { - //LEGACY - console.error( - "WARNING! onBackgroundRender deprecated, now is named onDrawBackground " - ); - this.onBackgroundRender = null; - } - - //DEBUG: show clipping area - //ctx.fillStyle = "red"; - //ctx.fillRect( this.visible_area[0] + 10, this.visible_area[1] + 10, this.visible_area[2] - 20, this.visible_area[3] - 20); - - //bg - if (this.render_canvas_border) { - ctx.strokeStyle = "#235"; - ctx.strokeRect(0, 0, canvas.width, canvas.height); - } - - if (this.render_connections_shadows) { - ctx.shadowColor = "#000"; - ctx.shadowOffsetX = 0; - ctx.shadowOffsetY = 0; - ctx.shadowBlur = 6; - } else { - ctx.shadowColor = "rgba(0,0,0,0)"; - } - - //draw connections - if (!this.live_mode) { - this.drawConnections(ctx); - } - - ctx.shadowColor = "rgba(0,0,0,0)"; - - //restore state - ctx.restore(); - } - - if (ctx.finish) { - ctx.finish(); - } - - this.dirty_bgcanvas = false; - this.dirty_canvas = true; //to force to repaint the front canvas with the bgcanvas - }; - - var temp_vec2 = new Float32Array(2); - - /** - * draws the given node inside the canvas - * @method drawNode - **/ - LGraphCanvas.prototype.drawNode = function(node, ctx) { - var glow = false; - this.current_node = node; - - var color = - node.color || - node.constructor.color || - LiteGraph.NODE_DEFAULT_COLOR; - var bgcolor = - node.bgcolor || - node.constructor.bgcolor || - LiteGraph.NODE_DEFAULT_BGCOLOR; - - //shadow and glow - if (node.mouseOver) { - glow = true; - } - - var low_quality = this.ds.scale < 0.6; //zoomed out - - //only render if it forces it to do it - if (this.live_mode) { - if (!node.flags.collapsed) { - ctx.shadowColor = "transparent"; - if (node.onDrawForeground) { - node.onDrawForeground(ctx, this, this.canvas); - } - } - return; - } - - var editor_alpha = this.editor_alpha; - ctx.globalAlpha = editor_alpha; - - if (this.render_shadows && !low_quality) { - ctx.shadowColor = LiteGraph.DEFAULT_SHADOW_COLOR; - ctx.shadowOffsetX = 2 * this.ds.scale; - ctx.shadowOffsetY = 2 * this.ds.scale; - ctx.shadowBlur = 3 * this.ds.scale; - } else { - ctx.shadowColor = "transparent"; - } - - //custom draw collapsed method (draw after shadows because they are affected) - if ( - node.flags.collapsed && - node.onDrawCollapsed && - node.onDrawCollapsed(ctx, this) == true - ) { - return; - } - - //clip if required (mask) - var shape = node._shape || LiteGraph.BOX_SHAPE; - var size = temp_vec2; - temp_vec2.set(node.size); - var horizontal = node.horizontal; // || node.flags.horizontal; - - if (node.flags.collapsed) { - ctx.font = this.inner_text_font; - var title = node.getTitle ? node.getTitle() : node.title; - if (title != null) { - node._collapsed_width = Math.min( - node.size[0], - ctx.measureText(title).width + - LiteGraph.NODE_TITLE_HEIGHT * 2 - ); //LiteGraph.NODE_COLLAPSED_WIDTH; - size[0] = node._collapsed_width; - size[1] = 0; - } - } - - if (node.clip_area) { - //Start clipping - ctx.save(); - ctx.beginPath(); - if (shape == LiteGraph.BOX_SHAPE) { - ctx.rect(0, 0, size[0], size[1]); - } else if (shape == LiteGraph.ROUND_SHAPE) { - ctx.roundRect(0, 0, size[0], size[1], 10); - } else if (shape == LiteGraph.CIRCLE_SHAPE) { - ctx.arc( - size[0] * 0.5, - size[1] * 0.5, - size[0] * 0.5, - 0, - Math.PI * 2 - ); - } - ctx.clip(); - } - - //draw shape - if (node.has_errors) { - bgcolor = "red"; - } - this.drawNodeShape( - node, - ctx, - size, - color, - bgcolor, - node.is_selected, - node.mouseOver - ); - ctx.shadowColor = "transparent"; - - //draw foreground - if (node.onDrawForeground) { - node.onDrawForeground(ctx, this, this.canvas); - } - - //connection slots - ctx.textAlign = horizontal ? "center" : "left"; - ctx.font = this.inner_text_font; - - var render_text = !low_quality; - - var out_slot = this.connecting_output; - ctx.lineWidth = 1; - - var max_y = 0; - var slot_pos = new Float32Array(2); //to reuse - - //render inputs and outputs - if (!node.flags.collapsed) { - //input connection slots - if (node.inputs) { - for (var i = 0; i < node.inputs.length; i++) { - var slot = node.inputs[i]; - - ctx.globalAlpha = editor_alpha; - //change opacity of incompatible slots when dragging a connection - if ( this.connecting_node && !LiteGraph.isValidConnection( slot.type , out_slot.type) ) { - ctx.globalAlpha = 0.4 * editor_alpha; - } - - ctx.fillStyle = - slot.link != null - ? slot.color_on || - this.default_connection_color.input_on - : slot.color_off || - this.default_connection_color.input_off; - - var pos = node.getConnectionPos(true, i, slot_pos); - pos[0] -= node.pos[0]; - pos[1] -= node.pos[1]; - if (max_y < pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5) { - max_y = pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5; - } - - ctx.beginPath(); - - if ( - slot.type === LiteGraph.EVENT || - slot.shape === LiteGraph.BOX_SHAPE - ) { - if (horizontal) { - ctx.rect( - pos[0] - 5 + 0.5, - pos[1] - 8 + 0.5, - 10, - 14 - ); - } else { - ctx.rect( - pos[0] - 6 + 0.5, - pos[1] - 5 + 0.5, - 14, - 10 - ); - } - } else if (slot.shape === LiteGraph.ARROW_SHAPE) { - ctx.moveTo(pos[0] + 8, pos[1] + 0.5); - ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5); - ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5); - ctx.closePath(); - } else { - if(low_quality) - ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); //faster - else - ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); - } - ctx.fill(); - - //render name - if (render_text) { - var text = slot.label != null ? slot.label : slot.name; - if (text) { - ctx.fillStyle = LiteGraph.NODE_TEXT_COLOR; - if (horizontal || slot.dir == LiteGraph.UP) { - ctx.fillText(text, pos[0], pos[1] - 10); - } else { - ctx.fillText(text, pos[0] + 10, pos[1] + 5); - } - } - } - } - } - - //output connection slots - if (this.connecting_node) { - ctx.globalAlpha = 0.4 * editor_alpha; - } - - ctx.textAlign = horizontal ? "center" : "right"; - ctx.strokeStyle = "black"; - if (node.outputs) { - for (var i = 0; i < node.outputs.length; i++) { - var slot = node.outputs[i]; - - var pos = node.getConnectionPos(false, i, slot_pos); - pos[0] -= node.pos[0]; - pos[1] -= node.pos[1]; - if (max_y < pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5) { - max_y = pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5; - } - - ctx.fillStyle = - slot.links && slot.links.length - ? slot.color_on || - this.default_connection_color.output_on - : slot.color_off || - this.default_connection_color.output_off; - ctx.beginPath(); - //ctx.rect( node.size[0] - 14,i*14,10,10); - - if ( - slot.type === LiteGraph.EVENT || - slot.shape === LiteGraph.BOX_SHAPE - ) { - if (horizontal) { - ctx.rect( - pos[0] - 5 + 0.5, - pos[1] - 8 + 0.5, - 10, - 14 - ); - } else { - ctx.rect( - pos[0] - 6 + 0.5, - pos[1] - 5 + 0.5, - 14, - 10 - ); - } - } else if (slot.shape === LiteGraph.ARROW_SHAPE) { - ctx.moveTo(pos[0] + 8, pos[1] + 0.5); - ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5); - ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5); - ctx.closePath(); - } else { - if(low_quality) - ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); - else - ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); - } - - //trigger - //if(slot.node_id != null && slot.slot == -1) - // ctx.fillStyle = "#F85"; - - //if(slot.links != null && slot.links.length) - ctx.fill(); - if(!low_quality) - ctx.stroke(); - - //render output name - if (render_text) { - var text = slot.label != null ? slot.label : slot.name; - if (text) { - ctx.fillStyle = LiteGraph.NODE_TEXT_COLOR; - if (horizontal || slot.dir == LiteGraph.DOWN) { - ctx.fillText(text, pos[0], pos[1] - 8); - } else { - ctx.fillText(text, pos[0] - 10, pos[1] + 5); - } - } - } - } - } - - ctx.textAlign = "left"; - ctx.globalAlpha = 1; - - if (node.widgets) { - var widgets_y = max_y; - if (horizontal || node.widgets_up) { - widgets_y = 2; - } - if( node.widgets_start_y != null ) - widgets_y = node.widgets_start_y; - this.drawNodeWidgets( - node, - widgets_y, - ctx, - this.node_widget && this.node_widget[0] == node - ? this.node_widget[1] - : null - ); - } - } else if (this.render_collapsed_slots) { - //if collapsed - var input_slot = null; - var output_slot = null; - - //get first connected slot to render - if (node.inputs) { - for (var i = 0; i < node.inputs.length; i++) { - var slot = node.inputs[i]; - if (slot.link == null) { - continue; - } - input_slot = slot; - break; - } - } - if (node.outputs) { - for (var i = 0; i < node.outputs.length; i++) { - var slot = node.outputs[i]; - if (!slot.links || !slot.links.length) { - continue; - } - output_slot = slot; - } - } - - if (input_slot) { - var x = 0; - var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; //center - if (horizontal) { - x = node._collapsed_width * 0.5; - y = -LiteGraph.NODE_TITLE_HEIGHT; - } - ctx.fillStyle = "#686"; - ctx.beginPath(); - if ( - slot.type === LiteGraph.EVENT || - slot.shape === LiteGraph.BOX_SHAPE - ) { - ctx.rect(x - 7 + 0.5, y - 4, 14, 8); - } else if (slot.shape === LiteGraph.ARROW_SHAPE) { - ctx.moveTo(x + 8, y); - ctx.lineTo(x + -4, y - 4); - ctx.lineTo(x + -4, y + 4); - ctx.closePath(); - } else { - ctx.arc(x, y, 4, 0, Math.PI * 2); - } - ctx.fill(); - } - - if (output_slot) { - var x = node._collapsed_width; - var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; //center - if (horizontal) { - x = node._collapsed_width * 0.5; - y = 0; - } - ctx.fillStyle = "#686"; - ctx.strokeStyle = "black"; - ctx.beginPath(); - if ( - slot.type === LiteGraph.EVENT || - slot.shape === LiteGraph.BOX_SHAPE - ) { - ctx.rect(x - 7 + 0.5, y - 4, 14, 8); - } else if (slot.shape === LiteGraph.ARROW_SHAPE) { - ctx.moveTo(x + 6, y); - ctx.lineTo(x - 6, y - 4); - ctx.lineTo(x - 6, y + 4); - ctx.closePath(); - } else { - ctx.arc(x, y, 4, 0, Math.PI * 2); - } - ctx.fill(); - //ctx.stroke(); - } - } - - if (node.clip_area) { - ctx.restore(); - } - - ctx.globalAlpha = 1.0; - }; - - //used by this.over_link_center - LGraphCanvas.prototype.drawLinkTooltip = function( ctx, link ) - { - var pos = link._pos; - ctx.fillStyle = "black"; - ctx.beginPath(); - ctx.arc( pos[0], pos[1], 3, 0, Math.PI * 2 ); - ctx.fill(); - - if(link.data == null) - return; - - if(this.onDrawLinkTooltip) - if( this.onDrawLinkTooltip(ctx,link,this) == true ) - return; - - var data = link.data; - var text = null; - - if( data.constructor === Number ) - text = data.toFixed(2); - else if( data.constructor === String ) - text = "\"" + data + "\""; - else if( data.constructor === Boolean ) - text = String(data); - else if (data.toToolTip) - text = data.toToolTip(); - else - text = "[" + data.constructor.name + "]"; - - if(text == null) - return; - text = text.substr(0,30); //avoid weird - - ctx.font = "14px Courier New"; - var info = ctx.measureText(text); - var w = info.width + 20; - var h = 24; - ctx.shadowColor = "black"; - ctx.shadowOffsetX = 2; - ctx.shadowOffsetY = 2; - ctx.shadowBlur = 3; - ctx.fillStyle = "#454"; - ctx.beginPath(); - ctx.roundRect( pos[0] - w*0.5, pos[1] - 15 - h, w, h,3, 3); - ctx.moveTo( pos[0] - 10, pos[1] - 15 ); - ctx.lineTo( pos[0] + 10, pos[1] - 15 ); - ctx.lineTo( pos[0], pos[1] - 5 ); - ctx.fill(); - ctx.shadowColor = "transparent"; - ctx.textAlign = "center"; - ctx.fillStyle = "#CEC"; - ctx.fillText(text, pos[0], pos[1] - 15 - h * 0.3); - } - - /** - * draws the shape of the given node in the canvas - * @method drawNodeShape - **/ - var tmp_area = new Float32Array(4); - - LGraphCanvas.prototype.drawNodeShape = function( - node, - ctx, - size, - fgcolor, - bgcolor, - selected, - mouse_over - ) { - //bg rect - ctx.strokeStyle = fgcolor; - ctx.fillStyle = bgcolor; - - var title_height = LiteGraph.NODE_TITLE_HEIGHT; - var low_quality = this.ds.scale < 0.5; - - //render node area depending on shape - var shape = - node._shape || node.constructor.shape || LiteGraph.ROUND_SHAPE; - - var title_mode = node.constructor.title_mode; - - var render_title = true; - if (title_mode == LiteGraph.TRANSPARENT_TITLE) { - render_title = false; - } else if (title_mode == LiteGraph.AUTOHIDE_TITLE && mouse_over) { - render_title = true; - } - - var area = tmp_area; - area[0] = 0; //x - area[1] = render_title ? -title_height : 0; //y - area[2] = size[0] + 1; //w - area[3] = render_title ? size[1] + title_height : size[1]; //h - - var old_alpha = ctx.globalAlpha; - - //full node shape - //if(node.flags.collapsed) - { - ctx.beginPath(); - if (shape == LiteGraph.BOX_SHAPE || low_quality) { - ctx.fillRect(area[0], area[1], area[2], area[3]); - } else if ( - shape == LiteGraph.ROUND_SHAPE || - shape == LiteGraph.CARD_SHAPE - ) { - ctx.roundRect( - area[0], - area[1], - area[2], - area[3], - this.round_radius, - shape == LiteGraph.CARD_SHAPE ? 0 : this.round_radius - ); - } else if (shape == LiteGraph.CIRCLE_SHAPE) { - ctx.arc( - size[0] * 0.5, - size[1] * 0.5, - size[0] * 0.5, - 0, - Math.PI * 2 - ); - } - ctx.fill(); - - //separator - if(!node.flags.collapsed) - { - ctx.shadowColor = "transparent"; - ctx.fillStyle = "rgba(0,0,0,0.2)"; - ctx.fillRect(0, -1, area[2], 2); - } - } - ctx.shadowColor = "transparent"; - - if (node.onDrawBackground) { - node.onDrawBackground(ctx, this, this.canvas); - } - - //title bg (remember, it is rendered ABOVE the node) - if (render_title || title_mode == LiteGraph.TRANSPARENT_TITLE) { - //title bar - if (node.onDrawTitleBar) { - node.onDrawTitleBar( - ctx, - title_height, - size, - this.ds.scale, - fgcolor - ); - } else if ( - title_mode != LiteGraph.TRANSPARENT_TITLE && - (node.constructor.title_color || this.render_title_colored) - ) { - var title_color = node.constructor.title_color || fgcolor; - - if (node.flags.collapsed) { - ctx.shadowColor = LiteGraph.DEFAULT_SHADOW_COLOR; - } - - //* gradient test - if (this.use_gradients) { - var grad = LGraphCanvas.gradients[title_color]; - if (!grad) { - grad = LGraphCanvas.gradients[ title_color ] = ctx.createLinearGradient(0, 0, 400, 0); - grad.addColorStop(0, title_color); - grad.addColorStop(1, "#000"); - } - ctx.fillStyle = grad; - } else { - ctx.fillStyle = title_color; - } - - //ctx.globalAlpha = 0.5 * old_alpha; - ctx.beginPath(); - if (shape == LiteGraph.BOX_SHAPE || low_quality) { - ctx.rect(0, -title_height, size[0] + 1, title_height); - } else if ( shape == LiteGraph.ROUND_SHAPE || shape == LiteGraph.CARD_SHAPE ) { - ctx.roundRect( - 0, - -title_height, - size[0] + 1, - title_height, - this.round_radius, - node.flags.collapsed ? this.round_radius : 0 - ); - } - ctx.fill(); - ctx.shadowColor = "transparent"; - } - - //title box - var box_size = 10; - if (node.onDrawTitleBox) { - node.onDrawTitleBox(ctx, title_height, size, this.ds.scale); - } else if ( - shape == LiteGraph.ROUND_SHAPE || - shape == LiteGraph.CIRCLE_SHAPE || - shape == LiteGraph.CARD_SHAPE - ) { - if (low_quality) { - ctx.fillStyle = "black"; - ctx.beginPath(); - ctx.arc( - title_height * 0.5, - title_height * -0.5, - box_size * 0.5 + 1, - 0, - Math.PI * 2 - ); - ctx.fill(); - } - - ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR; - if(low_quality) - ctx.fillRect( title_height * 0.5 - box_size *0.5, title_height * -0.5 - box_size *0.5, box_size , box_size ); - else - { - ctx.beginPath(); - ctx.arc( - title_height * 0.5, - title_height * -0.5, - box_size * 0.5, - 0, - Math.PI * 2 - ); - ctx.fill(); - } - } else { - if (low_quality) { - ctx.fillStyle = "black"; - ctx.fillRect( - (title_height - box_size) * 0.5 - 1, - (title_height + box_size) * -0.5 - 1, - box_size + 2, - box_size + 2 - ); - } - ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR; - ctx.fillRect( - (title_height - box_size) * 0.5, - (title_height + box_size) * -0.5, - box_size, - box_size - ); - } - ctx.globalAlpha = old_alpha; - - //title text - if (node.onDrawTitleText) { - node.onDrawTitleText( - ctx, - title_height, - size, - this.ds.scale, - this.title_text_font, - selected - ); - } - if (!low_quality) { - ctx.font = this.title_text_font; - var title = String(node.getTitle()); - if (title) { - if (selected) { - ctx.fillStyle = "white"; - } else { - ctx.fillStyle = - node.constructor.title_text_color || - this.node_title_color; - } - if (node.flags.collapsed) { - ctx.textAlign = "left"; - var measure = ctx.measureText(title); - ctx.fillText( - title.substr(0,20), //avoid urls too long - title_height,// + measure.width * 0.5, - LiteGraph.NODE_TITLE_TEXT_Y - title_height - ); - ctx.textAlign = "left"; - } else { - ctx.textAlign = "left"; - ctx.fillText( - title, - title_height, - LiteGraph.NODE_TITLE_TEXT_Y - title_height - ); - } - } - } - - if (node.onDrawTitle) { - node.onDrawTitle(ctx); - } - } - - //render selection marker - if (selected) { - if (node.onBounding) { - node.onBounding(area); - } - - if (title_mode == LiteGraph.TRANSPARENT_TITLE) { - area[1] -= title_height; - area[3] += title_height; - } - ctx.lineWidth = 1; - ctx.globalAlpha = 0.8; - ctx.beginPath(); - if (shape == LiteGraph.BOX_SHAPE) { - ctx.rect( - -6 + area[0], - -6 + area[1], - 12 + area[2], - 12 + area[3] - ); - } else if ( - shape == LiteGraph.ROUND_SHAPE || - (shape == LiteGraph.CARD_SHAPE && node.flags.collapsed) - ) { - ctx.roundRect( - -6 + area[0], - -6 + area[1], - 12 + area[2], - 12 + area[3], - this.round_radius * 2 - ); - } else if (shape == LiteGraph.CARD_SHAPE) { - ctx.roundRect( - -6 + area[0], - -6 + area[1], - 12 + area[2], - 12 + area[3], - this.round_radius * 2, - 2 - ); - } else if (shape == LiteGraph.CIRCLE_SHAPE) { - ctx.arc( - size[0] * 0.5, - size[1] * 0.5, - size[0] * 0.5 + 6, - 0, - Math.PI * 2 - ); - } - ctx.strokeStyle = "#FFF"; - ctx.stroke(); - ctx.strokeStyle = fgcolor; - ctx.globalAlpha = 1; - } - }; - - var margin_area = new Float32Array(4); - var link_bounding = new Float32Array(4); - var tempA = new Float32Array(2); - var tempB = new Float32Array(2); - - /** - * draws every connection visible in the canvas - * OPTIMIZE THIS: pre-catch connections position instead of recomputing them every time - * @method drawConnections - **/ - LGraphCanvas.prototype.drawConnections = function(ctx) { - var now = LiteGraph.getTime(); - var visible_area = this.visible_area; - margin_area[0] = visible_area[0] - 20; - margin_area[1] = visible_area[1] - 20; - margin_area[2] = visible_area[2] + 40; - margin_area[3] = visible_area[3] + 40; - - //draw connections - ctx.lineWidth = this.connections_width; - - ctx.fillStyle = "#AAA"; - ctx.strokeStyle = "#AAA"; - ctx.globalAlpha = this.editor_alpha; - //for every node - var nodes = this.graph._nodes; - for (var n = 0, l = nodes.length; n < l; ++n) { - var node = nodes[n]; - //for every input (we render just inputs because it is easier as every slot can only have one input) - if (!node.inputs || !node.inputs.length) { - continue; - } - - for (var i = 0; i < node.inputs.length; ++i) { - var input = node.inputs[i]; - if (!input || input.link == null) { - continue; - } - var link_id = input.link; - var link = this.graph.links[link_id]; - if (!link) { - continue; - } - - //find link info - var start_node = this.graph.getNodeById(link.origin_id); - if (start_node == null) { - continue; - } - var start_node_slot = link.origin_slot; - var start_node_slotpos = null; - if (start_node_slot == -1) { - start_node_slotpos = [ - start_node.pos[0] + 10, - start_node.pos[1] + 10 - ]; - } else { - start_node_slotpos = start_node.getConnectionPos( - false, - start_node_slot, - tempA - ); - } - var end_node_slotpos = node.getConnectionPos(true, i, tempB); - - //compute link bounding - link_bounding[0] = start_node_slotpos[0]; - link_bounding[1] = start_node_slotpos[1]; - link_bounding[2] = end_node_slotpos[0] - start_node_slotpos[0]; - link_bounding[3] = end_node_slotpos[1] - start_node_slotpos[1]; - if (link_bounding[2] < 0) { - link_bounding[0] += link_bounding[2]; - link_bounding[2] = Math.abs(link_bounding[2]); - } - if (link_bounding[3] < 0) { - link_bounding[1] += link_bounding[3]; - link_bounding[3] = Math.abs(link_bounding[3]); - } - - //skip links outside of the visible area of the canvas - if (!overlapBounding(link_bounding, margin_area)) { - continue; - } - - var start_slot = start_node.outputs[start_node_slot]; - var end_slot = node.inputs[i]; - if (!start_slot || !end_slot) { - continue; - } - var start_dir = - start_slot.dir || - (start_node.horizontal ? LiteGraph.DOWN : LiteGraph.RIGHT); - var end_dir = - end_slot.dir || - (node.horizontal ? LiteGraph.UP : LiteGraph.LEFT); - - this.renderLink( - ctx, - start_node_slotpos, - end_node_slotpos, - link, - false, - 0, - null, - start_dir, - end_dir - ); - - //event triggered rendered on top - if (link && link._last_time && now - link._last_time < 1000) { - var f = 2.0 - (now - link._last_time) * 0.002; - var tmp = ctx.globalAlpha; - ctx.globalAlpha = tmp * f; - this.renderLink( - ctx, - start_node_slotpos, - end_node_slotpos, - link, - true, - f, - "white", - start_dir, - end_dir - ); - ctx.globalAlpha = tmp; - } - } - } - ctx.globalAlpha = 1; - }; - - /** - * draws a link between two points - * @method renderLink - * @param {vec2} a start pos - * @param {vec2} b end pos - * @param {Object} link the link object with all the link info - * @param {boolean} skip_border ignore the shadow of the link - * @param {boolean} flow show flow animation (for events) - * @param {string} color the color for the link - * @param {number} start_dir the direction enum - * @param {number} end_dir the direction enum - * @param {number} num_sublines number of sublines (useful to represent vec3 or rgb) - **/ - LGraphCanvas.prototype.renderLink = function( - ctx, - a, - b, - link, - skip_border, - flow, - color, - start_dir, - end_dir, - num_sublines - ) { - if (link) { - this.visible_links.push(link); - } - - //choose color - if (!color && link) { - color = link.color || LGraphCanvas.link_type_colors[link.type]; - } - if (!color) { - color = this.default_link_color; - } - if (link != null && this.highlighted_links[link.id]) { - color = "#FFF"; - } - - start_dir = start_dir || LiteGraph.RIGHT; - end_dir = end_dir || LiteGraph.LEFT; - - var dist = distance(a, b); - - if (this.render_connections_border && this.ds.scale > 0.6) { - ctx.lineWidth = this.connections_width + 4; - } - ctx.lineJoin = "round"; - num_sublines = num_sublines || 1; - if (num_sublines > 1) { - ctx.lineWidth = 0.5; - } - - //begin line shape - ctx.beginPath(); - for (var i = 0; i < num_sublines; i += 1) { - var offsety = (i - (num_sublines - 1) * 0.5) * 5; - - if (this.links_render_mode == LiteGraph.SPLINE_LINK) { - ctx.moveTo(a[0], a[1] + offsety); - var start_offset_x = 0; - var start_offset_y = 0; - var end_offset_x = 0; - var end_offset_y = 0; - switch (start_dir) { - case LiteGraph.LEFT: - start_offset_x = dist * -0.25; - break; - case LiteGraph.RIGHT: - start_offset_x = dist * 0.25; - break; - case LiteGraph.UP: - start_offset_y = dist * -0.25; - break; - case LiteGraph.DOWN: - start_offset_y = dist * 0.25; - break; - } - switch (end_dir) { - case LiteGraph.LEFT: - end_offset_x = dist * -0.25; - break; - case LiteGraph.RIGHT: - end_offset_x = dist * 0.25; - break; - case LiteGraph.UP: - end_offset_y = dist * -0.25; - break; - case LiteGraph.DOWN: - end_offset_y = dist * 0.25; - break; - } - ctx.bezierCurveTo( - a[0] + start_offset_x, - a[1] + start_offset_y + offsety, - b[0] + end_offset_x, - b[1] + end_offset_y + offsety, - b[0], - b[1] + offsety - ); - } else if (this.links_render_mode == LiteGraph.LINEAR_LINK) { - ctx.moveTo(a[0], a[1] + offsety); - var start_offset_x = 0; - var start_offset_y = 0; - var end_offset_x = 0; - var end_offset_y = 0; - switch (start_dir) { - case LiteGraph.LEFT: - start_offset_x = -1; - break; - case LiteGraph.RIGHT: - start_offset_x = 1; - break; - case LiteGraph.UP: - start_offset_y = -1; - break; - case LiteGraph.DOWN: - start_offset_y = 1; - break; - } - switch (end_dir) { - case LiteGraph.LEFT: - end_offset_x = -1; - break; - case LiteGraph.RIGHT: - end_offset_x = 1; - break; - case LiteGraph.UP: - end_offset_y = -1; - break; - case LiteGraph.DOWN: - end_offset_y = 1; - break; - } - var l = 15; - ctx.lineTo( - a[0] + start_offset_x * l, - a[1] + start_offset_y * l + offsety - ); - ctx.lineTo( - b[0] + end_offset_x * l, - b[1] + end_offset_y * l + offsety - ); - ctx.lineTo(b[0], b[1] + offsety); - } else if (this.links_render_mode == LiteGraph.STRAIGHT_LINK) { - ctx.moveTo(a[0], a[1]); - var start_x = a[0]; - var start_y = a[1]; - var end_x = b[0]; - var end_y = b[1]; - if (start_dir == LiteGraph.RIGHT) { - start_x += 10; - } else { - start_y += 10; - } - if (end_dir == LiteGraph.LEFT) { - end_x -= 10; - } else { - end_y -= 10; - } - ctx.lineTo(start_x, start_y); - ctx.lineTo((start_x + end_x) * 0.5, start_y); - ctx.lineTo((start_x + end_x) * 0.5, end_y); - ctx.lineTo(end_x, end_y); - ctx.lineTo(b[0], b[1]); - } else { - return; - } //unknown - } - - //rendering the outline of the connection can be a little bit slow - if ( - this.render_connections_border && - this.ds.scale > 0.6 && - !skip_border - ) { - ctx.strokeStyle = "rgba(0,0,0,0.5)"; - ctx.stroke(); - } - - ctx.lineWidth = this.connections_width; - ctx.fillStyle = ctx.strokeStyle = color; - ctx.stroke(); - //end line shape - - var pos = this.computeConnectionPoint(a, b, 0.5, start_dir, end_dir); - if (link && link._pos) { - link._pos[0] = pos[0]; - link._pos[1] = pos[1]; - } - - //render arrow in the middle - if ( - this.ds.scale >= 0.6 && - this.highquality_render && - end_dir != LiteGraph.CENTER - ) { - //render arrow - if (this.render_connection_arrows) { - //compute two points in the connection - var posA = this.computeConnectionPoint( - a, - b, - 0.25, - start_dir, - end_dir - ); - var posB = this.computeConnectionPoint( - a, - b, - 0.26, - start_dir, - end_dir - ); - var posC = this.computeConnectionPoint( - a, - b, - 0.75, - start_dir, - end_dir - ); - var posD = this.computeConnectionPoint( - a, - b, - 0.76, - start_dir, - end_dir - ); - - //compute the angle between them so the arrow points in the right direction - var angleA = 0; - var angleB = 0; - if (this.render_curved_connections) { - angleA = -Math.atan2(posB[0] - posA[0], posB[1] - posA[1]); - angleB = -Math.atan2(posD[0] - posC[0], posD[1] - posC[1]); - } else { - angleB = angleA = b[1] > a[1] ? 0 : Math.PI; - } - - //render arrow - ctx.save(); - ctx.translate(posA[0], posA[1]); - ctx.rotate(angleA); - ctx.beginPath(); - ctx.moveTo(-5, -3); - ctx.lineTo(0, +7); - ctx.lineTo(+5, -3); - ctx.fill(); - ctx.restore(); - ctx.save(); - ctx.translate(posC[0], posC[1]); - ctx.rotate(angleB); - ctx.beginPath(); - ctx.moveTo(-5, -3); - ctx.lineTo(0, +7); - ctx.lineTo(+5, -3); - ctx.fill(); - ctx.restore(); - } - - //circle - ctx.beginPath(); - ctx.arc(pos[0], pos[1], 5, 0, Math.PI * 2); - ctx.fill(); - } - - //render flowing points - if (flow) { - ctx.fillStyle = color; - for (var i = 0; i < 5; ++i) { - var f = (LiteGraph.getTime() * 0.001 + i * 0.2) % 1; - var pos = this.computeConnectionPoint( - a, - b, - f, - start_dir, - end_dir - ); - ctx.beginPath(); - ctx.arc(pos[0], pos[1], 5, 0, 2 * Math.PI); - ctx.fill(); - } - } - }; - - //returns the link center point based on curvature - LGraphCanvas.prototype.computeConnectionPoint = function( - a, - b, - t, - start_dir, - end_dir - ) { - start_dir = start_dir || LiteGraph.RIGHT; - end_dir = end_dir || LiteGraph.LEFT; - - var dist = distance(a, b); - var p0 = a; - var p1 = [a[0], a[1]]; - var p2 = [b[0], b[1]]; - var p3 = b; - - switch (start_dir) { - case LiteGraph.LEFT: - p1[0] += dist * -0.25; - break; - case LiteGraph.RIGHT: - p1[0] += dist * 0.25; - break; - case LiteGraph.UP: - p1[1] += dist * -0.25; - break; - case LiteGraph.DOWN: - p1[1] += dist * 0.25; - break; - } - switch (end_dir) { - case LiteGraph.LEFT: - p2[0] += dist * -0.25; - break; - case LiteGraph.RIGHT: - p2[0] += dist * 0.25; - break; - case LiteGraph.UP: - p2[1] += dist * -0.25; - break; - case LiteGraph.DOWN: - p2[1] += dist * 0.25; - break; - } - - var c1 = (1 - t) * (1 - t) * (1 - t); - var c2 = 3 * ((1 - t) * (1 - t)) * t; - var c3 = 3 * (1 - t) * (t * t); - var c4 = t * t * t; - - var x = c1 * p0[0] + c2 * p1[0] + c3 * p2[0] + c4 * p3[0]; - var y = c1 * p0[1] + c2 * p1[1] + c3 * p2[1] + c4 * p3[1]; - return [x, y]; - }; - - LGraphCanvas.prototype.drawExecutionOrder = function(ctx) { - ctx.shadowColor = "transparent"; - ctx.globalAlpha = 0.25; - - ctx.textAlign = "center"; - ctx.strokeStyle = "white"; - ctx.globalAlpha = 0.75; - - var visible_nodes = this.visible_nodes; - for (var i = 0; i < visible_nodes.length; ++i) { - var node = visible_nodes[i]; - ctx.fillStyle = "black"; - ctx.fillRect( - node.pos[0] - LiteGraph.NODE_TITLE_HEIGHT, - node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, - LiteGraph.NODE_TITLE_HEIGHT, - LiteGraph.NODE_TITLE_HEIGHT - ); - if (node.order == 0) { - ctx.strokeRect( - node.pos[0] - LiteGraph.NODE_TITLE_HEIGHT + 0.5, - node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5, - LiteGraph.NODE_TITLE_HEIGHT, - LiteGraph.NODE_TITLE_HEIGHT - ); - } - ctx.fillStyle = "#FFF"; - ctx.fillText( - node.order, - node.pos[0] + LiteGraph.NODE_TITLE_HEIGHT * -0.5, - node.pos[1] - 6 - ); - } - ctx.globalAlpha = 1; - }; - - /** - * draws the widgets stored inside a node - * @method drawNodeWidgets - **/ - LGraphCanvas.prototype.drawNodeWidgets = function( - node, - posY, - ctx, - active_widget - ) { - if (!node.widgets || !node.widgets.length) { - return 0; - } - var width = node.size[0]; - var widgets = node.widgets; - posY += 2; - var H = LiteGraph.NODE_WIDGET_HEIGHT; - var show_text = this.ds.scale > 0.5; - ctx.save(); - ctx.globalAlpha = this.editor_alpha; - var outline_color = LiteGraph.WIDGET_OUTLINE_COLOR; - var background_color = LiteGraph.WIDGET_BGCOLOR; - var text_color = LiteGraph.WIDGET_TEXT_COLOR; - var secondary_text_color = LiteGraph.WIDGET_SECONDARY_TEXT_COLOR; - var margin = 15; - - for (var i = 0; i < widgets.length; ++i) { - var w = widgets[i]; - var y = posY; - if (w.y) { - y = w.y; - } - w.last_y = y; - ctx.strokeStyle = outline_color; - ctx.fillStyle = "#222"; - ctx.textAlign = "left"; - if(w.disabled) - ctx.globalAlpha *= 0.5; - - switch (w.type) { - case "button": - if (w.clicked) { - ctx.fillStyle = "#AAA"; - w.clicked = false; - this.dirty_canvas = true; - } - ctx.fillRect(margin, y, width - margin * 2, H); - if(show_text) - ctx.strokeRect( margin, y, width - margin * 2, H ); - if (show_text) { - ctx.textAlign = "center"; - ctx.fillStyle = text_color; - ctx.fillText(w.name, width * 0.5, y + H * 0.7); - } - break; - case "toggle": - ctx.textAlign = "left"; - ctx.strokeStyle = outline_color; - ctx.fillStyle = background_color; - ctx.beginPath(); - if (show_text) - ctx.roundRect(margin, posY, width - margin * 2, H, H * 0.5); - else - ctx.rect(margin, posY, width - margin * 2, H ); - ctx.fill(); - if(show_text) - ctx.stroke(); - ctx.fillStyle = w.value ? "#89A" : "#333"; - ctx.beginPath(); - ctx.arc( width - margin * 2, y + H * 0.5, H * 0.36, 0, Math.PI * 2 ); - ctx.fill(); - if (show_text) { - ctx.fillStyle = secondary_text_color; - if (w.name != null) { - ctx.fillText(w.name, margin * 2, y + H * 0.7); - } - ctx.fillStyle = w.value ? text_color : secondary_text_color; - ctx.textAlign = "right"; - ctx.fillText( - w.value - ? w.options.on || "true" - : w.options.off || "false", - width - 40, - y + H * 0.7 - ); - } - break; - case "slider": - ctx.fillStyle = background_color; - ctx.fillRect(margin, y, width - margin * 2, H); - var range = w.options.max - w.options.min; - var nvalue = (w.value - w.options.min) / range; - ctx.fillStyle = active_widget == w ? "#89A" : "#678"; - ctx.fillRect(margin, y, nvalue * (width - margin * 2), H); - if(show_text) - ctx.strokeRect(margin, y, width - margin * 2, H); - if (w.marker) { - var marker_nvalue = (w.marker - w.options.min) / range; - ctx.fillStyle = "#AA9"; - ctx.fillRect( margin + marker_nvalue * (width - margin * 2), y, 2, H ); - } - if (show_text) { - ctx.textAlign = "center"; - ctx.fillStyle = text_color; - ctx.fillText( - w.name + " " + Number(w.value).toFixed(3), - width * 0.5, - y + H * 0.7 - ); - } - break; - case "number": - case "combo": - ctx.textAlign = "left"; - ctx.strokeStyle = outline_color; - ctx.fillStyle = background_color; - ctx.beginPath(); - if(show_text) - ctx.roundRect(margin, posY, width - margin * 2, H, H * 0.5); - else - ctx.rect(margin, posY, width - margin * 2, H ); - ctx.fill(); - if (show_text) { - ctx.stroke(); - ctx.fillStyle = text_color; - ctx.beginPath(); - ctx.moveTo(margin + 16, posY + 5); - ctx.lineTo(margin + 6, posY + H * 0.5); - ctx.lineTo(margin + 16, posY + H - 5); - ctx.fill(); - ctx.beginPath(); - ctx.moveTo(width - margin - 16, posY + 5); - ctx.lineTo(width - margin - 6, posY + H * 0.5); - ctx.lineTo(width - margin - 16, posY + H - 5); - ctx.fill(); - ctx.fillStyle = secondary_text_color; - ctx.fillText(w.name, margin * 2 + 5, y + H * 0.7); - ctx.fillStyle = text_color; - ctx.textAlign = "right"; - if (w.type == "number") { - ctx.fillText( - Number(w.value).toFixed( - w.options.precision !== undefined - ? w.options.precision - : 3 - ), - width - margin * 2 - 20, - y + H * 0.7 - ); - } else { - var v = w.value; - if( w.options.values ) - { - var values = w.options.values; - if( values.constructor === Function ) - values = values(); - if(values && values.constructor !== Array) - v = values[ w.value ]; - } - ctx.fillText( - v, - width - margin * 2 - 20, - y + H * 0.7 - ); - } - } - break; - case "string": - case "text": - ctx.textAlign = "left"; - ctx.strokeStyle = outline_color; - ctx.fillStyle = background_color; - ctx.beginPath(); - if (show_text) - ctx.roundRect(margin, posY, width - margin * 2, H, H * 0.5); - else - ctx.rect( margin, posY, width - margin * 2, H ); - ctx.fill(); - if (show_text) { - ctx.save(); - ctx.beginPath(); - ctx.rect(margin, posY, width - margin * 2, H); - ctx.clip(); - - ctx.stroke(); - ctx.fillStyle = secondary_text_color; - if (w.name != null) { - ctx.fillText(w.name, margin * 2, y + H * 0.7); - } - ctx.fillStyle = text_color; - ctx.textAlign = "right"; - ctx.fillText(String(w.value).substr(0,30), width - margin * 2, y + H * 0.7); //30 chars max - ctx.restore(); - } - break; - default: - if (w.draw) { - w.draw(ctx, node, w, y, H); - } - break; - } - posY += H + 4; - ctx.globalAlpha = this.editor_alpha; - - } - ctx.restore(); - ctx.textAlign = "left"; - }; - - /** - * process an event on widgets - * @method processNodeWidgets - **/ - LGraphCanvas.prototype.processNodeWidgets = function( - node, - pos, - event, - active_widget - ) { - if (!node.widgets || !node.widgets.length) { - return null; - } - - var x = pos[0] - node.pos[0]; - var y = pos[1] - node.pos[1]; - var width = node.size[0]; - var that = this; - var ref_window = this.getCanvasWindow(); - - for (var i = 0; i < node.widgets.length; ++i) { - var w = node.widgets[i]; - if(!w || w.disabled) - continue; - if ( w == active_widget || (x > 6 && x < width - 12 && y > w.last_y && y < w.last_y + LiteGraph.NODE_WIDGET_HEIGHT) ) { - //inside widget - switch (w.type) { - case "button": - if (event.type === "mousemove") { - break; - } - if (w.callback) { - setTimeout(function() { - w.callback(w, that, node, pos, event); - }, 20); - } - w.clicked = true; - this.dirty_canvas = true; - break; - case "slider": - var range = w.options.max - w.options.min; - var nvalue = Math.clamp((x - 10) / (width - 20), 0, 1); - w.value = - w.options.min + - (w.options.max - w.options.min) * nvalue; - if (w.callback) { - setTimeout(function() { - inner_value_change(w, w.value); - }, 20); - } - this.dirty_canvas = true; - break; - case "number": - case "combo": - var old_value = w.value; - if (event.type == "mousemove" && w.type == "number") { - w.value += event.deltaX * 0.1 * (w.options.step || 1); - if ( w.options.min != null && w.value < w.options.min ) { - w.value = w.options.min; - } - if ( w.options.max != null && w.value > w.options.max ) { - w.value = w.options.max; - } - } else if (event.type == "mousedown") { - var values = w.options.values; - if (values && values.constructor === Function) { - values = w.options.values(w, node); - } - var values_list = null; - - if( w.type != "number") - values_list = values.constructor === Array ? values : Object.keys(values); - - var delta = x < 40 ? -1 : x > width - 40 ? 1 : 0; - if (w.type == "number") { - w.value += delta * 0.1 * (w.options.step || 1); - if ( w.options.min != null && w.value < w.options.min ) { - w.value = w.options.min; - } - if ( w.options.max != null && w.value > w.options.max ) { - w.value = w.options.max; - } - } else if (delta) { //clicked in arrow, used for combos - var index = -1; - if(values.constructor === Object) - index = values_list.indexOf( String( w.value ) ) + delta; - else - index = values_list.indexOf( w.value ) + delta; - if (index >= values_list.length) { - index = values_list.length - 1; - } - if (index < 0) { - index = 0; - } - if( values.constructor === Array ) - w.value = values[index]; - else - w.value = index; - } else { //combo clicked - var text_values = values != values_list ? Object.values(values) : values; - var menu = new LiteGraph.ContextMenu(text_values, { - scale: Math.max(1, this.ds.scale), - event: event, - className: "dark", - callback: inner_clicked.bind(w) - }, - ref_window); - function inner_clicked(v, option, event) { - if(values != values_list) - v = text_values.indexOf(v); - this.value = v; - inner_value_change(this, v); - that.dirty_canvas = true; - return false; - } - } - } //end mousedown - else if(event.type == "mouseup" && w.type == "number") - { - var delta = x < 40 ? -1 : x > width - 40 ? 1 : 0; - if (event.click_time < 200 && delta == 0) { - this.prompt("Value",w.value,function(v) { - this.value = Number(v); - inner_value_change(this, this.value); - }.bind(w), - event); - } - } - - if( old_value != w.value ) - setTimeout( - function() { - inner_value_change(this, this.value); - }.bind(w), - 20 - ); - this.dirty_canvas = true; - break; - case "toggle": - if (event.type == "mousedown") { - w.value = !w.value; - setTimeout(function() { - inner_value_change(w, w.value); - }, 20); - } - break; - case "string": - case "text": - if (event.type == "mousedown") { - this.prompt("Value",w.value,function(v) { - this.value = v; - inner_value_change(this, v); - }.bind(w), - event); - } - break; - default: - if (w.mouse) { - w.mouse(ctx, event, [x, y], node); - } - break; - } //end switch - - return w; - } - } - - function inner_value_change(widget, value) { - widget.value = value; - if ( widget.options && widget.options.property && node.properties[widget.options.property] !== undefined ) { - node.setProperty( widget.options.property, value ); - } - if (widget.callback) { - widget.callback(widget.value, that, node, pos, event); - } - } - - return null; - }; - - /** - * draws every group area in the background - * @method drawGroups - **/ - LGraphCanvas.prototype.drawGroups = function(canvas, ctx) { - if (!this.graph) { - return; - } - - var groups = this.graph._groups; - - ctx.save(); - ctx.globalAlpha = 0.5 * this.editor_alpha; - - for (var i = 0; i < groups.length; ++i) { - var group = groups[i]; - - if (!overlapBounding(this.visible_area, group._bounding)) { - continue; - } //out of the visible area - - ctx.fillStyle = group.color || "#335"; - ctx.strokeStyle = group.color || "#335"; - var pos = group._pos; - var size = group._size; - ctx.globalAlpha = 0.25 * this.editor_alpha; - ctx.beginPath(); - ctx.rect(pos[0] + 0.5, pos[1] + 0.5, size[0], size[1]); - ctx.fill(); - ctx.globalAlpha = this.editor_alpha; - ctx.stroke(); - - ctx.beginPath(); - ctx.moveTo(pos[0] + size[0], pos[1] + size[1]); - ctx.lineTo(pos[0] + size[0] - 10, pos[1] + size[1]); - ctx.lineTo(pos[0] + size[0], pos[1] + size[1] - 10); - ctx.fill(); - - var font_size = - group.font_size || LiteGraph.DEFAULT_GROUP_FONT_SIZE; - ctx.font = font_size + "px Arial"; - ctx.fillText(group.title, pos[0] + 4, pos[1] + font_size); - } - - ctx.restore(); - }; - - LGraphCanvas.prototype.adjustNodesSize = function() { - var nodes = this.graph._nodes; - for (var i = 0; i < nodes.length; ++i) { - nodes[i].size = nodes[i].computeSize(); - } - this.setDirty(true, true); - }; - - /** - * resizes the canvas to a given size, if no size is passed, then it tries to fill the parentNode - * @method resize - **/ - LGraphCanvas.prototype.resize = function(width, height) { - if (!width && !height) { - var parent = this.canvas.parentNode; - width = parent.offsetWidth; - height = parent.offsetHeight; - } - - if (this.canvas.width == width && this.canvas.height == height) { - return; - } - - this.canvas.width = width; - this.canvas.height = height; - this.bgcanvas.width = this.canvas.width; - this.bgcanvas.height = this.canvas.height; - this.setDirty(true, true); - }; - - /** - * switches to live mode (node shapes are not rendered, only the content) - * this feature was designed when graphs where meant to create user interfaces - * @method switchLiveMode - **/ - LGraphCanvas.prototype.switchLiveMode = function(transition) { - if (!transition) { - this.live_mode = !this.live_mode; - this.dirty_canvas = true; - this.dirty_bgcanvas = true; - return; - } - - var self = this; - var delta = this.live_mode ? 1.1 : 0.9; - if (this.live_mode) { - this.live_mode = false; - this.editor_alpha = 0.1; - } - - var t = setInterval(function() { - self.editor_alpha *= delta; - self.dirty_canvas = true; - self.dirty_bgcanvas = true; - - if (delta < 1 && self.editor_alpha < 0.01) { - clearInterval(t); - if (delta < 1) { - self.live_mode = true; - } - } - if (delta > 1 && self.editor_alpha > 0.99) { - clearInterval(t); - self.editor_alpha = 1; - } - }, 1); - }; - - LGraphCanvas.prototype.onNodeSelectionChange = function(node) { - return; //disabled - }; - - LGraphCanvas.prototype.touchHandler = function(event) { - //alert("foo"); - var touches = event.changedTouches, - first = touches[0], - type = ""; - - switch (event.type) { - case "touchstart": - type = "mousedown"; - break; - case "touchmove": - type = "mousemove"; - break; - case "touchend": - type = "mouseup"; - break; - default: - return; - } - - //initMouseEvent(type, canBubble, cancelable, view, clickCount, - // screenX, screenY, clientX, clientY, ctrlKey, - // altKey, shiftKey, metaKey, button, relatedTarget); - - var window = this.getCanvasWindow(); - var document = window.document; - - var simulatedEvent = document.createEvent("MouseEvent"); - simulatedEvent.initMouseEvent( - type, - true, - true, - window, - 1, - first.screenX, - first.screenY, - first.clientX, - first.clientY, - false, - false, - false, - false, - 0 /*left*/, - null - ); - first.target.dispatchEvent(simulatedEvent); - event.preventDefault(); - }; - - /* CONTEXT MENU ********************/ - - LGraphCanvas.onGroupAdd = function(info, entry, mouse_event) { - var canvas = LGraphCanvas.active_canvas; - var ref_window = canvas.getCanvasWindow(); - - var group = new LiteGraph.LGraphGroup(); - group.pos = canvas.convertEventToCanvasOffset(mouse_event); - canvas.graph.add(group); - }; - - LGraphCanvas.onMenuAdd = function(node, options, e, prev_menu, callback) { - var canvas = LGraphCanvas.active_canvas; - var ref_window = canvas.getCanvasWindow(); - - var values = LiteGraph.getNodeTypesCategories( canvas.filter ); - var entries = []; - for (var i in values) { - if (values[i]) { - entries.push({ value: values[i], content: values[i], has_submenu: true }); - } - } - - //show categories - var menu = new LiteGraph.ContextMenu( entries, { event: e, callback: inner_clicked, parentMenu: prev_menu }, ref_window ); - - function inner_clicked(v, option, e) { - var category = v.value; - var node_types = LiteGraph.getNodeTypesInCategory( category, canvas.filter ); - var values = []; - for (var i in node_types) { - if (!node_types[i].skip_list) { - values.push({ - content: node_types[i].title, - value: node_types[i].type - }); - } - } - - new LiteGraph.ContextMenu( values, { event: e, callback: inner_create, parentMenu: menu }, ref_window ); - return false; - } - - function inner_create(v, e) { - var first_event = prev_menu.getFirstEvent(); - var node = LiteGraph.createNode(v.value); - if (node) { - node.pos = canvas.convertEventToCanvasOffset(first_event); - canvas.graph.add(node); - } - if(callback) - callback(node); - } - - return false; - }; - - LGraphCanvas.onMenuCollapseAll = function() {}; - - LGraphCanvas.onMenuNodeEdit = function() {}; - - LGraphCanvas.showMenuNodeOptionalInputs = function( - v, - options, - e, - prev_menu, - node - ) { - if (!node) { - return; - } - - var that = this; - var canvas = LGraphCanvas.active_canvas; - var ref_window = canvas.getCanvasWindow(); - - var options = node.optional_inputs; - if (node.onGetInputs) { - options = node.onGetInputs(); - } - - var entries = []; - if (options) { - for (var i in options) { - var entry = options[i]; - if (!entry) { - entries.push(null); - continue; - } - var label = entry[0]; - if (entry[2] && entry[2].label) { - label = entry[2].label; - } - var data = { content: label, value: entry }; - if (entry[1] == LiteGraph.ACTION) { - data.className = "event"; - } - entries.push(data); - } - } - - if (this.onMenuNodeInputs) { - entries = this.onMenuNodeInputs(entries); - } - - if (!entries.length) { - return; - } - - var menu = new LiteGraph.ContextMenu( - entries, - { - event: e, - callback: inner_clicked, - parentMenu: prev_menu, - node: node - }, - ref_window - ); - - function inner_clicked(v, e, prev) { - if (!node) { - return; - } - - if (v.callback) { - v.callback.call(that, node, v, e, prev); - } - - if (v.value) { - node.addInput(v.value[0], v.value[1], v.value[2]); - node.setDirtyCanvas(true, true); - } - } - - return false; - }; - - LGraphCanvas.showMenuNodeOptionalOutputs = function( - v, - options, - e, - prev_menu, - node - ) { - if (!node) { - return; - } - - var that = this; - var canvas = LGraphCanvas.active_canvas; - var ref_window = canvas.getCanvasWindow(); - - var options = node.optional_outputs; - if (node.onGetOutputs) { - options = node.onGetOutputs(); - } - - var entries = []; - if (options) { - for (var i in options) { - var entry = options[i]; - if (!entry) { - //separator? - entries.push(null); - continue; - } - - if ( - node.flags && - node.flags.skip_repeated_outputs && - node.findOutputSlot(entry[0]) != -1 - ) { - continue; - } //skip the ones already on - var label = entry[0]; - if (entry[2] && entry[2].label) { - label = entry[2].label; - } - var data = { content: label, value: entry }; - if (entry[1] == LiteGraph.EVENT) { - data.className = "event"; - } - entries.push(data); - } - } - - if (this.onMenuNodeOutputs) { - entries = this.onMenuNodeOutputs(entries); - } - - if (!entries.length) { - return; - } - - var menu = new LiteGraph.ContextMenu( - entries, - { - event: e, - callback: inner_clicked, - parentMenu: prev_menu, - node: node - }, - ref_window - ); - - function inner_clicked(v, e, prev) { - if (!node) { - return; - } - - if (v.callback) { - v.callback.call(that, node, v, e, prev); - } - - if (!v.value) { - return; - } - - var value = v.value[1]; - - if ( - value && - (value.constructor === Object || value.constructor === Array) - ) { - //submenu why? - var entries = []; - for (var i in value) { - entries.push({ content: i, value: value[i] }); - } - new LiteGraph.ContextMenu(entries, { - event: e, - callback: inner_clicked, - parentMenu: prev_menu, - node: node - }); - return false; - } else { - node.addOutput(v.value[0], v.value[1], v.value[2]); - node.setDirtyCanvas(true, true); - } - } - - return false; - }; - - LGraphCanvas.onShowMenuNodeProperties = function( - value, - options, - e, - prev_menu, - node - ) { - if (!node || !node.properties) { - return; - } - - var that = this; - var canvas = LGraphCanvas.active_canvas; - var ref_window = canvas.getCanvasWindow(); - - var entries = []; - for (var i in node.properties) { - var value = node.properties[i] !== undefined ? node.properties[i] : " "; - if( typeof value == "object" ) - value = JSON.stringify(value); - //value could contain invalid html characters, clean that - value = LGraphCanvas.decodeHTML(value); - entries.push({ - content: - "" + - i + - "" + - "" + - value + - "", - value: i - }); - } - if (!entries.length) { - return; - } - - var menu = new LiteGraph.ContextMenu( - entries, - { - event: e, - callback: inner_clicked, - parentMenu: prev_menu, - allow_html: true, - node: node - }, - ref_window - ); - - function inner_clicked(v, options, e, prev) { - if (!node) { - return; - } - var rect = this.getBoundingClientRect(); - canvas.showEditPropertyValue(node, v.value, { - position: [rect.left, rect.top] - }); - } - - return false; - }; - - LGraphCanvas.decodeHTML = function(str) { - var e = document.createElement("div"); - e.innerText = str; - return e.innerHTML; - }; - - LGraphCanvas.onResizeNode = function(value, options, e, menu, node) { - if (!node) { - return; - } - node.size = node.computeSize(); - node.setDirtyCanvas(true, true); - }; - - LGraphCanvas.prototype.showLinkMenu = function(link, e) { - var that = this; - console.log(link); - var options = ["Add Node",null,"Delete"]; - var menu = new LiteGraph.ContextMenu(options, { - event: e, - title: link.data != null ? link.data.constructor.name : null, - callback: inner_clicked - }); - - function inner_clicked(v,options,e) { - switch (v) { - case "Add Node": - LGraphCanvas.onMenuAdd(null, null, e, menu, function(node){ - console.log("node autoconnect"); - var node_left = that.graph.getNodeById( link.origin_id ); - var node_right = that.graph.getNodeById( link.target_id ); - if(!node.inputs || !node.inputs.length || !node.outputs || !node.outputs.length) - return; - if( node_left.outputs[ link.origin_slot ].type == node.inputs[0].type && node.outputs[0].type == node_right.inputs[0].type ) - { - node_left.connect( link.origin_slot, node, 0 ); - node.connect( 0, node_right, link.target_slot ); - node.pos[0] -= node.size[0] * 0.5; - } - }); - break; - case "Delete": - that.graph.removeLink(link.id); - break; - default: - } - } - - return false; - }; - - LGraphCanvas.onShowPropertyEditor = function(item, options, e, menu, node) { - var input_html = ""; - var property = item.property || "title"; - var value = node[property]; - - var dialog = document.createElement("div"); - dialog.className = "graphdialog"; - dialog.innerHTML = - ""; - var title = dialog.querySelector(".name"); - title.innerText = property; - var input = dialog.querySelector("input"); - if (input) { - input.value = value; - input.addEventListener("blur", function(e) { - this.focus(); - }); - input.addEventListener("keydown", function(e) { - if (e.keyCode != 13) { - return; - } - inner(); - e.preventDefault(); - e.stopPropagation(); - }); - } - - var graphcanvas = LGraphCanvas.active_canvas; - var canvas = graphcanvas.canvas; - - var rect = canvas.getBoundingClientRect(); - var offsetx = -20; - var offsety = -20; - if (rect) { - offsetx -= rect.left; - offsety -= rect.top; - } - - if (event) { - dialog.style.left = event.clientX + offsetx + "px"; - dialog.style.top = event.clientY + offsety + "px"; - } else { - dialog.style.left = canvas.width * 0.5 + offsetx + "px"; - dialog.style.top = canvas.height * 0.5 + offsety + "px"; - } - - var button = dialog.querySelector("button"); - button.addEventListener("click", inner); - canvas.parentNode.appendChild(dialog); - - function inner() { - setValue(input.value); - } - - function setValue(value) { - if (item.type == "Number") { - value = Number(value); - } else if (item.type == "Boolean") { - value = Boolean(value); - } - node[property] = value; - if (dialog.parentNode) { - dialog.parentNode.removeChild(dialog); - } - node.setDirtyCanvas(true, true); - } - }; - - LGraphCanvas.prototype.prompt = function(title, value, callback, event) { - var that = this; - var input_html = ""; - title = title || ""; - - var modified = false; - - var dialog = document.createElement("div"); - dialog.className = "graphdialog rounded"; - dialog.innerHTML = - " "; - dialog.close = function() { - that.prompt_box = null; - if (dialog.parentNode) { - dialog.parentNode.removeChild(dialog); - } - }; - - if (this.ds.scale > 1) { - dialog.style.transform = "scale(" + this.ds.scale + ")"; - } - - dialog.addEventListener("mouseleave", function(e) { - if (!modified) { - dialog.close(); - } - }); - - if (that.prompt_box) { - that.prompt_box.close(); - } - that.prompt_box = dialog; - - var first = null; - var timeout = null; - var selected = null; - - var name_element = dialog.querySelector(".name"); - name_element.innerText = title; - var value_element = dialog.querySelector(".value"); - value_element.value = value; - - var input = dialog.querySelector("input"); - input.addEventListener("keydown", function(e) { - modified = true; - if (e.keyCode == 27) { - //ESC - dialog.close(); - } else if (e.keyCode == 13) { - if (callback) { - callback(this.value); - } - dialog.close(); - } else { - return; - } - e.preventDefault(); - e.stopPropagation(); - }); - - var button = dialog.querySelector("button"); - button.addEventListener("click", function(e) { - if (callback) { - callback(input.value); - } - that.setDirty(true); - dialog.close(); - }); - - var graphcanvas = LGraphCanvas.active_canvas; - var canvas = graphcanvas.canvas; - - var rect = canvas.getBoundingClientRect(); - var offsetx = -20; - var offsety = -20; - if (rect) { - offsetx -= rect.left; - offsety -= rect.top; - } - - if (event) { - dialog.style.left = event.clientX + offsetx + "px"; - dialog.style.top = event.clientY + offsety + "px"; - } else { - dialog.style.left = canvas.width * 0.5 + offsetx + "px"; - dialog.style.top = canvas.height * 0.5 + offsety + "px"; - } - - canvas.parentNode.appendChild(dialog); - setTimeout(function() { - input.focus(); - }, 10); - - return dialog; - }; - - LGraphCanvas.search_limit = -1; - LGraphCanvas.prototype.showSearchBox = function(event) { - var that = this; - var input_html = ""; - var graphcanvas = LGraphCanvas.active_canvas; - var canvas = graphcanvas.canvas; - var root_document = canvas.ownerDocument || document; - - var dialog = document.createElement("div"); - dialog.className = "litegraph litesearchbox graphdialog rounded"; - dialog.innerHTML = - "Search
"; - dialog.close = function() { - that.search_box = null; - root_document.body.focus(); - root_document.body.style.overflow = ""; - - setTimeout(function() { - that.canvas.focus(); - }, 20); //important, if canvas loses focus keys wont be captured - if (dialog.parentNode) { - dialog.parentNode.removeChild(dialog); - } - }; - - var timeout_close = null; - - if (this.ds.scale > 1) { - dialog.style.transform = "scale(" + this.ds.scale + ")"; - } - - dialog.addEventListener("mouseenter", function(e) { - if (timeout_close) { - clearTimeout(timeout_close); - timeout_close = null; - } - }); - - dialog.addEventListener("mouseleave", function(e) { - //dialog.close(); - timeout_close = setTimeout(function() { - dialog.close(); - }, 500); - }); - - if (that.search_box) { - that.search_box.close(); - } - that.search_box = dialog; - - var helper = dialog.querySelector(".helper"); - - var first = null; - var timeout = null; - var selected = null; - - var input = dialog.querySelector("input"); - if (input) { - input.addEventListener("blur", function(e) { - this.focus(); - }); - input.addEventListener("keydown", function(e) { - if (e.keyCode == 38) { - //UP - changeSelection(false); - } else if (e.keyCode == 40) { - //DOWN - changeSelection(true); - } else if (e.keyCode == 27) { - //ESC - dialog.close(); - } else if (e.keyCode == 13) { - if (selected) { - select(selected.innerHTML); - } else if (first) { - select(first); - } else { - dialog.close(); - } - } else { - if (timeout) { - clearInterval(timeout); - } - timeout = setTimeout(refreshHelper, 10); - return; - } - e.preventDefault(); - e.stopPropagation(); - e.stopImmediatePropagation(); - return true; - }); - } - - if( root_document.fullscreenElement ) - root_document.fullscreenElement.appendChild(dialog); - else - { - root_document.body.appendChild(dialog); - root_document.body.style.overflow = "hidden"; - } - - //compute best position - var rect = canvas.getBoundingClientRect(); - - var left = ( event ? event.clientX : (rect.left + rect.width * 0.5) ) - 80; - var top = ( event ? event.clientY : (rect.top + rect.height * 0.5) ) - 20; - dialog.style.left = left + "px"; - dialog.style.top = top + "px"; - - //To avoid out of screen problems - if(event.layerY > (rect.height - 200)) - helper.style.maxHeight = (rect.height - event.layerY - 20) + "px"; - - /* - var offsetx = -20; - var offsety = -20; - if (rect) { - offsetx -= rect.left; - offsety -= rect.top; - } - - if (event) { - dialog.style.left = event.clientX + offsetx + "px"; - dialog.style.top = event.clientY + offsety + "px"; - } else { - dialog.style.left = canvas.width * 0.5 + offsetx + "px"; - dialog.style.top = canvas.height * 0.5 + offsety + "px"; - } - canvas.parentNode.appendChild(dialog); - */ - - input.focus(); - - function select(name) { - if (name) { - if (that.onSearchBoxSelection) { - that.onSearchBoxSelection(name, event, graphcanvas); - } else { - var extra = LiteGraph.searchbox_extras[name.toLowerCase()]; - if (extra) { - name = extra.type; - } - - var node = LiteGraph.createNode(name); - if (node) { - node.pos = graphcanvas.convertEventToCanvasOffset( - event - ); - graphcanvas.graph.add(node); - } - - if (extra && extra.data) { - if (extra.data.properties) { - for (var i in extra.data.properties) { - node.addProperty( i, extra.data.properties[i] ); - } - } - if (extra.data.inputs) { - node.inputs = []; - for (var i in extra.data.inputs) { - node.addOutput( - extra.data.inputs[i][0], - extra.data.inputs[i][1] - ); - } - } - if (extra.data.outputs) { - node.outputs = []; - for (var i in extra.data.outputs) { - node.addOutput( - extra.data.outputs[i][0], - extra.data.outputs[i][1] - ); - } - } - if (extra.data.title) { - node.title = extra.data.title; - } - if (extra.data.json) { - node.configure(extra.data.json); - } - } - } - } - - dialog.close(); - } - - function changeSelection(forward) { - var prev = selected; - if (selected) { - selected.classList.remove("selected"); - } - if (!selected) { - selected = forward - ? helper.childNodes[0] - : helper.childNodes[helper.childNodes.length]; - } else { - selected = forward - ? selected.nextSibling - : selected.previousSibling; - if (!selected) { - selected = prev; - } - } - if (!selected) { - return; - } - selected.classList.add("selected"); - selected.scrollIntoView({block: "end", behavior: "smooth"}); - } - - function refreshHelper() { - timeout = null; - var str = input.value; - first = null; - helper.innerHTML = ""; - if (!str) { - return; - } - - if (that.onSearchBox) { - var list = that.onSearchBox(helper, str, graphcanvas); - if (list) { - for (var i = 0; i < list.length; ++i) { - addResult(list[i]); - } - } - } else { - var c = 0; - str = str.toLowerCase(); - var filter = graphcanvas.filter || graphcanvas.graph.filter; - - //extras - for (var i in LiteGraph.searchbox_extras) { - var extra = LiteGraph.searchbox_extras[i]; - if (extra.desc.toLowerCase().indexOf(str) === -1) { - continue; - } - var ctor = LiteGraph.registered_node_types[ extra.type ]; - if( ctor && ctor.filter && ctor.filter != filter ) - continue; - addResult( extra.desc, "searchbox_extra" ); - if ( LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit ) { - break; - } - } - - var filtered = null; - if (Array.prototype.filter) { //filter supported - var keys = Object.keys( LiteGraph.registered_node_types ); //types - var filtered = keys.filter( inner_test_filter ); - } else { - filtered = []; - for (var i in LiteGraph.registered_node_types) { - if( inner_test_filter(i) ) - filtered.push(i); - } - } - - for (var i = 0; i < filtered.length; i++) { - addResult(filtered[i]); - if ( LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit ) { - break; - } - } - - function inner_test_filter( type ) - { - var ctor = LiteGraph.registered_node_types[ type ]; - if(filter && ctor.filter != filter ) - return false; - return type.toLowerCase().indexOf(str) !== -1; - } - } - - function addResult(type, className) { - var help = document.createElement("div"); - if (!first) { - first = type; - } - help.innerText = type; - help.dataset["type"] = escape(type); - help.className = "litegraph lite-search-item"; - if (className) { - help.className += " " + className; - } - help.addEventListener("click", function(e) { - select(unescape(this.dataset["type"])); - }); - helper.appendChild(help); - } - } - - return dialog; - }; - - LGraphCanvas.prototype.showEditPropertyValue = function( node, property, options ) { - if (!node || node.properties[property] === undefined) { - return; - } - - options = options || {}; - var that = this; - - var info = node.getPropertyInfo(property); - var type = info.type; - - var input_html = ""; - - if (type == "string" || type == "number" || type == "array" || type == "object") { - input_html = ""; - } else if (type == "enum" && info.values) { - input_html = ""; - } else if (type == "boolean") { - input_html = - ""; - } else { - console.warn("unknown type: " + type); - return; - } - - var dialog = this.createDialog( - "" + - property + - "" + - input_html + - "", - options - ); - - if (type == "enum" && info.values) { - var input = dialog.querySelector("select"); - input.addEventListener("change", function(e) { - setValue(e.target.value); - //var index = e.target.value; - //setValue( e.options[e.selectedIndex].value ); - }); - } else if (type == "boolean") { - var input = dialog.querySelector("input"); - if (input) { - input.addEventListener("click", function(e) { - setValue(!!input.checked); - }); - } - } else { - var input = dialog.querySelector("input"); - if (input) { - input.addEventListener("blur", function(e) { - this.focus(); - }); - var v = node.properties[property] !== undefined ? node.properties[property] : ""; - v = JSON.stringify(v); - input.value = v; - input.addEventListener("keydown", function(e) { - if (e.keyCode != 13) { - return; - } - inner(); - e.preventDefault(); - e.stopPropagation(); - }); - } - } - - var button = dialog.querySelector("button"); - button.addEventListener("click", inner); - - function inner() { - setValue(input.value); - } - - function setValue(value) { - if (typeof node.properties[property] == "number") { - value = Number(value); - } - if (type == "array" || type == "object") { - value = JSON.parse(value); - } - node.properties[property] = value; - if (node._graph) { - node._graph._version++; - } - if (node.onPropertyChanged) { - node.onPropertyChanged(property, value); - } - if(options.onclose) - options.onclose(); - dialog.close(); - node.setDirtyCanvas(true, true); - } - - return dialog; - }; - - LGraphCanvas.prototype.createDialog = function(html, options) { - options = options || {}; - - var dialog = document.createElement("div"); - dialog.className = "graphdialog"; - dialog.innerHTML = html; - - var rect = this.canvas.getBoundingClientRect(); - var offsetx = -20; - var offsety = -20; - if (rect) { - offsetx -= rect.left; - offsety -= rect.top; - } - - if (options.position) { - offsetx += options.position[0]; - offsety += options.position[1]; - } else if (options.event) { - offsetx += options.event.clientX; - offsety += options.event.clientY; - } //centered - else { - offsetx += this.canvas.width * 0.5; - offsety += this.canvas.height * 0.5; - } - - dialog.style.left = offsetx + "px"; - dialog.style.top = offsety + "px"; - - this.canvas.parentNode.appendChild(dialog); - - dialog.close = function() { - if (this.parentNode) { - this.parentNode.removeChild(this); - } - }; - - return dialog; - }; - - LGraphCanvas.onMenuNodeCollapse = function(value, options, e, menu, node) { - node.collapse(); - }; - - LGraphCanvas.onMenuNodePin = function(value, options, e, menu, node) { - node.pin(); - }; - - LGraphCanvas.onMenuNodeMode = function(value, options, e, menu, node) { - new LiteGraph.ContextMenu( - ["Always", "On Event", "On Trigger", "Never"], - { event: e, callback: inner_clicked, parentMenu: menu, node: node } - ); - - function inner_clicked(v) { - if (!node) { - return; - } - switch (v) { - case "On Event": - node.mode = LiteGraph.ON_EVENT; - break; - case "On Trigger": - node.mode = LiteGraph.ON_TRIGGER; - break; - case "Never": - node.mode = LiteGraph.NEVER; - break; - case "Always": - default: - node.mode = LiteGraph.ALWAYS; - break; - } - } - - return false; - }; - - LGraphCanvas.onMenuNodeColors = function(value, options, e, menu, node) { - if (!node) { - throw "no node for color"; - } - - var values = []; - values.push({ - value: null, - content: - "No color" - }); - - for (var i in LGraphCanvas.node_colors) { - var color = LGraphCanvas.node_colors[i]; - var value = { - value: i, - content: - "" + - i + - "" - }; - values.push(value); - } - new LiteGraph.ContextMenu(values, { - event: e, - callback: inner_clicked, - parentMenu: menu, - node: node - }); - - function inner_clicked(v) { - if (!node) { - return; - } - - var color = v.value ? LGraphCanvas.node_colors[v.value] : null; - if (color) { - if (node.constructor === LiteGraph.LGraphGroup) { - node.color = color.groupcolor; - } else { - node.color = color.color; - node.bgcolor = color.bgcolor; - } - } else { - delete node.color; - delete node.bgcolor; - } - node.setDirtyCanvas(true, true); - } - - return false; - }; - - LGraphCanvas.onMenuNodeShapes = function(value, options, e, menu, node) { - if (!node) { - throw "no node passed"; - } - - new LiteGraph.ContextMenu(LiteGraph.VALID_SHAPES, { - event: e, - callback: inner_clicked, - parentMenu: menu, - node: node - }); - - function inner_clicked(v) { - if (!node) { - return; - } - node.shape = v; - node.setDirtyCanvas(true); - } - - return false; - }; - - LGraphCanvas.onMenuNodeRemove = function(value, options, e, menu, node) { - if (!node) { - throw "no node passed"; - } - - if (node.removable === false) { - return; - } - - node.graph.remove(node); - node.setDirtyCanvas(true, true); - }; - - LGraphCanvas.onMenuNodeClone = function(value, options, e, menu, node) { - if (node.clonable == false) { - return; - } - var newnode = node.clone(); - if (!newnode) { - return; - } - newnode.pos = [node.pos[0] + 5, node.pos[1] + 5]; - node.graph.add(newnode); - node.setDirtyCanvas(true, true); - }; - - LGraphCanvas.node_colors = { - red: { color: "#322", bgcolor: "#533", groupcolor: "#A88" }, - brown: { color: "#332922", bgcolor: "#593930", groupcolor: "#b06634" }, - green: { color: "#232", bgcolor: "#353", groupcolor: "#8A8" }, - blue: { color: "#223", bgcolor: "#335", groupcolor: "#88A" }, - pale_blue: { - color: "#2a363b", - bgcolor: "#3f5159", - groupcolor: "#3f789e" - }, - cyan: { color: "#233", bgcolor: "#355", groupcolor: "#8AA" }, - purple: { color: "#323", bgcolor: "#535", groupcolor: "#a1309b" }, - yellow: { color: "#432", bgcolor: "#653", groupcolor: "#b58b2a" }, - black: { color: "#222", bgcolor: "#000", groupcolor: "#444" } - }; - - LGraphCanvas.prototype.getCanvasMenuOptions = function() { - var options = null; - if (this.getMenuOptions) { - options = this.getMenuOptions(); - } else { - options = [ - { - content: "Add Node", - has_submenu: true, - callback: LGraphCanvas.onMenuAdd - }, - { content: "Add Group", callback: LGraphCanvas.onGroupAdd } - //{content:"Collapse All", callback: LGraphCanvas.onMenuCollapseAll } - ]; - - if (this._graph_stack && this._graph_stack.length > 0) { - options.push(null, { - content: "Close subgraph", - callback: this.closeSubgraph.bind(this) - }); - } - } - - if (this.getExtraMenuOptions) { - var extra = this.getExtraMenuOptions(this, options); - if (extra) { - options = options.concat(extra); - } - } - - return options; - }; - - //called by processContextMenu to extract the menu list - LGraphCanvas.prototype.getNodeMenuOptions = function(node) { - var options = null; - - if (node.getMenuOptions) { - options = node.getMenuOptions(this); - } else { - options = [ - { - content: "Inputs", - has_submenu: true, - disabled: true, - callback: LGraphCanvas.showMenuNodeOptionalInputs - }, - { - content: "Outputs", - has_submenu: true, - disabled: true, - callback: LGraphCanvas.showMenuNodeOptionalOutputs - }, - null, - { - content: "Properties", - has_submenu: true, - callback: LGraphCanvas.onShowMenuNodeProperties - }, - null, - { - content: "Title", - callback: LGraphCanvas.onShowPropertyEditor - }, - { - content: "Mode", - has_submenu: true, - callback: LGraphCanvas.onMenuNodeMode - }, - { content: "Resize", callback: LGraphCanvas.onResizeNode }, - { - content: "Collapse", - callback: LGraphCanvas.onMenuNodeCollapse - }, - { content: "Pin", callback: LGraphCanvas.onMenuNodePin }, - { - content: "Colors", - has_submenu: true, - callback: LGraphCanvas.onMenuNodeColors - }, - { - content: "Shapes", - has_submenu: true, - callback: LGraphCanvas.onMenuNodeShapes - }, - null - ]; - } - - if (node.onGetInputs) { - var inputs = node.onGetInputs(); - if (inputs && inputs.length) { - options[0].disabled = false; - } - } - - if (node.onGetOutputs) { - var outputs = node.onGetOutputs(); - if (outputs && outputs.length) { - options[1].disabled = false; - } - } - - if (node.getExtraMenuOptions) { - var extra = node.getExtraMenuOptions(this); - if (extra) { - extra.push(null); - options = extra.concat(options); - } - } - - if (node.clonable !== false) { - options.push({ - content: "Clone", - callback: LGraphCanvas.onMenuNodeClone - }); - } - if (node.removable !== false) { - options.push(null, { - content: "Remove", - callback: LGraphCanvas.onMenuNodeRemove - }); - } - - if (node.graph && node.graph.onGetNodeMenuOptions) { - node.graph.onGetNodeMenuOptions(options, node); - } - - return options; - }; - - LGraphCanvas.prototype.getGroupMenuOptions = function(node) { - var o = [ - { content: "Title", callback: LGraphCanvas.onShowPropertyEditor }, - { - content: "Color", - has_submenu: true, - callback: LGraphCanvas.onMenuNodeColors - }, - { - content: "Font size", - property: "font_size", - type: "Number", - callback: LGraphCanvas.onShowPropertyEditor - }, - null, - { content: "Remove", callback: LGraphCanvas.onMenuNodeRemove } - ]; - - return o; - }; - - LGraphCanvas.prototype.processContextMenu = function(node, event) { - var that = this; - var canvas = LGraphCanvas.active_canvas; - var ref_window = canvas.getCanvasWindow(); - - var menu_info = null; - var options = { - event: event, - callback: inner_option_clicked, - extra: node - }; - - if(node) - options.title = node.type; - - //check if mouse is in input - var slot = null; - if (node) { - slot = node.getSlotInPosition(event.canvasX, event.canvasY); - LGraphCanvas.active_node = node; - } - - if (slot) { - //on slot - menu_info = []; - if ( - slot && - slot.output && - slot.output.links && - slot.output.links.length - ) { - menu_info.push({ content: "Disconnect Links", slot: slot }); - } - var _slot = slot.input || slot.output; - menu_info.push( - _slot.locked - ? "Cannot remove" - : { content: "Remove Slot", slot: slot } - ); - menu_info.push( - _slot.nameLocked - ? "Cannot rename" - : { content: "Rename Slot", slot: slot } - ); - options.title = - (slot.input ? slot.input.type : slot.output.type) || "*"; - if (slot.input && slot.input.type == LiteGraph.ACTION) { - options.title = "Action"; - } - if (slot.output && slot.output.type == LiteGraph.EVENT) { - options.title = "Event"; - } - } else { - if (node) { - //on node - menu_info = this.getNodeMenuOptions(node); - } else { - menu_info = this.getCanvasMenuOptions(); - var group = this.graph.getGroupOnPos( - event.canvasX, - event.canvasY - ); - if (group) { - //on group - menu_info.push(null, { - content: "Edit Group", - has_submenu: true, - submenu: { - title: "Group", - extra: group, - options: this.getGroupMenuOptions(group) - } - }); - } - } - } - - //show menu - if (!menu_info) { - return; - } - - var menu = new LiteGraph.ContextMenu(menu_info, options, ref_window); - - function inner_option_clicked(v, options, e) { - if (!v) { - return; - } - - if (v.content == "Remove Slot") { - var info = v.slot; - if (info.input) { - node.removeInput(info.slot); - } else if (info.output) { - node.removeOutput(info.slot); - } - return; - } else if (v.content == "Disconnect Links") { - var info = v.slot; - if (info.output) { - node.disconnectOutput(info.slot); - } else if (info.input) { - node.disconnectInput(info.slot); - } - return; - } else if (v.content == "Rename Slot") { - var info = v.slot; - var slot_info = info.input - ? node.getInputInfo(info.slot) - : node.getOutputInfo(info.slot); - var dialog = that.createDialog( - "Name", - options - ); - var input = dialog.querySelector("input"); - if (input && slot_info) { - input.value = slot_info.label || ""; - } - dialog - .querySelector("button") - .addEventListener("click", function(e) { - if (input.value) { - if (slot_info) { - slot_info.label = input.value; - } - that.setDirty(true); - } - dialog.close(); - }); - } - - //if(v.callback) - // return v.callback.call(that, node, options, e, menu, that, event ); - } - }; - - //API ************************************************* - //like rect but rounded corners - if (typeof(window) != "undefined" && window.CanvasRenderingContext2D) { - window.CanvasRenderingContext2D.prototype.roundRect = function( - x, - y, - width, - height, - radius, - radius_low - ) { - if (radius === undefined) { - radius = 5; - } - - if (radius_low === undefined) { - radius_low = radius; - } - - this.moveTo(x + radius, y); - this.lineTo(x + width - radius, y); - this.quadraticCurveTo(x + width, y, x + width, y + radius); - - this.lineTo(x + width, y + height - radius_low); - this.quadraticCurveTo( - x + width, - y + height, - x + width - radius_low, - y + height - ); - this.lineTo(x + radius_low, y + height); - this.quadraticCurveTo(x, y + height, x, y + height - radius_low); - this.lineTo(x, y + radius); - this.quadraticCurveTo(x, y, x + radius, y); - }; - } - - function compareObjects(a, b) { - for (var i in a) { - if (a[i] != b[i]) { - return false; - } - } - return true; - } - LiteGraph.compareObjects = compareObjects; - - function distance(a, b) { - return Math.sqrt( - (b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1]) - ); - } - LiteGraph.distance = distance; - - function colorToString(c) { - return ( - "rgba(" + - Math.round(c[0] * 255).toFixed() + - "," + - Math.round(c[1] * 255).toFixed() + - "," + - Math.round(c[2] * 255).toFixed() + - "," + - (c.length == 4 ? c[3].toFixed(2) : "1.0") + - ")" - ); - } - LiteGraph.colorToString = colorToString; - - function isInsideRectangle(x, y, left, top, width, height) { - if (left < x && left + width > x && top < y && top + height > y) { - return true; - } - return false; - } - LiteGraph.isInsideRectangle = isInsideRectangle; - - //[minx,miny,maxx,maxy] - function growBounding(bounding, x, y) { - if (x < bounding[0]) { - bounding[0] = x; - } else if (x > bounding[2]) { - bounding[2] = x; - } - - if (y < bounding[1]) { - bounding[1] = y; - } else if (y > bounding[3]) { - bounding[3] = y; - } - } - LiteGraph.growBounding = growBounding; - - //point inside bounding box - function isInsideBounding(p, bb) { - if ( - p[0] < bb[0][0] || - p[1] < bb[0][1] || - p[0] > bb[1][0] || - p[1] > bb[1][1] - ) { - return false; - } - return true; - } - LiteGraph.isInsideBounding = isInsideBounding; - - //bounding overlap, format: [ startx, starty, width, height ] - function overlapBounding(a, b) { - var A_end_x = a[0] + a[2]; - var A_end_y = a[1] + a[3]; - var B_end_x = b[0] + b[2]; - var B_end_y = b[1] + b[3]; - - if ( - a[0] > B_end_x || - a[1] > B_end_y || - A_end_x < b[0] || - A_end_y < b[1] - ) { - return false; - } - return true; - } - LiteGraph.overlapBounding = overlapBounding; - - //Convert a hex value to its decimal value - the inputted hex must be in the - // format of a hex triplet - the kind we use for HTML colours. The function - // will return an array with three values. - function hex2num(hex) { - if (hex.charAt(0) == "#") { - hex = hex.slice(1); - } //Remove the '#' char - if there is one. - hex = hex.toUpperCase(); - var hex_alphabets = "0123456789ABCDEF"; - var value = new Array(3); - var k = 0; - var int1, int2; - for (var i = 0; i < 6; i += 2) { - int1 = hex_alphabets.indexOf(hex.charAt(i)); - int2 = hex_alphabets.indexOf(hex.charAt(i + 1)); - value[k] = int1 * 16 + int2; - k++; - } - return value; - } - - LiteGraph.hex2num = hex2num; - - //Give a array with three values as the argument and the function will return - // the corresponding hex triplet. - function num2hex(triplet) { - var hex_alphabets = "0123456789ABCDEF"; - var hex = "#"; - var int1, int2; - for (var i = 0; i < 3; i++) { - int1 = triplet[i] / 16; - int2 = triplet[i] % 16; - - hex += hex_alphabets.charAt(int1) + hex_alphabets.charAt(int2); - } - return hex; - } - - LiteGraph.num2hex = num2hex; - - /* LiteGraph GUI elements used for canvas editing *************************************/ - - /** - * ContextMenu from LiteGUI - * - * @class ContextMenu - * @constructor - * @param {Array} values (allows object { title: "Nice text", callback: function ... }) - * @param {Object} options [optional] Some options:\ - * - title: title to show on top of the menu - * - callback: function to call when an option is clicked, it receives the item information - * - ignore_item_callbacks: ignores the callback inside the item, it just calls the options.callback - * - event: you can pass a MouseEvent, this way the ContextMenu appears in that position - */ - function ContextMenu(values, options) { - options = options || {}; - this.options = options; - var that = this; - - //to link a menu with its parent - if (options.parentMenu) { - if (options.parentMenu.constructor !== this.constructor) { - console.error( - "parentMenu must be of class ContextMenu, ignoring it" - ); - options.parentMenu = null; - } else { - this.parentMenu = options.parentMenu; - this.parentMenu.lock = true; - this.parentMenu.current_submenu = this; - } - } - - var eventClass = null; - if(options.event) //use strings because comparing classes between windows doesnt work - eventClass = options.event.constructor.name; - if ( eventClass !== "MouseEvent" && - eventClass !== "CustomEvent" && - eventClass !== "PointerEvent" - ) { - console.error( - "Event passed to ContextMenu is not of type MouseEvent or CustomEvent. Ignoring it." - ); - options.event = null; - } - - var root = document.createElement("div"); - root.className = "litegraph litecontextmenu litemenubar-panel"; - if (options.className) { - root.className += " " + options.className; - } - root.style.minWidth = 100; - root.style.minHeight = 100; - root.style.pointerEvents = "none"; - setTimeout(function() { - root.style.pointerEvents = "auto"; - }, 100); //delay so the mouse up event is not caught by this element - - //this prevents the default context browser menu to open in case this menu was created when pressing right button - root.addEventListener( - "mouseup", - function(e) { - e.preventDefault(); - return true; - }, - true - ); - root.addEventListener( - "contextmenu", - function(e) { - if (e.button != 2) { - //right button - return false; - } - e.preventDefault(); - return false; - }, - true - ); - - root.addEventListener( - "mousedown", - function(e) { - if (e.button == 2) { - that.close(); - e.preventDefault(); - return true; - } - }, - true - ); - - function on_mouse_wheel(e) { - var pos = parseInt(root.style.top); - root.style.top = - (pos + e.deltaY * options.scroll_speed).toFixed() + "px"; - e.preventDefault(); - return true; - } - - if (!options.scroll_speed) { - options.scroll_speed = 0.1; - } - - root.addEventListener("wheel", on_mouse_wheel, true); - root.addEventListener("mousewheel", on_mouse_wheel, true); - - this.root = root; - - //title - if (options.title) { - var element = document.createElement("div"); - element.className = "litemenu-title"; - element.innerHTML = options.title; - root.appendChild(element); - } - - //entries - var num = 0; - for (var i in values) { - var name = values.constructor == Array ? values[i] : i; - if (name != null && name.constructor !== String) { - name = name.content === undefined ? String(name) : name.content; - } - var value = values[i]; - this.addItem(name, value, options); - num++; - } - - //close on leave - root.addEventListener("mouseleave", function(e) { - if (that.lock) { - return; - } - if (root.closing_timer) { - clearTimeout(root.closing_timer); - } - root.closing_timer = setTimeout(that.close.bind(that, e), 500); - //that.close(e); - }); - - root.addEventListener("mouseenter", function(e) { - if (root.closing_timer) { - clearTimeout(root.closing_timer); - } - }); - - //insert before checking position - var root_document = document; - if (options.event) { - root_document = options.event.target.ownerDocument; - } - - if (!root_document) { - root_document = document; - } - - if( root_document.fullscreenElement ) - root_document.fullscreenElement.appendChild(root); - else - root_document.body.appendChild(root); - - //compute best position - var left = options.left || 0; - var top = options.top || 0; - if (options.event) { - left = options.event.clientX - 10; - top = options.event.clientY - 10; - if (options.title) { - top -= 20; - } - - if (options.parentMenu) { - var rect = options.parentMenu.root.getBoundingClientRect(); - left = rect.left + rect.width; - } - - var body_rect = document.body.getBoundingClientRect(); - var root_rect = root.getBoundingClientRect(); - - if (left > body_rect.width - root_rect.width - 10) { - left = body_rect.width - root_rect.width - 10; - } - if (top > body_rect.height - root_rect.height - 10) { - top = body_rect.height - root_rect.height - 10; - } - } - - root.style.left = left + "px"; - root.style.top = top + "px"; - - if (options.scale) { - root.style.transform = "scale(" + options.scale + ")"; - } - } - - ContextMenu.prototype.addItem = function(name, value, options) { - var that = this; - options = options || {}; - - var element = document.createElement("div"); - element.className = "litemenu-entry submenu"; - - var disabled = false; - - if (value === null) { - element.classList.add("separator"); - //element.innerHTML = "
" - //continue; - } else { - element.innerHTML = value && value.title ? value.title : name; - element.value = value; - - if (value) { - if (value.disabled) { - disabled = true; - element.classList.add("disabled"); - } - if (value.submenu || value.has_submenu) { - element.classList.add("has_submenu"); - } - } - - if (typeof value == "function") { - element.dataset["value"] = name; - element.onclick_callback = value; - } else { - element.dataset["value"] = value; - } - - if (value.className) { - element.className += " " + value.className; - } - } - - this.root.appendChild(element); - if (!disabled) { - element.addEventListener("click", inner_onclick); - } - if (options.autoopen) { - element.addEventListener("mouseenter", inner_over); - } - - function inner_over(e) { - var value = this.value; - if (!value || !value.has_submenu) { - return; - } - //if it is a submenu, autoopen like the item was clicked - inner_onclick.call(this, e); - } - - //menu option clicked - function inner_onclick(e) { - var value = this.value; - var close_parent = true; - - if (that.current_submenu) { - that.current_submenu.close(e); - } - - //global callback - if (options.callback) { - var r = options.callback.call( - this, - value, - options, - e, - that, - options.node - ); - if (r === true) { - close_parent = false; - } - } - - //special cases - if (value) { - if ( - value.callback && - !options.ignore_item_callbacks && - value.disabled !== true - ) { - //item callback - var r = value.callback.call( - this, - value, - options, - e, - that, - options.extra - ); - if (r === true) { - close_parent = false; - } - } - if (value.submenu) { - if (!value.submenu.options) { - throw "ContextMenu submenu needs options"; - } - var submenu = new that.constructor(value.submenu.options, { - callback: value.submenu.callback, - event: e, - parentMenu: that, - ignore_item_callbacks: - value.submenu.ignore_item_callbacks, - title: value.submenu.title, - extra: value.submenu.extra, - autoopen: options.autoopen - }); - close_parent = false; - } - } - - if (close_parent && !that.lock) { - that.close(); - } - } - - return element; - }; - - ContextMenu.prototype.close = function(e, ignore_parent_menu) { - if (this.root.parentNode) { - this.root.parentNode.removeChild(this.root); - } - if (this.parentMenu && !ignore_parent_menu) { - this.parentMenu.lock = false; - this.parentMenu.current_submenu = null; - if (e === undefined) { - this.parentMenu.close(); - } else if ( - e && - !ContextMenu.isCursorOverElement(e, this.parentMenu.root) - ) { - ContextMenu.trigger(this.parentMenu.root, "mouseleave", e); - } - } - if (this.current_submenu) { - this.current_submenu.close(e, true); - } - - if (this.root.closing_timer) { - clearTimeout(this.root.closing_timer); - } - }; - - //this code is used to trigger events easily (used in the context menu mouseleave - ContextMenu.trigger = function(element, event_name, params, origin) { - var evt = document.createEvent("CustomEvent"); - evt.initCustomEvent(event_name, true, true, params); //canBubble, cancelable, detail - evt.srcElement = origin; - if (element.dispatchEvent) { - element.dispatchEvent(evt); - } else if (element.__events) { - element.__events.dispatchEvent(evt); - } - //else nothing seems binded here so nothing to do - return evt; - }; - - //returns the top most menu - ContextMenu.prototype.getTopMenu = function() { - if (this.options.parentMenu) { - return this.options.parentMenu.getTopMenu(); - } - return this; - }; - - ContextMenu.prototype.getFirstEvent = function() { - if (this.options.parentMenu) { - return this.options.parentMenu.getFirstEvent(); - } - return this.options.event; - }; - - ContextMenu.isCursorOverElement = function(event, element) { - var left = event.clientX; - var top = event.clientY; - var rect = element.getBoundingClientRect(); - if (!rect) { - return false; - } - if ( - top > rect.top && - top < rect.top + rect.height && - left > rect.left && - left < rect.left + rect.width - ) { - return true; - } - return false; - }; - - LiteGraph.ContextMenu = ContextMenu; - - LiteGraph.closeAllContextMenus = function(ref_window) { - ref_window = ref_window || window; - - var elements = ref_window.document.querySelectorAll(".litecontextmenu"); - if (!elements.length) { - return; - } - - var result = []; - for (var i = 0; i < elements.length; i++) { - result.push(elements[i]); - } - - for (var i in result) { - if (result[i].close) { - result[i].close(); - } else if (result[i].parentNode) { - result[i].parentNode.removeChild(result[i]); - } - } - }; - - LiteGraph.extendClass = function(target, origin) { - for (var i in origin) { - //copy class properties - if (target.hasOwnProperty(i)) { - continue; - } - target[i] = origin[i]; - } - - if (origin.prototype) { - //copy prototype properties - for (var i in origin.prototype) { - //only enumerable - if (!origin.prototype.hasOwnProperty(i)) { - continue; - } - - if (target.prototype.hasOwnProperty(i)) { - //avoid overwriting existing ones - continue; - } - - //copy getters - if (origin.prototype.__lookupGetter__(i)) { - target.prototype.__defineGetter__( - i, - origin.prototype.__lookupGetter__(i) - ); - } else { - target.prototype[i] = origin.prototype[i]; - } - - //and setters - if (origin.prototype.__lookupSetter__(i)) { - target.prototype.__defineSetter__( - i, - origin.prototype.__lookupSetter__(i) - ); - } - } - } - }; - - //used by some widgets to render a curve editor - function CurveEditor( points ) - { - this.points = points; - this.selected = -1; - this.nearest = -1; - this.size = null; //stores last size used - this.must_update = true; - this.margin = 5; - } - - CurveEditor.sampleCurve = function(f,points) - { - if(!points) - return; - for(var i = 0; i < points.length - 1; ++i) - { - var p = points[i]; - var pn = points[i+1]; - if(pn[0] < f) - continue; - var r = (pn[0] - p[0]); - if( Math.abs(r) < 0.00001 ) - return p[1]; - var local_f = (f - p[0]) / r; - return p[1] * (1.0 - local_f) + pn[1] * local_f; - } - return 0; - } - - CurveEditor.prototype.draw = function( ctx, size, graphcanvas, background_color, line_color, inactive ) - { - var points = this.points; - if(!points) - return; - this.size = size; - var w = size[0] - this.margin * 2; - var h = size[1] - this.margin * 2; - - line_color = line_color || "#666"; - - ctx.save(); - ctx.translate(this.margin,this.margin); - - if(background_color) - { - ctx.fillStyle = "#111"; - ctx.fillRect(0,0,w,h); - ctx.fillStyle = "#222"; - ctx.fillRect(w*0.5,0,1,h); - ctx.strokeStyle = "#333"; - ctx.strokeRect(0,0,w,h); - } - ctx.strokeStyle = line_color; - if(inactive) - ctx.globalAlpha = 0.5; - ctx.beginPath(); - for(var i = 0; i < points.length; ++i) - { - var p = points[i]; - ctx.lineTo( p[0] * w, (1.0 - p[1]) * h ); - } - ctx.stroke(); - ctx.globalAlpha = 1; - if(!inactive) - for(var i = 0; i < points.length; ++i) - { - var p = points[i]; - ctx.fillStyle = this.selected == i ? "#FFF" : (this.nearest == i ? "#DDD" : "#AAA"); - ctx.beginPath(); - ctx.arc( p[0] * w, (1.0 - p[1]) * h, 2, 0, Math.PI * 2 ); - ctx.fill(); - } - ctx.restore(); - } - - //localpos is mouse in curve editor space - CurveEditor.prototype.onMouseDown = function( localpos, graphcanvas ) - { - var points = this.points; - if(!points) - return; - if( localpos[1] < 0 ) - return; - - //this.captureInput(true); - var w = this.size[0] - this.margin * 2; - var h = this.size[1] - this.margin * 2; - var x = localpos[0] - this.margin; - var y = localpos[1] - this.margin; - var pos = [x,y]; - var max_dist = 30 / graphcanvas.ds.scale; - //search closer one - this.selected = this.getCloserPoint(pos, max_dist); - //create one - if(this.selected == -1) - { - var point = [x / w, 1 - y / h]; - points.push(point); - points.sort(function(a,b){ return a[0] - b[0]; }); - this.selected = points.indexOf(point); - this.must_update = true; - } - if(this.selected != -1) - return true; - } - - CurveEditor.prototype.onMouseMove = function( localpos, graphcanvas ) - { - var points = this.points; - if(!points) - return; - var s = this.selected; - if(s < 0) - return; - var x = (localpos[0] - this.margin) / (this.size[0] - this.margin * 2 ); - var y = (localpos[1] - this.margin) / (this.size[1] - this.margin * 2 ); - var curvepos = [(localpos[0] - this.margin),(localpos[1] - this.margin)]; - var max_dist = 30 / graphcanvas.ds.scale; - this._nearest = this.getCloserPoint(curvepos, max_dist); - var point = points[s]; - if(point) - { - var is_edge_point = s == 0 || s == points.length - 1; - if( !is_edge_point && (localpos[0] < -10 || localpos[0] > this.size[0] + 10 || localpos[1] < -10 || localpos[1] > this.size[1] + 10) ) - { - points.splice(s,1); - this.selected = -1; - return; - } - if( !is_edge_point ) //not edges - point[0] = Math.clamp(x,0,1); - else - point[0] = s == 0 ? 0 : 1; - point[1] = 1.0 - Math.clamp(y,0,1); - points.sort(function(a,b){ return a[0] - b[0]; }); - this.selected = points.indexOf(point); - this.must_update = true; - } - } - - CurveEditor.prototype.onMouseUp = function( localpos, graphcanvas ) - { - this.selected = -1; - return false; - } - - CurveEditor.prototype.getCloserPoint = function(pos, max_dist) - { - var points = this.points; - if(!points) - return -1; - max_dist = max_dist || 30; - var w = (this.size[0] - this.margin * 2); - var h = (this.size[1] - this.margin * 2); - var num = points.length; - var p2 = [0,0]; - var min_dist = 1000000; - var closest = -1; - var last_valid = -1; - for(var i = 0; i < num; ++i) - { - var p = points[i]; - p2[0] = p[0] * w; - p2[1] = (1.0 - p[1]) * h; - if(p2[0] < pos[0]) - last_valid = i; - var dist = vec2.distance(pos,p2); - if(dist > min_dist || dist > max_dist) - continue; - closest = i; - min_dist = dist; - } - return closest; - } - - LiteGraph.CurveEditor = CurveEditor; - - //used to create nodes from wrapping functions - LiteGraph.getParameterNames = function(func) { - return (func + "") - .replace(/[/][/].*$/gm, "") // strip single-line comments - .replace(/\s+/g, "") // strip white space - .replace(/[/][*][^/*]*[*][/]/g, "") // strip multi-line comments /**/ - .split("){", 1)[0] - .replace(/^[^(]*[(]/, "") // extract the parameters - .replace(/=[^,]+/g, "") // strip any ES6 defaults - .split(",") - .filter(Boolean); // split & filter [""] - }; - - Math.clamp = function(v, a, b) { - return a > v ? a : b < v ? b : v; - }; - - if (typeof window != "undefined" && !window["requestAnimationFrame"]) { - window.requestAnimationFrame = - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - function(callback) { - window.setTimeout(callback, 1000 / 60); - }; - } -})(this); - -if (typeof exports != "undefined") { - exports.LiteGraph = this.LiteGraph; -} - -//basic nodes -(function(global) { - var LiteGraph = global.LiteGraph; - - //Constant - function Time() { - this.addOutput("in ms", "number"); - this.addOutput("in sec", "number"); - } - - Time.title = "Time"; - Time.desc = "Time"; - - Time.prototype.onExecute = function() { - this.setOutputData(0, this.graph.globaltime * 1000); - this.setOutputData(1, this.graph.globaltime); - }; - - LiteGraph.registerNodeType("basic/time", Time); - - //Subgraph: a node that contains a graph - function Subgraph() { - var that = this; - this.size = [140, 80]; - this.properties = { enabled: true }; - this.enabled = true; - - //create inner graph - this.subgraph = new LiteGraph.LGraph(); - this.subgraph._subgraph_node = this; - this.subgraph._is_subgraph = true; - - this.subgraph.onTrigger = this.onSubgraphTrigger.bind(this); - - this.subgraph.onInputAdded = this.onSubgraphNewInput.bind(this); - this.subgraph.onInputRenamed = this.onSubgraphRenamedInput.bind(this); - this.subgraph.onInputTypeChanged = this.onSubgraphTypeChangeInput.bind( - this - ); - this.subgraph.onInputRemoved = this.onSubgraphRemovedInput.bind(this); - - this.subgraph.onOutputAdded = this.onSubgraphNewOutput.bind(this); - this.subgraph.onOutputRenamed = this.onSubgraphRenamedOutput.bind(this); - this.subgraph.onOutputTypeChanged = this.onSubgraphTypeChangeOutput.bind( - this - ); - this.subgraph.onOutputRemoved = this.onSubgraphRemovedOutput.bind(this); - } - - Subgraph.title = "Subgraph"; - Subgraph.desc = "Graph inside a node"; - Subgraph.title_color = "#334"; - - Subgraph.prototype.onGetInputs = function() { - return [["enabled", "boolean"]]; - }; - - Subgraph.prototype.onDrawTitle = function(ctx) { - if (this.flags.collapsed) { - return; - } - - ctx.fillStyle = "#555"; - var w = LiteGraph.NODE_TITLE_HEIGHT; - var x = this.size[0] - w; - ctx.fillRect(x, -w, w, w); - ctx.fillStyle = "#333"; - ctx.beginPath(); - ctx.moveTo(x + w * 0.2, -w * 0.6); - ctx.lineTo(x + w * 0.8, -w * 0.6); - ctx.lineTo(x + w * 0.5, -w * 0.3); - ctx.fill(); - }; - - Subgraph.prototype.onDblClick = function(e, pos, graphcanvas) { - var that = this; - setTimeout(function() { - graphcanvas.openSubgraph(that.subgraph); - }, 10); - }; - - Subgraph.prototype.onMouseDown = function(e, pos, graphcanvas) { - if ( - !this.flags.collapsed && - pos[0] > this.size[0] - LiteGraph.NODE_TITLE_HEIGHT && - pos[1] < 0 - ) { - var that = this; - setTimeout(function() { - graphcanvas.openSubgraph(that.subgraph); - }, 10); - } - }; - - Subgraph.prototype.onAction = function(action, param) { - this.subgraph.onAction(action, param); - }; - - Subgraph.prototype.onExecute = function() { - this.enabled = this.getInputOrProperty("enabled"); - if (!this.enabled) { - return; - } - - //send inputs to subgraph global inputs - if (this.inputs) { - for (var i = 0; i < this.inputs.length; i++) { - var input = this.inputs[i]; - var value = this.getInputData(i); - this.subgraph.setInputData(input.name, value); - } - } - - //execute - this.subgraph.runStep(); - - //send subgraph global outputs to outputs - if (this.outputs) { - for (var i = 0; i < this.outputs.length; i++) { - var output = this.outputs[i]; - var value = this.subgraph.getOutputData(output.name); - this.setOutputData(i, value); - } - } - }; - - Subgraph.prototype.sendEventToAllNodes = function(eventname, param, mode) { - if (this.enabled) { - this.subgraph.sendEventToAllNodes(eventname, param, mode); - } - }; - - //**** INPUTS *********************************** - Subgraph.prototype.onSubgraphTrigger = function(event, param) { - var slot = this.findOutputSlot(event); - if (slot != -1) { - this.triggerSlot(slot); - } - }; - - Subgraph.prototype.onSubgraphNewInput = function(name, type) { - var slot = this.findInputSlot(name); - if (slot == -1) { - //add input to the node - this.addInput(name, type); - } - }; - - Subgraph.prototype.onSubgraphRenamedInput = function(oldname, name) { - var slot = this.findInputSlot(oldname); - if (slot == -1) { - return; - } - var info = this.getInputInfo(slot); - info.name = name; - }; - - Subgraph.prototype.onSubgraphTypeChangeInput = function(name, type) { - var slot = this.findInputSlot(name); - if (slot == -1) { - return; - } - var info = this.getInputInfo(slot); - info.type = type; - }; - - Subgraph.prototype.onSubgraphRemovedInput = function(name) { - var slot = this.findInputSlot(name); - if (slot == -1) { - return; - } - this.removeInput(slot); - }; - - //**** OUTPUTS *********************************** - Subgraph.prototype.onSubgraphNewOutput = function(name, type) { - var slot = this.findOutputSlot(name); - if (slot == -1) { - this.addOutput(name, type); - } - }; - - Subgraph.prototype.onSubgraphRenamedOutput = function(oldname, name) { - var slot = this.findOutputSlot(oldname); - if (slot == -1) { - return; - } - var info = this.getOutputInfo(slot); - info.name = name; - }; - - Subgraph.prototype.onSubgraphTypeChangeOutput = function(name, type) { - var slot = this.findOutputSlot(name); - if (slot == -1) { - return; - } - var info = this.getOutputInfo(slot); - info.type = type; - }; - - Subgraph.prototype.onSubgraphRemovedOutput = function(name) { - var slot = this.findInputSlot(name); - if (slot == -1) { - return; - } - this.removeOutput(slot); - }; - // ***************************************************** - - Subgraph.prototype.getExtraMenuOptions = function(graphcanvas) { - var that = this; - return [ - { - content: "Open", - callback: function() { - graphcanvas.openSubgraph(that.subgraph); - } - } - ]; - }; - - Subgraph.prototype.onResize = function(size) { - size[1] += 20; - }; - - Subgraph.prototype.serialize = function() { - var data = LiteGraph.LGraphNode.prototype.serialize.call(this); - data.subgraph = this.subgraph.serialize(); - return data; - }; - //no need to define node.configure, the default method detects node.subgraph and passes the object to node.subgraph.configure() - - Subgraph.prototype.clone = function() { - var node = LiteGraph.createNode(this.type); - var data = this.serialize(); - delete data["id"]; - delete data["inputs"]; - delete data["outputs"]; - node.configure(data); - return node; - }; - - LiteGraph.Subgraph = Subgraph; - LiteGraph.registerNodeType("graph/subgraph", Subgraph); - - //Input for a subgraph - function GraphInput() { - this.addOutput("", "number"); - - this.name_in_graph = ""; - this.properties = { - name: "", - type: "number", - value: 0 - }; - - var that = this; - - this.name_widget = this.addWidget( - "text", - "Name", - this.properties.name, - function(v) { - if (!v) { - return; - } - that.setProperty("name",v); - } - ); - this.type_widget = this.addWidget( - "text", - "Type", - this.properties.type, - function(v) { - that.setProperty("type",v); - } - ); - - this.value_widget = this.addWidget( - "number", - "Value", - this.properties.value, - function(v) { - that.setProperty("value",v); - } - ); - - this.widgets_up = true; - this.size = [180, 90]; - } - - GraphInput.title = "Input"; - GraphInput.desc = "Input of the graph"; - - GraphInput.prototype.onConfigure = function() - { - this.updateType(); - } - - GraphInput.prototype.updateType = function() - { - var type = this.properties.type; - this.type_widget.value = type; - if(this.outputs[0].type != type) - { - this.outputs[0].type = type; - this.disconnectOutput(0); - } - if(type == "number") - { - this.value_widget.type = "number"; - this.value_widget.value = 0; - } - else if(type == "boolean") - { - this.value_widget.type = "toggle"; - this.value_widget.value = true; - } - else if(type == "string") - { - this.value_widget.type = "text"; - this.value_widget.value = ""; - } - else - { - this.value_widget.type = null; - this.value_widget.value = null; - } - this.properties.value = this.value_widget.value; - } - - GraphInput.prototype.onPropertyChanged = function(name,v) - { - if( name == "name" ) - { - if (v == "" || v == this.name_in_graph || v == "enabled") { - return false; - } - if(this.graph) - { - if (this.name_in_graph) { - //already added - this.graph.renameInput( this.name_in_graph, v ); - } else { - this.graph.addInput( v, this.properties.type ); - } - } //what if not?! - this.name_widget.value = v; - this.name_in_graph = v; - } - else if( name == "type" ) - { - v = v || ""; - this.updateType(v); - } - else if( name == "value" ) - { - } - } - - GraphInput.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this.properties.name; - } - return this.title; - }; - - GraphInput.prototype.onAction = function(action, param) { - if (this.properties.type == LiteGraph.EVENT) { - this.triggerSlot(0, param); - } - }; - - GraphInput.prototype.onExecute = function() { - var name = this.properties.name; - //read from global input - var data = this.graph.inputs[name]; - if (!data) { - this.setOutputData(0, this.properties.value ); - return; - } - - this.setOutputData(0, data.value !== undefined ? data.value : this.properties.value ); - }; - - GraphInput.prototype.onRemoved = function() { - if (this.name_in_graph) { - this.graph.removeInput(this.name_in_graph); - } - }; - - LiteGraph.GraphInput = GraphInput; - LiteGraph.registerNodeType("graph/input", GraphInput); - - //Output for a subgraph - function GraphOutput() { - this.addInput("", ""); - - this.name_in_graph = ""; - this.properties = {}; - var that = this; - - Object.defineProperty(this.properties, "name", { - get: function() { - return that.name_in_graph; - }, - set: function(v) { - if (v == "" || v == that.name_in_graph) { - return; - } - if (that.name_in_graph) { - //already added - that.graph.renameOutput(that.name_in_graph, v); - } else { - that.graph.addOutput(v, that.properties.type); - } - that.name_widget.value = v; - that.name_in_graph = v; - }, - enumerable: true - }); - - Object.defineProperty(this.properties, "type", { - get: function() { - return that.inputs[0].type; - }, - set: function(v) { - if (v == "action" || v == "event") { - v = LiteGraph.ACTION; - } - that.inputs[0].type = v; - if (that.name_in_graph) { - //already added - that.graph.changeOutputType( - that.name_in_graph, - that.inputs[0].type - ); - } - that.type_widget.value = v || ""; - }, - enumerable: true - }); - - this.name_widget = this.addWidget("text","Name",this.properties.name,"name"); - this.type_widget = this.addWidget("text","Type",this.properties.type,"type"); - this.widgets_up = true; - this.size = [180, 60]; - } - - GraphOutput.title = "Output"; - GraphOutput.desc = "Output of the graph"; - - GraphOutput.prototype.onExecute = function() { - this._value = this.getInputData(0); - this.graph.setOutputData(this.properties.name, this._value); - }; - - GraphOutput.prototype.onAction = function(action, param) { - if (this.properties.type == LiteGraph.ACTION) { - this.graph.trigger(this.properties.name, param); - } - }; - - GraphOutput.prototype.onRemoved = function() { - if (this.name_in_graph) { - this.graph.removeOutput(this.name_in_graph); - } - }; - - GraphOutput.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this.properties.name; - } - return this.title; - }; - - LiteGraph.GraphOutput = GraphOutput; - LiteGraph.registerNodeType("graph/output", GraphOutput); - - //Constant - function ConstantNumber() { - this.addOutput("value", "number"); - this.addProperty("value", 1.0); - this.widget = this.addWidget("number","value",1,"value"); - this.widgets_up = true; - this.size = [180, 30]; - } - - ConstantNumber.title = "Const Number"; - ConstantNumber.desc = "Constant number"; - - ConstantNumber.prototype.onExecute = function() { - this.setOutputData(0, parseFloat(this.properties["value"])); - }; - - ConstantNumber.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this.properties.value; - } - return this.title; - }; - - ConstantNumber.prototype.setValue = function(v) - { - this.setProperty("value",v); - } - - ConstantNumber.prototype.onDrawBackground = function(ctx) { - //show the current value - this.outputs[0].label = this.properties["value"].toFixed(3); - }; - - LiteGraph.registerNodeType("basic/const", ConstantNumber); - - function ConstantBoolean() { - this.addOutput("", "boolean"); - this.addProperty("value", true); - this.widget = this.addWidget("toggle","value",true,"value"); - this.widgets_up = true; - this.size = [140, 30]; - } - - ConstantBoolean.title = "Const Boolean"; - ConstantBoolean.desc = "Constant boolean"; - ConstantBoolean.prototype.getTitle = ConstantNumber.prototype.getTitle; - - ConstantBoolean.prototype.onExecute = function() { - this.setOutputData(0, this.properties["value"]); - }; - - ConstantBoolean.prototype.setValue = ConstantNumber.prototype.setValue; - - ConstantBoolean.prototype.onGetInputs = function() { - return [["toggle", LiteGraph.ACTION]]; - }; - - ConstantBoolean.prototype.onAction = function(action) - { - this.setValue( !this.properties.value ); - } - - LiteGraph.registerNodeType("basic/boolean", ConstantBoolean); - - function ConstantString() { - this.addOutput("", "string"); - this.addProperty("value", ""); - this.widget = this.addWidget("text","value","","value"); //link to property value - this.widgets_up = true; - this.size = [180, 30]; - } - - ConstantString.title = "Const String"; - ConstantString.desc = "Constant string"; - - ConstantString.prototype.getTitle = ConstantNumber.prototype.getTitle; - - ConstantString.prototype.onExecute = function() { - this.setOutputData(0, this.properties["value"]); - }; - - ConstantString.prototype.setValue = ConstantNumber.prototype.setValue; - - ConstantString.prototype.onDropFile = function(file) - { - var that = this; - var reader = new FileReader(); - reader.onload = function(e) - { - that.setProperty("value",e.target.result); - } - reader.readAsText(file); - } - - LiteGraph.registerNodeType("basic/string", ConstantString); - - function ConstantFile() { - this.addInput("url", ""); - this.addOutput("", ""); - this.addProperty("url", ""); - this.addProperty("type", "text"); - this.widget = this.addWidget("text","url","","url"); - this._data = null; - } - - ConstantFile.title = "Const File"; - ConstantFile.desc = "Fetches a file from an url"; - ConstantFile["@type"] = { type: "enum", values: ["text","arraybuffer","blob","json"] }; - - ConstantFile.prototype.onPropertyChanged = function(name, value) { - if (name == "url") - { - if( value == null || value == "") - this._data = null; - else - { - this.fetchFile(value); - } - } - } - - ConstantFile.prototype.onExecute = function() { - var url = this.getInputData(0) || this.properties.url; - if(url && (url != this._url || this._type != this.properties.type)) - this.fetchFile(url); - this.setOutputData(0, this._data ); - }; - - ConstantFile.prototype.setValue = ConstantNumber.prototype.setValue; - - ConstantFile.prototype.fetchFile = function(url) { - var that = this; - if(!url || url.constructor !== String) - { - that._data = null; - that.boxcolor = null; - return; - } - - this._url = url; - this._type = this.properties.type; - if (url.substr(0, 4) == "http" && LiteGraph.proxy) { - url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); - } - fetch(url) - .then(function(response) { - if(!response.ok) - throw new Error("File not found"); - - if(that.properties.type == "arraybuffer") - return response.arrayBuffer(); - else if(that.properties.type == "text") - return response.text(); - else if(that.properties.type == "json") - return response.json(); - else if(that.properties.type == "blob") - return response.blob(); - }) - .then(function(data) { - that._data = data; - that.boxcolor = "#AEA"; - }) - .catch(function(error) { - that._data = null; - that.boxcolor = "red"; - console.error("error fetching file:",url); - }); - }; - - ConstantFile.prototype.onDropFile = function(file) - { - var that = this; - this._url = file.name; - this._type = this.properties.type; - this.properties.url = file.name; - var reader = new FileReader(); - reader.onload = function(e) - { - that.boxcolor = "#AEA"; - var v = e.target.result; - if( that.properties.type == "json" ) - v = JSON.parse(v); - that._data = v; - } - if(that.properties.type == "arraybuffer") - reader.readAsArrayBuffer(file); - else if(that.properties.type == "text" || that.properties.type == "json") - reader.readAsText(file); - else if(that.properties.type == "blob") - return reader.readAsBinaryString(file); - } - - LiteGraph.registerNodeType("basic/file", ConstantFile); - - //to store json objects - function ConstantData() { - this.addOutput("", ""); - this.addProperty("value", ""); - this.widget = this.addWidget("text","json","","value"); - this.widgets_up = true; - this.size = [140, 30]; - this._value = null; - } - - ConstantData.title = "Const Data"; - ConstantData.desc = "Constant Data"; - - ConstantData.prototype.onPropertyChanged = function(name, value) { - this.widget.value = value; - if (value == null || value == "") { - return; - } - - try { - this._value = JSON.parse(value); - this.boxcolor = "#AEA"; - } catch (err) { - this.boxcolor = "red"; - } - }; - - ConstantData.prototype.onExecute = function() { - this.setOutputData(0, this._value); - }; - - ConstantData.prototype.setValue = ConstantNumber.prototype.setValue; - - LiteGraph.registerNodeType("basic/data", ConstantData); - - //to store json objects - function ConstantArray() { - this.addInput("", ""); - this.addOutput("", "array"); - this.addProperty("value", ""); - this.widget = this.addWidget("text","array","","value"); - this.widgets_up = true; - this.size = [140, 30]; - this._value = null; - } - - ConstantArray.title = "Const Array"; - ConstantArray.desc = "Constant Array"; - - ConstantArray.prototype.onPropertyChanged = function(name, value) { - this.widget.value = value; - if (value == null || value == "") { - return; - } - - try { - this._value = JSON.parse(value); - this.boxcolor = "#AEA"; - } catch (err) { - this.boxcolor = "red"; - } - }; - - ConstantArray.prototype.onExecute = function() { - var v = this.getInputData(0); - if(v && v.length) - { - if(!this._value) - this._value = new Array(); - this._value.length = v.length; - for(var i = 0; i < v.length; ++i) - this._value[i] = v[i]; - } - this.setOutputData(0, this._value); - }; - - ConstantArray.prototype.setValue = ConstantNumber.prototype.setValue; - - LiteGraph.registerNodeType("basic/array", ConstantArray); - - function ArrayElement() { - this.addInput("array", "array,table,string"); - this.addInput("index", "number"); - this.addOutput("value", ""); - this.addProperty("index",0); - } - - ArrayElement.title = "Array[i]"; - ArrayElement.desc = "Returns an element from an array"; - - ArrayElement.prototype.onExecute = function() { - var array = this.getInputData(0); - var index = this.getInputData(1); - if(index == null) - index = this.properties.index; - if(array == null || index == null ) - return; - this.setOutputData(0, array[Math.floor(Number(index))] ); - }; - - LiteGraph.registerNodeType("basic/array[]", ArrayElement); - - function TableElement() { - this.addInput("table", "table"); - this.addInput("row", "number"); - this.addInput("col", "number"); - this.addOutput("value", ""); - this.addProperty("row",0); - this.addProperty("column",0); - } - - TableElement.title = "Table[row][col]"; - TableElement.desc = "Returns an element from a table"; - - TableElement.prototype.onExecute = function() { - var table = this.getInputData(0); - var row = this.getInputData(1); - var col = this.getInputData(2); - if(row == null) - row = this.properties.row; - if(col == null) - col = this.properties.column; - if(table == null || row == null || col == null) - return; - var row = table[Math.floor(Number(row))]; - if(row) - this.setOutputData(0, row[Math.floor(Number(col))] ); - else - this.setOutputData(0, null ); - }; - - LiteGraph.registerNodeType("basic/table[][]", TableElement); - - function ObjectProperty() { - this.addInput("obj", ""); - this.addOutput("", ""); - this.addProperty("value", ""); - this.widget = this.addWidget("text","prop.","",this.setValue.bind(this) ); - this.widgets_up = true; - this.size = [140, 30]; - this._value = null; - } - - ObjectProperty.title = "Object property"; - ObjectProperty.desc = "Outputs the property of an object"; - - ObjectProperty.prototype.setValue = function(v) { - this.properties.value = v; - this.widget.value = v; - }; - - ObjectProperty.prototype.getTitle = function() { - if (this.flags.collapsed) { - return "in." + this.properties.value; - } - return this.title; - }; - - ObjectProperty.prototype.onPropertyChanged = function(name, value) { - this.widget.value = value; - }; - - ObjectProperty.prototype.onExecute = function() { - var data = this.getInputData(0); - if (data != null) { - this.setOutputData(0, data[this.properties.value]); - } - }; - - LiteGraph.registerNodeType("basic/object_property", ObjectProperty); - - function ObjectKeys() { - this.addInput("obj", ""); - this.addOutput("keys", "array"); - this.size = [140, 30]; - } - - ObjectKeys.title = "Object keys"; - ObjectKeys.desc = "Outputs an array with the keys of an object"; - - ObjectKeys.prototype.onExecute = function() { - var data = this.getInputData(0); - if (data != null) { - this.setOutputData(0, Object.keys(data) ); - } - }; - - LiteGraph.registerNodeType("basic/object_keys", ObjectKeys); - - function MergeObjects() { - this.addInput("A", "object"); - this.addInput("B", "object"); - this.addOutput("", "object"); - this._result = {}; - var that = this; - this.addWidget("button","clear","",function(){ - that._result = {}; - }); - this.size = this.computeSize(); - } - - MergeObjects.title = "Merge Objects"; - MergeObjects.desc = "Creates an object copying properties from others"; - - MergeObjects.prototype.onExecute = function() { - var A = this.getInputData(0); - var B = this.getInputData(1); - var C = this._result; - if(A) - for(var i in A) - C[i] = A[i]; - if(B) - for(var i in B) - C[i] = B[i]; - this.setOutputData(0,C); - }; - - LiteGraph.registerNodeType("basic/merge_objects", MergeObjects ); - - //Store as variable - function Variable() { - this.size = [60, 30]; - this.addInput("in"); - this.addOutput("out"); - this.properties = { varname: "myname", global: false }; - this.value = null; - } - - Variable.title = "Variable"; - Variable.desc = "store/read variable value"; - - Variable.prototype.onExecute = function() { - this.value = this.getInputData(0); - if(this.graph) - this.graph.vars[ this.properties.varname ] = this.value; - if(this.properties.global) - global[this.properties.varname] = this.value; - this.setOutputData(0, this.value ); - }; - - Variable.prototype.getTitle = function() { - return this.properties.varname; - }; - - LiteGraph.registerNodeType("basic/variable", Variable); - - function length(v) { - if(v && v.length != null) - return Number(v.length); - return 0; - } - - LiteGraph.wrapFunctionAsNode( - "basic/length", - length, - ["*"], - "number" - ); - - function DownloadData() { - this.size = [60, 30]; - this.addInput("data", 0 ); - this.addInput("download", LiteGraph.ACTION ); - this.properties = { filename: "data.json" }; - this.value = null; - var that = this; - this.addWidget("button","Download","", function(v){ - if(!that.value) - return; - that.downloadAsFile(); - }); - } - - DownloadData.title = "Download"; - DownloadData.desc = "Download some data"; - - DownloadData.prototype.downloadAsFile = function() - { - if(this.value == null) - return; - - var str = null; - if(this.value.constructor === String) - str = this.value; - else - str = JSON.stringify(this.value); - - var file = new Blob([str]); - var url = URL.createObjectURL( file ); - var element = document.createElement("a"); - element.setAttribute('href', url); - element.setAttribute('download', this.properties.filename ); - element.style.display = 'none'; - document.body.appendChild(element); - element.click(); - document.body.removeChild(element); - setTimeout( function(){ URL.revokeObjectURL( url ); }, 1000*60 ); //wait one minute to revoke url - } - - DownloadData.prototype.onAction = function(action, param) { - var that = this; - setTimeout( function(){ that.downloadAsFile(); }, 100); //deferred to avoid blocking the renderer with the popup - } - - DownloadData.prototype.onExecute = function() { - if (this.inputs[0]) { - this.value = this.getInputData(0); - } - }; - - DownloadData.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this.properties.filename; - } - return this.title; - }; - - LiteGraph.registerNodeType("basic/download", DownloadData); - - - - //Watch a value in the editor - function Watch() { - this.size = [60, 30]; - this.addInput("value", 0, { label: "" }); - this.value = 0; - } - - Watch.title = "Watch"; - Watch.desc = "Show value of input"; - - Watch.prototype.onExecute = function() { - if (this.inputs[0]) { - this.value = this.getInputData(0); - } - }; - - Watch.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this.inputs[0].label; - } - return this.title; - }; - - Watch.toString = function(o) { - if (o == null) { - return "null"; - } else if (o.constructor === Number) { - return o.toFixed(3); - } else if (o.constructor === Array) { - var str = "["; - for (var i = 0; i < o.length; ++i) { - str += Watch.toString(o[i]) + (i + 1 != o.length ? "," : ""); - } - str += "]"; - return str; - } else { - return String(o); - } - }; - - Watch.prototype.onDrawBackground = function(ctx) { - //show the current value - this.inputs[0].label = Watch.toString(this.value); - }; - - LiteGraph.registerNodeType("basic/watch", Watch); - - //in case one type doesnt match other type but you want to connect them anyway - function Cast() { - this.addInput("in", 0); - this.addOutput("out", 0); - this.size = [40, 30]; - } - - Cast.title = "Cast"; - Cast.desc = "Allows to connect different types"; - - Cast.prototype.onExecute = function() { - this.setOutputData(0, this.getInputData(0)); - }; - - LiteGraph.registerNodeType("basic/cast", Cast); - - //Show value inside the debug console - function Console() { - this.mode = LiteGraph.ON_EVENT; - this.size = [80, 30]; - this.addProperty("msg", ""); - this.addInput("log", LiteGraph.EVENT); - this.addInput("msg", 0); - } - - Console.title = "Console"; - Console.desc = "Show value inside the console"; - - Console.prototype.onAction = function(action, param) { - if (action == "log") { - console.log(param); - } else if (action == "warn") { - console.warn(param); - } else if (action == "error") { - console.error(param); - } - }; - - Console.prototype.onExecute = function() { - var msg = this.getInputData(1); - if (msg !== null) { - this.properties.msg = msg; - } - console.log(msg); - }; - - Console.prototype.onGetInputs = function() { - return [ - ["log", LiteGraph.ACTION], - ["warn", LiteGraph.ACTION], - ["error", LiteGraph.ACTION] - ]; - }; - - LiteGraph.registerNodeType("basic/console", Console); - - //Show value inside the debug console - function Alert() { - this.mode = LiteGraph.ON_EVENT; - this.addProperty("msg", ""); - this.addInput("", LiteGraph.EVENT); - var that = this; - this.widget = this.addWidget("text", "Text", "", function(v) { - that.properties.msg = v; - }); - this.widgets_up = true; - this.size = [200, 30]; - } - - Alert.title = "Alert"; - Alert.desc = "Show an alert window"; - Alert.color = "#510"; - - Alert.prototype.onConfigure = function(o) { - this.widget.value = o.properties.msg; - }; - - Alert.prototype.onAction = function(action, param) { - var msg = this.properties.msg; - setTimeout(function() { - alert(msg); - }, 10); - }; - - LiteGraph.registerNodeType("basic/alert", Alert); - - //Execites simple code - function NodeScript() { - this.size = [60, 30]; - this.addProperty("onExecute", "return A;"); - this.addInput("A", ""); - this.addInput("B", ""); - this.addOutput("out", ""); - - this._func = null; - this.data = {}; - } - - NodeScript.prototype.onConfigure = function(o) { - if (o.properties.onExecute && LiteGraph.allow_scripts) - this.compileCode(o.properties.onExecute); - else - console.warn("Script not compiled, LiteGraph.allow_scripts is false"); - }; - - NodeScript.title = "Script"; - NodeScript.desc = "executes a code (max 100 characters)"; - - NodeScript.widgets_info = { - onExecute: { type: "code" } - }; - - NodeScript.prototype.onPropertyChanged = function(name, value) { - if (name == "onExecute" && LiteGraph.allow_scripts) - this.compileCode(value); - else - console.warn("Script not compiled, LiteGraph.allow_scripts is false"); - }; - - NodeScript.prototype.compileCode = function(code) { - this._func = null; - if (code.length > 256) { - console.warn("Script too long, max 256 chars"); - } else { - var code_low = code.toLowerCase(); - var forbidden_words = [ - "script", - "body", - "document", - "eval", - "nodescript", - "function" - ]; //bad security solution - for (var i = 0; i < forbidden_words.length; ++i) { - if (code_low.indexOf(forbidden_words[i]) != -1) { - console.warn("invalid script"); - return; - } - } - try { - this._func = new Function("A", "B", "C", "DATA", "node", code); - } catch (err) { - console.error("Error parsing script"); - console.error(err); - } - } - }; - - NodeScript.prototype.onExecute = function() { - if (!this._func) { - return; - } - - try { - var A = this.getInputData(0); - var B = this.getInputData(1); - var C = this.getInputData(2); - this.setOutputData(0, this._func(A, B, C, this.data, this)); - } catch (err) { - console.error("Error in script"); - console.error(err); - } - }; - - NodeScript.prototype.onGetOutputs = function() { - return [["C", ""]]; - }; - - LiteGraph.registerNodeType("basic/script", NodeScript); -})(this); - +(function(global) { + // ************************************************************* + // LiteGraph CLASS ******* + // ************************************************************* + + /** + * The Global Scope. It contains all the registered node classes. + * + * @class LiteGraph + * @constructor + */ + + var LiteGraph = (global.LiteGraph = { + VERSION: 0.4, + + CANVAS_GRID_SIZE: 10, + + NODE_TITLE_HEIGHT: 30, + NODE_TITLE_TEXT_Y: 20, + NODE_SLOT_HEIGHT: 20, + NODE_WIDGET_HEIGHT: 20, + NODE_WIDTH: 140, + NODE_MIN_WIDTH: 50, + NODE_COLLAPSED_RADIUS: 10, + NODE_COLLAPSED_WIDTH: 80, + NODE_TITLE_COLOR: "#999", + NODE_TEXT_SIZE: 14, + NODE_TEXT_COLOR: "#AAA", + NODE_SUBTEXT_SIZE: 12, + NODE_DEFAULT_COLOR: "#333", + NODE_DEFAULT_BGCOLOR: "#353535", + NODE_DEFAULT_BOXCOLOR: "#666", + NODE_DEFAULT_SHAPE: "box", + DEFAULT_SHADOW_COLOR: "rgba(0,0,0,0.5)", + DEFAULT_GROUP_FONT: 24, + + WIDGET_BGCOLOR: "#222", + WIDGET_OUTLINE_COLOR: "#666", + WIDGET_TEXT_COLOR: "#DDD", + WIDGET_SECONDARY_TEXT_COLOR: "#999", + + LINK_COLOR: "#9A9", + EVENT_LINK_COLOR: "#A86", + CONNECTING_LINK_COLOR: "#AFA", + + MAX_NUMBER_OF_NODES: 1000, //avoid infinite loops + DEFAULT_POSITION: [100, 100], //default node position + VALID_SHAPES: ["default", "box", "round", "card"], //,"circle" + + //shapes are used for nodes but also for slots + BOX_SHAPE: 1, + ROUND_SHAPE: 2, + CIRCLE_SHAPE: 3, + CARD_SHAPE: 4, + ARROW_SHAPE: 5, + + //enums + INPUT: 1, + OUTPUT: 2, + + EVENT: -1, //for outputs + ACTION: -1, //for inputs + + ALWAYS: 0, + ON_EVENT: 1, + NEVER: 2, + ON_TRIGGER: 3, + + UP: 1, + DOWN: 2, + LEFT: 3, + RIGHT: 4, + CENTER: 5, + + STRAIGHT_LINK: 0, + LINEAR_LINK: 1, + SPLINE_LINK: 2, + + NORMAL_TITLE: 0, + NO_TITLE: 1, + TRANSPARENT_TITLE: 2, + AUTOHIDE_TITLE: 3, + + proxy: null, //used to redirect calls + node_images_path: "", + + debug: false, + catch_exceptions: true, + throw_errors: true, + allow_scripts: false, //if set to true some nodes like Formula would be allowed to evaluate code that comes from unsafe sources (like node configuration), which could lead to exploits + registered_node_types: {}, //nodetypes by string + node_types_by_file_extension: {}, //used for dropping files in the canvas + Nodes: {}, //node types by classname + + searchbox_extras: {}, //used to add extra features to the search box + + /** + * Register a node class so it can be listed when the user wants to create a new one + * @method registerNodeType + * @param {String} type name of the node and path + * @param {Class} base_class class containing the structure of a node + */ + + registerNodeType: function(type, base_class) { + if (!base_class.prototype) { + throw "Cannot register a simple object, it must be a class with a prototype"; + } + base_class.type = type; + + if (LiteGraph.debug) { + console.log("Node registered: " + type); + } + + var categories = type.split("/"); + var classname = base_class.name; + + var pos = type.lastIndexOf("/"); + base_class.category = type.substr(0, pos); + + if (!base_class.title) { + base_class.title = classname; + } + //info.name = name.substr(pos+1,name.length - pos); + + //extend class + if (base_class.prototype) { + //is a class + for (var i in LGraphNode.prototype) { + if (!base_class.prototype[i]) { + base_class.prototype[i] = LGraphNode.prototype[i]; + } + } + } + + var prev = this.registered_node_types[type]; + if(prev) + console.log("replacing node type: " + type); + else + { + if( !Object.hasOwnProperty( base_class.prototype, "shape") ) + Object.defineProperty(base_class.prototype, "shape", { + set: function(v) { + switch (v) { + case "default": + delete this._shape; + break; + case "box": + this._shape = LiteGraph.BOX_SHAPE; + break; + case "round": + this._shape = LiteGraph.ROUND_SHAPE; + break; + case "circle": + this._shape = LiteGraph.CIRCLE_SHAPE; + break; + case "card": + this._shape = LiteGraph.CARD_SHAPE; + break; + default: + this._shape = v; + } + }, + get: function(v) { + return this._shape; + }, + enumerable: true, + configurable: true + }); + + //warnings + if (base_class.prototype.onPropertyChange) { + console.warn( + "LiteGraph node class " + + type + + " has onPropertyChange method, it must be called onPropertyChanged with d at the end" + ); + } + + //used to know which nodes create when dragging files to the canvas + if (base_class.supported_extensions) { + for (var i in base_class.supported_extensions) { + var ext = base_class.supported_extensions[i]; + if(ext && ext.constructor === String) + this.node_types_by_file_extension[ ext.toLowerCase() ] = base_class; + } + } + } + + this.registered_node_types[type] = base_class; + if (base_class.constructor.name) { + this.Nodes[classname] = base_class; + } + if (LiteGraph.onNodeTypeRegistered) { + LiteGraph.onNodeTypeRegistered(type, base_class); + } + if (prev && LiteGraph.onNodeTypeReplaced) { + LiteGraph.onNodeTypeReplaced(type, base_class, prev); + } + }, + + /** + * removes a node type from the system + * @method unregisterNodeType + * @param {String|Object} type name of the node or the node constructor itself + */ + unregisterNodeType: function(type) { + var base_class = type.constructor === String ? this.registered_node_types[type] : type; + if(!base_class) + throw("node type not found: " + type ); + delete this.registered_node_types[base_class.type]; + if(base_class.constructor.name) + delete this.Nodes[base_class.constructor.name]; + }, + + /** + * Create a new nodetype by passing a function, it wraps it with a proper class and generates inputs according to the parameters of the function. + * Useful to wrap simple methods that do not require properties, and that only process some input to generate an output. + * @method wrapFunctionAsNode + * @param {String} name node name with namespace (p.e.: 'math/sum') + * @param {Function} func + * @param {Array} param_types [optional] an array containing the type of every parameter, otherwise parameters will accept any type + * @param {String} return_type [optional] string with the return type, otherwise it will be generic + * @param {Object} properties [optional] properties to be configurable + */ + wrapFunctionAsNode: function( + name, + func, + param_types, + return_type, + properties + ) { + var params = Array(func.length); + var code = ""; + var names = LiteGraph.getParameterNames(func); + for (var i = 0; i < names.length; ++i) { + code += + "this.addInput('" + + names[i] + + "'," + + (param_types && param_types[i] + ? "'" + param_types[i] + "'" + : "0") + + ");\n"; + } + code += + "this.addOutput('out'," + + (return_type ? "'" + return_type + "'" : 0) + + ");\n"; + if (properties) { + code += + "this.properties = " + JSON.stringify(properties) + ";\n"; + } + var classobj = Function(code); + classobj.title = name.split("/").pop(); + classobj.desc = "Generated from " + func.name; + classobj.prototype.onExecute = function onExecute() { + for (var i = 0; i < params.length; ++i) { + params[i] = this.getInputData(i); + } + var r = func.apply(this, params); + this.setOutputData(0, r); + }; + this.registerNodeType(name, classobj); + }, + + /** + * Adds this method to all nodetypes, existing and to be created + * (You can add it to LGraphNode.prototype but then existing node types wont have it) + * @method addNodeMethod + * @param {Function} func + */ + addNodeMethod: function(name, func) { + LGraphNode.prototype[name] = func; + for (var i in this.registered_node_types) { + var type = this.registered_node_types[i]; + if (type.prototype[name]) { + type.prototype["_" + name] = type.prototype[name]; + } //keep old in case of replacing + type.prototype[name] = func; + } + }, + + /** + * Create a node of a given type with a name. The node is not attached to any graph yet. + * @method createNode + * @param {String} type full name of the node class. p.e. "math/sin" + * @param {String} name a name to distinguish from other nodes + * @param {Object} options to set options + */ + + createNode: function(type, title, options) { + var base_class = this.registered_node_types[type]; + if (!base_class) { + if (LiteGraph.debug) { + console.log( + 'GraphNode type "' + type + '" not registered.' + ); + } + return null; + } + + var prototype = base_class.prototype || base_class; + + title = title || base_class.title || type; + + var node = null; + + if (LiteGraph.catch_exceptions) { + try { + node = new base_class(title); + } catch (err) { + console.error(err); + return null; + } + } else { + node = new base_class(title); + } + + node.type = type; + + if (!node.title && title) { + node.title = title; + } + if (!node.properties) { + node.properties = {}; + } + if (!node.properties_info) { + node.properties_info = []; + } + if (!node.flags) { + node.flags = {}; + } + if (!node.size) { + node.size = node.computeSize(); + } + if (!node.pos) { + node.pos = LiteGraph.DEFAULT_POSITION.concat(); + } + if (!node.mode) { + node.mode = LiteGraph.ALWAYS; + } + + //extra options + if (options) { + for (var i in options) { + node[i] = options[i]; + } + } + + return node; + }, + + /** + * Returns a registered node type with a given name + * @method getNodeType + * @param {String} type full name of the node class. p.e. "math/sin" + * @return {Class} the node class + */ + getNodeType: function(type) { + return this.registered_node_types[type]; + }, + + /** + * Returns a list of node types matching one category + * @method getNodeType + * @param {String} category category name + * @return {Array} array with all the node classes + */ + + getNodeTypesInCategory: function(category, filter) { + var r = []; + for (var i in this.registered_node_types) { + var type = this.registered_node_types[i]; + if (filter && type.filter && type.filter != filter) { + continue; + } + + if (category == "") { + if (type.category == null) { + r.push(type); + } + } else if (type.category == category) { + r.push(type); + } + } + + return r; + }, + + /** + * Returns a list with all the node type categories + * @method getNodeTypesCategories + * @return {Array} array with all the names of the categories + */ + getNodeTypesCategories: function( filter ) { + var categories = { "": 1 }; + for (var i in this.registered_node_types) { + var type = this.registered_node_types[i]; + if ( type.category && !type.skip_list ) + { + if(filter && type.filter != filter) + continue; + categories[type.category] = 1; + } + } + var result = []; + for (var i in categories) { + result.push(i); + } + return result; + }, + + //debug purposes: reloads all the js scripts that matches a wildcard + reloadNodes: function(folder_wildcard) { + var tmp = document.getElementsByTagName("script"); + //weird, this array changes by its own, so we use a copy + var script_files = []; + for (var i in tmp) { + script_files.push(tmp[i]); + } + + var docHeadObj = document.getElementsByTagName("head")[0]; + folder_wildcard = document.location.href + folder_wildcard; + + for (var i in script_files) { + var src = script_files[i].src; + if ( + !src || + src.substr(0, folder_wildcard.length) != folder_wildcard + ) { + continue; + } + + try { + if (LiteGraph.debug) { + console.log("Reloading: " + src); + } + var dynamicScript = document.createElement("script"); + dynamicScript.type = "text/javascript"; + dynamicScript.src = src; + docHeadObj.appendChild(dynamicScript); + docHeadObj.removeChild(script_files[i]); + } catch (err) { + if (LiteGraph.throw_errors) { + throw err; + } + if (LiteGraph.debug) { + console.log("Error while reloading " + src); + } + } + } + + if (LiteGraph.debug) { + console.log("Nodes reloaded"); + } + }, + + //separated just to improve if it doesn't work + cloneObject: function(obj, target) { + if (obj == null) { + return null; + } + var r = JSON.parse(JSON.stringify(obj)); + if (!target) { + return r; + } + + for (var i in r) { + target[i] = r[i]; + } + return target; + }, + + /** + * Returns if the types of two slots are compatible (taking into account wildcards, etc) + * @method isValidConnection + * @param {String} type_a + * @param {String} type_b + * @return {Boolean} true if they can be connected + */ + isValidConnection: function(type_a, type_b) { + if ( + !type_a || //generic output + !type_b || //generic input + type_a == type_b || //same type (is valid for triggers) + (type_a == LiteGraph.EVENT && type_b == LiteGraph.ACTION) + ) { + return true; + } + + // Enforce string type to handle toLowerCase call (-1 number not ok) + type_a = String(type_a); + type_b = String(type_b); + type_a = type_a.toLowerCase(); + type_b = type_b.toLowerCase(); + + // For nodes supporting multiple connection types + if (type_a.indexOf(",") == -1 && type_b.indexOf(",") == -1) { + return type_a == type_b; + } + + // Check all permutations to see if one is valid + var supported_types_a = type_a.split(","); + var supported_types_b = type_b.split(","); + for (var i = 0; i < supported_types_a.length; ++i) { + for (var j = 0; j < supported_types_b.length; ++j) { + if (supported_types_a[i] == supported_types_b[j]) { + return true; + } + } + } + + return false; + }, + + /** + * Register a string in the search box so when the user types it it will recommend this node + * @method registerSearchboxExtra + * @param {String} node_type the node recommended + * @param {String} description text to show next to it + * @param {Object} data it could contain info of how the node should be configured + * @return {Boolean} true if they can be connected + */ + registerSearchboxExtra: function(node_type, description, data) { + this.searchbox_extras[description.toLowerCase()] = { + type: node_type, + desc: description, + data: data + }; + }, + + /** + * Wrapper to load files (from url using fetch or from file using FileReader) + * @method fetchFile + * @param {String|File|Blob} url the url of the file (or the file itself) + * @param {String} type an string to know how to fetch it: "text","arraybuffer","json","blob" + * @param {Function} on_complete callback(data) + * @param {Function} on_error in case of an error + * @return {FileReader|Promise} returns the object used to + */ + fetchFile: function( url, type, on_complete, on_error ) { + var that = this; + if(!url) + return null; + + type = type || "text"; + if( url.constructor === String ) + { + if (url.substr(0, 4) == "http" && LiteGraph.proxy) { + url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); + } + return fetch(url) + .then(function(response) { + if(!response.ok) + throw new Error("File not found"); //it will be catch below + if(type == "arraybuffer") + return response.arrayBuffer(); + else if(type == "text" || type == "string") + return response.text(); + else if(type == "json") + return response.json(); + else if(type == "blob") + return response.blob(); + }) + .then(function(data) { + if(on_complete) + on_complete(data); + }) + .catch(function(error) { + console.error("error fetching file:",url); + if(on_error) + on_error(error); + }); + } + else if( url.constructor === File || url.constructor === Blob) + { + var reader = new FileReader(); + reader.onload = function(e) + { + var v = e.target.result; + if( type == "json" ) + v = JSON.parse(v); + if(on_complete) + on_complete(v); + } + if(type == "arraybuffer") + return reader.readAsArrayBuffer(url); + else if(type == "text" || type == "json") + return reader.readAsText(url); + else if(type == "blob") + return reader.readAsBinaryString(url); + } + return null; + } + }); + + //timer that works everywhere + if (typeof performance != "undefined") { + LiteGraph.getTime = performance.now.bind(performance); + } else if (typeof Date != "undefined" && Date.now) { + LiteGraph.getTime = Date.now.bind(Date); + } else if (typeof process != "undefined") { + LiteGraph.getTime = function() { + var t = process.hrtime(); + return t[0] * 0.001 + t[1] * 1e-6; + }; + } else { + LiteGraph.getTime = function getTime() { + return new Date().getTime(); + }; + } + + //********************************************************************************* + // LGraph CLASS + //********************************************************************************* + + /** + * LGraph is the class that contain a full graph. We instantiate one and add nodes to it, and then we can run the execution loop. + * + * @class LGraph + * @constructor + * @param {Object} o data from previous serialization [optional] + */ + + function LGraph(o) { + if (LiteGraph.debug) { + console.log("Graph created"); + } + this.list_of_graphcanvas = null; + this.clear(); + + if (o) { + this.configure(o); + } + } + + global.LGraph = LiteGraph.LGraph = LGraph; + + //default supported types + LGraph.supported_types = ["number", "string", "boolean"]; + + //used to know which types of connections support this graph (some graphs do not allow certain types) + LGraph.prototype.getSupportedTypes = function() { + return this.supported_types || LGraph.supported_types; + }; + + LGraph.STATUS_STOPPED = 1; + LGraph.STATUS_RUNNING = 2; + + /** + * Removes all nodes from this graph + * @method clear + */ + + LGraph.prototype.clear = function() { + this.stop(); + this.status = LGraph.STATUS_STOPPED; + + this.last_node_id = 0; + this.last_link_id = 0; + + this._version = -1; //used to detect changes + + //safe clear + if (this._nodes) { + for (var i = 0; i < this._nodes.length; ++i) { + var node = this._nodes[i]; + if (node.onRemoved) { + node.onRemoved(); + } + } + } + + //nodes + this._nodes = []; + this._nodes_by_id = {}; + this._nodes_in_order = []; //nodes that are executable sorted in execution order + this._nodes_executable = null; //nodes that contain onExecute + + //other scene stuff + this._groups = []; + + //links + this.links = {}; //container with all the links + + //iterations + this.iteration = 0; + + //custom data + this.config = {}; + this.vars = {}; + + //timing + this.globaltime = 0; + this.runningtime = 0; + this.fixedtime = 0; + this.fixedtime_lapse = 0.01; + this.elapsed_time = 0.01; + this.last_update_time = 0; + this.starttime = 0; + + this.catch_errors = true; + + //subgraph_data + this.inputs = {}; + this.outputs = {}; + + //notify canvas to redraw + this.change(); + + this.sendActionToCanvas("clear"); + }; + + /** + * Attach Canvas to this graph + * @method attachCanvas + * @param {GraphCanvas} graph_canvas + */ + + LGraph.prototype.attachCanvas = function(graphcanvas) { + if (graphcanvas.constructor != LGraphCanvas) { + throw "attachCanvas expects a LGraphCanvas instance"; + } + if (graphcanvas.graph && graphcanvas.graph != this) { + graphcanvas.graph.detachCanvas(graphcanvas); + } + + graphcanvas.graph = this; + if (!this.list_of_graphcanvas) { + this.list_of_graphcanvas = []; + } + this.list_of_graphcanvas.push(graphcanvas); + }; + + /** + * Detach Canvas from this graph + * @method detachCanvas + * @param {GraphCanvas} graph_canvas + */ + LGraph.prototype.detachCanvas = function(graphcanvas) { + if (!this.list_of_graphcanvas) { + return; + } + + var pos = this.list_of_graphcanvas.indexOf(graphcanvas); + if (pos == -1) { + return; + } + graphcanvas.graph = null; + this.list_of_graphcanvas.splice(pos, 1); + }; + + /** + * Starts running this graph every interval milliseconds. + * @method start + * @param {number} interval amount of milliseconds between executions, if 0 then it renders to the monitor refresh rate + */ + + LGraph.prototype.start = function(interval) { + if (this.status == LGraph.STATUS_RUNNING) { + return; + } + this.status = LGraph.STATUS_RUNNING; + + if (this.onPlayEvent) { + this.onPlayEvent(); + } + + this.sendEventToAllNodes("onStart"); + + //launch + this.starttime = LiteGraph.getTime(); + this.last_update_time = this.starttime; + interval = interval || 0; + var that = this; + + //execute once per frame + if ( interval == 0 && typeof window != "undefined" && window.requestAnimationFrame ) { + function on_frame() { + if (that.execution_timer_id != -1) { + return; + } + window.requestAnimationFrame(on_frame); + if(that.onBeforeStep) + that.onBeforeStep(); + that.runStep(1, !this.catch_errors); + if(that.onAfterStep) + that.onAfterStep(); + } + this.execution_timer_id = -1; + on_frame(); + } else { //execute every 'interval' ms + this.execution_timer_id = setInterval(function() { + //execute + if(that.onBeforeStep) + that.onBeforeStep(); + that.runStep(1, !this.catch_errors); + if(that.onAfterStep) + that.onAfterStep(); + }, interval); + } + }; + + /** + * Stops the execution loop of the graph + * @method stop execution + */ + + LGraph.prototype.stop = function() { + if (this.status == LGraph.STATUS_STOPPED) { + return; + } + + this.status = LGraph.STATUS_STOPPED; + + if (this.onStopEvent) { + this.onStopEvent(); + } + + if (this.execution_timer_id != null) { + if (this.execution_timer_id != -1) { + clearInterval(this.execution_timer_id); + } + this.execution_timer_id = null; + } + + this.sendEventToAllNodes("onStop"); + }; + + /** + * Run N steps (cycles) of the graph + * @method runStep + * @param {number} num number of steps to run, default is 1 + * @param {Boolean} do_not_catch_errors [optional] if you want to try/catch errors + * @param {number} limit max number of nodes to execute (used to execute from start to a node) + */ + + LGraph.prototype.runStep = function(num, do_not_catch_errors, limit ) { + num = num || 1; + + var start = LiteGraph.getTime(); + this.globaltime = 0.001 * (start - this.starttime); + + var nodes = this._nodes_executable + ? this._nodes_executable + : this._nodes; + if (!nodes) { + return; + } + + limit = limit || nodes.length; + + if (do_not_catch_errors) { + //iterations + for (var i = 0; i < num; i++) { + for (var j = 0; j < limit; ++j) { + var node = nodes[j]; + if (node.mode == LiteGraph.ALWAYS && node.onExecute) { + node.onExecute(); //hard to send elapsed time + } + } + + this.fixedtime += this.fixedtime_lapse; + if (this.onExecuteStep) { + this.onExecuteStep(); + } + } + + if (this.onAfterExecute) { + this.onAfterExecute(); + } + } else { + try { + //iterations + for (var i = 0; i < num; i++) { + for (var j = 0; j < limit; ++j) { + var node = nodes[j]; + if (node.mode == LiteGraph.ALWAYS && node.onExecute) { + node.onExecute(); + } + } + + this.fixedtime += this.fixedtime_lapse; + if (this.onExecuteStep) { + this.onExecuteStep(); + } + } + + if (this.onAfterExecute) { + this.onAfterExecute(); + } + this.errors_in_execution = false; + } catch (err) { + this.errors_in_execution = true; + if (LiteGraph.throw_errors) { + throw err; + } + if (LiteGraph.debug) { + console.log("Error during execution: " + err); + } + this.stop(); + } + } + + var now = LiteGraph.getTime(); + var elapsed = now - start; + if (elapsed == 0) { + elapsed = 1; + } + this.execution_time = 0.001 * elapsed; + this.globaltime += 0.001 * elapsed; + this.iteration += 1; + this.elapsed_time = (now - this.last_update_time) * 0.001; + this.last_update_time = now; + }; + + /** + * Updates the graph execution order according to relevance of the nodes (nodes with only outputs have more relevance than + * nodes with only inputs. + * @method updateExecutionOrder + */ + LGraph.prototype.updateExecutionOrder = function() { + this._nodes_in_order = this.computeExecutionOrder(false); + this._nodes_executable = []; + for (var i = 0; i < this._nodes_in_order.length; ++i) { + if (this._nodes_in_order[i].onExecute) { + this._nodes_executable.push(this._nodes_in_order[i]); + } + } + }; + + //This is more internal, it computes the executable nodes in order and returns it + LGraph.prototype.computeExecutionOrder = function( + only_onExecute, + set_level + ) { + var L = []; + var S = []; + var M = {}; + var visited_links = {}; //to avoid repeating links + var remaining_links = {}; //to a + + //search for the nodes without inputs (starting nodes) + for (var i = 0, l = this._nodes.length; i < l; ++i) { + var node = this._nodes[i]; + if (only_onExecute && !node.onExecute) { + continue; + } + + M[node.id] = node; //add to pending nodes + + var num = 0; //num of input connections + if (node.inputs) { + for (var j = 0, l2 = node.inputs.length; j < l2; j++) { + if (node.inputs[j] && node.inputs[j].link != null) { + num += 1; + } + } + } + + if (num == 0) { + //is a starting node + S.push(node); + if (set_level) { + node._level = 1; + } + } //num of input links + else { + if (set_level) { + node._level = 0; + } + remaining_links[node.id] = num; + } + } + + while (true) { + if (S.length == 0) { + break; + } + + //get an starting node + var node = S.shift(); + L.push(node); //add to ordered list + delete M[node.id]; //remove from the pending nodes + + if (!node.outputs) { + continue; + } + + //for every output + for (var i = 0; i < node.outputs.length; i++) { + var output = node.outputs[i]; + //not connected + if ( + output == null || + output.links == null || + output.links.length == 0 + ) { + continue; + } + + //for every connection + for (var j = 0; j < output.links.length; j++) { + var link_id = output.links[j]; + var link = this.links[link_id]; + if (!link) { + continue; + } + + //already visited link (ignore it) + if (visited_links[link.id]) { + continue; + } + + var target_node = this.getNodeById(link.target_id); + if (target_node == null) { + visited_links[link.id] = true; + continue; + } + + if ( + set_level && + (!target_node._level || + target_node._level <= node._level) + ) { + target_node._level = node._level + 1; + } + + visited_links[link.id] = true; //mark as visited + remaining_links[target_node.id] -= 1; //reduce the number of links remaining + if (remaining_links[target_node.id] == 0) { + S.push(target_node); + } //if no more links, then add to starters array + } + } + } + + //the remaining ones (loops) + for (var i in M) { + L.push(M[i]); + } + + if (L.length != this._nodes.length && LiteGraph.debug) { + console.warn("something went wrong, nodes missing"); + } + + var l = L.length; + + //save order number in the node + for (var i = 0; i < l; ++i) { + L[i].order = i; + } + + //sort now by priority + L = L.sort(function(A, B) { + var Ap = A.constructor.priority || A.priority || 0; + var Bp = B.constructor.priority || B.priority || 0; + if (Ap == Bp) { + //if same priority, sort by order + return A.order - B.order; + } + return Ap - Bp; //sort by priority + }); + + //save order number in the node, again... + for (var i = 0; i < l; ++i) { + L[i].order = i; + } + + return L; + }; + + /** + * Returns all the nodes that could affect this one (ancestors) by crawling all the inputs recursively. + * It doesn't include the node itself + * @method getAncestors + * @return {Array} an array with all the LGraphNodes that affect this node, in order of execution + */ + LGraph.prototype.getAncestors = function(node) { + var ancestors = []; + var pending = [node]; + var visited = {}; + + while (pending.length) { + var current = pending.shift(); + if (!current.inputs) { + continue; + } + if (!visited[current.id] && current != node) { + visited[current.id] = true; + ancestors.push(current); + } + + for (var i = 0; i < current.inputs.length; ++i) { + var input = current.getInputNode(i); + if (input && ancestors.indexOf(input) == -1) { + pending.push(input); + } + } + } + + ancestors.sort(function(a, b) { + return a.order - b.order; + }); + return ancestors; + }; + + /** + * Positions every node in a more readable manner + * @method arrange + */ + LGraph.prototype.arrange = function(margin) { + margin = margin || 100; + + var nodes = this.computeExecutionOrder(false, true); + var columns = []; + for (var i = 0; i < nodes.length; ++i) { + var node = nodes[i]; + var col = node._level || 1; + if (!columns[col]) { + columns[col] = []; + } + columns[col].push(node); + } + + var x = margin; + + for (var i = 0; i < columns.length; ++i) { + var column = columns[i]; + if (!column) { + continue; + } + var max_size = 100; + var y = margin + LiteGraph.NODE_TITLE_HEIGHT; + for (var j = 0; j < column.length; ++j) { + var node = column[j]; + node.pos[0] = x; + node.pos[1] = y; + if (node.size[0] > max_size) { + max_size = node.size[0]; + } + y += node.size[1] + margin + LiteGraph.NODE_TITLE_HEIGHT; + } + x += max_size + margin; + } + + this.setDirtyCanvas(true, true); + }; + + /** + * Returns the amount of time the graph has been running in milliseconds + * @method getTime + * @return {number} number of milliseconds the graph has been running + */ + LGraph.prototype.getTime = function() { + return this.globaltime; + }; + + /** + * Returns the amount of time accumulated using the fixedtime_lapse var. This is used in context where the time increments should be constant + * @method getFixedTime + * @return {number} number of milliseconds the graph has been running + */ + + LGraph.prototype.getFixedTime = function() { + return this.fixedtime; + }; + + /** + * Returns the amount of time it took to compute the latest iteration. Take into account that this number could be not correct + * if the nodes are using graphical actions + * @method getElapsedTime + * @return {number} number of milliseconds it took the last cycle + */ + + LGraph.prototype.getElapsedTime = function() { + return this.elapsed_time; + }; + + /** + * Sends an event to all the nodes, useful to trigger stuff + * @method sendEventToAllNodes + * @param {String} eventname the name of the event (function to be called) + * @param {Array} params parameters in array format + */ + LGraph.prototype.sendEventToAllNodes = function(eventname, params, mode) { + mode = mode || LiteGraph.ALWAYS; + + var nodes = this._nodes_in_order ? this._nodes_in_order : this._nodes; + if (!nodes) { + return; + } + + for (var j = 0, l = nodes.length; j < l; ++j) { + var node = nodes[j]; + + if ( + node.constructor === LiteGraph.Subgraph && + eventname != "onExecute" + ) { + if (node.mode == mode) { + node.sendEventToAllNodes(eventname, params, mode); + } + continue; + } + + if (!node[eventname] || node.mode != mode) { + continue; + } + if (params === undefined) { + node[eventname](); + } else if (params && params.constructor === Array) { + node[eventname].apply(node, params); + } else { + node[eventname](params); + } + } + }; + + LGraph.prototype.sendActionToCanvas = function(action, params) { + if (!this.list_of_graphcanvas) { + return; + } + + for (var i = 0; i < this.list_of_graphcanvas.length; ++i) { + var c = this.list_of_graphcanvas[i]; + if (c[action]) { + c[action].apply(c, params); + } + } + }; + + /** + * Adds a new node instance to this graph + * @method add + * @param {LGraphNode} node the instance of the node + */ + + LGraph.prototype.add = function(node, skip_compute_order) { + if (!node) { + return; + } + + //groups + if (node.constructor === LGraphGroup) { + this._groups.push(node); + this.setDirtyCanvas(true); + this.change(); + node.graph = this; + this._version++; + return; + } + + //nodes + if (node.id != -1 && this._nodes_by_id[node.id] != null) { + console.warn( + "LiteGraph: there is already a node with this ID, changing it" + ); + node.id = ++this.last_node_id; + } + + if (this._nodes.length >= LiteGraph.MAX_NUMBER_OF_NODES) { + throw "LiteGraph: max number of nodes in a graph reached"; + } + + //give him an id + if (node.id == null || node.id == -1) { + node.id = ++this.last_node_id; + } else if (this.last_node_id < node.id) { + this.last_node_id = node.id; + } + + node.graph = this; + this._version++; + + this._nodes.push(node); + this._nodes_by_id[node.id] = node; + + if (node.onAdded) { + node.onAdded(this); + } + + if (this.config.align_to_grid) { + node.alignToGrid(); + } + + if (!skip_compute_order) { + this.updateExecutionOrder(); + } + + if (this.onNodeAdded) { + this.onNodeAdded(node); + } + + this.setDirtyCanvas(true); + this.change(); + + return node; //to chain actions + }; + + /** + * Removes a node from the graph + * @method remove + * @param {LGraphNode} node the instance of the node + */ + + LGraph.prototype.remove = function(node) { + if (node.constructor === LiteGraph.LGraphGroup) { + var index = this._groups.indexOf(node); + if (index != -1) { + this._groups.splice(index, 1); + } + node.graph = null; + this._version++; + this.setDirtyCanvas(true, true); + this.change(); + return; + } + + if (this._nodes_by_id[node.id] == null) { + return; + } //not found + + if (node.ignore_remove) { + return; + } //cannot be removed + + //disconnect inputs + if (node.inputs) { + for (var i = 0; i < node.inputs.length; i++) { + var slot = node.inputs[i]; + if (slot.link != null) { + node.disconnectInput(i); + } + } + } + + //disconnect outputs + if (node.outputs) { + for (var i = 0; i < node.outputs.length; i++) { + var slot = node.outputs[i]; + if (slot.links != null && slot.links.length) { + node.disconnectOutput(i); + } + } + } + + //node.id = -1; //why? + + //callback + if (node.onRemoved) { + node.onRemoved(); + } + + node.graph = null; + this._version++; + + //remove from canvas render + if (this.list_of_graphcanvas) { + for (var i = 0; i < this.list_of_graphcanvas.length; ++i) { + var canvas = this.list_of_graphcanvas[i]; + if (canvas.selected_nodes[node.id]) { + delete canvas.selected_nodes[node.id]; + } + if (canvas.node_dragged == node) { + canvas.node_dragged = null; + } + } + } + + //remove from containers + var pos = this._nodes.indexOf(node); + if (pos != -1) { + this._nodes.splice(pos, 1); + } + delete this._nodes_by_id[node.id]; + + if (this.onNodeRemoved) { + this.onNodeRemoved(node); + } + + this.setDirtyCanvas(true, true); + this.change(); + + this.updateExecutionOrder(); + }; + + /** + * Returns a node by its id. + * @method getNodeById + * @param {Number} id + */ + + LGraph.prototype.getNodeById = function(id) { + if (id == null) { + return null; + } + return this._nodes_by_id[id]; + }; + + /** + * Returns a list of nodes that matches a class + * @method findNodesByClass + * @param {Class} classObject the class itself (not an string) + * @return {Array} a list with all the nodes of this type + */ + LGraph.prototype.findNodesByClass = function(classObject, result) { + result = result || []; + result.length = 0; + for (var i = 0, l = this._nodes.length; i < l; ++i) { + if (this._nodes[i].constructor === classObject) { + result.push(this._nodes[i]); + } + } + return result; + }; + + /** + * Returns a list of nodes that matches a type + * @method findNodesByType + * @param {String} type the name of the node type + * @return {Array} a list with all the nodes of this type + */ + LGraph.prototype.findNodesByType = function(type, result) { + var type = type.toLowerCase(); + result = result || []; + result.length = 0; + for (var i = 0, l = this._nodes.length; i < l; ++i) { + if (this._nodes[i].type.toLowerCase() == type) { + result.push(this._nodes[i]); + } + } + return result; + }; + + /** + * Returns the first node that matches a name in its title + * @method findNodeByTitle + * @param {String} name the name of the node to search + * @return {Node} the node or null + */ + LGraph.prototype.findNodeByTitle = function(title) { + for (var i = 0, l = this._nodes.length; i < l; ++i) { + if (this._nodes[i].title == title) { + return this._nodes[i]; + } + } + return null; + }; + + /** + * Returns a list of nodes that matches a name + * @method findNodesByTitle + * @param {String} name the name of the node to search + * @return {Array} a list with all the nodes with this name + */ + LGraph.prototype.findNodesByTitle = function(title) { + var result = []; + for (var i = 0, l = this._nodes.length; i < l; ++i) { + if (this._nodes[i].title == title) { + result.push(this._nodes[i]); + } + } + return result; + }; + + /** + * Returns the top-most node in this position of the canvas + * @method getNodeOnPos + * @param {number} x the x coordinate in canvas space + * @param {number} y the y coordinate in canvas space + * @param {Array} nodes_list a list with all the nodes to search from, by default is all the nodes in the graph + * @return {LGraphNode} the node at this position or null + */ + LGraph.prototype.getNodeOnPos = function(x, y, nodes_list, margin) { + nodes_list = nodes_list || this._nodes; + for (var i = nodes_list.length - 1; i >= 0; i--) { + var n = nodes_list[i]; + if (n.isPointInside(x, y, margin)) { + return n; + } + } + return null; + }; + + /** + * Returns the top-most group in that position + * @method getGroupOnPos + * @param {number} x the x coordinate in canvas space + * @param {number} y the y coordinate in canvas space + * @return {LGraphGroup} the group or null + */ + LGraph.prototype.getGroupOnPos = function(x, y) { + for (var i = this._groups.length - 1; i >= 0; i--) { + var g = this._groups[i]; + if (g.isPointInside(x, y, 2, true)) { + return g; + } + } + return null; + }; + + /** + * Checks that the node type matches the node type registered, used when replacing a nodetype by a newer version during execution + * this replaces the ones using the old version with the new version + * @method checkNodeTypes + */ + LGraph.prototype.checkNodeTypes = function() { + var changes = false; + for (var i = 0; i < this._nodes.length; i++) { + var node = this._nodes[i]; + var ctor = LiteGraph.registered_node_types[node.type]; + if (node.constructor == ctor) { + continue; + } + console.log("node being replaced by newer version: " + node.type); + var newnode = LiteGraph.createNode(node.type); + changes = true; + this._nodes[i] = newnode; + newnode.configure(node.serialize()); + newnode.graph = this; + this._nodes_by_id[newnode.id] = newnode; + if (node.inputs) { + newnode.inputs = node.inputs.concat(); + } + if (node.outputs) { + newnode.outputs = node.outputs.concat(); + } + } + this.updateExecutionOrder(); + }; + + // ********** GLOBALS ***************** + + LGraph.prototype.onAction = function(action, param) { + this._input_nodes = this.findNodesByClass( + LiteGraph.GraphInput, + this._input_nodes + ); + for (var i = 0; i < this._input_nodes.length; ++i) { + var node = this._input_nodes[i]; + if (node.properties.name != action) { + continue; + } + node.onAction(action, param); + break; + } + }; + + LGraph.prototype.trigger = function(action, param) { + if (this.onTrigger) { + this.onTrigger(action, param); + } + }; + + /** + * Tell this graph it has a global graph input of this type + * @method addGlobalInput + * @param {String} name + * @param {String} type + * @param {*} value [optional] + */ + LGraph.prototype.addInput = function(name, type, value) { + var input = this.inputs[name]; + if (input) { + //already exist + return; + } + + this.inputs[name] = { name: name, type: type, value: value }; + this._version++; + + if (this.onInputAdded) { + this.onInputAdded(name, type); + } + + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + }; + + /** + * Assign a data to the global graph input + * @method setGlobalInputData + * @param {String} name + * @param {*} data + */ + LGraph.prototype.setInputData = function(name, data) { + var input = this.inputs[name]; + if (!input) { + return; + } + input.value = data; + }; + + /** + * Returns the current value of a global graph input + * @method getInputData + * @param {String} name + * @return {*} the data + */ + LGraph.prototype.getInputData = function(name) { + var input = this.inputs[name]; + if (!input) { + return null; + } + return input.value; + }; + + /** + * Changes the name of a global graph input + * @method renameInput + * @param {String} old_name + * @param {String} new_name + */ + LGraph.prototype.renameInput = function(old_name, name) { + if (name == old_name) { + return; + } + + if (!this.inputs[old_name]) { + return false; + } + + if (this.inputs[name]) { + console.error("there is already one input with that name"); + return false; + } + + this.inputs[name] = this.inputs[old_name]; + delete this.inputs[old_name]; + this._version++; + + if (this.onInputRenamed) { + this.onInputRenamed(old_name, name); + } + + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + }; + + /** + * Changes the type of a global graph input + * @method changeInputType + * @param {String} name + * @param {String} type + */ + LGraph.prototype.changeInputType = function(name, type) { + if (!this.inputs[name]) { + return false; + } + + if ( + this.inputs[name].type && + String(this.inputs[name].type).toLowerCase() == + String(type).toLowerCase() + ) { + return; + } + + this.inputs[name].type = type; + this._version++; + if (this.onInputTypeChanged) { + this.onInputTypeChanged(name, type); + } + }; + + /** + * Removes a global graph input + * @method removeInput + * @param {String} name + * @param {String} type + */ + LGraph.prototype.removeInput = function(name) { + if (!this.inputs[name]) { + return false; + } + + delete this.inputs[name]; + this._version++; + + if (this.onInputRemoved) { + this.onInputRemoved(name); + } + + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + return true; + }; + + /** + * Creates a global graph output + * @method addOutput + * @param {String} name + * @param {String} type + * @param {*} value + */ + LGraph.prototype.addOutput = function(name, type, value) { + this.outputs[name] = { name: name, type: type, value: value }; + this._version++; + + if (this.onOutputAdded) { + this.onOutputAdded(name, type); + } + + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + }; + + /** + * Assign a data to the global output + * @method setOutputData + * @param {String} name + * @param {String} value + */ + LGraph.prototype.setOutputData = function(name, value) { + var output = this.outputs[name]; + if (!output) { + return; + } + output.value = value; + }; + + /** + * Returns the current value of a global graph output + * @method getOutputData + * @param {String} name + * @return {*} the data + */ + LGraph.prototype.getOutputData = function(name) { + var output = this.outputs[name]; + if (!output) { + return null; + } + return output.value; + }; + + /** + * Renames a global graph output + * @method renameOutput + * @param {String} old_name + * @param {String} new_name + */ + LGraph.prototype.renameOutput = function(old_name, name) { + if (!this.outputs[old_name]) { + return false; + } + + if (this.outputs[name]) { + console.error("there is already one output with that name"); + return false; + } + + this.outputs[name] = this.outputs[old_name]; + delete this.outputs[old_name]; + this._version++; + + if (this.onOutputRenamed) { + this.onOutputRenamed(old_name, name); + } + + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + }; + + /** + * Changes the type of a global graph output + * @method changeOutputType + * @param {String} name + * @param {String} type + */ + LGraph.prototype.changeOutputType = function(name, type) { + if (!this.outputs[name]) { + return false; + } + + if ( + this.outputs[name].type && + String(this.outputs[name].type).toLowerCase() == + String(type).toLowerCase() + ) { + return; + } + + this.outputs[name].type = type; + this._version++; + if (this.onOutputTypeChanged) { + this.onOutputTypeChanged(name, type); + } + }; + + /** + * Removes a global graph output + * @method removeOutput + * @param {String} name + */ + LGraph.prototype.removeOutput = function(name) { + if (!this.outputs[name]) { + return false; + } + delete this.outputs[name]; + this._version++; + + if (this.onOutputRemoved) { + this.onOutputRemoved(name); + } + + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + return true; + }; + + LGraph.prototype.triggerInput = function(name, value) { + var nodes = this.findNodesByTitle(name); + for (var i = 0; i < nodes.length; ++i) { + nodes[i].onTrigger(value); + } + }; + + LGraph.prototype.setCallback = function(name, func) { + var nodes = this.findNodesByTitle(name); + for (var i = 0; i < nodes.length; ++i) { + nodes[i].setTrigger(func); + } + }; + + LGraph.prototype.connectionChange = function(node, link_info) { + this.updateExecutionOrder(); + if (this.onConnectionChange) { + this.onConnectionChange(node); + } + this._version++; + this.sendActionToCanvas("onConnectionChange"); + }; + + /** + * returns if the graph is in live mode + * @method isLive + */ + + LGraph.prototype.isLive = function() { + if (!this.list_of_graphcanvas) { + return false; + } + + for (var i = 0; i < this.list_of_graphcanvas.length; ++i) { + var c = this.list_of_graphcanvas[i]; + if (c.live_mode) { + return true; + } + } + return false; + }; + + /** + * clears the triggered slot animation in all links (stop visual animation) + * @method clearTriggeredSlots + */ + LGraph.prototype.clearTriggeredSlots = function() { + for (var i in this.links) { + var link_info = this.links[i]; + if (!link_info) { + continue; + } + if (link_info._last_time) { + link_info._last_time = 0; + } + } + }; + + /* Called when something visually changed (not the graph!) */ + LGraph.prototype.change = function() { + if (LiteGraph.debug) { + console.log("Graph changed"); + } + this.sendActionToCanvas("setDirty", [true, true]); + if (this.on_change) { + this.on_change(this); + } + }; + + LGraph.prototype.setDirtyCanvas = function(fg, bg) { + this.sendActionToCanvas("setDirty", [fg, bg]); + }; + + /** + * Destroys a link + * @method removeLink + * @param {Number} link_id + */ + LGraph.prototype.removeLink = function(link_id) { + var link = this.links[link_id]; + if (!link) { + return; + } + var node = this.getNodeById(link.target_id); + if (node) { + node.disconnectInput(link.target_slot); + } + }; + + //save and recover app state *************************************** + /** + * Creates a Object containing all the info about this graph, it can be serialized + * @method serialize + * @return {Object} value of the node + */ + LGraph.prototype.serialize = function() { + var nodes_info = []; + for (var i = 0, l = this._nodes.length; i < l; ++i) { + nodes_info.push(this._nodes[i].serialize()); + } + + //pack link info into a non-verbose format + var links = []; + for (var i in this.links) { + //links is an OBJECT + var link = this.links[i]; + if (!link.serialize) { + //weird bug I havent solved yet + console.warn( + "weird LLink bug, link info is not a LLink but a regular object" + ); + var link2 = new LLink(); + for (var i in link) { + link2[i] = link[i]; + } + this.links[i] = link2; + link = link2; + } + + links.push(link.serialize()); + } + + var groups_info = []; + for (var i = 0; i < this._groups.length; ++i) { + groups_info.push(this._groups[i].serialize()); + } + + var data = { + last_node_id: this.last_node_id, + last_link_id: this.last_link_id, + nodes: nodes_info, + links: links, + groups: groups_info, + config: this.config, + version: LiteGraph.VERSION + }; + + return data; + }; + + /** + * Configure a graph from a JSON string + * @method configure + * @param {String} str configure a graph from a JSON string + * @param {Boolean} returns if there was any error parsing + */ + LGraph.prototype.configure = function(data, keep_old) { + if (!data) { + return; + } + + if (!keep_old) { + this.clear(); + } + + var nodes = data.nodes; + + //decode links info (they are very verbose) + if (data.links && data.links.constructor === Array) { + var links = []; + for (var i = 0; i < data.links.length; ++i) { + var link_data = data.links[i]; + if(!link_data) //weird bug + { + console.warn("serialized graph link data contains errors, skipping."); + continue; + } + var link = new LLink(); + link.configure(link_data); + links[link.id] = link; + } + data.links = links; + } + + //copy all stored fields + for (var i in data) { + if(i == "nodes" || i == "groups" ) //links must be accepted + continue; + this[i] = data[i]; + } + + var error = false; + + //create nodes + this._nodes = []; + if (nodes) { + for (var i = 0, l = nodes.length; i < l; ++i) { + var n_info = nodes[i]; //stored info + var node = LiteGraph.createNode(n_info.type, n_info.title); + if (!node) { + if (LiteGraph.debug) { + console.log( + "Node not found or has errors: " + n_info.type + ); + } + + //in case of error we create a replacement node to avoid losing info + node = new LGraphNode(); + node.last_serialization = n_info; + node.has_errors = true; + error = true; + //continue; + } + + node.id = n_info.id; //id it or it will create a new id + this.add(node, true); //add before configure, otherwise configure cannot create links + } + + //configure nodes afterwards so they can reach each other + for (var i = 0, l = nodes.length; i < l; ++i) { + var n_info = nodes[i]; + var node = this.getNodeById(n_info.id); + if (node) { + node.configure(n_info); + } + } + } + + //groups + this._groups.length = 0; + if (data.groups) { + for (var i = 0; i < data.groups.length; ++i) { + var group = new LiteGraph.LGraphGroup(); + group.configure(data.groups[i]); + this.add(group); + } + } + + this.updateExecutionOrder(); + this._version++; + this.setDirtyCanvas(true, true); + return error; + }; + + LGraph.prototype.load = function(url) { + var that = this; + var req = new XMLHttpRequest(); + req.open("GET", url, true); + req.send(null); + req.onload = function(oEvent) { + if (req.status !== 200) { + console.error("Error loading graph:", req.status, req.response); + return; + } + var data = JSON.parse(req.response); + that.configure(data); + }; + req.onerror = function(err) { + console.error("Error loading graph:", err); + }; + }; + + LGraph.prototype.onNodeTrace = function(node, msg, color) { + //TODO + }; + + //this is the class in charge of storing link information + function LLink(id, type, origin_id, origin_slot, target_id, target_slot) { + this.id = id; + this.type = type; + this.origin_id = origin_id; + this.origin_slot = origin_slot; + this.target_id = target_id; + this.target_slot = target_slot; + + this._data = null; + this._pos = new Float32Array(2); //center + } + + LLink.prototype.configure = function(o) { + if (o.constructor === Array) { + this.id = o[0]; + this.origin_id = o[1]; + this.origin_slot = o[2]; + this.target_id = o[3]; + this.target_slot = o[4]; + this.type = o[5]; + } else { + this.id = o.id; + this.type = o.type; + this.origin_id = o.origin_id; + this.origin_slot = o.origin_slot; + this.target_id = o.target_id; + this.target_slot = o.target_slot; + } + }; + + LLink.prototype.serialize = function() { + return [ + this.id, + this.origin_id, + this.origin_slot, + this.target_id, + this.target_slot, + this.type + ]; + }; + + LiteGraph.LLink = LLink; + + // ************************************************************* + // Node CLASS ******* + // ************************************************************* + + /* + title: string + pos: [x,y] + size: [x,y] + + input|output: every connection + + { name:string, type:string, pos: [x,y]=Optional, direction: "input"|"output", links: Array }); + + general properties: + + clip_area: if you render outside the node, it will be clipped + + unsafe_execution: not allowed for safe execution + + skip_repeated_outputs: when adding new outputs, it wont show if there is one already connected + + resizable: if set to false it wont be resizable with the mouse + + horizontal: slots are distributed horizontally + + widgets_start_y: widgets start at y distance from the top of the node + + flags object: + + collapsed: if it is collapsed + + supported callbacks: + + onAdded: when added to graph (warning: this is called BEFORE the node is configured when loading) + + onRemoved: when removed from graph + + onStart: when the graph starts playing + + onStop: when the graph stops playing + + onDrawForeground: render the inside widgets inside the node + + onDrawBackground: render the background area inside the node (only in edit mode) + + onMouseDown + + onMouseMove + + onMouseUp + + onMouseEnter + + onMouseLeave + + onExecute: execute the node + + onPropertyChanged: when a property is changed in the panel (return true to skip default behaviour) + + onGetInputs: returns an array of possible inputs + + onGetOutputs: returns an array of possible outputs + + onBounding: in case this node has a bigger bounding than the node itself (the callback receives the bounding as [x,y,w,h]) + + onDblClick: double clicked in the node + + onInputDblClick: input slot double clicked (can be used to automatically create a node connected) + + onOutputDblClick: output slot double clicked (can be used to automatically create a node connected) + + onConfigure: called after the node has been configured + + onSerialize: to add extra info when serializing (the callback receives the object that should be filled with the data) + + onSelected + + onDeselected + + onDropItem : DOM item dropped over the node + + onDropFile : file dropped over the node + + onConnectInput : if returns false the incoming connection will be canceled + + onConnectionsChange : a connection changed (new one or removed) (LiteGraph.INPUT or LiteGraph.OUTPUT, slot, true if connected, link_info, input_info ) + + onAction: action slot triggered + + getExtraMenuOptions: to add option to context menu +*/ + + /** + * Base Class for all the node type classes + * @class LGraphNode + * @param {String} name a name for the node + */ + + function LGraphNode(title) { + this._ctor(title); + } + + global.LGraphNode = LiteGraph.LGraphNode = LGraphNode; + + LGraphNode.prototype._ctor = function(title) { + this.title = title || "Unnamed"; + this.size = [LiteGraph.NODE_WIDTH, 60]; + this.graph = null; + + this._pos = new Float32Array(10, 10); + + Object.defineProperty(this, "pos", { + set: function(v) { + if (!v || v.length < 2) { + return; + } + this._pos[0] = v[0]; + this._pos[1] = v[1]; + }, + get: function() { + return this._pos; + }, + enumerable: true + }); + + this.id = -1; //not know till not added + this.type = null; + + //inputs available: array of inputs + this.inputs = []; + this.outputs = []; + this.connections = []; + + //local data + this.properties = {}; //for the values + this.properties_info = []; //for the info + + this.flags = {}; + }; + + /** + * configure a node from an object containing the serialized info + * @method configure + */ + LGraphNode.prototype.configure = function(info) { + if (this.graph) { + this.graph._version++; + } + for (var j in info) { + if (j == "properties") { + //i don't want to clone properties, I want to reuse the old container + for (var k in info.properties) { + this.properties[k] = info.properties[k]; + if (this.onPropertyChanged) { + this.onPropertyChanged( k, info.properties[k] ); + } + } + continue; + } + + if (info[j] == null) { + continue; + } else if (typeof info[j] == "object") { + //object + if (this[j] && this[j].configure) { + this[j].configure(info[j]); + } else { + this[j] = LiteGraph.cloneObject(info[j], this[j]); + } + } //value + else { + this[j] = info[j]; + } + } + + if (!info.title) { + this.title = this.constructor.title; + } + + if (this.onConnectionsChange) { + if (this.inputs) { + for (var i = 0; i < this.inputs.length; ++i) { + var input = this.inputs[i]; + var link_info = this.graph + ? this.graph.links[input.link] + : null; + this.onConnectionsChange( + LiteGraph.INPUT, + i, + true, + link_info, + input + ); //link_info has been created now, so its updated + } + } + + if (this.outputs) { + for (var i = 0; i < this.outputs.length; ++i) { + var output = this.outputs[i]; + if (!output.links) { + continue; + } + for (var j = 0; j < output.links.length; ++j) { + var link_info = this.graph + ? this.graph.links[output.links[j]] + : null; + this.onConnectionsChange( + LiteGraph.OUTPUT, + i, + true, + link_info, + output + ); //link_info has been created now, so its updated + } + } + } + } + + if( this.widgets ) + { + for (var i = 0; i < this.widgets.length; ++i) + { + var w = this.widgets[i]; + if(!w) + continue; + if(w.options && w.options.property && this.properties[ w.options.property ]) + w.value = JSON.parse( JSON.stringify( this.properties[ w.options.property ] ) ); + } + if (info.widgets_values) { + for (var i = 0; i < info.widgets_values.length; ++i) { + if (this.widgets[i]) { + this.widgets[i].value = info.widgets_values[i]; + } + } + } + } + + if (this.onConfigure) { + this.onConfigure(info); + } + }; + + /** + * serialize the content + * @method serialize + */ + + LGraphNode.prototype.serialize = function() { + //create serialization object + var o = { + id: this.id, + type: this.type, + pos: this.pos, + size: this.size, + flags: LiteGraph.cloneObject(this.flags), + order: this.order, + mode: this.mode + }; + + //special case for when there were errors + if (this.constructor === LGraphNode && this.last_serialization) { + return this.last_serialization; + } + + if (this.inputs) { + o.inputs = this.inputs; + } + + if (this.outputs) { + //clear outputs last data (because data in connections is never serialized but stored inside the outputs info) + for (var i = 0; i < this.outputs.length; i++) { + delete this.outputs[i]._data; + } + o.outputs = this.outputs; + } + + if (this.title && this.title != this.constructor.title) { + o.title = this.title; + } + + if (this.properties) { + o.properties = LiteGraph.cloneObject(this.properties); + } + + if (this.widgets && this.serialize_widgets) { + o.widgets_values = []; + for (var i = 0; i < this.widgets.length; ++i) { + if(this.widgets[i]) + o.widgets_values[i] = this.widgets[i].value; + else + o.widgets_values[i] = null; + } + } + + if (!o.type) { + o.type = this.constructor.type; + } + + if (this.color) { + o.color = this.color; + } + if (this.bgcolor) { + o.bgcolor = this.bgcolor; + } + if (this.boxcolor) { + o.boxcolor = this.boxcolor; + } + if (this.shape) { + o.shape = this.shape; + } + + if (this.onSerialize) { + if (this.onSerialize(o)) { + console.warn( + "node onSerialize shouldnt return anything, data should be stored in the object pass in the first parameter" + ); + } + } + + return o; + }; + + /* Creates a clone of this node */ + LGraphNode.prototype.clone = function() { + var node = LiteGraph.createNode(this.type); + if (!node) { + return null; + } + + //we clone it because serialize returns shared containers + var data = LiteGraph.cloneObject(this.serialize()); + + //remove links + if (data.inputs) { + for (var i = 0; i < data.inputs.length; ++i) { + data.inputs[i].link = null; + } + } + + if (data.outputs) { + for (var i = 0; i < data.outputs.length; ++i) { + if (data.outputs[i].links) { + data.outputs[i].links.length = 0; + } + } + } + + delete data["id"]; + //remove links + node.configure(data); + + return node; + }; + + /** + * serialize and stringify + * @method toString + */ + + LGraphNode.prototype.toString = function() { + return JSON.stringify(this.serialize()); + }; + //LGraphNode.prototype.deserialize = function(info) {} //this cannot be done from within, must be done in LiteGraph + + /** + * get the title string + * @method getTitle + */ + + LGraphNode.prototype.getTitle = function() { + return this.title || this.constructor.title; + }; + + /** + * sets the value of a property + * @method setProperty + * @param {String} name + * @param {*} value + */ + LGraphNode.prototype.setProperty = function(name, value) { + if (!this.properties) { + this.properties = {}; + } + if( value === this.properties[name] ) + return; + var prev_value = this.properties[name]; + this.properties[name] = value; + if (this.onPropertyChanged) { + if( this.onPropertyChanged(name, value, prev_value) === false ) //abort change + this.properties[name] = prev_value; + } + if(this.widgets) //widgets could be linked to properties + for(var i = 0; i < this.widgets.length; ++i) + { + var w = this.widgets[i]; + if(!w) + continue; + if(w.options.property == name) + { + w.value = value; + break; + } + } + }; + + // Execution ************************* + /** + * sets the output data + * @method setOutputData + * @param {number} slot + * @param {*} data + */ + LGraphNode.prototype.setOutputData = function(slot, data) { + if (!this.outputs) { + return; + } + + //this maybe slow and a niche case + //if(slot && slot.constructor === String) + // slot = this.findOutputSlot(slot); + + if (slot == -1 || slot >= this.outputs.length) { + return; + } + + var output_info = this.outputs[slot]; + if (!output_info) { + return; + } + + //store data in the output itself in case we want to debug + output_info._data = data; + + //if there are connections, pass the data to the connections + if (this.outputs[slot].links) { + for (var i = 0; i < this.outputs[slot].links.length; i++) { + var link_id = this.outputs[slot].links[i]; + var link = this.graph.links[link_id]; + if(link) + link.data = data; + } + } + }; + + /** + * sets the output data type, useful when you want to be able to overwrite the data type + * @method setOutputDataType + * @param {number} slot + * @param {String} datatype + */ + LGraphNode.prototype.setOutputDataType = function(slot, type) { + if (!this.outputs) { + return; + } + if (slot == -1 || slot >= this.outputs.length) { + return; + } + var output_info = this.outputs[slot]; + if (!output_info) { + return; + } + //store data in the output itself in case we want to debug + output_info.type = type; + + //if there are connections, pass the data to the connections + if (this.outputs[slot].links) { + for (var i = 0; i < this.outputs[slot].links.length; i++) { + var link_id = this.outputs[slot].links[i]; + this.graph.links[link_id].type = type; + } + } + }; + + /** + * Retrieves the input data (data traveling through the connection) from one slot + * @method getInputData + * @param {number} slot + * @param {boolean} force_update if set to true it will force the connected node of this slot to output data into this link + * @return {*} data or if it is not connected returns undefined + */ + LGraphNode.prototype.getInputData = function(slot, force_update) { + if (!this.inputs) { + return; + } //undefined; + + if (slot >= this.inputs.length || this.inputs[slot].link == null) { + return; + } + + var link_id = this.inputs[slot].link; + var link = this.graph.links[link_id]; + if (!link) { + //bug: weird case but it happens sometimes + return null; + } + + if (!force_update) { + return link.data; + } + + //special case: used to extract data from the incoming connection before the graph has been executed + var node = this.graph.getNodeById(link.origin_id); + if (!node) { + return link.data; + } + + if (node.updateOutputData) { + node.updateOutputData(link.origin_slot); + } else if (node.onExecute) { + node.onExecute(); + } + + return link.data; + }; + + /** + * Retrieves the input data type (in case this supports multiple input types) + * @method getInputDataType + * @param {number} slot + * @return {String} datatype in string format + */ + LGraphNode.prototype.getInputDataType = function(slot) { + if (!this.inputs) { + return null; + } //undefined; + + if (slot >= this.inputs.length || this.inputs[slot].link == null) { + return null; + } + var link_id = this.inputs[slot].link; + var link = this.graph.links[link_id]; + if (!link) { + //bug: weird case but it happens sometimes + return null; + } + var node = this.graph.getNodeById(link.origin_id); + if (!node) { + return link.type; + } + var output_info = node.outputs[link.origin_slot]; + if (output_info) { + return output_info.type; + } + return null; + }; + + /** + * Retrieves the input data from one slot using its name instead of slot number + * @method getInputDataByName + * @param {String} slot_name + * @param {boolean} force_update if set to true it will force the connected node of this slot to output data into this link + * @return {*} data or if it is not connected returns null + */ + LGraphNode.prototype.getInputDataByName = function( + slot_name, + force_update + ) { + var slot = this.findInputSlot(slot_name); + if (slot == -1) { + return null; + } + return this.getInputData(slot, force_update); + }; + + /** + * tells you if there is a connection in one input slot + * @method isInputConnected + * @param {number} slot + * @return {boolean} + */ + LGraphNode.prototype.isInputConnected = function(slot) { + if (!this.inputs) { + return false; + } + return slot < this.inputs.length && this.inputs[slot].link != null; + }; + + /** + * tells you info about an input connection (which node, type, etc) + * @method getInputInfo + * @param {number} slot + * @return {Object} object or null { link: id, name: string, type: string or 0 } + */ + LGraphNode.prototype.getInputInfo = function(slot) { + if (!this.inputs) { + return null; + } + if (slot < this.inputs.length) { + return this.inputs[slot]; + } + return null; + }; + + /** + * returns the node connected in the input slot + * @method getInputNode + * @param {number} slot + * @return {LGraphNode} node or null + */ + LGraphNode.prototype.getInputNode = function(slot) { + if (!this.inputs) { + return null; + } + if (slot >= this.inputs.length) { + return null; + } + var input = this.inputs[slot]; + if (!input || input.link === null) { + return null; + } + var link_info = this.graph.links[input.link]; + if (!link_info) { + return null; + } + return this.graph.getNodeById(link_info.origin_id); + }; + + /** + * returns the value of an input with this name, otherwise checks if there is a property with that name + * @method getInputOrProperty + * @param {string} name + * @return {*} value + */ + LGraphNode.prototype.getInputOrProperty = function(name) { + if (!this.inputs || !this.inputs.length) { + return this.properties ? this.properties[name] : null; + } + + for (var i = 0, l = this.inputs.length; i < l; ++i) { + var input_info = this.inputs[i]; + if (name == input_info.name && input_info.link != null) { + var link = this.graph.links[input_info.link]; + if (link) { + return link.data; + } + } + } + return this.properties[name]; + }; + + /** + * tells you the last output data that went in that slot + * @method getOutputData + * @param {number} slot + * @return {Object} object or null + */ + LGraphNode.prototype.getOutputData = function(slot) { + if (!this.outputs) { + return null; + } + if (slot >= this.outputs.length) { + return null; + } + + var info = this.outputs[slot]; + return info._data; + }; + + /** + * tells you info about an output connection (which node, type, etc) + * @method getOutputInfo + * @param {number} slot + * @return {Object} object or null { name: string, type: string, links: [ ids of links in number ] } + */ + LGraphNode.prototype.getOutputInfo = function(slot) { + if (!this.outputs) { + return null; + } + if (slot < this.outputs.length) { + return this.outputs[slot]; + } + return null; + }; + + /** + * tells you if there is a connection in one output slot + * @method isOutputConnected + * @param {number} slot + * @return {boolean} + */ + LGraphNode.prototype.isOutputConnected = function(slot) { + if (!this.outputs) { + return false; + } + return ( + slot < this.outputs.length && + this.outputs[slot].links && + this.outputs[slot].links.length + ); + }; + + /** + * tells you if there is any connection in the output slots + * @method isAnyOutputConnected + * @return {boolean} + */ + LGraphNode.prototype.isAnyOutputConnected = function() { + if (!this.outputs) { + return false; + } + for (var i = 0; i < this.outputs.length; ++i) { + if (this.outputs[i].links && this.outputs[i].links.length) { + return true; + } + } + return false; + }; + + /** + * retrieves all the nodes connected to this output slot + * @method getOutputNodes + * @param {number} slot + * @return {array} + */ + LGraphNode.prototype.getOutputNodes = function(slot) { + if (!this.outputs || this.outputs.length == 0) { + return null; + } + + if (slot >= this.outputs.length) { + return null; + } + + var output = this.outputs[slot]; + if (!output.links || output.links.length == 0) { + return null; + } + + var r = []; + for (var i = 0; i < output.links.length; i++) { + var link_id = output.links[i]; + var link = this.graph.links[link_id]; + if (link) { + var target_node = this.graph.getNodeById(link.target_id); + if (target_node) { + r.push(target_node); + } + } + } + return r; + }; + + /** + * Triggers an event in this node, this will trigger any output with the same name + * @method trigger + * @param {String} event name ( "on_play", ... ) if action is equivalent to false then the event is send to all + * @param {*} param + */ + LGraphNode.prototype.trigger = function(action, param) { + if (!this.outputs || !this.outputs.length) { + return; + } + + if (this.graph) + this.graph._last_trigger_time = LiteGraph.getTime(); + + for (var i = 0; i < this.outputs.length; ++i) { + var output = this.outputs[i]; + if ( !output || output.type !== LiteGraph.EVENT || (action && output.name != action) ) + continue; + this.triggerSlot(i, param); + } + }; + + /** + * Triggers an slot event in this node + * @method triggerSlot + * @param {Number} slot the index of the output slot + * @param {*} param + * @param {Number} link_id [optional] in case you want to trigger and specific output link in a slot + */ + LGraphNode.prototype.triggerSlot = function(slot, param, link_id) { + if (!this.outputs) { + return; + } + + var output = this.outputs[slot]; + if (!output) { + return; + } + + var links = output.links; + if (!links || !links.length) { + return; + } + + if (this.graph) { + this.graph._last_trigger_time = LiteGraph.getTime(); + } + + //for every link attached here + for (var k = 0; k < links.length; ++k) { + var id = links[k]; + if (link_id != null && link_id != id) { + //to skip links + continue; + } + var link_info = this.graph.links[links[k]]; + if (!link_info) { + //not connected + continue; + } + link_info._last_time = LiteGraph.getTime(); + var node = this.graph.getNodeById(link_info.target_id); + if (!node) { + //node not found? + continue; + } + + //used to mark events in graph + var target_connection = node.inputs[link_info.target_slot]; + + if (node.onAction) { + node.onAction(target_connection.name, param); + } else if (node.mode === LiteGraph.ON_TRIGGER) { + if (node.onExecute) { + node.onExecute(param); + } + } + } + }; + + /** + * clears the trigger slot animation + * @method clearTriggeredSlot + * @param {Number} slot the index of the output slot + * @param {Number} link_id [optional] in case you want to trigger and specific output link in a slot + */ + LGraphNode.prototype.clearTriggeredSlot = function(slot, link_id) { + if (!this.outputs) { + return; + } + + var output = this.outputs[slot]; + if (!output) { + return; + } + + var links = output.links; + if (!links || !links.length) { + return; + } + + //for every link attached here + for (var k = 0; k < links.length; ++k) { + var id = links[k]; + if (link_id != null && link_id != id) { + //to skip links + continue; + } + var link_info = this.graph.links[links[k]]; + if (!link_info) { + //not connected + continue; + } + link_info._last_time = 0; + } + }; + + /** + * add a new property to this node + * @method addProperty + * @param {string} name + * @param {*} default_value + * @param {string} type string defining the output type ("vec3","number",...) + * @param {Object} extra_info this can be used to have special properties of the property (like values, etc) + */ + LGraphNode.prototype.addProperty = function( + name, + default_value, + type, + extra_info + ) { + var o = { name: name, type: type, default_value: default_value }; + if (extra_info) { + for (var i in extra_info) { + o[i] = extra_info[i]; + } + } + if (!this.properties_info) { + this.properties_info = []; + } + this.properties_info.push(o); + if (!this.properties) { + this.properties = {}; + } + this.properties[name] = default_value; + return o; + }; + + //connections + + /** + * add a new output slot to use in this node + * @method addOutput + * @param {string} name + * @param {string} type string defining the output type ("vec3","number",...) + * @param {Object} extra_info this can be used to have special properties of an output (label, special color, position, etc) + */ + LGraphNode.prototype.addOutput = function(name, type, extra_info) { + var o = { name: name, type: type, links: null }; + if (extra_info) { + for (var i in extra_info) { + o[i] = extra_info[i]; + } + } + + if (!this.outputs) { + this.outputs = []; + } + this.outputs.push(o); + if (this.onOutputAdded) { + this.onOutputAdded(o); + } + this.size = this.computeSize(); + this.setDirtyCanvas(true, true); + return o; + }; + + /** + * add a new output slot to use in this node + * @method addOutputs + * @param {Array} array of triplets like [[name,type,extra_info],[...]] + */ + LGraphNode.prototype.addOutputs = function(array) { + for (var i = 0; i < array.length; ++i) { + var info = array[i]; + var o = { name: info[0], type: info[1], link: null }; + if (array[2]) { + for (var j in info[2]) { + o[j] = info[2][j]; + } + } + + if (!this.outputs) { + this.outputs = []; + } + this.outputs.push(o); + if (this.onOutputAdded) { + this.onOutputAdded(o); + } + } + + this.size = this.computeSize(); + this.setDirtyCanvas(true, true); + }; + + /** + * remove an existing output slot + * @method removeOutput + * @param {number} slot + */ + LGraphNode.prototype.removeOutput = function(slot) { + this.disconnectOutput(slot); + this.outputs.splice(slot, 1); + for (var i = slot; i < this.outputs.length; ++i) { + if (!this.outputs[i] || !this.outputs[i].links) { + continue; + } + var links = this.outputs[i].links; + for (var j = 0; j < links.length; ++j) { + var link = this.graph.links[links[j]]; + if (!link) { + continue; + } + link.origin_slot -= 1; + } + } + + this.size = this.computeSize(); + if (this.onOutputRemoved) { + this.onOutputRemoved(slot); + } + this.setDirtyCanvas(true, true); + }; + + /** + * add a new input slot to use in this node + * @method addInput + * @param {string} name + * @param {string} type string defining the input type ("vec3","number",...), it its a generic one use 0 + * @param {Object} extra_info this can be used to have special properties of an input (label, color, position, etc) + */ + LGraphNode.prototype.addInput = function(name, type, extra_info) { + type = type || 0; + var o = { name: name, type: type, link: null }; + if (extra_info) { + for (var i in extra_info) { + o[i] = extra_info[i]; + } + } + + if (!this.inputs) { + this.inputs = []; + } + this.inputs.push(o); + this.size = this.computeSize(); + if (this.onInputAdded) { + this.onInputAdded(o); + } + this.setDirtyCanvas(true, true); + return o; + }; + + /** + * add several new input slots in this node + * @method addInputs + * @param {Array} array of triplets like [[name,type,extra_info],[...]] + */ + LGraphNode.prototype.addInputs = function(array) { + for (var i = 0; i < array.length; ++i) { + var info = array[i]; + var o = { name: info[0], type: info[1], link: null }; + if (array[2]) { + for (var j in info[2]) { + o[j] = info[2][j]; + } + } + + if (!this.inputs) { + this.inputs = []; + } + this.inputs.push(o); + if (this.onInputAdded) { + this.onInputAdded(o); + } + } + + this.size = this.computeSize(); + this.setDirtyCanvas(true, true); + }; + + /** + * remove an existing input slot + * @method removeInput + * @param {number} slot + */ + LGraphNode.prototype.removeInput = function(slot) { + this.disconnectInput(slot); + this.inputs.splice(slot, 1); + for (var i = slot; i < this.inputs.length; ++i) { + if (!this.inputs[i]) { + continue; + } + var link = this.graph.links[this.inputs[i].link]; + if (!link) { + continue; + } + link.target_slot -= 1; + } + this.size = this.computeSize(); + if (this.onInputRemoved) { + this.onInputRemoved(slot); + } + this.setDirtyCanvas(true, true); + }; + + /** + * add an special connection to this node (used for special kinds of graphs) + * @method addConnection + * @param {string} name + * @param {string} type string defining the input type ("vec3","number",...) + * @param {[x,y]} pos position of the connection inside the node + * @param {string} direction if is input or output + */ + LGraphNode.prototype.addConnection = function(name, type, pos, direction) { + var o = { + name: name, + type: type, + pos: pos, + direction: direction, + links: null + }; + this.connections.push(o); + return o; + }; + + /** + * computes the size of a node according to its inputs and output slots + * @method computeSize + * @param {number} minHeight + * @return {number} the total size + */ + LGraphNode.prototype.computeSize = function(minHeight, out) { + if (this.constructor.size) { + return this.constructor.size.concat(); + } + + var rows = Math.max( + this.inputs ? this.inputs.length : 1, + this.outputs ? this.outputs.length : 1 + ); + var size = out || new Float32Array([0, 0]); + rows = Math.max(rows, 1); + var font_size = LiteGraph.NODE_TEXT_SIZE; //although it should be graphcanvas.inner_text_font size + + var font_size = font_size; + var title_width = compute_text_size(this.title); + var input_width = 0; + var output_width = 0; + + if (this.inputs) { + for (var i = 0, l = this.inputs.length; i < l; ++i) { + var input = this.inputs[i]; + var text = input.label || input.name || ""; + var text_width = compute_text_size(text); + if (input_width < text_width) { + input_width = text_width; + } + } + } + + if (this.outputs) { + for (var i = 0, l = this.outputs.length; i < l; ++i) { + var output = this.outputs[i]; + var text = output.label || output.name || ""; + var text_width = compute_text_size(text); + if (output_width < text_width) { + output_width = text_width; + } + } + } + + size[0] = Math.max(input_width + output_width + 10, title_width); + size[0] = Math.max(size[0], LiteGraph.NODE_WIDTH); + if (this.widgets && this.widgets.length) { + size[0] = Math.max(size[0], LiteGraph.NODE_WIDTH * 1.5); + } + + size[1] = (this.constructor.slot_start_y || 0) + rows * LiteGraph.NODE_SLOT_HEIGHT; + + var widgets_height = 0; + if (this.widgets && this.widgets.length) { + for (var i = 0, l = this.widgets.length; i < l; ++i) { + if (this.widgets[i].computeSize) + widgets_height += this.widgets[i].computeSize(size[0])[1] + 4; + else + widgets_height += LiteGraph.NODE_WIDGET_HEIGHT + 4; + } + widgets_height += 8; + } + + //compute height using widgets height + if( this.widgets_up ) + size[1] = Math.max( size[1], widgets_height ); + else if( this.widgets_start_y != null ) + size[1] = Math.max( size[1], widgets_height + this.widgets_start_y ); + else + size[1] += widgets_height; + + if (this.onResize) { + this.onResize(size); + } + + function compute_text_size(text) { + if (!text) { + return 0; + } + return font_size * text.length * 0.6; + } + + if ( + this.constructor.min_height && + size[1] < this.constructor.min_height + ) { + size[1] = this.constructor.min_height; + } + + size[1] += 6; //margin + + return size; + }; + + /** + * returns all the info available about a property of this node. + * + * @method getPropertyInfo + * @param {String} property name of the property + * @return {Object} the object with all the available info + */ + LGraphNode.prototype.getPropertyInfo = function( property ) + { + var info = null; + + //there are several ways to define info about a property + //legacy mode + if (this.properties_info) { + for (var i = 0; i < this.properties_info.length; ++i) { + if (this.properties_info[i].name == property) { + info = this.properties_info[i]; + break; + } + } + } + //litescene mode using the constructor + if(this.constructor["@" + property]) + info = this.constructor["@" + property]; + + //litescene mode using the constructor + if (this.onGetPropertyInfo) { + info = this.onGetPropertyInfo(property); + } + + if (!info) + info = {}; + if(!info.type) + info.type = typeof this.properties[property]; + + return info; + } + + /** + * Defines a widget inside the node, it will be rendered on top of the node, you can control lots of properties + * + * @method addWidget + * @param {String} type the widget type (could be "number","string","combo" + * @param {String} name the text to show on the widget + * @param {String} value the default value + * @param {Function|String} callback function to call when it changes (optionally, it can be the name of the property to modify) + * @param {Object} options the object that contains special properties of this widget + * @return {Object} the created widget object + */ + LGraphNode.prototype.addWidget = function( type, name, value, callback, options ) + { + if (!this.widgets) { + this.widgets = []; + } + + if(!options && callback && callback.constructor === Object) + { + options = callback; + callback = null; + } + + if(options && options.constructor === String) //options can be the property name + options = { property: options }; + + if(callback && callback.constructor === String) //callback can be the property name + { + if(!options) + options = {}; + options.property = callback; + callback = null; + } + + if(callback && callback.constructor !== Function) + { + console.warn("addWidget: callback must be a function"); + callback = null; + } + + var w = { + type: type.toLowerCase(), + name: name, + value: value, + callback: callback, + options: options || {} + }; + + if (w.options.y !== undefined) { + w.y = w.options.y; + } + + if (!callback && !w.options.callback && !w.options.property) { + console.warn("LiteGraph addWidget(...) without a callback or property assigned"); + } + if (type == "combo" && !w.options.values) { + throw "LiteGraph addWidget('combo',...) requires to pass values in options: { values:['red','blue'] }"; + } + this.widgets.push(w); + this.size = this.computeSize(); + return w; + }; + + LGraphNode.prototype.addCustomWidget = function(custom_widget) { + if (!this.widgets) { + this.widgets = []; + } + this.widgets.push(custom_widget); + return custom_widget; + }; + + /** + * returns the bounding of the object, used for rendering purposes + * bounding is: [topleft_cornerx, topleft_cornery, width, height] + * @method getBounding + * @return {Float32Array[4]} the total size + */ + LGraphNode.prototype.getBounding = function(out) { + out = out || new Float32Array(4); + out[0] = this.pos[0] - 4; + out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT; + out[2] = this.size[0] + 4; + out[3] = this.size[1] + LiteGraph.NODE_TITLE_HEIGHT; + + if (this.onBounding) { + this.onBounding(out); + } + return out; + }; + + /** + * checks if a point is inside the shape of a node + * @method isPointInside + * @param {number} x + * @param {number} y + * @return {boolean} + */ + LGraphNode.prototype.isPointInside = function(x, y, margin, skip_title) { + margin = margin || 0; + + var margin_top = this.graph && this.graph.isLive() ? 0 : LiteGraph.NODE_TITLE_HEIGHT; + if (skip_title) { + margin_top = 0; + } + if (this.flags && this.flags.collapsed) { + //if ( distance([x,y], [this.pos[0] + this.size[0]*0.5, this.pos[1] + this.size[1]*0.5]) < LiteGraph.NODE_COLLAPSED_RADIUS) + if ( + isInsideRectangle( + x, + y, + this.pos[0] - margin, + this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT - margin, + (this._collapsed_width || LiteGraph.NODE_COLLAPSED_WIDTH) + + 2 * margin, + LiteGraph.NODE_TITLE_HEIGHT + 2 * margin + ) + ) { + return true; + } + } else if ( + this.pos[0] - 4 - margin < x && + this.pos[0] + this.size[0] + 4 + margin > x && + this.pos[1] - margin_top - margin < y && + this.pos[1] + this.size[1] + margin > y + ) { + return true; + } + return false; + }; + + /** + * checks if a point is inside a node slot, and returns info about which slot + * @method getSlotInPosition + * @param {number} x + * @param {number} y + * @return {Object} if found the object contains { input|output: slot object, slot: number, link_pos: [x,y] } + */ + LGraphNode.prototype.getSlotInPosition = function(x, y) { + //search for inputs + var link_pos = new Float32Array(2); + if (this.inputs) { + for (var i = 0, l = this.inputs.length; i < l; ++i) { + var input = this.inputs[i]; + this.getConnectionPos(true, i, link_pos); + if ( + isInsideRectangle( + x, + y, + link_pos[0] - 10, + link_pos[1] - 5, + 20, + 10 + ) + ) { + return { input: input, slot: i, link_pos: link_pos }; + } + } + } + + if (this.outputs) { + for (var i = 0, l = this.outputs.length; i < l; ++i) { + var output = this.outputs[i]; + this.getConnectionPos(false, i, link_pos); + if ( + isInsideRectangle( + x, + y, + link_pos[0] - 10, + link_pos[1] - 5, + 20, + 10 + ) + ) { + return { output: output, slot: i, link_pos: link_pos }; + } + } + } + + return null; + }; + + /** + * returns the input slot with a given name (used for dynamic slots), -1 if not found + * @method findInputSlot + * @param {string} name the name of the slot + * @return {number} the slot (-1 if not found) + */ + LGraphNode.prototype.findInputSlot = function(name) { + if (!this.inputs) { + return -1; + } + for (var i = 0, l = this.inputs.length; i < l; ++i) { + if (name == this.inputs[i].name) { + return i; + } + } + return -1; + }; + + /** + * returns the output slot with a given name (used for dynamic slots), -1 if not found + * @method findOutputSlot + * @param {string} name the name of the slot + * @return {number} the slot (-1 if not found) + */ + LGraphNode.prototype.findOutputSlot = function(name) { + if (!this.outputs) { + return -1; + } + for (var i = 0, l = this.outputs.length; i < l; ++i) { + if (name == this.outputs[i].name) { + return i; + } + } + return -1; + }; + + /** + * connect this node output to the input of another node + * @method connect + * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) + * @param {LGraphNode} node the target node + * @param {number_or_string} target_slot the input slot of the target node (could be the number of the slot or the string with the name of the slot, or -1 to connect a trigger) + * @return {Object} the link_info is created, otherwise null + */ + LGraphNode.prototype.connect = function(slot, target_node, target_slot) { + target_slot = target_slot || 0; + + if (!this.graph) { + //could be connected before adding it to a graph + console.log( + "Connect: Error, node doesn't belong to any graph. Nodes must be added first to a graph before connecting them." + ); //due to link ids being associated with graphs + return null; + } + + //seek for the output slot + if (slot.constructor === String) { + slot = this.findOutputSlot(slot); + if (slot == -1) { + if (LiteGraph.debug) { + console.log("Connect: Error, no slot of name " + slot); + } + return null; + } + } else if (!this.outputs || slot >= this.outputs.length) { + if (LiteGraph.debug) { + console.log("Connect: Error, slot number not found"); + } + return null; + } + + if (target_node && target_node.constructor === Number) { + target_node = this.graph.getNodeById(target_node); + } + if (!target_node) { + throw "target node is null"; + } + + //avoid loopback + if (target_node == this) { + return null; + } + + //you can specify the slot by name + if (target_slot.constructor === String) { + target_slot = target_node.findInputSlot(target_slot); + if (target_slot == -1) { + if (LiteGraph.debug) { + console.log( + "Connect: Error, no slot of name " + target_slot + ); + } + return null; + } + } else if (target_slot === LiteGraph.EVENT) { + //search for first slot with event? + /* + //create input for trigger + var input = target_node.addInput("onTrigger", LiteGraph.EVENT ); + target_slot = target_node.inputs.length - 1; //last one is the one created + target_node.mode = LiteGraph.ON_TRIGGER; + */ + return null; + } else if ( + !target_node.inputs || + target_slot >= target_node.inputs.length + ) { + if (LiteGraph.debug) { + console.log("Connect: Error, slot number not found"); + } + return null; + } + + //if there is something already plugged there, disconnect + if (target_node.inputs[target_slot].link != null) { + target_node.disconnectInput(target_slot); + } + + //why here?? + //this.setDirtyCanvas(false,true); + //this.graph.connectionChange( this ); + + var output = this.outputs[slot]; + + //allows nodes to block connection + if (target_node.onConnectInput) { + if ( + target_node.onConnectInput(target_slot, output.type, output) === + false + ) { + return null; + } + } + + var input = target_node.inputs[target_slot]; + var link_info = null; + + if (LiteGraph.isValidConnection(output.type, input.type)) { + link_info = new LLink( + ++this.graph.last_link_id, + input.type, + this.id, + slot, + target_node.id, + target_slot + ); + + //add to graph links list + this.graph.links[link_info.id] = link_info; + + //connect in output + if (output.links == null) { + output.links = []; + } + output.links.push(link_info.id); + //connect in input + target_node.inputs[target_slot].link = link_info.id; + if (this.graph) { + this.graph._version++; + } + if (this.onConnectionsChange) { + this.onConnectionsChange( + LiteGraph.OUTPUT, + slot, + true, + link_info, + output + ); + } //link_info has been created now, so its updated + if (target_node.onConnectionsChange) { + target_node.onConnectionsChange( + LiteGraph.INPUT, + target_slot, + true, + link_info, + input + ); + } + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange( + LiteGraph.INPUT, + target_node, + target_slot, + this, + slot + ); + this.graph.onNodeConnectionChange( + LiteGraph.OUTPUT, + this, + slot, + target_node, + target_slot + ); + } + } + + this.setDirtyCanvas(false, true); + this.graph.connectionChange(this, link_info); + + return link_info; + }; + + /** + * disconnect one output to an specific node + * @method disconnectOutput + * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) + * @param {LGraphNode} target_node the target node to which this slot is connected [Optional, if not target_node is specified all nodes will be disconnected] + * @return {boolean} if it was disconnected successfully + */ + LGraphNode.prototype.disconnectOutput = function(slot, target_node) { + if (slot.constructor === String) { + slot = this.findOutputSlot(slot); + if (slot == -1) { + if (LiteGraph.debug) { + console.log("Connect: Error, no slot of name " + slot); + } + return false; + } + } else if (!this.outputs || slot >= this.outputs.length) { + if (LiteGraph.debug) { + console.log("Connect: Error, slot number not found"); + } + return false; + } + + //get output slot + var output = this.outputs[slot]; + if (!output || !output.links || output.links.length == 0) { + return false; + } + + //one of the output links in this slot + if (target_node) { + if (target_node.constructor === Number) { + target_node = this.graph.getNodeById(target_node); + } + if (!target_node) { + throw "Target Node not found"; + } + + for (var i = 0, l = output.links.length; i < l; i++) { + var link_id = output.links[i]; + var link_info = this.graph.links[link_id]; + + //is the link we are searching for... + if (link_info.target_id == target_node.id) { + output.links.splice(i, 1); //remove here + var input = target_node.inputs[link_info.target_slot]; + input.link = null; //remove there + delete this.graph.links[link_id]; //remove the link from the links pool + if (this.graph) { + this.graph._version++; + } + if (target_node.onConnectionsChange) { + target_node.onConnectionsChange( + LiteGraph.INPUT, + link_info.target_slot, + false, + link_info, + input + ); + } //link_info hasn't been modified so its ok + if (this.onConnectionsChange) { + this.onConnectionsChange( + LiteGraph.OUTPUT, + slot, + false, + link_info, + output + ); + } + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange( + LiteGraph.OUTPUT, + this, + slot + ); + } + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange( + LiteGraph.OUTPUT, + this, + slot + ); + this.graph.onNodeConnectionChange( + LiteGraph.INPUT, + target_node, + link_info.target_slot + ); + } + break; + } + } + } //all the links in this output slot + else { + for (var i = 0, l = output.links.length; i < l; i++) { + var link_id = output.links[i]; + var link_info = this.graph.links[link_id]; + if (!link_info) { + //bug: it happens sometimes + continue; + } + + var target_node = this.graph.getNodeById(link_info.target_id); + var input = null; + if (this.graph) { + this.graph._version++; + } + if (target_node) { + input = target_node.inputs[link_info.target_slot]; + input.link = null; //remove other side link + if (target_node.onConnectionsChange) { + target_node.onConnectionsChange( + LiteGraph.INPUT, + link_info.target_slot, + false, + link_info, + input + ); + } //link_info hasn't been modified so its ok + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange( + LiteGraph.INPUT, + target_node, + link_info.target_slot + ); + } + } + delete this.graph.links[link_id]; //remove the link from the links pool + if (this.onConnectionsChange) { + this.onConnectionsChange( + LiteGraph.OUTPUT, + slot, + false, + link_info, + output + ); + } + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange( + LiteGraph.OUTPUT, + this, + slot + ); + this.graph.onNodeConnectionChange( + LiteGraph.INPUT, + target_node, + link_info.target_slot + ); + } + } + output.links = null; + } + + this.setDirtyCanvas(false, true); + this.graph.connectionChange(this); + return true; + }; + + /** + * disconnect one input + * @method disconnectInput + * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) + * @return {boolean} if it was disconnected successfully + */ + LGraphNode.prototype.disconnectInput = function(slot) { + //seek for the output slot + if (slot.constructor === String) { + slot = this.findInputSlot(slot); + if (slot == -1) { + if (LiteGraph.debug) { + console.log("Connect: Error, no slot of name " + slot); + } + return false; + } + } else if (!this.inputs || slot >= this.inputs.length) { + if (LiteGraph.debug) { + console.log("Connect: Error, slot number not found"); + } + return false; + } + + var input = this.inputs[slot]; + if (!input) { + return false; + } + + var link_id = this.inputs[slot].link; + this.inputs[slot].link = null; + + //remove other side + var link_info = this.graph.links[link_id]; + if (link_info) { + var target_node = this.graph.getNodeById(link_info.origin_id); + if (!target_node) { + return false; + } + + var output = target_node.outputs[link_info.origin_slot]; + if (!output || !output.links || output.links.length == 0) { + return false; + } + + //search in the inputs list for this link + for (var i = 0, l = output.links.length; i < l; i++) { + if (output.links[i] == link_id) { + output.links.splice(i, 1); + break; + } + } + + delete this.graph.links[link_id]; //remove from the pool + if (this.graph) { + this.graph._version++; + } + if (this.onConnectionsChange) { + this.onConnectionsChange( + LiteGraph.INPUT, + slot, + false, + link_info, + input + ); + } + if (target_node.onConnectionsChange) { + target_node.onConnectionsChange( + LiteGraph.OUTPUT, + i, + false, + link_info, + output + ); + } + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange( + LiteGraph.OUTPUT, + target_node, + i + ); + this.graph.onNodeConnectionChange(LiteGraph.INPUT, this, slot); + } + } + + this.setDirtyCanvas(false, true); + this.graph.connectionChange(this); + return true; + }; + + /** + * returns the center of a connection point in canvas coords + * @method getConnectionPos + * @param {boolean} is_input true if if a input slot, false if it is an output + * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) + * @param {vec2} out [optional] a place to store the output, to free garbage + * @return {[x,y]} the position + **/ + LGraphNode.prototype.getConnectionPos = function( + is_input, + slot_number, + out + ) { + out = out || new Float32Array(2); + var num_slots = 0; + if (is_input && this.inputs) { + num_slots = this.inputs.length; + } + if (!is_input && this.outputs) { + num_slots = this.outputs.length; + } + + var offset = LiteGraph.NODE_SLOT_HEIGHT * 0.5; + + if (this.flags.collapsed) { + var w = this._collapsed_width || LiteGraph.NODE_COLLAPSED_WIDTH; + if (this.horizontal) { + out[0] = this.pos[0] + w * 0.5; + if (is_input) { + out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT; + } else { + out[1] = this.pos[1]; + } + } else { + if (is_input) { + out[0] = this.pos[0]; + } else { + out[0] = this.pos[0] + w; + } + out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT * 0.5; + } + return out; + } + + //weird feature that never got finished + if (is_input && slot_number == -1) { + out[0] = this.pos[0] + LiteGraph.NODE_TITLE_HEIGHT * 0.5; + out[1] = this.pos[1] + LiteGraph.NODE_TITLE_HEIGHT * 0.5; + return out; + } + + //hard-coded pos + if ( + is_input && + num_slots > slot_number && + this.inputs[slot_number].pos + ) { + out[0] = this.pos[0] + this.inputs[slot_number].pos[0]; + out[1] = this.pos[1] + this.inputs[slot_number].pos[1]; + return out; + } else if ( + !is_input && + num_slots > slot_number && + this.outputs[slot_number].pos + ) { + out[0] = this.pos[0] + this.outputs[slot_number].pos[0]; + out[1] = this.pos[1] + this.outputs[slot_number].pos[1]; + return out; + } + + //horizontal distributed slots + if (this.horizontal) { + out[0] = + this.pos[0] + (slot_number + 0.5) * (this.size[0] / num_slots); + if (is_input) { + out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT; + } else { + out[1] = this.pos[1] + this.size[1]; + } + return out; + } + + //default vertical slots + if (is_input) { + out[0] = this.pos[0] + offset; + } else { + out[0] = this.pos[0] + this.size[0] + 1 - offset; + } + out[1] = + this.pos[1] + + (slot_number + 0.7) * LiteGraph.NODE_SLOT_HEIGHT + + (this.constructor.slot_start_y || 0); + return out; + }; + + /* Force align to grid */ + LGraphNode.prototype.alignToGrid = function() { + this.pos[0] = + LiteGraph.CANVAS_GRID_SIZE * + Math.round(this.pos[0] / LiteGraph.CANVAS_GRID_SIZE); + this.pos[1] = + LiteGraph.CANVAS_GRID_SIZE * + Math.round(this.pos[1] / LiteGraph.CANVAS_GRID_SIZE); + }; + + /* Console output */ + LGraphNode.prototype.trace = function(msg) { + if (!this.console) { + this.console = []; + } + this.console.push(msg); + if (this.console.length > LGraphNode.MAX_CONSOLE) { + this.console.shift(); + } + + this.graph.onNodeTrace(this, msg); + }; + + /* Forces to redraw or the main canvas (LGraphNode) or the bg canvas (links) */ + LGraphNode.prototype.setDirtyCanvas = function( + dirty_foreground, + dirty_background + ) { + if (!this.graph) { + return; + } + this.graph.sendActionToCanvas("setDirty", [ + dirty_foreground, + dirty_background + ]); + }; + + LGraphNode.prototype.loadImage = function(url) { + var img = new Image(); + img.src = LiteGraph.node_images_path + url; + img.ready = false; + + var that = this; + img.onload = function() { + this.ready = true; + that.setDirtyCanvas(true); + }; + return img; + }; + + //safe LGraphNode action execution (not sure if safe) + /* +LGraphNode.prototype.executeAction = function(action) +{ + if(action == "") return false; + + if( action.indexOf(";") != -1 || action.indexOf("}") != -1) + { + this.trace("Error: Action contains unsafe characters"); + return false; + } + + var tokens = action.split("("); + var func_name = tokens[0]; + if( typeof(this[func_name]) != "function") + { + this.trace("Error: Action not found on node: " + func_name); + return false; + } + + var code = action; + + try + { + var _foo = eval; + eval = null; + (new Function("with(this) { " + code + "}")).call(this); + eval = _foo; + } + catch (err) + { + this.trace("Error executing action {" + action + "} :" + err); + return false; + } + + return true; +} +*/ + + /* Allows to get onMouseMove and onMouseUp events even if the mouse is out of focus */ + LGraphNode.prototype.captureInput = function(v) { + if (!this.graph || !this.graph.list_of_graphcanvas) { + return; + } + + var list = this.graph.list_of_graphcanvas; + + for (var i = 0; i < list.length; ++i) { + var c = list[i]; + //releasing somebody elses capture?! + if (!v && c.node_capturing_input != this) { + continue; + } + + //change + c.node_capturing_input = v ? this : null; + } + }; + + /** + * Collapse the node to make it smaller on the canvas + * @method collapse + **/ + LGraphNode.prototype.collapse = function(force) { + this.graph._version++; + if (this.constructor.collapsable === false && !force) { + return; + } + if (!this.flags.collapsed) { + this.flags.collapsed = true; + } else { + this.flags.collapsed = false; + } + this.setDirtyCanvas(true, true); + }; + + /** + * Forces the node to do not move or realign on Z + * @method pin + **/ + + LGraphNode.prototype.pin = function(v) { + this.graph._version++; + if (v === undefined) { + this.flags.pinned = !this.flags.pinned; + } else { + this.flags.pinned = v; + } + }; + + LGraphNode.prototype.localToScreen = function(x, y, graphcanvas) { + return [ + (x + this.pos[0]) * graphcanvas.scale + graphcanvas.offset[0], + (y + this.pos[1]) * graphcanvas.scale + graphcanvas.offset[1] + ]; + }; + + function LGraphGroup(title) { + this._ctor(title); + } + + global.LGraphGroup = LiteGraph.LGraphGroup = LGraphGroup; + + LGraphGroup.prototype._ctor = function(title) { + this.title = title || "Group"; + this.font_size = 24; + this.color = LGraphCanvas.node_colors.pale_blue + ? LGraphCanvas.node_colors.pale_blue.groupcolor + : "#AAA"; + this._bounding = new Float32Array([10, 10, 140, 80]); + this._pos = this._bounding.subarray(0, 2); + this._size = this._bounding.subarray(2, 4); + this._nodes = []; + this.graph = null; + + Object.defineProperty(this, "pos", { + set: function(v) { + if (!v || v.length < 2) { + return; + } + this._pos[0] = v[0]; + this._pos[1] = v[1]; + }, + get: function() { + return this._pos; + }, + enumerable: true + }); + + Object.defineProperty(this, "size", { + set: function(v) { + if (!v || v.length < 2) { + return; + } + this._size[0] = Math.max(140, v[0]); + this._size[1] = Math.max(80, v[1]); + }, + get: function() { + return this._size; + }, + enumerable: true + }); + }; + + LGraphGroup.prototype.configure = function(o) { + this.title = o.title; + this._bounding.set(o.bounding); + this.color = o.color; + this.font = o.font; + }; + + LGraphGroup.prototype.serialize = function() { + var b = this._bounding; + return { + title: this.title, + bounding: [ + Math.round(b[0]), + Math.round(b[1]), + Math.round(b[2]), + Math.round(b[3]) + ], + color: this.color, + font: this.font + }; + }; + + LGraphGroup.prototype.move = function(deltax, deltay, ignore_nodes) { + this._pos[0] += deltax; + this._pos[1] += deltay; + if (ignore_nodes) { + return; + } + for (var i = 0; i < this._nodes.length; ++i) { + var node = this._nodes[i]; + node.pos[0] += deltax; + node.pos[1] += deltay; + } + }; + + LGraphGroup.prototype.recomputeInsideNodes = function() { + this._nodes.length = 0; + var nodes = this.graph._nodes; + var node_bounding = new Float32Array(4); + + for (var i = 0; i < nodes.length; ++i) { + var node = nodes[i]; + node.getBounding(node_bounding); + if (!overlapBounding(this._bounding, node_bounding)) { + continue; + } //out of the visible area + this._nodes.push(node); + } + }; + + LGraphGroup.prototype.isPointInside = LGraphNode.prototype.isPointInside; + LGraphGroup.prototype.setDirtyCanvas = LGraphNode.prototype.setDirtyCanvas; + + //**************************************** + + //Scale and Offset + function DragAndScale(element, skip_events) { + this.offset = new Float32Array([0, 0]); + this.scale = 1; + this.max_scale = 10; + this.min_scale = 0.1; + this.onredraw = null; + this.enabled = true; + this.last_mouse = [0, 0]; + this.element = null; + this.visible_area = new Float32Array(4); + + if (element) { + this.element = element; + if (!skip_events) { + this.bindEvents(element); + } + } + } + + LiteGraph.DragAndScale = DragAndScale; + + DragAndScale.prototype.bindEvents = function(element) { + this.last_mouse = new Float32Array(2); + + this._binded_mouse_callback = this.onMouse.bind(this); + + element.addEventListener("mousedown", this._binded_mouse_callback); + element.addEventListener("mousemove", this._binded_mouse_callback); + + element.addEventListener( + "mousewheel", + this._binded_mouse_callback, + false + ); + element.addEventListener("wheel", this._binded_mouse_callback, false); + }; + + DragAndScale.prototype.computeVisibleArea = function() { + if (!this.element) { + this.visible_area[0] = this.visible_area[1] = this.visible_area[2] = this.visible_area[3] = 0; + return; + } + var width = this.element.width; + var height = this.element.height; + var startx = -this.offset[0]; + var starty = -this.offset[1]; + var endx = startx + width / this.scale; + var endy = starty + height / this.scale; + this.visible_area[0] = startx; + this.visible_area[1] = starty; + this.visible_area[2] = endx - startx; + this.visible_area[3] = endy - starty; + }; + + DragAndScale.prototype.onMouse = function(e) { + if (!this.enabled) { + return; + } + + var canvas = this.element; + var rect = canvas.getBoundingClientRect(); + var x = e.clientX - rect.left; + var y = e.clientY - rect.top; + e.canvasx = x; + e.canvasy = y; + e.dragging = this.dragging; + + var ignore = false; + if (this.onmouse) { + ignore = this.onmouse(e); + } + + if (e.type == "mousedown") { + this.dragging = true; + canvas.removeEventListener( + "mousemove", + this._binded_mouse_callback + ); + document.body.addEventListener( + "mousemove", + this._binded_mouse_callback + ); + document.body.addEventListener( + "mouseup", + this._binded_mouse_callback + ); + } else if (e.type == "mousemove") { + if (!ignore) { + var deltax = x - this.last_mouse[0]; + var deltay = y - this.last_mouse[1]; + if (this.dragging) { + this.mouseDrag(deltax, deltay); + } + } + } else if (e.type == "mouseup") { + this.dragging = false; + document.body.removeEventListener( + "mousemove", + this._binded_mouse_callback + ); + document.body.removeEventListener( + "mouseup", + this._binded_mouse_callback + ); + canvas.addEventListener("mousemove", this._binded_mouse_callback); + } else if ( + e.type == "mousewheel" || + e.type == "wheel" || + e.type == "DOMMouseScroll" + ) { + e.eventType = "mousewheel"; + if (e.type == "wheel") { + e.wheel = -e.deltaY; + } else { + e.wheel = + e.wheelDeltaY != null ? e.wheelDeltaY : e.detail * -60; + } + + //from stack overflow + e.delta = e.wheelDelta + ? e.wheelDelta / 40 + : e.deltaY + ? -e.deltaY / 3 + : 0; + this.changeDeltaScale(1.0 + e.delta * 0.05); + } + + this.last_mouse[0] = x; + this.last_mouse[1] = y; + + e.preventDefault(); + e.stopPropagation(); + return false; + }; + + DragAndScale.prototype.toCanvasContext = function(ctx) { + ctx.scale(this.scale, this.scale); + ctx.translate(this.offset[0], this.offset[1]); + }; + + DragAndScale.prototype.convertOffsetToCanvas = function(pos) { + //return [pos[0] / this.scale - this.offset[0], pos[1] / this.scale - this.offset[1]]; + return [ + (pos[0] + this.offset[0]) * this.scale, + (pos[1] + this.offset[1]) * this.scale + ]; + }; + + DragAndScale.prototype.convertCanvasToOffset = function(pos, out) { + out = out || [0, 0]; + out[0] = pos[0] / this.scale - this.offset[0]; + out[1] = pos[1] / this.scale - this.offset[1]; + return out; + }; + + DragAndScale.prototype.mouseDrag = function(x, y) { + this.offset[0] += x / this.scale; + this.offset[1] += y / this.scale; + + if (this.onredraw) { + this.onredraw(this); + } + }; + + DragAndScale.prototype.changeScale = function(value, zooming_center) { + if (value < this.min_scale) { + value = this.min_scale; + } else if (value > this.max_scale) { + value = this.max_scale; + } + + if (value == this.scale) { + return; + } + + if (!this.element) { + return; + } + + var rect = this.element.getBoundingClientRect(); + if (!rect) { + return; + } + + zooming_center = zooming_center || [ + rect.width * 0.5, + rect.height * 0.5 + ]; + var center = this.convertCanvasToOffset(zooming_center); + this.scale = value; + if (Math.abs(this.scale - 1) < 0.01) { + this.scale = 1; + } + + var new_center = this.convertCanvasToOffset(zooming_center); + var delta_offset = [ + new_center[0] - center[0], + new_center[1] - center[1] + ]; + + this.offset[0] += delta_offset[0]; + this.offset[1] += delta_offset[1]; + + if (this.onredraw) { + this.onredraw(this); + } + }; + + DragAndScale.prototype.changeDeltaScale = function(value, zooming_center) { + this.changeScale(this.scale * value, zooming_center); + }; + + DragAndScale.prototype.reset = function() { + this.scale = 1; + this.offset[0] = 0; + this.offset[1] = 0; + }; + + //********************************************************************************* + // LGraphCanvas: LGraph renderer CLASS + //********************************************************************************* + + /** + * This class is in charge of rendering one graph inside a canvas. And provides all the interaction required. + * Valid callbacks are: onNodeSelected, onNodeDeselected, onShowNodePanel, onNodeDblClicked + * + * @class LGraphCanvas + * @constructor + * @param {HTMLCanvas} canvas the canvas where you want to render (it accepts a selector in string format or the canvas element itself) + * @param {LGraph} graph [optional] + * @param {Object} options [optional] { skip_rendering, autoresize } + */ + function LGraphCanvas(canvas, graph, options) { + options = options || {}; + + //if(graph === undefined) + // throw ("No graph assigned"); + this.background_image = + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; + + if (canvas && canvas.constructor === String) { + canvas = document.querySelector(canvas); + } + + this.ds = new DragAndScale(); + this.zoom_modify_alpha = true; //otherwise it generates ugly patterns when scaling down too much + + this.title_text_font = "" + LiteGraph.NODE_TEXT_SIZE + "px Arial"; + this.inner_text_font = + "normal " + LiteGraph.NODE_SUBTEXT_SIZE + "px Arial"; + this.node_title_color = LiteGraph.NODE_TITLE_COLOR; + this.default_link_color = LiteGraph.LINK_COLOR; + this.default_connection_color = { + input_off: "#778", + input_on: "#7F7", + output_off: "#778", + output_on: "#7F7" + }; + + this.highquality_render = true; + this.use_gradients = false; //set to true to render titlebar with gradients + this.editor_alpha = 1; //used for transition + this.pause_rendering = false; + this.clear_background = true; + + this.read_only = false; //if set to true users cannot modify the graph + this.render_only_selected = true; + this.live_mode = false; + this.show_info = true; + this.allow_dragcanvas = true; + this.allow_dragnodes = true; + this.allow_interaction = true; //allow to control widgets, buttons, collapse, etc + this.allow_searchbox = true; + this.allow_reconnect_links = false; //allows to change a connection with having to redo it again + + this.drag_mode = false; + this.dragging_rectangle = null; + + this.filter = null; //allows to filter to only accept some type of nodes in a graph + + this.always_render_background = false; + this.render_shadows = true; + this.render_canvas_border = true; + this.render_connections_shadows = false; //too much cpu + this.render_connections_border = true; + this.render_curved_connections = false; + this.render_connection_arrows = false; + this.render_collapsed_slots = true; + this.render_execution_order = false; + this.render_title_colored = true; + this.render_link_tooltip = true; + + this.links_render_mode = LiteGraph.SPLINE_LINK; + + this.canvas_mouse = [0, 0]; //mouse in canvas graph coordinates, where 0,0 is the top-left corner of the blue rectangle + + //to personalize the search box + this.onSearchBox = null; + this.onSearchBoxSelection = null; + + //callbacks + this.onMouse = null; + this.onDrawBackground = null; //to render background objects (behind nodes and connections) in the canvas affected by transform + this.onDrawForeground = null; //to render foreground objects (above nodes and connections) in the canvas affected by transform + this.onDrawOverlay = null; //to render foreground objects not affected by transform (for GUIs) + this.onDrawLinkTooltip = null; //called when rendering a tooltip + this.onNodeMoved = null; //called after moving a node + this.onSelectionChange = null; //called if the selection changes + + this.connections_width = 3; + this.round_radius = 8; + + this.current_node = null; + this.node_widget = null; //used for widgets + this.over_link_center = null; + this.last_mouse_position = [0, 0]; + this.visible_area = this.ds.visible_area; + this.visible_links = []; + + //link canvas and graph + if (graph) { + graph.attachCanvas(this); + } + + this.setCanvas(canvas); + this.clear(); + + if (!options.skip_render) { + this.startRendering(); + } + + this.autoresize = options.autoresize; + } + + global.LGraphCanvas = LiteGraph.LGraphCanvas = LGraphCanvas; + + LGraphCanvas.link_type_colors = { + "-1": LiteGraph.EVENT_LINK_COLOR, + number: "#AAA", + node: "#DCA" + }; + LGraphCanvas.gradients = {}; //cache of gradients + + /** + * clears all the data inside + * + * @method clear + */ + LGraphCanvas.prototype.clear = function() { + this.frame = 0; + this.last_draw_time = 0; + this.render_time = 0; + this.fps = 0; + + //this.scale = 1; + //this.offset = [0,0]; + + this.dragging_rectangle = null; + + this.selected_nodes = {}; + this.selected_group = null; + + this.visible_nodes = []; + this.node_dragged = null; + this.node_over = null; + this.node_capturing_input = null; + this.connecting_node = null; + this.highlighted_links = {}; + + this.dirty_canvas = true; + this.dirty_bgcanvas = true; + this.dirty_area = null; + + this.node_in_panel = null; + this.node_widget = null; + + this.last_mouse = [0, 0]; + this.last_mouseclick = 0; + this.visible_area.set([0, 0, 0, 0]); + + if (this.onClear) { + this.onClear(); + } + }; + + /** + * assigns a graph, you can reassign graphs to the same canvas + * + * @method setGraph + * @param {LGraph} graph + */ + LGraphCanvas.prototype.setGraph = function(graph, skip_clear) { + if (this.graph == graph) { + return; + } + + if (!skip_clear) { + this.clear(); + } + + if (!graph && this.graph) { + this.graph.detachCanvas(this); + return; + } + + graph.attachCanvas(this); + + //remove the graph stack in case a subgraph was open + if (this._graph_stack) + this._graph_stack = null; + + this.setDirty(true, true); + }; + + /** + * opens a graph contained inside a node in the current graph + * + * @method openSubgraph + * @param {LGraph} graph + */ + LGraphCanvas.prototype.openSubgraph = function(graph) { + if (!graph) { + throw "graph cannot be null"; + } + + if (this.graph == graph) { + throw "graph cannot be the same"; + } + + this.clear(); + + if (this.graph) { + if (!this._graph_stack) { + this._graph_stack = []; + } + this._graph_stack.push(this.graph); + } + + graph.attachCanvas(this); + this.setDirty(true, true); + }; + + /** + * closes a subgraph contained inside a node + * + * @method closeSubgraph + * @param {LGraph} assigns a graph + */ + LGraphCanvas.prototype.closeSubgraph = function() { + if (!this._graph_stack || this._graph_stack.length == 0) { + return; + } + var subgraph_node = this.graph._subgraph_node; + var graph = this._graph_stack.pop(); + this.selected_nodes = {}; + this.highlighted_links = {}; + graph.attachCanvas(this); + this.setDirty(true, true); + if (subgraph_node) { + this.centerOnNode(subgraph_node); + this.selectNodes([subgraph_node]); + } + }; + + /** + * returns the visualy active graph (in case there are more in the stack) + * @method getCurrentGraph + * @return {LGraph} the active graph + */ + LGraphCanvas.prototype.getCurrentGraph = function() { + return this.graph; + }; + + /** + * assigns a canvas + * + * @method setCanvas + * @param {Canvas} assigns a canvas (also accepts the ID of the element (not a selector) + */ + LGraphCanvas.prototype.setCanvas = function(canvas, skip_events) { + var that = this; + + if (canvas) { + if (canvas.constructor === String) { + canvas = document.getElementById(canvas); + if (!canvas) { + throw "Error creating LiteGraph canvas: Canvas not found"; + } + } + } + + if (canvas === this.canvas) { + return; + } + + if (!canvas && this.canvas) { + //maybe detach events from old_canvas + if (!skip_events) { + this.unbindEvents(); + } + } + + this.canvas = canvas; + this.ds.element = canvas; + + if (!canvas) { + return; + } + + //this.canvas.tabindex = "1000"; + canvas.className += " lgraphcanvas"; + canvas.data = this; + canvas.tabindex = "1"; //to allow key events + + //bg canvas: used for non changing stuff + this.bgcanvas = null; + if (!this.bgcanvas) { + this.bgcanvas = document.createElement("canvas"); + this.bgcanvas.width = this.canvas.width; + this.bgcanvas.height = this.canvas.height; + } + + if (canvas.getContext == null) { + if (canvas.localName != "canvas") { + throw "Element supplied for LGraphCanvas must be a element, you passed a " + + canvas.localName; + } + throw "This browser doesn't support Canvas"; + } + + var ctx = (this.ctx = canvas.getContext("2d")); + if (ctx == null) { + if (!canvas.webgl_enabled) { + console.warn( + "This canvas seems to be WebGL, enabling WebGL renderer" + ); + } + this.enableWebGL(); + } + + //input: (move and up could be unbinded) + this._mousemove_callback = this.processMouseMove.bind(this); + this._mouseup_callback = this.processMouseUp.bind(this); + + if (!skip_events) { + this.bindEvents(); + } + }; + + //used in some events to capture them + LGraphCanvas.prototype._doNothing = function doNothing(e) { + e.preventDefault(); + return false; + }; + LGraphCanvas.prototype._doReturnTrue = function doNothing(e) { + e.preventDefault(); + return true; + }; + + /** + * binds mouse, keyboard, touch and drag events to the canvas + * @method bindEvents + **/ + LGraphCanvas.prototype.bindEvents = function() { + if (this._events_binded) { + console.warn("LGraphCanvas: events already binded"); + return; + } + + var canvas = this.canvas; + var ref_window = this.getCanvasWindow(); + var document = ref_window.document; //hack used when moving canvas between windows + + this._mousedown_callback = this.processMouseDown.bind(this); + this._mousewheel_callback = this.processMouseWheel.bind(this); + + canvas.addEventListener("mousedown", this._mousedown_callback, true); //down do not need to store the binded + canvas.addEventListener("mousemove", this._mousemove_callback); + canvas.addEventListener("mousewheel", this._mousewheel_callback, false); + + canvas.addEventListener("contextmenu", this._doNothing); + canvas.addEventListener( + "DOMMouseScroll", + this._mousewheel_callback, + false + ); + + //touch events + //if( 'touchstart' in document.documentElement ) + { + canvas.addEventListener("touchstart", this.touchHandler, true); + canvas.addEventListener("touchmove", this.touchHandler, true); + canvas.addEventListener("touchend", this.touchHandler, true); + canvas.addEventListener("touchcancel", this.touchHandler, true); + } + + //Keyboard ****************** + this._key_callback = this.processKey.bind(this); + + canvas.addEventListener("keydown", this._key_callback, true); + document.addEventListener("keyup", this._key_callback, true); //in document, otherwise it doesn't fire keyup + + //Dropping Stuff over nodes ************************************ + this._ondrop_callback = this.processDrop.bind(this); + + canvas.addEventListener("dragover", this._doNothing, false); + canvas.addEventListener("dragend", this._doNothing, false); + canvas.addEventListener("drop", this._ondrop_callback, false); + canvas.addEventListener("dragenter", this._doReturnTrue, false); + + this._events_binded = true; + }; + + /** + * unbinds mouse events from the canvas + * @method unbindEvents + **/ + LGraphCanvas.prototype.unbindEvents = function() { + if (!this._events_binded) { + console.warn("LGraphCanvas: no events binded"); + return; + } + + var ref_window = this.getCanvasWindow(); + var document = ref_window.document; + + this.canvas.removeEventListener("mousedown", this._mousedown_callback); + this.canvas.removeEventListener( + "mousewheel", + this._mousewheel_callback + ); + this.canvas.removeEventListener( + "DOMMouseScroll", + this._mousewheel_callback + ); + this.canvas.removeEventListener("keydown", this._key_callback); + document.removeEventListener("keyup", this._key_callback); + this.canvas.removeEventListener("contextmenu", this._doNothing); + this.canvas.removeEventListener("drop", this._ondrop_callback); + this.canvas.removeEventListener("dragenter", this._doReturnTrue); + + this.canvas.removeEventListener("touchstart", this.touchHandler); + this.canvas.removeEventListener("touchmove", this.touchHandler); + this.canvas.removeEventListener("touchend", this.touchHandler); + this.canvas.removeEventListener("touchcancel", this.touchHandler); + + this._mousedown_callback = null; + this._mousewheel_callback = null; + this._key_callback = null; + this._ondrop_callback = null; + + this._events_binded = false; + }; + + LGraphCanvas.getFileExtension = function(url) { + var question = url.indexOf("?"); + if (question != -1) { + url = url.substr(0, question); + } + var point = url.lastIndexOf("."); + if (point == -1) { + return ""; + } + return url.substr(point + 1).toLowerCase(); + }; + + /** + * this function allows to render the canvas using WebGL instead of Canvas2D + * this is useful if you plant to render 3D objects inside your nodes, it uses litegl.js for webgl and canvas2DtoWebGL to emulate the Canvas2D calls in webGL + * @method enableWebGL + **/ + LGraphCanvas.prototype.enableWebGL = function() { + if (typeof GL === undefined) { + throw "litegl.js must be included to use a WebGL canvas"; + } + if (typeof enableWebGLCanvas === undefined) { + throw "webglCanvas.js must be included to use this feature"; + } + + this.gl = this.ctx = enableWebGLCanvas(this.canvas); + this.ctx.webgl = true; + this.bgcanvas = this.canvas; + this.bgctx = this.gl; + this.canvas.webgl_enabled = true; + + /* + GL.create({ canvas: this.bgcanvas }); + this.bgctx = enableWebGLCanvas( this.bgcanvas ); + window.gl = this.gl; + */ + }; + + /** + * marks as dirty the canvas, this way it will be rendered again + * + * @class LGraphCanvas + * @method setDirty + * @param {bool} fgcanvas if the foreground canvas is dirty (the one containing the nodes) + * @param {bool} bgcanvas if the background canvas is dirty (the one containing the wires) + */ + LGraphCanvas.prototype.setDirty = function(fgcanvas, bgcanvas) { + if (fgcanvas) { + this.dirty_canvas = true; + } + if (bgcanvas) { + this.dirty_bgcanvas = true; + } + }; + + /** + * Used to attach the canvas in a popup + * + * @method getCanvasWindow + * @return {window} returns the window where the canvas is attached (the DOM root node) + */ + LGraphCanvas.prototype.getCanvasWindow = function() { + if (!this.canvas) { + return window; + } + var doc = this.canvas.ownerDocument; + return doc.defaultView || doc.parentWindow; + }; + + /** + * starts rendering the content of the canvas when needed + * + * @method startRendering + */ + LGraphCanvas.prototype.startRendering = function() { + if (this.is_rendering) { + return; + } //already rendering + + this.is_rendering = true; + renderFrame.call(this); + + function renderFrame() { + if (!this.pause_rendering) { + this.draw(); + } + + var window = this.getCanvasWindow(); + if (this.is_rendering) { + window.requestAnimationFrame(renderFrame.bind(this)); + } + } + }; + + /** + * stops rendering the content of the canvas (to save resources) + * + * @method stopRendering + */ + LGraphCanvas.prototype.stopRendering = function() { + this.is_rendering = false; + /* + if(this.rendering_timer_id) + { + clearInterval(this.rendering_timer_id); + this.rendering_timer_id = null; + } + */ + }; + + /* LiteGraphCanvas input */ + + LGraphCanvas.prototype.processMouseDown = function(e) { + if (!this.graph) { + return; + } + + this.adjustMouseEvent(e); + + var ref_window = this.getCanvasWindow(); + var document = ref_window.document; + LGraphCanvas.active_canvas = this; + var that = this; + + //move mouse move event to the window in case it drags outside of the canvas + this.canvas.removeEventListener("mousemove", this._mousemove_callback); + ref_window.document.addEventListener( + "mousemove", + this._mousemove_callback, + true + ); //catch for the entire window + ref_window.document.addEventListener( + "mouseup", + this._mouseup_callback, + true + ); + + var node = this.graph.getNodeOnPos( + e.canvasX, + e.canvasY, + this.visible_nodes, + 5 + ); + var skip_dragging = false; + var skip_action = false; + var now = LiteGraph.getTime(); + var is_double_click = now - this.last_mouseclick < 300; + + this.canvas_mouse[0] = e.canvasX; + this.canvas_mouse[1] = e.canvasY; + this.canvas.focus(); + + LiteGraph.closeAllContextMenus(ref_window); + + if (this.onMouse) { + if (this.onMouse(e) == true) { + return; + } + } + + if (e.which == 1) { + //left button mouse + if (e.ctrlKey) { + this.dragging_rectangle = new Float32Array(4); + this.dragging_rectangle[0] = e.canvasX; + this.dragging_rectangle[1] = e.canvasY; + this.dragging_rectangle[2] = 1; + this.dragging_rectangle[3] = 1; + skip_action = true; + } + + var clicking_canvas_bg = false; + + //when clicked on top of a node + //and it is not interactive + if (node && this.allow_interaction && !skip_action && !this.read_only) { + if (!this.live_mode && !node.flags.pinned) { + this.bringToFront(node); + } //if it wasn't selected? + + //not dragging mouse to connect two slots + if ( + !this.connecting_node && + !node.flags.collapsed && + !this.live_mode + ) { + //Search for corner for resize + if ( + !skip_action && + node.resizable !== false && + isInsideRectangle( + e.canvasX, + e.canvasY, + node.pos[0] + node.size[0] - 5, + node.pos[1] + node.size[1] - 5, + 10, + 10 + ) + ) { + this.resizing_node = node; + this.canvas.style.cursor = "se-resize"; + skip_action = true; + } else { + //search for outputs + if (node.outputs) { + for ( + var i = 0, l = node.outputs.length; + i < l; + ++i + ) { + var output = node.outputs[i]; + var link_pos = node.getConnectionPos(false, i); + if ( + isInsideRectangle( + e.canvasX, + e.canvasY, + link_pos[0] - 15, + link_pos[1] - 10, + 30, + 20 + ) + ) { + this.connecting_node = node; + this.connecting_output = output; + this.connecting_pos = node.getConnectionPos( false, i ); + this.connecting_slot = i; + + if (e.shiftKey) { + node.disconnectOutput(i); + } + + if (is_double_click) { + if (node.onOutputDblClick) { + node.onOutputDblClick(i, e); + } + } else { + if (node.onOutputClick) { + node.onOutputClick(i, e); + } + } + + skip_action = true; + break; + } + } + } + + //search for inputs + if (node.inputs) { + for ( + var i = 0, l = node.inputs.length; + i < l; + ++i + ) { + var input = node.inputs[i]; + var link_pos = node.getConnectionPos(true, i); + if ( + isInsideRectangle( + e.canvasX, + e.canvasY, + link_pos[0] - 15, + link_pos[1] - 10, + 30, + 20 + ) + ) { + if (is_double_click) { + if (node.onInputDblClick) { + node.onInputDblClick(i, e); + } + } else { + if (node.onInputClick) { + node.onInputClick(i, e); + } + } + + if (input.link !== null) { + var link_info = this.graph.links[ + input.link + ]; //before disconnecting + node.disconnectInput(i); + + if ( + this.allow_reconnect_links || + e.shiftKey + ) { + this.connecting_node = this.graph._nodes_by_id[ + link_info.origin_id + ]; + this.connecting_slot = + link_info.origin_slot; + this.connecting_output = this.connecting_node.outputs[ + this.connecting_slot + ]; + this.connecting_pos = this.connecting_node.getConnectionPos( false, this.connecting_slot ); + } + + this.dirty_bgcanvas = true; + skip_action = true; + } + } + } + } + } //not resizing + } + + //Search for corner for collapsing + /* + if( !skip_action && isInsideRectangle( e.canvasX, e.canvasY, node.pos[0], node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT )) + { + node.collapse(); + skip_action = true; + } + */ + + //it wasn't clicked on the links boxes + if (!skip_action) { + var block_drag_node = false; + + //widgets + var widget = this.processNodeWidgets( node, this.canvas_mouse, e ); + if (widget) { + block_drag_node = true; + this.node_widget = [node, widget]; + } + + //double clicking + if (is_double_click && this.selected_nodes[node.id]) { + //double click node + if (node.onDblClick) { + node.onDblClick( e, [ e.canvasX - node.pos[0], e.canvasY - node.pos[1] ], this ); + } + this.processNodeDblClicked(node); + block_drag_node = true; + } + + //if do not capture mouse + if ( node.onMouseDown && node.onMouseDown( e, [e.canvasX - node.pos[0], e.canvasY - node.pos[1]], this ) ) { + block_drag_node = true; + } else if (this.live_mode) { + clicking_canvas_bg = true; + block_drag_node = true; + } + + if (!block_drag_node) { + if (this.allow_dragnodes) { + this.node_dragged = node; + } + if (!this.selected_nodes[node.id]) { + this.processNodeSelected(node, e); + } + } + + this.dirty_canvas = true; + } + } //clicked outside of nodes + else { + //search for link connector + if(!this.read_only) + for (var i = 0; i < this.visible_links.length; ++i) { + var link = this.visible_links[i]; + var center = link._pos; + if ( + !center || + e.canvasX < center[0] - 4 || + e.canvasX > center[0] + 4 || + e.canvasY < center[1] - 4 || + e.canvasY > center[1] + 4 + ) { + continue; + } + //link clicked + this.showLinkMenu(link, e); + this.over_link_center = null; //clear tooltip + break; + } + + this.selected_group = this.graph.getGroupOnPos( e.canvasX, e.canvasY ); + this.selected_group_resizing = false; + if (this.selected_group && !this.read_only ) { + if (e.ctrlKey) { + this.dragging_rectangle = null; + } + + var dist = distance( [e.canvasX, e.canvasY], [ this.selected_group.pos[0] + this.selected_group.size[0], this.selected_group.pos[1] + this.selected_group.size[1] ] ); + if (dist * this.ds.scale < 10) { + this.selected_group_resizing = true; + } else { + this.selected_group.recomputeInsideNodes(); + } + } + + if (is_double_click && !this.read_only && this.allow_searchbox) { + this.showSearchBox(e); + } + + clicking_canvas_bg = true; + } + + if (!skip_action && clicking_canvas_bg && this.allow_dragcanvas) { + this.dragging_canvas = true; + } + } else if (e.which == 2) { + //middle button + } else if (e.which == 3) { + //right button + if(!this.read_only) + this.processContextMenu(node, e); + } + + //TODO + //if(this.node_selected != prev_selected) + // this.onNodeSelectionChange(this.node_selected); + + this.last_mouse[0] = e.localX; + this.last_mouse[1] = e.localY; + this.last_mouseclick = LiteGraph.getTime(); + this.last_mouse_dragging = true; + + /* + if( (this.dirty_canvas || this.dirty_bgcanvas) && this.rendering_timer_id == null) + this.draw(); + */ + + this.graph.change(); + + //this is to ensure to defocus(blur) if a text input element is on focus + if ( + !ref_window.document.activeElement || + (ref_window.document.activeElement.nodeName.toLowerCase() != + "input" && + ref_window.document.activeElement.nodeName.toLowerCase() != + "textarea") + ) { + e.preventDefault(); + } + e.stopPropagation(); + + if (this.onMouseDown) { + this.onMouseDown(e); + } + + return false; + }; + + /** + * Called when a mouse move event has to be processed + * @method processMouseMove + **/ + LGraphCanvas.prototype.processMouseMove = function(e) { + if (this.autoresize) { + this.resize(); + } + + if (!this.graph) { + return; + } + + LGraphCanvas.active_canvas = this; + this.adjustMouseEvent(e); + var mouse = [e.localX, e.localY]; + var delta = [ + mouse[0] - this.last_mouse[0], + mouse[1] - this.last_mouse[1] + ]; + this.last_mouse = mouse; + this.canvas_mouse[0] = e.canvasX; + this.canvas_mouse[1] = e.canvasY; + e.dragging = this.last_mouse_dragging; + + if (this.node_widget) { + this.processNodeWidgets( + this.node_widget[0], + this.canvas_mouse, + e, + this.node_widget[1] + ); + this.dirty_canvas = true; + } + + if (this.dragging_rectangle) { + this.dragging_rectangle[2] = e.canvasX - this.dragging_rectangle[0]; + this.dragging_rectangle[3] = e.canvasY - this.dragging_rectangle[1]; + this.dirty_canvas = true; + } else if (this.selected_group && !this.read_only) { + //moving/resizing a group + if (this.selected_group_resizing) { + this.selected_group.size = [ + e.canvasX - this.selected_group.pos[0], + e.canvasY - this.selected_group.pos[1] + ]; + } else { + var deltax = delta[0] / this.ds.scale; + var deltay = delta[1] / this.ds.scale; + this.selected_group.move(deltax, deltay, e.ctrlKey); + if (this.selected_group._nodes.length) { + this.dirty_canvas = true; + } + } + this.dirty_bgcanvas = true; + } else if (this.dragging_canvas) { + this.ds.offset[0] += delta[0] / this.ds.scale; + this.ds.offset[1] += delta[1] / this.ds.scale; + this.dirty_canvas = true; + this.dirty_bgcanvas = true; + } else if (this.allow_interaction && !this.read_only) { + if (this.connecting_node) { + this.dirty_canvas = true; + } + + //get node over + var node = this.graph.getNodeOnPos( + e.canvasX, + e.canvasY, + this.visible_nodes + ); + + //remove mouseover flag + for (var i = 0, l = this.graph._nodes.length; i < l; ++i) { + if ( + this.graph._nodes[i].mouseOver && + node != this.graph._nodes[i] + ) { + //mouse leave + this.graph._nodes[i].mouseOver = false; + if (this.node_over && this.node_over.onMouseLeave) { + this.node_over.onMouseLeave(e); + } + this.node_over = null; + this.dirty_canvas = true; + } + } + + //mouse over a node + if (node) { + //this.canvas.style.cursor = "move"; + if (!node.mouseOver) { + //mouse enter + node.mouseOver = true; + this.node_over = node; + this.dirty_canvas = true; + + if (node.onMouseEnter) { + node.onMouseEnter(e); + } + } + + //in case the node wants to do something + if (node.onMouseMove) { + node.onMouseMove( + e, + [e.canvasX - node.pos[0], e.canvasY - node.pos[1]], + this + ); + } + + //if dragging a link + if (this.connecting_node) { + var pos = this._highlight_input || [0, 0]; //to store the output of isOverNodeInput + + //on top of input + if (this.isOverNodeBox(node, e.canvasX, e.canvasY)) { + //mouse on top of the corner box, don't know what to do + } else { + //check if I have a slot below de mouse + var slot = this.isOverNodeInput( + node, + e.canvasX, + e.canvasY, + pos + ); + if (slot != -1 && node.inputs[slot]) { + var slot_type = node.inputs[slot].type; + if ( + LiteGraph.isValidConnection( + this.connecting_output.type, + slot_type + ) + ) { + this._highlight_input = pos; + } + } else { + this._highlight_input = null; + } + } + } + + //Search for corner + if (this.canvas) { + if ( + isInsideRectangle( + e.canvasX, + e.canvasY, + node.pos[0] + node.size[0] - 5, + node.pos[1] + node.size[1] - 5, + 5, + 5 + ) + ) { + this.canvas.style.cursor = "se-resize"; + } else { + this.canvas.style.cursor = "crosshair"; + } + } + } else { //outside + + //search for link connector + var over_link = null; + for (var i = 0; i < this.visible_links.length; ++i) { + var link = this.visible_links[i]; + var center = link._pos; + if ( + !center || + e.canvasX < center[0] - 4 || + e.canvasX > center[0] + 4 || + e.canvasY < center[1] - 4 || + e.canvasY > center[1] + 4 + ) { + continue; + } + over_link = link; + break; + } + if( over_link != this.over_link_center ) + { + this.over_link_center = over_link; + this.dirty_canvas = true; + } + + if (this.canvas) { + this.canvas.style.cursor = ""; + } + } + + if ( this.node_capturing_input && this.node_capturing_input != node && this.node_capturing_input.onMouseMove ) { + this.node_capturing_input.onMouseMove(e,[e.canvasX - this.node_capturing_input.pos[0],e.canvasY - this.node_capturing_input.pos[1]], this); + } + + if (this.node_dragged && !this.live_mode) { + for (var i in this.selected_nodes) { + var n = this.selected_nodes[i]; + n.pos[0] += delta[0] / this.ds.scale; + n.pos[1] += delta[1] / this.ds.scale; + } + + this.dirty_canvas = true; + this.dirty_bgcanvas = true; + } + + if (this.resizing_node && !this.live_mode) { + //convert mouse to node space + this.resizing_node.size[0] = e.canvasX - this.resizing_node.pos[0]; + this.resizing_node.size[1] = e.canvasY - this.resizing_node.pos[1]; + + //constraint size + var max_slots = Math.max( + this.resizing_node.inputs ? this.resizing_node.inputs.length : 0, + this.resizing_node.outputs ? this.resizing_node.outputs.length : 0 + ); + + if (this.resizing_node.size[0] < LiteGraph.NODE_MIN_WIDTH) { + this.resizing_node.size[0] = LiteGraph.NODE_MIN_WIDTH; + } + + var widgets = this.resizing_node.widgets; + var widgets_height = 0; + if (widgets && widgets.length) { + for (var i = 0, l = widgets.length; i < l; ++i) { + if (widgets[i].computeSize) + widgets_height += widgets[i].computeSize(this.resizing_node.size[0])[1] + 4; + else + widgets_height += LiteGraph.NODE_WIDGET_HEIGHT + 4; + } + widgets_height += 8; + } + + var min_height = max_slots * LiteGraph.NODE_SLOT_HEIGHT + widgets_height; + if (this.resizing_node.size[1] < min_height) { + this.resizing_node.size[1] = min_height; + } + + this.canvas.style.cursor = "se-resize"; + this.dirty_canvas = true; + this.dirty_bgcanvas = true; + } + } + + e.preventDefault(); + return false; + }; + + /** + * Called when a mouse up event has to be processed + * @method processMouseUp + **/ + LGraphCanvas.prototype.processMouseUp = function(e) { + if (!this.graph) { + return; + } + + var window = this.getCanvasWindow(); + var document = window.document; + LGraphCanvas.active_canvas = this; + + //restore the mousemove event back to the canvas + document.removeEventListener("mousemove",this._mousemove_callback,true); + this.canvas.addEventListener("mousemove",this._mousemove_callback,true); + document.removeEventListener("mouseup", this._mouseup_callback, true); + + this.adjustMouseEvent(e); + var now = LiteGraph.getTime(); + e.click_time = now - this.last_mouseclick; + this.last_mouse_dragging = false; + + if (e.which == 1) { + + if( this.node_widget ) + { + this.processNodeWidgets( this.node_widget[0], this.canvas_mouse, e ); + } + + //left button + this.node_widget = null; + + if (this.selected_group) { + var diffx = + this.selected_group.pos[0] - + Math.round(this.selected_group.pos[0]); + var diffy = + this.selected_group.pos[1] - + Math.round(this.selected_group.pos[1]); + this.selected_group.move(diffx, diffy, e.ctrlKey); + this.selected_group.pos[0] = Math.round( + this.selected_group.pos[0] + ); + this.selected_group.pos[1] = Math.round( + this.selected_group.pos[1] + ); + if (this.selected_group._nodes.length) { + this.dirty_canvas = true; + } + this.selected_group = null; + } + this.selected_group_resizing = false; + + if (this.dragging_rectangle) { + if (this.graph) { + var nodes = this.graph._nodes; + var node_bounding = new Float32Array(4); + this.deselectAllNodes(); + //compute bounding and flip if left to right + var w = Math.abs(this.dragging_rectangle[2]); + var h = Math.abs(this.dragging_rectangle[3]); + var startx = + this.dragging_rectangle[2] < 0 + ? this.dragging_rectangle[0] - w + : this.dragging_rectangle[0]; + var starty = + this.dragging_rectangle[3] < 0 + ? this.dragging_rectangle[1] - h + : this.dragging_rectangle[1]; + this.dragging_rectangle[0] = startx; + this.dragging_rectangle[1] = starty; + this.dragging_rectangle[2] = w; + this.dragging_rectangle[3] = h; + + //test against all nodes (not visible because the rectangle maybe start outside + var to_select = []; + for (var i = 0; i < nodes.length; ++i) { + var node = nodes[i]; + node.getBounding(node_bounding); + if ( + !overlapBounding( + this.dragging_rectangle, + node_bounding + ) + ) { + continue; + } //out of the visible area + to_select.push(node); + } + if (to_select.length) { + this.selectNodes(to_select); + } + } + this.dragging_rectangle = null; + } else if (this.connecting_node) { + //dragging a connection + this.dirty_canvas = true; + this.dirty_bgcanvas = true; + + var node = this.graph.getNodeOnPos( + e.canvasX, + e.canvasY, + this.visible_nodes + ); + + //node below mouse + if (node) { + if ( + this.connecting_output.type == LiteGraph.EVENT && + this.isOverNodeBox(node, e.canvasX, e.canvasY) + ) { + this.connecting_node.connect( + this.connecting_slot, + node, + LiteGraph.EVENT + ); + } else { + //slot below mouse? connect + var slot = this.isOverNodeInput( + node, + e.canvasX, + e.canvasY + ); + if (slot != -1) { + this.connecting_node.connect( + this.connecting_slot, + node, + slot + ); + } else { + //not on top of an input + var input = node.getInputInfo(0); + //auto connect + if ( + this.connecting_output.type == LiteGraph.EVENT + ) { + this.connecting_node.connect( + this.connecting_slot, + node, + LiteGraph.EVENT + ); + } else if ( + input && + !input.link && + LiteGraph.isValidConnection( + input.type && this.connecting_output.type + ) + ) { + this.connecting_node.connect( + this.connecting_slot, + node, + 0 + ); + } + } + } + } + + this.connecting_output = null; + this.connecting_pos = null; + this.connecting_node = null; + this.connecting_slot = -1; + } //not dragging connection + else if (this.resizing_node) { + this.dirty_canvas = true; + this.dirty_bgcanvas = true; + this.resizing_node = null; + } else if (this.node_dragged) { + //node being dragged? + var node = this.node_dragged; + if ( + node && + e.click_time < 300 && + isInsideRectangle( e.canvasX, e.canvasY, node.pos[0], node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT ) + ) { + node.collapse(); + } + + this.dirty_canvas = true; + this.dirty_bgcanvas = true; + this.node_dragged.pos[0] = Math.round(this.node_dragged.pos[0]); + this.node_dragged.pos[1] = Math.round(this.node_dragged.pos[1]); + if (this.graph.config.align_to_grid) { + this.node_dragged.alignToGrid(); + } + if( this.onNodeMoved ) + this.onNodeMoved( this.node_dragged ); + this.node_dragged = null; + } //no node being dragged + else { + //get node over + var node = this.graph.getNodeOnPos( + e.canvasX, + e.canvasY, + this.visible_nodes + ); + + if (!node && e.click_time < 300) { + this.deselectAllNodes(); + } + + this.dirty_canvas = true; + this.dragging_canvas = false; + + if (this.node_over && this.node_over.onMouseUp) { + this.node_over.onMouseUp( e, [ e.canvasX - this.node_over.pos[0], e.canvasY - this.node_over.pos[1] ], this ); + } + if ( + this.node_capturing_input && + this.node_capturing_input.onMouseUp + ) { + this.node_capturing_input.onMouseUp(e, [ + e.canvasX - this.node_capturing_input.pos[0], + e.canvasY - this.node_capturing_input.pos[1] + ]); + } + } + } else if (e.which == 2) { + //middle button + //trace("middle"); + this.dirty_canvas = true; + this.dragging_canvas = false; + } else if (e.which == 3) { + //right button + //trace("right"); + this.dirty_canvas = true; + this.dragging_canvas = false; + } + + /* + if((this.dirty_canvas || this.dirty_bgcanvas) && this.rendering_timer_id == null) + this.draw(); + */ + + this.graph.change(); + + e.stopPropagation(); + e.preventDefault(); + return false; + }; + + /** + * Called when a mouse wheel event has to be processed + * @method processMouseWheel + **/ + LGraphCanvas.prototype.processMouseWheel = function(e) { + if (!this.graph || !this.allow_dragcanvas) { + return; + } + + var delta = e.wheelDeltaY != null ? e.wheelDeltaY : e.detail * -60; + + this.adjustMouseEvent(e); + + var scale = this.ds.scale; + + if (delta > 0) { + scale *= 1.1; + } else if (delta < 0) { + scale *= 1 / 1.1; + } + + //this.setZoom( scale, [ e.localX, e.localY ] ); + this.ds.changeScale(scale, [e.localX, e.localY]); + + this.graph.change(); + + e.preventDefault(); + return false; // prevent default + }; + + /** + * returns true if a position (in graph space) is on top of a node little corner box + * @method isOverNodeBox + **/ + LGraphCanvas.prototype.isOverNodeBox = function(node, canvasx, canvasy) { + var title_height = LiteGraph.NODE_TITLE_HEIGHT; + if ( + isInsideRectangle( + canvasx, + canvasy, + node.pos[0] + 2, + node.pos[1] + 2 - title_height, + title_height - 4, + title_height - 4 + ) + ) { + return true; + } + return false; + }; + + /** + * returns true if a position (in graph space) is on top of a node input slot + * @method isOverNodeInput + **/ + LGraphCanvas.prototype.isOverNodeInput = function( + node, + canvasx, + canvasy, + slot_pos + ) { + if (node.inputs) { + for (var i = 0, l = node.inputs.length; i < l; ++i) { + var input = node.inputs[i]; + var link_pos = node.getConnectionPos(true, i); + var is_inside = false; + if (node.horizontal) { + is_inside = isInsideRectangle( + canvasx, + canvasy, + link_pos[0] - 5, + link_pos[1] - 10, + 10, + 20 + ); + } else { + is_inside = isInsideRectangle( + canvasx, + canvasy, + link_pos[0] - 10, + link_pos[1] - 5, + 40, + 10 + ); + } + if (is_inside) { + if (slot_pos) { + slot_pos[0] = link_pos[0]; + slot_pos[1] = link_pos[1]; + } + return i; + } + } + } + return -1; + }; + + /** + * process a key event + * @method processKey + **/ + LGraphCanvas.prototype.processKey = function(e) { + if (!this.graph) { + return; + } + + var block_default = false; + //console.log(e); //debug + + if (e.target.localName == "input") { + return; + } + + if (e.type == "keydown") { + if (e.keyCode == 32) { + //esc + this.dragging_canvas = true; + block_default = true; + } + + //select all Control A + if (e.keyCode == 65 && e.ctrlKey) { + this.selectNodes(); + block_default = true; + } + + if (e.code == "KeyC" && (e.metaKey || e.ctrlKey) && !e.shiftKey) { + //copy + if (this.selected_nodes) { + this.copyToClipboard(); + block_default = true; + } + } + + if (e.code == "KeyV" && (e.metaKey || e.ctrlKey) && !e.shiftKey) { + //paste + this.pasteFromClipboard(); + } + + //delete or backspace + if (e.keyCode == 46 || e.keyCode == 8) { + if ( + e.target.localName != "input" && + e.target.localName != "textarea" + ) { + this.deleteSelectedNodes(); + block_default = true; + } + } + + //collapse + //... + + //TODO + if (this.selected_nodes) { + for (var i in this.selected_nodes) { + if (this.selected_nodes[i].onKeyDown) { + this.selected_nodes[i].onKeyDown(e); + } + } + } + } else if (e.type == "keyup") { + if (e.keyCode == 32) { + this.dragging_canvas = false; + } + + if (this.selected_nodes) { + for (var i in this.selected_nodes) { + if (this.selected_nodes[i].onKeyUp) { + this.selected_nodes[i].onKeyUp(e); + } + } + } + } + + this.graph.change(); + + if (block_default) { + e.preventDefault(); + e.stopImmediatePropagation(); + return false; + } + }; + + LGraphCanvas.prototype.copyToClipboard = function() { + var clipboard_info = { + nodes: [], + links: [] + }; + var index = 0; + var selected_nodes_array = []; + for (var i in this.selected_nodes) { + var node = this.selected_nodes[i]; + node._relative_id = index; + selected_nodes_array.push(node); + index += 1; + } + + for (var i = 0; i < selected_nodes_array.length; ++i) { + var node = selected_nodes_array[i]; + var cloned = node.clone(); + if(!cloned) + { + console.warn("node type not found: " + node.type ); + continue; + } + clipboard_info.nodes.push(cloned.serialize()); + if (node.inputs && node.inputs.length) { + for (var j = 0; j < node.inputs.length; ++j) { + var input = node.inputs[j]; + if (!input || input.link == null) { + continue; + } + var link_info = this.graph.links[input.link]; + if (!link_info) { + continue; + } + var target_node = this.graph.getNodeById( + link_info.origin_id + ); + if (!target_node || !this.selected_nodes[target_node.id]) { + //improve this by allowing connections to non-selected nodes + continue; + } //not selected + clipboard_info.links.push([ + target_node._relative_id, + link_info.origin_slot, //j, + node._relative_id, + link_info.target_slot + ]); + } + } + } + localStorage.setItem( + "litegrapheditor_clipboard", + JSON.stringify(clipboard_info) + ); + }; + + LGraphCanvas.prototype.pasteFromClipboard = function() { + var data = localStorage.getItem("litegrapheditor_clipboard"); + if (!data) { + return; + } + + //create nodes + var clipboard_info = JSON.parse(data); + var nodes = []; + for (var i = 0; i < clipboard_info.nodes.length; ++i) { + var node_data = clipboard_info.nodes[i]; + var node = LiteGraph.createNode(node_data.type); + if (node) { + node.configure(node_data); + node.pos[0] += 5; + node.pos[1] += 5; + this.graph.add(node); + nodes.push(node); + } + } + + //create links + for (var i = 0; i < clipboard_info.links.length; ++i) { + var link_info = clipboard_info.links[i]; + var origin_node = nodes[link_info[0]]; + var target_node = nodes[link_info[2]]; + if( origin_node && target_node ) + origin_node.connect(link_info[1], target_node, link_info[3]); + else + console.warn("Warning, nodes missing on pasting"); + } + + this.selectNodes(nodes); + }; + + /** + * process a item drop event on top the canvas + * @method processDrop + **/ + LGraphCanvas.prototype.processDrop = function(e) { + e.preventDefault(); + this.adjustMouseEvent(e); + + var pos = [e.canvasX, e.canvasY]; + var node = this.graph.getNodeOnPos(pos[0], pos[1]); + + if (!node) { + var r = null; + if (this.onDropItem) { + r = this.onDropItem(event); + } + if (!r) { + this.checkDropItem(e); + } + return; + } + + if (node.onDropFile || node.onDropData) { + var files = e.dataTransfer.files; + if (files && files.length) { + for (var i = 0; i < files.length; i++) { + var file = e.dataTransfer.files[0]; + var filename = file.name; + var ext = LGraphCanvas.getFileExtension(filename); + //console.log(file); + + if (node.onDropFile) { + node.onDropFile(file); + } + + if (node.onDropData) { + //prepare reader + var reader = new FileReader(); + reader.onload = function(event) { + //console.log(event.target); + var data = event.target.result; + node.onDropData(data, filename, file); + }; + + //read data + var type = file.type.split("/")[0]; + if (type == "text" || type == "") { + reader.readAsText(file); + } else if (type == "image") { + reader.readAsDataURL(file); + } else { + reader.readAsArrayBuffer(file); + } + } + } + } + } + + if (node.onDropItem) { + if (node.onDropItem(event)) { + return true; + } + } + + if (this.onDropItem) { + return this.onDropItem(event); + } + + return false; + }; + + //called if the graph doesn't have a default drop item behaviour + LGraphCanvas.prototype.checkDropItem = function(e) { + if (e.dataTransfer.files.length) { + var file = e.dataTransfer.files[0]; + var ext = LGraphCanvas.getFileExtension(file.name).toLowerCase(); + var nodetype = LiteGraph.node_types_by_file_extension[ext]; + if (nodetype) { + var node = LiteGraph.createNode(nodetype.type); + node.pos = [e.canvasX, e.canvasY]; + this.graph.add(node); + if (node.onDropFile) { + node.onDropFile(file); + } + } + } + }; + + LGraphCanvas.prototype.processNodeDblClicked = function(n) { + if (this.onShowNodePanel) { + this.onShowNodePanel(n); + } + + if (this.onNodeDblClicked) { + this.onNodeDblClicked(n); + } + + this.setDirty(true); + }; + + LGraphCanvas.prototype.processNodeSelected = function(node, e) { + this.selectNode(node, e && e.shiftKey); + if (this.onNodeSelected) { + this.onNodeSelected(node); + } + }; + + /** + * selects a given node (or adds it to the current selection) + * @method selectNode + **/ + LGraphCanvas.prototype.selectNode = function( + node, + add_to_current_selection + ) { + if (node == null) { + this.deselectAllNodes(); + } else { + this.selectNodes([node], add_to_current_selection); + } + }; + + /** + * selects several nodes (or adds them to the current selection) + * @method selectNodes + **/ + LGraphCanvas.prototype.selectNodes = function( + nodes, + add_to_current_selection + ) { + if (!add_to_current_selection) { + this.deselectAllNodes(); + } + + nodes = nodes || this.graph._nodes; + for (var i = 0; i < nodes.length; ++i) { + var node = nodes[i]; + if (node.is_selected) { + continue; + } + + if (!node.is_selected && node.onSelected) { + node.onSelected(); + } + node.is_selected = true; + this.selected_nodes[node.id] = node; + + if (node.inputs) { + for (var j = 0; j < node.inputs.length; ++j) { + this.highlighted_links[node.inputs[j].link] = true; + } + } + if (node.outputs) { + for (var j = 0; j < node.outputs.length; ++j) { + var out = node.outputs[j]; + if (out.links) { + for (var k = 0; k < out.links.length; ++k) { + this.highlighted_links[out.links[k]] = true; + } + } + } + } + } + + if( this.onSelectionChange ) + this.onSelectionChange( this.selected_nodes ); + + this.setDirty(true); + }; + + /** + * removes a node from the current selection + * @method deselectNode + **/ + LGraphCanvas.prototype.deselectNode = function(node) { + if (!node.is_selected) { + return; + } + if (node.onDeselected) { + node.onDeselected(); + } + node.is_selected = false; + + if (this.onNodeDeselected) { + this.onNodeDeselected(node); + } + + //remove highlighted + if (node.inputs) { + for (var i = 0; i < node.inputs.length; ++i) { + delete this.highlighted_links[node.inputs[i].link]; + } + } + if (node.outputs) { + for (var i = 0; i < node.outputs.length; ++i) { + var out = node.outputs[i]; + if (out.links) { + for (var j = 0; j < out.links.length; ++j) { + delete this.highlighted_links[out.links[j]]; + } + } + } + } + }; + + /** + * removes all nodes from the current selection + * @method deselectAllNodes + **/ + LGraphCanvas.prototype.deselectAllNodes = function() { + if (!this.graph) { + return; + } + var nodes = this.graph._nodes; + for (var i = 0, l = nodes.length; i < l; ++i) { + var node = nodes[i]; + if (!node.is_selected) { + continue; + } + if (node.onDeselected) { + node.onDeselected(); + } + node.is_selected = false; + if (this.onNodeDeselected) { + this.onNodeDeselected(node); + } + } + this.selected_nodes = {}; + this.current_node = null; + this.highlighted_links = {}; + if( this.onSelectionChange ) + this.onSelectionChange( this.selected_nodes ); + this.setDirty(true); + }; + + /** + * deletes all nodes in the current selection from the graph + * @method deleteSelectedNodes + **/ + LGraphCanvas.prototype.deleteSelectedNodes = function() { + for (var i in this.selected_nodes) { + var node = this.selected_nodes[i]; + //autoconnect when possible (very basic, only takes into account first input-output) + if(node.inputs && node.inputs.length && node.outputs && node.outputs.length && LiteGraph.isValidConnection( node.inputs[0].type, node.outputs[0].type ) && node.inputs[0].link && node.outputs[0].links && node.outputs[0].links.length ) + { + var input_link = node.graph.links[ node.inputs[0].link ]; + var output_link = node.graph.links[ node.outputs[0].links[0] ]; + var input_node = node.getInputNode(0); + var output_node = node.getOutputNodes(0)[0]; + if(input_node && output_node) + input_node.connect( input_link.origin_slot, output_node, output_link.target_slot ); + } + this.graph.remove(node); + if (this.onNodeDeselected) { + this.onNodeDeselected(node); + } + } + this.selected_nodes = {}; + this.current_node = null; + this.highlighted_links = {}; + this.setDirty(true); + }; + + /** + * centers the camera on a given node + * @method centerOnNode + **/ + LGraphCanvas.prototype.centerOnNode = function(node) { + this.ds.offset[0] = + -node.pos[0] - + node.size[0] * 0.5 + + (this.canvas.width * 0.5) / this.ds.scale; + this.ds.offset[1] = + -node.pos[1] - + node.size[1] * 0.5 + + (this.canvas.height * 0.5) / this.ds.scale; + this.setDirty(true, true); + }; + + /** + * adds some useful properties to a mouse event, like the position in graph coordinates + * @method adjustMouseEvent + **/ + LGraphCanvas.prototype.adjustMouseEvent = function(e) { + if (this.canvas) { + var b = this.canvas.getBoundingClientRect(); + e.localX = e.clientX - b.left; + e.localY = e.clientY - b.top; + } else { + e.localX = e.clientX; + e.localY = e.clientY; + } + + e.deltaX = e.localX - this.last_mouse_position[0]; + e.deltaY = e.localY - this.last_mouse_position[1]; + + this.last_mouse_position[0] = e.localX; + this.last_mouse_position[1] = e.localY; + + e.canvasX = e.localX / this.ds.scale - this.ds.offset[0]; + e.canvasY = e.localY / this.ds.scale - this.ds.offset[1]; + }; + + /** + * changes the zoom level of the graph (default is 1), you can pass also a place used to pivot the zoom + * @method setZoom + **/ + LGraphCanvas.prototype.setZoom = function(value, zooming_center) { + this.ds.changeScale(value, zooming_center); + /* + if(!zooming_center && this.canvas) + zooming_center = [this.canvas.width * 0.5,this.canvas.height * 0.5]; + + var center = this.convertOffsetToCanvas( zooming_center ); + + this.ds.scale = value; + + if(this.scale > this.max_zoom) + this.scale = this.max_zoom; + else if(this.scale < this.min_zoom) + this.scale = this.min_zoom; + + var new_center = this.convertOffsetToCanvas( zooming_center ); + var delta_offset = [new_center[0] - center[0], new_center[1] - center[1]]; + + this.offset[0] += delta_offset[0]; + this.offset[1] += delta_offset[1]; + */ + + this.dirty_canvas = true; + this.dirty_bgcanvas = true; + }; + + /** + * converts a coordinate from graph coordinates to canvas2D coordinates + * @method convertOffsetToCanvas + **/ + LGraphCanvas.prototype.convertOffsetToCanvas = function(pos, out) { + return this.ds.convertOffsetToCanvas(pos, out); + }; + + /** + * converts a coordinate from Canvas2D coordinates to graph space + * @method convertCanvasToOffset + **/ + LGraphCanvas.prototype.convertCanvasToOffset = function(pos, out) { + return this.ds.convertCanvasToOffset(pos, out); + }; + + //converts event coordinates from canvas2D to graph coordinates + LGraphCanvas.prototype.convertEventToCanvasOffset = function(e) { + var rect = this.canvas.getBoundingClientRect(); + return this.convertCanvasToOffset([ + e.clientX - rect.left, + e.clientY - rect.top + ]); + }; + + /** + * brings a node to front (above all other nodes) + * @method bringToFront + **/ + LGraphCanvas.prototype.bringToFront = function(node) { + var i = this.graph._nodes.indexOf(node); + if (i == -1) { + return; + } + + this.graph._nodes.splice(i, 1); + this.graph._nodes.push(node); + }; + + /** + * sends a node to the back (below all other nodes) + * @method sendToBack + **/ + LGraphCanvas.prototype.sendToBack = function(node) { + var i = this.graph._nodes.indexOf(node); + if (i == -1) { + return; + } + + this.graph._nodes.splice(i, 1); + this.graph._nodes.unshift(node); + }; + + /* Interaction */ + + /* LGraphCanvas render */ + var temp = new Float32Array(4); + + /** + * checks which nodes are visible (inside the camera area) + * @method computeVisibleNodes + **/ + LGraphCanvas.prototype.computeVisibleNodes = function(nodes, out) { + var visible_nodes = out || []; + visible_nodes.length = 0; + nodes = nodes || this.graph._nodes; + for (var i = 0, l = nodes.length; i < l; ++i) { + var n = nodes[i]; + + //skip rendering nodes in live mode + if (this.live_mode && !n.onDrawBackground && !n.onDrawForeground) { + continue; + } + + if (!overlapBounding(this.visible_area, n.getBounding(temp))) { + continue; + } //out of the visible area + + visible_nodes.push(n); + } + return visible_nodes; + }; + + /** + * renders the whole canvas content, by rendering in two separated canvas, one containing the background grid and the connections, and one containing the nodes) + * @method draw + **/ + LGraphCanvas.prototype.draw = function(force_canvas, force_bgcanvas) { + if (!this.canvas) { + return; + } + + //fps counting + var now = LiteGraph.getTime(); + this.render_time = (now - this.last_draw_time) * 0.001; + this.last_draw_time = now; + + if (this.graph) { + this.ds.computeVisibleArea(); + } + + if ( + this.dirty_bgcanvas || + force_bgcanvas || + this.always_render_background || + (this.graph && + this.graph._last_trigger_time && + now - this.graph._last_trigger_time < 1000) + ) { + this.drawBackCanvas(); + } + + if (this.dirty_canvas || force_canvas) { + this.drawFrontCanvas(); + } + + this.fps = this.render_time ? 1.0 / this.render_time : 0; + this.frame += 1; + }; + + /** + * draws the front canvas (the one containing all the nodes) + * @method drawFrontCanvas + **/ + LGraphCanvas.prototype.drawFrontCanvas = function() { + this.dirty_canvas = false; + + if (!this.ctx) { + this.ctx = this.bgcanvas.getContext("2d"); + } + var ctx = this.ctx; + if (!ctx) { + //maybe is using webgl... + return; + } + + if (ctx.start2D) { + ctx.start2D(); + } + + var canvas = this.canvas; + + //reset in case of error + ctx.restore(); + ctx.setTransform(1, 0, 0, 1, 0, 0); + + //clip dirty area if there is one, otherwise work in full canvas + if (this.dirty_area) { + ctx.save(); + ctx.beginPath(); + ctx.rect( + this.dirty_area[0], + this.dirty_area[1], + this.dirty_area[2], + this.dirty_area[3] + ); + ctx.clip(); + } + + //clear + //canvas.width = canvas.width; + if (this.clear_background) { + ctx.clearRect(0, 0, canvas.width, canvas.height); + } + + //draw bg canvas + if (this.bgcanvas == this.canvas) { + this.drawBackCanvas(); + } else { + ctx.drawImage(this.bgcanvas, 0, 0); + } + + //rendering + if (this.onRender) { + this.onRender(canvas, ctx); + } + + //info widget + if (this.show_info) { + this.renderInfo(ctx); + } + + if (this.graph) { + //apply transformations + ctx.save(); + this.ds.toCanvasContext(ctx); + + //draw nodes + var drawn_nodes = 0; + var visible_nodes = this.computeVisibleNodes( + null, + this.visible_nodes + ); + + for (var i = 0; i < visible_nodes.length; ++i) { + var node = visible_nodes[i]; + + //transform coords system + ctx.save(); + ctx.translate(node.pos[0], node.pos[1]); + + //Draw + this.drawNode(node, ctx); + drawn_nodes += 1; + + //Restore + ctx.restore(); + } + + //on top (debug) + if (this.render_execution_order) { + this.drawExecutionOrder(ctx); + } + + //connections ontop? + if (this.graph.config.links_ontop) { + if (!this.live_mode) { + this.drawConnections(ctx); + } + } + + //current connection (the one being dragged by the mouse) + if (this.connecting_pos != null) { + ctx.lineWidth = this.connections_width; + var link_color = null; + + switch (this.connecting_output.type) { + case LiteGraph.EVENT: + link_color = LiteGraph.EVENT_LINK_COLOR; + break; + default: + link_color = LiteGraph.CONNECTING_LINK_COLOR; + } + + //the connection being dragged by the mouse + this.renderLink( + ctx, + this.connecting_pos, + [this.canvas_mouse[0], this.canvas_mouse[1]], + null, + false, + null, + link_color, + this.connecting_output.dir || + (this.connecting_node.horizontal + ? LiteGraph.DOWN + : LiteGraph.RIGHT), + LiteGraph.CENTER + ); + + ctx.beginPath(); + if ( + this.connecting_output.type === LiteGraph.EVENT || + this.connecting_output.shape === LiteGraph.BOX_SHAPE + ) { + ctx.rect( + this.connecting_pos[0] - 6 + 0.5, + this.connecting_pos[1] - 5 + 0.5, + 14, + 10 + ); + } else { + ctx.arc( + this.connecting_pos[0], + this.connecting_pos[1], + 4, + 0, + Math.PI * 2 + ); + } + ctx.fill(); + + ctx.fillStyle = "#ffcc00"; + if (this._highlight_input) { + ctx.beginPath(); + ctx.arc( + this._highlight_input[0], + this._highlight_input[1], + 6, + 0, + Math.PI * 2 + ); + ctx.fill(); + } + } + + //the selection rectangle + if (this.dragging_rectangle) { + ctx.strokeStyle = "#FFF"; + ctx.strokeRect( + this.dragging_rectangle[0], + this.dragging_rectangle[1], + this.dragging_rectangle[2], + this.dragging_rectangle[3] + ); + } + + //on top of link center + if(this.over_link_center && this.render_link_tooltip) + this.drawLinkTooltip( ctx, this.over_link_center ); + else + if(this.onDrawLinkTooltip) //to remove + this.onDrawLinkTooltip(ctx,null); + + //custom info + if (this.onDrawForeground) { + this.onDrawForeground(ctx, this.visible_rect); + } + + ctx.restore(); + } + + if (this.onDrawOverlay) { + this.onDrawOverlay(ctx); + } + + if (this.dirty_area) { + ctx.restore(); + //this.dirty_area = null; + } + + if (ctx.finish2D) { + //this is a function I use in webgl renderer + ctx.finish2D(); + } + }; + + /** + * draws some useful stats in the corner of the canvas + * @method renderInfo + **/ + LGraphCanvas.prototype.renderInfo = function(ctx, x, y) { + x = x || 0; + y = y || 0; + + ctx.save(); + ctx.translate(x, y); + + ctx.font = "10px Arial"; + ctx.fillStyle = "#888"; + if (this.graph) { + ctx.fillText( "T: " + this.graph.globaltime.toFixed(2) + "s", 5, 13 * 1 ); + ctx.fillText("I: " + this.graph.iteration, 5, 13 * 2 ); + ctx.fillText("N: " + this.graph._nodes.length + " [" + this.visible_nodes.length + "]", 5, 13 * 3 ); + ctx.fillText("V: " + this.graph._version, 5, 13 * 4); + ctx.fillText("FPS:" + this.fps.toFixed(2), 5, 13 * 5); + } else { + ctx.fillText("No graph selected", 5, 13 * 1); + } + ctx.restore(); + }; + + /** + * draws the back canvas (the one containing the background and the connections) + * @method drawBackCanvas + **/ + LGraphCanvas.prototype.drawBackCanvas = function() { + var canvas = this.bgcanvas; + if ( + canvas.width != this.canvas.width || + canvas.height != this.canvas.height + ) { + canvas.width = this.canvas.width; + canvas.height = this.canvas.height; + } + + if (!this.bgctx) { + this.bgctx = this.bgcanvas.getContext("2d"); + } + var ctx = this.bgctx; + if (ctx.start) { + ctx.start(); + } + + //clear + if (this.clear_background) { + ctx.clearRect(0, 0, canvas.width, canvas.height); + } + + if (this._graph_stack && this._graph_stack.length) { + ctx.save(); + var parent_graph = this._graph_stack[this._graph_stack.length - 1]; + var subgraph_node = this.graph._subgraph_node; + ctx.strokeStyle = subgraph_node.bgcolor; + ctx.lineWidth = 10; + ctx.strokeRect(1, 1, canvas.width - 2, canvas.height - 2); + ctx.lineWidth = 1; + ctx.font = "40px Arial"; + ctx.textAlign = "center"; + ctx.fillStyle = subgraph_node.bgcolor || "#AAA"; + var title = ""; + for (var i = 1; i < this._graph_stack.length; ++i) { + title += + this._graph_stack[i]._subgraph_node.getTitle() + " >> "; + } + ctx.fillText( + title + subgraph_node.getTitle(), + canvas.width * 0.5, + 40 + ); + ctx.restore(); + } + + var bg_already_painted = false; + if (this.onRenderBackground) { + bg_already_painted = this.onRenderBackground(canvas, ctx); + } + + //reset in case of error + ctx.restore(); + ctx.setTransform(1, 0, 0, 1, 0, 0); + this.visible_links.length = 0; + + if (this.graph) { + //apply transformations + ctx.save(); + this.ds.toCanvasContext(ctx); + + //render BG + if ( + this.background_image && + this.ds.scale > 0.5 && + !bg_already_painted + ) { + if (this.zoom_modify_alpha) { + ctx.globalAlpha = + (1.0 - 0.5 / this.ds.scale) * this.editor_alpha; + } else { + ctx.globalAlpha = this.editor_alpha; + } + ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.imageSmoothingEnabled = false; + if ( + !this._bg_img || + this._bg_img.name != this.background_image + ) { + this._bg_img = new Image(); + this._bg_img.name = this.background_image; + this._bg_img.src = this.background_image; + var that = this; + this._bg_img.onload = function() { + that.draw(true, true); + }; + } + + var pattern = null; + if (this._pattern == null && this._bg_img.width > 0) { + pattern = ctx.createPattern(this._bg_img, "repeat"); + this._pattern_img = this._bg_img; + this._pattern = pattern; + } else { + pattern = this._pattern; + } + if (pattern) { + ctx.fillStyle = pattern; + ctx.fillRect( + this.visible_area[0], + this.visible_area[1], + this.visible_area[2], + this.visible_area[3] + ); + ctx.fillStyle = "transparent"; + } + + ctx.globalAlpha = 1.0; + ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.imageSmoothingEnabled = true; + } + + //groups + if (this.graph._groups.length && !this.live_mode) { + this.drawGroups(canvas, ctx); + } + + if (this.onDrawBackground) { + this.onDrawBackground(ctx, this.visible_area); + } + if (this.onBackgroundRender) { + //LEGACY + console.error( + "WARNING! onBackgroundRender deprecated, now is named onDrawBackground " + ); + this.onBackgroundRender = null; + } + + //DEBUG: show clipping area + //ctx.fillStyle = "red"; + //ctx.fillRect( this.visible_area[0] + 10, this.visible_area[1] + 10, this.visible_area[2] - 20, this.visible_area[3] - 20); + + //bg + if (this.render_canvas_border) { + ctx.strokeStyle = "#235"; + ctx.strokeRect(0, 0, canvas.width, canvas.height); + } + + if (this.render_connections_shadows) { + ctx.shadowColor = "#000"; + ctx.shadowOffsetX = 0; + ctx.shadowOffsetY = 0; + ctx.shadowBlur = 6; + } else { + ctx.shadowColor = "rgba(0,0,0,0)"; + } + + //draw connections + if (!this.live_mode) { + this.drawConnections(ctx); + } + + ctx.shadowColor = "rgba(0,0,0,0)"; + + //restore state + ctx.restore(); + } + + if (ctx.finish) { + ctx.finish(); + } + + this.dirty_bgcanvas = false; + this.dirty_canvas = true; //to force to repaint the front canvas with the bgcanvas + }; + + var temp_vec2 = new Float32Array(2); + + /** + * draws the given node inside the canvas + * @method drawNode + **/ + LGraphCanvas.prototype.drawNode = function(node, ctx) { + var glow = false; + this.current_node = node; + + var color = + node.color || + node.constructor.color || + LiteGraph.NODE_DEFAULT_COLOR; + var bgcolor = + node.bgcolor || + node.constructor.bgcolor || + LiteGraph.NODE_DEFAULT_BGCOLOR; + + //shadow and glow + if (node.mouseOver) { + glow = true; + } + + var low_quality = this.ds.scale < 0.6; //zoomed out + + //only render if it forces it to do it + if (this.live_mode) { + if (!node.flags.collapsed) { + ctx.shadowColor = "transparent"; + if (node.onDrawForeground) { + node.onDrawForeground(ctx, this, this.canvas); + } + } + return; + } + + var editor_alpha = this.editor_alpha; + ctx.globalAlpha = editor_alpha; + + if (this.render_shadows && !low_quality) { + ctx.shadowColor = LiteGraph.DEFAULT_SHADOW_COLOR; + ctx.shadowOffsetX = 2 * this.ds.scale; + ctx.shadowOffsetY = 2 * this.ds.scale; + ctx.shadowBlur = 3 * this.ds.scale; + } else { + ctx.shadowColor = "transparent"; + } + + //custom draw collapsed method (draw after shadows because they are affected) + if ( + node.flags.collapsed && + node.onDrawCollapsed && + node.onDrawCollapsed(ctx, this) == true + ) { + return; + } + + //clip if required (mask) + var shape = node._shape || LiteGraph.BOX_SHAPE; + var size = temp_vec2; + temp_vec2.set(node.size); + var horizontal = node.horizontal; // || node.flags.horizontal; + + if (node.flags.collapsed) { + ctx.font = this.inner_text_font; + var title = node.getTitle ? node.getTitle() : node.title; + if (title != null) { + node._collapsed_width = Math.min( + node.size[0], + ctx.measureText(title).width + + LiteGraph.NODE_TITLE_HEIGHT * 2 + ); //LiteGraph.NODE_COLLAPSED_WIDTH; + size[0] = node._collapsed_width; + size[1] = 0; + } + } + + if (node.clip_area) { + //Start clipping + ctx.save(); + ctx.beginPath(); + if (shape == LiteGraph.BOX_SHAPE) { + ctx.rect(0, 0, size[0], size[1]); + } else if (shape == LiteGraph.ROUND_SHAPE) { + ctx.roundRect(0, 0, size[0], size[1], 10); + } else if (shape == LiteGraph.CIRCLE_SHAPE) { + ctx.arc( + size[0] * 0.5, + size[1] * 0.5, + size[0] * 0.5, + 0, + Math.PI * 2 + ); + } + ctx.clip(); + } + + //draw shape + if (node.has_errors) { + bgcolor = "red"; + } + this.drawNodeShape( + node, + ctx, + size, + color, + bgcolor, + node.is_selected, + node.mouseOver + ); + ctx.shadowColor = "transparent"; + + //draw foreground + if (node.onDrawForeground) { + node.onDrawForeground(ctx, this, this.canvas); + } + + //connection slots + ctx.textAlign = horizontal ? "center" : "left"; + ctx.font = this.inner_text_font; + + var render_text = !low_quality; + + var out_slot = this.connecting_output; + ctx.lineWidth = 1; + + var max_y = 0; + var slot_pos = new Float32Array(2); //to reuse + + //render inputs and outputs + if (!node.flags.collapsed) { + //input connection slots + if (node.inputs) { + for (var i = 0; i < node.inputs.length; i++) { + var slot = node.inputs[i]; + + ctx.globalAlpha = editor_alpha; + //change opacity of incompatible slots when dragging a connection + if ( this.connecting_node && !LiteGraph.isValidConnection( slot.type , out_slot.type) ) { + ctx.globalAlpha = 0.4 * editor_alpha; + } + + ctx.fillStyle = + slot.link != null + ? slot.color_on || + this.default_connection_color.input_on + : slot.color_off || + this.default_connection_color.input_off; + + var pos = node.getConnectionPos(true, i, slot_pos); + pos[0] -= node.pos[0]; + pos[1] -= node.pos[1]; + if (max_y < pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5) { + max_y = pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5; + } + + ctx.beginPath(); + + if ( + slot.type === LiteGraph.EVENT || + slot.shape === LiteGraph.BOX_SHAPE + ) { + if (horizontal) { + ctx.rect( + pos[0] - 5 + 0.5, + pos[1] - 8 + 0.5, + 10, + 14 + ); + } else { + ctx.rect( + pos[0] - 6 + 0.5, + pos[1] - 5 + 0.5, + 14, + 10 + ); + } + } else if (slot.shape === LiteGraph.ARROW_SHAPE) { + ctx.moveTo(pos[0] + 8, pos[1] + 0.5); + ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5); + ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5); + ctx.closePath(); + } else { + if(low_quality) + ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); //faster + else + ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); + } + ctx.fill(); + + //render name + if (render_text) { + var text = slot.label != null ? slot.label : slot.name; + if (text) { + ctx.fillStyle = LiteGraph.NODE_TEXT_COLOR; + if (horizontal || slot.dir == LiteGraph.UP) { + ctx.fillText(text, pos[0], pos[1] - 10); + } else { + ctx.fillText(text, pos[0] + 10, pos[1] + 5); + } + } + } + } + } + + //output connection slots + if (this.connecting_node) { + ctx.globalAlpha = 0.4 * editor_alpha; + } + + ctx.textAlign = horizontal ? "center" : "right"; + ctx.strokeStyle = "black"; + if (node.outputs) { + for (var i = 0; i < node.outputs.length; i++) { + var slot = node.outputs[i]; + + var pos = node.getConnectionPos(false, i, slot_pos); + pos[0] -= node.pos[0]; + pos[1] -= node.pos[1]; + if (max_y < pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5) { + max_y = pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5; + } + + ctx.fillStyle = + slot.links && slot.links.length + ? slot.color_on || + this.default_connection_color.output_on + : slot.color_off || + this.default_connection_color.output_off; + ctx.beginPath(); + //ctx.rect( node.size[0] - 14,i*14,10,10); + + if ( + slot.type === LiteGraph.EVENT || + slot.shape === LiteGraph.BOX_SHAPE + ) { + if (horizontal) { + ctx.rect( + pos[0] - 5 + 0.5, + pos[1] - 8 + 0.5, + 10, + 14 + ); + } else { + ctx.rect( + pos[0] - 6 + 0.5, + pos[1] - 5 + 0.5, + 14, + 10 + ); + } + } else if (slot.shape === LiteGraph.ARROW_SHAPE) { + ctx.moveTo(pos[0] + 8, pos[1] + 0.5); + ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5); + ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5); + ctx.closePath(); + } else { + if(low_quality) + ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); + else + ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); + } + + //trigger + //if(slot.node_id != null && slot.slot == -1) + // ctx.fillStyle = "#F85"; + + //if(slot.links != null && slot.links.length) + ctx.fill(); + if(!low_quality) + ctx.stroke(); + + //render output name + if (render_text) { + var text = slot.label != null ? slot.label : slot.name; + if (text) { + ctx.fillStyle = LiteGraph.NODE_TEXT_COLOR; + if (horizontal || slot.dir == LiteGraph.DOWN) { + ctx.fillText(text, pos[0], pos[1] - 8); + } else { + ctx.fillText(text, pos[0] - 10, pos[1] + 5); + } + } + } + } + } + + ctx.textAlign = "left"; + ctx.globalAlpha = 1; + + if (node.widgets) { + var widgets_y = max_y; + if (horizontal || node.widgets_up) { + widgets_y = 2; + } + if( node.widgets_start_y != null ) + widgets_y = node.widgets_start_y; + this.drawNodeWidgets( + node, + widgets_y, + ctx, + this.node_widget && this.node_widget[0] == node + ? this.node_widget[1] + : null + ); + } + } else if (this.render_collapsed_slots) { + //if collapsed + var input_slot = null; + var output_slot = null; + + //get first connected slot to render + if (node.inputs) { + for (var i = 0; i < node.inputs.length; i++) { + var slot = node.inputs[i]; + if (slot.link == null) { + continue; + } + input_slot = slot; + break; + } + } + if (node.outputs) { + for (var i = 0; i < node.outputs.length; i++) { + var slot = node.outputs[i]; + if (!slot.links || !slot.links.length) { + continue; + } + output_slot = slot; + } + } + + if (input_slot) { + var x = 0; + var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; //center + if (horizontal) { + x = node._collapsed_width * 0.5; + y = -LiteGraph.NODE_TITLE_HEIGHT; + } + ctx.fillStyle = "#686"; + ctx.beginPath(); + if ( + slot.type === LiteGraph.EVENT || + slot.shape === LiteGraph.BOX_SHAPE + ) { + ctx.rect(x - 7 + 0.5, y - 4, 14, 8); + } else if (slot.shape === LiteGraph.ARROW_SHAPE) { + ctx.moveTo(x + 8, y); + ctx.lineTo(x + -4, y - 4); + ctx.lineTo(x + -4, y + 4); + ctx.closePath(); + } else { + ctx.arc(x, y, 4, 0, Math.PI * 2); + } + ctx.fill(); + } + + if (output_slot) { + var x = node._collapsed_width; + var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; //center + if (horizontal) { + x = node._collapsed_width * 0.5; + y = 0; + } + ctx.fillStyle = "#686"; + ctx.strokeStyle = "black"; + ctx.beginPath(); + if ( + slot.type === LiteGraph.EVENT || + slot.shape === LiteGraph.BOX_SHAPE + ) { + ctx.rect(x - 7 + 0.5, y - 4, 14, 8); + } else if (slot.shape === LiteGraph.ARROW_SHAPE) { + ctx.moveTo(x + 6, y); + ctx.lineTo(x - 6, y - 4); + ctx.lineTo(x - 6, y + 4); + ctx.closePath(); + } else { + ctx.arc(x, y, 4, 0, Math.PI * 2); + } + ctx.fill(); + //ctx.stroke(); + } + } + + if (node.clip_area) { + ctx.restore(); + } + + ctx.globalAlpha = 1.0; + }; + + //used by this.over_link_center + LGraphCanvas.prototype.drawLinkTooltip = function( ctx, link ) + { + var pos = link._pos; + ctx.fillStyle = "black"; + ctx.beginPath(); + ctx.arc( pos[0], pos[1], 3, 0, Math.PI * 2 ); + ctx.fill(); + + if(link.data == null) + return; + + if(this.onDrawLinkTooltip) + if( this.onDrawLinkTooltip(ctx,link,this) == true ) + return; + + var data = link.data; + var text = null; + + if( data.constructor === Number ) + text = data.toFixed(2); + else if( data.constructor === String ) + text = "\"" + data + "\""; + else if( data.constructor === Boolean ) + text = String(data); + else if (data.toToolTip) + text = data.toToolTip(); + else + text = "[" + data.constructor.name + "]"; + + if(text == null) + return; + text = text.substr(0,30); //avoid weird + + ctx.font = "14px Courier New"; + var info = ctx.measureText(text); + var w = info.width + 20; + var h = 24; + ctx.shadowColor = "black"; + ctx.shadowOffsetX = 2; + ctx.shadowOffsetY = 2; + ctx.shadowBlur = 3; + ctx.fillStyle = "#454"; + ctx.beginPath(); + ctx.roundRect( pos[0] - w*0.5, pos[1] - 15 - h, w, h,3, 3); + ctx.moveTo( pos[0] - 10, pos[1] - 15 ); + ctx.lineTo( pos[0] + 10, pos[1] - 15 ); + ctx.lineTo( pos[0], pos[1] - 5 ); + ctx.fill(); + ctx.shadowColor = "transparent"; + ctx.textAlign = "center"; + ctx.fillStyle = "#CEC"; + ctx.fillText(text, pos[0], pos[1] - 15 - h * 0.3); + } + + /** + * draws the shape of the given node in the canvas + * @method drawNodeShape + **/ + var tmp_area = new Float32Array(4); + + LGraphCanvas.prototype.drawNodeShape = function( + node, + ctx, + size, + fgcolor, + bgcolor, + selected, + mouse_over + ) { + //bg rect + ctx.strokeStyle = fgcolor; + ctx.fillStyle = bgcolor; + + var title_height = LiteGraph.NODE_TITLE_HEIGHT; + var low_quality = this.ds.scale < 0.5; + + //render node area depending on shape + var shape = + node._shape || node.constructor.shape || LiteGraph.ROUND_SHAPE; + + var title_mode = node.constructor.title_mode; + + var render_title = true; + if (title_mode == LiteGraph.TRANSPARENT_TITLE) { + render_title = false; + } else if (title_mode == LiteGraph.AUTOHIDE_TITLE && mouse_over) { + render_title = true; + } + + var area = tmp_area; + area[0] = 0; //x + area[1] = render_title ? -title_height : 0; //y + area[2] = size[0] + 1; //w + area[3] = render_title ? size[1] + title_height : size[1]; //h + + var old_alpha = ctx.globalAlpha; + + //full node shape + //if(node.flags.collapsed) + { + ctx.beginPath(); + if (shape == LiteGraph.BOX_SHAPE || low_quality) { + ctx.fillRect(area[0], area[1], area[2], area[3]); + } else if ( + shape == LiteGraph.ROUND_SHAPE || + shape == LiteGraph.CARD_SHAPE + ) { + ctx.roundRect( + area[0], + area[1], + area[2], + area[3], + this.round_radius, + shape == LiteGraph.CARD_SHAPE ? 0 : this.round_radius + ); + } else if (shape == LiteGraph.CIRCLE_SHAPE) { + ctx.arc( + size[0] * 0.5, + size[1] * 0.5, + size[0] * 0.5, + 0, + Math.PI * 2 + ); + } + ctx.fill(); + + //separator + if(!node.flags.collapsed) + { + ctx.shadowColor = "transparent"; + ctx.fillStyle = "rgba(0,0,0,0.2)"; + ctx.fillRect(0, -1, area[2], 2); + } + } + ctx.shadowColor = "transparent"; + + if (node.onDrawBackground) { + node.onDrawBackground(ctx, this, this.canvas); + } + + //title bg (remember, it is rendered ABOVE the node) + if (render_title || title_mode == LiteGraph.TRANSPARENT_TITLE) { + //title bar + if (node.onDrawTitleBar) { + node.onDrawTitleBar( + ctx, + title_height, + size, + this.ds.scale, + fgcolor + ); + } else if ( + title_mode != LiteGraph.TRANSPARENT_TITLE && + (node.constructor.title_color || this.render_title_colored) + ) { + var title_color = node.constructor.title_color || fgcolor; + + if (node.flags.collapsed) { + ctx.shadowColor = LiteGraph.DEFAULT_SHADOW_COLOR; + } + + //* gradient test + if (this.use_gradients) { + var grad = LGraphCanvas.gradients[title_color]; + if (!grad) { + grad = LGraphCanvas.gradients[ title_color ] = ctx.createLinearGradient(0, 0, 400, 0); + grad.addColorStop(0, title_color); + grad.addColorStop(1, "#000"); + } + ctx.fillStyle = grad; + } else { + ctx.fillStyle = title_color; + } + + //ctx.globalAlpha = 0.5 * old_alpha; + ctx.beginPath(); + if (shape == LiteGraph.BOX_SHAPE || low_quality) { + ctx.rect(0, -title_height, size[0] + 1, title_height); + } else if ( shape == LiteGraph.ROUND_SHAPE || shape == LiteGraph.CARD_SHAPE ) { + ctx.roundRect( + 0, + -title_height, + size[0] + 1, + title_height, + this.round_radius, + node.flags.collapsed ? this.round_radius : 0 + ); + } + ctx.fill(); + ctx.shadowColor = "transparent"; + } + + //title box + var box_size = 10; + if (node.onDrawTitleBox) { + node.onDrawTitleBox(ctx, title_height, size, this.ds.scale); + } else if ( + shape == LiteGraph.ROUND_SHAPE || + shape == LiteGraph.CIRCLE_SHAPE || + shape == LiteGraph.CARD_SHAPE + ) { + if (low_quality) { + ctx.fillStyle = "black"; + ctx.beginPath(); + ctx.arc( + title_height * 0.5, + title_height * -0.5, + box_size * 0.5 + 1, + 0, + Math.PI * 2 + ); + ctx.fill(); + } + + ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR; + if(low_quality) + ctx.fillRect( title_height * 0.5 - box_size *0.5, title_height * -0.5 - box_size *0.5, box_size , box_size ); + else + { + ctx.beginPath(); + ctx.arc( + title_height * 0.5, + title_height * -0.5, + box_size * 0.5, + 0, + Math.PI * 2 + ); + ctx.fill(); + } + } else { + if (low_quality) { + ctx.fillStyle = "black"; + ctx.fillRect( + (title_height - box_size) * 0.5 - 1, + (title_height + box_size) * -0.5 - 1, + box_size + 2, + box_size + 2 + ); + } + ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR; + ctx.fillRect( + (title_height - box_size) * 0.5, + (title_height + box_size) * -0.5, + box_size, + box_size + ); + } + ctx.globalAlpha = old_alpha; + + //title text + if (node.onDrawTitleText) { + node.onDrawTitleText( + ctx, + title_height, + size, + this.ds.scale, + this.title_text_font, + selected + ); + } + if (!low_quality) { + ctx.font = this.title_text_font; + var title = String(node.getTitle()); + if (title) { + if (selected) { + ctx.fillStyle = "white"; + } else { + ctx.fillStyle = + node.constructor.title_text_color || + this.node_title_color; + } + if (node.flags.collapsed) { + ctx.textAlign = "left"; + var measure = ctx.measureText(title); + ctx.fillText( + title.substr(0,20), //avoid urls too long + title_height,// + measure.width * 0.5, + LiteGraph.NODE_TITLE_TEXT_Y - title_height + ); + ctx.textAlign = "left"; + } else { + ctx.textAlign = "left"; + ctx.fillText( + title, + title_height, + LiteGraph.NODE_TITLE_TEXT_Y - title_height + ); + } + } + } + + if (node.onDrawTitle) { + node.onDrawTitle(ctx); + } + } + + //render selection marker + if (selected) { + if (node.onBounding) { + node.onBounding(area); + } + + if (title_mode == LiteGraph.TRANSPARENT_TITLE) { + area[1] -= title_height; + area[3] += title_height; + } + ctx.lineWidth = 1; + ctx.globalAlpha = 0.8; + ctx.beginPath(); + if (shape == LiteGraph.BOX_SHAPE) { + ctx.rect( + -6 + area[0], + -6 + area[1], + 12 + area[2], + 12 + area[3] + ); + } else if ( + shape == LiteGraph.ROUND_SHAPE || + (shape == LiteGraph.CARD_SHAPE && node.flags.collapsed) + ) { + ctx.roundRect( + -6 + area[0], + -6 + area[1], + 12 + area[2], + 12 + area[3], + this.round_radius * 2 + ); + } else if (shape == LiteGraph.CARD_SHAPE) { + ctx.roundRect( + -6 + area[0], + -6 + area[1], + 12 + area[2], + 12 + area[3], + this.round_radius * 2, + 2 + ); + } else if (shape == LiteGraph.CIRCLE_SHAPE) { + ctx.arc( + size[0] * 0.5, + size[1] * 0.5, + size[0] * 0.5 + 6, + 0, + Math.PI * 2 + ); + } + ctx.strokeStyle = "#FFF"; + ctx.stroke(); + ctx.strokeStyle = fgcolor; + ctx.globalAlpha = 1; + } + }; + + var margin_area = new Float32Array(4); + var link_bounding = new Float32Array(4); + var tempA = new Float32Array(2); + var tempB = new Float32Array(2); + + /** + * draws every connection visible in the canvas + * OPTIMIZE THIS: pre-catch connections position instead of recomputing them every time + * @method drawConnections + **/ + LGraphCanvas.prototype.drawConnections = function(ctx) { + var now = LiteGraph.getTime(); + var visible_area = this.visible_area; + margin_area[0] = visible_area[0] - 20; + margin_area[1] = visible_area[1] - 20; + margin_area[2] = visible_area[2] + 40; + margin_area[3] = visible_area[3] + 40; + + //draw connections + ctx.lineWidth = this.connections_width; + + ctx.fillStyle = "#AAA"; + ctx.strokeStyle = "#AAA"; + ctx.globalAlpha = this.editor_alpha; + //for every node + var nodes = this.graph._nodes; + for (var n = 0, l = nodes.length; n < l; ++n) { + var node = nodes[n]; + //for every input (we render just inputs because it is easier as every slot can only have one input) + if (!node.inputs || !node.inputs.length) { + continue; + } + + for (var i = 0; i < node.inputs.length; ++i) { + var input = node.inputs[i]; + if (!input || input.link == null) { + continue; + } + var link_id = input.link; + var link = this.graph.links[link_id]; + if (!link) { + continue; + } + + //find link info + var start_node = this.graph.getNodeById(link.origin_id); + if (start_node == null) { + continue; + } + var start_node_slot = link.origin_slot; + var start_node_slotpos = null; + if (start_node_slot == -1) { + start_node_slotpos = [ + start_node.pos[0] + 10, + start_node.pos[1] + 10 + ]; + } else { + start_node_slotpos = start_node.getConnectionPos( + false, + start_node_slot, + tempA + ); + } + var end_node_slotpos = node.getConnectionPos(true, i, tempB); + + //compute link bounding + link_bounding[0] = start_node_slotpos[0]; + link_bounding[1] = start_node_slotpos[1]; + link_bounding[2] = end_node_slotpos[0] - start_node_slotpos[0]; + link_bounding[3] = end_node_slotpos[1] - start_node_slotpos[1]; + if (link_bounding[2] < 0) { + link_bounding[0] += link_bounding[2]; + link_bounding[2] = Math.abs(link_bounding[2]); + } + if (link_bounding[3] < 0) { + link_bounding[1] += link_bounding[3]; + link_bounding[3] = Math.abs(link_bounding[3]); + } + + //skip links outside of the visible area of the canvas + if (!overlapBounding(link_bounding, margin_area)) { + continue; + } + + var start_slot = start_node.outputs[start_node_slot]; + var end_slot = node.inputs[i]; + if (!start_slot || !end_slot) { + continue; + } + var start_dir = + start_slot.dir || + (start_node.horizontal ? LiteGraph.DOWN : LiteGraph.RIGHT); + var end_dir = + end_slot.dir || + (node.horizontal ? LiteGraph.UP : LiteGraph.LEFT); + + this.renderLink( + ctx, + start_node_slotpos, + end_node_slotpos, + link, + false, + 0, + null, + start_dir, + end_dir + ); + + //event triggered rendered on top + if (link && link._last_time && now - link._last_time < 1000) { + var f = 2.0 - (now - link._last_time) * 0.002; + var tmp = ctx.globalAlpha; + ctx.globalAlpha = tmp * f; + this.renderLink( + ctx, + start_node_slotpos, + end_node_slotpos, + link, + true, + f, + "white", + start_dir, + end_dir + ); + ctx.globalAlpha = tmp; + } + } + } + ctx.globalAlpha = 1; + }; + + /** + * draws a link between two points + * @method renderLink + * @param {vec2} a start pos + * @param {vec2} b end pos + * @param {Object} link the link object with all the link info + * @param {boolean} skip_border ignore the shadow of the link + * @param {boolean} flow show flow animation (for events) + * @param {string} color the color for the link + * @param {number} start_dir the direction enum + * @param {number} end_dir the direction enum + * @param {number} num_sublines number of sublines (useful to represent vec3 or rgb) + **/ + LGraphCanvas.prototype.renderLink = function( + ctx, + a, + b, + link, + skip_border, + flow, + color, + start_dir, + end_dir, + num_sublines + ) { + if (link) { + this.visible_links.push(link); + } + + //choose color + if (!color && link) { + color = link.color || LGraphCanvas.link_type_colors[link.type]; + } + if (!color) { + color = this.default_link_color; + } + if (link != null && this.highlighted_links[link.id]) { + color = "#FFF"; + } + + start_dir = start_dir || LiteGraph.RIGHT; + end_dir = end_dir || LiteGraph.LEFT; + + var dist = distance(a, b); + + if (this.render_connections_border && this.ds.scale > 0.6) { + ctx.lineWidth = this.connections_width + 4; + } + ctx.lineJoin = "round"; + num_sublines = num_sublines || 1; + if (num_sublines > 1) { + ctx.lineWidth = 0.5; + } + + //begin line shape + ctx.beginPath(); + for (var i = 0; i < num_sublines; i += 1) { + var offsety = (i - (num_sublines - 1) * 0.5) * 5; + + if (this.links_render_mode == LiteGraph.SPLINE_LINK) { + ctx.moveTo(a[0], a[1] + offsety); + var start_offset_x = 0; + var start_offset_y = 0; + var end_offset_x = 0; + var end_offset_y = 0; + switch (start_dir) { + case LiteGraph.LEFT: + start_offset_x = dist * -0.25; + break; + case LiteGraph.RIGHT: + start_offset_x = dist * 0.25; + break; + case LiteGraph.UP: + start_offset_y = dist * -0.25; + break; + case LiteGraph.DOWN: + start_offset_y = dist * 0.25; + break; + } + switch (end_dir) { + case LiteGraph.LEFT: + end_offset_x = dist * -0.25; + break; + case LiteGraph.RIGHT: + end_offset_x = dist * 0.25; + break; + case LiteGraph.UP: + end_offset_y = dist * -0.25; + break; + case LiteGraph.DOWN: + end_offset_y = dist * 0.25; + break; + } + ctx.bezierCurveTo( + a[0] + start_offset_x, + a[1] + start_offset_y + offsety, + b[0] + end_offset_x, + b[1] + end_offset_y + offsety, + b[0], + b[1] + offsety + ); + } else if (this.links_render_mode == LiteGraph.LINEAR_LINK) { + ctx.moveTo(a[0], a[1] + offsety); + var start_offset_x = 0; + var start_offset_y = 0; + var end_offset_x = 0; + var end_offset_y = 0; + switch (start_dir) { + case LiteGraph.LEFT: + start_offset_x = -1; + break; + case LiteGraph.RIGHT: + start_offset_x = 1; + break; + case LiteGraph.UP: + start_offset_y = -1; + break; + case LiteGraph.DOWN: + start_offset_y = 1; + break; + } + switch (end_dir) { + case LiteGraph.LEFT: + end_offset_x = -1; + break; + case LiteGraph.RIGHT: + end_offset_x = 1; + break; + case LiteGraph.UP: + end_offset_y = -1; + break; + case LiteGraph.DOWN: + end_offset_y = 1; + break; + } + var l = 15; + ctx.lineTo( + a[0] + start_offset_x * l, + a[1] + start_offset_y * l + offsety + ); + ctx.lineTo( + b[0] + end_offset_x * l, + b[1] + end_offset_y * l + offsety + ); + ctx.lineTo(b[0], b[1] + offsety); + } else if (this.links_render_mode == LiteGraph.STRAIGHT_LINK) { + ctx.moveTo(a[0], a[1]); + var start_x = a[0]; + var start_y = a[1]; + var end_x = b[0]; + var end_y = b[1]; + if (start_dir == LiteGraph.RIGHT) { + start_x += 10; + } else { + start_y += 10; + } + if (end_dir == LiteGraph.LEFT) { + end_x -= 10; + } else { + end_y -= 10; + } + ctx.lineTo(start_x, start_y); + ctx.lineTo((start_x + end_x) * 0.5, start_y); + ctx.lineTo((start_x + end_x) * 0.5, end_y); + ctx.lineTo(end_x, end_y); + ctx.lineTo(b[0], b[1]); + } else { + return; + } //unknown + } + + //rendering the outline of the connection can be a little bit slow + if ( + this.render_connections_border && + this.ds.scale > 0.6 && + !skip_border + ) { + ctx.strokeStyle = "rgba(0,0,0,0.5)"; + ctx.stroke(); + } + + ctx.lineWidth = this.connections_width; + ctx.fillStyle = ctx.strokeStyle = color; + ctx.stroke(); + //end line shape + + var pos = this.computeConnectionPoint(a, b, 0.5, start_dir, end_dir); + if (link && link._pos) { + link._pos[0] = pos[0]; + link._pos[1] = pos[1]; + } + + //render arrow in the middle + if ( + this.ds.scale >= 0.6 && + this.highquality_render && + end_dir != LiteGraph.CENTER + ) { + //render arrow + if (this.render_connection_arrows) { + //compute two points in the connection + var posA = this.computeConnectionPoint( + a, + b, + 0.25, + start_dir, + end_dir + ); + var posB = this.computeConnectionPoint( + a, + b, + 0.26, + start_dir, + end_dir + ); + var posC = this.computeConnectionPoint( + a, + b, + 0.75, + start_dir, + end_dir + ); + var posD = this.computeConnectionPoint( + a, + b, + 0.76, + start_dir, + end_dir + ); + + //compute the angle between them so the arrow points in the right direction + var angleA = 0; + var angleB = 0; + if (this.render_curved_connections) { + angleA = -Math.atan2(posB[0] - posA[0], posB[1] - posA[1]); + angleB = -Math.atan2(posD[0] - posC[0], posD[1] - posC[1]); + } else { + angleB = angleA = b[1] > a[1] ? 0 : Math.PI; + } + + //render arrow + ctx.save(); + ctx.translate(posA[0], posA[1]); + ctx.rotate(angleA); + ctx.beginPath(); + ctx.moveTo(-5, -3); + ctx.lineTo(0, +7); + ctx.lineTo(+5, -3); + ctx.fill(); + ctx.restore(); + ctx.save(); + ctx.translate(posC[0], posC[1]); + ctx.rotate(angleB); + ctx.beginPath(); + ctx.moveTo(-5, -3); + ctx.lineTo(0, +7); + ctx.lineTo(+5, -3); + ctx.fill(); + ctx.restore(); + } + + //circle + ctx.beginPath(); + ctx.arc(pos[0], pos[1], 5, 0, Math.PI * 2); + ctx.fill(); + } + + //render flowing points + if (flow) { + ctx.fillStyle = color; + for (var i = 0; i < 5; ++i) { + var f = (LiteGraph.getTime() * 0.001 + i * 0.2) % 1; + var pos = this.computeConnectionPoint( + a, + b, + f, + start_dir, + end_dir + ); + ctx.beginPath(); + ctx.arc(pos[0], pos[1], 5, 0, 2 * Math.PI); + ctx.fill(); + } + } + }; + + //returns the link center point based on curvature + LGraphCanvas.prototype.computeConnectionPoint = function( + a, + b, + t, + start_dir, + end_dir + ) { + start_dir = start_dir || LiteGraph.RIGHT; + end_dir = end_dir || LiteGraph.LEFT; + + var dist = distance(a, b); + var p0 = a; + var p1 = [a[0], a[1]]; + var p2 = [b[0], b[1]]; + var p3 = b; + + switch (start_dir) { + case LiteGraph.LEFT: + p1[0] += dist * -0.25; + break; + case LiteGraph.RIGHT: + p1[0] += dist * 0.25; + break; + case LiteGraph.UP: + p1[1] += dist * -0.25; + break; + case LiteGraph.DOWN: + p1[1] += dist * 0.25; + break; + } + switch (end_dir) { + case LiteGraph.LEFT: + p2[0] += dist * -0.25; + break; + case LiteGraph.RIGHT: + p2[0] += dist * 0.25; + break; + case LiteGraph.UP: + p2[1] += dist * -0.25; + break; + case LiteGraph.DOWN: + p2[1] += dist * 0.25; + break; + } + + var c1 = (1 - t) * (1 - t) * (1 - t); + var c2 = 3 * ((1 - t) * (1 - t)) * t; + var c3 = 3 * (1 - t) * (t * t); + var c4 = t * t * t; + + var x = c1 * p0[0] + c2 * p1[0] + c3 * p2[0] + c4 * p3[0]; + var y = c1 * p0[1] + c2 * p1[1] + c3 * p2[1] + c4 * p3[1]; + return [x, y]; + }; + + LGraphCanvas.prototype.drawExecutionOrder = function(ctx) { + ctx.shadowColor = "transparent"; + ctx.globalAlpha = 0.25; + + ctx.textAlign = "center"; + ctx.strokeStyle = "white"; + ctx.globalAlpha = 0.75; + + var visible_nodes = this.visible_nodes; + for (var i = 0; i < visible_nodes.length; ++i) { + var node = visible_nodes[i]; + ctx.fillStyle = "black"; + ctx.fillRect( + node.pos[0] - LiteGraph.NODE_TITLE_HEIGHT, + node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, + LiteGraph.NODE_TITLE_HEIGHT, + LiteGraph.NODE_TITLE_HEIGHT + ); + if (node.order == 0) { + ctx.strokeRect( + node.pos[0] - LiteGraph.NODE_TITLE_HEIGHT + 0.5, + node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5, + LiteGraph.NODE_TITLE_HEIGHT, + LiteGraph.NODE_TITLE_HEIGHT + ); + } + ctx.fillStyle = "#FFF"; + ctx.fillText( + node.order, + node.pos[0] + LiteGraph.NODE_TITLE_HEIGHT * -0.5, + node.pos[1] - 6 + ); + } + ctx.globalAlpha = 1; + }; + + /** + * draws the widgets stored inside a node + * @method drawNodeWidgets + **/ + LGraphCanvas.prototype.drawNodeWidgets = function( + node, + posY, + ctx, + active_widget + ) { + if (!node.widgets || !node.widgets.length) { + return 0; + } + var width = node.size[0]; + var widgets = node.widgets; + posY += 2; + var H = LiteGraph.NODE_WIDGET_HEIGHT; + var show_text = this.ds.scale > 0.5; + ctx.save(); + ctx.globalAlpha = this.editor_alpha; + var outline_color = LiteGraph.WIDGET_OUTLINE_COLOR; + var background_color = LiteGraph.WIDGET_BGCOLOR; + var text_color = LiteGraph.WIDGET_TEXT_COLOR; + var secondary_text_color = LiteGraph.WIDGET_SECONDARY_TEXT_COLOR; + var margin = 15; + + for (var i = 0; i < widgets.length; ++i) { + var h = H; + var w = widgets[i]; + var y = posY; + if (w.y) { + y = w.y; + } + w.last_y = y; + ctx.strokeStyle = outline_color; + ctx.fillStyle = "#222"; + ctx.textAlign = "left"; + if(w.disabled) + ctx.globalAlpha *= 0.5; + + switch (w.type) { + case "button": + if (w.clicked) { + ctx.fillStyle = "#AAA"; + w.clicked = false; + this.dirty_canvas = true; + } + ctx.fillRect(margin, y, width - margin * 2, H); + if(show_text) + ctx.strokeRect( margin, y, width - margin * 2, H ); + if (show_text) { + ctx.textAlign = "center"; + ctx.fillStyle = text_color; + ctx.fillText(w.name, width * 0.5, y + H * 0.7); + } + break; + case "toggle": + ctx.textAlign = "left"; + ctx.strokeStyle = outline_color; + ctx.fillStyle = background_color; + ctx.beginPath(); + if (show_text) + ctx.roundRect(margin, posY, width - margin * 2, H, H * 0.5); + else + ctx.rect(margin, posY, width - margin * 2, H ); + ctx.fill(); + if(show_text) + ctx.stroke(); + ctx.fillStyle = w.value ? "#89A" : "#333"; + ctx.beginPath(); + ctx.arc( width - margin * 2, y + H * 0.5, H * 0.36, 0, Math.PI * 2 ); + ctx.fill(); + if (show_text) { + ctx.fillStyle = secondary_text_color; + if (w.name != null) { + ctx.fillText(w.name, margin * 2, y + H * 0.7); + } + ctx.fillStyle = w.value ? text_color : secondary_text_color; + ctx.textAlign = "right"; + ctx.fillText( + w.value + ? w.options.on || "true" + : w.options.off || "false", + width - 40, + y + H * 0.7 + ); + } + break; + case "slider": + ctx.fillStyle = background_color; + ctx.fillRect(margin, y, width - margin * 2, H); + var range = w.options.max - w.options.min; + var nvalue = (w.value - w.options.min) / range; + ctx.fillStyle = active_widget == w ? "#89A" : "#678"; + ctx.fillRect(margin, y, nvalue * (width - margin * 2), H); + if(show_text) + ctx.strokeRect(margin, y, width - margin * 2, H); + if (w.marker) { + var marker_nvalue = (w.marker - w.options.min) / range; + ctx.fillStyle = "#AA9"; + ctx.fillRect( margin + marker_nvalue * (width - margin * 2), y, 2, H ); + } + if (show_text) { + ctx.textAlign = "center"; + ctx.fillStyle = text_color; + ctx.fillText( + w.name + " " + Number(w.value).toFixed(3), + width * 0.5, + y + H * 0.7 + ); + } + break; + case "number": + case "combo": + ctx.textAlign = "left"; + ctx.strokeStyle = outline_color; + ctx.fillStyle = background_color; + ctx.beginPath(); + if(show_text) + ctx.roundRect(margin, posY, width - margin * 2, H, H * 0.5); + else + ctx.rect(margin, posY, width - margin * 2, H ); + ctx.fill(); + if (show_text) { + ctx.stroke(); + ctx.fillStyle = text_color; + ctx.beginPath(); + ctx.moveTo(margin + 16, posY + 5); + ctx.lineTo(margin + 6, posY + H * 0.5); + ctx.lineTo(margin + 16, posY + H - 5); + ctx.fill(); + ctx.beginPath(); + ctx.moveTo(width - margin - 16, posY + 5); + ctx.lineTo(width - margin - 6, posY + H * 0.5); + ctx.lineTo(width - margin - 16, posY + H - 5); + ctx.fill(); + ctx.fillStyle = secondary_text_color; + ctx.fillText(w.name, margin * 2 + 5, y + H * 0.7); + ctx.fillStyle = text_color; + ctx.textAlign = "right"; + if (w.type == "number") { + ctx.fillText( + Number(w.value).toFixed( + w.options.precision !== undefined + ? w.options.precision + : 3 + ), + width - margin * 2 - 20, + y + H * 0.7 + ); + } else { + var v = w.value; + if( w.options.values ) + { + var values = w.options.values; + if( values.constructor === Function ) + values = values(); + if(values && values.constructor !== Array) + v = values[ w.value ]; + } + ctx.fillText( + v, + width - margin * 2 - 20, + y + H * 0.7 + ); + } + } + break; + case "string": + case "text": + ctx.textAlign = "left"; + ctx.strokeStyle = outline_color; + ctx.fillStyle = background_color; + ctx.beginPath(); + if (show_text) + ctx.roundRect(margin, posY, width - margin * 2, H, H * 0.5); + else + ctx.rect( margin, posY, width - margin * 2, H ); + ctx.fill(); + if (show_text) { + ctx.save(); + ctx.beginPath(); + ctx.rect(margin, posY, width - margin * 2, H); + ctx.clip(); + + ctx.stroke(); + ctx.fillStyle = secondary_text_color; + if (w.name != null) { + ctx.fillText(w.name, margin * 2, y + H * 0.7); + } + ctx.fillStyle = text_color; + ctx.textAlign = "right"; + ctx.fillText(String(w.value).substr(0,30), width - margin * 2, y + H * 0.7); //30 chars max + ctx.restore(); + } + break; + default: + if (w.draw) { + h = w.draw(ctx, node, width, y, H) || H; + } + break; + } + posY += h + 4; + ctx.globalAlpha = this.editor_alpha; + + } + ctx.restore(); + ctx.textAlign = "left"; + }; + + /** + * process an event on widgets + * @method processNodeWidgets + **/ + LGraphCanvas.prototype.processNodeWidgets = function( + node, + pos, + event, + active_widget + ) { + if (!node.widgets || !node.widgets.length) { + return null; + } + + var x = pos[0] - node.pos[0]; + var y = pos[1] - node.pos[1]; + var width = node.size[0]; + var that = this; + var ref_window = this.getCanvasWindow(); + + for (var i = 0; i < node.widgets.length; ++i) { + var w = node.widgets[i]; + if(!w || w.disabled) + continue; + if ( w == active_widget || (x > 6 && x < width - 12 && y > w.last_y && y < w.last_y + LiteGraph.NODE_WIDGET_HEIGHT) ) { + //inside widget + switch (w.type) { + case "button": + if (event.type === "mousemove") { + break; + } + if (w.callback) { + setTimeout(function() { + w.callback(w, that, node, pos, event); + }, 20); + } + w.clicked = true; + this.dirty_canvas = true; + break; + case "slider": + var range = w.options.max - w.options.min; + var nvalue = Math.clamp((x - 10) / (width - 20), 0, 1); + w.value = + w.options.min + + (w.options.max - w.options.min) * nvalue; + if (w.callback) { + setTimeout(function() { + inner_value_change(w, w.value); + }, 20); + } + this.dirty_canvas = true; + break; + case "number": + case "combo": + var old_value = w.value; + if (event.type == "mousemove" && w.type == "number") { + w.value += event.deltaX * 0.1 * (w.options.step || 1); + if ( w.options.min != null && w.value < w.options.min ) { + w.value = w.options.min; + } + if ( w.options.max != null && w.value > w.options.max ) { + w.value = w.options.max; + } + } else if (event.type == "mousedown") { + var values = w.options.values; + if (values && values.constructor === Function) { + values = w.options.values(w, node); + } + var values_list = null; + + if( w.type != "number") + values_list = values.constructor === Array ? values : Object.keys(values); + + var delta = x < 40 ? -1 : x > width - 40 ? 1 : 0; + if (w.type == "number") { + w.value += delta * 0.1 * (w.options.step || 1); + if ( w.options.min != null && w.value < w.options.min ) { + w.value = w.options.min; + } + if ( w.options.max != null && w.value > w.options.max ) { + w.value = w.options.max; + } + } else if (delta) { //clicked in arrow, used for combos + var index = -1; + if(values.constructor === Object) + index = values_list.indexOf( String( w.value ) ) + delta; + else + index = values_list.indexOf( w.value ) + delta; + if (index >= values_list.length) { + index = values_list.length - 1; + } + if (index < 0) { + index = 0; + } + if( values.constructor === Array ) + w.value = values[index]; + else + w.value = index; + } else { //combo clicked + var text_values = values != values_list ? Object.values(values) : values; + var menu = new LiteGraph.ContextMenu(text_values, { + scale: Math.max(1, this.ds.scale), + event: event, + className: "dark", + callback: inner_clicked.bind(w) + }, + ref_window); + function inner_clicked(v, option, event) { + if(values != values_list) + v = text_values.indexOf(v); + this.value = v; + inner_value_change(this, v); + that.dirty_canvas = true; + return false; + } + } + } //end mousedown + else if(event.type == "mouseup" && w.type == "number") + { + var delta = x < 40 ? -1 : x > width - 40 ? 1 : 0; + if (event.click_time < 200 && delta == 0) { + this.prompt("Value",w.value,function(v) { + this.value = Number(v); + inner_value_change(this, this.value); + }.bind(w), + event); + } + } + + if( old_value != w.value ) + setTimeout( + function() { + inner_value_change(this, this.value); + }.bind(w), + 20 + ); + this.dirty_canvas = true; + break; + case "toggle": + if (event.type == "mousedown") { + w.value = !w.value; + setTimeout(function() { + inner_value_change(w, w.value); + }, 20); + } + break; + case "string": + case "text": + if (event.type == "mousedown") { + this.prompt("Value",w.value,function(v) { + this.value = v; + inner_value_change(this, v); + }.bind(w), + event); + } + break; + default: + if (w.mouse) { + w.mouse(ctx, event, [x, y], node); + } + break; + } //end switch + + return w; + } + } + + function inner_value_change(widget, value) { + widget.value = value; + if ( widget.options && widget.options.property && node.properties[widget.options.property] !== undefined ) { + node.setProperty( widget.options.property, value ); + } + if (widget.callback) { + widget.callback(widget.value, that, node, pos, event); + } + } + + return null; + }; + + /** + * draws every group area in the background + * @method drawGroups + **/ + LGraphCanvas.prototype.drawGroups = function(canvas, ctx) { + if (!this.graph) { + return; + } + + var groups = this.graph._groups; + + ctx.save(); + ctx.globalAlpha = 0.5 * this.editor_alpha; + + for (var i = 0; i < groups.length; ++i) { + var group = groups[i]; + + if (!overlapBounding(this.visible_area, group._bounding)) { + continue; + } //out of the visible area + + ctx.fillStyle = group.color || "#335"; + ctx.strokeStyle = group.color || "#335"; + var pos = group._pos; + var size = group._size; + ctx.globalAlpha = 0.25 * this.editor_alpha; + ctx.beginPath(); + ctx.rect(pos[0] + 0.5, pos[1] + 0.5, size[0], size[1]); + ctx.fill(); + ctx.globalAlpha = this.editor_alpha; + ctx.stroke(); + + ctx.beginPath(); + ctx.moveTo(pos[0] + size[0], pos[1] + size[1]); + ctx.lineTo(pos[0] + size[0] - 10, pos[1] + size[1]); + ctx.lineTo(pos[0] + size[0], pos[1] + size[1] - 10); + ctx.fill(); + + var font_size = + group.font_size || LiteGraph.DEFAULT_GROUP_FONT_SIZE; + ctx.font = font_size + "px Arial"; + ctx.fillText(group.title, pos[0] + 4, pos[1] + font_size); + } + + ctx.restore(); + }; + + LGraphCanvas.prototype.adjustNodesSize = function() { + var nodes = this.graph._nodes; + for (var i = 0; i < nodes.length; ++i) { + nodes[i].size = nodes[i].computeSize(); + } + this.setDirty(true, true); + }; + + /** + * resizes the canvas to a given size, if no size is passed, then it tries to fill the parentNode + * @method resize + **/ + LGraphCanvas.prototype.resize = function(width, height) { + if (!width && !height) { + var parent = this.canvas.parentNode; + width = parent.offsetWidth; + height = parent.offsetHeight; + } + + if (this.canvas.width == width && this.canvas.height == height) { + return; + } + + this.canvas.width = width; + this.canvas.height = height; + this.bgcanvas.width = this.canvas.width; + this.bgcanvas.height = this.canvas.height; + this.setDirty(true, true); + }; + + /** + * switches to live mode (node shapes are not rendered, only the content) + * this feature was designed when graphs where meant to create user interfaces + * @method switchLiveMode + **/ + LGraphCanvas.prototype.switchLiveMode = function(transition) { + if (!transition) { + this.live_mode = !this.live_mode; + this.dirty_canvas = true; + this.dirty_bgcanvas = true; + return; + } + + var self = this; + var delta = this.live_mode ? 1.1 : 0.9; + if (this.live_mode) { + this.live_mode = false; + this.editor_alpha = 0.1; + } + + var t = setInterval(function() { + self.editor_alpha *= delta; + self.dirty_canvas = true; + self.dirty_bgcanvas = true; + + if (delta < 1 && self.editor_alpha < 0.01) { + clearInterval(t); + if (delta < 1) { + self.live_mode = true; + } + } + if (delta > 1 && self.editor_alpha > 0.99) { + clearInterval(t); + self.editor_alpha = 1; + } + }, 1); + }; + + LGraphCanvas.prototype.onNodeSelectionChange = function(node) { + return; //disabled + }; + + LGraphCanvas.prototype.touchHandler = function(event) { + //alert("foo"); + var touches = event.changedTouches, + first = touches[0], + type = ""; + + switch (event.type) { + case "touchstart": + type = "mousedown"; + break; + case "touchmove": + type = "mousemove"; + break; + case "touchend": + type = "mouseup"; + break; + default: + return; + } + + //initMouseEvent(type, canBubble, cancelable, view, clickCount, + // screenX, screenY, clientX, clientY, ctrlKey, + // altKey, shiftKey, metaKey, button, relatedTarget); + + var window = this.getCanvasWindow(); + var document = window.document; + + var simulatedEvent = document.createEvent("MouseEvent"); + simulatedEvent.initMouseEvent( + type, + true, + true, + window, + 1, + first.screenX, + first.screenY, + first.clientX, + first.clientY, + false, + false, + false, + false, + 0 /*left*/, + null + ); + first.target.dispatchEvent(simulatedEvent); + event.preventDefault(); + }; + + /* CONTEXT MENU ********************/ + + LGraphCanvas.onGroupAdd = function(info, entry, mouse_event) { + var canvas = LGraphCanvas.active_canvas; + var ref_window = canvas.getCanvasWindow(); + + var group = new LiteGraph.LGraphGroup(); + group.pos = canvas.convertEventToCanvasOffset(mouse_event); + canvas.graph.add(group); + }; + + LGraphCanvas.onMenuAdd = function(node, options, e, prev_menu, callback) { + var canvas = LGraphCanvas.active_canvas; + var ref_window = canvas.getCanvasWindow(); + + var values = LiteGraph.getNodeTypesCategories( canvas.filter ); + var entries = []; + for (var i in values) { + if (values[i]) { + entries.push({ value: values[i], content: values[i], has_submenu: true }); + } + } + + //show categories + var menu = new LiteGraph.ContextMenu( entries, { event: e, callback: inner_clicked, parentMenu: prev_menu }, ref_window ); + + function inner_clicked(v, option, e) { + var category = v.value; + var node_types = LiteGraph.getNodeTypesInCategory( category, canvas.filter ); + var values = []; + for (var i in node_types) { + if (!node_types[i].skip_list) { + values.push({ + content: node_types[i].title, + value: node_types[i].type + }); + } + } + + new LiteGraph.ContextMenu( values, { event: e, callback: inner_create, parentMenu: menu }, ref_window ); + return false; + } + + function inner_create(v, e) { + var first_event = prev_menu.getFirstEvent(); + var node = LiteGraph.createNode(v.value); + if (node) { + node.pos = canvas.convertEventToCanvasOffset(first_event); + canvas.graph.add(node); + } + if(callback) + callback(node); + } + + return false; + }; + + LGraphCanvas.onMenuCollapseAll = function() {}; + + LGraphCanvas.onMenuNodeEdit = function() {}; + + LGraphCanvas.showMenuNodeOptionalInputs = function( + v, + options, + e, + prev_menu, + node + ) { + if (!node) { + return; + } + + var that = this; + var canvas = LGraphCanvas.active_canvas; + var ref_window = canvas.getCanvasWindow(); + + var options = node.optional_inputs; + if (node.onGetInputs) { + options = node.onGetInputs(); + } + + var entries = []; + if (options) { + for (var i in options) { + var entry = options[i]; + if (!entry) { + entries.push(null); + continue; + } + var label = entry[0]; + if (entry[2] && entry[2].label) { + label = entry[2].label; + } + var data = { content: label, value: entry }; + if (entry[1] == LiteGraph.ACTION) { + data.className = "event"; + } + entries.push(data); + } + } + + if (this.onMenuNodeInputs) { + entries = this.onMenuNodeInputs(entries); + } + + if (!entries.length) { + return; + } + + var menu = new LiteGraph.ContextMenu( + entries, + { + event: e, + callback: inner_clicked, + parentMenu: prev_menu, + node: node + }, + ref_window + ); + + function inner_clicked(v, e, prev) { + if (!node) { + return; + } + + if (v.callback) { + v.callback.call(that, node, v, e, prev); + } + + if (v.value) { + node.addInput(v.value[0], v.value[1], v.value[2]); + node.setDirtyCanvas(true, true); + } + } + + return false; + }; + + LGraphCanvas.showMenuNodeOptionalOutputs = function( + v, + options, + e, + prev_menu, + node + ) { + if (!node) { + return; + } + + var that = this; + var canvas = LGraphCanvas.active_canvas; + var ref_window = canvas.getCanvasWindow(); + + var options = node.optional_outputs; + if (node.onGetOutputs) { + options = node.onGetOutputs(); + } + + var entries = []; + if (options) { + for (var i in options) { + var entry = options[i]; + if (!entry) { + //separator? + entries.push(null); + continue; + } + + if ( + node.flags && + node.flags.skip_repeated_outputs && + node.findOutputSlot(entry[0]) != -1 + ) { + continue; + } //skip the ones already on + var label = entry[0]; + if (entry[2] && entry[2].label) { + label = entry[2].label; + } + var data = { content: label, value: entry }; + if (entry[1] == LiteGraph.EVENT) { + data.className = "event"; + } + entries.push(data); + } + } + + if (this.onMenuNodeOutputs) { + entries = this.onMenuNodeOutputs(entries); + } + + if (!entries.length) { + return; + } + + var menu = new LiteGraph.ContextMenu( + entries, + { + event: e, + callback: inner_clicked, + parentMenu: prev_menu, + node: node + }, + ref_window + ); + + function inner_clicked(v, e, prev) { + if (!node) { + return; + } + + if (v.callback) { + v.callback.call(that, node, v, e, prev); + } + + if (!v.value) { + return; + } + + var value = v.value[1]; + + if ( + value && + (value.constructor === Object || value.constructor === Array) + ) { + //submenu why? + var entries = []; + for (var i in value) { + entries.push({ content: i, value: value[i] }); + } + new LiteGraph.ContextMenu(entries, { + event: e, + callback: inner_clicked, + parentMenu: prev_menu, + node: node + }); + return false; + } else { + node.addOutput(v.value[0], v.value[1], v.value[2]); + node.setDirtyCanvas(true, true); + } + } + + return false; + }; + + LGraphCanvas.onShowMenuNodeProperties = function( + value, + options, + e, + prev_menu, + node + ) { + if (!node || !node.properties) { + return; + } + + var that = this; + var canvas = LGraphCanvas.active_canvas; + var ref_window = canvas.getCanvasWindow(); + + var entries = []; + for (var i in node.properties) { + var value = node.properties[i] !== undefined ? node.properties[i] : " "; + if( typeof value == "object" ) + value = JSON.stringify(value); + //value could contain invalid html characters, clean that + value = LGraphCanvas.decodeHTML(value); + entries.push({ + content: + "" + + i + + "" + + "" + + value + + "", + value: i + }); + } + if (!entries.length) { + return; + } + + var menu = new LiteGraph.ContextMenu( + entries, + { + event: e, + callback: inner_clicked, + parentMenu: prev_menu, + allow_html: true, + node: node + }, + ref_window + ); + + function inner_clicked(v, options, e, prev) { + if (!node) { + return; + } + var rect = this.getBoundingClientRect(); + canvas.showEditPropertyValue(node, v.value, { + position: [rect.left, rect.top] + }); + } + + return false; + }; + + LGraphCanvas.decodeHTML = function(str) { + var e = document.createElement("div"); + e.innerText = str; + return e.innerHTML; + }; + + LGraphCanvas.onResizeNode = function(value, options, e, menu, node) { + if (!node) { + return; + } + node.size = node.computeSize(); + node.setDirtyCanvas(true, true); + }; + + LGraphCanvas.prototype.showLinkMenu = function(link, e) { + var that = this; + console.log(link); + var options = ["Add Node",null,"Delete"]; + var menu = new LiteGraph.ContextMenu(options, { + event: e, + title: link.data != null ? link.data.constructor.name : null, + callback: inner_clicked + }); + + function inner_clicked(v,options,e) { + switch (v) { + case "Add Node": + LGraphCanvas.onMenuAdd(null, null, e, menu, function(node){ + console.log("node autoconnect"); + var node_left = that.graph.getNodeById( link.origin_id ); + var node_right = that.graph.getNodeById( link.target_id ); + if(!node.inputs || !node.inputs.length || !node.outputs || !node.outputs.length) + return; + if( node_left.outputs[ link.origin_slot ].type == node.inputs[0].type && node.outputs[0].type == node_right.inputs[0].type ) + { + node_left.connect( link.origin_slot, node, 0 ); + node.connect( 0, node_right, link.target_slot ); + node.pos[0] -= node.size[0] * 0.5; + } + }); + break; + case "Delete": + that.graph.removeLink(link.id); + break; + default: + } + } + + return false; + }; + + LGraphCanvas.onShowPropertyEditor = function(item, options, e, menu, node) { + var input_html = ""; + var property = item.property || "title"; + var value = node[property]; + + var dialog = document.createElement("div"); + dialog.className = "graphdialog"; + dialog.innerHTML = + ""; + var title = dialog.querySelector(".name"); + title.innerText = property; + var input = dialog.querySelector("input"); + if (input) { + input.value = value; + input.addEventListener("blur", function(e) { + this.focus(); + }); + input.addEventListener("keydown", function(e) { + if (e.keyCode != 13) { + return; + } + inner(); + e.preventDefault(); + e.stopPropagation(); + }); + } + + var graphcanvas = LGraphCanvas.active_canvas; + var canvas = graphcanvas.canvas; + + var rect = canvas.getBoundingClientRect(); + var offsetx = -20; + var offsety = -20; + if (rect) { + offsetx -= rect.left; + offsety -= rect.top; + } + + if (event) { + dialog.style.left = event.clientX + offsetx + "px"; + dialog.style.top = event.clientY + offsety + "px"; + } else { + dialog.style.left = canvas.width * 0.5 + offsetx + "px"; + dialog.style.top = canvas.height * 0.5 + offsety + "px"; + } + + var button = dialog.querySelector("button"); + button.addEventListener("click", inner); + canvas.parentNode.appendChild(dialog); + + function inner() { + setValue(input.value); + } + + function setValue(value) { + if (item.type == "Number") { + value = Number(value); + } else if (item.type == "Boolean") { + value = Boolean(value); + } + node[property] = value; + if (dialog.parentNode) { + dialog.parentNode.removeChild(dialog); + } + node.setDirtyCanvas(true, true); + } + }; + + LGraphCanvas.prototype.prompt = function(title, value, callback, event) { + var that = this; + var input_html = ""; + title = title || ""; + + var modified = false; + + var dialog = document.createElement("div"); + dialog.className = "graphdialog rounded"; + dialog.innerHTML = + " "; + dialog.close = function() { + that.prompt_box = null; + if (dialog.parentNode) { + dialog.parentNode.removeChild(dialog); + } + }; + + if (this.ds.scale > 1) { + dialog.style.transform = "scale(" + this.ds.scale + ")"; + } + + dialog.addEventListener("mouseleave", function(e) { + if (!modified) { + dialog.close(); + } + }); + + if (that.prompt_box) { + that.prompt_box.close(); + } + that.prompt_box = dialog; + + var first = null; + var timeout = null; + var selected = null; + + var name_element = dialog.querySelector(".name"); + name_element.innerText = title; + var value_element = dialog.querySelector(".value"); + value_element.value = value; + + var input = dialog.querySelector("input"); + input.addEventListener("keydown", function(e) { + modified = true; + if (e.keyCode == 27) { + //ESC + dialog.close(); + } else if (e.keyCode == 13) { + if (callback) { + callback(this.value); + } + dialog.close(); + } else { + return; + } + e.preventDefault(); + e.stopPropagation(); + }); + + var button = dialog.querySelector("button"); + button.addEventListener("click", function(e) { + if (callback) { + callback(input.value); + } + that.setDirty(true); + dialog.close(); + }); + + var graphcanvas = LGraphCanvas.active_canvas; + var canvas = graphcanvas.canvas; + + var rect = canvas.getBoundingClientRect(); + var offsetx = -20; + var offsety = -20; + if (rect) { + offsetx -= rect.left; + offsety -= rect.top; + } + + if (event) { + dialog.style.left = event.clientX + offsetx + "px"; + dialog.style.top = event.clientY + offsety + "px"; + } else { + dialog.style.left = canvas.width * 0.5 + offsetx + "px"; + dialog.style.top = canvas.height * 0.5 + offsety + "px"; + } + + canvas.parentNode.appendChild(dialog); + setTimeout(function() { + input.focus(); + }, 10); + + return dialog; + }; + + LGraphCanvas.search_limit = -1; + LGraphCanvas.prototype.showSearchBox = function(event) { + var that = this; + var input_html = ""; + var graphcanvas = LGraphCanvas.active_canvas; + var canvas = graphcanvas.canvas; + var root_document = canvas.ownerDocument || document; + + var dialog = document.createElement("div"); + dialog.className = "litegraph litesearchbox graphdialog rounded"; + dialog.innerHTML = + "Search
"; + dialog.close = function() { + that.search_box = null; + root_document.body.focus(); + root_document.body.style.overflow = ""; + + setTimeout(function() { + that.canvas.focus(); + }, 20); //important, if canvas loses focus keys wont be captured + if (dialog.parentNode) { + dialog.parentNode.removeChild(dialog); + } + }; + + var timeout_close = null; + + if (this.ds.scale > 1) { + dialog.style.transform = "scale(" + this.ds.scale + ")"; + } + + dialog.addEventListener("mouseenter", function(e) { + if (timeout_close) { + clearTimeout(timeout_close); + timeout_close = null; + } + }); + + dialog.addEventListener("mouseleave", function(e) { + //dialog.close(); + timeout_close = setTimeout(function() { + dialog.close(); + }, 500); + }); + + if (that.search_box) { + that.search_box.close(); + } + that.search_box = dialog; + + var helper = dialog.querySelector(".helper"); + + var first = null; + var timeout = null; + var selected = null; + + var input = dialog.querySelector("input"); + if (input) { + input.addEventListener("blur", function(e) { + this.focus(); + }); + input.addEventListener("keydown", function(e) { + if (e.keyCode == 38) { + //UP + changeSelection(false); + } else if (e.keyCode == 40) { + //DOWN + changeSelection(true); + } else if (e.keyCode == 27) { + //ESC + dialog.close(); + } else if (e.keyCode == 13) { + if (selected) { + select(selected.innerHTML); + } else if (first) { + select(first); + } else { + dialog.close(); + } + } else { + if (timeout) { + clearInterval(timeout); + } + timeout = setTimeout(refreshHelper, 10); + return; + } + e.preventDefault(); + e.stopPropagation(); + e.stopImmediatePropagation(); + return true; + }); + } + + if( root_document.fullscreenElement ) + root_document.fullscreenElement.appendChild(dialog); + else + { + root_document.body.appendChild(dialog); + root_document.body.style.overflow = "hidden"; + } + + //compute best position + var rect = canvas.getBoundingClientRect(); + + var left = ( event ? event.clientX : (rect.left + rect.width * 0.5) ) - 80; + var top = ( event ? event.clientY : (rect.top + rect.height * 0.5) ) - 20; + dialog.style.left = left + "px"; + dialog.style.top = top + "px"; + + //To avoid out of screen problems + if(event.layerY > (rect.height - 200)) + helper.style.maxHeight = (rect.height - event.layerY - 20) + "px"; + + /* + var offsetx = -20; + var offsety = -20; + if (rect) { + offsetx -= rect.left; + offsety -= rect.top; + } + + if (event) { + dialog.style.left = event.clientX + offsetx + "px"; + dialog.style.top = event.clientY + offsety + "px"; + } else { + dialog.style.left = canvas.width * 0.5 + offsetx + "px"; + dialog.style.top = canvas.height * 0.5 + offsety + "px"; + } + canvas.parentNode.appendChild(dialog); + */ + + input.focus(); + + function select(name) { + if (name) { + if (that.onSearchBoxSelection) { + that.onSearchBoxSelection(name, event, graphcanvas); + } else { + var extra = LiteGraph.searchbox_extras[name.toLowerCase()]; + if (extra) { + name = extra.type; + } + + var node = LiteGraph.createNode(name); + if (node) { + node.pos = graphcanvas.convertEventToCanvasOffset( + event + ); + graphcanvas.graph.add(node); + } + + if (extra && extra.data) { + if (extra.data.properties) { + for (var i in extra.data.properties) { + node.addProperty( i, extra.data.properties[i] ); + } + } + if (extra.data.inputs) { + node.inputs = []; + for (var i in extra.data.inputs) { + node.addOutput( + extra.data.inputs[i][0], + extra.data.inputs[i][1] + ); + } + } + if (extra.data.outputs) { + node.outputs = []; + for (var i in extra.data.outputs) { + node.addOutput( + extra.data.outputs[i][0], + extra.data.outputs[i][1] + ); + } + } + if (extra.data.title) { + node.title = extra.data.title; + } + if (extra.data.json) { + node.configure(extra.data.json); + } + } + } + } + + dialog.close(); + } + + function changeSelection(forward) { + var prev = selected; + if (selected) { + selected.classList.remove("selected"); + } + if (!selected) { + selected = forward + ? helper.childNodes[0] + : helper.childNodes[helper.childNodes.length]; + } else { + selected = forward + ? selected.nextSibling + : selected.previousSibling; + if (!selected) { + selected = prev; + } + } + if (!selected) { + return; + } + selected.classList.add("selected"); + selected.scrollIntoView({block: "end", behavior: "smooth"}); + } + + function refreshHelper() { + timeout = null; + var str = input.value; + first = null; + helper.innerHTML = ""; + if (!str) { + return; + } + + if (that.onSearchBox) { + var list = that.onSearchBox(helper, str, graphcanvas); + if (list) { + for (var i = 0; i < list.length; ++i) { + addResult(list[i]); + } + } + } else { + var c = 0; + str = str.toLowerCase(); + var filter = graphcanvas.filter || graphcanvas.graph.filter; + + //extras + for (var i in LiteGraph.searchbox_extras) { + var extra = LiteGraph.searchbox_extras[i]; + if (extra.desc.toLowerCase().indexOf(str) === -1) { + continue; + } + var ctor = LiteGraph.registered_node_types[ extra.type ]; + if( ctor && ctor.filter && ctor.filter != filter ) + continue; + addResult( extra.desc, "searchbox_extra" ); + if ( LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit ) { + break; + } + } + + var filtered = null; + if (Array.prototype.filter) { //filter supported + var keys = Object.keys( LiteGraph.registered_node_types ); //types + var filtered = keys.filter( inner_test_filter ); + } else { + filtered = []; + for (var i in LiteGraph.registered_node_types) { + if( inner_test_filter(i) ) + filtered.push(i); + } + } + + for (var i = 0; i < filtered.length; i++) { + addResult(filtered[i]); + if ( LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit ) { + break; + } + } + + function inner_test_filter( type ) + { + var ctor = LiteGraph.registered_node_types[ type ]; + if(filter && ctor.filter != filter ) + return false; + return type.toLowerCase().indexOf(str) !== -1; + } + } + + function addResult(type, className) { + var help = document.createElement("div"); + if (!first) { + first = type; + } + help.innerText = type; + help.dataset["type"] = escape(type); + help.className = "litegraph lite-search-item"; + if (className) { + help.className += " " + className; + } + help.addEventListener("click", function(e) { + select(unescape(this.dataset["type"])); + }); + helper.appendChild(help); + } + } + + return dialog; + }; + + LGraphCanvas.prototype.showEditPropertyValue = function( node, property, options ) { + if (!node || node.properties[property] === undefined) { + return; + } + + options = options || {}; + var that = this; + + var info = node.getPropertyInfo(property); + var type = info.type; + + var input_html = ""; + + if (type == "string" || type == "number" || type == "array" || type == "object") { + input_html = ""; + } else if (type == "enum" && info.values) { + input_html = ""; + } else if (type == "boolean") { + input_html = + ""; + } else { + console.warn("unknown type: " + type); + return; + } + + var dialog = this.createDialog( + "" + + property + + "" + + input_html + + "", + options + ); + + if (type == "enum" && info.values) { + var input = dialog.querySelector("select"); + input.addEventListener("change", function(e) { + setValue(e.target.value); + //var index = e.target.value; + //setValue( e.options[e.selectedIndex].value ); + }); + } else if (type == "boolean") { + var input = dialog.querySelector("input"); + if (input) { + input.addEventListener("click", function(e) { + setValue(!!input.checked); + }); + } + } else { + var input = dialog.querySelector("input"); + if (input) { + input.addEventListener("blur", function(e) { + this.focus(); + }); + var v = node.properties[property] !== undefined ? node.properties[property] : ""; + v = JSON.stringify(v); + input.value = v; + input.addEventListener("keydown", function(e) { + if (e.keyCode != 13) { + return; + } + inner(); + e.preventDefault(); + e.stopPropagation(); + }); + } + } + + var button = dialog.querySelector("button"); + button.addEventListener("click", inner); + + function inner() { + setValue(input.value); + } + + function setValue(value) { + if (typeof node.properties[property] == "number") { + value = Number(value); + } + if (type == "array" || type == "object") { + value = JSON.parse(value); + } + node.properties[property] = value; + if (node._graph) { + node._graph._version++; + } + if (node.onPropertyChanged) { + node.onPropertyChanged(property, value); + } + if(options.onclose) + options.onclose(); + dialog.close(); + node.setDirtyCanvas(true, true); + } + + return dialog; + }; + + LGraphCanvas.prototype.createDialog = function(html, options) { + options = options || {}; + + var dialog = document.createElement("div"); + dialog.className = "graphdialog"; + dialog.innerHTML = html; + + var rect = this.canvas.getBoundingClientRect(); + var offsetx = -20; + var offsety = -20; + if (rect) { + offsetx -= rect.left; + offsety -= rect.top; + } + + if (options.position) { + offsetx += options.position[0]; + offsety += options.position[1]; + } else if (options.event) { + offsetx += options.event.clientX; + offsety += options.event.clientY; + } //centered + else { + offsetx += this.canvas.width * 0.5; + offsety += this.canvas.height * 0.5; + } + + dialog.style.left = offsetx + "px"; + dialog.style.top = offsety + "px"; + + this.canvas.parentNode.appendChild(dialog); + + dialog.close = function() { + if (this.parentNode) { + this.parentNode.removeChild(this); + } + }; + + return dialog; + }; + + LGraphCanvas.onMenuNodeCollapse = function(value, options, e, menu, node) { + node.collapse(); + }; + + LGraphCanvas.onMenuNodePin = function(value, options, e, menu, node) { + node.pin(); + }; + + LGraphCanvas.onMenuNodeMode = function(value, options, e, menu, node) { + new LiteGraph.ContextMenu( + ["Always", "On Event", "On Trigger", "Never"], + { event: e, callback: inner_clicked, parentMenu: menu, node: node } + ); + + function inner_clicked(v) { + if (!node) { + return; + } + switch (v) { + case "On Event": + node.mode = LiteGraph.ON_EVENT; + break; + case "On Trigger": + node.mode = LiteGraph.ON_TRIGGER; + break; + case "Never": + node.mode = LiteGraph.NEVER; + break; + case "Always": + default: + node.mode = LiteGraph.ALWAYS; + break; + } + } + + return false; + }; + + LGraphCanvas.onMenuNodeColors = function(value, options, e, menu, node) { + if (!node) { + throw "no node for color"; + } + + var values = []; + values.push({ + value: null, + content: + "No color" + }); + + for (var i in LGraphCanvas.node_colors) { + var color = LGraphCanvas.node_colors[i]; + var value = { + value: i, + content: + "" + + i + + "" + }; + values.push(value); + } + new LiteGraph.ContextMenu(values, { + event: e, + callback: inner_clicked, + parentMenu: menu, + node: node + }); + + function inner_clicked(v) { + if (!node) { + return; + } + + var color = v.value ? LGraphCanvas.node_colors[v.value] : null; + if (color) { + if (node.constructor === LiteGraph.LGraphGroup) { + node.color = color.groupcolor; + } else { + node.color = color.color; + node.bgcolor = color.bgcolor; + } + } else { + delete node.color; + delete node.bgcolor; + } + node.setDirtyCanvas(true, true); + } + + return false; + }; + + LGraphCanvas.onMenuNodeShapes = function(value, options, e, menu, node) { + if (!node) { + throw "no node passed"; + } + + new LiteGraph.ContextMenu(LiteGraph.VALID_SHAPES, { + event: e, + callback: inner_clicked, + parentMenu: menu, + node: node + }); + + function inner_clicked(v) { + if (!node) { + return; + } + node.shape = v; + node.setDirtyCanvas(true); + } + + return false; + }; + + LGraphCanvas.onMenuNodeRemove = function(value, options, e, menu, node) { + if (!node) { + throw "no node passed"; + } + + if (node.removable === false) { + return; + } + + node.graph.remove(node); + node.setDirtyCanvas(true, true); + }; + + LGraphCanvas.onMenuNodeClone = function(value, options, e, menu, node) { + if (node.clonable == false) { + return; + } + var newnode = node.clone(); + if (!newnode) { + return; + } + newnode.pos = [node.pos[0] + 5, node.pos[1] + 5]; + node.graph.add(newnode); + node.setDirtyCanvas(true, true); + }; + + LGraphCanvas.node_colors = { + red: { color: "#322", bgcolor: "#533", groupcolor: "#A88" }, + brown: { color: "#332922", bgcolor: "#593930", groupcolor: "#b06634" }, + green: { color: "#232", bgcolor: "#353", groupcolor: "#8A8" }, + blue: { color: "#223", bgcolor: "#335", groupcolor: "#88A" }, + pale_blue: { + color: "#2a363b", + bgcolor: "#3f5159", + groupcolor: "#3f789e" + }, + cyan: { color: "#233", bgcolor: "#355", groupcolor: "#8AA" }, + purple: { color: "#323", bgcolor: "#535", groupcolor: "#a1309b" }, + yellow: { color: "#432", bgcolor: "#653", groupcolor: "#b58b2a" }, + black: { color: "#222", bgcolor: "#000", groupcolor: "#444" } + }; + + LGraphCanvas.prototype.getCanvasMenuOptions = function() { + var options = null; + if (this.getMenuOptions) { + options = this.getMenuOptions(); + } else { + options = [ + { + content: "Add Node", + has_submenu: true, + callback: LGraphCanvas.onMenuAdd + }, + { content: "Add Group", callback: LGraphCanvas.onGroupAdd } + //{content:"Collapse All", callback: LGraphCanvas.onMenuCollapseAll } + ]; + + if (this._graph_stack && this._graph_stack.length > 0) { + options.push(null, { + content: "Close subgraph", + callback: this.closeSubgraph.bind(this) + }); + } + } + + if (this.getExtraMenuOptions) { + var extra = this.getExtraMenuOptions(this, options); + if (extra) { + options = options.concat(extra); + } + } + + return options; + }; + + //called by processContextMenu to extract the menu list + LGraphCanvas.prototype.getNodeMenuOptions = function(node) { + var options = null; + + if (node.getMenuOptions) { + options = node.getMenuOptions(this); + } else { + options = [ + { + content: "Inputs", + has_submenu: true, + disabled: true, + callback: LGraphCanvas.showMenuNodeOptionalInputs + }, + { + content: "Outputs", + has_submenu: true, + disabled: true, + callback: LGraphCanvas.showMenuNodeOptionalOutputs + }, + null, + { + content: "Properties", + has_submenu: true, + callback: LGraphCanvas.onShowMenuNodeProperties + }, + null, + { + content: "Title", + callback: LGraphCanvas.onShowPropertyEditor + }, + { + content: "Mode", + has_submenu: true, + callback: LGraphCanvas.onMenuNodeMode + }, + { content: "Resize", callback: LGraphCanvas.onResizeNode }, + { + content: "Collapse", + callback: LGraphCanvas.onMenuNodeCollapse + }, + { content: "Pin", callback: LGraphCanvas.onMenuNodePin }, + { + content: "Colors", + has_submenu: true, + callback: LGraphCanvas.onMenuNodeColors + }, + { + content: "Shapes", + has_submenu: true, + callback: LGraphCanvas.onMenuNodeShapes + }, + null + ]; + } + + if (node.onGetInputs) { + var inputs = node.onGetInputs(); + if (inputs && inputs.length) { + options[0].disabled = false; + } + } + + if (node.onGetOutputs) { + var outputs = node.onGetOutputs(); + if (outputs && outputs.length) { + options[1].disabled = false; + } + } + + if (node.getExtraMenuOptions) { + var extra = node.getExtraMenuOptions(this); + if (extra) { + extra.push(null); + options = extra.concat(options); + } + } + + if (node.clonable !== false) { + options.push({ + content: "Clone", + callback: LGraphCanvas.onMenuNodeClone + }); + } + if (node.removable !== false) { + options.push(null, { + content: "Remove", + callback: LGraphCanvas.onMenuNodeRemove + }); + } + + if (node.graph && node.graph.onGetNodeMenuOptions) { + node.graph.onGetNodeMenuOptions(options, node); + } + + return options; + }; + + LGraphCanvas.prototype.getGroupMenuOptions = function(node) { + var o = [ + { content: "Title", callback: LGraphCanvas.onShowPropertyEditor }, + { + content: "Color", + has_submenu: true, + callback: LGraphCanvas.onMenuNodeColors + }, + { + content: "Font size", + property: "font_size", + type: "Number", + callback: LGraphCanvas.onShowPropertyEditor + }, + null, + { content: "Remove", callback: LGraphCanvas.onMenuNodeRemove } + ]; + + return o; + }; + + LGraphCanvas.prototype.processContextMenu = function(node, event) { + var that = this; + var canvas = LGraphCanvas.active_canvas; + var ref_window = canvas.getCanvasWindow(); + + var menu_info = null; + var options = { + event: event, + callback: inner_option_clicked, + extra: node + }; + + if(node) + options.title = node.type; + + //check if mouse is in input + var slot = null; + if (node) { + slot = node.getSlotInPosition(event.canvasX, event.canvasY); + LGraphCanvas.active_node = node; + } + + if (slot) { + //on slot + menu_info = []; + if ( + slot && + slot.output && + slot.output.links && + slot.output.links.length + ) { + menu_info.push({ content: "Disconnect Links", slot: slot }); + } + var _slot = slot.input || slot.output; + menu_info.push( + _slot.locked + ? "Cannot remove" + : { content: "Remove Slot", slot: slot } + ); + menu_info.push( + _slot.nameLocked + ? "Cannot rename" + : { content: "Rename Slot", slot: slot } + ); + options.title = + (slot.input ? slot.input.type : slot.output.type) || "*"; + if (slot.input && slot.input.type == LiteGraph.ACTION) { + options.title = "Action"; + } + if (slot.output && slot.output.type == LiteGraph.EVENT) { + options.title = "Event"; + } + } else { + if (node) { + //on node + menu_info = this.getNodeMenuOptions(node); + } else { + menu_info = this.getCanvasMenuOptions(); + var group = this.graph.getGroupOnPos( + event.canvasX, + event.canvasY + ); + if (group) { + //on group + menu_info.push(null, { + content: "Edit Group", + has_submenu: true, + submenu: { + title: "Group", + extra: group, + options: this.getGroupMenuOptions(group) + } + }); + } + } + } + + //show menu + if (!menu_info) { + return; + } + + var menu = new LiteGraph.ContextMenu(menu_info, options, ref_window); + + function inner_option_clicked(v, options, e) { + if (!v) { + return; + } + + if (v.content == "Remove Slot") { + var info = v.slot; + if (info.input) { + node.removeInput(info.slot); + } else if (info.output) { + node.removeOutput(info.slot); + } + return; + } else if (v.content == "Disconnect Links") { + var info = v.slot; + if (info.output) { + node.disconnectOutput(info.slot); + } else if (info.input) { + node.disconnectInput(info.slot); + } + return; + } else if (v.content == "Rename Slot") { + var info = v.slot; + var slot_info = info.input + ? node.getInputInfo(info.slot) + : node.getOutputInfo(info.slot); + var dialog = that.createDialog( + "Name", + options + ); + var input = dialog.querySelector("input"); + if (input && slot_info) { + input.value = slot_info.label || ""; + } + dialog + .querySelector("button") + .addEventListener("click", function(e) { + if (input.value) { + if (slot_info) { + slot_info.label = input.value; + } + that.setDirty(true); + } + dialog.close(); + }); + } + + //if(v.callback) + // return v.callback.call(that, node, options, e, menu, that, event ); + } + }; + + //API ************************************************* + //like rect but rounded corners + if (typeof(window) != "undefined" && window.CanvasRenderingContext2D) { + window.CanvasRenderingContext2D.prototype.roundRect = function( + x, + y, + width, + height, + radius, + radius_low + ) { + if (radius === undefined) { + radius = 5; + } + + if (radius_low === undefined) { + radius_low = radius; + } + + this.moveTo(x + radius, y); + this.lineTo(x + width - radius, y); + this.quadraticCurveTo(x + width, y, x + width, y + radius); + + this.lineTo(x + width, y + height - radius_low); + this.quadraticCurveTo( + x + width, + y + height, + x + width - radius_low, + y + height + ); + this.lineTo(x + radius_low, y + height); + this.quadraticCurveTo(x, y + height, x, y + height - radius_low); + this.lineTo(x, y + radius); + this.quadraticCurveTo(x, y, x + radius, y); + }; + } + + function compareObjects(a, b) { + for (var i in a) { + if (a[i] != b[i]) { + return false; + } + } + return true; + } + LiteGraph.compareObjects = compareObjects; + + function distance(a, b) { + return Math.sqrt( + (b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1]) + ); + } + LiteGraph.distance = distance; + + function colorToString(c) { + return ( + "rgba(" + + Math.round(c[0] * 255).toFixed() + + "," + + Math.round(c[1] * 255).toFixed() + + "," + + Math.round(c[2] * 255).toFixed() + + "," + + (c.length == 4 ? c[3].toFixed(2) : "1.0") + + ")" + ); + } + LiteGraph.colorToString = colorToString; + + function isInsideRectangle(x, y, left, top, width, height) { + if (left < x && left + width > x && top < y && top + height > y) { + return true; + } + return false; + } + LiteGraph.isInsideRectangle = isInsideRectangle; + + //[minx,miny,maxx,maxy] + function growBounding(bounding, x, y) { + if (x < bounding[0]) { + bounding[0] = x; + } else if (x > bounding[2]) { + bounding[2] = x; + } + + if (y < bounding[1]) { + bounding[1] = y; + } else if (y > bounding[3]) { + bounding[3] = y; + } + } + LiteGraph.growBounding = growBounding; + + //point inside bounding box + function isInsideBounding(p, bb) { + if ( + p[0] < bb[0][0] || + p[1] < bb[0][1] || + p[0] > bb[1][0] || + p[1] > bb[1][1] + ) { + return false; + } + return true; + } + LiteGraph.isInsideBounding = isInsideBounding; + + //bounding overlap, format: [ startx, starty, width, height ] + function overlapBounding(a, b) { + var A_end_x = a[0] + a[2]; + var A_end_y = a[1] + a[3]; + var B_end_x = b[0] + b[2]; + var B_end_y = b[1] + b[3]; + + if ( + a[0] > B_end_x || + a[1] > B_end_y || + A_end_x < b[0] || + A_end_y < b[1] + ) { + return false; + } + return true; + } + LiteGraph.overlapBounding = overlapBounding; + + //Convert a hex value to its decimal value - the inputted hex must be in the + // format of a hex triplet - the kind we use for HTML colours. The function + // will return an array with three values. + function hex2num(hex) { + if (hex.charAt(0) == "#") { + hex = hex.slice(1); + } //Remove the '#' char - if there is one. + hex = hex.toUpperCase(); + var hex_alphabets = "0123456789ABCDEF"; + var value = new Array(3); + var k = 0; + var int1, int2; + for (var i = 0; i < 6; i += 2) { + int1 = hex_alphabets.indexOf(hex.charAt(i)); + int2 = hex_alphabets.indexOf(hex.charAt(i + 1)); + value[k] = int1 * 16 + int2; + k++; + } + return value; + } + + LiteGraph.hex2num = hex2num; + + //Give a array with three values as the argument and the function will return + // the corresponding hex triplet. + function num2hex(triplet) { + var hex_alphabets = "0123456789ABCDEF"; + var hex = "#"; + var int1, int2; + for (var i = 0; i < 3; i++) { + int1 = triplet[i] / 16; + int2 = triplet[i] % 16; + + hex += hex_alphabets.charAt(int1) + hex_alphabets.charAt(int2); + } + return hex; + } + + LiteGraph.num2hex = num2hex; + + /* LiteGraph GUI elements used for canvas editing *************************************/ + + /** + * ContextMenu from LiteGUI + * + * @class ContextMenu + * @constructor + * @param {Array} values (allows object { title: "Nice text", callback: function ... }) + * @param {Object} options [optional] Some options:\ + * - title: title to show on top of the menu + * - callback: function to call when an option is clicked, it receives the item information + * - ignore_item_callbacks: ignores the callback inside the item, it just calls the options.callback + * - event: you can pass a MouseEvent, this way the ContextMenu appears in that position + */ + function ContextMenu(values, options) { + options = options || {}; + this.options = options; + var that = this; + + //to link a menu with its parent + if (options.parentMenu) { + if (options.parentMenu.constructor !== this.constructor) { + console.error( + "parentMenu must be of class ContextMenu, ignoring it" + ); + options.parentMenu = null; + } else { + this.parentMenu = options.parentMenu; + this.parentMenu.lock = true; + this.parentMenu.current_submenu = this; + } + } + + var eventClass = null; + if(options.event) //use strings because comparing classes between windows doesnt work + eventClass = options.event.constructor.name; + if ( eventClass !== "MouseEvent" && + eventClass !== "CustomEvent" && + eventClass !== "PointerEvent" + ) { + console.error( + "Event passed to ContextMenu is not of type MouseEvent or CustomEvent. Ignoring it." + ); + options.event = null; + } + + var root = document.createElement("div"); + root.className = "litegraph litecontextmenu litemenubar-panel"; + if (options.className) { + root.className += " " + options.className; + } + root.style.minWidth = 100; + root.style.minHeight = 100; + root.style.pointerEvents = "none"; + setTimeout(function() { + root.style.pointerEvents = "auto"; + }, 100); //delay so the mouse up event is not caught by this element + + //this prevents the default context browser menu to open in case this menu was created when pressing right button + root.addEventListener( + "mouseup", + function(e) { + e.preventDefault(); + return true; + }, + true + ); + root.addEventListener( + "contextmenu", + function(e) { + if (e.button != 2) { + //right button + return false; + } + e.preventDefault(); + return false; + }, + true + ); + + root.addEventListener( + "mousedown", + function(e) { + if (e.button == 2) { + that.close(); + e.preventDefault(); + return true; + } + }, + true + ); + + function on_mouse_wheel(e) { + var pos = parseInt(root.style.top); + root.style.top = + (pos + e.deltaY * options.scroll_speed).toFixed() + "px"; + e.preventDefault(); + return true; + } + + if (!options.scroll_speed) { + options.scroll_speed = 0.1; + } + + root.addEventListener("wheel", on_mouse_wheel, true); + root.addEventListener("mousewheel", on_mouse_wheel, true); + + this.root = root; + + //title + if (options.title) { + var element = document.createElement("div"); + element.className = "litemenu-title"; + element.innerHTML = options.title; + root.appendChild(element); + } + + //entries + var num = 0; + for (var i in values) { + var name = values.constructor == Array ? values[i] : i; + if (name != null && name.constructor !== String) { + name = name.content === undefined ? String(name) : name.content; + } + var value = values[i]; + this.addItem(name, value, options); + num++; + } + + //close on leave + root.addEventListener("mouseleave", function(e) { + if (that.lock) { + return; + } + if (root.closing_timer) { + clearTimeout(root.closing_timer); + } + root.closing_timer = setTimeout(that.close.bind(that, e), 500); + //that.close(e); + }); + + root.addEventListener("mouseenter", function(e) { + if (root.closing_timer) { + clearTimeout(root.closing_timer); + } + }); + + //insert before checking position + var root_document = document; + if (options.event) { + root_document = options.event.target.ownerDocument; + } + + if (!root_document) { + root_document = document; + } + + if( root_document.fullscreenElement ) + root_document.fullscreenElement.appendChild(root); + else + root_document.body.appendChild(root); + + //compute best position + var left = options.left || 0; + var top = options.top || 0; + if (options.event) { + left = options.event.clientX - 10; + top = options.event.clientY - 10; + if (options.title) { + top -= 20; + } + + if (options.parentMenu) { + var rect = options.parentMenu.root.getBoundingClientRect(); + left = rect.left + rect.width; + } + + var body_rect = document.body.getBoundingClientRect(); + var root_rect = root.getBoundingClientRect(); + + if (left > body_rect.width - root_rect.width - 10) { + left = body_rect.width - root_rect.width - 10; + } + if (top > body_rect.height - root_rect.height - 10) { + top = body_rect.height - root_rect.height - 10; + } + } + + root.style.left = left + "px"; + root.style.top = top + "px"; + + if (options.scale) { + root.style.transform = "scale(" + options.scale + ")"; + } + } + + ContextMenu.prototype.addItem = function(name, value, options) { + var that = this; + options = options || {}; + + var element = document.createElement("div"); + element.className = "litemenu-entry submenu"; + + var disabled = false; + + if (value === null) { + element.classList.add("separator"); + //element.innerHTML = "
" + //continue; + } else { + element.innerHTML = value && value.title ? value.title : name; + element.value = value; + + if (value) { + if (value.disabled) { + disabled = true; + element.classList.add("disabled"); + } + if (value.submenu || value.has_submenu) { + element.classList.add("has_submenu"); + } + } + + if (typeof value == "function") { + element.dataset["value"] = name; + element.onclick_callback = value; + } else { + element.dataset["value"] = value; + } + + if (value.className) { + element.className += " " + value.className; + } + } + + this.root.appendChild(element); + if (!disabled) { + element.addEventListener("click", inner_onclick); + } + if (options.autoopen) { + element.addEventListener("mouseenter", inner_over); + } + + function inner_over(e) { + var value = this.value; + if (!value || !value.has_submenu) { + return; + } + //if it is a submenu, autoopen like the item was clicked + inner_onclick.call(this, e); + } + + //menu option clicked + function inner_onclick(e) { + var value = this.value; + var close_parent = true; + + if (that.current_submenu) { + that.current_submenu.close(e); + } + + //global callback + if (options.callback) { + var r = options.callback.call( + this, + value, + options, + e, + that, + options.node + ); + if (r === true) { + close_parent = false; + } + } + + //special cases + if (value) { + if ( + value.callback && + !options.ignore_item_callbacks && + value.disabled !== true + ) { + //item callback + var r = value.callback.call( + this, + value, + options, + e, + that, + options.extra + ); + if (r === true) { + close_parent = false; + } + } + if (value.submenu) { + if (!value.submenu.options) { + throw "ContextMenu submenu needs options"; + } + var submenu = new that.constructor(value.submenu.options, { + callback: value.submenu.callback, + event: e, + parentMenu: that, + ignore_item_callbacks: + value.submenu.ignore_item_callbacks, + title: value.submenu.title, + extra: value.submenu.extra, + autoopen: options.autoopen + }); + close_parent = false; + } + } + + if (close_parent && !that.lock) { + that.close(); + } + } + + return element; + }; + + ContextMenu.prototype.close = function(e, ignore_parent_menu) { + if (this.root.parentNode) { + this.root.parentNode.removeChild(this.root); + } + if (this.parentMenu && !ignore_parent_menu) { + this.parentMenu.lock = false; + this.parentMenu.current_submenu = null; + if (e === undefined) { + this.parentMenu.close(); + } else if ( + e && + !ContextMenu.isCursorOverElement(e, this.parentMenu.root) + ) { + ContextMenu.trigger(this.parentMenu.root, "mouseleave", e); + } + } + if (this.current_submenu) { + this.current_submenu.close(e, true); + } + + if (this.root.closing_timer) { + clearTimeout(this.root.closing_timer); + } + }; + + //this code is used to trigger events easily (used in the context menu mouseleave + ContextMenu.trigger = function(element, event_name, params, origin) { + var evt = document.createEvent("CustomEvent"); + evt.initCustomEvent(event_name, true, true, params); //canBubble, cancelable, detail + evt.srcElement = origin; + if (element.dispatchEvent) { + element.dispatchEvent(evt); + } else if (element.__events) { + element.__events.dispatchEvent(evt); + } + //else nothing seems binded here so nothing to do + return evt; + }; + + //returns the top most menu + ContextMenu.prototype.getTopMenu = function() { + if (this.options.parentMenu) { + return this.options.parentMenu.getTopMenu(); + } + return this; + }; + + ContextMenu.prototype.getFirstEvent = function() { + if (this.options.parentMenu) { + return this.options.parentMenu.getFirstEvent(); + } + return this.options.event; + }; + + ContextMenu.isCursorOverElement = function(event, element) { + var left = event.clientX; + var top = event.clientY; + var rect = element.getBoundingClientRect(); + if (!rect) { + return false; + } + if ( + top > rect.top && + top < rect.top + rect.height && + left > rect.left && + left < rect.left + rect.width + ) { + return true; + } + return false; + }; + + LiteGraph.ContextMenu = ContextMenu; + + LiteGraph.closeAllContextMenus = function(ref_window) { + ref_window = ref_window || window; + + var elements = ref_window.document.querySelectorAll(".litecontextmenu"); + if (!elements.length) { + return; + } + + var result = []; + for (var i = 0; i < elements.length; i++) { + result.push(elements[i]); + } + + for (var i in result) { + if (result[i].close) { + result[i].close(); + } else if (result[i].parentNode) { + result[i].parentNode.removeChild(result[i]); + } + } + }; + + LiteGraph.extendClass = function(target, origin) { + for (var i in origin) { + //copy class properties + if (target.hasOwnProperty(i)) { + continue; + } + target[i] = origin[i]; + } + + if (origin.prototype) { + //copy prototype properties + for (var i in origin.prototype) { + //only enumerable + if (!origin.prototype.hasOwnProperty(i)) { + continue; + } + + if (target.prototype.hasOwnProperty(i)) { + //avoid overwriting existing ones + continue; + } + + //copy getters + if (origin.prototype.__lookupGetter__(i)) { + target.prototype.__defineGetter__( + i, + origin.prototype.__lookupGetter__(i) + ); + } else { + target.prototype[i] = origin.prototype[i]; + } + + //and setters + if (origin.prototype.__lookupSetter__(i)) { + target.prototype.__defineSetter__( + i, + origin.prototype.__lookupSetter__(i) + ); + } + } + } + }; + + //used by some widgets to render a curve editor + function CurveEditor( points ) + { + this.points = points; + this.selected = -1; + this.nearest = -1; + this.size = null; //stores last size used + this.must_update = true; + this.margin = 5; + } + + CurveEditor.sampleCurve = function(f,points) + { + if(!points) + return; + for(var i = 0; i < points.length - 1; ++i) + { + var p = points[i]; + var pn = points[i+1]; + if(pn[0] < f) + continue; + var r = (pn[0] - p[0]); + if( Math.abs(r) < 0.00001 ) + return p[1]; + var local_f = (f - p[0]) / r; + return p[1] * (1.0 - local_f) + pn[1] * local_f; + } + return 0; + } + + CurveEditor.prototype.draw = function( ctx, size, graphcanvas, background_color, line_color, inactive ) + { + var points = this.points; + if(!points) + return; + this.size = size; + var w = size[0] - this.margin * 2; + var h = size[1] - this.margin * 2; + + line_color = line_color || "#666"; + + ctx.save(); + ctx.translate(this.margin,this.margin); + + if(background_color) + { + ctx.fillStyle = "#111"; + ctx.fillRect(0,0,w,h); + ctx.fillStyle = "#222"; + ctx.fillRect(w*0.5,0,1,h); + ctx.strokeStyle = "#333"; + ctx.strokeRect(0,0,w,h); + } + ctx.strokeStyle = line_color; + if(inactive) + ctx.globalAlpha = 0.5; + ctx.beginPath(); + for(var i = 0; i < points.length; ++i) + { + var p = points[i]; + ctx.lineTo( p[0] * w, (1.0 - p[1]) * h ); + } + ctx.stroke(); + ctx.globalAlpha = 1; + if(!inactive) + for(var i = 0; i < points.length; ++i) + { + var p = points[i]; + ctx.fillStyle = this.selected == i ? "#FFF" : (this.nearest == i ? "#DDD" : "#AAA"); + ctx.beginPath(); + ctx.arc( p[0] * w, (1.0 - p[1]) * h, 2, 0, Math.PI * 2 ); + ctx.fill(); + } + ctx.restore(); + } + + //localpos is mouse in curve editor space + CurveEditor.prototype.onMouseDown = function( localpos, graphcanvas ) + { + var points = this.points; + if(!points) + return; + if( localpos[1] < 0 ) + return; + + //this.captureInput(true); + var w = this.size[0] - this.margin * 2; + var h = this.size[1] - this.margin * 2; + var x = localpos[0] - this.margin; + var y = localpos[1] - this.margin; + var pos = [x,y]; + var max_dist = 30 / graphcanvas.ds.scale; + //search closer one + this.selected = this.getCloserPoint(pos, max_dist); + //create one + if(this.selected == -1) + { + var point = [x / w, 1 - y / h]; + points.push(point); + points.sort(function(a,b){ return a[0] - b[0]; }); + this.selected = points.indexOf(point); + this.must_update = true; + } + if(this.selected != -1) + return true; + } + + CurveEditor.prototype.onMouseMove = function( localpos, graphcanvas ) + { + var points = this.points; + if(!points) + return; + var s = this.selected; + if(s < 0) + return; + var x = (localpos[0] - this.margin) / (this.size[0] - this.margin * 2 ); + var y = (localpos[1] - this.margin) / (this.size[1] - this.margin * 2 ); + var curvepos = [(localpos[0] - this.margin),(localpos[1] - this.margin)]; + var max_dist = 30 / graphcanvas.ds.scale; + this._nearest = this.getCloserPoint(curvepos, max_dist); + var point = points[s]; + if(point) + { + var is_edge_point = s == 0 || s == points.length - 1; + if( !is_edge_point && (localpos[0] < -10 || localpos[0] > this.size[0] + 10 || localpos[1] < -10 || localpos[1] > this.size[1] + 10) ) + { + points.splice(s,1); + this.selected = -1; + return; + } + if( !is_edge_point ) //not edges + point[0] = Math.clamp(x,0,1); + else + point[0] = s == 0 ? 0 : 1; + point[1] = 1.0 - Math.clamp(y,0,1); + points.sort(function(a,b){ return a[0] - b[0]; }); + this.selected = points.indexOf(point); + this.must_update = true; + } + } + + CurveEditor.prototype.onMouseUp = function( localpos, graphcanvas ) + { + this.selected = -1; + return false; + } + + CurveEditor.prototype.getCloserPoint = function(pos, max_dist) + { + var points = this.points; + if(!points) + return -1; + max_dist = max_dist || 30; + var w = (this.size[0] - this.margin * 2); + var h = (this.size[1] - this.margin * 2); + var num = points.length; + var p2 = [0,0]; + var min_dist = 1000000; + var closest = -1; + var last_valid = -1; + for(var i = 0; i < num; ++i) + { + var p = points[i]; + p2[0] = p[0] * w; + p2[1] = (1.0 - p[1]) * h; + if(p2[0] < pos[0]) + last_valid = i; + var dist = vec2.distance(pos,p2); + if(dist > min_dist || dist > max_dist) + continue; + closest = i; + min_dist = dist; + } + return closest; + } + + LiteGraph.CurveEditor = CurveEditor; + + //used to create nodes from wrapping functions + LiteGraph.getParameterNames = function(func) { + return (func + "") + .replace(/[/][/].*$/gm, "") // strip single-line comments + .replace(/\s+/g, "") // strip white space + .replace(/[/][*][^/*]*[*][/]/g, "") // strip multi-line comments /**/ + .split("){", 1)[0] + .replace(/^[^(]*[(]/, "") // extract the parameters + .replace(/=[^,]+/g, "") // strip any ES6 defaults + .split(",") + .filter(Boolean); // split & filter [""] + }; + + Math.clamp = function(v, a, b) { + return a > v ? a : b < v ? b : v; + }; + + if (typeof window != "undefined" && !window["requestAnimationFrame"]) { + window.requestAnimationFrame = + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + function(callback) { + window.setTimeout(callback, 1000 / 60); + }; + } +})(this); + +if (typeof exports != "undefined") { + exports.LiteGraph = this.LiteGraph; +} + +//basic nodes +(function(global) { + var LiteGraph = global.LiteGraph; + + //Constant + function Time() { + this.addOutput("in ms", "number"); + this.addOutput("in sec", "number"); + } + + Time.title = "Time"; + Time.desc = "Time"; + + Time.prototype.onExecute = function() { + this.setOutputData(0, this.graph.globaltime * 1000); + this.setOutputData(1, this.graph.globaltime); + }; + + LiteGraph.registerNodeType("basic/time", Time); + + //Subgraph: a node that contains a graph + function Subgraph() { + var that = this; + this.size = [140, 80]; + this.properties = { enabled: true }; + this.enabled = true; + + //create inner graph + this.subgraph = new LiteGraph.LGraph(); + this.subgraph._subgraph_node = this; + this.subgraph._is_subgraph = true; + + this.subgraph.onTrigger = this.onSubgraphTrigger.bind(this); + + this.subgraph.onInputAdded = this.onSubgraphNewInput.bind(this); + this.subgraph.onInputRenamed = this.onSubgraphRenamedInput.bind(this); + this.subgraph.onInputTypeChanged = this.onSubgraphTypeChangeInput.bind( + this + ); + this.subgraph.onInputRemoved = this.onSubgraphRemovedInput.bind(this); + + this.subgraph.onOutputAdded = this.onSubgraphNewOutput.bind(this); + this.subgraph.onOutputRenamed = this.onSubgraphRenamedOutput.bind(this); + this.subgraph.onOutputTypeChanged = this.onSubgraphTypeChangeOutput.bind( + this + ); + this.subgraph.onOutputRemoved = this.onSubgraphRemovedOutput.bind(this); + } + + Subgraph.title = "Subgraph"; + Subgraph.desc = "Graph inside a node"; + Subgraph.title_color = "#334"; + + Subgraph.prototype.onGetInputs = function() { + return [["enabled", "boolean"]]; + }; + + Subgraph.prototype.onDrawTitle = function(ctx) { + if (this.flags.collapsed) { + return; + } + + ctx.fillStyle = "#555"; + var w = LiteGraph.NODE_TITLE_HEIGHT; + var x = this.size[0] - w; + ctx.fillRect(x, -w, w, w); + ctx.fillStyle = "#333"; + ctx.beginPath(); + ctx.moveTo(x + w * 0.2, -w * 0.6); + ctx.lineTo(x + w * 0.8, -w * 0.6); + ctx.lineTo(x + w * 0.5, -w * 0.3); + ctx.fill(); + }; + + Subgraph.prototype.onDblClick = function(e, pos, graphcanvas) { + var that = this; + setTimeout(function() { + graphcanvas.openSubgraph(that.subgraph); + }, 10); + }; + + Subgraph.prototype.onMouseDown = function(e, pos, graphcanvas) { + if ( + !this.flags.collapsed && + pos[0] > this.size[0] - LiteGraph.NODE_TITLE_HEIGHT && + pos[1] < 0 + ) { + var that = this; + setTimeout(function() { + graphcanvas.openSubgraph(that.subgraph); + }, 10); + } + }; + + Subgraph.prototype.onAction = function(action, param) { + this.subgraph.onAction(action, param); + }; + + Subgraph.prototype.onExecute = function() { + this.enabled = this.getInputOrProperty("enabled"); + if (!this.enabled) { + return; + } + + //send inputs to subgraph global inputs + if (this.inputs) { + for (var i = 0; i < this.inputs.length; i++) { + var input = this.inputs[i]; + var value = this.getInputData(i); + this.subgraph.setInputData(input.name, value); + } + } + + //execute + this.subgraph.runStep(); + + //send subgraph global outputs to outputs + if (this.outputs) { + for (var i = 0; i < this.outputs.length; i++) { + var output = this.outputs[i]; + var value = this.subgraph.getOutputData(output.name); + this.setOutputData(i, value); + } + } + }; + + Subgraph.prototype.sendEventToAllNodes = function(eventname, param, mode) { + if (this.enabled) { + this.subgraph.sendEventToAllNodes(eventname, param, mode); + } + }; + + //**** INPUTS *********************************** + Subgraph.prototype.onSubgraphTrigger = function(event, param) { + var slot = this.findOutputSlot(event); + if (slot != -1) { + this.triggerSlot(slot); + } + }; + + Subgraph.prototype.onSubgraphNewInput = function(name, type) { + var slot = this.findInputSlot(name); + if (slot == -1) { + //add input to the node + this.addInput(name, type); + } + }; + + Subgraph.prototype.onSubgraphRenamedInput = function(oldname, name) { + var slot = this.findInputSlot(oldname); + if (slot == -1) { + return; + } + var info = this.getInputInfo(slot); + info.name = name; + }; + + Subgraph.prototype.onSubgraphTypeChangeInput = function(name, type) { + var slot = this.findInputSlot(name); + if (slot == -1) { + return; + } + var info = this.getInputInfo(slot); + info.type = type; + }; + + Subgraph.prototype.onSubgraphRemovedInput = function(name) { + var slot = this.findInputSlot(name); + if (slot == -1) { + return; + } + this.removeInput(slot); + }; + + //**** OUTPUTS *********************************** + Subgraph.prototype.onSubgraphNewOutput = function(name, type) { + var slot = this.findOutputSlot(name); + if (slot == -1) { + this.addOutput(name, type); + } + }; + + Subgraph.prototype.onSubgraphRenamedOutput = function(oldname, name) { + var slot = this.findOutputSlot(oldname); + if (slot == -1) { + return; + } + var info = this.getOutputInfo(slot); + info.name = name; + }; + + Subgraph.prototype.onSubgraphTypeChangeOutput = function(name, type) { + var slot = this.findOutputSlot(name); + if (slot == -1) { + return; + } + var info = this.getOutputInfo(slot); + info.type = type; + }; + + Subgraph.prototype.onSubgraphRemovedOutput = function(name) { + var slot = this.findInputSlot(name); + if (slot == -1) { + return; + } + this.removeOutput(slot); + }; + // ***************************************************** + + Subgraph.prototype.getExtraMenuOptions = function(graphcanvas) { + var that = this; + return [ + { + content: "Open", + callback: function() { + graphcanvas.openSubgraph(that.subgraph); + } + } + ]; + }; + + Subgraph.prototype.onResize = function(size) { + size[1] += 20; + }; + + Subgraph.prototype.serialize = function() { + var data = LiteGraph.LGraphNode.prototype.serialize.call(this); + data.subgraph = this.subgraph.serialize(); + return data; + }; + //no need to define node.configure, the default method detects node.subgraph and passes the object to node.subgraph.configure() + + Subgraph.prototype.clone = function() { + var node = LiteGraph.createNode(this.type); + var data = this.serialize(); + delete data["id"]; + delete data["inputs"]; + delete data["outputs"]; + node.configure(data); + return node; + }; + + LiteGraph.Subgraph = Subgraph; + LiteGraph.registerNodeType("graph/subgraph", Subgraph); + + //Input for a subgraph + function GraphInput() { + this.addOutput("", "number"); + + this.name_in_graph = ""; + this.properties = { + name: "", + type: "number", + value: 0 + }; + + var that = this; + + this.name_widget = this.addWidget( + "text", + "Name", + this.properties.name, + function(v) { + if (!v) { + return; + } + that.setProperty("name",v); + } + ); + this.type_widget = this.addWidget( + "text", + "Type", + this.properties.type, + function(v) { + that.setProperty("type",v); + } + ); + + this.value_widget = this.addWidget( + "number", + "Value", + this.properties.value, + function(v) { + that.setProperty("value",v); + } + ); + + this.widgets_up = true; + this.size = [180, 90]; + } + + GraphInput.title = "Input"; + GraphInput.desc = "Input of the graph"; + + GraphInput.prototype.onConfigure = function() + { + this.updateType(); + } + + GraphInput.prototype.updateType = function() + { + var type = this.properties.type; + this.type_widget.value = type; + if(this.outputs[0].type != type) + { + this.outputs[0].type = type; + this.disconnectOutput(0); + } + if(type == "number") + { + this.value_widget.type = "number"; + this.value_widget.value = 0; + } + else if(type == "boolean") + { + this.value_widget.type = "toggle"; + this.value_widget.value = true; + } + else if(type == "string") + { + this.value_widget.type = "text"; + this.value_widget.value = ""; + } + else + { + this.value_widget.type = null; + this.value_widget.value = null; + } + this.properties.value = this.value_widget.value; + } + + GraphInput.prototype.onPropertyChanged = function(name,v) + { + if( name == "name" ) + { + if (v == "" || v == this.name_in_graph || v == "enabled") { + return false; + } + if(this.graph) + { + if (this.name_in_graph) { + //already added + this.graph.renameInput( this.name_in_graph, v ); + } else { + this.graph.addInput( v, this.properties.type ); + } + } //what if not?! + this.name_widget.value = v; + this.name_in_graph = v; + } + else if( name == "type" ) + { + v = v || ""; + this.updateType(v); + } + else if( name == "value" ) + { + } + } + + GraphInput.prototype.getTitle = function() { + if (this.flags.collapsed) { + return this.properties.name; + } + return this.title; + }; + + GraphInput.prototype.onAction = function(action, param) { + if (this.properties.type == LiteGraph.EVENT) { + this.triggerSlot(0, param); + } + }; + + GraphInput.prototype.onExecute = function() { + var name = this.properties.name; + //read from global input + var data = this.graph.inputs[name]; + if (!data) { + this.setOutputData(0, this.properties.value ); + return; + } + + this.setOutputData(0, data.value !== undefined ? data.value : this.properties.value ); + }; + + GraphInput.prototype.onRemoved = function() { + if (this.name_in_graph) { + this.graph.removeInput(this.name_in_graph); + } + }; + + LiteGraph.GraphInput = GraphInput; + LiteGraph.registerNodeType("graph/input", GraphInput); + + //Output for a subgraph + function GraphOutput() { + this.addInput("", ""); + + this.name_in_graph = ""; + this.properties = {}; + var that = this; + + Object.defineProperty(this.properties, "name", { + get: function() { + return that.name_in_graph; + }, + set: function(v) { + if (v == "" || v == that.name_in_graph) { + return; + } + if (that.name_in_graph) { + //already added + that.graph.renameOutput(that.name_in_graph, v); + } else { + that.graph.addOutput(v, that.properties.type); + } + that.name_widget.value = v; + that.name_in_graph = v; + }, + enumerable: true + }); + + Object.defineProperty(this.properties, "type", { + get: function() { + return that.inputs[0].type; + }, + set: function(v) { + if (v == "action" || v == "event") { + v = LiteGraph.ACTION; + } + that.inputs[0].type = v; + if (that.name_in_graph) { + //already added + that.graph.changeOutputType( + that.name_in_graph, + that.inputs[0].type + ); + } + that.type_widget.value = v || ""; + }, + enumerable: true + }); + + this.name_widget = this.addWidget("text","Name",this.properties.name,"name"); + this.type_widget = this.addWidget("text","Type",this.properties.type,"type"); + this.widgets_up = true; + this.size = [180, 60]; + } + + GraphOutput.title = "Output"; + GraphOutput.desc = "Output of the graph"; + + GraphOutput.prototype.onExecute = function() { + this._value = this.getInputData(0); + this.graph.setOutputData(this.properties.name, this._value); + }; + + GraphOutput.prototype.onAction = function(action, param) { + if (this.properties.type == LiteGraph.ACTION) { + this.graph.trigger(this.properties.name, param); + } + }; + + GraphOutput.prototype.onRemoved = function() { + if (this.name_in_graph) { + this.graph.removeOutput(this.name_in_graph); + } + }; + + GraphOutput.prototype.getTitle = function() { + if (this.flags.collapsed) { + return this.properties.name; + } + return this.title; + }; + + LiteGraph.GraphOutput = GraphOutput; + LiteGraph.registerNodeType("graph/output", GraphOutput); + + //Constant + function ConstantNumber() { + this.addOutput("value", "number"); + this.addProperty("value", 1.0); + this.widget = this.addWidget("number","value",1,"value"); + this.widgets_up = true; + this.size = [180, 30]; + } + + ConstantNumber.title = "Const Number"; + ConstantNumber.desc = "Constant number"; + + ConstantNumber.prototype.onExecute = function() { + this.setOutputData(0, parseFloat(this.properties["value"])); + }; + + ConstantNumber.prototype.getTitle = function() { + if (this.flags.collapsed) { + return this.properties.value; + } + return this.title; + }; + + ConstantNumber.prototype.setValue = function(v) + { + this.setProperty("value",v); + } + + ConstantNumber.prototype.onDrawBackground = function(ctx) { + //show the current value + this.outputs[0].label = this.properties["value"].toFixed(3); + }; + + LiteGraph.registerNodeType("basic/const", ConstantNumber); + + function ConstantBoolean() { + this.addOutput("", "boolean"); + this.addProperty("value", true); + this.widget = this.addWidget("toggle","value",true,"value"); + this.widgets_up = true; + this.size = [140, 30]; + } + + ConstantBoolean.title = "Const Boolean"; + ConstantBoolean.desc = "Constant boolean"; + ConstantBoolean.prototype.getTitle = ConstantNumber.prototype.getTitle; + + ConstantBoolean.prototype.onExecute = function() { + this.setOutputData(0, this.properties["value"]); + }; + + ConstantBoolean.prototype.setValue = ConstantNumber.prototype.setValue; + + ConstantBoolean.prototype.onGetInputs = function() { + return [["toggle", LiteGraph.ACTION]]; + }; + + ConstantBoolean.prototype.onAction = function(action) + { + this.setValue( !this.properties.value ); + } + + LiteGraph.registerNodeType("basic/boolean", ConstantBoolean); + + function ConstantString() { + this.addOutput("", "string"); + this.addProperty("value", ""); + this.widget = this.addWidget("text","value","","value"); //link to property value + this.widgets_up = true; + this.size = [180, 30]; + } + + ConstantString.title = "Const String"; + ConstantString.desc = "Constant string"; + + ConstantString.prototype.getTitle = ConstantNumber.prototype.getTitle; + + ConstantString.prototype.onExecute = function() { + this.setOutputData(0, this.properties["value"]); + }; + + ConstantString.prototype.setValue = ConstantNumber.prototype.setValue; + + ConstantString.prototype.onDropFile = function(file) + { + var that = this; + var reader = new FileReader(); + reader.onload = function(e) + { + that.setProperty("value",e.target.result); + } + reader.readAsText(file); + } + + LiteGraph.registerNodeType("basic/string", ConstantString); + + function ConstantFile() { + this.addInput("url", ""); + this.addOutput("", ""); + this.addProperty("url", ""); + this.addProperty("type", "text"); + this.widget = this.addWidget("text","url","","url"); + this._data = null; + } + + ConstantFile.title = "Const File"; + ConstantFile.desc = "Fetches a file from an url"; + ConstantFile["@type"] = { type: "enum", values: ["text","arraybuffer","blob","json"] }; + + ConstantFile.prototype.onPropertyChanged = function(name, value) { + if (name == "url") + { + if( value == null || value == "") + this._data = null; + else + { + this.fetchFile(value); + } + } + } + + ConstantFile.prototype.onExecute = function() { + var url = this.getInputData(0) || this.properties.url; + if(url && (url != this._url || this._type != this.properties.type)) + this.fetchFile(url); + this.setOutputData(0, this._data ); + }; + + ConstantFile.prototype.setValue = ConstantNumber.prototype.setValue; + + ConstantFile.prototype.fetchFile = function(url) { + var that = this; + if(!url || url.constructor !== String) + { + that._data = null; + that.boxcolor = null; + return; + } + + this._url = url; + this._type = this.properties.type; + if (url.substr(0, 4) == "http" && LiteGraph.proxy) { + url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); + } + fetch(url) + .then(function(response) { + if(!response.ok) + throw new Error("File not found"); + + if(that.properties.type == "arraybuffer") + return response.arrayBuffer(); + else if(that.properties.type == "text") + return response.text(); + else if(that.properties.type == "json") + return response.json(); + else if(that.properties.type == "blob") + return response.blob(); + }) + .then(function(data) { + that._data = data; + that.boxcolor = "#AEA"; + }) + .catch(function(error) { + that._data = null; + that.boxcolor = "red"; + console.error("error fetching file:",url); + }); + }; + + ConstantFile.prototype.onDropFile = function(file) + { + var that = this; + this._url = file.name; + this._type = this.properties.type; + this.properties.url = file.name; + var reader = new FileReader(); + reader.onload = function(e) + { + that.boxcolor = "#AEA"; + var v = e.target.result; + if( that.properties.type == "json" ) + v = JSON.parse(v); + that._data = v; + } + if(that.properties.type == "arraybuffer") + reader.readAsArrayBuffer(file); + else if(that.properties.type == "text" || that.properties.type == "json") + reader.readAsText(file); + else if(that.properties.type == "blob") + return reader.readAsBinaryString(file); + } + + LiteGraph.registerNodeType("basic/file", ConstantFile); + + //to store json objects + function ConstantData() { + this.addOutput("", ""); + this.addProperty("value", ""); + this.widget = this.addWidget("text","json","","value"); + this.widgets_up = true; + this.size = [140, 30]; + this._value = null; + } + + ConstantData.title = "Const Data"; + ConstantData.desc = "Constant Data"; + + ConstantData.prototype.onPropertyChanged = function(name, value) { + this.widget.value = value; + if (value == null || value == "") { + return; + } + + try { + this._value = JSON.parse(value); + this.boxcolor = "#AEA"; + } catch (err) { + this.boxcolor = "red"; + } + }; + + ConstantData.prototype.onExecute = function() { + this.setOutputData(0, this._value); + }; + + ConstantData.prototype.setValue = ConstantNumber.prototype.setValue; + + LiteGraph.registerNodeType("basic/data", ConstantData); + + //to store json objects + function ConstantArray() { + this.addInput("", ""); + this.addOutput("", "array"); + this.addProperty("value", ""); + this.widget = this.addWidget("text","array","","value"); + this.widgets_up = true; + this.size = [140, 30]; + this._value = null; + } + + ConstantArray.title = "Const Array"; + ConstantArray.desc = "Constant Array"; + + ConstantArray.prototype.onPropertyChanged = function(name, value) { + this.widget.value = value; + if (value == null || value == "") { + return; + } + + try { + this._value = JSON.parse(value); + this.boxcolor = "#AEA"; + } catch (err) { + this.boxcolor = "red"; + } + }; + + ConstantArray.prototype.onExecute = function() { + var v = this.getInputData(0); + if(v && v.length) + { + if(!this._value) + this._value = new Array(); + this._value.length = v.length; + for(var i = 0; i < v.length; ++i) + this._value[i] = v[i]; + } + this.setOutputData(0, this._value); + }; + + ConstantArray.prototype.setValue = ConstantNumber.prototype.setValue; + + LiteGraph.registerNodeType("basic/array", ConstantArray); + + function ArrayElement() { + this.addInput("array", "array,table,string"); + this.addInput("index", "number"); + this.addOutput("value", ""); + this.addProperty("index",0); + } + + ArrayElement.title = "Array[i]"; + ArrayElement.desc = "Returns an element from an array"; + + ArrayElement.prototype.onExecute = function() { + var array = this.getInputData(0); + var index = this.getInputData(1); + if(index == null) + index = this.properties.index; + if(array == null || index == null ) + return; + this.setOutputData(0, array[Math.floor(Number(index))] ); + }; + + LiteGraph.registerNodeType("basic/array[]", ArrayElement); + + function TableElement() { + this.addInput("table", "table"); + this.addInput("row", "number"); + this.addInput("col", "number"); + this.addOutput("value", ""); + this.addProperty("row",0); + this.addProperty("column",0); + } + + TableElement.title = "Table[row][col]"; + TableElement.desc = "Returns an element from a table"; + + TableElement.prototype.onExecute = function() { + var table = this.getInputData(0); + var row = this.getInputData(1); + var col = this.getInputData(2); + if(row == null) + row = this.properties.row; + if(col == null) + col = this.properties.column; + if(table == null || row == null || col == null) + return; + var row = table[Math.floor(Number(row))]; + if(row) + this.setOutputData(0, row[Math.floor(Number(col))] ); + else + this.setOutputData(0, null ); + }; + + LiteGraph.registerNodeType("basic/table[][]", TableElement); + + function ObjectProperty() { + this.addInput("obj", ""); + this.addOutput("", ""); + this.addProperty("value", ""); + this.widget = this.addWidget("text","prop.","",this.setValue.bind(this) ); + this.widgets_up = true; + this.size = [140, 30]; + this._value = null; + } + + ObjectProperty.title = "Object property"; + ObjectProperty.desc = "Outputs the property of an object"; + + ObjectProperty.prototype.setValue = function(v) { + this.properties.value = v; + this.widget.value = v; + }; + + ObjectProperty.prototype.getTitle = function() { + if (this.flags.collapsed) { + return "in." + this.properties.value; + } + return this.title; + }; + + ObjectProperty.prototype.onPropertyChanged = function(name, value) { + this.widget.value = value; + }; + + ObjectProperty.prototype.onExecute = function() { + var data = this.getInputData(0); + if (data != null) { + this.setOutputData(0, data[this.properties.value]); + } + }; + + LiteGraph.registerNodeType("basic/object_property", ObjectProperty); + + function ObjectKeys() { + this.addInput("obj", ""); + this.addOutput("keys", "array"); + this.size = [140, 30]; + } + + ObjectKeys.title = "Object keys"; + ObjectKeys.desc = "Outputs an array with the keys of an object"; + + ObjectKeys.prototype.onExecute = function() { + var data = this.getInputData(0); + if (data != null) { + this.setOutputData(0, Object.keys(data) ); + } + }; + + LiteGraph.registerNodeType("basic/object_keys", ObjectKeys); + + function MergeObjects() { + this.addInput("A", "object"); + this.addInput("B", "object"); + this.addOutput("", "object"); + this._result = {}; + var that = this; + this.addWidget("button","clear","",function(){ + that._result = {}; + }); + this.size = this.computeSize(); + } + + MergeObjects.title = "Merge Objects"; + MergeObjects.desc = "Creates an object copying properties from others"; + + MergeObjects.prototype.onExecute = function() { + var A = this.getInputData(0); + var B = this.getInputData(1); + var C = this._result; + if(A) + for(var i in A) + C[i] = A[i]; + if(B) + for(var i in B) + C[i] = B[i]; + this.setOutputData(0,C); + }; + + LiteGraph.registerNodeType("basic/merge_objects", MergeObjects ); + + //Store as variable + function Variable() { + this.size = [60, 30]; + this.addInput("in"); + this.addOutput("out"); + this.properties = { varname: "myname", global: false }; + this.value = null; + } + + Variable.title = "Variable"; + Variable.desc = "store/read variable value"; + + Variable.prototype.onExecute = function() { + this.value = this.getInputData(0); + if(this.graph) + this.graph.vars[ this.properties.varname ] = this.value; + if(this.properties.global) + global[this.properties.varname] = this.value; + this.setOutputData(0, this.value ); + }; + + Variable.prototype.getTitle = function() { + return this.properties.varname; + }; + + LiteGraph.registerNodeType("basic/variable", Variable); + + function length(v) { + if(v && v.length != null) + return Number(v.length); + return 0; + } + + LiteGraph.wrapFunctionAsNode( + "basic/length", + length, + ["*"], + "number" + ); + + function DownloadData() { + this.size = [60, 30]; + this.addInput("data", 0 ); + this.addInput("download", LiteGraph.ACTION ); + this.properties = { filename: "data.json" }; + this.value = null; + var that = this; + this.addWidget("button","Download","", function(v){ + if(!that.value) + return; + that.downloadAsFile(); + }); + } + + DownloadData.title = "Download"; + DownloadData.desc = "Download some data"; + + DownloadData.prototype.downloadAsFile = function() + { + if(this.value == null) + return; + + var str = null; + if(this.value.constructor === String) + str = this.value; + else + str = JSON.stringify(this.value); + + var file = new Blob([str]); + var url = URL.createObjectURL( file ); + var element = document.createElement("a"); + element.setAttribute('href', url); + element.setAttribute('download', this.properties.filename ); + element.style.display = 'none'; + document.body.appendChild(element); + element.click(); + document.body.removeChild(element); + setTimeout( function(){ URL.revokeObjectURL( url ); }, 1000*60 ); //wait one minute to revoke url + } + + DownloadData.prototype.onAction = function(action, param) { + var that = this; + setTimeout( function(){ that.downloadAsFile(); }, 100); //deferred to avoid blocking the renderer with the popup + } + + DownloadData.prototype.onExecute = function() { + if (this.inputs[0]) { + this.value = this.getInputData(0); + } + }; + + DownloadData.prototype.getTitle = function() { + if (this.flags.collapsed) { + return this.properties.filename; + } + return this.title; + }; + + LiteGraph.registerNodeType("basic/download", DownloadData); + + + + //Watch a value in the editor + function Watch() { + this.size = [60, 30]; + this.addInput("value", 0, { label: "" }); + this.value = 0; + } + + Watch.title = "Watch"; + Watch.desc = "Show value of input"; + + Watch.prototype.onExecute = function() { + if (this.inputs[0]) { + this.value = this.getInputData(0); + } + }; + + Watch.prototype.getTitle = function() { + if (this.flags.collapsed) { + return this.inputs[0].label; + } + return this.title; + }; + + Watch.toString = function(o) { + if (o == null) { + return "null"; + } else if (o.constructor === Number) { + return o.toFixed(3); + } else if (o.constructor === Array) { + var str = "["; + for (var i = 0; i < o.length; ++i) { + str += Watch.toString(o[i]) + (i + 1 != o.length ? "," : ""); + } + str += "]"; + return str; + } else { + return String(o); + } + }; + + Watch.prototype.onDrawBackground = function(ctx) { + //show the current value + this.inputs[0].label = Watch.toString(this.value); + }; + + LiteGraph.registerNodeType("basic/watch", Watch); + + //in case one type doesnt match other type but you want to connect them anyway + function Cast() { + this.addInput("in", 0); + this.addOutput("out", 0); + this.size = [40, 30]; + } + + Cast.title = "Cast"; + Cast.desc = "Allows to connect different types"; + + Cast.prototype.onExecute = function() { + this.setOutputData(0, this.getInputData(0)); + }; + + LiteGraph.registerNodeType("basic/cast", Cast); + + //Show value inside the debug console + function Console() { + this.mode = LiteGraph.ON_EVENT; + this.size = [80, 30]; + this.addProperty("msg", ""); + this.addInput("log", LiteGraph.EVENT); + this.addInput("msg", 0); + } + + Console.title = "Console"; + Console.desc = "Show value inside the console"; + + Console.prototype.onAction = function(action, param) { + if (action == "log") { + console.log(param); + } else if (action == "warn") { + console.warn(param); + } else if (action == "error") { + console.error(param); + } + }; + + Console.prototype.onExecute = function() { + var msg = this.getInputData(1); + if (msg !== null) { + this.properties.msg = msg; + } + console.log(msg); + }; + + Console.prototype.onGetInputs = function() { + return [ + ["log", LiteGraph.ACTION], + ["warn", LiteGraph.ACTION], + ["error", LiteGraph.ACTION] + ]; + }; + + LiteGraph.registerNodeType("basic/console", Console); + + //Show value inside the debug console + function Alert() { + this.mode = LiteGraph.ON_EVENT; + this.addProperty("msg", ""); + this.addInput("", LiteGraph.EVENT); + var that = this; + this.widget = this.addWidget("text", "Text", "", function(v) { + that.properties.msg = v; + }); + this.widgets_up = true; + this.size = [200, 30]; + } + + Alert.title = "Alert"; + Alert.desc = "Show an alert window"; + Alert.color = "#510"; + + Alert.prototype.onConfigure = function(o) { + this.widget.value = o.properties.msg; + }; + + Alert.prototype.onAction = function(action, param) { + var msg = this.properties.msg; + setTimeout(function() { + alert(msg); + }, 10); + }; + + LiteGraph.registerNodeType("basic/alert", Alert); + + //Execites simple code + function NodeScript() { + this.size = [60, 30]; + this.addProperty("onExecute", "return A;"); + this.addInput("A", ""); + this.addInput("B", ""); + this.addOutput("out", ""); + + this._func = null; + this.data = {}; + } + + NodeScript.prototype.onConfigure = function(o) { + if (o.properties.onExecute && LiteGraph.allow_scripts) + this.compileCode(o.properties.onExecute); + else + console.warn("Script not compiled, LiteGraph.allow_scripts is false"); + }; + + NodeScript.title = "Script"; + NodeScript.desc = "executes a code (max 100 characters)"; + + NodeScript.widgets_info = { + onExecute: { type: "code" } + }; + + NodeScript.prototype.onPropertyChanged = function(name, value) { + if (name == "onExecute" && LiteGraph.allow_scripts) + this.compileCode(value); + else + console.warn("Script not compiled, LiteGraph.allow_scripts is false"); + }; + + NodeScript.prototype.compileCode = function(code) { + this._func = null; + if (code.length > 256) { + console.warn("Script too long, max 256 chars"); + } else { + var code_low = code.toLowerCase(); + var forbidden_words = [ + "script", + "body", + "document", + "eval", + "nodescript", + "function" + ]; //bad security solution + for (var i = 0; i < forbidden_words.length; ++i) { + if (code_low.indexOf(forbidden_words[i]) != -1) { + console.warn("invalid script"); + return; + } + } + try { + this._func = new Function("A", "B", "C", "DATA", "node", code); + } catch (err) { + console.error("Error parsing script"); + console.error(err); + } + } + }; + + NodeScript.prototype.onExecute = function() { + if (!this._func) { + return; + } + + try { + var A = this.getInputData(0); + var B = this.getInputData(1); + var C = this.getInputData(2); + this.setOutputData(0, this._func(A, B, C, this.data, this)); + } catch (err) { + console.error("Error in script"); + console.error(err); + } + }; + + NodeScript.prototype.onGetOutputs = function() { + return [["C", ""]]; + }; + + LiteGraph.registerNodeType("basic/script", NodeScript); +})(this); + //event related nodes (function(global) { var LiteGraph = global.LiteGraph; @@ -12576,805 +12594,805 @@ if (typeof exports != "undefined") { LiteGraph.registerNodeType("basic/data_store", DataStore); })(this); - -//widgets -(function(global) { - var LiteGraph = global.LiteGraph; - - /* Button ****************/ - - function WidgetButton() { - this.addOutput("", LiteGraph.EVENT); - this.addOutput("", "boolean"); - this.addProperty("text", "click me"); - this.addProperty("font_size", 30); - this.addProperty("message", ""); - this.size = [164, 84]; - this.clicked = false; - } - - WidgetButton.title = "Button"; - WidgetButton.desc = "Triggers an event"; - - WidgetButton.font = "Arial"; - WidgetButton.prototype.onDrawForeground = function(ctx) { - if (this.flags.collapsed) { - return; - } - var margin = 10; - ctx.fillStyle = "black"; - ctx.fillRect( - margin + 1, - margin + 1, - this.size[0] - margin * 2, - this.size[1] - margin * 2 - ); - ctx.fillStyle = "#AAF"; - ctx.fillRect( - margin - 1, - margin - 1, - this.size[0] - margin * 2, - this.size[1] - margin * 2 - ); - ctx.fillStyle = this.clicked - ? "white" - : this.mouseOver - ? "#668" - : "#334"; - ctx.fillRect( - margin, - margin, - this.size[0] - margin * 2, - this.size[1] - margin * 2 - ); - - if (this.properties.text || this.properties.text === 0) { - var font_size = this.properties.font_size || 30; - ctx.textAlign = "center"; - ctx.fillStyle = this.clicked ? "black" : "white"; - ctx.font = font_size + "px " + WidgetButton.font; - ctx.fillText( - this.properties.text, - this.size[0] * 0.5, - this.size[1] * 0.5 + font_size * 0.3 - ); - ctx.textAlign = "left"; - } - }; - - WidgetButton.prototype.onMouseDown = function(e, local_pos) { - if ( - local_pos[0] > 1 && - local_pos[1] > 1 && - local_pos[0] < this.size[0] - 2 && - local_pos[1] < this.size[1] - 2 - ) { - this.clicked = true; - this.triggerSlot(0, this.properties.message); - return true; - } - }; - - WidgetButton.prototype.onExecute = function() { - this.setOutputData(1, this.clicked); - }; - - WidgetButton.prototype.onMouseUp = function(e) { - this.clicked = false; - }; - - LiteGraph.registerNodeType("widget/button", WidgetButton); - - function WidgetToggle() { - this.addInput("", "boolean"); - this.addInput("e", LiteGraph.ACTION); - this.addOutput("v", "boolean"); - this.addOutput("e", LiteGraph.EVENT); - this.properties = { font: "", value: false }; - this.size = [160, 44]; - } - - WidgetToggle.title = "Toggle"; - WidgetToggle.desc = "Toggles between true or false"; - - WidgetToggle.prototype.onDrawForeground = function(ctx) { - if (this.flags.collapsed) { - return; - } - - var size = this.size[1] * 0.5; - var margin = 0.25; - var h = this.size[1] * 0.8; - ctx.font = this.properties.font || (size * 0.8).toFixed(0) + "px Arial"; - var w = ctx.measureText(this.title).width; - var x = (this.size[0] - (w + size)) * 0.5; - - ctx.fillStyle = "#AAA"; - ctx.fillRect(x, h - size, size, size); - - ctx.fillStyle = this.properties.value ? "#AEF" : "#000"; - ctx.fillRect( - x + size * margin, - h - size + size * margin, - size * (1 - margin * 2), - size * (1 - margin * 2) - ); - - ctx.textAlign = "left"; - ctx.fillStyle = "#AAA"; - ctx.fillText(this.title, size * 1.2 + x, h * 0.85); - ctx.textAlign = "left"; - }; - - WidgetToggle.prototype.onAction = function(action) { - this.properties.value = !this.properties.value; - this.trigger("e", this.properties.value); - }; - - WidgetToggle.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v != null) { - this.properties.value = v; - } - this.setOutputData(0, this.properties.value); - }; - - WidgetToggle.prototype.onMouseDown = function(e, local_pos) { - if ( - local_pos[0] > 1 && - local_pos[1] > 1 && - local_pos[0] < this.size[0] - 2 && - local_pos[1] < this.size[1] - 2 - ) { - this.properties.value = !this.properties.value; - this.graph._version++; - this.trigger("e", this.properties.value); - return true; - } - }; - - LiteGraph.registerNodeType("widget/toggle", WidgetToggle); - - /* Number ****************/ - - function WidgetNumber() { - this.addOutput("", "number"); - this.size = [80, 60]; - this.properties = { min: -1000, max: 1000, value: 1, step: 1 }; - this.old_y = -1; - this._remainder = 0; - this._precision = 0; - this.mouse_captured = false; - } - - WidgetNumber.title = "Number"; - WidgetNumber.desc = "Widget to select number value"; - - WidgetNumber.pixels_threshold = 10; - WidgetNumber.markers_color = "#666"; - - WidgetNumber.prototype.onDrawForeground = function(ctx) { - var x = this.size[0] * 0.5; - var h = this.size[1]; - if (h > 30) { - ctx.fillStyle = WidgetNumber.markers_color; - ctx.beginPath(); - ctx.moveTo(x, h * 0.1); - ctx.lineTo(x + h * 0.1, h * 0.2); - ctx.lineTo(x + h * -0.1, h * 0.2); - ctx.fill(); - ctx.beginPath(); - ctx.moveTo(x, h * 0.9); - ctx.lineTo(x + h * 0.1, h * 0.8); - ctx.lineTo(x + h * -0.1, h * 0.8); - ctx.fill(); - ctx.font = (h * 0.7).toFixed(1) + "px Arial"; - } else { - ctx.font = (h * 0.8).toFixed(1) + "px Arial"; - } - - ctx.textAlign = "center"; - ctx.font = (h * 0.7).toFixed(1) + "px Arial"; - ctx.fillStyle = "#EEE"; - ctx.fillText( - this.properties.value.toFixed(this._precision), - x, - h * 0.75 - ); - }; - - WidgetNumber.prototype.onExecute = function() { - this.setOutputData(0, this.properties.value); - }; - - WidgetNumber.prototype.onPropertyChanged = function(name, value) { - var t = (this.properties.step + "").split("."); - this._precision = t.length > 1 ? t[1].length : 0; - }; - - WidgetNumber.prototype.onMouseDown = function(e, pos) { - if (pos[1] < 0) { - return; - } - - this.old_y = e.canvasY; - this.captureInput(true); - this.mouse_captured = true; - - return true; - }; - - WidgetNumber.prototype.onMouseMove = function(e) { - if (!this.mouse_captured) { - return; - } - - var delta = this.old_y - e.canvasY; - if (e.shiftKey) { - delta *= 10; - } - if (e.metaKey || e.altKey) { - delta *= 0.1; - } - this.old_y = e.canvasY; - - var steps = this._remainder + delta / WidgetNumber.pixels_threshold; - this._remainder = steps % 1; - steps = steps | 0; - - var v = Math.clamp( - this.properties.value + steps * this.properties.step, - this.properties.min, - this.properties.max - ); - this.properties.value = v; - this.graph._version++; - this.setDirtyCanvas(true); - }; - - WidgetNumber.prototype.onMouseUp = function(e, pos) { - if (e.click_time < 200) { - var steps = pos[1] > this.size[1] * 0.5 ? -1 : 1; - this.properties.value = Math.clamp( - this.properties.value + steps * this.properties.step, - this.properties.min, - this.properties.max - ); - this.graph._version++; - this.setDirtyCanvas(true); - } - - if (this.mouse_captured) { - this.mouse_captured = false; - this.captureInput(false); - } - }; - - LiteGraph.registerNodeType("widget/number", WidgetNumber); - - - /* Combo ****************/ - - function WidgetCombo() { - this.addOutput("", "string"); - this.addOutput("change", LiteGraph.EVENT); - this.size = [80, 60]; - this.properties = { value: "A", values:"A;B;C" }; - this.old_y = -1; - this.mouse_captured = false; - this._values = this.properties.values.split(";"); - var that = this; - this.widgets_up = true; - this.widget = this.addWidget("combo","", this.properties.value, function(v){ - that.properties.value = v; - that.triggerSlot(1, v); - }, { property: "value", values: this._values } ); - } - - WidgetCombo.title = "Combo"; - WidgetCombo.desc = "Widget to select from a list"; - - WidgetCombo.prototype.onExecute = function() { - this.setOutputData( 0, this.properties.value ); - }; - - WidgetCombo.prototype.onPropertyChanged = function(name, value) { - if(name == "values") - { - this._values = value.split(";"); - this.widget.options.values = this._values; - } - else if(name == "value") - { - this.widget.value = value; - } - }; - - LiteGraph.registerNodeType("widget/combo", WidgetCombo); - - - /* Knob ****************/ - - function WidgetKnob() { - this.addOutput("", "number"); - this.size = [64, 84]; - this.properties = { - min: 0, - max: 1, - value: 0.5, - color: "#7AF", - precision: 2 - }; - this.value = -1; - } - - WidgetKnob.title = "Knob"; - WidgetKnob.desc = "Circular controller"; - WidgetKnob.size = [80, 100]; - - WidgetKnob.prototype.onDrawForeground = function(ctx) { - if (this.flags.collapsed) { - return; - } - - if (this.value == -1) { - this.value = - (this.properties.value - this.properties.min) / - (this.properties.max - this.properties.min); - } - - var center_x = this.size[0] * 0.5; - var center_y = this.size[1] * 0.5; - var radius = Math.min(this.size[0], this.size[1]) * 0.5 - 5; - var w = Math.floor(radius * 0.05); - - ctx.globalAlpha = 1; - ctx.save(); - ctx.translate(center_x, center_y); - ctx.rotate(Math.PI * 0.75); - - //bg - ctx.fillStyle = "rgba(0,0,0,0.5)"; - ctx.beginPath(); - ctx.moveTo(0, 0); - ctx.arc(0, 0, radius, 0, Math.PI * 1.5); - ctx.fill(); - - //value - ctx.strokeStyle = "black"; - ctx.fillStyle = this.properties.color; - ctx.lineWidth = 2; - ctx.beginPath(); - ctx.moveTo(0, 0); - ctx.arc( - 0, - 0, - radius - 4, - 0, - Math.PI * 1.5 * Math.max(0.01, this.value) - ); - ctx.closePath(); - ctx.fill(); - //ctx.stroke(); - ctx.lineWidth = 1; - ctx.globalAlpha = 1; - ctx.restore(); - - //inner - ctx.fillStyle = "black"; - ctx.beginPath(); - ctx.arc(center_x, center_y, radius * 0.75, 0, Math.PI * 2, true); - ctx.fill(); - - //miniball - ctx.fillStyle = this.mouseOver ? "white" : this.properties.color; - ctx.beginPath(); - var angle = this.value * Math.PI * 1.5 + Math.PI * 0.75; - ctx.arc( - center_x + Math.cos(angle) * radius * 0.65, - center_y + Math.sin(angle) * radius * 0.65, - radius * 0.05, - 0, - Math.PI * 2, - true - ); - ctx.fill(); - - //text - ctx.fillStyle = this.mouseOver ? "white" : "#AAA"; - ctx.font = Math.floor(radius * 0.5) + "px Arial"; - ctx.textAlign = "center"; - ctx.fillText( - this.properties.value.toFixed(this.properties.precision), - center_x, - center_y + radius * 0.15 - ); - }; - - WidgetKnob.prototype.onExecute = function() { - this.setOutputData(0, this.properties.value); - this.boxcolor = LiteGraph.colorToString([ - this.value, - this.value, - this.value - ]); - }; - - WidgetKnob.prototype.onMouseDown = function(e) { - this.center = [this.size[0] * 0.5, this.size[1] * 0.5 + 20]; - this.radius = this.size[0] * 0.5; - if ( - e.canvasY - this.pos[1] < 20 || - LiteGraph.distance( - [e.canvasX, e.canvasY], - [this.pos[0] + this.center[0], this.pos[1] + this.center[1]] - ) > this.radius - ) { - return false; - } - this.oldmouse = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; - this.captureInput(true); - return true; - }; - - WidgetKnob.prototype.onMouseMove = function(e) { - if (!this.oldmouse) { - return; - } - - var m = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; - - var v = this.value; - v -= (m[1] - this.oldmouse[1]) * 0.01; - if (v > 1.0) { - v = 1.0; - } else if (v < 0.0) { - v = 0.0; - } - this.value = v; - this.properties.value = - this.properties.min + - (this.properties.max - this.properties.min) * this.value; - this.oldmouse = m; - this.setDirtyCanvas(true); - }; - - WidgetKnob.prototype.onMouseUp = function(e) { - if (this.oldmouse) { - this.oldmouse = null; - this.captureInput(false); - } - }; - - WidgetKnob.prototype.onPropertyChanged = function(name, value) { - if (name == "min" || name == "max" || name == "value") { - this.properties[name] = parseFloat(value); - return true; //block - } - }; - - LiteGraph.registerNodeType("widget/knob", WidgetKnob); - - //Show value inside the debug console - function WidgetSliderGUI() { - this.addOutput("", "number"); - this.properties = { - value: 0.5, - min: 0, - max: 1, - text: "V" - }; - var that = this; - this.size = [140, 40]; - this.slider = this.addWidget( - "slider", - "V", - this.properties.value, - function(v) { - that.properties.value = v; - }, - this.properties - ); - this.widgets_up = true; - } - - WidgetSliderGUI.title = "Inner Slider"; - - WidgetSliderGUI.prototype.onPropertyChanged = function(name, value) { - if (name == "value") { - this.slider.value = value; - } - }; - - WidgetSliderGUI.prototype.onExecute = function() { - this.setOutputData(0, this.properties.value); - }; - - LiteGraph.registerNodeType("widget/internal_slider", WidgetSliderGUI); - - //Widget H SLIDER - function WidgetHSlider() { - this.size = [160, 26]; - this.addOutput("", "number"); - this.properties = { color: "#7AF", min: 0, max: 1, value: 0.5 }; - this.value = -1; - } - - WidgetHSlider.title = "H.Slider"; - WidgetHSlider.desc = "Linear slider controller"; - - WidgetHSlider.prototype.onDrawForeground = function(ctx) { - if (this.value == -1) { - this.value = - (this.properties.value - this.properties.min) / - (this.properties.max - this.properties.min); - } - - //border - ctx.globalAlpha = 1; - ctx.lineWidth = 1; - ctx.fillStyle = "#000"; - ctx.fillRect(2, 2, this.size[0] - 4, this.size[1] - 4); - - ctx.fillStyle = this.properties.color; - ctx.beginPath(); - ctx.rect(4, 4, (this.size[0] - 8) * this.value, this.size[1] - 8); - ctx.fill(); - }; - - WidgetHSlider.prototype.onExecute = function() { - this.properties.value = - this.properties.min + - (this.properties.max - this.properties.min) * this.value; - this.setOutputData(0, this.properties.value); - this.boxcolor = LiteGraph.colorToString([ - this.value, - this.value, - this.value - ]); - }; - - WidgetHSlider.prototype.onMouseDown = function(e) { - if (e.canvasY - this.pos[1] < 0) { - return false; - } - - this.oldmouse = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; - this.captureInput(true); - return true; - }; - - WidgetHSlider.prototype.onMouseMove = function(e) { - if (!this.oldmouse) { - return; - } - - var m = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; - - var v = this.value; - var delta = m[0] - this.oldmouse[0]; - v += delta / this.size[0]; - if (v > 1.0) { - v = 1.0; - } else if (v < 0.0) { - v = 0.0; - } - - this.value = v; - - this.oldmouse = m; - this.setDirtyCanvas(true); - }; - - WidgetHSlider.prototype.onMouseUp = function(e) { - this.oldmouse = null; - this.captureInput(false); - }; - - WidgetHSlider.prototype.onMouseLeave = function(e) { - //this.oldmouse = null; - }; - - LiteGraph.registerNodeType("widget/hslider", WidgetHSlider); - - function WidgetProgress() { - this.size = [160, 26]; - this.addInput("", "number"); - this.properties = { min: 0, max: 1, value: 0, color: "#AAF" }; - } - - WidgetProgress.title = "Progress"; - WidgetProgress.desc = "Shows data in linear progress"; - - WidgetProgress.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v != undefined) { - this.properties["value"] = v; - } - }; - - WidgetProgress.prototype.onDrawForeground = function(ctx) { - //border - ctx.lineWidth = 1; - ctx.fillStyle = this.properties.color; - var v = - (this.properties.value - this.properties.min) / - (this.properties.max - this.properties.min); - v = Math.min(1, v); - v = Math.max(0, v); - ctx.fillRect(2, 2, (this.size[0] - 4) * v, this.size[1] - 4); - }; - - LiteGraph.registerNodeType("widget/progress", WidgetProgress); - - function WidgetText() { - this.addInputs("", 0); - this.properties = { - value: "...", - font: "Arial", - fontsize: 18, - color: "#AAA", - align: "left", - glowSize: 0, - decimals: 1 - }; - } - - WidgetText.title = "Text"; - WidgetText.desc = "Shows the input value"; - WidgetText.widgets = [ - { name: "resize", text: "Resize box", type: "button" }, - { name: "led_text", text: "LED", type: "minibutton" }, - { name: "normal_text", text: "Normal", type: "minibutton" } - ]; - - WidgetText.prototype.onDrawForeground = function(ctx) { - //ctx.fillStyle="#000"; - //ctx.fillRect(0,0,100,60); - ctx.fillStyle = this.properties["color"]; - var v = this.properties["value"]; - - if (this.properties["glowSize"]) { - ctx.shadowColor = this.properties.color; - ctx.shadowOffsetX = 0; - ctx.shadowOffsetY = 0; - ctx.shadowBlur = this.properties["glowSize"]; - } else { - ctx.shadowColor = "transparent"; - } - - var fontsize = this.properties["fontsize"]; - - ctx.textAlign = this.properties["align"]; - ctx.font = fontsize.toString() + "px " + this.properties["font"]; - this.str = - typeof v == "number" ? v.toFixed(this.properties["decimals"]) : v; - - if (typeof this.str == "string") { - var lines = this.str.split("\\n"); - for (var i in lines) { - ctx.fillText( - lines[i], - this.properties["align"] == "left" ? 15 : this.size[0] - 15, - fontsize * -0.15 + fontsize * (parseInt(i) + 1) - ); - } - } - - ctx.shadowColor = "transparent"; - this.last_ctx = ctx; - ctx.textAlign = "left"; - }; - - WidgetText.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v != null) { - this.properties["value"] = v; - } - //this.setDirtyCanvas(true); - }; - - WidgetText.prototype.resize = function() { - if (!this.last_ctx) { - return; - } - - var lines = this.str.split("\\n"); - this.last_ctx.font = - this.properties["fontsize"] + "px " + this.properties["font"]; - var max = 0; - for (var i in lines) { - var w = this.last_ctx.measureText(lines[i]).width; - if (max < w) { - max = w; - } - } - this.size[0] = max + 20; - this.size[1] = 4 + lines.length * this.properties["fontsize"]; - - this.setDirtyCanvas(true); - }; - - WidgetText.prototype.onPropertyChanged = function(name, value) { - this.properties[name] = value; - this.str = typeof value == "number" ? value.toFixed(3) : value; - //this.resize(); - return true; - }; - - LiteGraph.registerNodeType("widget/text", WidgetText); - - function WidgetPanel() { - this.size = [200, 100]; - this.properties = { - borderColor: "#ffffff", - bgcolorTop: "#f0f0f0", - bgcolorBottom: "#e0e0e0", - shadowSize: 2, - borderRadius: 3 - }; - } - - WidgetPanel.title = "Panel"; - WidgetPanel.desc = "Non interactive panel"; - WidgetPanel.widgets = [{ name: "update", text: "Update", type: "button" }]; - - WidgetPanel.prototype.createGradient = function(ctx) { - if ( - this.properties["bgcolorTop"] == "" || - this.properties["bgcolorBottom"] == "" - ) { - this.lineargradient = 0; - return; - } - - this.lineargradient = ctx.createLinearGradient(0, 0, 0, this.size[1]); - this.lineargradient.addColorStop(0, this.properties["bgcolorTop"]); - this.lineargradient.addColorStop(1, this.properties["bgcolorBottom"]); - }; - - WidgetPanel.prototype.onDrawForeground = function(ctx) { - if (this.flags.collapsed) { - return; - } - - if (this.lineargradient == null) { - this.createGradient(ctx); - } - - if (!this.lineargradient) { - return; - } - - ctx.lineWidth = 1; - ctx.strokeStyle = this.properties["borderColor"]; - //ctx.fillStyle = "#ebebeb"; - ctx.fillStyle = this.lineargradient; - - if (this.properties["shadowSize"]) { - ctx.shadowColor = "#000"; - ctx.shadowOffsetX = 0; - ctx.shadowOffsetY = 0; - ctx.shadowBlur = this.properties["shadowSize"]; - } else { - ctx.shadowColor = "transparent"; - } - - ctx.roundRect( - 0, - 0, - this.size[0] - 1, - this.size[1] - 1, - this.properties["shadowSize"] - ); - ctx.fill(); - ctx.shadowColor = "transparent"; - ctx.stroke(); - }; - - LiteGraph.registerNodeType("widget/panel", WidgetPanel); -})(this); - + +//widgets +(function(global) { + var LiteGraph = global.LiteGraph; + + /* Button ****************/ + + function WidgetButton() { + this.addOutput("", LiteGraph.EVENT); + this.addOutput("", "boolean"); + this.addProperty("text", "click me"); + this.addProperty("font_size", 30); + this.addProperty("message", ""); + this.size = [164, 84]; + this.clicked = false; + } + + WidgetButton.title = "Button"; + WidgetButton.desc = "Triggers an event"; + + WidgetButton.font = "Arial"; + WidgetButton.prototype.onDrawForeground = function(ctx) { + if (this.flags.collapsed) { + return; + } + var margin = 10; + ctx.fillStyle = "black"; + ctx.fillRect( + margin + 1, + margin + 1, + this.size[0] - margin * 2, + this.size[1] - margin * 2 + ); + ctx.fillStyle = "#AAF"; + ctx.fillRect( + margin - 1, + margin - 1, + this.size[0] - margin * 2, + this.size[1] - margin * 2 + ); + ctx.fillStyle = this.clicked + ? "white" + : this.mouseOver + ? "#668" + : "#334"; + ctx.fillRect( + margin, + margin, + this.size[0] - margin * 2, + this.size[1] - margin * 2 + ); + + if (this.properties.text || this.properties.text === 0) { + var font_size = this.properties.font_size || 30; + ctx.textAlign = "center"; + ctx.fillStyle = this.clicked ? "black" : "white"; + ctx.font = font_size + "px " + WidgetButton.font; + ctx.fillText( + this.properties.text, + this.size[0] * 0.5, + this.size[1] * 0.5 + font_size * 0.3 + ); + ctx.textAlign = "left"; + } + }; + + WidgetButton.prototype.onMouseDown = function(e, local_pos) { + if ( + local_pos[0] > 1 && + local_pos[1] > 1 && + local_pos[0] < this.size[0] - 2 && + local_pos[1] < this.size[1] - 2 + ) { + this.clicked = true; + this.triggerSlot(0, this.properties.message); + return true; + } + }; + + WidgetButton.prototype.onExecute = function() { + this.setOutputData(1, this.clicked); + }; + + WidgetButton.prototype.onMouseUp = function(e) { + this.clicked = false; + }; + + LiteGraph.registerNodeType("widget/button", WidgetButton); + + function WidgetToggle() { + this.addInput("", "boolean"); + this.addInput("e", LiteGraph.ACTION); + this.addOutput("v", "boolean"); + this.addOutput("e", LiteGraph.EVENT); + this.properties = { font: "", value: false }; + this.size = [160, 44]; + } + + WidgetToggle.title = "Toggle"; + WidgetToggle.desc = "Toggles between true or false"; + + WidgetToggle.prototype.onDrawForeground = function(ctx) { + if (this.flags.collapsed) { + return; + } + + var size = this.size[1] * 0.5; + var margin = 0.25; + var h = this.size[1] * 0.8; + ctx.font = this.properties.font || (size * 0.8).toFixed(0) + "px Arial"; + var w = ctx.measureText(this.title).width; + var x = (this.size[0] - (w + size)) * 0.5; + + ctx.fillStyle = "#AAA"; + ctx.fillRect(x, h - size, size, size); + + ctx.fillStyle = this.properties.value ? "#AEF" : "#000"; + ctx.fillRect( + x + size * margin, + h - size + size * margin, + size * (1 - margin * 2), + size * (1 - margin * 2) + ); + + ctx.textAlign = "left"; + ctx.fillStyle = "#AAA"; + ctx.fillText(this.title, size * 1.2 + x, h * 0.85); + ctx.textAlign = "left"; + }; + + WidgetToggle.prototype.onAction = function(action) { + this.properties.value = !this.properties.value; + this.trigger("e", this.properties.value); + }; + + WidgetToggle.prototype.onExecute = function() { + var v = this.getInputData(0); + if (v != null) { + this.properties.value = v; + } + this.setOutputData(0, this.properties.value); + }; + + WidgetToggle.prototype.onMouseDown = function(e, local_pos) { + if ( + local_pos[0] > 1 && + local_pos[1] > 1 && + local_pos[0] < this.size[0] - 2 && + local_pos[1] < this.size[1] - 2 + ) { + this.properties.value = !this.properties.value; + this.graph._version++; + this.trigger("e", this.properties.value); + return true; + } + }; + + LiteGraph.registerNodeType("widget/toggle", WidgetToggle); + + /* Number ****************/ + + function WidgetNumber() { + this.addOutput("", "number"); + this.size = [80, 60]; + this.properties = { min: -1000, max: 1000, value: 1, step: 1 }; + this.old_y = -1; + this._remainder = 0; + this._precision = 0; + this.mouse_captured = false; + } + + WidgetNumber.title = "Number"; + WidgetNumber.desc = "Widget to select number value"; + + WidgetNumber.pixels_threshold = 10; + WidgetNumber.markers_color = "#666"; + + WidgetNumber.prototype.onDrawForeground = function(ctx) { + var x = this.size[0] * 0.5; + var h = this.size[1]; + if (h > 30) { + ctx.fillStyle = WidgetNumber.markers_color; + ctx.beginPath(); + ctx.moveTo(x, h * 0.1); + ctx.lineTo(x + h * 0.1, h * 0.2); + ctx.lineTo(x + h * -0.1, h * 0.2); + ctx.fill(); + ctx.beginPath(); + ctx.moveTo(x, h * 0.9); + ctx.lineTo(x + h * 0.1, h * 0.8); + ctx.lineTo(x + h * -0.1, h * 0.8); + ctx.fill(); + ctx.font = (h * 0.7).toFixed(1) + "px Arial"; + } else { + ctx.font = (h * 0.8).toFixed(1) + "px Arial"; + } + + ctx.textAlign = "center"; + ctx.font = (h * 0.7).toFixed(1) + "px Arial"; + ctx.fillStyle = "#EEE"; + ctx.fillText( + this.properties.value.toFixed(this._precision), + x, + h * 0.75 + ); + }; + + WidgetNumber.prototype.onExecute = function() { + this.setOutputData(0, this.properties.value); + }; + + WidgetNumber.prototype.onPropertyChanged = function(name, value) { + var t = (this.properties.step + "").split("."); + this._precision = t.length > 1 ? t[1].length : 0; + }; + + WidgetNumber.prototype.onMouseDown = function(e, pos) { + if (pos[1] < 0) { + return; + } + + this.old_y = e.canvasY; + this.captureInput(true); + this.mouse_captured = true; + + return true; + }; + + WidgetNumber.prototype.onMouseMove = function(e) { + if (!this.mouse_captured) { + return; + } + + var delta = this.old_y - e.canvasY; + if (e.shiftKey) { + delta *= 10; + } + if (e.metaKey || e.altKey) { + delta *= 0.1; + } + this.old_y = e.canvasY; + + var steps = this._remainder + delta / WidgetNumber.pixels_threshold; + this._remainder = steps % 1; + steps = steps | 0; + + var v = Math.clamp( + this.properties.value + steps * this.properties.step, + this.properties.min, + this.properties.max + ); + this.properties.value = v; + this.graph._version++; + this.setDirtyCanvas(true); + }; + + WidgetNumber.prototype.onMouseUp = function(e, pos) { + if (e.click_time < 200) { + var steps = pos[1] > this.size[1] * 0.5 ? -1 : 1; + this.properties.value = Math.clamp( + this.properties.value + steps * this.properties.step, + this.properties.min, + this.properties.max + ); + this.graph._version++; + this.setDirtyCanvas(true); + } + + if (this.mouse_captured) { + this.mouse_captured = false; + this.captureInput(false); + } + }; + + LiteGraph.registerNodeType("widget/number", WidgetNumber); + + + /* Combo ****************/ + + function WidgetCombo() { + this.addOutput("", "string"); + this.addOutput("change", LiteGraph.EVENT); + this.size = [80, 60]; + this.properties = { value: "A", values:"A;B;C" }; + this.old_y = -1; + this.mouse_captured = false; + this._values = this.properties.values.split(";"); + var that = this; + this.widgets_up = true; + this.widget = this.addWidget("combo","", this.properties.value, function(v){ + that.properties.value = v; + that.triggerSlot(1, v); + }, { property: "value", values: this._values } ); + } + + WidgetCombo.title = "Combo"; + WidgetCombo.desc = "Widget to select from a list"; + + WidgetCombo.prototype.onExecute = function() { + this.setOutputData( 0, this.properties.value ); + }; + + WidgetCombo.prototype.onPropertyChanged = function(name, value) { + if(name == "values") + { + this._values = value.split(";"); + this.widget.options.values = this._values; + } + else if(name == "value") + { + this.widget.value = value; + } + }; + + LiteGraph.registerNodeType("widget/combo", WidgetCombo); + + + /* Knob ****************/ + + function WidgetKnob() { + this.addOutput("", "number"); + this.size = [64, 84]; + this.properties = { + min: 0, + max: 1, + value: 0.5, + color: "#7AF", + precision: 2 + }; + this.value = -1; + } + + WidgetKnob.title = "Knob"; + WidgetKnob.desc = "Circular controller"; + WidgetKnob.size = [80, 100]; + + WidgetKnob.prototype.onDrawForeground = function(ctx) { + if (this.flags.collapsed) { + return; + } + + if (this.value == -1) { + this.value = + (this.properties.value - this.properties.min) / + (this.properties.max - this.properties.min); + } + + var center_x = this.size[0] * 0.5; + var center_y = this.size[1] * 0.5; + var radius = Math.min(this.size[0], this.size[1]) * 0.5 - 5; + var w = Math.floor(radius * 0.05); + + ctx.globalAlpha = 1; + ctx.save(); + ctx.translate(center_x, center_y); + ctx.rotate(Math.PI * 0.75); + + //bg + ctx.fillStyle = "rgba(0,0,0,0.5)"; + ctx.beginPath(); + ctx.moveTo(0, 0); + ctx.arc(0, 0, radius, 0, Math.PI * 1.5); + ctx.fill(); + + //value + ctx.strokeStyle = "black"; + ctx.fillStyle = this.properties.color; + ctx.lineWidth = 2; + ctx.beginPath(); + ctx.moveTo(0, 0); + ctx.arc( + 0, + 0, + radius - 4, + 0, + Math.PI * 1.5 * Math.max(0.01, this.value) + ); + ctx.closePath(); + ctx.fill(); + //ctx.stroke(); + ctx.lineWidth = 1; + ctx.globalAlpha = 1; + ctx.restore(); + + //inner + ctx.fillStyle = "black"; + ctx.beginPath(); + ctx.arc(center_x, center_y, radius * 0.75, 0, Math.PI * 2, true); + ctx.fill(); + + //miniball + ctx.fillStyle = this.mouseOver ? "white" : this.properties.color; + ctx.beginPath(); + var angle = this.value * Math.PI * 1.5 + Math.PI * 0.75; + ctx.arc( + center_x + Math.cos(angle) * radius * 0.65, + center_y + Math.sin(angle) * radius * 0.65, + radius * 0.05, + 0, + Math.PI * 2, + true + ); + ctx.fill(); + + //text + ctx.fillStyle = this.mouseOver ? "white" : "#AAA"; + ctx.font = Math.floor(radius * 0.5) + "px Arial"; + ctx.textAlign = "center"; + ctx.fillText( + this.properties.value.toFixed(this.properties.precision), + center_x, + center_y + radius * 0.15 + ); + }; + + WidgetKnob.prototype.onExecute = function() { + this.setOutputData(0, this.properties.value); + this.boxcolor = LiteGraph.colorToString([ + this.value, + this.value, + this.value + ]); + }; + + WidgetKnob.prototype.onMouseDown = function(e) { + this.center = [this.size[0] * 0.5, this.size[1] * 0.5 + 20]; + this.radius = this.size[0] * 0.5; + if ( + e.canvasY - this.pos[1] < 20 || + LiteGraph.distance( + [e.canvasX, e.canvasY], + [this.pos[0] + this.center[0], this.pos[1] + this.center[1]] + ) > this.radius + ) { + return false; + } + this.oldmouse = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; + this.captureInput(true); + return true; + }; + + WidgetKnob.prototype.onMouseMove = function(e) { + if (!this.oldmouse) { + return; + } + + var m = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; + + var v = this.value; + v -= (m[1] - this.oldmouse[1]) * 0.01; + if (v > 1.0) { + v = 1.0; + } else if (v < 0.0) { + v = 0.0; + } + this.value = v; + this.properties.value = + this.properties.min + + (this.properties.max - this.properties.min) * this.value; + this.oldmouse = m; + this.setDirtyCanvas(true); + }; + + WidgetKnob.prototype.onMouseUp = function(e) { + if (this.oldmouse) { + this.oldmouse = null; + this.captureInput(false); + } + }; + + WidgetKnob.prototype.onPropertyChanged = function(name, value) { + if (name == "min" || name == "max" || name == "value") { + this.properties[name] = parseFloat(value); + return true; //block + } + }; + + LiteGraph.registerNodeType("widget/knob", WidgetKnob); + + //Show value inside the debug console + function WidgetSliderGUI() { + this.addOutput("", "number"); + this.properties = { + value: 0.5, + min: 0, + max: 1, + text: "V" + }; + var that = this; + this.size = [140, 40]; + this.slider = this.addWidget( + "slider", + "V", + this.properties.value, + function(v) { + that.properties.value = v; + }, + this.properties + ); + this.widgets_up = true; + } + + WidgetSliderGUI.title = "Inner Slider"; + + WidgetSliderGUI.prototype.onPropertyChanged = function(name, value) { + if (name == "value") { + this.slider.value = value; + } + }; + + WidgetSliderGUI.prototype.onExecute = function() { + this.setOutputData(0, this.properties.value); + }; + + LiteGraph.registerNodeType("widget/internal_slider", WidgetSliderGUI); + + //Widget H SLIDER + function WidgetHSlider() { + this.size = [160, 26]; + this.addOutput("", "number"); + this.properties = { color: "#7AF", min: 0, max: 1, value: 0.5 }; + this.value = -1; + } + + WidgetHSlider.title = "H.Slider"; + WidgetHSlider.desc = "Linear slider controller"; + + WidgetHSlider.prototype.onDrawForeground = function(ctx) { + if (this.value == -1) { + this.value = + (this.properties.value - this.properties.min) / + (this.properties.max - this.properties.min); + } + + //border + ctx.globalAlpha = 1; + ctx.lineWidth = 1; + ctx.fillStyle = "#000"; + ctx.fillRect(2, 2, this.size[0] - 4, this.size[1] - 4); + + ctx.fillStyle = this.properties.color; + ctx.beginPath(); + ctx.rect(4, 4, (this.size[0] - 8) * this.value, this.size[1] - 8); + ctx.fill(); + }; + + WidgetHSlider.prototype.onExecute = function() { + this.properties.value = + this.properties.min + + (this.properties.max - this.properties.min) * this.value; + this.setOutputData(0, this.properties.value); + this.boxcolor = LiteGraph.colorToString([ + this.value, + this.value, + this.value + ]); + }; + + WidgetHSlider.prototype.onMouseDown = function(e) { + if (e.canvasY - this.pos[1] < 0) { + return false; + } + + this.oldmouse = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; + this.captureInput(true); + return true; + }; + + WidgetHSlider.prototype.onMouseMove = function(e) { + if (!this.oldmouse) { + return; + } + + var m = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; + + var v = this.value; + var delta = m[0] - this.oldmouse[0]; + v += delta / this.size[0]; + if (v > 1.0) { + v = 1.0; + } else if (v < 0.0) { + v = 0.0; + } + + this.value = v; + + this.oldmouse = m; + this.setDirtyCanvas(true); + }; + + WidgetHSlider.prototype.onMouseUp = function(e) { + this.oldmouse = null; + this.captureInput(false); + }; + + WidgetHSlider.prototype.onMouseLeave = function(e) { + //this.oldmouse = null; + }; + + LiteGraph.registerNodeType("widget/hslider", WidgetHSlider); + + function WidgetProgress() { + this.size = [160, 26]; + this.addInput("", "number"); + this.properties = { min: 0, max: 1, value: 0, color: "#AAF" }; + } + + WidgetProgress.title = "Progress"; + WidgetProgress.desc = "Shows data in linear progress"; + + WidgetProgress.prototype.onExecute = function() { + var v = this.getInputData(0); + if (v != undefined) { + this.properties["value"] = v; + } + }; + + WidgetProgress.prototype.onDrawForeground = function(ctx) { + //border + ctx.lineWidth = 1; + ctx.fillStyle = this.properties.color; + var v = + (this.properties.value - this.properties.min) / + (this.properties.max - this.properties.min); + v = Math.min(1, v); + v = Math.max(0, v); + ctx.fillRect(2, 2, (this.size[0] - 4) * v, this.size[1] - 4); + }; + + LiteGraph.registerNodeType("widget/progress", WidgetProgress); + + function WidgetText() { + this.addInputs("", 0); + this.properties = { + value: "...", + font: "Arial", + fontsize: 18, + color: "#AAA", + align: "left", + glowSize: 0, + decimals: 1 + }; + } + + WidgetText.title = "Text"; + WidgetText.desc = "Shows the input value"; + WidgetText.widgets = [ + { name: "resize", text: "Resize box", type: "button" }, + { name: "led_text", text: "LED", type: "minibutton" }, + { name: "normal_text", text: "Normal", type: "minibutton" } + ]; + + WidgetText.prototype.onDrawForeground = function(ctx) { + //ctx.fillStyle="#000"; + //ctx.fillRect(0,0,100,60); + ctx.fillStyle = this.properties["color"]; + var v = this.properties["value"]; + + if (this.properties["glowSize"]) { + ctx.shadowColor = this.properties.color; + ctx.shadowOffsetX = 0; + ctx.shadowOffsetY = 0; + ctx.shadowBlur = this.properties["glowSize"]; + } else { + ctx.shadowColor = "transparent"; + } + + var fontsize = this.properties["fontsize"]; + + ctx.textAlign = this.properties["align"]; + ctx.font = fontsize.toString() + "px " + this.properties["font"]; + this.str = + typeof v == "number" ? v.toFixed(this.properties["decimals"]) : v; + + if (typeof this.str == "string") { + var lines = this.str.split("\\n"); + for (var i in lines) { + ctx.fillText( + lines[i], + this.properties["align"] == "left" ? 15 : this.size[0] - 15, + fontsize * -0.15 + fontsize * (parseInt(i) + 1) + ); + } + } + + ctx.shadowColor = "transparent"; + this.last_ctx = ctx; + ctx.textAlign = "left"; + }; + + WidgetText.prototype.onExecute = function() { + var v = this.getInputData(0); + if (v != null) { + this.properties["value"] = v; + } + //this.setDirtyCanvas(true); + }; + + WidgetText.prototype.resize = function() { + if (!this.last_ctx) { + return; + } + + var lines = this.str.split("\\n"); + this.last_ctx.font = + this.properties["fontsize"] + "px " + this.properties["font"]; + var max = 0; + for (var i in lines) { + var w = this.last_ctx.measureText(lines[i]).width; + if (max < w) { + max = w; + } + } + this.size[0] = max + 20; + this.size[1] = 4 + lines.length * this.properties["fontsize"]; + + this.setDirtyCanvas(true); + }; + + WidgetText.prototype.onPropertyChanged = function(name, value) { + this.properties[name] = value; + this.str = typeof value == "number" ? value.toFixed(3) : value; + //this.resize(); + return true; + }; + + LiteGraph.registerNodeType("widget/text", WidgetText); + + function WidgetPanel() { + this.size = [200, 100]; + this.properties = { + borderColor: "#ffffff", + bgcolorTop: "#f0f0f0", + bgcolorBottom: "#e0e0e0", + shadowSize: 2, + borderRadius: 3 + }; + } + + WidgetPanel.title = "Panel"; + WidgetPanel.desc = "Non interactive panel"; + WidgetPanel.widgets = [{ name: "update", text: "Update", type: "button" }]; + + WidgetPanel.prototype.createGradient = function(ctx) { + if ( + this.properties["bgcolorTop"] == "" || + this.properties["bgcolorBottom"] == "" + ) { + this.lineargradient = 0; + return; + } + + this.lineargradient = ctx.createLinearGradient(0, 0, 0, this.size[1]); + this.lineargradient.addColorStop(0, this.properties["bgcolorTop"]); + this.lineargradient.addColorStop(1, this.properties["bgcolorBottom"]); + }; + + WidgetPanel.prototype.onDrawForeground = function(ctx) { + if (this.flags.collapsed) { + return; + } + + if (this.lineargradient == null) { + this.createGradient(ctx); + } + + if (!this.lineargradient) { + return; + } + + ctx.lineWidth = 1; + ctx.strokeStyle = this.properties["borderColor"]; + //ctx.fillStyle = "#ebebeb"; + ctx.fillStyle = this.lineargradient; + + if (this.properties["shadowSize"]) { + ctx.shadowColor = "#000"; + ctx.shadowOffsetX = 0; + ctx.shadowOffsetY = 0; + ctx.shadowBlur = this.properties["shadowSize"]; + } else { + ctx.shadowColor = "transparent"; + } + + ctx.roundRect( + 0, + 0, + this.size[0] - 1, + this.size[1] - 1, + this.properties["shadowSize"] + ); + ctx.fill(); + ctx.shadowColor = "transparent"; + ctx.stroke(); + }; + + LiteGraph.registerNodeType("widget/panel", WidgetPanel); +})(this); + (function(global) { var LiteGraph = global.LiteGraph; @@ -13728,7 +13746,7 @@ if (typeof exports != "undefined") { LiteGraph.registerNodeType("input/gamepad", GamepadInput); })(this); - + (function(global) { var LiteGraph = global.LiteGraph; @@ -14992,8713 +15010,5256 @@ if (typeof exports != "undefined") { LiteGraph.registerNodeType("math3d/xyzw-to-vec4", Math3DXYZWToVec4); })(this); - + (function(global) { var LiteGraph = global.LiteGraph; + function Selector() { + this.addInput("sel", "number"); + this.addInput("A"); + this.addInput("B"); + this.addInput("C"); + this.addInput("D"); + this.addOutput("out"); - function Math3DMat4() - { - this.addInput("T", "vec3"); - this.addInput("R", "vec3"); - this.addInput("S", "vec3"); - this.addOutput("mat4", "mat4"); - this.properties = { - "T":[0,0,0], - "R":[0,0,0], - "S":[1,1,1], - R_in_degrees: true - }; - this._result = mat4.create(); - this._must_update = true; - } - - Math3DMat4.title = "mat4"; - Math3DMat4.temp_quat = new Float32Array([0,0,0,1]); - Math3DMat4.temp_mat4 = new Float32Array(16); - Math3DMat4.temp_vec3 = new Float32Array(3); - - Math3DMat4.prototype.onPropertyChanged = function(name, value) - { - this._must_update = true; - } - - Math3DMat4.prototype.onExecute = function() - { - var M = this._result; - var Q = Math3DMat4.temp_quat; - var temp_mat4 = Math3DMat4.temp_mat4; - var temp_vec3 = Math3DMat4.temp_vec3; - - var T = this.getInputData(0); - var R = this.getInputData(1); - var S = this.getInputData(2); - - if( this._must_update || T || R || S ) - { - T = T || this.properties.T; - R = R || this.properties.R; - S = S || this.properties.S; - mat4.identity( M ); - mat4.translate( M, M, T ); - if(this.properties.R_in_degrees) - { - temp_vec3.set( R ); - vec3.scale(temp_vec3,temp_vec3,DEG2RAD); - quat.fromEuler( Q, temp_vec3 ); - } - else - quat.fromEuler( Q, R ); - mat4.fromQuat( temp_mat4, Q ); - mat4.multiply( M, M, temp_mat4 ); - mat4.scale( M, M, S ); - } - - this.setOutputData(0, M); - } - - LiteGraph.registerNodeType("math3d/mat4", Math3DMat4); - - //Math 3D operation - function Math3DOperation() { - this.addInput("A", "number,vec3"); - this.addInput("B", "number,vec3"); - this.addOutput("=", "vec3"); - this.addProperty("OP", "+", "enum", { values: Math3DOperation.values }); - this._result = vec3.create(); + this.selected = 0; } - Math3DOperation.values = ["+", "-", "*", "/", "%", "^", "max", "min"]; + Selector.title = "Selector"; + Selector.desc = "selects an output"; - Math3DOperation.title = "Operation"; - Math3DOperation.desc = "Easy math 3D operators"; - Math3DOperation["@OP"] = { - type: "enum", - title: "operation", - values: Math3DOperation.values - }; - Math3DOperation.size = [100, 60]; - - Math3DOperation.prototype.getTitle = function() { - if(this.properties.OP == "max" || this.properties.OP == "min" ) - return this.properties.OP + "(A,B)"; - return "A " + this.properties.OP + " B"; - }; - - Math3DOperation.prototype.onExecute = function() { - var A = this.getInputData(0); - var B = this.getInputData(1); - if(A == null || B == null) - return; - if(A.constructor === Number) - A = [A,A,A]; - if(B.constructor === Number) - B = [B,B,B]; - - var result = this._result; - switch (this.properties.OP) { - case "+": - result = vec3.add(result,A,B); - break; - case "-": - result = vec3.sub(result,A,B); - break; - case "x": - case "X": - case "*": - result = vec3.mul(result,A,B); - break; - case "/": - result = vec3.div(result,A,B); - break; - case "%": - result[0] = A[0]%B[0]; - result[1] = A[1]%B[1]; - result[2] = A[2]%B[2]; - break; - case "^": - result[0] = Math.pow(A[0],B[0]); - result[1] = Math.pow(A[1],B[1]); - result[2] = Math.pow(A[2],B[2]); - break; - case "max": - result[0] = Math.max(A[0],B[0]); - result[1] = Math.max(A[1],B[1]); - result[2] = Math.max(A[2],B[2]); - break; - case "min": - result[0] = Math.min(A[0],B[0]); - result[1] = Math.min(A[1],B[1]); - result[2] = Math.min(A[2],B[2]); - break; - default: - console.warn("Unknown operation: " + this.properties.OP); - } - this.setOutputData(0, result); - }; - - Math3DOperation.prototype.onDrawBackground = function(ctx) { + Selector.prototype.onDrawBackground = function(ctx) { if (this.flags.collapsed) { return; } - - ctx.font = "40px Arial"; - ctx.fillStyle = "#666"; - ctx.textAlign = "center"; - ctx.fillText( - this.properties.OP, - this.size[0] * 0.5, - (this.size[1] + LiteGraph.NODE_TITLE_HEIGHT) * 0.5 - ); - ctx.textAlign = "left"; + ctx.fillStyle = "#AFB"; + var y = (this.selected + 1) * LiteGraph.NODE_SLOT_HEIGHT + 6; + ctx.beginPath(); + ctx.moveTo(50, y); + ctx.lineTo(50, y + LiteGraph.NODE_SLOT_HEIGHT); + ctx.lineTo(34, y + LiteGraph.NODE_SLOT_HEIGHT * 0.5); + ctx.fill(); }; - LiteGraph.registerNodeType("math3d/operation", Math3DOperation); + Selector.prototype.onExecute = function() { + var sel = this.getInputData(0); + if (sel == null || sel.constructor !== Number) + sel = 0; + this.selected = sel = Math.round(sel) % (this.inputs.length - 1); + var v = this.getInputData(sel + 1); + if (v !== undefined) { + this.setOutputData(0, v); + } + }; - function Math3DVec3Scale() { - this.addInput("in", "vec3"); - this.addInput("f", "number"); - this.addOutput("out", "vec3"); - this.properties = { f: 1 }; - this._data = new Float32Array(3); + Selector.prototype.onGetInputs = function() { + return [["E", 0], ["F", 0], ["G", 0], ["H", 0]]; + }; + + LiteGraph.registerNodeType("logic/selector", Selector); + + function Sequence() { + this.properties = { + sequence: "A,B,C" + }; + this.addInput("index", "number"); + this.addInput("seq"); + this.addOutput("out"); + + this.index = 0; + this.values = this.properties.sequence.split(","); } - Math3DVec3Scale.title = "vec3_scale"; - Math3DVec3Scale.desc = "scales the components of a vec3"; + Sequence.title = "Sequence"; + Sequence.desc = "select one element from a sequence from a string"; - Math3DVec3Scale.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - return; + Sequence.prototype.onPropertyChanged = function(name, value) { + if (name == "sequence") { + this.values = value.split(","); } - var f = this.getInputData(1); - if (f == null) { - f = this.properties.f; - } - - var data = this._data; - data[0] = v[0] * f; - data[1] = v[1] * f; - data[2] = v[2] * f; - this.setOutputData(0, data); }; - LiteGraph.registerNodeType("math3d/vec3-scale", Math3DVec3Scale); - - function Math3DVec3Length() { - this.addInput("in", "vec3"); - this.addOutput("out", "number"); - } - - Math3DVec3Length.title = "vec3_length"; - Math3DVec3Length.desc = "returns the module of a vector"; - - Math3DVec3Length.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - return; + Sequence.prototype.onExecute = function() { + var seq = this.getInputData(1); + if (seq && seq != this.current_sequence) { + this.values = seq.split(","); + this.current_sequence = seq; } - var dist = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); - this.setOutputData(0, dist); + var index = this.getInputData(0); + if (index == null) { + index = 0; + } + this.index = index = Math.round(index) % this.values.length; + + this.setOutputData(0, this.values[index]); }; - LiteGraph.registerNodeType("math3d/vec3-length", Math3DVec3Length); + LiteGraph.registerNodeType("logic/sequence", Sequence); +})(this); - function Math3DVec3Normalize() { - this.addInput("in", "vec3"); - this.addOutput("out", "vec3"); - this._data = new Float32Array(3); - } +(function(global) { + var LiteGraph = global.LiteGraph; - Math3DVec3Normalize.title = "vec3_normalize"; - Math3DVec3Normalize.desc = "returns the vector normalized"; + //Works with Litegl.js to create WebGL nodes + global.LGraphTexture = null; - Math3DVec3Normalize.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - return; - } - var dist = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); - var data = this._data; - data[0] = v[0] / dist; - data[1] = v[1] / dist; - data[2] = v[2] / dist; + if (typeof GL == "undefined") + return; - this.setOutputData(0, data); - }; + LGraphCanvas.link_type_colors["Texture"] = "#987"; - LiteGraph.registerNodeType("math3d/vec3-normalize", Math3DVec3Normalize); + function LGraphTexture() { + this.addOutput("tex", "Texture"); + this.addOutput("name", "string"); + this.properties = { name: "", filter: true }; + this.size = [ + LGraphTexture.image_preview_size, + LGraphTexture.image_preview_size + ]; + } - function Math3DVec3Lerp() { - this.addInput("A", "vec3"); - this.addInput("B", "vec3"); - this.addInput("f", "vec3"); - this.addOutput("out", "vec3"); - this.properties = { f: 0.5 }; - this._data = new Float32Array(3); - } + global.LGraphTexture = LGraphTexture; - Math3DVec3Lerp.title = "vec3_lerp"; - Math3DVec3Lerp.desc = "returns the interpolated vector"; + LGraphTexture.title = "Texture"; + LGraphTexture.desc = "Texture"; + LGraphTexture.widgets_info = { + name: { widget: "texture" }, + filter: { widget: "checkbox" } + }; - Math3DVec3Lerp.prototype.onExecute = function() { - var A = this.getInputData(0); - if (A == null) { - return; - } - var B = this.getInputData(1); - if (B == null) { - return; - } - var f = this.getInputOrProperty("f"); + //REPLACE THIS TO INTEGRATE WITH YOUR FRAMEWORK + LGraphTexture.loadTextureCallback = null; //function in charge of loading textures when not present in the container + LGraphTexture.image_preview_size = 256; - var data = this._data; - data[0] = A[0] * (1 - f) + B[0] * f; - data[1] = A[1] * (1 - f) + B[1] * f; - data[2] = A[2] * (1 - f) + B[2] * f; + //flags to choose output texture type + LGraphTexture.PASS_THROUGH = 1; //do not apply FX + LGraphTexture.COPY = 2; //create new texture with the same properties as the origin texture + LGraphTexture.LOW = 3; //create new texture with low precision (byte) + LGraphTexture.HIGH = 4; //create new texture with high precision (half-float) + LGraphTexture.REUSE = 5; //reuse input texture + LGraphTexture.DEFAULT = 2; - this.setOutputData(0, data); - }; + LGraphTexture.MODE_VALUES = { + "pass through": LGraphTexture.PASS_THROUGH, + copy: LGraphTexture.COPY, + low: LGraphTexture.LOW, + high: LGraphTexture.HIGH, + reuse: LGraphTexture.REUSE, + default: LGraphTexture.DEFAULT + }; - LiteGraph.registerNodeType("math3d/vec3-lerp", Math3DVec3Lerp); + //returns the container where all the loaded textures are stored (overwrite if you have a Resources Manager) + LGraphTexture.getTexturesContainer = function() { + return gl.textures; + }; - function Math3DVec3Dot() { - this.addInput("A", "vec3"); - this.addInput("B", "vec3"); - this.addOutput("out", "number"); - } + //process the loading of a texture (overwrite it if you have a Resources Manager) + LGraphTexture.loadTexture = function(name, options) { + options = options || {}; + var url = name; + if (url.substr(0, 7) == "http://") { + if (LiteGraph.proxy) { + //proxy external files + url = LiteGraph.proxy + url.substr(7); + } + } - Math3DVec3Dot.title = "vec3_dot"; - Math3DVec3Dot.desc = "returns the dot product"; + var container = LGraphTexture.getTexturesContainer(); + var tex = (container[name] = GL.Texture.fromURL(url, options)); + return tex; + }; - Math3DVec3Dot.prototype.onExecute = function() { - var A = this.getInputData(0); - if (A == null) { - return; - } - var B = this.getInputData(1); - if (B == null) { - return; - } + LGraphTexture.getTexture = function(name) { + var container = this.getTexturesContainer(); - var dot = A[0] * B[0] + A[1] * B[1] + A[2] * B[2]; - this.setOutputData(0, dot); - }; + if (!container) { + throw "Cannot load texture, container of textures not found"; + } - LiteGraph.registerNodeType("math3d/vec3-dot", Math3DVec3Dot); + var tex = container[name]; + if (!tex && name && name[0] != ":") { + return this.loadTexture(name); + } - //if glMatrix is installed... - if (global.glMatrix) { - function Math3DQuaternion() { - this.addOutput("quat", "quat"); - this.properties = { x: 0, y: 0, z: 0, w: 1, normalize: false }; - this._value = quat.create(); - } + return tex; + }; - Math3DQuaternion.title = "Quaternion"; - Math3DQuaternion.desc = "quaternion"; + //used to compute the appropiate output texture + LGraphTexture.getTargetTexture = function(origin, target, mode) { + if (!origin) { + throw "LGraphTexture.getTargetTexture expects a reference texture"; + } - Math3DQuaternion.prototype.onExecute = function() { - this._value[0] = this.getInputOrProperty("x"); - this._value[1] = this.getInputOrProperty("y"); - this._value[2] = this.getInputOrProperty("z"); - this._value[3] = this.getInputOrProperty("w"); - if (this.properties.normalize) { - quat.normalize(this._value, this._value); - } - this.setOutputData(0, this._value); - }; + var tex_type = null; - Math3DQuaternion.prototype.onGetInputs = function() { - return [ - ["x", "number"], - ["y", "number"], - ["z", "number"], - ["w", "number"] - ]; - }; + switch (mode) { + case LGraphTexture.LOW: + tex_type = gl.UNSIGNED_BYTE; + break; + case LGraphTexture.HIGH: + tex_type = gl.HIGH_PRECISION_FORMAT; + break; + case LGraphTexture.REUSE: + return origin; + break; + case LGraphTexture.COPY: + default: + tex_type = origin ? origin.type : gl.UNSIGNED_BYTE; + break; + } - LiteGraph.registerNodeType("math3d/quaternion", Math3DQuaternion); + if ( + !target || + target.width != origin.width || + target.height != origin.height || + target.type != tex_type + ) { + target = new GL.Texture(origin.width, origin.height, { + type: tex_type, + format: gl.RGBA, + filter: gl.LINEAR + }); + } - function Math3DRotation() { - this.addInputs([["degrees", "number"], ["axis", "vec3"]]); - this.addOutput("quat", "quat"); - this.properties = { angle: 90.0, axis: vec3.fromValues(0, 1, 0) }; + return target; + }; - this._value = quat.create(); - } + LGraphTexture.getTextureType = function(precision, ref_texture) { + var type = ref_texture ? ref_texture.type : gl.UNSIGNED_BYTE; + switch (precision) { + case LGraphTexture.HIGH: + type = gl.HIGH_PRECISION_FORMAT; + break; + case LGraphTexture.LOW: + type = gl.UNSIGNED_BYTE; + break; + //no default + } + return type; + }; - Math3DRotation.title = "Rotation"; - Math3DRotation.desc = "quaternion rotation"; + LGraphTexture.getWhiteTexture = function() { + if (this._white_texture) { + return this._white_texture; + } + var texture = (this._white_texture = GL.Texture.fromMemory( + 1, + 1, + [255, 255, 255, 255], + { format: gl.RGBA, wrap: gl.REPEAT, filter: gl.NEAREST } + )); + return texture; + }; - Math3DRotation.prototype.onExecute = function() { - var angle = this.getInputData(0); - if (angle == null) { - angle = this.properties.angle; - } - var axis = this.getInputData(1); - if (axis == null) { - axis = this.properties.axis; - } + LGraphTexture.getNoiseTexture = function() { + if (this._noise_texture) { + return this._noise_texture; + } - var R = quat.setAxisAngle(this._value, axis, angle * 0.0174532925); - this.setOutputData(0, R); - }; + var noise = new Uint8Array(512 * 512 * 4); + for (var i = 0; i < 512 * 512 * 4; ++i) { + noise[i] = Math.random() * 255; + } - LiteGraph.registerNodeType("math3d/rotation", Math3DRotation); + var texture = GL.Texture.fromMemory(512, 512, noise, { + format: gl.RGBA, + wrap: gl.REPEAT, + filter: gl.NEAREST + }); + this._noise_texture = texture; + return texture; + }; - //Math3D rotate vec3 - function Math3DRotateVec3() { - this.addInputs([["vec3", "vec3"], ["quat", "quat"]]); - this.addOutput("result", "vec3"); - this.properties = { vec: [0, 0, 1] }; - } - - Math3DRotateVec3.title = "Rot. Vec3"; - Math3DRotateVec3.desc = "rotate a point"; - - Math3DRotateVec3.prototype.onExecute = function() { - var vec = this.getInputData(0); - if (vec == null) { - vec = this.properties.vec; - } - var quat = this.getInputData(1); - if (quat == null) { - this.setOutputData(vec); - } else { - this.setOutputData( - 0, - vec3.transformQuat(vec3.create(), vec, quat) - ); - } - }; - - LiteGraph.registerNodeType("math3d/rotate_vec3", Math3DRotateVec3); - - function Math3DMultQuat() { - this.addInputs([["A", "quat"], ["B", "quat"]]); - this.addOutput("A*B", "quat"); - - this._value = quat.create(); - } - - Math3DMultQuat.title = "Mult. Quat"; - Math3DMultQuat.desc = "rotate quaternion"; - - Math3DMultQuat.prototype.onExecute = function() { - var A = this.getInputData(0); - if (A == null) { - return; - } - var B = this.getInputData(1); - if (B == null) { - return; - } - - var R = quat.multiply(this._value, A, B); - this.setOutputData(0, R); - }; - - LiteGraph.registerNodeType("math3d/mult-quat", Math3DMultQuat); - - function Math3DQuatSlerp() { - this.addInputs([ - ["A", "quat"], - ["B", "quat"], - ["factor", "number"] - ]); - this.addOutput("slerp", "quat"); - this.addProperty("factor", 0.5); - - this._value = quat.create(); - } - - Math3DQuatSlerp.title = "Quat Slerp"; - Math3DQuatSlerp.desc = "quaternion spherical interpolation"; - - Math3DQuatSlerp.prototype.onExecute = function() { - var A = this.getInputData(0); - if (A == null) { - return; - } - var B = this.getInputData(1); - if (B == null) { - return; - } - var factor = this.properties.factor; - if (this.getInputData(2) != null) { - factor = this.getInputData(2); - } - - var R = quat.slerp(this._value, A, B, factor); - this.setOutputData(0, R); - }; - - LiteGraph.registerNodeType("math3d/quat-slerp", Math3DQuatSlerp); - - - //Math3D rotate vec3 - function Math3DRemapRange() { - this.addInput("vec3", "vec3"); - this.addOutput("remap", "vec3"); - this.addOutput("clamped", "vec3"); - this.properties = { clamp: true, range_min: [-1, -1, 0], range_max: [1, 1, 0], target_min: [-1,-1,0], target_max:[1,1,0] }; - this._value = vec3.create(); - this._clamped = vec3.create(); - } - - Math3DRemapRange.title = "Remap Range"; - Math3DRemapRange.desc = "remap a 3D range"; - - Math3DRemapRange.prototype.onExecute = function() { - var vec = this.getInputData(0); - if(vec) - this._value.set(vec); - var range_min = this.properties.range_min; - var range_max = this.properties.range_max; - var target_min = this.properties.target_min; - var target_max = this.properties.target_max; - - //swap to avoid errors - /* - if(range_min > range_max) - { - range_min = range_max; - range_max = this.properties.range_min; + LGraphTexture.prototype.onDropFile = function(data, filename, file) { + if (!data) { + this._drop_texture = null; + this.properties.name = ""; + } else { + var texture = null; + if (typeof data == "string") { + texture = GL.Texture.fromURL(data); + } else if (filename.toLowerCase().indexOf(".dds") != -1) { + texture = GL.Texture.fromDDSInMemory(data); + } else { + var blob = new Blob([file]); + var url = URL.createObjectURL(blob); + texture = GL.Texture.fromURL(url); } - if(target_min > target_max) - { - target_min = target_max; - target_max = this.properties.target_min; - } - */ + this._drop_texture = texture; + this.properties.name = filename; + } + }; - for(var i = 0; i < 3; ++i) + LGraphTexture.prototype.getExtraMenuOptions = function(graphcanvas) { + var that = this; + if (!this._drop_texture) { + return; + } + return [ { - var r = range_max[i] - range_min[i]; - this._clamped[i] = Math.clamp( this._value[i], range_min[i], range_max[i] ); - if(r == 0) - { - this._value[i] = (target_min[i] + target_max[i]) * 0.5; + content: "Clear", + callback: function() { + that._drop_texture = null; + that.properties.name = ""; + } + } + ]; + }; + + LGraphTexture.prototype.onExecute = function() { + var tex = null; + if (this.isOutputConnected(1)) { + tex = this.getInputData(0); + } + + if (!tex && this._drop_texture) { + tex = this._drop_texture; + } + + if (!tex && this.properties.name) { + tex = LGraphTexture.getTexture(this.properties.name); + } + + if (!tex) { + this.setOutputData( 0, null ); + this.setOutputData( 1, "" ); + return; + } + + this._last_tex = tex; + + if (this.properties.filter === false) { + tex.setParameter(gl.TEXTURE_MAG_FILTER, gl.NEAREST); + } else { + tex.setParameter(gl.TEXTURE_MAG_FILTER, gl.LINEAR); + } + + this.setOutputData( 0, tex ); + this.setOutputData( 1, tex.fullpath || tex.filename ); + + for (var i = 2; i < this.outputs.length; i++) { + var output = this.outputs[i]; + if (!output) { + continue; + } + var v = null; + if (output.name == "width") { + v = tex.width; + } else if (output.name == "height") { + v = tex.height; + } else if (output.name == "aspect") { + v = tex.width / tex.height; + } + this.setOutputData(i, v); + } + }; + + LGraphTexture.prototype.onResourceRenamed = function( + old_name, + new_name + ) { + if (this.properties.name == old_name) { + this.properties.name = new_name; + } + }; + + LGraphTexture.prototype.onDrawBackground = function(ctx) { + if (this.flags.collapsed || this.size[1] <= 20) { + return; + } + + if (this._drop_texture && ctx.webgl) { + ctx.drawImage( + this._drop_texture, + 0, + 0, + this.size[0], + this.size[1] + ); + //this._drop_texture.renderQuad(this.pos[0],this.pos[1],this.size[0],this.size[1]); + return; + } + + //Different texture? then get it from the GPU + if (this._last_preview_tex != this._last_tex) { + if (ctx.webgl) { + this._canvas = this._last_tex; + } else { + var tex_canvas = LGraphTexture.generateLowResTexturePreview( + this._last_tex + ); + if (!tex_canvas) { + return; + } + + this._last_preview_tex = this._last_tex; + this._canvas = cloneCanvas(tex_canvas); + } + } + + if (!this._canvas) { + return; + } + + //render to graph canvas + ctx.save(); + if (!ctx.webgl) { + //reverse image + ctx.translate(0, this.size[1]); + ctx.scale(1, -1); + } + ctx.drawImage(this._canvas, 0, 0, this.size[0], this.size[1]); + ctx.restore(); + }; + + //very slow, used at your own risk + LGraphTexture.generateLowResTexturePreview = function(tex) { + if (!tex) { + return null; + } + + var size = LGraphTexture.image_preview_size; + var temp_tex = tex; + + if (tex.format == gl.DEPTH_COMPONENT) { + return null; + } //cannot generate from depth + + //Generate low-level version in the GPU to speed up + if (tex.width > size || tex.height > size) { + temp_tex = this._preview_temp_tex; + if (!this._preview_temp_tex) { + temp_tex = new GL.Texture(size, size, { + minFilter: gl.NEAREST + }); + this._preview_temp_tex = temp_tex; + } + + //copy + tex.copyTo(temp_tex); + tex = temp_tex; + } + + //create intermediate canvas with lowquality version + var tex_canvas = this._preview_canvas; + if (!tex_canvas) { + tex_canvas = createCanvas(size, size); + this._preview_canvas = tex_canvas; + } + + if (temp_tex) { + temp_tex.toCanvas(tex_canvas); + } + return tex_canvas; + }; + + LGraphTexture.prototype.getResources = function(res) { + if(this.properties.name) + res[this.properties.name] = GL.Texture; + return res; + }; + + LGraphTexture.prototype.onGetInputs = function() { + return [["in", "Texture"]]; + }; + + LGraphTexture.prototype.onGetOutputs = function() { + return [ + ["width", "number"], + ["height", "number"], + ["aspect", "number"] + ]; + }; + + //used to replace shader code + LGraphTexture.replaceCode = function( code, context ) + { + return code.replace(/\{\{[a-zA-Z0-9_]*\}\}/g, function(v){ + v = v.replace( /[\{\}]/g, "" ); + return context[v] || ""; + }); + } + + LiteGraph.registerNodeType("texture/texture", LGraphTexture); + + //************************** + function LGraphTexturePreview() { + this.addInput("Texture", "Texture"); + this.properties = { flipY: false }; + this.size = [ + LGraphTexture.image_preview_size, + LGraphTexture.image_preview_size + ]; + } + + LGraphTexturePreview.title = "Preview"; + LGraphTexturePreview.desc = "Show a texture in the graph canvas"; + LGraphTexturePreview.allow_preview = false; + + LGraphTexturePreview.prototype.onDrawBackground = function(ctx) { + if (this.flags.collapsed) { + return; + } + + if (!ctx.webgl && !LGraphTexturePreview.allow_preview) { + return; + } //not working well + + var tex = this.getInputData(0); + if (!tex) { + return; + } + + var tex_canvas = null; + + if (!tex.handle && ctx.webgl) { + tex_canvas = tex; + } else { + tex_canvas = LGraphTexture.generateLowResTexturePreview(tex); + } + + //render to graph canvas + ctx.save(); + if (this.properties.flipY) { + ctx.translate(0, this.size[1]); + ctx.scale(1, -1); + } + ctx.drawImage(tex_canvas, 0, 0, this.size[0], this.size[1]); + ctx.restore(); + }; + + LiteGraph.registerNodeType("texture/preview", LGraphTexturePreview); + + //************************************** + + function LGraphTextureSave() { + this.addInput("Texture", "Texture"); + this.addOutput("tex", "Texture"); + this.addOutput("name", "string"); + this.properties = { name: "", generate_mipmaps: false }; + } + + LGraphTextureSave.title = "Save"; + LGraphTextureSave.desc = "Save a texture in the repository"; + + LGraphTextureSave.prototype.getPreviewTexture = function() + { + return this._texture; + } + + LGraphTextureSave.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (this.properties.generate_mipmaps) { + tex.bind(0); + tex.setParameter( gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR ); + gl.generateMipmap(tex.texture_type); + tex.unbind(0); + } + + if (this.properties.name) { + //for cases where we want to perform something when storing it + if (LGraphTexture.storeTexture) { + LGraphTexture.storeTexture(this.properties.name, tex); + } else { + var container = LGraphTexture.getTexturesContainer(); + container[this.properties.name] = tex; + } + } + + this._texture = tex; + this.setOutputData(0, tex); + this.setOutputData(1, this.properties.name); + }; + + LiteGraph.registerNodeType("texture/save", LGraphTextureSave); + + //**************************************************** + + function LGraphTextureOperation() { + this.addInput("Texture", "Texture"); + this.addInput("TextureB", "Texture"); + this.addInput("value", "number"); + this.addOutput("Texture", "Texture"); + this.help = "

pixelcode must be vec3, uvcode must be vec2, is optional

\ +

uv: tex. coords

color: texture colorB: textureB

time: scene time value: input value

For multiline you must type: result = ...

"; + + this.properties = { + value: 1, + pixelcode: "color + colorB * value", + uvcode: "", + precision: LGraphTexture.DEFAULT + }; + + this.has_error = false; + } + + LGraphTextureOperation.widgets_info = { + uvcode: { widget: "code" }, + pixelcode: { widget: "code" }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureOperation.title = "Operation"; + LGraphTextureOperation.desc = "Texture shader operation"; + + LGraphTextureOperation.presets = {}; + + LGraphTextureOperation.prototype.getExtraMenuOptions = function( + graphcanvas + ) { + var that = this; + var txt = !that.properties.show ? "Show Texture" : "Hide Texture"; + return [ + { + content: txt, + callback: function() { + that.properties.show = !that.properties.show; + } + } + ]; + }; + + LGraphTextureOperation.prototype.onPropertyChanged = function() + { + this.has_error = false; + } + + LGraphTextureOperation.prototype.onDrawBackground = function(ctx) { + if ( + this.flags.collapsed || + this.size[1] <= 20 || + !this.properties.show + ) { + return; + } + + if (!this._tex) { + return; + } + + //only works if using a webgl renderer + if (this._tex.gl != ctx) { + return; + } + + //render to graph canvas + ctx.save(); + ctx.drawImage(this._tex, 0, 0, this.size[0], this.size[1]); + ctx.restore(); + }; + + LGraphTextureOperation.prototype.onExecute = function() { + var tex = this.getInputData(0); + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, tex); + return; + } + + var texB = this.getInputData(1); + + if (!this.properties.uvcode && !this.properties.pixelcode) { + return; + } + + var width = 512; + var height = 512; + if (tex) { + width = tex.width; + height = tex.height; + } else if (texB) { + width = texB.width; + height = texB.height; + } + + if(!texB) + texB = GL.Texture.getWhiteTexture(); + + var type = LGraphTexture.getTextureType( this.properties.precision, tex ); + + if (!tex && !this._tex) { + this._tex = new GL.Texture(width, height, { type: type, format: gl.RGBA, filter: gl.LINEAR }); + } else { + this._tex = LGraphTexture.getTargetTexture( tex || this._tex, this._tex, this.properties.precision ); + } + + var uvcode = ""; + if (this.properties.uvcode) { + uvcode = "uv = " + this.properties.uvcode; + if (this.properties.uvcode.indexOf(";") != -1) { + //there are line breaks, means multiline code + uvcode = this.properties.uvcode; + } + } + + var pixelcode = ""; + if (this.properties.pixelcode) { + pixelcode = "result = " + this.properties.pixelcode; + if (this.properties.pixelcode.indexOf(";") != -1) { + //there are line breaks, means multiline code + pixelcode = this.properties.pixelcode; + } + } + + var shader = this._shader; + + if ( !this.has_error && (!shader || this._shader_code != uvcode + "|" + pixelcode) ) { + + var final_pixel_code = LGraphTexture.replaceCode( LGraphTextureOperation.pixel_shader, { UV_CODE:uvcode, PIXEL_CODE:pixelcode }); + + try { + shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, final_pixel_code ); + this.boxcolor = "#00FF00"; + } catch (err) { + //console.log("Error compiling shader: ", err, final_pixel_code ); + GL.Shader.dumpErrorToConsole(err,Shader.SCREEN_VERTEX_SHADER, final_pixel_code); + this.boxcolor = "#FF0000"; + this.has_error = true; + return; + } + this._shader = shader; + this._shader_code = uvcode + "|" + pixelcode; + } + + if(!this._shader) + return; + + var value = this.getInputData(2); + if (value != null) { + this.properties.value = value; + } else { + value = parseFloat(this.properties.value); + } + + var time = this.graph.getTime(); + + this._tex.drawTo(function() { + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + gl.disable(gl.BLEND); + if (tex) { + tex.bind(0); + } + if (texB) { + texB.bind(1); + } + var mesh = Mesh.getScreenQuad(); + shader + .uniforms({ + u_texture: 0, + u_textureB: 1, + value: value, + texSize: [width, height], + time: time + }) + .draw(mesh); + }); + + this.setOutputData(0, this._tex); + }; + + LGraphTextureOperation.pixel_shader = + "precision highp float;\n\ + \n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_textureB;\n\ + varying vec2 v_coord;\n\ + uniform vec2 texSize;\n\ + uniform float time;\n\ + uniform float value;\n\ + \n\ + void main() {\n\ + vec2 uv = v_coord;\n\ + {{UV_CODE}};\n\ + vec4 color4 = texture2D(u_texture, uv);\n\ + vec3 color = color4.rgb;\n\ + vec4 color4B = texture2D(u_textureB, uv);\n\ + vec3 colorB = color4B.rgb;\n\ + vec3 result = color;\n\ + float alpha = 1.0;\n\ + {{PIXEL_CODE}};\n\ + gl_FragColor = vec4(result, alpha);\n\ + }\n\ + "; + + LGraphTextureOperation.registerPreset = function ( name, code ) + { + LGraphTextureOperation.presets[name] = code; + } + + LGraphTextureOperation.registerPreset("",""); + LGraphTextureOperation.registerPreset("bypass","color"); + LGraphTextureOperation.registerPreset("add","color + colorB * value"); + LGraphTextureOperation.registerPreset("substract","(color - colorB) * value"); + LGraphTextureOperation.registerPreset("mate","mix( color, colorB, color4B.a * value)"); + LGraphTextureOperation.registerPreset("invert","vec3(1.0) - color"); + LGraphTextureOperation.registerPreset("multiply","color * colorB * value"); + LGraphTextureOperation.registerPreset("divide","(color / colorB) / value"); + LGraphTextureOperation.registerPreset("difference","abs(color - colorB) * value"); + LGraphTextureOperation.registerPreset("max","max(color, colorB) * value"); + LGraphTextureOperation.registerPreset("min","min(color, colorB) * value"); + LGraphTextureOperation.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"); + LGraphTextureOperation.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"); + LGraphTextureOperation.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"); + LGraphTextureOperation.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"); + + //webglstudio stuff... + LGraphTextureOperation.prototype.onInspect = function(widgets) + { + var that = this; + widgets.addCombo("Presets","",{ values: Object.keys(LGraphTextureOperation.presets), callback: function(v){ + var code = LGraphTextureOperation.presets[v]; + if(!code) + return; + that.setProperty("pixelcode",code); + that.title = v; + widgets.refresh(); + }}); + } + + LiteGraph.registerNodeType("texture/operation", LGraphTextureOperation); + + //**************************************************** + + function LGraphTextureShader() { + this.addOutput("out", "Texture"); + this.properties = { + code: "", + u_value: 1, + u_color: [1,1,1,1], + width: 512, + height: 512, + precision: LGraphTexture.DEFAULT + }; + + this.properties.code = LGraphTextureShader.pixel_shader; + this._uniforms = { u_value: 1, u_color: vec4.create(), in_texture: 0, texSize: vec2.create(), time: 0 }; + } + + LGraphTextureShader.title = "Shader"; + LGraphTextureShader.desc = "Texture shader"; + LGraphTextureShader.widgets_info = { + code: { type: "code", lang: "glsl" }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureShader.prototype.onPropertyChanged = function( + name, + value + ) { + if (name != "code") { + return; + } + + var shader = this.getShader(); + if (!shader) { + return; + } + + //update connections + var uniforms = shader.uniformInfo; + + //remove deprecated slots + if (this.inputs) { + var already = {}; + for (var i = 0; i < this.inputs.length; ++i) { + var info = this.getInputInfo(i); + if (!info) { continue; } - var n = (this._value[i] - range_min[i]) / r; - if(this.properties.clamp) - n = Math.clamp(n,0,1); - var t = target_max[i] - target_min[i]; - this._value[i] = target_min[i] + n * t; + if (uniforms[info.name] && !already[info.name]) { + already[info.name] = true; + continue; + } + this.removeInput(i); + i--; + } + } + + //update existing ones + for (var i in uniforms) { + var info = shader.uniformInfo[i]; + if (info.loc === null) { + continue; + } //is an attribute, not a uniform + if (i == "time") { + //default one + continue; } - this.setOutputData(0,this._value); - this.setOutputData(1,this._clamped); - }; + var type = "number"; + if (this._shader.samplers[i]) { + type = "texture"; + } else { + switch (info.size) { + case 1: + type = "number"; + break; + case 2: + type = "vec2"; + break; + case 3: + type = "vec3"; + break; + case 4: + type = "vec4"; + break; + case 9: + type = "mat3"; + break; + case 16: + type = "mat4"; + break; + default: + continue; + } + } - LiteGraph.registerNodeType("math3d/remap_range", Math3DRemapRange); + var slot = this.findInputSlot(i); + if (slot == -1) { + this.addInput(i, type); + continue; + } - - - } //glMatrix - else - console.warn("No glmatrix found, some Math3D nodes may not work"); - -})(this); - -//basic nodes -(function(global) { - var LiteGraph = global.LiteGraph; - - function toString(a) { - return String(a); - } - - LiteGraph.wrapFunctionAsNode("string/toString", compare, ["*"], "String"); - - function compare(a, b) { - return a == b; - } - - LiteGraph.wrapFunctionAsNode( - "string/compare", - compare, - ["string", "string"], - "boolean" - ); - - function concatenate(a, b) { - if (a === undefined) { - return b; - } - if (b === undefined) { - return a; - } - return a + b; - } - - LiteGraph.wrapFunctionAsNode( - "string/concatenate", - concatenate, - ["string", "string"], - "string" - ); - - function contains(a, b) { - if (a === undefined || b === undefined) { - return false; - } - return a.indexOf(b) != -1; - } - - LiteGraph.wrapFunctionAsNode( - "string/contains", - contains, - ["string", "string"], - "boolean" - ); - - function toUpperCase(a) { - if (a != null && a.constructor === String) { - return a.toUpperCase(); - } - return a; - } - - LiteGraph.wrapFunctionAsNode( - "string/toUpperCase", - toUpperCase, - ["string"], - "string" - ); - - function split(str, separator) { - if(separator == null) - separator = this.properties.separator; - if (str == null ) - return []; - if( str.constructor === String ) - return str.split(separator || " "); - else if( str.constructor === Array ) - { - var r = []; - for(var i = 0; i < str.length; ++i) - r[i] = str[i].split(separator || " "); - return r; + var input_info = this.getInputInfo(slot); + if (!input_info) { + this.addInput(i, type); + } else { + if (input_info.type == type) { + continue; + } + this.removeInput(slot, type); + this.addInput(i, type); + } } - return null; - } + }; - LiteGraph.wrapFunctionAsNode( - "string/split", - split, - ["string,array", "string"], - "array", - { separator: "," } - ); + LGraphTextureShader.prototype.getShader = function() { + //replug + if (this._shader && this._shader_code == this.properties.code) { + return this._shader; + } - function toFixed(a) { - if (a != null && a.constructor === Number) { - return a.toFixed(this.properties.precision); - } - return a; - } + this._shader_code = this.properties.code; + this._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, this.properties.code ); + if (!this._shader) { + this.boxcolor = "red"; + return null; + } else { + this.boxcolor = "green"; + } + return this._shader; + }; - LiteGraph.wrapFunctionAsNode( - "string/toFixed", - toFixed, - ["number"], - "string", - { precision: 0 } - ); - - - function StringToTable() { - this.addInput("", "string"); - this.addOutput("table", "table"); - this.addOutput("rows", "number"); - this.addProperty("value", ""); - this.addProperty("separator", ","); - this._table = null; - } - - StringToTable.title = "toTable"; - StringToTable.desc = "Splits a string to table"; - - StringToTable.prototype.onExecute = function() { - var input = this.getInputData(0); - if(!input) + LGraphTextureShader.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { + return; + } //saves work + + var shader = this.getShader(); + if (!shader) { return; - var separator = this.properties.separator || ","; - if(input != this._str || separator != this._last_separator ) - { - this._last_separator = separator; - this._str = input; - this._table = input.split("\n").map(function(a){ return a.trim().split(separator)}); } - this.setOutputData(0, this._table ); - this.setOutputData(1, this._table ? this._table.length : 0 ); - }; - LiteGraph.registerNodeType("string/toTable", StringToTable); + var tex_slot = 0; + var in_tex = null; -})(this); - -(function(global) { - var LiteGraph = global.LiteGraph; - - function Selector() { - this.addInput("sel", "number"); - this.addInput("A"); - this.addInput("B"); - this.addInput("C"); - this.addInput("D"); - this.addOutput("out"); - - this.selected = 0; - } - - Selector.title = "Selector"; - Selector.desc = "selects an output"; - - Selector.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } - ctx.fillStyle = "#AFB"; - var y = (this.selected + 1) * LiteGraph.NODE_SLOT_HEIGHT + 6; - ctx.beginPath(); - ctx.moveTo(50, y); - ctx.lineTo(50, y + LiteGraph.NODE_SLOT_HEIGHT); - ctx.lineTo(34, y + LiteGraph.NODE_SLOT_HEIGHT * 0.5); - ctx.fill(); - }; - - Selector.prototype.onExecute = function() { - var sel = this.getInputData(0); - if (sel == null || sel.constructor !== Number) - sel = 0; - this.selected = sel = Math.round(sel) % (this.inputs.length - 1); - var v = this.getInputData(sel + 1); - if (v !== undefined) { - this.setOutputData(0, v); - } - }; - - Selector.prototype.onGetInputs = function() { - return [["E", 0], ["F", 0], ["G", 0], ["H", 0]]; - }; - - LiteGraph.registerNodeType("logic/selector", Selector); - - function Sequence() { - this.properties = { - sequence: "A,B,C" - }; - this.addInput("index", "number"); - this.addInput("seq"); - this.addOutput("out"); - - this.index = 0; - this.values = this.properties.sequence.split(","); - } - - Sequence.title = "Sequence"; - Sequence.desc = "select one element from a sequence from a string"; - - Sequence.prototype.onPropertyChanged = function(name, value) { - if (name == "sequence") { - this.values = value.split(","); - } - }; - - Sequence.prototype.onExecute = function() { - var seq = this.getInputData(1); - if (seq && seq != this.current_sequence) { - this.values = seq.split(","); - this.current_sequence = seq; - } - var index = this.getInputData(0); - if (index == null) { - index = 0; - } - this.index = index = Math.round(index) % this.values.length; - - this.setOutputData(0, this.values[index]); - }; - - LiteGraph.registerNodeType("logic/sequence", Sequence); -})(this); - -(function(global) { - var LiteGraph = global.LiteGraph; - - function GraphicsPlot() { - this.addInput("A", "Number"); - this.addInput("B", "Number"); - this.addInput("C", "Number"); - this.addInput("D", "Number"); - - this.values = [[], [], [], []]; - this.properties = { scale: 2 }; - } - - GraphicsPlot.title = "Plot"; - GraphicsPlot.desc = "Plots data over time"; - GraphicsPlot.colors = ["#FFF", "#F99", "#9F9", "#99F"]; - - GraphicsPlot.prototype.onExecute = function(ctx) { - if (this.flags.collapsed) { - return; - } - - var size = this.size; - - for (var i = 0; i < 4; ++i) { - var v = this.getInputData(i); - if (v == null) { - continue; - } - var values = this.values[i]; - values.push(v); - if (values.length > size[0]) { - values.shift(); - } - } - }; - - GraphicsPlot.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } - - var size = this.size; - - var scale = (0.5 * size[1]) / this.properties.scale; - var colors = GraphicsPlot.colors; - var offset = size[1] * 0.5; - - ctx.fillStyle = "#000"; - ctx.fillRect(0, 0, size[0], size[1]); - ctx.strokeStyle = "#555"; - ctx.beginPath(); - ctx.moveTo(0, offset); - ctx.lineTo(size[0], offset); - ctx.stroke(); - - if (this.inputs) { - for (var i = 0; i < 4; ++i) { - var values = this.values[i]; - if (!this.inputs[i] || !this.inputs[i].link) { - continue; - } - ctx.strokeStyle = colors[i]; - ctx.beginPath(); - var v = values[0] * scale * -1 + offset; - ctx.moveTo(0, Math.clamp(v, 0, size[1])); - for (var j = 1; j < values.length && j < size[0]; ++j) { - var v = values[j] * scale * -1 + offset; - ctx.lineTo(j, Math.clamp(v, 0, size[1])); - } - ctx.stroke(); - } - } - }; - - LiteGraph.registerNodeType("graphics/plot", GraphicsPlot); - - function GraphicsImage() { - this.addOutput("frame", "image"); - this.properties = { url: "" }; - } - - GraphicsImage.title = "Image"; - GraphicsImage.desc = "Image loader"; - GraphicsImage.widgets = [{ name: "load", text: "Load", type: "button" }]; - - GraphicsImage.supported_extensions = ["jpg", "jpeg", "png", "gif"]; - - GraphicsImage.prototype.onAdded = function() { - if (this.properties["url"] != "" && this.img == null) { - this.loadImage(this.properties["url"]); - } - }; - - GraphicsImage.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } - if (this.img && this.size[0] > 5 && this.size[1] > 5 && this.img.width) { - ctx.drawImage(this.img, 0, 0, this.size[0], this.size[1]); - } - }; - - GraphicsImage.prototype.onExecute = function() { - if (!this.img) { - this.boxcolor = "#000"; - } - if (this.img && this.img.width) { - this.setOutputData(0, this.img); - } else { - this.setOutputData(0, null); - } - if (this.img && this.img.dirty) { - this.img.dirty = false; - } - }; - - GraphicsImage.prototype.onPropertyChanged = function(name, value) { - this.properties[name] = value; - if (name == "url" && value != "") { - this.loadImage(value); - } - - return true; - }; - - GraphicsImage.prototype.loadImage = function(url, callback) { - if (url == "") { - this.img = null; - return; - } - - this.img = document.createElement("img"); - - if (url.substr(0, 4) == "http" && LiteGraph.proxy) { - url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); - } - - this.img.src = url; - this.boxcolor = "#F95"; - var that = this; - this.img.onload = function() { - if (callback) { - callback(this); - } - console.log( "Image loaded, size: " + that.img.width + "x" + that.img.height ); - this.dirty = true; - that.boxcolor = "#9F9"; - that.setDirtyCanvas(true); - }; - this.img.onerror = function() { - console.log("error loading the image:" + url); - } - }; - - GraphicsImage.prototype.onWidget = function(e, widget) { - if (widget.name == "load") { - this.loadImage(this.properties["url"]); - } - }; - - GraphicsImage.prototype.onDropFile = function(file) { - var that = this; - if (this._url) { - URL.revokeObjectURL(this._url); - } - this._url = URL.createObjectURL(file); - this.properties.url = this._url; - this.loadImage(this._url, function(img) { - that.size[1] = (img.height / img.width) * that.size[0]; - }); - }; - - LiteGraph.registerNodeType("graphics/image", GraphicsImage); - - function ColorPalette() { - this.addInput("f", "number"); - this.addOutput("Color", "color"); - this.properties = { - colorA: "#444444", - colorB: "#44AAFF", - colorC: "#44FFAA", - colorD: "#FFFFFF" - }; - } - - ColorPalette.title = "Palette"; - ColorPalette.desc = "Generates a color"; - - ColorPalette.prototype.onExecute = function() { - var c = []; - - if (this.properties.colorA != null) { - c.push(hex2num(this.properties.colorA)); - } - if (this.properties.colorB != null) { - c.push(hex2num(this.properties.colorB)); - } - if (this.properties.colorC != null) { - c.push(hex2num(this.properties.colorC)); - } - if (this.properties.colorD != null) { - c.push(hex2num(this.properties.colorD)); - } - - var f = this.getInputData(0); - if (f == null) { - f = 0.5; - } - if (f > 1.0) { - f = 1.0; - } else if (f < 0.0) { - f = 0.0; - } - - if (c.length == 0) { - return; - } - - var result = [0, 0, 0]; - if (f == 0) { - result = c[0]; - } else if (f == 1) { - result = c[c.length - 1]; - } else { - var pos = (c.length - 1) * f; - var c1 = c[Math.floor(pos)]; - var c2 = c[Math.floor(pos) + 1]; - var t = pos - Math.floor(pos); - result[0] = c1[0] * (1 - t) + c2[0] * t; - result[1] = c1[1] * (1 - t) + c2[1] * t; - result[2] = c1[2] * (1 - t) + c2[2] * t; - } - - /* - c[0] = 1.0 - Math.abs( Math.sin( 0.1 * reModular.getTime() * Math.PI) ); - c[1] = Math.abs( Math.sin( 0.07 * reModular.getTime() * Math.PI) ); - c[2] = Math.abs( Math.sin( 0.01 * reModular.getTime() * Math.PI) ); - */ - - for (var i in result) { - result[i] /= 255; - } - - this.boxcolor = colorToString(result); - this.setOutputData(0, result); - }; - - LiteGraph.registerNodeType("color/palette", ColorPalette); - - function ImageFrame() { - this.addInput("", "image,canvas"); - this.size = [200, 200]; - } - - ImageFrame.title = "Frame"; - ImageFrame.desc = "Frame viewerew"; - ImageFrame.widgets = [ - { name: "resize", text: "Resize box", type: "button" }, - { name: "view", text: "View Image", type: "button" } - ]; - - ImageFrame.prototype.onDrawBackground = function(ctx) { - if (this.frame && !this.flags.collapsed) { - ctx.drawImage(this.frame, 0, 0, this.size[0], this.size[1]); - } - }; - - ImageFrame.prototype.onExecute = function() { - this.frame = this.getInputData(0); - this.setDirtyCanvas(true); - }; - - ImageFrame.prototype.onWidget = function(e, widget) { - if (widget.name == "resize" && this.frame) { - var width = this.frame.width; - var height = this.frame.height; - - if (!width && this.frame.videoWidth != null) { - width = this.frame.videoWidth; - height = this.frame.videoHeight; - } - - if (width && height) { - this.size = [width, height]; - } - this.setDirtyCanvas(true, true); - } else if (widget.name == "view") { - this.show(); - } - }; - - ImageFrame.prototype.show = function() { - //var str = this.canvas.toDataURL("image/png"); - if (showElement && this.frame) { - showElement(this.frame); - } - }; - - LiteGraph.registerNodeType("graphics/frame", ImageFrame); - - function ImageFade() { - this.addInputs([ - ["img1", "image"], - ["img2", "image"], - ["fade", "number"] - ]); - this.addOutput("", "image"); - this.properties = { fade: 0.5, width: 512, height: 512 }; - } - - ImageFade.title = "Image fade"; - ImageFade.desc = "Fades between images"; - ImageFade.widgets = [ - { name: "resizeA", text: "Resize to A", type: "button" }, - { name: "resizeB", text: "Resize to B", type: "button" } - ]; - - ImageFade.prototype.onAdded = function() { - this.createCanvas(); - var ctx = this.canvas.getContext("2d"); - ctx.fillStyle = "#000"; - ctx.fillRect(0, 0, this.properties["width"], this.properties["height"]); - }; - - ImageFade.prototype.createCanvas = function() { - this.canvas = document.createElement("canvas"); - this.canvas.width = this.properties["width"]; - this.canvas.height = this.properties["height"]; - }; - - ImageFade.prototype.onExecute = function() { - var ctx = this.canvas.getContext("2d"); - this.canvas.width = this.canvas.width; - - var A = this.getInputData(0); - if (A != null) { - ctx.drawImage(A, 0, 0, this.canvas.width, this.canvas.height); - } - - var fade = this.getInputData(2); - if (fade == null) { - fade = this.properties["fade"]; - } else { - this.properties["fade"] = fade; - } - - ctx.globalAlpha = fade; - var B = this.getInputData(1); - if (B != null) { - ctx.drawImage(B, 0, 0, this.canvas.width, this.canvas.height); - } - ctx.globalAlpha = 1.0; - - this.setOutputData(0, this.canvas); - this.setDirtyCanvas(true); - }; - - LiteGraph.registerNodeType("graphics/imagefade", ImageFade); - - function ImageCrop() { - this.addInput("", "image"); - this.addOutput("", "image"); - this.properties = { width: 256, height: 256, x: 0, y: 0, scale: 1.0 }; - this.size = [50, 20]; - } - - ImageCrop.title = "Crop"; - ImageCrop.desc = "Crop Image"; - - ImageCrop.prototype.onAdded = function() { - this.createCanvas(); - }; - - ImageCrop.prototype.createCanvas = function() { - this.canvas = document.createElement("canvas"); - this.canvas.width = this.properties["width"]; - this.canvas.height = this.properties["height"]; - }; - - ImageCrop.prototype.onExecute = function() { - var input = this.getInputData(0); - if (!input) { - return; - } - - if (input.width) { - var ctx = this.canvas.getContext("2d"); - - ctx.drawImage( - input, - -this.properties["x"], - -this.properties["y"], - input.width * this.properties["scale"], - input.height * this.properties["scale"] - ); - this.setOutputData(0, this.canvas); - } else { - this.setOutputData(0, null); - } - }; - - ImageCrop.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } - if (this.canvas) { - ctx.drawImage( - this.canvas, - 0, - 0, - this.canvas.width, - this.canvas.height, - 0, - 0, - this.size[0], - this.size[1] - ); - } - }; - - ImageCrop.prototype.onPropertyChanged = function(name, value) { - this.properties[name] = value; - - if (name == "scale") { - this.properties[name] = parseFloat(value); - if (this.properties[name] == 0) { - console.error("Error in scale"); - this.properties[name] = 1.0; - } - } else { - this.properties[name] = parseInt(value); - } - - this.createCanvas(); - - return true; - }; - - LiteGraph.registerNodeType("graphics/cropImage", ImageCrop); - - //CANVAS stuff - - function CanvasNode() { - this.addInput("clear", LiteGraph.ACTION); - this.addOutput("", "canvas"); - this.properties = { width: 512, height: 512, autoclear: true }; - - this.canvas = document.createElement("canvas"); - this.ctx = this.canvas.getContext("2d"); - } - - CanvasNode.title = "Canvas"; - CanvasNode.desc = "Canvas to render stuff"; - - CanvasNode.prototype.onExecute = function() { - var canvas = this.canvas; - var w = this.properties.width | 0; - var h = this.properties.height | 0; - if (canvas.width != w) { - canvas.width = w; - } - if (canvas.height != h) { - canvas.height = h; - } - - if (this.properties.autoclear) { - this.ctx.clearRect(0, 0, canvas.width, canvas.height); - } - this.setOutputData(0, canvas); - }; - - CanvasNode.prototype.onAction = function(action, param) { - if (action == "clear") { - this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); - } - }; - - LiteGraph.registerNodeType("graphics/canvas", CanvasNode); - - function DrawImageNode() { - this.addInput("canvas", "canvas"); - this.addInput("img", "image,canvas"); - this.addInput("x", "number"); - this.addInput("y", "number"); - this.properties = { x: 0, y: 0, opacity: 1 }; - } - - DrawImageNode.title = "DrawImage"; - DrawImageNode.desc = "Draws image into a canvas"; - - DrawImageNode.prototype.onExecute = function() { - var canvas = this.getInputData(0); - if (!canvas) { - return; - } - - var img = this.getInputOrProperty("img"); - if (!img) { - return; - } - - var x = this.getInputOrProperty("x"); - var y = this.getInputOrProperty("y"); - var ctx = canvas.getContext("2d"); - ctx.drawImage(img, x, y); - }; - - LiteGraph.registerNodeType("graphics/drawImage", DrawImageNode); - - function DrawRectangleNode() { - this.addInput("canvas", "canvas"); - this.addInput("x", "number"); - this.addInput("y", "number"); - this.addInput("w", "number"); - this.addInput("h", "number"); - this.properties = { - x: 0, - y: 0, - w: 10, - h: 10, - color: "white", - opacity: 1 - }; - } - - DrawRectangleNode.title = "DrawRectangle"; - DrawRectangleNode.desc = "Draws rectangle in canvas"; - - DrawRectangleNode.prototype.onExecute = function() { - var canvas = this.getInputData(0); - if (!canvas) { - return; - } - - var x = this.getInputOrProperty("x"); - var y = this.getInputOrProperty("y"); - var w = this.getInputOrProperty("w"); - var h = this.getInputOrProperty("h"); - var ctx = canvas.getContext("2d"); - ctx.fillRect(x, y, w, h); - }; - - LiteGraph.registerNodeType("graphics/drawRectangle", DrawRectangleNode); - - function ImageVideo() { - this.addInput("t", "number"); - this.addOutputs([["frame", "image"], ["t", "number"], ["d", "number"]]); - this.properties = { url: "", use_proxy: true }; - } - - ImageVideo.title = "Video"; - ImageVideo.desc = "Video playback"; - ImageVideo.widgets = [ - { name: "play", text: "PLAY", type: "minibutton" }, - { name: "stop", text: "STOP", type: "minibutton" }, - { name: "demo", text: "Demo video", type: "button" }, - { name: "mute", text: "Mute video", type: "button" } - ]; - - ImageVideo.prototype.onExecute = function() { - if (!this.properties.url) { - return; - } - - if (this.properties.url != this._video_url) { - this.loadVideo(this.properties.url); - } - - if (!this._video || this._video.width == 0) { - return; - } - - var t = this.getInputData(0); - if (t && t >= 0 && t <= 1.0) { - this._video.currentTime = t * this._video.duration; - this._video.pause(); - } - - this._video.dirty = true; - this.setOutputData(0, this._video); - this.setOutputData(1, this._video.currentTime); - this.setOutputData(2, this._video.duration); - this.setDirtyCanvas(true); - }; - - ImageVideo.prototype.onStart = function() { - this.play(); - }; - - ImageVideo.prototype.onStop = function() { - this.stop(); - }; - - ImageVideo.prototype.loadVideo = function(url) { - this._video_url = url; - - var pos = url.substr(0,10).indexOf(":"); - var protocol = ""; - if(pos != -1) - protocol = url.substr(0,pos); - - var host = ""; - if(protocol) - { - host = url.substr(0,url.indexOf("/",protocol.length + 3)); - host = host.substr(protocol.length+3); - } - - if ( - this.properties.use_proxy && - protocol && - LiteGraph.proxy && - host != location.host - ) { - url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); - } - - this._video = document.createElement("video"); - this._video.src = url; - this._video.type = "type=video/mp4"; - - this._video.muted = true; - this._video.autoplay = true; - - var that = this; - this._video.addEventListener("loadedmetadata", function(e) { - //onload - console.log("Duration: " + this.duration + " seconds"); - console.log("Size: " + this.videoWidth + "," + this.videoHeight); - that.setDirtyCanvas(true); - this.width = this.videoWidth; - this.height = this.videoHeight; - }); - this._video.addEventListener("progress", function(e) { - //onload - console.log("video loading..."); - }); - this._video.addEventListener("error", function(e) { - console.error("Error loading video: " + this.src); - if (this.error) { - switch (this.error.code) { - case this.error.MEDIA_ERR_ABORTED: - console.error("You stopped the video."); - break; - case this.error.MEDIA_ERR_NETWORK: - console.error("Network error - please try again later."); - break; - case this.error.MEDIA_ERR_DECODE: - console.error("Video is broken.."); - break; - case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED: - console.error("Sorry, your browser can't play this video."); - break; - } - } - }); - - this._video.addEventListener("ended", function(e) { - console.log("Video Ended."); - this.play(); //loop - }); - - //document.body.appendChild(this.video); - }; - - ImageVideo.prototype.onPropertyChanged = function(name, value) { - this.properties[name] = value; - if (name == "url" && value != "") { - this.loadVideo(value); - } - - return true; - }; - - ImageVideo.prototype.play = function() { - if (this._video && this._video.videoWidth ) { //is loaded - this._video.play(); - } - }; - - ImageVideo.prototype.playPause = function() { - if (!this._video) { - return; - } - if (this._video.paused) { - this.play(); - } else { - this.pause(); - } - }; - - ImageVideo.prototype.stop = function() { - if (!this._video) { - return; - } - this._video.pause(); - this._video.currentTime = 0; - }; - - ImageVideo.prototype.pause = function() { - if (!this._video) { - return; - } - console.log("Video paused"); - this._video.pause(); - }; - - ImageVideo.prototype.onWidget = function(e, widget) { - /* - if(widget.name == "demo") - { - this.loadVideo(); - } - else if(widget.name == "play") - { - if(this._video) - this.playPause(); - } - if(widget.name == "stop") - { - this.stop(); - } - else if(widget.name == "mute") - { - if(this._video) - this._video.muted = !this._video.muted; - } - */ - }; - - LiteGraph.registerNodeType("graphics/video", ImageVideo); - - // Texture Webcam ***************************************** - function ImageWebcam() { - this.addOutput("Webcam", "image"); - this.properties = { facingMode: "user" }; - this.boxcolor = "black"; - this.frame = 0; - } - - ImageWebcam.title = "Webcam"; - ImageWebcam.desc = "Webcam image"; - ImageWebcam.is_webcam_open = false; - - ImageWebcam.prototype.openStream = function() { - if (!navigator.getUserMedia) { - //console.log('getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags'); - return; - } - - this._waiting_confirmation = true; - - // Not showing vendor prefixes. - var constraints = { - audio: false, - video: { facingMode: this.properties.facingMode } - }; - navigator.mediaDevices - .getUserMedia(constraints) - .then(this.streamReady.bind(this)) - .catch(onFailSoHard); - - var that = this; - function onFailSoHard(e) { - console.log("Webcam rejected", e); - that._webcam_stream = false; - ImageWebcam.is_webcam_open = false; - that.boxcolor = "red"; - that.trigger("stream_error"); - } - }; - - ImageWebcam.prototype.closeStream = function() { - if (this._webcam_stream) { - var tracks = this._webcam_stream.getTracks(); - if (tracks.length) { - for (var i = 0; i < tracks.length; ++i) { - tracks[i].stop(); - } - } - ImageWebcam.is_webcam_open = false; - this._webcam_stream = null; - this._video = null; - this.boxcolor = "black"; - this.trigger("stream_closed"); - } - }; - - ImageWebcam.prototype.onPropertyChanged = function(name, value) { - if (name == "facingMode") { - this.properties.facingMode = value; - this.closeStream(); - this.openStream(); - } - }; - - ImageWebcam.prototype.onRemoved = function() { - this.closeStream(); - }; - - ImageWebcam.prototype.streamReady = function(localMediaStream) { - this._webcam_stream = localMediaStream; - //this._waiting_confirmation = false; - this.boxcolor = "green"; - - var video = this._video; - if (!video) { - video = document.createElement("video"); - video.autoplay = true; - video.srcObject = localMediaStream; - this._video = video; - //document.body.appendChild( video ); //debug - //when video info is loaded (size and so) - video.onloadedmetadata = function(e) { - // Ready to go. Do some stuff. - console.log(e); - ImageWebcam.is_webcam_open = true; - }; - } - - this.trigger("stream_ready", video); - }; - - ImageWebcam.prototype.onExecute = function() { - if (this._webcam_stream == null && !this._waiting_confirmation) { - this.openStream(); - } - - if (!this._video || !this._video.videoWidth) { - return; - } - - this._video.frame = ++this.frame; - this._video.width = this._video.videoWidth; - this._video.height = this._video.videoHeight; - this.setOutputData(0, this._video); - for (var i = 1; i < this.outputs.length; ++i) { - if (!this.outputs[i]) { - continue; - } - switch (this.outputs[i].name) { - case "width": - this.setOutputData(i, this._video.videoWidth); - break; - case "height": - this.setOutputData(i, this._video.videoHeight); - break; - } - } - }; - - ImageWebcam.prototype.getExtraMenuOptions = function(graphcanvas) { - var that = this; - var txt = !that.properties.show ? "Show Frame" : "Hide Frame"; - return [ - { - content: txt, - callback: function() { - that.properties.show = !that.properties.show; - } - } - ]; - }; - - ImageWebcam.prototype.onDrawBackground = function(ctx) { - if ( - this.flags.collapsed || - this.size[1] <= 20 || - !this.properties.show - ) { - return; - } - - if (!this._video) { - return; - } - - //render to graph canvas - ctx.save(); - ctx.drawImage(this._video, 0, 0, this.size[0], this.size[1]); - ctx.restore(); - }; - - ImageWebcam.prototype.onGetOutputs = function() { - return [ - ["width", "number"], - ["height", "number"], - ["stream_ready", LiteGraph.EVENT], - ["stream_closed", LiteGraph.EVENT], - ["stream_error", LiteGraph.EVENT] - ]; - }; - - LiteGraph.registerNodeType("graphics/webcam", ImageWebcam); -})(this); - -(function(global) { - var LiteGraph = global.LiteGraph; - - //Works with Litegl.js to create WebGL nodes - global.LGraphTexture = null; - - if (typeof GL == "undefined") - return; - - LGraphCanvas.link_type_colors["Texture"] = "#987"; - - function LGraphTexture() { - this.addOutput("tex", "Texture"); - this.addOutput("name", "string"); - this.properties = { name: "", filter: true }; - this.size = [ - LGraphTexture.image_preview_size, - LGraphTexture.image_preview_size - ]; - } - - global.LGraphTexture = LGraphTexture; - - LGraphTexture.title = "Texture"; - LGraphTexture.desc = "Texture"; - LGraphTexture.widgets_info = { - name: { widget: "texture" }, - filter: { widget: "checkbox" } - }; - - //REPLACE THIS TO INTEGRATE WITH YOUR FRAMEWORK - LGraphTexture.loadTextureCallback = null; //function in charge of loading textures when not present in the container - LGraphTexture.image_preview_size = 256; - - //flags to choose output texture type - LGraphTexture.PASS_THROUGH = 1; //do not apply FX - LGraphTexture.COPY = 2; //create new texture with the same properties as the origin texture - LGraphTexture.LOW = 3; //create new texture with low precision (byte) - LGraphTexture.HIGH = 4; //create new texture with high precision (half-float) - LGraphTexture.REUSE = 5; //reuse input texture - LGraphTexture.DEFAULT = 2; - - LGraphTexture.MODE_VALUES = { - "pass through": LGraphTexture.PASS_THROUGH, - copy: LGraphTexture.COPY, - low: LGraphTexture.LOW, - high: LGraphTexture.HIGH, - reuse: LGraphTexture.REUSE, - default: LGraphTexture.DEFAULT - }; - - //returns the container where all the loaded textures are stored (overwrite if you have a Resources Manager) - LGraphTexture.getTexturesContainer = function() { - return gl.textures; - }; - - //process the loading of a texture (overwrite it if you have a Resources Manager) - LGraphTexture.loadTexture = function(name, options) { - options = options || {}; - var url = name; - if (url.substr(0, 7) == "http://") { - if (LiteGraph.proxy) { - //proxy external files - url = LiteGraph.proxy + url.substr(7); - } - } - - var container = LGraphTexture.getTexturesContainer(); - var tex = (container[name] = GL.Texture.fromURL(url, options)); - return tex; - }; - - LGraphTexture.getTexture = function(name) { - var container = this.getTexturesContainer(); - - if (!container) { - throw "Cannot load texture, container of textures not found"; - } - - var tex = container[name]; - if (!tex && name && name[0] != ":") { - return this.loadTexture(name); - } - - return tex; - }; - - //used to compute the appropiate output texture - LGraphTexture.getTargetTexture = function(origin, target, mode) { - if (!origin) { - throw "LGraphTexture.getTargetTexture expects a reference texture"; - } - - var tex_type = null; - - switch (mode) { - case LGraphTexture.LOW: - tex_type = gl.UNSIGNED_BYTE; - break; - case LGraphTexture.HIGH: - tex_type = gl.HIGH_PRECISION_FORMAT; - break; - case LGraphTexture.REUSE: - return origin; - break; - case LGraphTexture.COPY: - default: - tex_type = origin ? origin.type : gl.UNSIGNED_BYTE; - break; - } - - if ( - !target || - target.width != origin.width || - target.height != origin.height || - target.type != tex_type - ) { - target = new GL.Texture(origin.width, origin.height, { - type: tex_type, - format: gl.RGBA, - filter: gl.LINEAR - }); - } - - return target; - }; - - LGraphTexture.getTextureType = function(precision, ref_texture) { - var type = ref_texture ? ref_texture.type : gl.UNSIGNED_BYTE; - switch (precision) { - case LGraphTexture.HIGH: - type = gl.HIGH_PRECISION_FORMAT; - break; - case LGraphTexture.LOW: - type = gl.UNSIGNED_BYTE; - break; - //no default - } - return type; - }; - - LGraphTexture.getWhiteTexture = function() { - if (this._white_texture) { - return this._white_texture; - } - var texture = (this._white_texture = GL.Texture.fromMemory( - 1, - 1, - [255, 255, 255, 255], - { format: gl.RGBA, wrap: gl.REPEAT, filter: gl.NEAREST } - )); - return texture; - }; - - LGraphTexture.getNoiseTexture = function() { - if (this._noise_texture) { - return this._noise_texture; - } - - var noise = new Uint8Array(512 * 512 * 4); - for (var i = 0; i < 512 * 512 * 4; ++i) { - noise[i] = Math.random() * 255; - } - - var texture = GL.Texture.fromMemory(512, 512, noise, { - format: gl.RGBA, - wrap: gl.REPEAT, - filter: gl.NEAREST - }); - this._noise_texture = texture; - return texture; - }; - - LGraphTexture.prototype.onDropFile = function(data, filename, file) { - if (!data) { - this._drop_texture = null; - this.properties.name = ""; - } else { - var texture = null; - if (typeof data == "string") { - texture = GL.Texture.fromURL(data); - } else if (filename.toLowerCase().indexOf(".dds") != -1) { - texture = GL.Texture.fromDDSInMemory(data); - } else { - var blob = new Blob([file]); - var url = URL.createObjectURL(blob); - texture = GL.Texture.fromURL(url); - } - - this._drop_texture = texture; - this.properties.name = filename; - } - }; - - LGraphTexture.prototype.getExtraMenuOptions = function(graphcanvas) { - var that = this; - if (!this._drop_texture) { - return; - } - return [ - { - content: "Clear", - callback: function() { - that._drop_texture = null; - that.properties.name = ""; - } - } - ]; - }; - - LGraphTexture.prototype.onExecute = function() { - var tex = null; - if (this.isOutputConnected(1)) { - tex = this.getInputData(0); - } - - if (!tex && this._drop_texture) { - tex = this._drop_texture; - } - - if (!tex && this.properties.name) { - tex = LGraphTexture.getTexture(this.properties.name); - } - - if (!tex) { - this.setOutputData( 0, null ); - this.setOutputData( 1, "" ); - return; - } - - this._last_tex = tex; - - if (this.properties.filter === false) { - tex.setParameter(gl.TEXTURE_MAG_FILTER, gl.NEAREST); - } else { - tex.setParameter(gl.TEXTURE_MAG_FILTER, gl.LINEAR); - } - - this.setOutputData( 0, tex ); - this.setOutputData( 1, tex.fullpath || tex.filename ); - - for (var i = 2; i < this.outputs.length; i++) { - var output = this.outputs[i]; - if (!output) { - continue; - } - var v = null; - if (output.name == "width") { - v = tex.width; - } else if (output.name == "height") { - v = tex.height; - } else if (output.name == "aspect") { - v = tex.width / tex.height; - } - this.setOutputData(i, v); - } - }; - - LGraphTexture.prototype.onResourceRenamed = function( - old_name, - new_name - ) { - if (this.properties.name == old_name) { - this.properties.name = new_name; - } - }; - - LGraphTexture.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed || this.size[1] <= 20) { - return; - } - - if (this._drop_texture && ctx.webgl) { - ctx.drawImage( - this._drop_texture, - 0, - 0, - this.size[0], - this.size[1] - ); - //this._drop_texture.renderQuad(this.pos[0],this.pos[1],this.size[0],this.size[1]); - return; - } - - //Different texture? then get it from the GPU - if (this._last_preview_tex != this._last_tex) { - if (ctx.webgl) { - this._canvas = this._last_tex; - } else { - var tex_canvas = LGraphTexture.generateLowResTexturePreview( - this._last_tex - ); - if (!tex_canvas) { - return; - } - - this._last_preview_tex = this._last_tex; - this._canvas = cloneCanvas(tex_canvas); - } - } - - if (!this._canvas) { - return; - } - - //render to graph canvas - ctx.save(); - if (!ctx.webgl) { - //reverse image - ctx.translate(0, this.size[1]); - ctx.scale(1, -1); - } - ctx.drawImage(this._canvas, 0, 0, this.size[0], this.size[1]); - ctx.restore(); - }; - - //very slow, used at your own risk - LGraphTexture.generateLowResTexturePreview = function(tex) { - if (!tex) { - return null; - } - - var size = LGraphTexture.image_preview_size; - var temp_tex = tex; - - if (tex.format == gl.DEPTH_COMPONENT) { - return null; - } //cannot generate from depth - - //Generate low-level version in the GPU to speed up - if (tex.width > size || tex.height > size) { - temp_tex = this._preview_temp_tex; - if (!this._preview_temp_tex) { - temp_tex = new GL.Texture(size, size, { - minFilter: gl.NEAREST - }); - this._preview_temp_tex = temp_tex; - } - - //copy - tex.copyTo(temp_tex); - tex = temp_tex; - } - - //create intermediate canvas with lowquality version - var tex_canvas = this._preview_canvas; - if (!tex_canvas) { - tex_canvas = createCanvas(size, size); - this._preview_canvas = tex_canvas; - } - - if (temp_tex) { - temp_tex.toCanvas(tex_canvas); - } - return tex_canvas; - }; - - LGraphTexture.prototype.getResources = function(res) { - if(this.properties.name) - res[this.properties.name] = GL.Texture; - return res; - }; - - LGraphTexture.prototype.onGetInputs = function() { - return [["in", "Texture"]]; - }; - - LGraphTexture.prototype.onGetOutputs = function() { - return [ - ["width", "number"], - ["height", "number"], - ["aspect", "number"] - ]; - }; - - //used to replace shader code - LGraphTexture.replaceCode = function( code, context ) - { - return code.replace(/\{\{[a-zA-Z0-9_]*\}\}/g, function(v){ - v = v.replace( /[\{\}]/g, "" ); - return context[v] || ""; - }); - } - - LiteGraph.registerNodeType("texture/texture", LGraphTexture); - - //************************** - function LGraphTexturePreview() { - this.addInput("Texture", "Texture"); - this.properties = { flipY: false }; - this.size = [ - LGraphTexture.image_preview_size, - LGraphTexture.image_preview_size - ]; - } - - LGraphTexturePreview.title = "Preview"; - LGraphTexturePreview.desc = "Show a texture in the graph canvas"; - LGraphTexturePreview.allow_preview = false; - - LGraphTexturePreview.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } - - if (!ctx.webgl && !LGraphTexturePreview.allow_preview) { - return; - } //not working well - - var tex = this.getInputData(0); - if (!tex) { - return; - } - - var tex_canvas = null; - - if (!tex.handle && ctx.webgl) { - tex_canvas = tex; - } else { - tex_canvas = LGraphTexture.generateLowResTexturePreview(tex); - } - - //render to graph canvas - ctx.save(); - if (this.properties.flipY) { - ctx.translate(0, this.size[1]); - ctx.scale(1, -1); - } - ctx.drawImage(tex_canvas, 0, 0, this.size[0], this.size[1]); - ctx.restore(); - }; - - LiteGraph.registerNodeType("texture/preview", LGraphTexturePreview); - - //************************************** - - function LGraphTextureSave() { - this.addInput("Texture", "Texture"); - this.addOutput("tex", "Texture"); - this.addOutput("name", "string"); - this.properties = { name: "", generate_mipmaps: false }; - } - - LGraphTextureSave.title = "Save"; - LGraphTextureSave.desc = "Save a texture in the repository"; - - LGraphTextureSave.prototype.getPreviewTexture = function() - { - return this._texture; - } - - LGraphTextureSave.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if (this.properties.generate_mipmaps) { - tex.bind(0); - tex.setParameter( gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR ); - gl.generateMipmap(tex.texture_type); - tex.unbind(0); - } - - if (this.properties.name) { - //for cases where we want to perform something when storing it - if (LGraphTexture.storeTexture) { - LGraphTexture.storeTexture(this.properties.name, tex); - } else { - var container = LGraphTexture.getTexturesContainer(); - container[this.properties.name] = tex; - } - } - - this._texture = tex; - this.setOutputData(0, tex); - this.setOutputData(1, this.properties.name); - }; - - LiteGraph.registerNodeType("texture/save", LGraphTextureSave); - - //**************************************************** - - function LGraphTextureOperation() { - this.addInput("Texture", "Texture"); - this.addInput("TextureB", "Texture"); - this.addInput("value", "number"); - this.addOutput("Texture", "Texture"); - this.help = "

pixelcode must be vec3, uvcode must be vec2, is optional

\ -

uv: tex. coords

color: texture colorB: textureB

time: scene time value: input value

For multiline you must type: result = ...

"; - - this.properties = { - value: 1, - pixelcode: "color + colorB * value", - uvcode: "", - precision: LGraphTexture.DEFAULT - }; - - this.has_error = false; - } - - LGraphTextureOperation.widgets_info = { - uvcode: { widget: "code" }, - pixelcode: { widget: "code" }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureOperation.title = "Operation"; - LGraphTextureOperation.desc = "Texture shader operation"; - - LGraphTextureOperation.presets = {}; - - LGraphTextureOperation.prototype.getExtraMenuOptions = function( - graphcanvas - ) { - var that = this; - var txt = !that.properties.show ? "Show Texture" : "Hide Texture"; - return [ - { - content: txt, - callback: function() { - that.properties.show = !that.properties.show; - } - } - ]; - }; - - LGraphTextureOperation.prototype.onPropertyChanged = function() - { - this.has_error = false; - } - - LGraphTextureOperation.prototype.onDrawBackground = function(ctx) { - if ( - this.flags.collapsed || - this.size[1] <= 20 || - !this.properties.show - ) { - return; - } - - if (!this._tex) { - return; - } - - //only works if using a webgl renderer - if (this._tex.gl != ctx) { - return; - } - - //render to graph canvas - ctx.save(); - ctx.drawImage(this._tex, 0, 0, this.size[0], this.size[1]); - ctx.restore(); - }; - - LGraphTextureOperation.prototype.onExecute = function() { - var tex = this.getInputData(0); - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, tex); - return; - } - - var texB = this.getInputData(1); - - if (!this.properties.uvcode && !this.properties.pixelcode) { - return; - } - - var width = 512; - var height = 512; - if (tex) { - width = tex.width; - height = tex.height; - } else if (texB) { - width = texB.width; - height = texB.height; - } - - if(!texB) - texB = GL.Texture.getWhiteTexture(); - - var type = LGraphTexture.getTextureType( this.properties.precision, tex ); - - if (!tex && !this._tex) { - this._tex = new GL.Texture(width, height, { type: type, format: gl.RGBA, filter: gl.LINEAR }); - } else { - this._tex = LGraphTexture.getTargetTexture( tex || this._tex, this._tex, this.properties.precision ); - } - - var uvcode = ""; - if (this.properties.uvcode) { - uvcode = "uv = " + this.properties.uvcode; - if (this.properties.uvcode.indexOf(";") != -1) { - //there are line breaks, means multiline code - uvcode = this.properties.uvcode; - } - } - - var pixelcode = ""; - if (this.properties.pixelcode) { - pixelcode = "result = " + this.properties.pixelcode; - if (this.properties.pixelcode.indexOf(";") != -1) { - //there are line breaks, means multiline code - pixelcode = this.properties.pixelcode; - } - } - - var shader = this._shader; - - if ( !this.has_error && (!shader || this._shader_code != uvcode + "|" + pixelcode) ) { - - var final_pixel_code = LGraphTexture.replaceCode( LGraphTextureOperation.pixel_shader, { UV_CODE:uvcode, PIXEL_CODE:pixelcode }); - - try { - shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, final_pixel_code ); - this.boxcolor = "#00FF00"; - } catch (err) { - //console.log("Error compiling shader: ", err, final_pixel_code ); - GL.Shader.dumpErrorToConsole(err,Shader.SCREEN_VERTEX_SHADER, final_pixel_code); - this.boxcolor = "#FF0000"; - this.has_error = true; - return; - } - this._shader = shader; - this._shader_code = uvcode + "|" + pixelcode; - } - - if(!this._shader) - return; - - var value = this.getInputData(2); - if (value != null) { - this.properties.value = value; - } else { - value = parseFloat(this.properties.value); - } - - var time = this.graph.getTime(); - - this._tex.drawTo(function() { - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.CULL_FACE); - gl.disable(gl.BLEND); - if (tex) { - tex.bind(0); - } - if (texB) { - texB.bind(1); - } - var mesh = Mesh.getScreenQuad(); - shader - .uniforms({ - u_texture: 0, - u_textureB: 1, - value: value, - texSize: [width, height], - time: time - }) - .draw(mesh); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureOperation.pixel_shader = - "precision highp float;\n\ - \n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - varying vec2 v_coord;\n\ - uniform vec2 texSize;\n\ - uniform float time;\n\ - uniform float value;\n\ - \n\ - void main() {\n\ - vec2 uv = v_coord;\n\ - {{UV_CODE}};\n\ - vec4 color4 = texture2D(u_texture, uv);\n\ - vec3 color = color4.rgb;\n\ - vec4 color4B = texture2D(u_textureB, uv);\n\ - vec3 colorB = color4B.rgb;\n\ - vec3 result = color;\n\ - float alpha = 1.0;\n\ - {{PIXEL_CODE}};\n\ - gl_FragColor = vec4(result, alpha);\n\ - }\n\ - "; - - LGraphTextureOperation.registerPreset = function ( name, code ) - { - LGraphTextureOperation.presets[name] = code; - } - - LGraphTextureOperation.registerPreset("",""); - LGraphTextureOperation.registerPreset("bypass","color"); - LGraphTextureOperation.registerPreset("add","color + colorB * value"); - LGraphTextureOperation.registerPreset("substract","(color - colorB) * value"); - LGraphTextureOperation.registerPreset("mate","mix( color, colorB, color4B.a * value)"); - LGraphTextureOperation.registerPreset("invert","vec3(1.0) - color"); - LGraphTextureOperation.registerPreset("multiply","color * colorB * value"); - LGraphTextureOperation.registerPreset("divide","(color / colorB) / value"); - LGraphTextureOperation.registerPreset("difference","abs(color - colorB) * value"); - LGraphTextureOperation.registerPreset("max","max(color, colorB) * value"); - LGraphTextureOperation.registerPreset("min","min(color, colorB) * value"); - LGraphTextureOperation.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"); - LGraphTextureOperation.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"); - LGraphTextureOperation.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"); - LGraphTextureOperation.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"); - - //webglstudio stuff... - LGraphTextureOperation.prototype.onInspect = function(widgets) - { - var that = this; - widgets.addCombo("Presets","",{ values: Object.keys(LGraphTextureOperation.presets), callback: function(v){ - var code = LGraphTextureOperation.presets[v]; - if(!code) - return; - that.setProperty("pixelcode",code); - that.title = v; - widgets.refresh(); - }}); - } - - LiteGraph.registerNodeType("texture/operation", LGraphTextureOperation); - - //**************************************************** - - function LGraphTextureShader() { - this.addOutput("out", "Texture"); - this.properties = { - code: "", - u_value: 1, - u_color: [1,1,1,1], - width: 512, - height: 512, - precision: LGraphTexture.DEFAULT - }; - - this.properties.code = LGraphTextureShader.pixel_shader; - this._uniforms = { u_value: 1, u_color: vec4.create(), in_texture: 0, texSize: vec2.create(), time: 0 }; - } - - LGraphTextureShader.title = "Shader"; - LGraphTextureShader.desc = "Texture shader"; - LGraphTextureShader.widgets_info = { - code: { type: "code", lang: "glsl" }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureShader.prototype.onPropertyChanged = function( - name, - value - ) { - if (name != "code") { - return; - } - - var shader = this.getShader(); - if (!shader) { - return; - } - - //update connections - var uniforms = shader.uniformInfo; - - //remove deprecated slots - if (this.inputs) { - var already = {}; - for (var i = 0; i < this.inputs.length; ++i) { - var info = this.getInputInfo(i); - if (!info) { - continue; - } - - if (uniforms[info.name] && !already[info.name]) { - already[info.name] = true; - continue; - } - this.removeInput(i); - i--; - } - } - - //update existing ones - for (var i in uniforms) { - var info = shader.uniformInfo[i]; - if (info.loc === null) { - continue; - } //is an attribute, not a uniform - if (i == "time") { - //default one - continue; - } - - var type = "number"; - if (this._shader.samplers[i]) { - type = "texture"; - } else { - switch (info.size) { - case 1: - type = "number"; - break; - case 2: - type = "vec2"; - break; - case 3: - type = "vec3"; - break; - case 4: - type = "vec4"; - break; - case 9: - type = "mat3"; - break; - case 16: - type = "mat4"; - break; - default: - continue; - } - } - - var slot = this.findInputSlot(i); - if (slot == -1) { - this.addInput(i, type); - continue; - } - - var input_info = this.getInputInfo(slot); - if (!input_info) { - this.addInput(i, type); - } else { - if (input_info.type == type) { - continue; - } - this.removeInput(slot, type); - this.addInput(i, type); - } - } - }; - - LGraphTextureShader.prototype.getShader = function() { - //replug - if (this._shader && this._shader_code == this.properties.code) { - return this._shader; - } - - this._shader_code = this.properties.code; - this._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, this.properties.code ); - if (!this._shader) { - this.boxcolor = "red"; - return null; - } else { - this.boxcolor = "green"; - } - return this._shader; - }; - - LGraphTextureShader.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var shader = this.getShader(); - if (!shader) { - return; - } - - var tex_slot = 0; - var in_tex = null; - - //set uniforms - if(this.inputs) - for (var i = 0; i < this.inputs.length; ++i) { - var info = this.getInputInfo(i); - var data = this.getInputData(i); - if (data == null) { - continue; - } - - if (data.constructor === GL.Texture) { - data.bind(tex_slot); - if (!in_tex) { - in_tex = data; - } - data = tex_slot; - tex_slot++; - } - shader.setUniform(info.name, data); //data is tex_slot - } - - var uniforms = this._uniforms; - var type = LGraphTexture.getTextureType( this.properties.precision, in_tex ); - - //render to texture - var w = this.properties.width | 0; - var h = this.properties.height | 0; - if (w == 0) { - w = in_tex ? in_tex.width : gl.canvas.width; - } - if (h == 0) { - h = in_tex ? in_tex.height : gl.canvas.height; - } - uniforms.texSize[0] = w; - uniforms.texSize[1] = h; - uniforms.time = this.graph.getTime(); - uniforms.u_value = this.properties.u_value; - uniforms.u_color.set( this.properties.u_color ); - - if ( !this._tex || this._tex.type != type || this._tex.width != w || this._tex.height != h ) { - this._tex = new GL.Texture(w, h, { type: type, format: gl.RGBA, filter: gl.LINEAR }); - } - var tex = this._tex; - tex.drawTo(function() { - shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad()); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureShader.pixel_shader = -"precision highp float;\n\ -\n\ -varying vec2 v_coord;\n\ -uniform float time; //time in seconds\n\ -uniform vec2 texSize; //tex resolution\n\ -uniform float u_value;\n\ -uniform vec4 u_color;\n\n\ -void main() {\n\ - vec2 uv = v_coord;\n\ - vec3 color = vec3(0.0);\n\ - //your code here\n\ - color.xy=uv;\n\n\ - gl_FragColor = vec4(color, 1.0);\n\ -}\n\ -"; - - LiteGraph.registerNodeType("texture/shader", LGraphTextureShader); - - // Texture Scale Offset - - function LGraphTextureScaleOffset() { - this.addInput("in", "Texture"); - this.addInput("scale", "vec2"); - this.addInput("offset", "vec2"); - this.addOutput("out", "Texture"); - this.properties = { - offset: vec2.fromValues(0, 0), - scale: vec2.fromValues(1, 1), - precision: LGraphTexture.DEFAULT - }; - } - - LGraphTextureScaleOffset.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureScaleOffset.title = "Scale/Offset"; - LGraphTextureScaleOffset.desc = "Applies an scaling and offseting"; - - LGraphTextureScaleOffset.prototype.onExecute = function() { - var tex = this.getInputData(0); - - if (!this.isOutputConnected(0) || !tex) { - return; - } //saves work - - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, tex); - return; - } - - var width = tex.width; - var height = tex.height; - var type = this.precision === LGraphTexture.LOW ? gl.UNSIGNED_BYTE : gl.HIGH_PRECISION_FORMAT; - if (this.precision === LGraphTexture.DEFAULT) { - type = tex.type; - } - - if ( - !this._tex || - this._tex.width != width || - this._tex.height != height || - this._tex.type != type - ) { - this._tex = new GL.Texture(width, height, { - type: type, - format: gl.RGBA, - filter: gl.LINEAR - }); - } - - var shader = this._shader; - - if (!shader) { - shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureScaleOffset.pixel_shader - ); - } - - var scale = this.getInputData(1); - if (scale) { - this.properties.scale[0] = scale[0]; - this.properties.scale[1] = scale[1]; - } else { - scale = this.properties.scale; - } - - var offset = this.getInputData(2); - if (offset) { - this.properties.offset[0] = offset[0]; - this.properties.offset[1] = offset[1]; - } else { - offset = this.properties.offset; - } - - this._tex.drawTo(function() { - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.CULL_FACE); - gl.disable(gl.BLEND); - tex.bind(0); - var mesh = Mesh.getScreenQuad(); - shader - .uniforms({ - u_texture: 0, - u_scale: scale, - u_offset: offset - }) - .draw(mesh); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureScaleOffset.pixel_shader = - "precision highp float;\n\ - \n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - varying vec2 v_coord;\n\ - uniform vec2 u_scale;\n\ - uniform vec2 u_offset;\n\ - \n\ - void main() {\n\ - vec2 uv = v_coord;\n\ - uv = uv / u_scale - u_offset;\n\ - gl_FragColor = texture2D(u_texture, uv);\n\ - }\n\ - "; - - LiteGraph.registerNodeType( - "texture/scaleOffset", - LGraphTextureScaleOffset - ); - - // Warp (distort a texture) ************************* - - function LGraphTextureWarp() { - this.addInput("in", "Texture"); - this.addInput("warp", "Texture"); - this.addInput("factor", "number"); - this.addOutput("out", "Texture"); - this.properties = { - factor: 0.01, - scale: [1,1], - offset: [0,0], - precision: LGraphTexture.DEFAULT - }; - - this._uniforms = { - u_texture: 0, - u_textureB: 1, - u_factor: 1, - u_scale: vec2.create(), - u_offset: vec2.create() - }; - } - - LGraphTextureWarp.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureWarp.title = "Warp"; - LGraphTextureWarp.desc = "Texture warp operation"; - - LGraphTextureWarp.prototype.onExecute = function() { - var tex = this.getInputData(0); - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, tex); - return; - } - - var texB = this.getInputData(1); - - var width = 512; - var height = 512; - var type = gl.UNSIGNED_BYTE; - if (tex) { - width = tex.width; - height = tex.height; - type = tex.type; - } else if (texB) { - width = texB.width; - height = texB.height; - type = texB.type; - } - - if (!tex && !this._tex) { - this._tex = new GL.Texture(width, height, { - type: - this.precision === LGraphTexture.LOW - ? gl.UNSIGNED_BYTE - : gl.HIGH_PRECISION_FORMAT, - format: gl.RGBA, - filter: gl.LINEAR - }); - } else { - this._tex = LGraphTexture.getTargetTexture( - tex || this._tex, - this._tex, - this.properties.precision - ); - } - - var shader = this._shader; - - if (!shader) { - shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureWarp.pixel_shader - ); - } - - var factor = this.getInputData(2); - if (factor != null) { - this.properties.factor = factor; - } else { - factor = parseFloat(this.properties.factor); - } - var uniforms = this._uniforms; - uniforms.u_factor = factor; - uniforms.u_scale.set( this.properties.scale ); - uniforms.u_offset.set( this.properties.offset ); - - this._tex.drawTo(function() { - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.CULL_FACE); - gl.disable(gl.BLEND); - if (tex) { - tex.bind(0); - } - if (texB) { - texB.bind(1); - } - var mesh = Mesh.getScreenQuad(); - shader - .uniforms( uniforms ) - .draw(mesh); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureWarp.pixel_shader = - "precision highp float;\n\ - \n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - varying vec2 v_coord;\n\ - uniform float u_factor;\n\ - uniform vec2 u_scale;\n\ - uniform vec2 u_offset;\n\ - \n\ - void main() {\n\ - vec2 uv = v_coord;\n\ - uv += ( texture2D(u_textureB, uv).rg - vec2(0.5)) * u_factor * u_scale + u_offset;\n\ - gl_FragColor = texture2D(u_texture, uv);\n\ - }\n\ - "; - - LiteGraph.registerNodeType("texture/warp", LGraphTextureWarp); - - //**************************************************** - - // Texture to Viewport ***************************************** - function LGraphTextureToViewport() { - this.addInput("Texture", "Texture"); - this.properties = { - additive: false, - antialiasing: false, - filter: true, - disable_alpha: false, - gamma: 1.0, - viewport: [0,0,1,1] - }; - this.size[0] = 130; - } - - LGraphTextureToViewport.title = "to Viewport"; - LGraphTextureToViewport.desc = "Texture to viewport"; - - LGraphTextureToViewport._prev_viewport = new Float32Array(4); - LGraphTextureToViewport.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if (this.properties.disable_alpha) { - gl.disable(gl.BLEND); - } else { - gl.enable(gl.BLEND); - if (this.properties.additive) { - gl.blendFunc(gl.SRC_ALPHA, gl.ONE); - } else { - gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); - } - } - - gl.disable(gl.DEPTH_TEST); - var gamma = this.properties.gamma || 1.0; - if (this.isInputConnected(1)) { - gamma = this.getInputData(1); - } - - tex.setParameter( - gl.TEXTURE_MAG_FILTER, - this.properties.filter ? gl.LINEAR : gl.NEAREST - ); - - var old_viewport = LGraphTextureToViewport._prev_viewport; - old_viewport.set( gl.viewport_data ); - var new_view = this.properties.viewport; - gl.viewport( old_viewport[0] + old_viewport[2] * new_view[0], old_viewport[1] + old_viewport[3] * new_view[1], old_viewport[2] * new_view[2], old_viewport[3] * new_view[3] ); - var viewport = gl.getViewport(); //gl.getParameter(gl.VIEWPORT); - - if (this.properties.antialiasing) { - if (!LGraphTextureToViewport._shader) { - LGraphTextureToViewport._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureToViewport.aa_pixel_shader - ); - } - - var mesh = Mesh.getScreenQuad(); - tex.bind(0); - LGraphTextureToViewport._shader - .uniforms({ - u_texture: 0, - uViewportSize: [tex.width, tex.height], - u_igamma: 1 / gamma, - inverseVP: [1 / tex.width, 1 / tex.height] - }) - .draw(mesh); - } else { - if (gamma != 1.0) { - if (!LGraphTextureToViewport._gamma_shader) { - LGraphTextureToViewport._gamma_shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureToViewport.gamma_pixel_shader - ); - } - tex.toViewport(LGraphTextureToViewport._gamma_shader, { - u_texture: 0, - u_igamma: 1 / gamma - }); - } else { - tex.toViewport(); - } - } - - gl.viewport( old_viewport[0], old_viewport[1], old_viewport[2], old_viewport[3] ); - }; - - LGraphTextureToViewport.prototype.onGetInputs = function() { - return [["gamma", "number"]]; - }; - - LGraphTextureToViewport.aa_pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 uViewportSize;\n\ - uniform vec2 inverseVP;\n\ - uniform float u_igamma;\n\ - #define FXAA_REDUCE_MIN (1.0/ 128.0)\n\ - #define FXAA_REDUCE_MUL (1.0 / 8.0)\n\ - #define FXAA_SPAN_MAX 8.0\n\ - \n\ - /* from mitsuhiko/webgl-meincraft based on the code on geeks3d.com */\n\ - vec4 applyFXAA(sampler2D tex, vec2 fragCoord)\n\ - {\n\ - vec4 color = vec4(0.0);\n\ - /*vec2 inverseVP = vec2(1.0 / uViewportSize.x, 1.0 / uViewportSize.y);*/\n\ - vec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * inverseVP).xyz;\n\ - vec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * inverseVP).xyz;\n\ - vec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * inverseVP).xyz;\n\ - vec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * inverseVP).xyz;\n\ - vec3 rgbM = texture2D(tex, fragCoord * inverseVP).xyz;\n\ - vec3 luma = vec3(0.299, 0.587, 0.114);\n\ - float lumaNW = dot(rgbNW, luma);\n\ - float lumaNE = dot(rgbNE, luma);\n\ - float lumaSW = dot(rgbSW, luma);\n\ - float lumaSE = dot(rgbSE, luma);\n\ - float lumaM = dot(rgbM, luma);\n\ - float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n\ - float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\ - \n\ - vec2 dir;\n\ - dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n\ - dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\ - \n\ - float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\ - \n\ - float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n\ - dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * inverseVP;\n\ - \n\ - vec3 rgbA = 0.5 * (texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + \n\ - texture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz);\n\ - vec3 rgbB = rgbA * 0.5 + 0.25 * (texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + \n\ - texture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz);\n\ - \n\ - //return vec4(rgbA,1.0);\n\ - float lumaB = dot(rgbB, luma);\n\ - if ((lumaB < lumaMin) || (lumaB > lumaMax))\n\ - color = vec4(rgbA, 1.0);\n\ - else\n\ - color = vec4(rgbB, 1.0);\n\ - if(u_igamma != 1.0)\n\ - color.xyz = pow( color.xyz, vec3(u_igamma) );\n\ - return color;\n\ - }\n\ - \n\ - void main() {\n\ - gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\ - }\n\ - "; - - LGraphTextureToViewport.gamma_pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_igamma;\n\ - void main() {\n\ - vec4 color = texture2D( u_texture, v_coord);\n\ - color.xyz = pow(color.xyz, vec3(u_igamma) );\n\ - gl_FragColor = color;\n\ - }\n\ - "; - - LiteGraph.registerNodeType( - "texture/toviewport", - LGraphTextureToViewport - ); - - // Texture Copy ***************************************** - function LGraphTextureCopy() { - this.addInput("Texture", "Texture"); - this.addOutput("", "Texture"); - this.properties = { - size: 0, - generate_mipmaps: false, - precision: LGraphTexture.DEFAULT - }; - } - - LGraphTextureCopy.title = "Copy"; - LGraphTextureCopy.desc = "Copy Texture"; - LGraphTextureCopy.widgets_info = { - size: { - widget: "combo", - values: [0, 32, 64, 128, 256, 512, 1024, 2048] - }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureCopy.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex && !this._temp_texture) { - return; - } - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - //copy the texture - if (tex) { - var width = tex.width; - var height = tex.height; - - if (this.properties.size != 0) { - width = this.properties.size; - height = this.properties.size; - } - - var temp = this._temp_texture; - - var type = tex.type; - if (this.properties.precision === LGraphTexture.LOW) { - type = gl.UNSIGNED_BYTE; - } else if (this.properties.precision === LGraphTexture.HIGH) { - type = gl.HIGH_PRECISION_FORMAT; - } - - if ( - !temp || - temp.width != width || - temp.height != height || - temp.type != type - ) { - var minFilter = gl.LINEAR; - if ( - this.properties.generate_mipmaps && - isPowerOfTwo(width) && - isPowerOfTwo(height) - ) { - minFilter = gl.LINEAR_MIPMAP_LINEAR; - } - this._temp_texture = new GL.Texture(width, height, { - type: type, - format: gl.RGBA, - minFilter: minFilter, - magFilter: gl.LINEAR - }); - } - tex.copyTo(this._temp_texture); - - if (this.properties.generate_mipmaps) { - this._temp_texture.bind(0); - gl.generateMipmap(this._temp_texture.texture_type); - this._temp_texture.unbind(0); - } - } - - this.setOutputData(0, this._temp_texture); - }; - - LiteGraph.registerNodeType("texture/copy", LGraphTextureCopy); - - // Texture Downsample ***************************************** - function LGraphTextureDownsample() { - this.addInput("Texture", "Texture"); - this.addOutput("", "Texture"); - this.properties = { - iterations: 1, - generate_mipmaps: false, - precision: LGraphTexture.DEFAULT - }; - } - - LGraphTextureDownsample.title = "Downsample"; - LGraphTextureDownsample.desc = "Downsample Texture"; - LGraphTextureDownsample.widgets_info = { - iterations: { type: "number", step: 1, precision: 0, min: 0 }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureDownsample.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex && !this._temp_texture) { - return; - } - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - //we do not allow any texture different than texture 2D - if (!tex || tex.texture_type !== GL.TEXTURE_2D) { - return; - } - - if (this.properties.iterations < 1) { - this.setOutputData(0, tex); - return; - } - - var shader = LGraphTextureDownsample._shader; - if (!shader) { - LGraphTextureDownsample._shader = shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureDownsample.pixel_shader - ); - } - - var width = tex.width | 0; - var height = tex.height | 0; - var type = tex.type; - if (this.properties.precision === LGraphTexture.LOW) { - type = gl.UNSIGNED_BYTE; - } else if (this.properties.precision === LGraphTexture.HIGH) { - type = gl.HIGH_PRECISION_FORMAT; - } - var iterations = this.properties.iterations || 1; - - var origin = tex; - var target = null; - - var temp = []; - var options = { - type: type, - format: tex.format - }; - - var offset = vec2.create(); - var uniforms = { - u_offset: offset - }; - - if (this._texture) { - GL.Texture.releaseTemporary(this._texture); - } - - for (var i = 0; i < iterations; ++i) { - offset[0] = 1 / width; - offset[1] = 1 / height; - width = width >> 1 || 0; - height = height >> 1 || 0; - target = GL.Texture.getTemporary(width, height, options); - temp.push(target); - origin.setParameter(GL.TEXTURE_MAG_FILTER, GL.NEAREST); - origin.copyTo(target, shader, uniforms); - if (width == 1 && height == 1) { - break; - } //nothing else to do - origin = target; - } - - //keep the last texture used - this._texture = temp.pop(); - - //free the rest - for (var i = 0; i < temp.length; ++i) { - GL.Texture.releaseTemporary(temp[i]); - } - - if (this.properties.generate_mipmaps) { - this._texture.bind(0); - gl.generateMipmap(this._texture.texture_type); - this._texture.unbind(0); - } - - this.setOutputData(0, this._texture); - }; - - LGraphTextureDownsample.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_offset;\n\ - varying vec2 v_coord;\n\ - \n\ - void main() {\n\ - vec4 color = texture2D(u_texture, v_coord );\n\ - color += texture2D(u_texture, v_coord + vec2( u_offset.x, 0.0 ) );\n\ - color += texture2D(u_texture, v_coord + vec2( 0.0, u_offset.y ) );\n\ - color += texture2D(u_texture, v_coord + vec2( u_offset.x, u_offset.y ) );\n\ - gl_FragColor = color * 0.25;\n\ - }\n\ - "; - - LiteGraph.registerNodeType( - "texture/downsample", - LGraphTextureDownsample - ); - - // Texture Average ***************************************** - function LGraphTextureAverage() { - this.addInput("Texture", "Texture"); - this.addOutput("tex", "Texture"); - this.addOutput("avg", "vec4"); - this.addOutput("lum", "number"); - this.properties = { - use_previous_frame: true, //to avoid stalls - high_quality: false //to use as much pixels as possible - }; - - this._uniforms = { - u_texture: 0, - u_mipmap_offset: 0 - }; - this._luminance = new Float32Array(4); - } - - LGraphTextureAverage.title = "Average"; - LGraphTextureAverage.desc = - "Compute a partial average (32 random samples) of a texture and stores it as a 1x1 pixel texture.\n If high_quality is true, then it generates the mipmaps first and reads from the lower one."; - - LGraphTextureAverage.prototype.onExecute = function() { - if (!this.properties.use_previous_frame) { - this.updateAverage(); - } - - var v = this._luminance; - this.setOutputData(0, this._temp_texture); - this.setOutputData(1, v); - this.setOutputData(2, (v[0] + v[1] + v[2]) / 3); - }; - - //executed before rendering the frame - LGraphTextureAverage.prototype.onPreRenderExecute = function() { - this.updateAverage(); - }; - - LGraphTextureAverage.prototype.updateAverage = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if ( - !this.isOutputConnected(0) && - !this.isOutputConnected(1) && - !this.isOutputConnected(2) - ) { - return; - } //saves work - - if (!LGraphTextureAverage._shader) { - LGraphTextureAverage._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureAverage.pixel_shader - ); - //creates 256 random numbers and stores them in two mat4 - var samples = new Float32Array(16); - for (var i = 0; i < samples.length; ++i) { - samples[i] = Math.random(); //poorly distributed samples - } - //upload only once - LGraphTextureAverage._shader.uniforms({ - u_samples_a: samples.subarray(0, 16), - u_samples_b: samples.subarray(16, 32) - }); - } - - var temp = this._temp_texture; - var type = gl.UNSIGNED_BYTE; - if (tex.type != type) { - //force floats, half floats cannot be read with gl.readPixels - type = gl.FLOAT; - } - - if (!temp || temp.type != type) { - this._temp_texture = new GL.Texture(1, 1, { - type: type, - format: gl.RGBA, - filter: gl.NEAREST - }); - } - - this._uniforms.u_mipmap_offset = 0; - - if(this.properties.high_quality) - { - if( !this._temp_pot2_texture || this._temp_pot2_texture.type != type ) - this._temp_pot2_texture = new GL.Texture(512, 512, { - type: type, - format: gl.RGBA, - minFilter: gl.LINEAR_MIPMAP_LINEAR, - magFilter: gl.LINEAR - }); - - tex.copyTo( this._temp_pot2_texture ); - tex = this._temp_pot2_texture; - tex.bind(0); - gl.generateMipmap(GL.TEXTURE_2D); - this._uniforms.u_mipmap_offset = 9; - } - - var shader = LGraphTextureAverage._shader; - var uniforms = this._uniforms; - uniforms.u_mipmap_offset = this.properties.mipmap_offset; - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.BLEND); - this._temp_texture.drawTo(function() { - tex.toViewport(shader, uniforms); - }); - - if (this.isOutputConnected(1) || this.isOutputConnected(2)) { - var pixel = this._temp_texture.getPixels(); - if (pixel) { - var v = this._luminance; - var type = this._temp_texture.type; - v.set(pixel); - if (type == gl.UNSIGNED_BYTE) { - vec4.scale(v, v, 1 / 255); - } else if ( - type == GL.HALF_FLOAT || - type == GL.HALF_FLOAT_OES - ) { - //no half floats possible, hard to read back unless copyed to a FLOAT texture, so temp_texture is always forced to FLOAT - } - } - } - }; - - LGraphTextureAverage.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - uniform mat4 u_samples_a;\n\ - uniform mat4 u_samples_b;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_mipmap_offset;\n\ - varying vec2 v_coord;\n\ - \n\ - void main() {\n\ - vec4 color = vec4(0.0);\n\ - //random average\n\ - for(int i = 0; i < 4; ++i)\n\ - for(int j = 0; j < 4; ++j)\n\ - {\n\ - color += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\ - color += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\ - }\n\ - gl_FragColor = color * 0.03125;\n\ - }\n\ - "; - - LiteGraph.registerNodeType("texture/average", LGraphTextureAverage); - - - - // Computes operation between pixels (max, min) ***************************************** - function LGraphTextureMinMax() { - this.addInput("Texture", "Texture"); - this.addOutput("min_t", "Texture"); - this.addOutput("max_t", "Texture"); - this.addOutput("min", "vec4"); - this.addOutput("max", "vec4"); - this.properties = { - mode: "max", - use_previous_frame: true //to avoid stalls - }; - - this._uniforms = { - u_texture: 0 - }; - - this._max = new Float32Array(4); - this._min = new Float32Array(4); - - this._textures_chain = []; - } - - LGraphTextureMinMax.widgets_info = { - mode: { widget: "combo", values: ["min","max","avg"] } - }; - - LGraphTextureMinMax.title = "MinMax"; - LGraphTextureMinMax.desc = "Compute the scene min max"; - - LGraphTextureMinMax.prototype.onExecute = function() { - if (!this.properties.use_previous_frame) { - this.update(); - } - - this.setOutputData(0, this._temp_texture); - this.setOutputData(1, this._luminance); - }; - - //executed before rendering the frame - LGraphTextureMinMax.prototype.onPreRenderExecute = function() { - this.update(); - }; - - LGraphTextureMinMax.prototype.update = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if ( !this.isOutputConnected(0) && !this.isOutputConnected(1) ) { - return; - } //saves work - - if (!LGraphTextureMinMax._shader) { - LGraphTextureMinMax._shader = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureMinMax.pixel_shader ); - } - - var temp = this._temp_texture; - var type = gl.UNSIGNED_BYTE; - if (tex.type != type) { - //force floats, half floats cannot be read with gl.readPixels - type = gl.FLOAT; - } - - var size = 512; - - if( !this._textures_chain.length || this._textures_chain[0].type != type ) - { - var index = 0; - while(i) - { - this._textures_chain[i] = new GL.Texture( size, size, { - type: type, - format: gl.RGBA, - filter: gl.NEAREST - }); - size = size >> 2; - i++; - if(size == 1) - break; - } - } - - tex.copyTo( this._textures_chain[0] ); - var prev = this._textures_chain[0]; - for(var i = 1; i <= this._textures_chain.length; ++i) - { - var tex = this._textures_chain[i]; - - prev = tex; - } - - var shader = LGraphTextureMinMax._shader; - var uniforms = this._uniforms; - uniforms.u_mipmap_offset = this.properties.mipmap_offset; - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.BLEND); - this._temp_texture.drawTo(function() { - tex.toViewport(shader, uniforms); - }); - }; - - LGraphTextureMinMax.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - uniform mat4 u_samples_a;\n\ - uniform mat4 u_samples_b;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_mipmap_offset;\n\ - varying vec2 v_coord;\n\ - \n\ - void main() {\n\ - vec4 color = vec4(0.0);\n\ - //random average\n\ - for(int i = 0; i < 4; ++i)\n\ - for(int j = 0; j < 4; ++j)\n\ - {\n\ - color += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\ - color += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\ - }\n\ - gl_FragColor = color * 0.03125;\n\ - }\n\ - "; - - //LiteGraph.registerNodeType("texture/clustered_operation", LGraphTextureClusteredOperation); - - - function LGraphTextureTemporalSmooth() { - this.addInput("in", "Texture"); - this.addInput("factor", "Number"); - this.addOutput("out", "Texture"); - this.properties = { factor: 0.5 }; - this._uniforms = { - u_texture: 0, - u_textureB: 1, - u_factor: this.properties.factor - }; - } - - LGraphTextureTemporalSmooth.title = "Smooth"; - LGraphTextureTemporalSmooth.desc = "Smooth texture over time"; - - LGraphTextureTemporalSmooth.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex || !this.isOutputConnected(0)) { - return; - } - - if (!LGraphTextureTemporalSmooth._shader) { - LGraphTextureTemporalSmooth._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureTemporalSmooth.pixel_shader - ); - } - - var temp = this._temp_texture; - if ( - !temp || - temp.type != tex.type || - temp.width != tex.width || - temp.height != tex.height - ) { - var options = { - type: tex.type, - format: gl.RGBA, - filter: gl.NEAREST - }; - this._temp_texture = new GL.Texture(tex.width, tex.height, options ); - this._temp_texture2 = new GL.Texture(tex.width, tex.height, options ); - tex.copyTo(this._temp_texture2); - } - - var tempA = this._temp_texture; - var tempB = this._temp_texture2; - - var shader = LGraphTextureTemporalSmooth._shader; - var uniforms = this._uniforms; - uniforms.u_factor = 1.0 - this.getInputOrProperty("factor"); - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - tempA.drawTo(function() { - tempB.bind(1); - tex.toViewport(shader, uniforms); - }); - - this.setOutputData(0, tempA); - - //swap - this._temp_texture = tempB; - this._temp_texture2 = tempA; - }; - - LGraphTextureTemporalSmooth.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - uniform float u_factor;\n\ - varying vec2 v_coord;\n\ - \n\ - void main() {\n\ - gl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\ - }\n\ - "; - - LiteGraph.registerNodeType( "texture/temporal_smooth", LGraphTextureTemporalSmooth ); - - - function LGraphTextureLinearAvgSmooth() { - this.addInput("in", "Texture"); - this.addOutput("avg", "Texture"); - this.addOutput("array", "Texture"); - this.properties = { samples: 64, frames_interval: 1 }; - this._uniforms = { - u_texture: 0, - u_textureB: 1, - u_samples: this.properties.samples, - u_isamples: 1/this.properties.samples - }; - this.frame = 0; - } - - LGraphTextureLinearAvgSmooth.title = "Lineal Avg Smooth"; - LGraphTextureLinearAvgSmooth.desc = "Smooth texture linearly over time"; - - LGraphTextureLinearAvgSmooth["@samples"] = { type: "number", min: 1, max: 64, step: 1, precision: 1 }; - - LGraphTextureLinearAvgSmooth.prototype.getPreviewTexture = function() - { - return this._temp_texture2; - } - - LGraphTextureLinearAvgSmooth.prototype.onExecute = function() { - - var tex = this.getInputData(0); - if (!tex || !this.isOutputConnected(0)) { - return; - } - - if (!LGraphTextureLinearAvgSmooth._shader) { - LGraphTextureLinearAvgSmooth._shader_copy = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureLinearAvgSmooth.pixel_shader_copy ); - LGraphTextureLinearAvgSmooth._shader_avg = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureLinearAvgSmooth.pixel_shader_avg ); - } - - var samples = Math.clamp(this.properties.samples,0,64); - var frame = this.frame; - var interval = this.properties.frames_interval; - - if( interval == 0 || frame % interval == 0 ) - { - var temp = this._temp_texture; - if ( !temp || temp.type != tex.type || temp.width != samples ) { - var options = { - type: tex.type, - format: gl.RGBA, - filter: gl.NEAREST - }; - this._temp_texture = new GL.Texture( samples, 1, options ); - this._temp_texture2 = new GL.Texture( samples, 1, options ); - this._temp_texture_out = new GL.Texture( 1, 1, options ); - } - - var tempA = this._temp_texture; - var tempB = this._temp_texture2; - - var shader_copy = LGraphTextureLinearAvgSmooth._shader_copy; - var shader_avg = LGraphTextureLinearAvgSmooth._shader_avg; - var uniforms = this._uniforms; - uniforms.u_samples = samples; - uniforms.u_isamples = 1.0 / samples; - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - tempA.drawTo(function() { - tempB.bind(1); - tex.toViewport( shader_copy, uniforms ); - }); - - this._temp_texture_out.drawTo(function() { - tempA.toViewport( shader_avg, uniforms ); - }); - - this.setOutputData( 0, this._temp_texture_out ); - - //swap - this._temp_texture = tempB; - this._temp_texture2 = tempA; - } - else - this.setOutputData(0, this._temp_texture_out); - this.setOutputData(1, this._temp_texture2); - this.frame++; - }; - - LGraphTextureLinearAvgSmooth.pixel_shader_copy = - "precision highp float;\n\ - precision highp float;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - uniform float u_isamples;\n\ - varying vec2 v_coord;\n\ - \n\ - void main() {\n\ - if( v_coord.x <= u_isamples )\n\ - gl_FragColor = texture2D( u_texture, vec2(0.5) );\n\ - else\n\ - gl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\ - }\n\ - "; - - LGraphTextureLinearAvgSmooth.pixel_shader_avg = - "precision highp float;\n\ - precision highp float;\n\ - uniform sampler2D u_texture;\n\ - uniform int u_samples;\n\ - uniform float u_isamples;\n\ - varying vec2 v_coord;\n\ - \n\ - void main() {\n\ - vec4 color = vec4(0.0);\n\ - for(int i = 0; i < 64; ++i)\n\ - {\n\ - color += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\ - if(i == (u_samples - 1))\n\ - break;\n\ - }\n\ - gl_FragColor = color * u_isamples;\n\ - }\n\ - "; - - - LiteGraph.registerNodeType( "texture/linear_avg_smooth", LGraphTextureLinearAvgSmooth ); - - // Image To Texture ***************************************** - function LGraphImageToTexture() { - this.addInput("Image", "image"); - this.addOutput("", "Texture"); - this.properties = {}; - } - - LGraphImageToTexture.title = "Image to Texture"; - LGraphImageToTexture.desc = "Uploads an image to the GPU"; - //LGraphImageToTexture.widgets_info = { size: { widget:"combo", values:[0,32,64,128,256,512,1024,2048]} }; - - LGraphImageToTexture.prototype.onExecute = function() { - var img = this.getInputData(0); - if (!img) { - return; - } - - var width = img.videoWidth || img.width; - var height = img.videoHeight || img.height; - - //this is in case we are using a webgl canvas already, no need to reupload it - if (img.gltexture) { - this.setOutputData(0, img.gltexture); - return; - } - - var temp = this._temp_texture; - if (!temp || temp.width != width || temp.height != height) { - this._temp_texture = new GL.Texture(width, height, { - format: gl.RGBA, - filter: gl.LINEAR - }); - } - - try { - this._temp_texture.uploadImage(img); - } catch (err) { - console.error( - "image comes from an unsafe location, cannot be uploaded to webgl: " + - err - ); - return; - } - - this.setOutputData(0, this._temp_texture); - }; - - LiteGraph.registerNodeType( - "texture/imageToTexture", - LGraphImageToTexture - ); - - // Texture LUT ***************************************** - function LGraphTextureLUT() { - this.addInput("Texture", "Texture"); - this.addInput("LUT", "Texture"); - this.addInput("Intensity", "number"); - this.addOutput("", "Texture"); - this.properties = { enabled: true, intensity: 1, precision: LGraphTexture.DEFAULT, texture: null }; - - if (!LGraphTextureLUT._shader) { - LGraphTextureLUT._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, LGraphTextureLUT.pixel_shader ); - } - } - - LGraphTextureLUT.widgets_info = { - texture: { widget: "texture" }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureLUT.title = "LUT"; - LGraphTextureLUT.desc = "Apply LUT to Texture"; - - LGraphTextureLUT.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var tex = this.getInputData(0); - - if (this.properties.precision === LGraphTexture.PASS_THROUGH || this.properties.enabled === false) { - this.setOutputData(0, tex); - return; - } - - if (!tex) { - return; - } - - var lut_tex = this.getInputData(1); - - if (!lut_tex) { - lut_tex = LGraphTexture.getTexture(this.properties.texture); - } - - if (!lut_tex) { - this.setOutputData(0, tex); - return; - } - - lut_tex.bind(0); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - gl.texParameteri( - gl.TEXTURE_2D, - gl.TEXTURE_WRAP_S, - gl.CLAMP_TO_EDGE - ); - gl.texParameteri( - gl.TEXTURE_2D, - gl.TEXTURE_WRAP_T, - gl.CLAMP_TO_EDGE - ); - gl.bindTexture(gl.TEXTURE_2D, null); - - var intensity = this.properties.intensity; - if (this.isInputConnected(2)) { - this.properties.intensity = intensity = this.getInputData(2); - } - - this._tex = LGraphTexture.getTargetTexture( - tex, - this._tex, - this.properties.precision - ); - - //var mesh = Mesh.getScreenQuad(); - - this._tex.drawTo(function() { - lut_tex.bind(1); - tex.toViewport(LGraphTextureLUT._shader, { - u_texture: 0, - u_textureB: 1, - u_amount: intensity - }); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureLUT.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - uniform float u_amount;\n\ - \n\ - void main() {\n\ - lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\ - mediump float blueColor = textureColor.b * 63.0;\n\ - mediump vec2 quad1;\n\ - quad1.y = floor(floor(blueColor) / 8.0);\n\ - quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\ - mediump vec2 quad2;\n\ - quad2.y = floor(ceil(blueColor) / 8.0);\n\ - quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\ - highp vec2 texPos1;\n\ - texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\ - texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\ - highp vec2 texPos2;\n\ - texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\ - texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\ - lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\ - lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\ - lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\ - gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\ - }\n\ - "; - - LiteGraph.registerNodeType("texture/LUT", LGraphTextureLUT); - - // Texture Channels ***************************************** - function LGraphTextureChannels() { - this.addInput("Texture", "Texture"); - - this.addOutput("R", "Texture"); - this.addOutput("G", "Texture"); - this.addOutput("B", "Texture"); - this.addOutput("A", "Texture"); - - //this.properties = { use_single_channel: true }; - if (!LGraphTextureChannels._shader) { - LGraphTextureChannels._shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureChannels.pixel_shader - ); - } - } - - LGraphTextureChannels.title = "Texture to Channels"; - LGraphTextureChannels.desc = "Split texture channels"; - - LGraphTextureChannels.prototype.onExecute = function() { - var texA = this.getInputData(0); - if (!texA) { - return; - } - - if (!this._channels) { - this._channels = Array(4); - } - - //var format = this.properties.use_single_channel ? gl.LUMINANCE : gl.RGBA; //not supported by WebGL1 - var format = gl.RGB; - var connections = 0; - for (var i = 0; i < 4; i++) { - if (this.isOutputConnected(i)) { - if ( - !this._channels[i] || - this._channels[i].width != texA.width || - this._channels[i].height != texA.height || - this._channels[i].type != texA.type || - this._channels[i].format != format - ) { - this._channels[i] = new GL.Texture( - texA.width, - texA.height, - { - type: texA.type, - format: format, - filter: gl.LINEAR - } - ); - } - connections++; - } else { - this._channels[i] = null; - } - } - - if (!connections) { - return; - } - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - - var mesh = Mesh.getScreenQuad(); - var shader = LGraphTextureChannels._shader; - var masks = [ - [1, 0, 0, 0], - [0, 1, 0, 0], - [0, 0, 1, 0], - [0, 0, 0, 1] - ]; - - for (var i = 0; i < 4; i++) { - if (!this._channels[i]) { - continue; - } - - this._channels[i].drawTo(function() { - texA.bind(0); - shader - .uniforms({ u_texture: 0, u_mask: masks[i] }) - .draw(mesh); - }); - this.setOutputData(i, this._channels[i]); - } - }; - - LGraphTextureChannels.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec4 u_mask;\n\ - \n\ - void main() {\n\ - gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\ - }\n\ - "; - - LiteGraph.registerNodeType( - "texture/textureChannels", - LGraphTextureChannels - ); - - // Texture Channels to Texture ***************************************** - function LGraphChannelsTexture() { - this.addInput("R", "Texture"); - this.addInput("G", "Texture"); - this.addInput("B", "Texture"); - this.addInput("A", "Texture"); - - this.addOutput("Texture", "Texture"); - - this.properties = { - precision: LGraphTexture.DEFAULT, - R: 1, - G: 1, - B: 1, - A: 1 - }; - this._color = vec4.create(); - this._uniforms = { - u_textureR: 0, - u_textureG: 1, - u_textureB: 2, - u_textureA: 3, - u_color: this._color - }; - } - - LGraphChannelsTexture.title = "Channels to Texture"; - LGraphChannelsTexture.desc = "Split texture channels"; - LGraphChannelsTexture.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphChannelsTexture.prototype.onExecute = function() { - var white = LGraphTexture.getWhiteTexture(); - var texR = this.getInputData(0) || white; - var texG = this.getInputData(1) || white; - var texB = this.getInputData(2) || white; - var texA = this.getInputData(3) || white; - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - - var mesh = Mesh.getScreenQuad(); - if (!LGraphChannelsTexture._shader) { - LGraphChannelsTexture._shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphChannelsTexture.pixel_shader - ); - } - var shader = LGraphChannelsTexture._shader; - - var w = Math.max(texR.width, texG.width, texB.width, texA.width); - var h = Math.max( - texR.height, - texG.height, - texB.height, - texA.height - ); - var type = - this.properties.precision == LGraphTexture.HIGH - ? LGraphTexture.HIGH_PRECISION_FORMAT - : gl.UNSIGNED_BYTE; - - if ( - !this._texture || - this._texture.width != w || - this._texture.height != h || - this._texture.type != type - ) { - this._texture = new GL.Texture(w, h, { - type: type, - format: gl.RGBA, - filter: gl.LINEAR - }); - } - - var color = this._color; - color[0] = this.properties.R; - color[1] = this.properties.G; - color[2] = this.properties.B; - color[3] = this.properties.A; - var uniforms = this._uniforms; - - this._texture.drawTo(function() { - texR.bind(0); - texG.bind(1); - texB.bind(2); - texA.bind(3); - shader.uniforms(uniforms).draw(mesh); - }); - this.setOutputData(0, this._texture); - }; - - LGraphChannelsTexture.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_textureR;\n\ - uniform sampler2D u_textureG;\n\ - uniform sampler2D u_textureB;\n\ - uniform sampler2D u_textureA;\n\ - uniform vec4 u_color;\n\ - \n\ - void main() {\n\ - gl_FragColor = u_color * vec4( \ - texture2D(u_textureR, v_coord).r,\ - texture2D(u_textureG, v_coord).r,\ - texture2D(u_textureB, v_coord).r,\ - texture2D(u_textureA, v_coord).r);\n\ - }\n\ - "; - - LiteGraph.registerNodeType( - "texture/channelsTexture", - LGraphChannelsTexture - ); - - // Texture Color ***************************************** - function LGraphTextureColor() { - this.addOutput("Texture", "Texture"); - - this._tex_color = vec4.create(); - this.properties = { - color: vec4.create(), - precision: LGraphTexture.DEFAULT - }; - } - - LGraphTextureColor.title = "Color"; - LGraphTextureColor.desc = - "Generates a 1x1 texture with a constant color"; - - LGraphTextureColor.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureColor.prototype.onDrawBackground = function(ctx) { - var c = this.properties.color; - ctx.fillStyle = - "rgb(" + - Math.floor(Math.clamp(c[0], 0, 1) * 255) + - "," + - Math.floor(Math.clamp(c[1], 0, 1) * 255) + - "," + - Math.floor(Math.clamp(c[2], 0, 1) * 255) + - ")"; - if (this.flags.collapsed) { - this.boxcolor = ctx.fillStyle; - } else { - ctx.fillRect(0, 0, this.size[0], this.size[1]); - } - }; - - LGraphTextureColor.prototype.onExecute = function() { - var type = - this.properties.precision == LGraphTexture.HIGH - ? LGraphTexture.HIGH_PRECISION_FORMAT - : gl.UNSIGNED_BYTE; - - if (!this._tex || this._tex.type != type) { - this._tex = new GL.Texture(1, 1, { - format: gl.RGBA, - type: type, - minFilter: gl.NEAREST - }); - } - var color = this.properties.color; - - if (this.inputs) { - for (var i = 0; i < this.inputs.length; i++) { - var input = this.inputs[i]; - var v = this.getInputData(i); - if (v === undefined) { - continue; - } - switch (input.name) { - case "RGB": - case "RGBA": - color.set(v); - break; - case "R": - color[0] = v; - break; - case "G": - color[1] = v; - break; - case "B": - color[2] = v; - break; - case "A": - color[3] = v; - break; - } - } - } - - if (vec4.sqrDist(this._tex_color, color) > 0.001) { - this._tex_color.set(color); - this._tex.fill(color); - } - this.setOutputData(0, this._tex); - }; - - LGraphTextureColor.prototype.onGetInputs = function() { - return [ - ["RGB", "vec3"], - ["RGBA", "vec4"], - ["R", "number"], - ["G", "number"], - ["B", "number"], - ["A", "number"] - ]; - }; - - LiteGraph.registerNodeType("texture/color", LGraphTextureColor); - - // Texture Channels to Texture ***************************************** - function LGraphTextureGradient() { - this.addInput("A", "color"); - this.addInput("B", "color"); - this.addOutput("Texture", "Texture"); - - this.properties = { - angle: 0, - scale: 1, - A: [0, 0, 0], - B: [1, 1, 1], - texture_size: 32 - }; - if (!LGraphTextureGradient._shader) { - LGraphTextureGradient._shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureGradient.pixel_shader - ); - } - - this._uniforms = { - u_angle: 0, - u_colorA: vec3.create(), - u_colorB: vec3.create() - }; - } - - LGraphTextureGradient.title = "Gradient"; - LGraphTextureGradient.desc = "Generates a gradient"; - LGraphTextureGradient["@A"] = { type: "color" }; - LGraphTextureGradient["@B"] = { type: "color" }; - LGraphTextureGradient["@texture_size"] = { - type: "enum", - values: [32, 64, 128, 256, 512] - }; - - LGraphTextureGradient.prototype.onExecute = function() { - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - - var mesh = GL.Mesh.getScreenQuad(); - var shader = LGraphTextureGradient._shader; - - var A = this.getInputData(0); - if (!A) { - A = this.properties.A; - } - var B = this.getInputData(1); - if (!B) { - B = this.properties.B; - } - - //angle and scale - for (var i = 2; i < this.inputs.length; i++) { - var input = this.inputs[i]; - var v = this.getInputData(i); - if (v === undefined) { - continue; - } - this.properties[input.name] = v; - } - - var uniforms = this._uniforms; - this._uniforms.u_angle = this.properties.angle * DEG2RAD; - this._uniforms.u_scale = this.properties.scale; - vec3.copy(uniforms.u_colorA, A); - vec3.copy(uniforms.u_colorB, B); - - var size = parseInt(this.properties.texture_size); - if (!this._tex || this._tex.width != size) { - this._tex = new GL.Texture(size, size, { - format: gl.RGB, - filter: gl.LINEAR - }); - } - - this._tex.drawTo(function() { - shader.uniforms(uniforms).draw(mesh); - }); - this.setOutputData(0, this._tex); - }; - - LGraphTextureGradient.prototype.onGetInputs = function() { - return [["angle", "number"], ["scale", "number"]]; - }; - - LGraphTextureGradient.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform float u_angle;\n\ - uniform float u_scale;\n\ - uniform vec3 u_colorA;\n\ - uniform vec3 u_colorB;\n\ - \n\ - vec2 rotate(vec2 v, float angle)\n\ - {\n\ - vec2 result;\n\ - float _cos = cos(angle);\n\ - float _sin = sin(angle);\n\ - result.x = v.x * _cos - v.y * _sin;\n\ - result.y = v.x * _sin + v.y * _cos;\n\ - return result;\n\ - }\n\ - void main() {\n\ - float f = (rotate(u_scale * (v_coord - vec2(0.5)), u_angle) + vec2(0.5)).x;\n\ - vec3 color = mix(u_colorA,u_colorB,clamp(f,0.0,1.0));\n\ - gl_FragColor = vec4(color,1.0);\n\ - }\n\ - "; - - LiteGraph.registerNodeType("texture/gradient", LGraphTextureGradient); - - // Texture Mix ***************************************** - function LGraphTextureMix() { - this.addInput("A", "Texture"); - this.addInput("B", "Texture"); - this.addInput("Mixer", "Texture"); - - this.addOutput("Texture", "Texture"); - this.properties = { factor: 0.5, size_from_biggest: true, invert: false, precision: LGraphTexture.DEFAULT }; - this._uniforms = { - u_textureA: 0, - u_textureB: 1, - u_textureMix: 2, - u_mix: vec4.create() - }; - } - - LGraphTextureMix.title = "Mix"; - LGraphTextureMix.desc = "Generates a texture mixing two textures"; - - LGraphTextureMix.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureMix.prototype.onExecute = function() { - var texA = this.getInputData(0); - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, texA); - return; - } - - var texB = this.getInputData(1); - if (!texA || !texB) { - return; - } - - var texMix = this.getInputData(2); - - var factor = this.getInputData(3); - - this._tex = LGraphTexture.getTargetTexture( - this.properties.size_from_biggest && texB.width > texA.width ? texB : texA, - this._tex, - this.properties.precision - ); - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - - var mesh = Mesh.getScreenQuad(); - var shader = null; - var uniforms = this._uniforms; - if (texMix) { - shader = LGraphTextureMix._shader_tex; - if (!shader) { - shader = LGraphTextureMix._shader_tex = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureMix.pixel_shader, - { MIX_TEX: "" } - ); - } - } else { - shader = LGraphTextureMix._shader_factor; - if (!shader) { - shader = LGraphTextureMix._shader_factor = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureMix.pixel_shader - ); - } - var f = factor == null ? this.properties.factor : factor; - uniforms.u_mix.set([f, f, f, f]); - } - - var invert = this.properties.invert; - - this._tex.drawTo(function() { - texA.bind( invert ? 1 : 0 ); - texB.bind( invert ? 0 : 1 ); - if (texMix) { - texMix.bind(2); - } - shader.uniforms(uniforms).draw(mesh); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureMix.prototype.onGetInputs = function() { - return [["factor", "number"]]; - }; - - LGraphTextureMix.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_textureA;\n\ - uniform sampler2D u_textureB;\n\ - #ifdef MIX_TEX\n\ - uniform sampler2D u_textureMix;\n\ - #else\n\ - uniform vec4 u_mix;\n\ - #endif\n\ - \n\ - void main() {\n\ - #ifdef MIX_TEX\n\ - vec4 f = texture2D(u_textureMix, v_coord);\n\ - #else\n\ - vec4 f = u_mix;\n\ - #endif\n\ - gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\ - }\n\ - "; - - LiteGraph.registerNodeType("texture/mix", LGraphTextureMix); - - // Texture Edges detection ***************************************** - function LGraphTextureEdges() { - this.addInput("Tex.", "Texture"); - - this.addOutput("Edges", "Texture"); - this.properties = { - invert: true, - threshold: false, - factor: 1, - precision: LGraphTexture.DEFAULT - }; - - if (!LGraphTextureEdges._shader) { - LGraphTextureEdges._shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureEdges.pixel_shader - ); - } - } - - LGraphTextureEdges.title = "Edges"; - LGraphTextureEdges.desc = "Detects edges"; - - LGraphTextureEdges.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureEdges.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var tex = this.getInputData(0); - - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, tex); - return; - } - - if (!tex) { - return; - } - - this._tex = LGraphTexture.getTargetTexture( - tex, - this._tex, - this.properties.precision - ); - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - - var mesh = Mesh.getScreenQuad(); - var shader = LGraphTextureEdges._shader; - var invert = this.properties.invert; - var factor = this.properties.factor; - var threshold = this.properties.threshold ? 1 : 0; - - this._tex.drawTo(function() { - tex.bind(0); - shader - .uniforms({ - u_texture: 0, - u_isize: [1 / tex.width, 1 / tex.height], - u_factor: factor, - u_threshold: threshold, - u_invert: invert ? 1 : 0 - }) - .draw(mesh); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureEdges.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_isize;\n\ - uniform int u_invert;\n\ - uniform float u_factor;\n\ - uniform float u_threshold;\n\ - \n\ - void main() {\n\ - vec4 center = texture2D(u_texture, v_coord);\n\ - vec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\ - vec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\ - vec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\ - vec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\ - vec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\ - diff *= u_factor;\n\ - if(u_invert == 1)\n\ - diff.xyz = vec3(1.0) - diff.xyz;\n\ - if( u_threshold == 0.0 )\n\ - gl_FragColor = vec4( diff.xyz, center.a );\n\ - else\n\ - gl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\ - }\n\ - "; - - LiteGraph.registerNodeType("texture/edges", LGraphTextureEdges); - - // Texture Depth ***************************************** - function LGraphTextureDepthRange() { - this.addInput("Texture", "Texture"); - this.addInput("Distance", "number"); - this.addInput("Range", "number"); - this.addOutput("Texture", "Texture"); - this.properties = { - distance: 100, - range: 50, - only_depth: false, - high_precision: false - }; - this._uniforms = { - u_texture: 0, - u_distance: 100, - u_range: 50, - u_camera_planes: null - }; - } - - LGraphTextureDepthRange.title = "Depth Range"; - LGraphTextureDepthRange.desc = "Generates a texture with a depth range"; - - LGraphTextureDepthRange.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var tex = this.getInputData(0); - if (!tex) { - return; - } - - var precision = gl.UNSIGNED_BYTE; - if (this.properties.high_precision) { - precision = gl.half_float_ext ? gl.HALF_FLOAT_OES : gl.FLOAT; - } - - if ( - !this._temp_texture || - this._temp_texture.type != precision || - this._temp_texture.width != tex.width || - this._temp_texture.height != tex.height - ) { - this._temp_texture = new GL.Texture(tex.width, tex.height, { - type: precision, - format: gl.RGBA, - filter: gl.LINEAR - }); - } - - var uniforms = this._uniforms; - - //iterations - var distance = this.properties.distance; - if (this.isInputConnected(1)) { - distance = this.getInputData(1); - this.properties.distance = distance; - } - - var range = this.properties.range; - if (this.isInputConnected(2)) { - range = this.getInputData(2); - this.properties.range = range; - } - - uniforms.u_distance = distance; - uniforms.u_range = range; - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - var mesh = Mesh.getScreenQuad(); - if (!LGraphTextureDepthRange._shader) { - LGraphTextureDepthRange._shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureDepthRange.pixel_shader - ); - LGraphTextureDepthRange._shader_onlydepth = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureDepthRange.pixel_shader, - { ONLY_DEPTH: "" } - ); - } - var shader = this.properties.only_depth - ? LGraphTextureDepthRange._shader_onlydepth - : LGraphTextureDepthRange._shader; - - //NEAR AND FAR PLANES - var planes = null; - if (tex.near_far_planes) { - planes = tex.near_far_planes; - } else if (window.LS && LS.Renderer._main_camera) { - planes = LS.Renderer._main_camera._uniforms.u_camera_planes; - } else { - planes = [0.1, 1000]; - } //hardcoded - uniforms.u_camera_planes = planes; - - this._temp_texture.drawTo(function() { - tex.bind(0); - shader.uniforms(uniforms).draw(mesh); - }); - - this._temp_texture.near_far_planes = planes; - this.setOutputData(0, this._temp_texture); - }; - - LGraphTextureDepthRange.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_camera_planes;\n\ - uniform float u_distance;\n\ - uniform float u_range;\n\ - \n\ - float LinearDepth()\n\ - {\n\ - float zNear = u_camera_planes.x;\n\ - float zFar = u_camera_planes.y;\n\ - float depth = texture2D(u_texture, v_coord).x;\n\ - depth = depth * 2.0 - 1.0;\n\ - return zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\ - }\n\ - \n\ - void main() {\n\ - float depth = LinearDepth();\n\ - #ifdef ONLY_DEPTH\n\ - gl_FragColor = vec4(depth);\n\ - #else\n\ - float diff = abs(depth * u_camera_planes.y - u_distance);\n\ - float dof = 1.0;\n\ - if(diff <= u_range)\n\ - dof = diff / u_range;\n\ - gl_FragColor = vec4(dof);\n\ - #endif\n\ - }\n\ - "; - - LiteGraph.registerNodeType( "texture/depth_range", LGraphTextureDepthRange ); - - - // Texture Depth ***************************************** - function LGraphTextureLinearDepth() { - this.addInput("Texture", "Texture"); - this.addOutput("Texture", "Texture"); - this.properties = { - precision: LGraphTexture.DEFAULT, - invert: false - }; - this._uniforms = { - u_texture: 0, - u_near: 0.1, - u_far: 10000 - }; - } - - LGraphTextureLinearDepth.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureLinearDepth.title = "Linear Depth"; - LGraphTextureLinearDepth.desc = "Creates a color texture with linear depth"; - - LGraphTextureLinearDepth.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var tex = this.getInputData(0); - if (!tex || (tex.format != gl.DEPTH_COMPONENT && tex.format != gl.DEPTH_STENCIL) ) { - return; - } - - var precision = this.properties.precision == LGraphTexture.HIGH ? gl.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE; - - if ( !this._temp_texture || this._temp_texture.type != precision || this._temp_texture.width != tex.width || this._temp_texture.height != tex.height ) { - this._temp_texture = new GL.Texture(tex.width, tex.height, { - type: precision, - format: gl.RGB, - filter: gl.LINEAR - }); - } - - var uniforms = this._uniforms; - - uniforms.u_near = tex.near_far_planes[0]; - uniforms.u_far = tex.near_far_planes[1]; - uniforms.u_invert = this.properties.invert ? 1 : 0; - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - var mesh = Mesh.getScreenQuad(); - if(!LGraphTextureLinearDepth._shader) - LGraphTextureLinearDepth._shader = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureLinearDepth.pixel_shader); - var shader = LGraphTextureLinearDepth._shader; - - //NEAR AND FAR PLANES - var planes = null; - if (tex.near_far_planes) { - planes = tex.near_far_planes; - } else if (window.LS && LS.Renderer._main_camera) { - planes = LS.Renderer._main_camera._uniforms.u_camera_planes; - } else { - planes = [0.1, 1000]; - } //hardcoded - uniforms.u_camera_planes = planes; - - this._temp_texture.drawTo(function() { - tex.bind(0); - shader.uniforms(uniforms).draw(mesh); - }); - - this._temp_texture.near_far_planes = planes; - this.setOutputData(0, this._temp_texture); - }; - - LGraphTextureLinearDepth.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_near;\n\ - uniform float u_far;\n\ - uniform int u_invert;\n\ - \n\ - void main() {\n\ - float zNear = u_near;\n\ - float zFar = u_far;\n\ - float depth = texture2D(u_texture, v_coord).x;\n\ - depth = depth * 2.0 - 1.0;\n\ - float f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\ - if( u_invert == 1 )\n\ - f = 1.0 - f;\n\ - gl_FragColor = vec4(vec3(f),1.0);\n\ - }\n\ - "; - - LiteGraph.registerNodeType( "texture/linear_depth", LGraphTextureLinearDepth ); - - // Texture Blur ***************************************** - function LGraphTextureBlur() { - this.addInput("Texture", "Texture"); - this.addInput("Iterations", "number"); - this.addInput("Intensity", "number"); - this.addOutput("Blurred", "Texture"); - this.properties = { - intensity: 1, - iterations: 1, - preserve_aspect: false, - scale: [1, 1], - precision: LGraphTexture.DEFAULT - }; - } - - LGraphTextureBlur.title = "Blur"; - LGraphTextureBlur.desc = "Blur a texture"; - - LGraphTextureBlur.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureBlur.max_iterations = 20; - - LGraphTextureBlur.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var temp = this._final_texture; - - if ( - !temp || - temp.width != tex.width || - temp.height != tex.height || - temp.type != tex.type - ) { - //we need two textures to do the blurring - //this._temp_texture = new GL.Texture( tex.width, tex.height, { type: tex.type, format: gl.RGBA, filter: gl.LINEAR }); - temp = this._final_texture = new GL.Texture( - tex.width, - tex.height, - { type: tex.type, format: gl.RGBA, filter: gl.LINEAR } - ); - } - - //iterations - var iterations = this.properties.iterations; - if (this.isInputConnected(1)) { - iterations = this.getInputData(1); - this.properties.iterations = iterations; - } - iterations = Math.min( - Math.floor(iterations), - LGraphTextureBlur.max_iterations - ); - if (iterations == 0) { - //skip blurring - this.setOutputData(0, tex); - return; - } - - var intensity = this.properties.intensity; - if (this.isInputConnected(2)) { - intensity = this.getInputData(2); - this.properties.intensity = intensity; - } - - //blur sometimes needs an aspect correction - var aspect = LiteGraph.camera_aspect; - if (!aspect && window.gl !== undefined) { - aspect = gl.canvas.height / gl.canvas.width; - } - if (!aspect) { - aspect = 1; - } - aspect = this.properties.preserve_aspect ? aspect : 1; - - var scale = this.properties.scale || [1, 1]; - tex.applyBlur(aspect * scale[0], scale[1], intensity, temp); - for (var i = 1; i < iterations; ++i) { - temp.applyBlur( - aspect * scale[0] * (i + 1), - scale[1] * (i + 1), - intensity - ); - } - - this.setOutputData(0, temp); - }; - - /* -LGraphTextureBlur.pixel_shader = "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_offset;\n\ - uniform float u_intensity;\n\ - void main() {\n\ - vec4 sum = vec4(0.0);\n\ - vec4 center = texture2D(u_texture, v_coord);\n\ - sum += texture2D(u_texture, v_coord + u_offset * -4.0) * 0.05/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * -3.0) * 0.09/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * -2.0) * 0.12/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * -1.0) * 0.15/0.98;\n\ - sum += center * 0.16/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * 4.0) * 0.05/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * 3.0) * 0.09/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * 2.0) * 0.12/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * 1.0) * 0.15/0.98;\n\ - gl_FragColor = u_intensity * sum;\n\ - }\n\ - "; -*/ - - LiteGraph.registerNodeType("texture/blur", LGraphTextureBlur); - - // Texture Glow ***************************************** - //based in https://catlikecoding.com/unity/tutorials/advanced-rendering/bloom/ - function LGraphTextureGlow() { - this.addInput("in", "Texture"); - this.addInput("dirt", "Texture"); - this.addOutput("out", "Texture"); - this.addOutput("glow", "Texture"); - this.properties = { - enabled: true, - intensity: 1, - persistence: 0.99, - iterations: 16, - threshold: 0, - scale: 1, - dirt_factor: 0.5, - precision: LGraphTexture.DEFAULT - }; - this._textures = []; - this._uniforms = { - u_intensity: 1, - u_texture: 0, - u_glow_texture: 1, - u_threshold: 0, - u_texel_size: vec2.create() - }; - } - - LGraphTextureGlow.title = "Glow"; - LGraphTextureGlow.desc = "Filters a texture giving it a glow effect"; - LGraphTextureGlow.weights = new Float32Array([0.5, 0.4, 0.3, 0.2]); - - LGraphTextureGlow.widgets_info = { - iterations: { - type: "number", - min: 0, - max: 16, - step: 1, - precision: 0 - }, - threshold: { - type: "number", - min: 0, - max: 10, - step: 0.01, - precision: 2 - }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureGlow.prototype.onGetInputs = function() { - return [ - ["enabled", "boolean"], - ["threshold", "number"], - ["intensity", "number"], - ["persistence", "number"], - ["iterations", "number"], - ["dirt_factor", "number"] - ]; - }; - - LGraphTextureGlow.prototype.onGetOutputs = function() { - return [["average", "Texture"]]; - }; - - LGraphTextureGlow.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if (!this.isAnyOutputConnected()) { - return; - } //saves work - - if ( - this.properties.precision === LGraphTexture.PASS_THROUGH || - this.getInputOrProperty("enabled") === false - ) { - this.setOutputData(0, tex); - return; - } - - var width = tex.width; - var height = tex.height; - - var texture_info = { - format: tex.format, - type: tex.type, - minFilter: GL.LINEAR, - magFilter: GL.LINEAR, - wrap: gl.CLAMP_TO_EDGE - }; - var type = LGraphTexture.getTextureType( - this.properties.precision, - tex - ); - - var uniforms = this._uniforms; - var textures = this._textures; - - //cut - var shader = LGraphTextureGlow._cut_shader; - if (!shader) { - shader = LGraphTextureGlow._cut_shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureGlow.cut_pixel_shader - ); - } - - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.BLEND); - - uniforms.u_threshold = this.getInputOrProperty("threshold"); - var currentDestination = (textures[0] = GL.Texture.getTemporary( - width, - height, - texture_info - )); - tex.blit(currentDestination, shader.uniforms(uniforms)); - var currentSource = currentDestination; - - var iterations = this.getInputOrProperty("iterations"); - iterations = Math.clamp(iterations, 1, 16) | 0; - var texel_size = uniforms.u_texel_size; - var intensity = this.getInputOrProperty("intensity"); - - uniforms.u_intensity = 1; - uniforms.u_delta = this.properties.scale; //1 - - //downscale/upscale shader - var shader = LGraphTextureGlow._shader; - if (!shader) { - shader = LGraphTextureGlow._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureGlow.scale_pixel_shader - ); - } - - var i = 1; - //downscale - for (; i < iterations; i++) { - width = width >> 1; - if ((height | 0) > 1) { - height = height >> 1; - } - if (width < 2) { - break; - } - currentDestination = textures[i] = GL.Texture.getTemporary( - width, - height, - texture_info - ); - texel_size[0] = 1 / currentSource.width; - texel_size[1] = 1 / currentSource.height; - currentSource.blit( - currentDestination, - shader.uniforms(uniforms) - ); - currentSource = currentDestination; - } - - //average - if (this.isOutputConnected(2)) { - var average_texture = this._average_texture; - if ( - !average_texture || - average_texture.type != tex.type || - average_texture.format != tex.format - ) { - average_texture = this._average_texture = new GL.Texture( - 1, - 1, - { - type: tex.type, - format: tex.format, - filter: gl.LINEAR - } - ); - } - texel_size[0] = 1 / currentSource.width; - texel_size[1] = 1 / currentSource.height; - uniforms.u_intensity = intensity; - uniforms.u_delta = 1; - currentSource.blit(average_texture, shader.uniforms(uniforms)); - this.setOutputData(2, average_texture); - } - - //upscale and blend - gl.enable(gl.BLEND); - gl.blendFunc(gl.ONE, gl.ONE); - uniforms.u_intensity = this.getInputOrProperty("persistence"); - uniforms.u_delta = 0.5; - - for ( - i -= 2; - i >= 0; - i-- // i-=2 => -1 to point to last element in array, -1 to go to texture above - ) { - currentDestination = textures[i]; - textures[i] = null; - texel_size[0] = 1 / currentSource.width; - texel_size[1] = 1 / currentSource.height; - currentSource.blit( - currentDestination, - shader.uniforms(uniforms) - ); - GL.Texture.releaseTemporary(currentSource); - currentSource = currentDestination; - } - gl.disable(gl.BLEND); - - //glow - if (this.isOutputConnected(1)) { - var glow_texture = this._glow_texture; - if ( - !glow_texture || - glow_texture.width != tex.width || - glow_texture.height != tex.height || - glow_texture.type != type || - glow_texture.format != tex.format - ) { - glow_texture = this._glow_texture = new GL.Texture( - tex.width, - tex.height, - { type: type, format: tex.format, filter: gl.LINEAR } - ); - } - currentSource.blit(glow_texture); - this.setOutputData(1, glow_texture); - } - - //final composition - if (this.isOutputConnected(0)) { - var final_texture = this._final_texture; - if ( - !final_texture || - final_texture.width != tex.width || - final_texture.height != tex.height || - final_texture.type != type || - final_texture.format != tex.format - ) { - final_texture = this._final_texture = new GL.Texture( - tex.width, - tex.height, - { type: type, format: tex.format, filter: gl.LINEAR } - ); - } - - var dirt_texture = this.getInputData(1); - var dirt_factor = this.getInputOrProperty("dirt_factor"); - - uniforms.u_intensity = intensity; - - shader = dirt_texture - ? LGraphTextureGlow._dirt_final_shader - : LGraphTextureGlow._final_shader; - if (!shader) { - if (dirt_texture) { - shader = LGraphTextureGlow._dirt_final_shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureGlow.final_pixel_shader, - { USE_DIRT: "" } - ); - } else { - shader = LGraphTextureGlow._final_shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureGlow.final_pixel_shader - ); - } - } - - final_texture.drawTo(function() { - tex.bind(0); - currentSource.bind(1); - if (dirt_texture) { - shader.setUniform("u_dirt_factor", dirt_factor); - shader.setUniform( - "u_dirt_texture", - dirt_texture.bind(2) - ); - } - shader.toViewport(uniforms); - }); - this.setOutputData(0, final_texture); - } - - GL.Texture.releaseTemporary(currentSource); - }; - - LGraphTextureGlow.cut_pixel_shader = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_threshold;\n\ - void main() {\n\ - gl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\ - }"; - - LGraphTextureGlow.scale_pixel_shader = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_texel_size;\n\ - uniform float u_delta;\n\ - uniform float u_intensity;\n\ - \n\ - vec4 sampleBox(vec2 uv) {\n\ - vec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\ - vec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\ - return s * 0.25;\n\ - }\n\ - void main() {\n\ - gl_FragColor = u_intensity * sampleBox( v_coord );\n\ - }"; - - LGraphTextureGlow.final_pixel_shader = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_glow_texture;\n\ - #ifdef USE_DIRT\n\ - uniform sampler2D u_dirt_texture;\n\ - #endif\n\ - uniform vec2 u_texel_size;\n\ - uniform float u_delta;\n\ - uniform float u_intensity;\n\ - uniform float u_dirt_factor;\n\ - \n\ - vec4 sampleBox(vec2 uv) {\n\ - vec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\ - vec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\ - return s * 0.25;\n\ - }\n\ - void main() {\n\ - vec4 glow = sampleBox( v_coord );\n\ - #ifdef USE_DIRT\n\ - glow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\ - #endif\n\ - gl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\ - }"; - - LiteGraph.registerNodeType("texture/glow", LGraphTextureGlow); - - // Texture Filter ***************************************** - function LGraphTextureKuwaharaFilter() { - this.addInput("Texture", "Texture"); - this.addOutput("Filtered", "Texture"); - this.properties = { intensity: 1, radius: 5 }; - } - - LGraphTextureKuwaharaFilter.title = "Kuwahara Filter"; - LGraphTextureKuwaharaFilter.desc = - "Filters a texture giving an artistic oil canvas painting"; - - LGraphTextureKuwaharaFilter.max_radius = 10; - LGraphTextureKuwaharaFilter._shaders = []; - - LGraphTextureKuwaharaFilter.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var temp = this._temp_texture; - - if ( - !temp || - temp.width != tex.width || - temp.height != tex.height || - temp.type != tex.type - ) { - this._temp_texture = new GL.Texture(tex.width, tex.height, { - type: tex.type, - format: gl.RGBA, - filter: gl.LINEAR - }); - } - - //iterations - var radius = this.properties.radius; - radius = Math.min( - Math.floor(radius), - LGraphTextureKuwaharaFilter.max_radius - ); - if (radius == 0) { - //skip blurring - this.setOutputData(0, tex); - return; - } - - var intensity = this.properties.intensity; - - //blur sometimes needs an aspect correction - var aspect = LiteGraph.camera_aspect; - if (!aspect && window.gl !== undefined) { - aspect = gl.canvas.height / gl.canvas.width; - } - if (!aspect) { - aspect = 1; - } - aspect = this.properties.preserve_aspect ? aspect : 1; - - if (!LGraphTextureKuwaharaFilter._shaders[radius]) { - LGraphTextureKuwaharaFilter._shaders[radius] = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureKuwaharaFilter.pixel_shader, - { RADIUS: radius.toFixed(0) } - ); - } - - var shader = LGraphTextureKuwaharaFilter._shaders[radius]; - var mesh = GL.Mesh.getScreenQuad(); - tex.bind(0); - - this._temp_texture.drawTo(function() { - shader - .uniforms({ - u_texture: 0, - u_intensity: intensity, - u_resolution: [tex.width, tex.height], - u_iResolution: [1 / tex.width, 1 / tex.height] - }) - .draw(mesh); - }); - - this.setOutputData(0, this._temp_texture); - }; - - //from https://www.shadertoy.com/view/MsXSz4 - LGraphTextureKuwaharaFilter.pixel_shader = - "\n\ -precision highp float;\n\ -varying vec2 v_coord;\n\ -uniform sampler2D u_texture;\n\ -uniform float u_intensity;\n\ -uniform vec2 u_resolution;\n\ -uniform vec2 u_iResolution;\n\ -#ifndef RADIUS\n\ - #define RADIUS 7\n\ -#endif\n\ -void main() {\n\ -\n\ - const int radius = RADIUS;\n\ - vec2 fragCoord = v_coord;\n\ - vec2 src_size = u_iResolution;\n\ - vec2 uv = v_coord;\n\ - float n = float((radius + 1) * (radius + 1));\n\ - int i;\n\ - int j;\n\ - vec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\ - vec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\ - vec3 c;\n\ - \n\ - for (int j = -radius; j <= 0; ++j) {\n\ - for (int i = -radius; i <= 0; ++i) {\n\ - c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ - m0 += c;\n\ - s0 += c * c;\n\ - }\n\ - }\n\ - \n\ - for (int j = -radius; j <= 0; ++j) {\n\ - for (int i = 0; i <= radius; ++i) {\n\ - c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ - m1 += c;\n\ - s1 += c * c;\n\ - }\n\ - }\n\ - \n\ - for (int j = 0; j <= radius; ++j) {\n\ - for (int i = 0; i <= radius; ++i) {\n\ - c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ - m2 += c;\n\ - s2 += c * c;\n\ - }\n\ - }\n\ - \n\ - for (int j = 0; j <= radius; ++j) {\n\ - for (int i = -radius; i <= 0; ++i) {\n\ - c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ - m3 += c;\n\ - s3 += c * c;\n\ - }\n\ - }\n\ - \n\ - float min_sigma2 = 1e+2;\n\ - m0 /= n;\n\ - s0 = abs(s0 / n - m0 * m0);\n\ - \n\ - float sigma2 = s0.r + s0.g + s0.b;\n\ - if (sigma2 < min_sigma2) {\n\ - min_sigma2 = sigma2;\n\ - gl_FragColor = vec4(m0, 1.0);\n\ - }\n\ - \n\ - m1 /= n;\n\ - s1 = abs(s1 / n - m1 * m1);\n\ - \n\ - sigma2 = s1.r + s1.g + s1.b;\n\ - if (sigma2 < min_sigma2) {\n\ - min_sigma2 = sigma2;\n\ - gl_FragColor = vec4(m1, 1.0);\n\ - }\n\ - \n\ - m2 /= n;\n\ - s2 = abs(s2 / n - m2 * m2);\n\ - \n\ - sigma2 = s2.r + s2.g + s2.b;\n\ - if (sigma2 < min_sigma2) {\n\ - min_sigma2 = sigma2;\n\ - gl_FragColor = vec4(m2, 1.0);\n\ - }\n\ - \n\ - m3 /= n;\n\ - s3 = abs(s3 / n - m3 * m3);\n\ - \n\ - sigma2 = s3.r + s3.g + s3.b;\n\ - if (sigma2 < min_sigma2) {\n\ - min_sigma2 = sigma2;\n\ - gl_FragColor = vec4(m3, 1.0);\n\ - }\n\ -}\n\ -"; - - LiteGraph.registerNodeType( - "texture/kuwahara", - LGraphTextureKuwaharaFilter - ); - - // Texture ***************************************** - function LGraphTextureXDoGFilter() { - this.addInput("Texture", "Texture"); - this.addOutput("Filtered", "Texture"); - this.properties = { - sigma: 1.4, - k: 1.6, - p: 21.7, - epsilon: 79, - phi: 0.017 - }; - } - - LGraphTextureXDoGFilter.title = "XDoG Filter"; - LGraphTextureXDoGFilter.desc = - "Filters a texture giving an artistic ink style"; - - LGraphTextureXDoGFilter.max_radius = 10; - LGraphTextureXDoGFilter._shaders = []; - - LGraphTextureXDoGFilter.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var temp = this._temp_texture; - if ( - !temp || - temp.width != tex.width || - temp.height != tex.height || - temp.type != tex.type - ) { - this._temp_texture = new GL.Texture(tex.width, tex.height, { - type: tex.type, - format: gl.RGBA, - filter: gl.LINEAR - }); - } - - if (!LGraphTextureXDoGFilter._xdog_shader) { - LGraphTextureXDoGFilter._xdog_shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureXDoGFilter.xdog_pixel_shader - ); - } - var shader = LGraphTextureXDoGFilter._xdog_shader; - var mesh = GL.Mesh.getScreenQuad(); - - var sigma = this.properties.sigma; - var k = this.properties.k; - var p = this.properties.p; - var epsilon = this.properties.epsilon; - var phi = this.properties.phi; - tex.bind(0); - this._temp_texture.drawTo(function() { - shader - .uniforms({ - src: 0, - sigma: sigma, - k: k, - p: p, - epsilon: epsilon, - phi: phi, - cvsWidth: tex.width, - cvsHeight: tex.height - }) - .draw(mesh); - }); - - this.setOutputData(0, this._temp_texture); - }; - - //from https://github.com/RaymondMcGuire/GPU-Based-Image-Processing-Tools/blob/master/lib_webgl/scripts/main.js - LGraphTextureXDoGFilter.xdog_pixel_shader = - "\n\ -precision highp float;\n\ -uniform sampler2D src;\n\n\ -uniform float cvsHeight;\n\ -uniform float cvsWidth;\n\n\ -uniform float sigma;\n\ -uniform float k;\n\ -uniform float p;\n\ -uniform float epsilon;\n\ -uniform float phi;\n\ -varying vec2 v_coord;\n\n\ -float cosh(float val)\n\ -{\n\ - float tmp = exp(val);\n\ - float cosH = (tmp + 1.0 / tmp) / 2.0;\n\ - return cosH;\n\ -}\n\n\ -float tanh(float val)\n\ -{\n\ - float tmp = exp(val);\n\ - float tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\ - return tanH;\n\ -}\n\n\ -float sinh(float val)\n\ -{\n\ - float tmp = exp(val);\n\ - float sinH = (tmp - 1.0 / tmp) / 2.0;\n\ - return sinH;\n\ -}\n\n\ -void main(void){\n\ - vec3 destColor = vec3(0.0);\n\ - float tFrag = 1.0 / cvsHeight;\n\ - float sFrag = 1.0 / cvsWidth;\n\ - vec2 Frag = vec2(sFrag,tFrag);\n\ - vec2 uv = gl_FragCoord.st;\n\ - float twoSigmaESquared = 2.0 * sigma * sigma;\n\ - float twoSigmaRSquared = twoSigmaESquared * k * k;\n\ - int halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\ - const int MAX_NUM_ITERATION = 99999;\n\ - vec2 sum = vec2(0.0);\n\ - vec2 norm = vec2(0.0);\n\n\ - for(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\ - int i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\ - int j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\ - float d = length(vec2(i,j));\n\ - vec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\ - exp( -d * d / twoSigmaRSquared ));\n\n\ - vec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\ - norm += kernel;\n\ - sum += kernel * L;\n\ - }\n\n\ - sum /= norm;\n\n\ - float H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\ - float edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\ - destColor = vec3(edge);\n\ - gl_FragColor = vec4(destColor, 1.0);\n\ -}"; - - LiteGraph.registerNodeType("texture/xDoG", LGraphTextureXDoGFilter); - - // Texture Webcam ***************************************** - function LGraphTextureWebcam() { - this.addOutput("Webcam", "Texture"); - this.properties = { texture_name: "", facingMode: "user" }; - this.boxcolor = "black"; - this.version = 0; - } - - LGraphTextureWebcam.title = "Webcam"; - LGraphTextureWebcam.desc = "Webcam texture"; - - LGraphTextureWebcam.is_webcam_open = false; - - LGraphTextureWebcam.prototype.openStream = function() { - if (!navigator.getUserMedia) { - //console.log('getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags'); - return; - } - - this._waiting_confirmation = true; - - // Not showing vendor prefixes. - var constraints = { - audio: false, - video: { facingMode: this.properties.facingMode } - }; - navigator.mediaDevices - .getUserMedia(constraints) - .then(this.streamReady.bind(this)) - .catch(onFailSoHard); - - var that = this; - function onFailSoHard(e) { - LGraphTextureWebcam.is_webcam_open = false; - console.log("Webcam rejected", e); - that._webcam_stream = false; - that.boxcolor = "red"; - that.trigger("stream_error"); - } - }; - - LGraphTextureWebcam.prototype.closeStream = function() { - if (this._webcam_stream) { - var tracks = this._webcam_stream.getTracks(); - if (tracks.length) { - for (var i = 0; i < tracks.length; ++i) { - tracks[i].stop(); - } - } - LGraphTextureWebcam.is_webcam_open = false; - this._webcam_stream = null; - this._video = null; - this.boxcolor = "black"; - this.trigger("stream_closed"); - } - }; - - LGraphTextureWebcam.prototype.streamReady = function(localMediaStream) { - this._webcam_stream = localMediaStream; - //this._waiting_confirmation = false; - this.boxcolor = "green"; - var video = this._video; - if (!video) { - video = document.createElement("video"); - video.autoplay = true; - video.srcObject = localMediaStream; - this._video = video; - //document.body.appendChild( video ); //debug - //when video info is loaded (size and so) - video.onloadedmetadata = function(e) { - // Ready to go. Do some stuff. - LGraphTextureWebcam.is_webcam_open = true; - console.log(e); - }; - } - this.trigger("stream_ready", video); - }; - - LGraphTextureWebcam.prototype.onPropertyChanged = function( - name, - value - ) { - if (name == "facingMode") { - this.properties.facingMode = value; - this.closeStream(); - this.openStream(); - } - }; - - LGraphTextureWebcam.prototype.onRemoved = function() { - if (!this._webcam_stream) { - return; - } - - var tracks = this._webcam_stream.getTracks(); - if (tracks.length) { - for (var i = 0; i < tracks.length; ++i) { - tracks[i].stop(); - } - } - - this._webcam_stream = null; - this._video = null; - }; - - LGraphTextureWebcam.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed || this.size[1] <= 20) { - return; - } - - if (!this._video) { - return; - } - - //render to graph canvas - ctx.save(); - if (!ctx.webgl) { - //reverse image - ctx.drawImage(this._video, 0, 0, this.size[0], this.size[1]); - } else { - if (this._video_texture) { - ctx.drawImage( - this._video_texture, - 0, - 0, - this.size[0], - this.size[1] - ); - } - } - ctx.restore(); - }; - - LGraphTextureWebcam.prototype.onExecute = function() { - if (this._webcam_stream == null && !this._waiting_confirmation) { - this.openStream(); - } - - if (!this._video || !this._video.videoWidth) { - return; - } - - var width = this._video.videoWidth; - var height = this._video.videoHeight; - - var temp = this._video_texture; - if (!temp || temp.width != width || temp.height != height) { - this._video_texture = new GL.Texture(width, height, { - format: gl.RGB, - filter: gl.LINEAR - }); - } - - this._video_texture.uploadImage(this._video); - this._video_texture.version = ++this.version; - - if (this.properties.texture_name) { - var container = LGraphTexture.getTexturesContainer(); - container[this.properties.texture_name] = this._video_texture; - } - - this.setOutputData(0, this._video_texture); - for (var i = 1; i < this.outputs.length; ++i) { - if (!this.outputs[i]) { - continue; - } - switch (this.outputs[i].name) { - case "width": - this.setOutputData(i, this._video.videoWidth); - break; - case "height": - this.setOutputData(i, this._video.videoHeight); - break; - } - } - }; - - LGraphTextureWebcam.prototype.onGetOutputs = function() { - return [ - ["width", "number"], - ["height", "number"], - ["stream_ready", LiteGraph.EVENT], - ["stream_closed", LiteGraph.EVENT], - ["stream_error", LiteGraph.EVENT] - ]; - }; - - LiteGraph.registerNodeType("texture/webcam", LGraphTextureWebcam); - - //from https://github.com/spite/Wagner - function LGraphLensFX() { - this.addInput("in", "Texture"); - this.addInput("f", "number"); - this.addOutput("out", "Texture"); - this.properties = { - enabled: true, - factor: 1, - precision: LGraphTexture.LOW - }; - - this._uniforms = { u_texture: 0, u_factor: 1 }; - } - - LGraphLensFX.title = "Lens FX"; - LGraphLensFX.desc = "distortion and chromatic aberration"; - - LGraphLensFX.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphLensFX.prototype.onGetInputs = function() { - return [["enabled", "boolean"]]; - }; - - LGraphLensFX.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - if ( - this.properties.precision === LGraphTexture.PASS_THROUGH || - this.getInputOrProperty("enabled") === false - ) { - this.setOutputData(0, tex); - return; - } - - var temp = this._temp_texture; - if ( - !temp || - temp.width != tex.width || - temp.height != tex.height || - temp.type != tex.type - ) { - temp = this._temp_texture = new GL.Texture( - tex.width, - tex.height, - { type: tex.type, format: gl.RGBA, filter: gl.LINEAR } - ); - } - - var shader = LGraphLensFX._shader; - if (!shader) { - shader = LGraphLensFX._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphLensFX.pixel_shader - ); - } - - var factor = this.getInputData(1); - if (factor == null) { - factor = this.properties.factor; - } - - var uniforms = this._uniforms; - uniforms.u_factor = factor; - - //apply shader - gl.disable(gl.DEPTH_TEST); - temp.drawTo(function() { - tex.bind(0); - shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad()); - }); - - this.setOutputData(0, temp); - }; - - LGraphLensFX.pixel_shader = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_factor;\n\ - vec2 barrelDistortion(vec2 coord, float amt) {\n\ - vec2 cc = coord - 0.5;\n\ - float dist = dot(cc, cc);\n\ - return coord + cc * dist * amt;\n\ - }\n\ - \n\ - float sat( float t )\n\ - {\n\ - return clamp( t, 0.0, 1.0 );\n\ - }\n\ - \n\ - float linterp( float t ) {\n\ - return sat( 1.0 - abs( 2.0*t - 1.0 ) );\n\ - }\n\ - \n\ - float remap( float t, float a, float b ) {\n\ - return sat( (t - a) / (b - a) );\n\ - }\n\ - \n\ - vec4 spectrum_offset( float t ) {\n\ - vec4 ret;\n\ - float lo = step(t,0.5);\n\ - float hi = 1.0-lo;\n\ - float w = linterp( remap( t, 1.0/6.0, 5.0/6.0 ) );\n\ - ret = vec4(lo,1.0,hi, 1.) * vec4(1.0-w, w, 1.0-w, 1.);\n\ - \n\ - return pow( ret, vec4(1.0/2.2) );\n\ - }\n\ - \n\ - const float max_distort = 2.2;\n\ - const int num_iter = 12;\n\ - const float reci_num_iter_f = 1.0 / float(num_iter);\n\ - \n\ - void main()\n\ - { \n\ - vec2 uv=v_coord;\n\ - vec4 sumcol = vec4(0.0);\n\ - vec4 sumw = vec4(0.0); \n\ - for ( int i=0; i= res)\n\ - break;\n\ - iCount++;\n\ - }\n\ - float nf = n/normK;\n\ - return nf*nf*nf*nf;\n\ - }\n\ - void main() {\n\ - vec2 uv = v_coord * u_scale * u_viewport + u_offset * u_scale;\n\ - vec4 color = vec4( pNoise( uv, u_octaves ) * u_amplitude );\n\ - gl_FragColor = color;\n\ - }"; - - LiteGraph.registerNodeType("texture/perlin", LGraphTexturePerlin); - - function LGraphTextureCanvas2D() { - this.addInput("v"); - this.addOutput("out", "Texture"); - this.properties = { - code: LGraphTextureCanvas2D.default_code, - width: 512, - height: 512, - clear: true, - precision: LGraphTexture.DEFAULT, - use_html_canvas: false - }; - this._func = null; - this._temp_texture = null; - this.compileCode(); - } - - LGraphTextureCanvas2D.title = "Canvas2D"; - LGraphTextureCanvas2D.desc = "Executes Canvas2D code inside a texture or the viewport."; - LGraphTextureCanvas2D.help = "Set width and height to 0 to match viewport size."; - - LGraphTextureCanvas2D.default_code = "//vars: canvas,ctx,time\nctx.fillStyle='red';\nctx.fillRect(0,0,50,50);\n"; - - LGraphTextureCanvas2D.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }, - code: { type: "code" }, - width: { type: "Number", precision: 0, step: 1 }, - height: { type: "Number", precision: 0, step: 1 } - }; - - LGraphTextureCanvas2D.prototype.onPropertyChanged = function( name, value ) { - if (name == "code" ) - this.compileCode( value ); - } - - LGraphTextureCanvas2D.prototype.compileCode = function( code ) { - this._func = null; - if( !LiteGraph.allow_scripts ) - return; - - try { - this._func = new Function( "canvas", "ctx", "time", "script","v", code ); - this.boxcolor = "#00FF00"; - } catch (err) { - this.boxcolor = "#FF0000"; - console.error("Error parsing script"); - console.error(err); - } - }; - - LGraphTextureCanvas2D.prototype.onExecute = function() { - var func = this._func; - if (!func || !this.isOutputConnected(0)) { - return; - } - this.executeDraw( func ); - } - - LGraphTextureCanvas2D.prototype.executeDraw = function( func_context ) { - - var width = this.properties.width || gl.canvas.width; - var height = this.properties.height || gl.canvas.height; - var temp = this._temp_texture; - var type = LGraphTexture.getTextureType( this.properties.precision ); - if (!temp || temp.width != width || temp.height != height || temp.type != type ) { - temp = this._temp_texture = new GL.Texture(width, height, { - format: gl.RGBA, - filter: gl.LINEAR, - type: type - }); - } - - var v = this.getInputData(0); - - var properties = this.properties; - var that = this; - var time = this.graph.getTime(); - var ctx = gl; - var canvas = gl.canvas; - if( this.properties.use_html_canvas || !global.enableWebGLCanvas ) - { - if(!this._canvas) - { - canvas = this._canvas = createCanvas(width.height); - ctx = this._ctx = canvas.getContext("2d"); - } - else - { - canvas = this._canvas; - ctx = this._ctx; - } - canvas.width = width; - canvas.height = height; - } - - if(ctx == gl) //using Canvas2DtoWebGL - temp.drawTo(function() { - gl.start2D(); - if(properties.clear) - { - gl.clearColor(0,0,0,0); - gl.clear( gl.COLOR_BUFFER_BIT ); - } - - try { - if (func_context.draw) { - func_context.draw.call(that, canvas, ctx, time, func_context, v); - } else { - func_context.call(that, canvas, ctx, time, func_context,v); - } - that.boxcolor = "#00FF00"; - } catch (err) { - that.boxcolor = "#FF0000"; - console.error("Error executing script"); - console.error(err); - } - gl.finish2D(); - }); - else //rendering to offscren canvas and uploading to texture - { - if(properties.clear) - ctx.clearRect(0,0,canvas.width,canvas.height); - - try { - if (func_context.draw) { - func_context.draw.call(this, canvas, ctx, time, func_context, v); - } else { - func_context.call(this, canvas, ctx, time, func_context,v); - } - this.boxcolor = "#00FF00"; - } catch (err) { - this.boxcolor = "#FF0000"; - console.error("Error executing script"); - console.error(err); - } - temp.uploadImage( canvas ); - } - - this.setOutputData(0, temp); - }; - - LiteGraph.registerNodeType("texture/canvas2D", LGraphTextureCanvas2D); - - // To do chroma keying ***************** - - function LGraphTextureMatte() { - this.addInput("in", "Texture"); - - this.addOutput("out", "Texture"); - this.properties = { - key_color: vec3.fromValues(0, 1, 0), - threshold: 0.8, - slope: 0.2, - precision: LGraphTexture.DEFAULT - }; - } - - LGraphTextureMatte.title = "Matte"; - LGraphTextureMatte.desc = "Extracts background"; - - LGraphTextureMatte.widgets_info = { - key_color: { widget: "color" }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureMatte.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var tex = this.getInputData(0); - - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, tex); - return; - } - - if (!tex) { - return; - } - - this._tex = LGraphTexture.getTargetTexture( - tex, - this._tex, - this.properties.precision - ); - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - - if (!this._uniforms) { - this._uniforms = { - u_texture: 0, - u_key_color: this.properties.key_color, - u_threshold: 1, - u_slope: 1 - }; - } - var uniforms = this._uniforms; - - var mesh = Mesh.getScreenQuad(); - var shader = LGraphTextureMatte._shader; - if (!shader) { - shader = LGraphTextureMatte._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureMatte.pixel_shader - ); - } - - uniforms.u_key_color = this.properties.key_color; - uniforms.u_threshold = this.properties.threshold; - uniforms.u_slope = this.properties.slope; - - this._tex.drawTo(function() { - tex.bind(0); - shader.uniforms(uniforms).draw(mesh); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureMatte.pixel_shader = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec3 u_key_color;\n\ - uniform float u_threshold;\n\ - uniform float u_slope;\n\ - \n\ - void main() {\n\ - vec3 color = texture2D( u_texture, v_coord ).xyz;\n\ - float diff = length( normalize(color) - normalize(u_key_color) );\n\ - float edge = u_threshold * (1.0 - u_slope);\n\ - float alpha = smoothstep( edge, u_threshold, diff);\n\ - gl_FragColor = vec4( color, alpha );\n\ - }"; - - LiteGraph.registerNodeType("texture/matte", LGraphTextureMatte); - - //*********************************** - function LGraphCubemapToTexture2D() { - this.addInput("in", "texture"); - this.addInput("yaw", "number"); - this.addOutput("out", "texture"); - this.properties = { yaw: 0 }; - } - - LGraphCubemapToTexture2D.title = "CubemapToTexture2D"; - LGraphCubemapToTexture2D.desc = "Transforms a CUBEMAP texture into a TEXTURE2D in Polar Representation"; - - LGraphCubemapToTexture2D.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) - return; - - var tex = this.getInputData(0); - if ( !tex || tex.texture_type != GL.TEXTURE_CUBE_MAP ) - return; - if( this._last_tex && ( this._last_tex.height != tex.height || this._last_tex.type != tex.type )) - this._last_tex = null; - var yaw = this.getInputOrProperty("yaw"); - this._last_tex = GL.Texture.cubemapToTexture2D( tex, tex.height, this._last_tex, true, yaw ); - this.setOutputData( 0, this._last_tex ); - }; - - LiteGraph.registerNodeType( "texture/cubemapToTexture2D", LGraphCubemapToTexture2D ); -})(this); - -(function(global) { - var LiteGraph = global.LiteGraph; + //set uniforms + if(this.inputs) + for (var i = 0; i < this.inputs.length; ++i) { + var info = this.getInputInfo(i); + var data = this.getInputData(i); + if (data == null) { + continue; + } - var view_matrix = new Float32Array(16); - var projection_matrix = new Float32Array(16); - var viewprojection_matrix = new Float32Array(16); - var model_matrix = new Float32Array(16); - var global_uniforms = { - u_view: view_matrix, - u_projection: projection_matrix, - u_viewprojection: viewprojection_matrix, - u_model: model_matrix + if (data.constructor === GL.Texture) { + data.bind(tex_slot); + if (!in_tex) { + in_tex = data; + } + data = tex_slot; + tex_slot++; + } + shader.setUniform(info.name, data); //data is tex_slot + } + + var uniforms = this._uniforms; + var type = LGraphTexture.getTextureType( this.properties.precision, in_tex ); + + //render to texture + var w = this.properties.width | 0; + var h = this.properties.height | 0; + if (w == 0) { + w = in_tex ? in_tex.width : gl.canvas.width; + } + if (h == 0) { + h = in_tex ? in_tex.height : gl.canvas.height; + } + uniforms.texSize[0] = w; + uniforms.texSize[1] = h; + uniforms.time = this.graph.getTime(); + uniforms.u_value = this.properties.u_value; + uniforms.u_color.set( this.properties.u_color ); + + if ( !this._tex || this._tex.type != type || this._tex.width != w || this._tex.height != h ) { + this._tex = new GL.Texture(w, h, { type: type, format: gl.RGBA, filter: gl.LINEAR }); + } + var tex = this._tex; + tex.drawTo(function() { + shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad()); + }); + + this.setOutputData(0, this._tex); }; - LiteGraph.LGraphRender = { - onRequestCameraMatrices: null //overwrite with your 3D engine specifics, it will receive (view_matrix, projection_matrix,viewprojection_matrix) and must be filled - }; + LGraphTextureShader.pixel_shader = +"precision highp float;\n\ +\n\ +varying vec2 v_coord;\n\ +uniform float time; //time in seconds\n\ +uniform vec2 texSize; //tex resolution\n\ +uniform float u_value;\n\ +uniform vec4 u_color;\n\n\ +void main() {\n\ + vec2 uv = v_coord;\n\ + vec3 color = vec3(0.0);\n\ + //your code here\n\ + color.xy=uv;\n\n\ + gl_FragColor = vec4(color, 1.0);\n\ +}\n\ +"; - function generateGeometryId() { - return (Math.random() * 100000)|0; + LiteGraph.registerNodeType("texture/shader", LGraphTextureShader); + + // Texture Scale Offset + + function LGraphTextureScaleOffset() { + this.addInput("in", "Texture"); + this.addInput("scale", "vec2"); + this.addInput("offset", "vec2"); + this.addOutput("out", "Texture"); + this.properties = { + offset: vec2.fromValues(0, 0), + scale: vec2.fromValues(1, 1), + precision: LGraphTexture.DEFAULT + }; } - function LGraphPoints3D() { + LGraphTextureScaleOffset.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; - this.addInput("obj", ""); - this.addInput("radius", "number"); + LGraphTextureScaleOffset.title = "Scale/Offset"; + LGraphTextureScaleOffset.desc = "Applies an scaling and offseting"; - this.addOutput("out", "geometry"); - this.addOutput("points", "[vec3]"); + LGraphTextureScaleOffset.prototype.onExecute = function() { + var tex = this.getInputData(0); + + if (!this.isOutputConnected(0) || !tex) { + return; + } //saves work + + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, tex); + return; + } + + var width = tex.width; + var height = tex.height; + var type = this.precision === LGraphTexture.LOW ? gl.UNSIGNED_BYTE : gl.HIGH_PRECISION_FORMAT; + if (this.precision === LGraphTexture.DEFAULT) { + type = tex.type; + } + + if ( + !this._tex || + this._tex.width != width || + this._tex.height != height || + this._tex.type != type + ) { + this._tex = new GL.Texture(width, height, { + type: type, + format: gl.RGBA, + filter: gl.LINEAR + }); + } + + var shader = this._shader; + + if (!shader) { + shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureScaleOffset.pixel_shader + ); + } + + var scale = this.getInputData(1); + if (scale) { + this.properties.scale[0] = scale[0]; + this.properties.scale[1] = scale[1]; + } else { + scale = this.properties.scale; + } + + var offset = this.getInputData(2); + if (offset) { + this.properties.offset[0] = offset[0]; + this.properties.offset[1] = offset[1]; + } else { + offset = this.properties.offset; + } + + this._tex.drawTo(function() { + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + gl.disable(gl.BLEND); + tex.bind(0); + var mesh = Mesh.getScreenQuad(); + shader + .uniforms({ + u_texture: 0, + u_scale: scale, + u_offset: offset + }) + .draw(mesh); + }); + + this.setOutputData(0, this._tex); + }; + + LGraphTextureScaleOffset.pixel_shader = + "precision highp float;\n\ + \n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_textureB;\n\ + varying vec2 v_coord;\n\ + uniform vec2 u_scale;\n\ + uniform vec2 u_offset;\n\ + \n\ + void main() {\n\ + vec2 uv = v_coord;\n\ + uv = uv / u_scale - u_offset;\n\ + gl_FragColor = texture2D(u_texture, uv);\n\ + }\n\ + "; + + LiteGraph.registerNodeType( + "texture/scaleOffset", + LGraphTextureScaleOffset + ); + + // Warp (distort a texture) ************************* + + function LGraphTextureWarp() { + this.addInput("in", "Texture"); + this.addInput("warp", "Texture"); + this.addInput("factor", "number"); + this.addOutput("out", "Texture"); this.properties = { - radius: 1, - num_points: 4096, - generate_normals: true, - regular: false, - mode: LGraphPoints3D.SPHERE, - force_update: false + factor: 0.01, + scale: [1,1], + offset: [0,0], + precision: LGraphTexture.DEFAULT }; - this.points = new Float32Array( this.properties.num_points * 3 ); - this.normals = new Float32Array( this.properties.num_points * 3 ); - this.must_update = true; - this.version = 0; - - var that = this; - this.addWidget("button","update",null, function(){ that.must_update = true; }); - - this.geometry = { - vertices: null, - _id: generateGeometryId() - } - - this._old_obj = null; - this._last_radius = null; + this._uniforms = { + u_texture: 0, + u_textureB: 1, + u_factor: 1, + u_scale: vec2.create(), + u_offset: vec2.create() + }; } - global.LGraphPoints3D = LGraphPoints3D; - - LGraphPoints3D.RECTANGLE = 1; - LGraphPoints3D.CIRCLE = 2; - - LGraphPoints3D.CUBE = 10; - LGraphPoints3D.SPHERE = 11; - LGraphPoints3D.HEMISPHERE = 12; - LGraphPoints3D.INSIDE_SPHERE = 13; - - LGraphPoints3D.OBJECT = 20; - LGraphPoints3D.OBJECT_UNIFORMLY = 21; - LGraphPoints3D.OBJECT_INSIDE = 22; - - LGraphPoints3D.MODE_VALUES = { "rectangle":LGraphPoints3D.RECTANGLE, "circle":LGraphPoints3D.CIRCLE, "cube":LGraphPoints3D.CUBE, "sphere":LGraphPoints3D.SPHERE, "hemisphere":LGraphPoints3D.HEMISPHERE, "inside_sphere":LGraphPoints3D.INSIDE_SPHERE, "object":LGraphPoints3D.OBJECT, "object_uniformly":LGraphPoints3D.OBJECT_UNIFORMLY, "object_inside":LGraphPoints3D.OBJECT_INSIDE }; - - LGraphPoints3D.widgets_info = { - mode: { widget: "combo", values: LGraphPoints3D.MODE_VALUES } + LGraphTextureWarp.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } }; - LGraphPoints3D.title = "list of points"; - LGraphPoints3D.desc = "returns an array of points"; + LGraphTextureWarp.title = "Warp"; + LGraphTextureWarp.desc = "Texture warp operation"; - LGraphPoints3D.prototype.onPropertyChanged = function(name,value) + LGraphTextureWarp.prototype.onExecute = function() { + var tex = this.getInputData(0); + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, tex); + return; + } + + var texB = this.getInputData(1); + + var width = 512; + var height = 512; + var type = gl.UNSIGNED_BYTE; + if (tex) { + width = tex.width; + height = tex.height; + type = tex.type; + } else if (texB) { + width = texB.width; + height = texB.height; + type = texB.type; + } + + if (!tex && !this._tex) { + this._tex = new GL.Texture(width, height, { + type: + this.precision === LGraphTexture.LOW + ? gl.UNSIGNED_BYTE + : gl.HIGH_PRECISION_FORMAT, + format: gl.RGBA, + filter: gl.LINEAR + }); + } else { + this._tex = LGraphTexture.getTargetTexture( + tex || this._tex, + this._tex, + this.properties.precision + ); + } + + var shader = this._shader; + + if (!shader) { + shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureWarp.pixel_shader + ); + } + + var factor = this.getInputData(2); + if (factor != null) { + this.properties.factor = factor; + } else { + factor = parseFloat(this.properties.factor); + } + var uniforms = this._uniforms; + uniforms.u_factor = factor; + uniforms.u_scale.set( this.properties.scale ); + uniforms.u_offset.set( this.properties.offset ); + + this._tex.drawTo(function() { + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + gl.disable(gl.BLEND); + if (tex) { + tex.bind(0); + } + if (texB) { + texB.bind(1); + } + var mesh = Mesh.getScreenQuad(); + shader + .uniforms( uniforms ) + .draw(mesh); + }); + + this.setOutputData(0, this._tex); + }; + + LGraphTextureWarp.pixel_shader = + "precision highp float;\n\ + \n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_textureB;\n\ + varying vec2 v_coord;\n\ + uniform float u_factor;\n\ + uniform vec2 u_scale;\n\ + uniform vec2 u_offset;\n\ + \n\ + void main() {\n\ + vec2 uv = v_coord;\n\ + uv += ( texture2D(u_textureB, uv).rg - vec2(0.5)) * u_factor * u_scale + u_offset;\n\ + gl_FragColor = texture2D(u_texture, uv);\n\ + }\n\ + "; + + LiteGraph.registerNodeType("texture/warp", LGraphTextureWarp); + + //**************************************************** + + // Texture to Viewport ***************************************** + function LGraphTextureToViewport() { + this.addInput("Texture", "Texture"); + this.properties = { + additive: false, + antialiasing: false, + filter: true, + disable_alpha: false, + gamma: 1.0, + viewport: [0,0,1,1] + }; + this.size[0] = 130; + } + + LGraphTextureToViewport.title = "to Viewport"; + LGraphTextureToViewport.desc = "Texture to viewport"; + + LGraphTextureToViewport._prev_viewport = new Float32Array(4); + LGraphTextureToViewport.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (this.properties.disable_alpha) { + gl.disable(gl.BLEND); + } else { + gl.enable(gl.BLEND); + if (this.properties.additive) { + gl.blendFunc(gl.SRC_ALPHA, gl.ONE); + } else { + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + } + } + + gl.disable(gl.DEPTH_TEST); + var gamma = this.properties.gamma || 1.0; + if (this.isInputConnected(1)) { + gamma = this.getInputData(1); + } + + tex.setParameter( + gl.TEXTURE_MAG_FILTER, + this.properties.filter ? gl.LINEAR : gl.NEAREST + ); + + var old_viewport = LGraphTextureToViewport._prev_viewport; + old_viewport.set( gl.viewport_data ); + var new_view = this.properties.viewport; + gl.viewport( old_viewport[0] + old_viewport[2] * new_view[0], old_viewport[1] + old_viewport[3] * new_view[1], old_viewport[2] * new_view[2], old_viewport[3] * new_view[3] ); + var viewport = gl.getViewport(); //gl.getParameter(gl.VIEWPORT); + + if (this.properties.antialiasing) { + if (!LGraphTextureToViewport._shader) { + LGraphTextureToViewport._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureToViewport.aa_pixel_shader + ); + } + + var mesh = Mesh.getScreenQuad(); + tex.bind(0); + LGraphTextureToViewport._shader + .uniforms({ + u_texture: 0, + uViewportSize: [tex.width, tex.height], + u_igamma: 1 / gamma, + inverseVP: [1 / tex.width, 1 / tex.height] + }) + .draw(mesh); + } else { + if (gamma != 1.0) { + if (!LGraphTextureToViewport._gamma_shader) { + LGraphTextureToViewport._gamma_shader = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureToViewport.gamma_pixel_shader + ); + } + tex.toViewport(LGraphTextureToViewport._gamma_shader, { + u_texture: 0, + u_igamma: 1 / gamma + }); + } else { + tex.toViewport(); + } + } + + gl.viewport( old_viewport[0], old_viewport[1], old_viewport[2], old_viewport[3] ); + }; + + LGraphTextureToViewport.prototype.onGetInputs = function() { + return [["gamma", "number"]]; + }; + + LGraphTextureToViewport.aa_pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 uViewportSize;\n\ + uniform vec2 inverseVP;\n\ + uniform float u_igamma;\n\ + #define FXAA_REDUCE_MIN (1.0/ 128.0)\n\ + #define FXAA_REDUCE_MUL (1.0 / 8.0)\n\ + #define FXAA_SPAN_MAX 8.0\n\ + \n\ + /* from mitsuhiko/webgl-meincraft based on the code on geeks3d.com */\n\ + vec4 applyFXAA(sampler2D tex, vec2 fragCoord)\n\ + {\n\ + vec4 color = vec4(0.0);\n\ + /*vec2 inverseVP = vec2(1.0 / uViewportSize.x, 1.0 / uViewportSize.y);*/\n\ + vec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * inverseVP).xyz;\n\ + vec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * inverseVP).xyz;\n\ + vec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * inverseVP).xyz;\n\ + vec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * inverseVP).xyz;\n\ + vec3 rgbM = texture2D(tex, fragCoord * inverseVP).xyz;\n\ + vec3 luma = vec3(0.299, 0.587, 0.114);\n\ + float lumaNW = dot(rgbNW, luma);\n\ + float lumaNE = dot(rgbNE, luma);\n\ + float lumaSW = dot(rgbSW, luma);\n\ + float lumaSE = dot(rgbSE, luma);\n\ + float lumaM = dot(rgbM, luma);\n\ + float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n\ + float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\ + \n\ + vec2 dir;\n\ + dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n\ + dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\ + \n\ + float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\ + \n\ + float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n\ + dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * inverseVP;\n\ + \n\ + vec3 rgbA = 0.5 * (texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + \n\ + texture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz);\n\ + vec3 rgbB = rgbA * 0.5 + 0.25 * (texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + \n\ + texture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz);\n\ + \n\ + //return vec4(rgbA,1.0);\n\ + float lumaB = dot(rgbB, luma);\n\ + if ((lumaB < lumaMin) || (lumaB > lumaMax))\n\ + color = vec4(rgbA, 1.0);\n\ + else\n\ + color = vec4(rgbB, 1.0);\n\ + if(u_igamma != 1.0)\n\ + color.xyz = pow( color.xyz, vec3(u_igamma) );\n\ + return color;\n\ + }\n\ + \n\ + void main() {\n\ + gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\ + }\n\ + "; + + LGraphTextureToViewport.gamma_pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform float u_igamma;\n\ + void main() {\n\ + vec4 color = texture2D( u_texture, v_coord);\n\ + color.xyz = pow(color.xyz, vec3(u_igamma) );\n\ + gl_FragColor = color;\n\ + }\n\ + "; + + LiteGraph.registerNodeType( + "texture/toviewport", + LGraphTextureToViewport + ); + + // Texture Copy ***************************************** + function LGraphTextureCopy() { + this.addInput("Texture", "Texture"); + this.addOutput("", "Texture"); + this.properties = { + size: 0, + generate_mipmaps: false, + precision: LGraphTexture.DEFAULT + }; + } + + LGraphTextureCopy.title = "Copy"; + LGraphTextureCopy.desc = "Copy Texture"; + LGraphTextureCopy.widgets_info = { + size: { + widget: "combo", + values: [0, 32, 64, 128, 256, 512, 1024, 2048] + }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureCopy.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex && !this._temp_texture) { + return; + } + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + //copy the texture + if (tex) { + var width = tex.width; + var height = tex.height; + + if (this.properties.size != 0) { + width = this.properties.size; + height = this.properties.size; + } + + var temp = this._temp_texture; + + var type = tex.type; + if (this.properties.precision === LGraphTexture.LOW) { + type = gl.UNSIGNED_BYTE; + } else if (this.properties.precision === LGraphTexture.HIGH) { + type = gl.HIGH_PRECISION_FORMAT; + } + + if ( + !temp || + temp.width != width || + temp.height != height || + temp.type != type + ) { + var minFilter = gl.LINEAR; + if ( + this.properties.generate_mipmaps && + isPowerOfTwo(width) && + isPowerOfTwo(height) + ) { + minFilter = gl.LINEAR_MIPMAP_LINEAR; + } + this._temp_texture = new GL.Texture(width, height, { + type: type, + format: gl.RGBA, + minFilter: minFilter, + magFilter: gl.LINEAR + }); + } + tex.copyTo(this._temp_texture); + + if (this.properties.generate_mipmaps) { + this._temp_texture.bind(0); + gl.generateMipmap(this._temp_texture.texture_type); + this._temp_texture.unbind(0); + } + } + + this.setOutputData(0, this._temp_texture); + }; + + LiteGraph.registerNodeType("texture/copy", LGraphTextureCopy); + + // Texture Downsample ***************************************** + function LGraphTextureDownsample() { + this.addInput("Texture", "Texture"); + this.addOutput("", "Texture"); + this.properties = { + iterations: 1, + generate_mipmaps: false, + precision: LGraphTexture.DEFAULT + }; + } + + LGraphTextureDownsample.title = "Downsample"; + LGraphTextureDownsample.desc = "Downsample Texture"; + LGraphTextureDownsample.widgets_info = { + iterations: { type: "number", step: 1, precision: 0, min: 0 }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureDownsample.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex && !this._temp_texture) { + return; + } + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + //we do not allow any texture different than texture 2D + if (!tex || tex.texture_type !== GL.TEXTURE_2D) { + return; + } + + if (this.properties.iterations < 1) { + this.setOutputData(0, tex); + return; + } + + var shader = LGraphTextureDownsample._shader; + if (!shader) { + LGraphTextureDownsample._shader = shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureDownsample.pixel_shader + ); + } + + var width = tex.width | 0; + var height = tex.height | 0; + var type = tex.type; + if (this.properties.precision === LGraphTexture.LOW) { + type = gl.UNSIGNED_BYTE; + } else if (this.properties.precision === LGraphTexture.HIGH) { + type = gl.HIGH_PRECISION_FORMAT; + } + var iterations = this.properties.iterations || 1; + + var origin = tex; + var target = null; + + var temp = []; + var options = { + type: type, + format: tex.format + }; + + var offset = vec2.create(); + var uniforms = { + u_offset: offset + }; + + if (this._texture) { + GL.Texture.releaseTemporary(this._texture); + } + + for (var i = 0; i < iterations; ++i) { + offset[0] = 1 / width; + offset[1] = 1 / height; + width = width >> 1 || 0; + height = height >> 1 || 0; + target = GL.Texture.getTemporary(width, height, options); + temp.push(target); + origin.setParameter(GL.TEXTURE_MAG_FILTER, GL.NEAREST); + origin.copyTo(target, shader, uniforms); + if (width == 1 && height == 1) { + break; + } //nothing else to do + origin = target; + } + + //keep the last texture used + this._texture = temp.pop(); + + //free the rest + for (var i = 0; i < temp.length; ++i) { + GL.Texture.releaseTemporary(temp[i]); + } + + if (this.properties.generate_mipmaps) { + this._texture.bind(0); + gl.generateMipmap(this._texture.texture_type); + this._texture.unbind(0); + } + + this.setOutputData(0, this._texture); + }; + + LGraphTextureDownsample.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 u_offset;\n\ + varying vec2 v_coord;\n\ + \n\ + void main() {\n\ + vec4 color = texture2D(u_texture, v_coord );\n\ + color += texture2D(u_texture, v_coord + vec2( u_offset.x, 0.0 ) );\n\ + color += texture2D(u_texture, v_coord + vec2( 0.0, u_offset.y ) );\n\ + color += texture2D(u_texture, v_coord + vec2( u_offset.x, u_offset.y ) );\n\ + gl_FragColor = color * 0.25;\n\ + }\n\ + "; + + LiteGraph.registerNodeType( + "texture/downsample", + LGraphTextureDownsample + ); + + // Texture Average ***************************************** + function LGraphTextureAverage() { + this.addInput("Texture", "Texture"); + this.addOutput("tex", "Texture"); + this.addOutput("avg", "vec4"); + this.addOutput("lum", "number"); + this.properties = { + use_previous_frame: true, //to avoid stalls + high_quality: false //to use as much pixels as possible + }; + + this._uniforms = { + u_texture: 0, + u_mipmap_offset: 0 + }; + this._luminance = new Float32Array(4); + } + + LGraphTextureAverage.title = "Average"; + LGraphTextureAverage.desc = + "Compute a partial average (32 random samples) of a texture and stores it as a 1x1 pixel texture.\n If high_quality is true, then it generates the mipmaps first and reads from the lower one."; + + LGraphTextureAverage.prototype.onExecute = function() { + if (!this.properties.use_previous_frame) { + this.updateAverage(); + } + + var v = this._luminance; + this.setOutputData(0, this._temp_texture); + this.setOutputData(1, v); + this.setOutputData(2, (v[0] + v[1] + v[2]) / 3); + }; + + //executed before rendering the frame + LGraphTextureAverage.prototype.onPreRenderExecute = function() { + this.updateAverage(); + }; + + LGraphTextureAverage.prototype.updateAverage = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if ( + !this.isOutputConnected(0) && + !this.isOutputConnected(1) && + !this.isOutputConnected(2) + ) { + return; + } //saves work + + if (!LGraphTextureAverage._shader) { + LGraphTextureAverage._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureAverage.pixel_shader + ); + //creates 256 random numbers and stores them in two mat4 + var samples = new Float32Array(16); + for (var i = 0; i < samples.length; ++i) { + samples[i] = Math.random(); //poorly distributed samples + } + //upload only once + LGraphTextureAverage._shader.uniforms({ + u_samples_a: samples.subarray(0, 16), + u_samples_b: samples.subarray(16, 32) + }); + } + + var temp = this._temp_texture; + var type = gl.UNSIGNED_BYTE; + if (tex.type != type) { + //force floats, half floats cannot be read with gl.readPixels + type = gl.FLOAT; + } + + if (!temp || temp.type != type) { + this._temp_texture = new GL.Texture(1, 1, { + type: type, + format: gl.RGBA, + filter: gl.NEAREST + }); + } + + this._uniforms.u_mipmap_offset = 0; + + if(this.properties.high_quality) + { + if( !this._temp_pot2_texture || this._temp_pot2_texture.type != type ) + this._temp_pot2_texture = new GL.Texture(512, 512, { + type: type, + format: gl.RGBA, + minFilter: gl.LINEAR_MIPMAP_LINEAR, + magFilter: gl.LINEAR + }); + + tex.copyTo( this._temp_pot2_texture ); + tex = this._temp_pot2_texture; + tex.bind(0); + gl.generateMipmap(GL.TEXTURE_2D); + this._uniforms.u_mipmap_offset = 9; + } + + var shader = LGraphTextureAverage._shader; + var uniforms = this._uniforms; + uniforms.u_mipmap_offset = this.properties.mipmap_offset; + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.BLEND); + this._temp_texture.drawTo(function() { + tex.toViewport(shader, uniforms); + }); + + if (this.isOutputConnected(1) || this.isOutputConnected(2)) { + var pixel = this._temp_texture.getPixels(); + if (pixel) { + var v = this._luminance; + var type = this._temp_texture.type; + v.set(pixel); + if (type == gl.UNSIGNED_BYTE) { + vec4.scale(v, v, 1 / 255); + } else if ( + type == GL.HALF_FLOAT || + type == GL.HALF_FLOAT_OES + ) { + //no half floats possible, hard to read back unless copyed to a FLOAT texture, so temp_texture is always forced to FLOAT + } + } + } + }; + + LGraphTextureAverage.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + uniform mat4 u_samples_a;\n\ + uniform mat4 u_samples_b;\n\ + uniform sampler2D u_texture;\n\ + uniform float u_mipmap_offset;\n\ + varying vec2 v_coord;\n\ + \n\ + void main() {\n\ + vec4 color = vec4(0.0);\n\ + //random average\n\ + for(int i = 0; i < 4; ++i)\n\ + for(int j = 0; j < 4; ++j)\n\ + {\n\ + color += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\ + color += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\ + }\n\ + gl_FragColor = color * 0.03125;\n\ + }\n\ + "; + + LiteGraph.registerNodeType("texture/average", LGraphTextureAverage); + + + + // Computes operation between pixels (max, min) ***************************************** + function LGraphTextureMinMax() { + this.addInput("Texture", "Texture"); + this.addOutput("min_t", "Texture"); + this.addOutput("max_t", "Texture"); + this.addOutput("min", "vec4"); + this.addOutput("max", "vec4"); + this.properties = { + mode: "max", + use_previous_frame: true //to avoid stalls + }; + + this._uniforms = { + u_texture: 0 + }; + + this._max = new Float32Array(4); + this._min = new Float32Array(4); + + this._textures_chain = []; + } + + LGraphTextureMinMax.widgets_info = { + mode: { widget: "combo", values: ["min","max","avg"] } + }; + + LGraphTextureMinMax.title = "MinMax"; + LGraphTextureMinMax.desc = "Compute the scene min max"; + + LGraphTextureMinMax.prototype.onExecute = function() { + if (!this.properties.use_previous_frame) { + this.update(); + } + + this.setOutputData(0, this._temp_texture); + this.setOutputData(1, this._luminance); + }; + + //executed before rendering the frame + LGraphTextureMinMax.prototype.onPreRenderExecute = function() { + this.update(); + }; + + LGraphTextureMinMax.prototype.update = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if ( !this.isOutputConnected(0) && !this.isOutputConnected(1) ) { + return; + } //saves work + + if (!LGraphTextureMinMax._shader) { + LGraphTextureMinMax._shader = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureMinMax.pixel_shader ); + } + + var temp = this._temp_texture; + var type = gl.UNSIGNED_BYTE; + if (tex.type != type) { + //force floats, half floats cannot be read with gl.readPixels + type = gl.FLOAT; + } + + var size = 512; + + if( !this._textures_chain.length || this._textures_chain[0].type != type ) + { + var index = 0; + while(i) + { + this._textures_chain[i] = new GL.Texture( size, size, { + type: type, + format: gl.RGBA, + filter: gl.NEAREST + }); + size = size >> 2; + i++; + if(size == 1) + break; + } + } + + tex.copyTo( this._textures_chain[0] ); + var prev = this._textures_chain[0]; + for(var i = 1; i <= this._textures_chain.length; ++i) + { + var tex = this._textures_chain[i]; + + prev = tex; + } + + var shader = LGraphTextureMinMax._shader; + var uniforms = this._uniforms; + uniforms.u_mipmap_offset = this.properties.mipmap_offset; + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.BLEND); + this._temp_texture.drawTo(function() { + tex.toViewport(shader, uniforms); + }); + }; + + LGraphTextureMinMax.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + uniform mat4 u_samples_a;\n\ + uniform mat4 u_samples_b;\n\ + uniform sampler2D u_texture;\n\ + uniform float u_mipmap_offset;\n\ + varying vec2 v_coord;\n\ + \n\ + void main() {\n\ + vec4 color = vec4(0.0);\n\ + //random average\n\ + for(int i = 0; i < 4; ++i)\n\ + for(int j = 0; j < 4; ++j)\n\ + {\n\ + color += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\ + color += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\ + }\n\ + gl_FragColor = color * 0.03125;\n\ + }\n\ + "; + + //LiteGraph.registerNodeType("texture/clustered_operation", LGraphTextureClusteredOperation); + + + function LGraphTextureTemporalSmooth() { + this.addInput("in", "Texture"); + this.addInput("factor", "Number"); + this.addOutput("out", "Texture"); + this.properties = { factor: 0.5 }; + this._uniforms = { + u_texture: 0, + u_textureB: 1, + u_factor: this.properties.factor + }; + } + + LGraphTextureTemporalSmooth.title = "Smooth"; + LGraphTextureTemporalSmooth.desc = "Smooth texture over time"; + + LGraphTextureTemporalSmooth.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex || !this.isOutputConnected(0)) { + return; + } + + if (!LGraphTextureTemporalSmooth._shader) { + LGraphTextureTemporalSmooth._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureTemporalSmooth.pixel_shader + ); + } + + var temp = this._temp_texture; + if ( + !temp || + temp.type != tex.type || + temp.width != tex.width || + temp.height != tex.height + ) { + var options = { + type: tex.type, + format: gl.RGBA, + filter: gl.NEAREST + }; + this._temp_texture = new GL.Texture(tex.width, tex.height, options ); + this._temp_texture2 = new GL.Texture(tex.width, tex.height, options ); + tex.copyTo(this._temp_texture2); + } + + var tempA = this._temp_texture; + var tempB = this._temp_texture2; + + var shader = LGraphTextureTemporalSmooth._shader; + var uniforms = this._uniforms; + uniforms.u_factor = 1.0 - this.getInputOrProperty("factor"); + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + tempA.drawTo(function() { + tempB.bind(1); + tex.toViewport(shader, uniforms); + }); + + this.setOutputData(0, tempA); + + //swap + this._temp_texture = tempB; + this._temp_texture2 = tempA; + }; + + LGraphTextureTemporalSmooth.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_textureB;\n\ + uniform float u_factor;\n\ + varying vec2 v_coord;\n\ + \n\ + void main() {\n\ + gl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\ + }\n\ + "; + + LiteGraph.registerNodeType( "texture/temporal_smooth", LGraphTextureTemporalSmooth ); + + + function LGraphTextureLinearAvgSmooth() { + this.addInput("in", "Texture"); + this.addOutput("avg", "Texture"); + this.addOutput("array", "Texture"); + this.properties = { samples: 64, frames_interval: 1 }; + this._uniforms = { + u_texture: 0, + u_textureB: 1, + u_samples: this.properties.samples, + u_isamples: 1/this.properties.samples + }; + this.frame = 0; + } + + LGraphTextureLinearAvgSmooth.title = "Lineal Avg Smooth"; + LGraphTextureLinearAvgSmooth.desc = "Smooth texture linearly over time"; + + LGraphTextureLinearAvgSmooth["@samples"] = { type: "number", min: 1, max: 64, step: 1, precision: 1 }; + + LGraphTextureLinearAvgSmooth.prototype.getPreviewTexture = function() { - this.must_update = true; + return this._temp_texture2; } - LGraphPoints3D.prototype.onExecute = function() { + LGraphTextureLinearAvgSmooth.prototype.onExecute = function() { - var obj = this.getInputData(0); - if( obj != this._old_obj || (obj && obj._version != this._old_obj_version) ) - { - this._old_obj = obj; - this.must_update = true; + var tex = this.getInputData(0); + if (!tex || !this.isOutputConnected(0)) { + return; } - var radius = this.getInputData(1); - if(radius == null) - radius = this.properties.radius; - if( this._last_radius != radius ) - { - this._last_radius = radius; - this.must_update = true; + if (!LGraphTextureLinearAvgSmooth._shader) { + LGraphTextureLinearAvgSmooth._shader_copy = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureLinearAvgSmooth.pixel_shader_copy ); + LGraphTextureLinearAvgSmooth._shader_avg = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureLinearAvgSmooth.pixel_shader_avg ); } - if(this.must_update || this.properties.force_update ) + var samples = Math.clamp(this.properties.samples,0,64); + var frame = this.frame; + var interval = this.properties.frames_interval; + + if( interval == 0 || frame % interval == 0 ) { - this.must_update = false; - this.updatePoints(); - } + var temp = this._temp_texture; + if ( !temp || temp.type != tex.type || temp.width != samples ) { + var options = { + type: tex.type, + format: gl.RGBA, + filter: gl.NEAREST + }; + this._temp_texture = new GL.Texture( samples, 1, options ); + this._temp_texture2 = new GL.Texture( samples, 1, options ); + this._temp_texture_out = new GL.Texture( 1, 1, options ); + } - this.geometry.vertices = this.points; - this.geometry.normals = this.normals; - this.geometry._version = this.version; + var tempA = this._temp_texture; + var tempB = this._temp_texture2; - this.setOutputData( 0, this.geometry ); - } + var shader_copy = LGraphTextureLinearAvgSmooth._shader_copy; + var shader_avg = LGraphTextureLinearAvgSmooth._shader_avg; + var uniforms = this._uniforms; + uniforms.u_samples = samples; + uniforms.u_isamples = 1.0 / samples; - LGraphPoints3D.prototype.updatePoints = function() { - var num_points = this.properties.num_points|0; - if(num_points < 1) - num_points = 1; + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + tempA.drawTo(function() { + tempB.bind(1); + tex.toViewport( shader_copy, uniforms ); + }); - if(!this.points || this.points.length != num_points * 3) - this.points = new Float32Array( num_points * 3 ); + this._temp_texture_out.drawTo(function() { + tempA.toViewport( shader_avg, uniforms ); + }); - if(this.properties.generate_normals) - { - if (!this.normals || this.normals.length != this.points.length) - this.normals = new Float32Array( this.points.length ); + this.setOutputData( 0, this._temp_texture_out ); + + //swap + this._temp_texture = tempB; + this._temp_texture2 = tempA; } else - this.normals = null; - - var radius = this._last_radius || this.properties.radius; - var mode = this.properties.mode; - - var obj = this.getInputData(0); - this._old_obj_version = obj ? obj._version : null; - - this.points = LGraphPoints3D.generatePoints( radius, num_points, mode, this.points, this.normals, this.properties.regular, obj ); - - this.version++; - } - - //global - LGraphPoints3D.generatePoints = function( radius, num_points, mode, points, normals, regular, obj ) - { - var size = num_points * 3; - if(!points || points.length != size) - points = new Float32Array( size ); - var temp = new Float32Array(3); - var UP = new Float32Array([0,1,0]); - - if(regular) - { - if( mode == LGraphPoints3D.RECTANGLE) - { - var side = Math.floor(Math.sqrt(num_points)); - for(var i = 0; i < side; ++i) - for(var j = 0; j < side; ++j) - { - var pos = i*3 + j*3*side; - points[pos] = ((i/side) - 0.5) * radius * 2; - points[pos+1] = 0; - points[pos+2] = ((j/side) - 0.5) * radius * 2; - } - points = new Float32Array( points.subarray(0,side*side*3) ); - if(normals) - { - for(var i = 0; i < normals.length; i+=3) - normals.set(UP, i); - } - } - else if( mode == LGraphPoints3D.SPHERE) - { - var side = Math.floor(Math.sqrt(num_points)); - for(var i = 0; i < side; ++i) - for(var j = 0; j < side; ++j) - { - var pos = i*3 + j*3*side; - polarToCartesian( temp, (i/side) * 2 * Math.PI, ((j/side) - 0.5) * 2 * Math.PI, radius ); - points[pos] = temp[0]; - points[pos+1] = temp[1]; - points[pos+2] = temp[2]; - } - points = new Float32Array( points.subarray(0,side*side*3) ); - if(normals) - LGraphPoints3D.generateSphericalNormals( points, normals ); - } - else if( mode == LGraphPoints3D.CIRCLE) - { - for(var i = 0; i < size; i+=3) - { - var angle = 2 * Math.PI * (i/size); - points[i] = Math.cos( angle ) * radius; - points[i+1] = 0; - points[i+2] = Math.sin( angle ) * radius; - } - if(normals) - { - for(var i = 0; i < normals.length; i+=3) - normals.set(UP, i); - } - } - } - else //non regular - { - if( mode == LGraphPoints3D.RECTANGLE) - { - for(var i = 0; i < size; i+=3) - { - points[i] = (Math.random() - 0.5) * radius * 2; - points[i+1] = 0; - points[i+2] = (Math.random() - 0.5) * radius * 2; - } - if(normals) - { - for(var i = 0; i < normals.length; i+=3) - normals.set(UP, i); - } - } - else if( mode == LGraphPoints3D.CUBE) - { - for(var i = 0; i < size; i+=3) - { - points[i] = (Math.random() - 0.5) * radius * 2; - points[i+1] = (Math.random() - 0.5) * radius * 2; - points[i+2] = (Math.random() - 0.5) * radius * 2; - } - if(normals) - { - for(var i = 0; i < normals.length; i+=3) - normals.set(UP, i); - } - } - else if( mode == LGraphPoints3D.SPHERE) - { - LGraphPoints3D.generateSphere( points, size, radius ); - if(normals) - LGraphPoints3D.generateSphericalNormals( points, normals ); - } - else if( mode == LGraphPoints3D.HEMISPHERE) - { - LGraphPoints3D.generateHemisphere( points, size, radius ); - if(normals) - LGraphPoints3D.generateSphericalNormals( points, normals ); - } - else if( mode == LGraphPoints3D.CIRCLE) - { - LGraphPoints3D.generateInsideCircle( points, size, radius ); - if(normals) - LGraphPoints3D.generateSphericalNormals( points, normals ); - } - else if( mode == LGraphPoints3D.INSIDE_SPHERE) - { - LGraphPoints3D.generateInsideSphere( points, size, radius ); - if(normals) - LGraphPoints3D.generateSphericalNormals( points, normals ); - } - else if( mode == LGraphPoints3D.OBJECT) - { - LGraphPoints3D.generateFromObject( points, normals, size, obj, false ); - } - else if( mode == LGraphPoints3D.OBJECT_UNIFORMLY) - { - LGraphPoints3D.generateFromObject( points, normals, size, obj, true ); - } - else if( mode == LGraphPoints3D.OBJECT_INSIDE) - { - LGraphPoints3D.generateFromInsideObject( points, size, obj ); - //if(normals) - // LGraphPoints3D.generateSphericalNormals( points, normals ); - } - else - console.warn("wrong mode in LGraphPoints3D"); - } - - return points; - } - - LGraphPoints3D.generateSphericalNormals = function(points, normals) - { - var temp = new Float32Array(3); - for(var i = 0; i < normals.length; i+=3) - { - temp[0] = points[i]; - temp[1] = points[i+1]; - temp[2] = points[i+2]; - vec3.normalize(temp,temp); - normals.set(temp,i); - } - } - - LGraphPoints3D.generateSphere = function (points, size, radius) - { - for(var i = 0; i < size; i+=3) - { - var r1 = Math.random(); - var r2 = Math.random(); - var x = 2 * Math.cos( 2 * Math.PI * r1 ) * Math.sqrt( r2 * (1-r2) ); - var y = 1 - 2 * r2; - var z = 2 * Math.sin( 2 * Math.PI * r1 ) * Math.sqrt( r2 * (1-r2) ); - points[i] = x * radius; - points[i+1] = y * radius; - points[i+2] = z * radius; - } - } - - LGraphPoints3D.generateHemisphere = function (points, size, radius) - { - for(var i = 0; i < size; i+=3) - { - var r1 = Math.random(); - var r2 = Math.random(); - var x = Math.cos( 2 * Math.PI * r1 ) * Math.sqrt(1 - r2*r2 ); - var y = r2; - var z = Math.sin( 2 * Math.PI * r1 ) * Math.sqrt(1 - r2*r2 ); - points[i] = x * radius; - points[i+1] = y * radius; - points[i+2] = z * radius; - } - } - - LGraphPoints3D.generateInsideCircle = function (points, size, radius) - { - for(var i = 0; i < size; i+=3) - { - var r1 = Math.random(); - var r2 = Math.random(); - var x = Math.cos( 2 * Math.PI * r1 ) * Math.sqrt(1 - r2*r2 ); - var y = r2; - var z = Math.sin( 2 * Math.PI * r1 ) * Math.sqrt(1 - r2*r2 ); - points[i] = x * radius; - points[i+1] = 0; - points[i+2] = z * radius; - } - } - - LGraphPoints3D.generateInsideSphere = function (points, size, radius) - { - for(var i = 0; i < size; i+=3) - { - var u = Math.random(); - var v = Math.random(); - var theta = u * 2.0 * Math.PI; - var phi = Math.acos(2.0 * v - 1.0); - var r = Math.cbrt(Math.random()) * radius; - var sinTheta = Math.sin(theta); - var cosTheta = Math.cos(theta); - var sinPhi = Math.sin(phi); - var cosPhi = Math.cos(phi); - points[i] = r * sinPhi * cosTheta; - points[i+1] = r * sinPhi * sinTheta; - points[i+2] = r * cosPhi; - } - } - - function findRandomTriangle( areas, f ) - { - var l = areas.length; - var imin = 0; - var imid = 0; - var imax = l; - - if(l == 0) - return -1; - if(l == 1) - return 0; - //dichotimic search - while (imax >= imin) - { - imid = ((imax + imin)*0.5)|0; - var t = areas[ imid ]; - if( t == f ) - return imid; - if( imin == (imax - 1) ) - return imin; - if (t < f) - imin = imid; - else - imax = imid; - } - return imid; - } - - LGraphPoints3D.generateFromObject = function( points, normals, size, obj, evenly ) - { - if(!obj) - return; - - var vertices = null; - var mesh_normals = null; - var indices = null; - var areas = null; - if( obj.constructor === GL.Mesh ) - { - vertices = obj.vertexBuffers.vertices.data; - mesh_normals = obj.vertexBuffers.normals ? obj.vertexBuffers.normals.data : null; - indices = obj.indexBuffers.indices ? obj.indexBuffers.indices.data : null; - if(!indices) - indices = obj.indexBuffers.triangles ? obj.indexBuffers.triangles.data : null; - } - if(!vertices) - return null; - var num_triangles = indices ? indices.length / 3 : vertices.length / (3*3); - var total_area = 0; //sum of areas of all triangles - - if(evenly) - { - areas = new Float32Array(num_triangles); //accum - for(var i = 0; i < num_triangles; ++i) - { - if(indices) - { - a = indices[i*3]*3; - b = indices[i*3+1]*3; - c = indices[i*3+2]*3; - } - else - { - a = i*9; - b = i*9+3; - c = i*9+6; - } - var P1 = vertices.subarray(a,a+3); - var P2 = vertices.subarray(b,b+3); - var P3 = vertices.subarray(c,c+3); - var aL = vec3.distance( P1, P2 ); - var bL = vec3.distance( P2, P3 ); - var cL = vec3.distance( P3, P1 ); - var s = (aL + bL+ cL) / 2; - total_area += Math.sqrt(s * (s - aL) * (s - bL) * (s - cL)); - areas[i] = total_area; - } - for(var i = 0; i < num_triangles; ++i) //normalize - areas[i] /= total_area; - } - - for(var i = 0; i < size; i+=3) - { - var r = Math.random(); - var index = evenly ? findRandomTriangle( areas, r ) : Math.floor(r * num_triangles ); - //get random triangle - var a = 0; - var b = 0; - var c = 0; - if(indices) - { - a = indices[index*3]*3; - b = indices[index*3+1]*3; - c = indices[index*3+2]*3; - } - else - { - a = index*9; - b = index*9+3; - c = index*9+6; - } - var s = Math.random(); - var t = Math.random(); - var sqrt_s = Math.sqrt(s); - var af = 1 - sqrt_s; - var bf = sqrt_s * ( 1 - t); - var cf = t * sqrt_s; - points[i] = af * vertices[a] + bf*vertices[b] + cf*vertices[c]; - points[i+1] = af * vertices[a+1] + bf*vertices[b+1] + cf*vertices[c+1]; - points[i+2] = af * vertices[a+2] + bf*vertices[b+2] + cf*vertices[c+2]; - if(normals && mesh_normals) - { - normals[i] = af * mesh_normals[a] + bf*mesh_normals[b] + cf*mesh_normals[c]; - normals[i+1] = af * mesh_normals[a+1] + bf*mesh_normals[b+1] + cf*mesh_normals[c+1]; - normals[i+2] = af * mesh_normals[a+2] + bf*mesh_normals[b+2] + cf*mesh_normals[c+2]; - var N = normals.subarray(i,i+3); - vec3.normalize(N,N); - } - } - } - - LGraphPoints3D.generateFromInsideObject = function( points, size, mesh ) - { - if(!mesh || mesh.constructor !== GL.Mesh) - return; - - var aabb = mesh.getBoundingBox(); - if(!mesh.octree) - mesh.octree = new GL.Octree( mesh ); - var octree = mesh.octree; - var origin = vec3.create(); - var direction = vec3.fromValues(1,0,0); - var temp = vec3.create(); - var i = 0; - var tries = 0; - while(i < size && tries < points.length * 10) //limit to avoid problems - { - tries += 1 - var r = vec3.random(temp); //random point inside the aabb - r[0] = (r[0] * 2 - 1) * aabb[3] + aabb[0]; - r[1] = (r[1] * 2 - 1) * aabb[4] + aabb[1]; - r[2] = (r[2] * 2 - 1) * aabb[5] + aabb[2]; - origin.set(r); - var hit = octree.testRay( origin, direction, 0, 10000, true, GL.Octree.ALL ); - if(!hit || hit.length % 2 == 0) //not inside - continue; - points.set( r, i ); - i+=3; - } - } - - LiteGraph.registerNodeType( "geometry/points3D", LGraphPoints3D ); - - - - function LGraphPointsToInstances() { - this.addInput("points", "geometry"); - this.addOutput("instances", "[mat4]"); - this.properties = { - mode: 1, - autoupdate: true - }; - - this.must_update = true; - this.matrices = []; - this.first_time = true; - } - - LGraphPointsToInstances.NORMAL = 0; - LGraphPointsToInstances.VERTICAL = 1; - LGraphPointsToInstances.SPHERICAL = 2; - LGraphPointsToInstances.RANDOM = 3; - LGraphPointsToInstances.RANDOM_VERTICAL = 4; - - LGraphPointsToInstances.modes = {"normal":0,"vertical":1,"spherical":2,"random":3,"random_vertical":4}; - LGraphPointsToInstances.widgets_info = { - mode: { widget: "combo", values: LGraphPointsToInstances.modes } + this.setOutputData(0, this._temp_texture_out); + this.setOutputData(1, this._temp_texture2); + this.frame++; }; - LGraphPointsToInstances.title = "points to inst"; + LGraphTextureLinearAvgSmooth.pixel_shader_copy = + "precision highp float;\n\ + precision highp float;\n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_textureB;\n\ + uniform float u_isamples;\n\ + varying vec2 v_coord;\n\ + \n\ + void main() {\n\ + if( v_coord.x <= u_isamples )\n\ + gl_FragColor = texture2D( u_texture, vec2(0.5) );\n\ + else\n\ + gl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\ + }\n\ + "; - LGraphPointsToInstances.prototype.onExecute = function() - { - var geo = this.getInputData(0); - if( !geo ) - { - this.setOutputData(0,null); - return; - } - - if( !this.isOutputConnected(0) ) - return; - - var has_changed = (geo._version != this._version || geo._id != this._geometry_id); - - if( has_changed && this.properties.autoupdate || this.first_time ) - { - this.first_time = false; - this.updateInstances( geo ); - } - - this.setOutputData( 0, this.matrices ); - } - - LGraphPointsToInstances.prototype.updateInstances = function( geometry ) - { - var vertices = geometry.vertices; - if(!vertices) - return null; - var normals = geometry.normals; - - var matrices = this.matrices; - var num_points = vertices.length / 3; - if( matrices.length != num_points) - matrices.length = num_points; - var identity = mat4.create(); - var temp = vec3.create(); - var zero = vec3.create(); - var UP = vec3.fromValues(0,1,0); - var FRONT = vec3.fromValues(0,0,-1); - var RIGHT = vec3.fromValues(1,0,0); - var R = quat.create(); - - var front = vec3.create(); - var right = vec3.create(); - var top = vec3.create(); - - for(var i = 0; i < vertices.length; i += 3) - { - var index = i/3; - var m = matrices[index]; - if(!m) - m = matrices[index] = mat4.create(); - m.set( identity ); - var point = vertices.subarray(i,i+3); - - switch(this.properties.mode) - { - case LGraphPointsToInstances.NORMAL: - mat4.setTranslation( m, point ); - if(normals) - { - var normal = normals.subarray(i,i+3); - top.set( normal ); - vec3.normalize( top, top ); - vec3.cross( right, FRONT, top ); - vec3.normalize( right, right ); - vec3.cross( front, right, top ); - vec3.normalize( front, front ); - m.set(right,0); - m.set(top,4); - m.set(front,8); - mat4.setTranslation( m, point ); - } - break; - case LGraphPointsToInstances.VERTICAL: - mat4.setTranslation( m, point ); - break; - case LGraphPointsToInstances.SPHERICAL: - front.set( point ); - vec3.normalize( front, front ); - vec3.cross( right, UP, front ); - vec3.normalize( right, right ); - vec3.cross( top, front, right ); - vec3.normalize( top, top ); - m.set(right,0); - m.set(top,4); - m.set(front,8); - mat4.setTranslation( m, point ); - break; - case LGraphPointsToInstances.RANDOM: - temp[0] = Math.random()*2 - 1; - temp[1] = Math.random()*2 - 1; - temp[2] = Math.random()*2 - 1; - vec3.normalize( temp, temp ); - quat.setAxisAngle( R, temp, Math.random() * 2 * Math.PI ); - mat4.fromQuat(m, R); - mat4.setTranslation( m, point ); - break; - case LGraphPointsToInstances.RANDOM_VERTICAL: - quat.setAxisAngle( R, UP, Math.random() * 2 * Math.PI ); - mat4.fromQuat(m, R); - mat4.setTranslation( m, point ); - break; - } - } - - this._version = geometry._version; - this._geometry_id = geometry._id; - } - - LiteGraph.registerNodeType( "geometry/points_to_instances", LGraphPointsToInstances ); + LGraphTextureLinearAvgSmooth.pixel_shader_avg = + "precision highp float;\n\ + precision highp float;\n\ + uniform sampler2D u_texture;\n\ + uniform int u_samples;\n\ + uniform float u_isamples;\n\ + varying vec2 v_coord;\n\ + \n\ + void main() {\n\ + vec4 color = vec4(0.0);\n\ + for(int i = 0; i < 64; ++i)\n\ + {\n\ + color += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\ + if(i == (u_samples - 1))\n\ + break;\n\ + }\n\ + gl_FragColor = color * u_isamples;\n\ + }\n\ + "; - function LGraphGeometryTransform() { - this.addInput("in", "geometry,[mat4]"); - this.addInput("mat4", "mat4"); - this.addOutput("out", "geometry"); + LiteGraph.registerNodeType( "texture/linear_avg_smooth", LGraphTextureLinearAvgSmooth ); + + // Image To Texture ***************************************** + function LGraphImageToTexture() { + this.addInput("Image", "image"); + this.addOutput("", "Texture"); this.properties = {}; - - this.geometry = { - type: "triangles", - vertices: null, - _id: generateGeometryId(), - _version: 0 - }; - - this._last_geometry_id = -1; - this._last_version = -1; - this._last_key = ""; - - this.must_update = true; } - LGraphGeometryTransform.title = "Transform"; + LGraphImageToTexture.title = "Image to Texture"; + LGraphImageToTexture.desc = "Uploads an image to the GPU"; + //LGraphImageToTexture.widgets_info = { size: { widget:"combo", values:[0,32,64,128,256,512,1024,2048]} }; - LGraphGeometryTransform.prototype.onExecute = function() { - - var input = this.getInputData(0); - var model = this.getInputData(1); - - if(!input) - return; - - //array of matrices - if(input.constructor === Array) - { - if(input.length == 0) - return; - this.outputs[0].type = "[mat4]"; - if( !this.isOutputConnected(0) ) - return; - - if(!model) - { - this.setOutputData(0,input); - return; - } - - if(!this._output) - this._output = new Array(); - if(this._output.length != input.length) - this._output.length = input.length; - for(var i = 0; i < input.length; ++i) - { - var m = this._output[i]; - if(!m) - m = this._output[i] = mat4.create(); - mat4.multiply(m,input[i],model); - } - this.setOutputData(0,this._output); + LGraphImageToTexture.prototype.onExecute = function() { + var img = this.getInputData(0); + if (!img) { return; } - //geometry - if(!input.vertices || !input.vertices.length) - return; - var geo = input; - this.outputs[0].type = "geometry"; - if( !this.isOutputConnected(0) ) - return; - if(!model) - { - this.setOutputData(0,geo); + var width = img.videoWidth || img.width; + var height = img.videoHeight || img.height; + + //this is in case we are using a webgl canvas already, no need to reupload it + if (img.gltexture) { + this.setOutputData(0, img.gltexture); return; } - var key = typedArrayToArray(model).join(","); - - if( this.must_update || geo._id != this._last_geometry_id || geo._version != this._last_version || key != this._last_key ) - { - this.updateGeometry(geo, model); - this._last_key = key; - this._last_version = geo._version; - this._last_geometry_id = geo._id; - this.must_update = false; + var temp = this._temp_texture; + if (!temp || temp.width != width || temp.height != height) { + this._temp_texture = new GL.Texture(width, height, { + format: gl.RGBA, + filter: gl.LINEAR + }); } - this.setOutputData(0,this.geometry); - } - - LGraphGeometryTransform.prototype.updateGeometry = function(geometry, model) { - var old_vertices = geometry.vertices; - var vertices = this.geometry.vertices; - if( !vertices || vertices.length != old_vertices.length ) - vertices = this.geometry.vertices = new Float32Array( old_vertices.length ); - var temp = vec3.create(); - - for(var i = 0, l = vertices.length; i < l; i+=3) - { - temp[0] = old_vertices[i]; temp[1] = old_vertices[i+1]; temp[2] = old_vertices[i+2]; - mat4.multiplyVec3( temp, model, temp ); - vertices[i] = temp[0]; vertices[i+1] = temp[1]; vertices[i+2] = temp[2]; - } - - if(geometry.normals) - { - if( !this.geometry.normals || this.geometry.normals.length != geometry.normals.length ) - this.geometry.normals = new Float32Array( geometry.normals.length ); - var normals = this.geometry.normals; - var normal_model = mat4.invert(mat4.create(), model); - if(normal_model) - mat4.transpose(normal_model, normal_model); - var old_normals = geometry.normals; - for(var i = 0, l = normals.length; i < l; i+=3) - { - temp[0] = old_normals[i]; temp[1] = old_normals[i+1]; temp[2] = old_normals[i+2]; - mat4.multiplyVec3( temp, normal_model, temp ); - normals[i] = temp[0]; normals[i+1] = temp[1]; normals[i+2] = temp[2]; - } - } - - this.geometry.type = geometry.type; - this.geometry._version++; - } - - LiteGraph.registerNodeType( "geometry/transform", LGraphGeometryTransform ); - - - function LGraphGeometryPolygon() { - this.addInput("sides", "number"); - this.addInput("radius", "number"); - this.addOutput("out", "geometry"); - this.properties = { sides: 6, radius: 1, uvs: false } - - this.geometry = { - type: "line_loop", - vertices: null, - _id: generateGeometryId() - }; - this.geometry_id = -1; - this.version = -1; - this.must_update = true; - - this.last_info = { sides: -1, radius: -1 }; - } - - LGraphGeometryPolygon.title = "Polygon"; - - LGraphGeometryPolygon.prototype.onExecute = function() { - - if( !this.isOutputConnected(0) ) + try { + this._temp_texture.uploadImage(img); + } catch (err) { + console.error( + "image comes from an unsafe location, cannot be uploaded to webgl: " + + err + ); return; - - var sides = this.getInputOrProperty("sides"); - var radius = this.getInputOrProperty("radius"); - sides = Math.max(3,sides)|0; - - //update - if( this.last_info.sides != sides || this.last_info.radius != radius ) - this.updateGeometry(sides, radius); - - this.setOutputData(0,this.geometry); - } - - LGraphGeometryPolygon.prototype.updateGeometry = function(sides, radius) { - var num = 3*sides; - var vertices = this.geometry.vertices; - if( !vertices || vertices.length != num ) - vertices = this.geometry.vertices = new Float32Array( 3*sides ); - var delta = (Math.PI * 2) / sides; - var gen_uvs = this.properties.uvs; - if(gen_uvs) - { - uvs = this.geometry.coords = new Float32Array( 3*sides ); } - - for(var i = 0; i < sides; ++i) - { - var angle = delta * -i; - var x = Math.cos( angle ) * radius; - var y = 0; - var z = Math.sin( angle ) * radius; - vertices[i*3] = x; - vertices[i*3+1] = y; - vertices[i*3+2] = z; - - if(gen_uvs) - { - - - } - } - this.geometry._id = ++this.geometry_id; - this.geometry._version = ++this.version; - this.last_info.sides = sides; - this.last_info.radius = radius; - } - - LiteGraph.registerNodeType( "geometry/polygon", LGraphGeometryPolygon ); - - - function LGraphGeometryExtrude() { - - this.addInput("", "geometry"); - this.addOutput("", "geometry"); - this.properties = { top_cap: true, bottom_cap: true, offset: [0,100,0] }; - this.version = -1; - - this._last_geo_version = -1; - this._must_update = true; - } - - LGraphGeometryExtrude.title = "extrude"; - - LGraphGeometryExtrude.prototype.onPropertyChanged = function(name, value) - { - this._must_update = true; - } - - LGraphGeometryExtrude.prototype.onExecute = function() - { - var geo = this.getInputData(0); - if( !geo || !this.isOutputConnected(0) ) - return; - - if(geo.version != this._last_geo_version || this._must_update) - { - this._geo = this.extrudeGeometry( geo, this._geo ); - if(this._geo) - this._geo.version = this.version++; - this._must_update = false; - } - - this.setOutputData(0, this._geo); - } - - LGraphGeometryExtrude.prototype.extrudeGeometry = function( geo ) - { - //for every pair of vertices - var vertices = geo.vertices; - var num_points = vertices.length / 3; - - var tempA = vec3.create(); - var tempB = vec3.create(); - var tempC = vec3.create(); - var tempD = vec3.create(); - var offset = new Float32Array( this.properties.offset ); - - if(geo.type == "line_loop") - { - var new_vertices = new Float32Array( num_points * 6 * 3 ); //every points become 6 ( caps not included ) - var npos = 0; - for(var i = 0, l = vertices.length; i < l; i += 3) - { - tempA[0] = vertices[i]; tempA[1] = vertices[i+1]; tempA[2] = vertices[i+2]; - - if( i+3 < l ) //loop - { - tempB[0] = vertices[i+3]; tempB[1] = vertices[i+4]; tempB[2] = vertices[i+5]; - } - else - { - tempB[0] = vertices[0]; tempB[1] = vertices[1]; tempB[2] = vertices[2]; - } - - vec3.add( tempC, tempA, offset ); - vec3.add( tempD, tempB, offset ); - - new_vertices.set( tempA, npos ); npos += 3; - new_vertices.set( tempB, npos ); npos += 3; - new_vertices.set( tempC, npos ); npos += 3; - - new_vertices.set( tempB, npos ); npos += 3; - new_vertices.set( tempD, npos ); npos += 3; - new_vertices.set( tempC, npos ); npos += 3; - } - } - - var out_geo = { - _id: generateGeometryId(), - type: "triangles", - vertices: new_vertices - }; - - return out_geo; - } - - LiteGraph.registerNodeType( "geometry/extrude", LGraphGeometryExtrude ); - - - function LGraphGeometryEval() { - this.addInput("in", "geometry"); - this.addOutput("out", "geometry"); - - this.properties = { - code: "V[1] += 0.01 * Math.sin(I + T*0.001);", - execute_every_frame: false - }; - - this.geometry = null; - this.geometry_id = -1; - this.version = -1; - this.must_update = true; - - this.vertices = null; - this.func = null; - } - - LGraphGeometryEval.title = "geoeval"; - LGraphGeometryEval.desc = "eval code"; - - LGraphGeometryEval.widgets_info = { - code: { widget: "code" } + this.setOutputData(0, this._temp_texture); }; - LGraphGeometryEval.prototype.onConfigure = function(o) - { - this.compileCode(); - } + LiteGraph.registerNodeType( + "texture/imageToTexture", + LGraphImageToTexture + ); - LGraphGeometryEval.prototype.compileCode = function() - { - if(!this.properties.code) - return; + // Texture LUT ***************************************** + function LGraphTextureLUT() { + this.addInput("Texture", "Texture"); + this.addInput("LUT", "Texture"); + this.addInput("Intensity", "number"); + this.addOutput("", "Texture"); + this.properties = { enabled: true, intensity: 1, precision: LGraphTexture.DEFAULT, texture: null }; - try - { - this.func = new Function("V","I","T", this.properties.code); - this.boxcolor = "#AFA"; - this.must_update = true; - } - catch (err) - { - this.boxcolor = "red"; + if (!LGraphTextureLUT._shader) { + LGraphTextureLUT._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, LGraphTextureLUT.pixel_shader ); } } - LGraphGeometryEval.prototype.onPropertyChanged = function(name, value) - { - if(name == "code") - { - this.properties.code = value; - this.compileCode(); + LGraphTextureLUT.widgets_info = { + texture: { widget: "texture" }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureLUT.title = "LUT"; + LGraphTextureLUT.desc = "Apply LUT to Texture"; + + LGraphTextureLUT.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { + return; + } //saves work + + var tex = this.getInputData(0); + + if (this.properties.precision === LGraphTexture.PASS_THROUGH || this.properties.enabled === false) { + this.setOutputData(0, tex); + return; + } + + if (!tex) { + return; + } + + var lut_tex = this.getInputData(1); + + if (!lut_tex) { + lut_tex = LGraphTexture.getTexture(this.properties.texture); + } + + if (!lut_tex) { + this.setOutputData(0, tex); + return; + } + + lut_tex.bind(0); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri( + gl.TEXTURE_2D, + gl.TEXTURE_WRAP_S, + gl.CLAMP_TO_EDGE + ); + gl.texParameteri( + gl.TEXTURE_2D, + gl.TEXTURE_WRAP_T, + gl.CLAMP_TO_EDGE + ); + gl.bindTexture(gl.TEXTURE_2D, null); + + var intensity = this.properties.intensity; + if (this.isInputConnected(2)) { + this.properties.intensity = intensity = this.getInputData(2); + } + + this._tex = LGraphTexture.getTargetTexture( + tex, + this._tex, + this.properties.precision + ); + + //var mesh = Mesh.getScreenQuad(); + + this._tex.drawTo(function() { + lut_tex.bind(1); + tex.toViewport(LGraphTextureLUT._shader, { + u_texture: 0, + u_textureB: 1, + u_amount: intensity + }); + }); + + this.setOutputData(0, this._tex); + }; + + LGraphTextureLUT.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_textureB;\n\ + uniform float u_amount;\n\ + \n\ + void main() {\n\ + lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\ + mediump float blueColor = textureColor.b * 63.0;\n\ + mediump vec2 quad1;\n\ + quad1.y = floor(floor(blueColor) / 8.0);\n\ + quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\ + mediump vec2 quad2;\n\ + quad2.y = floor(ceil(blueColor) / 8.0);\n\ + quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\ + highp vec2 texPos1;\n\ + texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\ + texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\ + highp vec2 texPos2;\n\ + texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\ + texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\ + lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\ + lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\ + lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\ + gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\ + }\n\ + "; + + LiteGraph.registerNodeType("texture/LUT", LGraphTextureLUT); + + // Texture Channels ***************************************** + function LGraphTextureChannels() { + this.addInput("Texture", "Texture"); + + this.addOutput("R", "Texture"); + this.addOutput("G", "Texture"); + this.addOutput("B", "Texture"); + this.addOutput("A", "Texture"); + + //this.properties = { use_single_channel: true }; + if (!LGraphTextureChannels._shader) { + LGraphTextureChannels._shader = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureChannels.pixel_shader + ); } } - LGraphGeometryEval.prototype.onExecute = function() { - var geometry = this.getInputData(0); - if(!geometry) - return; + LGraphTextureChannels.title = "Texture to Channels"; + LGraphTextureChannels.desc = "Split texture channels"; - if(!this.func) - { - this.setOutputData(0,geometry); + LGraphTextureChannels.prototype.onExecute = function() { + var texA = this.getInputData(0); + if (!texA) { return; } - if( this.geometry_id != geometry._id || this.version != geometry._version || this.must_update || this.properties.execute_every_frame ) - { - this.must_update = false; - this.geometry_id = geometry._id; - if(this.properties.execute_every_frame) - this.version++; - else - this.version = geometry._version; - var func = this.func; - var T = getTime(); - - //clone - if(!this.geometry) - this.geometry = {}; - for(var i in geometry) - { - if(geometry[i] == null) - continue; - if( geometry[i].constructor == Float32Array ) - this.geometry[i] = new Float32Array( geometry[i] ); - else - this.geometry[i] = geometry[i]; - } - this.geometry._id = geometry._id; - if(this.properties.execute_every_frame) - this.geometry._version = this.version; - else - this.geometry._version = geometry._version + 1; - - var V = vec3.create(); - var vertices = this.vertices; - if(!vertices || this.vertices.length != geometry.vertices.length) - vertices = this.vertices = new Float32Array( geometry.vertices ); - else - vertices.set( geometry.vertices ); - for(var i = 0; i < vertices.length; i+=3) - { - V[0] = vertices[i]; - V[1] = vertices[i+1]; - V[2] = vertices[i+2]; - func(V,i/3,T); - vertices[i] = V[0]; - vertices[i+1] = V[1]; - vertices[i+2] = V[2]; - } - this.geometry.vertices = vertices; + if (!this._channels) { + this._channels = Array(4); } - this.setOutputData(0,this.geometry); - } - - LiteGraph.registerNodeType( "geometry/eval", LGraphGeometryEval ); - -/* -function LGraphGeometryDisplace() { - this.addInput("in", "geometry"); - this.addInput("img", "image"); - this.addOutput("out", "geometry"); - - this.properties = { - grid_size: 1 - }; - - this.geometry = null; - this.geometry_id = -1; - this.version = -1; - this.must_update = true; - - this.vertices = null; - } - - LGraphGeometryDisplace.title = "displace"; - LGraphGeometryDisplace.desc = "displace points"; - - LGraphGeometryDisplace.prototype.onExecute = function() { - var geometry = this.getInputData(0); - var image = this.getInputData(1); - if(!geometry) - return; - - if(!image) - { - this.setOutputData(0,geometry); - return; - } - - if( this.geometry_id != geometry._id || this.version != geometry._version || this.must_update ) - { - this.must_update = false; - this.geometry_id = geometry._id; - this.version = geometry._version; - - //copy - this.geometry = {}; - for(var i in geometry) - this.geometry[i] = geometry[i]; - this.geometry._id = geometry._id; - this.geometry._version = geometry._version + 1; - - var grid_size = this.properties.grid_size; - if(grid_size != 0) - { - var vertices = this.vertices; - if(!vertices || this.vertices.length != this.geometry.vertices.length) - vertices = this.vertices = new Float32Array( this.geometry.vertices ); - for(var i = 0; i < vertices.length; i+=3) - { - vertices[i] = Math.round(vertices[i]/grid_size) * grid_size; - vertices[i+1] = Math.round(vertices[i+1]/grid_size) * grid_size; - vertices[i+2] = Math.round(vertices[i+2]/grid_size) * grid_size; + //var format = this.properties.use_single_channel ? gl.LUMINANCE : gl.RGBA; //not supported by WebGL1 + var format = gl.RGB; + var connections = 0; + for (var i = 0; i < 4; i++) { + if (this.isOutputConnected(i)) { + if ( + !this._channels[i] || + this._channels[i].width != texA.width || + this._channels[i].height != texA.height || + this._channels[i].type != texA.type || + this._channels[i].format != format + ) { + this._channels[i] = new GL.Texture( + texA.width, + texA.height, + { + type: texA.type, + format: format, + filter: gl.LINEAR + } + ); } - this.geometry.vertices = vertices; + connections++; + } else { + this._channels[i] = null; } } - this.setOutputData(0,this.geometry); - } + if (!connections) { + return; + } - LiteGraph.registerNodeType( "geometry/displace", LGraphGeometryDisplace ); -*/ + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); - function LGraphConnectPoints() { - this.addInput("in", "geometry"); - this.addOutput("out", "geometry"); + var mesh = Mesh.getScreenQuad(); + var shader = LGraphTextureChannels._shader; + var masks = [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1] + ]; + + for (var i = 0; i < 4; i++) { + if (!this._channels[i]) { + continue; + } + + this._channels[i].drawTo(function() { + texA.bind(0); + shader + .uniforms({ u_texture: 0, u_mask: masks[i] }) + .draw(mesh); + }); + this.setOutputData(i, this._channels[i]); + } + }; + + LGraphTextureChannels.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec4 u_mask;\n\ + \n\ + void main() {\n\ + gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\ + }\n\ + "; + + LiteGraph.registerNodeType( + "texture/textureChannels", + LGraphTextureChannels + ); + + // Texture Channels to Texture ***************************************** + function LGraphChannelsTexture() { + this.addInput("R", "Texture"); + this.addInput("G", "Texture"); + this.addInput("B", "Texture"); + this.addInput("A", "Texture"); + + this.addOutput("Texture", "Texture"); this.properties = { - min_dist: 0.4, - max_dist: 0.5, - max_connections: 0, - probability: 1 + precision: LGraphTexture.DEFAULT, + R: 1, + G: 1, + B: 1, + A: 1 + }; + this._color = vec4.create(); + this._uniforms = { + u_textureR: 0, + u_textureG: 1, + u_textureB: 2, + u_textureA: 3, + u_color: this._color }; - - this.geometry_id = -1; - this.version = -1; - this.my_version = 1; - this.must_update = true; } - LGraphConnectPoints.title = "connect points"; - LGraphConnectPoints.desc = "adds indices between near points"; + LGraphChannelsTexture.title = "Channels to Texture"; + LGraphChannelsTexture.desc = "Split texture channels"; + LGraphChannelsTexture.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; - LGraphConnectPoints.prototype.onPropertyChanged = function(name,value) - { - this.must_update = true; + LGraphChannelsTexture.prototype.onExecute = function() { + var white = LGraphTexture.getWhiteTexture(); + var texR = this.getInputData(0) || white; + var texG = this.getInputData(1) || white; + var texB = this.getInputData(2) || white; + var texA = this.getInputData(3) || white; + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + + var mesh = Mesh.getScreenQuad(); + if (!LGraphChannelsTexture._shader) { + LGraphChannelsTexture._shader = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphChannelsTexture.pixel_shader + ); + } + var shader = LGraphChannelsTexture._shader; + + var w = Math.max(texR.width, texG.width, texB.width, texA.width); + var h = Math.max( + texR.height, + texG.height, + texB.height, + texA.height + ); + var type = + this.properties.precision == LGraphTexture.HIGH + ? LGraphTexture.HIGH_PRECISION_FORMAT + : gl.UNSIGNED_BYTE; + + if ( + !this._texture || + this._texture.width != w || + this._texture.height != h || + this._texture.type != type + ) { + this._texture = new GL.Texture(w, h, { + type: type, + format: gl.RGBA, + filter: gl.LINEAR + }); + } + + var color = this._color; + color[0] = this.properties.R; + color[1] = this.properties.G; + color[2] = this.properties.B; + color[3] = this.properties.A; + var uniforms = this._uniforms; + + this._texture.drawTo(function() { + texR.bind(0); + texG.bind(1); + texB.bind(2); + texA.bind(3); + shader.uniforms(uniforms).draw(mesh); + }); + this.setOutputData(0, this._texture); + }; + + LGraphChannelsTexture.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_textureR;\n\ + uniform sampler2D u_textureG;\n\ + uniform sampler2D u_textureB;\n\ + uniform sampler2D u_textureA;\n\ + uniform vec4 u_color;\n\ + \n\ + void main() {\n\ + gl_FragColor = u_color * vec4( \ + texture2D(u_textureR, v_coord).r,\ + texture2D(u_textureG, v_coord).r,\ + texture2D(u_textureB, v_coord).r,\ + texture2D(u_textureA, v_coord).r);\n\ + }\n\ + "; + + LiteGraph.registerNodeType( + "texture/channelsTexture", + LGraphChannelsTexture + ); + + // Texture Color ***************************************** + function LGraphTextureColor() { + this.addOutput("Texture", "Texture"); + + this._tex_color = vec4.create(); + this.properties = { + color: vec4.create(), + precision: LGraphTexture.DEFAULT + }; } - LGraphConnectPoints.prototype.onExecute = function() { - var geometry = this.getInputData(0); - if(!geometry) - return; + LGraphTextureColor.title = "Color"; + LGraphTextureColor.desc = + "Generates a 1x1 texture with a constant color"; - if( this.geometry_id != geometry._id || this.version != geometry._version || this.must_update ) - { - this.must_update = false; - this.geometry_id = geometry._id; - this.version = geometry._version; + LGraphTextureColor.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; - //copy - this.geometry = {}; - for(var i in geometry) - this.geometry[i] = geometry[i]; - this.geometry._id = generateGeometryId(); - this.geometry._version = this.my_version++; + LGraphTextureColor.prototype.onDrawBackground = function(ctx) { + var c = this.properties.color; + ctx.fillStyle = + "rgb(" + + Math.floor(Math.clamp(c[0], 0, 1) * 255) + + "," + + Math.floor(Math.clamp(c[1], 0, 1) * 255) + + "," + + Math.floor(Math.clamp(c[2], 0, 1) * 255) + + ")"; + if (this.flags.collapsed) { + this.boxcolor = ctx.fillStyle; + } else { + ctx.fillRect(0, 0, this.size[0], this.size[1]); + } + }; - var vertices = geometry.vertices; - var l = vertices.length; - var min_dist = this.properties.min_dist; - var max_dist = this.properties.max_dist; - var probability = this.properties.probability; - var max_connections = this.properties.max_connections; - var indices = []; - - for(var i = 0; i < l; i+=3) - { - var x = vertices[i]; - var y = vertices[i+1]; - var z = vertices[i+2]; - var connections = 0; - for(var j = i+3; j < l; j+=3) - { - var x2 = vertices[j]; - var y2 = vertices[j+1]; - var z2 = vertices[j+2]; - var dist = Math.sqrt( (x-x2)*(x-x2) + (y-y2)*(y-y2) + (z-z2)*(z-z2)); - if(dist > max_dist || dist < min_dist || (probability < 1 && probability < Math.random()) ) - continue; - indices.push(i/3,j/3); - connections += 1; - if(max_connections && connections > max_connections) + LGraphTextureColor.prototype.onExecute = function() { + var type = + this.properties.precision == LGraphTexture.HIGH + ? LGraphTexture.HIGH_PRECISION_FORMAT + : gl.UNSIGNED_BYTE; + + if (!this._tex || this._tex.type != type) { + this._tex = new GL.Texture(1, 1, { + format: gl.RGBA, + type: type, + minFilter: gl.NEAREST + }); + } + var color = this.properties.color; + + if (this.inputs) { + for (var i = 0; i < this.inputs.length; i++) { + var input = this.inputs[i]; + var v = this.getInputData(i); + if (v === undefined) { + continue; + } + switch (input.name) { + case "RGB": + case "RGBA": + color.set(v); + break; + case "R": + color[0] = v; + break; + case "G": + color[1] = v; + break; + case "B": + color[2] = v; + break; + case "A": + color[3] = v; break; } } - this.geometry.indices = this.indices = new Uint32Array(indices); } - if(this.indices && this.indices.length) - { - this.geometry.indices = this.indices; - this.setOutputData( 0, this.geometry ); + if (vec4.sqrDist(this._tex_color, color) > 0.001) { + this._tex_color.set(color); + this._tex.fill(color); } - else - this.setOutputData( 0, null ); + this.setOutputData(0, this._tex); + }; + + LGraphTextureColor.prototype.onGetInputs = function() { + return [ + ["RGB", "vec3"], + ["RGBA", "vec4"], + ["R", "number"], + ["G", "number"], + ["B", "number"], + ["A", "number"] + ]; + }; + + LiteGraph.registerNodeType("texture/color", LGraphTextureColor); + + // Texture Channels to Texture ***************************************** + function LGraphTextureGradient() { + this.addInput("A", "color"); + this.addInput("B", "color"); + this.addOutput("Texture", "Texture"); + + this.properties = { + angle: 0, + scale: 1, + A: [0, 0, 0], + B: [1, 1, 1], + texture_size: 32 + }; + if (!LGraphTextureGradient._shader) { + LGraphTextureGradient._shader = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureGradient.pixel_shader + ); + } + + this._uniforms = { + u_angle: 0, + u_colorA: vec3.create(), + u_colorB: vec3.create() + }; } - LiteGraph.registerNodeType( "geometry/connectPoints", LGraphConnectPoints ); + LGraphTextureGradient.title = "Gradient"; + LGraphTextureGradient.desc = "Generates a gradient"; + LGraphTextureGradient["@A"] = { type: "color" }; + LGraphTextureGradient["@B"] = { type: "color" }; + LGraphTextureGradient["@texture_size"] = { + type: "enum", + values: [32, 64, 128, 256, 512] + }; - //Works with Litegl.js to create WebGL nodes - if (typeof GL == "undefined") //LiteGL RELATED ********************************************** - return; + LGraphTextureGradient.prototype.onExecute = function() { + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); - function LGraphToGeometry() { - this.addInput("mesh", "mesh"); - this.addOutput("out", "geometry"); + var mesh = GL.Mesh.getScreenQuad(); + var shader = LGraphTextureGradient._shader; - this.geometry = {}; - this.last_mesh = null; - } + var A = this.getInputData(0); + if (!A) { + A = this.properties.A; + } + var B = this.getInputData(1); + if (!B) { + B = this.properties.B; + } - LGraphToGeometry.title = "to geometry"; - LGraphToGeometry.desc = "converts a mesh to geometry"; - - LGraphToGeometry.prototype.onExecute = function() { - var mesh = this.getInputData(0); - if(!mesh) - return; - - if(mesh != this.last_mesh) - { - this.last_mesh = mesh; - for(i in mesh.vertexBuffers) - { - var buffer = mesh.vertexBuffers[i]; - this.geometry[i] = buffer.data + //angle and scale + for (var i = 2; i < this.inputs.length; i++) { + var input = this.inputs[i]; + var v = this.getInputData(i); + if (v === undefined) { + continue; } - if(mesh.indexBuffers["triangles"]) - this.geometry.indices = mesh.indexBuffers["triangles"].data; - - this.geometry._id = generateGeometryId(); - this.geometry._version = 0; + this.properties[input.name] = v; } - this.setOutputData(0,this.geometry); - if(this.geometry) - this.setOutputData(1,this.geometry.vertices); + var uniforms = this._uniforms; + this._uniforms.u_angle = this.properties.angle * DEG2RAD; + this._uniforms.u_scale = this.properties.scale; + vec3.copy(uniforms.u_colorA, A); + vec3.copy(uniforms.u_colorB, B); + + var size = parseInt(this.properties.texture_size); + if (!this._tex || this._tex.width != size) { + this._tex = new GL.Texture(size, size, { + format: gl.RGB, + filter: gl.LINEAR + }); + } + + this._tex.drawTo(function() { + shader.uniforms(uniforms).draw(mesh); + }); + this.setOutputData(0, this._tex); + }; + + LGraphTextureGradient.prototype.onGetInputs = function() { + return [["angle", "number"], ["scale", "number"]]; + }; + + LGraphTextureGradient.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform float u_angle;\n\ + uniform float u_scale;\n\ + uniform vec3 u_colorA;\n\ + uniform vec3 u_colorB;\n\ + \n\ + vec2 rotate(vec2 v, float angle)\n\ + {\n\ + vec2 result;\n\ + float _cos = cos(angle);\n\ + float _sin = sin(angle);\n\ + result.x = v.x * _cos - v.y * _sin;\n\ + result.y = v.x * _sin + v.y * _cos;\n\ + return result;\n\ + }\n\ + void main() {\n\ + float f = (rotate(u_scale * (v_coord - vec2(0.5)), u_angle) + vec2(0.5)).x;\n\ + vec3 color = mix(u_colorA,u_colorB,clamp(f,0.0,1.0));\n\ + gl_FragColor = vec4(color,1.0);\n\ + }\n\ + "; + + LiteGraph.registerNodeType("texture/gradient", LGraphTextureGradient); + + // Texture Mix ***************************************** + function LGraphTextureMix() { + this.addInput("A", "Texture"); + this.addInput("B", "Texture"); + this.addInput("Mixer", "Texture"); + + this.addOutput("Texture", "Texture"); + this.properties = { factor: 0.5, size_from_biggest: true, invert: false, precision: LGraphTexture.DEFAULT }; + this._uniforms = { + u_textureA: 0, + u_textureB: 1, + u_textureMix: 2, + u_mix: vec4.create() + }; } - LiteGraph.registerNodeType( "geometry/toGeometry", LGraphToGeometry ); + LGraphTextureMix.title = "Mix"; + LGraphTextureMix.desc = "Generates a texture mixing two textures"; - function LGraphGeometryToMesh() { - this.addInput("in", "geometry"); - this.addOutput("mesh", "mesh"); - this.properties = {}; - this.version = -1; - this.mesh = null; + LGraphTextureMix.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureMix.prototype.onExecute = function() { + var texA = this.getInputData(0); + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, texA); + return; + } + + var texB = this.getInputData(1); + if (!texA || !texB) { + return; + } + + var texMix = this.getInputData(2); + + var factor = this.getInputData(3); + + this._tex = LGraphTexture.getTargetTexture( + this.properties.size_from_biggest && texB.width > texA.width ? texB : texA, + this._tex, + this.properties.precision + ); + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + + var mesh = Mesh.getScreenQuad(); + var shader = null; + var uniforms = this._uniforms; + if (texMix) { + shader = LGraphTextureMix._shader_tex; + if (!shader) { + shader = LGraphTextureMix._shader_tex = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureMix.pixel_shader, + { MIX_TEX: "" } + ); + } + } else { + shader = LGraphTextureMix._shader_factor; + if (!shader) { + shader = LGraphTextureMix._shader_factor = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureMix.pixel_shader + ); + } + var f = factor == null ? this.properties.factor : factor; + uniforms.u_mix.set([f, f, f, f]); + } + + var invert = this.properties.invert; + + this._tex.drawTo(function() { + texA.bind( invert ? 1 : 0 ); + texB.bind( invert ? 0 : 1 ); + if (texMix) { + texMix.bind(2); + } + shader.uniforms(uniforms).draw(mesh); + }); + + this.setOutputData(0, this._tex); + }; + + LGraphTextureMix.prototype.onGetInputs = function() { + return [["factor", "number"]]; + }; + + LGraphTextureMix.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_textureA;\n\ + uniform sampler2D u_textureB;\n\ + #ifdef MIX_TEX\n\ + uniform sampler2D u_textureMix;\n\ + #else\n\ + uniform vec4 u_mix;\n\ + #endif\n\ + \n\ + void main() {\n\ + #ifdef MIX_TEX\n\ + vec4 f = texture2D(u_textureMix, v_coord);\n\ + #else\n\ + vec4 f = u_mix;\n\ + #endif\n\ + gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\ + }\n\ + "; + + LiteGraph.registerNodeType("texture/mix", LGraphTextureMix); + + // Texture Edges detection ***************************************** + function LGraphTextureEdges() { + this.addInput("Tex.", "Texture"); + + this.addOutput("Edges", "Texture"); + this.properties = { + invert: true, + threshold: false, + factor: 1, + precision: LGraphTexture.DEFAULT + }; + + if (!LGraphTextureEdges._shader) { + LGraphTextureEdges._shader = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureEdges.pixel_shader + ); + } } - LGraphGeometryToMesh.title = "Geo to Mesh"; + LGraphTextureEdges.title = "Edges"; + LGraphTextureEdges.desc = "Detects edges"; - LGraphGeometryToMesh.prototype.updateMesh = function(geometry) + LGraphTextureEdges.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureEdges.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { + return; + } //saves work + + var tex = this.getInputData(0); + + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, tex); + return; + } + + if (!tex) { + return; + } + + this._tex = LGraphTexture.getTargetTexture( + tex, + this._tex, + this.properties.precision + ); + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + + var mesh = Mesh.getScreenQuad(); + var shader = LGraphTextureEdges._shader; + var invert = this.properties.invert; + var factor = this.properties.factor; + var threshold = this.properties.threshold ? 1 : 0; + + this._tex.drawTo(function() { + tex.bind(0); + shader + .uniforms({ + u_texture: 0, + u_isize: [1 / tex.width, 1 / tex.height], + u_factor: factor, + u_threshold: threshold, + u_invert: invert ? 1 : 0 + }) + .draw(mesh); + }); + + this.setOutputData(0, this._tex); + }; + + LGraphTextureEdges.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 u_isize;\n\ + uniform int u_invert;\n\ + uniform float u_factor;\n\ + uniform float u_threshold;\n\ + \n\ + void main() {\n\ + vec4 center = texture2D(u_texture, v_coord);\n\ + vec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\ + vec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\ + vec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\ + vec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\ + vec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\ + diff *= u_factor;\n\ + if(u_invert == 1)\n\ + diff.xyz = vec3(1.0) - diff.xyz;\n\ + if( u_threshold == 0.0 )\n\ + gl_FragColor = vec4( diff.xyz, center.a );\n\ + else\n\ + gl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\ + }\n\ + "; + + LiteGraph.registerNodeType("texture/edges", LGraphTextureEdges); + + // Texture Depth ***************************************** + function LGraphTextureDepthRange() { + this.addInput("Texture", "Texture"); + this.addInput("Distance", "number"); + this.addInput("Range", "number"); + this.addOutput("Texture", "Texture"); + this.properties = { + distance: 100, + range: 50, + only_depth: false, + high_precision: false + }; + this._uniforms = { + u_texture: 0, + u_distance: 100, + u_range: 50, + u_camera_planes: null + }; + } + + LGraphTextureDepthRange.title = "Depth Range"; + LGraphTextureDepthRange.desc = "Generates a texture with a depth range"; + + LGraphTextureDepthRange.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { + return; + } //saves work + + var tex = this.getInputData(0); + if (!tex) { + return; + } + + var precision = gl.UNSIGNED_BYTE; + if (this.properties.high_precision) { + precision = gl.half_float_ext ? gl.HALF_FLOAT_OES : gl.FLOAT; + } + + if ( + !this._temp_texture || + this._temp_texture.type != precision || + this._temp_texture.width != tex.width || + this._temp_texture.height != tex.height + ) { + this._temp_texture = new GL.Texture(tex.width, tex.height, { + type: precision, + format: gl.RGBA, + filter: gl.LINEAR + }); + } + + var uniforms = this._uniforms; + + //iterations + var distance = this.properties.distance; + if (this.isInputConnected(1)) { + distance = this.getInputData(1); + this.properties.distance = distance; + } + + var range = this.properties.range; + if (this.isInputConnected(2)) { + range = this.getInputData(2); + this.properties.range = range; + } + + uniforms.u_distance = distance; + uniforms.u_range = range; + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var mesh = Mesh.getScreenQuad(); + if (!LGraphTextureDepthRange._shader) { + LGraphTextureDepthRange._shader = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureDepthRange.pixel_shader + ); + LGraphTextureDepthRange._shader_onlydepth = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureDepthRange.pixel_shader, + { ONLY_DEPTH: "" } + ); + } + var shader = this.properties.only_depth + ? LGraphTextureDepthRange._shader_onlydepth + : LGraphTextureDepthRange._shader; + + //NEAR AND FAR PLANES + var planes = null; + if (tex.near_far_planes) { + planes = tex.near_far_planes; + } else if (window.LS && LS.Renderer._main_camera) { + planes = LS.Renderer._main_camera._uniforms.u_camera_planes; + } else { + planes = [0.1, 1000]; + } //hardcoded + uniforms.u_camera_planes = planes; + + this._temp_texture.drawTo(function() { + tex.bind(0); + shader.uniforms(uniforms).draw(mesh); + }); + + this._temp_texture.near_far_planes = planes; + this.setOutputData(0, this._temp_texture); + }; + + LGraphTextureDepthRange.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 u_camera_planes;\n\ + uniform float u_distance;\n\ + uniform float u_range;\n\ + \n\ + float LinearDepth()\n\ + {\n\ + float zNear = u_camera_planes.x;\n\ + float zFar = u_camera_planes.y;\n\ + float depth = texture2D(u_texture, v_coord).x;\n\ + depth = depth * 2.0 - 1.0;\n\ + return zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\ + }\n\ + \n\ + void main() {\n\ + float depth = LinearDepth();\n\ + #ifdef ONLY_DEPTH\n\ + gl_FragColor = vec4(depth);\n\ + #else\n\ + float diff = abs(depth * u_camera_planes.y - u_distance);\n\ + float dof = 1.0;\n\ + if(diff <= u_range)\n\ + dof = diff / u_range;\n\ + gl_FragColor = vec4(dof);\n\ + #endif\n\ + }\n\ + "; + + LiteGraph.registerNodeType( "texture/depth_range", LGraphTextureDepthRange ); + + + // Texture Depth ***************************************** + function LGraphTextureLinearDepth() { + this.addInput("Texture", "Texture"); + this.addOutput("Texture", "Texture"); + this.properties = { + precision: LGraphTexture.DEFAULT, + invert: false + }; + this._uniforms = { + u_texture: 0, + u_near: 0.1, + u_far: 10000 + }; + } + + LGraphTextureLinearDepth.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureLinearDepth.title = "Linear Depth"; + LGraphTextureLinearDepth.desc = "Creates a color texture with linear depth"; + + LGraphTextureLinearDepth.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { + return; + } //saves work + + var tex = this.getInputData(0); + if (!tex || (tex.format != gl.DEPTH_COMPONENT && tex.format != gl.DEPTH_STENCIL) ) { + return; + } + + var precision = this.properties.precision == LGraphTexture.HIGH ? gl.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE; + + if ( !this._temp_texture || this._temp_texture.type != precision || this._temp_texture.width != tex.width || this._temp_texture.height != tex.height ) { + this._temp_texture = new GL.Texture(tex.width, tex.height, { + type: precision, + format: gl.RGB, + filter: gl.LINEAR + }); + } + + var uniforms = this._uniforms; + + uniforms.u_near = tex.near_far_planes[0]; + uniforms.u_far = tex.near_far_planes[1]; + uniforms.u_invert = this.properties.invert ? 1 : 0; + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var mesh = Mesh.getScreenQuad(); + if(!LGraphTextureLinearDepth._shader) + LGraphTextureLinearDepth._shader = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureLinearDepth.pixel_shader); + var shader = LGraphTextureLinearDepth._shader; + + //NEAR AND FAR PLANES + var planes = null; + if (tex.near_far_planes) { + planes = tex.near_far_planes; + } else if (window.LS && LS.Renderer._main_camera) { + planes = LS.Renderer._main_camera._uniforms.u_camera_planes; + } else { + planes = [0.1, 1000]; + } //hardcoded + uniforms.u_camera_planes = planes; + + this._temp_texture.drawTo(function() { + tex.bind(0); + shader.uniforms(uniforms).draw(mesh); + }); + + this._temp_texture.near_far_planes = planes; + this.setOutputData(0, this._temp_texture); + }; + + LGraphTextureLinearDepth.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform float u_near;\n\ + uniform float u_far;\n\ + uniform int u_invert;\n\ + \n\ + void main() {\n\ + float zNear = u_near;\n\ + float zFar = u_far;\n\ + float depth = texture2D(u_texture, v_coord).x;\n\ + depth = depth * 2.0 - 1.0;\n\ + float f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\ + if( u_invert == 1 )\n\ + f = 1.0 - f;\n\ + gl_FragColor = vec4(vec3(f),1.0);\n\ + }\n\ + "; + + LiteGraph.registerNodeType( "texture/linear_depth", LGraphTextureLinearDepth ); + + // Texture Blur ***************************************** + function LGraphTextureBlur() { + this.addInput("Texture", "Texture"); + this.addInput("Iterations", "number"); + this.addInput("Intensity", "number"); + this.addOutput("Blurred", "Texture"); + this.properties = { + intensity: 1, + iterations: 1, + preserve_aspect: false, + scale: [1, 1], + precision: LGraphTexture.DEFAULT + }; + } + + LGraphTextureBlur.title = "Blur"; + LGraphTextureBlur.desc = "Blur a texture"; + + LGraphTextureBlur.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureBlur.max_iterations = 20; + + LGraphTextureBlur.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + var temp = this._final_texture; + + if ( + !temp || + temp.width != tex.width || + temp.height != tex.height || + temp.type != tex.type + ) { + //we need two textures to do the blurring + //this._temp_texture = new GL.Texture( tex.width, tex.height, { type: tex.type, format: gl.RGBA, filter: gl.LINEAR }); + temp = this._final_texture = new GL.Texture( + tex.width, + tex.height, + { type: tex.type, format: gl.RGBA, filter: gl.LINEAR } + ); + } + + //iterations + var iterations = this.properties.iterations; + if (this.isInputConnected(1)) { + iterations = this.getInputData(1); + this.properties.iterations = iterations; + } + iterations = Math.min( + Math.floor(iterations), + LGraphTextureBlur.max_iterations + ); + if (iterations == 0) { + //skip blurring + this.setOutputData(0, tex); + return; + } + + var intensity = this.properties.intensity; + if (this.isInputConnected(2)) { + intensity = this.getInputData(2); + this.properties.intensity = intensity; + } + + //blur sometimes needs an aspect correction + var aspect = LiteGraph.camera_aspect; + if (!aspect && window.gl !== undefined) { + aspect = gl.canvas.height / gl.canvas.width; + } + if (!aspect) { + aspect = 1; + } + aspect = this.properties.preserve_aspect ? aspect : 1; + + var scale = this.properties.scale || [1, 1]; + tex.applyBlur(aspect * scale[0], scale[1], intensity, temp); + for (var i = 1; i < iterations; ++i) { + temp.applyBlur( + aspect * scale[0] * (i + 1), + scale[1] * (i + 1), + intensity + ); + } + + this.setOutputData(0, temp); + }; + + /* +LGraphTextureBlur.pixel_shader = "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 u_offset;\n\ + uniform float u_intensity;\n\ + void main() {\n\ + vec4 sum = vec4(0.0);\n\ + vec4 center = texture2D(u_texture, v_coord);\n\ + sum += texture2D(u_texture, v_coord + u_offset * -4.0) * 0.05/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * -3.0) * 0.09/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * -2.0) * 0.12/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * -1.0) * 0.15/0.98;\n\ + sum += center * 0.16/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * 4.0) * 0.05/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * 3.0) * 0.09/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * 2.0) * 0.12/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * 1.0) * 0.15/0.98;\n\ + gl_FragColor = u_intensity * sum;\n\ + }\n\ + "; +*/ + + LiteGraph.registerNodeType("texture/blur", LGraphTextureBlur); + + // Texture Glow ***************************************** + //based in https://catlikecoding.com/unity/tutorials/advanced-rendering/bloom/ + function LGraphTextureGlow() { + this.addInput("in", "Texture"); + this.addInput("dirt", "Texture"); + this.addOutput("out", "Texture"); + this.addOutput("glow", "Texture"); + this.properties = { + enabled: true, + intensity: 1, + persistence: 0.99, + iterations: 16, + threshold: 0, + scale: 1, + dirt_factor: 0.5, + precision: LGraphTexture.DEFAULT + }; + this._textures = []; + this._uniforms = { + u_intensity: 1, + u_texture: 0, + u_glow_texture: 1, + u_threshold: 0, + u_texel_size: vec2.create() + }; + } + + LGraphTextureGlow.title = "Glow"; + LGraphTextureGlow.desc = "Filters a texture giving it a glow effect"; + LGraphTextureGlow.weights = new Float32Array([0.5, 0.4, 0.3, 0.2]); + + LGraphTextureGlow.widgets_info = { + iterations: { + type: "number", + min: 0, + max: 16, + step: 1, + precision: 0 + }, + threshold: { + type: "number", + min: 0, + max: 10, + step: 0.01, + precision: 2 + }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureGlow.prototype.onGetInputs = function() { + return [ + ["enabled", "boolean"], + ["threshold", "number"], + ["intensity", "number"], + ["persistence", "number"], + ["iterations", "number"], + ["dirt_factor", "number"] + ]; + }; + + LGraphTextureGlow.prototype.onGetOutputs = function() { + return [["average", "Texture"]]; + }; + + LGraphTextureGlow.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (!this.isAnyOutputConnected()) { + return; + } //saves work + + if ( + this.properties.precision === LGraphTexture.PASS_THROUGH || + this.getInputOrProperty("enabled") === false + ) { + this.setOutputData(0, tex); + return; + } + + var width = tex.width; + var height = tex.height; + + var texture_info = { + format: tex.format, + type: tex.type, + minFilter: GL.LINEAR, + magFilter: GL.LINEAR, + wrap: gl.CLAMP_TO_EDGE + }; + var type = LGraphTexture.getTextureType( + this.properties.precision, + tex + ); + + var uniforms = this._uniforms; + var textures = this._textures; + + //cut + var shader = LGraphTextureGlow._cut_shader; + if (!shader) { + shader = LGraphTextureGlow._cut_shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureGlow.cut_pixel_shader + ); + } + + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.BLEND); + + uniforms.u_threshold = this.getInputOrProperty("threshold"); + var currentDestination = (textures[0] = GL.Texture.getTemporary( + width, + height, + texture_info + )); + tex.blit(currentDestination, shader.uniforms(uniforms)); + var currentSource = currentDestination; + + var iterations = this.getInputOrProperty("iterations"); + iterations = Math.clamp(iterations, 1, 16) | 0; + var texel_size = uniforms.u_texel_size; + var intensity = this.getInputOrProperty("intensity"); + + uniforms.u_intensity = 1; + uniforms.u_delta = this.properties.scale; //1 + + //downscale/upscale shader + var shader = LGraphTextureGlow._shader; + if (!shader) { + shader = LGraphTextureGlow._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureGlow.scale_pixel_shader + ); + } + + var i = 1; + //downscale + for (; i < iterations; i++) { + width = width >> 1; + if ((height | 0) > 1) { + height = height >> 1; + } + if (width < 2) { + break; + } + currentDestination = textures[i] = GL.Texture.getTemporary( + width, + height, + texture_info + ); + texel_size[0] = 1 / currentSource.width; + texel_size[1] = 1 / currentSource.height; + currentSource.blit( + currentDestination, + shader.uniforms(uniforms) + ); + currentSource = currentDestination; + } + + //average + if (this.isOutputConnected(2)) { + var average_texture = this._average_texture; + if ( + !average_texture || + average_texture.type != tex.type || + average_texture.format != tex.format + ) { + average_texture = this._average_texture = new GL.Texture( + 1, + 1, + { + type: tex.type, + format: tex.format, + filter: gl.LINEAR + } + ); + } + texel_size[0] = 1 / currentSource.width; + texel_size[1] = 1 / currentSource.height; + uniforms.u_intensity = intensity; + uniforms.u_delta = 1; + currentSource.blit(average_texture, shader.uniforms(uniforms)); + this.setOutputData(2, average_texture); + } + + //upscale and blend + gl.enable(gl.BLEND); + gl.blendFunc(gl.ONE, gl.ONE); + uniforms.u_intensity = this.getInputOrProperty("persistence"); + uniforms.u_delta = 0.5; + + for ( + i -= 2; + i >= 0; + i-- // i-=2 => -1 to point to last element in array, -1 to go to texture above + ) { + currentDestination = textures[i]; + textures[i] = null; + texel_size[0] = 1 / currentSource.width; + texel_size[1] = 1 / currentSource.height; + currentSource.blit( + currentDestination, + shader.uniforms(uniforms) + ); + GL.Texture.releaseTemporary(currentSource); + currentSource = currentDestination; + } + gl.disable(gl.BLEND); + + //glow + if (this.isOutputConnected(1)) { + var glow_texture = this._glow_texture; + if ( + !glow_texture || + glow_texture.width != tex.width || + glow_texture.height != tex.height || + glow_texture.type != type || + glow_texture.format != tex.format + ) { + glow_texture = this._glow_texture = new GL.Texture( + tex.width, + tex.height, + { type: type, format: tex.format, filter: gl.LINEAR } + ); + } + currentSource.blit(glow_texture); + this.setOutputData(1, glow_texture); + } + + //final composition + if (this.isOutputConnected(0)) { + var final_texture = this._final_texture; + if ( + !final_texture || + final_texture.width != tex.width || + final_texture.height != tex.height || + final_texture.type != type || + final_texture.format != tex.format + ) { + final_texture = this._final_texture = new GL.Texture( + tex.width, + tex.height, + { type: type, format: tex.format, filter: gl.LINEAR } + ); + } + + var dirt_texture = this.getInputData(1); + var dirt_factor = this.getInputOrProperty("dirt_factor"); + + uniforms.u_intensity = intensity; + + shader = dirt_texture + ? LGraphTextureGlow._dirt_final_shader + : LGraphTextureGlow._final_shader; + if (!shader) { + if (dirt_texture) { + shader = LGraphTextureGlow._dirt_final_shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureGlow.final_pixel_shader, + { USE_DIRT: "" } + ); + } else { + shader = LGraphTextureGlow._final_shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureGlow.final_pixel_shader + ); + } + } + + final_texture.drawTo(function() { + tex.bind(0); + currentSource.bind(1); + if (dirt_texture) { + shader.setUniform("u_dirt_factor", dirt_factor); + shader.setUniform( + "u_dirt_texture", + dirt_texture.bind(2) + ); + } + shader.toViewport(uniforms); + }); + this.setOutputData(0, final_texture); + } + + GL.Texture.releaseTemporary(currentSource); + }; + + LGraphTextureGlow.cut_pixel_shader = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform float u_threshold;\n\ + void main() {\n\ + gl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\ + }"; + + LGraphTextureGlow.scale_pixel_shader = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 u_texel_size;\n\ + uniform float u_delta;\n\ + uniform float u_intensity;\n\ + \n\ + vec4 sampleBox(vec2 uv) {\n\ + vec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\ + vec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\ + return s * 0.25;\n\ + }\n\ + void main() {\n\ + gl_FragColor = u_intensity * sampleBox( v_coord );\n\ + }"; + + LGraphTextureGlow.final_pixel_shader = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_glow_texture;\n\ + #ifdef USE_DIRT\n\ + uniform sampler2D u_dirt_texture;\n\ + #endif\n\ + uniform vec2 u_texel_size;\n\ + uniform float u_delta;\n\ + uniform float u_intensity;\n\ + uniform float u_dirt_factor;\n\ + \n\ + vec4 sampleBox(vec2 uv) {\n\ + vec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\ + vec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\ + return s * 0.25;\n\ + }\n\ + void main() {\n\ + vec4 glow = sampleBox( v_coord );\n\ + #ifdef USE_DIRT\n\ + glow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\ + #endif\n\ + gl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\ + }"; + + LiteGraph.registerNodeType("texture/glow", LGraphTextureGlow); + + // Texture Filter ***************************************** + function LGraphTextureKuwaharaFilter() { + this.addInput("Texture", "Texture"); + this.addOutput("Filtered", "Texture"); + this.properties = { intensity: 1, radius: 5 }; + } + + LGraphTextureKuwaharaFilter.title = "Kuwahara Filter"; + LGraphTextureKuwaharaFilter.desc = + "Filters a texture giving an artistic oil canvas painting"; + + LGraphTextureKuwaharaFilter.max_radius = 10; + LGraphTextureKuwaharaFilter._shaders = []; + + LGraphTextureKuwaharaFilter.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + var temp = this._temp_texture; + + if ( + !temp || + temp.width != tex.width || + temp.height != tex.height || + temp.type != tex.type + ) { + this._temp_texture = new GL.Texture(tex.width, tex.height, { + type: tex.type, + format: gl.RGBA, + filter: gl.LINEAR + }); + } + + //iterations + var radius = this.properties.radius; + radius = Math.min( + Math.floor(radius), + LGraphTextureKuwaharaFilter.max_radius + ); + if (radius == 0) { + //skip blurring + this.setOutputData(0, tex); + return; + } + + var intensity = this.properties.intensity; + + //blur sometimes needs an aspect correction + var aspect = LiteGraph.camera_aspect; + if (!aspect && window.gl !== undefined) { + aspect = gl.canvas.height / gl.canvas.width; + } + if (!aspect) { + aspect = 1; + } + aspect = this.properties.preserve_aspect ? aspect : 1; + + if (!LGraphTextureKuwaharaFilter._shaders[radius]) { + LGraphTextureKuwaharaFilter._shaders[radius] = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureKuwaharaFilter.pixel_shader, + { RADIUS: radius.toFixed(0) } + ); + } + + var shader = LGraphTextureKuwaharaFilter._shaders[radius]; + var mesh = GL.Mesh.getScreenQuad(); + tex.bind(0); + + this._temp_texture.drawTo(function() { + shader + .uniforms({ + u_texture: 0, + u_intensity: intensity, + u_resolution: [tex.width, tex.height], + u_iResolution: [1 / tex.width, 1 / tex.height] + }) + .draw(mesh); + }); + + this.setOutputData(0, this._temp_texture); + }; + + //from https://www.shadertoy.com/view/MsXSz4 + LGraphTextureKuwaharaFilter.pixel_shader = + "\n\ +precision highp float;\n\ +varying vec2 v_coord;\n\ +uniform sampler2D u_texture;\n\ +uniform float u_intensity;\n\ +uniform vec2 u_resolution;\n\ +uniform vec2 u_iResolution;\n\ +#ifndef RADIUS\n\ + #define RADIUS 7\n\ +#endif\n\ +void main() {\n\ +\n\ + const int radius = RADIUS;\n\ + vec2 fragCoord = v_coord;\n\ + vec2 src_size = u_iResolution;\n\ + vec2 uv = v_coord;\n\ + float n = float((radius + 1) * (radius + 1));\n\ + int i;\n\ + int j;\n\ + vec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\ + vec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\ + vec3 c;\n\ + \n\ + for (int j = -radius; j <= 0; ++j) {\n\ + for (int i = -radius; i <= 0; ++i) {\n\ + c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ + m0 += c;\n\ + s0 += c * c;\n\ + }\n\ + }\n\ + \n\ + for (int j = -radius; j <= 0; ++j) {\n\ + for (int i = 0; i <= radius; ++i) {\n\ + c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ + m1 += c;\n\ + s1 += c * c;\n\ + }\n\ + }\n\ + \n\ + for (int j = 0; j <= radius; ++j) {\n\ + for (int i = 0; i <= radius; ++i) {\n\ + c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ + m2 += c;\n\ + s2 += c * c;\n\ + }\n\ + }\n\ + \n\ + for (int j = 0; j <= radius; ++j) {\n\ + for (int i = -radius; i <= 0; ++i) {\n\ + c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ + m3 += c;\n\ + s3 += c * c;\n\ + }\n\ + }\n\ + \n\ + float min_sigma2 = 1e+2;\n\ + m0 /= n;\n\ + s0 = abs(s0 / n - m0 * m0);\n\ + \n\ + float sigma2 = s0.r + s0.g + s0.b;\n\ + if (sigma2 < min_sigma2) {\n\ + min_sigma2 = sigma2;\n\ + gl_FragColor = vec4(m0, 1.0);\n\ + }\n\ + \n\ + m1 /= n;\n\ + s1 = abs(s1 / n - m1 * m1);\n\ + \n\ + sigma2 = s1.r + s1.g + s1.b;\n\ + if (sigma2 < min_sigma2) {\n\ + min_sigma2 = sigma2;\n\ + gl_FragColor = vec4(m1, 1.0);\n\ + }\n\ + \n\ + m2 /= n;\n\ + s2 = abs(s2 / n - m2 * m2);\n\ + \n\ + sigma2 = s2.r + s2.g + s2.b;\n\ + if (sigma2 < min_sigma2) {\n\ + min_sigma2 = sigma2;\n\ + gl_FragColor = vec4(m2, 1.0);\n\ + }\n\ + \n\ + m3 /= n;\n\ + s3 = abs(s3 / n - m3 * m3);\n\ + \n\ + sigma2 = s3.r + s3.g + s3.b;\n\ + if (sigma2 < min_sigma2) {\n\ + min_sigma2 = sigma2;\n\ + gl_FragColor = vec4(m3, 1.0);\n\ + }\n\ +}\n\ +"; + + LiteGraph.registerNodeType( + "texture/kuwahara", + LGraphTextureKuwaharaFilter + ); + + // Texture ***************************************** + function LGraphTextureXDoGFilter() { + this.addInput("Texture", "Texture"); + this.addOutput("Filtered", "Texture"); + this.properties = { + sigma: 1.4, + k: 1.6, + p: 21.7, + epsilon: 79, + phi: 0.017 + }; + } + + LGraphTextureXDoGFilter.title = "XDoG Filter"; + LGraphTextureXDoGFilter.desc = + "Filters a texture giving an artistic ink style"; + + LGraphTextureXDoGFilter.max_radius = 10; + LGraphTextureXDoGFilter._shaders = []; + + LGraphTextureXDoGFilter.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + var temp = this._temp_texture; + if ( + !temp || + temp.width != tex.width || + temp.height != tex.height || + temp.type != tex.type + ) { + this._temp_texture = new GL.Texture(tex.width, tex.height, { + type: tex.type, + format: gl.RGBA, + filter: gl.LINEAR + }); + } + + if (!LGraphTextureXDoGFilter._xdog_shader) { + LGraphTextureXDoGFilter._xdog_shader = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureXDoGFilter.xdog_pixel_shader + ); + } + var shader = LGraphTextureXDoGFilter._xdog_shader; + var mesh = GL.Mesh.getScreenQuad(); + + var sigma = this.properties.sigma; + var k = this.properties.k; + var p = this.properties.p; + var epsilon = this.properties.epsilon; + var phi = this.properties.phi; + tex.bind(0); + this._temp_texture.drawTo(function() { + shader + .uniforms({ + src: 0, + sigma: sigma, + k: k, + p: p, + epsilon: epsilon, + phi: phi, + cvsWidth: tex.width, + cvsHeight: tex.height + }) + .draw(mesh); + }); + + this.setOutputData(0, this._temp_texture); + }; + + //from https://github.com/RaymondMcGuire/GPU-Based-Image-Processing-Tools/blob/master/lib_webgl/scripts/main.js + LGraphTextureXDoGFilter.xdog_pixel_shader = + "\n\ +precision highp float;\n\ +uniform sampler2D src;\n\n\ +uniform float cvsHeight;\n\ +uniform float cvsWidth;\n\n\ +uniform float sigma;\n\ +uniform float k;\n\ +uniform float p;\n\ +uniform float epsilon;\n\ +uniform float phi;\n\ +varying vec2 v_coord;\n\n\ +float cosh(float val)\n\ +{\n\ + float tmp = exp(val);\n\ + float cosH = (tmp + 1.0 / tmp) / 2.0;\n\ + return cosH;\n\ +}\n\n\ +float tanh(float val)\n\ +{\n\ + float tmp = exp(val);\n\ + float tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\ + return tanH;\n\ +}\n\n\ +float sinh(float val)\n\ +{\n\ + float tmp = exp(val);\n\ + float sinH = (tmp - 1.0 / tmp) / 2.0;\n\ + return sinH;\n\ +}\n\n\ +void main(void){\n\ + vec3 destColor = vec3(0.0);\n\ + float tFrag = 1.0 / cvsHeight;\n\ + float sFrag = 1.0 / cvsWidth;\n\ + vec2 Frag = vec2(sFrag,tFrag);\n\ + vec2 uv = gl_FragCoord.st;\n\ + float twoSigmaESquared = 2.0 * sigma * sigma;\n\ + float twoSigmaRSquared = twoSigmaESquared * k * k;\n\ + int halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\ + const int MAX_NUM_ITERATION = 99999;\n\ + vec2 sum = vec2(0.0);\n\ + vec2 norm = vec2(0.0);\n\n\ + for(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\ + int i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\ + int j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\ + float d = length(vec2(i,j));\n\ + vec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\ + exp( -d * d / twoSigmaRSquared ));\n\n\ + vec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\ + norm += kernel;\n\ + sum += kernel * L;\n\ + }\n\n\ + sum /= norm;\n\n\ + float H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\ + float edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\ + destColor = vec3(edge);\n\ + gl_FragColor = vec4(destColor, 1.0);\n\ +}"; + + LiteGraph.registerNodeType("texture/xDoG", LGraphTextureXDoGFilter); + + // Texture Webcam ***************************************** + function LGraphTextureWebcam() { + this.addOutput("Webcam", "Texture"); + this.properties = { texture_name: "", facingMode: "user" }; + this.boxcolor = "black"; + this.version = 0; + } + + LGraphTextureWebcam.title = "Webcam"; + LGraphTextureWebcam.desc = "Webcam texture"; + + LGraphTextureWebcam.is_webcam_open = false; + + LGraphTextureWebcam.prototype.openStream = function() { + if (!navigator.getUserMedia) { + //console.log('getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags'); + return; + } + + this._waiting_confirmation = true; + + // Not showing vendor prefixes. + var constraints = { + audio: false, + video: { facingMode: this.properties.facingMode } + }; + navigator.mediaDevices + .getUserMedia(constraints) + .then(this.streamReady.bind(this)) + .catch(onFailSoHard); + + var that = this; + function onFailSoHard(e) { + LGraphTextureWebcam.is_webcam_open = false; + console.log("Webcam rejected", e); + that._webcam_stream = false; + that.boxcolor = "red"; + that.trigger("stream_error"); + } + }; + + LGraphTextureWebcam.prototype.closeStream = function() { + if (this._webcam_stream) { + var tracks = this._webcam_stream.getTracks(); + if (tracks.length) { + for (var i = 0; i < tracks.length; ++i) { + tracks[i].stop(); + } + } + LGraphTextureWebcam.is_webcam_open = false; + this._webcam_stream = null; + this._video = null; + this.boxcolor = "black"; + this.trigger("stream_closed"); + } + }; + + LGraphTextureWebcam.prototype.streamReady = function(localMediaStream) { + this._webcam_stream = localMediaStream; + //this._waiting_confirmation = false; + this.boxcolor = "green"; + var video = this._video; + if (!video) { + video = document.createElement("video"); + video.autoplay = true; + video.srcObject = localMediaStream; + this._video = video; + //document.body.appendChild( video ); //debug + //when video info is loaded (size and so) + video.onloadedmetadata = function(e) { + // Ready to go. Do some stuff. + LGraphTextureWebcam.is_webcam_open = true; + console.log(e); + }; + } + this.trigger("stream_ready", video); + }; + + LGraphTextureWebcam.prototype.onPropertyChanged = function( + name, + value + ) { + if (name == "facingMode") { + this.properties.facingMode = value; + this.closeStream(); + this.openStream(); + } + }; + + LGraphTextureWebcam.prototype.onRemoved = function() { + if (!this._webcam_stream) { + return; + } + + var tracks = this._webcam_stream.getTracks(); + if (tracks.length) { + for (var i = 0; i < tracks.length; ++i) { + tracks[i].stop(); + } + } + + this._webcam_stream = null; + this._video = null; + }; + + LGraphTextureWebcam.prototype.onDrawBackground = function(ctx) { + if (this.flags.collapsed || this.size[1] <= 20) { + return; + } + + if (!this._video) { + return; + } + + //render to graph canvas + ctx.save(); + if (!ctx.webgl) { + //reverse image + ctx.drawImage(this._video, 0, 0, this.size[0], this.size[1]); + } else { + if (this._video_texture) { + ctx.drawImage( + this._video_texture, + 0, + 0, + this.size[0], + this.size[1] + ); + } + } + ctx.restore(); + }; + + LGraphTextureWebcam.prototype.onExecute = function() { + if (this._webcam_stream == null && !this._waiting_confirmation) { + this.openStream(); + } + + if (!this._video || !this._video.videoWidth) { + return; + } + + var width = this._video.videoWidth; + var height = this._video.videoHeight; + + var temp = this._video_texture; + if (!temp || temp.width != width || temp.height != height) { + this._video_texture = new GL.Texture(width, height, { + format: gl.RGB, + filter: gl.LINEAR + }); + } + + this._video_texture.uploadImage(this._video); + this._video_texture.version = ++this.version; + + if (this.properties.texture_name) { + var container = LGraphTexture.getTexturesContainer(); + container[this.properties.texture_name] = this._video_texture; + } + + this.setOutputData(0, this._video_texture); + for (var i = 1; i < this.outputs.length; ++i) { + if (!this.outputs[i]) { + continue; + } + switch (this.outputs[i].name) { + case "width": + this.setOutputData(i, this._video.videoWidth); + break; + case "height": + this.setOutputData(i, this._video.videoHeight); + break; + } + } + }; + + LGraphTextureWebcam.prototype.onGetOutputs = function() { + return [ + ["width", "number"], + ["height", "number"], + ["stream_ready", LiteGraph.EVENT], + ["stream_closed", LiteGraph.EVENT], + ["stream_error", LiteGraph.EVENT] + ]; + }; + + LiteGraph.registerNodeType("texture/webcam", LGraphTextureWebcam); + + //from https://github.com/spite/Wagner + function LGraphLensFX() { + this.addInput("in", "Texture"); + this.addInput("f", "number"); + this.addOutput("out", "Texture"); + this.properties = { + enabled: true, + factor: 1, + precision: LGraphTexture.LOW + }; + + this._uniforms = { u_texture: 0, u_factor: 1 }; + } + + LGraphLensFX.title = "Lens FX"; + LGraphLensFX.desc = "distortion and chromatic aberration"; + + LGraphLensFX.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphLensFX.prototype.onGetInputs = function() { + return [["enabled", "boolean"]]; + }; + + LGraphLensFX.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + if ( + this.properties.precision === LGraphTexture.PASS_THROUGH || + this.getInputOrProperty("enabled") === false + ) { + this.setOutputData(0, tex); + return; + } + + var temp = this._temp_texture; + if ( + !temp || + temp.width != tex.width || + temp.height != tex.height || + temp.type != tex.type + ) { + temp = this._temp_texture = new GL.Texture( + tex.width, + tex.height, + { type: tex.type, format: gl.RGBA, filter: gl.LINEAR } + ); + } + + var shader = LGraphLensFX._shader; + if (!shader) { + shader = LGraphLensFX._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphLensFX.pixel_shader + ); + } + + var factor = this.getInputData(1); + if (factor == null) { + factor = this.properties.factor; + } + + var uniforms = this._uniforms; + uniforms.u_factor = factor; + + //apply shader + gl.disable(gl.DEPTH_TEST); + temp.drawTo(function() { + tex.bind(0); + shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad()); + }); + + this.setOutputData(0, temp); + }; + + LGraphLensFX.pixel_shader = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform float u_factor;\n\ + vec2 barrelDistortion(vec2 coord, float amt) {\n\ + vec2 cc = coord - 0.5;\n\ + float dist = dot(cc, cc);\n\ + return coord + cc * dist * amt;\n\ + }\n\ + \n\ + float sat( float t )\n\ + {\n\ + return clamp( t, 0.0, 1.0 );\n\ + }\n\ + \n\ + float linterp( float t ) {\n\ + return sat( 1.0 - abs( 2.0*t - 1.0 ) );\n\ + }\n\ + \n\ + float remap( float t, float a, float b ) {\n\ + return sat( (t - a) / (b - a) );\n\ + }\n\ + \n\ + vec4 spectrum_offset( float t ) {\n\ + vec4 ret;\n\ + float lo = step(t,0.5);\n\ + float hi = 1.0-lo;\n\ + float w = linterp( remap( t, 1.0/6.0, 5.0/6.0 ) );\n\ + ret = vec4(lo,1.0,hi, 1.) * vec4(1.0-w, w, 1.0-w, 1.);\n\ + \n\ + return pow( ret, vec4(1.0/2.2) );\n\ + }\n\ + \n\ + const float max_distort = 2.2;\n\ + const int num_iter = 12;\n\ + const float reci_num_iter_f = 1.0 / float(num_iter);\n\ + \n\ + void main()\n\ + { \n\ + vec2 uv=v_coord;\n\ + vec4 sumcol = vec4(0.0);\n\ + vec4 sumw = vec4(0.0); \n\ + for ( int i=0; i= 1) - gl.disable( gl.BLEND ); - else - gl.enable( gl.BLEND ); - gl.enable( gl.DEPTH_TEST ); - if( this.properties.additive ) - { - gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); - gl.depthMask( false ); - } - else - gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA ); - - var indices = "indices"; - if( mesh.indexBuffers.triangles ) - indices = "triangles"; - shader.draw( mesh, primitive, indices ); - gl.disable( gl.BLEND ); - gl.depthMask( true ); - } - - LiteGraph.registerNodeType( "geometry/render_mesh", LGraphRenderMesh ); - - //************************** - - - function LGraphGeometryPrimitive() { - this.addInput("size", "number"); - this.addOutput("out", "mesh"); - this.properties = { type: 1, size: 1, subdivisions: 32 }; - - this.version = (Math.random() * 100000)|0; - this.last_info = { type: -1, size: -1, subdivisions: -1 }; - } - - LGraphGeometryPrimitive.title = "Primitive"; - - LGraphGeometryPrimitive.VALID = { "CUBE":1, "PLANE":2, "CYLINDER":3, "SPHERE":4, "CIRCLE":5, "HEMISPHERE":6, "ICOSAHEDRON":7, "CONE":8, "QUAD":9 }; - LGraphGeometryPrimitive.widgets_info = { - type: { widget: "combo", values: LGraphGeometryPrimitive.VALID } - }; - - LGraphGeometryPrimitive.prototype.onExecute = function() { - - if( !this.isOutputConnected(0) ) - return; - - var size = this.getInputOrProperty("size"); - - //update - if( this.last_info.type != this.properties.type || this.last_info.size != size || this.last_info.subdivisions != this.properties.subdivisions ) - this.updateMesh( this.properties.type, size, this.properties.subdivisions ); - - this.setOutputData(0,this._mesh); - } - - LGraphGeometryPrimitive.prototype.updateMesh = function(type, size, subdivisions) + LGraphTextureCurve.prototype.onSerialize = function(o) { - subdivisions = Math.max(0,subdivisions)|0; - - switch (type) - { - case 1: //CUBE: - this._mesh = GL.Mesh.cube({size: size, normals:true,coords:true}); - break; - case 2: //PLANE: - this._mesh = GL.Mesh.plane({size: size, xz: true, detail: subdivisions, normals:true,coords:true}); - break; - case 3: //CYLINDER: - this._mesh = GL.Mesh.cylinder({size: size, subdivisions: subdivisions, normals:true,coords:true}); - break; - case 4: //SPHERE: - this._mesh = GL.Mesh.sphere({size: size, "long": subdivisions, lat: subdivisions, normals:true,coords:true}); - break; - case 5: //CIRCLE: - this._mesh = GL.Mesh.circle({size: size, slices: subdivisions, normals:true, coords:true}); - break; - case 6: //HEMISPHERE: - this._mesh = GL.Mesh.sphere({size: size, "long": subdivisions, lat: subdivisions, normals:true, coords:true, hemi: true}); - break; - case 7: //ICOSAHEDRON: - this._mesh = GL.Mesh.icosahedron({size: size, subdivisions:subdivisions }); - break; - case 8: //CONE: - this._mesh = GL.Mesh.cone({radius: size, height: size, subdivisions:subdivisions }); - break; - case 9: //QUAD: - this._mesh = GL.Mesh.plane({size: size, xz: false, detail: subdivisions, normals:true, coords:true }); - break; - } - - this.last_info.type = type; - this.last_info.size = size; - this.last_info.subdivisions = subdivisions; - this._mesh.version = this.version++; + var curves = {}; + for(var i in this._points) + curves[i] = this._points[i].concat(); + o.curves = curves; } - LiteGraph.registerNodeType( "geometry/mesh_primitive", LGraphGeometryPrimitive ); - - - function LGraphRenderPoints() { - this.addInput("in", "geometry"); - this.addInput("mat4", "mat4"); - this.addInput("tex", "texture"); - this.properties = { - enabled: true, - point_size: 0.1, - fixed_size: false, - additive: true, - color: [1,1,1], - opacity: 1 - }; - - this.color = vec4.create([1,1,1,1]); - - this.uniforms = { - u_point_size: 1, - u_perspective: 1, - u_point_perspective: 1, - u_color: this.color - }; - - this.geometry_id = -1; - this.version = -1; - this.mesh = null; - } - - LGraphRenderPoints.title = "renderPoints"; - LGraphRenderPoints.desc = "render points with a texture"; - - LGraphRenderPoints.widgets_info = { - color: { widget: "color" } - }; - - LGraphRenderPoints.prototype.updateMesh = function(geometry) + LGraphTextureCurve.prototype.onConfigure = function(o) { - var buffer = this.buffer; - if(!this.buffer || !this.buffer.data || this.buffer.data.length != geometry.vertices.length) - this.buffer = new GL.Buffer( GL.ARRAY_BUFFER, geometry.vertices,3,GL.DYNAMIC_DRAW); - else - { - this.buffer.data.set( geometry.vertices ); - this.buffer.upload(GL.DYNAMIC_DRAW); - } - - if(!this.mesh) - this.mesh = new GL.Mesh(); - - this.mesh.addBuffer("vertices",this.buffer); - this.geometry_id = this.mesh.id = geometry._id; - this.version = this.mesh.version = geometry._version; + this._points = o.curves; + if(this.curve_editor) + curve_editor.points = this._points; + this._must_update = true; } - LGraphRenderPoints.prototype.onExecute = function() { - - if(!this.properties.enabled) - return; - - var geometry = this.getInputData(0); - if(!geometry) - return; - if(this.version != geometry._version || this.geometry_id != geometry._id ) - this.updateMesh( geometry ); - - if(!LiteGraph.LGraphRender.onRequestCameraMatrices) + LGraphTextureCurve.prototype.onMouseDown = function(e, localpos, graphcanvas) + { + if(this.curve_editor) { - console.warn("cannot render geometry, LiteGraph.onRequestCameraMatrices is null, remember to fill this with a callback(view_matrix, projection_matrix,viewprojection_matrix) to use 3D rendering from the graph"); - return; + var r = this.curve_editor.onMouseDown([localpos[0],localpos[1]-this.curve_offset], graphcanvas); + if(r) + this.captureInput(true); + return r; } - - LiteGraph.LGraphRender.onRequestCameraMatrices( view_matrix, projection_matrix,viewprojection_matrix ); - var shader = null; - - var texture = this.getInputData(2); - - if(texture) - { - shader = gl.shaders["textured_points"]; - if(!shader) - shader = gl.shaders["textured_points"] = new GL.Shader( LGraphRenderPoints.vertex_shader_code, LGraphRenderPoints.fragment_shader_code, { USE_TEXTURED_POINTS:"" }); - } - else - { - shader = gl.shaders["points"]; - if(!shader) - shader = gl.shaders["points"] = new GL.Shader( LGraphRenderPoints.vertex_shader_code, LGraphRenderPoints.fragment_shader_code, { USE_POINTS: "" }); - } - - this.color.set( this.properties.color ); - this.color[3] = this.properties.opacity; - - var m = this.getInputData(1); - if(m) - model_matrix.set(m); - else - mat4.identity( model_matrix ); - - this.uniforms.u_point_size = this.properties.point_size; - this.uniforms.u_point_perspective = this.properties.fixed_size ? 0 : 1; - this.uniforms.u_perspective = gl.viewport_data[3] * projection_matrix[5]; - - shader.uniforms( global_uniforms ); - shader.uniforms( this.uniforms ); - - if(this.properties.opacity >= 1) - gl.disable( gl.BLEND ); - else - gl.enable( gl.BLEND ); - - gl.enable( gl.DEPTH_TEST ); - if( this.properties.additive ) - { - gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); - gl.depthMask( false ); - } - else - gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA ); - - shader.draw( this.mesh, GL.POINTS ); - - gl.disable( gl.BLEND ); - gl.depthMask( true ); } - LiteGraph.registerNodeType( "geometry/render_points", LGraphRenderPoints ); + LGraphTextureCurve.prototype.onMouseMove = function(e, localpos, graphcanvas) + { + if(this.curve_editor) + return this.curve_editor.onMouseMove([localpos[0],localpos[1]-this.curve_offset], graphcanvas); + } - LGraphRenderPoints.vertex_shader_code = '\ - precision mediump float;\n\ - attribute vec3 a_vertex;\n\ - varying vec3 v_vertex;\n\ - attribute vec3 a_normal;\n\ - varying vec3 v_normal;\n\ - #ifdef USE_COLOR\n\ - attribute vec4 a_color;\n\ - varying vec4 v_color;\n\ - #endif\n\ - attribute vec2 a_coord;\n\ - varying vec2 v_coord;\n\ - #ifdef USE_SIZE\n\ - attribute float a_extra;\n\ - #endif\n\ - #ifdef USE_INSTANCING\n\ - attribute mat4 u_model;\n\ - #else\n\ - uniform mat4 u_model;\n\ - #endif\n\ - uniform mat4 u_viewprojection;\n\ - uniform float u_point_size;\n\ - uniform float u_perspective;\n\ - uniform float u_point_perspective;\n\ - float computePointSize(float radius, float w)\n\ - {\n\ - if(radius < 0.0)\n\ - return -radius;\n\ - return u_perspective * radius / w;\n\ - }\n\ - void main() {\n\ - v_coord = a_coord;\n\ - #ifdef USE_COLOR\n\ - v_color = a_color;\n\ - #endif\n\ - v_vertex = ( u_model * vec4( a_vertex, 1.0 )).xyz;\n\ - v_normal = ( u_model * vec4( a_normal, 0.0 )).xyz;\n\ - gl_Position = u_viewprojection * vec4(v_vertex,1.0);\n\ - gl_PointSize = u_point_size;\n\ - #ifdef USE_SIZE\n\ - gl_PointSize = a_extra;\n\ - #endif\n\ - if(u_point_perspective != 0.0)\n\ - gl_PointSize = computePointSize( gl_PointSize, gl_Position.w );\n\ - }\ - '; + LGraphTextureCurve.prototype.onMouseUp = function(e, localpos, graphcanvas) + { + if(this.curve_editor) + return this.curve_editor.onMouseUp([localpos[0],localpos[1]-this.curve_offset], graphcanvas); + this.captureInput(false); + } - LGraphRenderPoints.fragment_shader_code = '\ - precision mediump float;\n\ - uniform vec4 u_color;\n\ - #ifdef USE_COLOR\n\ - varying vec4 v_color;\n\ - #endif\n\ + LGraphTextureCurve.channel_line_colors = { "RGB":"#666","R":"#F33","G":"#3F3","B":"#33F" }; + + LGraphTextureCurve.prototype.onDrawBackground = function(ctx, graphcanvas) + { + if(this.flags.collapsed) + return; + + if(!this.curve_editor) + this.curve_editor = new LiteGraph.CurveEditor(this._points.R); + ctx.save(); + ctx.translate(0,this.curve_offset); + var channel = this.widgets[1].value; + + if(this.properties.split_channels) + { + if(channel == "RGB") + { + this.widgets[1].value = channel = "R"; + this.widgets[1].disabled = false; + } + this.curve_editor.points = this._points.R; + this.curve_editor.draw( ctx, [this.size[0],this.size[1] - this.curve_offset], graphcanvas, "#111", LGraphTextureCurve.channel_line_colors.R, true ); + ctx.globalCompositeOperation = "lighten"; + this.curve_editor.points = this._points.G; + this.curve_editor.draw( ctx, [this.size[0],this.size[1] - this.curve_offset], graphcanvas, null, LGraphTextureCurve.channel_line_colors.G, true ); + this.curve_editor.points = this._points.B; + this.curve_editor.draw( ctx, [this.size[0],this.size[1] - this.curve_offset], graphcanvas, null, LGraphTextureCurve.channel_line_colors.B, true ); + ctx.globalCompositeOperation = "source-over"; + } + else + { + this.widgets[1].value = channel = "RGB"; + this.widgets[1].disabled = true; + } + + this.curve_editor.points = this._points[channel]; + this.curve_editor.draw( ctx, [this.size[0],this.size[1] - this.curve_offset], graphcanvas, this.properties.split_channels ? null : "#111", LGraphTextureCurve.channel_line_colors[channel] ); + ctx.restore(); + } + + LGraphTextureCurve.pixel_shader = + "precision highp float;\n\ varying vec2 v_coord;\n\ uniform sampler2D u_texture;\n\ + uniform sampler2D u_curve;\n\ + uniform float u_range;\n\ + \n\ void main() {\n\ - vec4 color = u_color;\n\ - #ifdef USE_TEXTURED_POINTS\n\ - color *= texture2D(u_texture, gl_PointCoord.xy);\n\ - #else\n\ - #ifdef USE_TEXTURE\n\ - color *= texture2D(u_texture, v_coord);\n\ - if(color.a < 0.1)\n\ - discard;\n\ - #endif\n\ - #ifdef USE_POINTS\n\ - float dist = length( gl_PointCoord.xy - vec2(0.5) );\n\ - if( dist > 0.45 )\n\ - discard;\n\ - #endif\n\ - #endif\n\ - #ifdef USE_COLOR\n\ - color *= v_color;\n\ - #endif\n\ + vec4 color = texture2D( u_texture, v_coord ) * u_range;\n\ + color.x = texture2D( u_curve, vec2( color.x, 0.5 ) ).x;\n\ + color.y = texture2D( u_curve, vec2( color.y, 0.5 ) ).y;\n\ + color.z = texture2D( u_curve, vec2( color.z, 0.5 ) ).z;\n\ + //color.w = texture2D( u_curve, vec2( color.w, 0.5 ) ).w;\n\ gl_FragColor = color;\n\ - }\ - '; + }"; - //based on https://inconvergent.net/2019/depth-of-field/ - /* - function LGraphRenderGeometryDOF() { - this.addInput("in", "geometry"); - this.addInput("mat4", "mat4"); - this.addInput("tex", "texture"); - this.properties = { - enabled: true, - lines: true, - point_size: 0.1, - fixed_size: false, - additive: true, - color: [1,1,1], - opacity: 1 - }; + LiteGraph.registerNodeType("texture/curve", LGraphTextureCurve); - this.color = vec4.create([1,1,1,1]); - - this.uniforms = { - u_point_size: 1, - u_perspective: 1, - u_point_perspective: 1, - u_color: this.color - }; - - this.geometry_id = -1; - this.version = -1; - this.mesh = null; + //simple exposition, but plan to expand it to support different gamma curves + function LGraphExposition() { + this.addInput("in", "Texture"); + this.addInput("exp", "number"); + this.addOutput("out", "Texture"); + this.properties = { exposition: 1, precision: LGraphTexture.LOW }; + this._uniforms = { u_texture: 0, u_exposition: 1 }; } - LGraphRenderGeometryDOF.widgets_info = { - color: { widget: "color" } + LGraphExposition.title = "Exposition"; + LGraphExposition.desc = "Controls texture exposition"; + + LGraphExposition.widgets_info = { + exposition: { widget: "slider", min: 0, max: 3 }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } }; - LGraphRenderGeometryDOF.prototype.updateMesh = function(geometry) - { - var buffer = this.buffer; - if(!this.buffer || this.buffer.data.length != geometry.vertices.length) - this.buffer = new GL.Buffer( GL.ARRAY_BUFFER, geometry.vertices,3,GL.DYNAMIC_DRAW); - else - { - this.buffer.data.set( geometry.vertices ); - this.buffer.upload(GL.DYNAMIC_DRAW); - } - - if(!this.mesh) - this.mesh = new GL.Mesh(); - - this.mesh.addBuffer("vertices",this.buffer); - this.geometry_id = this.mesh.id = geometry._id; - this.version = this.mesh.version = geometry._version; - } - - LGraphRenderGeometryDOF.prototype.onExecute = function() { - - if(!this.properties.enabled) - return; - - var geometry = this.getInputData(0); - if(!geometry) - return; - if(this.version != geometry._version || this.geometry_id != geometry._id ) - this.updateMesh( geometry ); - - if(!LiteGraph.LGraphRender.onRequestCameraMatrices) - { - console.warn("cannot render geometry, LiteGraph.onRequestCameraMatrices is null, remember to fill this with a callback(view_matrix, projection_matrix,viewprojection_matrix) to use 3D rendering from the graph"); + LGraphExposition.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { return; } - LiteGraph.LGraphRender.onRequestCameraMatrices( view_matrix, projection_matrix,viewprojection_matrix ); - var shader = null; + if (!this.isOutputConnected(0)) { + return; + } //saves work - var texture = this.getInputData(2); - - if(texture) - { - shader = gl.shaders["textured_points"]; - if(!shader) - shader = gl.shaders["textured_points"] = new GL.Shader( LGraphRenderGeometryDOF.vertex_shader_code, LGraphRenderGeometryDOF.fragment_shader_code, { USE_TEXTURED_POINTS:"" }); - } - else - { - shader = gl.shaders["points"]; - if(!shader) - shader = gl.shaders["points"] = new GL.Shader( LGraphRenderGeometryDOF.vertex_shader_code, LGraphRenderGeometryDOF.fragment_shader_code, { USE_POINTS: "" }); + var temp = this._temp_texture; + if ( + !temp || + temp.width != tex.width || + temp.height != tex.height || + temp.type != tex.type + ) { + temp = this._temp_texture = new GL.Texture( + tex.width, + tex.height, + { type: tex.type, format: gl.RGBA, filter: gl.LINEAR } + ); } - this.color.set( this.properties.color ); - this.color[3] = this.properties.opacity; - - var m = this.getInputData(1); - if(m) - model_matrix.set(m); - else - mat4.identity( model_matrix ); - - this.uniforms.u_point_size = this.properties.point_size; - this.uniforms.u_point_perspective = this.properties.fixed_size ? 0 : 1; - this.uniforms.u_perspective = gl.viewport_data[3] * projection_matrix[5]; - - shader.uniforms( global_uniforms ); - shader.uniforms( this.uniforms ); - - if(this.properties.opacity >= 1) - gl.disable( gl.BLEND ); - else - gl.enable( gl.BLEND ); - - gl.enable( gl.DEPTH_TEST ); - if( this.properties.additive ) - { - gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); - gl.depthMask( false ); + var shader = LGraphExposition._shader; + if (!shader) { + shader = LGraphExposition._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphExposition.pixel_shader + ); } - else - gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA ); - shader.draw( this.mesh, GL.POINTS ); + var exp = this.properties.exposition; + var exp_input = this.getInputData(1); + if (exp_input != null) { + exp = this.properties.exposition = exp_input; + } + var uniforms = this._uniforms; - gl.disable( gl.BLEND ); - gl.depthMask( true ); - } + //apply shader + temp.drawTo(function() { + gl.disable(gl.DEPTH_TEST); + tex.bind(0); + shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad()); + }); - LiteGraph.registerNodeType( "geometry/render_dof", LGraphRenderGeometryDOF ); + this.setOutputData(0, temp); + }; - LGraphRenderGeometryDOF.vertex_shader_code = '\ - precision mediump float;\n\ - attribute vec3 a_vertex;\n\ - varying vec3 v_vertex;\n\ - attribute vec3 a_normal;\n\ - varying vec3 v_normal;\n\ - #ifdef USE_COLOR\n\ - attribute vec4 a_color;\n\ - varying vec4 v_color;\n\ - #endif\n\ - attribute vec2 a_coord;\n\ - varying vec2 v_coord;\n\ - #ifdef USE_SIZE\n\ - attribute float a_extra;\n\ - #endif\n\ - #ifdef USE_INSTANCING\n\ - attribute mat4 u_model;\n\ - #else\n\ - uniform mat4 u_model;\n\ - #endif\n\ - uniform mat4 u_viewprojection;\n\ - uniform float u_point_size;\n\ - uniform float u_perspective;\n\ - uniform float u_point_perspective;\n\ - float computePointSize(float radius, float w)\n\ - {\n\ - if(radius < 0.0)\n\ - return -radius;\n\ - return u_perspective * radius / w;\n\ - }\n\ - void main() {\n\ - v_coord = a_coord;\n\ - #ifdef USE_COLOR\n\ - v_color = a_color;\n\ - #endif\n\ - v_vertex = ( u_model * vec4( a_vertex, 1.0 )).xyz;\n\ - v_normal = ( u_model * vec4( a_normal, 0.0 )).xyz;\n\ - gl_Position = u_viewprojection * vec4(v_vertex,1.0);\n\ - gl_PointSize = u_point_size;\n\ - #ifdef USE_SIZE\n\ - gl_PointSize = a_extra;\n\ - #endif\n\ - if(u_point_perspective != 0.0)\n\ - gl_PointSize = computePointSize( gl_PointSize, gl_Position.w );\n\ - }\ - '; - - LGraphRenderGeometryDOF.fragment_shader_code = '\ - precision mediump float;\n\ - uniform vec4 u_color;\n\ - #ifdef USE_COLOR\n\ - varying vec4 v_color;\n\ - #endif\n\ + LGraphExposition.pixel_shader = + "precision highp float;\n\ varying vec2 v_coord;\n\ uniform sampler2D u_texture;\n\ + uniform float u_exposition;\n\ + \n\ void main() {\n\ - vec4 color = u_color;\n\ - #ifdef USE_TEXTURED_POINTS\n\ - color *= texture2D(u_texture, gl_PointCoord.xy);\n\ + vec4 color = texture2D( u_texture, v_coord );\n\ + gl_FragColor = vec4( color.xyz * u_exposition, color.a );\n\ + }"; + + LiteGraph.registerNodeType("texture/exposition", LGraphExposition); + + function LGraphToneMapping() { + this.addInput("in", "Texture"); + this.addInput("avg", "number,Texture"); + this.addOutput("out", "Texture"); + this.properties = { + enabled: true, + scale: 1, + gamma: 1, + average_lum: 1, + lum_white: 1, + precision: LGraphTexture.LOW + }; + + this._uniforms = { + u_texture: 0, + u_lumwhite2: 1, + u_igamma: 1, + u_scale: 1, + u_average_lum: 1 + }; + } + + LGraphToneMapping.title = "Tone Mapping"; + LGraphToneMapping.desc = + "Applies Tone Mapping to convert from high to low"; + + LGraphToneMapping.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphToneMapping.prototype.onGetInputs = function() { + return [["enabled", "boolean"]]; + }; + + LGraphToneMapping.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + if ( + this.properties.precision === LGraphTexture.PASS_THROUGH || + this.getInputOrProperty("enabled") === false + ) { + this.setOutputData(0, tex); + return; + } + + var temp = this._temp_texture; + + if ( + !temp || + temp.width != tex.width || + temp.height != tex.height || + temp.type != tex.type + ) { + temp = this._temp_texture = new GL.Texture( + tex.width, + tex.height, + { type: tex.type, format: gl.RGBA, filter: gl.LINEAR } + ); + } + + var avg = this.getInputData(1); + if (avg == null) { + avg = this.properties.average_lum; + } + + var uniforms = this._uniforms; + var shader = null; + + if (avg.constructor === Number) { + this.properties.average_lum = avg; + uniforms.u_average_lum = this.properties.average_lum; + shader = LGraphToneMapping._shader; + if (!shader) { + shader = LGraphToneMapping._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphToneMapping.pixel_shader + ); + } + } else if (avg.constructor === GL.Texture) { + uniforms.u_average_texture = avg.bind(1); + shader = LGraphToneMapping._shader_texture; + if (!shader) { + shader = LGraphToneMapping._shader_texture = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphToneMapping.pixel_shader, + { AVG_TEXTURE: "" } + ); + } + } + + uniforms.u_lumwhite2 = + this.properties.lum_white * this.properties.lum_white; + uniforms.u_scale = this.properties.scale; + uniforms.u_igamma = 1 / this.properties.gamma; + + //apply shader + gl.disable(gl.DEPTH_TEST); + temp.drawTo(function() { + tex.bind(0); + shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad()); + }); + + this.setOutputData(0, this._temp_texture); + }; + + LGraphToneMapping.pixel_shader = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform float u_scale;\n\ + #ifdef AVG_TEXTURE\n\ + uniform sampler2D u_average_texture;\n\ + #else\n\ + uniform float u_average_lum;\n\ + #endif\n\ + uniform float u_lumwhite2;\n\ + uniform float u_igamma;\n\ + vec3 RGB2xyY (vec3 rgb)\n\ + {\n\ + const mat3 RGB2XYZ = mat3(0.4124, 0.3576, 0.1805,\n\ + 0.2126, 0.7152, 0.0722,\n\ + 0.0193, 0.1192, 0.9505);\n\ + vec3 XYZ = RGB2XYZ * rgb;\n\ + \n\ + float f = (XYZ.x + XYZ.y + XYZ.z);\n\ + return vec3(XYZ.x / f,\n\ + XYZ.y / f,\n\ + XYZ.y);\n\ + }\n\ + \n\ + void main() {\n\ + vec4 color = texture2D( u_texture, v_coord );\n\ + vec3 rgb = color.xyz;\n\ + float average_lum = 0.0;\n\ + #ifdef AVG_TEXTURE\n\ + vec3 pixel = texture2D(u_average_texture,vec2(0.5)).xyz;\n\ + average_lum = (pixel.x + pixel.y + pixel.z) / 3.0;\n\ #else\n\ - #ifdef USE_TEXTURE\n\ - color *= texture2D(u_texture, v_coord);\n\ - if(color.a < 0.1)\n\ - discard;\n\ - #endif\n\ - #ifdef USE_POINTS\n\ - float dist = length( gl_PointCoord.xy - vec2(0.5) );\n\ - if( dist > 0.45 )\n\ - discard;\n\ - #endif\n\ - #endif\n\ - #ifdef USE_COLOR\n\ - color *= v_color;\n\ + average_lum = u_average_lum;\n\ #endif\n\ + //Ld - this part of the code is the same for both versions\n\ + float lum = dot(rgb, vec3(0.2126, 0.7152, 0.0722));\n\ + float L = (u_scale / average_lum) * lum;\n\ + float Ld = (L * (1.0 + L / u_lumwhite2)) / (1.0 + L);\n\ + //first\n\ + //vec3 xyY = RGB2xyY(rgb);\n\ + //xyY.z *= Ld;\n\ + //rgb = xyYtoRGB(xyY);\n\ + //second\n\ + rgb = (rgb / lum) * Ld;\n\ + rgb = max(rgb,vec3(0.001));\n\ + rgb = pow( rgb, vec3( u_igamma ) );\n\ + gl_FragColor = vec4( rgb, color.a );\n\ + }"; + + LiteGraph.registerNodeType("texture/tonemapping", LGraphToneMapping); + + function LGraphTexturePerlin() { + this.addOutput("out", "Texture"); + this.properties = { + width: 512, + height: 512, + seed: 0, + persistence: 0.1, + octaves: 8, + scale: 1, + offset: [0, 0], + amplitude: 1, + precision: LGraphTexture.DEFAULT + }; + this._key = 0; + this._texture = null; + this._uniforms = { + u_persistence: 0.1, + u_seed: 0, + u_offset: vec2.create(), + u_scale: 1, + u_viewport: vec2.create() + }; + } + + LGraphTexturePerlin.title = "Perlin"; + LGraphTexturePerlin.desc = "Generates a perlin noise texture"; + + LGraphTexturePerlin.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }, + width: { type: "Number", precision: 0, step: 1 }, + height: { type: "Number", precision: 0, step: 1 }, + octaves: { type: "Number", precision: 0, step: 1, min: 1, max: 50 } + }; + + LGraphTexturePerlin.prototype.onGetInputs = function() { + return [ + ["seed", "Number"], + ["persistence", "Number"], + ["octaves", "Number"], + ["scale", "Number"], + ["amplitude", "Number"], + ["offset", "vec2"] + ]; + }; + + LGraphTexturePerlin.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { + return; + } //saves work + + var w = this.properties.width | 0; + var h = this.properties.height | 0; + if (w == 0) { + w = gl.viewport_data[2]; + } //0 means default + if (h == 0) { + h = gl.viewport_data[3]; + } //0 means default + var type = LGraphTexture.getTextureType(this.properties.precision); + + var temp = this._texture; + if ( + !temp || + temp.width != w || + temp.height != h || + temp.type != type + ) { + temp = this._texture = new GL.Texture(w, h, { + type: type, + format: gl.RGB, + filter: gl.LINEAR + }); + } + + var persistence = this.getInputOrProperty("persistence"); + var octaves = this.getInputOrProperty("octaves"); + var offset = this.getInputOrProperty("offset"); + var scale = this.getInputOrProperty("scale"); + var amplitude = this.getInputOrProperty("amplitude"); + var seed = this.getInputOrProperty("seed"); + + //reusing old texture + var key = + "" + + w + + h + + type + + persistence + + octaves + + scale + + seed + + offset[0] + + offset[1] + + amplitude; + if (key == this._key) { + this.setOutputData(0, temp); + return; + } + this._key = key; + + //gather uniforms + var uniforms = this._uniforms; + uniforms.u_persistence = persistence; + uniforms.u_octaves = octaves; + uniforms.u_offset.set(offset); + uniforms.u_scale = scale; + uniforms.u_amplitude = amplitude; + uniforms.u_seed = seed * 128; + uniforms.u_viewport[0] = w; + uniforms.u_viewport[1] = h; + + //render + var shader = LGraphTexturePerlin._shader; + if (!shader) { + shader = LGraphTexturePerlin._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTexturePerlin.pixel_shader + ); + } + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + + temp.drawTo(function() { + shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad()); + }); + + this.setOutputData(0, temp); + }; + + LGraphTexturePerlin.pixel_shader = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform vec2 u_offset;\n\ + uniform float u_scale;\n\ + uniform float u_persistence;\n\ + uniform int u_octaves;\n\ + uniform float u_amplitude;\n\ + uniform vec2 u_viewport;\n\ + uniform float u_seed;\n\ + #define M_PI 3.14159265358979323846\n\ + \n\ + float rand(vec2 c){ return fract(sin(dot(c.xy ,vec2( 12.9898 + u_seed,78.233 + u_seed))) * 43758.5453); }\n\ + \n\ + float noise(vec2 p, float freq ){\n\ + float unit = u_viewport.x/freq;\n\ + vec2 ij = floor(p/unit);\n\ + vec2 xy = mod(p,unit)/unit;\n\ + //xy = 3.*xy*xy-2.*xy*xy*xy;\n\ + xy = .5*(1.-cos(M_PI*xy));\n\ + float a = rand((ij+vec2(0.,0.)));\n\ + float b = rand((ij+vec2(1.,0.)));\n\ + float c = rand((ij+vec2(0.,1.)));\n\ + float d = rand((ij+vec2(1.,1.)));\n\ + float x1 = mix(a, b, xy.x);\n\ + float x2 = mix(c, d, xy.x);\n\ + return mix(x1, x2, xy.y);\n\ + }\n\ + \n\ + float pNoise(vec2 p, int res){\n\ + float persistance = u_persistence;\n\ + float n = 0.;\n\ + float normK = 0.;\n\ + float f = 4.;\n\ + float amp = 1.0;\n\ + int iCount = 0;\n\ + for (int i = 0; i<50; i++){\n\ + n+=amp*noise(p, f);\n\ + f*=2.;\n\ + normK+=amp;\n\ + amp*=persistance;\n\ + if (iCount >= res)\n\ + break;\n\ + iCount++;\n\ + }\n\ + float nf = n/normK;\n\ + return nf*nf*nf*nf;\n\ + }\n\ + void main() {\n\ + vec2 uv = v_coord * u_scale * u_viewport + u_offset * u_scale;\n\ + vec4 color = vec4( pNoise( uv, u_octaves ) * u_amplitude );\n\ gl_FragColor = color;\n\ - }\ - '; - */ + }"; + LiteGraph.registerNodeType("texture/perlin", LGraphTexturePerlin); + function LGraphTextureCanvas2D() { + this.addInput("v"); + this.addOutput("out", "Texture"); + this.properties = { + code: LGraphTextureCanvas2D.default_code, + width: 512, + height: 512, + clear: true, + precision: LGraphTexture.DEFAULT, + use_html_canvas: false + }; + this._func = null; + this._temp_texture = null; + this.compileCode(); + } + + LGraphTextureCanvas2D.title = "Canvas2D"; + LGraphTextureCanvas2D.desc = "Executes Canvas2D code inside a texture or the viewport."; + LGraphTextureCanvas2D.help = "Set width and height to 0 to match viewport size."; + + LGraphTextureCanvas2D.default_code = "//vars: canvas,ctx,time\nctx.fillStyle='red';\nctx.fillRect(0,0,50,50);\n"; + + LGraphTextureCanvas2D.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }, + code: { type: "code" }, + width: { type: "Number", precision: 0, step: 1 }, + height: { type: "Number", precision: 0, step: 1 } + }; + + LGraphTextureCanvas2D.prototype.onPropertyChanged = function( name, value ) { + if (name == "code" ) + this.compileCode( value ); + } + + LGraphTextureCanvas2D.prototype.compileCode = function( code ) { + this._func = null; + if( !LiteGraph.allow_scripts ) + return; + + try { + this._func = new Function( "canvas", "ctx", "time", "script","v", code ); + this.boxcolor = "#00FF00"; + } catch (err) { + this.boxcolor = "#FF0000"; + console.error("Error parsing script"); + console.error(err); + } + }; + + LGraphTextureCanvas2D.prototype.onExecute = function() { + var func = this._func; + if (!func || !this.isOutputConnected(0)) { + return; + } + this.executeDraw( func ); + } + + LGraphTextureCanvas2D.prototype.executeDraw = function( func_context ) { + + var width = this.properties.width || gl.canvas.width; + var height = this.properties.height || gl.canvas.height; + var temp = this._temp_texture; + var type = LGraphTexture.getTextureType( this.properties.precision ); + if (!temp || temp.width != width || temp.height != height || temp.type != type ) { + temp = this._temp_texture = new GL.Texture(width, height, { + format: gl.RGBA, + filter: gl.LINEAR, + type: type + }); + } + + var v = this.getInputData(0); + + var properties = this.properties; + var that = this; + var time = this.graph.getTime(); + var ctx = gl; + var canvas = gl.canvas; + if( this.properties.use_html_canvas || !global.enableWebGLCanvas ) + { + if(!this._canvas) + { + canvas = this._canvas = createCanvas(width.height); + ctx = this._ctx = canvas.getContext("2d"); + } + else + { + canvas = this._canvas; + ctx = this._ctx; + } + canvas.width = width; + canvas.height = height; + } + + if(ctx == gl) //using Canvas2DtoWebGL + temp.drawTo(function() { + gl.start2D(); + if(properties.clear) + { + gl.clearColor(0,0,0,0); + gl.clear( gl.COLOR_BUFFER_BIT ); + } + + try { + if (func_context.draw) { + func_context.draw.call(that, canvas, ctx, time, func_context, v); + } else { + func_context.call(that, canvas, ctx, time, func_context,v); + } + that.boxcolor = "#00FF00"; + } catch (err) { + that.boxcolor = "#FF0000"; + console.error("Error executing script"); + console.error(err); + } + gl.finish2D(); + }); + else //rendering to offscren canvas and uploading to texture + { + if(properties.clear) + ctx.clearRect(0,0,canvas.width,canvas.height); + + try { + if (func_context.draw) { + func_context.draw.call(this, canvas, ctx, time, func_context, v); + } else { + func_context.call(this, canvas, ctx, time, func_context,v); + } + this.boxcolor = "#00FF00"; + } catch (err) { + this.boxcolor = "#FF0000"; + console.error("Error executing script"); + console.error(err); + } + temp.uploadImage( canvas ); + } + + this.setOutputData(0, temp); + }; + + LiteGraph.registerNodeType("texture/canvas2D", LGraphTextureCanvas2D); + + // To do chroma keying ***************** + + function LGraphTextureMatte() { + this.addInput("in", "Texture"); + + this.addOutput("out", "Texture"); + this.properties = { + key_color: vec3.fromValues(0, 1, 0), + threshold: 0.8, + slope: 0.2, + precision: LGraphTexture.DEFAULT + }; + } + + LGraphTextureMatte.title = "Matte"; + LGraphTextureMatte.desc = "Extracts background"; + + LGraphTextureMatte.widgets_info = { + key_color: { widget: "color" }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureMatte.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { + return; + } //saves work + + var tex = this.getInputData(0); + + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, tex); + return; + } + + if (!tex) { + return; + } + + this._tex = LGraphTexture.getTargetTexture( + tex, + this._tex, + this.properties.precision + ); + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + + if (!this._uniforms) { + this._uniforms = { + u_texture: 0, + u_key_color: this.properties.key_color, + u_threshold: 1, + u_slope: 1 + }; + } + var uniforms = this._uniforms; + + var mesh = Mesh.getScreenQuad(); + var shader = LGraphTextureMatte._shader; + if (!shader) { + shader = LGraphTextureMatte._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureMatte.pixel_shader + ); + } + + uniforms.u_key_color = this.properties.key_color; + uniforms.u_threshold = this.properties.threshold; + uniforms.u_slope = this.properties.slope; + + this._tex.drawTo(function() { + tex.bind(0); + shader.uniforms(uniforms).draw(mesh); + }); + + this.setOutputData(0, this._tex); + }; + + LGraphTextureMatte.pixel_shader = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec3 u_key_color;\n\ + uniform float u_threshold;\n\ + uniform float u_slope;\n\ + \n\ + void main() {\n\ + vec3 color = texture2D( u_texture, v_coord ).xyz;\n\ + float diff = length( normalize(color) - normalize(u_key_color) );\n\ + float edge = u_threshold * (1.0 - u_slope);\n\ + float alpha = smoothstep( edge, u_threshold, diff);\n\ + gl_FragColor = vec4( color, alpha );\n\ + }"; + + LiteGraph.registerNodeType("texture/matte", LGraphTextureMatte); + + //*********************************** + function LGraphCubemapToTexture2D() { + this.addInput("in", "texture"); + this.addInput("yaw", "number"); + this.addOutput("out", "texture"); + this.properties = { yaw: 0 }; + } + + LGraphCubemapToTexture2D.title = "CubemapToTexture2D"; + LGraphCubemapToTexture2D.desc = "Transforms a CUBEMAP texture into a TEXTURE2D in Polar Representation"; + + LGraphCubemapToTexture2D.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) + return; + + var tex = this.getInputData(0); + if ( !tex || tex.texture_type != GL.TEXTURE_CUBE_MAP ) + return; + if( this._last_tex && ( this._last_tex.height != tex.height || this._last_tex.type != tex.type )) + this._last_tex = null; + var yaw = this.getInputOrProperty("yaw"); + this._last_tex = GL.Texture.cubemapToTexture2D( tex, tex.height, this._last_tex, true, yaw ); + this.setOutputData( 0, this._last_tex ); + }; + + LiteGraph.registerNodeType( "texture/cubemapToTexture2D", LGraphCubemapToTexture2D ); +})(this); -})(this); (function(global) { var LiteGraph = global.LiteGraph; @@ -24486,7 +21047,7 @@ function LGraphGeometryDisplace() { global.LGraphFXVigneting = LGraphFXVigneting; } })(this); - + (function(global) { var LiteGraph = global.LiteGraph; var MIDI_COLOR = "#243"; @@ -26070,7 +22631,7 @@ function LGraphGeometryDisplace() { return window.performance.now(); } })(this); - + (function(global) { var LiteGraph = global.LiteGraph; @@ -27527,7 +24088,7 @@ LiteGraph.registerNodeType("audio/waveShaper", LGAudioWaveShaper); LGAudioDestination.desc = "Audio output"; LiteGraph.registerNodeType("audio/destination", LGAudioDestination); })(this); - + //event related nodes (function(global) { var LiteGraph = global.LiteGraph; @@ -27893,4 +24454,3 @@ LiteGraph.registerNodeType("audio/waveShaper", LGAudioWaveShaper); LiteGraph.registerNodeType("network/sillyclient", LGSillyClient); })(this); - diff --git a/build/litegraph.min.js b/build/litegraph.min.js index 2054e16b3..514a07e55 100755 --- a/build/litegraph.min.js +++ b/build/litegraph.min.js @@ -1,717 +1,10335 @@ -(function(B){function c(a){h.debug&&console.log("Graph created");this.list_of_graphcanvas=null;this.clear();a&&this.configure(a)}function m(a,b,d,g,f,r){this.id=a;this.type=b;this.origin_id=d;this.origin_slot=g;this.target_id=f;this.target_slot=r;this._data=null;this._pos=new Float32Array(2)}function n(a){this._ctor(a)}function l(a){this._ctor(a)}function x(a,b){this.offset=new Float32Array([0,0]);this.scale=1;this.max_scale=10;this.min_scale=0.1;this.onredraw=null;this.enabled=!0;this.last_mouse= -[0,0];this.element=null;this.visible_area=new Float32Array(4);a&&(this.element=a,b||this.bindEvents(a))}function e(a,b,d){d=d||{};this.background_image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; -a&&a.constructor===String&&(a=document.querySelector(a));this.ds=new x;this.zoom_modify_alpha=!0;this.title_text_font=""+h.NODE_TEXT_SIZE+"px Arial";this.inner_text_font="normal "+h.NODE_SUBTEXT_SIZE+"px Arial";this.node_title_color=h.NODE_TITLE_COLOR;this.default_link_color=h.LINK_COLOR;this.default_connection_color={input_off:"#778",input_on:"#7F7",output_off:"#778",output_on:"#7F7"};this.highquality_render=!0;this.use_gradients=!1;this.editor_alpha=1;this.pause_rendering=!1;this.clear_background= -!0;this.read_only=!1;this.render_only_selected=!0;this.live_mode=!1;this.allow_searchbox=this.allow_interaction=this.allow_dragnodes=this.allow_dragcanvas=this.show_info=!0;this.drag_mode=this.allow_reconnect_links=!1;this.filter=this.dragging_rectangle=null;this.always_render_background=!1;this.render_canvas_border=this.render_shadows=!0;this.render_connections_shadows=!1;this.render_connections_border=!0;this.render_connection_arrows=this.render_curved_connections=!1;this.render_collapsed_slots= -!0;this.render_execution_order=!1;this.render_link_tooltip=this.render_title_colored=!0;this.links_render_mode=h.SPLINE_LINK;this.canvas_mouse=[0,0];this.onSelectionChange=this.onNodeMoved=this.onDrawLinkTooltip=this.onDrawOverlay=this.onDrawForeground=this.onDrawBackground=this.onMouse=this.onSearchBoxSelection=this.onSearchBox=null;this.connections_width=3;this.round_radius=8;this.over_link_center=this.node_widget=this.current_node=null;this.last_mouse_position=[0,0];this.visible_area=this.ds.visible_area; -this.visible_links=[];b&&b.attachCanvas(this);this.setCanvas(a);this.clear();d.skip_render||this.startRendering();this.autoresize=d.autoresize}function C(a,b){return Math.sqrt((b[0]-a[0])*(b[0]-a[0])+(b[1]-a[1])*(b[1]-a[1]))}function w(a,b,d,g,f,r){return da&&gb?!0:!1}function u(a,b){var d=a[0]+a[2],g=a[1]+a[3],f=b[1]+b[3];return a[0]>b[0]+b[2]||a[1]>f||dc.width-k.width-10&&(f=c.width-k.width-10);h>c.height-k.height-10&&(h=c.height-k.height-10)}r.style.left=f+"px";r.style.top=h+"px";b.scale&&(r.style.transform="scale("+b.scale+")")}function A(a){this.points=a;this.nearest=this.selected=-1;this.size=null;this.must_update=!0;this.margin=5}var h=B.LiteGraph={VERSION:0.4,CANVAS_GRID_SIZE:10,NODE_TITLE_HEIGHT:30,NODE_TITLE_TEXT_Y:20,NODE_SLOT_HEIGHT:20,NODE_WIDGET_HEIGHT:20,NODE_WIDTH:140,NODE_MIN_WIDTH:50,NODE_COLLAPSED_RADIUS:10, -NODE_COLLAPSED_WIDTH:80,NODE_TITLE_COLOR:"#999",NODE_TEXT_SIZE:14,NODE_TEXT_COLOR:"#AAA",NODE_SUBTEXT_SIZE:12,NODE_DEFAULT_COLOR:"#333",NODE_DEFAULT_BGCOLOR:"#353535",NODE_DEFAULT_BOXCOLOR:"#666",NODE_DEFAULT_SHAPE:"box",DEFAULT_SHADOW_COLOR:"rgba(0,0,0,0.5)",DEFAULT_GROUP_FONT:24,WIDGET_BGCOLOR:"#222",WIDGET_OUTLINE_COLOR:"#666",WIDGET_TEXT_COLOR:"#DDD",WIDGET_SECONDARY_TEXT_COLOR:"#999",LINK_COLOR:"#9A9",EVENT_LINK_COLOR:"#A86",CONNECTING_LINK_COLOR:"#AFA",MAX_NUMBER_OF_NODES:1E3,DEFAULT_POSITION:[100, -100],VALID_SHAPES:["default","box","round","card"],BOX_SHAPE:1,ROUND_SHAPE:2,CIRCLE_SHAPE:3,CARD_SHAPE:4,ARROW_SHAPE:5,INPUT:1,OUTPUT:2,EVENT:-1,ACTION:-1,ALWAYS:0,ON_EVENT:1,NEVER:2,ON_TRIGGER:3,UP:1,DOWN:2,LEFT:3,RIGHT:4,CENTER:5,STRAIGHT_LINK:0,LINEAR_LINK:1,SPLINE_LINK:2,NORMAL_TITLE:0,NO_TITLE:1,TRANSPARENT_TITLE:2,AUTOHIDE_TITLE:3,proxy:null,node_images_path:"",debug:!1,catch_exceptions:!0,throw_errors:!0,allow_scripts:!1,registered_node_types:{},node_types_by_file_extension:{},Nodes:{},searchbox_extras:{}, -registerNodeType:function(a,b){if(!b.prototype)throw"Cannot register a simple object, it must be a class with a prototype";b.type=a;h.debug&&console.log("Node registered: "+a);a.split("/");var d=b.name,g=a.lastIndexOf("/");b.category=a.substr(0,g);b.title||(b.title=d);if(b.prototype)for(var f in n.prototype)b.prototype[f]||(b.prototype[f]=n.prototype[f]);if(g=this.registered_node_types[a])console.log("replacing node type: "+a);else if(Object.hasOwnProperty(b.prototype,"shape")||Object.defineProperty(b.prototype, -"shape",{set:function(a){switch(a){case "default":delete this._shape;break;case "box":this._shape=h.BOX_SHAPE;break;case "round":this._shape=h.ROUND_SHAPE;break;case "circle":this._shape=h.CIRCLE_SHAPE;break;case "card":this._shape=h.CARD_SHAPE;break;default:this._shape=a}},get:function(a){return this._shape},enumerable:!0,configurable:!0}),b.prototype.onPropertyChange&&console.warn("LiteGraph node class "+a+" has onPropertyChange method, it must be called onPropertyChanged with d at the end"),b.supported_extensions)for(f in b.supported_extensions){var r= -b.supported_extensions[f];r&&r.constructor===String&&(this.node_types_by_file_extension[r.toLowerCase()]=b)}this.registered_node_types[a]=b;b.constructor.name&&(this.Nodes[d]=b);if(h.onNodeTypeRegistered)h.onNodeTypeRegistered(a,b);if(g&&h.onNodeTypeReplaced)h.onNodeTypeReplaced(a,b,g)},unregisterNodeType:function(a){var b=a.constructor===String?this.registered_node_types[a]:a;if(!b)throw"node type not found: "+a;delete this.registered_node_types[b.type];b.constructor.name&&delete this.Nodes[b.constructor.name]}, -wrapFunctionAsNode:function(a,b,d,g,f){for(var r=Array(b.length),s="",c=h.getParameterNames(b),k=0;ks&&(s=f.size[0]),c+=f.size[1]+a+h.NODE_TITLE_HEIGHT;b+=s+a}this.setDirtyCanvas(!0,!0)};c.prototype.getTime=function(){return this.globaltime};c.prototype.getFixedTime=function(){return this.fixedtime};c.prototype.getElapsedTime=function(){return this.elapsed_time};c.prototype.sendEventToAllNodes=function(a,b,d){d=d||h.ALWAYS;var g=this._nodes_in_order? -this._nodes_in_order:this._nodes;if(g)for(var f=0,r=g.length;f=h.MAX_NUMBER_OF_NODES)throw"LiteGraph: max number of nodes in a graph reached";null==a.id||-1==a.id?a.id=++this.last_node_id:this.last_node_ida.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos}, -enumerable:!0});this.id=-1;this.type=null;this.inputs=[];this.outputs=[];this.connections=[];this.properties={};this.properties_info=[];this.flags={}};n.prototype.configure=function(a){this.graph&&this.graph._version++;for(var b in a)if("properties"==b)for(var d in a.properties){if(this.properties[d]=a.properties[d],this.onPropertyChanged)this.onPropertyChanged(d,a.properties[d])}else null!=a[b]&&("object"==typeof a[b]?this[b]&&this[b].configure?this[b].configure(a[b]):this[b]=h.cloneObject(a[b], -this[b]):this[b]=a[b]);a.title||(this.title=this.constructor.title);if(this.onConnectionsChange){if(this.inputs)for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d._data=b,this.outputs[a].links))for(d=0;d=this.outputs.length)){var d=this.outputs[a]; -if(d&&(d.type=b,this.outputs[a].links))for(d=0;d=this.inputs.length||null==this.inputs[a].link)){var d=this.graph.links[this.inputs[a].link];if(!d)return null;if(!b)return d.data;var g=this.graph.getNodeById(d.origin_id);if(!g)return d.data;if(g.updateOutputData)g.updateOutputData(d.origin_slot);else if(g.onExecute)g.onExecute();return d.data}};n.prototype.getInputDataType= -function(a){if(!this.inputs||a>=this.inputs.length||null==this.inputs[a].link)return null;a=this.graph.links[this.inputs[a].link];if(!a)return null;var b=this.graph.getNodeById(a.origin_id);return b?(a=b.outputs[a.origin_slot])?a.type:null:a.type};n.prototype.getInputDataByName=function(a,b){var d=this.findInputSlot(a);return-1==d?null:this.getInputData(d,b)};n.prototype.isInputConnected=function(a){return this.inputs?a=this.inputs.length)return null;a=this.inputs[a];return a&&null!==a.link?(a=this.graph.links[a.link])?this.graph.getNodeById(a.origin_id):null:null};n.prototype.getInputOrProperty=function(a){if(!this.inputs||!this.inputs.length)return this.properties?this.properties[a]:null;for(var b=0,d=this.inputs.length;b=this.outputs.length?null:this.outputs[a]._data};n.prototype.getOutputInfo=function(a){return this.outputs?a=this.outputs.length)return null;a=this.outputs[a];if(!a.links||0==a.links.length)return null;for(var b=[],d=0;da&&this.pos[1]-f-db)return!0;return!1};n.prototype.getSlotInPosition=function(a,b){var d=new Float32Array(2);if(this.inputs)for(var g=0,f=this.inputs.length;g=this.outputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),null;b&&b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"target node is null";if(b==this)return null;if(d.constructor===String){if(d=b.findInputSlot(d),-1==d)return h.debug&&console.log("Connect: Error, no slot of name "+d),null}else{if(d=== -h.EVENT)return null;if(!b.inputs||d>=b.inputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),null}null!=b.inputs[d].link&&b.disconnectInput(d);var g=this.outputs[a];if(b.onConnectInput&&!1===b.onConnectInput(d,g.type,g))return null;var f=b.inputs[d],r=null;if(h.isValidConnection(g.type,f.type)){r=new m(++this.graph.last_link_id,f.type,this.id,a,b.id,d);this.graph.links[r.id]=r;null==g.links&&(g.links=[]);g.links.push(r.id);b.inputs[d].link=r.id;this.graph&&this.graph._version++; -if(this.onConnectionsChange)this.onConnectionsChange(h.OUTPUT,a,!0,r,g);if(b.onConnectionsChange)b.onConnectionsChange(h.INPUT,d,!0,r,f);this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(h.INPUT,b,d,this,a),this.graph.onNodeConnectionChange(h.OUTPUT,this,a,b,d))}this.setDirtyCanvas(!1,!0);this.graph.connectionChange(this,r);return r};n.prototype.disconnectOutput=function(a,b){if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return h.debug&&console.log("Connect: Error, no slot of name "+ -a),!1}else if(!this.outputs||a>=this.outputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),!1;var d=this.outputs[a];if(!d||!d.links||0==d.links.length)return!1;if(b){b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"Target Node not found";for(var g=0,f=d.links.length;g=this.inputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),!1;var b=this.inputs[a];if(!b)return!1;var d=this.inputs[a].link;this.inputs[a].link=null;var g=this.graph.links[d];if(g){var f= -this.graph.getNodeById(g.origin_id);if(!f)return!1;var r=f.outputs[g.origin_slot];if(!r||!r.links||0==r.links.length)return!1;for(var s=0,c=r.links.length;sb&&this.inputs[b].pos)return d[0]=this.pos[0]+this.inputs[b].pos[0],d[1]=this.pos[1]+this.inputs[b].pos[1],d;if(!a&&g>b&&this.outputs[b].pos)return d[0]=this.pos[0]+this.outputs[b].pos[0],d[1]=this.pos[1]+this.outputs[b].pos[1],d;if(this.horizontal)return d[0]=this.pos[0]+this.size[0]/g*(b+0.5),d[1]=a?this.pos[1]-h.NODE_TITLE_HEIGHT: -this.pos[1]+this.size[1],d;d[0]=a?this.pos[0]+f:this.pos[0]+this.size[0]+1-f;d[1]=this.pos[1]+(b+0.7)*h.NODE_SLOT_HEIGHT+(this.constructor.slot_start_y||0);return d};n.prototype.alignToGrid=function(){this.pos[0]=h.CANVAS_GRID_SIZE*Math.round(this.pos[0]/h.CANVAS_GRID_SIZE);this.pos[1]=h.CANVAS_GRID_SIZE*Math.round(this.pos[1]/h.CANVAS_GRID_SIZE)};n.prototype.trace=function(a){this.console||(this.console=[]);this.console.push(a);this.console.length>n.MAX_CONSOLE&&this.console.shift();this.graph.onNodeTrace(this, -a)};n.prototype.setDirtyCanvas=function(a,b){this.graph&&this.graph.sendActionToCanvas("setDirty",[a,b])};n.prototype.loadImage=function(a){var b=new Image;b.src=h.node_images_path+a;b.ready=!1;var d=this;b.onload=function(){this.ready=!0;d.setDirtyCanvas(!0)};return b};n.prototype.captureInput=function(a){if(this.graph&&this.graph.list_of_graphcanvas)for(var b=this.graph.list_of_graphcanvas,d=0;da.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});Object.defineProperty(this,"size",{set:function(a){!a||2>a.length||(this._size[0]=Math.max(140,a[0]),this._size[1]=Math.max(80,a[1]))},get:function(){return this._size}, -enumerable:!0})};l.prototype.configure=function(a){this.title=a.title;this._bounding.set(a.bounding);this.color=a.color;this.font=a.font};l.prototype.serialize=function(){var a=this._bounding;return{title:this.title,bounding:[Math.round(a[0]),Math.round(a[1]),Math.round(a[2]),Math.round(a[3])],color:this.color,font:this.font}};l.prototype.move=function(a,b,d){this._pos[0]+=a;this._pos[1]+=b;if(!d)for(d=0;dthis.max_scale&&(a=this.max_scale);if(a!=this.scale&&this.element){var d=this.element.getBoundingClientRect();if(d){b=b||[0.5*d.width,0.5*d.height];d=this.convertCanvasToOffset(b);this.scale=a;0.01>Math.abs(this.scale-1)&&(this.scale= -1);var g=this.convertCanvasToOffset(b),d=[g[0]-d[0],g[1]-d[1]];this.offset[0]+=d[0];this.offset[1]+=d[1];if(this.onredraw)this.onredraw(this)}}};x.prototype.changeDeltaScale=function(a,b){this.changeScale(this.scale*a,b)};x.prototype.reset=function(){this.scale=1;this.offset[0]=0;this.offset[1]=0};B.LGraphCanvas=h.LGraphCanvas=e;e.link_type_colors={"-1":h.EVENT_LINK_COLOR,number:"#AAA",node:"#DCA"};e.gradients={};e.prototype.clear=function(){this.fps=this.render_time=this.last_draw_time=this.frame= -0;this.dragging_rectangle=null;this.selected_nodes={};this.selected_group=null;this.visible_nodes=[];this.connecting_node=this.node_capturing_input=this.node_over=this.node_dragged=null;this.highlighted_links={};this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_widget=this.node_in_panel=this.dirty_area=null;this.last_mouse=[0,0];this.last_mouseclick=0;this.visible_area.set([0,0,0,0]);if(this.onClear)this.onClear()};e.prototype.setGraph=function(a,b){this.graph!=a&&(b||this.clear(),!a&&this.graph? -this.graph.detachCanvas(this):(a.attachCanvas(this),this._graph_stack&&(this._graph_stack=null),this.setDirty(!0,!0)))};e.prototype.openSubgraph=function(a){if(!a)throw"graph cannot be null";if(this.graph==a)throw"graph cannot be the same";this.clear();this.graph&&(this._graph_stack||(this._graph_stack=[]),this._graph_stack.push(this.graph));a.attachCanvas(this);this.setDirty(!0,!0)};e.prototype.closeSubgraph=function(){if(this._graph_stack&&0!=this._graph_stack.length){var a=this.graph._subgraph_node, -b=this._graph_stack.pop();this.selected_nodes={};this.highlighted_links={};b.attachCanvas(this);this.setDirty(!0,!0);a&&(this.centerOnNode(a),this.selectNodes([a]))}};e.prototype.getCurrentGraph=function(){return this.graph};e.prototype.setCanvas=function(a,b){if(a&&a.constructor===String&&(a=document.getElementById(a),!a))throw"Error creating LiteGraph canvas: Canvas not found";if(a!==this.canvas&&(!a&&this.canvas&&(b||this.unbindEvents()),this.canvas=a,this.ds.element=a)){a.className+=" lgraphcanvas"; -a.data=this;a.tabindex="1";this.bgcanvas=null;this.bgcanvas||(this.bgcanvas=document.createElement("canvas"),this.bgcanvas.width=this.canvas.width,this.bgcanvas.height=this.canvas.height);if(null==a.getContext){if("canvas"!=a.localName)throw"Element supplied for LGraphCanvas must be a element, you passed a "+a.localName;throw"This browser doesn't support Canvas";}null==(this.ctx=a.getContext("2d"))&&(a.webgl_enabled||console.warn("This canvas seems to be WebGL, enabling WebGL renderer"), -this.enableWebGL());this._mousemove_callback=this.processMouseMove.bind(this);this._mouseup_callback=this.processMouseUp.bind(this);b||this.bindEvents()}};e.prototype._doNothing=function(a){a.preventDefault();return!1};e.prototype._doReturnTrue=function(a){a.preventDefault();return!0};e.prototype.bindEvents=function(){if(this._events_binded)console.warn("LGraphCanvas: events already binded");else{var a=this.canvas,b=this.getCanvasWindow().document;this._mousedown_callback=this.processMouseDown.bind(this); -this._mousewheel_callback=this.processMouseWheel.bind(this);a.addEventListener("mousedown",this._mousedown_callback,!0);a.addEventListener("mousemove",this._mousemove_callback);a.addEventListener("mousewheel",this._mousewheel_callback,!1);a.addEventListener("contextmenu",this._doNothing);a.addEventListener("DOMMouseScroll",this._mousewheel_callback,!1);a.addEventListener("touchstart",this.touchHandler,!0);a.addEventListener("touchmove",this.touchHandler,!0);a.addEventListener("touchend",this.touchHandler, -!0);a.addEventListener("touchcancel",this.touchHandler,!0);this._key_callback=this.processKey.bind(this);a.addEventListener("keydown",this._key_callback,!0);b.addEventListener("keyup",this._key_callback,!0);this._ondrop_callback=this.processDrop.bind(this);a.addEventListener("dragover",this._doNothing,!1);a.addEventListener("dragend",this._doNothing,!1);a.addEventListener("drop",this._ondrop_callback,!1);a.addEventListener("dragenter",this._doReturnTrue,!1);this._events_binded=!0}};e.prototype.unbindEvents= -function(){if(this._events_binded){var a=this.getCanvasWindow().document;this.canvas.removeEventListener("mousedown",this._mousedown_callback);this.canvas.removeEventListener("mousewheel",this._mousewheel_callback);this.canvas.removeEventListener("DOMMouseScroll",this._mousewheel_callback);this.canvas.removeEventListener("keydown",this._key_callback);a.removeEventListener("keyup",this._key_callback);this.canvas.removeEventListener("contextmenu",this._doNothing);this.canvas.removeEventListener("drop", -this._ondrop_callback);this.canvas.removeEventListener("dragenter",this._doReturnTrue);this.canvas.removeEventListener("touchstart",this.touchHandler);this.canvas.removeEventListener("touchmove",this.touchHandler);this.canvas.removeEventListener("touchend",this.touchHandler);this.canvas.removeEventListener("touchcancel",this.touchHandler);this._ondrop_callback=this._key_callback=this._mousewheel_callback=this._mousedown_callback=null;this._events_binded=!1}else console.warn("LGraphCanvas: no events binded")}; -e.getFileExtension=function(a){var b=a.indexOf("?");-1!=b&&(a=a.substr(0,b));b=a.lastIndexOf(".");return-1==b?"":a.substr(b+1).toLowerCase()};e.prototype.enableWebGL=function(){if(void 0===typeof GL)throw"litegl.js must be included to use a WebGL canvas";if(void 0===typeof enableWebGLCanvas)throw"webglCanvas.js must be included to use this feature";this.gl=this.ctx=enableWebGLCanvas(this.canvas);this.ctx.webgl=!0;this.bgcanvas=this.canvas;this.bgctx=this.gl;this.canvas.webgl_enabled=!0};e.prototype.setDirty= -function(a,b){a&&(this.dirty_canvas=!0);b&&(this.dirty_bgcanvas=!0)};e.prototype.getCanvasWindow=function(){if(!this.canvas)return window;var a=this.canvas.ownerDocument;return a.defaultView||a.parentWindow};e.prototype.startRendering=function(){function a(){this.pause_rendering||this.draw();var b=this.getCanvasWindow();this.is_rendering&&b.requestAnimationFrame(a.bind(this))}this.is_rendering||(this.is_rendering=!0,a.call(this))};e.prototype.stopRendering=function(){this.is_rendering=!1};e.prototype.processMouseDown= -function(a){if(this.graph){this.adjustMouseEvent(a);var b=this.getCanvasWindow();e.active_canvas=this;this.canvas.removeEventListener("mousemove",this._mousemove_callback);b.document.addEventListener("mousemove",this._mousemove_callback,!0);b.document.addEventListener("mouseup",this._mouseup_callback,!0);var d=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes,5),g=!1,f=300>h.getTime()-this.last_mouseclick;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;this.canvas.focus(); -h.closeAllContextMenus(b);if(!this.onMouse||!0!=this.onMouse(a)){if(1==a.which){a.ctrlKey&&(this.dragging_rectangle=new Float32Array(4),this.dragging_rectangle[0]=a.canvasX,this.dragging_rectangle[1]=a.canvasY,this.dragging_rectangle[2]=1,this.dragging_rectangle[3]=1,g=!0);var r=!1;if(d&&this.allow_interaction&&!g&&!this.read_only){this.live_mode||d.flags.pinned||this.bringToFront(d);if(!this.connecting_node&&!d.flags.collapsed&&!this.live_mode)if(!g&&!1!==d.resizable&&w(a.canvasX,a.canvasY,d.pos[0]+ -d.size[0]-5,d.pos[1]+d.size[1]-5,10,10))this.resizing_node=d,this.canvas.style.cursor="se-resize",g=!0;else{if(d.outputs)for(var s=0,c=d.outputs.length;sr[0]+4||a.canvasYr[1]+4)){this.showLinkMenu(d,a);this.over_link_center=null;break}this.selected_group=this.graph.getGroupOnPos(a.canvasX,a.canvasY);this.selected_group_resizing=!1;this.selected_group&&!this.read_only&&(a.ctrlKey&&(this.dragging_rectangle=null),10>C([a.canvasX, -a.canvasY],[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])*this.ds.scale?this.selected_group_resizing=!0:this.selected_group.recomputeInsideNodes());f&&!this.read_only&&this.allow_searchbox&&this.showSearchBox(a);r=!0}!g&&r&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}else 2!=a.which&&3==a.which&&(this.read_only||this.processContextMenu(d,a));this.last_mouse[0]=a.localX;this.last_mouse[1]=a.localY;this.last_mouseclick=h.getTime(); -this.last_mouse_dragging=!0;this.graph.change();(!b.document.activeElement||"input"!=b.document.activeElement.nodeName.toLowerCase()&&"textarea"!=b.document.activeElement.nodeName.toLowerCase())&&a.preventDefault();a.stopPropagation();if(this.onMouseDown)this.onMouseDown(a);return!1}}};e.prototype.processMouseMove=function(a){this.autoresize&&this.resize();if(this.graph){e.active_canvas=this;this.adjustMouseEvent(a);var b=[a.localX,a.localY],d=[b[0]-this.last_mouse[0],b[1]-this.last_mouse[1]];this.last_mouse= -b;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;a.dragging=this.last_mouse_dragging;this.node_widget&&(this.processNodeWidgets(this.node_widget[0],this.canvas_mouse,a,this.node_widget[1]),this.dirty_canvas=!0);if(this.dragging_rectangle)this.dragging_rectangle[2]=a.canvasX-this.dragging_rectangle[0],this.dragging_rectangle[3]=a.canvasY-this.dragging_rectangle[1],this.dirty_canvas=!0;else if(this.selected_group&&!this.read_only)this.selected_group_resizing?this.selected_group.size= -[a.canvasX-this.selected_group.pos[0],a.canvasY-this.selected_group.pos[1]]:(this.selected_group.move(d[0]/this.ds.scale,d[1]/this.ds.scale,a.ctrlKey),this.selected_group._nodes.length&&(this.dirty_canvas=!0)),this.dirty_bgcanvas=!0;else if(this.dragging_canvas)this.ds.offset[0]+=d[0]/this.ds.scale,this.ds.offset[1]+=d[1]/this.ds.scale,this.dirty_bgcanvas=this.dirty_canvas=!0;else if(this.allow_interaction&&!this.read_only){this.connecting_node&&(this.dirty_canvas=!0);for(var g=this.graph.getNodeOnPos(a.canvasX, -a.canvasY,this.visible_nodes),b=0,f=this.graph._nodes.length;bs[0]+4||a.canvasYs[1]+4)){f=r;break}}f!=this.over_link_center&&(this.over_link_center=f,this.dirty_canvas=!0);this.canvas&&(this.canvas.style.cursor="")}if(this.node_capturing_input&&this.node_capturing_input!=g&&this.node_capturing_input.onMouseMove)this.node_capturing_input.onMouseMove(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]],this);if(this.node_dragged&&!this.live_mode){for(b in this.selected_nodes)g=this.selected_nodes[b], -g.pos[0]+=d[0]/this.ds.scale,g.pos[1]+=d[1]/this.ds.scale;this.dirty_bgcanvas=this.dirty_canvas=!0}this.resizing_node&&!this.live_mode&&(this.resizing_node.size[0]=a.canvasX-this.resizing_node.pos[0],this.resizing_node.size[1]=a.canvasY-this.resizing_node.pos[1],d=Math.max(this.resizing_node.inputs?this.resizing_node.inputs.length:0,this.resizing_node.outputs?this.resizing_node.outputs.length:0)*h.NODE_SLOT_HEIGHT+(this.resizing_node.widgets?this.resizing_node.widgets.length:0)*(h.NODE_WIDGET_HEIGHT+ -4)+4,this.resizing_node.size[1]this.dragging_rectangle[3]?this.dragging_rectangle[1]-f:this.dragging_rectangle[1];this.dragging_rectangle[0]= -0>this.dragging_rectangle[2]?this.dragging_rectangle[0]-g:this.dragging_rectangle[0];this.dragging_rectangle[1]=r;this.dragging_rectangle[2]=g;this.dragging_rectangle[3]=f;f=[];for(r=0;ra.click_time&&w(a.canvasX,a.canvasY,g.pos[0],g.pos[1]-h.NODE_TITLE_HEIGHT,h.NODE_TITLE_HEIGHT,h.NODE_TITLE_HEIGHT)&&g.collapse();this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_dragged.pos[0]=Math.round(this.node_dragged.pos[0]);this.node_dragged.pos[1]=Math.round(this.node_dragged.pos[1]);this.graph.config.align_to_grid&& -this.node_dragged.alignToGrid();if(this.onNodeMoved)this.onNodeMoved(this.node_dragged);this.node_dragged=null}else{g=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes);!g&&300>a.click_time&&this.deselectAllNodes();this.dirty_canvas=!0;this.dragging_canvas=!1;if(this.node_over&&this.node_over.onMouseUp)this.node_over.onMouseUp(a,[a.canvasX-this.node_over.pos[0],a.canvasY-this.node_over.pos[1]],this);if(this.node_capturing_input&&this.node_capturing_input.onMouseUp)this.node_capturing_input.onMouseUp(a, -[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]])}}else 2==a.which?(this.dirty_canvas=!0,this.dragging_canvas=!1):3==a.which&&(this.dirty_canvas=!0,this.dragging_canvas=!1);this.graph.change();a.stopPropagation();a.preventDefault();return!1}};e.prototype.processMouseWheel=function(a){if(this.graph&&this.allow_dragcanvas){var b=null!=a.wheelDeltaY?a.wheelDeltaY:-60*a.detail;this.adjustMouseEvent(a);var d=this.ds.scale;0b&&(d*=1/1.1);this.ds.changeScale(d, -[a.localX,a.localY]);this.graph.change();a.preventDefault();return!1}};e.prototype.isOverNodeBox=function(a,b,d){var g=h.NODE_TITLE_HEIGHT;return w(b,d,a.pos[0]+2,a.pos[1]+2-g,g-4,g-4)?!0:!1};e.prototype.isOverNodeInput=function(a,b,d,g){if(a.inputs)for(var f=0,h=a.inputs.length;fd- -this.graph._last_trigger_time)&&this.drawBackCanvas();(this.dirty_canvas||a)&&this.drawFrontCanvas();this.fps=this.render_time?1/this.render_time:0;this.frame+=1}};e.prototype.drawFrontCanvas=function(){this.dirty_canvas=!1;this.ctx||(this.ctx=this.bgcanvas.getContext("2d"));var a=this.ctx;if(a){a.start2D&&a.start2D();var b=this.canvas;a.restore();a.setTransform(1,0,0,1,0,0);this.dirty_area&&(a.save(),a.beginPath(),a.rect(this.dirty_area[0],this.dirty_area[1],this.dirty_area[2],this.dirty_area[3]), -a.clip());this.clear_background&&a.clearRect(0,0,b.width,b.height);this.bgcanvas==this.canvas?this.drawBackCanvas():a.drawImage(this.bgcanvas,0,0);if(this.onRender)this.onRender(b,a);this.show_info&&this.renderInfo(a);if(this.graph){a.save();this.ds.toCanvasContext(a);for(var b=this.computeVisibleNodes(null,this.visible_nodes),d=0;d> ";b.fillText(g+d.getTitle(),0.5*a.width,40);b.restore()}d=!1;this.onRenderBackground&&(d=this.onRenderBackground(a,b));b.restore(); -b.setTransform(1,0,0,1,0,0);this.visible_links.length=0;if(this.graph){b.save();this.ds.toCanvasContext(b);if(this.background_image&&0.5this.ds.scale;if(this.live_mode){if(!a.flags.collapsed&&(b.shadowColor="transparent",a.onDrawForeground))a.onDrawForeground(b,this,this.canvas)}else{var r=this.editor_alpha;b.globalAlpha=r;this.render_shadows&&!f?(b.shadowColor= -h.DEFAULT_SHADOW_COLOR,b.shadowOffsetX=2*this.ds.scale,b.shadowOffsetY=2*this.ds.scale,b.shadowBlur=3*this.ds.scale):b.shadowColor="transparent";if(!a.flags.collapsed||!a.onDrawCollapsed||!0!=a.onDrawCollapsed(b,this)){var s=a._shape||h.BOX_SHAPE;y.set(a.size);var c=a.horizontal;if(a.flags.collapsed){b.font=this.inner_text_font;var k=a.getTitle?a.getTitle():a.title;null!=k&&(a._collapsed_width=Math.min(a.size[0],b.measureText(k).width+2*h.NODE_TITLE_HEIGHT),y[0]=a._collapsed_width,y[1]=0)}a.clip_area&& -(b.save(),b.beginPath(),s==h.BOX_SHAPE?b.rect(0,0,y[0],y[1]):s==h.ROUND_SHAPE?b.roundRect(0,0,y[0],y[1],10):s==h.CIRCLE_SHAPE&&b.arc(0.5*y[0],0.5*y[1],0.5*y[0],0,2*Math.PI),b.clip());a.has_errors&&(g="red");this.drawNodeShape(a,b,y,d,g,a.is_selected,a.mouseOver);b.shadowColor="transparent";if(a.onDrawForeground)a.onDrawForeground(b,this,this.canvas);b.textAlign=c?"center":"left";b.font=this.inner_text_font;g=!f;s=this.connecting_output;b.lineWidth=1;var k=0,e=new Float32Array(2);if(!a.flags.collapsed){if(a.inputs)for(d= -0;dthis.ds.scale,k=a._shape||a.constructor.shape||h.ROUND_SHAPE,p=a.constructor.title_mode,q=!0;p==h.TRANSPARENT_TITLE?q=!1:p==h.AUTOHIDE_TITLE&&s&&(q=!0);v[0]=0;v[1]=q?-f:0;v[2]=d[0]+1;v[3]=q?d[1]+f:d[1];s=b.globalAlpha;b.beginPath();k==h.BOX_SHAPE|| -c?b.fillRect(v[0],v[1],v[2],v[3]):k==h.ROUND_SHAPE||k==h.CARD_SHAPE?b.roundRect(v[0],v[1],v[2],v[3],this.round_radius,k==h.CARD_SHAPE?0:this.round_radius):k==h.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0],0,2*Math.PI);b.fill();a.flags.collapsed||(b.shadowColor="transparent",b.fillStyle="rgba(0,0,0,0.2)",b.fillRect(0,-1,v[2],2));b.shadowColor="transparent";if(a.onDrawBackground)a.onDrawBackground(b,this,this.canvas);if(q||p==h.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,f,d,this.ds.scale, -g);else if(p!=h.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){q=a.constructor.title_color||g;a.flags.collapsed&&(b.shadowColor=h.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var t=e.gradients[q];t||(t=e.gradients[q]=b.createLinearGradient(0,0,400,0),t.addColorStop(0,q),t.addColorStop(1,"#000"));b.fillStyle=t}else b.fillStyle=q;b.beginPath();k==h.BOX_SHAPE||c?b.rect(0,-f,d[0]+1,f):k!=h.ROUND_SHAPE&&k!=h.CARD_SHAPE||b.roundRect(0,-f,d[0]+1,f,this.round_radius,a.flags.collapsed? -this.round_radius:0);b.fill();b.shadowColor="transparent"}if(a.onDrawTitleBox)a.onDrawTitleBox(b,f,d,this.ds.scale);else k==h.ROUND_SHAPE||k==h.CIRCLE_SHAPE||k==h.CARD_SHAPE?(c&&(b.fillStyle="black",b.beginPath(),b.arc(0.5*f,-0.5*f,6,0,2*Math.PI),b.fill()),b.fillStyle=a.boxcolor||h.NODE_DEFAULT_BOXCOLOR,c?b.fillRect(0.5*f-5,-0.5*f-5,10,10):(b.beginPath(),b.arc(0.5*f,-0.5*f,5,0,2*Math.PI),b.fill())):(c&&(b.fillStyle="black",b.fillRect(0.5*(f-10)-1,-0.5*(f+10)-1,12,12)),b.fillStyle=a.boxcolor||h.NODE_DEFAULT_BOXCOLOR, -b.fillRect(0.5*(f-10),-0.5*(f+10),10,10));b.globalAlpha=s;if(a.onDrawTitleText)a.onDrawTitleText(b,f,d,this.ds.scale,this.title_text_font,r);!c&&(b.font=this.title_text_font,c=String(a.getTitle()))&&(b.fillStyle=r?"white":a.constructor.title_text_color||this.node_title_color,a.flags.collapsed?(b.textAlign="left",b.measureText(c),b.fillText(c.substr(0,20),f,h.NODE_TITLE_TEXT_Y-f),b.textAlign="left"):(b.textAlign="left",b.fillText(c,f,h.NODE_TITLE_TEXT_Y-f)));if(a.onDrawTitle)a.onDrawTitle(b)}if(r){if(a.onBounding)a.onBounding(v); -p==h.TRANSPARENT_TITLE&&(v[1]-=f,v[3]+=f);b.lineWidth=1;b.globalAlpha=0.8;b.beginPath();k==h.BOX_SHAPE?b.rect(-6+v[0],-6+v[1],12+v[2],12+v[3]):k==h.ROUND_SHAPE||k==h.CARD_SHAPE&&a.flags.collapsed?b.roundRect(-6+v[0],-6+v[1],12+v[2],12+v[3],2*this.round_radius):k==h.CARD_SHAPE?b.roundRect(-6+v[0],-6+v[1],12+v[2],12+v[3],2*this.round_radius,2):k==h.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0]+6,0,2*Math.PI);b.strokeStyle="#FFF";b.stroke();b.strokeStyle=g;b.globalAlpha=1}};var G=new Float32Array(4), -p=new Float32Array(4),q=new Float32Array(2),k=new Float32Array(2);e.prototype.drawConnections=function(a){var b=h.getTime(),d=this.visible_area;G[0]=d[0]-20;G[1]=d[1]-20;G[2]=d[2]+40;G[3]=d[3]+40;a.lineWidth=this.connections_width;a.fillStyle="#AAA";a.strokeStyle="#AAA";a.globalAlpha=this.editor_alpha;for(var d=this.graph._nodes,g=0,f=d.length;gp[2]&&(p[0]+=p[2],p[2]=Math.abs(p[2]));0>p[3]&&(p[1]+=p[3],p[3]=Math.abs(p[3]));if(u(p,G)){var n=e.outputs[z],z=r.inputs[s];if(n&&z&&(e=n.dir||(e.horizontal?h.DOWN:h.RIGHT),z=z.dir||(r.horizontal?h.UP:h.LEFT),this.renderLink(a,l,t,c,!1,0,null,e,z),c&&c._last_time&&1E3>b-c._last_time)){var n= -2-0.002*(b-c._last_time),K=a.globalAlpha;a.globalAlpha=K*n;this.renderLink(a,l,t,c,!0,n,"white",e,z);a.globalAlpha=K}}}}}}a.globalAlpha=1};e.prototype.renderLink=function(a,b,d,g,f,r,c,k,p,q){g&&this.visible_links.push(g);!c&&g&&(c=g.color||e.link_type_colors[g.type]);c||(c=this.default_link_color);null!=g&&this.highlighted_links[g.id]&&(c="#FFF");k=k||h.RIGHT;p=p||h.LEFT;var z=C(b,d);this.render_connections_border&&0.6b[1]?0:Math.PI,a.save(),a.translate(t[0],t[1]),a.rotate(l),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore(),a.save(),a.translate(q[0],q[1]),a.rotate(n),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill());if(r)for(a.fillStyle= -c,t=0;5>t;++t)r=(0.001*h.getTime()+0.2*t)%1,f=this.computeConnectionPoint(b,d,r,k,p),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill()};e.prototype.computeConnectionPoint=function(a,b,d,g,f){g=g||h.RIGHT;f=f||h.LEFT;var r=C(a,b),c=[a[0],a[1]],k=[b[0],b[1]];switch(g){case h.LEFT:c[0]+=-0.25*r;break;case h.RIGHT:c[0]+=0.25*r;break;case h.UP:c[1]+=-0.25*r;break;case h.DOWN:c[1]+=0.25*r}switch(f){case h.LEFT:k[0]+=-0.25*r;break;case h.RIGHT:k[0]+=0.25*r;break;case h.UP:k[1]+=-0.25*r;break;case h.DOWN:k[1]+= -0.25*r}g=(1-d)*(1-d)*(1-d);f=3*(1-d)*(1-d)*d;r=3*(1-d)*d*d;d*=d*d;return[g*a[0]+f*c[0]+r*k[0]+d*b[0],g*a[1]+f*c[1]+r*k[1]+d*b[1]]};e.prototype.drawExecutionOrder=function(a){a.shadowColor="transparent";a.globalAlpha=0.25;a.textAlign="center";a.strokeStyle="white";a.globalAlpha=0.75;for(var b=this.visible_nodes,d=0;dt.last_y&&st.options.max&&(t.value=t.options.max);else if("mousedown"==d.type){var l=t.options.values; -l&&l.constructor===Function&&(l=t.options.values(t,a));var z=null;"number"!=t.type&&(z=l.constructor===Array?l:Object.keys(l));c=40>c?-1:c>k-40?1:0;if("number"==t.type)t.value+=0.1*c*(t.options.step||1),null!=t.options.min&&t.valuet.options.max&&(t.value=t.options.max);else if(c)e=-1,e=l.constructor===Object?z.indexOf(String(t.value))+c:z.indexOf(t.value)+c,e>=z.length&&(e=z.length-1),0>e&&(e=0),t.value=l.constructor===Array?l[e]: -e;else{var n=l!=z?Object.values(l):l;new h.ContextMenu(n,{scale:Math.max(1,this.ds.scale),event:d,className:"dark",callback:y.bind(t)},e);var y=function(a,b,d){l!=z&&(a=n.indexOf(a));this.value=a;f(this,a);p.dirty_canvas=!0;return!1}}}else"mouseup"==d.type&&"number"==t.type&&(c=40>c?-1:c>k-40?1:0,200>d.click_time&&0==c&&this.prompt("Value",t.value,function(a){this.value=Number(a);f(this,this.value)}.bind(t),d));g!=t.value&&setTimeout(function(){f(this,this.value)}.bind(t),20);this.dirty_canvas=!0; -break;case "toggle":"mousedown"==d.type&&(t.value=!t.value,setTimeout(function(){f(t,t.value)},20));break;case "string":case "text":"mousedown"==d.type&&this.prompt("Value",t.value,function(a){this.value=a;f(this,a)}.bind(t),d);break;default:t.mouse&&t.mouse(ctx,d,[c,s],a)}return t}}return null};e.prototype.drawGroups=function(a,b){if(this.graph){var d=this.graph._groups;b.save();b.globalAlpha=0.5*this.editor_alpha;for(var g=0;gd&&0.01>b.editor_alpha&&(clearInterval(g),1>d&&(b.live_mode=!0));1"+q+""+a+"",value:q});if(p.length)return new h.ContextMenu(p, -{event:d,callback:c,parentMenu:g,allow_html:!0,node:f},b),!1}};e.decodeHTML=function(a){var b=document.createElement("div");b.innerText=a;return b.innerHTML};e.onResizeNode=function(a,b,d,g,f){f&&(f.size=f.computeSize(),f.setDirtyCanvas(!0,!0))};e.prototype.showLinkMenu=function(a,b){var d=this;console.log(a);var g=new h.ContextMenu(["Add Node",null,"Delete"],{event:b,title:null!=a.data?a.data.constructor.name:null,callback:function(b,h,c){switch(b){case "Add Node":e.onMenuAdd(null,null,c,g,function(b){console.log("node autoconnect"); -var f=d.graph.getNodeById(a.origin_id),g=d.graph.getNodeById(a.target_id);b.inputs&&b.inputs.length&&b.outputs&&b.outputs.length&&f.outputs[a.origin_slot].type==b.inputs[0].type&&b.outputs[0].type==g.inputs[0].type&&(f.connect(a.origin_slot,b,0),b.connect(0,g,a.target_slot),b.pos[0]-=0.5*b.size[0])});break;case "Delete":d.graph.removeLink(a.id)}}});return!1};e.onShowPropertyEditor=function(a,b,d,g,f){function h(){var b=p.value;"Number"==a.type?b=Number(b):"Boolean"==a.type&&(b=Boolean(b));f[c]=b; -k.parentNode&&k.parentNode.removeChild(k);f.setDirtyCanvas(!0,!0)}var c=a.property||"title";b=f[c];var k=document.createElement("div");k.className="graphdialog";k.innerHTML="";k.querySelector(".name").innerText=c;var p=k.querySelector("input");p&&(p.value=b,p.addEventListener("blur",function(a){this.focus()}),p.addEventListener("keydown",function(a){13==a.keyCode&&(h(),a.preventDefault(),a.stopPropagation())})); -b=e.active_canvas.canvas;d=b.getBoundingClientRect();var q=g=-20;d&&(g-=d.left,q-=d.top);event?(k.style.left=event.clientX+g+"px",k.style.top=event.clientY+q+"px"):(k.style.left=0.5*b.width+g+"px",k.style.top=0.5*b.height+q+"px");k.querySelector("button").addEventListener("click",h);b.parentNode.appendChild(k)};e.prototype.prompt=function(a,b,d,g){var f=this;a=a||"";var h=!1,c=document.createElement("div");c.className="graphdialog rounded";c.innerHTML=" "; -c.close=function(){f.prompt_box=null;c.parentNode&&c.parentNode.removeChild(c)};1e.search_limit)break}}s=null;if(Array.prototype.filter)s=Object.keys(h.registered_node_types).filter(l); -else for(k in s=[],h.registered_node_types)l(k)&&s.push(k);for(k=0;ke.search_limit);k++);var l=function(a){var b=h.registered_node_types[a];return p&&b.filter!=p?!1:-1!==a.toLowerCase().indexOf(d)}}}var f=this,c=e.active_canvas,k=c.canvas,p=k.ownerDocument||document,q=document.createElement("div");q.className="litegraph litesearchbox graphdialog rounded";q.innerHTML="Search
"; -q.close=function(){f.search_box=null;p.body.focus();p.body.style.overflow="";setTimeout(function(){f.canvas.focus()},20);q.parentNode&&q.parentNode.removeChild(q)};var l=null;1k.height-200&&(z.style.maxHeight=k.height-a.layerY-20+"px");u.focus();return q};e.prototype.showEditPropertyValue=function(a,b,d){function g(){f(t.value)}function f(f){"number"==typeof a.properties[b]&&(f=Number(f));if("array"==h||"object"==h)f=JSON.parse(f);a.properties[b]=f;a._graph&&a._graph._version++;if(a.onPropertyChanged)a.onPropertyChanged(b, -f);if(d.onclose)d.onclose();e.close();a.setDirtyCanvas(!0,!0)}if(a&&void 0!==a.properties[b]){d=d||{};var c=a.getPropertyInfo(b),h=c.type,k="";if("string"==h||"number"==h||"array"==h||"object"==h)k="";else if("enum"==h&&c.values){var k=""}else if("boolean"== -h)k="";else{console.warn("unknown type: "+h);return}var e=this.createDialog(""+b+""+k+"",d);if("enum"==h&&c.values){var t=e.querySelector("select");t.addEventListener("change",function(a){f(a.target.value)})}else if("boolean"==h)(t=e.querySelector("input"))&&t.addEventListener("click",function(a){f(!!t.checked)});else if(t=e.querySelector("input"))t.addEventListener("blur", -function(a){this.focus()}),q=void 0!==a.properties[b]?a.properties[b]:"",q=JSON.stringify(q),t.value=q,t.addEventListener("keydown",function(a){13==a.keyCode&&(g(),a.preventDefault(),a.stopPropagation())});e.querySelector("button").addEventListener("click",g);return e}};e.prototype.createDialog=function(a,b){b=b||{};var d=document.createElement("div");d.className="graphdialog";d.innerHTML=a;var g=this.canvas.getBoundingClientRect(),f=-20,c=-20;g&&(f-=g.left,c-=g.top);b.position?(f+=b.position[0], -c+=b.position[1]):b.event?(f+=b.event.clientX,c+=b.event.clientY):(f+=0.5*this.canvas.width,c+=0.5*this.canvas.height);d.style.left=f+"px";d.style.top=c+"px";this.canvas.parentNode.appendChild(d);d.close=function(){this.parentNode&&this.parentNode.removeChild(this)};return d};e.onMenuNodeCollapse=function(a,b,d,g,f){f.collapse()};e.onMenuNodePin=function(a,b,d,g,f){f.pin()};e.onMenuNodeMode=function(a,b,d,g,f){new h.ContextMenu(["Always","On Event","On Trigger","Never"],{event:d,callback:function(a){if(f)switch(a){case "On Event":f.mode= -h.ON_EVENT;break;case "On Trigger":f.mode=h.ON_TRIGGER;break;case "Never":f.mode=h.NEVER;break;default:f.mode=h.ALWAYS}},parentMenu:g,node:f});return!1};e.onMenuNodeColors=function(a,b,d,g,f){if(!f)throw"no node for color";b=[];b.push({value:null,content:"No color"});for(var c in e.node_colors)a=e.node_colors[c],a={value:c,content:""+c+""},b.push(a);new h.ContextMenu(b,{event:d,callback:function(a){f&&((a=a.value?e.node_colors[a.value]:null)?f.constructor===h.LGraphGroup?f.color=a.groupcolor:(f.color=a.color,f.bgcolor=a.bgcolor):(delete f.color,delete f.bgcolor),f.setDirtyCanvas(!0,!0))},parentMenu:g,node:f});return!1};e.onMenuNodeShapes=function(a,b,d,g,f){if(!f)throw"no node passed";new h.ContextMenu(h.VALID_SHAPES,{event:d,callback:function(a){f&&(f.shape=a,f.setDirtyCanvas(!0))},parentMenu:g,node:f}); -return!1};e.onMenuNodeRemove=function(a,b,d,g,f){if(!f)throw"no node passed";!1!==f.removable&&(f.graph.remove(f),f.setDirtyCanvas(!0,!0))};e.onMenuNodeClone=function(a,b,d,g,f){!1!=f.clonable&&(a=f.clone())&&(a.pos=[f.pos[0]+5,f.pos[1]+5],f.graph.add(a),f.setDirtyCanvas(!0,!0))};e.node_colors={red:{color:"#322",bgcolor:"#533",groupcolor:"#A88"},brown:{color:"#332922",bgcolor:"#593930",groupcolor:"#b06634"},green:{color:"#232",bgcolor:"#353",groupcolor:"#8A8"},blue:{color:"#223",bgcolor:"#335",groupcolor:"#88A"}, -pale_blue:{color:"#2a363b",bgcolor:"#3f5159",groupcolor:"#3f789e"},cyan:{color:"#233",bgcolor:"#355",groupcolor:"#8AA"},purple:{color:"#323",bgcolor:"#535",groupcolor:"#a1309b"},yellow:{color:"#432",bgcolor:"#653",groupcolor:"#b58b2a"},black:{color:"#222",bgcolor:"#000",groupcolor:"#444"}};e.prototype.getCanvasMenuOptions=function(){var a=null;this.getMenuOptions?a=this.getMenuOptions():(a=[{content:"Add Node",has_submenu:!0,callback:e.onMenuAdd},{content:"Add Group",callback:e.onGroupAdd}],this._graph_stack&& -0Name",f),k=h.querySelector("input");k&&c&&(k.value=c.label||"");h.querySelector("button").addEventListener("click",function(a){k.value&&(c&&(c.label=k.value),d.setDirty(!0));h.close()})}},extra:a};a&&(c.title=a.type);var k=null;a&&(k=a.getSlotInPosition(b.canvasX,b.canvasY),e.active_node= -a);if(k){f=[];k&&k.output&&k.output.links&&k.output.links.length&&f.push({content:"Disconnect Links",slot:k});var p=k.input||k.output;f.push(p.locked?"Cannot remove":{content:"Remove Slot",slot:k});f.push(p.nameLocked?"Cannot rename":{content:"Rename Slot",slot:k});c.title=(k.input?k.input.type:k.output.type)||"*";k.input&&k.input.type==h.ACTION&&(c.title="Action");k.output&&k.output.type==h.EVENT&&(c.title="Event")}else a?f=this.getNodeMenuOptions(a):(f=this.getCanvasMenuOptions(),(k=this.graph.getGroupOnPos(b.canvasX, -b.canvasY))&&f.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:k,options:this.getGroupMenuOptions(k)}}));f&&new h.ContextMenu(f,c,g)};"undefined"!=typeof window&&window.CanvasRenderingContext2D&&(window.CanvasRenderingContext2D.prototype.roundRect=function(a,b,d,g,f,c){void 0===f&&(f=5);void 0===c&&(c=f);this.moveTo(a+f,b);this.lineTo(a+d-f,b);this.quadraticCurveTo(a+d,b,a+d,b+f);this.lineTo(a+d,b+g-c);this.quadraticCurveTo(a+d,b+g,a+d-c,b+g);this.lineTo(a+c,b+g);this.quadraticCurveTo(a, -b+g,a,b+g-c);this.lineTo(a,b+f);this.quadraticCurveTo(a,b,a+f,b)});h.compareObjects=function(a,b){for(var d in a)if(a[d]!=b[d])return!1;return!0};h.distance=C;h.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+","+Math.round(255*a[2]).toFixed()+","+(4==a.length?a[3].toFixed(2):"1.0")+")"};h.isInsideRectangle=w;h.growBounding=function(a,b,d){ba[2]&&(a[2]=b);da[3]&&(a[3]=d)};h.isInsideBounding=function(a,b){return a[0]< -b[0][0]||a[1]b[1][0]||a[1]>b[1][1]?!1:!0};h.overlapBounding=u;h.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),d=0,g,f,c=0;6>c;c+=2)g="0123456789ABCDEF".indexOf(a.charAt(c)),f="0123456789ABCDEF".indexOf(a.charAt(c+1)),b[d]=16*g+f,d++;return b};h.num2hex=function(a){for(var b="#",d,g,f=0;3>f;f++)d=a[f]/16,g=a[f]%16,b+="0123456789ABCDEF".charAt(d)+"0123456789ABCDEF".charAt(g);return b};D.prototype.addItem=function(a,b,d){function g(a){var b= -this.value;b&&b.has_submenu&&f.call(this,a)}function f(a){var b=this.value,f=!0;c.current_submenu&&c.current_submenu.close(a);if(d.callback){var g=d.callback.call(this,b,d,a,c,d.node);!0===g&&(f=!1)}if(b&&(b.callback&&!d.ignore_item_callbacks&&!0!==b.disabled&&(g=b.callback.call(this,b,d,a,c,d.extra),!0===g&&(f=!1)),b.submenu)){if(!b.submenu.options)throw"ContextMenu submenu needs options";new c.constructor(b.submenu.options,{callback:b.submenu.callback,event:a,parentMenu:c,ignore_item_callbacks:b.submenu.ignore_item_callbacks, -title:b.submenu.title,extra:b.submenu.extra,autoopen:d.autoopen});f=!1}f&&!c.lock&&c.close()}var c=this;d=d||{};var h=document.createElement("div");h.className="litemenu-entry submenu";var k=!1;if(null===b)h.classList.add("separator");else{h.innerHTML=b&&b.title?b.title:a;if(h.value=b)b.disabled&&(k=!0,h.classList.add("disabled")),(b.submenu||b.has_submenu)&&h.classList.add("has_submenu");"function"==typeof b?(h.dataset.value=a,h.onclick_callback=b):h.dataset.value=b;b.className&&(h.className+=" "+ -b.className)}this.root.appendChild(h);k||h.addEventListener("click",f);d.autoopen&&h.addEventListener("mouseenter",g);return h};D.prototype.close=function(a,b){this.root.parentNode&&this.root.parentNode.removeChild(this.root);this.parentMenu&&!b&&(this.parentMenu.lock=!1,this.parentMenu.current_submenu=null,void 0===a?this.parentMenu.close():a&&!D.isCursorOverElement(a,this.parentMenu.root)&&D.trigger(this.parentMenu.root,"mouseleave",a));this.current_submenu&&this.current_submenu.close(a,!0);this.root.closing_timer&& -clearTimeout(this.root.closing_timer)};D.trigger=function(a,b,d,g){var f=document.createEvent("CustomEvent");f.initCustomEvent(b,!0,!0,d);f.srcElement=g;a.dispatchEvent?a.dispatchEvent(f):a.__events&&a.__events.dispatchEvent(f);return f};D.prototype.getTopMenu=function(){return this.options.parentMenu?this.options.parentMenu.getTopMenu():this};D.prototype.getFirstEvent=function(){return this.options.parentMenu?this.options.parentMenu.getFirstEvent():this.options.event};D.isCursorOverElement=function(a, -b){var d=a.clientX,g=a.clientY,f=b.getBoundingClientRect();return f?g>f.top&&gf.left&&dMath.abs(d))return g[1];d=(a-g[0])/d;return g[1]*(1-d)+f[1]*d}}return 0}};A.prototype.draw=function(a,b,d,g,f,c){if(d=this.points){this.size= -b;var h=b[0]-2*this.margin;b=b[1]-2*this.margin;f=f||"#666";a.save();a.translate(this.margin,this.margin);g&&(a.fillStyle="#111",a.fillRect(0,0,h,b),a.fillStyle="#222",a.fillRect(0.5*h,0,1,b),a.strokeStyle="#333",a.strokeRect(0,0,h,b));a.strokeStyle=f;c&&(a.globalAlpha=0.5);a.beginPath();for(g=0;ga[1])){var g=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=a[0]-this.margin,h=a[1]-this.margin;this.selected=this.getCloserPoint([c,h],30/b.ds.scale);-1==this.selected&&(g=[c/g,1-h/f],d.push(g),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(g),this.must_update=!0);if(-1!=this.selected)return!0}};A.prototype.onMouseMove=function(a,b){var d=this.points;if(d){var g= -this.selected;if(!(0>g)){var f=(a[0]-this.margin)/(this.size[0]-2*this.margin),c=(a[1]-this.margin)/(this.size[1]-2*this.margin);this._nearest=this.getCloserPoint([a[0]-this.margin,a[1]-this.margin],30/b.ds.scale);var h=d[g];if(h){var k=0==g||g==d.length-1;!k&&(-10>a[0]||a[0]>this.size[0]+10||-10>a[1]||a[1]>this.size[1]+10)?(d.splice(g,1),this.selected=-1):(h[0]=k?0==g?0:1:Math.clamp(f,0,1),h[1]=1-Math.clamp(c,0,1),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(h),this.must_update= -!0)}}}};A.prototype.onMouseUp=function(a,b){this.selected=-1;return!1};A.prototype.getCloserPoint=function(a,b){var d=this.points;if(!d)return-1;b=b||30;for(var g=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=d.length,h=[0,0],k=1E6,p=-1,q=0;qk||e>b||(p=q,k=e)}return p};h.CurveEditor=A;h.getParameterNames=function(a){return(a+"").replace(/[/][/].*$/gm,"").replace(/\s+/g,"").replace(/[/][*][^/*]*[*][/]/g,"").split("){", -1)[0].replace(/^[^(]*[(]/,"").replace(/=[^,]+/g,"").split(",").filter(Boolean)};Math.clamp=function(a,b,d){return b>a?b:dthis.size[0]-g.NODE_TITLE_HEIGHT&&0>b[1]){var c=this;setTimeout(function(){d.openSubgraph(c.subgraph)},10)}};m.prototype.onAction=function(a,b){this.subgraph.onAction(a,b)};m.prototype.onExecute=function(){if(this.enabled= -this.getInputOrProperty("enabled")){if(this.inputs)for(var a=0;a=h?this.trigger(null,e):this._pending.push([h,e])};e.prototype.onExecute=function(){var c=1E3* -this.graph.elapsed_time;this.isInputConnected(1)&&(this.properties.time_in_ms=this.getInputData(1));for(var e=0;ee[1]))return this.old_y=c.canvasY,this.captureInput(!0),this.mouse_captured=!0};n.prototype.onMouseMove=function(c){if(this.mouse_captured){var e=this.old_y-c.canvasY;c.shiftKey&&(e*=10);if(c.metaKey||c.altKey)e*=0.1;this.old_y=c.canvasY;c=this._remainder+e/n.pixels_threshold;this._remainder=c%1;c=Math.clamp(this.properties.value+ -(c|0)*this.properties.step,this.properties.min,this.properties.max);this.properties.value=c;this.graph._version++;this.setDirtyCanvas(!0)}};n.prototype.onMouseUp=function(c,e){200>c.click_time&&(this.properties.value=Math.clamp(this.properties.value+(e[1]>0.5*this.size[1]?-1:1)*this.properties.step,this.properties.min,this.properties.max),this.graph._version++,this.setDirtyCanvas(!0));this.mouse_captured&&(this.mouse_captured=!1,this.captureInput(!1))};A.registerNodeType("widget/number",n);l.title= -"Combo";l.desc="Widget to select from a list";l.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};l.prototype.onPropertyChanged=function(c,e){"values"==c?(this._values=e.split(";"),this.widget.options.values=this._values):"value"==c&&(this.widget.value=e)};A.registerNodeType("widget/combo",l);x.title="Knob";x.desc="Circular controller";x.size=[80,100];x.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/ -(this.properties.max-this.properties.min));var e=0.5*this.size[0],l=0.5*this.size[1],n=0.5*Math.min(this.size[0],this.size[1])-5;c.globalAlpha=1;c.save();c.translate(e,l);c.rotate(0.75*Math.PI);c.fillStyle="rgba(0,0,0,0.5)";c.beginPath();c.moveTo(0,0);c.arc(0,0,n,0,1.5*Math.PI);c.fill();c.strokeStyle="black";c.fillStyle=this.properties.color;c.lineWidth=2;c.beginPath();c.moveTo(0,0);c.arc(0,0,n-4,0,1.5*Math.PI*Math.max(0.01,this.value));c.closePath();c.fill();c.lineWidth=1;c.globalAlpha=1;c.restore(); -c.fillStyle="black";c.beginPath();c.arc(e,l,0.75*n,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":this.properties.color;c.beginPath();var u=this.value*Math.PI*1.5+0.75*Math.PI;c.arc(e+Math.cos(u)*n*0.65,l+Math.sin(u)*n*0.65,0.05*n,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":"#AAA";c.font=Math.floor(0.5*n)+"px Arial";c.textAlign="center";c.fillText(this.properties.value.toFixed(this.properties.precision),e,l+0.15*n)}};x.prototype.onExecute=function(){this.setOutputData(0, -this.properties.value);this.boxcolor=A.colorToString([this.value,this.value,this.value])};x.prototype.onMouseDown=function(c){this.center=[0.5*this.size[0],0.5*this.size[1]+20];this.radius=0.5*this.size[0];if(20>c.canvasY-this.pos[1]||A.distance([c.canvasX,c.canvasY],[this.pos[0]+this.center[0],this.pos[1]+this.center[1]])>this.radius)return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];this.captureInput(!0);return!0};x.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX- -this.pos[0],c.canvasY-this.pos[1]];var e=this.value,e=e-0.01*(c[1]-this.oldmouse[1]);1e&&(e=0);this.value=e;this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.oldmouse=c;this.setDirtyCanvas(!0)}};x.prototype.onMouseUp=function(c){this.oldmouse&&(this.oldmouse=null,this.captureInput(!1))};x.prototype.onPropertyChanged=function(c,e){if("min"==c||"max"==c||"value"==c)return this.properties[c]=parseFloat(e),!0};A.registerNodeType("widget/knob", -x);e.title="Inner Slider";e.prototype.onPropertyChanged=function(c,e){"value"==c&&(this.slider.value=e)};e.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};A.registerNodeType("widget/internal_slider",e);C.title="H.Slider";C.desc="Linear slider controller";C.prototype.onDrawForeground=function(c){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min));c.globalAlpha=1;c.lineWidth=1;c.fillStyle="#000";c.fillRect(2, -2,this.size[0]-4,this.size[1]-4);c.fillStyle=this.properties.color;c.beginPath();c.rect(4,4,(this.size[0]-8)*this.value,this.size[1]-8);c.fill()};C.prototype.onExecute=function(){this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.setOutputData(0,this.properties.value);this.boxcolor=A.colorToString([this.value,this.value,this.value])};C.prototype.onMouseDown=function(c){if(0>c.canvasY-this.pos[1])return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY- -this.pos[1]];this.captureInput(!0);return!0};C.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];var e=this.value,e=e+(c[0]-this.oldmouse[0])/this.size[0];1e&&(e=0);this.value=e;this.oldmouse=c;this.setDirtyCanvas(!0)}};C.prototype.onMouseUp=function(c){this.oldmouse=null;this.captureInput(!1)};C.prototype.onMouseLeave=function(c){};A.registerNodeType("widget/hslider",C);w.title="Progress";w.desc="Shows data in linear progress";w.prototype.onExecute= -function(){var c=this.getInputData(0);void 0!=c&&(this.properties.value=c)};w.prototype.onDrawForeground=function(c){c.lineWidth=1;c.fillStyle=this.properties.color;var e=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min),e=Math.min(1,e),e=Math.max(0,e);c.fillRect(2,2,(this.size[0]-4)*e,this.size[1]-4)};A.registerNodeType("widget/progress",w);u.title="Text";u.desc="Shows the input value";u.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"led_text", -text:"LED",type:"minibutton"},{name:"normal_text",text:"Normal",type:"minibutton"}];u.prototype.onDrawForeground=function(c){c.fillStyle=this.properties.color;var e=this.properties.value;this.properties.glowSize?(c.shadowColor=this.properties.color,c.shadowOffsetX=0,c.shadowOffsetY=0,c.shadowBlur=this.properties.glowSize):c.shadowColor="transparent";var l=this.properties.fontsize;c.textAlign=this.properties.align;c.font=l.toString()+"px "+this.properties.font;this.str="number"==typeof e?e.toFixed(this.properties.decimals): -e;if("string"==typeof this.str){var e=this.str.split("\\n"),n;for(n in e)c.fillText(e[n],"left"==this.properties.align?15:this.size[0]-15,-0.15*l+l*(parseInt(n)+1))}c.shadowColor="transparent";this.last_ctx=c;c.textAlign="left"};u.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(this.properties.value=c)};u.prototype.resize=function(){if(this.last_ctx){var c=this.str.split("\\n");this.last_ctx.font=this.properties.fontsize+"px "+this.properties.font;var e=0,l;for(l in c){var n=this.last_ctx.measureText(c[l]).width; -el?n.xbox.axes.lx:0,this._left_axis[1]=Math.abs(n.xbox.axes.ly)>l?n.xbox.axes.ly:0,this._right_axis[0]=Math.abs(n.xbox.axes.rx)>l?n.xbox.axes.rx:0,this._right_axis[1]=Math.abs(n.xbox.axes.ry)>l?n.xbox.axes.ry:0,this._triggers[0]=Math.abs(n.xbox.axes.ltrigger)>l?n.xbox.axes.ltrigger: -0,this._triggers[1]=Math.abs(n.xbox.axes.rtrigger)>l?n.xbox.axes.rtrigger:0);if(this.outputs)for(l=0;ln;n++)if(l[n]){n=l[n];l=this.xbox_mapping;l||(l=this.xbox_mapping={axes:[], -buttons:{},hat:"",hatmap:c.CENTER});l.axes.lx=n.axes[0];l.axes.ly=n.axes[1];l.axes.rx=n.axes[2];l.axes.ry=n.axes[3];l.axes.ltrigger=n.buttons[6].value;l.axes.rtrigger=n.buttons[7].value;l.hat="";l.hatmap=c.CENTER;for(var m=0;mm)l.buttons[c.mapping_array[m]]=n.buttons[m].pressed,n.buttons[m].was_pressed&&this.trigger(c.mapping_array[m]+"_button_event");else switch(m){case 12:n.buttons[m].pressed&&(l.hat+="up",l.hatmap|=c.UP); -break;case 13:n.buttons[m].pressed&&(l.hat+="down",l.hatmap|=c.DOWN);break;case 14:n.buttons[m].pressed&&(l.hat+="left",l.hatmap|=c.LEFT);break;case 15:n.buttons[m].pressed&&(l.hat+="right",l.hatmap|=c.RIGHT);break;case 16:l.buttons.home=n.buttons[m].pressed}n.xbox=l;return n}};c.prototype.onDrawBackground=function(c){if(!this.flags.collapsed){var l=this._left_axis,m=this._right_axis;c.strokeStyle="#88A";c.strokeRect(0.5*(l[0]+1)*this.size[0]-4,0.5*(l[1]+1)*this.size[1]-4,8,8);c.strokeStyle="#8A8"; -c.strokeRect(0.5*(m[0]+1)*this.size[0]-4,0.5*(m[1]+1)*this.size[1]-4,8,8);l=this.size[1]/this._current_buttons.length;c.fillStyle="#AEB";for(m=0;m","enum",{values:a.values});this.size=[80,60]}function b(){this.addInput("inc","number");this.addOutput("total","number");this.addProperty("increment",1);this.addProperty("value",0)}function d(){this.addInput("v","number");this.addOutput("sin","number");this.addProperty("amplitude", -1);this.addProperty("offset",0);this.bgImageUrl="nodes/imgs/icon-sin.png"}function g(){this.addInput("x","number");this.addInput("y","number");this.addOutput("","number");this.properties={x:1,y:1,formula:"x+y"};this.code_widget=this.addWidget("text","F(x,y)",this.properties.formula,function(a,b,d){d.properties.formula=a});this.addWidget("toggle","allow",E.allow_scripts,function(a){E.allow_scripts=a});this._func=null}function f(){this.addInput("vec2","vec2");this.addOutput("x","number");this.addOutput("y", -"number")}function r(){this.addInputs([["x","number"],["y","number"]]);this.addOutput("vec2","vec2");this.properties={x:0,y:0};this._data=new Float32Array(2)}function s(){this.addInput("vec3","vec3");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number")}function I(){this.addInputs([["x","number"],["y","number"],["z","number"]]);this.addOutput("vec3","vec3");this.properties={x:0,y:0,z:0};this._data=new Float32Array(3)}function J(){this.addInput("vec4","vec4");this.addOutput("x", -"number");this.addOutput("y","number");this.addOutput("z","number");this.addOutput("w","number")}function H(){this.addInputs([["x","number"],["y","number"],["z","number"],["w","number"]]);this.addOutput("vec4","vec4");this.properties={x:0,y:0,z:0,w:0};this._data=new Float32Array(4)}var E=B.LiteGraph;c.title="Converter";c.desc="type A to type B";c.prototype.onExecute=function(){var a=this.getInputData(0);if(null!=a&&this.outputs)for(var b=0;ba&&(a+=1024);var c=Math.floor(a);a-=c;d=e.data[c];c=e.data[1023==c?0:c+1]; -b&&(a=a*a*a*(a*(6*a-15)+10));return d*(1-a)+c*a};e.prototype.onExecute=function(){var a=this.getInputData(0)||0,a=e.getValue(a,this.properties.smooth),b=this.properties.min;this._last_v=a*(this.properties.max-b)+b;this.setOutputData(0,this._last_v)};e.prototype.onDrawBackground=function(a){this.outputs[0].label=(this._last_v||0).toFixed(3)};E.registerNodeType("math/noise",e);C.title="Spikes";C.desc="spike every random time";C.prototype.onExecute=function(){var a=this.graph.elapsed_time;this._remaining_time-= -a;this._blink_time-=a;a=0;0this._remaining_time?(this._remaining_time=Math.random()*(this.properties.max_time-this.properties.min_time)+this.properties.min_time,this._blink_time=this.properties.duration,this.boxcolor="#FFF"):this.boxcolor="#000";this.setOutputData(0,a)};E.registerNodeType("math/spikes",C);w.title="Clamp";w.desc="Clamp number between min and max";w.prototype.onExecute=function(){var a=this.getInputData(0); -null!=a&&(a=Math.max(this.properties.min,a),a=Math.min(this.properties.max,a),this.setOutputData(0,a))};w.prototype.getCode=function(a){a="";this.isInputConnected(0)&&(a+="clamp({{0}},"+this.properties.min+","+this.properties.max+")");return a};E.registerNodeType("math/clamp",w);u.title="Lerp";u.desc="Linear Interpolation";u.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.getInputData(1);null==b&&(b=0);var d=this.properties.f,c=this.getInputData(2);void 0!==c&& -(d=c);this.setOutputData(0,a*(1-d)+b*d)};u.prototype.onGetInputs=function(){return[["f","number"]]};E.registerNodeType("math/lerp",u);D.title="Abs";D.desc="Absolute";D.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.abs(a))};E.registerNodeType("math/abs",D);A.title="Floor";A.desc="Floor number to remove fractional part";A.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.floor(a))};E.registerNodeType("math/floor", -A);h.title="Frac";h.desc="Returns fractional part";h.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a%1)};E.registerNodeType("math/frac",h);z.title="Smoothstep";z.desc="Smoothstep";z.prototype.onExecute=function(){var a=this.getInputData(0);if(void 0!==a){var b=this.properties.A,a=Math.clamp((a-b)/(this.properties.B-b),0,1);this.setOutputData(0,a*a*(3-2*a))}};E.registerNodeType("math/smoothstep",z);y.title="Scale";y.desc="v * factor";y.prototype.onExecute= -function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a*this.properties.factor)};E.registerNodeType("math/scale",y);v.title="Gate";v.desc="if v is true, then outputs A, otherwise B";v.prototype.onExecute=function(){var a=this.getInputData(0);this.setOutputData(0,this.getInputData(a?1:2))};E.registerNodeType("math/gate",v);G.title="Average";G.desc="Average Filter";G.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this._values.length;this._values[this._current% -b]=a;this._current+=1;this._current>b&&(this._current=0);for(var d=a=0;db&&(b=1);this.properties.samples=Math.round(b);var d=this._values;this._values=new Float32Array(this.properties.samples);d.length<=this._values.length?this._values.set(d):this._values.set(d.subarray(0,this._values.length))};E.registerNodeType("math/average",G);p.title="TendTo";p.desc="moves the output value always closer to the input"; -p.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.factor;this._value=null==this._value?a:this._value*(1-b)+a*b;this.setOutputData(0,this._value)};E.registerNodeType("math/tendTo",p);q.values="+ - * / % ^ max min".split(" ");q.title="Operation";q.desc="Easy math operators";q["@OP"]={type:"enum",title:"operation",values:q.values};q.size=[100,60];q.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+ -"(A,B)":"A "+this.properties.OP+" B"};q.prototype.setValue=function(a){"string"==typeof a&&(a=parseFloat(a));this.properties.value=a};q.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);null!=a?this.properties.A=a:a=this.properties.A;null!=b?this.properties.B=b:b=this.properties.B;var d=0;switch(this.properties.OP){case "+":d=a+b;break;case "-":d=a-b;break;case "x":case "X":case "*":d=a*b;break;case "/":d=a/b;break;case "%":d=a%b;break;case "^":d=Math.pow(a,b);break; -case "max":d=Math.max(a,b);break;case "min":d=Math.min(a,b);break;default:console.warn("Unknown operation: "+this.properties.OP)}this.setOutputData(0,d)};q.prototype.onDrawBackground=function(a){this.flags.collapsed||(a.font="40px Arial",a.fillStyle="#666",a.textAlign="center",a.fillText(this.properties.OP,0.5*this.size[0],0.5*(this.size[1]+E.NODE_TITLE_HEIGHT)),a.textAlign="left")};E.registerNodeType("math/operation",q);E.registerSearchboxExtra("math/operation","MAX",{properties:{OP:"max"},title:"MAX()"}); -E.registerSearchboxExtra("math/operation","MIN",{properties:{OP:"min"},title:"MIN()"});k.title="Compare";k.desc="compares between two values";k.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);void 0!==a?this.properties.A=a:a=this.properties.A;void 0!==b?this.properties.B=b:b=this.properties.B;for(var d=0,c=this.outputs.length;dB":f= -a>b;break;case "A=B":f=a>=b}this.setOutputData(d,f)}}};k.prototype.onGetOutputs=function(){return[["A==B","boolean"],["A!=B","boolean"],["A>B","boolean"],["A=B","boolean"],["A<=B","boolean"]]};E.registerNodeType("math/compare",k);E.registerSearchboxExtra("math/compare","==",{outputs:[["A==B","boolean"]],title:"A==B"});E.registerSearchboxExtra("math/compare","!=",{outputs:[["A!=B","boolean"]],title:"A!=B"});E.registerSearchboxExtra("math/compare", -">",{outputs:[["A>B","boolean"]],title:"A>B"});E.registerSearchboxExtra("math/compare","<",{outputs:[["A=",{outputs:[["A>=B","boolean"]],title:"A>=B"});E.registerSearchboxExtra("math/compare","<=",{outputs:[["A<=B","boolean"]],title:"A<=B"});a.values="> < == != <= >= || &&".split(" ");a["@OP"]={type:"enum",title:"operation",values:a.values};a.title="Condition";a.desc="evaluates condition between A and B";a.prototype.getTitle= -function(){return"A "+this.properties.OP+" B"};a.prototype.onExecute=function(){var a=this.getInputData(0);void 0===a?a=this.properties.A:this.properties.A=a;var b=this.getInputData(1);void 0===b?b=this.properties.B:this.properties.B=b;var d=!0;switch(this.properties.OP){case ">":d=a>b;break;case "<":d=a=":d=a>=b;break;case "||":d=a||b;break;case "&&":d=a&&b}this.setOutputData(0,d);this.setOutputData(1,!d)};E.registerNodeType("math/condition", -a);b.title="Accumulate";b.desc="Increments a value every time";b.prototype.onExecute=function(){null===this.properties.value&&(this.properties.value=0);var a=this.getInputData(0);this.properties.value=null!==a?this.properties.value+a:this.properties.value+this.properties.increment;this.setOutputData(0,this.properties.value)};E.registerNodeType("math/accumulate",b);d.title="Trigonometry";d.desc="Sin Cos Tan";d.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.amplitude, -d=this.findInputSlot("amplitude");-1!=d&&(b=this.getInputData(d));var c=this.properties.offset,d=this.findInputSlot("offset");-1!=d&&(c=this.getInputData(d));for(var d=0,g=this.outputs.length;dXY";f.desc="vector 2 to components";f.prototype.onExecute=function(){var a=this.getInputData(0); -null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]))};E.registerNodeType("math3d/vec2-to-xy",f);r.title="XY->Vec2";r.desc="components to vector2";r.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this._data;d[0]=a;d[1]=b;this.setOutputData(0,d)};E.registerNodeType("math3d/xy-to-vec2",r);s.title="Vec3->XYZ";s.desc="vector 3 to components";s.prototype.onExecute=function(){var a=this.getInputData(0); -null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]))};E.registerNodeType("math3d/vec3-to-xyz",s);I.title="XYZ->Vec3";I.desc="components to vector3";I.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this._data;c[0]=a;c[1]=b;c[2]=d;this.setOutputData(0,c)};E.registerNodeType("math3d/xyz-to-vec3",I);J.title= -"Vec4->XYZW";J.desc="vector 4 to components";J.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]),this.setOutputData(3,a[3]))};E.registerNodeType("math3d/vec4-to-xyzw",J);H.title="XYZW->Vec4";H.desc="components to vector4";H.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null== -d&&(d=this.properties.z);var c=this.getInputData(3);null==c&&(c=this.properties.w);var g=this._data;g[0]=a;g[1]=b;g[2]=d;g[3]=c;this.setOutputData(0,g)};E.registerNodeType("math3d/xyzw-to-vec4",H)})(this); -(function(B){function c(){this.addInput("T","vec3");this.addInput("R","vec3");this.addInput("S","vec3");this.addOutput("mat4","mat4");this.properties={T:[0,0,0],R:[0,0,0],S:[1,1,1],R_in_degrees:!0};this._result=mat4.create();this._must_update=!0}function m(){this.addInput("A","number,vec3");this.addInput("B","number,vec3");this.addOutput("=","vec3");this.addProperty("OP","+","enum",{values:m.values});this._result=vec3.create()}function n(){this.addInput("in","vec3");this.addInput("f","number");this.addOutput("out", -"vec3");this.properties={f:1};this._data=new Float32Array(3)}function l(){this.addInput("in","vec3");this.addOutput("out","number")}function x(){this.addInput("in","vec3");this.addOutput("out","vec3");this._data=new Float32Array(3)}function e(){this.addInput("A","vec3");this.addInput("B","vec3");this.addInput("f","vec3");this.addOutput("out","vec3");this.properties={f:0.5};this._data=new Float32Array(3)}function C(){this.addInput("A","vec3");this.addInput("B","vec3");this.addOutput("out","number")} -var w=B.LiteGraph;c.title="mat4";c.temp_quat=new Float32Array([0,0,0,1]);c.temp_mat4=new Float32Array(16);c.temp_vec3=new Float32Array(3);c.prototype.onPropertyChanged=function(c,e){this._must_update=!0};c.prototype.onExecute=function(){var e=this._result,l=c.temp_quat,n=c.temp_mat4,h=c.temp_vec3,m=this.getInputData(0),y=this.getInputData(1),v=this.getInputData(2);if(this._must_update||m||y||v)m=m||this.properties.T,y=y||this.properties.R,v=v||this.properties.S,mat4.identity(e),mat4.translate(e,e, -m),this.properties.R_in_degrees?(h.set(y),vec3.scale(h,h,DEG2RAD),quat.fromEuler(l,h)):quat.fromEuler(l,y),mat4.fromQuat(n,l),mat4.multiply(e,e,n),mat4.scale(e,e,v);this.setOutputData(0,e)};w.registerNodeType("math3d/mat4",c);m.values="+ - * / % ^ max min".split(" ");m.title="Operation";m.desc="Easy math 3D operators";m["@OP"]={type:"enum",title:"operation",values:m.values};m.size=[100,60];m.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+ -"(A,B)":"A "+this.properties.OP+" B"};m.prototype.onExecute=function(){var c=this.getInputData(0),e=this.getInputData(1);if(null!=c&&null!=e){c.constructor===Number&&(c=[c,c,c]);e.constructor===Number&&(e=[e,e,e]);var l=this._result;switch(this.properties.OP){case "+":l=vec3.add(l,c,e);break;case "-":l=vec3.sub(l,c,e);break;case "x":case "X":case "*":l=vec3.mul(l,c,e);break;case "/":l=vec3.div(l,c,e);break;case "%":l[0]=c[0]%e[0];l[1]=c[1]%e[1];l[2]=c[2]%e[2];break;case "^":l[0]=Math.pow(c[0],e[0]); -l[1]=Math.pow(c[1],e[1]);l[2]=Math.pow(c[2],e[2]);break;case "max":l[0]=Math.max(c[0],e[0]);l[1]=Math.max(c[1],e[1]);l[2]=Math.max(c[2],e[2]);break;case "min":l[0]=Math.min(c[0],e[0]);l[1]=Math.min(c[1],e[1]);l[2]=Math.min(c[2],e[2]);break;default:console.warn("Unknown operation: "+this.properties.OP)}this.setOutputData(0,l)}};m.prototype.onDrawBackground=function(c){this.flags.collapsed||(c.font="40px Arial",c.fillStyle="#666",c.textAlign="center",c.fillText(this.properties.OP,0.5*this.size[0],0.5* -(this.size[1]+w.NODE_TITLE_HEIGHT)),c.textAlign="left")};w.registerNodeType("math3d/operation",m);n.title="vec3_scale";n.desc="scales the components of a vec3";n.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null==e&&(e=this.properties.f);var l=this._data;l[0]=c[0]*e;l[1]=c[1]*e;l[2]=c[2]*e;this.setOutputData(0,l)}};w.registerNodeType("math3d/vec3-scale",n);l.title="vec3_length";l.desc="returns the module of a vector";l.prototype.onExecute=function(){var c= -this.getInputData(0);null!=c&&(c=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),this.setOutputData(0,c))};w.registerNodeType("math3d/vec3-length",l);x.title="vec3_normalize";x.desc="returns the vector normalized";x.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),l=this._data;l[0]=c[0]/e;l[1]=c[1]/e;l[2]=c[2]/e;this.setOutputData(0,l)}};w.registerNodeType("math3d/vec3-normalize",x);e.title="vec3_lerp";e.desc="returns the interpolated vector"; -e.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);if(null!=e){var l=this.getInputOrProperty("f"),h=this._data;h[0]=c[0]*(1-l)+e[0]*l;h[1]=c[1]*(1-l)+e[1]*l;h[2]=c[2]*(1-l)+e[2]*l;this.setOutputData(0,h)}}};w.registerNodeType("math3d/vec3-lerp",e);C.title="vec3_dot";C.desc="returns the dot product";C.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null!=e&&this.setOutputData(0,c[0]*e[0]+c[1]*e[1]+c[2]* -e[2])}};w.registerNodeType("math3d/vec3-dot",C);B.glMatrix?(B=function(){this.addOutput("quat","quat");this.properties={x:0,y:0,z:0,w:1,normalize:!1};this._value=quat.create()},B.title="Quaternion",B.desc="quaternion",B.prototype.onExecute=function(){this._value[0]=this.getInputOrProperty("x");this._value[1]=this.getInputOrProperty("y");this._value[2]=this.getInputOrProperty("z");this._value[3]=this.getInputOrProperty("w");this.properties.normalize&&quat.normalize(this._value,this._value);this.setOutputData(0, -this._value)},B.prototype.onGetInputs=function(){return[["x","number"],["y","number"],["z","number"],["w","number"]]},w.registerNodeType("math3d/quaternion",B),B=function(){this.addInputs([["degrees","number"],["axis","vec3"]]);this.addOutput("quat","quat");this.properties={angle:90,axis:vec3.fromValues(0,1,0)};this._value=quat.create()},B.title="Rotation",B.desc="quaternion rotation",B.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.angle);var e=this.getInputData(1); -null==e&&(e=this.properties.axis);c=quat.setAxisAngle(this._value,e,0.0174532925*c);this.setOutputData(0,c)},w.registerNodeType("math3d/rotation",B),B=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},B.title="Rot. Vec3",B.desc="rotate a point",B.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.vec);var e=this.getInputData(1);null==e?this.setOutputData(c):this.setOutputData(0,vec3.transformQuat(vec3.create(), -c,e))},w.registerNodeType("math3d/rotate_vec3",B),B=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},B.title="Mult. Quat",B.desc="rotate quaternion",B.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null!=e&&(c=quat.multiply(this._value,c,e),this.setOutputData(0,c))}},w.registerNodeType("math3d/mult-quat",B),B=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp", -"quat");this.addProperty("factor",0.5);this._value=quat.create()},B.title="Quat Slerp",B.desc="quaternion spherical interpolation",B.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);if(null!=e){var l=this.properties.factor;null!=this.getInputData(2)&&(l=this.getInputData(2));c=quat.slerp(this._value,c,e,l);this.setOutputData(0,c)}}},w.registerNodeType("math3d/quat-slerp",B),B=function(){this.addInput("vec3","vec3");this.addOutput("remap","vec3");this.addOutput("clamped", -"vec3");this.properties={clamp:!0,range_min:[-1,-1,0],range_max:[1,1,0],target_min:[-1,-1,0],target_max:[1,1,0]};this._value=vec3.create();this._clamped=vec3.create()},B.title="Remap Range",B.desc="remap a 3D range",B.prototype.onExecute=function(){var c=this.getInputData(0);c&&this._value.set(c);for(var c=this.properties.range_min,e=this.properties.range_max,l=this.properties.target_min,h=this.properties.target_max,n=0;3>n;++n){var m=e[n]-c[n];this._clamped[n]=Math.clamp(this._value[n],c[n],e[n]); -0==m?this._value[n]=0.5*(l[n]+h[n]):(m=(this._value[n]-c[n])/m,this.properties.clamp&&(m=Math.clamp(m,0,1)),this._value[n]=l[n]+m*(h[n]-l[n]))}this.setOutputData(0,this._value);this.setOutputData(1,this._clamped)},w.registerNodeType("math3d/remap_range",B)):console.warn("No glmatrix found, some Math3D nodes may not work")})(this); -(function(B){function c(c,l){return c==l}function m(){this.addInput("","string");this.addOutput("table","table");this.addOutput("rows","number");this.addProperty("value","");this.addProperty("separator",",");this._table=null}B=B.LiteGraph;B.wrapFunctionAsNode("string/toString",c,["*"],"String");B.wrapFunctionAsNode("string/compare",c,["string","string"],"boolean");B.wrapFunctionAsNode("string/concatenate",function(c,l){return void 0===c?l:void 0===l?c:c+l},["string","string"],"string");B.wrapFunctionAsNode("string/contains", -function(c,l){return void 0===c||void 0===l?!1:-1!=c.indexOf(l)},["string","string"],"boolean");B.wrapFunctionAsNode("string/toUpperCase",function(c){return null!=c&&c.constructor===String?c.toUpperCase():c},["string"],"string");B.wrapFunctionAsNode("string/split",function(c,l){null==l&&(l=this.properties.separator);if(null==c)return[];if(c.constructor===String)return c.split(l||" ");if(c.constructor===Array){for(var m=[],e=0;ee;++e){var h=this.getInputData(e);if(null!=h){var l=this.values[e];l.push(h);l.length>c[0]&&l.shift()}}}};c.prototype.onDrawBackground=function(e){if(!this.flags.collapsed){var h=this.size,l=0.5*h[1]/ -this.properties.scale,n=c.colors,p=0.5*h[1];e.fillStyle="#000";e.fillRect(0,0,h[0],h[1]);e.strokeStyle="#555";e.beginPath();e.moveTo(0,p);e.lineTo(h[0],p);e.stroke();if(this.inputs)for(var q=0;4>q;++q){var k=this.values[q];if(this.inputs[q]&&this.inputs[q].link){e.strokeStyle=n[q];e.beginPath();var a=k[0]*l*-1+p;e.moveTo(0,Math.clamp(a,0,h[1]));for(var b=1;be&&(e=0);if(0!=c.length){var h=[0,0,0];if(0==e)h=c[0];else if(1==e)h=c[c.length-1];else{var l= -(c.length-1)*e,e=c[Math.floor(l)],c=c[Math.floor(l)+1],l=l-Math.floor(l);h[0]=e[0]*(1-l)+c[0]*l;h[1]=e[1]*(1-l)+c[1]*l;h[2]=e[2]*(1-l)+c[2]*l}for(var p in h)h[p]/=255;this.boxcolor=colorToString(h);this.setOutputData(0,h)}};h.registerNodeType("color/palette",n);l.title="Frame";l.desc="Frame viewerew";l.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}];l.prototype.onDrawBackground=function(c){this.frame&&!this.flags.collapsed&&c.drawImage(this.frame, -0,0,this.size[0],this.size[1])};l.prototype.onExecute=function(){this.frame=this.getInputData(0);this.setDirtyCanvas(!0)};l.prototype.onWidget=function(c,e){if("resize"==e.name&&this.frame){var h=this.frame.width,l=this.frame.height;h||null==this.frame.videoWidth||(h=this.frame.videoWidth,l=this.frame.videoHeight);h&&l&&(this.size=[h,l]);this.setDirtyCanvas(!0,!0)}else"view"==e.name&&this.show()};l.prototype.show=function(){showElement&&this.frame&&showElement(this.frame)};h.registerNodeType("graphics/frame", -l);x.title="Image fade";x.desc="Fades between images";x.widgets=[{name:"resizeA",text:"Resize to A",type:"button"},{name:"resizeB",text:"Resize to B",type:"button"}];x.prototype.onAdded=function(){this.createCanvas();var c=this.canvas.getContext("2d");c.fillStyle="#000";c.fillRect(0,0,this.properties.width,this.properties.height)};x.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};x.prototype.onExecute= -function(){var c=this.canvas.getContext("2d");this.canvas.width=this.canvas.width;var e=this.getInputData(0);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);e=this.getInputData(2);null==e?e=this.properties.fade:this.properties.fade=e;c.globalAlpha=e;e=this.getInputData(1);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);c.globalAlpha=1;this.setOutputData(0,this.canvas);this.setDirtyCanvas(!0)};h.registerNodeType("graphics/imagefade",x);e.title="Crop";e.desc="Crop Image"; -e.prototype.onAdded=function(){this.createCanvas()};e.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};e.prototype.onExecute=function(){var c=this.getInputData(0);c&&(c.width?(this.canvas.getContext("2d").drawImage(c,-this.properties.x,-this.properties.y,c.width*this.properties.scale,c.height*this.properties.scale),this.setOutputData(0,this.canvas)):this.setOutputData(0,null))};e.prototype.onDrawBackground= -function(c){this.flags.collapsed||this.canvas&&c.drawImage(this.canvas,0,0,this.canvas.width,this.canvas.height,0,0,this.size[0],this.size[1])};e.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"scale"==c?(this.properties[c]=parseFloat(e),0==this.properties[c]&&(console.error("Error in scale"),this.properties[c]=1)):this.properties[c]=parseInt(e);this.createCanvas();return!0};h.registerNodeType("graphics/cropImage",e);C.title="Canvas";C.desc="Canvas to render stuff";C.prototype.onExecute= -function(){var c=this.canvas,e=this.properties.width|0,h=this.properties.height|0;c.width!=e&&(c.width=e);c.height!=h&&(c.height=h);this.properties.autoclear&&this.ctx.clearRect(0,0,c.width,c.height);this.setOutputData(0,c)};C.prototype.onAction=function(c,e){"clear"==c&&this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)};h.registerNodeType("graphics/canvas",C);w.title="DrawImage";w.desc="Draws image into a canvas";w.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e= -this.getInputOrProperty("img");if(e){var h=this.getInputOrProperty("x"),l=this.getInputOrProperty("y");c.getContext("2d").drawImage(e,h,l)}}};h.registerNodeType("graphics/drawImage",w);u.title="DrawRectangle";u.desc="Draws rectangle in canvas";u.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e=this.getInputOrProperty("x"),h=this.getInputOrProperty("y"),l=this.getInputOrProperty("w"),p=this.getInputOrProperty("h");c.getContext("2d").fillRect(e,h,l,p)}};h.registerNodeType("graphics/drawRectangle", -u);D.title="Video";D.desc="Video playback";D.widgets=[{name:"play",text:"PLAY",type:"minibutton"},{name:"stop",text:"STOP",type:"minibutton"},{name:"demo",text:"Demo video",type:"button"},{name:"mute",text:"Mute video",type:"button"}];D.prototype.onExecute=function(){if(this.properties.url&&(this.properties.url!=this._video_url&&this.loadVideo(this.properties.url),this._video&&0!=this._video.width)){var c=this.getInputData(0);c&&0<=c&&1>=c&&(this._video.currentTime=c*this._video.duration,this._video.pause()); -this._video.dirty=!0;this.setOutputData(0,this._video);this.setOutputData(1,this._video.currentTime);this.setOutputData(2,this._video.duration);this.setDirtyCanvas(!0)}};D.prototype.onStart=function(){this.play()};D.prototype.onStop=function(){this.stop()};D.prototype.loadVideo=function(c){this._video_url=c;var e=c.substr(0,10).indexOf(":"),l="";-1!=e&&(l=c.substr(0,e));e="";l&&(e=c.substr(0,c.indexOf("/",l.length+3)),e=e.substr(l.length+3));this.properties.use_proxy&&l&&h.proxy&&e!=location.host&& -(c=h.proxy+c.substr(c.indexOf(":")+3));this._video=document.createElement("video");this._video.src=c;this._video.type="type=video/mp4";this._video.muted=!0;this._video.autoplay=!0;var n=this;this._video.addEventListener("loadedmetadata",function(c){console.log("Duration: "+this.duration+" seconds");console.log("Size: "+this.videoWidth+","+this.videoHeight);n.setDirtyCanvas(!0);this.width=this.videoWidth;this.height=this.videoHeight});this._video.addEventListener("progress",function(c){console.log("video loading...")}); -this._video.addEventListener("error",function(c){console.error("Error loading video: "+this.src);if(this.error)switch(this.error.code){case this.error.MEDIA_ERR_ABORTED:console.error("You stopped the video.");break;case this.error.MEDIA_ERR_NETWORK:console.error("Network error - please try again later.");break;case this.error.MEDIA_ERR_DECODE:console.error("Video is broken..");break;case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED:console.error("Sorry, your browser can't play this video.")}});this._video.addEventListener("ended", -function(c){console.log("Video Ended.");this.play()})};D.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"url"==c&&""!=e&&this.loadVideo(e);return!0};D.prototype.play=function(){this._video&&this._video.videoWidth&&this._video.play()};D.prototype.playPause=function(){this._video&&(this._video.paused?this.play():this.pause())};D.prototype.stop=function(){this._video&&(this._video.pause(),this._video.currentTime=0)};D.prototype.pause=function(){this._video&&(console.log("Video paused"), -this._video.pause())};D.prototype.onWidget=function(c,e){};h.registerNodeType("graphics/video",D);A.title="Webcam";A.desc="Webcam image";A.is_webcam_open=!1;A.prototype.openStream=function(){function c(h){console.log("Webcam rejected",h);e._webcam_stream=!1;A.is_webcam_open=!1;e.boxcolor="red";e.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](c); -var e=this}};A.prototype.closeStream=function(){if(this._webcam_stream){var c=this._webcam_stream.getTracks();if(c.length)for(var e=0;e=this.size[1]||!this.properties.show|| -!this._video||(c.save(),c.drawImage(this._video,0,0,this.size[0],this.size[1]),c.restore())};A.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["stream_ready",h.EVENT],["stream_closed",h.EVENT],["stream_error",h.EVENT]]};h.registerNodeType("graphics/webcam",A)})(this); -(function(B){function c(){this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",filter:!0};this.size=[c.image_preview_size,c.image_preview_size]}function m(){this.addInput("Texture","Texture");this.properties={flipY:!1};this.size=[c.image_preview_size,c.image_preview_size]}function n(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",generate_mipmaps:!1}}function l(){this.addInput("Texture", -"Texture");this.addInput("TextureB","Texture");this.addInput("value","number");this.addOutput("Texture","Texture");this.help="

pixelcode must be vec3, uvcode must be vec2, is optional

\t\t

uv: tex. coords

color: texture colorB: textureB

time: scene time value: input value

For multiline you must type: result = ...

";this.properties={value:1,pixelcode:"color + colorB * value",uvcode:"",precision:c.DEFAULT}; -this.has_error=!1}function x(){this.addOutput("out","Texture");this.properties={code:"",u_value:1,u_color:[1,1,1,1],width:512,height:512,precision:c.DEFAULT};this.properties.code=x.pixel_shader;this._uniforms={u_value:1,u_color:vec4.create(),in_texture:0,texSize:vec2.create(),time:0}}function e(){this.addInput("in","Texture");this.addInput("scale","vec2");this.addInput("offset","vec2");this.addOutput("out","Texture");this.properties={offset:vec2.fromValues(0,0),scale:vec2.fromValues(1,1),precision:c.DEFAULT}} -function C(){this.addInput("in","Texture");this.addInput("warp","Texture");this.addInput("factor","number");this.addOutput("out","Texture");this.properties={factor:0.01,scale:[1,1],offset:[0,0],precision:c.DEFAULT};this._uniforms={u_texture:0,u_textureB:1,u_factor:1,u_scale:vec2.create(),u_offset:vec2.create()}}function w(){this.addInput("Texture","Texture");this.properties={additive:!1,antialiasing:!1,filter:!0,disable_alpha:!1,gamma:1,viewport:[0,0,1,1]};this.size[0]=130}function u(){this.addInput("Texture", -"Texture");this.addOutput("","Texture");this.properties={size:0,generate_mipmaps:!1,precision:c.DEFAULT}}function D(){this.addInput("Texture","Texture");this.addOutput("","Texture");this.properties={iterations:1,generate_mipmaps:!1,precision:c.DEFAULT}}function A(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("avg","vec4");this.addOutput("lum","number");this.properties={use_previous_frame:!0,high_quality:!1};this._uniforms={u_texture:0,u_mipmap_offset:0};this._luminance= -new Float32Array(4)}function h(){this.addInput("Texture","Texture");this.addOutput("min_t","Texture");this.addOutput("max_t","Texture");this.addOutput("min","vec4");this.addOutput("max","vec4");this.properties={mode:"max",use_previous_frame:!0};this._uniforms={u_texture:0};this._max=new Float32Array(4);this._min=new Float32Array(4);this._textures_chain=[]}function z(){this.addInput("in","Texture");this.addInput("factor","Number");this.addOutput("out","Texture");this.properties={factor:0.5};this._uniforms= -{u_texture:0,u_textureB:1,u_factor:this.properties.factor}}function y(){this.addInput("in","Texture");this.addOutput("avg","Texture");this.addOutput("array","Texture");this.properties={samples:64,frames_interval:1};this._uniforms={u_texture:0,u_textureB:1,u_samples:this.properties.samples,u_isamples:1/this.properties.samples};this.frame=0}function v(){this.addInput("Image","image");this.addOutput("","Texture");this.properties={}}function G(){this.addInput("Texture","Texture");this.addInput("LUT", -"Texture");this.addInput("Intensity","number");this.addOutput("","Texture");this.properties={enabled:!0,intensity:1,precision:c.DEFAULT,texture:null};G._shader||(G._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,G.pixel_shader))}function p(){this.addInput("Texture","Texture");this.addOutput("R","Texture");this.addOutput("G","Texture");this.addOutput("B","Texture");this.addOutput("A","Texture");p._shader||(p._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,p.pixel_shader))}function q(){this.addInput("R", -"Texture");this.addInput("G","Texture");this.addInput("B","Texture");this.addInput("A","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,R:1,G:1,B:1,A:1};this._color=vec4.create();this._uniforms={u_textureR:0,u_textureG:1,u_textureB:2,u_textureA:3,u_color:this._color}}function k(){this.addOutput("Texture","Texture");this._tex_color=vec4.create();this.properties={color:vec4.create(),precision:c.DEFAULT}}function a(){this.addInput("A","color");this.addInput("B","color"); -this.addOutput("Texture","Texture");this.properties={angle:0,scale:1,A:[0,0,0],B:[1,1,1],texture_size:32};a._shader||(a._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,a.pixel_shader));this._uniforms={u_angle:0,u_colorA:vec3.create(),u_colorB:vec3.create()}}function b(){this.addInput("A","Texture");this.addInput("B","Texture");this.addInput("Mixer","Texture");this.addOutput("Texture","Texture");this.properties={factor:0.5,size_from_biggest:!0,invert:!1,precision:c.DEFAULT};this._uniforms={u_textureA:0, -u_textureB:1,u_textureMix:2,u_mix:vec4.create()}}function d(){this.addInput("Tex.","Texture");this.addOutput("Edges","Texture");this.properties={invert:!0,threshold:!1,factor:1,precision:c.DEFAULT};d._shader||(d._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,d.pixel_shader))}function g(){this.addInput("Texture","Texture");this.addInput("Distance","number");this.addInput("Range","number");this.addOutput("Texture","Texture");this.properties={distance:100,range:50,only_depth:!1,high_precision:!1}; -this._uniforms={u_texture:0,u_distance:100,u_range:50,u_camera_planes:null}}function f(){this.addInput("Texture","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,invert:!1};this._uniforms={u_texture:0,u_near:0.1,u_far:1E4}}function r(){this.addInput("Texture","Texture");this.addInput("Iterations","number");this.addInput("Intensity","number");this.addOutput("Blurred","Texture");this.properties={intensity:1,iterations:1,preserve_aspect:!1,scale:[1,1],precision:c.DEFAULT}} -function s(){this.addInput("in","Texture");this.addInput("dirt","Texture");this.addOutput("out","Texture");this.addOutput("glow","Texture");this.properties={enabled:!0,intensity:1,persistence:0.99,iterations:16,threshold:0,scale:1,dirt_factor:0.5,precision:c.DEFAULT};this._textures=[];this._uniforms={u_intensity:1,u_texture:0,u_glow_texture:1,u_threshold:0,u_texel_size:vec2.create()}}function I(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={intensity:1, -radius:5}}function J(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={sigma:1.4,k:1.6,p:21.7,epsilon:79,phi:0.017}}function H(){this.addOutput("Webcam","Texture");this.properties={texture_name:"",facingMode:"user"};this.boxcolor="black";this.version=0}function E(){this.addInput("in","Texture");this.addInput("f","number");this.addOutput("out","Texture");this.properties={enabled:!0,factor:1,precision:c.LOW};this._uniforms={u_texture:0,u_factor:1}}function t(){this.addInput("in", -"Texture");this.addOutput("out","Texture");this.properties={precision:c.LOW,split_channels:!1};this._values=new Uint8Array(1024);this._values.fill(255);this._curve_texture=null;this._uniforms={u_texture:0,u_curve:1,u_range:1};this._must_update=!0;this._points={RGB:[[0,0],[1,1]],R:[[0,0],[1,1]],G:[[0,0],[1,1]],B:[[0,0],[1,1]]};this.curve_editor=null;this.addWidget("toggle","Split Channels",!1,"split_channels");this.addWidget("combo","Channel","RGB",{values:["RGB","R","G","B"]});this.curve_offset=68; -this.size=[240,160]}function N(){this.addInput("in","Texture");this.addInput("exp","number");this.addOutput("out","Texture");this.properties={exposition:1,precision:c.LOW};this._uniforms={u_texture:0,u_exposition:1}}function K(){this.addInput("in","Texture");this.addInput("avg","number,Texture");this.addOutput("out","Texture");this.properties={enabled:!0,scale:1,gamma:1,average_lum:1,lum_white:1,precision:c.LOW};this._uniforms={u_texture:0,u_lumwhite2:1,u_igamma:1,u_scale:1,u_average_lum:1}}function M(){this.addOutput("out", -"Texture");this.properties={width:512,height:512,seed:0,persistence:0.1,octaves:8,scale:1,offset:[0,0],amplitude:1,precision:c.DEFAULT};this._key=0;this._texture=null;this._uniforms={u_persistence:0.1,u_seed:0,u_offset:vec2.create(),u_scale:1,u_viewport:vec2.create()}}function L(){this.addInput("v");this.addOutput("out","Texture");this.properties={code:L.default_code,width:512,height:512,clear:!0,precision:c.DEFAULT,use_html_canvas:!1};this._temp_texture=this._func=null;this.compileCode()}function O(){this.addInput("in", -"Texture");this.addOutput("out","Texture");this.properties={key_color:vec3.fromValues(0,1,0),threshold:0.8,slope:0.2,precision:c.DEFAULT}}function P(){this.addInput("in","texture");this.addInput("yaw","number");this.addOutput("out","texture");this.properties={yaw:0}}var F=B.LiteGraph;B.LGraphTexture=null;"undefined"!=typeof GL&&(LGraphCanvas.link_type_colors.Texture="#987",B.LGraphTexture=c,c.title="Texture",c.desc="Texture",c.widgets_info={name:{widget:"texture"},filter:{widget:"checkbox"}},c.loadTextureCallback= -null,c.image_preview_size=256,c.PASS_THROUGH=1,c.COPY=2,c.LOW=3,c.HIGH=4,c.REUSE=5,c.DEFAULT=2,c.MODE_VALUES={"pass through":c.PASS_THROUGH,copy:c.COPY,low:c.LOW,high:c.HIGH,reuse:c.REUSE,"default":c.DEFAULT},c.getTexturesContainer=function(){return gl.textures},c.loadTexture=function(a,b){b=b||{};var d=a;"http://"==d.substr(0,7)&&F.proxy&&(d=F.proxy+d.substr(7));return c.getTexturesContainer()[a]=GL.Texture.fromURL(d,b)},c.getTexture=function(a){var b=this.getTexturesContainer();if(!b)throw"Cannot load texture, container of textures not found"; -b=b[a];return!b&&a&&":"!=a[0]?this.loadTexture(a):b},c.getTargetTexture=function(a,b,d){if(!a)throw"LGraphTexture.getTargetTexture expects a reference texture";var g=null;switch(d){case c.LOW:g=gl.UNSIGNED_BYTE;break;case c.HIGH:g=gl.HIGH_PRECISION_FORMAT;break;case c.REUSE:return a;default:g=a?a.type:gl.UNSIGNED_BYTE}b&&b.width==a.width&&b.height==a.height&&b.type==g||(b=new GL.Texture(a.width,a.height,{type:g,format:gl.RGBA,filter:gl.LINEAR}));return b},c.getTextureType=function(a,b){var d=b?b.type: -gl.UNSIGNED_BYTE;switch(a){case c.HIGH:d=gl.HIGH_PRECISION_FORMAT;break;case c.LOW:d=gl.UNSIGNED_BYTE}return d},c.getWhiteTexture=function(){return this._white_texture?this._white_texture:this._white_texture=GL.Texture.fromMemory(1,1,[255,255,255,255],{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.getNoiseTexture=function(){if(this._noise_texture)return this._noise_texture;for(var a=new Uint8Array(1048576),b=0;1048576>b;++b)a[b]=255*Math.random();return this._noise_texture=a=GL.Texture.fromMemory(512, -512,a,{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.prototype.onDropFile=function(a,b,d){if(a){var c=null;"string"==typeof a?c=GL.Texture.fromURL(a):-1!=b.toLowerCase().indexOf(".dds")?c=GL.Texture.fromDDSInMemory(a):(a=new Blob([d]),a=URL.createObjectURL(a),c=GL.Texture.fromURL(a));this._drop_texture=c;this.properties.name=b}else this._drop_texture=null,this.properties.name=""},c.prototype.getExtraMenuOptions=function(a){var b=this;if(this._drop_texture)return[{content:"Clear",callback:function(){b._drop_texture= -null;b.properties.name=""}}]},c.prototype.onExecute=function(){var a=null;this.isOutputConnected(1)&&(a=this.getInputData(0));!a&&this._drop_texture&&(a=this._drop_texture);!a&&this.properties.name&&(a=c.getTexture(this.properties.name));if(a){this._last_tex=a;!1===this.properties.filter?a.setParameter(gl.TEXTURE_MAG_FILTER,gl.NEAREST):a.setParameter(gl.TEXTURE_MAG_FILTER,gl.LINEAR);this.setOutputData(0,a);this.setOutputData(1,a.fullpath||a.filename);for(var b=2;b=this.size[1]))if(this._drop_texture&&a.webgl)a.drawImage(this._drop_texture,0,0,this.size[0],this.size[1]);else{if(this._last_preview_tex!=this._last_tex)if(a.webgl)this._canvas= -this._last_tex;else{var b=c.generateLowResTexturePreview(this._last_tex);if(!b)return;this._last_preview_tex=this._last_tex;this._canvas=cloneCanvas(b)}this._canvas&&(a.save(),a.webgl||(a.translate(0,this.size[1]),a.scale(1,-1)),a.drawImage(this._canvas,0,0,this.size[0],this.size[1]),a.restore())}},c.generateLowResTexturePreview=function(a){if(!a)return null;var b=c.image_preview_size,d=a;if(a.format==gl.DEPTH_COMPONENT)return null;if(a.width>b||a.height>b)d=this._preview_temp_tex,this._preview_temp_tex|| -(this._preview_temp_tex=d=new GL.Texture(b,b,{minFilter:gl.NEAREST})),a.copyTo(d);a=this._preview_canvas;a||(this._preview_canvas=a=createCanvas(b,b));d&&d.toCanvas(a);return a},c.prototype.getResources=function(a){this.properties.name&&(a[this.properties.name]=GL.Texture);return a},c.prototype.onGetInputs=function(){return[["in","Texture"]]},c.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["aspect","number"]]},c.replaceCode=function(a,b){return a.replace(/\{\{[a-zA-Z0-9_]*\}\}/g, -function(a){a=a.replace(/[\{\}]/g,"");return b[a]||""})},F.registerNodeType("texture/texture",c),m.title="Preview",m.desc="Show a texture in the graph canvas",m.allow_preview=!1,m.prototype.onDrawBackground=function(a){if(!this.flags.collapsed&&(a.webgl||m.allow_preview)){var b=this.getInputData(0);if(b){var d=null,d=!b.handle&&a.webgl?b:c.generateLowResTexturePreview(b);a.save();this.properties.flipY&&(a.translate(0,this.size[1]),a.scale(1,-1));a.drawImage(d,0,0,this.size[0],this.size[1]);a.restore()}}}, -F.registerNodeType("texture/preview",m),n.title="Save",n.desc="Save a texture in the repository",n.prototype.getPreviewTexture=function(){return this._texture},n.prototype.onExecute=function(){var a=this.getInputData(0);a&&(this.properties.generate_mipmaps&&(a.bind(0),a.setParameter(gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_LINEAR),gl.generateMipmap(a.texture_type),a.unbind(0)),this.properties.name&&(c.storeTexture?c.storeTexture(this.properties.name,a):c.getTexturesContainer()[this.properties.name]= -a),this._texture=a,this.setOutputData(0,a),this.setOutputData(1,this.properties.name))},F.registerNodeType("texture/save",n),l.widgets_info={uvcode:{widget:"code"},pixelcode:{widget:"code"},precision:{widget:"combo",values:c.MODE_VALUES}},l.title="Operation",l.desc="Texture shader operation",l.presets={},l.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:b.properties.show?"Hide Texture":"Show Texture",callback:function(){b.properties.show=!b.properties.show}}]},l.prototype.onPropertyChanged= -function(){this.has_error=!1},l.prototype.onDrawBackground=function(a){this.flags.collapsed||20>=this.size[1]||!this.properties.show||!this._tex||this._tex.gl!=a||(a.save(),a.drawImage(this._tex,0,0,this.size[0],this.size[1]),a.restore())},l.prototype.onExecute=function(){var a=this.getInputData(0);if(this.isOutputConnected(0))if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else{var b=this.getInputData(1);if(this.properties.uvcode||this.properties.pixelcode){var d=512,g=512; -a?(d=a.width,g=a.height):b&&(d=b.width,g=b.height);b||(b=GL.Texture.getWhiteTexture());var f=c.getTextureType(this.properties.precision,a);this._tex=a||this._tex?c.getTargetTexture(a||this._tex,this._tex,this.properties.precision):new GL.Texture(d,g,{type:f,format:gl.RGBA,filter:gl.LINEAR});f="";this.properties.uvcode&&(f="uv = "+this.properties.uvcode,-1!=this.properties.uvcode.indexOf(";")&&(f=this.properties.uvcode));var e="";this.properties.pixelcode&&(e="result = "+this.properties.pixelcode, --1!=this.properties.pixelcode.indexOf(";")&&(e=this.properties.pixelcode));var k=this._shader;if(!(this.has_error||k&&this._shader_code==f+"|"+e)){var h=c.replaceCode(l.pixel_shader,{UV_CODE:f,PIXEL_CODE:e});try{k=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,h),this.boxcolor="#00FF00"}catch(p){GL.Shader.dumpErrorToConsole(p,Shader.SCREEN_VERTEX_SHADER,h);this.boxcolor="#FF0000";this.has_error=!0;return}this._shader=k;this._shader_code=f+"|"+e}if(this._shader){var q=this.getInputData(2);null!=q?this.properties.value= -q:q=parseFloat(this.properties.value);var r=this.graph.getTime();this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);gl.disable(gl.BLEND);a&&a.bind(0);b&&b.bind(1);var c=Mesh.getScreenQuad();k.uniforms({u_texture:0,u_textureB:1,value:q,texSize:[d,g],time:r}).draw(c)});this.setOutputData(0,this._tex)}}}},l.pixel_shader="precision highp float;\n\t\t\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec2 texSize;\n\t\tuniform float time;\n\t\tuniform float value;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord;\n\t\t\t{{UV_CODE}};\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\tvec3 color = color4.rgb;\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\tvec3 colorB = color4B.rgb;\n\t\t\tvec3 result = color;\n\t\t\tfloat alpha = 1.0;\n\t\t\t{{PIXEL_CODE}};\n\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t}\n\t\t", -l.registerPreset=function(a,b){l.presets[a]=b},l.registerPreset("",""),l.registerPreset("bypass","color"),l.registerPreset("add","color + colorB * value"),l.registerPreset("substract","(color - colorB) * value"),l.registerPreset("mate","mix( color, colorB, color4B.a * value)"),l.registerPreset("invert","vec3(1.0) - color"),l.registerPreset("multiply","color * colorB * value"),l.registerPreset("divide","(color / colorB) / value"),l.registerPreset("difference","abs(color - colorB) * value"),l.registerPreset("max", -"max(color, colorB) * value"),l.registerPreset("min","min(color, colorB) * value"),l.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"),l.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"),l.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"),l.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"), -l.prototype.onInspect=function(a){var b=this;a.addCombo("Presets","",{values:Object.keys(l.presets),callback:function(d){var c=l.presets[d];c&&(b.setProperty("pixelcode",c),b.title=d,a.refresh())}})},F.registerNodeType("texture/operation",l),x.title="Shader",x.desc="Texture shader",x.widgets_info={code:{type:"code",lang:"glsl"},precision:{widget:"combo",values:c.MODE_VALUES}},x.prototype.onPropertyChanged=function(a,b){if("code"==a){var d=this.getShader();if(d){var c=d.uniformInfo;if(this.inputs)for(var g= -{},f=0;f lumaMax))\n\t\t\t\tcolor = vec4(rgbA, 1.0);\n\t\t\telse\n\t\t\t\tcolor = vec4(rgbB, 1.0);\n\t\t\tif(u_igamma != 1.0)\n\t\t\t\tcolor.xyz = pow( color.xyz, vec3(u_igamma) );\n\t\t\treturn color;\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\t\t}\n\t\t", -w.gamma_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_igamma;\n\t\tvoid main() {\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\t\t gl_FragColor = color;\n\t\t}\n\t\t",F.registerNodeType("texture/toviewport",w),u.title="Copy",u.desc="Copy Texture",u.widgets_info={size:{widget:"combo",values:[0,32,64,128,256,512,1024,2048]},precision:{widget:"combo", -values:c.MODE_VALUES}},u.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)){if(a){var b=a.width,d=a.height;0!=this.properties.size&&(d=b=this.properties.size);var g=this._temp_texture,f=a.type;this.properties.precision===c.LOW?f=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(f=gl.HIGH_PRECISION_FORMAT);g&&g.width==b&&g.height==d&&g.type==f||(g=gl.LINEAR,this.properties.generate_mipmaps&&isPowerOfTwo(b)&&isPowerOfTwo(d)&&(g=gl.LINEAR_MIPMAP_LINEAR), -this._temp_texture=new GL.Texture(b,d,{type:f,format:gl.RGBA,minFilter:g,magFilter:gl.LINEAR}));a.copyTo(this._temp_texture);this.properties.generate_mipmaps&&(this._temp_texture.bind(0),gl.generateMipmap(this._temp_texture.texture_type),this._temp_texture.unbind(0))}this.setOutputData(0,this._temp_texture)}},F.registerNodeType("texture/copy",u),D.title="Downsample",D.desc="Downsample Texture",D.widgets_info={iterations:{type:"number",step:1,precision:0,min:0},precision:{widget:"combo",values:c.MODE_VALUES}}, -D.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)&&a&&a.texture_type===GL.TEXTURE_2D)if(1>this.properties.iterations)this.setOutputData(0,a);else{var b=D._shader;b||(D._shader=b=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,D.pixel_shader));var d=a.width|0,g=a.height|0,f=a.type;this.properties.precision===c.LOW?f=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(f=gl.HIGH_PRECISION_FORMAT);var e=this.properties.iterations||1,k=a,h= -null,l=[],a={type:f,format:a.format},f=vec2.create(),p={u_offset:f};this._texture&&GL.Texture.releaseTemporary(this._texture);for(var q=0;q>1||0;g=g>>1||0;h=GL.Texture.getTemporary(d,g,a);l.push(h);k.setParameter(GL.TEXTURE_MAG_FILTER,GL.NEAREST);k.copyTo(h,b,p);if(1==d&&1==g)break;k=h}this._texture=l.pop();for(q=0;q>=2,c++,1!=d););a.copyTo(this._textures_chain[0]);for(var c=1;c<=this._textures_chain.length;++c)a=this._textures_chain[c];var g=h._shader,f=this._uniforms;f.u_mipmap_offset=this.properties.mipmap_offset;gl.disable(gl.DEPTH_TEST); -gl.disable(gl.BLEND);this._temp_texture.drawTo(function(){a.toViewport(g,f)})}},h.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform mat4 u_samples_a;\n\t\tuniform mat4 u_samples_b;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_mipmap_offset;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t//random average\n\t\t\tfor(int i = 0; i < 4; ++i)\n\t\t\t\tfor(int j = 0; j < 4; ++j)\n\t\t\t\t{\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t}\n\t\t gl_FragColor = color * 0.03125;\n\t\t}\n\t\t", -z.title="Smooth",z.desc="Smooth texture over time",z.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){z._shader||(z._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,z.pixel_shader));var b=this._temp_texture;b&&b.type==a.type&&b.width==a.width&&b.height==a.height||(b={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(a.width,a.height,b),this._temp_texture2=new GL.Texture(a.width,a.height,b),a.copyTo(this._temp_texture2));var b= -this._temp_texture,d=this._temp_texture2,c=z._shader,g=this._uniforms;g.u_factor=1-this.getInputOrProperty("factor");gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);b.drawTo(function(){d.bind(1);a.toViewport(c,g)});this.setOutputData(0,b);this._temp_texture=d;this._temp_texture2=b}},z.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_factor;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tgl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\t\t}\n\t\t", -F.registerNodeType("texture/temporal_smooth",z),y.title="Lineal Avg Smooth",y.desc="Smooth texture linearly over time",y["@samples"]={type:"number",min:1,max:64,step:1,precision:1},y.prototype.getPreviewTexture=function(){return this._temp_texture2},y.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){y._shader||(y._shader_copy=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,y.pixel_shader_copy),y._shader_avg=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,y.pixel_shader_avg)); -var b=Math.clamp(this.properties.samples,0,64),d=this.frame,c=this.properties.frames_interval;if(0==c||0==d%c){d=this._temp_texture;d&&d.type==a.type&&d.width==b||(d={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(b,1,d),this._temp_texture2=new GL.Texture(b,1,d),this._temp_texture_out=new GL.Texture(1,1,d));var g=this._temp_texture,f=this._temp_texture2,e=y._shader_copy,k=y._shader_avg,h=this._uniforms;h.u_samples=b;h.u_isamples=1/b;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST); -g.drawTo(function(){f.bind(1);a.toViewport(e,h)});this._temp_texture_out.drawTo(function(){g.toViewport(k,h)});this.setOutputData(0,this._temp_texture_out);this._temp_texture=f;this._temp_texture2=g}else this.setOutputData(0,this._temp_texture_out);this.setOutputData(1,this._temp_texture2);this.frame++}},y.pixel_shader_copy="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tif( v_coord.x <= u_isamples )\n\t\t\t\tgl_FragColor = texture2D( u_texture, vec2(0.5) );\n\t\t\telse\n\t\t\t\tgl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\t\t}\n\t\t", -y.pixel_shader_avg="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform int u_samples;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\tfor(int i = 0; i < 64; ++i)\n\t\t\t{\n\t\t\t\tcolor += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\t\t\t\tif(i == (u_samples - 1))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tgl_FragColor = color * u_isamples;\n\t\t}\n\t\t",F.registerNodeType("texture/linear_avg_smooth", -y),v.title="Image to Texture",v.desc="Uploads an image to the GPU",v.prototype.onExecute=function(){var a=this.getInputData(0);if(a){var b=a.videoWidth||a.width,d=a.videoHeight||a.height;if(a.gltexture)this.setOutputData(0,a.gltexture);else{var c=this._temp_texture;c&&c.width==b&&c.height==d||(this._temp_texture=new GL.Texture(b,d,{format:gl.RGBA,filter:gl.LINEAR}));try{this._temp_texture.uploadImage(a)}catch(g){console.error("image comes from an unsafe location, cannot be uploaded to webgl: "+g); -return}this.setOutputData(0,this._temp_texture)}}},F.registerNodeType("texture/imageToTexture",v),G.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},G.title="LUT",G.desc="Apply LUT to Texture",G.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture)); -if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.bindTexture(gl.TEXTURE_2D,null);var d=this.properties.intensity;this.isInputConnected(2)&&(this.properties.intensity=d=this.getInputData(2));this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);this._tex.drawTo(function(){b.bind(1);a.toViewport(G._shader,{u_texture:0,u_textureB:1, -u_amount:d})});this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}},G.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_amount;\n\t\t\n\t\tvoid main() {\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\t\t\t mediump vec2 quad1;\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\t\t\t mediump vec2 quad2;\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\t\t\t highp vec2 texPos1;\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t highp vec2 texPos2;\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\t\t}\n\t\t", -F.registerNodeType("texture/LUT",G),p.title="Texture to Channels",p.desc="Split texture channels",p.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this._channels||(this._channels=Array(4));for(var b=gl.RGB,d=0,c=0;4>c;c++)this.isOutputConnected(c)?(this._channels[c]&&this._channels[c].width==a.width&&this._channels[c].height==a.height&&this._channels[c].type==a.type&&this._channels[c].format==b||(this._channels[c]=new GL.Texture(a.width,a.height,{type:a.type,format:b,filter:gl.LINEAR})), -d++):this._channels[c]=null;if(d){gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);for(var g=Mesh.getScreenQuad(),f=p._shader,e=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],c=0;4>c;c++)this._channels[c]&&(this._channels[c].drawTo(function(){a.bind(0);f.uniforms({u_texture:0,u_mask:e[c]}).draw(g)}),this.setOutputData(c,this._channels[c]))}}},p.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec4 u_mask;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\t\t}\n\t\t", -F.registerNodeType("texture/textureChannels",p),q.title="Channels to Texture",q.desc="Split texture channels",q.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},q.prototype.onExecute=function(){var a=c.getWhiteTexture(),b=this.getInputData(0)||a,d=this.getInputData(1)||a,g=this.getInputData(2)||a,f=this.getInputData(3)||a;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad();q._shader||(q._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,q.pixel_shader));var k=q._shader, -a=Math.max(b.width,d.width,g.width,f.width),h=Math.max(b.height,d.height,g.height,f.height),l=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._texture&&this._texture.width==a&&this._texture.height==h&&this._texture.type==l||(this._texture=new GL.Texture(a,h,{type:l,format:gl.RGBA,filter:gl.LINEAR}));a=this._color;a[0]=this.properties.R;a[1]=this.properties.G;a[2]=this.properties.B;a[3]=this.properties.A;var p=this._uniforms;this._texture.drawTo(function(){b.bind(0); -d.bind(1);g.bind(2);f.bind(3);k.uniforms(p).draw(e)});this.setOutputData(0,this._texture)},q.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureR;\n\t\tuniform sampler2D u_textureG;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform vec4 u_color;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = u_color * vec4( \t\t\t\t\ttexture2D(u_textureR, v_coord).r,\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\t\t}\n\t\t", -F.registerNodeType("texture/channelsTexture",q),k.title="Color",k.desc="Generates a 1x1 texture with a constant color",k.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},k.prototype.onDrawBackground=function(a){var b=this.properties.color;a.fillStyle="rgb("+Math.floor(255*Math.clamp(b[0],0,1))+","+Math.floor(255*Math.clamp(b[1],0,1))+","+Math.floor(255*Math.clamp(b[2],0,1))+")";this.flags.collapsed?this.boxcolor=a.fillStyle:a.fillRect(0,0,this.size[0],this.size[1])},k.prototype.onExecute= -function(){var a=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._tex&&this._tex.type==a||(this._tex=new GL.Texture(1,1,{format:gl.RGBA,type:a,minFilter:gl.NEAREST}));a=this.properties.color;if(this.inputs)for(var b=0;ba.width?d: -a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad(),k=null,h=this._uniforms;g?(k=b._shader_tex,k||(k=b._shader_tex=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader,{MIX_TEX:""}))):(k=b._shader_factor,k||(k=b._shader_factor=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader)),f=null==f?this.properties.factor:f,h.u_mix.set([f,f,f,f]));var l=this.properties.invert;this._tex.drawTo(function(){a.bind(l?1:0);d.bind(l?0:1);g&&g.bind(2); -k.uniforms(h).draw(e)});this.setOutputData(0,this._tex)}}},b.prototype.onGetInputs=function(){return[["factor","number"]]},b.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform sampler2D u_textureB;\n\t\t#ifdef MIX_TEX\n\t\t\tuniform sampler2D u_textureMix;\n\t\t#else\n\t\t\tuniform vec4 u_mix;\n\t\t#endif\n\t\t\n\t\tvoid main() {\n\t\t\t#ifdef MIX_TEX\n\t\t\t vec4 f = texture2D(u_textureMix, v_coord);\n\t\t\t#else\n\t\t\t vec4 f = u_mix;\n\t\t\t#endif\n\t\t gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\t\t}\n\t\t", -F.registerNodeType("texture/mix",b),d.title="Edges",d.desc="Detects edges",d.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},d.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else if(a){this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var b=Mesh.getScreenQuad(),g=d._shader,f=this.properties.invert,e=this.properties.factor, -k=this.properties.threshold?1:0;this._tex.drawTo(function(){a.bind(0);g.uniforms({u_texture:0,u_isize:[1/a.width,1/a.height],u_factor:e,u_threshold:k,u_invert:f?1:0}).draw(b)});this.setOutputData(0,this._tex)}}},d.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_isize;\n\t\tuniform int u_invert;\n\t\tuniform float u_factor;\n\t\tuniform float u_threshold;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 center = texture2D(u_texture, v_coord);\n\t\t\tvec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\t\t\tvec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\t\t\tvec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\t\t\tvec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\t\t\tvec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\t\t\tdiff *= u_factor;\n\t\t\tif(u_invert == 1)\n\t\t\t\tdiff.xyz = vec3(1.0) - diff.xyz;\n\t\t\tif( u_threshold == 0.0 )\n\t\t\t\tgl_FragColor = vec4( diff.xyz, center.a );\n\t\t\telse\n\t\t\t\tgl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\t\t}\n\t\t", -F.registerNodeType("texture/edges",d),g.title="Depth Range",g.desc="Generates a texture with a depth range",g.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a){var b=gl.UNSIGNED_BYTE;this.properties.high_precision&&(b=gl.half_float_ext?gl.HALF_FLOAT_OES:gl.FLOAT);this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&&this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGBA, -filter:gl.LINEAR}));var d=this._uniforms,b=this.properties.distance;this.isInputConnected(1)&&(b=this.getInputData(1),this.properties.distance=b);var c=this.properties.range;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.range=c);d.u_distance=b;d.u_range=c;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var f=Mesh.getScreenQuad();g._shader||(g._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,g.pixel_shader),g._shader_onlydepth=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,g.pixel_shader, -{ONLY_DEPTH:""}));var e=this.properties.only_depth?g._shader_onlydepth:g._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1,1E3];d.u_camera_planes=b;this._temp_texture.drawTo(function(){a.bind(0);e.uniforms(d).draw(f)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},g.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform float u_distance;\n\t\tuniform float u_range;\n\t\t\n\t\tfloat LinearDepth()\n\t\t{\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\t\t\tdepth = depth * 2.0 - 1.0;\n\t\t\treturn zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t\tfloat depth = LinearDepth();\n\t\t\t#ifdef ONLY_DEPTH\n\t\t\t gl_FragColor = vec4(depth);\n\t\t\t#else\n\t\t\t\tfloat diff = abs(depth * u_camera_planes.y - u_distance);\n\t\t\t\tfloat dof = 1.0;\n\t\t\t\tif(diff <= u_range)\n\t\t\t\t\tdof = diff / u_range;\n\t\t\t gl_FragColor = vec4(dof);\n\t\t\t#endif\n\t\t}\n\t\t", -F.registerNodeType("texture/depth_range",g),f.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},f.title="Linear Depth",f.desc="Creates a color texture with linear depth",f.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a&&(a.format==gl.DEPTH_COMPONENT||a.format==gl.DEPTH_STENCIL)){var b=this.properties.precision==c.HIGH?gl.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&& -this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGB,filter:gl.LINEAR}));var d=this._uniforms;d.u_near=a.near_far_planes[0];d.u_far=a.near_far_planes[1];d.u_invert=this.properties.invert?1:0;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var g=Mesh.getScreenQuad();f._shader||(f._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,f.pixel_shader));var e=f._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera? -LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1,1E3];d.u_camera_planes=b;this._temp_texture.drawTo(function(){a.bind(0);e.uniforms(d).draw(g)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},f.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_near;\n\t\tuniform float u_far;\n\t\tuniform int u_invert;\n\t\t\n\t\tvoid main() {\n\t\t\tfloat zNear = u_near;\n\t\t\tfloat zFar = u_far;\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\t\t\tdepth = depth * 2.0 - 1.0;\n\t\t\tfloat f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t\tif( u_invert == 1 )\n\t\t\t\tf = 1.0 - f;\n\t\t\tgl_FragColor = vec4(vec3(f),1.0);\n\t\t}\n\t\t", -F.registerNodeType("texture/linear_depth",f),r.title="Blur",r.desc="Blur a texture",r.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},r.max_iterations=20,r.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._final_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(b=this._final_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));var d=this.properties.iterations;this.isInputConnected(1)&& -(d=this.getInputData(1),this.properties.iterations=d);d=Math.min(Math.floor(d),r.max_iterations);if(0==d)this.setOutputData(0,a);else{var c=this.properties.intensity;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.intensity=c);var g=F.camera_aspect;g||void 0===window.gl||(g=gl.canvas.height/gl.canvas.width);g||(g=1);var g=this.properties.preserve_aspect?g:1,f=this.properties.scale||[1,1];a.applyBlur(g*f[0],f[1],c,b);for(a=1;a>=1;1<(d|0)&&(d>>=1);if(2>b)break;l=k[m]=GL.Texture.getTemporary(b,d,g);r[0]=1/p.width;r[1]=1/p.height;p.blit(l,h.uniforms(e));p=l}this.isOutputConnected(2)&&(b=this._average_texture,b&&b.type==a.type&&b.format==a.format||(b=this._average_texture=new GL.Texture(1,1,{type:a.type,format:a.format,filter:gl.LINEAR})),r[0]=1/p.width,r[1]=1/p.height,e.u_intensity=n,e.u_delta=1,p.blit(b,h.uniforms(e)),this.setOutputData(2,b));gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE);e.u_intensity= -this.getInputOrProperty("persistence");e.u_delta=0.5;for(m-=2;0<=m;m--)l=k[m],k[m]=null,r[0]=1/p.width,r[1]=1/p.height,p.blit(l,h.uniforms(e)),GL.Texture.releaseTemporary(p),p=l;gl.disable(gl.BLEND);this.isOutputConnected(1)&&(k=this._glow_texture,k&&k.width==a.width&&k.height==a.height&&k.type==f&&k.format==a.format||(k=this._glow_texture=new GL.Texture(a.width,a.height,{type:f,format:a.format,filter:gl.LINEAR})),p.blit(k),this.setOutputData(1,k));if(this.isOutputConnected(0)){k=this._final_texture; -k&&k.width==a.width&&k.height==a.height&&k.type==f&&k.format==a.format||(k=this._final_texture=new GL.Texture(a.width,a.height,{type:f,format:a.format,filter:gl.LINEAR}));var t=this.getInputData(1),u=this.getInputOrProperty("dirt_factor");e.u_intensity=n;h=t?s._dirt_final_shader:s._final_shader;h||(h=t?s._dirt_final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,s.final_pixel_shader,{USE_DIRT:""}):s._final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,s.final_pixel_shader));k.drawTo(function(){a.bind(0); -p.bind(1);t&&(h.setUniform("u_dirt_factor",u),h.setUniform("u_dirt_texture",t.bind(2)));h.toViewport(e)});this.setOutputData(0,k)}GL.Texture.releaseTemporary(p)}},s.cut_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform float u_threshold;\n\tvoid main() {\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\t}",s.scale_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\t}", -s.final_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform sampler2D u_glow_texture;\n\t#ifdef USE_DIRT\n\t\tuniform sampler2D u_dirt_texture;\n\t#endif\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\tuniform float u_dirt_factor;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tvec4 glow = sampleBox( v_coord );\n\t\t#ifdef USE_DIRT\n\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\t\t#endif\n\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\t}", -F.registerNodeType("texture/glow",s),I.title="Kuwahara Filter",I.desc="Filters a texture giving an artistic oil canvas painting",I.max_radius=10,I._shaders=[],I.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));b=this.properties.radius;b=Math.min(Math.floor(b),I.max_radius);if(0== -b)this.setOutputData(0,a);else{var d=this.properties.intensity,c=F.camera_aspect;c||void 0===window.gl||(c=gl.canvas.height/gl.canvas.width);c||(c=1);c=this.properties.preserve_aspect?c:1;I._shaders[b]||(I._shaders[b]=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,I.pixel_shader,{RADIUS:b.toFixed(0)}));var g=I._shaders[b],f=GL.Mesh.getScreenQuad();a.bind(0);this._temp_texture.drawTo(function(){g.uniforms({u_texture:0,u_intensity:d,u_resolution:[a.width,a.height],u_iResolution:[1/a.width,1/a.height]}).draw(f)}); -this.setOutputData(0,this._temp_texture)}}},I.pixel_shader="\nprecision highp float;\nvarying vec2 v_coord;\nuniform sampler2D u_texture;\nuniform float u_intensity;\nuniform vec2 u_resolution;\nuniform vec2 u_iResolution;\n#ifndef RADIUS\n\t#define RADIUS 7\n#endif\nvoid main() {\n\n\tconst int radius = RADIUS;\n\tvec2 fragCoord = v_coord;\n\tvec2 src_size = u_iResolution;\n\tvec2 uv = v_coord;\n\tfloat n = float((radius + 1) * (radius + 1));\n\tint i;\n\tint j;\n\tvec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\tvec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\tvec3 c;\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm0 += c;\n\t\t\ts0 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm1 += c;\n\t\t\ts1 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm2 += c;\n\t\t\ts2 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm3 += c;\n\t\t\ts3 += c * c;\n\t\t}\n\t}\n\t\n\tfloat min_sigma2 = 1e+2;\n\tm0 /= n;\n\ts0 = abs(s0 / n - m0 * m0);\n\t\n\tfloat sigma2 = s0.r + s0.g + s0.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m0, 1.0);\n\t}\n\t\n\tm1 /= n;\n\ts1 = abs(s1 / n - m1 * m1);\n\t\n\tsigma2 = s1.r + s1.g + s1.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m1, 1.0);\n\t}\n\t\n\tm2 /= n;\n\ts2 = abs(s2 / n - m2 * m2);\n\t\n\tsigma2 = s2.r + s2.g + s2.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m2, 1.0);\n\t}\n\t\n\tm3 /= n;\n\ts3 = abs(s3 / n - m3 * m3);\n\t\n\tsigma2 = s3.r + s3.g + s3.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m3, 1.0);\n\t}\n}\n", -F.registerNodeType("texture/kuwahara",I),J.title="XDoG Filter",J.desc="Filters a texture giving an artistic ink style",J.max_radius=10,J._shaders=[],J.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));J._xdog_shader||(J._xdog_shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,J.xdog_pixel_shader)); -var d=J._xdog_shader,c=GL.Mesh.getScreenQuad(),g=this.properties.sigma,f=this.properties.k,e=this.properties.p,k=this.properties.epsilon,h=this.properties.phi;a.bind(0);this._temp_texture.drawTo(function(){d.uniforms({src:0,sigma:g,k:f,p:e,epsilon:k,phi:h,cvsWidth:a.width,cvsHeight:a.height}).draw(c)});this.setOutputData(0,this._temp_texture)}},J.xdog_pixel_shader="\nprecision highp float;\nuniform sampler2D src;\n\nuniform float cvsHeight;\nuniform float cvsWidth;\n\nuniform float sigma;\nuniform float k;\nuniform float p;\nuniform float epsilon;\nuniform float phi;\nvarying vec2 v_coord;\n\nfloat cosh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat cosH = (tmp + 1.0 / tmp) / 2.0;\n\treturn cosH;\n}\n\nfloat tanh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\treturn tanH;\n}\n\nfloat sinh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat sinH = (tmp - 1.0 / tmp) / 2.0;\n\treturn sinH;\n}\n\nvoid main(void){\n\tvec3 destColor = vec3(0.0);\n\tfloat tFrag = 1.0 / cvsHeight;\n\tfloat sFrag = 1.0 / cvsWidth;\n\tvec2 Frag = vec2(sFrag,tFrag);\n\tvec2 uv = gl_FragCoord.st;\n\tfloat twoSigmaESquared = 2.0 * sigma * sigma;\n\tfloat twoSigmaRSquared = twoSigmaESquared * k * k;\n\tint halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\tconst int MAX_NUM_ITERATION = 99999;\n\tvec2 sum = vec2(0.0);\n\tvec2 norm = vec2(0.0);\n\n\tfor(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\t\tint i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\t\tint j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\t\tfloat d = length(vec2(i,j));\n\t\tvec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\t\t\t\t\t\t\texp( -d * d / twoSigmaRSquared ));\n\n\t\tvec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\t\tnorm += kernel;\n\t\tsum += kernel * L;\n\t}\n\n\tsum /= norm;\n\n\tfloat H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\tfloat edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\tdestColor = vec3(edge);\n\tgl_FragColor = vec4(destColor, 1.0);\n}", -F.registerNodeType("texture/xDoG",J),H.title="Webcam",H.desc="Webcam texture",H.is_webcam_open=!1,H.prototype.openStream=function(){function a(d){H.is_webcam_open=!1;console.log("Webcam rejected",d);b._webcam_stream=!1;b.boxcolor="red";b.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](a);var b=this}},H.prototype.closeStream=function(){if(this._webcam_stream){var a= -this._webcam_stream.getTracks();if(a.length)for(var b=0;b=this.size[1]||!this._video||(a.save(),a.webgl?this._video_texture&&a.drawImage(this._video_texture,0,0,this.size[0],this.size[1]):a.drawImage(this._video, -0,0,this.size[0],this.size[1]),a.restore())},H.prototype.onExecute=function(){null!=this._webcam_stream||this._waiting_confirmation||this.openStream();if(this._video&&this._video.videoWidth){var a=this._video.videoWidth,b=this._video.videoHeight,d=this._video_texture;d&&d.width==a&&d.height==b||(this._video_texture=new GL.Texture(a,b,{format:gl.RGB,filter:gl.LINEAR}));this._video_texture.uploadImage(this._video);this._video_texture.version=++this.version;this.properties.texture_name&&(c.getTexturesContainer()[this.properties.texture_name]= -this._video_texture);this.setOutputData(0,this._video_texture);for(a=1;aMath.abs(d))return c[1];d=(a-c[0])/d;return c[1]*(1-d)+g[1]*d}}return 0}},t.prototype.updateCurve=function(){for(var a=this._values,b=a.length/4,d=this.properties.split_channels,c=0;c=f;){e=0.5*(k+f)|0;c=a[e];if(c==d)break;if(f==k-1)return f;ca&&(a=1);this.points&&this.points.length==3*a||(this.points=new Float32Array(3*a));this.properties.generate_normals?this.normals&&this.normals.length==this.points.length||(this.normals=new Float32Array(this.points.length)):this.normals=null;var d=this._last_radius||this.properties.radius,c=this.properties.mode,f=this.getInputData(0);this._old_obj_version=f?f._version:null;this.points=m.generatePoints(d,a,c,this.points, -this.normals,this.properties.regular,f);this.version++};m.generatePoints=function(a,d,c,f,e,k,h){var p=3*d;f&&f.length==p||(f=new Float32Array(p));var l=new Float32Array(3),q=new Float32Array([0,1,0]);if(k)if(c==m.RECTANGLE){p=Math.floor(Math.sqrt(d));for(d=0;de||wk&&kh))break}this.geometry.indices=this.indices=new Uint32Array(p)}this.indices&&this.indices.length?(this.geometry.indices=this.indices,this.setOutputData(0,this.geometry)):this.setOutputData(0,null)}};v.registerNodeType("geometry/connectPoints",u);"undefined"!=typeof GL&&(D.title="to geometry",D.desc="converts a mesh to geometry",D.prototype.onExecute= -function(){var a=this.getInputData(0);if(a){if(a!=this.last_mesh){this.last_mesh=a;for(i in a.vertexBuffers)this.geometry[i]=a.vertexBuffers[i].data;a.indexBuffers.triangles&&(this.geometry.indices=a.indexBuffers.triangles.data);this.geometry._id=c();this.geometry._version=0}this.setOutputData(0,this.geometry);this.geometry&&this.setOutputData(1,this.geometry.vertices)}},v.registerNodeType("geometry/toGeometry",D),A.title="Geo to Mesh",A.prototype.updateMesh=function(a){this.mesh||(this.mesh=new GL.Mesh); -for(var d in a)if("_"!=d[0]){var c=a[d],f=GL.Mesh.common_buffers[d];if(f||"indices"==d){var f=f?f.spacing:3,e=this.mesh.vertexBuffers[d];e&&e.data.length==c.length?(e.data.set(c),e.upload(GL.DYNAMIC_DRAW)):e=new GL.Buffer("indices"==d?GL.ELEMENT_ARRAY_BUFFER:GL.ARRAY_BUFFER,c,f,GL.DYNAMIC_DRAW);this.mesh.addBuffer(d,e)}}if(this.mesh.vertexBuffers.normals&&this.mesh.vertexBuffers.normals.data.length!=this.mesh.vertexBuffers.vertices.data.length){c=new Float32Array([0,1,0]);f=new Float32Array(this.mesh.vertexBuffers.vertices.data.length); -for(d=0;d=c.NOTEON||h<=c.NOTEOFF)this.channel=e&15};Object.defineProperty(c.prototype,"velocity",{get:function(){return this.cmd==c.NOTEON?this.data[2]:-1},set:function(c){this.data[2]=c},enumerable:!0});c.notes="A A# B C C# D D# E F F# G G#".split(" ");c.note_to_index={A:0,"A#":1,B:2,C:3,"C#":4,D:5,"D#":6,E:7,F:8,"F#":9,G:10,"G#":11};Object.defineProperty(c.prototype,"note",{get:function(){return this.cmd!= -c.NOTEON?-1:c.toNoteString(this.data[1],!0)},set:function(c){throw"notes cannot be assigned this way, must modify the data[1]";},enumerable:!0});Object.defineProperty(c.prototype,"octave",{get:function(){return this.cmd!=c.NOTEON?-1:Math.floor((this.data[1]-24)/12+1)},set:function(c){throw"octave cannot be assigned this way, must modify the data[1]";},enumerable:!0});c.prototype.getPitch=function(){return 440*Math.pow(2,(this.data[1]-69)/12)};c.computePitch=function(c){return 440*Math.pow(2,(c-69)/ -12)};c.prototype.getCC=function(){return this.data[1]};c.prototype.getCCValue=function(){return this.data[2]};c.prototype.getPitchBend=function(){return this.data[1]+(this.data[2]<<7)-8192};c.computePitchBend=function(c,e){return c+(e<<7)-8192};c.prototype.setCommandFromString=function(e){this.cmd=c.computeCommandFromString(e)};c.computeCommandFromString=function(e){if(!e)return 0;if(e&&e.constructor===Number)return e;e=e.toUpperCase();switch(e){case "NOTE ON":case "NOTEON":return c.NOTEON;case "NOTE OFF":case "NOTEOFF":return c.NOTEON; -case "KEY PRESSURE":case "KEYPRESSURE":return c.KEYPRESSURE;case "CONTROLLER CHANGE":case "CONTROLLERCHANGE":case "CC":return c.CONTROLLERCHANGE;case "PROGRAM CHANGE":case "PROGRAMCHANGE":case "PC":return c.PROGRAMCHANGE;case "CHANNEL PRESSURE":case "CHANNELPRESSURE":return c.CHANNELPRESSURE;case "PITCH BEND":case "PITCHBEND":return c.PITCHBEND;case "TIME TICK":case "TIMETICK":return c.TIMETICK;default:return Number(e)}};c.toNoteString=function(e,h){e=Math.round(e);var k,a=Math.floor((e-24)/12+1); -k=(e-21)%12;0>k&&(k=12+k);return c.notes[k]+(h?"":a)};c.NoteStringToPitch=function(e){e=e.toUpperCase();var h=e[0],k=4;"#"==e[1]?(h+="#",2this.properties.max_value)return;this.trigger("on_midi",h)}};v.registerNodeType("midi/filter",e);C.title="MIDIEvent";C.desc="Create a MIDI Event";C.color="#243";C.prototype.onAction=function(e,h){"assign"==e?(this.properties.channel=h.channel,this.properties.cmd=h.cmd,this.properties.value1=h.data[1],this.properties.value2=h.data[2],h.cmd== -c.NOTEON?this.gate=!0:h.cmd==c.NOTEOFF&&(this.gate=!1)):(h=this.midi_event,h.channel=this.properties.channel,this.properties.cmd&&this.properties.cmd.constructor===String?h.setCommandFromString(this.properties.cmd):h.cmd=this.properties.cmd,h.data[0]=h.cmd|h.channel,h.data[1]=Number(this.properties.value1),h.data[2]=Number(this.properties.value2),this.trigger("on_midi",h))};C.prototype.onExecute=function(){var e=this.properties;if(this.inputs)for(var h=0;hc;++c)this.valid_notes[c]=-1!=this.notes_pitches.indexOf(c);for(c=0;12>c;++c)if(this.valid_notes[c])this.offset_notes[c]=0;else for(var e=1;12>e;++e){if(this.valid_notes[(c-e)%12]){this.offset_notes[c]=-e;break}if(this.valid_notes[(c+e)%12]){this.offset_notes[c]=e;break}}};A.prototype.onAction=function(e,h){h&&h.constructor===c&&(h.data[0]==c.NOTEON||h.data[0]==c.NOTEOFF?(this.midi_event=new c,this.midi_event.setup(h.data),this.midi_event.data[1]+=this.offset_notes[c.note_to_index[h.note]], -this.trigger("out",this.midi_event)):this.trigger("out",h))};A.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&c!=this._current_scale&&this.processScale(c)};v.registerNodeType("midi/quantize",A);h.title="MIDI fromFile";h.desc="Plays a MIDI file";h.color="#243";h.prototype.onAction=function(c){"play"==c?this.play():"pause"==c&&(this._playing=!this._playing)};h.prototype.onPropertyChanged=function(c,e){"url"==c&&this.loadMIDIFile(e)};h.prototype.onExecute=function(){if(this._midi&& -this._playing){this._current_time+=this.graph.elapsed_time;for(var e=100*this._current_time,h=0;hb;b++)for(var d=0;dg+f||c[1]>d))return b}}return-1};y.prototype.onAction=function(e,h){if("reset"==e)for(var k=0;kh[1])){var k=this.getKeyIndex(h);this.keys[k]=!0;this._last_key=k;var k=12*(this.properties.start_octave-1)+29+k,a=new c;a.setup([c.NOTEON,k,100]);this.trigger("note",a);return!0}};y.prototype.onMouseMove=function(e,h){if(!(0>h[1]||-1==this._last_key)){this.setDirtyCanvas(!0); -var k=this.getKeyIndex(h);if(this._last_key==k)return!0;this.keys[this._last_key]=!1;var a=12*(this.properties.start_octave-1)+29+this._last_key,b=new c;b.setup([c.NOTEOFF,a,100]);this.trigger("note",b);this.keys[k]=!0;a=12*(this.properties.start_octave-1)+29+k;b=new c;b.setup([c.NOTEON,a,100]);this.trigger("note",b);this._last_key=k;return!0}};y.prototype.onMouseUp=function(e,h){if(!(0>h[1])){var k=this.getKeyIndex(h);this.keys[k]=!1;this._last_key=-1;var k=12*(this.properties.start_octave-1)+29+ -k,a=new c;a.setup([c.NOTEOFF,k,100]);this.trigger("note",a);return!0}};v.registerNodeType("midi/keys",y)})(this); -(function(B){function c(){this.properties={src:"",gain:0.5,loop:!0,autoplay:!0,playbackRate:1};this._loading_audio=!1;this._audiobuffer=null;this._audionodes=[];this._last_sourcenode=null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=q.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain;this.properties.src&&this.loadSound(this.properties.src)}function m(){this.properties={gain:0.5};this._audionodes=[];this._media_stream= -null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=q.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain}function n(){this.properties={fftSize:2048,minDecibels:-100,maxDecibels:-10,smoothingTimeConstant:0.5};this.audionode=q.getAudioContext().createAnalyser();this.audionode.graphnode=this;this.audionode.fftSize=this.properties.fftSize;this.audionode.minDecibels=this.properties.minDecibels;this.audionode.maxDecibels= -this.properties.maxDecibels;this.audionode.smoothingTimeConstant=this.properties.smoothingTimeConstant;this.addInput("in","audio");this.addOutput("freqs","array");this.addOutput("samples","array");this._time_bin=this._freq_bin=null}function l(){this.properties={gain:1};this.audionode=q.getAudioContext().createGain();this.addInput("in","audio");this.addInput("gain","number");this.addOutput("out","audio")}function x(){this.properties={impulse_src:"",normalize:!0};this.audionode=q.getAudioContext().createConvolver(); -this.addInput("in","audio");this.addOutput("out","audio")}function e(){this.properties={threshold:-50,knee:40,ratio:12,reduction:-20,attack:0,release:0.25};this.audionode=q.getAudioContext().createDynamicsCompressor();this.addInput("in","audio");this.addOutput("out","audio")}function C(){this.properties={};this.audionode=q.getAudioContext().createWaveShaper();this.addInput("in","audio");this.addInput("shape","waveshape");this.addOutput("out","audio")}function w(){this.properties={gain1:0.5,gain2:0.5}; -this.audionode=q.getAudioContext().createGain();this.audionode1=q.getAudioContext().createGain();this.audionode1.gain.value=this.properties.gain1;this.audionode2=q.getAudioContext().createGain();this.audionode2.gain.value=this.properties.gain2;this.audionode1.connect(this.audionode);this.audionode2.connect(this.audionode);this.addInput("in1","audio");this.addInput("in1 gain","number");this.addInput("in2","audio");this.addInput("in2 gain","number");this.addOutput("out","audio")}function u(){this.properties= -{A:0.1,D:0.1,S:0.1,R:0.1};this.audionode=q.getAudioContext().createGain();this.audionode.gain.value=0;this.addInput("in","audio");this.addInput("gate","bool");this.addOutput("out","audio");this.gate=!1}function D(){this.properties={delayTime:0.5};this.audionode=q.getAudioContext().createDelay(10);this.audionode.delayTime.value=this.properties.delayTime;this.addInput("in","audio");this.addInput("time","number");this.addOutput("out","audio")}function A(){this.properties={frequency:350,detune:0,Q:1}; -this.addProperty("type","lowpass","enum",{values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")});this.audionode=q.getAudioContext().createBiquadFilter();this.addInput("in","audio");this.addOutput("out","audio")}function h(){this.properties={frequency:440,detune:0,type:"sine"};this.addProperty("type","sine","enum",{values:["sine","square","sawtooth","triangle","custom"]});this.audionode=q.getAudioContext().createOscillator();this.addOutput("out","audio")}function z(){this.properties= -{continuous:!0,mark:-1};this.addInput("data","array");this.addInput("mark","number");this.size=[300,200];this._last_buffer=null}function y(){this.properties={band:440,amplitude:1};this.addInput("freqs","array");this.addOutput("signal","number")}function v(){if(!v.default_code){var c=v.default_function.toString(),a=c.indexOf("{")+1,b=c.lastIndexOf("}");v.default_code=c.substr(a,b-a)}this.properties={code:v.default_code};c=q.getAudioContext();c.createScriptProcessor?this.audionode=c.createScriptProcessor(4096, -1,1):(console.warn("ScriptProcessorNode deprecated"),this.audionode=c.createGain());this.processCode();v._bypass_function||(v._bypass_function=this.audionode.onaudioprocess);this.addInput("in","audio");this.addOutput("out","audio")}function G(){this.audionode=q.getAudioContext().destination;this.addInput("in","audio")}var p=B.LiteGraph,q={};B.LGAudio=q;q.getAudioContext=function(){if(!this._audio_context){window.AudioContext=window.AudioContext||window.webkitAudioContext;if(!window.AudioContext)return console.error("AudioContext not supported by browser"), -null;this._audio_context=new AudioContext;this._audio_context.onmessage=function(c){console.log("msg",c)};this._audio_context.onended=function(c){console.log("ended",c)};this._audio_context.oncomplete=function(c){console.log("complete",c)}}return this._audio_context};q.connect=function(c,a){try{c.connect(a)}catch(b){console.warn("LGraphAudio:",b)}};q.disconnect=function(c,a){try{c.disconnect(a)}catch(b){console.warn("LGraphAudio:",b)}};q.changeAllAudiosConnections=function(c,a){if(c.inputs)for(var b= -0;b=this.size[0]&&(e=this.size[0]-1),c.strokeStyle="red",c.beginPath(),c.moveTo(e,d),c.lineTo(e,0),c.stroke())}};z.title="Visualization";z.desc="Audio Visualization";p.registerNodeType("audio/visualization",z);y.prototype.onExecute=function(){if(this._freqs=this.getInputData(0)){var c=this.properties.band,a=this.getInputData(1); -void 0!==a&&(c=a);a=q.getAudioContext().sampleRate/this._freqs.length;a=c/a*2;a>=this._freqs.length?a=this._freqs[this._freqs.length-1]:(c=a|0,a-=c,a=this._freqs[c]*(1-a)+this._freqs[c+1]*a);this.setOutputData(0,a/255*this.properties.amplitude)}};y.prototype.onGetInputs=function(){return[["band","number"]]};y.title="Signal";y.desc="extract the signal of some frequency";p.registerNodeType("audio/signal",y);v.prototype.onAdded=function(c){c.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess= -this._callback)};v["@code"]={widget:"code",type:"code"};v.prototype.onStart=function(){this.audionode.onaudioprocess=this._callback};v.prototype.onStop=function(){this.audionode.onaudioprocess=v._bypass_function};v.prototype.onPause=function(){this.audionode.onaudioprocess=v._bypass_function};v.prototype.onUnpause=function(){this.audionode.onaudioprocess=this._callback};v.prototype.onExecute=function(){};v.prototype.onRemoved=function(){this.audionode.onaudioprocess=v._bypass_function};v.prototype.processCode= -function(){try{this._script=new new Function("properties",this.properties.code)(this.properties),this._old_code=this.properties.code,this._callback=this._script.onaudioprocess}catch(c){console.error("Error in onaudioprocess code",c),this._callback=v._bypass_function,this.audionode.onaudioprocess=this._callback}};v.prototype.onPropertyChanged=function(c,a){"code"==c&&(this.properties.code=a,this.processCode(),this.graph&&this.graph.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback))}; -v.default_function=function(){this.onaudioprocess=function(c){var a=c.inputBuffer;c=c.outputBuffer;for(var b=0;b p && (p = Math.max(0, g + p)); + if (null == m || m > g) { + m = g; + } + m = Number(m); + 0 > m && (m = Math.max(0, g + m)); + for (p = Number(p || 0); p < m; p++) { + this[p] = c; + } + return this; + }; +}, "es6", "es3"); +$jscomp.SYMBOL_PREFIX = "jscomp_symbol_"; +$jscomp.initSymbol = function() { + $jscomp.initSymbol = function() { + }; + $jscomp.global.Symbol || ($jscomp.global.Symbol = $jscomp.Symbol); +}; +$jscomp.Symbol = function() { + var w = 0; + return function(c) { + return $jscomp.SYMBOL_PREFIX + (c || "") + w++; + }; +}(); +$jscomp.initSymbolIterator = function() { + $jscomp.initSymbol(); + var w = $jscomp.global.Symbol.iterator; + w || (w = $jscomp.global.Symbol.iterator = $jscomp.global.Symbol("iterator")); + "function" != typeof Array.prototype[w] && $jscomp.defineProperty(Array.prototype, w, {configurable:!0, writable:!0, value:function() { + return $jscomp.arrayIterator(this); + }}); + $jscomp.initSymbolIterator = function() { + }; +}; +$jscomp.arrayIterator = function(w) { + var c = 0; + return $jscomp.iteratorPrototype(function() { + return c < w.length ? {done:!1, value:w[c++]} : {done:!0}; + }); +}; +$jscomp.iteratorPrototype = function(w) { + $jscomp.initSymbolIterator(); + w = {next:w}; + w[$jscomp.global.Symbol.iterator] = function() { + return this; + }; + return w; +}; +$jscomp.iteratorFromArray = function(w, c) { + $jscomp.initSymbolIterator(); + w instanceof String && (w += ""); + var p = 0, m = {next:function() { + if (p < w.length) { + var g = p++; + return {value:c(g, w[g]), done:!1}; + } + m.next = function() { + return {done:!0, value:void 0}; + }; + return m.next(); + }}; + m[Symbol.iterator] = function() { + return m; + }; + return m; +}; +$jscomp.polyfill("Array.prototype.values", function(w) { + return w ? w : function() { + return $jscomp.iteratorFromArray(this, function(c, p) { + return p; + }); + }; +}, "es8", "es3"); +$jscomp.polyfill("Array.prototype.keys", function(w) { + return w ? w : function() { + return $jscomp.iteratorFromArray(this, function(c) { + return c; + }); + }; +}, "es6", "es3"); +$jscomp.owns = function(w, c) { + return Object.prototype.hasOwnProperty.call(w, c); +}; +$jscomp.polyfill("Object.values", function(w) { + return w ? w : function(c) { + var p = [], m; + for (m in c) { + $jscomp.owns(c, m) && p.push(c[m]); + } + return p; + }; +}, "es8", "es3"); +(function(w) { + function c(a) { + e.debug && console.log("Graph created"); + this.list_of_graphcanvas = null; + this.clear(); + a && this.configure(a); + } + function p(a, b, d, h, f, e) { + this.id = a; + this.type = b; + this.origin_id = d; + this.origin_slot = h; + this.target_id = f; + this.target_slot = e; + this._data = null; + this._pos = new Float32Array(2); + } + function m(a) { + this._ctor(a); + } + function g(a) { + this._ctor(a); + } + function u(a, b) { + this.offset = new Float32Array([0, 0]); + this.scale = 1; + this.max_scale = 10; + this.min_scale = 0.1; + this.onredraw = null; + this.enabled = !0; + this.last_mouse = [0, 0]; + this.element = null; + this.visible_area = new Float32Array(4); + a && (this.element = a, b || this.bindEvents(a)); + } + function l(a, b, d) { + d = d || {}; + this.background_image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; + a && a.constructor === String && (a = document.querySelector(a)); + this.ds = new u; + this.zoom_modify_alpha = !0; + this.title_text_font = "" + e.NODE_TEXT_SIZE + "px Arial"; + this.inner_text_font = "normal " + e.NODE_SUBTEXT_SIZE + "px Arial"; + this.node_title_color = e.NODE_TITLE_COLOR; + this.default_link_color = e.LINK_COLOR; + this.default_connection_color = {input_off:"#778", input_on:"#7F7", output_off:"#778", output_on:"#7F7"}; + this.highquality_render = !0; + this.use_gradients = !1; + this.editor_alpha = 1; + this.pause_rendering = !1; + this.clear_background = !0; + this.read_only = !1; + this.render_only_selected = !0; + this.live_mode = !1; + this.allow_searchbox = this.allow_interaction = this.allow_dragnodes = this.allow_dragcanvas = this.show_info = !0; + this.drag_mode = this.allow_reconnect_links = !1; + this.filter = this.dragging_rectangle = null; + this.always_render_background = !1; + this.render_canvas_border = this.render_shadows = !0; + this.render_connections_shadows = !1; + this.render_connections_border = !0; + this.render_connection_arrows = this.render_curved_connections = !1; + this.render_collapsed_slots = !0; + this.render_execution_order = !1; + this.render_link_tooltip = this.render_title_colored = !0; + this.links_render_mode = e.SPLINE_LINK; + this.canvas_mouse = [0, 0]; + this.onSelectionChange = this.onNodeMoved = this.onDrawLinkTooltip = this.onDrawOverlay = this.onDrawForeground = this.onDrawBackground = this.onMouse = this.onSearchBoxSelection = this.onSearchBox = null; + this.connections_width = 3; + this.round_radius = 8; + this.over_link_center = this.node_widget = this.current_node = null; + this.last_mouse_position = [0, 0]; + this.visible_area = this.ds.visible_area; + this.visible_links = []; + b && b.attachCanvas(this); + this.setCanvas(a); + this.clear(); + d.skip_render || this.startRendering(); + this.autoresize = d.autoresize; + } + function B(a, b) { + return Math.sqrt((b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1])); + } + function y(a, b, d, h, f, e) { + return d < a && d + f > a && h < b && h + e > b ? !0 : !1; + } + function v(a, b) { + var d = a[0] + a[2], h = a[1] + a[3], f = b[1] + b[3]; + return a[0] > b[0] + b[2] || a[1] > f || d < b[0] || h < b[1] ? !1 : !0; + } + function E(a, b) { + function d(a) { + var d = parseInt(e.style.top); + e.style.top = (d + a.deltaY * b.scroll_speed).toFixed() + "px"; + a.preventDefault(); + return !0; + } + this.options = b = b || {}; + var h = this; + b.parentMenu && (b.parentMenu.constructor !== this.constructor ? (console.error("parentMenu must be of class ContextMenu, ignoring it"), b.parentMenu = null) : (this.parentMenu = b.parentMenu, this.parentMenu.lock = !0, this.parentMenu.current_submenu = this)); + var f = null; + b.event && (f = b.event.constructor.name); + "MouseEvent" !== f && "CustomEvent" !== f && "PointerEvent" !== f && (console.error("Event passed to ContextMenu is not of type MouseEvent or CustomEvent. Ignoring it."), b.event = null); + var e = document.createElement("div"); + e.className = "litegraph litecontextmenu litemenubar-panel"; + b.className && (e.className += " " + b.className); + e.style.minWidth = 100; + e.style.minHeight = 100; + e.style.pointerEvents = "none"; + setTimeout(function() { + e.style.pointerEvents = "auto"; + }, 100); + e.addEventListener("mouseup", function(a) { + a.preventDefault(); + return !0; + }, !0); + e.addEventListener("contextmenu", function(a) { + if (2 != a.button) { + return !1; + } + a.preventDefault(); + return !1; + }, !0); + e.addEventListener("mousedown", function(a) { + if (2 == a.button) { + return h.close(), a.preventDefault(), !0; + } + }, !0); + b.scroll_speed || (b.scroll_speed = 0.1); + e.addEventListener("wheel", d, !0); + e.addEventListener("mousewheel", d, !0); + this.root = e; + b.title && (f = document.createElement("div"), f.className = "litemenu-title", f.innerHTML = b.title, e.appendChild(f)); + f = 0; + for (var c in a) { + var k = a.constructor == Array ? a[c] : c; + null != k && k.constructor !== String && (k = void 0 === k.content ? String(k) : k.content); + this.addItem(k, a[c], b); + f++; + } + e.addEventListener("mouseleave", function(a) { + h.lock || (e.closing_timer && clearTimeout(e.closing_timer), e.closing_timer = setTimeout(h.close.bind(h, a), 500)); + }); + e.addEventListener("mouseenter", function(a) { + e.closing_timer && clearTimeout(e.closing_timer); + }); + a = document; + b.event && (a = b.event.target.ownerDocument); + a || (a = document); + a.fullscreenElement ? a.fullscreenElement.appendChild(e) : a.body.appendChild(e); + c = b.left || 0; + a = b.top || 0; + b.event && (c = b.event.clientX - 10, a = b.event.clientY - 10, b.title && (a -= 20), b.parentMenu && (c = b.parentMenu.root.getBoundingClientRect(), c = c.left + c.width), f = document.body.getBoundingClientRect(), k = e.getBoundingClientRect(), c > f.width - k.width - 10 && (c = f.width - k.width - 10), a > f.height - k.height - 10 && (a = f.height - k.height - 10)); + e.style.left = c + "px"; + e.style.top = a + "px"; + b.scale && (e.style.transform = "scale(" + b.scale + ")"); + } + function z(a) { + this.points = a; + this.nearest = this.selected = -1; + this.size = null; + this.must_update = !0; + this.margin = 5; + } + var e = w.LiteGraph = {VERSION:0.4, CANVAS_GRID_SIZE:10, NODE_TITLE_HEIGHT:30, NODE_TITLE_TEXT_Y:20, NODE_SLOT_HEIGHT:20, NODE_WIDGET_HEIGHT:20, NODE_WIDTH:140, NODE_MIN_WIDTH:50, NODE_COLLAPSED_RADIUS:10, NODE_COLLAPSED_WIDTH:80, NODE_TITLE_COLOR:"#999", NODE_TEXT_SIZE:14, NODE_TEXT_COLOR:"#AAA", NODE_SUBTEXT_SIZE:12, NODE_DEFAULT_COLOR:"#333", NODE_DEFAULT_BGCOLOR:"#353535", NODE_DEFAULT_BOXCOLOR:"#666", NODE_DEFAULT_SHAPE:"box", DEFAULT_SHADOW_COLOR:"rgba(0,0,0,0.5)", DEFAULT_GROUP_FONT:24, + WIDGET_BGCOLOR:"#222", WIDGET_OUTLINE_COLOR:"#666", WIDGET_TEXT_COLOR:"#DDD", WIDGET_SECONDARY_TEXT_COLOR:"#999", LINK_COLOR:"#9A9", EVENT_LINK_COLOR:"#A86", CONNECTING_LINK_COLOR:"#AFA", MAX_NUMBER_OF_NODES:1000, DEFAULT_POSITION:[100, 100], VALID_SHAPES:["default", "box", "round", "card"], BOX_SHAPE:1, ROUND_SHAPE:2, CIRCLE_SHAPE:3, CARD_SHAPE:4, ARROW_SHAPE:5, INPUT:1, OUTPUT:2, EVENT:-1, ACTION:-1, ALWAYS:0, ON_EVENT:1, NEVER:2, ON_TRIGGER:3, UP:1, DOWN:2, LEFT:3, RIGHT:4, CENTER:5, STRAIGHT_LINK:0, + LINEAR_LINK:1, SPLINE_LINK:2, NORMAL_TITLE:0, NO_TITLE:1, TRANSPARENT_TITLE:2, AUTOHIDE_TITLE:3, proxy:null, node_images_path:"", debug:!1, catch_exceptions:!0, throw_errors:!0, allow_scripts:!1, registered_node_types:{}, node_types_by_file_extension:{}, Nodes:{}, searchbox_extras:{}, registerNodeType:function(a, b) { + if (!b.prototype) { + throw "Cannot register a simple object, it must be a class with a prototype"; + } + b.type = a; + e.debug && console.log("Node registered: " + a); + a.split("/"); + var d = b.name, h = a.lastIndexOf("/"); + b.category = a.substr(0, h); + b.title || (b.title = d); + if (b.prototype) { + for (var f in m.prototype) { + b.prototype[f] || (b.prototype[f] = m.prototype[f]); + } + } + if (h = this.registered_node_types[a]) { + console.log("replacing node type: " + a); + } else { + if (Object.hasOwnProperty(b.prototype, "shape") || Object.defineProperty(b.prototype, "shape", {set:function(a) { + switch(a) { + case "default": + delete this._shape; + break; + case "box": + this._shape = e.BOX_SHAPE; + break; + case "round": + this._shape = e.ROUND_SHAPE; + break; + case "circle": + this._shape = e.CIRCLE_SHAPE; + break; + case "card": + this._shape = e.CARD_SHAPE; + break; + default: + this._shape = a; + } + }, get:function(a) { + return this._shape; + }, enumerable:!0, configurable:!0}), b.prototype.onPropertyChange && console.warn("LiteGraph node class " + a + " has onPropertyChange method, it must be called onPropertyChanged with d at the end"), b.supported_extensions) { + for (f in b.supported_extensions) { + var x = b.supported_extensions[f]; + x && x.constructor === String && (this.node_types_by_file_extension[x.toLowerCase()] = b); + } + } + } + this.registered_node_types[a] = b; + b.constructor.name && (this.Nodes[d] = b); + if (e.onNodeTypeRegistered) { + e.onNodeTypeRegistered(a, b); + } + if (h && e.onNodeTypeReplaced) { + e.onNodeTypeReplaced(a, b, h); + } + }, unregisterNodeType:function(a) { + var b = a.constructor === String ? this.registered_node_types[a] : a; + if (!b) { + throw "node type not found: " + a; + } + delete this.registered_node_types[b.type]; + b.constructor.name && delete this.Nodes[b.constructor.name]; + }, wrapFunctionAsNode:function(a, b, d, h, f) { + for (var x = Array(b.length), c = "", k = e.getParameterNames(b), n = 0; n < k.length; ++n) { + c += "this.addInput('" + k[n] + "'," + (d && d[n] ? "'" + d[n] + "'" : "0") + ");\n"; + } + c += "this.addOutput('out'," + (h ? "'" + h + "'" : 0) + ");\n"; + f && (c += "this.properties = " + JSON.stringify(f) + ";\n"); + d = Function(c); + d.title = a.split("/").pop(); + d.desc = "Generated from " + b.name; + d.prototype.onExecute = function() { + for (var a = 0; a < x.length; ++a) { + x[a] = this.getInputData(a); + } + a = b.apply(this, x); + this.setOutputData(0, a); + }; + this.registerNodeType(a, d); + }, addNodeMethod:function(a, b) { + m.prototype[a] = b; + for (var d in this.registered_node_types) { + var h = this.registered_node_types[d]; + h.prototype[a] && (h.prototype["_" + a] = h.prototype[a]); + h.prototype[a] = b; + } + }, createNode:function(a, b, d) { + var h = this.registered_node_types[a]; + if (!h) { + return e.debug && console.log('GraphNode type "' + a + '" not registered.'), null; + } + b = b || h.title || a; + var f = null; + if (e.catch_exceptions) { + try { + f = new h(b); + } catch (H) { + return console.error(H), null; + } + } else { + f = new h(b); + } + f.type = a; + !f.title && b && (f.title = b); + f.properties || (f.properties = {}); + f.properties_info || (f.properties_info = []); + f.flags || (f.flags = {}); + f.size || (f.size = f.computeSize()); + f.pos || (f.pos = e.DEFAULT_POSITION.concat()); + f.mode || (f.mode = e.ALWAYS); + if (d) { + for (var x in d) { + f[x] = d[x]; + } + } + return f; + }, getNodeType:function(a) { + return this.registered_node_types[a]; + }, getNodeTypesInCategory:function(a, b) { + var d = [], h; + for (h in this.registered_node_types) { + var f = this.registered_node_types[h]; + b && f.filter && f.filter != b || ("" == a ? null == f.category && d.push(f) : f.category == a && d.push(f)); + } + return d; + }, getNodeTypesCategories:function(a) { + var b = {"":1}, d; + for (d in this.registered_node_types) { + var h = this.registered_node_types[d]; + !h.category || h.skip_list || a && h.filter != a || (b[h.category] = 1); + } + a = []; + for (d in b) { + a.push(d); + } + return a; + }, reloadNodes:function(a) { + var b = document.getElementsByTagName("script"), d = [], h; + for (h in b) { + d.push(b[h]); + } + b = document.getElementsByTagName("head")[0]; + a = document.location.href + a; + for (h in d) { + var f = d[h].src; + if (f && f.substr(0, a.length) == a) { + try { + e.debug && console.log("Reloading: " + f); + var x = document.createElement("script"); + x.type = "text/javascript"; + x.src = f; + b.appendChild(x); + b.removeChild(d[h]); + } catch (H) { + if (e.throw_errors) { + throw H; + } + e.debug && console.log("Error while reloading " + f); + } + } + } + e.debug && console.log("Nodes reloaded"); + }, cloneObject:function(a, b) { + if (null == a) { + return null; + } + a = JSON.parse(JSON.stringify(a)); + if (!b) { + return a; + } + for (var d in a) { + b[d] = a[d]; + } + return b; + }, isValidConnection:function(a, b) { + if (!a || !b || a == b || a == e.EVENT && b == e.ACTION) { + return !0; + } + a = String(a); + b = String(b); + a = a.toLowerCase(); + b = b.toLowerCase(); + if (-1 == a.indexOf(",") && -1 == b.indexOf(",")) { + return a == b; + } + a = a.split(","); + b = b.split(","); + for (var d = 0; d < a.length; ++d) { + for (var h = 0; h < b.length; ++h) { + if (a[d] == b[h]) { + return !0; + } + } + } + return !1; + }, registerSearchboxExtra:function(a, b, d) { + this.searchbox_extras[b.toLowerCase()] = {type:a, desc:b, data:d}; + }, fetchFile:function(a, b, d, h) { + if (!a) { + return null; + } + b = b || "text"; + if (a.constructor === String) { + return "http" == a.substr(0, 4) && e.proxy && (a = e.proxy + a.substr(a.indexOf(":") + 3)), fetch(a).then(function(a) { + if (!a.ok) { + throw Error("File not found"); + } + if ("arraybuffer" == b) { + return a.arrayBuffer(); + } + if ("text" == b || "string" == b) { + return a.text(); + } + if ("json" == b) { + return a.json(); + } + if ("blob" == b) { + return a.blob(); + } + }).then(function(a) { + d && d(a); + }).catch(function(b) { + console.error("error fetching file:", a); + h && h(b); + }); + } + if (a.constructor === File || a.constructor === Blob) { + var f = new FileReader; + f.onload = function(a) { + a = a.target.result; + "json" == b && (a = JSON.parse(a)); + d && d(a); + }; + if ("arraybuffer" == b) { + return f.readAsArrayBuffer(a); + } + if ("text" == b || "json" == b) { + return f.readAsText(a); + } + if ("blob" == b) { + return f.readAsBinaryString(a); + } + } + return null; + }}; + e.getTime = "undefined" != typeof performance ? performance.now.bind(performance) : "undefined" != typeof Date && Date.now ? Date.now.bind(Date) : "undefined" != typeof process ? function() { + var a = process.hrtime(); + return 0.001 * a[0] + 1e-6 * a[1]; + } : function() { + return (new Date).getTime(); + }; + w.LGraph = e.LGraph = c; + c.supported_types = ["number", "string", "boolean"]; + c.prototype.getSupportedTypes = function() { + return this.supported_types || c.supported_types; + }; + c.STATUS_STOPPED = 1; + c.STATUS_RUNNING = 2; + c.prototype.clear = function() { + this.stop(); + this.status = c.STATUS_STOPPED; + this.last_link_id = this.last_node_id = 0; + this._version = -1; + if (this._nodes) { + for (var a = 0; a < this._nodes.length; ++a) { + var b = this._nodes[a]; + if (b.onRemoved) { + b.onRemoved(); + } + } + } + this._nodes = []; + this._nodes_by_id = {}; + this._nodes_in_order = []; + this._nodes_executable = null; + this._groups = []; + this.links = {}; + this.iteration = 0; + this.config = {}; + this.vars = {}; + this.fixedtime = this.runningtime = this.globaltime = 0; + this.elapsed_time = this.fixedtime_lapse = 0.01; + this.starttime = this.last_update_time = 0; + this.catch_errors = !0; + this.inputs = {}; + this.outputs = {}; + this.change(); + this.sendActionToCanvas("clear"); + }; + c.prototype.attachCanvas = function(a) { + if (a.constructor != l) { + throw "attachCanvas expects a LGraphCanvas instance"; + } + a.graph && a.graph != this && a.graph.detachCanvas(a); + a.graph = this; + this.list_of_graphcanvas || (this.list_of_graphcanvas = []); + this.list_of_graphcanvas.push(a); + }; + c.prototype.detachCanvas = function(a) { + if (this.list_of_graphcanvas) { + var b = this.list_of_graphcanvas.indexOf(a); + -1 != b && (a.graph = null, this.list_of_graphcanvas.splice(b, 1)); + } + }; + c.prototype.start = function(a) { + if (this.status != c.STATUS_RUNNING) { + this.status = c.STATUS_RUNNING; + if (this.onPlayEvent) { + this.onPlayEvent(); + } + this.sendEventToAllNodes("onStart"); + this.last_update_time = this.starttime = e.getTime(); + a = a || 0; + var b = this; + if (0 == a && "undefined" != typeof window && window.requestAnimationFrame) { + var d = function() { + if (-1 == b.execution_timer_id) { + window.requestAnimationFrame(d); + if (b.onBeforeStep) { + b.onBeforeStep(); + } + b.runStep(1, !this.catch_errors); + if (b.onAfterStep) { + b.onAfterStep(); + } + } + }; + this.execution_timer_id = -1; + d(); + } else { + this.execution_timer_id = setInterval(function() { + if (b.onBeforeStep) { + b.onBeforeStep(); + } + b.runStep(1, !this.catch_errors); + if (b.onAfterStep) { + b.onAfterStep(); + } + }, a); + } + } + }; + c.prototype.stop = function() { + if (this.status != c.STATUS_STOPPED) { + this.status = c.STATUS_STOPPED; + if (this.onStopEvent) { + this.onStopEvent(); + } + null != this.execution_timer_id && (-1 != this.execution_timer_id && clearInterval(this.execution_timer_id), this.execution_timer_id = null); + this.sendEventToAllNodes("onStop"); + } + }; + c.prototype.runStep = function(a, b, d) { + a = a || 1; + var h = e.getTime(); + this.globaltime = 0.001 * (h - this.starttime); + var f = this._nodes_executable ? this._nodes_executable : this._nodes; + if (f) { + d = d || f.length; + if (b) { + for (var x = 0; x < a; x++) { + for (var c = 0; c < d; ++c) { + var k = f[c]; + if (k.mode == e.ALWAYS && k.onExecute) { + k.onExecute(); + } + } + this.fixedtime += this.fixedtime_lapse; + if (this.onExecuteStep) { + this.onExecuteStep(); + } + } + if (this.onAfterExecute) { + this.onAfterExecute(); + } + } else { + try { + for (x = 0; x < a; x++) { + for (c = 0; c < d; ++c) { + if (k = f[c], k.mode == e.ALWAYS && k.onExecute) { + k.onExecute(); + } + } + this.fixedtime += this.fixedtime_lapse; + if (this.onExecuteStep) { + this.onExecuteStep(); + } + } + if (this.onAfterExecute) { + this.onAfterExecute(); + } + this.errors_in_execution = !1; + } catch (J) { + this.errors_in_execution = !0; + if (e.throw_errors) { + throw J; + } + e.debug && console.log("Error during execution: " + J); + this.stop(); + } + } + a = e.getTime(); + h = a - h; + 0 == h && (h = 1); + this.execution_time = 0.001 * h; + this.globaltime += 0.001 * h; + this.iteration += 1; + this.elapsed_time = 0.001 * (a - this.last_update_time); + this.last_update_time = a; + } + }; + c.prototype.updateExecutionOrder = function() { + this._nodes_in_order = this.computeExecutionOrder(!1); + this._nodes_executable = []; + for (var a = 0; a < this._nodes_in_order.length; ++a) { + this._nodes_in_order[a].onExecute && this._nodes_executable.push(this._nodes_in_order[a]); + } + }; + c.prototype.computeExecutionOrder = function(a, b) { + for (var d = [], h = [], f = {}, x = {}, c = {}, k = 0, n = this._nodes.length; k < n; ++k) { + var q = this._nodes[k]; + if (!a || q.onExecute) { + f[q.id] = q; + var l = 0; + if (q.inputs) { + for (var r = 0, g = q.inputs.length; r < g; r++) { + q.inputs[r] && null != q.inputs[r].link && (l += 1); + } + } + 0 == l ? (h.push(q), b && (q._level = 1)) : (b && (q._level = 0), c[q.id] = l); + } + } + for (; 0 != h.length;) { + if (q = h.shift(), d.push(q), delete f[q.id], q.outputs) { + for (k = 0; k < q.outputs.length; k++) { + if (a = q.outputs[k], null != a && null != a.links && 0 != a.links.length) { + for (r = 0; r < a.links.length; r++) { + (n = this.links[a.links[r]]) && !x[n.id] && (l = this.getNodeById(n.target_id), null == l ? x[n.id] = !0 : (b && (!l._level || l._level <= q._level) && (l._level = q._level + 1), x[n.id] = !0, --c[l.id], 0 == c[l.id] && h.push(l))); + } + } + } + } + } + for (k in f) { + d.push(f[k]); + } + d.length != this._nodes.length && e.debug && console.warn("something went wrong, nodes missing"); + n = d.length; + for (k = 0; k < n; ++k) { + d[k].order = k; + } + d = d.sort(function(a, b) { + var d = a.constructor.priority || a.priority || 0, f = b.constructor.priority || b.priority || 0; + return d == f ? a.order - b.order : d - f; + }); + for (k = 0; k < n; ++k) { + d[k].order = k; + } + return d; + }; + c.prototype.getAncestors = function(a) { + for (var b = [], d = [a], h = {}; d.length;) { + var f = d.shift(); + if (f.inputs) { + h[f.id] || f == a || (h[f.id] = !0, b.push(f)); + for (var e = 0; e < f.inputs.length; ++e) { + var c = f.getInputNode(e); + c && -1 == b.indexOf(c) && d.push(c); + } + } + } + b.sort(function(a, b) { + return a.order - b.order; + }); + return b; + }; + c.prototype.arrange = function(a) { + a = a || 100; + for (var b = this.computeExecutionOrder(!1, !0), d = [], h = 0; h < b.length; ++h) { + var f = b[h], x = f._level || 1; + d[x] || (d[x] = []); + d[x].push(f); + } + b = a; + for (h = 0; h < d.length; ++h) { + if (x = d[h]) { + for (var c = 100, k = a + e.NODE_TITLE_HEIGHT, n = 0; n < x.length; ++n) { + f = x[n], f.pos[0] = b, f.pos[1] = k, f.size[0] > c && (c = f.size[0]), k += f.size[1] + a + e.NODE_TITLE_HEIGHT; + } + b += c + a; + } + } + this.setDirtyCanvas(!0, !0); + }; + c.prototype.getTime = function() { + return this.globaltime; + }; + c.prototype.getFixedTime = function() { + return this.fixedtime; + }; + c.prototype.getElapsedTime = function() { + return this.elapsed_time; + }; + c.prototype.sendEventToAllNodes = function(a, b, d) { + d = d || e.ALWAYS; + var h = this._nodes_in_order ? this._nodes_in_order : this._nodes; + if (h) { + for (var f = 0, x = h.length; f < x; ++f) { + var c = h[f]; + if (c.constructor === e.Subgraph && "onExecute" != a) { + c.mode == d && c.sendEventToAllNodes(a, b, d); + } else { + if (c[a] && c.mode == d) { + if (void 0 === b) { + c[a](); + } else { + if (b && b.constructor === Array) { + c[a].apply(c, b); + } else { + c[a](b); + } + } + } + } + } + } + }; + c.prototype.sendActionToCanvas = function(a, b) { + if (this.list_of_graphcanvas) { + for (var d = 0; d < this.list_of_graphcanvas.length; ++d) { + var h = this.list_of_graphcanvas[d]; + h[a] && h[a].apply(h, b); + } + } + }; + c.prototype.add = function(a, b) { + if (a) { + if (a.constructor === g) { + this._groups.push(a), this.setDirtyCanvas(!0), this.change(), a.graph = this, this._version++; + } else { + -1 != a.id && null != this._nodes_by_id[a.id] && (console.warn("LiteGraph: there is already a node with this ID, changing it"), a.id = ++this.last_node_id); + if (this._nodes.length >= e.MAX_NUMBER_OF_NODES) { + throw "LiteGraph: max number of nodes in a graph reached"; + } + null == a.id || -1 == a.id ? a.id = ++this.last_node_id : this.last_node_id < a.id && (this.last_node_id = a.id); + a.graph = this; + this._version++; + this._nodes.push(a); + this._nodes_by_id[a.id] = a; + if (a.onAdded) { + a.onAdded(this); + } + this.config.align_to_grid && a.alignToGrid(); + b || this.updateExecutionOrder(); + if (this.onNodeAdded) { + this.onNodeAdded(a); + } + this.setDirtyCanvas(!0); + this.change(); + return a; + } + } + }; + c.prototype.remove = function(a) { + if (a.constructor === e.LGraphGroup) { + var b = this._groups.indexOf(a); + -1 != b && this._groups.splice(b, 1); + a.graph = null; + this._version++; + this.setDirtyCanvas(!0, !0); + this.change(); + } else { + if (null != this._nodes_by_id[a.id] && !a.ignore_remove) { + if (a.inputs) { + for (b = 0; b < a.inputs.length; b++) { + var d = a.inputs[b]; + null != d.link && a.disconnectInput(b); + } + } + if (a.outputs) { + for (b = 0; b < a.outputs.length; b++) { + d = a.outputs[b], null != d.links && d.links.length && a.disconnectOutput(b); + } + } + if (a.onRemoved) { + a.onRemoved(); + } + a.graph = null; + this._version++; + if (this.list_of_graphcanvas) { + for (b = 0; b < this.list_of_graphcanvas.length; ++b) { + d = this.list_of_graphcanvas[b], d.selected_nodes[a.id] && delete d.selected_nodes[a.id], d.node_dragged == a && (d.node_dragged = null); + } + } + b = this._nodes.indexOf(a); + -1 != b && this._nodes.splice(b, 1); + delete this._nodes_by_id[a.id]; + if (this.onNodeRemoved) { + this.onNodeRemoved(a); + } + this.setDirtyCanvas(!0, !0); + this.change(); + this.updateExecutionOrder(); + } + } + }; + c.prototype.getNodeById = function(a) { + return null == a ? null : this._nodes_by_id[a]; + }; + c.prototype.findNodesByClass = function(a, b) { + b = b || []; + for (var d = b.length = 0, h = this._nodes.length; d < h; ++d) { + this._nodes[d].constructor === a && b.push(this._nodes[d]); + } + return b; + }; + c.prototype.findNodesByType = function(a, b) { + a = a.toLowerCase(); + b = b || []; + for (var d = b.length = 0, h = this._nodes.length; d < h; ++d) { + this._nodes[d].type.toLowerCase() == a && b.push(this._nodes[d]); + } + return b; + }; + c.prototype.findNodeByTitle = function(a) { + for (var b = 0, d = this._nodes.length; b < d; ++b) { + if (this._nodes[b].title == a) { + return this._nodes[b]; + } + } + return null; + }; + c.prototype.findNodesByTitle = function(a) { + for (var b = [], d = 0, h = this._nodes.length; d < h; ++d) { + this._nodes[d].title == a && b.push(this._nodes[d]); + } + return b; + }; + c.prototype.getNodeOnPos = function(a, b, d, h) { + d = d || this._nodes; + for (var f = d.length - 1; 0 <= f; f--) { + var e = d[f]; + if (e.isPointInside(a, b, h)) { + return e; + } + } + return null; + }; + c.prototype.getGroupOnPos = function(a, b) { + for (var d = this._groups.length - 1; 0 <= d; d--) { + var h = this._groups[d]; + if (h.isPointInside(a, b, 2, !0)) { + return h; + } + } + return null; + }; + c.prototype.checkNodeTypes = function() { + for (var a = 0; a < this._nodes.length; a++) { + var b = this._nodes[a]; + if (b.constructor != e.registered_node_types[b.type]) { + console.log("node being replaced by newer version: " + b.type); + var d = e.createNode(b.type); + this._nodes[a] = d; + d.configure(b.serialize()); + d.graph = this; + this._nodes_by_id[d.id] = d; + b.inputs && (d.inputs = b.inputs.concat()); + b.outputs && (d.outputs = b.outputs.concat()); + } + } + this.updateExecutionOrder(); + }; + c.prototype.onAction = function(a, b) { + this._input_nodes = this.findNodesByClass(e.GraphInput, this._input_nodes); + for (var d = 0; d < this._input_nodes.length; ++d) { + var h = this._input_nodes[d]; + if (h.properties.name == a) { + h.onAction(a, b); + break; + } + } + }; + c.prototype.trigger = function(a, b) { + if (this.onTrigger) { + this.onTrigger(a, b); + } + }; + c.prototype.addInput = function(a, b, d) { + if (!this.inputs[a]) { + this.inputs[a] = {name:a, type:b, value:d}; + this._version++; + if (this.onInputAdded) { + this.onInputAdded(a, b); + } + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + } + }; + c.prototype.setInputData = function(a, b) { + if (a = this.inputs[a]) { + a.value = b; + } + }; + c.prototype.getInputData = function(a) { + return (a = this.inputs[a]) ? a.value : null; + }; + c.prototype.renameInput = function(a, b) { + if (b != a) { + if (!this.inputs[a]) { + return !1; + } + if (this.inputs[b]) { + return console.error("there is already one input with that name"), !1; + } + this.inputs[b] = this.inputs[a]; + delete this.inputs[a]; + this._version++; + if (this.onInputRenamed) { + this.onInputRenamed(a, b); + } + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + } + }; + c.prototype.changeInputType = function(a, b) { + if (!this.inputs[a]) { + return !1; + } + if (!this.inputs[a].type || String(this.inputs[a].type).toLowerCase() != String(b).toLowerCase()) { + if (this.inputs[a].type = b, this._version++, this.onInputTypeChanged) { + this.onInputTypeChanged(a, b); + } + } + }; + c.prototype.removeInput = function(a) { + if (!this.inputs[a]) { + return !1; + } + delete this.inputs[a]; + this._version++; + if (this.onInputRemoved) { + this.onInputRemoved(a); + } + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + return !0; + }; + c.prototype.addOutput = function(a, b, d) { + this.outputs[a] = {name:a, type:b, value:d}; + this._version++; + if (this.onOutputAdded) { + this.onOutputAdded(a, b); + } + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + }; + c.prototype.setOutputData = function(a, b) { + if (a = this.outputs[a]) { + a.value = b; + } + }; + c.prototype.getOutputData = function(a) { + return (a = this.outputs[a]) ? a.value : null; + }; + c.prototype.renameOutput = function(a, b) { + if (!this.outputs[a]) { + return !1; + } + if (this.outputs[b]) { + return console.error("there is already one output with that name"), !1; + } + this.outputs[b] = this.outputs[a]; + delete this.outputs[a]; + this._version++; + if (this.onOutputRenamed) { + this.onOutputRenamed(a, b); + } + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + }; + c.prototype.changeOutputType = function(a, b) { + if (!this.outputs[a]) { + return !1; + } + if (!this.outputs[a].type || String(this.outputs[a].type).toLowerCase() != String(b).toLowerCase()) { + if (this.outputs[a].type = b, this._version++, this.onOutputTypeChanged) { + this.onOutputTypeChanged(a, b); + } + } + }; + c.prototype.removeOutput = function(a) { + if (!this.outputs[a]) { + return !1; + } + delete this.outputs[a]; + this._version++; + if (this.onOutputRemoved) { + this.onOutputRemoved(a); + } + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + return !0; + }; + c.prototype.triggerInput = function(a, b) { + a = this.findNodesByTitle(a); + for (var d = 0; d < a.length; ++d) { + a[d].onTrigger(b); + } + }; + c.prototype.setCallback = function(a, b) { + a = this.findNodesByTitle(a); + for (var d = 0; d < a.length; ++d) { + a[d].setTrigger(b); + } + }; + c.prototype.connectionChange = function(a, b) { + this.updateExecutionOrder(); + if (this.onConnectionChange) { + this.onConnectionChange(a); + } + this._version++; + this.sendActionToCanvas("onConnectionChange"); + }; + c.prototype.isLive = function() { + if (!this.list_of_graphcanvas) { + return !1; + } + for (var a = 0; a < this.list_of_graphcanvas.length; ++a) { + if (this.list_of_graphcanvas[a].live_mode) { + return !0; + } + } + return !1; + }; + c.prototype.clearTriggeredSlots = function() { + for (var a in this.links) { + var b = this.links[a]; + b && b._last_time && (b._last_time = 0); + } + }; + c.prototype.change = function() { + e.debug && console.log("Graph changed"); + this.sendActionToCanvas("setDirty", [!0, !0]); + if (this.on_change) { + this.on_change(this); + } + }; + c.prototype.setDirtyCanvas = function(a, b) { + this.sendActionToCanvas("setDirty", [a, b]); + }; + c.prototype.removeLink = function(a) { + if (a = this.links[a]) { + var b = this.getNodeById(a.target_id); + b && b.disconnectInput(a.target_slot); + } + }; + c.prototype.serialize = function() { + for (var a = [], b = 0, d = this._nodes.length; b < d; ++b) { + a.push(this._nodes[b].serialize()); + } + d = []; + for (b in this.links) { + var h = this.links[b]; + if (!h.serialize) { + console.warn("weird LLink bug, link info is not a LLink but a regular object"); + var f = new p; + for (b in h) { + f[b] = h[b]; + } + h = this.links[b] = f; + } + d.push(h.serialize()); + } + h = []; + for (b = 0; b < this._groups.length; ++b) { + h.push(this._groups[b].serialize()); + } + return {last_node_id:this.last_node_id, last_link_id:this.last_link_id, nodes:a, links:d, groups:h, config:this.config, version:e.VERSION}; + }; + c.prototype.configure = function(a, b) { + if (a) { + b || this.clear(); + b = a.nodes; + if (a.links && a.links.constructor === Array) { + for (var d = [], h = 0; h < a.links.length; ++h) { + var f = a.links[h]; + if (f) { + var c = new p; + c.configure(f); + d[c.id] = c; + } else { + console.warn("serialized graph link data contains errors, skipping."); + } + } + a.links = d; + } + for (h in a) { + "nodes" != h && "groups" != h && (this[h] = a[h]); + } + d = !1; + this._nodes = []; + if (b) { + h = 0; + for (f = b.length; h < f; ++h) { + c = b[h]; + var k = e.createNode(c.type, c.title); + k || (e.debug && console.log("Node not found or has errors: " + c.type), k = new m, k.last_serialization = c, d = k.has_errors = !0); + k.id = c.id; + this.add(k, !0); + } + h = 0; + for (f = b.length; h < f; ++h) { + c = b[h], (k = this.getNodeById(c.id)) && k.configure(c); + } + } + this._groups.length = 0; + if (a.groups) { + for (h = 0; h < a.groups.length; ++h) { + b = new e.LGraphGroup, b.configure(a.groups[h]), this.add(b); + } + } + this.updateExecutionOrder(); + this._version++; + this.setDirtyCanvas(!0, !0); + return d; + } + }; + c.prototype.load = function(a) { + var b = this, d = new XMLHttpRequest; + d.open("GET", a, !0); + d.send(null); + d.onload = function(a) { + 200 !== d.status ? console.error("Error loading graph:", d.status, d.response) : (a = JSON.parse(d.response), b.configure(a)); + }; + d.onerror = function(a) { + console.error("Error loading graph:", a); + }; + }; + c.prototype.onNodeTrace = function(a, b, d) { + }; + p.prototype.configure = function(a) { + a.constructor === Array ? (this.id = a[0], this.origin_id = a[1], this.origin_slot = a[2], this.target_id = a[3], this.target_slot = a[4], this.type = a[5]) : (this.id = a.id, this.type = a.type, this.origin_id = a.origin_id, this.origin_slot = a.origin_slot, this.target_id = a.target_id, this.target_slot = a.target_slot); + }; + p.prototype.serialize = function() { + return [this.id, this.origin_id, this.origin_slot, this.target_id, this.target_slot, this.type]; + }; + e.LLink = p; + w.LGraphNode = e.LGraphNode = m; + m.prototype._ctor = function(a) { + this.title = a || "Unnamed"; + this.size = [e.NODE_WIDTH, 60]; + this.graph = null; + this._pos = new Float32Array(10, 10); + Object.defineProperty(this, "pos", {set:function(a) { + !a || 2 > a.length || (this._pos[0] = a[0], this._pos[1] = a[1]); + }, get:function() { + return this._pos; + }, enumerable:!0}); + this.id = -1; + this.type = null; + this.inputs = []; + this.outputs = []; + this.connections = []; + this.properties = {}; + this.properties_info = []; + this.flags = {}; + }; + m.prototype.configure = function(a) { + this.graph && this.graph._version++; + for (var b in a) { + if ("properties" == b) { + for (var d in a.properties) { + if (this.properties[d] = a.properties[d], this.onPropertyChanged) { + this.onPropertyChanged(d, a.properties[d]); + } + } + } else { + null != a[b] && ("object" == typeof a[b] ? this[b] && this[b].configure ? this[b].configure(a[b]) : this[b] = e.cloneObject(a[b], this[b]) : this[b] = a[b]); + } + } + a.title || (this.title = this.constructor.title); + if (this.onConnectionsChange) { + if (this.inputs) { + for (d = 0; d < this.inputs.length; ++d) { + b = this.inputs[d]; + var h = this.graph ? this.graph.links[b.link] : null; + this.onConnectionsChange(e.INPUT, d, !0, h, b); + } + } + if (this.outputs) { + for (d = 0; d < this.outputs.length; ++d) { + var f = this.outputs[d]; + if (f.links) { + for (b = 0; b < f.links.length; ++b) { + h = this.graph ? this.graph.links[f.links[b]] : null, this.onConnectionsChange(e.OUTPUT, d, !0, h, f); + } + } + } + } + } + if (this.widgets) { + for (d = 0; d < this.widgets.length; ++d) { + (b = this.widgets[d]) && b.options && b.options.property && this.properties[b.options.property] && (b.value = JSON.parse(JSON.stringify(this.properties[b.options.property]))); + } + if (a.widgets_values) { + for (d = 0; d < a.widgets_values.length; ++d) { + this.widgets[d] && (this.widgets[d].value = a.widgets_values[d]); + } + } + } + if (this.onConfigure) { + this.onConfigure(a); + } + }; + m.prototype.serialize = function() { + var a = {id:this.id, type:this.type, pos:this.pos, size:this.size, flags:e.cloneObject(this.flags), order:this.order, mode:this.mode}; + if (this.constructor === m && this.last_serialization) { + return this.last_serialization; + } + this.inputs && (a.inputs = this.inputs); + if (this.outputs) { + for (var b = 0; b < this.outputs.length; b++) { + delete this.outputs[b]._data; + } + a.outputs = this.outputs; + } + this.title && this.title != this.constructor.title && (a.title = this.title); + this.properties && (a.properties = e.cloneObject(this.properties)); + if (this.widgets && this.serialize_widgets) { + for (a.widgets_values = [], b = 0; b < this.widgets.length; ++b) { + a.widgets_values[b] = this.widgets[b] ? this.widgets[b].value : null; + } + } + a.type || (a.type = this.constructor.type); + this.color && (a.color = this.color); + this.bgcolor && (a.bgcolor = this.bgcolor); + this.boxcolor && (a.boxcolor = this.boxcolor); + this.shape && (a.shape = this.shape); + this.onSerialize && this.onSerialize(a) && console.warn("node onSerialize shouldnt return anything, data should be stored in the object pass in the first parameter"); + return a; + }; + m.prototype.clone = function() { + var a = e.createNode(this.type); + if (!a) { + return null; + } + var b = e.cloneObject(this.serialize()); + if (b.inputs) { + for (var d = 0; d < b.inputs.length; ++d) { + b.inputs[d].link = null; + } + } + if (b.outputs) { + for (d = 0; d < b.outputs.length; ++d) { + b.outputs[d].links && (b.outputs[d].links.length = 0); + } + } + delete b.id; + a.configure(b); + return a; + }; + m.prototype.toString = function() { + return JSON.stringify(this.serialize()); + }; + m.prototype.getTitle = function() { + return this.title || this.constructor.title; + }; + m.prototype.setProperty = function(a, b) { + this.properties || (this.properties = {}); + if (b !== this.properties[a]) { + var d = this.properties[a]; + this.properties[a] = b; + this.onPropertyChanged && !1 === this.onPropertyChanged(a, b, d) && (this.properties[a] = d); + if (this.widgets) { + for (d = 0; d < this.widgets.length; ++d) { + var h = this.widgets[d]; + if (h && h.options.property == a) { + h.value = b; + break; + } + } + } + } + }; + m.prototype.setOutputData = function(a, b) { + if (this.outputs && !(-1 == a || a >= this.outputs.length)) { + var d = this.outputs[a]; + if (d && (d._data = b, this.outputs[a].links)) { + for (d = 0; d < this.outputs[a].links.length; d++) { + var h = this.graph.links[this.outputs[a].links[d]]; + h && (h.data = b); + } + } + } + }; + m.prototype.setOutputDataType = function(a, b) { + if (this.outputs && !(-1 == a || a >= this.outputs.length)) { + var d = this.outputs[a]; + if (d && (d.type = b, this.outputs[a].links)) { + for (d = 0; d < this.outputs[a].links.length; d++) { + this.graph.links[this.outputs[a].links[d]].type = b; + } + } + } + }; + m.prototype.getInputData = function(a, b) { + if (this.inputs && !(a >= this.inputs.length || null == this.inputs[a].link)) { + a = this.graph.links[this.inputs[a].link]; + if (!a) { + return null; + } + if (!b) { + return a.data; + } + b = this.graph.getNodeById(a.origin_id); + if (!b) { + return a.data; + } + if (b.updateOutputData) { + b.updateOutputData(a.origin_slot); + } else { + if (b.onExecute) { + b.onExecute(); + } + } + return a.data; + } + }; + m.prototype.getInputDataType = function(a) { + if (!this.inputs || a >= this.inputs.length || null == this.inputs[a].link) { + return null; + } + a = this.graph.links[this.inputs[a].link]; + if (!a) { + return null; + } + var b = this.graph.getNodeById(a.origin_id); + return b ? (a = b.outputs[a.origin_slot]) ? a.type : null : a.type; + }; + m.prototype.getInputDataByName = function(a, b) { + a = this.findInputSlot(a); + return -1 == a ? null : this.getInputData(a, b); + }; + m.prototype.isInputConnected = function(a) { + return this.inputs ? a < this.inputs.length && null != this.inputs[a].link : !1; + }; + m.prototype.getInputInfo = function(a) { + return this.inputs ? a < this.inputs.length ? this.inputs[a] : null : null; + }; + m.prototype.getInputNode = function(a) { + if (!this.inputs || a >= this.inputs.length) { + return null; + } + a = this.inputs[a]; + return a && null !== a.link ? (a = this.graph.links[a.link]) ? this.graph.getNodeById(a.origin_id) : null : null; + }; + m.prototype.getInputOrProperty = function(a) { + if (!this.inputs || !this.inputs.length) { + return this.properties ? this.properties[a] : null; + } + for (var b = 0, d = this.inputs.length; b < d; ++b) { + var h = this.inputs[b]; + if (a == h.name && null != h.link && (h = this.graph.links[h.link])) { + return h.data; + } + } + return this.properties[a]; + }; + m.prototype.getOutputData = function(a) { + return !this.outputs || a >= this.outputs.length ? null : this.outputs[a]._data; + }; + m.prototype.getOutputInfo = function(a) { + return this.outputs ? a < this.outputs.length ? this.outputs[a] : null : null; + }; + m.prototype.isOutputConnected = function(a) { + return this.outputs ? a < this.outputs.length && this.outputs[a].links && this.outputs[a].links.length : !1; + }; + m.prototype.isAnyOutputConnected = function() { + if (!this.outputs) { + return !1; + } + for (var a = 0; a < this.outputs.length; ++a) { + if (this.outputs[a].links && this.outputs[a].links.length) { + return !0; + } + } + return !1; + }; + m.prototype.getOutputNodes = function(a) { + if (!this.outputs || 0 == this.outputs.length || a >= this.outputs.length) { + return null; + } + a = this.outputs[a]; + if (!a.links || 0 == a.links.length) { + return null; + } + for (var b = [], d = 0; d < a.links.length; d++) { + var h = this.graph.links[a.links[d]]; + h && (h = this.graph.getNodeById(h.target_id)) && b.push(h); + } + return b; + }; + m.prototype.trigger = function(a, b) { + if (this.outputs && this.outputs.length) { + this.graph && (this.graph._last_trigger_time = e.getTime()); + for (var d = 0; d < this.outputs.length; ++d) { + var h = this.outputs[d]; + !h || h.type !== e.EVENT || a && h.name != a || this.triggerSlot(d, b); + } + } + }; + m.prototype.triggerSlot = function(a, b, d) { + if (this.outputs && (a = this.outputs[a]) && (a = a.links) && a.length) { + this.graph && (this.graph._last_trigger_time = e.getTime()); + for (var h = 0; h < a.length; ++h) { + var f = a[h]; + if (null == d || d == f) { + var c = this.graph.links[a[h]]; + if (c && (c._last_time = e.getTime(), f = this.graph.getNodeById(c.target_id))) { + if (c = f.inputs[c.target_slot], f.onAction) { + f.onAction(c.name, b); + } else { + if (f.mode === e.ON_TRIGGER && f.onExecute) { + f.onExecute(b); + } + } + } + } + } + } + }; + m.prototype.clearTriggeredSlot = function(a, b) { + if (this.outputs && (a = this.outputs[a]) && (a = a.links) && a.length) { + for (var d = 0; d < a.length; ++d) { + var h = a[d]; + if (null == b || b == h) { + if (h = this.graph.links[a[d]]) { + h._last_time = 0; + } + } + } + } + }; + m.prototype.addProperty = function(a, b, d, h) { + d = {name:a, type:d, default_value:b}; + if (h) { + for (var f in h) { + d[f] = h[f]; + } + } + this.properties_info || (this.properties_info = []); + this.properties_info.push(d); + this.properties || (this.properties = {}); + this.properties[a] = b; + return d; + }; + m.prototype.addOutput = function(a, b, d) { + a = {name:a, type:b, links:null}; + if (d) { + for (var h in d) { + a[h] = d[h]; + } + } + this.outputs || (this.outputs = []); + this.outputs.push(a); + if (this.onOutputAdded) { + this.onOutputAdded(a); + } + this.size = this.computeSize(); + this.setDirtyCanvas(!0, !0); + return a; + }; + m.prototype.addOutputs = function(a) { + for (var b = 0; b < a.length; ++b) { + var d = a[b], h = {name:d[0], type:d[1], link:null}; + if (a[2]) { + for (var f in d[2]) { + h[f] = d[2][f]; + } + } + this.outputs || (this.outputs = []); + this.outputs.push(h); + if (this.onOutputAdded) { + this.onOutputAdded(h); + } + } + this.size = this.computeSize(); + this.setDirtyCanvas(!0, !0); + }; + m.prototype.removeOutput = function(a) { + this.disconnectOutput(a); + this.outputs.splice(a, 1); + for (var b = a; b < this.outputs.length; ++b) { + if (this.outputs[b] && this.outputs[b].links) { + for (var d = this.outputs[b].links, h = 0; h < d.length; ++h) { + var f = this.graph.links[d[h]]; + f && --f.origin_slot; + } + } + } + this.size = this.computeSize(); + if (this.onOutputRemoved) { + this.onOutputRemoved(a); + } + this.setDirtyCanvas(!0, !0); + }; + m.prototype.addInput = function(a, b, d) { + a = {name:a, type:b || 0, link:null}; + if (d) { + for (var h in d) { + a[h] = d[h]; + } + } + this.inputs || (this.inputs = []); + this.inputs.push(a); + this.size = this.computeSize(); + if (this.onInputAdded) { + this.onInputAdded(a); + } + this.setDirtyCanvas(!0, !0); + return a; + }; + m.prototype.addInputs = function(a) { + for (var b = 0; b < a.length; ++b) { + var d = a[b], h = {name:d[0], type:d[1], link:null}; + if (a[2]) { + for (var f in d[2]) { + h[f] = d[2][f]; + } + } + this.inputs || (this.inputs = []); + this.inputs.push(h); + if (this.onInputAdded) { + this.onInputAdded(h); + } + } + this.size = this.computeSize(); + this.setDirtyCanvas(!0, !0); + }; + m.prototype.removeInput = function(a) { + this.disconnectInput(a); + this.inputs.splice(a, 1); + for (var b = a; b < this.inputs.length; ++b) { + if (this.inputs[b]) { + var d = this.graph.links[this.inputs[b].link]; + d && --d.target_slot; + } + } + this.size = this.computeSize(); + if (this.onInputRemoved) { + this.onInputRemoved(a); + } + this.setDirtyCanvas(!0, !0); + }; + m.prototype.addConnection = function(a, b, d, h) { + a = {name:a, type:b, pos:d, direction:h, links:null}; + this.connections.push(a); + return a; + }; + m.prototype.computeSize = function(a, b) { + function d(a) { + return a ? h * a.length * 0.6 : 0; + } + if (this.constructor.size) { + return this.constructor.size.concat(); + } + a = Math.max(this.inputs ? this.inputs.length : 1, this.outputs ? this.outputs.length : 1); + b = b || new Float32Array([0, 0]); + a = Math.max(a, 1); + var h = e.NODE_TEXT_SIZE, f = d(this.title), c = 0, k = 0; + if (this.inputs) { + for (var n = 0, q = this.inputs.length; n < q; ++n) { + var l = this.inputs[n]; + l = l.label || l.name || ""; + l = d(l); + c < l && (c = l); + } + } + if (this.outputs) { + for (n = 0, q = this.outputs.length; n < q; ++n) { + l = this.outputs[n], l = l.label || l.name || "", l = d(l), k < l && (k = l); + } + } + b[0] = Math.max(c + k + 10, f); + b[0] = Math.max(b[0], e.NODE_WIDTH); + this.widgets && this.widgets.length && (b[0] = Math.max(b[0], 1.5 * e.NODE_WIDTH)); + b[1] = (this.constructor.slot_start_y || 0) + a * e.NODE_SLOT_HEIGHT; + a = 0; + if (this.widgets && this.widgets.length) { + n = 0; + for (q = this.widgets.length; n < q; ++n) { + a = this.widgets[n].computeSize ? a + (this.widgets[n].computeSize(b[0])[1] + 4) : a + (e.NODE_WIDGET_HEIGHT + 4); + } + a += 8; + } + b[1] = this.widgets_up ? Math.max(b[1], a) : null != this.widgets_start_y ? Math.max(b[1], a + this.widgets_start_y) : b[1] + a; + if (this.onResize) { + this.onResize(b); + } + this.constructor.min_height && b[1] < this.constructor.min_height && (b[1] = this.constructor.min_height); + b[1] += 6; + return b; + }; + m.prototype.getPropertyInfo = function(a) { + var b = null; + if (this.properties_info) { + for (var d = 0; d < this.properties_info.length; ++d) { + if (this.properties_info[d].name == a) { + b = this.properties_info[d]; + break; + } + } + } + this.constructor["@" + a] && (b = this.constructor["@" + a]); + this.onGetPropertyInfo && (b = this.onGetPropertyInfo(a)); + b || (b = {}); + b.type || (b.type = typeof this.properties[a]); + return b; + }; + m.prototype.addWidget = function(a, b, d, h, f) { + this.widgets || (this.widgets = []); + !f && h && h.constructor === Object && (f = h, h = null); + f && f.constructor === String && (f = {property:f}); + h && h.constructor === String && (f || (f = {}), f.property = h, h = null); + h && h.constructor !== Function && (console.warn("addWidget: callback must be a function"), h = null); + b = {type:a.toLowerCase(), name:b, value:d, callback:h, options:f || {}}; + void 0 !== b.options.y && (b.y = b.options.y); + h || b.options.callback || b.options.property || console.warn("LiteGraph addWidget(...) without a callback or property assigned"); + if ("combo" == a && !b.options.values) { + throw "LiteGraph addWidget('combo',...) requires to pass values in options: { values:['red','blue'] }"; + } + this.widgets.push(b); + this.size = this.computeSize(); + return b; + }; + m.prototype.addCustomWidget = function(a) { + this.widgets || (this.widgets = []); + this.widgets.push(a); + return a; + }; + m.prototype.getBounding = function(a) { + a = a || new Float32Array(4); + a[0] = this.pos[0] - 4; + a[1] = this.pos[1] - e.NODE_TITLE_HEIGHT; + a[2] = this.size[0] + 4; + a[3] = this.size[1] + e.NODE_TITLE_HEIGHT; + if (this.onBounding) { + this.onBounding(a); + } + return a; + }; + m.prototype.isPointInside = function(a, b, d, h) { + d = d || 0; + var f = this.graph && this.graph.isLive() ? 0 : e.NODE_TITLE_HEIGHT; + h && (f = 0); + if (this.flags && this.flags.collapsed) { + if (y(a, b, this.pos[0] - d, this.pos[1] - e.NODE_TITLE_HEIGHT - d, (this._collapsed_width || e.NODE_COLLAPSED_WIDTH) + 2 * d, e.NODE_TITLE_HEIGHT + 2 * d)) { + return !0; + } + } else { + if (this.pos[0] - 4 - d < a && this.pos[0] + this.size[0] + 4 + d > a && this.pos[1] - f - d < b && this.pos[1] + this.size[1] + d > b) { + return !0; + } + } + return !1; + }; + m.prototype.getSlotInPosition = function(a, b) { + var d = new Float32Array(2); + if (this.inputs) { + for (var h = 0, f = this.inputs.length; h < f; ++h) { + var e = this.inputs[h]; + this.getConnectionPos(!0, h, d); + if (y(a, b, d[0] - 10, d[1] - 5, 20, 10)) { + return {input:e, slot:h, link_pos:d}; + } + } + } + if (this.outputs) { + for (h = 0, f = this.outputs.length; h < f; ++h) { + if (e = this.outputs[h], this.getConnectionPos(!1, h, d), y(a, b, d[0] - 10, d[1] - 5, 20, 10)) { + return {output:e, slot:h, link_pos:d}; + } + } + } + return null; + }; + m.prototype.findInputSlot = function(a) { + if (!this.inputs) { + return -1; + } + for (var b = 0, d = this.inputs.length; b < d; ++b) { + if (a == this.inputs[b].name) { + return b; + } + } + return -1; + }; + m.prototype.findOutputSlot = function(a) { + if (!this.outputs) { + return -1; + } + for (var b = 0, d = this.outputs.length; b < d; ++b) { + if (a == this.outputs[b].name) { + return b; + } + } + return -1; + }; + m.prototype.connect = function(a, b, d) { + d = d || 0; + if (!this.graph) { + return console.log("Connect: Error, node doesn't belong to any graph. Nodes must be added first to a graph before connecting them."), null; + } + if (a.constructor === String) { + if (a = this.findOutputSlot(a), -1 == a) { + return e.debug && console.log("Connect: Error, no slot of name " + a), null; + } + } else { + if (!this.outputs || a >= this.outputs.length) { + return e.debug && console.log("Connect: Error, slot number not found"), null; + } + } + b && b.constructor === Number && (b = this.graph.getNodeById(b)); + if (!b) { + throw "target node is null"; + } + if (b == this) { + return null; + } + if (d.constructor === String) { + if (d = b.findInputSlot(d), -1 == d) { + return e.debug && console.log("Connect: Error, no slot of name " + d), null; + } + } else { + if (d === e.EVENT) { + return null; + } + if (!b.inputs || d >= b.inputs.length) { + return e.debug && console.log("Connect: Error, slot number not found"), null; + } + } + null != b.inputs[d].link && b.disconnectInput(d); + var h = this.outputs[a]; + if (b.onConnectInput && !1 === b.onConnectInput(d, h.type, h)) { + return null; + } + var f = b.inputs[d], c = null; + if (e.isValidConnection(h.type, f.type)) { + c = new p(++this.graph.last_link_id, f.type, this.id, a, b.id, d); + this.graph.links[c.id] = c; + null == h.links && (h.links = []); + h.links.push(c.id); + b.inputs[d].link = c.id; + this.graph && this.graph._version++; + if (this.onConnectionsChange) { + this.onConnectionsChange(e.OUTPUT, a, !0, c, h); + } + if (b.onConnectionsChange) { + b.onConnectionsChange(e.INPUT, d, !0, c, f); + } + this.graph && this.graph.onNodeConnectionChange && (this.graph.onNodeConnectionChange(e.INPUT, b, d, this, a), this.graph.onNodeConnectionChange(e.OUTPUT, this, a, b, d)); + } + this.setDirtyCanvas(!1, !0); + this.graph.connectionChange(this, c); + return c; + }; + m.prototype.disconnectOutput = function(a, b) { + if (a.constructor === String) { + if (a = this.findOutputSlot(a), -1 == a) { + return e.debug && console.log("Connect: Error, no slot of name " + a), !1; + } + } else { + if (!this.outputs || a >= this.outputs.length) { + return e.debug && console.log("Connect: Error, slot number not found"), !1; + } + } + var d = this.outputs[a]; + if (!d || !d.links || 0 == d.links.length) { + return !1; + } + if (b) { + b.constructor === Number && (b = this.graph.getNodeById(b)); + if (!b) { + throw "Target Node not found"; + } + for (var h = 0, f = d.links.length; h < f; h++) { + var c = d.links[h], k = this.graph.links[c]; + if (k.target_id == b.id) { + d.links.splice(h, 1); + var n = b.inputs[k.target_slot]; + n.link = null; + delete this.graph.links[c]; + this.graph && this.graph._version++; + if (b.onConnectionsChange) { + b.onConnectionsChange(e.INPUT, k.target_slot, !1, k, n); + } + if (this.onConnectionsChange) { + this.onConnectionsChange(e.OUTPUT, a, !1, k, d); + } + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange(e.OUTPUT, this, a); + } + this.graph && this.graph.onNodeConnectionChange && (this.graph.onNodeConnectionChange(e.OUTPUT, this, a), this.graph.onNodeConnectionChange(e.INPUT, b, k.target_slot)); + break; + } + } + } else { + h = 0; + for (f = d.links.length; h < f; h++) { + if (c = d.links[h], k = this.graph.links[c]) { + b = this.graph.getNodeById(k.target_id); + this.graph && this.graph._version++; + if (b) { + n = b.inputs[k.target_slot]; + n.link = null; + if (b.onConnectionsChange) { + b.onConnectionsChange(e.INPUT, k.target_slot, !1, k, n); + } + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange(e.INPUT, b, k.target_slot); + } + } + delete this.graph.links[c]; + if (this.onConnectionsChange) { + this.onConnectionsChange(e.OUTPUT, a, !1, k, d); + } + this.graph && this.graph.onNodeConnectionChange && (this.graph.onNodeConnectionChange(e.OUTPUT, this, a), this.graph.onNodeConnectionChange(e.INPUT, b, k.target_slot)); + } + } + d.links = null; + } + this.setDirtyCanvas(!1, !0); + this.graph.connectionChange(this); + return !0; + }; + m.prototype.disconnectInput = function(a) { + if (a.constructor === String) { + if (a = this.findInputSlot(a), -1 == a) { + return e.debug && console.log("Connect: Error, no slot of name " + a), !1; + } + } else { + if (!this.inputs || a >= this.inputs.length) { + return e.debug && console.log("Connect: Error, slot number not found"), !1; + } + } + var b = this.inputs[a]; + if (!b) { + return !1; + } + var d = this.inputs[a].link; + this.inputs[a].link = null; + var h = this.graph.links[d]; + if (h) { + var f = this.graph.getNodeById(h.origin_id); + if (!f) { + return !1; + } + var c = f.outputs[h.origin_slot]; + if (!c || !c.links || 0 == c.links.length) { + return !1; + } + for (var k = 0, n = c.links.length; k < n; k++) { + if (c.links[k] == d) { + c.links.splice(k, 1); + break; + } + } + delete this.graph.links[d]; + this.graph && this.graph._version++; + if (this.onConnectionsChange) { + this.onConnectionsChange(e.INPUT, a, !1, h, b); + } + if (f.onConnectionsChange) { + f.onConnectionsChange(e.OUTPUT, k, !1, h, c); + } + this.graph && this.graph.onNodeConnectionChange && (this.graph.onNodeConnectionChange(e.OUTPUT, f, k), this.graph.onNodeConnectionChange(e.INPUT, this, a)); + } + this.setDirtyCanvas(!1, !0); + this.graph.connectionChange(this); + return !0; + }; + m.prototype.getConnectionPos = function(a, b, d) { + d = d || new Float32Array(2); + var h = 0; + a && this.inputs && (h = this.inputs.length); + !a && this.outputs && (h = this.outputs.length); + var f = 0.5 * e.NODE_SLOT_HEIGHT; + if (this.flags.collapsed) { + return b = this._collapsed_width || e.NODE_COLLAPSED_WIDTH, this.horizontal ? (d[0] = this.pos[0] + 0.5 * b, d[1] = a ? this.pos[1] - e.NODE_TITLE_HEIGHT : this.pos[1]) : (d[0] = a ? this.pos[0] : this.pos[0] + b, d[1] = this.pos[1] - 0.5 * e.NODE_TITLE_HEIGHT), d; + } + if (a && -1 == b) { + return d[0] = this.pos[0] + 0.5 * e.NODE_TITLE_HEIGHT, d[1] = this.pos[1] + 0.5 * e.NODE_TITLE_HEIGHT, d; + } + if (a && h > b && this.inputs[b].pos) { + return d[0] = this.pos[0] + this.inputs[b].pos[0], d[1] = this.pos[1] + this.inputs[b].pos[1], d; + } + if (!a && h > b && this.outputs[b].pos) { + return d[0] = this.pos[0] + this.outputs[b].pos[0], d[1] = this.pos[1] + this.outputs[b].pos[1], d; + } + if (this.horizontal) { + return d[0] = this.pos[0] + this.size[0] / h * (b + 0.5), d[1] = a ? this.pos[1] - e.NODE_TITLE_HEIGHT : this.pos[1] + this.size[1], d; + } + d[0] = a ? this.pos[0] + f : this.pos[0] + this.size[0] + 1 - f; + d[1] = this.pos[1] + (b + 0.7) * e.NODE_SLOT_HEIGHT + (this.constructor.slot_start_y || 0); + return d; + }; + m.prototype.alignToGrid = function() { + this.pos[0] = e.CANVAS_GRID_SIZE * Math.round(this.pos[0] / e.CANVAS_GRID_SIZE); + this.pos[1] = e.CANVAS_GRID_SIZE * Math.round(this.pos[1] / e.CANVAS_GRID_SIZE); + }; + m.prototype.trace = function(a) { + this.console || (this.console = []); + this.console.push(a); + this.console.length > m.MAX_CONSOLE && this.console.shift(); + this.graph.onNodeTrace(this, a); + }; + m.prototype.setDirtyCanvas = function(a, b) { + this.graph && this.graph.sendActionToCanvas("setDirty", [a, b]); + }; + m.prototype.loadImage = function(a) { + var b = new Image; + b.src = e.node_images_path + a; + b.ready = !1; + var d = this; + b.onload = function() { + this.ready = !0; + d.setDirtyCanvas(!0); + }; + return b; + }; + m.prototype.captureInput = function(a) { + if (this.graph && this.graph.list_of_graphcanvas) { + for (var b = this.graph.list_of_graphcanvas, d = 0; d < b.length; ++d) { + var h = b[d]; + if (a || h.node_capturing_input == this) { + h.node_capturing_input = a ? this : null; + } + } + } + }; + m.prototype.collapse = function(a) { + this.graph._version++; + if (!1 !== this.constructor.collapsable || a) { + this.flags.collapsed = this.flags.collapsed ? !1 : !0, this.setDirtyCanvas(!0, !0); + } + }; + m.prototype.pin = function(a) { + this.graph._version++; + this.flags.pinned = void 0 === a ? !this.flags.pinned : a; + }; + m.prototype.localToScreen = function(a, b, d) { + return [(a + this.pos[0]) * d.scale + d.offset[0], (b + this.pos[1]) * d.scale + d.offset[1]]; + }; + w.LGraphGroup = e.LGraphGroup = g; + g.prototype._ctor = function(a) { + this.title = a || "Group"; + this.font_size = 24; + this.color = l.node_colors.pale_blue ? l.node_colors.pale_blue.groupcolor : "#AAA"; + this._bounding = new Float32Array([10, 10, 140, 80]); + this._pos = this._bounding.subarray(0, 2); + this._size = this._bounding.subarray(2, 4); + this._nodes = []; + this.graph = null; + Object.defineProperty(this, "pos", {set:function(a) { + !a || 2 > a.length || (this._pos[0] = a[0], this._pos[1] = a[1]); + }, get:function() { + return this._pos; + }, enumerable:!0}); + Object.defineProperty(this, "size", {set:function(a) { + !a || 2 > a.length || (this._size[0] = Math.max(140, a[0]), this._size[1] = Math.max(80, a[1])); + }, get:function() { + return this._size; + }, enumerable:!0}); + }; + g.prototype.configure = function(a) { + this.title = a.title; + this._bounding.set(a.bounding); + this.color = a.color; + this.font = a.font; + }; + g.prototype.serialize = function() { + var a = this._bounding; + return {title:this.title, bounding:[Math.round(a[0]), Math.round(a[1]), Math.round(a[2]), Math.round(a[3])], color:this.color, font:this.font}; + }; + g.prototype.move = function(a, b, d) { + this._pos[0] += a; + this._pos[1] += b; + if (!d) { + for (d = 0; d < this._nodes.length; ++d) { + var h = this._nodes[d]; + h.pos[0] += a; + h.pos[1] += b; + } + } + }; + g.prototype.recomputeInsideNodes = function() { + this._nodes.length = 0; + for (var a = this.graph._nodes, b = new Float32Array(4), d = 0; d < a.length; ++d) { + var h = a[d]; + h.getBounding(b); + v(this._bounding, b) && this._nodes.push(h); + } + }; + g.prototype.isPointInside = m.prototype.isPointInside; + g.prototype.setDirtyCanvas = m.prototype.setDirtyCanvas; + e.DragAndScale = u; + u.prototype.bindEvents = function(a) { + this.last_mouse = new Float32Array(2); + this._binded_mouse_callback = this.onMouse.bind(this); + a.addEventListener("mousedown", this._binded_mouse_callback); + a.addEventListener("mousemove", this._binded_mouse_callback); + a.addEventListener("mousewheel", this._binded_mouse_callback, !1); + a.addEventListener("wheel", this._binded_mouse_callback, !1); + }; + u.prototype.computeVisibleArea = function() { + if (this.element) { + var a = -this.offset[0], b = -this.offset[1], d = a + this.element.width / this.scale, h = b + this.element.height / this.scale; + this.visible_area[0] = a; + this.visible_area[1] = b; + this.visible_area[2] = d - a; + this.visible_area[3] = h - b; + } else { + this.visible_area[0] = this.visible_area[1] = this.visible_area[2] = this.visible_area[3] = 0; + } + }; + u.prototype.onMouse = function(a) { + if (this.enabled) { + var b = this.element, d = b.getBoundingClientRect(), h = a.clientX - d.left; + d = a.clientY - d.top; + a.canvasx = h; + a.canvasy = d; + a.dragging = this.dragging; + var f = !1; + this.onmouse && (f = this.onmouse(a)); + if ("mousedown" == a.type) { + this.dragging = !0, b.removeEventListener("mousemove", this._binded_mouse_callback), document.body.addEventListener("mousemove", this._binded_mouse_callback), document.body.addEventListener("mouseup", this._binded_mouse_callback); + } else { + if ("mousemove" == a.type) { + f || (b = h - this.last_mouse[0], f = d - this.last_mouse[1], this.dragging && this.mouseDrag(b, f)); + } else { + if ("mouseup" == a.type) { + this.dragging = !1, document.body.removeEventListener("mousemove", this._binded_mouse_callback), document.body.removeEventListener("mouseup", this._binded_mouse_callback), b.addEventListener("mousemove", this._binded_mouse_callback); + } else { + if ("mousewheel" == a.type || "wheel" == a.type || "DOMMouseScroll" == a.type) { + a.eventType = "mousewheel", a.wheel = "wheel" == a.type ? -a.deltaY : null != a.wheelDeltaY ? a.wheelDeltaY : -60 * a.detail, a.delta = a.wheelDelta ? a.wheelDelta / 40 : a.deltaY ? -a.deltaY / 3 : 0, this.changeDeltaScale(1.0 + 0.05 * a.delta); + } + } + } + } + this.last_mouse[0] = h; + this.last_mouse[1] = d; + a.preventDefault(); + a.stopPropagation(); + return !1; + } + }; + u.prototype.toCanvasContext = function(a) { + a.scale(this.scale, this.scale); + a.translate(this.offset[0], this.offset[1]); + }; + u.prototype.convertOffsetToCanvas = function(a) { + return [(a[0] + this.offset[0]) * this.scale, (a[1] + this.offset[1]) * this.scale]; + }; + u.prototype.convertCanvasToOffset = function(a, b) { + b = b || [0, 0]; + b[0] = a[0] / this.scale - this.offset[0]; + b[1] = a[1] / this.scale - this.offset[1]; + return b; + }; + u.prototype.mouseDrag = function(a, b) { + this.offset[0] += a / this.scale; + this.offset[1] += b / this.scale; + if (this.onredraw) { + this.onredraw(this); + } + }; + u.prototype.changeScale = function(a, b) { + a < this.min_scale ? a = this.min_scale : a > this.max_scale && (a = this.max_scale); + if (a != this.scale && this.element) { + var d = this.element.getBoundingClientRect(); + if (d && (b = b || [0.5 * d.width, 0.5 * d.height], d = this.convertCanvasToOffset(b), this.scale = a, 0.01 > Math.abs(this.scale - 1) && (this.scale = 1), a = this.convertCanvasToOffset(b), a = [a[0] - d[0], a[1] - d[1]], this.offset[0] += a[0], this.offset[1] += a[1], this.onredraw)) { + this.onredraw(this); + } + } + }; + u.prototype.changeDeltaScale = function(a, b) { + this.changeScale(this.scale * a, b); + }; + u.prototype.reset = function() { + this.scale = 1; + this.offset[0] = 0; + this.offset[1] = 0; + }; + w.LGraphCanvas = e.LGraphCanvas = l; + l.link_type_colors = {"-1":e.EVENT_LINK_COLOR, number:"#AAA", node:"#DCA"}; + l.gradients = {}; + l.prototype.clear = function() { + this.fps = this.render_time = this.last_draw_time = this.frame = 0; + this.dragging_rectangle = null; + this.selected_nodes = {}; + this.selected_group = null; + this.visible_nodes = []; + this.connecting_node = this.node_capturing_input = this.node_over = this.node_dragged = null; + this.highlighted_links = {}; + this.dirty_bgcanvas = this.dirty_canvas = !0; + this.node_widget = this.node_in_panel = this.dirty_area = null; + this.last_mouse = [0, 0]; + this.last_mouseclick = 0; + this.visible_area.set([0, 0, 0, 0]); + if (this.onClear) { + this.onClear(); + } + }; + l.prototype.setGraph = function(a, b) { + this.graph != a && (b || this.clear(), !a && this.graph ? this.graph.detachCanvas(this) : (a.attachCanvas(this), this._graph_stack && (this._graph_stack = null), this.setDirty(!0, !0))); + }; + l.prototype.openSubgraph = function(a) { + if (!a) { + throw "graph cannot be null"; + } + if (this.graph == a) { + throw "graph cannot be the same"; + } + this.clear(); + this.graph && (this._graph_stack || (this._graph_stack = []), this._graph_stack.push(this.graph)); + a.attachCanvas(this); + this.setDirty(!0, !0); + }; + l.prototype.closeSubgraph = function() { + if (this._graph_stack && 0 != this._graph_stack.length) { + var a = this.graph._subgraph_node, b = this._graph_stack.pop(); + this.selected_nodes = {}; + this.highlighted_links = {}; + b.attachCanvas(this); + this.setDirty(!0, !0); + a && (this.centerOnNode(a), this.selectNodes([a])); + } + }; + l.prototype.getCurrentGraph = function() { + return this.graph; + }; + l.prototype.setCanvas = function(a, b) { + if (a && a.constructor === String && (a = document.getElementById(a), !a)) { + throw "Error creating LiteGraph canvas: Canvas not found"; + } + if (a !== this.canvas && (!a && this.canvas && (b || this.unbindEvents()), this.canvas = a, this.ds.element = a)) { + a.className += " lgraphcanvas"; + a.data = this; + a.tabindex = "1"; + this.bgcanvas = null; + this.bgcanvas || (this.bgcanvas = document.createElement("canvas"), this.bgcanvas.width = this.canvas.width, this.bgcanvas.height = this.canvas.height); + if (null == a.getContext) { + if ("canvas" != a.localName) { + throw "Element supplied for LGraphCanvas must be a element, you passed a " + a.localName; + } + throw "This browser doesn't support Canvas"; + } + null == (this.ctx = a.getContext("2d")) && (a.webgl_enabled || console.warn("This canvas seems to be WebGL, enabling WebGL renderer"), this.enableWebGL()); + this._mousemove_callback = this.processMouseMove.bind(this); + this._mouseup_callback = this.processMouseUp.bind(this); + b || this.bindEvents(); + } + }; + l.prototype._doNothing = function(a) { + a.preventDefault(); + return !1; + }; + l.prototype._doReturnTrue = function(a) { + a.preventDefault(); + return !0; + }; + l.prototype.bindEvents = function() { + if (this._events_binded) { + console.warn("LGraphCanvas: events already binded"); + } else { + var a = this.canvas, b = this.getCanvasWindow().document; + this._mousedown_callback = this.processMouseDown.bind(this); + this._mousewheel_callback = this.processMouseWheel.bind(this); + a.addEventListener("mousedown", this._mousedown_callback, !0); + a.addEventListener("mousemove", this._mousemove_callback); + a.addEventListener("mousewheel", this._mousewheel_callback, !1); + a.addEventListener("contextmenu", this._doNothing); + a.addEventListener("DOMMouseScroll", this._mousewheel_callback, !1); + a.addEventListener("touchstart", this.touchHandler, !0); + a.addEventListener("touchmove", this.touchHandler, !0); + a.addEventListener("touchend", this.touchHandler, !0); + a.addEventListener("touchcancel", this.touchHandler, !0); + this._key_callback = this.processKey.bind(this); + a.addEventListener("keydown", this._key_callback, !0); + b.addEventListener("keyup", this._key_callback, !0); + this._ondrop_callback = this.processDrop.bind(this); + a.addEventListener("dragover", this._doNothing, !1); + a.addEventListener("dragend", this._doNothing, !1); + a.addEventListener("drop", this._ondrop_callback, !1); + a.addEventListener("dragenter", this._doReturnTrue, !1); + this._events_binded = !0; + } + }; + l.prototype.unbindEvents = function() { + if (this._events_binded) { + var a = this.getCanvasWindow().document; + this.canvas.removeEventListener("mousedown", this._mousedown_callback); + this.canvas.removeEventListener("mousewheel", this._mousewheel_callback); + this.canvas.removeEventListener("DOMMouseScroll", this._mousewheel_callback); + this.canvas.removeEventListener("keydown", this._key_callback); + a.removeEventListener("keyup", this._key_callback); + this.canvas.removeEventListener("contextmenu", this._doNothing); + this.canvas.removeEventListener("drop", this._ondrop_callback); + this.canvas.removeEventListener("dragenter", this._doReturnTrue); + this.canvas.removeEventListener("touchstart", this.touchHandler); + this.canvas.removeEventListener("touchmove", this.touchHandler); + this.canvas.removeEventListener("touchend", this.touchHandler); + this.canvas.removeEventListener("touchcancel", this.touchHandler); + this._ondrop_callback = this._key_callback = this._mousewheel_callback = this._mousedown_callback = null; + this._events_binded = !1; + } else { + console.warn("LGraphCanvas: no events binded"); + } + }; + l.getFileExtension = function(a) { + var b = a.indexOf("?"); + -1 != b && (a = a.substr(0, b)); + b = a.lastIndexOf("."); + return -1 == b ? "" : a.substr(b + 1).toLowerCase(); + }; + l.prototype.enableWebGL = function() { + this.gl = this.ctx = enableWebGLCanvas(this.canvas); + this.ctx.webgl = !0; + this.bgcanvas = this.canvas; + this.bgctx = this.gl; + this.canvas.webgl_enabled = !0; + }; + l.prototype.setDirty = function(a, b) { + a && (this.dirty_canvas = !0); + b && (this.dirty_bgcanvas = !0); + }; + l.prototype.getCanvasWindow = function() { + if (!this.canvas) { + return window; + } + var a = this.canvas.ownerDocument; + return a.defaultView || a.parentWindow; + }; + l.prototype.startRendering = function() { + function a() { + this.pause_rendering || this.draw(); + var b = this.getCanvasWindow(); + this.is_rendering && b.requestAnimationFrame(a.bind(this)); + } + this.is_rendering || (this.is_rendering = !0, a.call(this)); + }; + l.prototype.stopRendering = function() { + this.is_rendering = !1; + }; + l.prototype.processMouseDown = function(a) { + if (this.graph) { + this.adjustMouseEvent(a); + var b = this.getCanvasWindow(); + l.active_canvas = this; + this.canvas.removeEventListener("mousemove", this._mousemove_callback); + b.document.addEventListener("mousemove", this._mousemove_callback, !0); + b.document.addEventListener("mouseup", this._mouseup_callback, !0); + var d = this.graph.getNodeOnPos(a.canvasX, a.canvasY, this.visible_nodes, 5), h = !1, f = 300 > e.getTime() - this.last_mouseclick; + this.canvas_mouse[0] = a.canvasX; + this.canvas_mouse[1] = a.canvasY; + this.canvas.focus(); + e.closeAllContextMenus(b); + if (!this.onMouse || 1 != this.onMouse(a)) { + if (1 == a.which) { + a.ctrlKey && (this.dragging_rectangle = new Float32Array(4), this.dragging_rectangle[0] = a.canvasX, this.dragging_rectangle[1] = a.canvasY, this.dragging_rectangle[2] = 1, this.dragging_rectangle[3] = 1, h = !0); + var c = !1; + if (d && this.allow_interaction && !h && !this.read_only) { + this.live_mode || d.flags.pinned || this.bringToFront(d); + if (!this.connecting_node && !d.flags.collapsed && !this.live_mode) { + if (!h && !1 !== d.resizable && y(a.canvasX, a.canvasY, d.pos[0] + d.size[0] - 5, d.pos[1] + d.size[1] - 5, 10, 10)) { + this.resizing_node = d, this.canvas.style.cursor = "se-resize", h = !0; + } else { + if (d.outputs) { + for (var k = 0, n = d.outputs.length; k < n; ++k) { + var q = d.outputs[k], g = d.getConnectionPos(!1, k); + if (y(a.canvasX, a.canvasY, g[0] - 15, g[1] - 10, 30, 20)) { + this.connecting_node = d; + this.connecting_output = q; + this.connecting_pos = d.getConnectionPos(!1, k); + this.connecting_slot = k; + a.shiftKey && d.disconnectOutput(k); + if (f) { + if (d.onOutputDblClick) { + d.onOutputDblClick(k, a); + } + } else { + if (d.onOutputClick) { + d.onOutputClick(k, a); + } + } + h = !0; + break; + } + } + } + if (d.inputs) { + for (k = 0, n = d.inputs.length; k < n; ++k) { + if (q = d.inputs[k], g = d.getConnectionPos(!0, k), y(a.canvasX, a.canvasY, g[0] - 15, g[1] - 10, 30, 20)) { + if (f) { + if (d.onInputDblClick) { + d.onInputDblClick(k, a); + } + } else { + if (d.onInputClick) { + d.onInputClick(k, a); + } + } + if (null !== q.link) { + h = this.graph.links[q.link]; + d.disconnectInput(k); + if (this.allow_reconnect_links || a.shiftKey) { + this.connecting_node = this.graph._nodes_by_id[h.origin_id], this.connecting_slot = h.origin_slot, this.connecting_output = this.connecting_node.outputs[this.connecting_slot], this.connecting_pos = this.connecting_node.getConnectionPos(!1, this.connecting_slot); + } + h = this.dirty_bgcanvas = !0; + } + } + } + } + } + } + if (!h) { + k = !1; + if (n = this.processNodeWidgets(d, this.canvas_mouse, a)) { + k = !0, this.node_widget = [d, n]; + } + if (f && this.selected_nodes[d.id]) { + if (d.onDblClick) { + d.onDblClick(a, [a.canvasX - d.pos[0], a.canvasY - d.pos[1]], this); + } + this.processNodeDblClicked(d); + k = !0; + } + d.onMouseDown && d.onMouseDown(a, [a.canvasX - d.pos[0], a.canvasY - d.pos[1]], this) ? k = !0 : this.live_mode && (k = c = !0); + k || (this.allow_dragnodes && (this.node_dragged = d), this.selected_nodes[d.id] || this.processNodeSelected(d, a)); + this.dirty_canvas = !0; + } + } else { + if (!this.read_only) { + for (k = 0; k < this.visible_links.length; ++k) { + if (d = this.visible_links[k], c = d._pos, !(!c || a.canvasX < c[0] - 4 || a.canvasX > c[0] + 4 || a.canvasY < c[1] - 4 || a.canvasY > c[1] + 4)) { + this.showLinkMenu(d, a); + this.over_link_center = null; + break; + } + } + } + this.selected_group = this.graph.getGroupOnPos(a.canvasX, a.canvasY); + this.selected_group_resizing = !1; + this.selected_group && !this.read_only && (a.ctrlKey && (this.dragging_rectangle = null), 10 > B([a.canvasX, a.canvasY], [this.selected_group.pos[0] + this.selected_group.size[0], this.selected_group.pos[1] + this.selected_group.size[1]]) * this.ds.scale ? this.selected_group_resizing = !0 : this.selected_group.recomputeInsideNodes()); + f && !this.read_only && this.allow_searchbox && this.showSearchBox(a); + c = !0; + } + !h && c && this.allow_dragcanvas && (this.dragging_canvas = !0); + } else { + 2 != a.which && 3 == a.which && (this.read_only || this.processContextMenu(d, a)); + } + this.last_mouse[0] = a.localX; + this.last_mouse[1] = a.localY; + this.last_mouseclick = e.getTime(); + this.last_mouse_dragging = !0; + this.graph.change(); + (!b.document.activeElement || "input" != b.document.activeElement.nodeName.toLowerCase() && "textarea" != b.document.activeElement.nodeName.toLowerCase()) && a.preventDefault(); + a.stopPropagation(); + if (this.onMouseDown) { + this.onMouseDown(a); + } + return !1; + } + } + }; + l.prototype.processMouseMove = function(a) { + this.autoresize && this.resize(); + if (this.graph) { + l.active_canvas = this; + this.adjustMouseEvent(a); + var b = [a.localX, a.localY], d = [b[0] - this.last_mouse[0], b[1] - this.last_mouse[1]]; + this.last_mouse = b; + this.canvas_mouse[0] = a.canvasX; + this.canvas_mouse[1] = a.canvasY; + a.dragging = this.last_mouse_dragging; + this.node_widget && (this.processNodeWidgets(this.node_widget[0], this.canvas_mouse, a, this.node_widget[1]), this.dirty_canvas = !0); + if (this.dragging_rectangle) { + this.dragging_rectangle[2] = a.canvasX - this.dragging_rectangle[0], this.dragging_rectangle[3] = a.canvasY - this.dragging_rectangle[1], this.dirty_canvas = !0; + } else { + if (this.selected_group && !this.read_only) { + this.selected_group_resizing ? this.selected_group.size = [a.canvasX - this.selected_group.pos[0], a.canvasY - this.selected_group.pos[1]] : (this.selected_group.move(d[0] / this.ds.scale, d[1] / this.ds.scale, a.ctrlKey), this.selected_group._nodes.length && (this.dirty_canvas = !0)), this.dirty_bgcanvas = !0; + } else { + if (this.dragging_canvas) { + this.ds.offset[0] += d[0] / this.ds.scale, this.ds.offset[1] += d[1] / this.ds.scale, this.dirty_bgcanvas = this.dirty_canvas = !0; + } else { + if (this.allow_interaction && !this.read_only) { + this.connecting_node && (this.dirty_canvas = !0); + var h = this.graph.getNodeOnPos(a.canvasX, a.canvasY, this.visible_nodes); + b = 0; + for (var f = this.graph._nodes.length; b < f; ++b) { + if (this.graph._nodes[b].mouseOver && h != this.graph._nodes[b]) { + this.graph._nodes[b].mouseOver = !1; + if (this.node_over && this.node_over.onMouseLeave) { + this.node_over.onMouseLeave(a); + } + this.node_over = null; + this.dirty_canvas = !0; + } + } + if (h) { + if (!h.mouseOver && (h.mouseOver = !0, this.node_over = h, this.dirty_canvas = !0, h.onMouseEnter)) { + h.onMouseEnter(a); + } + if (h.onMouseMove) { + h.onMouseMove(a, [a.canvasX - h.pos[0], a.canvasY - h.pos[1]], this); + } + if (this.connecting_node && (f = this._highlight_input || [0, 0], !this.isOverNodeBox(h, a.canvasX, a.canvasY))) { + var c = this.isOverNodeInput(h, a.canvasX, a.canvasY, f); + -1 != c && h.inputs[c] ? e.isValidConnection(this.connecting_output.type, h.inputs[c].type) && (this._highlight_input = f) : this._highlight_input = null; + } + this.canvas && (y(a.canvasX, a.canvasY, h.pos[0] + h.size[0] - 5, h.pos[1] + h.size[1] - 5, 5, 5) ? this.canvas.style.cursor = "se-resize" : this.canvas.style.cursor = "crosshair"); + } else { + f = null; + for (b = 0; b < this.visible_links.length; ++b) { + c = this.visible_links[b]; + var k = c._pos; + if (!(!k || a.canvasX < k[0] - 4 || a.canvasX > k[0] + 4 || a.canvasY < k[1] - 4 || a.canvasY > k[1] + 4)) { + f = c; + break; + } + } + f != this.over_link_center && (this.over_link_center = f, this.dirty_canvas = !0); + this.canvas && (this.canvas.style.cursor = ""); + } + if (this.node_capturing_input && this.node_capturing_input != h && this.node_capturing_input.onMouseMove) { + this.node_capturing_input.onMouseMove(a, [a.canvasX - this.node_capturing_input.pos[0], a.canvasY - this.node_capturing_input.pos[1]], this); + } + if (this.node_dragged && !this.live_mode) { + for (b in this.selected_nodes) { + h = this.selected_nodes[b], h.pos[0] += d[0] / this.ds.scale, h.pos[1] += d[1] / this.ds.scale; + } + this.dirty_bgcanvas = this.dirty_canvas = !0; + } + if (this.resizing_node && !this.live_mode) { + this.resizing_node.size[0] = a.canvasX - this.resizing_node.pos[0]; + this.resizing_node.size[1] = a.canvasY - this.resizing_node.pos[1]; + d = Math.max(this.resizing_node.inputs ? this.resizing_node.inputs.length : 0, this.resizing_node.outputs ? this.resizing_node.outputs.length : 0); + this.resizing_node.size[0] < e.NODE_MIN_WIDTH && (this.resizing_node.size[0] = e.NODE_MIN_WIDTH); + h = this.resizing_node.widgets; + c = 0; + if (h && h.length) { + b = 0; + for (f = h.length; b < f; ++b) { + c = h[b].computeSize ? c + (h[b].computeSize(this.resizing_node.size[0])[1] + 4) : c + (e.NODE_WIDGET_HEIGHT + 4); + } + c += 8; + } + b = d * e.NODE_SLOT_HEIGHT + c; + this.resizing_node.size[1] < b && (this.resizing_node.size[1] = b); + this.canvas.style.cursor = "se-resize"; + this.dirty_bgcanvas = this.dirty_canvas = !0; + } + } + } + } + } + a.preventDefault(); + return !1; + } + }; + l.prototype.processMouseUp = function(a) { + if (this.graph) { + var b = this.getCanvasWindow().document; + l.active_canvas = this; + b.removeEventListener("mousemove", this._mousemove_callback, !0); + this.canvas.addEventListener("mousemove", this._mousemove_callback, !0); + b.removeEventListener("mouseup", this._mouseup_callback, !0); + this.adjustMouseEvent(a); + b = e.getTime(); + a.click_time = b - this.last_mouseclick; + this.last_mouse_dragging = !1; + if (1 == a.which) { + if (this.node_widget && this.processNodeWidgets(this.node_widget[0], this.canvas_mouse, a), this.node_widget = null, this.selected_group && (this.selected_group.move(this.selected_group.pos[0] - Math.round(this.selected_group.pos[0]), this.selected_group.pos[1] - Math.round(this.selected_group.pos[1]), a.ctrlKey), this.selected_group.pos[0] = Math.round(this.selected_group.pos[0]), this.selected_group.pos[1] = Math.round(this.selected_group.pos[1]), this.selected_group._nodes.length && (this.dirty_canvas = + !0), this.selected_group = null), this.selected_group_resizing = !1, this.dragging_rectangle) { + if (this.graph) { + b = this.graph._nodes; + var d = new Float32Array(4); + this.deselectAllNodes(); + var h = Math.abs(this.dragging_rectangle[2]), f = Math.abs(this.dragging_rectangle[3]), c = 0 > this.dragging_rectangle[3] ? this.dragging_rectangle[1] - f : this.dragging_rectangle[1]; + this.dragging_rectangle[0] = 0 > this.dragging_rectangle[2] ? this.dragging_rectangle[0] - h : this.dragging_rectangle[0]; + this.dragging_rectangle[1] = c; + this.dragging_rectangle[2] = h; + this.dragging_rectangle[3] = f; + f = []; + for (c = 0; c < b.length; ++c) { + h = b[c], h.getBounding(d), v(this.dragging_rectangle, d) && f.push(h); + } + f.length && this.selectNodes(f); + } + this.dragging_rectangle = null; + } else { + if (this.connecting_node) { + this.dirty_bgcanvas = this.dirty_canvas = !0; + if (h = this.graph.getNodeOnPos(a.canvasX, a.canvasY, this.visible_nodes)) { + this.connecting_output.type == e.EVENT && this.isOverNodeBox(h, a.canvasX, a.canvasY) ? this.connecting_node.connect(this.connecting_slot, h, e.EVENT) : (b = this.isOverNodeInput(h, a.canvasX, a.canvasY), -1 != b ? this.connecting_node.connect(this.connecting_slot, h, b) : (b = h.getInputInfo(0), this.connecting_output.type == e.EVENT ? this.connecting_node.connect(this.connecting_slot, h, e.EVENT) : b && !b.link && e.isValidConnection(b.type && this.connecting_output.type) && this.connecting_node.connect(this.connecting_slot, + h, 0))); + } + this.connecting_node = this.connecting_pos = this.connecting_output = null; + this.connecting_slot = -1; + } else { + if (this.resizing_node) { + this.dirty_bgcanvas = this.dirty_canvas = !0, this.resizing_node = null; + } else { + if (this.node_dragged) { + (h = this.node_dragged) && 300 > a.click_time && y(a.canvasX, a.canvasY, h.pos[0], h.pos[1] - e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT) && h.collapse(); + this.dirty_bgcanvas = this.dirty_canvas = !0; + this.node_dragged.pos[0] = Math.round(this.node_dragged.pos[0]); + this.node_dragged.pos[1] = Math.round(this.node_dragged.pos[1]); + this.graph.config.align_to_grid && this.node_dragged.alignToGrid(); + if (this.onNodeMoved) { + this.onNodeMoved(this.node_dragged); + } + this.node_dragged = null; + } else { + h = this.graph.getNodeOnPos(a.canvasX, a.canvasY, this.visible_nodes); + !h && 300 > a.click_time && this.deselectAllNodes(); + this.dirty_canvas = !0; + this.dragging_canvas = !1; + if (this.node_over && this.node_over.onMouseUp) { + this.node_over.onMouseUp(a, [a.canvasX - this.node_over.pos[0], a.canvasY - this.node_over.pos[1]], this); + } + if (this.node_capturing_input && this.node_capturing_input.onMouseUp) { + this.node_capturing_input.onMouseUp(a, [a.canvasX - this.node_capturing_input.pos[0], a.canvasY - this.node_capturing_input.pos[1]]); + } + } + } + } + } + } else { + 2 == a.which ? (this.dirty_canvas = !0, this.dragging_canvas = !1) : 3 == a.which && (this.dirty_canvas = !0, this.dragging_canvas = !1); + } + this.graph.change(); + a.stopPropagation(); + a.preventDefault(); + return !1; + } + }; + l.prototype.processMouseWheel = function(a) { + if (this.graph && this.allow_dragcanvas) { + var b = null != a.wheelDeltaY ? a.wheelDeltaY : -60 * a.detail; + this.adjustMouseEvent(a); + var d = this.ds.scale; + 0 < b ? d *= 1.1 : 0 > b && (d *= 1 / 1.1); + this.ds.changeScale(d, [a.localX, a.localY]); + this.graph.change(); + a.preventDefault(); + return !1; + } + }; + l.prototype.isOverNodeBox = function(a, b, d) { + var h = e.NODE_TITLE_HEIGHT; + return y(b, d, a.pos[0] + 2, a.pos[1] + 2 - h, h - 4, h - 4) ? !0 : !1; + }; + l.prototype.isOverNodeInput = function(a, b, d, h) { + if (a.inputs) { + for (var f = 0, e = a.inputs.length; f < e; ++f) { + var c = a.getConnectionPos(!0, f); + if (a.horizontal ? y(b, d, c[0] - 5, c[1] - 10, 10, 20) : y(b, d, c[0] - 10, c[1] - 5, 40, 10)) { + return h && (h[0] = c[0], h[1] = c[1]), f; + } + } + } + return -1; + }; + l.prototype.processKey = function(a) { + if (this.graph) { + var b = !1; + if ("input" != a.target.localName) { + if ("keydown" == a.type) { + if (32 == a.keyCode && (b = this.dragging_canvas = !0), 65 == a.keyCode && a.ctrlKey && (this.selectNodes(), b = !0), "KeyC" == a.code && (a.metaKey || a.ctrlKey) && !a.shiftKey && this.selected_nodes && (this.copyToClipboard(), b = !0), "KeyV" != a.code || !a.metaKey && !a.ctrlKey || a.shiftKey || this.pasteFromClipboard(), 46 != a.keyCode && 8 != a.keyCode || "input" == a.target.localName || "textarea" == a.target.localName || (this.deleteSelectedNodes(), b = !0), this.selected_nodes) { + for (var d in this.selected_nodes) { + if (this.selected_nodes[d].onKeyDown) { + this.selected_nodes[d].onKeyDown(a); + } + } + } + } else { + if ("keyup" == a.type && (32 == a.keyCode && (this.dragging_canvas = !1), this.selected_nodes)) { + for (d in this.selected_nodes) { + if (this.selected_nodes[d].onKeyUp) { + this.selected_nodes[d].onKeyUp(a); + } + } + } + } + this.graph.change(); + if (b) { + return a.preventDefault(), a.stopImmediatePropagation(), !1; + } + } + } + }; + l.prototype.copyToClipboard = function() { + var a = {nodes:[], links:[]}, b = 0, d = [], h; + for (h in this.selected_nodes) { + var f = this.selected_nodes[h]; + f._relative_id = b; + d.push(f); + b += 1; + } + for (h = 0; h < d.length; ++h) { + if (f = d[h], b = f.clone()) { + if (a.nodes.push(b.serialize()), f.inputs && f.inputs.length) { + for (b = 0; b < f.inputs.length; ++b) { + var e = f.inputs[b]; + if (e && null != e.link && (e = this.graph.links[e.link])) { + var c = this.graph.getNodeById(e.origin_id); + c && this.selected_nodes[c.id] && a.links.push([c._relative_id, e.origin_slot, f._relative_id, e.target_slot]); + } + } + } + } else { + console.warn("node type not found: " + f.type); + } + } + localStorage.setItem("litegrapheditor_clipboard", JSON.stringify(a)); + }; + l.prototype.pasteFromClipboard = function() { + var a = localStorage.getItem("litegrapheditor_clipboard"); + if (a) { + a = JSON.parse(a); + for (var b = [], d = 0; d < a.nodes.length; ++d) { + var h = a.nodes[d], f = e.createNode(h.type); + f && (f.configure(h), f.pos[0] += 5, f.pos[1] += 5, this.graph.add(f), b.push(f)); + } + for (d = 0; d < a.links.length; ++d) { + h = a.links[d]; + f = b[h[0]]; + var c = b[h[2]]; + f && c ? f.connect(h[1], c, h[3]) : console.warn("Warning, nodes missing on pasting"); + } + this.selectNodes(b); + } + }; + l.prototype.processDrop = function(a) { + a.preventDefault(); + this.adjustMouseEvent(a); + var b = [a.canvasX, a.canvasY], d = this.graph.getNodeOnPos(b[0], b[1]); + if (d) { + if ((d.onDropFile || d.onDropData) && (b = a.dataTransfer.files) && b.length) { + for (var h = 0; h < b.length; h++) { + var f = a.dataTransfer.files[0], e = f.name; + l.getFileExtension(e); + if (d.onDropFile) { + d.onDropFile(f); + } + if (d.onDropData) { + var c = new FileReader; + c.onload = function(a) { + d.onDropData(a.target.result, e, f); + }; + var k = f.type.split("/")[0]; + "text" == k || "" == k ? c.readAsText(f) : "image" == k ? c.readAsDataURL(f) : c.readAsArrayBuffer(f); + } + } + } + return d.onDropItem && d.onDropItem(event) ? !0 : this.onDropItem ? this.onDropItem(event) : !1; + } + b = null; + this.onDropItem && (b = this.onDropItem(event)); + b || this.checkDropItem(a); + }; + l.prototype.checkDropItem = function(a) { + if (a.dataTransfer.files.length) { + var b = a.dataTransfer.files[0], d = l.getFileExtension(b.name).toLowerCase(); + if (d = e.node_types_by_file_extension[d]) { + if (d = e.createNode(d.type), d.pos = [a.canvasX, a.canvasY], this.graph.add(d), d.onDropFile) { + d.onDropFile(b); + } + } + } + }; + l.prototype.processNodeDblClicked = function(a) { + if (this.onShowNodePanel) { + this.onShowNodePanel(a); + } + if (this.onNodeDblClicked) { + this.onNodeDblClicked(a); + } + this.setDirty(!0); + }; + l.prototype.processNodeSelected = function(a, b) { + this.selectNode(a, b && b.shiftKey); + if (this.onNodeSelected) { + this.onNodeSelected(a); + } + }; + l.prototype.selectNode = function(a, b) { + null == a ? this.deselectAllNodes() : this.selectNodes([a], b); + }; + l.prototype.selectNodes = function(a, b) { + b || this.deselectAllNodes(); + a = a || this.graph._nodes; + for (b = 0; b < a.length; ++b) { + var d = a[b]; + if (!d.is_selected) { + if (!d.is_selected && d.onSelected) { + d.onSelected(); + } + d.is_selected = !0; + this.selected_nodes[d.id] = d; + if (d.inputs) { + for (var h = 0; h < d.inputs.length; ++h) { + this.highlighted_links[d.inputs[h].link] = !0; + } + } + if (d.outputs) { + for (h = 0; h < d.outputs.length; ++h) { + var f = d.outputs[h]; + if (f.links) { + for (var e = 0; e < f.links.length; ++e) { + this.highlighted_links[f.links[e]] = !0; + } + } + } + } + } + } + if (this.onSelectionChange) { + this.onSelectionChange(this.selected_nodes); + } + this.setDirty(!0); + }; + l.prototype.deselectNode = function(a) { + if (a.is_selected) { + if (a.onDeselected) { + a.onDeselected(); + } + a.is_selected = !1; + if (this.onNodeDeselected) { + this.onNodeDeselected(a); + } + if (a.inputs) { + for (var b = 0; b < a.inputs.length; ++b) { + delete this.highlighted_links[a.inputs[b].link]; + } + } + if (a.outputs) { + for (b = 0; b < a.outputs.length; ++b) { + var d = a.outputs[b]; + if (d.links) { + for (var h = 0; h < d.links.length; ++h) { + delete this.highlighted_links[d.links[h]]; + } + } + } + } + } + }; + l.prototype.deselectAllNodes = function() { + if (this.graph) { + for (var a = this.graph._nodes, b = 0, d = a.length; b < d; ++b) { + var h = a[b]; + if (h.is_selected) { + if (h.onDeselected) { + h.onDeselected(); + } + h.is_selected = !1; + if (this.onNodeDeselected) { + this.onNodeDeselected(h); + } + } + } + this.selected_nodes = {}; + this.current_node = null; + this.highlighted_links = {}; + if (this.onSelectionChange) { + this.onSelectionChange(this.selected_nodes); + } + this.setDirty(!0); + } + }; + l.prototype.deleteSelectedNodes = function() { + for (var a in this.selected_nodes) { + var b = this.selected_nodes[a]; + if (b.inputs && b.inputs.length && b.outputs && b.outputs.length && e.isValidConnection(b.inputs[0].type, b.outputs[0].type) && b.inputs[0].link && b.outputs[0].links && b.outputs[0].links.length) { + var d = b.graph.links[b.inputs[0].link], h = b.graph.links[b.outputs[0].links[0]], f = b.getInputNode(0), c = b.getOutputNodes(0)[0]; + f && c && f.connect(d.origin_slot, c, h.target_slot); + } + this.graph.remove(b); + if (this.onNodeDeselected) { + this.onNodeDeselected(b); + } + } + this.selected_nodes = {}; + this.current_node = null; + this.highlighted_links = {}; + this.setDirty(!0); + }; + l.prototype.centerOnNode = function(a) { + this.ds.offset[0] = -a.pos[0] - 0.5 * a.size[0] + 0.5 * this.canvas.width / this.ds.scale; + this.ds.offset[1] = -a.pos[1] - 0.5 * a.size[1] + 0.5 * this.canvas.height / this.ds.scale; + this.setDirty(!0, !0); + }; + l.prototype.adjustMouseEvent = function(a) { + if (this.canvas) { + var b = this.canvas.getBoundingClientRect(); + a.localX = a.clientX - b.left; + a.localY = a.clientY - b.top; + } else { + a.localX = a.clientX, a.localY = a.clientY; + } + a.deltaX = a.localX - this.last_mouse_position[0]; + a.deltaY = a.localY - this.last_mouse_position[1]; + this.last_mouse_position[0] = a.localX; + this.last_mouse_position[1] = a.localY; + a.canvasX = a.localX / this.ds.scale - this.ds.offset[0]; + a.canvasY = a.localY / this.ds.scale - this.ds.offset[1]; + }; + l.prototype.setZoom = function(a, b) { + this.ds.changeScale(a, b); + this.dirty_bgcanvas = this.dirty_canvas = !0; + }; + l.prototype.convertOffsetToCanvas = function(a, b) { + return this.ds.convertOffsetToCanvas(a, b); + }; + l.prototype.convertCanvasToOffset = function(a, b) { + return this.ds.convertCanvasToOffset(a, b); + }; + l.prototype.convertEventToCanvasOffset = function(a) { + var b = this.canvas.getBoundingClientRect(); + return this.convertCanvasToOffset([a.clientX - b.left, a.clientY - b.top]); + }; + l.prototype.bringToFront = function(a) { + var b = this.graph._nodes.indexOf(a); + -1 != b && (this.graph._nodes.splice(b, 1), this.graph._nodes.push(a)); + }; + l.prototype.sendToBack = function(a) { + var b = this.graph._nodes.indexOf(a); + -1 != b && (this.graph._nodes.splice(b, 1), this.graph._nodes.unshift(a)); + }; + var C = new Float32Array(4); + l.prototype.computeVisibleNodes = function(a, b) { + b = b || []; + b.length = 0; + a = a || this.graph._nodes; + for (var d = 0, h = a.length; d < h; ++d) { + var f = a[d]; + (!this.live_mode || f.onDrawBackground || f.onDrawForeground) && v(this.visible_area, f.getBounding(C)) && b.push(f); + } + return b; + }; + l.prototype.draw = function(a, b) { + if (this.canvas) { + var d = e.getTime(); + this.render_time = 0.001 * (d - this.last_draw_time); + this.last_draw_time = d; + this.graph && this.ds.computeVisibleArea(); + (this.dirty_bgcanvas || b || this.always_render_background || this.graph && this.graph._last_trigger_time && 1000 > d - this.graph._last_trigger_time) && this.drawBackCanvas(); + (this.dirty_canvas || a) && this.drawFrontCanvas(); + this.fps = this.render_time ? 1.0 / this.render_time : 0; + this.frame += 1; + } + }; + l.prototype.drawFrontCanvas = function() { + this.dirty_canvas = !1; + this.ctx || (this.ctx = this.bgcanvas.getContext("2d")); + var a = this.ctx; + if (a) { + a.start2D && a.start2D(); + var b = this.canvas; + a.restore(); + a.setTransform(1, 0, 0, 1, 0, 0); + this.dirty_area && (a.save(), a.beginPath(), a.rect(this.dirty_area[0], this.dirty_area[1], this.dirty_area[2], this.dirty_area[3]), a.clip()); + this.clear_background && a.clearRect(0, 0, b.width, b.height); + this.bgcanvas == this.canvas ? this.drawBackCanvas() : a.drawImage(this.bgcanvas, 0, 0); + if (this.onRender) { + this.onRender(b, a); + } + this.show_info && this.renderInfo(a); + if (this.graph) { + a.save(); + this.ds.toCanvasContext(a); + b = this.computeVisibleNodes(null, this.visible_nodes); + for (var d = 0; d < b.length; ++d) { + var h = b[d]; + a.save(); + a.translate(h.pos[0], h.pos[1]); + this.drawNode(h, a); + a.restore(); + } + this.render_execution_order && this.drawExecutionOrder(a); + this.graph.config.links_ontop && (this.live_mode || this.drawConnections(a)); + if (null != this.connecting_pos) { + a.lineWidth = this.connections_width; + switch(this.connecting_output.type) { + case e.EVENT: + b = e.EVENT_LINK_COLOR; + break; + default: + b = e.CONNECTING_LINK_COLOR; + } + this.renderLink(a, this.connecting_pos, [this.canvas_mouse[0], this.canvas_mouse[1]], null, !1, null, b, this.connecting_output.dir || (this.connecting_node.horizontal ? e.DOWN : e.RIGHT), e.CENTER); + a.beginPath(); + this.connecting_output.type === e.EVENT || this.connecting_output.shape === e.BOX_SHAPE ? a.rect(this.connecting_pos[0] - 6 + 0.5, this.connecting_pos[1] - 5 + 0.5, 14, 10) : a.arc(this.connecting_pos[0], this.connecting_pos[1], 4, 0, 2 * Math.PI); + a.fill(); + a.fillStyle = "#ffcc00"; + this._highlight_input && (a.beginPath(), a.arc(this._highlight_input[0], this._highlight_input[1], 6, 0, 2 * Math.PI), a.fill()); + } + this.dragging_rectangle && (a.strokeStyle = "#FFF", a.strokeRect(this.dragging_rectangle[0], this.dragging_rectangle[1], this.dragging_rectangle[2], this.dragging_rectangle[3])); + if (this.over_link_center && this.render_link_tooltip) { + this.drawLinkTooltip(a, this.over_link_center); + } else { + if (this.onDrawLinkTooltip) { + this.onDrawLinkTooltip(a, null); + } + } + if (this.onDrawForeground) { + this.onDrawForeground(a, this.visible_rect); + } + a.restore(); + } + if (this.onDrawOverlay) { + this.onDrawOverlay(a); + } + this.dirty_area && a.restore(); + a.finish2D && a.finish2D(); + } + }; + l.prototype.renderInfo = function(a, b, d) { + b = b || 0; + d = d || 0; + a.save(); + a.translate(b, d); + a.font = "10px Arial"; + a.fillStyle = "#888"; + this.graph ? (a.fillText("T: " + this.graph.globaltime.toFixed(2) + "s", 5, 13), a.fillText("I: " + this.graph.iteration, 5, 26), a.fillText("N: " + this.graph._nodes.length + " [" + this.visible_nodes.length + "]", 5, 39), a.fillText("V: " + this.graph._version, 5, 52), a.fillText("FPS:" + this.fps.toFixed(2), 5, 65)) : a.fillText("No graph selected", 5, 13); + a.restore(); + }; + l.prototype.drawBackCanvas = function() { + var a = this.bgcanvas; + if (a.width != this.canvas.width || a.height != this.canvas.height) { + a.width = this.canvas.width, a.height = this.canvas.height; + } + this.bgctx || (this.bgctx = this.bgcanvas.getContext("2d")); + var b = this.bgctx; + b.start && b.start(); + this.clear_background && b.clearRect(0, 0, a.width, a.height); + if (this._graph_stack && this._graph_stack.length) { + b.save(); + var d = this.graph._subgraph_node; + b.strokeStyle = d.bgcolor; + b.lineWidth = 10; + b.strokeRect(1, 1, a.width - 2, a.height - 2); + b.lineWidth = 1; + b.font = "40px Arial"; + b.textAlign = "center"; + b.fillStyle = d.bgcolor || "#AAA"; + for (var h = "", f = 1; f < this._graph_stack.length; ++f) { + h += this._graph_stack[f]._subgraph_node.getTitle() + " >> "; + } + b.fillText(h + d.getTitle(), 0.5 * a.width, 40); + b.restore(); + } + d = !1; + this.onRenderBackground && (d = this.onRenderBackground(a, b)); + b.restore(); + b.setTransform(1, 0, 0, 1, 0, 0); + this.visible_links.length = 0; + if (this.graph) { + b.save(); + this.ds.toCanvasContext(b); + if (this.background_image && 0.5 < this.ds.scale && !d) { + b.globalAlpha = this.zoom_modify_alpha ? (1.0 - 0.5 / this.ds.scale) * this.editor_alpha : this.editor_alpha; + b.imageSmoothingEnabled = b.mozImageSmoothingEnabled = b.imageSmoothingEnabled = !1; + if (!this._bg_img || this._bg_img.name != this.background_image) { + this._bg_img = new Image; + this._bg_img.name = this.background_image; + this._bg_img.src = this.background_image; + var e = this; + this._bg_img.onload = function() { + e.draw(!0, !0); + }; + } + d = null; + null == this._pattern && 0 < this._bg_img.width ? (d = b.createPattern(this._bg_img, "repeat"), this._pattern_img = this._bg_img, this._pattern = d) : d = this._pattern; + d && (b.fillStyle = d, b.fillRect(this.visible_area[0], this.visible_area[1], this.visible_area[2], this.visible_area[3]), b.fillStyle = "transparent"); + b.globalAlpha = 1.0; + b.imageSmoothingEnabled = b.mozImageSmoothingEnabled = b.imageSmoothingEnabled = !0; + } + this.graph._groups.length && !this.live_mode && this.drawGroups(a, b); + if (this.onDrawBackground) { + this.onDrawBackground(b, this.visible_area); + } + this.onBackgroundRender && (console.error("WARNING! onBackgroundRender deprecated, now is named onDrawBackground "), this.onBackgroundRender = null); + this.render_canvas_border && (b.strokeStyle = "#235", b.strokeRect(0, 0, a.width, a.height)); + this.render_connections_shadows ? (b.shadowColor = "#000", b.shadowOffsetX = 0, b.shadowOffsetY = 0, b.shadowBlur = 6) : b.shadowColor = "rgba(0,0,0,0)"; + this.live_mode || this.drawConnections(b); + b.shadowColor = "rgba(0,0,0,0)"; + b.restore(); + } + b.finish && b.finish(); + this.dirty_bgcanvas = !1; + this.dirty_canvas = !0; + }; + var D = new Float32Array(2); + l.prototype.drawNode = function(a, b) { + this.current_node = a; + var d = a.color || a.constructor.color || e.NODE_DEFAULT_COLOR, h = a.bgcolor || a.constructor.bgcolor || e.NODE_DEFAULT_BGCOLOR, f = 0.6 > this.ds.scale; + if (this.live_mode) { + if (!a.flags.collapsed && (b.shadowColor = "transparent", a.onDrawForeground)) { + a.onDrawForeground(b, this, this.canvas); + } + } else { + var c = this.editor_alpha; + b.globalAlpha = c; + this.render_shadows && !f ? (b.shadowColor = e.DEFAULT_SHADOW_COLOR, b.shadowOffsetX = 2 * this.ds.scale, b.shadowOffsetY = 2 * this.ds.scale, b.shadowBlur = 3 * this.ds.scale) : b.shadowColor = "transparent"; + if (!a.flags.collapsed || !a.onDrawCollapsed || 1 != a.onDrawCollapsed(b, this)) { + var k = a._shape || e.BOX_SHAPE; + D.set(a.size); + var n = a.horizontal; + if (a.flags.collapsed) { + b.font = this.inner_text_font; + var q = a.getTitle ? a.getTitle() : a.title; + null != q && (a._collapsed_width = Math.min(a.size[0], b.measureText(q).width + 2 * e.NODE_TITLE_HEIGHT), D[0] = a._collapsed_width, D[1] = 0); + } + a.clip_area && (b.save(), b.beginPath(), k == e.BOX_SHAPE ? b.rect(0, 0, D[0], D[1]) : k == e.ROUND_SHAPE ? b.roundRect(0, 0, D[0], D[1], 10) : k == e.CIRCLE_SHAPE && b.arc(0.5 * D[0], 0.5 * D[1], 0.5 * D[0], 0, 2 * Math.PI), b.clip()); + a.has_errors && (h = "red"); + this.drawNodeShape(a, b, D, d, h, a.is_selected, a.mouseOver); + b.shadowColor = "transparent"; + if (a.onDrawForeground) { + a.onDrawForeground(b, this, this.canvas); + } + b.textAlign = n ? "center" : "left"; + b.font = this.inner_text_font; + h = !f; + k = this.connecting_output; + b.lineWidth = 1; + q = 0; + var l = new Float32Array(2); + if (!a.flags.collapsed) { + if (a.inputs) { + for (d = 0; d < a.inputs.length; d++) { + var g = a.inputs[d]; + b.globalAlpha = c; + this.connecting_node && !e.isValidConnection(g.type, k.type) && (b.globalAlpha = 0.4 * c); + b.fillStyle = null != g.link ? g.color_on || this.default_connection_color.input_on : g.color_off || this.default_connection_color.input_off; + var r = a.getConnectionPos(!0, d, l); + r[0] -= a.pos[0]; + r[1] -= a.pos[1]; + q < r[1] + 0.5 * e.NODE_SLOT_HEIGHT && (q = r[1] + 0.5 * e.NODE_SLOT_HEIGHT); + b.beginPath(); + g.type === e.EVENT || g.shape === e.BOX_SHAPE ? n ? b.rect(r[0] - 5 + 0.5, r[1] - 8 + 0.5, 10, 14) : b.rect(r[0] - 6 + 0.5, r[1] - 5 + 0.5, 14, 10) : g.shape === e.ARROW_SHAPE ? (b.moveTo(r[0] + 8, r[1] + 0.5), b.lineTo(r[0] - 4, r[1] + 6 + 0.5), b.lineTo(r[0] - 4, r[1] - 6 + 0.5), b.closePath()) : f ? b.rect(r[0] - 4, r[1] - 4, 8, 8) : b.arc(r[0], r[1], 4, 0, 2 * Math.PI); + b.fill(); + if (h) { + var m = null != g.label ? g.label : g.name; + m && (b.fillStyle = e.NODE_TEXT_COLOR, n || g.dir == e.UP ? b.fillText(m, r[0], r[1] - 10) : b.fillText(m, r[0] + 10, r[1] + 5)); + } + } + } + this.connecting_node && (b.globalAlpha = 0.4 * c); + b.textAlign = n ? "center" : "right"; + b.strokeStyle = "black"; + if (a.outputs) { + for (d = 0; d < a.outputs.length; d++) { + if (g = a.outputs[d], r = a.getConnectionPos(!1, d, l), r[0] -= a.pos[0], r[1] -= a.pos[1], q < r[1] + 0.5 * e.NODE_SLOT_HEIGHT && (q = r[1] + 0.5 * e.NODE_SLOT_HEIGHT), b.fillStyle = g.links && g.links.length ? g.color_on || this.default_connection_color.output_on : g.color_off || this.default_connection_color.output_off, b.beginPath(), g.type === e.EVENT || g.shape === e.BOX_SHAPE ? n ? b.rect(r[0] - 5 + 0.5, r[1] - 8 + 0.5, 10, 14) : b.rect(r[0] - 6 + 0.5, r[1] - 5 + 0.5, 14, 10) : + g.shape === e.ARROW_SHAPE ? (b.moveTo(r[0] + 8, r[1] + 0.5), b.lineTo(r[0] - 4, r[1] + 6 + 0.5), b.lineTo(r[0] - 4, r[1] - 6 + 0.5), b.closePath()) : f ? b.rect(r[0] - 4, r[1] - 4, 8, 8) : b.arc(r[0], r[1], 4, 0, 2 * Math.PI), b.fill(), f || b.stroke(), h && (m = null != g.label ? g.label : g.name)) { + b.fillStyle = e.NODE_TEXT_COLOR, n || g.dir == e.DOWN ? b.fillText(m, r[0], r[1] - 8) : b.fillText(m, r[0] - 10, r[1] + 5); + } + } + } + b.textAlign = "left"; + b.globalAlpha = 1; + if (a.widgets) { + g = q; + if (n || a.widgets_up) { + g = 2; + } + null != a.widgets_start_y && (g = a.widgets_start_y); + this.drawNodeWidgets(a, g, b, this.node_widget && this.node_widget[0] == a ? this.node_widget[1] : null); + } + } else { + if (this.render_collapsed_slots) { + f = c = null; + if (a.inputs) { + for (d = 0; d < a.inputs.length; d++) { + if (g = a.inputs[d], null != g.link) { + c = g; + break; + } + } + } + if (a.outputs) { + for (d = 0; d < a.outputs.length; d++) { + g = a.outputs[d], g.links && g.links.length && (f = g); + } + } + c && (c = 0, d = -0.5 * e.NODE_TITLE_HEIGHT, n && (c = 0.5 * a._collapsed_width, d = -e.NODE_TITLE_HEIGHT), b.fillStyle = "#686", b.beginPath(), g.type === e.EVENT || g.shape === e.BOX_SHAPE ? b.rect(c - 7 + 0.5, d - 4, 14, 8) : g.shape === e.ARROW_SHAPE ? (b.moveTo(c + 8, d), b.lineTo(c + -4, d - 4), b.lineTo(c + -4, d + 4), b.closePath()) : b.arc(c, d, 4, 0, 2 * Math.PI), b.fill()); + f && (c = a._collapsed_width, d = -0.5 * e.NODE_TITLE_HEIGHT, n && (c = 0.5 * a._collapsed_width, d = 0), b.fillStyle = "#686", b.strokeStyle = "black", b.beginPath(), g.type === e.EVENT || g.shape === e.BOX_SHAPE ? b.rect(c - 7 + 0.5, d - 4, 14, 8) : g.shape === e.ARROW_SHAPE ? (b.moveTo(c + 6, d), b.lineTo(c - 6, d - 4), b.lineTo(c - 6, d + 4), b.closePath()) : b.arc(c, d, 4, 0, 2 * Math.PI), b.fill()); + } + } + a.clip_area && b.restore(); + b.globalAlpha = 1.0; + } + } + }; + l.prototype.drawLinkTooltip = function(a, b) { + var d = b._pos; + a.fillStyle = "black"; + a.beginPath(); + a.arc(d[0], d[1], 3, 0, 2 * Math.PI); + a.fill(); + if (null != b.data && (!this.onDrawLinkTooltip || 1 != this.onDrawLinkTooltip(a, b, this)) && (b = b.data, b = b.constructor === Number ? b.toFixed(2) : b.constructor === String ? '"' + b + '"' : b.constructor === Boolean ? String(b) : b.toToolTip ? b.toToolTip() : "[" + b.constructor.name + "]", null != b)) { + b = b.substr(0, 30); + a.font = "14px Courier New"; + var h = a.measureText(b).width + 20; + a.shadowColor = "black"; + a.shadowOffsetX = 2; + a.shadowOffsetY = 2; + a.shadowBlur = 3; + a.fillStyle = "#454"; + a.beginPath(); + a.roundRect(d[0] - 0.5 * h, d[1] - 15 - 24, h, 24, 3, 3); + a.moveTo(d[0] - 10, d[1] - 15); + a.lineTo(d[0] + 10, d[1] - 15); + a.lineTo(d[0], d[1] - 5); + a.fill(); + a.shadowColor = "transparent"; + a.textAlign = "center"; + a.fillStyle = "#CEC"; + a.fillText(b, d[0], d[1] - 15 - 24 * 0.3); + } + }; + var t = new Float32Array(4); + l.prototype.drawNodeShape = function(a, b, d, h, f, c, k) { + b.strokeStyle = h; + b.fillStyle = f; + f = e.NODE_TITLE_HEIGHT; + var n = 0.5 > this.ds.scale, q = a._shape || a.constructor.shape || e.ROUND_SHAPE, x = a.constructor.title_mode, g = !0; + x == e.TRANSPARENT_TITLE ? g = !1 : x == e.AUTOHIDE_TITLE && k && (g = !0); + t[0] = 0; + t[1] = g ? -f : 0; + t[2] = d[0] + 1; + t[3] = g ? d[1] + f : d[1]; + k = b.globalAlpha; + b.beginPath(); + q == e.BOX_SHAPE || n ? b.fillRect(t[0], t[1], t[2], t[3]) : q == e.ROUND_SHAPE || q == e.CARD_SHAPE ? b.roundRect(t[0], t[1], t[2], t[3], this.round_radius, q == e.CARD_SHAPE ? 0 : this.round_radius) : q == e.CIRCLE_SHAPE && b.arc(0.5 * d[0], 0.5 * d[1], 0.5 * d[0], 0, 2 * Math.PI); + b.fill(); + a.flags.collapsed || (b.shadowColor = "transparent", b.fillStyle = "rgba(0,0,0,0.2)", b.fillRect(0, -1, t[2], 2)); + b.shadowColor = "transparent"; + if (a.onDrawBackground) { + a.onDrawBackground(b, this, this.canvas); + } + if (g || x == e.TRANSPARENT_TITLE) { + if (a.onDrawTitleBar) { + a.onDrawTitleBar(b, f, d, this.ds.scale, h); + } else { + if (x != e.TRANSPARENT_TITLE && (a.constructor.title_color || this.render_title_colored)) { + g = a.constructor.title_color || h; + a.flags.collapsed && (b.shadowColor = e.DEFAULT_SHADOW_COLOR); + if (this.use_gradients) { + var r = l.gradients[g]; + r || (r = l.gradients[g] = b.createLinearGradient(0, 0, 400, 0), r.addColorStop(0, g), r.addColorStop(1, "#000")); + b.fillStyle = r; + } else { + b.fillStyle = g; + } + b.beginPath(); + q == e.BOX_SHAPE || n ? b.rect(0, -f, d[0] + 1, f) : (q == e.ROUND_SHAPE || q == e.CARD_SHAPE) && b.roundRect(0, -f, d[0] + 1, f, this.round_radius, a.flags.collapsed ? this.round_radius : 0); + b.fill(); + b.shadowColor = "transparent"; + } + } + if (a.onDrawTitleBox) { + a.onDrawTitleBox(b, f, d, this.ds.scale); + } else { + q == e.ROUND_SHAPE || q == e.CIRCLE_SHAPE || q == e.CARD_SHAPE ? (n && (b.fillStyle = "black", b.beginPath(), b.arc(0.5 * f, -0.5 * f, 6, 0, 2 * Math.PI), b.fill()), b.fillStyle = a.boxcolor || e.NODE_DEFAULT_BOXCOLOR, n ? b.fillRect(0.5 * f - 5, -0.5 * f - 5, 10, 10) : (b.beginPath(), b.arc(0.5 * f, -0.5 * f, 5, 0, 2 * Math.PI), b.fill())) : (n && (b.fillStyle = "black", b.fillRect(0.5 * (f - 10) - 1, -0.5 * (f + 10) - 1, 12, 12)), b.fillStyle = a.boxcolor || e.NODE_DEFAULT_BOXCOLOR, b.fillRect(0.5 * + (f - 10), -0.5 * (f + 10), 10, 10)); + } + b.globalAlpha = k; + if (a.onDrawTitleText) { + a.onDrawTitleText(b, f, d, this.ds.scale, this.title_text_font, c); + } + !n && (b.font = this.title_text_font, n = String(a.getTitle())) && (b.fillStyle = c ? "white" : a.constructor.title_text_color || this.node_title_color, a.flags.collapsed ? (b.textAlign = "left", b.measureText(n), b.fillText(n.substr(0, 20), f, e.NODE_TITLE_TEXT_Y - f), b.textAlign = "left") : (b.textAlign = "left", b.fillText(n, f, e.NODE_TITLE_TEXT_Y - f))); + if (a.onDrawTitle) { + a.onDrawTitle(b); + } + } + if (c) { + if (a.onBounding) { + a.onBounding(t); + } + x == e.TRANSPARENT_TITLE && (t[1] -= f, t[3] += f); + b.lineWidth = 1; + b.globalAlpha = 0.8; + b.beginPath(); + q == e.BOX_SHAPE ? b.rect(-6 + t[0], -6 + t[1], 12 + t[2], 12 + t[3]) : q == e.ROUND_SHAPE || q == e.CARD_SHAPE && a.flags.collapsed ? b.roundRect(-6 + t[0], -6 + t[1], 12 + t[2], 12 + t[3], 2 * this.round_radius) : q == e.CARD_SHAPE ? b.roundRect(-6 + t[0], -6 + t[1], 12 + t[2], 12 + t[3], 2 * this.round_radius, 2) : q == e.CIRCLE_SHAPE && b.arc(0.5 * d[0], 0.5 * d[1], 0.5 * d[0] + 6, 0, 2 * Math.PI); + b.strokeStyle = "#FFF"; + b.stroke(); + b.strokeStyle = h; + b.globalAlpha = 1; + } + }; + var G = new Float32Array(4), n = new Float32Array(4), q = new Float32Array(2), k = new Float32Array(2); + l.prototype.drawConnections = function(a) { + var b = e.getTime(), d = this.visible_area; + G[0] = d[0] - 20; + G[1] = d[1] - 20; + G[2] = d[2] + 40; + G[3] = d[3] + 40; + a.lineWidth = this.connections_width; + a.fillStyle = "#AAA"; + a.strokeStyle = "#AAA"; + a.globalAlpha = this.editor_alpha; + d = this.graph._nodes; + for (var h = 0, f = d.length; h < f; ++h) { + var c = d[h]; + if (c.inputs && c.inputs.length) { + for (var l = 0; l < c.inputs.length; ++l) { + var g = c.inputs[l]; + if (g && null != g.link && (g = this.graph.links[g.link])) { + var m = this.graph.getNodeById(g.origin_id); + if (null != m) { + var u = g.origin_slot; + var A = -1 == u ? [m.pos[0] + 10, m.pos[1] + 10] : m.getConnectionPos(!1, u, q); + var r = c.getConnectionPos(!0, l, k); + n[0] = A[0]; + n[1] = A[1]; + n[2] = r[0] - A[0]; + n[3] = r[1] - A[1]; + 0 > n[2] && (n[0] += n[2], n[2] = Math.abs(n[2])); + 0 > n[3] && (n[1] += n[3], n[3] = Math.abs(n[3])); + if (v(n, G)) { + var K = m.outputs[u]; + u = c.inputs[l]; + if (K && u && (m = K.dir || (m.horizontal ? e.DOWN : e.RIGHT), u = u.dir || (c.horizontal ? e.UP : e.LEFT), this.renderLink(a, A, r, g, !1, 0, null, m, u), g && g._last_time && 1000 > b - g._last_time)) { + K = 2.0 - 0.002 * (b - g._last_time); + var p = a.globalAlpha; + a.globalAlpha = p * K; + this.renderLink(a, A, r, g, !0, K, "white", m, u); + a.globalAlpha = p; + } + } + } + } + } + } + } + a.globalAlpha = 1; + }; + l.prototype.renderLink = function(a, b, d, h, f, c, k, n, q, g) { + h && this.visible_links.push(h); + !k && h && (k = h.color || l.link_type_colors[h.type]); + k || (k = this.default_link_color); + null != h && this.highlighted_links[h.id] && (k = "#FFF"); + n = n || e.RIGHT; + q = q || e.LEFT; + var x = B(b, d); + this.render_connections_border && 0.6 < this.ds.scale && (a.lineWidth = this.connections_width + 4); + a.lineJoin = "round"; + g = g || 1; + 1 < g && (a.lineWidth = 0.5); + a.beginPath(); + for (var r = 0; r < g; r += 1) { + var m = 5 * (r - 0.5 * (g - 1)); + if (this.links_render_mode == e.SPLINE_LINK) { + a.moveTo(b[0], b[1] + m); + var H = 0, u = 0, p = 0, I = 0; + switch(n) { + case e.LEFT: + H = -0.25 * x; + break; + case e.RIGHT: + H = 0.25 * x; + break; + case e.UP: + u = -0.25 * x; + break; + case e.DOWN: + u = 0.25 * x; + } + switch(q) { + case e.LEFT: + p = -0.25 * x; + break; + case e.RIGHT: + p = 0.25 * x; + break; + case e.UP: + I = -0.25 * x; + break; + case e.DOWN: + I = 0.25 * x; + } + a.bezierCurveTo(b[0] + H, b[1] + u + m, d[0] + p, d[1] + I + m, d[0], d[1] + m); + } else { + if (this.links_render_mode == e.LINEAR_LINK) { + a.moveTo(b[0], b[1] + m); + I = p = u = H = 0; + switch(n) { + case e.LEFT: + H = -1; + break; + case e.RIGHT: + H = 1; + break; + case e.UP: + u = -1; + break; + case e.DOWN: + u = 1; + } + switch(q) { + case e.LEFT: + p = -1; + break; + case e.RIGHT: + p = 1; + break; + case e.UP: + I = -1; + break; + case e.DOWN: + I = 1; + } + a.lineTo(b[0] + 15 * H, b[1] + 15 * u + m); + a.lineTo(d[0] + 15 * p, d[1] + 15 * I + m); + a.lineTo(d[0], d[1] + m); + } else { + if (this.links_render_mode == e.STRAIGHT_LINK) { + a.moveTo(b[0], b[1]), m = b[0], H = b[1], u = d[0], p = d[1], n == e.RIGHT ? m += 10 : H += 10, q == e.LEFT ? u -= 10 : p -= 10, a.lineTo(m, H), a.lineTo(0.5 * (m + u), H), a.lineTo(0.5 * (m + u), p), a.lineTo(u, p), a.lineTo(d[0], d[1]); + } else { + return; + } + } + } + } + this.render_connections_border && 0.6 < this.ds.scale && !f && (a.strokeStyle = "rgba(0,0,0,0.5)", a.stroke()); + a.lineWidth = this.connections_width; + a.fillStyle = a.strokeStyle = k; + a.stroke(); + f = this.computeConnectionPoint(b, d, 0.5, n, q); + h && h._pos && (h._pos[0] = f[0], h._pos[1] = f[1]); + 0.6 <= this.ds.scale && this.highquality_render && q != e.CENTER && (this.render_connection_arrows && (r = this.computeConnectionPoint(b, d, 0.25, n, q), x = this.computeConnectionPoint(b, d, 0.26, n, q), h = this.computeConnectionPoint(b, d, 0.75, n, q), g = this.computeConnectionPoint(b, d, 0.76, n, q), this.render_curved_connections ? (x = -Math.atan2(x[0] - r[0], x[1] - r[1]), g = -Math.atan2(g[0] - h[0], g[1] - h[1])) : g = x = d[1] > b[1] ? 0 : Math.PI, a.save(), a.translate(r[0], r[1]), + a.rotate(x), a.beginPath(), a.moveTo(-5, -3), a.lineTo(0, 7), a.lineTo(5, -3), a.fill(), a.restore(), a.save(), a.translate(h[0], h[1]), a.rotate(g), a.beginPath(), a.moveTo(-5, -3), a.lineTo(0, 7), a.lineTo(5, -3), a.fill(), a.restore()), a.beginPath(), a.arc(f[0], f[1], 5, 0, 2 * Math.PI), a.fill()); + if (c) { + for (a.fillStyle = k, r = 0; 5 > r; ++r) { + c = (0.001 * e.getTime() + 0.2 * r) % 1, f = this.computeConnectionPoint(b, d, c, n, q), a.beginPath(), a.arc(f[0], f[1], 5, 0, 2 * Math.PI), a.fill(); + } + } + }; + l.prototype.computeConnectionPoint = function(a, b, d, h, f) { + h = h || e.RIGHT; + f = f || e.LEFT; + var c = B(a, b), k = [a[0], a[1]], n = [b[0], b[1]]; + switch(h) { + case e.LEFT: + k[0] += -0.25 * c; + break; + case e.RIGHT: + k[0] += 0.25 * c; + break; + case e.UP: + k[1] += -0.25 * c; + break; + case e.DOWN: + k[1] += 0.25 * c; + } + switch(f) { + case e.LEFT: + n[0] += -0.25 * c; + break; + case e.RIGHT: + n[0] += 0.25 * c; + break; + case e.UP: + n[1] += -0.25 * c; + break; + case e.DOWN: + n[1] += 0.25 * c; + } + h = (1 - d) * (1 - d) * (1 - d); + f = 3 * (1 - d) * (1 - d) * d; + c = 3 * (1 - d) * d * d; + d *= d * d; + return [h * a[0] + f * k[0] + c * n[0] + d * b[0], h * a[1] + f * k[1] + c * n[1] + d * b[1]]; + }; + l.prototype.drawExecutionOrder = function(a) { + a.shadowColor = "transparent"; + a.globalAlpha = 0.25; + a.textAlign = "center"; + a.strokeStyle = "white"; + a.globalAlpha = 0.75; + for (var b = this.visible_nodes, d = 0; d < b.length; ++d) { + var h = b[d]; + a.fillStyle = "black"; + a.fillRect(h.pos[0] - e.NODE_TITLE_HEIGHT, h.pos[1] - e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT); + 0 == h.order && a.strokeRect(h.pos[0] - e.NODE_TITLE_HEIGHT + 0.5, h.pos[1] - e.NODE_TITLE_HEIGHT + 0.5, e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT); + a.fillStyle = "#FFF"; + a.fillText(h.order, h.pos[0] + -0.5 * e.NODE_TITLE_HEIGHT, h.pos[1] - 6); + } + a.globalAlpha = 1; + }; + l.prototype.drawNodeWidgets = function(a, b, d, h) { + if (!a.widgets || !a.widgets.length) { + return 0; + } + var f = a.size[0], c = a.widgets; + b += 2; + var k = e.NODE_WIDGET_HEIGHT, n = 0.5 < this.ds.scale; + d.save(); + d.globalAlpha = this.editor_alpha; + for (var q = e.WIDGET_OUTLINE_COLOR, l = e.WIDGET_BGCOLOR, g = e.WIDGET_TEXT_COLOR, r = e.WIDGET_SECONDARY_TEXT_COLOR, m = 0; m < c.length; ++m) { + var u = k, p = c[m], t = b; + p.y && (t = p.y); + p.last_y = t; + d.strokeStyle = q; + d.fillStyle = "#222"; + d.textAlign = "left"; + p.disabled && (d.globalAlpha *= 0.5); + switch(p.type) { + case "button": + p.clicked && (d.fillStyle = "#AAA", p.clicked = !1, this.dirty_canvas = !0); + d.fillRect(15, t, f - 30, k); + n && d.strokeRect(15, t, f - 30, k); + n && (d.textAlign = "center", d.fillStyle = g, d.fillText(p.name, 0.5 * f, t + 0.7 * k)); + break; + case "toggle": + d.textAlign = "left"; + d.strokeStyle = q; + d.fillStyle = l; + d.beginPath(); + n ? d.roundRect(15, b, f - 30, k, 0.5 * k) : d.rect(15, b, f - 30, k); + d.fill(); + n && d.stroke(); + d.fillStyle = p.value ? "#89A" : "#333"; + d.beginPath(); + d.arc(f - 30, t + 0.5 * k, 0.36 * k, 0, 2 * Math.PI); + d.fill(); + n && (d.fillStyle = r, null != p.name && d.fillText(p.name, 30, t + 0.7 * k), d.fillStyle = p.value ? g : r, d.textAlign = "right", d.fillText(p.value ? p.options.on || "true" : p.options.off || "false", f - 40, t + 0.7 * k)); + break; + case "slider": + d.fillStyle = l; + d.fillRect(15, t, f - 30, k); + var C = p.options.max - p.options.min, v = (p.value - p.options.min) / C; + d.fillStyle = h == p ? "#89A" : "#678"; + d.fillRect(15, t, v * (f - 30), k); + n && d.strokeRect(15, t, f - 30, k); + p.marker && (C = (p.marker - p.options.min) / C, d.fillStyle = "#AA9", d.fillRect(15 + C * (f - 30), t, 2, k)); + n && (d.textAlign = "center", d.fillStyle = g, d.fillText(p.name + " " + Number(p.value).toFixed(3), 0.5 * f, t + 0.7 * k)); + break; + case "number": + case "combo": + d.textAlign = "left"; + d.strokeStyle = q; + d.fillStyle = l; + d.beginPath(); + n ? d.roundRect(15, b, f - 30, k, 0.5 * k) : d.rect(15, b, f - 30, k); + d.fill(); + n && (d.stroke(), d.fillStyle = g, d.beginPath(), d.moveTo(31, b + 5), d.lineTo(21, b + 0.5 * k), d.lineTo(31, b + k - 5), d.fill(), d.beginPath(), d.moveTo(f - 15 - 16, b + 5), d.lineTo(f - 15 - 6, b + 0.5 * k), d.lineTo(f - 15 - 16, b + k - 5), d.fill(), d.fillStyle = r, d.fillText(p.name, 35, t + 0.7 * k), d.fillStyle = g, d.textAlign = "right", "number" == p.type ? d.fillText(Number(p.value).toFixed(void 0 !== p.options.precision ? p.options.precision : 3), f - 30 - 20, t + 0.7 * k) : + (C = p.value, p.options.values && (v = p.options.values, v.constructor === Function && (v = v()), v && v.constructor !== Array && (C = v[p.value])), d.fillText(C, f - 30 - 20, t + 0.7 * k))); + break; + case "string": + case "text": + d.textAlign = "left"; + d.strokeStyle = q; + d.fillStyle = l; + d.beginPath(); + n ? d.roundRect(15, b, f - 30, k, 0.5 * k) : d.rect(15, b, f - 30, k); + d.fill(); + n && (d.save(), d.beginPath(), d.rect(15, b, f - 30, k), d.clip(), d.stroke(), d.fillStyle = r, null != p.name && d.fillText(p.name, 30, t + 0.7 * k), d.fillStyle = g, d.textAlign = "right", d.fillText(String(p.value).substr(0, 30), f - 30, t + 0.7 * k), d.restore()); + break; + default: + p.draw && (u = p.draw(d, a, f, t, k) || k); + } + b += u + 4; + d.globalAlpha = this.editor_alpha; + } + d.restore(); + d.textAlign = "left"; + }; + l.prototype.processNodeWidgets = function(a, b, d, c) { + function f(f, h) { + f.value = h; + f.options && f.options.property && void 0 !== a.properties[f.options.property] && a.setProperty(f.options.property, h); + f.callback && f.callback(f.value, q, a, b, d); + } + if (!a.widgets || !a.widgets.length) { + return null; + } + for (var h = b[0] - a.pos[0], k = b[1] - a.pos[1], n = a.size[0], q = this, l = this.getCanvasWindow(), g = 0; g < a.widgets.length; ++g) { + var r = a.widgets[g]; + if (r && !r.disabled && (r == c || 6 < h && h < n - 12 && k > r.last_y && k < r.last_y + e.NODE_WIDGET_HEIGHT)) { + switch(r.type) { + case "button": + if ("mousemove" === d.type) { + break; + } + r.callback && setTimeout(function() { + r.callback(r, q, a, b, d); + }, 20); + this.dirty_canvas = r.clicked = !0; + break; + case "slider": + l = Math.clamp((h - 10) / (n - 20), 0, 1); + r.value = r.options.min + (r.options.max - r.options.min) * l; + r.callback && setTimeout(function() { + f(r, r.value); + }, 20); + this.dirty_canvas = !0; + break; + case "number": + case "combo": + c = r.value; + if ("mousemove" == d.type && "number" == r.type) { + r.value += 0.1 * d.deltaX * (r.options.step || 1), null != r.options.min && r.value < r.options.min && (r.value = r.options.min), null != r.options.max && r.value > r.options.max && (r.value = r.options.max); + } else { + if ("mousedown" == d.type) { + var m = r.options.values; + m && m.constructor === Function && (m = r.options.values(r, a)); + var p = null; + "number" != r.type && (p = m.constructor === Array ? m : Object.keys(m)); + h = 40 > h ? -1 : h > n - 40 ? 1 : 0; + if ("number" == r.type) { + r.value += 0.1 * h * (r.options.step || 1), null != r.options.min && r.value < r.options.min && (r.value = r.options.min), null != r.options.max && r.value > r.options.max && (r.value = r.options.max); + } else { + if (h) { + l = -1, l = m.constructor === Object ? p.indexOf(String(r.value)) + h : p.indexOf(r.value) + h, l >= p.length && (l = p.length - 1), 0 > l && (l = 0), r.value = m.constructor === Array ? m[l] : l; + } else { + var u = m != p ? Object.values(m) : m; + new e.ContextMenu(u, {scale:Math.max(1, this.ds.scale), event:d, className:"dark", callback:function(a, b, d) { + m != p && (a = u.indexOf(a)); + this.value = a; + f(this, a); + q.dirty_canvas = !0; + return !1; + }.bind(r)}, l); + } + } + } else { + "mouseup" == d.type && "number" == r.type && (h = 40 > h ? -1 : h > n - 40 ? 1 : 0, 200 > d.click_time && 0 == h && this.prompt("Value", r.value, function(a) { + this.value = Number(a); + f(this, this.value); + }.bind(r), d)); + } + } + c != r.value && setTimeout(function() { + f(this, this.value); + }.bind(r), 20); + this.dirty_canvas = !0; + break; + case "toggle": + "mousedown" == d.type && (r.value = !r.value, setTimeout(function() { + f(r, r.value); + }, 20)); + break; + case "string": + case "text": + "mousedown" == d.type && this.prompt("Value", r.value, function(a) { + this.value = a; + f(this, a); + }.bind(r), d); + break; + default: + r.mouse && r.mouse(ctx, d, [h, k], a); + } + return r; + } + } + return null; + }; + l.prototype.drawGroups = function(a, b) { + if (this.graph) { + a = this.graph._groups; + b.save(); + b.globalAlpha = 0.5 * this.editor_alpha; + for (var d = 0; d < a.length; ++d) { + var h = a[d]; + if (v(this.visible_area, h._bounding)) { + b.fillStyle = h.color || "#335"; + b.strokeStyle = h.color || "#335"; + var f = h._pos, c = h._size; + b.globalAlpha = 0.25 * this.editor_alpha; + b.beginPath(); + b.rect(f[0] + 0.5, f[1] + 0.5, c[0], c[1]); + b.fill(); + b.globalAlpha = this.editor_alpha; + b.stroke(); + b.beginPath(); + b.moveTo(f[0] + c[0], f[1] + c[1]); + b.lineTo(f[0] + c[0] - 10, f[1] + c[1]); + b.lineTo(f[0] + c[0], f[1] + c[1] - 10); + b.fill(); + c = h.font_size || e.DEFAULT_GROUP_FONT_SIZE; + b.font = c + "px Arial"; + b.fillText(h.title, f[0] + 4, f[1] + c); + } + } + b.restore(); + } + }; + l.prototype.adjustNodesSize = function() { + for (var a = this.graph._nodes, b = 0; b < a.length; ++b) { + a[b].size = a[b].computeSize(); + } + this.setDirty(!0, !0); + }; + l.prototype.resize = function(a, b) { + a || b || (b = this.canvas.parentNode, a = b.offsetWidth, b = b.offsetHeight); + if (this.canvas.width != a || this.canvas.height != b) { + this.canvas.width = a, this.canvas.height = b, this.bgcanvas.width = this.canvas.width, this.bgcanvas.height = this.canvas.height, this.setDirty(!0, !0); + } + }; + l.prototype.switchLiveMode = function(a) { + if (a) { + var b = this, d = this.live_mode ? 1.1 : 0.9; + this.live_mode && (this.live_mode = !1, this.editor_alpha = 0.1); + var c = setInterval(function() { + b.editor_alpha *= d; + b.dirty_canvas = !0; + b.dirty_bgcanvas = !0; + 1 > d && 0.01 > b.editor_alpha && (clearInterval(c), 1 > d && (b.live_mode = !0)); + 1 < d && 0.99 < b.editor_alpha && (clearInterval(c), b.editor_alpha = 1); + }, 1); + } else { + this.live_mode = !this.live_mode, this.dirty_bgcanvas = this.dirty_canvas = !0; + } + }; + l.prototype.onNodeSelectionChange = function(a) { + }; + l.prototype.touchHandler = function(a) { + var b = a.changedTouches[0]; + switch(a.type) { + case "touchstart": + var d = "mousedown"; + break; + case "touchmove": + d = "mousemove"; + break; + case "touchend": + d = "mouseup"; + break; + default: + return; + } + var c = this.getCanvasWindow(), f = c.document.createEvent("MouseEvent"); + f.initMouseEvent(d, !0, !0, c, 1, b.screenX, b.screenY, b.clientX, b.clientY, !1, !1, !1, !1, 0, null); + b.target.dispatchEvent(f); + a.preventDefault(); + }; + l.onGroupAdd = function(a, b, d) { + a = l.active_canvas; + a.getCanvasWindow(); + b = new e.LGraphGroup; + b.pos = a.convertEventToCanvasOffset(d); + a.graph.add(b); + }; + l.onMenuAdd = function(a, b, d, c, f) { + function h(a, b) { + b = c.getFirstEvent(); + if (a = e.createNode(a.value)) { + a.pos = k.convertEventToCanvasOffset(b), k.graph.add(a); + } + f && f(a); + } + var k = l.active_canvas, n = k.getCanvasWindow(); + a = e.getNodeTypesCategories(k.filter); + b = []; + for (var q in a) { + a[q] && b.push({value:a[q], content:a[q], has_submenu:!0}); + } + var g = new e.ContextMenu(b, {event:d, callback:function(a, b, d) { + a = e.getNodeTypesInCategory(a.value, k.filter); + b = []; + for (var f in a) { + a[f].skip_list || b.push({content:a[f].title, value:a[f].type}); + } + new e.ContextMenu(b, {event:d, callback:h, parentMenu:g}, n); + return !1; + }, parentMenu:c}, n); + return !1; + }; + l.onMenuCollapseAll = function() { + }; + l.onMenuNodeEdit = function() { + }; + l.showMenuNodeOptionalInputs = function(a, b, d, c, f) { + if (f) { + var h = this; + a = l.active_canvas.getCanvasWindow(); + b = f.optional_inputs; + f.onGetInputs && (b = f.onGetInputs()); + var k = []; + if (b) { + for (var n in b) { + var q = b[n]; + if (q) { + var g = q[0]; + q[2] && q[2].label && (g = q[2].label); + g = {content:g, value:q}; + q[1] == e.ACTION && (g.className = "event"); + k.push(g); + } else { + k.push(null); + } + } + } + this.onMenuNodeInputs && (k = this.onMenuNodeInputs(k)); + if (k.length) { + return new e.ContextMenu(k, {event:d, callback:function(a, b, d) { + f && (a.callback && a.callback.call(h, f, a, b, d), a.value && (f.addInput(a.value[0], a.value[1], a.value[2]), f.setDirtyCanvas(!0, !0))); + }, parentMenu:c, node:f}, a), !1; + } + } + }; + l.showMenuNodeOptionalOutputs = function(a, b, d, c, f) { + function h(a, b, d) { + if (f && (a.callback && a.callback.call(k, f, a, b, d), a.value)) { + if (d = a.value[1], !d || d.constructor !== Object && d.constructor !== Array) { + f.addOutput(a.value[0], a.value[1], a.value[2]), f.setDirtyCanvas(!0, !0); + } else { + a = []; + for (var n in d) { + a.push({content:n, value:d[n]}); + } + new e.ContextMenu(a, {event:b, callback:h, parentMenu:c, node:f}); + return !1; + } + } + } + if (f) { + var k = this; + a = l.active_canvas.getCanvasWindow(); + b = f.optional_outputs; + f.onGetOutputs && (b = f.onGetOutputs()); + var n = []; + if (b) { + for (var q in b) { + var g = b[q]; + if (!g) { + n.push(null); + } else { + if (!f.flags || !f.flags.skip_repeated_outputs || -1 == f.findOutputSlot(g[0])) { + var m = g[0]; + g[2] && g[2].label && (m = g[2].label); + m = {content:m, value:g}; + g[1] == e.EVENT && (m.className = "event"); + n.push(m); + } + } + } + } + this.onMenuNodeOutputs && (n = this.onMenuNodeOutputs(n)); + if (n.length) { + return new e.ContextMenu(n, {event:d, callback:h, parentMenu:c, node:f}, a), !1; + } + } + }; + l.onShowMenuNodeProperties = function(a, b, d, c, f) { + if (f && f.properties) { + var h = l.active_canvas; + b = h.getCanvasWindow(); + var k = [], n; + for (n in f.properties) { + a = void 0 !== f.properties[n] ? f.properties[n] : " ", "object" == typeof a && (a = JSON.stringify(a)), a = l.decodeHTML(a), k.push({content:"" + n + "" + a + "", value:n}); + } + if (k.length) { + return new e.ContextMenu(k, {event:d, callback:function(a, b, d, c) { + f && (b = this.getBoundingClientRect(), h.showEditPropertyValue(f, a.value, {position:[b.left, b.top]})); + }, parentMenu:c, allow_html:!0, node:f}, b), !1; + } + } + }; + l.decodeHTML = function(a) { + var b = document.createElement("div"); + b.innerText = a; + return b.innerHTML; + }; + l.onResizeNode = function(a, b, d, c, f) { + f && (f.size = f.computeSize(), f.setDirtyCanvas(!0, !0)); + }; + l.prototype.showLinkMenu = function(a, b) { + var d = this; + console.log(a); + var c = new e.ContextMenu(["Add Node", null, "Delete"], {event:b, title:null != a.data ? a.data.constructor.name : null, callback:function(b, h, e) { + switch(b) { + case "Add Node": + l.onMenuAdd(null, null, e, c, function(b) { + console.log("node autoconnect"); + var f = d.graph.getNodeById(a.origin_id), c = d.graph.getNodeById(a.target_id); + b.inputs && b.inputs.length && b.outputs && b.outputs.length && f.outputs[a.origin_slot].type == b.inputs[0].type && b.outputs[0].type == c.inputs[0].type && (f.connect(a.origin_slot, b, 0), b.connect(0, c, a.target_slot), b.pos[0] -= 0.5 * b.size[0]); + }); + break; + case "Delete": + d.graph.removeLink(a.id); + } + }}); + return !1; + }; + l.onShowPropertyEditor = function(a, b, d, c, f) { + function h() { + var b = n.value; + "Number" == a.type ? b = Number(b) : "Boolean" == a.type && (b = !!b); + f[e] = b; + k.parentNode && k.parentNode.removeChild(k); + f.setDirtyCanvas(!0, !0); + } + var e = a.property || "title"; + b = f[e]; + var k = document.createElement("div"); + k.className = "graphdialog"; + k.innerHTML = ""; + k.querySelector(".name").innerText = e; + var n = k.querySelector("input"); + n && (n.value = b, n.addEventListener("blur", function(a) { + this.focus(); + }), n.addEventListener("keydown", function(a) { + 13 == a.keyCode && (h(), a.preventDefault(), a.stopPropagation()); + })); + b = l.active_canvas.canvas; + d = b.getBoundingClientRect(); + var q = c = -20; + d && (c -= d.left, q -= d.top); + event ? (k.style.left = event.clientX + c + "px", k.style.top = event.clientY + q + "px") : (k.style.left = 0.5 * b.width + c + "px", k.style.top = 0.5 * b.height + q + "px"); + k.querySelector("button").addEventListener("click", h); + b.parentNode.appendChild(k); + }; + l.prototype.prompt = function(a, b, d, c) { + var f = this; + a = a || ""; + var h = !1, e = document.createElement("div"); + e.className = "graphdialog rounded"; + e.innerHTML = " "; + e.close = function() { + f.prompt_box = null; + e.parentNode && e.parentNode.removeChild(e); + }; + 1 < this.ds.scale && (e.style.transform = "scale(" + this.ds.scale + ")"); + e.addEventListener("mouseleave", function(a) { + h || e.close(); + }); + f.prompt_box && f.prompt_box.close(); + f.prompt_box = e; + e.querySelector(".name").innerText = a; + e.querySelector(".value").value = b; + var k = e.querySelector("input"); + k.addEventListener("keydown", function(a) { + h = !0; + if (27 == a.keyCode) { + e.close(); + } else { + if (13 == a.keyCode) { + d && d(this.value), e.close(); + } else { + return; + } + } + a.preventDefault(); + a.stopPropagation(); + }); + e.querySelector("button").addEventListener("click", function(a) { + d && d(k.value); + f.setDirty(!0); + e.close(); + }); + a = l.active_canvas.canvas; + b = a.getBoundingClientRect(); + var n = -20, q = -20; + b && (n -= b.left, q -= b.top); + c ? (e.style.left = c.clientX + n + "px", e.style.top = c.clientY + q + "px") : (e.style.left = 0.5 * a.width + n + "px", e.style.top = 0.5 * a.height + q + "px"); + a.parentNode.appendChild(e); + setTimeout(function() { + k.focus(); + }, 10); + return e; + }; + l.search_limit = -1; + l.prototype.showSearchBox = function(a) { + function b(b) { + if (b) { + if (f.onSearchBoxSelection) { + f.onSearchBoxSelection(b, a, k); + } else { + var d = e.searchbox_extras[b.toLowerCase()]; + d && (b = d.type); + if (b = e.createNode(b)) { + b.pos = k.convertEventToCanvasOffset(a), k.graph.add(b); + } + if (d && d.data) { + if (d.data.properties) { + for (var c in d.data.properties) { + b.addProperty(c, d.data.properties[c]); + } + } + if (d.data.inputs) { + for (c in b.inputs = [], d.data.inputs) { + b.addOutput(d.data.inputs[c][0], d.data.inputs[c][1]); + } + } + if (d.data.outputs) { + for (c in b.outputs = [], d.data.outputs) { + b.addOutput(d.data.outputs[c][0], d.data.outputs[c][1]); + } + } + d.data.title && (b.title = d.data.title); + d.data.json && b.configure(d.data.json); + } + } + } + g.close(); + } + function d(a) { + var b = t; + t && t.classList.remove("selected"); + t ? (t = a ? t.nextSibling : t.previousSibling) || (t = b) : t = a ? p.childNodes[0] : p.childNodes[p.childNodes.length]; + t && (t.classList.add("selected"), t.scrollIntoView({block:"end", behavior:"smooth"})); + } + function c() { + function a(a, d) { + var f = document.createElement("div"); + r || (r = a); + f.innerText = a; + f.dataset.type = escape(a); + f.className = "litegraph lite-search-item"; + d && (f.className += " " + d); + f.addEventListener("click", function(a) { + b(unescape(this.dataset.type)); + }); + p.appendChild(f); + } + u = null; + var d = C.value; + r = null; + p.innerHTML = ""; + if (d) { + if (f.onSearchBox) { + var c = f.onSearchBox(p, d, k); + if (c) { + for (var h = 0; h < c.length; ++h) { + a(c[h]); + } + } + } else { + c = function(a) { + var b = e.registered_node_types[a]; + return q && b.filter != q ? !1 : -1 !== a.toLowerCase().indexOf(d); + }; + var n = 0; + d = d.toLowerCase(); + var q = k.filter || k.graph.filter; + for (h in e.searchbox_extras) { + var g = e.searchbox_extras[h]; + if (-1 !== g.desc.toLowerCase().indexOf(d)) { + var m = e.registered_node_types[g.type]; + if (!m || !m.filter || m.filter == q) { + if (a(g.desc, "searchbox_extra"), -1 !== l.search_limit && n++ > l.search_limit) { + break; + } + } + } + } + g = null; + if (Array.prototype.filter) { + g = Object.keys(e.registered_node_types).filter(c); + } else { + for (h in g = [], e.registered_node_types) { + c(h) && g.push(h); + } + } + for (h = 0; h < g.length && !(a(g[h]), -1 !== l.search_limit && n++ > l.search_limit); h++) { + } + } + } + } + var f = this, k = l.active_canvas, n = k.canvas, q = n.ownerDocument || document, g = document.createElement("div"); + g.className = "litegraph litesearchbox graphdialog rounded"; + g.innerHTML = "Search
"; + g.close = function() { + f.search_box = null; + q.body.focus(); + q.body.style.overflow = ""; + setTimeout(function() { + f.canvas.focus(); + }, 20); + g.parentNode && g.parentNode.removeChild(g); + }; + var m = null; + 1 < this.ds.scale && (g.style.transform = "scale(" + this.ds.scale + ")"); + g.addEventListener("mouseenter", function(a) { + m && (clearTimeout(m), m = null); + }); + g.addEventListener("mouseleave", function(a) { + m = setTimeout(function() { + g.close(); + }, 500); + }); + f.search_box && f.search_box.close(); + f.search_box = g; + var p = g.querySelector(".helper"), r = null, u = null, t = null, C = g.querySelector("input"); + C && (C.addEventListener("blur", function(a) { + this.focus(); + }), C.addEventListener("keydown", function(a) { + if (38 == a.keyCode) { + d(!1); + } else { + if (40 == a.keyCode) { + d(!0); + } else { + if (27 == a.keyCode) { + g.close(); + } else { + if (13 == a.keyCode) { + t ? b(t.innerHTML) : r ? b(r) : g.close(); + } else { + u && clearInterval(u); + u = setTimeout(c, 10); + return; + } + } + } + } + a.preventDefault(); + a.stopPropagation(); + a.stopImmediatePropagation(); + return !0; + })); + q.fullscreenElement ? q.fullscreenElement.appendChild(g) : (q.body.appendChild(g), q.body.style.overflow = "hidden"); + n = n.getBoundingClientRect(); + var v = (a ? a.clientY : n.top + 0.5 * n.height) - 20; + g.style.left = (a ? a.clientX : n.left + 0.5 * n.width) - 80 + "px"; + g.style.top = v + "px"; + a.layerY > n.height - 200 && (p.style.maxHeight = n.height - a.layerY - 20 + "px"); + C.focus(); + return g; + }; + l.prototype.showEditPropertyValue = function(a, b, d) { + function c() { + f(r.value); + } + function f(f) { + "number" == typeof a.properties[b] && (f = Number(f)); + if ("array" == k || "object" == k) { + f = JSON.parse(f); + } + a.properties[b] = f; + a._graph && a._graph._version++; + if (a.onPropertyChanged) { + a.onPropertyChanged(b, f); + } + if (d.onclose) { + d.onclose(); + } + l.close(); + a.setDirtyCanvas(!0, !0); + } + if (a && void 0 !== a.properties[b]) { + d = d || {}; + var e = a.getPropertyInfo(b), k = e.type, n = ""; + if ("string" == k || "number" == k || "array" == k || "object" == k) { + n = ""; + } else { + if ("enum" == k && e.values) { + n = ""; + } else { + if ("boolean" == k) { + n = ""; + } else { + console.warn("unknown type: " + k); + return; + } + } + } + var l = this.createDialog("" + b + "" + n + "", d); + if ("enum" == k && e.values) { + var r = l.querySelector("select"); + r.addEventListener("change", function(a) { + f(a.target.value); + }); + } else { + if ("boolean" == k) { + (r = l.querySelector("input")) && r.addEventListener("click", function(a) { + f(!!r.checked); + }); + } else { + if (r = l.querySelector("input")) { + r.addEventListener("blur", function(a) { + this.focus(); + }), g = void 0 !== a.properties[b] ? a.properties[b] : "", g = JSON.stringify(g), r.value = g, r.addEventListener("keydown", function(a) { + 13 == a.keyCode && (c(), a.preventDefault(), a.stopPropagation()); + }); + } + } + } + l.querySelector("button").addEventListener("click", c); + return l; + } + }; + l.prototype.createDialog = function(a, b) { + b = b || {}; + var d = document.createElement("div"); + d.className = "graphdialog"; + d.innerHTML = a; + a = this.canvas.getBoundingClientRect(); + var c = -20, f = -20; + a && (c -= a.left, f -= a.top); + b.position ? (c += b.position[0], f += b.position[1]) : b.event ? (c += b.event.clientX, f += b.event.clientY) : (c += 0.5 * this.canvas.width, f += 0.5 * this.canvas.height); + d.style.left = c + "px"; + d.style.top = f + "px"; + this.canvas.parentNode.appendChild(d); + d.close = function() { + this.parentNode && this.parentNode.removeChild(this); + }; + return d; + }; + l.onMenuNodeCollapse = function(a, b, d, c, f) { + f.collapse(); + }; + l.onMenuNodePin = function(a, b, d, c, f) { + f.pin(); + }; + l.onMenuNodeMode = function(a, b, d, c, f) { + new e.ContextMenu(["Always", "On Event", "On Trigger", "Never"], {event:d, callback:function(a) { + if (f) { + switch(a) { + case "On Event": + f.mode = e.ON_EVENT; + break; + case "On Trigger": + f.mode = e.ON_TRIGGER; + break; + case "Never": + f.mode = e.NEVER; + break; + default: + f.mode = e.ALWAYS; + } + } + }, parentMenu:c, node:f}); + return !1; + }; + l.onMenuNodeColors = function(a, b, d, c, f) { + if (!f) { + throw "no node for color"; + } + b = []; + b.push({value:null, content:"No color"}); + for (var h in l.node_colors) { + a = l.node_colors[h], a = {value:h, content:"" + h + ""}, b.push(a); + } + new e.ContextMenu(b, {event:d, callback:function(a) { + f && ((a = a.value ? l.node_colors[a.value] : null) ? f.constructor === e.LGraphGroup ? f.color = a.groupcolor : (f.color = a.color, f.bgcolor = a.bgcolor) : (delete f.color, delete f.bgcolor), f.setDirtyCanvas(!0, !0)); + }, parentMenu:c, node:f}); + return !1; + }; + l.onMenuNodeShapes = function(a, b, d, c, f) { + if (!f) { + throw "no node passed"; + } + new e.ContextMenu(e.VALID_SHAPES, {event:d, callback:function(a) { + f && (f.shape = a, f.setDirtyCanvas(!0)); + }, parentMenu:c, node:f}); + return !1; + }; + l.onMenuNodeRemove = function(a, b, d, c, f) { + if (!f) { + throw "no node passed"; + } + !1 !== f.removable && (f.graph.remove(f), f.setDirtyCanvas(!0, !0)); + }; + l.onMenuNodeClone = function(a, b, d, c, f) { + 0 != f.clonable && (a = f.clone()) && (a.pos = [f.pos[0] + 5, f.pos[1] + 5], f.graph.add(a), f.setDirtyCanvas(!0, !0)); + }; + l.node_colors = {red:{color:"#322", bgcolor:"#533", groupcolor:"#A88"}, brown:{color:"#332922", bgcolor:"#593930", groupcolor:"#b06634"}, green:{color:"#232", bgcolor:"#353", groupcolor:"#8A8"}, blue:{color:"#223", bgcolor:"#335", groupcolor:"#88A"}, pale_blue:{color:"#2a363b", bgcolor:"#3f5159", groupcolor:"#3f789e"}, cyan:{color:"#233", bgcolor:"#355", groupcolor:"#8AA"}, purple:{color:"#323", bgcolor:"#535", groupcolor:"#a1309b"}, yellow:{color:"#432", bgcolor:"#653", groupcolor:"#b58b2a"}, + black:{color:"#222", bgcolor:"#000", groupcolor:"#444"}}; + l.prototype.getCanvasMenuOptions = function() { + if (this.getMenuOptions) { + var a = this.getMenuOptions(); + } else { + a = [{content:"Add Node", has_submenu:!0, callback:l.onMenuAdd}, {content:"Add Group", callback:l.onGroupAdd}], this._graph_stack && 0 < this._graph_stack.length && a.push(null, {content:"Close subgraph", callback:this.closeSubgraph.bind(this)}); + } + if (this.getExtraMenuOptions) { + var b = this.getExtraMenuOptions(this, a); + b && (a = a.concat(b)); + } + return a; + }; + l.prototype.getNodeMenuOptions = function(a) { + var b = a.getMenuOptions ? a.getMenuOptions(this) : [{content:"Inputs", has_submenu:!0, disabled:!0, callback:l.showMenuNodeOptionalInputs}, {content:"Outputs", has_submenu:!0, disabled:!0, callback:l.showMenuNodeOptionalOutputs}, null, {content:"Properties", has_submenu:!0, callback:l.onShowMenuNodeProperties}, null, {content:"Title", callback:l.onShowPropertyEditor}, {content:"Mode", has_submenu:!0, callback:l.onMenuNodeMode}, {content:"Resize", callback:l.onResizeNode}, {content:"Collapse", + callback:l.onMenuNodeCollapse}, {content:"Pin", callback:l.onMenuNodePin}, {content:"Colors", has_submenu:!0, callback:l.onMenuNodeColors}, {content:"Shapes", has_submenu:!0, callback:l.onMenuNodeShapes}, null]; + if (a.onGetInputs) { + var d = a.onGetInputs(); + d && d.length && (b[0].disabled = !1); + } + a.onGetOutputs && (d = a.onGetOutputs()) && d.length && (b[1].disabled = !1); + a.getExtraMenuOptions && (d = a.getExtraMenuOptions(this)) && (d.push(null), b = d.concat(b)); + !1 !== a.clonable && b.push({content:"Clone", callback:l.onMenuNodeClone}); + !1 !== a.removable && b.push(null, {content:"Remove", callback:l.onMenuNodeRemove}); + if (a.graph && a.graph.onGetNodeMenuOptions) { + a.graph.onGetNodeMenuOptions(b, a); + } + return b; + }; + l.prototype.getGroupMenuOptions = function(a) { + return [{content:"Title", callback:l.onShowPropertyEditor}, {content:"Color", has_submenu:!0, callback:l.onMenuNodeColors}, {content:"Font size", property:"font_size", type:"Number", callback:l.onShowPropertyEditor}, null, {content:"Remove", callback:l.onMenuNodeRemove}]; + }; + l.prototype.processContextMenu = function(a, b) { + var d = this, c = l.active_canvas.getCanvasWindow(), f = null, k = {event:b, callback:function(b, f, c) { + if (b) { + if ("Remove Slot" == b.content) { + b = b.slot, b.input ? a.removeInput(b.slot) : b.output && a.removeOutput(b.slot); + } else { + if ("Disconnect Links" == b.content) { + b = b.slot, b.output ? a.disconnectOutput(b.slot) : b.input && a.disconnectInput(b.slot); + } else { + if ("Rename Slot" == b.content) { + b = b.slot; + var e = b.input ? a.getInputInfo(b.slot) : a.getOutputInfo(b.slot), h = d.createDialog("Name", f), k = h.querySelector("input"); + k && e && (k.value = e.label || ""); + h.querySelector("button").addEventListener("click", function(a) { + k.value && (e && (e.label = k.value), d.setDirty(!0)); + h.close(); + }); + } + } + } + } + }, extra:a}; + a && (k.title = a.type); + var n = null; + a && (n = a.getSlotInPosition(b.canvasX, b.canvasY), l.active_node = a); + n ? (f = [], n && n.output && n.output.links && n.output.links.length && f.push({content:"Disconnect Links", slot:n}), b = n.input || n.output, f.push(b.locked ? "Cannot remove" : {content:"Remove Slot", slot:n}), f.push(b.nameLocked ? "Cannot rename" : {content:"Rename Slot", slot:n}), k.title = (n.input ? n.input.type : n.output.type) || "*", n.input && n.input.type == e.ACTION && (k.title = "Action"), n.output && n.output.type == e.EVENT && (k.title = "Event")) : a ? f = this.getNodeMenuOptions(a) : + (f = this.getCanvasMenuOptions(), (n = this.graph.getGroupOnPos(b.canvasX, b.canvasY)) && f.push(null, {content:"Edit Group", has_submenu:!0, submenu:{title:"Group", extra:n, options:this.getGroupMenuOptions(n)}})); + f && new e.ContextMenu(f, k, c); + }; + "undefined" != typeof window && window.CanvasRenderingContext2D && (window.CanvasRenderingContext2D.prototype.roundRect = function(a, b, d, c, f, e) { + void 0 === f && (f = 5); + void 0 === e && (e = f); + this.moveTo(a + f, b); + this.lineTo(a + d - f, b); + this.quadraticCurveTo(a + d, b, a + d, b + f); + this.lineTo(a + d, b + c - e); + this.quadraticCurveTo(a + d, b + c, a + d - e, b + c); + this.lineTo(a + e, b + c); + this.quadraticCurveTo(a, b + c, a, b + c - e); + this.lineTo(a, b + f); + this.quadraticCurveTo(a, b, a + f, b); + }); + e.compareObjects = function(a, b) { + for (var d in a) { + if (a[d] != b[d]) { + return !1; + } + } + return !0; + }; + e.distance = B; + e.colorToString = function(a) { + return "rgba(" + Math.round(255 * a[0]).toFixed() + "," + Math.round(255 * a[1]).toFixed() + "," + Math.round(255 * a[2]).toFixed() + "," + (4 == a.length ? a[3].toFixed(2) : "1.0") + ")"; + }; + e.isInsideRectangle = y; + e.growBounding = function(a, b, d) { + b < a[0] ? a[0] = b : b > a[2] && (a[2] = b); + d < a[1] ? a[1] = d : d > a[3] && (a[3] = d); + }; + e.isInsideBounding = function(a, b) { + return a[0] < b[0][0] || a[1] < b[0][1] || a[0] > b[1][0] || a[1] > b[1][1] ? !1 : !0; + }; + e.overlapBounding = v; + e.hex2num = function(a) { + "#" == a.charAt(0) && (a = a.slice(1)); + a = a.toUpperCase(); + for (var b = Array(3), d = 0, c, f, e = 0; 6 > e; e += 2) { + c = "0123456789ABCDEF".indexOf(a.charAt(e)), f = "0123456789ABCDEF".indexOf(a.charAt(e + 1)), b[d] = 16 * c + f, d++; + } + return b; + }; + e.num2hex = function(a) { + for (var b = "#", d, c, f = 0; 3 > f; f++) { + d = a[f] / 16, c = a[f] % 16, b += "0123456789ABCDEF".charAt(d) + "0123456789ABCDEF".charAt(c); + } + return b; + }; + E.prototype.addItem = function(a, b, d) { + function c(a) { + var b = this.value; + b && b.has_submenu && f.call(this, a); + } + function f(a) { + var b = this.value, f = !0; + e.current_submenu && e.current_submenu.close(a); + if (d.callback) { + var c = d.callback.call(this, b, d, a, e, d.node); + !0 === c && (f = !1); + } + if (b && (b.callback && !d.ignore_item_callbacks && !0 !== b.disabled && (c = b.callback.call(this, b, d, a, e, d.extra), !0 === c && (f = !1)), b.submenu)) { + if (!b.submenu.options) { + throw "ContextMenu submenu needs options"; + } + new e.constructor(b.submenu.options, {callback:b.submenu.callback, event:a, parentMenu:e, ignore_item_callbacks:b.submenu.ignore_item_callbacks, title:b.submenu.title, extra:b.submenu.extra, autoopen:d.autoopen}); + f = !1; + } + f && !e.lock && e.close(); + } + var e = this; + d = d || {}; + var k = document.createElement("div"); + k.className = "litemenu-entry submenu"; + var n = !1; + if (null === b) { + k.classList.add("separator"); + } else { + k.innerHTML = b && b.title ? b.title : a; + if (k.value = b) { + b.disabled && (n = !0, k.classList.add("disabled")), (b.submenu || b.has_submenu) && k.classList.add("has_submenu"); + } + "function" == typeof b ? (k.dataset.value = a, k.onclick_callback = b) : k.dataset.value = b; + b.className && (k.className += " " + b.className); + } + this.root.appendChild(k); + n || k.addEventListener("click", f); + d.autoopen && k.addEventListener("mouseenter", c); + return k; + }; + E.prototype.close = function(a, b) { + this.root.parentNode && this.root.parentNode.removeChild(this.root); + this.parentMenu && !b && (this.parentMenu.lock = !1, this.parentMenu.current_submenu = null, void 0 === a ? this.parentMenu.close() : a && !E.isCursorOverElement(a, this.parentMenu.root) && E.trigger(this.parentMenu.root, "mouseleave", a)); + this.current_submenu && this.current_submenu.close(a, !0); + this.root.closing_timer && clearTimeout(this.root.closing_timer); + }; + E.trigger = function(a, b, d, c) { + var f = document.createEvent("CustomEvent"); + f.initCustomEvent(b, !0, !0, d); + f.srcElement = c; + a.dispatchEvent ? a.dispatchEvent(f) : a.__events && a.__events.dispatchEvent(f); + return f; + }; + E.prototype.getTopMenu = function() { + return this.options.parentMenu ? this.options.parentMenu.getTopMenu() : this; + }; + E.prototype.getFirstEvent = function() { + return this.options.parentMenu ? this.options.parentMenu.getFirstEvent() : this.options.event; + }; + E.isCursorOverElement = function(a, b) { + var d = a.clientX; + a = a.clientY; + return (b = b.getBoundingClientRect()) ? a > b.top && a < b.top + b.height && d > b.left && d < b.left + b.width ? !0 : !1 : !1; + }; + e.ContextMenu = E; + e.closeAllContextMenus = function(a) { + a = a || window; + a = a.document.querySelectorAll(".litecontextmenu"); + if (a.length) { + for (var b = [], d = 0; d < a.length; d++) { + b.push(a[d]); + } + for (d in b) { + b[d].close ? b[d].close() : b[d].parentNode && b[d].parentNode.removeChild(b[d]); + } + } + }; + e.extendClass = function(a, b) { + for (var d in b) { + a.hasOwnProperty(d) || (a[d] = b[d]); + } + if (b.prototype) { + for (d in b.prototype) { + b.prototype.hasOwnProperty(d) && !a.prototype.hasOwnProperty(d) && (b.prototype.__lookupGetter__(d) ? a.prototype.__defineGetter__(d, b.prototype.__lookupGetter__(d)) : a.prototype[d] = b.prototype[d], b.prototype.__lookupSetter__(d) && a.prototype.__defineSetter__(d, b.prototype.__lookupSetter__(d))); + } + } + }; + z.sampleCurve = function(a, b) { + if (b) { + for (var d = 0; d < b.length - 1; ++d) { + var c = b[d], f = b[d + 1]; + if (!(f[0] < a)) { + b = f[0] - c[0]; + if (0.00001 > Math.abs(b)) { + return c[1]; + } + a = (a - c[0]) / b; + return c[1] * (1.0 - a) + f[1] * a; + } + } + return 0; + } + }; + z.prototype.draw = function(a, b, d, c, f, e) { + if (d = this.points) { + this.size = b; + var k = b[0] - 2 * this.margin; + b = b[1] - 2 * this.margin; + f = f || "#666"; + a.save(); + a.translate(this.margin, this.margin); + c && (a.fillStyle = "#111", a.fillRect(0, 0, k, b), a.fillStyle = "#222", a.fillRect(0.5 * k, 0, 1, b), a.strokeStyle = "#333", a.strokeRect(0, 0, k, b)); + a.strokeStyle = f; + e && (a.globalAlpha = 0.5); + a.beginPath(); + for (c = 0; c < d.length; ++c) { + f = d[c], a.lineTo(f[0] * k, (1.0 - f[1]) * b); + } + a.stroke(); + a.globalAlpha = 1; + if (!e) { + for (c = 0; c < d.length; ++c) { + f = d[c], a.fillStyle = this.selected == c ? "#FFF" : this.nearest == c ? "#DDD" : "#AAA", a.beginPath(), a.arc(f[0] * k, (1.0 - f[1]) * b, 2, 0, 2 * Math.PI), a.fill(); + } + } + a.restore(); + } + }; + z.prototype.onMouseDown = function(a, b) { + var d = this.points; + if (d && !(0 > a[1])) { + var c = this.size[0] - 2 * this.margin, f = this.size[1] - 2 * this.margin, e = a[0] - this.margin; + a = a[1] - this.margin; + this.selected = this.getCloserPoint([e, a], 30 / b.ds.scale); + -1 == this.selected && (b = [e / c, 1 - a / f], d.push(b), d.sort(function(a, b) { + return a[0] - b[0]; + }), this.selected = d.indexOf(b), this.must_update = !0); + if (-1 != this.selected) { + return !0; + } + } + }; + z.prototype.onMouseMove = function(a, b) { + var d = this.points; + if (d) { + var c = this.selected; + if (!(0 > c)) { + var f = (a[0] - this.margin) / (this.size[0] - 2 * this.margin), e = (a[1] - this.margin) / (this.size[1] - 2 * this.margin); + this._nearest = this.getCloserPoint([a[0] - this.margin, a[1] - this.margin], 30 / b.ds.scale); + if (b = d[c]) { + var k = 0 == c || c == d.length - 1; + !k && (-10 > a[0] || a[0] > this.size[0] + 10 || -10 > a[1] || a[1] > this.size[1] + 10) ? (d.splice(c, 1), this.selected = -1) : (b[0] = k ? 0 == c ? 0 : 1 : Math.clamp(f, 0, 1), b[1] = 1.0 - Math.clamp(e, 0, 1), d.sort(function(a, b) { + return a[0] - b[0]; + }), this.selected = d.indexOf(b), this.must_update = !0); + } + } + } + }; + z.prototype.onMouseUp = function(a, b) { + this.selected = -1; + return !1; + }; + z.prototype.getCloserPoint = function(a, b) { + var d = this.points; + if (!d) { + return -1; + } + b = b || 30; + for (var c = this.size[0] - 2 * this.margin, f = this.size[1] - 2 * this.margin, e = d.length, k = [0, 0], n = 1000000, q = -1, g = 0; g < e; ++g) { + var l = d[g]; + k[0] = l[0] * c; + k[1] = (1.0 - l[1]) * f; + l = vec2.distance(a, k); + l > n || l > b || (q = g, n = l); + } + return q; + }; + e.CurveEditor = z; + e.getParameterNames = function(a) { + return (a + "").replace(/[/][/].*$/gm, "").replace(/\s+/g, "").replace(/[/][*][^/*]*[*][/]/g, "").split("){", 1)[0].replace(/^[^(]*[(]/, "").replace(/=[^,]+/g, "").split(",").filter(Boolean); + }; + Math.clamp = function(a, b, d) { + return b > a ? b : d < a ? d : a; + }; + "undefined" == typeof window || window.requestAnimationFrame || (window.requestAnimationFrame = window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(a) { + window.setTimeout(a, 1000 / 60); + }); +})(this); +"undefined" != typeof exports && (exports.LiteGraph = this.LiteGraph); +(function(w) { + function c() { + this.addOutput("in ms", "number"); + this.addOutput("in sec", "number"); + } + function p() { + this.size = [140, 80]; + this.properties = {enabled:!0}; + this.enabled = !0; + this.subgraph = new h.LGraph; + this.subgraph._subgraph_node = this; + this.subgraph._is_subgraph = !0; + this.subgraph.onTrigger = this.onSubgraphTrigger.bind(this); + this.subgraph.onInputAdded = this.onSubgraphNewInput.bind(this); + this.subgraph.onInputRenamed = this.onSubgraphRenamedInput.bind(this); + this.subgraph.onInputTypeChanged = this.onSubgraphTypeChangeInput.bind(this); + this.subgraph.onInputRemoved = this.onSubgraphRemovedInput.bind(this); + this.subgraph.onOutputAdded = this.onSubgraphNewOutput.bind(this); + this.subgraph.onOutputRenamed = this.onSubgraphRenamedOutput.bind(this); + this.subgraph.onOutputTypeChanged = this.onSubgraphTypeChangeOutput.bind(this); + this.subgraph.onOutputRemoved = this.onSubgraphRemovedOutput.bind(this); + } + function m() { + this.addOutput("", "number"); + this.name_in_graph = ""; + this.properties = {name:"", type:"number", value:0}; + var a = this; + this.name_widget = this.addWidget("text", "Name", this.properties.name, function(b) { + b && a.setProperty("name", b); + }); + this.type_widget = this.addWidget("text", "Type", this.properties.type, function(b) { + a.setProperty("type", b); + }); + this.value_widget = this.addWidget("number", "Value", this.properties.value, function(b) { + a.setProperty("value", b); + }); + this.widgets_up = !0; + this.size = [180, 90]; + } + function g() { + this.addInput("", ""); + this.name_in_graph = ""; + this.properties = {}; + var a = this; + Object.defineProperty(this.properties, "name", {get:function() { + return a.name_in_graph; + }, set:function(b) { + "" != b && b != a.name_in_graph && (a.name_in_graph ? a.graph.renameOutput(a.name_in_graph, b) : a.graph.addOutput(b, a.properties.type), a.name_widget.value = b, a.name_in_graph = b); + }, enumerable:!0}); + Object.defineProperty(this.properties, "type", {get:function() { + return a.inputs[0].type; + }, set:function(b) { + if ("action" == b || "event" == b) { + b = h.ACTION; + } + a.inputs[0].type = b; + a.name_in_graph && a.graph.changeOutputType(a.name_in_graph, a.inputs[0].type); + a.type_widget.value = b || ""; + }, enumerable:!0}); + this.name_widget = this.addWidget("text", "Name", this.properties.name, "name"); + this.type_widget = this.addWidget("text", "Type", this.properties.type, "type"); + this.widgets_up = !0; + this.size = [180, 60]; + } + function u() { + this.addOutput("value", "number"); + this.addProperty("value", 1.0); + this.widget = this.addWidget("number", "value", 1, "value"); + this.widgets_up = !0; + this.size = [180, 30]; + } + function l() { + this.addOutput("", "boolean"); + this.addProperty("value", !0); + this.widget = this.addWidget("toggle", "value", !0, "value"); + this.widgets_up = !0; + this.size = [140, 30]; + } + function B() { + this.addOutput("", "string"); + this.addProperty("value", ""); + this.widget = this.addWidget("text", "value", "", "value"); + this.widgets_up = !0; + this.size = [180, 30]; + } + function y() { + this.addInput("url", ""); + this.addOutput("", ""); + this.addProperty("url", ""); + this.addProperty("type", "text"); + this.widget = this.addWidget("text", "url", "", "url"); + this._data = null; + } + function v() { + this.addOutput("", ""); + this.addProperty("value", ""); + this.widget = this.addWidget("text", "json", "", "value"); + this.widgets_up = !0; + this.size = [140, 30]; + this._value = null; + } + function E() { + this.addInput("", ""); + this.addOutput("", "array"); + this.addProperty("value", ""); + this.widget = this.addWidget("text", "array", "", "value"); + this.widgets_up = !0; + this.size = [140, 30]; + this._value = null; + } + function z() { + this.addInput("array", "array,table,string"); + this.addInput("index", "number"); + this.addOutput("value", ""); + this.addProperty("index", 0); + } + function e() { + this.addInput("table", "table"); + this.addInput("row", "number"); + this.addInput("col", "number"); + this.addOutput("value", ""); + this.addProperty("row", 0); + this.addProperty("column", 0); + } + function C() { + this.addInput("obj", ""); + this.addOutput("", ""); + this.addProperty("value", ""); + this.widget = this.addWidget("text", "prop.", "", this.setValue.bind(this)); + this.widgets_up = !0; + this.size = [140, 30]; + this._value = null; + } + function D() { + this.addInput("obj", ""); + this.addOutput("keys", "array"); + this.size = [140, 30]; + } + function t() { + this.addInput("A", "object"); + this.addInput("B", "object"); + this.addOutput("", "object"); + this._result = {}; + var a = this; + this.addWidget("button", "clear", "", function() { + a._result = {}; + }); + this.size = this.computeSize(); + } + function G() { + this.size = [60, 30]; + this.addInput("in"); + this.addOutput("out"); + this.properties = {varname:"myname", global:!1}; + this.value = null; + } + function n() { + this.size = [60, 30]; + this.addInput("data", 0); + this.addInput("download", h.ACTION); + this.properties = {filename:"data.json"}; + this.value = null; + var a = this; + this.addWidget("button", "Download", "", function(b) { + a.value && a.downloadAsFile(); + }); + } + function q() { + this.size = [60, 30]; + this.addInput("value", 0, {label:""}); + this.value = 0; + } + function k() { + this.addInput("in", 0); + this.addOutput("out", 0); + this.size = [40, 30]; + } + function a() { + this.mode = h.ON_EVENT; + this.size = [80, 30]; + this.addProperty("msg", ""); + this.addInput("log", h.EVENT); + this.addInput("msg", 0); + } + function b() { + this.mode = h.ON_EVENT; + this.addProperty("msg", ""); + this.addInput("", h.EVENT); + var a = this; + this.widget = this.addWidget("text", "Text", "", function(b) { + a.properties.msg = b; + }); + this.widgets_up = !0; + this.size = [200, 30]; + } + function d() { + this.size = [60, 30]; + this.addProperty("onExecute", "return A;"); + this.addInput("A", ""); + this.addInput("B", ""); + this.addOutput("out", ""); + this._func = null; + this.data = {}; + } + var h = w.LiteGraph; + c.title = "Time"; + c.desc = "Time"; + c.prototype.onExecute = function() { + this.setOutputData(0, 1000 * this.graph.globaltime); + this.setOutputData(1, this.graph.globaltime); + }; + h.registerNodeType("basic/time", c); + p.title = "Subgraph"; + p.desc = "Graph inside a node"; + p.title_color = "#334"; + p.prototype.onGetInputs = function() { + return [["enabled", "boolean"]]; + }; + p.prototype.onDrawTitle = function(a) { + if (!this.flags.collapsed) { + a.fillStyle = "#555"; + var b = h.NODE_TITLE_HEIGHT, d = this.size[0] - b; + a.fillRect(d, -b, b, b); + a.fillStyle = "#333"; + a.beginPath(); + a.moveTo(d + 0.2 * b, 0.6 * -b); + a.lineTo(d + 0.8 * b, 0.6 * -b); + a.lineTo(d + 0.5 * b, 0.3 * -b); + a.fill(); + } + }; + p.prototype.onDblClick = function(a, b, d) { + var f = this; + setTimeout(function() { + d.openSubgraph(f.subgraph); + }, 10); + }; + p.prototype.onMouseDown = function(a, b, d) { + if (!this.flags.collapsed && b[0] > this.size[0] - h.NODE_TITLE_HEIGHT && 0 > b[1]) { + var f = this; + setTimeout(function() { + d.openSubgraph(f.subgraph); + }, 10); + } + }; + p.prototype.onAction = function(a, b) { + this.subgraph.onAction(a, b); + }; + p.prototype.onExecute = function() { + if (this.enabled = this.getInputOrProperty("enabled")) { + if (this.inputs) { + for (var a = 0; a < this.inputs.length; a++) { + var b = this.inputs[a], d = this.getInputData(a); + this.subgraph.setInputData(b.name, d); + } + } + this.subgraph.runStep(); + if (this.outputs) { + for (a = 0; a < this.outputs.length; a++) { + d = this.subgraph.getOutputData(this.outputs[a].name), this.setOutputData(a, d); + } + } + } + }; + p.prototype.sendEventToAllNodes = function(a, b, d) { + this.enabled && this.subgraph.sendEventToAllNodes(a, b, d); + }; + p.prototype.onSubgraphTrigger = function(a, b) { + a = this.findOutputSlot(a); + -1 != a && this.triggerSlot(a); + }; + p.prototype.onSubgraphNewInput = function(a, b) { + -1 == this.findInputSlot(a) && this.addInput(a, b); + }; + p.prototype.onSubgraphRenamedInput = function(a, b) { + a = this.findInputSlot(a); + -1 != a && (this.getInputInfo(a).name = b); + }; + p.prototype.onSubgraphTypeChangeInput = function(a, b) { + a = this.findInputSlot(a); + -1 != a && (this.getInputInfo(a).type = b); + }; + p.prototype.onSubgraphRemovedInput = function(a) { + a = this.findInputSlot(a); + -1 != a && this.removeInput(a); + }; + p.prototype.onSubgraphNewOutput = function(a, b) { + -1 == this.findOutputSlot(a) && this.addOutput(a, b); + }; + p.prototype.onSubgraphRenamedOutput = function(a, b) { + a = this.findOutputSlot(a); + -1 != a && (this.getOutputInfo(a).name = b); + }; + p.prototype.onSubgraphTypeChangeOutput = function(a, b) { + a = this.findOutputSlot(a); + -1 != a && (this.getOutputInfo(a).type = b); + }; + p.prototype.onSubgraphRemovedOutput = function(a) { + a = this.findInputSlot(a); + -1 != a && this.removeOutput(a); + }; + p.prototype.getExtraMenuOptions = function(a) { + var b = this; + return [{content:"Open", callback:function() { + a.openSubgraph(b.subgraph); + }}]; + }; + p.prototype.onResize = function(a) { + a[1] += 20; + }; + p.prototype.serialize = function() { + var a = h.LGraphNode.prototype.serialize.call(this); + a.subgraph = this.subgraph.serialize(); + return a; + }; + p.prototype.clone = function() { + var a = h.createNode(this.type), b = this.serialize(); + delete b.id; + delete b.inputs; + delete b.outputs; + a.configure(b); + return a; + }; + h.Subgraph = p; + h.registerNodeType("graph/subgraph", p); + m.title = "Input"; + m.desc = "Input of the graph"; + m.prototype.onConfigure = function() { + this.updateType(); + }; + m.prototype.updateType = function() { + var a = this.properties.type; + this.type_widget.value = a; + this.outputs[0].type != a && (this.outputs[0].type = a, this.disconnectOutput(0)); + "number" == a ? (this.value_widget.type = "number", this.value_widget.value = 0) : "boolean" == a ? (this.value_widget.type = "toggle", this.value_widget.value = !0) : "string" == a ? (this.value_widget.type = "text", this.value_widget.value = "") : (this.value_widget.type = null, this.value_widget.value = null); + this.properties.value = this.value_widget.value; + }; + m.prototype.onPropertyChanged = function(a, b) { + if ("name" == a) { + if ("" == b || b == this.name_in_graph || "enabled" == b) { + return !1; + } + this.graph && (this.name_in_graph ? this.graph.renameInput(this.name_in_graph, b) : this.graph.addInput(b, this.properties.type)); + this.name_in_graph = this.name_widget.value = b; + } else { + "type" == a && this.updateType(b || ""); + } + }; + m.prototype.getTitle = function() { + return this.flags.collapsed ? this.properties.name : this.title; + }; + m.prototype.onAction = function(a, b) { + this.properties.type == h.EVENT && this.triggerSlot(0, b); + }; + m.prototype.onExecute = function() { + var a = this.graph.inputs[this.properties.name]; + a ? this.setOutputData(0, void 0 !== a.value ? a.value : this.properties.value) : this.setOutputData(0, this.properties.value); + }; + m.prototype.onRemoved = function() { + this.name_in_graph && this.graph.removeInput(this.name_in_graph); + }; + h.GraphInput = m; + h.registerNodeType("graph/input", m); + g.title = "Output"; + g.desc = "Output of the graph"; + g.prototype.onExecute = function() { + this._value = this.getInputData(0); + this.graph.setOutputData(this.properties.name, this._value); + }; + g.prototype.onAction = function(a, b) { + this.properties.type == h.ACTION && this.graph.trigger(this.properties.name, b); + }; + g.prototype.onRemoved = function() { + this.name_in_graph && this.graph.removeOutput(this.name_in_graph); + }; + g.prototype.getTitle = function() { + return this.flags.collapsed ? this.properties.name : this.title; + }; + h.GraphOutput = g; + h.registerNodeType("graph/output", g); + u.title = "Const Number"; + u.desc = "Constant number"; + u.prototype.onExecute = function() { + this.setOutputData(0, parseFloat(this.properties.value)); + }; + u.prototype.getTitle = function() { + return this.flags.collapsed ? this.properties.value : this.title; + }; + u.prototype.setValue = function(a) { + this.setProperty("value", a); + }; + u.prototype.onDrawBackground = function(a) { + this.outputs[0].label = this.properties.value.toFixed(3); + }; + h.registerNodeType("basic/const", u); + l.title = "Const Boolean"; + l.desc = "Constant boolean"; + l.prototype.getTitle = u.prototype.getTitle; + l.prototype.onExecute = function() { + this.setOutputData(0, this.properties.value); + }; + l.prototype.setValue = u.prototype.setValue; + l.prototype.onGetInputs = function() { + return [["toggle", h.ACTION]]; + }; + l.prototype.onAction = function(a) { + this.setValue(!this.properties.value); + }; + h.registerNodeType("basic/boolean", l); + B.title = "Const String"; + B.desc = "Constant string"; + B.prototype.getTitle = u.prototype.getTitle; + B.prototype.onExecute = function() { + this.setOutputData(0, this.properties.value); + }; + B.prototype.setValue = u.prototype.setValue; + B.prototype.onDropFile = function(a) { + var b = this, d = new FileReader; + d.onload = function(a) { + b.setProperty("value", a.target.result); + }; + d.readAsText(a); + }; + h.registerNodeType("basic/string", B); + y.title = "Const File"; + y.desc = "Fetches a file from an url"; + y["@type"] = {type:"enum", values:["text", "arraybuffer", "blob", "json"]}; + y.prototype.onPropertyChanged = function(a, b) { + "url" == a && (null == b || "" == b ? this._data = null : this.fetchFile(b)); + }; + y.prototype.onExecute = function() { + var a = this.getInputData(0) || this.properties.url; + !a || a == this._url && this._type == this.properties.type || this.fetchFile(a); + this.setOutputData(0, this._data); + }; + y.prototype.setValue = u.prototype.setValue; + y.prototype.fetchFile = function(a) { + var b = this; + a && a.constructor === String ? (this._url = a, this._type = this.properties.type, "http" == a.substr(0, 4) && h.proxy && (a = h.proxy + a.substr(a.indexOf(":") + 3)), fetch(a).then(function(a) { + if (!a.ok) { + throw Error("File not found"); + } + if ("arraybuffer" == b.properties.type) { + return a.arrayBuffer(); + } + if ("text" == b.properties.type) { + return a.text(); + } + if ("json" == b.properties.type) { + return a.json(); + } + if ("blob" == b.properties.type) { + return a.blob(); + } + }).then(function(a) { + b._data = a; + b.boxcolor = "#AEA"; + }).catch(function(d) { + b._data = null; + b.boxcolor = "red"; + console.error("error fetching file:", a); + })) : (b._data = null, b.boxcolor = null); + }; + y.prototype.onDropFile = function(a) { + var b = this; + this._url = a.name; + this._type = this.properties.type; + this.properties.url = a.name; + var d = new FileReader; + d.onload = function(a) { + b.boxcolor = "#AEA"; + a = a.target.result; + "json" == b.properties.type && (a = JSON.parse(a)); + b._data = a; + }; + if ("arraybuffer" == b.properties.type) { + d.readAsArrayBuffer(a); + } else { + if ("text" == b.properties.type || "json" == b.properties.type) { + d.readAsText(a); + } else { + if ("blob" == b.properties.type) { + return d.readAsBinaryString(a); + } + } + } + }; + h.registerNodeType("basic/file", y); + v.title = "Const Data"; + v.desc = "Constant Data"; + v.prototype.onPropertyChanged = function(a, b) { + this.widget.value = b; + if (null != b && "" != b) { + try { + this._value = JSON.parse(b), this.boxcolor = "#AEA"; + } catch (H) { + this.boxcolor = "red"; + } + } + }; + v.prototype.onExecute = function() { + this.setOutputData(0, this._value); + }; + v.prototype.setValue = u.prototype.setValue; + h.registerNodeType("basic/data", v); + E.title = "Const Array"; + E.desc = "Constant Array"; + E.prototype.onPropertyChanged = function(a, b) { + this.widget.value = b; + if (null != b && "" != b) { + try { + this._value = JSON.parse(b), this.boxcolor = "#AEA"; + } catch (H) { + this.boxcolor = "red"; + } + } + }; + E.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a && a.length) { + this._value || (this._value = []); + this._value.length = a.length; + for (var b = 0; b < a.length; ++b) { + this._value[b] = a[b]; + } + } + this.setOutputData(0, this._value); + }; + E.prototype.setValue = u.prototype.setValue; + h.registerNodeType("basic/array", E); + z.title = "Array[i]"; + z.desc = "Returns an element from an array"; + z.prototype.onExecute = function() { + var a = this.getInputData(0), b = this.getInputData(1); + null == b && (b = this.properties.index); + null != a && null != b && this.setOutputData(0, a[Math.floor(Number(b))]); + }; + h.registerNodeType("basic/array[]", z); + e.title = "Table[row][col]"; + e.desc = "Returns an element from a table"; + e.prototype.onExecute = function() { + var a = this.getInputData(0), b = this.getInputData(1), d = this.getInputData(2); + null == b && (b = this.properties.row); + null == d && (d = this.properties.column); + null != a && null != b && null != d && ((b = a[Math.floor(Number(b))]) ? this.setOutputData(0, b[Math.floor(Number(d))]) : this.setOutputData(0, null)); + }; + h.registerNodeType("basic/table[][]", e); + C.title = "Object property"; + C.desc = "Outputs the property of an object"; + C.prototype.setValue = function(a) { + this.properties.value = a; + this.widget.value = a; + }; + C.prototype.getTitle = function() { + return this.flags.collapsed ? "in." + this.properties.value : this.title; + }; + C.prototype.onPropertyChanged = function(a, b) { + this.widget.value = b; + }; + C.prototype.onExecute = function() { + var a = this.getInputData(0); + null != a && this.setOutputData(0, a[this.properties.value]); + }; + h.registerNodeType("basic/object_property", C); + D.title = "Object keys"; + D.desc = "Outputs an array with the keys of an object"; + D.prototype.onExecute = function() { + var a = this.getInputData(0); + null != a && this.setOutputData(0, Object.keys(a)); + }; + h.registerNodeType("basic/object_keys", D); + t.title = "Merge Objects"; + t.desc = "Creates an object copying properties from others"; + t.prototype.onExecute = function() { + var a = this.getInputData(0), b = this.getInputData(1), d = this._result; + if (a) { + for (var c in a) { + d[c] = a[c]; + } + } + if (b) { + for (c in b) { + d[c] = b[c]; + } + } + this.setOutputData(0, d); + }; + h.registerNodeType("basic/merge_objects", t); + G.title = "Variable"; + G.desc = "store/read variable value"; + G.prototype.onExecute = function() { + this.value = this.getInputData(0); + this.graph && (this.graph.vars[this.properties.varname] = this.value); + this.properties.global && (w[this.properties.varname] = this.value); + this.setOutputData(0, this.value); + }; + G.prototype.getTitle = function() { + return this.properties.varname; + }; + h.registerNodeType("basic/variable", G); + h.wrapFunctionAsNode("basic/length", function(a) { + return a && null != a.length ? Number(a.length) : 0; + }, ["*"], "number"); + n.title = "Download"; + n.desc = "Download some data"; + n.prototype.downloadAsFile = function() { + if (null != this.value) { + var a = null; + a = this.value.constructor === String ? this.value : JSON.stringify(this.value); + a = new Blob([a]); + var b = URL.createObjectURL(a); + a = document.createElement("a"); + a.setAttribute("href", b); + a.setAttribute("download", this.properties.filename); + a.style.display = "none"; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + setTimeout(function() { + URL.revokeObjectURL(b); + }, 6E4); + } + }; + n.prototype.onAction = function(a, b) { + var d = this; + setTimeout(function() { + d.downloadAsFile(); + }, 100); + }; + n.prototype.onExecute = function() { + this.inputs[0] && (this.value = this.getInputData(0)); + }; + n.prototype.getTitle = function() { + return this.flags.collapsed ? this.properties.filename : this.title; + }; + h.registerNodeType("basic/download", n); + q.title = "Watch"; + q.desc = "Show value of input"; + q.prototype.onExecute = function() { + this.inputs[0] && (this.value = this.getInputData(0)); + }; + q.prototype.getTitle = function() { + return this.flags.collapsed ? this.inputs[0].label : this.title; + }; + q.toString = function(a) { + if (null == a) { + return "null"; + } + if (a.constructor === Number) { + return a.toFixed(3); + } + if (a.constructor === Array) { + for (var b = "[", d = 0; d < a.length; ++d) { + b += q.toString(a[d]) + (d + 1 != a.length ? "," : ""); + } + return b + "]"; + } + return String(a); + }; + q.prototype.onDrawBackground = function(a) { + this.inputs[0].label = q.toString(this.value); + }; + h.registerNodeType("basic/watch", q); + k.title = "Cast"; + k.desc = "Allows to connect different types"; + k.prototype.onExecute = function() { + this.setOutputData(0, this.getInputData(0)); + }; + h.registerNodeType("basic/cast", k); + a.title = "Console"; + a.desc = "Show value inside the console"; + a.prototype.onAction = function(a, b) { + "log" == a ? console.log(b) : "warn" == a ? console.warn(b) : "error" == a && console.error(b); + }; + a.prototype.onExecute = function() { + var a = this.getInputData(1); + null !== a && (this.properties.msg = a); + console.log(a); + }; + a.prototype.onGetInputs = function() { + return [["log", h.ACTION], ["warn", h.ACTION], ["error", h.ACTION]]; + }; + h.registerNodeType("basic/console", a); + b.title = "Alert"; + b.desc = "Show an alert window"; + b.color = "#510"; + b.prototype.onConfigure = function(a) { + this.widget.value = a.properties.msg; + }; + b.prototype.onAction = function(a, b) { + var d = this.properties.msg; + setTimeout(function() { + alert(d); + }, 10); + }; + h.registerNodeType("basic/alert", b); + d.prototype.onConfigure = function(a) { + a.properties.onExecute && h.allow_scripts ? this.compileCode(a.properties.onExecute) : console.warn("Script not compiled, LiteGraph.allow_scripts is false"); + }; + d.title = "Script"; + d.desc = "executes a code (max 100 characters)"; + d.widgets_info = {onExecute:{type:"code"}}; + d.prototype.onPropertyChanged = function(a, b) { + "onExecute" == a && h.allow_scripts ? this.compileCode(b) : console.warn("Script not compiled, LiteGraph.allow_scripts is false"); + }; + d.prototype.compileCode = function(a) { + this._func = null; + if (256 < a.length) { + console.warn("Script too long, max 256 chars"); + } else { + for (var b = a.toLowerCase(), d = "script body document eval nodescript function".split(" "), c = 0; c < d.length; ++c) { + if (-1 != b.indexOf(d[c])) { + console.warn("invalid script"); + return; + } + } + try { + this._func = new Function("A", "B", "C", "DATA", "node", a); + } catch (J) { + console.error("Error parsing script"), console.error(J); + } + } + }; + d.prototype.onExecute = function() { + if (this._func) { + try { + var a = this.getInputData(0), b = this.getInputData(1), d = this.getInputData(2); + this.setOutputData(0, this._func(a, b, d, this.data, this)); + } catch (I) { + console.error("Error in script"), console.error(I); + } + } + }; + d.prototype.onGetOutputs = function() { + return [["C", ""]]; + }; + h.registerNodeType("basic/script", d); +})(this); +(function(w) { + function c() { + this.size = [60, 30]; + this.addInput("event", v.ACTION); + } + function p() { + this.size = [60, 30]; + this.addInput("if", ""); + this.addOutput("true", v.EVENT); + this.addOutput("change", v.EVENT); + this.addOutput("false", v.EVENT); + this.properties = {only_on_change:!0}; + this.prev = 0; + } + function m() { + this.addInput("", v.ACTION); + this.addInput("", v.ACTION); + this.addInput("", v.ACTION); + this.addInput("", v.ACTION); + this.addInput("", v.ACTION); + this.addInput("", v.ACTION); + this.addOutput("", v.EVENT); + this.addOutput("", v.EVENT); + this.addOutput("", v.EVENT); + this.addOutput("", v.EVENT); + this.addOutput("", v.EVENT); + this.addOutput("", v.EVENT); + this.size = [120, 30]; + this.flags = {horizontal:!0, render_box:!1}; + } + function g() { + this.size = [60, 30]; + this.addInput("event", v.ACTION); + this.addOutput("event", v.EVENT); + this.properties = {equal_to:"", has_property:"", property_equal_to:""}; + } + function u() { + this.addInput("inc", v.ACTION); + this.addInput("dec", v.ACTION); + this.addInput("reset", v.ACTION); + this.addOutput("change", v.EVENT); + this.addOutput("num", "number"); + this.num = 0; + } + function l() { + this.size = [60, 30]; + this.addProperty("time_in_ms", 1000); + this.addInput("event", v.ACTION); + this.addOutput("on_time", v.EVENT); + this._pending = []; + } + function B() { + this.addProperty("interval", 1000); + this.addProperty("event", "tick"); + this.addOutput("on_tick", v.EVENT); + this.time = 0; + this.last_interval = 1000; + this.triggered = !1; + } + function y() { + this.addInput("data", ""); + this.addInput("assign", v.ACTION); + this.addOutput("data", ""); + this._last_value = null; + this.properties = {data:null, serialize:!0}; + var c = this; + this.addWidget("button", "store", "", function() { + c.properties.data = c._last_value; + }); + } + var v = w.LiteGraph; + c.title = "Log Event"; + c.desc = "Log event in console"; + c.prototype.onAction = function(c, g) { + console.log(c, g); + }; + v.registerNodeType("events/log", c); + p.title = "TriggerEvent"; + p.desc = "Triggers event if input evaluates to true"; + p.prototype.onExecute = function(c, g) { + c = this.getInputData(0); + var e = c != this.prev; + 0 === this.prev && (e = !1); + var l = e && this.properties.only_on_change || !e && !this.properties.only_on_change; + c && l && this.triggerSlot(0, g); + !c && l && this.triggerSlot(2, g); + e && this.triggerSlot(1, g); + this.prev = c; + }; + v.registerNodeType("events/trigger", p); + m.title = "Sequencer"; + m.desc = "Trigger events when an event arrives"; + m.prototype.getTitle = function() { + return ""; + }; + m.prototype.onAction = function(c, g) { + if (this.outputs) { + for (c = 0; c < this.outputs.length; ++c) { + this.triggerSlot(c, g); + } + } + }; + v.registerNodeType("events/sequencer", m); + g.title = "Filter Event"; + g.desc = "Blocks events that do not match the filter"; + g.prototype.onAction = function(c, g) { + if (null != g && (!this.properties.equal_to || this.properties.equal_to == g)) { + if (this.properties.has_property && (c = g[this.properties.has_property], null == c || this.properties.property_equal_to && this.properties.property_equal_to != c)) { + return; + } + this.triggerSlot(0, g); + } + }; + v.registerNodeType("events/filter", g); + u.title = "Counter"; + u.desc = "Counts events"; + u.prototype.getTitle = function() { + return this.flags.collapsed ? String(this.num) : this.title; + }; + u.prototype.onAction = function(c, g) { + g = this.num; + "inc" == c ? this.num += 1 : "dec" == c ? --this.num : "reset" == c && (this.num = 0); + this.num != g && this.trigger("change", this.num); + }; + u.prototype.onDrawBackground = function(c) { + this.flags.collapsed || (c.fillStyle = "#AAA", c.font = "20px Arial", c.textAlign = "center", c.fillText(this.num, 0.5 * this.size[0], 0.5 * this.size[1])); + }; + u.prototype.onExecute = function() { + this.setOutputData(1, this.num); + }; + v.registerNodeType("events/counter", u); + l.title = "Delay"; + l.desc = "Delays one event"; + l.prototype.onAction = function(c, g) { + c = this.properties.time_in_ms; + 0 >= c ? this.trigger(null, g) : this._pending.push([c, g]); + }; + l.prototype.onExecute = function() { + var c = 1000 * this.graph.elapsed_time; + this.isInputConnected(1) && (this.properties.time_in_ms = this.getInputData(1)); + for (var g = 0; g < this._pending.length; ++g) { + var e = this._pending[g]; + e[0] -= c; + 0 < e[0] || (this._pending.splice(g, 1), --g, this.trigger(null, e[1])); + } + }; + l.prototype.onGetInputs = function() { + return [["event", v.ACTION], ["time_in_ms", "number"]]; + }; + v.registerNodeType("events/delay", l); + B.title = "Timer"; + B.desc = "Sends an event every N milliseconds"; + B.prototype.onStart = function() { + this.time = 0; + }; + B.prototype.getTitle = function() { + return "Timer: " + this.last_interval.toString() + "ms"; + }; + B.on_color = "#AAA"; + B.off_color = "#222"; + B.prototype.onDrawBackground = function() { + this.boxcolor = this.triggered ? B.on_color : B.off_color; + this.triggered = !1; + }; + B.prototype.onExecute = function() { + var c = 0 == this.time; + this.time += 1000 * this.graph.elapsed_time; + this.last_interval = Math.max(1, this.getInputOrProperty("interval") | 0); + !c && (this.time < this.last_interval || isNaN(this.last_interval)) ? this.inputs && 1 < this.inputs.length && this.inputs[1] && this.setOutputData(1, !1) : (this.triggered = !0, this.time %= this.last_interval, this.trigger("on_tick", this.properties.event), this.inputs && 1 < this.inputs.length && this.inputs[1] && this.setOutputData(1, !0)); + }; + B.prototype.onGetInputs = function() { + return [["interval", "number"]]; + }; + B.prototype.onGetOutputs = function() { + return [["tick", "boolean"]]; + }; + v.registerNodeType("events/timer", B); + y.title = "Data Store"; + y.desc = "Stores data and only changes when event is received"; + y.prototype.onExecute = function() { + this._last_value = this.getInputData(0); + this.setOutputData(0, this.properties.data); + }; + y.prototype.onAction = function(c, g) { + this.properties.data = this._last_value; + }; + y.prototype.onSerialize = function(c) { + null != c.data && (0 == this.properties.serialize || c.data.constructor !== String && c.data.constructor !== Number && c.data.constructor !== Boolean && c.data.constructor !== Array && c.data.constructor !== Object) && (c.data = null); + }; + v.registerNodeType("basic/data_store", y); +})(this); +(function(w) { + function c() { + this.addOutput("", z.EVENT); + this.addOutput("", "boolean"); + this.addProperty("text", "click me"); + this.addProperty("font_size", 30); + this.addProperty("message", ""); + this.size = [164, 84]; + this.clicked = !1; + } + function p() { + this.addInput("", "boolean"); + this.addInput("e", z.ACTION); + this.addOutput("v", "boolean"); + this.addOutput("e", z.EVENT); + this.properties = {font:"", value:!1}; + this.size = [160, 44]; + } + function m() { + this.addOutput("", "number"); + this.size = [80, 60]; + this.properties = {min:-1000, max:1000, value:1, step:1}; + this.old_y = -1; + this._precision = this._remainder = 0; + this.mouse_captured = !1; + } + function g() { + this.addOutput("", "string"); + this.addOutput("change", z.EVENT); + this.size = [80, 60]; + this.properties = {value:"A", values:"A;B;C"}; + this.old_y = -1; + this.mouse_captured = !1; + this._values = this.properties.values.split(";"); + var c = this; + this.widgets_up = !0; + this.widget = this.addWidget("combo", "", this.properties.value, function(e) { + c.properties.value = e; + c.triggerSlot(1, e); + }, {property:"value", values:this._values}); + } + function u() { + this.addOutput("", "number"); + this.size = [64, 84]; + this.properties = {min:0, max:1, value:0.5, color:"#7AF", precision:2}; + this.value = -1; + } + function l() { + this.addOutput("", "number"); + this.properties = {value:0.5, min:0, max:1, text:"V"}; + var c = this; + this.size = [140, 40]; + this.slider = this.addWidget("slider", "V", this.properties.value, function(e) { + c.properties.value = e; + }, this.properties); + this.widgets_up = !0; + } + function B() { + this.size = [160, 26]; + this.addOutput("", "number"); + this.properties = {color:"#7AF", min:0, max:1, value:0.5}; + this.value = -1; + } + function y() { + this.size = [160, 26]; + this.addInput("", "number"); + this.properties = {min:0, max:1, value:0, color:"#AAF"}; + } + function v() { + this.addInputs("", 0); + this.properties = {value:"...", font:"Arial", fontsize:18, color:"#AAA", align:"left", glowSize:0, decimals:1}; + } + function E() { + this.size = [200, 100]; + this.properties = {borderColor:"#ffffff", bgcolorTop:"#f0f0f0", bgcolorBottom:"#e0e0e0", shadowSize:2, borderRadius:3}; + } + var z = w.LiteGraph; + c.title = "Button"; + c.desc = "Triggers an event"; + c.font = "Arial"; + c.prototype.onDrawForeground = function(e) { + if (!this.flags.collapsed && (e.fillStyle = "black", e.fillRect(11, 11, this.size[0] - 20, this.size[1] - 20), e.fillStyle = "#AAF", e.fillRect(9, 9, this.size[0] - 20, this.size[1] - 20), e.fillStyle = this.clicked ? "white" : this.mouseOver ? "#668" : "#334", e.fillRect(10, 10, this.size[0] - 20, this.size[1] - 20), this.properties.text || 0 === this.properties.text)) { + var g = this.properties.font_size || 30; + e.textAlign = "center"; + e.fillStyle = this.clicked ? "black" : "white"; + e.font = g + "px " + c.font; + e.fillText(this.properties.text, 0.5 * this.size[0], 0.5 * this.size[1] + 0.3 * g); + e.textAlign = "left"; + } + }; + c.prototype.onMouseDown = function(c, g) { + if (1 < g[0] && 1 < g[1] && g[0] < this.size[0] - 2 && g[1] < this.size[1] - 2) { + return this.clicked = !0, this.triggerSlot(0, this.properties.message), !0; + } + }; + c.prototype.onExecute = function() { + this.setOutputData(1, this.clicked); + }; + c.prototype.onMouseUp = function(c) { + this.clicked = !1; + }; + z.registerNodeType("widget/button", c); + p.title = "Toggle"; + p.desc = "Toggles between true or false"; + p.prototype.onDrawForeground = function(c) { + if (!this.flags.collapsed) { + var e = 0.5 * this.size[1], g = 0.8 * this.size[1]; + c.font = this.properties.font || (0.8 * e).toFixed(0) + "px Arial"; + var l = c.measureText(this.title).width; + l = 0.5 * (this.size[0] - (l + e)); + c.fillStyle = "#AAA"; + c.fillRect(l, g - e, e, e); + c.fillStyle = this.properties.value ? "#AEF" : "#000"; + c.fillRect(l + 0.25 * e, g - e + 0.25 * e, .5 * e, .5 * e); + c.textAlign = "left"; + c.fillStyle = "#AAA"; + c.fillText(this.title, 1.2 * e + l, 0.85 * g); + c.textAlign = "left"; + } + }; + p.prototype.onAction = function(c) { + this.properties.value = !this.properties.value; + this.trigger("e", this.properties.value); + }; + p.prototype.onExecute = function() { + var c = this.getInputData(0); + null != c && (this.properties.value = c); + this.setOutputData(0, this.properties.value); + }; + p.prototype.onMouseDown = function(c, g) { + if (1 < g[0] && 1 < g[1] && g[0] < this.size[0] - 2 && g[1] < this.size[1] - 2) { + return this.properties.value = !this.properties.value, this.graph._version++, this.trigger("e", this.properties.value), !0; + } + }; + z.registerNodeType("widget/toggle", p); + m.title = "Number"; + m.desc = "Widget to select number value"; + m.pixels_threshold = 10; + m.markers_color = "#666"; + m.prototype.onDrawForeground = function(c) { + var e = 0.5 * this.size[0], g = this.size[1]; + 30 < g ? (c.fillStyle = m.markers_color, c.beginPath(), c.moveTo(e, 0.1 * g), c.lineTo(e + 0.1 * g, 0.2 * g), c.lineTo(e + -0.1 * g, 0.2 * g), c.fill(), c.beginPath(), c.moveTo(e, 0.9 * g), c.lineTo(e + 0.1 * g, 0.8 * g), c.lineTo(e + -0.1 * g, 0.8 * g), c.fill(), c.font = (0.7 * g).toFixed(1) + "px Arial") : c.font = (0.8 * g).toFixed(1) + "px Arial"; + c.textAlign = "center"; + c.font = (0.7 * g).toFixed(1) + "px Arial"; + c.fillStyle = "#EEE"; + c.fillText(this.properties.value.toFixed(this._precision), e, 0.75 * g); + }; + m.prototype.onExecute = function() { + this.setOutputData(0, this.properties.value); + }; + m.prototype.onPropertyChanged = function(c, g) { + c = (this.properties.step + "").split("."); + this._precision = 1 < c.length ? c[1].length : 0; + }; + m.prototype.onMouseDown = function(c, g) { + if (!(0 > g[1])) { + return this.old_y = c.canvasY, this.captureInput(!0), this.mouse_captured = !0; + } + }; + m.prototype.onMouseMove = function(c) { + if (this.mouse_captured) { + var e = this.old_y - c.canvasY; + c.shiftKey && (e *= 10); + if (c.metaKey || c.altKey) { + e *= 0.1; + } + this.old_y = c.canvasY; + c = this._remainder + e / m.pixels_threshold; + this._remainder = c % 1; + c = Math.clamp(this.properties.value + (c | 0) * this.properties.step, this.properties.min, this.properties.max); + this.properties.value = c; + this.graph._version++; + this.setDirtyCanvas(!0); + } + }; + m.prototype.onMouseUp = function(c, g) { + 200 > c.click_time && (this.properties.value = Math.clamp(this.properties.value + (g[1] > 0.5 * this.size[1] ? -1 : 1) * this.properties.step, this.properties.min, this.properties.max), this.graph._version++, this.setDirtyCanvas(!0)); + this.mouse_captured && (this.mouse_captured = !1, this.captureInput(!1)); + }; + z.registerNodeType("widget/number", m); + g.title = "Combo"; + g.desc = "Widget to select from a list"; + g.prototype.onExecute = function() { + this.setOutputData(0, this.properties.value); + }; + g.prototype.onPropertyChanged = function(c, g) { + "values" == c ? (this._values = g.split(";"), this.widget.options.values = this._values) : "value" == c && (this.widget.value = g); + }; + z.registerNodeType("widget/combo", g); + u.title = "Knob"; + u.desc = "Circular controller"; + u.size = [80, 100]; + u.prototype.onDrawForeground = function(c) { + if (!this.flags.collapsed) { + -1 == this.value && (this.value = (this.properties.value - this.properties.min) / (this.properties.max - this.properties.min)); + var e = 0.5 * this.size[0], g = 0.5 * this.size[1], l = 0.5 * Math.min(this.size[0], this.size[1]) - 5; + c.globalAlpha = 1; + c.save(); + c.translate(e, g); + c.rotate(0.75 * Math.PI); + c.fillStyle = "rgba(0,0,0,0.5)"; + c.beginPath(); + c.moveTo(0, 0); + c.arc(0, 0, l, 0, 1.5 * Math.PI); + c.fill(); + c.strokeStyle = "black"; + c.fillStyle = this.properties.color; + c.lineWidth = 2; + c.beginPath(); + c.moveTo(0, 0); + c.arc(0, 0, l - 4, 0, 1.5 * Math.PI * Math.max(0.01, this.value)); + c.closePath(); + c.fill(); + c.lineWidth = 1; + c.globalAlpha = 1; + c.restore(); + c.fillStyle = "black"; + c.beginPath(); + c.arc(e, g, 0.75 * l, 0, 2 * Math.PI, !0); + c.fill(); + c.fillStyle = this.mouseOver ? "white" : this.properties.color; + c.beginPath(); + var m = this.value * Math.PI * 1.5 + 0.75 * Math.PI; + c.arc(e + Math.cos(m) * l * 0.65, g + Math.sin(m) * l * 0.65, 0.05 * l, 0, 2 * Math.PI, !0); + c.fill(); + c.fillStyle = this.mouseOver ? "white" : "#AAA"; + c.font = Math.floor(0.5 * l) + "px Arial"; + c.textAlign = "center"; + c.fillText(this.properties.value.toFixed(this.properties.precision), e, g + 0.15 * l); + } + }; + u.prototype.onExecute = function() { + this.setOutputData(0, this.properties.value); + this.boxcolor = z.colorToString([this.value, this.value, this.value]); + }; + u.prototype.onMouseDown = function(c) { + this.center = [0.5 * this.size[0], 0.5 * this.size[1] + 20]; + this.radius = 0.5 * this.size[0]; + if (20 > c.canvasY - this.pos[1] || z.distance([c.canvasX, c.canvasY], [this.pos[0] + this.center[0], this.pos[1] + this.center[1]]) > this.radius) { + return !1; + } + this.oldmouse = [c.canvasX - this.pos[0], c.canvasY - this.pos[1]]; + this.captureInput(!0); + return !0; + }; + u.prototype.onMouseMove = function(c) { + if (this.oldmouse) { + c = [c.canvasX - this.pos[0], c.canvasY - this.pos[1]]; + var e = this.value; + e -= 0.01 * (c[1] - this.oldmouse[1]); + 1.0 < e ? e = 1.0 : 0.0 > e && (e = 0.0); + this.value = e; + this.properties.value = this.properties.min + (this.properties.max - this.properties.min) * this.value; + this.oldmouse = c; + this.setDirtyCanvas(!0); + } + }; + u.prototype.onMouseUp = function(c) { + this.oldmouse && (this.oldmouse = null, this.captureInput(!1)); + }; + u.prototype.onPropertyChanged = function(c, g) { + if ("min" == c || "max" == c || "value" == c) { + return this.properties[c] = parseFloat(g), !0; + } + }; + z.registerNodeType("widget/knob", u); + l.title = "Inner Slider"; + l.prototype.onPropertyChanged = function(c, g) { + "value" == c && (this.slider.value = g); + }; + l.prototype.onExecute = function() { + this.setOutputData(0, this.properties.value); + }; + z.registerNodeType("widget/internal_slider", l); + B.title = "H.Slider"; + B.desc = "Linear slider controller"; + B.prototype.onDrawForeground = function(c) { + -1 == this.value && (this.value = (this.properties.value - this.properties.min) / (this.properties.max - this.properties.min)); + c.globalAlpha = 1; + c.lineWidth = 1; + c.fillStyle = "#000"; + c.fillRect(2, 2, this.size[0] - 4, this.size[1] - 4); + c.fillStyle = this.properties.color; + c.beginPath(); + c.rect(4, 4, (this.size[0] - 8) * this.value, this.size[1] - 8); + c.fill(); + }; + B.prototype.onExecute = function() { + this.properties.value = this.properties.min + (this.properties.max - this.properties.min) * this.value; + this.setOutputData(0, this.properties.value); + this.boxcolor = z.colorToString([this.value, this.value, this.value]); + }; + B.prototype.onMouseDown = function(c) { + if (0 > c.canvasY - this.pos[1]) { + return !1; + } + this.oldmouse = [c.canvasX - this.pos[0], c.canvasY - this.pos[1]]; + this.captureInput(!0); + return !0; + }; + B.prototype.onMouseMove = function(c) { + if (this.oldmouse) { + c = [c.canvasX - this.pos[0], c.canvasY - this.pos[1]]; + var e = this.value; + e += (c[0] - this.oldmouse[0]) / this.size[0]; + 1.0 < e ? e = 1.0 : 0.0 > e && (e = 0.0); + this.value = e; + this.oldmouse = c; + this.setDirtyCanvas(!0); + } + }; + B.prototype.onMouseUp = function(c) { + this.oldmouse = null; + this.captureInput(!1); + }; + B.prototype.onMouseLeave = function(c) { + }; + z.registerNodeType("widget/hslider", B); + y.title = "Progress"; + y.desc = "Shows data in linear progress"; + y.prototype.onExecute = function() { + var c = this.getInputData(0); + void 0 != c && (this.properties.value = c); + }; + y.prototype.onDrawForeground = function(c) { + c.lineWidth = 1; + c.fillStyle = this.properties.color; + var e = (this.properties.value - this.properties.min) / (this.properties.max - this.properties.min); + e = Math.min(1, e); + e = Math.max(0, e); + c.fillRect(2, 2, (this.size[0] - 4) * e, this.size[1] - 4); + }; + z.registerNodeType("widget/progress", y); + v.title = "Text"; + v.desc = "Shows the input value"; + v.widgets = [{name:"resize", text:"Resize box", type:"button"}, {name:"led_text", text:"LED", type:"minibutton"}, {name:"normal_text", text:"Normal", type:"minibutton"}]; + v.prototype.onDrawForeground = function(c) { + c.fillStyle = this.properties.color; + var e = this.properties.value; + this.properties.glowSize ? (c.shadowColor = this.properties.color, c.shadowOffsetX = 0, c.shadowOffsetY = 0, c.shadowBlur = this.properties.glowSize) : c.shadowColor = "transparent"; + var g = this.properties.fontsize; + c.textAlign = this.properties.align; + c.font = g.toString() + "px " + this.properties.font; + this.str = "number" == typeof e ? e.toFixed(this.properties.decimals) : e; + if ("string" == typeof this.str) { + e = this.str.split("\\n"); + for (var l in e) { + c.fillText(e[l], "left" == this.properties.align ? 15 : this.size[0] - 15, -0.15 * g + g * (parseInt(l) + 1)); + } + } + c.shadowColor = "transparent"; + this.last_ctx = c; + c.textAlign = "left"; + }; + v.prototype.onExecute = function() { + var c = this.getInputData(0); + null != c && (this.properties.value = c); + }; + v.prototype.resize = function() { + if (this.last_ctx) { + var c = this.str.split("\\n"); + this.last_ctx.font = this.properties.fontsize + "px " + this.properties.font; + var g = 0, l; + for (l in c) { + var m = this.last_ctx.measureText(c[l]).width; + g < m && (g = m); + } + this.size[0] = g + 20; + this.size[1] = 4 + c.length * this.properties.fontsize; + this.setDirtyCanvas(!0); + } + }; + v.prototype.onPropertyChanged = function(c, g) { + this.properties[c] = g; + this.str = "number" == typeof g ? g.toFixed(3) : g; + return !0; + }; + z.registerNodeType("widget/text", v); + E.title = "Panel"; + E.desc = "Non interactive panel"; + E.widgets = [{name:"update", text:"Update", type:"button"}]; + E.prototype.createGradient = function(c) { + "" == this.properties.bgcolorTop || "" == this.properties.bgcolorBottom ? this.lineargradient = 0 : (this.lineargradient = c.createLinearGradient(0, 0, 0, this.size[1]), this.lineargradient.addColorStop(0, this.properties.bgcolorTop), this.lineargradient.addColorStop(1, this.properties.bgcolorBottom)); + }; + E.prototype.onDrawForeground = function(c) { + this.flags.collapsed || (null == this.lineargradient && this.createGradient(c), this.lineargradient && (c.lineWidth = 1, c.strokeStyle = this.properties.borderColor, c.fillStyle = this.lineargradient, this.properties.shadowSize ? (c.shadowColor = "#000", c.shadowOffsetX = 0, c.shadowOffsetY = 0, c.shadowBlur = this.properties.shadowSize) : c.shadowColor = "transparent", c.roundRect(0, 0, this.size[0] - 1, this.size[1] - 1, this.properties.shadowSize), c.fill(), c.shadowColor = "transparent", + c.stroke())); + }; + z.registerNodeType("widget/panel", E); +})(this); +(function(w) { + function c() { + this.addOutput("left_x_axis", "number"); + this.addOutput("left_y_axis", "number"); + this.addOutput("button_pressed", p.EVENT); + this.properties = {gamepad_index:0, threshold:0.1}; + this._left_axis = new Float32Array(2); + this._right_axis = new Float32Array(2); + this._triggers = new Float32Array(2); + this._previous_buttons = new Uint8Array(17); + this._current_buttons = new Uint8Array(17); + } + var p = w.LiteGraph; + c.title = "Gamepad"; + c.desc = "gets the input of the gamepad"; + c.CENTER = 0; + c.LEFT = 1; + c.RIGHT = 2; + c.UP = 4; + c.DOWN = 8; + c.zero = new Float32Array(2); + c.buttons = "a b x y lb rb lt rt back start ls rs home".split(" "); + c.prototype.onExecute = function() { + var m = this.getGamepad(), g = this.properties.threshold || 0.0; + m && (this._left_axis[0] = Math.abs(m.xbox.axes.lx) > g ? m.xbox.axes.lx : 0, this._left_axis[1] = Math.abs(m.xbox.axes.ly) > g ? m.xbox.axes.ly : 0, this._right_axis[0] = Math.abs(m.xbox.axes.rx) > g ? m.xbox.axes.rx : 0, this._right_axis[1] = Math.abs(m.xbox.axes.ry) > g ? m.xbox.axes.ry : 0, this._triggers[0] = Math.abs(m.xbox.axes.ltrigger) > g ? m.xbox.axes.ltrigger : 0, this._triggers[1] = Math.abs(m.xbox.axes.rtrigger) > g ? m.xbox.axes.rtrigger : 0); + if (this.outputs) { + for (g = 0; g < this.outputs.length; g++) { + var p = this.outputs[g]; + if (p.links && p.links.length) { + var l = null; + if (m) { + switch(p.name) { + case "left_axis": + l = this._left_axis; + break; + case "right_axis": + l = this._right_axis; + break; + case "left_x_axis": + l = this._left_axis[0]; + break; + case "left_y_axis": + l = this._left_axis[1]; + break; + case "right_x_axis": + l = this._right_axis[0]; + break; + case "right_y_axis": + l = this._right_axis[1]; + break; + case "trigger_left": + l = this._triggers[0]; + break; + case "trigger_right": + l = this._triggers[1]; + break; + case "a_button": + l = m.xbox.buttons.a ? 1 : 0; + break; + case "b_button": + l = m.xbox.buttons.b ? 1 : 0; + break; + case "x_button": + l = m.xbox.buttons.x ? 1 : 0; + break; + case "y_button": + l = m.xbox.buttons.y ? 1 : 0; + break; + case "lb_button": + l = m.xbox.buttons.lb ? 1 : 0; + break; + case "rb_button": + l = m.xbox.buttons.rb ? 1 : 0; + break; + case "ls_button": + l = m.xbox.buttons.ls ? 1 : 0; + break; + case "rs_button": + l = m.xbox.buttons.rs ? 1 : 0; + break; + case "hat_left": + l = m.xbox.hatmap & c.LEFT; + break; + case "hat_right": + l = m.xbox.hatmap & c.RIGHT; + break; + case "hat_up": + l = m.xbox.hatmap & c.UP; + break; + case "hat_down": + l = m.xbox.hatmap & c.DOWN; + break; + case "hat": + l = m.xbox.hatmap; + break; + case "start_button": + l = m.xbox.buttons.start ? 1 : 0; + break; + case "back_button": + l = m.xbox.buttons.back ? 1 : 0; + break; + case "button_pressed": + for (p = 0; p < this._current_buttons.length; ++p) { + this._current_buttons[p] && !this._previous_buttons[p] && this.triggerSlot(g, c.buttons[p]); + } + } + } else { + switch(p.name) { + case "button_pressed": + break; + case "left_axis": + case "right_axis": + l = c.zero; + break; + default: + l = 0; + } + } + this.setOutputData(g, l); + } + } + } + }; + c.mapping = {a:0, b:1, x:2, y:3, lb:4, rb:5, lt:6, rt:7, back:8, start:9, ls:10, rs:11}; + c.mapping_array = "a b x y lb rb lt rt back start ls rs".split(" "); + c.prototype.getGamepad = function() { + var m = navigator.getGamepads || navigator.webkitGetGamepads || navigator.mozGetGamepads; + if (!m) { + return null; + } + m = m.call(navigator); + this._previous_buttons.set(this._current_buttons); + for (var g = this.properties.gamepad_index; 4 > g; g++) { + if (m[g]) { + m = m[g]; + g = this.xbox_mapping; + g || (g = this.xbox_mapping = {axes:[], buttons:{}, hat:"", hatmap:c.CENTER}); + g.axes.lx = m.axes[0]; + g.axes.ly = m.axes[1]; + g.axes.rx = m.axes[2]; + g.axes.ry = m.axes[3]; + g.axes.ltrigger = m.buttons[6].value; + g.axes.rtrigger = m.buttons[7].value; + g.hat = ""; + g.hatmap = c.CENTER; + for (var p = 0; p < m.buttons.length; p++) { + if (this._current_buttons[p] = m.buttons[p].pressed, 12 > p) { + g.buttons[c.mapping_array[p]] = m.buttons[p].pressed, m.buttons[p].was_pressed && this.trigger(c.mapping_array[p] + "_button_event"); + } else { + switch(p) { + case 12: + m.buttons[p].pressed && (g.hat += "up", g.hatmap |= c.UP); + break; + case 13: + m.buttons[p].pressed && (g.hat += "down", g.hatmap |= c.DOWN); + break; + case 14: + m.buttons[p].pressed && (g.hat += "left", g.hatmap |= c.LEFT); + break; + case 15: + m.buttons[p].pressed && (g.hat += "right", g.hatmap |= c.RIGHT); + break; + case 16: + g.buttons.home = m.buttons[p].pressed; + } + } + } + m.xbox = g; + return m; + } + } + }; + c.prototype.onDrawBackground = function(c) { + if (!this.flags.collapsed) { + var g = this._left_axis, m = this._right_axis; + c.strokeStyle = "#88A"; + c.strokeRect(0.5 * (g[0] + 1) * this.size[0] - 4, 0.5 * (g[1] + 1) * this.size[1] - 4, 8, 8); + c.strokeStyle = "#8A8"; + c.strokeRect(0.5 * (m[0] + 1) * this.size[0] - 4, 0.5 * (m[1] + 1) * this.size[1] - 4, 8, 8); + g = this.size[1] / this._current_buttons.length; + c.fillStyle = "#AEB"; + for (m = 0; m < this._current_buttons.length; ++m) { + this._current_buttons[m] && c.fillRect(0, g * m, 6, g); + } + } + }; + c.prototype.onGetOutputs = function() { + return [["left_axis", "vec2"], ["right_axis", "vec2"], ["left_x_axis", "number"], ["left_y_axis", "number"], ["right_x_axis", "number"], ["right_y_axis", "number"], ["trigger_left", "number"], ["trigger_right", "number"], ["a_button", "number"], ["b_button", "number"], ["x_button", "number"], ["y_button", "number"], ["lb_button", "number"], ["rb_button", "number"], ["ls_button", "number"], ["rs_button", "number"], ["start_button", "number"], ["back_button", "number"], ["a_button_event", p.EVENT], + ["b_button_event", p.EVENT], ["x_button_event", p.EVENT], ["y_button_event", p.EVENT], ["lb_button_event", p.EVENT], ["rb_button_event", p.EVENT], ["ls_button_event", p.EVENT], ["rs_button_event", p.EVENT], ["start_button_event", p.EVENT], ["back_button_event", p.EVENT], ["hat_left", "number"], ["hat_right", "number"], ["hat_up", "number"], ["hat_down", "number"], ["hat", "number"], ["button_pressed", p.EVENT]]; + }; + p.registerNodeType("input/gamepad", c); +})(this); +(function(w) { + function c() { + this.addInput("in", "*"); + this.size = [80, 30]; + } + function p() { + this.addInput("in"); + this.addOutput("out"); + this.size = [80, 30]; + } + function m() { + this.addInput("in"); + this.addOutput("out"); + } + function g() { + this.addInput("in", "number", {locked:!0}); + this.addOutput("out", "number", {locked:!0}); + this.addOutput("clamped", "number", {locked:!0}); + this.addProperty("in", 0); + this.addProperty("in_min", 0); + this.addProperty("in_max", 1); + this.addProperty("out_min", 0); + this.addProperty("out_max", 1); + this.size = [120, 50]; + } + function u() { + this.addOutput("value", "number"); + this.addProperty("min", 0); + this.addProperty("max", 1); + this.size = [80, 30]; + } + function l() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.addProperty("min", 0); + this.addProperty("max", 1); + this.addProperty("smooth", !0); + this.size = [90, 30]; + } + function B() { + this.addOutput("out", "number"); + this.addProperty("min_time", 1); + this.addProperty("max_time", 2); + this.addProperty("duration", 0.2); + this.size = [90, 30]; + this._blink_time = this._remaining_time = 0; + } + function y() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.size = [80, 30]; + this.addProperty("min", 0); + this.addProperty("max", 1); + } + function v() { + this.properties = {f:0.5}; + this.addInput("A", "number"); + this.addInput("B", "number"); + this.addOutput("out", "number"); + } + function E() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.size = [80, 30]; + } + function z() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.size = [80, 30]; + } + function e() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.size = [80, 30]; + } + function C() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.size = [80, 30]; + this.properties = {A:0, B:1}; + } + function D() { + this.addInput("in", "number", {label:""}); + this.addOutput("out", "number", {label:""}); + this.size = [80, 30]; + this.addProperty("factor", 1); + } + function t() { + this.addInput("v", "boolean"); + this.addInput("A"); + this.addInput("B"); + this.addOutput("out"); + } + function G() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.size = [80, 30]; + this.addProperty("samples", 10); + this._values = new Float32Array(10); + this._current = 0; + } + function n() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.addProperty("factor", 0.1); + this.size = [80, 30]; + this._value = null; + } + function q() { + this.addInput("A", "number"); + this.addInput("B", "number"); + this.addOutput("=", "number"); + this.addProperty("A", 1); + this.addProperty("B", 1); + this.addProperty("OP", "+", "enum", {values:q.values}); + } + function k() { + this.addInput("A", "number"); + this.addInput("B", "number"); + this.addOutput("A==B", "boolean"); + this.addOutput("A!=B", "boolean"); + this.addProperty("A", 0); + this.addProperty("B", 0); + } + function a() { + this.addInput("A", "number"); + this.addInput("B", "number"); + this.addOutput("true", "boolean"); + this.addOutput("false", "boolean"); + this.addProperty("A", 1); + this.addProperty("B", 1); + this.addProperty("OP", ">", "enum", {values:a.values}); + this.size = [80, 60]; + } + function b() { + this.addInput("inc", "number"); + this.addOutput("total", "number"); + this.addProperty("increment", 1); + this.addProperty("value", 0); + } + function d() { + this.addInput("v", "number"); + this.addOutput("sin", "number"); + this.addProperty("amplitude", 1); + this.addProperty("offset", 0); + this.bgImageUrl = "nodes/imgs/icon-sin.png"; + } + function h() { + this.addInput("x", "number"); + this.addInput("y", "number"); + this.addOutput("", "number"); + this.properties = {x:1.0, y:1.0, formula:"x+y"}; + this.code_widget = this.addWidget("text", "F(x,y)", this.properties.formula, function(a, b, d) { + d.properties.formula = a; + }); + this.addWidget("toggle", "allow", A.allow_scripts, function(a) { + A.allow_scripts = a; + }); + this._func = null; + } + function f() { + this.addInput("vec2", "vec2"); + this.addOutput("x", "number"); + this.addOutput("y", "number"); + } + function x() { + this.addInputs([["x", "number"], ["y", "number"]]); + this.addOutput("vec2", "vec2"); + this.properties = {x:0, y:0}; + this._data = new Float32Array(2); + } + function H() { + this.addInput("vec3", "vec3"); + this.addOutput("x", "number"); + this.addOutput("y", "number"); + this.addOutput("z", "number"); + } + function I() { + this.addInputs([["x", "number"], ["y", "number"], ["z", "number"]]); + this.addOutput("vec3", "vec3"); + this.properties = {x:0, y:0, z:0}; + this._data = new Float32Array(3); + } + function J() { + this.addInput("vec4", "vec4"); + this.addOutput("x", "number"); + this.addOutput("y", "number"); + this.addOutput("z", "number"); + this.addOutput("w", "number"); + } + function L() { + this.addInputs([["x", "number"], ["y", "number"], ["z", "number"], ["w", "number"]]); + this.addOutput("vec4", "vec4"); + this.properties = {x:0, y:0, z:0, w:0}; + this._data = new Float32Array(4); + } + var A = w.LiteGraph; + c.title = "Converter"; + c.desc = "type A to type B"; + c.prototype.onExecute = function() { + var a = this.getInputData(0); + if (null != a && this.outputs) { + for (var b = 0; b < this.outputs.length; b++) { + var d = this.outputs[b]; + if (d.links && d.links.length) { + var c = null; + switch(d.name) { + case "number": + c = a.length ? a[0] : parseFloat(a); + break; + case "vec2": + case "vec3": + case "vec4": + c = 1; + switch(d.name) { + case "vec2": + c = 2; + break; + case "vec3": + c = 3; + break; + case "vec4": + c = 4; + }c = new Float32Array(c); + if (a.length) { + for (d = 0; d < a.length && d < c.length; d++) { + c[d] = a[d]; + } + } else { + c[0] = parseFloat(a); + } + } + this.setOutputData(b, c); + } + } + } + }; + c.prototype.onGetOutputs = function() { + return [["number", "number"], ["vec2", "vec2"], ["vec3", "vec3"], ["vec4", "vec4"]]; + }; + A.registerNodeType("math/converter", c); + p.title = "Bypass"; + p.desc = "removes the type"; + p.prototype.onExecute = function() { + var a = this.getInputData(0); + this.setOutputData(0, a); + }; + A.registerNodeType("math/bypass", p); + m.title = "to Number"; + m.desc = "Cast to number"; + m.prototype.onExecute = function() { + var a = this.getInputData(0); + this.setOutputData(0, Number(a)); + }; + A.registerNodeType("math/to_number", m); + g.title = "Range"; + g.desc = "Convert a number from one range to another"; + g.prototype.getTitle = function() { + return this.flags.collapsed ? (this._last_v || 0).toFixed(2) : this.title; + }; + g.prototype.onExecute = function() { + if (this.inputs) { + for (var a = 0; a < this.inputs.length; a++) { + var b = this.inputs[a], d = this.getInputData(a); + void 0 !== d && (this.properties[b.name] = d); + } + } + d = this.properties["in"]; + if (void 0 === d || null === d || d.constructor !== Number) { + d = 0; + } + a = this.properties.in_min; + b = this.properties.out_min; + var c = this.properties.out_max; + this._last_v = (d - a) / (this.properties.in_max - a) * (c - b) + b; + this.setOutputData(0, this._last_v); + this.setOutputData(1, Math.clamp(this._last_v, b, c)); + }; + g.prototype.onDrawBackground = function(a) { + this.outputs[0].label = this._last_v ? this._last_v.toFixed(3) : "?"; + }; + g.prototype.onGetInputs = function() { + return [["in_min", "number"], ["in_max", "number"], ["out_min", "number"], ["out_max", "number"]]; + }; + A.registerNodeType("math/range", g); + u.title = "Rand"; + u.desc = "Random number"; + u.prototype.onExecute = function() { + if (this.inputs) { + for (var a = 0; a < this.inputs.length; a++) { + var b = this.inputs[a], d = this.getInputData(a); + void 0 !== d && (this.properties[b.name] = d); + } + } + a = this.properties.min; + this._last_v = Math.random() * (this.properties.max - a) + a; + this.setOutputData(0, this._last_v); + }; + u.prototype.onDrawBackground = function(a) { + this.outputs[0].label = (this._last_v || 0).toFixed(3); + }; + u.prototype.onGetInputs = function() { + return [["min", "number"], ["max", "number"]]; + }; + A.registerNodeType("math/rand", u); + l.title = "Noise"; + l.desc = "Random number with temporal continuity"; + l.data = null; + l.getValue = function(a, b) { + if (!l.data) { + l.data = new Float32Array(1024); + for (var d = 0; d < l.data.length; ++d) { + l.data[d] = Math.random(); + } + } + a %= 1024; + 0 > a && (a += 1024); + var c = Math.floor(a); + a -= c; + d = l.data[c]; + c = l.data[1023 == c ? 0 : c + 1]; + b && (a = a * a * a * (a * (6.0 * a - 15.0) + 10.0)); + return d * (1 - a) + c * a; + }; + l.prototype.onExecute = function() { + var a = this.getInputData(0) || 0; + a = l.getValue(a, this.properties.smooth); + var b = this.properties.min; + this._last_v = a * (this.properties.max - b) + b; + this.setOutputData(0, this._last_v); + }; + l.prototype.onDrawBackground = function(a) { + this.outputs[0].label = (this._last_v || 0).toFixed(3); + }; + A.registerNodeType("math/noise", l); + B.title = "Spikes"; + B.desc = "spike every random time"; + B.prototype.onExecute = function() { + var a = this.graph.elapsed_time; + this._remaining_time -= a; + this._blink_time -= a; + a = 0; + 0 < this._blink_time && (a = 1 / (Math.pow(this._blink_time / this.properties.duration * 8 - 4, 4) + 1)); + 0 > this._remaining_time ? (this._remaining_time = Math.random() * (this.properties.max_time - this.properties.min_time) + this.properties.min_time, this._blink_time = this.properties.duration, this.boxcolor = "#FFF") : this.boxcolor = "#000"; + this.setOutputData(0, a); + }; + A.registerNodeType("math/spikes", B); + y.title = "Clamp"; + y.desc = "Clamp number between min and max"; + y.prototype.onExecute = function() { + var a = this.getInputData(0); + null != a && (a = Math.max(this.properties.min, a), a = Math.min(this.properties.max, a), this.setOutputData(0, a)); + }; + y.prototype.getCode = function(a) { + a = ""; + this.isInputConnected(0) && (a += "clamp({{0}}," + this.properties.min + "," + this.properties.max + ")"); + return a; + }; + A.registerNodeType("math/clamp", y); + v.title = "Lerp"; + v.desc = "Linear Interpolation"; + v.prototype.onExecute = function() { + var a = this.getInputData(0); + null == a && (a = 0); + var b = this.getInputData(1); + null == b && (b = 0); + var d = this.properties.f, c = this.getInputData(2); + void 0 !== c && (d = c); + this.setOutputData(0, a * (1 - d) + b * d); + }; + v.prototype.onGetInputs = function() { + return [["f", "number"]]; + }; + A.registerNodeType("math/lerp", v); + E.title = "Abs"; + E.desc = "Absolute"; + E.prototype.onExecute = function() { + var a = this.getInputData(0); + null != a && this.setOutputData(0, Math.abs(a)); + }; + A.registerNodeType("math/abs", E); + z.title = "Floor"; + z.desc = "Floor number to remove fractional part"; + z.prototype.onExecute = function() { + var a = this.getInputData(0); + null != a && this.setOutputData(0, Math.floor(a)); + }; + A.registerNodeType("math/floor", z); + e.title = "Frac"; + e.desc = "Returns fractional part"; + e.prototype.onExecute = function() { + var a = this.getInputData(0); + null != a && this.setOutputData(0, a % 1); + }; + A.registerNodeType("math/frac", e); + C.title = "Smoothstep"; + C.desc = "Smoothstep"; + C.prototype.onExecute = function() { + var a = this.getInputData(0); + if (void 0 !== a) { + var b = this.properties.A; + a = Math.clamp((a - b) / (this.properties.B - b), 0.0, 1.0); + this.setOutputData(0, a * a * (3 - 2 * a)); + } + }; + A.registerNodeType("math/smoothstep", C); + D.title = "Scale"; + D.desc = "v * factor"; + D.prototype.onExecute = function() { + var a = this.getInputData(0); + null != a && this.setOutputData(0, a * this.properties.factor); + }; + A.registerNodeType("math/scale", D); + t.title = "Gate"; + t.desc = "if v is true, then outputs A, otherwise B"; + t.prototype.onExecute = function() { + var a = this.getInputData(0); + this.setOutputData(0, this.getInputData(a ? 1 : 2)); + }; + A.registerNodeType("math/gate", t); + G.title = "Average"; + G.desc = "Average Filter"; + G.prototype.onExecute = function() { + var a = this.getInputData(0); + null == a && (a = 0); + var b = this._values.length; + this._values[this._current % b] = a; + this._current += 1; + this._current > b && (this._current = 0); + for (var d = a = 0; d < b; ++d) { + a += this._values[d]; + } + this.setOutputData(0, a / b); + }; + G.prototype.onPropertyChanged = function(a, b) { + 1 > b && (b = 1); + this.properties.samples = Math.round(b); + a = this._values; + this._values = new Float32Array(this.properties.samples); + a.length <= this._values.length ? this._values.set(a) : this._values.set(a.subarray(0, this._values.length)); + }; + A.registerNodeType("math/average", G); + n.title = "TendTo"; + n.desc = "moves the output value always closer to the input"; + n.prototype.onExecute = function() { + var a = this.getInputData(0); + null == a && (a = 0); + var b = this.properties.factor; + this._value = null == this._value ? a : this._value * (1 - b) + a * b; + this.setOutputData(0, this._value); + }; + A.registerNodeType("math/tendTo", n); + q.values = "+ - * / % ^ max min".split(" "); + q.title = "Operation"; + q.desc = "Easy math operators"; + q["@OP"] = {type:"enum", title:"operation", values:q.values}; + q.size = [100, 60]; + q.prototype.getTitle = function() { + return "max" == this.properties.OP || "min" == this.properties.OP ? this.properties.OP + "(A,B)" : "A " + this.properties.OP + " B"; + }; + q.prototype.setValue = function(a) { + "string" == typeof a && (a = parseFloat(a)); + this.properties.value = a; + }; + q.prototype.onExecute = function() { + var a = this.getInputData(0), b = this.getInputData(1); + null != a ? this.properties.A = a : a = this.properties.A; + null != b ? this.properties.B = b : b = this.properties.B; + var d = 0; + switch(this.properties.OP) { + case "+": + d = a + b; + break; + case "-": + d = a - b; + break; + case "x": + case "X": + case "*": + d = a * b; + break; + case "/": + d = a / b; + break; + case "%": + d = a % b; + break; + case "^": + d = Math.pow(a, b); + break; + case "max": + d = Math.max(a, b); + break; + case "min": + d = Math.min(a, b); + break; + default: + console.warn("Unknown operation: " + this.properties.OP); + } + this.setOutputData(0, d); + }; + q.prototype.onDrawBackground = function(a) { + this.flags.collapsed || (a.font = "40px Arial", a.fillStyle = "#666", a.textAlign = "center", a.fillText(this.properties.OP, 0.5 * this.size[0], 0.5 * (this.size[1] + A.NODE_TITLE_HEIGHT)), a.textAlign = "left"); + }; + A.registerNodeType("math/operation", q); + A.registerSearchboxExtra("math/operation", "MAX", {properties:{OP:"max"}, title:"MAX()"}); + A.registerSearchboxExtra("math/operation", "MIN", {properties:{OP:"min"}, title:"MIN()"}); + k.title = "Compare"; + k.desc = "compares between two values"; + k.prototype.onExecute = function() { + var a = this.getInputData(0), b = this.getInputData(1); + void 0 !== a ? this.properties.A = a : a = this.properties.A; + void 0 !== b ? this.properties.B = b : b = this.properties.B; + for (var d = 0, c = this.outputs.length; d < c; ++d) { + var k = this.outputs[d]; + if (k.links && k.links.length) { + switch(k.name) { + case "A==B": + var f = a == b; + break; + case "A!=B": + f = a != b; + break; + case "A>B": + f = a > b; + break; + case "A=B": + f = a >= b; + } + this.setOutputData(d, f); + } + } + }; + k.prototype.onGetOutputs = function() { + return [["A==B", "boolean"], ["A!=B", "boolean"], ["A>B", "boolean"], ["A=B", "boolean"], ["A<=B", "boolean"]]; + }; + A.registerNodeType("math/compare", k); + A.registerSearchboxExtra("math/compare", "==", {outputs:[["A==B", "boolean"]], title:"A==B"}); + A.registerSearchboxExtra("math/compare", "!=", {outputs:[["A!=B", "boolean"]], title:"A!=B"}); + A.registerSearchboxExtra("math/compare", ">", {outputs:[["A>B", "boolean"]], title:"A>B"}); + A.registerSearchboxExtra("math/compare", "<", {outputs:[["A=", {outputs:[["A>=B", "boolean"]], title:"A>=B"}); + A.registerSearchboxExtra("math/compare", "<=", {outputs:[["A<=B", "boolean"]], title:"A<=B"}); + a.values = "> < == != <= >= || &&".split(" "); + a["@OP"] = {type:"enum", title:"operation", values:a.values}; + a.title = "Condition"; + a.desc = "evaluates condition between A and B"; + a.prototype.getTitle = function() { + return "A " + this.properties.OP + " B"; + }; + a.prototype.onExecute = function() { + var a = this.getInputData(0); + void 0 === a ? a = this.properties.A : this.properties.A = a; + var b = this.getInputData(1); + void 0 === b ? b = this.properties.B : this.properties.B = b; + var d = !0; + switch(this.properties.OP) { + case ">": + d = a > b; + break; + case "<": + d = a < b; + break; + case "==": + d = a == b; + break; + case "!=": + d = a != b; + break; + case "<=": + d = a <= b; + break; + case ">=": + d = a >= b; + break; + case "||": + d = a || b; + break; + case "&&": + d = a && b; + } + this.setOutputData(0, d); + this.setOutputData(1, !d); + }; + A.registerNodeType("math/condition", a); + b.title = "Accumulate"; + b.desc = "Increments a value every time"; + b.prototype.onExecute = function() { + null === this.properties.value && (this.properties.value = 0); + var a = this.getInputData(0); + this.properties.value = null !== a ? this.properties.value + a : this.properties.value + this.properties.increment; + this.setOutputData(0, this.properties.value); + }; + A.registerNodeType("math/accumulate", b); + d.title = "Trigonometry"; + d.desc = "Sin Cos Tan"; + d.prototype.onExecute = function() { + var a = this.getInputData(0); + null == a && (a = 0); + var b = this.properties.amplitude, d = this.findInputSlot("amplitude"); + -1 != d && (b = this.getInputData(d)); + var c = this.properties.offset; + d = this.findInputSlot("offset"); + -1 != d && (c = this.getInputData(d)); + d = 0; + for (var k = this.outputs.length; d < k; ++d) { + switch(this.outputs[d].name) { + case "sin": + var f = Math.sin(a); + break; + case "cos": + f = Math.cos(a); + break; + case "tan": + f = Math.tan(a); + break; + case "asin": + f = Math.asin(a); + break; + case "acos": + f = Math.acos(a); + break; + case "atan": + f = Math.atan(a); + } + this.setOutputData(d, b * f + c); + } + }; + d.prototype.onGetInputs = function() { + return [["v", "number"], ["amplitude", "number"], ["offset", "number"]]; + }; + d.prototype.onGetOutputs = function() { + return [["sin", "number"], ["cos", "number"], ["tan", "number"], ["asin", "number"], ["acos", "number"], ["atan", "number"]]; + }; + A.registerNodeType("math/trigonometry", d); + A.registerSearchboxExtra("math/trigonometry", "SIN()", {outputs:[["sin", "number"]], title:"SIN()"}); + A.registerSearchboxExtra("math/trigonometry", "COS()", {outputs:[["cos", "number"]], title:"COS()"}); + A.registerSearchboxExtra("math/trigonometry", "TAN()", {outputs:[["tan", "number"]], title:"TAN()"}); + h.title = "Formula"; + h.desc = "Compute formula"; + h.size = [160, 100]; + G.prototype.onPropertyChanged = function(a, b) { + "formula" == a && (this.code_widget.value = b); + }; + h.prototype.onExecute = function() { + if (A.allow_scripts) { + var a = this.getInputData(0), b = this.getInputData(1); + null != a ? this.properties.x = a : a = this.properties.x; + null != b ? this.properties.y = b : b = this.properties.y; + try { + this._func && this._func_code == this.properties.formula || (this._func = new Function("x", "y", "TIME", "return " + this.properties.formula), this._func_code = this.properties.formula); + var d = this._func(a, b, this.graph.globaltime); + this.boxcolor = null; + } catch (M) { + this.boxcolor = "red"; + } + this.setOutputData(0, d); + } + }; + h.prototype.getTitle = function() { + return this._func_code || "Formula"; + }; + h.prototype.onDrawBackground = function() { + var a = this.properties.formula; + this.outputs && this.outputs.length && (this.outputs[0].label = a); + }; + A.registerNodeType("math/formula", h); + f.title = "Vec2->XY"; + f.desc = "vector 2 to components"; + f.prototype.onExecute = function() { + var a = this.getInputData(0); + null != a && (this.setOutputData(0, a[0]), this.setOutputData(1, a[1])); + }; + A.registerNodeType("math3d/vec2-to-xy", f); + x.title = "XY->Vec2"; + x.desc = "components to vector2"; + x.prototype.onExecute = function() { + var a = this.getInputData(0); + null == a && (a = this.properties.x); + var b = this.getInputData(1); + null == b && (b = this.properties.y); + var d = this._data; + d[0] = a; + d[1] = b; + this.setOutputData(0, d); + }; + A.registerNodeType("math3d/xy-to-vec2", x); + H.title = "Vec3->XYZ"; + H.desc = "vector 3 to components"; + H.prototype.onExecute = function() { + var a = this.getInputData(0); + null != a && (this.setOutputData(0, a[0]), this.setOutputData(1, a[1]), this.setOutputData(2, a[2])); + }; + A.registerNodeType("math3d/vec3-to-xyz", H); + I.title = "XYZ->Vec3"; + I.desc = "components to vector3"; + I.prototype.onExecute = function() { + var a = this.getInputData(0); + null == a && (a = this.properties.x); + var b = this.getInputData(1); + null == b && (b = this.properties.y); + var d = this.getInputData(2); + null == d && (d = this.properties.z); + var c = this._data; + c[0] = a; + c[1] = b; + c[2] = d; + this.setOutputData(0, c); + }; + A.registerNodeType("math3d/xyz-to-vec3", I); + J.title = "Vec4->XYZW"; + J.desc = "vector 4 to components"; + J.prototype.onExecute = function() { + var a = this.getInputData(0); + null != a && (this.setOutputData(0, a[0]), this.setOutputData(1, a[1]), this.setOutputData(2, a[2]), this.setOutputData(3, a[3])); + }; + A.registerNodeType("math3d/vec4-to-xyzw", J); + L.title = "XYZW->Vec4"; + L.desc = "components to vector4"; + L.prototype.onExecute = function() { + var a = this.getInputData(0); + null == a && (a = this.properties.x); + var b = this.getInputData(1); + null == b && (b = this.properties.y); + var d = this.getInputData(2); + null == d && (d = this.properties.z); + var c = this.getInputData(3); + null == c && (c = this.properties.w); + var k = this._data; + k[0] = a; + k[1] = b; + k[2] = d; + k[3] = c; + this.setOutputData(0, k); + }; + A.registerNodeType("math3d/xyzw-to-vec4", L); +})(this); +(function(w) { + function c() { + this.addInput("sel", "number"); + this.addInput("A"); + this.addInput("B"); + this.addInput("C"); + this.addInput("D"); + this.addOutput("out"); + this.selected = 0; + } + function p() { + this.properties = {sequence:"A,B,C"}; + this.addInput("index", "number"); + this.addInput("seq"); + this.addOutput("out"); + this.index = 0; + this.values = this.properties.sequence.split(","); + } + var m = w.LiteGraph; + c.title = "Selector"; + c.desc = "selects an output"; + c.prototype.onDrawBackground = function(c) { + if (!this.flags.collapsed) { + c.fillStyle = "#AFB"; + var g = (this.selected + 1) * m.NODE_SLOT_HEIGHT + 6; + c.beginPath(); + c.moveTo(50, g); + c.lineTo(50, g + m.NODE_SLOT_HEIGHT); + c.lineTo(34, g + 0.5 * m.NODE_SLOT_HEIGHT); + c.fill(); + } + }; + c.prototype.onExecute = function() { + var c = this.getInputData(0); + if (null == c || c.constructor !== Number) { + c = 0; + } + this.selected = c = Math.round(c) % (this.inputs.length - 1); + c = this.getInputData(c + 1); + void 0 !== c && this.setOutputData(0, c); + }; + c.prototype.onGetInputs = function() { + return [["E", 0], ["F", 0], ["G", 0], ["H", 0]]; + }; + m.registerNodeType("logic/selector", c); + p.title = "Sequence"; + p.desc = "select one element from a sequence from a string"; + p.prototype.onPropertyChanged = function(c, m) { + "sequence" == c && (this.values = m.split(",")); + }; + p.prototype.onExecute = function() { + var c = this.getInputData(1); + c && c != this.current_sequence && (this.values = c.split(","), this.current_sequence = c); + c = this.getInputData(0); + null == c && (c = 0); + this.index = c = Math.round(c) % this.values.length; + this.setOutputData(0, this.values[c]); + }; + m.registerNodeType("logic/sequence", p); +})(this); +(function(w) { + function c() { + this.addOutput("tex", "Texture"); + this.addOutput("name", "string"); + this.properties = {name:"", filter:!0}; + this.size = [c.image_preview_size, c.image_preview_size]; + } + function p() { + this.addInput("Texture", "Texture"); + this.properties = {flipY:!1}; + this.size = [c.image_preview_size, c.image_preview_size]; + } + function m() { + this.addInput("Texture", "Texture"); + this.addOutput("tex", "Texture"); + this.addOutput("name", "string"); + this.properties = {name:"", generate_mipmaps:!1}; + } + function g() { + this.addInput("Texture", "Texture"); + this.addInput("TextureB", "Texture"); + this.addInput("value", "number"); + this.addOutput("Texture", "Texture"); + this.help = "

pixelcode must be vec3, uvcode must be vec2, is optional

\r\n\t\t

uv: tex. coords

color: texture colorB: textureB

time: scene time value: input value

For multiline you must type: result = ...

"; + this.properties = {value:1, pixelcode:"color + colorB * value", uvcode:"", precision:c.DEFAULT}; + this.has_error = !1; + } + function u() { + this.addOutput("out", "Texture"); + this.properties = {code:"", u_value:1, u_color:[1, 1, 1, 1], width:512, height:512, precision:c.DEFAULT}; + this.properties.code = u.pixel_shader; + this._uniforms = {u_value:1, u_color:vec4.create(), in_texture:0, texSize:vec2.create(), time:0}; + } + function l() { + this.addInput("in", "Texture"); + this.addInput("scale", "vec2"); + this.addInput("offset", "vec2"); + this.addOutput("out", "Texture"); + this.properties = {offset:vec2.fromValues(0, 0), scale:vec2.fromValues(1, 1), precision:c.DEFAULT}; + } + function B() { + this.addInput("in", "Texture"); + this.addInput("warp", "Texture"); + this.addInput("factor", "number"); + this.addOutput("out", "Texture"); + this.properties = {factor:0.01, scale:[1, 1], offset:[0, 0], precision:c.DEFAULT}; + this._uniforms = {u_texture:0, u_textureB:1, u_factor:1, u_scale:vec2.create(), u_offset:vec2.create()}; + } + function y() { + this.addInput("Texture", "Texture"); + this.properties = {additive:!1, antialiasing:!1, filter:!0, disable_alpha:!1, gamma:1.0, viewport:[0, 0, 1, 1]}; + this.size[0] = 130; + } + function v() { + this.addInput("Texture", "Texture"); + this.addOutput("", "Texture"); + this.properties = {size:0, generate_mipmaps:!1, precision:c.DEFAULT}; + } + function E() { + this.addInput("Texture", "Texture"); + this.addOutput("", "Texture"); + this.properties = {iterations:1, generate_mipmaps:!1, precision:c.DEFAULT}; + } + function z() { + this.addInput("Texture", "Texture"); + this.addOutput("tex", "Texture"); + this.addOutput("avg", "vec4"); + this.addOutput("lum", "number"); + this.properties = {use_previous_frame:!0, high_quality:!1}; + this._uniforms = {u_texture:0, u_mipmap_offset:0}; + this._luminance = new Float32Array(4); + } + function e() { + this.addInput("in", "Texture"); + this.addInput("factor", "Number"); + this.addOutput("out", "Texture"); + this.properties = {factor:0.5}; + this._uniforms = {u_texture:0, u_textureB:1, u_factor:this.properties.factor}; + } + function C() { + this.addInput("in", "Texture"); + this.addOutput("avg", "Texture"); + this.addOutput("array", "Texture"); + this.properties = {samples:64, frames_interval:1}; + this._uniforms = {u_texture:0, u_textureB:1, u_samples:this.properties.samples, u_isamples:1 / this.properties.samples}; + this.frame = 0; + } + function D() { + this.addInput("Image", "image"); + this.addOutput("", "Texture"); + this.properties = {}; + } + function t() { + this.addInput("Texture", "Texture"); + this.addInput("LUT", "Texture"); + this.addInput("Intensity", "number"); + this.addOutput("", "Texture"); + this.properties = {enabled:!0, intensity:1, precision:c.DEFAULT, texture:null}; + t._shader || (t._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, t.pixel_shader)); + } + function G() { + this.addInput("Texture", "Texture"); + this.addOutput("R", "Texture"); + this.addOutput("G", "Texture"); + this.addOutput("B", "Texture"); + this.addOutput("A", "Texture"); + G._shader || (G._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, G.pixel_shader)); + } + function n() { + this.addInput("R", "Texture"); + this.addInput("G", "Texture"); + this.addInput("B", "Texture"); + this.addInput("A", "Texture"); + this.addOutput("Texture", "Texture"); + this.properties = {precision:c.DEFAULT, R:1, G:1, B:1, A:1}; + this._color = vec4.create(); + this._uniforms = {u_textureR:0, u_textureG:1, u_textureB:2, u_textureA:3, u_color:this._color}; + } + function q() { + this.addOutput("Texture", "Texture"); + this._tex_color = vec4.create(); + this.properties = {color:vec4.create(), precision:c.DEFAULT}; + } + function k() { + this.addInput("A", "color"); + this.addInput("B", "color"); + this.addOutput("Texture", "Texture"); + this.properties = {angle:0, scale:1, A:[0, 0, 0], B:[1, 1, 1], texture_size:32}; + k._shader || (k._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, k.pixel_shader)); + this._uniforms = {u_angle:0, u_colorA:vec3.create(), u_colorB:vec3.create()}; + } + function a() { + this.addInput("A", "Texture"); + this.addInput("B", "Texture"); + this.addInput("Mixer", "Texture"); + this.addOutput("Texture", "Texture"); + this.properties = {factor:0.5, size_from_biggest:!0, invert:!1, precision:c.DEFAULT}; + this._uniforms = {u_textureA:0, u_textureB:1, u_textureMix:2, u_mix:vec4.create()}; + } + function b() { + this.addInput("Tex.", "Texture"); + this.addOutput("Edges", "Texture"); + this.properties = {invert:!0, threshold:!1, factor:1, precision:c.DEFAULT}; + b._shader || (b._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, b.pixel_shader)); + } + function d() { + this.addInput("Texture", "Texture"); + this.addInput("Distance", "number"); + this.addInput("Range", "number"); + this.addOutput("Texture", "Texture"); + this.properties = {distance:100, range:50, only_depth:!1, high_precision:!1}; + this._uniforms = {u_texture:0, u_distance:100, u_range:50, u_camera_planes:null}; + } + function h() { + this.addInput("Texture", "Texture"); + this.addOutput("Texture", "Texture"); + this.properties = {precision:c.DEFAULT, invert:!1}; + this._uniforms = {u_texture:0, u_near:0.1, u_far:10000}; + } + function f() { + this.addInput("Texture", "Texture"); + this.addInput("Iterations", "number"); + this.addInput("Intensity", "number"); + this.addOutput("Blurred", "Texture"); + this.properties = {intensity:1, iterations:1, preserve_aspect:!1, scale:[1, 1], precision:c.DEFAULT}; + } + function x() { + this.addInput("in", "Texture"); + this.addInput("dirt", "Texture"); + this.addOutput("out", "Texture"); + this.addOutput("glow", "Texture"); + this.properties = {enabled:!0, intensity:1, persistence:0.99, iterations:16, threshold:0, scale:1, dirt_factor:0.5, precision:c.DEFAULT}; + this._textures = []; + this._uniforms = {u_intensity:1, u_texture:0, u_glow_texture:1, u_threshold:0, u_texel_size:vec2.create()}; + } + function H() { + this.addInput("Texture", "Texture"); + this.addOutput("Filtered", "Texture"); + this.properties = {intensity:1, radius:5}; + } + function I() { + this.addInput("Texture", "Texture"); + this.addOutput("Filtered", "Texture"); + this.properties = {sigma:1.4, k:1.6, p:21.7, epsilon:79, phi:0.017}; + } + function J() { + this.addOutput("Webcam", "Texture"); + this.properties = {texture_name:"", facingMode:"user"}; + this.boxcolor = "black"; + this.version = 0; + } + function L() { + this.addInput("in", "Texture"); + this.addInput("f", "number"); + this.addOutput("out", "Texture"); + this.properties = {enabled:!0, factor:1, precision:c.LOW}; + this._uniforms = {u_texture:0, u_factor:1}; + } + function A() { + this.addInput("in", "Texture"); + this.addOutput("out", "Texture"); + this.properties = {precision:c.LOW, split_channels:!1}; + this._values = new Uint8Array(1024); + this._values.fill(255); + this._curve_texture = null; + this._uniforms = {u_texture:0, u_curve:1, u_range:1.0}; + this._must_update = !0; + this._points = {RGB:[[0, 0], [1, 1]], R:[[0, 0], [1, 1]], G:[[0, 0], [1, 1]], B:[[0, 0], [1, 1]]}; + this.curve_editor = null; + this.addWidget("toggle", "Split Channels", !1, "split_channels"); + this.addWidget("combo", "Channel", "RGB", {values:["RGB", "R", "G", "B"]}); + this.curve_offset = 68; + this.size = [240, 160]; + } + function r() { + this.addInput("in", "Texture"); + this.addInput("exp", "number"); + this.addOutput("out", "Texture"); + this.properties = {exposition:1, precision:c.LOW}; + this._uniforms = {u_texture:0, u_exposition:1}; + } + function K() { + this.addInput("in", "Texture"); + this.addInput("avg", "number,Texture"); + this.addOutput("out", "Texture"); + this.properties = {enabled:!0, scale:1, gamma:1, average_lum:1, lum_white:1, precision:c.LOW}; + this._uniforms = {u_texture:0, u_lumwhite2:1, u_igamma:1, u_scale:1, u_average_lum:1}; + } + function N() { + this.addOutput("out", "Texture"); + this.properties = {width:512, height:512, seed:0, persistence:0.1, octaves:8, scale:1, offset:[0, 0], amplitude:1, precision:c.DEFAULT}; + this._key = 0; + this._texture = null; + this._uniforms = {u_persistence:0.1, u_seed:0, u_offset:vec2.create(), u_scale:1, u_viewport:vec2.create()}; + } + function M() { + this.addInput("v"); + this.addOutput("out", "Texture"); + this.properties = {code:M.default_code, width:512, height:512, clear:!0, precision:c.DEFAULT, use_html_canvas:!1}; + this._temp_texture = this._func = null; + this.compileCode(); + } + function O() { + this.addInput("in", "Texture"); + this.addOutput("out", "Texture"); + this.properties = {key_color:vec3.fromValues(0, 1, 0), threshold:0.8, slope:0.2, precision:c.DEFAULT}; + } + function P() { + this.addInput("in", "texture"); + this.addInput("yaw", "number"); + this.addOutput("out", "texture"); + this.properties = {yaw:0}; + } + var F = w.LiteGraph; + w.LGraphTexture = null; + "undefined" != typeof GL && (LGraphCanvas.link_type_colors.Texture = "#987", w.LGraphTexture = c, c.title = "Texture", c.desc = "Texture", c.widgets_info = {name:{widget:"texture"}, filter:{widget:"checkbox"}}, c.loadTextureCallback = null, c.image_preview_size = 256, c.PASS_THROUGH = 1, c.COPY = 2, c.LOW = 3, c.HIGH = 4, c.REUSE = 5, c.DEFAULT = 2, c.MODE_VALUES = {"pass through":c.PASS_THROUGH, copy:c.COPY, low:c.LOW, high:c.HIGH, reuse:c.REUSE, default:c.DEFAULT}, c.getTexturesContainer = function() { + return gl.textures; + }, c.loadTexture = function(a, b) { + b = b || {}; + var d = a; + "http://" == d.substr(0, 7) && F.proxy && (d = F.proxy + d.substr(7)); + return c.getTexturesContainer()[a] = GL.Texture.fromURL(d, b); + }, c.getTexture = function(a) { + var b = this.getTexturesContainer(); + if (!b) { + throw "Cannot load texture, container of textures not found"; + } + b = b[a]; + return !b && a && ":" != a[0] ? this.loadTexture(a) : b; + }, c.getTargetTexture = function(a, b, d) { + if (!a) { + throw "LGraphTexture.getTargetTexture expects a reference texture"; + } + switch(d) { + case c.LOW: + d = gl.UNSIGNED_BYTE; + break; + case c.HIGH: + d = gl.HIGH_PRECISION_FORMAT; + break; + case c.REUSE: + return a; + default: + d = a ? a.type : gl.UNSIGNED_BYTE; + } + b && b.width == a.width && b.height == a.height && b.type == d || (b = new GL.Texture(a.width, a.height, {type:d, format:gl.RGBA, filter:gl.LINEAR})); + return b; + }, c.getTextureType = function(a, b) { + b = b ? b.type : gl.UNSIGNED_BYTE; + switch(a) { + case c.HIGH: + b = gl.HIGH_PRECISION_FORMAT; + break; + case c.LOW: + b = gl.UNSIGNED_BYTE; + } + return b; + }, c.getWhiteTexture = function() { + return this._white_texture ? this._white_texture : this._white_texture = GL.Texture.fromMemory(1, 1, [255, 255, 255, 255], {format:gl.RGBA, wrap:gl.REPEAT, filter:gl.NEAREST}); + }, c.getNoiseTexture = function() { + if (this._noise_texture) { + return this._noise_texture; + } + for (var a = new Uint8Array(1048576), b = 0; 1048576 > b; ++b) { + a[b] = 255 * Math.random(); + } + return this._noise_texture = a = GL.Texture.fromMemory(512, 512, a, {format:gl.RGBA, wrap:gl.REPEAT, filter:gl.NEAREST}); + }, c.prototype.onDropFile = function(a, b, d) { + a ? ("string" == typeof a ? a = GL.Texture.fromURL(a) : -1 != b.toLowerCase().indexOf(".dds") ? a = GL.Texture.fromDDSInMemory(a) : (a = new Blob([d]), a = URL.createObjectURL(a), a = GL.Texture.fromURL(a)), this._drop_texture = a, this.properties.name = b) : (this._drop_texture = null, this.properties.name = ""); + }, c.prototype.getExtraMenuOptions = function(a) { + var b = this; + if (this._drop_texture) { + return [{content:"Clear", callback:function() { + b._drop_texture = null; + b.properties.name = ""; + }}]; + } + }, c.prototype.onExecute = function() { + var a = null; + this.isOutputConnected(1) && (a = this.getInputData(0)); + !a && this._drop_texture && (a = this._drop_texture); + !a && this.properties.name && (a = c.getTexture(this.properties.name)); + if (a) { + this._last_tex = a; + !1 === this.properties.filter ? a.setParameter(gl.TEXTURE_MAG_FILTER, gl.NEAREST) : a.setParameter(gl.TEXTURE_MAG_FILTER, gl.LINEAR); + this.setOutputData(0, a); + this.setOutputData(1, a.fullpath || a.filename); + for (var b = 2; b < this.outputs.length; b++) { + var d = this.outputs[b]; + if (d) { + var k = null; + "width" == d.name ? k = a.width : "height" == d.name ? k = a.height : "aspect" == d.name && (k = a.width / a.height); + this.setOutputData(b, k); + } + } + } else { + this.setOutputData(0, null), this.setOutputData(1, ""); + } + }, c.prototype.onResourceRenamed = function(a, b) { + this.properties.name == a && (this.properties.name = b); + }, c.prototype.onDrawBackground = function(a) { + if (!(this.flags.collapsed || 20 >= this.size[1])) { + if (this._drop_texture && a.webgl) { + a.drawImage(this._drop_texture, 0, 0, this.size[0], this.size[1]); + } else { + if (this._last_preview_tex != this._last_tex) { + if (a.webgl) { + this._canvas = this._last_tex; + } else { + var b = c.generateLowResTexturePreview(this._last_tex); + if (!b) { + return; + } + this._last_preview_tex = this._last_tex; + this._canvas = cloneCanvas(b); + } + } + this._canvas && (a.save(), a.webgl || (a.translate(0, this.size[1]), a.scale(1, -1)), a.drawImage(this._canvas, 0, 0, this.size[0], this.size[1]), a.restore()); + } + } + }, c.generateLowResTexturePreview = function(a) { + if (!a) { + return null; + } + var b = c.image_preview_size, d = a; + if (a.format == gl.DEPTH_COMPONENT) { + return null; + } + if (a.width > b || a.height > b) { + d = this._preview_temp_tex, this._preview_temp_tex || (this._preview_temp_tex = d = new GL.Texture(b, b, {minFilter:gl.NEAREST})), a.copyTo(d); + } + a = this._preview_canvas; + a || (this._preview_canvas = a = createCanvas(b, b)); + d && d.toCanvas(a); + return a; + }, c.prototype.getResources = function(a) { + this.properties.name && (a[this.properties.name] = GL.Texture); + return a; + }, c.prototype.onGetInputs = function() { + return [["in", "Texture"]]; + }, c.prototype.onGetOutputs = function() { + return [["width", "number"], ["height", "number"], ["aspect", "number"]]; + }, c.replaceCode = function(a, b) { + return a.replace(/\{\{[a-zA-Z0-9_]*\}\}/g, function(a) { + a = a.replace(/[\{\}]/g, ""); + return b[a] || ""; + }); + }, F.registerNodeType("texture/texture", c), p.title = "Preview", p.desc = "Show a texture in the graph canvas", p.allow_preview = !1, p.prototype.onDrawBackground = function(a) { + if (!this.flags.collapsed && (a.webgl || p.allow_preview)) { + var b = this.getInputData(0); + b && (b = !b.handle && a.webgl ? b : c.generateLowResTexturePreview(b), a.save(), this.properties.flipY && (a.translate(0, this.size[1]), a.scale(1, -1)), a.drawImage(b, 0, 0, this.size[0], this.size[1]), a.restore()); + } + }, F.registerNodeType("texture/preview", p), m.title = "Save", m.desc = "Save a texture in the repository", m.prototype.getPreviewTexture = function() { + return this._texture; + }, m.prototype.onExecute = function() { + var a = this.getInputData(0); + a && (this.properties.generate_mipmaps && (a.bind(0), a.setParameter(gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR), gl.generateMipmap(a.texture_type), a.unbind(0)), this.properties.name && (c.storeTexture ? c.storeTexture(this.properties.name, a) : c.getTexturesContainer()[this.properties.name] = a), this._texture = a, this.setOutputData(0, a), this.setOutputData(1, this.properties.name)); + }, F.registerNodeType("texture/save", m), g.widgets_info = {uvcode:{widget:"code"}, pixelcode:{widget:"code"}, precision:{widget:"combo", values:c.MODE_VALUES}}, g.title = "Operation", g.desc = "Texture shader operation", g.presets = {}, g.prototype.getExtraMenuOptions = function(a) { + var b = this; + return [{content:b.properties.show ? "Hide Texture" : "Show Texture", callback:function() { + b.properties.show = !b.properties.show; + }}]; + }, g.prototype.onPropertyChanged = function() { + this.has_error = !1; + }, g.prototype.onDrawBackground = function(a) { + this.flags.collapsed || 20 >= this.size[1] || !this.properties.show || !this._tex || this._tex.gl != a || (a.save(), a.drawImage(this._tex, 0, 0, this.size[0], this.size[1]), a.restore()); + }, g.prototype.onExecute = function() { + var a = this.getInputData(0); + if (this.isOutputConnected(0)) { + if (this.properties.precision === c.PASS_THROUGH) { + this.setOutputData(0, a); + } else { + var b = this.getInputData(1); + if (this.properties.uvcode || this.properties.pixelcode) { + var d = 512, k = 512; + a ? (d = a.width, k = a.height) : b && (d = b.width, k = b.height); + b || (b = GL.Texture.getWhiteTexture()); + var f = c.getTextureType(this.properties.precision, a); + this._tex = a || this._tex ? c.getTargetTexture(a || this._tex, this._tex, this.properties.precision) : new GL.Texture(d, k, {type:f, format:gl.RGBA, filter:gl.LINEAR}); + f = ""; + this.properties.uvcode && (f = "uv = " + this.properties.uvcode, -1 != this.properties.uvcode.indexOf(";") && (f = this.properties.uvcode)); + var h = ""; + this.properties.pixelcode && (h = "result = " + this.properties.pixelcode, -1 != this.properties.pixelcode.indexOf(";") && (h = this.properties.pixelcode)); + var e = this._shader; + if (!(this.has_error || e && this._shader_code == f + "|" + h)) { + var n = c.replaceCode(g.pixel_shader, {UV_CODE:f, PIXEL_CODE:h}); + try { + e = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, n), this.boxcolor = "#00FF00"; + } catch (T) { + GL.Shader.dumpErrorToConsole(T, Shader.SCREEN_VERTEX_SHADER, n); + this.boxcolor = "#FF0000"; + this.has_error = !0; + return; + } + this._shader = e; + this._shader_code = f + "|" + h; + } + if (this._shader) { + var q = this.getInputData(2); + null != q ? this.properties.value = q : q = parseFloat(this.properties.value); + var l = this.graph.getTime(); + this._tex.drawTo(function() { + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + gl.disable(gl.BLEND); + a && a.bind(0); + b && b.bind(1); + var c = Mesh.getScreenQuad(); + e.uniforms({u_texture:0, u_textureB:1, value:q, texSize:[d, k], time:l}).draw(c); + }); + this.setOutputData(0, this._tex); + } + } + } + } + }, g.pixel_shader = "precision highp float;\n\r\n\t\t\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform vec2 texSize;\n\r\n\t\tuniform float time;\n\r\n\t\tuniform float value;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec2 uv = v_coord;\n\r\n\t\t\t{{UV_CODE}};\n\r\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\r\n\t\t\tvec3 color = color4.rgb;\n\r\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\r\n\t\t\tvec3 colorB = color4B.rgb;\n\r\n\t\t\tvec3 result = color;\n\r\n\t\t\tfloat alpha = 1.0;\n\r\n\t\t\t{{PIXEL_CODE}};\n\r\n\t\t\tgl_FragColor = vec4(result, alpha);\n\r\n\t\t}\n\r\n\t\t", + g.registerPreset = function(a, b) { + g.presets[a] = b; + }, g.registerPreset("", ""), g.registerPreset("bypass", "color"), g.registerPreset("add", "color + colorB * value"), g.registerPreset("substract", "(color - colorB) * value"), g.registerPreset("mate", "mix( color, colorB, color4B.a * value)"), g.registerPreset("invert", "vec3(1.0) - color"), g.registerPreset("multiply", "color * colorB * value"), g.registerPreset("divide", "(color / colorB) / value"), g.registerPreset("difference", "abs(color - colorB) * value"), g.registerPreset("max", "max(color, colorB) * value"), + g.registerPreset("min", "min(color, colorB) * value"), g.registerPreset("displace", "texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"), g.registerPreset("grayscale", "vec3(color.x + color.y + color.z) * value / 3.0"), g.registerPreset("saturation", "mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"), g.registerPreset("threshold", "vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"), g.prototype.onInspect = + function(a) { + var b = this; + a.addCombo("Presets", "", {values:Object.keys(g.presets), callback:function(d) { + var c = g.presets[d]; + c && (b.setProperty("pixelcode", c), b.title = d, a.refresh()); + }}); + }, F.registerNodeType("texture/operation", g), u.title = "Shader", u.desc = "Texture shader", u.widgets_info = {code:{type:"code", lang:"glsl"}, precision:{widget:"combo", values:c.MODE_VALUES}}, u.prototype.onPropertyChanged = function(a, b) { + if ("code" == a && (a = this.getShader())) { + b = a.uniformInfo; + if (this.inputs) { + for (var d = {}, c = 0; c < this.inputs.length; ++c) { + var k = this.getInputInfo(c); + k && (b[k.name] && !d[k.name] ? d[k.name] = !0 : (this.removeInput(c), c--)); + } + } + for (c in b) { + if (k = a.uniformInfo[c], null !== k.loc && "time" != c) { + if (this._shader.samplers[c]) { + b = "texture"; + } else { + switch(k.size) { + case 1: + b = "number"; + break; + case 2: + b = "vec2"; + break; + case 3: + b = "vec3"; + break; + case 4: + b = "vec4"; + break; + case 9: + b = "mat3"; + break; + case 16: + b = "mat4"; + break; + default: + continue; + } + } + d = this.findInputSlot(c); + if (-1 != d && (k = this.getInputInfo(d))) { + if (k.type == b) { + continue; + } + this.removeInput(d, b); + } + this.addInput(c, b); + } + } + } + }, u.prototype.getShader = function() { + if (this._shader && this._shader_code == this.properties.code) { + return this._shader; + } + this._shader_code = this.properties.code; + this._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, this.properties.code), this.boxcolor = "green"; + return this._shader; + }, u.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var a = this.getShader(); + if (a) { + var b = 0, d = null; + if (this.inputs) { + for (var k = 0; k < this.inputs.length; ++k) { + var f = this.getInputInfo(k), h = this.getInputData(k); + null != h && (h.constructor === GL.Texture && (h.bind(b), d || (d = h), h = b, b++), a.setUniform(f.name, h)); + } + } + var e = this._uniforms; + b = c.getTextureType(this.properties.precision, d); + k = this.properties.width | 0; + f = this.properties.height | 0; + 0 == k && (k = d ? d.width : gl.canvas.width); + 0 == f && (f = d ? d.height : gl.canvas.height); + e.texSize[0] = k; + e.texSize[1] = f; + e.time = this.graph.getTime(); + e.u_value = this.properties.u_value; + e.u_color.set(this.properties.u_color); + this._tex && this._tex.type == b && this._tex.width == k && this._tex.height == f || (this._tex = new GL.Texture(k, f, {type:b, format:gl.RGBA, filter:gl.LINEAR})); + this._tex.drawTo(function() { + a.uniforms(e).draw(GL.Mesh.getScreenQuad()); + }); + this.setOutputData(0, this._tex); + } + } + }, u.pixel_shader = "precision highp float;\n\r\n\n\r\nvarying vec2 v_coord;\n\r\nuniform float time; //time in seconds\n\r\nuniform vec2 texSize; //tex resolution\n\r\nuniform float u_value;\n\r\nuniform vec4 u_color;\n\n\r\nvoid main() {\n\r\n\tvec2 uv = v_coord;\n\r\n\tvec3 color = vec3(0.0);\n\r\n\t//your code here\n\r\n\tcolor.xy=uv;\n\n\r\n\tgl_FragColor = vec4(color, 1.0);\n\r\n}\n\r\n", F.registerNodeType("texture/shader", u), l.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, + l.title = "Scale/Offset", l.desc = "Applies an scaling and offseting", l.prototype.onExecute = function() { + var a = this.getInputData(0); + if (this.isOutputConnected(0) && a) { + if (this.properties.precision === c.PASS_THROUGH) { + this.setOutputData(0, a); + } else { + var b = a.width, d = a.height, k = this.precision === c.LOW ? gl.UNSIGNED_BYTE : gl.HIGH_PRECISION_FORMAT; + this.precision === c.DEFAULT && (k = a.type); + this._tex && this._tex.width == b && this._tex.height == d && this._tex.type == k || (this._tex = new GL.Texture(b, d, {type:k, format:gl.RGBA, filter:gl.LINEAR})); + var f = this._shader; + f || (f = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, l.pixel_shader)); + var h = this.getInputData(1); + h ? (this.properties.scale[0] = h[0], this.properties.scale[1] = h[1]) : h = this.properties.scale; + var e = this.getInputData(2); + e ? (this.properties.offset[0] = e[0], this.properties.offset[1] = e[1]) : e = this.properties.offset; + this._tex.drawTo(function() { + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + gl.disable(gl.BLEND); + a.bind(0); + var b = Mesh.getScreenQuad(); + f.uniforms({u_texture:0, u_scale:h, u_offset:e}).draw(b); + }); + this.setOutputData(0, this._tex); + } + } + }, l.pixel_shader = "precision highp float;\n\r\n\t\t\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform vec2 u_scale;\n\r\n\t\tuniform vec2 u_offset;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec2 uv = v_coord;\n\r\n\t\t\tuv = uv / u_scale - u_offset;\n\r\n\t\t\tgl_FragColor = texture2D(u_texture, uv);\n\r\n\t\t}\n\r\n\t\t", F.registerNodeType("texture/scaleOffset", l), B.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, + B.title = "Warp", B.desc = "Texture warp operation", B.prototype.onExecute = function() { + var a = this.getInputData(0); + if (this.isOutputConnected(0)) { + if (this.properties.precision === c.PASS_THROUGH) { + this.setOutputData(0, a); + } else { + var b = this.getInputData(1), d = 512, k = 512; + a ? (d = a.width, k = a.height) : b && (d = b.width, k = b.height); + this._tex = a || this._tex ? c.getTargetTexture(a || this._tex, this._tex, this.properties.precision) : new GL.Texture(d, k, {type:this.precision === c.LOW ? gl.UNSIGNED_BYTE : gl.HIGH_PRECISION_FORMAT, format:gl.RGBA, filter:gl.LINEAR}); + var f = this._shader; + f || (f = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, B.pixel_shader)); + d = this.getInputData(2); + null != d ? this.properties.factor = d : d = parseFloat(this.properties.factor); + var h = this._uniforms; + h.u_factor = d; + h.u_scale.set(this.properties.scale); + h.u_offset.set(this.properties.offset); + this._tex.drawTo(function() { + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + gl.disable(gl.BLEND); + a && a.bind(0); + b && b.bind(1); + var d = Mesh.getScreenQuad(); + f.uniforms(h).draw(d); + }); + this.setOutputData(0, this._tex); + } + } + }, B.pixel_shader = "precision highp float;\n\r\n\t\t\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform float u_factor;\n\r\n\t\tuniform vec2 u_scale;\n\r\n\t\tuniform vec2 u_offset;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec2 uv = v_coord;\n\r\n\t\t\tuv += ( texture2D(u_textureB, uv).rg - vec2(0.5)) * u_factor * u_scale + u_offset;\n\r\n\t\t\tgl_FragColor = texture2D(u_texture, uv);\n\r\n\t\t}\n\r\n\t\t", F.registerNodeType("texture/warp", + B), y.title = "to Viewport", y.desc = "Texture to viewport", y._prev_viewport = new Float32Array(4), y.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a) { + this.properties.disable_alpha ? gl.disable(gl.BLEND) : (gl.enable(gl.BLEND), this.properties.additive ? gl.blendFunc(gl.SRC_ALPHA, gl.ONE) : gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)); + gl.disable(gl.DEPTH_TEST); + var b = this.properties.gamma || 1.0; + this.isInputConnected(1) && (b = this.getInputData(1)); + a.setParameter(gl.TEXTURE_MAG_FILTER, this.properties.filter ? gl.LINEAR : gl.NEAREST); + var d = y._prev_viewport; + d.set(gl.viewport_data); + var c = this.properties.viewport; + gl.viewport(d[0] + d[2] * c[0], d[1] + d[3] * c[1], d[2] * c[2], d[3] * c[3]); + gl.getViewport(); + this.properties.antialiasing ? (y._shader || (y._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, y.aa_pixel_shader)), c = Mesh.getScreenQuad(), a.bind(0), y._shader.uniforms({u_texture:0, uViewportSize:[a.width, a.height], u_igamma:1 / b, inverseVP:[1 / a.width, 1 / a.height]}).draw(c)) : 1.0 != b ? (y._gamma_shader || (y._gamma_shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, y.gamma_pixel_shader)), a.toViewport(y._gamma_shader, {u_texture:0, u_igamma:1 / b})) : a.toViewport(); + gl.viewport(d[0], d[1], d[2], d[3]); + } + }, y.prototype.onGetInputs = function() { + return [["gamma", "number"]]; + }, y.aa_pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec2 uViewportSize;\n\r\n\t\tuniform vec2 inverseVP;\n\r\n\t\tuniform float u_igamma;\n\r\n\t\t#define FXAA_REDUCE_MIN (1.0/ 128.0)\n\r\n\t\t#define FXAA_REDUCE_MUL (1.0 / 8.0)\n\r\n\t\t#define FXAA_SPAN_MAX 8.0\n\r\n\t\t\n\r\n\t\t/* from mitsuhiko/webgl-meincraft based on the code on geeks3d.com */\n\r\n\t\tvec4 applyFXAA(sampler2D tex, vec2 fragCoord)\n\r\n\t\t{\n\r\n\t\t\tvec4 color = vec4(0.0);\n\r\n\t\t\t/*vec2 inverseVP = vec2(1.0 / uViewportSize.x, 1.0 / uViewportSize.y);*/\n\r\n\t\t\tvec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * inverseVP).xyz;\n\r\n\t\t\tvec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * inverseVP).xyz;\n\r\n\t\t\tvec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * inverseVP).xyz;\n\r\n\t\t\tvec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * inverseVP).xyz;\n\r\n\t\t\tvec3 rgbM = texture2D(tex, fragCoord * inverseVP).xyz;\n\r\n\t\t\tvec3 luma = vec3(0.299, 0.587, 0.114);\n\r\n\t\t\tfloat lumaNW = dot(rgbNW, luma);\n\r\n\t\t\tfloat lumaNE = dot(rgbNE, luma);\n\r\n\t\t\tfloat lumaSW = dot(rgbSW, luma);\n\r\n\t\t\tfloat lumaSE = dot(rgbSE, luma);\n\r\n\t\t\tfloat lumaM = dot(rgbM, luma);\n\r\n\t\t\tfloat lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n\r\n\t\t\tfloat lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\r\n\t\t\t\n\r\n\t\t\tvec2 dir;\n\r\n\t\t\tdir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n\r\n\t\t\tdir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\r\n\t\t\t\n\r\n\t\t\tfloat dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\r\n\t\t\t\n\r\n\t\t\tfloat rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n\r\n\t\t\tdir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * inverseVP;\n\r\n\t\t\t\n\r\n\t\t\tvec3 rgbA = 0.5 * (texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + \n\r\n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz);\n\r\n\t\t\tvec3 rgbB = rgbA * 0.5 + 0.25 * (texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + \n\r\n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz);\n\r\n\t\t\t\n\r\n\t\t\t//return vec4(rgbA,1.0);\n\r\n\t\t\tfloat lumaB = dot(rgbB, luma);\n\r\n\t\t\tif ((lumaB < lumaMin) || (lumaB > lumaMax))\n\r\n\t\t\t\tcolor = vec4(rgbA, 1.0);\n\r\n\t\t\telse\n\r\n\t\t\t\tcolor = vec4(rgbB, 1.0);\n\r\n\t\t\tif(u_igamma != 1.0)\n\r\n\t\t\t\tcolor.xyz = pow( color.xyz, vec3(u_igamma) );\n\r\n\t\t\treturn color;\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\r\n\t\t}\n\r\n\t\t", + y.gamma_pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_igamma;\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\r\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\r\n\t\t gl_FragColor = color;\n\r\n\t\t}\n\r\n\t\t", F.registerNodeType("texture/toviewport", y), v.title = "Copy", v.desc = "Copy Texture", v.widgets_info = {size:{widget:"combo", + values:[0, 32, 64, 128, 256, 512, 1024, 2048]}, precision:{widget:"combo", values:c.MODE_VALUES}}, v.prototype.onExecute = function() { + var a = this.getInputData(0); + if ((a || this._temp_texture) && this.isOutputConnected(0)) { + if (a) { + var b = a.width, d = a.height; + 0 != this.properties.size && (d = b = this.properties.size); + var k = this._temp_texture, f = a.type; + this.properties.precision === c.LOW ? f = gl.UNSIGNED_BYTE : this.properties.precision === c.HIGH && (f = gl.HIGH_PRECISION_FORMAT); + k && k.width == b && k.height == d && k.type == f || (k = gl.LINEAR, this.properties.generate_mipmaps && isPowerOfTwo(b) && isPowerOfTwo(d) && (k = gl.LINEAR_MIPMAP_LINEAR), this._temp_texture = new GL.Texture(b, d, {type:f, format:gl.RGBA, minFilter:k, magFilter:gl.LINEAR})); + a.copyTo(this._temp_texture); + this.properties.generate_mipmaps && (this._temp_texture.bind(0), gl.generateMipmap(this._temp_texture.texture_type), this._temp_texture.unbind(0)); + } + this.setOutputData(0, this._temp_texture); + } + }, F.registerNodeType("texture/copy", v), E.title = "Downsample", E.desc = "Downsample Texture", E.widgets_info = {iterations:{type:"number", step:1, precision:0, min:0}, precision:{widget:"combo", values:c.MODE_VALUES}}, E.prototype.onExecute = function() { + var a = this.getInputData(0); + if ((a || this._temp_texture) && this.isOutputConnected(0) && a && a.texture_type === GL.TEXTURE_2D) { + if (1 > this.properties.iterations) { + this.setOutputData(0, a); + } else { + var b = E._shader; + b || (E._shader = b = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, E.pixel_shader)); + var d = a.width | 0, k = a.height | 0, f = a.type; + this.properties.precision === c.LOW ? f = gl.UNSIGNED_BYTE : this.properties.precision === c.HIGH && (f = gl.HIGH_PRECISION_FORMAT); + var h = this.properties.iterations || 1, e = a, n = []; + f = {type:f, format:a.format}; + var g = vec2.create(), q = {u_offset:g}; + this._texture && GL.Texture.releaseTemporary(this._texture); + for (var l = 0; l < h; ++l) { + g[0] = 1 / d; + g[1] = 1 / k; + d = d >> 1 || 0; + k = k >> 1 || 0; + a = GL.Texture.getTemporary(d, k, f); + n.push(a); + e.setParameter(GL.TEXTURE_MAG_FILTER, GL.NEAREST); + e.copyTo(a, b, q); + if (1 == d && 1 == k) { + break; + } + e = a; + } + this._texture = n.pop(); + for (l = 0; l < n.length; ++l) { + GL.Texture.releaseTemporary(n[l]); + } + this.properties.generate_mipmaps && (this._texture.bind(0), gl.generateMipmap(this._texture.texture_type), this._texture.unbind(0)); + this.setOutputData(0, this._texture); + } + } + }, E.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec2 u_offset;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = texture2D(u_texture, v_coord );\n\r\n\t\t\tcolor += texture2D(u_texture, v_coord + vec2( u_offset.x, 0.0 ) );\n\r\n\t\t\tcolor += texture2D(u_texture, v_coord + vec2( 0.0, u_offset.y ) );\n\r\n\t\t\tcolor += texture2D(u_texture, v_coord + vec2( u_offset.x, u_offset.y ) );\n\r\n\t\t gl_FragColor = color * 0.25;\n\r\n\t\t}\n\r\n\t\t", + F.registerNodeType("texture/downsample", E), z.title = "Average", z.desc = "Compute a partial average (32 random samples) of a texture and stores it as a 1x1 pixel texture.\n If high_quality is true, then it generates the mipmaps first and reads from the lower one.", z.prototype.onExecute = function() { + this.properties.use_previous_frame || this.updateAverage(); + var a = this._luminance; + this.setOutputData(0, this._temp_texture); + this.setOutputData(1, a); + this.setOutputData(2, (a[0] + a[1] + a[2]) / 3); + }, z.prototype.onPreRenderExecute = function() { + this.updateAverage(); + }, z.prototype.updateAverage = function() { + var a = this.getInputData(0); + if (a && (this.isOutputConnected(0) || this.isOutputConnected(1) || this.isOutputConnected(2))) { + if (!z._shader) { + z._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, z.pixel_shader); + for (var b = new Float32Array(16), d = 0; d < b.length; ++d) { + b[d] = Math.random(); + } + z._shader.uniforms({u_samples_a:b.subarray(0, 16), u_samples_b:b.subarray(16, 32)}); + } + d = this._temp_texture; + b = gl.UNSIGNED_BYTE; + a.type != b && (b = gl.FLOAT); + d && d.type == b || (this._temp_texture = new GL.Texture(1, 1, {type:b, format:gl.RGBA, filter:gl.NEAREST})); + this._uniforms.u_mipmap_offset = 0; + this.properties.high_quality && (this._temp_pot2_texture && this._temp_pot2_texture.type == b || (this._temp_pot2_texture = new GL.Texture(512, 512, {type:b, format:gl.RGBA, minFilter:gl.LINEAR_MIPMAP_LINEAR, magFilter:gl.LINEAR})), a.copyTo(this._temp_pot2_texture), a = this._temp_pot2_texture, a.bind(0), gl.generateMipmap(GL.TEXTURE_2D), this._uniforms.u_mipmap_offset = 9); + var c = z._shader, k = this._uniforms; + k.u_mipmap_offset = this.properties.mipmap_offset; + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.BLEND); + this._temp_texture.drawTo(function() { + a.toViewport(c, k); + }); + if (this.isOutputConnected(1) || this.isOutputConnected(2)) { + if (d = this._temp_texture.getPixels()) { + var f = this._luminance; + b = this._temp_texture.type; + f.set(d); + b == gl.UNSIGNED_BYTE && vec4.scale(f, f, 1 / 255); + } + } + } + }, z.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tuniform mat4 u_samples_a;\n\r\n\t\tuniform mat4 u_samples_b;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_mipmap_offset;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = vec4(0.0);\n\r\n\t\t\t//random average\n\r\n\t\t\tfor(int i = 0; i < 4; ++i)\n\r\n\t\t\t\tfor(int j = 0; j < 4; ++j)\n\r\n\t\t\t\t{\n\r\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\r\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\r\n\t\t\t\t}\n\r\n\t\t gl_FragColor = color * 0.03125;\n\r\n\t\t}\n\r\n\t\t", + F.registerNodeType("texture/average", z), e.title = "Smooth", e.desc = "Smooth texture over time", e.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a && this.isOutputConnected(0)) { + e._shader || (e._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, e.pixel_shader)); + var b = this._temp_texture; + b && b.type == a.type && b.width == a.width && b.height == a.height || (b = {type:a.type, format:gl.RGBA, filter:gl.NEAREST}, this._temp_texture = new GL.Texture(a.width, a.height, b), this._temp_texture2 = new GL.Texture(a.width, a.height, b), a.copyTo(this._temp_texture2)); + b = this._temp_texture; + var d = this._temp_texture2, c = e._shader, k = this._uniforms; + k.u_factor = 1.0 - this.getInputOrProperty("factor"); + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + b.drawTo(function() { + d.bind(1); + a.toViewport(c, k); + }); + this.setOutputData(0, b); + this._temp_texture = d; + this._temp_texture2 = b; + } + }, e.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tuniform float u_factor;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tgl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\r\n\t\t}\n\r\n\t\t", F.registerNodeType("texture/temporal_smooth", e), C.title = "Lineal Avg Smooth", C.desc = "Smooth texture linearly over time", + C["@samples"] = {type:"number", min:1, max:64, step:1, precision:1}, C.prototype.getPreviewTexture = function() { + return this._temp_texture2; + }, C.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a && this.isOutputConnected(0)) { + C._shader || (C._shader_copy = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, C.pixel_shader_copy), C._shader_avg = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, C.pixel_shader_avg)); + var b = Math.clamp(this.properties.samples, 0, 64), d = this.frame, c = this.properties.frames_interval; + if (0 == c || 0 == d % c) { + d = this._temp_texture; + d && d.type == a.type && d.width == b || (d = {type:a.type, format:gl.RGBA, filter:gl.NEAREST}, this._temp_texture = new GL.Texture(b, 1, d), this._temp_texture2 = new GL.Texture(b, 1, d), this._temp_texture_out = new GL.Texture(1, 1, d)); + var k = this._temp_texture, f = this._temp_texture2, h = C._shader_copy, e = C._shader_avg, n = this._uniforms; + n.u_samples = b; + n.u_isamples = 1.0 / b; + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + k.drawTo(function() { + f.bind(1); + a.toViewport(h, n); + }); + this._temp_texture_out.drawTo(function() { + k.toViewport(e, n); + }); + this.setOutputData(0, this._temp_texture_out); + this._temp_texture = f; + this._temp_texture2 = k; + } else { + this.setOutputData(0, this._temp_texture_out); + } + this.setOutputData(1, this._temp_texture2); + this.frame++; + } + }, C.pixel_shader_copy = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tuniform float u_isamples;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tif( v_coord.x <= u_isamples )\n\r\n\t\t\t\tgl_FragColor = texture2D( u_texture, vec2(0.5) );\n\r\n\t\t\telse\n\r\n\t\t\t\tgl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\r\n\t\t}\n\r\n\t\t", C.pixel_shader_avg = + "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform int u_samples;\n\r\n\t\tuniform float u_isamples;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = vec4(0.0);\n\r\n\t\t\tfor(int i = 0; i < 64; ++i)\n\r\n\t\t\t{\n\r\n\t\t\t\tcolor += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\r\n\t\t\t\tif(i == (u_samples - 1))\n\r\n\t\t\t\t\tbreak;\n\r\n\t\t\t}\n\r\n\t\t\tgl_FragColor = color * u_isamples;\n\r\n\t\t}\n\r\n\t\t", + F.registerNodeType("texture/linear_avg_smooth", C), D.title = "Image to Texture", D.desc = "Uploads an image to the GPU", D.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a) { + var b = a.videoWidth || a.width, d = a.videoHeight || a.height; + if (a.gltexture) { + this.setOutputData(0, a.gltexture); + } else { + var c = this._temp_texture; + c && c.width == b && c.height == d || (this._temp_texture = new GL.Texture(b, d, {format:gl.RGBA, filter:gl.LINEAR})); + try { + this._temp_texture.uploadImage(a); + } catch (S) { + console.error("image comes from an unsafe location, cannot be uploaded to webgl: " + S); + return; + } + this.setOutputData(0, this._temp_texture); + } + } + }, F.registerNodeType("texture/imageToTexture", D), t.widgets_info = {texture:{widget:"texture"}, precision:{widget:"combo", values:c.MODE_VALUES}}, t.title = "LUT", t.desc = "Apply LUT to Texture", t.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var a = this.getInputData(0); + if (this.properties.precision === c.PASS_THROUGH || !1 === this.properties.enabled) { + this.setOutputData(0, a); + } else { + if (a) { + var b = this.getInputData(1); + b || (b = c.getTexture(this.properties.texture)); + if (b) { + b.bind(0); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.bindTexture(gl.TEXTURE_2D, null); + var d = this.properties.intensity; + this.isInputConnected(2) && (this.properties.intensity = d = this.getInputData(2)); + this._tex = c.getTargetTexture(a, this._tex, this.properties.precision); + this._tex.drawTo(function() { + b.bind(1); + a.toViewport(t._shader, {u_texture:0, u_textureB:1, u_amount:d}); + }); + this.setOutputData(0, this._tex); + } else { + this.setOutputData(0, a); + } + } + } + } + }, t.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tuniform float u_amount;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\r\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\r\n\t\t\t mediump vec2 quad1;\n\r\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\r\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\r\n\t\t\t mediump vec2 quad2;\n\r\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\r\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\r\n\t\t\t highp vec2 texPos1;\n\r\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\r\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\r\n\t\t\t highp vec2 texPos2;\n\r\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\r\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\r\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\r\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\r\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\r\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\r\n\t\t}\n\r\n\t\t", + F.registerNodeType("texture/LUT", t), G.title = "Texture to Channels", G.desc = "Split texture channels", G.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a) { + this._channels || (this._channels = Array(4)); + for (var b = gl.RGB, d = 0, c = 0; 4 > c; c++) { + this.isOutputConnected(c) ? (this._channels[c] && this._channels[c].width == a.width && this._channels[c].height == a.height && this._channels[c].type == a.type && this._channels[c].format == b || (this._channels[c] = new GL.Texture(a.width, a.height, {type:a.type, format:b, filter:gl.LINEAR})), d++) : this._channels[c] = null; + } + if (d) { + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var k = Mesh.getScreenQuad(), f = G._shader, h = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]; + for (c = 0; 4 > c; c++) { + this._channels[c] && (this._channels[c].drawTo(function() { + a.bind(0); + f.uniforms({u_texture:0, u_mask:h[c]}).draw(k); + }), this.setOutputData(c, this._channels[c])); + } + } + } + }, G.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec4 u_mask;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\r\n\t\t}\n\r\n\t\t", F.registerNodeType("texture/textureChannels", G), n.title = "Channels to Texture", n.desc = "Split texture channels", n.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, + n.prototype.onExecute = function() { + var a = c.getWhiteTexture(), b = this.getInputData(0) || a, d = this.getInputData(1) || a, k = this.getInputData(2) || a, f = this.getInputData(3) || a; + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var h = Mesh.getScreenQuad(); + n._shader || (n._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, n.pixel_shader)); + var e = n._shader; + a = Math.max(b.width, d.width, k.width, f.width); + var g = Math.max(b.height, d.height, k.height, f.height), q = this.properties.precision == c.HIGH ? c.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE; + this._texture && this._texture.width == a && this._texture.height == g && this._texture.type == q || (this._texture = new GL.Texture(a, g, {type:q, format:gl.RGBA, filter:gl.LINEAR})); + a = this._color; + a[0] = this.properties.R; + a[1] = this.properties.G; + a[2] = this.properties.B; + a[3] = this.properties.A; + var l = this._uniforms; + this._texture.drawTo(function() { + b.bind(0); + d.bind(1); + k.bind(2); + f.bind(3); + e.uniforms(l).draw(h); + }); + this.setOutputData(0, this._texture); + }, n.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_textureR;\n\r\n\t\tuniform sampler2D u_textureG;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tuniform sampler2D u_textureA;\n\r\n\t\tuniform vec4 u_color;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t gl_FragColor = u_color * vec4( \r\n\t\t\t\t\ttexture2D(u_textureR, v_coord).r,\r\n\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\r\n\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\r\n\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\r\n\t\t}\n\r\n\t\t", + F.registerNodeType("texture/channelsTexture", n), q.title = "Color", q.desc = "Generates a 1x1 texture with a constant color", q.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, q.prototype.onDrawBackground = function(a) { + var b = this.properties.color; + a.fillStyle = "rgb(" + Math.floor(255 * Math.clamp(b[0], 0, 1)) + "," + Math.floor(255 * Math.clamp(b[1], 0, 1)) + "," + Math.floor(255 * Math.clamp(b[2], 0, 1)) + ")"; + this.flags.collapsed ? this.boxcolor = a.fillStyle : a.fillRect(0, 0, this.size[0], this.size[1]); + }, q.prototype.onExecute = function() { + var a = this.properties.precision == c.HIGH ? c.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE; + this._tex && this._tex.type == a || (this._tex = new GL.Texture(1, 1, {format:gl.RGBA, type:a, minFilter:gl.NEAREST})); + a = this.properties.color; + if (this.inputs) { + for (var b = 0; b < this.inputs.length; b++) { + var d = this.inputs[b], k = this.getInputData(b); + if (void 0 !== k) { + switch(d.name) { + case "RGB": + case "RGBA": + a.set(k); + break; + case "R": + a[0] = k; + break; + case "G": + a[1] = k; + break; + case "B": + a[2] = k; + break; + case "A": + a[3] = k; + } + } + } + } + 0.001 < vec4.sqrDist(this._tex_color, a) && (this._tex_color.set(a), this._tex.fill(a)); + this.setOutputData(0, this._tex); + }, q.prototype.onGetInputs = function() { + return [["RGB", "vec3"], ["RGBA", "vec4"], ["R", "number"], ["G", "number"], ["B", "number"], ["A", "number"]]; + }, F.registerNodeType("texture/color", q), k.title = "Gradient", k.desc = "Generates a gradient", k["@A"] = {type:"color"}, k["@B"] = {type:"color"}, k["@texture_size"] = {type:"enum", values:[32, 64, 128, 256, 512]}, k.prototype.onExecute = function() { + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var a = GL.Mesh.getScreenQuad(), b = k._shader, d = this.getInputData(0); + d || (d = this.properties.A); + var c = this.getInputData(1); + c || (c = this.properties.B); + for (var f = 2; f < this.inputs.length; f++) { + var h = this.inputs[f], e = this.getInputData(f); + void 0 !== e && (this.properties[h.name] = e); + } + var n = this._uniforms; + this._uniforms.u_angle = this.properties.angle * DEG2RAD; + this._uniforms.u_scale = this.properties.scale; + vec3.copy(n.u_colorA, d); + vec3.copy(n.u_colorB, c); + d = parseInt(this.properties.texture_size); + this._tex && this._tex.width == d || (this._tex = new GL.Texture(d, d, {format:gl.RGB, filter:gl.LINEAR})); + this._tex.drawTo(function() { + b.uniforms(n).draw(a); + }); + this.setOutputData(0, this._tex); + }, k.prototype.onGetInputs = function() { + return [["angle", "number"], ["scale", "number"]]; + }, k.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform float u_angle;\n\r\n\t\tuniform float u_scale;\n\r\n\t\tuniform vec3 u_colorA;\n\r\n\t\tuniform vec3 u_colorB;\n\r\n\t\t\n\r\n\t\tvec2 rotate(vec2 v, float angle)\n\r\n\t\t{\n\r\n\t\t\tvec2 result;\n\r\n\t\t\tfloat _cos = cos(angle);\n\r\n\t\t\tfloat _sin = sin(angle);\n\r\n\t\t\tresult.x = v.x * _cos - v.y * _sin;\n\r\n\t\t\tresult.y = v.x * _sin + v.y * _cos;\n\r\n\t\t\treturn result;\n\r\n\t\t}\n\r\n\t\tvoid main() {\n\r\n\t\t\tfloat f = (rotate(u_scale * (v_coord - vec2(0.5)), u_angle) + vec2(0.5)).x;\n\r\n\t\t\tvec3 color = mix(u_colorA,u_colorB,clamp(f,0.0,1.0));\n\r\n\t\t gl_FragColor = vec4(color,1.0);\n\r\n\t\t}\n\r\n\t\t", + F.registerNodeType("texture/gradient", k), a.title = "Mix", a.desc = "Generates a texture mixing two textures", a.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, a.prototype.onExecute = function() { + var b = this.getInputData(0); + if (this.isOutputConnected(0)) { + if (this.properties.precision === c.PASS_THROUGH) { + this.setOutputData(0, b); + } else { + var d = this.getInputData(1); + if (b && d) { + var k = this.getInputData(2), f = this.getInputData(3); + this._tex = c.getTargetTexture(this.properties.size_from_biggest && d.width > b.width ? d : b, this._tex, this.properties.precision); + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var h = Mesh.getScreenQuad(), e = null, n = this._uniforms; + k ? (e = a._shader_tex, e || (e = a._shader_tex = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, a.pixel_shader, {MIX_TEX:""}))) : (e = a._shader_factor, e || (e = a._shader_factor = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, a.pixel_shader)), f = null == f ? this.properties.factor : f, n.u_mix.set([f, f, f, f])); + var g = this.properties.invert; + this._tex.drawTo(function() { + b.bind(g ? 1 : 0); + d.bind(g ? 0 : 1); + k && k.bind(2); + e.uniforms(n).draw(h); + }); + this.setOutputData(0, this._tex); + } + } + } + }, a.prototype.onGetInputs = function() { + return [["factor", "number"]]; + }, a.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_textureA;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\t#ifdef MIX_TEX\n\r\n\t\t\tuniform sampler2D u_textureMix;\n\r\n\t\t#else\n\r\n\t\t\tuniform vec4 u_mix;\n\r\n\t\t#endif\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\t#ifdef MIX_TEX\n\r\n\t\t\t vec4 f = texture2D(u_textureMix, v_coord);\n\r\n\t\t\t#else\n\r\n\t\t\t vec4 f = u_mix;\n\r\n\t\t\t#endif\n\r\n\t\t gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\r\n\t\t}\n\r\n\t\t", + F.registerNodeType("texture/mix", a), b.title = "Edges", b.desc = "Detects edges", b.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, b.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var a = this.getInputData(0); + if (this.properties.precision === c.PASS_THROUGH) { + this.setOutputData(0, a); + } else { + if (a) { + this._tex = c.getTargetTexture(a, this._tex, this.properties.precision); + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var d = Mesh.getScreenQuad(), k = b._shader, f = this.properties.invert, h = this.properties.factor, e = this.properties.threshold ? 1 : 0; + this._tex.drawTo(function() { + a.bind(0); + k.uniforms({u_texture:0, u_isize:[1 / a.width, 1 / a.height], u_factor:h, u_threshold:e, u_invert:f ? 1 : 0}).draw(d); + }); + this.setOutputData(0, this._tex); + } + } + } + }, b.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec2 u_isize;\n\r\n\t\tuniform int u_invert;\n\r\n\t\tuniform float u_factor;\n\r\n\t\tuniform float u_threshold;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 center = texture2D(u_texture, v_coord);\n\r\n\t\t\tvec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\r\n\t\t\tvec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\r\n\t\t\tvec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\r\n\t\t\tvec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\r\n\t\t\tvec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\r\n\t\t\tdiff *= u_factor;\n\r\n\t\t\tif(u_invert == 1)\n\r\n\t\t\t\tdiff.xyz = vec3(1.0) - diff.xyz;\n\r\n\t\t\tif( u_threshold == 0.0 )\n\r\n\t\t\t\tgl_FragColor = vec4( diff.xyz, center.a );\n\r\n\t\t\telse\n\r\n\t\t\t\tgl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\r\n\t\t}\n\r\n\t\t", + F.registerNodeType("texture/edges", b), d.title = "Depth Range", d.desc = "Generates a texture with a depth range", d.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var a = this.getInputData(0); + if (a) { + var b = gl.UNSIGNED_BYTE; + this.properties.high_precision && (b = gl.half_float_ext ? gl.HALF_FLOAT_OES : gl.FLOAT); + this._temp_texture && this._temp_texture.type == b && this._temp_texture.width == a.width && this._temp_texture.height == a.height || (this._temp_texture = new GL.Texture(a.width, a.height, {type:b, format:gl.RGBA, filter:gl.LINEAR})); + var c = this._uniforms; + b = this.properties.distance; + this.isInputConnected(1) && (b = this.getInputData(1), this.properties.distance = b); + var k = this.properties.range; + this.isInputConnected(2) && (k = this.getInputData(2), this.properties.range = k); + c.u_distance = b; + c.u_range = k; + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var f = Mesh.getScreenQuad(); + d._shader || (d._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, d.pixel_shader), d._shader_onlydepth = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, d.pixel_shader, {ONLY_DEPTH:""})); + var h = this.properties.only_depth ? d._shader_onlydepth : d._shader; + b = null; + b = a.near_far_planes ? a.near_far_planes : window.LS && LS.Renderer._main_camera ? LS.Renderer._main_camera._uniforms.u_camera_planes : [0.1, 1000]; + c.u_camera_planes = b; + this._temp_texture.drawTo(function() { + a.bind(0); + h.uniforms(c).draw(f); + }); + this._temp_texture.near_far_planes = b; + this.setOutputData(0, this._temp_texture); + } + } + }, d.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec2 u_camera_planes;\n\r\n\t\tuniform float u_distance;\n\r\n\t\tuniform float u_range;\n\r\n\t\t\n\r\n\t\tfloat LinearDepth()\n\r\n\t\t{\n\r\n\t\t\tfloat zNear = u_camera_planes.x;\n\r\n\t\t\tfloat zFar = u_camera_planes.y;\n\r\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\r\n\t\t\tdepth = depth * 2.0 - 1.0;\n\r\n\t\t\treturn zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tfloat depth = LinearDepth();\n\r\n\t\t\t#ifdef ONLY_DEPTH\n\r\n\t\t\t gl_FragColor = vec4(depth);\n\r\n\t\t\t#else\n\r\n\t\t\t\tfloat diff = abs(depth * u_camera_planes.y - u_distance);\n\r\n\t\t\t\tfloat dof = 1.0;\n\r\n\t\t\t\tif(diff <= u_range)\n\r\n\t\t\t\t\tdof = diff / u_range;\n\r\n\t\t\t gl_FragColor = vec4(dof);\n\r\n\t\t\t#endif\n\r\n\t\t}\n\r\n\t\t", + F.registerNodeType("texture/depth_range", d), h.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, h.title = "Linear Depth", h.desc = "Creates a color texture with linear depth", h.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var a = this.getInputData(0); + if (a && (a.format == gl.DEPTH_COMPONENT || a.format == gl.DEPTH_STENCIL)) { + var b = this.properties.precision == c.HIGH ? gl.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE; + this._temp_texture && this._temp_texture.type == b && this._temp_texture.width == a.width && this._temp_texture.height == a.height || (this._temp_texture = new GL.Texture(a.width, a.height, {type:b, format:gl.RGB, filter:gl.LINEAR})); + var d = this._uniforms; + d.u_near = a.near_far_planes[0]; + d.u_far = a.near_far_planes[1]; + d.u_invert = this.properties.invert ? 1 : 0; + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var k = Mesh.getScreenQuad(); + h._shader || (h._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, h.pixel_shader)); + var f = h._shader; + b = null; + b = a.near_far_planes ? a.near_far_planes : window.LS && LS.Renderer._main_camera ? LS.Renderer._main_camera._uniforms.u_camera_planes : [0.1, 1000]; + d.u_camera_planes = b; + this._temp_texture.drawTo(function() { + a.bind(0); + f.uniforms(d).draw(k); + }); + this._temp_texture.near_far_planes = b; + this.setOutputData(0, this._temp_texture); + } + } + }, h.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_near;\n\r\n\t\tuniform float u_far;\n\r\n\t\tuniform int u_invert;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tfloat zNear = u_near;\n\r\n\t\t\tfloat zFar = u_far;\n\r\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\r\n\t\t\tdepth = depth * 2.0 - 1.0;\n\r\n\t\t\tfloat f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\r\n\t\t\tif( u_invert == 1 )\n\r\n\t\t\t\tf = 1.0 - f;\n\r\n\t\t\tgl_FragColor = vec4(vec3(f),1.0);\n\r\n\t\t}\n\r\n\t\t", + F.registerNodeType("texture/linear_depth", h), f.title = "Blur", f.desc = "Blur a texture", f.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, f.max_iterations = 20, f.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a && this.isOutputConnected(0)) { + var b = this._final_texture; + b && b.width == a.width && b.height == a.height && b.type == a.type || (b = this._final_texture = new GL.Texture(a.width, a.height, {type:a.type, format:gl.RGBA, filter:gl.LINEAR})); + var d = this.properties.iterations; + this.isInputConnected(1) && (d = this.getInputData(1), this.properties.iterations = d); + d = Math.min(Math.floor(d), f.max_iterations); + if (0 == d) { + this.setOutputData(0, a); + } else { + var c = this.properties.intensity; + this.isInputConnected(2) && (c = this.getInputData(2), this.properties.intensity = c); + var k = F.camera_aspect; + k || void 0 === window.gl || (k = gl.canvas.height / gl.canvas.width); + k || (k = 1); + k = this.properties.preserve_aspect ? k : 1; + var h = this.properties.scale || [1, 1]; + a.applyBlur(k * h[0], h[1], c, b); + for (a = 1; a < d; ++a) { + b.applyBlur(k * h[0] * (a + 1), h[1] * (a + 1), c); + } + this.setOutputData(0, b); + } + } + }, F.registerNodeType("texture/blur", f), x.title = "Glow", x.desc = "Filters a texture giving it a glow effect", x.weights = new Float32Array([0.5, 0.4, 0.3, 0.2]), x.widgets_info = {iterations:{type:"number", min:0, max:16, step:1, precision:0}, threshold:{type:"number", min:0, max:10, step:0.01, precision:2}, precision:{widget:"combo", values:c.MODE_VALUES}}, x.prototype.onGetInputs = function() { + return [["enabled", "boolean"], ["threshold", "number"], ["intensity", "number"], ["persistence", "number"], ["iterations", "number"], ["dirt_factor", "number"]]; + }, x.prototype.onGetOutputs = function() { + return [["average", "Texture"]]; + }, x.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a && this.isAnyOutputConnected()) { + if (this.properties.precision === c.PASS_THROUGH || !1 === this.getInputOrProperty("enabled")) { + this.setOutputData(0, a); + } else { + var b = a.width, d = a.height, k = {format:a.format, type:a.type, minFilter:GL.LINEAR, magFilter:GL.LINEAR, wrap:gl.CLAMP_TO_EDGE}, f = c.getTextureType(this.properties.precision, a), h = this._uniforms, e = this._textures, n = x._cut_shader; + n || (n = x._cut_shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, x.cut_pixel_shader)); + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.BLEND); + h.u_threshold = this.getInputOrProperty("threshold"); + var g = e[0] = GL.Texture.getTemporary(b, d, k); + a.blit(g, n.uniforms(h)); + var q = g, l = this.getInputOrProperty("iterations"); + l = Math.clamp(l, 1, 16) | 0; + var m = h.u_texel_size, p = this.getInputOrProperty("intensity"); + h.u_intensity = 1; + h.u_delta = this.properties.scale; + n = x._shader; + n || (n = x._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, x.scale_pixel_shader)); + for (var r = 1; r < l; r++) { + b >>= 1; + 1 < (d | 0) && (d >>= 1); + if (2 > b) { + break; + } + g = e[r] = GL.Texture.getTemporary(b, d, k); + m[0] = 1 / q.width; + m[1] = 1 / q.height; + q.blit(g, n.uniforms(h)); + q = g; + } + this.isOutputConnected(2) && (b = this._average_texture, b && b.type == a.type && b.format == a.format || (b = this._average_texture = new GL.Texture(1, 1, {type:a.type, format:a.format, filter:gl.LINEAR})), m[0] = 1 / q.width, m[1] = 1 / q.height, h.u_intensity = p, h.u_delta = 1, q.blit(b, n.uniforms(h)), this.setOutputData(2, b)); + gl.enable(gl.BLEND); + gl.blendFunc(gl.ONE, gl.ONE); + h.u_intensity = this.getInputOrProperty("persistence"); + h.u_delta = 0.5; + for (r -= 2; 0 <= r; r--) { + g = e[r], e[r] = null, m[0] = 1 / q.width, m[1] = 1 / q.height, q.blit(g, n.uniforms(h)), GL.Texture.releaseTemporary(q), q = g; + } + gl.disable(gl.BLEND); + this.isOutputConnected(1) && (e = this._glow_texture, e && e.width == a.width && e.height == a.height && e.type == f && e.format == a.format || (e = this._glow_texture = new GL.Texture(a.width, a.height, {type:f, format:a.format, filter:gl.LINEAR})), q.blit(e), this.setOutputData(1, e)); + if (this.isOutputConnected(0)) { + e = this._final_texture; + e && e.width == a.width && e.height == a.height && e.type == f && e.format == a.format || (e = this._final_texture = new GL.Texture(a.width, a.height, {type:f, format:a.format, filter:gl.LINEAR})); + var u = this.getInputData(1), t = this.getInputOrProperty("dirt_factor"); + h.u_intensity = p; + n = u ? x._dirt_final_shader : x._final_shader; + n || (n = u ? x._dirt_final_shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, x.final_pixel_shader, {USE_DIRT:""}) : x._final_shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, x.final_pixel_shader)); + e.drawTo(function() { + a.bind(0); + q.bind(1); + u && (n.setUniform("u_dirt_factor", t), n.setUniform("u_dirt_texture", u.bind(2))); + n.toViewport(h); + }); + this.setOutputData(0, e); + } + GL.Texture.releaseTemporary(q); + } + } + }, x.cut_pixel_shader = "precision highp float;\n\r\n\tvarying vec2 v_coord;\n\r\n\tuniform sampler2D u_texture;\n\r\n\tuniform float u_threshold;\n\r\n\tvoid main() {\n\r\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\r\n\t}", x.scale_pixel_shader = "precision highp float;\n\r\n\tvarying vec2 v_coord;\n\r\n\tuniform sampler2D u_texture;\n\r\n\tuniform vec2 u_texel_size;\n\r\n\tuniform float u_delta;\n\r\n\tuniform float u_intensity;\n\r\n\t\n\r\n\tvec4 sampleBox(vec2 uv) {\n\r\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\r\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\r\n\t\treturn s * 0.25;\n\r\n\t}\n\r\n\tvoid main() {\n\r\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\r\n\t}", + x.final_pixel_shader = "precision highp float;\n\r\n\tvarying vec2 v_coord;\n\r\n\tuniform sampler2D u_texture;\n\r\n\tuniform sampler2D u_glow_texture;\n\r\n\t#ifdef USE_DIRT\n\r\n\t\tuniform sampler2D u_dirt_texture;\n\r\n\t#endif\n\r\n\tuniform vec2 u_texel_size;\n\r\n\tuniform float u_delta;\n\r\n\tuniform float u_intensity;\n\r\n\tuniform float u_dirt_factor;\n\r\n\t\n\r\n\tvec4 sampleBox(vec2 uv) {\n\r\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\r\n\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\r\n\t\treturn s * 0.25;\n\r\n\t}\n\r\n\tvoid main() {\n\r\n\t\tvec4 glow = sampleBox( v_coord );\n\r\n\t\t#ifdef USE_DIRT\n\r\n\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\r\n\t\t#endif\n\r\n\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\r\n\t}", + F.registerNodeType("texture/glow", x), H.title = "Kuwahara Filter", H.desc = "Filters a texture giving an artistic oil canvas painting", H.max_radius = 10, H._shaders = [], H.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a && this.isOutputConnected(0)) { + var b = this._temp_texture; + b && b.width == a.width && b.height == a.height && b.type == a.type || (this._temp_texture = new GL.Texture(a.width, a.height, {type:a.type, format:gl.RGBA, filter:gl.LINEAR})); + b = this.properties.radius; + b = Math.min(Math.floor(b), H.max_radius); + if (0 == b) { + this.setOutputData(0, a); + } else { + var d = this.properties.intensity, c = F.camera_aspect; + c || void 0 === window.gl || (c = gl.canvas.height / gl.canvas.width); + c || (c = 1); + c = this.properties.preserve_aspect ? c : 1; + H._shaders[b] || (H._shaders[b] = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, H.pixel_shader, {RADIUS:b.toFixed(0)})); + var k = H._shaders[b], f = GL.Mesh.getScreenQuad(); + a.bind(0); + this._temp_texture.drawTo(function() { + k.uniforms({u_texture:0, u_intensity:d, u_resolution:[a.width, a.height], u_iResolution:[1 / a.width, 1 / a.height]}).draw(f); + }); + this.setOutputData(0, this._temp_texture); + } + } + }, H.pixel_shader = "\n\r\nprecision highp float;\n\r\nvarying vec2 v_coord;\n\r\nuniform sampler2D u_texture;\n\r\nuniform float u_intensity;\n\r\nuniform vec2 u_resolution;\n\r\nuniform vec2 u_iResolution;\n\r\n#ifndef RADIUS\n\r\n\t#define RADIUS 7\n\r\n#endif\n\r\nvoid main() {\n\r\n\n\r\n\tconst int radius = RADIUS;\n\r\n\tvec2 fragCoord = v_coord;\n\r\n\tvec2 src_size = u_iResolution;\n\r\n\tvec2 uv = v_coord;\n\r\n\tfloat n = float((radius + 1) * (radius + 1));\n\r\n\tint i;\n\r\n\tint j;\n\r\n\tvec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\r\n\tvec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\r\n\tvec3 c;\n\r\n\t\n\r\n\tfor (int j = -radius; j <= 0; ++j) {\n\r\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\r\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\r\n\t\t\tm0 += c;\n\r\n\t\t\ts0 += c * c;\n\r\n\t\t}\n\r\n\t}\n\r\n\t\n\r\n\tfor (int j = -radius; j <= 0; ++j) {\n\r\n\t\tfor (int i = 0; i <= radius; ++i) {\n\r\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\r\n\t\t\tm1 += c;\n\r\n\t\t\ts1 += c * c;\n\r\n\t\t}\n\r\n\t}\n\r\n\t\n\r\n\tfor (int j = 0; j <= radius; ++j) {\n\r\n\t\tfor (int i = 0; i <= radius; ++i) {\n\r\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\r\n\t\t\tm2 += c;\n\r\n\t\t\ts2 += c * c;\n\r\n\t\t}\n\r\n\t}\n\r\n\t\n\r\n\tfor (int j = 0; j <= radius; ++j) {\n\r\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\r\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\r\n\t\t\tm3 += c;\n\r\n\t\t\ts3 += c * c;\n\r\n\t\t}\n\r\n\t}\n\r\n\t\n\r\n\tfloat min_sigma2 = 1e+2;\n\r\n\tm0 /= n;\n\r\n\ts0 = abs(s0 / n - m0 * m0);\n\r\n\t\n\r\n\tfloat sigma2 = s0.r + s0.g + s0.b;\n\r\n\tif (sigma2 < min_sigma2) {\n\r\n\t\tmin_sigma2 = sigma2;\n\r\n\t\tgl_FragColor = vec4(m0, 1.0);\n\r\n\t}\n\r\n\t\n\r\n\tm1 /= n;\n\r\n\ts1 = abs(s1 / n - m1 * m1);\n\r\n\t\n\r\n\tsigma2 = s1.r + s1.g + s1.b;\n\r\n\tif (sigma2 < min_sigma2) {\n\r\n\t\tmin_sigma2 = sigma2;\n\r\n\t\tgl_FragColor = vec4(m1, 1.0);\n\r\n\t}\n\r\n\t\n\r\n\tm2 /= n;\n\r\n\ts2 = abs(s2 / n - m2 * m2);\n\r\n\t\n\r\n\tsigma2 = s2.r + s2.g + s2.b;\n\r\n\tif (sigma2 < min_sigma2) {\n\r\n\t\tmin_sigma2 = sigma2;\n\r\n\t\tgl_FragColor = vec4(m2, 1.0);\n\r\n\t}\n\r\n\t\n\r\n\tm3 /= n;\n\r\n\ts3 = abs(s3 / n - m3 * m3);\n\r\n\t\n\r\n\tsigma2 = s3.r + s3.g + s3.b;\n\r\n\tif (sigma2 < min_sigma2) {\n\r\n\t\tmin_sigma2 = sigma2;\n\r\n\t\tgl_FragColor = vec4(m3, 1.0);\n\r\n\t}\n\r\n}\n\r\n", + F.registerNodeType("texture/kuwahara", H), I.title = "XDoG Filter", I.desc = "Filters a texture giving an artistic ink style", I.max_radius = 10, I._shaders = [], I.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a && this.isOutputConnected(0)) { + var b = this._temp_texture; + b && b.width == a.width && b.height == a.height && b.type == a.type || (this._temp_texture = new GL.Texture(a.width, a.height, {type:a.type, format:gl.RGBA, filter:gl.LINEAR})); + I._xdog_shader || (I._xdog_shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, I.xdog_pixel_shader)); + var d = I._xdog_shader, c = GL.Mesh.getScreenQuad(), k = this.properties.sigma, f = this.properties.k, h = this.properties.p, e = this.properties.epsilon, n = this.properties.phi; + a.bind(0); + this._temp_texture.drawTo(function() { + d.uniforms({src:0, sigma:k, k:f, p:h, epsilon:e, phi:n, cvsWidth:a.width, cvsHeight:a.height}).draw(c); + }); + this.setOutputData(0, this._temp_texture); + } + }, I.xdog_pixel_shader = "\n\r\nprecision highp float;\n\r\nuniform sampler2D src;\n\n\r\nuniform float cvsHeight;\n\r\nuniform float cvsWidth;\n\n\r\nuniform float sigma;\n\r\nuniform float k;\n\r\nuniform float p;\n\r\nuniform float epsilon;\n\r\nuniform float phi;\n\r\nvarying vec2 v_coord;\n\n\r\nfloat cosh(float val)\n\r\n{\n\r\n\tfloat tmp = exp(val);\n\r\n\tfloat cosH = (tmp + 1.0 / tmp) / 2.0;\n\r\n\treturn cosH;\n\r\n}\n\n\r\nfloat tanh(float val)\n\r\n{\n\r\n\tfloat tmp = exp(val);\n\r\n\tfloat tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\r\n\treturn tanH;\n\r\n}\n\n\r\nfloat sinh(float val)\n\r\n{\n\r\n\tfloat tmp = exp(val);\n\r\n\tfloat sinH = (tmp - 1.0 / tmp) / 2.0;\n\r\n\treturn sinH;\n\r\n}\n\n\r\nvoid main(void){\n\r\n\tvec3 destColor = vec3(0.0);\n\r\n\tfloat tFrag = 1.0 / cvsHeight;\n\r\n\tfloat sFrag = 1.0 / cvsWidth;\n\r\n\tvec2 Frag = vec2(sFrag,tFrag);\n\r\n\tvec2 uv = gl_FragCoord.st;\n\r\n\tfloat twoSigmaESquared = 2.0 * sigma * sigma;\n\r\n\tfloat twoSigmaRSquared = twoSigmaESquared * k * k;\n\r\n\tint halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\r\n\tconst int MAX_NUM_ITERATION = 99999;\n\r\n\tvec2 sum = vec2(0.0);\n\r\n\tvec2 norm = vec2(0.0);\n\n\r\n\tfor(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\r\n\t\tint i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\r\n\t\tint j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\r\n\t\tfloat d = length(vec2(i,j));\n\r\n\t\tvec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\r\n\t\t\t\t\t\t\texp( -d * d / twoSigmaRSquared ));\n\n\r\n\t\tvec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\r\n\t\tnorm += kernel;\n\r\n\t\tsum += kernel * L;\n\r\n\t}\n\n\r\n\tsum /= norm;\n\n\r\n\tfloat H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\r\n\tfloat edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\r\n\tdestColor = vec3(edge);\n\r\n\tgl_FragColor = vec4(destColor, 1.0);\n\r\n}", + F.registerNodeType("texture/xDoG", I), J.title = "Webcam", J.desc = "Webcam texture", J.is_webcam_open = !1, J.prototype.openStream = function() { + if (navigator.getUserMedia) { + this._waiting_confirmation = !0; + navigator.mediaDevices.getUserMedia({audio:!1, video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this)).catch(function(b) { + J.is_webcam_open = !1; + console.log("Webcam rejected", b); + a._webcam_stream = !1; + a.boxcolor = "red"; + a.trigger("stream_error"); + }); + var a = this; + } + }, J.prototype.closeStream = function() { + if (this._webcam_stream) { + var a = this._webcam_stream.getTracks(); + if (a.length) { + for (var b = 0; b < a.length; ++b) { + a[b].stop(); + } + } + J.is_webcam_open = !1; + this._video = this._webcam_stream = null; + this.boxcolor = "black"; + this.trigger("stream_closed"); + } + }, J.prototype.streamReady = function(a) { + this._webcam_stream = a; + this.boxcolor = "green"; + var b = this._video; + b || (b = document.createElement("video"), b.autoplay = !0, b.srcObject = a, this._video = b, b.onloadedmetadata = function(a) { + J.is_webcam_open = !0; + console.log(a); + }); + this.trigger("stream_ready", b); + }, J.prototype.onPropertyChanged = function(a, b) { + "facingMode" == a && (this.properties.facingMode = b, this.closeStream(), this.openStream()); + }, J.prototype.onRemoved = function() { + if (this._webcam_stream) { + var a = this._webcam_stream.getTracks(); + if (a.length) { + for (var b = 0; b < a.length; ++b) { + a[b].stop(); + } + } + this._video = this._webcam_stream = null; + } + }, J.prototype.onDrawBackground = function(a) { + this.flags.collapsed || 20 >= this.size[1] || !this._video || (a.save(), a.webgl ? this._video_texture && a.drawImage(this._video_texture, 0, 0, this.size[0], this.size[1]) : a.drawImage(this._video, 0, 0, this.size[0], this.size[1]), a.restore()); + }, J.prototype.onExecute = function() { + null != this._webcam_stream || this._waiting_confirmation || this.openStream(); + if (this._video && this._video.videoWidth) { + var a = this._video.videoWidth, b = this._video.videoHeight, d = this._video_texture; + d && d.width == a && d.height == b || (this._video_texture = new GL.Texture(a, b, {format:gl.RGB, filter:gl.LINEAR})); + this._video_texture.uploadImage(this._video); + this._video_texture.version = ++this.version; + this.properties.texture_name && (c.getTexturesContainer()[this.properties.texture_name] = this._video_texture); + this.setOutputData(0, this._video_texture); + for (a = 1; a < this.outputs.length; ++a) { + if (this.outputs[a]) { + switch(this.outputs[a].name) { + case "width": + this.setOutputData(a, this._video.videoWidth); + break; + case "height": + this.setOutputData(a, this._video.videoHeight); + } + } + } + } + }, J.prototype.onGetOutputs = function() { + return [["width", "number"], ["height", "number"], ["stream_ready", F.EVENT], ["stream_closed", F.EVENT], ["stream_error", F.EVENT]]; + }, F.registerNodeType("texture/webcam", J), L.title = "Lens FX", L.desc = "distortion and chromatic aberration", L.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, L.prototype.onGetInputs = function() { + return [["enabled", "boolean"]]; + }, L.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a && this.isOutputConnected(0)) { + if (this.properties.precision === c.PASS_THROUGH || !1 === this.getInputOrProperty("enabled")) { + this.setOutputData(0, a); + } else { + var b = this._temp_texture; + b && b.width == a.width && b.height == a.height && b.type == a.type || (b = this._temp_texture = new GL.Texture(a.width, a.height, {type:a.type, format:gl.RGBA, filter:gl.LINEAR})); + var d = L._shader; + d || (d = L._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, L.pixel_shader)); + var k = this.getInputData(1); + null == k && (k = this.properties.factor); + var f = this._uniforms; + f.u_factor = k; + gl.disable(gl.DEPTH_TEST); + b.drawTo(function() { + a.bind(0); + d.uniforms(f).draw(GL.Mesh.getScreenQuad()); + }); + this.setOutputData(0, b); + } + } + }, L.pixel_shader = "precision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_factor;\n\r\n\t\tvec2 barrelDistortion(vec2 coord, float amt) {\n\r\n\t\t\tvec2 cc = coord - 0.5;\n\r\n\t\t\tfloat dist = dot(cc, cc);\n\r\n\t\t\treturn coord + cc * dist * amt;\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tfloat sat( float t )\n\r\n\t\t{\n\r\n\t\t\treturn clamp( t, 0.0, 1.0 );\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tfloat linterp( float t ) {\n\r\n\t\t\treturn sat( 1.0 - abs( 2.0*t - 1.0 ) );\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tfloat remap( float t, float a, float b ) {\n\r\n\t\t\treturn sat( (t - a) / (b - a) );\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tvec4 spectrum_offset( float t ) {\n\r\n\t\t\tvec4 ret;\n\r\n\t\t\tfloat lo = step(t,0.5);\n\r\n\t\t\tfloat hi = 1.0-lo;\n\r\n\t\t\tfloat w = linterp( remap( t, 1.0/6.0, 5.0/6.0 ) );\n\r\n\t\t\tret = vec4(lo,1.0,hi, 1.) * vec4(1.0-w, w, 1.0-w, 1.);\n\r\n\t\t\n\r\n\t\t\treturn pow( ret, vec4(1.0/2.2) );\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tconst float max_distort = 2.2;\n\r\n\t\tconst int num_iter = 12;\n\r\n\t\tconst float reci_num_iter_f = 1.0 / float(num_iter);\n\r\n\t\t\n\r\n\t\tvoid main()\n\r\n\t\t{\t\n\r\n\t\t\tvec2 uv=v_coord;\n\r\n\t\t\tvec4 sumcol = vec4(0.0);\n\r\n\t\t\tvec4 sumw = vec4(0.0);\t\n\r\n\t\t\tfor ( int i=0; i Math.abs(b)) { + return c[1]; + } + a = (a - c[0]) / b; + return c[1] * (1.0 - a) + k[1] * a; + } + } + return 0; + } + }, A.prototype.updateCurve = function() { + for (var a = this._values, b = a.length / 4, d = this.properties.split_channels, c = 0; c < b; ++c) { + if (d) { + a[4 * c] = Math.clamp(255 * this.sampleCurve(c / b, this._points.R), 0, 255), a[4 * c + 1] = Math.clamp(255 * this.sampleCurve(c / b, this._points.G), 0, 255), a[4 * c + 2] = Math.clamp(255 * this.sampleCurve(c / b, this._points.B), 0, 255); + } else { + var k = this.sampleCurve(c / b); + a[4 * c] = a[4 * c + 1] = a[4 * c + 2] = Math.clamp(255 * k, 0, 255); + } + a[4 * c + 3] = 255; + } + this._curve_texture || (this._curve_texture = new GL.Texture(256, 1, {format:gl.RGBA, magFilter:gl.LINEAR, wrap:gl.CLAMP_TO_EDGE})); + this._curve_texture.uploadData(a, null, !0); + }, A.prototype.onSerialize = function(a) { + var b = {}, d; + for (d in this._points) { + b[d] = this._points[d].concat(); + } + a.curves = b; + }, A.prototype.onConfigure = function(a) { + this._points = a.curves; + this.curve_editor && (curve_editor.points = this._points); + this._must_update = !0; + }, A.prototype.onMouseDown = function(a, b, d) { + if (this.curve_editor) { + return (a = this.curve_editor.onMouseDown([b[0], b[1] - this.curve_offset], d)) && this.captureInput(!0), a; + } + }, A.prototype.onMouseMove = function(a, b, d) { + if (this.curve_editor) { + return this.curve_editor.onMouseMove([b[0], b[1] - this.curve_offset], d); + } + }, A.prototype.onMouseUp = function(a, b, d) { + if (this.curve_editor) { + return this.curve_editor.onMouseUp([b[0], b[1] - this.curve_offset], d); + } + this.captureInput(!1); + }, A.channel_line_colors = {RGB:"#666", R:"#F33", G:"#3F3", B:"#33F"}, A.prototype.onDrawBackground = function(a, b) { + if (!this.flags.collapsed) { + this.curve_editor || (this.curve_editor = new F.CurveEditor(this._points.R)); + a.save(); + a.translate(0, this.curve_offset); + var d = this.widgets[1].value; + this.properties.split_channels ? ("RGB" == d && (this.widgets[1].value = d = "R", this.widgets[1].disabled = !1), this.curve_editor.points = this._points.R, this.curve_editor.draw(a, [this.size[0], this.size[1] - this.curve_offset], b, "#111", A.channel_line_colors.R, !0), a.globalCompositeOperation = "lighten", this.curve_editor.points = this._points.G, this.curve_editor.draw(a, [this.size[0], this.size[1] - this.curve_offset], b, null, A.channel_line_colors.G, !0), this.curve_editor.points = + this._points.B, this.curve_editor.draw(a, [this.size[0], this.size[1] - this.curve_offset], b, null, A.channel_line_colors.B, !0), a.globalCompositeOperation = "source-over") : (this.widgets[1].value = d = "RGB", this.widgets[1].disabled = !0); + this.curve_editor.points = this._points[d]; + this.curve_editor.draw(a, [this.size[0], this.size[1] - this.curve_offset], b, this.properties.split_channels ? null : "#111", A.channel_line_colors[d]); + a.restore(); + } + }, A.pixel_shader = "precision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_curve;\n\r\n\t\tuniform float u_range;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = texture2D( u_texture, v_coord ) * u_range;\n\r\n\t\t\tcolor.x = texture2D( u_curve, vec2( color.x, 0.5 ) ).x;\n\r\n\t\t\tcolor.y = texture2D( u_curve, vec2( color.y, 0.5 ) ).y;\n\r\n\t\t\tcolor.z = texture2D( u_curve, vec2( color.z, 0.5 ) ).z;\n\r\n\t\t\t//color.w = texture2D( u_curve, vec2( color.w, 0.5 ) ).w;\n\r\n\t\t\tgl_FragColor = color;\n\r\n\t\t}", + F.registerNodeType("texture/curve", A), r.title = "Exposition", r.desc = "Controls texture exposition", r.widgets_info = {exposition:{widget:"slider", min:0, max:3}, precision:{widget:"combo", values:c.MODE_VALUES}}, r.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a && this.isOutputConnected(0)) { + var b = this._temp_texture; + b && b.width == a.width && b.height == a.height && b.type == a.type || (b = this._temp_texture = new GL.Texture(a.width, a.height, {type:a.type, format:gl.RGBA, filter:gl.LINEAR})); + var d = r._shader; + d || (d = r._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, r.pixel_shader)); + var c = this.getInputData(1); + null != c && (this.properties.exposition = c); + var k = this._uniforms; + b.drawTo(function() { + gl.disable(gl.DEPTH_TEST); + a.bind(0); + d.uniforms(k).draw(GL.Mesh.getScreenQuad()); + }); + this.setOutputData(0, b); + } + }, r.pixel_shader = "precision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_exposition;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = texture2D( u_texture, v_coord );\n\r\n\t\t\tgl_FragColor = vec4( color.xyz * u_exposition, color.a );\n\r\n\t\t}", F.registerNodeType("texture/exposition", r), K.title = "Tone Mapping", K.desc = "Applies Tone Mapping to convert from high to low", K.widgets_info = {precision:{widget:"combo", + values:c.MODE_VALUES}}, K.prototype.onGetInputs = function() { + return [["enabled", "boolean"]]; + }, K.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a && this.isOutputConnected(0)) { + if (this.properties.precision === c.PASS_THROUGH || !1 === this.getInputOrProperty("enabled")) { + this.setOutputData(0, a); + } else { + var b = this._temp_texture; + b && b.width == a.width && b.height == a.height && b.type == a.type || (b = this._temp_texture = new GL.Texture(a.width, a.height, {type:a.type, format:gl.RGBA, filter:gl.LINEAR})); + var d = this.getInputData(1); + null == d && (d = this.properties.average_lum); + var k = this._uniforms, f = null; + d.constructor === Number ? (this.properties.average_lum = d, k.u_average_lum = this.properties.average_lum, f = K._shader, f || (f = K._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, K.pixel_shader))) : d.constructor === GL.Texture && (k.u_average_texture = d.bind(1), f = K._shader_texture, f || (f = K._shader_texture = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, K.pixel_shader, {AVG_TEXTURE:""}))); + k.u_lumwhite2 = this.properties.lum_white * this.properties.lum_white; + k.u_scale = this.properties.scale; + k.u_igamma = 1 / this.properties.gamma; + gl.disable(gl.DEPTH_TEST); + b.drawTo(function() { + a.bind(0); + f.uniforms(k).draw(GL.Mesh.getScreenQuad()); + }); + this.setOutputData(0, this._temp_texture); + } + } + }, K.pixel_shader = "precision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_scale;\n\r\n\t\t#ifdef AVG_TEXTURE\n\r\n\t\t\tuniform sampler2D u_average_texture;\n\r\n\t\t#else\n\r\n\t\t\tuniform float u_average_lum;\n\r\n\t\t#endif\n\r\n\t\tuniform float u_lumwhite2;\n\r\n\t\tuniform float u_igamma;\n\r\n\t\tvec3 RGB2xyY (vec3 rgb)\n\r\n\t\t{\n\r\n\t\t\t const mat3 RGB2XYZ = mat3(0.4124, 0.3576, 0.1805,\n\r\n\t\t\t\t\t\t\t\t\t 0.2126, 0.7152, 0.0722,\n\r\n\t\t\t\t\t\t\t\t\t 0.0193, 0.1192, 0.9505);\n\r\n\t\t\tvec3 XYZ = RGB2XYZ * rgb;\n\r\n\t\t\t\n\r\n\t\t\tfloat f = (XYZ.x + XYZ.y + XYZ.z);\n\r\n\t\t\treturn vec3(XYZ.x / f,\n\r\n\t\t\t\t\t\tXYZ.y / f,\n\r\n\t\t\t\t\t\tXYZ.y);\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = texture2D( u_texture, v_coord );\n\r\n\t\t\tvec3 rgb = color.xyz;\n\r\n\t\t\tfloat average_lum = 0.0;\n\r\n\t\t\t#ifdef AVG_TEXTURE\n\r\n\t\t\t\tvec3 pixel = texture2D(u_average_texture,vec2(0.5)).xyz;\n\r\n\t\t\t\taverage_lum = (pixel.x + pixel.y + pixel.z) / 3.0;\n\r\n\t\t\t#else\n\r\n\t\t\t\taverage_lum = u_average_lum;\n\r\n\t\t\t#endif\n\r\n\t\t\t//Ld - this part of the code is the same for both versions\n\r\n\t\t\tfloat lum = dot(rgb, vec3(0.2126, 0.7152, 0.0722));\n\r\n\t\t\tfloat L = (u_scale / average_lum) * lum;\n\r\n\t\t\tfloat Ld = (L * (1.0 + L / u_lumwhite2)) / (1.0 + L);\n\r\n\t\t\t//first\n\r\n\t\t\t//vec3 xyY = RGB2xyY(rgb);\n\r\n\t\t\t//xyY.z *= Ld;\n\r\n\t\t\t//rgb = xyYtoRGB(xyY);\n\r\n\t\t\t//second\n\r\n\t\t\trgb = (rgb / lum) * Ld;\n\r\n\t\t\trgb = max(rgb,vec3(0.001));\n\r\n\t\t\trgb = pow( rgb, vec3( u_igamma ) );\n\r\n\t\t\tgl_FragColor = vec4( rgb, color.a );\n\r\n\t\t}", + F.registerNodeType("texture/tonemapping", K), N.title = "Perlin", N.desc = "Generates a perlin noise texture", N.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}, width:{type:"Number", precision:0, step:1}, height:{type:"Number", precision:0, step:1}, octaves:{type:"Number", precision:0, step:1, min:1, max:50}}, N.prototype.onGetInputs = function() { + return [["seed", "Number"], ["persistence", "Number"], ["octaves", "Number"], ["scale", "Number"], ["amplitude", "Number"], ["offset", "vec2"]]; + }, N.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var a = this.properties.width | 0, b = this.properties.height | 0; + 0 == a && (a = gl.viewport_data[2]); + 0 == b && (b = gl.viewport_data[3]); + var d = c.getTextureType(this.properties.precision), k = this._texture; + k && k.width == a && k.height == b && k.type == d || (k = this._texture = new GL.Texture(a, b, {type:d, format:gl.RGB, filter:gl.LINEAR})); + var f = this.getInputOrProperty("persistence"), h = this.getInputOrProperty("octaves"), e = this.getInputOrProperty("offset"), n = this.getInputOrProperty("scale"), g = this.getInputOrProperty("amplitude"), q = this.getInputOrProperty("seed"); + d = "" + a + b + d + f + h + n + q + e[0] + e[1] + g; + if (d != this._key) { + this._key = d; + var l = this._uniforms; + l.u_persistence = f; + l.u_octaves = h; + l.u_offset.set(e); + l.u_scale = n; + l.u_amplitude = g; + l.u_seed = 128 * q; + l.u_viewport[0] = a; + l.u_viewport[1] = b; + var m = N._shader; + m || (m = N._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, N.pixel_shader)); + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + k.drawTo(function() { + m.uniforms(l).draw(GL.Mesh.getScreenQuad()); + }); + } + this.setOutputData(0, k); + } + }, N.pixel_shader = "precision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform vec2 u_offset;\n\r\n\t\tuniform float u_scale;\n\r\n\t\tuniform float u_persistence;\n\r\n\t\tuniform int u_octaves;\n\r\n\t\tuniform float u_amplitude;\n\r\n\t\tuniform vec2 u_viewport;\n\r\n\t\tuniform float u_seed;\n\r\n\t\t#define M_PI 3.14159265358979323846\n\r\n\t\t\n\r\n\t\tfloat rand(vec2 c){\treturn fract(sin(dot(c.xy ,vec2( 12.9898 + u_seed,78.233 + u_seed))) * 43758.5453); }\n\r\n\t\t\n\r\n\t\tfloat noise(vec2 p, float freq ){\n\r\n\t\t\tfloat unit = u_viewport.x/freq;\n\r\n\t\t\tvec2 ij = floor(p/unit);\n\r\n\t\t\tvec2 xy = mod(p,unit)/unit;\n\r\n\t\t\t//xy = 3.*xy*xy-2.*xy*xy*xy;\n\r\n\t\t\txy = .5*(1.-cos(M_PI*xy));\n\r\n\t\t\tfloat a = rand((ij+vec2(0.,0.)));\n\r\n\t\t\tfloat b = rand((ij+vec2(1.,0.)));\n\r\n\t\t\tfloat c = rand((ij+vec2(0.,1.)));\n\r\n\t\t\tfloat d = rand((ij+vec2(1.,1.)));\n\r\n\t\t\tfloat x1 = mix(a, b, xy.x);\n\r\n\t\t\tfloat x2 = mix(c, d, xy.x);\n\r\n\t\t\treturn mix(x1, x2, xy.y);\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tfloat pNoise(vec2 p, int res){\n\r\n\t\t\tfloat persistance = u_persistence;\n\r\n\t\t\tfloat n = 0.;\n\r\n\t\t\tfloat normK = 0.;\n\r\n\t\t\tfloat f = 4.;\n\r\n\t\t\tfloat amp = 1.0;\n\r\n\t\t\tint iCount = 0;\n\r\n\t\t\tfor (int i = 0; i<50; i++){\n\r\n\t\t\t\tn+=amp*noise(p, f);\n\r\n\t\t\t\tf*=2.;\n\r\n\t\t\t\tnormK+=amp;\n\r\n\t\t\t\tamp*=persistance;\n\r\n\t\t\t\tif (iCount >= res)\n\r\n\t\t\t\t\tbreak;\n\r\n\t\t\t\tiCount++;\n\r\n\t\t\t}\n\r\n\t\t\tfloat nf = n/normK;\n\r\n\t\t\treturn nf*nf*nf*nf;\n\r\n\t\t}\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec2 uv = v_coord * u_scale * u_viewport + u_offset * u_scale;\n\r\n\t\t\tvec4 color = vec4( pNoise( uv, u_octaves ) * u_amplitude );\n\r\n\t\t\tgl_FragColor = color;\n\r\n\t\t}", + F.registerNodeType("texture/perlin", N), M.title = "Canvas2D", M.desc = "Executes Canvas2D code inside a texture or the viewport.", M.help = "Set width and height to 0 to match viewport size.", M.default_code = "//vars: canvas,ctx,time\nctx.fillStyle='red';\nctx.fillRect(0,0,50,50);\n", M.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}, code:{type:"code"}, width:{type:"Number", precision:0, step:1}, height:{type:"Number", precision:0, step:1}}, M.prototype.onPropertyChanged = function(a, + b) { + "code" == a && this.compileCode(b); + }, M.prototype.compileCode = function(a) { + this._func = null; + if (F.allow_scripts) { + try { + this._func = new Function("canvas", "ctx", "time", "script", "v", a), this.boxcolor = "#00FF00"; + } catch (R) { + this.boxcolor = "#FF0000", console.error("Error parsing script"), console.error(R); + } + } + }, M.prototype.onExecute = function() { + var a = this._func; + a && this.isOutputConnected(0) && this.executeDraw(a); + }, M.prototype.executeDraw = function(a) { + var b = this.properties.width || gl.canvas.width, d = this.properties.height || gl.canvas.height, k = this._temp_texture, f = c.getTextureType(this.properties.precision); + k && k.width == b && k.height == d && k.type == f || (k = this._temp_texture = new GL.Texture(b, d, {format:gl.RGBA, filter:gl.LINEAR, type:f})); + var h = this.getInputData(0), e = this.properties, n = this, g = this.graph.getTime(), q = gl, l = gl.canvas; + if (this.properties.use_html_canvas || !w.enableWebGLCanvas) { + this._canvas ? (l = this._canvas, q = this._ctx) : (l = this._canvas = createCanvas(b.height), q = this._ctx = l.getContext("2d")), l.width = b, l.height = d; + } + if (q == gl) { + k.drawTo(function() { + gl.start2D(); + e.clear && (gl.clearColor(0, 0, 0, 0), gl.clear(gl.COLOR_BUFFER_BIT)); + try { + a.draw ? a.draw.call(n, l, q, g, a, h) : a.call(n, l, q, g, a, h), n.boxcolor = "#00FF00"; + } catch (Q) { + n.boxcolor = "#FF0000", console.error("Error executing script"), console.error(Q); + } + gl.finish2D(); + }); + } else { + e.clear && q.clearRect(0, 0, l.width, l.height); + try { + a.draw ? a.draw.call(this, l, q, g, a, h) : a.call(this, l, q, g, a, h), this.boxcolor = "#00FF00"; + } catch (Q) { + this.boxcolor = "#FF0000", console.error("Error executing script"), console.error(Q); + } + k.uploadImage(l); + } + this.setOutputData(0, k); + }, F.registerNodeType("texture/canvas2D", M), O.title = "Matte", O.desc = "Extracts background", O.widgets_info = {key_color:{widget:"color"}, precision:{widget:"combo", values:c.MODE_VALUES}}, O.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var a = this.getInputData(0); + if (this.properties.precision === c.PASS_THROUGH) { + this.setOutputData(0, a); + } else { + if (a) { + this._tex = c.getTargetTexture(a, this._tex, this.properties.precision); + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + this._uniforms || (this._uniforms = {u_texture:0, u_key_color:this.properties.key_color, u_threshold:1, u_slope:1}); + var b = this._uniforms, d = Mesh.getScreenQuad(), k = O._shader; + k || (k = O._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, O.pixel_shader)); + b.u_key_color = this.properties.key_color; + b.u_threshold = this.properties.threshold; + b.u_slope = this.properties.slope; + this._tex.drawTo(function() { + a.bind(0); + k.uniforms(b).draw(d); + }); + this.setOutputData(0, this._tex); + } + } + } + }, O.pixel_shader = "precision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec3 u_key_color;\n\r\n\t\tuniform float u_threshold;\n\r\n\t\tuniform float u_slope;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec3 color = texture2D( u_texture, v_coord ).xyz;\n\r\n\t\t\tfloat diff = length( normalize(color) - normalize(u_key_color) );\n\r\n\t\t\tfloat edge = u_threshold * (1.0 - u_slope);\n\r\n\t\t\tfloat alpha = smoothstep( edge, u_threshold, diff);\n\r\n\t\t\tgl_FragColor = vec4( color, alpha );\n\r\n\t\t}", + F.registerNodeType("texture/matte", O), P.title = "CubemapToTexture2D", P.desc = "Transforms a CUBEMAP texture into a TEXTURE2D in Polar Representation", P.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var a = this.getInputData(0); + if (a && a.texture_type == GL.TEXTURE_CUBE_MAP) { + !this._last_tex || this._last_tex.height == a.height && this._last_tex.type == a.type || (this._last_tex = null); + var b = this.getInputOrProperty("yaw"); + this._last_tex = GL.Texture.cubemapToTexture2D(a, a.height, this._last_tex, !0, b); + this.setOutputData(0, this._last_tex); + } + } + }, F.registerNodeType("texture/cubemapToTexture2D", P)); +})(this); +(function(w) { + var c = w.LiteGraph; + if ("undefined" != typeof GL) { + var p = function() { + this.addInput("Tex.", "Texture"); + this.addInput("intensity", "number"); + this.addOutput("Texture", "Texture"); + this.properties = {intensity:1, invert:!1, precision:LGraphTexture.DEFAULT}; + p._shader || (p._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, p.pixel_shader)); + }, m = function() { + this.addInput("Texture", "Texture"); + this.addInput("value1", "number"); + this.addInput("value2", "number"); + this.addOutput("Texture", "Texture"); + this.properties = {fx:"halftone", value1:1, value2:1, precision:LGraphTexture.DEFAULT}; + }, g = function() { + this.addInput("Texture", "Texture"); + this.addInput("Blurred", "Texture"); + this.addInput("Mask", "Texture"); + this.addInput("Threshold", "number"); + this.addOutput("Texture", "Texture"); + this.properties = {shape:"", size:10, alpha:1.0, threshold:1.0, high_precision:!1}; + }, u = function() { + this.addInput("Texture", "Texture"); + this.addInput("Aberration", "number"); + this.addInput("Distortion", "number"); + this.addInput("Blur", "number"); + this.addOutput("Texture", "Texture"); + this.properties = {aberration:1.0, distortion:1.0, blur:1.0, precision:LGraphTexture.DEFAULT}; + u._shader || (u._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, u.pixel_shader), u._texture = new GL.Texture(3, 1, {format:gl.RGB, wrap:gl.CLAMP_TO_EDGE, magFilter:gl.LINEAR, minFilter:gl.LINEAR, pixel_data:[255, 0, 0, 0, 255, 0, 0, 0, 255]})); + }; + u.title = "Lens"; + u.desc = "Camera Lens distortion"; + u.widgets_info = {precision:{widget:"combo", values:LGraphTexture.MODE_VALUES}}; + u.prototype.onExecute = function() { + var c = this.getInputData(0); + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, c); + } else { + if (c) { + this._tex = LGraphTexture.getTargetTexture(c, this._tex, this.properties.precision); + var g = this.properties.aberration; + this.isInputConnected(1) && (g = this.getInputData(1), this.properties.aberration = g); + var m = this.properties.distortion; + this.isInputConnected(2) && (m = this.getInputData(2), this.properties.distortion = m); + var p = this.properties.blur; + this.isInputConnected(3) && (p = this.getInputData(3), this.properties.blur = p); + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var w = Mesh.getScreenQuad(), z = u._shader; + this._tex.drawTo(function() { + c.bind(0); + z.uniforms({u_texture:0, u_aberration:g, u_distortion:m, u_blur:p}).draw(w); + }); + this.setOutputData(0, this._tex); + } + } + }; + u.pixel_shader = "precision highp float;\n\r\n\t\t\tprecision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform vec2 u_camera_planes;\n\r\n\t\t\tuniform float u_aberration;\n\r\n\t\t\tuniform float u_distortion;\n\r\n\t\t\tuniform float u_blur;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec2 coord = v_coord;\n\r\n\t\t\t\tfloat dist = distance(vec2(0.5), coord);\n\r\n\t\t\t\tvec2 dist_coord = coord - vec2(0.5);\n\r\n\t\t\t\tfloat percent = 1.0 + ((0.5 - dist) / 0.5) * u_distortion;\n\r\n\t\t\t\tdist_coord *= percent;\n\r\n\t\t\t\tcoord = dist_coord + vec2(0.5);\n\r\n\t\t\t\tvec4 color = texture2D(u_texture,coord, u_blur * dist);\n\r\n\t\t\t\tcolor.r = texture2D(u_texture,vec2(0.5) + dist_coord * (1.0+0.01*u_aberration), u_blur * dist ).r;\n\r\n\t\t\t\tcolor.b = texture2D(u_texture,vec2(0.5) + dist_coord * (1.0-0.01*u_aberration), u_blur * dist ).b;\n\r\n\t\t\t\tgl_FragColor = color;\n\r\n\t\t\t}\n\r\n\t\t\t"; + c.registerNodeType("fx/lens", u); + w.LGraphFXLens = u; + g.title = "Bokeh"; + g.desc = "applies an Bokeh effect"; + g.widgets_info = {shape:{widget:"texture"}}; + g.prototype.onExecute = function() { + var c = this.getInputData(0), m = this.getInputData(1), p = this.getInputData(2); + if (c && p && this.properties.shape) { + m || (m = c); + var u = LGraphTexture.getTexture(this.properties.shape); + if (u) { + var w = this.properties.threshold; + this.isInputConnected(3) && (w = this.getInputData(3), this.properties.threshold = w); + var z = gl.UNSIGNED_BYTE; + this.properties.high_precision && (z = gl.half_float_ext ? gl.HALF_FLOAT_OES : gl.FLOAT); + this._temp_texture && this._temp_texture.type == z && this._temp_texture.width == c.width && this._temp_texture.height == c.height || (this._temp_texture = new GL.Texture(c.width, c.height, {type:z, format:gl.RGBA, filter:gl.LINEAR})); + var e = g._first_shader; + e || (e = g._first_shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, g._first_pixel_shader)); + var C = g._second_shader; + C || (C = g._second_shader = new GL.Shader(g._second_vertex_shader, g._second_pixel_shader)); + var D = this._points_mesh; + D && D._width == c.width && D._height == c.height && 2 == D._spacing || (D = this.createPointsMesh(c.width, c.height, 2)); + var t = Mesh.getScreenQuad(), G = this.properties.size, n = this.properties.alpha; + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.BLEND); + this._temp_texture.drawTo(function() { + c.bind(0); + m.bind(1); + p.bind(2); + e.uniforms({u_texture:0, u_texture_blur:1, u_mask:2, u_texsize:[c.width, c.height]}).draw(t); + }); + this._temp_texture.drawTo(function() { + gl.enable(gl.BLEND); + gl.blendFunc(gl.ONE, gl.ONE); + c.bind(0); + u.bind(3); + C.uniforms({u_texture:0, u_mask:2, u_shape:3, u_alpha:n, u_threshold:w, u_pointSize:G, u_itexsize:[1.0 / c.width, 1.0 / c.height]}).draw(D, gl.POINTS); + }); + this.setOutputData(0, this._temp_texture); + } + } else { + this.setOutputData(0, c); + } + }; + g.prototype.createPointsMesh = function(c, g, m) { + for (var l = Math.round(c / m), p = Math.round(g / m), u = new Float32Array(l * p * 2), e = -1, w = 2 / c * m, y = 2 / g * m, t = 0; t < p; ++t) { + for (var B = -1, n = 0; n < l; ++n) { + var q = t * l * 2 + 2 * n; + u[q] = B; + u[q + 1] = e; + B += w; + } + e += y; + } + this._points_mesh = GL.Mesh.load({vertices2D:u}); + this._points_mesh._width = c; + this._points_mesh._height = g; + this._points_mesh._spacing = m; + return this._points_mesh; + }; + g._first_pixel_shader = "precision highp float;\n\r\n\t\t\tprecision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform sampler2D u_texture_blur;\n\r\n\t\t\tuniform sampler2D u_mask;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, v_coord);\n\r\n\t\t\t\tvec4 blurred_color = texture2D(u_texture_blur, v_coord);\n\r\n\t\t\t\tfloat mask = texture2D(u_mask, v_coord).x;\n\r\n\t\t\t gl_FragColor = mix(color, blurred_color, mask);\n\r\n\t\t\t}\n\r\n\t\t\t"; + g._second_vertex_shader = "precision highp float;\n\r\n\t\t\tattribute vec2 a_vertex2D;\n\r\n\t\t\tvarying vec4 v_color;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform sampler2D u_mask;\n\r\n\t\t\tuniform vec2 u_itexsize;\n\r\n\t\t\tuniform float u_pointSize;\n\r\n\t\t\tuniform float u_threshold;\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec2 coord = a_vertex2D * 0.5 + 0.5;\n\r\n\t\t\t\tv_color = texture2D( u_texture, coord );\n\r\n\t\t\t\tv_color += texture2D( u_texture, coord + vec2(u_itexsize.x, 0.0) );\n\r\n\t\t\t\tv_color += texture2D( u_texture, coord + vec2(0.0, u_itexsize.y));\n\r\n\t\t\t\tv_color += texture2D( u_texture, coord + u_itexsize);\n\r\n\t\t\t\tv_color *= 0.25;\n\r\n\t\t\t\tfloat mask = texture2D(u_mask, coord).x;\n\r\n\t\t\t\tfloat luminance = length(v_color) * mask;\n\r\n\t\t\t\t/*luminance /= (u_pointSize*u_pointSize)*0.01 */;\n\r\n\t\t\t\tluminance -= u_threshold;\n\r\n\t\t\t\tif(luminance < 0.0)\n\r\n\t\t\t\t{\n\r\n\t\t\t\t\tgl_Position.x = -100.0;\n\r\n\t\t\t\t\treturn;\n\r\n\t\t\t\t}\n\r\n\t\t\t\tgl_PointSize = u_pointSize;\n\r\n\t\t\t\tgl_Position = vec4(a_vertex2D,0.0,1.0);\n\r\n\t\t\t}\n\r\n\t\t\t"; + g._second_pixel_shader = "precision highp float;\n\r\n\t\t\tvarying vec4 v_color;\n\r\n\t\t\tuniform sampler2D u_shape;\n\r\n\t\t\tuniform float u_alpha;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec4 color = texture2D( u_shape, gl_PointCoord );\n\r\n\t\t\t\tcolor *= v_color * u_alpha;\n\r\n\t\t\t\tgl_FragColor = color;\n\r\n\t\t\t}\n"; + c.registerNodeType("fx/bokeh", g); + w.LGraphFXBokeh = g; + m.title = "FX"; + m.desc = "applies an FX from a list"; + m.widgets_info = {fx:{widget:"combo", values:["halftone", "pixelate", "lowpalette", "noise", "gamma"]}, precision:{widget:"combo", values:LGraphTexture.MODE_VALUES}}; + m.shaders = {}; + m.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var c = this.getInputData(0); + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, c); + } else { + if (c) { + this._tex = LGraphTexture.getTargetTexture(c, this._tex, this.properties.precision); + var g = this.properties.value1; + this.isInputConnected(1) && (g = this.getInputData(1), this.properties.value1 = g); + var p = this.properties.value2; + this.isInputConnected(2) && (p = this.getInputData(2), this.properties.value2 = p); + var u = this.properties.fx, E = m.shaders[u]; + if (!E) { + var z = m["pixel_shader_" + u]; + if (!z) { + return; + } + E = m.shaders[u] = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, z); + } + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var e = Mesh.getScreenQuad(); + var C = w.LS && LS.Renderer._current_camera ? [LS.Renderer._current_camera.near, LS.Renderer._current_camera.far] : [1, 100]; + var D = null; + "noise" == u && (D = LGraphTexture.getNoiseTexture()); + this._tex.drawTo(function() { + c.bind(0); + "noise" == u && D.bind(1); + E.uniforms({u_texture:0, u_noise:1, u_size:[c.width, c.height], u_rand:[Math.random(), Math.random()], u_value1:g, u_value2:p, u_camera_planes:C}).draw(e); + }); + this.setOutputData(0, this._tex); + } + } + } + }; + m.pixel_shader_halftone = "precision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform vec2 u_camera_planes;\n\r\n\t\t\tuniform vec2 u_size;\n\r\n\t\t\tuniform float u_value1;\n\r\n\t\t\tuniform float u_value2;\n\r\n\t\t\t\n\r\n\t\t\tfloat pattern() {\n\r\n\t\t\t\tfloat s = sin(u_value1 * 3.1415), c = cos(u_value1 * 3.1415);\n\r\n\t\t\t\tvec2 tex = v_coord * u_size.xy;\n\r\n\t\t\t\tvec2 point = vec2(\n\r\n\t\t\t\t c * tex.x - s * tex.y ,\n\r\n\t\t\t\t s * tex.x + c * tex.y \n\r\n\t\t\t\t) * u_value2;\n\r\n\t\t\t\treturn (sin(point.x) * sin(point.y)) * 4.0;\n\r\n\t\t\t}\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, v_coord);\n\r\n\t\t\t\tfloat average = (color.r + color.g + color.b) / 3.0;\n\r\n\t\t\t\tgl_FragColor = vec4(vec3(average * 10.0 - 5.0 + pattern()), color.a);\n\r\n\t\t\t}\n"; + m.pixel_shader_pixelate = "precision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform vec2 u_camera_planes;\n\r\n\t\t\tuniform vec2 u_size;\n\r\n\t\t\tuniform float u_value1;\n\r\n\t\t\tuniform float u_value2;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec2 coord = vec2( floor(v_coord.x * u_value1) / u_value1, floor(v_coord.y * u_value2) / u_value2 );\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, coord);\n\r\n\t\t\t\tgl_FragColor = color;\n\r\n\t\t\t}\n"; + m.pixel_shader_lowpalette = "precision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform vec2 u_camera_planes;\n\r\n\t\t\tuniform vec2 u_size;\n\r\n\t\t\tuniform float u_value1;\n\r\n\t\t\tuniform float u_value2;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, v_coord);\n\r\n\t\t\t\tgl_FragColor = floor(color * u_value1) / u_value1;\n\r\n\t\t\t}\n"; + m.pixel_shader_noise = "precision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform sampler2D u_noise;\n\r\n\t\t\tuniform vec2 u_size;\n\r\n\t\t\tuniform float u_value1;\n\r\n\t\t\tuniform float u_value2;\n\r\n\t\t\tuniform vec2 u_rand;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, v_coord);\n\r\n\t\t\t\tvec3 noise = texture2D(u_noise, v_coord * vec2(u_size.x / 512.0, u_size.y / 512.0) + u_rand).xyz - vec3(0.5);\n\r\n\t\t\t\tgl_FragColor = vec4( color.xyz + noise * u_value1, color.a );\n\r\n\t\t\t}\n"; + m.pixel_shader_gamma = "precision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform float u_value1;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, v_coord);\n\r\n\t\t\t\tfloat gamma = 1.0 / u_value1;\n\r\n\t\t\t\tgl_FragColor = vec4( pow( color.xyz, vec3(gamma) ), color.a );\n\r\n\t\t\t}\n"; + c.registerNodeType("fx/generic", m); + w.LGraphFXGeneric = m; + p.title = "Vigneting"; + p.desc = "Vigneting"; + p.widgets_info = {precision:{widget:"combo", values:LGraphTexture.MODE_VALUES}}; + p.prototype.onExecute = function() { + var c = this.getInputData(0); + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, c); + } else { + if (c) { + this._tex = LGraphTexture.getTargetTexture(c, this._tex, this.properties.precision); + var g = this.properties.intensity; + this.isInputConnected(1) && (g = this.getInputData(1), this.properties.intensity = g); + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var m = Mesh.getScreenQuad(), u = p._shader, w = this.properties.invert; + this._tex.drawTo(function() { + c.bind(0); + u.uniforms({u_texture:0, u_intensity:g, u_isize:[1 / c.width, 1 / c.height], u_invert:w ? 1 : 0}).draw(m); + }); + this.setOutputData(0, this._tex); + } + } + }; + p.pixel_shader = "precision highp float;\n\r\n\t\t\tprecision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform float u_intensity;\n\r\n\t\t\tuniform int u_invert;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tfloat luminance = 1.0 - length( v_coord - vec2(0.5) ) * 1.414;\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, v_coord);\n\r\n\t\t\t\tif(u_invert == 1)\n\r\n\t\t\t\t\tluminance = 1.0 - luminance;\n\r\n\t\t\t\tluminance = mix(1.0, luminance, u_intensity);\n\r\n\t\t\t gl_FragColor = vec4( luminance * color.xyz, color.a);\n\r\n\t\t\t}\n\r\n\t\t\t"; + c.registerNodeType("fx/vigneting", p); + w.LGraphFXVigneting = p; + } +})(this); +(function(w) { + function c(c) { + this.cmd = this.channel = 0; + this.data = new Uint32Array(3); + c && this.setup(c); + } + function p(c, e) { + navigator.requestMIDIAccess ? (this.on_ready = c, this.state = {note:[], cc:[]}, this.input_ports = null, this.input_ports_info = [], this.output_ports = null, this.output_ports_info = [], navigator.requestMIDIAccess().then(this.onMIDISuccess.bind(this), this.onMIDIFailure.bind(this))) : (this.error = "not suppoorted", e ? e("Not supported") : console.error("MIDI NOT SUPPORTED, enable by chrome://flags")); + } + function m() { + this.addOutput("on_midi", t.EVENT); + this.addOutput("out", "midi"); + this.properties = {port:0}; + this._current_midi_event = this._last_midi_event = null; + this.boxcolor = "#AAA"; + this._last_time = 0; + var c = this; + new p(function(e) { + c._midi = e; + if (c._waiting) { + c.onStart(); + } + c._waiting = !1; + }); + } + function g() { + this.addInput("send", t.EVENT); + this.properties = {port:0}; + var c = this; + new p(function(e) { + c._midi = e; + c.widget.options.values = c.getMIDIOutputs(); + }); + this.widget = this.addWidget("combo", "Device", this.properties.port, {property:"port", values:this.getMIDIOutputs.bind(this)}); + this.size = [340, 60]; + } + function u() { + this.addInput("on_midi", t.EVENT); + this._str = ""; + this.size = [200, 40]; + } + function l() { + this.properties = {channel:-1, cmd:-1, min_value:-1, max_value:-1}; + var c = this; + this._learning = !1; + this.addWidget("button", "Learn", "", function() { + c._learning = !0; + c.boxcolor = "#FA3"; + }); + this.addInput("in", t.EVENT); + this.addOutput("on_midi", t.EVENT); + this.boxcolor = "#AAA"; + } + function B() { + this.properties = {channel:0, cmd:144, value1:1, value2:1}; + this.addInput("send", t.EVENT); + this.addInput("assign", t.EVENT); + this.addOutput("on_midi", t.EVENT); + this.midi_event = new c; + this.gate = !1; + } + function y() { + this.properties = {cc:1, value:0}; + this.addOutput("value", "number"); + } + function v() { + this.addInput("generate", t.ACTION); + this.addInput("scale", "string"); + this.addInput("octave", "number"); + this.addOutput("note", t.EVENT); + this.properties = {notes:"A,A#,B,C,C#,D,D#,E,F,F#,G,G#", octave:2, duration:0.5, mode:"sequence"}; + this.notes_pitches = v.processScale(this.properties.notes); + this.sequence_index = 0; + } + function E() { + this.properties = {amount:0}; + this.addInput("in", t.ACTION); + this.addInput("amount", "number"); + this.addOutput("out", t.EVENT); + this.midi_event = new c; + } + function z() { + this.properties = {scale:"A,A#,B,C,C#,D,D#,E,F,F#,G,G#"}; + this.addInput("note", t.ACTION); + this.addInput("scale", "string"); + this.addOutput("out", t.EVENT); + this.valid_notes = Array(12); + this.offset_notes = Array(12); + this.processScale(this.properties.scale); + } + function e() { + this.properties = {url:"", autoplay:!0}; + this.addInput("play", t.ACTION); + this.addInput("pause", t.ACTION); + this.addOutput("note", t.EVENT); + this._midi = null; + this._current_time = 0; + this._playing = !1; + "undefined" == typeof MidiParser && (console.error("midi-parser.js not included, LGMidiPlay requires that library: https://raw.githubusercontent.com/colxi/midi-parser-js/master/src/main.js"), this.boxcolor = "red"); + } + function C() { + this.properties = {volume:0.5, duration:1}; + this.addInput("note", t.ACTION); + this.addInput("volume", "number"); + this.addInput("duration", "number"); + this.addOutput("note", t.EVENT); + "undefined" == typeof AudioSynth ? (console.error("Audiosynth.js not included, LGMidiPlay requires that library"), this.boxcolor = "red") : this.instrument = (this.synth = new AudioSynth).createInstrument("piano"); + } + function D() { + this.properties = {num_octaves:2, start_octave:2}; + this.addInput("note", t.ACTION); + this.addInput("reset", t.ACTION); + this.addOutput("note", t.EVENT); + this.size = [400, 100]; + this.keys = []; + this._last_key = -1; + } + var t = w.LiteGraph; + t.MIDIEvent = c; + c.prototype.fromJSON = function(c) { + this.setup(c.data); + }; + c.prototype.setup = function(e) { + var n = e; + e.constructor === Object && (n = e.data); + this.data.set(n); + this.status = e = n[0]; + n = e & 240; + this.cmd = 240 <= e ? e : n; + this.cmd == c.NOTEON && 0 == this.velocity && (this.cmd = c.NOTEOFF); + this.cmd_str = c.commands[this.cmd] || ""; + if (n >= c.NOTEON || n <= c.NOTEOFF) { + this.channel = e & 15; + } + }; + Object.defineProperty(c.prototype, "velocity", {get:function() { + return this.cmd == c.NOTEON ? this.data[2] : -1; + }, set:function(c) { + this.data[2] = c; + }, enumerable:!0}); + c.notes = "A A# B C C# D D# E F F# G G#".split(" "); + c.note_to_index = {A:0, "A#":1, B:2, C:3, "C#":4, D:5, "D#":6, E:7, F:8, "F#":9, G:10, "G#":11}; + Object.defineProperty(c.prototype, "note", {get:function() { + return this.cmd != c.NOTEON ? -1 : c.toNoteString(this.data[1], !0); + }, set:function(c) { + throw "notes cannot be assigned this way, must modify the data[1]"; + }, enumerable:!0}); + Object.defineProperty(c.prototype, "octave", {get:function() { + return this.cmd != c.NOTEON ? -1 : Math.floor((this.data[1] - 24) / 12 + 1); + }, set:function(c) { + throw "octave cannot be assigned this way, must modify the data[1]"; + }, enumerable:!0}); + c.prototype.getPitch = function() { + return 440 * Math.pow(2, (this.data[1] - 69) / 12); + }; + c.computePitch = function(c) { + return 440 * Math.pow(2, (c - 69) / 12); + }; + c.prototype.getCC = function() { + return this.data[1]; + }; + c.prototype.getCCValue = function() { + return this.data[2]; + }; + c.prototype.getPitchBend = function() { + return this.data[1] + (this.data[2] << 7) - 8192; + }; + c.computePitchBend = function(c, e) { + return c + (e << 7) - 8192; + }; + c.prototype.setCommandFromString = function(e) { + this.cmd = c.computeCommandFromString(e); + }; + c.computeCommandFromString = function(e) { + if (!e) { + return 0; + } + if (e && e.constructor === Number) { + return e; + } + e = e.toUpperCase(); + switch(e) { + case "NOTE ON": + case "NOTEON": + return c.NOTEON; + case "NOTE OFF": + case "NOTEOFF": + return c.NOTEON; + case "KEY PRESSURE": + case "KEYPRESSURE": + return c.KEYPRESSURE; + case "CONTROLLER CHANGE": + case "CONTROLLERCHANGE": + case "CC": + return c.CONTROLLERCHANGE; + case "PROGRAM CHANGE": + case "PROGRAMCHANGE": + case "PC": + return c.PROGRAMCHANGE; + case "CHANNEL PRESSURE": + case "CHANNELPRESSURE": + return c.CHANNELPRESSURE; + case "PITCH BEND": + case "PITCHBEND": + return c.PITCHBEND; + case "TIME TICK": + case "TIMETICK": + return c.TIMETICK; + default: + return Number(e); + } + }; + c.toNoteString = function(e, g) { + e = Math.round(e); + var k = Math.floor((e - 24) / 12 + 1); + e = (e - 21) % 12; + 0 > e && (e = 12 + e); + return c.notes[e] + (g ? "" : k); + }; + c.NoteStringToPitch = function(e) { + e = e.toUpperCase(); + var n = e[0], k = 4; + "#" == e[1] ? (n += "#", 2 < e.length && (k = Number(e[2]))) : 1 < e.length && (k = Number(e[1])); + e = c.note_to_index[n]; + return null == e ? null : 12 * (k - 1) + e + 21; + }; + c.prototype.toString = function() { + var e = "" + this.channel + ". "; + switch(this.cmd) { + case c.NOTEON: + e += "NOTEON " + c.toNoteString(this.data[1]); + break; + case c.NOTEOFF: + e += "NOTEOFF " + c.toNoteString(this.data[1]); + break; + case c.CONTROLLERCHANGE: + e += "CC " + this.data[1] + " " + this.data[2]; + break; + case c.PROGRAMCHANGE: + e += "PC " + this.data[1]; + break; + case c.PITCHBEND: + e += "PITCHBEND " + this.getPitchBend(); + break; + case c.KEYPRESSURE: + e += "KEYPRESS " + this.data[1]; + } + return e; + }; + c.prototype.toHexString = function() { + for (var c = "", e = 0; e < this.data.length; e++) { + c += this.data[e].toString(16) + " "; + } + }; + c.prototype.toJSON = function() { + return {data:[this.data[0], this.data[1], this.data[2]], object_class:"MIDIEvent"}; + }; + c.NOTEOFF = 128; + c.NOTEON = 144; + c.KEYPRESSURE = 160; + c.CONTROLLERCHANGE = 176; + c.PROGRAMCHANGE = 192; + c.CHANNELPRESSURE = 208; + c.PITCHBEND = 224; + c.TIMETICK = 248; + c.commands = {128:"note off", 144:"note on", 160:"key pressure", 176:"controller change", 192:"program change", 208:"channel pressure", 224:"pitch bend", 240:"system", 242:"Song pos", 243:"Song select", 246:"Tune request", 248:"time tick", 250:"Start Song", 251:"Continue Song", 252:"Stop Song", 254:"Sensing", 255:"Reset"}; + c.commands_short = {128:"NOTEOFF", 144:"NOTEOFF", 160:"KEYP", 176:"CC", 192:"PC", 208:"CP", 224:"PB", 240:"SYS", 242:"POS", 243:"SELECT", 246:"TUNEREQ", 248:"TT", 250:"START", 251:"CONTINUE", 252:"STOP", 254:"SENS", 255:"RESET"}; + c.commands_reversed = {}; + for (var G in c.commands) { + c.commands_reversed[c.commands[G]] = G; + } + p.input = null; + p.MIDIEvent = c; + p.prototype.onMIDISuccess = function(c) { + console.log("MIDI ready!"); + console.log(c); + this.midi = c; + this.updatePorts(); + if (this.on_ready) { + this.on_ready(this); + } + }; + p.prototype.updatePorts = function() { + var c = this.midi; + this.input_ports = c.inputs; + this.input_ports_info = []; + this.output_ports = c.outputs; + this.output_ports_info = []; + c = 0; + for (var e = this.input_ports.values(), k = e.next(); k && !1 === k.done;) { + k = k.value, this.input_ports_info.push(k), console.log("Input port [type:'" + k.type + "'] id:'" + k.id + "' manufacturer:'" + k.manufacturer + "' name:'" + k.name + "' version:'" + k.version + "'"), c++, k = e.next(); + } + this.num_input_ports = c; + c = 0; + e = this.output_ports.values(); + for (k = e.next(); k && !1 === k.done;) { + k = k.value, this.output_ports_info.push(k), console.log("Output port [type:'" + k.type + "'] id:'" + k.id + "' manufacturer:'" + k.manufacturer + "' name:'" + k.name + "' version:'" + k.version + "'"), c++, k = e.next(); + } + this.num_output_ports = c; + }; + p.prototype.onMIDIFailure = function(c) { + console.error("Failed to get MIDI access - " + c); + }; + p.prototype.openInputPort = function(e, g) { + e = this.input_ports.get("input-" + e); + if (!e) { + return !1; + } + p.input = this; + var k = this; + e.onmidimessage = function(a) { + var b = new c(a.data); + k.updateState(b); + g && g(a.data, b); + if (p.on_message) { + p.on_message(a.data, b); + } + }; + console.log("port open: ", e); + return !0; + }; + p.parseMsg = function(c) { + }; + p.prototype.updateState = function(e) { + switch(e.cmd) { + case c.NOTEON: + this.state.note[e.value1 | 0] = e.value2; + break; + case c.NOTEOFF: + this.state.note[e.value1 | 0] = 0; + break; + case c.CONTROLLERCHANGE: + this.state.cc[e.getCC()] = e.getCCValue(); + } + }; + p.prototype.sendMIDI = function(e, g) { + g && (e = this.output_ports_info[e]) && (p.output = this, g.constructor === c ? e.send(g.data) : e.send(g)); + }; + m.MIDIInterface = p; + m.title = "MIDI Input"; + m.desc = "Reads MIDI from a input port"; + m.color = "#243"; + m.prototype.getPropertyInfo = function(c) { + if (this._midi && "port" == c) { + c = {}; + for (var e = 0; e < this._midi.input_ports_info.length; ++e) { + var k = this._midi.input_ports_info[e]; + c[e] = e + ".- " + k.name + " version:" + k.version; + } + return {type:"enum", values:c}; + } + }; + m.prototype.onStart = function() { + this._midi ? this._midi.openInputPort(this.properties.port, this.onMIDIEvent.bind(this)) : this._waiting = !0; + }; + m.prototype.onMIDIEvent = function(e, g) { + this._last_midi_event = g; + this.boxcolor = "#AFA"; + this._last_time = t.getTime(); + this.trigger("on_midi", g); + g.cmd == c.NOTEON ? this.trigger("on_noteon", g) : g.cmd == c.NOTEOFF ? this.trigger("on_noteoff", g) : g.cmd == c.CONTROLLERCHANGE ? this.trigger("on_cc", g) : g.cmd == c.PROGRAMCHANGE ? this.trigger("on_pc", g) : g.cmd == c.PITCHBEND && this.trigger("on_pitchbend", g); + }; + m.prototype.onDrawBackground = function(c) { + this.boxcolor = "#AAA"; + if (!this.flags.collapsed && this._last_midi_event) { + c.fillStyle = "white"; + var e = t.getTime(); + e = 1.0 - Math.max(0, 0.001 * (e - this._last_time)); + if (0 < e) { + var k = c.globalAlpha; + c.globalAlpha *= e; + c.font = "12px Tahoma"; + c.fillText(this._last_midi_event.toString(), 2, 0.5 * this.size[1] + 3); + c.globalAlpha = k; + } + } + }; + m.prototype.onExecute = function() { + if (this.outputs) { + for (var c = this._last_midi_event, e = 0; e < this.outputs.length; ++e) { + switch(this.outputs[e].name) { + case "midi": + var k = this._midi; + break; + case "last_midi": + k = c; + break; + default: + continue; + } + this.setOutputData(e, k); + } + } + }; + m.prototype.onGetOutputs = function() { + return [["last_midi", "midi"], ["on_midi", t.EVENT], ["on_noteon", t.EVENT], ["on_noteoff", t.EVENT], ["on_cc", t.EVENT], ["on_pc", t.EVENT], ["on_pitchbend", t.EVENT]]; + }; + t.registerNodeType("midi/input", m); + g.MIDIInterface = p; + g.title = "MIDI Output"; + g.desc = "Sends MIDI to output channel"; + g.color = "#243"; + g.prototype.onGetPropertyInfo = function(c) { + if (this._midi && "port" == c) { + return {type:"enum", values:this.getMIDIOutputs()}; + } + }; + g.default_ports = {0:"unknown"}; + g.prototype.getMIDIOutputs = function() { + var c = {}; + if (!this._midi) { + return g.default_ports; + } + if (this._midi.output_ports_info) { + for (var e = 0; e < this._midi.output_ports_info.length; ++e) { + var k = this._midi.output_ports_info[e]; + k && (c[e] = e + ".- " + k.name + " version:" + k.version); + } + } + return c; + }; + g.prototype.onAction = function(c, e) { + this._midi && ("send" == c && this._midi.sendMIDI(this.properties.port, e), this.trigger("midi", e)); + }; + g.prototype.onGetInputs = function() { + return [["send", t.ACTION]]; + }; + g.prototype.onGetOutputs = function() { + return [["on_midi", t.EVENT]]; + }; + t.registerNodeType("midi/output", g); + u.title = "MIDI Show"; + u.desc = "Shows MIDI in the graph"; + u.color = "#243"; + u.prototype.getTitle = function() { + return this.flags.collapsed ? this._str : this.title; + }; + u.prototype.onAction = function(e, g) { + g && (this._str = g.constructor === c ? g.toString() : "???"); + }; + u.prototype.onDrawForeground = function(c) { + this._str && !this.flags.collapsed && (c.font = "30px Arial", c.fillText(this._str, 10, 0.8 * this.size[1])); + }; + u.prototype.onGetInputs = function() { + return [["in", t.ACTION]]; + }; + u.prototype.onGetOutputs = function() { + return [["on_midi", t.EVENT]]; + }; + t.registerNodeType("midi/show", u); + l.title = "MIDI Filter"; + l.desc = "Filters MIDI messages"; + l.color = "#243"; + l["@cmd"] = {type:"enum", title:"Command", values:c.commands_reversed}; + l.prototype.getTitle = function() { + var e = -1 == this.properties.cmd ? "Nothing" : c.commands_short[this.properties.cmd] || "Unknown"; + -1 != this.properties.min_value && -1 != this.properties.max_value && (e += " " + (this.properties.min_value == this.properties.max_value ? this.properties.max_value : this.properties.min_value + ".." + this.properties.max_value)); + return "Filter: " + e; + }; + l.prototype.onPropertyChanged = function(e, g) { + "cmd" == e && (e = Number(g), isNaN(e) && (e = c.commands[g] || 0), this.properties.cmd = e); + }; + l.prototype.onAction = function(e, g) { + if (g && g.constructor === c) { + if (this._learning) { + this._learning = !1, this.boxcolor = "#AAA", this.properties.channel = g.channel, this.properties.cmd = g.cmd, this.properties.min_value = this.properties.max_value = g.data[1]; + } else { + if (-1 != this.properties.channel && g.channel != this.properties.channel || -1 != this.properties.cmd && g.cmd != this.properties.cmd || -1 != this.properties.min_value && g.data[1] < this.properties.min_value || -1 != this.properties.max_value && g.data[1] > this.properties.max_value) { + return; + } + } + this.trigger("on_midi", g); + } + }; + t.registerNodeType("midi/filter", l); + B.title = "MIDIEvent"; + B.desc = "Create a MIDI Event"; + B.color = "#243"; + B.prototype.onAction = function(e, g) { + "assign" == e ? (this.properties.channel = g.channel, this.properties.cmd = g.cmd, this.properties.value1 = g.data[1], this.properties.value2 = g.data[2], g.cmd == c.NOTEON ? this.gate = !0 : g.cmd == c.NOTEOFF && (this.gate = !1)) : (g = this.midi_event, g.channel = this.properties.channel, this.properties.cmd && this.properties.cmd.constructor === String ? g.setCommandFromString(this.properties.cmd) : g.cmd = this.properties.cmd, g.data[0] = g.cmd | g.channel, g.data[1] = Number(this.properties.value1), + g.data[2] = Number(this.properties.value2), this.trigger("on_midi", g)); + }; + B.prototype.onExecute = function() { + var e = this.properties; + if (this.inputs) { + for (var g = 0; g < this.inputs.length; ++g) { + var k = this.inputs[g]; + if (-1 != k.link) { + switch(k.name) { + case "note": + k = this.getInputData(g); + null != k && (k.constructor === String && (k = c.NoteStringToPitch(k)), this.properties.value1 = (k | 0) % 255); + break; + case "cmd": + k = this.getInputData(g); + null != k && (this.properties.cmd = k); + break; + case "value1": + k = this.getInputData(g); + null != k && (this.properties.value1 = Math.clamp(k | 0, 0, 127)); + break; + case "value2": + k = this.getInputData(g), null != k && (this.properties.value2 = Math.clamp(k | 0, 0, 127)); + } + } + } + } + if (this.outputs) { + for (g = 0; g < this.outputs.length; ++g) { + switch(this.outputs[g].name) { + case "midi": + k = new c; + k.setup([e.cmd, e.value1, e.value2]); + k.channel = e.channel; + break; + case "command": + k = e.cmd; + break; + case "cc": + k = e.value1; + break; + case "cc_value": + k = e.value2; + break; + case "note": + k = e.cmd == c.NOTEON || e.cmd == c.NOTEOFF ? e.value1 : null; + break; + case "velocity": + k = e.cmd == c.NOTEON ? e.value2 : null; + break; + case "pitch": + k = e.cmd == c.NOTEON ? c.computePitch(e.value1) : null; + break; + case "pitchbend": + k = e.cmd == c.PITCHBEND ? c.computePitchBend(e.value1, e.value2) : null; + break; + case "gate": + k = this.gate; + break; + default: + continue; + } + null !== k && this.setOutputData(g, k); + } + } + }; + B.prototype.onPropertyChanged = function(e, g) { + "cmd" == e && (this.properties.cmd = c.computeCommandFromString(g)); + }; + B.prototype.onGetInputs = function() { + return [["cmd", "number"], ["note", "number"], ["value1", "number"], ["value2", "number"]]; + }; + B.prototype.onGetOutputs = function() { + return [["midi", "midi"], ["on_midi", t.EVENT], ["command", "number"], ["note", "number"], ["velocity", "number"], ["cc", "number"], ["cc_value", "number"], ["pitch", "number"], ["gate", "bool"], ["pitchbend", "number"]]; + }; + t.registerNodeType("midi/event", B); + y.title = "MIDICC"; + y.desc = "gets a Controller Change"; + y.color = "#243"; + y.prototype.onExecute = function() { + p.input && (this.properties.value = p.input.state.cc[this.properties.cc]); + this.setOutputData(0, this.properties.value); + }; + t.registerNodeType("midi/cc", y); + v.title = "MIDI Generator"; + v.desc = "Generates a random MIDI note"; + v.color = "#243"; + v.processScale = function(e) { + e = e.split(","); + for (var g = 0; g < e.length; ++g) { + var k = e[g]; + e[g] = 2 == k.length && "#" != k[1] || 2 < k.length ? -t.MIDIEvent.NoteStringToPitch(k) : c.note_to_index[k] || 0; + } + return e; + }; + v.prototype.onPropertyChanged = function(c, e) { + "notes" == c && (this.notes_pitches = v.processScale(e)); + }; + v.prototype.onExecute = function() { + var c = this.getInputData(2); + null != c && (this.properties.octave = c); + if (c = this.getInputData(1)) { + this.notes_pitches = v.processScale(c); + } + }; + v.prototype.onAction = function(e, g) { + var k = 0; + g = this.notes_pitches.length; + e = 0; + "sequence" == this.properties.mode ? e = this.sequence_index = (this.sequence_index + 1) % g : "random" == this.properties.mode && (e = Math.floor(Math.random() * g)); + g = this.notes_pitches[e]; + k = 0 <= g ? g + 12 * (this.properties.octave - 1) + 33 : -g; + g = new c; + g.setup([c.NOTEON, k, 10]); + e = this.properties.duration || 1; + this.trigger("note", g); + setTimeout(function() { + var a = new c; + a.setup([c.NOTEOFF, k, 0]); + this.trigger("note", a); + }.bind(this), 1000 * e); + }; + t.registerNodeType("midi/generator", v); + E.title = "MIDI Transpose"; + E.desc = "Transpose a MIDI note"; + E.color = "#243"; + E.prototype.onAction = function(e, g) { + g && g.constructor === c && (g.data[0] == c.NOTEON || g.data[0] == c.NOTEOFF ? (this.midi_event = new c, this.midi_event.setup(g.data), this.midi_event.data[1] = Math.round(this.midi_event.data[1] + this.properties.amount), this.trigger("out", this.midi_event)) : this.trigger("out", g)); + }; + E.prototype.onExecute = function() { + var c = this.getInputData(1); + null != c && (this.properties.amount = c); + }; + t.registerNodeType("midi/transpose", E); + z.title = "MIDI Quantize Pitch"; + z.desc = "Transpose a MIDI note tp fit an scale"; + z.color = "#243"; + z.prototype.onPropertyChanged = function(c, e) { + "scale" == c && this.processScale(e); + }; + z.prototype.processScale = function(c) { + this._current_scale = c; + this.notes_pitches = v.processScale(c); + for (c = 0; 12 > c; ++c) { + this.valid_notes[c] = -1 != this.notes_pitches.indexOf(c); + } + for (c = 0; 12 > c; ++c) { + if (this.valid_notes[c]) { + this.offset_notes[c] = 0; + } else { + for (var e = 1; 12 > e; ++e) { + if (this.valid_notes[(c - e) % 12]) { + this.offset_notes[c] = -e; + break; + } + if (this.valid_notes[(c + e) % 12]) { + this.offset_notes[c] = e; + break; + } + } + } + } + }; + z.prototype.onAction = function(e, g) { + g && g.constructor === c && (g.data[0] == c.NOTEON || g.data[0] == c.NOTEOFF ? (this.midi_event = new c, this.midi_event.setup(g.data), this.midi_event.data[1] += this.offset_notes[c.note_to_index[g.note]], this.trigger("out", this.midi_event)) : this.trigger("out", g)); + }; + z.prototype.onExecute = function() { + var c = this.getInputData(1); + null != c && c != this._current_scale && this.processScale(c); + }; + t.registerNodeType("midi/quantize", z); + e.title = "MIDI fromFile"; + e.desc = "Plays a MIDI file"; + e.color = "#243"; + e.prototype.onAction = function(c) { + "play" == c ? this.play() : "pause" == c && (this._playing = !this._playing); + }; + e.prototype.onPropertyChanged = function(c, e) { + "url" == c && this.loadMIDIFile(e); + }; + e.prototype.onExecute = function() { + if (this._midi && this._playing) { + this._current_time += this.graph.elapsed_time; + for (var e = 100 * this._current_time, g = 0; g < this._midi.tracks; ++g) { + var k = this._midi.track[g]; + k._last_pos || (k._last_pos = 0, k._time = 0); + var a = k.event[k._last_pos]; + if (a && k._time + a.deltaTime <= e && (k._last_pos++, k._time += a.deltaTime, a.data)) { + k = a.type << 4 + a.channel; + var b = new c; + b.setup([k, a.data[0], a.data[1]]); + this.trigger("note", b); + } + } + } + }; + e.prototype.play = function() { + this._playing = !0; + for (var c = this._current_time = 0; c < this._midi.tracks; ++c) { + var e = this._midi.track[c]; + e._last_pos = 0; + e._time = 0; + } + }; + e.prototype.loadMIDIFile = function(c) { + var e = this; + t.fetchFile(c, "arraybuffer", function(c) { + e.boxcolor = "#AFA"; + e._midi = MidiParser.parse(new Uint8Array(c)); + e.properties.autoplay && e.play(); + }, function(c) { + e.boxcolor = "#FAA"; + e._midi = null; + }); + }; + e.prototype.onDropFile = function(c) { + this.properties.url = ""; + this.loadMIDIFile(c); + }; + t.registerNodeType("midi/fromFile", e); + C.title = "MIDI Play"; + C.desc = "Plays a MIDI note"; + C.color = "#243"; + C.prototype.onAction = function(e, g) { + if (g && g.constructor === c) { + if (this.instrument && g.data[0] == c.NOTEON) { + e = g.note; + if (!e || "undefined" == e || e.constructor !== String) { + return; + } + this.instrument.play(e, g.octave, this.properties.duration, this.properties.volume); + } + this.trigger("note", g); + } + }; + C.prototype.onExecute = function() { + var c = this.getInputData(1); + null != c && (this.properties.volume = c); + c = this.getInputData(2); + null != c && (this.properties.duration = c); + }; + t.registerNodeType("midi/play", C); + D.title = "MIDI Keys"; + D.desc = "Keyboard to play notes"; + D.color = "#243"; + D.keys = [{x:0, w:1, h:1, t:0}, {x:0.75, w:0.5, h:0.6, t:1}, {x:1, w:1, h:1, t:0}, {x:1.75, w:0.5, h:0.6, t:1}, {x:2, w:1, h:1, t:0}, {x:2.75, w:0.5, h:0.6, t:1}, {x:3, w:1, h:1, t:0}, {x:4, w:1, h:1, t:0}, {x:4.75, w:0.5, h:0.6, t:1}, {x:5, w:1, h:1, t:0}, {x:5.75, w:0.5, h:0.6, t:1}, {x:6, w:1, h:1, t:0}]; + D.prototype.onDrawForeground = function(c) { + if (!this.flags.collapsed) { + var e = 12 * this.properties.num_octaves; + this.keys.length = e; + var k = this.size[0] / (7 * this.properties.num_octaves), a = this.size[1]; + c.globalAlpha = 1; + for (var b = 0; 2 > b; b++) { + for (var d = 0; d < e; ++d) { + var h = D.keys[d % 12]; + if (h.t == b) { + var f = 7 * Math.floor(d / 12) * k + h.x * k; + c.fillStyle = 0 == b ? this.keys[d] ? "#CCC" : "white" : this.keys[d] ? "#333" : "black"; + c.fillRect(f + 1, 0, k * h.w - 2, a * h.h); + } + } + } + } + }; + D.prototype.getKeyIndex = function(c) { + for (var e = this.size[0] / (7 * this.properties.num_octaves), k = this.size[1], a = 1; 0 <= a; a--) { + for (var b = 0; b < this.keys.length; ++b) { + var d = D.keys[b % 12]; + if (d.t == a) { + var h = 7 * Math.floor(b / 12) * e + d.x * e, f = e * d.w; + d = k * d.h; + if (!(c[0] < h || c[0] > h + f || c[1] > d)) { + return b; + } + } + } + } + return -1; + }; + D.prototype.onAction = function(e, g) { + if ("reset" == e) { + for (g = 0; g < this.keys.length; ++g) { + this.keys[g] = !1; + } + } else { + g && g.constructor === c && (e = g.data[1] - (12 * (this.properties.start_octave - 1) + 29), 0 <= e && e < this.keys.length && (g.data[0] == c.NOTEON ? this.keys[e] = !0 : g.data[0] == c.NOTEOFF && (this.keys[e] = !1)), this.trigger("note", g)); + } + }; + D.prototype.onMouseDown = function(e, g) { + if (!(0 > g[1])) { + return e = this.getKeyIndex(g), this.keys[e] = !0, this._last_key = e, e = 12 * (this.properties.start_octave - 1) + 29 + e, g = new c, g.setup([c.NOTEON, e, 100]), this.trigger("note", g), !0; + } + }; + D.prototype.onMouseMove = function(e, g) { + if (!(0 > g[1] || -1 == this._last_key)) { + this.setDirtyCanvas(!0); + e = this.getKeyIndex(g); + if (this._last_key == e) { + return !0; + } + this.keys[this._last_key] = !1; + g = 12 * (this.properties.start_octave - 1) + 29 + this._last_key; + var k = new c; + k.setup([c.NOTEOFF, g, 100]); + this.trigger("note", k); + this.keys[e] = !0; + g = 12 * (this.properties.start_octave - 1) + 29 + e; + k = new c; + k.setup([c.NOTEON, g, 100]); + this.trigger("note", k); + this._last_key = e; + return !0; + } + }; + D.prototype.onMouseUp = function(e, g) { + if (!(0 > g[1])) { + return e = this.getKeyIndex(g), this.keys[e] = !1, this._last_key = -1, e = 12 * (this.properties.start_octave - 1) + 29 + e, g = new c, g.setup([c.NOTEOFF, e, 100]), this.trigger("note", g), !0; + } + }; + t.registerNodeType("midi/keys", D); +})(this); +(function(w) { + function c() { + this.properties = {src:"", gain:0.5, loop:!0, autoplay:!0, playbackRate:1}; + this._loading_audio = !1; + this._audiobuffer = null; + this._audionodes = []; + this._last_sourcenode = null; + this.addOutput("out", "audio"); + this.addInput("gain", "number"); + this.audionode = q.getAudioContext().createGain(); + this.audionode.graphnode = this; + this.audionode.gain.value = this.properties.gain; + this.properties.src && this.loadSound(this.properties.src); + } + function p() { + this.properties = {gain:0.5}; + this._audionodes = []; + this._media_stream = null; + this.addOutput("out", "audio"); + this.addInput("gain", "number"); + this.audionode = q.getAudioContext().createGain(); + this.audionode.graphnode = this; + this.audionode.gain.value = this.properties.gain; + } + function m() { + this.properties = {fftSize:2048, minDecibels:-100, maxDecibels:-10, smoothingTimeConstant:0.5}; + this.audionode = q.getAudioContext().createAnalyser(); + this.audionode.graphnode = this; + this.audionode.fftSize = this.properties.fftSize; + this.audionode.minDecibels = this.properties.minDecibels; + this.audionode.maxDecibels = this.properties.maxDecibels; + this.audionode.smoothingTimeConstant = this.properties.smoothingTimeConstant; + this.addInput("in", "audio"); + this.addOutput("freqs", "array"); + this.addOutput("samples", "array"); + this._time_bin = this._freq_bin = null; + } + function g() { + this.properties = {gain:1}; + this.audionode = q.getAudioContext().createGain(); + this.addInput("in", "audio"); + this.addInput("gain", "number"); + this.addOutput("out", "audio"); + } + function u() { + this.properties = {impulse_src:"", normalize:!0}; + this.audionode = q.getAudioContext().createConvolver(); + this.addInput("in", "audio"); + this.addOutput("out", "audio"); + } + function l() { + this.properties = {threshold:-50, knee:40, ratio:12, reduction:-20, attack:0, release:0.25}; + this.audionode = q.getAudioContext().createDynamicsCompressor(); + this.addInput("in", "audio"); + this.addOutput("out", "audio"); + } + function B() { + this.properties = {}; + this.audionode = q.getAudioContext().createWaveShaper(); + this.addInput("in", "audio"); + this.addInput("shape", "waveshape"); + this.addOutput("out", "audio"); + } + function y() { + this.properties = {gain1:0.5, gain2:0.5}; + this.audionode = q.getAudioContext().createGain(); + this.audionode1 = q.getAudioContext().createGain(); + this.audionode1.gain.value = this.properties.gain1; + this.audionode2 = q.getAudioContext().createGain(); + this.audionode2.gain.value = this.properties.gain2; + this.audionode1.connect(this.audionode); + this.audionode2.connect(this.audionode); + this.addInput("in1", "audio"); + this.addInput("in1 gain", "number"); + this.addInput("in2", "audio"); + this.addInput("in2 gain", "number"); + this.addOutput("out", "audio"); + } + function v() { + this.properties = {A:0.1, D:0.1, S:0.1, R:0.1}; + this.audionode = q.getAudioContext().createGain(); + this.audionode.gain.value = 0; + this.addInput("in", "audio"); + this.addInput("gate", "bool"); + this.addOutput("out", "audio"); + this.gate = !1; + } + function E() { + this.properties = {delayTime:0.5}; + this.audionode = q.getAudioContext().createDelay(10); + this.audionode.delayTime.value = this.properties.delayTime; + this.addInput("in", "audio"); + this.addInput("time", "number"); + this.addOutput("out", "audio"); + } + function z() { + this.properties = {frequency:350, detune:0, Q:1}; + this.addProperty("type", "lowpass", "enum", {values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")}); + this.audionode = q.getAudioContext().createBiquadFilter(); + this.addInput("in", "audio"); + this.addOutput("out", "audio"); + } + function e() { + this.properties = {frequency:440, detune:0, type:"sine"}; + this.addProperty("type", "sine", "enum", {values:["sine", "square", "sawtooth", "triangle", "custom"]}); + this.audionode = q.getAudioContext().createOscillator(); + this.addOutput("out", "audio"); + } + function C() { + this.properties = {continuous:!0, mark:-1}; + this.addInput("data", "array"); + this.addInput("mark", "number"); + this.size = [300, 200]; + this._last_buffer = null; + } + function D() { + this.properties = {band:440, amplitude:1}; + this.addInput("freqs", "array"); + this.addOutput("signal", "number"); + } + function t() { + if (!t.default_code) { + var c = t.default_function.toString(), a = c.indexOf("{") + 1, b = c.lastIndexOf("}"); + t.default_code = c.substr(a, b - a); + } + this.properties = {code:t.default_code}; + c = q.getAudioContext(); + c.createScriptProcessor ? this.audionode = c.createScriptProcessor(4096, 1, 1) : (console.warn("ScriptProcessorNode deprecated"), this.audionode = c.createGain()); + this.processCode(); + t._bypass_function || (t._bypass_function = this.audionode.onaudioprocess); + this.addInput("in", "audio"); + this.addOutput("out", "audio"); + } + function G() { + this.audionode = q.getAudioContext().destination; + this.addInput("in", "audio"); + } + var n = w.LiteGraph, q = {}; + w.LGAudio = q; + q.getAudioContext = function() { + if (!this._audio_context) { + window.AudioContext = window.AudioContext || window.webkitAudioContext; + if (!window.AudioContext) { + return console.error("AudioContext not supported by browser"), null; + } + this._audio_context = new AudioContext; + this._audio_context.onmessage = function(c) { + console.log("msg", c); + }; + this._audio_context.onended = function(c) { + console.log("ended", c); + }; + this._audio_context.oncomplete = function(c) { + console.log("complete", c); + }; + } + return this._audio_context; + }; + q.connect = function(c, a) { + try { + c.connect(a); + } catch (b) { + console.warn("LGraphAudio:", b); + } + }; + q.disconnect = function(c, a) { + try { + c.disconnect(a); + } catch (b) { + console.warn("LGraphAudio:", b); + } + }; + q.changeAllAudiosConnections = function(c, a) { + if (c.inputs) { + for (var b = 0; b < c.inputs.length; ++b) { + var d = c.graph.links[c.inputs[b].link]; + if (d) { + var e = c.graph.getNodeById(d.origin_id); + e = e.getAudioNodeInOutputSlot ? e.getAudioNodeInOutputSlot(d.origin_slot) : e.audionode; + d = c.getAudioNodeInInputSlot ? c.getAudioNodeInInputSlot(b) : c.audionode; + a ? q.connect(e, d) : q.disconnect(e, d); + } + } + } + if (c.outputs) { + for (b = 0; b < c.outputs.length; ++b) { + for (var k = c.outputs[b], g = 0; g < k.links.length; ++g) { + if (d = c.graph.links[k.links[g]]) { + e = c.getAudioNodeInOutputSlot ? c.getAudioNodeInOutputSlot(b) : c.audionode; + var l = c.graph.getNodeById(d.target_id); + d = l.getAudioNodeInInputSlot ? l.getAudioNodeInInputSlot(d.target_slot) : l.audionode; + a ? q.connect(e, d) : q.disconnect(e, d); + } + } + } + } + }; + q.onConnectionsChange = function(c, a, b, d) { + c == n.OUTPUT && (c = null, d && (c = this.graph.getNodeById(d.target_id)), c && (a = this.getAudioNodeInOutputSlot ? this.getAudioNodeInOutputSlot(a) : this.audionode, d = c.getAudioNodeInInputSlot ? c.getAudioNodeInInputSlot(d.target_slot) : c.audionode, b ? q.connect(a, d) : q.disconnect(a, d))); + }; + q.createAudioNodeWrapper = function(c) { + var a = c.prototype.onPropertyChanged; + c.prototype.onPropertyChanged = function(b, d) { + a && a.call(this, b, d); + this.audionode && void 0 !== this.audionode[b] && (void 0 !== this.audionode[b].value ? this.audionode[b].value = d : this.audionode[b] = d); + }; + c.prototype.onConnectionsChange = q.onConnectionsChange; + }; + q.cached_audios = {}; + q.loadSound = function(c, a, b) { + function d(a) { + console.log("Audio loading sample error:", a); + b && b(a); + } + if (q.cached_audios[c] && -1 == c.indexOf("blob:")) { + a && a(q.cached_audios[c]); + } else { + q.onProcessAudioURL && (c = q.onProcessAudioURL(c)); + var e = new XMLHttpRequest; + e.open("GET", c, !0); + e.responseType = "arraybuffer"; + var k = q.getAudioContext(); + e.onload = function() { + console.log("AudioSource loaded"); + k.decodeAudioData(e.response, function(b) { + console.log("AudioSource decoded"); + q.cached_audios[c] = b; + a && a(b); + }, d); + }; + e.send(); + return e; + } + }; + c.desc = "Plays an audio file"; + c["@src"] = {widget:"resource"}; + c.supported_extensions = ["wav", "ogg", "mp3"]; + c.prototype.onAdded = function(c) { + if (c.status === LGraph.STATUS_RUNNING) { + this.onStart(); + } + }; + c.prototype.onStart = function() { + this._audiobuffer && this.properties.autoplay && this.playBuffer(this._audiobuffer); + }; + c.prototype.onStop = function() { + this.stopAllSounds(); + }; + c.prototype.onPause = function() { + this.pauseAllSounds(); + }; + c.prototype.onUnpause = function() { + this.unpauseAllSounds(); + }; + c.prototype.onRemoved = function() { + this.stopAllSounds(); + this._dropped_url && URL.revokeObjectURL(this._url); + }; + c.prototype.stopAllSounds = function() { + for (var c = 0; c < this._audionodes.length; ++c) { + this._audionodes[c].started && (this._audionodes[c].started = !1, this._audionodes[c].stop()); + } + this._audionodes.length = 0; + }; + c.prototype.pauseAllSounds = function() { + q.getAudioContext().suspend(); + }; + c.prototype.unpauseAllSounds = function() { + q.getAudioContext().resume(); + }; + c.prototype.onExecute = function() { + if (this.inputs) { + for (var c = 0; c < this.inputs.length; ++c) { + var a = this.inputs[c]; + if (null != a.link) { + var b = this.getInputData(c); + if (void 0 !== b) { + if ("gain" == a.name) { + this.audionode.gain.value = b; + } else { + if ("src" == a.name) { + this.setProperty("src", b); + } else { + if ("playbackRate" == a.name) { + for (this.properties.playbackRate = b, a = 0; a < this._audionodes.length; ++a) { + this._audionodes[a].playbackRate.value = b; + } + } + } + } + } + } + } + } + if (this.outputs) { + for (c = 0; c < this.outputs.length; ++c) { + "buffer" == this.outputs[c].name && this._audiobuffer && this.setOutputData(c, this._audiobuffer); + } + } + }; + c.prototype.onAction = function(c) { + this._audiobuffer && ("Play" == c ? this.playBuffer(this._audiobuffer) : "Stop" == c && this.stopAllSounds()); + }; + c.prototype.onPropertyChanged = function(c, a) { + if ("src" == c) { + this.loadSound(a); + } else { + if ("gain" == c) { + this.audionode.gain.value = a; + } else { + if ("playbackRate" == c) { + for (c = 0; c < this._audionodes.length; ++c) { + this._audionodes[c].playbackRate.value = a; + } + } + } + } + }; + c.prototype.playBuffer = function(c) { + var a = this, b = q.getAudioContext().createBufferSource(); + this._last_sourcenode = b; + b.graphnode = this; + b.buffer = c; + b.loop = this.properties.loop; + b.playbackRate.value = this.properties.playbackRate; + this._audionodes.push(b); + b.connect(this.audionode); + this._audionodes.push(b); + this.trigger("start"); + b.onended = function() { + a.trigger("ended"); + var c = a._audionodes.indexOf(b); + -1 != c && a._audionodes.splice(c, 1); + }; + b.started || (b.started = !0, b.start()); + return b; + }; + c.prototype.loadSound = function(c) { + var a = this; + this._request && (this._request.abort(), this._request = null); + this._audiobuffer = null; + this._loading_audio = !1; + c && (this._request = q.loadSound(c, function(b) { + this.boxcolor = n.NODE_DEFAULT_BOXCOLOR; + a._audiobuffer = b; + a._loading_audio = !1; + if (a.graph && a.graph.status === LGraph.STATUS_RUNNING) { + a.onStart(); + } + }), this._loading_audio = !0, this.boxcolor = "#AA4"); + }; + c.prototype.onConnectionsChange = q.onConnectionsChange; + c.prototype.onGetInputs = function() { + return [["playbackRate", "number"], ["src", "string"], ["Play", n.ACTION], ["Stop", n.ACTION]]; + }; + c.prototype.onGetOutputs = function() { + return [["buffer", "audiobuffer"], ["start", n.EVENT], ["ended", n.EVENT]]; + }; + c.prototype.onDropFile = function(c) { + this._dropped_url && URL.revokeObjectURL(this._dropped_url); + c = URL.createObjectURL(c); + this.properties.src = c; + this.loadSound(c); + this._dropped_url = c; + }; + c.title = "Source"; + c.desc = "Plays audio"; + n.registerNodeType("audio/source", c); + p.prototype.onAdded = function(c) { + if (c.status === LGraph.STATUS_RUNNING) { + this.onStart(); + } + }; + p.prototype.onStart = function() { + null != this._media_stream || this._waiting_confirmation || this.openStream(); + }; + p.prototype.onStop = function() { + this.audionode.gain.value = 0; + }; + p.prototype.onPause = function() { + this.audionode.gain.value = 0; + }; + p.prototype.onUnpause = function() { + this.audionode.gain.value = this.properties.gain; + }; + p.prototype.onRemoved = function() { + this.audionode.gain.value = 0; + this.audiosource_node && (this.audiosource_node.disconnect(this.audionode), this.audiosource_node = null); + if (this._media_stream) { + var c = this._media_stream.getTracks(); + c.length && c[0].stop(); + } + }; + p.prototype.openStream = function() { + if (navigator.mediaDevices) { + this._waiting_confirmation = !0; + navigator.mediaDevices.getUserMedia({audio:!0, video:!1}).then(this.streamReady.bind(this)).catch(function(a) { + console.log("Media rejected", a); + c._media_stream = !1; + c.boxcolor = "red"; + }); + var c = this; + } else { + console.log("getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags"); + } + }; + p.prototype.streamReady = function(c) { + this._media_stream = c; + this.audiosource_node && this.audiosource_node.disconnect(this.audionode); + this.audiosource_node = q.getAudioContext().createMediaStreamSource(c); + this.audiosource_node.graphnode = this; + this.audiosource_node.connect(this.audionode); + this.boxcolor = "white"; + }; + p.prototype.onExecute = function() { + null != this._media_stream || this._waiting_confirmation || this.openStream(); + if (this.inputs) { + for (var c = 0; c < this.inputs.length; ++c) { + var a = this.inputs[c]; + if (null != a.link) { + var b = this.getInputData(c); + void 0 !== b && "gain" == a.name && (this.audionode.gain.value = this.properties.gain = b); + } + } + } + }; + p.prototype.onAction = function(c) { + "Play" == c ? this.audionode.gain.value = this.properties.gain : "Stop" == c && (this.audionode.gain.value = 0); + }; + p.prototype.onPropertyChanged = function(c, a) { + "gain" == c && (this.audionode.gain.value = a); + }; + p.prototype.onConnectionsChange = q.onConnectionsChange; + p.prototype.onGetInputs = function() { + return [["playbackRate", "number"], ["Play", n.ACTION], ["Stop", n.ACTION]]; + }; + p.title = "MediaSource"; + p.desc = "Plays microphone"; + n.registerNodeType("audio/media_source", p); + m.prototype.onPropertyChanged = function(c, a) { + this.audionode[c] = a; + }; + m.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var c = this.audionode.frequencyBinCount; + this._freq_bin && this._freq_bin.length == c || (this._freq_bin = new Uint8Array(c)); + this.audionode.getByteFrequencyData(this._freq_bin); + this.setOutputData(0, this._freq_bin); + } + this.isOutputConnected(1) && (c = this.audionode.frequencyBinCount, this._time_bin && this._time_bin.length == c || (this._time_bin = new Uint8Array(c)), this.audionode.getByteTimeDomainData(this._time_bin), this.setOutputData(1, this._time_bin)); + for (c = 1; c < this.inputs.length; ++c) { + var a = this.inputs[c]; + if (null != a.link) { + var b = this.getInputData(c); + void 0 !== b && (this.audionode[a.name].value = b); + } + } + }; + m.prototype.onGetInputs = function() { + return [["minDecibels", "number"], ["maxDecibels", "number"], ["smoothingTimeConstant", "number"]]; + }; + m.prototype.onGetOutputs = function() { + return [["freqs", "array"], ["samples", "array"]]; + }; + m.title = "Analyser"; + m.desc = "Audio Analyser"; + n.registerNodeType("audio/analyser", m); + g.prototype.onExecute = function() { + if (this.inputs && this.inputs.length) { + for (var c = 1; c < this.inputs.length; ++c) { + var a = this.inputs[c], b = this.getInputData(c); + void 0 !== b && (this.audionode[a.name].value = b); + } + } + }; + q.createAudioNodeWrapper(g); + g.title = "Gain"; + g.desc = "Audio gain"; + n.registerNodeType("audio/gain", g); + q.createAudioNodeWrapper(u); + u.prototype.onRemove = function() { + this._dropped_url && URL.revokeObjectURL(this._dropped_url); + }; + u.prototype.onPropertyChanged = function(c, a) { + "impulse_src" == c ? this.loadImpulse(a) : "normalize" == c && (this.audionode.normalize = a); + }; + u.prototype.onDropFile = function(c) { + this._dropped_url && URL.revokeObjectURL(this._dropped_url); + this._dropped_url = URL.createObjectURL(c); + this.properties.impulse_src = this._dropped_url; + this.loadImpulse(this._dropped_url); + }; + u.prototype.loadImpulse = function(c) { + var a = this; + this._request && (this._request.abort(), this._request = null); + this._impulse_buffer = null; + this._loading_impulse = !1; + c && (this._request = q.loadSound(c, function(b) { + a._impulse_buffer = b; + a.audionode.buffer = b; + console.log("Impulse signal set"); + a._loading_impulse = !1; + }), this._loading_impulse = !0); + }; + u.title = "Convolver"; + u.desc = "Convolves the signal (used for reverb)"; + n.registerNodeType("audio/convolver", u); + q.createAudioNodeWrapper(l); + l.prototype.onExecute = function() { + if (this.inputs && this.inputs.length) { + for (var c = 1; c < this.inputs.length; ++c) { + var a = this.inputs[c]; + if (null != a.link) { + var b = this.getInputData(c); + void 0 !== b && (this.audionode[a.name].value = b); + } + } + } + }; + l.prototype.onGetInputs = function() { + return [["threshold", "number"], ["knee", "number"], ["ratio", "number"], ["reduction", "number"], ["attack", "number"], ["release", "number"]]; + }; + l.title = "DynamicsCompressor"; + l.desc = "Dynamics Compressor"; + n.registerNodeType("audio/dynamicsCompressor", l); + B.prototype.onExecute = function() { + if (this.inputs && this.inputs.length) { + var c = this.getInputData(1); + void 0 !== c && (this.audionode.curve = c); + } + }; + B.prototype.setWaveShape = function(c) { + this.audionode.curve = c; + }; + q.createAudioNodeWrapper(B); + y.prototype.getAudioNodeInInputSlot = function(c) { + if (0 == c) { + return this.audionode1; + } + if (2 == c) { + return this.audionode2; + } + }; + y.prototype.onPropertyChanged = function(c, a) { + "gain1" == c ? this.audionode1.gain.value = a : "gain2" == c && (this.audionode2.gain.value = a); + }; + y.prototype.onExecute = function() { + if (this.inputs && this.inputs.length) { + for (var c = 1; c < this.inputs.length; ++c) { + var a = this.inputs[c]; + null != a.link && "audio" != a.type && (a = this.getInputData(c), void 0 !== a && (1 == c ? this.audionode1.gain.value = a : 3 == c && (this.audionode2.gain.value = a))); + } + } + }; + q.createAudioNodeWrapper(y); + y.title = "Mixer"; + y.desc = "Audio mixer"; + n.registerNodeType("audio/mixer", y); + v.prototype.onExecute = function() { + var c = q.getAudioContext().currentTime, a = this.audionode.gain, b = this.getInputData(1), d = this.getInputOrProperty("A"), e = this.getInputOrProperty("D"), f = this.getInputOrProperty("S"), g = this.getInputOrProperty("R"); + !this.gate && b ? (a.cancelScheduledValues(0), a.setValueAtTime(0, c), a.linearRampToValueAtTime(1, c + d), a.linearRampToValueAtTime(f, c + d + e)) : this.gate && !b && (a.cancelScheduledValues(0), a.setValueAtTime(a.value, c), a.linearRampToValueAtTime(0, c + g)); + this.gate = b; + }; + v.prototype.onGetInputs = function() { + return [["A", "number"], ["D", "number"], ["S", "number"], ["R", "number"]]; + }; + q.createAudioNodeWrapper(v); + v.title = "ADSR"; + v.desc = "Audio envelope"; + n.registerNodeType("audio/adsr", v); + q.createAudioNodeWrapper(E); + E.prototype.onExecute = function() { + var c = this.getInputData(1); + void 0 !== c && (this.audionode.delayTime.value = c); + }; + E.title = "Delay"; + E.desc = "Audio delay"; + n.registerNodeType("audio/delay", E); + z.prototype.onExecute = function() { + if (this.inputs && this.inputs.length) { + for (var c = 1; c < this.inputs.length; ++c) { + var a = this.inputs[c]; + if (null != a.link) { + var b = this.getInputData(c); + void 0 !== b && (this.audionode[a.name].value = b); + } + } + } + }; + z.prototype.onGetInputs = function() { + return [["frequency", "number"], ["detune", "number"], ["Q", "number"]]; + }; + q.createAudioNodeWrapper(z); + z.title = "BiquadFilter"; + z.desc = "Audio filter"; + n.registerNodeType("audio/biquadfilter", z); + e.prototype.onStart = function() { + if (!this.audionode.started) { + this.audionode.started = !0; + try { + this.audionode.start(); + } catch (k) { + } + } + }; + e.prototype.onStop = function() { + this.audionode.started && (this.audionode.started = !1, this.audionode.stop()); + }; + e.prototype.onPause = function() { + this.onStop(); + }; + e.prototype.onUnpause = function() { + this.onStart(); + }; + e.prototype.onExecute = function() { + if (this.inputs && this.inputs.length) { + for (var c = 0; c < this.inputs.length; ++c) { + var a = this.inputs[c]; + if (null != a.link) { + var b = this.getInputData(c); + void 0 !== b && (this.audionode[a.name].value = b); + } + } + } + }; + e.prototype.onGetInputs = function() { + return [["frequency", "number"], ["detune", "number"], ["type", "string"]]; + }; + q.createAudioNodeWrapper(e); + e.title = "Oscillator"; + e.desc = "Oscillator"; + n.registerNodeType("audio/oscillator", e); + C.prototype.onExecute = function() { + this._last_buffer = this.getInputData(0); + var c = this.getInputData(1); + void 0 !== c && (this.properties.mark = c); + this.setDirtyCanvas(!0, !1); + }; + C.prototype.onDrawForeground = function(c) { + if (this._last_buffer) { + var a = this._last_buffer, b = a.length / this.size[0], d = this.size[1]; + c.fillStyle = "black"; + c.fillRect(0, 0, this.size[0], this.size[1]); + c.strokeStyle = "white"; + c.beginPath(); + var e = 0; + if (this.properties.continuous) { + c.moveTo(e, d); + for (var f = 0; f < a.length; f += b) { + c.lineTo(e, d - a[f | 0] / 255 * d), e++; + } + } else { + for (f = 0; f < a.length; f += b) { + c.moveTo(e + 0.5, d), c.lineTo(e + 0.5, d - a[f | 0] / 255 * d), e++; + } + } + c.stroke(); + 0 <= this.properties.mark && (a = q.getAudioContext().sampleRate / a.length, e = this.properties.mark / a * 2 / b, e >= this.size[0] && (e = this.size[0] - 1), c.strokeStyle = "red", c.beginPath(), c.moveTo(e, d), c.lineTo(e, 0), c.stroke()); + } + }; + C.title = "Visualization"; + C.desc = "Audio Visualization"; + n.registerNodeType("audio/visualization", C); + D.prototype.onExecute = function() { + if (this._freqs = this.getInputData(0)) { + var c = this.properties.band, a = this.getInputData(1); + void 0 !== a && (c = a); + a = q.getAudioContext().sampleRate / this._freqs.length; + a = c / a * 2; + a >= this._freqs.length ? a = this._freqs[this._freqs.length - 1] : (c = a | 0, a -= c, a = this._freqs[c] * (1 - a) + this._freqs[c + 1] * a); + this.setOutputData(0, a / 255 * this.properties.amplitude); + } + }; + D.prototype.onGetInputs = function() { + return [["band", "number"]]; + }; + D.title = "Signal"; + D.desc = "extract the signal of some frequency"; + n.registerNodeType("audio/signal", D); + t.prototype.onAdded = function(c) { + c.status == LGraph.STATUS_RUNNING && (this.audionode.onaudioprocess = this._callback); + }; + t["@code"] = {widget:"code", type:"code"}; + t.prototype.onStart = function() { + this.audionode.onaudioprocess = this._callback; + }; + t.prototype.onStop = function() { + this.audionode.onaudioprocess = t._bypass_function; + }; + t.prototype.onPause = function() { + this.audionode.onaudioprocess = t._bypass_function; + }; + t.prototype.onUnpause = function() { + this.audionode.onaudioprocess = this._callback; + }; + t.prototype.onExecute = function() { + }; + t.prototype.onRemoved = function() { + this.audionode.onaudioprocess = t._bypass_function; + }; + t.prototype.processCode = function() { + try { + this._script = new (new Function("properties", this.properties.code))(this.properties), this._old_code = this.properties.code, this._callback = this._script.onaudioprocess; + } catch (k) { + console.error("Error in onaudioprocess code", k), this._callback = t._bypass_function, this.audionode.onaudioprocess = this._callback; + } + }; + t.prototype.onPropertyChanged = function(c, a) { + "code" == c && (this.properties.code = a, this.processCode(), this.graph && this.graph.status == LGraph.STATUS_RUNNING && (this.audionode.onaudioprocess = this._callback)); + }; + t.default_function = function() { + this.onaudioprocess = function(c) { + var a = c.inputBuffer; + c = c.outputBuffer; + for (var b = 0; b < c.numberOfChannels; b++) { + for (var d = a.getChannelData(b), e = c.getChannelData(b), f = 0; f < a.length; f++) { + e[f] = d[f]; + } + } + }; + }; + q.createAudioNodeWrapper(t); + t.title = "Script"; + t.desc = "apply script to signal"; + n.registerNodeType("audio/script", t); + G.title = "Destination"; + G.desc = "Audio output"; + n.registerNodeType("audio/destination", G); +})(this); +(function(w) { + function c() { + this.size = [60, 20]; + this.addInput("send", m.ACTION); + this.addOutput("received", m.EVENT); + this.addInput("in", 0); + this.addOutput("out", 0); + this.properties = {url:"", room:"lgraph", only_send_changes:!0}; + this._ws = null; + this._last_sent_data = []; + this._last_received_data = []; + } + function p() { + this.room_widget = this.addWidget("text", "Room", "lgraph", this.setRoom.bind(this)); + this.addWidget("button", "Reconnect", null, this.connectSocket.bind(this)); + this.addInput("send", m.ACTION); + this.addOutput("received", m.EVENT); + this.addInput("in", 0); + this.addOutput("out", 0); + this.properties = {url:"tamats.com:55000", room:"lgraph", only_send_changes:!0}; + this._server = null; + this.connectSocket(); + this._last_sent_data = []; + this._last_received_data = []; + "undefined" == typeof SillyClient && console.warn("remember to add SillyClient.js to your project: https://tamats.com/projects/sillyserver/src/sillyclient.js"); + } + var m = w.LiteGraph; + c.title = "WebSocket"; + c.desc = "Send data through a websocket"; + c.prototype.onPropertyChanged = function(c, m) { + "url" == c && this.connectSocket(); + }; + c.prototype.onExecute = function() { + !this._ws && this.properties.url && this.connectSocket(); + if (this._ws && this._ws.readyState == WebSocket.OPEN) { + for (var c = this.properties.room, m = this.properties.only_send_changes, l = 1; l < this.inputs.length; ++l) { + var p = this.getInputData(l); + if (null != p) { + try { + var w = JSON.stringify({type:0, room:c, channel:l, data:p}); + } catch (v) { + continue; + } + m && this._last_sent_data[l] == w || (this._last_sent_data[l] = w, this._ws.send(w)); + } + } + for (l = 1; l < this.outputs.length; ++l) { + this.setOutputData(l, this._last_received_data[l]); + } + "#AFA" == this.boxcolor && (this.boxcolor = "#6C6"); + } + }; + c.prototype.connectSocket = function() { + var c = this, p = this.properties.url; + "ws" != p.substr(0, 2) && (p = "ws://" + p); + this._ws = new WebSocket(p); + this._ws.onopen = function() { + console.log("ready"); + c.boxcolor = "#6C6"; + }; + this._ws.onmessage = function(g) { + c.boxcolor = "#AFA"; + g = JSON.parse(g.data); + if (!g.room || g.room == c.properties.room) { + if (1 == g.type) { + if (g.data.object_class && m[g.data.object_class]) { + var l = null; + try { + l = new m[g.data.object_class](g.data), c.triggerSlot(0, l); + } catch (y) { + } + } else { + c.triggerSlot(0, g.data); + } + } else { + c._last_received_data[g.channel || 0] = g.data; + } + } + }; + this._ws.onerror = function(g) { + console.log("couldnt connect to websocket"); + c.boxcolor = "#E88"; + }; + this._ws.onclose = function(g) { + console.log("connection closed"); + c.boxcolor = "#000"; + }; + }; + c.prototype.send = function(c) { + this._ws && this._ws.readyState == WebSocket.OPEN && this._ws.send(JSON.stringify({type:1, msg:c})); + }; + c.prototype.onAction = function(c, m) { + this._ws && this._ws.readyState == WebSocket.OPEN && this._ws.send({type:1, room:this.properties.room, action:c, data:m}); + }; + c.prototype.onGetInputs = function() { + return [["in", 0]]; + }; + c.prototype.onGetOutputs = function() { + return [["out", 0]]; + }; + m.registerNodeType("network/websocket", c); + p.title = "SillyClient"; + p.desc = "Connects to SillyServer to broadcast messages"; + p.prototype.onPropertyChanged = function(c, m) { + "room" == c && (this.room_widget.value = m); + this.connectSocket(); + }; + p.prototype.setRoom = function(c) { + this.properties.room = c; + this.room_widget.value = c; + this.connectSocket(); + }; + p.prototype.onDrawForeground = function() { + for (var c = 1; c < this.inputs.length; ++c) { + var m = this.inputs[c]; + m.label = "in_" + c; + } + for (c = 1; c < this.outputs.length; ++c) { + m = this.outputs[c], m.label = "out_" + c; + } + }; + p.prototype.onExecute = function() { + if (this._server && this._server.is_connected) { + for (var c = this.properties.only_send_changes, m = 1; m < this.inputs.length; ++m) { + var l = this.getInputData(m), p = this._last_sent_data[m]; + if (null != l) { + if (c) { + var w = !0; + if (l && l.length && p && p.length == l.length && l.constructor !== String) { + for (var v = 0; v < l.length; ++v) { + if (p[v] != l[v]) { + w = !1; + break; + } + } + } else { + this._last_sent_data[m] != l && (w = !1); + } + if (w) { + continue; + } + } + this._server.sendMessage({type:0, channel:m, data:l}); + if (l.length && l.constructor !== String) { + if (this._last_sent_data[m]) { + for (this._last_sent_data[m].length = l.length, v = 0; v < l.length; ++v) { + this._last_sent_data[m][v] = l[v]; + } + } else { + this._last_sent_data[m] = l.constructor === Array ? l.concat() : new l.constructor(l); + } + } else { + this._last_sent_data[m] = l; + } + } + } + for (m = 1; m < this.outputs.length; ++m) { + this.setOutputData(m, this._last_received_data[m]); + } + "#AFA" == this.boxcolor && (this.boxcolor = "#6C6"); + } + }; + p.prototype.connectSocket = function() { + var c = this; + if ("undefined" == typeof SillyClient) { + this._error || console.error("SillyClient node cannot be used, you must include SillyServer.js"), this._error = !0; + } else { + if (this._server = new SillyClient, this._server.on_ready = function() { + console.log("ready"); + c.boxcolor = "#6C6"; + }, this._server.on_message = function(g, l) { + g = null; + try { + g = JSON.parse(l); + } catch (B) { + return; + } + if (1 == g.type) { + if (g.data.object_class && m[g.data.object_class]) { + l = null; + try { + l = new m[g.data.object_class](g.data), c.triggerSlot(0, l); + } catch (B) { + return; + } + } else { + c.triggerSlot(0, g.data); + } + } else { + c._last_received_data[g.channel || 0] = g.data; + } + c.boxcolor = "#AFA"; + }, this._server.on_error = function(g) { + console.log("couldnt connect to websocket"); + c.boxcolor = "#E88"; + }, this._server.on_close = function(g) { + console.log("connection closed"); + c.boxcolor = "#000"; + }, this.properties.url && this.properties.room) { + try { + this._server.connect(this.properties.url, this.properties.room); + } catch (u) { + console.error("SillyServer error: " + u); + this._server = null; + return; + } + this._final_url = this.properties.url + "/" + this.properties.room; + } + } + }; + p.prototype.send = function(c) { + this._server && this._server.is_connected && this._server.sendMessage({type:1, data:c}); + }; + p.prototype.onAction = function(c, m) { + this._server && this._server.is_connected && this._server.sendMessage({type:1, action:c, data:m}); + }; + p.prototype.onGetInputs = function() { + return [["in", 0]]; + }; + p.prototype.onGetOutputs = function() { + return [["out", 0]]; + }; + m.registerNodeType("network/sillyclient", p); +})(this); + diff --git a/src/litegraph.d.ts b/src/litegraph.d.ts index 69220a8df..c8168c80b 100644 --- a/src/litegraph.d.ts +++ b/src/litegraph.d.ts @@ -62,7 +62,7 @@ export interface IWidget { width: number, posY: number, height: number - ): void; + ): number | undefined; /** * Called by `LGraphCanvas.processNodeWidgets` * https://github.com/jagenjo/litegraph.js/issues/76 @@ -73,6 +73,8 @@ export interface IWidget { pos: Vector2, node: LGraphNode ): void; + /** Called by `LGraphNode.computeSize` */ + computeSize?(width: number): [number, number]; } export interface IButtonWidget extends IWidget { type: "button"; diff --git a/src/litegraph.js b/src/litegraph.js index 12193f7a2..6260c112e 100755 --- a/src/litegraph.js +++ b/src/litegraph.js @@ -3173,20 +3173,6 @@ var size = out || new Float32Array([0, 0]); rows = Math.max(rows, 1); var font_size = LiteGraph.NODE_TEXT_SIZE; //although it should be graphcanvas.inner_text_font size - size[1] = (this.constructor.slot_start_y || 0) + rows * LiteGraph.NODE_SLOT_HEIGHT; - - var widgets_height = 0; - if (this.widgets && this.widgets.length) { - widgets_height = this.widgets.length * (LiteGraph.NODE_WIDGET_HEIGHT + 4) + 8; - } - - //compute height using widgets height - if( this.widgets_up ) - size[1] = Math.max( size[1], widgets_height ); - else if( this.widgets_start_y != null ) - size[1] = Math.max( size[1], widgets_height + this.widgets_start_y ); - else - size[1] += widgets_height; var font_size = font_size; var title_width = compute_text_size(this.title); @@ -3221,6 +3207,27 @@ size[0] = Math.max(size[0], LiteGraph.NODE_WIDTH * 1.5); } + size[1] = (this.constructor.slot_start_y || 0) + rows * LiteGraph.NODE_SLOT_HEIGHT; + + var widgets_height = 0; + if (this.widgets && this.widgets.length) { + for (var i = 0, l = this.widgets.length; i < l; ++i) { + if (this.widgets[i].computeSize) + widgets_height += this.widgets[i].computeSize(size[0])[1] + 4; + else + widgets_height += LiteGraph.NODE_WIDGET_HEIGHT + 4; + } + widgets_height += 8; + } + + //compute height using widgets height + if( this.widgets_up ) + size[1] = Math.max( size[1], widgets_height ); + else if( this.widgets_start_y != null ) + size[1] = Math.max( size[1], widgets_height + this.widgets_start_y ); + else + size[1] += widgets_height; + if (this.onResize) { this.onResize(size); } @@ -5569,16 +5576,28 @@ LGraphNode.prototype.executeAction = function(action) this.resizing_node.inputs ? this.resizing_node.inputs.length : 0, this.resizing_node.outputs ? this.resizing_node.outputs.length : 0 ); - var min_height = - max_slots * LiteGraph.NODE_SLOT_HEIGHT + - (this.resizing_node.widgets ? this.resizing_node.widgets.length : 0) * (LiteGraph.NODE_WIDGET_HEIGHT + 4) + 4; - if (this.resizing_node.size[1] < min_height) { - this.resizing_node.size[1] = min_height; - } + if (this.resizing_node.size[0] < LiteGraph.NODE_MIN_WIDTH) { this.resizing_node.size[0] = LiteGraph.NODE_MIN_WIDTH; } + var widgets = this.resizing_node.widgets; + var widgets_height = 0; + if (widgets && widgets.length) { + for (var i = 0, l = widgets.length; i < l; ++i) { + if (widgets[i].computeSize) + widgets_height += widgets[i].computeSize(this.resizing_node.size[0])[1] + 4; + else + widgets_height += LiteGraph.NODE_WIDGET_HEIGHT + 4; + } + widgets_height += 8; + } + + var min_height = max_slots * LiteGraph.NODE_SLOT_HEIGHT + widgets_height; + if (this.resizing_node.size[1] < min_height) { + this.resizing_node.size[1] = min_height; + } + this.canvas.style.cursor = "se-resize"; this.dirty_canvas = true; this.dirty_bgcanvas = true; @@ -8241,6 +8260,7 @@ LGraphNode.prototype.executeAction = function(action) var margin = 15; for (var i = 0; i < widgets.length; ++i) { + var h = H; var w = widgets[i]; var y = posY; if (w.y) { @@ -8411,11 +8431,11 @@ LGraphNode.prototype.executeAction = function(action) break; default: if (w.draw) { - w.draw(ctx, node, w, y, H); + h = w.draw(ctx, node, width, y, H) || H; } break; } - posY += H + 4; + posY += h + 4; ctx.globalAlpha = this.editor_alpha; } From 11a1026074c4a5e5b0d60e24f1cb7bfd89f21701 Mon Sep 17 00:00:00 2001 From: altarfinch Date: Fri, 8 May 2020 01:40:15 +0200 Subject: [PATCH 32/63] fixed mouse function for custom widget : take custom height into account --- build/litegraph.js | 10 +- build/litegraph.min.js | 1855 ++++++++++++++++++++-------------------- src/litegraph.d.ts | 5 +- src/litegraph.js | 10 +- 4 files changed, 941 insertions(+), 939 deletions(-) diff --git a/build/litegraph.js b/build/litegraph.js index 6b449c569..3ec735a32 100644 --- a/build/litegraph.js +++ b/build/litegraph.js @@ -8260,7 +8260,6 @@ LGraphNode.prototype.executeAction = function(action) var margin = 15; for (var i = 0; i < widgets.length; ++i) { - var h = H; var w = widgets[i]; var y = posY; if (w.y) { @@ -8431,11 +8430,11 @@ LGraphNode.prototype.executeAction = function(action) break; default: if (w.draw) { - h = w.draw(ctx, node, width, y, H) || H; + w.draw(ctx, node, width, y, H); } break; } - posY += h + 4; + posY += (w.computeSize ? w.computeSize(width)[1] : H) + 4; ctx.globalAlpha = this.editor_alpha; } @@ -8467,7 +8466,8 @@ LGraphNode.prototype.executeAction = function(action) var w = node.widgets[i]; if(!w || w.disabled) continue; - if ( w == active_widget || (x > 6 && x < width - 12 && y > w.last_y && y < w.last_y + LiteGraph.NODE_WIDGET_HEIGHT) ) { + var widget_height = w.computeSize ? w.computeSize(width)[1] : LiteGraph.NODE_WIDGET_HEIGHT; + if ( w == active_widget || (x > 6 && x < width - 12 && y > w.last_y && y < w.last_y + widget_height) ) { //inside widget switch (w.type) { case "button": @@ -8601,7 +8601,7 @@ LGraphNode.prototype.executeAction = function(action) break; default: if (w.mouse) { - w.mouse(ctx, event, [x, y], node); + this.dirty_canvas = w.mouse(event, [x, y], node); } break; } //end switch diff --git a/build/litegraph.min.js b/build/litegraph.min.js index 514a07e55..c48980b40 100755 --- a/build/litegraph.min.js +++ b/build/litegraph.min.js @@ -3,39 +3,39 @@ $jscomp.scope = {}; $jscomp.ASSUME_ES5 = !1; $jscomp.ASSUME_NO_NATIVE_MAP = !1; $jscomp.ASSUME_NO_NATIVE_SET = !1; -$jscomp.defineProperty = $jscomp.ASSUME_ES5 || "function" == typeof Object.defineProperties ? Object.defineProperty : function(w, c, p) { - w != Array.prototype && w != Object.prototype && (w[c] = p.value); +$jscomp.defineProperty = $jscomp.ASSUME_ES5 || "function" == typeof Object.defineProperties ? Object.defineProperty : function(v, c, q) { + v != Array.prototype && v != Object.prototype && (v[c] = q.value); }; -$jscomp.getGlobal = function(w) { - return "undefined" != typeof window && window === w ? w : "undefined" != typeof global && null != global ? global : w; +$jscomp.getGlobal = function(v) { + return "undefined" != typeof window && window === v ? v : "undefined" != typeof global && null != global ? global : v; }; $jscomp.global = $jscomp.getGlobal(this); -$jscomp.polyfill = function(w, c, p, m) { +$jscomp.polyfill = function(v, c, q, m) { if (c) { - p = $jscomp.global; - w = w.split("."); - for (m = 0; m < w.length - 1; m++) { - var g = w[m]; - g in p || (p[g] = {}); - p = p[g]; + q = $jscomp.global; + v = v.split("."); + for (m = 0; m < v.length - 1; m++) { + var g = v[m]; + g in q || (q[g] = {}); + q = q[g]; } - w = w[w.length - 1]; - m = p[w]; + v = v[v.length - 1]; + m = q[v]; c = c(m); - c != m && null != c && $jscomp.defineProperty(p, w, {configurable:!0, writable:!0, value:c}); + c != m && null != c && $jscomp.defineProperty(q, v, {configurable:!0, writable:!0, value:c}); } }; -$jscomp.polyfill("Array.prototype.fill", function(w) { - return w ? w : function(c, p, m) { +$jscomp.polyfill("Array.prototype.fill", function(v) { + return v ? v : function(c, q, m) { var g = this.length || 0; - 0 > p && (p = Math.max(0, g + p)); + 0 > q && (q = Math.max(0, g + q)); if (null == m || m > g) { m = g; } m = Number(m); 0 > m && (m = Math.max(0, g + m)); - for (p = Number(p || 0); p < m; p++) { - this[p] = c; + for (q = Number(q || 0); q < m; q++) { + this[q] = c; } return this; }; @@ -47,42 +47,42 @@ $jscomp.initSymbol = function() { $jscomp.global.Symbol || ($jscomp.global.Symbol = $jscomp.Symbol); }; $jscomp.Symbol = function() { - var w = 0; + var v = 0; return function(c) { - return $jscomp.SYMBOL_PREFIX + (c || "") + w++; + return $jscomp.SYMBOL_PREFIX + (c || "") + v++; }; }(); $jscomp.initSymbolIterator = function() { $jscomp.initSymbol(); - var w = $jscomp.global.Symbol.iterator; - w || (w = $jscomp.global.Symbol.iterator = $jscomp.global.Symbol("iterator")); - "function" != typeof Array.prototype[w] && $jscomp.defineProperty(Array.prototype, w, {configurable:!0, writable:!0, value:function() { + var v = $jscomp.global.Symbol.iterator; + v || (v = $jscomp.global.Symbol.iterator = $jscomp.global.Symbol("iterator")); + "function" != typeof Array.prototype[v] && $jscomp.defineProperty(Array.prototype, v, {configurable:!0, writable:!0, value:function() { return $jscomp.arrayIterator(this); }}); $jscomp.initSymbolIterator = function() { }; }; -$jscomp.arrayIterator = function(w) { +$jscomp.arrayIterator = function(v) { var c = 0; return $jscomp.iteratorPrototype(function() { - return c < w.length ? {done:!1, value:w[c++]} : {done:!0}; + return c < v.length ? {done:!1, value:v[c++]} : {done:!0}; }); }; -$jscomp.iteratorPrototype = function(w) { +$jscomp.iteratorPrototype = function(v) { $jscomp.initSymbolIterator(); - w = {next:w}; - w[$jscomp.global.Symbol.iterator] = function() { + v = {next:v}; + v[$jscomp.global.Symbol.iterator] = function() { return this; }; - return w; + return v; }; -$jscomp.iteratorFromArray = function(w, c) { +$jscomp.iteratorFromArray = function(v, c) { $jscomp.initSymbolIterator(); - w instanceof String && (w += ""); - var p = 0, m = {next:function() { - if (p < w.length) { - var g = p++; - return {value:c(g, w[g]), done:!1}; + v instanceof String && (v += ""); + var q = 0, m = {next:function() { + if (q < v.length) { + var g = q++; + return {value:c(g, v[g]), done:!1}; } m.next = function() { return {done:!0, value:void 0}; @@ -94,40 +94,40 @@ $jscomp.iteratorFromArray = function(w, c) { }; return m; }; -$jscomp.polyfill("Array.prototype.values", function(w) { - return w ? w : function() { - return $jscomp.iteratorFromArray(this, function(c, p) { - return p; +$jscomp.polyfill("Array.prototype.values", function(v) { + return v ? v : function() { + return $jscomp.iteratorFromArray(this, function(c, q) { + return q; }); }; }, "es8", "es3"); -$jscomp.polyfill("Array.prototype.keys", function(w) { - return w ? w : function() { +$jscomp.polyfill("Array.prototype.keys", function(v) { + return v ? v : function() { return $jscomp.iteratorFromArray(this, function(c) { return c; }); }; }, "es6", "es3"); -$jscomp.owns = function(w, c) { - return Object.prototype.hasOwnProperty.call(w, c); +$jscomp.owns = function(v, c) { + return Object.prototype.hasOwnProperty.call(v, c); }; -$jscomp.polyfill("Object.values", function(w) { - return w ? w : function(c) { - var p = [], m; +$jscomp.polyfill("Object.values", function(v) { + return v ? v : function(c) { + var q = [], m; for (m in c) { - $jscomp.owns(c, m) && p.push(c[m]); + $jscomp.owns(c, m) && q.push(c[m]); } - return p; + return q; }; }, "es8", "es3"); -(function(w) { +(function(v) { function c(a) { e.debug && console.log("Graph created"); this.list_of_graphcanvas = null; this.clear(); a && this.configure(a); } - function p(a, b, d, h, f, e) { + function q(a, b, d, h, f, e) { this.id = a; this.type = b; this.origin_id = d; @@ -143,7 +143,7 @@ $jscomp.polyfill("Object.values", function(w) { function g(a) { this._ctor(a); } - function u(a, b) { + function r(a, b) { this.offset = new Float32Array([0, 0]); this.scale = 1; this.max_scale = 10; @@ -159,7 +159,7 @@ $jscomp.polyfill("Object.values", function(w) { d = d || {}; this.background_image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; a && a.constructor === String && (a = document.querySelector(a)); - this.ds = new u; + this.ds = new r; this.zoom_modify_alpha = !0; this.title_text_font = "" + e.NODE_TEXT_SIZE + "px Arial"; this.inner_text_font = "normal " + e.NODE_SUBTEXT_SIZE + "px Arial"; @@ -206,7 +206,7 @@ $jscomp.polyfill("Object.values", function(w) { function y(a, b, d, h, f, e) { return d < a && d + f > a && h < b && h + e > b ? !0 : !1; } - function v(a, b) { + function w(a, b) { var d = a[0] + a[2], h = a[1] + a[3], f = b[1] + b[3]; return a[0] > b[0] + b[2] || a[1] > f || d < b[0] || h < b[1] ? !1 : !0; } @@ -284,7 +284,7 @@ $jscomp.polyfill("Object.values", function(w) { this.must_update = !0; this.margin = 5; } - var e = w.LiteGraph = {VERSION:0.4, CANVAS_GRID_SIZE:10, NODE_TITLE_HEIGHT:30, NODE_TITLE_TEXT_Y:20, NODE_SLOT_HEIGHT:20, NODE_WIDGET_HEIGHT:20, NODE_WIDTH:140, NODE_MIN_WIDTH:50, NODE_COLLAPSED_RADIUS:10, NODE_COLLAPSED_WIDTH:80, NODE_TITLE_COLOR:"#999", NODE_TEXT_SIZE:14, NODE_TEXT_COLOR:"#AAA", NODE_SUBTEXT_SIZE:12, NODE_DEFAULT_COLOR:"#333", NODE_DEFAULT_BGCOLOR:"#353535", NODE_DEFAULT_BOXCOLOR:"#666", NODE_DEFAULT_SHAPE:"box", DEFAULT_SHADOW_COLOR:"rgba(0,0,0,0.5)", DEFAULT_GROUP_FONT:24, + var e = v.LiteGraph = {VERSION:0.4, CANVAS_GRID_SIZE:10, NODE_TITLE_HEIGHT:30, NODE_TITLE_TEXT_Y:20, NODE_SLOT_HEIGHT:20, NODE_WIDGET_HEIGHT:20, NODE_WIDTH:140, NODE_MIN_WIDTH:50, NODE_COLLAPSED_RADIUS:10, NODE_COLLAPSED_WIDTH:80, NODE_TITLE_COLOR:"#999", NODE_TEXT_SIZE:14, NODE_TEXT_COLOR:"#AAA", NODE_SUBTEXT_SIZE:12, NODE_DEFAULT_COLOR:"#333", NODE_DEFAULT_BGCOLOR:"#353535", NODE_DEFAULT_BOXCOLOR:"#666", NODE_DEFAULT_SHAPE:"box", DEFAULT_SHADOW_COLOR:"rgba(0,0,0,0.5)", DEFAULT_GROUP_FONT:24, WIDGET_BGCOLOR:"#222", WIDGET_OUTLINE_COLOR:"#666", WIDGET_TEXT_COLOR:"#DDD", WIDGET_SECONDARY_TEXT_COLOR:"#999", LINK_COLOR:"#9A9", EVENT_LINK_COLOR:"#A86", CONNECTING_LINK_COLOR:"#AFA", MAX_NUMBER_OF_NODES:1000, DEFAULT_POSITION:[100, 100], VALID_SHAPES:["default", "box", "round", "card"], BOX_SHAPE:1, ROUND_SHAPE:2, CIRCLE_SHAPE:3, CARD_SHAPE:4, ARROW_SHAPE:5, INPUT:1, OUTPUT:2, EVENT:-1, ACTION:-1, ALWAYS:0, ON_EVENT:1, NEVER:2, ON_TRIGGER:3, UP:1, DOWN:2, LEFT:3, RIGHT:4, CENTER:5, STRAIGHT_LINK:0, LINEAR_LINK:1, SPLINE_LINK:2, NORMAL_TITLE:0, NO_TITLE:1, TRANSPARENT_TITLE:2, AUTOHIDE_TITLE:3, proxy:null, node_images_path:"", debug:!1, catch_exceptions:!0, throw_errors:!0, allow_scripts:!1, registered_node_types:{}, node_types_by_file_extension:{}, Nodes:{}, searchbox_extras:{}, registerNodeType:function(a, b) { if (!b.prototype) { @@ -537,7 +537,7 @@ $jscomp.polyfill("Object.values", function(w) { } : function() { return (new Date).getTime(); }; - w.LGraph = e.LGraph = c; + v.LGraph = e.LGraph = c; c.supported_types = ["number", "string", "boolean"]; c.prototype.getSupportedTypes = function() { return this.supported_types || c.supported_types; @@ -706,24 +706,24 @@ $jscomp.polyfill("Object.values", function(w) { }; c.prototype.computeExecutionOrder = function(a, b) { for (var d = [], h = [], f = {}, x = {}, c = {}, k = 0, n = this._nodes.length; k < n; ++k) { - var q = this._nodes[k]; - if (!a || q.onExecute) { - f[q.id] = q; + var p = this._nodes[k]; + if (!a || p.onExecute) { + f[p.id] = p; var l = 0; - if (q.inputs) { - for (var r = 0, g = q.inputs.length; r < g; r++) { - q.inputs[r] && null != q.inputs[r].link && (l += 1); + if (p.inputs) { + for (var t = 0, g = p.inputs.length; t < g; t++) { + p.inputs[t] && null != p.inputs[t].link && (l += 1); } } - 0 == l ? (h.push(q), b && (q._level = 1)) : (b && (q._level = 0), c[q.id] = l); + 0 == l ? (h.push(p), b && (p._level = 1)) : (b && (p._level = 0), c[p.id] = l); } } for (; 0 != h.length;) { - if (q = h.shift(), d.push(q), delete f[q.id], q.outputs) { - for (k = 0; k < q.outputs.length; k++) { - if (a = q.outputs[k], null != a && null != a.links && 0 != a.links.length) { - for (r = 0; r < a.links.length; r++) { - (n = this.links[a.links[r]]) && !x[n.id] && (l = this.getNodeById(n.target_id), null == l ? x[n.id] = !0 : (b && (!l._level || l._level <= q._level) && (l._level = q._level + 1), x[n.id] = !0, --c[l.id], 0 == c[l.id] && h.push(l))); + if (p = h.shift(), d.push(p), delete f[p.id], p.outputs) { + for (k = 0; k < p.outputs.length; k++) { + if (a = p.outputs[k], null != a && null != a.links && 0 != a.links.length) { + for (t = 0; t < a.links.length; t++) { + (n = this.links[a.links[t]]) && !x[n.id] && (l = this.getNodeById(n.target_id), null == l ? x[n.id] = !0 : (b && (!l._level || l._level <= p._level) && (l._level = p._level + 1), x[n.id] = !0, --c[l.id], 0 == c[l.id] && h.push(l))); } } } @@ -1158,7 +1158,7 @@ $jscomp.polyfill("Object.values", function(w) { var h = this.links[b]; if (!h.serialize) { console.warn("weird LLink bug, link info is not a LLink but a regular object"); - var f = new p; + var f = new q; for (b in h) { f[b] = h[b]; } @@ -1180,7 +1180,7 @@ $jscomp.polyfill("Object.values", function(w) { for (var d = [], h = 0; h < a.links.length; ++h) { var f = a.links[h]; if (f) { - var c = new p; + var c = new q; c.configure(f); d[c.id] = c; } else { @@ -1233,14 +1233,14 @@ $jscomp.polyfill("Object.values", function(w) { }; c.prototype.onNodeTrace = function(a, b, d) { }; - p.prototype.configure = function(a) { + q.prototype.configure = function(a) { a.constructor === Array ? (this.id = a[0], this.origin_id = a[1], this.origin_slot = a[2], this.target_id = a[3], this.target_slot = a[4], this.type = a[5]) : (this.id = a.id, this.type = a.type, this.origin_id = a.origin_id, this.origin_slot = a.origin_slot, this.target_id = a.target_id, this.target_slot = a.target_slot); }; - p.prototype.serialize = function() { + q.prototype.serialize = function() { return [this.id, this.origin_id, this.origin_slot, this.target_id, this.target_slot, this.type]; }; - e.LLink = p; - w.LGraphNode = e.LGraphNode = m; + e.LLink = q; + v.LGraphNode = e.LGraphNode = m; m.prototype._ctor = function(a) { this.title = a || "Unnamed"; this.size = [e.NODE_WIDTH, 60]; @@ -1664,7 +1664,7 @@ $jscomp.polyfill("Object.values", function(w) { a = Math.max(a, 1); var h = e.NODE_TEXT_SIZE, f = d(this.title), c = 0, k = 0; if (this.inputs) { - for (var n = 0, q = this.inputs.length; n < q; ++n) { + for (var n = 0, p = this.inputs.length; n < p; ++n) { var l = this.inputs[n]; l = l.label || l.name || ""; l = d(l); @@ -1672,7 +1672,7 @@ $jscomp.polyfill("Object.values", function(w) { } } if (this.outputs) { - for (n = 0, q = this.outputs.length; n < q; ++n) { + for (n = 0, p = this.outputs.length; n < p; ++n) { l = this.outputs[n], l = l.label || l.name || "", l = d(l), k < l && (k = l); } } @@ -1683,7 +1683,7 @@ $jscomp.polyfill("Object.values", function(w) { a = 0; if (this.widgets && this.widgets.length) { n = 0; - for (q = this.widgets.length; n < q; ++n) { + for (p = this.widgets.length; n < p; ++n) { a = this.widgets[n].computeSize ? a + (this.widgets[n].computeSize(b[0])[1] + 4) : a + (e.NODE_WIDGET_HEIGHT + 4); } a += 8; @@ -1841,7 +1841,7 @@ $jscomp.polyfill("Object.values", function(w) { } var f = b.inputs[d], c = null; if (e.isValidConnection(h.type, f.type)) { - c = new p(++this.graph.last_link_id, f.type, this.id, a, b.id, d); + c = new q(++this.graph.last_link_id, f.type, this.id, a, b.id, d); this.graph.links[c.id] = c; null == h.links && (h.links = []); h.links.push(c.id); @@ -2046,7 +2046,7 @@ $jscomp.polyfill("Object.values", function(w) { m.prototype.localToScreen = function(a, b, d) { return [(a + this.pos[0]) * d.scale + d.offset[0], (b + this.pos[1]) * d.scale + d.offset[1]]; }; - w.LGraphGroup = e.LGraphGroup = g; + v.LGraphGroup = e.LGraphGroup = g; g.prototype._ctor = function(a) { this.title = a || "Group"; this.font_size = 24; @@ -2093,13 +2093,13 @@ $jscomp.polyfill("Object.values", function(w) { for (var a = this.graph._nodes, b = new Float32Array(4), d = 0; d < a.length; ++d) { var h = a[d]; h.getBounding(b); - v(this._bounding, b) && this._nodes.push(h); + w(this._bounding, b) && this._nodes.push(h); } }; g.prototype.isPointInside = m.prototype.isPointInside; g.prototype.setDirtyCanvas = m.prototype.setDirtyCanvas; - e.DragAndScale = u; - u.prototype.bindEvents = function(a) { + e.DragAndScale = r; + r.prototype.bindEvents = function(a) { this.last_mouse = new Float32Array(2); this._binded_mouse_callback = this.onMouse.bind(this); a.addEventListener("mousedown", this._binded_mouse_callback); @@ -2107,7 +2107,7 @@ $jscomp.polyfill("Object.values", function(w) { a.addEventListener("mousewheel", this._binded_mouse_callback, !1); a.addEventListener("wheel", this._binded_mouse_callback, !1); }; - u.prototype.computeVisibleArea = function() { + r.prototype.computeVisibleArea = function() { if (this.element) { var a = -this.offset[0], b = -this.offset[1], d = a + this.element.width / this.scale, h = b + this.element.height / this.scale; this.visible_area[0] = a; @@ -2118,7 +2118,7 @@ $jscomp.polyfill("Object.values", function(w) { this.visible_area[0] = this.visible_area[1] = this.visible_area[2] = this.visible_area[3] = 0; } }; - u.prototype.onMouse = function(a) { + r.prototype.onMouse = function(a) { if (this.enabled) { var b = this.element, d = b.getBoundingClientRect(), h = a.clientX - d.left; d = a.clientY - d.top; @@ -2149,27 +2149,27 @@ $jscomp.polyfill("Object.values", function(w) { return !1; } }; - u.prototype.toCanvasContext = function(a) { + r.prototype.toCanvasContext = function(a) { a.scale(this.scale, this.scale); a.translate(this.offset[0], this.offset[1]); }; - u.prototype.convertOffsetToCanvas = function(a) { + r.prototype.convertOffsetToCanvas = function(a) { return [(a[0] + this.offset[0]) * this.scale, (a[1] + this.offset[1]) * this.scale]; }; - u.prototype.convertCanvasToOffset = function(a, b) { + r.prototype.convertCanvasToOffset = function(a, b) { b = b || [0, 0]; b[0] = a[0] / this.scale - this.offset[0]; b[1] = a[1] / this.scale - this.offset[1]; return b; }; - u.prototype.mouseDrag = function(a, b) { + r.prototype.mouseDrag = function(a, b) { this.offset[0] += a / this.scale; this.offset[1] += b / this.scale; if (this.onredraw) { this.onredraw(this); } }; - u.prototype.changeScale = function(a, b) { + r.prototype.changeScale = function(a, b) { a < this.min_scale ? a = this.min_scale : a > this.max_scale && (a = this.max_scale); if (a != this.scale && this.element) { var d = this.element.getBoundingClientRect(); @@ -2178,15 +2178,15 @@ $jscomp.polyfill("Object.values", function(w) { } } }; - u.prototype.changeDeltaScale = function(a, b) { + r.prototype.changeDeltaScale = function(a, b) { this.changeScale(this.scale * a, b); }; - u.prototype.reset = function() { + r.prototype.reset = function() { this.scale = 1; this.offset[0] = 0; this.offset[1] = 0; }; - w.LGraphCanvas = e.LGraphCanvas = l; + v.LGraphCanvas = e.LGraphCanvas = l; l.link_type_colors = {"-1":e.EVENT_LINK_COLOR, number:"#AAA", node:"#DCA"}; l.gradients = {}; l.prototype.clear = function() { @@ -2372,10 +2372,10 @@ $jscomp.polyfill("Object.values", function(w) { } else { if (d.outputs) { for (var k = 0, n = d.outputs.length; k < n; ++k) { - var q = d.outputs[k], g = d.getConnectionPos(!1, k); + var p = d.outputs[k], g = d.getConnectionPos(!1, k); if (y(a.canvasX, a.canvasY, g[0] - 15, g[1] - 10, 30, 20)) { this.connecting_node = d; - this.connecting_output = q; + this.connecting_output = p; this.connecting_pos = d.getConnectionPos(!1, k); this.connecting_slot = k; a.shiftKey && d.disconnectOutput(k); @@ -2395,7 +2395,7 @@ $jscomp.polyfill("Object.values", function(w) { } if (d.inputs) { for (k = 0, n = d.inputs.length; k < n; ++k) { - if (q = d.inputs[k], g = d.getConnectionPos(!0, k), y(a.canvasX, a.canvasY, g[0] - 15, g[1] - 10, 30, 20)) { + if (p = d.inputs[k], g = d.getConnectionPos(!0, k), y(a.canvasX, a.canvasY, g[0] - 15, g[1] - 10, 30, 20)) { if (f) { if (d.onInputDblClick) { d.onInputDblClick(k, a); @@ -2405,8 +2405,8 @@ $jscomp.polyfill("Object.values", function(w) { d.onInputClick(k, a); } } - if (null !== q.link) { - h = this.graph.links[q.link]; + if (null !== p.link) { + h = this.graph.links[p.link]; d.disconnectInput(k); if (this.allow_reconnect_links || a.shiftKey) { this.connecting_node = this.graph._nodes_by_id[h.origin_id], this.connecting_slot = h.origin_slot, this.connecting_output = this.connecting_node.outputs[this.connecting_slot], this.connecting_pos = this.connecting_node.getConnectionPos(!1, this.connecting_slot); @@ -2588,7 +2588,7 @@ $jscomp.polyfill("Object.values", function(w) { this.dragging_rectangle[3] = f; f = []; for (c = 0; c < b.length; ++c) { - h = b[c], h.getBounding(d), v(this.dragging_rectangle, d) && f.push(h); + h = b[c], h.getBounding(d), w(this.dragging_rectangle, d) && f.push(h); } f.length && this.selectNodes(f); } @@ -2941,7 +2941,7 @@ $jscomp.polyfill("Object.values", function(w) { a = a || this.graph._nodes; for (var d = 0, h = a.length; d < h; ++d) { var f = a[d]; - (!this.live_mode || f.onDrawBackground || f.onDrawForeground) && v(this.visible_area, f.getBounding(C)) && b.push(f); + (!this.live_mode || f.onDrawBackground || f.onDrawForeground) && w(this.visible_area, f.getBounding(C)) && b.push(f); } return b; }; @@ -3116,8 +3116,8 @@ $jscomp.polyfill("Object.values", function(w) { var n = a.horizontal; if (a.flags.collapsed) { b.font = this.inner_text_font; - var q = a.getTitle ? a.getTitle() : a.title; - null != q && (a._collapsed_width = Math.min(a.size[0], b.measureText(q).width + 2 * e.NODE_TITLE_HEIGHT), D[0] = a._collapsed_width, D[1] = 0); + var p = a.getTitle ? a.getTitle() : a.title; + null != p && (a._collapsed_width = Math.min(a.size[0], b.measureText(p).width + 2 * e.NODE_TITLE_HEIGHT), D[0] = a._collapsed_width, D[1] = 0); } a.clip_area && (b.save(), b.beginPath(), k == e.BOX_SHAPE ? b.rect(0, 0, D[0], D[1]) : k == e.ROUND_SHAPE ? b.roundRect(0, 0, D[0], D[1], 10) : k == e.CIRCLE_SHAPE && b.arc(0.5 * D[0], 0.5 * D[1], 0.5 * D[0], 0, 2 * Math.PI), b.clip()); a.has_errors && (h = "red"); @@ -3131,7 +3131,7 @@ $jscomp.polyfill("Object.values", function(w) { h = !f; k = this.connecting_output; b.lineWidth = 1; - q = 0; + p = 0; var l = new Float32Array(2); if (!a.flags.collapsed) { if (a.inputs) { @@ -3140,16 +3140,16 @@ $jscomp.polyfill("Object.values", function(w) { b.globalAlpha = c; this.connecting_node && !e.isValidConnection(g.type, k.type) && (b.globalAlpha = 0.4 * c); b.fillStyle = null != g.link ? g.color_on || this.default_connection_color.input_on : g.color_off || this.default_connection_color.input_off; - var r = a.getConnectionPos(!0, d, l); - r[0] -= a.pos[0]; - r[1] -= a.pos[1]; - q < r[1] + 0.5 * e.NODE_SLOT_HEIGHT && (q = r[1] + 0.5 * e.NODE_SLOT_HEIGHT); + var t = a.getConnectionPos(!0, d, l); + t[0] -= a.pos[0]; + t[1] -= a.pos[1]; + p < t[1] + 0.5 * e.NODE_SLOT_HEIGHT && (p = t[1] + 0.5 * e.NODE_SLOT_HEIGHT); b.beginPath(); - g.type === e.EVENT || g.shape === e.BOX_SHAPE ? n ? b.rect(r[0] - 5 + 0.5, r[1] - 8 + 0.5, 10, 14) : b.rect(r[0] - 6 + 0.5, r[1] - 5 + 0.5, 14, 10) : g.shape === e.ARROW_SHAPE ? (b.moveTo(r[0] + 8, r[1] + 0.5), b.lineTo(r[0] - 4, r[1] + 6 + 0.5), b.lineTo(r[0] - 4, r[1] - 6 + 0.5), b.closePath()) : f ? b.rect(r[0] - 4, r[1] - 4, 8, 8) : b.arc(r[0], r[1], 4, 0, 2 * Math.PI); + g.type === e.EVENT || g.shape === e.BOX_SHAPE ? n ? b.rect(t[0] - 5 + 0.5, t[1] - 8 + 0.5, 10, 14) : b.rect(t[0] - 6 + 0.5, t[1] - 5 + 0.5, 14, 10) : g.shape === e.ARROW_SHAPE ? (b.moveTo(t[0] + 8, t[1] + 0.5), b.lineTo(t[0] - 4, t[1] + 6 + 0.5), b.lineTo(t[0] - 4, t[1] - 6 + 0.5), b.closePath()) : f ? b.rect(t[0] - 4, t[1] - 4, 8, 8) : b.arc(t[0], t[1], 4, 0, 2 * Math.PI); b.fill(); if (h) { var m = null != g.label ? g.label : g.name; - m && (b.fillStyle = e.NODE_TEXT_COLOR, n || g.dir == e.UP ? b.fillText(m, r[0], r[1] - 10) : b.fillText(m, r[0] + 10, r[1] + 5)); + m && (b.fillStyle = e.NODE_TEXT_COLOR, n || g.dir == e.UP ? b.fillText(m, t[0], t[1] - 10) : b.fillText(m, t[0] + 10, t[1] + 5)); } } } @@ -3158,16 +3158,16 @@ $jscomp.polyfill("Object.values", function(w) { b.strokeStyle = "black"; if (a.outputs) { for (d = 0; d < a.outputs.length; d++) { - if (g = a.outputs[d], r = a.getConnectionPos(!1, d, l), r[0] -= a.pos[0], r[1] -= a.pos[1], q < r[1] + 0.5 * e.NODE_SLOT_HEIGHT && (q = r[1] + 0.5 * e.NODE_SLOT_HEIGHT), b.fillStyle = g.links && g.links.length ? g.color_on || this.default_connection_color.output_on : g.color_off || this.default_connection_color.output_off, b.beginPath(), g.type === e.EVENT || g.shape === e.BOX_SHAPE ? n ? b.rect(r[0] - 5 + 0.5, r[1] - 8 + 0.5, 10, 14) : b.rect(r[0] - 6 + 0.5, r[1] - 5 + 0.5, 14, 10) : - g.shape === e.ARROW_SHAPE ? (b.moveTo(r[0] + 8, r[1] + 0.5), b.lineTo(r[0] - 4, r[1] + 6 + 0.5), b.lineTo(r[0] - 4, r[1] - 6 + 0.5), b.closePath()) : f ? b.rect(r[0] - 4, r[1] - 4, 8, 8) : b.arc(r[0], r[1], 4, 0, 2 * Math.PI), b.fill(), f || b.stroke(), h && (m = null != g.label ? g.label : g.name)) { - b.fillStyle = e.NODE_TEXT_COLOR, n || g.dir == e.DOWN ? b.fillText(m, r[0], r[1] - 8) : b.fillText(m, r[0] - 10, r[1] + 5); + if (g = a.outputs[d], t = a.getConnectionPos(!1, d, l), t[0] -= a.pos[0], t[1] -= a.pos[1], p < t[1] + 0.5 * e.NODE_SLOT_HEIGHT && (p = t[1] + 0.5 * e.NODE_SLOT_HEIGHT), b.fillStyle = g.links && g.links.length ? g.color_on || this.default_connection_color.output_on : g.color_off || this.default_connection_color.output_off, b.beginPath(), g.type === e.EVENT || g.shape === e.BOX_SHAPE ? n ? b.rect(t[0] - 5 + 0.5, t[1] - 8 + 0.5, 10, 14) : b.rect(t[0] - 6 + 0.5, t[1] - 5 + 0.5, 14, 10) : + g.shape === e.ARROW_SHAPE ? (b.moveTo(t[0] + 8, t[1] + 0.5), b.lineTo(t[0] - 4, t[1] + 6 + 0.5), b.lineTo(t[0] - 4, t[1] - 6 + 0.5), b.closePath()) : f ? b.rect(t[0] - 4, t[1] - 4, 8, 8) : b.arc(t[0], t[1], 4, 0, 2 * Math.PI), b.fill(), f || b.stroke(), h && (m = null != g.label ? g.label : g.name)) { + b.fillStyle = e.NODE_TEXT_COLOR, n || g.dir == e.DOWN ? b.fillText(m, t[0], t[1] - 8) : b.fillText(m, t[0] - 10, t[1] + 5); } } } b.textAlign = "left"; b.globalAlpha = 1; if (a.widgets) { - g = q; + g = p; if (n || a.widgets_up) { g = 2; } @@ -3226,22 +3226,22 @@ $jscomp.polyfill("Object.values", function(w) { a.fillText(b, d[0], d[1] - 15 - 24 * 0.3); } }; - var t = new Float32Array(4); + var u = new Float32Array(4); l.prototype.drawNodeShape = function(a, b, d, h, f, c, k) { b.strokeStyle = h; b.fillStyle = f; f = e.NODE_TITLE_HEIGHT; - var n = 0.5 > this.ds.scale, q = a._shape || a.constructor.shape || e.ROUND_SHAPE, x = a.constructor.title_mode, g = !0; + var n = 0.5 > this.ds.scale, p = a._shape || a.constructor.shape || e.ROUND_SHAPE, x = a.constructor.title_mode, g = !0; x == e.TRANSPARENT_TITLE ? g = !1 : x == e.AUTOHIDE_TITLE && k && (g = !0); - t[0] = 0; - t[1] = g ? -f : 0; - t[2] = d[0] + 1; - t[3] = g ? d[1] + f : d[1]; + u[0] = 0; + u[1] = g ? -f : 0; + u[2] = d[0] + 1; + u[3] = g ? d[1] + f : d[1]; k = b.globalAlpha; b.beginPath(); - q == e.BOX_SHAPE || n ? b.fillRect(t[0], t[1], t[2], t[3]) : q == e.ROUND_SHAPE || q == e.CARD_SHAPE ? b.roundRect(t[0], t[1], t[2], t[3], this.round_radius, q == e.CARD_SHAPE ? 0 : this.round_radius) : q == e.CIRCLE_SHAPE && b.arc(0.5 * d[0], 0.5 * d[1], 0.5 * d[0], 0, 2 * Math.PI); + p == e.BOX_SHAPE || n ? b.fillRect(u[0], u[1], u[2], u[3]) : p == e.ROUND_SHAPE || p == e.CARD_SHAPE ? b.roundRect(u[0], u[1], u[2], u[3], this.round_radius, p == e.CARD_SHAPE ? 0 : this.round_radius) : p == e.CIRCLE_SHAPE && b.arc(0.5 * d[0], 0.5 * d[1], 0.5 * d[0], 0, 2 * Math.PI); b.fill(); - a.flags.collapsed || (b.shadowColor = "transparent", b.fillStyle = "rgba(0,0,0,0.2)", b.fillRect(0, -1, t[2], 2)); + a.flags.collapsed || (b.shadowColor = "transparent", b.fillStyle = "rgba(0,0,0,0.2)", b.fillRect(0, -1, u[2], 2)); b.shadowColor = "transparent"; if (a.onDrawBackground) { a.onDrawBackground(b, this, this.canvas); @@ -3254,14 +3254,14 @@ $jscomp.polyfill("Object.values", function(w) { g = a.constructor.title_color || h; a.flags.collapsed && (b.shadowColor = e.DEFAULT_SHADOW_COLOR); if (this.use_gradients) { - var r = l.gradients[g]; - r || (r = l.gradients[g] = b.createLinearGradient(0, 0, 400, 0), r.addColorStop(0, g), r.addColorStop(1, "#000")); - b.fillStyle = r; + var t = l.gradients[g]; + t || (t = l.gradients[g] = b.createLinearGradient(0, 0, 400, 0), t.addColorStop(0, g), t.addColorStop(1, "#000")); + b.fillStyle = t; } else { b.fillStyle = g; } b.beginPath(); - q == e.BOX_SHAPE || n ? b.rect(0, -f, d[0] + 1, f) : (q == e.ROUND_SHAPE || q == e.CARD_SHAPE) && b.roundRect(0, -f, d[0] + 1, f, this.round_radius, a.flags.collapsed ? this.round_radius : 0); + p == e.BOX_SHAPE || n ? b.rect(0, -f, d[0] + 1, f) : (p == e.ROUND_SHAPE || p == e.CARD_SHAPE) && b.roundRect(0, -f, d[0] + 1, f, this.round_radius, a.flags.collapsed ? this.round_radius : 0); b.fill(); b.shadowColor = "transparent"; } @@ -3269,7 +3269,7 @@ $jscomp.polyfill("Object.values", function(w) { if (a.onDrawTitleBox) { a.onDrawTitleBox(b, f, d, this.ds.scale); } else { - q == e.ROUND_SHAPE || q == e.CIRCLE_SHAPE || q == e.CARD_SHAPE ? (n && (b.fillStyle = "black", b.beginPath(), b.arc(0.5 * f, -0.5 * f, 6, 0, 2 * Math.PI), b.fill()), b.fillStyle = a.boxcolor || e.NODE_DEFAULT_BOXCOLOR, n ? b.fillRect(0.5 * f - 5, -0.5 * f - 5, 10, 10) : (b.beginPath(), b.arc(0.5 * f, -0.5 * f, 5, 0, 2 * Math.PI), b.fill())) : (n && (b.fillStyle = "black", b.fillRect(0.5 * (f - 10) - 1, -0.5 * (f + 10) - 1, 12, 12)), b.fillStyle = a.boxcolor || e.NODE_DEFAULT_BOXCOLOR, b.fillRect(0.5 * + p == e.ROUND_SHAPE || p == e.CIRCLE_SHAPE || p == e.CARD_SHAPE ? (n && (b.fillStyle = "black", b.beginPath(), b.arc(0.5 * f, -0.5 * f, 6, 0, 2 * Math.PI), b.fill()), b.fillStyle = a.boxcolor || e.NODE_DEFAULT_BOXCOLOR, n ? b.fillRect(0.5 * f - 5, -0.5 * f - 5, 10, 10) : (b.beginPath(), b.arc(0.5 * f, -0.5 * f, 5, 0, 2 * Math.PI), b.fill())) : (n && (b.fillStyle = "black", b.fillRect(0.5 * (f - 10) - 1, -0.5 * (f + 10) - 1, 12, 12)), b.fillStyle = a.boxcolor || e.NODE_DEFAULT_BOXCOLOR, b.fillRect(0.5 * (f - 10), -0.5 * (f + 10), 10, 10)); } b.globalAlpha = k; @@ -3283,20 +3283,20 @@ $jscomp.polyfill("Object.values", function(w) { } if (c) { if (a.onBounding) { - a.onBounding(t); + a.onBounding(u); } - x == e.TRANSPARENT_TITLE && (t[1] -= f, t[3] += f); + x == e.TRANSPARENT_TITLE && (u[1] -= f, u[3] += f); b.lineWidth = 1; b.globalAlpha = 0.8; b.beginPath(); - q == e.BOX_SHAPE ? b.rect(-6 + t[0], -6 + t[1], 12 + t[2], 12 + t[3]) : q == e.ROUND_SHAPE || q == e.CARD_SHAPE && a.flags.collapsed ? b.roundRect(-6 + t[0], -6 + t[1], 12 + t[2], 12 + t[3], 2 * this.round_radius) : q == e.CARD_SHAPE ? b.roundRect(-6 + t[0], -6 + t[1], 12 + t[2], 12 + t[3], 2 * this.round_radius, 2) : q == e.CIRCLE_SHAPE && b.arc(0.5 * d[0], 0.5 * d[1], 0.5 * d[0] + 6, 0, 2 * Math.PI); + p == e.BOX_SHAPE ? b.rect(-6 + u[0], -6 + u[1], 12 + u[2], 12 + u[3]) : p == e.ROUND_SHAPE || p == e.CARD_SHAPE && a.flags.collapsed ? b.roundRect(-6 + u[0], -6 + u[1], 12 + u[2], 12 + u[3], 2 * this.round_radius) : p == e.CARD_SHAPE ? b.roundRect(-6 + u[0], -6 + u[1], 12 + u[2], 12 + u[3], 2 * this.round_radius, 2) : p == e.CIRCLE_SHAPE && b.arc(0.5 * d[0], 0.5 * d[1], 0.5 * d[0] + 6, 0, 2 * Math.PI); b.strokeStyle = "#FFF"; b.stroke(); b.strokeStyle = h; b.globalAlpha = 1; } }; - var G = new Float32Array(4), n = new Float32Array(4), q = new Float32Array(2), k = new Float32Array(2); + var G = new Float32Array(4), n = new Float32Array(4), p = new Float32Array(2), k = new Float32Array(2); l.prototype.drawConnections = function(a) { var b = e.getTime(), d = this.visible_area; G[0] = d[0] - 20; @@ -3316,24 +3316,24 @@ $jscomp.polyfill("Object.values", function(w) { if (g && null != g.link && (g = this.graph.links[g.link])) { var m = this.graph.getNodeById(g.origin_id); if (null != m) { - var u = g.origin_slot; - var A = -1 == u ? [m.pos[0] + 10, m.pos[1] + 10] : m.getConnectionPos(!1, u, q); - var r = c.getConnectionPos(!0, l, k); + var r = g.origin_slot; + var A = -1 == r ? [m.pos[0] + 10, m.pos[1] + 10] : m.getConnectionPos(!1, r, p); + var t = c.getConnectionPos(!0, l, k); n[0] = A[0]; n[1] = A[1]; - n[2] = r[0] - A[0]; - n[3] = r[1] - A[1]; + n[2] = t[0] - A[0]; + n[3] = t[1] - A[1]; 0 > n[2] && (n[0] += n[2], n[2] = Math.abs(n[2])); 0 > n[3] && (n[1] += n[3], n[3] = Math.abs(n[3])); - if (v(n, G)) { - var K = m.outputs[u]; - u = c.inputs[l]; - if (K && u && (m = K.dir || (m.horizontal ? e.DOWN : e.RIGHT), u = u.dir || (c.horizontal ? e.UP : e.LEFT), this.renderLink(a, A, r, g, !1, 0, null, m, u), g && g._last_time && 1000 > b - g._last_time)) { - K = 2.0 - 0.002 * (b - g._last_time); - var p = a.globalAlpha; - a.globalAlpha = p * K; - this.renderLink(a, A, r, g, !0, K, "white", m, u); - a.globalAlpha = p; + if (w(n, G)) { + var q = m.outputs[r]; + r = c.inputs[l]; + if (q && r && (m = q.dir || (m.horizontal ? e.DOWN : e.RIGHT), r = r.dir || (c.horizontal ? e.UP : e.LEFT), this.renderLink(a, A, t, g, !1, 0, null, m, r), g && g._last_time && 1000 > b - g._last_time)) { + q = 2.0 - 0.002 * (b - g._last_time); + var M = a.globalAlpha; + a.globalAlpha = M * q; + this.renderLink(a, A, t, g, !0, q, "white", m, r); + a.globalAlpha = M; } } } @@ -3343,24 +3343,24 @@ $jscomp.polyfill("Object.values", function(w) { } a.globalAlpha = 1; }; - l.prototype.renderLink = function(a, b, d, h, f, c, k, n, q, g) { + l.prototype.renderLink = function(a, b, d, h, f, c, k, n, p, g) { h && this.visible_links.push(h); !k && h && (k = h.color || l.link_type_colors[h.type]); k || (k = this.default_link_color); null != h && this.highlighted_links[h.id] && (k = "#FFF"); n = n || e.RIGHT; - q = q || e.LEFT; + p = p || e.LEFT; var x = B(b, d); this.render_connections_border && 0.6 < this.ds.scale && (a.lineWidth = this.connections_width + 4); a.lineJoin = "round"; g = g || 1; 1 < g && (a.lineWidth = 0.5); a.beginPath(); - for (var r = 0; r < g; r += 1) { - var m = 5 * (r - 0.5 * (g - 1)); + for (var t = 0; t < g; t += 1) { + var m = 5 * (t - 0.5 * (g - 1)); if (this.links_render_mode == e.SPLINE_LINK) { a.moveTo(b[0], b[1] + m); - var H = 0, u = 0, p = 0, I = 0; + var H = 0, r = 0, q = 0, I = 0; switch(n) { case e.LEFT: H = -0.25 * x; @@ -3369,17 +3369,17 @@ $jscomp.polyfill("Object.values", function(w) { H = 0.25 * x; break; case e.UP: - u = -0.25 * x; + r = -0.25 * x; break; case e.DOWN: - u = 0.25 * x; + r = 0.25 * x; } - switch(q) { + switch(p) { case e.LEFT: - p = -0.25 * x; + q = -0.25 * x; break; case e.RIGHT: - p = 0.25 * x; + q = 0.25 * x; break; case e.UP: I = -0.25 * x; @@ -3387,11 +3387,11 @@ $jscomp.polyfill("Object.values", function(w) { case e.DOWN: I = 0.25 * x; } - a.bezierCurveTo(b[0] + H, b[1] + u + m, d[0] + p, d[1] + I + m, d[0], d[1] + m); + a.bezierCurveTo(b[0] + H, b[1] + r + m, d[0] + q, d[1] + I + m, d[0], d[1] + m); } else { if (this.links_render_mode == e.LINEAR_LINK) { a.moveTo(b[0], b[1] + m); - I = p = u = H = 0; + I = q = r = H = 0; switch(n) { case e.LEFT: H = -1; @@ -3400,17 +3400,17 @@ $jscomp.polyfill("Object.values", function(w) { H = 1; break; case e.UP: - u = -1; + r = -1; break; case e.DOWN: - u = 1; + r = 1; } - switch(q) { + switch(p) { case e.LEFT: - p = -1; + q = -1; break; case e.RIGHT: - p = 1; + q = 1; break; case e.UP: I = -1; @@ -3418,12 +3418,12 @@ $jscomp.polyfill("Object.values", function(w) { case e.DOWN: I = 1; } - a.lineTo(b[0] + 15 * H, b[1] + 15 * u + m); - a.lineTo(d[0] + 15 * p, d[1] + 15 * I + m); + a.lineTo(b[0] + 15 * H, b[1] + 15 * r + m); + a.lineTo(d[0] + 15 * q, d[1] + 15 * I + m); a.lineTo(d[0], d[1] + m); } else { if (this.links_render_mode == e.STRAIGHT_LINK) { - a.moveTo(b[0], b[1]), m = b[0], H = b[1], u = d[0], p = d[1], n == e.RIGHT ? m += 10 : H += 10, q == e.LEFT ? u -= 10 : p -= 10, a.lineTo(m, H), a.lineTo(0.5 * (m + u), H), a.lineTo(0.5 * (m + u), p), a.lineTo(u, p), a.lineTo(d[0], d[1]); + a.moveTo(b[0], b[1]), m = b[0], H = b[1], r = d[0], q = d[1], n == e.RIGHT ? m += 10 : H += 10, p == e.LEFT ? r -= 10 : q -= 10, a.lineTo(m, H), a.lineTo(0.5 * (m + r), H), a.lineTo(0.5 * (m + r), q), a.lineTo(r, q), a.lineTo(d[0], d[1]); } else { return; } @@ -3434,13 +3434,13 @@ $jscomp.polyfill("Object.values", function(w) { a.lineWidth = this.connections_width; a.fillStyle = a.strokeStyle = k; a.stroke(); - f = this.computeConnectionPoint(b, d, 0.5, n, q); + f = this.computeConnectionPoint(b, d, 0.5, n, p); h && h._pos && (h._pos[0] = f[0], h._pos[1] = f[1]); - 0.6 <= this.ds.scale && this.highquality_render && q != e.CENTER && (this.render_connection_arrows && (r = this.computeConnectionPoint(b, d, 0.25, n, q), x = this.computeConnectionPoint(b, d, 0.26, n, q), h = this.computeConnectionPoint(b, d, 0.75, n, q), g = this.computeConnectionPoint(b, d, 0.76, n, q), this.render_curved_connections ? (x = -Math.atan2(x[0] - r[0], x[1] - r[1]), g = -Math.atan2(g[0] - h[0], g[1] - h[1])) : g = x = d[1] > b[1] ? 0 : Math.PI, a.save(), a.translate(r[0], r[1]), + 0.6 <= this.ds.scale && this.highquality_render && p != e.CENTER && (this.render_connection_arrows && (t = this.computeConnectionPoint(b, d, 0.25, n, p), x = this.computeConnectionPoint(b, d, 0.26, n, p), h = this.computeConnectionPoint(b, d, 0.75, n, p), g = this.computeConnectionPoint(b, d, 0.76, n, p), this.render_curved_connections ? (x = -Math.atan2(x[0] - t[0], x[1] - t[1]), g = -Math.atan2(g[0] - h[0], g[1] - h[1])) : g = x = d[1] > b[1] ? 0 : Math.PI, a.save(), a.translate(t[0], t[1]), a.rotate(x), a.beginPath(), a.moveTo(-5, -3), a.lineTo(0, 7), a.lineTo(5, -3), a.fill(), a.restore(), a.save(), a.translate(h[0], h[1]), a.rotate(g), a.beginPath(), a.moveTo(-5, -3), a.lineTo(0, 7), a.lineTo(5, -3), a.fill(), a.restore()), a.beginPath(), a.arc(f[0], f[1], 5, 0, 2 * Math.PI), a.fill()); if (c) { - for (a.fillStyle = k, r = 0; 5 > r; ++r) { - c = (0.001 * e.getTime() + 0.2 * r) % 1, f = this.computeConnectionPoint(b, d, c, n, q), a.beginPath(), a.arc(f[0], f[1], 5, 0, 2 * Math.PI), a.fill(); + for (a.fillStyle = k, t = 0; 5 > t; ++t) { + c = (0.001 * e.getTime() + 0.2 * t) % 1, f = this.computeConnectionPoint(b, d, c, n, p), a.beginPath(), a.arc(f[0], f[1], 5, 0, 2 * Math.PI), a.fill(); } } }; @@ -3505,70 +3505,70 @@ $jscomp.polyfill("Object.values", function(w) { var k = e.NODE_WIDGET_HEIGHT, n = 0.5 < this.ds.scale; d.save(); d.globalAlpha = this.editor_alpha; - for (var q = e.WIDGET_OUTLINE_COLOR, l = e.WIDGET_BGCOLOR, g = e.WIDGET_TEXT_COLOR, r = e.WIDGET_SECONDARY_TEXT_COLOR, m = 0; m < c.length; ++m) { - var u = k, p = c[m], t = b; - p.y && (t = p.y); - p.last_y = t; - d.strokeStyle = q; + for (var p = e.WIDGET_OUTLINE_COLOR, l = e.WIDGET_BGCOLOR, g = e.WIDGET_TEXT_COLOR, t = e.WIDGET_SECONDARY_TEXT_COLOR, m = 0; m < c.length; ++m) { + var r = c[m], q = b; + r.y && (q = r.y); + r.last_y = q; + d.strokeStyle = p; d.fillStyle = "#222"; d.textAlign = "left"; - p.disabled && (d.globalAlpha *= 0.5); - switch(p.type) { + r.disabled && (d.globalAlpha *= 0.5); + switch(r.type) { case "button": - p.clicked && (d.fillStyle = "#AAA", p.clicked = !1, this.dirty_canvas = !0); - d.fillRect(15, t, f - 30, k); - n && d.strokeRect(15, t, f - 30, k); - n && (d.textAlign = "center", d.fillStyle = g, d.fillText(p.name, 0.5 * f, t + 0.7 * k)); + r.clicked && (d.fillStyle = "#AAA", r.clicked = !1, this.dirty_canvas = !0); + d.fillRect(15, q, f - 30, k); + n && d.strokeRect(15, q, f - 30, k); + n && (d.textAlign = "center", d.fillStyle = g, d.fillText(r.name, 0.5 * f, q + 0.7 * k)); break; case "toggle": d.textAlign = "left"; - d.strokeStyle = q; + d.strokeStyle = p; d.fillStyle = l; d.beginPath(); n ? d.roundRect(15, b, f - 30, k, 0.5 * k) : d.rect(15, b, f - 30, k); d.fill(); n && d.stroke(); - d.fillStyle = p.value ? "#89A" : "#333"; + d.fillStyle = r.value ? "#89A" : "#333"; d.beginPath(); - d.arc(f - 30, t + 0.5 * k, 0.36 * k, 0, 2 * Math.PI); + d.arc(f - 30, q + 0.5 * k, 0.36 * k, 0, 2 * Math.PI); d.fill(); - n && (d.fillStyle = r, null != p.name && d.fillText(p.name, 30, t + 0.7 * k), d.fillStyle = p.value ? g : r, d.textAlign = "right", d.fillText(p.value ? p.options.on || "true" : p.options.off || "false", f - 40, t + 0.7 * k)); + n && (d.fillStyle = t, null != r.name && d.fillText(r.name, 30, q + 0.7 * k), d.fillStyle = r.value ? g : t, d.textAlign = "right", d.fillText(r.value ? r.options.on || "true" : r.options.off || "false", f - 40, q + 0.7 * k)); break; case "slider": d.fillStyle = l; - d.fillRect(15, t, f - 30, k); - var C = p.options.max - p.options.min, v = (p.value - p.options.min) / C; - d.fillStyle = h == p ? "#89A" : "#678"; - d.fillRect(15, t, v * (f - 30), k); - n && d.strokeRect(15, t, f - 30, k); - p.marker && (C = (p.marker - p.options.min) / C, d.fillStyle = "#AA9", d.fillRect(15 + C * (f - 30), t, 2, k)); - n && (d.textAlign = "center", d.fillStyle = g, d.fillText(p.name + " " + Number(p.value).toFixed(3), 0.5 * f, t + 0.7 * k)); + d.fillRect(15, q, f - 30, k); + var u = r.options.max - r.options.min, C = (r.value - r.options.min) / u; + d.fillStyle = h == r ? "#89A" : "#678"; + d.fillRect(15, q, C * (f - 30), k); + n && d.strokeRect(15, q, f - 30, k); + r.marker && (u = (r.marker - r.options.min) / u, d.fillStyle = "#AA9", d.fillRect(15 + u * (f - 30), q, 2, k)); + n && (d.textAlign = "center", d.fillStyle = g, d.fillText(r.name + " " + Number(r.value).toFixed(3), 0.5 * f, q + 0.7 * k)); break; case "number": case "combo": d.textAlign = "left"; - d.strokeStyle = q; + d.strokeStyle = p; d.fillStyle = l; d.beginPath(); n ? d.roundRect(15, b, f - 30, k, 0.5 * k) : d.rect(15, b, f - 30, k); d.fill(); - n && (d.stroke(), d.fillStyle = g, d.beginPath(), d.moveTo(31, b + 5), d.lineTo(21, b + 0.5 * k), d.lineTo(31, b + k - 5), d.fill(), d.beginPath(), d.moveTo(f - 15 - 16, b + 5), d.lineTo(f - 15 - 6, b + 0.5 * k), d.lineTo(f - 15 - 16, b + k - 5), d.fill(), d.fillStyle = r, d.fillText(p.name, 35, t + 0.7 * k), d.fillStyle = g, d.textAlign = "right", "number" == p.type ? d.fillText(Number(p.value).toFixed(void 0 !== p.options.precision ? p.options.precision : 3), f - 30 - 20, t + 0.7 * k) : - (C = p.value, p.options.values && (v = p.options.values, v.constructor === Function && (v = v()), v && v.constructor !== Array && (C = v[p.value])), d.fillText(C, f - 30 - 20, t + 0.7 * k))); + n && (d.stroke(), d.fillStyle = g, d.beginPath(), d.moveTo(31, b + 5), d.lineTo(21, b + 0.5 * k), d.lineTo(31, b + k - 5), d.fill(), d.beginPath(), d.moveTo(f - 15 - 16, b + 5), d.lineTo(f - 15 - 6, b + 0.5 * k), d.lineTo(f - 15 - 16, b + k - 5), d.fill(), d.fillStyle = t, d.fillText(r.name, 35, q + 0.7 * k), d.fillStyle = g, d.textAlign = "right", "number" == r.type ? d.fillText(Number(r.value).toFixed(void 0 !== r.options.precision ? r.options.precision : 3), f - 30 - 20, q + 0.7 * k) : + (u = r.value, r.options.values && (C = r.options.values, C.constructor === Function && (C = C()), C && C.constructor !== Array && (u = C[r.value])), d.fillText(u, f - 30 - 20, q + 0.7 * k))); break; case "string": case "text": d.textAlign = "left"; - d.strokeStyle = q; + d.strokeStyle = p; d.fillStyle = l; d.beginPath(); n ? d.roundRect(15, b, f - 30, k, 0.5 * k) : d.rect(15, b, f - 30, k); d.fill(); - n && (d.save(), d.beginPath(), d.rect(15, b, f - 30, k), d.clip(), d.stroke(), d.fillStyle = r, null != p.name && d.fillText(p.name, 30, t + 0.7 * k), d.fillStyle = g, d.textAlign = "right", d.fillText(String(p.value).substr(0, 30), f - 30, t + 0.7 * k), d.restore()); + n && (d.save(), d.beginPath(), d.rect(15, b, f - 30, k), d.clip(), d.stroke(), d.fillStyle = t, null != r.name && d.fillText(r.name, 30, q + 0.7 * k), d.fillStyle = g, d.textAlign = "right", d.fillText(String(r.value).substr(0, 30), f - 30, q + 0.7 * k), d.restore()); break; default: - p.draw && (u = p.draw(d, a, f, t, k) || k); + r.draw && r.draw(d, a, f, q, k); } - b += u + 4; + b += (r.computeSize ? r.computeSize(f)[1] : k) + 4; d.globalAlpha = this.editor_alpha; } d.restore(); @@ -3578,88 +3578,91 @@ $jscomp.polyfill("Object.values", function(w) { function f(f, h) { f.value = h; f.options && f.options.property && void 0 !== a.properties[f.options.property] && a.setProperty(f.options.property, h); - f.callback && f.callback(f.value, q, a, b, d); + f.callback && f.callback(f.value, p, a, b, d); } if (!a.widgets || !a.widgets.length) { return null; } - for (var h = b[0] - a.pos[0], k = b[1] - a.pos[1], n = a.size[0], q = this, l = this.getCanvasWindow(), g = 0; g < a.widgets.length; ++g) { - var r = a.widgets[g]; - if (r && !r.disabled && (r == c || 6 < h && h < n - 12 && k > r.last_y && k < r.last_y + e.NODE_WIDGET_HEIGHT)) { - switch(r.type) { - case "button": - if ("mousemove" === d.type) { - break; - } - r.callback && setTimeout(function() { - r.callback(r, q, a, b, d); - }, 20); - this.dirty_canvas = r.clicked = !0; - break; - case "slider": - l = Math.clamp((h - 10) / (n - 20), 0, 1); - r.value = r.options.min + (r.options.max - r.options.min) * l; - r.callback && setTimeout(function() { - f(r, r.value); - }, 20); - this.dirty_canvas = !0; - break; - case "number": - case "combo": - c = r.value; - if ("mousemove" == d.type && "number" == r.type) { - r.value += 0.1 * d.deltaX * (r.options.step || 1), null != r.options.min && r.value < r.options.min && (r.value = r.options.min), null != r.options.max && r.value > r.options.max && (r.value = r.options.max); - } else { - if ("mousedown" == d.type) { - var m = r.options.values; - m && m.constructor === Function && (m = r.options.values(r, a)); - var p = null; - "number" != r.type && (p = m.constructor === Array ? m : Object.keys(m)); - h = 40 > h ? -1 : h > n - 40 ? 1 : 0; - if ("number" == r.type) { - r.value += 0.1 * h * (r.options.step || 1), null != r.options.min && r.value < r.options.min && (r.value = r.options.min), null != r.options.max && r.value > r.options.max && (r.value = r.options.max); - } else { - if (h) { - l = -1, l = m.constructor === Object ? p.indexOf(String(r.value)) + h : p.indexOf(r.value) + h, l >= p.length && (l = p.length - 1), 0 > l && (l = 0), r.value = m.constructor === Array ? m[l] : l; - } else { - var u = m != p ? Object.values(m) : m; - new e.ContextMenu(u, {scale:Math.max(1, this.ds.scale), event:d, className:"dark", callback:function(a, b, d) { - m != p && (a = u.indexOf(a)); - this.value = a; - f(this, a); - q.dirty_canvas = !0; - return !1; - }.bind(r)}, l); - } - } - } else { - "mouseup" == d.type && "number" == r.type && (h = 40 > h ? -1 : h > n - 40 ? 1 : 0, 200 > d.click_time && 0 == h && this.prompt("Value", r.value, function(a) { - this.value = Number(a); - f(this, this.value); - }.bind(r), d)); + for (var h = b[0] - a.pos[0], k = b[1] - a.pos[1], n = a.size[0], p = this, l = this.getCanvasWindow(), g = 0; g < a.widgets.length; ++g) { + var t = a.widgets[g]; + if (t && !t.disabled) { + var m = t.computeSize ? t.computeSize(n)[1] : e.NODE_WIDGET_HEIGHT; + if (t == c || 6 < h && h < n - 12 && k > t.last_y && k < t.last_y + m) { + switch(t.type) { + case "button": + if ("mousemove" === d.type) { + break; } - } - c != r.value && setTimeout(function() { - f(this, this.value); - }.bind(r), 20); - this.dirty_canvas = !0; - break; - case "toggle": - "mousedown" == d.type && (r.value = !r.value, setTimeout(function() { - f(r, r.value); - }, 20)); - break; - case "string": - case "text": - "mousedown" == d.type && this.prompt("Value", r.value, function(a) { - this.value = a; - f(this, a); - }.bind(r), d); - break; - default: - r.mouse && r.mouse(ctx, d, [h, k], a); + t.callback && setTimeout(function() { + t.callback(t, p, a, b, d); + }, 20); + this.dirty_canvas = t.clicked = !0; + break; + case "slider": + l = Math.clamp((h - 10) / (n - 20), 0, 1); + t.value = t.options.min + (t.options.max - t.options.min) * l; + t.callback && setTimeout(function() { + f(t, t.value); + }, 20); + this.dirty_canvas = !0; + break; + case "number": + case "combo": + c = t.value; + if ("mousemove" == d.type && "number" == t.type) { + t.value += 0.1 * d.deltaX * (t.options.step || 1), null != t.options.min && t.value < t.options.min && (t.value = t.options.min), null != t.options.max && t.value > t.options.max && (t.value = t.options.max); + } else { + if ("mousedown" == d.type) { + var r = t.options.values; + r && r.constructor === Function && (r = t.options.values(t, a)); + var q = null; + "number" != t.type && (q = r.constructor === Array ? r : Object.keys(r)); + h = 40 > h ? -1 : h > n - 40 ? 1 : 0; + if ("number" == t.type) { + t.value += 0.1 * h * (t.options.step || 1), null != t.options.min && t.value < t.options.min && (t.value = t.options.min), null != t.options.max && t.value > t.options.max && (t.value = t.options.max); + } else { + if (h) { + l = -1, l = r.constructor === Object ? q.indexOf(String(t.value)) + h : q.indexOf(t.value) + h, l >= q.length && (l = q.length - 1), 0 > l && (l = 0), t.value = r.constructor === Array ? r[l] : l; + } else { + var u = r != q ? Object.values(r) : r; + new e.ContextMenu(u, {scale:Math.max(1, this.ds.scale), event:d, className:"dark", callback:function(a, b, d) { + r != q && (a = u.indexOf(a)); + this.value = a; + f(this, a); + p.dirty_canvas = !0; + return !1; + }.bind(t)}, l); + } + } + } else { + "mouseup" == d.type && "number" == t.type && (h = 40 > h ? -1 : h > n - 40 ? 1 : 0, 200 > d.click_time && 0 == h && this.prompt("Value", t.value, function(a) { + this.value = Number(a); + f(this, this.value); + }.bind(t), d)); + } + } + c != t.value && setTimeout(function() { + f(this, this.value); + }.bind(t), 20); + this.dirty_canvas = !0; + break; + case "toggle": + "mousedown" == d.type && (t.value = !t.value, setTimeout(function() { + f(t, t.value); + }, 20)); + break; + case "string": + case "text": + "mousedown" == d.type && this.prompt("Value", t.value, function(a) { + this.value = a; + f(this, a); + }.bind(t), d); + break; + default: + t.mouse && (this.dirty_canvas = t.mouse(d, [h, k], a)); + } + return t; } - return r; } } return null; @@ -3671,7 +3674,7 @@ $jscomp.polyfill("Object.values", function(w) { b.globalAlpha = 0.5 * this.editor_alpha; for (var d = 0; d < a.length; ++d) { var h = a[d]; - if (v(this.visible_area, h._bounding)) { + if (w(this.visible_area, h._bounding)) { b.fillStyle = h.color || "#335"; b.strokeStyle = h.color || "#335"; var f = h._pos, c = h._size; @@ -3761,8 +3764,8 @@ $jscomp.polyfill("Object.values", function(w) { var k = l.active_canvas, n = k.getCanvasWindow(); a = e.getNodeTypesCategories(k.filter); b = []; - for (var q in a) { - a[q] && b.push({value:a[q], content:a[q], has_submenu:!0}); + for (var p in a) { + a[p] && b.push({value:a[p], content:a[p], has_submenu:!0}); } var g = new e.ContextMenu(b, {event:d, callback:function(a, b, d) { a = e.getNodeTypesInCategory(a.value, k.filter); @@ -3788,12 +3791,12 @@ $jscomp.polyfill("Object.values", function(w) { var k = []; if (b) { for (var n in b) { - var q = b[n]; - if (q) { - var g = q[0]; - q[2] && q[2].label && (g = q[2].label); - g = {content:g, value:q}; - q[1] == e.ACTION && (g.className = "event"); + var p = b[n]; + if (p) { + var g = p[0]; + p[2] && p[2].label && (g = p[2].label); + g = {content:g, value:p}; + p[1] == e.ACTION && (g.className = "event"); k.push(g); } else { k.push(null); @@ -3830,8 +3833,8 @@ $jscomp.polyfill("Object.values", function(w) { f.onGetOutputs && (b = f.onGetOutputs()); var n = []; if (b) { - for (var q in b) { - var g = b[q]; + for (var p in b) { + var g = b[p]; if (!g) { n.push(null); } else { @@ -3914,9 +3917,9 @@ $jscomp.polyfill("Object.values", function(w) { })); b = l.active_canvas.canvas; d = b.getBoundingClientRect(); - var q = c = -20; - d && (c -= d.left, q -= d.top); - event ? (k.style.left = event.clientX + c + "px", k.style.top = event.clientY + q + "px") : (k.style.left = 0.5 * b.width + c + "px", k.style.top = 0.5 * b.height + q + "px"); + var p = c = -20; + d && (c -= d.left, p -= d.top); + event ? (k.style.left = event.clientX + c + "px", k.style.top = event.clientY + p + "px") : (k.style.left = 0.5 * b.width + c + "px", k.style.top = 0.5 * b.height + p + "px"); k.querySelector("button").addEventListener("click", h); b.parentNode.appendChild(k); }; @@ -3960,9 +3963,9 @@ $jscomp.polyfill("Object.values", function(w) { }); a = l.active_canvas.canvas; b = a.getBoundingClientRect(); - var n = -20, q = -20; - b && (n -= b.left, q -= b.top); - c ? (e.style.left = c.clientX + n + "px", e.style.top = c.clientY + q + "px") : (e.style.left = 0.5 * a.width + n + "px", e.style.top = 0.5 * a.height + q + "px"); + var n = -20, p = -20; + b && (n -= b.left, p -= b.top); + c ? (e.style.left = c.clientX + n + "px", e.style.top = c.clientY + p + "px") : (e.style.left = 0.5 * a.width + n + "px", e.style.top = 0.5 * a.height + p + "px"); a.parentNode.appendChild(e); setTimeout(function() { k.focus(); @@ -4005,15 +4008,15 @@ $jscomp.polyfill("Object.values", function(w) { g.close(); } function d(a) { - var b = t; - t && t.classList.remove("selected"); - t ? (t = a ? t.nextSibling : t.previousSibling) || (t = b) : t = a ? p.childNodes[0] : p.childNodes[p.childNodes.length]; - t && (t.classList.add("selected"), t.scrollIntoView({block:"end", behavior:"smooth"})); + var b = u; + u && u.classList.remove("selected"); + u ? (u = a ? u.nextSibling : u.previousSibling) || (u = b) : u = a ? r.childNodes[0] : r.childNodes[r.childNodes.length]; + u && (u.classList.add("selected"), u.scrollIntoView({block:"end", behavior:"smooth"})); } function c() { function a(a, d) { var f = document.createElement("div"); - r || (r = a); + t || (t = a); f.innerText = a; f.dataset.type = escape(a); f.className = "litegraph lite-search-item"; @@ -4021,15 +4024,15 @@ $jscomp.polyfill("Object.values", function(w) { f.addEventListener("click", function(a) { b(unescape(this.dataset.type)); }); - p.appendChild(f); + r.appendChild(f); } - u = null; + q = null; var d = C.value; - r = null; - p.innerHTML = ""; + t = null; + r.innerHTML = ""; if (d) { if (f.onSearchBox) { - var c = f.onSearchBox(p, d, k); + var c = f.onSearchBox(r, d, k); if (c) { for (var h = 0; h < c.length; ++h) { a(c[h]); @@ -4038,16 +4041,16 @@ $jscomp.polyfill("Object.values", function(w) { } else { c = function(a) { var b = e.registered_node_types[a]; - return q && b.filter != q ? !1 : -1 !== a.toLowerCase().indexOf(d); + return p && b.filter != p ? !1 : -1 !== a.toLowerCase().indexOf(d); }; var n = 0; d = d.toLowerCase(); - var q = k.filter || k.graph.filter; + var p = k.filter || k.graph.filter; for (h in e.searchbox_extras) { var g = e.searchbox_extras[h]; if (-1 !== g.desc.toLowerCase().indexOf(d)) { - var m = e.registered_node_types[g.type]; - if (!m || !m.filter || m.filter == q) { + var x = e.registered_node_types[g.type]; + if (!x || !x.filter || x.filter == p) { if (a(g.desc, "searchbox_extra"), -1 !== l.search_limit && n++ > l.search_limit) { break; } @@ -4067,13 +4070,13 @@ $jscomp.polyfill("Object.values", function(w) { } } } - var f = this, k = l.active_canvas, n = k.canvas, q = n.ownerDocument || document, g = document.createElement("div"); + var f = this, k = l.active_canvas, n = k.canvas, p = n.ownerDocument || document, g = document.createElement("div"); g.className = "litegraph litesearchbox graphdialog rounded"; g.innerHTML = "Search
"; g.close = function() { f.search_box = null; - q.body.focus(); - q.body.style.overflow = ""; + p.body.focus(); + p.body.style.overflow = ""; setTimeout(function() { f.canvas.focus(); }, 20); @@ -4091,7 +4094,7 @@ $jscomp.polyfill("Object.values", function(w) { }); f.search_box && f.search_box.close(); f.search_box = g; - var p = g.querySelector(".helper"), r = null, u = null, t = null, C = g.querySelector("input"); + var r = g.querySelector(".helper"), t = null, q = null, u = null, C = g.querySelector("input"); C && (C.addEventListener("blur", function(a) { this.focus(); }), C.addEventListener("keydown", function(a) { @@ -4105,10 +4108,10 @@ $jscomp.polyfill("Object.values", function(w) { g.close(); } else { if (13 == a.keyCode) { - t ? b(t.innerHTML) : r ? b(r) : g.close(); + u ? b(u.innerHTML) : t ? b(t) : g.close(); } else { - u && clearInterval(u); - u = setTimeout(c, 10); + q && clearInterval(q); + q = setTimeout(c, 10); return; } } @@ -4119,18 +4122,18 @@ $jscomp.polyfill("Object.values", function(w) { a.stopImmediatePropagation(); return !0; })); - q.fullscreenElement ? q.fullscreenElement.appendChild(g) : (q.body.appendChild(g), q.body.style.overflow = "hidden"); + p.fullscreenElement ? p.fullscreenElement.appendChild(g) : (p.body.appendChild(g), p.body.style.overflow = "hidden"); n = n.getBoundingClientRect(); - var v = (a ? a.clientY : n.top + 0.5 * n.height) - 20; + var w = (a ? a.clientY : n.top + 0.5 * n.height) - 20; g.style.left = (a ? a.clientX : n.left + 0.5 * n.width) - 80 + "px"; - g.style.top = v + "px"; - a.layerY > n.height - 200 && (p.style.maxHeight = n.height - a.layerY - 20 + "px"); + g.style.top = w + "px"; + a.layerY > n.height - 200 && (r.style.maxHeight = n.height - a.layerY - 20 + "px"); C.focus(); return g; }; l.prototype.showEditPropertyValue = function(a, b, d) { function c() { - f(r.value); + f(t.value); } function f(f) { "number" == typeof a.properties[b] && (f = Number(f)); @@ -4156,9 +4159,9 @@ $jscomp.polyfill("Object.values", function(w) { } else { if ("enum" == k && e.values) { n = ""; } else { @@ -4172,20 +4175,20 @@ $jscomp.polyfill("Object.values", function(w) { } var l = this.createDialog("" + b + "" + n + "", d); if ("enum" == k && e.values) { - var r = l.querySelector("select"); - r.addEventListener("change", function(a) { + var t = l.querySelector("select"); + t.addEventListener("change", function(a) { f(a.target.value); }); } else { if ("boolean" == k) { - (r = l.querySelector("input")) && r.addEventListener("click", function(a) { - f(!!r.checked); + (t = l.querySelector("input")) && t.addEventListener("click", function(a) { + f(!!t.checked); }); } else { - if (r = l.querySelector("input")) { - r.addEventListener("blur", function(a) { + if (t = l.querySelector("input")) { + t.addEventListener("blur", function(a) { this.focus(); - }), g = void 0 !== a.properties[b] ? a.properties[b] : "", g = JSON.stringify(g), r.value = g, r.addEventListener("keydown", function(a) { + }), g = void 0 !== a.properties[b] ? a.properties[b] : "", g = JSON.stringify(g), t.value = g, t.addEventListener("keydown", function(a) { 13 == a.keyCode && (c(), a.preventDefault(), a.stopPropagation()); }); } @@ -4365,7 +4368,7 @@ $jscomp.polyfill("Object.values", function(w) { e.isInsideBounding = function(a, b) { return a[0] < b[0][0] || a[1] < b[0][1] || a[0] > b[1][0] || a[1] > b[1][1] ? !1 : !0; }; - e.overlapBounding = v; + e.overlapBounding = w; e.hex2num = function(a) { "#" == a.charAt(0) && (a = a.slice(1)); a = a.toUpperCase(); @@ -4549,14 +4552,14 @@ $jscomp.polyfill("Object.values", function(w) { return -1; } b = b || 30; - for (var c = this.size[0] - 2 * this.margin, f = this.size[1] - 2 * this.margin, e = d.length, k = [0, 0], n = 1000000, q = -1, g = 0; g < e; ++g) { + for (var c = this.size[0] - 2 * this.margin, f = this.size[1] - 2 * this.margin, e = d.length, k = [0, 0], n = 1000000, p = -1, g = 0; g < e; ++g) { var l = d[g]; k[0] = l[0] * c; k[1] = (1.0 - l[1]) * f; l = vec2.distance(a, k); - l > n || l > b || (q = g, n = l); + l > n || l > b || (p = g, n = l); } - return q; + return p; }; e.CurveEditor = z; e.getParameterNames = function(a) { @@ -4570,12 +4573,12 @@ $jscomp.polyfill("Object.values", function(w) { }); })(this); "undefined" != typeof exports && (exports.LiteGraph = this.LiteGraph); -(function(w) { +(function(v) { function c() { this.addOutput("in ms", "number"); this.addOutput("in sec", "number"); } - function p() { + function q() { this.size = [140, 80]; this.properties = {enabled:!0}; this.enabled = !0; @@ -4634,7 +4637,7 @@ $jscomp.polyfill("Object.values", function(w) { this.widgets_up = !0; this.size = [180, 60]; } - function u() { + function r() { this.addOutput("value", "number"); this.addProperty("value", 1.0); this.widget = this.addWidget("number", "value", 1, "value"); @@ -4663,7 +4666,7 @@ $jscomp.polyfill("Object.values", function(w) { this.widget = this.addWidget("text", "url", "", "url"); this._data = null; } - function v() { + function w() { this.addOutput("", ""); this.addProperty("value", ""); this.widget = this.addWidget("text", "json", "", "value"); @@ -4708,7 +4711,7 @@ $jscomp.polyfill("Object.values", function(w) { this.addOutput("keys", "array"); this.size = [140, 30]; } - function t() { + function u() { this.addInput("A", "object"); this.addInput("B", "object"); this.addOutput("", "object"); @@ -4737,7 +4740,7 @@ $jscomp.polyfill("Object.values", function(w) { a.value && a.downloadAsFile(); }); } - function q() { + function p() { this.size = [60, 30]; this.addInput("value", 0, {label:""}); this.value = 0; @@ -4774,7 +4777,7 @@ $jscomp.polyfill("Object.values", function(w) { this._func = null; this.data = {}; } - var h = w.LiteGraph; + var h = v.LiteGraph; c.title = "Time"; c.desc = "Time"; c.prototype.onExecute = function() { @@ -4782,13 +4785,13 @@ $jscomp.polyfill("Object.values", function(w) { this.setOutputData(1, this.graph.globaltime); }; h.registerNodeType("basic/time", c); - p.title = "Subgraph"; - p.desc = "Graph inside a node"; - p.title_color = "#334"; - p.prototype.onGetInputs = function() { + q.title = "Subgraph"; + q.desc = "Graph inside a node"; + q.title_color = "#334"; + q.prototype.onGetInputs = function() { return [["enabled", "boolean"]]; }; - p.prototype.onDrawTitle = function(a) { + q.prototype.onDrawTitle = function(a) { if (!this.flags.collapsed) { a.fillStyle = "#555"; var b = h.NODE_TITLE_HEIGHT, d = this.size[0] - b; @@ -4801,13 +4804,13 @@ $jscomp.polyfill("Object.values", function(w) { a.fill(); } }; - p.prototype.onDblClick = function(a, b, d) { + q.prototype.onDblClick = function(a, b, d) { var f = this; setTimeout(function() { d.openSubgraph(f.subgraph); }, 10); }; - p.prototype.onMouseDown = function(a, b, d) { + q.prototype.onMouseDown = function(a, b, d) { if (!this.flags.collapsed && b[0] > this.size[0] - h.NODE_TITLE_HEIGHT && 0 > b[1]) { var f = this; setTimeout(function() { @@ -4815,10 +4818,10 @@ $jscomp.polyfill("Object.values", function(w) { }, 10); } }; - p.prototype.onAction = function(a, b) { + q.prototype.onAction = function(a, b) { this.subgraph.onAction(a, b); }; - p.prototype.onExecute = function() { + q.prototype.onExecute = function() { if (this.enabled = this.getInputOrProperty("enabled")) { if (this.inputs) { for (var a = 0; a < this.inputs.length; a++) { @@ -4834,58 +4837,58 @@ $jscomp.polyfill("Object.values", function(w) { } } }; - p.prototype.sendEventToAllNodes = function(a, b, d) { + q.prototype.sendEventToAllNodes = function(a, b, d) { this.enabled && this.subgraph.sendEventToAllNodes(a, b, d); }; - p.prototype.onSubgraphTrigger = function(a, b) { + q.prototype.onSubgraphTrigger = function(a, b) { a = this.findOutputSlot(a); -1 != a && this.triggerSlot(a); }; - p.prototype.onSubgraphNewInput = function(a, b) { + q.prototype.onSubgraphNewInput = function(a, b) { -1 == this.findInputSlot(a) && this.addInput(a, b); }; - p.prototype.onSubgraphRenamedInput = function(a, b) { + q.prototype.onSubgraphRenamedInput = function(a, b) { a = this.findInputSlot(a); -1 != a && (this.getInputInfo(a).name = b); }; - p.prototype.onSubgraphTypeChangeInput = function(a, b) { + q.prototype.onSubgraphTypeChangeInput = function(a, b) { a = this.findInputSlot(a); -1 != a && (this.getInputInfo(a).type = b); }; - p.prototype.onSubgraphRemovedInput = function(a) { + q.prototype.onSubgraphRemovedInput = function(a) { a = this.findInputSlot(a); -1 != a && this.removeInput(a); }; - p.prototype.onSubgraphNewOutput = function(a, b) { + q.prototype.onSubgraphNewOutput = function(a, b) { -1 == this.findOutputSlot(a) && this.addOutput(a, b); }; - p.prototype.onSubgraphRenamedOutput = function(a, b) { + q.prototype.onSubgraphRenamedOutput = function(a, b) { a = this.findOutputSlot(a); -1 != a && (this.getOutputInfo(a).name = b); }; - p.prototype.onSubgraphTypeChangeOutput = function(a, b) { + q.prototype.onSubgraphTypeChangeOutput = function(a, b) { a = this.findOutputSlot(a); -1 != a && (this.getOutputInfo(a).type = b); }; - p.prototype.onSubgraphRemovedOutput = function(a) { + q.prototype.onSubgraphRemovedOutput = function(a) { a = this.findInputSlot(a); -1 != a && this.removeOutput(a); }; - p.prototype.getExtraMenuOptions = function(a) { + q.prototype.getExtraMenuOptions = function(a) { var b = this; return [{content:"Open", callback:function() { a.openSubgraph(b.subgraph); }}]; }; - p.prototype.onResize = function(a) { + q.prototype.onResize = function(a) { a[1] += 20; }; - p.prototype.serialize = function() { + q.prototype.serialize = function() { var a = h.LGraphNode.prototype.serialize.call(this); a.subgraph = this.subgraph.serialize(); return a; }; - p.prototype.clone = function() { + q.prototype.clone = function() { var a = h.createNode(this.type), b = this.serialize(); delete b.id; delete b.inputs; @@ -4893,8 +4896,8 @@ $jscomp.polyfill("Object.values", function(w) { a.configure(b); return a; }; - h.Subgraph = p; - h.registerNodeType("graph/subgraph", p); + h.Subgraph = q; + h.registerNodeType("graph/subgraph", q); m.title = "Input"; m.desc = "Input of the graph"; m.prototype.onConfigure = function() { @@ -4950,28 +4953,28 @@ $jscomp.polyfill("Object.values", function(w) { }; h.GraphOutput = g; h.registerNodeType("graph/output", g); - u.title = "Const Number"; - u.desc = "Constant number"; - u.prototype.onExecute = function() { + r.title = "Const Number"; + r.desc = "Constant number"; + r.prototype.onExecute = function() { this.setOutputData(0, parseFloat(this.properties.value)); }; - u.prototype.getTitle = function() { + r.prototype.getTitle = function() { return this.flags.collapsed ? this.properties.value : this.title; }; - u.prototype.setValue = function(a) { + r.prototype.setValue = function(a) { this.setProperty("value", a); }; - u.prototype.onDrawBackground = function(a) { + r.prototype.onDrawBackground = function(a) { this.outputs[0].label = this.properties.value.toFixed(3); }; - h.registerNodeType("basic/const", u); + h.registerNodeType("basic/const", r); l.title = "Const Boolean"; l.desc = "Constant boolean"; - l.prototype.getTitle = u.prototype.getTitle; + l.prototype.getTitle = r.prototype.getTitle; l.prototype.onExecute = function() { this.setOutputData(0, this.properties.value); }; - l.prototype.setValue = u.prototype.setValue; + l.prototype.setValue = r.prototype.setValue; l.prototype.onGetInputs = function() { return [["toggle", h.ACTION]]; }; @@ -4981,11 +4984,11 @@ $jscomp.polyfill("Object.values", function(w) { h.registerNodeType("basic/boolean", l); B.title = "Const String"; B.desc = "Constant string"; - B.prototype.getTitle = u.prototype.getTitle; + B.prototype.getTitle = r.prototype.getTitle; B.prototype.onExecute = function() { this.setOutputData(0, this.properties.value); }; - B.prototype.setValue = u.prototype.setValue; + B.prototype.setValue = r.prototype.setValue; B.prototype.onDropFile = function(a) { var b = this, d = new FileReader; d.onload = function(a) { @@ -5005,7 +5008,7 @@ $jscomp.polyfill("Object.values", function(w) { !a || a == this._url && this._type == this.properties.type || this.fetchFile(a); this.setOutputData(0, this._data); }; - y.prototype.setValue = u.prototype.setValue; + y.prototype.setValue = r.prototype.setValue; y.prototype.fetchFile = function(a) { var b = this; a && a.constructor === String ? (this._url = a, this._type = this.properties.type, "http" == a.substr(0, 4) && h.proxy && (a = h.proxy + a.substr(a.indexOf(":") + 3)), fetch(a).then(function(a) { @@ -5058,9 +5061,9 @@ $jscomp.polyfill("Object.values", function(w) { } }; h.registerNodeType("basic/file", y); - v.title = "Const Data"; - v.desc = "Constant Data"; - v.prototype.onPropertyChanged = function(a, b) { + w.title = "Const Data"; + w.desc = "Constant Data"; + w.prototype.onPropertyChanged = function(a, b) { this.widget.value = b; if (null != b && "" != b) { try { @@ -5070,11 +5073,11 @@ $jscomp.polyfill("Object.values", function(w) { } } }; - v.prototype.onExecute = function() { + w.prototype.onExecute = function() { this.setOutputData(0, this._value); }; - v.prototype.setValue = u.prototype.setValue; - h.registerNodeType("basic/data", v); + w.prototype.setValue = r.prototype.setValue; + h.registerNodeType("basic/data", w); E.title = "Const Array"; E.desc = "Constant Array"; E.prototype.onPropertyChanged = function(a, b) { @@ -5098,7 +5101,7 @@ $jscomp.polyfill("Object.values", function(w) { } this.setOutputData(0, this._value); }; - E.prototype.setValue = u.prototype.setValue; + E.prototype.setValue = r.prototype.setValue; h.registerNodeType("basic/array", E); z.title = "Array[i]"; z.desc = "Returns an element from an array"; @@ -5141,9 +5144,9 @@ $jscomp.polyfill("Object.values", function(w) { null != a && this.setOutputData(0, Object.keys(a)); }; h.registerNodeType("basic/object_keys", D); - t.title = "Merge Objects"; - t.desc = "Creates an object copying properties from others"; - t.prototype.onExecute = function() { + u.title = "Merge Objects"; + u.desc = "Creates an object copying properties from others"; + u.prototype.onExecute = function() { var a = this.getInputData(0), b = this.getInputData(1), d = this._result; if (a) { for (var c in a) { @@ -5157,13 +5160,13 @@ $jscomp.polyfill("Object.values", function(w) { } this.setOutputData(0, d); }; - h.registerNodeType("basic/merge_objects", t); + h.registerNodeType("basic/merge_objects", u); G.title = "Variable"; G.desc = "store/read variable value"; G.prototype.onExecute = function() { this.value = this.getInputData(0); this.graph && (this.graph.vars[this.properties.varname] = this.value); - this.properties.global && (w[this.properties.varname] = this.value); + this.properties.global && (v[this.properties.varname] = this.value); this.setOutputData(0, this.value); }; G.prototype.getTitle = function() { @@ -5206,15 +5209,15 @@ $jscomp.polyfill("Object.values", function(w) { return this.flags.collapsed ? this.properties.filename : this.title; }; h.registerNodeType("basic/download", n); - q.title = "Watch"; - q.desc = "Show value of input"; - q.prototype.onExecute = function() { + p.title = "Watch"; + p.desc = "Show value of input"; + p.prototype.onExecute = function() { this.inputs[0] && (this.value = this.getInputData(0)); }; - q.prototype.getTitle = function() { + p.prototype.getTitle = function() { return this.flags.collapsed ? this.inputs[0].label : this.title; }; - q.toString = function(a) { + p.toString = function(a) { if (null == a) { return "null"; } @@ -5223,16 +5226,16 @@ $jscomp.polyfill("Object.values", function(w) { } if (a.constructor === Array) { for (var b = "[", d = 0; d < a.length; ++d) { - b += q.toString(a[d]) + (d + 1 != a.length ? "," : ""); + b += p.toString(a[d]) + (d + 1 != a.length ? "," : ""); } return b + "]"; } return String(a); }; - q.prototype.onDrawBackground = function(a) { - this.inputs[0].label = q.toString(this.value); + p.prototype.onDrawBackground = function(a) { + this.inputs[0].label = p.toString(this.value); }; - h.registerNodeType("basic/watch", q); + h.registerNodeType("basic/watch", p); k.title = "Cast"; k.desc = "Allows to connect different types"; k.prototype.onExecute = function() { @@ -5308,68 +5311,68 @@ $jscomp.polyfill("Object.values", function(w) { }; h.registerNodeType("basic/script", d); })(this); -(function(w) { +(function(v) { function c() { this.size = [60, 30]; - this.addInput("event", v.ACTION); + this.addInput("event", w.ACTION); } - function p() { + function q() { this.size = [60, 30]; this.addInput("if", ""); - this.addOutput("true", v.EVENT); - this.addOutput("change", v.EVENT); - this.addOutput("false", v.EVENT); + this.addOutput("true", w.EVENT); + this.addOutput("change", w.EVENT); + this.addOutput("false", w.EVENT); this.properties = {only_on_change:!0}; this.prev = 0; } function m() { - this.addInput("", v.ACTION); - this.addInput("", v.ACTION); - this.addInput("", v.ACTION); - this.addInput("", v.ACTION); - this.addInput("", v.ACTION); - this.addInput("", v.ACTION); - this.addOutput("", v.EVENT); - this.addOutput("", v.EVENT); - this.addOutput("", v.EVENT); - this.addOutput("", v.EVENT); - this.addOutput("", v.EVENT); - this.addOutput("", v.EVENT); + this.addInput("", w.ACTION); + this.addInput("", w.ACTION); + this.addInput("", w.ACTION); + this.addInput("", w.ACTION); + this.addInput("", w.ACTION); + this.addInput("", w.ACTION); + this.addOutput("", w.EVENT); + this.addOutput("", w.EVENT); + this.addOutput("", w.EVENT); + this.addOutput("", w.EVENT); + this.addOutput("", w.EVENT); + this.addOutput("", w.EVENT); this.size = [120, 30]; this.flags = {horizontal:!0, render_box:!1}; } function g() { this.size = [60, 30]; - this.addInput("event", v.ACTION); - this.addOutput("event", v.EVENT); + this.addInput("event", w.ACTION); + this.addOutput("event", w.EVENT); this.properties = {equal_to:"", has_property:"", property_equal_to:""}; } - function u() { - this.addInput("inc", v.ACTION); - this.addInput("dec", v.ACTION); - this.addInput("reset", v.ACTION); - this.addOutput("change", v.EVENT); + function r() { + this.addInput("inc", w.ACTION); + this.addInput("dec", w.ACTION); + this.addInput("reset", w.ACTION); + this.addOutput("change", w.EVENT); this.addOutput("num", "number"); this.num = 0; } function l() { this.size = [60, 30]; this.addProperty("time_in_ms", 1000); - this.addInput("event", v.ACTION); - this.addOutput("on_time", v.EVENT); + this.addInput("event", w.ACTION); + this.addOutput("on_time", w.EVENT); this._pending = []; } function B() { this.addProperty("interval", 1000); this.addProperty("event", "tick"); - this.addOutput("on_tick", v.EVENT); + this.addOutput("on_tick", w.EVENT); this.time = 0; this.last_interval = 1000; this.triggered = !1; } function y() { this.addInput("data", ""); - this.addInput("assign", v.ACTION); + this.addInput("assign", w.ACTION); this.addOutput("data", ""); this._last_value = null; this.properties = {data:null, serialize:!0}; @@ -5378,16 +5381,16 @@ $jscomp.polyfill("Object.values", function(w) { c.properties.data = c._last_value; }); } - var v = w.LiteGraph; + var w = v.LiteGraph; c.title = "Log Event"; c.desc = "Log event in console"; c.prototype.onAction = function(c, g) { console.log(c, g); }; - v.registerNodeType("events/log", c); - p.title = "TriggerEvent"; - p.desc = "Triggers event if input evaluates to true"; - p.prototype.onExecute = function(c, g) { + w.registerNodeType("events/log", c); + q.title = "TriggerEvent"; + q.desc = "Triggers event if input evaluates to true"; + q.prototype.onExecute = function(c, g) { c = this.getInputData(0); var e = c != this.prev; 0 === this.prev && (e = !1); @@ -5397,7 +5400,7 @@ $jscomp.polyfill("Object.values", function(w) { e && this.triggerSlot(1, g); this.prev = c; }; - v.registerNodeType("events/trigger", p); + w.registerNodeType("events/trigger", q); m.title = "Sequencer"; m.desc = "Trigger events when an event arrives"; m.prototype.getTitle = function() { @@ -5410,7 +5413,7 @@ $jscomp.polyfill("Object.values", function(w) { } } }; - v.registerNodeType("events/sequencer", m); + w.registerNodeType("events/sequencer", m); g.title = "Filter Event"; g.desc = "Blocks events that do not match the filter"; g.prototype.onAction = function(c, g) { @@ -5421,24 +5424,24 @@ $jscomp.polyfill("Object.values", function(w) { this.triggerSlot(0, g); } }; - v.registerNodeType("events/filter", g); - u.title = "Counter"; - u.desc = "Counts events"; - u.prototype.getTitle = function() { + w.registerNodeType("events/filter", g); + r.title = "Counter"; + r.desc = "Counts events"; + r.prototype.getTitle = function() { return this.flags.collapsed ? String(this.num) : this.title; }; - u.prototype.onAction = function(c, g) { + r.prototype.onAction = function(c, g) { g = this.num; "inc" == c ? this.num += 1 : "dec" == c ? --this.num : "reset" == c && (this.num = 0); this.num != g && this.trigger("change", this.num); }; - u.prototype.onDrawBackground = function(c) { + r.prototype.onDrawBackground = function(c) { this.flags.collapsed || (c.fillStyle = "#AAA", c.font = "20px Arial", c.textAlign = "center", c.fillText(this.num, 0.5 * this.size[0], 0.5 * this.size[1])); }; - u.prototype.onExecute = function() { + r.prototype.onExecute = function() { this.setOutputData(1, this.num); }; - v.registerNodeType("events/counter", u); + w.registerNodeType("events/counter", r); l.title = "Delay"; l.desc = "Delays one event"; l.prototype.onAction = function(c, g) { @@ -5455,9 +5458,9 @@ $jscomp.polyfill("Object.values", function(w) { } }; l.prototype.onGetInputs = function() { - return [["event", v.ACTION], ["time_in_ms", "number"]]; + return [["event", w.ACTION], ["time_in_ms", "number"]]; }; - v.registerNodeType("events/delay", l); + w.registerNodeType("events/delay", l); B.title = "Timer"; B.desc = "Sends an event every N milliseconds"; B.prototype.onStart = function() { @@ -5484,7 +5487,7 @@ $jscomp.polyfill("Object.values", function(w) { B.prototype.onGetOutputs = function() { return [["tick", "boolean"]]; }; - v.registerNodeType("events/timer", B); + w.registerNodeType("events/timer", B); y.title = "Data Store"; y.desc = "Stores data and only changes when event is received"; y.prototype.onExecute = function() { @@ -5497,9 +5500,9 @@ $jscomp.polyfill("Object.values", function(w) { y.prototype.onSerialize = function(c) { null != c.data && (0 == this.properties.serialize || c.data.constructor !== String && c.data.constructor !== Number && c.data.constructor !== Boolean && c.data.constructor !== Array && c.data.constructor !== Object) && (c.data = null); }; - v.registerNodeType("basic/data_store", y); + w.registerNodeType("basic/data_store", y); })(this); -(function(w) { +(function(v) { function c() { this.addOutput("", z.EVENT); this.addOutput("", "boolean"); @@ -5509,7 +5512,7 @@ $jscomp.polyfill("Object.values", function(w) { this.size = [164, 84]; this.clicked = !1; } - function p() { + function q() { this.addInput("", "boolean"); this.addInput("e", z.ACTION); this.addOutput("v", "boolean"); @@ -5540,7 +5543,7 @@ $jscomp.polyfill("Object.values", function(w) { c.triggerSlot(1, e); }, {property:"value", values:this._values}); } - function u() { + function r() { this.addOutput("", "number"); this.size = [64, 84]; this.properties = {min:0, max:1, value:0.5, color:"#7AF", precision:2}; @@ -5567,7 +5570,7 @@ $jscomp.polyfill("Object.values", function(w) { this.addInput("", "number"); this.properties = {min:0, max:1, value:0, color:"#AAF"}; } - function v() { + function w() { this.addInputs("", 0); this.properties = {value:"...", font:"Arial", fontsize:18, color:"#AAA", align:"left", glowSize:0, decimals:1}; } @@ -5575,7 +5578,7 @@ $jscomp.polyfill("Object.values", function(w) { this.size = [200, 100]; this.properties = {borderColor:"#ffffff", bgcolorTop:"#f0f0f0", bgcolorBottom:"#e0e0e0", shadowSize:2, borderRadius:3}; } - var z = w.LiteGraph; + var z = v.LiteGraph; c.title = "Button"; c.desc = "Triggers an event"; c.font = "Arial"; @@ -5601,9 +5604,9 @@ $jscomp.polyfill("Object.values", function(w) { this.clicked = !1; }; z.registerNodeType("widget/button", c); - p.title = "Toggle"; - p.desc = "Toggles between true or false"; - p.prototype.onDrawForeground = function(c) { + q.title = "Toggle"; + q.desc = "Toggles between true or false"; + q.prototype.onDrawForeground = function(c) { if (!this.flags.collapsed) { var e = 0.5 * this.size[1], g = 0.8 * this.size[1]; c.font = this.properties.font || (0.8 * e).toFixed(0) + "px Arial"; @@ -5619,21 +5622,21 @@ $jscomp.polyfill("Object.values", function(w) { c.textAlign = "left"; } }; - p.prototype.onAction = function(c) { + q.prototype.onAction = function(c) { this.properties.value = !this.properties.value; this.trigger("e", this.properties.value); }; - p.prototype.onExecute = function() { + q.prototype.onExecute = function() { var c = this.getInputData(0); null != c && (this.properties.value = c); this.setOutputData(0, this.properties.value); }; - p.prototype.onMouseDown = function(c, g) { + q.prototype.onMouseDown = function(c, g) { if (1 < g[0] && 1 < g[1] && g[0] < this.size[0] - 2 && g[1] < this.size[1] - 2) { return this.properties.value = !this.properties.value, this.graph._version++, this.trigger("e", this.properties.value), !0; } }; - z.registerNodeType("widget/toggle", p); + z.registerNodeType("widget/toggle", q); m.title = "Number"; m.desc = "Widget to select number value"; m.pixels_threshold = 10; @@ -5688,10 +5691,10 @@ $jscomp.polyfill("Object.values", function(w) { "values" == c ? (this._values = g.split(";"), this.widget.options.values = this._values) : "value" == c && (this.widget.value = g); }; z.registerNodeType("widget/combo", g); - u.title = "Knob"; - u.desc = "Circular controller"; - u.size = [80, 100]; - u.prototype.onDrawForeground = function(c) { + r.title = "Knob"; + r.desc = "Circular controller"; + r.size = [80, 100]; + r.prototype.onDrawForeground = function(c) { if (!this.flags.collapsed) { -1 == this.value && (this.value = (this.properties.value - this.properties.min) / (this.properties.max - this.properties.min)); var e = 0.5 * this.size[0], g = 0.5 * this.size[1], l = 0.5 * Math.min(this.size[0], this.size[1]) - 5; @@ -5730,11 +5733,11 @@ $jscomp.polyfill("Object.values", function(w) { c.fillText(this.properties.value.toFixed(this.properties.precision), e, g + 0.15 * l); } }; - u.prototype.onExecute = function() { + r.prototype.onExecute = function() { this.setOutputData(0, this.properties.value); this.boxcolor = z.colorToString([this.value, this.value, this.value]); }; - u.prototype.onMouseDown = function(c) { + r.prototype.onMouseDown = function(c) { this.center = [0.5 * this.size[0], 0.5 * this.size[1] + 20]; this.radius = 0.5 * this.size[0]; if (20 > c.canvasY - this.pos[1] || z.distance([c.canvasX, c.canvasY], [this.pos[0] + this.center[0], this.pos[1] + this.center[1]]) > this.radius) { @@ -5744,7 +5747,7 @@ $jscomp.polyfill("Object.values", function(w) { this.captureInput(!0); return !0; }; - u.prototype.onMouseMove = function(c) { + r.prototype.onMouseMove = function(c) { if (this.oldmouse) { c = [c.canvasX - this.pos[0], c.canvasY - this.pos[1]]; var e = this.value; @@ -5756,15 +5759,15 @@ $jscomp.polyfill("Object.values", function(w) { this.setDirtyCanvas(!0); } }; - u.prototype.onMouseUp = function(c) { + r.prototype.onMouseUp = function(c) { this.oldmouse && (this.oldmouse = null, this.captureInput(!1)); }; - u.prototype.onPropertyChanged = function(c, g) { + r.prototype.onPropertyChanged = function(c, g) { if ("min" == c || "max" == c || "value" == c) { return this.properties[c] = parseFloat(g), !0; } }; - z.registerNodeType("widget/knob", u); + z.registerNodeType("widget/knob", r); l.title = "Inner Slider"; l.prototype.onPropertyChanged = function(c, g) { "value" == c && (this.slider.value = g); @@ -5832,10 +5835,10 @@ $jscomp.polyfill("Object.values", function(w) { c.fillRect(2, 2, (this.size[0] - 4) * e, this.size[1] - 4); }; z.registerNodeType("widget/progress", y); - v.title = "Text"; - v.desc = "Shows the input value"; - v.widgets = [{name:"resize", text:"Resize box", type:"button"}, {name:"led_text", text:"LED", type:"minibutton"}, {name:"normal_text", text:"Normal", type:"minibutton"}]; - v.prototype.onDrawForeground = function(c) { + w.title = "Text"; + w.desc = "Shows the input value"; + w.widgets = [{name:"resize", text:"Resize box", type:"button"}, {name:"led_text", text:"LED", type:"minibutton"}, {name:"normal_text", text:"Normal", type:"minibutton"}]; + w.prototype.onDrawForeground = function(c) { c.fillStyle = this.properties.color; var e = this.properties.value; this.properties.glowSize ? (c.shadowColor = this.properties.color, c.shadowOffsetX = 0, c.shadowOffsetY = 0, c.shadowBlur = this.properties.glowSize) : c.shadowColor = "transparent"; @@ -5853,11 +5856,11 @@ $jscomp.polyfill("Object.values", function(w) { this.last_ctx = c; c.textAlign = "left"; }; - v.prototype.onExecute = function() { + w.prototype.onExecute = function() { var c = this.getInputData(0); null != c && (this.properties.value = c); }; - v.prototype.resize = function() { + w.prototype.resize = function() { if (this.last_ctx) { var c = this.str.split("\\n"); this.last_ctx.font = this.properties.fontsize + "px " + this.properties.font; @@ -5871,12 +5874,12 @@ $jscomp.polyfill("Object.values", function(w) { this.setDirtyCanvas(!0); } }; - v.prototype.onPropertyChanged = function(c, g) { + w.prototype.onPropertyChanged = function(c, g) { this.properties[c] = g; this.str = "number" == typeof g ? g.toFixed(3) : g; return !0; }; - z.registerNodeType("widget/text", v); + z.registerNodeType("widget/text", w); E.title = "Panel"; E.desc = "Non interactive panel"; E.widgets = [{name:"update", text:"Update", type:"button"}]; @@ -5889,11 +5892,11 @@ $jscomp.polyfill("Object.values", function(w) { }; z.registerNodeType("widget/panel", E); })(this); -(function(w) { +(function(v) { function c() { this.addOutput("left_x_axis", "number"); this.addOutput("left_y_axis", "number"); - this.addOutput("button_pressed", p.EVENT); + this.addOutput("button_pressed", q.EVENT); this.properties = {gamepad_index:0, threshold:0.1}; this._left_axis = new Float32Array(2); this._right_axis = new Float32Array(2); @@ -5901,7 +5904,7 @@ $jscomp.polyfill("Object.values", function(w) { this._previous_buttons = new Uint8Array(17); this._current_buttons = new Uint8Array(17); } - var p = w.LiteGraph; + var q = v.LiteGraph; c.title = "Gamepad"; c.desc = "gets the input of the gamepad"; c.CENTER = 0; @@ -5916,11 +5919,11 @@ $jscomp.polyfill("Object.values", function(w) { m && (this._left_axis[0] = Math.abs(m.xbox.axes.lx) > g ? m.xbox.axes.lx : 0, this._left_axis[1] = Math.abs(m.xbox.axes.ly) > g ? m.xbox.axes.ly : 0, this._right_axis[0] = Math.abs(m.xbox.axes.rx) > g ? m.xbox.axes.rx : 0, this._right_axis[1] = Math.abs(m.xbox.axes.ry) > g ? m.xbox.axes.ry : 0, this._triggers[0] = Math.abs(m.xbox.axes.ltrigger) > g ? m.xbox.axes.ltrigger : 0, this._triggers[1] = Math.abs(m.xbox.axes.rtrigger) > g ? m.xbox.axes.rtrigger : 0); if (this.outputs) { for (g = 0; g < this.outputs.length; g++) { - var p = this.outputs[g]; - if (p.links && p.links.length) { + var r = this.outputs[g]; + if (r.links && r.links.length) { var l = null; if (m) { - switch(p.name) { + switch(r.name) { case "left_axis": l = this._left_axis; break; @@ -5991,12 +5994,12 @@ $jscomp.polyfill("Object.values", function(w) { l = m.xbox.buttons.back ? 1 : 0; break; case "button_pressed": - for (p = 0; p < this._current_buttons.length; ++p) { - this._current_buttons[p] && !this._previous_buttons[p] && this.triggerSlot(g, c.buttons[p]); + for (r = 0; r < this._current_buttons.length; ++r) { + this._current_buttons[r] && !this._previous_buttons[r] && this.triggerSlot(g, c.buttons[r]); } } } else { - switch(p.name) { + switch(r.name) { case "button_pressed": break; case "left_axis": @@ -6034,25 +6037,25 @@ $jscomp.polyfill("Object.values", function(w) { g.axes.rtrigger = m.buttons[7].value; g.hat = ""; g.hatmap = c.CENTER; - for (var p = 0; p < m.buttons.length; p++) { - if (this._current_buttons[p] = m.buttons[p].pressed, 12 > p) { - g.buttons[c.mapping_array[p]] = m.buttons[p].pressed, m.buttons[p].was_pressed && this.trigger(c.mapping_array[p] + "_button_event"); + for (var r = 0; r < m.buttons.length; r++) { + if (this._current_buttons[r] = m.buttons[r].pressed, 12 > r) { + g.buttons[c.mapping_array[r]] = m.buttons[r].pressed, m.buttons[r].was_pressed && this.trigger(c.mapping_array[r] + "_button_event"); } else { - switch(p) { + switch(r) { case 12: - m.buttons[p].pressed && (g.hat += "up", g.hatmap |= c.UP); + m.buttons[r].pressed && (g.hat += "up", g.hatmap |= c.UP); break; case 13: - m.buttons[p].pressed && (g.hat += "down", g.hatmap |= c.DOWN); + m.buttons[r].pressed && (g.hat += "down", g.hatmap |= c.DOWN); break; case 14: - m.buttons[p].pressed && (g.hat += "left", g.hatmap |= c.LEFT); + m.buttons[r].pressed && (g.hat += "left", g.hatmap |= c.LEFT); break; case 15: - m.buttons[p].pressed && (g.hat += "right", g.hatmap |= c.RIGHT); + m.buttons[r].pressed && (g.hat += "right", g.hatmap |= c.RIGHT); break; case 16: - g.buttons.home = m.buttons[p].pressed; + g.buttons.home = m.buttons[r].pressed; } } } @@ -6076,17 +6079,17 @@ $jscomp.polyfill("Object.values", function(w) { } }; c.prototype.onGetOutputs = function() { - return [["left_axis", "vec2"], ["right_axis", "vec2"], ["left_x_axis", "number"], ["left_y_axis", "number"], ["right_x_axis", "number"], ["right_y_axis", "number"], ["trigger_left", "number"], ["trigger_right", "number"], ["a_button", "number"], ["b_button", "number"], ["x_button", "number"], ["y_button", "number"], ["lb_button", "number"], ["rb_button", "number"], ["ls_button", "number"], ["rs_button", "number"], ["start_button", "number"], ["back_button", "number"], ["a_button_event", p.EVENT], - ["b_button_event", p.EVENT], ["x_button_event", p.EVENT], ["y_button_event", p.EVENT], ["lb_button_event", p.EVENT], ["rb_button_event", p.EVENT], ["ls_button_event", p.EVENT], ["rs_button_event", p.EVENT], ["start_button_event", p.EVENT], ["back_button_event", p.EVENT], ["hat_left", "number"], ["hat_right", "number"], ["hat_up", "number"], ["hat_down", "number"], ["hat", "number"], ["button_pressed", p.EVENT]]; + return [["left_axis", "vec2"], ["right_axis", "vec2"], ["left_x_axis", "number"], ["left_y_axis", "number"], ["right_x_axis", "number"], ["right_y_axis", "number"], ["trigger_left", "number"], ["trigger_right", "number"], ["a_button", "number"], ["b_button", "number"], ["x_button", "number"], ["y_button", "number"], ["lb_button", "number"], ["rb_button", "number"], ["ls_button", "number"], ["rs_button", "number"], ["start_button", "number"], ["back_button", "number"], ["a_button_event", q.EVENT], + ["b_button_event", q.EVENT], ["x_button_event", q.EVENT], ["y_button_event", q.EVENT], ["lb_button_event", q.EVENT], ["rb_button_event", q.EVENT], ["ls_button_event", q.EVENT], ["rs_button_event", q.EVENT], ["start_button_event", q.EVENT], ["back_button_event", q.EVENT], ["hat_left", "number"], ["hat_right", "number"], ["hat_up", "number"], ["hat_down", "number"], ["hat", "number"], ["button_pressed", q.EVENT]]; }; - p.registerNodeType("input/gamepad", c); + q.registerNodeType("input/gamepad", c); })(this); -(function(w) { +(function(v) { function c() { this.addInput("in", "*"); this.size = [80, 30]; } - function p() { + function q() { this.addInput("in"); this.addOutput("out"); this.size = [80, 30]; @@ -6106,7 +6109,7 @@ $jscomp.polyfill("Object.values", function(w) { this.addProperty("out_max", 1); this.size = [120, 50]; } - function u() { + function r() { this.addOutput("value", "number"); this.addProperty("min", 0); this.addProperty("max", 1); @@ -6135,7 +6138,7 @@ $jscomp.polyfill("Object.values", function(w) { this.addProperty("min", 0); this.addProperty("max", 1); } - function v() { + function w() { this.properties = {f:0.5}; this.addInput("A", "number"); this.addInput("B", "number"); @@ -6168,7 +6171,7 @@ $jscomp.polyfill("Object.values", function(w) { this.size = [80, 30]; this.addProperty("factor", 1); } - function t() { + function u() { this.addInput("v", "boolean"); this.addInput("A"); this.addInput("B"); @@ -6189,13 +6192,13 @@ $jscomp.polyfill("Object.values", function(w) { this.size = [80, 30]; this._value = null; } - function q() { + function p() { this.addInput("A", "number"); this.addInput("B", "number"); this.addOutput("=", "number"); this.addProperty("A", 1); this.addProperty("B", 1); - this.addProperty("OP", "+", "enum", {values:q.values}); + this.addProperty("OP", "+", "enum", {values:p.values}); } function k() { this.addInput("A", "number"); @@ -6271,13 +6274,13 @@ $jscomp.polyfill("Object.values", function(w) { this.addOutput("z", "number"); this.addOutput("w", "number"); } - function L() { + function K() { this.addInputs([["x", "number"], ["y", "number"], ["z", "number"], ["w", "number"]]); this.addOutput("vec4", "vec4"); this.properties = {x:0, y:0, z:0, w:0}; this._data = new Float32Array(4); } - var A = w.LiteGraph; + var A = v.LiteGraph; c.title = "Converter"; c.desc = "type A to type B"; c.prototype.onExecute = function() { @@ -6322,13 +6325,13 @@ $jscomp.polyfill("Object.values", function(w) { return [["number", "number"], ["vec2", "vec2"], ["vec3", "vec3"], ["vec4", "vec4"]]; }; A.registerNodeType("math/converter", c); - p.title = "Bypass"; - p.desc = "removes the type"; - p.prototype.onExecute = function() { + q.title = "Bypass"; + q.desc = "removes the type"; + q.prototype.onExecute = function() { var a = this.getInputData(0); this.setOutputData(0, a); }; - A.registerNodeType("math/bypass", p); + A.registerNodeType("math/bypass", q); m.title = "to Number"; m.desc = "Cast to number"; m.prototype.onExecute = function() { @@ -6366,9 +6369,9 @@ $jscomp.polyfill("Object.values", function(w) { return [["in_min", "number"], ["in_max", "number"], ["out_min", "number"], ["out_max", "number"]]; }; A.registerNodeType("math/range", g); - u.title = "Rand"; - u.desc = "Random number"; - u.prototype.onExecute = function() { + r.title = "Rand"; + r.desc = "Random number"; + r.prototype.onExecute = function() { if (this.inputs) { for (var a = 0; a < this.inputs.length; a++) { var b = this.inputs[a], d = this.getInputData(a); @@ -6379,13 +6382,13 @@ $jscomp.polyfill("Object.values", function(w) { this._last_v = Math.random() * (this.properties.max - a) + a; this.setOutputData(0, this._last_v); }; - u.prototype.onDrawBackground = function(a) { + r.prototype.onDrawBackground = function(a) { this.outputs[0].label = (this._last_v || 0).toFixed(3); }; - u.prototype.onGetInputs = function() { + r.prototype.onGetInputs = function() { return [["min", "number"], ["max", "number"]]; }; - A.registerNodeType("math/rand", u); + A.registerNodeType("math/rand", r); l.title = "Noise"; l.desc = "Random number with temporal continuity"; l.data = null; @@ -6440,9 +6443,9 @@ $jscomp.polyfill("Object.values", function(w) { return a; }; A.registerNodeType("math/clamp", y); - v.title = "Lerp"; - v.desc = "Linear Interpolation"; - v.prototype.onExecute = function() { + w.title = "Lerp"; + w.desc = "Linear Interpolation"; + w.prototype.onExecute = function() { var a = this.getInputData(0); null == a && (a = 0); var b = this.getInputData(1); @@ -6451,10 +6454,10 @@ $jscomp.polyfill("Object.values", function(w) { void 0 !== c && (d = c); this.setOutputData(0, a * (1 - d) + b * d); }; - v.prototype.onGetInputs = function() { + w.prototype.onGetInputs = function() { return [["f", "number"]]; }; - A.registerNodeType("math/lerp", v); + A.registerNodeType("math/lerp", w); E.title = "Abs"; E.desc = "Absolute"; E.prototype.onExecute = function() { @@ -6494,13 +6497,13 @@ $jscomp.polyfill("Object.values", function(w) { null != a && this.setOutputData(0, a * this.properties.factor); }; A.registerNodeType("math/scale", D); - t.title = "Gate"; - t.desc = "if v is true, then outputs A, otherwise B"; - t.prototype.onExecute = function() { + u.title = "Gate"; + u.desc = "if v is true, then outputs A, otherwise B"; + u.prototype.onExecute = function() { var a = this.getInputData(0); this.setOutputData(0, this.getInputData(a ? 1 : 2)); }; - A.registerNodeType("math/gate", t); + A.registerNodeType("math/gate", u); G.title = "Average"; G.desc = "Average Filter"; G.prototype.onExecute = function() { @@ -6533,19 +6536,19 @@ $jscomp.polyfill("Object.values", function(w) { this.setOutputData(0, this._value); }; A.registerNodeType("math/tendTo", n); - q.values = "+ - * / % ^ max min".split(" "); - q.title = "Operation"; - q.desc = "Easy math operators"; - q["@OP"] = {type:"enum", title:"operation", values:q.values}; - q.size = [100, 60]; - q.prototype.getTitle = function() { + p.values = "+ - * / % ^ max min".split(" "); + p.title = "Operation"; + p.desc = "Easy math operators"; + p["@OP"] = {type:"enum", title:"operation", values:p.values}; + p.size = [100, 60]; + p.prototype.getTitle = function() { return "max" == this.properties.OP || "min" == this.properties.OP ? this.properties.OP + "(A,B)" : "A " + this.properties.OP + " B"; }; - q.prototype.setValue = function(a) { + p.prototype.setValue = function(a) { "string" == typeof a && (a = parseFloat(a)); this.properties.value = a; }; - q.prototype.onExecute = function() { + p.prototype.onExecute = function() { var a = this.getInputData(0), b = this.getInputData(1); null != a ? this.properties.A = a : a = this.properties.A; null != b ? this.properties.B = b : b = this.properties.B; @@ -6582,10 +6585,10 @@ $jscomp.polyfill("Object.values", function(w) { } this.setOutputData(0, d); }; - q.prototype.onDrawBackground = function(a) { + p.prototype.onDrawBackground = function(a) { this.flags.collapsed || (a.font = "40px Arial", a.fillStyle = "#666", a.textAlign = "center", a.fillText(this.properties.OP, 0.5 * this.size[0], 0.5 * (this.size[1] + A.NODE_TITLE_HEIGHT)), a.textAlign = "left"); }; - A.registerNodeType("math/operation", q); + A.registerNodeType("math/operation", p); A.registerSearchboxExtra("math/operation", "MAX", {properties:{OP:"max"}, title:"MAX()"}); A.registerSearchboxExtra("math/operation", "MIN", {properties:{OP:"min"}, title:"MIN()"}); k.title = "Compare"; @@ -6740,7 +6743,7 @@ $jscomp.polyfill("Object.values", function(w) { this._func && this._func_code == this.properties.formula || (this._func = new Function("x", "y", "TIME", "return " + this.properties.formula), this._func_code = this.properties.formula); var d = this._func(a, b, this.graph.globaltime); this.boxcolor = null; - } catch (M) { + } catch (N) { this.boxcolor = "red"; } this.setOutputData(0, d); @@ -6804,9 +6807,9 @@ $jscomp.polyfill("Object.values", function(w) { null != a && (this.setOutputData(0, a[0]), this.setOutputData(1, a[1]), this.setOutputData(2, a[2]), this.setOutputData(3, a[3])); }; A.registerNodeType("math3d/vec4-to-xyzw", J); - L.title = "XYZW->Vec4"; - L.desc = "components to vector4"; - L.prototype.onExecute = function() { + K.title = "XYZW->Vec4"; + K.desc = "components to vector4"; + K.prototype.onExecute = function() { var a = this.getInputData(0); null == a && (a = this.properties.x); var b = this.getInputData(1); @@ -6822,9 +6825,9 @@ $jscomp.polyfill("Object.values", function(w) { k[3] = c; this.setOutputData(0, k); }; - A.registerNodeType("math3d/xyzw-to-vec4", L); + A.registerNodeType("math3d/xyzw-to-vec4", K); })(this); -(function(w) { +(function(v) { function c() { this.addInput("sel", "number"); this.addInput("A"); @@ -6834,7 +6837,7 @@ $jscomp.polyfill("Object.values", function(w) { this.addOutput("out"); this.selected = 0; } - function p() { + function q() { this.properties = {sequence:"A,B,C"}; this.addInput("index", "number"); this.addInput("seq"); @@ -6842,7 +6845,7 @@ $jscomp.polyfill("Object.values", function(w) { this.index = 0; this.values = this.properties.sequence.split(","); } - var m = w.LiteGraph; + var m = v.LiteGraph; c.title = "Selector"; c.desc = "selects an output"; c.prototype.onDrawBackground = function(c) { @@ -6869,12 +6872,12 @@ $jscomp.polyfill("Object.values", function(w) { return [["E", 0], ["F", 0], ["G", 0], ["H", 0]]; }; m.registerNodeType("logic/selector", c); - p.title = "Sequence"; - p.desc = "select one element from a sequence from a string"; - p.prototype.onPropertyChanged = function(c, m) { + q.title = "Sequence"; + q.desc = "select one element from a sequence from a string"; + q.prototype.onPropertyChanged = function(c, m) { "sequence" == c && (this.values = m.split(",")); }; - p.prototype.onExecute = function() { + q.prototype.onExecute = function() { var c = this.getInputData(1); c && c != this.current_sequence && (this.values = c.split(","), this.current_sequence = c); c = this.getInputData(0); @@ -6882,16 +6885,16 @@ $jscomp.polyfill("Object.values", function(w) { this.index = c = Math.round(c) % this.values.length; this.setOutputData(0, this.values[c]); }; - m.registerNodeType("logic/sequence", p); + m.registerNodeType("logic/sequence", q); })(this); -(function(w) { +(function(v) { function c() { this.addOutput("tex", "Texture"); this.addOutput("name", "string"); this.properties = {name:"", filter:!0}; this.size = [c.image_preview_size, c.image_preview_size]; } - function p() { + function q() { this.addInput("Texture", "Texture"); this.properties = {flipY:!1}; this.size = [c.image_preview_size, c.image_preview_size]; @@ -6911,10 +6914,10 @@ $jscomp.polyfill("Object.values", function(w) { this.properties = {value:1, pixelcode:"color + colorB * value", uvcode:"", precision:c.DEFAULT}; this.has_error = !1; } - function u() { + function r() { this.addOutput("out", "Texture"); this.properties = {code:"", u_value:1, u_color:[1, 1, 1, 1], width:512, height:512, precision:c.DEFAULT}; - this.properties.code = u.pixel_shader; + this.properties.code = r.pixel_shader; this._uniforms = {u_value:1, u_color:vec4.create(), in_texture:0, texSize:vec2.create(), time:0}; } function l() { @@ -6937,7 +6940,7 @@ $jscomp.polyfill("Object.values", function(w) { this.properties = {additive:!1, antialiasing:!1, filter:!0, disable_alpha:!1, gamma:1.0, viewport:[0, 0, 1, 1]}; this.size[0] = 130; } - function v() { + function w() { this.addInput("Texture", "Texture"); this.addOutput("", "Texture"); this.properties = {size:0, generate_mipmaps:!1, precision:c.DEFAULT}; @@ -6976,13 +6979,13 @@ $jscomp.polyfill("Object.values", function(w) { this.addOutput("", "Texture"); this.properties = {}; } - function t() { + function u() { this.addInput("Texture", "Texture"); this.addInput("LUT", "Texture"); this.addInput("Intensity", "number"); this.addOutput("", "Texture"); this.properties = {enabled:!0, intensity:1, precision:c.DEFAULT, texture:null}; - t._shader || (t._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, t.pixel_shader)); + u._shader || (u._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, u.pixel_shader)); } function G() { this.addInput("Texture", "Texture"); @@ -7002,7 +7005,7 @@ $jscomp.polyfill("Object.values", function(w) { this._color = vec4.create(); this._uniforms = {u_textureR:0, u_textureG:1, u_textureB:2, u_textureA:3, u_color:this._color}; } - function q() { + function p() { this.addOutput("Texture", "Texture"); this._tex_color = vec4.create(); this.properties = {color:vec4.create(), precision:c.DEFAULT}; @@ -7075,7 +7078,7 @@ $jscomp.polyfill("Object.values", function(w) { this.boxcolor = "black"; this.version = 0; } - function L() { + function K() { this.addInput("in", "Texture"); this.addInput("f", "number"); this.addOutput("out", "Texture"); @@ -7098,31 +7101,31 @@ $jscomp.polyfill("Object.values", function(w) { this.curve_offset = 68; this.size = [240, 160]; } - function r() { + function t() { this.addInput("in", "Texture"); this.addInput("exp", "number"); this.addOutput("out", "Texture"); this.properties = {exposition:1, precision:c.LOW}; this._uniforms = {u_texture:0, u_exposition:1}; } - function K() { + function L() { this.addInput("in", "Texture"); this.addInput("avg", "number,Texture"); this.addOutput("out", "Texture"); this.properties = {enabled:!0, scale:1, gamma:1, average_lum:1, lum_white:1, precision:c.LOW}; this._uniforms = {u_texture:0, u_lumwhite2:1, u_igamma:1, u_scale:1, u_average_lum:1}; } - function N() { + function M() { this.addOutput("out", "Texture"); this.properties = {width:512, height:512, seed:0, persistence:0.1, octaves:8, scale:1, offset:[0, 0], amplitude:1, precision:c.DEFAULT}; this._key = 0; this._texture = null; this._uniforms = {u_persistence:0.1, u_seed:0, u_offset:vec2.create(), u_scale:1, u_viewport:vec2.create()}; } - function M() { + function N() { this.addInput("v"); this.addOutput("out", "Texture"); - this.properties = {code:M.default_code, width:512, height:512, clear:!0, precision:c.DEFAULT, use_html_canvas:!1}; + this.properties = {code:N.default_code, width:512, height:512, clear:!0, precision:c.DEFAULT, use_html_canvas:!1}; this._temp_texture = this._func = null; this.compileCode(); } @@ -7137,9 +7140,9 @@ $jscomp.polyfill("Object.values", function(w) { this.addOutput("out", "texture"); this.properties = {yaw:0}; } - var F = w.LiteGraph; - w.LGraphTexture = null; - "undefined" != typeof GL && (LGraphCanvas.link_type_colors.Texture = "#987", w.LGraphTexture = c, c.title = "Texture", c.desc = "Texture", c.widgets_info = {name:{widget:"texture"}, filter:{widget:"checkbox"}}, c.loadTextureCallback = null, c.image_preview_size = 256, c.PASS_THROUGH = 1, c.COPY = 2, c.LOW = 3, c.HIGH = 4, c.REUSE = 5, c.DEFAULT = 2, c.MODE_VALUES = {"pass through":c.PASS_THROUGH, copy:c.COPY, low:c.LOW, high:c.HIGH, reuse:c.REUSE, default:c.DEFAULT}, c.getTexturesContainer = function() { + var F = v.LiteGraph; + v.LGraphTexture = null; + "undefined" != typeof GL && (LGraphCanvas.link_type_colors.Texture = "#987", v.LGraphTexture = c, c.title = "Texture", c.desc = "Texture", c.widgets_info = {name:{widget:"texture"}, filter:{widget:"checkbox"}}, c.loadTextureCallback = null, c.image_preview_size = 256, c.PASS_THROUGH = 1, c.COPY = 2, c.LOW = 3, c.HIGH = 4, c.REUSE = 5, c.DEFAULT = 2, c.MODE_VALUES = {"pass through":c.PASS_THROUGH, copy:c.COPY, low:c.LOW, high:c.HIGH, reuse:c.REUSE, default:c.DEFAULT}, c.getTexturesContainer = function() { return gl.textures; }, c.loadTexture = function(a, b) { b = b || {}; @@ -7271,12 +7274,12 @@ $jscomp.polyfill("Object.values", function(w) { a = a.replace(/[\{\}]/g, ""); return b[a] || ""; }); - }, F.registerNodeType("texture/texture", c), p.title = "Preview", p.desc = "Show a texture in the graph canvas", p.allow_preview = !1, p.prototype.onDrawBackground = function(a) { - if (!this.flags.collapsed && (a.webgl || p.allow_preview)) { + }, F.registerNodeType("texture/texture", c), q.title = "Preview", q.desc = "Show a texture in the graph canvas", q.allow_preview = !1, q.prototype.onDrawBackground = function(a) { + if (!this.flags.collapsed && (a.webgl || q.allow_preview)) { var b = this.getInputData(0); b && (b = !b.handle && a.webgl ? b : c.generateLowResTexturePreview(b), a.save(), this.properties.flipY && (a.translate(0, this.size[1]), a.scale(1, -1)), a.drawImage(b, 0, 0, this.size[0], this.size[1]), a.restore()); } - }, F.registerNodeType("texture/preview", p), m.title = "Save", m.desc = "Save a texture in the repository", m.prototype.getPreviewTexture = function() { + }, F.registerNodeType("texture/preview", q), m.title = "Save", m.desc = "Save a texture in the repository", m.prototype.getPreviewTexture = function() { return this._texture; }, m.prototype.onExecute = function() { var a = this.getInputData(0); @@ -7322,8 +7325,8 @@ $jscomp.polyfill("Object.values", function(w) { this._shader_code = f + "|" + h; } if (this._shader) { - var q = this.getInputData(2); - null != q ? this.properties.value = q : q = parseFloat(this.properties.value); + var p = this.getInputData(2); + null != p ? this.properties.value = p : p = parseFloat(this.properties.value); var l = this.graph.getTime(); this._tex.drawTo(function() { gl.disable(gl.DEPTH_TEST); @@ -7332,7 +7335,7 @@ $jscomp.polyfill("Object.values", function(w) { a && a.bind(0); b && b.bind(1); var c = Mesh.getScreenQuad(); - e.uniforms({u_texture:0, u_textureB:1, value:q, texSize:[d, k], time:l}).draw(c); + e.uniforms({u_texture:0, u_textureB:1, value:p, texSize:[d, k], time:l}).draw(c); }); this.setOutputData(0, this._tex); } @@ -7350,7 +7353,7 @@ $jscomp.polyfill("Object.values", function(w) { var c = g.presets[d]; c && (b.setProperty("pixelcode", c), b.title = d, a.refresh()); }}); - }, F.registerNodeType("texture/operation", g), u.title = "Shader", u.desc = "Texture shader", u.widgets_info = {code:{type:"code", lang:"glsl"}, precision:{widget:"combo", values:c.MODE_VALUES}}, u.prototype.onPropertyChanged = function(a, b) { + }, F.registerNodeType("texture/operation", g), r.title = "Shader", r.desc = "Texture shader", r.widgets_info = {code:{type:"code", lang:"glsl"}, precision:{widget:"combo", values:c.MODE_VALUES}}, r.prototype.onPropertyChanged = function(a, b) { if ("code" == a && (a = this.getShader())) { b = a.uniformInfo; if (this.inputs) { @@ -7398,14 +7401,14 @@ $jscomp.polyfill("Object.values", function(w) { } } } - }, u.prototype.getShader = function() { + }, r.prototype.getShader = function() { if (this._shader && this._shader_code == this.properties.code) { return this._shader; } this._shader_code = this.properties.code; this._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, this.properties.code), this.boxcolor = "green"; return this._shader; - }, u.prototype.onExecute = function() { + }, r.prototype.onExecute = function() { if (this.isOutputConnected(0)) { var a = this.getShader(); if (a) { @@ -7434,7 +7437,7 @@ $jscomp.polyfill("Object.values", function(w) { this.setOutputData(0, this._tex); } } - }, u.pixel_shader = "precision highp float;\n\r\n\n\r\nvarying vec2 v_coord;\n\r\nuniform float time; //time in seconds\n\r\nuniform vec2 texSize; //tex resolution\n\r\nuniform float u_value;\n\r\nuniform vec4 u_color;\n\n\r\nvoid main() {\n\r\n\tvec2 uv = v_coord;\n\r\n\tvec3 color = vec3(0.0);\n\r\n\t//your code here\n\r\n\tcolor.xy=uv;\n\n\r\n\tgl_FragColor = vec4(color, 1.0);\n\r\n}\n\r\n", F.registerNodeType("texture/shader", u), l.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, + }, r.pixel_shader = "precision highp float;\n\r\n\n\r\nvarying vec2 v_coord;\n\r\nuniform float time; //time in seconds\n\r\nuniform vec2 texSize; //tex resolution\n\r\nuniform float u_value;\n\r\nuniform vec4 u_color;\n\n\r\nvoid main() {\n\r\n\tvec2 uv = v_coord;\n\r\n\tvec3 color = vec3(0.0);\n\r\n\t//your code here\n\r\n\tcolor.xy=uv;\n\n\r\n\tgl_FragColor = vec4(color, 1.0);\n\r\n}\n\r\n", F.registerNodeType("texture/shader", r), l.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, l.title = "Scale/Offset", l.desc = "Applies an scaling and offseting", l.prototype.onExecute = function() { var a = this.getInputData(0); if (this.isOutputConnected(0) && a) { @@ -7511,8 +7514,8 @@ $jscomp.polyfill("Object.values", function(w) { }, y.prototype.onGetInputs = function() { return [["gamma", "number"]]; }, y.aa_pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec2 uViewportSize;\n\r\n\t\tuniform vec2 inverseVP;\n\r\n\t\tuniform float u_igamma;\n\r\n\t\t#define FXAA_REDUCE_MIN (1.0/ 128.0)\n\r\n\t\t#define FXAA_REDUCE_MUL (1.0 / 8.0)\n\r\n\t\t#define FXAA_SPAN_MAX 8.0\n\r\n\t\t\n\r\n\t\t/* from mitsuhiko/webgl-meincraft based on the code on geeks3d.com */\n\r\n\t\tvec4 applyFXAA(sampler2D tex, vec2 fragCoord)\n\r\n\t\t{\n\r\n\t\t\tvec4 color = vec4(0.0);\n\r\n\t\t\t/*vec2 inverseVP = vec2(1.0 / uViewportSize.x, 1.0 / uViewportSize.y);*/\n\r\n\t\t\tvec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * inverseVP).xyz;\n\r\n\t\t\tvec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * inverseVP).xyz;\n\r\n\t\t\tvec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * inverseVP).xyz;\n\r\n\t\t\tvec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * inverseVP).xyz;\n\r\n\t\t\tvec3 rgbM = texture2D(tex, fragCoord * inverseVP).xyz;\n\r\n\t\t\tvec3 luma = vec3(0.299, 0.587, 0.114);\n\r\n\t\t\tfloat lumaNW = dot(rgbNW, luma);\n\r\n\t\t\tfloat lumaNE = dot(rgbNE, luma);\n\r\n\t\t\tfloat lumaSW = dot(rgbSW, luma);\n\r\n\t\t\tfloat lumaSE = dot(rgbSE, luma);\n\r\n\t\t\tfloat lumaM = dot(rgbM, luma);\n\r\n\t\t\tfloat lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n\r\n\t\t\tfloat lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\r\n\t\t\t\n\r\n\t\t\tvec2 dir;\n\r\n\t\t\tdir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n\r\n\t\t\tdir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\r\n\t\t\t\n\r\n\t\t\tfloat dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\r\n\t\t\t\n\r\n\t\t\tfloat rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n\r\n\t\t\tdir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * inverseVP;\n\r\n\t\t\t\n\r\n\t\t\tvec3 rgbA = 0.5 * (texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + \n\r\n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz);\n\r\n\t\t\tvec3 rgbB = rgbA * 0.5 + 0.25 * (texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + \n\r\n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz);\n\r\n\t\t\t\n\r\n\t\t\t//return vec4(rgbA,1.0);\n\r\n\t\t\tfloat lumaB = dot(rgbB, luma);\n\r\n\t\t\tif ((lumaB < lumaMin) || (lumaB > lumaMax))\n\r\n\t\t\t\tcolor = vec4(rgbA, 1.0);\n\r\n\t\t\telse\n\r\n\t\t\t\tcolor = vec4(rgbB, 1.0);\n\r\n\t\t\tif(u_igamma != 1.0)\n\r\n\t\t\t\tcolor.xyz = pow( color.xyz, vec3(u_igamma) );\n\r\n\t\t\treturn color;\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\r\n\t\t}\n\r\n\t\t", - y.gamma_pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_igamma;\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\r\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\r\n\t\t gl_FragColor = color;\n\r\n\t\t}\n\r\n\t\t", F.registerNodeType("texture/toviewport", y), v.title = "Copy", v.desc = "Copy Texture", v.widgets_info = {size:{widget:"combo", - values:[0, 32, 64, 128, 256, 512, 1024, 2048]}, precision:{widget:"combo", values:c.MODE_VALUES}}, v.prototype.onExecute = function() { + y.gamma_pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_igamma;\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\r\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\r\n\t\t gl_FragColor = color;\n\r\n\t\t}\n\r\n\t\t", F.registerNodeType("texture/toviewport", y), w.title = "Copy", w.desc = "Copy Texture", w.widgets_info = {size:{widget:"combo", + values:[0, 32, 64, 128, 256, 512, 1024, 2048]}, precision:{widget:"combo", values:c.MODE_VALUES}}, w.prototype.onExecute = function() { var a = this.getInputData(0); if ((a || this._temp_texture) && this.isOutputConnected(0)) { if (a) { @@ -7526,7 +7529,7 @@ $jscomp.polyfill("Object.values", function(w) { } this.setOutputData(0, this._temp_texture); } - }, F.registerNodeType("texture/copy", v), E.title = "Downsample", E.desc = "Downsample Texture", E.widgets_info = {iterations:{type:"number", step:1, precision:0, min:0}, precision:{widget:"combo", values:c.MODE_VALUES}}, E.prototype.onExecute = function() { + }, F.registerNodeType("texture/copy", w), E.title = "Downsample", E.desc = "Downsample Texture", E.widgets_info = {iterations:{type:"number", step:1, precision:0, min:0}, precision:{widget:"combo", values:c.MODE_VALUES}}, E.prototype.onExecute = function() { var a = this.getInputData(0); if ((a || this._temp_texture) && this.isOutputConnected(0) && a && a.texture_type === GL.TEXTURE_2D) { if (1 > this.properties.iterations) { @@ -7538,7 +7541,7 @@ $jscomp.polyfill("Object.values", function(w) { this.properties.precision === c.LOW ? f = gl.UNSIGNED_BYTE : this.properties.precision === c.HIGH && (f = gl.HIGH_PRECISION_FORMAT); var h = this.properties.iterations || 1, e = a, n = []; f = {type:f, format:a.format}; - var g = vec2.create(), q = {u_offset:g}; + var g = vec2.create(), p = {u_offset:g}; this._texture && GL.Texture.releaseTemporary(this._texture); for (var l = 0; l < h; ++l) { g[0] = 1 / d; @@ -7548,7 +7551,7 @@ $jscomp.polyfill("Object.values", function(w) { a = GL.Texture.getTemporary(d, k, f); n.push(a); e.setParameter(GL.TEXTURE_MAG_FILTER, GL.NEAREST); - e.copyTo(a, b, q); + e.copyTo(a, b, p); if (1 == d && 1 == k) { break; } @@ -7675,7 +7678,7 @@ $jscomp.polyfill("Object.values", function(w) { this.setOutputData(0, this._temp_texture); } } - }, F.registerNodeType("texture/imageToTexture", D), t.widgets_info = {texture:{widget:"texture"}, precision:{widget:"combo", values:c.MODE_VALUES}}, t.title = "LUT", t.desc = "Apply LUT to Texture", t.prototype.onExecute = function() { + }, F.registerNodeType("texture/imageToTexture", D), u.widgets_info = {texture:{widget:"texture"}, precision:{widget:"combo", values:c.MODE_VALUES}}, u.title = "LUT", u.desc = "Apply LUT to Texture", u.prototype.onExecute = function() { if (this.isOutputConnected(0)) { var a = this.getInputData(0); if (this.properties.precision === c.PASS_THROUGH || !1 === this.properties.enabled) { @@ -7695,7 +7698,7 @@ $jscomp.polyfill("Object.values", function(w) { this._tex = c.getTargetTexture(a, this._tex, this.properties.precision); this._tex.drawTo(function() { b.bind(1); - a.toViewport(t._shader, {u_texture:0, u_textureB:1, u_amount:d}); + a.toViewport(u._shader, {u_texture:0, u_textureB:1, u_amount:d}); }); this.setOutputData(0, this._tex); } else { @@ -7704,8 +7707,8 @@ $jscomp.polyfill("Object.values", function(w) { } } } - }, t.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tuniform float u_amount;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\r\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\r\n\t\t\t mediump vec2 quad1;\n\r\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\r\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\r\n\t\t\t mediump vec2 quad2;\n\r\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\r\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\r\n\t\t\t highp vec2 texPos1;\n\r\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\r\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\r\n\t\t\t highp vec2 texPos2;\n\r\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\r\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\r\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\r\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\r\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\r\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\r\n\t\t}\n\r\n\t\t", - F.registerNodeType("texture/LUT", t), G.title = "Texture to Channels", G.desc = "Split texture channels", G.prototype.onExecute = function() { + }, u.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tuniform float u_amount;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\r\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\r\n\t\t\t mediump vec2 quad1;\n\r\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\r\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\r\n\t\t\t mediump vec2 quad2;\n\r\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\r\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\r\n\t\t\t highp vec2 texPos1;\n\r\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\r\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\r\n\t\t\t highp vec2 texPos2;\n\r\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\r\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\r\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\r\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\r\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\r\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\r\n\t\t}\n\r\n\t\t", + F.registerNodeType("texture/LUT", u), G.title = "Texture to Channels", G.desc = "Split texture channels", G.prototype.onExecute = function() { var a = this.getInputData(0); if (a) { this._channels || (this._channels = Array(4)); @@ -7733,8 +7736,8 @@ $jscomp.polyfill("Object.values", function(w) { n._shader || (n._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, n.pixel_shader)); var e = n._shader; a = Math.max(b.width, d.width, k.width, f.width); - var g = Math.max(b.height, d.height, k.height, f.height), q = this.properties.precision == c.HIGH ? c.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE; - this._texture && this._texture.width == a && this._texture.height == g && this._texture.type == q || (this._texture = new GL.Texture(a, g, {type:q, format:gl.RGBA, filter:gl.LINEAR})); + var g = Math.max(b.height, d.height, k.height, f.height), p = this.properties.precision == c.HIGH ? c.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE; + this._texture && this._texture.width == a && this._texture.height == g && this._texture.type == p || (this._texture = new GL.Texture(a, g, {type:p, format:gl.RGBA, filter:gl.LINEAR})); a = this._color; a[0] = this.properties.R; a[1] = this.properties.G; @@ -7750,11 +7753,11 @@ $jscomp.polyfill("Object.values", function(w) { }); this.setOutputData(0, this._texture); }, n.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_textureR;\n\r\n\t\tuniform sampler2D u_textureG;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tuniform sampler2D u_textureA;\n\r\n\t\tuniform vec4 u_color;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t gl_FragColor = u_color * vec4( \r\n\t\t\t\t\ttexture2D(u_textureR, v_coord).r,\r\n\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\r\n\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\r\n\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\r\n\t\t}\n\r\n\t\t", - F.registerNodeType("texture/channelsTexture", n), q.title = "Color", q.desc = "Generates a 1x1 texture with a constant color", q.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, q.prototype.onDrawBackground = function(a) { + F.registerNodeType("texture/channelsTexture", n), p.title = "Color", p.desc = "Generates a 1x1 texture with a constant color", p.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, p.prototype.onDrawBackground = function(a) { var b = this.properties.color; a.fillStyle = "rgb(" + Math.floor(255 * Math.clamp(b[0], 0, 1)) + "," + Math.floor(255 * Math.clamp(b[1], 0, 1)) + "," + Math.floor(255 * Math.clamp(b[2], 0, 1)) + ")"; this.flags.collapsed ? this.boxcolor = a.fillStyle : a.fillRect(0, 0, this.size[0], this.size[1]); - }, q.prototype.onExecute = function() { + }, p.prototype.onExecute = function() { var a = this.properties.precision == c.HIGH ? c.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE; this._tex && this._tex.type == a || (this._tex = new GL.Texture(1, 1, {format:gl.RGBA, type:a, minFilter:gl.NEAREST})); a = this.properties.color; @@ -7784,9 +7787,9 @@ $jscomp.polyfill("Object.values", function(w) { } 0.001 < vec4.sqrDist(this._tex_color, a) && (this._tex_color.set(a), this._tex.fill(a)); this.setOutputData(0, this._tex); - }, q.prototype.onGetInputs = function() { + }, p.prototype.onGetInputs = function() { return [["RGB", "vec3"], ["RGBA", "vec4"], ["R", "number"], ["G", "number"], ["B", "number"], ["A", "number"]]; - }, F.registerNodeType("texture/color", q), k.title = "Gradient", k.desc = "Generates a gradient", k["@A"] = {type:"color"}, k["@B"] = {type:"color"}, k["@texture_size"] = {type:"enum", values:[32, 64, 128, 256, 512]}, k.prototype.onExecute = function() { + }, F.registerNodeType("texture/color", p), k.title = "Gradient", k.desc = "Generates a gradient", k["@A"] = {type:"color"}, k["@B"] = {type:"color"}, k["@texture_size"] = {type:"enum", values:[32, 64, 128, 256, 512]}, k.prototype.onExecute = function() { gl.disable(gl.BLEND); gl.disable(gl.DEPTH_TEST); var a = GL.Mesh.getScreenQuad(), b = k._shader, d = this.getInputData(0); @@ -7959,51 +7962,51 @@ $jscomp.polyfill("Object.values", function(w) { h.u_threshold = this.getInputOrProperty("threshold"); var g = e[0] = GL.Texture.getTemporary(b, d, k); a.blit(g, n.uniforms(h)); - var q = g, l = this.getInputOrProperty("iterations"); + var p = g, l = this.getInputOrProperty("iterations"); l = Math.clamp(l, 1, 16) | 0; - var m = h.u_texel_size, p = this.getInputOrProperty("intensity"); + var m = h.u_texel_size, r = this.getInputOrProperty("intensity"); h.u_intensity = 1; h.u_delta = this.properties.scale; n = x._shader; n || (n = x._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, x.scale_pixel_shader)); - for (var r = 1; r < l; r++) { + for (var q = 1; q < l; q++) { b >>= 1; 1 < (d | 0) && (d >>= 1); if (2 > b) { break; } - g = e[r] = GL.Texture.getTemporary(b, d, k); - m[0] = 1 / q.width; - m[1] = 1 / q.height; - q.blit(g, n.uniforms(h)); - q = g; + g = e[q] = GL.Texture.getTemporary(b, d, k); + m[0] = 1 / p.width; + m[1] = 1 / p.height; + p.blit(g, n.uniforms(h)); + p = g; } - this.isOutputConnected(2) && (b = this._average_texture, b && b.type == a.type && b.format == a.format || (b = this._average_texture = new GL.Texture(1, 1, {type:a.type, format:a.format, filter:gl.LINEAR})), m[0] = 1 / q.width, m[1] = 1 / q.height, h.u_intensity = p, h.u_delta = 1, q.blit(b, n.uniforms(h)), this.setOutputData(2, b)); + this.isOutputConnected(2) && (b = this._average_texture, b && b.type == a.type && b.format == a.format || (b = this._average_texture = new GL.Texture(1, 1, {type:a.type, format:a.format, filter:gl.LINEAR})), m[0] = 1 / p.width, m[1] = 1 / p.height, h.u_intensity = r, h.u_delta = 1, p.blit(b, n.uniforms(h)), this.setOutputData(2, b)); gl.enable(gl.BLEND); gl.blendFunc(gl.ONE, gl.ONE); h.u_intensity = this.getInputOrProperty("persistence"); h.u_delta = 0.5; - for (r -= 2; 0 <= r; r--) { - g = e[r], e[r] = null, m[0] = 1 / q.width, m[1] = 1 / q.height, q.blit(g, n.uniforms(h)), GL.Texture.releaseTemporary(q), q = g; + for (q -= 2; 0 <= q; q--) { + g = e[q], e[q] = null, m[0] = 1 / p.width, m[1] = 1 / p.height, p.blit(g, n.uniforms(h)), GL.Texture.releaseTemporary(p), p = g; } gl.disable(gl.BLEND); - this.isOutputConnected(1) && (e = this._glow_texture, e && e.width == a.width && e.height == a.height && e.type == f && e.format == a.format || (e = this._glow_texture = new GL.Texture(a.width, a.height, {type:f, format:a.format, filter:gl.LINEAR})), q.blit(e), this.setOutputData(1, e)); + this.isOutputConnected(1) && (e = this._glow_texture, e && e.width == a.width && e.height == a.height && e.type == f && e.format == a.format || (e = this._glow_texture = new GL.Texture(a.width, a.height, {type:f, format:a.format, filter:gl.LINEAR})), p.blit(e), this.setOutputData(1, e)); if (this.isOutputConnected(0)) { e = this._final_texture; e && e.width == a.width && e.height == a.height && e.type == f && e.format == a.format || (e = this._final_texture = new GL.Texture(a.width, a.height, {type:f, format:a.format, filter:gl.LINEAR})); - var u = this.getInputData(1), t = this.getInputOrProperty("dirt_factor"); - h.u_intensity = p; - n = u ? x._dirt_final_shader : x._final_shader; - n || (n = u ? x._dirt_final_shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, x.final_pixel_shader, {USE_DIRT:""}) : x._final_shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, x.final_pixel_shader)); + var t = this.getInputData(1), u = this.getInputOrProperty("dirt_factor"); + h.u_intensity = r; + n = t ? x._dirt_final_shader : x._final_shader; + n || (n = t ? x._dirt_final_shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, x.final_pixel_shader, {USE_DIRT:""}) : x._final_shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, x.final_pixel_shader)); e.drawTo(function() { a.bind(0); - q.bind(1); - u && (n.setUniform("u_dirt_factor", t), n.setUniform("u_dirt_texture", u.bind(2))); + p.bind(1); + t && (n.setUniform("u_dirt_factor", u), n.setUniform("u_dirt_texture", t.bind(2))); n.toViewport(h); }); this.setOutputData(0, e); } - GL.Texture.releaseTemporary(q); + GL.Texture.releaseTemporary(p); } } }, x.cut_pixel_shader = "precision highp float;\n\r\n\tvarying vec2 v_coord;\n\r\n\tuniform sampler2D u_texture;\n\r\n\tuniform float u_threshold;\n\r\n\tvoid main() {\n\r\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\r\n\t}", x.scale_pixel_shader = "precision highp float;\n\r\n\tvarying vec2 v_coord;\n\r\n\tuniform sampler2D u_texture;\n\r\n\tuniform vec2 u_texel_size;\n\r\n\tuniform float u_delta;\n\r\n\tuniform float u_intensity;\n\r\n\t\n\r\n\tvec4 sampleBox(vec2 uv) {\n\r\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\r\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\r\n\t\treturn s * 0.25;\n\r\n\t}\n\r\n\tvoid main() {\n\r\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\r\n\t}", @@ -8117,9 +8120,9 @@ $jscomp.polyfill("Object.values", function(w) { } }, J.prototype.onGetOutputs = function() { return [["width", "number"], ["height", "number"], ["stream_ready", F.EVENT], ["stream_closed", F.EVENT], ["stream_error", F.EVENT]]; - }, F.registerNodeType("texture/webcam", J), L.title = "Lens FX", L.desc = "distortion and chromatic aberration", L.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, L.prototype.onGetInputs = function() { + }, F.registerNodeType("texture/webcam", J), K.title = "Lens FX", K.desc = "distortion and chromatic aberration", K.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, K.prototype.onGetInputs = function() { return [["enabled", "boolean"]]; - }, L.prototype.onExecute = function() { + }, K.prototype.onExecute = function() { var a = this.getInputData(0); if (a && this.isOutputConnected(0)) { if (this.properties.precision === c.PASS_THROUGH || !1 === this.getInputOrProperty("enabled")) { @@ -8127,8 +8130,8 @@ $jscomp.polyfill("Object.values", function(w) { } else { var b = this._temp_texture; b && b.width == a.width && b.height == a.height && b.type == a.type || (b = this._temp_texture = new GL.Texture(a.width, a.height, {type:a.type, format:gl.RGBA, filter:gl.LINEAR})); - var d = L._shader; - d || (d = L._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, L.pixel_shader)); + var d = K._shader; + d || (d = K._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, K.pixel_shader)); var k = this.getInputData(1); null == k && (k = this.properties.factor); var f = this._uniforms; @@ -8141,8 +8144,8 @@ $jscomp.polyfill("Object.values", function(w) { this.setOutputData(0, b); } } - }, L.pixel_shader = "precision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_factor;\n\r\n\t\tvec2 barrelDistortion(vec2 coord, float amt) {\n\r\n\t\t\tvec2 cc = coord - 0.5;\n\r\n\t\t\tfloat dist = dot(cc, cc);\n\r\n\t\t\treturn coord + cc * dist * amt;\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tfloat sat( float t )\n\r\n\t\t{\n\r\n\t\t\treturn clamp( t, 0.0, 1.0 );\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tfloat linterp( float t ) {\n\r\n\t\t\treturn sat( 1.0 - abs( 2.0*t - 1.0 ) );\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tfloat remap( float t, float a, float b ) {\n\r\n\t\t\treturn sat( (t - a) / (b - a) );\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tvec4 spectrum_offset( float t ) {\n\r\n\t\t\tvec4 ret;\n\r\n\t\t\tfloat lo = step(t,0.5);\n\r\n\t\t\tfloat hi = 1.0-lo;\n\r\n\t\t\tfloat w = linterp( remap( t, 1.0/6.0, 5.0/6.0 ) );\n\r\n\t\t\tret = vec4(lo,1.0,hi, 1.) * vec4(1.0-w, w, 1.0-w, 1.);\n\r\n\t\t\n\r\n\t\t\treturn pow( ret, vec4(1.0/2.2) );\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tconst float max_distort = 2.2;\n\r\n\t\tconst int num_iter = 12;\n\r\n\t\tconst float reci_num_iter_f = 1.0 / float(num_iter);\n\r\n\t\t\n\r\n\t\tvoid main()\n\r\n\t\t{\t\n\r\n\t\t\tvec2 uv=v_coord;\n\r\n\t\t\tvec4 sumcol = vec4(0.0);\n\r\n\t\t\tvec4 sumw = vec4(0.0);\t\n\r\n\t\t\tfor ( int i=0; i c; ++c) { this.valid_notes[c] = -1 != this.notes_pitches.indexOf(c); } @@ -9228,7 +9231,7 @@ $jscomp.polyfill("Object.values", function(w) { var c = this.getInputData(1); null != c && c != this._current_scale && this.processScale(c); }; - t.registerNodeType("midi/quantize", z); + u.registerNodeType("midi/quantize", z); e.title = "MIDI fromFile"; e.desc = "Plays a MIDI file"; e.color = "#243"; @@ -9264,7 +9267,7 @@ $jscomp.polyfill("Object.values", function(w) { }; e.prototype.loadMIDIFile = function(c) { var e = this; - t.fetchFile(c, "arraybuffer", function(c) { + u.fetchFile(c, "arraybuffer", function(c) { e.boxcolor = "#AFA"; e._midi = MidiParser.parse(new Uint8Array(c)); e.properties.autoplay && e.play(); @@ -9277,7 +9280,7 @@ $jscomp.polyfill("Object.values", function(w) { this.properties.url = ""; this.loadMIDIFile(c); }; - t.registerNodeType("midi/fromFile", e); + u.registerNodeType("midi/fromFile", e); C.title = "MIDI Play"; C.desc = "Plays a MIDI note"; C.color = "#243"; @@ -9299,7 +9302,7 @@ $jscomp.polyfill("Object.values", function(w) { c = this.getInputData(2); null != c && (this.properties.duration = c); }; - t.registerNodeType("midi/play", C); + u.registerNodeType("midi/play", C); D.title = "MIDI Keys"; D.desc = "Keyboard to play notes"; D.color = "#243"; @@ -9377,9 +9380,9 @@ $jscomp.polyfill("Object.values", function(w) { return e = this.getKeyIndex(g), this.keys[e] = !1, this._last_key = -1, e = 12 * (this.properties.start_octave - 1) + 29 + e, g = new c, g.setup([c.NOTEOFF, e, 100]), this.trigger("note", g), !0; } }; - t.registerNodeType("midi/keys", D); + u.registerNodeType("midi/keys", D); })(this); -(function(w) { +(function(v) { function c() { this.properties = {src:"", gain:0.5, loop:!0, autoplay:!0, playbackRate:1}; this._loading_audio = !1; @@ -9388,24 +9391,24 @@ $jscomp.polyfill("Object.values", function(w) { this._last_sourcenode = null; this.addOutput("out", "audio"); this.addInput("gain", "number"); - this.audionode = q.getAudioContext().createGain(); + this.audionode = p.getAudioContext().createGain(); this.audionode.graphnode = this; this.audionode.gain.value = this.properties.gain; this.properties.src && this.loadSound(this.properties.src); } - function p() { + function q() { this.properties = {gain:0.5}; this._audionodes = []; this._media_stream = null; this.addOutput("out", "audio"); this.addInput("gain", "number"); - this.audionode = q.getAudioContext().createGain(); + this.audionode = p.getAudioContext().createGain(); this.audionode.graphnode = this; this.audionode.gain.value = this.properties.gain; } function m() { this.properties = {fftSize:2048, minDecibels:-100, maxDecibels:-10, smoothingTimeConstant:0.5}; - this.audionode = q.getAudioContext().createAnalyser(); + this.audionode = p.getAudioContext().createAnalyser(); this.audionode.graphnode = this; this.audionode.fftSize = this.properties.fftSize; this.audionode.minDecibels = this.properties.minDecibels; @@ -9418,36 +9421,36 @@ $jscomp.polyfill("Object.values", function(w) { } function g() { this.properties = {gain:1}; - this.audionode = q.getAudioContext().createGain(); + this.audionode = p.getAudioContext().createGain(); this.addInput("in", "audio"); this.addInput("gain", "number"); this.addOutput("out", "audio"); } - function u() { + function r() { this.properties = {impulse_src:"", normalize:!0}; - this.audionode = q.getAudioContext().createConvolver(); + this.audionode = p.getAudioContext().createConvolver(); this.addInput("in", "audio"); this.addOutput("out", "audio"); } function l() { this.properties = {threshold:-50, knee:40, ratio:12, reduction:-20, attack:0, release:0.25}; - this.audionode = q.getAudioContext().createDynamicsCompressor(); + this.audionode = p.getAudioContext().createDynamicsCompressor(); this.addInput("in", "audio"); this.addOutput("out", "audio"); } function B() { this.properties = {}; - this.audionode = q.getAudioContext().createWaveShaper(); + this.audionode = p.getAudioContext().createWaveShaper(); this.addInput("in", "audio"); this.addInput("shape", "waveshape"); this.addOutput("out", "audio"); } function y() { this.properties = {gain1:0.5, gain2:0.5}; - this.audionode = q.getAudioContext().createGain(); - this.audionode1 = q.getAudioContext().createGain(); + this.audionode = p.getAudioContext().createGain(); + this.audionode1 = p.getAudioContext().createGain(); this.audionode1.gain.value = this.properties.gain1; - this.audionode2 = q.getAudioContext().createGain(); + this.audionode2 = p.getAudioContext().createGain(); this.audionode2.gain.value = this.properties.gain2; this.audionode1.connect(this.audionode); this.audionode2.connect(this.audionode); @@ -9457,9 +9460,9 @@ $jscomp.polyfill("Object.values", function(w) { this.addInput("in2 gain", "number"); this.addOutput("out", "audio"); } - function v() { + function w() { this.properties = {A:0.1, D:0.1, S:0.1, R:0.1}; - this.audionode = q.getAudioContext().createGain(); + this.audionode = p.getAudioContext().createGain(); this.audionode.gain.value = 0; this.addInput("in", "audio"); this.addInput("gate", "bool"); @@ -9468,7 +9471,7 @@ $jscomp.polyfill("Object.values", function(w) { } function E() { this.properties = {delayTime:0.5}; - this.audionode = q.getAudioContext().createDelay(10); + this.audionode = p.getAudioContext().createDelay(10); this.audionode.delayTime.value = this.properties.delayTime; this.addInput("in", "audio"); this.addInput("time", "number"); @@ -9477,14 +9480,14 @@ $jscomp.polyfill("Object.values", function(w) { function z() { this.properties = {frequency:350, detune:0, Q:1}; this.addProperty("type", "lowpass", "enum", {values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")}); - this.audionode = q.getAudioContext().createBiquadFilter(); + this.audionode = p.getAudioContext().createBiquadFilter(); this.addInput("in", "audio"); this.addOutput("out", "audio"); } function e() { this.properties = {frequency:440, detune:0, type:"sine"}; this.addProperty("type", "sine", "enum", {values:["sine", "square", "sawtooth", "triangle", "custom"]}); - this.audionode = q.getAudioContext().createOscillator(); + this.audionode = p.getAudioContext().createOscillator(); this.addOutput("out", "audio"); } function C() { @@ -9499,26 +9502,26 @@ $jscomp.polyfill("Object.values", function(w) { this.addInput("freqs", "array"); this.addOutput("signal", "number"); } - function t() { - if (!t.default_code) { - var c = t.default_function.toString(), a = c.indexOf("{") + 1, b = c.lastIndexOf("}"); - t.default_code = c.substr(a, b - a); + function u() { + if (!u.default_code) { + var c = u.default_function.toString(), a = c.indexOf("{") + 1, b = c.lastIndexOf("}"); + u.default_code = c.substr(a, b - a); } - this.properties = {code:t.default_code}; - c = q.getAudioContext(); + this.properties = {code:u.default_code}; + c = p.getAudioContext(); c.createScriptProcessor ? this.audionode = c.createScriptProcessor(4096, 1, 1) : (console.warn("ScriptProcessorNode deprecated"), this.audionode = c.createGain()); this.processCode(); - t._bypass_function || (t._bypass_function = this.audionode.onaudioprocess); + u._bypass_function || (u._bypass_function = this.audionode.onaudioprocess); this.addInput("in", "audio"); this.addOutput("out", "audio"); } function G() { - this.audionode = q.getAudioContext().destination; + this.audionode = p.getAudioContext().destination; this.addInput("in", "audio"); } - var n = w.LiteGraph, q = {}; - w.LGAudio = q; - q.getAudioContext = function() { + var n = v.LiteGraph, p = {}; + v.LGAudio = p; + p.getAudioContext = function() { if (!this._audio_context) { window.AudioContext = window.AudioContext || window.webkitAudioContext; if (!window.AudioContext) { @@ -9537,21 +9540,21 @@ $jscomp.polyfill("Object.values", function(w) { } return this._audio_context; }; - q.connect = function(c, a) { + p.connect = function(c, a) { try { c.connect(a); } catch (b) { console.warn("LGraphAudio:", b); } }; - q.disconnect = function(c, a) { + p.disconnect = function(c, a) { try { c.disconnect(a); } catch (b) { console.warn("LGraphAudio:", b); } }; - q.changeAllAudiosConnections = function(c, a) { + p.changeAllAudiosConnections = function(c, a) { if (c.inputs) { for (var b = 0; b < c.inputs.length; ++b) { var d = c.graph.links[c.inputs[b].link]; @@ -9559,7 +9562,7 @@ $jscomp.polyfill("Object.values", function(w) { var e = c.graph.getNodeById(d.origin_id); e = e.getAudioNodeInOutputSlot ? e.getAudioNodeInOutputSlot(d.origin_slot) : e.audionode; d = c.getAudioNodeInInputSlot ? c.getAudioNodeInInputSlot(b) : c.audionode; - a ? q.connect(e, d) : q.disconnect(e, d); + a ? p.connect(e, d) : p.disconnect(e, d); } } } @@ -9570,42 +9573,42 @@ $jscomp.polyfill("Object.values", function(w) { e = c.getAudioNodeInOutputSlot ? c.getAudioNodeInOutputSlot(b) : c.audionode; var l = c.graph.getNodeById(d.target_id); d = l.getAudioNodeInInputSlot ? l.getAudioNodeInInputSlot(d.target_slot) : l.audionode; - a ? q.connect(e, d) : q.disconnect(e, d); + a ? p.connect(e, d) : p.disconnect(e, d); } } } } }; - q.onConnectionsChange = function(c, a, b, d) { - c == n.OUTPUT && (c = null, d && (c = this.graph.getNodeById(d.target_id)), c && (a = this.getAudioNodeInOutputSlot ? this.getAudioNodeInOutputSlot(a) : this.audionode, d = c.getAudioNodeInInputSlot ? c.getAudioNodeInInputSlot(d.target_slot) : c.audionode, b ? q.connect(a, d) : q.disconnect(a, d))); + p.onConnectionsChange = function(c, a, b, d) { + c == n.OUTPUT && (c = null, d && (c = this.graph.getNodeById(d.target_id)), c && (a = this.getAudioNodeInOutputSlot ? this.getAudioNodeInOutputSlot(a) : this.audionode, d = c.getAudioNodeInInputSlot ? c.getAudioNodeInInputSlot(d.target_slot) : c.audionode, b ? p.connect(a, d) : p.disconnect(a, d))); }; - q.createAudioNodeWrapper = function(c) { + p.createAudioNodeWrapper = function(c) { var a = c.prototype.onPropertyChanged; c.prototype.onPropertyChanged = function(b, d) { a && a.call(this, b, d); this.audionode && void 0 !== this.audionode[b] && (void 0 !== this.audionode[b].value ? this.audionode[b].value = d : this.audionode[b] = d); }; - c.prototype.onConnectionsChange = q.onConnectionsChange; + c.prototype.onConnectionsChange = p.onConnectionsChange; }; - q.cached_audios = {}; - q.loadSound = function(c, a, b) { + p.cached_audios = {}; + p.loadSound = function(c, a, b) { function d(a) { console.log("Audio loading sample error:", a); b && b(a); } - if (q.cached_audios[c] && -1 == c.indexOf("blob:")) { - a && a(q.cached_audios[c]); + if (p.cached_audios[c] && -1 == c.indexOf("blob:")) { + a && a(p.cached_audios[c]); } else { - q.onProcessAudioURL && (c = q.onProcessAudioURL(c)); + p.onProcessAudioURL && (c = p.onProcessAudioURL(c)); var e = new XMLHttpRequest; e.open("GET", c, !0); e.responseType = "arraybuffer"; - var k = q.getAudioContext(); + var k = p.getAudioContext(); e.onload = function() { console.log("AudioSource loaded"); k.decodeAudioData(e.response, function(b) { console.log("AudioSource decoded"); - q.cached_audios[c] = b; + p.cached_audios[c] = b; a && a(b); }, d); }; @@ -9644,10 +9647,10 @@ $jscomp.polyfill("Object.values", function(w) { this._audionodes.length = 0; }; c.prototype.pauseAllSounds = function() { - q.getAudioContext().suspend(); + p.getAudioContext().suspend(); }; c.prototype.unpauseAllSounds = function() { - q.getAudioContext().resume(); + p.getAudioContext().resume(); }; c.prototype.onExecute = function() { if (this.inputs) { @@ -9698,7 +9701,7 @@ $jscomp.polyfill("Object.values", function(w) { } }; c.prototype.playBuffer = function(c) { - var a = this, b = q.getAudioContext().createBufferSource(); + var a = this, b = p.getAudioContext().createBufferSource(); this._last_sourcenode = b; b.graphnode = this; b.buffer = c; @@ -9721,7 +9724,7 @@ $jscomp.polyfill("Object.values", function(w) { this._request && (this._request.abort(), this._request = null); this._audiobuffer = null; this._loading_audio = !1; - c && (this._request = q.loadSound(c, function(b) { + c && (this._request = p.loadSound(c, function(b) { this.boxcolor = n.NODE_DEFAULT_BOXCOLOR; a._audiobuffer = b; a._loading_audio = !1; @@ -9730,7 +9733,7 @@ $jscomp.polyfill("Object.values", function(w) { } }), this._loading_audio = !0, this.boxcolor = "#AA4"); }; - c.prototype.onConnectionsChange = q.onConnectionsChange; + c.prototype.onConnectionsChange = p.onConnectionsChange; c.prototype.onGetInputs = function() { return [["playbackRate", "number"], ["src", "string"], ["Play", n.ACTION], ["Stop", n.ACTION]]; }; @@ -9747,24 +9750,24 @@ $jscomp.polyfill("Object.values", function(w) { c.title = "Source"; c.desc = "Plays audio"; n.registerNodeType("audio/source", c); - p.prototype.onAdded = function(c) { + q.prototype.onAdded = function(c) { if (c.status === LGraph.STATUS_RUNNING) { this.onStart(); } }; - p.prototype.onStart = function() { + q.prototype.onStart = function() { null != this._media_stream || this._waiting_confirmation || this.openStream(); }; - p.prototype.onStop = function() { + q.prototype.onStop = function() { this.audionode.gain.value = 0; }; - p.prototype.onPause = function() { + q.prototype.onPause = function() { this.audionode.gain.value = 0; }; - p.prototype.onUnpause = function() { + q.prototype.onUnpause = function() { this.audionode.gain.value = this.properties.gain; }; - p.prototype.onRemoved = function() { + q.prototype.onRemoved = function() { this.audionode.gain.value = 0; this.audiosource_node && (this.audiosource_node.disconnect(this.audionode), this.audiosource_node = null); if (this._media_stream) { @@ -9772,7 +9775,7 @@ $jscomp.polyfill("Object.values", function(w) { c.length && c[0].stop(); } }; - p.prototype.openStream = function() { + q.prototype.openStream = function() { if (navigator.mediaDevices) { this._waiting_confirmation = !0; navigator.mediaDevices.getUserMedia({audio:!0, video:!1}).then(this.streamReady.bind(this)).catch(function(a) { @@ -9785,15 +9788,15 @@ $jscomp.polyfill("Object.values", function(w) { console.log("getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags"); } }; - p.prototype.streamReady = function(c) { + q.prototype.streamReady = function(c) { this._media_stream = c; this.audiosource_node && this.audiosource_node.disconnect(this.audionode); - this.audiosource_node = q.getAudioContext().createMediaStreamSource(c); + this.audiosource_node = p.getAudioContext().createMediaStreamSource(c); this.audiosource_node.graphnode = this; this.audiosource_node.connect(this.audionode); this.boxcolor = "white"; }; - p.prototype.onExecute = function() { + q.prototype.onExecute = function() { null != this._media_stream || this._waiting_confirmation || this.openStream(); if (this.inputs) { for (var c = 0; c < this.inputs.length; ++c) { @@ -9805,19 +9808,19 @@ $jscomp.polyfill("Object.values", function(w) { } } }; - p.prototype.onAction = function(c) { + q.prototype.onAction = function(c) { "Play" == c ? this.audionode.gain.value = this.properties.gain : "Stop" == c && (this.audionode.gain.value = 0); }; - p.prototype.onPropertyChanged = function(c, a) { + q.prototype.onPropertyChanged = function(c, a) { "gain" == c && (this.audionode.gain.value = a); }; - p.prototype.onConnectionsChange = q.onConnectionsChange; - p.prototype.onGetInputs = function() { + q.prototype.onConnectionsChange = p.onConnectionsChange; + q.prototype.onGetInputs = function() { return [["playbackRate", "number"], ["Play", n.ACTION], ["Stop", n.ACTION]]; }; - p.title = "MediaSource"; - p.desc = "Plays microphone"; - n.registerNodeType("audio/media_source", p); + q.title = "MediaSource"; + q.desc = "Plays microphone"; + n.registerNodeType("audio/media_source", q); m.prototype.onPropertyChanged = function(c, a) { this.audionode[c] = a; }; @@ -9854,39 +9857,39 @@ $jscomp.polyfill("Object.values", function(w) { } } }; - q.createAudioNodeWrapper(g); + p.createAudioNodeWrapper(g); g.title = "Gain"; g.desc = "Audio gain"; n.registerNodeType("audio/gain", g); - q.createAudioNodeWrapper(u); - u.prototype.onRemove = function() { + p.createAudioNodeWrapper(r); + r.prototype.onRemove = function() { this._dropped_url && URL.revokeObjectURL(this._dropped_url); }; - u.prototype.onPropertyChanged = function(c, a) { + r.prototype.onPropertyChanged = function(c, a) { "impulse_src" == c ? this.loadImpulse(a) : "normalize" == c && (this.audionode.normalize = a); }; - u.prototype.onDropFile = function(c) { + r.prototype.onDropFile = function(c) { this._dropped_url && URL.revokeObjectURL(this._dropped_url); this._dropped_url = URL.createObjectURL(c); this.properties.impulse_src = this._dropped_url; this.loadImpulse(this._dropped_url); }; - u.prototype.loadImpulse = function(c) { + r.prototype.loadImpulse = function(c) { var a = this; this._request && (this._request.abort(), this._request = null); this._impulse_buffer = null; this._loading_impulse = !1; - c && (this._request = q.loadSound(c, function(b) { + c && (this._request = p.loadSound(c, function(b) { a._impulse_buffer = b; a.audionode.buffer = b; console.log("Impulse signal set"); a._loading_impulse = !1; }), this._loading_impulse = !0); }; - u.title = "Convolver"; - u.desc = "Convolves the signal (used for reverb)"; - n.registerNodeType("audio/convolver", u); - q.createAudioNodeWrapper(l); + r.title = "Convolver"; + r.desc = "Convolves the signal (used for reverb)"; + n.registerNodeType("audio/convolver", r); + p.createAudioNodeWrapper(l); l.prototype.onExecute = function() { if (this.inputs && this.inputs.length) { for (var c = 1; c < this.inputs.length; ++c) { @@ -9913,7 +9916,7 @@ $jscomp.polyfill("Object.values", function(w) { B.prototype.setWaveShape = function(c) { this.audionode.curve = c; }; - q.createAudioNodeWrapper(B); + p.createAudioNodeWrapper(B); y.prototype.getAudioNodeInInputSlot = function(c) { if (0 == c) { return this.audionode1; @@ -9933,23 +9936,23 @@ $jscomp.polyfill("Object.values", function(w) { } } }; - q.createAudioNodeWrapper(y); + p.createAudioNodeWrapper(y); y.title = "Mixer"; y.desc = "Audio mixer"; n.registerNodeType("audio/mixer", y); - v.prototype.onExecute = function() { - var c = q.getAudioContext().currentTime, a = this.audionode.gain, b = this.getInputData(1), d = this.getInputOrProperty("A"), e = this.getInputOrProperty("D"), f = this.getInputOrProperty("S"), g = this.getInputOrProperty("R"); + w.prototype.onExecute = function() { + var c = p.getAudioContext().currentTime, a = this.audionode.gain, b = this.getInputData(1), d = this.getInputOrProperty("A"), e = this.getInputOrProperty("D"), f = this.getInputOrProperty("S"), g = this.getInputOrProperty("R"); !this.gate && b ? (a.cancelScheduledValues(0), a.setValueAtTime(0, c), a.linearRampToValueAtTime(1, c + d), a.linearRampToValueAtTime(f, c + d + e)) : this.gate && !b && (a.cancelScheduledValues(0), a.setValueAtTime(a.value, c), a.linearRampToValueAtTime(0, c + g)); this.gate = b; }; - v.prototype.onGetInputs = function() { + w.prototype.onGetInputs = function() { return [["A", "number"], ["D", "number"], ["S", "number"], ["R", "number"]]; }; - q.createAudioNodeWrapper(v); - v.title = "ADSR"; - v.desc = "Audio envelope"; - n.registerNodeType("audio/adsr", v); - q.createAudioNodeWrapper(E); + p.createAudioNodeWrapper(w); + w.title = "ADSR"; + w.desc = "Audio envelope"; + n.registerNodeType("audio/adsr", w); + p.createAudioNodeWrapper(E); E.prototype.onExecute = function() { var c = this.getInputData(1); void 0 !== c && (this.audionode.delayTime.value = c); @@ -9971,7 +9974,7 @@ $jscomp.polyfill("Object.values", function(w) { z.prototype.onGetInputs = function() { return [["frequency", "number"], ["detune", "number"], ["Q", "number"]]; }; - q.createAudioNodeWrapper(z); + p.createAudioNodeWrapper(z); z.title = "BiquadFilter"; z.desc = "Audio filter"; n.registerNodeType("audio/biquadfilter", z); @@ -10007,7 +10010,7 @@ $jscomp.polyfill("Object.values", function(w) { e.prototype.onGetInputs = function() { return [["frequency", "number"], ["detune", "number"], ["type", "string"]]; }; - q.createAudioNodeWrapper(e); + p.createAudioNodeWrapper(e); e.title = "Oscillator"; e.desc = "Oscillator"; n.registerNodeType("audio/oscillator", e); @@ -10036,7 +10039,7 @@ $jscomp.polyfill("Object.values", function(w) { } } c.stroke(); - 0 <= this.properties.mark && (a = q.getAudioContext().sampleRate / a.length, e = this.properties.mark / a * 2 / b, e >= this.size[0] && (e = this.size[0] - 1), c.strokeStyle = "red", c.beginPath(), c.moveTo(e, d), c.lineTo(e, 0), c.stroke()); + 0 <= this.properties.mark && (a = p.getAudioContext().sampleRate / a.length, e = this.properties.mark / a * 2 / b, e >= this.size[0] && (e = this.size[0] - 1), c.strokeStyle = "red", c.beginPath(), c.moveTo(e, d), c.lineTo(e, 0), c.stroke()); } }; C.title = "Visualization"; @@ -10046,7 +10049,7 @@ $jscomp.polyfill("Object.values", function(w) { if (this._freqs = this.getInputData(0)) { var c = this.properties.band, a = this.getInputData(1); void 0 !== a && (c = a); - a = q.getAudioContext().sampleRate / this._freqs.length; + a = p.getAudioContext().sampleRate / this._freqs.length; a = c / a * 2; a >= this._freqs.length ? a = this._freqs[this._freqs.length - 1] : (c = a | 0, a -= c, a = this._freqs[c] * (1 - a) + this._freqs[c + 1] * a); this.setOutputData(0, a / 255 * this.properties.amplitude); @@ -10058,38 +10061,38 @@ $jscomp.polyfill("Object.values", function(w) { D.title = "Signal"; D.desc = "extract the signal of some frequency"; n.registerNodeType("audio/signal", D); - t.prototype.onAdded = function(c) { + u.prototype.onAdded = function(c) { c.status == LGraph.STATUS_RUNNING && (this.audionode.onaudioprocess = this._callback); }; - t["@code"] = {widget:"code", type:"code"}; - t.prototype.onStart = function() { + u["@code"] = {widget:"code", type:"code"}; + u.prototype.onStart = function() { this.audionode.onaudioprocess = this._callback; }; - t.prototype.onStop = function() { - this.audionode.onaudioprocess = t._bypass_function; + u.prototype.onStop = function() { + this.audionode.onaudioprocess = u._bypass_function; }; - t.prototype.onPause = function() { - this.audionode.onaudioprocess = t._bypass_function; + u.prototype.onPause = function() { + this.audionode.onaudioprocess = u._bypass_function; }; - t.prototype.onUnpause = function() { + u.prototype.onUnpause = function() { this.audionode.onaudioprocess = this._callback; }; - t.prototype.onExecute = function() { + u.prototype.onExecute = function() { }; - t.prototype.onRemoved = function() { - this.audionode.onaudioprocess = t._bypass_function; + u.prototype.onRemoved = function() { + this.audionode.onaudioprocess = u._bypass_function; }; - t.prototype.processCode = function() { + u.prototype.processCode = function() { try { this._script = new (new Function("properties", this.properties.code))(this.properties), this._old_code = this.properties.code, this._callback = this._script.onaudioprocess; } catch (k) { - console.error("Error in onaudioprocess code", k), this._callback = t._bypass_function, this.audionode.onaudioprocess = this._callback; + console.error("Error in onaudioprocess code", k), this._callback = u._bypass_function, this.audionode.onaudioprocess = this._callback; } }; - t.prototype.onPropertyChanged = function(c, a) { + u.prototype.onPropertyChanged = function(c, a) { "code" == c && (this.properties.code = a, this.processCode(), this.graph && this.graph.status == LGraph.STATUS_RUNNING && (this.audionode.onaudioprocess = this._callback)); }; - t.default_function = function() { + u.default_function = function() { this.onaudioprocess = function(c) { var a = c.inputBuffer; c = c.outputBuffer; @@ -10100,15 +10103,15 @@ $jscomp.polyfill("Object.values", function(w) { } }; }; - q.createAudioNodeWrapper(t); - t.title = "Script"; - t.desc = "apply script to signal"; - n.registerNodeType("audio/script", t); + p.createAudioNodeWrapper(u); + u.title = "Script"; + u.desc = "apply script to signal"; + n.registerNodeType("audio/script", u); G.title = "Destination"; G.desc = "Audio output"; n.registerNodeType("audio/destination", G); })(this); -(function(w) { +(function(v) { function c() { this.size = [60, 20]; this.addInput("send", m.ACTION); @@ -10120,7 +10123,7 @@ $jscomp.polyfill("Object.values", function(w) { this._last_sent_data = []; this._last_received_data = []; } - function p() { + function q() { this.room_widget = this.addWidget("text", "Room", "lgraph", this.setRoom.bind(this)); this.addWidget("button", "Reconnect", null, this.connectSocket.bind(this)); this.addInput("send", m.ACTION); @@ -10134,7 +10137,7 @@ $jscomp.polyfill("Object.values", function(w) { this._last_received_data = []; "undefined" == typeof SillyClient && console.warn("remember to add SillyClient.js to your project: https://tamats.com/projects/sillyserver/src/sillyclient.js"); } - var m = w.LiteGraph; + var m = v.LiteGraph; c.title = "WebSocket"; c.desc = "Send data through a websocket"; c.prototype.onPropertyChanged = function(c, m) { @@ -10144,14 +10147,14 @@ $jscomp.polyfill("Object.values", function(w) { !this._ws && this.properties.url && this.connectSocket(); if (this._ws && this._ws.readyState == WebSocket.OPEN) { for (var c = this.properties.room, m = this.properties.only_send_changes, l = 1; l < this.inputs.length; ++l) { - var p = this.getInputData(l); - if (null != p) { + var q = this.getInputData(l); + if (null != q) { try { - var w = JSON.stringify({type:0, room:c, channel:l, data:p}); - } catch (v) { + var v = JSON.stringify({type:0, room:c, channel:l, data:q}); + } catch (w) { continue; } - m && this._last_sent_data[l] == w || (this._last_sent_data[l] = w, this._ws.send(w)); + m && this._last_sent_data[l] == v || (this._last_sent_data[l] = v, this._ws.send(v)); } } for (l = 1; l < this.outputs.length; ++l) { @@ -10161,9 +10164,9 @@ $jscomp.polyfill("Object.values", function(w) { } }; c.prototype.connectSocket = function() { - var c = this, p = this.properties.url; - "ws" != p.substr(0, 2) && (p = "ws://" + p); - this._ws = new WebSocket(p); + var c = this, r = this.properties.url; + "ws" != r.substr(0, 2) && (r = "ws://" + r); + this._ws = new WebSocket(r); this._ws.onopen = function() { console.log("ready"); c.boxcolor = "#6C6"; @@ -10209,18 +10212,18 @@ $jscomp.polyfill("Object.values", function(w) { return [["out", 0]]; }; m.registerNodeType("network/websocket", c); - p.title = "SillyClient"; - p.desc = "Connects to SillyServer to broadcast messages"; - p.prototype.onPropertyChanged = function(c, m) { + q.title = "SillyClient"; + q.desc = "Connects to SillyServer to broadcast messages"; + q.prototype.onPropertyChanged = function(c, m) { "room" == c && (this.room_widget.value = m); this.connectSocket(); }; - p.prototype.setRoom = function(c) { + q.prototype.setRoom = function(c) { this.properties.room = c; this.room_widget.value = c; this.connectSocket(); }; - p.prototype.onDrawForeground = function() { + q.prototype.onDrawForeground = function() { for (var c = 1; c < this.inputs.length; ++c) { var m = this.inputs[c]; m.label = "in_" + c; @@ -10229,32 +10232,32 @@ $jscomp.polyfill("Object.values", function(w) { m = this.outputs[c], m.label = "out_" + c; } }; - p.prototype.onExecute = function() { + q.prototype.onExecute = function() { if (this._server && this._server.is_connected) { for (var c = this.properties.only_send_changes, m = 1; m < this.inputs.length; ++m) { - var l = this.getInputData(m), p = this._last_sent_data[m]; + var l = this.getInputData(m), q = this._last_sent_data[m]; if (null != l) { if (c) { - var w = !0; - if (l && l.length && p && p.length == l.length && l.constructor !== String) { - for (var v = 0; v < l.length; ++v) { - if (p[v] != l[v]) { - w = !1; + var v = !0; + if (l && l.length && q && q.length == l.length && l.constructor !== String) { + for (var w = 0; w < l.length; ++w) { + if (q[w] != l[w]) { + v = !1; break; } } } else { - this._last_sent_data[m] != l && (w = !1); + this._last_sent_data[m] != l && (v = !1); } - if (w) { + if (v) { continue; } } this._server.sendMessage({type:0, channel:m, data:l}); if (l.length && l.constructor !== String) { if (this._last_sent_data[m]) { - for (this._last_sent_data[m].length = l.length, v = 0; v < l.length; ++v) { - this._last_sent_data[m][v] = l[v]; + for (this._last_sent_data[m].length = l.length, w = 0; w < l.length; ++w) { + this._last_sent_data[m][w] = l[w]; } } else { this._last_sent_data[m] = l.constructor === Array ? l.concat() : new l.constructor(l); @@ -10270,7 +10273,7 @@ $jscomp.polyfill("Object.values", function(w) { "#AFA" == this.boxcolor && (this.boxcolor = "#6C6"); } }; - p.prototype.connectSocket = function() { + q.prototype.connectSocket = function() { var c = this; if ("undefined" == typeof SillyClient) { this._error || console.error("SillyClient node cannot be used, you must include SillyServer.js"), this._error = !0; @@ -10309,8 +10312,8 @@ $jscomp.polyfill("Object.values", function(w) { }, this.properties.url && this.properties.room) { try { this._server.connect(this.properties.url, this.properties.room); - } catch (u) { - console.error("SillyServer error: " + u); + } catch (r) { + console.error("SillyServer error: " + r); this._server = null; return; } @@ -10318,18 +10321,18 @@ $jscomp.polyfill("Object.values", function(w) { } } }; - p.prototype.send = function(c) { + q.prototype.send = function(c) { this._server && this._server.is_connected && this._server.sendMessage({type:1, data:c}); }; - p.prototype.onAction = function(c, m) { + q.prototype.onAction = function(c, m) { this._server && this._server.is_connected && this._server.sendMessage({type:1, action:c, data:m}); }; - p.prototype.onGetInputs = function() { + q.prototype.onGetInputs = function() { return [["in", 0]]; }; - p.prototype.onGetOutputs = function() { + q.prototype.onGetOutputs = function() { return [["out", 0]]; }; - m.registerNodeType("network/sillyclient", p); + m.registerNodeType("network/sillyclient", q); })(this); diff --git a/src/litegraph.d.ts b/src/litegraph.d.ts index c8168c80b..0d308b544 100644 --- a/src/litegraph.d.ts +++ b/src/litegraph.d.ts @@ -62,17 +62,16 @@ export interface IWidget { width: number, posY: number, height: number - ): number | undefined; + ): void; /** * Called by `LGraphCanvas.processNodeWidgets` * https://github.com/jagenjo/litegraph.js/issues/76 */ mouse?( - ctx: undefined, event: MouseEvent, pos: Vector2, node: LGraphNode - ): void; + ): boolean; /** Called by `LGraphNode.computeSize` */ computeSize?(width: number): [number, number]; } diff --git a/src/litegraph.js b/src/litegraph.js index 6260c112e..0603d8521 100755 --- a/src/litegraph.js +++ b/src/litegraph.js @@ -8260,7 +8260,6 @@ LGraphNode.prototype.executeAction = function(action) var margin = 15; for (var i = 0; i < widgets.length; ++i) { - var h = H; var w = widgets[i]; var y = posY; if (w.y) { @@ -8431,11 +8430,11 @@ LGraphNode.prototype.executeAction = function(action) break; default: if (w.draw) { - h = w.draw(ctx, node, width, y, H) || H; + w.draw(ctx, node, width, y, H); } break; } - posY += h + 4; + posY += (w.computeSize ? w.computeSize(width)[1] : H) + 4; ctx.globalAlpha = this.editor_alpha; } @@ -8467,7 +8466,8 @@ LGraphNode.prototype.executeAction = function(action) var w = node.widgets[i]; if(!w || w.disabled) continue; - if ( w == active_widget || (x > 6 && x < width - 12 && y > w.last_y && y < w.last_y + LiteGraph.NODE_WIDGET_HEIGHT) ) { + var widget_height = w.computeSize ? w.computeSize(width)[1] : LiteGraph.NODE_WIDGET_HEIGHT; + if ( w == active_widget || (x > 6 && x < width - 12 && y > w.last_y && y < w.last_y + widget_height) ) { //inside widget switch (w.type) { case "button": @@ -8601,7 +8601,7 @@ LGraphNode.prototype.executeAction = function(action) break; default: if (w.mouse) { - w.mouse(ctx, event, [x, y], node); + this.dirty_canvas = w.mouse(event, [x, y], node); } break; } //end switch From 0a2f8a1d6d86e59acab3c32c9428602a7e69f31d Mon Sep 17 00:00:00 2001 From: Shan M Date: Mon, 11 May 2020 13:44:52 +0300 Subject: [PATCH 33/63] added type def for callback onNodeAdded --- src/litegraph.d.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/litegraph.d.ts b/src/litegraph.d.ts index ac3218400..27b4a2b50 100644 --- a/src/litegraph.d.ts +++ b/src/litegraph.d.ts @@ -433,6 +433,11 @@ export declare class LGraph { * @param node the instance of the node */ add(node: LGraphNode, skip_compute_order?: boolean): void; + /** + * Called when a new node is added + * @param node the instance of the node + */ + onNodeAdded(node: LGraphNode): void; /** Removes a node from the graph */ remove(node: LGraphNode): void; /** Returns a node by its id. */ From c2f92e62aa89c01fd2176dd227985b081e2c5082 Mon Sep 17 00:00:00 2001 From: Shan M Date: Mon, 11 May 2020 13:49:59 +0300 Subject: [PATCH 34/63] fixes typedef for onDrawBackground and onDrawForeground fixes the issue #131 --- src/litegraph.d.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/litegraph.d.ts b/src/litegraph.d.ts index ac3218400..a3601dbbf 100644 --- a/src/litegraph.d.ts +++ b/src/litegraph.d.ts @@ -865,12 +865,12 @@ export declare class LGraphNode { // https://github.com/jagenjo/litegraph.js/blob/master/guides/README.md#custom-node-appearance onDrawBackground?( - canvas: HTMLCanvasElement, - ctx: CanvasRenderingContext2D + ctx: CanvasRenderingContext2D, + canvas: HTMLCanvasElement ): void; onDrawForeground?( - canvas: HTMLCanvasElement, - ctx: CanvasRenderingContext2D + ctx: CanvasRenderingContext2D, + canvas: HTMLCanvasElement ): void; // https://github.com/jagenjo/litegraph.js/blob/master/guides/README.md#custom-node-behaviour From 7ba41d1d75d88dd165b21889a9a5a110f2cfc071 Mon Sep 17 00:00:00 2001 From: altarfinch Date: Thu, 7 May 2020 19:45:36 +0200 Subject: [PATCH 35/63] custom widget custom size support --- build/litegraph.js | 21343 +++++++++++++++++++++++++++++++++------ build/litegraph.min.js | 10338 +++++++++++++++++++ src/litegraph.d.ts | 4 +- src/litegraph.js | 66 +- 4 files changed, 28534 insertions(+), 3217 deletions(-) diff --git a/build/litegraph.js b/build/litegraph.js index 0fd7b38f4..a99693f42 100644 --- a/build/litegraph.js +++ b/build/litegraph.js @@ -1,3 +1,4 @@ +<<<<<<< HEAD //packer version (function(global) { @@ -12245,6 +12246,12271 @@ if (typeof exports != "undefined") { LiteGraph.registerNodeType("basic/script", NodeScript); })(this); +//event related nodes +======= +>>>>>>> custom widget custom size support +(function(global) { + // ************************************************************* + // LiteGraph CLASS ******* + // ************************************************************* + + /** + * The Global Scope. It contains all the registered node classes. + * + * @class LiteGraph + * @constructor + */ + + var LiteGraph = (global.LiteGraph = { + VERSION: 0.4, + + CANVAS_GRID_SIZE: 10, + + NODE_TITLE_HEIGHT: 30, + NODE_TITLE_TEXT_Y: 20, + NODE_SLOT_HEIGHT: 20, + NODE_WIDGET_HEIGHT: 20, + NODE_WIDTH: 140, + NODE_MIN_WIDTH: 50, + NODE_COLLAPSED_RADIUS: 10, + NODE_COLLAPSED_WIDTH: 80, + NODE_TITLE_COLOR: "#999", + NODE_TEXT_SIZE: 14, + NODE_TEXT_COLOR: "#AAA", + NODE_SUBTEXT_SIZE: 12, + NODE_DEFAULT_COLOR: "#333", + NODE_DEFAULT_BGCOLOR: "#353535", + NODE_DEFAULT_BOXCOLOR: "#666", + NODE_DEFAULT_SHAPE: "box", + DEFAULT_SHADOW_COLOR: "rgba(0,0,0,0.5)", + DEFAULT_GROUP_FONT: 24, + + WIDGET_BGCOLOR: "#222", + WIDGET_OUTLINE_COLOR: "#666", + WIDGET_TEXT_COLOR: "#DDD", + WIDGET_SECONDARY_TEXT_COLOR: "#999", + + LINK_COLOR: "#9A9", + EVENT_LINK_COLOR: "#A86", + CONNECTING_LINK_COLOR: "#AFA", + + MAX_NUMBER_OF_NODES: 1000, //avoid infinite loops + DEFAULT_POSITION: [100, 100], //default node position + VALID_SHAPES: ["default", "box", "round", "card"], //,"circle" + + //shapes are used for nodes but also for slots + BOX_SHAPE: 1, + ROUND_SHAPE: 2, + CIRCLE_SHAPE: 3, + CARD_SHAPE: 4, + ARROW_SHAPE: 5, + + //enums + INPUT: 1, + OUTPUT: 2, + + EVENT: -1, //for outputs + ACTION: -1, //for inputs + + ALWAYS: 0, + ON_EVENT: 1, + NEVER: 2, + ON_TRIGGER: 3, + + UP: 1, + DOWN: 2, + LEFT: 3, + RIGHT: 4, + CENTER: 5, + + STRAIGHT_LINK: 0, + LINEAR_LINK: 1, + SPLINE_LINK: 2, + + NORMAL_TITLE: 0, + NO_TITLE: 1, + TRANSPARENT_TITLE: 2, + AUTOHIDE_TITLE: 3, + + proxy: null, //used to redirect calls + node_images_path: "", + + debug: false, + catch_exceptions: true, + throw_errors: true, + allow_scripts: false, //if set to true some nodes like Formula would be allowed to evaluate code that comes from unsafe sources (like node configuration), which could lead to exploits + registered_node_types: {}, //nodetypes by string + node_types_by_file_extension: {}, //used for dropping files in the canvas + Nodes: {}, //node types by classname + + searchbox_extras: {}, //used to add extra features to the search box + + /** + * Register a node class so it can be listed when the user wants to create a new one + * @method registerNodeType + * @param {String} type name of the node and path + * @param {Class} base_class class containing the structure of a node + */ + + registerNodeType: function(type, base_class) { + if (!base_class.prototype) { + throw "Cannot register a simple object, it must be a class with a prototype"; + } + base_class.type = type; + + if (LiteGraph.debug) { + console.log("Node registered: " + type); + } + + var categories = type.split("/"); + var classname = base_class.name; + + var pos = type.lastIndexOf("/"); + base_class.category = type.substr(0, pos); + + if (!base_class.title) { + base_class.title = classname; + } + //info.name = name.substr(pos+1,name.length - pos); + + //extend class + if (base_class.prototype) { + //is a class + for (var i in LGraphNode.prototype) { + if (!base_class.prototype[i]) { + base_class.prototype[i] = LGraphNode.prototype[i]; + } + } + } + + var prev = this.registered_node_types[type]; + if(prev) + console.log("replacing node type: " + type); + else + { + if( !Object.hasOwnProperty( base_class.prototype, "shape") ) + Object.defineProperty(base_class.prototype, "shape", { + set: function(v) { + switch (v) { + case "default": + delete this._shape; + break; + case "box": + this._shape = LiteGraph.BOX_SHAPE; + break; + case "round": + this._shape = LiteGraph.ROUND_SHAPE; + break; + case "circle": + this._shape = LiteGraph.CIRCLE_SHAPE; + break; + case "card": + this._shape = LiteGraph.CARD_SHAPE; + break; + default: + this._shape = v; + } + }, + get: function(v) { + return this._shape; + }, + enumerable: true, + configurable: true + }); + + //warnings + if (base_class.prototype.onPropertyChange) { + console.warn( + "LiteGraph node class " + + type + + " has onPropertyChange method, it must be called onPropertyChanged with d at the end" + ); + } + + //used to know which nodes create when dragging files to the canvas + if (base_class.supported_extensions) { + for (var i in base_class.supported_extensions) { + var ext = base_class.supported_extensions[i]; + if(ext && ext.constructor === String) + this.node_types_by_file_extension[ ext.toLowerCase() ] = base_class; + } + } + } + + this.registered_node_types[type] = base_class; + if (base_class.constructor.name) { + this.Nodes[classname] = base_class; + } + if (LiteGraph.onNodeTypeRegistered) { + LiteGraph.onNodeTypeRegistered(type, base_class); + } + if (prev && LiteGraph.onNodeTypeReplaced) { + LiteGraph.onNodeTypeReplaced(type, base_class, prev); + } + }, + + /** + * removes a node type from the system + * @method unregisterNodeType + * @param {String|Object} type name of the node or the node constructor itself + */ + unregisterNodeType: function(type) { + var base_class = type.constructor === String ? this.registered_node_types[type] : type; + if(!base_class) + throw("node type not found: " + type ); + delete this.registered_node_types[base_class.type]; + if(base_class.constructor.name) + delete this.Nodes[base_class.constructor.name]; + }, + + /** + * Create a new nodetype by passing a function, it wraps it with a proper class and generates inputs according to the parameters of the function. + * Useful to wrap simple methods that do not require properties, and that only process some input to generate an output. + * @method wrapFunctionAsNode + * @param {String} name node name with namespace (p.e.: 'math/sum') + * @param {Function} func + * @param {Array} param_types [optional] an array containing the type of every parameter, otherwise parameters will accept any type + * @param {String} return_type [optional] string with the return type, otherwise it will be generic + * @param {Object} properties [optional] properties to be configurable + */ + wrapFunctionAsNode: function( + name, + func, + param_types, + return_type, + properties + ) { + var params = Array(func.length); + var code = ""; + var names = LiteGraph.getParameterNames(func); + for (var i = 0; i < names.length; ++i) { + code += + "this.addInput('" + + names[i] + + "'," + + (param_types && param_types[i] + ? "'" + param_types[i] + "'" + : "0") + + ");\n"; + } + code += + "this.addOutput('out'," + + (return_type ? "'" + return_type + "'" : 0) + + ");\n"; + if (properties) { + code += + "this.properties = " + JSON.stringify(properties) + ";\n"; + } + var classobj = Function(code); + classobj.title = name.split("/").pop(); + classobj.desc = "Generated from " + func.name; + classobj.prototype.onExecute = function onExecute() { + for (var i = 0; i < params.length; ++i) { + params[i] = this.getInputData(i); + } + var r = func.apply(this, params); + this.setOutputData(0, r); + }; + this.registerNodeType(name, classobj); + }, + + /** + * Adds this method to all nodetypes, existing and to be created + * (You can add it to LGraphNode.prototype but then existing node types wont have it) + * @method addNodeMethod + * @param {Function} func + */ + addNodeMethod: function(name, func) { + LGraphNode.prototype[name] = func; + for (var i in this.registered_node_types) { + var type = this.registered_node_types[i]; + if (type.prototype[name]) { + type.prototype["_" + name] = type.prototype[name]; + } //keep old in case of replacing + type.prototype[name] = func; + } + }, + + /** + * Create a node of a given type with a name. The node is not attached to any graph yet. + * @method createNode + * @param {String} type full name of the node class. p.e. "math/sin" + * @param {String} name a name to distinguish from other nodes + * @param {Object} options to set options + */ + + createNode: function(type, title, options) { + var base_class = this.registered_node_types[type]; + if (!base_class) { + if (LiteGraph.debug) { + console.log( + 'GraphNode type "' + type + '" not registered.' + ); + } + return null; + } + + var prototype = base_class.prototype || base_class; + + title = title || base_class.title || type; + + var node = null; + + if (LiteGraph.catch_exceptions) { + try { + node = new base_class(title); + } catch (err) { + console.error(err); + return null; + } + } else { + node = new base_class(title); + } + + node.type = type; + + if (!node.title && title) { + node.title = title; + } + if (!node.properties) { + node.properties = {}; + } + if (!node.properties_info) { + node.properties_info = []; + } + if (!node.flags) { + node.flags = {}; + } + if (!node.size) { + node.size = node.computeSize(); + } + if (!node.pos) { + node.pos = LiteGraph.DEFAULT_POSITION.concat(); + } + if (!node.mode) { + node.mode = LiteGraph.ALWAYS; + } + + //extra options + if (options) { + for (var i in options) { + node[i] = options[i]; + } + } + + return node; + }, + + /** + * Returns a registered node type with a given name + * @method getNodeType + * @param {String} type full name of the node class. p.e. "math/sin" + * @return {Class} the node class + */ + getNodeType: function(type) { + return this.registered_node_types[type]; + }, + + /** + * Returns a list of node types matching one category + * @method getNodeType + * @param {String} category category name + * @return {Array} array with all the node classes + */ + + getNodeTypesInCategory: function(category, filter) { + var r = []; + for (var i in this.registered_node_types) { + var type = this.registered_node_types[i]; + if (filter && type.filter && type.filter != filter) { + continue; + } + + if (category == "") { + if (type.category == null) { + r.push(type); + } + } else if (type.category == category) { + r.push(type); + } + } + + return r; + }, + + /** + * Returns a list with all the node type categories + * @method getNodeTypesCategories + * @return {Array} array with all the names of the categories + */ + getNodeTypesCategories: function( filter ) { + var categories = { "": 1 }; + for (var i in this.registered_node_types) { + var type = this.registered_node_types[i]; + if ( type.category && !type.skip_list ) + { + if(filter && type.filter != filter) + continue; + categories[type.category] = 1; + } + } + var result = []; + for (var i in categories) { + result.push(i); + } + return result; + }, + + //debug purposes: reloads all the js scripts that matches a wildcard + reloadNodes: function(folder_wildcard) { + var tmp = document.getElementsByTagName("script"); + //weird, this array changes by its own, so we use a copy + var script_files = []; + for (var i in tmp) { + script_files.push(tmp[i]); + } + + var docHeadObj = document.getElementsByTagName("head")[0]; + folder_wildcard = document.location.href + folder_wildcard; + + for (var i in script_files) { + var src = script_files[i].src; + if ( + !src || + src.substr(0, folder_wildcard.length) != folder_wildcard + ) { + continue; + } + + try { + if (LiteGraph.debug) { + console.log("Reloading: " + src); + } + var dynamicScript = document.createElement("script"); + dynamicScript.type = "text/javascript"; + dynamicScript.src = src; + docHeadObj.appendChild(dynamicScript); + docHeadObj.removeChild(script_files[i]); + } catch (err) { + if (LiteGraph.throw_errors) { + throw err; + } + if (LiteGraph.debug) { + console.log("Error while reloading " + src); + } + } + } + + if (LiteGraph.debug) { + console.log("Nodes reloaded"); + } + }, + + //separated just to improve if it doesn't work + cloneObject: function(obj, target) { + if (obj == null) { + return null; + } + var r = JSON.parse(JSON.stringify(obj)); + if (!target) { + return r; + } + + for (var i in r) { + target[i] = r[i]; + } + return target; + }, + + /** + * Returns if the types of two slots are compatible (taking into account wildcards, etc) + * @method isValidConnection + * @param {String} type_a + * @param {String} type_b + * @return {Boolean} true if they can be connected + */ + isValidConnection: function(type_a, type_b) { + if ( + !type_a || //generic output + !type_b || //generic input + type_a == type_b || //same type (is valid for triggers) + (type_a == LiteGraph.EVENT && type_b == LiteGraph.ACTION) + ) { + return true; + } + + // Enforce string type to handle toLowerCase call (-1 number not ok) + type_a = String(type_a); + type_b = String(type_b); + type_a = type_a.toLowerCase(); + type_b = type_b.toLowerCase(); + + // For nodes supporting multiple connection types + if (type_a.indexOf(",") == -1 && type_b.indexOf(",") == -1) { + return type_a == type_b; + } + + // Check all permutations to see if one is valid + var supported_types_a = type_a.split(","); + var supported_types_b = type_b.split(","); + for (var i = 0; i < supported_types_a.length; ++i) { + for (var j = 0; j < supported_types_b.length; ++j) { + if (supported_types_a[i] == supported_types_b[j]) { + return true; + } + } + } + + return false; + }, + + /** + * Register a string in the search box so when the user types it it will recommend this node + * @method registerSearchboxExtra + * @param {String} node_type the node recommended + * @param {String} description text to show next to it + * @param {Object} data it could contain info of how the node should be configured + * @return {Boolean} true if they can be connected + */ + registerSearchboxExtra: function(node_type, description, data) { + this.searchbox_extras[description.toLowerCase()] = { + type: node_type, + desc: description, + data: data + }; + }, + + /** + * Wrapper to load files (from url using fetch or from file using FileReader) + * @method fetchFile + * @param {String|File|Blob} url the url of the file (or the file itself) + * @param {String} type an string to know how to fetch it: "text","arraybuffer","json","blob" + * @param {Function} on_complete callback(data) + * @param {Function} on_error in case of an error + * @return {FileReader|Promise} returns the object used to + */ + fetchFile: function( url, type, on_complete, on_error ) { + var that = this; + if(!url) + return null; + + type = type || "text"; + if( url.constructor === String ) + { + if (url.substr(0, 4) == "http" && LiteGraph.proxy) { + url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); + } + return fetch(url) + .then(function(response) { + if(!response.ok) + throw new Error("File not found"); //it will be catch below + if(type == "arraybuffer") + return response.arrayBuffer(); + else if(type == "text" || type == "string") + return response.text(); + else if(type == "json") + return response.json(); + else if(type == "blob") + return response.blob(); + }) + .then(function(data) { + if(on_complete) + on_complete(data); + }) + .catch(function(error) { + console.error("error fetching file:",url); + if(on_error) + on_error(error); + }); + } + else if( url.constructor === File || url.constructor === Blob) + { + var reader = new FileReader(); + reader.onload = function(e) + { + var v = e.target.result; + if( type == "json" ) + v = JSON.parse(v); + if(on_complete) + on_complete(v); + } + if(type == "arraybuffer") + return reader.readAsArrayBuffer(url); + else if(type == "text" || type == "json") + return reader.readAsText(url); + else if(type == "blob") + return reader.readAsBinaryString(url); + } + return null; + } + }); + + //timer that works everywhere + if (typeof performance != "undefined") { + LiteGraph.getTime = performance.now.bind(performance); + } else if (typeof Date != "undefined" && Date.now) { + LiteGraph.getTime = Date.now.bind(Date); + } else if (typeof process != "undefined") { + LiteGraph.getTime = function() { + var t = process.hrtime(); + return t[0] * 0.001 + t[1] * 1e-6; + }; + } else { + LiteGraph.getTime = function getTime() { + return new Date().getTime(); + }; + } + + //********************************************************************************* + // LGraph CLASS + //********************************************************************************* + + /** + * LGraph is the class that contain a full graph. We instantiate one and add nodes to it, and then we can run the execution loop. + * + * @class LGraph + * @constructor + * @param {Object} o data from previous serialization [optional] + */ + + function LGraph(o) { + if (LiteGraph.debug) { + console.log("Graph created"); + } + this.list_of_graphcanvas = null; + this.clear(); + + if (o) { + this.configure(o); + } + } + + global.LGraph = LiteGraph.LGraph = LGraph; + + //default supported types + LGraph.supported_types = ["number", "string", "boolean"]; + + //used to know which types of connections support this graph (some graphs do not allow certain types) + LGraph.prototype.getSupportedTypes = function() { + return this.supported_types || LGraph.supported_types; + }; + + LGraph.STATUS_STOPPED = 1; + LGraph.STATUS_RUNNING = 2; + + /** + * Removes all nodes from this graph + * @method clear + */ + + LGraph.prototype.clear = function() { + this.stop(); + this.status = LGraph.STATUS_STOPPED; + + this.last_node_id = 0; + this.last_link_id = 0; + + this._version = -1; //used to detect changes + + //safe clear + if (this._nodes) { + for (var i = 0; i < this._nodes.length; ++i) { + var node = this._nodes[i]; + if (node.onRemoved) { + node.onRemoved(); + } + } + } + + //nodes + this._nodes = []; + this._nodes_by_id = {}; + this._nodes_in_order = []; //nodes that are executable sorted in execution order + this._nodes_executable = null; //nodes that contain onExecute + + //other scene stuff + this._groups = []; + + //links + this.links = {}; //container with all the links + + //iterations + this.iteration = 0; + + //custom data + this.config = {}; + this.vars = {}; + + //timing + this.globaltime = 0; + this.runningtime = 0; + this.fixedtime = 0; + this.fixedtime_lapse = 0.01; + this.elapsed_time = 0.01; + this.last_update_time = 0; + this.starttime = 0; + + this.catch_errors = true; + + //subgraph_data + this.inputs = {}; + this.outputs = {}; + + //notify canvas to redraw + this.change(); + + this.sendActionToCanvas("clear"); + }; + + /** + * Attach Canvas to this graph + * @method attachCanvas + * @param {GraphCanvas} graph_canvas + */ + + LGraph.prototype.attachCanvas = function(graphcanvas) { + if (graphcanvas.constructor != LGraphCanvas) { + throw "attachCanvas expects a LGraphCanvas instance"; + } + if (graphcanvas.graph && graphcanvas.graph != this) { + graphcanvas.graph.detachCanvas(graphcanvas); + } + + graphcanvas.graph = this; + if (!this.list_of_graphcanvas) { + this.list_of_graphcanvas = []; + } + this.list_of_graphcanvas.push(graphcanvas); + }; + + /** + * Detach Canvas from this graph + * @method detachCanvas + * @param {GraphCanvas} graph_canvas + */ + LGraph.prototype.detachCanvas = function(graphcanvas) { + if (!this.list_of_graphcanvas) { + return; + } + + var pos = this.list_of_graphcanvas.indexOf(graphcanvas); + if (pos == -1) { + return; + } + graphcanvas.graph = null; + this.list_of_graphcanvas.splice(pos, 1); + }; + + /** + * Starts running this graph every interval milliseconds. + * @method start + * @param {number} interval amount of milliseconds between executions, if 0 then it renders to the monitor refresh rate + */ + + LGraph.prototype.start = function(interval) { + if (this.status == LGraph.STATUS_RUNNING) { + return; + } + this.status = LGraph.STATUS_RUNNING; + + if (this.onPlayEvent) { + this.onPlayEvent(); + } + + this.sendEventToAllNodes("onStart"); + + //launch + this.starttime = LiteGraph.getTime(); + this.last_update_time = this.starttime; + interval = interval || 0; + var that = this; + + //execute once per frame + if ( interval == 0 && typeof window != "undefined" && window.requestAnimationFrame ) { + function on_frame() { + if (that.execution_timer_id != -1) { + return; + } + window.requestAnimationFrame(on_frame); + if(that.onBeforeStep) + that.onBeforeStep(); + that.runStep(1, !this.catch_errors); + if(that.onAfterStep) + that.onAfterStep(); + } + this.execution_timer_id = -1; + on_frame(); + } else { //execute every 'interval' ms + this.execution_timer_id = setInterval(function() { + //execute + if(that.onBeforeStep) + that.onBeforeStep(); + that.runStep(1, !this.catch_errors); + if(that.onAfterStep) + that.onAfterStep(); + }, interval); + } + }; + + /** + * Stops the execution loop of the graph + * @method stop execution + */ + + LGraph.prototype.stop = function() { + if (this.status == LGraph.STATUS_STOPPED) { + return; + } + + this.status = LGraph.STATUS_STOPPED; + + if (this.onStopEvent) { + this.onStopEvent(); + } + + if (this.execution_timer_id != null) { + if (this.execution_timer_id != -1) { + clearInterval(this.execution_timer_id); + } + this.execution_timer_id = null; + } + + this.sendEventToAllNodes("onStop"); + }; + + /** + * Run N steps (cycles) of the graph + * @method runStep + * @param {number} num number of steps to run, default is 1 + * @param {Boolean} do_not_catch_errors [optional] if you want to try/catch errors + * @param {number} limit max number of nodes to execute (used to execute from start to a node) + */ + + LGraph.prototype.runStep = function(num, do_not_catch_errors, limit ) { + num = num || 1; + + var start = LiteGraph.getTime(); + this.globaltime = 0.001 * (start - this.starttime); + + var nodes = this._nodes_executable + ? this._nodes_executable + : this._nodes; + if (!nodes) { + return; + } + + limit = limit || nodes.length; + + if (do_not_catch_errors) { + //iterations + for (var i = 0; i < num; i++) { + for (var j = 0; j < limit; ++j) { + var node = nodes[j]; + if (node.mode == LiteGraph.ALWAYS && node.onExecute) { + node.onExecute(); //hard to send elapsed time + } + } + + this.fixedtime += this.fixedtime_lapse; + if (this.onExecuteStep) { + this.onExecuteStep(); + } + } + + if (this.onAfterExecute) { + this.onAfterExecute(); + } + } else { + try { + //iterations + for (var i = 0; i < num; i++) { + for (var j = 0; j < limit; ++j) { + var node = nodes[j]; + if (node.mode == LiteGraph.ALWAYS && node.onExecute) { + node.onExecute(); + } + } + + this.fixedtime += this.fixedtime_lapse; + if (this.onExecuteStep) { + this.onExecuteStep(); + } + } + + if (this.onAfterExecute) { + this.onAfterExecute(); + } + this.errors_in_execution = false; + } catch (err) { + this.errors_in_execution = true; + if (LiteGraph.throw_errors) { + throw err; + } + if (LiteGraph.debug) { + console.log("Error during execution: " + err); + } + this.stop(); + } + } + + var now = LiteGraph.getTime(); + var elapsed = now - start; + if (elapsed == 0) { + elapsed = 1; + } + this.execution_time = 0.001 * elapsed; + this.globaltime += 0.001 * elapsed; + this.iteration += 1; + this.elapsed_time = (now - this.last_update_time) * 0.001; + this.last_update_time = now; + }; + + /** + * Updates the graph execution order according to relevance of the nodes (nodes with only outputs have more relevance than + * nodes with only inputs. + * @method updateExecutionOrder + */ + LGraph.prototype.updateExecutionOrder = function() { + this._nodes_in_order = this.computeExecutionOrder(false); + this._nodes_executable = []; + for (var i = 0; i < this._nodes_in_order.length; ++i) { + if (this._nodes_in_order[i].onExecute) { + this._nodes_executable.push(this._nodes_in_order[i]); + } + } + }; + + //This is more internal, it computes the executable nodes in order and returns it + LGraph.prototype.computeExecutionOrder = function( + only_onExecute, + set_level + ) { + var L = []; + var S = []; + var M = {}; + var visited_links = {}; //to avoid repeating links + var remaining_links = {}; //to a + + //search for the nodes without inputs (starting nodes) + for (var i = 0, l = this._nodes.length; i < l; ++i) { + var node = this._nodes[i]; + if (only_onExecute && !node.onExecute) { + continue; + } + + M[node.id] = node; //add to pending nodes + + var num = 0; //num of input connections + if (node.inputs) { + for (var j = 0, l2 = node.inputs.length; j < l2; j++) { + if (node.inputs[j] && node.inputs[j].link != null) { + num += 1; + } + } + } + + if (num == 0) { + //is a starting node + S.push(node); + if (set_level) { + node._level = 1; + } + } //num of input links + else { + if (set_level) { + node._level = 0; + } + remaining_links[node.id] = num; + } + } + + while (true) { + if (S.length == 0) { + break; + } + + //get an starting node + var node = S.shift(); + L.push(node); //add to ordered list + delete M[node.id]; //remove from the pending nodes + + if (!node.outputs) { + continue; + } + + //for every output + for (var i = 0; i < node.outputs.length; i++) { + var output = node.outputs[i]; + //not connected + if ( + output == null || + output.links == null || + output.links.length == 0 + ) { + continue; + } + + //for every connection + for (var j = 0; j < output.links.length; j++) { + var link_id = output.links[j]; + var link = this.links[link_id]; + if (!link) { + continue; + } + + //already visited link (ignore it) + if (visited_links[link.id]) { + continue; + } + + var target_node = this.getNodeById(link.target_id); + if (target_node == null) { + visited_links[link.id] = true; + continue; + } + + if ( + set_level && + (!target_node._level || + target_node._level <= node._level) + ) { + target_node._level = node._level + 1; + } + + visited_links[link.id] = true; //mark as visited + remaining_links[target_node.id] -= 1; //reduce the number of links remaining + if (remaining_links[target_node.id] == 0) { + S.push(target_node); + } //if no more links, then add to starters array + } + } + } + + //the remaining ones (loops) + for (var i in M) { + L.push(M[i]); + } + + if (L.length != this._nodes.length && LiteGraph.debug) { + console.warn("something went wrong, nodes missing"); + } + + var l = L.length; + + //save order number in the node + for (var i = 0; i < l; ++i) { + L[i].order = i; + } + + //sort now by priority + L = L.sort(function(A, B) { + var Ap = A.constructor.priority || A.priority || 0; + var Bp = B.constructor.priority || B.priority || 0; + if (Ap == Bp) { + //if same priority, sort by order + return A.order - B.order; + } + return Ap - Bp; //sort by priority + }); + + //save order number in the node, again... + for (var i = 0; i < l; ++i) { + L[i].order = i; + } + + return L; + }; + + /** + * Returns all the nodes that could affect this one (ancestors) by crawling all the inputs recursively. + * It doesn't include the node itself + * @method getAncestors + * @return {Array} an array with all the LGraphNodes that affect this node, in order of execution + */ + LGraph.prototype.getAncestors = function(node) { + var ancestors = []; + var pending = [node]; + var visited = {}; + + while (pending.length) { + var current = pending.shift(); + if (!current.inputs) { + continue; + } + if (!visited[current.id] && current != node) { + visited[current.id] = true; + ancestors.push(current); + } + + for (var i = 0; i < current.inputs.length; ++i) { + var input = current.getInputNode(i); + if (input && ancestors.indexOf(input) == -1) { + pending.push(input); + } + } + } + + ancestors.sort(function(a, b) { + return a.order - b.order; + }); + return ancestors; + }; + + /** + * Positions every node in a more readable manner + * @method arrange + */ + LGraph.prototype.arrange = function(margin) { + margin = margin || 100; + + var nodes = this.computeExecutionOrder(false, true); + var columns = []; + for (var i = 0; i < nodes.length; ++i) { + var node = nodes[i]; + var col = node._level || 1; + if (!columns[col]) { + columns[col] = []; + } + columns[col].push(node); + } + + var x = margin; + + for (var i = 0; i < columns.length; ++i) { + var column = columns[i]; + if (!column) { + continue; + } + var max_size = 100; + var y = margin + LiteGraph.NODE_TITLE_HEIGHT; + for (var j = 0; j < column.length; ++j) { + var node = column[j]; + node.pos[0] = x; + node.pos[1] = y; + if (node.size[0] > max_size) { + max_size = node.size[0]; + } + y += node.size[1] + margin + LiteGraph.NODE_TITLE_HEIGHT; + } + x += max_size + margin; + } + + this.setDirtyCanvas(true, true); + }; + + /** + * Returns the amount of time the graph has been running in milliseconds + * @method getTime + * @return {number} number of milliseconds the graph has been running + */ + LGraph.prototype.getTime = function() { + return this.globaltime; + }; + + /** + * Returns the amount of time accumulated using the fixedtime_lapse var. This is used in context where the time increments should be constant + * @method getFixedTime + * @return {number} number of milliseconds the graph has been running + */ + + LGraph.prototype.getFixedTime = function() { + return this.fixedtime; + }; + + /** + * Returns the amount of time it took to compute the latest iteration. Take into account that this number could be not correct + * if the nodes are using graphical actions + * @method getElapsedTime + * @return {number} number of milliseconds it took the last cycle + */ + + LGraph.prototype.getElapsedTime = function() { + return this.elapsed_time; + }; + + /** + * Sends an event to all the nodes, useful to trigger stuff + * @method sendEventToAllNodes + * @param {String} eventname the name of the event (function to be called) + * @param {Array} params parameters in array format + */ + LGraph.prototype.sendEventToAllNodes = function(eventname, params, mode) { + mode = mode || LiteGraph.ALWAYS; + + var nodes = this._nodes_in_order ? this._nodes_in_order : this._nodes; + if (!nodes) { + return; + } + + for (var j = 0, l = nodes.length; j < l; ++j) { + var node = nodes[j]; + + if ( + node.constructor === LiteGraph.Subgraph && + eventname != "onExecute" + ) { + if (node.mode == mode) { + node.sendEventToAllNodes(eventname, params, mode); + } + continue; + } + + if (!node[eventname] || node.mode != mode) { + continue; + } + if (params === undefined) { + node[eventname](); + } else if (params && params.constructor === Array) { + node[eventname].apply(node, params); + } else { + node[eventname](params); + } + } + }; + + LGraph.prototype.sendActionToCanvas = function(action, params) { + if (!this.list_of_graphcanvas) { + return; + } + + for (var i = 0; i < this.list_of_graphcanvas.length; ++i) { + var c = this.list_of_graphcanvas[i]; + if (c[action]) { + c[action].apply(c, params); + } + } + }; + + /** + * Adds a new node instance to this graph + * @method add + * @param {LGraphNode} node the instance of the node + */ + + LGraph.prototype.add = function(node, skip_compute_order) { + if (!node) { + return; + } + + //groups + if (node.constructor === LGraphGroup) { + this._groups.push(node); + this.setDirtyCanvas(true); + this.change(); + node.graph = this; + this._version++; + return; + } + + //nodes + if (node.id != -1 && this._nodes_by_id[node.id] != null) { + console.warn( + "LiteGraph: there is already a node with this ID, changing it" + ); + node.id = ++this.last_node_id; + } + + if (this._nodes.length >= LiteGraph.MAX_NUMBER_OF_NODES) { + throw "LiteGraph: max number of nodes in a graph reached"; + } + + //give him an id + if (node.id == null || node.id == -1) { + node.id = ++this.last_node_id; + } else if (this.last_node_id < node.id) { + this.last_node_id = node.id; + } + + node.graph = this; + this._version++; + + this._nodes.push(node); + this._nodes_by_id[node.id] = node; + + if (node.onAdded) { + node.onAdded(this); + } + + if (this.config.align_to_grid) { + node.alignToGrid(); + } + + if (!skip_compute_order) { + this.updateExecutionOrder(); + } + + if (this.onNodeAdded) { + this.onNodeAdded(node); + } + + this.setDirtyCanvas(true); + this.change(); + + return node; //to chain actions + }; + + /** + * Removes a node from the graph + * @method remove + * @param {LGraphNode} node the instance of the node + */ + + LGraph.prototype.remove = function(node) { + if (node.constructor === LiteGraph.LGraphGroup) { + var index = this._groups.indexOf(node); + if (index != -1) { + this._groups.splice(index, 1); + } + node.graph = null; + this._version++; + this.setDirtyCanvas(true, true); + this.change(); + return; + } + + if (this._nodes_by_id[node.id] == null) { + return; + } //not found + + if (node.ignore_remove) { + return; + } //cannot be removed + + //disconnect inputs + if (node.inputs) { + for (var i = 0; i < node.inputs.length; i++) { + var slot = node.inputs[i]; + if (slot.link != null) { + node.disconnectInput(i); + } + } + } + + //disconnect outputs + if (node.outputs) { + for (var i = 0; i < node.outputs.length; i++) { + var slot = node.outputs[i]; + if (slot.links != null && slot.links.length) { + node.disconnectOutput(i); + } + } + } + + //node.id = -1; //why? + + //callback + if (node.onRemoved) { + node.onRemoved(); + } + + node.graph = null; + this._version++; + + //remove from canvas render + if (this.list_of_graphcanvas) { + for (var i = 0; i < this.list_of_graphcanvas.length; ++i) { + var canvas = this.list_of_graphcanvas[i]; + if (canvas.selected_nodes[node.id]) { + delete canvas.selected_nodes[node.id]; + } + if (canvas.node_dragged == node) { + canvas.node_dragged = null; + } + } + } + + //remove from containers + var pos = this._nodes.indexOf(node); + if (pos != -1) { + this._nodes.splice(pos, 1); + } + delete this._nodes_by_id[node.id]; + + if (this.onNodeRemoved) { + this.onNodeRemoved(node); + } + + this.setDirtyCanvas(true, true); + this.change(); + + this.updateExecutionOrder(); + }; + + /** + * Returns a node by its id. + * @method getNodeById + * @param {Number} id + */ + + LGraph.prototype.getNodeById = function(id) { + if (id == null) { + return null; + } + return this._nodes_by_id[id]; + }; + + /** + * Returns a list of nodes that matches a class + * @method findNodesByClass + * @param {Class} classObject the class itself (not an string) + * @return {Array} a list with all the nodes of this type + */ + LGraph.prototype.findNodesByClass = function(classObject, result) { + result = result || []; + result.length = 0; + for (var i = 0, l = this._nodes.length; i < l; ++i) { + if (this._nodes[i].constructor === classObject) { + result.push(this._nodes[i]); + } + } + return result; + }; + + /** + * Returns a list of nodes that matches a type + * @method findNodesByType + * @param {String} type the name of the node type + * @return {Array} a list with all the nodes of this type + */ + LGraph.prototype.findNodesByType = function(type, result) { + var type = type.toLowerCase(); + result = result || []; + result.length = 0; + for (var i = 0, l = this._nodes.length; i < l; ++i) { + if (this._nodes[i].type.toLowerCase() == type) { + result.push(this._nodes[i]); + } + } + return result; + }; + + /** + * Returns the first node that matches a name in its title + * @method findNodeByTitle + * @param {String} name the name of the node to search + * @return {Node} the node or null + */ + LGraph.prototype.findNodeByTitle = function(title) { + for (var i = 0, l = this._nodes.length; i < l; ++i) { + if (this._nodes[i].title == title) { + return this._nodes[i]; + } + } + return null; + }; + + /** + * Returns a list of nodes that matches a name + * @method findNodesByTitle + * @param {String} name the name of the node to search + * @return {Array} a list with all the nodes with this name + */ + LGraph.prototype.findNodesByTitle = function(title) { + var result = []; + for (var i = 0, l = this._nodes.length; i < l; ++i) { + if (this._nodes[i].title == title) { + result.push(this._nodes[i]); + } + } + return result; + }; + + /** + * Returns the top-most node in this position of the canvas + * @method getNodeOnPos + * @param {number} x the x coordinate in canvas space + * @param {number} y the y coordinate in canvas space + * @param {Array} nodes_list a list with all the nodes to search from, by default is all the nodes in the graph + * @return {LGraphNode} the node at this position or null + */ + LGraph.prototype.getNodeOnPos = function(x, y, nodes_list, margin) { + nodes_list = nodes_list || this._nodes; + for (var i = nodes_list.length - 1; i >= 0; i--) { + var n = nodes_list[i]; + if (n.isPointInside(x, y, margin)) { + return n; + } + } + return null; + }; + + /** + * Returns the top-most group in that position + * @method getGroupOnPos + * @param {number} x the x coordinate in canvas space + * @param {number} y the y coordinate in canvas space + * @return {LGraphGroup} the group or null + */ + LGraph.prototype.getGroupOnPos = function(x, y) { + for (var i = this._groups.length - 1; i >= 0; i--) { + var g = this._groups[i]; + if (g.isPointInside(x, y, 2, true)) { + return g; + } + } + return null; + }; + + /** + * Checks that the node type matches the node type registered, used when replacing a nodetype by a newer version during execution + * this replaces the ones using the old version with the new version + * @method checkNodeTypes + */ + LGraph.prototype.checkNodeTypes = function() { + var changes = false; + for (var i = 0; i < this._nodes.length; i++) { + var node = this._nodes[i]; + var ctor = LiteGraph.registered_node_types[node.type]; + if (node.constructor == ctor) { + continue; + } + console.log("node being replaced by newer version: " + node.type); + var newnode = LiteGraph.createNode(node.type); + changes = true; + this._nodes[i] = newnode; + newnode.configure(node.serialize()); + newnode.graph = this; + this._nodes_by_id[newnode.id] = newnode; + if (node.inputs) { + newnode.inputs = node.inputs.concat(); + } + if (node.outputs) { + newnode.outputs = node.outputs.concat(); + } + } + this.updateExecutionOrder(); + }; + + // ********** GLOBALS ***************** + + LGraph.prototype.onAction = function(action, param) { + this._input_nodes = this.findNodesByClass( + LiteGraph.GraphInput, + this._input_nodes + ); + for (var i = 0; i < this._input_nodes.length; ++i) { + var node = this._input_nodes[i]; + if (node.properties.name != action) { + continue; + } + node.onAction(action, param); + break; + } + }; + + LGraph.prototype.trigger = function(action, param) { + if (this.onTrigger) { + this.onTrigger(action, param); + } + }; + + /** + * Tell this graph it has a global graph input of this type + * @method addGlobalInput + * @param {String} name + * @param {String} type + * @param {*} value [optional] + */ + LGraph.prototype.addInput = function(name, type, value) { + var input = this.inputs[name]; + if (input) { + //already exist + return; + } + + this.inputs[name] = { name: name, type: type, value: value }; + this._version++; + + if (this.onInputAdded) { + this.onInputAdded(name, type); + } + + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + }; + + /** + * Assign a data to the global graph input + * @method setGlobalInputData + * @param {String} name + * @param {*} data + */ + LGraph.prototype.setInputData = function(name, data) { + var input = this.inputs[name]; + if (!input) { + return; + } + input.value = data; + }; + + /** + * Returns the current value of a global graph input + * @method getInputData + * @param {String} name + * @return {*} the data + */ + LGraph.prototype.getInputData = function(name) { + var input = this.inputs[name]; + if (!input) { + return null; + } + return input.value; + }; + + /** + * Changes the name of a global graph input + * @method renameInput + * @param {String} old_name + * @param {String} new_name + */ + LGraph.prototype.renameInput = function(old_name, name) { + if (name == old_name) { + return; + } + + if (!this.inputs[old_name]) { + return false; + } + + if (this.inputs[name]) { + console.error("there is already one input with that name"); + return false; + } + + this.inputs[name] = this.inputs[old_name]; + delete this.inputs[old_name]; + this._version++; + + if (this.onInputRenamed) { + this.onInputRenamed(old_name, name); + } + + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + }; + + /** + * Changes the type of a global graph input + * @method changeInputType + * @param {String} name + * @param {String} type + */ + LGraph.prototype.changeInputType = function(name, type) { + if (!this.inputs[name]) { + return false; + } + + if ( + this.inputs[name].type && + String(this.inputs[name].type).toLowerCase() == + String(type).toLowerCase() + ) { + return; + } + + this.inputs[name].type = type; + this._version++; + if (this.onInputTypeChanged) { + this.onInputTypeChanged(name, type); + } + }; + + /** + * Removes a global graph input + * @method removeInput + * @param {String} name + * @param {String} type + */ + LGraph.prototype.removeInput = function(name) { + if (!this.inputs[name]) { + return false; + } + + delete this.inputs[name]; + this._version++; + + if (this.onInputRemoved) { + this.onInputRemoved(name); + } + + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + return true; + }; + + /** + * Creates a global graph output + * @method addOutput + * @param {String} name + * @param {String} type + * @param {*} value + */ + LGraph.prototype.addOutput = function(name, type, value) { + this.outputs[name] = { name: name, type: type, value: value }; + this._version++; + + if (this.onOutputAdded) { + this.onOutputAdded(name, type); + } + + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + }; + + /** + * Assign a data to the global output + * @method setOutputData + * @param {String} name + * @param {String} value + */ + LGraph.prototype.setOutputData = function(name, value) { + var output = this.outputs[name]; + if (!output) { + return; + } + output.value = value; + }; + + /** + * Returns the current value of a global graph output + * @method getOutputData + * @param {String} name + * @return {*} the data + */ + LGraph.prototype.getOutputData = function(name) { + var output = this.outputs[name]; + if (!output) { + return null; + } + return output.value; + }; + + /** + * Renames a global graph output + * @method renameOutput + * @param {String} old_name + * @param {String} new_name + */ + LGraph.prototype.renameOutput = function(old_name, name) { + if (!this.outputs[old_name]) { + return false; + } + + if (this.outputs[name]) { + console.error("there is already one output with that name"); + return false; + } + + this.outputs[name] = this.outputs[old_name]; + delete this.outputs[old_name]; + this._version++; + + if (this.onOutputRenamed) { + this.onOutputRenamed(old_name, name); + } + + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + }; + + /** + * Changes the type of a global graph output + * @method changeOutputType + * @param {String} name + * @param {String} type + */ + LGraph.prototype.changeOutputType = function(name, type) { + if (!this.outputs[name]) { + return false; + } + + if ( + this.outputs[name].type && + String(this.outputs[name].type).toLowerCase() == + String(type).toLowerCase() + ) { + return; + } + + this.outputs[name].type = type; + this._version++; + if (this.onOutputTypeChanged) { + this.onOutputTypeChanged(name, type); + } + }; + + /** + * Removes a global graph output + * @method removeOutput + * @param {String} name + */ + LGraph.prototype.removeOutput = function(name) { + if (!this.outputs[name]) { + return false; + } + delete this.outputs[name]; + this._version++; + + if (this.onOutputRemoved) { + this.onOutputRemoved(name); + } + + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + return true; + }; + + LGraph.prototype.triggerInput = function(name, value) { + var nodes = this.findNodesByTitle(name); + for (var i = 0; i < nodes.length; ++i) { + nodes[i].onTrigger(value); + } + }; + + LGraph.prototype.setCallback = function(name, func) { + var nodes = this.findNodesByTitle(name); + for (var i = 0; i < nodes.length; ++i) { + nodes[i].setTrigger(func); + } + }; + + LGraph.prototype.connectionChange = function(node, link_info) { + this.updateExecutionOrder(); + if (this.onConnectionChange) { + this.onConnectionChange(node); + } + this._version++; + this.sendActionToCanvas("onConnectionChange"); + }; + + /** + * returns if the graph is in live mode + * @method isLive + */ + + LGraph.prototype.isLive = function() { + if (!this.list_of_graphcanvas) { + return false; + } + + for (var i = 0; i < this.list_of_graphcanvas.length; ++i) { + var c = this.list_of_graphcanvas[i]; + if (c.live_mode) { + return true; + } + } + return false; + }; + + /** + * clears the triggered slot animation in all links (stop visual animation) + * @method clearTriggeredSlots + */ + LGraph.prototype.clearTriggeredSlots = function() { + for (var i in this.links) { + var link_info = this.links[i]; + if (!link_info) { + continue; + } + if (link_info._last_time) { + link_info._last_time = 0; + } + } + }; + + /* Called when something visually changed (not the graph!) */ + LGraph.prototype.change = function() { + if (LiteGraph.debug) { + console.log("Graph changed"); + } + this.sendActionToCanvas("setDirty", [true, true]); + if (this.on_change) { + this.on_change(this); + } + }; + + LGraph.prototype.setDirtyCanvas = function(fg, bg) { + this.sendActionToCanvas("setDirty", [fg, bg]); + }; + + /** + * Destroys a link + * @method removeLink + * @param {Number} link_id + */ + LGraph.prototype.removeLink = function(link_id) { + var link = this.links[link_id]; + if (!link) { + return; + } + var node = this.getNodeById(link.target_id); + if (node) { + node.disconnectInput(link.target_slot); + } + }; + + //save and recover app state *************************************** + /** + * Creates a Object containing all the info about this graph, it can be serialized + * @method serialize + * @return {Object} value of the node + */ + LGraph.prototype.serialize = function() { + var nodes_info = []; + for (var i = 0, l = this._nodes.length; i < l; ++i) { + nodes_info.push(this._nodes[i].serialize()); + } + + //pack link info into a non-verbose format + var links = []; + for (var i in this.links) { + //links is an OBJECT + var link = this.links[i]; + if (!link.serialize) { + //weird bug I havent solved yet + console.warn( + "weird LLink bug, link info is not a LLink but a regular object" + ); + var link2 = new LLink(); + for (var i in link) { + link2[i] = link[i]; + } + this.links[i] = link2; + link = link2; + } + + links.push(link.serialize()); + } + + var groups_info = []; + for (var i = 0; i < this._groups.length; ++i) { + groups_info.push(this._groups[i].serialize()); + } + + var data = { + last_node_id: this.last_node_id, + last_link_id: this.last_link_id, + nodes: nodes_info, + links: links, + groups: groups_info, + config: this.config, + version: LiteGraph.VERSION + }; + + return data; + }; + + /** + * Configure a graph from a JSON string + * @method configure + * @param {String} str configure a graph from a JSON string + * @param {Boolean} returns if there was any error parsing + */ + LGraph.prototype.configure = function(data, keep_old) { + if (!data) { + return; + } + + if (!keep_old) { + this.clear(); + } + + var nodes = data.nodes; + + //decode links info (they are very verbose) + if (data.links && data.links.constructor === Array) { + var links = []; + for (var i = 0; i < data.links.length; ++i) { + var link_data = data.links[i]; + if(!link_data) //weird bug + { + console.warn("serialized graph link data contains errors, skipping."); + continue; + } + var link = new LLink(); + link.configure(link_data); + links[link.id] = link; + } + data.links = links; + } + + //copy all stored fields + for (var i in data) { + if(i == "nodes" || i == "groups" ) //links must be accepted + continue; + this[i] = data[i]; + } + + var error = false; + + //create nodes + this._nodes = []; + if (nodes) { + for (var i = 0, l = nodes.length; i < l; ++i) { + var n_info = nodes[i]; //stored info + var node = LiteGraph.createNode(n_info.type, n_info.title); + if (!node) { + if (LiteGraph.debug) { + console.log( + "Node not found or has errors: " + n_info.type + ); + } + + //in case of error we create a replacement node to avoid losing info + node = new LGraphNode(); + node.last_serialization = n_info; + node.has_errors = true; + error = true; + //continue; + } + + node.id = n_info.id; //id it or it will create a new id + this.add(node, true); //add before configure, otherwise configure cannot create links + } + + //configure nodes afterwards so they can reach each other + for (var i = 0, l = nodes.length; i < l; ++i) { + var n_info = nodes[i]; + var node = this.getNodeById(n_info.id); + if (node) { + node.configure(n_info); + } + } + } + + //groups + this._groups.length = 0; + if (data.groups) { + for (var i = 0; i < data.groups.length; ++i) { + var group = new LiteGraph.LGraphGroup(); + group.configure(data.groups[i]); + this.add(group); + } + } + + this.updateExecutionOrder(); + this._version++; + this.setDirtyCanvas(true, true); + return error; + }; + + LGraph.prototype.load = function(url) { + var that = this; + var req = new XMLHttpRequest(); + req.open("GET", url, true); + req.send(null); + req.onload = function(oEvent) { + if (req.status !== 200) { + console.error("Error loading graph:", req.status, req.response); + return; + } + var data = JSON.parse(req.response); + that.configure(data); + }; + req.onerror = function(err) { + console.error("Error loading graph:", err); + }; + }; + + LGraph.prototype.onNodeTrace = function(node, msg, color) { + //TODO + }; + + //this is the class in charge of storing link information + function LLink(id, type, origin_id, origin_slot, target_id, target_slot) { + this.id = id; + this.type = type; + this.origin_id = origin_id; + this.origin_slot = origin_slot; + this.target_id = target_id; + this.target_slot = target_slot; + + this._data = null; + this._pos = new Float32Array(2); //center + } + + LLink.prototype.configure = function(o) { + if (o.constructor === Array) { + this.id = o[0]; + this.origin_id = o[1]; + this.origin_slot = o[2]; + this.target_id = o[3]; + this.target_slot = o[4]; + this.type = o[5]; + } else { + this.id = o.id; + this.type = o.type; + this.origin_id = o.origin_id; + this.origin_slot = o.origin_slot; + this.target_id = o.target_id; + this.target_slot = o.target_slot; + } + }; + + LLink.prototype.serialize = function() { + return [ + this.id, + this.origin_id, + this.origin_slot, + this.target_id, + this.target_slot, + this.type + ]; + }; + + LiteGraph.LLink = LLink; + + // ************************************************************* + // Node CLASS ******* + // ************************************************************* + + /* + title: string + pos: [x,y] + size: [x,y] + + input|output: every connection + + { name:string, type:string, pos: [x,y]=Optional, direction: "input"|"output", links: Array }); + + general properties: + + clip_area: if you render outside the node, it will be clipped + + unsafe_execution: not allowed for safe execution + + skip_repeated_outputs: when adding new outputs, it wont show if there is one already connected + + resizable: if set to false it wont be resizable with the mouse + + horizontal: slots are distributed horizontally + + widgets_start_y: widgets start at y distance from the top of the node + + flags object: + + collapsed: if it is collapsed + + supported callbacks: + + onAdded: when added to graph (warning: this is called BEFORE the node is configured when loading) + + onRemoved: when removed from graph + + onStart: when the graph starts playing + + onStop: when the graph stops playing + + onDrawForeground: render the inside widgets inside the node + + onDrawBackground: render the background area inside the node (only in edit mode) + + onMouseDown + + onMouseMove + + onMouseUp + + onMouseEnter + + onMouseLeave + + onExecute: execute the node + + onPropertyChanged: when a property is changed in the panel (return true to skip default behaviour) + + onGetInputs: returns an array of possible inputs + + onGetOutputs: returns an array of possible outputs + + onBounding: in case this node has a bigger bounding than the node itself (the callback receives the bounding as [x,y,w,h]) + + onDblClick: double clicked in the node + + onInputDblClick: input slot double clicked (can be used to automatically create a node connected) + + onOutputDblClick: output slot double clicked (can be used to automatically create a node connected) + + onConfigure: called after the node has been configured + + onSerialize: to add extra info when serializing (the callback receives the object that should be filled with the data) + + onSelected + + onDeselected + + onDropItem : DOM item dropped over the node + + onDropFile : file dropped over the node + + onConnectInput : if returns false the incoming connection will be canceled + + onConnectionsChange : a connection changed (new one or removed) (LiteGraph.INPUT or LiteGraph.OUTPUT, slot, true if connected, link_info, input_info ) + + onAction: action slot triggered + + getExtraMenuOptions: to add option to context menu +*/ + + /** + * Base Class for all the node type classes + * @class LGraphNode + * @param {String} name a name for the node + */ + + function LGraphNode(title) { + this._ctor(title); + } + + global.LGraphNode = LiteGraph.LGraphNode = LGraphNode; + + LGraphNode.prototype._ctor = function(title) { + this.title = title || "Unnamed"; + this.size = [LiteGraph.NODE_WIDTH, 60]; + this.graph = null; + + this._pos = new Float32Array(10, 10); + + Object.defineProperty(this, "pos", { + set: function(v) { + if (!v || v.length < 2) { + return; + } + this._pos[0] = v[0]; + this._pos[1] = v[1]; + }, + get: function() { + return this._pos; + }, + enumerable: true + }); + + this.id = -1; //not know till not added + this.type = null; + + //inputs available: array of inputs + this.inputs = []; + this.outputs = []; + this.connections = []; + + //local data + this.properties = {}; //for the values + this.properties_info = []; //for the info + + this.flags = {}; + }; + + /** + * configure a node from an object containing the serialized info + * @method configure + */ + LGraphNode.prototype.configure = function(info) { + if (this.graph) { + this.graph._version++; + } + for (var j in info) { + if (j == "properties") { + //i don't want to clone properties, I want to reuse the old container + for (var k in info.properties) { + this.properties[k] = info.properties[k]; + if (this.onPropertyChanged) { + this.onPropertyChanged( k, info.properties[k] ); + } + } + continue; + } + + if (info[j] == null) { + continue; + } else if (typeof info[j] == "object") { + //object + if (this[j] && this[j].configure) { + this[j].configure(info[j]); + } else { + this[j] = LiteGraph.cloneObject(info[j], this[j]); + } + } //value + else { + this[j] = info[j]; + } + } + + if (!info.title) { + this.title = this.constructor.title; + } + + if (this.onConnectionsChange) { + if (this.inputs) { + for (var i = 0; i < this.inputs.length; ++i) { + var input = this.inputs[i]; + var link_info = this.graph + ? this.graph.links[input.link] + : null; + this.onConnectionsChange( + LiteGraph.INPUT, + i, + true, + link_info, + input + ); //link_info has been created now, so its updated + } + } + + if (this.outputs) { + for (var i = 0; i < this.outputs.length; ++i) { + var output = this.outputs[i]; + if (!output.links) { + continue; + } + for (var j = 0; j < output.links.length; ++j) { + var link_info = this.graph + ? this.graph.links[output.links[j]] + : null; + this.onConnectionsChange( + LiteGraph.OUTPUT, + i, + true, + link_info, + output + ); //link_info has been created now, so its updated + } + } + } + } + + if( this.widgets ) + { + for (var i = 0; i < this.widgets.length; ++i) + { + var w = this.widgets[i]; + if(!w) + continue; + if(w.options && w.options.property && this.properties[ w.options.property ]) + w.value = JSON.parse( JSON.stringify( this.properties[ w.options.property ] ) ); + } + if (info.widgets_values) { + for (var i = 0; i < info.widgets_values.length; ++i) { + if (this.widgets[i]) { + this.widgets[i].value = info.widgets_values[i]; + } + } + } + } + + if (this.onConfigure) { + this.onConfigure(info); + } + }; + + /** + * serialize the content + * @method serialize + */ + + LGraphNode.prototype.serialize = function() { + //create serialization object + var o = { + id: this.id, + type: this.type, + pos: this.pos, + size: this.size, + flags: LiteGraph.cloneObject(this.flags), + order: this.order, + mode: this.mode + }; + + //special case for when there were errors + if (this.constructor === LGraphNode && this.last_serialization) { + return this.last_serialization; + } + + if (this.inputs) { + o.inputs = this.inputs; + } + + if (this.outputs) { + //clear outputs last data (because data in connections is never serialized but stored inside the outputs info) + for (var i = 0; i < this.outputs.length; i++) { + delete this.outputs[i]._data; + } + o.outputs = this.outputs; + } + + if (this.title && this.title != this.constructor.title) { + o.title = this.title; + } + + if (this.properties) { + o.properties = LiteGraph.cloneObject(this.properties); + } + + if (this.widgets && this.serialize_widgets) { + o.widgets_values = []; + for (var i = 0; i < this.widgets.length; ++i) { + if(this.widgets[i]) + o.widgets_values[i] = this.widgets[i].value; + else + o.widgets_values[i] = null; + } + } + + if (!o.type) { + o.type = this.constructor.type; + } + + if (this.color) { + o.color = this.color; + } + if (this.bgcolor) { + o.bgcolor = this.bgcolor; + } + if (this.boxcolor) { + o.boxcolor = this.boxcolor; + } + if (this.shape) { + o.shape = this.shape; + } + + if (this.onSerialize) { + if (this.onSerialize(o)) { + console.warn( + "node onSerialize shouldnt return anything, data should be stored in the object pass in the first parameter" + ); + } + } + + return o; + }; + + /* Creates a clone of this node */ + LGraphNode.prototype.clone = function() { + var node = LiteGraph.createNode(this.type); + if (!node) { + return null; + } + + //we clone it because serialize returns shared containers + var data = LiteGraph.cloneObject(this.serialize()); + + //remove links + if (data.inputs) { + for (var i = 0; i < data.inputs.length; ++i) { + data.inputs[i].link = null; + } + } + + if (data.outputs) { + for (var i = 0; i < data.outputs.length; ++i) { + if (data.outputs[i].links) { + data.outputs[i].links.length = 0; + } + } + } + + delete data["id"]; + //remove links + node.configure(data); + + return node; + }; + + /** + * serialize and stringify + * @method toString + */ + + LGraphNode.prototype.toString = function() { + return JSON.stringify(this.serialize()); + }; + //LGraphNode.prototype.deserialize = function(info) {} //this cannot be done from within, must be done in LiteGraph + + /** + * get the title string + * @method getTitle + */ + + LGraphNode.prototype.getTitle = function() { + return this.title || this.constructor.title; + }; + + /** + * sets the value of a property + * @method setProperty + * @param {String} name + * @param {*} value + */ + LGraphNode.prototype.setProperty = function(name, value) { + if (!this.properties) { + this.properties = {}; + } + if( value === this.properties[name] ) + return; + var prev_value = this.properties[name]; + this.properties[name] = value; + if (this.onPropertyChanged) { + if( this.onPropertyChanged(name, value, prev_value) === false ) //abort change + this.properties[name] = prev_value; + } + if(this.widgets) //widgets could be linked to properties + for(var i = 0; i < this.widgets.length; ++i) + { + var w = this.widgets[i]; + if(!w) + continue; + if(w.options.property == name) + { + w.value = value; + break; + } + } + }; + + // Execution ************************* + /** + * sets the output data + * @method setOutputData + * @param {number} slot + * @param {*} data + */ + LGraphNode.prototype.setOutputData = function(slot, data) { + if (!this.outputs) { + return; + } + + //this maybe slow and a niche case + //if(slot && slot.constructor === String) + // slot = this.findOutputSlot(slot); + + if (slot == -1 || slot >= this.outputs.length) { + return; + } + + var output_info = this.outputs[slot]; + if (!output_info) { + return; + } + + //store data in the output itself in case we want to debug + output_info._data = data; + + //if there are connections, pass the data to the connections + if (this.outputs[slot].links) { + for (var i = 0; i < this.outputs[slot].links.length; i++) { + var link_id = this.outputs[slot].links[i]; + var link = this.graph.links[link_id]; + if(link) + link.data = data; + } + } + }; + + /** + * sets the output data type, useful when you want to be able to overwrite the data type + * @method setOutputDataType + * @param {number} slot + * @param {String} datatype + */ + LGraphNode.prototype.setOutputDataType = function(slot, type) { + if (!this.outputs) { + return; + } + if (slot == -1 || slot >= this.outputs.length) { + return; + } + var output_info = this.outputs[slot]; + if (!output_info) { + return; + } + //store data in the output itself in case we want to debug + output_info.type = type; + + //if there are connections, pass the data to the connections + if (this.outputs[slot].links) { + for (var i = 0; i < this.outputs[slot].links.length; i++) { + var link_id = this.outputs[slot].links[i]; + this.graph.links[link_id].type = type; + } + } + }; + + /** + * Retrieves the input data (data traveling through the connection) from one slot + * @method getInputData + * @param {number} slot + * @param {boolean} force_update if set to true it will force the connected node of this slot to output data into this link + * @return {*} data or if it is not connected returns undefined + */ + LGraphNode.prototype.getInputData = function(slot, force_update) { + if (!this.inputs) { + return; + } //undefined; + + if (slot >= this.inputs.length || this.inputs[slot].link == null) { + return; + } + + var link_id = this.inputs[slot].link; + var link = this.graph.links[link_id]; + if (!link) { + //bug: weird case but it happens sometimes + return null; + } + + if (!force_update) { + return link.data; + } + + //special case: used to extract data from the incoming connection before the graph has been executed + var node = this.graph.getNodeById(link.origin_id); + if (!node) { + return link.data; + } + + if (node.updateOutputData) { + node.updateOutputData(link.origin_slot); + } else if (node.onExecute) { + node.onExecute(); + } + + return link.data; + }; + + /** + * Retrieves the input data type (in case this supports multiple input types) + * @method getInputDataType + * @param {number} slot + * @return {String} datatype in string format + */ + LGraphNode.prototype.getInputDataType = function(slot) { + if (!this.inputs) { + return null; + } //undefined; + + if (slot >= this.inputs.length || this.inputs[slot].link == null) { + return null; + } + var link_id = this.inputs[slot].link; + var link = this.graph.links[link_id]; + if (!link) { + //bug: weird case but it happens sometimes + return null; + } + var node = this.graph.getNodeById(link.origin_id); + if (!node) { + return link.type; + } + var output_info = node.outputs[link.origin_slot]; + if (output_info) { + return output_info.type; + } + return null; + }; + + /** + * Retrieves the input data from one slot using its name instead of slot number + * @method getInputDataByName + * @param {String} slot_name + * @param {boolean} force_update if set to true it will force the connected node of this slot to output data into this link + * @return {*} data or if it is not connected returns null + */ + LGraphNode.prototype.getInputDataByName = function( + slot_name, + force_update + ) { + var slot = this.findInputSlot(slot_name); + if (slot == -1) { + return null; + } + return this.getInputData(slot, force_update); + }; + + /** + * tells you if there is a connection in one input slot + * @method isInputConnected + * @param {number} slot + * @return {boolean} + */ + LGraphNode.prototype.isInputConnected = function(slot) { + if (!this.inputs) { + return false; + } + return slot < this.inputs.length && this.inputs[slot].link != null; + }; + + /** + * tells you info about an input connection (which node, type, etc) + * @method getInputInfo + * @param {number} slot + * @return {Object} object or null { link: id, name: string, type: string or 0 } + */ + LGraphNode.prototype.getInputInfo = function(slot) { + if (!this.inputs) { + return null; + } + if (slot < this.inputs.length) { + return this.inputs[slot]; + } + return null; + }; + + /** + * returns the node connected in the input slot + * @method getInputNode + * @param {number} slot + * @return {LGraphNode} node or null + */ + LGraphNode.prototype.getInputNode = function(slot) { + if (!this.inputs) { + return null; + } + if (slot >= this.inputs.length) { + return null; + } + var input = this.inputs[slot]; + if (!input || input.link === null) { + return null; + } + var link_info = this.graph.links[input.link]; + if (!link_info) { + return null; + } + return this.graph.getNodeById(link_info.origin_id); + }; + + /** + * returns the value of an input with this name, otherwise checks if there is a property with that name + * @method getInputOrProperty + * @param {string} name + * @return {*} value + */ + LGraphNode.prototype.getInputOrProperty = function(name) { + if (!this.inputs || !this.inputs.length) { + return this.properties ? this.properties[name] : null; + } + + for (var i = 0, l = this.inputs.length; i < l; ++i) { + var input_info = this.inputs[i]; + if (name == input_info.name && input_info.link != null) { + var link = this.graph.links[input_info.link]; + if (link) { + return link.data; + } + } + } + return this.properties[name]; + }; + + /** + * tells you the last output data that went in that slot + * @method getOutputData + * @param {number} slot + * @return {Object} object or null + */ + LGraphNode.prototype.getOutputData = function(slot) { + if (!this.outputs) { + return null; + } + if (slot >= this.outputs.length) { + return null; + } + + var info = this.outputs[slot]; + return info._data; + }; + + /** + * tells you info about an output connection (which node, type, etc) + * @method getOutputInfo + * @param {number} slot + * @return {Object} object or null { name: string, type: string, links: [ ids of links in number ] } + */ + LGraphNode.prototype.getOutputInfo = function(slot) { + if (!this.outputs) { + return null; + } + if (slot < this.outputs.length) { + return this.outputs[slot]; + } + return null; + }; + + /** + * tells you if there is a connection in one output slot + * @method isOutputConnected + * @param {number} slot + * @return {boolean} + */ + LGraphNode.prototype.isOutputConnected = function(slot) { + if (!this.outputs) { + return false; + } + return ( + slot < this.outputs.length && + this.outputs[slot].links && + this.outputs[slot].links.length + ); + }; + + /** + * tells you if there is any connection in the output slots + * @method isAnyOutputConnected + * @return {boolean} + */ + LGraphNode.prototype.isAnyOutputConnected = function() { + if (!this.outputs) { + return false; + } + for (var i = 0; i < this.outputs.length; ++i) { + if (this.outputs[i].links && this.outputs[i].links.length) { + return true; + } + } + return false; + }; + + /** + * retrieves all the nodes connected to this output slot + * @method getOutputNodes + * @param {number} slot + * @return {array} + */ + LGraphNode.prototype.getOutputNodes = function(slot) { + if (!this.outputs || this.outputs.length == 0) { + return null; + } + + if (slot >= this.outputs.length) { + return null; + } + + var output = this.outputs[slot]; + if (!output.links || output.links.length == 0) { + return null; + } + + var r = []; + for (var i = 0; i < output.links.length; i++) { + var link_id = output.links[i]; + var link = this.graph.links[link_id]; + if (link) { + var target_node = this.graph.getNodeById(link.target_id); + if (target_node) { + r.push(target_node); + } + } + } + return r; + }; + + /** + * Triggers an event in this node, this will trigger any output with the same name + * @method trigger + * @param {String} event name ( "on_play", ... ) if action is equivalent to false then the event is send to all + * @param {*} param + */ + LGraphNode.prototype.trigger = function(action, param) { + if (!this.outputs || !this.outputs.length) { + return; + } + + if (this.graph) + this.graph._last_trigger_time = LiteGraph.getTime(); + + for (var i = 0; i < this.outputs.length; ++i) { + var output = this.outputs[i]; + if ( !output || output.type !== LiteGraph.EVENT || (action && output.name != action) ) + continue; + this.triggerSlot(i, param); + } + }; + + /** + * Triggers an slot event in this node + * @method triggerSlot + * @param {Number} slot the index of the output slot + * @param {*} param + * @param {Number} link_id [optional] in case you want to trigger and specific output link in a slot + */ + LGraphNode.prototype.triggerSlot = function(slot, param, link_id) { + if (!this.outputs) { + return; + } + + var output = this.outputs[slot]; + if (!output) { + return; + } + + var links = output.links; + if (!links || !links.length) { + return; + } + + if (this.graph) { + this.graph._last_trigger_time = LiteGraph.getTime(); + } + + //for every link attached here + for (var k = 0; k < links.length; ++k) { + var id = links[k]; + if (link_id != null && link_id != id) { + //to skip links + continue; + } + var link_info = this.graph.links[links[k]]; + if (!link_info) { + //not connected + continue; + } + link_info._last_time = LiteGraph.getTime(); + var node = this.graph.getNodeById(link_info.target_id); + if (!node) { + //node not found? + continue; + } + + //used to mark events in graph + var target_connection = node.inputs[link_info.target_slot]; + + if (node.onAction) { + node.onAction(target_connection.name, param); + } else if (node.mode === LiteGraph.ON_TRIGGER) { + if (node.onExecute) { + node.onExecute(param); + } + } + } + }; + + /** + * clears the trigger slot animation + * @method clearTriggeredSlot + * @param {Number} slot the index of the output slot + * @param {Number} link_id [optional] in case you want to trigger and specific output link in a slot + */ + LGraphNode.prototype.clearTriggeredSlot = function(slot, link_id) { + if (!this.outputs) { + return; + } + + var output = this.outputs[slot]; + if (!output) { + return; + } + + var links = output.links; + if (!links || !links.length) { + return; + } + + //for every link attached here + for (var k = 0; k < links.length; ++k) { + var id = links[k]; + if (link_id != null && link_id != id) { + //to skip links + continue; + } + var link_info = this.graph.links[links[k]]; + if (!link_info) { + //not connected + continue; + } + link_info._last_time = 0; + } + }; + + /** + * add a new property to this node + * @method addProperty + * @param {string} name + * @param {*} default_value + * @param {string} type string defining the output type ("vec3","number",...) + * @param {Object} extra_info this can be used to have special properties of the property (like values, etc) + */ + LGraphNode.prototype.addProperty = function( + name, + default_value, + type, + extra_info + ) { + var o = { name: name, type: type, default_value: default_value }; + if (extra_info) { + for (var i in extra_info) { + o[i] = extra_info[i]; + } + } + if (!this.properties_info) { + this.properties_info = []; + } + this.properties_info.push(o); + if (!this.properties) { + this.properties = {}; + } + this.properties[name] = default_value; + return o; + }; + + //connections + + /** + * add a new output slot to use in this node + * @method addOutput + * @param {string} name + * @param {string} type string defining the output type ("vec3","number",...) + * @param {Object} extra_info this can be used to have special properties of an output (label, special color, position, etc) + */ + LGraphNode.prototype.addOutput = function(name, type, extra_info) { + var o = { name: name, type: type, links: null }; + if (extra_info) { + for (var i in extra_info) { + o[i] = extra_info[i]; + } + } + + if (!this.outputs) { + this.outputs = []; + } + this.outputs.push(o); + if (this.onOutputAdded) { + this.onOutputAdded(o); + } + this.size = this.computeSize(); + this.setDirtyCanvas(true, true); + return o; + }; + + /** + * add a new output slot to use in this node + * @method addOutputs + * @param {Array} array of triplets like [[name,type,extra_info],[...]] + */ + LGraphNode.prototype.addOutputs = function(array) { + for (var i = 0; i < array.length; ++i) { + var info = array[i]; + var o = { name: info[0], type: info[1], link: null }; + if (array[2]) { + for (var j in info[2]) { + o[j] = info[2][j]; + } + } + + if (!this.outputs) { + this.outputs = []; + } + this.outputs.push(o); + if (this.onOutputAdded) { + this.onOutputAdded(o); + } + } + + this.size = this.computeSize(); + this.setDirtyCanvas(true, true); + }; + + /** + * remove an existing output slot + * @method removeOutput + * @param {number} slot + */ + LGraphNode.prototype.removeOutput = function(slot) { + this.disconnectOutput(slot); + this.outputs.splice(slot, 1); + for (var i = slot; i < this.outputs.length; ++i) { + if (!this.outputs[i] || !this.outputs[i].links) { + continue; + } + var links = this.outputs[i].links; + for (var j = 0; j < links.length; ++j) { + var link = this.graph.links[links[j]]; + if (!link) { + continue; + } + link.origin_slot -= 1; + } + } + + this.size = this.computeSize(); + if (this.onOutputRemoved) { + this.onOutputRemoved(slot); + } + this.setDirtyCanvas(true, true); + }; + + /** + * add a new input slot to use in this node + * @method addInput + * @param {string} name + * @param {string} type string defining the input type ("vec3","number",...), it its a generic one use 0 + * @param {Object} extra_info this can be used to have special properties of an input (label, color, position, etc) + */ + LGraphNode.prototype.addInput = function(name, type, extra_info) { + type = type || 0; + var o = { name: name, type: type, link: null }; + if (extra_info) { + for (var i in extra_info) { + o[i] = extra_info[i]; + } + } + + if (!this.inputs) { + this.inputs = []; + } + this.inputs.push(o); + this.size = this.computeSize(); + if (this.onInputAdded) { + this.onInputAdded(o); + } + this.setDirtyCanvas(true, true); + return o; + }; + + /** + * add several new input slots in this node + * @method addInputs + * @param {Array} array of triplets like [[name,type,extra_info],[...]] + */ + LGraphNode.prototype.addInputs = function(array) { + for (var i = 0; i < array.length; ++i) { + var info = array[i]; + var o = { name: info[0], type: info[1], link: null }; + if (array[2]) { + for (var j in info[2]) { + o[j] = info[2][j]; + } + } + + if (!this.inputs) { + this.inputs = []; + } + this.inputs.push(o); + if (this.onInputAdded) { + this.onInputAdded(o); + } + } + + this.size = this.computeSize(); + this.setDirtyCanvas(true, true); + }; + + /** + * remove an existing input slot + * @method removeInput + * @param {number} slot + */ + LGraphNode.prototype.removeInput = function(slot) { + this.disconnectInput(slot); + this.inputs.splice(slot, 1); + for (var i = slot; i < this.inputs.length; ++i) { + if (!this.inputs[i]) { + continue; + } + var link = this.graph.links[this.inputs[i].link]; + if (!link) { + continue; + } + link.target_slot -= 1; + } + this.size = this.computeSize(); + if (this.onInputRemoved) { + this.onInputRemoved(slot); + } + this.setDirtyCanvas(true, true); + }; + + /** + * add an special connection to this node (used for special kinds of graphs) + * @method addConnection + * @param {string} name + * @param {string} type string defining the input type ("vec3","number",...) + * @param {[x,y]} pos position of the connection inside the node + * @param {string} direction if is input or output + */ + LGraphNode.prototype.addConnection = function(name, type, pos, direction) { + var o = { + name: name, + type: type, + pos: pos, + direction: direction, + links: null + }; + this.connections.push(o); + return o; + }; + + /** + * computes the size of a node according to its inputs and output slots + * @method computeSize + * @param {number} minHeight + * @return {number} the total size + */ + LGraphNode.prototype.computeSize = function(minHeight, out) { + if (this.constructor.size) { + return this.constructor.size.concat(); + } + + var rows = Math.max( + this.inputs ? this.inputs.length : 1, + this.outputs ? this.outputs.length : 1 + ); + var size = out || new Float32Array([0, 0]); + rows = Math.max(rows, 1); + var font_size = LiteGraph.NODE_TEXT_SIZE; //although it should be graphcanvas.inner_text_font size + + var font_size = font_size; + var title_width = compute_text_size(this.title); + var input_width = 0; + var output_width = 0; + + if (this.inputs) { + for (var i = 0, l = this.inputs.length; i < l; ++i) { + var input = this.inputs[i]; + var text = input.label || input.name || ""; + var text_width = compute_text_size(text); + if (input_width < text_width) { + input_width = text_width; + } + } + } + + if (this.outputs) { + for (var i = 0, l = this.outputs.length; i < l; ++i) { + var output = this.outputs[i]; + var text = output.label || output.name || ""; + var text_width = compute_text_size(text); + if (output_width < text_width) { + output_width = text_width; + } + } + } + + size[0] = Math.max(input_width + output_width + 10, title_width); + size[0] = Math.max(size[0], LiteGraph.NODE_WIDTH); + if (this.widgets && this.widgets.length) { + size[0] = Math.max(size[0], LiteGraph.NODE_WIDTH * 1.5); + } + + size[1] = (this.constructor.slot_start_y || 0) + rows * LiteGraph.NODE_SLOT_HEIGHT; + + var widgets_height = 0; + if (this.widgets && this.widgets.length) { + for (var i = 0, l = this.widgets.length; i < l; ++i) { + if (this.widgets[i].computeSize) + widgets_height += this.widgets[i].computeSize(size[0])[1] + 4; + else + widgets_height += LiteGraph.NODE_WIDGET_HEIGHT + 4; + } + widgets_height += 8; + } + + //compute height using widgets height + if( this.widgets_up ) + size[1] = Math.max( size[1], widgets_height ); + else if( this.widgets_start_y != null ) + size[1] = Math.max( size[1], widgets_height + this.widgets_start_y ); + else + size[1] += widgets_height; + + if (this.onResize) { + this.onResize(size); + } + + function compute_text_size(text) { + if (!text) { + return 0; + } + return font_size * text.length * 0.6; + } + + if ( + this.constructor.min_height && + size[1] < this.constructor.min_height + ) { + size[1] = this.constructor.min_height; + } + + size[1] += 6; //margin + + return size; + }; + + /** + * returns all the info available about a property of this node. + * + * @method getPropertyInfo + * @param {String} property name of the property + * @return {Object} the object with all the available info + */ + LGraphNode.prototype.getPropertyInfo = function( property ) + { + var info = null; + + //there are several ways to define info about a property + //legacy mode + if (this.properties_info) { + for (var i = 0; i < this.properties_info.length; ++i) { + if (this.properties_info[i].name == property) { + info = this.properties_info[i]; + break; + } + } + } + //litescene mode using the constructor + if(this.constructor["@" + property]) + info = this.constructor["@" + property]; + + //litescene mode using the constructor + if (this.onGetPropertyInfo) { + info = this.onGetPropertyInfo(property); + } + + if (!info) + info = {}; + if(!info.type) + info.type = typeof this.properties[property]; + + return info; + } + + /** + * Defines a widget inside the node, it will be rendered on top of the node, you can control lots of properties + * + * @method addWidget + * @param {String} type the widget type (could be "number","string","combo" + * @param {String} name the text to show on the widget + * @param {String} value the default value + * @param {Function|String} callback function to call when it changes (optionally, it can be the name of the property to modify) + * @param {Object} options the object that contains special properties of this widget + * @return {Object} the created widget object + */ + LGraphNode.prototype.addWidget = function( type, name, value, callback, options ) + { + if (!this.widgets) { + this.widgets = []; + } + + if(!options && callback && callback.constructor === Object) + { + options = callback; + callback = null; + } + + if(options && options.constructor === String) //options can be the property name + options = { property: options }; + + if(callback && callback.constructor === String) //callback can be the property name + { + if(!options) + options = {}; + options.property = callback; + callback = null; + } + + if(callback && callback.constructor !== Function) + { + console.warn("addWidget: callback must be a function"); + callback = null; + } + + var w = { + type: type.toLowerCase(), + name: name, + value: value, + callback: callback, + options: options || {} + }; + + if (w.options.y !== undefined) { + w.y = w.options.y; + } + + if (!callback && !w.options.callback && !w.options.property) { + console.warn("LiteGraph addWidget(...) without a callback or property assigned"); + } + if (type == "combo" && !w.options.values) { + throw "LiteGraph addWidget('combo',...) requires to pass values in options: { values:['red','blue'] }"; + } + this.widgets.push(w); + this.size = this.computeSize(); + return w; + }; + + LGraphNode.prototype.addCustomWidget = function(custom_widget) { + if (!this.widgets) { + this.widgets = []; + } + this.widgets.push(custom_widget); + return custom_widget; + }; + + /** + * returns the bounding of the object, used for rendering purposes + * bounding is: [topleft_cornerx, topleft_cornery, width, height] + * @method getBounding + * @return {Float32Array[4]} the total size + */ + LGraphNode.prototype.getBounding = function(out) { + out = out || new Float32Array(4); + out[0] = this.pos[0] - 4; + out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT; + out[2] = this.size[0] + 4; + out[3] = this.size[1] + LiteGraph.NODE_TITLE_HEIGHT; + + if (this.onBounding) { + this.onBounding(out); + } + return out; + }; + + /** + * checks if a point is inside the shape of a node + * @method isPointInside + * @param {number} x + * @param {number} y + * @return {boolean} + */ + LGraphNode.prototype.isPointInside = function(x, y, margin, skip_title) { + margin = margin || 0; + + var margin_top = this.graph && this.graph.isLive() ? 0 : LiteGraph.NODE_TITLE_HEIGHT; + if (skip_title) { + margin_top = 0; + } + if (this.flags && this.flags.collapsed) { + //if ( distance([x,y], [this.pos[0] + this.size[0]*0.5, this.pos[1] + this.size[1]*0.5]) < LiteGraph.NODE_COLLAPSED_RADIUS) + if ( + isInsideRectangle( + x, + y, + this.pos[0] - margin, + this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT - margin, + (this._collapsed_width || LiteGraph.NODE_COLLAPSED_WIDTH) + + 2 * margin, + LiteGraph.NODE_TITLE_HEIGHT + 2 * margin + ) + ) { + return true; + } + } else if ( + this.pos[0] - 4 - margin < x && + this.pos[0] + this.size[0] + 4 + margin > x && + this.pos[1] - margin_top - margin < y && + this.pos[1] + this.size[1] + margin > y + ) { + return true; + } + return false; + }; + + /** + * checks if a point is inside a node slot, and returns info about which slot + * @method getSlotInPosition + * @param {number} x + * @param {number} y + * @return {Object} if found the object contains { input|output: slot object, slot: number, link_pos: [x,y] } + */ + LGraphNode.prototype.getSlotInPosition = function(x, y) { + //search for inputs + var link_pos = new Float32Array(2); + if (this.inputs) { + for (var i = 0, l = this.inputs.length; i < l; ++i) { + var input = this.inputs[i]; + this.getConnectionPos(true, i, link_pos); + if ( + isInsideRectangle( + x, + y, + link_pos[0] - 10, + link_pos[1] - 5, + 20, + 10 + ) + ) { + return { input: input, slot: i, link_pos: link_pos }; + } + } + } + + if (this.outputs) { + for (var i = 0, l = this.outputs.length; i < l; ++i) { + var output = this.outputs[i]; + this.getConnectionPos(false, i, link_pos); + if ( + isInsideRectangle( + x, + y, + link_pos[0] - 10, + link_pos[1] - 5, + 20, + 10 + ) + ) { + return { output: output, slot: i, link_pos: link_pos }; + } + } + } + + return null; + }; + + /** + * returns the input slot with a given name (used for dynamic slots), -1 if not found + * @method findInputSlot + * @param {string} name the name of the slot + * @return {number} the slot (-1 if not found) + */ + LGraphNode.prototype.findInputSlot = function(name) { + if (!this.inputs) { + return -1; + } + for (var i = 0, l = this.inputs.length; i < l; ++i) { + if (name == this.inputs[i].name) { + return i; + } + } + return -1; + }; + + /** + * returns the output slot with a given name (used for dynamic slots), -1 if not found + * @method findOutputSlot + * @param {string} name the name of the slot + * @return {number} the slot (-1 if not found) + */ + LGraphNode.prototype.findOutputSlot = function(name) { + if (!this.outputs) { + return -1; + } + for (var i = 0, l = this.outputs.length; i < l; ++i) { + if (name == this.outputs[i].name) { + return i; + } + } + return -1; + }; + + /** + * connect this node output to the input of another node + * @method connect + * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) + * @param {LGraphNode} node the target node + * @param {number_or_string} target_slot the input slot of the target node (could be the number of the slot or the string with the name of the slot, or -1 to connect a trigger) + * @return {Object} the link_info is created, otherwise null + */ + LGraphNode.prototype.connect = function(slot, target_node, target_slot) { + target_slot = target_slot || 0; + + if (!this.graph) { + //could be connected before adding it to a graph + console.log( + "Connect: Error, node doesn't belong to any graph. Nodes must be added first to a graph before connecting them." + ); //due to link ids being associated with graphs + return null; + } + + //seek for the output slot + if (slot.constructor === String) { + slot = this.findOutputSlot(slot); + if (slot == -1) { + if (LiteGraph.debug) { + console.log("Connect: Error, no slot of name " + slot); + } + return null; + } + } else if (!this.outputs || slot >= this.outputs.length) { + if (LiteGraph.debug) { + console.log("Connect: Error, slot number not found"); + } + return null; + } + + if (target_node && target_node.constructor === Number) { + target_node = this.graph.getNodeById(target_node); + } + if (!target_node) { + throw "target node is null"; + } + + //avoid loopback + if (target_node == this) { + return null; + } + + //you can specify the slot by name + if (target_slot.constructor === String) { + target_slot = target_node.findInputSlot(target_slot); + if (target_slot == -1) { + if (LiteGraph.debug) { + console.log( + "Connect: Error, no slot of name " + target_slot + ); + } + return null; + } + } else if (target_slot === LiteGraph.EVENT) { + //search for first slot with event? + /* + //create input for trigger + var input = target_node.addInput("onTrigger", LiteGraph.EVENT ); + target_slot = target_node.inputs.length - 1; //last one is the one created + target_node.mode = LiteGraph.ON_TRIGGER; + */ + return null; + } else if ( + !target_node.inputs || + target_slot >= target_node.inputs.length + ) { + if (LiteGraph.debug) { + console.log("Connect: Error, slot number not found"); + } + return null; + } + + //if there is something already plugged there, disconnect + if (target_node.inputs[target_slot].link != null) { + target_node.disconnectInput(target_slot); + } + + //why here?? + //this.setDirtyCanvas(false,true); + //this.graph.connectionChange( this ); + + var output = this.outputs[slot]; + + //allows nodes to block connection + if (target_node.onConnectInput) { + if ( + target_node.onConnectInput(target_slot, output.type, output) === + false + ) { + return null; + } + } + + var input = target_node.inputs[target_slot]; + var link_info = null; + + if (LiteGraph.isValidConnection(output.type, input.type)) { + link_info = new LLink( + ++this.graph.last_link_id, + input.type, + this.id, + slot, + target_node.id, + target_slot + ); + + //add to graph links list + this.graph.links[link_info.id] = link_info; + + //connect in output + if (output.links == null) { + output.links = []; + } + output.links.push(link_info.id); + //connect in input + target_node.inputs[target_slot].link = link_info.id; + if (this.graph) { + this.graph._version++; + } + if (this.onConnectionsChange) { + this.onConnectionsChange( + LiteGraph.OUTPUT, + slot, + true, + link_info, + output + ); + } //link_info has been created now, so its updated + if (target_node.onConnectionsChange) { + target_node.onConnectionsChange( + LiteGraph.INPUT, + target_slot, + true, + link_info, + input + ); + } + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange( + LiteGraph.INPUT, + target_node, + target_slot, + this, + slot + ); + this.graph.onNodeConnectionChange( + LiteGraph.OUTPUT, + this, + slot, + target_node, + target_slot + ); + } + } + + this.setDirtyCanvas(false, true); + this.graph.connectionChange(this, link_info); + + return link_info; + }; + + /** + * disconnect one output to an specific node + * @method disconnectOutput + * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) + * @param {LGraphNode} target_node the target node to which this slot is connected [Optional, if not target_node is specified all nodes will be disconnected] + * @return {boolean} if it was disconnected successfully + */ + LGraphNode.prototype.disconnectOutput = function(slot, target_node) { + if (slot.constructor === String) { + slot = this.findOutputSlot(slot); + if (slot == -1) { + if (LiteGraph.debug) { + console.log("Connect: Error, no slot of name " + slot); + } + return false; + } + } else if (!this.outputs || slot >= this.outputs.length) { + if (LiteGraph.debug) { + console.log("Connect: Error, slot number not found"); + } + return false; + } + + //get output slot + var output = this.outputs[slot]; + if (!output || !output.links || output.links.length == 0) { + return false; + } + + //one of the output links in this slot + if (target_node) { + if (target_node.constructor === Number) { + target_node = this.graph.getNodeById(target_node); + } + if (!target_node) { + throw "Target Node not found"; + } + + for (var i = 0, l = output.links.length; i < l; i++) { + var link_id = output.links[i]; + var link_info = this.graph.links[link_id]; + + //is the link we are searching for... + if (link_info.target_id == target_node.id) { + output.links.splice(i, 1); //remove here + var input = target_node.inputs[link_info.target_slot]; + input.link = null; //remove there + delete this.graph.links[link_id]; //remove the link from the links pool + if (this.graph) { + this.graph._version++; + } + if (target_node.onConnectionsChange) { + target_node.onConnectionsChange( + LiteGraph.INPUT, + link_info.target_slot, + false, + link_info, + input + ); + } //link_info hasn't been modified so its ok + if (this.onConnectionsChange) { + this.onConnectionsChange( + LiteGraph.OUTPUT, + slot, + false, + link_info, + output + ); + } + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange( + LiteGraph.OUTPUT, + this, + slot + ); + } + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange( + LiteGraph.OUTPUT, + this, + slot + ); + this.graph.onNodeConnectionChange( + LiteGraph.INPUT, + target_node, + link_info.target_slot + ); + } + break; + } + } + } //all the links in this output slot + else { + for (var i = 0, l = output.links.length; i < l; i++) { + var link_id = output.links[i]; + var link_info = this.graph.links[link_id]; + if (!link_info) { + //bug: it happens sometimes + continue; + } + + var target_node = this.graph.getNodeById(link_info.target_id); + var input = null; + if (this.graph) { + this.graph._version++; + } + if (target_node) { + input = target_node.inputs[link_info.target_slot]; + input.link = null; //remove other side link + if (target_node.onConnectionsChange) { + target_node.onConnectionsChange( + LiteGraph.INPUT, + link_info.target_slot, + false, + link_info, + input + ); + } //link_info hasn't been modified so its ok + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange( + LiteGraph.INPUT, + target_node, + link_info.target_slot + ); + } + } + delete this.graph.links[link_id]; //remove the link from the links pool + if (this.onConnectionsChange) { + this.onConnectionsChange( + LiteGraph.OUTPUT, + slot, + false, + link_info, + output + ); + } + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange( + LiteGraph.OUTPUT, + this, + slot + ); + this.graph.onNodeConnectionChange( + LiteGraph.INPUT, + target_node, + link_info.target_slot + ); + } + } + output.links = null; + } + + this.setDirtyCanvas(false, true); + this.graph.connectionChange(this); + return true; + }; + + /** + * disconnect one input + * @method disconnectInput + * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) + * @return {boolean} if it was disconnected successfully + */ + LGraphNode.prototype.disconnectInput = function(slot) { + //seek for the output slot + if (slot.constructor === String) { + slot = this.findInputSlot(slot); + if (slot == -1) { + if (LiteGraph.debug) { + console.log("Connect: Error, no slot of name " + slot); + } + return false; + } + } else if (!this.inputs || slot >= this.inputs.length) { + if (LiteGraph.debug) { + console.log("Connect: Error, slot number not found"); + } + return false; + } + + var input = this.inputs[slot]; + if (!input) { + return false; + } + + var link_id = this.inputs[slot].link; + this.inputs[slot].link = null; + + //remove other side + var link_info = this.graph.links[link_id]; + if (link_info) { + var target_node = this.graph.getNodeById(link_info.origin_id); + if (!target_node) { + return false; + } + + var output = target_node.outputs[link_info.origin_slot]; + if (!output || !output.links || output.links.length == 0) { + return false; + } + + //search in the inputs list for this link + for (var i = 0, l = output.links.length; i < l; i++) { + if (output.links[i] == link_id) { + output.links.splice(i, 1); + break; + } + } + + delete this.graph.links[link_id]; //remove from the pool + if (this.graph) { + this.graph._version++; + } + if (this.onConnectionsChange) { + this.onConnectionsChange( + LiteGraph.INPUT, + slot, + false, + link_info, + input + ); + } + if (target_node.onConnectionsChange) { + target_node.onConnectionsChange( + LiteGraph.OUTPUT, + i, + false, + link_info, + output + ); + } + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange( + LiteGraph.OUTPUT, + target_node, + i + ); + this.graph.onNodeConnectionChange(LiteGraph.INPUT, this, slot); + } + } + + this.setDirtyCanvas(false, true); + this.graph.connectionChange(this); + return true; + }; + + /** + * returns the center of a connection point in canvas coords + * @method getConnectionPos + * @param {boolean} is_input true if if a input slot, false if it is an output + * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) + * @param {vec2} out [optional] a place to store the output, to free garbage + * @return {[x,y]} the position + **/ + LGraphNode.prototype.getConnectionPos = function( + is_input, + slot_number, + out + ) { + out = out || new Float32Array(2); + var num_slots = 0; + if (is_input && this.inputs) { + num_slots = this.inputs.length; + } + if (!is_input && this.outputs) { + num_slots = this.outputs.length; + } + + var offset = LiteGraph.NODE_SLOT_HEIGHT * 0.5; + + if (this.flags.collapsed) { + var w = this._collapsed_width || LiteGraph.NODE_COLLAPSED_WIDTH; + if (this.horizontal) { + out[0] = this.pos[0] + w * 0.5; + if (is_input) { + out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT; + } else { + out[1] = this.pos[1]; + } + } else { + if (is_input) { + out[0] = this.pos[0]; + } else { + out[0] = this.pos[0] + w; + } + out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT * 0.5; + } + return out; + } + + //weird feature that never got finished + if (is_input && slot_number == -1) { + out[0] = this.pos[0] + LiteGraph.NODE_TITLE_HEIGHT * 0.5; + out[1] = this.pos[1] + LiteGraph.NODE_TITLE_HEIGHT * 0.5; + return out; + } + + //hard-coded pos + if ( + is_input && + num_slots > slot_number && + this.inputs[slot_number].pos + ) { + out[0] = this.pos[0] + this.inputs[slot_number].pos[0]; + out[1] = this.pos[1] + this.inputs[slot_number].pos[1]; + return out; + } else if ( + !is_input && + num_slots > slot_number && + this.outputs[slot_number].pos + ) { + out[0] = this.pos[0] + this.outputs[slot_number].pos[0]; + out[1] = this.pos[1] + this.outputs[slot_number].pos[1]; + return out; + } + + //horizontal distributed slots + if (this.horizontal) { + out[0] = + this.pos[0] + (slot_number + 0.5) * (this.size[0] / num_slots); + if (is_input) { + out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT; + } else { + out[1] = this.pos[1] + this.size[1]; + } + return out; + } + + //default vertical slots + if (is_input) { + out[0] = this.pos[0] + offset; + } else { + out[0] = this.pos[0] + this.size[0] + 1 - offset; + } + out[1] = + this.pos[1] + + (slot_number + 0.7) * LiteGraph.NODE_SLOT_HEIGHT + + (this.constructor.slot_start_y || 0); + return out; + }; + + /* Force align to grid */ + LGraphNode.prototype.alignToGrid = function() { + this.pos[0] = + LiteGraph.CANVAS_GRID_SIZE * + Math.round(this.pos[0] / LiteGraph.CANVAS_GRID_SIZE); + this.pos[1] = + LiteGraph.CANVAS_GRID_SIZE * + Math.round(this.pos[1] / LiteGraph.CANVAS_GRID_SIZE); + }; + + /* Console output */ + LGraphNode.prototype.trace = function(msg) { + if (!this.console) { + this.console = []; + } + this.console.push(msg); + if (this.console.length > LGraphNode.MAX_CONSOLE) { + this.console.shift(); + } + + this.graph.onNodeTrace(this, msg); + }; + + /* Forces to redraw or the main canvas (LGraphNode) or the bg canvas (links) */ + LGraphNode.prototype.setDirtyCanvas = function( + dirty_foreground, + dirty_background + ) { + if (!this.graph) { + return; + } + this.graph.sendActionToCanvas("setDirty", [ + dirty_foreground, + dirty_background + ]); + }; + + LGraphNode.prototype.loadImage = function(url) { + var img = new Image(); + img.src = LiteGraph.node_images_path + url; + img.ready = false; + + var that = this; + img.onload = function() { + this.ready = true; + that.setDirtyCanvas(true); + }; + return img; + }; + + //safe LGraphNode action execution (not sure if safe) + /* +LGraphNode.prototype.executeAction = function(action) +{ + if(action == "") return false; + + if( action.indexOf(";") != -1 || action.indexOf("}") != -1) + { + this.trace("Error: Action contains unsafe characters"); + return false; + } + + var tokens = action.split("("); + var func_name = tokens[0]; + if( typeof(this[func_name]) != "function") + { + this.trace("Error: Action not found on node: " + func_name); + return false; + } + + var code = action; + + try + { + var _foo = eval; + eval = null; + (new Function("with(this) { " + code + "}")).call(this); + eval = _foo; + } + catch (err) + { + this.trace("Error executing action {" + action + "} :" + err); + return false; + } + + return true; +} +*/ + + /* Allows to get onMouseMove and onMouseUp events even if the mouse is out of focus */ + LGraphNode.prototype.captureInput = function(v) { + if (!this.graph || !this.graph.list_of_graphcanvas) { + return; + } + + var list = this.graph.list_of_graphcanvas; + + for (var i = 0; i < list.length; ++i) { + var c = list[i]; + //releasing somebody elses capture?! + if (!v && c.node_capturing_input != this) { + continue; + } + + //change + c.node_capturing_input = v ? this : null; + } + }; + + /** + * Collapse the node to make it smaller on the canvas + * @method collapse + **/ + LGraphNode.prototype.collapse = function(force) { + this.graph._version++; + if (this.constructor.collapsable === false && !force) { + return; + } + if (!this.flags.collapsed) { + this.flags.collapsed = true; + } else { + this.flags.collapsed = false; + } + this.setDirtyCanvas(true, true); + }; + + /** + * Forces the node to do not move or realign on Z + * @method pin + **/ + + LGraphNode.prototype.pin = function(v) { + this.graph._version++; + if (v === undefined) { + this.flags.pinned = !this.flags.pinned; + } else { + this.flags.pinned = v; + } + }; + + LGraphNode.prototype.localToScreen = function(x, y, graphcanvas) { + return [ + (x + this.pos[0]) * graphcanvas.scale + graphcanvas.offset[0], + (y + this.pos[1]) * graphcanvas.scale + graphcanvas.offset[1] + ]; + }; + + function LGraphGroup(title) { + this._ctor(title); + } + + global.LGraphGroup = LiteGraph.LGraphGroup = LGraphGroup; + + LGraphGroup.prototype._ctor = function(title) { + this.title = title || "Group"; + this.font_size = 24; + this.color = LGraphCanvas.node_colors.pale_blue + ? LGraphCanvas.node_colors.pale_blue.groupcolor + : "#AAA"; + this._bounding = new Float32Array([10, 10, 140, 80]); + this._pos = this._bounding.subarray(0, 2); + this._size = this._bounding.subarray(2, 4); + this._nodes = []; + this.graph = null; + + Object.defineProperty(this, "pos", { + set: function(v) { + if (!v || v.length < 2) { + return; + } + this._pos[0] = v[0]; + this._pos[1] = v[1]; + }, + get: function() { + return this._pos; + }, + enumerable: true + }); + + Object.defineProperty(this, "size", { + set: function(v) { + if (!v || v.length < 2) { + return; + } + this._size[0] = Math.max(140, v[0]); + this._size[1] = Math.max(80, v[1]); + }, + get: function() { + return this._size; + }, + enumerable: true + }); + }; + + LGraphGroup.prototype.configure = function(o) { + this.title = o.title; + this._bounding.set(o.bounding); + this.color = o.color; + this.font = o.font; + }; + + LGraphGroup.prototype.serialize = function() { + var b = this._bounding; + return { + title: this.title, + bounding: [ + Math.round(b[0]), + Math.round(b[1]), + Math.round(b[2]), + Math.round(b[3]) + ], + color: this.color, + font: this.font + }; + }; + + LGraphGroup.prototype.move = function(deltax, deltay, ignore_nodes) { + this._pos[0] += deltax; + this._pos[1] += deltay; + if (ignore_nodes) { + return; + } + for (var i = 0; i < this._nodes.length; ++i) { + var node = this._nodes[i]; + node.pos[0] += deltax; + node.pos[1] += deltay; + } + }; + + LGraphGroup.prototype.recomputeInsideNodes = function() { + this._nodes.length = 0; + var nodes = this.graph._nodes; + var node_bounding = new Float32Array(4); + + for (var i = 0; i < nodes.length; ++i) { + var node = nodes[i]; + node.getBounding(node_bounding); + if (!overlapBounding(this._bounding, node_bounding)) { + continue; + } //out of the visible area + this._nodes.push(node); + } + }; + + LGraphGroup.prototype.isPointInside = LGraphNode.prototype.isPointInside; + LGraphGroup.prototype.setDirtyCanvas = LGraphNode.prototype.setDirtyCanvas; + + //**************************************** + + //Scale and Offset + function DragAndScale(element, skip_events) { + this.offset = new Float32Array([0, 0]); + this.scale = 1; + this.max_scale = 10; + this.min_scale = 0.1; + this.onredraw = null; + this.enabled = true; + this.last_mouse = [0, 0]; + this.element = null; + this.visible_area = new Float32Array(4); + + if (element) { + this.element = element; + if (!skip_events) { + this.bindEvents(element); + } + } + } + + LiteGraph.DragAndScale = DragAndScale; + + DragAndScale.prototype.bindEvents = function(element) { + this.last_mouse = new Float32Array(2); + + this._binded_mouse_callback = this.onMouse.bind(this); + + element.addEventListener("mousedown", this._binded_mouse_callback); + element.addEventListener("mousemove", this._binded_mouse_callback); + + element.addEventListener( + "mousewheel", + this._binded_mouse_callback, + false + ); + element.addEventListener("wheel", this._binded_mouse_callback, false); + }; + + DragAndScale.prototype.computeVisibleArea = function() { + if (!this.element) { + this.visible_area[0] = this.visible_area[1] = this.visible_area[2] = this.visible_area[3] = 0; + return; + } + var width = this.element.width; + var height = this.element.height; + var startx = -this.offset[0]; + var starty = -this.offset[1]; + var endx = startx + width / this.scale; + var endy = starty + height / this.scale; + this.visible_area[0] = startx; + this.visible_area[1] = starty; + this.visible_area[2] = endx - startx; + this.visible_area[3] = endy - starty; + }; + + DragAndScale.prototype.onMouse = function(e) { + if (!this.enabled) { + return; + } + + var canvas = this.element; + var rect = canvas.getBoundingClientRect(); + var x = e.clientX - rect.left; + var y = e.clientY - rect.top; + e.canvasx = x; + e.canvasy = y; + e.dragging = this.dragging; + + var ignore = false; + if (this.onmouse) { + ignore = this.onmouse(e); + } + + if (e.type == "mousedown") { + this.dragging = true; + canvas.removeEventListener( + "mousemove", + this._binded_mouse_callback + ); + document.body.addEventListener( + "mousemove", + this._binded_mouse_callback + ); + document.body.addEventListener( + "mouseup", + this._binded_mouse_callback + ); + } else if (e.type == "mousemove") { + if (!ignore) { + var deltax = x - this.last_mouse[0]; + var deltay = y - this.last_mouse[1]; + if (this.dragging) { + this.mouseDrag(deltax, deltay); + } + } + } else if (e.type == "mouseup") { + this.dragging = false; + document.body.removeEventListener( + "mousemove", + this._binded_mouse_callback + ); + document.body.removeEventListener( + "mouseup", + this._binded_mouse_callback + ); + canvas.addEventListener("mousemove", this._binded_mouse_callback); + } else if ( + e.type == "mousewheel" || + e.type == "wheel" || + e.type == "DOMMouseScroll" + ) { + e.eventType = "mousewheel"; + if (e.type == "wheel") { + e.wheel = -e.deltaY; + } else { + e.wheel = + e.wheelDeltaY != null ? e.wheelDeltaY : e.detail * -60; + } + + //from stack overflow + e.delta = e.wheelDelta + ? e.wheelDelta / 40 + : e.deltaY + ? -e.deltaY / 3 + : 0; + this.changeDeltaScale(1.0 + e.delta * 0.05); + } + + this.last_mouse[0] = x; + this.last_mouse[1] = y; + + e.preventDefault(); + e.stopPropagation(); + return false; + }; + + DragAndScale.prototype.toCanvasContext = function(ctx) { + ctx.scale(this.scale, this.scale); + ctx.translate(this.offset[0], this.offset[1]); + }; + + DragAndScale.prototype.convertOffsetToCanvas = function(pos) { + //return [pos[0] / this.scale - this.offset[0], pos[1] / this.scale - this.offset[1]]; + return [ + (pos[0] + this.offset[0]) * this.scale, + (pos[1] + this.offset[1]) * this.scale + ]; + }; + + DragAndScale.prototype.convertCanvasToOffset = function(pos, out) { + out = out || [0, 0]; + out[0] = pos[0] / this.scale - this.offset[0]; + out[1] = pos[1] / this.scale - this.offset[1]; + return out; + }; + + DragAndScale.prototype.mouseDrag = function(x, y) { + this.offset[0] += x / this.scale; + this.offset[1] += y / this.scale; + + if (this.onredraw) { + this.onredraw(this); + } + }; + + DragAndScale.prototype.changeScale = function(value, zooming_center) { + if (value < this.min_scale) { + value = this.min_scale; + } else if (value > this.max_scale) { + value = this.max_scale; + } + + if (value == this.scale) { + return; + } + + if (!this.element) { + return; + } + + var rect = this.element.getBoundingClientRect(); + if (!rect) { + return; + } + + zooming_center = zooming_center || [ + rect.width * 0.5, + rect.height * 0.5 + ]; + var center = this.convertCanvasToOffset(zooming_center); + this.scale = value; + if (Math.abs(this.scale - 1) < 0.01) { + this.scale = 1; + } + + var new_center = this.convertCanvasToOffset(zooming_center); + var delta_offset = [ + new_center[0] - center[0], + new_center[1] - center[1] + ]; + + this.offset[0] += delta_offset[0]; + this.offset[1] += delta_offset[1]; + + if (this.onredraw) { + this.onredraw(this); + } + }; + + DragAndScale.prototype.changeDeltaScale = function(value, zooming_center) { + this.changeScale(this.scale * value, zooming_center); + }; + + DragAndScale.prototype.reset = function() { + this.scale = 1; + this.offset[0] = 0; + this.offset[1] = 0; + }; + + //********************************************************************************* + // LGraphCanvas: LGraph renderer CLASS + //********************************************************************************* + + /** + * This class is in charge of rendering one graph inside a canvas. And provides all the interaction required. + * Valid callbacks are: onNodeSelected, onNodeDeselected, onShowNodePanel, onNodeDblClicked + * + * @class LGraphCanvas + * @constructor + * @param {HTMLCanvas} canvas the canvas where you want to render (it accepts a selector in string format or the canvas element itself) + * @param {LGraph} graph [optional] + * @param {Object} options [optional] { skip_rendering, autoresize } + */ + function LGraphCanvas(canvas, graph, options) { + options = options || {}; + + //if(graph === undefined) + // throw ("No graph assigned"); + this.background_image = + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; + + if (canvas && canvas.constructor === String) { + canvas = document.querySelector(canvas); + } + + this.ds = new DragAndScale(); + this.zoom_modify_alpha = true; //otherwise it generates ugly patterns when scaling down too much + + this.title_text_font = "" + LiteGraph.NODE_TEXT_SIZE + "px Arial"; + this.inner_text_font = + "normal " + LiteGraph.NODE_SUBTEXT_SIZE + "px Arial"; + this.node_title_color = LiteGraph.NODE_TITLE_COLOR; + this.default_link_color = LiteGraph.LINK_COLOR; + this.default_connection_color = { + input_off: "#778", + input_on: "#7F7", + output_off: "#778", + output_on: "#7F7" + }; + + this.highquality_render = true; + this.use_gradients = false; //set to true to render titlebar with gradients + this.editor_alpha = 1; //used for transition + this.pause_rendering = false; + this.clear_background = true; + + this.read_only = false; //if set to true users cannot modify the graph + this.render_only_selected = true; + this.live_mode = false; + this.show_info = true; + this.allow_dragcanvas = true; + this.allow_dragnodes = true; + this.allow_interaction = true; //allow to control widgets, buttons, collapse, etc + this.allow_searchbox = true; + this.allow_reconnect_links = false; //allows to change a connection with having to redo it again + + this.drag_mode = false; + this.dragging_rectangle = null; + + this.filter = null; //allows to filter to only accept some type of nodes in a graph + + this.always_render_background = false; + this.render_shadows = true; + this.render_canvas_border = true; + this.render_connections_shadows = false; //too much cpu + this.render_connections_border = true; + this.render_curved_connections = false; + this.render_connection_arrows = false; + this.render_collapsed_slots = true; + this.render_execution_order = false; + this.render_title_colored = true; + this.render_link_tooltip = true; + + this.links_render_mode = LiteGraph.SPLINE_LINK; + + this.canvas_mouse = [0, 0]; //mouse in canvas graph coordinates, where 0,0 is the top-left corner of the blue rectangle + + //to personalize the search box + this.onSearchBox = null; + this.onSearchBoxSelection = null; + + //callbacks + this.onMouse = null; + this.onDrawBackground = null; //to render background objects (behind nodes and connections) in the canvas affected by transform + this.onDrawForeground = null; //to render foreground objects (above nodes and connections) in the canvas affected by transform + this.onDrawOverlay = null; //to render foreground objects not affected by transform (for GUIs) + this.onDrawLinkTooltip = null; //called when rendering a tooltip + this.onNodeMoved = null; //called after moving a node + this.onSelectionChange = null; //called if the selection changes + + this.connections_width = 3; + this.round_radius = 8; + + this.current_node = null; + this.node_widget = null; //used for widgets + this.over_link_center = null; + this.last_mouse_position = [0, 0]; + this.visible_area = this.ds.visible_area; + this.visible_links = []; + + //link canvas and graph + if (graph) { + graph.attachCanvas(this); + } + + this.setCanvas(canvas); + this.clear(); + + if (!options.skip_render) { + this.startRendering(); + } + + this.autoresize = options.autoresize; + } + + global.LGraphCanvas = LiteGraph.LGraphCanvas = LGraphCanvas; + + LGraphCanvas.link_type_colors = { + "-1": LiteGraph.EVENT_LINK_COLOR, + number: "#AAA", + node: "#DCA" + }; + LGraphCanvas.gradients = {}; //cache of gradients + + /** + * clears all the data inside + * + * @method clear + */ + LGraphCanvas.prototype.clear = function() { + this.frame = 0; + this.last_draw_time = 0; + this.render_time = 0; + this.fps = 0; + + //this.scale = 1; + //this.offset = [0,0]; + + this.dragging_rectangle = null; + + this.selected_nodes = {}; + this.selected_group = null; + + this.visible_nodes = []; + this.node_dragged = null; + this.node_over = null; + this.node_capturing_input = null; + this.connecting_node = null; + this.highlighted_links = {}; + + this.dirty_canvas = true; + this.dirty_bgcanvas = true; + this.dirty_area = null; + + this.node_in_panel = null; + this.node_widget = null; + + this.last_mouse = [0, 0]; + this.last_mouseclick = 0; + this.visible_area.set([0, 0, 0, 0]); + + if (this.onClear) { + this.onClear(); + } + }; + + /** + * assigns a graph, you can reassign graphs to the same canvas + * + * @method setGraph + * @param {LGraph} graph + */ + LGraphCanvas.prototype.setGraph = function(graph, skip_clear) { + if (this.graph == graph) { + return; + } + + if (!skip_clear) { + this.clear(); + } + + if (!graph && this.graph) { + this.graph.detachCanvas(this); + return; + } + + graph.attachCanvas(this); + + //remove the graph stack in case a subgraph was open + if (this._graph_stack) + this._graph_stack = null; + + this.setDirty(true, true); + }; + + /** + * opens a graph contained inside a node in the current graph + * + * @method openSubgraph + * @param {LGraph} graph + */ + LGraphCanvas.prototype.openSubgraph = function(graph) { + if (!graph) { + throw "graph cannot be null"; + } + + if (this.graph == graph) { + throw "graph cannot be the same"; + } + + this.clear(); + + if (this.graph) { + if (!this._graph_stack) { + this._graph_stack = []; + } + this._graph_stack.push(this.graph); + } + + graph.attachCanvas(this); + this.setDirty(true, true); + }; + + /** + * closes a subgraph contained inside a node + * + * @method closeSubgraph + * @param {LGraph} assigns a graph + */ + LGraphCanvas.prototype.closeSubgraph = function() { + if (!this._graph_stack || this._graph_stack.length == 0) { + return; + } + var subgraph_node = this.graph._subgraph_node; + var graph = this._graph_stack.pop(); + this.selected_nodes = {}; + this.highlighted_links = {}; + graph.attachCanvas(this); + this.setDirty(true, true); + if (subgraph_node) { + this.centerOnNode(subgraph_node); + this.selectNodes([subgraph_node]); + } + }; + + /** + * returns the visualy active graph (in case there are more in the stack) + * @method getCurrentGraph + * @return {LGraph} the active graph + */ + LGraphCanvas.prototype.getCurrentGraph = function() { + return this.graph; + }; + + /** + * assigns a canvas + * + * @method setCanvas + * @param {Canvas} assigns a canvas (also accepts the ID of the element (not a selector) + */ + LGraphCanvas.prototype.setCanvas = function(canvas, skip_events) { + var that = this; + + if (canvas) { + if (canvas.constructor === String) { + canvas = document.getElementById(canvas); + if (!canvas) { + throw "Error creating LiteGraph canvas: Canvas not found"; + } + } + } + + if (canvas === this.canvas) { + return; + } + + if (!canvas && this.canvas) { + //maybe detach events from old_canvas + if (!skip_events) { + this.unbindEvents(); + } + } + + this.canvas = canvas; + this.ds.element = canvas; + + if (!canvas) { + return; + } + + //this.canvas.tabindex = "1000"; + canvas.className += " lgraphcanvas"; + canvas.data = this; + canvas.tabindex = "1"; //to allow key events + + //bg canvas: used for non changing stuff + this.bgcanvas = null; + if (!this.bgcanvas) { + this.bgcanvas = document.createElement("canvas"); + this.bgcanvas.width = this.canvas.width; + this.bgcanvas.height = this.canvas.height; + } + + if (canvas.getContext == null) { + if (canvas.localName != "canvas") { + throw "Element supplied for LGraphCanvas must be a element, you passed a " + + canvas.localName; + } + throw "This browser doesn't support Canvas"; + } + + var ctx = (this.ctx = canvas.getContext("2d")); + if (ctx == null) { + if (!canvas.webgl_enabled) { + console.warn( + "This canvas seems to be WebGL, enabling WebGL renderer" + ); + } + this.enableWebGL(); + } + + //input: (move and up could be unbinded) + this._mousemove_callback = this.processMouseMove.bind(this); + this._mouseup_callback = this.processMouseUp.bind(this); + + if (!skip_events) { + this.bindEvents(); + } + }; + + //used in some events to capture them + LGraphCanvas.prototype._doNothing = function doNothing(e) { + e.preventDefault(); + return false; + }; + LGraphCanvas.prototype._doReturnTrue = function doNothing(e) { + e.preventDefault(); + return true; + }; + + /** + * binds mouse, keyboard, touch and drag events to the canvas + * @method bindEvents + **/ + LGraphCanvas.prototype.bindEvents = function() { + if (this._events_binded) { + console.warn("LGraphCanvas: events already binded"); + return; + } + + var canvas = this.canvas; + var ref_window = this.getCanvasWindow(); + var document = ref_window.document; //hack used when moving canvas between windows + + this._mousedown_callback = this.processMouseDown.bind(this); + this._mousewheel_callback = this.processMouseWheel.bind(this); + + canvas.addEventListener("mousedown", this._mousedown_callback, true); //down do not need to store the binded + canvas.addEventListener("mousemove", this._mousemove_callback); + canvas.addEventListener("mousewheel", this._mousewheel_callback, false); + + canvas.addEventListener("contextmenu", this._doNothing); + canvas.addEventListener( + "DOMMouseScroll", + this._mousewheel_callback, + false + ); + + //touch events + //if( 'touchstart' in document.documentElement ) + { + canvas.addEventListener("touchstart", this.touchHandler, true); + canvas.addEventListener("touchmove", this.touchHandler, true); + canvas.addEventListener("touchend", this.touchHandler, true); + canvas.addEventListener("touchcancel", this.touchHandler, true); + } + + //Keyboard ****************** + this._key_callback = this.processKey.bind(this); + + canvas.addEventListener("keydown", this._key_callback, true); + document.addEventListener("keyup", this._key_callback, true); //in document, otherwise it doesn't fire keyup + + //Dropping Stuff over nodes ************************************ + this._ondrop_callback = this.processDrop.bind(this); + + canvas.addEventListener("dragover", this._doNothing, false); + canvas.addEventListener("dragend", this._doNothing, false); + canvas.addEventListener("drop", this._ondrop_callback, false); + canvas.addEventListener("dragenter", this._doReturnTrue, false); + + this._events_binded = true; + }; + + /** + * unbinds mouse events from the canvas + * @method unbindEvents + **/ + LGraphCanvas.prototype.unbindEvents = function() { + if (!this._events_binded) { + console.warn("LGraphCanvas: no events binded"); + return; + } + + var ref_window = this.getCanvasWindow(); + var document = ref_window.document; + + this.canvas.removeEventListener("mousedown", this._mousedown_callback); + this.canvas.removeEventListener( + "mousewheel", + this._mousewheel_callback + ); + this.canvas.removeEventListener( + "DOMMouseScroll", + this._mousewheel_callback + ); + this.canvas.removeEventListener("keydown", this._key_callback); + document.removeEventListener("keyup", this._key_callback); + this.canvas.removeEventListener("contextmenu", this._doNothing); + this.canvas.removeEventListener("drop", this._ondrop_callback); + this.canvas.removeEventListener("dragenter", this._doReturnTrue); + + this.canvas.removeEventListener("touchstart", this.touchHandler); + this.canvas.removeEventListener("touchmove", this.touchHandler); + this.canvas.removeEventListener("touchend", this.touchHandler); + this.canvas.removeEventListener("touchcancel", this.touchHandler); + + this._mousedown_callback = null; + this._mousewheel_callback = null; + this._key_callback = null; + this._ondrop_callback = null; + + this._events_binded = false; + }; + + LGraphCanvas.getFileExtension = function(url) { + var question = url.indexOf("?"); + if (question != -1) { + url = url.substr(0, question); + } + var point = url.lastIndexOf("."); + if (point == -1) { + return ""; + } + return url.substr(point + 1).toLowerCase(); + }; + + /** + * this function allows to render the canvas using WebGL instead of Canvas2D + * this is useful if you plant to render 3D objects inside your nodes, it uses litegl.js for webgl and canvas2DtoWebGL to emulate the Canvas2D calls in webGL + * @method enableWebGL + **/ + LGraphCanvas.prototype.enableWebGL = function() { + if (typeof GL === undefined) { + throw "litegl.js must be included to use a WebGL canvas"; + } + if (typeof enableWebGLCanvas === undefined) { + throw "webglCanvas.js must be included to use this feature"; + } + + this.gl = this.ctx = enableWebGLCanvas(this.canvas); + this.ctx.webgl = true; + this.bgcanvas = this.canvas; + this.bgctx = this.gl; + this.canvas.webgl_enabled = true; + + /* + GL.create({ canvas: this.bgcanvas }); + this.bgctx = enableWebGLCanvas( this.bgcanvas ); + window.gl = this.gl; + */ + }; + + /** + * marks as dirty the canvas, this way it will be rendered again + * + * @class LGraphCanvas + * @method setDirty + * @param {bool} fgcanvas if the foreground canvas is dirty (the one containing the nodes) + * @param {bool} bgcanvas if the background canvas is dirty (the one containing the wires) + */ + LGraphCanvas.prototype.setDirty = function(fgcanvas, bgcanvas) { + if (fgcanvas) { + this.dirty_canvas = true; + } + if (bgcanvas) { + this.dirty_bgcanvas = true; + } + }; + + /** + * Used to attach the canvas in a popup + * + * @method getCanvasWindow + * @return {window} returns the window where the canvas is attached (the DOM root node) + */ + LGraphCanvas.prototype.getCanvasWindow = function() { + if (!this.canvas) { + return window; + } + var doc = this.canvas.ownerDocument; + return doc.defaultView || doc.parentWindow; + }; + + /** + * starts rendering the content of the canvas when needed + * + * @method startRendering + */ + LGraphCanvas.prototype.startRendering = function() { + if (this.is_rendering) { + return; + } //already rendering + + this.is_rendering = true; + renderFrame.call(this); + + function renderFrame() { + if (!this.pause_rendering) { + this.draw(); + } + + var window = this.getCanvasWindow(); + if (this.is_rendering) { + window.requestAnimationFrame(renderFrame.bind(this)); + } + } + }; + + /** + * stops rendering the content of the canvas (to save resources) + * + * @method stopRendering + */ + LGraphCanvas.prototype.stopRendering = function() { + this.is_rendering = false; + /* + if(this.rendering_timer_id) + { + clearInterval(this.rendering_timer_id); + this.rendering_timer_id = null; + } + */ + }; + + /* LiteGraphCanvas input */ + + LGraphCanvas.prototype.processMouseDown = function(e) { + if (!this.graph) { + return; + } + + this.adjustMouseEvent(e); + + var ref_window = this.getCanvasWindow(); + var document = ref_window.document; + LGraphCanvas.active_canvas = this; + var that = this; + + //move mouse move event to the window in case it drags outside of the canvas + this.canvas.removeEventListener("mousemove", this._mousemove_callback); + ref_window.document.addEventListener( + "mousemove", + this._mousemove_callback, + true + ); //catch for the entire window + ref_window.document.addEventListener( + "mouseup", + this._mouseup_callback, + true + ); + + var node = this.graph.getNodeOnPos( + e.canvasX, + e.canvasY, + this.visible_nodes, + 5 + ); + var skip_dragging = false; + var skip_action = false; + var now = LiteGraph.getTime(); + var is_double_click = now - this.last_mouseclick < 300; + + this.canvas_mouse[0] = e.canvasX; + this.canvas_mouse[1] = e.canvasY; + this.canvas.focus(); + + LiteGraph.closeAllContextMenus(ref_window); + + if (this.onMouse) { + if (this.onMouse(e) == true) { + return; + } + } + + if (e.which == 1) { + //left button mouse + if (e.ctrlKey) { + this.dragging_rectangle = new Float32Array(4); + this.dragging_rectangle[0] = e.canvasX; + this.dragging_rectangle[1] = e.canvasY; + this.dragging_rectangle[2] = 1; + this.dragging_rectangle[3] = 1; + skip_action = true; + } + + var clicking_canvas_bg = false; + + //when clicked on top of a node + //and it is not interactive + if (node && this.allow_interaction && !skip_action && !this.read_only) { + if (!this.live_mode && !node.flags.pinned) { + this.bringToFront(node); + } //if it wasn't selected? + + //not dragging mouse to connect two slots + if ( + !this.connecting_node && + !node.flags.collapsed && + !this.live_mode + ) { + //Search for corner for resize + if ( + !skip_action && + node.resizable !== false && + isInsideRectangle( + e.canvasX, + e.canvasY, + node.pos[0] + node.size[0] - 5, + node.pos[1] + node.size[1] - 5, + 10, + 10 + ) + ) { + this.resizing_node = node; + this.canvas.style.cursor = "se-resize"; + skip_action = true; + } else { + //search for outputs + if (node.outputs) { + for ( + var i = 0, l = node.outputs.length; + i < l; + ++i + ) { + var output = node.outputs[i]; + var link_pos = node.getConnectionPos(false, i); + if ( + isInsideRectangle( + e.canvasX, + e.canvasY, + link_pos[0] - 15, + link_pos[1] - 10, + 30, + 20 + ) + ) { + this.connecting_node = node; + this.connecting_output = output; + this.connecting_pos = node.getConnectionPos( false, i ); + this.connecting_slot = i; + + if (e.shiftKey) { + node.disconnectOutput(i); + } + + if (is_double_click) { + if (node.onOutputDblClick) { + node.onOutputDblClick(i, e); + } + } else { + if (node.onOutputClick) { + node.onOutputClick(i, e); + } + } + + skip_action = true; + break; + } + } + } + + //search for inputs + if (node.inputs) { + for ( + var i = 0, l = node.inputs.length; + i < l; + ++i + ) { + var input = node.inputs[i]; + var link_pos = node.getConnectionPos(true, i); + if ( + isInsideRectangle( + e.canvasX, + e.canvasY, + link_pos[0] - 15, + link_pos[1] - 10, + 30, + 20 + ) + ) { + if (is_double_click) { + if (node.onInputDblClick) { + node.onInputDblClick(i, e); + } + } else { + if (node.onInputClick) { + node.onInputClick(i, e); + } + } + + if (input.link !== null) { + var link_info = this.graph.links[ + input.link + ]; //before disconnecting + node.disconnectInput(i); + + if ( + this.allow_reconnect_links || + e.shiftKey + ) { + this.connecting_node = this.graph._nodes_by_id[ + link_info.origin_id + ]; + this.connecting_slot = + link_info.origin_slot; + this.connecting_output = this.connecting_node.outputs[ + this.connecting_slot + ]; + this.connecting_pos = this.connecting_node.getConnectionPos( false, this.connecting_slot ); + } + + this.dirty_bgcanvas = true; + skip_action = true; + } + } + } + } + } //not resizing + } + + //Search for corner for collapsing + /* + if( !skip_action && isInsideRectangle( e.canvasX, e.canvasY, node.pos[0], node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT )) + { + node.collapse(); + skip_action = true; + } + */ + + //it wasn't clicked on the links boxes + if (!skip_action) { + var block_drag_node = false; + + //widgets + var widget = this.processNodeWidgets( node, this.canvas_mouse, e ); + if (widget) { + block_drag_node = true; + this.node_widget = [node, widget]; + } + + //double clicking + if (is_double_click && this.selected_nodes[node.id]) { + //double click node + if (node.onDblClick) { + node.onDblClick( e, [ e.canvasX - node.pos[0], e.canvasY - node.pos[1] ], this ); + } + this.processNodeDblClicked(node); + block_drag_node = true; + } + + //if do not capture mouse + if ( node.onMouseDown && node.onMouseDown( e, [e.canvasX - node.pos[0], e.canvasY - node.pos[1]], this ) ) { + block_drag_node = true; + } else if (this.live_mode) { + clicking_canvas_bg = true; + block_drag_node = true; + } + + if (!block_drag_node) { + if (this.allow_dragnodes) { + this.node_dragged = node; + } + if (!this.selected_nodes[node.id]) { + this.processNodeSelected(node, e); + } + } + + this.dirty_canvas = true; + } + } //clicked outside of nodes + else { + //search for link connector + if(!this.read_only) + for (var i = 0; i < this.visible_links.length; ++i) { + var link = this.visible_links[i]; + var center = link._pos; + if ( + !center || + e.canvasX < center[0] - 4 || + e.canvasX > center[0] + 4 || + e.canvasY < center[1] - 4 || + e.canvasY > center[1] + 4 + ) { + continue; + } + //link clicked + this.showLinkMenu(link, e); + this.over_link_center = null; //clear tooltip + break; + } + + this.selected_group = this.graph.getGroupOnPos( e.canvasX, e.canvasY ); + this.selected_group_resizing = false; + if (this.selected_group && !this.read_only ) { + if (e.ctrlKey) { + this.dragging_rectangle = null; + } + + var dist = distance( [e.canvasX, e.canvasY], [ this.selected_group.pos[0] + this.selected_group.size[0], this.selected_group.pos[1] + this.selected_group.size[1] ] ); + if (dist * this.ds.scale < 10) { + this.selected_group_resizing = true; + } else { + this.selected_group.recomputeInsideNodes(); + } + } + + if (is_double_click && !this.read_only && this.allow_searchbox) { + this.showSearchBox(e); + } + + clicking_canvas_bg = true; + } + + if (!skip_action && clicking_canvas_bg && this.allow_dragcanvas) { + this.dragging_canvas = true; + } + } else if (e.which == 2) { + //middle button + } else if (e.which == 3) { + //right button + if(!this.read_only) + this.processContextMenu(node, e); + } + + //TODO + //if(this.node_selected != prev_selected) + // this.onNodeSelectionChange(this.node_selected); + + this.last_mouse[0] = e.localX; + this.last_mouse[1] = e.localY; + this.last_mouseclick = LiteGraph.getTime(); + this.last_mouse_dragging = true; + + /* + if( (this.dirty_canvas || this.dirty_bgcanvas) && this.rendering_timer_id == null) + this.draw(); + */ + + this.graph.change(); + + //this is to ensure to defocus(blur) if a text input element is on focus + if ( + !ref_window.document.activeElement || + (ref_window.document.activeElement.nodeName.toLowerCase() != + "input" && + ref_window.document.activeElement.nodeName.toLowerCase() != + "textarea") + ) { + e.preventDefault(); + } + e.stopPropagation(); + + if (this.onMouseDown) { + this.onMouseDown(e); + } + + return false; + }; + + /** + * Called when a mouse move event has to be processed + * @method processMouseMove + **/ + LGraphCanvas.prototype.processMouseMove = function(e) { + if (this.autoresize) { + this.resize(); + } + + if (!this.graph) { + return; + } + + LGraphCanvas.active_canvas = this; + this.adjustMouseEvent(e); + var mouse = [e.localX, e.localY]; + var delta = [ + mouse[0] - this.last_mouse[0], + mouse[1] - this.last_mouse[1] + ]; + this.last_mouse = mouse; + this.canvas_mouse[0] = e.canvasX; + this.canvas_mouse[1] = e.canvasY; + e.dragging = this.last_mouse_dragging; + + if (this.node_widget) { + this.processNodeWidgets( + this.node_widget[0], + this.canvas_mouse, + e, + this.node_widget[1] + ); + this.dirty_canvas = true; + } + + if (this.dragging_rectangle) { + this.dragging_rectangle[2] = e.canvasX - this.dragging_rectangle[0]; + this.dragging_rectangle[3] = e.canvasY - this.dragging_rectangle[1]; + this.dirty_canvas = true; + } else if (this.selected_group && !this.read_only) { + //moving/resizing a group + if (this.selected_group_resizing) { + this.selected_group.size = [ + e.canvasX - this.selected_group.pos[0], + e.canvasY - this.selected_group.pos[1] + ]; + } else { + var deltax = delta[0] / this.ds.scale; + var deltay = delta[1] / this.ds.scale; + this.selected_group.move(deltax, deltay, e.ctrlKey); + if (this.selected_group._nodes.length) { + this.dirty_canvas = true; + } + } + this.dirty_bgcanvas = true; + } else if (this.dragging_canvas) { + this.ds.offset[0] += delta[0] / this.ds.scale; + this.ds.offset[1] += delta[1] / this.ds.scale; + this.dirty_canvas = true; + this.dirty_bgcanvas = true; + } else if (this.allow_interaction && !this.read_only) { + if (this.connecting_node) { + this.dirty_canvas = true; + } + + //get node over + var node = this.graph.getNodeOnPos( + e.canvasX, + e.canvasY, + this.visible_nodes + ); + + //remove mouseover flag + for (var i = 0, l = this.graph._nodes.length; i < l; ++i) { + if ( + this.graph._nodes[i].mouseOver && + node != this.graph._nodes[i] + ) { + //mouse leave + this.graph._nodes[i].mouseOver = false; + if (this.node_over && this.node_over.onMouseLeave) { + this.node_over.onMouseLeave(e); + } + this.node_over = null; + this.dirty_canvas = true; + } + } + + //mouse over a node + if (node) { + //this.canvas.style.cursor = "move"; + if (!node.mouseOver) { + //mouse enter + node.mouseOver = true; + this.node_over = node; + this.dirty_canvas = true; + + if (node.onMouseEnter) { + node.onMouseEnter(e); + } + } + + //in case the node wants to do something + if (node.onMouseMove) { + node.onMouseMove( + e, + [e.canvasX - node.pos[0], e.canvasY - node.pos[1]], + this + ); + } + + //if dragging a link + if (this.connecting_node) { + var pos = this._highlight_input || [0, 0]; //to store the output of isOverNodeInput + + //on top of input + if (this.isOverNodeBox(node, e.canvasX, e.canvasY)) { + //mouse on top of the corner box, don't know what to do + } else { + //check if I have a slot below de mouse + var slot = this.isOverNodeInput( + node, + e.canvasX, + e.canvasY, + pos + ); + if (slot != -1 && node.inputs[slot]) { + var slot_type = node.inputs[slot].type; + if ( + LiteGraph.isValidConnection( + this.connecting_output.type, + slot_type + ) + ) { + this._highlight_input = pos; + } + } else { + this._highlight_input = null; + } + } + } + + //Search for corner + if (this.canvas) { + if ( + isInsideRectangle( + e.canvasX, + e.canvasY, + node.pos[0] + node.size[0] - 5, + node.pos[1] + node.size[1] - 5, + 5, + 5 + ) + ) { + this.canvas.style.cursor = "se-resize"; + } else { + this.canvas.style.cursor = "crosshair"; + } + } + } else { //outside + + //search for link connector + var over_link = null; + for (var i = 0; i < this.visible_links.length; ++i) { + var link = this.visible_links[i]; + var center = link._pos; + if ( + !center || + e.canvasX < center[0] - 4 || + e.canvasX > center[0] + 4 || + e.canvasY < center[1] - 4 || + e.canvasY > center[1] + 4 + ) { + continue; + } + over_link = link; + break; + } + if( over_link != this.over_link_center ) + { + this.over_link_center = over_link; + this.dirty_canvas = true; + } + + if (this.canvas) { + this.canvas.style.cursor = ""; + } + } + + if ( this.node_capturing_input && this.node_capturing_input != node && this.node_capturing_input.onMouseMove ) { + this.node_capturing_input.onMouseMove(e,[e.canvasX - this.node_capturing_input.pos[0],e.canvasY - this.node_capturing_input.pos[1]], this); + } + + if (this.node_dragged && !this.live_mode) { + for (var i in this.selected_nodes) { + var n = this.selected_nodes[i]; + n.pos[0] += delta[0] / this.ds.scale; + n.pos[1] += delta[1] / this.ds.scale; + } + + this.dirty_canvas = true; + this.dirty_bgcanvas = true; + } + + if (this.resizing_node && !this.live_mode) { + //convert mouse to node space + this.resizing_node.size[0] = e.canvasX - this.resizing_node.pos[0]; + this.resizing_node.size[1] = e.canvasY - this.resizing_node.pos[1]; + + //constraint size + var max_slots = Math.max( + this.resizing_node.inputs ? this.resizing_node.inputs.length : 0, + this.resizing_node.outputs ? this.resizing_node.outputs.length : 0 + ); + + if (this.resizing_node.size[0] < LiteGraph.NODE_MIN_WIDTH) { + this.resizing_node.size[0] = LiteGraph.NODE_MIN_WIDTH; + } + + var widgets = this.resizing_node.widgets; + var widgets_height = 0; + if (widgets && widgets.length) { + for (var i = 0, l = widgets.length; i < l; ++i) { + if (widgets[i].computeSize) + widgets_height += widgets[i].computeSize(this.resizing_node.size[0])[1] + 4; + else + widgets_height += LiteGraph.NODE_WIDGET_HEIGHT + 4; + } + widgets_height += 8; + } + + var min_height = max_slots * LiteGraph.NODE_SLOT_HEIGHT + widgets_height; + if (this.resizing_node.size[1] < min_height) { + this.resizing_node.size[1] = min_height; + } + + this.canvas.style.cursor = "se-resize"; + this.dirty_canvas = true; + this.dirty_bgcanvas = true; + } + } + + e.preventDefault(); + return false; + }; + + /** + * Called when a mouse up event has to be processed + * @method processMouseUp + **/ + LGraphCanvas.prototype.processMouseUp = function(e) { + if (!this.graph) { + return; + } + + var window = this.getCanvasWindow(); + var document = window.document; + LGraphCanvas.active_canvas = this; + + //restore the mousemove event back to the canvas + document.removeEventListener("mousemove",this._mousemove_callback,true); + this.canvas.addEventListener("mousemove",this._mousemove_callback,true); + document.removeEventListener("mouseup", this._mouseup_callback, true); + + this.adjustMouseEvent(e); + var now = LiteGraph.getTime(); + e.click_time = now - this.last_mouseclick; + this.last_mouse_dragging = false; + + if (e.which == 1) { + + if( this.node_widget ) + { + this.processNodeWidgets( this.node_widget[0], this.canvas_mouse, e ); + } + + //left button + this.node_widget = null; + + if (this.selected_group) { + var diffx = + this.selected_group.pos[0] - + Math.round(this.selected_group.pos[0]); + var diffy = + this.selected_group.pos[1] - + Math.round(this.selected_group.pos[1]); + this.selected_group.move(diffx, diffy, e.ctrlKey); + this.selected_group.pos[0] = Math.round( + this.selected_group.pos[0] + ); + this.selected_group.pos[1] = Math.round( + this.selected_group.pos[1] + ); + if (this.selected_group._nodes.length) { + this.dirty_canvas = true; + } + this.selected_group = null; + } + this.selected_group_resizing = false; + + if (this.dragging_rectangle) { + if (this.graph) { + var nodes = this.graph._nodes; + var node_bounding = new Float32Array(4); + this.deselectAllNodes(); + //compute bounding and flip if left to right + var w = Math.abs(this.dragging_rectangle[2]); + var h = Math.abs(this.dragging_rectangle[3]); + var startx = + this.dragging_rectangle[2] < 0 + ? this.dragging_rectangle[0] - w + : this.dragging_rectangle[0]; + var starty = + this.dragging_rectangle[3] < 0 + ? this.dragging_rectangle[1] - h + : this.dragging_rectangle[1]; + this.dragging_rectangle[0] = startx; + this.dragging_rectangle[1] = starty; + this.dragging_rectangle[2] = w; + this.dragging_rectangle[3] = h; + + //test against all nodes (not visible because the rectangle maybe start outside + var to_select = []; + for (var i = 0; i < nodes.length; ++i) { + var node = nodes[i]; + node.getBounding(node_bounding); + if ( + !overlapBounding( + this.dragging_rectangle, + node_bounding + ) + ) { + continue; + } //out of the visible area + to_select.push(node); + } + if (to_select.length) { + this.selectNodes(to_select); + } + } + this.dragging_rectangle = null; + } else if (this.connecting_node) { + //dragging a connection + this.dirty_canvas = true; + this.dirty_bgcanvas = true; + + var node = this.graph.getNodeOnPos( + e.canvasX, + e.canvasY, + this.visible_nodes + ); + + //node below mouse + if (node) { + if ( + this.connecting_output.type == LiteGraph.EVENT && + this.isOverNodeBox(node, e.canvasX, e.canvasY) + ) { + this.connecting_node.connect( + this.connecting_slot, + node, + LiteGraph.EVENT + ); + } else { + //slot below mouse? connect + var slot = this.isOverNodeInput( + node, + e.canvasX, + e.canvasY + ); + if (slot != -1) { + this.connecting_node.connect( + this.connecting_slot, + node, + slot + ); + } else { + //not on top of an input + var input = node.getInputInfo(0); + //auto connect + if ( + this.connecting_output.type == LiteGraph.EVENT + ) { + this.connecting_node.connect( + this.connecting_slot, + node, + LiteGraph.EVENT + ); + } else if ( + input && + !input.link && + LiteGraph.isValidConnection( + input.type && this.connecting_output.type + ) + ) { + this.connecting_node.connect( + this.connecting_slot, + node, + 0 + ); + } + } + } + } + + this.connecting_output = null; + this.connecting_pos = null; + this.connecting_node = null; + this.connecting_slot = -1; + } //not dragging connection + else if (this.resizing_node) { + this.dirty_canvas = true; + this.dirty_bgcanvas = true; + this.resizing_node = null; + } else if (this.node_dragged) { + //node being dragged? + var node = this.node_dragged; + if ( + node && + e.click_time < 300 && + isInsideRectangle( e.canvasX, e.canvasY, node.pos[0], node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT ) + ) { + node.collapse(); + } + + this.dirty_canvas = true; + this.dirty_bgcanvas = true; + this.node_dragged.pos[0] = Math.round(this.node_dragged.pos[0]); + this.node_dragged.pos[1] = Math.round(this.node_dragged.pos[1]); + if (this.graph.config.align_to_grid) { + this.node_dragged.alignToGrid(); + } + if( this.onNodeMoved ) + this.onNodeMoved( this.node_dragged ); + this.node_dragged = null; + } //no node being dragged + else { + //get node over + var node = this.graph.getNodeOnPos( + e.canvasX, + e.canvasY, + this.visible_nodes + ); + + if (!node && e.click_time < 300) { + this.deselectAllNodes(); + } + + this.dirty_canvas = true; + this.dragging_canvas = false; + + if (this.node_over && this.node_over.onMouseUp) { + this.node_over.onMouseUp( e, [ e.canvasX - this.node_over.pos[0], e.canvasY - this.node_over.pos[1] ], this ); + } + if ( + this.node_capturing_input && + this.node_capturing_input.onMouseUp + ) { + this.node_capturing_input.onMouseUp(e, [ + e.canvasX - this.node_capturing_input.pos[0], + e.canvasY - this.node_capturing_input.pos[1] + ]); + } + } + } else if (e.which == 2) { + //middle button + //trace("middle"); + this.dirty_canvas = true; + this.dragging_canvas = false; + } else if (e.which == 3) { + //right button + //trace("right"); + this.dirty_canvas = true; + this.dragging_canvas = false; + } + + /* + if((this.dirty_canvas || this.dirty_bgcanvas) && this.rendering_timer_id == null) + this.draw(); + */ + + this.graph.change(); + + e.stopPropagation(); + e.preventDefault(); + return false; + }; + + /** + * Called when a mouse wheel event has to be processed + * @method processMouseWheel + **/ + LGraphCanvas.prototype.processMouseWheel = function(e) { + if (!this.graph || !this.allow_dragcanvas) { + return; + } + + var delta = e.wheelDeltaY != null ? e.wheelDeltaY : e.detail * -60; + + this.adjustMouseEvent(e); + + var scale = this.ds.scale; + + if (delta > 0) { + scale *= 1.1; + } else if (delta < 0) { + scale *= 1 / 1.1; + } + + //this.setZoom( scale, [ e.localX, e.localY ] ); + this.ds.changeScale(scale, [e.localX, e.localY]); + + this.graph.change(); + + e.preventDefault(); + return false; // prevent default + }; + + /** + * returns true if a position (in graph space) is on top of a node little corner box + * @method isOverNodeBox + **/ + LGraphCanvas.prototype.isOverNodeBox = function(node, canvasx, canvasy) { + var title_height = LiteGraph.NODE_TITLE_HEIGHT; + if ( + isInsideRectangle( + canvasx, + canvasy, + node.pos[0] + 2, + node.pos[1] + 2 - title_height, + title_height - 4, + title_height - 4 + ) + ) { + return true; + } + return false; + }; + + /** + * returns true if a position (in graph space) is on top of a node input slot + * @method isOverNodeInput + **/ + LGraphCanvas.prototype.isOverNodeInput = function( + node, + canvasx, + canvasy, + slot_pos + ) { + if (node.inputs) { + for (var i = 0, l = node.inputs.length; i < l; ++i) { + var input = node.inputs[i]; + var link_pos = node.getConnectionPos(true, i); + var is_inside = false; + if (node.horizontal) { + is_inside = isInsideRectangle( + canvasx, + canvasy, + link_pos[0] - 5, + link_pos[1] - 10, + 10, + 20 + ); + } else { + is_inside = isInsideRectangle( + canvasx, + canvasy, + link_pos[0] - 10, + link_pos[1] - 5, + 40, + 10 + ); + } + if (is_inside) { + if (slot_pos) { + slot_pos[0] = link_pos[0]; + slot_pos[1] = link_pos[1]; + } + return i; + } + } + } + return -1; + }; + + /** + * process a key event + * @method processKey + **/ + LGraphCanvas.prototype.processKey = function(e) { + if (!this.graph) { + return; + } + + var block_default = false; + //console.log(e); //debug + + if (e.target.localName == "input") { + return; + } + + if (e.type == "keydown") { + if (e.keyCode == 32) { + //esc + this.dragging_canvas = true; + block_default = true; + } + + //select all Control A + if (e.keyCode == 65 && e.ctrlKey) { + this.selectNodes(); + block_default = true; + } + + if (e.code == "KeyC" && (e.metaKey || e.ctrlKey) && !e.shiftKey) { + //copy + if (this.selected_nodes) { + this.copyToClipboard(); + block_default = true; + } + } + + if (e.code == "KeyV" && (e.metaKey || e.ctrlKey) && !e.shiftKey) { + //paste + this.pasteFromClipboard(); + } + + //delete or backspace + if (e.keyCode == 46 || e.keyCode == 8) { + if ( + e.target.localName != "input" && + e.target.localName != "textarea" + ) { + this.deleteSelectedNodes(); + block_default = true; + } + } + + //collapse + //... + + //TODO + if (this.selected_nodes) { + for (var i in this.selected_nodes) { + if (this.selected_nodes[i].onKeyDown) { + this.selected_nodes[i].onKeyDown(e); + } + } + } + } else if (e.type == "keyup") { + if (e.keyCode == 32) { + this.dragging_canvas = false; + } + + if (this.selected_nodes) { + for (var i in this.selected_nodes) { + if (this.selected_nodes[i].onKeyUp) { + this.selected_nodes[i].onKeyUp(e); + } + } + } + } + + this.graph.change(); + + if (block_default) { + e.preventDefault(); + e.stopImmediatePropagation(); + return false; + } + }; + + LGraphCanvas.prototype.copyToClipboard = function() { + var clipboard_info = { + nodes: [], + links: [] + }; + var index = 0; + var selected_nodes_array = []; + for (var i in this.selected_nodes) { + var node = this.selected_nodes[i]; + node._relative_id = index; + selected_nodes_array.push(node); + index += 1; + } + + for (var i = 0; i < selected_nodes_array.length; ++i) { + var node = selected_nodes_array[i]; + var cloned = node.clone(); + if(!cloned) + { + console.warn("node type not found: " + node.type ); + continue; + } + clipboard_info.nodes.push(cloned.serialize()); + if (node.inputs && node.inputs.length) { + for (var j = 0; j < node.inputs.length; ++j) { + var input = node.inputs[j]; + if (!input || input.link == null) { + continue; + } + var link_info = this.graph.links[input.link]; + if (!link_info) { + continue; + } + var target_node = this.graph.getNodeById( + link_info.origin_id + ); + if (!target_node || !this.selected_nodes[target_node.id]) { + //improve this by allowing connections to non-selected nodes + continue; + } //not selected + clipboard_info.links.push([ + target_node._relative_id, + link_info.origin_slot, //j, + node._relative_id, + link_info.target_slot + ]); + } + } + } + localStorage.setItem( + "litegrapheditor_clipboard", + JSON.stringify(clipboard_info) + ); + }; + + LGraphCanvas.prototype.pasteFromClipboard = function() { + var data = localStorage.getItem("litegrapheditor_clipboard"); + if (!data) { + return; + } + + //create nodes + var clipboard_info = JSON.parse(data); + var nodes = []; + for (var i = 0; i < clipboard_info.nodes.length; ++i) { + var node_data = clipboard_info.nodes[i]; + var node = LiteGraph.createNode(node_data.type); + if (node) { + node.configure(node_data); + node.pos[0] += 5; + node.pos[1] += 5; + this.graph.add(node); + nodes.push(node); + } + } + + //create links + for (var i = 0; i < clipboard_info.links.length; ++i) { + var link_info = clipboard_info.links[i]; + var origin_node = nodes[link_info[0]]; + var target_node = nodes[link_info[2]]; + if( origin_node && target_node ) + origin_node.connect(link_info[1], target_node, link_info[3]); + else + console.warn("Warning, nodes missing on pasting"); + } + + this.selectNodes(nodes); + }; + + /** + * process a item drop event on top the canvas + * @method processDrop + **/ + LGraphCanvas.prototype.processDrop = function(e) { + e.preventDefault(); + this.adjustMouseEvent(e); + + var pos = [e.canvasX, e.canvasY]; + var node = this.graph.getNodeOnPos(pos[0], pos[1]); + + if (!node) { + var r = null; + if (this.onDropItem) { + r = this.onDropItem(event); + } + if (!r) { + this.checkDropItem(e); + } + return; + } + + if (node.onDropFile || node.onDropData) { + var files = e.dataTransfer.files; + if (files && files.length) { + for (var i = 0; i < files.length; i++) { + var file = e.dataTransfer.files[0]; + var filename = file.name; + var ext = LGraphCanvas.getFileExtension(filename); + //console.log(file); + + if (node.onDropFile) { + node.onDropFile(file); + } + + if (node.onDropData) { + //prepare reader + var reader = new FileReader(); + reader.onload = function(event) { + //console.log(event.target); + var data = event.target.result; + node.onDropData(data, filename, file); + }; + + //read data + var type = file.type.split("/")[0]; + if (type == "text" || type == "") { + reader.readAsText(file); + } else if (type == "image") { + reader.readAsDataURL(file); + } else { + reader.readAsArrayBuffer(file); + } + } + } + } + } + + if (node.onDropItem) { + if (node.onDropItem(event)) { + return true; + } + } + + if (this.onDropItem) { + return this.onDropItem(event); + } + + return false; + }; + + //called if the graph doesn't have a default drop item behaviour + LGraphCanvas.prototype.checkDropItem = function(e) { + if (e.dataTransfer.files.length) { + var file = e.dataTransfer.files[0]; + var ext = LGraphCanvas.getFileExtension(file.name).toLowerCase(); + var nodetype = LiteGraph.node_types_by_file_extension[ext]; + if (nodetype) { + var node = LiteGraph.createNode(nodetype.type); + node.pos = [e.canvasX, e.canvasY]; + this.graph.add(node); + if (node.onDropFile) { + node.onDropFile(file); + } + } + } + }; + + LGraphCanvas.prototype.processNodeDblClicked = function(n) { + if (this.onShowNodePanel) { + this.onShowNodePanel(n); + } + + if (this.onNodeDblClicked) { + this.onNodeDblClicked(n); + } + + this.setDirty(true); + }; + + LGraphCanvas.prototype.processNodeSelected = function(node, e) { + this.selectNode(node, e && e.shiftKey); + if (this.onNodeSelected) { + this.onNodeSelected(node); + } + }; + + /** + * selects a given node (or adds it to the current selection) + * @method selectNode + **/ + LGraphCanvas.prototype.selectNode = function( + node, + add_to_current_selection + ) { + if (node == null) { + this.deselectAllNodes(); + } else { + this.selectNodes([node], add_to_current_selection); + } + }; + + /** + * selects several nodes (or adds them to the current selection) + * @method selectNodes + **/ + LGraphCanvas.prototype.selectNodes = function( + nodes, + add_to_current_selection + ) { + if (!add_to_current_selection) { + this.deselectAllNodes(); + } + + nodes = nodes || this.graph._nodes; + for (var i = 0; i < nodes.length; ++i) { + var node = nodes[i]; + if (node.is_selected) { + continue; + } + + if (!node.is_selected && node.onSelected) { + node.onSelected(); + } + node.is_selected = true; + this.selected_nodes[node.id] = node; + + if (node.inputs) { + for (var j = 0; j < node.inputs.length; ++j) { + this.highlighted_links[node.inputs[j].link] = true; + } + } + if (node.outputs) { + for (var j = 0; j < node.outputs.length; ++j) { + var out = node.outputs[j]; + if (out.links) { + for (var k = 0; k < out.links.length; ++k) { + this.highlighted_links[out.links[k]] = true; + } + } + } + } + } + + if( this.onSelectionChange ) + this.onSelectionChange( this.selected_nodes ); + + this.setDirty(true); + }; + + /** + * removes a node from the current selection + * @method deselectNode + **/ + LGraphCanvas.prototype.deselectNode = function(node) { + if (!node.is_selected) { + return; + } + if (node.onDeselected) { + node.onDeselected(); + } + node.is_selected = false; + + if (this.onNodeDeselected) { + this.onNodeDeselected(node); + } + + //remove highlighted + if (node.inputs) { + for (var i = 0; i < node.inputs.length; ++i) { + delete this.highlighted_links[node.inputs[i].link]; + } + } + if (node.outputs) { + for (var i = 0; i < node.outputs.length; ++i) { + var out = node.outputs[i]; + if (out.links) { + for (var j = 0; j < out.links.length; ++j) { + delete this.highlighted_links[out.links[j]]; + } + } + } + } + }; + + /** + * removes all nodes from the current selection + * @method deselectAllNodes + **/ + LGraphCanvas.prototype.deselectAllNodes = function() { + if (!this.graph) { + return; + } + var nodes = this.graph._nodes; + for (var i = 0, l = nodes.length; i < l; ++i) { + var node = nodes[i]; + if (!node.is_selected) { + continue; + } + if (node.onDeselected) { + node.onDeselected(); + } + node.is_selected = false; + if (this.onNodeDeselected) { + this.onNodeDeselected(node); + } + } + this.selected_nodes = {}; + this.current_node = null; + this.highlighted_links = {}; + if( this.onSelectionChange ) + this.onSelectionChange( this.selected_nodes ); + this.setDirty(true); + }; + + /** + * deletes all nodes in the current selection from the graph + * @method deleteSelectedNodes + **/ + LGraphCanvas.prototype.deleteSelectedNodes = function() { + for (var i in this.selected_nodes) { + var node = this.selected_nodes[i]; + //autoconnect when possible (very basic, only takes into account first input-output) + if(node.inputs && node.inputs.length && node.outputs && node.outputs.length && LiteGraph.isValidConnection( node.inputs[0].type, node.outputs[0].type ) && node.inputs[0].link && node.outputs[0].links && node.outputs[0].links.length ) + { + var input_link = node.graph.links[ node.inputs[0].link ]; + var output_link = node.graph.links[ node.outputs[0].links[0] ]; + var input_node = node.getInputNode(0); + var output_node = node.getOutputNodes(0)[0]; + if(input_node && output_node) + input_node.connect( input_link.origin_slot, output_node, output_link.target_slot ); + } + this.graph.remove(node); + if (this.onNodeDeselected) { + this.onNodeDeselected(node); + } + } + this.selected_nodes = {}; + this.current_node = null; + this.highlighted_links = {}; + this.setDirty(true); + }; + + /** + * centers the camera on a given node + * @method centerOnNode + **/ + LGraphCanvas.prototype.centerOnNode = function(node) { + this.ds.offset[0] = + -node.pos[0] - + node.size[0] * 0.5 + + (this.canvas.width * 0.5) / this.ds.scale; + this.ds.offset[1] = + -node.pos[1] - + node.size[1] * 0.5 + + (this.canvas.height * 0.5) / this.ds.scale; + this.setDirty(true, true); + }; + + /** + * adds some useful properties to a mouse event, like the position in graph coordinates + * @method adjustMouseEvent + **/ + LGraphCanvas.prototype.adjustMouseEvent = function(e) { + if (this.canvas) { + var b = this.canvas.getBoundingClientRect(); + e.localX = e.clientX - b.left; + e.localY = e.clientY - b.top; + } else { + e.localX = e.clientX; + e.localY = e.clientY; + } + + e.deltaX = e.localX - this.last_mouse_position[0]; + e.deltaY = e.localY - this.last_mouse_position[1]; + + this.last_mouse_position[0] = e.localX; + this.last_mouse_position[1] = e.localY; + + e.canvasX = e.localX / this.ds.scale - this.ds.offset[0]; + e.canvasY = e.localY / this.ds.scale - this.ds.offset[1]; + }; + + /** + * changes the zoom level of the graph (default is 1), you can pass also a place used to pivot the zoom + * @method setZoom + **/ + LGraphCanvas.prototype.setZoom = function(value, zooming_center) { + this.ds.changeScale(value, zooming_center); + /* + if(!zooming_center && this.canvas) + zooming_center = [this.canvas.width * 0.5,this.canvas.height * 0.5]; + + var center = this.convertOffsetToCanvas( zooming_center ); + + this.ds.scale = value; + + if(this.scale > this.max_zoom) + this.scale = this.max_zoom; + else if(this.scale < this.min_zoom) + this.scale = this.min_zoom; + + var new_center = this.convertOffsetToCanvas( zooming_center ); + var delta_offset = [new_center[0] - center[0], new_center[1] - center[1]]; + + this.offset[0] += delta_offset[0]; + this.offset[1] += delta_offset[1]; + */ + + this.dirty_canvas = true; + this.dirty_bgcanvas = true; + }; + + /** + * converts a coordinate from graph coordinates to canvas2D coordinates + * @method convertOffsetToCanvas + **/ + LGraphCanvas.prototype.convertOffsetToCanvas = function(pos, out) { + return this.ds.convertOffsetToCanvas(pos, out); + }; + + /** + * converts a coordinate from Canvas2D coordinates to graph space + * @method convertCanvasToOffset + **/ + LGraphCanvas.prototype.convertCanvasToOffset = function(pos, out) { + return this.ds.convertCanvasToOffset(pos, out); + }; + + //converts event coordinates from canvas2D to graph coordinates + LGraphCanvas.prototype.convertEventToCanvasOffset = function(e) { + var rect = this.canvas.getBoundingClientRect(); + return this.convertCanvasToOffset([ + e.clientX - rect.left, + e.clientY - rect.top + ]); + }; + + /** + * brings a node to front (above all other nodes) + * @method bringToFront + **/ + LGraphCanvas.prototype.bringToFront = function(node) { + var i = this.graph._nodes.indexOf(node); + if (i == -1) { + return; + } + + this.graph._nodes.splice(i, 1); + this.graph._nodes.push(node); + }; + + /** + * sends a node to the back (below all other nodes) + * @method sendToBack + **/ + LGraphCanvas.prototype.sendToBack = function(node) { + var i = this.graph._nodes.indexOf(node); + if (i == -1) { + return; + } + + this.graph._nodes.splice(i, 1); + this.graph._nodes.unshift(node); + }; + + /* Interaction */ + + /* LGraphCanvas render */ + var temp = new Float32Array(4); + + /** + * checks which nodes are visible (inside the camera area) + * @method computeVisibleNodes + **/ + LGraphCanvas.prototype.computeVisibleNodes = function(nodes, out) { + var visible_nodes = out || []; + visible_nodes.length = 0; + nodes = nodes || this.graph._nodes; + for (var i = 0, l = nodes.length; i < l; ++i) { + var n = nodes[i]; + + //skip rendering nodes in live mode + if (this.live_mode && !n.onDrawBackground && !n.onDrawForeground) { + continue; + } + + if (!overlapBounding(this.visible_area, n.getBounding(temp))) { + continue; + } //out of the visible area + + visible_nodes.push(n); + } + return visible_nodes; + }; + + /** + * renders the whole canvas content, by rendering in two separated canvas, one containing the background grid and the connections, and one containing the nodes) + * @method draw + **/ + LGraphCanvas.prototype.draw = function(force_canvas, force_bgcanvas) { + if (!this.canvas) { + return; + } + + //fps counting + var now = LiteGraph.getTime(); + this.render_time = (now - this.last_draw_time) * 0.001; + this.last_draw_time = now; + + if (this.graph) { + this.ds.computeVisibleArea(); + } + + if ( + this.dirty_bgcanvas || + force_bgcanvas || + this.always_render_background || + (this.graph && + this.graph._last_trigger_time && + now - this.graph._last_trigger_time < 1000) + ) { + this.drawBackCanvas(); + } + + if (this.dirty_canvas || force_canvas) { + this.drawFrontCanvas(); + } + + this.fps = this.render_time ? 1.0 / this.render_time : 0; + this.frame += 1; + }; + + /** + * draws the front canvas (the one containing all the nodes) + * @method drawFrontCanvas + **/ + LGraphCanvas.prototype.drawFrontCanvas = function() { + this.dirty_canvas = false; + + if (!this.ctx) { + this.ctx = this.bgcanvas.getContext("2d"); + } + var ctx = this.ctx; + if (!ctx) { + //maybe is using webgl... + return; + } + + if (ctx.start2D) { + ctx.start2D(); + } + + var canvas = this.canvas; + + //reset in case of error + ctx.restore(); + ctx.setTransform(1, 0, 0, 1, 0, 0); + + //clip dirty area if there is one, otherwise work in full canvas + if (this.dirty_area) { + ctx.save(); + ctx.beginPath(); + ctx.rect( + this.dirty_area[0], + this.dirty_area[1], + this.dirty_area[2], + this.dirty_area[3] + ); + ctx.clip(); + } + + //clear + //canvas.width = canvas.width; + if (this.clear_background) { + ctx.clearRect(0, 0, canvas.width, canvas.height); + } + + //draw bg canvas + if (this.bgcanvas == this.canvas) { + this.drawBackCanvas(); + } else { + ctx.drawImage(this.bgcanvas, 0, 0); + } + + //rendering + if (this.onRender) { + this.onRender(canvas, ctx); + } + + //info widget + if (this.show_info) { + this.renderInfo(ctx); + } + + if (this.graph) { + //apply transformations + ctx.save(); + this.ds.toCanvasContext(ctx); + + //draw nodes + var drawn_nodes = 0; + var visible_nodes = this.computeVisibleNodes( + null, + this.visible_nodes + ); + + for (var i = 0; i < visible_nodes.length; ++i) { + var node = visible_nodes[i]; + + //transform coords system + ctx.save(); + ctx.translate(node.pos[0], node.pos[1]); + + //Draw + this.drawNode(node, ctx); + drawn_nodes += 1; + + //Restore + ctx.restore(); + } + + //on top (debug) + if (this.render_execution_order) { + this.drawExecutionOrder(ctx); + } + + //connections ontop? + if (this.graph.config.links_ontop) { + if (!this.live_mode) { + this.drawConnections(ctx); + } + } + + //current connection (the one being dragged by the mouse) + if (this.connecting_pos != null) { + ctx.lineWidth = this.connections_width; + var link_color = null; + + switch (this.connecting_output.type) { + case LiteGraph.EVENT: + link_color = LiteGraph.EVENT_LINK_COLOR; + break; + default: + link_color = LiteGraph.CONNECTING_LINK_COLOR; + } + + //the connection being dragged by the mouse + this.renderLink( + ctx, + this.connecting_pos, + [this.canvas_mouse[0], this.canvas_mouse[1]], + null, + false, + null, + link_color, + this.connecting_output.dir || + (this.connecting_node.horizontal + ? LiteGraph.DOWN + : LiteGraph.RIGHT), + LiteGraph.CENTER + ); + + ctx.beginPath(); + if ( + this.connecting_output.type === LiteGraph.EVENT || + this.connecting_output.shape === LiteGraph.BOX_SHAPE + ) { + ctx.rect( + this.connecting_pos[0] - 6 + 0.5, + this.connecting_pos[1] - 5 + 0.5, + 14, + 10 + ); + } else { + ctx.arc( + this.connecting_pos[0], + this.connecting_pos[1], + 4, + 0, + Math.PI * 2 + ); + } + ctx.fill(); + + ctx.fillStyle = "#ffcc00"; + if (this._highlight_input) { + ctx.beginPath(); + ctx.arc( + this._highlight_input[0], + this._highlight_input[1], + 6, + 0, + Math.PI * 2 + ); + ctx.fill(); + } + } + + //the selection rectangle + if (this.dragging_rectangle) { + ctx.strokeStyle = "#FFF"; + ctx.strokeRect( + this.dragging_rectangle[0], + this.dragging_rectangle[1], + this.dragging_rectangle[2], + this.dragging_rectangle[3] + ); + } + + //on top of link center + if(this.over_link_center && this.render_link_tooltip) + this.drawLinkTooltip( ctx, this.over_link_center ); + else + if(this.onDrawLinkTooltip) //to remove + this.onDrawLinkTooltip(ctx,null); + + //custom info + if (this.onDrawForeground) { + this.onDrawForeground(ctx, this.visible_rect); + } + + ctx.restore(); + } + + if (this.onDrawOverlay) { + this.onDrawOverlay(ctx); + } + + if (this.dirty_area) { + ctx.restore(); + //this.dirty_area = null; + } + + if (ctx.finish2D) { + //this is a function I use in webgl renderer + ctx.finish2D(); + } + }; + + /** + * draws some useful stats in the corner of the canvas + * @method renderInfo + **/ + LGraphCanvas.prototype.renderInfo = function(ctx, x, y) { + x = x || 0; + y = y || 0; + + ctx.save(); + ctx.translate(x, y); + + ctx.font = "10px Arial"; + ctx.fillStyle = "#888"; + if (this.graph) { + ctx.fillText( "T: " + this.graph.globaltime.toFixed(2) + "s", 5, 13 * 1 ); + ctx.fillText("I: " + this.graph.iteration, 5, 13 * 2 ); + ctx.fillText("N: " + this.graph._nodes.length + " [" + this.visible_nodes.length + "]", 5, 13 * 3 ); + ctx.fillText("V: " + this.graph._version, 5, 13 * 4); + ctx.fillText("FPS:" + this.fps.toFixed(2), 5, 13 * 5); + } else { + ctx.fillText("No graph selected", 5, 13 * 1); + } + ctx.restore(); + }; + + /** + * draws the back canvas (the one containing the background and the connections) + * @method drawBackCanvas + **/ + LGraphCanvas.prototype.drawBackCanvas = function() { + var canvas = this.bgcanvas; + if ( + canvas.width != this.canvas.width || + canvas.height != this.canvas.height + ) { + canvas.width = this.canvas.width; + canvas.height = this.canvas.height; + } + + if (!this.bgctx) { + this.bgctx = this.bgcanvas.getContext("2d"); + } + var ctx = this.bgctx; + if (ctx.start) { + ctx.start(); + } + + //clear + if (this.clear_background) { + ctx.clearRect(0, 0, canvas.width, canvas.height); + } + + if (this._graph_stack && this._graph_stack.length) { + ctx.save(); + var parent_graph = this._graph_stack[this._graph_stack.length - 1]; + var subgraph_node = this.graph._subgraph_node; + ctx.strokeStyle = subgraph_node.bgcolor; + ctx.lineWidth = 10; + ctx.strokeRect(1, 1, canvas.width - 2, canvas.height - 2); + ctx.lineWidth = 1; + ctx.font = "40px Arial"; + ctx.textAlign = "center"; + ctx.fillStyle = subgraph_node.bgcolor || "#AAA"; + var title = ""; + for (var i = 1; i < this._graph_stack.length; ++i) { + title += + this._graph_stack[i]._subgraph_node.getTitle() + " >> "; + } + ctx.fillText( + title + subgraph_node.getTitle(), + canvas.width * 0.5, + 40 + ); + ctx.restore(); + } + + var bg_already_painted = false; + if (this.onRenderBackground) { + bg_already_painted = this.onRenderBackground(canvas, ctx); + } + + //reset in case of error + ctx.restore(); + ctx.setTransform(1, 0, 0, 1, 0, 0); + this.visible_links.length = 0; + + if (this.graph) { + //apply transformations + ctx.save(); + this.ds.toCanvasContext(ctx); + + //render BG + if ( + this.background_image && + this.ds.scale > 0.5 && + !bg_already_painted + ) { + if (this.zoom_modify_alpha) { + ctx.globalAlpha = + (1.0 - 0.5 / this.ds.scale) * this.editor_alpha; + } else { + ctx.globalAlpha = this.editor_alpha; + } + ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.imageSmoothingEnabled = false; + if ( + !this._bg_img || + this._bg_img.name != this.background_image + ) { + this._bg_img = new Image(); + this._bg_img.name = this.background_image; + this._bg_img.src = this.background_image; + var that = this; + this._bg_img.onload = function() { + that.draw(true, true); + }; + } + + var pattern = null; + if (this._pattern == null && this._bg_img.width > 0) { + pattern = ctx.createPattern(this._bg_img, "repeat"); + this._pattern_img = this._bg_img; + this._pattern = pattern; + } else { + pattern = this._pattern; + } + if (pattern) { + ctx.fillStyle = pattern; + ctx.fillRect( + this.visible_area[0], + this.visible_area[1], + this.visible_area[2], + this.visible_area[3] + ); + ctx.fillStyle = "transparent"; + } + + ctx.globalAlpha = 1.0; + ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.imageSmoothingEnabled = true; + } + + //groups + if (this.graph._groups.length && !this.live_mode) { + this.drawGroups(canvas, ctx); + } + + if (this.onDrawBackground) { + this.onDrawBackground(ctx, this.visible_area); + } + if (this.onBackgroundRender) { + //LEGACY + console.error( + "WARNING! onBackgroundRender deprecated, now is named onDrawBackground " + ); + this.onBackgroundRender = null; + } + + //DEBUG: show clipping area + //ctx.fillStyle = "red"; + //ctx.fillRect( this.visible_area[0] + 10, this.visible_area[1] + 10, this.visible_area[2] - 20, this.visible_area[3] - 20); + + //bg + if (this.render_canvas_border) { + ctx.strokeStyle = "#235"; + ctx.strokeRect(0, 0, canvas.width, canvas.height); + } + + if (this.render_connections_shadows) { + ctx.shadowColor = "#000"; + ctx.shadowOffsetX = 0; + ctx.shadowOffsetY = 0; + ctx.shadowBlur = 6; + } else { + ctx.shadowColor = "rgba(0,0,0,0)"; + } + + //draw connections + if (!this.live_mode) { + this.drawConnections(ctx); + } + + ctx.shadowColor = "rgba(0,0,0,0)"; + + //restore state + ctx.restore(); + } + + if (ctx.finish) { + ctx.finish(); + } + + this.dirty_bgcanvas = false; + this.dirty_canvas = true; //to force to repaint the front canvas with the bgcanvas + }; + + var temp_vec2 = new Float32Array(2); + + /** + * draws the given node inside the canvas + * @method drawNode + **/ + LGraphCanvas.prototype.drawNode = function(node, ctx) { + var glow = false; + this.current_node = node; + + var color = + node.color || + node.constructor.color || + LiteGraph.NODE_DEFAULT_COLOR; + var bgcolor = + node.bgcolor || + node.constructor.bgcolor || + LiteGraph.NODE_DEFAULT_BGCOLOR; + + //shadow and glow + if (node.mouseOver) { + glow = true; + } + + var low_quality = this.ds.scale < 0.6; //zoomed out + + //only render if it forces it to do it + if (this.live_mode) { + if (!node.flags.collapsed) { + ctx.shadowColor = "transparent"; + if (node.onDrawForeground) { + node.onDrawForeground(ctx, this, this.canvas); + } + } + return; + } + + var editor_alpha = this.editor_alpha; + ctx.globalAlpha = editor_alpha; + + if (this.render_shadows && !low_quality) { + ctx.shadowColor = LiteGraph.DEFAULT_SHADOW_COLOR; + ctx.shadowOffsetX = 2 * this.ds.scale; + ctx.shadowOffsetY = 2 * this.ds.scale; + ctx.shadowBlur = 3 * this.ds.scale; + } else { + ctx.shadowColor = "transparent"; + } + + //custom draw collapsed method (draw after shadows because they are affected) + if ( + node.flags.collapsed && + node.onDrawCollapsed && + node.onDrawCollapsed(ctx, this) == true + ) { + return; + } + + //clip if required (mask) + var shape = node._shape || LiteGraph.BOX_SHAPE; + var size = temp_vec2; + temp_vec2.set(node.size); + var horizontal = node.horizontal; // || node.flags.horizontal; + + if (node.flags.collapsed) { + ctx.font = this.inner_text_font; + var title = node.getTitle ? node.getTitle() : node.title; + if (title != null) { + node._collapsed_width = Math.min( + node.size[0], + ctx.measureText(title).width + + LiteGraph.NODE_TITLE_HEIGHT * 2 + ); //LiteGraph.NODE_COLLAPSED_WIDTH; + size[0] = node._collapsed_width; + size[1] = 0; + } + } + + if (node.clip_area) { + //Start clipping + ctx.save(); + ctx.beginPath(); + if (shape == LiteGraph.BOX_SHAPE) { + ctx.rect(0, 0, size[0], size[1]); + } else if (shape == LiteGraph.ROUND_SHAPE) { + ctx.roundRect(0, 0, size[0], size[1], 10); + } else if (shape == LiteGraph.CIRCLE_SHAPE) { + ctx.arc( + size[0] * 0.5, + size[1] * 0.5, + size[0] * 0.5, + 0, + Math.PI * 2 + ); + } + ctx.clip(); + } + + //draw shape + if (node.has_errors) { + bgcolor = "red"; + } + this.drawNodeShape( + node, + ctx, + size, + color, + bgcolor, + node.is_selected, + node.mouseOver + ); + ctx.shadowColor = "transparent"; + + //draw foreground + if (node.onDrawForeground) { + node.onDrawForeground(ctx, this, this.canvas); + } + + //connection slots + ctx.textAlign = horizontal ? "center" : "left"; + ctx.font = this.inner_text_font; + + var render_text = !low_quality; + + var out_slot = this.connecting_output; + ctx.lineWidth = 1; + + var max_y = 0; + var slot_pos = new Float32Array(2); //to reuse + + //render inputs and outputs + if (!node.flags.collapsed) { + //input connection slots + if (node.inputs) { + for (var i = 0; i < node.inputs.length; i++) { + var slot = node.inputs[i]; + + ctx.globalAlpha = editor_alpha; + //change opacity of incompatible slots when dragging a connection + if ( this.connecting_node && !LiteGraph.isValidConnection( slot.type , out_slot.type) ) { + ctx.globalAlpha = 0.4 * editor_alpha; + } + + ctx.fillStyle = + slot.link != null + ? slot.color_on || + this.default_connection_color.input_on + : slot.color_off || + this.default_connection_color.input_off; + + var pos = node.getConnectionPos(true, i, slot_pos); + pos[0] -= node.pos[0]; + pos[1] -= node.pos[1]; + if (max_y < pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5) { + max_y = pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5; + } + + ctx.beginPath(); + + if ( + slot.type === LiteGraph.EVENT || + slot.shape === LiteGraph.BOX_SHAPE + ) { + if (horizontal) { + ctx.rect( + pos[0] - 5 + 0.5, + pos[1] - 8 + 0.5, + 10, + 14 + ); + } else { + ctx.rect( + pos[0] - 6 + 0.5, + pos[1] - 5 + 0.5, + 14, + 10 + ); + } + } else if (slot.shape === LiteGraph.ARROW_SHAPE) { + ctx.moveTo(pos[0] + 8, pos[1] + 0.5); + ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5); + ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5); + ctx.closePath(); + } else { + if(low_quality) + ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); //faster + else + ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); + } + ctx.fill(); + + //render name + if (render_text) { + var text = slot.label != null ? slot.label : slot.name; + if (text) { + ctx.fillStyle = LiteGraph.NODE_TEXT_COLOR; + if (horizontal || slot.dir == LiteGraph.UP) { + ctx.fillText(text, pos[0], pos[1] - 10); + } else { + ctx.fillText(text, pos[0] + 10, pos[1] + 5); + } + } + } + } + } + + //output connection slots + if (this.connecting_node) { + ctx.globalAlpha = 0.4 * editor_alpha; + } + + ctx.textAlign = horizontal ? "center" : "right"; + ctx.strokeStyle = "black"; + if (node.outputs) { + for (var i = 0; i < node.outputs.length; i++) { + var slot = node.outputs[i]; + + var pos = node.getConnectionPos(false, i, slot_pos); + pos[0] -= node.pos[0]; + pos[1] -= node.pos[1]; + if (max_y < pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5) { + max_y = pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5; + } + + ctx.fillStyle = + slot.links && slot.links.length + ? slot.color_on || + this.default_connection_color.output_on + : slot.color_off || + this.default_connection_color.output_off; + ctx.beginPath(); + //ctx.rect( node.size[0] - 14,i*14,10,10); + + if ( + slot.type === LiteGraph.EVENT || + slot.shape === LiteGraph.BOX_SHAPE + ) { + if (horizontal) { + ctx.rect( + pos[0] - 5 + 0.5, + pos[1] - 8 + 0.5, + 10, + 14 + ); + } else { + ctx.rect( + pos[0] - 6 + 0.5, + pos[1] - 5 + 0.5, + 14, + 10 + ); + } + } else if (slot.shape === LiteGraph.ARROW_SHAPE) { + ctx.moveTo(pos[0] + 8, pos[1] + 0.5); + ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5); + ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5); + ctx.closePath(); + } else { + if(low_quality) + ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); + else + ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); + } + + //trigger + //if(slot.node_id != null && slot.slot == -1) + // ctx.fillStyle = "#F85"; + + //if(slot.links != null && slot.links.length) + ctx.fill(); + if(!low_quality) + ctx.stroke(); + + //render output name + if (render_text) { + var text = slot.label != null ? slot.label : slot.name; + if (text) { + ctx.fillStyle = LiteGraph.NODE_TEXT_COLOR; + if (horizontal || slot.dir == LiteGraph.DOWN) { + ctx.fillText(text, pos[0], pos[1] - 8); + } else { + ctx.fillText(text, pos[0] - 10, pos[1] + 5); + } + } + } + } + } + + ctx.textAlign = "left"; + ctx.globalAlpha = 1; + + if (node.widgets) { + var widgets_y = max_y; + if (horizontal || node.widgets_up) { + widgets_y = 2; + } + if( node.widgets_start_y != null ) + widgets_y = node.widgets_start_y; + this.drawNodeWidgets( + node, + widgets_y, + ctx, + this.node_widget && this.node_widget[0] == node + ? this.node_widget[1] + : null + ); + } + } else if (this.render_collapsed_slots) { + //if collapsed + var input_slot = null; + var output_slot = null; + + //get first connected slot to render + if (node.inputs) { + for (var i = 0; i < node.inputs.length; i++) { + var slot = node.inputs[i]; + if (slot.link == null) { + continue; + } + input_slot = slot; + break; + } + } + if (node.outputs) { + for (var i = 0; i < node.outputs.length; i++) { + var slot = node.outputs[i]; + if (!slot.links || !slot.links.length) { + continue; + } + output_slot = slot; + } + } + + if (input_slot) { + var x = 0; + var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; //center + if (horizontal) { + x = node._collapsed_width * 0.5; + y = -LiteGraph.NODE_TITLE_HEIGHT; + } + ctx.fillStyle = "#686"; + ctx.beginPath(); + if ( + slot.type === LiteGraph.EVENT || + slot.shape === LiteGraph.BOX_SHAPE + ) { + ctx.rect(x - 7 + 0.5, y - 4, 14, 8); + } else if (slot.shape === LiteGraph.ARROW_SHAPE) { + ctx.moveTo(x + 8, y); + ctx.lineTo(x + -4, y - 4); + ctx.lineTo(x + -4, y + 4); + ctx.closePath(); + } else { + ctx.arc(x, y, 4, 0, Math.PI * 2); + } + ctx.fill(); + } + + if (output_slot) { + var x = node._collapsed_width; + var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; //center + if (horizontal) { + x = node._collapsed_width * 0.5; + y = 0; + } + ctx.fillStyle = "#686"; + ctx.strokeStyle = "black"; + ctx.beginPath(); + if ( + slot.type === LiteGraph.EVENT || + slot.shape === LiteGraph.BOX_SHAPE + ) { + ctx.rect(x - 7 + 0.5, y - 4, 14, 8); + } else if (slot.shape === LiteGraph.ARROW_SHAPE) { + ctx.moveTo(x + 6, y); + ctx.lineTo(x - 6, y - 4); + ctx.lineTo(x - 6, y + 4); + ctx.closePath(); + } else { + ctx.arc(x, y, 4, 0, Math.PI * 2); + } + ctx.fill(); + //ctx.stroke(); + } + } + + if (node.clip_area) { + ctx.restore(); + } + + ctx.globalAlpha = 1.0; + }; + + //used by this.over_link_center + LGraphCanvas.prototype.drawLinkTooltip = function( ctx, link ) + { + var pos = link._pos; + ctx.fillStyle = "black"; + ctx.beginPath(); + ctx.arc( pos[0], pos[1], 3, 0, Math.PI * 2 ); + ctx.fill(); + + if(link.data == null) + return; + + if(this.onDrawLinkTooltip) + if( this.onDrawLinkTooltip(ctx,link,this) == true ) + return; + + var data = link.data; + var text = null; + + if( data.constructor === Number ) + text = data.toFixed(2); + else if( data.constructor === String ) + text = "\"" + data + "\""; + else if( data.constructor === Boolean ) + text = String(data); + else if (data.toToolTip) + text = data.toToolTip(); + else + text = "[" + data.constructor.name + "]"; + + if(text == null) + return; + text = text.substr(0,30); //avoid weird + + ctx.font = "14px Courier New"; + var info = ctx.measureText(text); + var w = info.width + 20; + var h = 24; + ctx.shadowColor = "black"; + ctx.shadowOffsetX = 2; + ctx.shadowOffsetY = 2; + ctx.shadowBlur = 3; + ctx.fillStyle = "#454"; + ctx.beginPath(); + ctx.roundRect( pos[0] - w*0.5, pos[1] - 15 - h, w, h,3, 3); + ctx.moveTo( pos[0] - 10, pos[1] - 15 ); + ctx.lineTo( pos[0] + 10, pos[1] - 15 ); + ctx.lineTo( pos[0], pos[1] - 5 ); + ctx.fill(); + ctx.shadowColor = "transparent"; + ctx.textAlign = "center"; + ctx.fillStyle = "#CEC"; + ctx.fillText(text, pos[0], pos[1] - 15 - h * 0.3); + } + + /** + * draws the shape of the given node in the canvas + * @method drawNodeShape + **/ + var tmp_area = new Float32Array(4); + + LGraphCanvas.prototype.drawNodeShape = function( + node, + ctx, + size, + fgcolor, + bgcolor, + selected, + mouse_over + ) { + //bg rect + ctx.strokeStyle = fgcolor; + ctx.fillStyle = bgcolor; + + var title_height = LiteGraph.NODE_TITLE_HEIGHT; + var low_quality = this.ds.scale < 0.5; + + //render node area depending on shape + var shape = + node._shape || node.constructor.shape || LiteGraph.ROUND_SHAPE; + + var title_mode = node.constructor.title_mode; + + var render_title = true; + if (title_mode == LiteGraph.TRANSPARENT_TITLE) { + render_title = false; + } else if (title_mode == LiteGraph.AUTOHIDE_TITLE && mouse_over) { + render_title = true; + } + + var area = tmp_area; + area[0] = 0; //x + area[1] = render_title ? -title_height : 0; //y + area[2] = size[0] + 1; //w + area[3] = render_title ? size[1] + title_height : size[1]; //h + + var old_alpha = ctx.globalAlpha; + + //full node shape + //if(node.flags.collapsed) + { + ctx.beginPath(); + if (shape == LiteGraph.BOX_SHAPE || low_quality) { + ctx.fillRect(area[0], area[1], area[2], area[3]); + } else if ( + shape == LiteGraph.ROUND_SHAPE || + shape == LiteGraph.CARD_SHAPE + ) { + ctx.roundRect( + area[0], + area[1], + area[2], + area[3], + this.round_radius, + shape == LiteGraph.CARD_SHAPE ? 0 : this.round_radius + ); + } else if (shape == LiteGraph.CIRCLE_SHAPE) { + ctx.arc( + size[0] * 0.5, + size[1] * 0.5, + size[0] * 0.5, + 0, + Math.PI * 2 + ); + } + ctx.fill(); + + //separator + if(!node.flags.collapsed) + { + ctx.shadowColor = "transparent"; + ctx.fillStyle = "rgba(0,0,0,0.2)"; + ctx.fillRect(0, -1, area[2], 2); + } + } + ctx.shadowColor = "transparent"; + + if (node.onDrawBackground) { + node.onDrawBackground(ctx, this, this.canvas); + } + + //title bg (remember, it is rendered ABOVE the node) + if (render_title || title_mode == LiteGraph.TRANSPARENT_TITLE) { + //title bar + if (node.onDrawTitleBar) { + node.onDrawTitleBar( + ctx, + title_height, + size, + this.ds.scale, + fgcolor + ); + } else if ( + title_mode != LiteGraph.TRANSPARENT_TITLE && + (node.constructor.title_color || this.render_title_colored) + ) { + var title_color = node.constructor.title_color || fgcolor; + + if (node.flags.collapsed) { + ctx.shadowColor = LiteGraph.DEFAULT_SHADOW_COLOR; + } + + //* gradient test + if (this.use_gradients) { + var grad = LGraphCanvas.gradients[title_color]; + if (!grad) { + grad = LGraphCanvas.gradients[ title_color ] = ctx.createLinearGradient(0, 0, 400, 0); + grad.addColorStop(0, title_color); + grad.addColorStop(1, "#000"); + } + ctx.fillStyle = grad; + } else { + ctx.fillStyle = title_color; + } + + //ctx.globalAlpha = 0.5 * old_alpha; + ctx.beginPath(); + if (shape == LiteGraph.BOX_SHAPE || low_quality) { + ctx.rect(0, -title_height, size[0] + 1, title_height); + } else if ( shape == LiteGraph.ROUND_SHAPE || shape == LiteGraph.CARD_SHAPE ) { + ctx.roundRect( + 0, + -title_height, + size[0] + 1, + title_height, + this.round_radius, + node.flags.collapsed ? this.round_radius : 0 + ); + } + ctx.fill(); + ctx.shadowColor = "transparent"; + } + + //title box + var box_size = 10; + if (node.onDrawTitleBox) { + node.onDrawTitleBox(ctx, title_height, size, this.ds.scale); + } else if ( + shape == LiteGraph.ROUND_SHAPE || + shape == LiteGraph.CIRCLE_SHAPE || + shape == LiteGraph.CARD_SHAPE + ) { + if (low_quality) { + ctx.fillStyle = "black"; + ctx.beginPath(); + ctx.arc( + title_height * 0.5, + title_height * -0.5, + box_size * 0.5 + 1, + 0, + Math.PI * 2 + ); + ctx.fill(); + } + + ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR; + if(low_quality) + ctx.fillRect( title_height * 0.5 - box_size *0.5, title_height * -0.5 - box_size *0.5, box_size , box_size ); + else + { + ctx.beginPath(); + ctx.arc( + title_height * 0.5, + title_height * -0.5, + box_size * 0.5, + 0, + Math.PI * 2 + ); + ctx.fill(); + } + } else { + if (low_quality) { + ctx.fillStyle = "black"; + ctx.fillRect( + (title_height - box_size) * 0.5 - 1, + (title_height + box_size) * -0.5 - 1, + box_size + 2, + box_size + 2 + ); + } + ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR; + ctx.fillRect( + (title_height - box_size) * 0.5, + (title_height + box_size) * -0.5, + box_size, + box_size + ); + } + ctx.globalAlpha = old_alpha; + + //title text + if (node.onDrawTitleText) { + node.onDrawTitleText( + ctx, + title_height, + size, + this.ds.scale, + this.title_text_font, + selected + ); + } + if (!low_quality) { + ctx.font = this.title_text_font; + var title = String(node.getTitle()); + if (title) { + if (selected) { + ctx.fillStyle = "white"; + } else { + ctx.fillStyle = + node.constructor.title_text_color || + this.node_title_color; + } + if (node.flags.collapsed) { + ctx.textAlign = "left"; + var measure = ctx.measureText(title); + ctx.fillText( + title.substr(0,20), //avoid urls too long + title_height,// + measure.width * 0.5, + LiteGraph.NODE_TITLE_TEXT_Y - title_height + ); + ctx.textAlign = "left"; + } else { + ctx.textAlign = "left"; + ctx.fillText( + title, + title_height, + LiteGraph.NODE_TITLE_TEXT_Y - title_height + ); + } + } + } + + if (node.onDrawTitle) { + node.onDrawTitle(ctx); + } + } + + //render selection marker + if (selected) { + if (node.onBounding) { + node.onBounding(area); + } + + if (title_mode == LiteGraph.TRANSPARENT_TITLE) { + area[1] -= title_height; + area[3] += title_height; + } + ctx.lineWidth = 1; + ctx.globalAlpha = 0.8; + ctx.beginPath(); + if (shape == LiteGraph.BOX_SHAPE) { + ctx.rect( + -6 + area[0], + -6 + area[1], + 12 + area[2], + 12 + area[3] + ); + } else if ( + shape == LiteGraph.ROUND_SHAPE || + (shape == LiteGraph.CARD_SHAPE && node.flags.collapsed) + ) { + ctx.roundRect( + -6 + area[0], + -6 + area[1], + 12 + area[2], + 12 + area[3], + this.round_radius * 2 + ); + } else if (shape == LiteGraph.CARD_SHAPE) { + ctx.roundRect( + -6 + area[0], + -6 + area[1], + 12 + area[2], + 12 + area[3], + this.round_radius * 2, + 2 + ); + } else if (shape == LiteGraph.CIRCLE_SHAPE) { + ctx.arc( + size[0] * 0.5, + size[1] * 0.5, + size[0] * 0.5 + 6, + 0, + Math.PI * 2 + ); + } + ctx.strokeStyle = "#FFF"; + ctx.stroke(); + ctx.strokeStyle = fgcolor; + ctx.globalAlpha = 1; + } + }; + + var margin_area = new Float32Array(4); + var link_bounding = new Float32Array(4); + var tempA = new Float32Array(2); + var tempB = new Float32Array(2); + + /** + * draws every connection visible in the canvas + * OPTIMIZE THIS: pre-catch connections position instead of recomputing them every time + * @method drawConnections + **/ + LGraphCanvas.prototype.drawConnections = function(ctx) { + var now = LiteGraph.getTime(); + var visible_area = this.visible_area; + margin_area[0] = visible_area[0] - 20; + margin_area[1] = visible_area[1] - 20; + margin_area[2] = visible_area[2] + 40; + margin_area[3] = visible_area[3] + 40; + + //draw connections + ctx.lineWidth = this.connections_width; + + ctx.fillStyle = "#AAA"; + ctx.strokeStyle = "#AAA"; + ctx.globalAlpha = this.editor_alpha; + //for every node + var nodes = this.graph._nodes; + for (var n = 0, l = nodes.length; n < l; ++n) { + var node = nodes[n]; + //for every input (we render just inputs because it is easier as every slot can only have one input) + if (!node.inputs || !node.inputs.length) { + continue; + } + + for (var i = 0; i < node.inputs.length; ++i) { + var input = node.inputs[i]; + if (!input || input.link == null) { + continue; + } + var link_id = input.link; + var link = this.graph.links[link_id]; + if (!link) { + continue; + } + + //find link info + var start_node = this.graph.getNodeById(link.origin_id); + if (start_node == null) { + continue; + } + var start_node_slot = link.origin_slot; + var start_node_slotpos = null; + if (start_node_slot == -1) { + start_node_slotpos = [ + start_node.pos[0] + 10, + start_node.pos[1] + 10 + ]; + } else { + start_node_slotpos = start_node.getConnectionPos( + false, + start_node_slot, + tempA + ); + } + var end_node_slotpos = node.getConnectionPos(true, i, tempB); + + //compute link bounding + link_bounding[0] = start_node_slotpos[0]; + link_bounding[1] = start_node_slotpos[1]; + link_bounding[2] = end_node_slotpos[0] - start_node_slotpos[0]; + link_bounding[3] = end_node_slotpos[1] - start_node_slotpos[1]; + if (link_bounding[2] < 0) { + link_bounding[0] += link_bounding[2]; + link_bounding[2] = Math.abs(link_bounding[2]); + } + if (link_bounding[3] < 0) { + link_bounding[1] += link_bounding[3]; + link_bounding[3] = Math.abs(link_bounding[3]); + } + + //skip links outside of the visible area of the canvas + if (!overlapBounding(link_bounding, margin_area)) { + continue; + } + + var start_slot = start_node.outputs[start_node_slot]; + var end_slot = node.inputs[i]; + if (!start_slot || !end_slot) { + continue; + } + var start_dir = + start_slot.dir || + (start_node.horizontal ? LiteGraph.DOWN : LiteGraph.RIGHT); + var end_dir = + end_slot.dir || + (node.horizontal ? LiteGraph.UP : LiteGraph.LEFT); + + this.renderLink( + ctx, + start_node_slotpos, + end_node_slotpos, + link, + false, + 0, + null, + start_dir, + end_dir + ); + + //event triggered rendered on top + if (link && link._last_time && now - link._last_time < 1000) { + var f = 2.0 - (now - link._last_time) * 0.002; + var tmp = ctx.globalAlpha; + ctx.globalAlpha = tmp * f; + this.renderLink( + ctx, + start_node_slotpos, + end_node_slotpos, + link, + true, + f, + "white", + start_dir, + end_dir + ); + ctx.globalAlpha = tmp; + } + } + } + ctx.globalAlpha = 1; + }; + + /** + * draws a link between two points + * @method renderLink + * @param {vec2} a start pos + * @param {vec2} b end pos + * @param {Object} link the link object with all the link info + * @param {boolean} skip_border ignore the shadow of the link + * @param {boolean} flow show flow animation (for events) + * @param {string} color the color for the link + * @param {number} start_dir the direction enum + * @param {number} end_dir the direction enum + * @param {number} num_sublines number of sublines (useful to represent vec3 or rgb) + **/ + LGraphCanvas.prototype.renderLink = function( + ctx, + a, + b, + link, + skip_border, + flow, + color, + start_dir, + end_dir, + num_sublines + ) { + if (link) { + this.visible_links.push(link); + } + + //choose color + if (!color && link) { + color = link.color || LGraphCanvas.link_type_colors[link.type]; + } + if (!color) { + color = this.default_link_color; + } + if (link != null && this.highlighted_links[link.id]) { + color = "#FFF"; + } + + start_dir = start_dir || LiteGraph.RIGHT; + end_dir = end_dir || LiteGraph.LEFT; + + var dist = distance(a, b); + + if (this.render_connections_border && this.ds.scale > 0.6) { + ctx.lineWidth = this.connections_width + 4; + } + ctx.lineJoin = "round"; + num_sublines = num_sublines || 1; + if (num_sublines > 1) { + ctx.lineWidth = 0.5; + } + + //begin line shape + ctx.beginPath(); + for (var i = 0; i < num_sublines; i += 1) { + var offsety = (i - (num_sublines - 1) * 0.5) * 5; + + if (this.links_render_mode == LiteGraph.SPLINE_LINK) { + ctx.moveTo(a[0], a[1] + offsety); + var start_offset_x = 0; + var start_offset_y = 0; + var end_offset_x = 0; + var end_offset_y = 0; + switch (start_dir) { + case LiteGraph.LEFT: + start_offset_x = dist * -0.25; + break; + case LiteGraph.RIGHT: + start_offset_x = dist * 0.25; + break; + case LiteGraph.UP: + start_offset_y = dist * -0.25; + break; + case LiteGraph.DOWN: + start_offset_y = dist * 0.25; + break; + } + switch (end_dir) { + case LiteGraph.LEFT: + end_offset_x = dist * -0.25; + break; + case LiteGraph.RIGHT: + end_offset_x = dist * 0.25; + break; + case LiteGraph.UP: + end_offset_y = dist * -0.25; + break; + case LiteGraph.DOWN: + end_offset_y = dist * 0.25; + break; + } + ctx.bezierCurveTo( + a[0] + start_offset_x, + a[1] + start_offset_y + offsety, + b[0] + end_offset_x, + b[1] + end_offset_y + offsety, + b[0], + b[1] + offsety + ); + } else if (this.links_render_mode == LiteGraph.LINEAR_LINK) { + ctx.moveTo(a[0], a[1] + offsety); + var start_offset_x = 0; + var start_offset_y = 0; + var end_offset_x = 0; + var end_offset_y = 0; + switch (start_dir) { + case LiteGraph.LEFT: + start_offset_x = -1; + break; + case LiteGraph.RIGHT: + start_offset_x = 1; + break; + case LiteGraph.UP: + start_offset_y = -1; + break; + case LiteGraph.DOWN: + start_offset_y = 1; + break; + } + switch (end_dir) { + case LiteGraph.LEFT: + end_offset_x = -1; + break; + case LiteGraph.RIGHT: + end_offset_x = 1; + break; + case LiteGraph.UP: + end_offset_y = -1; + break; + case LiteGraph.DOWN: + end_offset_y = 1; + break; + } + var l = 15; + ctx.lineTo( + a[0] + start_offset_x * l, + a[1] + start_offset_y * l + offsety + ); + ctx.lineTo( + b[0] + end_offset_x * l, + b[1] + end_offset_y * l + offsety + ); + ctx.lineTo(b[0], b[1] + offsety); + } else if (this.links_render_mode == LiteGraph.STRAIGHT_LINK) { + ctx.moveTo(a[0], a[1]); + var start_x = a[0]; + var start_y = a[1]; + var end_x = b[0]; + var end_y = b[1]; + if (start_dir == LiteGraph.RIGHT) { + start_x += 10; + } else { + start_y += 10; + } + if (end_dir == LiteGraph.LEFT) { + end_x -= 10; + } else { + end_y -= 10; + } + ctx.lineTo(start_x, start_y); + ctx.lineTo((start_x + end_x) * 0.5, start_y); + ctx.lineTo((start_x + end_x) * 0.5, end_y); + ctx.lineTo(end_x, end_y); + ctx.lineTo(b[0], b[1]); + } else { + return; + } //unknown + } + + //rendering the outline of the connection can be a little bit slow + if ( + this.render_connections_border && + this.ds.scale > 0.6 && + !skip_border + ) { + ctx.strokeStyle = "rgba(0,0,0,0.5)"; + ctx.stroke(); + } + + ctx.lineWidth = this.connections_width; + ctx.fillStyle = ctx.strokeStyle = color; + ctx.stroke(); + //end line shape + + var pos = this.computeConnectionPoint(a, b, 0.5, start_dir, end_dir); + if (link && link._pos) { + link._pos[0] = pos[0]; + link._pos[1] = pos[1]; + } + + //render arrow in the middle + if ( + this.ds.scale >= 0.6 && + this.highquality_render && + end_dir != LiteGraph.CENTER + ) { + //render arrow + if (this.render_connection_arrows) { + //compute two points in the connection + var posA = this.computeConnectionPoint( + a, + b, + 0.25, + start_dir, + end_dir + ); + var posB = this.computeConnectionPoint( + a, + b, + 0.26, + start_dir, + end_dir + ); + var posC = this.computeConnectionPoint( + a, + b, + 0.75, + start_dir, + end_dir + ); + var posD = this.computeConnectionPoint( + a, + b, + 0.76, + start_dir, + end_dir + ); + + //compute the angle between them so the arrow points in the right direction + var angleA = 0; + var angleB = 0; + if (this.render_curved_connections) { + angleA = -Math.atan2(posB[0] - posA[0], posB[1] - posA[1]); + angleB = -Math.atan2(posD[0] - posC[0], posD[1] - posC[1]); + } else { + angleB = angleA = b[1] > a[1] ? 0 : Math.PI; + } + + //render arrow + ctx.save(); + ctx.translate(posA[0], posA[1]); + ctx.rotate(angleA); + ctx.beginPath(); + ctx.moveTo(-5, -3); + ctx.lineTo(0, +7); + ctx.lineTo(+5, -3); + ctx.fill(); + ctx.restore(); + ctx.save(); + ctx.translate(posC[0], posC[1]); + ctx.rotate(angleB); + ctx.beginPath(); + ctx.moveTo(-5, -3); + ctx.lineTo(0, +7); + ctx.lineTo(+5, -3); + ctx.fill(); + ctx.restore(); + } + + //circle + ctx.beginPath(); + ctx.arc(pos[0], pos[1], 5, 0, Math.PI * 2); + ctx.fill(); + } + + //render flowing points + if (flow) { + ctx.fillStyle = color; + for (var i = 0; i < 5; ++i) { + var f = (LiteGraph.getTime() * 0.001 + i * 0.2) % 1; + var pos = this.computeConnectionPoint( + a, + b, + f, + start_dir, + end_dir + ); + ctx.beginPath(); + ctx.arc(pos[0], pos[1], 5, 0, 2 * Math.PI); + ctx.fill(); + } + } + }; + + //returns the link center point based on curvature + LGraphCanvas.prototype.computeConnectionPoint = function( + a, + b, + t, + start_dir, + end_dir + ) { + start_dir = start_dir || LiteGraph.RIGHT; + end_dir = end_dir || LiteGraph.LEFT; + + var dist = distance(a, b); + var p0 = a; + var p1 = [a[0], a[1]]; + var p2 = [b[0], b[1]]; + var p3 = b; + + switch (start_dir) { + case LiteGraph.LEFT: + p1[0] += dist * -0.25; + break; + case LiteGraph.RIGHT: + p1[0] += dist * 0.25; + break; + case LiteGraph.UP: + p1[1] += dist * -0.25; + break; + case LiteGraph.DOWN: + p1[1] += dist * 0.25; + break; + } + switch (end_dir) { + case LiteGraph.LEFT: + p2[0] += dist * -0.25; + break; + case LiteGraph.RIGHT: + p2[0] += dist * 0.25; + break; + case LiteGraph.UP: + p2[1] += dist * -0.25; + break; + case LiteGraph.DOWN: + p2[1] += dist * 0.25; + break; + } + + var c1 = (1 - t) * (1 - t) * (1 - t); + var c2 = 3 * ((1 - t) * (1 - t)) * t; + var c3 = 3 * (1 - t) * (t * t); + var c4 = t * t * t; + + var x = c1 * p0[0] + c2 * p1[0] + c3 * p2[0] + c4 * p3[0]; + var y = c1 * p0[1] + c2 * p1[1] + c3 * p2[1] + c4 * p3[1]; + return [x, y]; + }; + + LGraphCanvas.prototype.drawExecutionOrder = function(ctx) { + ctx.shadowColor = "transparent"; + ctx.globalAlpha = 0.25; + + ctx.textAlign = "center"; + ctx.strokeStyle = "white"; + ctx.globalAlpha = 0.75; + + var visible_nodes = this.visible_nodes; + for (var i = 0; i < visible_nodes.length; ++i) { + var node = visible_nodes[i]; + ctx.fillStyle = "black"; + ctx.fillRect( + node.pos[0] - LiteGraph.NODE_TITLE_HEIGHT, + node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, + LiteGraph.NODE_TITLE_HEIGHT, + LiteGraph.NODE_TITLE_HEIGHT + ); + if (node.order == 0) { + ctx.strokeRect( + node.pos[0] - LiteGraph.NODE_TITLE_HEIGHT + 0.5, + node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5, + LiteGraph.NODE_TITLE_HEIGHT, + LiteGraph.NODE_TITLE_HEIGHT + ); + } + ctx.fillStyle = "#FFF"; + ctx.fillText( + node.order, + node.pos[0] + LiteGraph.NODE_TITLE_HEIGHT * -0.5, + node.pos[1] - 6 + ); + } + ctx.globalAlpha = 1; + }; + + /** + * draws the widgets stored inside a node + * @method drawNodeWidgets + **/ + LGraphCanvas.prototype.drawNodeWidgets = function( + node, + posY, + ctx, + active_widget + ) { + if (!node.widgets || !node.widgets.length) { + return 0; + } + var width = node.size[0]; + var widgets = node.widgets; + posY += 2; + var H = LiteGraph.NODE_WIDGET_HEIGHT; + var show_text = this.ds.scale > 0.5; + ctx.save(); + ctx.globalAlpha = this.editor_alpha; + var outline_color = LiteGraph.WIDGET_OUTLINE_COLOR; + var background_color = LiteGraph.WIDGET_BGCOLOR; + var text_color = LiteGraph.WIDGET_TEXT_COLOR; + var secondary_text_color = LiteGraph.WIDGET_SECONDARY_TEXT_COLOR; + var margin = 15; + + for (var i = 0; i < widgets.length; ++i) { + var h = H; + var w = widgets[i]; + var y = posY; + if (w.y) { + y = w.y; + } + w.last_y = y; + ctx.strokeStyle = outline_color; + ctx.fillStyle = "#222"; + ctx.textAlign = "left"; + if(w.disabled) + ctx.globalAlpha *= 0.5; + + switch (w.type) { + case "button": + if (w.clicked) { + ctx.fillStyle = "#AAA"; + w.clicked = false; + this.dirty_canvas = true; + } + ctx.fillRect(margin, y, width - margin * 2, H); + if(show_text) + ctx.strokeRect( margin, y, width - margin * 2, H ); + if (show_text) { + ctx.textAlign = "center"; + ctx.fillStyle = text_color; + ctx.fillText(w.name, width * 0.5, y + H * 0.7); + } + break; + case "toggle": + ctx.textAlign = "left"; + ctx.strokeStyle = outline_color; + ctx.fillStyle = background_color; + ctx.beginPath(); + if (show_text) + ctx.roundRect(margin, posY, width - margin * 2, H, H * 0.5); + else + ctx.rect(margin, posY, width - margin * 2, H ); + ctx.fill(); + if(show_text) + ctx.stroke(); + ctx.fillStyle = w.value ? "#89A" : "#333"; + ctx.beginPath(); + ctx.arc( width - margin * 2, y + H * 0.5, H * 0.36, 0, Math.PI * 2 ); + ctx.fill(); + if (show_text) { + ctx.fillStyle = secondary_text_color; + if (w.name != null) { + ctx.fillText(w.name, margin * 2, y + H * 0.7); + } + ctx.fillStyle = w.value ? text_color : secondary_text_color; + ctx.textAlign = "right"; + ctx.fillText( + w.value + ? w.options.on || "true" + : w.options.off || "false", + width - 40, + y + H * 0.7 + ); + } + break; + case "slider": + ctx.fillStyle = background_color; + ctx.fillRect(margin, y, width - margin * 2, H); + var range = w.options.max - w.options.min; + var nvalue = (w.value - w.options.min) / range; + ctx.fillStyle = active_widget == w ? "#89A" : "#678"; + ctx.fillRect(margin, y, nvalue * (width - margin * 2), H); + if(show_text) + ctx.strokeRect(margin, y, width - margin * 2, H); + if (w.marker) { + var marker_nvalue = (w.marker - w.options.min) / range; + ctx.fillStyle = "#AA9"; + ctx.fillRect( margin + marker_nvalue * (width - margin * 2), y, 2, H ); + } + if (show_text) { + ctx.textAlign = "center"; + ctx.fillStyle = text_color; + ctx.fillText( + w.name + " " + Number(w.value).toFixed(3), + width * 0.5, + y + H * 0.7 + ); + } + break; + case "number": + case "combo": + ctx.textAlign = "left"; + ctx.strokeStyle = outline_color; + ctx.fillStyle = background_color; + ctx.beginPath(); + if(show_text) + ctx.roundRect(margin, posY, width - margin * 2, H, H * 0.5); + else + ctx.rect(margin, posY, width - margin * 2, H ); + ctx.fill(); + if (show_text) { + ctx.stroke(); + ctx.fillStyle = text_color; + ctx.beginPath(); + ctx.moveTo(margin + 16, posY + 5); + ctx.lineTo(margin + 6, posY + H * 0.5); + ctx.lineTo(margin + 16, posY + H - 5); + ctx.fill(); + ctx.beginPath(); + ctx.moveTo(width - margin - 16, posY + 5); + ctx.lineTo(width - margin - 6, posY + H * 0.5); + ctx.lineTo(width - margin - 16, posY + H - 5); + ctx.fill(); + ctx.fillStyle = secondary_text_color; + ctx.fillText(w.name, margin * 2 + 5, y + H * 0.7); + ctx.fillStyle = text_color; + ctx.textAlign = "right"; + if (w.type == "number") { + ctx.fillText( + Number(w.value).toFixed( + w.options.precision !== undefined + ? w.options.precision + : 3 + ), + width - margin * 2 - 20, + y + H * 0.7 + ); + } else { + var v = w.value; + if( w.options.values ) + { + var values = w.options.values; + if( values.constructor === Function ) + values = values(); + if(values && values.constructor !== Array) + v = values[ w.value ]; + } + ctx.fillText( + v, + width - margin * 2 - 20, + y + H * 0.7 + ); + } + } + break; + case "string": + case "text": + ctx.textAlign = "left"; + ctx.strokeStyle = outline_color; + ctx.fillStyle = background_color; + ctx.beginPath(); + if (show_text) + ctx.roundRect(margin, posY, width - margin * 2, H, H * 0.5); + else + ctx.rect( margin, posY, width - margin * 2, H ); + ctx.fill(); + if (show_text) { + ctx.save(); + ctx.beginPath(); + ctx.rect(margin, posY, width - margin * 2, H); + ctx.clip(); + + ctx.stroke(); + ctx.fillStyle = secondary_text_color; + if (w.name != null) { + ctx.fillText(w.name, margin * 2, y + H * 0.7); + } + ctx.fillStyle = text_color; + ctx.textAlign = "right"; + ctx.fillText(String(w.value).substr(0,30), width - margin * 2, y + H * 0.7); //30 chars max + ctx.restore(); + } + break; + default: + if (w.draw) { + h = w.draw(ctx, node, width, y, H) || H; + } + break; + } + posY += h + 4; + ctx.globalAlpha = this.editor_alpha; + + } + ctx.restore(); + ctx.textAlign = "left"; + }; + + /** + * process an event on widgets + * @method processNodeWidgets + **/ + LGraphCanvas.prototype.processNodeWidgets = function( + node, + pos, + event, + active_widget + ) { + if (!node.widgets || !node.widgets.length) { + return null; + } + + var x = pos[0] - node.pos[0]; + var y = pos[1] - node.pos[1]; + var width = node.size[0]; + var that = this; + var ref_window = this.getCanvasWindow(); + + for (var i = 0; i < node.widgets.length; ++i) { + var w = node.widgets[i]; + if(!w || w.disabled) + continue; + if ( w == active_widget || (x > 6 && x < width - 12 && y > w.last_y && y < w.last_y + LiteGraph.NODE_WIDGET_HEIGHT) ) { + //inside widget + switch (w.type) { + case "button": + if (event.type === "mousemove") { + break; + } + if (w.callback) { + setTimeout(function() { + w.callback(w, that, node, pos, event); + }, 20); + } + w.clicked = true; + this.dirty_canvas = true; + break; + case "slider": + var range = w.options.max - w.options.min; + var nvalue = Math.clamp((x - 10) / (width - 20), 0, 1); + w.value = + w.options.min + + (w.options.max - w.options.min) * nvalue; + if (w.callback) { + setTimeout(function() { + inner_value_change(w, w.value); + }, 20); + } + this.dirty_canvas = true; + break; + case "number": + case "combo": + var old_value = w.value; + if (event.type == "mousemove" && w.type == "number") { + w.value += event.deltaX * 0.1 * (w.options.step || 1); + if ( w.options.min != null && w.value < w.options.min ) { + w.value = w.options.min; + } + if ( w.options.max != null && w.value > w.options.max ) { + w.value = w.options.max; + } + } else if (event.type == "mousedown") { + var values = w.options.values; + if (values && values.constructor === Function) { + values = w.options.values(w, node); + } + var values_list = null; + + if( w.type != "number") + values_list = values.constructor === Array ? values : Object.keys(values); + + var delta = x < 40 ? -1 : x > width - 40 ? 1 : 0; + if (w.type == "number") { + w.value += delta * 0.1 * (w.options.step || 1); + if ( w.options.min != null && w.value < w.options.min ) { + w.value = w.options.min; + } + if ( w.options.max != null && w.value > w.options.max ) { + w.value = w.options.max; + } + } else if (delta) { //clicked in arrow, used for combos + var index = -1; + if(values.constructor === Object) + index = values_list.indexOf( String( w.value ) ) + delta; + else + index = values_list.indexOf( w.value ) + delta; + if (index >= values_list.length) { + index = values_list.length - 1; + } + if (index < 0) { + index = 0; + } + if( values.constructor === Array ) + w.value = values[index]; + else + w.value = index; + } else { //combo clicked + var text_values = values != values_list ? Object.values(values) : values; + var menu = new LiteGraph.ContextMenu(text_values, { + scale: Math.max(1, this.ds.scale), + event: event, + className: "dark", + callback: inner_clicked.bind(w) + }, + ref_window); + function inner_clicked(v, option, event) { + if(values != values_list) + v = text_values.indexOf(v); + this.value = v; + inner_value_change(this, v); + that.dirty_canvas = true; + return false; + } + } + } //end mousedown + else if(event.type == "mouseup" && w.type == "number") + { + var delta = x < 40 ? -1 : x > width - 40 ? 1 : 0; + if (event.click_time < 200 && delta == 0) { + this.prompt("Value",w.value,function(v) { + this.value = Number(v); + inner_value_change(this, this.value); + }.bind(w), + event); + } + } + + if( old_value != w.value ) + setTimeout( + function() { + inner_value_change(this, this.value); + }.bind(w), + 20 + ); + this.dirty_canvas = true; + break; + case "toggle": + if (event.type == "mousedown") { + w.value = !w.value; + setTimeout(function() { + inner_value_change(w, w.value); + }, 20); + } + break; + case "string": + case "text": + if (event.type == "mousedown") { + this.prompt("Value",w.value,function(v) { + this.value = v; + inner_value_change(this, v); + }.bind(w), + event); + } + break; + default: + if (w.mouse) { + w.mouse(ctx, event, [x, y], node); + } + break; + } //end switch + + return w; + } + } + + function inner_value_change(widget, value) { + widget.value = value; + if ( widget.options && widget.options.property && node.properties[widget.options.property] !== undefined ) { + node.setProperty( widget.options.property, value ); + } + if (widget.callback) { + widget.callback(widget.value, that, node, pos, event); + } + } + + return null; + }; + + /** + * draws every group area in the background + * @method drawGroups + **/ + LGraphCanvas.prototype.drawGroups = function(canvas, ctx) { + if (!this.graph) { + return; + } + + var groups = this.graph._groups; + + ctx.save(); + ctx.globalAlpha = 0.5 * this.editor_alpha; + + for (var i = 0; i < groups.length; ++i) { + var group = groups[i]; + + if (!overlapBounding(this.visible_area, group._bounding)) { + continue; + } //out of the visible area + + ctx.fillStyle = group.color || "#335"; + ctx.strokeStyle = group.color || "#335"; + var pos = group._pos; + var size = group._size; + ctx.globalAlpha = 0.25 * this.editor_alpha; + ctx.beginPath(); + ctx.rect(pos[0] + 0.5, pos[1] + 0.5, size[0], size[1]); + ctx.fill(); + ctx.globalAlpha = this.editor_alpha; + ctx.stroke(); + + ctx.beginPath(); + ctx.moveTo(pos[0] + size[0], pos[1] + size[1]); + ctx.lineTo(pos[0] + size[0] - 10, pos[1] + size[1]); + ctx.lineTo(pos[0] + size[0], pos[1] + size[1] - 10); + ctx.fill(); + + var font_size = + group.font_size || LiteGraph.DEFAULT_GROUP_FONT_SIZE; + ctx.font = font_size + "px Arial"; + ctx.fillText(group.title, pos[0] + 4, pos[1] + font_size); + } + + ctx.restore(); + }; + + LGraphCanvas.prototype.adjustNodesSize = function() { + var nodes = this.graph._nodes; + for (var i = 0; i < nodes.length; ++i) { + nodes[i].size = nodes[i].computeSize(); + } + this.setDirty(true, true); + }; + + /** + * resizes the canvas to a given size, if no size is passed, then it tries to fill the parentNode + * @method resize + **/ + LGraphCanvas.prototype.resize = function(width, height) { + if (!width && !height) { + var parent = this.canvas.parentNode; + width = parent.offsetWidth; + height = parent.offsetHeight; + } + + if (this.canvas.width == width && this.canvas.height == height) { + return; + } + + this.canvas.width = width; + this.canvas.height = height; + this.bgcanvas.width = this.canvas.width; + this.bgcanvas.height = this.canvas.height; + this.setDirty(true, true); + }; + + /** + * switches to live mode (node shapes are not rendered, only the content) + * this feature was designed when graphs where meant to create user interfaces + * @method switchLiveMode + **/ + LGraphCanvas.prototype.switchLiveMode = function(transition) { + if (!transition) { + this.live_mode = !this.live_mode; + this.dirty_canvas = true; + this.dirty_bgcanvas = true; + return; + } + + var self = this; + var delta = this.live_mode ? 1.1 : 0.9; + if (this.live_mode) { + this.live_mode = false; + this.editor_alpha = 0.1; + } + + var t = setInterval(function() { + self.editor_alpha *= delta; + self.dirty_canvas = true; + self.dirty_bgcanvas = true; + + if (delta < 1 && self.editor_alpha < 0.01) { + clearInterval(t); + if (delta < 1) { + self.live_mode = true; + } + } + if (delta > 1 && self.editor_alpha > 0.99) { + clearInterval(t); + self.editor_alpha = 1; + } + }, 1); + }; + + LGraphCanvas.prototype.onNodeSelectionChange = function(node) { + return; //disabled + }; + + LGraphCanvas.prototype.touchHandler = function(event) { + //alert("foo"); + var touches = event.changedTouches, + first = touches[0], + type = ""; + + switch (event.type) { + case "touchstart": + type = "mousedown"; + break; + case "touchmove": + type = "mousemove"; + break; + case "touchend": + type = "mouseup"; + break; + default: + return; + } + + //initMouseEvent(type, canBubble, cancelable, view, clickCount, + // screenX, screenY, clientX, clientY, ctrlKey, + // altKey, shiftKey, metaKey, button, relatedTarget); + + var window = this.getCanvasWindow(); + var document = window.document; + + var simulatedEvent = document.createEvent("MouseEvent"); + simulatedEvent.initMouseEvent( + type, + true, + true, + window, + 1, + first.screenX, + first.screenY, + first.clientX, + first.clientY, + false, + false, + false, + false, + 0 /*left*/, + null + ); + first.target.dispatchEvent(simulatedEvent); + event.preventDefault(); + }; + + /* CONTEXT MENU ********************/ + + LGraphCanvas.onGroupAdd = function(info, entry, mouse_event) { + var canvas = LGraphCanvas.active_canvas; + var ref_window = canvas.getCanvasWindow(); + + var group = new LiteGraph.LGraphGroup(); + group.pos = canvas.convertEventToCanvasOffset(mouse_event); + canvas.graph.add(group); + }; + + LGraphCanvas.onMenuAdd = function(node, options, e, prev_menu, callback) { + var canvas = LGraphCanvas.active_canvas; + var ref_window = canvas.getCanvasWindow(); + + var values = LiteGraph.getNodeTypesCategories( canvas.filter ); + var entries = []; + for (var i in values) { + if (values[i]) { + entries.push({ value: values[i], content: values[i], has_submenu: true }); + } + } + + //show categories + var menu = new LiteGraph.ContextMenu( entries, { event: e, callback: inner_clicked, parentMenu: prev_menu }, ref_window ); + + function inner_clicked(v, option, e) { + var category = v.value; + var node_types = LiteGraph.getNodeTypesInCategory( category, canvas.filter ); + var values = []; + for (var i in node_types) { + if (!node_types[i].skip_list) { + values.push({ + content: node_types[i].title, + value: node_types[i].type + }); + } + } + + new LiteGraph.ContextMenu( values, { event: e, callback: inner_create, parentMenu: menu }, ref_window ); + return false; + } + + function inner_create(v, e) { + var first_event = prev_menu.getFirstEvent(); + var node = LiteGraph.createNode(v.value); + if (node) { + node.pos = canvas.convertEventToCanvasOffset(first_event); + canvas.graph.add(node); + } + if(callback) + callback(node); + } + + return false; + }; + + LGraphCanvas.onMenuCollapseAll = function() {}; + + LGraphCanvas.onMenuNodeEdit = function() {}; + + LGraphCanvas.showMenuNodeOptionalInputs = function( + v, + options, + e, + prev_menu, + node + ) { + if (!node) { + return; + } + + var that = this; + var canvas = LGraphCanvas.active_canvas; + var ref_window = canvas.getCanvasWindow(); + + var options = node.optional_inputs; + if (node.onGetInputs) { + options = node.onGetInputs(); + } + + var entries = []; + if (options) { + for (var i in options) { + var entry = options[i]; + if (!entry) { + entries.push(null); + continue; + } + var label = entry[0]; + if (entry[2] && entry[2].label) { + label = entry[2].label; + } + var data = { content: label, value: entry }; + if (entry[1] == LiteGraph.ACTION) { + data.className = "event"; + } + entries.push(data); + } + } + + if (this.onMenuNodeInputs) { + entries = this.onMenuNodeInputs(entries); + } + + if (!entries.length) { + return; + } + + var menu = new LiteGraph.ContextMenu( + entries, + { + event: e, + callback: inner_clicked, + parentMenu: prev_menu, + node: node + }, + ref_window + ); + + function inner_clicked(v, e, prev) { + if (!node) { + return; + } + + if (v.callback) { + v.callback.call(that, node, v, e, prev); + } + + if (v.value) { + node.addInput(v.value[0], v.value[1], v.value[2]); + node.setDirtyCanvas(true, true); + } + } + + return false; + }; + + LGraphCanvas.showMenuNodeOptionalOutputs = function( + v, + options, + e, + prev_menu, + node + ) { + if (!node) { + return; + } + + var that = this; + var canvas = LGraphCanvas.active_canvas; + var ref_window = canvas.getCanvasWindow(); + + var options = node.optional_outputs; + if (node.onGetOutputs) { + options = node.onGetOutputs(); + } + + var entries = []; + if (options) { + for (var i in options) { + var entry = options[i]; + if (!entry) { + //separator? + entries.push(null); + continue; + } + + if ( + node.flags && + node.flags.skip_repeated_outputs && + node.findOutputSlot(entry[0]) != -1 + ) { + continue; + } //skip the ones already on + var label = entry[0]; + if (entry[2] && entry[2].label) { + label = entry[2].label; + } + var data = { content: label, value: entry }; + if (entry[1] == LiteGraph.EVENT) { + data.className = "event"; + } + entries.push(data); + } + } + + if (this.onMenuNodeOutputs) { + entries = this.onMenuNodeOutputs(entries); + } + + if (!entries.length) { + return; + } + + var menu = new LiteGraph.ContextMenu( + entries, + { + event: e, + callback: inner_clicked, + parentMenu: prev_menu, + node: node + }, + ref_window + ); + + function inner_clicked(v, e, prev) { + if (!node) { + return; + } + + if (v.callback) { + v.callback.call(that, node, v, e, prev); + } + + if (!v.value) { + return; + } + + var value = v.value[1]; + + if ( + value && + (value.constructor === Object || value.constructor === Array) + ) { + //submenu why? + var entries = []; + for (var i in value) { + entries.push({ content: i, value: value[i] }); + } + new LiteGraph.ContextMenu(entries, { + event: e, + callback: inner_clicked, + parentMenu: prev_menu, + node: node + }); + return false; + } else { + node.addOutput(v.value[0], v.value[1], v.value[2]); + node.setDirtyCanvas(true, true); + } + } + + return false; + }; + + LGraphCanvas.onShowMenuNodeProperties = function( + value, + options, + e, + prev_menu, + node + ) { + if (!node || !node.properties) { + return; + } + + var that = this; + var canvas = LGraphCanvas.active_canvas; + var ref_window = canvas.getCanvasWindow(); + + var entries = []; + for (var i in node.properties) { + var value = node.properties[i] !== undefined ? node.properties[i] : " "; + if( typeof value == "object" ) + value = JSON.stringify(value); + //value could contain invalid html characters, clean that + value = LGraphCanvas.decodeHTML(value); + entries.push({ + content: + "" + + i + + "" + + "" + + value + + "", + value: i + }); + } + if (!entries.length) { + return; + } + + var menu = new LiteGraph.ContextMenu( + entries, + { + event: e, + callback: inner_clicked, + parentMenu: prev_menu, + allow_html: true, + node: node + }, + ref_window + ); + + function inner_clicked(v, options, e, prev) { + if (!node) { + return; + } + var rect = this.getBoundingClientRect(); + canvas.showEditPropertyValue(node, v.value, { + position: [rect.left, rect.top] + }); + } + + return false; + }; + + LGraphCanvas.decodeHTML = function(str) { + var e = document.createElement("div"); + e.innerText = str; + return e.innerHTML; + }; + + LGraphCanvas.onResizeNode = function(value, options, e, menu, node) { + if (!node) { + return; + } + node.size = node.computeSize(); + node.setDirtyCanvas(true, true); + }; + + LGraphCanvas.prototype.showLinkMenu = function(link, e) { + var that = this; + console.log(link); + var options = ["Add Node",null,"Delete"]; + var menu = new LiteGraph.ContextMenu(options, { + event: e, + title: link.data != null ? link.data.constructor.name : null, + callback: inner_clicked + }); + + function inner_clicked(v,options,e) { + switch (v) { + case "Add Node": + LGraphCanvas.onMenuAdd(null, null, e, menu, function(node){ + console.log("node autoconnect"); + var node_left = that.graph.getNodeById( link.origin_id ); + var node_right = that.graph.getNodeById( link.target_id ); + if(!node.inputs || !node.inputs.length || !node.outputs || !node.outputs.length) + return; + if( node_left.outputs[ link.origin_slot ].type == node.inputs[0].type && node.outputs[0].type == node_right.inputs[0].type ) + { + node_left.connect( link.origin_slot, node, 0 ); + node.connect( 0, node_right, link.target_slot ); + node.pos[0] -= node.size[0] * 0.5; + } + }); + break; + case "Delete": + that.graph.removeLink(link.id); + break; + default: + } + } + + return false; + }; + + LGraphCanvas.onShowPropertyEditor = function(item, options, e, menu, node) { + var input_html = ""; + var property = item.property || "title"; + var value = node[property]; + + var dialog = document.createElement("div"); + dialog.className = "graphdialog"; + dialog.innerHTML = + ""; + var title = dialog.querySelector(".name"); + title.innerText = property; + var input = dialog.querySelector("input"); + if (input) { + input.value = value; + input.addEventListener("blur", function(e) { + this.focus(); + }); + input.addEventListener("keydown", function(e) { + if (e.keyCode != 13) { + return; + } + inner(); + e.preventDefault(); + e.stopPropagation(); + }); + } + + var graphcanvas = LGraphCanvas.active_canvas; + var canvas = graphcanvas.canvas; + + var rect = canvas.getBoundingClientRect(); + var offsetx = -20; + var offsety = -20; + if (rect) { + offsetx -= rect.left; + offsety -= rect.top; + } + + if (event) { + dialog.style.left = event.clientX + offsetx + "px"; + dialog.style.top = event.clientY + offsety + "px"; + } else { + dialog.style.left = canvas.width * 0.5 + offsetx + "px"; + dialog.style.top = canvas.height * 0.5 + offsety + "px"; + } + + var button = dialog.querySelector("button"); + button.addEventListener("click", inner); + canvas.parentNode.appendChild(dialog); + + function inner() { + setValue(input.value); + } + + function setValue(value) { + if (item.type == "Number") { + value = Number(value); + } else if (item.type == "Boolean") { + value = Boolean(value); + } + node[property] = value; + if (dialog.parentNode) { + dialog.parentNode.removeChild(dialog); + } + node.setDirtyCanvas(true, true); + } + }; + + LGraphCanvas.prototype.prompt = function(title, value, callback, event) { + var that = this; + var input_html = ""; + title = title || ""; + + var modified = false; + + var dialog = document.createElement("div"); + dialog.className = "graphdialog rounded"; + dialog.innerHTML = + " "; + dialog.close = function() { + that.prompt_box = null; + if (dialog.parentNode) { + dialog.parentNode.removeChild(dialog); + } + }; + + if (this.ds.scale > 1) { + dialog.style.transform = "scale(" + this.ds.scale + ")"; + } + + dialog.addEventListener("mouseleave", function(e) { + if (!modified) { + dialog.close(); + } + }); + + if (that.prompt_box) { + that.prompt_box.close(); + } + that.prompt_box = dialog; + + var first = null; + var timeout = null; + var selected = null; + + var name_element = dialog.querySelector(".name"); + name_element.innerText = title; + var value_element = dialog.querySelector(".value"); + value_element.value = value; + + var input = dialog.querySelector("input"); + input.addEventListener("keydown", function(e) { + modified = true; + if (e.keyCode == 27) { + //ESC + dialog.close(); + } else if (e.keyCode == 13) { + if (callback) { + callback(this.value); + } + dialog.close(); + } else { + return; + } + e.preventDefault(); + e.stopPropagation(); + }); + + var button = dialog.querySelector("button"); + button.addEventListener("click", function(e) { + if (callback) { + callback(input.value); + } + that.setDirty(true); + dialog.close(); + }); + + var graphcanvas = LGraphCanvas.active_canvas; + var canvas = graphcanvas.canvas; + + var rect = canvas.getBoundingClientRect(); + var offsetx = -20; + var offsety = -20; + if (rect) { + offsetx -= rect.left; + offsety -= rect.top; + } + + if (event) { + dialog.style.left = event.clientX + offsetx + "px"; + dialog.style.top = event.clientY + offsety + "px"; + } else { + dialog.style.left = canvas.width * 0.5 + offsetx + "px"; + dialog.style.top = canvas.height * 0.5 + offsety + "px"; + } + + canvas.parentNode.appendChild(dialog); + setTimeout(function() { + input.focus(); + }, 10); + + return dialog; + }; + + LGraphCanvas.search_limit = -1; + LGraphCanvas.prototype.showSearchBox = function(event) { + var that = this; + var input_html = ""; + var graphcanvas = LGraphCanvas.active_canvas; + var canvas = graphcanvas.canvas; + var root_document = canvas.ownerDocument || document; + + var dialog = document.createElement("div"); + dialog.className = "litegraph litesearchbox graphdialog rounded"; + dialog.innerHTML = + "Search
"; + dialog.close = function() { + that.search_box = null; + root_document.body.focus(); + root_document.body.style.overflow = ""; + + setTimeout(function() { + that.canvas.focus(); + }, 20); //important, if canvas loses focus keys wont be captured + if (dialog.parentNode) { + dialog.parentNode.removeChild(dialog); + } + }; + + var timeout_close = null; + + if (this.ds.scale > 1) { + dialog.style.transform = "scale(" + this.ds.scale + ")"; + } + + dialog.addEventListener("mouseenter", function(e) { + if (timeout_close) { + clearTimeout(timeout_close); + timeout_close = null; + } + }); + + dialog.addEventListener("mouseleave", function(e) { + //dialog.close(); + timeout_close = setTimeout(function() { + dialog.close(); + }, 500); + }); + + if (that.search_box) { + that.search_box.close(); + } + that.search_box = dialog; + + var helper = dialog.querySelector(".helper"); + + var first = null; + var timeout = null; + var selected = null; + + var input = dialog.querySelector("input"); + if (input) { + input.addEventListener("blur", function(e) { + this.focus(); + }); + input.addEventListener("keydown", function(e) { + if (e.keyCode == 38) { + //UP + changeSelection(false); + } else if (e.keyCode == 40) { + //DOWN + changeSelection(true); + } else if (e.keyCode == 27) { + //ESC + dialog.close(); + } else if (e.keyCode == 13) { + if (selected) { + select(selected.innerHTML); + } else if (first) { + select(first); + } else { + dialog.close(); + } + } else { + if (timeout) { + clearInterval(timeout); + } + timeout = setTimeout(refreshHelper, 10); + return; + } + e.preventDefault(); + e.stopPropagation(); + e.stopImmediatePropagation(); + return true; + }); + } + + if( root_document.fullscreenElement ) + root_document.fullscreenElement.appendChild(dialog); + else + { + root_document.body.appendChild(dialog); + root_document.body.style.overflow = "hidden"; + } + + //compute best position + var rect = canvas.getBoundingClientRect(); + + var left = ( event ? event.clientX : (rect.left + rect.width * 0.5) ) - 80; + var top = ( event ? event.clientY : (rect.top + rect.height * 0.5) ) - 20; + dialog.style.left = left + "px"; + dialog.style.top = top + "px"; + + //To avoid out of screen problems + if(event.layerY > (rect.height - 200)) + helper.style.maxHeight = (rect.height - event.layerY - 20) + "px"; + + /* + var offsetx = -20; + var offsety = -20; + if (rect) { + offsetx -= rect.left; + offsety -= rect.top; + } + + if (event) { + dialog.style.left = event.clientX + offsetx + "px"; + dialog.style.top = event.clientY + offsety + "px"; + } else { + dialog.style.left = canvas.width * 0.5 + offsetx + "px"; + dialog.style.top = canvas.height * 0.5 + offsety + "px"; + } + canvas.parentNode.appendChild(dialog); + */ + + input.focus(); + + function select(name) { + if (name) { + if (that.onSearchBoxSelection) { + that.onSearchBoxSelection(name, event, graphcanvas); + } else { + var extra = LiteGraph.searchbox_extras[name.toLowerCase()]; + if (extra) { + name = extra.type; + } + + var node = LiteGraph.createNode(name); + if (node) { + node.pos = graphcanvas.convertEventToCanvasOffset( + event + ); + graphcanvas.graph.add(node); + } + + if (extra && extra.data) { + if (extra.data.properties) { + for (var i in extra.data.properties) { + node.addProperty( i, extra.data.properties[i] ); + } + } + if (extra.data.inputs) { + node.inputs = []; + for (var i in extra.data.inputs) { + node.addOutput( + extra.data.inputs[i][0], + extra.data.inputs[i][1] + ); + } + } + if (extra.data.outputs) { + node.outputs = []; + for (var i in extra.data.outputs) { + node.addOutput( + extra.data.outputs[i][0], + extra.data.outputs[i][1] + ); + } + } + if (extra.data.title) { + node.title = extra.data.title; + } + if (extra.data.json) { + node.configure(extra.data.json); + } + } + } + } + + dialog.close(); + } + + function changeSelection(forward) { + var prev = selected; + if (selected) { + selected.classList.remove("selected"); + } + if (!selected) { + selected = forward + ? helper.childNodes[0] + : helper.childNodes[helper.childNodes.length]; + } else { + selected = forward + ? selected.nextSibling + : selected.previousSibling; + if (!selected) { + selected = prev; + } + } + if (!selected) { + return; + } + selected.classList.add("selected"); + selected.scrollIntoView({block: "end", behavior: "smooth"}); + } + + function refreshHelper() { + timeout = null; + var str = input.value; + first = null; + helper.innerHTML = ""; + if (!str) { + return; + } + + if (that.onSearchBox) { + var list = that.onSearchBox(helper, str, graphcanvas); + if (list) { + for (var i = 0; i < list.length; ++i) { + addResult(list[i]); + } + } + } else { + var c = 0; + str = str.toLowerCase(); + var filter = graphcanvas.filter || graphcanvas.graph.filter; + + //extras + for (var i in LiteGraph.searchbox_extras) { + var extra = LiteGraph.searchbox_extras[i]; + if (extra.desc.toLowerCase().indexOf(str) === -1) { + continue; + } + var ctor = LiteGraph.registered_node_types[ extra.type ]; + if( ctor && ctor.filter && ctor.filter != filter ) + continue; + addResult( extra.desc, "searchbox_extra" ); + if ( LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit ) { + break; + } + } + + var filtered = null; + if (Array.prototype.filter) { //filter supported + var keys = Object.keys( LiteGraph.registered_node_types ); //types + var filtered = keys.filter( inner_test_filter ); + } else { + filtered = []; + for (var i in LiteGraph.registered_node_types) { + if( inner_test_filter(i) ) + filtered.push(i); + } + } + + for (var i = 0; i < filtered.length; i++) { + addResult(filtered[i]); + if ( LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit ) { + break; + } + } + + function inner_test_filter( type ) + { + var ctor = LiteGraph.registered_node_types[ type ]; + if(filter && ctor.filter != filter ) + return false; + return type.toLowerCase().indexOf(str) !== -1; + } + } + + function addResult(type, className) { + var help = document.createElement("div"); + if (!first) { + first = type; + } + help.innerText = type; + help.dataset["type"] = escape(type); + help.className = "litegraph lite-search-item"; + if (className) { + help.className += " " + className; + } + help.addEventListener("click", function(e) { + select(unescape(this.dataset["type"])); + }); + helper.appendChild(help); + } + } + + return dialog; + }; + + LGraphCanvas.prototype.showEditPropertyValue = function( node, property, options ) { + if (!node || node.properties[property] === undefined) { + return; + } + + options = options || {}; + var that = this; + + var info = node.getPropertyInfo(property); + var type = info.type; + + var input_html = ""; + + if (type == "string" || type == "number" || type == "array" || type == "object") { + input_html = ""; + } else if (type == "enum" && info.values) { + input_html = ""; + } else if (type == "boolean") { + input_html = + ""; + } else { + console.warn("unknown type: " + type); + return; + } + + var dialog = this.createDialog( + "" + + property + + "" + + input_html + + "", + options + ); + + if (type == "enum" && info.values) { + var input = dialog.querySelector("select"); + input.addEventListener("change", function(e) { + setValue(e.target.value); + //var index = e.target.value; + //setValue( e.options[e.selectedIndex].value ); + }); + } else if (type == "boolean") { + var input = dialog.querySelector("input"); + if (input) { + input.addEventListener("click", function(e) { + setValue(!!input.checked); + }); + } + } else { + var input = dialog.querySelector("input"); + if (input) { + input.addEventListener("blur", function(e) { + this.focus(); + }); + var v = node.properties[property] !== undefined ? node.properties[property] : ""; + v = JSON.stringify(v); + input.value = v; + input.addEventListener("keydown", function(e) { + if (e.keyCode != 13) { + return; + } + inner(); + e.preventDefault(); + e.stopPropagation(); + }); + } + } + + var button = dialog.querySelector("button"); + button.addEventListener("click", inner); + + function inner() { + setValue(input.value); + } + + function setValue(value) { + if (typeof node.properties[property] == "number") { + value = Number(value); + } + if (type == "array" || type == "object") { + value = JSON.parse(value); + } + node.properties[property] = value; + if (node._graph) { + node._graph._version++; + } + if (node.onPropertyChanged) { + node.onPropertyChanged(property, value); + } + if(options.onclose) + options.onclose(); + dialog.close(); + node.setDirtyCanvas(true, true); + } + + return dialog; + }; + + LGraphCanvas.prototype.createDialog = function(html, options) { + options = options || {}; + + var dialog = document.createElement("div"); + dialog.className = "graphdialog"; + dialog.innerHTML = html; + + var rect = this.canvas.getBoundingClientRect(); + var offsetx = -20; + var offsety = -20; + if (rect) { + offsetx -= rect.left; + offsety -= rect.top; + } + + if (options.position) { + offsetx += options.position[0]; + offsety += options.position[1]; + } else if (options.event) { + offsetx += options.event.clientX; + offsety += options.event.clientY; + } //centered + else { + offsetx += this.canvas.width * 0.5; + offsety += this.canvas.height * 0.5; + } + + dialog.style.left = offsetx + "px"; + dialog.style.top = offsety + "px"; + + this.canvas.parentNode.appendChild(dialog); + + dialog.close = function() { + if (this.parentNode) { + this.parentNode.removeChild(this); + } + }; + + return dialog; + }; + + LGraphCanvas.onMenuNodeCollapse = function(value, options, e, menu, node) { + node.collapse(); + }; + + LGraphCanvas.onMenuNodePin = function(value, options, e, menu, node) { + node.pin(); + }; + + LGraphCanvas.onMenuNodeMode = function(value, options, e, menu, node) { + new LiteGraph.ContextMenu( + ["Always", "On Event", "On Trigger", "Never"], + { event: e, callback: inner_clicked, parentMenu: menu, node: node } + ); + + function inner_clicked(v) { + if (!node) { + return; + } + switch (v) { + case "On Event": + node.mode = LiteGraph.ON_EVENT; + break; + case "On Trigger": + node.mode = LiteGraph.ON_TRIGGER; + break; + case "Never": + node.mode = LiteGraph.NEVER; + break; + case "Always": + default: + node.mode = LiteGraph.ALWAYS; + break; + } + } + + return false; + }; + + LGraphCanvas.onMenuNodeColors = function(value, options, e, menu, node) { + if (!node) { + throw "no node for color"; + } + + var values = []; + values.push({ + value: null, + content: + "No color" + }); + + for (var i in LGraphCanvas.node_colors) { + var color = LGraphCanvas.node_colors[i]; + var value = { + value: i, + content: + "" + + i + + "" + }; + values.push(value); + } + new LiteGraph.ContextMenu(values, { + event: e, + callback: inner_clicked, + parentMenu: menu, + node: node + }); + + function inner_clicked(v) { + if (!node) { + return; + } + + var color = v.value ? LGraphCanvas.node_colors[v.value] : null; + if (color) { + if (node.constructor === LiteGraph.LGraphGroup) { + node.color = color.groupcolor; + } else { + node.color = color.color; + node.bgcolor = color.bgcolor; + } + } else { + delete node.color; + delete node.bgcolor; + } + node.setDirtyCanvas(true, true); + } + + return false; + }; + + LGraphCanvas.onMenuNodeShapes = function(value, options, e, menu, node) { + if (!node) { + throw "no node passed"; + } + + new LiteGraph.ContextMenu(LiteGraph.VALID_SHAPES, { + event: e, + callback: inner_clicked, + parentMenu: menu, + node: node + }); + + function inner_clicked(v) { + if (!node) { + return; + } + node.shape = v; + node.setDirtyCanvas(true); + } + + return false; + }; + + LGraphCanvas.onMenuNodeRemove = function(value, options, e, menu, node) { + if (!node) { + throw "no node passed"; + } + + if (node.removable === false) { + return; + } + + node.graph.remove(node); + node.setDirtyCanvas(true, true); + }; + + LGraphCanvas.onMenuNodeClone = function(value, options, e, menu, node) { + if (node.clonable == false) { + return; + } + var newnode = node.clone(); + if (!newnode) { + return; + } + newnode.pos = [node.pos[0] + 5, node.pos[1] + 5]; + node.graph.add(newnode); + node.setDirtyCanvas(true, true); + }; + + LGraphCanvas.node_colors = { + red: { color: "#322", bgcolor: "#533", groupcolor: "#A88" }, + brown: { color: "#332922", bgcolor: "#593930", groupcolor: "#b06634" }, + green: { color: "#232", bgcolor: "#353", groupcolor: "#8A8" }, + blue: { color: "#223", bgcolor: "#335", groupcolor: "#88A" }, + pale_blue: { + color: "#2a363b", + bgcolor: "#3f5159", + groupcolor: "#3f789e" + }, + cyan: { color: "#233", bgcolor: "#355", groupcolor: "#8AA" }, + purple: { color: "#323", bgcolor: "#535", groupcolor: "#a1309b" }, + yellow: { color: "#432", bgcolor: "#653", groupcolor: "#b58b2a" }, + black: { color: "#222", bgcolor: "#000", groupcolor: "#444" } + }; + + LGraphCanvas.prototype.getCanvasMenuOptions = function() { + var options = null; + if (this.getMenuOptions) { + options = this.getMenuOptions(); + } else { + options = [ + { + content: "Add Node", + has_submenu: true, + callback: LGraphCanvas.onMenuAdd + }, + { content: "Add Group", callback: LGraphCanvas.onGroupAdd } + //{content:"Collapse All", callback: LGraphCanvas.onMenuCollapseAll } + ]; + + if (this._graph_stack && this._graph_stack.length > 0) { + options.push(null, { + content: "Close subgraph", + callback: this.closeSubgraph.bind(this) + }); + } + } + + if (this.getExtraMenuOptions) { + var extra = this.getExtraMenuOptions(this, options); + if (extra) { + options = options.concat(extra); + } + } + + return options; + }; + + //called by processContextMenu to extract the menu list + LGraphCanvas.prototype.getNodeMenuOptions = function(node) { + var options = null; + + if (node.getMenuOptions) { + options = node.getMenuOptions(this); + } else { + options = [ + { + content: "Inputs", + has_submenu: true, + disabled: true, + callback: LGraphCanvas.showMenuNodeOptionalInputs + }, + { + content: "Outputs", + has_submenu: true, + disabled: true, + callback: LGraphCanvas.showMenuNodeOptionalOutputs + }, + null, + { + content: "Properties", + has_submenu: true, + callback: LGraphCanvas.onShowMenuNodeProperties + }, + null, + { + content: "Title", + callback: LGraphCanvas.onShowPropertyEditor + }, + { + content: "Mode", + has_submenu: true, + callback: LGraphCanvas.onMenuNodeMode + }, + { content: "Resize", callback: LGraphCanvas.onResizeNode }, + { + content: "Collapse", + callback: LGraphCanvas.onMenuNodeCollapse + }, + { content: "Pin", callback: LGraphCanvas.onMenuNodePin }, + { + content: "Colors", + has_submenu: true, + callback: LGraphCanvas.onMenuNodeColors + }, + { + content: "Shapes", + has_submenu: true, + callback: LGraphCanvas.onMenuNodeShapes + }, + null + ]; + } + + if (node.onGetInputs) { + var inputs = node.onGetInputs(); + if (inputs && inputs.length) { + options[0].disabled = false; + } + } + + if (node.onGetOutputs) { + var outputs = node.onGetOutputs(); + if (outputs && outputs.length) { + options[1].disabled = false; + } + } + + if (node.getExtraMenuOptions) { + var extra = node.getExtraMenuOptions(this); + if (extra) { + extra.push(null); + options = extra.concat(options); + } + } + + if (node.clonable !== false) { + options.push({ + content: "Clone", + callback: LGraphCanvas.onMenuNodeClone + }); + } + if (node.removable !== false) { + options.push(null, { + content: "Remove", + callback: LGraphCanvas.onMenuNodeRemove + }); + } + + if (node.graph && node.graph.onGetNodeMenuOptions) { + node.graph.onGetNodeMenuOptions(options, node); + } + + return options; + }; + + LGraphCanvas.prototype.getGroupMenuOptions = function(node) { + var o = [ + { content: "Title", callback: LGraphCanvas.onShowPropertyEditor }, + { + content: "Color", + has_submenu: true, + callback: LGraphCanvas.onMenuNodeColors + }, + { + content: "Font size", + property: "font_size", + type: "Number", + callback: LGraphCanvas.onShowPropertyEditor + }, + null, + { content: "Remove", callback: LGraphCanvas.onMenuNodeRemove } + ]; + + return o; + }; + + LGraphCanvas.prototype.processContextMenu = function(node, event) { + var that = this; + var canvas = LGraphCanvas.active_canvas; + var ref_window = canvas.getCanvasWindow(); + + var menu_info = null; + var options = { + event: event, + callback: inner_option_clicked, + extra: node + }; + + if(node) + options.title = node.type; + + //check if mouse is in input + var slot = null; + if (node) { + slot = node.getSlotInPosition(event.canvasX, event.canvasY); + LGraphCanvas.active_node = node; + } + + if (slot) { + //on slot + menu_info = []; + if ( + slot && + slot.output && + slot.output.links && + slot.output.links.length + ) { + menu_info.push({ content: "Disconnect Links", slot: slot }); + } + var _slot = slot.input || slot.output; + menu_info.push( + _slot.locked + ? "Cannot remove" + : { content: "Remove Slot", slot: slot } + ); + menu_info.push( + _slot.nameLocked + ? "Cannot rename" + : { content: "Rename Slot", slot: slot } + ); + options.title = + (slot.input ? slot.input.type : slot.output.type) || "*"; + if (slot.input && slot.input.type == LiteGraph.ACTION) { + options.title = "Action"; + } + if (slot.output && slot.output.type == LiteGraph.EVENT) { + options.title = "Event"; + } + } else { + if (node) { + //on node + menu_info = this.getNodeMenuOptions(node); + } else { + menu_info = this.getCanvasMenuOptions(); + var group = this.graph.getGroupOnPos( + event.canvasX, + event.canvasY + ); + if (group) { + //on group + menu_info.push(null, { + content: "Edit Group", + has_submenu: true, + submenu: { + title: "Group", + extra: group, + options: this.getGroupMenuOptions(group) + } + }); + } + } + } + + //show menu + if (!menu_info) { + return; + } + + var menu = new LiteGraph.ContextMenu(menu_info, options, ref_window); + + function inner_option_clicked(v, options, e) { + if (!v) { + return; + } + + if (v.content == "Remove Slot") { + var info = v.slot; + if (info.input) { + node.removeInput(info.slot); + } else if (info.output) { + node.removeOutput(info.slot); + } + return; + } else if (v.content == "Disconnect Links") { + var info = v.slot; + if (info.output) { + node.disconnectOutput(info.slot); + } else if (info.input) { + node.disconnectInput(info.slot); + } + return; + } else if (v.content == "Rename Slot") { + var info = v.slot; + var slot_info = info.input + ? node.getInputInfo(info.slot) + : node.getOutputInfo(info.slot); + var dialog = that.createDialog( + "Name", + options + ); + var input = dialog.querySelector("input"); + if (input && slot_info) { + input.value = slot_info.label || ""; + } + dialog + .querySelector("button") + .addEventListener("click", function(e) { + if (input.value) { + if (slot_info) { + slot_info.label = input.value; + } + that.setDirty(true); + } + dialog.close(); + }); + } + + //if(v.callback) + // return v.callback.call(that, node, options, e, menu, that, event ); + } + }; + + //API ************************************************* + //like rect but rounded corners + if (typeof(window) != "undefined" && window.CanvasRenderingContext2D) { + window.CanvasRenderingContext2D.prototype.roundRect = function( + x, + y, + width, + height, + radius, + radius_low + ) { + if (radius === undefined) { + radius = 5; + } + + if (radius_low === undefined) { + radius_low = radius; + } + + this.moveTo(x + radius, y); + this.lineTo(x + width - radius, y); + this.quadraticCurveTo(x + width, y, x + width, y + radius); + + this.lineTo(x + width, y + height - radius_low); + this.quadraticCurveTo( + x + width, + y + height, + x + width - radius_low, + y + height + ); + this.lineTo(x + radius_low, y + height); + this.quadraticCurveTo(x, y + height, x, y + height - radius_low); + this.lineTo(x, y + radius); + this.quadraticCurveTo(x, y, x + radius, y); + }; + } + + function compareObjects(a, b) { + for (var i in a) { + if (a[i] != b[i]) { + return false; + } + } + return true; + } + LiteGraph.compareObjects = compareObjects; + + function distance(a, b) { + return Math.sqrt( + (b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1]) + ); + } + LiteGraph.distance = distance; + + function colorToString(c) { + return ( + "rgba(" + + Math.round(c[0] * 255).toFixed() + + "," + + Math.round(c[1] * 255).toFixed() + + "," + + Math.round(c[2] * 255).toFixed() + + "," + + (c.length == 4 ? c[3].toFixed(2) : "1.0") + + ")" + ); + } + LiteGraph.colorToString = colorToString; + + function isInsideRectangle(x, y, left, top, width, height) { + if (left < x && left + width > x && top < y && top + height > y) { + return true; + } + return false; + } + LiteGraph.isInsideRectangle = isInsideRectangle; + + //[minx,miny,maxx,maxy] + function growBounding(bounding, x, y) { + if (x < bounding[0]) { + bounding[0] = x; + } else if (x > bounding[2]) { + bounding[2] = x; + } + + if (y < bounding[1]) { + bounding[1] = y; + } else if (y > bounding[3]) { + bounding[3] = y; + } + } + LiteGraph.growBounding = growBounding; + + //point inside bounding box + function isInsideBounding(p, bb) { + if ( + p[0] < bb[0][0] || + p[1] < bb[0][1] || + p[0] > bb[1][0] || + p[1] > bb[1][1] + ) { + return false; + } + return true; + } + LiteGraph.isInsideBounding = isInsideBounding; + + //bounding overlap, format: [ startx, starty, width, height ] + function overlapBounding(a, b) { + var A_end_x = a[0] + a[2]; + var A_end_y = a[1] + a[3]; + var B_end_x = b[0] + b[2]; + var B_end_y = b[1] + b[3]; + + if ( + a[0] > B_end_x || + a[1] > B_end_y || + A_end_x < b[0] || + A_end_y < b[1] + ) { + return false; + } + return true; + } + LiteGraph.overlapBounding = overlapBounding; + + //Convert a hex value to its decimal value - the inputted hex must be in the + // format of a hex triplet - the kind we use for HTML colours. The function + // will return an array with three values. + function hex2num(hex) { + if (hex.charAt(0) == "#") { + hex = hex.slice(1); + } //Remove the '#' char - if there is one. + hex = hex.toUpperCase(); + var hex_alphabets = "0123456789ABCDEF"; + var value = new Array(3); + var k = 0; + var int1, int2; + for (var i = 0; i < 6; i += 2) { + int1 = hex_alphabets.indexOf(hex.charAt(i)); + int2 = hex_alphabets.indexOf(hex.charAt(i + 1)); + value[k] = int1 * 16 + int2; + k++; + } + return value; + } + + LiteGraph.hex2num = hex2num; + + //Give a array with three values as the argument and the function will return + // the corresponding hex triplet. + function num2hex(triplet) { + var hex_alphabets = "0123456789ABCDEF"; + var hex = "#"; + var int1, int2; + for (var i = 0; i < 3; i++) { + int1 = triplet[i] / 16; + int2 = triplet[i] % 16; + + hex += hex_alphabets.charAt(int1) + hex_alphabets.charAt(int2); + } + return hex; + } + + LiteGraph.num2hex = num2hex; + + /* LiteGraph GUI elements used for canvas editing *************************************/ + + /** + * ContextMenu from LiteGUI + * + * @class ContextMenu + * @constructor + * @param {Array} values (allows object { title: "Nice text", callback: function ... }) + * @param {Object} options [optional] Some options:\ + * - title: title to show on top of the menu + * - callback: function to call when an option is clicked, it receives the item information + * - ignore_item_callbacks: ignores the callback inside the item, it just calls the options.callback + * - event: you can pass a MouseEvent, this way the ContextMenu appears in that position + */ + function ContextMenu(values, options) { + options = options || {}; + this.options = options; + var that = this; + + //to link a menu with its parent + if (options.parentMenu) { + if (options.parentMenu.constructor !== this.constructor) { + console.error( + "parentMenu must be of class ContextMenu, ignoring it" + ); + options.parentMenu = null; + } else { + this.parentMenu = options.parentMenu; + this.parentMenu.lock = true; + this.parentMenu.current_submenu = this; + } + } + + var eventClass = null; + if(options.event) //use strings because comparing classes between windows doesnt work + eventClass = options.event.constructor.name; + if ( eventClass !== "MouseEvent" && + eventClass !== "CustomEvent" && + eventClass !== "PointerEvent" + ) { + console.error( + "Event passed to ContextMenu is not of type MouseEvent or CustomEvent. Ignoring it." + ); + options.event = null; + } + + var root = document.createElement("div"); + root.className = "litegraph litecontextmenu litemenubar-panel"; + if (options.className) { + root.className += " " + options.className; + } + root.style.minWidth = 100; + root.style.minHeight = 100; + root.style.pointerEvents = "none"; + setTimeout(function() { + root.style.pointerEvents = "auto"; + }, 100); //delay so the mouse up event is not caught by this element + + //this prevents the default context browser menu to open in case this menu was created when pressing right button + root.addEventListener( + "mouseup", + function(e) { + e.preventDefault(); + return true; + }, + true + ); + root.addEventListener( + "contextmenu", + function(e) { + if (e.button != 2) { + //right button + return false; + } + e.preventDefault(); + return false; + }, + true + ); + + root.addEventListener( + "mousedown", + function(e) { + if (e.button == 2) { + that.close(); + e.preventDefault(); + return true; + } + }, + true + ); + + function on_mouse_wheel(e) { + var pos = parseInt(root.style.top); + root.style.top = + (pos + e.deltaY * options.scroll_speed).toFixed() + "px"; + e.preventDefault(); + return true; + } + + if (!options.scroll_speed) { + options.scroll_speed = 0.1; + } + + root.addEventListener("wheel", on_mouse_wheel, true); + root.addEventListener("mousewheel", on_mouse_wheel, true); + + this.root = root; + + //title + if (options.title) { + var element = document.createElement("div"); + element.className = "litemenu-title"; + element.innerHTML = options.title; + root.appendChild(element); + } + + //entries + var num = 0; + for (var i in values) { + var name = values.constructor == Array ? values[i] : i; + if (name != null && name.constructor !== String) { + name = name.content === undefined ? String(name) : name.content; + } + var value = values[i]; + this.addItem(name, value, options); + num++; + } + + //close on leave + root.addEventListener("mouseleave", function(e) { + if (that.lock) { + return; + } + if (root.closing_timer) { + clearTimeout(root.closing_timer); + } + root.closing_timer = setTimeout(that.close.bind(that, e), 500); + //that.close(e); + }); + + root.addEventListener("mouseenter", function(e) { + if (root.closing_timer) { + clearTimeout(root.closing_timer); + } + }); + + //insert before checking position + var root_document = document; + if (options.event) { + root_document = options.event.target.ownerDocument; + } + + if (!root_document) { + root_document = document; + } + + if( root_document.fullscreenElement ) + root_document.fullscreenElement.appendChild(root); + else + root_document.body.appendChild(root); + + //compute best position + var left = options.left || 0; + var top = options.top || 0; + if (options.event) { + left = options.event.clientX - 10; + top = options.event.clientY - 10; + if (options.title) { + top -= 20; + } + + if (options.parentMenu) { + var rect = options.parentMenu.root.getBoundingClientRect(); + left = rect.left + rect.width; + } + + var body_rect = document.body.getBoundingClientRect(); + var root_rect = root.getBoundingClientRect(); + + if (left > body_rect.width - root_rect.width - 10) { + left = body_rect.width - root_rect.width - 10; + } + if (top > body_rect.height - root_rect.height - 10) { + top = body_rect.height - root_rect.height - 10; + } + } + + root.style.left = left + "px"; + root.style.top = top + "px"; + + if (options.scale) { + root.style.transform = "scale(" + options.scale + ")"; + } + } + + ContextMenu.prototype.addItem = function(name, value, options) { + var that = this; + options = options || {}; + + var element = document.createElement("div"); + element.className = "litemenu-entry submenu"; + + var disabled = false; + + if (value === null) { + element.classList.add("separator"); + //element.innerHTML = "
" + //continue; + } else { + element.innerHTML = value && value.title ? value.title : name; + element.value = value; + + if (value) { + if (value.disabled) { + disabled = true; + element.classList.add("disabled"); + } + if (value.submenu || value.has_submenu) { + element.classList.add("has_submenu"); + } + } + + if (typeof value == "function") { + element.dataset["value"] = name; + element.onclick_callback = value; + } else { + element.dataset["value"] = value; + } + + if (value.className) { + element.className += " " + value.className; + } + } + + this.root.appendChild(element); + if (!disabled) { + element.addEventListener("click", inner_onclick); + } + if (options.autoopen) { + element.addEventListener("mouseenter", inner_over); + } + + function inner_over(e) { + var value = this.value; + if (!value || !value.has_submenu) { + return; + } + //if it is a submenu, autoopen like the item was clicked + inner_onclick.call(this, e); + } + + //menu option clicked + function inner_onclick(e) { + var value = this.value; + var close_parent = true; + + if (that.current_submenu) { + that.current_submenu.close(e); + } + + //global callback + if (options.callback) { + var r = options.callback.call( + this, + value, + options, + e, + that, + options.node + ); + if (r === true) { + close_parent = false; + } + } + + //special cases + if (value) { + if ( + value.callback && + !options.ignore_item_callbacks && + value.disabled !== true + ) { + //item callback + var r = value.callback.call( + this, + value, + options, + e, + that, + options.extra + ); + if (r === true) { + close_parent = false; + } + } + if (value.submenu) { + if (!value.submenu.options) { + throw "ContextMenu submenu needs options"; + } + var submenu = new that.constructor(value.submenu.options, { + callback: value.submenu.callback, + event: e, + parentMenu: that, + ignore_item_callbacks: + value.submenu.ignore_item_callbacks, + title: value.submenu.title, + extra: value.submenu.extra, + autoopen: options.autoopen + }); + close_parent = false; + } + } + + if (close_parent && !that.lock) { + that.close(); + } + } + + return element; + }; + + ContextMenu.prototype.close = function(e, ignore_parent_menu) { + if (this.root.parentNode) { + this.root.parentNode.removeChild(this.root); + } + if (this.parentMenu && !ignore_parent_menu) { + this.parentMenu.lock = false; + this.parentMenu.current_submenu = null; + if (e === undefined) { + this.parentMenu.close(); + } else if ( + e && + !ContextMenu.isCursorOverElement(e, this.parentMenu.root) + ) { + ContextMenu.trigger(this.parentMenu.root, "mouseleave", e); + } + } + if (this.current_submenu) { + this.current_submenu.close(e, true); + } + + if (this.root.closing_timer) { + clearTimeout(this.root.closing_timer); + } + }; + + //this code is used to trigger events easily (used in the context menu mouseleave + ContextMenu.trigger = function(element, event_name, params, origin) { + var evt = document.createEvent("CustomEvent"); + evt.initCustomEvent(event_name, true, true, params); //canBubble, cancelable, detail + evt.srcElement = origin; + if (element.dispatchEvent) { + element.dispatchEvent(evt); + } else if (element.__events) { + element.__events.dispatchEvent(evt); + } + //else nothing seems binded here so nothing to do + return evt; + }; + + //returns the top most menu + ContextMenu.prototype.getTopMenu = function() { + if (this.options.parentMenu) { + return this.options.parentMenu.getTopMenu(); + } + return this; + }; + + ContextMenu.prototype.getFirstEvent = function() { + if (this.options.parentMenu) { + return this.options.parentMenu.getFirstEvent(); + } + return this.options.event; + }; + + ContextMenu.isCursorOverElement = function(event, element) { + var left = event.clientX; + var top = event.clientY; + var rect = element.getBoundingClientRect(); + if (!rect) { + return false; + } + if ( + top > rect.top && + top < rect.top + rect.height && + left > rect.left && + left < rect.left + rect.width + ) { + return true; + } + return false; + }; + + LiteGraph.ContextMenu = ContextMenu; + + LiteGraph.closeAllContextMenus = function(ref_window) { + ref_window = ref_window || window; + + var elements = ref_window.document.querySelectorAll(".litecontextmenu"); + if (!elements.length) { + return; + } + + var result = []; + for (var i = 0; i < elements.length; i++) { + result.push(elements[i]); + } + + for (var i in result) { + if (result[i].close) { + result[i].close(); + } else if (result[i].parentNode) { + result[i].parentNode.removeChild(result[i]); + } + } + }; + + LiteGraph.extendClass = function(target, origin) { + for (var i in origin) { + //copy class properties + if (target.hasOwnProperty(i)) { + continue; + } + target[i] = origin[i]; + } + + if (origin.prototype) { + //copy prototype properties + for (var i in origin.prototype) { + //only enumerable + if (!origin.prototype.hasOwnProperty(i)) { + continue; + } + + if (target.prototype.hasOwnProperty(i)) { + //avoid overwriting existing ones + continue; + } + + //copy getters + if (origin.prototype.__lookupGetter__(i)) { + target.prototype.__defineGetter__( + i, + origin.prototype.__lookupGetter__(i) + ); + } else { + target.prototype[i] = origin.prototype[i]; + } + + //and setters + if (origin.prototype.__lookupSetter__(i)) { + target.prototype.__defineSetter__( + i, + origin.prototype.__lookupSetter__(i) + ); + } + } + } + }; + + //used by some widgets to render a curve editor + function CurveEditor( points ) + { + this.points = points; + this.selected = -1; + this.nearest = -1; + this.size = null; //stores last size used + this.must_update = true; + this.margin = 5; + } + + CurveEditor.sampleCurve = function(f,points) + { + if(!points) + return; + for(var i = 0; i < points.length - 1; ++i) + { + var p = points[i]; + var pn = points[i+1]; + if(pn[0] < f) + continue; + var r = (pn[0] - p[0]); + if( Math.abs(r) < 0.00001 ) + return p[1]; + var local_f = (f - p[0]) / r; + return p[1] * (1.0 - local_f) + pn[1] * local_f; + } + return 0; + } + + CurveEditor.prototype.draw = function( ctx, size, graphcanvas, background_color, line_color, inactive ) + { + var points = this.points; + if(!points) + return; + this.size = size; + var w = size[0] - this.margin * 2; + var h = size[1] - this.margin * 2; + + line_color = line_color || "#666"; + + ctx.save(); + ctx.translate(this.margin,this.margin); + + if(background_color) + { + ctx.fillStyle = "#111"; + ctx.fillRect(0,0,w,h); + ctx.fillStyle = "#222"; + ctx.fillRect(w*0.5,0,1,h); + ctx.strokeStyle = "#333"; + ctx.strokeRect(0,0,w,h); + } + ctx.strokeStyle = line_color; + if(inactive) + ctx.globalAlpha = 0.5; + ctx.beginPath(); + for(var i = 0; i < points.length; ++i) + { + var p = points[i]; + ctx.lineTo( p[0] * w, (1.0 - p[1]) * h ); + } + ctx.stroke(); + ctx.globalAlpha = 1; + if(!inactive) + for(var i = 0; i < points.length; ++i) + { + var p = points[i]; + ctx.fillStyle = this.selected == i ? "#FFF" : (this.nearest == i ? "#DDD" : "#AAA"); + ctx.beginPath(); + ctx.arc( p[0] * w, (1.0 - p[1]) * h, 2, 0, Math.PI * 2 ); + ctx.fill(); + } + ctx.restore(); + } + + //localpos is mouse in curve editor space + CurveEditor.prototype.onMouseDown = function( localpos, graphcanvas ) + { + var points = this.points; + if(!points) + return; + if( localpos[1] < 0 ) + return; + + //this.captureInput(true); + var w = this.size[0] - this.margin * 2; + var h = this.size[1] - this.margin * 2; + var x = localpos[0] - this.margin; + var y = localpos[1] - this.margin; + var pos = [x,y]; + var max_dist = 30 / graphcanvas.ds.scale; + //search closer one + this.selected = this.getCloserPoint(pos, max_dist); + //create one + if(this.selected == -1) + { + var point = [x / w, 1 - y / h]; + points.push(point); + points.sort(function(a,b){ return a[0] - b[0]; }); + this.selected = points.indexOf(point); + this.must_update = true; + } + if(this.selected != -1) + return true; + } + + CurveEditor.prototype.onMouseMove = function( localpos, graphcanvas ) + { + var points = this.points; + if(!points) + return; + var s = this.selected; + if(s < 0) + return; + var x = (localpos[0] - this.margin) / (this.size[0] - this.margin * 2 ); + var y = (localpos[1] - this.margin) / (this.size[1] - this.margin * 2 ); + var curvepos = [(localpos[0] - this.margin),(localpos[1] - this.margin)]; + var max_dist = 30 / graphcanvas.ds.scale; + this._nearest = this.getCloserPoint(curvepos, max_dist); + var point = points[s]; + if(point) + { + var is_edge_point = s == 0 || s == points.length - 1; + if( !is_edge_point && (localpos[0] < -10 || localpos[0] > this.size[0] + 10 || localpos[1] < -10 || localpos[1] > this.size[1] + 10) ) + { + points.splice(s,1); + this.selected = -1; + return; + } + if( !is_edge_point ) //not edges + point[0] = Math.clamp(x,0,1); + else + point[0] = s == 0 ? 0 : 1; + point[1] = 1.0 - Math.clamp(y,0,1); + points.sort(function(a,b){ return a[0] - b[0]; }); + this.selected = points.indexOf(point); + this.must_update = true; + } + } + + CurveEditor.prototype.onMouseUp = function( localpos, graphcanvas ) + { + this.selected = -1; + return false; + } + + CurveEditor.prototype.getCloserPoint = function(pos, max_dist) + { + var points = this.points; + if(!points) + return -1; + max_dist = max_dist || 30; + var w = (this.size[0] - this.margin * 2); + var h = (this.size[1] - this.margin * 2); + var num = points.length; + var p2 = [0,0]; + var min_dist = 1000000; + var closest = -1; + var last_valid = -1; + for(var i = 0; i < num; ++i) + { + var p = points[i]; + p2[0] = p[0] * w; + p2[1] = (1.0 - p[1]) * h; + if(p2[0] < pos[0]) + last_valid = i; + var dist = vec2.distance(pos,p2); + if(dist > min_dist || dist > max_dist) + continue; + closest = i; + min_dist = dist; + } + return closest; + } + + LiteGraph.CurveEditor = CurveEditor; + + //used to create nodes from wrapping functions + LiteGraph.getParameterNames = function(func) { + return (func + "") + .replace(/[/][/].*$/gm, "") // strip single-line comments + .replace(/\s+/g, "") // strip white space + .replace(/[/][*][^/*]*[*][/]/g, "") // strip multi-line comments /**/ + .split("){", 1)[0] + .replace(/^[^(]*[(]/, "") // extract the parameters + .replace(/=[^,]+/g, "") // strip any ES6 defaults + .split(",") + .filter(Boolean); // split & filter [""] + }; + + Math.clamp = function(v, a, b) { + return a > v ? a : b < v ? b : v; + }; + + if (typeof window != "undefined" && !window["requestAnimationFrame"]) { + window.requestAnimationFrame = + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + function(callback) { + window.setTimeout(callback, 1000 / 60); + }; + } +})(this); + +if (typeof exports != "undefined") { + exports.LiteGraph = this.LiteGraph; +} + +//basic nodes +(function(global) { + var LiteGraph = global.LiteGraph; + + //Constant + function Time() { + this.addOutput("in ms", "number"); + this.addOutput("in sec", "number"); + } + + Time.title = "Time"; + Time.desc = "Time"; + + Time.prototype.onExecute = function() { + this.setOutputData(0, this.graph.globaltime * 1000); + this.setOutputData(1, this.graph.globaltime); + }; + + LiteGraph.registerNodeType("basic/time", Time); + + //Subgraph: a node that contains a graph + function Subgraph() { + var that = this; + this.size = [140, 80]; + this.properties = { enabled: true }; + this.enabled = true; + + //create inner graph + this.subgraph = new LiteGraph.LGraph(); + this.subgraph._subgraph_node = this; + this.subgraph._is_subgraph = true; + + this.subgraph.onTrigger = this.onSubgraphTrigger.bind(this); + + this.subgraph.onInputAdded = this.onSubgraphNewInput.bind(this); + this.subgraph.onInputRenamed = this.onSubgraphRenamedInput.bind(this); + this.subgraph.onInputTypeChanged = this.onSubgraphTypeChangeInput.bind( + this + ); + this.subgraph.onInputRemoved = this.onSubgraphRemovedInput.bind(this); + + this.subgraph.onOutputAdded = this.onSubgraphNewOutput.bind(this); + this.subgraph.onOutputRenamed = this.onSubgraphRenamedOutput.bind(this); + this.subgraph.onOutputTypeChanged = this.onSubgraphTypeChangeOutput.bind( + this + ); + this.subgraph.onOutputRemoved = this.onSubgraphRemovedOutput.bind(this); + } + + Subgraph.title = "Subgraph"; + Subgraph.desc = "Graph inside a node"; + Subgraph.title_color = "#334"; + + Subgraph.prototype.onGetInputs = function() { + return [["enabled", "boolean"]]; + }; + + Subgraph.prototype.onDrawTitle = function(ctx) { + if (this.flags.collapsed) { + return; + } + + ctx.fillStyle = "#555"; + var w = LiteGraph.NODE_TITLE_HEIGHT; + var x = this.size[0] - w; + ctx.fillRect(x, -w, w, w); + ctx.fillStyle = "#333"; + ctx.beginPath(); + ctx.moveTo(x + w * 0.2, -w * 0.6); + ctx.lineTo(x + w * 0.8, -w * 0.6); + ctx.lineTo(x + w * 0.5, -w * 0.3); + ctx.fill(); + }; + + Subgraph.prototype.onDblClick = function(e, pos, graphcanvas) { + var that = this; + setTimeout(function() { + graphcanvas.openSubgraph(that.subgraph); + }, 10); + }; + + Subgraph.prototype.onMouseDown = function(e, pos, graphcanvas) { + if ( + !this.flags.collapsed && + pos[0] > this.size[0] - LiteGraph.NODE_TITLE_HEIGHT && + pos[1] < 0 + ) { + var that = this; + setTimeout(function() { + graphcanvas.openSubgraph(that.subgraph); + }, 10); + } + }; + + Subgraph.prototype.onAction = function(action, param) { + this.subgraph.onAction(action, param); + }; + + Subgraph.prototype.onExecute = function() { + this.enabled = this.getInputOrProperty("enabled"); + if (!this.enabled) { + return; + } + + //send inputs to subgraph global inputs + if (this.inputs) { + for (var i = 0; i < this.inputs.length; i++) { + var input = this.inputs[i]; + var value = this.getInputData(i); + this.subgraph.setInputData(input.name, value); + } + } + + //execute + this.subgraph.runStep(); + + //send subgraph global outputs to outputs + if (this.outputs) { + for (var i = 0; i < this.outputs.length; i++) { + var output = this.outputs[i]; + var value = this.subgraph.getOutputData(output.name); + this.setOutputData(i, value); + } + } + }; + + Subgraph.prototype.sendEventToAllNodes = function(eventname, param, mode) { + if (this.enabled) { + this.subgraph.sendEventToAllNodes(eventname, param, mode); + } + }; + + //**** INPUTS *********************************** + Subgraph.prototype.onSubgraphTrigger = function(event, param) { + var slot = this.findOutputSlot(event); + if (slot != -1) { + this.triggerSlot(slot); + } + }; + + Subgraph.prototype.onSubgraphNewInput = function(name, type) { + var slot = this.findInputSlot(name); + if (slot == -1) { + //add input to the node + this.addInput(name, type); + } + }; + + Subgraph.prototype.onSubgraphRenamedInput = function(oldname, name) { + var slot = this.findInputSlot(oldname); + if (slot == -1) { + return; + } + var info = this.getInputInfo(slot); + info.name = name; + }; + + Subgraph.prototype.onSubgraphTypeChangeInput = function(name, type) { + var slot = this.findInputSlot(name); + if (slot == -1) { + return; + } + var info = this.getInputInfo(slot); + info.type = type; + }; + + Subgraph.prototype.onSubgraphRemovedInput = function(name) { + var slot = this.findInputSlot(name); + if (slot == -1) { + return; + } + this.removeInput(slot); + }; + + //**** OUTPUTS *********************************** + Subgraph.prototype.onSubgraphNewOutput = function(name, type) { + var slot = this.findOutputSlot(name); + if (slot == -1) { + this.addOutput(name, type); + } + }; + + Subgraph.prototype.onSubgraphRenamedOutput = function(oldname, name) { + var slot = this.findOutputSlot(oldname); + if (slot == -1) { + return; + } + var info = this.getOutputInfo(slot); + info.name = name; + }; + + Subgraph.prototype.onSubgraphTypeChangeOutput = function(name, type) { + var slot = this.findOutputSlot(name); + if (slot == -1) { + return; + } + var info = this.getOutputInfo(slot); + info.type = type; + }; + + Subgraph.prototype.onSubgraphRemovedOutput = function(name) { + var slot = this.findInputSlot(name); + if (slot == -1) { + return; + } + this.removeOutput(slot); + }; + // ***************************************************** + + Subgraph.prototype.getExtraMenuOptions = function(graphcanvas) { + var that = this; + return [ + { + content: "Open", + callback: function() { + graphcanvas.openSubgraph(that.subgraph); + } + } + ]; + }; + + Subgraph.prototype.onResize = function(size) { + size[1] += 20; + }; + + Subgraph.prototype.serialize = function() { + var data = LiteGraph.LGraphNode.prototype.serialize.call(this); + data.subgraph = this.subgraph.serialize(); + return data; + }; + //no need to define node.configure, the default method detects node.subgraph and passes the object to node.subgraph.configure() + + Subgraph.prototype.clone = function() { + var node = LiteGraph.createNode(this.type); + var data = this.serialize(); + delete data["id"]; + delete data["inputs"]; + delete data["outputs"]; + node.configure(data); + return node; + }; + + LiteGraph.Subgraph = Subgraph; + LiteGraph.registerNodeType("graph/subgraph", Subgraph); + + //Input for a subgraph + function GraphInput() { + this.addOutput("", "number"); + + this.name_in_graph = ""; + this.properties = { + name: "", + type: "number", + value: 0 + }; + + var that = this; + + this.name_widget = this.addWidget( + "text", + "Name", + this.properties.name, + function(v) { + if (!v) { + return; + } + that.setProperty("name",v); + } + ); + this.type_widget = this.addWidget( + "text", + "Type", + this.properties.type, + function(v) { + that.setProperty("type",v); + } + ); + + this.value_widget = this.addWidget( + "number", + "Value", + this.properties.value, + function(v) { + that.setProperty("value",v); + } + ); + + this.widgets_up = true; + this.size = [180, 90]; + } + + GraphInput.title = "Input"; + GraphInput.desc = "Input of the graph"; + + GraphInput.prototype.onConfigure = function() + { + this.updateType(); + } + + GraphInput.prototype.updateType = function() + { + var type = this.properties.type; + this.type_widget.value = type; + if(this.outputs[0].type != type) + { + this.outputs[0].type = type; + this.disconnectOutput(0); + } + if(type == "number") + { + this.value_widget.type = "number"; + this.value_widget.value = 0; + } + else if(type == "boolean") + { + this.value_widget.type = "toggle"; + this.value_widget.value = true; + } + else if(type == "string") + { + this.value_widget.type = "text"; + this.value_widget.value = ""; + } + else + { + this.value_widget.type = null; + this.value_widget.value = null; + } + this.properties.value = this.value_widget.value; + } + + GraphInput.prototype.onPropertyChanged = function(name,v) + { + if( name == "name" ) + { + if (v == "" || v == this.name_in_graph || v == "enabled") { + return false; + } + if(this.graph) + { + if (this.name_in_graph) { + //already added + this.graph.renameInput( this.name_in_graph, v ); + } else { + this.graph.addInput( v, this.properties.type ); + } + } //what if not?! + this.name_widget.value = v; + this.name_in_graph = v; + } + else if( name == "type" ) + { + v = v || ""; + this.updateType(v); + } + else if( name == "value" ) + { + } + } + + GraphInput.prototype.getTitle = function() { + if (this.flags.collapsed) { + return this.properties.name; + } + return this.title; + }; + + GraphInput.prototype.onAction = function(action, param) { + if (this.properties.type == LiteGraph.EVENT) { + this.triggerSlot(0, param); + } + }; + + GraphInput.prototype.onExecute = function() { + var name = this.properties.name; + //read from global input + var data = this.graph.inputs[name]; + if (!data) { + this.setOutputData(0, this.properties.value ); + return; + } + + this.setOutputData(0, data.value !== undefined ? data.value : this.properties.value ); + }; + + GraphInput.prototype.onRemoved = function() { + if (this.name_in_graph) { + this.graph.removeInput(this.name_in_graph); + } + }; + + LiteGraph.GraphInput = GraphInput; + LiteGraph.registerNodeType("graph/input", GraphInput); + + //Output for a subgraph + function GraphOutput() { + this.addInput("", ""); + + this.name_in_graph = ""; + this.properties = {}; + var that = this; + + Object.defineProperty(this.properties, "name", { + get: function() { + return that.name_in_graph; + }, + set: function(v) { + if (v == "" || v == that.name_in_graph) { + return; + } + if (that.name_in_graph) { + //already added + that.graph.renameOutput(that.name_in_graph, v); + } else { + that.graph.addOutput(v, that.properties.type); + } + that.name_widget.value = v; + that.name_in_graph = v; + }, + enumerable: true + }); + + Object.defineProperty(this.properties, "type", { + get: function() { + return that.inputs[0].type; + }, + set: function(v) { + if (v == "action" || v == "event") { + v = LiteGraph.ACTION; + } + that.inputs[0].type = v; + if (that.name_in_graph) { + //already added + that.graph.changeOutputType( + that.name_in_graph, + that.inputs[0].type + ); + } + that.type_widget.value = v || ""; + }, + enumerable: true + }); + + this.name_widget = this.addWidget("text","Name",this.properties.name,"name"); + this.type_widget = this.addWidget("text","Type",this.properties.type,"type"); + this.widgets_up = true; + this.size = [180, 60]; + } + + GraphOutput.title = "Output"; + GraphOutput.desc = "Output of the graph"; + + GraphOutput.prototype.onExecute = function() { + this._value = this.getInputData(0); + this.graph.setOutputData(this.properties.name, this._value); + }; + + GraphOutput.prototype.onAction = function(action, param) { + if (this.properties.type == LiteGraph.ACTION) { + this.graph.trigger(this.properties.name, param); + } + }; + + GraphOutput.prototype.onRemoved = function() { + if (this.name_in_graph) { + this.graph.removeOutput(this.name_in_graph); + } + }; + + GraphOutput.prototype.getTitle = function() { + if (this.flags.collapsed) { + return this.properties.name; + } + return this.title; + }; + + LiteGraph.GraphOutput = GraphOutput; + LiteGraph.registerNodeType("graph/output", GraphOutput); + + //Constant + function ConstantNumber() { + this.addOutput("value", "number"); + this.addProperty("value", 1.0); + this.widget = this.addWidget("number","value",1,"value"); + this.widgets_up = true; + this.size = [180, 30]; + } + + ConstantNumber.title = "Const Number"; + ConstantNumber.desc = "Constant number"; + + ConstantNumber.prototype.onExecute = function() { + this.setOutputData(0, parseFloat(this.properties["value"])); + }; + + ConstantNumber.prototype.getTitle = function() { + if (this.flags.collapsed) { + return this.properties.value; + } + return this.title; + }; + + ConstantNumber.prototype.setValue = function(v) + { + this.setProperty("value",v); + } + + ConstantNumber.prototype.onDrawBackground = function(ctx) { + //show the current value + this.outputs[0].label = this.properties["value"].toFixed(3); + }; + + LiteGraph.registerNodeType("basic/const", ConstantNumber); + + function ConstantBoolean() { + this.addOutput("", "boolean"); + this.addProperty("value", true); + this.widget = this.addWidget("toggle","value",true,"value"); + this.widgets_up = true; + this.size = [140, 30]; + } + + ConstantBoolean.title = "Const Boolean"; + ConstantBoolean.desc = "Constant boolean"; + ConstantBoolean.prototype.getTitle = ConstantNumber.prototype.getTitle; + + ConstantBoolean.prototype.onExecute = function() { + this.setOutputData(0, this.properties["value"]); + }; + + ConstantBoolean.prototype.setValue = ConstantNumber.prototype.setValue; + + ConstantBoolean.prototype.onGetInputs = function() { + return [["toggle", LiteGraph.ACTION]]; + }; + + ConstantBoolean.prototype.onAction = function(action) + { + this.setValue( !this.properties.value ); + } + + LiteGraph.registerNodeType("basic/boolean", ConstantBoolean); + + function ConstantString() { + this.addOutput("", "string"); + this.addProperty("value", ""); + this.widget = this.addWidget("text","value","","value"); //link to property value + this.widgets_up = true; + this.size = [180, 30]; + } + + ConstantString.title = "Const String"; + ConstantString.desc = "Constant string"; + + ConstantString.prototype.getTitle = ConstantNumber.prototype.getTitle; + + ConstantString.prototype.onExecute = function() { + this.setOutputData(0, this.properties["value"]); + }; + + ConstantString.prototype.setValue = ConstantNumber.prototype.setValue; + + ConstantString.prototype.onDropFile = function(file) + { + var that = this; + var reader = new FileReader(); + reader.onload = function(e) + { + that.setProperty("value",e.target.result); + } + reader.readAsText(file); + } + + LiteGraph.registerNodeType("basic/string", ConstantString); + + function ConstantFile() { + this.addInput("url", ""); + this.addOutput("", ""); + this.addProperty("url", ""); + this.addProperty("type", "text"); + this.widget = this.addWidget("text","url","","url"); + this._data = null; + } + + ConstantFile.title = "Const File"; + ConstantFile.desc = "Fetches a file from an url"; + ConstantFile["@type"] = { type: "enum", values: ["text","arraybuffer","blob","json"] }; + + ConstantFile.prototype.onPropertyChanged = function(name, value) { + if (name == "url") + { + if( value == null || value == "") + this._data = null; + else + { + this.fetchFile(value); + } + } + } + + ConstantFile.prototype.onExecute = function() { + var url = this.getInputData(0) || this.properties.url; + if(url && (url != this._url || this._type != this.properties.type)) + this.fetchFile(url); + this.setOutputData(0, this._data ); + }; + + ConstantFile.prototype.setValue = ConstantNumber.prototype.setValue; + + ConstantFile.prototype.fetchFile = function(url) { + var that = this; + if(!url || url.constructor !== String) + { + that._data = null; + that.boxcolor = null; + return; + } + + this._url = url; + this._type = this.properties.type; + if (url.substr(0, 4) == "http" && LiteGraph.proxy) { + url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); + } + fetch(url) + .then(function(response) { + if(!response.ok) + throw new Error("File not found"); + + if(that.properties.type == "arraybuffer") + return response.arrayBuffer(); + else if(that.properties.type == "text") + return response.text(); + else if(that.properties.type == "json") + return response.json(); + else if(that.properties.type == "blob") + return response.blob(); + }) + .then(function(data) { + that._data = data; + that.boxcolor = "#AEA"; + }) + .catch(function(error) { + that._data = null; + that.boxcolor = "red"; + console.error("error fetching file:",url); + }); + }; + + ConstantFile.prototype.onDropFile = function(file) + { + var that = this; + this._url = file.name; + this._type = this.properties.type; + this.properties.url = file.name; + var reader = new FileReader(); + reader.onload = function(e) + { + that.boxcolor = "#AEA"; + var v = e.target.result; + if( that.properties.type == "json" ) + v = JSON.parse(v); + that._data = v; + } + if(that.properties.type == "arraybuffer") + reader.readAsArrayBuffer(file); + else if(that.properties.type == "text" || that.properties.type == "json") + reader.readAsText(file); + else if(that.properties.type == "blob") + return reader.readAsBinaryString(file); + } + + LiteGraph.registerNodeType("basic/file", ConstantFile); + + //to store json objects + function ConstantData() { + this.addOutput("", ""); + this.addProperty("value", ""); + this.widget = this.addWidget("text","json","","value"); + this.widgets_up = true; + this.size = [140, 30]; + this._value = null; + } + + ConstantData.title = "Const Data"; + ConstantData.desc = "Constant Data"; + + ConstantData.prototype.onPropertyChanged = function(name, value) { + this.widget.value = value; + if (value == null || value == "") { + return; + } + + try { + this._value = JSON.parse(value); + this.boxcolor = "#AEA"; + } catch (err) { + this.boxcolor = "red"; + } + }; + + ConstantData.prototype.onExecute = function() { + this.setOutputData(0, this._value); + }; + + ConstantData.prototype.setValue = ConstantNumber.prototype.setValue; + + LiteGraph.registerNodeType("basic/data", ConstantData); + + //to store json objects + function ConstantArray() { + this.addInput("", ""); + this.addOutput("", "array"); + this.addProperty("value", ""); + this.widget = this.addWidget("text","array","","value"); + this.widgets_up = true; + this.size = [140, 30]; + this._value = null; + } + + ConstantArray.title = "Const Array"; + ConstantArray.desc = "Constant Array"; + + ConstantArray.prototype.onPropertyChanged = function(name, value) { + this.widget.value = value; + if (value == null || value == "") { + return; + } + + try { + this._value = JSON.parse(value); + this.boxcolor = "#AEA"; + } catch (err) { + this.boxcolor = "red"; + } + }; + + ConstantArray.prototype.onExecute = function() { + var v = this.getInputData(0); + if(v && v.length) + { + if(!this._value) + this._value = new Array(); + this._value.length = v.length; + for(var i = 0; i < v.length; ++i) + this._value[i] = v[i]; + } + this.setOutputData(0, this._value); + }; + + ConstantArray.prototype.setValue = ConstantNumber.prototype.setValue; + + LiteGraph.registerNodeType("basic/array", ConstantArray); + + function ArrayElement() { + this.addInput("array", "array,table,string"); + this.addInput("index", "number"); + this.addOutput("value", ""); + this.addProperty("index",0); + } + + ArrayElement.title = "Array[i]"; + ArrayElement.desc = "Returns an element from an array"; + + ArrayElement.prototype.onExecute = function() { + var array = this.getInputData(0); + var index = this.getInputData(1); + if(index == null) + index = this.properties.index; + if(array == null || index == null ) + return; + this.setOutputData(0, array[Math.floor(Number(index))] ); + }; + + LiteGraph.registerNodeType("basic/array[]", ArrayElement); + + function TableElement() { + this.addInput("table", "table"); + this.addInput("row", "number"); + this.addInput("col", "number"); + this.addOutput("value", ""); + this.addProperty("row",0); + this.addProperty("column",0); + } + + TableElement.title = "Table[row][col]"; + TableElement.desc = "Returns an element from a table"; + + TableElement.prototype.onExecute = function() { + var table = this.getInputData(0); + var row = this.getInputData(1); + var col = this.getInputData(2); + if(row == null) + row = this.properties.row; + if(col == null) + col = this.properties.column; + if(table == null || row == null || col == null) + return; + var row = table[Math.floor(Number(row))]; + if(row) + this.setOutputData(0, row[Math.floor(Number(col))] ); + else + this.setOutputData(0, null ); + }; + + LiteGraph.registerNodeType("basic/table[][]", TableElement); + + function ObjectProperty() { + this.addInput("obj", ""); + this.addOutput("", ""); + this.addProperty("value", ""); + this.widget = this.addWidget("text","prop.","",this.setValue.bind(this) ); + this.widgets_up = true; + this.size = [140, 30]; + this._value = null; + } + + ObjectProperty.title = "Object property"; + ObjectProperty.desc = "Outputs the property of an object"; + + ObjectProperty.prototype.setValue = function(v) { + this.properties.value = v; + this.widget.value = v; + }; + + ObjectProperty.prototype.getTitle = function() { + if (this.flags.collapsed) { + return "in." + this.properties.value; + } + return this.title; + }; + + ObjectProperty.prototype.onPropertyChanged = function(name, value) { + this.widget.value = value; + }; + + ObjectProperty.prototype.onExecute = function() { + var data = this.getInputData(0); + if (data != null) { + this.setOutputData(0, data[this.properties.value]); + } + }; + + LiteGraph.registerNodeType("basic/object_property", ObjectProperty); + + function ObjectKeys() { + this.addInput("obj", ""); + this.addOutput("keys", "array"); + this.size = [140, 30]; + } + + ObjectKeys.title = "Object keys"; + ObjectKeys.desc = "Outputs an array with the keys of an object"; + + ObjectKeys.prototype.onExecute = function() { + var data = this.getInputData(0); + if (data != null) { + this.setOutputData(0, Object.keys(data) ); + } + }; + + LiteGraph.registerNodeType("basic/object_keys", ObjectKeys); + + function MergeObjects() { + this.addInput("A", "object"); + this.addInput("B", "object"); + this.addOutput("", "object"); + this._result = {}; + var that = this; + this.addWidget("button","clear","",function(){ + that._result = {}; + }); + this.size = this.computeSize(); + } + + MergeObjects.title = "Merge Objects"; + MergeObjects.desc = "Creates an object copying properties from others"; + + MergeObjects.prototype.onExecute = function() { + var A = this.getInputData(0); + var B = this.getInputData(1); + var C = this._result; + if(A) + for(var i in A) + C[i] = A[i]; + if(B) + for(var i in B) + C[i] = B[i]; + this.setOutputData(0,C); + }; + + LiteGraph.registerNodeType("basic/merge_objects", MergeObjects ); + + //Store as variable + function Variable() { + this.size = [60, 30]; + this.addInput("in"); + this.addOutput("out"); + this.properties = { varname: "myname", global: false }; + this.value = null; + } + + Variable.title = "Variable"; + Variable.desc = "store/read variable value"; + + Variable.prototype.onExecute = function() { + this.value = this.getInputData(0); + if(this.graph) + this.graph.vars[ this.properties.varname ] = this.value; + if(this.properties.global) + global[this.properties.varname] = this.value; + this.setOutputData(0, this.value ); + }; + + Variable.prototype.getTitle = function() { + return this.properties.varname; + }; + + LiteGraph.registerNodeType("basic/variable", Variable); + + function length(v) { + if(v && v.length != null) + return Number(v.length); + return 0; + } + + LiteGraph.wrapFunctionAsNode( + "basic/length", + length, + ["*"], + "number" + ); + + function DownloadData() { + this.size = [60, 30]; + this.addInput("data", 0 ); + this.addInput("download", LiteGraph.ACTION ); + this.properties = { filename: "data.json" }; + this.value = null; + var that = this; + this.addWidget("button","Download","", function(v){ + if(!that.value) + return; + that.downloadAsFile(); + }); + } + + DownloadData.title = "Download"; + DownloadData.desc = "Download some data"; + + DownloadData.prototype.downloadAsFile = function() + { + if(this.value == null) + return; + + var str = null; + if(this.value.constructor === String) + str = this.value; + else + str = JSON.stringify(this.value); + + var file = new Blob([str]); + var url = URL.createObjectURL( file ); + var element = document.createElement("a"); + element.setAttribute('href', url); + element.setAttribute('download', this.properties.filename ); + element.style.display = 'none'; + document.body.appendChild(element); + element.click(); + document.body.removeChild(element); + setTimeout( function(){ URL.revokeObjectURL( url ); }, 1000*60 ); //wait one minute to revoke url + } + + DownloadData.prototype.onAction = function(action, param) { + var that = this; + setTimeout( function(){ that.downloadAsFile(); }, 100); //deferred to avoid blocking the renderer with the popup + } + + DownloadData.prototype.onExecute = function() { + if (this.inputs[0]) { + this.value = this.getInputData(0); + } + }; + + DownloadData.prototype.getTitle = function() { + if (this.flags.collapsed) { + return this.properties.filename; + } + return this.title; + }; + + LiteGraph.registerNodeType("basic/download", DownloadData); + + + + //Watch a value in the editor + function Watch() { + this.size = [60, 30]; + this.addInput("value", 0, { label: "" }); + this.value = 0; + } + + Watch.title = "Watch"; + Watch.desc = "Show value of input"; + + Watch.prototype.onExecute = function() { + if (this.inputs[0]) { + this.value = this.getInputData(0); + } + }; + + Watch.prototype.getTitle = function() { + if (this.flags.collapsed) { + return this.inputs[0].label; + } + return this.title; + }; + + Watch.toString = function(o) { + if (o == null) { + return "null"; + } else if (o.constructor === Number) { + return o.toFixed(3); + } else if (o.constructor === Array) { + var str = "["; + for (var i = 0; i < o.length; ++i) { + str += Watch.toString(o[i]) + (i + 1 != o.length ? "," : ""); + } + str += "]"; + return str; + } else { + return String(o); + } + }; + + Watch.prototype.onDrawBackground = function(ctx) { + //show the current value + this.inputs[0].label = Watch.toString(this.value); + }; + + LiteGraph.registerNodeType("basic/watch", Watch); + + //in case one type doesnt match other type but you want to connect them anyway + function Cast() { + this.addInput("in", 0); + this.addOutput("out", 0); + this.size = [40, 30]; + } + + Cast.title = "Cast"; + Cast.desc = "Allows to connect different types"; + + Cast.prototype.onExecute = function() { + this.setOutputData(0, this.getInputData(0)); + }; + + LiteGraph.registerNodeType("basic/cast", Cast); + + //Show value inside the debug console + function Console() { + this.mode = LiteGraph.ON_EVENT; + this.size = [80, 30]; + this.addProperty("msg", ""); + this.addInput("log", LiteGraph.EVENT); + this.addInput("msg", 0); + } + + Console.title = "Console"; + Console.desc = "Show value inside the console"; + + Console.prototype.onAction = function(action, param) { + if (action == "log") { + console.log(param); + } else if (action == "warn") { + console.warn(param); + } else if (action == "error") { + console.error(param); + } + }; + + Console.prototype.onExecute = function() { + var msg = this.getInputData(1); + if (msg !== null) { + this.properties.msg = msg; + } + console.log(msg); + }; + + Console.prototype.onGetInputs = function() { + return [ + ["log", LiteGraph.ACTION], + ["warn", LiteGraph.ACTION], + ["error", LiteGraph.ACTION] + ]; + }; + + LiteGraph.registerNodeType("basic/console", Console); + + //Show value inside the debug console + function Alert() { + this.mode = LiteGraph.ON_EVENT; + this.addProperty("msg", ""); + this.addInput("", LiteGraph.EVENT); + var that = this; + this.widget = this.addWidget("text", "Text", "", function(v) { + that.properties.msg = v; + }); + this.widgets_up = true; + this.size = [200, 30]; + } + + Alert.title = "Alert"; + Alert.desc = "Show an alert window"; + Alert.color = "#510"; + + Alert.prototype.onConfigure = function(o) { + this.widget.value = o.properties.msg; + }; + + Alert.prototype.onAction = function(action, param) { + var msg = this.properties.msg; + setTimeout(function() { + alert(msg); + }, 10); + }; + + LiteGraph.registerNodeType("basic/alert", Alert); + + //Execites simple code + function NodeScript() { + this.size = [60, 30]; + this.addProperty("onExecute", "return A;"); + this.addInput("A", ""); + this.addInput("B", ""); + this.addOutput("out", ""); + + this._func = null; + this.data = {}; + } + + NodeScript.prototype.onConfigure = function(o) { + if (o.properties.onExecute && LiteGraph.allow_scripts) + this.compileCode(o.properties.onExecute); + else + console.warn("Script not compiled, LiteGraph.allow_scripts is false"); + }; + + NodeScript.title = "Script"; + NodeScript.desc = "executes a code (max 100 characters)"; + + NodeScript.widgets_info = { + onExecute: { type: "code" } + }; + + NodeScript.prototype.onPropertyChanged = function(name, value) { + if (name == "onExecute" && LiteGraph.allow_scripts) + this.compileCode(value); + else + console.warn("Script not compiled, LiteGraph.allow_scripts is false"); + }; + + NodeScript.prototype.compileCode = function(code) { + this._func = null; + if (code.length > 256) { + console.warn("Script too long, max 256 chars"); + } else { + var code_low = code.toLowerCase(); + var forbidden_words = [ + "script", + "body", + "document", + "eval", + "nodescript", + "function" + ]; //bad security solution + for (var i = 0; i < forbidden_words.length; ++i) { + if (code_low.indexOf(forbidden_words[i]) != -1) { + console.warn("invalid script"); + return; + } + } + try { + this._func = new Function("A", "B", "C", "DATA", "node", code); + } catch (err) { + console.error("Error parsing script"); + console.error(err); + } + } + }; + + NodeScript.prototype.onExecute = function() { + if (!this._func) { + return; + } + + try { + var A = this.getInputData(0); + var B = this.getInputData(1); + var C = this.getInputData(2); + this.setOutputData(0, this._func(A, B, C, this.data, this)); + } catch (err) { + console.error("Error in script"); + console.error(err); + } + }; + + NodeScript.prototype.onGetOutputs = function() { + return [["C", ""]]; + }; + + LiteGraph.registerNodeType("basic/script", NodeScript); +})(this); + //event related nodes (function(global) { var LiteGraph = global.LiteGraph; @@ -12579,805 +24845,805 @@ if (typeof exports != "undefined") { LiteGraph.registerNodeType("basic/data_store", DataStore); })(this); - -//widgets -(function(global) { - var LiteGraph = global.LiteGraph; - - /* Button ****************/ - - function WidgetButton() { - this.addOutput("", LiteGraph.EVENT); - this.addOutput("", "boolean"); - this.addProperty("text", "click me"); - this.addProperty("font_size", 30); - this.addProperty("message", ""); - this.size = [164, 84]; - this.clicked = false; - } - - WidgetButton.title = "Button"; - WidgetButton.desc = "Triggers an event"; - - WidgetButton.font = "Arial"; - WidgetButton.prototype.onDrawForeground = function(ctx) { - if (this.flags.collapsed) { - return; - } - var margin = 10; - ctx.fillStyle = "black"; - ctx.fillRect( - margin + 1, - margin + 1, - this.size[0] - margin * 2, - this.size[1] - margin * 2 - ); - ctx.fillStyle = "#AAF"; - ctx.fillRect( - margin - 1, - margin - 1, - this.size[0] - margin * 2, - this.size[1] - margin * 2 - ); - ctx.fillStyle = this.clicked - ? "white" - : this.mouseOver - ? "#668" - : "#334"; - ctx.fillRect( - margin, - margin, - this.size[0] - margin * 2, - this.size[1] - margin * 2 - ); - - if (this.properties.text || this.properties.text === 0) { - var font_size = this.properties.font_size || 30; - ctx.textAlign = "center"; - ctx.fillStyle = this.clicked ? "black" : "white"; - ctx.font = font_size + "px " + WidgetButton.font; - ctx.fillText( - this.properties.text, - this.size[0] * 0.5, - this.size[1] * 0.5 + font_size * 0.3 - ); - ctx.textAlign = "left"; - } - }; - - WidgetButton.prototype.onMouseDown = function(e, local_pos) { - if ( - local_pos[0] > 1 && - local_pos[1] > 1 && - local_pos[0] < this.size[0] - 2 && - local_pos[1] < this.size[1] - 2 - ) { - this.clicked = true; - this.triggerSlot(0, this.properties.message); - return true; - } - }; - - WidgetButton.prototype.onExecute = function() { - this.setOutputData(1, this.clicked); - }; - - WidgetButton.prototype.onMouseUp = function(e) { - this.clicked = false; - }; - - LiteGraph.registerNodeType("widget/button", WidgetButton); - - function WidgetToggle() { - this.addInput("", "boolean"); - this.addInput("e", LiteGraph.ACTION); - this.addOutput("v", "boolean"); - this.addOutput("e", LiteGraph.EVENT); - this.properties = { font: "", value: false }; - this.size = [160, 44]; - } - - WidgetToggle.title = "Toggle"; - WidgetToggle.desc = "Toggles between true or false"; - - WidgetToggle.prototype.onDrawForeground = function(ctx) { - if (this.flags.collapsed) { - return; - } - - var size = this.size[1] * 0.5; - var margin = 0.25; - var h = this.size[1] * 0.8; - ctx.font = this.properties.font || (size * 0.8).toFixed(0) + "px Arial"; - var w = ctx.measureText(this.title).width; - var x = (this.size[0] - (w + size)) * 0.5; - - ctx.fillStyle = "#AAA"; - ctx.fillRect(x, h - size, size, size); - - ctx.fillStyle = this.properties.value ? "#AEF" : "#000"; - ctx.fillRect( - x + size * margin, - h - size + size * margin, - size * (1 - margin * 2), - size * (1 - margin * 2) - ); - - ctx.textAlign = "left"; - ctx.fillStyle = "#AAA"; - ctx.fillText(this.title, size * 1.2 + x, h * 0.85); - ctx.textAlign = "left"; - }; - - WidgetToggle.prototype.onAction = function(action) { - this.properties.value = !this.properties.value; - this.trigger("e", this.properties.value); - }; - - WidgetToggle.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v != null) { - this.properties.value = v; - } - this.setOutputData(0, this.properties.value); - }; - - WidgetToggle.prototype.onMouseDown = function(e, local_pos) { - if ( - local_pos[0] > 1 && - local_pos[1] > 1 && - local_pos[0] < this.size[0] - 2 && - local_pos[1] < this.size[1] - 2 - ) { - this.properties.value = !this.properties.value; - this.graph._version++; - this.trigger("e", this.properties.value); - return true; - } - }; - - LiteGraph.registerNodeType("widget/toggle", WidgetToggle); - - /* Number ****************/ - - function WidgetNumber() { - this.addOutput("", "number"); - this.size = [80, 60]; - this.properties = { min: -1000, max: 1000, value: 1, step: 1 }; - this.old_y = -1; - this._remainder = 0; - this._precision = 0; - this.mouse_captured = false; - } - - WidgetNumber.title = "Number"; - WidgetNumber.desc = "Widget to select number value"; - - WidgetNumber.pixels_threshold = 10; - WidgetNumber.markers_color = "#666"; - - WidgetNumber.prototype.onDrawForeground = function(ctx) { - var x = this.size[0] * 0.5; - var h = this.size[1]; - if (h > 30) { - ctx.fillStyle = WidgetNumber.markers_color; - ctx.beginPath(); - ctx.moveTo(x, h * 0.1); - ctx.lineTo(x + h * 0.1, h * 0.2); - ctx.lineTo(x + h * -0.1, h * 0.2); - ctx.fill(); - ctx.beginPath(); - ctx.moveTo(x, h * 0.9); - ctx.lineTo(x + h * 0.1, h * 0.8); - ctx.lineTo(x + h * -0.1, h * 0.8); - ctx.fill(); - ctx.font = (h * 0.7).toFixed(1) + "px Arial"; - } else { - ctx.font = (h * 0.8).toFixed(1) + "px Arial"; - } - - ctx.textAlign = "center"; - ctx.font = (h * 0.7).toFixed(1) + "px Arial"; - ctx.fillStyle = "#EEE"; - ctx.fillText( - this.properties.value.toFixed(this._precision), - x, - h * 0.75 - ); - }; - - WidgetNumber.prototype.onExecute = function() { - this.setOutputData(0, this.properties.value); - }; - - WidgetNumber.prototype.onPropertyChanged = function(name, value) { - var t = (this.properties.step + "").split("."); - this._precision = t.length > 1 ? t[1].length : 0; - }; - - WidgetNumber.prototype.onMouseDown = function(e, pos) { - if (pos[1] < 0) { - return; - } - - this.old_y = e.canvasY; - this.captureInput(true); - this.mouse_captured = true; - - return true; - }; - - WidgetNumber.prototype.onMouseMove = function(e) { - if (!this.mouse_captured) { - return; - } - - var delta = this.old_y - e.canvasY; - if (e.shiftKey) { - delta *= 10; - } - if (e.metaKey || e.altKey) { - delta *= 0.1; - } - this.old_y = e.canvasY; - - var steps = this._remainder + delta / WidgetNumber.pixels_threshold; - this._remainder = steps % 1; - steps = steps | 0; - - var v = Math.clamp( - this.properties.value + steps * this.properties.step, - this.properties.min, - this.properties.max - ); - this.properties.value = v; - this.graph._version++; - this.setDirtyCanvas(true); - }; - - WidgetNumber.prototype.onMouseUp = function(e, pos) { - if (e.click_time < 200) { - var steps = pos[1] > this.size[1] * 0.5 ? -1 : 1; - this.properties.value = Math.clamp( - this.properties.value + steps * this.properties.step, - this.properties.min, - this.properties.max - ); - this.graph._version++; - this.setDirtyCanvas(true); - } - - if (this.mouse_captured) { - this.mouse_captured = false; - this.captureInput(false); - } - }; - - LiteGraph.registerNodeType("widget/number", WidgetNumber); - - - /* Combo ****************/ - - function WidgetCombo() { - this.addOutput("", "string"); - this.addOutput("change", LiteGraph.EVENT); - this.size = [80, 60]; - this.properties = { value: "A", values:"A;B;C" }; - this.old_y = -1; - this.mouse_captured = false; - this._values = this.properties.values.split(";"); - var that = this; - this.widgets_up = true; - this.widget = this.addWidget("combo","", this.properties.value, function(v){ - that.properties.value = v; - that.triggerSlot(1, v); - }, { property: "value", values: this._values } ); - } - - WidgetCombo.title = "Combo"; - WidgetCombo.desc = "Widget to select from a list"; - - WidgetCombo.prototype.onExecute = function() { - this.setOutputData( 0, this.properties.value ); - }; - - WidgetCombo.prototype.onPropertyChanged = function(name, value) { - if(name == "values") - { - this._values = value.split(";"); - this.widget.options.values = this._values; - } - else if(name == "value") - { - this.widget.value = value; - } - }; - - LiteGraph.registerNodeType("widget/combo", WidgetCombo); - - - /* Knob ****************/ - - function WidgetKnob() { - this.addOutput("", "number"); - this.size = [64, 84]; - this.properties = { - min: 0, - max: 1, - value: 0.5, - color: "#7AF", - precision: 2 - }; - this.value = -1; - } - - WidgetKnob.title = "Knob"; - WidgetKnob.desc = "Circular controller"; - WidgetKnob.size = [80, 100]; - - WidgetKnob.prototype.onDrawForeground = function(ctx) { - if (this.flags.collapsed) { - return; - } - - if (this.value == -1) { - this.value = - (this.properties.value - this.properties.min) / - (this.properties.max - this.properties.min); - } - - var center_x = this.size[0] * 0.5; - var center_y = this.size[1] * 0.5; - var radius = Math.min(this.size[0], this.size[1]) * 0.5 - 5; - var w = Math.floor(radius * 0.05); - - ctx.globalAlpha = 1; - ctx.save(); - ctx.translate(center_x, center_y); - ctx.rotate(Math.PI * 0.75); - - //bg - ctx.fillStyle = "rgba(0,0,0,0.5)"; - ctx.beginPath(); - ctx.moveTo(0, 0); - ctx.arc(0, 0, radius, 0, Math.PI * 1.5); - ctx.fill(); - - //value - ctx.strokeStyle = "black"; - ctx.fillStyle = this.properties.color; - ctx.lineWidth = 2; - ctx.beginPath(); - ctx.moveTo(0, 0); - ctx.arc( - 0, - 0, - radius - 4, - 0, - Math.PI * 1.5 * Math.max(0.01, this.value) - ); - ctx.closePath(); - ctx.fill(); - //ctx.stroke(); - ctx.lineWidth = 1; - ctx.globalAlpha = 1; - ctx.restore(); - - //inner - ctx.fillStyle = "black"; - ctx.beginPath(); - ctx.arc(center_x, center_y, radius * 0.75, 0, Math.PI * 2, true); - ctx.fill(); - - //miniball - ctx.fillStyle = this.mouseOver ? "white" : this.properties.color; - ctx.beginPath(); - var angle = this.value * Math.PI * 1.5 + Math.PI * 0.75; - ctx.arc( - center_x + Math.cos(angle) * radius * 0.65, - center_y + Math.sin(angle) * radius * 0.65, - radius * 0.05, - 0, - Math.PI * 2, - true - ); - ctx.fill(); - - //text - ctx.fillStyle = this.mouseOver ? "white" : "#AAA"; - ctx.font = Math.floor(radius * 0.5) + "px Arial"; - ctx.textAlign = "center"; - ctx.fillText( - this.properties.value.toFixed(this.properties.precision), - center_x, - center_y + radius * 0.15 - ); - }; - - WidgetKnob.prototype.onExecute = function() { - this.setOutputData(0, this.properties.value); - this.boxcolor = LiteGraph.colorToString([ - this.value, - this.value, - this.value - ]); - }; - - WidgetKnob.prototype.onMouseDown = function(e) { - this.center = [this.size[0] * 0.5, this.size[1] * 0.5 + 20]; - this.radius = this.size[0] * 0.5; - if ( - e.canvasY - this.pos[1] < 20 || - LiteGraph.distance( - [e.canvasX, e.canvasY], - [this.pos[0] + this.center[0], this.pos[1] + this.center[1]] - ) > this.radius - ) { - return false; - } - this.oldmouse = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; - this.captureInput(true); - return true; - }; - - WidgetKnob.prototype.onMouseMove = function(e) { - if (!this.oldmouse) { - return; - } - - var m = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; - - var v = this.value; - v -= (m[1] - this.oldmouse[1]) * 0.01; - if (v > 1.0) { - v = 1.0; - } else if (v < 0.0) { - v = 0.0; - } - this.value = v; - this.properties.value = - this.properties.min + - (this.properties.max - this.properties.min) * this.value; - this.oldmouse = m; - this.setDirtyCanvas(true); - }; - - WidgetKnob.prototype.onMouseUp = function(e) { - if (this.oldmouse) { - this.oldmouse = null; - this.captureInput(false); - } - }; - - WidgetKnob.prototype.onPropertyChanged = function(name, value) { - if (name == "min" || name == "max" || name == "value") { - this.properties[name] = parseFloat(value); - return true; //block - } - }; - - LiteGraph.registerNodeType("widget/knob", WidgetKnob); - - //Show value inside the debug console - function WidgetSliderGUI() { - this.addOutput("", "number"); - this.properties = { - value: 0.5, - min: 0, - max: 1, - text: "V" - }; - var that = this; - this.size = [140, 40]; - this.slider = this.addWidget( - "slider", - "V", - this.properties.value, - function(v) { - that.properties.value = v; - }, - this.properties - ); - this.widgets_up = true; - } - - WidgetSliderGUI.title = "Inner Slider"; - - WidgetSliderGUI.prototype.onPropertyChanged = function(name, value) { - if (name == "value") { - this.slider.value = value; - } - }; - - WidgetSliderGUI.prototype.onExecute = function() { - this.setOutputData(0, this.properties.value); - }; - - LiteGraph.registerNodeType("widget/internal_slider", WidgetSliderGUI); - - //Widget H SLIDER - function WidgetHSlider() { - this.size = [160, 26]; - this.addOutput("", "number"); - this.properties = { color: "#7AF", min: 0, max: 1, value: 0.5 }; - this.value = -1; - } - - WidgetHSlider.title = "H.Slider"; - WidgetHSlider.desc = "Linear slider controller"; - - WidgetHSlider.prototype.onDrawForeground = function(ctx) { - if (this.value == -1) { - this.value = - (this.properties.value - this.properties.min) / - (this.properties.max - this.properties.min); - } - - //border - ctx.globalAlpha = 1; - ctx.lineWidth = 1; - ctx.fillStyle = "#000"; - ctx.fillRect(2, 2, this.size[0] - 4, this.size[1] - 4); - - ctx.fillStyle = this.properties.color; - ctx.beginPath(); - ctx.rect(4, 4, (this.size[0] - 8) * this.value, this.size[1] - 8); - ctx.fill(); - }; - - WidgetHSlider.prototype.onExecute = function() { - this.properties.value = - this.properties.min + - (this.properties.max - this.properties.min) * this.value; - this.setOutputData(0, this.properties.value); - this.boxcolor = LiteGraph.colorToString([ - this.value, - this.value, - this.value - ]); - }; - - WidgetHSlider.prototype.onMouseDown = function(e) { - if (e.canvasY - this.pos[1] < 0) { - return false; - } - - this.oldmouse = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; - this.captureInput(true); - return true; - }; - - WidgetHSlider.prototype.onMouseMove = function(e) { - if (!this.oldmouse) { - return; - } - - var m = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; - - var v = this.value; - var delta = m[0] - this.oldmouse[0]; - v += delta / this.size[0]; - if (v > 1.0) { - v = 1.0; - } else if (v < 0.0) { - v = 0.0; - } - - this.value = v; - - this.oldmouse = m; - this.setDirtyCanvas(true); - }; - - WidgetHSlider.prototype.onMouseUp = function(e) { - this.oldmouse = null; - this.captureInput(false); - }; - - WidgetHSlider.prototype.onMouseLeave = function(e) { - //this.oldmouse = null; - }; - - LiteGraph.registerNodeType("widget/hslider", WidgetHSlider); - - function WidgetProgress() { - this.size = [160, 26]; - this.addInput("", "number"); - this.properties = { min: 0, max: 1, value: 0, color: "#AAF" }; - } - - WidgetProgress.title = "Progress"; - WidgetProgress.desc = "Shows data in linear progress"; - - WidgetProgress.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v != undefined) { - this.properties["value"] = v; - } - }; - - WidgetProgress.prototype.onDrawForeground = function(ctx) { - //border - ctx.lineWidth = 1; - ctx.fillStyle = this.properties.color; - var v = - (this.properties.value - this.properties.min) / - (this.properties.max - this.properties.min); - v = Math.min(1, v); - v = Math.max(0, v); - ctx.fillRect(2, 2, (this.size[0] - 4) * v, this.size[1] - 4); - }; - - LiteGraph.registerNodeType("widget/progress", WidgetProgress); - - function WidgetText() { - this.addInputs("", 0); - this.properties = { - value: "...", - font: "Arial", - fontsize: 18, - color: "#AAA", - align: "left", - glowSize: 0, - decimals: 1 - }; - } - - WidgetText.title = "Text"; - WidgetText.desc = "Shows the input value"; - WidgetText.widgets = [ - { name: "resize", text: "Resize box", type: "button" }, - { name: "led_text", text: "LED", type: "minibutton" }, - { name: "normal_text", text: "Normal", type: "minibutton" } - ]; - - WidgetText.prototype.onDrawForeground = function(ctx) { - //ctx.fillStyle="#000"; - //ctx.fillRect(0,0,100,60); - ctx.fillStyle = this.properties["color"]; - var v = this.properties["value"]; - - if (this.properties["glowSize"]) { - ctx.shadowColor = this.properties.color; - ctx.shadowOffsetX = 0; - ctx.shadowOffsetY = 0; - ctx.shadowBlur = this.properties["glowSize"]; - } else { - ctx.shadowColor = "transparent"; - } - - var fontsize = this.properties["fontsize"]; - - ctx.textAlign = this.properties["align"]; - ctx.font = fontsize.toString() + "px " + this.properties["font"]; - this.str = - typeof v == "number" ? v.toFixed(this.properties["decimals"]) : v; - - if (typeof this.str == "string") { - var lines = this.str.split("\\n"); - for (var i in lines) { - ctx.fillText( - lines[i], - this.properties["align"] == "left" ? 15 : this.size[0] - 15, - fontsize * -0.15 + fontsize * (parseInt(i) + 1) - ); - } - } - - ctx.shadowColor = "transparent"; - this.last_ctx = ctx; - ctx.textAlign = "left"; - }; - - WidgetText.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v != null) { - this.properties["value"] = v; - } - //this.setDirtyCanvas(true); - }; - - WidgetText.prototype.resize = function() { - if (!this.last_ctx) { - return; - } - - var lines = this.str.split("\\n"); - this.last_ctx.font = - this.properties["fontsize"] + "px " + this.properties["font"]; - var max = 0; - for (var i in lines) { - var w = this.last_ctx.measureText(lines[i]).width; - if (max < w) { - max = w; - } - } - this.size[0] = max + 20; - this.size[1] = 4 + lines.length * this.properties["fontsize"]; - - this.setDirtyCanvas(true); - }; - - WidgetText.prototype.onPropertyChanged = function(name, value) { - this.properties[name] = value; - this.str = typeof value == "number" ? value.toFixed(3) : value; - //this.resize(); - return true; - }; - - LiteGraph.registerNodeType("widget/text", WidgetText); - - function WidgetPanel() { - this.size = [200, 100]; - this.properties = { - borderColor: "#ffffff", - bgcolorTop: "#f0f0f0", - bgcolorBottom: "#e0e0e0", - shadowSize: 2, - borderRadius: 3 - }; - } - - WidgetPanel.title = "Panel"; - WidgetPanel.desc = "Non interactive panel"; - WidgetPanel.widgets = [{ name: "update", text: "Update", type: "button" }]; - - WidgetPanel.prototype.createGradient = function(ctx) { - if ( - this.properties["bgcolorTop"] == "" || - this.properties["bgcolorBottom"] == "" - ) { - this.lineargradient = 0; - return; - } - - this.lineargradient = ctx.createLinearGradient(0, 0, 0, this.size[1]); - this.lineargradient.addColorStop(0, this.properties["bgcolorTop"]); - this.lineargradient.addColorStop(1, this.properties["bgcolorBottom"]); - }; - - WidgetPanel.prototype.onDrawForeground = function(ctx) { - if (this.flags.collapsed) { - return; - } - - if (this.lineargradient == null) { - this.createGradient(ctx); - } - - if (!this.lineargradient) { - return; - } - - ctx.lineWidth = 1; - ctx.strokeStyle = this.properties["borderColor"]; - //ctx.fillStyle = "#ebebeb"; - ctx.fillStyle = this.lineargradient; - - if (this.properties["shadowSize"]) { - ctx.shadowColor = "#000"; - ctx.shadowOffsetX = 0; - ctx.shadowOffsetY = 0; - ctx.shadowBlur = this.properties["shadowSize"]; - } else { - ctx.shadowColor = "transparent"; - } - - ctx.roundRect( - 0, - 0, - this.size[0] - 1, - this.size[1] - 1, - this.properties["shadowSize"] - ); - ctx.fill(); - ctx.shadowColor = "transparent"; - ctx.stroke(); - }; - - LiteGraph.registerNodeType("widget/panel", WidgetPanel); -})(this); - + +//widgets +(function(global) { + var LiteGraph = global.LiteGraph; + + /* Button ****************/ + + function WidgetButton() { + this.addOutput("", LiteGraph.EVENT); + this.addOutput("", "boolean"); + this.addProperty("text", "click me"); + this.addProperty("font_size", 30); + this.addProperty("message", ""); + this.size = [164, 84]; + this.clicked = false; + } + + WidgetButton.title = "Button"; + WidgetButton.desc = "Triggers an event"; + + WidgetButton.font = "Arial"; + WidgetButton.prototype.onDrawForeground = function(ctx) { + if (this.flags.collapsed) { + return; + } + var margin = 10; + ctx.fillStyle = "black"; + ctx.fillRect( + margin + 1, + margin + 1, + this.size[0] - margin * 2, + this.size[1] - margin * 2 + ); + ctx.fillStyle = "#AAF"; + ctx.fillRect( + margin - 1, + margin - 1, + this.size[0] - margin * 2, + this.size[1] - margin * 2 + ); + ctx.fillStyle = this.clicked + ? "white" + : this.mouseOver + ? "#668" + : "#334"; + ctx.fillRect( + margin, + margin, + this.size[0] - margin * 2, + this.size[1] - margin * 2 + ); + + if (this.properties.text || this.properties.text === 0) { + var font_size = this.properties.font_size || 30; + ctx.textAlign = "center"; + ctx.fillStyle = this.clicked ? "black" : "white"; + ctx.font = font_size + "px " + WidgetButton.font; + ctx.fillText( + this.properties.text, + this.size[0] * 0.5, + this.size[1] * 0.5 + font_size * 0.3 + ); + ctx.textAlign = "left"; + } + }; + + WidgetButton.prototype.onMouseDown = function(e, local_pos) { + if ( + local_pos[0] > 1 && + local_pos[1] > 1 && + local_pos[0] < this.size[0] - 2 && + local_pos[1] < this.size[1] - 2 + ) { + this.clicked = true; + this.triggerSlot(0, this.properties.message); + return true; + } + }; + + WidgetButton.prototype.onExecute = function() { + this.setOutputData(1, this.clicked); + }; + + WidgetButton.prototype.onMouseUp = function(e) { + this.clicked = false; + }; + + LiteGraph.registerNodeType("widget/button", WidgetButton); + + function WidgetToggle() { + this.addInput("", "boolean"); + this.addInput("e", LiteGraph.ACTION); + this.addOutput("v", "boolean"); + this.addOutput("e", LiteGraph.EVENT); + this.properties = { font: "", value: false }; + this.size = [160, 44]; + } + + WidgetToggle.title = "Toggle"; + WidgetToggle.desc = "Toggles between true or false"; + + WidgetToggle.prototype.onDrawForeground = function(ctx) { + if (this.flags.collapsed) { + return; + } + + var size = this.size[1] * 0.5; + var margin = 0.25; + var h = this.size[1] * 0.8; + ctx.font = this.properties.font || (size * 0.8).toFixed(0) + "px Arial"; + var w = ctx.measureText(this.title).width; + var x = (this.size[0] - (w + size)) * 0.5; + + ctx.fillStyle = "#AAA"; + ctx.fillRect(x, h - size, size, size); + + ctx.fillStyle = this.properties.value ? "#AEF" : "#000"; + ctx.fillRect( + x + size * margin, + h - size + size * margin, + size * (1 - margin * 2), + size * (1 - margin * 2) + ); + + ctx.textAlign = "left"; + ctx.fillStyle = "#AAA"; + ctx.fillText(this.title, size * 1.2 + x, h * 0.85); + ctx.textAlign = "left"; + }; + + WidgetToggle.prototype.onAction = function(action) { + this.properties.value = !this.properties.value; + this.trigger("e", this.properties.value); + }; + + WidgetToggle.prototype.onExecute = function() { + var v = this.getInputData(0); + if (v != null) { + this.properties.value = v; + } + this.setOutputData(0, this.properties.value); + }; + + WidgetToggle.prototype.onMouseDown = function(e, local_pos) { + if ( + local_pos[0] > 1 && + local_pos[1] > 1 && + local_pos[0] < this.size[0] - 2 && + local_pos[1] < this.size[1] - 2 + ) { + this.properties.value = !this.properties.value; + this.graph._version++; + this.trigger("e", this.properties.value); + return true; + } + }; + + LiteGraph.registerNodeType("widget/toggle", WidgetToggle); + + /* Number ****************/ + + function WidgetNumber() { + this.addOutput("", "number"); + this.size = [80, 60]; + this.properties = { min: -1000, max: 1000, value: 1, step: 1 }; + this.old_y = -1; + this._remainder = 0; + this._precision = 0; + this.mouse_captured = false; + } + + WidgetNumber.title = "Number"; + WidgetNumber.desc = "Widget to select number value"; + + WidgetNumber.pixels_threshold = 10; + WidgetNumber.markers_color = "#666"; + + WidgetNumber.prototype.onDrawForeground = function(ctx) { + var x = this.size[0] * 0.5; + var h = this.size[1]; + if (h > 30) { + ctx.fillStyle = WidgetNumber.markers_color; + ctx.beginPath(); + ctx.moveTo(x, h * 0.1); + ctx.lineTo(x + h * 0.1, h * 0.2); + ctx.lineTo(x + h * -0.1, h * 0.2); + ctx.fill(); + ctx.beginPath(); + ctx.moveTo(x, h * 0.9); + ctx.lineTo(x + h * 0.1, h * 0.8); + ctx.lineTo(x + h * -0.1, h * 0.8); + ctx.fill(); + ctx.font = (h * 0.7).toFixed(1) + "px Arial"; + } else { + ctx.font = (h * 0.8).toFixed(1) + "px Arial"; + } + + ctx.textAlign = "center"; + ctx.font = (h * 0.7).toFixed(1) + "px Arial"; + ctx.fillStyle = "#EEE"; + ctx.fillText( + this.properties.value.toFixed(this._precision), + x, + h * 0.75 + ); + }; + + WidgetNumber.prototype.onExecute = function() { + this.setOutputData(0, this.properties.value); + }; + + WidgetNumber.prototype.onPropertyChanged = function(name, value) { + var t = (this.properties.step + "").split("."); + this._precision = t.length > 1 ? t[1].length : 0; + }; + + WidgetNumber.prototype.onMouseDown = function(e, pos) { + if (pos[1] < 0) { + return; + } + + this.old_y = e.canvasY; + this.captureInput(true); + this.mouse_captured = true; + + return true; + }; + + WidgetNumber.prototype.onMouseMove = function(e) { + if (!this.mouse_captured) { + return; + } + + var delta = this.old_y - e.canvasY; + if (e.shiftKey) { + delta *= 10; + } + if (e.metaKey || e.altKey) { + delta *= 0.1; + } + this.old_y = e.canvasY; + + var steps = this._remainder + delta / WidgetNumber.pixels_threshold; + this._remainder = steps % 1; + steps = steps | 0; + + var v = Math.clamp( + this.properties.value + steps * this.properties.step, + this.properties.min, + this.properties.max + ); + this.properties.value = v; + this.graph._version++; + this.setDirtyCanvas(true); + }; + + WidgetNumber.prototype.onMouseUp = function(e, pos) { + if (e.click_time < 200) { + var steps = pos[1] > this.size[1] * 0.5 ? -1 : 1; + this.properties.value = Math.clamp( + this.properties.value + steps * this.properties.step, + this.properties.min, + this.properties.max + ); + this.graph._version++; + this.setDirtyCanvas(true); + } + + if (this.mouse_captured) { + this.mouse_captured = false; + this.captureInput(false); + } + }; + + LiteGraph.registerNodeType("widget/number", WidgetNumber); + + + /* Combo ****************/ + + function WidgetCombo() { + this.addOutput("", "string"); + this.addOutput("change", LiteGraph.EVENT); + this.size = [80, 60]; + this.properties = { value: "A", values:"A;B;C" }; + this.old_y = -1; + this.mouse_captured = false; + this._values = this.properties.values.split(";"); + var that = this; + this.widgets_up = true; + this.widget = this.addWidget("combo","", this.properties.value, function(v){ + that.properties.value = v; + that.triggerSlot(1, v); + }, { property: "value", values: this._values } ); + } + + WidgetCombo.title = "Combo"; + WidgetCombo.desc = "Widget to select from a list"; + + WidgetCombo.prototype.onExecute = function() { + this.setOutputData( 0, this.properties.value ); + }; + + WidgetCombo.prototype.onPropertyChanged = function(name, value) { + if(name == "values") + { + this._values = value.split(";"); + this.widget.options.values = this._values; + } + else if(name == "value") + { + this.widget.value = value; + } + }; + + LiteGraph.registerNodeType("widget/combo", WidgetCombo); + + + /* Knob ****************/ + + function WidgetKnob() { + this.addOutput("", "number"); + this.size = [64, 84]; + this.properties = { + min: 0, + max: 1, + value: 0.5, + color: "#7AF", + precision: 2 + }; + this.value = -1; + } + + WidgetKnob.title = "Knob"; + WidgetKnob.desc = "Circular controller"; + WidgetKnob.size = [80, 100]; + + WidgetKnob.prototype.onDrawForeground = function(ctx) { + if (this.flags.collapsed) { + return; + } + + if (this.value == -1) { + this.value = + (this.properties.value - this.properties.min) / + (this.properties.max - this.properties.min); + } + + var center_x = this.size[0] * 0.5; + var center_y = this.size[1] * 0.5; + var radius = Math.min(this.size[0], this.size[1]) * 0.5 - 5; + var w = Math.floor(radius * 0.05); + + ctx.globalAlpha = 1; + ctx.save(); + ctx.translate(center_x, center_y); + ctx.rotate(Math.PI * 0.75); + + //bg + ctx.fillStyle = "rgba(0,0,0,0.5)"; + ctx.beginPath(); + ctx.moveTo(0, 0); + ctx.arc(0, 0, radius, 0, Math.PI * 1.5); + ctx.fill(); + + //value + ctx.strokeStyle = "black"; + ctx.fillStyle = this.properties.color; + ctx.lineWidth = 2; + ctx.beginPath(); + ctx.moveTo(0, 0); + ctx.arc( + 0, + 0, + radius - 4, + 0, + Math.PI * 1.5 * Math.max(0.01, this.value) + ); + ctx.closePath(); + ctx.fill(); + //ctx.stroke(); + ctx.lineWidth = 1; + ctx.globalAlpha = 1; + ctx.restore(); + + //inner + ctx.fillStyle = "black"; + ctx.beginPath(); + ctx.arc(center_x, center_y, radius * 0.75, 0, Math.PI * 2, true); + ctx.fill(); + + //miniball + ctx.fillStyle = this.mouseOver ? "white" : this.properties.color; + ctx.beginPath(); + var angle = this.value * Math.PI * 1.5 + Math.PI * 0.75; + ctx.arc( + center_x + Math.cos(angle) * radius * 0.65, + center_y + Math.sin(angle) * radius * 0.65, + radius * 0.05, + 0, + Math.PI * 2, + true + ); + ctx.fill(); + + //text + ctx.fillStyle = this.mouseOver ? "white" : "#AAA"; + ctx.font = Math.floor(radius * 0.5) + "px Arial"; + ctx.textAlign = "center"; + ctx.fillText( + this.properties.value.toFixed(this.properties.precision), + center_x, + center_y + radius * 0.15 + ); + }; + + WidgetKnob.prototype.onExecute = function() { + this.setOutputData(0, this.properties.value); + this.boxcolor = LiteGraph.colorToString([ + this.value, + this.value, + this.value + ]); + }; + + WidgetKnob.prototype.onMouseDown = function(e) { + this.center = [this.size[0] * 0.5, this.size[1] * 0.5 + 20]; + this.radius = this.size[0] * 0.5; + if ( + e.canvasY - this.pos[1] < 20 || + LiteGraph.distance( + [e.canvasX, e.canvasY], + [this.pos[0] + this.center[0], this.pos[1] + this.center[1]] + ) > this.radius + ) { + return false; + } + this.oldmouse = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; + this.captureInput(true); + return true; + }; + + WidgetKnob.prototype.onMouseMove = function(e) { + if (!this.oldmouse) { + return; + } + + var m = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; + + var v = this.value; + v -= (m[1] - this.oldmouse[1]) * 0.01; + if (v > 1.0) { + v = 1.0; + } else if (v < 0.0) { + v = 0.0; + } + this.value = v; + this.properties.value = + this.properties.min + + (this.properties.max - this.properties.min) * this.value; + this.oldmouse = m; + this.setDirtyCanvas(true); + }; + + WidgetKnob.prototype.onMouseUp = function(e) { + if (this.oldmouse) { + this.oldmouse = null; + this.captureInput(false); + } + }; + + WidgetKnob.prototype.onPropertyChanged = function(name, value) { + if (name == "min" || name == "max" || name == "value") { + this.properties[name] = parseFloat(value); + return true; //block + } + }; + + LiteGraph.registerNodeType("widget/knob", WidgetKnob); + + //Show value inside the debug console + function WidgetSliderGUI() { + this.addOutput("", "number"); + this.properties = { + value: 0.5, + min: 0, + max: 1, + text: "V" + }; + var that = this; + this.size = [140, 40]; + this.slider = this.addWidget( + "slider", + "V", + this.properties.value, + function(v) { + that.properties.value = v; + }, + this.properties + ); + this.widgets_up = true; + } + + WidgetSliderGUI.title = "Inner Slider"; + + WidgetSliderGUI.prototype.onPropertyChanged = function(name, value) { + if (name == "value") { + this.slider.value = value; + } + }; + + WidgetSliderGUI.prototype.onExecute = function() { + this.setOutputData(0, this.properties.value); + }; + + LiteGraph.registerNodeType("widget/internal_slider", WidgetSliderGUI); + + //Widget H SLIDER + function WidgetHSlider() { + this.size = [160, 26]; + this.addOutput("", "number"); + this.properties = { color: "#7AF", min: 0, max: 1, value: 0.5 }; + this.value = -1; + } + + WidgetHSlider.title = "H.Slider"; + WidgetHSlider.desc = "Linear slider controller"; + + WidgetHSlider.prototype.onDrawForeground = function(ctx) { + if (this.value == -1) { + this.value = + (this.properties.value - this.properties.min) / + (this.properties.max - this.properties.min); + } + + //border + ctx.globalAlpha = 1; + ctx.lineWidth = 1; + ctx.fillStyle = "#000"; + ctx.fillRect(2, 2, this.size[0] - 4, this.size[1] - 4); + + ctx.fillStyle = this.properties.color; + ctx.beginPath(); + ctx.rect(4, 4, (this.size[0] - 8) * this.value, this.size[1] - 8); + ctx.fill(); + }; + + WidgetHSlider.prototype.onExecute = function() { + this.properties.value = + this.properties.min + + (this.properties.max - this.properties.min) * this.value; + this.setOutputData(0, this.properties.value); + this.boxcolor = LiteGraph.colorToString([ + this.value, + this.value, + this.value + ]); + }; + + WidgetHSlider.prototype.onMouseDown = function(e) { + if (e.canvasY - this.pos[1] < 0) { + return false; + } + + this.oldmouse = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; + this.captureInput(true); + return true; + }; + + WidgetHSlider.prototype.onMouseMove = function(e) { + if (!this.oldmouse) { + return; + } + + var m = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; + + var v = this.value; + var delta = m[0] - this.oldmouse[0]; + v += delta / this.size[0]; + if (v > 1.0) { + v = 1.0; + } else if (v < 0.0) { + v = 0.0; + } + + this.value = v; + + this.oldmouse = m; + this.setDirtyCanvas(true); + }; + + WidgetHSlider.prototype.onMouseUp = function(e) { + this.oldmouse = null; + this.captureInput(false); + }; + + WidgetHSlider.prototype.onMouseLeave = function(e) { + //this.oldmouse = null; + }; + + LiteGraph.registerNodeType("widget/hslider", WidgetHSlider); + + function WidgetProgress() { + this.size = [160, 26]; + this.addInput("", "number"); + this.properties = { min: 0, max: 1, value: 0, color: "#AAF" }; + } + + WidgetProgress.title = "Progress"; + WidgetProgress.desc = "Shows data in linear progress"; + + WidgetProgress.prototype.onExecute = function() { + var v = this.getInputData(0); + if (v != undefined) { + this.properties["value"] = v; + } + }; + + WidgetProgress.prototype.onDrawForeground = function(ctx) { + //border + ctx.lineWidth = 1; + ctx.fillStyle = this.properties.color; + var v = + (this.properties.value - this.properties.min) / + (this.properties.max - this.properties.min); + v = Math.min(1, v); + v = Math.max(0, v); + ctx.fillRect(2, 2, (this.size[0] - 4) * v, this.size[1] - 4); + }; + + LiteGraph.registerNodeType("widget/progress", WidgetProgress); + + function WidgetText() { + this.addInputs("", 0); + this.properties = { + value: "...", + font: "Arial", + fontsize: 18, + color: "#AAA", + align: "left", + glowSize: 0, + decimals: 1 + }; + } + + WidgetText.title = "Text"; + WidgetText.desc = "Shows the input value"; + WidgetText.widgets = [ + { name: "resize", text: "Resize box", type: "button" }, + { name: "led_text", text: "LED", type: "minibutton" }, + { name: "normal_text", text: "Normal", type: "minibutton" } + ]; + + WidgetText.prototype.onDrawForeground = function(ctx) { + //ctx.fillStyle="#000"; + //ctx.fillRect(0,0,100,60); + ctx.fillStyle = this.properties["color"]; + var v = this.properties["value"]; + + if (this.properties["glowSize"]) { + ctx.shadowColor = this.properties.color; + ctx.shadowOffsetX = 0; + ctx.shadowOffsetY = 0; + ctx.shadowBlur = this.properties["glowSize"]; + } else { + ctx.shadowColor = "transparent"; + } + + var fontsize = this.properties["fontsize"]; + + ctx.textAlign = this.properties["align"]; + ctx.font = fontsize.toString() + "px " + this.properties["font"]; + this.str = + typeof v == "number" ? v.toFixed(this.properties["decimals"]) : v; + + if (typeof this.str == "string") { + var lines = this.str.split("\\n"); + for (var i in lines) { + ctx.fillText( + lines[i], + this.properties["align"] == "left" ? 15 : this.size[0] - 15, + fontsize * -0.15 + fontsize * (parseInt(i) + 1) + ); + } + } + + ctx.shadowColor = "transparent"; + this.last_ctx = ctx; + ctx.textAlign = "left"; + }; + + WidgetText.prototype.onExecute = function() { + var v = this.getInputData(0); + if (v != null) { + this.properties["value"] = v; + } + //this.setDirtyCanvas(true); + }; + + WidgetText.prototype.resize = function() { + if (!this.last_ctx) { + return; + } + + var lines = this.str.split("\\n"); + this.last_ctx.font = + this.properties["fontsize"] + "px " + this.properties["font"]; + var max = 0; + for (var i in lines) { + var w = this.last_ctx.measureText(lines[i]).width; + if (max < w) { + max = w; + } + } + this.size[0] = max + 20; + this.size[1] = 4 + lines.length * this.properties["fontsize"]; + + this.setDirtyCanvas(true); + }; + + WidgetText.prototype.onPropertyChanged = function(name, value) { + this.properties[name] = value; + this.str = typeof value == "number" ? value.toFixed(3) : value; + //this.resize(); + return true; + }; + + LiteGraph.registerNodeType("widget/text", WidgetText); + + function WidgetPanel() { + this.size = [200, 100]; + this.properties = { + borderColor: "#ffffff", + bgcolorTop: "#f0f0f0", + bgcolorBottom: "#e0e0e0", + shadowSize: 2, + borderRadius: 3 + }; + } + + WidgetPanel.title = "Panel"; + WidgetPanel.desc = "Non interactive panel"; + WidgetPanel.widgets = [{ name: "update", text: "Update", type: "button" }]; + + WidgetPanel.prototype.createGradient = function(ctx) { + if ( + this.properties["bgcolorTop"] == "" || + this.properties["bgcolorBottom"] == "" + ) { + this.lineargradient = 0; + return; + } + + this.lineargradient = ctx.createLinearGradient(0, 0, 0, this.size[1]); + this.lineargradient.addColorStop(0, this.properties["bgcolorTop"]); + this.lineargradient.addColorStop(1, this.properties["bgcolorBottom"]); + }; + + WidgetPanel.prototype.onDrawForeground = function(ctx) { + if (this.flags.collapsed) { + return; + } + + if (this.lineargradient == null) { + this.createGradient(ctx); + } + + if (!this.lineargradient) { + return; + } + + ctx.lineWidth = 1; + ctx.strokeStyle = this.properties["borderColor"]; + //ctx.fillStyle = "#ebebeb"; + ctx.fillStyle = this.lineargradient; + + if (this.properties["shadowSize"]) { + ctx.shadowColor = "#000"; + ctx.shadowOffsetX = 0; + ctx.shadowOffsetY = 0; + ctx.shadowBlur = this.properties["shadowSize"]; + } else { + ctx.shadowColor = "transparent"; + } + + ctx.roundRect( + 0, + 0, + this.size[0] - 1, + this.size[1] - 1, + this.properties["shadowSize"] + ); + ctx.fill(); + ctx.shadowColor = "transparent"; + ctx.stroke(); + }; + + LiteGraph.registerNodeType("widget/panel", WidgetPanel); +})(this); + (function(global) { var LiteGraph = global.LiteGraph; @@ -13731,7 +25997,7 @@ if (typeof exports != "undefined") { LiteGraph.registerNodeType("input/gamepad", GamepadInput); })(this); - + (function(global) { var LiteGraph = global.LiteGraph; @@ -14995,655 +27261,2721 @@ if (typeof exports != "undefined") { LiteGraph.registerNodeType("math3d/xyzw-to-vec4", Math3DXYZWToVec4); })(this); - + (function(global) { var LiteGraph = global.LiteGraph; + function Selector() { + this.addInput("sel", "number"); + this.addInput("A"); + this.addInput("B"); + this.addInput("C"); + this.addInput("D"); + this.addOutput("out"); - function Math3DMat4() - { - this.addInput("T", "vec3"); - this.addInput("R", "vec3"); - this.addInput("S", "vec3"); - this.addOutput("mat4", "mat4"); - this.properties = { - "T":[0,0,0], - "R":[0,0,0], - "S":[1,1,1], - R_in_degrees: true - }; - this._result = mat4.create(); - this._must_update = true; - } - - Math3DMat4.title = "mat4"; - Math3DMat4.temp_quat = new Float32Array([0,0,0,1]); - Math3DMat4.temp_mat4 = new Float32Array(16); - Math3DMat4.temp_vec3 = new Float32Array(3); - - Math3DMat4.prototype.onPropertyChanged = function(name, value) - { - this._must_update = true; - } - - Math3DMat4.prototype.onExecute = function() - { - var M = this._result; - var Q = Math3DMat4.temp_quat; - var temp_mat4 = Math3DMat4.temp_mat4; - var temp_vec3 = Math3DMat4.temp_vec3; - - var T = this.getInputData(0); - var R = this.getInputData(1); - var S = this.getInputData(2); - - if( this._must_update || T || R || S ) - { - T = T || this.properties.T; - R = R || this.properties.R; - S = S || this.properties.S; - mat4.identity( M ); - mat4.translate( M, M, T ); - if(this.properties.R_in_degrees) - { - temp_vec3.set( R ); - vec3.scale(temp_vec3,temp_vec3,DEG2RAD); - quat.fromEuler( Q, temp_vec3 ); - } - else - quat.fromEuler( Q, R ); - mat4.fromQuat( temp_mat4, Q ); - mat4.multiply( M, M, temp_mat4 ); - mat4.scale( M, M, S ); - } - - this.setOutputData(0, M); - } - - LiteGraph.registerNodeType("math3d/mat4", Math3DMat4); - - //Math 3D operation - function Math3DOperation() { - this.addInput("A", "number,vec3"); - this.addInput("B", "number,vec3"); - this.addOutput("=", "vec3"); - this.addProperty("OP", "+", "enum", { values: Math3DOperation.values }); - this._result = vec3.create(); + this.selected = 0; } - Math3DOperation.values = ["+", "-", "*", "/", "%", "^", "max", "min"]; + Selector.title = "Selector"; + Selector.desc = "selects an output"; - Math3DOperation.title = "Operation"; - Math3DOperation.desc = "Easy math 3D operators"; - Math3DOperation["@OP"] = { - type: "enum", - title: "operation", - values: Math3DOperation.values - }; - Math3DOperation.size = [100, 60]; - - Math3DOperation.prototype.getTitle = function() { - if(this.properties.OP == "max" || this.properties.OP == "min" ) - return this.properties.OP + "(A,B)"; - return "A " + this.properties.OP + " B"; - }; - - Math3DOperation.prototype.onExecute = function() { - var A = this.getInputData(0); - var B = this.getInputData(1); - if(A == null || B == null) - return; - if(A.constructor === Number) - A = [A,A,A]; - if(B.constructor === Number) - B = [B,B,B]; - - var result = this._result; - switch (this.properties.OP) { - case "+": - result = vec3.add(result,A,B); - break; - case "-": - result = vec3.sub(result,A,B); - break; - case "x": - case "X": - case "*": - result = vec3.mul(result,A,B); - break; - case "/": - result = vec3.div(result,A,B); - break; - case "%": - result[0] = A[0]%B[0]; - result[1] = A[1]%B[1]; - result[2] = A[2]%B[2]; - break; - case "^": - result[0] = Math.pow(A[0],B[0]); - result[1] = Math.pow(A[1],B[1]); - result[2] = Math.pow(A[2],B[2]); - break; - case "max": - result[0] = Math.max(A[0],B[0]); - result[1] = Math.max(A[1],B[1]); - result[2] = Math.max(A[2],B[2]); - break; - case "min": - result[0] = Math.min(A[0],B[0]); - result[1] = Math.min(A[1],B[1]); - result[2] = Math.min(A[2],B[2]); - break; - default: - console.warn("Unknown operation: " + this.properties.OP); - } - this.setOutputData(0, result); - }; - - Math3DOperation.prototype.onDrawBackground = function(ctx) { + Selector.prototype.onDrawBackground = function(ctx) { if (this.flags.collapsed) { return; } - - ctx.font = "40px Arial"; - ctx.fillStyle = "#666"; - ctx.textAlign = "center"; - ctx.fillText( - this.properties.OP, - this.size[0] * 0.5, - (this.size[1] + LiteGraph.NODE_TITLE_HEIGHT) * 0.5 - ); - ctx.textAlign = "left"; + ctx.fillStyle = "#AFB"; + var y = (this.selected + 1) * LiteGraph.NODE_SLOT_HEIGHT + 6; + ctx.beginPath(); + ctx.moveTo(50, y); + ctx.lineTo(50, y + LiteGraph.NODE_SLOT_HEIGHT); + ctx.lineTo(34, y + LiteGraph.NODE_SLOT_HEIGHT * 0.5); + ctx.fill(); }; - LiteGraph.registerNodeType("math3d/operation", Math3DOperation); + Selector.prototype.onExecute = function() { + var sel = this.getInputData(0); + if (sel == null || sel.constructor !== Number) + sel = 0; + this.selected = sel = Math.round(sel) % (this.inputs.length - 1); + var v = this.getInputData(sel + 1); + if (v !== undefined) { + this.setOutputData(0, v); + } + }; - function Math3DVec3Scale() { - this.addInput("in", "vec3"); - this.addInput("f", "number"); - this.addOutput("out", "vec3"); - this.properties = { f: 1 }; - this._data = new Float32Array(3); + Selector.prototype.onGetInputs = function() { + return [["E", 0], ["F", 0], ["G", 0], ["H", 0]]; + }; + + LiteGraph.registerNodeType("logic/selector", Selector); + + function Sequence() { + this.properties = { + sequence: "A,B,C" + }; + this.addInput("index", "number"); + this.addInput("seq"); + this.addOutput("out"); + + this.index = 0; + this.values = this.properties.sequence.split(","); } - Math3DVec3Scale.title = "vec3_scale"; - Math3DVec3Scale.desc = "scales the components of a vec3"; + Sequence.title = "Sequence"; + Sequence.desc = "select one element from a sequence from a string"; - Math3DVec3Scale.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - return; + Sequence.prototype.onPropertyChanged = function(name, value) { + if (name == "sequence") { + this.values = value.split(","); } - var f = this.getInputData(1); - if (f == null) { - f = this.properties.f; - } - - var data = this._data; - data[0] = v[0] * f; - data[1] = v[1] * f; - data[2] = v[2] * f; - this.setOutputData(0, data); }; - LiteGraph.registerNodeType("math3d/vec3-scale", Math3DVec3Scale); - - function Math3DVec3Length() { - this.addInput("in", "vec3"); - this.addOutput("out", "number"); - } - - Math3DVec3Length.title = "vec3_length"; - Math3DVec3Length.desc = "returns the module of a vector"; - - Math3DVec3Length.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - return; + Sequence.prototype.onExecute = function() { + var seq = this.getInputData(1); + if (seq && seq != this.current_sequence) { + this.values = seq.split(","); + this.current_sequence = seq; } - var dist = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); - this.setOutputData(0, dist); + var index = this.getInputData(0); + if (index == null) { + index = 0; + } + this.index = index = Math.round(index) % this.values.length; + + this.setOutputData(0, this.values[index]); }; - LiteGraph.registerNodeType("math3d/vec3-length", Math3DVec3Length); - - function Math3DVec3Normalize() { - this.addInput("in", "vec3"); - this.addOutput("out", "vec3"); - this._data = new Float32Array(3); - } - - Math3DVec3Normalize.title = "vec3_normalize"; - Math3DVec3Normalize.desc = "returns the vector normalized"; - - Math3DVec3Normalize.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - return; - } - var dist = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); - var data = this._data; - data[0] = v[0] / dist; - data[1] = v[1] / dist; - data[2] = v[2] / dist; - - this.setOutputData(0, data); - }; - - LiteGraph.registerNodeType("math3d/vec3-normalize", Math3DVec3Normalize); - - function Math3DVec3Lerp() { - this.addInput("A", "vec3"); - this.addInput("B", "vec3"); - this.addInput("f", "vec3"); - this.addOutput("out", "vec3"); - this.properties = { f: 0.5 }; - this._data = new Float32Array(3); - } - - Math3DVec3Lerp.title = "vec3_lerp"; - Math3DVec3Lerp.desc = "returns the interpolated vector"; - - Math3DVec3Lerp.prototype.onExecute = function() { - var A = this.getInputData(0); - if (A == null) { - return; - } - var B = this.getInputData(1); - if (B == null) { - return; - } - var f = this.getInputOrProperty("f"); - - var data = this._data; - data[0] = A[0] * (1 - f) + B[0] * f; - data[1] = A[1] * (1 - f) + B[1] * f; - data[2] = A[2] * (1 - f) + B[2] * f; - - this.setOutputData(0, data); - }; - - LiteGraph.registerNodeType("math3d/vec3-lerp", Math3DVec3Lerp); - - function Math3DVec3Dot() { - this.addInput("A", "vec3"); - this.addInput("B", "vec3"); - this.addOutput("out", "number"); - } - - Math3DVec3Dot.title = "vec3_dot"; - Math3DVec3Dot.desc = "returns the dot product"; - - Math3DVec3Dot.prototype.onExecute = function() { - var A = this.getInputData(0); - if (A == null) { - return; - } - var B = this.getInputData(1); - if (B == null) { - return; - } - - var dot = A[0] * B[0] + A[1] * B[1] + A[2] * B[2]; - this.setOutputData(0, dot); - }; - - LiteGraph.registerNodeType("math3d/vec3-dot", Math3DVec3Dot); - - //if glMatrix is installed... - if (global.glMatrix) { - function Math3DQuaternion() { - this.addOutput("quat", "quat"); - this.properties = { x: 0, y: 0, z: 0, w: 1, normalize: false }; - this._value = quat.create(); - } - - Math3DQuaternion.title = "Quaternion"; - Math3DQuaternion.desc = "quaternion"; - - Math3DQuaternion.prototype.onExecute = function() { - this._value[0] = this.getInputOrProperty("x"); - this._value[1] = this.getInputOrProperty("y"); - this._value[2] = this.getInputOrProperty("z"); - this._value[3] = this.getInputOrProperty("w"); - if (this.properties.normalize) { - quat.normalize(this._value, this._value); - } - this.setOutputData(0, this._value); - }; - - Math3DQuaternion.prototype.onGetInputs = function() { - return [ - ["x", "number"], - ["y", "number"], - ["z", "number"], - ["w", "number"] - ]; - }; - - LiteGraph.registerNodeType("math3d/quaternion", Math3DQuaternion); - - function Math3DRotation() { - this.addInputs([["degrees", "number"], ["axis", "vec3"]]); - this.addOutput("quat", "quat"); - this.properties = { angle: 90.0, axis: vec3.fromValues(0, 1, 0) }; - - this._value = quat.create(); - } - - Math3DRotation.title = "Rotation"; - Math3DRotation.desc = "quaternion rotation"; - - Math3DRotation.prototype.onExecute = function() { - var angle = this.getInputData(0); - if (angle == null) { - angle = this.properties.angle; - } - var axis = this.getInputData(1); - if (axis == null) { - axis = this.properties.axis; - } - - var R = quat.setAxisAngle(this._value, axis, angle * 0.0174532925); - this.setOutputData(0, R); - }; - - LiteGraph.registerNodeType("math3d/rotation", Math3DRotation); - - //Math3D rotate vec3 - function Math3DRotateVec3() { - this.addInputs([["vec3", "vec3"], ["quat", "quat"]]); - this.addOutput("result", "vec3"); - this.properties = { vec: [0, 0, 1] }; - } - - Math3DRotateVec3.title = "Rot. Vec3"; - Math3DRotateVec3.desc = "rotate a point"; - - Math3DRotateVec3.prototype.onExecute = function() { - var vec = this.getInputData(0); - if (vec == null) { - vec = this.properties.vec; - } - var quat = this.getInputData(1); - if (quat == null) { - this.setOutputData(vec); - } else { - this.setOutputData( - 0, - vec3.transformQuat(vec3.create(), vec, quat) - ); - } - }; - - LiteGraph.registerNodeType("math3d/rotate_vec3", Math3DRotateVec3); - - function Math3DMultQuat() { - this.addInputs([["A", "quat"], ["B", "quat"]]); - this.addOutput("A*B", "quat"); - - this._value = quat.create(); - } - - Math3DMultQuat.title = "Mult. Quat"; - Math3DMultQuat.desc = "rotate quaternion"; - - Math3DMultQuat.prototype.onExecute = function() { - var A = this.getInputData(0); - if (A == null) { - return; - } - var B = this.getInputData(1); - if (B == null) { - return; - } - - var R = quat.multiply(this._value, A, B); - this.setOutputData(0, R); - }; - - LiteGraph.registerNodeType("math3d/mult-quat", Math3DMultQuat); - - function Math3DQuatSlerp() { - this.addInputs([ - ["A", "quat"], - ["B", "quat"], - ["factor", "number"] - ]); - this.addOutput("slerp", "quat"); - this.addProperty("factor", 0.5); - - this._value = quat.create(); - } - - Math3DQuatSlerp.title = "Quat Slerp"; - Math3DQuatSlerp.desc = "quaternion spherical interpolation"; - - Math3DQuatSlerp.prototype.onExecute = function() { - var A = this.getInputData(0); - if (A == null) { - return; - } - var B = this.getInputData(1); - if (B == null) { - return; - } - var factor = this.properties.factor; - if (this.getInputData(2) != null) { - factor = this.getInputData(2); - } - - var R = quat.slerp(this._value, A, B, factor); - this.setOutputData(0, R); - }; - - LiteGraph.registerNodeType("math3d/quat-slerp", Math3DQuatSlerp); - - - //Math3D rotate vec3 - function Math3DRemapRange() { - this.addInput("vec3", "vec3"); - this.addOutput("remap", "vec3"); - this.addOutput("clamped", "vec3"); - this.properties = { clamp: true, range_min: [-1, -1, 0], range_max: [1, 1, 0], target_min: [-1,-1,0], target_max:[1,1,0] }; - this._value = vec3.create(); - this._clamped = vec3.create(); - } - - Math3DRemapRange.title = "Remap Range"; - Math3DRemapRange.desc = "remap a 3D range"; - - Math3DRemapRange.prototype.onExecute = function() { - var vec = this.getInputData(0); - if(vec) - this._value.set(vec); - var range_min = this.properties.range_min; - var range_max = this.properties.range_max; - var target_min = this.properties.target_min; - var target_max = this.properties.target_max; - - //swap to avoid errors - /* - if(range_min > range_max) - { - range_min = range_max; - range_max = this.properties.range_min; - } - - if(target_min > target_max) - { - target_min = target_max; - target_max = this.properties.target_min; - } - */ - - for(var i = 0; i < 3; ++i) - { - var r = range_max[i] - range_min[i]; - this._clamped[i] = Math.clamp( this._value[i], range_min[i], range_max[i] ); - if(r == 0) - { - this._value[i] = (target_min[i] + target_max[i]) * 0.5; - continue; - } - - var n = (this._value[i] - range_min[i]) / r; - if(this.properties.clamp) - n = Math.clamp(n,0,1); - var t = target_max[i] - target_min[i]; - this._value[i] = target_min[i] + n * t; - } - - this.setOutputData(0,this._value); - this.setOutputData(1,this._clamped); - }; - - LiteGraph.registerNodeType("math3d/remap_range", Math3DRemapRange); - - - - } //glMatrix - else - console.warn("No glmatrix found, some Math3D nodes may not work"); - + LiteGraph.registerNodeType("logic/sequence", Sequence); })(this); - -//basic nodes + (function(global) { var LiteGraph = global.LiteGraph; - function toString(a) { - return String(a); - } + //Works with Litegl.js to create WebGL nodes + global.LGraphTexture = null; - LiteGraph.wrapFunctionAsNode("string/toString", compare, ["*"], "String"); + if (typeof GL == "undefined") + return; - function compare(a, b) { - return a == b; - } + LGraphCanvas.link_type_colors["Texture"] = "#987"; - LiteGraph.wrapFunctionAsNode( - "string/compare", - compare, - ["string", "string"], - "boolean" - ); + function LGraphTexture() { + this.addOutput("tex", "Texture"); + this.addOutput("name", "string"); + this.properties = { name: "", filter: true }; + this.size = [ + LGraphTexture.image_preview_size, + LGraphTexture.image_preview_size + ]; + } - function concatenate(a, b) { - if (a === undefined) { - return b; - } - if (b === undefined) { - return a; - } - return a + b; - } + global.LGraphTexture = LGraphTexture; - LiteGraph.wrapFunctionAsNode( - "string/concatenate", - concatenate, - ["string", "string"], - "string" - ); + LGraphTexture.title = "Texture"; + LGraphTexture.desc = "Texture"; + LGraphTexture.widgets_info = { + name: { widget: "texture" }, + filter: { widget: "checkbox" } + }; - function contains(a, b) { - if (a === undefined || b === undefined) { - return false; - } - return a.indexOf(b) != -1; - } + //REPLACE THIS TO INTEGRATE WITH YOUR FRAMEWORK + LGraphTexture.loadTextureCallback = null; //function in charge of loading textures when not present in the container + LGraphTexture.image_preview_size = 256; - LiteGraph.wrapFunctionAsNode( - "string/contains", - contains, - ["string", "string"], - "boolean" - ); + //flags to choose output texture type + LGraphTexture.PASS_THROUGH = 1; //do not apply FX + LGraphTexture.COPY = 2; //create new texture with the same properties as the origin texture + LGraphTexture.LOW = 3; //create new texture with low precision (byte) + LGraphTexture.HIGH = 4; //create new texture with high precision (half-float) + LGraphTexture.REUSE = 5; //reuse input texture + LGraphTexture.DEFAULT = 2; - function toUpperCase(a) { - if (a != null && a.constructor === String) { - return a.toUpperCase(); - } - return a; - } + LGraphTexture.MODE_VALUES = { + "pass through": LGraphTexture.PASS_THROUGH, + copy: LGraphTexture.COPY, + low: LGraphTexture.LOW, + high: LGraphTexture.HIGH, + reuse: LGraphTexture.REUSE, + default: LGraphTexture.DEFAULT + }; - LiteGraph.wrapFunctionAsNode( - "string/toUpperCase", - toUpperCase, - ["string"], - "string" - ); + //returns the container where all the loaded textures are stored (overwrite if you have a Resources Manager) + LGraphTexture.getTexturesContainer = function() { + return gl.textures; + }; - function split(str, separator) { - if(separator == null) - separator = this.properties.separator; - if (str == null ) - return []; - if( str.constructor === String ) - return str.split(separator || " "); - else if( str.constructor === Array ) - { - var r = []; - for(var i = 0; i < str.length; ++i) - r[i] = str[i].split(separator || " "); - return r; + //process the loading of a texture (overwrite it if you have a Resources Manager) + LGraphTexture.loadTexture = function(name, options) { + options = options || {}; + var url = name; + if (url.substr(0, 7) == "http://") { + if (LiteGraph.proxy) { + //proxy external files + url = LiteGraph.proxy + url.substr(7); + } } - return null; - } - LiteGraph.wrapFunctionAsNode( - "string/split", - split, - ["string,array", "string"], - "array", - { separator: "," } - ); + var container = LGraphTexture.getTexturesContainer(); + var tex = (container[name] = GL.Texture.fromURL(url, options)); + return tex; + }; - function toFixed(a) { - if (a != null && a.constructor === Number) { - return a.toFixed(this.properties.precision); - } - return a; - } + LGraphTexture.getTexture = function(name) { + var container = this.getTexturesContainer(); - LiteGraph.wrapFunctionAsNode( - "string/toFixed", - toFixed, - ["number"], - "string", - { precision: 0 } - ); + if (!container) { + throw "Cannot load texture, container of textures not found"; + } + var tex = container[name]; + if (!tex && name && name[0] != ":") { + return this.loadTexture(name); + } - function StringToTable() { - this.addInput("", "string"); - this.addOutput("table", "table"); - this.addOutput("rows", "number"); - this.addProperty("value", ""); - this.addProperty("separator", ","); - this._table = null; - } + return tex; + }; - StringToTable.title = "toTable"; - StringToTable.desc = "Splits a string to table"; + //used to compute the appropiate output texture + LGraphTexture.getTargetTexture = function(origin, target, mode) { + if (!origin) { + throw "LGraphTexture.getTargetTexture expects a reference texture"; + } - StringToTable.prototype.onExecute = function() { - var input = this.getInputData(0); - if(!input) + var tex_type = null; + + switch (mode) { + case LGraphTexture.LOW: + tex_type = gl.UNSIGNED_BYTE; + break; + case LGraphTexture.HIGH: + tex_type = gl.HIGH_PRECISION_FORMAT; + break; + case LGraphTexture.REUSE: + return origin; + break; + case LGraphTexture.COPY: + default: + tex_type = origin ? origin.type : gl.UNSIGNED_BYTE; + break; + } + + if ( + !target || + target.width != origin.width || + target.height != origin.height || + target.type != tex_type + ) { + target = new GL.Texture(origin.width, origin.height, { + type: tex_type, + format: gl.RGBA, + filter: gl.LINEAR + }); + } + + return target; + }; + + LGraphTexture.getTextureType = function(precision, ref_texture) { + var type = ref_texture ? ref_texture.type : gl.UNSIGNED_BYTE; + switch (precision) { + case LGraphTexture.HIGH: + type = gl.HIGH_PRECISION_FORMAT; + break; + case LGraphTexture.LOW: + type = gl.UNSIGNED_BYTE; + break; + //no default + } + return type; + }; + + LGraphTexture.getWhiteTexture = function() { + if (this._white_texture) { + return this._white_texture; + } + var texture = (this._white_texture = GL.Texture.fromMemory( + 1, + 1, + [255, 255, 255, 255], + { format: gl.RGBA, wrap: gl.REPEAT, filter: gl.NEAREST } + )); + return texture; + }; + + LGraphTexture.getNoiseTexture = function() { + if (this._noise_texture) { + return this._noise_texture; + } + + var noise = new Uint8Array(512 * 512 * 4); + for (var i = 0; i < 512 * 512 * 4; ++i) { + noise[i] = Math.random() * 255; + } + + var texture = GL.Texture.fromMemory(512, 512, noise, { + format: gl.RGBA, + wrap: gl.REPEAT, + filter: gl.NEAREST + }); + this._noise_texture = texture; + return texture; + }; + + LGraphTexture.prototype.onDropFile = function(data, filename, file) { + if (!data) { + this._drop_texture = null; + this.properties.name = ""; + } else { + var texture = null; + if (typeof data == "string") { + texture = GL.Texture.fromURL(data); + } else if (filename.toLowerCase().indexOf(".dds") != -1) { + texture = GL.Texture.fromDDSInMemory(data); + } else { + var blob = new Blob([file]); + var url = URL.createObjectURL(blob); + texture = GL.Texture.fromURL(url); + } + + this._drop_texture = texture; + this.properties.name = filename; + } + }; + + LGraphTexture.prototype.getExtraMenuOptions = function(graphcanvas) { + var that = this; + if (!this._drop_texture) { return; - var separator = this.properties.separator || ","; - if(input != this._str || separator != this._last_separator ) - { - this._last_separator = separator; - this._str = input; - this._table = input.split("\n").map(function(a){ return a.trim().split(separator)}); } - this.setOutputData(0, this._table ); - this.setOutputData(1, this._table ? this._table.length : 0 ); - }; + return [ + { + content: "Clear", + callback: function() { + that._drop_texture = null; + that.properties.name = ""; + } + } + ]; + }; - LiteGraph.registerNodeType("string/toTable", StringToTable); + LGraphTexture.prototype.onExecute = function() { + var tex = null; + if (this.isOutputConnected(1)) { + tex = this.getInputData(0); + } + if (!tex && this._drop_texture) { + tex = this._drop_texture; + } + + if (!tex && this.properties.name) { + tex = LGraphTexture.getTexture(this.properties.name); + } + + if (!tex) { + this.setOutputData( 0, null ); + this.setOutputData( 1, "" ); + return; + } + + this._last_tex = tex; + + if (this.properties.filter === false) { + tex.setParameter(gl.TEXTURE_MAG_FILTER, gl.NEAREST); + } else { + tex.setParameter(gl.TEXTURE_MAG_FILTER, gl.LINEAR); + } + + this.setOutputData( 0, tex ); + this.setOutputData( 1, tex.fullpath || tex.filename ); + + for (var i = 2; i < this.outputs.length; i++) { + var output = this.outputs[i]; + if (!output) { + continue; + } + var v = null; + if (output.name == "width") { + v = tex.width; + } else if (output.name == "height") { + v = tex.height; + } else if (output.name == "aspect") { + v = tex.width / tex.height; + } + this.setOutputData(i, v); + } + }; + + LGraphTexture.prototype.onResourceRenamed = function( + old_name, + new_name + ) { + if (this.properties.name == old_name) { + this.properties.name = new_name; + } + }; + + LGraphTexture.prototype.onDrawBackground = function(ctx) { + if (this.flags.collapsed || this.size[1] <= 20) { + return; + } + + if (this._drop_texture && ctx.webgl) { + ctx.drawImage( + this._drop_texture, + 0, + 0, + this.size[0], + this.size[1] + ); + //this._drop_texture.renderQuad(this.pos[0],this.pos[1],this.size[0],this.size[1]); + return; + } + + //Different texture? then get it from the GPU + if (this._last_preview_tex != this._last_tex) { + if (ctx.webgl) { + this._canvas = this._last_tex; + } else { + var tex_canvas = LGraphTexture.generateLowResTexturePreview( + this._last_tex + ); + if (!tex_canvas) { + return; + } + + this._last_preview_tex = this._last_tex; + this._canvas = cloneCanvas(tex_canvas); + } + } + + if (!this._canvas) { + return; + } + + //render to graph canvas + ctx.save(); + if (!ctx.webgl) { + //reverse image + ctx.translate(0, this.size[1]); + ctx.scale(1, -1); + } + ctx.drawImage(this._canvas, 0, 0, this.size[0], this.size[1]); + ctx.restore(); + }; + + //very slow, used at your own risk + LGraphTexture.generateLowResTexturePreview = function(tex) { + if (!tex) { + return null; + } + + var size = LGraphTexture.image_preview_size; + var temp_tex = tex; + + if (tex.format == gl.DEPTH_COMPONENT) { + return null; + } //cannot generate from depth + + //Generate low-level version in the GPU to speed up + if (tex.width > size || tex.height > size) { + temp_tex = this._preview_temp_tex; + if (!this._preview_temp_tex) { + temp_tex = new GL.Texture(size, size, { + minFilter: gl.NEAREST + }); + this._preview_temp_tex = temp_tex; + } + + //copy + tex.copyTo(temp_tex); + tex = temp_tex; + } + + //create intermediate canvas with lowquality version + var tex_canvas = this._preview_canvas; + if (!tex_canvas) { + tex_canvas = createCanvas(size, size); + this._preview_canvas = tex_canvas; + } + + if (temp_tex) { + temp_tex.toCanvas(tex_canvas); + } + return tex_canvas; + }; + + LGraphTexture.prototype.getResources = function(res) { + if(this.properties.name) + res[this.properties.name] = GL.Texture; + return res; + }; + + LGraphTexture.prototype.onGetInputs = function() { + return [["in", "Texture"]]; + }; + + LGraphTexture.prototype.onGetOutputs = function() { + return [ + ["width", "number"], + ["height", "number"], + ["aspect", "number"] + ]; + }; + + //used to replace shader code + LGraphTexture.replaceCode = function( code, context ) + { + return code.replace(/\{\{[a-zA-Z0-9_]*\}\}/g, function(v){ + v = v.replace( /[\{\}]/g, "" ); + return context[v] || ""; + }); + } + + LiteGraph.registerNodeType("texture/texture", LGraphTexture); + + //************************** + function LGraphTexturePreview() { + this.addInput("Texture", "Texture"); + this.properties = { flipY: false }; + this.size = [ + LGraphTexture.image_preview_size, + LGraphTexture.image_preview_size + ]; + } + + LGraphTexturePreview.title = "Preview"; + LGraphTexturePreview.desc = "Show a texture in the graph canvas"; + LGraphTexturePreview.allow_preview = false; + + LGraphTexturePreview.prototype.onDrawBackground = function(ctx) { + if (this.flags.collapsed) { + return; + } + + if (!ctx.webgl && !LGraphTexturePreview.allow_preview) { + return; + } //not working well + + var tex = this.getInputData(0); + if (!tex) { + return; + } + + var tex_canvas = null; + + if (!tex.handle && ctx.webgl) { + tex_canvas = tex; + } else { + tex_canvas = LGraphTexture.generateLowResTexturePreview(tex); + } + + //render to graph canvas + ctx.save(); + if (this.properties.flipY) { + ctx.translate(0, this.size[1]); + ctx.scale(1, -1); + } + ctx.drawImage(tex_canvas, 0, 0, this.size[0], this.size[1]); + ctx.restore(); + }; + + LiteGraph.registerNodeType("texture/preview", LGraphTexturePreview); + + //************************************** + + function LGraphTextureSave() { + this.addInput("Texture", "Texture"); + this.addOutput("tex", "Texture"); + this.addOutput("name", "string"); + this.properties = { name: "", generate_mipmaps: false }; + } + + LGraphTextureSave.title = "Save"; + LGraphTextureSave.desc = "Save a texture in the repository"; + + LGraphTextureSave.prototype.getPreviewTexture = function() + { + return this._texture; + } + + LGraphTextureSave.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (this.properties.generate_mipmaps) { + tex.bind(0); + tex.setParameter( gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR ); + gl.generateMipmap(tex.texture_type); + tex.unbind(0); + } + + if (this.properties.name) { + //for cases where we want to perform something when storing it + if (LGraphTexture.storeTexture) { + LGraphTexture.storeTexture(this.properties.name, tex); + } else { + var container = LGraphTexture.getTexturesContainer(); + container[this.properties.name] = tex; + } + } + + this._texture = tex; + this.setOutputData(0, tex); + this.setOutputData(1, this.properties.name); + }; + + LiteGraph.registerNodeType("texture/save", LGraphTextureSave); + + //**************************************************** + + function LGraphTextureOperation() { + this.addInput("Texture", "Texture"); + this.addInput("TextureB", "Texture"); + this.addInput("value", "number"); + this.addOutput("Texture", "Texture"); + this.help = "

pixelcode must be vec3, uvcode must be vec2, is optional

\ +

uv: tex. coords

color: texture colorB: textureB

time: scene time value: input value

For multiline you must type: result = ...

"; + + this.properties = { + value: 1, + pixelcode: "color + colorB * value", + uvcode: "", + precision: LGraphTexture.DEFAULT + }; + + this.has_error = false; + } + + LGraphTextureOperation.widgets_info = { + uvcode: { widget: "code" }, + pixelcode: { widget: "code" }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureOperation.title = "Operation"; + LGraphTextureOperation.desc = "Texture shader operation"; + + LGraphTextureOperation.presets = {}; + + LGraphTextureOperation.prototype.getExtraMenuOptions = function( + graphcanvas + ) { + var that = this; + var txt = !that.properties.show ? "Show Texture" : "Hide Texture"; + return [ + { + content: txt, + callback: function() { + that.properties.show = !that.properties.show; + } + } + ]; + }; + + LGraphTextureOperation.prototype.onPropertyChanged = function() + { + this.has_error = false; + } + + LGraphTextureOperation.prototype.onDrawBackground = function(ctx) { + if ( + this.flags.collapsed || + this.size[1] <= 20 || + !this.properties.show + ) { + return; + } + + if (!this._tex) { + return; + } + + //only works if using a webgl renderer + if (this._tex.gl != ctx) { + return; + } + + //render to graph canvas + ctx.save(); + ctx.drawImage(this._tex, 0, 0, this.size[0], this.size[1]); + ctx.restore(); + }; + + LGraphTextureOperation.prototype.onExecute = function() { + var tex = this.getInputData(0); + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, tex); + return; + } + + var texB = this.getInputData(1); + + if (!this.properties.uvcode && !this.properties.pixelcode) { + return; + } + + var width = 512; + var height = 512; + if (tex) { + width = tex.width; + height = tex.height; + } else if (texB) { + width = texB.width; + height = texB.height; + } + + if(!texB) + texB = GL.Texture.getWhiteTexture(); + + var type = LGraphTexture.getTextureType( this.properties.precision, tex ); + + if (!tex && !this._tex) { + this._tex = new GL.Texture(width, height, { type: type, format: gl.RGBA, filter: gl.LINEAR }); + } else { + this._tex = LGraphTexture.getTargetTexture( tex || this._tex, this._tex, this.properties.precision ); + } + + var uvcode = ""; + if (this.properties.uvcode) { + uvcode = "uv = " + this.properties.uvcode; + if (this.properties.uvcode.indexOf(";") != -1) { + //there are line breaks, means multiline code + uvcode = this.properties.uvcode; + } + } + + var pixelcode = ""; + if (this.properties.pixelcode) { + pixelcode = "result = " + this.properties.pixelcode; + if (this.properties.pixelcode.indexOf(";") != -1) { + //there are line breaks, means multiline code + pixelcode = this.properties.pixelcode; + } + } + + var shader = this._shader; + + if ( !this.has_error && (!shader || this._shader_code != uvcode + "|" + pixelcode) ) { + + var final_pixel_code = LGraphTexture.replaceCode( LGraphTextureOperation.pixel_shader, { UV_CODE:uvcode, PIXEL_CODE:pixelcode }); + + try { + shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, final_pixel_code ); + this.boxcolor = "#00FF00"; + } catch (err) { + //console.log("Error compiling shader: ", err, final_pixel_code ); + GL.Shader.dumpErrorToConsole(err,Shader.SCREEN_VERTEX_SHADER, final_pixel_code); + this.boxcolor = "#FF0000"; + this.has_error = true; + return; + } + this._shader = shader; + this._shader_code = uvcode + "|" + pixelcode; + } + + if(!this._shader) + return; + + var value = this.getInputData(2); + if (value != null) { + this.properties.value = value; + } else { + value = parseFloat(this.properties.value); + } + + var time = this.graph.getTime(); + + this._tex.drawTo(function() { + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + gl.disable(gl.BLEND); + if (tex) { + tex.bind(0); + } + if (texB) { + texB.bind(1); + } + var mesh = Mesh.getScreenQuad(); + shader + .uniforms({ + u_texture: 0, + u_textureB: 1, + value: value, + texSize: [width, height], + time: time + }) + .draw(mesh); + }); + + this.setOutputData(0, this._tex); + }; + + LGraphTextureOperation.pixel_shader = + "precision highp float;\n\ + \n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_textureB;\n\ + varying vec2 v_coord;\n\ + uniform vec2 texSize;\n\ + uniform float time;\n\ + uniform float value;\n\ + \n\ + void main() {\n\ + vec2 uv = v_coord;\n\ + {{UV_CODE}};\n\ + vec4 color4 = texture2D(u_texture, uv);\n\ + vec3 color = color4.rgb;\n\ + vec4 color4B = texture2D(u_textureB, uv);\n\ + vec3 colorB = color4B.rgb;\n\ + vec3 result = color;\n\ + float alpha = 1.0;\n\ + {{PIXEL_CODE}};\n\ + gl_FragColor = vec4(result, alpha);\n\ + }\n\ + "; + + LGraphTextureOperation.registerPreset = function ( name, code ) + { + LGraphTextureOperation.presets[name] = code; + } + + LGraphTextureOperation.registerPreset("",""); + LGraphTextureOperation.registerPreset("bypass","color"); + LGraphTextureOperation.registerPreset("add","color + colorB * value"); + LGraphTextureOperation.registerPreset("substract","(color - colorB) * value"); + LGraphTextureOperation.registerPreset("mate","mix( color, colorB, color4B.a * value)"); + LGraphTextureOperation.registerPreset("invert","vec3(1.0) - color"); + LGraphTextureOperation.registerPreset("multiply","color * colorB * value"); + LGraphTextureOperation.registerPreset("divide","(color / colorB) / value"); + LGraphTextureOperation.registerPreset("difference","abs(color - colorB) * value"); + LGraphTextureOperation.registerPreset("max","max(color, colorB) * value"); + LGraphTextureOperation.registerPreset("min","min(color, colorB) * value"); + LGraphTextureOperation.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"); + LGraphTextureOperation.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"); + LGraphTextureOperation.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"); + LGraphTextureOperation.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"); + + //webglstudio stuff... + LGraphTextureOperation.prototype.onInspect = function(widgets) + { + var that = this; + widgets.addCombo("Presets","",{ values: Object.keys(LGraphTextureOperation.presets), callback: function(v){ + var code = LGraphTextureOperation.presets[v]; + if(!code) + return; + that.setProperty("pixelcode",code); + that.title = v; + widgets.refresh(); + }}); + } + + LiteGraph.registerNodeType("texture/operation", LGraphTextureOperation); + + //**************************************************** + + function LGraphTextureShader() { + this.addOutput("out", "Texture"); + this.properties = { + code: "", + u_value: 1, + u_color: [1,1,1,1], + width: 512, + height: 512, + precision: LGraphTexture.DEFAULT + }; + + this.properties.code = LGraphTextureShader.pixel_shader; + this._uniforms = { u_value: 1, u_color: vec4.create(), in_texture: 0, texSize: vec2.create(), time: 0 }; + } + + LGraphTextureShader.title = "Shader"; + LGraphTextureShader.desc = "Texture shader"; + LGraphTextureShader.widgets_info = { + code: { type: "code", lang: "glsl" }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureShader.prototype.onPropertyChanged = function( + name, + value + ) { + if (name != "code") { + return; + } + + var shader = this.getShader(); + if (!shader) { + return; + } + + //update connections + var uniforms = shader.uniformInfo; + + //remove deprecated slots + if (this.inputs) { + var already = {}; + for (var i = 0; i < this.inputs.length; ++i) { + var info = this.getInputInfo(i); + if (!info) { + continue; + } + + if (uniforms[info.name] && !already[info.name]) { + already[info.name] = true; + continue; + } + this.removeInput(i); + i--; + } + } + + //update existing ones + for (var i in uniforms) { + var info = shader.uniformInfo[i]; + if (info.loc === null) { + continue; + } //is an attribute, not a uniform + if (i == "time") { + //default one + continue; + } + + var type = "number"; + if (this._shader.samplers[i]) { + type = "texture"; + } else { + switch (info.size) { + case 1: + type = "number"; + break; + case 2: + type = "vec2"; + break; + case 3: + type = "vec3"; + break; + case 4: + type = "vec4"; + break; + case 9: + type = "mat3"; + break; + case 16: + type = "mat4"; + break; + default: + continue; + } + } + + var slot = this.findInputSlot(i); + if (slot == -1) { + this.addInput(i, type); + continue; + } + + var input_info = this.getInputInfo(slot); + if (!input_info) { + this.addInput(i, type); + } else { + if (input_info.type == type) { + continue; + } + this.removeInput(slot, type); + this.addInput(i, type); + } + } + }; + + LGraphTextureShader.prototype.getShader = function() { + //replug + if (this._shader && this._shader_code == this.properties.code) { + return this._shader; + } + + this._shader_code = this.properties.code; + this._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, this.properties.code ); + if (!this._shader) { + this.boxcolor = "red"; + return null; + } else { + this.boxcolor = "green"; + } + return this._shader; + }; + + LGraphTextureShader.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { + return; + } //saves work + + var shader = this.getShader(); + if (!shader) { + return; + } + + var tex_slot = 0; + var in_tex = null; + + //set uniforms + if(this.inputs) + for (var i = 0; i < this.inputs.length; ++i) { + var info = this.getInputInfo(i); + var data = this.getInputData(i); + if (data == null) { + continue; + } + + if (data.constructor === GL.Texture) { + data.bind(tex_slot); + if (!in_tex) { + in_tex = data; + } + data = tex_slot; + tex_slot++; + } + shader.setUniform(info.name, data); //data is tex_slot + } + + var uniforms = this._uniforms; + var type = LGraphTexture.getTextureType( this.properties.precision, in_tex ); + + //render to texture + var w = this.properties.width | 0; + var h = this.properties.height | 0; + if (w == 0) { + w = in_tex ? in_tex.width : gl.canvas.width; + } + if (h == 0) { + h = in_tex ? in_tex.height : gl.canvas.height; + } + uniforms.texSize[0] = w; + uniforms.texSize[1] = h; + uniforms.time = this.graph.getTime(); + uniforms.u_value = this.properties.u_value; + uniforms.u_color.set( this.properties.u_color ); + + if ( !this._tex || this._tex.type != type || this._tex.width != w || this._tex.height != h ) { + this._tex = new GL.Texture(w, h, { type: type, format: gl.RGBA, filter: gl.LINEAR }); + } + var tex = this._tex; + tex.drawTo(function() { + shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad()); + }); + + this.setOutputData(0, this._tex); + }; + + LGraphTextureShader.pixel_shader = +"precision highp float;\n\ +\n\ +varying vec2 v_coord;\n\ +uniform float time; //time in seconds\n\ +uniform vec2 texSize; //tex resolution\n\ +uniform float u_value;\n\ +uniform vec4 u_color;\n\n\ +void main() {\n\ + vec2 uv = v_coord;\n\ + vec3 color = vec3(0.0);\n\ + //your code here\n\ + color.xy=uv;\n\n\ + gl_FragColor = vec4(color, 1.0);\n\ +}\n\ +"; + + LiteGraph.registerNodeType("texture/shader", LGraphTextureShader); + + // Texture Scale Offset + + function LGraphTextureScaleOffset() { + this.addInput("in", "Texture"); + this.addInput("scale", "vec2"); + this.addInput("offset", "vec2"); + this.addOutput("out", "Texture"); + this.properties = { + offset: vec2.fromValues(0, 0), + scale: vec2.fromValues(1, 1), + precision: LGraphTexture.DEFAULT + }; + } + + LGraphTextureScaleOffset.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureScaleOffset.title = "Scale/Offset"; + LGraphTextureScaleOffset.desc = "Applies an scaling and offseting"; + + LGraphTextureScaleOffset.prototype.onExecute = function() { + var tex = this.getInputData(0); + + if (!this.isOutputConnected(0) || !tex) { + return; + } //saves work + + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, tex); + return; + } + + var width = tex.width; + var height = tex.height; + var type = this.precision === LGraphTexture.LOW ? gl.UNSIGNED_BYTE : gl.HIGH_PRECISION_FORMAT; + if (this.precision === LGraphTexture.DEFAULT) { + type = tex.type; + } + + if ( + !this._tex || + this._tex.width != width || + this._tex.height != height || + this._tex.type != type + ) { + this._tex = new GL.Texture(width, height, { + type: type, + format: gl.RGBA, + filter: gl.LINEAR + }); + } + + var shader = this._shader; + + if (!shader) { + shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureScaleOffset.pixel_shader + ); + } + + var scale = this.getInputData(1); + if (scale) { + this.properties.scale[0] = scale[0]; + this.properties.scale[1] = scale[1]; + } else { + scale = this.properties.scale; + } + + var offset = this.getInputData(2); + if (offset) { + this.properties.offset[0] = offset[0]; + this.properties.offset[1] = offset[1]; + } else { + offset = this.properties.offset; + } + + this._tex.drawTo(function() { + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + gl.disable(gl.BLEND); + tex.bind(0); + var mesh = Mesh.getScreenQuad(); + shader + .uniforms({ + u_texture: 0, + u_scale: scale, + u_offset: offset + }) + .draw(mesh); + }); + + this.setOutputData(0, this._tex); + }; + + LGraphTextureScaleOffset.pixel_shader = + "precision highp float;\n\ + \n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_textureB;\n\ + varying vec2 v_coord;\n\ + uniform vec2 u_scale;\n\ + uniform vec2 u_offset;\n\ + \n\ + void main() {\n\ + vec2 uv = v_coord;\n\ + uv = uv / u_scale - u_offset;\n\ + gl_FragColor = texture2D(u_texture, uv);\n\ + }\n\ + "; + + LiteGraph.registerNodeType( + "texture/scaleOffset", + LGraphTextureScaleOffset + ); + + // Warp (distort a texture) ************************* + + function LGraphTextureWarp() { + this.addInput("in", "Texture"); + this.addInput("warp", "Texture"); + this.addInput("factor", "number"); + this.addOutput("out", "Texture"); + this.properties = { + factor: 0.01, + scale: [1,1], + offset: [0,0], + precision: LGraphTexture.DEFAULT + }; + + this._uniforms = { + u_texture: 0, + u_textureB: 1, + u_factor: 1, + u_scale: vec2.create(), + u_offset: vec2.create() + }; + } + + LGraphTextureWarp.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureWarp.title = "Warp"; + LGraphTextureWarp.desc = "Texture warp operation"; + + LGraphTextureWarp.prototype.onExecute = function() { + var tex = this.getInputData(0); + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, tex); + return; + } + + var texB = this.getInputData(1); + + var width = 512; + var height = 512; + var type = gl.UNSIGNED_BYTE; + if (tex) { + width = tex.width; + height = tex.height; + type = tex.type; + } else if (texB) { + width = texB.width; + height = texB.height; + type = texB.type; + } + + if (!tex && !this._tex) { + this._tex = new GL.Texture(width, height, { + type: + this.precision === LGraphTexture.LOW + ? gl.UNSIGNED_BYTE + : gl.HIGH_PRECISION_FORMAT, + format: gl.RGBA, + filter: gl.LINEAR + }); + } else { + this._tex = LGraphTexture.getTargetTexture( + tex || this._tex, + this._tex, + this.properties.precision + ); + } + + var shader = this._shader; + + if (!shader) { + shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureWarp.pixel_shader + ); + } + + var factor = this.getInputData(2); + if (factor != null) { + this.properties.factor = factor; + } else { + factor = parseFloat(this.properties.factor); + } + var uniforms = this._uniforms; + uniforms.u_factor = factor; + uniforms.u_scale.set( this.properties.scale ); + uniforms.u_offset.set( this.properties.offset ); + + this._tex.drawTo(function() { + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + gl.disable(gl.BLEND); + if (tex) { + tex.bind(0); + } + if (texB) { + texB.bind(1); + } + var mesh = Mesh.getScreenQuad(); + shader + .uniforms( uniforms ) + .draw(mesh); + }); + + this.setOutputData(0, this._tex); + }; + + LGraphTextureWarp.pixel_shader = + "precision highp float;\n\ + \n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_textureB;\n\ + varying vec2 v_coord;\n\ + uniform float u_factor;\n\ + uniform vec2 u_scale;\n\ + uniform vec2 u_offset;\n\ + \n\ + void main() {\n\ + vec2 uv = v_coord;\n\ + uv += ( texture2D(u_textureB, uv).rg - vec2(0.5)) * u_factor * u_scale + u_offset;\n\ + gl_FragColor = texture2D(u_texture, uv);\n\ + }\n\ + "; + + LiteGraph.registerNodeType("texture/warp", LGraphTextureWarp); + + //**************************************************** + + // Texture to Viewport ***************************************** + function LGraphTextureToViewport() { + this.addInput("Texture", "Texture"); + this.properties = { + additive: false, + antialiasing: false, + filter: true, + disable_alpha: false, + gamma: 1.0, + viewport: [0,0,1,1] + }; + this.size[0] = 130; + } + + LGraphTextureToViewport.title = "to Viewport"; + LGraphTextureToViewport.desc = "Texture to viewport"; + + LGraphTextureToViewport._prev_viewport = new Float32Array(4); + LGraphTextureToViewport.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (this.properties.disable_alpha) { + gl.disable(gl.BLEND); + } else { + gl.enable(gl.BLEND); + if (this.properties.additive) { + gl.blendFunc(gl.SRC_ALPHA, gl.ONE); + } else { + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + } + } + + gl.disable(gl.DEPTH_TEST); + var gamma = this.properties.gamma || 1.0; + if (this.isInputConnected(1)) { + gamma = this.getInputData(1); + } + + tex.setParameter( + gl.TEXTURE_MAG_FILTER, + this.properties.filter ? gl.LINEAR : gl.NEAREST + ); + + var old_viewport = LGraphTextureToViewport._prev_viewport; + old_viewport.set( gl.viewport_data ); + var new_view = this.properties.viewport; + gl.viewport( old_viewport[0] + old_viewport[2] * new_view[0], old_viewport[1] + old_viewport[3] * new_view[1], old_viewport[2] * new_view[2], old_viewport[3] * new_view[3] ); + var viewport = gl.getViewport(); //gl.getParameter(gl.VIEWPORT); + + if (this.properties.antialiasing) { + if (!LGraphTextureToViewport._shader) { + LGraphTextureToViewport._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureToViewport.aa_pixel_shader + ); + } + + var mesh = Mesh.getScreenQuad(); + tex.bind(0); + LGraphTextureToViewport._shader + .uniforms({ + u_texture: 0, + uViewportSize: [tex.width, tex.height], + u_igamma: 1 / gamma, + inverseVP: [1 / tex.width, 1 / tex.height] + }) + .draw(mesh); + } else { + if (gamma != 1.0) { + if (!LGraphTextureToViewport._gamma_shader) { + LGraphTextureToViewport._gamma_shader = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureToViewport.gamma_pixel_shader + ); + } + tex.toViewport(LGraphTextureToViewport._gamma_shader, { + u_texture: 0, + u_igamma: 1 / gamma + }); + } else { + tex.toViewport(); + } + } + + gl.viewport( old_viewport[0], old_viewport[1], old_viewport[2], old_viewport[3] ); + }; + + LGraphTextureToViewport.prototype.onGetInputs = function() { + return [["gamma", "number"]]; + }; + + LGraphTextureToViewport.aa_pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 uViewportSize;\n\ + uniform vec2 inverseVP;\n\ + uniform float u_igamma;\n\ + #define FXAA_REDUCE_MIN (1.0/ 128.0)\n\ + #define FXAA_REDUCE_MUL (1.0 / 8.0)\n\ + #define FXAA_SPAN_MAX 8.0\n\ + \n\ + /* from mitsuhiko/webgl-meincraft based on the code on geeks3d.com */\n\ + vec4 applyFXAA(sampler2D tex, vec2 fragCoord)\n\ + {\n\ + vec4 color = vec4(0.0);\n\ + /*vec2 inverseVP = vec2(1.0 / uViewportSize.x, 1.0 / uViewportSize.y);*/\n\ + vec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * inverseVP).xyz;\n\ + vec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * inverseVP).xyz;\n\ + vec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * inverseVP).xyz;\n\ + vec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * inverseVP).xyz;\n\ + vec3 rgbM = texture2D(tex, fragCoord * inverseVP).xyz;\n\ + vec3 luma = vec3(0.299, 0.587, 0.114);\n\ + float lumaNW = dot(rgbNW, luma);\n\ + float lumaNE = dot(rgbNE, luma);\n\ + float lumaSW = dot(rgbSW, luma);\n\ + float lumaSE = dot(rgbSE, luma);\n\ + float lumaM = dot(rgbM, luma);\n\ + float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n\ + float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\ + \n\ + vec2 dir;\n\ + dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n\ + dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\ + \n\ + float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\ + \n\ + float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n\ + dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * inverseVP;\n\ + \n\ + vec3 rgbA = 0.5 * (texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + \n\ + texture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz);\n\ + vec3 rgbB = rgbA * 0.5 + 0.25 * (texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + \n\ + texture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz);\n\ + \n\ + //return vec4(rgbA,1.0);\n\ + float lumaB = dot(rgbB, luma);\n\ + if ((lumaB < lumaMin) || (lumaB > lumaMax))\n\ + color = vec4(rgbA, 1.0);\n\ + else\n\ + color = vec4(rgbB, 1.0);\n\ + if(u_igamma != 1.0)\n\ + color.xyz = pow( color.xyz, vec3(u_igamma) );\n\ + return color;\n\ + }\n\ + \n\ + void main() {\n\ + gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\ + }\n\ + "; + + LGraphTextureToViewport.gamma_pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform float u_igamma;\n\ + void main() {\n\ + vec4 color = texture2D( u_texture, v_coord);\n\ + color.xyz = pow(color.xyz, vec3(u_igamma) );\n\ + gl_FragColor = color;\n\ + }\n\ + "; + + LiteGraph.registerNodeType( + "texture/toviewport", + LGraphTextureToViewport + ); + + // Texture Copy ***************************************** + function LGraphTextureCopy() { + this.addInput("Texture", "Texture"); + this.addOutput("", "Texture"); + this.properties = { + size: 0, + generate_mipmaps: false, + precision: LGraphTexture.DEFAULT + }; + } + + LGraphTextureCopy.title = "Copy"; + LGraphTextureCopy.desc = "Copy Texture"; + LGraphTextureCopy.widgets_info = { + size: { + widget: "combo", + values: [0, 32, 64, 128, 256, 512, 1024, 2048] + }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureCopy.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex && !this._temp_texture) { + return; + } + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + //copy the texture + if (tex) { + var width = tex.width; + var height = tex.height; + + if (this.properties.size != 0) { + width = this.properties.size; + height = this.properties.size; + } + + var temp = this._temp_texture; + + var type = tex.type; + if (this.properties.precision === LGraphTexture.LOW) { + type = gl.UNSIGNED_BYTE; + } else if (this.properties.precision === LGraphTexture.HIGH) { + type = gl.HIGH_PRECISION_FORMAT; + } + + if ( + !temp || + temp.width != width || + temp.height != height || + temp.type != type + ) { + var minFilter = gl.LINEAR; + if ( + this.properties.generate_mipmaps && + isPowerOfTwo(width) && + isPowerOfTwo(height) + ) { + minFilter = gl.LINEAR_MIPMAP_LINEAR; + } + this._temp_texture = new GL.Texture(width, height, { + type: type, + format: gl.RGBA, + minFilter: minFilter, + magFilter: gl.LINEAR + }); + } + tex.copyTo(this._temp_texture); + + if (this.properties.generate_mipmaps) { + this._temp_texture.bind(0); + gl.generateMipmap(this._temp_texture.texture_type); + this._temp_texture.unbind(0); + } + } + + this.setOutputData(0, this._temp_texture); + }; + + LiteGraph.registerNodeType("texture/copy", LGraphTextureCopy); + + // Texture Downsample ***************************************** + function LGraphTextureDownsample() { + this.addInput("Texture", "Texture"); + this.addOutput("", "Texture"); + this.properties = { + iterations: 1, + generate_mipmaps: false, + precision: LGraphTexture.DEFAULT + }; + } + + LGraphTextureDownsample.title = "Downsample"; + LGraphTextureDownsample.desc = "Downsample Texture"; + LGraphTextureDownsample.widgets_info = { + iterations: { type: "number", step: 1, precision: 0, min: 0 }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureDownsample.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex && !this._temp_texture) { + return; + } + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + //we do not allow any texture different than texture 2D + if (!tex || tex.texture_type !== GL.TEXTURE_2D) { + return; + } + + if (this.properties.iterations < 1) { + this.setOutputData(0, tex); + return; + } + + var shader = LGraphTextureDownsample._shader; + if (!shader) { + LGraphTextureDownsample._shader = shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureDownsample.pixel_shader + ); + } + + var width = tex.width | 0; + var height = tex.height | 0; + var type = tex.type; + if (this.properties.precision === LGraphTexture.LOW) { + type = gl.UNSIGNED_BYTE; + } else if (this.properties.precision === LGraphTexture.HIGH) { + type = gl.HIGH_PRECISION_FORMAT; + } + var iterations = this.properties.iterations || 1; + + var origin = tex; + var target = null; + + var temp = []; + var options = { + type: type, + format: tex.format + }; + + var offset = vec2.create(); + var uniforms = { + u_offset: offset + }; + + if (this._texture) { + GL.Texture.releaseTemporary(this._texture); + } + + for (var i = 0; i < iterations; ++i) { + offset[0] = 1 / width; + offset[1] = 1 / height; + width = width >> 1 || 0; + height = height >> 1 || 0; + target = GL.Texture.getTemporary(width, height, options); + temp.push(target); + origin.setParameter(GL.TEXTURE_MAG_FILTER, GL.NEAREST); + origin.copyTo(target, shader, uniforms); + if (width == 1 && height == 1) { + break; + } //nothing else to do + origin = target; + } + + //keep the last texture used + this._texture = temp.pop(); + + //free the rest + for (var i = 0; i < temp.length; ++i) { + GL.Texture.releaseTemporary(temp[i]); + } + + if (this.properties.generate_mipmaps) { + this._texture.bind(0); + gl.generateMipmap(this._texture.texture_type); + this._texture.unbind(0); + } + + this.setOutputData(0, this._texture); + }; + + LGraphTextureDownsample.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 u_offset;\n\ + varying vec2 v_coord;\n\ + \n\ + void main() {\n\ + vec4 color = texture2D(u_texture, v_coord );\n\ + color += texture2D(u_texture, v_coord + vec2( u_offset.x, 0.0 ) );\n\ + color += texture2D(u_texture, v_coord + vec2( 0.0, u_offset.y ) );\n\ + color += texture2D(u_texture, v_coord + vec2( u_offset.x, u_offset.y ) );\n\ + gl_FragColor = color * 0.25;\n\ + }\n\ + "; + + LiteGraph.registerNodeType( + "texture/downsample", + LGraphTextureDownsample + ); + + // Texture Average ***************************************** + function LGraphTextureAverage() { + this.addInput("Texture", "Texture"); + this.addOutput("tex", "Texture"); + this.addOutput("avg", "vec4"); + this.addOutput("lum", "number"); + this.properties = { + use_previous_frame: true, //to avoid stalls + high_quality: false //to use as much pixels as possible + }; + + this._uniforms = { + u_texture: 0, + u_mipmap_offset: 0 + }; + this._luminance = new Float32Array(4); + } + + LGraphTextureAverage.title = "Average"; + LGraphTextureAverage.desc = + "Compute a partial average (32 random samples) of a texture and stores it as a 1x1 pixel texture.\n If high_quality is true, then it generates the mipmaps first and reads from the lower one."; + + LGraphTextureAverage.prototype.onExecute = function() { + if (!this.properties.use_previous_frame) { + this.updateAverage(); + } + + var v = this._luminance; + this.setOutputData(0, this._temp_texture); + this.setOutputData(1, v); + this.setOutputData(2, (v[0] + v[1] + v[2]) / 3); + }; + + //executed before rendering the frame + LGraphTextureAverage.prototype.onPreRenderExecute = function() { + this.updateAverage(); + }; + + LGraphTextureAverage.prototype.updateAverage = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if ( + !this.isOutputConnected(0) && + !this.isOutputConnected(1) && + !this.isOutputConnected(2) + ) { + return; + } //saves work + + if (!LGraphTextureAverage._shader) { + LGraphTextureAverage._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureAverage.pixel_shader + ); + //creates 256 random numbers and stores them in two mat4 + var samples = new Float32Array(16); + for (var i = 0; i < samples.length; ++i) { + samples[i] = Math.random(); //poorly distributed samples + } + //upload only once + LGraphTextureAverage._shader.uniforms({ + u_samples_a: samples.subarray(0, 16), + u_samples_b: samples.subarray(16, 32) + }); + } + + var temp = this._temp_texture; + var type = gl.UNSIGNED_BYTE; + if (tex.type != type) { + //force floats, half floats cannot be read with gl.readPixels + type = gl.FLOAT; + } + + if (!temp || temp.type != type) { + this._temp_texture = new GL.Texture(1, 1, { + type: type, + format: gl.RGBA, + filter: gl.NEAREST + }); + } + + this._uniforms.u_mipmap_offset = 0; + + if(this.properties.high_quality) + { + if( !this._temp_pot2_texture || this._temp_pot2_texture.type != type ) + this._temp_pot2_texture = new GL.Texture(512, 512, { + type: type, + format: gl.RGBA, + minFilter: gl.LINEAR_MIPMAP_LINEAR, + magFilter: gl.LINEAR + }); + + tex.copyTo( this._temp_pot2_texture ); + tex = this._temp_pot2_texture; + tex.bind(0); + gl.generateMipmap(GL.TEXTURE_2D); + this._uniforms.u_mipmap_offset = 9; + } + + var shader = LGraphTextureAverage._shader; + var uniforms = this._uniforms; + uniforms.u_mipmap_offset = this.properties.mipmap_offset; + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.BLEND); + this._temp_texture.drawTo(function() { + tex.toViewport(shader, uniforms); + }); + + if (this.isOutputConnected(1) || this.isOutputConnected(2)) { + var pixel = this._temp_texture.getPixels(); + if (pixel) { + var v = this._luminance; + var type = this._temp_texture.type; + v.set(pixel); + if (type == gl.UNSIGNED_BYTE) { + vec4.scale(v, v, 1 / 255); + } else if ( + type == GL.HALF_FLOAT || + type == GL.HALF_FLOAT_OES + ) { + //no half floats possible, hard to read back unless copyed to a FLOAT texture, so temp_texture is always forced to FLOAT + } + } + } + }; + + LGraphTextureAverage.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + uniform mat4 u_samples_a;\n\ + uniform mat4 u_samples_b;\n\ + uniform sampler2D u_texture;\n\ + uniform float u_mipmap_offset;\n\ + varying vec2 v_coord;\n\ + \n\ + void main() {\n\ + vec4 color = vec4(0.0);\n\ + //random average\n\ + for(int i = 0; i < 4; ++i)\n\ + for(int j = 0; j < 4; ++j)\n\ + {\n\ + color += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\ + color += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\ + }\n\ + gl_FragColor = color * 0.03125;\n\ + }\n\ + "; + + LiteGraph.registerNodeType("texture/average", LGraphTextureAverage); + + + + // Computes operation between pixels (max, min) ***************************************** + function LGraphTextureMinMax() { + this.addInput("Texture", "Texture"); + this.addOutput("min_t", "Texture"); + this.addOutput("max_t", "Texture"); + this.addOutput("min", "vec4"); + this.addOutput("max", "vec4"); + this.properties = { + mode: "max", + use_previous_frame: true //to avoid stalls + }; + + this._uniforms = { + u_texture: 0 + }; + + this._max = new Float32Array(4); + this._min = new Float32Array(4); + + this._textures_chain = []; + } + + LGraphTextureMinMax.widgets_info = { + mode: { widget: "combo", values: ["min","max","avg"] } + }; + + LGraphTextureMinMax.title = "MinMax"; + LGraphTextureMinMax.desc = "Compute the scene min max"; + + LGraphTextureMinMax.prototype.onExecute = function() { + if (!this.properties.use_previous_frame) { + this.update(); + } + + this.setOutputData(0, this._temp_texture); + this.setOutputData(1, this._luminance); + }; + + //executed before rendering the frame + LGraphTextureMinMax.prototype.onPreRenderExecute = function() { + this.update(); + }; + + LGraphTextureMinMax.prototype.update = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if ( !this.isOutputConnected(0) && !this.isOutputConnected(1) ) { + return; + } //saves work + + if (!LGraphTextureMinMax._shader) { + LGraphTextureMinMax._shader = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureMinMax.pixel_shader ); + } + + var temp = this._temp_texture; + var type = gl.UNSIGNED_BYTE; + if (tex.type != type) { + //force floats, half floats cannot be read with gl.readPixels + type = gl.FLOAT; + } + + var size = 512; + + if( !this._textures_chain.length || this._textures_chain[0].type != type ) + { + var index = 0; + while(i) + { + this._textures_chain[i] = new GL.Texture( size, size, { + type: type, + format: gl.RGBA, + filter: gl.NEAREST + }); + size = size >> 2; + i++; + if(size == 1) + break; + } + } + + tex.copyTo( this._textures_chain[0] ); + var prev = this._textures_chain[0]; + for(var i = 1; i <= this._textures_chain.length; ++i) + { + var tex = this._textures_chain[i]; + + prev = tex; + } + + var shader = LGraphTextureMinMax._shader; + var uniforms = this._uniforms; + uniforms.u_mipmap_offset = this.properties.mipmap_offset; + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.BLEND); + this._temp_texture.drawTo(function() { + tex.toViewport(shader, uniforms); + }); + }; + + LGraphTextureMinMax.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + uniform mat4 u_samples_a;\n\ + uniform mat4 u_samples_b;\n\ + uniform sampler2D u_texture;\n\ + uniform float u_mipmap_offset;\n\ + varying vec2 v_coord;\n\ + \n\ + void main() {\n\ + vec4 color = vec4(0.0);\n\ + //random average\n\ + for(int i = 0; i < 4; ++i)\n\ + for(int j = 0; j < 4; ++j)\n\ + {\n\ + color += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\ + color += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\ + }\n\ + gl_FragColor = color * 0.03125;\n\ + }\n\ + "; + + //LiteGraph.registerNodeType("texture/clustered_operation", LGraphTextureClusteredOperation); + + + function LGraphTextureTemporalSmooth() { + this.addInput("in", "Texture"); + this.addInput("factor", "Number"); + this.addOutput("out", "Texture"); + this.properties = { factor: 0.5 }; + this._uniforms = { + u_texture: 0, + u_textureB: 1, + u_factor: this.properties.factor + }; + } + + LGraphTextureTemporalSmooth.title = "Smooth"; + LGraphTextureTemporalSmooth.desc = "Smooth texture over time"; + + LGraphTextureTemporalSmooth.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex || !this.isOutputConnected(0)) { + return; + } + + if (!LGraphTextureTemporalSmooth._shader) { + LGraphTextureTemporalSmooth._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureTemporalSmooth.pixel_shader + ); + } + + var temp = this._temp_texture; + if ( + !temp || + temp.type != tex.type || + temp.width != tex.width || + temp.height != tex.height + ) { + var options = { + type: tex.type, + format: gl.RGBA, + filter: gl.NEAREST + }; + this._temp_texture = new GL.Texture(tex.width, tex.height, options ); + this._temp_texture2 = new GL.Texture(tex.width, tex.height, options ); + tex.copyTo(this._temp_texture2); + } + + var tempA = this._temp_texture; + var tempB = this._temp_texture2; + + var shader = LGraphTextureTemporalSmooth._shader; + var uniforms = this._uniforms; + uniforms.u_factor = 1.0 - this.getInputOrProperty("factor"); + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + tempA.drawTo(function() { + tempB.bind(1); + tex.toViewport(shader, uniforms); + }); + + this.setOutputData(0, tempA); + + //swap + this._temp_texture = tempB; + this._temp_texture2 = tempA; + }; + + LGraphTextureTemporalSmooth.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_textureB;\n\ + uniform float u_factor;\n\ + varying vec2 v_coord;\n\ + \n\ + void main() {\n\ + gl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\ + }\n\ + "; + + LiteGraph.registerNodeType( "texture/temporal_smooth", LGraphTextureTemporalSmooth ); + + + function LGraphTextureLinearAvgSmooth() { + this.addInput("in", "Texture"); + this.addOutput("avg", "Texture"); + this.addOutput("array", "Texture"); + this.properties = { samples: 64, frames_interval: 1 }; + this._uniforms = { + u_texture: 0, + u_textureB: 1, + u_samples: this.properties.samples, + u_isamples: 1/this.properties.samples + }; + this.frame = 0; + } + + LGraphTextureLinearAvgSmooth.title = "Lineal Avg Smooth"; + LGraphTextureLinearAvgSmooth.desc = "Smooth texture linearly over time"; + + LGraphTextureLinearAvgSmooth["@samples"] = { type: "number", min: 1, max: 64, step: 1, precision: 1 }; + + LGraphTextureLinearAvgSmooth.prototype.getPreviewTexture = function() + { + return this._temp_texture2; + } + + LGraphTextureLinearAvgSmooth.prototype.onExecute = function() { + + var tex = this.getInputData(0); + if (!tex || !this.isOutputConnected(0)) { + return; + } + + if (!LGraphTextureLinearAvgSmooth._shader) { + LGraphTextureLinearAvgSmooth._shader_copy = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureLinearAvgSmooth.pixel_shader_copy ); + LGraphTextureLinearAvgSmooth._shader_avg = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureLinearAvgSmooth.pixel_shader_avg ); + } + + var samples = Math.clamp(this.properties.samples,0,64); + var frame = this.frame; + var interval = this.properties.frames_interval; + + if( interval == 0 || frame % interval == 0 ) + { + var temp = this._temp_texture; + if ( !temp || temp.type != tex.type || temp.width != samples ) { + var options = { + type: tex.type, + format: gl.RGBA, + filter: gl.NEAREST + }; + this._temp_texture = new GL.Texture( samples, 1, options ); + this._temp_texture2 = new GL.Texture( samples, 1, options ); + this._temp_texture_out = new GL.Texture( 1, 1, options ); + } + + var tempA = this._temp_texture; + var tempB = this._temp_texture2; + + var shader_copy = LGraphTextureLinearAvgSmooth._shader_copy; + var shader_avg = LGraphTextureLinearAvgSmooth._shader_avg; + var uniforms = this._uniforms; + uniforms.u_samples = samples; + uniforms.u_isamples = 1.0 / samples; + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + tempA.drawTo(function() { + tempB.bind(1); + tex.toViewport( shader_copy, uniforms ); + }); + + this._temp_texture_out.drawTo(function() { + tempA.toViewport( shader_avg, uniforms ); + }); + + this.setOutputData( 0, this._temp_texture_out ); + + //swap + this._temp_texture = tempB; + this._temp_texture2 = tempA; + } + else + this.setOutputData(0, this._temp_texture_out); + this.setOutputData(1, this._temp_texture2); + this.frame++; + }; + + LGraphTextureLinearAvgSmooth.pixel_shader_copy = + "precision highp float;\n\ + precision highp float;\n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_textureB;\n\ + uniform float u_isamples;\n\ + varying vec2 v_coord;\n\ + \n\ + void main() {\n\ + if( v_coord.x <= u_isamples )\n\ + gl_FragColor = texture2D( u_texture, vec2(0.5) );\n\ + else\n\ + gl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\ + }\n\ + "; + + LGraphTextureLinearAvgSmooth.pixel_shader_avg = + "precision highp float;\n\ + precision highp float;\n\ + uniform sampler2D u_texture;\n\ + uniform int u_samples;\n\ + uniform float u_isamples;\n\ + varying vec2 v_coord;\n\ + \n\ + void main() {\n\ + vec4 color = vec4(0.0);\n\ + for(int i = 0; i < 64; ++i)\n\ + {\n\ + color += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\ + if(i == (u_samples - 1))\n\ + break;\n\ + }\n\ + gl_FragColor = color * u_isamples;\n\ + }\n\ + "; + + + LiteGraph.registerNodeType( "texture/linear_avg_smooth", LGraphTextureLinearAvgSmooth ); + + // Image To Texture ***************************************** + function LGraphImageToTexture() { + this.addInput("Image", "image"); + this.addOutput("", "Texture"); + this.properties = {}; + } + + LGraphImageToTexture.title = "Image to Texture"; + LGraphImageToTexture.desc = "Uploads an image to the GPU"; + //LGraphImageToTexture.widgets_info = { size: { widget:"combo", values:[0,32,64,128,256,512,1024,2048]} }; + + LGraphImageToTexture.prototype.onExecute = function() { + var img = this.getInputData(0); + if (!img) { + return; + } + + var width = img.videoWidth || img.width; + var height = img.videoHeight || img.height; + + //this is in case we are using a webgl canvas already, no need to reupload it + if (img.gltexture) { + this.setOutputData(0, img.gltexture); + return; + } + + var temp = this._temp_texture; + if (!temp || temp.width != width || temp.height != height) { + this._temp_texture = new GL.Texture(width, height, { + format: gl.RGBA, + filter: gl.LINEAR + }); + } + + try { + this._temp_texture.uploadImage(img); + } catch (err) { + console.error( + "image comes from an unsafe location, cannot be uploaded to webgl: " + + err + ); + return; + } + + this.setOutputData(0, this._temp_texture); + }; + + LiteGraph.registerNodeType( + "texture/imageToTexture", + LGraphImageToTexture + ); + + // Texture LUT ***************************************** + function LGraphTextureLUT() { + this.addInput("Texture", "Texture"); + this.addInput("LUT", "Texture"); + this.addInput("Intensity", "number"); + this.addOutput("", "Texture"); + this.properties = { enabled: true, intensity: 1, precision: LGraphTexture.DEFAULT, texture: null }; + + if (!LGraphTextureLUT._shader) { + LGraphTextureLUT._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, LGraphTextureLUT.pixel_shader ); + } + } + + LGraphTextureLUT.widgets_info = { + texture: { widget: "texture" }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureLUT.title = "LUT"; + LGraphTextureLUT.desc = "Apply LUT to Texture"; + + LGraphTextureLUT.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { + return; + } //saves work + + var tex = this.getInputData(0); + + if (this.properties.precision === LGraphTexture.PASS_THROUGH || this.properties.enabled === false) { + this.setOutputData(0, tex); + return; + } + + if (!tex) { + return; + } + + var lut_tex = this.getInputData(1); + + if (!lut_tex) { + lut_tex = LGraphTexture.getTexture(this.properties.texture); + } + + if (!lut_tex) { + this.setOutputData(0, tex); + return; + } + + lut_tex.bind(0); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri( + gl.TEXTURE_2D, + gl.TEXTURE_WRAP_S, + gl.CLAMP_TO_EDGE + ); + gl.texParameteri( + gl.TEXTURE_2D, + gl.TEXTURE_WRAP_T, + gl.CLAMP_TO_EDGE + ); + gl.bindTexture(gl.TEXTURE_2D, null); + + var intensity = this.properties.intensity; + if (this.isInputConnected(2)) { + this.properties.intensity = intensity = this.getInputData(2); + } + + this._tex = LGraphTexture.getTargetTexture( + tex, + this._tex, + this.properties.precision + ); + + //var mesh = Mesh.getScreenQuad(); + + this._tex.drawTo(function() { + lut_tex.bind(1); + tex.toViewport(LGraphTextureLUT._shader, { + u_texture: 0, + u_textureB: 1, + u_amount: intensity + }); + }); + + this.setOutputData(0, this._tex); + }; + + LGraphTextureLUT.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_textureB;\n\ + uniform float u_amount;\n\ + \n\ + void main() {\n\ + lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\ + mediump float blueColor = textureColor.b * 63.0;\n\ + mediump vec2 quad1;\n\ + quad1.y = floor(floor(blueColor) / 8.0);\n\ + quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\ + mediump vec2 quad2;\n\ + quad2.y = floor(ceil(blueColor) / 8.0);\n\ + quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\ + highp vec2 texPos1;\n\ + texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\ + texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\ + highp vec2 texPos2;\n\ + texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\ + texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\ + lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\ + lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\ + lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\ + gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\ + }\n\ + "; + + LiteGraph.registerNodeType("texture/LUT", LGraphTextureLUT); + + // Texture Channels ***************************************** + function LGraphTextureChannels() { + this.addInput("Texture", "Texture"); + + this.addOutput("R", "Texture"); + this.addOutput("G", "Texture"); + this.addOutput("B", "Texture"); + this.addOutput("A", "Texture"); + + //this.properties = { use_single_channel: true }; + if (!LGraphTextureChannels._shader) { + LGraphTextureChannels._shader = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureChannels.pixel_shader + ); + } + } + + LGraphTextureChannels.title = "Texture to Channels"; + LGraphTextureChannels.desc = "Split texture channels"; + + LGraphTextureChannels.prototype.onExecute = function() { + var texA = this.getInputData(0); + if (!texA) { + return; + } + + if (!this._channels) { + this._channels = Array(4); + } + + //var format = this.properties.use_single_channel ? gl.LUMINANCE : gl.RGBA; //not supported by WebGL1 + var format = gl.RGB; + var connections = 0; + for (var i = 0; i < 4; i++) { + if (this.isOutputConnected(i)) { + if ( + !this._channels[i] || + this._channels[i].width != texA.width || + this._channels[i].height != texA.height || + this._channels[i].type != texA.type || + this._channels[i].format != format + ) { + this._channels[i] = new GL.Texture( + texA.width, + texA.height, + { + type: texA.type, + format: format, + filter: gl.LINEAR + } + ); + } + connections++; + } else { + this._channels[i] = null; + } + } + + if (!connections) { + return; + } + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + + var mesh = Mesh.getScreenQuad(); + var shader = LGraphTextureChannels._shader; + var masks = [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1] + ]; + + for (var i = 0; i < 4; i++) { + if (!this._channels[i]) { + continue; + } + + this._channels[i].drawTo(function() { + texA.bind(0); + shader + .uniforms({ u_texture: 0, u_mask: masks[i] }) + .draw(mesh); + }); + this.setOutputData(i, this._channels[i]); + } + }; + + LGraphTextureChannels.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec4 u_mask;\n\ + \n\ + void main() {\n\ + gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\ + }\n\ + "; + + LiteGraph.registerNodeType( + "texture/textureChannels", + LGraphTextureChannels + ); + + // Texture Channels to Texture ***************************************** + function LGraphChannelsTexture() { + this.addInput("R", "Texture"); + this.addInput("G", "Texture"); + this.addInput("B", "Texture"); + this.addInput("A", "Texture"); + + this.addOutput("Texture", "Texture"); + + this.properties = { + precision: LGraphTexture.DEFAULT, + R: 1, + G: 1, + B: 1, + A: 1 + }; + this._color = vec4.create(); + this._uniforms = { + u_textureR: 0, + u_textureG: 1, + u_textureB: 2, + u_textureA: 3, + u_color: this._color + }; + } + + LGraphChannelsTexture.title = "Channels to Texture"; + LGraphChannelsTexture.desc = "Split texture channels"; + LGraphChannelsTexture.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphChannelsTexture.prototype.onExecute = function() { + var white = LGraphTexture.getWhiteTexture(); + var texR = this.getInputData(0) || white; + var texG = this.getInputData(1) || white; + var texB = this.getInputData(2) || white; + var texA = this.getInputData(3) || white; + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + + var mesh = Mesh.getScreenQuad(); + if (!LGraphChannelsTexture._shader) { + LGraphChannelsTexture._shader = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphChannelsTexture.pixel_shader + ); + } + var shader = LGraphChannelsTexture._shader; + + var w = Math.max(texR.width, texG.width, texB.width, texA.width); + var h = Math.max( + texR.height, + texG.height, + texB.height, + texA.height + ); + var type = + this.properties.precision == LGraphTexture.HIGH + ? LGraphTexture.HIGH_PRECISION_FORMAT + : gl.UNSIGNED_BYTE; + + if ( + !this._texture || + this._texture.width != w || + this._texture.height != h || + this._texture.type != type + ) { + this._texture = new GL.Texture(w, h, { + type: type, + format: gl.RGBA, + filter: gl.LINEAR + }); + } + + var color = this._color; + color[0] = this.properties.R; + color[1] = this.properties.G; + color[2] = this.properties.B; + color[3] = this.properties.A; + var uniforms = this._uniforms; + + this._texture.drawTo(function() { + texR.bind(0); + texG.bind(1); + texB.bind(2); + texA.bind(3); + shader.uniforms(uniforms).draw(mesh); + }); + this.setOutputData(0, this._texture); + }; + + LGraphChannelsTexture.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_textureR;\n\ + uniform sampler2D u_textureG;\n\ + uniform sampler2D u_textureB;\n\ + uniform sampler2D u_textureA;\n\ + uniform vec4 u_color;\n\ + \n\ + void main() {\n\ + gl_FragColor = u_color * vec4( \ + texture2D(u_textureR, v_coord).r,\ + texture2D(u_textureG, v_coord).r,\ + texture2D(u_textureB, v_coord).r,\ + texture2D(u_textureA, v_coord).r);\n\ + }\n\ + "; + + LiteGraph.registerNodeType( + "texture/channelsTexture", + LGraphChannelsTexture + ); + + // Texture Color ***************************************** + function LGraphTextureColor() { + this.addOutput("Texture", "Texture"); + + this._tex_color = vec4.create(); + this.properties = { + color: vec4.create(), + precision: LGraphTexture.DEFAULT + }; + } + + LGraphTextureColor.title = "Color"; + LGraphTextureColor.desc = + "Generates a 1x1 texture with a constant color"; + + LGraphTextureColor.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureColor.prototype.onDrawBackground = function(ctx) { + var c = this.properties.color; + ctx.fillStyle = + "rgb(" + + Math.floor(Math.clamp(c[0], 0, 1) * 255) + + "," + + Math.floor(Math.clamp(c[1], 0, 1) * 255) + + "," + + Math.floor(Math.clamp(c[2], 0, 1) * 255) + + ")"; + if (this.flags.collapsed) { + this.boxcolor = ctx.fillStyle; + } else { + ctx.fillRect(0, 0, this.size[0], this.size[1]); + } + }; + + LGraphTextureColor.prototype.onExecute = function() { + var type = + this.properties.precision == LGraphTexture.HIGH + ? LGraphTexture.HIGH_PRECISION_FORMAT + : gl.UNSIGNED_BYTE; + + if (!this._tex || this._tex.type != type) { + this._tex = new GL.Texture(1, 1, { + format: gl.RGBA, + type: type, + minFilter: gl.NEAREST + }); + } + var color = this.properties.color; + + if (this.inputs) { + for (var i = 0; i < this.inputs.length; i++) { + var input = this.inputs[i]; + var v = this.getInputData(i); + if (v === undefined) { + continue; + } + switch (input.name) { + case "RGB": + case "RGBA": + color.set(v); + break; + case "R": + color[0] = v; + break; + case "G": + color[1] = v; + break; + case "B": + color[2] = v; + break; + case "A": + color[3] = v; + break; + } + } + } + + if (vec4.sqrDist(this._tex_color, color) > 0.001) { + this._tex_color.set(color); + this._tex.fill(color); + } + this.setOutputData(0, this._tex); + }; + + LGraphTextureColor.prototype.onGetInputs = function() { + return [ + ["RGB", "vec3"], + ["RGBA", "vec4"], + ["R", "number"], + ["G", "number"], + ["B", "number"], + ["A", "number"] + ]; + }; + + LiteGraph.registerNodeType("texture/color", LGraphTextureColor); + + // Texture Channels to Texture ***************************************** + function LGraphTextureGradient() { + this.addInput("A", "color"); + this.addInput("B", "color"); + this.addOutput("Texture", "Texture"); + + this.properties = { + angle: 0, + scale: 1, + A: [0, 0, 0], + B: [1, 1, 1], + texture_size: 32 + }; + if (!LGraphTextureGradient._shader) { + LGraphTextureGradient._shader = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureGradient.pixel_shader + ); + } + + this._uniforms = { + u_angle: 0, + u_colorA: vec3.create(), + u_colorB: vec3.create() + }; + } + + LGraphTextureGradient.title = "Gradient"; + LGraphTextureGradient.desc = "Generates a gradient"; + LGraphTextureGradient["@A"] = { type: "color" }; + LGraphTextureGradient["@B"] = { type: "color" }; + LGraphTextureGradient["@texture_size"] = { + type: "enum", + values: [32, 64, 128, 256, 512] + }; + + LGraphTextureGradient.prototype.onExecute = function() { + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + + var mesh = GL.Mesh.getScreenQuad(); + var shader = LGraphTextureGradient._shader; + + var A = this.getInputData(0); + if (!A) { + A = this.properties.A; + } + var B = this.getInputData(1); + if (!B) { + B = this.properties.B; + } + + //angle and scale + for (var i = 2; i < this.inputs.length; i++) { + var input = this.inputs[i]; + var v = this.getInputData(i); + if (v === undefined) { + continue; + } + this.properties[input.name] = v; + } + +<<<<<<< HEAD })(this); (function(global) { @@ -21792,1914 +36124,2544 @@ void main(void){\n\ (function(global) { var LiteGraph = global.LiteGraph; +======= + var uniforms = this._uniforms; + this._uniforms.u_angle = this.properties.angle * DEG2RAD; + this._uniforms.u_scale = this.properties.scale; + vec3.copy(uniforms.u_colorA, A); + vec3.copy(uniforms.u_colorB, B); - var view_matrix = new Float32Array(16); - var projection_matrix = new Float32Array(16); - var viewprojection_matrix = new Float32Array(16); - var model_matrix = new Float32Array(16); - var global_uniforms = { - u_view: view_matrix, - u_projection: projection_matrix, - u_viewprojection: viewprojection_matrix, - u_model: model_matrix + var size = parseInt(this.properties.texture_size); + if (!this._tex || this._tex.width != size) { + this._tex = new GL.Texture(size, size, { + format: gl.RGB, + filter: gl.LINEAR + }); + } +>>>>>>> custom widget custom size support + + this._tex.drawTo(function() { + shader.uniforms(uniforms).draw(mesh); + }); + this.setOutputData(0, this._tex); }; - LiteGraph.LGraphRender = { - onRequestCameraMatrices: null //overwrite with your 3D engine specifics, it will receive (view_matrix, projection_matrix,viewprojection_matrix) and must be filled + LGraphTextureGradient.prototype.onGetInputs = function() { + return [["angle", "number"], ["scale", "number"]]; }; - function generateGeometryId() { - return (Math.random() * 100000)|0; + LGraphTextureGradient.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform float u_angle;\n\ + uniform float u_scale;\n\ + uniform vec3 u_colorA;\n\ + uniform vec3 u_colorB;\n\ + \n\ + vec2 rotate(vec2 v, float angle)\n\ + {\n\ + vec2 result;\n\ + float _cos = cos(angle);\n\ + float _sin = sin(angle);\n\ + result.x = v.x * _cos - v.y * _sin;\n\ + result.y = v.x * _sin + v.y * _cos;\n\ + return result;\n\ + }\n\ + void main() {\n\ + float f = (rotate(u_scale * (v_coord - vec2(0.5)), u_angle) + vec2(0.5)).x;\n\ + vec3 color = mix(u_colorA,u_colorB,clamp(f,0.0,1.0));\n\ + gl_FragColor = vec4(color,1.0);\n\ + }\n\ + "; + + LiteGraph.registerNodeType("texture/gradient", LGraphTextureGradient); + + // Texture Mix ***************************************** + function LGraphTextureMix() { + this.addInput("A", "Texture"); + this.addInput("B", "Texture"); + this.addInput("Mixer", "Texture"); + + this.addOutput("Texture", "Texture"); + this.properties = { factor: 0.5, size_from_biggest: true, invert: false, precision: LGraphTexture.DEFAULT }; + this._uniforms = { + u_textureA: 0, + u_textureB: 1, + u_textureMix: 2, + u_mix: vec4.create() + }; } - function LGraphPoints3D() { + LGraphTextureMix.title = "Mix"; + LGraphTextureMix.desc = "Generates a texture mixing two textures"; - this.addInput("obj", ""); - this.addInput("radius", "number"); + LGraphTextureMix.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; - this.addOutput("out", "geometry"); - this.addOutput("points", "[vec3]"); + LGraphTextureMix.prototype.onExecute = function() { + var texA = this.getInputData(0); + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, texA); + return; + } + + var texB = this.getInputData(1); + if (!texA || !texB) { + return; + } + + var texMix = this.getInputData(2); + + var factor = this.getInputData(3); + + this._tex = LGraphTexture.getTargetTexture( + this.properties.size_from_biggest && texB.width > texA.width ? texB : texA, + this._tex, + this.properties.precision + ); + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + + var mesh = Mesh.getScreenQuad(); + var shader = null; + var uniforms = this._uniforms; + if (texMix) { + shader = LGraphTextureMix._shader_tex; + if (!shader) { + shader = LGraphTextureMix._shader_tex = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureMix.pixel_shader, + { MIX_TEX: "" } + ); + } + } else { + shader = LGraphTextureMix._shader_factor; + if (!shader) { + shader = LGraphTextureMix._shader_factor = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureMix.pixel_shader + ); + } + var f = factor == null ? this.properties.factor : factor; + uniforms.u_mix.set([f, f, f, f]); + } + + var invert = this.properties.invert; + + this._tex.drawTo(function() { + texA.bind( invert ? 1 : 0 ); + texB.bind( invert ? 0 : 1 ); + if (texMix) { + texMix.bind(2); + } + shader.uniforms(uniforms).draw(mesh); + }); + + this.setOutputData(0, this._tex); + }; + + LGraphTextureMix.prototype.onGetInputs = function() { + return [["factor", "number"]]; + }; + + LGraphTextureMix.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_textureA;\n\ + uniform sampler2D u_textureB;\n\ + #ifdef MIX_TEX\n\ + uniform sampler2D u_textureMix;\n\ + #else\n\ + uniform vec4 u_mix;\n\ + #endif\n\ + \n\ + void main() {\n\ + #ifdef MIX_TEX\n\ + vec4 f = texture2D(u_textureMix, v_coord);\n\ + #else\n\ + vec4 f = u_mix;\n\ + #endif\n\ + gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\ + }\n\ + "; + + LiteGraph.registerNodeType("texture/mix", LGraphTextureMix); + + // Texture Edges detection ***************************************** + function LGraphTextureEdges() { + this.addInput("Tex.", "Texture"); + + this.addOutput("Edges", "Texture"); this.properties = { - radius: 1, - num_points: 4096, - generate_normals: true, - regular: false, - mode: LGraphPoints3D.SPHERE, - force_update: false + invert: true, + threshold: false, + factor: 1, + precision: LGraphTexture.DEFAULT }; - this.points = new Float32Array( this.properties.num_points * 3 ); - this.normals = new Float32Array( this.properties.num_points * 3 ); - this.must_update = true; + if (!LGraphTextureEdges._shader) { + LGraphTextureEdges._shader = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureEdges.pixel_shader + ); + } + } + + LGraphTextureEdges.title = "Edges"; + LGraphTextureEdges.desc = "Detects edges"; + + LGraphTextureEdges.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureEdges.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { + return; + } //saves work + + var tex = this.getInputData(0); + + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, tex); + return; + } + + if (!tex) { + return; + } + + this._tex = LGraphTexture.getTargetTexture( + tex, + this._tex, + this.properties.precision + ); + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + + var mesh = Mesh.getScreenQuad(); + var shader = LGraphTextureEdges._shader; + var invert = this.properties.invert; + var factor = this.properties.factor; + var threshold = this.properties.threshold ? 1 : 0; + + this._tex.drawTo(function() { + tex.bind(0); + shader + .uniforms({ + u_texture: 0, + u_isize: [1 / tex.width, 1 / tex.height], + u_factor: factor, + u_threshold: threshold, + u_invert: invert ? 1 : 0 + }) + .draw(mesh); + }); + + this.setOutputData(0, this._tex); + }; + + LGraphTextureEdges.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 u_isize;\n\ + uniform int u_invert;\n\ + uniform float u_factor;\n\ + uniform float u_threshold;\n\ + \n\ + void main() {\n\ + vec4 center = texture2D(u_texture, v_coord);\n\ + vec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\ + vec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\ + vec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\ + vec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\ + vec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\ + diff *= u_factor;\n\ + if(u_invert == 1)\n\ + diff.xyz = vec3(1.0) - diff.xyz;\n\ + if( u_threshold == 0.0 )\n\ + gl_FragColor = vec4( diff.xyz, center.a );\n\ + else\n\ + gl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\ + }\n\ + "; + + LiteGraph.registerNodeType("texture/edges", LGraphTextureEdges); + + // Texture Depth ***************************************** + function LGraphTextureDepthRange() { + this.addInput("Texture", "Texture"); + this.addInput("Distance", "number"); + this.addInput("Range", "number"); + this.addOutput("Texture", "Texture"); + this.properties = { + distance: 100, + range: 50, + only_depth: false, + high_precision: false + }; + this._uniforms = { + u_texture: 0, + u_distance: 100, + u_range: 50, + u_camera_planes: null + }; + } + + LGraphTextureDepthRange.title = "Depth Range"; + LGraphTextureDepthRange.desc = "Generates a texture with a depth range"; + + LGraphTextureDepthRange.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { + return; + } //saves work + + var tex = this.getInputData(0); + if (!tex) { + return; + } + + var precision = gl.UNSIGNED_BYTE; + if (this.properties.high_precision) { + precision = gl.half_float_ext ? gl.HALF_FLOAT_OES : gl.FLOAT; + } + + if ( + !this._temp_texture || + this._temp_texture.type != precision || + this._temp_texture.width != tex.width || + this._temp_texture.height != tex.height + ) { + this._temp_texture = new GL.Texture(tex.width, tex.height, { + type: precision, + format: gl.RGBA, + filter: gl.LINEAR + }); + } + + var uniforms = this._uniforms; + + //iterations + var distance = this.properties.distance; + if (this.isInputConnected(1)) { + distance = this.getInputData(1); + this.properties.distance = distance; + } + + var range = this.properties.range; + if (this.isInputConnected(2)) { + range = this.getInputData(2); + this.properties.range = range; + } + + uniforms.u_distance = distance; + uniforms.u_range = range; + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var mesh = Mesh.getScreenQuad(); + if (!LGraphTextureDepthRange._shader) { + LGraphTextureDepthRange._shader = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureDepthRange.pixel_shader + ); + LGraphTextureDepthRange._shader_onlydepth = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureDepthRange.pixel_shader, + { ONLY_DEPTH: "" } + ); + } + var shader = this.properties.only_depth + ? LGraphTextureDepthRange._shader_onlydepth + : LGraphTextureDepthRange._shader; + + //NEAR AND FAR PLANES + var planes = null; + if (tex.near_far_planes) { + planes = tex.near_far_planes; + } else if (window.LS && LS.Renderer._main_camera) { + planes = LS.Renderer._main_camera._uniforms.u_camera_planes; + } else { + planes = [0.1, 1000]; + } //hardcoded + uniforms.u_camera_planes = planes; + + this._temp_texture.drawTo(function() { + tex.bind(0); + shader.uniforms(uniforms).draw(mesh); + }); + + this._temp_texture.near_far_planes = planes; + this.setOutputData(0, this._temp_texture); + }; + + LGraphTextureDepthRange.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 u_camera_planes;\n\ + uniform float u_distance;\n\ + uniform float u_range;\n\ + \n\ + float LinearDepth()\n\ + {\n\ + float zNear = u_camera_planes.x;\n\ + float zFar = u_camera_planes.y;\n\ + float depth = texture2D(u_texture, v_coord).x;\n\ + depth = depth * 2.0 - 1.0;\n\ + return zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\ + }\n\ + \n\ + void main() {\n\ + float depth = LinearDepth();\n\ + #ifdef ONLY_DEPTH\n\ + gl_FragColor = vec4(depth);\n\ + #else\n\ + float diff = abs(depth * u_camera_planes.y - u_distance);\n\ + float dof = 1.0;\n\ + if(diff <= u_range)\n\ + dof = diff / u_range;\n\ + gl_FragColor = vec4(dof);\n\ + #endif\n\ + }\n\ + "; + + LiteGraph.registerNodeType( "texture/depth_range", LGraphTextureDepthRange ); + + + // Texture Depth ***************************************** + function LGraphTextureLinearDepth() { + this.addInput("Texture", "Texture"); + this.addOutput("Texture", "Texture"); + this.properties = { + precision: LGraphTexture.DEFAULT, + invert: false + }; + this._uniforms = { + u_texture: 0, + u_near: 0.1, + u_far: 10000 + }; + } + + LGraphTextureLinearDepth.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureLinearDepth.title = "Linear Depth"; + LGraphTextureLinearDepth.desc = "Creates a color texture with linear depth"; + + LGraphTextureLinearDepth.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { + return; + } //saves work + + var tex = this.getInputData(0); + if (!tex || (tex.format != gl.DEPTH_COMPONENT && tex.format != gl.DEPTH_STENCIL) ) { + return; + } + + var precision = this.properties.precision == LGraphTexture.HIGH ? gl.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE; + + if ( !this._temp_texture || this._temp_texture.type != precision || this._temp_texture.width != tex.width || this._temp_texture.height != tex.height ) { + this._temp_texture = new GL.Texture(tex.width, tex.height, { + type: precision, + format: gl.RGB, + filter: gl.LINEAR + }); + } + + var uniforms = this._uniforms; + + uniforms.u_near = tex.near_far_planes[0]; + uniforms.u_far = tex.near_far_planes[1]; + uniforms.u_invert = this.properties.invert ? 1 : 0; + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var mesh = Mesh.getScreenQuad(); + if(!LGraphTextureLinearDepth._shader) + LGraphTextureLinearDepth._shader = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureLinearDepth.pixel_shader); + var shader = LGraphTextureLinearDepth._shader; + + //NEAR AND FAR PLANES + var planes = null; + if (tex.near_far_planes) { + planes = tex.near_far_planes; + } else if (window.LS && LS.Renderer._main_camera) { + planes = LS.Renderer._main_camera._uniforms.u_camera_planes; + } else { + planes = [0.1, 1000]; + } //hardcoded + uniforms.u_camera_planes = planes; + + this._temp_texture.drawTo(function() { + tex.bind(0); + shader.uniforms(uniforms).draw(mesh); + }); + + this._temp_texture.near_far_planes = planes; + this.setOutputData(0, this._temp_texture); + }; + + LGraphTextureLinearDepth.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform float u_near;\n\ + uniform float u_far;\n\ + uniform int u_invert;\n\ + \n\ + void main() {\n\ + float zNear = u_near;\n\ + float zFar = u_far;\n\ + float depth = texture2D(u_texture, v_coord).x;\n\ + depth = depth * 2.0 - 1.0;\n\ + float f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\ + if( u_invert == 1 )\n\ + f = 1.0 - f;\n\ + gl_FragColor = vec4(vec3(f),1.0);\n\ + }\n\ + "; + + LiteGraph.registerNodeType( "texture/linear_depth", LGraphTextureLinearDepth ); + + // Texture Blur ***************************************** + function LGraphTextureBlur() { + this.addInput("Texture", "Texture"); + this.addInput("Iterations", "number"); + this.addInput("Intensity", "number"); + this.addOutput("Blurred", "Texture"); + this.properties = { + intensity: 1, + iterations: 1, + preserve_aspect: false, + scale: [1, 1], + precision: LGraphTexture.DEFAULT + }; + } + + LGraphTextureBlur.title = "Blur"; + LGraphTextureBlur.desc = "Blur a texture"; + + LGraphTextureBlur.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureBlur.max_iterations = 20; + + LGraphTextureBlur.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + var temp = this._final_texture; + + if ( + !temp || + temp.width != tex.width || + temp.height != tex.height || + temp.type != tex.type + ) { + //we need two textures to do the blurring + //this._temp_texture = new GL.Texture( tex.width, tex.height, { type: tex.type, format: gl.RGBA, filter: gl.LINEAR }); + temp = this._final_texture = new GL.Texture( + tex.width, + tex.height, + { type: tex.type, format: gl.RGBA, filter: gl.LINEAR } + ); + } + + //iterations + var iterations = this.properties.iterations; + if (this.isInputConnected(1)) { + iterations = this.getInputData(1); + this.properties.iterations = iterations; + } + iterations = Math.min( + Math.floor(iterations), + LGraphTextureBlur.max_iterations + ); + if (iterations == 0) { + //skip blurring + this.setOutputData(0, tex); + return; + } + + var intensity = this.properties.intensity; + if (this.isInputConnected(2)) { + intensity = this.getInputData(2); + this.properties.intensity = intensity; + } + + //blur sometimes needs an aspect correction + var aspect = LiteGraph.camera_aspect; + if (!aspect && window.gl !== undefined) { + aspect = gl.canvas.height / gl.canvas.width; + } + if (!aspect) { + aspect = 1; + } + aspect = this.properties.preserve_aspect ? aspect : 1; + + var scale = this.properties.scale || [1, 1]; + tex.applyBlur(aspect * scale[0], scale[1], intensity, temp); + for (var i = 1; i < iterations; ++i) { + temp.applyBlur( + aspect * scale[0] * (i + 1), + scale[1] * (i + 1), + intensity + ); + } + + this.setOutputData(0, temp); + }; + + /* +LGraphTextureBlur.pixel_shader = "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 u_offset;\n\ + uniform float u_intensity;\n\ + void main() {\n\ + vec4 sum = vec4(0.0);\n\ + vec4 center = texture2D(u_texture, v_coord);\n\ + sum += texture2D(u_texture, v_coord + u_offset * -4.0) * 0.05/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * -3.0) * 0.09/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * -2.0) * 0.12/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * -1.0) * 0.15/0.98;\n\ + sum += center * 0.16/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * 4.0) * 0.05/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * 3.0) * 0.09/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * 2.0) * 0.12/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * 1.0) * 0.15/0.98;\n\ + gl_FragColor = u_intensity * sum;\n\ + }\n\ + "; +*/ + + LiteGraph.registerNodeType("texture/blur", LGraphTextureBlur); + + // Texture Glow ***************************************** + //based in https://catlikecoding.com/unity/tutorials/advanced-rendering/bloom/ + function LGraphTextureGlow() { + this.addInput("in", "Texture"); + this.addInput("dirt", "Texture"); + this.addOutput("out", "Texture"); + this.addOutput("glow", "Texture"); + this.properties = { + enabled: true, + intensity: 1, + persistence: 0.99, + iterations: 16, + threshold: 0, + scale: 1, + dirt_factor: 0.5, + precision: LGraphTexture.DEFAULT + }; + this._textures = []; + this._uniforms = { + u_intensity: 1, + u_texture: 0, + u_glow_texture: 1, + u_threshold: 0, + u_texel_size: vec2.create() + }; + } + + LGraphTextureGlow.title = "Glow"; + LGraphTextureGlow.desc = "Filters a texture giving it a glow effect"; + LGraphTextureGlow.weights = new Float32Array([0.5, 0.4, 0.3, 0.2]); + + LGraphTextureGlow.widgets_info = { + iterations: { + type: "number", + min: 0, + max: 16, + step: 1, + precision: 0 + }, + threshold: { + type: "number", + min: 0, + max: 10, + step: 0.01, + precision: 2 + }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureGlow.prototype.onGetInputs = function() { + return [ + ["enabled", "boolean"], + ["threshold", "number"], + ["intensity", "number"], + ["persistence", "number"], + ["iterations", "number"], + ["dirt_factor", "number"] + ]; + }; + + LGraphTextureGlow.prototype.onGetOutputs = function() { + return [["average", "Texture"]]; + }; + + LGraphTextureGlow.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (!this.isAnyOutputConnected()) { + return; + } //saves work + + if ( + this.properties.precision === LGraphTexture.PASS_THROUGH || + this.getInputOrProperty("enabled") === false + ) { + this.setOutputData(0, tex); + return; + } + + var width = tex.width; + var height = tex.height; + + var texture_info = { + format: tex.format, + type: tex.type, + minFilter: GL.LINEAR, + magFilter: GL.LINEAR, + wrap: gl.CLAMP_TO_EDGE + }; + var type = LGraphTexture.getTextureType( + this.properties.precision, + tex + ); + + var uniforms = this._uniforms; + var textures = this._textures; + + //cut + var shader = LGraphTextureGlow._cut_shader; + if (!shader) { + shader = LGraphTextureGlow._cut_shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureGlow.cut_pixel_shader + ); + } + + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.BLEND); + + uniforms.u_threshold = this.getInputOrProperty("threshold"); + var currentDestination = (textures[0] = GL.Texture.getTemporary( + width, + height, + texture_info + )); + tex.blit(currentDestination, shader.uniforms(uniforms)); + var currentSource = currentDestination; + + var iterations = this.getInputOrProperty("iterations"); + iterations = Math.clamp(iterations, 1, 16) | 0; + var texel_size = uniforms.u_texel_size; + var intensity = this.getInputOrProperty("intensity"); + + uniforms.u_intensity = 1; + uniforms.u_delta = this.properties.scale; //1 + + //downscale/upscale shader + var shader = LGraphTextureGlow._shader; + if (!shader) { + shader = LGraphTextureGlow._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureGlow.scale_pixel_shader + ); + } + + var i = 1; + //downscale + for (; i < iterations; i++) { + width = width >> 1; + if ((height | 0) > 1) { + height = height >> 1; + } + if (width < 2) { + break; + } + currentDestination = textures[i] = GL.Texture.getTemporary( + width, + height, + texture_info + ); + texel_size[0] = 1 / currentSource.width; + texel_size[1] = 1 / currentSource.height; + currentSource.blit( + currentDestination, + shader.uniforms(uniforms) + ); + currentSource = currentDestination; + } + + //average + if (this.isOutputConnected(2)) { + var average_texture = this._average_texture; + if ( + !average_texture || + average_texture.type != tex.type || + average_texture.format != tex.format + ) { + average_texture = this._average_texture = new GL.Texture( + 1, + 1, + { + type: tex.type, + format: tex.format, + filter: gl.LINEAR + } + ); + } + texel_size[0] = 1 / currentSource.width; + texel_size[1] = 1 / currentSource.height; + uniforms.u_intensity = intensity; + uniforms.u_delta = 1; + currentSource.blit(average_texture, shader.uniforms(uniforms)); + this.setOutputData(2, average_texture); + } + + //upscale and blend + gl.enable(gl.BLEND); + gl.blendFunc(gl.ONE, gl.ONE); + uniforms.u_intensity = this.getInputOrProperty("persistence"); + uniforms.u_delta = 0.5; + + for ( + i -= 2; + i >= 0; + i-- // i-=2 => -1 to point to last element in array, -1 to go to texture above + ) { + currentDestination = textures[i]; + textures[i] = null; + texel_size[0] = 1 / currentSource.width; + texel_size[1] = 1 / currentSource.height; + currentSource.blit( + currentDestination, + shader.uniforms(uniforms) + ); + GL.Texture.releaseTemporary(currentSource); + currentSource = currentDestination; + } + gl.disable(gl.BLEND); + + //glow + if (this.isOutputConnected(1)) { + var glow_texture = this._glow_texture; + if ( + !glow_texture || + glow_texture.width != tex.width || + glow_texture.height != tex.height || + glow_texture.type != type || + glow_texture.format != tex.format + ) { + glow_texture = this._glow_texture = new GL.Texture( + tex.width, + tex.height, + { type: type, format: tex.format, filter: gl.LINEAR } + ); + } + currentSource.blit(glow_texture); + this.setOutputData(1, glow_texture); + } + + //final composition + if (this.isOutputConnected(0)) { + var final_texture = this._final_texture; + if ( + !final_texture || + final_texture.width != tex.width || + final_texture.height != tex.height || + final_texture.type != type || + final_texture.format != tex.format + ) { + final_texture = this._final_texture = new GL.Texture( + tex.width, + tex.height, + { type: type, format: tex.format, filter: gl.LINEAR } + ); + } + + var dirt_texture = this.getInputData(1); + var dirt_factor = this.getInputOrProperty("dirt_factor"); + + uniforms.u_intensity = intensity; + + shader = dirt_texture + ? LGraphTextureGlow._dirt_final_shader + : LGraphTextureGlow._final_shader; + if (!shader) { + if (dirt_texture) { + shader = LGraphTextureGlow._dirt_final_shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureGlow.final_pixel_shader, + { USE_DIRT: "" } + ); + } else { + shader = LGraphTextureGlow._final_shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureGlow.final_pixel_shader + ); + } + } + + final_texture.drawTo(function() { + tex.bind(0); + currentSource.bind(1); + if (dirt_texture) { + shader.setUniform("u_dirt_factor", dirt_factor); + shader.setUniform( + "u_dirt_texture", + dirt_texture.bind(2) + ); + } + shader.toViewport(uniforms); + }); + this.setOutputData(0, final_texture); + } + + GL.Texture.releaseTemporary(currentSource); + }; + + LGraphTextureGlow.cut_pixel_shader = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform float u_threshold;\n\ + void main() {\n\ + gl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\ + }"; + + LGraphTextureGlow.scale_pixel_shader = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 u_texel_size;\n\ + uniform float u_delta;\n\ + uniform float u_intensity;\n\ + \n\ + vec4 sampleBox(vec2 uv) {\n\ + vec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\ + vec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\ + return s * 0.25;\n\ + }\n\ + void main() {\n\ + gl_FragColor = u_intensity * sampleBox( v_coord );\n\ + }"; + + LGraphTextureGlow.final_pixel_shader = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_glow_texture;\n\ + #ifdef USE_DIRT\n\ + uniform sampler2D u_dirt_texture;\n\ + #endif\n\ + uniform vec2 u_texel_size;\n\ + uniform float u_delta;\n\ + uniform float u_intensity;\n\ + uniform float u_dirt_factor;\n\ + \n\ + vec4 sampleBox(vec2 uv) {\n\ + vec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\ + vec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\ + return s * 0.25;\n\ + }\n\ + void main() {\n\ + vec4 glow = sampleBox( v_coord );\n\ + #ifdef USE_DIRT\n\ + glow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\ + #endif\n\ + gl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\ + }"; + + LiteGraph.registerNodeType("texture/glow", LGraphTextureGlow); + + // Texture Filter ***************************************** + function LGraphTextureKuwaharaFilter() { + this.addInput("Texture", "Texture"); + this.addOutput("Filtered", "Texture"); + this.properties = { intensity: 1, radius: 5 }; + } + + LGraphTextureKuwaharaFilter.title = "Kuwahara Filter"; + LGraphTextureKuwaharaFilter.desc = + "Filters a texture giving an artistic oil canvas painting"; + + LGraphTextureKuwaharaFilter.max_radius = 10; + LGraphTextureKuwaharaFilter._shaders = []; + + LGraphTextureKuwaharaFilter.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + var temp = this._temp_texture; + + if ( + !temp || + temp.width != tex.width || + temp.height != tex.height || + temp.type != tex.type + ) { + this._temp_texture = new GL.Texture(tex.width, tex.height, { + type: tex.type, + format: gl.RGBA, + filter: gl.LINEAR + }); + } + + //iterations + var radius = this.properties.radius; + radius = Math.min( + Math.floor(radius), + LGraphTextureKuwaharaFilter.max_radius + ); + if (radius == 0) { + //skip blurring + this.setOutputData(0, tex); + return; + } + + var intensity = this.properties.intensity; + + //blur sometimes needs an aspect correction + var aspect = LiteGraph.camera_aspect; + if (!aspect && window.gl !== undefined) { + aspect = gl.canvas.height / gl.canvas.width; + } + if (!aspect) { + aspect = 1; + } + aspect = this.properties.preserve_aspect ? aspect : 1; + + if (!LGraphTextureKuwaharaFilter._shaders[radius]) { + LGraphTextureKuwaharaFilter._shaders[radius] = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureKuwaharaFilter.pixel_shader, + { RADIUS: radius.toFixed(0) } + ); + } + + var shader = LGraphTextureKuwaharaFilter._shaders[radius]; + var mesh = GL.Mesh.getScreenQuad(); + tex.bind(0); + + this._temp_texture.drawTo(function() { + shader + .uniforms({ + u_texture: 0, + u_intensity: intensity, + u_resolution: [tex.width, tex.height], + u_iResolution: [1 / tex.width, 1 / tex.height] + }) + .draw(mesh); + }); + + this.setOutputData(0, this._temp_texture); + }; + + //from https://www.shadertoy.com/view/MsXSz4 + LGraphTextureKuwaharaFilter.pixel_shader = + "\n\ +precision highp float;\n\ +varying vec2 v_coord;\n\ +uniform sampler2D u_texture;\n\ +uniform float u_intensity;\n\ +uniform vec2 u_resolution;\n\ +uniform vec2 u_iResolution;\n\ +#ifndef RADIUS\n\ + #define RADIUS 7\n\ +#endif\n\ +void main() {\n\ +\n\ + const int radius = RADIUS;\n\ + vec2 fragCoord = v_coord;\n\ + vec2 src_size = u_iResolution;\n\ + vec2 uv = v_coord;\n\ + float n = float((radius + 1) * (radius + 1));\n\ + int i;\n\ + int j;\n\ + vec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\ + vec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\ + vec3 c;\n\ + \n\ + for (int j = -radius; j <= 0; ++j) {\n\ + for (int i = -radius; i <= 0; ++i) {\n\ + c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ + m0 += c;\n\ + s0 += c * c;\n\ + }\n\ + }\n\ + \n\ + for (int j = -radius; j <= 0; ++j) {\n\ + for (int i = 0; i <= radius; ++i) {\n\ + c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ + m1 += c;\n\ + s1 += c * c;\n\ + }\n\ + }\n\ + \n\ + for (int j = 0; j <= radius; ++j) {\n\ + for (int i = 0; i <= radius; ++i) {\n\ + c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ + m2 += c;\n\ + s2 += c * c;\n\ + }\n\ + }\n\ + \n\ + for (int j = 0; j <= radius; ++j) {\n\ + for (int i = -radius; i <= 0; ++i) {\n\ + c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ + m3 += c;\n\ + s3 += c * c;\n\ + }\n\ + }\n\ + \n\ + float min_sigma2 = 1e+2;\n\ + m0 /= n;\n\ + s0 = abs(s0 / n - m0 * m0);\n\ + \n\ + float sigma2 = s0.r + s0.g + s0.b;\n\ + if (sigma2 < min_sigma2) {\n\ + min_sigma2 = sigma2;\n\ + gl_FragColor = vec4(m0, 1.0);\n\ + }\n\ + \n\ + m1 /= n;\n\ + s1 = abs(s1 / n - m1 * m1);\n\ + \n\ + sigma2 = s1.r + s1.g + s1.b;\n\ + if (sigma2 < min_sigma2) {\n\ + min_sigma2 = sigma2;\n\ + gl_FragColor = vec4(m1, 1.0);\n\ + }\n\ + \n\ + m2 /= n;\n\ + s2 = abs(s2 / n - m2 * m2);\n\ + \n\ + sigma2 = s2.r + s2.g + s2.b;\n\ + if (sigma2 < min_sigma2) {\n\ + min_sigma2 = sigma2;\n\ + gl_FragColor = vec4(m2, 1.0);\n\ + }\n\ + \n\ + m3 /= n;\n\ + s3 = abs(s3 / n - m3 * m3);\n\ + \n\ + sigma2 = s3.r + s3.g + s3.b;\n\ + if (sigma2 < min_sigma2) {\n\ + min_sigma2 = sigma2;\n\ + gl_FragColor = vec4(m3, 1.0);\n\ + }\n\ +}\n\ +"; + + LiteGraph.registerNodeType( + "texture/kuwahara", + LGraphTextureKuwaharaFilter + ); + + // Texture ***************************************** + function LGraphTextureXDoGFilter() { + this.addInput("Texture", "Texture"); + this.addOutput("Filtered", "Texture"); + this.properties = { + sigma: 1.4, + k: 1.6, + p: 21.7, + epsilon: 79, + phi: 0.017 + }; + } + + LGraphTextureXDoGFilter.title = "XDoG Filter"; + LGraphTextureXDoGFilter.desc = + "Filters a texture giving an artistic ink style"; + + LGraphTextureXDoGFilter.max_radius = 10; + LGraphTextureXDoGFilter._shaders = []; + + LGraphTextureXDoGFilter.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + var temp = this._temp_texture; + if ( + !temp || + temp.width != tex.width || + temp.height != tex.height || + temp.type != tex.type + ) { + this._temp_texture = new GL.Texture(tex.width, tex.height, { + type: tex.type, + format: gl.RGBA, + filter: gl.LINEAR + }); + } + + if (!LGraphTextureXDoGFilter._xdog_shader) { + LGraphTextureXDoGFilter._xdog_shader = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureXDoGFilter.xdog_pixel_shader + ); + } + var shader = LGraphTextureXDoGFilter._xdog_shader; + var mesh = GL.Mesh.getScreenQuad(); + + var sigma = this.properties.sigma; + var k = this.properties.k; + var p = this.properties.p; + var epsilon = this.properties.epsilon; + var phi = this.properties.phi; + tex.bind(0); + this._temp_texture.drawTo(function() { + shader + .uniforms({ + src: 0, + sigma: sigma, + k: k, + p: p, + epsilon: epsilon, + phi: phi, + cvsWidth: tex.width, + cvsHeight: tex.height + }) + .draw(mesh); + }); + + this.setOutputData(0, this._temp_texture); + }; + + //from https://github.com/RaymondMcGuire/GPU-Based-Image-Processing-Tools/blob/master/lib_webgl/scripts/main.js + LGraphTextureXDoGFilter.xdog_pixel_shader = + "\n\ +precision highp float;\n\ +uniform sampler2D src;\n\n\ +uniform float cvsHeight;\n\ +uniform float cvsWidth;\n\n\ +uniform float sigma;\n\ +uniform float k;\n\ +uniform float p;\n\ +uniform float epsilon;\n\ +uniform float phi;\n\ +varying vec2 v_coord;\n\n\ +float cosh(float val)\n\ +{\n\ + float tmp = exp(val);\n\ + float cosH = (tmp + 1.0 / tmp) / 2.0;\n\ + return cosH;\n\ +}\n\n\ +float tanh(float val)\n\ +{\n\ + float tmp = exp(val);\n\ + float tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\ + return tanH;\n\ +}\n\n\ +float sinh(float val)\n\ +{\n\ + float tmp = exp(val);\n\ + float sinH = (tmp - 1.0 / tmp) / 2.0;\n\ + return sinH;\n\ +}\n\n\ +void main(void){\n\ + vec3 destColor = vec3(0.0);\n\ + float tFrag = 1.0 / cvsHeight;\n\ + float sFrag = 1.0 / cvsWidth;\n\ + vec2 Frag = vec2(sFrag,tFrag);\n\ + vec2 uv = gl_FragCoord.st;\n\ + float twoSigmaESquared = 2.0 * sigma * sigma;\n\ + float twoSigmaRSquared = twoSigmaESquared * k * k;\n\ + int halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\ + const int MAX_NUM_ITERATION = 99999;\n\ + vec2 sum = vec2(0.0);\n\ + vec2 norm = vec2(0.0);\n\n\ + for(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\ + int i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\ + int j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\ + float d = length(vec2(i,j));\n\ + vec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\ + exp( -d * d / twoSigmaRSquared ));\n\n\ + vec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\ + norm += kernel;\n\ + sum += kernel * L;\n\ + }\n\n\ + sum /= norm;\n\n\ + float H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\ + float edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\ + destColor = vec3(edge);\n\ + gl_FragColor = vec4(destColor, 1.0);\n\ +}"; + + LiteGraph.registerNodeType("texture/xDoG", LGraphTextureXDoGFilter); + + // Texture Webcam ***************************************** + function LGraphTextureWebcam() { + this.addOutput("Webcam", "Texture"); + this.properties = { texture_name: "", facingMode: "user" }; + this.boxcolor = "black"; this.version = 0; + } + + LGraphTextureWebcam.title = "Webcam"; + LGraphTextureWebcam.desc = "Webcam texture"; + + LGraphTextureWebcam.is_webcam_open = false; + + LGraphTextureWebcam.prototype.openStream = function() { + if (!navigator.getUserMedia) { + //console.log('getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags'); + return; + } + + this._waiting_confirmation = true; + + // Not showing vendor prefixes. + var constraints = { + audio: false, + video: { facingMode: this.properties.facingMode } + }; + navigator.mediaDevices + .getUserMedia(constraints) + .then(this.streamReady.bind(this)) + .catch(onFailSoHard); var that = this; - this.addWidget("button","update",null, function(){ that.must_update = true; }); - - this.geometry = { - vertices: null, - _id: generateGeometryId() + function onFailSoHard(e) { + LGraphTextureWebcam.is_webcam_open = false; + console.log("Webcam rejected", e); + that._webcam_stream = false; + that.boxcolor = "red"; + that.trigger("stream_error"); } - - this._old_obj = null; - this._last_radius = null; - } - - global.LGraphPoints3D = LGraphPoints3D; - - LGraphPoints3D.RECTANGLE = 1; - LGraphPoints3D.CIRCLE = 2; - - LGraphPoints3D.CUBE = 10; - LGraphPoints3D.SPHERE = 11; - LGraphPoints3D.HEMISPHERE = 12; - LGraphPoints3D.INSIDE_SPHERE = 13; - - LGraphPoints3D.OBJECT = 20; - LGraphPoints3D.OBJECT_UNIFORMLY = 21; - LGraphPoints3D.OBJECT_INSIDE = 22; - - LGraphPoints3D.MODE_VALUES = { "rectangle":LGraphPoints3D.RECTANGLE, "circle":LGraphPoints3D.CIRCLE, "cube":LGraphPoints3D.CUBE, "sphere":LGraphPoints3D.SPHERE, "hemisphere":LGraphPoints3D.HEMISPHERE, "inside_sphere":LGraphPoints3D.INSIDE_SPHERE, "object":LGraphPoints3D.OBJECT, "object_uniformly":LGraphPoints3D.OBJECT_UNIFORMLY, "object_inside":LGraphPoints3D.OBJECT_INSIDE }; - - LGraphPoints3D.widgets_info = { - mode: { widget: "combo", values: LGraphPoints3D.MODE_VALUES } }; - LGraphPoints3D.title = "list of points"; - LGraphPoints3D.desc = "returns an array of points"; + LGraphTextureWebcam.prototype.closeStream = function() { + if (this._webcam_stream) { + var tracks = this._webcam_stream.getTracks(); + if (tracks.length) { + for (var i = 0; i < tracks.length; ++i) { + tracks[i].stop(); + } + } + LGraphTextureWebcam.is_webcam_open = false; + this._webcam_stream = null; + this._video = null; + this.boxcolor = "black"; + this.trigger("stream_closed"); + } + }; - LGraphPoints3D.prototype.onPropertyChanged = function(name,value) + LGraphTextureWebcam.prototype.streamReady = function(localMediaStream) { + this._webcam_stream = localMediaStream; + //this._waiting_confirmation = false; + this.boxcolor = "green"; + var video = this._video; + if (!video) { + video = document.createElement("video"); + video.autoplay = true; + video.srcObject = localMediaStream; + this._video = video; + //document.body.appendChild( video ); //debug + //when video info is loaded (size and so) + video.onloadedmetadata = function(e) { + // Ready to go. Do some stuff. + LGraphTextureWebcam.is_webcam_open = true; + console.log(e); + }; + } + this.trigger("stream_ready", video); + }; + + LGraphTextureWebcam.prototype.onPropertyChanged = function( + name, + value + ) { + if (name == "facingMode") { + this.properties.facingMode = value; + this.closeStream(); + this.openStream(); + } + }; + + LGraphTextureWebcam.prototype.onRemoved = function() { + if (!this._webcam_stream) { + return; + } + + var tracks = this._webcam_stream.getTracks(); + if (tracks.length) { + for (var i = 0; i < tracks.length; ++i) { + tracks[i].stop(); + } + } + + this._webcam_stream = null; + this._video = null; + }; + + LGraphTextureWebcam.prototype.onDrawBackground = function(ctx) { + if (this.flags.collapsed || this.size[1] <= 20) { + return; + } + + if (!this._video) { + return; + } + + //render to graph canvas + ctx.save(); + if (!ctx.webgl) { + //reverse image + ctx.drawImage(this._video, 0, 0, this.size[0], this.size[1]); + } else { + if (this._video_texture) { + ctx.drawImage( + this._video_texture, + 0, + 0, + this.size[0], + this.size[1] + ); + } + } + ctx.restore(); + }; + + LGraphTextureWebcam.prototype.onExecute = function() { + if (this._webcam_stream == null && !this._waiting_confirmation) { + this.openStream(); + } + + if (!this._video || !this._video.videoWidth) { + return; + } + + var width = this._video.videoWidth; + var height = this._video.videoHeight; + + var temp = this._video_texture; + if (!temp || temp.width != width || temp.height != height) { + this._video_texture = new GL.Texture(width, height, { + format: gl.RGB, + filter: gl.LINEAR + }); + } + + this._video_texture.uploadImage(this._video); + this._video_texture.version = ++this.version; + + if (this.properties.texture_name) { + var container = LGraphTexture.getTexturesContainer(); + container[this.properties.texture_name] = this._video_texture; + } + + this.setOutputData(0, this._video_texture); + for (var i = 1; i < this.outputs.length; ++i) { + if (!this.outputs[i]) { + continue; + } + switch (this.outputs[i].name) { + case "width": + this.setOutputData(i, this._video.videoWidth); + break; + case "height": + this.setOutputData(i, this._video.videoHeight); + break; + } + } + }; + + LGraphTextureWebcam.prototype.onGetOutputs = function() { + return [ + ["width", "number"], + ["height", "number"], + ["stream_ready", LiteGraph.EVENT], + ["stream_closed", LiteGraph.EVENT], + ["stream_error", LiteGraph.EVENT] + ]; + }; + + LiteGraph.registerNodeType("texture/webcam", LGraphTextureWebcam); + + //from https://github.com/spite/Wagner + function LGraphLensFX() { + this.addInput("in", "Texture"); + this.addInput("f", "number"); + this.addOutput("out", "Texture"); + this.properties = { + enabled: true, + factor: 1, + precision: LGraphTexture.LOW + }; + + this._uniforms = { u_texture: 0, u_factor: 1 }; + } + + LGraphLensFX.title = "Lens FX"; + LGraphLensFX.desc = "distortion and chromatic aberration"; + + LGraphLensFX.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphLensFX.prototype.onGetInputs = function() { + return [["enabled", "boolean"]]; + }; + + LGraphLensFX.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + if ( + this.properties.precision === LGraphTexture.PASS_THROUGH || + this.getInputOrProperty("enabled") === false + ) { + this.setOutputData(0, tex); + return; + } + + var temp = this._temp_texture; + if ( + !temp || + temp.width != tex.width || + temp.height != tex.height || + temp.type != tex.type + ) { + temp = this._temp_texture = new GL.Texture( + tex.width, + tex.height, + { type: tex.type, format: gl.RGBA, filter: gl.LINEAR } + ); + } + + var shader = LGraphLensFX._shader; + if (!shader) { + shader = LGraphLensFX._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphLensFX.pixel_shader + ); + } + + var factor = this.getInputData(1); + if (factor == null) { + factor = this.properties.factor; + } + + var uniforms = this._uniforms; + uniforms.u_factor = factor; + + //apply shader + gl.disable(gl.DEPTH_TEST); + temp.drawTo(function() { + tex.bind(0); + shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad()); + }); + + this.setOutputData(0, temp); + }; + + LGraphLensFX.pixel_shader = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform float u_factor;\n\ + vec2 barrelDistortion(vec2 coord, float amt) {\n\ + vec2 cc = coord - 0.5;\n\ + float dist = dot(cc, cc);\n\ + return coord + cc * dist * amt;\n\ + }\n\ + \n\ + float sat( float t )\n\ + {\n\ + return clamp( t, 0.0, 1.0 );\n\ + }\n\ + \n\ + float linterp( float t ) {\n\ + return sat( 1.0 - abs( 2.0*t - 1.0 ) );\n\ + }\n\ + \n\ + float remap( float t, float a, float b ) {\n\ + return sat( (t - a) / (b - a) );\n\ + }\n\ + \n\ + vec4 spectrum_offset( float t ) {\n\ + vec4 ret;\n\ + float lo = step(t,0.5);\n\ + float hi = 1.0-lo;\n\ + float w = linterp( remap( t, 1.0/6.0, 5.0/6.0 ) );\n\ + ret = vec4(lo,1.0,hi, 1.) * vec4(1.0-w, w, 1.0-w, 1.);\n\ + \n\ + return pow( ret, vec4(1.0/2.2) );\n\ + }\n\ + \n\ + const float max_distort = 2.2;\n\ + const int num_iter = 12;\n\ + const float reci_num_iter_f = 1.0 / float(num_iter);\n\ + \n\ + void main()\n\ + { \n\ + vec2 uv=v_coord;\n\ + vec4 sumcol = vec4(0.0);\n\ + vec4 sumw = vec4(0.0); \n\ + for ( int i=0; i= imin) - { - imid = ((imax + imin)*0.5)|0; - var t = areas[ imid ]; - if( t == f ) - return imid; - if( imin == (imax - 1) ) - return imin; - if (t < f) - imin = imid; - else - imax = imid; - } - return imid; - } - - LGraphPoints3D.generateFromObject = function( points, normals, size, obj, evenly ) - { - if(!obj) - return; - - var vertices = null; - var mesh_normals = null; - var indices = null; - var areas = null; - if( obj.constructor === GL.Mesh ) - { - vertices = obj.vertexBuffers.vertices.data; - mesh_normals = obj.vertexBuffers.normals ? obj.vertexBuffers.normals.data : null; - indices = obj.indexBuffers.indices ? obj.indexBuffers.indices.data : null; - if(!indices) - indices = obj.indexBuffers.triangles ? obj.indexBuffers.triangles.data : null; - } - if(!vertices) - return null; - var num_triangles = indices ? indices.length / 3 : vertices.length / (3*3); - var total_area = 0; //sum of areas of all triangles - - if(evenly) - { - areas = new Float32Array(num_triangles); //accum - for(var i = 0; i < num_triangles; ++i) - { - if(indices) - { - a = indices[i*3]*3; - b = indices[i*3+1]*3; - c = indices[i*3+2]*3; - } - else - { - a = i*9; - b = i*9+3; - c = i*9+6; - } - var P1 = vertices.subarray(a,a+3); - var P2 = vertices.subarray(b,b+3); - var P3 = vertices.subarray(c,c+3); - var aL = vec3.distance( P1, P2 ); - var bL = vec3.distance( P2, P3 ); - var cL = vec3.distance( P3, P1 ); - var s = (aL + bL+ cL) / 2; - total_area += Math.sqrt(s * (s - aL) * (s - bL) * (s - cL)); - areas[i] = total_area; - } - for(var i = 0; i < num_triangles; ++i) //normalize - areas[i] /= total_area; - } - - for(var i = 0; i < size; i+=3) - { - var r = Math.random(); - var index = evenly ? findRandomTriangle( areas, r ) : Math.floor(r * num_triangles ); - //get random triangle - var a = 0; - var b = 0; - var c = 0; - if(indices) - { - a = indices[index*3]*3; - b = indices[index*3+1]*3; - c = indices[index*3+2]*3; - } - else - { - a = index*9; - b = index*9+3; - c = index*9+6; - } - var s = Math.random(); - var t = Math.random(); - var sqrt_s = Math.sqrt(s); - var af = 1 - sqrt_s; - var bf = sqrt_s * ( 1 - t); - var cf = t * sqrt_s; - points[i] = af * vertices[a] + bf*vertices[b] + cf*vertices[c]; - points[i+1] = af * vertices[a+1] + bf*vertices[b+1] + cf*vertices[c+1]; - points[i+2] = af * vertices[a+2] + bf*vertices[b+2] + cf*vertices[c+2]; - if(normals && mesh_normals) - { - normals[i] = af * mesh_normals[a] + bf*mesh_normals[b] + cf*mesh_normals[c]; - normals[i+1] = af * mesh_normals[a+1] + bf*mesh_normals[b+1] + cf*mesh_normals[c+1]; - normals[i+2] = af * mesh_normals[a+2] + bf*mesh_normals[b+2] + cf*mesh_normals[c+2]; - var N = normals.subarray(i,i+3); - vec3.normalize(N,N); - } - } - } - - LGraphPoints3D.generateFromInsideObject = function( points, size, mesh ) - { - if(!mesh || mesh.constructor !== GL.Mesh) - return; - - var aabb = mesh.getBoundingBox(); - if(!mesh.octree) - mesh.octree = new GL.Octree( mesh ); - var octree = mesh.octree; - var origin = vec3.create(); - var direction = vec3.fromValues(1,0,0); - var temp = vec3.create(); - var i = 0; - var tries = 0; - while(i < size && tries < points.length * 10) //limit to avoid problems - { - tries += 1 - var r = vec3.random(temp); //random point inside the aabb - r[0] = (r[0] * 2 - 1) * aabb[3] + aabb[0]; - r[1] = (r[1] * 2 - 1) * aabb[4] + aabb[1]; - r[2] = (r[2] * 2 - 1) * aabb[5] + aabb[2]; - origin.set(r); - var hit = octree.testRay( origin, direction, 0, 10000, true, GL.Octree.ALL ); - if(!hit || hit.length % 2 == 0) //not inside - continue; - points.set( r, i ); - i+=3; - } - } - - LiteGraph.registerNodeType( "geometry/points3D", LGraphPoints3D ); - - - - function LGraphPointsToInstances() { - this.addInput("points", "geometry"); - this.addOutput("instances", "[mat4]"); - this.properties = { - mode: 1, - autoupdate: true - }; - - this.must_update = true; - this.matrices = []; - this.first_time = true; - } - - LGraphPointsToInstances.NORMAL = 0; - LGraphPointsToInstances.VERTICAL = 1; - LGraphPointsToInstances.SPHERICAL = 2; - LGraphPointsToInstances.RANDOM = 3; - LGraphPointsToInstances.RANDOM_VERTICAL = 4; - - LGraphPointsToInstances.modes = {"normal":0,"vertical":1,"spherical":2,"random":3,"random_vertical":4}; - LGraphPointsToInstances.widgets_info = { - mode: { widget: "combo", values: LGraphPointsToInstances.modes } + LGraphExposition.widgets_info = { + exposition: { widget: "slider", min: 0, max: 3 }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } }; - LGraphPointsToInstances.title = "points to inst"; - - LGraphPointsToInstances.prototype.onExecute = function() - { - var geo = this.getInputData(0); - if( !geo ) - { - this.setOutputData(0,null); + LGraphExposition.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { return; } - if( !this.isOutputConnected(0) ) + if (!this.isOutputConnected(0)) { return; + } //saves work - var has_changed = (geo._version != this._version || geo._id != this._geometry_id); - - if( has_changed && this.properties.autoupdate || this.first_time ) - { - this.first_time = false; - this.updateInstances( geo ); + var temp = this._temp_texture; + if ( + !temp || + temp.width != tex.width || + temp.height != tex.height || + temp.type != tex.type + ) { + temp = this._temp_texture = new GL.Texture( + tex.width, + tex.height, + { type: tex.type, format: gl.RGBA, filter: gl.LINEAR } + ); } - this.setOutputData( 0, this.matrices ); - } - - LGraphPointsToInstances.prototype.updateInstances = function( geometry ) - { - var vertices = geometry.vertices; - if(!vertices) - return null; - var normals = geometry.normals; - - var matrices = this.matrices; - var num_points = vertices.length / 3; - if( matrices.length != num_points) - matrices.length = num_points; - var identity = mat4.create(); - var temp = vec3.create(); - var zero = vec3.create(); - var UP = vec3.fromValues(0,1,0); - var FRONT = vec3.fromValues(0,0,-1); - var RIGHT = vec3.fromValues(1,0,0); - var R = quat.create(); - - var front = vec3.create(); - var right = vec3.create(); - var top = vec3.create(); - - for(var i = 0; i < vertices.length; i += 3) - { - var index = i/3; - var m = matrices[index]; - if(!m) - m = matrices[index] = mat4.create(); - m.set( identity ); - var point = vertices.subarray(i,i+3); - - switch(this.properties.mode) - { - case LGraphPointsToInstances.NORMAL: - mat4.setTranslation( m, point ); - if(normals) - { - var normal = normals.subarray(i,i+3); - top.set( normal ); - vec3.normalize( top, top ); - vec3.cross( right, FRONT, top ); - vec3.normalize( right, right ); - vec3.cross( front, right, top ); - vec3.normalize( front, front ); - m.set(right,0); - m.set(top,4); - m.set(front,8); - mat4.setTranslation( m, point ); - } - break; - case LGraphPointsToInstances.VERTICAL: - mat4.setTranslation( m, point ); - break; - case LGraphPointsToInstances.SPHERICAL: - front.set( point ); - vec3.normalize( front, front ); - vec3.cross( right, UP, front ); - vec3.normalize( right, right ); - vec3.cross( top, front, right ); - vec3.normalize( top, top ); - m.set(right,0); - m.set(top,4); - m.set(front,8); - mat4.setTranslation( m, point ); - break; - case LGraphPointsToInstances.RANDOM: - temp[0] = Math.random()*2 - 1; - temp[1] = Math.random()*2 - 1; - temp[2] = Math.random()*2 - 1; - vec3.normalize( temp, temp ); - quat.setAxisAngle( R, temp, Math.random() * 2 * Math.PI ); - mat4.fromQuat(m, R); - mat4.setTranslation( m, point ); - break; - case LGraphPointsToInstances.RANDOM_VERTICAL: - quat.setAxisAngle( R, UP, Math.random() * 2 * Math.PI ); - mat4.fromQuat(m, R); - mat4.setTranslation( m, point ); - break; - } + var shader = LGraphExposition._shader; + if (!shader) { + shader = LGraphExposition._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphExposition.pixel_shader + ); } - this._version = geometry._version; - this._geometry_id = geometry._id; - } - - LiteGraph.registerNodeType( "geometry/points_to_instances", LGraphPointsToInstances ); - - - function LGraphGeometryTransform() { - this.addInput("in", "geometry,[mat4]"); - this.addInput("mat4", "mat4"); - this.addOutput("out", "geometry"); - this.properties = {}; - - this.geometry = { - type: "triangles", - vertices: null, - _id: generateGeometryId(), - _version: 0 - }; - - this._last_geometry_id = -1; - this._last_version = -1; - this._last_key = ""; - - this.must_update = true; - } - - LGraphGeometryTransform.title = "Transform"; - - LGraphGeometryTransform.prototype.onExecute = function() { - - var input = this.getInputData(0); - var model = this.getInputData(1); - - if(!input) - return; - - //array of matrices - if(input.constructor === Array) - { - if(input.length == 0) - return; - this.outputs[0].type = "[mat4]"; - if( !this.isOutputConnected(0) ) - return; - - if(!model) - { - this.setOutputData(0,input); - return; - } - - if(!this._output) - this._output = new Array(); - if(this._output.length != input.length) - this._output.length = input.length; - for(var i = 0; i < input.length; ++i) - { - var m = this._output[i]; - if(!m) - m = this._output[i] = mat4.create(); - mat4.multiply(m,input[i],model); - } - this.setOutputData(0,this._output); - return; + var exp = this.properties.exposition; + var exp_input = this.getInputData(1); + if (exp_input != null) { + exp = this.properties.exposition = exp_input; } + var uniforms = this._uniforms; - //geometry - if(!input.vertices || !input.vertices.length) - return; - var geo = input; - this.outputs[0].type = "geometry"; - if( !this.isOutputConnected(0) ) - return; - if(!model) - { - this.setOutputData(0,geo); - return; - } + //apply shader + temp.drawTo(function() { + gl.disable(gl.DEPTH_TEST); + tex.bind(0); + shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad()); + }); - var key = typedArrayToArray(model).join(","); - - if( this.must_update || geo._id != this._last_geometry_id || geo._version != this._last_version || key != this._last_key ) - { - this.updateGeometry(geo, model); - this._last_key = key; - this._last_version = geo._version; - this._last_geometry_id = geo._id; - this.must_update = false; - } - - this.setOutputData(0,this.geometry); - } - - LGraphGeometryTransform.prototype.updateGeometry = function(geometry, model) { - var old_vertices = geometry.vertices; - var vertices = this.geometry.vertices; - if( !vertices || vertices.length != old_vertices.length ) - vertices = this.geometry.vertices = new Float32Array( old_vertices.length ); - var temp = vec3.create(); - - for(var i = 0, l = vertices.length; i < l; i+=3) - { - temp[0] = old_vertices[i]; temp[1] = old_vertices[i+1]; temp[2] = old_vertices[i+2]; - mat4.multiplyVec3( temp, model, temp ); - vertices[i] = temp[0]; vertices[i+1] = temp[1]; vertices[i+2] = temp[2]; - } - - if(geometry.normals) - { - if( !this.geometry.normals || this.geometry.normals.length != geometry.normals.length ) - this.geometry.normals = new Float32Array( geometry.normals.length ); - var normals = this.geometry.normals; - var normal_model = mat4.invert(mat4.create(), model); - if(normal_model) - mat4.transpose(normal_model, normal_model); - var old_normals = geometry.normals; - for(var i = 0, l = normals.length; i < l; i+=3) - { - temp[0] = old_normals[i]; temp[1] = old_normals[i+1]; temp[2] = old_normals[i+2]; - mat4.multiplyVec3( temp, normal_model, temp ); - normals[i] = temp[0]; normals[i+1] = temp[1]; normals[i+2] = temp[2]; - } - } - - this.geometry.type = geometry.type; - this.geometry._version++; - } - - LiteGraph.registerNodeType( "geometry/transform", LGraphGeometryTransform ); - - - function LGraphGeometryPolygon() { - this.addInput("sides", "number"); - this.addInput("radius", "number"); - this.addOutput("out", "geometry"); - this.properties = { sides: 6, radius: 1, uvs: false } - - this.geometry = { - type: "line_loop", - vertices: null, - _id: generateGeometryId() - }; - this.geometry_id = -1; - this.version = -1; - this.must_update = true; - - this.last_info = { sides: -1, radius: -1 }; - } - - LGraphGeometryPolygon.title = "Polygon"; - - LGraphGeometryPolygon.prototype.onExecute = function() { - - if( !this.isOutputConnected(0) ) - return; - - var sides = this.getInputOrProperty("sides"); - var radius = this.getInputOrProperty("radius"); - sides = Math.max(3,sides)|0; - - //update - if( this.last_info.sides != sides || this.last_info.radius != radius ) - this.updateGeometry(sides, radius); - - this.setOutputData(0,this.geometry); - } - - LGraphGeometryPolygon.prototype.updateGeometry = function(sides, radius) { - var num = 3*sides; - var vertices = this.geometry.vertices; - if( !vertices || vertices.length != num ) - vertices = this.geometry.vertices = new Float32Array( 3*sides ); - var delta = (Math.PI * 2) / sides; - var gen_uvs = this.properties.uvs; - if(gen_uvs) - { - uvs = this.geometry.coords = new Float32Array( 3*sides ); - } - - - for(var i = 0; i < sides; ++i) - { - var angle = delta * -i; - var x = Math.cos( angle ) * radius; - var y = 0; - var z = Math.sin( angle ) * radius; - vertices[i*3] = x; - vertices[i*3+1] = y; - vertices[i*3+2] = z; - - if(gen_uvs) - { - - - } - } - this.geometry._id = ++this.geometry_id; - this.geometry._version = ++this.version; - this.last_info.sides = sides; - this.last_info.radius = radius; - } - - LiteGraph.registerNodeType( "geometry/polygon", LGraphGeometryPolygon ); - - - function LGraphGeometryExtrude() { - - this.addInput("", "geometry"); - this.addOutput("", "geometry"); - this.properties = { top_cap: true, bottom_cap: true, offset: [0,100,0] }; - this.version = -1; - - this._last_geo_version = -1; - this._must_update = true; - } - - LGraphGeometryExtrude.title = "extrude"; - - LGraphGeometryExtrude.prototype.onPropertyChanged = function(name, value) - { - this._must_update = true; - } - - LGraphGeometryExtrude.prototype.onExecute = function() - { - var geo = this.getInputData(0); - if( !geo || !this.isOutputConnected(0) ) - return; - - if(geo.version != this._last_geo_version || this._must_update) - { - this._geo = this.extrudeGeometry( geo, this._geo ); - if(this._geo) - this._geo.version = this.version++; - this._must_update = false; - } - - this.setOutputData(0, this._geo); - } - - LGraphGeometryExtrude.prototype.extrudeGeometry = function( geo ) - { - //for every pair of vertices - var vertices = geo.vertices; - var num_points = vertices.length / 3; - - var tempA = vec3.create(); - var tempB = vec3.create(); - var tempC = vec3.create(); - var tempD = vec3.create(); - var offset = new Float32Array( this.properties.offset ); - - if(geo.type == "line_loop") - { - var new_vertices = new Float32Array( num_points * 6 * 3 ); //every points become 6 ( caps not included ) - var npos = 0; - for(var i = 0, l = vertices.length; i < l; i += 3) - { - tempA[0] = vertices[i]; tempA[1] = vertices[i+1]; tempA[2] = vertices[i+2]; - - if( i+3 < l ) //loop - { - tempB[0] = vertices[i+3]; tempB[1] = vertices[i+4]; tempB[2] = vertices[i+5]; - } - else - { - tempB[0] = vertices[0]; tempB[1] = vertices[1]; tempB[2] = vertices[2]; - } - - vec3.add( tempC, tempA, offset ); - vec3.add( tempD, tempB, offset ); - - new_vertices.set( tempA, npos ); npos += 3; - new_vertices.set( tempB, npos ); npos += 3; - new_vertices.set( tempC, npos ); npos += 3; - - new_vertices.set( tempB, npos ); npos += 3; - new_vertices.set( tempD, npos ); npos += 3; - new_vertices.set( tempC, npos ); npos += 3; - } - } - - var out_geo = { - _id: generateGeometryId(), - type: "triangles", - vertices: new_vertices - }; - - return out_geo; - } - - LiteGraph.registerNodeType( "geometry/extrude", LGraphGeometryExtrude ); - - - function LGraphGeometryEval() { - this.addInput("in", "geometry"); - this.addOutput("out", "geometry"); - - this.properties = { - code: "V[1] += 0.01 * Math.sin(I + T*0.001);", - execute_every_frame: false - }; - - this.geometry = null; - this.geometry_id = -1; - this.version = -1; - this.must_update = true; - - this.vertices = null; - this.func = null; - } - - LGraphGeometryEval.title = "geoeval"; - LGraphGeometryEval.desc = "eval code"; - - LGraphGeometryEval.widgets_info = { - code: { widget: "code" } + this.setOutputData(0, temp); }; - LGraphGeometryEval.prototype.onConfigure = function(o) - { + LGraphExposition.pixel_shader = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform float u_exposition;\n\ + \n\ + void main() {\n\ + vec4 color = texture2D( u_texture, v_coord );\n\ + gl_FragColor = vec4( color.xyz * u_exposition, color.a );\n\ + }"; + + LiteGraph.registerNodeType("texture/exposition", LGraphExposition); + + function LGraphToneMapping() { + this.addInput("in", "Texture"); + this.addInput("avg", "number,Texture"); + this.addOutput("out", "Texture"); + this.properties = { + enabled: true, + scale: 1, + gamma: 1, + average_lum: 1, + lum_white: 1, + precision: LGraphTexture.LOW + }; + + this._uniforms = { + u_texture: 0, + u_lumwhite2: 1, + u_igamma: 1, + u_scale: 1, + u_average_lum: 1 + }; + } + + LGraphToneMapping.title = "Tone Mapping"; + LGraphToneMapping.desc = + "Applies Tone Mapping to convert from high to low"; + + LGraphToneMapping.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphToneMapping.prototype.onGetInputs = function() { + return [["enabled", "boolean"]]; + }; + + LGraphToneMapping.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + if ( + this.properties.precision === LGraphTexture.PASS_THROUGH || + this.getInputOrProperty("enabled") === false + ) { + this.setOutputData(0, tex); + return; + } + + var temp = this._temp_texture; + + if ( + !temp || + temp.width != tex.width || + temp.height != tex.height || + temp.type != tex.type + ) { + temp = this._temp_texture = new GL.Texture( + tex.width, + tex.height, + { type: tex.type, format: gl.RGBA, filter: gl.LINEAR } + ); + } + + var avg = this.getInputData(1); + if (avg == null) { + avg = this.properties.average_lum; + } + + var uniforms = this._uniforms; + var shader = null; + + if (avg.constructor === Number) { + this.properties.average_lum = avg; + uniforms.u_average_lum = this.properties.average_lum; + shader = LGraphToneMapping._shader; + if (!shader) { + shader = LGraphToneMapping._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphToneMapping.pixel_shader + ); + } + } else if (avg.constructor === GL.Texture) { + uniforms.u_average_texture = avg.bind(1); + shader = LGraphToneMapping._shader_texture; + if (!shader) { + shader = LGraphToneMapping._shader_texture = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphToneMapping.pixel_shader, + { AVG_TEXTURE: "" } + ); + } + } + + uniforms.u_lumwhite2 = + this.properties.lum_white * this.properties.lum_white; + uniforms.u_scale = this.properties.scale; + uniforms.u_igamma = 1 / this.properties.gamma; + + //apply shader + gl.disable(gl.DEPTH_TEST); + temp.drawTo(function() { + tex.bind(0); + shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad()); + }); + + this.setOutputData(0, this._temp_texture); + }; + + LGraphToneMapping.pixel_shader = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform float u_scale;\n\ + #ifdef AVG_TEXTURE\n\ + uniform sampler2D u_average_texture;\n\ + #else\n\ + uniform float u_average_lum;\n\ + #endif\n\ + uniform float u_lumwhite2;\n\ + uniform float u_igamma;\n\ + vec3 RGB2xyY (vec3 rgb)\n\ + {\n\ + const mat3 RGB2XYZ = mat3(0.4124, 0.3576, 0.1805,\n\ + 0.2126, 0.7152, 0.0722,\n\ + 0.0193, 0.1192, 0.9505);\n\ + vec3 XYZ = RGB2XYZ * rgb;\n\ + \n\ + float f = (XYZ.x + XYZ.y + XYZ.z);\n\ + return vec3(XYZ.x / f,\n\ + XYZ.y / f,\n\ + XYZ.y);\n\ + }\n\ + \n\ + void main() {\n\ + vec4 color = texture2D( u_texture, v_coord );\n\ + vec3 rgb = color.xyz;\n\ + float average_lum = 0.0;\n\ + #ifdef AVG_TEXTURE\n\ + vec3 pixel = texture2D(u_average_texture,vec2(0.5)).xyz;\n\ + average_lum = (pixel.x + pixel.y + pixel.z) / 3.0;\n\ + #else\n\ + average_lum = u_average_lum;\n\ + #endif\n\ + //Ld - this part of the code is the same for both versions\n\ + float lum = dot(rgb, vec3(0.2126, 0.7152, 0.0722));\n\ + float L = (u_scale / average_lum) * lum;\n\ + float Ld = (L * (1.0 + L / u_lumwhite2)) / (1.0 + L);\n\ + //first\n\ + //vec3 xyY = RGB2xyY(rgb);\n\ + //xyY.z *= Ld;\n\ + //rgb = xyYtoRGB(xyY);\n\ + //second\n\ + rgb = (rgb / lum) * Ld;\n\ + rgb = max(rgb,vec3(0.001));\n\ + rgb = pow( rgb, vec3( u_igamma ) );\n\ + gl_FragColor = vec4( rgb, color.a );\n\ + }"; + + LiteGraph.registerNodeType("texture/tonemapping", LGraphToneMapping); + + function LGraphTexturePerlin() { + this.addOutput("out", "Texture"); + this.properties = { + width: 512, + height: 512, + seed: 0, + persistence: 0.1, + octaves: 8, + scale: 1, + offset: [0, 0], + amplitude: 1, + precision: LGraphTexture.DEFAULT + }; + this._key = 0; + this._texture = null; + this._uniforms = { + u_persistence: 0.1, + u_seed: 0, + u_offset: vec2.create(), + u_scale: 1, + u_viewport: vec2.create() + }; + } + + LGraphTexturePerlin.title = "Perlin"; + LGraphTexturePerlin.desc = "Generates a perlin noise texture"; + + LGraphTexturePerlin.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }, + width: { type: "Number", precision: 0, step: 1 }, + height: { type: "Number", precision: 0, step: 1 }, + octaves: { type: "Number", precision: 0, step: 1, min: 1, max: 50 } + }; + + LGraphTexturePerlin.prototype.onGetInputs = function() { + return [ + ["seed", "Number"], + ["persistence", "Number"], + ["octaves", "Number"], + ["scale", "Number"], + ["amplitude", "Number"], + ["offset", "vec2"] + ]; + }; + + LGraphTexturePerlin.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { + return; + } //saves work + + var w = this.properties.width | 0; + var h = this.properties.height | 0; + if (w == 0) { + w = gl.viewport_data[2]; + } //0 means default + if (h == 0) { + h = gl.viewport_data[3]; + } //0 means default + var type = LGraphTexture.getTextureType(this.properties.precision); + + var temp = this._texture; + if ( + !temp || + temp.width != w || + temp.height != h || + temp.type != type + ) { + temp = this._texture = new GL.Texture(w, h, { + type: type, + format: gl.RGB, + filter: gl.LINEAR + }); + } + + var persistence = this.getInputOrProperty("persistence"); + var octaves = this.getInputOrProperty("octaves"); + var offset = this.getInputOrProperty("offset"); + var scale = this.getInputOrProperty("scale"); + var amplitude = this.getInputOrProperty("amplitude"); + var seed = this.getInputOrProperty("seed"); + + //reusing old texture + var key = + "" + + w + + h + + type + + persistence + + octaves + + scale + + seed + + offset[0] + + offset[1] + + amplitude; + if (key == this._key) { + this.setOutputData(0, temp); + return; + } + this._key = key; + + //gather uniforms + var uniforms = this._uniforms; + uniforms.u_persistence = persistence; + uniforms.u_octaves = octaves; + uniforms.u_offset.set(offset); + uniforms.u_scale = scale; + uniforms.u_amplitude = amplitude; + uniforms.u_seed = seed * 128; + uniforms.u_viewport[0] = w; + uniforms.u_viewport[1] = h; + + //render + var shader = LGraphTexturePerlin._shader; + if (!shader) { + shader = LGraphTexturePerlin._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTexturePerlin.pixel_shader + ); + } + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + + temp.drawTo(function() { + shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad()); + }); + + this.setOutputData(0, temp); + }; + + LGraphTexturePerlin.pixel_shader = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform vec2 u_offset;\n\ + uniform float u_scale;\n\ + uniform float u_persistence;\n\ + uniform int u_octaves;\n\ + uniform float u_amplitude;\n\ + uniform vec2 u_viewport;\n\ + uniform float u_seed;\n\ + #define M_PI 3.14159265358979323846\n\ + \n\ + float rand(vec2 c){ return fract(sin(dot(c.xy ,vec2( 12.9898 + u_seed,78.233 + u_seed))) * 43758.5453); }\n\ + \n\ + float noise(vec2 p, float freq ){\n\ + float unit = u_viewport.x/freq;\n\ + vec2 ij = floor(p/unit);\n\ + vec2 xy = mod(p,unit)/unit;\n\ + //xy = 3.*xy*xy-2.*xy*xy*xy;\n\ + xy = .5*(1.-cos(M_PI*xy));\n\ + float a = rand((ij+vec2(0.,0.)));\n\ + float b = rand((ij+vec2(1.,0.)));\n\ + float c = rand((ij+vec2(0.,1.)));\n\ + float d = rand((ij+vec2(1.,1.)));\n\ + float x1 = mix(a, b, xy.x);\n\ + float x2 = mix(c, d, xy.x);\n\ + return mix(x1, x2, xy.y);\n\ + }\n\ + \n\ + float pNoise(vec2 p, int res){\n\ + float persistance = u_persistence;\n\ + float n = 0.;\n\ + float normK = 0.;\n\ + float f = 4.;\n\ + float amp = 1.0;\n\ + int iCount = 0;\n\ + for (int i = 0; i<50; i++){\n\ + n+=amp*noise(p, f);\n\ + f*=2.;\n\ + normK+=amp;\n\ + amp*=persistance;\n\ + if (iCount >= res)\n\ + break;\n\ + iCount++;\n\ + }\n\ + float nf = n/normK;\n\ + return nf*nf*nf*nf;\n\ + }\n\ + void main() {\n\ + vec2 uv = v_coord * u_scale * u_viewport + u_offset * u_scale;\n\ + vec4 color = vec4( pNoise( uv, u_octaves ) * u_amplitude );\n\ + gl_FragColor = color;\n\ + }"; + + LiteGraph.registerNodeType("texture/perlin", LGraphTexturePerlin); + + function LGraphTextureCanvas2D() { + this.addInput("v"); + this.addOutput("out", "Texture"); + this.properties = { + code: LGraphTextureCanvas2D.default_code, + width: 512, + height: 512, + clear: true, + precision: LGraphTexture.DEFAULT, + use_html_canvas: false + }; + this._func = null; + this._temp_texture = null; this.compileCode(); } - LGraphGeometryEval.prototype.compileCode = function() - { - if(!this.properties.code) + LGraphTextureCanvas2D.title = "Canvas2D"; + LGraphTextureCanvas2D.desc = "Executes Canvas2D code inside a texture or the viewport."; + LGraphTextureCanvas2D.help = "Set width and height to 0 to match viewport size."; + + LGraphTextureCanvas2D.default_code = "//vars: canvas,ctx,time\nctx.fillStyle='red';\nctx.fillRect(0,0,50,50);\n"; + + LGraphTextureCanvas2D.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }, + code: { type: "code" }, + width: { type: "Number", precision: 0, step: 1 }, + height: { type: "Number", precision: 0, step: 1 } + }; + + LGraphTextureCanvas2D.prototype.onPropertyChanged = function( name, value ) { + if (name == "code" ) + this.compileCode( value ); + } + + LGraphTextureCanvas2D.prototype.compileCode = function( code ) { + this._func = null; + if( !LiteGraph.allow_scripts ) return; - try - { - this.func = new Function("V","I","T", this.properties.code); - this.boxcolor = "#AFA"; - this.must_update = true; + try { + this._func = new Function( "canvas", "ctx", "time", "script","v", code ); + this.boxcolor = "#00FF00"; + } catch (err) { + this.boxcolor = "#FF0000"; + console.error("Error parsing script"); + console.error(err); } - catch (err) - { - this.boxcolor = "red"; + }; + + LGraphTextureCanvas2D.prototype.onExecute = function() { + var func = this._func; + if (!func || !this.isOutputConnected(0)) { + return; } + this.executeDraw( func ); } - LGraphGeometryEval.prototype.onPropertyChanged = function(name, value) - { - if(name == "code") - { - this.properties.code = value; - this.compileCode(); - } - } + LGraphTextureCanvas2D.prototype.executeDraw = function( func_context ) { - LGraphGeometryEval.prototype.onExecute = function() { - var geometry = this.getInputData(0); - if(!geometry) - return; - - if(!this.func) - { - this.setOutputData(0,geometry); - return; + var width = this.properties.width || gl.canvas.width; + var height = this.properties.height || gl.canvas.height; + var temp = this._temp_texture; + var type = LGraphTexture.getTextureType( this.properties.precision ); + if (!temp || temp.width != width || temp.height != height || temp.type != type ) { + temp = this._temp_texture = new GL.Texture(width, height, { + format: gl.RGBA, + filter: gl.LINEAR, + type: type + }); } - if( this.geometry_id != geometry._id || this.version != geometry._version || this.must_update || this.properties.execute_every_frame ) - { - this.must_update = false; - this.geometry_id = geometry._id; - if(this.properties.execute_every_frame) - this.version++; - else - this.version = geometry._version; - var func = this.func; - var T = getTime(); + var v = this.getInputData(0); - //clone - if(!this.geometry) - this.geometry = {}; - for(var i in geometry) + var properties = this.properties; + var that = this; + var time = this.graph.getTime(); + var ctx = gl; + var canvas = gl.canvas; + if( this.properties.use_html_canvas || !global.enableWebGLCanvas ) + { + if(!this._canvas) { - if(geometry[i] == null) - continue; - if( geometry[i].constructor == Float32Array ) - this.geometry[i] = new Float32Array( geometry[i] ); - else - this.geometry[i] = geometry[i]; + canvas = this._canvas = createCanvas(width.height); + ctx = this._ctx = canvas.getContext("2d"); } - this.geometry._id = geometry._id; - if(this.properties.execute_every_frame) - this.geometry._version = this.version; else - this.geometry._version = geometry._version + 1; - - var V = vec3.create(); - var vertices = this.vertices; - if(!vertices || this.vertices.length != geometry.vertices.length) - vertices = this.vertices = new Float32Array( geometry.vertices ); - else - vertices.set( geometry.vertices ); - for(var i = 0; i < vertices.length; i+=3) { - V[0] = vertices[i]; - V[1] = vertices[i+1]; - V[2] = vertices[i+2]; - func(V,i/3,T); - vertices[i] = V[0]; - vertices[i+1] = V[1]; - vertices[i+2] = V[2]; + canvas = this._canvas; + ctx = this._ctx; } - this.geometry.vertices = vertices; + canvas.width = width; + canvas.height = height; } - this.setOutputData(0,this.geometry); - } - - LiteGraph.registerNodeType( "geometry/eval", LGraphGeometryEval ); - -/* -function LGraphGeometryDisplace() { - this.addInput("in", "geometry"); - this.addInput("img", "image"); - this.addOutput("out", "geometry"); - - this.properties = { - grid_size: 1 - }; - - this.geometry = null; - this.geometry_id = -1; - this.version = -1; - this.must_update = true; - - this.vertices = null; - } - - LGraphGeometryDisplace.title = "displace"; - LGraphGeometryDisplace.desc = "displace points"; - - LGraphGeometryDisplace.prototype.onExecute = function() { - var geometry = this.getInputData(0); - var image = this.getInputData(1); - if(!geometry) - return; - - if(!image) - { - this.setOutputData(0,geometry); - return; - } - - if( this.geometry_id != geometry._id || this.version != geometry._version || this.must_update ) - { - this.must_update = false; - this.geometry_id = geometry._id; - this.version = geometry._version; - - //copy - this.geometry = {}; - for(var i in geometry) - this.geometry[i] = geometry[i]; - this.geometry._id = geometry._id; - this.geometry._version = geometry._version + 1; - - var grid_size = this.properties.grid_size; - if(grid_size != 0) - { - var vertices = this.vertices; - if(!vertices || this.vertices.length != this.geometry.vertices.length) - vertices = this.vertices = new Float32Array( this.geometry.vertices ); - for(var i = 0; i < vertices.length; i+=3) + if(ctx == gl) //using Canvas2DtoWebGL + temp.drawTo(function() { + gl.start2D(); + if(properties.clear) { - vertices[i] = Math.round(vertices[i]/grid_size) * grid_size; - vertices[i+1] = Math.round(vertices[i+1]/grid_size) * grid_size; - vertices[i+2] = Math.round(vertices[i+2]/grid_size) * grid_size; + gl.clearColor(0,0,0,0); + gl.clear( gl.COLOR_BUFFER_BIT ); } - this.geometry.vertices = vertices; - } - } - this.setOutputData(0,this.geometry); - } - - LiteGraph.registerNodeType( "geometry/displace", LGraphGeometryDisplace ); -*/ - - function LGraphConnectPoints() { - this.addInput("in", "geometry"); - this.addOutput("out", "geometry"); - - this.properties = { - min_dist: 0.4, - max_dist: 0.5, - max_connections: 0, - probability: 1 - }; - - this.geometry_id = -1; - this.version = -1; - this.my_version = 1; - this.must_update = true; - } - - LGraphConnectPoints.title = "connect points"; - LGraphConnectPoints.desc = "adds indices between near points"; - - LGraphConnectPoints.prototype.onPropertyChanged = function(name,value) - { - this.must_update = true; - } - - LGraphConnectPoints.prototype.onExecute = function() { - var geometry = this.getInputData(0); - if(!geometry) - return; - - if( this.geometry_id != geometry._id || this.version != geometry._version || this.must_update ) - { - this.must_update = false; - this.geometry_id = geometry._id; - this.version = geometry._version; - - //copy - this.geometry = {}; - for(var i in geometry) - this.geometry[i] = geometry[i]; - this.geometry._id = generateGeometryId(); - this.geometry._version = this.my_version++; - - var vertices = geometry.vertices; - var l = vertices.length; - var min_dist = this.properties.min_dist; - var max_dist = this.properties.max_dist; - var probability = this.properties.probability; - var max_connections = this.properties.max_connections; - var indices = []; - - for(var i = 0; i < l; i+=3) - { - var x = vertices[i]; - var y = vertices[i+1]; - var z = vertices[i+2]; - var connections = 0; - for(var j = i+3; j < l; j+=3) - { - var x2 = vertices[j]; - var y2 = vertices[j+1]; - var z2 = vertices[j+2]; - var dist = Math.sqrt( (x-x2)*(x-x2) + (y-y2)*(y-y2) + (z-z2)*(z-z2)); - if(dist > max_dist || dist < min_dist || (probability < 1 && probability < Math.random()) ) - continue; - indices.push(i/3,j/3); - connections += 1; - if(max_connections && connections > max_connections) - break; + try { + if (func_context.draw) { + func_context.draw.call(that, canvas, ctx, time, func_context, v); + } else { + func_context.call(that, canvas, ctx, time, func_context,v); + } + that.boxcolor = "#00FF00"; + } catch (err) { + that.boxcolor = "#FF0000"; + console.error("Error executing script"); + console.error(err); } - } - this.geometry.indices = this.indices = new Uint32Array(indices); - } - - if(this.indices && this.indices.length) + gl.finish2D(); + }); + else //rendering to offscren canvas and uploading to texture { - this.geometry.indices = this.indices; - this.setOutputData( 0, this.geometry ); - } - else - this.setOutputData( 0, null ); - } + if(properties.clear) + ctx.clearRect(0,0,canvas.width,canvas.height); - LiteGraph.registerNodeType( "geometry/connectPoints", LGraphConnectPoints ); - - //Works with Litegl.js to create WebGL nodes - if (typeof GL == "undefined") //LiteGL RELATED ********************************************** - return; - - function LGraphToGeometry() { - this.addInput("mesh", "mesh"); - this.addOutput("out", "geometry"); - - this.geometry = {}; - this.last_mesh = null; - } - - LGraphToGeometry.title = "to geometry"; - LGraphToGeometry.desc = "converts a mesh to geometry"; - - LGraphToGeometry.prototype.onExecute = function() { - var mesh = this.getInputData(0); - if(!mesh) - return; - - if(mesh != this.last_mesh) - { - this.last_mesh = mesh; - for(i in mesh.vertexBuffers) - { - var buffer = mesh.vertexBuffers[i]; - this.geometry[i] = buffer.data + try { + if (func_context.draw) { + func_context.draw.call(this, canvas, ctx, time, func_context, v); + } else { + func_context.call(this, canvas, ctx, time, func_context,v); + } + this.boxcolor = "#00FF00"; + } catch (err) { + this.boxcolor = "#FF0000"; + console.error("Error executing script"); + console.error(err); } - if(mesh.indexBuffers["triangles"]) - this.geometry.indices = mesh.indexBuffers["triangles"].data; - - this.geometry._id = generateGeometryId(); - this.geometry._version = 0; + temp.uploadImage( canvas ); } - this.setOutputData(0,this.geometry); - if(this.geometry) - this.setOutputData(1,this.geometry.vertices); - } + this.setOutputData(0, temp); + }; - LiteGraph.registerNodeType( "geometry/toGeometry", LGraphToGeometry ); + LiteGraph.registerNodeType("texture/canvas2D", LGraphTextureCanvas2D); - function LGraphGeometryToMesh() { - this.addInput("in", "geometry"); - this.addOutput("mesh", "mesh"); - this.properties = {}; - this.version = -1; - this.mesh = null; - } + // To do chroma keying ***************** - LGraphGeometryToMesh.title = "Geo to Mesh"; - - LGraphGeometryToMesh.prototype.updateMesh = function(geometry) - { - if(!this.mesh) - this.mesh = new GL.Mesh(); - - for(var i in geometry) - { - if(i[0] == "_") - continue; - - var buffer_data = geometry[i]; - - var info = GL.Mesh.common_buffers[i]; - if(!info && i != "indices") //unknown buffer - continue; - var spacing = info ? info.spacing : 3; - var mesh_buffer = this.mesh.vertexBuffers[i]; - - if(!mesh_buffer || mesh_buffer.data.length != buffer_data.length) - { - mesh_buffer = new GL.Buffer( i == "indices" ? GL.ELEMENT_ARRAY_BUFFER : GL.ARRAY_BUFFER, buffer_data, spacing, GL.DYNAMIC_DRAW ); - } - else - { - mesh_buffer.data.set( buffer_data ); - mesh_buffer.upload(GL.DYNAMIC_DRAW); - } - - this.mesh.addBuffer( i, mesh_buffer ); - } - - if(this.mesh.vertexBuffers.normals &&this.mesh.vertexBuffers.normals.data.length != this.mesh.vertexBuffers.vertices.data.length ) - { - var n = new Float32Array([0,1,0]); - var normals = new Float32Array( this.mesh.vertexBuffers.vertices.data.length ); - for(var i = 0; i < normals.length; i+= 3) - normals.set( n, i ); - mesh_buffer = new GL.Buffer( GL.ARRAY_BUFFER, normals, 3 ); - this.mesh.addBuffer( "normals", mesh_buffer ); - } - - this.mesh.updateBoundingBox(); - this.geometry_id = this.mesh.id = geometry._id; - this.version = this.mesh.version = geometry._version; - return this.mesh; - } - - LGraphGeometryToMesh.prototype.onExecute = function() { - - var geometry = this.getInputData(0); - if(!geometry) - return; - if( this.version != geometry._version || this.geometry_id != geometry._id ) - this.updateMesh( geometry ); - this.setOutputData(0, this.mesh); - } - - LiteGraph.registerNodeType( "geometry/toMesh", LGraphGeometryToMesh ); - - function LGraphRenderMesh() { - this.addInput("mesh", "mesh"); - this.addInput("mat4", "mat4"); - this.addInput("tex", "texture"); + function LGraphTextureMatte() { + this.addInput("in", "Texture"); + this.addOutput("out", "Texture"); this.properties = { - enabled: true, - primitive: GL.TRIANGLES, - additive: false, - color: [1,1,1], - opacity: 1 - }; - - this.color = vec4.create([1,1,1,1]); - this.model_matrix = mat4.create(); - this.uniforms = { - u_color: this.color, - u_model: this.model_matrix + key_color: vec3.fromValues(0, 1, 0), + threshold: 0.8, + slope: 0.2, + precision: LGraphTexture.DEFAULT }; } - LGraphRenderMesh.title = "Render Mesh"; - LGraphRenderMesh.desc = "renders a mesh flat"; + LGraphTextureMatte.title = "Matte"; + LGraphTextureMatte.desc = "Extracts background"; - LGraphRenderMesh.PRIMITIVE_VALUES = { "points":GL.POINTS, "lines":GL.LINES, "line_loop":GL.LINE_LOOP,"line_strip":GL.LINE_STRIP, "triangles":GL.TRIANGLES, "triangle_fan":GL.TRIANGLE_FAN, "triangle_strip":GL.TRIANGLE_STRIP }; - - LGraphRenderMesh.widgets_info = { - primitive: { widget: "combo", values: LGraphRenderMesh.PRIMITIVE_VALUES }, - color: { widget: "color" } + LGraphTextureMatte.widgets_info = { + key_color: { widget: "color" }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } }; - LGraphRenderMesh.prototype.onExecute = function() { - - if(!this.properties.enabled) + LGraphTextureMatte.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { return; + } //saves work - var mesh = this.getInputData(0); - if(!mesh) - return; + var tex = this.getInputData(0); - if(!LiteGraph.LGraphRender.onRequestCameraMatrices) - { - console.warn("cannot render geometry, LiteGraph.onRequestCameraMatrices is null, remember to fill this with a callback(view_matrix, projection_matrix,viewprojection_matrix) to use 3D rendering from the graph"); + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, tex); return; } - LiteGraph.LGraphRender.onRequestCameraMatrices( view_matrix, projection_matrix,viewprojection_matrix ); - var shader = null; - var texture = this.getInputData(2); - if(texture) - { - shader = gl.shaders["textured"]; - if(!shader) - shader = gl.shaders["textured"] = new GL.Shader( LGraphRenderPoints.vertex_shader_code, LGraphRenderPoints.fragment_shader_code, { USE_TEXTURE:"" }); - } - else - { - shader = gl.shaders["flat"]; - if(!shader) - shader = gl.shaders["flat"] = new GL.Shader( LGraphRenderPoints.vertex_shader_code, LGraphRenderPoints.fragment_shader_code ); + if (!tex) { + return; } - this.color.set( this.properties.color ); - this.color[3] = this.properties.opacity; + this._tex = LGraphTexture.getTargetTexture( + tex, + this._tex, + this.properties.precision + ); - var model_matrix = this.model_matrix; - var m = this.getInputData(1); - if(m) - model_matrix.set(m); - else - mat4.identity( model_matrix ); + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); - this.uniforms.u_point_size = 1; - var primitive = this.properties.primitive; - - shader.uniforms( global_uniforms ); - shader.uniforms( this.uniforms ); - - if(this.properties.opacity >= 1) - gl.disable( gl.BLEND ); - else - gl.enable( gl.BLEND ); - gl.enable( gl.DEPTH_TEST ); - if( this.properties.additive ) - { - gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); - gl.depthMask( false ); + if (!this._uniforms) { + this._uniforms = { + u_texture: 0, + u_key_color: this.properties.key_color, + u_threshold: 1, + u_slope: 1 + }; } - else - gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA ); + var uniforms = this._uniforms; - var indices = "indices"; - if( mesh.indexBuffers.triangles ) - indices = "triangles"; - shader.draw( mesh, primitive, indices ); - gl.disable( gl.BLEND ); - gl.depthMask( true ); - } + var mesh = Mesh.getScreenQuad(); + var shader = LGraphTextureMatte._shader; + if (!shader) { + shader = LGraphTextureMatte._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureMatte.pixel_shader + ); + } - LiteGraph.registerNodeType( "geometry/render_mesh", LGraphRenderMesh ); + uniforms.u_key_color = this.properties.key_color; + uniforms.u_threshold = this.properties.threshold; + uniforms.u_slope = this.properties.slope; - //************************** + this._tex.drawTo(function() { + tex.bind(0); + shader.uniforms(uniforms).draw(mesh); + }); - - function LGraphGeometryPrimitive() { - this.addInput("size", "number"); - this.addOutput("out", "mesh"); - this.properties = { type: 1, size: 1, subdivisions: 32 }; - - this.version = (Math.random() * 100000)|0; - this.last_info = { type: -1, size: -1, subdivisions: -1 }; - } - - LGraphGeometryPrimitive.title = "Primitive"; - - LGraphGeometryPrimitive.VALID = { "CUBE":1, "PLANE":2, "CYLINDER":3, "SPHERE":4, "CIRCLE":5, "HEMISPHERE":6, "ICOSAHEDRON":7, "CONE":8, "QUAD":9 }; - LGraphGeometryPrimitive.widgets_info = { - type: { widget: "combo", values: LGraphGeometryPrimitive.VALID } + this.setOutputData(0, this._tex); }; - LGraphGeometryPrimitive.prototype.onExecute = function() { - - if( !this.isOutputConnected(0) ) - return; - - var size = this.getInputOrProperty("size"); - - //update - if( this.last_info.type != this.properties.type || this.last_info.size != size || this.last_info.subdivisions != this.properties.subdivisions ) - this.updateMesh( this.properties.type, size, this.properties.subdivisions ); - - this.setOutputData(0,this._mesh); - } - - LGraphGeometryPrimitive.prototype.updateMesh = function(type, size, subdivisions) - { - subdivisions = Math.max(0,subdivisions)|0; - - switch (type) - { - case 1: //CUBE: - this._mesh = GL.Mesh.cube({size: size, normals:true,coords:true}); - break; - case 2: //PLANE: - this._mesh = GL.Mesh.plane({size: size, xz: true, detail: subdivisions, normals:true,coords:true}); - break; - case 3: //CYLINDER: - this._mesh = GL.Mesh.cylinder({size: size, subdivisions: subdivisions, normals:true,coords:true}); - break; - case 4: //SPHERE: - this._mesh = GL.Mesh.sphere({size: size, "long": subdivisions, lat: subdivisions, normals:true,coords:true}); - break; - case 5: //CIRCLE: - this._mesh = GL.Mesh.circle({size: size, slices: subdivisions, normals:true, coords:true}); - break; - case 6: //HEMISPHERE: - this._mesh = GL.Mesh.sphere({size: size, "long": subdivisions, lat: subdivisions, normals:true, coords:true, hemi: true}); - break; - case 7: //ICOSAHEDRON: - this._mesh = GL.Mesh.icosahedron({size: size, subdivisions:subdivisions }); - break; - case 8: //CONE: - this._mesh = GL.Mesh.cone({radius: size, height: size, subdivisions:subdivisions }); - break; - case 9: //QUAD: - this._mesh = GL.Mesh.plane({size: size, xz: false, detail: subdivisions, normals:true, coords:true }); - break; - } - - this.last_info.type = type; - this.last_info.size = size; - this.last_info.subdivisions = subdivisions; - this._mesh.version = this.version++; - } - - LiteGraph.registerNodeType( "geometry/mesh_primitive", LGraphGeometryPrimitive ); - - - function LGraphRenderPoints() { - this.addInput("in", "geometry"); - this.addInput("mat4", "mat4"); - this.addInput("tex", "texture"); - this.properties = { - enabled: true, - point_size: 0.1, - fixed_size: false, - additive: true, - color: [1,1,1], - opacity: 1 - }; - - this.color = vec4.create([1,1,1,1]); - - this.uniforms = { - u_point_size: 1, - u_perspective: 1, - u_point_perspective: 1, - u_color: this.color - }; - - this.geometry_id = -1; - this.version = -1; - this.mesh = null; - } - - LGraphRenderPoints.title = "renderPoints"; - LGraphRenderPoints.desc = "render points with a texture"; - - LGraphRenderPoints.widgets_info = { - color: { widget: "color" } - }; - - LGraphRenderPoints.prototype.updateMesh = function(geometry) - { - var buffer = this.buffer; - if(!this.buffer || !this.buffer.data || this.buffer.data.length != geometry.vertices.length) - this.buffer = new GL.Buffer( GL.ARRAY_BUFFER, geometry.vertices,3,GL.DYNAMIC_DRAW); - else - { - this.buffer.data.set( geometry.vertices ); - this.buffer.upload(GL.DYNAMIC_DRAW); - } - - if(!this.mesh) - this.mesh = new GL.Mesh(); - - this.mesh.addBuffer("vertices",this.buffer); - this.geometry_id = this.mesh.id = geometry._id; - this.version = this.mesh.version = geometry._version; - } - - LGraphRenderPoints.prototype.onExecute = function() { - - if(!this.properties.enabled) - return; - - var geometry = this.getInputData(0); - if(!geometry) - return; - if(this.version != geometry._version || this.geometry_id != geometry._id ) - this.updateMesh( geometry ); - - if(!LiteGraph.LGraphRender.onRequestCameraMatrices) - { - console.warn("cannot render geometry, LiteGraph.onRequestCameraMatrices is null, remember to fill this with a callback(view_matrix, projection_matrix,viewprojection_matrix) to use 3D rendering from the graph"); - return; - } - - LiteGraph.LGraphRender.onRequestCameraMatrices( view_matrix, projection_matrix,viewprojection_matrix ); - var shader = null; - - var texture = this.getInputData(2); - - if(texture) - { - shader = gl.shaders["textured_points"]; - if(!shader) - shader = gl.shaders["textured_points"] = new GL.Shader( LGraphRenderPoints.vertex_shader_code, LGraphRenderPoints.fragment_shader_code, { USE_TEXTURED_POINTS:"" }); - } - else - { - shader = gl.shaders["points"]; - if(!shader) - shader = gl.shaders["points"] = new GL.Shader( LGraphRenderPoints.vertex_shader_code, LGraphRenderPoints.fragment_shader_code, { USE_POINTS: "" }); - } - - this.color.set( this.properties.color ); - this.color[3] = this.properties.opacity; - - var m = this.getInputData(1); - if(m) - model_matrix.set(m); - else - mat4.identity( model_matrix ); - - this.uniforms.u_point_size = this.properties.point_size; - this.uniforms.u_point_perspective = this.properties.fixed_size ? 0 : 1; - this.uniforms.u_perspective = gl.viewport_data[3] * projection_matrix[5]; - - shader.uniforms( global_uniforms ); - shader.uniforms( this.uniforms ); - - if(this.properties.opacity >= 1) - gl.disable( gl.BLEND ); - else - gl.enable( gl.BLEND ); - - gl.enable( gl.DEPTH_TEST ); - if( this.properties.additive ) - { - gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); - gl.depthMask( false ); - } - else - gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA ); - - shader.draw( this.mesh, GL.POINTS ); - - gl.disable( gl.BLEND ); - gl.depthMask( true ); - } - - LiteGraph.registerNodeType( "geometry/render_points", LGraphRenderPoints ); - - LGraphRenderPoints.vertex_shader_code = '\ - precision mediump float;\n\ - attribute vec3 a_vertex;\n\ - varying vec3 v_vertex;\n\ - attribute vec3 a_normal;\n\ - varying vec3 v_normal;\n\ - #ifdef USE_COLOR\n\ - attribute vec4 a_color;\n\ - varying vec4 v_color;\n\ - #endif\n\ - attribute vec2 a_coord;\n\ - varying vec2 v_coord;\n\ - #ifdef USE_SIZE\n\ - attribute float a_extra;\n\ - #endif\n\ - #ifdef USE_INSTANCING\n\ - attribute mat4 u_model;\n\ - #else\n\ - uniform mat4 u_model;\n\ - #endif\n\ - uniform mat4 u_viewprojection;\n\ - uniform float u_point_size;\n\ - uniform float u_perspective;\n\ - uniform float u_point_perspective;\n\ - float computePointSize(float radius, float w)\n\ - {\n\ - if(radius < 0.0)\n\ - return -radius;\n\ - return u_perspective * radius / w;\n\ - }\n\ - void main() {\n\ - v_coord = a_coord;\n\ - #ifdef USE_COLOR\n\ - v_color = a_color;\n\ - #endif\n\ - v_vertex = ( u_model * vec4( a_vertex, 1.0 )).xyz;\n\ - v_normal = ( u_model * vec4( a_normal, 0.0 )).xyz;\n\ - gl_Position = u_viewprojection * vec4(v_vertex,1.0);\n\ - gl_PointSize = u_point_size;\n\ - #ifdef USE_SIZE\n\ - gl_PointSize = a_extra;\n\ - #endif\n\ - if(u_point_perspective != 0.0)\n\ - gl_PointSize = computePointSize( gl_PointSize, gl_Position.w );\n\ - }\ - '; - - LGraphRenderPoints.fragment_shader_code = '\ - precision mediump float;\n\ - uniform vec4 u_color;\n\ - #ifdef USE_COLOR\n\ - varying vec4 v_color;\n\ - #endif\n\ + LGraphTextureMatte.pixel_shader = + "precision highp float;\n\ varying vec2 v_coord;\n\ uniform sampler2D u_texture;\n\ + uniform vec3 u_key_color;\n\ + uniform float u_threshold;\n\ + uniform float u_slope;\n\ + \n\ void main() {\n\ - vec4 color = u_color;\n\ - #ifdef USE_TEXTURED_POINTS\n\ - color *= texture2D(u_texture, gl_PointCoord.xy);\n\ - #else\n\ - #ifdef USE_TEXTURE\n\ - color *= texture2D(u_texture, v_coord);\n\ - if(color.a < 0.1)\n\ - discard;\n\ - #endif\n\ - #ifdef USE_POINTS\n\ - float dist = length( gl_PointCoord.xy - vec2(0.5) );\n\ - if( dist > 0.45 )\n\ - discard;\n\ - #endif\n\ - #endif\n\ - #ifdef USE_COLOR\n\ - color *= v_color;\n\ - #endif\n\ - gl_FragColor = color;\n\ - }\ - '; + vec3 color = texture2D( u_texture, v_coord ).xyz;\n\ + float diff = length( normalize(color) - normalize(u_key_color) );\n\ + float edge = u_threshold * (1.0 - u_slope);\n\ + float alpha = smoothstep( edge, u_threshold, diff);\n\ + gl_FragColor = vec4( color, alpha );\n\ + }"; - //based on https://inconvergent.net/2019/depth-of-field/ - /* - function LGraphRenderGeometryDOF() { - this.addInput("in", "geometry"); - this.addInput("mat4", "mat4"); - this.addInput("tex", "texture"); - this.properties = { - enabled: true, - lines: true, - point_size: 0.1, - fixed_size: false, - additive: true, - color: [1,1,1], - opacity: 1 - }; + LiteGraph.registerNodeType("texture/matte", LGraphTextureMatte); - this.color = vec4.create([1,1,1,1]); - - this.uniforms = { - u_point_size: 1, - u_perspective: 1, - u_point_perspective: 1, - u_color: this.color - }; - - this.geometry_id = -1; - this.version = -1; - this.mesh = null; + //*********************************** + function LGraphCubemapToTexture2D() { + this.addInput("in", "texture"); + this.addInput("yaw", "number"); + this.addOutput("out", "texture"); + this.properties = { yaw: 0 }; } - LGraphRenderGeometryDOF.widgets_info = { - color: { widget: "color" } + LGraphCubemapToTexture2D.title = "CubemapToTexture2D"; + LGraphCubemapToTexture2D.desc = "Transforms a CUBEMAP texture into a TEXTURE2D in Polar Representation"; + + LGraphCubemapToTexture2D.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) + return; + + var tex = this.getInputData(0); + if ( !tex || tex.texture_type != GL.TEXTURE_CUBE_MAP ) + return; + if( this._last_tex && ( this._last_tex.height != tex.height || this._last_tex.type != tex.type )) + this._last_tex = null; + var yaw = this.getInputOrProperty("yaw"); + this._last_tex = GL.Texture.cubemapToTexture2D( tex, tex.height, this._last_tex, true, yaw ); + this.setOutputData( 0, this._last_tex ); }; - LGraphRenderGeometryDOF.prototype.updateMesh = function(geometry) - { - var buffer = this.buffer; - if(!this.buffer || this.buffer.data.length != geometry.vertices.length) - this.buffer = new GL.Buffer( GL.ARRAY_BUFFER, geometry.vertices,3,GL.DYNAMIC_DRAW); - else - { - this.buffer.data.set( geometry.vertices ); - this.buffer.upload(GL.DYNAMIC_DRAW); - } + LiteGraph.registerNodeType( "texture/cubemapToTexture2D", LGraphCubemapToTexture2D ); +})(this); - if(!this.mesh) - this.mesh = new GL.Mesh(); - - this.mesh.addBuffer("vertices",this.buffer); - this.geometry_id = this.mesh.id = geometry._id; - this.version = this.mesh.version = geometry._version; - } - - LGraphRenderGeometryDOF.prototype.onExecute = function() { - - if(!this.properties.enabled) - return; - - var geometry = this.getInputData(0); - if(!geometry) - return; - if(this.version != geometry._version || this.geometry_id != geometry._id ) - this.updateMesh( geometry ); - - if(!LiteGraph.LGraphRender.onRequestCameraMatrices) - { - console.warn("cannot render geometry, LiteGraph.onRequestCameraMatrices is null, remember to fill this with a callback(view_matrix, projection_matrix,viewprojection_matrix) to use 3D rendering from the graph"); - return; - } - - LiteGraph.LGraphRender.onRequestCameraMatrices( view_matrix, projection_matrix,viewprojection_matrix ); - var shader = null; - - var texture = this.getInputData(2); - - if(texture) - { - shader = gl.shaders["textured_points"]; - if(!shader) - shader = gl.shaders["textured_points"] = new GL.Shader( LGraphRenderGeometryDOF.vertex_shader_code, LGraphRenderGeometryDOF.fragment_shader_code, { USE_TEXTURED_POINTS:"" }); - } - else - { - shader = gl.shaders["points"]; - if(!shader) - shader = gl.shaders["points"] = new GL.Shader( LGraphRenderGeometryDOF.vertex_shader_code, LGraphRenderGeometryDOF.fragment_shader_code, { USE_POINTS: "" }); - } - - this.color.set( this.properties.color ); - this.color[3] = this.properties.opacity; - - var m = this.getInputData(1); - if(m) - model_matrix.set(m); - else - mat4.identity( model_matrix ); - - this.uniforms.u_point_size = this.properties.point_size; - this.uniforms.u_point_perspective = this.properties.fixed_size ? 0 : 1; - this.uniforms.u_perspective = gl.viewport_data[3] * projection_matrix[5]; - - shader.uniforms( global_uniforms ); - shader.uniforms( this.uniforms ); - - if(this.properties.opacity >= 1) - gl.disable( gl.BLEND ); - else - gl.enable( gl.BLEND ); - - gl.enable( gl.DEPTH_TEST ); - if( this.properties.additive ) - { - gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); - gl.depthMask( false ); - } - else - gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA ); - - shader.draw( this.mesh, GL.POINTS ); - - gl.disable( gl.BLEND ); - gl.depthMask( true ); - } - - LiteGraph.registerNodeType( "geometry/render_dof", LGraphRenderGeometryDOF ); - - LGraphRenderGeometryDOF.vertex_shader_code = '\ - precision mediump float;\n\ - attribute vec3 a_vertex;\n\ - varying vec3 v_vertex;\n\ - attribute vec3 a_normal;\n\ - varying vec3 v_normal;\n\ - #ifdef USE_COLOR\n\ - attribute vec4 a_color;\n\ - varying vec4 v_color;\n\ - #endif\n\ - attribute vec2 a_coord;\n\ - varying vec2 v_coord;\n\ - #ifdef USE_SIZE\n\ - attribute float a_extra;\n\ - #endif\n\ - #ifdef USE_INSTANCING\n\ - attribute mat4 u_model;\n\ - #else\n\ - uniform mat4 u_model;\n\ - #endif\n\ - uniform mat4 u_viewprojection;\n\ - uniform float u_point_size;\n\ - uniform float u_perspective;\n\ - uniform float u_point_perspective;\n\ - float computePointSize(float radius, float w)\n\ - {\n\ - if(radius < 0.0)\n\ - return -radius;\n\ - return u_perspective * radius / w;\n\ - }\n\ - void main() {\n\ - v_coord = a_coord;\n\ - #ifdef USE_COLOR\n\ - v_color = a_color;\n\ - #endif\n\ - v_vertex = ( u_model * vec4( a_vertex, 1.0 )).xyz;\n\ - v_normal = ( u_model * vec4( a_normal, 0.0 )).xyz;\n\ - gl_Position = u_viewprojection * vec4(v_vertex,1.0);\n\ - gl_PointSize = u_point_size;\n\ - #ifdef USE_SIZE\n\ - gl_PointSize = a_extra;\n\ - #endif\n\ - if(u_point_perspective != 0.0)\n\ - gl_PointSize = computePointSize( gl_PointSize, gl_Position.w );\n\ - }\ - '; - - LGraphRenderGeometryDOF.fragment_shader_code = '\ - precision mediump float;\n\ - uniform vec4 u_color;\n\ - #ifdef USE_COLOR\n\ - varying vec4 v_color;\n\ - #endif\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - void main() {\n\ - vec4 color = u_color;\n\ - #ifdef USE_TEXTURED_POINTS\n\ - color *= texture2D(u_texture, gl_PointCoord.xy);\n\ - #else\n\ - #ifdef USE_TEXTURE\n\ - color *= texture2D(u_texture, v_coord);\n\ - if(color.a < 0.1)\n\ - discard;\n\ - #endif\n\ - #ifdef USE_POINTS\n\ - float dist = length( gl_PointCoord.xy - vec2(0.5) );\n\ - if( dist > 0.45 )\n\ - discard;\n\ - #endif\n\ - #endif\n\ - #ifdef USE_COLOR\n\ - color *= v_color;\n\ - #endif\n\ - gl_FragColor = color;\n\ - }\ - '; - */ - - - -})(this); (function(global) { var LiteGraph = global.LiteGraph; @@ -24487,7 +39449,7 @@ function LGraphGeometryDisplace() { global.LGraphFXVigneting = LGraphFXVigneting; } })(this); - + (function(global) { var LiteGraph = global.LiteGraph; var MIDI_COLOR = "#243"; @@ -26071,7 +41033,7 @@ function LGraphGeometryDisplace() { return window.performance.now(); } })(this); - + (function(global) { var LiteGraph = global.LiteGraph; @@ -27528,7 +42490,7 @@ LiteGraph.registerNodeType("audio/waveShaper", LGAudioWaveShaper); LGAudioDestination.desc = "Audio output"; LiteGraph.registerNodeType("audio/destination", LGAudioDestination); })(this); - + //event related nodes (function(global) { var LiteGraph = global.LiteGraph; @@ -27894,4 +42856,3 @@ LiteGraph.registerNodeType("audio/waveShaper", LGAudioWaveShaper); LiteGraph.registerNodeType("network/sillyclient", LGSillyClient); })(this); - diff --git a/build/litegraph.min.js b/build/litegraph.min.js index b0d422061..7a0258a22 100755 --- a/build/litegraph.min.js +++ b/build/litegraph.min.js @@ -1,3 +1,4 @@ +<<<<<<< HEAD (function(B){function c(a){h.debug&&console.log("Graph created");this.list_of_graphcanvas=null;this.clear();a&&this.configure(a)}function m(a,b,d,g,f,r){this.id=a;this.type=b;this.origin_id=d;this.origin_slot=g;this.target_id=f;this.target_slot=r;this._data=null;this._pos=new Float32Array(2)}function n(a){this._ctor(a)}function l(a){this._ctor(a)}function x(a,b){this.offset=new Float32Array([0,0]);this.scale=1;this.max_scale=10;this.min_scale=0.1;this.onredraw=null;this.enabled=!0;this.last_mouse= [0,0];this.element=null;this.visible_area=new Float32Array(4);a&&(this.element=a,b||this.bindEvents(a))}function e(a,b,d){d=d||{};this.background_image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; a&&a.constructor===String&&(a=document.querySelector(a));this.ds=new x;this.zoom_modify_alpha=!0;this.title_text_font=""+h.NODE_TEXT_SIZE+"px Arial";this.inner_text_font="normal "+h.NODE_SUBTEXT_SIZE+"px Arial";this.node_title_color=h.NODE_TITLE_COLOR;this.default_link_color=h.LINK_COLOR;this.default_connection_color={input_off:"#778",input_on:"#7F7",output_off:"#778",output_on:"#7F7"};this.highquality_render=!0;this.use_gradients=!1;this.editor_alpha=1;this.pause_rendering=!1;this.clear_background= @@ -716,3 +717,10340 @@ e&&(w=!1);if(w)continue}this._server.sendMessage({type:0,channel:m,data:e});if(e function(){var c=this;if("undefined"==typeof SillyClient)this._error||console.error("SillyClient node cannot be used, you must include SillyServer.js"),this._error=!0;else if(this._server=new SillyClient,this._server.on_ready=function(){console.log("ready");c.boxcolor="#6C6"},this._server.on_message=function(e,m){var w=null;try{w=JSON.parse(m)}catch(u){return}if(1==w.type)if(w.data.object_class&&n[w.data.object_class]){var x=null;try{x=new n[w.data.object_class](w.data),c.triggerSlot(0,x)}catch(A){return}}else c.triggerSlot(0, w.data);else c._last_received_data[w.channel||0]=w.data;c.boxcolor="#AFA"},this._server.on_error=function(e){console.log("couldnt connect to websocket");c.boxcolor="#E88"},this._server.on_close=function(e){console.log("connection closed");c.boxcolor="#000"},this.properties.url&&this.properties.room){try{this._server.connect(this.properties.url,this.properties.room)}catch(m){console.error("SillyServer error: "+m);this._server=null;return}this._final_url=this.properties.url+"/"+this.properties.room}}; m.prototype.send=function(c){this._server&&this._server.is_connected&&this._server.sendMessage({type:1,data:c})};m.prototype.onAction=function(c,m){this._server&&this._server.is_connected&&this._server.sendMessage({type:1,action:c,data:m})};m.prototype.onGetInputs=function(){return[["in",0]]};m.prototype.onGetOutputs=function(){return[["out",0]]};n.registerNodeType("network/sillyclient",m)})(this); +======= +var $jscomp = $jscomp || {}; +$jscomp.scope = {}; +$jscomp.ASSUME_ES5 = !1; +$jscomp.ASSUME_NO_NATIVE_MAP = !1; +$jscomp.ASSUME_NO_NATIVE_SET = !1; +$jscomp.defineProperty = $jscomp.ASSUME_ES5 || "function" == typeof Object.defineProperties ? Object.defineProperty : function(w, c, p) { + w != Array.prototype && w != Object.prototype && (w[c] = p.value); +}; +$jscomp.getGlobal = function(w) { + return "undefined" != typeof window && window === w ? w : "undefined" != typeof global && null != global ? global : w; +}; +$jscomp.global = $jscomp.getGlobal(this); +$jscomp.polyfill = function(w, c, p, m) { + if (c) { + p = $jscomp.global; + w = w.split("."); + for (m = 0; m < w.length - 1; m++) { + var g = w[m]; + g in p || (p[g] = {}); + p = p[g]; + } + w = w[w.length - 1]; + m = p[w]; + c = c(m); + c != m && null != c && $jscomp.defineProperty(p, w, {configurable:!0, writable:!0, value:c}); + } +}; +$jscomp.polyfill("Array.prototype.fill", function(w) { + return w ? w : function(c, p, m) { + var g = this.length || 0; + 0 > p && (p = Math.max(0, g + p)); + if (null == m || m > g) { + m = g; + } + m = Number(m); + 0 > m && (m = Math.max(0, g + m)); + for (p = Number(p || 0); p < m; p++) { + this[p] = c; + } + return this; + }; +}, "es6", "es3"); +$jscomp.SYMBOL_PREFIX = "jscomp_symbol_"; +$jscomp.initSymbol = function() { + $jscomp.initSymbol = function() { + }; + $jscomp.global.Symbol || ($jscomp.global.Symbol = $jscomp.Symbol); +}; +$jscomp.Symbol = function() { + var w = 0; + return function(c) { + return $jscomp.SYMBOL_PREFIX + (c || "") + w++; + }; +}(); +$jscomp.initSymbolIterator = function() { + $jscomp.initSymbol(); + var w = $jscomp.global.Symbol.iterator; + w || (w = $jscomp.global.Symbol.iterator = $jscomp.global.Symbol("iterator")); + "function" != typeof Array.prototype[w] && $jscomp.defineProperty(Array.prototype, w, {configurable:!0, writable:!0, value:function() { + return $jscomp.arrayIterator(this); + }}); + $jscomp.initSymbolIterator = function() { + }; +}; +$jscomp.arrayIterator = function(w) { + var c = 0; + return $jscomp.iteratorPrototype(function() { + return c < w.length ? {done:!1, value:w[c++]} : {done:!0}; + }); +}; +$jscomp.iteratorPrototype = function(w) { + $jscomp.initSymbolIterator(); + w = {next:w}; + w[$jscomp.global.Symbol.iterator] = function() { + return this; + }; + return w; +}; +$jscomp.iteratorFromArray = function(w, c) { + $jscomp.initSymbolIterator(); + w instanceof String && (w += ""); + var p = 0, m = {next:function() { + if (p < w.length) { + var g = p++; + return {value:c(g, w[g]), done:!1}; + } + m.next = function() { + return {done:!0, value:void 0}; + }; + return m.next(); + }}; + m[Symbol.iterator] = function() { + return m; + }; + return m; +}; +$jscomp.polyfill("Array.prototype.values", function(w) { + return w ? w : function() { + return $jscomp.iteratorFromArray(this, function(c, p) { + return p; + }); + }; +}, "es8", "es3"); +$jscomp.polyfill("Array.prototype.keys", function(w) { + return w ? w : function() { + return $jscomp.iteratorFromArray(this, function(c) { + return c; + }); + }; +}, "es6", "es3"); +$jscomp.owns = function(w, c) { + return Object.prototype.hasOwnProperty.call(w, c); +}; +$jscomp.polyfill("Object.values", function(w) { + return w ? w : function(c) { + var p = [], m; + for (m in c) { + $jscomp.owns(c, m) && p.push(c[m]); + } + return p; + }; +}, "es8", "es3"); +(function(w) { + function c(a) { + e.debug && console.log("Graph created"); + this.list_of_graphcanvas = null; + this.clear(); + a && this.configure(a); + } + function p(a, b, d, h, f, e) { + this.id = a; + this.type = b; + this.origin_id = d; + this.origin_slot = h; + this.target_id = f; + this.target_slot = e; + this._data = null; + this._pos = new Float32Array(2); + } + function m(a) { + this._ctor(a); + } + function g(a) { + this._ctor(a); + } + function u(a, b) { + this.offset = new Float32Array([0, 0]); + this.scale = 1; + this.max_scale = 10; + this.min_scale = 0.1; + this.onredraw = null; + this.enabled = !0; + this.last_mouse = [0, 0]; + this.element = null; + this.visible_area = new Float32Array(4); + a && (this.element = a, b || this.bindEvents(a)); + } + function l(a, b, d) { + d = d || {}; + this.background_image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; + a && a.constructor === String && (a = document.querySelector(a)); + this.ds = new u; + this.zoom_modify_alpha = !0; + this.title_text_font = "" + e.NODE_TEXT_SIZE + "px Arial"; + this.inner_text_font = "normal " + e.NODE_SUBTEXT_SIZE + "px Arial"; + this.node_title_color = e.NODE_TITLE_COLOR; + this.default_link_color = e.LINK_COLOR; + this.default_connection_color = {input_off:"#778", input_on:"#7F7", output_off:"#778", output_on:"#7F7"}; + this.highquality_render = !0; + this.use_gradients = !1; + this.editor_alpha = 1; + this.pause_rendering = !1; + this.clear_background = !0; + this.read_only = !1; + this.render_only_selected = !0; + this.live_mode = !1; + this.allow_searchbox = this.allow_interaction = this.allow_dragnodes = this.allow_dragcanvas = this.show_info = !0; + this.drag_mode = this.allow_reconnect_links = !1; + this.filter = this.dragging_rectangle = null; + this.always_render_background = !1; + this.render_canvas_border = this.render_shadows = !0; + this.render_connections_shadows = !1; + this.render_connections_border = !0; + this.render_connection_arrows = this.render_curved_connections = !1; + this.render_collapsed_slots = !0; + this.render_execution_order = !1; + this.render_link_tooltip = this.render_title_colored = !0; + this.links_render_mode = e.SPLINE_LINK; + this.canvas_mouse = [0, 0]; + this.onSelectionChange = this.onNodeMoved = this.onDrawLinkTooltip = this.onDrawOverlay = this.onDrawForeground = this.onDrawBackground = this.onMouse = this.onSearchBoxSelection = this.onSearchBox = null; + this.connections_width = 3; + this.round_radius = 8; + this.over_link_center = this.node_widget = this.current_node = null; + this.last_mouse_position = [0, 0]; + this.visible_area = this.ds.visible_area; + this.visible_links = []; + b && b.attachCanvas(this); + this.setCanvas(a); + this.clear(); + d.skip_render || this.startRendering(); + this.autoresize = d.autoresize; + } + function B(a, b) { + return Math.sqrt((b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1])); + } + function y(a, b, d, h, f, e) { + return d < a && d + f > a && h < b && h + e > b ? !0 : !1; + } + function v(a, b) { + var d = a[0] + a[2], h = a[1] + a[3], f = b[1] + b[3]; + return a[0] > b[0] + b[2] || a[1] > f || d < b[0] || h < b[1] ? !1 : !0; + } + function E(a, b) { + function d(a) { + var d = parseInt(e.style.top); + e.style.top = (d + a.deltaY * b.scroll_speed).toFixed() + "px"; + a.preventDefault(); + return !0; + } + this.options = b = b || {}; + var h = this; + b.parentMenu && (b.parentMenu.constructor !== this.constructor ? (console.error("parentMenu must be of class ContextMenu, ignoring it"), b.parentMenu = null) : (this.parentMenu = b.parentMenu, this.parentMenu.lock = !0, this.parentMenu.current_submenu = this)); + var f = null; + b.event && (f = b.event.constructor.name); + "MouseEvent" !== f && "CustomEvent" !== f && "PointerEvent" !== f && (console.error("Event passed to ContextMenu is not of type MouseEvent or CustomEvent. Ignoring it."), b.event = null); + var e = document.createElement("div"); + e.className = "litegraph litecontextmenu litemenubar-panel"; + b.className && (e.className += " " + b.className); + e.style.minWidth = 100; + e.style.minHeight = 100; + e.style.pointerEvents = "none"; + setTimeout(function() { + e.style.pointerEvents = "auto"; + }, 100); + e.addEventListener("mouseup", function(a) { + a.preventDefault(); + return !0; + }, !0); + e.addEventListener("contextmenu", function(a) { + if (2 != a.button) { + return !1; + } + a.preventDefault(); + return !1; + }, !0); + e.addEventListener("mousedown", function(a) { + if (2 == a.button) { + return h.close(), a.preventDefault(), !0; + } + }, !0); + b.scroll_speed || (b.scroll_speed = 0.1); + e.addEventListener("wheel", d, !0); + e.addEventListener("mousewheel", d, !0); + this.root = e; + b.title && (f = document.createElement("div"), f.className = "litemenu-title", f.innerHTML = b.title, e.appendChild(f)); + f = 0; + for (var c in a) { + var k = a.constructor == Array ? a[c] : c; + null != k && k.constructor !== String && (k = void 0 === k.content ? String(k) : k.content); + this.addItem(k, a[c], b); + f++; + } + e.addEventListener("mouseleave", function(a) { + h.lock || (e.closing_timer && clearTimeout(e.closing_timer), e.closing_timer = setTimeout(h.close.bind(h, a), 500)); + }); + e.addEventListener("mouseenter", function(a) { + e.closing_timer && clearTimeout(e.closing_timer); + }); + a = document; + b.event && (a = b.event.target.ownerDocument); + a || (a = document); + a.fullscreenElement ? a.fullscreenElement.appendChild(e) : a.body.appendChild(e); + c = b.left || 0; + a = b.top || 0; + b.event && (c = b.event.clientX - 10, a = b.event.clientY - 10, b.title && (a -= 20), b.parentMenu && (c = b.parentMenu.root.getBoundingClientRect(), c = c.left + c.width), f = document.body.getBoundingClientRect(), k = e.getBoundingClientRect(), c > f.width - k.width - 10 && (c = f.width - k.width - 10), a > f.height - k.height - 10 && (a = f.height - k.height - 10)); + e.style.left = c + "px"; + e.style.top = a + "px"; + b.scale && (e.style.transform = "scale(" + b.scale + ")"); + } + function z(a) { + this.points = a; + this.nearest = this.selected = -1; + this.size = null; + this.must_update = !0; + this.margin = 5; + } + var e = w.LiteGraph = {VERSION:0.4, CANVAS_GRID_SIZE:10, NODE_TITLE_HEIGHT:30, NODE_TITLE_TEXT_Y:20, NODE_SLOT_HEIGHT:20, NODE_WIDGET_HEIGHT:20, NODE_WIDTH:140, NODE_MIN_WIDTH:50, NODE_COLLAPSED_RADIUS:10, NODE_COLLAPSED_WIDTH:80, NODE_TITLE_COLOR:"#999", NODE_TEXT_SIZE:14, NODE_TEXT_COLOR:"#AAA", NODE_SUBTEXT_SIZE:12, NODE_DEFAULT_COLOR:"#333", NODE_DEFAULT_BGCOLOR:"#353535", NODE_DEFAULT_BOXCOLOR:"#666", NODE_DEFAULT_SHAPE:"box", DEFAULT_SHADOW_COLOR:"rgba(0,0,0,0.5)", DEFAULT_GROUP_FONT:24, + WIDGET_BGCOLOR:"#222", WIDGET_OUTLINE_COLOR:"#666", WIDGET_TEXT_COLOR:"#DDD", WIDGET_SECONDARY_TEXT_COLOR:"#999", LINK_COLOR:"#9A9", EVENT_LINK_COLOR:"#A86", CONNECTING_LINK_COLOR:"#AFA", MAX_NUMBER_OF_NODES:1000, DEFAULT_POSITION:[100, 100], VALID_SHAPES:["default", "box", "round", "card"], BOX_SHAPE:1, ROUND_SHAPE:2, CIRCLE_SHAPE:3, CARD_SHAPE:4, ARROW_SHAPE:5, INPUT:1, OUTPUT:2, EVENT:-1, ACTION:-1, ALWAYS:0, ON_EVENT:1, NEVER:2, ON_TRIGGER:3, UP:1, DOWN:2, LEFT:3, RIGHT:4, CENTER:5, STRAIGHT_LINK:0, + LINEAR_LINK:1, SPLINE_LINK:2, NORMAL_TITLE:0, NO_TITLE:1, TRANSPARENT_TITLE:2, AUTOHIDE_TITLE:3, proxy:null, node_images_path:"", debug:!1, catch_exceptions:!0, throw_errors:!0, allow_scripts:!1, registered_node_types:{}, node_types_by_file_extension:{}, Nodes:{}, searchbox_extras:{}, registerNodeType:function(a, b) { + if (!b.prototype) { + throw "Cannot register a simple object, it must be a class with a prototype"; + } + b.type = a; + e.debug && console.log("Node registered: " + a); + a.split("/"); + var d = b.name, h = a.lastIndexOf("/"); + b.category = a.substr(0, h); + b.title || (b.title = d); + if (b.prototype) { + for (var f in m.prototype) { + b.prototype[f] || (b.prototype[f] = m.prototype[f]); + } + } + if (h = this.registered_node_types[a]) { + console.log("replacing node type: " + a); + } else { + if (Object.hasOwnProperty(b.prototype, "shape") || Object.defineProperty(b.prototype, "shape", {set:function(a) { + switch(a) { + case "default": + delete this._shape; + break; + case "box": + this._shape = e.BOX_SHAPE; + break; + case "round": + this._shape = e.ROUND_SHAPE; + break; + case "circle": + this._shape = e.CIRCLE_SHAPE; + break; + case "card": + this._shape = e.CARD_SHAPE; + break; + default: + this._shape = a; + } + }, get:function(a) { + return this._shape; + }, enumerable:!0, configurable:!0}), b.prototype.onPropertyChange && console.warn("LiteGraph node class " + a + " has onPropertyChange method, it must be called onPropertyChanged with d at the end"), b.supported_extensions) { + for (f in b.supported_extensions) { + var x = b.supported_extensions[f]; + x && x.constructor === String && (this.node_types_by_file_extension[x.toLowerCase()] = b); + } + } + } + this.registered_node_types[a] = b; + b.constructor.name && (this.Nodes[d] = b); + if (e.onNodeTypeRegistered) { + e.onNodeTypeRegistered(a, b); + } + if (h && e.onNodeTypeReplaced) { + e.onNodeTypeReplaced(a, b, h); + } + }, unregisterNodeType:function(a) { + var b = a.constructor === String ? this.registered_node_types[a] : a; + if (!b) { + throw "node type not found: " + a; + } + delete this.registered_node_types[b.type]; + b.constructor.name && delete this.Nodes[b.constructor.name]; + }, wrapFunctionAsNode:function(a, b, d, h, f) { + for (var x = Array(b.length), c = "", k = e.getParameterNames(b), n = 0; n < k.length; ++n) { + c += "this.addInput('" + k[n] + "'," + (d && d[n] ? "'" + d[n] + "'" : "0") + ");\n"; + } + c += "this.addOutput('out'," + (h ? "'" + h + "'" : 0) + ");\n"; + f && (c += "this.properties = " + JSON.stringify(f) + ";\n"); + d = Function(c); + d.title = a.split("/").pop(); + d.desc = "Generated from " + b.name; + d.prototype.onExecute = function() { + for (var a = 0; a < x.length; ++a) { + x[a] = this.getInputData(a); + } + a = b.apply(this, x); + this.setOutputData(0, a); + }; + this.registerNodeType(a, d); + }, addNodeMethod:function(a, b) { + m.prototype[a] = b; + for (var d in this.registered_node_types) { + var h = this.registered_node_types[d]; + h.prototype[a] && (h.prototype["_" + a] = h.prototype[a]); + h.prototype[a] = b; + } + }, createNode:function(a, b, d) { + var h = this.registered_node_types[a]; + if (!h) { + return e.debug && console.log('GraphNode type "' + a + '" not registered.'), null; + } + b = b || h.title || a; + var f = null; + if (e.catch_exceptions) { + try { + f = new h(b); + } catch (H) { + return console.error(H), null; + } + } else { + f = new h(b); + } + f.type = a; + !f.title && b && (f.title = b); + f.properties || (f.properties = {}); + f.properties_info || (f.properties_info = []); + f.flags || (f.flags = {}); + f.size || (f.size = f.computeSize()); + f.pos || (f.pos = e.DEFAULT_POSITION.concat()); + f.mode || (f.mode = e.ALWAYS); + if (d) { + for (var x in d) { + f[x] = d[x]; + } + } + return f; + }, getNodeType:function(a) { + return this.registered_node_types[a]; + }, getNodeTypesInCategory:function(a, b) { + var d = [], h; + for (h in this.registered_node_types) { + var f = this.registered_node_types[h]; + b && f.filter && f.filter != b || ("" == a ? null == f.category && d.push(f) : f.category == a && d.push(f)); + } + return d; + }, getNodeTypesCategories:function(a) { + var b = {"":1}, d; + for (d in this.registered_node_types) { + var h = this.registered_node_types[d]; + !h.category || h.skip_list || a && h.filter != a || (b[h.category] = 1); + } + a = []; + for (d in b) { + a.push(d); + } + return a; + }, reloadNodes:function(a) { + var b = document.getElementsByTagName("script"), d = [], h; + for (h in b) { + d.push(b[h]); + } + b = document.getElementsByTagName("head")[0]; + a = document.location.href + a; + for (h in d) { + var f = d[h].src; + if (f && f.substr(0, a.length) == a) { + try { + e.debug && console.log("Reloading: " + f); + var x = document.createElement("script"); + x.type = "text/javascript"; + x.src = f; + b.appendChild(x); + b.removeChild(d[h]); + } catch (H) { + if (e.throw_errors) { + throw H; + } + e.debug && console.log("Error while reloading " + f); + } + } + } + e.debug && console.log("Nodes reloaded"); + }, cloneObject:function(a, b) { + if (null == a) { + return null; + } + a = JSON.parse(JSON.stringify(a)); + if (!b) { + return a; + } + for (var d in a) { + b[d] = a[d]; + } + return b; + }, isValidConnection:function(a, b) { + if (!a || !b || a == b || a == e.EVENT && b == e.ACTION) { + return !0; + } + a = String(a); + b = String(b); + a = a.toLowerCase(); + b = b.toLowerCase(); + if (-1 == a.indexOf(",") && -1 == b.indexOf(",")) { + return a == b; + } + a = a.split(","); + b = b.split(","); + for (var d = 0; d < a.length; ++d) { + for (var h = 0; h < b.length; ++h) { + if (a[d] == b[h]) { + return !0; + } + } + } + return !1; + }, registerSearchboxExtra:function(a, b, d) { + this.searchbox_extras[b.toLowerCase()] = {type:a, desc:b, data:d}; + }, fetchFile:function(a, b, d, h) { + if (!a) { + return null; + } + b = b || "text"; + if (a.constructor === String) { + return "http" == a.substr(0, 4) && e.proxy && (a = e.proxy + a.substr(a.indexOf(":") + 3)), fetch(a).then(function(a) { + if (!a.ok) { + throw Error("File not found"); + } + if ("arraybuffer" == b) { + return a.arrayBuffer(); + } + if ("text" == b || "string" == b) { + return a.text(); + } + if ("json" == b) { + return a.json(); + } + if ("blob" == b) { + return a.blob(); + } + }).then(function(a) { + d && d(a); + }).catch(function(b) { + console.error("error fetching file:", a); + h && h(b); + }); + } + if (a.constructor === File || a.constructor === Blob) { + var f = new FileReader; + f.onload = function(a) { + a = a.target.result; + "json" == b && (a = JSON.parse(a)); + d && d(a); + }; + if ("arraybuffer" == b) { + return f.readAsArrayBuffer(a); + } + if ("text" == b || "json" == b) { + return f.readAsText(a); + } + if ("blob" == b) { + return f.readAsBinaryString(a); + } + } + return null; + }}; + e.getTime = "undefined" != typeof performance ? performance.now.bind(performance) : "undefined" != typeof Date && Date.now ? Date.now.bind(Date) : "undefined" != typeof process ? function() { + var a = process.hrtime(); + return 0.001 * a[0] + 1e-6 * a[1]; + } : function() { + return (new Date).getTime(); + }; + w.LGraph = e.LGraph = c; + c.supported_types = ["number", "string", "boolean"]; + c.prototype.getSupportedTypes = function() { + return this.supported_types || c.supported_types; + }; + c.STATUS_STOPPED = 1; + c.STATUS_RUNNING = 2; + c.prototype.clear = function() { + this.stop(); + this.status = c.STATUS_STOPPED; + this.last_link_id = this.last_node_id = 0; + this._version = -1; + if (this._nodes) { + for (var a = 0; a < this._nodes.length; ++a) { + var b = this._nodes[a]; + if (b.onRemoved) { + b.onRemoved(); + } + } + } + this._nodes = []; + this._nodes_by_id = {}; + this._nodes_in_order = []; + this._nodes_executable = null; + this._groups = []; + this.links = {}; + this.iteration = 0; + this.config = {}; + this.vars = {}; + this.fixedtime = this.runningtime = this.globaltime = 0; + this.elapsed_time = this.fixedtime_lapse = 0.01; + this.starttime = this.last_update_time = 0; + this.catch_errors = !0; + this.inputs = {}; + this.outputs = {}; + this.change(); + this.sendActionToCanvas("clear"); + }; + c.prototype.attachCanvas = function(a) { + if (a.constructor != l) { + throw "attachCanvas expects a LGraphCanvas instance"; + } + a.graph && a.graph != this && a.graph.detachCanvas(a); + a.graph = this; + this.list_of_graphcanvas || (this.list_of_graphcanvas = []); + this.list_of_graphcanvas.push(a); + }; + c.prototype.detachCanvas = function(a) { + if (this.list_of_graphcanvas) { + var b = this.list_of_graphcanvas.indexOf(a); + -1 != b && (a.graph = null, this.list_of_graphcanvas.splice(b, 1)); + } + }; + c.prototype.start = function(a) { + if (this.status != c.STATUS_RUNNING) { + this.status = c.STATUS_RUNNING; + if (this.onPlayEvent) { + this.onPlayEvent(); + } + this.sendEventToAllNodes("onStart"); + this.last_update_time = this.starttime = e.getTime(); + a = a || 0; + var b = this; + if (0 == a && "undefined" != typeof window && window.requestAnimationFrame) { + var d = function() { + if (-1 == b.execution_timer_id) { + window.requestAnimationFrame(d); + if (b.onBeforeStep) { + b.onBeforeStep(); + } + b.runStep(1, !this.catch_errors); + if (b.onAfterStep) { + b.onAfterStep(); + } + } + }; + this.execution_timer_id = -1; + d(); + } else { + this.execution_timer_id = setInterval(function() { + if (b.onBeforeStep) { + b.onBeforeStep(); + } + b.runStep(1, !this.catch_errors); + if (b.onAfterStep) { + b.onAfterStep(); + } + }, a); + } + } + }; + c.prototype.stop = function() { + if (this.status != c.STATUS_STOPPED) { + this.status = c.STATUS_STOPPED; + if (this.onStopEvent) { + this.onStopEvent(); + } + null != this.execution_timer_id && (-1 != this.execution_timer_id && clearInterval(this.execution_timer_id), this.execution_timer_id = null); + this.sendEventToAllNodes("onStop"); + } + }; + c.prototype.runStep = function(a, b, d) { + a = a || 1; + var h = e.getTime(); + this.globaltime = 0.001 * (h - this.starttime); + var f = this._nodes_executable ? this._nodes_executable : this._nodes; + if (f) { + d = d || f.length; + if (b) { + for (var x = 0; x < a; x++) { + for (var c = 0; c < d; ++c) { + var k = f[c]; + if (k.mode == e.ALWAYS && k.onExecute) { + k.onExecute(); + } + } + this.fixedtime += this.fixedtime_lapse; + if (this.onExecuteStep) { + this.onExecuteStep(); + } + } + if (this.onAfterExecute) { + this.onAfterExecute(); + } + } else { + try { + for (x = 0; x < a; x++) { + for (c = 0; c < d; ++c) { + if (k = f[c], k.mode == e.ALWAYS && k.onExecute) { + k.onExecute(); + } + } + this.fixedtime += this.fixedtime_lapse; + if (this.onExecuteStep) { + this.onExecuteStep(); + } + } + if (this.onAfterExecute) { + this.onAfterExecute(); + } + this.errors_in_execution = !1; + } catch (J) { + this.errors_in_execution = !0; + if (e.throw_errors) { + throw J; + } + e.debug && console.log("Error during execution: " + J); + this.stop(); + } + } + a = e.getTime(); + h = a - h; + 0 == h && (h = 1); + this.execution_time = 0.001 * h; + this.globaltime += 0.001 * h; + this.iteration += 1; + this.elapsed_time = 0.001 * (a - this.last_update_time); + this.last_update_time = a; + } + }; + c.prototype.updateExecutionOrder = function() { + this._nodes_in_order = this.computeExecutionOrder(!1); + this._nodes_executable = []; + for (var a = 0; a < this._nodes_in_order.length; ++a) { + this._nodes_in_order[a].onExecute && this._nodes_executable.push(this._nodes_in_order[a]); + } + }; + c.prototype.computeExecutionOrder = function(a, b) { + for (var d = [], h = [], f = {}, x = {}, c = {}, k = 0, n = this._nodes.length; k < n; ++k) { + var q = this._nodes[k]; + if (!a || q.onExecute) { + f[q.id] = q; + var l = 0; + if (q.inputs) { + for (var r = 0, g = q.inputs.length; r < g; r++) { + q.inputs[r] && null != q.inputs[r].link && (l += 1); + } + } + 0 == l ? (h.push(q), b && (q._level = 1)) : (b && (q._level = 0), c[q.id] = l); + } + } + for (; 0 != h.length;) { + if (q = h.shift(), d.push(q), delete f[q.id], q.outputs) { + for (k = 0; k < q.outputs.length; k++) { + if (a = q.outputs[k], null != a && null != a.links && 0 != a.links.length) { + for (r = 0; r < a.links.length; r++) { + (n = this.links[a.links[r]]) && !x[n.id] && (l = this.getNodeById(n.target_id), null == l ? x[n.id] = !0 : (b && (!l._level || l._level <= q._level) && (l._level = q._level + 1), x[n.id] = !0, --c[l.id], 0 == c[l.id] && h.push(l))); + } + } + } + } + } + for (k in f) { + d.push(f[k]); + } + d.length != this._nodes.length && e.debug && console.warn("something went wrong, nodes missing"); + n = d.length; + for (k = 0; k < n; ++k) { + d[k].order = k; + } + d = d.sort(function(a, b) { + var d = a.constructor.priority || a.priority || 0, f = b.constructor.priority || b.priority || 0; + return d == f ? a.order - b.order : d - f; + }); + for (k = 0; k < n; ++k) { + d[k].order = k; + } + return d; + }; + c.prototype.getAncestors = function(a) { + for (var b = [], d = [a], h = {}; d.length;) { + var f = d.shift(); + if (f.inputs) { + h[f.id] || f == a || (h[f.id] = !0, b.push(f)); + for (var e = 0; e < f.inputs.length; ++e) { + var c = f.getInputNode(e); + c && -1 == b.indexOf(c) && d.push(c); + } + } + } + b.sort(function(a, b) { + return a.order - b.order; + }); + return b; + }; + c.prototype.arrange = function(a) { + a = a || 100; + for (var b = this.computeExecutionOrder(!1, !0), d = [], h = 0; h < b.length; ++h) { + var f = b[h], x = f._level || 1; + d[x] || (d[x] = []); + d[x].push(f); + } + b = a; + for (h = 0; h < d.length; ++h) { + if (x = d[h]) { + for (var c = 100, k = a + e.NODE_TITLE_HEIGHT, n = 0; n < x.length; ++n) { + f = x[n], f.pos[0] = b, f.pos[1] = k, f.size[0] > c && (c = f.size[0]), k += f.size[1] + a + e.NODE_TITLE_HEIGHT; + } + b += c + a; + } + } + this.setDirtyCanvas(!0, !0); + }; + c.prototype.getTime = function() { + return this.globaltime; + }; + c.prototype.getFixedTime = function() { + return this.fixedtime; + }; + c.prototype.getElapsedTime = function() { + return this.elapsed_time; + }; + c.prototype.sendEventToAllNodes = function(a, b, d) { + d = d || e.ALWAYS; + var h = this._nodes_in_order ? this._nodes_in_order : this._nodes; + if (h) { + for (var f = 0, x = h.length; f < x; ++f) { + var c = h[f]; + if (c.constructor === e.Subgraph && "onExecute" != a) { + c.mode == d && c.sendEventToAllNodes(a, b, d); + } else { + if (c[a] && c.mode == d) { + if (void 0 === b) { + c[a](); + } else { + if (b && b.constructor === Array) { + c[a].apply(c, b); + } else { + c[a](b); + } + } + } + } + } + } + }; + c.prototype.sendActionToCanvas = function(a, b) { + if (this.list_of_graphcanvas) { + for (var d = 0; d < this.list_of_graphcanvas.length; ++d) { + var h = this.list_of_graphcanvas[d]; + h[a] && h[a].apply(h, b); + } + } + }; + c.prototype.add = function(a, b) { + if (a) { + if (a.constructor === g) { + this._groups.push(a), this.setDirtyCanvas(!0), this.change(), a.graph = this, this._version++; + } else { + -1 != a.id && null != this._nodes_by_id[a.id] && (console.warn("LiteGraph: there is already a node with this ID, changing it"), a.id = ++this.last_node_id); + if (this._nodes.length >= e.MAX_NUMBER_OF_NODES) { + throw "LiteGraph: max number of nodes in a graph reached"; + } + null == a.id || -1 == a.id ? a.id = ++this.last_node_id : this.last_node_id < a.id && (this.last_node_id = a.id); + a.graph = this; + this._version++; + this._nodes.push(a); + this._nodes_by_id[a.id] = a; + if (a.onAdded) { + a.onAdded(this); + } + this.config.align_to_grid && a.alignToGrid(); + b || this.updateExecutionOrder(); + if (this.onNodeAdded) { + this.onNodeAdded(a); + } + this.setDirtyCanvas(!0); + this.change(); + return a; + } + } + }; + c.prototype.remove = function(a) { + if (a.constructor === e.LGraphGroup) { + var b = this._groups.indexOf(a); + -1 != b && this._groups.splice(b, 1); + a.graph = null; + this._version++; + this.setDirtyCanvas(!0, !0); + this.change(); + } else { + if (null != this._nodes_by_id[a.id] && !a.ignore_remove) { + if (a.inputs) { + for (b = 0; b < a.inputs.length; b++) { + var d = a.inputs[b]; + null != d.link && a.disconnectInput(b); + } + } + if (a.outputs) { + for (b = 0; b < a.outputs.length; b++) { + d = a.outputs[b], null != d.links && d.links.length && a.disconnectOutput(b); + } + } + if (a.onRemoved) { + a.onRemoved(); + } + a.graph = null; + this._version++; + if (this.list_of_graphcanvas) { + for (b = 0; b < this.list_of_graphcanvas.length; ++b) { + d = this.list_of_graphcanvas[b], d.selected_nodes[a.id] && delete d.selected_nodes[a.id], d.node_dragged == a && (d.node_dragged = null); + } + } + b = this._nodes.indexOf(a); + -1 != b && this._nodes.splice(b, 1); + delete this._nodes_by_id[a.id]; + if (this.onNodeRemoved) { + this.onNodeRemoved(a); + } + this.setDirtyCanvas(!0, !0); + this.change(); + this.updateExecutionOrder(); + } + } + }; + c.prototype.getNodeById = function(a) { + return null == a ? null : this._nodes_by_id[a]; + }; + c.prototype.findNodesByClass = function(a, b) { + b = b || []; + for (var d = b.length = 0, h = this._nodes.length; d < h; ++d) { + this._nodes[d].constructor === a && b.push(this._nodes[d]); + } + return b; + }; + c.prototype.findNodesByType = function(a, b) { + a = a.toLowerCase(); + b = b || []; + for (var d = b.length = 0, h = this._nodes.length; d < h; ++d) { + this._nodes[d].type.toLowerCase() == a && b.push(this._nodes[d]); + } + return b; + }; + c.prototype.findNodeByTitle = function(a) { + for (var b = 0, d = this._nodes.length; b < d; ++b) { + if (this._nodes[b].title == a) { + return this._nodes[b]; + } + } + return null; + }; + c.prototype.findNodesByTitle = function(a) { + for (var b = [], d = 0, h = this._nodes.length; d < h; ++d) { + this._nodes[d].title == a && b.push(this._nodes[d]); + } + return b; + }; + c.prototype.getNodeOnPos = function(a, b, d, h) { + d = d || this._nodes; + for (var f = d.length - 1; 0 <= f; f--) { + var e = d[f]; + if (e.isPointInside(a, b, h)) { + return e; + } + } + return null; + }; + c.prototype.getGroupOnPos = function(a, b) { + for (var d = this._groups.length - 1; 0 <= d; d--) { + var h = this._groups[d]; + if (h.isPointInside(a, b, 2, !0)) { + return h; + } + } + return null; + }; + c.prototype.checkNodeTypes = function() { + for (var a = 0; a < this._nodes.length; a++) { + var b = this._nodes[a]; + if (b.constructor != e.registered_node_types[b.type]) { + console.log("node being replaced by newer version: " + b.type); + var d = e.createNode(b.type); + this._nodes[a] = d; + d.configure(b.serialize()); + d.graph = this; + this._nodes_by_id[d.id] = d; + b.inputs && (d.inputs = b.inputs.concat()); + b.outputs && (d.outputs = b.outputs.concat()); + } + } + this.updateExecutionOrder(); + }; + c.prototype.onAction = function(a, b) { + this._input_nodes = this.findNodesByClass(e.GraphInput, this._input_nodes); + for (var d = 0; d < this._input_nodes.length; ++d) { + var h = this._input_nodes[d]; + if (h.properties.name == a) { + h.onAction(a, b); + break; + } + } + }; + c.prototype.trigger = function(a, b) { + if (this.onTrigger) { + this.onTrigger(a, b); + } + }; + c.prototype.addInput = function(a, b, d) { + if (!this.inputs[a]) { + this.inputs[a] = {name:a, type:b, value:d}; + this._version++; + if (this.onInputAdded) { + this.onInputAdded(a, b); + } + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + } + }; + c.prototype.setInputData = function(a, b) { + if (a = this.inputs[a]) { + a.value = b; + } + }; + c.prototype.getInputData = function(a) { + return (a = this.inputs[a]) ? a.value : null; + }; + c.prototype.renameInput = function(a, b) { + if (b != a) { + if (!this.inputs[a]) { + return !1; + } + if (this.inputs[b]) { + return console.error("there is already one input with that name"), !1; + } + this.inputs[b] = this.inputs[a]; + delete this.inputs[a]; + this._version++; + if (this.onInputRenamed) { + this.onInputRenamed(a, b); + } + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + } + }; + c.prototype.changeInputType = function(a, b) { + if (!this.inputs[a]) { + return !1; + } + if (!this.inputs[a].type || String(this.inputs[a].type).toLowerCase() != String(b).toLowerCase()) { + if (this.inputs[a].type = b, this._version++, this.onInputTypeChanged) { + this.onInputTypeChanged(a, b); + } + } + }; + c.prototype.removeInput = function(a) { + if (!this.inputs[a]) { + return !1; + } + delete this.inputs[a]; + this._version++; + if (this.onInputRemoved) { + this.onInputRemoved(a); + } + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + return !0; + }; + c.prototype.addOutput = function(a, b, d) { + this.outputs[a] = {name:a, type:b, value:d}; + this._version++; + if (this.onOutputAdded) { + this.onOutputAdded(a, b); + } + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + }; + c.prototype.setOutputData = function(a, b) { + if (a = this.outputs[a]) { + a.value = b; + } + }; + c.prototype.getOutputData = function(a) { + return (a = this.outputs[a]) ? a.value : null; + }; + c.prototype.renameOutput = function(a, b) { + if (!this.outputs[a]) { + return !1; + } + if (this.outputs[b]) { + return console.error("there is already one output with that name"), !1; + } + this.outputs[b] = this.outputs[a]; + delete this.outputs[a]; + this._version++; + if (this.onOutputRenamed) { + this.onOutputRenamed(a, b); + } + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + }; + c.prototype.changeOutputType = function(a, b) { + if (!this.outputs[a]) { + return !1; + } + if (!this.outputs[a].type || String(this.outputs[a].type).toLowerCase() != String(b).toLowerCase()) { + if (this.outputs[a].type = b, this._version++, this.onOutputTypeChanged) { + this.onOutputTypeChanged(a, b); + } + } + }; + c.prototype.removeOutput = function(a) { + if (!this.outputs[a]) { + return !1; + } + delete this.outputs[a]; + this._version++; + if (this.onOutputRemoved) { + this.onOutputRemoved(a); + } + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + return !0; + }; + c.prototype.triggerInput = function(a, b) { + a = this.findNodesByTitle(a); + for (var d = 0; d < a.length; ++d) { + a[d].onTrigger(b); + } + }; + c.prototype.setCallback = function(a, b) { + a = this.findNodesByTitle(a); + for (var d = 0; d < a.length; ++d) { + a[d].setTrigger(b); + } + }; + c.prototype.connectionChange = function(a, b) { + this.updateExecutionOrder(); + if (this.onConnectionChange) { + this.onConnectionChange(a); + } + this._version++; + this.sendActionToCanvas("onConnectionChange"); + }; + c.prototype.isLive = function() { + if (!this.list_of_graphcanvas) { + return !1; + } + for (var a = 0; a < this.list_of_graphcanvas.length; ++a) { + if (this.list_of_graphcanvas[a].live_mode) { + return !0; + } + } + return !1; + }; + c.prototype.clearTriggeredSlots = function() { + for (var a in this.links) { + var b = this.links[a]; + b && b._last_time && (b._last_time = 0); + } + }; + c.prototype.change = function() { + e.debug && console.log("Graph changed"); + this.sendActionToCanvas("setDirty", [!0, !0]); + if (this.on_change) { + this.on_change(this); + } + }; + c.prototype.setDirtyCanvas = function(a, b) { + this.sendActionToCanvas("setDirty", [a, b]); + }; + c.prototype.removeLink = function(a) { + if (a = this.links[a]) { + var b = this.getNodeById(a.target_id); + b && b.disconnectInput(a.target_slot); + } + }; + c.prototype.serialize = function() { + for (var a = [], b = 0, d = this._nodes.length; b < d; ++b) { + a.push(this._nodes[b].serialize()); + } + d = []; + for (b in this.links) { + var h = this.links[b]; + if (!h.serialize) { + console.warn("weird LLink bug, link info is not a LLink but a regular object"); + var f = new p; + for (b in h) { + f[b] = h[b]; + } + h = this.links[b] = f; + } + d.push(h.serialize()); + } + h = []; + for (b = 0; b < this._groups.length; ++b) { + h.push(this._groups[b].serialize()); + } + return {last_node_id:this.last_node_id, last_link_id:this.last_link_id, nodes:a, links:d, groups:h, config:this.config, version:e.VERSION}; + }; + c.prototype.configure = function(a, b) { + if (a) { + b || this.clear(); + b = a.nodes; + if (a.links && a.links.constructor === Array) { + for (var d = [], h = 0; h < a.links.length; ++h) { + var f = a.links[h]; + if (f) { + var c = new p; + c.configure(f); + d[c.id] = c; + } else { + console.warn("serialized graph link data contains errors, skipping."); + } + } + a.links = d; + } + for (h in a) { + "nodes" != h && "groups" != h && (this[h] = a[h]); + } + d = !1; + this._nodes = []; + if (b) { + h = 0; + for (f = b.length; h < f; ++h) { + c = b[h]; + var k = e.createNode(c.type, c.title); + k || (e.debug && console.log("Node not found or has errors: " + c.type), k = new m, k.last_serialization = c, d = k.has_errors = !0); + k.id = c.id; + this.add(k, !0); + } + h = 0; + for (f = b.length; h < f; ++h) { + c = b[h], (k = this.getNodeById(c.id)) && k.configure(c); + } + } + this._groups.length = 0; + if (a.groups) { + for (h = 0; h < a.groups.length; ++h) { + b = new e.LGraphGroup, b.configure(a.groups[h]), this.add(b); + } + } + this.updateExecutionOrder(); + this._version++; + this.setDirtyCanvas(!0, !0); + return d; + } + }; + c.prototype.load = function(a) { + var b = this, d = new XMLHttpRequest; + d.open("GET", a, !0); + d.send(null); + d.onload = function(a) { + 200 !== d.status ? console.error("Error loading graph:", d.status, d.response) : (a = JSON.parse(d.response), b.configure(a)); + }; + d.onerror = function(a) { + console.error("Error loading graph:", a); + }; + }; + c.prototype.onNodeTrace = function(a, b, d) { + }; + p.prototype.configure = function(a) { + a.constructor === Array ? (this.id = a[0], this.origin_id = a[1], this.origin_slot = a[2], this.target_id = a[3], this.target_slot = a[4], this.type = a[5]) : (this.id = a.id, this.type = a.type, this.origin_id = a.origin_id, this.origin_slot = a.origin_slot, this.target_id = a.target_id, this.target_slot = a.target_slot); + }; + p.prototype.serialize = function() { + return [this.id, this.origin_id, this.origin_slot, this.target_id, this.target_slot, this.type]; + }; + e.LLink = p; + w.LGraphNode = e.LGraphNode = m; + m.prototype._ctor = function(a) { + this.title = a || "Unnamed"; + this.size = [e.NODE_WIDTH, 60]; + this.graph = null; + this._pos = new Float32Array(10, 10); + Object.defineProperty(this, "pos", {set:function(a) { + !a || 2 > a.length || (this._pos[0] = a[0], this._pos[1] = a[1]); + }, get:function() { + return this._pos; + }, enumerable:!0}); + this.id = -1; + this.type = null; + this.inputs = []; + this.outputs = []; + this.connections = []; + this.properties = {}; + this.properties_info = []; + this.flags = {}; + }; + m.prototype.configure = function(a) { + this.graph && this.graph._version++; + for (var b in a) { + if ("properties" == b) { + for (var d in a.properties) { + if (this.properties[d] = a.properties[d], this.onPropertyChanged) { + this.onPropertyChanged(d, a.properties[d]); + } + } + } else { + null != a[b] && ("object" == typeof a[b] ? this[b] && this[b].configure ? this[b].configure(a[b]) : this[b] = e.cloneObject(a[b], this[b]) : this[b] = a[b]); + } + } + a.title || (this.title = this.constructor.title); + if (this.onConnectionsChange) { + if (this.inputs) { + for (d = 0; d < this.inputs.length; ++d) { + b = this.inputs[d]; + var h = this.graph ? this.graph.links[b.link] : null; + this.onConnectionsChange(e.INPUT, d, !0, h, b); + } + } + if (this.outputs) { + for (d = 0; d < this.outputs.length; ++d) { + var f = this.outputs[d]; + if (f.links) { + for (b = 0; b < f.links.length; ++b) { + h = this.graph ? this.graph.links[f.links[b]] : null, this.onConnectionsChange(e.OUTPUT, d, !0, h, f); + } + } + } + } + } + if (this.widgets) { + for (d = 0; d < this.widgets.length; ++d) { + (b = this.widgets[d]) && b.options && b.options.property && this.properties[b.options.property] && (b.value = JSON.parse(JSON.stringify(this.properties[b.options.property]))); + } + if (a.widgets_values) { + for (d = 0; d < a.widgets_values.length; ++d) { + this.widgets[d] && (this.widgets[d].value = a.widgets_values[d]); + } + } + } + if (this.onConfigure) { + this.onConfigure(a); + } + }; + m.prototype.serialize = function() { + var a = {id:this.id, type:this.type, pos:this.pos, size:this.size, flags:e.cloneObject(this.flags), order:this.order, mode:this.mode}; + if (this.constructor === m && this.last_serialization) { + return this.last_serialization; + } + this.inputs && (a.inputs = this.inputs); + if (this.outputs) { + for (var b = 0; b < this.outputs.length; b++) { + delete this.outputs[b]._data; + } + a.outputs = this.outputs; + } + this.title && this.title != this.constructor.title && (a.title = this.title); + this.properties && (a.properties = e.cloneObject(this.properties)); + if (this.widgets && this.serialize_widgets) { + for (a.widgets_values = [], b = 0; b < this.widgets.length; ++b) { + a.widgets_values[b] = this.widgets[b] ? this.widgets[b].value : null; + } + } + a.type || (a.type = this.constructor.type); + this.color && (a.color = this.color); + this.bgcolor && (a.bgcolor = this.bgcolor); + this.boxcolor && (a.boxcolor = this.boxcolor); + this.shape && (a.shape = this.shape); + this.onSerialize && this.onSerialize(a) && console.warn("node onSerialize shouldnt return anything, data should be stored in the object pass in the first parameter"); + return a; + }; + m.prototype.clone = function() { + var a = e.createNode(this.type); + if (!a) { + return null; + } + var b = e.cloneObject(this.serialize()); + if (b.inputs) { + for (var d = 0; d < b.inputs.length; ++d) { + b.inputs[d].link = null; + } + } + if (b.outputs) { + for (d = 0; d < b.outputs.length; ++d) { + b.outputs[d].links && (b.outputs[d].links.length = 0); + } + } + delete b.id; + a.configure(b); + return a; + }; + m.prototype.toString = function() { + return JSON.stringify(this.serialize()); + }; + m.prototype.getTitle = function() { + return this.title || this.constructor.title; + }; + m.prototype.setProperty = function(a, b) { + this.properties || (this.properties = {}); + if (b !== this.properties[a]) { + var d = this.properties[a]; + this.properties[a] = b; + this.onPropertyChanged && !1 === this.onPropertyChanged(a, b, d) && (this.properties[a] = d); + if (this.widgets) { + for (d = 0; d < this.widgets.length; ++d) { + var h = this.widgets[d]; + if (h && h.options.property == a) { + h.value = b; + break; + } + } + } + } + }; + m.prototype.setOutputData = function(a, b) { + if (this.outputs && !(-1 == a || a >= this.outputs.length)) { + var d = this.outputs[a]; + if (d && (d._data = b, this.outputs[a].links)) { + for (d = 0; d < this.outputs[a].links.length; d++) { + var h = this.graph.links[this.outputs[a].links[d]]; + h && (h.data = b); + } + } + } + }; + m.prototype.setOutputDataType = function(a, b) { + if (this.outputs && !(-1 == a || a >= this.outputs.length)) { + var d = this.outputs[a]; + if (d && (d.type = b, this.outputs[a].links)) { + for (d = 0; d < this.outputs[a].links.length; d++) { + this.graph.links[this.outputs[a].links[d]].type = b; + } + } + } + }; + m.prototype.getInputData = function(a, b) { + if (this.inputs && !(a >= this.inputs.length || null == this.inputs[a].link)) { + a = this.graph.links[this.inputs[a].link]; + if (!a) { + return null; + } + if (!b) { + return a.data; + } + b = this.graph.getNodeById(a.origin_id); + if (!b) { + return a.data; + } + if (b.updateOutputData) { + b.updateOutputData(a.origin_slot); + } else { + if (b.onExecute) { + b.onExecute(); + } + } + return a.data; + } + }; + m.prototype.getInputDataType = function(a) { + if (!this.inputs || a >= this.inputs.length || null == this.inputs[a].link) { + return null; + } + a = this.graph.links[this.inputs[a].link]; + if (!a) { + return null; + } + var b = this.graph.getNodeById(a.origin_id); + return b ? (a = b.outputs[a.origin_slot]) ? a.type : null : a.type; + }; + m.prototype.getInputDataByName = function(a, b) { + a = this.findInputSlot(a); + return -1 == a ? null : this.getInputData(a, b); + }; + m.prototype.isInputConnected = function(a) { + return this.inputs ? a < this.inputs.length && null != this.inputs[a].link : !1; + }; + m.prototype.getInputInfo = function(a) { + return this.inputs ? a < this.inputs.length ? this.inputs[a] : null : null; + }; + m.prototype.getInputNode = function(a) { + if (!this.inputs || a >= this.inputs.length) { + return null; + } + a = this.inputs[a]; + return a && null !== a.link ? (a = this.graph.links[a.link]) ? this.graph.getNodeById(a.origin_id) : null : null; + }; + m.prototype.getInputOrProperty = function(a) { + if (!this.inputs || !this.inputs.length) { + return this.properties ? this.properties[a] : null; + } + for (var b = 0, d = this.inputs.length; b < d; ++b) { + var h = this.inputs[b]; + if (a == h.name && null != h.link && (h = this.graph.links[h.link])) { + return h.data; + } + } + return this.properties[a]; + }; + m.prototype.getOutputData = function(a) { + return !this.outputs || a >= this.outputs.length ? null : this.outputs[a]._data; + }; + m.prototype.getOutputInfo = function(a) { + return this.outputs ? a < this.outputs.length ? this.outputs[a] : null : null; + }; + m.prototype.isOutputConnected = function(a) { + return this.outputs ? a < this.outputs.length && this.outputs[a].links && this.outputs[a].links.length : !1; + }; + m.prototype.isAnyOutputConnected = function() { + if (!this.outputs) { + return !1; + } + for (var a = 0; a < this.outputs.length; ++a) { + if (this.outputs[a].links && this.outputs[a].links.length) { + return !0; + } + } + return !1; + }; + m.prototype.getOutputNodes = function(a) { + if (!this.outputs || 0 == this.outputs.length || a >= this.outputs.length) { + return null; + } + a = this.outputs[a]; + if (!a.links || 0 == a.links.length) { + return null; + } + for (var b = [], d = 0; d < a.links.length; d++) { + var h = this.graph.links[a.links[d]]; + h && (h = this.graph.getNodeById(h.target_id)) && b.push(h); + } + return b; + }; + m.prototype.trigger = function(a, b) { + if (this.outputs && this.outputs.length) { + this.graph && (this.graph._last_trigger_time = e.getTime()); + for (var d = 0; d < this.outputs.length; ++d) { + var h = this.outputs[d]; + !h || h.type !== e.EVENT || a && h.name != a || this.triggerSlot(d, b); + } + } + }; + m.prototype.triggerSlot = function(a, b, d) { + if (this.outputs && (a = this.outputs[a]) && (a = a.links) && a.length) { + this.graph && (this.graph._last_trigger_time = e.getTime()); + for (var h = 0; h < a.length; ++h) { + var f = a[h]; + if (null == d || d == f) { + var c = this.graph.links[a[h]]; + if (c && (c._last_time = e.getTime(), f = this.graph.getNodeById(c.target_id))) { + if (c = f.inputs[c.target_slot], f.onAction) { + f.onAction(c.name, b); + } else { + if (f.mode === e.ON_TRIGGER && f.onExecute) { + f.onExecute(b); + } + } + } + } + } + } + }; + m.prototype.clearTriggeredSlot = function(a, b) { + if (this.outputs && (a = this.outputs[a]) && (a = a.links) && a.length) { + for (var d = 0; d < a.length; ++d) { + var h = a[d]; + if (null == b || b == h) { + if (h = this.graph.links[a[d]]) { + h._last_time = 0; + } + } + } + } + }; + m.prototype.addProperty = function(a, b, d, h) { + d = {name:a, type:d, default_value:b}; + if (h) { + for (var f in h) { + d[f] = h[f]; + } + } + this.properties_info || (this.properties_info = []); + this.properties_info.push(d); + this.properties || (this.properties = {}); + this.properties[a] = b; + return d; + }; + m.prototype.addOutput = function(a, b, d) { + a = {name:a, type:b, links:null}; + if (d) { + for (var h in d) { + a[h] = d[h]; + } + } + this.outputs || (this.outputs = []); + this.outputs.push(a); + if (this.onOutputAdded) { + this.onOutputAdded(a); + } + this.size = this.computeSize(); + this.setDirtyCanvas(!0, !0); + return a; + }; + m.prototype.addOutputs = function(a) { + for (var b = 0; b < a.length; ++b) { + var d = a[b], h = {name:d[0], type:d[1], link:null}; + if (a[2]) { + for (var f in d[2]) { + h[f] = d[2][f]; + } + } + this.outputs || (this.outputs = []); + this.outputs.push(h); + if (this.onOutputAdded) { + this.onOutputAdded(h); + } + } + this.size = this.computeSize(); + this.setDirtyCanvas(!0, !0); + }; + m.prototype.removeOutput = function(a) { + this.disconnectOutput(a); + this.outputs.splice(a, 1); + for (var b = a; b < this.outputs.length; ++b) { + if (this.outputs[b] && this.outputs[b].links) { + for (var d = this.outputs[b].links, h = 0; h < d.length; ++h) { + var f = this.graph.links[d[h]]; + f && --f.origin_slot; + } + } + } + this.size = this.computeSize(); + if (this.onOutputRemoved) { + this.onOutputRemoved(a); + } + this.setDirtyCanvas(!0, !0); + }; + m.prototype.addInput = function(a, b, d) { + a = {name:a, type:b || 0, link:null}; + if (d) { + for (var h in d) { + a[h] = d[h]; + } + } + this.inputs || (this.inputs = []); + this.inputs.push(a); + this.size = this.computeSize(); + if (this.onInputAdded) { + this.onInputAdded(a); + } + this.setDirtyCanvas(!0, !0); + return a; + }; + m.prototype.addInputs = function(a) { + for (var b = 0; b < a.length; ++b) { + var d = a[b], h = {name:d[0], type:d[1], link:null}; + if (a[2]) { + for (var f in d[2]) { + h[f] = d[2][f]; + } + } + this.inputs || (this.inputs = []); + this.inputs.push(h); + if (this.onInputAdded) { + this.onInputAdded(h); + } + } + this.size = this.computeSize(); + this.setDirtyCanvas(!0, !0); + }; + m.prototype.removeInput = function(a) { + this.disconnectInput(a); + this.inputs.splice(a, 1); + for (var b = a; b < this.inputs.length; ++b) { + if (this.inputs[b]) { + var d = this.graph.links[this.inputs[b].link]; + d && --d.target_slot; + } + } + this.size = this.computeSize(); + if (this.onInputRemoved) { + this.onInputRemoved(a); + } + this.setDirtyCanvas(!0, !0); + }; + m.prototype.addConnection = function(a, b, d, h) { + a = {name:a, type:b, pos:d, direction:h, links:null}; + this.connections.push(a); + return a; + }; + m.prototype.computeSize = function(a, b) { + function d(a) { + return a ? h * a.length * 0.6 : 0; + } + if (this.constructor.size) { + return this.constructor.size.concat(); + } + a = Math.max(this.inputs ? this.inputs.length : 1, this.outputs ? this.outputs.length : 1); + b = b || new Float32Array([0, 0]); + a = Math.max(a, 1); + var h = e.NODE_TEXT_SIZE, f = d(this.title), c = 0, k = 0; + if (this.inputs) { + for (var n = 0, q = this.inputs.length; n < q; ++n) { + var l = this.inputs[n]; + l = l.label || l.name || ""; + l = d(l); + c < l && (c = l); + } + } + if (this.outputs) { + for (n = 0, q = this.outputs.length; n < q; ++n) { + l = this.outputs[n], l = l.label || l.name || "", l = d(l), k < l && (k = l); + } + } + b[0] = Math.max(c + k + 10, f); + b[0] = Math.max(b[0], e.NODE_WIDTH); + this.widgets && this.widgets.length && (b[0] = Math.max(b[0], 1.5 * e.NODE_WIDTH)); + b[1] = (this.constructor.slot_start_y || 0) + a * e.NODE_SLOT_HEIGHT; + a = 0; + if (this.widgets && this.widgets.length) { + n = 0; + for (q = this.widgets.length; n < q; ++n) { + a = this.widgets[n].computeSize ? a + (this.widgets[n].computeSize(b[0])[1] + 4) : a + (e.NODE_WIDGET_HEIGHT + 4); + } + a += 8; + } + b[1] = this.widgets_up ? Math.max(b[1], a) : null != this.widgets_start_y ? Math.max(b[1], a + this.widgets_start_y) : b[1] + a; + if (this.onResize) { + this.onResize(b); + } + this.constructor.min_height && b[1] < this.constructor.min_height && (b[1] = this.constructor.min_height); + b[1] += 6; + return b; + }; + m.prototype.getPropertyInfo = function(a) { + var b = null; + if (this.properties_info) { + for (var d = 0; d < this.properties_info.length; ++d) { + if (this.properties_info[d].name == a) { + b = this.properties_info[d]; + break; + } + } + } + this.constructor["@" + a] && (b = this.constructor["@" + a]); + this.onGetPropertyInfo && (b = this.onGetPropertyInfo(a)); + b || (b = {}); + b.type || (b.type = typeof this.properties[a]); + return b; + }; + m.prototype.addWidget = function(a, b, d, h, f) { + this.widgets || (this.widgets = []); + !f && h && h.constructor === Object && (f = h, h = null); + f && f.constructor === String && (f = {property:f}); + h && h.constructor === String && (f || (f = {}), f.property = h, h = null); + h && h.constructor !== Function && (console.warn("addWidget: callback must be a function"), h = null); + b = {type:a.toLowerCase(), name:b, value:d, callback:h, options:f || {}}; + void 0 !== b.options.y && (b.y = b.options.y); + h || b.options.callback || b.options.property || console.warn("LiteGraph addWidget(...) without a callback or property assigned"); + if ("combo" == a && !b.options.values) { + throw "LiteGraph addWidget('combo',...) requires to pass values in options: { values:['red','blue'] }"; + } + this.widgets.push(b); + this.size = this.computeSize(); + return b; + }; + m.prototype.addCustomWidget = function(a) { + this.widgets || (this.widgets = []); + this.widgets.push(a); + return a; + }; + m.prototype.getBounding = function(a) { + a = a || new Float32Array(4); + a[0] = this.pos[0] - 4; + a[1] = this.pos[1] - e.NODE_TITLE_HEIGHT; + a[2] = this.size[0] + 4; + a[3] = this.size[1] + e.NODE_TITLE_HEIGHT; + if (this.onBounding) { + this.onBounding(a); + } + return a; + }; + m.prototype.isPointInside = function(a, b, d, h) { + d = d || 0; + var f = this.graph && this.graph.isLive() ? 0 : e.NODE_TITLE_HEIGHT; + h && (f = 0); + if (this.flags && this.flags.collapsed) { + if (y(a, b, this.pos[0] - d, this.pos[1] - e.NODE_TITLE_HEIGHT - d, (this._collapsed_width || e.NODE_COLLAPSED_WIDTH) + 2 * d, e.NODE_TITLE_HEIGHT + 2 * d)) { + return !0; + } + } else { + if (this.pos[0] - 4 - d < a && this.pos[0] + this.size[0] + 4 + d > a && this.pos[1] - f - d < b && this.pos[1] + this.size[1] + d > b) { + return !0; + } + } + return !1; + }; + m.prototype.getSlotInPosition = function(a, b) { + var d = new Float32Array(2); + if (this.inputs) { + for (var h = 0, f = this.inputs.length; h < f; ++h) { + var e = this.inputs[h]; + this.getConnectionPos(!0, h, d); + if (y(a, b, d[0] - 10, d[1] - 5, 20, 10)) { + return {input:e, slot:h, link_pos:d}; + } + } + } + if (this.outputs) { + for (h = 0, f = this.outputs.length; h < f; ++h) { + if (e = this.outputs[h], this.getConnectionPos(!1, h, d), y(a, b, d[0] - 10, d[1] - 5, 20, 10)) { + return {output:e, slot:h, link_pos:d}; + } + } + } + return null; + }; + m.prototype.findInputSlot = function(a) { + if (!this.inputs) { + return -1; + } + for (var b = 0, d = this.inputs.length; b < d; ++b) { + if (a == this.inputs[b].name) { + return b; + } + } + return -1; + }; + m.prototype.findOutputSlot = function(a) { + if (!this.outputs) { + return -1; + } + for (var b = 0, d = this.outputs.length; b < d; ++b) { + if (a == this.outputs[b].name) { + return b; + } + } + return -1; + }; + m.prototype.connect = function(a, b, d) { + d = d || 0; + if (!this.graph) { + return console.log("Connect: Error, node doesn't belong to any graph. Nodes must be added first to a graph before connecting them."), null; + } + if (a.constructor === String) { + if (a = this.findOutputSlot(a), -1 == a) { + return e.debug && console.log("Connect: Error, no slot of name " + a), null; + } + } else { + if (!this.outputs || a >= this.outputs.length) { + return e.debug && console.log("Connect: Error, slot number not found"), null; + } + } + b && b.constructor === Number && (b = this.graph.getNodeById(b)); + if (!b) { + throw "target node is null"; + } + if (b == this) { + return null; + } + if (d.constructor === String) { + if (d = b.findInputSlot(d), -1 == d) { + return e.debug && console.log("Connect: Error, no slot of name " + d), null; + } + } else { + if (d === e.EVENT) { + return null; + } + if (!b.inputs || d >= b.inputs.length) { + return e.debug && console.log("Connect: Error, slot number not found"), null; + } + } + null != b.inputs[d].link && b.disconnectInput(d); + var h = this.outputs[a]; + if (b.onConnectInput && !1 === b.onConnectInput(d, h.type, h)) { + return null; + } + var f = b.inputs[d], c = null; + if (e.isValidConnection(h.type, f.type)) { + c = new p(++this.graph.last_link_id, f.type, this.id, a, b.id, d); + this.graph.links[c.id] = c; + null == h.links && (h.links = []); + h.links.push(c.id); + b.inputs[d].link = c.id; + this.graph && this.graph._version++; + if (this.onConnectionsChange) { + this.onConnectionsChange(e.OUTPUT, a, !0, c, h); + } + if (b.onConnectionsChange) { + b.onConnectionsChange(e.INPUT, d, !0, c, f); + } + this.graph && this.graph.onNodeConnectionChange && (this.graph.onNodeConnectionChange(e.INPUT, b, d, this, a), this.graph.onNodeConnectionChange(e.OUTPUT, this, a, b, d)); + } + this.setDirtyCanvas(!1, !0); + this.graph.connectionChange(this, c); + return c; + }; + m.prototype.disconnectOutput = function(a, b) { + if (a.constructor === String) { + if (a = this.findOutputSlot(a), -1 == a) { + return e.debug && console.log("Connect: Error, no slot of name " + a), !1; + } + } else { + if (!this.outputs || a >= this.outputs.length) { + return e.debug && console.log("Connect: Error, slot number not found"), !1; + } + } + var d = this.outputs[a]; + if (!d || !d.links || 0 == d.links.length) { + return !1; + } + if (b) { + b.constructor === Number && (b = this.graph.getNodeById(b)); + if (!b) { + throw "Target Node not found"; + } + for (var h = 0, f = d.links.length; h < f; h++) { + var c = d.links[h], k = this.graph.links[c]; + if (k.target_id == b.id) { + d.links.splice(h, 1); + var n = b.inputs[k.target_slot]; + n.link = null; + delete this.graph.links[c]; + this.graph && this.graph._version++; + if (b.onConnectionsChange) { + b.onConnectionsChange(e.INPUT, k.target_slot, !1, k, n); + } + if (this.onConnectionsChange) { + this.onConnectionsChange(e.OUTPUT, a, !1, k, d); + } + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange(e.OUTPUT, this, a); + } + this.graph && this.graph.onNodeConnectionChange && (this.graph.onNodeConnectionChange(e.OUTPUT, this, a), this.graph.onNodeConnectionChange(e.INPUT, b, k.target_slot)); + break; + } + } + } else { + h = 0; + for (f = d.links.length; h < f; h++) { + if (c = d.links[h], k = this.graph.links[c]) { + b = this.graph.getNodeById(k.target_id); + this.graph && this.graph._version++; + if (b) { + n = b.inputs[k.target_slot]; + n.link = null; + if (b.onConnectionsChange) { + b.onConnectionsChange(e.INPUT, k.target_slot, !1, k, n); + } + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange(e.INPUT, b, k.target_slot); + } + } + delete this.graph.links[c]; + if (this.onConnectionsChange) { + this.onConnectionsChange(e.OUTPUT, a, !1, k, d); + } + this.graph && this.graph.onNodeConnectionChange && (this.graph.onNodeConnectionChange(e.OUTPUT, this, a), this.graph.onNodeConnectionChange(e.INPUT, b, k.target_slot)); + } + } + d.links = null; + } + this.setDirtyCanvas(!1, !0); + this.graph.connectionChange(this); + return !0; + }; + m.prototype.disconnectInput = function(a) { + if (a.constructor === String) { + if (a = this.findInputSlot(a), -1 == a) { + return e.debug && console.log("Connect: Error, no slot of name " + a), !1; + } + } else { + if (!this.inputs || a >= this.inputs.length) { + return e.debug && console.log("Connect: Error, slot number not found"), !1; + } + } + var b = this.inputs[a]; + if (!b) { + return !1; + } + var d = this.inputs[a].link; + this.inputs[a].link = null; + var h = this.graph.links[d]; + if (h) { + var f = this.graph.getNodeById(h.origin_id); + if (!f) { + return !1; + } + var c = f.outputs[h.origin_slot]; + if (!c || !c.links || 0 == c.links.length) { + return !1; + } + for (var k = 0, n = c.links.length; k < n; k++) { + if (c.links[k] == d) { + c.links.splice(k, 1); + break; + } + } + delete this.graph.links[d]; + this.graph && this.graph._version++; + if (this.onConnectionsChange) { + this.onConnectionsChange(e.INPUT, a, !1, h, b); + } + if (f.onConnectionsChange) { + f.onConnectionsChange(e.OUTPUT, k, !1, h, c); + } + this.graph && this.graph.onNodeConnectionChange && (this.graph.onNodeConnectionChange(e.OUTPUT, f, k), this.graph.onNodeConnectionChange(e.INPUT, this, a)); + } + this.setDirtyCanvas(!1, !0); + this.graph.connectionChange(this); + return !0; + }; + m.prototype.getConnectionPos = function(a, b, d) { + d = d || new Float32Array(2); + var h = 0; + a && this.inputs && (h = this.inputs.length); + !a && this.outputs && (h = this.outputs.length); + var f = 0.5 * e.NODE_SLOT_HEIGHT; + if (this.flags.collapsed) { + return b = this._collapsed_width || e.NODE_COLLAPSED_WIDTH, this.horizontal ? (d[0] = this.pos[0] + 0.5 * b, d[1] = a ? this.pos[1] - e.NODE_TITLE_HEIGHT : this.pos[1]) : (d[0] = a ? this.pos[0] : this.pos[0] + b, d[1] = this.pos[1] - 0.5 * e.NODE_TITLE_HEIGHT), d; + } + if (a && -1 == b) { + return d[0] = this.pos[0] + 0.5 * e.NODE_TITLE_HEIGHT, d[1] = this.pos[1] + 0.5 * e.NODE_TITLE_HEIGHT, d; + } + if (a && h > b && this.inputs[b].pos) { + return d[0] = this.pos[0] + this.inputs[b].pos[0], d[1] = this.pos[1] + this.inputs[b].pos[1], d; + } + if (!a && h > b && this.outputs[b].pos) { + return d[0] = this.pos[0] + this.outputs[b].pos[0], d[1] = this.pos[1] + this.outputs[b].pos[1], d; + } + if (this.horizontal) { + return d[0] = this.pos[0] + this.size[0] / h * (b + 0.5), d[1] = a ? this.pos[1] - e.NODE_TITLE_HEIGHT : this.pos[1] + this.size[1], d; + } + d[0] = a ? this.pos[0] + f : this.pos[0] + this.size[0] + 1 - f; + d[1] = this.pos[1] + (b + 0.7) * e.NODE_SLOT_HEIGHT + (this.constructor.slot_start_y || 0); + return d; + }; + m.prototype.alignToGrid = function() { + this.pos[0] = e.CANVAS_GRID_SIZE * Math.round(this.pos[0] / e.CANVAS_GRID_SIZE); + this.pos[1] = e.CANVAS_GRID_SIZE * Math.round(this.pos[1] / e.CANVAS_GRID_SIZE); + }; + m.prototype.trace = function(a) { + this.console || (this.console = []); + this.console.push(a); + this.console.length > m.MAX_CONSOLE && this.console.shift(); + this.graph.onNodeTrace(this, a); + }; + m.prototype.setDirtyCanvas = function(a, b) { + this.graph && this.graph.sendActionToCanvas("setDirty", [a, b]); + }; + m.prototype.loadImage = function(a) { + var b = new Image; + b.src = e.node_images_path + a; + b.ready = !1; + var d = this; + b.onload = function() { + this.ready = !0; + d.setDirtyCanvas(!0); + }; + return b; + }; + m.prototype.captureInput = function(a) { + if (this.graph && this.graph.list_of_graphcanvas) { + for (var b = this.graph.list_of_graphcanvas, d = 0; d < b.length; ++d) { + var h = b[d]; + if (a || h.node_capturing_input == this) { + h.node_capturing_input = a ? this : null; + } + } + } + }; + m.prototype.collapse = function(a) { + this.graph._version++; + if (!1 !== this.constructor.collapsable || a) { + this.flags.collapsed = this.flags.collapsed ? !1 : !0, this.setDirtyCanvas(!0, !0); + } + }; + m.prototype.pin = function(a) { + this.graph._version++; + this.flags.pinned = void 0 === a ? !this.flags.pinned : a; + }; + m.prototype.localToScreen = function(a, b, d) { + return [(a + this.pos[0]) * d.scale + d.offset[0], (b + this.pos[1]) * d.scale + d.offset[1]]; + }; + w.LGraphGroup = e.LGraphGroup = g; + g.prototype._ctor = function(a) { + this.title = a || "Group"; + this.font_size = 24; + this.color = l.node_colors.pale_blue ? l.node_colors.pale_blue.groupcolor : "#AAA"; + this._bounding = new Float32Array([10, 10, 140, 80]); + this._pos = this._bounding.subarray(0, 2); + this._size = this._bounding.subarray(2, 4); + this._nodes = []; + this.graph = null; + Object.defineProperty(this, "pos", {set:function(a) { + !a || 2 > a.length || (this._pos[0] = a[0], this._pos[1] = a[1]); + }, get:function() { + return this._pos; + }, enumerable:!0}); + Object.defineProperty(this, "size", {set:function(a) { + !a || 2 > a.length || (this._size[0] = Math.max(140, a[0]), this._size[1] = Math.max(80, a[1])); + }, get:function() { + return this._size; + }, enumerable:!0}); + }; + g.prototype.configure = function(a) { + this.title = a.title; + this._bounding.set(a.bounding); + this.color = a.color; + this.font = a.font; + }; + g.prototype.serialize = function() { + var a = this._bounding; + return {title:this.title, bounding:[Math.round(a[0]), Math.round(a[1]), Math.round(a[2]), Math.round(a[3])], color:this.color, font:this.font}; + }; + g.prototype.move = function(a, b, d) { + this._pos[0] += a; + this._pos[1] += b; + if (!d) { + for (d = 0; d < this._nodes.length; ++d) { + var h = this._nodes[d]; + h.pos[0] += a; + h.pos[1] += b; + } + } + }; + g.prototype.recomputeInsideNodes = function() { + this._nodes.length = 0; + for (var a = this.graph._nodes, b = new Float32Array(4), d = 0; d < a.length; ++d) { + var h = a[d]; + h.getBounding(b); + v(this._bounding, b) && this._nodes.push(h); + } + }; + g.prototype.isPointInside = m.prototype.isPointInside; + g.prototype.setDirtyCanvas = m.prototype.setDirtyCanvas; + e.DragAndScale = u; + u.prototype.bindEvents = function(a) { + this.last_mouse = new Float32Array(2); + this._binded_mouse_callback = this.onMouse.bind(this); + a.addEventListener("mousedown", this._binded_mouse_callback); + a.addEventListener("mousemove", this._binded_mouse_callback); + a.addEventListener("mousewheel", this._binded_mouse_callback, !1); + a.addEventListener("wheel", this._binded_mouse_callback, !1); + }; + u.prototype.computeVisibleArea = function() { + if (this.element) { + var a = -this.offset[0], b = -this.offset[1], d = a + this.element.width / this.scale, h = b + this.element.height / this.scale; + this.visible_area[0] = a; + this.visible_area[1] = b; + this.visible_area[2] = d - a; + this.visible_area[3] = h - b; + } else { + this.visible_area[0] = this.visible_area[1] = this.visible_area[2] = this.visible_area[3] = 0; + } + }; + u.prototype.onMouse = function(a) { + if (this.enabled) { + var b = this.element, d = b.getBoundingClientRect(), h = a.clientX - d.left; + d = a.clientY - d.top; + a.canvasx = h; + a.canvasy = d; + a.dragging = this.dragging; + var f = !1; + this.onmouse && (f = this.onmouse(a)); + if ("mousedown" == a.type) { + this.dragging = !0, b.removeEventListener("mousemove", this._binded_mouse_callback), document.body.addEventListener("mousemove", this._binded_mouse_callback), document.body.addEventListener("mouseup", this._binded_mouse_callback); + } else { + if ("mousemove" == a.type) { + f || (b = h - this.last_mouse[0], f = d - this.last_mouse[1], this.dragging && this.mouseDrag(b, f)); + } else { + if ("mouseup" == a.type) { + this.dragging = !1, document.body.removeEventListener("mousemove", this._binded_mouse_callback), document.body.removeEventListener("mouseup", this._binded_mouse_callback), b.addEventListener("mousemove", this._binded_mouse_callback); + } else { + if ("mousewheel" == a.type || "wheel" == a.type || "DOMMouseScroll" == a.type) { + a.eventType = "mousewheel", a.wheel = "wheel" == a.type ? -a.deltaY : null != a.wheelDeltaY ? a.wheelDeltaY : -60 * a.detail, a.delta = a.wheelDelta ? a.wheelDelta / 40 : a.deltaY ? -a.deltaY / 3 : 0, this.changeDeltaScale(1.0 + 0.05 * a.delta); + } + } + } + } + this.last_mouse[0] = h; + this.last_mouse[1] = d; + a.preventDefault(); + a.stopPropagation(); + return !1; + } + }; + u.prototype.toCanvasContext = function(a) { + a.scale(this.scale, this.scale); + a.translate(this.offset[0], this.offset[1]); + }; + u.prototype.convertOffsetToCanvas = function(a) { + return [(a[0] + this.offset[0]) * this.scale, (a[1] + this.offset[1]) * this.scale]; + }; + u.prototype.convertCanvasToOffset = function(a, b) { + b = b || [0, 0]; + b[0] = a[0] / this.scale - this.offset[0]; + b[1] = a[1] / this.scale - this.offset[1]; + return b; + }; + u.prototype.mouseDrag = function(a, b) { + this.offset[0] += a / this.scale; + this.offset[1] += b / this.scale; + if (this.onredraw) { + this.onredraw(this); + } + }; + u.prototype.changeScale = function(a, b) { + a < this.min_scale ? a = this.min_scale : a > this.max_scale && (a = this.max_scale); + if (a != this.scale && this.element) { + var d = this.element.getBoundingClientRect(); + if (d && (b = b || [0.5 * d.width, 0.5 * d.height], d = this.convertCanvasToOffset(b), this.scale = a, 0.01 > Math.abs(this.scale - 1) && (this.scale = 1), a = this.convertCanvasToOffset(b), a = [a[0] - d[0], a[1] - d[1]], this.offset[0] += a[0], this.offset[1] += a[1], this.onredraw)) { + this.onredraw(this); + } + } + }; + u.prototype.changeDeltaScale = function(a, b) { + this.changeScale(this.scale * a, b); + }; + u.prototype.reset = function() { + this.scale = 1; + this.offset[0] = 0; + this.offset[1] = 0; + }; + w.LGraphCanvas = e.LGraphCanvas = l; + l.link_type_colors = {"-1":e.EVENT_LINK_COLOR, number:"#AAA", node:"#DCA"}; + l.gradients = {}; + l.prototype.clear = function() { + this.fps = this.render_time = this.last_draw_time = this.frame = 0; + this.dragging_rectangle = null; + this.selected_nodes = {}; + this.selected_group = null; + this.visible_nodes = []; + this.connecting_node = this.node_capturing_input = this.node_over = this.node_dragged = null; + this.highlighted_links = {}; + this.dirty_bgcanvas = this.dirty_canvas = !0; + this.node_widget = this.node_in_panel = this.dirty_area = null; + this.last_mouse = [0, 0]; + this.last_mouseclick = 0; + this.visible_area.set([0, 0, 0, 0]); + if (this.onClear) { + this.onClear(); + } + }; + l.prototype.setGraph = function(a, b) { + this.graph != a && (b || this.clear(), !a && this.graph ? this.graph.detachCanvas(this) : (a.attachCanvas(this), this._graph_stack && (this._graph_stack = null), this.setDirty(!0, !0))); + }; + l.prototype.openSubgraph = function(a) { + if (!a) { + throw "graph cannot be null"; + } + if (this.graph == a) { + throw "graph cannot be the same"; + } + this.clear(); + this.graph && (this._graph_stack || (this._graph_stack = []), this._graph_stack.push(this.graph)); + a.attachCanvas(this); + this.setDirty(!0, !0); + }; + l.prototype.closeSubgraph = function() { + if (this._graph_stack && 0 != this._graph_stack.length) { + var a = this.graph._subgraph_node, b = this._graph_stack.pop(); + this.selected_nodes = {}; + this.highlighted_links = {}; + b.attachCanvas(this); + this.setDirty(!0, !0); + a && (this.centerOnNode(a), this.selectNodes([a])); + } + }; + l.prototype.getCurrentGraph = function() { + return this.graph; + }; + l.prototype.setCanvas = function(a, b) { + if (a && a.constructor === String && (a = document.getElementById(a), !a)) { + throw "Error creating LiteGraph canvas: Canvas not found"; + } + if (a !== this.canvas && (!a && this.canvas && (b || this.unbindEvents()), this.canvas = a, this.ds.element = a)) { + a.className += " lgraphcanvas"; + a.data = this; + a.tabindex = "1"; + this.bgcanvas = null; + this.bgcanvas || (this.bgcanvas = document.createElement("canvas"), this.bgcanvas.width = this.canvas.width, this.bgcanvas.height = this.canvas.height); + if (null == a.getContext) { + if ("canvas" != a.localName) { + throw "Element supplied for LGraphCanvas must be a element, you passed a " + a.localName; + } + throw "This browser doesn't support Canvas"; + } + null == (this.ctx = a.getContext("2d")) && (a.webgl_enabled || console.warn("This canvas seems to be WebGL, enabling WebGL renderer"), this.enableWebGL()); + this._mousemove_callback = this.processMouseMove.bind(this); + this._mouseup_callback = this.processMouseUp.bind(this); + b || this.bindEvents(); + } + }; + l.prototype._doNothing = function(a) { + a.preventDefault(); + return !1; + }; + l.prototype._doReturnTrue = function(a) { + a.preventDefault(); + return !0; + }; + l.prototype.bindEvents = function() { + if (this._events_binded) { + console.warn("LGraphCanvas: events already binded"); + } else { + var a = this.canvas, b = this.getCanvasWindow().document; + this._mousedown_callback = this.processMouseDown.bind(this); + this._mousewheel_callback = this.processMouseWheel.bind(this); + a.addEventListener("mousedown", this._mousedown_callback, !0); + a.addEventListener("mousemove", this._mousemove_callback); + a.addEventListener("mousewheel", this._mousewheel_callback, !1); + a.addEventListener("contextmenu", this._doNothing); + a.addEventListener("DOMMouseScroll", this._mousewheel_callback, !1); + a.addEventListener("touchstart", this.touchHandler, !0); + a.addEventListener("touchmove", this.touchHandler, !0); + a.addEventListener("touchend", this.touchHandler, !0); + a.addEventListener("touchcancel", this.touchHandler, !0); + this._key_callback = this.processKey.bind(this); + a.addEventListener("keydown", this._key_callback, !0); + b.addEventListener("keyup", this._key_callback, !0); + this._ondrop_callback = this.processDrop.bind(this); + a.addEventListener("dragover", this._doNothing, !1); + a.addEventListener("dragend", this._doNothing, !1); + a.addEventListener("drop", this._ondrop_callback, !1); + a.addEventListener("dragenter", this._doReturnTrue, !1); + this._events_binded = !0; + } + }; + l.prototype.unbindEvents = function() { + if (this._events_binded) { + var a = this.getCanvasWindow().document; + this.canvas.removeEventListener("mousedown", this._mousedown_callback); + this.canvas.removeEventListener("mousewheel", this._mousewheel_callback); + this.canvas.removeEventListener("DOMMouseScroll", this._mousewheel_callback); + this.canvas.removeEventListener("keydown", this._key_callback); + a.removeEventListener("keyup", this._key_callback); + this.canvas.removeEventListener("contextmenu", this._doNothing); + this.canvas.removeEventListener("drop", this._ondrop_callback); + this.canvas.removeEventListener("dragenter", this._doReturnTrue); + this.canvas.removeEventListener("touchstart", this.touchHandler); + this.canvas.removeEventListener("touchmove", this.touchHandler); + this.canvas.removeEventListener("touchend", this.touchHandler); + this.canvas.removeEventListener("touchcancel", this.touchHandler); + this._ondrop_callback = this._key_callback = this._mousewheel_callback = this._mousedown_callback = null; + this._events_binded = !1; + } else { + console.warn("LGraphCanvas: no events binded"); + } + }; + l.getFileExtension = function(a) { + var b = a.indexOf("?"); + -1 != b && (a = a.substr(0, b)); + b = a.lastIndexOf("."); + return -1 == b ? "" : a.substr(b + 1).toLowerCase(); + }; + l.prototype.enableWebGL = function() { + this.gl = this.ctx = enableWebGLCanvas(this.canvas); + this.ctx.webgl = !0; + this.bgcanvas = this.canvas; + this.bgctx = this.gl; + this.canvas.webgl_enabled = !0; + }; + l.prototype.setDirty = function(a, b) { + a && (this.dirty_canvas = !0); + b && (this.dirty_bgcanvas = !0); + }; + l.prototype.getCanvasWindow = function() { + if (!this.canvas) { + return window; + } + var a = this.canvas.ownerDocument; + return a.defaultView || a.parentWindow; + }; + l.prototype.startRendering = function() { + function a() { + this.pause_rendering || this.draw(); + var b = this.getCanvasWindow(); + this.is_rendering && b.requestAnimationFrame(a.bind(this)); + } + this.is_rendering || (this.is_rendering = !0, a.call(this)); + }; + l.prototype.stopRendering = function() { + this.is_rendering = !1; + }; + l.prototype.processMouseDown = function(a) { + if (this.graph) { + this.adjustMouseEvent(a); + var b = this.getCanvasWindow(); + l.active_canvas = this; + this.canvas.removeEventListener("mousemove", this._mousemove_callback); + b.document.addEventListener("mousemove", this._mousemove_callback, !0); + b.document.addEventListener("mouseup", this._mouseup_callback, !0); + var d = this.graph.getNodeOnPos(a.canvasX, a.canvasY, this.visible_nodes, 5), h = !1, f = 300 > e.getTime() - this.last_mouseclick; + this.canvas_mouse[0] = a.canvasX; + this.canvas_mouse[1] = a.canvasY; + this.canvas.focus(); + e.closeAllContextMenus(b); + if (!this.onMouse || 1 != this.onMouse(a)) { + if (1 == a.which) { + a.ctrlKey && (this.dragging_rectangle = new Float32Array(4), this.dragging_rectangle[0] = a.canvasX, this.dragging_rectangle[1] = a.canvasY, this.dragging_rectangle[2] = 1, this.dragging_rectangle[3] = 1, h = !0); + var c = !1; + if (d && this.allow_interaction && !h && !this.read_only) { + this.live_mode || d.flags.pinned || this.bringToFront(d); + if (!this.connecting_node && !d.flags.collapsed && !this.live_mode) { + if (!h && !1 !== d.resizable && y(a.canvasX, a.canvasY, d.pos[0] + d.size[0] - 5, d.pos[1] + d.size[1] - 5, 10, 10)) { + this.resizing_node = d, this.canvas.style.cursor = "se-resize", h = !0; + } else { + if (d.outputs) { + for (var k = 0, n = d.outputs.length; k < n; ++k) { + var q = d.outputs[k], g = d.getConnectionPos(!1, k); + if (y(a.canvasX, a.canvasY, g[0] - 15, g[1] - 10, 30, 20)) { + this.connecting_node = d; + this.connecting_output = q; + this.connecting_pos = d.getConnectionPos(!1, k); + this.connecting_slot = k; + a.shiftKey && d.disconnectOutput(k); + if (f) { + if (d.onOutputDblClick) { + d.onOutputDblClick(k, a); + } + } else { + if (d.onOutputClick) { + d.onOutputClick(k, a); + } + } + h = !0; + break; + } + } + } + if (d.inputs) { + for (k = 0, n = d.inputs.length; k < n; ++k) { + if (q = d.inputs[k], g = d.getConnectionPos(!0, k), y(a.canvasX, a.canvasY, g[0] - 15, g[1] - 10, 30, 20)) { + if (f) { + if (d.onInputDblClick) { + d.onInputDblClick(k, a); + } + } else { + if (d.onInputClick) { + d.onInputClick(k, a); + } + } + if (null !== q.link) { + h = this.graph.links[q.link]; + d.disconnectInput(k); + if (this.allow_reconnect_links || a.shiftKey) { + this.connecting_node = this.graph._nodes_by_id[h.origin_id], this.connecting_slot = h.origin_slot, this.connecting_output = this.connecting_node.outputs[this.connecting_slot], this.connecting_pos = this.connecting_node.getConnectionPos(!1, this.connecting_slot); + } + h = this.dirty_bgcanvas = !0; + } + } + } + } + } + } + if (!h) { + k = !1; + if (n = this.processNodeWidgets(d, this.canvas_mouse, a)) { + k = !0, this.node_widget = [d, n]; + } + if (f && this.selected_nodes[d.id]) { + if (d.onDblClick) { + d.onDblClick(a, [a.canvasX - d.pos[0], a.canvasY - d.pos[1]], this); + } + this.processNodeDblClicked(d); + k = !0; + } + d.onMouseDown && d.onMouseDown(a, [a.canvasX - d.pos[0], a.canvasY - d.pos[1]], this) ? k = !0 : this.live_mode && (k = c = !0); + k || (this.allow_dragnodes && (this.node_dragged = d), this.selected_nodes[d.id] || this.processNodeSelected(d, a)); + this.dirty_canvas = !0; + } + } else { + if (!this.read_only) { + for (k = 0; k < this.visible_links.length; ++k) { + if (d = this.visible_links[k], c = d._pos, !(!c || a.canvasX < c[0] - 4 || a.canvasX > c[0] + 4 || a.canvasY < c[1] - 4 || a.canvasY > c[1] + 4)) { + this.showLinkMenu(d, a); + this.over_link_center = null; + break; + } + } + } + this.selected_group = this.graph.getGroupOnPos(a.canvasX, a.canvasY); + this.selected_group_resizing = !1; + this.selected_group && !this.read_only && (a.ctrlKey && (this.dragging_rectangle = null), 10 > B([a.canvasX, a.canvasY], [this.selected_group.pos[0] + this.selected_group.size[0], this.selected_group.pos[1] + this.selected_group.size[1]]) * this.ds.scale ? this.selected_group_resizing = !0 : this.selected_group.recomputeInsideNodes()); + f && !this.read_only && this.allow_searchbox && this.showSearchBox(a); + c = !0; + } + !h && c && this.allow_dragcanvas && (this.dragging_canvas = !0); + } else { + 2 != a.which && 3 == a.which && (this.read_only || this.processContextMenu(d, a)); + } + this.last_mouse[0] = a.localX; + this.last_mouse[1] = a.localY; + this.last_mouseclick = e.getTime(); + this.last_mouse_dragging = !0; + this.graph.change(); + (!b.document.activeElement || "input" != b.document.activeElement.nodeName.toLowerCase() && "textarea" != b.document.activeElement.nodeName.toLowerCase()) && a.preventDefault(); + a.stopPropagation(); + if (this.onMouseDown) { + this.onMouseDown(a); + } + return !1; + } + } + }; + l.prototype.processMouseMove = function(a) { + this.autoresize && this.resize(); + if (this.graph) { + l.active_canvas = this; + this.adjustMouseEvent(a); + var b = [a.localX, a.localY], d = [b[0] - this.last_mouse[0], b[1] - this.last_mouse[1]]; + this.last_mouse = b; + this.canvas_mouse[0] = a.canvasX; + this.canvas_mouse[1] = a.canvasY; + a.dragging = this.last_mouse_dragging; + this.node_widget && (this.processNodeWidgets(this.node_widget[0], this.canvas_mouse, a, this.node_widget[1]), this.dirty_canvas = !0); + if (this.dragging_rectangle) { + this.dragging_rectangle[2] = a.canvasX - this.dragging_rectangle[0], this.dragging_rectangle[3] = a.canvasY - this.dragging_rectangle[1], this.dirty_canvas = !0; + } else { + if (this.selected_group && !this.read_only) { + this.selected_group_resizing ? this.selected_group.size = [a.canvasX - this.selected_group.pos[0], a.canvasY - this.selected_group.pos[1]] : (this.selected_group.move(d[0] / this.ds.scale, d[1] / this.ds.scale, a.ctrlKey), this.selected_group._nodes.length && (this.dirty_canvas = !0)), this.dirty_bgcanvas = !0; + } else { + if (this.dragging_canvas) { + this.ds.offset[0] += d[0] / this.ds.scale, this.ds.offset[1] += d[1] / this.ds.scale, this.dirty_bgcanvas = this.dirty_canvas = !0; + } else { + if (this.allow_interaction && !this.read_only) { + this.connecting_node && (this.dirty_canvas = !0); + var h = this.graph.getNodeOnPos(a.canvasX, a.canvasY, this.visible_nodes); + b = 0; + for (var f = this.graph._nodes.length; b < f; ++b) { + if (this.graph._nodes[b].mouseOver && h != this.graph._nodes[b]) { + this.graph._nodes[b].mouseOver = !1; + if (this.node_over && this.node_over.onMouseLeave) { + this.node_over.onMouseLeave(a); + } + this.node_over = null; + this.dirty_canvas = !0; + } + } + if (h) { + if (!h.mouseOver && (h.mouseOver = !0, this.node_over = h, this.dirty_canvas = !0, h.onMouseEnter)) { + h.onMouseEnter(a); + } + if (h.onMouseMove) { + h.onMouseMove(a, [a.canvasX - h.pos[0], a.canvasY - h.pos[1]], this); + } + if (this.connecting_node && (f = this._highlight_input || [0, 0], !this.isOverNodeBox(h, a.canvasX, a.canvasY))) { + var c = this.isOverNodeInput(h, a.canvasX, a.canvasY, f); + -1 != c && h.inputs[c] ? e.isValidConnection(this.connecting_output.type, h.inputs[c].type) && (this._highlight_input = f) : this._highlight_input = null; + } + this.canvas && (y(a.canvasX, a.canvasY, h.pos[0] + h.size[0] - 5, h.pos[1] + h.size[1] - 5, 5, 5) ? this.canvas.style.cursor = "se-resize" : this.canvas.style.cursor = "crosshair"); + } else { + f = null; + for (b = 0; b < this.visible_links.length; ++b) { + c = this.visible_links[b]; + var k = c._pos; + if (!(!k || a.canvasX < k[0] - 4 || a.canvasX > k[0] + 4 || a.canvasY < k[1] - 4 || a.canvasY > k[1] + 4)) { + f = c; + break; + } + } + f != this.over_link_center && (this.over_link_center = f, this.dirty_canvas = !0); + this.canvas && (this.canvas.style.cursor = ""); + } + if (this.node_capturing_input && this.node_capturing_input != h && this.node_capturing_input.onMouseMove) { + this.node_capturing_input.onMouseMove(a, [a.canvasX - this.node_capturing_input.pos[0], a.canvasY - this.node_capturing_input.pos[1]], this); + } + if (this.node_dragged && !this.live_mode) { + for (b in this.selected_nodes) { + h = this.selected_nodes[b], h.pos[0] += d[0] / this.ds.scale, h.pos[1] += d[1] / this.ds.scale; + } + this.dirty_bgcanvas = this.dirty_canvas = !0; + } + if (this.resizing_node && !this.live_mode) { + this.resizing_node.size[0] = a.canvasX - this.resizing_node.pos[0]; + this.resizing_node.size[1] = a.canvasY - this.resizing_node.pos[1]; + d = Math.max(this.resizing_node.inputs ? this.resizing_node.inputs.length : 0, this.resizing_node.outputs ? this.resizing_node.outputs.length : 0); + this.resizing_node.size[0] < e.NODE_MIN_WIDTH && (this.resizing_node.size[0] = e.NODE_MIN_WIDTH); + h = this.resizing_node.widgets; + c = 0; + if (h && h.length) { + b = 0; + for (f = h.length; b < f; ++b) { + c = h[b].computeSize ? c + (h[b].computeSize(this.resizing_node.size[0])[1] + 4) : c + (e.NODE_WIDGET_HEIGHT + 4); + } + c += 8; + } + b = d * e.NODE_SLOT_HEIGHT + c; + this.resizing_node.size[1] < b && (this.resizing_node.size[1] = b); + this.canvas.style.cursor = "se-resize"; + this.dirty_bgcanvas = this.dirty_canvas = !0; + } + } + } + } + } + a.preventDefault(); + return !1; + } + }; + l.prototype.processMouseUp = function(a) { + if (this.graph) { + var b = this.getCanvasWindow().document; + l.active_canvas = this; + b.removeEventListener("mousemove", this._mousemove_callback, !0); + this.canvas.addEventListener("mousemove", this._mousemove_callback, !0); + b.removeEventListener("mouseup", this._mouseup_callback, !0); + this.adjustMouseEvent(a); + b = e.getTime(); + a.click_time = b - this.last_mouseclick; + this.last_mouse_dragging = !1; + if (1 == a.which) { + if (this.node_widget && this.processNodeWidgets(this.node_widget[0], this.canvas_mouse, a), this.node_widget = null, this.selected_group && (this.selected_group.move(this.selected_group.pos[0] - Math.round(this.selected_group.pos[0]), this.selected_group.pos[1] - Math.round(this.selected_group.pos[1]), a.ctrlKey), this.selected_group.pos[0] = Math.round(this.selected_group.pos[0]), this.selected_group.pos[1] = Math.round(this.selected_group.pos[1]), this.selected_group._nodes.length && (this.dirty_canvas = + !0), this.selected_group = null), this.selected_group_resizing = !1, this.dragging_rectangle) { + if (this.graph) { + b = this.graph._nodes; + var d = new Float32Array(4); + this.deselectAllNodes(); + var h = Math.abs(this.dragging_rectangle[2]), f = Math.abs(this.dragging_rectangle[3]), c = 0 > this.dragging_rectangle[3] ? this.dragging_rectangle[1] - f : this.dragging_rectangle[1]; + this.dragging_rectangle[0] = 0 > this.dragging_rectangle[2] ? this.dragging_rectangle[0] - h : this.dragging_rectangle[0]; + this.dragging_rectangle[1] = c; + this.dragging_rectangle[2] = h; + this.dragging_rectangle[3] = f; + f = []; + for (c = 0; c < b.length; ++c) { + h = b[c], h.getBounding(d), v(this.dragging_rectangle, d) && f.push(h); + } + f.length && this.selectNodes(f); + } + this.dragging_rectangle = null; + } else { + if (this.connecting_node) { + this.dirty_bgcanvas = this.dirty_canvas = !0; + if (h = this.graph.getNodeOnPos(a.canvasX, a.canvasY, this.visible_nodes)) { + this.connecting_output.type == e.EVENT && this.isOverNodeBox(h, a.canvasX, a.canvasY) ? this.connecting_node.connect(this.connecting_slot, h, e.EVENT) : (b = this.isOverNodeInput(h, a.canvasX, a.canvasY), -1 != b ? this.connecting_node.connect(this.connecting_slot, h, b) : (b = h.getInputInfo(0), this.connecting_output.type == e.EVENT ? this.connecting_node.connect(this.connecting_slot, h, e.EVENT) : b && !b.link && e.isValidConnection(b.type && this.connecting_output.type) && this.connecting_node.connect(this.connecting_slot, + h, 0))); + } + this.connecting_node = this.connecting_pos = this.connecting_output = null; + this.connecting_slot = -1; + } else { + if (this.resizing_node) { + this.dirty_bgcanvas = this.dirty_canvas = !0, this.resizing_node = null; + } else { + if (this.node_dragged) { + (h = this.node_dragged) && 300 > a.click_time && y(a.canvasX, a.canvasY, h.pos[0], h.pos[1] - e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT) && h.collapse(); + this.dirty_bgcanvas = this.dirty_canvas = !0; + this.node_dragged.pos[0] = Math.round(this.node_dragged.pos[0]); + this.node_dragged.pos[1] = Math.round(this.node_dragged.pos[1]); + this.graph.config.align_to_grid && this.node_dragged.alignToGrid(); + if (this.onNodeMoved) { + this.onNodeMoved(this.node_dragged); + } + this.node_dragged = null; + } else { + h = this.graph.getNodeOnPos(a.canvasX, a.canvasY, this.visible_nodes); + !h && 300 > a.click_time && this.deselectAllNodes(); + this.dirty_canvas = !0; + this.dragging_canvas = !1; + if (this.node_over && this.node_over.onMouseUp) { + this.node_over.onMouseUp(a, [a.canvasX - this.node_over.pos[0], a.canvasY - this.node_over.pos[1]], this); + } + if (this.node_capturing_input && this.node_capturing_input.onMouseUp) { + this.node_capturing_input.onMouseUp(a, [a.canvasX - this.node_capturing_input.pos[0], a.canvasY - this.node_capturing_input.pos[1]]); + } + } + } + } + } + } else { + 2 == a.which ? (this.dirty_canvas = !0, this.dragging_canvas = !1) : 3 == a.which && (this.dirty_canvas = !0, this.dragging_canvas = !1); + } + this.graph.change(); + a.stopPropagation(); + a.preventDefault(); + return !1; + } + }; + l.prototype.processMouseWheel = function(a) { + if (this.graph && this.allow_dragcanvas) { + var b = null != a.wheelDeltaY ? a.wheelDeltaY : -60 * a.detail; + this.adjustMouseEvent(a); + var d = this.ds.scale; + 0 < b ? d *= 1.1 : 0 > b && (d *= 1 / 1.1); + this.ds.changeScale(d, [a.localX, a.localY]); + this.graph.change(); + a.preventDefault(); + return !1; + } + }; + l.prototype.isOverNodeBox = function(a, b, d) { + var h = e.NODE_TITLE_HEIGHT; + return y(b, d, a.pos[0] + 2, a.pos[1] + 2 - h, h - 4, h - 4) ? !0 : !1; + }; + l.prototype.isOverNodeInput = function(a, b, d, h) { + if (a.inputs) { + for (var f = 0, e = a.inputs.length; f < e; ++f) { + var c = a.getConnectionPos(!0, f); + if (a.horizontal ? y(b, d, c[0] - 5, c[1] - 10, 10, 20) : y(b, d, c[0] - 10, c[1] - 5, 40, 10)) { + return h && (h[0] = c[0], h[1] = c[1]), f; + } + } + } + return -1; + }; + l.prototype.processKey = function(a) { + if (this.graph) { + var b = !1; + if ("input" != a.target.localName) { + if ("keydown" == a.type) { + if (32 == a.keyCode && (b = this.dragging_canvas = !0), 65 == a.keyCode && a.ctrlKey && (this.selectNodes(), b = !0), "KeyC" == a.code && (a.metaKey || a.ctrlKey) && !a.shiftKey && this.selected_nodes && (this.copyToClipboard(), b = !0), "KeyV" != a.code || !a.metaKey && !a.ctrlKey || a.shiftKey || this.pasteFromClipboard(), 46 != a.keyCode && 8 != a.keyCode || "input" == a.target.localName || "textarea" == a.target.localName || (this.deleteSelectedNodes(), b = !0), this.selected_nodes) { + for (var d in this.selected_nodes) { + if (this.selected_nodes[d].onKeyDown) { + this.selected_nodes[d].onKeyDown(a); + } + } + } + } else { + if ("keyup" == a.type && (32 == a.keyCode && (this.dragging_canvas = !1), this.selected_nodes)) { + for (d in this.selected_nodes) { + if (this.selected_nodes[d].onKeyUp) { + this.selected_nodes[d].onKeyUp(a); + } + } + } + } + this.graph.change(); + if (b) { + return a.preventDefault(), a.stopImmediatePropagation(), !1; + } + } + } + }; + l.prototype.copyToClipboard = function() { + var a = {nodes:[], links:[]}, b = 0, d = [], h; + for (h in this.selected_nodes) { + var f = this.selected_nodes[h]; + f._relative_id = b; + d.push(f); + b += 1; + } + for (h = 0; h < d.length; ++h) { + if (f = d[h], b = f.clone()) { + if (a.nodes.push(b.serialize()), f.inputs && f.inputs.length) { + for (b = 0; b < f.inputs.length; ++b) { + var e = f.inputs[b]; + if (e && null != e.link && (e = this.graph.links[e.link])) { + var c = this.graph.getNodeById(e.origin_id); + c && this.selected_nodes[c.id] && a.links.push([c._relative_id, e.origin_slot, f._relative_id, e.target_slot]); + } + } + } + } else { + console.warn("node type not found: " + f.type); + } + } + localStorage.setItem("litegrapheditor_clipboard", JSON.stringify(a)); + }; + l.prototype.pasteFromClipboard = function() { + var a = localStorage.getItem("litegrapheditor_clipboard"); + if (a) { + a = JSON.parse(a); + for (var b = [], d = 0; d < a.nodes.length; ++d) { + var h = a.nodes[d], f = e.createNode(h.type); + f && (f.configure(h), f.pos[0] += 5, f.pos[1] += 5, this.graph.add(f), b.push(f)); + } + for (d = 0; d < a.links.length; ++d) { + h = a.links[d]; + f = b[h[0]]; + var c = b[h[2]]; + f && c ? f.connect(h[1], c, h[3]) : console.warn("Warning, nodes missing on pasting"); + } + this.selectNodes(b); + } + }; + l.prototype.processDrop = function(a) { + a.preventDefault(); + this.adjustMouseEvent(a); + var b = [a.canvasX, a.canvasY], d = this.graph.getNodeOnPos(b[0], b[1]); + if (d) { + if ((d.onDropFile || d.onDropData) && (b = a.dataTransfer.files) && b.length) { + for (var h = 0; h < b.length; h++) { + var f = a.dataTransfer.files[0], e = f.name; + l.getFileExtension(e); + if (d.onDropFile) { + d.onDropFile(f); + } + if (d.onDropData) { + var c = new FileReader; + c.onload = function(a) { + d.onDropData(a.target.result, e, f); + }; + var k = f.type.split("/")[0]; + "text" == k || "" == k ? c.readAsText(f) : "image" == k ? c.readAsDataURL(f) : c.readAsArrayBuffer(f); + } + } + } + return d.onDropItem && d.onDropItem(event) ? !0 : this.onDropItem ? this.onDropItem(event) : !1; + } + b = null; + this.onDropItem && (b = this.onDropItem(event)); + b || this.checkDropItem(a); + }; + l.prototype.checkDropItem = function(a) { + if (a.dataTransfer.files.length) { + var b = a.dataTransfer.files[0], d = l.getFileExtension(b.name).toLowerCase(); + if (d = e.node_types_by_file_extension[d]) { + if (d = e.createNode(d.type), d.pos = [a.canvasX, a.canvasY], this.graph.add(d), d.onDropFile) { + d.onDropFile(b); + } + } + } + }; + l.prototype.processNodeDblClicked = function(a) { + if (this.onShowNodePanel) { + this.onShowNodePanel(a); + } + if (this.onNodeDblClicked) { + this.onNodeDblClicked(a); + } + this.setDirty(!0); + }; + l.prototype.processNodeSelected = function(a, b) { + this.selectNode(a, b && b.shiftKey); + if (this.onNodeSelected) { + this.onNodeSelected(a); + } + }; + l.prototype.selectNode = function(a, b) { + null == a ? this.deselectAllNodes() : this.selectNodes([a], b); + }; + l.prototype.selectNodes = function(a, b) { + b || this.deselectAllNodes(); + a = a || this.graph._nodes; + for (b = 0; b < a.length; ++b) { + var d = a[b]; + if (!d.is_selected) { + if (!d.is_selected && d.onSelected) { + d.onSelected(); + } + d.is_selected = !0; + this.selected_nodes[d.id] = d; + if (d.inputs) { + for (var h = 0; h < d.inputs.length; ++h) { + this.highlighted_links[d.inputs[h].link] = !0; + } + } + if (d.outputs) { + for (h = 0; h < d.outputs.length; ++h) { + var f = d.outputs[h]; + if (f.links) { + for (var e = 0; e < f.links.length; ++e) { + this.highlighted_links[f.links[e]] = !0; + } + } + } + } + } + } + if (this.onSelectionChange) { + this.onSelectionChange(this.selected_nodes); + } + this.setDirty(!0); + }; + l.prototype.deselectNode = function(a) { + if (a.is_selected) { + if (a.onDeselected) { + a.onDeselected(); + } + a.is_selected = !1; + if (this.onNodeDeselected) { + this.onNodeDeselected(a); + } + if (a.inputs) { + for (var b = 0; b < a.inputs.length; ++b) { + delete this.highlighted_links[a.inputs[b].link]; + } + } + if (a.outputs) { + for (b = 0; b < a.outputs.length; ++b) { + var d = a.outputs[b]; + if (d.links) { + for (var h = 0; h < d.links.length; ++h) { + delete this.highlighted_links[d.links[h]]; + } + } + } + } + } + }; + l.prototype.deselectAllNodes = function() { + if (this.graph) { + for (var a = this.graph._nodes, b = 0, d = a.length; b < d; ++b) { + var h = a[b]; + if (h.is_selected) { + if (h.onDeselected) { + h.onDeselected(); + } + h.is_selected = !1; + if (this.onNodeDeselected) { + this.onNodeDeselected(h); + } + } + } + this.selected_nodes = {}; + this.current_node = null; + this.highlighted_links = {}; + if (this.onSelectionChange) { + this.onSelectionChange(this.selected_nodes); + } + this.setDirty(!0); + } + }; + l.prototype.deleteSelectedNodes = function() { + for (var a in this.selected_nodes) { + var b = this.selected_nodes[a]; + if (b.inputs && b.inputs.length && b.outputs && b.outputs.length && e.isValidConnection(b.inputs[0].type, b.outputs[0].type) && b.inputs[0].link && b.outputs[0].links && b.outputs[0].links.length) { + var d = b.graph.links[b.inputs[0].link], h = b.graph.links[b.outputs[0].links[0]], f = b.getInputNode(0), c = b.getOutputNodes(0)[0]; + f && c && f.connect(d.origin_slot, c, h.target_slot); + } + this.graph.remove(b); + if (this.onNodeDeselected) { + this.onNodeDeselected(b); + } + } + this.selected_nodes = {}; + this.current_node = null; + this.highlighted_links = {}; + this.setDirty(!0); + }; + l.prototype.centerOnNode = function(a) { + this.ds.offset[0] = -a.pos[0] - 0.5 * a.size[0] + 0.5 * this.canvas.width / this.ds.scale; + this.ds.offset[1] = -a.pos[1] - 0.5 * a.size[1] + 0.5 * this.canvas.height / this.ds.scale; + this.setDirty(!0, !0); + }; + l.prototype.adjustMouseEvent = function(a) { + if (this.canvas) { + var b = this.canvas.getBoundingClientRect(); + a.localX = a.clientX - b.left; + a.localY = a.clientY - b.top; + } else { + a.localX = a.clientX, a.localY = a.clientY; + } + a.deltaX = a.localX - this.last_mouse_position[0]; + a.deltaY = a.localY - this.last_mouse_position[1]; + this.last_mouse_position[0] = a.localX; + this.last_mouse_position[1] = a.localY; + a.canvasX = a.localX / this.ds.scale - this.ds.offset[0]; + a.canvasY = a.localY / this.ds.scale - this.ds.offset[1]; + }; + l.prototype.setZoom = function(a, b) { + this.ds.changeScale(a, b); + this.dirty_bgcanvas = this.dirty_canvas = !0; + }; + l.prototype.convertOffsetToCanvas = function(a, b) { + return this.ds.convertOffsetToCanvas(a, b); + }; + l.prototype.convertCanvasToOffset = function(a, b) { + return this.ds.convertCanvasToOffset(a, b); + }; + l.prototype.convertEventToCanvasOffset = function(a) { + var b = this.canvas.getBoundingClientRect(); + return this.convertCanvasToOffset([a.clientX - b.left, a.clientY - b.top]); + }; + l.prototype.bringToFront = function(a) { + var b = this.graph._nodes.indexOf(a); + -1 != b && (this.graph._nodes.splice(b, 1), this.graph._nodes.push(a)); + }; + l.prototype.sendToBack = function(a) { + var b = this.graph._nodes.indexOf(a); + -1 != b && (this.graph._nodes.splice(b, 1), this.graph._nodes.unshift(a)); + }; + var C = new Float32Array(4); + l.prototype.computeVisibleNodes = function(a, b) { + b = b || []; + b.length = 0; + a = a || this.graph._nodes; + for (var d = 0, h = a.length; d < h; ++d) { + var f = a[d]; + (!this.live_mode || f.onDrawBackground || f.onDrawForeground) && v(this.visible_area, f.getBounding(C)) && b.push(f); + } + return b; + }; + l.prototype.draw = function(a, b) { + if (this.canvas) { + var d = e.getTime(); + this.render_time = 0.001 * (d - this.last_draw_time); + this.last_draw_time = d; + this.graph && this.ds.computeVisibleArea(); + (this.dirty_bgcanvas || b || this.always_render_background || this.graph && this.graph._last_trigger_time && 1000 > d - this.graph._last_trigger_time) && this.drawBackCanvas(); + (this.dirty_canvas || a) && this.drawFrontCanvas(); + this.fps = this.render_time ? 1.0 / this.render_time : 0; + this.frame += 1; + } + }; + l.prototype.drawFrontCanvas = function() { + this.dirty_canvas = !1; + this.ctx || (this.ctx = this.bgcanvas.getContext("2d")); + var a = this.ctx; + if (a) { + a.start2D && a.start2D(); + var b = this.canvas; + a.restore(); + a.setTransform(1, 0, 0, 1, 0, 0); + this.dirty_area && (a.save(), a.beginPath(), a.rect(this.dirty_area[0], this.dirty_area[1], this.dirty_area[2], this.dirty_area[3]), a.clip()); + this.clear_background && a.clearRect(0, 0, b.width, b.height); + this.bgcanvas == this.canvas ? this.drawBackCanvas() : a.drawImage(this.bgcanvas, 0, 0); + if (this.onRender) { + this.onRender(b, a); + } + this.show_info && this.renderInfo(a); + if (this.graph) { + a.save(); + this.ds.toCanvasContext(a); + b = this.computeVisibleNodes(null, this.visible_nodes); + for (var d = 0; d < b.length; ++d) { + var h = b[d]; + a.save(); + a.translate(h.pos[0], h.pos[1]); + this.drawNode(h, a); + a.restore(); + } + this.render_execution_order && this.drawExecutionOrder(a); + this.graph.config.links_ontop && (this.live_mode || this.drawConnections(a)); + if (null != this.connecting_pos) { + a.lineWidth = this.connections_width; + switch(this.connecting_output.type) { + case e.EVENT: + b = e.EVENT_LINK_COLOR; + break; + default: + b = e.CONNECTING_LINK_COLOR; + } + this.renderLink(a, this.connecting_pos, [this.canvas_mouse[0], this.canvas_mouse[1]], null, !1, null, b, this.connecting_output.dir || (this.connecting_node.horizontal ? e.DOWN : e.RIGHT), e.CENTER); + a.beginPath(); + this.connecting_output.type === e.EVENT || this.connecting_output.shape === e.BOX_SHAPE ? a.rect(this.connecting_pos[0] - 6 + 0.5, this.connecting_pos[1] - 5 + 0.5, 14, 10) : a.arc(this.connecting_pos[0], this.connecting_pos[1], 4, 0, 2 * Math.PI); + a.fill(); + a.fillStyle = "#ffcc00"; + this._highlight_input && (a.beginPath(), a.arc(this._highlight_input[0], this._highlight_input[1], 6, 0, 2 * Math.PI), a.fill()); + } + this.dragging_rectangle && (a.strokeStyle = "#FFF", a.strokeRect(this.dragging_rectangle[0], this.dragging_rectangle[1], this.dragging_rectangle[2], this.dragging_rectangle[3])); + if (this.over_link_center && this.render_link_tooltip) { + this.drawLinkTooltip(a, this.over_link_center); + } else { + if (this.onDrawLinkTooltip) { + this.onDrawLinkTooltip(a, null); + } + } + if (this.onDrawForeground) { + this.onDrawForeground(a, this.visible_rect); + } + a.restore(); + } + if (this.onDrawOverlay) { + this.onDrawOverlay(a); + } + this.dirty_area && a.restore(); + a.finish2D && a.finish2D(); + } + }; + l.prototype.renderInfo = function(a, b, d) { + b = b || 0; + d = d || 0; + a.save(); + a.translate(b, d); + a.font = "10px Arial"; + a.fillStyle = "#888"; + this.graph ? (a.fillText("T: " + this.graph.globaltime.toFixed(2) + "s", 5, 13), a.fillText("I: " + this.graph.iteration, 5, 26), a.fillText("N: " + this.graph._nodes.length + " [" + this.visible_nodes.length + "]", 5, 39), a.fillText("V: " + this.graph._version, 5, 52), a.fillText("FPS:" + this.fps.toFixed(2), 5, 65)) : a.fillText("No graph selected", 5, 13); + a.restore(); + }; + l.prototype.drawBackCanvas = function() { + var a = this.bgcanvas; + if (a.width != this.canvas.width || a.height != this.canvas.height) { + a.width = this.canvas.width, a.height = this.canvas.height; + } + this.bgctx || (this.bgctx = this.bgcanvas.getContext("2d")); + var b = this.bgctx; + b.start && b.start(); + this.clear_background && b.clearRect(0, 0, a.width, a.height); + if (this._graph_stack && this._graph_stack.length) { + b.save(); + var d = this.graph._subgraph_node; + b.strokeStyle = d.bgcolor; + b.lineWidth = 10; + b.strokeRect(1, 1, a.width - 2, a.height - 2); + b.lineWidth = 1; + b.font = "40px Arial"; + b.textAlign = "center"; + b.fillStyle = d.bgcolor || "#AAA"; + for (var h = "", f = 1; f < this._graph_stack.length; ++f) { + h += this._graph_stack[f]._subgraph_node.getTitle() + " >> "; + } + b.fillText(h + d.getTitle(), 0.5 * a.width, 40); + b.restore(); + } + d = !1; + this.onRenderBackground && (d = this.onRenderBackground(a, b)); + b.restore(); + b.setTransform(1, 0, 0, 1, 0, 0); + this.visible_links.length = 0; + if (this.graph) { + b.save(); + this.ds.toCanvasContext(b); + if (this.background_image && 0.5 < this.ds.scale && !d) { + b.globalAlpha = this.zoom_modify_alpha ? (1.0 - 0.5 / this.ds.scale) * this.editor_alpha : this.editor_alpha; + b.imageSmoothingEnabled = b.mozImageSmoothingEnabled = b.imageSmoothingEnabled = !1; + if (!this._bg_img || this._bg_img.name != this.background_image) { + this._bg_img = new Image; + this._bg_img.name = this.background_image; + this._bg_img.src = this.background_image; + var e = this; + this._bg_img.onload = function() { + e.draw(!0, !0); + }; + } + d = null; + null == this._pattern && 0 < this._bg_img.width ? (d = b.createPattern(this._bg_img, "repeat"), this._pattern_img = this._bg_img, this._pattern = d) : d = this._pattern; + d && (b.fillStyle = d, b.fillRect(this.visible_area[0], this.visible_area[1], this.visible_area[2], this.visible_area[3]), b.fillStyle = "transparent"); + b.globalAlpha = 1.0; + b.imageSmoothingEnabled = b.mozImageSmoothingEnabled = b.imageSmoothingEnabled = !0; + } + this.graph._groups.length && !this.live_mode && this.drawGroups(a, b); + if (this.onDrawBackground) { + this.onDrawBackground(b, this.visible_area); + } + this.onBackgroundRender && (console.error("WARNING! onBackgroundRender deprecated, now is named onDrawBackground "), this.onBackgroundRender = null); + this.render_canvas_border && (b.strokeStyle = "#235", b.strokeRect(0, 0, a.width, a.height)); + this.render_connections_shadows ? (b.shadowColor = "#000", b.shadowOffsetX = 0, b.shadowOffsetY = 0, b.shadowBlur = 6) : b.shadowColor = "rgba(0,0,0,0)"; + this.live_mode || this.drawConnections(b); + b.shadowColor = "rgba(0,0,0,0)"; + b.restore(); + } + b.finish && b.finish(); + this.dirty_bgcanvas = !1; + this.dirty_canvas = !0; + }; + var D = new Float32Array(2); + l.prototype.drawNode = function(a, b) { + this.current_node = a; + var d = a.color || a.constructor.color || e.NODE_DEFAULT_COLOR, h = a.bgcolor || a.constructor.bgcolor || e.NODE_DEFAULT_BGCOLOR, f = 0.6 > this.ds.scale; + if (this.live_mode) { + if (!a.flags.collapsed && (b.shadowColor = "transparent", a.onDrawForeground)) { + a.onDrawForeground(b, this, this.canvas); + } + } else { + var c = this.editor_alpha; + b.globalAlpha = c; + this.render_shadows && !f ? (b.shadowColor = e.DEFAULT_SHADOW_COLOR, b.shadowOffsetX = 2 * this.ds.scale, b.shadowOffsetY = 2 * this.ds.scale, b.shadowBlur = 3 * this.ds.scale) : b.shadowColor = "transparent"; + if (!a.flags.collapsed || !a.onDrawCollapsed || 1 != a.onDrawCollapsed(b, this)) { + var k = a._shape || e.BOX_SHAPE; + D.set(a.size); + var n = a.horizontal; + if (a.flags.collapsed) { + b.font = this.inner_text_font; + var q = a.getTitle ? a.getTitle() : a.title; + null != q && (a._collapsed_width = Math.min(a.size[0], b.measureText(q).width + 2 * e.NODE_TITLE_HEIGHT), D[0] = a._collapsed_width, D[1] = 0); + } + a.clip_area && (b.save(), b.beginPath(), k == e.BOX_SHAPE ? b.rect(0, 0, D[0], D[1]) : k == e.ROUND_SHAPE ? b.roundRect(0, 0, D[0], D[1], 10) : k == e.CIRCLE_SHAPE && b.arc(0.5 * D[0], 0.5 * D[1], 0.5 * D[0], 0, 2 * Math.PI), b.clip()); + a.has_errors && (h = "red"); + this.drawNodeShape(a, b, D, d, h, a.is_selected, a.mouseOver); + b.shadowColor = "transparent"; + if (a.onDrawForeground) { + a.onDrawForeground(b, this, this.canvas); + } + b.textAlign = n ? "center" : "left"; + b.font = this.inner_text_font; + h = !f; + k = this.connecting_output; + b.lineWidth = 1; + q = 0; + var l = new Float32Array(2); + if (!a.flags.collapsed) { + if (a.inputs) { + for (d = 0; d < a.inputs.length; d++) { + var g = a.inputs[d]; + b.globalAlpha = c; + this.connecting_node && !e.isValidConnection(g.type, k.type) && (b.globalAlpha = 0.4 * c); + b.fillStyle = null != g.link ? g.color_on || this.default_connection_color.input_on : g.color_off || this.default_connection_color.input_off; + var r = a.getConnectionPos(!0, d, l); + r[0] -= a.pos[0]; + r[1] -= a.pos[1]; + q < r[1] + 0.5 * e.NODE_SLOT_HEIGHT && (q = r[1] + 0.5 * e.NODE_SLOT_HEIGHT); + b.beginPath(); + g.type === e.EVENT || g.shape === e.BOX_SHAPE ? n ? b.rect(r[0] - 5 + 0.5, r[1] - 8 + 0.5, 10, 14) : b.rect(r[0] - 6 + 0.5, r[1] - 5 + 0.5, 14, 10) : g.shape === e.ARROW_SHAPE ? (b.moveTo(r[0] + 8, r[1] + 0.5), b.lineTo(r[0] - 4, r[1] + 6 + 0.5), b.lineTo(r[0] - 4, r[1] - 6 + 0.5), b.closePath()) : f ? b.rect(r[0] - 4, r[1] - 4, 8, 8) : b.arc(r[0], r[1], 4, 0, 2 * Math.PI); + b.fill(); + if (h) { + var m = null != g.label ? g.label : g.name; + m && (b.fillStyle = e.NODE_TEXT_COLOR, n || g.dir == e.UP ? b.fillText(m, r[0], r[1] - 10) : b.fillText(m, r[0] + 10, r[1] + 5)); + } + } + } + this.connecting_node && (b.globalAlpha = 0.4 * c); + b.textAlign = n ? "center" : "right"; + b.strokeStyle = "black"; + if (a.outputs) { + for (d = 0; d < a.outputs.length; d++) { + if (g = a.outputs[d], r = a.getConnectionPos(!1, d, l), r[0] -= a.pos[0], r[1] -= a.pos[1], q < r[1] + 0.5 * e.NODE_SLOT_HEIGHT && (q = r[1] + 0.5 * e.NODE_SLOT_HEIGHT), b.fillStyle = g.links && g.links.length ? g.color_on || this.default_connection_color.output_on : g.color_off || this.default_connection_color.output_off, b.beginPath(), g.type === e.EVENT || g.shape === e.BOX_SHAPE ? n ? b.rect(r[0] - 5 + 0.5, r[1] - 8 + 0.5, 10, 14) : b.rect(r[0] - 6 + 0.5, r[1] - 5 + 0.5, 14, 10) : + g.shape === e.ARROW_SHAPE ? (b.moveTo(r[0] + 8, r[1] + 0.5), b.lineTo(r[0] - 4, r[1] + 6 + 0.5), b.lineTo(r[0] - 4, r[1] - 6 + 0.5), b.closePath()) : f ? b.rect(r[0] - 4, r[1] - 4, 8, 8) : b.arc(r[0], r[1], 4, 0, 2 * Math.PI), b.fill(), f || b.stroke(), h && (m = null != g.label ? g.label : g.name)) { + b.fillStyle = e.NODE_TEXT_COLOR, n || g.dir == e.DOWN ? b.fillText(m, r[0], r[1] - 8) : b.fillText(m, r[0] - 10, r[1] + 5); + } + } + } + b.textAlign = "left"; + b.globalAlpha = 1; + if (a.widgets) { + g = q; + if (n || a.widgets_up) { + g = 2; + } + null != a.widgets_start_y && (g = a.widgets_start_y); + this.drawNodeWidgets(a, g, b, this.node_widget && this.node_widget[0] == a ? this.node_widget[1] : null); + } + } else { + if (this.render_collapsed_slots) { + f = c = null; + if (a.inputs) { + for (d = 0; d < a.inputs.length; d++) { + if (g = a.inputs[d], null != g.link) { + c = g; + break; + } + } + } + if (a.outputs) { + for (d = 0; d < a.outputs.length; d++) { + g = a.outputs[d], g.links && g.links.length && (f = g); + } + } + c && (c = 0, d = -0.5 * e.NODE_TITLE_HEIGHT, n && (c = 0.5 * a._collapsed_width, d = -e.NODE_TITLE_HEIGHT), b.fillStyle = "#686", b.beginPath(), g.type === e.EVENT || g.shape === e.BOX_SHAPE ? b.rect(c - 7 + 0.5, d - 4, 14, 8) : g.shape === e.ARROW_SHAPE ? (b.moveTo(c + 8, d), b.lineTo(c + -4, d - 4), b.lineTo(c + -4, d + 4), b.closePath()) : b.arc(c, d, 4, 0, 2 * Math.PI), b.fill()); + f && (c = a._collapsed_width, d = -0.5 * e.NODE_TITLE_HEIGHT, n && (c = 0.5 * a._collapsed_width, d = 0), b.fillStyle = "#686", b.strokeStyle = "black", b.beginPath(), g.type === e.EVENT || g.shape === e.BOX_SHAPE ? b.rect(c - 7 + 0.5, d - 4, 14, 8) : g.shape === e.ARROW_SHAPE ? (b.moveTo(c + 6, d), b.lineTo(c - 6, d - 4), b.lineTo(c - 6, d + 4), b.closePath()) : b.arc(c, d, 4, 0, 2 * Math.PI), b.fill()); + } + } + a.clip_area && b.restore(); + b.globalAlpha = 1.0; + } + } + }; + l.prototype.drawLinkTooltip = function(a, b) { + var d = b._pos; + a.fillStyle = "black"; + a.beginPath(); + a.arc(d[0], d[1], 3, 0, 2 * Math.PI); + a.fill(); + if (null != b.data && (!this.onDrawLinkTooltip || 1 != this.onDrawLinkTooltip(a, b, this)) && (b = b.data, b = b.constructor === Number ? b.toFixed(2) : b.constructor === String ? '"' + b + '"' : b.constructor === Boolean ? String(b) : b.toToolTip ? b.toToolTip() : "[" + b.constructor.name + "]", null != b)) { + b = b.substr(0, 30); + a.font = "14px Courier New"; + var h = a.measureText(b).width + 20; + a.shadowColor = "black"; + a.shadowOffsetX = 2; + a.shadowOffsetY = 2; + a.shadowBlur = 3; + a.fillStyle = "#454"; + a.beginPath(); + a.roundRect(d[0] - 0.5 * h, d[1] - 15 - 24, h, 24, 3, 3); + a.moveTo(d[0] - 10, d[1] - 15); + a.lineTo(d[0] + 10, d[1] - 15); + a.lineTo(d[0], d[1] - 5); + a.fill(); + a.shadowColor = "transparent"; + a.textAlign = "center"; + a.fillStyle = "#CEC"; + a.fillText(b, d[0], d[1] - 15 - 24 * 0.3); + } + }; + var t = new Float32Array(4); + l.prototype.drawNodeShape = function(a, b, d, h, f, c, k) { + b.strokeStyle = h; + b.fillStyle = f; + f = e.NODE_TITLE_HEIGHT; + var n = 0.5 > this.ds.scale, q = a._shape || a.constructor.shape || e.ROUND_SHAPE, x = a.constructor.title_mode, g = !0; + x == e.TRANSPARENT_TITLE ? g = !1 : x == e.AUTOHIDE_TITLE && k && (g = !0); + t[0] = 0; + t[1] = g ? -f : 0; + t[2] = d[0] + 1; + t[3] = g ? d[1] + f : d[1]; + k = b.globalAlpha; + b.beginPath(); + q == e.BOX_SHAPE || n ? b.fillRect(t[0], t[1], t[2], t[3]) : q == e.ROUND_SHAPE || q == e.CARD_SHAPE ? b.roundRect(t[0], t[1], t[2], t[3], this.round_radius, q == e.CARD_SHAPE ? 0 : this.round_radius) : q == e.CIRCLE_SHAPE && b.arc(0.5 * d[0], 0.5 * d[1], 0.5 * d[0], 0, 2 * Math.PI); + b.fill(); + a.flags.collapsed || (b.shadowColor = "transparent", b.fillStyle = "rgba(0,0,0,0.2)", b.fillRect(0, -1, t[2], 2)); + b.shadowColor = "transparent"; + if (a.onDrawBackground) { + a.onDrawBackground(b, this, this.canvas); + } + if (g || x == e.TRANSPARENT_TITLE) { + if (a.onDrawTitleBar) { + a.onDrawTitleBar(b, f, d, this.ds.scale, h); + } else { + if (x != e.TRANSPARENT_TITLE && (a.constructor.title_color || this.render_title_colored)) { + g = a.constructor.title_color || h; + a.flags.collapsed && (b.shadowColor = e.DEFAULT_SHADOW_COLOR); + if (this.use_gradients) { + var r = l.gradients[g]; + r || (r = l.gradients[g] = b.createLinearGradient(0, 0, 400, 0), r.addColorStop(0, g), r.addColorStop(1, "#000")); + b.fillStyle = r; + } else { + b.fillStyle = g; + } + b.beginPath(); + q == e.BOX_SHAPE || n ? b.rect(0, -f, d[0] + 1, f) : (q == e.ROUND_SHAPE || q == e.CARD_SHAPE) && b.roundRect(0, -f, d[0] + 1, f, this.round_radius, a.flags.collapsed ? this.round_radius : 0); + b.fill(); + b.shadowColor = "transparent"; + } + } + if (a.onDrawTitleBox) { + a.onDrawTitleBox(b, f, d, this.ds.scale); + } else { + q == e.ROUND_SHAPE || q == e.CIRCLE_SHAPE || q == e.CARD_SHAPE ? (n && (b.fillStyle = "black", b.beginPath(), b.arc(0.5 * f, -0.5 * f, 6, 0, 2 * Math.PI), b.fill()), b.fillStyle = a.boxcolor || e.NODE_DEFAULT_BOXCOLOR, n ? b.fillRect(0.5 * f - 5, -0.5 * f - 5, 10, 10) : (b.beginPath(), b.arc(0.5 * f, -0.5 * f, 5, 0, 2 * Math.PI), b.fill())) : (n && (b.fillStyle = "black", b.fillRect(0.5 * (f - 10) - 1, -0.5 * (f + 10) - 1, 12, 12)), b.fillStyle = a.boxcolor || e.NODE_DEFAULT_BOXCOLOR, b.fillRect(0.5 * + (f - 10), -0.5 * (f + 10), 10, 10)); + } + b.globalAlpha = k; + if (a.onDrawTitleText) { + a.onDrawTitleText(b, f, d, this.ds.scale, this.title_text_font, c); + } + !n && (b.font = this.title_text_font, n = String(a.getTitle())) && (b.fillStyle = c ? "white" : a.constructor.title_text_color || this.node_title_color, a.flags.collapsed ? (b.textAlign = "left", b.measureText(n), b.fillText(n.substr(0, 20), f, e.NODE_TITLE_TEXT_Y - f), b.textAlign = "left") : (b.textAlign = "left", b.fillText(n, f, e.NODE_TITLE_TEXT_Y - f))); + if (a.onDrawTitle) { + a.onDrawTitle(b); + } + } + if (c) { + if (a.onBounding) { + a.onBounding(t); + } + x == e.TRANSPARENT_TITLE && (t[1] -= f, t[3] += f); + b.lineWidth = 1; + b.globalAlpha = 0.8; + b.beginPath(); + q == e.BOX_SHAPE ? b.rect(-6 + t[0], -6 + t[1], 12 + t[2], 12 + t[3]) : q == e.ROUND_SHAPE || q == e.CARD_SHAPE && a.flags.collapsed ? b.roundRect(-6 + t[0], -6 + t[1], 12 + t[2], 12 + t[3], 2 * this.round_radius) : q == e.CARD_SHAPE ? b.roundRect(-6 + t[0], -6 + t[1], 12 + t[2], 12 + t[3], 2 * this.round_radius, 2) : q == e.CIRCLE_SHAPE && b.arc(0.5 * d[0], 0.5 * d[1], 0.5 * d[0] + 6, 0, 2 * Math.PI); + b.strokeStyle = "#FFF"; + b.stroke(); + b.strokeStyle = h; + b.globalAlpha = 1; + } + }; + var G = new Float32Array(4), n = new Float32Array(4), q = new Float32Array(2), k = new Float32Array(2); + l.prototype.drawConnections = function(a) { + var b = e.getTime(), d = this.visible_area; + G[0] = d[0] - 20; + G[1] = d[1] - 20; + G[2] = d[2] + 40; + G[3] = d[3] + 40; + a.lineWidth = this.connections_width; + a.fillStyle = "#AAA"; + a.strokeStyle = "#AAA"; + a.globalAlpha = this.editor_alpha; + d = this.graph._nodes; + for (var h = 0, f = d.length; h < f; ++h) { + var c = d[h]; + if (c.inputs && c.inputs.length) { + for (var l = 0; l < c.inputs.length; ++l) { + var g = c.inputs[l]; + if (g && null != g.link && (g = this.graph.links[g.link])) { + var m = this.graph.getNodeById(g.origin_id); + if (null != m) { + var u = g.origin_slot; + var A = -1 == u ? [m.pos[0] + 10, m.pos[1] + 10] : m.getConnectionPos(!1, u, q); + var r = c.getConnectionPos(!0, l, k); + n[0] = A[0]; + n[1] = A[1]; + n[2] = r[0] - A[0]; + n[3] = r[1] - A[1]; + 0 > n[2] && (n[0] += n[2], n[2] = Math.abs(n[2])); + 0 > n[3] && (n[1] += n[3], n[3] = Math.abs(n[3])); + if (v(n, G)) { + var K = m.outputs[u]; + u = c.inputs[l]; + if (K && u && (m = K.dir || (m.horizontal ? e.DOWN : e.RIGHT), u = u.dir || (c.horizontal ? e.UP : e.LEFT), this.renderLink(a, A, r, g, !1, 0, null, m, u), g && g._last_time && 1000 > b - g._last_time)) { + K = 2.0 - 0.002 * (b - g._last_time); + var p = a.globalAlpha; + a.globalAlpha = p * K; + this.renderLink(a, A, r, g, !0, K, "white", m, u); + a.globalAlpha = p; + } + } + } + } + } + } + } + a.globalAlpha = 1; + }; + l.prototype.renderLink = function(a, b, d, h, f, c, k, n, q, g) { + h && this.visible_links.push(h); + !k && h && (k = h.color || l.link_type_colors[h.type]); + k || (k = this.default_link_color); + null != h && this.highlighted_links[h.id] && (k = "#FFF"); + n = n || e.RIGHT; + q = q || e.LEFT; + var x = B(b, d); + this.render_connections_border && 0.6 < this.ds.scale && (a.lineWidth = this.connections_width + 4); + a.lineJoin = "round"; + g = g || 1; + 1 < g && (a.lineWidth = 0.5); + a.beginPath(); + for (var r = 0; r < g; r += 1) { + var m = 5 * (r - 0.5 * (g - 1)); + if (this.links_render_mode == e.SPLINE_LINK) { + a.moveTo(b[0], b[1] + m); + var H = 0, u = 0, p = 0, I = 0; + switch(n) { + case e.LEFT: + H = -0.25 * x; + break; + case e.RIGHT: + H = 0.25 * x; + break; + case e.UP: + u = -0.25 * x; + break; + case e.DOWN: + u = 0.25 * x; + } + switch(q) { + case e.LEFT: + p = -0.25 * x; + break; + case e.RIGHT: + p = 0.25 * x; + break; + case e.UP: + I = -0.25 * x; + break; + case e.DOWN: + I = 0.25 * x; + } + a.bezierCurveTo(b[0] + H, b[1] + u + m, d[0] + p, d[1] + I + m, d[0], d[1] + m); + } else { + if (this.links_render_mode == e.LINEAR_LINK) { + a.moveTo(b[0], b[1] + m); + I = p = u = H = 0; + switch(n) { + case e.LEFT: + H = -1; + break; + case e.RIGHT: + H = 1; + break; + case e.UP: + u = -1; + break; + case e.DOWN: + u = 1; + } + switch(q) { + case e.LEFT: + p = -1; + break; + case e.RIGHT: + p = 1; + break; + case e.UP: + I = -1; + break; + case e.DOWN: + I = 1; + } + a.lineTo(b[0] + 15 * H, b[1] + 15 * u + m); + a.lineTo(d[0] + 15 * p, d[1] + 15 * I + m); + a.lineTo(d[0], d[1] + m); + } else { + if (this.links_render_mode == e.STRAIGHT_LINK) { + a.moveTo(b[0], b[1]), m = b[0], H = b[1], u = d[0], p = d[1], n == e.RIGHT ? m += 10 : H += 10, q == e.LEFT ? u -= 10 : p -= 10, a.lineTo(m, H), a.lineTo(0.5 * (m + u), H), a.lineTo(0.5 * (m + u), p), a.lineTo(u, p), a.lineTo(d[0], d[1]); + } else { + return; + } + } + } + } + this.render_connections_border && 0.6 < this.ds.scale && !f && (a.strokeStyle = "rgba(0,0,0,0.5)", a.stroke()); + a.lineWidth = this.connections_width; + a.fillStyle = a.strokeStyle = k; + a.stroke(); + f = this.computeConnectionPoint(b, d, 0.5, n, q); + h && h._pos && (h._pos[0] = f[0], h._pos[1] = f[1]); + 0.6 <= this.ds.scale && this.highquality_render && q != e.CENTER && (this.render_connection_arrows && (r = this.computeConnectionPoint(b, d, 0.25, n, q), x = this.computeConnectionPoint(b, d, 0.26, n, q), h = this.computeConnectionPoint(b, d, 0.75, n, q), g = this.computeConnectionPoint(b, d, 0.76, n, q), this.render_curved_connections ? (x = -Math.atan2(x[0] - r[0], x[1] - r[1]), g = -Math.atan2(g[0] - h[0], g[1] - h[1])) : g = x = d[1] > b[1] ? 0 : Math.PI, a.save(), a.translate(r[0], r[1]), + a.rotate(x), a.beginPath(), a.moveTo(-5, -3), a.lineTo(0, 7), a.lineTo(5, -3), a.fill(), a.restore(), a.save(), a.translate(h[0], h[1]), a.rotate(g), a.beginPath(), a.moveTo(-5, -3), a.lineTo(0, 7), a.lineTo(5, -3), a.fill(), a.restore()), a.beginPath(), a.arc(f[0], f[1], 5, 0, 2 * Math.PI), a.fill()); + if (c) { + for (a.fillStyle = k, r = 0; 5 > r; ++r) { + c = (0.001 * e.getTime() + 0.2 * r) % 1, f = this.computeConnectionPoint(b, d, c, n, q), a.beginPath(), a.arc(f[0], f[1], 5, 0, 2 * Math.PI), a.fill(); + } + } + }; + l.prototype.computeConnectionPoint = function(a, b, d, h, f) { + h = h || e.RIGHT; + f = f || e.LEFT; + var c = B(a, b), k = [a[0], a[1]], n = [b[0], b[1]]; + switch(h) { + case e.LEFT: + k[0] += -0.25 * c; + break; + case e.RIGHT: + k[0] += 0.25 * c; + break; + case e.UP: + k[1] += -0.25 * c; + break; + case e.DOWN: + k[1] += 0.25 * c; + } + switch(f) { + case e.LEFT: + n[0] += -0.25 * c; + break; + case e.RIGHT: + n[0] += 0.25 * c; + break; + case e.UP: + n[1] += -0.25 * c; + break; + case e.DOWN: + n[1] += 0.25 * c; + } + h = (1 - d) * (1 - d) * (1 - d); + f = 3 * (1 - d) * (1 - d) * d; + c = 3 * (1 - d) * d * d; + d *= d * d; + return [h * a[0] + f * k[0] + c * n[0] + d * b[0], h * a[1] + f * k[1] + c * n[1] + d * b[1]]; + }; + l.prototype.drawExecutionOrder = function(a) { + a.shadowColor = "transparent"; + a.globalAlpha = 0.25; + a.textAlign = "center"; + a.strokeStyle = "white"; + a.globalAlpha = 0.75; + for (var b = this.visible_nodes, d = 0; d < b.length; ++d) { + var h = b[d]; + a.fillStyle = "black"; + a.fillRect(h.pos[0] - e.NODE_TITLE_HEIGHT, h.pos[1] - e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT); + 0 == h.order && a.strokeRect(h.pos[0] - e.NODE_TITLE_HEIGHT + 0.5, h.pos[1] - e.NODE_TITLE_HEIGHT + 0.5, e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT); + a.fillStyle = "#FFF"; + a.fillText(h.order, h.pos[0] + -0.5 * e.NODE_TITLE_HEIGHT, h.pos[1] - 6); + } + a.globalAlpha = 1; + }; + l.prototype.drawNodeWidgets = function(a, b, d, h) { + if (!a.widgets || !a.widgets.length) { + return 0; + } + var f = a.size[0], c = a.widgets; + b += 2; + var k = e.NODE_WIDGET_HEIGHT, n = 0.5 < this.ds.scale; + d.save(); + d.globalAlpha = this.editor_alpha; + for (var q = e.WIDGET_OUTLINE_COLOR, l = e.WIDGET_BGCOLOR, g = e.WIDGET_TEXT_COLOR, r = e.WIDGET_SECONDARY_TEXT_COLOR, m = 0; m < c.length; ++m) { + var u = k, p = c[m], t = b; + p.y && (t = p.y); + p.last_y = t; + d.strokeStyle = q; + d.fillStyle = "#222"; + d.textAlign = "left"; + p.disabled && (d.globalAlpha *= 0.5); + switch(p.type) { + case "button": + p.clicked && (d.fillStyle = "#AAA", p.clicked = !1, this.dirty_canvas = !0); + d.fillRect(15, t, f - 30, k); + n && d.strokeRect(15, t, f - 30, k); + n && (d.textAlign = "center", d.fillStyle = g, d.fillText(p.name, 0.5 * f, t + 0.7 * k)); + break; + case "toggle": + d.textAlign = "left"; + d.strokeStyle = q; + d.fillStyle = l; + d.beginPath(); + n ? d.roundRect(15, b, f - 30, k, 0.5 * k) : d.rect(15, b, f - 30, k); + d.fill(); + n && d.stroke(); + d.fillStyle = p.value ? "#89A" : "#333"; + d.beginPath(); + d.arc(f - 30, t + 0.5 * k, 0.36 * k, 0, 2 * Math.PI); + d.fill(); + n && (d.fillStyle = r, null != p.name && d.fillText(p.name, 30, t + 0.7 * k), d.fillStyle = p.value ? g : r, d.textAlign = "right", d.fillText(p.value ? p.options.on || "true" : p.options.off || "false", f - 40, t + 0.7 * k)); + break; + case "slider": + d.fillStyle = l; + d.fillRect(15, t, f - 30, k); + var C = p.options.max - p.options.min, v = (p.value - p.options.min) / C; + d.fillStyle = h == p ? "#89A" : "#678"; + d.fillRect(15, t, v * (f - 30), k); + n && d.strokeRect(15, t, f - 30, k); + p.marker && (C = (p.marker - p.options.min) / C, d.fillStyle = "#AA9", d.fillRect(15 + C * (f - 30), t, 2, k)); + n && (d.textAlign = "center", d.fillStyle = g, d.fillText(p.name + " " + Number(p.value).toFixed(3), 0.5 * f, t + 0.7 * k)); + break; + case "number": + case "combo": + d.textAlign = "left"; + d.strokeStyle = q; + d.fillStyle = l; + d.beginPath(); + n ? d.roundRect(15, b, f - 30, k, 0.5 * k) : d.rect(15, b, f - 30, k); + d.fill(); + n && (d.stroke(), d.fillStyle = g, d.beginPath(), d.moveTo(31, b + 5), d.lineTo(21, b + 0.5 * k), d.lineTo(31, b + k - 5), d.fill(), d.beginPath(), d.moveTo(f - 15 - 16, b + 5), d.lineTo(f - 15 - 6, b + 0.5 * k), d.lineTo(f - 15 - 16, b + k - 5), d.fill(), d.fillStyle = r, d.fillText(p.name, 35, t + 0.7 * k), d.fillStyle = g, d.textAlign = "right", "number" == p.type ? d.fillText(Number(p.value).toFixed(void 0 !== p.options.precision ? p.options.precision : 3), f - 30 - 20, t + 0.7 * k) : + (C = p.value, p.options.values && (v = p.options.values, v.constructor === Function && (v = v()), v && v.constructor !== Array && (C = v[p.value])), d.fillText(C, f - 30 - 20, t + 0.7 * k))); + break; + case "string": + case "text": + d.textAlign = "left"; + d.strokeStyle = q; + d.fillStyle = l; + d.beginPath(); + n ? d.roundRect(15, b, f - 30, k, 0.5 * k) : d.rect(15, b, f - 30, k); + d.fill(); + n && (d.save(), d.beginPath(), d.rect(15, b, f - 30, k), d.clip(), d.stroke(), d.fillStyle = r, null != p.name && d.fillText(p.name, 30, t + 0.7 * k), d.fillStyle = g, d.textAlign = "right", d.fillText(String(p.value).substr(0, 30), f - 30, t + 0.7 * k), d.restore()); + break; + default: + p.draw && (u = p.draw(d, a, f, t, k) || k); + } + b += u + 4; + d.globalAlpha = this.editor_alpha; + } + d.restore(); + d.textAlign = "left"; + }; + l.prototype.processNodeWidgets = function(a, b, d, c) { + function f(f, h) { + f.value = h; + f.options && f.options.property && void 0 !== a.properties[f.options.property] && a.setProperty(f.options.property, h); + f.callback && f.callback(f.value, q, a, b, d); + } + if (!a.widgets || !a.widgets.length) { + return null; + } + for (var h = b[0] - a.pos[0], k = b[1] - a.pos[1], n = a.size[0], q = this, l = this.getCanvasWindow(), g = 0; g < a.widgets.length; ++g) { + var r = a.widgets[g]; + if (r && !r.disabled && (r == c || 6 < h && h < n - 12 && k > r.last_y && k < r.last_y + e.NODE_WIDGET_HEIGHT)) { + switch(r.type) { + case "button": + if ("mousemove" === d.type) { + break; + } + r.callback && setTimeout(function() { + r.callback(r, q, a, b, d); + }, 20); + this.dirty_canvas = r.clicked = !0; + break; + case "slider": + l = Math.clamp((h - 10) / (n - 20), 0, 1); + r.value = r.options.min + (r.options.max - r.options.min) * l; + r.callback && setTimeout(function() { + f(r, r.value); + }, 20); + this.dirty_canvas = !0; + break; + case "number": + case "combo": + c = r.value; + if ("mousemove" == d.type && "number" == r.type) { + r.value += 0.1 * d.deltaX * (r.options.step || 1), null != r.options.min && r.value < r.options.min && (r.value = r.options.min), null != r.options.max && r.value > r.options.max && (r.value = r.options.max); + } else { + if ("mousedown" == d.type) { + var m = r.options.values; + m && m.constructor === Function && (m = r.options.values(r, a)); + var p = null; + "number" != r.type && (p = m.constructor === Array ? m : Object.keys(m)); + h = 40 > h ? -1 : h > n - 40 ? 1 : 0; + if ("number" == r.type) { + r.value += 0.1 * h * (r.options.step || 1), null != r.options.min && r.value < r.options.min && (r.value = r.options.min), null != r.options.max && r.value > r.options.max && (r.value = r.options.max); + } else { + if (h) { + l = -1, l = m.constructor === Object ? p.indexOf(String(r.value)) + h : p.indexOf(r.value) + h, l >= p.length && (l = p.length - 1), 0 > l && (l = 0), r.value = m.constructor === Array ? m[l] : l; + } else { + var u = m != p ? Object.values(m) : m; + new e.ContextMenu(u, {scale:Math.max(1, this.ds.scale), event:d, className:"dark", callback:function(a, b, d) { + m != p && (a = u.indexOf(a)); + this.value = a; + f(this, a); + q.dirty_canvas = !0; + return !1; + }.bind(r)}, l); + } + } + } else { + "mouseup" == d.type && "number" == r.type && (h = 40 > h ? -1 : h > n - 40 ? 1 : 0, 200 > d.click_time && 0 == h && this.prompt("Value", r.value, function(a) { + this.value = Number(a); + f(this, this.value); + }.bind(r), d)); + } + } + c != r.value && setTimeout(function() { + f(this, this.value); + }.bind(r), 20); + this.dirty_canvas = !0; + break; + case "toggle": + "mousedown" == d.type && (r.value = !r.value, setTimeout(function() { + f(r, r.value); + }, 20)); + break; + case "string": + case "text": + "mousedown" == d.type && this.prompt("Value", r.value, function(a) { + this.value = a; + f(this, a); + }.bind(r), d); + break; + default: + r.mouse && r.mouse(ctx, d, [h, k], a); + } + return r; + } + } + return null; + }; + l.prototype.drawGroups = function(a, b) { + if (this.graph) { + a = this.graph._groups; + b.save(); + b.globalAlpha = 0.5 * this.editor_alpha; + for (var d = 0; d < a.length; ++d) { + var h = a[d]; + if (v(this.visible_area, h._bounding)) { + b.fillStyle = h.color || "#335"; + b.strokeStyle = h.color || "#335"; + var f = h._pos, c = h._size; + b.globalAlpha = 0.25 * this.editor_alpha; + b.beginPath(); + b.rect(f[0] + 0.5, f[1] + 0.5, c[0], c[1]); + b.fill(); + b.globalAlpha = this.editor_alpha; + b.stroke(); + b.beginPath(); + b.moveTo(f[0] + c[0], f[1] + c[1]); + b.lineTo(f[0] + c[0] - 10, f[1] + c[1]); + b.lineTo(f[0] + c[0], f[1] + c[1] - 10); + b.fill(); + c = h.font_size || e.DEFAULT_GROUP_FONT_SIZE; + b.font = c + "px Arial"; + b.fillText(h.title, f[0] + 4, f[1] + c); + } + } + b.restore(); + } + }; + l.prototype.adjustNodesSize = function() { + for (var a = this.graph._nodes, b = 0; b < a.length; ++b) { + a[b].size = a[b].computeSize(); + } + this.setDirty(!0, !0); + }; + l.prototype.resize = function(a, b) { + a || b || (b = this.canvas.parentNode, a = b.offsetWidth, b = b.offsetHeight); + if (this.canvas.width != a || this.canvas.height != b) { + this.canvas.width = a, this.canvas.height = b, this.bgcanvas.width = this.canvas.width, this.bgcanvas.height = this.canvas.height, this.setDirty(!0, !0); + } + }; + l.prototype.switchLiveMode = function(a) { + if (a) { + var b = this, d = this.live_mode ? 1.1 : 0.9; + this.live_mode && (this.live_mode = !1, this.editor_alpha = 0.1); + var c = setInterval(function() { + b.editor_alpha *= d; + b.dirty_canvas = !0; + b.dirty_bgcanvas = !0; + 1 > d && 0.01 > b.editor_alpha && (clearInterval(c), 1 > d && (b.live_mode = !0)); + 1 < d && 0.99 < b.editor_alpha && (clearInterval(c), b.editor_alpha = 1); + }, 1); + } else { + this.live_mode = !this.live_mode, this.dirty_bgcanvas = this.dirty_canvas = !0; + } + }; + l.prototype.onNodeSelectionChange = function(a) { + }; + l.prototype.touchHandler = function(a) { + var b = a.changedTouches[0]; + switch(a.type) { + case "touchstart": + var d = "mousedown"; + break; + case "touchmove": + d = "mousemove"; + break; + case "touchend": + d = "mouseup"; + break; + default: + return; + } + var c = this.getCanvasWindow(), f = c.document.createEvent("MouseEvent"); + f.initMouseEvent(d, !0, !0, c, 1, b.screenX, b.screenY, b.clientX, b.clientY, !1, !1, !1, !1, 0, null); + b.target.dispatchEvent(f); + a.preventDefault(); + }; + l.onGroupAdd = function(a, b, d) { + a = l.active_canvas; + a.getCanvasWindow(); + b = new e.LGraphGroup; + b.pos = a.convertEventToCanvasOffset(d); + a.graph.add(b); + }; + l.onMenuAdd = function(a, b, d, c, f) { + function h(a, b) { + b = c.getFirstEvent(); + if (a = e.createNode(a.value)) { + a.pos = k.convertEventToCanvasOffset(b), k.graph.add(a); + } + f && f(a); + } + var k = l.active_canvas, n = k.getCanvasWindow(); + a = e.getNodeTypesCategories(k.filter); + b = []; + for (var q in a) { + a[q] && b.push({value:a[q], content:a[q], has_submenu:!0}); + } + var g = new e.ContextMenu(b, {event:d, callback:function(a, b, d) { + a = e.getNodeTypesInCategory(a.value, k.filter); + b = []; + for (var f in a) { + a[f].skip_list || b.push({content:a[f].title, value:a[f].type}); + } + new e.ContextMenu(b, {event:d, callback:h, parentMenu:g}, n); + return !1; + }, parentMenu:c}, n); + return !1; + }; + l.onMenuCollapseAll = function() { + }; + l.onMenuNodeEdit = function() { + }; + l.showMenuNodeOptionalInputs = function(a, b, d, c, f) { + if (f) { + var h = this; + a = l.active_canvas.getCanvasWindow(); + b = f.optional_inputs; + f.onGetInputs && (b = f.onGetInputs()); + var k = []; + if (b) { + for (var n in b) { + var q = b[n]; + if (q) { + var g = q[0]; + q[2] && q[2].label && (g = q[2].label); + g = {content:g, value:q}; + q[1] == e.ACTION && (g.className = "event"); + k.push(g); + } else { + k.push(null); + } + } + } + this.onMenuNodeInputs && (k = this.onMenuNodeInputs(k)); + if (k.length) { + return new e.ContextMenu(k, {event:d, callback:function(a, b, d) { + f && (a.callback && a.callback.call(h, f, a, b, d), a.value && (f.addInput(a.value[0], a.value[1], a.value[2]), f.setDirtyCanvas(!0, !0))); + }, parentMenu:c, node:f}, a), !1; + } + } + }; + l.showMenuNodeOptionalOutputs = function(a, b, d, c, f) { + function h(a, b, d) { + if (f && (a.callback && a.callback.call(k, f, a, b, d), a.value)) { + if (d = a.value[1], !d || d.constructor !== Object && d.constructor !== Array) { + f.addOutput(a.value[0], a.value[1], a.value[2]), f.setDirtyCanvas(!0, !0); + } else { + a = []; + for (var n in d) { + a.push({content:n, value:d[n]}); + } + new e.ContextMenu(a, {event:b, callback:h, parentMenu:c, node:f}); + return !1; + } + } + } + if (f) { + var k = this; + a = l.active_canvas.getCanvasWindow(); + b = f.optional_outputs; + f.onGetOutputs && (b = f.onGetOutputs()); + var n = []; + if (b) { + for (var q in b) { + var g = b[q]; + if (!g) { + n.push(null); + } else { + if (!f.flags || !f.flags.skip_repeated_outputs || -1 == f.findOutputSlot(g[0])) { + var m = g[0]; + g[2] && g[2].label && (m = g[2].label); + m = {content:m, value:g}; + g[1] == e.EVENT && (m.className = "event"); + n.push(m); + } + } + } + } + this.onMenuNodeOutputs && (n = this.onMenuNodeOutputs(n)); + if (n.length) { + return new e.ContextMenu(n, {event:d, callback:h, parentMenu:c, node:f}, a), !1; + } + } + }; + l.onShowMenuNodeProperties = function(a, b, d, c, f) { + if (f && f.properties) { + var h = l.active_canvas; + b = h.getCanvasWindow(); + var k = [], n; + for (n in f.properties) { + a = void 0 !== f.properties[n] ? f.properties[n] : " ", "object" == typeof a && (a = JSON.stringify(a)), a = l.decodeHTML(a), k.push({content:"" + n + "" + a + "", value:n}); + } + if (k.length) { + return new e.ContextMenu(k, {event:d, callback:function(a, b, d, c) { + f && (b = this.getBoundingClientRect(), h.showEditPropertyValue(f, a.value, {position:[b.left, b.top]})); + }, parentMenu:c, allow_html:!0, node:f}, b), !1; + } + } + }; + l.decodeHTML = function(a) { + var b = document.createElement("div"); + b.innerText = a; + return b.innerHTML; + }; + l.onResizeNode = function(a, b, d, c, f) { + f && (f.size = f.computeSize(), f.setDirtyCanvas(!0, !0)); + }; + l.prototype.showLinkMenu = function(a, b) { + var d = this; + console.log(a); + var c = new e.ContextMenu(["Add Node", null, "Delete"], {event:b, title:null != a.data ? a.data.constructor.name : null, callback:function(b, h, e) { + switch(b) { + case "Add Node": + l.onMenuAdd(null, null, e, c, function(b) { + console.log("node autoconnect"); + var f = d.graph.getNodeById(a.origin_id), c = d.graph.getNodeById(a.target_id); + b.inputs && b.inputs.length && b.outputs && b.outputs.length && f.outputs[a.origin_slot].type == b.inputs[0].type && b.outputs[0].type == c.inputs[0].type && (f.connect(a.origin_slot, b, 0), b.connect(0, c, a.target_slot), b.pos[0] -= 0.5 * b.size[0]); + }); + break; + case "Delete": + d.graph.removeLink(a.id); + } + }}); + return !1; + }; + l.onShowPropertyEditor = function(a, b, d, c, f) { + function h() { + var b = n.value; + "Number" == a.type ? b = Number(b) : "Boolean" == a.type && (b = !!b); + f[e] = b; + k.parentNode && k.parentNode.removeChild(k); + f.setDirtyCanvas(!0, !0); + } + var e = a.property || "title"; + b = f[e]; + var k = document.createElement("div"); + k.className = "graphdialog"; + k.innerHTML = ""; + k.querySelector(".name").innerText = e; + var n = k.querySelector("input"); + n && (n.value = b, n.addEventListener("blur", function(a) { + this.focus(); + }), n.addEventListener("keydown", function(a) { + 13 == a.keyCode && (h(), a.preventDefault(), a.stopPropagation()); + })); + b = l.active_canvas.canvas; + d = b.getBoundingClientRect(); + var q = c = -20; + d && (c -= d.left, q -= d.top); + event ? (k.style.left = event.clientX + c + "px", k.style.top = event.clientY + q + "px") : (k.style.left = 0.5 * b.width + c + "px", k.style.top = 0.5 * b.height + q + "px"); + k.querySelector("button").addEventListener("click", h); + b.parentNode.appendChild(k); + }; + l.prototype.prompt = function(a, b, d, c) { + var f = this; + a = a || ""; + var h = !1, e = document.createElement("div"); + e.className = "graphdialog rounded"; + e.innerHTML = " "; + e.close = function() { + f.prompt_box = null; + e.parentNode && e.parentNode.removeChild(e); + }; + 1 < this.ds.scale && (e.style.transform = "scale(" + this.ds.scale + ")"); + e.addEventListener("mouseleave", function(a) { + h || e.close(); + }); + f.prompt_box && f.prompt_box.close(); + f.prompt_box = e; + e.querySelector(".name").innerText = a; + e.querySelector(".value").value = b; + var k = e.querySelector("input"); + k.addEventListener("keydown", function(a) { + h = !0; + if (27 == a.keyCode) { + e.close(); + } else { + if (13 == a.keyCode) { + d && d(this.value), e.close(); + } else { + return; + } + } + a.preventDefault(); + a.stopPropagation(); + }); + e.querySelector("button").addEventListener("click", function(a) { + d && d(k.value); + f.setDirty(!0); + e.close(); + }); + a = l.active_canvas.canvas; + b = a.getBoundingClientRect(); + var n = -20, q = -20; + b && (n -= b.left, q -= b.top); + c ? (e.style.left = c.clientX + n + "px", e.style.top = c.clientY + q + "px") : (e.style.left = 0.5 * a.width + n + "px", e.style.top = 0.5 * a.height + q + "px"); + a.parentNode.appendChild(e); + setTimeout(function() { + k.focus(); + }, 10); + return e; + }; + l.search_limit = -1; + l.prototype.showSearchBox = function(a) { + function b(b) { + if (b) { + if (f.onSearchBoxSelection) { + f.onSearchBoxSelection(b, a, k); + } else { + var d = e.searchbox_extras[b.toLowerCase()]; + d && (b = d.type); + if (b = e.createNode(b)) { + b.pos = k.convertEventToCanvasOffset(a), k.graph.add(b); + } + if (d && d.data) { + if (d.data.properties) { + for (var c in d.data.properties) { + b.addProperty(c, d.data.properties[c]); + } + } + if (d.data.inputs) { + for (c in b.inputs = [], d.data.inputs) { + b.addOutput(d.data.inputs[c][0], d.data.inputs[c][1]); + } + } + if (d.data.outputs) { + for (c in b.outputs = [], d.data.outputs) { + b.addOutput(d.data.outputs[c][0], d.data.outputs[c][1]); + } + } + d.data.title && (b.title = d.data.title); + d.data.json && b.configure(d.data.json); + } + } + } + g.close(); + } + function d(a) { + var b = t; + t && t.classList.remove("selected"); + t ? (t = a ? t.nextSibling : t.previousSibling) || (t = b) : t = a ? p.childNodes[0] : p.childNodes[p.childNodes.length]; + t && (t.classList.add("selected"), t.scrollIntoView({block:"end", behavior:"smooth"})); + } + function c() { + function a(a, d) { + var f = document.createElement("div"); + r || (r = a); + f.innerText = a; + f.dataset.type = escape(a); + f.className = "litegraph lite-search-item"; + d && (f.className += " " + d); + f.addEventListener("click", function(a) { + b(unescape(this.dataset.type)); + }); + p.appendChild(f); + } + u = null; + var d = C.value; + r = null; + p.innerHTML = ""; + if (d) { + if (f.onSearchBox) { + var c = f.onSearchBox(p, d, k); + if (c) { + for (var h = 0; h < c.length; ++h) { + a(c[h]); + } + } + } else { + c = function(a) { + var b = e.registered_node_types[a]; + return q && b.filter != q ? !1 : -1 !== a.toLowerCase().indexOf(d); + }; + var n = 0; + d = d.toLowerCase(); + var q = k.filter || k.graph.filter; + for (h in e.searchbox_extras) { + var g = e.searchbox_extras[h]; + if (-1 !== g.desc.toLowerCase().indexOf(d)) { + var m = e.registered_node_types[g.type]; + if (!m || !m.filter || m.filter == q) { + if (a(g.desc, "searchbox_extra"), -1 !== l.search_limit && n++ > l.search_limit) { + break; + } + } + } + } + g = null; + if (Array.prototype.filter) { + g = Object.keys(e.registered_node_types).filter(c); + } else { + for (h in g = [], e.registered_node_types) { + c(h) && g.push(h); + } + } + for (h = 0; h < g.length && !(a(g[h]), -1 !== l.search_limit && n++ > l.search_limit); h++) { + } + } + } + } + var f = this, k = l.active_canvas, n = k.canvas, q = n.ownerDocument || document, g = document.createElement("div"); + g.className = "litegraph litesearchbox graphdialog rounded"; + g.innerHTML = "Search
"; + g.close = function() { + f.search_box = null; + q.body.focus(); + q.body.style.overflow = ""; + setTimeout(function() { + f.canvas.focus(); + }, 20); + g.parentNode && g.parentNode.removeChild(g); + }; + var m = null; + 1 < this.ds.scale && (g.style.transform = "scale(" + this.ds.scale + ")"); + g.addEventListener("mouseenter", function(a) { + m && (clearTimeout(m), m = null); + }); + g.addEventListener("mouseleave", function(a) { + m = setTimeout(function() { + g.close(); + }, 500); + }); + f.search_box && f.search_box.close(); + f.search_box = g; + var p = g.querySelector(".helper"), r = null, u = null, t = null, C = g.querySelector("input"); + C && (C.addEventListener("blur", function(a) { + this.focus(); + }), C.addEventListener("keydown", function(a) { + if (38 == a.keyCode) { + d(!1); + } else { + if (40 == a.keyCode) { + d(!0); + } else { + if (27 == a.keyCode) { + g.close(); + } else { + if (13 == a.keyCode) { + t ? b(t.innerHTML) : r ? b(r) : g.close(); + } else { + u && clearInterval(u); + u = setTimeout(c, 10); + return; + } + } + } + } + a.preventDefault(); + a.stopPropagation(); + a.stopImmediatePropagation(); + return !0; + })); + q.fullscreenElement ? q.fullscreenElement.appendChild(g) : (q.body.appendChild(g), q.body.style.overflow = "hidden"); + n = n.getBoundingClientRect(); + var v = (a ? a.clientY : n.top + 0.5 * n.height) - 20; + g.style.left = (a ? a.clientX : n.left + 0.5 * n.width) - 80 + "px"; + g.style.top = v + "px"; + a.layerY > n.height - 200 && (p.style.maxHeight = n.height - a.layerY - 20 + "px"); + C.focus(); + return g; + }; + l.prototype.showEditPropertyValue = function(a, b, d) { + function c() { + f(r.value); + } + function f(f) { + "number" == typeof a.properties[b] && (f = Number(f)); + if ("array" == k || "object" == k) { + f = JSON.parse(f); + } + a.properties[b] = f; + a._graph && a._graph._version++; + if (a.onPropertyChanged) { + a.onPropertyChanged(b, f); + } + if (d.onclose) { + d.onclose(); + } + l.close(); + a.setDirtyCanvas(!0, !0); + } + if (a && void 0 !== a.properties[b]) { + d = d || {}; + var e = a.getPropertyInfo(b), k = e.type, n = ""; + if ("string" == k || "number" == k || "array" == k || "object" == k) { + n = ""; + } else { + if ("enum" == k && e.values) { + n = ""; + } else { + if ("boolean" == k) { + n = ""; + } else { + console.warn("unknown type: " + k); + return; + } + } + } + var l = this.createDialog("" + b + "" + n + "", d); + if ("enum" == k && e.values) { + var r = l.querySelector("select"); + r.addEventListener("change", function(a) { + f(a.target.value); + }); + } else { + if ("boolean" == k) { + (r = l.querySelector("input")) && r.addEventListener("click", function(a) { + f(!!r.checked); + }); + } else { + if (r = l.querySelector("input")) { + r.addEventListener("blur", function(a) { + this.focus(); + }), g = void 0 !== a.properties[b] ? a.properties[b] : "", g = JSON.stringify(g), r.value = g, r.addEventListener("keydown", function(a) { + 13 == a.keyCode && (c(), a.preventDefault(), a.stopPropagation()); + }); + } + } + } + l.querySelector("button").addEventListener("click", c); + return l; + } + }; + l.prototype.createDialog = function(a, b) { + b = b || {}; + var d = document.createElement("div"); + d.className = "graphdialog"; + d.innerHTML = a; + a = this.canvas.getBoundingClientRect(); + var c = -20, f = -20; + a && (c -= a.left, f -= a.top); + b.position ? (c += b.position[0], f += b.position[1]) : b.event ? (c += b.event.clientX, f += b.event.clientY) : (c += 0.5 * this.canvas.width, f += 0.5 * this.canvas.height); + d.style.left = c + "px"; + d.style.top = f + "px"; + this.canvas.parentNode.appendChild(d); + d.close = function() { + this.parentNode && this.parentNode.removeChild(this); + }; + return d; + }; + l.onMenuNodeCollapse = function(a, b, d, c, f) { + f.collapse(); + }; + l.onMenuNodePin = function(a, b, d, c, f) { + f.pin(); + }; + l.onMenuNodeMode = function(a, b, d, c, f) { + new e.ContextMenu(["Always", "On Event", "On Trigger", "Never"], {event:d, callback:function(a) { + if (f) { + switch(a) { + case "On Event": + f.mode = e.ON_EVENT; + break; + case "On Trigger": + f.mode = e.ON_TRIGGER; + break; + case "Never": + f.mode = e.NEVER; + break; + default: + f.mode = e.ALWAYS; + } + } + }, parentMenu:c, node:f}); + return !1; + }; + l.onMenuNodeColors = function(a, b, d, c, f) { + if (!f) { + throw "no node for color"; + } + b = []; + b.push({value:null, content:"No color"}); + for (var h in l.node_colors) { + a = l.node_colors[h], a = {value:h, content:"" + h + ""}, b.push(a); + } + new e.ContextMenu(b, {event:d, callback:function(a) { + f && ((a = a.value ? l.node_colors[a.value] : null) ? f.constructor === e.LGraphGroup ? f.color = a.groupcolor : (f.color = a.color, f.bgcolor = a.bgcolor) : (delete f.color, delete f.bgcolor), f.setDirtyCanvas(!0, !0)); + }, parentMenu:c, node:f}); + return !1; + }; + l.onMenuNodeShapes = function(a, b, d, c, f) { + if (!f) { + throw "no node passed"; + } + new e.ContextMenu(e.VALID_SHAPES, {event:d, callback:function(a) { + f && (f.shape = a, f.setDirtyCanvas(!0)); + }, parentMenu:c, node:f}); + return !1; + }; + l.onMenuNodeRemove = function(a, b, d, c, f) { + if (!f) { + throw "no node passed"; + } + !1 !== f.removable && (f.graph.remove(f), f.setDirtyCanvas(!0, !0)); + }; + l.onMenuNodeClone = function(a, b, d, c, f) { + 0 != f.clonable && (a = f.clone()) && (a.pos = [f.pos[0] + 5, f.pos[1] + 5], f.graph.add(a), f.setDirtyCanvas(!0, !0)); + }; + l.node_colors = {red:{color:"#322", bgcolor:"#533", groupcolor:"#A88"}, brown:{color:"#332922", bgcolor:"#593930", groupcolor:"#b06634"}, green:{color:"#232", bgcolor:"#353", groupcolor:"#8A8"}, blue:{color:"#223", bgcolor:"#335", groupcolor:"#88A"}, pale_blue:{color:"#2a363b", bgcolor:"#3f5159", groupcolor:"#3f789e"}, cyan:{color:"#233", bgcolor:"#355", groupcolor:"#8AA"}, purple:{color:"#323", bgcolor:"#535", groupcolor:"#a1309b"}, yellow:{color:"#432", bgcolor:"#653", groupcolor:"#b58b2a"}, + black:{color:"#222", bgcolor:"#000", groupcolor:"#444"}}; + l.prototype.getCanvasMenuOptions = function() { + if (this.getMenuOptions) { + var a = this.getMenuOptions(); + } else { + a = [{content:"Add Node", has_submenu:!0, callback:l.onMenuAdd}, {content:"Add Group", callback:l.onGroupAdd}], this._graph_stack && 0 < this._graph_stack.length && a.push(null, {content:"Close subgraph", callback:this.closeSubgraph.bind(this)}); + } + if (this.getExtraMenuOptions) { + var b = this.getExtraMenuOptions(this, a); + b && (a = a.concat(b)); + } + return a; + }; + l.prototype.getNodeMenuOptions = function(a) { + var b = a.getMenuOptions ? a.getMenuOptions(this) : [{content:"Inputs", has_submenu:!0, disabled:!0, callback:l.showMenuNodeOptionalInputs}, {content:"Outputs", has_submenu:!0, disabled:!0, callback:l.showMenuNodeOptionalOutputs}, null, {content:"Properties", has_submenu:!0, callback:l.onShowMenuNodeProperties}, null, {content:"Title", callback:l.onShowPropertyEditor}, {content:"Mode", has_submenu:!0, callback:l.onMenuNodeMode}, {content:"Resize", callback:l.onResizeNode}, {content:"Collapse", + callback:l.onMenuNodeCollapse}, {content:"Pin", callback:l.onMenuNodePin}, {content:"Colors", has_submenu:!0, callback:l.onMenuNodeColors}, {content:"Shapes", has_submenu:!0, callback:l.onMenuNodeShapes}, null]; + if (a.onGetInputs) { + var d = a.onGetInputs(); + d && d.length && (b[0].disabled = !1); + } + a.onGetOutputs && (d = a.onGetOutputs()) && d.length && (b[1].disabled = !1); + a.getExtraMenuOptions && (d = a.getExtraMenuOptions(this)) && (d.push(null), b = d.concat(b)); + !1 !== a.clonable && b.push({content:"Clone", callback:l.onMenuNodeClone}); + !1 !== a.removable && b.push(null, {content:"Remove", callback:l.onMenuNodeRemove}); + if (a.graph && a.graph.onGetNodeMenuOptions) { + a.graph.onGetNodeMenuOptions(b, a); + } + return b; + }; + l.prototype.getGroupMenuOptions = function(a) { + return [{content:"Title", callback:l.onShowPropertyEditor}, {content:"Color", has_submenu:!0, callback:l.onMenuNodeColors}, {content:"Font size", property:"font_size", type:"Number", callback:l.onShowPropertyEditor}, null, {content:"Remove", callback:l.onMenuNodeRemove}]; + }; + l.prototype.processContextMenu = function(a, b) { + var d = this, c = l.active_canvas.getCanvasWindow(), f = null, k = {event:b, callback:function(b, f, c) { + if (b) { + if ("Remove Slot" == b.content) { + b = b.slot, b.input ? a.removeInput(b.slot) : b.output && a.removeOutput(b.slot); + } else { + if ("Disconnect Links" == b.content) { + b = b.slot, b.output ? a.disconnectOutput(b.slot) : b.input && a.disconnectInput(b.slot); + } else { + if ("Rename Slot" == b.content) { + b = b.slot; + var e = b.input ? a.getInputInfo(b.slot) : a.getOutputInfo(b.slot), h = d.createDialog("Name", f), k = h.querySelector("input"); + k && e && (k.value = e.label || ""); + h.querySelector("button").addEventListener("click", function(a) { + k.value && (e && (e.label = k.value), d.setDirty(!0)); + h.close(); + }); + } + } + } + } + }, extra:a}; + a && (k.title = a.type); + var n = null; + a && (n = a.getSlotInPosition(b.canvasX, b.canvasY), l.active_node = a); + n ? (f = [], n && n.output && n.output.links && n.output.links.length && f.push({content:"Disconnect Links", slot:n}), b = n.input || n.output, f.push(b.locked ? "Cannot remove" : {content:"Remove Slot", slot:n}), f.push(b.nameLocked ? "Cannot rename" : {content:"Rename Slot", slot:n}), k.title = (n.input ? n.input.type : n.output.type) || "*", n.input && n.input.type == e.ACTION && (k.title = "Action"), n.output && n.output.type == e.EVENT && (k.title = "Event")) : a ? f = this.getNodeMenuOptions(a) : + (f = this.getCanvasMenuOptions(), (n = this.graph.getGroupOnPos(b.canvasX, b.canvasY)) && f.push(null, {content:"Edit Group", has_submenu:!0, submenu:{title:"Group", extra:n, options:this.getGroupMenuOptions(n)}})); + f && new e.ContextMenu(f, k, c); + }; + "undefined" != typeof window && window.CanvasRenderingContext2D && (window.CanvasRenderingContext2D.prototype.roundRect = function(a, b, d, c, f, e) { + void 0 === f && (f = 5); + void 0 === e && (e = f); + this.moveTo(a + f, b); + this.lineTo(a + d - f, b); + this.quadraticCurveTo(a + d, b, a + d, b + f); + this.lineTo(a + d, b + c - e); + this.quadraticCurveTo(a + d, b + c, a + d - e, b + c); + this.lineTo(a + e, b + c); + this.quadraticCurveTo(a, b + c, a, b + c - e); + this.lineTo(a, b + f); + this.quadraticCurveTo(a, b, a + f, b); + }); + e.compareObjects = function(a, b) { + for (var d in a) { + if (a[d] != b[d]) { + return !1; + } + } + return !0; + }; + e.distance = B; + e.colorToString = function(a) { + return "rgba(" + Math.round(255 * a[0]).toFixed() + "," + Math.round(255 * a[1]).toFixed() + "," + Math.round(255 * a[2]).toFixed() + "," + (4 == a.length ? a[3].toFixed(2) : "1.0") + ")"; + }; + e.isInsideRectangle = y; + e.growBounding = function(a, b, d) { + b < a[0] ? a[0] = b : b > a[2] && (a[2] = b); + d < a[1] ? a[1] = d : d > a[3] && (a[3] = d); + }; + e.isInsideBounding = function(a, b) { + return a[0] < b[0][0] || a[1] < b[0][1] || a[0] > b[1][0] || a[1] > b[1][1] ? !1 : !0; + }; + e.overlapBounding = v; + e.hex2num = function(a) { + "#" == a.charAt(0) && (a = a.slice(1)); + a = a.toUpperCase(); + for (var b = Array(3), d = 0, c, f, e = 0; 6 > e; e += 2) { + c = "0123456789ABCDEF".indexOf(a.charAt(e)), f = "0123456789ABCDEF".indexOf(a.charAt(e + 1)), b[d] = 16 * c + f, d++; + } + return b; + }; + e.num2hex = function(a) { + for (var b = "#", d, c, f = 0; 3 > f; f++) { + d = a[f] / 16, c = a[f] % 16, b += "0123456789ABCDEF".charAt(d) + "0123456789ABCDEF".charAt(c); + } + return b; + }; + E.prototype.addItem = function(a, b, d) { + function c(a) { + var b = this.value; + b && b.has_submenu && f.call(this, a); + } + function f(a) { + var b = this.value, f = !0; + e.current_submenu && e.current_submenu.close(a); + if (d.callback) { + var c = d.callback.call(this, b, d, a, e, d.node); + !0 === c && (f = !1); + } + if (b && (b.callback && !d.ignore_item_callbacks && !0 !== b.disabled && (c = b.callback.call(this, b, d, a, e, d.extra), !0 === c && (f = !1)), b.submenu)) { + if (!b.submenu.options) { + throw "ContextMenu submenu needs options"; + } + new e.constructor(b.submenu.options, {callback:b.submenu.callback, event:a, parentMenu:e, ignore_item_callbacks:b.submenu.ignore_item_callbacks, title:b.submenu.title, extra:b.submenu.extra, autoopen:d.autoopen}); + f = !1; + } + f && !e.lock && e.close(); + } + var e = this; + d = d || {}; + var k = document.createElement("div"); + k.className = "litemenu-entry submenu"; + var n = !1; + if (null === b) { + k.classList.add("separator"); + } else { + k.innerHTML = b && b.title ? b.title : a; + if (k.value = b) { + b.disabled && (n = !0, k.classList.add("disabled")), (b.submenu || b.has_submenu) && k.classList.add("has_submenu"); + } + "function" == typeof b ? (k.dataset.value = a, k.onclick_callback = b) : k.dataset.value = b; + b.className && (k.className += " " + b.className); + } + this.root.appendChild(k); + n || k.addEventListener("click", f); + d.autoopen && k.addEventListener("mouseenter", c); + return k; + }; + E.prototype.close = function(a, b) { + this.root.parentNode && this.root.parentNode.removeChild(this.root); + this.parentMenu && !b && (this.parentMenu.lock = !1, this.parentMenu.current_submenu = null, void 0 === a ? this.parentMenu.close() : a && !E.isCursorOverElement(a, this.parentMenu.root) && E.trigger(this.parentMenu.root, "mouseleave", a)); + this.current_submenu && this.current_submenu.close(a, !0); + this.root.closing_timer && clearTimeout(this.root.closing_timer); + }; + E.trigger = function(a, b, d, c) { + var f = document.createEvent("CustomEvent"); + f.initCustomEvent(b, !0, !0, d); + f.srcElement = c; + a.dispatchEvent ? a.dispatchEvent(f) : a.__events && a.__events.dispatchEvent(f); + return f; + }; + E.prototype.getTopMenu = function() { + return this.options.parentMenu ? this.options.parentMenu.getTopMenu() : this; + }; + E.prototype.getFirstEvent = function() { + return this.options.parentMenu ? this.options.parentMenu.getFirstEvent() : this.options.event; + }; + E.isCursorOverElement = function(a, b) { + var d = a.clientX; + a = a.clientY; + return (b = b.getBoundingClientRect()) ? a > b.top && a < b.top + b.height && d > b.left && d < b.left + b.width ? !0 : !1 : !1; + }; + e.ContextMenu = E; + e.closeAllContextMenus = function(a) { + a = a || window; + a = a.document.querySelectorAll(".litecontextmenu"); + if (a.length) { + for (var b = [], d = 0; d < a.length; d++) { + b.push(a[d]); + } + for (d in b) { + b[d].close ? b[d].close() : b[d].parentNode && b[d].parentNode.removeChild(b[d]); + } + } + }; + e.extendClass = function(a, b) { + for (var d in b) { + a.hasOwnProperty(d) || (a[d] = b[d]); + } + if (b.prototype) { + for (d in b.prototype) { + b.prototype.hasOwnProperty(d) && !a.prototype.hasOwnProperty(d) && (b.prototype.__lookupGetter__(d) ? a.prototype.__defineGetter__(d, b.prototype.__lookupGetter__(d)) : a.prototype[d] = b.prototype[d], b.prototype.__lookupSetter__(d) && a.prototype.__defineSetter__(d, b.prototype.__lookupSetter__(d))); + } + } + }; + z.sampleCurve = function(a, b) { + if (b) { + for (var d = 0; d < b.length - 1; ++d) { + var c = b[d], f = b[d + 1]; + if (!(f[0] < a)) { + b = f[0] - c[0]; + if (0.00001 > Math.abs(b)) { + return c[1]; + } + a = (a - c[0]) / b; + return c[1] * (1.0 - a) + f[1] * a; + } + } + return 0; + } + }; + z.prototype.draw = function(a, b, d, c, f, e) { + if (d = this.points) { + this.size = b; + var k = b[0] - 2 * this.margin; + b = b[1] - 2 * this.margin; + f = f || "#666"; + a.save(); + a.translate(this.margin, this.margin); + c && (a.fillStyle = "#111", a.fillRect(0, 0, k, b), a.fillStyle = "#222", a.fillRect(0.5 * k, 0, 1, b), a.strokeStyle = "#333", a.strokeRect(0, 0, k, b)); + a.strokeStyle = f; + e && (a.globalAlpha = 0.5); + a.beginPath(); + for (c = 0; c < d.length; ++c) { + f = d[c], a.lineTo(f[0] * k, (1.0 - f[1]) * b); + } + a.stroke(); + a.globalAlpha = 1; + if (!e) { + for (c = 0; c < d.length; ++c) { + f = d[c], a.fillStyle = this.selected == c ? "#FFF" : this.nearest == c ? "#DDD" : "#AAA", a.beginPath(), a.arc(f[0] * k, (1.0 - f[1]) * b, 2, 0, 2 * Math.PI), a.fill(); + } + } + a.restore(); + } + }; + z.prototype.onMouseDown = function(a, b) { + var d = this.points; + if (d && !(0 > a[1])) { + var c = this.size[0] - 2 * this.margin, f = this.size[1] - 2 * this.margin, e = a[0] - this.margin; + a = a[1] - this.margin; + this.selected = this.getCloserPoint([e, a], 30 / b.ds.scale); + -1 == this.selected && (b = [e / c, 1 - a / f], d.push(b), d.sort(function(a, b) { + return a[0] - b[0]; + }), this.selected = d.indexOf(b), this.must_update = !0); + if (-1 != this.selected) { + return !0; + } + } + }; + z.prototype.onMouseMove = function(a, b) { + var d = this.points; + if (d) { + var c = this.selected; + if (!(0 > c)) { + var f = (a[0] - this.margin) / (this.size[0] - 2 * this.margin), e = (a[1] - this.margin) / (this.size[1] - 2 * this.margin); + this._nearest = this.getCloserPoint([a[0] - this.margin, a[1] - this.margin], 30 / b.ds.scale); + if (b = d[c]) { + var k = 0 == c || c == d.length - 1; + !k && (-10 > a[0] || a[0] > this.size[0] + 10 || -10 > a[1] || a[1] > this.size[1] + 10) ? (d.splice(c, 1), this.selected = -1) : (b[0] = k ? 0 == c ? 0 : 1 : Math.clamp(f, 0, 1), b[1] = 1.0 - Math.clamp(e, 0, 1), d.sort(function(a, b) { + return a[0] - b[0]; + }), this.selected = d.indexOf(b), this.must_update = !0); + } + } + } + }; + z.prototype.onMouseUp = function(a, b) { + this.selected = -1; + return !1; + }; + z.prototype.getCloserPoint = function(a, b) { + var d = this.points; + if (!d) { + return -1; + } + b = b || 30; + for (var c = this.size[0] - 2 * this.margin, f = this.size[1] - 2 * this.margin, e = d.length, k = [0, 0], n = 1000000, q = -1, g = 0; g < e; ++g) { + var l = d[g]; + k[0] = l[0] * c; + k[1] = (1.0 - l[1]) * f; + l = vec2.distance(a, k); + l > n || l > b || (q = g, n = l); + } + return q; + }; + e.CurveEditor = z; + e.getParameterNames = function(a) { + return (a + "").replace(/[/][/].*$/gm, "").replace(/\s+/g, "").replace(/[/][*][^/*]*[*][/]/g, "").split("){", 1)[0].replace(/^[^(]*[(]/, "").replace(/=[^,]+/g, "").split(",").filter(Boolean); + }; + Math.clamp = function(a, b, d) { + return b > a ? b : d < a ? d : a; + }; + "undefined" == typeof window || window.requestAnimationFrame || (window.requestAnimationFrame = window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(a) { + window.setTimeout(a, 1000 / 60); + }); +})(this); +"undefined" != typeof exports && (exports.LiteGraph = this.LiteGraph); +(function(w) { + function c() { + this.addOutput("in ms", "number"); + this.addOutput("in sec", "number"); + } + function p() { + this.size = [140, 80]; + this.properties = {enabled:!0}; + this.enabled = !0; + this.subgraph = new h.LGraph; + this.subgraph._subgraph_node = this; + this.subgraph._is_subgraph = !0; + this.subgraph.onTrigger = this.onSubgraphTrigger.bind(this); + this.subgraph.onInputAdded = this.onSubgraphNewInput.bind(this); + this.subgraph.onInputRenamed = this.onSubgraphRenamedInput.bind(this); + this.subgraph.onInputTypeChanged = this.onSubgraphTypeChangeInput.bind(this); + this.subgraph.onInputRemoved = this.onSubgraphRemovedInput.bind(this); + this.subgraph.onOutputAdded = this.onSubgraphNewOutput.bind(this); + this.subgraph.onOutputRenamed = this.onSubgraphRenamedOutput.bind(this); + this.subgraph.onOutputTypeChanged = this.onSubgraphTypeChangeOutput.bind(this); + this.subgraph.onOutputRemoved = this.onSubgraphRemovedOutput.bind(this); + } + function m() { + this.addOutput("", "number"); + this.name_in_graph = ""; + this.properties = {name:"", type:"number", value:0}; + var a = this; + this.name_widget = this.addWidget("text", "Name", this.properties.name, function(b) { + b && a.setProperty("name", b); + }); + this.type_widget = this.addWidget("text", "Type", this.properties.type, function(b) { + a.setProperty("type", b); + }); + this.value_widget = this.addWidget("number", "Value", this.properties.value, function(b) { + a.setProperty("value", b); + }); + this.widgets_up = !0; + this.size = [180, 90]; + } + function g() { + this.addInput("", ""); + this.name_in_graph = ""; + this.properties = {}; + var a = this; + Object.defineProperty(this.properties, "name", {get:function() { + return a.name_in_graph; + }, set:function(b) { + "" != b && b != a.name_in_graph && (a.name_in_graph ? a.graph.renameOutput(a.name_in_graph, b) : a.graph.addOutput(b, a.properties.type), a.name_widget.value = b, a.name_in_graph = b); + }, enumerable:!0}); + Object.defineProperty(this.properties, "type", {get:function() { + return a.inputs[0].type; + }, set:function(b) { + if ("action" == b || "event" == b) { + b = h.ACTION; + } + a.inputs[0].type = b; + a.name_in_graph && a.graph.changeOutputType(a.name_in_graph, a.inputs[0].type); + a.type_widget.value = b || ""; + }, enumerable:!0}); + this.name_widget = this.addWidget("text", "Name", this.properties.name, "name"); + this.type_widget = this.addWidget("text", "Type", this.properties.type, "type"); + this.widgets_up = !0; + this.size = [180, 60]; + } + function u() { + this.addOutput("value", "number"); + this.addProperty("value", 1.0); + this.widget = this.addWidget("number", "value", 1, "value"); + this.widgets_up = !0; + this.size = [180, 30]; + } + function l() { + this.addOutput("", "boolean"); + this.addProperty("value", !0); + this.widget = this.addWidget("toggle", "value", !0, "value"); + this.widgets_up = !0; + this.size = [140, 30]; + } + function B() { + this.addOutput("", "string"); + this.addProperty("value", ""); + this.widget = this.addWidget("text", "value", "", "value"); + this.widgets_up = !0; + this.size = [180, 30]; + } + function y() { + this.addInput("url", ""); + this.addOutput("", ""); + this.addProperty("url", ""); + this.addProperty("type", "text"); + this.widget = this.addWidget("text", "url", "", "url"); + this._data = null; + } + function v() { + this.addOutput("", ""); + this.addProperty("value", ""); + this.widget = this.addWidget("text", "json", "", "value"); + this.widgets_up = !0; + this.size = [140, 30]; + this._value = null; + } + function E() { + this.addInput("", ""); + this.addOutput("", "array"); + this.addProperty("value", ""); + this.widget = this.addWidget("text", "array", "", "value"); + this.widgets_up = !0; + this.size = [140, 30]; + this._value = null; + } + function z() { + this.addInput("array", "array,table,string"); + this.addInput("index", "number"); + this.addOutput("value", ""); + this.addProperty("index", 0); + } + function e() { + this.addInput("table", "table"); + this.addInput("row", "number"); + this.addInput("col", "number"); + this.addOutput("value", ""); + this.addProperty("row", 0); + this.addProperty("column", 0); + } + function C() { + this.addInput("obj", ""); + this.addOutput("", ""); + this.addProperty("value", ""); + this.widget = this.addWidget("text", "prop.", "", this.setValue.bind(this)); + this.widgets_up = !0; + this.size = [140, 30]; + this._value = null; + } + function D() { + this.addInput("obj", ""); + this.addOutput("keys", "array"); + this.size = [140, 30]; + } + function t() { + this.addInput("A", "object"); + this.addInput("B", "object"); + this.addOutput("", "object"); + this._result = {}; + var a = this; + this.addWidget("button", "clear", "", function() { + a._result = {}; + }); + this.size = this.computeSize(); + } + function G() { + this.size = [60, 30]; + this.addInput("in"); + this.addOutput("out"); + this.properties = {varname:"myname", global:!1}; + this.value = null; + } + function n() { + this.size = [60, 30]; + this.addInput("data", 0); + this.addInput("download", h.ACTION); + this.properties = {filename:"data.json"}; + this.value = null; + var a = this; + this.addWidget("button", "Download", "", function(b) { + a.value && a.downloadAsFile(); + }); + } + function q() { + this.size = [60, 30]; + this.addInput("value", 0, {label:""}); + this.value = 0; + } + function k() { + this.addInput("in", 0); + this.addOutput("out", 0); + this.size = [40, 30]; + } + function a() { + this.mode = h.ON_EVENT; + this.size = [80, 30]; + this.addProperty("msg", ""); + this.addInput("log", h.EVENT); + this.addInput("msg", 0); + } + function b() { + this.mode = h.ON_EVENT; + this.addProperty("msg", ""); + this.addInput("", h.EVENT); + var a = this; + this.widget = this.addWidget("text", "Text", "", function(b) { + a.properties.msg = b; + }); + this.widgets_up = !0; + this.size = [200, 30]; + } + function d() { + this.size = [60, 30]; + this.addProperty("onExecute", "return A;"); + this.addInput("A", ""); + this.addInput("B", ""); + this.addOutput("out", ""); + this._func = null; + this.data = {}; + } + var h = w.LiteGraph; + c.title = "Time"; + c.desc = "Time"; + c.prototype.onExecute = function() { + this.setOutputData(0, 1000 * this.graph.globaltime); + this.setOutputData(1, this.graph.globaltime); + }; + h.registerNodeType("basic/time", c); + p.title = "Subgraph"; + p.desc = "Graph inside a node"; + p.title_color = "#334"; + p.prototype.onGetInputs = function() { + return [["enabled", "boolean"]]; + }; + p.prototype.onDrawTitle = function(a) { + if (!this.flags.collapsed) { + a.fillStyle = "#555"; + var b = h.NODE_TITLE_HEIGHT, d = this.size[0] - b; + a.fillRect(d, -b, b, b); + a.fillStyle = "#333"; + a.beginPath(); + a.moveTo(d + 0.2 * b, 0.6 * -b); + a.lineTo(d + 0.8 * b, 0.6 * -b); + a.lineTo(d + 0.5 * b, 0.3 * -b); + a.fill(); + } + }; + p.prototype.onDblClick = function(a, b, d) { + var f = this; + setTimeout(function() { + d.openSubgraph(f.subgraph); + }, 10); + }; + p.prototype.onMouseDown = function(a, b, d) { + if (!this.flags.collapsed && b[0] > this.size[0] - h.NODE_TITLE_HEIGHT && 0 > b[1]) { + var f = this; + setTimeout(function() { + d.openSubgraph(f.subgraph); + }, 10); + } + }; + p.prototype.onAction = function(a, b) { + this.subgraph.onAction(a, b); + }; + p.prototype.onExecute = function() { + if (this.enabled = this.getInputOrProperty("enabled")) { + if (this.inputs) { + for (var a = 0; a < this.inputs.length; a++) { + var b = this.inputs[a], d = this.getInputData(a); + this.subgraph.setInputData(b.name, d); + } + } + this.subgraph.runStep(); + if (this.outputs) { + for (a = 0; a < this.outputs.length; a++) { + d = this.subgraph.getOutputData(this.outputs[a].name), this.setOutputData(a, d); + } + } + } + }; + p.prototype.sendEventToAllNodes = function(a, b, d) { + this.enabled && this.subgraph.sendEventToAllNodes(a, b, d); + }; + p.prototype.onSubgraphTrigger = function(a, b) { + a = this.findOutputSlot(a); + -1 != a && this.triggerSlot(a); + }; + p.prototype.onSubgraphNewInput = function(a, b) { + -1 == this.findInputSlot(a) && this.addInput(a, b); + }; + p.prototype.onSubgraphRenamedInput = function(a, b) { + a = this.findInputSlot(a); + -1 != a && (this.getInputInfo(a).name = b); + }; + p.prototype.onSubgraphTypeChangeInput = function(a, b) { + a = this.findInputSlot(a); + -1 != a && (this.getInputInfo(a).type = b); + }; + p.prototype.onSubgraphRemovedInput = function(a) { + a = this.findInputSlot(a); + -1 != a && this.removeInput(a); + }; + p.prototype.onSubgraphNewOutput = function(a, b) { + -1 == this.findOutputSlot(a) && this.addOutput(a, b); + }; + p.prototype.onSubgraphRenamedOutput = function(a, b) { + a = this.findOutputSlot(a); + -1 != a && (this.getOutputInfo(a).name = b); + }; + p.prototype.onSubgraphTypeChangeOutput = function(a, b) { + a = this.findOutputSlot(a); + -1 != a && (this.getOutputInfo(a).type = b); + }; + p.prototype.onSubgraphRemovedOutput = function(a) { + a = this.findInputSlot(a); + -1 != a && this.removeOutput(a); + }; + p.prototype.getExtraMenuOptions = function(a) { + var b = this; + return [{content:"Open", callback:function() { + a.openSubgraph(b.subgraph); + }}]; + }; + p.prototype.onResize = function(a) { + a[1] += 20; + }; + p.prototype.serialize = function() { + var a = h.LGraphNode.prototype.serialize.call(this); + a.subgraph = this.subgraph.serialize(); + return a; + }; + p.prototype.clone = function() { + var a = h.createNode(this.type), b = this.serialize(); + delete b.id; + delete b.inputs; + delete b.outputs; + a.configure(b); + return a; + }; + h.Subgraph = p; + h.registerNodeType("graph/subgraph", p); + m.title = "Input"; + m.desc = "Input of the graph"; + m.prototype.onConfigure = function() { + this.updateType(); + }; + m.prototype.updateType = function() { + var a = this.properties.type; + this.type_widget.value = a; + this.outputs[0].type != a && (this.outputs[0].type = a, this.disconnectOutput(0)); + "number" == a ? (this.value_widget.type = "number", this.value_widget.value = 0) : "boolean" == a ? (this.value_widget.type = "toggle", this.value_widget.value = !0) : "string" == a ? (this.value_widget.type = "text", this.value_widget.value = "") : (this.value_widget.type = null, this.value_widget.value = null); + this.properties.value = this.value_widget.value; + }; + m.prototype.onPropertyChanged = function(a, b) { + if ("name" == a) { + if ("" == b || b == this.name_in_graph || "enabled" == b) { + return !1; + } + this.graph && (this.name_in_graph ? this.graph.renameInput(this.name_in_graph, b) : this.graph.addInput(b, this.properties.type)); + this.name_in_graph = this.name_widget.value = b; + } else { + "type" == a && this.updateType(b || ""); + } + }; + m.prototype.getTitle = function() { + return this.flags.collapsed ? this.properties.name : this.title; + }; + m.prototype.onAction = function(a, b) { + this.properties.type == h.EVENT && this.triggerSlot(0, b); + }; + m.prototype.onExecute = function() { + var a = this.graph.inputs[this.properties.name]; + a ? this.setOutputData(0, void 0 !== a.value ? a.value : this.properties.value) : this.setOutputData(0, this.properties.value); + }; + m.prototype.onRemoved = function() { + this.name_in_graph && this.graph.removeInput(this.name_in_graph); + }; + h.GraphInput = m; + h.registerNodeType("graph/input", m); + g.title = "Output"; + g.desc = "Output of the graph"; + g.prototype.onExecute = function() { + this._value = this.getInputData(0); + this.graph.setOutputData(this.properties.name, this._value); + }; + g.prototype.onAction = function(a, b) { + this.properties.type == h.ACTION && this.graph.trigger(this.properties.name, b); + }; + g.prototype.onRemoved = function() { + this.name_in_graph && this.graph.removeOutput(this.name_in_graph); + }; + g.prototype.getTitle = function() { + return this.flags.collapsed ? this.properties.name : this.title; + }; + h.GraphOutput = g; + h.registerNodeType("graph/output", g); + u.title = "Const Number"; + u.desc = "Constant number"; + u.prototype.onExecute = function() { + this.setOutputData(0, parseFloat(this.properties.value)); + }; + u.prototype.getTitle = function() { + return this.flags.collapsed ? this.properties.value : this.title; + }; + u.prototype.setValue = function(a) { + this.setProperty("value", a); + }; + u.prototype.onDrawBackground = function(a) { + this.outputs[0].label = this.properties.value.toFixed(3); + }; + h.registerNodeType("basic/const", u); + l.title = "Const Boolean"; + l.desc = "Constant boolean"; + l.prototype.getTitle = u.prototype.getTitle; + l.prototype.onExecute = function() { + this.setOutputData(0, this.properties.value); + }; + l.prototype.setValue = u.prototype.setValue; + l.prototype.onGetInputs = function() { + return [["toggle", h.ACTION]]; + }; + l.prototype.onAction = function(a) { + this.setValue(!this.properties.value); + }; + h.registerNodeType("basic/boolean", l); + B.title = "Const String"; + B.desc = "Constant string"; + B.prototype.getTitle = u.prototype.getTitle; + B.prototype.onExecute = function() { + this.setOutputData(0, this.properties.value); + }; + B.prototype.setValue = u.prototype.setValue; + B.prototype.onDropFile = function(a) { + var b = this, d = new FileReader; + d.onload = function(a) { + b.setProperty("value", a.target.result); + }; + d.readAsText(a); + }; + h.registerNodeType("basic/string", B); + y.title = "Const File"; + y.desc = "Fetches a file from an url"; + y["@type"] = {type:"enum", values:["text", "arraybuffer", "blob", "json"]}; + y.prototype.onPropertyChanged = function(a, b) { + "url" == a && (null == b || "" == b ? this._data = null : this.fetchFile(b)); + }; + y.prototype.onExecute = function() { + var a = this.getInputData(0) || this.properties.url; + !a || a == this._url && this._type == this.properties.type || this.fetchFile(a); + this.setOutputData(0, this._data); + }; + y.prototype.setValue = u.prototype.setValue; + y.prototype.fetchFile = function(a) { + var b = this; + a && a.constructor === String ? (this._url = a, this._type = this.properties.type, "http" == a.substr(0, 4) && h.proxy && (a = h.proxy + a.substr(a.indexOf(":") + 3)), fetch(a).then(function(a) { + if (!a.ok) { + throw Error("File not found"); + } + if ("arraybuffer" == b.properties.type) { + return a.arrayBuffer(); + } + if ("text" == b.properties.type) { + return a.text(); + } + if ("json" == b.properties.type) { + return a.json(); + } + if ("blob" == b.properties.type) { + return a.blob(); + } + }).then(function(a) { + b._data = a; + b.boxcolor = "#AEA"; + }).catch(function(d) { + b._data = null; + b.boxcolor = "red"; + console.error("error fetching file:", a); + })) : (b._data = null, b.boxcolor = null); + }; + y.prototype.onDropFile = function(a) { + var b = this; + this._url = a.name; + this._type = this.properties.type; + this.properties.url = a.name; + var d = new FileReader; + d.onload = function(a) { + b.boxcolor = "#AEA"; + a = a.target.result; + "json" == b.properties.type && (a = JSON.parse(a)); + b._data = a; + }; + if ("arraybuffer" == b.properties.type) { + d.readAsArrayBuffer(a); + } else { + if ("text" == b.properties.type || "json" == b.properties.type) { + d.readAsText(a); + } else { + if ("blob" == b.properties.type) { + return d.readAsBinaryString(a); + } + } + } + }; + h.registerNodeType("basic/file", y); + v.title = "Const Data"; + v.desc = "Constant Data"; + v.prototype.onPropertyChanged = function(a, b) { + this.widget.value = b; + if (null != b && "" != b) { + try { + this._value = JSON.parse(b), this.boxcolor = "#AEA"; + } catch (H) { + this.boxcolor = "red"; + } + } + }; + v.prototype.onExecute = function() { + this.setOutputData(0, this._value); + }; + v.prototype.setValue = u.prototype.setValue; + h.registerNodeType("basic/data", v); + E.title = "Const Array"; + E.desc = "Constant Array"; + E.prototype.onPropertyChanged = function(a, b) { + this.widget.value = b; + if (null != b && "" != b) { + try { + this._value = JSON.parse(b), this.boxcolor = "#AEA"; + } catch (H) { + this.boxcolor = "red"; + } + } + }; + E.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a && a.length) { + this._value || (this._value = []); + this._value.length = a.length; + for (var b = 0; b < a.length; ++b) { + this._value[b] = a[b]; + } + } + this.setOutputData(0, this._value); + }; + E.prototype.setValue = u.prototype.setValue; + h.registerNodeType("basic/array", E); + z.title = "Array[i]"; + z.desc = "Returns an element from an array"; + z.prototype.onExecute = function() { + var a = this.getInputData(0), b = this.getInputData(1); + null == b && (b = this.properties.index); + null != a && null != b && this.setOutputData(0, a[Math.floor(Number(b))]); + }; + h.registerNodeType("basic/array[]", z); + e.title = "Table[row][col]"; + e.desc = "Returns an element from a table"; + e.prototype.onExecute = function() { + var a = this.getInputData(0), b = this.getInputData(1), d = this.getInputData(2); + null == b && (b = this.properties.row); + null == d && (d = this.properties.column); + null != a && null != b && null != d && ((b = a[Math.floor(Number(b))]) ? this.setOutputData(0, b[Math.floor(Number(d))]) : this.setOutputData(0, null)); + }; + h.registerNodeType("basic/table[][]", e); + C.title = "Object property"; + C.desc = "Outputs the property of an object"; + C.prototype.setValue = function(a) { + this.properties.value = a; + this.widget.value = a; + }; + C.prototype.getTitle = function() { + return this.flags.collapsed ? "in." + this.properties.value : this.title; + }; + C.prototype.onPropertyChanged = function(a, b) { + this.widget.value = b; + }; + C.prototype.onExecute = function() { + var a = this.getInputData(0); + null != a && this.setOutputData(0, a[this.properties.value]); + }; + h.registerNodeType("basic/object_property", C); + D.title = "Object keys"; + D.desc = "Outputs an array with the keys of an object"; + D.prototype.onExecute = function() { + var a = this.getInputData(0); + null != a && this.setOutputData(0, Object.keys(a)); + }; + h.registerNodeType("basic/object_keys", D); + t.title = "Merge Objects"; + t.desc = "Creates an object copying properties from others"; + t.prototype.onExecute = function() { + var a = this.getInputData(0), b = this.getInputData(1), d = this._result; + if (a) { + for (var c in a) { + d[c] = a[c]; + } + } + if (b) { + for (c in b) { + d[c] = b[c]; + } + } + this.setOutputData(0, d); + }; + h.registerNodeType("basic/merge_objects", t); + G.title = "Variable"; + G.desc = "store/read variable value"; + G.prototype.onExecute = function() { + this.value = this.getInputData(0); + this.graph && (this.graph.vars[this.properties.varname] = this.value); + this.properties.global && (w[this.properties.varname] = this.value); + this.setOutputData(0, this.value); + }; + G.prototype.getTitle = function() { + return this.properties.varname; + }; + h.registerNodeType("basic/variable", G); + h.wrapFunctionAsNode("basic/length", function(a) { + return a && null != a.length ? Number(a.length) : 0; + }, ["*"], "number"); + n.title = "Download"; + n.desc = "Download some data"; + n.prototype.downloadAsFile = function() { + if (null != this.value) { + var a = null; + a = this.value.constructor === String ? this.value : JSON.stringify(this.value); + a = new Blob([a]); + var b = URL.createObjectURL(a); + a = document.createElement("a"); + a.setAttribute("href", b); + a.setAttribute("download", this.properties.filename); + a.style.display = "none"; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + setTimeout(function() { + URL.revokeObjectURL(b); + }, 6E4); + } + }; + n.prototype.onAction = function(a, b) { + var d = this; + setTimeout(function() { + d.downloadAsFile(); + }, 100); + }; + n.prototype.onExecute = function() { + this.inputs[0] && (this.value = this.getInputData(0)); + }; + n.prototype.getTitle = function() { + return this.flags.collapsed ? this.properties.filename : this.title; + }; + h.registerNodeType("basic/download", n); + q.title = "Watch"; + q.desc = "Show value of input"; + q.prototype.onExecute = function() { + this.inputs[0] && (this.value = this.getInputData(0)); + }; + q.prototype.getTitle = function() { + return this.flags.collapsed ? this.inputs[0].label : this.title; + }; + q.toString = function(a) { + if (null == a) { + return "null"; + } + if (a.constructor === Number) { + return a.toFixed(3); + } + if (a.constructor === Array) { + for (var b = "[", d = 0; d < a.length; ++d) { + b += q.toString(a[d]) + (d + 1 != a.length ? "," : ""); + } + return b + "]"; + } + return String(a); + }; + q.prototype.onDrawBackground = function(a) { + this.inputs[0].label = q.toString(this.value); + }; + h.registerNodeType("basic/watch", q); + k.title = "Cast"; + k.desc = "Allows to connect different types"; + k.prototype.onExecute = function() { + this.setOutputData(0, this.getInputData(0)); + }; + h.registerNodeType("basic/cast", k); + a.title = "Console"; + a.desc = "Show value inside the console"; + a.prototype.onAction = function(a, b) { + "log" == a ? console.log(b) : "warn" == a ? console.warn(b) : "error" == a && console.error(b); + }; + a.prototype.onExecute = function() { + var a = this.getInputData(1); + null !== a && (this.properties.msg = a); + console.log(a); + }; + a.prototype.onGetInputs = function() { + return [["log", h.ACTION], ["warn", h.ACTION], ["error", h.ACTION]]; + }; + h.registerNodeType("basic/console", a); + b.title = "Alert"; + b.desc = "Show an alert window"; + b.color = "#510"; + b.prototype.onConfigure = function(a) { + this.widget.value = a.properties.msg; + }; + b.prototype.onAction = function(a, b) { + var d = this.properties.msg; + setTimeout(function() { + alert(d); + }, 10); + }; + h.registerNodeType("basic/alert", b); + d.prototype.onConfigure = function(a) { + a.properties.onExecute && h.allow_scripts ? this.compileCode(a.properties.onExecute) : console.warn("Script not compiled, LiteGraph.allow_scripts is false"); + }; + d.title = "Script"; + d.desc = "executes a code (max 100 characters)"; + d.widgets_info = {onExecute:{type:"code"}}; + d.prototype.onPropertyChanged = function(a, b) { + "onExecute" == a && h.allow_scripts ? this.compileCode(b) : console.warn("Script not compiled, LiteGraph.allow_scripts is false"); + }; + d.prototype.compileCode = function(a) { + this._func = null; + if (256 < a.length) { + console.warn("Script too long, max 256 chars"); + } else { + for (var b = a.toLowerCase(), d = "script body document eval nodescript function".split(" "), c = 0; c < d.length; ++c) { + if (-1 != b.indexOf(d[c])) { + console.warn("invalid script"); + return; + } + } + try { + this._func = new Function("A", "B", "C", "DATA", "node", a); + } catch (J) { + console.error("Error parsing script"), console.error(J); + } + } + }; + d.prototype.onExecute = function() { + if (this._func) { + try { + var a = this.getInputData(0), b = this.getInputData(1), d = this.getInputData(2); + this.setOutputData(0, this._func(a, b, d, this.data, this)); + } catch (I) { + console.error("Error in script"), console.error(I); + } + } + }; + d.prototype.onGetOutputs = function() { + return [["C", ""]]; + }; + h.registerNodeType("basic/script", d); +})(this); +(function(w) { + function c() { + this.size = [60, 30]; + this.addInput("event", v.ACTION); + } + function p() { + this.size = [60, 30]; + this.addInput("if", ""); + this.addOutput("true", v.EVENT); + this.addOutput("change", v.EVENT); + this.addOutput("false", v.EVENT); + this.properties = {only_on_change:!0}; + this.prev = 0; + } + function m() { + this.addInput("", v.ACTION); + this.addInput("", v.ACTION); + this.addInput("", v.ACTION); + this.addInput("", v.ACTION); + this.addInput("", v.ACTION); + this.addInput("", v.ACTION); + this.addOutput("", v.EVENT); + this.addOutput("", v.EVENT); + this.addOutput("", v.EVENT); + this.addOutput("", v.EVENT); + this.addOutput("", v.EVENT); + this.addOutput("", v.EVENT); + this.size = [120, 30]; + this.flags = {horizontal:!0, render_box:!1}; + } + function g() { + this.size = [60, 30]; + this.addInput("event", v.ACTION); + this.addOutput("event", v.EVENT); + this.properties = {equal_to:"", has_property:"", property_equal_to:""}; + } + function u() { + this.addInput("inc", v.ACTION); + this.addInput("dec", v.ACTION); + this.addInput("reset", v.ACTION); + this.addOutput("change", v.EVENT); + this.addOutput("num", "number"); + this.num = 0; + } + function l() { + this.size = [60, 30]; + this.addProperty("time_in_ms", 1000); + this.addInput("event", v.ACTION); + this.addOutput("on_time", v.EVENT); + this._pending = []; + } + function B() { + this.addProperty("interval", 1000); + this.addProperty("event", "tick"); + this.addOutput("on_tick", v.EVENT); + this.time = 0; + this.last_interval = 1000; + this.triggered = !1; + } + function y() { + this.addInput("data", ""); + this.addInput("assign", v.ACTION); + this.addOutput("data", ""); + this._last_value = null; + this.properties = {data:null, serialize:!0}; + var c = this; + this.addWidget("button", "store", "", function() { + c.properties.data = c._last_value; + }); + } + var v = w.LiteGraph; + c.title = "Log Event"; + c.desc = "Log event in console"; + c.prototype.onAction = function(c, g) { + console.log(c, g); + }; + v.registerNodeType("events/log", c); + p.title = "TriggerEvent"; + p.desc = "Triggers event if input evaluates to true"; + p.prototype.onExecute = function(c, g) { + c = this.getInputData(0); + var e = c != this.prev; + 0 === this.prev && (e = !1); + var l = e && this.properties.only_on_change || !e && !this.properties.only_on_change; + c && l && this.triggerSlot(0, g); + !c && l && this.triggerSlot(2, g); + e && this.triggerSlot(1, g); + this.prev = c; + }; + v.registerNodeType("events/trigger", p); + m.title = "Sequencer"; + m.desc = "Trigger events when an event arrives"; + m.prototype.getTitle = function() { + return ""; + }; + m.prototype.onAction = function(c, g) { + if (this.outputs) { + for (c = 0; c < this.outputs.length; ++c) { + this.triggerSlot(c, g); + } + } + }; + v.registerNodeType("events/sequencer", m); + g.title = "Filter Event"; + g.desc = "Blocks events that do not match the filter"; + g.prototype.onAction = function(c, g) { + if (null != g && (!this.properties.equal_to || this.properties.equal_to == g)) { + if (this.properties.has_property && (c = g[this.properties.has_property], null == c || this.properties.property_equal_to && this.properties.property_equal_to != c)) { + return; + } + this.triggerSlot(0, g); + } + }; + v.registerNodeType("events/filter", g); + u.title = "Counter"; + u.desc = "Counts events"; + u.prototype.getTitle = function() { + return this.flags.collapsed ? String(this.num) : this.title; + }; + u.prototype.onAction = function(c, g) { + g = this.num; + "inc" == c ? this.num += 1 : "dec" == c ? --this.num : "reset" == c && (this.num = 0); + this.num != g && this.trigger("change", this.num); + }; + u.prototype.onDrawBackground = function(c) { + this.flags.collapsed || (c.fillStyle = "#AAA", c.font = "20px Arial", c.textAlign = "center", c.fillText(this.num, 0.5 * this.size[0], 0.5 * this.size[1])); + }; + u.prototype.onExecute = function() { + this.setOutputData(1, this.num); + }; + v.registerNodeType("events/counter", u); + l.title = "Delay"; + l.desc = "Delays one event"; + l.prototype.onAction = function(c, g) { + c = this.properties.time_in_ms; + 0 >= c ? this.trigger(null, g) : this._pending.push([c, g]); + }; + l.prototype.onExecute = function() { + var c = 1000 * this.graph.elapsed_time; + this.isInputConnected(1) && (this.properties.time_in_ms = this.getInputData(1)); + for (var g = 0; g < this._pending.length; ++g) { + var e = this._pending[g]; + e[0] -= c; + 0 < e[0] || (this._pending.splice(g, 1), --g, this.trigger(null, e[1])); + } + }; + l.prototype.onGetInputs = function() { + return [["event", v.ACTION], ["time_in_ms", "number"]]; + }; + v.registerNodeType("events/delay", l); + B.title = "Timer"; + B.desc = "Sends an event every N milliseconds"; + B.prototype.onStart = function() { + this.time = 0; + }; + B.prototype.getTitle = function() { + return "Timer: " + this.last_interval.toString() + "ms"; + }; + B.on_color = "#AAA"; + B.off_color = "#222"; + B.prototype.onDrawBackground = function() { + this.boxcolor = this.triggered ? B.on_color : B.off_color; + this.triggered = !1; + }; + B.prototype.onExecute = function() { + var c = 0 == this.time; + this.time += 1000 * this.graph.elapsed_time; + this.last_interval = Math.max(1, this.getInputOrProperty("interval") | 0); + !c && (this.time < this.last_interval || isNaN(this.last_interval)) ? this.inputs && 1 < this.inputs.length && this.inputs[1] && this.setOutputData(1, !1) : (this.triggered = !0, this.time %= this.last_interval, this.trigger("on_tick", this.properties.event), this.inputs && 1 < this.inputs.length && this.inputs[1] && this.setOutputData(1, !0)); + }; + B.prototype.onGetInputs = function() { + return [["interval", "number"]]; + }; + B.prototype.onGetOutputs = function() { + return [["tick", "boolean"]]; + }; + v.registerNodeType("events/timer", B); + y.title = "Data Store"; + y.desc = "Stores data and only changes when event is received"; + y.prototype.onExecute = function() { + this._last_value = this.getInputData(0); + this.setOutputData(0, this.properties.data); + }; + y.prototype.onAction = function(c, g) { + this.properties.data = this._last_value; + }; + y.prototype.onSerialize = function(c) { + null != c.data && (0 == this.properties.serialize || c.data.constructor !== String && c.data.constructor !== Number && c.data.constructor !== Boolean && c.data.constructor !== Array && c.data.constructor !== Object) && (c.data = null); + }; + v.registerNodeType("basic/data_store", y); +})(this); +(function(w) { + function c() { + this.addOutput("", z.EVENT); + this.addOutput("", "boolean"); + this.addProperty("text", "click me"); + this.addProperty("font_size", 30); + this.addProperty("message", ""); + this.size = [164, 84]; + this.clicked = !1; + } + function p() { + this.addInput("", "boolean"); + this.addInput("e", z.ACTION); + this.addOutput("v", "boolean"); + this.addOutput("e", z.EVENT); + this.properties = {font:"", value:!1}; + this.size = [160, 44]; + } + function m() { + this.addOutput("", "number"); + this.size = [80, 60]; + this.properties = {min:-1000, max:1000, value:1, step:1}; + this.old_y = -1; + this._precision = this._remainder = 0; + this.mouse_captured = !1; + } + function g() { + this.addOutput("", "string"); + this.addOutput("change", z.EVENT); + this.size = [80, 60]; + this.properties = {value:"A", values:"A;B;C"}; + this.old_y = -1; + this.mouse_captured = !1; + this._values = this.properties.values.split(";"); + var c = this; + this.widgets_up = !0; + this.widget = this.addWidget("combo", "", this.properties.value, function(e) { + c.properties.value = e; + c.triggerSlot(1, e); + }, {property:"value", values:this._values}); + } + function u() { + this.addOutput("", "number"); + this.size = [64, 84]; + this.properties = {min:0, max:1, value:0.5, color:"#7AF", precision:2}; + this.value = -1; + } + function l() { + this.addOutput("", "number"); + this.properties = {value:0.5, min:0, max:1, text:"V"}; + var c = this; + this.size = [140, 40]; + this.slider = this.addWidget("slider", "V", this.properties.value, function(e) { + c.properties.value = e; + }, this.properties); + this.widgets_up = !0; + } + function B() { + this.size = [160, 26]; + this.addOutput("", "number"); + this.properties = {color:"#7AF", min:0, max:1, value:0.5}; + this.value = -1; + } + function y() { + this.size = [160, 26]; + this.addInput("", "number"); + this.properties = {min:0, max:1, value:0, color:"#AAF"}; + } + function v() { + this.addInputs("", 0); + this.properties = {value:"...", font:"Arial", fontsize:18, color:"#AAA", align:"left", glowSize:0, decimals:1}; + } + function E() { + this.size = [200, 100]; + this.properties = {borderColor:"#ffffff", bgcolorTop:"#f0f0f0", bgcolorBottom:"#e0e0e0", shadowSize:2, borderRadius:3}; + } + var z = w.LiteGraph; + c.title = "Button"; + c.desc = "Triggers an event"; + c.font = "Arial"; + c.prototype.onDrawForeground = function(e) { + if (!this.flags.collapsed && (e.fillStyle = "black", e.fillRect(11, 11, this.size[0] - 20, this.size[1] - 20), e.fillStyle = "#AAF", e.fillRect(9, 9, this.size[0] - 20, this.size[1] - 20), e.fillStyle = this.clicked ? "white" : this.mouseOver ? "#668" : "#334", e.fillRect(10, 10, this.size[0] - 20, this.size[1] - 20), this.properties.text || 0 === this.properties.text)) { + var g = this.properties.font_size || 30; + e.textAlign = "center"; + e.fillStyle = this.clicked ? "black" : "white"; + e.font = g + "px " + c.font; + e.fillText(this.properties.text, 0.5 * this.size[0], 0.5 * this.size[1] + 0.3 * g); + e.textAlign = "left"; + } + }; + c.prototype.onMouseDown = function(c, g) { + if (1 < g[0] && 1 < g[1] && g[0] < this.size[0] - 2 && g[1] < this.size[1] - 2) { + return this.clicked = !0, this.triggerSlot(0, this.properties.message), !0; + } + }; + c.prototype.onExecute = function() { + this.setOutputData(1, this.clicked); + }; + c.prototype.onMouseUp = function(c) { + this.clicked = !1; + }; + z.registerNodeType("widget/button", c); + p.title = "Toggle"; + p.desc = "Toggles between true or false"; + p.prototype.onDrawForeground = function(c) { + if (!this.flags.collapsed) { + var e = 0.5 * this.size[1], g = 0.8 * this.size[1]; + c.font = this.properties.font || (0.8 * e).toFixed(0) + "px Arial"; + var l = c.measureText(this.title).width; + l = 0.5 * (this.size[0] - (l + e)); + c.fillStyle = "#AAA"; + c.fillRect(l, g - e, e, e); + c.fillStyle = this.properties.value ? "#AEF" : "#000"; + c.fillRect(l + 0.25 * e, g - e + 0.25 * e, .5 * e, .5 * e); + c.textAlign = "left"; + c.fillStyle = "#AAA"; + c.fillText(this.title, 1.2 * e + l, 0.85 * g); + c.textAlign = "left"; + } + }; + p.prototype.onAction = function(c) { + this.properties.value = !this.properties.value; + this.trigger("e", this.properties.value); + }; + p.prototype.onExecute = function() { + var c = this.getInputData(0); + null != c && (this.properties.value = c); + this.setOutputData(0, this.properties.value); + }; + p.prototype.onMouseDown = function(c, g) { + if (1 < g[0] && 1 < g[1] && g[0] < this.size[0] - 2 && g[1] < this.size[1] - 2) { + return this.properties.value = !this.properties.value, this.graph._version++, this.trigger("e", this.properties.value), !0; + } + }; + z.registerNodeType("widget/toggle", p); + m.title = "Number"; + m.desc = "Widget to select number value"; + m.pixels_threshold = 10; + m.markers_color = "#666"; + m.prototype.onDrawForeground = function(c) { + var e = 0.5 * this.size[0], g = this.size[1]; + 30 < g ? (c.fillStyle = m.markers_color, c.beginPath(), c.moveTo(e, 0.1 * g), c.lineTo(e + 0.1 * g, 0.2 * g), c.lineTo(e + -0.1 * g, 0.2 * g), c.fill(), c.beginPath(), c.moveTo(e, 0.9 * g), c.lineTo(e + 0.1 * g, 0.8 * g), c.lineTo(e + -0.1 * g, 0.8 * g), c.fill(), c.font = (0.7 * g).toFixed(1) + "px Arial") : c.font = (0.8 * g).toFixed(1) + "px Arial"; + c.textAlign = "center"; + c.font = (0.7 * g).toFixed(1) + "px Arial"; + c.fillStyle = "#EEE"; + c.fillText(this.properties.value.toFixed(this._precision), e, 0.75 * g); + }; + m.prototype.onExecute = function() { + this.setOutputData(0, this.properties.value); + }; + m.prototype.onPropertyChanged = function(c, g) { + c = (this.properties.step + "").split("."); + this._precision = 1 < c.length ? c[1].length : 0; + }; + m.prototype.onMouseDown = function(c, g) { + if (!(0 > g[1])) { + return this.old_y = c.canvasY, this.captureInput(!0), this.mouse_captured = !0; + } + }; + m.prototype.onMouseMove = function(c) { + if (this.mouse_captured) { + var e = this.old_y - c.canvasY; + c.shiftKey && (e *= 10); + if (c.metaKey || c.altKey) { + e *= 0.1; + } + this.old_y = c.canvasY; + c = this._remainder + e / m.pixels_threshold; + this._remainder = c % 1; + c = Math.clamp(this.properties.value + (c | 0) * this.properties.step, this.properties.min, this.properties.max); + this.properties.value = c; + this.graph._version++; + this.setDirtyCanvas(!0); + } + }; + m.prototype.onMouseUp = function(c, g) { + 200 > c.click_time && (this.properties.value = Math.clamp(this.properties.value + (g[1] > 0.5 * this.size[1] ? -1 : 1) * this.properties.step, this.properties.min, this.properties.max), this.graph._version++, this.setDirtyCanvas(!0)); + this.mouse_captured && (this.mouse_captured = !1, this.captureInput(!1)); + }; + z.registerNodeType("widget/number", m); + g.title = "Combo"; + g.desc = "Widget to select from a list"; + g.prototype.onExecute = function() { + this.setOutputData(0, this.properties.value); + }; + g.prototype.onPropertyChanged = function(c, g) { + "values" == c ? (this._values = g.split(";"), this.widget.options.values = this._values) : "value" == c && (this.widget.value = g); + }; + z.registerNodeType("widget/combo", g); + u.title = "Knob"; + u.desc = "Circular controller"; + u.size = [80, 100]; + u.prototype.onDrawForeground = function(c) { + if (!this.flags.collapsed) { + -1 == this.value && (this.value = (this.properties.value - this.properties.min) / (this.properties.max - this.properties.min)); + var e = 0.5 * this.size[0], g = 0.5 * this.size[1], l = 0.5 * Math.min(this.size[0], this.size[1]) - 5; + c.globalAlpha = 1; + c.save(); + c.translate(e, g); + c.rotate(0.75 * Math.PI); + c.fillStyle = "rgba(0,0,0,0.5)"; + c.beginPath(); + c.moveTo(0, 0); + c.arc(0, 0, l, 0, 1.5 * Math.PI); + c.fill(); + c.strokeStyle = "black"; + c.fillStyle = this.properties.color; + c.lineWidth = 2; + c.beginPath(); + c.moveTo(0, 0); + c.arc(0, 0, l - 4, 0, 1.5 * Math.PI * Math.max(0.01, this.value)); + c.closePath(); + c.fill(); + c.lineWidth = 1; + c.globalAlpha = 1; + c.restore(); + c.fillStyle = "black"; + c.beginPath(); + c.arc(e, g, 0.75 * l, 0, 2 * Math.PI, !0); + c.fill(); + c.fillStyle = this.mouseOver ? "white" : this.properties.color; + c.beginPath(); + var m = this.value * Math.PI * 1.5 + 0.75 * Math.PI; + c.arc(e + Math.cos(m) * l * 0.65, g + Math.sin(m) * l * 0.65, 0.05 * l, 0, 2 * Math.PI, !0); + c.fill(); + c.fillStyle = this.mouseOver ? "white" : "#AAA"; + c.font = Math.floor(0.5 * l) + "px Arial"; + c.textAlign = "center"; + c.fillText(this.properties.value.toFixed(this.properties.precision), e, g + 0.15 * l); + } + }; + u.prototype.onExecute = function() { + this.setOutputData(0, this.properties.value); + this.boxcolor = z.colorToString([this.value, this.value, this.value]); + }; + u.prototype.onMouseDown = function(c) { + this.center = [0.5 * this.size[0], 0.5 * this.size[1] + 20]; + this.radius = 0.5 * this.size[0]; + if (20 > c.canvasY - this.pos[1] || z.distance([c.canvasX, c.canvasY], [this.pos[0] + this.center[0], this.pos[1] + this.center[1]]) > this.radius) { + return !1; + } + this.oldmouse = [c.canvasX - this.pos[0], c.canvasY - this.pos[1]]; + this.captureInput(!0); + return !0; + }; + u.prototype.onMouseMove = function(c) { + if (this.oldmouse) { + c = [c.canvasX - this.pos[0], c.canvasY - this.pos[1]]; + var e = this.value; + e -= 0.01 * (c[1] - this.oldmouse[1]); + 1.0 < e ? e = 1.0 : 0.0 > e && (e = 0.0); + this.value = e; + this.properties.value = this.properties.min + (this.properties.max - this.properties.min) * this.value; + this.oldmouse = c; + this.setDirtyCanvas(!0); + } + }; + u.prototype.onMouseUp = function(c) { + this.oldmouse && (this.oldmouse = null, this.captureInput(!1)); + }; + u.prototype.onPropertyChanged = function(c, g) { + if ("min" == c || "max" == c || "value" == c) { + return this.properties[c] = parseFloat(g), !0; + } + }; + z.registerNodeType("widget/knob", u); + l.title = "Inner Slider"; + l.prototype.onPropertyChanged = function(c, g) { + "value" == c && (this.slider.value = g); + }; + l.prototype.onExecute = function() { + this.setOutputData(0, this.properties.value); + }; + z.registerNodeType("widget/internal_slider", l); + B.title = "H.Slider"; + B.desc = "Linear slider controller"; + B.prototype.onDrawForeground = function(c) { + -1 == this.value && (this.value = (this.properties.value - this.properties.min) / (this.properties.max - this.properties.min)); + c.globalAlpha = 1; + c.lineWidth = 1; + c.fillStyle = "#000"; + c.fillRect(2, 2, this.size[0] - 4, this.size[1] - 4); + c.fillStyle = this.properties.color; + c.beginPath(); + c.rect(4, 4, (this.size[0] - 8) * this.value, this.size[1] - 8); + c.fill(); + }; + B.prototype.onExecute = function() { + this.properties.value = this.properties.min + (this.properties.max - this.properties.min) * this.value; + this.setOutputData(0, this.properties.value); + this.boxcolor = z.colorToString([this.value, this.value, this.value]); + }; + B.prototype.onMouseDown = function(c) { + if (0 > c.canvasY - this.pos[1]) { + return !1; + } + this.oldmouse = [c.canvasX - this.pos[0], c.canvasY - this.pos[1]]; + this.captureInput(!0); + return !0; + }; + B.prototype.onMouseMove = function(c) { + if (this.oldmouse) { + c = [c.canvasX - this.pos[0], c.canvasY - this.pos[1]]; + var e = this.value; + e += (c[0] - this.oldmouse[0]) / this.size[0]; + 1.0 < e ? e = 1.0 : 0.0 > e && (e = 0.0); + this.value = e; + this.oldmouse = c; + this.setDirtyCanvas(!0); + } + }; + B.prototype.onMouseUp = function(c) { + this.oldmouse = null; + this.captureInput(!1); + }; + B.prototype.onMouseLeave = function(c) { + }; + z.registerNodeType("widget/hslider", B); + y.title = "Progress"; + y.desc = "Shows data in linear progress"; + y.prototype.onExecute = function() { + var c = this.getInputData(0); + void 0 != c && (this.properties.value = c); + }; + y.prototype.onDrawForeground = function(c) { + c.lineWidth = 1; + c.fillStyle = this.properties.color; + var e = (this.properties.value - this.properties.min) / (this.properties.max - this.properties.min); + e = Math.min(1, e); + e = Math.max(0, e); + c.fillRect(2, 2, (this.size[0] - 4) * e, this.size[1] - 4); + }; + z.registerNodeType("widget/progress", y); + v.title = "Text"; + v.desc = "Shows the input value"; + v.widgets = [{name:"resize", text:"Resize box", type:"button"}, {name:"led_text", text:"LED", type:"minibutton"}, {name:"normal_text", text:"Normal", type:"minibutton"}]; + v.prototype.onDrawForeground = function(c) { + c.fillStyle = this.properties.color; + var e = this.properties.value; + this.properties.glowSize ? (c.shadowColor = this.properties.color, c.shadowOffsetX = 0, c.shadowOffsetY = 0, c.shadowBlur = this.properties.glowSize) : c.shadowColor = "transparent"; + var g = this.properties.fontsize; + c.textAlign = this.properties.align; + c.font = g.toString() + "px " + this.properties.font; + this.str = "number" == typeof e ? e.toFixed(this.properties.decimals) : e; + if ("string" == typeof this.str) { + e = this.str.split("\\n"); + for (var l in e) { + c.fillText(e[l], "left" == this.properties.align ? 15 : this.size[0] - 15, -0.15 * g + g * (parseInt(l) + 1)); + } + } + c.shadowColor = "transparent"; + this.last_ctx = c; + c.textAlign = "left"; + }; + v.prototype.onExecute = function() { + var c = this.getInputData(0); + null != c && (this.properties.value = c); + }; + v.prototype.resize = function() { + if (this.last_ctx) { + var c = this.str.split("\\n"); + this.last_ctx.font = this.properties.fontsize + "px " + this.properties.font; + var g = 0, l; + for (l in c) { + var m = this.last_ctx.measureText(c[l]).width; + g < m && (g = m); + } + this.size[0] = g + 20; + this.size[1] = 4 + c.length * this.properties.fontsize; + this.setDirtyCanvas(!0); + } + }; + v.prototype.onPropertyChanged = function(c, g) { + this.properties[c] = g; + this.str = "number" == typeof g ? g.toFixed(3) : g; + return !0; + }; + z.registerNodeType("widget/text", v); + E.title = "Panel"; + E.desc = "Non interactive panel"; + E.widgets = [{name:"update", text:"Update", type:"button"}]; + E.prototype.createGradient = function(c) { + "" == this.properties.bgcolorTop || "" == this.properties.bgcolorBottom ? this.lineargradient = 0 : (this.lineargradient = c.createLinearGradient(0, 0, 0, this.size[1]), this.lineargradient.addColorStop(0, this.properties.bgcolorTop), this.lineargradient.addColorStop(1, this.properties.bgcolorBottom)); + }; + E.prototype.onDrawForeground = function(c) { + this.flags.collapsed || (null == this.lineargradient && this.createGradient(c), this.lineargradient && (c.lineWidth = 1, c.strokeStyle = this.properties.borderColor, c.fillStyle = this.lineargradient, this.properties.shadowSize ? (c.shadowColor = "#000", c.shadowOffsetX = 0, c.shadowOffsetY = 0, c.shadowBlur = this.properties.shadowSize) : c.shadowColor = "transparent", c.roundRect(0, 0, this.size[0] - 1, this.size[1] - 1, this.properties.shadowSize), c.fill(), c.shadowColor = "transparent", + c.stroke())); + }; + z.registerNodeType("widget/panel", E); +})(this); +(function(w) { + function c() { + this.addOutput("left_x_axis", "number"); + this.addOutput("left_y_axis", "number"); + this.addOutput("button_pressed", p.EVENT); + this.properties = {gamepad_index:0, threshold:0.1}; + this._left_axis = new Float32Array(2); + this._right_axis = new Float32Array(2); + this._triggers = new Float32Array(2); + this._previous_buttons = new Uint8Array(17); + this._current_buttons = new Uint8Array(17); + } + var p = w.LiteGraph; + c.title = "Gamepad"; + c.desc = "gets the input of the gamepad"; + c.CENTER = 0; + c.LEFT = 1; + c.RIGHT = 2; + c.UP = 4; + c.DOWN = 8; + c.zero = new Float32Array(2); + c.buttons = "a b x y lb rb lt rt back start ls rs home".split(" "); + c.prototype.onExecute = function() { + var m = this.getGamepad(), g = this.properties.threshold || 0.0; + m && (this._left_axis[0] = Math.abs(m.xbox.axes.lx) > g ? m.xbox.axes.lx : 0, this._left_axis[1] = Math.abs(m.xbox.axes.ly) > g ? m.xbox.axes.ly : 0, this._right_axis[0] = Math.abs(m.xbox.axes.rx) > g ? m.xbox.axes.rx : 0, this._right_axis[1] = Math.abs(m.xbox.axes.ry) > g ? m.xbox.axes.ry : 0, this._triggers[0] = Math.abs(m.xbox.axes.ltrigger) > g ? m.xbox.axes.ltrigger : 0, this._triggers[1] = Math.abs(m.xbox.axes.rtrigger) > g ? m.xbox.axes.rtrigger : 0); + if (this.outputs) { + for (g = 0; g < this.outputs.length; g++) { + var p = this.outputs[g]; + if (p.links && p.links.length) { + var l = null; + if (m) { + switch(p.name) { + case "left_axis": + l = this._left_axis; + break; + case "right_axis": + l = this._right_axis; + break; + case "left_x_axis": + l = this._left_axis[0]; + break; + case "left_y_axis": + l = this._left_axis[1]; + break; + case "right_x_axis": + l = this._right_axis[0]; + break; + case "right_y_axis": + l = this._right_axis[1]; + break; + case "trigger_left": + l = this._triggers[0]; + break; + case "trigger_right": + l = this._triggers[1]; + break; + case "a_button": + l = m.xbox.buttons.a ? 1 : 0; + break; + case "b_button": + l = m.xbox.buttons.b ? 1 : 0; + break; + case "x_button": + l = m.xbox.buttons.x ? 1 : 0; + break; + case "y_button": + l = m.xbox.buttons.y ? 1 : 0; + break; + case "lb_button": + l = m.xbox.buttons.lb ? 1 : 0; + break; + case "rb_button": + l = m.xbox.buttons.rb ? 1 : 0; + break; + case "ls_button": + l = m.xbox.buttons.ls ? 1 : 0; + break; + case "rs_button": + l = m.xbox.buttons.rs ? 1 : 0; + break; + case "hat_left": + l = m.xbox.hatmap & c.LEFT; + break; + case "hat_right": + l = m.xbox.hatmap & c.RIGHT; + break; + case "hat_up": + l = m.xbox.hatmap & c.UP; + break; + case "hat_down": + l = m.xbox.hatmap & c.DOWN; + break; + case "hat": + l = m.xbox.hatmap; + break; + case "start_button": + l = m.xbox.buttons.start ? 1 : 0; + break; + case "back_button": + l = m.xbox.buttons.back ? 1 : 0; + break; + case "button_pressed": + for (p = 0; p < this._current_buttons.length; ++p) { + this._current_buttons[p] && !this._previous_buttons[p] && this.triggerSlot(g, c.buttons[p]); + } + } + } else { + switch(p.name) { + case "button_pressed": + break; + case "left_axis": + case "right_axis": + l = c.zero; + break; + default: + l = 0; + } + } + this.setOutputData(g, l); + } + } + } + }; + c.mapping = {a:0, b:1, x:2, y:3, lb:4, rb:5, lt:6, rt:7, back:8, start:9, ls:10, rs:11}; + c.mapping_array = "a b x y lb rb lt rt back start ls rs".split(" "); + c.prototype.getGamepad = function() { + var m = navigator.getGamepads || navigator.webkitGetGamepads || navigator.mozGetGamepads; + if (!m) { + return null; + } + m = m.call(navigator); + this._previous_buttons.set(this._current_buttons); + for (var g = this.properties.gamepad_index; 4 > g; g++) { + if (m[g]) { + m = m[g]; + g = this.xbox_mapping; + g || (g = this.xbox_mapping = {axes:[], buttons:{}, hat:"", hatmap:c.CENTER}); + g.axes.lx = m.axes[0]; + g.axes.ly = m.axes[1]; + g.axes.rx = m.axes[2]; + g.axes.ry = m.axes[3]; + g.axes.ltrigger = m.buttons[6].value; + g.axes.rtrigger = m.buttons[7].value; + g.hat = ""; + g.hatmap = c.CENTER; + for (var p = 0; p < m.buttons.length; p++) { + if (this._current_buttons[p] = m.buttons[p].pressed, 12 > p) { + g.buttons[c.mapping_array[p]] = m.buttons[p].pressed, m.buttons[p].was_pressed && this.trigger(c.mapping_array[p] + "_button_event"); + } else { + switch(p) { + case 12: + m.buttons[p].pressed && (g.hat += "up", g.hatmap |= c.UP); + break; + case 13: + m.buttons[p].pressed && (g.hat += "down", g.hatmap |= c.DOWN); + break; + case 14: + m.buttons[p].pressed && (g.hat += "left", g.hatmap |= c.LEFT); + break; + case 15: + m.buttons[p].pressed && (g.hat += "right", g.hatmap |= c.RIGHT); + break; + case 16: + g.buttons.home = m.buttons[p].pressed; + } + } + } + m.xbox = g; + return m; + } + } + }; + c.prototype.onDrawBackground = function(c) { + if (!this.flags.collapsed) { + var g = this._left_axis, m = this._right_axis; + c.strokeStyle = "#88A"; + c.strokeRect(0.5 * (g[0] + 1) * this.size[0] - 4, 0.5 * (g[1] + 1) * this.size[1] - 4, 8, 8); + c.strokeStyle = "#8A8"; + c.strokeRect(0.5 * (m[0] + 1) * this.size[0] - 4, 0.5 * (m[1] + 1) * this.size[1] - 4, 8, 8); + g = this.size[1] / this._current_buttons.length; + c.fillStyle = "#AEB"; + for (m = 0; m < this._current_buttons.length; ++m) { + this._current_buttons[m] && c.fillRect(0, g * m, 6, g); + } + } + }; + c.prototype.onGetOutputs = function() { + return [["left_axis", "vec2"], ["right_axis", "vec2"], ["left_x_axis", "number"], ["left_y_axis", "number"], ["right_x_axis", "number"], ["right_y_axis", "number"], ["trigger_left", "number"], ["trigger_right", "number"], ["a_button", "number"], ["b_button", "number"], ["x_button", "number"], ["y_button", "number"], ["lb_button", "number"], ["rb_button", "number"], ["ls_button", "number"], ["rs_button", "number"], ["start_button", "number"], ["back_button", "number"], ["a_button_event", p.EVENT], + ["b_button_event", p.EVENT], ["x_button_event", p.EVENT], ["y_button_event", p.EVENT], ["lb_button_event", p.EVENT], ["rb_button_event", p.EVENT], ["ls_button_event", p.EVENT], ["rs_button_event", p.EVENT], ["start_button_event", p.EVENT], ["back_button_event", p.EVENT], ["hat_left", "number"], ["hat_right", "number"], ["hat_up", "number"], ["hat_down", "number"], ["hat", "number"], ["button_pressed", p.EVENT]]; + }; + p.registerNodeType("input/gamepad", c); +})(this); +(function(w) { + function c() { + this.addInput("in", "*"); + this.size = [80, 30]; + } + function p() { + this.addInput("in"); + this.addOutput("out"); + this.size = [80, 30]; + } + function m() { + this.addInput("in"); + this.addOutput("out"); + } + function g() { + this.addInput("in", "number", {locked:!0}); + this.addOutput("out", "number", {locked:!0}); + this.addOutput("clamped", "number", {locked:!0}); + this.addProperty("in", 0); + this.addProperty("in_min", 0); + this.addProperty("in_max", 1); + this.addProperty("out_min", 0); + this.addProperty("out_max", 1); + this.size = [120, 50]; + } + function u() { + this.addOutput("value", "number"); + this.addProperty("min", 0); + this.addProperty("max", 1); + this.size = [80, 30]; + } + function l() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.addProperty("min", 0); + this.addProperty("max", 1); + this.addProperty("smooth", !0); + this.size = [90, 30]; + } + function B() { + this.addOutput("out", "number"); + this.addProperty("min_time", 1); + this.addProperty("max_time", 2); + this.addProperty("duration", 0.2); + this.size = [90, 30]; + this._blink_time = this._remaining_time = 0; + } + function y() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.size = [80, 30]; + this.addProperty("min", 0); + this.addProperty("max", 1); + } + function v() { + this.properties = {f:0.5}; + this.addInput("A", "number"); + this.addInput("B", "number"); + this.addOutput("out", "number"); + } + function E() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.size = [80, 30]; + } + function z() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.size = [80, 30]; + } + function e() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.size = [80, 30]; + } + function C() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.size = [80, 30]; + this.properties = {A:0, B:1}; + } + function D() { + this.addInput("in", "number", {label:""}); + this.addOutput("out", "number", {label:""}); + this.size = [80, 30]; + this.addProperty("factor", 1); + } + function t() { + this.addInput("v", "boolean"); + this.addInput("A"); + this.addInput("B"); + this.addOutput("out"); + } + function G() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.size = [80, 30]; + this.addProperty("samples", 10); + this._values = new Float32Array(10); + this._current = 0; + } + function n() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.addProperty("factor", 0.1); + this.size = [80, 30]; + this._value = null; + } + function q() { + this.addInput("A", "number"); + this.addInput("B", "number"); + this.addOutput("=", "number"); + this.addProperty("A", 1); + this.addProperty("B", 1); + this.addProperty("OP", "+", "enum", {values:q.values}); + } + function k() { + this.addInput("A", "number"); + this.addInput("B", "number"); + this.addOutput("A==B", "boolean"); + this.addOutput("A!=B", "boolean"); + this.addProperty("A", 0); + this.addProperty("B", 0); + } + function a() { + this.addInput("A", "number"); + this.addInput("B", "number"); + this.addOutput("true", "boolean"); + this.addOutput("false", "boolean"); + this.addProperty("A", 1); + this.addProperty("B", 1); + this.addProperty("OP", ">", "enum", {values:a.values}); + this.size = [80, 60]; + } + function b() { + this.addInput("inc", "number"); + this.addOutput("total", "number"); + this.addProperty("increment", 1); + this.addProperty("value", 0); + } + function d() { + this.addInput("v", "number"); + this.addOutput("sin", "number"); + this.addProperty("amplitude", 1); + this.addProperty("offset", 0); + this.bgImageUrl = "nodes/imgs/icon-sin.png"; + } + function h() { + this.addInput("x", "number"); + this.addInput("y", "number"); + this.addOutput("", "number"); + this.properties = {x:1.0, y:1.0, formula:"x+y"}; + this.code_widget = this.addWidget("text", "F(x,y)", this.properties.formula, function(a, b, d) { + d.properties.formula = a; + }); + this.addWidget("toggle", "allow", A.allow_scripts, function(a) { + A.allow_scripts = a; + }); + this._func = null; + } + function f() { + this.addInput("vec2", "vec2"); + this.addOutput("x", "number"); + this.addOutput("y", "number"); + } + function x() { + this.addInputs([["x", "number"], ["y", "number"]]); + this.addOutput("vec2", "vec2"); + this.properties = {x:0, y:0}; + this._data = new Float32Array(2); + } + function H() { + this.addInput("vec3", "vec3"); + this.addOutput("x", "number"); + this.addOutput("y", "number"); + this.addOutput("z", "number"); + } + function I() { + this.addInputs([["x", "number"], ["y", "number"], ["z", "number"]]); + this.addOutput("vec3", "vec3"); + this.properties = {x:0, y:0, z:0}; + this._data = new Float32Array(3); + } + function J() { + this.addInput("vec4", "vec4"); + this.addOutput("x", "number"); + this.addOutput("y", "number"); + this.addOutput("z", "number"); + this.addOutput("w", "number"); + } + function L() { + this.addInputs([["x", "number"], ["y", "number"], ["z", "number"], ["w", "number"]]); + this.addOutput("vec4", "vec4"); + this.properties = {x:0, y:0, z:0, w:0}; + this._data = new Float32Array(4); + } + var A = w.LiteGraph; + c.title = "Converter"; + c.desc = "type A to type B"; + c.prototype.onExecute = function() { + var a = this.getInputData(0); + if (null != a && this.outputs) { + for (var b = 0; b < this.outputs.length; b++) { + var d = this.outputs[b]; + if (d.links && d.links.length) { + var c = null; + switch(d.name) { + case "number": + c = a.length ? a[0] : parseFloat(a); + break; + case "vec2": + case "vec3": + case "vec4": + c = 1; + switch(d.name) { + case "vec2": + c = 2; + break; + case "vec3": + c = 3; + break; + case "vec4": + c = 4; + }c = new Float32Array(c); + if (a.length) { + for (d = 0; d < a.length && d < c.length; d++) { + c[d] = a[d]; + } + } else { + c[0] = parseFloat(a); + } + } + this.setOutputData(b, c); + } + } + } + }; + c.prototype.onGetOutputs = function() { + return [["number", "number"], ["vec2", "vec2"], ["vec3", "vec3"], ["vec4", "vec4"]]; + }; + A.registerNodeType("math/converter", c); + p.title = "Bypass"; + p.desc = "removes the type"; + p.prototype.onExecute = function() { + var a = this.getInputData(0); + this.setOutputData(0, a); + }; + A.registerNodeType("math/bypass", p); + m.title = "to Number"; + m.desc = "Cast to number"; + m.prototype.onExecute = function() { + var a = this.getInputData(0); + this.setOutputData(0, Number(a)); + }; + A.registerNodeType("math/to_number", m); + g.title = "Range"; + g.desc = "Convert a number from one range to another"; + g.prototype.getTitle = function() { + return this.flags.collapsed ? (this._last_v || 0).toFixed(2) : this.title; + }; + g.prototype.onExecute = function() { + if (this.inputs) { + for (var a = 0; a < this.inputs.length; a++) { + var b = this.inputs[a], d = this.getInputData(a); + void 0 !== d && (this.properties[b.name] = d); + } + } + d = this.properties["in"]; + if (void 0 === d || null === d || d.constructor !== Number) { + d = 0; + } + a = this.properties.in_min; + b = this.properties.out_min; + var c = this.properties.out_max; + this._last_v = (d - a) / (this.properties.in_max - a) * (c - b) + b; + this.setOutputData(0, this._last_v); + this.setOutputData(1, Math.clamp(this._last_v, b, c)); + }; + g.prototype.onDrawBackground = function(a) { + this.outputs[0].label = this._last_v ? this._last_v.toFixed(3) : "?"; + }; + g.prototype.onGetInputs = function() { + return [["in_min", "number"], ["in_max", "number"], ["out_min", "number"], ["out_max", "number"]]; + }; + A.registerNodeType("math/range", g); + u.title = "Rand"; + u.desc = "Random number"; + u.prototype.onExecute = function() { + if (this.inputs) { + for (var a = 0; a < this.inputs.length; a++) { + var b = this.inputs[a], d = this.getInputData(a); + void 0 !== d && (this.properties[b.name] = d); + } + } + a = this.properties.min; + this._last_v = Math.random() * (this.properties.max - a) + a; + this.setOutputData(0, this._last_v); + }; + u.prototype.onDrawBackground = function(a) { + this.outputs[0].label = (this._last_v || 0).toFixed(3); + }; + u.prototype.onGetInputs = function() { + return [["min", "number"], ["max", "number"]]; + }; + A.registerNodeType("math/rand", u); + l.title = "Noise"; + l.desc = "Random number with temporal continuity"; + l.data = null; + l.getValue = function(a, b) { + if (!l.data) { + l.data = new Float32Array(1024); + for (var d = 0; d < l.data.length; ++d) { + l.data[d] = Math.random(); + } + } + a %= 1024; + 0 > a && (a += 1024); + var c = Math.floor(a); + a -= c; + d = l.data[c]; + c = l.data[1023 == c ? 0 : c + 1]; + b && (a = a * a * a * (a * (6.0 * a - 15.0) + 10.0)); + return d * (1 - a) + c * a; + }; + l.prototype.onExecute = function() { + var a = this.getInputData(0) || 0; + a = l.getValue(a, this.properties.smooth); + var b = this.properties.min; + this._last_v = a * (this.properties.max - b) + b; + this.setOutputData(0, this._last_v); + }; + l.prototype.onDrawBackground = function(a) { + this.outputs[0].label = (this._last_v || 0).toFixed(3); + }; + A.registerNodeType("math/noise", l); + B.title = "Spikes"; + B.desc = "spike every random time"; + B.prototype.onExecute = function() { + var a = this.graph.elapsed_time; + this._remaining_time -= a; + this._blink_time -= a; + a = 0; + 0 < this._blink_time && (a = 1 / (Math.pow(this._blink_time / this.properties.duration * 8 - 4, 4) + 1)); + 0 > this._remaining_time ? (this._remaining_time = Math.random() * (this.properties.max_time - this.properties.min_time) + this.properties.min_time, this._blink_time = this.properties.duration, this.boxcolor = "#FFF") : this.boxcolor = "#000"; + this.setOutputData(0, a); + }; + A.registerNodeType("math/spikes", B); + y.title = "Clamp"; + y.desc = "Clamp number between min and max"; + y.prototype.onExecute = function() { + var a = this.getInputData(0); + null != a && (a = Math.max(this.properties.min, a), a = Math.min(this.properties.max, a), this.setOutputData(0, a)); + }; + y.prototype.getCode = function(a) { + a = ""; + this.isInputConnected(0) && (a += "clamp({{0}}," + this.properties.min + "," + this.properties.max + ")"); + return a; + }; + A.registerNodeType("math/clamp", y); + v.title = "Lerp"; + v.desc = "Linear Interpolation"; + v.prototype.onExecute = function() { + var a = this.getInputData(0); + null == a && (a = 0); + var b = this.getInputData(1); + null == b && (b = 0); + var d = this.properties.f, c = this.getInputData(2); + void 0 !== c && (d = c); + this.setOutputData(0, a * (1 - d) + b * d); + }; + v.prototype.onGetInputs = function() { + return [["f", "number"]]; + }; + A.registerNodeType("math/lerp", v); + E.title = "Abs"; + E.desc = "Absolute"; + E.prototype.onExecute = function() { + var a = this.getInputData(0); + null != a && this.setOutputData(0, Math.abs(a)); + }; + A.registerNodeType("math/abs", E); + z.title = "Floor"; + z.desc = "Floor number to remove fractional part"; + z.prototype.onExecute = function() { + var a = this.getInputData(0); + null != a && this.setOutputData(0, Math.floor(a)); + }; + A.registerNodeType("math/floor", z); + e.title = "Frac"; + e.desc = "Returns fractional part"; + e.prototype.onExecute = function() { + var a = this.getInputData(0); + null != a && this.setOutputData(0, a % 1); + }; + A.registerNodeType("math/frac", e); + C.title = "Smoothstep"; + C.desc = "Smoothstep"; + C.prototype.onExecute = function() { + var a = this.getInputData(0); + if (void 0 !== a) { + var b = this.properties.A; + a = Math.clamp((a - b) / (this.properties.B - b), 0.0, 1.0); + this.setOutputData(0, a * a * (3 - 2 * a)); + } + }; + A.registerNodeType("math/smoothstep", C); + D.title = "Scale"; + D.desc = "v * factor"; + D.prototype.onExecute = function() { + var a = this.getInputData(0); + null != a && this.setOutputData(0, a * this.properties.factor); + }; + A.registerNodeType("math/scale", D); + t.title = "Gate"; + t.desc = "if v is true, then outputs A, otherwise B"; + t.prototype.onExecute = function() { + var a = this.getInputData(0); + this.setOutputData(0, this.getInputData(a ? 1 : 2)); + }; + A.registerNodeType("math/gate", t); + G.title = "Average"; + G.desc = "Average Filter"; + G.prototype.onExecute = function() { + var a = this.getInputData(0); + null == a && (a = 0); + var b = this._values.length; + this._values[this._current % b] = a; + this._current += 1; + this._current > b && (this._current = 0); + for (var d = a = 0; d < b; ++d) { + a += this._values[d]; + } + this.setOutputData(0, a / b); + }; + G.prototype.onPropertyChanged = function(a, b) { + 1 > b && (b = 1); + this.properties.samples = Math.round(b); + a = this._values; + this._values = new Float32Array(this.properties.samples); + a.length <= this._values.length ? this._values.set(a) : this._values.set(a.subarray(0, this._values.length)); + }; + A.registerNodeType("math/average", G); + n.title = "TendTo"; + n.desc = "moves the output value always closer to the input"; + n.prototype.onExecute = function() { + var a = this.getInputData(0); + null == a && (a = 0); + var b = this.properties.factor; + this._value = null == this._value ? a : this._value * (1 - b) + a * b; + this.setOutputData(0, this._value); + }; + A.registerNodeType("math/tendTo", n); + q.values = "+ - * / % ^ max min".split(" "); + q.title = "Operation"; + q.desc = "Easy math operators"; + q["@OP"] = {type:"enum", title:"operation", values:q.values}; + q.size = [100, 60]; + q.prototype.getTitle = function() { + return "max" == this.properties.OP || "min" == this.properties.OP ? this.properties.OP + "(A,B)" : "A " + this.properties.OP + " B"; + }; + q.prototype.setValue = function(a) { + "string" == typeof a && (a = parseFloat(a)); + this.properties.value = a; + }; + q.prototype.onExecute = function() { + var a = this.getInputData(0), b = this.getInputData(1); + null != a ? this.properties.A = a : a = this.properties.A; + null != b ? this.properties.B = b : b = this.properties.B; + var d = 0; + switch(this.properties.OP) { + case "+": + d = a + b; + break; + case "-": + d = a - b; + break; + case "x": + case "X": + case "*": + d = a * b; + break; + case "/": + d = a / b; + break; + case "%": + d = a % b; + break; + case "^": + d = Math.pow(a, b); + break; + case "max": + d = Math.max(a, b); + break; + case "min": + d = Math.min(a, b); + break; + default: + console.warn("Unknown operation: " + this.properties.OP); + } + this.setOutputData(0, d); + }; + q.prototype.onDrawBackground = function(a) { + this.flags.collapsed || (a.font = "40px Arial", a.fillStyle = "#666", a.textAlign = "center", a.fillText(this.properties.OP, 0.5 * this.size[0], 0.5 * (this.size[1] + A.NODE_TITLE_HEIGHT)), a.textAlign = "left"); + }; + A.registerNodeType("math/operation", q); + A.registerSearchboxExtra("math/operation", "MAX", {properties:{OP:"max"}, title:"MAX()"}); + A.registerSearchboxExtra("math/operation", "MIN", {properties:{OP:"min"}, title:"MIN()"}); + k.title = "Compare"; + k.desc = "compares between two values"; + k.prototype.onExecute = function() { + var a = this.getInputData(0), b = this.getInputData(1); + void 0 !== a ? this.properties.A = a : a = this.properties.A; + void 0 !== b ? this.properties.B = b : b = this.properties.B; + for (var d = 0, c = this.outputs.length; d < c; ++d) { + var k = this.outputs[d]; + if (k.links && k.links.length) { + switch(k.name) { + case "A==B": + var f = a == b; + break; + case "A!=B": + f = a != b; + break; + case "A>B": + f = a > b; + break; + case "A=B": + f = a >= b; + } + this.setOutputData(d, f); + } + } + }; + k.prototype.onGetOutputs = function() { + return [["A==B", "boolean"], ["A!=B", "boolean"], ["A>B", "boolean"], ["A=B", "boolean"], ["A<=B", "boolean"]]; + }; + A.registerNodeType("math/compare", k); + A.registerSearchboxExtra("math/compare", "==", {outputs:[["A==B", "boolean"]], title:"A==B"}); + A.registerSearchboxExtra("math/compare", "!=", {outputs:[["A!=B", "boolean"]], title:"A!=B"}); + A.registerSearchboxExtra("math/compare", ">", {outputs:[["A>B", "boolean"]], title:"A>B"}); + A.registerSearchboxExtra("math/compare", "<", {outputs:[["A=", {outputs:[["A>=B", "boolean"]], title:"A>=B"}); + A.registerSearchboxExtra("math/compare", "<=", {outputs:[["A<=B", "boolean"]], title:"A<=B"}); + a.values = "> < == != <= >= || &&".split(" "); + a["@OP"] = {type:"enum", title:"operation", values:a.values}; + a.title = "Condition"; + a.desc = "evaluates condition between A and B"; + a.prototype.getTitle = function() { + return "A " + this.properties.OP + " B"; + }; + a.prototype.onExecute = function() { + var a = this.getInputData(0); + void 0 === a ? a = this.properties.A : this.properties.A = a; + var b = this.getInputData(1); + void 0 === b ? b = this.properties.B : this.properties.B = b; + var d = !0; + switch(this.properties.OP) { + case ">": + d = a > b; + break; + case "<": + d = a < b; + break; + case "==": + d = a == b; + break; + case "!=": + d = a != b; + break; + case "<=": + d = a <= b; + break; + case ">=": + d = a >= b; + break; + case "||": + d = a || b; + break; + case "&&": + d = a && b; + } + this.setOutputData(0, d); + this.setOutputData(1, !d); + }; + A.registerNodeType("math/condition", a); + b.title = "Accumulate"; + b.desc = "Increments a value every time"; + b.prototype.onExecute = function() { + null === this.properties.value && (this.properties.value = 0); + var a = this.getInputData(0); + this.properties.value = null !== a ? this.properties.value + a : this.properties.value + this.properties.increment; + this.setOutputData(0, this.properties.value); + }; + A.registerNodeType("math/accumulate", b); + d.title = "Trigonometry"; + d.desc = "Sin Cos Tan"; + d.prototype.onExecute = function() { + var a = this.getInputData(0); + null == a && (a = 0); + var b = this.properties.amplitude, d = this.findInputSlot("amplitude"); + -1 != d && (b = this.getInputData(d)); + var c = this.properties.offset; + d = this.findInputSlot("offset"); + -1 != d && (c = this.getInputData(d)); + d = 0; + for (var k = this.outputs.length; d < k; ++d) { + switch(this.outputs[d].name) { + case "sin": + var f = Math.sin(a); + break; + case "cos": + f = Math.cos(a); + break; + case "tan": + f = Math.tan(a); + break; + case "asin": + f = Math.asin(a); + break; + case "acos": + f = Math.acos(a); + break; + case "atan": + f = Math.atan(a); + } + this.setOutputData(d, b * f + c); + } + }; + d.prototype.onGetInputs = function() { + return [["v", "number"], ["amplitude", "number"], ["offset", "number"]]; + }; + d.prototype.onGetOutputs = function() { + return [["sin", "number"], ["cos", "number"], ["tan", "number"], ["asin", "number"], ["acos", "number"], ["atan", "number"]]; + }; + A.registerNodeType("math/trigonometry", d); + A.registerSearchboxExtra("math/trigonometry", "SIN()", {outputs:[["sin", "number"]], title:"SIN()"}); + A.registerSearchboxExtra("math/trigonometry", "COS()", {outputs:[["cos", "number"]], title:"COS()"}); + A.registerSearchboxExtra("math/trigonometry", "TAN()", {outputs:[["tan", "number"]], title:"TAN()"}); + h.title = "Formula"; + h.desc = "Compute formula"; + h.size = [160, 100]; + G.prototype.onPropertyChanged = function(a, b) { + "formula" == a && (this.code_widget.value = b); + }; + h.prototype.onExecute = function() { + if (A.allow_scripts) { + var a = this.getInputData(0), b = this.getInputData(1); + null != a ? this.properties.x = a : a = this.properties.x; + null != b ? this.properties.y = b : b = this.properties.y; + try { + this._func && this._func_code == this.properties.formula || (this._func = new Function("x", "y", "TIME", "return " + this.properties.formula), this._func_code = this.properties.formula); + var d = this._func(a, b, this.graph.globaltime); + this.boxcolor = null; + } catch (M) { + this.boxcolor = "red"; + } + this.setOutputData(0, d); + } + }; + h.prototype.getTitle = function() { + return this._func_code || "Formula"; + }; + h.prototype.onDrawBackground = function() { + var a = this.properties.formula; + this.outputs && this.outputs.length && (this.outputs[0].label = a); + }; + A.registerNodeType("math/formula", h); + f.title = "Vec2->XY"; + f.desc = "vector 2 to components"; + f.prototype.onExecute = function() { + var a = this.getInputData(0); + null != a && (this.setOutputData(0, a[0]), this.setOutputData(1, a[1])); + }; + A.registerNodeType("math3d/vec2-to-xy", f); + x.title = "XY->Vec2"; + x.desc = "components to vector2"; + x.prototype.onExecute = function() { + var a = this.getInputData(0); + null == a && (a = this.properties.x); + var b = this.getInputData(1); + null == b && (b = this.properties.y); + var d = this._data; + d[0] = a; + d[1] = b; + this.setOutputData(0, d); + }; + A.registerNodeType("math3d/xy-to-vec2", x); + H.title = "Vec3->XYZ"; + H.desc = "vector 3 to components"; + H.prototype.onExecute = function() { + var a = this.getInputData(0); + null != a && (this.setOutputData(0, a[0]), this.setOutputData(1, a[1]), this.setOutputData(2, a[2])); + }; + A.registerNodeType("math3d/vec3-to-xyz", H); + I.title = "XYZ->Vec3"; + I.desc = "components to vector3"; + I.prototype.onExecute = function() { + var a = this.getInputData(0); + null == a && (a = this.properties.x); + var b = this.getInputData(1); + null == b && (b = this.properties.y); + var d = this.getInputData(2); + null == d && (d = this.properties.z); + var c = this._data; + c[0] = a; + c[1] = b; + c[2] = d; + this.setOutputData(0, c); + }; + A.registerNodeType("math3d/xyz-to-vec3", I); + J.title = "Vec4->XYZW"; + J.desc = "vector 4 to components"; + J.prototype.onExecute = function() { + var a = this.getInputData(0); + null != a && (this.setOutputData(0, a[0]), this.setOutputData(1, a[1]), this.setOutputData(2, a[2]), this.setOutputData(3, a[3])); + }; + A.registerNodeType("math3d/vec4-to-xyzw", J); + L.title = "XYZW->Vec4"; + L.desc = "components to vector4"; + L.prototype.onExecute = function() { + var a = this.getInputData(0); + null == a && (a = this.properties.x); + var b = this.getInputData(1); + null == b && (b = this.properties.y); + var d = this.getInputData(2); + null == d && (d = this.properties.z); + var c = this.getInputData(3); + null == c && (c = this.properties.w); + var k = this._data; + k[0] = a; + k[1] = b; + k[2] = d; + k[3] = c; + this.setOutputData(0, k); + }; + A.registerNodeType("math3d/xyzw-to-vec4", L); +})(this); +(function(w) { + function c() { + this.addInput("sel", "number"); + this.addInput("A"); + this.addInput("B"); + this.addInput("C"); + this.addInput("D"); + this.addOutput("out"); + this.selected = 0; + } + function p() { + this.properties = {sequence:"A,B,C"}; + this.addInput("index", "number"); + this.addInput("seq"); + this.addOutput("out"); + this.index = 0; + this.values = this.properties.sequence.split(","); + } + var m = w.LiteGraph; + c.title = "Selector"; + c.desc = "selects an output"; + c.prototype.onDrawBackground = function(c) { + if (!this.flags.collapsed) { + c.fillStyle = "#AFB"; + var g = (this.selected + 1) * m.NODE_SLOT_HEIGHT + 6; + c.beginPath(); + c.moveTo(50, g); + c.lineTo(50, g + m.NODE_SLOT_HEIGHT); + c.lineTo(34, g + 0.5 * m.NODE_SLOT_HEIGHT); + c.fill(); + } + }; + c.prototype.onExecute = function() { + var c = this.getInputData(0); + if (null == c || c.constructor !== Number) { + c = 0; + } + this.selected = c = Math.round(c) % (this.inputs.length - 1); + c = this.getInputData(c + 1); + void 0 !== c && this.setOutputData(0, c); + }; + c.prototype.onGetInputs = function() { + return [["E", 0], ["F", 0], ["G", 0], ["H", 0]]; + }; + m.registerNodeType("logic/selector", c); + p.title = "Sequence"; + p.desc = "select one element from a sequence from a string"; + p.prototype.onPropertyChanged = function(c, m) { + "sequence" == c && (this.values = m.split(",")); + }; + p.prototype.onExecute = function() { + var c = this.getInputData(1); + c && c != this.current_sequence && (this.values = c.split(","), this.current_sequence = c); + c = this.getInputData(0); + null == c && (c = 0); + this.index = c = Math.round(c) % this.values.length; + this.setOutputData(0, this.values[c]); + }; + m.registerNodeType("logic/sequence", p); +})(this); +(function(w) { + function c() { + this.addOutput("tex", "Texture"); + this.addOutput("name", "string"); + this.properties = {name:"", filter:!0}; + this.size = [c.image_preview_size, c.image_preview_size]; + } + function p() { + this.addInput("Texture", "Texture"); + this.properties = {flipY:!1}; + this.size = [c.image_preview_size, c.image_preview_size]; + } + function m() { + this.addInput("Texture", "Texture"); + this.addOutput("tex", "Texture"); + this.addOutput("name", "string"); + this.properties = {name:"", generate_mipmaps:!1}; + } + function g() { + this.addInput("Texture", "Texture"); + this.addInput("TextureB", "Texture"); + this.addInput("value", "number"); + this.addOutput("Texture", "Texture"); + this.help = "

pixelcode must be vec3, uvcode must be vec2, is optional

\r\n\t\t

uv: tex. coords

color: texture colorB: textureB

time: scene time value: input value

For multiline you must type: result = ...

"; + this.properties = {value:1, pixelcode:"color + colorB * value", uvcode:"", precision:c.DEFAULT}; + this.has_error = !1; + } + function u() { + this.addOutput("out", "Texture"); + this.properties = {code:"", u_value:1, u_color:[1, 1, 1, 1], width:512, height:512, precision:c.DEFAULT}; + this.properties.code = u.pixel_shader; + this._uniforms = {u_value:1, u_color:vec4.create(), in_texture:0, texSize:vec2.create(), time:0}; + } + function l() { + this.addInput("in", "Texture"); + this.addInput("scale", "vec2"); + this.addInput("offset", "vec2"); + this.addOutput("out", "Texture"); + this.properties = {offset:vec2.fromValues(0, 0), scale:vec2.fromValues(1, 1), precision:c.DEFAULT}; + } + function B() { + this.addInput("in", "Texture"); + this.addInput("warp", "Texture"); + this.addInput("factor", "number"); + this.addOutput("out", "Texture"); + this.properties = {factor:0.01, scale:[1, 1], offset:[0, 0], precision:c.DEFAULT}; + this._uniforms = {u_texture:0, u_textureB:1, u_factor:1, u_scale:vec2.create(), u_offset:vec2.create()}; + } + function y() { + this.addInput("Texture", "Texture"); + this.properties = {additive:!1, antialiasing:!1, filter:!0, disable_alpha:!1, gamma:1.0, viewport:[0, 0, 1, 1]}; + this.size[0] = 130; + } + function v() { + this.addInput("Texture", "Texture"); + this.addOutput("", "Texture"); + this.properties = {size:0, generate_mipmaps:!1, precision:c.DEFAULT}; + } + function E() { + this.addInput("Texture", "Texture"); + this.addOutput("", "Texture"); + this.properties = {iterations:1, generate_mipmaps:!1, precision:c.DEFAULT}; + } + function z() { + this.addInput("Texture", "Texture"); + this.addOutput("tex", "Texture"); + this.addOutput("avg", "vec4"); + this.addOutput("lum", "number"); + this.properties = {use_previous_frame:!0, high_quality:!1}; + this._uniforms = {u_texture:0, u_mipmap_offset:0}; + this._luminance = new Float32Array(4); + } + function e() { + this.addInput("in", "Texture"); + this.addInput("factor", "Number"); + this.addOutput("out", "Texture"); + this.properties = {factor:0.5}; + this._uniforms = {u_texture:0, u_textureB:1, u_factor:this.properties.factor}; + } + function C() { + this.addInput("in", "Texture"); + this.addOutput("avg", "Texture"); + this.addOutput("array", "Texture"); + this.properties = {samples:64, frames_interval:1}; + this._uniforms = {u_texture:0, u_textureB:1, u_samples:this.properties.samples, u_isamples:1 / this.properties.samples}; + this.frame = 0; + } + function D() { + this.addInput("Image", "image"); + this.addOutput("", "Texture"); + this.properties = {}; + } + function t() { + this.addInput("Texture", "Texture"); + this.addInput("LUT", "Texture"); + this.addInput("Intensity", "number"); + this.addOutput("", "Texture"); + this.properties = {enabled:!0, intensity:1, precision:c.DEFAULT, texture:null}; + t._shader || (t._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, t.pixel_shader)); + } + function G() { + this.addInput("Texture", "Texture"); + this.addOutput("R", "Texture"); + this.addOutput("G", "Texture"); + this.addOutput("B", "Texture"); + this.addOutput("A", "Texture"); + G._shader || (G._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, G.pixel_shader)); + } + function n() { + this.addInput("R", "Texture"); + this.addInput("G", "Texture"); + this.addInput("B", "Texture"); + this.addInput("A", "Texture"); + this.addOutput("Texture", "Texture"); + this.properties = {precision:c.DEFAULT, R:1, G:1, B:1, A:1}; + this._color = vec4.create(); + this._uniforms = {u_textureR:0, u_textureG:1, u_textureB:2, u_textureA:3, u_color:this._color}; + } + function q() { + this.addOutput("Texture", "Texture"); + this._tex_color = vec4.create(); + this.properties = {color:vec4.create(), precision:c.DEFAULT}; + } + function k() { + this.addInput("A", "color"); + this.addInput("B", "color"); + this.addOutput("Texture", "Texture"); + this.properties = {angle:0, scale:1, A:[0, 0, 0], B:[1, 1, 1], texture_size:32}; + k._shader || (k._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, k.pixel_shader)); + this._uniforms = {u_angle:0, u_colorA:vec3.create(), u_colorB:vec3.create()}; + } + function a() { + this.addInput("A", "Texture"); + this.addInput("B", "Texture"); + this.addInput("Mixer", "Texture"); + this.addOutput("Texture", "Texture"); + this.properties = {factor:0.5, size_from_biggest:!0, invert:!1, precision:c.DEFAULT}; + this._uniforms = {u_textureA:0, u_textureB:1, u_textureMix:2, u_mix:vec4.create()}; + } + function b() { + this.addInput("Tex.", "Texture"); + this.addOutput("Edges", "Texture"); + this.properties = {invert:!0, threshold:!1, factor:1, precision:c.DEFAULT}; + b._shader || (b._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, b.pixel_shader)); + } + function d() { + this.addInput("Texture", "Texture"); + this.addInput("Distance", "number"); + this.addInput("Range", "number"); + this.addOutput("Texture", "Texture"); + this.properties = {distance:100, range:50, only_depth:!1, high_precision:!1}; + this._uniforms = {u_texture:0, u_distance:100, u_range:50, u_camera_planes:null}; + } + function h() { + this.addInput("Texture", "Texture"); + this.addOutput("Texture", "Texture"); + this.properties = {precision:c.DEFAULT, invert:!1}; + this._uniforms = {u_texture:0, u_near:0.1, u_far:10000}; + } + function f() { + this.addInput("Texture", "Texture"); + this.addInput("Iterations", "number"); + this.addInput("Intensity", "number"); + this.addOutput("Blurred", "Texture"); + this.properties = {intensity:1, iterations:1, preserve_aspect:!1, scale:[1, 1], precision:c.DEFAULT}; + } + function x() { + this.addInput("in", "Texture"); + this.addInput("dirt", "Texture"); + this.addOutput("out", "Texture"); + this.addOutput("glow", "Texture"); + this.properties = {enabled:!0, intensity:1, persistence:0.99, iterations:16, threshold:0, scale:1, dirt_factor:0.5, precision:c.DEFAULT}; + this._textures = []; + this._uniforms = {u_intensity:1, u_texture:0, u_glow_texture:1, u_threshold:0, u_texel_size:vec2.create()}; + } + function H() { + this.addInput("Texture", "Texture"); + this.addOutput("Filtered", "Texture"); + this.properties = {intensity:1, radius:5}; + } + function I() { + this.addInput("Texture", "Texture"); + this.addOutput("Filtered", "Texture"); + this.properties = {sigma:1.4, k:1.6, p:21.7, epsilon:79, phi:0.017}; + } + function J() { + this.addOutput("Webcam", "Texture"); + this.properties = {texture_name:"", facingMode:"user"}; + this.boxcolor = "black"; + this.version = 0; + } + function L() { + this.addInput("in", "Texture"); + this.addInput("f", "number"); + this.addOutput("out", "Texture"); + this.properties = {enabled:!0, factor:1, precision:c.LOW}; + this._uniforms = {u_texture:0, u_factor:1}; + } + function A() { + this.addInput("in", "Texture"); + this.addOutput("out", "Texture"); + this.properties = {precision:c.LOW, split_channels:!1}; + this._values = new Uint8Array(1024); + this._values.fill(255); + this._curve_texture = null; + this._uniforms = {u_texture:0, u_curve:1, u_range:1.0}; + this._must_update = !0; + this._points = {RGB:[[0, 0], [1, 1]], R:[[0, 0], [1, 1]], G:[[0, 0], [1, 1]], B:[[0, 0], [1, 1]]}; + this.curve_editor = null; + this.addWidget("toggle", "Split Channels", !1, "split_channels"); + this.addWidget("combo", "Channel", "RGB", {values:["RGB", "R", "G", "B"]}); + this.curve_offset = 68; + this.size = [240, 160]; + } + function r() { + this.addInput("in", "Texture"); + this.addInput("exp", "number"); + this.addOutput("out", "Texture"); + this.properties = {exposition:1, precision:c.LOW}; + this._uniforms = {u_texture:0, u_exposition:1}; + } + function K() { + this.addInput("in", "Texture"); + this.addInput("avg", "number,Texture"); + this.addOutput("out", "Texture"); + this.properties = {enabled:!0, scale:1, gamma:1, average_lum:1, lum_white:1, precision:c.LOW}; + this._uniforms = {u_texture:0, u_lumwhite2:1, u_igamma:1, u_scale:1, u_average_lum:1}; + } + function N() { + this.addOutput("out", "Texture"); + this.properties = {width:512, height:512, seed:0, persistence:0.1, octaves:8, scale:1, offset:[0, 0], amplitude:1, precision:c.DEFAULT}; + this._key = 0; + this._texture = null; + this._uniforms = {u_persistence:0.1, u_seed:0, u_offset:vec2.create(), u_scale:1, u_viewport:vec2.create()}; + } + function M() { + this.addInput("v"); + this.addOutput("out", "Texture"); + this.properties = {code:M.default_code, width:512, height:512, clear:!0, precision:c.DEFAULT, use_html_canvas:!1}; + this._temp_texture = this._func = null; + this.compileCode(); + } + function O() { + this.addInput("in", "Texture"); + this.addOutput("out", "Texture"); + this.properties = {key_color:vec3.fromValues(0, 1, 0), threshold:0.8, slope:0.2, precision:c.DEFAULT}; + } + function P() { + this.addInput("in", "texture"); + this.addInput("yaw", "number"); + this.addOutput("out", "texture"); + this.properties = {yaw:0}; + } + var F = w.LiteGraph; + w.LGraphTexture = null; + "undefined" != typeof GL && (LGraphCanvas.link_type_colors.Texture = "#987", w.LGraphTexture = c, c.title = "Texture", c.desc = "Texture", c.widgets_info = {name:{widget:"texture"}, filter:{widget:"checkbox"}}, c.loadTextureCallback = null, c.image_preview_size = 256, c.PASS_THROUGH = 1, c.COPY = 2, c.LOW = 3, c.HIGH = 4, c.REUSE = 5, c.DEFAULT = 2, c.MODE_VALUES = {"pass through":c.PASS_THROUGH, copy:c.COPY, low:c.LOW, high:c.HIGH, reuse:c.REUSE, default:c.DEFAULT}, c.getTexturesContainer = function() { + return gl.textures; + }, c.loadTexture = function(a, b) { + b = b || {}; + var d = a; + "http://" == d.substr(0, 7) && F.proxy && (d = F.proxy + d.substr(7)); + return c.getTexturesContainer()[a] = GL.Texture.fromURL(d, b); + }, c.getTexture = function(a) { + var b = this.getTexturesContainer(); + if (!b) { + throw "Cannot load texture, container of textures not found"; + } + b = b[a]; + return !b && a && ":" != a[0] ? this.loadTexture(a) : b; + }, c.getTargetTexture = function(a, b, d) { + if (!a) { + throw "LGraphTexture.getTargetTexture expects a reference texture"; + } + switch(d) { + case c.LOW: + d = gl.UNSIGNED_BYTE; + break; + case c.HIGH: + d = gl.HIGH_PRECISION_FORMAT; + break; + case c.REUSE: + return a; + default: + d = a ? a.type : gl.UNSIGNED_BYTE; + } + b && b.width == a.width && b.height == a.height && b.type == d || (b = new GL.Texture(a.width, a.height, {type:d, format:gl.RGBA, filter:gl.LINEAR})); + return b; + }, c.getTextureType = function(a, b) { + b = b ? b.type : gl.UNSIGNED_BYTE; + switch(a) { + case c.HIGH: + b = gl.HIGH_PRECISION_FORMAT; + break; + case c.LOW: + b = gl.UNSIGNED_BYTE; + } + return b; + }, c.getWhiteTexture = function() { + return this._white_texture ? this._white_texture : this._white_texture = GL.Texture.fromMemory(1, 1, [255, 255, 255, 255], {format:gl.RGBA, wrap:gl.REPEAT, filter:gl.NEAREST}); + }, c.getNoiseTexture = function() { + if (this._noise_texture) { + return this._noise_texture; + } + for (var a = new Uint8Array(1048576), b = 0; 1048576 > b; ++b) { + a[b] = 255 * Math.random(); + } + return this._noise_texture = a = GL.Texture.fromMemory(512, 512, a, {format:gl.RGBA, wrap:gl.REPEAT, filter:gl.NEAREST}); + }, c.prototype.onDropFile = function(a, b, d) { + a ? ("string" == typeof a ? a = GL.Texture.fromURL(a) : -1 != b.toLowerCase().indexOf(".dds") ? a = GL.Texture.fromDDSInMemory(a) : (a = new Blob([d]), a = URL.createObjectURL(a), a = GL.Texture.fromURL(a)), this._drop_texture = a, this.properties.name = b) : (this._drop_texture = null, this.properties.name = ""); + }, c.prototype.getExtraMenuOptions = function(a) { + var b = this; + if (this._drop_texture) { + return [{content:"Clear", callback:function() { + b._drop_texture = null; + b.properties.name = ""; + }}]; + } + }, c.prototype.onExecute = function() { + var a = null; + this.isOutputConnected(1) && (a = this.getInputData(0)); + !a && this._drop_texture && (a = this._drop_texture); + !a && this.properties.name && (a = c.getTexture(this.properties.name)); + if (a) { + this._last_tex = a; + !1 === this.properties.filter ? a.setParameter(gl.TEXTURE_MAG_FILTER, gl.NEAREST) : a.setParameter(gl.TEXTURE_MAG_FILTER, gl.LINEAR); + this.setOutputData(0, a); + this.setOutputData(1, a.fullpath || a.filename); + for (var b = 2; b < this.outputs.length; b++) { + var d = this.outputs[b]; + if (d) { + var k = null; + "width" == d.name ? k = a.width : "height" == d.name ? k = a.height : "aspect" == d.name && (k = a.width / a.height); + this.setOutputData(b, k); + } + } + } else { + this.setOutputData(0, null), this.setOutputData(1, ""); + } + }, c.prototype.onResourceRenamed = function(a, b) { + this.properties.name == a && (this.properties.name = b); + }, c.prototype.onDrawBackground = function(a) { + if (!(this.flags.collapsed || 20 >= this.size[1])) { + if (this._drop_texture && a.webgl) { + a.drawImage(this._drop_texture, 0, 0, this.size[0], this.size[1]); + } else { + if (this._last_preview_tex != this._last_tex) { + if (a.webgl) { + this._canvas = this._last_tex; + } else { + var b = c.generateLowResTexturePreview(this._last_tex); + if (!b) { + return; + } + this._last_preview_tex = this._last_tex; + this._canvas = cloneCanvas(b); + } + } + this._canvas && (a.save(), a.webgl || (a.translate(0, this.size[1]), a.scale(1, -1)), a.drawImage(this._canvas, 0, 0, this.size[0], this.size[1]), a.restore()); + } + } + }, c.generateLowResTexturePreview = function(a) { + if (!a) { + return null; + } + var b = c.image_preview_size, d = a; + if (a.format == gl.DEPTH_COMPONENT) { + return null; + } + if (a.width > b || a.height > b) { + d = this._preview_temp_tex, this._preview_temp_tex || (this._preview_temp_tex = d = new GL.Texture(b, b, {minFilter:gl.NEAREST})), a.copyTo(d); + } + a = this._preview_canvas; + a || (this._preview_canvas = a = createCanvas(b, b)); + d && d.toCanvas(a); + return a; + }, c.prototype.getResources = function(a) { + this.properties.name && (a[this.properties.name] = GL.Texture); + return a; + }, c.prototype.onGetInputs = function() { + return [["in", "Texture"]]; + }, c.prototype.onGetOutputs = function() { + return [["width", "number"], ["height", "number"], ["aspect", "number"]]; + }, c.replaceCode = function(a, b) { + return a.replace(/\{\{[a-zA-Z0-9_]*\}\}/g, function(a) { + a = a.replace(/[\{\}]/g, ""); + return b[a] || ""; + }); + }, F.registerNodeType("texture/texture", c), p.title = "Preview", p.desc = "Show a texture in the graph canvas", p.allow_preview = !1, p.prototype.onDrawBackground = function(a) { + if (!this.flags.collapsed && (a.webgl || p.allow_preview)) { + var b = this.getInputData(0); + b && (b = !b.handle && a.webgl ? b : c.generateLowResTexturePreview(b), a.save(), this.properties.flipY && (a.translate(0, this.size[1]), a.scale(1, -1)), a.drawImage(b, 0, 0, this.size[0], this.size[1]), a.restore()); + } + }, F.registerNodeType("texture/preview", p), m.title = "Save", m.desc = "Save a texture in the repository", m.prototype.getPreviewTexture = function() { + return this._texture; + }, m.prototype.onExecute = function() { + var a = this.getInputData(0); + a && (this.properties.generate_mipmaps && (a.bind(0), a.setParameter(gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR), gl.generateMipmap(a.texture_type), a.unbind(0)), this.properties.name && (c.storeTexture ? c.storeTexture(this.properties.name, a) : c.getTexturesContainer()[this.properties.name] = a), this._texture = a, this.setOutputData(0, a), this.setOutputData(1, this.properties.name)); + }, F.registerNodeType("texture/save", m), g.widgets_info = {uvcode:{widget:"code"}, pixelcode:{widget:"code"}, precision:{widget:"combo", values:c.MODE_VALUES}}, g.title = "Operation", g.desc = "Texture shader operation", g.presets = {}, g.prototype.getExtraMenuOptions = function(a) { + var b = this; + return [{content:b.properties.show ? "Hide Texture" : "Show Texture", callback:function() { + b.properties.show = !b.properties.show; + }}]; + }, g.prototype.onPropertyChanged = function() { + this.has_error = !1; + }, g.prototype.onDrawBackground = function(a) { + this.flags.collapsed || 20 >= this.size[1] || !this.properties.show || !this._tex || this._tex.gl != a || (a.save(), a.drawImage(this._tex, 0, 0, this.size[0], this.size[1]), a.restore()); + }, g.prototype.onExecute = function() { + var a = this.getInputData(0); + if (this.isOutputConnected(0)) { + if (this.properties.precision === c.PASS_THROUGH) { + this.setOutputData(0, a); + } else { + var b = this.getInputData(1); + if (this.properties.uvcode || this.properties.pixelcode) { + var d = 512, k = 512; + a ? (d = a.width, k = a.height) : b && (d = b.width, k = b.height); + b || (b = GL.Texture.getWhiteTexture()); + var f = c.getTextureType(this.properties.precision, a); + this._tex = a || this._tex ? c.getTargetTexture(a || this._tex, this._tex, this.properties.precision) : new GL.Texture(d, k, {type:f, format:gl.RGBA, filter:gl.LINEAR}); + f = ""; + this.properties.uvcode && (f = "uv = " + this.properties.uvcode, -1 != this.properties.uvcode.indexOf(";") && (f = this.properties.uvcode)); + var h = ""; + this.properties.pixelcode && (h = "result = " + this.properties.pixelcode, -1 != this.properties.pixelcode.indexOf(";") && (h = this.properties.pixelcode)); + var e = this._shader; + if (!(this.has_error || e && this._shader_code == f + "|" + h)) { + var n = c.replaceCode(g.pixel_shader, {UV_CODE:f, PIXEL_CODE:h}); + try { + e = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, n), this.boxcolor = "#00FF00"; + } catch (T) { + GL.Shader.dumpErrorToConsole(T, Shader.SCREEN_VERTEX_SHADER, n); + this.boxcolor = "#FF0000"; + this.has_error = !0; + return; + } + this._shader = e; + this._shader_code = f + "|" + h; + } + if (this._shader) { + var q = this.getInputData(2); + null != q ? this.properties.value = q : q = parseFloat(this.properties.value); + var l = this.graph.getTime(); + this._tex.drawTo(function() { + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + gl.disable(gl.BLEND); + a && a.bind(0); + b && b.bind(1); + var c = Mesh.getScreenQuad(); + e.uniforms({u_texture:0, u_textureB:1, value:q, texSize:[d, k], time:l}).draw(c); + }); + this.setOutputData(0, this._tex); + } + } + } + } + }, g.pixel_shader = "precision highp float;\n\r\n\t\t\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform vec2 texSize;\n\r\n\t\tuniform float time;\n\r\n\t\tuniform float value;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec2 uv = v_coord;\n\r\n\t\t\t{{UV_CODE}};\n\r\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\r\n\t\t\tvec3 color = color4.rgb;\n\r\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\r\n\t\t\tvec3 colorB = color4B.rgb;\n\r\n\t\t\tvec3 result = color;\n\r\n\t\t\tfloat alpha = 1.0;\n\r\n\t\t\t{{PIXEL_CODE}};\n\r\n\t\t\tgl_FragColor = vec4(result, alpha);\n\r\n\t\t}\n\r\n\t\t", + g.registerPreset = function(a, b) { + g.presets[a] = b; + }, g.registerPreset("", ""), g.registerPreset("bypass", "color"), g.registerPreset("add", "color + colorB * value"), g.registerPreset("substract", "(color - colorB) * value"), g.registerPreset("mate", "mix( color, colorB, color4B.a * value)"), g.registerPreset("invert", "vec3(1.0) - color"), g.registerPreset("multiply", "color * colorB * value"), g.registerPreset("divide", "(color / colorB) / value"), g.registerPreset("difference", "abs(color - colorB) * value"), g.registerPreset("max", "max(color, colorB) * value"), + g.registerPreset("min", "min(color, colorB) * value"), g.registerPreset("displace", "texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"), g.registerPreset("grayscale", "vec3(color.x + color.y + color.z) * value / 3.0"), g.registerPreset("saturation", "mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"), g.registerPreset("threshold", "vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"), g.prototype.onInspect = + function(a) { + var b = this; + a.addCombo("Presets", "", {values:Object.keys(g.presets), callback:function(d) { + var c = g.presets[d]; + c && (b.setProperty("pixelcode", c), b.title = d, a.refresh()); + }}); + }, F.registerNodeType("texture/operation", g), u.title = "Shader", u.desc = "Texture shader", u.widgets_info = {code:{type:"code", lang:"glsl"}, precision:{widget:"combo", values:c.MODE_VALUES}}, u.prototype.onPropertyChanged = function(a, b) { + if ("code" == a && (a = this.getShader())) { + b = a.uniformInfo; + if (this.inputs) { + for (var d = {}, c = 0; c < this.inputs.length; ++c) { + var k = this.getInputInfo(c); + k && (b[k.name] && !d[k.name] ? d[k.name] = !0 : (this.removeInput(c), c--)); + } + } + for (c in b) { + if (k = a.uniformInfo[c], null !== k.loc && "time" != c) { + if (this._shader.samplers[c]) { + b = "texture"; + } else { + switch(k.size) { + case 1: + b = "number"; + break; + case 2: + b = "vec2"; + break; + case 3: + b = "vec3"; + break; + case 4: + b = "vec4"; + break; + case 9: + b = "mat3"; + break; + case 16: + b = "mat4"; + break; + default: + continue; + } + } + d = this.findInputSlot(c); + if (-1 != d && (k = this.getInputInfo(d))) { + if (k.type == b) { + continue; + } + this.removeInput(d, b); + } + this.addInput(c, b); + } + } + } + }, u.prototype.getShader = function() { + if (this._shader && this._shader_code == this.properties.code) { + return this._shader; + } + this._shader_code = this.properties.code; + this._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, this.properties.code), this.boxcolor = "green"; + return this._shader; + }, u.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var a = this.getShader(); + if (a) { + var b = 0, d = null; + if (this.inputs) { + for (var k = 0; k < this.inputs.length; ++k) { + var f = this.getInputInfo(k), h = this.getInputData(k); + null != h && (h.constructor === GL.Texture && (h.bind(b), d || (d = h), h = b, b++), a.setUniform(f.name, h)); + } + } + var e = this._uniforms; + b = c.getTextureType(this.properties.precision, d); + k = this.properties.width | 0; + f = this.properties.height | 0; + 0 == k && (k = d ? d.width : gl.canvas.width); + 0 == f && (f = d ? d.height : gl.canvas.height); + e.texSize[0] = k; + e.texSize[1] = f; + e.time = this.graph.getTime(); + e.u_value = this.properties.u_value; + e.u_color.set(this.properties.u_color); + this._tex && this._tex.type == b && this._tex.width == k && this._tex.height == f || (this._tex = new GL.Texture(k, f, {type:b, format:gl.RGBA, filter:gl.LINEAR})); + this._tex.drawTo(function() { + a.uniforms(e).draw(GL.Mesh.getScreenQuad()); + }); + this.setOutputData(0, this._tex); + } + } + }, u.pixel_shader = "precision highp float;\n\r\n\n\r\nvarying vec2 v_coord;\n\r\nuniform float time; //time in seconds\n\r\nuniform vec2 texSize; //tex resolution\n\r\nuniform float u_value;\n\r\nuniform vec4 u_color;\n\n\r\nvoid main() {\n\r\n\tvec2 uv = v_coord;\n\r\n\tvec3 color = vec3(0.0);\n\r\n\t//your code here\n\r\n\tcolor.xy=uv;\n\n\r\n\tgl_FragColor = vec4(color, 1.0);\n\r\n}\n\r\n", F.registerNodeType("texture/shader", u), l.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, + l.title = "Scale/Offset", l.desc = "Applies an scaling and offseting", l.prototype.onExecute = function() { + var a = this.getInputData(0); + if (this.isOutputConnected(0) && a) { + if (this.properties.precision === c.PASS_THROUGH) { + this.setOutputData(0, a); + } else { + var b = a.width, d = a.height, k = this.precision === c.LOW ? gl.UNSIGNED_BYTE : gl.HIGH_PRECISION_FORMAT; + this.precision === c.DEFAULT && (k = a.type); + this._tex && this._tex.width == b && this._tex.height == d && this._tex.type == k || (this._tex = new GL.Texture(b, d, {type:k, format:gl.RGBA, filter:gl.LINEAR})); + var f = this._shader; + f || (f = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, l.pixel_shader)); + var h = this.getInputData(1); + h ? (this.properties.scale[0] = h[0], this.properties.scale[1] = h[1]) : h = this.properties.scale; + var e = this.getInputData(2); + e ? (this.properties.offset[0] = e[0], this.properties.offset[1] = e[1]) : e = this.properties.offset; + this._tex.drawTo(function() { + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + gl.disable(gl.BLEND); + a.bind(0); + var b = Mesh.getScreenQuad(); + f.uniforms({u_texture:0, u_scale:h, u_offset:e}).draw(b); + }); + this.setOutputData(0, this._tex); + } + } + }, l.pixel_shader = "precision highp float;\n\r\n\t\t\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform vec2 u_scale;\n\r\n\t\tuniform vec2 u_offset;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec2 uv = v_coord;\n\r\n\t\t\tuv = uv / u_scale - u_offset;\n\r\n\t\t\tgl_FragColor = texture2D(u_texture, uv);\n\r\n\t\t}\n\r\n\t\t", F.registerNodeType("texture/scaleOffset", l), B.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, + B.title = "Warp", B.desc = "Texture warp operation", B.prototype.onExecute = function() { + var a = this.getInputData(0); + if (this.isOutputConnected(0)) { + if (this.properties.precision === c.PASS_THROUGH) { + this.setOutputData(0, a); + } else { + var b = this.getInputData(1), d = 512, k = 512; + a ? (d = a.width, k = a.height) : b && (d = b.width, k = b.height); + this._tex = a || this._tex ? c.getTargetTexture(a || this._tex, this._tex, this.properties.precision) : new GL.Texture(d, k, {type:this.precision === c.LOW ? gl.UNSIGNED_BYTE : gl.HIGH_PRECISION_FORMAT, format:gl.RGBA, filter:gl.LINEAR}); + var f = this._shader; + f || (f = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, B.pixel_shader)); + d = this.getInputData(2); + null != d ? this.properties.factor = d : d = parseFloat(this.properties.factor); + var h = this._uniforms; + h.u_factor = d; + h.u_scale.set(this.properties.scale); + h.u_offset.set(this.properties.offset); + this._tex.drawTo(function() { + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + gl.disable(gl.BLEND); + a && a.bind(0); + b && b.bind(1); + var d = Mesh.getScreenQuad(); + f.uniforms(h).draw(d); + }); + this.setOutputData(0, this._tex); + } + } + }, B.pixel_shader = "precision highp float;\n\r\n\t\t\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform float u_factor;\n\r\n\t\tuniform vec2 u_scale;\n\r\n\t\tuniform vec2 u_offset;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec2 uv = v_coord;\n\r\n\t\t\tuv += ( texture2D(u_textureB, uv).rg - vec2(0.5)) * u_factor * u_scale + u_offset;\n\r\n\t\t\tgl_FragColor = texture2D(u_texture, uv);\n\r\n\t\t}\n\r\n\t\t", F.registerNodeType("texture/warp", + B), y.title = "to Viewport", y.desc = "Texture to viewport", y._prev_viewport = new Float32Array(4), y.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a) { + this.properties.disable_alpha ? gl.disable(gl.BLEND) : (gl.enable(gl.BLEND), this.properties.additive ? gl.blendFunc(gl.SRC_ALPHA, gl.ONE) : gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)); + gl.disable(gl.DEPTH_TEST); + var b = this.properties.gamma || 1.0; + this.isInputConnected(1) && (b = this.getInputData(1)); + a.setParameter(gl.TEXTURE_MAG_FILTER, this.properties.filter ? gl.LINEAR : gl.NEAREST); + var d = y._prev_viewport; + d.set(gl.viewport_data); + var c = this.properties.viewport; + gl.viewport(d[0] + d[2] * c[0], d[1] + d[3] * c[1], d[2] * c[2], d[3] * c[3]); + gl.getViewport(); + this.properties.antialiasing ? (y._shader || (y._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, y.aa_pixel_shader)), c = Mesh.getScreenQuad(), a.bind(0), y._shader.uniforms({u_texture:0, uViewportSize:[a.width, a.height], u_igamma:1 / b, inverseVP:[1 / a.width, 1 / a.height]}).draw(c)) : 1.0 != b ? (y._gamma_shader || (y._gamma_shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, y.gamma_pixel_shader)), a.toViewport(y._gamma_shader, {u_texture:0, u_igamma:1 / b})) : a.toViewport(); + gl.viewport(d[0], d[1], d[2], d[3]); + } + }, y.prototype.onGetInputs = function() { + return [["gamma", "number"]]; + }, y.aa_pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec2 uViewportSize;\n\r\n\t\tuniform vec2 inverseVP;\n\r\n\t\tuniform float u_igamma;\n\r\n\t\t#define FXAA_REDUCE_MIN (1.0/ 128.0)\n\r\n\t\t#define FXAA_REDUCE_MUL (1.0 / 8.0)\n\r\n\t\t#define FXAA_SPAN_MAX 8.0\n\r\n\t\t\n\r\n\t\t/* from mitsuhiko/webgl-meincraft based on the code on geeks3d.com */\n\r\n\t\tvec4 applyFXAA(sampler2D tex, vec2 fragCoord)\n\r\n\t\t{\n\r\n\t\t\tvec4 color = vec4(0.0);\n\r\n\t\t\t/*vec2 inverseVP = vec2(1.0 / uViewportSize.x, 1.0 / uViewportSize.y);*/\n\r\n\t\t\tvec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * inverseVP).xyz;\n\r\n\t\t\tvec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * inverseVP).xyz;\n\r\n\t\t\tvec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * inverseVP).xyz;\n\r\n\t\t\tvec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * inverseVP).xyz;\n\r\n\t\t\tvec3 rgbM = texture2D(tex, fragCoord * inverseVP).xyz;\n\r\n\t\t\tvec3 luma = vec3(0.299, 0.587, 0.114);\n\r\n\t\t\tfloat lumaNW = dot(rgbNW, luma);\n\r\n\t\t\tfloat lumaNE = dot(rgbNE, luma);\n\r\n\t\t\tfloat lumaSW = dot(rgbSW, luma);\n\r\n\t\t\tfloat lumaSE = dot(rgbSE, luma);\n\r\n\t\t\tfloat lumaM = dot(rgbM, luma);\n\r\n\t\t\tfloat lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n\r\n\t\t\tfloat lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\r\n\t\t\t\n\r\n\t\t\tvec2 dir;\n\r\n\t\t\tdir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n\r\n\t\t\tdir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\r\n\t\t\t\n\r\n\t\t\tfloat dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\r\n\t\t\t\n\r\n\t\t\tfloat rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n\r\n\t\t\tdir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * inverseVP;\n\r\n\t\t\t\n\r\n\t\t\tvec3 rgbA = 0.5 * (texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + \n\r\n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz);\n\r\n\t\t\tvec3 rgbB = rgbA * 0.5 + 0.25 * (texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + \n\r\n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz);\n\r\n\t\t\t\n\r\n\t\t\t//return vec4(rgbA,1.0);\n\r\n\t\t\tfloat lumaB = dot(rgbB, luma);\n\r\n\t\t\tif ((lumaB < lumaMin) || (lumaB > lumaMax))\n\r\n\t\t\t\tcolor = vec4(rgbA, 1.0);\n\r\n\t\t\telse\n\r\n\t\t\t\tcolor = vec4(rgbB, 1.0);\n\r\n\t\t\tif(u_igamma != 1.0)\n\r\n\t\t\t\tcolor.xyz = pow( color.xyz, vec3(u_igamma) );\n\r\n\t\t\treturn color;\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\r\n\t\t}\n\r\n\t\t", + y.gamma_pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_igamma;\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\r\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\r\n\t\t gl_FragColor = color;\n\r\n\t\t}\n\r\n\t\t", F.registerNodeType("texture/toviewport", y), v.title = "Copy", v.desc = "Copy Texture", v.widgets_info = {size:{widget:"combo", + values:[0, 32, 64, 128, 256, 512, 1024, 2048]}, precision:{widget:"combo", values:c.MODE_VALUES}}, v.prototype.onExecute = function() { + var a = this.getInputData(0); + if ((a || this._temp_texture) && this.isOutputConnected(0)) { + if (a) { + var b = a.width, d = a.height; + 0 != this.properties.size && (d = b = this.properties.size); + var k = this._temp_texture, f = a.type; + this.properties.precision === c.LOW ? f = gl.UNSIGNED_BYTE : this.properties.precision === c.HIGH && (f = gl.HIGH_PRECISION_FORMAT); + k && k.width == b && k.height == d && k.type == f || (k = gl.LINEAR, this.properties.generate_mipmaps && isPowerOfTwo(b) && isPowerOfTwo(d) && (k = gl.LINEAR_MIPMAP_LINEAR), this._temp_texture = new GL.Texture(b, d, {type:f, format:gl.RGBA, minFilter:k, magFilter:gl.LINEAR})); + a.copyTo(this._temp_texture); + this.properties.generate_mipmaps && (this._temp_texture.bind(0), gl.generateMipmap(this._temp_texture.texture_type), this._temp_texture.unbind(0)); + } + this.setOutputData(0, this._temp_texture); + } + }, F.registerNodeType("texture/copy", v), E.title = "Downsample", E.desc = "Downsample Texture", E.widgets_info = {iterations:{type:"number", step:1, precision:0, min:0}, precision:{widget:"combo", values:c.MODE_VALUES}}, E.prototype.onExecute = function() { + var a = this.getInputData(0); + if ((a || this._temp_texture) && this.isOutputConnected(0) && a && a.texture_type === GL.TEXTURE_2D) { + if (1 > this.properties.iterations) { + this.setOutputData(0, a); + } else { + var b = E._shader; + b || (E._shader = b = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, E.pixel_shader)); + var d = a.width | 0, k = a.height | 0, f = a.type; + this.properties.precision === c.LOW ? f = gl.UNSIGNED_BYTE : this.properties.precision === c.HIGH && (f = gl.HIGH_PRECISION_FORMAT); + var h = this.properties.iterations || 1, e = a, n = []; + f = {type:f, format:a.format}; + var g = vec2.create(), q = {u_offset:g}; + this._texture && GL.Texture.releaseTemporary(this._texture); + for (var l = 0; l < h; ++l) { + g[0] = 1 / d; + g[1] = 1 / k; + d = d >> 1 || 0; + k = k >> 1 || 0; + a = GL.Texture.getTemporary(d, k, f); + n.push(a); + e.setParameter(GL.TEXTURE_MAG_FILTER, GL.NEAREST); + e.copyTo(a, b, q); + if (1 == d && 1 == k) { + break; + } + e = a; + } + this._texture = n.pop(); + for (l = 0; l < n.length; ++l) { + GL.Texture.releaseTemporary(n[l]); + } + this.properties.generate_mipmaps && (this._texture.bind(0), gl.generateMipmap(this._texture.texture_type), this._texture.unbind(0)); + this.setOutputData(0, this._texture); + } + } + }, E.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec2 u_offset;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = texture2D(u_texture, v_coord );\n\r\n\t\t\tcolor += texture2D(u_texture, v_coord + vec2( u_offset.x, 0.0 ) );\n\r\n\t\t\tcolor += texture2D(u_texture, v_coord + vec2( 0.0, u_offset.y ) );\n\r\n\t\t\tcolor += texture2D(u_texture, v_coord + vec2( u_offset.x, u_offset.y ) );\n\r\n\t\t gl_FragColor = color * 0.25;\n\r\n\t\t}\n\r\n\t\t", + F.registerNodeType("texture/downsample", E), z.title = "Average", z.desc = "Compute a partial average (32 random samples) of a texture and stores it as a 1x1 pixel texture.\n If high_quality is true, then it generates the mipmaps first and reads from the lower one.", z.prototype.onExecute = function() { + this.properties.use_previous_frame || this.updateAverage(); + var a = this._luminance; + this.setOutputData(0, this._temp_texture); + this.setOutputData(1, a); + this.setOutputData(2, (a[0] + a[1] + a[2]) / 3); + }, z.prototype.onPreRenderExecute = function() { + this.updateAverage(); + }, z.prototype.updateAverage = function() { + var a = this.getInputData(0); + if (a && (this.isOutputConnected(0) || this.isOutputConnected(1) || this.isOutputConnected(2))) { + if (!z._shader) { + z._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, z.pixel_shader); + for (var b = new Float32Array(16), d = 0; d < b.length; ++d) { + b[d] = Math.random(); + } + z._shader.uniforms({u_samples_a:b.subarray(0, 16), u_samples_b:b.subarray(16, 32)}); + } + d = this._temp_texture; + b = gl.UNSIGNED_BYTE; + a.type != b && (b = gl.FLOAT); + d && d.type == b || (this._temp_texture = new GL.Texture(1, 1, {type:b, format:gl.RGBA, filter:gl.NEAREST})); + this._uniforms.u_mipmap_offset = 0; + this.properties.high_quality && (this._temp_pot2_texture && this._temp_pot2_texture.type == b || (this._temp_pot2_texture = new GL.Texture(512, 512, {type:b, format:gl.RGBA, minFilter:gl.LINEAR_MIPMAP_LINEAR, magFilter:gl.LINEAR})), a.copyTo(this._temp_pot2_texture), a = this._temp_pot2_texture, a.bind(0), gl.generateMipmap(GL.TEXTURE_2D), this._uniforms.u_mipmap_offset = 9); + var c = z._shader, k = this._uniforms; + k.u_mipmap_offset = this.properties.mipmap_offset; + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.BLEND); + this._temp_texture.drawTo(function() { + a.toViewport(c, k); + }); + if (this.isOutputConnected(1) || this.isOutputConnected(2)) { + if (d = this._temp_texture.getPixels()) { + var f = this._luminance; + b = this._temp_texture.type; + f.set(d); + b == gl.UNSIGNED_BYTE && vec4.scale(f, f, 1 / 255); + } + } + } + }, z.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tuniform mat4 u_samples_a;\n\r\n\t\tuniform mat4 u_samples_b;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_mipmap_offset;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = vec4(0.0);\n\r\n\t\t\t//random average\n\r\n\t\t\tfor(int i = 0; i < 4; ++i)\n\r\n\t\t\t\tfor(int j = 0; j < 4; ++j)\n\r\n\t\t\t\t{\n\r\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\r\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\r\n\t\t\t\t}\n\r\n\t\t gl_FragColor = color * 0.03125;\n\r\n\t\t}\n\r\n\t\t", + F.registerNodeType("texture/average", z), e.title = "Smooth", e.desc = "Smooth texture over time", e.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a && this.isOutputConnected(0)) { + e._shader || (e._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, e.pixel_shader)); + var b = this._temp_texture; + b && b.type == a.type && b.width == a.width && b.height == a.height || (b = {type:a.type, format:gl.RGBA, filter:gl.NEAREST}, this._temp_texture = new GL.Texture(a.width, a.height, b), this._temp_texture2 = new GL.Texture(a.width, a.height, b), a.copyTo(this._temp_texture2)); + b = this._temp_texture; + var d = this._temp_texture2, c = e._shader, k = this._uniforms; + k.u_factor = 1.0 - this.getInputOrProperty("factor"); + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + b.drawTo(function() { + d.bind(1); + a.toViewport(c, k); + }); + this.setOutputData(0, b); + this._temp_texture = d; + this._temp_texture2 = b; + } + }, e.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tuniform float u_factor;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tgl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\r\n\t\t}\n\r\n\t\t", F.registerNodeType("texture/temporal_smooth", e), C.title = "Lineal Avg Smooth", C.desc = "Smooth texture linearly over time", + C["@samples"] = {type:"number", min:1, max:64, step:1, precision:1}, C.prototype.getPreviewTexture = function() { + return this._temp_texture2; + }, C.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a && this.isOutputConnected(0)) { + C._shader || (C._shader_copy = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, C.pixel_shader_copy), C._shader_avg = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, C.pixel_shader_avg)); + var b = Math.clamp(this.properties.samples, 0, 64), d = this.frame, c = this.properties.frames_interval; + if (0 == c || 0 == d % c) { + d = this._temp_texture; + d && d.type == a.type && d.width == b || (d = {type:a.type, format:gl.RGBA, filter:gl.NEAREST}, this._temp_texture = new GL.Texture(b, 1, d), this._temp_texture2 = new GL.Texture(b, 1, d), this._temp_texture_out = new GL.Texture(1, 1, d)); + var k = this._temp_texture, f = this._temp_texture2, h = C._shader_copy, e = C._shader_avg, n = this._uniforms; + n.u_samples = b; + n.u_isamples = 1.0 / b; + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + k.drawTo(function() { + f.bind(1); + a.toViewport(h, n); + }); + this._temp_texture_out.drawTo(function() { + k.toViewport(e, n); + }); + this.setOutputData(0, this._temp_texture_out); + this._temp_texture = f; + this._temp_texture2 = k; + } else { + this.setOutputData(0, this._temp_texture_out); + } + this.setOutputData(1, this._temp_texture2); + this.frame++; + } + }, C.pixel_shader_copy = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tuniform float u_isamples;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tif( v_coord.x <= u_isamples )\n\r\n\t\t\t\tgl_FragColor = texture2D( u_texture, vec2(0.5) );\n\r\n\t\t\telse\n\r\n\t\t\t\tgl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\r\n\t\t}\n\r\n\t\t", C.pixel_shader_avg = + "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform int u_samples;\n\r\n\t\tuniform float u_isamples;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = vec4(0.0);\n\r\n\t\t\tfor(int i = 0; i < 64; ++i)\n\r\n\t\t\t{\n\r\n\t\t\t\tcolor += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\r\n\t\t\t\tif(i == (u_samples - 1))\n\r\n\t\t\t\t\tbreak;\n\r\n\t\t\t}\n\r\n\t\t\tgl_FragColor = color * u_isamples;\n\r\n\t\t}\n\r\n\t\t", + F.registerNodeType("texture/linear_avg_smooth", C), D.title = "Image to Texture", D.desc = "Uploads an image to the GPU", D.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a) { + var b = a.videoWidth || a.width, d = a.videoHeight || a.height; + if (a.gltexture) { + this.setOutputData(0, a.gltexture); + } else { + var c = this._temp_texture; + c && c.width == b && c.height == d || (this._temp_texture = new GL.Texture(b, d, {format:gl.RGBA, filter:gl.LINEAR})); + try { + this._temp_texture.uploadImage(a); + } catch (S) { + console.error("image comes from an unsafe location, cannot be uploaded to webgl: " + S); + return; + } + this.setOutputData(0, this._temp_texture); + } + } + }, F.registerNodeType("texture/imageToTexture", D), t.widgets_info = {texture:{widget:"texture"}, precision:{widget:"combo", values:c.MODE_VALUES}}, t.title = "LUT", t.desc = "Apply LUT to Texture", t.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var a = this.getInputData(0); + if (this.properties.precision === c.PASS_THROUGH || !1 === this.properties.enabled) { + this.setOutputData(0, a); + } else { + if (a) { + var b = this.getInputData(1); + b || (b = c.getTexture(this.properties.texture)); + if (b) { + b.bind(0); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.bindTexture(gl.TEXTURE_2D, null); + var d = this.properties.intensity; + this.isInputConnected(2) && (this.properties.intensity = d = this.getInputData(2)); + this._tex = c.getTargetTexture(a, this._tex, this.properties.precision); + this._tex.drawTo(function() { + b.bind(1); + a.toViewport(t._shader, {u_texture:0, u_textureB:1, u_amount:d}); + }); + this.setOutputData(0, this._tex); + } else { + this.setOutputData(0, a); + } + } + } + } + }, t.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tuniform float u_amount;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\r\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\r\n\t\t\t mediump vec2 quad1;\n\r\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\r\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\r\n\t\t\t mediump vec2 quad2;\n\r\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\r\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\r\n\t\t\t highp vec2 texPos1;\n\r\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\r\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\r\n\t\t\t highp vec2 texPos2;\n\r\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\r\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\r\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\r\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\r\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\r\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\r\n\t\t}\n\r\n\t\t", + F.registerNodeType("texture/LUT", t), G.title = "Texture to Channels", G.desc = "Split texture channels", G.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a) { + this._channels || (this._channels = Array(4)); + for (var b = gl.RGB, d = 0, c = 0; 4 > c; c++) { + this.isOutputConnected(c) ? (this._channels[c] && this._channels[c].width == a.width && this._channels[c].height == a.height && this._channels[c].type == a.type && this._channels[c].format == b || (this._channels[c] = new GL.Texture(a.width, a.height, {type:a.type, format:b, filter:gl.LINEAR})), d++) : this._channels[c] = null; + } + if (d) { + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var k = Mesh.getScreenQuad(), f = G._shader, h = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]; + for (c = 0; 4 > c; c++) { + this._channels[c] && (this._channels[c].drawTo(function() { + a.bind(0); + f.uniforms({u_texture:0, u_mask:h[c]}).draw(k); + }), this.setOutputData(c, this._channels[c])); + } + } + } + }, G.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec4 u_mask;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\r\n\t\t}\n\r\n\t\t", F.registerNodeType("texture/textureChannels", G), n.title = "Channels to Texture", n.desc = "Split texture channels", n.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, + n.prototype.onExecute = function() { + var a = c.getWhiteTexture(), b = this.getInputData(0) || a, d = this.getInputData(1) || a, k = this.getInputData(2) || a, f = this.getInputData(3) || a; + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var h = Mesh.getScreenQuad(); + n._shader || (n._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, n.pixel_shader)); + var e = n._shader; + a = Math.max(b.width, d.width, k.width, f.width); + var g = Math.max(b.height, d.height, k.height, f.height), q = this.properties.precision == c.HIGH ? c.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE; + this._texture && this._texture.width == a && this._texture.height == g && this._texture.type == q || (this._texture = new GL.Texture(a, g, {type:q, format:gl.RGBA, filter:gl.LINEAR})); + a = this._color; + a[0] = this.properties.R; + a[1] = this.properties.G; + a[2] = this.properties.B; + a[3] = this.properties.A; + var l = this._uniforms; + this._texture.drawTo(function() { + b.bind(0); + d.bind(1); + k.bind(2); + f.bind(3); + e.uniforms(l).draw(h); + }); + this.setOutputData(0, this._texture); + }, n.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_textureR;\n\r\n\t\tuniform sampler2D u_textureG;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tuniform sampler2D u_textureA;\n\r\n\t\tuniform vec4 u_color;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t gl_FragColor = u_color * vec4( \r\n\t\t\t\t\ttexture2D(u_textureR, v_coord).r,\r\n\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\r\n\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\r\n\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\r\n\t\t}\n\r\n\t\t", + F.registerNodeType("texture/channelsTexture", n), q.title = "Color", q.desc = "Generates a 1x1 texture with a constant color", q.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, q.prototype.onDrawBackground = function(a) { + var b = this.properties.color; + a.fillStyle = "rgb(" + Math.floor(255 * Math.clamp(b[0], 0, 1)) + "," + Math.floor(255 * Math.clamp(b[1], 0, 1)) + "," + Math.floor(255 * Math.clamp(b[2], 0, 1)) + ")"; + this.flags.collapsed ? this.boxcolor = a.fillStyle : a.fillRect(0, 0, this.size[0], this.size[1]); + }, q.prototype.onExecute = function() { + var a = this.properties.precision == c.HIGH ? c.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE; + this._tex && this._tex.type == a || (this._tex = new GL.Texture(1, 1, {format:gl.RGBA, type:a, minFilter:gl.NEAREST})); + a = this.properties.color; + if (this.inputs) { + for (var b = 0; b < this.inputs.length; b++) { + var d = this.inputs[b], k = this.getInputData(b); + if (void 0 !== k) { + switch(d.name) { + case "RGB": + case "RGBA": + a.set(k); + break; + case "R": + a[0] = k; + break; + case "G": + a[1] = k; + break; + case "B": + a[2] = k; + break; + case "A": + a[3] = k; + } + } + } + } + 0.001 < vec4.sqrDist(this._tex_color, a) && (this._tex_color.set(a), this._tex.fill(a)); + this.setOutputData(0, this._tex); + }, q.prototype.onGetInputs = function() { + return [["RGB", "vec3"], ["RGBA", "vec4"], ["R", "number"], ["G", "number"], ["B", "number"], ["A", "number"]]; + }, F.registerNodeType("texture/color", q), k.title = "Gradient", k.desc = "Generates a gradient", k["@A"] = {type:"color"}, k["@B"] = {type:"color"}, k["@texture_size"] = {type:"enum", values:[32, 64, 128, 256, 512]}, k.prototype.onExecute = function() { + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var a = GL.Mesh.getScreenQuad(), b = k._shader, d = this.getInputData(0); + d || (d = this.properties.A); + var c = this.getInputData(1); + c || (c = this.properties.B); + for (var f = 2; f < this.inputs.length; f++) { + var h = this.inputs[f], e = this.getInputData(f); + void 0 !== e && (this.properties[h.name] = e); + } + var n = this._uniforms; + this._uniforms.u_angle = this.properties.angle * DEG2RAD; + this._uniforms.u_scale = this.properties.scale; + vec3.copy(n.u_colorA, d); + vec3.copy(n.u_colorB, c); + d = parseInt(this.properties.texture_size); + this._tex && this._tex.width == d || (this._tex = new GL.Texture(d, d, {format:gl.RGB, filter:gl.LINEAR})); + this._tex.drawTo(function() { + b.uniforms(n).draw(a); + }); + this.setOutputData(0, this._tex); + }, k.prototype.onGetInputs = function() { + return [["angle", "number"], ["scale", "number"]]; + }, k.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform float u_angle;\n\r\n\t\tuniform float u_scale;\n\r\n\t\tuniform vec3 u_colorA;\n\r\n\t\tuniform vec3 u_colorB;\n\r\n\t\t\n\r\n\t\tvec2 rotate(vec2 v, float angle)\n\r\n\t\t{\n\r\n\t\t\tvec2 result;\n\r\n\t\t\tfloat _cos = cos(angle);\n\r\n\t\t\tfloat _sin = sin(angle);\n\r\n\t\t\tresult.x = v.x * _cos - v.y * _sin;\n\r\n\t\t\tresult.y = v.x * _sin + v.y * _cos;\n\r\n\t\t\treturn result;\n\r\n\t\t}\n\r\n\t\tvoid main() {\n\r\n\t\t\tfloat f = (rotate(u_scale * (v_coord - vec2(0.5)), u_angle) + vec2(0.5)).x;\n\r\n\t\t\tvec3 color = mix(u_colorA,u_colorB,clamp(f,0.0,1.0));\n\r\n\t\t gl_FragColor = vec4(color,1.0);\n\r\n\t\t}\n\r\n\t\t", + F.registerNodeType("texture/gradient", k), a.title = "Mix", a.desc = "Generates a texture mixing two textures", a.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, a.prototype.onExecute = function() { + var b = this.getInputData(0); + if (this.isOutputConnected(0)) { + if (this.properties.precision === c.PASS_THROUGH) { + this.setOutputData(0, b); + } else { + var d = this.getInputData(1); + if (b && d) { + var k = this.getInputData(2), f = this.getInputData(3); + this._tex = c.getTargetTexture(this.properties.size_from_biggest && d.width > b.width ? d : b, this._tex, this.properties.precision); + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var h = Mesh.getScreenQuad(), e = null, n = this._uniforms; + k ? (e = a._shader_tex, e || (e = a._shader_tex = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, a.pixel_shader, {MIX_TEX:""}))) : (e = a._shader_factor, e || (e = a._shader_factor = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, a.pixel_shader)), f = null == f ? this.properties.factor : f, n.u_mix.set([f, f, f, f])); + var g = this.properties.invert; + this._tex.drawTo(function() { + b.bind(g ? 1 : 0); + d.bind(g ? 0 : 1); + k && k.bind(2); + e.uniforms(n).draw(h); + }); + this.setOutputData(0, this._tex); + } + } + } + }, a.prototype.onGetInputs = function() { + return [["factor", "number"]]; + }, a.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_textureA;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\t#ifdef MIX_TEX\n\r\n\t\t\tuniform sampler2D u_textureMix;\n\r\n\t\t#else\n\r\n\t\t\tuniform vec4 u_mix;\n\r\n\t\t#endif\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\t#ifdef MIX_TEX\n\r\n\t\t\t vec4 f = texture2D(u_textureMix, v_coord);\n\r\n\t\t\t#else\n\r\n\t\t\t vec4 f = u_mix;\n\r\n\t\t\t#endif\n\r\n\t\t gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\r\n\t\t}\n\r\n\t\t", + F.registerNodeType("texture/mix", a), b.title = "Edges", b.desc = "Detects edges", b.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, b.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var a = this.getInputData(0); + if (this.properties.precision === c.PASS_THROUGH) { + this.setOutputData(0, a); + } else { + if (a) { + this._tex = c.getTargetTexture(a, this._tex, this.properties.precision); + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var d = Mesh.getScreenQuad(), k = b._shader, f = this.properties.invert, h = this.properties.factor, e = this.properties.threshold ? 1 : 0; + this._tex.drawTo(function() { + a.bind(0); + k.uniforms({u_texture:0, u_isize:[1 / a.width, 1 / a.height], u_factor:h, u_threshold:e, u_invert:f ? 1 : 0}).draw(d); + }); + this.setOutputData(0, this._tex); + } + } + } + }, b.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec2 u_isize;\n\r\n\t\tuniform int u_invert;\n\r\n\t\tuniform float u_factor;\n\r\n\t\tuniform float u_threshold;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 center = texture2D(u_texture, v_coord);\n\r\n\t\t\tvec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\r\n\t\t\tvec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\r\n\t\t\tvec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\r\n\t\t\tvec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\r\n\t\t\tvec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\r\n\t\t\tdiff *= u_factor;\n\r\n\t\t\tif(u_invert == 1)\n\r\n\t\t\t\tdiff.xyz = vec3(1.0) - diff.xyz;\n\r\n\t\t\tif( u_threshold == 0.0 )\n\r\n\t\t\t\tgl_FragColor = vec4( diff.xyz, center.a );\n\r\n\t\t\telse\n\r\n\t\t\t\tgl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\r\n\t\t}\n\r\n\t\t", + F.registerNodeType("texture/edges", b), d.title = "Depth Range", d.desc = "Generates a texture with a depth range", d.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var a = this.getInputData(0); + if (a) { + var b = gl.UNSIGNED_BYTE; + this.properties.high_precision && (b = gl.half_float_ext ? gl.HALF_FLOAT_OES : gl.FLOAT); + this._temp_texture && this._temp_texture.type == b && this._temp_texture.width == a.width && this._temp_texture.height == a.height || (this._temp_texture = new GL.Texture(a.width, a.height, {type:b, format:gl.RGBA, filter:gl.LINEAR})); + var c = this._uniforms; + b = this.properties.distance; + this.isInputConnected(1) && (b = this.getInputData(1), this.properties.distance = b); + var k = this.properties.range; + this.isInputConnected(2) && (k = this.getInputData(2), this.properties.range = k); + c.u_distance = b; + c.u_range = k; + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var f = Mesh.getScreenQuad(); + d._shader || (d._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, d.pixel_shader), d._shader_onlydepth = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, d.pixel_shader, {ONLY_DEPTH:""})); + var h = this.properties.only_depth ? d._shader_onlydepth : d._shader; + b = null; + b = a.near_far_planes ? a.near_far_planes : window.LS && LS.Renderer._main_camera ? LS.Renderer._main_camera._uniforms.u_camera_planes : [0.1, 1000]; + c.u_camera_planes = b; + this._temp_texture.drawTo(function() { + a.bind(0); + h.uniforms(c).draw(f); + }); + this._temp_texture.near_far_planes = b; + this.setOutputData(0, this._temp_texture); + } + } + }, d.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec2 u_camera_planes;\n\r\n\t\tuniform float u_distance;\n\r\n\t\tuniform float u_range;\n\r\n\t\t\n\r\n\t\tfloat LinearDepth()\n\r\n\t\t{\n\r\n\t\t\tfloat zNear = u_camera_planes.x;\n\r\n\t\t\tfloat zFar = u_camera_planes.y;\n\r\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\r\n\t\t\tdepth = depth * 2.0 - 1.0;\n\r\n\t\t\treturn zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tfloat depth = LinearDepth();\n\r\n\t\t\t#ifdef ONLY_DEPTH\n\r\n\t\t\t gl_FragColor = vec4(depth);\n\r\n\t\t\t#else\n\r\n\t\t\t\tfloat diff = abs(depth * u_camera_planes.y - u_distance);\n\r\n\t\t\t\tfloat dof = 1.0;\n\r\n\t\t\t\tif(diff <= u_range)\n\r\n\t\t\t\t\tdof = diff / u_range;\n\r\n\t\t\t gl_FragColor = vec4(dof);\n\r\n\t\t\t#endif\n\r\n\t\t}\n\r\n\t\t", + F.registerNodeType("texture/depth_range", d), h.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, h.title = "Linear Depth", h.desc = "Creates a color texture with linear depth", h.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var a = this.getInputData(0); + if (a && (a.format == gl.DEPTH_COMPONENT || a.format == gl.DEPTH_STENCIL)) { + var b = this.properties.precision == c.HIGH ? gl.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE; + this._temp_texture && this._temp_texture.type == b && this._temp_texture.width == a.width && this._temp_texture.height == a.height || (this._temp_texture = new GL.Texture(a.width, a.height, {type:b, format:gl.RGB, filter:gl.LINEAR})); + var d = this._uniforms; + d.u_near = a.near_far_planes[0]; + d.u_far = a.near_far_planes[1]; + d.u_invert = this.properties.invert ? 1 : 0; + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var k = Mesh.getScreenQuad(); + h._shader || (h._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, h.pixel_shader)); + var f = h._shader; + b = null; + b = a.near_far_planes ? a.near_far_planes : window.LS && LS.Renderer._main_camera ? LS.Renderer._main_camera._uniforms.u_camera_planes : [0.1, 1000]; + d.u_camera_planes = b; + this._temp_texture.drawTo(function() { + a.bind(0); + f.uniforms(d).draw(k); + }); + this._temp_texture.near_far_planes = b; + this.setOutputData(0, this._temp_texture); + } + } + }, h.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_near;\n\r\n\t\tuniform float u_far;\n\r\n\t\tuniform int u_invert;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tfloat zNear = u_near;\n\r\n\t\t\tfloat zFar = u_far;\n\r\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\r\n\t\t\tdepth = depth * 2.0 - 1.0;\n\r\n\t\t\tfloat f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\r\n\t\t\tif( u_invert == 1 )\n\r\n\t\t\t\tf = 1.0 - f;\n\r\n\t\t\tgl_FragColor = vec4(vec3(f),1.0);\n\r\n\t\t}\n\r\n\t\t", + F.registerNodeType("texture/linear_depth", h), f.title = "Blur", f.desc = "Blur a texture", f.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, f.max_iterations = 20, f.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a && this.isOutputConnected(0)) { + var b = this._final_texture; + b && b.width == a.width && b.height == a.height && b.type == a.type || (b = this._final_texture = new GL.Texture(a.width, a.height, {type:a.type, format:gl.RGBA, filter:gl.LINEAR})); + var d = this.properties.iterations; + this.isInputConnected(1) && (d = this.getInputData(1), this.properties.iterations = d); + d = Math.min(Math.floor(d), f.max_iterations); + if (0 == d) { + this.setOutputData(0, a); + } else { + var c = this.properties.intensity; + this.isInputConnected(2) && (c = this.getInputData(2), this.properties.intensity = c); + var k = F.camera_aspect; + k || void 0 === window.gl || (k = gl.canvas.height / gl.canvas.width); + k || (k = 1); + k = this.properties.preserve_aspect ? k : 1; + var h = this.properties.scale || [1, 1]; + a.applyBlur(k * h[0], h[1], c, b); + for (a = 1; a < d; ++a) { + b.applyBlur(k * h[0] * (a + 1), h[1] * (a + 1), c); + } + this.setOutputData(0, b); + } + } + }, F.registerNodeType("texture/blur", f), x.title = "Glow", x.desc = "Filters a texture giving it a glow effect", x.weights = new Float32Array([0.5, 0.4, 0.3, 0.2]), x.widgets_info = {iterations:{type:"number", min:0, max:16, step:1, precision:0}, threshold:{type:"number", min:0, max:10, step:0.01, precision:2}, precision:{widget:"combo", values:c.MODE_VALUES}}, x.prototype.onGetInputs = function() { + return [["enabled", "boolean"], ["threshold", "number"], ["intensity", "number"], ["persistence", "number"], ["iterations", "number"], ["dirt_factor", "number"]]; + }, x.prototype.onGetOutputs = function() { + return [["average", "Texture"]]; + }, x.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a && this.isAnyOutputConnected()) { + if (this.properties.precision === c.PASS_THROUGH || !1 === this.getInputOrProperty("enabled")) { + this.setOutputData(0, a); + } else { + var b = a.width, d = a.height, k = {format:a.format, type:a.type, minFilter:GL.LINEAR, magFilter:GL.LINEAR, wrap:gl.CLAMP_TO_EDGE}, f = c.getTextureType(this.properties.precision, a), h = this._uniforms, e = this._textures, n = x._cut_shader; + n || (n = x._cut_shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, x.cut_pixel_shader)); + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.BLEND); + h.u_threshold = this.getInputOrProperty("threshold"); + var g = e[0] = GL.Texture.getTemporary(b, d, k); + a.blit(g, n.uniforms(h)); + var q = g, l = this.getInputOrProperty("iterations"); + l = Math.clamp(l, 1, 16) | 0; + var m = h.u_texel_size, p = this.getInputOrProperty("intensity"); + h.u_intensity = 1; + h.u_delta = this.properties.scale; + n = x._shader; + n || (n = x._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, x.scale_pixel_shader)); + for (var r = 1; r < l; r++) { + b >>= 1; + 1 < (d | 0) && (d >>= 1); + if (2 > b) { + break; + } + g = e[r] = GL.Texture.getTemporary(b, d, k); + m[0] = 1 / q.width; + m[1] = 1 / q.height; + q.blit(g, n.uniforms(h)); + q = g; + } + this.isOutputConnected(2) && (b = this._average_texture, b && b.type == a.type && b.format == a.format || (b = this._average_texture = new GL.Texture(1, 1, {type:a.type, format:a.format, filter:gl.LINEAR})), m[0] = 1 / q.width, m[1] = 1 / q.height, h.u_intensity = p, h.u_delta = 1, q.blit(b, n.uniforms(h)), this.setOutputData(2, b)); + gl.enable(gl.BLEND); + gl.blendFunc(gl.ONE, gl.ONE); + h.u_intensity = this.getInputOrProperty("persistence"); + h.u_delta = 0.5; + for (r -= 2; 0 <= r; r--) { + g = e[r], e[r] = null, m[0] = 1 / q.width, m[1] = 1 / q.height, q.blit(g, n.uniforms(h)), GL.Texture.releaseTemporary(q), q = g; + } + gl.disable(gl.BLEND); + this.isOutputConnected(1) && (e = this._glow_texture, e && e.width == a.width && e.height == a.height && e.type == f && e.format == a.format || (e = this._glow_texture = new GL.Texture(a.width, a.height, {type:f, format:a.format, filter:gl.LINEAR})), q.blit(e), this.setOutputData(1, e)); + if (this.isOutputConnected(0)) { + e = this._final_texture; + e && e.width == a.width && e.height == a.height && e.type == f && e.format == a.format || (e = this._final_texture = new GL.Texture(a.width, a.height, {type:f, format:a.format, filter:gl.LINEAR})); + var u = this.getInputData(1), t = this.getInputOrProperty("dirt_factor"); + h.u_intensity = p; + n = u ? x._dirt_final_shader : x._final_shader; + n || (n = u ? x._dirt_final_shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, x.final_pixel_shader, {USE_DIRT:""}) : x._final_shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, x.final_pixel_shader)); + e.drawTo(function() { + a.bind(0); + q.bind(1); + u && (n.setUniform("u_dirt_factor", t), n.setUniform("u_dirt_texture", u.bind(2))); + n.toViewport(h); + }); + this.setOutputData(0, e); + } + GL.Texture.releaseTemporary(q); + } + } + }, x.cut_pixel_shader = "precision highp float;\n\r\n\tvarying vec2 v_coord;\n\r\n\tuniform sampler2D u_texture;\n\r\n\tuniform float u_threshold;\n\r\n\tvoid main() {\n\r\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\r\n\t}", x.scale_pixel_shader = "precision highp float;\n\r\n\tvarying vec2 v_coord;\n\r\n\tuniform sampler2D u_texture;\n\r\n\tuniform vec2 u_texel_size;\n\r\n\tuniform float u_delta;\n\r\n\tuniform float u_intensity;\n\r\n\t\n\r\n\tvec4 sampleBox(vec2 uv) {\n\r\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\r\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\r\n\t\treturn s * 0.25;\n\r\n\t}\n\r\n\tvoid main() {\n\r\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\r\n\t}", + x.final_pixel_shader = "precision highp float;\n\r\n\tvarying vec2 v_coord;\n\r\n\tuniform sampler2D u_texture;\n\r\n\tuniform sampler2D u_glow_texture;\n\r\n\t#ifdef USE_DIRT\n\r\n\t\tuniform sampler2D u_dirt_texture;\n\r\n\t#endif\n\r\n\tuniform vec2 u_texel_size;\n\r\n\tuniform float u_delta;\n\r\n\tuniform float u_intensity;\n\r\n\tuniform float u_dirt_factor;\n\r\n\t\n\r\n\tvec4 sampleBox(vec2 uv) {\n\r\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\r\n\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\r\n\t\treturn s * 0.25;\n\r\n\t}\n\r\n\tvoid main() {\n\r\n\t\tvec4 glow = sampleBox( v_coord );\n\r\n\t\t#ifdef USE_DIRT\n\r\n\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\r\n\t\t#endif\n\r\n\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\r\n\t}", + F.registerNodeType("texture/glow", x), H.title = "Kuwahara Filter", H.desc = "Filters a texture giving an artistic oil canvas painting", H.max_radius = 10, H._shaders = [], H.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a && this.isOutputConnected(0)) { + var b = this._temp_texture; + b && b.width == a.width && b.height == a.height && b.type == a.type || (this._temp_texture = new GL.Texture(a.width, a.height, {type:a.type, format:gl.RGBA, filter:gl.LINEAR})); + b = this.properties.radius; + b = Math.min(Math.floor(b), H.max_radius); + if (0 == b) { + this.setOutputData(0, a); + } else { + var d = this.properties.intensity, c = F.camera_aspect; + c || void 0 === window.gl || (c = gl.canvas.height / gl.canvas.width); + c || (c = 1); + c = this.properties.preserve_aspect ? c : 1; + H._shaders[b] || (H._shaders[b] = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, H.pixel_shader, {RADIUS:b.toFixed(0)})); + var k = H._shaders[b], f = GL.Mesh.getScreenQuad(); + a.bind(0); + this._temp_texture.drawTo(function() { + k.uniforms({u_texture:0, u_intensity:d, u_resolution:[a.width, a.height], u_iResolution:[1 / a.width, 1 / a.height]}).draw(f); + }); + this.setOutputData(0, this._temp_texture); + } + } + }, H.pixel_shader = "\n\r\nprecision highp float;\n\r\nvarying vec2 v_coord;\n\r\nuniform sampler2D u_texture;\n\r\nuniform float u_intensity;\n\r\nuniform vec2 u_resolution;\n\r\nuniform vec2 u_iResolution;\n\r\n#ifndef RADIUS\n\r\n\t#define RADIUS 7\n\r\n#endif\n\r\nvoid main() {\n\r\n\n\r\n\tconst int radius = RADIUS;\n\r\n\tvec2 fragCoord = v_coord;\n\r\n\tvec2 src_size = u_iResolution;\n\r\n\tvec2 uv = v_coord;\n\r\n\tfloat n = float((radius + 1) * (radius + 1));\n\r\n\tint i;\n\r\n\tint j;\n\r\n\tvec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\r\n\tvec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\r\n\tvec3 c;\n\r\n\t\n\r\n\tfor (int j = -radius; j <= 0; ++j) {\n\r\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\r\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\r\n\t\t\tm0 += c;\n\r\n\t\t\ts0 += c * c;\n\r\n\t\t}\n\r\n\t}\n\r\n\t\n\r\n\tfor (int j = -radius; j <= 0; ++j) {\n\r\n\t\tfor (int i = 0; i <= radius; ++i) {\n\r\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\r\n\t\t\tm1 += c;\n\r\n\t\t\ts1 += c * c;\n\r\n\t\t}\n\r\n\t}\n\r\n\t\n\r\n\tfor (int j = 0; j <= radius; ++j) {\n\r\n\t\tfor (int i = 0; i <= radius; ++i) {\n\r\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\r\n\t\t\tm2 += c;\n\r\n\t\t\ts2 += c * c;\n\r\n\t\t}\n\r\n\t}\n\r\n\t\n\r\n\tfor (int j = 0; j <= radius; ++j) {\n\r\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\r\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\r\n\t\t\tm3 += c;\n\r\n\t\t\ts3 += c * c;\n\r\n\t\t}\n\r\n\t}\n\r\n\t\n\r\n\tfloat min_sigma2 = 1e+2;\n\r\n\tm0 /= n;\n\r\n\ts0 = abs(s0 / n - m0 * m0);\n\r\n\t\n\r\n\tfloat sigma2 = s0.r + s0.g + s0.b;\n\r\n\tif (sigma2 < min_sigma2) {\n\r\n\t\tmin_sigma2 = sigma2;\n\r\n\t\tgl_FragColor = vec4(m0, 1.0);\n\r\n\t}\n\r\n\t\n\r\n\tm1 /= n;\n\r\n\ts1 = abs(s1 / n - m1 * m1);\n\r\n\t\n\r\n\tsigma2 = s1.r + s1.g + s1.b;\n\r\n\tif (sigma2 < min_sigma2) {\n\r\n\t\tmin_sigma2 = sigma2;\n\r\n\t\tgl_FragColor = vec4(m1, 1.0);\n\r\n\t}\n\r\n\t\n\r\n\tm2 /= n;\n\r\n\ts2 = abs(s2 / n - m2 * m2);\n\r\n\t\n\r\n\tsigma2 = s2.r + s2.g + s2.b;\n\r\n\tif (sigma2 < min_sigma2) {\n\r\n\t\tmin_sigma2 = sigma2;\n\r\n\t\tgl_FragColor = vec4(m2, 1.0);\n\r\n\t}\n\r\n\t\n\r\n\tm3 /= n;\n\r\n\ts3 = abs(s3 / n - m3 * m3);\n\r\n\t\n\r\n\tsigma2 = s3.r + s3.g + s3.b;\n\r\n\tif (sigma2 < min_sigma2) {\n\r\n\t\tmin_sigma2 = sigma2;\n\r\n\t\tgl_FragColor = vec4(m3, 1.0);\n\r\n\t}\n\r\n}\n\r\n", + F.registerNodeType("texture/kuwahara", H), I.title = "XDoG Filter", I.desc = "Filters a texture giving an artistic ink style", I.max_radius = 10, I._shaders = [], I.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a && this.isOutputConnected(0)) { + var b = this._temp_texture; + b && b.width == a.width && b.height == a.height && b.type == a.type || (this._temp_texture = new GL.Texture(a.width, a.height, {type:a.type, format:gl.RGBA, filter:gl.LINEAR})); + I._xdog_shader || (I._xdog_shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, I.xdog_pixel_shader)); + var d = I._xdog_shader, c = GL.Mesh.getScreenQuad(), k = this.properties.sigma, f = this.properties.k, h = this.properties.p, e = this.properties.epsilon, n = this.properties.phi; + a.bind(0); + this._temp_texture.drawTo(function() { + d.uniforms({src:0, sigma:k, k:f, p:h, epsilon:e, phi:n, cvsWidth:a.width, cvsHeight:a.height}).draw(c); + }); + this.setOutputData(0, this._temp_texture); + } + }, I.xdog_pixel_shader = "\n\r\nprecision highp float;\n\r\nuniform sampler2D src;\n\n\r\nuniform float cvsHeight;\n\r\nuniform float cvsWidth;\n\n\r\nuniform float sigma;\n\r\nuniform float k;\n\r\nuniform float p;\n\r\nuniform float epsilon;\n\r\nuniform float phi;\n\r\nvarying vec2 v_coord;\n\n\r\nfloat cosh(float val)\n\r\n{\n\r\n\tfloat tmp = exp(val);\n\r\n\tfloat cosH = (tmp + 1.0 / tmp) / 2.0;\n\r\n\treturn cosH;\n\r\n}\n\n\r\nfloat tanh(float val)\n\r\n{\n\r\n\tfloat tmp = exp(val);\n\r\n\tfloat tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\r\n\treturn tanH;\n\r\n}\n\n\r\nfloat sinh(float val)\n\r\n{\n\r\n\tfloat tmp = exp(val);\n\r\n\tfloat sinH = (tmp - 1.0 / tmp) / 2.0;\n\r\n\treturn sinH;\n\r\n}\n\n\r\nvoid main(void){\n\r\n\tvec3 destColor = vec3(0.0);\n\r\n\tfloat tFrag = 1.0 / cvsHeight;\n\r\n\tfloat sFrag = 1.0 / cvsWidth;\n\r\n\tvec2 Frag = vec2(sFrag,tFrag);\n\r\n\tvec2 uv = gl_FragCoord.st;\n\r\n\tfloat twoSigmaESquared = 2.0 * sigma * sigma;\n\r\n\tfloat twoSigmaRSquared = twoSigmaESquared * k * k;\n\r\n\tint halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\r\n\tconst int MAX_NUM_ITERATION = 99999;\n\r\n\tvec2 sum = vec2(0.0);\n\r\n\tvec2 norm = vec2(0.0);\n\n\r\n\tfor(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\r\n\t\tint i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\r\n\t\tint j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\r\n\t\tfloat d = length(vec2(i,j));\n\r\n\t\tvec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\r\n\t\t\t\t\t\t\texp( -d * d / twoSigmaRSquared ));\n\n\r\n\t\tvec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\r\n\t\tnorm += kernel;\n\r\n\t\tsum += kernel * L;\n\r\n\t}\n\n\r\n\tsum /= norm;\n\n\r\n\tfloat H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\r\n\tfloat edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\r\n\tdestColor = vec3(edge);\n\r\n\tgl_FragColor = vec4(destColor, 1.0);\n\r\n}", + F.registerNodeType("texture/xDoG", I), J.title = "Webcam", J.desc = "Webcam texture", J.is_webcam_open = !1, J.prototype.openStream = function() { + if (navigator.getUserMedia) { + this._waiting_confirmation = !0; + navigator.mediaDevices.getUserMedia({audio:!1, video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this)).catch(function(b) { + J.is_webcam_open = !1; + console.log("Webcam rejected", b); + a._webcam_stream = !1; + a.boxcolor = "red"; + a.trigger("stream_error"); + }); + var a = this; + } + }, J.prototype.closeStream = function() { + if (this._webcam_stream) { + var a = this._webcam_stream.getTracks(); + if (a.length) { + for (var b = 0; b < a.length; ++b) { + a[b].stop(); + } + } + J.is_webcam_open = !1; + this._video = this._webcam_stream = null; + this.boxcolor = "black"; + this.trigger("stream_closed"); + } + }, J.prototype.streamReady = function(a) { + this._webcam_stream = a; + this.boxcolor = "green"; + var b = this._video; + b || (b = document.createElement("video"), b.autoplay = !0, b.srcObject = a, this._video = b, b.onloadedmetadata = function(a) { + J.is_webcam_open = !0; + console.log(a); + }); + this.trigger("stream_ready", b); + }, J.prototype.onPropertyChanged = function(a, b) { + "facingMode" == a && (this.properties.facingMode = b, this.closeStream(), this.openStream()); + }, J.prototype.onRemoved = function() { + if (this._webcam_stream) { + var a = this._webcam_stream.getTracks(); + if (a.length) { + for (var b = 0; b < a.length; ++b) { + a[b].stop(); + } + } + this._video = this._webcam_stream = null; + } + }, J.prototype.onDrawBackground = function(a) { + this.flags.collapsed || 20 >= this.size[1] || !this._video || (a.save(), a.webgl ? this._video_texture && a.drawImage(this._video_texture, 0, 0, this.size[0], this.size[1]) : a.drawImage(this._video, 0, 0, this.size[0], this.size[1]), a.restore()); + }, J.prototype.onExecute = function() { + null != this._webcam_stream || this._waiting_confirmation || this.openStream(); + if (this._video && this._video.videoWidth) { + var a = this._video.videoWidth, b = this._video.videoHeight, d = this._video_texture; + d && d.width == a && d.height == b || (this._video_texture = new GL.Texture(a, b, {format:gl.RGB, filter:gl.LINEAR})); + this._video_texture.uploadImage(this._video); + this._video_texture.version = ++this.version; + this.properties.texture_name && (c.getTexturesContainer()[this.properties.texture_name] = this._video_texture); + this.setOutputData(0, this._video_texture); + for (a = 1; a < this.outputs.length; ++a) { + if (this.outputs[a]) { + switch(this.outputs[a].name) { + case "width": + this.setOutputData(a, this._video.videoWidth); + break; + case "height": + this.setOutputData(a, this._video.videoHeight); + } + } + } + } + }, J.prototype.onGetOutputs = function() { + return [["width", "number"], ["height", "number"], ["stream_ready", F.EVENT], ["stream_closed", F.EVENT], ["stream_error", F.EVENT]]; + }, F.registerNodeType("texture/webcam", J), L.title = "Lens FX", L.desc = "distortion and chromatic aberration", L.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, L.prototype.onGetInputs = function() { + return [["enabled", "boolean"]]; + }, L.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a && this.isOutputConnected(0)) { + if (this.properties.precision === c.PASS_THROUGH || !1 === this.getInputOrProperty("enabled")) { + this.setOutputData(0, a); + } else { + var b = this._temp_texture; + b && b.width == a.width && b.height == a.height && b.type == a.type || (b = this._temp_texture = new GL.Texture(a.width, a.height, {type:a.type, format:gl.RGBA, filter:gl.LINEAR})); + var d = L._shader; + d || (d = L._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, L.pixel_shader)); + var k = this.getInputData(1); + null == k && (k = this.properties.factor); + var f = this._uniforms; + f.u_factor = k; + gl.disable(gl.DEPTH_TEST); + b.drawTo(function() { + a.bind(0); + d.uniforms(f).draw(GL.Mesh.getScreenQuad()); + }); + this.setOutputData(0, b); + } + } + }, L.pixel_shader = "precision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_factor;\n\r\n\t\tvec2 barrelDistortion(vec2 coord, float amt) {\n\r\n\t\t\tvec2 cc = coord - 0.5;\n\r\n\t\t\tfloat dist = dot(cc, cc);\n\r\n\t\t\treturn coord + cc * dist * amt;\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tfloat sat( float t )\n\r\n\t\t{\n\r\n\t\t\treturn clamp( t, 0.0, 1.0 );\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tfloat linterp( float t ) {\n\r\n\t\t\treturn sat( 1.0 - abs( 2.0*t - 1.0 ) );\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tfloat remap( float t, float a, float b ) {\n\r\n\t\t\treturn sat( (t - a) / (b - a) );\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tvec4 spectrum_offset( float t ) {\n\r\n\t\t\tvec4 ret;\n\r\n\t\t\tfloat lo = step(t,0.5);\n\r\n\t\t\tfloat hi = 1.0-lo;\n\r\n\t\t\tfloat w = linterp( remap( t, 1.0/6.0, 5.0/6.0 ) );\n\r\n\t\t\tret = vec4(lo,1.0,hi, 1.) * vec4(1.0-w, w, 1.0-w, 1.);\n\r\n\t\t\n\r\n\t\t\treturn pow( ret, vec4(1.0/2.2) );\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tconst float max_distort = 2.2;\n\r\n\t\tconst int num_iter = 12;\n\r\n\t\tconst float reci_num_iter_f = 1.0 / float(num_iter);\n\r\n\t\t\n\r\n\t\tvoid main()\n\r\n\t\t{\t\n\r\n\t\t\tvec2 uv=v_coord;\n\r\n\t\t\tvec4 sumcol = vec4(0.0);\n\r\n\t\t\tvec4 sumw = vec4(0.0);\t\n\r\n\t\t\tfor ( int i=0; i Math.abs(b)) { + return c[1]; + } + a = (a - c[0]) / b; + return c[1] * (1.0 - a) + k[1] * a; + } + } + return 0; + } + }, A.prototype.updateCurve = function() { + for (var a = this._values, b = a.length / 4, d = this.properties.split_channels, c = 0; c < b; ++c) { + if (d) { + a[4 * c] = Math.clamp(255 * this.sampleCurve(c / b, this._points.R), 0, 255), a[4 * c + 1] = Math.clamp(255 * this.sampleCurve(c / b, this._points.G), 0, 255), a[4 * c + 2] = Math.clamp(255 * this.sampleCurve(c / b, this._points.B), 0, 255); + } else { + var k = this.sampleCurve(c / b); + a[4 * c] = a[4 * c + 1] = a[4 * c + 2] = Math.clamp(255 * k, 0, 255); + } + a[4 * c + 3] = 255; + } + this._curve_texture || (this._curve_texture = new GL.Texture(256, 1, {format:gl.RGBA, magFilter:gl.LINEAR, wrap:gl.CLAMP_TO_EDGE})); + this._curve_texture.uploadData(a, null, !0); + }, A.prototype.onSerialize = function(a) { + var b = {}, d; + for (d in this._points) { + b[d] = this._points[d].concat(); + } + a.curves = b; + }, A.prototype.onConfigure = function(a) { + this._points = a.curves; + this.curve_editor && (curve_editor.points = this._points); + this._must_update = !0; + }, A.prototype.onMouseDown = function(a, b, d) { + if (this.curve_editor) { + return (a = this.curve_editor.onMouseDown([b[0], b[1] - this.curve_offset], d)) && this.captureInput(!0), a; + } + }, A.prototype.onMouseMove = function(a, b, d) { + if (this.curve_editor) { + return this.curve_editor.onMouseMove([b[0], b[1] - this.curve_offset], d); + } + }, A.prototype.onMouseUp = function(a, b, d) { + if (this.curve_editor) { + return this.curve_editor.onMouseUp([b[0], b[1] - this.curve_offset], d); + } + this.captureInput(!1); + }, A.channel_line_colors = {RGB:"#666", R:"#F33", G:"#3F3", B:"#33F"}, A.prototype.onDrawBackground = function(a, b) { + if (!this.flags.collapsed) { + this.curve_editor || (this.curve_editor = new F.CurveEditor(this._points.R)); + a.save(); + a.translate(0, this.curve_offset); + var d = this.widgets[1].value; + this.properties.split_channels ? ("RGB" == d && (this.widgets[1].value = d = "R", this.widgets[1].disabled = !1), this.curve_editor.points = this._points.R, this.curve_editor.draw(a, [this.size[0], this.size[1] - this.curve_offset], b, "#111", A.channel_line_colors.R, !0), a.globalCompositeOperation = "lighten", this.curve_editor.points = this._points.G, this.curve_editor.draw(a, [this.size[0], this.size[1] - this.curve_offset], b, null, A.channel_line_colors.G, !0), this.curve_editor.points = + this._points.B, this.curve_editor.draw(a, [this.size[0], this.size[1] - this.curve_offset], b, null, A.channel_line_colors.B, !0), a.globalCompositeOperation = "source-over") : (this.widgets[1].value = d = "RGB", this.widgets[1].disabled = !0); + this.curve_editor.points = this._points[d]; + this.curve_editor.draw(a, [this.size[0], this.size[1] - this.curve_offset], b, this.properties.split_channels ? null : "#111", A.channel_line_colors[d]); + a.restore(); + } + }, A.pixel_shader = "precision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_curve;\n\r\n\t\tuniform float u_range;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = texture2D( u_texture, v_coord ) * u_range;\n\r\n\t\t\tcolor.x = texture2D( u_curve, vec2( color.x, 0.5 ) ).x;\n\r\n\t\t\tcolor.y = texture2D( u_curve, vec2( color.y, 0.5 ) ).y;\n\r\n\t\t\tcolor.z = texture2D( u_curve, vec2( color.z, 0.5 ) ).z;\n\r\n\t\t\t//color.w = texture2D( u_curve, vec2( color.w, 0.5 ) ).w;\n\r\n\t\t\tgl_FragColor = color;\n\r\n\t\t}", + F.registerNodeType("texture/curve", A), r.title = "Exposition", r.desc = "Controls texture exposition", r.widgets_info = {exposition:{widget:"slider", min:0, max:3}, precision:{widget:"combo", values:c.MODE_VALUES}}, r.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a && this.isOutputConnected(0)) { + var b = this._temp_texture; + b && b.width == a.width && b.height == a.height && b.type == a.type || (b = this._temp_texture = new GL.Texture(a.width, a.height, {type:a.type, format:gl.RGBA, filter:gl.LINEAR})); + var d = r._shader; + d || (d = r._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, r.pixel_shader)); + var c = this.getInputData(1); + null != c && (this.properties.exposition = c); + var k = this._uniforms; + b.drawTo(function() { + gl.disable(gl.DEPTH_TEST); + a.bind(0); + d.uniforms(k).draw(GL.Mesh.getScreenQuad()); + }); + this.setOutputData(0, b); + } + }, r.pixel_shader = "precision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_exposition;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = texture2D( u_texture, v_coord );\n\r\n\t\t\tgl_FragColor = vec4( color.xyz * u_exposition, color.a );\n\r\n\t\t}", F.registerNodeType("texture/exposition", r), K.title = "Tone Mapping", K.desc = "Applies Tone Mapping to convert from high to low", K.widgets_info = {precision:{widget:"combo", + values:c.MODE_VALUES}}, K.prototype.onGetInputs = function() { + return [["enabled", "boolean"]]; + }, K.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a && this.isOutputConnected(0)) { + if (this.properties.precision === c.PASS_THROUGH || !1 === this.getInputOrProperty("enabled")) { + this.setOutputData(0, a); + } else { + var b = this._temp_texture; + b && b.width == a.width && b.height == a.height && b.type == a.type || (b = this._temp_texture = new GL.Texture(a.width, a.height, {type:a.type, format:gl.RGBA, filter:gl.LINEAR})); + var d = this.getInputData(1); + null == d && (d = this.properties.average_lum); + var k = this._uniforms, f = null; + d.constructor === Number ? (this.properties.average_lum = d, k.u_average_lum = this.properties.average_lum, f = K._shader, f || (f = K._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, K.pixel_shader))) : d.constructor === GL.Texture && (k.u_average_texture = d.bind(1), f = K._shader_texture, f || (f = K._shader_texture = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, K.pixel_shader, {AVG_TEXTURE:""}))); + k.u_lumwhite2 = this.properties.lum_white * this.properties.lum_white; + k.u_scale = this.properties.scale; + k.u_igamma = 1 / this.properties.gamma; + gl.disable(gl.DEPTH_TEST); + b.drawTo(function() { + a.bind(0); + f.uniforms(k).draw(GL.Mesh.getScreenQuad()); + }); + this.setOutputData(0, this._temp_texture); + } + } + }, K.pixel_shader = "precision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_scale;\n\r\n\t\t#ifdef AVG_TEXTURE\n\r\n\t\t\tuniform sampler2D u_average_texture;\n\r\n\t\t#else\n\r\n\t\t\tuniform float u_average_lum;\n\r\n\t\t#endif\n\r\n\t\tuniform float u_lumwhite2;\n\r\n\t\tuniform float u_igamma;\n\r\n\t\tvec3 RGB2xyY (vec3 rgb)\n\r\n\t\t{\n\r\n\t\t\t const mat3 RGB2XYZ = mat3(0.4124, 0.3576, 0.1805,\n\r\n\t\t\t\t\t\t\t\t\t 0.2126, 0.7152, 0.0722,\n\r\n\t\t\t\t\t\t\t\t\t 0.0193, 0.1192, 0.9505);\n\r\n\t\t\tvec3 XYZ = RGB2XYZ * rgb;\n\r\n\t\t\t\n\r\n\t\t\tfloat f = (XYZ.x + XYZ.y + XYZ.z);\n\r\n\t\t\treturn vec3(XYZ.x / f,\n\r\n\t\t\t\t\t\tXYZ.y / f,\n\r\n\t\t\t\t\t\tXYZ.y);\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = texture2D( u_texture, v_coord );\n\r\n\t\t\tvec3 rgb = color.xyz;\n\r\n\t\t\tfloat average_lum = 0.0;\n\r\n\t\t\t#ifdef AVG_TEXTURE\n\r\n\t\t\t\tvec3 pixel = texture2D(u_average_texture,vec2(0.5)).xyz;\n\r\n\t\t\t\taverage_lum = (pixel.x + pixel.y + pixel.z) / 3.0;\n\r\n\t\t\t#else\n\r\n\t\t\t\taverage_lum = u_average_lum;\n\r\n\t\t\t#endif\n\r\n\t\t\t//Ld - this part of the code is the same for both versions\n\r\n\t\t\tfloat lum = dot(rgb, vec3(0.2126, 0.7152, 0.0722));\n\r\n\t\t\tfloat L = (u_scale / average_lum) * lum;\n\r\n\t\t\tfloat Ld = (L * (1.0 + L / u_lumwhite2)) / (1.0 + L);\n\r\n\t\t\t//first\n\r\n\t\t\t//vec3 xyY = RGB2xyY(rgb);\n\r\n\t\t\t//xyY.z *= Ld;\n\r\n\t\t\t//rgb = xyYtoRGB(xyY);\n\r\n\t\t\t//second\n\r\n\t\t\trgb = (rgb / lum) * Ld;\n\r\n\t\t\trgb = max(rgb,vec3(0.001));\n\r\n\t\t\trgb = pow( rgb, vec3( u_igamma ) );\n\r\n\t\t\tgl_FragColor = vec4( rgb, color.a );\n\r\n\t\t}", + F.registerNodeType("texture/tonemapping", K), N.title = "Perlin", N.desc = "Generates a perlin noise texture", N.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}, width:{type:"Number", precision:0, step:1}, height:{type:"Number", precision:0, step:1}, octaves:{type:"Number", precision:0, step:1, min:1, max:50}}, N.prototype.onGetInputs = function() { + return [["seed", "Number"], ["persistence", "Number"], ["octaves", "Number"], ["scale", "Number"], ["amplitude", "Number"], ["offset", "vec2"]]; + }, N.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var a = this.properties.width | 0, b = this.properties.height | 0; + 0 == a && (a = gl.viewport_data[2]); + 0 == b && (b = gl.viewport_data[3]); + var d = c.getTextureType(this.properties.precision), k = this._texture; + k && k.width == a && k.height == b && k.type == d || (k = this._texture = new GL.Texture(a, b, {type:d, format:gl.RGB, filter:gl.LINEAR})); + var f = this.getInputOrProperty("persistence"), h = this.getInputOrProperty("octaves"), e = this.getInputOrProperty("offset"), n = this.getInputOrProperty("scale"), g = this.getInputOrProperty("amplitude"), q = this.getInputOrProperty("seed"); + d = "" + a + b + d + f + h + n + q + e[0] + e[1] + g; + if (d != this._key) { + this._key = d; + var l = this._uniforms; + l.u_persistence = f; + l.u_octaves = h; + l.u_offset.set(e); + l.u_scale = n; + l.u_amplitude = g; + l.u_seed = 128 * q; + l.u_viewport[0] = a; + l.u_viewport[1] = b; + var m = N._shader; + m || (m = N._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, N.pixel_shader)); + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + k.drawTo(function() { + m.uniforms(l).draw(GL.Mesh.getScreenQuad()); + }); + } + this.setOutputData(0, k); + } + }, N.pixel_shader = "precision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform vec2 u_offset;\n\r\n\t\tuniform float u_scale;\n\r\n\t\tuniform float u_persistence;\n\r\n\t\tuniform int u_octaves;\n\r\n\t\tuniform float u_amplitude;\n\r\n\t\tuniform vec2 u_viewport;\n\r\n\t\tuniform float u_seed;\n\r\n\t\t#define M_PI 3.14159265358979323846\n\r\n\t\t\n\r\n\t\tfloat rand(vec2 c){\treturn fract(sin(dot(c.xy ,vec2( 12.9898 + u_seed,78.233 + u_seed))) * 43758.5453); }\n\r\n\t\t\n\r\n\t\tfloat noise(vec2 p, float freq ){\n\r\n\t\t\tfloat unit = u_viewport.x/freq;\n\r\n\t\t\tvec2 ij = floor(p/unit);\n\r\n\t\t\tvec2 xy = mod(p,unit)/unit;\n\r\n\t\t\t//xy = 3.*xy*xy-2.*xy*xy*xy;\n\r\n\t\t\txy = .5*(1.-cos(M_PI*xy));\n\r\n\t\t\tfloat a = rand((ij+vec2(0.,0.)));\n\r\n\t\t\tfloat b = rand((ij+vec2(1.,0.)));\n\r\n\t\t\tfloat c = rand((ij+vec2(0.,1.)));\n\r\n\t\t\tfloat d = rand((ij+vec2(1.,1.)));\n\r\n\t\t\tfloat x1 = mix(a, b, xy.x);\n\r\n\t\t\tfloat x2 = mix(c, d, xy.x);\n\r\n\t\t\treturn mix(x1, x2, xy.y);\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tfloat pNoise(vec2 p, int res){\n\r\n\t\t\tfloat persistance = u_persistence;\n\r\n\t\t\tfloat n = 0.;\n\r\n\t\t\tfloat normK = 0.;\n\r\n\t\t\tfloat f = 4.;\n\r\n\t\t\tfloat amp = 1.0;\n\r\n\t\t\tint iCount = 0;\n\r\n\t\t\tfor (int i = 0; i<50; i++){\n\r\n\t\t\t\tn+=amp*noise(p, f);\n\r\n\t\t\t\tf*=2.;\n\r\n\t\t\t\tnormK+=amp;\n\r\n\t\t\t\tamp*=persistance;\n\r\n\t\t\t\tif (iCount >= res)\n\r\n\t\t\t\t\tbreak;\n\r\n\t\t\t\tiCount++;\n\r\n\t\t\t}\n\r\n\t\t\tfloat nf = n/normK;\n\r\n\t\t\treturn nf*nf*nf*nf;\n\r\n\t\t}\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec2 uv = v_coord * u_scale * u_viewport + u_offset * u_scale;\n\r\n\t\t\tvec4 color = vec4( pNoise( uv, u_octaves ) * u_amplitude );\n\r\n\t\t\tgl_FragColor = color;\n\r\n\t\t}", + F.registerNodeType("texture/perlin", N), M.title = "Canvas2D", M.desc = "Executes Canvas2D code inside a texture or the viewport.", M.help = "Set width and height to 0 to match viewport size.", M.default_code = "//vars: canvas,ctx,time\nctx.fillStyle='red';\nctx.fillRect(0,0,50,50);\n", M.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}, code:{type:"code"}, width:{type:"Number", precision:0, step:1}, height:{type:"Number", precision:0, step:1}}, M.prototype.onPropertyChanged = function(a, + b) { + "code" == a && this.compileCode(b); + }, M.prototype.compileCode = function(a) { + this._func = null; + if (F.allow_scripts) { + try { + this._func = new Function("canvas", "ctx", "time", "script", "v", a), this.boxcolor = "#00FF00"; + } catch (R) { + this.boxcolor = "#FF0000", console.error("Error parsing script"), console.error(R); + } + } + }, M.prototype.onExecute = function() { + var a = this._func; + a && this.isOutputConnected(0) && this.executeDraw(a); + }, M.prototype.executeDraw = function(a) { + var b = this.properties.width || gl.canvas.width, d = this.properties.height || gl.canvas.height, k = this._temp_texture, f = c.getTextureType(this.properties.precision); + k && k.width == b && k.height == d && k.type == f || (k = this._temp_texture = new GL.Texture(b, d, {format:gl.RGBA, filter:gl.LINEAR, type:f})); + var h = this.getInputData(0), e = this.properties, n = this, g = this.graph.getTime(), q = gl, l = gl.canvas; + if (this.properties.use_html_canvas || !w.enableWebGLCanvas) { + this._canvas ? (l = this._canvas, q = this._ctx) : (l = this._canvas = createCanvas(b.height), q = this._ctx = l.getContext("2d")), l.width = b, l.height = d; + } + if (q == gl) { + k.drawTo(function() { + gl.start2D(); + e.clear && (gl.clearColor(0, 0, 0, 0), gl.clear(gl.COLOR_BUFFER_BIT)); + try { + a.draw ? a.draw.call(n, l, q, g, a, h) : a.call(n, l, q, g, a, h), n.boxcolor = "#00FF00"; + } catch (Q) { + n.boxcolor = "#FF0000", console.error("Error executing script"), console.error(Q); + } + gl.finish2D(); + }); + } else { + e.clear && q.clearRect(0, 0, l.width, l.height); + try { + a.draw ? a.draw.call(this, l, q, g, a, h) : a.call(this, l, q, g, a, h), this.boxcolor = "#00FF00"; + } catch (Q) { + this.boxcolor = "#FF0000", console.error("Error executing script"), console.error(Q); + } + k.uploadImage(l); + } + this.setOutputData(0, k); + }, F.registerNodeType("texture/canvas2D", M), O.title = "Matte", O.desc = "Extracts background", O.widgets_info = {key_color:{widget:"color"}, precision:{widget:"combo", values:c.MODE_VALUES}}, O.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var a = this.getInputData(0); + if (this.properties.precision === c.PASS_THROUGH) { + this.setOutputData(0, a); + } else { + if (a) { + this._tex = c.getTargetTexture(a, this._tex, this.properties.precision); + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + this._uniforms || (this._uniforms = {u_texture:0, u_key_color:this.properties.key_color, u_threshold:1, u_slope:1}); + var b = this._uniforms, d = Mesh.getScreenQuad(), k = O._shader; + k || (k = O._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, O.pixel_shader)); + b.u_key_color = this.properties.key_color; + b.u_threshold = this.properties.threshold; + b.u_slope = this.properties.slope; + this._tex.drawTo(function() { + a.bind(0); + k.uniforms(b).draw(d); + }); + this.setOutputData(0, this._tex); + } + } + } + }, O.pixel_shader = "precision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec3 u_key_color;\n\r\n\t\tuniform float u_threshold;\n\r\n\t\tuniform float u_slope;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec3 color = texture2D( u_texture, v_coord ).xyz;\n\r\n\t\t\tfloat diff = length( normalize(color) - normalize(u_key_color) );\n\r\n\t\t\tfloat edge = u_threshold * (1.0 - u_slope);\n\r\n\t\t\tfloat alpha = smoothstep( edge, u_threshold, diff);\n\r\n\t\t\tgl_FragColor = vec4( color, alpha );\n\r\n\t\t}", + F.registerNodeType("texture/matte", O), P.title = "CubemapToTexture2D", P.desc = "Transforms a CUBEMAP texture into a TEXTURE2D in Polar Representation", P.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var a = this.getInputData(0); + if (a && a.texture_type == GL.TEXTURE_CUBE_MAP) { + !this._last_tex || this._last_tex.height == a.height && this._last_tex.type == a.type || (this._last_tex = null); + var b = this.getInputOrProperty("yaw"); + this._last_tex = GL.Texture.cubemapToTexture2D(a, a.height, this._last_tex, !0, b); + this.setOutputData(0, this._last_tex); + } + } + }, F.registerNodeType("texture/cubemapToTexture2D", P)); +})(this); +(function(w) { + var c = w.LiteGraph; + if ("undefined" != typeof GL) { + var p = function() { + this.addInput("Tex.", "Texture"); + this.addInput("intensity", "number"); + this.addOutput("Texture", "Texture"); + this.properties = {intensity:1, invert:!1, precision:LGraphTexture.DEFAULT}; + p._shader || (p._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, p.pixel_shader)); + }, m = function() { + this.addInput("Texture", "Texture"); + this.addInput("value1", "number"); + this.addInput("value2", "number"); + this.addOutput("Texture", "Texture"); + this.properties = {fx:"halftone", value1:1, value2:1, precision:LGraphTexture.DEFAULT}; + }, g = function() { + this.addInput("Texture", "Texture"); + this.addInput("Blurred", "Texture"); + this.addInput("Mask", "Texture"); + this.addInput("Threshold", "number"); + this.addOutput("Texture", "Texture"); + this.properties = {shape:"", size:10, alpha:1.0, threshold:1.0, high_precision:!1}; + }, u = function() { + this.addInput("Texture", "Texture"); + this.addInput("Aberration", "number"); + this.addInput("Distortion", "number"); + this.addInput("Blur", "number"); + this.addOutput("Texture", "Texture"); + this.properties = {aberration:1.0, distortion:1.0, blur:1.0, precision:LGraphTexture.DEFAULT}; + u._shader || (u._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, u.pixel_shader), u._texture = new GL.Texture(3, 1, {format:gl.RGB, wrap:gl.CLAMP_TO_EDGE, magFilter:gl.LINEAR, minFilter:gl.LINEAR, pixel_data:[255, 0, 0, 0, 255, 0, 0, 0, 255]})); + }; + u.title = "Lens"; + u.desc = "Camera Lens distortion"; + u.widgets_info = {precision:{widget:"combo", values:LGraphTexture.MODE_VALUES}}; + u.prototype.onExecute = function() { + var c = this.getInputData(0); + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, c); + } else { + if (c) { + this._tex = LGraphTexture.getTargetTexture(c, this._tex, this.properties.precision); + var g = this.properties.aberration; + this.isInputConnected(1) && (g = this.getInputData(1), this.properties.aberration = g); + var m = this.properties.distortion; + this.isInputConnected(2) && (m = this.getInputData(2), this.properties.distortion = m); + var p = this.properties.blur; + this.isInputConnected(3) && (p = this.getInputData(3), this.properties.blur = p); + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var w = Mesh.getScreenQuad(), z = u._shader; + this._tex.drawTo(function() { + c.bind(0); + z.uniforms({u_texture:0, u_aberration:g, u_distortion:m, u_blur:p}).draw(w); + }); + this.setOutputData(0, this._tex); + } + } + }; + u.pixel_shader = "precision highp float;\n\r\n\t\t\tprecision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform vec2 u_camera_planes;\n\r\n\t\t\tuniform float u_aberration;\n\r\n\t\t\tuniform float u_distortion;\n\r\n\t\t\tuniform float u_blur;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec2 coord = v_coord;\n\r\n\t\t\t\tfloat dist = distance(vec2(0.5), coord);\n\r\n\t\t\t\tvec2 dist_coord = coord - vec2(0.5);\n\r\n\t\t\t\tfloat percent = 1.0 + ((0.5 - dist) / 0.5) * u_distortion;\n\r\n\t\t\t\tdist_coord *= percent;\n\r\n\t\t\t\tcoord = dist_coord + vec2(0.5);\n\r\n\t\t\t\tvec4 color = texture2D(u_texture,coord, u_blur * dist);\n\r\n\t\t\t\tcolor.r = texture2D(u_texture,vec2(0.5) + dist_coord * (1.0+0.01*u_aberration), u_blur * dist ).r;\n\r\n\t\t\t\tcolor.b = texture2D(u_texture,vec2(0.5) + dist_coord * (1.0-0.01*u_aberration), u_blur * dist ).b;\n\r\n\t\t\t\tgl_FragColor = color;\n\r\n\t\t\t}\n\r\n\t\t\t"; + c.registerNodeType("fx/lens", u); + w.LGraphFXLens = u; + g.title = "Bokeh"; + g.desc = "applies an Bokeh effect"; + g.widgets_info = {shape:{widget:"texture"}}; + g.prototype.onExecute = function() { + var c = this.getInputData(0), m = this.getInputData(1), p = this.getInputData(2); + if (c && p && this.properties.shape) { + m || (m = c); + var u = LGraphTexture.getTexture(this.properties.shape); + if (u) { + var w = this.properties.threshold; + this.isInputConnected(3) && (w = this.getInputData(3), this.properties.threshold = w); + var z = gl.UNSIGNED_BYTE; + this.properties.high_precision && (z = gl.half_float_ext ? gl.HALF_FLOAT_OES : gl.FLOAT); + this._temp_texture && this._temp_texture.type == z && this._temp_texture.width == c.width && this._temp_texture.height == c.height || (this._temp_texture = new GL.Texture(c.width, c.height, {type:z, format:gl.RGBA, filter:gl.LINEAR})); + var e = g._first_shader; + e || (e = g._first_shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, g._first_pixel_shader)); + var C = g._second_shader; + C || (C = g._second_shader = new GL.Shader(g._second_vertex_shader, g._second_pixel_shader)); + var D = this._points_mesh; + D && D._width == c.width && D._height == c.height && 2 == D._spacing || (D = this.createPointsMesh(c.width, c.height, 2)); + var t = Mesh.getScreenQuad(), G = this.properties.size, n = this.properties.alpha; + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.BLEND); + this._temp_texture.drawTo(function() { + c.bind(0); + m.bind(1); + p.bind(2); + e.uniforms({u_texture:0, u_texture_blur:1, u_mask:2, u_texsize:[c.width, c.height]}).draw(t); + }); + this._temp_texture.drawTo(function() { + gl.enable(gl.BLEND); + gl.blendFunc(gl.ONE, gl.ONE); + c.bind(0); + u.bind(3); + C.uniforms({u_texture:0, u_mask:2, u_shape:3, u_alpha:n, u_threshold:w, u_pointSize:G, u_itexsize:[1.0 / c.width, 1.0 / c.height]}).draw(D, gl.POINTS); + }); + this.setOutputData(0, this._temp_texture); + } + } else { + this.setOutputData(0, c); + } + }; + g.prototype.createPointsMesh = function(c, g, m) { + for (var l = Math.round(c / m), p = Math.round(g / m), u = new Float32Array(l * p * 2), e = -1, w = 2 / c * m, y = 2 / g * m, t = 0; t < p; ++t) { + for (var B = -1, n = 0; n < l; ++n) { + var q = t * l * 2 + 2 * n; + u[q] = B; + u[q + 1] = e; + B += w; + } + e += y; + } + this._points_mesh = GL.Mesh.load({vertices2D:u}); + this._points_mesh._width = c; + this._points_mesh._height = g; + this._points_mesh._spacing = m; + return this._points_mesh; + }; + g._first_pixel_shader = "precision highp float;\n\r\n\t\t\tprecision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform sampler2D u_texture_blur;\n\r\n\t\t\tuniform sampler2D u_mask;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, v_coord);\n\r\n\t\t\t\tvec4 blurred_color = texture2D(u_texture_blur, v_coord);\n\r\n\t\t\t\tfloat mask = texture2D(u_mask, v_coord).x;\n\r\n\t\t\t gl_FragColor = mix(color, blurred_color, mask);\n\r\n\t\t\t}\n\r\n\t\t\t"; + g._second_vertex_shader = "precision highp float;\n\r\n\t\t\tattribute vec2 a_vertex2D;\n\r\n\t\t\tvarying vec4 v_color;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform sampler2D u_mask;\n\r\n\t\t\tuniform vec2 u_itexsize;\n\r\n\t\t\tuniform float u_pointSize;\n\r\n\t\t\tuniform float u_threshold;\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec2 coord = a_vertex2D * 0.5 + 0.5;\n\r\n\t\t\t\tv_color = texture2D( u_texture, coord );\n\r\n\t\t\t\tv_color += texture2D( u_texture, coord + vec2(u_itexsize.x, 0.0) );\n\r\n\t\t\t\tv_color += texture2D( u_texture, coord + vec2(0.0, u_itexsize.y));\n\r\n\t\t\t\tv_color += texture2D( u_texture, coord + u_itexsize);\n\r\n\t\t\t\tv_color *= 0.25;\n\r\n\t\t\t\tfloat mask = texture2D(u_mask, coord).x;\n\r\n\t\t\t\tfloat luminance = length(v_color) * mask;\n\r\n\t\t\t\t/*luminance /= (u_pointSize*u_pointSize)*0.01 */;\n\r\n\t\t\t\tluminance -= u_threshold;\n\r\n\t\t\t\tif(luminance < 0.0)\n\r\n\t\t\t\t{\n\r\n\t\t\t\t\tgl_Position.x = -100.0;\n\r\n\t\t\t\t\treturn;\n\r\n\t\t\t\t}\n\r\n\t\t\t\tgl_PointSize = u_pointSize;\n\r\n\t\t\t\tgl_Position = vec4(a_vertex2D,0.0,1.0);\n\r\n\t\t\t}\n\r\n\t\t\t"; + g._second_pixel_shader = "precision highp float;\n\r\n\t\t\tvarying vec4 v_color;\n\r\n\t\t\tuniform sampler2D u_shape;\n\r\n\t\t\tuniform float u_alpha;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec4 color = texture2D( u_shape, gl_PointCoord );\n\r\n\t\t\t\tcolor *= v_color * u_alpha;\n\r\n\t\t\t\tgl_FragColor = color;\n\r\n\t\t\t}\n"; + c.registerNodeType("fx/bokeh", g); + w.LGraphFXBokeh = g; + m.title = "FX"; + m.desc = "applies an FX from a list"; + m.widgets_info = {fx:{widget:"combo", values:["halftone", "pixelate", "lowpalette", "noise", "gamma"]}, precision:{widget:"combo", values:LGraphTexture.MODE_VALUES}}; + m.shaders = {}; + m.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var c = this.getInputData(0); + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, c); + } else { + if (c) { + this._tex = LGraphTexture.getTargetTexture(c, this._tex, this.properties.precision); + var g = this.properties.value1; + this.isInputConnected(1) && (g = this.getInputData(1), this.properties.value1 = g); + var p = this.properties.value2; + this.isInputConnected(2) && (p = this.getInputData(2), this.properties.value2 = p); + var u = this.properties.fx, E = m.shaders[u]; + if (!E) { + var z = m["pixel_shader_" + u]; + if (!z) { + return; + } + E = m.shaders[u] = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, z); + } + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var e = Mesh.getScreenQuad(); + var C = w.LS && LS.Renderer._current_camera ? [LS.Renderer._current_camera.near, LS.Renderer._current_camera.far] : [1, 100]; + var D = null; + "noise" == u && (D = LGraphTexture.getNoiseTexture()); + this._tex.drawTo(function() { + c.bind(0); + "noise" == u && D.bind(1); + E.uniforms({u_texture:0, u_noise:1, u_size:[c.width, c.height], u_rand:[Math.random(), Math.random()], u_value1:g, u_value2:p, u_camera_planes:C}).draw(e); + }); + this.setOutputData(0, this._tex); + } + } + } + }; + m.pixel_shader_halftone = "precision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform vec2 u_camera_planes;\n\r\n\t\t\tuniform vec2 u_size;\n\r\n\t\t\tuniform float u_value1;\n\r\n\t\t\tuniform float u_value2;\n\r\n\t\t\t\n\r\n\t\t\tfloat pattern() {\n\r\n\t\t\t\tfloat s = sin(u_value1 * 3.1415), c = cos(u_value1 * 3.1415);\n\r\n\t\t\t\tvec2 tex = v_coord * u_size.xy;\n\r\n\t\t\t\tvec2 point = vec2(\n\r\n\t\t\t\t c * tex.x - s * tex.y ,\n\r\n\t\t\t\t s * tex.x + c * tex.y \n\r\n\t\t\t\t) * u_value2;\n\r\n\t\t\t\treturn (sin(point.x) * sin(point.y)) * 4.0;\n\r\n\t\t\t}\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, v_coord);\n\r\n\t\t\t\tfloat average = (color.r + color.g + color.b) / 3.0;\n\r\n\t\t\t\tgl_FragColor = vec4(vec3(average * 10.0 - 5.0 + pattern()), color.a);\n\r\n\t\t\t}\n"; + m.pixel_shader_pixelate = "precision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform vec2 u_camera_planes;\n\r\n\t\t\tuniform vec2 u_size;\n\r\n\t\t\tuniform float u_value1;\n\r\n\t\t\tuniform float u_value2;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec2 coord = vec2( floor(v_coord.x * u_value1) / u_value1, floor(v_coord.y * u_value2) / u_value2 );\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, coord);\n\r\n\t\t\t\tgl_FragColor = color;\n\r\n\t\t\t}\n"; + m.pixel_shader_lowpalette = "precision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform vec2 u_camera_planes;\n\r\n\t\t\tuniform vec2 u_size;\n\r\n\t\t\tuniform float u_value1;\n\r\n\t\t\tuniform float u_value2;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, v_coord);\n\r\n\t\t\t\tgl_FragColor = floor(color * u_value1) / u_value1;\n\r\n\t\t\t}\n"; + m.pixel_shader_noise = "precision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform sampler2D u_noise;\n\r\n\t\t\tuniform vec2 u_size;\n\r\n\t\t\tuniform float u_value1;\n\r\n\t\t\tuniform float u_value2;\n\r\n\t\t\tuniform vec2 u_rand;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, v_coord);\n\r\n\t\t\t\tvec3 noise = texture2D(u_noise, v_coord * vec2(u_size.x / 512.0, u_size.y / 512.0) + u_rand).xyz - vec3(0.5);\n\r\n\t\t\t\tgl_FragColor = vec4( color.xyz + noise * u_value1, color.a );\n\r\n\t\t\t}\n"; + m.pixel_shader_gamma = "precision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform float u_value1;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, v_coord);\n\r\n\t\t\t\tfloat gamma = 1.0 / u_value1;\n\r\n\t\t\t\tgl_FragColor = vec4( pow( color.xyz, vec3(gamma) ), color.a );\n\r\n\t\t\t}\n"; + c.registerNodeType("fx/generic", m); + w.LGraphFXGeneric = m; + p.title = "Vigneting"; + p.desc = "Vigneting"; + p.widgets_info = {precision:{widget:"combo", values:LGraphTexture.MODE_VALUES}}; + p.prototype.onExecute = function() { + var c = this.getInputData(0); + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, c); + } else { + if (c) { + this._tex = LGraphTexture.getTargetTexture(c, this._tex, this.properties.precision); + var g = this.properties.intensity; + this.isInputConnected(1) && (g = this.getInputData(1), this.properties.intensity = g); + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var m = Mesh.getScreenQuad(), u = p._shader, w = this.properties.invert; + this._tex.drawTo(function() { + c.bind(0); + u.uniforms({u_texture:0, u_intensity:g, u_isize:[1 / c.width, 1 / c.height], u_invert:w ? 1 : 0}).draw(m); + }); + this.setOutputData(0, this._tex); + } + } + }; + p.pixel_shader = "precision highp float;\n\r\n\t\t\tprecision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform float u_intensity;\n\r\n\t\t\tuniform int u_invert;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tfloat luminance = 1.0 - length( v_coord - vec2(0.5) ) * 1.414;\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, v_coord);\n\r\n\t\t\t\tif(u_invert == 1)\n\r\n\t\t\t\t\tluminance = 1.0 - luminance;\n\r\n\t\t\t\tluminance = mix(1.0, luminance, u_intensity);\n\r\n\t\t\t gl_FragColor = vec4( luminance * color.xyz, color.a);\n\r\n\t\t\t}\n\r\n\t\t\t"; + c.registerNodeType("fx/vigneting", p); + w.LGraphFXVigneting = p; + } +})(this); +(function(w) { + function c(c) { + this.cmd = this.channel = 0; + this.data = new Uint32Array(3); + c && this.setup(c); + } + function p(c, e) { + navigator.requestMIDIAccess ? (this.on_ready = c, this.state = {note:[], cc:[]}, this.input_ports = null, this.input_ports_info = [], this.output_ports = null, this.output_ports_info = [], navigator.requestMIDIAccess().then(this.onMIDISuccess.bind(this), this.onMIDIFailure.bind(this))) : (this.error = "not suppoorted", e ? e("Not supported") : console.error("MIDI NOT SUPPORTED, enable by chrome://flags")); + } + function m() { + this.addOutput("on_midi", t.EVENT); + this.addOutput("out", "midi"); + this.properties = {port:0}; + this._current_midi_event = this._last_midi_event = null; + this.boxcolor = "#AAA"; + this._last_time = 0; + var c = this; + new p(function(e) { + c._midi = e; + if (c._waiting) { + c.onStart(); + } + c._waiting = !1; + }); + } + function g() { + this.addInput("send", t.EVENT); + this.properties = {port:0}; + var c = this; + new p(function(e) { + c._midi = e; + c.widget.options.values = c.getMIDIOutputs(); + }); + this.widget = this.addWidget("combo", "Device", this.properties.port, {property:"port", values:this.getMIDIOutputs.bind(this)}); + this.size = [340, 60]; + } + function u() { + this.addInput("on_midi", t.EVENT); + this._str = ""; + this.size = [200, 40]; + } + function l() { + this.properties = {channel:-1, cmd:-1, min_value:-1, max_value:-1}; + var c = this; + this._learning = !1; + this.addWidget("button", "Learn", "", function() { + c._learning = !0; + c.boxcolor = "#FA3"; + }); + this.addInput("in", t.EVENT); + this.addOutput("on_midi", t.EVENT); + this.boxcolor = "#AAA"; + } + function B() { + this.properties = {channel:0, cmd:144, value1:1, value2:1}; + this.addInput("send", t.EVENT); + this.addInput("assign", t.EVENT); + this.addOutput("on_midi", t.EVENT); + this.midi_event = new c; + this.gate = !1; + } + function y() { + this.properties = {cc:1, value:0}; + this.addOutput("value", "number"); + } + function v() { + this.addInput("generate", t.ACTION); + this.addInput("scale", "string"); + this.addInput("octave", "number"); + this.addOutput("note", t.EVENT); + this.properties = {notes:"A,A#,B,C,C#,D,D#,E,F,F#,G,G#", octave:2, duration:0.5, mode:"sequence"}; + this.notes_pitches = v.processScale(this.properties.notes); + this.sequence_index = 0; + } + function E() { + this.properties = {amount:0}; + this.addInput("in", t.ACTION); + this.addInput("amount", "number"); + this.addOutput("out", t.EVENT); + this.midi_event = new c; + } + function z() { + this.properties = {scale:"A,A#,B,C,C#,D,D#,E,F,F#,G,G#"}; + this.addInput("note", t.ACTION); + this.addInput("scale", "string"); + this.addOutput("out", t.EVENT); + this.valid_notes = Array(12); + this.offset_notes = Array(12); + this.processScale(this.properties.scale); + } + function e() { + this.properties = {url:"", autoplay:!0}; + this.addInput("play", t.ACTION); + this.addInput("pause", t.ACTION); + this.addOutput("note", t.EVENT); + this._midi = null; + this._current_time = 0; + this._playing = !1; + "undefined" == typeof MidiParser && (console.error("midi-parser.js not included, LGMidiPlay requires that library: https://raw.githubusercontent.com/colxi/midi-parser-js/master/src/main.js"), this.boxcolor = "red"); + } + function C() { + this.properties = {volume:0.5, duration:1}; + this.addInput("note", t.ACTION); + this.addInput("volume", "number"); + this.addInput("duration", "number"); + this.addOutput("note", t.EVENT); + "undefined" == typeof AudioSynth ? (console.error("Audiosynth.js not included, LGMidiPlay requires that library"), this.boxcolor = "red") : this.instrument = (this.synth = new AudioSynth).createInstrument("piano"); + } + function D() { + this.properties = {num_octaves:2, start_octave:2}; + this.addInput("note", t.ACTION); + this.addInput("reset", t.ACTION); + this.addOutput("note", t.EVENT); + this.size = [400, 100]; + this.keys = []; + this._last_key = -1; + } + var t = w.LiteGraph; + t.MIDIEvent = c; + c.prototype.fromJSON = function(c) { + this.setup(c.data); + }; + c.prototype.setup = function(e) { + var n = e; + e.constructor === Object && (n = e.data); + this.data.set(n); + this.status = e = n[0]; + n = e & 240; + this.cmd = 240 <= e ? e : n; + this.cmd == c.NOTEON && 0 == this.velocity && (this.cmd = c.NOTEOFF); + this.cmd_str = c.commands[this.cmd] || ""; + if (n >= c.NOTEON || n <= c.NOTEOFF) { + this.channel = e & 15; + } + }; + Object.defineProperty(c.prototype, "velocity", {get:function() { + return this.cmd == c.NOTEON ? this.data[2] : -1; + }, set:function(c) { + this.data[2] = c; + }, enumerable:!0}); + c.notes = "A A# B C C# D D# E F F# G G#".split(" "); + c.note_to_index = {A:0, "A#":1, B:2, C:3, "C#":4, D:5, "D#":6, E:7, F:8, "F#":9, G:10, "G#":11}; + Object.defineProperty(c.prototype, "note", {get:function() { + return this.cmd != c.NOTEON ? -1 : c.toNoteString(this.data[1], !0); + }, set:function(c) { + throw "notes cannot be assigned this way, must modify the data[1]"; + }, enumerable:!0}); + Object.defineProperty(c.prototype, "octave", {get:function() { + return this.cmd != c.NOTEON ? -1 : Math.floor((this.data[1] - 24) / 12 + 1); + }, set:function(c) { + throw "octave cannot be assigned this way, must modify the data[1]"; + }, enumerable:!0}); + c.prototype.getPitch = function() { + return 440 * Math.pow(2, (this.data[1] - 69) / 12); + }; + c.computePitch = function(c) { + return 440 * Math.pow(2, (c - 69) / 12); + }; + c.prototype.getCC = function() { + return this.data[1]; + }; + c.prototype.getCCValue = function() { + return this.data[2]; + }; + c.prototype.getPitchBend = function() { + return this.data[1] + (this.data[2] << 7) - 8192; + }; + c.computePitchBend = function(c, e) { + return c + (e << 7) - 8192; + }; + c.prototype.setCommandFromString = function(e) { + this.cmd = c.computeCommandFromString(e); + }; + c.computeCommandFromString = function(e) { + if (!e) { + return 0; + } + if (e && e.constructor === Number) { + return e; + } + e = e.toUpperCase(); + switch(e) { + case "NOTE ON": + case "NOTEON": + return c.NOTEON; + case "NOTE OFF": + case "NOTEOFF": + return c.NOTEON; + case "KEY PRESSURE": + case "KEYPRESSURE": + return c.KEYPRESSURE; + case "CONTROLLER CHANGE": + case "CONTROLLERCHANGE": + case "CC": + return c.CONTROLLERCHANGE; + case "PROGRAM CHANGE": + case "PROGRAMCHANGE": + case "PC": + return c.PROGRAMCHANGE; + case "CHANNEL PRESSURE": + case "CHANNELPRESSURE": + return c.CHANNELPRESSURE; + case "PITCH BEND": + case "PITCHBEND": + return c.PITCHBEND; + case "TIME TICK": + case "TIMETICK": + return c.TIMETICK; + default: + return Number(e); + } + }; + c.toNoteString = function(e, g) { + e = Math.round(e); + var k = Math.floor((e - 24) / 12 + 1); + e = (e - 21) % 12; + 0 > e && (e = 12 + e); + return c.notes[e] + (g ? "" : k); + }; + c.NoteStringToPitch = function(e) { + e = e.toUpperCase(); + var n = e[0], k = 4; + "#" == e[1] ? (n += "#", 2 < e.length && (k = Number(e[2]))) : 1 < e.length && (k = Number(e[1])); + e = c.note_to_index[n]; + return null == e ? null : 12 * (k - 1) + e + 21; + }; + c.prototype.toString = function() { + var e = "" + this.channel + ". "; + switch(this.cmd) { + case c.NOTEON: + e += "NOTEON " + c.toNoteString(this.data[1]); + break; + case c.NOTEOFF: + e += "NOTEOFF " + c.toNoteString(this.data[1]); + break; + case c.CONTROLLERCHANGE: + e += "CC " + this.data[1] + " " + this.data[2]; + break; + case c.PROGRAMCHANGE: + e += "PC " + this.data[1]; + break; + case c.PITCHBEND: + e += "PITCHBEND " + this.getPitchBend(); + break; + case c.KEYPRESSURE: + e += "KEYPRESS " + this.data[1]; + } + return e; + }; + c.prototype.toHexString = function() { + for (var c = "", e = 0; e < this.data.length; e++) { + c += this.data[e].toString(16) + " "; + } + }; + c.prototype.toJSON = function() { + return {data:[this.data[0], this.data[1], this.data[2]], object_class:"MIDIEvent"}; + }; + c.NOTEOFF = 128; + c.NOTEON = 144; + c.KEYPRESSURE = 160; + c.CONTROLLERCHANGE = 176; + c.PROGRAMCHANGE = 192; + c.CHANNELPRESSURE = 208; + c.PITCHBEND = 224; + c.TIMETICK = 248; + c.commands = {128:"note off", 144:"note on", 160:"key pressure", 176:"controller change", 192:"program change", 208:"channel pressure", 224:"pitch bend", 240:"system", 242:"Song pos", 243:"Song select", 246:"Tune request", 248:"time tick", 250:"Start Song", 251:"Continue Song", 252:"Stop Song", 254:"Sensing", 255:"Reset"}; + c.commands_short = {128:"NOTEOFF", 144:"NOTEOFF", 160:"KEYP", 176:"CC", 192:"PC", 208:"CP", 224:"PB", 240:"SYS", 242:"POS", 243:"SELECT", 246:"TUNEREQ", 248:"TT", 250:"START", 251:"CONTINUE", 252:"STOP", 254:"SENS", 255:"RESET"}; + c.commands_reversed = {}; + for (var G in c.commands) { + c.commands_reversed[c.commands[G]] = G; + } + p.input = null; + p.MIDIEvent = c; + p.prototype.onMIDISuccess = function(c) { + console.log("MIDI ready!"); + console.log(c); + this.midi = c; + this.updatePorts(); + if (this.on_ready) { + this.on_ready(this); + } + }; + p.prototype.updatePorts = function() { + var c = this.midi; + this.input_ports = c.inputs; + this.input_ports_info = []; + this.output_ports = c.outputs; + this.output_ports_info = []; + c = 0; + for (var e = this.input_ports.values(), k = e.next(); k && !1 === k.done;) { + k = k.value, this.input_ports_info.push(k), console.log("Input port [type:'" + k.type + "'] id:'" + k.id + "' manufacturer:'" + k.manufacturer + "' name:'" + k.name + "' version:'" + k.version + "'"), c++, k = e.next(); + } + this.num_input_ports = c; + c = 0; + e = this.output_ports.values(); + for (k = e.next(); k && !1 === k.done;) { + k = k.value, this.output_ports_info.push(k), console.log("Output port [type:'" + k.type + "'] id:'" + k.id + "' manufacturer:'" + k.manufacturer + "' name:'" + k.name + "' version:'" + k.version + "'"), c++, k = e.next(); + } + this.num_output_ports = c; + }; + p.prototype.onMIDIFailure = function(c) { + console.error("Failed to get MIDI access - " + c); + }; + p.prototype.openInputPort = function(e, g) { + e = this.input_ports.get("input-" + e); + if (!e) { + return !1; + } + p.input = this; + var k = this; + e.onmidimessage = function(a) { + var b = new c(a.data); + k.updateState(b); + g && g(a.data, b); + if (p.on_message) { + p.on_message(a.data, b); + } + }; + console.log("port open: ", e); + return !0; + }; + p.parseMsg = function(c) { + }; + p.prototype.updateState = function(e) { + switch(e.cmd) { + case c.NOTEON: + this.state.note[e.value1 | 0] = e.value2; + break; + case c.NOTEOFF: + this.state.note[e.value1 | 0] = 0; + break; + case c.CONTROLLERCHANGE: + this.state.cc[e.getCC()] = e.getCCValue(); + } + }; + p.prototype.sendMIDI = function(e, g) { + g && (e = this.output_ports_info[e]) && (p.output = this, g.constructor === c ? e.send(g.data) : e.send(g)); + }; + m.MIDIInterface = p; + m.title = "MIDI Input"; + m.desc = "Reads MIDI from a input port"; + m.color = "#243"; + m.prototype.getPropertyInfo = function(c) { + if (this._midi && "port" == c) { + c = {}; + for (var e = 0; e < this._midi.input_ports_info.length; ++e) { + var k = this._midi.input_ports_info[e]; + c[e] = e + ".- " + k.name + " version:" + k.version; + } + return {type:"enum", values:c}; + } + }; + m.prototype.onStart = function() { + this._midi ? this._midi.openInputPort(this.properties.port, this.onMIDIEvent.bind(this)) : this._waiting = !0; + }; + m.prototype.onMIDIEvent = function(e, g) { + this._last_midi_event = g; + this.boxcolor = "#AFA"; + this._last_time = t.getTime(); + this.trigger("on_midi", g); + g.cmd == c.NOTEON ? this.trigger("on_noteon", g) : g.cmd == c.NOTEOFF ? this.trigger("on_noteoff", g) : g.cmd == c.CONTROLLERCHANGE ? this.trigger("on_cc", g) : g.cmd == c.PROGRAMCHANGE ? this.trigger("on_pc", g) : g.cmd == c.PITCHBEND && this.trigger("on_pitchbend", g); + }; + m.prototype.onDrawBackground = function(c) { + this.boxcolor = "#AAA"; + if (!this.flags.collapsed && this._last_midi_event) { + c.fillStyle = "white"; + var e = t.getTime(); + e = 1.0 - Math.max(0, 0.001 * (e - this._last_time)); + if (0 < e) { + var k = c.globalAlpha; + c.globalAlpha *= e; + c.font = "12px Tahoma"; + c.fillText(this._last_midi_event.toString(), 2, 0.5 * this.size[1] + 3); + c.globalAlpha = k; + } + } + }; + m.prototype.onExecute = function() { + if (this.outputs) { + for (var c = this._last_midi_event, e = 0; e < this.outputs.length; ++e) { + switch(this.outputs[e].name) { + case "midi": + var k = this._midi; + break; + case "last_midi": + k = c; + break; + default: + continue; + } + this.setOutputData(e, k); + } + } + }; + m.prototype.onGetOutputs = function() { + return [["last_midi", "midi"], ["on_midi", t.EVENT], ["on_noteon", t.EVENT], ["on_noteoff", t.EVENT], ["on_cc", t.EVENT], ["on_pc", t.EVENT], ["on_pitchbend", t.EVENT]]; + }; + t.registerNodeType("midi/input", m); + g.MIDIInterface = p; + g.title = "MIDI Output"; + g.desc = "Sends MIDI to output channel"; + g.color = "#243"; + g.prototype.onGetPropertyInfo = function(c) { + if (this._midi && "port" == c) { + return {type:"enum", values:this.getMIDIOutputs()}; + } + }; + g.default_ports = {0:"unknown"}; + g.prototype.getMIDIOutputs = function() { + var c = {}; + if (!this._midi) { + return g.default_ports; + } + if (this._midi.output_ports_info) { + for (var e = 0; e < this._midi.output_ports_info.length; ++e) { + var k = this._midi.output_ports_info[e]; + k && (c[e] = e + ".- " + k.name + " version:" + k.version); + } + } + return c; + }; + g.prototype.onAction = function(c, e) { + this._midi && ("send" == c && this._midi.sendMIDI(this.properties.port, e), this.trigger("midi", e)); + }; + g.prototype.onGetInputs = function() { + return [["send", t.ACTION]]; + }; + g.prototype.onGetOutputs = function() { + return [["on_midi", t.EVENT]]; + }; + t.registerNodeType("midi/output", g); + u.title = "MIDI Show"; + u.desc = "Shows MIDI in the graph"; + u.color = "#243"; + u.prototype.getTitle = function() { + return this.flags.collapsed ? this._str : this.title; + }; + u.prototype.onAction = function(e, g) { + g && (this._str = g.constructor === c ? g.toString() : "???"); + }; + u.prototype.onDrawForeground = function(c) { + this._str && !this.flags.collapsed && (c.font = "30px Arial", c.fillText(this._str, 10, 0.8 * this.size[1])); + }; + u.prototype.onGetInputs = function() { + return [["in", t.ACTION]]; + }; + u.prototype.onGetOutputs = function() { + return [["on_midi", t.EVENT]]; + }; + t.registerNodeType("midi/show", u); + l.title = "MIDI Filter"; + l.desc = "Filters MIDI messages"; + l.color = "#243"; + l["@cmd"] = {type:"enum", title:"Command", values:c.commands_reversed}; + l.prototype.getTitle = function() { + var e = -1 == this.properties.cmd ? "Nothing" : c.commands_short[this.properties.cmd] || "Unknown"; + -1 != this.properties.min_value && -1 != this.properties.max_value && (e += " " + (this.properties.min_value == this.properties.max_value ? this.properties.max_value : this.properties.min_value + ".." + this.properties.max_value)); + return "Filter: " + e; + }; + l.prototype.onPropertyChanged = function(e, g) { + "cmd" == e && (e = Number(g), isNaN(e) && (e = c.commands[g] || 0), this.properties.cmd = e); + }; + l.prototype.onAction = function(e, g) { + if (g && g.constructor === c) { + if (this._learning) { + this._learning = !1, this.boxcolor = "#AAA", this.properties.channel = g.channel, this.properties.cmd = g.cmd, this.properties.min_value = this.properties.max_value = g.data[1]; + } else { + if (-1 != this.properties.channel && g.channel != this.properties.channel || -1 != this.properties.cmd && g.cmd != this.properties.cmd || -1 != this.properties.min_value && g.data[1] < this.properties.min_value || -1 != this.properties.max_value && g.data[1] > this.properties.max_value) { + return; + } + } + this.trigger("on_midi", g); + } + }; + t.registerNodeType("midi/filter", l); + B.title = "MIDIEvent"; + B.desc = "Create a MIDI Event"; + B.color = "#243"; + B.prototype.onAction = function(e, g) { + "assign" == e ? (this.properties.channel = g.channel, this.properties.cmd = g.cmd, this.properties.value1 = g.data[1], this.properties.value2 = g.data[2], g.cmd == c.NOTEON ? this.gate = !0 : g.cmd == c.NOTEOFF && (this.gate = !1)) : (g = this.midi_event, g.channel = this.properties.channel, this.properties.cmd && this.properties.cmd.constructor === String ? g.setCommandFromString(this.properties.cmd) : g.cmd = this.properties.cmd, g.data[0] = g.cmd | g.channel, g.data[1] = Number(this.properties.value1), + g.data[2] = Number(this.properties.value2), this.trigger("on_midi", g)); + }; + B.prototype.onExecute = function() { + var e = this.properties; + if (this.inputs) { + for (var g = 0; g < this.inputs.length; ++g) { + var k = this.inputs[g]; + if (-1 != k.link) { + switch(k.name) { + case "note": + k = this.getInputData(g); + null != k && (k.constructor === String && (k = c.NoteStringToPitch(k)), this.properties.value1 = (k | 0) % 255); + break; + case "cmd": + k = this.getInputData(g); + null != k && (this.properties.cmd = k); + break; + case "value1": + k = this.getInputData(g); + null != k && (this.properties.value1 = Math.clamp(k | 0, 0, 127)); + break; + case "value2": + k = this.getInputData(g), null != k && (this.properties.value2 = Math.clamp(k | 0, 0, 127)); + } + } + } + } + if (this.outputs) { + for (g = 0; g < this.outputs.length; ++g) { + switch(this.outputs[g].name) { + case "midi": + k = new c; + k.setup([e.cmd, e.value1, e.value2]); + k.channel = e.channel; + break; + case "command": + k = e.cmd; + break; + case "cc": + k = e.value1; + break; + case "cc_value": + k = e.value2; + break; + case "note": + k = e.cmd == c.NOTEON || e.cmd == c.NOTEOFF ? e.value1 : null; + break; + case "velocity": + k = e.cmd == c.NOTEON ? e.value2 : null; + break; + case "pitch": + k = e.cmd == c.NOTEON ? c.computePitch(e.value1) : null; + break; + case "pitchbend": + k = e.cmd == c.PITCHBEND ? c.computePitchBend(e.value1, e.value2) : null; + break; + case "gate": + k = this.gate; + break; + default: + continue; + } + null !== k && this.setOutputData(g, k); + } + } + }; + B.prototype.onPropertyChanged = function(e, g) { + "cmd" == e && (this.properties.cmd = c.computeCommandFromString(g)); + }; + B.prototype.onGetInputs = function() { + return [["cmd", "number"], ["note", "number"], ["value1", "number"], ["value2", "number"]]; + }; + B.prototype.onGetOutputs = function() { + return [["midi", "midi"], ["on_midi", t.EVENT], ["command", "number"], ["note", "number"], ["velocity", "number"], ["cc", "number"], ["cc_value", "number"], ["pitch", "number"], ["gate", "bool"], ["pitchbend", "number"]]; + }; + t.registerNodeType("midi/event", B); + y.title = "MIDICC"; + y.desc = "gets a Controller Change"; + y.color = "#243"; + y.prototype.onExecute = function() { + p.input && (this.properties.value = p.input.state.cc[this.properties.cc]); + this.setOutputData(0, this.properties.value); + }; + t.registerNodeType("midi/cc", y); + v.title = "MIDI Generator"; + v.desc = "Generates a random MIDI note"; + v.color = "#243"; + v.processScale = function(e) { + e = e.split(","); + for (var g = 0; g < e.length; ++g) { + var k = e[g]; + e[g] = 2 == k.length && "#" != k[1] || 2 < k.length ? -t.MIDIEvent.NoteStringToPitch(k) : c.note_to_index[k] || 0; + } + return e; + }; + v.prototype.onPropertyChanged = function(c, e) { + "notes" == c && (this.notes_pitches = v.processScale(e)); + }; + v.prototype.onExecute = function() { + var c = this.getInputData(2); + null != c && (this.properties.octave = c); + if (c = this.getInputData(1)) { + this.notes_pitches = v.processScale(c); + } + }; + v.prototype.onAction = function(e, g) { + var k = 0; + g = this.notes_pitches.length; + e = 0; + "sequence" == this.properties.mode ? e = this.sequence_index = (this.sequence_index + 1) % g : "random" == this.properties.mode && (e = Math.floor(Math.random() * g)); + g = this.notes_pitches[e]; + k = 0 <= g ? g + 12 * (this.properties.octave - 1) + 33 : -g; + g = new c; + g.setup([c.NOTEON, k, 10]); + e = this.properties.duration || 1; + this.trigger("note", g); + setTimeout(function() { + var a = new c; + a.setup([c.NOTEOFF, k, 0]); + this.trigger("note", a); + }.bind(this), 1000 * e); + }; + t.registerNodeType("midi/generator", v); + E.title = "MIDI Transpose"; + E.desc = "Transpose a MIDI note"; + E.color = "#243"; + E.prototype.onAction = function(e, g) { + g && g.constructor === c && (g.data[0] == c.NOTEON || g.data[0] == c.NOTEOFF ? (this.midi_event = new c, this.midi_event.setup(g.data), this.midi_event.data[1] = Math.round(this.midi_event.data[1] + this.properties.amount), this.trigger("out", this.midi_event)) : this.trigger("out", g)); + }; + E.prototype.onExecute = function() { + var c = this.getInputData(1); + null != c && (this.properties.amount = c); + }; + t.registerNodeType("midi/transpose", E); + z.title = "MIDI Quantize Pitch"; + z.desc = "Transpose a MIDI note tp fit an scale"; + z.color = "#243"; + z.prototype.onPropertyChanged = function(c, e) { + "scale" == c && this.processScale(e); + }; + z.prototype.processScale = function(c) { + this._current_scale = c; + this.notes_pitches = v.processScale(c); + for (c = 0; 12 > c; ++c) { + this.valid_notes[c] = -1 != this.notes_pitches.indexOf(c); + } + for (c = 0; 12 > c; ++c) { + if (this.valid_notes[c]) { + this.offset_notes[c] = 0; + } else { + for (var e = 1; 12 > e; ++e) { + if (this.valid_notes[(c - e) % 12]) { + this.offset_notes[c] = -e; + break; + } + if (this.valid_notes[(c + e) % 12]) { + this.offset_notes[c] = e; + break; + } + } + } + } + }; + z.prototype.onAction = function(e, g) { + g && g.constructor === c && (g.data[0] == c.NOTEON || g.data[0] == c.NOTEOFF ? (this.midi_event = new c, this.midi_event.setup(g.data), this.midi_event.data[1] += this.offset_notes[c.note_to_index[g.note]], this.trigger("out", this.midi_event)) : this.trigger("out", g)); + }; + z.prototype.onExecute = function() { + var c = this.getInputData(1); + null != c && c != this._current_scale && this.processScale(c); + }; + t.registerNodeType("midi/quantize", z); + e.title = "MIDI fromFile"; + e.desc = "Plays a MIDI file"; + e.color = "#243"; + e.prototype.onAction = function(c) { + "play" == c ? this.play() : "pause" == c && (this._playing = !this._playing); + }; + e.prototype.onPropertyChanged = function(c, e) { + "url" == c && this.loadMIDIFile(e); + }; + e.prototype.onExecute = function() { + if (this._midi && this._playing) { + this._current_time += this.graph.elapsed_time; + for (var e = 100 * this._current_time, g = 0; g < this._midi.tracks; ++g) { + var k = this._midi.track[g]; + k._last_pos || (k._last_pos = 0, k._time = 0); + var a = k.event[k._last_pos]; + if (a && k._time + a.deltaTime <= e && (k._last_pos++, k._time += a.deltaTime, a.data)) { + k = a.type << 4 + a.channel; + var b = new c; + b.setup([k, a.data[0], a.data[1]]); + this.trigger("note", b); + } + } + } + }; + e.prototype.play = function() { + this._playing = !0; + for (var c = this._current_time = 0; c < this._midi.tracks; ++c) { + var e = this._midi.track[c]; + e._last_pos = 0; + e._time = 0; + } + }; + e.prototype.loadMIDIFile = function(c) { + var e = this; + t.fetchFile(c, "arraybuffer", function(c) { + e.boxcolor = "#AFA"; + e._midi = MidiParser.parse(new Uint8Array(c)); + e.properties.autoplay && e.play(); + }, function(c) { + e.boxcolor = "#FAA"; + e._midi = null; + }); + }; + e.prototype.onDropFile = function(c) { + this.properties.url = ""; + this.loadMIDIFile(c); + }; + t.registerNodeType("midi/fromFile", e); + C.title = "MIDI Play"; + C.desc = "Plays a MIDI note"; + C.color = "#243"; + C.prototype.onAction = function(e, g) { + if (g && g.constructor === c) { + if (this.instrument && g.data[0] == c.NOTEON) { + e = g.note; + if (!e || "undefined" == e || e.constructor !== String) { + return; + } + this.instrument.play(e, g.octave, this.properties.duration, this.properties.volume); + } + this.trigger("note", g); + } + }; + C.prototype.onExecute = function() { + var c = this.getInputData(1); + null != c && (this.properties.volume = c); + c = this.getInputData(2); + null != c && (this.properties.duration = c); + }; + t.registerNodeType("midi/play", C); + D.title = "MIDI Keys"; + D.desc = "Keyboard to play notes"; + D.color = "#243"; + D.keys = [{x:0, w:1, h:1, t:0}, {x:0.75, w:0.5, h:0.6, t:1}, {x:1, w:1, h:1, t:0}, {x:1.75, w:0.5, h:0.6, t:1}, {x:2, w:1, h:1, t:0}, {x:2.75, w:0.5, h:0.6, t:1}, {x:3, w:1, h:1, t:0}, {x:4, w:1, h:1, t:0}, {x:4.75, w:0.5, h:0.6, t:1}, {x:5, w:1, h:1, t:0}, {x:5.75, w:0.5, h:0.6, t:1}, {x:6, w:1, h:1, t:0}]; + D.prototype.onDrawForeground = function(c) { + if (!this.flags.collapsed) { + var e = 12 * this.properties.num_octaves; + this.keys.length = e; + var k = this.size[0] / (7 * this.properties.num_octaves), a = this.size[1]; + c.globalAlpha = 1; + for (var b = 0; 2 > b; b++) { + for (var d = 0; d < e; ++d) { + var h = D.keys[d % 12]; + if (h.t == b) { + var f = 7 * Math.floor(d / 12) * k + h.x * k; + c.fillStyle = 0 == b ? this.keys[d] ? "#CCC" : "white" : this.keys[d] ? "#333" : "black"; + c.fillRect(f + 1, 0, k * h.w - 2, a * h.h); + } + } + } + } + }; + D.prototype.getKeyIndex = function(c) { + for (var e = this.size[0] / (7 * this.properties.num_octaves), k = this.size[1], a = 1; 0 <= a; a--) { + for (var b = 0; b < this.keys.length; ++b) { + var d = D.keys[b % 12]; + if (d.t == a) { + var h = 7 * Math.floor(b / 12) * e + d.x * e, f = e * d.w; + d = k * d.h; + if (!(c[0] < h || c[0] > h + f || c[1] > d)) { + return b; + } + } + } + } + return -1; + }; + D.prototype.onAction = function(e, g) { + if ("reset" == e) { + for (g = 0; g < this.keys.length; ++g) { + this.keys[g] = !1; + } + } else { + g && g.constructor === c && (e = g.data[1] - (12 * (this.properties.start_octave - 1) + 29), 0 <= e && e < this.keys.length && (g.data[0] == c.NOTEON ? this.keys[e] = !0 : g.data[0] == c.NOTEOFF && (this.keys[e] = !1)), this.trigger("note", g)); + } + }; + D.prototype.onMouseDown = function(e, g) { + if (!(0 > g[1])) { + return e = this.getKeyIndex(g), this.keys[e] = !0, this._last_key = e, e = 12 * (this.properties.start_octave - 1) + 29 + e, g = new c, g.setup([c.NOTEON, e, 100]), this.trigger("note", g), !0; + } + }; + D.prototype.onMouseMove = function(e, g) { + if (!(0 > g[1] || -1 == this._last_key)) { + this.setDirtyCanvas(!0); + e = this.getKeyIndex(g); + if (this._last_key == e) { + return !0; + } + this.keys[this._last_key] = !1; + g = 12 * (this.properties.start_octave - 1) + 29 + this._last_key; + var k = new c; + k.setup([c.NOTEOFF, g, 100]); + this.trigger("note", k); + this.keys[e] = !0; + g = 12 * (this.properties.start_octave - 1) + 29 + e; + k = new c; + k.setup([c.NOTEON, g, 100]); + this.trigger("note", k); + this._last_key = e; + return !0; + } + }; + D.prototype.onMouseUp = function(e, g) { + if (!(0 > g[1])) { + return e = this.getKeyIndex(g), this.keys[e] = !1, this._last_key = -1, e = 12 * (this.properties.start_octave - 1) + 29 + e, g = new c, g.setup([c.NOTEOFF, e, 100]), this.trigger("note", g), !0; + } + }; + t.registerNodeType("midi/keys", D); +})(this); +(function(w) { + function c() { + this.properties = {src:"", gain:0.5, loop:!0, autoplay:!0, playbackRate:1}; + this._loading_audio = !1; + this._audiobuffer = null; + this._audionodes = []; + this._last_sourcenode = null; + this.addOutput("out", "audio"); + this.addInput("gain", "number"); + this.audionode = q.getAudioContext().createGain(); + this.audionode.graphnode = this; + this.audionode.gain.value = this.properties.gain; + this.properties.src && this.loadSound(this.properties.src); + } + function p() { + this.properties = {gain:0.5}; + this._audionodes = []; + this._media_stream = null; + this.addOutput("out", "audio"); + this.addInput("gain", "number"); + this.audionode = q.getAudioContext().createGain(); + this.audionode.graphnode = this; + this.audionode.gain.value = this.properties.gain; + } + function m() { + this.properties = {fftSize:2048, minDecibels:-100, maxDecibels:-10, smoothingTimeConstant:0.5}; + this.audionode = q.getAudioContext().createAnalyser(); + this.audionode.graphnode = this; + this.audionode.fftSize = this.properties.fftSize; + this.audionode.minDecibels = this.properties.minDecibels; + this.audionode.maxDecibels = this.properties.maxDecibels; + this.audionode.smoothingTimeConstant = this.properties.smoothingTimeConstant; + this.addInput("in", "audio"); + this.addOutput("freqs", "array"); + this.addOutput("samples", "array"); + this._time_bin = this._freq_bin = null; + } + function g() { + this.properties = {gain:1}; + this.audionode = q.getAudioContext().createGain(); + this.addInput("in", "audio"); + this.addInput("gain", "number"); + this.addOutput("out", "audio"); + } + function u() { + this.properties = {impulse_src:"", normalize:!0}; + this.audionode = q.getAudioContext().createConvolver(); + this.addInput("in", "audio"); + this.addOutput("out", "audio"); + } + function l() { + this.properties = {threshold:-50, knee:40, ratio:12, reduction:-20, attack:0, release:0.25}; + this.audionode = q.getAudioContext().createDynamicsCompressor(); + this.addInput("in", "audio"); + this.addOutput("out", "audio"); + } + function B() { + this.properties = {}; + this.audionode = q.getAudioContext().createWaveShaper(); + this.addInput("in", "audio"); + this.addInput("shape", "waveshape"); + this.addOutput("out", "audio"); + } + function y() { + this.properties = {gain1:0.5, gain2:0.5}; + this.audionode = q.getAudioContext().createGain(); + this.audionode1 = q.getAudioContext().createGain(); + this.audionode1.gain.value = this.properties.gain1; + this.audionode2 = q.getAudioContext().createGain(); + this.audionode2.gain.value = this.properties.gain2; + this.audionode1.connect(this.audionode); + this.audionode2.connect(this.audionode); + this.addInput("in1", "audio"); + this.addInput("in1 gain", "number"); + this.addInput("in2", "audio"); + this.addInput("in2 gain", "number"); + this.addOutput("out", "audio"); + } + function v() { + this.properties = {A:0.1, D:0.1, S:0.1, R:0.1}; + this.audionode = q.getAudioContext().createGain(); + this.audionode.gain.value = 0; + this.addInput("in", "audio"); + this.addInput("gate", "bool"); + this.addOutput("out", "audio"); + this.gate = !1; + } + function E() { + this.properties = {delayTime:0.5}; + this.audionode = q.getAudioContext().createDelay(10); + this.audionode.delayTime.value = this.properties.delayTime; + this.addInput("in", "audio"); + this.addInput("time", "number"); + this.addOutput("out", "audio"); + } + function z() { + this.properties = {frequency:350, detune:0, Q:1}; + this.addProperty("type", "lowpass", "enum", {values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")}); + this.audionode = q.getAudioContext().createBiquadFilter(); + this.addInput("in", "audio"); + this.addOutput("out", "audio"); + } + function e() { + this.properties = {frequency:440, detune:0, type:"sine"}; + this.addProperty("type", "sine", "enum", {values:["sine", "square", "sawtooth", "triangle", "custom"]}); + this.audionode = q.getAudioContext().createOscillator(); + this.addOutput("out", "audio"); + } + function C() { + this.properties = {continuous:!0, mark:-1}; + this.addInput("data", "array"); + this.addInput("mark", "number"); + this.size = [300, 200]; + this._last_buffer = null; + } + function D() { + this.properties = {band:440, amplitude:1}; + this.addInput("freqs", "array"); + this.addOutput("signal", "number"); + } + function t() { + if (!t.default_code) { + var c = t.default_function.toString(), a = c.indexOf("{") + 1, b = c.lastIndexOf("}"); + t.default_code = c.substr(a, b - a); + } + this.properties = {code:t.default_code}; + c = q.getAudioContext(); + c.createScriptProcessor ? this.audionode = c.createScriptProcessor(4096, 1, 1) : (console.warn("ScriptProcessorNode deprecated"), this.audionode = c.createGain()); + this.processCode(); + t._bypass_function || (t._bypass_function = this.audionode.onaudioprocess); + this.addInput("in", "audio"); + this.addOutput("out", "audio"); + } + function G() { + this.audionode = q.getAudioContext().destination; + this.addInput("in", "audio"); + } + var n = w.LiteGraph, q = {}; + w.LGAudio = q; + q.getAudioContext = function() { + if (!this._audio_context) { + window.AudioContext = window.AudioContext || window.webkitAudioContext; + if (!window.AudioContext) { + return console.error("AudioContext not supported by browser"), null; + } + this._audio_context = new AudioContext; + this._audio_context.onmessage = function(c) { + console.log("msg", c); + }; + this._audio_context.onended = function(c) { + console.log("ended", c); + }; + this._audio_context.oncomplete = function(c) { + console.log("complete", c); + }; + } + return this._audio_context; + }; + q.connect = function(c, a) { + try { + c.connect(a); + } catch (b) { + console.warn("LGraphAudio:", b); + } + }; + q.disconnect = function(c, a) { + try { + c.disconnect(a); + } catch (b) { + console.warn("LGraphAudio:", b); + } + }; + q.changeAllAudiosConnections = function(c, a) { + if (c.inputs) { + for (var b = 0; b < c.inputs.length; ++b) { + var d = c.graph.links[c.inputs[b].link]; + if (d) { + var e = c.graph.getNodeById(d.origin_id); + e = e.getAudioNodeInOutputSlot ? e.getAudioNodeInOutputSlot(d.origin_slot) : e.audionode; + d = c.getAudioNodeInInputSlot ? c.getAudioNodeInInputSlot(b) : c.audionode; + a ? q.connect(e, d) : q.disconnect(e, d); + } + } + } + if (c.outputs) { + for (b = 0; b < c.outputs.length; ++b) { + for (var k = c.outputs[b], g = 0; g < k.links.length; ++g) { + if (d = c.graph.links[k.links[g]]) { + e = c.getAudioNodeInOutputSlot ? c.getAudioNodeInOutputSlot(b) : c.audionode; + var l = c.graph.getNodeById(d.target_id); + d = l.getAudioNodeInInputSlot ? l.getAudioNodeInInputSlot(d.target_slot) : l.audionode; + a ? q.connect(e, d) : q.disconnect(e, d); + } + } + } + } + }; + q.onConnectionsChange = function(c, a, b, d) { + c == n.OUTPUT && (c = null, d && (c = this.graph.getNodeById(d.target_id)), c && (a = this.getAudioNodeInOutputSlot ? this.getAudioNodeInOutputSlot(a) : this.audionode, d = c.getAudioNodeInInputSlot ? c.getAudioNodeInInputSlot(d.target_slot) : c.audionode, b ? q.connect(a, d) : q.disconnect(a, d))); + }; + q.createAudioNodeWrapper = function(c) { + var a = c.prototype.onPropertyChanged; + c.prototype.onPropertyChanged = function(b, d) { + a && a.call(this, b, d); + this.audionode && void 0 !== this.audionode[b] && (void 0 !== this.audionode[b].value ? this.audionode[b].value = d : this.audionode[b] = d); + }; + c.prototype.onConnectionsChange = q.onConnectionsChange; + }; + q.cached_audios = {}; + q.loadSound = function(c, a, b) { + function d(a) { + console.log("Audio loading sample error:", a); + b && b(a); + } + if (q.cached_audios[c] && -1 == c.indexOf("blob:")) { + a && a(q.cached_audios[c]); + } else { + q.onProcessAudioURL && (c = q.onProcessAudioURL(c)); + var e = new XMLHttpRequest; + e.open("GET", c, !0); + e.responseType = "arraybuffer"; + var k = q.getAudioContext(); + e.onload = function() { + console.log("AudioSource loaded"); + k.decodeAudioData(e.response, function(b) { + console.log("AudioSource decoded"); + q.cached_audios[c] = b; + a && a(b); + }, d); + }; + e.send(); + return e; + } + }; + c.desc = "Plays an audio file"; + c["@src"] = {widget:"resource"}; + c.supported_extensions = ["wav", "ogg", "mp3"]; + c.prototype.onAdded = function(c) { + if (c.status === LGraph.STATUS_RUNNING) { + this.onStart(); + } + }; + c.prototype.onStart = function() { + this._audiobuffer && this.properties.autoplay && this.playBuffer(this._audiobuffer); + }; + c.prototype.onStop = function() { + this.stopAllSounds(); + }; + c.prototype.onPause = function() { + this.pauseAllSounds(); + }; + c.prototype.onUnpause = function() { + this.unpauseAllSounds(); + }; + c.prototype.onRemoved = function() { + this.stopAllSounds(); + this._dropped_url && URL.revokeObjectURL(this._url); + }; + c.prototype.stopAllSounds = function() { + for (var c = 0; c < this._audionodes.length; ++c) { + this._audionodes[c].started && (this._audionodes[c].started = !1, this._audionodes[c].stop()); + } + this._audionodes.length = 0; + }; + c.prototype.pauseAllSounds = function() { + q.getAudioContext().suspend(); + }; + c.prototype.unpauseAllSounds = function() { + q.getAudioContext().resume(); + }; + c.prototype.onExecute = function() { + if (this.inputs) { + for (var c = 0; c < this.inputs.length; ++c) { + var a = this.inputs[c]; + if (null != a.link) { + var b = this.getInputData(c); + if (void 0 !== b) { + if ("gain" == a.name) { + this.audionode.gain.value = b; + } else { + if ("src" == a.name) { + this.setProperty("src", b); + } else { + if ("playbackRate" == a.name) { + for (this.properties.playbackRate = b, a = 0; a < this._audionodes.length; ++a) { + this._audionodes[a].playbackRate.value = b; + } + } + } + } + } + } + } + } + if (this.outputs) { + for (c = 0; c < this.outputs.length; ++c) { + "buffer" == this.outputs[c].name && this._audiobuffer && this.setOutputData(c, this._audiobuffer); + } + } + }; + c.prototype.onAction = function(c) { + this._audiobuffer && ("Play" == c ? this.playBuffer(this._audiobuffer) : "Stop" == c && this.stopAllSounds()); + }; + c.prototype.onPropertyChanged = function(c, a) { + if ("src" == c) { + this.loadSound(a); + } else { + if ("gain" == c) { + this.audionode.gain.value = a; + } else { + if ("playbackRate" == c) { + for (c = 0; c < this._audionodes.length; ++c) { + this._audionodes[c].playbackRate.value = a; + } + } + } + } + }; + c.prototype.playBuffer = function(c) { + var a = this, b = q.getAudioContext().createBufferSource(); + this._last_sourcenode = b; + b.graphnode = this; + b.buffer = c; + b.loop = this.properties.loop; + b.playbackRate.value = this.properties.playbackRate; + this._audionodes.push(b); + b.connect(this.audionode); + this._audionodes.push(b); + this.trigger("start"); + b.onended = function() { + a.trigger("ended"); + var c = a._audionodes.indexOf(b); + -1 != c && a._audionodes.splice(c, 1); + }; + b.started || (b.started = !0, b.start()); + return b; + }; + c.prototype.loadSound = function(c) { + var a = this; + this._request && (this._request.abort(), this._request = null); + this._audiobuffer = null; + this._loading_audio = !1; + c && (this._request = q.loadSound(c, function(b) { + this.boxcolor = n.NODE_DEFAULT_BOXCOLOR; + a._audiobuffer = b; + a._loading_audio = !1; + if (a.graph && a.graph.status === LGraph.STATUS_RUNNING) { + a.onStart(); + } + }), this._loading_audio = !0, this.boxcolor = "#AA4"); + }; + c.prototype.onConnectionsChange = q.onConnectionsChange; + c.prototype.onGetInputs = function() { + return [["playbackRate", "number"], ["src", "string"], ["Play", n.ACTION], ["Stop", n.ACTION]]; + }; + c.prototype.onGetOutputs = function() { + return [["buffer", "audiobuffer"], ["start", n.EVENT], ["ended", n.EVENT]]; + }; + c.prototype.onDropFile = function(c) { + this._dropped_url && URL.revokeObjectURL(this._dropped_url); + c = URL.createObjectURL(c); + this.properties.src = c; + this.loadSound(c); + this._dropped_url = c; + }; + c.title = "Source"; + c.desc = "Plays audio"; + n.registerNodeType("audio/source", c); + p.prototype.onAdded = function(c) { + if (c.status === LGraph.STATUS_RUNNING) { + this.onStart(); + } + }; + p.prototype.onStart = function() { + null != this._media_stream || this._waiting_confirmation || this.openStream(); + }; + p.prototype.onStop = function() { + this.audionode.gain.value = 0; + }; + p.prototype.onPause = function() { + this.audionode.gain.value = 0; + }; + p.prototype.onUnpause = function() { + this.audionode.gain.value = this.properties.gain; + }; + p.prototype.onRemoved = function() { + this.audionode.gain.value = 0; + this.audiosource_node && (this.audiosource_node.disconnect(this.audionode), this.audiosource_node = null); + if (this._media_stream) { + var c = this._media_stream.getTracks(); + c.length && c[0].stop(); + } + }; + p.prototype.openStream = function() { + if (navigator.mediaDevices) { + this._waiting_confirmation = !0; + navigator.mediaDevices.getUserMedia({audio:!0, video:!1}).then(this.streamReady.bind(this)).catch(function(a) { + console.log("Media rejected", a); + c._media_stream = !1; + c.boxcolor = "red"; + }); + var c = this; + } else { + console.log("getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags"); + } + }; + p.prototype.streamReady = function(c) { + this._media_stream = c; + this.audiosource_node && this.audiosource_node.disconnect(this.audionode); + this.audiosource_node = q.getAudioContext().createMediaStreamSource(c); + this.audiosource_node.graphnode = this; + this.audiosource_node.connect(this.audionode); + this.boxcolor = "white"; + }; + p.prototype.onExecute = function() { + null != this._media_stream || this._waiting_confirmation || this.openStream(); + if (this.inputs) { + for (var c = 0; c < this.inputs.length; ++c) { + var a = this.inputs[c]; + if (null != a.link) { + var b = this.getInputData(c); + void 0 !== b && "gain" == a.name && (this.audionode.gain.value = this.properties.gain = b); + } + } + } + }; + p.prototype.onAction = function(c) { + "Play" == c ? this.audionode.gain.value = this.properties.gain : "Stop" == c && (this.audionode.gain.value = 0); + }; + p.prototype.onPropertyChanged = function(c, a) { + "gain" == c && (this.audionode.gain.value = a); + }; + p.prototype.onConnectionsChange = q.onConnectionsChange; + p.prototype.onGetInputs = function() { + return [["playbackRate", "number"], ["Play", n.ACTION], ["Stop", n.ACTION]]; + }; + p.title = "MediaSource"; + p.desc = "Plays microphone"; + n.registerNodeType("audio/media_source", p); + m.prototype.onPropertyChanged = function(c, a) { + this.audionode[c] = a; + }; + m.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var c = this.audionode.frequencyBinCount; + this._freq_bin && this._freq_bin.length == c || (this._freq_bin = new Uint8Array(c)); + this.audionode.getByteFrequencyData(this._freq_bin); + this.setOutputData(0, this._freq_bin); + } + this.isOutputConnected(1) && (c = this.audionode.frequencyBinCount, this._time_bin && this._time_bin.length == c || (this._time_bin = new Uint8Array(c)), this.audionode.getByteTimeDomainData(this._time_bin), this.setOutputData(1, this._time_bin)); + for (c = 1; c < this.inputs.length; ++c) { + var a = this.inputs[c]; + if (null != a.link) { + var b = this.getInputData(c); + void 0 !== b && (this.audionode[a.name].value = b); + } + } + }; + m.prototype.onGetInputs = function() { + return [["minDecibels", "number"], ["maxDecibels", "number"], ["smoothingTimeConstant", "number"]]; + }; + m.prototype.onGetOutputs = function() { + return [["freqs", "array"], ["samples", "array"]]; + }; + m.title = "Analyser"; + m.desc = "Audio Analyser"; + n.registerNodeType("audio/analyser", m); + g.prototype.onExecute = function() { + if (this.inputs && this.inputs.length) { + for (var c = 1; c < this.inputs.length; ++c) { + var a = this.inputs[c], b = this.getInputData(c); + void 0 !== b && (this.audionode[a.name].value = b); + } + } + }; + q.createAudioNodeWrapper(g); + g.title = "Gain"; + g.desc = "Audio gain"; + n.registerNodeType("audio/gain", g); + q.createAudioNodeWrapper(u); + u.prototype.onRemove = function() { + this._dropped_url && URL.revokeObjectURL(this._dropped_url); + }; + u.prototype.onPropertyChanged = function(c, a) { + "impulse_src" == c ? this.loadImpulse(a) : "normalize" == c && (this.audionode.normalize = a); + }; + u.prototype.onDropFile = function(c) { + this._dropped_url && URL.revokeObjectURL(this._dropped_url); + this._dropped_url = URL.createObjectURL(c); + this.properties.impulse_src = this._dropped_url; + this.loadImpulse(this._dropped_url); + }; + u.prototype.loadImpulse = function(c) { + var a = this; + this._request && (this._request.abort(), this._request = null); + this._impulse_buffer = null; + this._loading_impulse = !1; + c && (this._request = q.loadSound(c, function(b) { + a._impulse_buffer = b; + a.audionode.buffer = b; + console.log("Impulse signal set"); + a._loading_impulse = !1; + }), this._loading_impulse = !0); + }; + u.title = "Convolver"; + u.desc = "Convolves the signal (used for reverb)"; + n.registerNodeType("audio/convolver", u); + q.createAudioNodeWrapper(l); + l.prototype.onExecute = function() { + if (this.inputs && this.inputs.length) { + for (var c = 1; c < this.inputs.length; ++c) { + var a = this.inputs[c]; + if (null != a.link) { + var b = this.getInputData(c); + void 0 !== b && (this.audionode[a.name].value = b); + } + } + } + }; + l.prototype.onGetInputs = function() { + return [["threshold", "number"], ["knee", "number"], ["ratio", "number"], ["reduction", "number"], ["attack", "number"], ["release", "number"]]; + }; + l.title = "DynamicsCompressor"; + l.desc = "Dynamics Compressor"; + n.registerNodeType("audio/dynamicsCompressor", l); + B.prototype.onExecute = function() { + if (this.inputs && this.inputs.length) { + var c = this.getInputData(1); + void 0 !== c && (this.audionode.curve = c); + } + }; + B.prototype.setWaveShape = function(c) { + this.audionode.curve = c; + }; + q.createAudioNodeWrapper(B); + y.prototype.getAudioNodeInInputSlot = function(c) { + if (0 == c) { + return this.audionode1; + } + if (2 == c) { + return this.audionode2; + } + }; + y.prototype.onPropertyChanged = function(c, a) { + "gain1" == c ? this.audionode1.gain.value = a : "gain2" == c && (this.audionode2.gain.value = a); + }; + y.prototype.onExecute = function() { + if (this.inputs && this.inputs.length) { + for (var c = 1; c < this.inputs.length; ++c) { + var a = this.inputs[c]; + null != a.link && "audio" != a.type && (a = this.getInputData(c), void 0 !== a && (1 == c ? this.audionode1.gain.value = a : 3 == c && (this.audionode2.gain.value = a))); + } + } + }; + q.createAudioNodeWrapper(y); + y.title = "Mixer"; + y.desc = "Audio mixer"; + n.registerNodeType("audio/mixer", y); + v.prototype.onExecute = function() { + var c = q.getAudioContext().currentTime, a = this.audionode.gain, b = this.getInputData(1), d = this.getInputOrProperty("A"), e = this.getInputOrProperty("D"), f = this.getInputOrProperty("S"), g = this.getInputOrProperty("R"); + !this.gate && b ? (a.cancelScheduledValues(0), a.setValueAtTime(0, c), a.linearRampToValueAtTime(1, c + d), a.linearRampToValueAtTime(f, c + d + e)) : this.gate && !b && (a.cancelScheduledValues(0), a.setValueAtTime(a.value, c), a.linearRampToValueAtTime(0, c + g)); + this.gate = b; + }; + v.prototype.onGetInputs = function() { + return [["A", "number"], ["D", "number"], ["S", "number"], ["R", "number"]]; + }; + q.createAudioNodeWrapper(v); + v.title = "ADSR"; + v.desc = "Audio envelope"; + n.registerNodeType("audio/adsr", v); + q.createAudioNodeWrapper(E); + E.prototype.onExecute = function() { + var c = this.getInputData(1); + void 0 !== c && (this.audionode.delayTime.value = c); + }; + E.title = "Delay"; + E.desc = "Audio delay"; + n.registerNodeType("audio/delay", E); + z.prototype.onExecute = function() { + if (this.inputs && this.inputs.length) { + for (var c = 1; c < this.inputs.length; ++c) { + var a = this.inputs[c]; + if (null != a.link) { + var b = this.getInputData(c); + void 0 !== b && (this.audionode[a.name].value = b); + } + } + } + }; + z.prototype.onGetInputs = function() { + return [["frequency", "number"], ["detune", "number"], ["Q", "number"]]; + }; + q.createAudioNodeWrapper(z); + z.title = "BiquadFilter"; + z.desc = "Audio filter"; + n.registerNodeType("audio/biquadfilter", z); + e.prototype.onStart = function() { + if (!this.audionode.started) { + this.audionode.started = !0; + try { + this.audionode.start(); + } catch (k) { + } + } + }; + e.prototype.onStop = function() { + this.audionode.started && (this.audionode.started = !1, this.audionode.stop()); + }; + e.prototype.onPause = function() { + this.onStop(); + }; + e.prototype.onUnpause = function() { + this.onStart(); + }; + e.prototype.onExecute = function() { + if (this.inputs && this.inputs.length) { + for (var c = 0; c < this.inputs.length; ++c) { + var a = this.inputs[c]; + if (null != a.link) { + var b = this.getInputData(c); + void 0 !== b && (this.audionode[a.name].value = b); + } + } + } + }; + e.prototype.onGetInputs = function() { + return [["frequency", "number"], ["detune", "number"], ["type", "string"]]; + }; + q.createAudioNodeWrapper(e); + e.title = "Oscillator"; + e.desc = "Oscillator"; + n.registerNodeType("audio/oscillator", e); + C.prototype.onExecute = function() { + this._last_buffer = this.getInputData(0); + var c = this.getInputData(1); + void 0 !== c && (this.properties.mark = c); + this.setDirtyCanvas(!0, !1); + }; + C.prototype.onDrawForeground = function(c) { + if (this._last_buffer) { + var a = this._last_buffer, b = a.length / this.size[0], d = this.size[1]; + c.fillStyle = "black"; + c.fillRect(0, 0, this.size[0], this.size[1]); + c.strokeStyle = "white"; + c.beginPath(); + var e = 0; + if (this.properties.continuous) { + c.moveTo(e, d); + for (var f = 0; f < a.length; f += b) { + c.lineTo(e, d - a[f | 0] / 255 * d), e++; + } + } else { + for (f = 0; f < a.length; f += b) { + c.moveTo(e + 0.5, d), c.lineTo(e + 0.5, d - a[f | 0] / 255 * d), e++; + } + } + c.stroke(); + 0 <= this.properties.mark && (a = q.getAudioContext().sampleRate / a.length, e = this.properties.mark / a * 2 / b, e >= this.size[0] && (e = this.size[0] - 1), c.strokeStyle = "red", c.beginPath(), c.moveTo(e, d), c.lineTo(e, 0), c.stroke()); + } + }; + C.title = "Visualization"; + C.desc = "Audio Visualization"; + n.registerNodeType("audio/visualization", C); + D.prototype.onExecute = function() { + if (this._freqs = this.getInputData(0)) { + var c = this.properties.band, a = this.getInputData(1); + void 0 !== a && (c = a); + a = q.getAudioContext().sampleRate / this._freqs.length; + a = c / a * 2; + a >= this._freqs.length ? a = this._freqs[this._freqs.length - 1] : (c = a | 0, a -= c, a = this._freqs[c] * (1 - a) + this._freqs[c + 1] * a); + this.setOutputData(0, a / 255 * this.properties.amplitude); + } + }; + D.prototype.onGetInputs = function() { + return [["band", "number"]]; + }; + D.title = "Signal"; + D.desc = "extract the signal of some frequency"; + n.registerNodeType("audio/signal", D); + t.prototype.onAdded = function(c) { + c.status == LGraph.STATUS_RUNNING && (this.audionode.onaudioprocess = this._callback); + }; + t["@code"] = {widget:"code", type:"code"}; + t.prototype.onStart = function() { + this.audionode.onaudioprocess = this._callback; + }; + t.prototype.onStop = function() { + this.audionode.onaudioprocess = t._bypass_function; + }; + t.prototype.onPause = function() { + this.audionode.onaudioprocess = t._bypass_function; + }; + t.prototype.onUnpause = function() { + this.audionode.onaudioprocess = this._callback; + }; + t.prototype.onExecute = function() { + }; + t.prototype.onRemoved = function() { + this.audionode.onaudioprocess = t._bypass_function; + }; + t.prototype.processCode = function() { + try { + this._script = new (new Function("properties", this.properties.code))(this.properties), this._old_code = this.properties.code, this._callback = this._script.onaudioprocess; + } catch (k) { + console.error("Error in onaudioprocess code", k), this._callback = t._bypass_function, this.audionode.onaudioprocess = this._callback; + } + }; + t.prototype.onPropertyChanged = function(c, a) { + "code" == c && (this.properties.code = a, this.processCode(), this.graph && this.graph.status == LGraph.STATUS_RUNNING && (this.audionode.onaudioprocess = this._callback)); + }; + t.default_function = function() { + this.onaudioprocess = function(c) { + var a = c.inputBuffer; + c = c.outputBuffer; + for (var b = 0; b < c.numberOfChannels; b++) { + for (var d = a.getChannelData(b), e = c.getChannelData(b), f = 0; f < a.length; f++) { + e[f] = d[f]; + } + } + }; + }; + q.createAudioNodeWrapper(t); + t.title = "Script"; + t.desc = "apply script to signal"; + n.registerNodeType("audio/script", t); + G.title = "Destination"; + G.desc = "Audio output"; + n.registerNodeType("audio/destination", G); +})(this); +(function(w) { + function c() { + this.size = [60, 20]; + this.addInput("send", m.ACTION); + this.addOutput("received", m.EVENT); + this.addInput("in", 0); + this.addOutput("out", 0); + this.properties = {url:"", room:"lgraph", only_send_changes:!0}; + this._ws = null; + this._last_sent_data = []; + this._last_received_data = []; + } + function p() { + this.room_widget = this.addWidget("text", "Room", "lgraph", this.setRoom.bind(this)); + this.addWidget("button", "Reconnect", null, this.connectSocket.bind(this)); + this.addInput("send", m.ACTION); + this.addOutput("received", m.EVENT); + this.addInput("in", 0); + this.addOutput("out", 0); + this.properties = {url:"tamats.com:55000", room:"lgraph", only_send_changes:!0}; + this._server = null; + this.connectSocket(); + this._last_sent_data = []; + this._last_received_data = []; + "undefined" == typeof SillyClient && console.warn("remember to add SillyClient.js to your project: https://tamats.com/projects/sillyserver/src/sillyclient.js"); + } + var m = w.LiteGraph; + c.title = "WebSocket"; + c.desc = "Send data through a websocket"; + c.prototype.onPropertyChanged = function(c, m) { + "url" == c && this.connectSocket(); + }; + c.prototype.onExecute = function() { + !this._ws && this.properties.url && this.connectSocket(); + if (this._ws && this._ws.readyState == WebSocket.OPEN) { + for (var c = this.properties.room, m = this.properties.only_send_changes, l = 1; l < this.inputs.length; ++l) { + var p = this.getInputData(l); + if (null != p) { + try { + var w = JSON.stringify({type:0, room:c, channel:l, data:p}); + } catch (v) { + continue; + } + m && this._last_sent_data[l] == w || (this._last_sent_data[l] = w, this._ws.send(w)); + } + } + for (l = 1; l < this.outputs.length; ++l) { + this.setOutputData(l, this._last_received_data[l]); + } + "#AFA" == this.boxcolor && (this.boxcolor = "#6C6"); + } + }; + c.prototype.connectSocket = function() { + var c = this, p = this.properties.url; + "ws" != p.substr(0, 2) && (p = "ws://" + p); + this._ws = new WebSocket(p); + this._ws.onopen = function() { + console.log("ready"); + c.boxcolor = "#6C6"; + }; + this._ws.onmessage = function(g) { + c.boxcolor = "#AFA"; + g = JSON.parse(g.data); + if (!g.room || g.room == c.properties.room) { + if (1 == g.type) { + if (g.data.object_class && m[g.data.object_class]) { + var l = null; + try { + l = new m[g.data.object_class](g.data), c.triggerSlot(0, l); + } catch (y) { + } + } else { + c.triggerSlot(0, g.data); + } + } else { + c._last_received_data[g.channel || 0] = g.data; + } + } + }; + this._ws.onerror = function(g) { + console.log("couldnt connect to websocket"); + c.boxcolor = "#E88"; + }; + this._ws.onclose = function(g) { + console.log("connection closed"); + c.boxcolor = "#000"; + }; + }; + c.prototype.send = function(c) { + this._ws && this._ws.readyState == WebSocket.OPEN && this._ws.send(JSON.stringify({type:1, msg:c})); + }; + c.prototype.onAction = function(c, m) { + this._ws && this._ws.readyState == WebSocket.OPEN && this._ws.send({type:1, room:this.properties.room, action:c, data:m}); + }; + c.prototype.onGetInputs = function() { + return [["in", 0]]; + }; + c.prototype.onGetOutputs = function() { + return [["out", 0]]; + }; + m.registerNodeType("network/websocket", c); + p.title = "SillyClient"; + p.desc = "Connects to SillyServer to broadcast messages"; + p.prototype.onPropertyChanged = function(c, m) { + "room" == c && (this.room_widget.value = m); + this.connectSocket(); + }; + p.prototype.setRoom = function(c) { + this.properties.room = c; + this.room_widget.value = c; + this.connectSocket(); + }; + p.prototype.onDrawForeground = function() { + for (var c = 1; c < this.inputs.length; ++c) { + var m = this.inputs[c]; + m.label = "in_" + c; + } + for (c = 1; c < this.outputs.length; ++c) { + m = this.outputs[c], m.label = "out_" + c; + } + }; + p.prototype.onExecute = function() { + if (this._server && this._server.is_connected) { + for (var c = this.properties.only_send_changes, m = 1; m < this.inputs.length; ++m) { + var l = this.getInputData(m), p = this._last_sent_data[m]; + if (null != l) { + if (c) { + var w = !0; + if (l && l.length && p && p.length == l.length && l.constructor !== String) { + for (var v = 0; v < l.length; ++v) { + if (p[v] != l[v]) { + w = !1; + break; + } + } + } else { + this._last_sent_data[m] != l && (w = !1); + } + if (w) { + continue; + } + } + this._server.sendMessage({type:0, channel:m, data:l}); + if (l.length && l.constructor !== String) { + if (this._last_sent_data[m]) { + for (this._last_sent_data[m].length = l.length, v = 0; v < l.length; ++v) { + this._last_sent_data[m][v] = l[v]; + } + } else { + this._last_sent_data[m] = l.constructor === Array ? l.concat() : new l.constructor(l); + } + } else { + this._last_sent_data[m] = l; + } + } + } + for (m = 1; m < this.outputs.length; ++m) { + this.setOutputData(m, this._last_received_data[m]); + } + "#AFA" == this.boxcolor && (this.boxcolor = "#6C6"); + } + }; + p.prototype.connectSocket = function() { + var c = this; + if ("undefined" == typeof SillyClient) { + this._error || console.error("SillyClient node cannot be used, you must include SillyServer.js"), this._error = !0; + } else { + if (this._server = new SillyClient, this._server.on_ready = function() { + console.log("ready"); + c.boxcolor = "#6C6"; + }, this._server.on_message = function(g, l) { + g = null; + try { + g = JSON.parse(l); + } catch (B) { + return; + } + if (1 == g.type) { + if (g.data.object_class && m[g.data.object_class]) { + l = null; + try { + l = new m[g.data.object_class](g.data), c.triggerSlot(0, l); + } catch (B) { + return; + } + } else { + c.triggerSlot(0, g.data); + } + } else { + c._last_received_data[g.channel || 0] = g.data; + } + c.boxcolor = "#AFA"; + }, this._server.on_error = function(g) { + console.log("couldnt connect to websocket"); + c.boxcolor = "#E88"; + }, this._server.on_close = function(g) { + console.log("connection closed"); + c.boxcolor = "#000"; + }, this.properties.url && this.properties.room) { + try { + this._server.connect(this.properties.url, this.properties.room); + } catch (u) { + console.error("SillyServer error: " + u); + this._server = null; + return; + } + this._final_url = this.properties.url + "/" + this.properties.room; + } + } + }; + p.prototype.send = function(c) { + this._server && this._server.is_connected && this._server.sendMessage({type:1, data:c}); + }; + p.prototype.onAction = function(c, m) { + this._server && this._server.is_connected && this._server.sendMessage({type:1, action:c, data:m}); + }; + p.prototype.onGetInputs = function() { + return [["in", 0]]; + }; + p.prototype.onGetOutputs = function() { + return [["out", 0]]; + }; + m.registerNodeType("network/sillyclient", p); +})(this); + +>>>>>>> custom widget custom size support diff --git a/src/litegraph.d.ts b/src/litegraph.d.ts index 6daa534f8..da611ae96 100644 --- a/src/litegraph.d.ts +++ b/src/litegraph.d.ts @@ -62,7 +62,7 @@ export interface IWidget { width: number, posY: number, height: number - ): void; + ): number | undefined; /** * Called by `LGraphCanvas.processNodeWidgets` * https://github.com/jagenjo/litegraph.js/issues/76 @@ -73,6 +73,8 @@ export interface IWidget { pos: Vector2, node: LGraphNode ): void; + /** Called by `LGraphNode.computeSize` */ + computeSize?(width: number): [number, number]; } export interface IButtonWidget extends IWidget { type: "button"; diff --git a/src/litegraph.js b/src/litegraph.js index 36a25459d..54b9a5ee2 100755 --- a/src/litegraph.js +++ b/src/litegraph.js @@ -3173,20 +3173,6 @@ var size = out || new Float32Array([0, 0]); rows = Math.max(rows, 1); var font_size = LiteGraph.NODE_TEXT_SIZE; //although it should be graphcanvas.inner_text_font size - size[1] = (this.constructor.slot_start_y || 0) + rows * LiteGraph.NODE_SLOT_HEIGHT; - - var widgets_height = 0; - if (this.widgets && this.widgets.length) { - widgets_height = this.widgets.length * (LiteGraph.NODE_WIDGET_HEIGHT + 4) + 8; - } - - //compute height using widgets height - if( this.widgets_up ) - size[1] = Math.max( size[1], widgets_height ); - else if( this.widgets_start_y != null ) - size[1] = Math.max( size[1], widgets_height + this.widgets_start_y ); - else - size[1] += widgets_height; var font_size = font_size; var title_width = compute_text_size(this.title); @@ -3221,6 +3207,27 @@ size[0] = Math.max(size[0], LiteGraph.NODE_WIDTH * 1.5); } + size[1] = (this.constructor.slot_start_y || 0) + rows * LiteGraph.NODE_SLOT_HEIGHT; + + var widgets_height = 0; + if (this.widgets && this.widgets.length) { + for (var i = 0, l = this.widgets.length; i < l; ++i) { + if (this.widgets[i].computeSize) + widgets_height += this.widgets[i].computeSize(size[0])[1] + 4; + else + widgets_height += LiteGraph.NODE_WIDGET_HEIGHT + 4; + } + widgets_height += 8; + } + + //compute height using widgets height + if( this.widgets_up ) + size[1] = Math.max( size[1], widgets_height ); + else if( this.widgets_start_y != null ) + size[1] = Math.max( size[1], widgets_height + this.widgets_start_y ); + else + size[1] += widgets_height; + if (this.onResize) { this.onResize(size); } @@ -5566,19 +5573,27 @@ LGraphNode.prototype.executeAction = function(action) this.resizing_node.inputs ? this.resizing_node.inputs.length : 0, this.resizing_node.outputs ? this.resizing_node.outputs.length : 0 ); - var min_height = - max_slots * LiteGraph.NODE_SLOT_HEIGHT + - (this.resizing_node.widgets ? this.resizing_node.widgets.length : 0) * (LiteGraph.NODE_WIDGET_HEIGHT + 4) + 4; - if (this.resizing_node.size[1] < min_height) { - this.resizing_node.size[1] = min_height; - } + if (this.resizing_node.size[0] < LiteGraph.NODE_MIN_WIDTH) { this.resizing_node.size[0] = LiteGraph.NODE_MIN_WIDTH; } - if (this.resizing_node.onResize) { - this.resizing_node.onResize(this.resizing_node.size); - } + var widgets = this.resizing_node.widgets; + var widgets_height = 0; + if (widgets && widgets.length) { + for (var i = 0, l = widgets.length; i < l; ++i) { + if (widgets[i].computeSize) + widgets_height += widgets[i].computeSize(this.resizing_node.size[0])[1] + 4; + else + } + widgets_height += LiteGraph.NODE_WIDGET_HEIGHT + 4; + widgets_height += 8; + } + + if (this.resizing_node.size[1] < min_height) { + var min_height = max_slots * LiteGraph.NODE_SLOT_HEIGHT + widgets_height; + this.resizing_node.size[1] = min_height; + } this.canvas.style.cursor = "se-resize"; this.dirty_canvas = true; @@ -8242,6 +8257,7 @@ LGraphNode.prototype.executeAction = function(action) var margin = 15; for (var i = 0; i < widgets.length; ++i) { + var h = H; var w = widgets[i]; var y = posY; if (w.y) { @@ -8412,11 +8428,11 @@ LGraphNode.prototype.executeAction = function(action) break; default: if (w.draw) { - w.draw(ctx, node, w, y, H); + h = w.draw(ctx, node, width, y, H) || H; } break; } - posY += H + 4; + posY += h + 4; ctx.globalAlpha = this.editor_alpha; } From 4fe32687d29fc224d4e16853b40657a477ec539e Mon Sep 17 00:00:00 2001 From: altarfinch Date: Fri, 8 May 2020 01:40:15 +0200 Subject: [PATCH 36/63] fixed mouse function for custom widget : take custom height into account --- build/litegraph.js | 10 +- build/litegraph.min.js | 1855 ++++++++++++++++++++-------------------- src/litegraph.d.ts | 5 +- src/litegraph.js | 10 +- 4 files changed, 941 insertions(+), 939 deletions(-) diff --git a/build/litegraph.js b/build/litegraph.js index a99693f42..8659453b5 100644 --- a/build/litegraph.js +++ b/build/litegraph.js @@ -20511,7 +20511,6 @@ LGraphNode.prototype.executeAction = function(action) var margin = 15; for (var i = 0; i < widgets.length; ++i) { - var h = H; var w = widgets[i]; var y = posY; if (w.y) { @@ -20682,11 +20681,11 @@ LGraphNode.prototype.executeAction = function(action) break; default: if (w.draw) { - h = w.draw(ctx, node, width, y, H) || H; + w.draw(ctx, node, width, y, H); } break; } - posY += h + 4; + posY += (w.computeSize ? w.computeSize(width)[1] : H) + 4; ctx.globalAlpha = this.editor_alpha; } @@ -20718,7 +20717,8 @@ LGraphNode.prototype.executeAction = function(action) var w = node.widgets[i]; if(!w || w.disabled) continue; - if ( w == active_widget || (x > 6 && x < width - 12 && y > w.last_y && y < w.last_y + LiteGraph.NODE_WIDGET_HEIGHT) ) { + var widget_height = w.computeSize ? w.computeSize(width)[1] : LiteGraph.NODE_WIDGET_HEIGHT; + if ( w == active_widget || (x > 6 && x < width - 12 && y > w.last_y && y < w.last_y + widget_height) ) { //inside widget switch (w.type) { case "button": @@ -20852,7 +20852,7 @@ LGraphNode.prototype.executeAction = function(action) break; default: if (w.mouse) { - w.mouse(ctx, event, [x, y], node); + this.dirty_canvas = w.mouse(event, [x, y], node); } break; } //end switch diff --git a/build/litegraph.min.js b/build/litegraph.min.js index 7a0258a22..4f672d6da 100755 --- a/build/litegraph.min.js +++ b/build/litegraph.min.js @@ -723,39 +723,39 @@ $jscomp.scope = {}; $jscomp.ASSUME_ES5 = !1; $jscomp.ASSUME_NO_NATIVE_MAP = !1; $jscomp.ASSUME_NO_NATIVE_SET = !1; -$jscomp.defineProperty = $jscomp.ASSUME_ES5 || "function" == typeof Object.defineProperties ? Object.defineProperty : function(w, c, p) { - w != Array.prototype && w != Object.prototype && (w[c] = p.value); +$jscomp.defineProperty = $jscomp.ASSUME_ES5 || "function" == typeof Object.defineProperties ? Object.defineProperty : function(v, c, q) { + v != Array.prototype && v != Object.prototype && (v[c] = q.value); }; -$jscomp.getGlobal = function(w) { - return "undefined" != typeof window && window === w ? w : "undefined" != typeof global && null != global ? global : w; +$jscomp.getGlobal = function(v) { + return "undefined" != typeof window && window === v ? v : "undefined" != typeof global && null != global ? global : v; }; $jscomp.global = $jscomp.getGlobal(this); -$jscomp.polyfill = function(w, c, p, m) { +$jscomp.polyfill = function(v, c, q, m) { if (c) { - p = $jscomp.global; - w = w.split("."); - for (m = 0; m < w.length - 1; m++) { - var g = w[m]; - g in p || (p[g] = {}); - p = p[g]; + q = $jscomp.global; + v = v.split("."); + for (m = 0; m < v.length - 1; m++) { + var g = v[m]; + g in q || (q[g] = {}); + q = q[g]; } - w = w[w.length - 1]; - m = p[w]; + v = v[v.length - 1]; + m = q[v]; c = c(m); - c != m && null != c && $jscomp.defineProperty(p, w, {configurable:!0, writable:!0, value:c}); + c != m && null != c && $jscomp.defineProperty(q, v, {configurable:!0, writable:!0, value:c}); } }; -$jscomp.polyfill("Array.prototype.fill", function(w) { - return w ? w : function(c, p, m) { +$jscomp.polyfill("Array.prototype.fill", function(v) { + return v ? v : function(c, q, m) { var g = this.length || 0; - 0 > p && (p = Math.max(0, g + p)); + 0 > q && (q = Math.max(0, g + q)); if (null == m || m > g) { m = g; } m = Number(m); 0 > m && (m = Math.max(0, g + m)); - for (p = Number(p || 0); p < m; p++) { - this[p] = c; + for (q = Number(q || 0); q < m; q++) { + this[q] = c; } return this; }; @@ -767,42 +767,42 @@ $jscomp.initSymbol = function() { $jscomp.global.Symbol || ($jscomp.global.Symbol = $jscomp.Symbol); }; $jscomp.Symbol = function() { - var w = 0; + var v = 0; return function(c) { - return $jscomp.SYMBOL_PREFIX + (c || "") + w++; + return $jscomp.SYMBOL_PREFIX + (c || "") + v++; }; }(); $jscomp.initSymbolIterator = function() { $jscomp.initSymbol(); - var w = $jscomp.global.Symbol.iterator; - w || (w = $jscomp.global.Symbol.iterator = $jscomp.global.Symbol("iterator")); - "function" != typeof Array.prototype[w] && $jscomp.defineProperty(Array.prototype, w, {configurable:!0, writable:!0, value:function() { + var v = $jscomp.global.Symbol.iterator; + v || (v = $jscomp.global.Symbol.iterator = $jscomp.global.Symbol("iterator")); + "function" != typeof Array.prototype[v] && $jscomp.defineProperty(Array.prototype, v, {configurable:!0, writable:!0, value:function() { return $jscomp.arrayIterator(this); }}); $jscomp.initSymbolIterator = function() { }; }; -$jscomp.arrayIterator = function(w) { +$jscomp.arrayIterator = function(v) { var c = 0; return $jscomp.iteratorPrototype(function() { - return c < w.length ? {done:!1, value:w[c++]} : {done:!0}; + return c < v.length ? {done:!1, value:v[c++]} : {done:!0}; }); }; -$jscomp.iteratorPrototype = function(w) { +$jscomp.iteratorPrototype = function(v) { $jscomp.initSymbolIterator(); - w = {next:w}; - w[$jscomp.global.Symbol.iterator] = function() { + v = {next:v}; + v[$jscomp.global.Symbol.iterator] = function() { return this; }; - return w; + return v; }; -$jscomp.iteratorFromArray = function(w, c) { +$jscomp.iteratorFromArray = function(v, c) { $jscomp.initSymbolIterator(); - w instanceof String && (w += ""); - var p = 0, m = {next:function() { - if (p < w.length) { - var g = p++; - return {value:c(g, w[g]), done:!1}; + v instanceof String && (v += ""); + var q = 0, m = {next:function() { + if (q < v.length) { + var g = q++; + return {value:c(g, v[g]), done:!1}; } m.next = function() { return {done:!0, value:void 0}; @@ -814,40 +814,40 @@ $jscomp.iteratorFromArray = function(w, c) { }; return m; }; -$jscomp.polyfill("Array.prototype.values", function(w) { - return w ? w : function() { - return $jscomp.iteratorFromArray(this, function(c, p) { - return p; +$jscomp.polyfill("Array.prototype.values", function(v) { + return v ? v : function() { + return $jscomp.iteratorFromArray(this, function(c, q) { + return q; }); }; }, "es8", "es3"); -$jscomp.polyfill("Array.prototype.keys", function(w) { - return w ? w : function() { +$jscomp.polyfill("Array.prototype.keys", function(v) { + return v ? v : function() { return $jscomp.iteratorFromArray(this, function(c) { return c; }); }; }, "es6", "es3"); -$jscomp.owns = function(w, c) { - return Object.prototype.hasOwnProperty.call(w, c); +$jscomp.owns = function(v, c) { + return Object.prototype.hasOwnProperty.call(v, c); }; -$jscomp.polyfill("Object.values", function(w) { - return w ? w : function(c) { - var p = [], m; +$jscomp.polyfill("Object.values", function(v) { + return v ? v : function(c) { + var q = [], m; for (m in c) { - $jscomp.owns(c, m) && p.push(c[m]); + $jscomp.owns(c, m) && q.push(c[m]); } - return p; + return q; }; }, "es8", "es3"); -(function(w) { +(function(v) { function c(a) { e.debug && console.log("Graph created"); this.list_of_graphcanvas = null; this.clear(); a && this.configure(a); } - function p(a, b, d, h, f, e) { + function q(a, b, d, h, f, e) { this.id = a; this.type = b; this.origin_id = d; @@ -863,7 +863,7 @@ $jscomp.polyfill("Object.values", function(w) { function g(a) { this._ctor(a); } - function u(a, b) { + function r(a, b) { this.offset = new Float32Array([0, 0]); this.scale = 1; this.max_scale = 10; @@ -879,7 +879,7 @@ $jscomp.polyfill("Object.values", function(w) { d = d || {}; this.background_image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; a && a.constructor === String && (a = document.querySelector(a)); - this.ds = new u; + this.ds = new r; this.zoom_modify_alpha = !0; this.title_text_font = "" + e.NODE_TEXT_SIZE + "px Arial"; this.inner_text_font = "normal " + e.NODE_SUBTEXT_SIZE + "px Arial"; @@ -926,7 +926,7 @@ $jscomp.polyfill("Object.values", function(w) { function y(a, b, d, h, f, e) { return d < a && d + f > a && h < b && h + e > b ? !0 : !1; } - function v(a, b) { + function w(a, b) { var d = a[0] + a[2], h = a[1] + a[3], f = b[1] + b[3]; return a[0] > b[0] + b[2] || a[1] > f || d < b[0] || h < b[1] ? !1 : !0; } @@ -1004,7 +1004,7 @@ $jscomp.polyfill("Object.values", function(w) { this.must_update = !0; this.margin = 5; } - var e = w.LiteGraph = {VERSION:0.4, CANVAS_GRID_SIZE:10, NODE_TITLE_HEIGHT:30, NODE_TITLE_TEXT_Y:20, NODE_SLOT_HEIGHT:20, NODE_WIDGET_HEIGHT:20, NODE_WIDTH:140, NODE_MIN_WIDTH:50, NODE_COLLAPSED_RADIUS:10, NODE_COLLAPSED_WIDTH:80, NODE_TITLE_COLOR:"#999", NODE_TEXT_SIZE:14, NODE_TEXT_COLOR:"#AAA", NODE_SUBTEXT_SIZE:12, NODE_DEFAULT_COLOR:"#333", NODE_DEFAULT_BGCOLOR:"#353535", NODE_DEFAULT_BOXCOLOR:"#666", NODE_DEFAULT_SHAPE:"box", DEFAULT_SHADOW_COLOR:"rgba(0,0,0,0.5)", DEFAULT_GROUP_FONT:24, + var e = v.LiteGraph = {VERSION:0.4, CANVAS_GRID_SIZE:10, NODE_TITLE_HEIGHT:30, NODE_TITLE_TEXT_Y:20, NODE_SLOT_HEIGHT:20, NODE_WIDGET_HEIGHT:20, NODE_WIDTH:140, NODE_MIN_WIDTH:50, NODE_COLLAPSED_RADIUS:10, NODE_COLLAPSED_WIDTH:80, NODE_TITLE_COLOR:"#999", NODE_TEXT_SIZE:14, NODE_TEXT_COLOR:"#AAA", NODE_SUBTEXT_SIZE:12, NODE_DEFAULT_COLOR:"#333", NODE_DEFAULT_BGCOLOR:"#353535", NODE_DEFAULT_BOXCOLOR:"#666", NODE_DEFAULT_SHAPE:"box", DEFAULT_SHADOW_COLOR:"rgba(0,0,0,0.5)", DEFAULT_GROUP_FONT:24, WIDGET_BGCOLOR:"#222", WIDGET_OUTLINE_COLOR:"#666", WIDGET_TEXT_COLOR:"#DDD", WIDGET_SECONDARY_TEXT_COLOR:"#999", LINK_COLOR:"#9A9", EVENT_LINK_COLOR:"#A86", CONNECTING_LINK_COLOR:"#AFA", MAX_NUMBER_OF_NODES:1000, DEFAULT_POSITION:[100, 100], VALID_SHAPES:["default", "box", "round", "card"], BOX_SHAPE:1, ROUND_SHAPE:2, CIRCLE_SHAPE:3, CARD_SHAPE:4, ARROW_SHAPE:5, INPUT:1, OUTPUT:2, EVENT:-1, ACTION:-1, ALWAYS:0, ON_EVENT:1, NEVER:2, ON_TRIGGER:3, UP:1, DOWN:2, LEFT:3, RIGHT:4, CENTER:5, STRAIGHT_LINK:0, LINEAR_LINK:1, SPLINE_LINK:2, NORMAL_TITLE:0, NO_TITLE:1, TRANSPARENT_TITLE:2, AUTOHIDE_TITLE:3, proxy:null, node_images_path:"", debug:!1, catch_exceptions:!0, throw_errors:!0, allow_scripts:!1, registered_node_types:{}, node_types_by_file_extension:{}, Nodes:{}, searchbox_extras:{}, registerNodeType:function(a, b) { if (!b.prototype) { @@ -1257,7 +1257,7 @@ $jscomp.polyfill("Object.values", function(w) { } : function() { return (new Date).getTime(); }; - w.LGraph = e.LGraph = c; + v.LGraph = e.LGraph = c; c.supported_types = ["number", "string", "boolean"]; c.prototype.getSupportedTypes = function() { return this.supported_types || c.supported_types; @@ -1426,24 +1426,24 @@ $jscomp.polyfill("Object.values", function(w) { }; c.prototype.computeExecutionOrder = function(a, b) { for (var d = [], h = [], f = {}, x = {}, c = {}, k = 0, n = this._nodes.length; k < n; ++k) { - var q = this._nodes[k]; - if (!a || q.onExecute) { - f[q.id] = q; + var p = this._nodes[k]; + if (!a || p.onExecute) { + f[p.id] = p; var l = 0; - if (q.inputs) { - for (var r = 0, g = q.inputs.length; r < g; r++) { - q.inputs[r] && null != q.inputs[r].link && (l += 1); + if (p.inputs) { + for (var t = 0, g = p.inputs.length; t < g; t++) { + p.inputs[t] && null != p.inputs[t].link && (l += 1); } } - 0 == l ? (h.push(q), b && (q._level = 1)) : (b && (q._level = 0), c[q.id] = l); + 0 == l ? (h.push(p), b && (p._level = 1)) : (b && (p._level = 0), c[p.id] = l); } } for (; 0 != h.length;) { - if (q = h.shift(), d.push(q), delete f[q.id], q.outputs) { - for (k = 0; k < q.outputs.length; k++) { - if (a = q.outputs[k], null != a && null != a.links && 0 != a.links.length) { - for (r = 0; r < a.links.length; r++) { - (n = this.links[a.links[r]]) && !x[n.id] && (l = this.getNodeById(n.target_id), null == l ? x[n.id] = !0 : (b && (!l._level || l._level <= q._level) && (l._level = q._level + 1), x[n.id] = !0, --c[l.id], 0 == c[l.id] && h.push(l))); + if (p = h.shift(), d.push(p), delete f[p.id], p.outputs) { + for (k = 0; k < p.outputs.length; k++) { + if (a = p.outputs[k], null != a && null != a.links && 0 != a.links.length) { + for (t = 0; t < a.links.length; t++) { + (n = this.links[a.links[t]]) && !x[n.id] && (l = this.getNodeById(n.target_id), null == l ? x[n.id] = !0 : (b && (!l._level || l._level <= p._level) && (l._level = p._level + 1), x[n.id] = !0, --c[l.id], 0 == c[l.id] && h.push(l))); } } } @@ -1878,7 +1878,7 @@ $jscomp.polyfill("Object.values", function(w) { var h = this.links[b]; if (!h.serialize) { console.warn("weird LLink bug, link info is not a LLink but a regular object"); - var f = new p; + var f = new q; for (b in h) { f[b] = h[b]; } @@ -1900,7 +1900,7 @@ $jscomp.polyfill("Object.values", function(w) { for (var d = [], h = 0; h < a.links.length; ++h) { var f = a.links[h]; if (f) { - var c = new p; + var c = new q; c.configure(f); d[c.id] = c; } else { @@ -1953,14 +1953,14 @@ $jscomp.polyfill("Object.values", function(w) { }; c.prototype.onNodeTrace = function(a, b, d) { }; - p.prototype.configure = function(a) { + q.prototype.configure = function(a) { a.constructor === Array ? (this.id = a[0], this.origin_id = a[1], this.origin_slot = a[2], this.target_id = a[3], this.target_slot = a[4], this.type = a[5]) : (this.id = a.id, this.type = a.type, this.origin_id = a.origin_id, this.origin_slot = a.origin_slot, this.target_id = a.target_id, this.target_slot = a.target_slot); }; - p.prototype.serialize = function() { + q.prototype.serialize = function() { return [this.id, this.origin_id, this.origin_slot, this.target_id, this.target_slot, this.type]; }; - e.LLink = p; - w.LGraphNode = e.LGraphNode = m; + e.LLink = q; + v.LGraphNode = e.LGraphNode = m; m.prototype._ctor = function(a) { this.title = a || "Unnamed"; this.size = [e.NODE_WIDTH, 60]; @@ -2384,7 +2384,7 @@ $jscomp.polyfill("Object.values", function(w) { a = Math.max(a, 1); var h = e.NODE_TEXT_SIZE, f = d(this.title), c = 0, k = 0; if (this.inputs) { - for (var n = 0, q = this.inputs.length; n < q; ++n) { + for (var n = 0, p = this.inputs.length; n < p; ++n) { var l = this.inputs[n]; l = l.label || l.name || ""; l = d(l); @@ -2392,7 +2392,7 @@ $jscomp.polyfill("Object.values", function(w) { } } if (this.outputs) { - for (n = 0, q = this.outputs.length; n < q; ++n) { + for (n = 0, p = this.outputs.length; n < p; ++n) { l = this.outputs[n], l = l.label || l.name || "", l = d(l), k < l && (k = l); } } @@ -2403,7 +2403,7 @@ $jscomp.polyfill("Object.values", function(w) { a = 0; if (this.widgets && this.widgets.length) { n = 0; - for (q = this.widgets.length; n < q; ++n) { + for (p = this.widgets.length; n < p; ++n) { a = this.widgets[n].computeSize ? a + (this.widgets[n].computeSize(b[0])[1] + 4) : a + (e.NODE_WIDGET_HEIGHT + 4); } a += 8; @@ -2561,7 +2561,7 @@ $jscomp.polyfill("Object.values", function(w) { } var f = b.inputs[d], c = null; if (e.isValidConnection(h.type, f.type)) { - c = new p(++this.graph.last_link_id, f.type, this.id, a, b.id, d); + c = new q(++this.graph.last_link_id, f.type, this.id, a, b.id, d); this.graph.links[c.id] = c; null == h.links && (h.links = []); h.links.push(c.id); @@ -2766,7 +2766,7 @@ $jscomp.polyfill("Object.values", function(w) { m.prototype.localToScreen = function(a, b, d) { return [(a + this.pos[0]) * d.scale + d.offset[0], (b + this.pos[1]) * d.scale + d.offset[1]]; }; - w.LGraphGroup = e.LGraphGroup = g; + v.LGraphGroup = e.LGraphGroup = g; g.prototype._ctor = function(a) { this.title = a || "Group"; this.font_size = 24; @@ -2813,13 +2813,13 @@ $jscomp.polyfill("Object.values", function(w) { for (var a = this.graph._nodes, b = new Float32Array(4), d = 0; d < a.length; ++d) { var h = a[d]; h.getBounding(b); - v(this._bounding, b) && this._nodes.push(h); + w(this._bounding, b) && this._nodes.push(h); } }; g.prototype.isPointInside = m.prototype.isPointInside; g.prototype.setDirtyCanvas = m.prototype.setDirtyCanvas; - e.DragAndScale = u; - u.prototype.bindEvents = function(a) { + e.DragAndScale = r; + r.prototype.bindEvents = function(a) { this.last_mouse = new Float32Array(2); this._binded_mouse_callback = this.onMouse.bind(this); a.addEventListener("mousedown", this._binded_mouse_callback); @@ -2827,7 +2827,7 @@ $jscomp.polyfill("Object.values", function(w) { a.addEventListener("mousewheel", this._binded_mouse_callback, !1); a.addEventListener("wheel", this._binded_mouse_callback, !1); }; - u.prototype.computeVisibleArea = function() { + r.prototype.computeVisibleArea = function() { if (this.element) { var a = -this.offset[0], b = -this.offset[1], d = a + this.element.width / this.scale, h = b + this.element.height / this.scale; this.visible_area[0] = a; @@ -2838,7 +2838,7 @@ $jscomp.polyfill("Object.values", function(w) { this.visible_area[0] = this.visible_area[1] = this.visible_area[2] = this.visible_area[3] = 0; } }; - u.prototype.onMouse = function(a) { + r.prototype.onMouse = function(a) { if (this.enabled) { var b = this.element, d = b.getBoundingClientRect(), h = a.clientX - d.left; d = a.clientY - d.top; @@ -2869,27 +2869,27 @@ $jscomp.polyfill("Object.values", function(w) { return !1; } }; - u.prototype.toCanvasContext = function(a) { + r.prototype.toCanvasContext = function(a) { a.scale(this.scale, this.scale); a.translate(this.offset[0], this.offset[1]); }; - u.prototype.convertOffsetToCanvas = function(a) { + r.prototype.convertOffsetToCanvas = function(a) { return [(a[0] + this.offset[0]) * this.scale, (a[1] + this.offset[1]) * this.scale]; }; - u.prototype.convertCanvasToOffset = function(a, b) { + r.prototype.convertCanvasToOffset = function(a, b) { b = b || [0, 0]; b[0] = a[0] / this.scale - this.offset[0]; b[1] = a[1] / this.scale - this.offset[1]; return b; }; - u.prototype.mouseDrag = function(a, b) { + r.prototype.mouseDrag = function(a, b) { this.offset[0] += a / this.scale; this.offset[1] += b / this.scale; if (this.onredraw) { this.onredraw(this); } }; - u.prototype.changeScale = function(a, b) { + r.prototype.changeScale = function(a, b) { a < this.min_scale ? a = this.min_scale : a > this.max_scale && (a = this.max_scale); if (a != this.scale && this.element) { var d = this.element.getBoundingClientRect(); @@ -2898,15 +2898,15 @@ $jscomp.polyfill("Object.values", function(w) { } } }; - u.prototype.changeDeltaScale = function(a, b) { + r.prototype.changeDeltaScale = function(a, b) { this.changeScale(this.scale * a, b); }; - u.prototype.reset = function() { + r.prototype.reset = function() { this.scale = 1; this.offset[0] = 0; this.offset[1] = 0; }; - w.LGraphCanvas = e.LGraphCanvas = l; + v.LGraphCanvas = e.LGraphCanvas = l; l.link_type_colors = {"-1":e.EVENT_LINK_COLOR, number:"#AAA", node:"#DCA"}; l.gradients = {}; l.prototype.clear = function() { @@ -3092,10 +3092,10 @@ $jscomp.polyfill("Object.values", function(w) { } else { if (d.outputs) { for (var k = 0, n = d.outputs.length; k < n; ++k) { - var q = d.outputs[k], g = d.getConnectionPos(!1, k); + var p = d.outputs[k], g = d.getConnectionPos(!1, k); if (y(a.canvasX, a.canvasY, g[0] - 15, g[1] - 10, 30, 20)) { this.connecting_node = d; - this.connecting_output = q; + this.connecting_output = p; this.connecting_pos = d.getConnectionPos(!1, k); this.connecting_slot = k; a.shiftKey && d.disconnectOutput(k); @@ -3115,7 +3115,7 @@ $jscomp.polyfill("Object.values", function(w) { } if (d.inputs) { for (k = 0, n = d.inputs.length; k < n; ++k) { - if (q = d.inputs[k], g = d.getConnectionPos(!0, k), y(a.canvasX, a.canvasY, g[0] - 15, g[1] - 10, 30, 20)) { + if (p = d.inputs[k], g = d.getConnectionPos(!0, k), y(a.canvasX, a.canvasY, g[0] - 15, g[1] - 10, 30, 20)) { if (f) { if (d.onInputDblClick) { d.onInputDblClick(k, a); @@ -3125,8 +3125,8 @@ $jscomp.polyfill("Object.values", function(w) { d.onInputClick(k, a); } } - if (null !== q.link) { - h = this.graph.links[q.link]; + if (null !== p.link) { + h = this.graph.links[p.link]; d.disconnectInput(k); if (this.allow_reconnect_links || a.shiftKey) { this.connecting_node = this.graph._nodes_by_id[h.origin_id], this.connecting_slot = h.origin_slot, this.connecting_output = this.connecting_node.outputs[this.connecting_slot], this.connecting_pos = this.connecting_node.getConnectionPos(!1, this.connecting_slot); @@ -3308,7 +3308,7 @@ $jscomp.polyfill("Object.values", function(w) { this.dragging_rectangle[3] = f; f = []; for (c = 0; c < b.length; ++c) { - h = b[c], h.getBounding(d), v(this.dragging_rectangle, d) && f.push(h); + h = b[c], h.getBounding(d), w(this.dragging_rectangle, d) && f.push(h); } f.length && this.selectNodes(f); } @@ -3661,7 +3661,7 @@ $jscomp.polyfill("Object.values", function(w) { a = a || this.graph._nodes; for (var d = 0, h = a.length; d < h; ++d) { var f = a[d]; - (!this.live_mode || f.onDrawBackground || f.onDrawForeground) && v(this.visible_area, f.getBounding(C)) && b.push(f); + (!this.live_mode || f.onDrawBackground || f.onDrawForeground) && w(this.visible_area, f.getBounding(C)) && b.push(f); } return b; }; @@ -3836,8 +3836,8 @@ $jscomp.polyfill("Object.values", function(w) { var n = a.horizontal; if (a.flags.collapsed) { b.font = this.inner_text_font; - var q = a.getTitle ? a.getTitle() : a.title; - null != q && (a._collapsed_width = Math.min(a.size[0], b.measureText(q).width + 2 * e.NODE_TITLE_HEIGHT), D[0] = a._collapsed_width, D[1] = 0); + var p = a.getTitle ? a.getTitle() : a.title; + null != p && (a._collapsed_width = Math.min(a.size[0], b.measureText(p).width + 2 * e.NODE_TITLE_HEIGHT), D[0] = a._collapsed_width, D[1] = 0); } a.clip_area && (b.save(), b.beginPath(), k == e.BOX_SHAPE ? b.rect(0, 0, D[0], D[1]) : k == e.ROUND_SHAPE ? b.roundRect(0, 0, D[0], D[1], 10) : k == e.CIRCLE_SHAPE && b.arc(0.5 * D[0], 0.5 * D[1], 0.5 * D[0], 0, 2 * Math.PI), b.clip()); a.has_errors && (h = "red"); @@ -3851,7 +3851,7 @@ $jscomp.polyfill("Object.values", function(w) { h = !f; k = this.connecting_output; b.lineWidth = 1; - q = 0; + p = 0; var l = new Float32Array(2); if (!a.flags.collapsed) { if (a.inputs) { @@ -3860,16 +3860,16 @@ $jscomp.polyfill("Object.values", function(w) { b.globalAlpha = c; this.connecting_node && !e.isValidConnection(g.type, k.type) && (b.globalAlpha = 0.4 * c); b.fillStyle = null != g.link ? g.color_on || this.default_connection_color.input_on : g.color_off || this.default_connection_color.input_off; - var r = a.getConnectionPos(!0, d, l); - r[0] -= a.pos[0]; - r[1] -= a.pos[1]; - q < r[1] + 0.5 * e.NODE_SLOT_HEIGHT && (q = r[1] + 0.5 * e.NODE_SLOT_HEIGHT); + var t = a.getConnectionPos(!0, d, l); + t[0] -= a.pos[0]; + t[1] -= a.pos[1]; + p < t[1] + 0.5 * e.NODE_SLOT_HEIGHT && (p = t[1] + 0.5 * e.NODE_SLOT_HEIGHT); b.beginPath(); - g.type === e.EVENT || g.shape === e.BOX_SHAPE ? n ? b.rect(r[0] - 5 + 0.5, r[1] - 8 + 0.5, 10, 14) : b.rect(r[0] - 6 + 0.5, r[1] - 5 + 0.5, 14, 10) : g.shape === e.ARROW_SHAPE ? (b.moveTo(r[0] + 8, r[1] + 0.5), b.lineTo(r[0] - 4, r[1] + 6 + 0.5), b.lineTo(r[0] - 4, r[1] - 6 + 0.5), b.closePath()) : f ? b.rect(r[0] - 4, r[1] - 4, 8, 8) : b.arc(r[0], r[1], 4, 0, 2 * Math.PI); + g.type === e.EVENT || g.shape === e.BOX_SHAPE ? n ? b.rect(t[0] - 5 + 0.5, t[1] - 8 + 0.5, 10, 14) : b.rect(t[0] - 6 + 0.5, t[1] - 5 + 0.5, 14, 10) : g.shape === e.ARROW_SHAPE ? (b.moveTo(t[0] + 8, t[1] + 0.5), b.lineTo(t[0] - 4, t[1] + 6 + 0.5), b.lineTo(t[0] - 4, t[1] - 6 + 0.5), b.closePath()) : f ? b.rect(t[0] - 4, t[1] - 4, 8, 8) : b.arc(t[0], t[1], 4, 0, 2 * Math.PI); b.fill(); if (h) { var m = null != g.label ? g.label : g.name; - m && (b.fillStyle = e.NODE_TEXT_COLOR, n || g.dir == e.UP ? b.fillText(m, r[0], r[1] - 10) : b.fillText(m, r[0] + 10, r[1] + 5)); + m && (b.fillStyle = e.NODE_TEXT_COLOR, n || g.dir == e.UP ? b.fillText(m, t[0], t[1] - 10) : b.fillText(m, t[0] + 10, t[1] + 5)); } } } @@ -3878,16 +3878,16 @@ $jscomp.polyfill("Object.values", function(w) { b.strokeStyle = "black"; if (a.outputs) { for (d = 0; d < a.outputs.length; d++) { - if (g = a.outputs[d], r = a.getConnectionPos(!1, d, l), r[0] -= a.pos[0], r[1] -= a.pos[1], q < r[1] + 0.5 * e.NODE_SLOT_HEIGHT && (q = r[1] + 0.5 * e.NODE_SLOT_HEIGHT), b.fillStyle = g.links && g.links.length ? g.color_on || this.default_connection_color.output_on : g.color_off || this.default_connection_color.output_off, b.beginPath(), g.type === e.EVENT || g.shape === e.BOX_SHAPE ? n ? b.rect(r[0] - 5 + 0.5, r[1] - 8 + 0.5, 10, 14) : b.rect(r[0] - 6 + 0.5, r[1] - 5 + 0.5, 14, 10) : - g.shape === e.ARROW_SHAPE ? (b.moveTo(r[0] + 8, r[1] + 0.5), b.lineTo(r[0] - 4, r[1] + 6 + 0.5), b.lineTo(r[0] - 4, r[1] - 6 + 0.5), b.closePath()) : f ? b.rect(r[0] - 4, r[1] - 4, 8, 8) : b.arc(r[0], r[1], 4, 0, 2 * Math.PI), b.fill(), f || b.stroke(), h && (m = null != g.label ? g.label : g.name)) { - b.fillStyle = e.NODE_TEXT_COLOR, n || g.dir == e.DOWN ? b.fillText(m, r[0], r[1] - 8) : b.fillText(m, r[0] - 10, r[1] + 5); + if (g = a.outputs[d], t = a.getConnectionPos(!1, d, l), t[0] -= a.pos[0], t[1] -= a.pos[1], p < t[1] + 0.5 * e.NODE_SLOT_HEIGHT && (p = t[1] + 0.5 * e.NODE_SLOT_HEIGHT), b.fillStyle = g.links && g.links.length ? g.color_on || this.default_connection_color.output_on : g.color_off || this.default_connection_color.output_off, b.beginPath(), g.type === e.EVENT || g.shape === e.BOX_SHAPE ? n ? b.rect(t[0] - 5 + 0.5, t[1] - 8 + 0.5, 10, 14) : b.rect(t[0] - 6 + 0.5, t[1] - 5 + 0.5, 14, 10) : + g.shape === e.ARROW_SHAPE ? (b.moveTo(t[0] + 8, t[1] + 0.5), b.lineTo(t[0] - 4, t[1] + 6 + 0.5), b.lineTo(t[0] - 4, t[1] - 6 + 0.5), b.closePath()) : f ? b.rect(t[0] - 4, t[1] - 4, 8, 8) : b.arc(t[0], t[1], 4, 0, 2 * Math.PI), b.fill(), f || b.stroke(), h && (m = null != g.label ? g.label : g.name)) { + b.fillStyle = e.NODE_TEXT_COLOR, n || g.dir == e.DOWN ? b.fillText(m, t[0], t[1] - 8) : b.fillText(m, t[0] - 10, t[1] + 5); } } } b.textAlign = "left"; b.globalAlpha = 1; if (a.widgets) { - g = q; + g = p; if (n || a.widgets_up) { g = 2; } @@ -3946,22 +3946,22 @@ $jscomp.polyfill("Object.values", function(w) { a.fillText(b, d[0], d[1] - 15 - 24 * 0.3); } }; - var t = new Float32Array(4); + var u = new Float32Array(4); l.prototype.drawNodeShape = function(a, b, d, h, f, c, k) { b.strokeStyle = h; b.fillStyle = f; f = e.NODE_TITLE_HEIGHT; - var n = 0.5 > this.ds.scale, q = a._shape || a.constructor.shape || e.ROUND_SHAPE, x = a.constructor.title_mode, g = !0; + var n = 0.5 > this.ds.scale, p = a._shape || a.constructor.shape || e.ROUND_SHAPE, x = a.constructor.title_mode, g = !0; x == e.TRANSPARENT_TITLE ? g = !1 : x == e.AUTOHIDE_TITLE && k && (g = !0); - t[0] = 0; - t[1] = g ? -f : 0; - t[2] = d[0] + 1; - t[3] = g ? d[1] + f : d[1]; + u[0] = 0; + u[1] = g ? -f : 0; + u[2] = d[0] + 1; + u[3] = g ? d[1] + f : d[1]; k = b.globalAlpha; b.beginPath(); - q == e.BOX_SHAPE || n ? b.fillRect(t[0], t[1], t[2], t[3]) : q == e.ROUND_SHAPE || q == e.CARD_SHAPE ? b.roundRect(t[0], t[1], t[2], t[3], this.round_radius, q == e.CARD_SHAPE ? 0 : this.round_radius) : q == e.CIRCLE_SHAPE && b.arc(0.5 * d[0], 0.5 * d[1], 0.5 * d[0], 0, 2 * Math.PI); + p == e.BOX_SHAPE || n ? b.fillRect(u[0], u[1], u[2], u[3]) : p == e.ROUND_SHAPE || p == e.CARD_SHAPE ? b.roundRect(u[0], u[1], u[2], u[3], this.round_radius, p == e.CARD_SHAPE ? 0 : this.round_radius) : p == e.CIRCLE_SHAPE && b.arc(0.5 * d[0], 0.5 * d[1], 0.5 * d[0], 0, 2 * Math.PI); b.fill(); - a.flags.collapsed || (b.shadowColor = "transparent", b.fillStyle = "rgba(0,0,0,0.2)", b.fillRect(0, -1, t[2], 2)); + a.flags.collapsed || (b.shadowColor = "transparent", b.fillStyle = "rgba(0,0,0,0.2)", b.fillRect(0, -1, u[2], 2)); b.shadowColor = "transparent"; if (a.onDrawBackground) { a.onDrawBackground(b, this, this.canvas); @@ -3974,14 +3974,14 @@ $jscomp.polyfill("Object.values", function(w) { g = a.constructor.title_color || h; a.flags.collapsed && (b.shadowColor = e.DEFAULT_SHADOW_COLOR); if (this.use_gradients) { - var r = l.gradients[g]; - r || (r = l.gradients[g] = b.createLinearGradient(0, 0, 400, 0), r.addColorStop(0, g), r.addColorStop(1, "#000")); - b.fillStyle = r; + var t = l.gradients[g]; + t || (t = l.gradients[g] = b.createLinearGradient(0, 0, 400, 0), t.addColorStop(0, g), t.addColorStop(1, "#000")); + b.fillStyle = t; } else { b.fillStyle = g; } b.beginPath(); - q == e.BOX_SHAPE || n ? b.rect(0, -f, d[0] + 1, f) : (q == e.ROUND_SHAPE || q == e.CARD_SHAPE) && b.roundRect(0, -f, d[0] + 1, f, this.round_radius, a.flags.collapsed ? this.round_radius : 0); + p == e.BOX_SHAPE || n ? b.rect(0, -f, d[0] + 1, f) : (p == e.ROUND_SHAPE || p == e.CARD_SHAPE) && b.roundRect(0, -f, d[0] + 1, f, this.round_radius, a.flags.collapsed ? this.round_radius : 0); b.fill(); b.shadowColor = "transparent"; } @@ -3989,7 +3989,7 @@ $jscomp.polyfill("Object.values", function(w) { if (a.onDrawTitleBox) { a.onDrawTitleBox(b, f, d, this.ds.scale); } else { - q == e.ROUND_SHAPE || q == e.CIRCLE_SHAPE || q == e.CARD_SHAPE ? (n && (b.fillStyle = "black", b.beginPath(), b.arc(0.5 * f, -0.5 * f, 6, 0, 2 * Math.PI), b.fill()), b.fillStyle = a.boxcolor || e.NODE_DEFAULT_BOXCOLOR, n ? b.fillRect(0.5 * f - 5, -0.5 * f - 5, 10, 10) : (b.beginPath(), b.arc(0.5 * f, -0.5 * f, 5, 0, 2 * Math.PI), b.fill())) : (n && (b.fillStyle = "black", b.fillRect(0.5 * (f - 10) - 1, -0.5 * (f + 10) - 1, 12, 12)), b.fillStyle = a.boxcolor || e.NODE_DEFAULT_BOXCOLOR, b.fillRect(0.5 * + p == e.ROUND_SHAPE || p == e.CIRCLE_SHAPE || p == e.CARD_SHAPE ? (n && (b.fillStyle = "black", b.beginPath(), b.arc(0.5 * f, -0.5 * f, 6, 0, 2 * Math.PI), b.fill()), b.fillStyle = a.boxcolor || e.NODE_DEFAULT_BOXCOLOR, n ? b.fillRect(0.5 * f - 5, -0.5 * f - 5, 10, 10) : (b.beginPath(), b.arc(0.5 * f, -0.5 * f, 5, 0, 2 * Math.PI), b.fill())) : (n && (b.fillStyle = "black", b.fillRect(0.5 * (f - 10) - 1, -0.5 * (f + 10) - 1, 12, 12)), b.fillStyle = a.boxcolor || e.NODE_DEFAULT_BOXCOLOR, b.fillRect(0.5 * (f - 10), -0.5 * (f + 10), 10, 10)); } b.globalAlpha = k; @@ -4003,20 +4003,20 @@ $jscomp.polyfill("Object.values", function(w) { } if (c) { if (a.onBounding) { - a.onBounding(t); + a.onBounding(u); } - x == e.TRANSPARENT_TITLE && (t[1] -= f, t[3] += f); + x == e.TRANSPARENT_TITLE && (u[1] -= f, u[3] += f); b.lineWidth = 1; b.globalAlpha = 0.8; b.beginPath(); - q == e.BOX_SHAPE ? b.rect(-6 + t[0], -6 + t[1], 12 + t[2], 12 + t[3]) : q == e.ROUND_SHAPE || q == e.CARD_SHAPE && a.flags.collapsed ? b.roundRect(-6 + t[0], -6 + t[1], 12 + t[2], 12 + t[3], 2 * this.round_radius) : q == e.CARD_SHAPE ? b.roundRect(-6 + t[0], -6 + t[1], 12 + t[2], 12 + t[3], 2 * this.round_radius, 2) : q == e.CIRCLE_SHAPE && b.arc(0.5 * d[0], 0.5 * d[1], 0.5 * d[0] + 6, 0, 2 * Math.PI); + p == e.BOX_SHAPE ? b.rect(-6 + u[0], -6 + u[1], 12 + u[2], 12 + u[3]) : p == e.ROUND_SHAPE || p == e.CARD_SHAPE && a.flags.collapsed ? b.roundRect(-6 + u[0], -6 + u[1], 12 + u[2], 12 + u[3], 2 * this.round_radius) : p == e.CARD_SHAPE ? b.roundRect(-6 + u[0], -6 + u[1], 12 + u[2], 12 + u[3], 2 * this.round_radius, 2) : p == e.CIRCLE_SHAPE && b.arc(0.5 * d[0], 0.5 * d[1], 0.5 * d[0] + 6, 0, 2 * Math.PI); b.strokeStyle = "#FFF"; b.stroke(); b.strokeStyle = h; b.globalAlpha = 1; } }; - var G = new Float32Array(4), n = new Float32Array(4), q = new Float32Array(2), k = new Float32Array(2); + var G = new Float32Array(4), n = new Float32Array(4), p = new Float32Array(2), k = new Float32Array(2); l.prototype.drawConnections = function(a) { var b = e.getTime(), d = this.visible_area; G[0] = d[0] - 20; @@ -4036,24 +4036,24 @@ $jscomp.polyfill("Object.values", function(w) { if (g && null != g.link && (g = this.graph.links[g.link])) { var m = this.graph.getNodeById(g.origin_id); if (null != m) { - var u = g.origin_slot; - var A = -1 == u ? [m.pos[0] + 10, m.pos[1] + 10] : m.getConnectionPos(!1, u, q); - var r = c.getConnectionPos(!0, l, k); + var r = g.origin_slot; + var A = -1 == r ? [m.pos[0] + 10, m.pos[1] + 10] : m.getConnectionPos(!1, r, p); + var t = c.getConnectionPos(!0, l, k); n[0] = A[0]; n[1] = A[1]; - n[2] = r[0] - A[0]; - n[3] = r[1] - A[1]; + n[2] = t[0] - A[0]; + n[3] = t[1] - A[1]; 0 > n[2] && (n[0] += n[2], n[2] = Math.abs(n[2])); 0 > n[3] && (n[1] += n[3], n[3] = Math.abs(n[3])); - if (v(n, G)) { - var K = m.outputs[u]; - u = c.inputs[l]; - if (K && u && (m = K.dir || (m.horizontal ? e.DOWN : e.RIGHT), u = u.dir || (c.horizontal ? e.UP : e.LEFT), this.renderLink(a, A, r, g, !1, 0, null, m, u), g && g._last_time && 1000 > b - g._last_time)) { - K = 2.0 - 0.002 * (b - g._last_time); - var p = a.globalAlpha; - a.globalAlpha = p * K; - this.renderLink(a, A, r, g, !0, K, "white", m, u); - a.globalAlpha = p; + if (w(n, G)) { + var q = m.outputs[r]; + r = c.inputs[l]; + if (q && r && (m = q.dir || (m.horizontal ? e.DOWN : e.RIGHT), r = r.dir || (c.horizontal ? e.UP : e.LEFT), this.renderLink(a, A, t, g, !1, 0, null, m, r), g && g._last_time && 1000 > b - g._last_time)) { + q = 2.0 - 0.002 * (b - g._last_time); + var M = a.globalAlpha; + a.globalAlpha = M * q; + this.renderLink(a, A, t, g, !0, q, "white", m, r); + a.globalAlpha = M; } } } @@ -4063,24 +4063,24 @@ $jscomp.polyfill("Object.values", function(w) { } a.globalAlpha = 1; }; - l.prototype.renderLink = function(a, b, d, h, f, c, k, n, q, g) { + l.prototype.renderLink = function(a, b, d, h, f, c, k, n, p, g) { h && this.visible_links.push(h); !k && h && (k = h.color || l.link_type_colors[h.type]); k || (k = this.default_link_color); null != h && this.highlighted_links[h.id] && (k = "#FFF"); n = n || e.RIGHT; - q = q || e.LEFT; + p = p || e.LEFT; var x = B(b, d); this.render_connections_border && 0.6 < this.ds.scale && (a.lineWidth = this.connections_width + 4); a.lineJoin = "round"; g = g || 1; 1 < g && (a.lineWidth = 0.5); a.beginPath(); - for (var r = 0; r < g; r += 1) { - var m = 5 * (r - 0.5 * (g - 1)); + for (var t = 0; t < g; t += 1) { + var m = 5 * (t - 0.5 * (g - 1)); if (this.links_render_mode == e.SPLINE_LINK) { a.moveTo(b[0], b[1] + m); - var H = 0, u = 0, p = 0, I = 0; + var H = 0, r = 0, q = 0, I = 0; switch(n) { case e.LEFT: H = -0.25 * x; @@ -4089,17 +4089,17 @@ $jscomp.polyfill("Object.values", function(w) { H = 0.25 * x; break; case e.UP: - u = -0.25 * x; + r = -0.25 * x; break; case e.DOWN: - u = 0.25 * x; + r = 0.25 * x; } - switch(q) { + switch(p) { case e.LEFT: - p = -0.25 * x; + q = -0.25 * x; break; case e.RIGHT: - p = 0.25 * x; + q = 0.25 * x; break; case e.UP: I = -0.25 * x; @@ -4107,11 +4107,11 @@ $jscomp.polyfill("Object.values", function(w) { case e.DOWN: I = 0.25 * x; } - a.bezierCurveTo(b[0] + H, b[1] + u + m, d[0] + p, d[1] + I + m, d[0], d[1] + m); + a.bezierCurveTo(b[0] + H, b[1] + r + m, d[0] + q, d[1] + I + m, d[0], d[1] + m); } else { if (this.links_render_mode == e.LINEAR_LINK) { a.moveTo(b[0], b[1] + m); - I = p = u = H = 0; + I = q = r = H = 0; switch(n) { case e.LEFT: H = -1; @@ -4120,17 +4120,17 @@ $jscomp.polyfill("Object.values", function(w) { H = 1; break; case e.UP: - u = -1; + r = -1; break; case e.DOWN: - u = 1; + r = 1; } - switch(q) { + switch(p) { case e.LEFT: - p = -1; + q = -1; break; case e.RIGHT: - p = 1; + q = 1; break; case e.UP: I = -1; @@ -4138,12 +4138,12 @@ $jscomp.polyfill("Object.values", function(w) { case e.DOWN: I = 1; } - a.lineTo(b[0] + 15 * H, b[1] + 15 * u + m); - a.lineTo(d[0] + 15 * p, d[1] + 15 * I + m); + a.lineTo(b[0] + 15 * H, b[1] + 15 * r + m); + a.lineTo(d[0] + 15 * q, d[1] + 15 * I + m); a.lineTo(d[0], d[1] + m); } else { if (this.links_render_mode == e.STRAIGHT_LINK) { - a.moveTo(b[0], b[1]), m = b[0], H = b[1], u = d[0], p = d[1], n == e.RIGHT ? m += 10 : H += 10, q == e.LEFT ? u -= 10 : p -= 10, a.lineTo(m, H), a.lineTo(0.5 * (m + u), H), a.lineTo(0.5 * (m + u), p), a.lineTo(u, p), a.lineTo(d[0], d[1]); + a.moveTo(b[0], b[1]), m = b[0], H = b[1], r = d[0], q = d[1], n == e.RIGHT ? m += 10 : H += 10, p == e.LEFT ? r -= 10 : q -= 10, a.lineTo(m, H), a.lineTo(0.5 * (m + r), H), a.lineTo(0.5 * (m + r), q), a.lineTo(r, q), a.lineTo(d[0], d[1]); } else { return; } @@ -4154,13 +4154,13 @@ $jscomp.polyfill("Object.values", function(w) { a.lineWidth = this.connections_width; a.fillStyle = a.strokeStyle = k; a.stroke(); - f = this.computeConnectionPoint(b, d, 0.5, n, q); + f = this.computeConnectionPoint(b, d, 0.5, n, p); h && h._pos && (h._pos[0] = f[0], h._pos[1] = f[1]); - 0.6 <= this.ds.scale && this.highquality_render && q != e.CENTER && (this.render_connection_arrows && (r = this.computeConnectionPoint(b, d, 0.25, n, q), x = this.computeConnectionPoint(b, d, 0.26, n, q), h = this.computeConnectionPoint(b, d, 0.75, n, q), g = this.computeConnectionPoint(b, d, 0.76, n, q), this.render_curved_connections ? (x = -Math.atan2(x[0] - r[0], x[1] - r[1]), g = -Math.atan2(g[0] - h[0], g[1] - h[1])) : g = x = d[1] > b[1] ? 0 : Math.PI, a.save(), a.translate(r[0], r[1]), + 0.6 <= this.ds.scale && this.highquality_render && p != e.CENTER && (this.render_connection_arrows && (t = this.computeConnectionPoint(b, d, 0.25, n, p), x = this.computeConnectionPoint(b, d, 0.26, n, p), h = this.computeConnectionPoint(b, d, 0.75, n, p), g = this.computeConnectionPoint(b, d, 0.76, n, p), this.render_curved_connections ? (x = -Math.atan2(x[0] - t[0], x[1] - t[1]), g = -Math.atan2(g[0] - h[0], g[1] - h[1])) : g = x = d[1] > b[1] ? 0 : Math.PI, a.save(), a.translate(t[0], t[1]), a.rotate(x), a.beginPath(), a.moveTo(-5, -3), a.lineTo(0, 7), a.lineTo(5, -3), a.fill(), a.restore(), a.save(), a.translate(h[0], h[1]), a.rotate(g), a.beginPath(), a.moveTo(-5, -3), a.lineTo(0, 7), a.lineTo(5, -3), a.fill(), a.restore()), a.beginPath(), a.arc(f[0], f[1], 5, 0, 2 * Math.PI), a.fill()); if (c) { - for (a.fillStyle = k, r = 0; 5 > r; ++r) { - c = (0.001 * e.getTime() + 0.2 * r) % 1, f = this.computeConnectionPoint(b, d, c, n, q), a.beginPath(), a.arc(f[0], f[1], 5, 0, 2 * Math.PI), a.fill(); + for (a.fillStyle = k, t = 0; 5 > t; ++t) { + c = (0.001 * e.getTime() + 0.2 * t) % 1, f = this.computeConnectionPoint(b, d, c, n, p), a.beginPath(), a.arc(f[0], f[1], 5, 0, 2 * Math.PI), a.fill(); } } }; @@ -4225,70 +4225,70 @@ $jscomp.polyfill("Object.values", function(w) { var k = e.NODE_WIDGET_HEIGHT, n = 0.5 < this.ds.scale; d.save(); d.globalAlpha = this.editor_alpha; - for (var q = e.WIDGET_OUTLINE_COLOR, l = e.WIDGET_BGCOLOR, g = e.WIDGET_TEXT_COLOR, r = e.WIDGET_SECONDARY_TEXT_COLOR, m = 0; m < c.length; ++m) { - var u = k, p = c[m], t = b; - p.y && (t = p.y); - p.last_y = t; - d.strokeStyle = q; + for (var p = e.WIDGET_OUTLINE_COLOR, l = e.WIDGET_BGCOLOR, g = e.WIDGET_TEXT_COLOR, t = e.WIDGET_SECONDARY_TEXT_COLOR, m = 0; m < c.length; ++m) { + var r = c[m], q = b; + r.y && (q = r.y); + r.last_y = q; + d.strokeStyle = p; d.fillStyle = "#222"; d.textAlign = "left"; - p.disabled && (d.globalAlpha *= 0.5); - switch(p.type) { + r.disabled && (d.globalAlpha *= 0.5); + switch(r.type) { case "button": - p.clicked && (d.fillStyle = "#AAA", p.clicked = !1, this.dirty_canvas = !0); - d.fillRect(15, t, f - 30, k); - n && d.strokeRect(15, t, f - 30, k); - n && (d.textAlign = "center", d.fillStyle = g, d.fillText(p.name, 0.5 * f, t + 0.7 * k)); + r.clicked && (d.fillStyle = "#AAA", r.clicked = !1, this.dirty_canvas = !0); + d.fillRect(15, q, f - 30, k); + n && d.strokeRect(15, q, f - 30, k); + n && (d.textAlign = "center", d.fillStyle = g, d.fillText(r.name, 0.5 * f, q + 0.7 * k)); break; case "toggle": d.textAlign = "left"; - d.strokeStyle = q; + d.strokeStyle = p; d.fillStyle = l; d.beginPath(); n ? d.roundRect(15, b, f - 30, k, 0.5 * k) : d.rect(15, b, f - 30, k); d.fill(); n && d.stroke(); - d.fillStyle = p.value ? "#89A" : "#333"; + d.fillStyle = r.value ? "#89A" : "#333"; d.beginPath(); - d.arc(f - 30, t + 0.5 * k, 0.36 * k, 0, 2 * Math.PI); + d.arc(f - 30, q + 0.5 * k, 0.36 * k, 0, 2 * Math.PI); d.fill(); - n && (d.fillStyle = r, null != p.name && d.fillText(p.name, 30, t + 0.7 * k), d.fillStyle = p.value ? g : r, d.textAlign = "right", d.fillText(p.value ? p.options.on || "true" : p.options.off || "false", f - 40, t + 0.7 * k)); + n && (d.fillStyle = t, null != r.name && d.fillText(r.name, 30, q + 0.7 * k), d.fillStyle = r.value ? g : t, d.textAlign = "right", d.fillText(r.value ? r.options.on || "true" : r.options.off || "false", f - 40, q + 0.7 * k)); break; case "slider": d.fillStyle = l; - d.fillRect(15, t, f - 30, k); - var C = p.options.max - p.options.min, v = (p.value - p.options.min) / C; - d.fillStyle = h == p ? "#89A" : "#678"; - d.fillRect(15, t, v * (f - 30), k); - n && d.strokeRect(15, t, f - 30, k); - p.marker && (C = (p.marker - p.options.min) / C, d.fillStyle = "#AA9", d.fillRect(15 + C * (f - 30), t, 2, k)); - n && (d.textAlign = "center", d.fillStyle = g, d.fillText(p.name + " " + Number(p.value).toFixed(3), 0.5 * f, t + 0.7 * k)); + d.fillRect(15, q, f - 30, k); + var u = r.options.max - r.options.min, C = (r.value - r.options.min) / u; + d.fillStyle = h == r ? "#89A" : "#678"; + d.fillRect(15, q, C * (f - 30), k); + n && d.strokeRect(15, q, f - 30, k); + r.marker && (u = (r.marker - r.options.min) / u, d.fillStyle = "#AA9", d.fillRect(15 + u * (f - 30), q, 2, k)); + n && (d.textAlign = "center", d.fillStyle = g, d.fillText(r.name + " " + Number(r.value).toFixed(3), 0.5 * f, q + 0.7 * k)); break; case "number": case "combo": d.textAlign = "left"; - d.strokeStyle = q; + d.strokeStyle = p; d.fillStyle = l; d.beginPath(); n ? d.roundRect(15, b, f - 30, k, 0.5 * k) : d.rect(15, b, f - 30, k); d.fill(); - n && (d.stroke(), d.fillStyle = g, d.beginPath(), d.moveTo(31, b + 5), d.lineTo(21, b + 0.5 * k), d.lineTo(31, b + k - 5), d.fill(), d.beginPath(), d.moveTo(f - 15 - 16, b + 5), d.lineTo(f - 15 - 6, b + 0.5 * k), d.lineTo(f - 15 - 16, b + k - 5), d.fill(), d.fillStyle = r, d.fillText(p.name, 35, t + 0.7 * k), d.fillStyle = g, d.textAlign = "right", "number" == p.type ? d.fillText(Number(p.value).toFixed(void 0 !== p.options.precision ? p.options.precision : 3), f - 30 - 20, t + 0.7 * k) : - (C = p.value, p.options.values && (v = p.options.values, v.constructor === Function && (v = v()), v && v.constructor !== Array && (C = v[p.value])), d.fillText(C, f - 30 - 20, t + 0.7 * k))); + n && (d.stroke(), d.fillStyle = g, d.beginPath(), d.moveTo(31, b + 5), d.lineTo(21, b + 0.5 * k), d.lineTo(31, b + k - 5), d.fill(), d.beginPath(), d.moveTo(f - 15 - 16, b + 5), d.lineTo(f - 15 - 6, b + 0.5 * k), d.lineTo(f - 15 - 16, b + k - 5), d.fill(), d.fillStyle = t, d.fillText(r.name, 35, q + 0.7 * k), d.fillStyle = g, d.textAlign = "right", "number" == r.type ? d.fillText(Number(r.value).toFixed(void 0 !== r.options.precision ? r.options.precision : 3), f - 30 - 20, q + 0.7 * k) : + (u = r.value, r.options.values && (C = r.options.values, C.constructor === Function && (C = C()), C && C.constructor !== Array && (u = C[r.value])), d.fillText(u, f - 30 - 20, q + 0.7 * k))); break; case "string": case "text": d.textAlign = "left"; - d.strokeStyle = q; + d.strokeStyle = p; d.fillStyle = l; d.beginPath(); n ? d.roundRect(15, b, f - 30, k, 0.5 * k) : d.rect(15, b, f - 30, k); d.fill(); - n && (d.save(), d.beginPath(), d.rect(15, b, f - 30, k), d.clip(), d.stroke(), d.fillStyle = r, null != p.name && d.fillText(p.name, 30, t + 0.7 * k), d.fillStyle = g, d.textAlign = "right", d.fillText(String(p.value).substr(0, 30), f - 30, t + 0.7 * k), d.restore()); + n && (d.save(), d.beginPath(), d.rect(15, b, f - 30, k), d.clip(), d.stroke(), d.fillStyle = t, null != r.name && d.fillText(r.name, 30, q + 0.7 * k), d.fillStyle = g, d.textAlign = "right", d.fillText(String(r.value).substr(0, 30), f - 30, q + 0.7 * k), d.restore()); break; default: - p.draw && (u = p.draw(d, a, f, t, k) || k); + r.draw && r.draw(d, a, f, q, k); } - b += u + 4; + b += (r.computeSize ? r.computeSize(f)[1] : k) + 4; d.globalAlpha = this.editor_alpha; } d.restore(); @@ -4298,88 +4298,91 @@ $jscomp.polyfill("Object.values", function(w) { function f(f, h) { f.value = h; f.options && f.options.property && void 0 !== a.properties[f.options.property] && a.setProperty(f.options.property, h); - f.callback && f.callback(f.value, q, a, b, d); + f.callback && f.callback(f.value, p, a, b, d); } if (!a.widgets || !a.widgets.length) { return null; } - for (var h = b[0] - a.pos[0], k = b[1] - a.pos[1], n = a.size[0], q = this, l = this.getCanvasWindow(), g = 0; g < a.widgets.length; ++g) { - var r = a.widgets[g]; - if (r && !r.disabled && (r == c || 6 < h && h < n - 12 && k > r.last_y && k < r.last_y + e.NODE_WIDGET_HEIGHT)) { - switch(r.type) { - case "button": - if ("mousemove" === d.type) { - break; - } - r.callback && setTimeout(function() { - r.callback(r, q, a, b, d); - }, 20); - this.dirty_canvas = r.clicked = !0; - break; - case "slider": - l = Math.clamp((h - 10) / (n - 20), 0, 1); - r.value = r.options.min + (r.options.max - r.options.min) * l; - r.callback && setTimeout(function() { - f(r, r.value); - }, 20); - this.dirty_canvas = !0; - break; - case "number": - case "combo": - c = r.value; - if ("mousemove" == d.type && "number" == r.type) { - r.value += 0.1 * d.deltaX * (r.options.step || 1), null != r.options.min && r.value < r.options.min && (r.value = r.options.min), null != r.options.max && r.value > r.options.max && (r.value = r.options.max); - } else { - if ("mousedown" == d.type) { - var m = r.options.values; - m && m.constructor === Function && (m = r.options.values(r, a)); - var p = null; - "number" != r.type && (p = m.constructor === Array ? m : Object.keys(m)); - h = 40 > h ? -1 : h > n - 40 ? 1 : 0; - if ("number" == r.type) { - r.value += 0.1 * h * (r.options.step || 1), null != r.options.min && r.value < r.options.min && (r.value = r.options.min), null != r.options.max && r.value > r.options.max && (r.value = r.options.max); - } else { - if (h) { - l = -1, l = m.constructor === Object ? p.indexOf(String(r.value)) + h : p.indexOf(r.value) + h, l >= p.length && (l = p.length - 1), 0 > l && (l = 0), r.value = m.constructor === Array ? m[l] : l; - } else { - var u = m != p ? Object.values(m) : m; - new e.ContextMenu(u, {scale:Math.max(1, this.ds.scale), event:d, className:"dark", callback:function(a, b, d) { - m != p && (a = u.indexOf(a)); - this.value = a; - f(this, a); - q.dirty_canvas = !0; - return !1; - }.bind(r)}, l); - } - } - } else { - "mouseup" == d.type && "number" == r.type && (h = 40 > h ? -1 : h > n - 40 ? 1 : 0, 200 > d.click_time && 0 == h && this.prompt("Value", r.value, function(a) { - this.value = Number(a); - f(this, this.value); - }.bind(r), d)); + for (var h = b[0] - a.pos[0], k = b[1] - a.pos[1], n = a.size[0], p = this, l = this.getCanvasWindow(), g = 0; g < a.widgets.length; ++g) { + var t = a.widgets[g]; + if (t && !t.disabled) { + var m = t.computeSize ? t.computeSize(n)[1] : e.NODE_WIDGET_HEIGHT; + if (t == c || 6 < h && h < n - 12 && k > t.last_y && k < t.last_y + m) { + switch(t.type) { + case "button": + if ("mousemove" === d.type) { + break; } - } - c != r.value && setTimeout(function() { - f(this, this.value); - }.bind(r), 20); - this.dirty_canvas = !0; - break; - case "toggle": - "mousedown" == d.type && (r.value = !r.value, setTimeout(function() { - f(r, r.value); - }, 20)); - break; - case "string": - case "text": - "mousedown" == d.type && this.prompt("Value", r.value, function(a) { - this.value = a; - f(this, a); - }.bind(r), d); - break; - default: - r.mouse && r.mouse(ctx, d, [h, k], a); + t.callback && setTimeout(function() { + t.callback(t, p, a, b, d); + }, 20); + this.dirty_canvas = t.clicked = !0; + break; + case "slider": + l = Math.clamp((h - 10) / (n - 20), 0, 1); + t.value = t.options.min + (t.options.max - t.options.min) * l; + t.callback && setTimeout(function() { + f(t, t.value); + }, 20); + this.dirty_canvas = !0; + break; + case "number": + case "combo": + c = t.value; + if ("mousemove" == d.type && "number" == t.type) { + t.value += 0.1 * d.deltaX * (t.options.step || 1), null != t.options.min && t.value < t.options.min && (t.value = t.options.min), null != t.options.max && t.value > t.options.max && (t.value = t.options.max); + } else { + if ("mousedown" == d.type) { + var r = t.options.values; + r && r.constructor === Function && (r = t.options.values(t, a)); + var q = null; + "number" != t.type && (q = r.constructor === Array ? r : Object.keys(r)); + h = 40 > h ? -1 : h > n - 40 ? 1 : 0; + if ("number" == t.type) { + t.value += 0.1 * h * (t.options.step || 1), null != t.options.min && t.value < t.options.min && (t.value = t.options.min), null != t.options.max && t.value > t.options.max && (t.value = t.options.max); + } else { + if (h) { + l = -1, l = r.constructor === Object ? q.indexOf(String(t.value)) + h : q.indexOf(t.value) + h, l >= q.length && (l = q.length - 1), 0 > l && (l = 0), t.value = r.constructor === Array ? r[l] : l; + } else { + var u = r != q ? Object.values(r) : r; + new e.ContextMenu(u, {scale:Math.max(1, this.ds.scale), event:d, className:"dark", callback:function(a, b, d) { + r != q && (a = u.indexOf(a)); + this.value = a; + f(this, a); + p.dirty_canvas = !0; + return !1; + }.bind(t)}, l); + } + } + } else { + "mouseup" == d.type && "number" == t.type && (h = 40 > h ? -1 : h > n - 40 ? 1 : 0, 200 > d.click_time && 0 == h && this.prompt("Value", t.value, function(a) { + this.value = Number(a); + f(this, this.value); + }.bind(t), d)); + } + } + c != t.value && setTimeout(function() { + f(this, this.value); + }.bind(t), 20); + this.dirty_canvas = !0; + break; + case "toggle": + "mousedown" == d.type && (t.value = !t.value, setTimeout(function() { + f(t, t.value); + }, 20)); + break; + case "string": + case "text": + "mousedown" == d.type && this.prompt("Value", t.value, function(a) { + this.value = a; + f(this, a); + }.bind(t), d); + break; + default: + t.mouse && (this.dirty_canvas = t.mouse(d, [h, k], a)); + } + return t; } - return r; } } return null; @@ -4391,7 +4394,7 @@ $jscomp.polyfill("Object.values", function(w) { b.globalAlpha = 0.5 * this.editor_alpha; for (var d = 0; d < a.length; ++d) { var h = a[d]; - if (v(this.visible_area, h._bounding)) { + if (w(this.visible_area, h._bounding)) { b.fillStyle = h.color || "#335"; b.strokeStyle = h.color || "#335"; var f = h._pos, c = h._size; @@ -4481,8 +4484,8 @@ $jscomp.polyfill("Object.values", function(w) { var k = l.active_canvas, n = k.getCanvasWindow(); a = e.getNodeTypesCategories(k.filter); b = []; - for (var q in a) { - a[q] && b.push({value:a[q], content:a[q], has_submenu:!0}); + for (var p in a) { + a[p] && b.push({value:a[p], content:a[p], has_submenu:!0}); } var g = new e.ContextMenu(b, {event:d, callback:function(a, b, d) { a = e.getNodeTypesInCategory(a.value, k.filter); @@ -4508,12 +4511,12 @@ $jscomp.polyfill("Object.values", function(w) { var k = []; if (b) { for (var n in b) { - var q = b[n]; - if (q) { - var g = q[0]; - q[2] && q[2].label && (g = q[2].label); - g = {content:g, value:q}; - q[1] == e.ACTION && (g.className = "event"); + var p = b[n]; + if (p) { + var g = p[0]; + p[2] && p[2].label && (g = p[2].label); + g = {content:g, value:p}; + p[1] == e.ACTION && (g.className = "event"); k.push(g); } else { k.push(null); @@ -4550,8 +4553,8 @@ $jscomp.polyfill("Object.values", function(w) { f.onGetOutputs && (b = f.onGetOutputs()); var n = []; if (b) { - for (var q in b) { - var g = b[q]; + for (var p in b) { + var g = b[p]; if (!g) { n.push(null); } else { @@ -4634,9 +4637,9 @@ $jscomp.polyfill("Object.values", function(w) { })); b = l.active_canvas.canvas; d = b.getBoundingClientRect(); - var q = c = -20; - d && (c -= d.left, q -= d.top); - event ? (k.style.left = event.clientX + c + "px", k.style.top = event.clientY + q + "px") : (k.style.left = 0.5 * b.width + c + "px", k.style.top = 0.5 * b.height + q + "px"); + var p = c = -20; + d && (c -= d.left, p -= d.top); + event ? (k.style.left = event.clientX + c + "px", k.style.top = event.clientY + p + "px") : (k.style.left = 0.5 * b.width + c + "px", k.style.top = 0.5 * b.height + p + "px"); k.querySelector("button").addEventListener("click", h); b.parentNode.appendChild(k); }; @@ -4680,9 +4683,9 @@ $jscomp.polyfill("Object.values", function(w) { }); a = l.active_canvas.canvas; b = a.getBoundingClientRect(); - var n = -20, q = -20; - b && (n -= b.left, q -= b.top); - c ? (e.style.left = c.clientX + n + "px", e.style.top = c.clientY + q + "px") : (e.style.left = 0.5 * a.width + n + "px", e.style.top = 0.5 * a.height + q + "px"); + var n = -20, p = -20; + b && (n -= b.left, p -= b.top); + c ? (e.style.left = c.clientX + n + "px", e.style.top = c.clientY + p + "px") : (e.style.left = 0.5 * a.width + n + "px", e.style.top = 0.5 * a.height + p + "px"); a.parentNode.appendChild(e); setTimeout(function() { k.focus(); @@ -4725,15 +4728,15 @@ $jscomp.polyfill("Object.values", function(w) { g.close(); } function d(a) { - var b = t; - t && t.classList.remove("selected"); - t ? (t = a ? t.nextSibling : t.previousSibling) || (t = b) : t = a ? p.childNodes[0] : p.childNodes[p.childNodes.length]; - t && (t.classList.add("selected"), t.scrollIntoView({block:"end", behavior:"smooth"})); + var b = u; + u && u.classList.remove("selected"); + u ? (u = a ? u.nextSibling : u.previousSibling) || (u = b) : u = a ? r.childNodes[0] : r.childNodes[r.childNodes.length]; + u && (u.classList.add("selected"), u.scrollIntoView({block:"end", behavior:"smooth"})); } function c() { function a(a, d) { var f = document.createElement("div"); - r || (r = a); + t || (t = a); f.innerText = a; f.dataset.type = escape(a); f.className = "litegraph lite-search-item"; @@ -4741,15 +4744,15 @@ $jscomp.polyfill("Object.values", function(w) { f.addEventListener("click", function(a) { b(unescape(this.dataset.type)); }); - p.appendChild(f); + r.appendChild(f); } - u = null; + q = null; var d = C.value; - r = null; - p.innerHTML = ""; + t = null; + r.innerHTML = ""; if (d) { if (f.onSearchBox) { - var c = f.onSearchBox(p, d, k); + var c = f.onSearchBox(r, d, k); if (c) { for (var h = 0; h < c.length; ++h) { a(c[h]); @@ -4758,16 +4761,16 @@ $jscomp.polyfill("Object.values", function(w) { } else { c = function(a) { var b = e.registered_node_types[a]; - return q && b.filter != q ? !1 : -1 !== a.toLowerCase().indexOf(d); + return p && b.filter != p ? !1 : -1 !== a.toLowerCase().indexOf(d); }; var n = 0; d = d.toLowerCase(); - var q = k.filter || k.graph.filter; + var p = k.filter || k.graph.filter; for (h in e.searchbox_extras) { var g = e.searchbox_extras[h]; if (-1 !== g.desc.toLowerCase().indexOf(d)) { - var m = e.registered_node_types[g.type]; - if (!m || !m.filter || m.filter == q) { + var x = e.registered_node_types[g.type]; + if (!x || !x.filter || x.filter == p) { if (a(g.desc, "searchbox_extra"), -1 !== l.search_limit && n++ > l.search_limit) { break; } @@ -4787,13 +4790,13 @@ $jscomp.polyfill("Object.values", function(w) { } } } - var f = this, k = l.active_canvas, n = k.canvas, q = n.ownerDocument || document, g = document.createElement("div"); + var f = this, k = l.active_canvas, n = k.canvas, p = n.ownerDocument || document, g = document.createElement("div"); g.className = "litegraph litesearchbox graphdialog rounded"; g.innerHTML = "Search
"; g.close = function() { f.search_box = null; - q.body.focus(); - q.body.style.overflow = ""; + p.body.focus(); + p.body.style.overflow = ""; setTimeout(function() { f.canvas.focus(); }, 20); @@ -4811,7 +4814,7 @@ $jscomp.polyfill("Object.values", function(w) { }); f.search_box && f.search_box.close(); f.search_box = g; - var p = g.querySelector(".helper"), r = null, u = null, t = null, C = g.querySelector("input"); + var r = g.querySelector(".helper"), t = null, q = null, u = null, C = g.querySelector("input"); C && (C.addEventListener("blur", function(a) { this.focus(); }), C.addEventListener("keydown", function(a) { @@ -4825,10 +4828,10 @@ $jscomp.polyfill("Object.values", function(w) { g.close(); } else { if (13 == a.keyCode) { - t ? b(t.innerHTML) : r ? b(r) : g.close(); + u ? b(u.innerHTML) : t ? b(t) : g.close(); } else { - u && clearInterval(u); - u = setTimeout(c, 10); + q && clearInterval(q); + q = setTimeout(c, 10); return; } } @@ -4839,18 +4842,18 @@ $jscomp.polyfill("Object.values", function(w) { a.stopImmediatePropagation(); return !0; })); - q.fullscreenElement ? q.fullscreenElement.appendChild(g) : (q.body.appendChild(g), q.body.style.overflow = "hidden"); + p.fullscreenElement ? p.fullscreenElement.appendChild(g) : (p.body.appendChild(g), p.body.style.overflow = "hidden"); n = n.getBoundingClientRect(); - var v = (a ? a.clientY : n.top + 0.5 * n.height) - 20; + var w = (a ? a.clientY : n.top + 0.5 * n.height) - 20; g.style.left = (a ? a.clientX : n.left + 0.5 * n.width) - 80 + "px"; - g.style.top = v + "px"; - a.layerY > n.height - 200 && (p.style.maxHeight = n.height - a.layerY - 20 + "px"); + g.style.top = w + "px"; + a.layerY > n.height - 200 && (r.style.maxHeight = n.height - a.layerY - 20 + "px"); C.focus(); return g; }; l.prototype.showEditPropertyValue = function(a, b, d) { function c() { - f(r.value); + f(t.value); } function f(f) { "number" == typeof a.properties[b] && (f = Number(f)); @@ -4876,9 +4879,9 @@ $jscomp.polyfill("Object.values", function(w) { } else { if ("enum" == k && e.values) { n = ""; } else { @@ -4892,20 +4895,20 @@ $jscomp.polyfill("Object.values", function(w) { } var l = this.createDialog("" + b + "" + n + "", d); if ("enum" == k && e.values) { - var r = l.querySelector("select"); - r.addEventListener("change", function(a) { + var t = l.querySelector("select"); + t.addEventListener("change", function(a) { f(a.target.value); }); } else { if ("boolean" == k) { - (r = l.querySelector("input")) && r.addEventListener("click", function(a) { - f(!!r.checked); + (t = l.querySelector("input")) && t.addEventListener("click", function(a) { + f(!!t.checked); }); } else { - if (r = l.querySelector("input")) { - r.addEventListener("blur", function(a) { + if (t = l.querySelector("input")) { + t.addEventListener("blur", function(a) { this.focus(); - }), g = void 0 !== a.properties[b] ? a.properties[b] : "", g = JSON.stringify(g), r.value = g, r.addEventListener("keydown", function(a) { + }), g = void 0 !== a.properties[b] ? a.properties[b] : "", g = JSON.stringify(g), t.value = g, t.addEventListener("keydown", function(a) { 13 == a.keyCode && (c(), a.preventDefault(), a.stopPropagation()); }); } @@ -5085,7 +5088,7 @@ $jscomp.polyfill("Object.values", function(w) { e.isInsideBounding = function(a, b) { return a[0] < b[0][0] || a[1] < b[0][1] || a[0] > b[1][0] || a[1] > b[1][1] ? !1 : !0; }; - e.overlapBounding = v; + e.overlapBounding = w; e.hex2num = function(a) { "#" == a.charAt(0) && (a = a.slice(1)); a = a.toUpperCase(); @@ -5269,14 +5272,14 @@ $jscomp.polyfill("Object.values", function(w) { return -1; } b = b || 30; - for (var c = this.size[0] - 2 * this.margin, f = this.size[1] - 2 * this.margin, e = d.length, k = [0, 0], n = 1000000, q = -1, g = 0; g < e; ++g) { + for (var c = this.size[0] - 2 * this.margin, f = this.size[1] - 2 * this.margin, e = d.length, k = [0, 0], n = 1000000, p = -1, g = 0; g < e; ++g) { var l = d[g]; k[0] = l[0] * c; k[1] = (1.0 - l[1]) * f; l = vec2.distance(a, k); - l > n || l > b || (q = g, n = l); + l > n || l > b || (p = g, n = l); } - return q; + return p; }; e.CurveEditor = z; e.getParameterNames = function(a) { @@ -5290,12 +5293,12 @@ $jscomp.polyfill("Object.values", function(w) { }); })(this); "undefined" != typeof exports && (exports.LiteGraph = this.LiteGraph); -(function(w) { +(function(v) { function c() { this.addOutput("in ms", "number"); this.addOutput("in sec", "number"); } - function p() { + function q() { this.size = [140, 80]; this.properties = {enabled:!0}; this.enabled = !0; @@ -5354,7 +5357,7 @@ $jscomp.polyfill("Object.values", function(w) { this.widgets_up = !0; this.size = [180, 60]; } - function u() { + function r() { this.addOutput("value", "number"); this.addProperty("value", 1.0); this.widget = this.addWidget("number", "value", 1, "value"); @@ -5383,7 +5386,7 @@ $jscomp.polyfill("Object.values", function(w) { this.widget = this.addWidget("text", "url", "", "url"); this._data = null; } - function v() { + function w() { this.addOutput("", ""); this.addProperty("value", ""); this.widget = this.addWidget("text", "json", "", "value"); @@ -5428,7 +5431,7 @@ $jscomp.polyfill("Object.values", function(w) { this.addOutput("keys", "array"); this.size = [140, 30]; } - function t() { + function u() { this.addInput("A", "object"); this.addInput("B", "object"); this.addOutput("", "object"); @@ -5457,7 +5460,7 @@ $jscomp.polyfill("Object.values", function(w) { a.value && a.downloadAsFile(); }); } - function q() { + function p() { this.size = [60, 30]; this.addInput("value", 0, {label:""}); this.value = 0; @@ -5494,7 +5497,7 @@ $jscomp.polyfill("Object.values", function(w) { this._func = null; this.data = {}; } - var h = w.LiteGraph; + var h = v.LiteGraph; c.title = "Time"; c.desc = "Time"; c.prototype.onExecute = function() { @@ -5502,13 +5505,13 @@ $jscomp.polyfill("Object.values", function(w) { this.setOutputData(1, this.graph.globaltime); }; h.registerNodeType("basic/time", c); - p.title = "Subgraph"; - p.desc = "Graph inside a node"; - p.title_color = "#334"; - p.prototype.onGetInputs = function() { + q.title = "Subgraph"; + q.desc = "Graph inside a node"; + q.title_color = "#334"; + q.prototype.onGetInputs = function() { return [["enabled", "boolean"]]; }; - p.prototype.onDrawTitle = function(a) { + q.prototype.onDrawTitle = function(a) { if (!this.flags.collapsed) { a.fillStyle = "#555"; var b = h.NODE_TITLE_HEIGHT, d = this.size[0] - b; @@ -5521,13 +5524,13 @@ $jscomp.polyfill("Object.values", function(w) { a.fill(); } }; - p.prototype.onDblClick = function(a, b, d) { + q.prototype.onDblClick = function(a, b, d) { var f = this; setTimeout(function() { d.openSubgraph(f.subgraph); }, 10); }; - p.prototype.onMouseDown = function(a, b, d) { + q.prototype.onMouseDown = function(a, b, d) { if (!this.flags.collapsed && b[0] > this.size[0] - h.NODE_TITLE_HEIGHT && 0 > b[1]) { var f = this; setTimeout(function() { @@ -5535,10 +5538,10 @@ $jscomp.polyfill("Object.values", function(w) { }, 10); } }; - p.prototype.onAction = function(a, b) { + q.prototype.onAction = function(a, b) { this.subgraph.onAction(a, b); }; - p.prototype.onExecute = function() { + q.prototype.onExecute = function() { if (this.enabled = this.getInputOrProperty("enabled")) { if (this.inputs) { for (var a = 0; a < this.inputs.length; a++) { @@ -5554,58 +5557,58 @@ $jscomp.polyfill("Object.values", function(w) { } } }; - p.prototype.sendEventToAllNodes = function(a, b, d) { + q.prototype.sendEventToAllNodes = function(a, b, d) { this.enabled && this.subgraph.sendEventToAllNodes(a, b, d); }; - p.prototype.onSubgraphTrigger = function(a, b) { + q.prototype.onSubgraphTrigger = function(a, b) { a = this.findOutputSlot(a); -1 != a && this.triggerSlot(a); }; - p.prototype.onSubgraphNewInput = function(a, b) { + q.prototype.onSubgraphNewInput = function(a, b) { -1 == this.findInputSlot(a) && this.addInput(a, b); }; - p.prototype.onSubgraphRenamedInput = function(a, b) { + q.prototype.onSubgraphRenamedInput = function(a, b) { a = this.findInputSlot(a); -1 != a && (this.getInputInfo(a).name = b); }; - p.prototype.onSubgraphTypeChangeInput = function(a, b) { + q.prototype.onSubgraphTypeChangeInput = function(a, b) { a = this.findInputSlot(a); -1 != a && (this.getInputInfo(a).type = b); }; - p.prototype.onSubgraphRemovedInput = function(a) { + q.prototype.onSubgraphRemovedInput = function(a) { a = this.findInputSlot(a); -1 != a && this.removeInput(a); }; - p.prototype.onSubgraphNewOutput = function(a, b) { + q.prototype.onSubgraphNewOutput = function(a, b) { -1 == this.findOutputSlot(a) && this.addOutput(a, b); }; - p.prototype.onSubgraphRenamedOutput = function(a, b) { + q.prototype.onSubgraphRenamedOutput = function(a, b) { a = this.findOutputSlot(a); -1 != a && (this.getOutputInfo(a).name = b); }; - p.prototype.onSubgraphTypeChangeOutput = function(a, b) { + q.prototype.onSubgraphTypeChangeOutput = function(a, b) { a = this.findOutputSlot(a); -1 != a && (this.getOutputInfo(a).type = b); }; - p.prototype.onSubgraphRemovedOutput = function(a) { + q.prototype.onSubgraphRemovedOutput = function(a) { a = this.findInputSlot(a); -1 != a && this.removeOutput(a); }; - p.prototype.getExtraMenuOptions = function(a) { + q.prototype.getExtraMenuOptions = function(a) { var b = this; return [{content:"Open", callback:function() { a.openSubgraph(b.subgraph); }}]; }; - p.prototype.onResize = function(a) { + q.prototype.onResize = function(a) { a[1] += 20; }; - p.prototype.serialize = function() { + q.prototype.serialize = function() { var a = h.LGraphNode.prototype.serialize.call(this); a.subgraph = this.subgraph.serialize(); return a; }; - p.prototype.clone = function() { + q.prototype.clone = function() { var a = h.createNode(this.type), b = this.serialize(); delete b.id; delete b.inputs; @@ -5613,8 +5616,8 @@ $jscomp.polyfill("Object.values", function(w) { a.configure(b); return a; }; - h.Subgraph = p; - h.registerNodeType("graph/subgraph", p); + h.Subgraph = q; + h.registerNodeType("graph/subgraph", q); m.title = "Input"; m.desc = "Input of the graph"; m.prototype.onConfigure = function() { @@ -5670,28 +5673,28 @@ $jscomp.polyfill("Object.values", function(w) { }; h.GraphOutput = g; h.registerNodeType("graph/output", g); - u.title = "Const Number"; - u.desc = "Constant number"; - u.prototype.onExecute = function() { + r.title = "Const Number"; + r.desc = "Constant number"; + r.prototype.onExecute = function() { this.setOutputData(0, parseFloat(this.properties.value)); }; - u.prototype.getTitle = function() { + r.prototype.getTitle = function() { return this.flags.collapsed ? this.properties.value : this.title; }; - u.prototype.setValue = function(a) { + r.prototype.setValue = function(a) { this.setProperty("value", a); }; - u.prototype.onDrawBackground = function(a) { + r.prototype.onDrawBackground = function(a) { this.outputs[0].label = this.properties.value.toFixed(3); }; - h.registerNodeType("basic/const", u); + h.registerNodeType("basic/const", r); l.title = "Const Boolean"; l.desc = "Constant boolean"; - l.prototype.getTitle = u.prototype.getTitle; + l.prototype.getTitle = r.prototype.getTitle; l.prototype.onExecute = function() { this.setOutputData(0, this.properties.value); }; - l.prototype.setValue = u.prototype.setValue; + l.prototype.setValue = r.prototype.setValue; l.prototype.onGetInputs = function() { return [["toggle", h.ACTION]]; }; @@ -5701,11 +5704,11 @@ $jscomp.polyfill("Object.values", function(w) { h.registerNodeType("basic/boolean", l); B.title = "Const String"; B.desc = "Constant string"; - B.prototype.getTitle = u.prototype.getTitle; + B.prototype.getTitle = r.prototype.getTitle; B.prototype.onExecute = function() { this.setOutputData(0, this.properties.value); }; - B.prototype.setValue = u.prototype.setValue; + B.prototype.setValue = r.prototype.setValue; B.prototype.onDropFile = function(a) { var b = this, d = new FileReader; d.onload = function(a) { @@ -5725,7 +5728,7 @@ $jscomp.polyfill("Object.values", function(w) { !a || a == this._url && this._type == this.properties.type || this.fetchFile(a); this.setOutputData(0, this._data); }; - y.prototype.setValue = u.prototype.setValue; + y.prototype.setValue = r.prototype.setValue; y.prototype.fetchFile = function(a) { var b = this; a && a.constructor === String ? (this._url = a, this._type = this.properties.type, "http" == a.substr(0, 4) && h.proxy && (a = h.proxy + a.substr(a.indexOf(":") + 3)), fetch(a).then(function(a) { @@ -5778,9 +5781,9 @@ $jscomp.polyfill("Object.values", function(w) { } }; h.registerNodeType("basic/file", y); - v.title = "Const Data"; - v.desc = "Constant Data"; - v.prototype.onPropertyChanged = function(a, b) { + w.title = "Const Data"; + w.desc = "Constant Data"; + w.prototype.onPropertyChanged = function(a, b) { this.widget.value = b; if (null != b && "" != b) { try { @@ -5790,11 +5793,11 @@ $jscomp.polyfill("Object.values", function(w) { } } }; - v.prototype.onExecute = function() { + w.prototype.onExecute = function() { this.setOutputData(0, this._value); }; - v.prototype.setValue = u.prototype.setValue; - h.registerNodeType("basic/data", v); + w.prototype.setValue = r.prototype.setValue; + h.registerNodeType("basic/data", w); E.title = "Const Array"; E.desc = "Constant Array"; E.prototype.onPropertyChanged = function(a, b) { @@ -5818,7 +5821,7 @@ $jscomp.polyfill("Object.values", function(w) { } this.setOutputData(0, this._value); }; - E.prototype.setValue = u.prototype.setValue; + E.prototype.setValue = r.prototype.setValue; h.registerNodeType("basic/array", E); z.title = "Array[i]"; z.desc = "Returns an element from an array"; @@ -5861,9 +5864,9 @@ $jscomp.polyfill("Object.values", function(w) { null != a && this.setOutputData(0, Object.keys(a)); }; h.registerNodeType("basic/object_keys", D); - t.title = "Merge Objects"; - t.desc = "Creates an object copying properties from others"; - t.prototype.onExecute = function() { + u.title = "Merge Objects"; + u.desc = "Creates an object copying properties from others"; + u.prototype.onExecute = function() { var a = this.getInputData(0), b = this.getInputData(1), d = this._result; if (a) { for (var c in a) { @@ -5877,13 +5880,13 @@ $jscomp.polyfill("Object.values", function(w) { } this.setOutputData(0, d); }; - h.registerNodeType("basic/merge_objects", t); + h.registerNodeType("basic/merge_objects", u); G.title = "Variable"; G.desc = "store/read variable value"; G.prototype.onExecute = function() { this.value = this.getInputData(0); this.graph && (this.graph.vars[this.properties.varname] = this.value); - this.properties.global && (w[this.properties.varname] = this.value); + this.properties.global && (v[this.properties.varname] = this.value); this.setOutputData(0, this.value); }; G.prototype.getTitle = function() { @@ -5926,15 +5929,15 @@ $jscomp.polyfill("Object.values", function(w) { return this.flags.collapsed ? this.properties.filename : this.title; }; h.registerNodeType("basic/download", n); - q.title = "Watch"; - q.desc = "Show value of input"; - q.prototype.onExecute = function() { + p.title = "Watch"; + p.desc = "Show value of input"; + p.prototype.onExecute = function() { this.inputs[0] && (this.value = this.getInputData(0)); }; - q.prototype.getTitle = function() { + p.prototype.getTitle = function() { return this.flags.collapsed ? this.inputs[0].label : this.title; }; - q.toString = function(a) { + p.toString = function(a) { if (null == a) { return "null"; } @@ -5943,16 +5946,16 @@ $jscomp.polyfill("Object.values", function(w) { } if (a.constructor === Array) { for (var b = "[", d = 0; d < a.length; ++d) { - b += q.toString(a[d]) + (d + 1 != a.length ? "," : ""); + b += p.toString(a[d]) + (d + 1 != a.length ? "," : ""); } return b + "]"; } return String(a); }; - q.prototype.onDrawBackground = function(a) { - this.inputs[0].label = q.toString(this.value); + p.prototype.onDrawBackground = function(a) { + this.inputs[0].label = p.toString(this.value); }; - h.registerNodeType("basic/watch", q); + h.registerNodeType("basic/watch", p); k.title = "Cast"; k.desc = "Allows to connect different types"; k.prototype.onExecute = function() { @@ -6028,68 +6031,68 @@ $jscomp.polyfill("Object.values", function(w) { }; h.registerNodeType("basic/script", d); })(this); -(function(w) { +(function(v) { function c() { this.size = [60, 30]; - this.addInput("event", v.ACTION); + this.addInput("event", w.ACTION); } - function p() { + function q() { this.size = [60, 30]; this.addInput("if", ""); - this.addOutput("true", v.EVENT); - this.addOutput("change", v.EVENT); - this.addOutput("false", v.EVENT); + this.addOutput("true", w.EVENT); + this.addOutput("change", w.EVENT); + this.addOutput("false", w.EVENT); this.properties = {only_on_change:!0}; this.prev = 0; } function m() { - this.addInput("", v.ACTION); - this.addInput("", v.ACTION); - this.addInput("", v.ACTION); - this.addInput("", v.ACTION); - this.addInput("", v.ACTION); - this.addInput("", v.ACTION); - this.addOutput("", v.EVENT); - this.addOutput("", v.EVENT); - this.addOutput("", v.EVENT); - this.addOutput("", v.EVENT); - this.addOutput("", v.EVENT); - this.addOutput("", v.EVENT); + this.addInput("", w.ACTION); + this.addInput("", w.ACTION); + this.addInput("", w.ACTION); + this.addInput("", w.ACTION); + this.addInput("", w.ACTION); + this.addInput("", w.ACTION); + this.addOutput("", w.EVENT); + this.addOutput("", w.EVENT); + this.addOutput("", w.EVENT); + this.addOutput("", w.EVENT); + this.addOutput("", w.EVENT); + this.addOutput("", w.EVENT); this.size = [120, 30]; this.flags = {horizontal:!0, render_box:!1}; } function g() { this.size = [60, 30]; - this.addInput("event", v.ACTION); - this.addOutput("event", v.EVENT); + this.addInput("event", w.ACTION); + this.addOutput("event", w.EVENT); this.properties = {equal_to:"", has_property:"", property_equal_to:""}; } - function u() { - this.addInput("inc", v.ACTION); - this.addInput("dec", v.ACTION); - this.addInput("reset", v.ACTION); - this.addOutput("change", v.EVENT); + function r() { + this.addInput("inc", w.ACTION); + this.addInput("dec", w.ACTION); + this.addInput("reset", w.ACTION); + this.addOutput("change", w.EVENT); this.addOutput("num", "number"); this.num = 0; } function l() { this.size = [60, 30]; this.addProperty("time_in_ms", 1000); - this.addInput("event", v.ACTION); - this.addOutput("on_time", v.EVENT); + this.addInput("event", w.ACTION); + this.addOutput("on_time", w.EVENT); this._pending = []; } function B() { this.addProperty("interval", 1000); this.addProperty("event", "tick"); - this.addOutput("on_tick", v.EVENT); + this.addOutput("on_tick", w.EVENT); this.time = 0; this.last_interval = 1000; this.triggered = !1; } function y() { this.addInput("data", ""); - this.addInput("assign", v.ACTION); + this.addInput("assign", w.ACTION); this.addOutput("data", ""); this._last_value = null; this.properties = {data:null, serialize:!0}; @@ -6098,16 +6101,16 @@ $jscomp.polyfill("Object.values", function(w) { c.properties.data = c._last_value; }); } - var v = w.LiteGraph; + var w = v.LiteGraph; c.title = "Log Event"; c.desc = "Log event in console"; c.prototype.onAction = function(c, g) { console.log(c, g); }; - v.registerNodeType("events/log", c); - p.title = "TriggerEvent"; - p.desc = "Triggers event if input evaluates to true"; - p.prototype.onExecute = function(c, g) { + w.registerNodeType("events/log", c); + q.title = "TriggerEvent"; + q.desc = "Triggers event if input evaluates to true"; + q.prototype.onExecute = function(c, g) { c = this.getInputData(0); var e = c != this.prev; 0 === this.prev && (e = !1); @@ -6117,7 +6120,7 @@ $jscomp.polyfill("Object.values", function(w) { e && this.triggerSlot(1, g); this.prev = c; }; - v.registerNodeType("events/trigger", p); + w.registerNodeType("events/trigger", q); m.title = "Sequencer"; m.desc = "Trigger events when an event arrives"; m.prototype.getTitle = function() { @@ -6130,7 +6133,7 @@ $jscomp.polyfill("Object.values", function(w) { } } }; - v.registerNodeType("events/sequencer", m); + w.registerNodeType("events/sequencer", m); g.title = "Filter Event"; g.desc = "Blocks events that do not match the filter"; g.prototype.onAction = function(c, g) { @@ -6141,24 +6144,24 @@ $jscomp.polyfill("Object.values", function(w) { this.triggerSlot(0, g); } }; - v.registerNodeType("events/filter", g); - u.title = "Counter"; - u.desc = "Counts events"; - u.prototype.getTitle = function() { + w.registerNodeType("events/filter", g); + r.title = "Counter"; + r.desc = "Counts events"; + r.prototype.getTitle = function() { return this.flags.collapsed ? String(this.num) : this.title; }; - u.prototype.onAction = function(c, g) { + r.prototype.onAction = function(c, g) { g = this.num; "inc" == c ? this.num += 1 : "dec" == c ? --this.num : "reset" == c && (this.num = 0); this.num != g && this.trigger("change", this.num); }; - u.prototype.onDrawBackground = function(c) { + r.prototype.onDrawBackground = function(c) { this.flags.collapsed || (c.fillStyle = "#AAA", c.font = "20px Arial", c.textAlign = "center", c.fillText(this.num, 0.5 * this.size[0], 0.5 * this.size[1])); }; - u.prototype.onExecute = function() { + r.prototype.onExecute = function() { this.setOutputData(1, this.num); }; - v.registerNodeType("events/counter", u); + w.registerNodeType("events/counter", r); l.title = "Delay"; l.desc = "Delays one event"; l.prototype.onAction = function(c, g) { @@ -6175,9 +6178,9 @@ $jscomp.polyfill("Object.values", function(w) { } }; l.prototype.onGetInputs = function() { - return [["event", v.ACTION], ["time_in_ms", "number"]]; + return [["event", w.ACTION], ["time_in_ms", "number"]]; }; - v.registerNodeType("events/delay", l); + w.registerNodeType("events/delay", l); B.title = "Timer"; B.desc = "Sends an event every N milliseconds"; B.prototype.onStart = function() { @@ -6204,7 +6207,7 @@ $jscomp.polyfill("Object.values", function(w) { B.prototype.onGetOutputs = function() { return [["tick", "boolean"]]; }; - v.registerNodeType("events/timer", B); + w.registerNodeType("events/timer", B); y.title = "Data Store"; y.desc = "Stores data and only changes when event is received"; y.prototype.onExecute = function() { @@ -6217,9 +6220,9 @@ $jscomp.polyfill("Object.values", function(w) { y.prototype.onSerialize = function(c) { null != c.data && (0 == this.properties.serialize || c.data.constructor !== String && c.data.constructor !== Number && c.data.constructor !== Boolean && c.data.constructor !== Array && c.data.constructor !== Object) && (c.data = null); }; - v.registerNodeType("basic/data_store", y); + w.registerNodeType("basic/data_store", y); })(this); -(function(w) { +(function(v) { function c() { this.addOutput("", z.EVENT); this.addOutput("", "boolean"); @@ -6229,7 +6232,7 @@ $jscomp.polyfill("Object.values", function(w) { this.size = [164, 84]; this.clicked = !1; } - function p() { + function q() { this.addInput("", "boolean"); this.addInput("e", z.ACTION); this.addOutput("v", "boolean"); @@ -6260,7 +6263,7 @@ $jscomp.polyfill("Object.values", function(w) { c.triggerSlot(1, e); }, {property:"value", values:this._values}); } - function u() { + function r() { this.addOutput("", "number"); this.size = [64, 84]; this.properties = {min:0, max:1, value:0.5, color:"#7AF", precision:2}; @@ -6287,7 +6290,7 @@ $jscomp.polyfill("Object.values", function(w) { this.addInput("", "number"); this.properties = {min:0, max:1, value:0, color:"#AAF"}; } - function v() { + function w() { this.addInputs("", 0); this.properties = {value:"...", font:"Arial", fontsize:18, color:"#AAA", align:"left", glowSize:0, decimals:1}; } @@ -6295,7 +6298,7 @@ $jscomp.polyfill("Object.values", function(w) { this.size = [200, 100]; this.properties = {borderColor:"#ffffff", bgcolorTop:"#f0f0f0", bgcolorBottom:"#e0e0e0", shadowSize:2, borderRadius:3}; } - var z = w.LiteGraph; + var z = v.LiteGraph; c.title = "Button"; c.desc = "Triggers an event"; c.font = "Arial"; @@ -6321,9 +6324,9 @@ $jscomp.polyfill("Object.values", function(w) { this.clicked = !1; }; z.registerNodeType("widget/button", c); - p.title = "Toggle"; - p.desc = "Toggles between true or false"; - p.prototype.onDrawForeground = function(c) { + q.title = "Toggle"; + q.desc = "Toggles between true or false"; + q.prototype.onDrawForeground = function(c) { if (!this.flags.collapsed) { var e = 0.5 * this.size[1], g = 0.8 * this.size[1]; c.font = this.properties.font || (0.8 * e).toFixed(0) + "px Arial"; @@ -6339,21 +6342,21 @@ $jscomp.polyfill("Object.values", function(w) { c.textAlign = "left"; } }; - p.prototype.onAction = function(c) { + q.prototype.onAction = function(c) { this.properties.value = !this.properties.value; this.trigger("e", this.properties.value); }; - p.prototype.onExecute = function() { + q.prototype.onExecute = function() { var c = this.getInputData(0); null != c && (this.properties.value = c); this.setOutputData(0, this.properties.value); }; - p.prototype.onMouseDown = function(c, g) { + q.prototype.onMouseDown = function(c, g) { if (1 < g[0] && 1 < g[1] && g[0] < this.size[0] - 2 && g[1] < this.size[1] - 2) { return this.properties.value = !this.properties.value, this.graph._version++, this.trigger("e", this.properties.value), !0; } }; - z.registerNodeType("widget/toggle", p); + z.registerNodeType("widget/toggle", q); m.title = "Number"; m.desc = "Widget to select number value"; m.pixels_threshold = 10; @@ -6408,10 +6411,10 @@ $jscomp.polyfill("Object.values", function(w) { "values" == c ? (this._values = g.split(";"), this.widget.options.values = this._values) : "value" == c && (this.widget.value = g); }; z.registerNodeType("widget/combo", g); - u.title = "Knob"; - u.desc = "Circular controller"; - u.size = [80, 100]; - u.prototype.onDrawForeground = function(c) { + r.title = "Knob"; + r.desc = "Circular controller"; + r.size = [80, 100]; + r.prototype.onDrawForeground = function(c) { if (!this.flags.collapsed) { -1 == this.value && (this.value = (this.properties.value - this.properties.min) / (this.properties.max - this.properties.min)); var e = 0.5 * this.size[0], g = 0.5 * this.size[1], l = 0.5 * Math.min(this.size[0], this.size[1]) - 5; @@ -6450,11 +6453,11 @@ $jscomp.polyfill("Object.values", function(w) { c.fillText(this.properties.value.toFixed(this.properties.precision), e, g + 0.15 * l); } }; - u.prototype.onExecute = function() { + r.prototype.onExecute = function() { this.setOutputData(0, this.properties.value); this.boxcolor = z.colorToString([this.value, this.value, this.value]); }; - u.prototype.onMouseDown = function(c) { + r.prototype.onMouseDown = function(c) { this.center = [0.5 * this.size[0], 0.5 * this.size[1] + 20]; this.radius = 0.5 * this.size[0]; if (20 > c.canvasY - this.pos[1] || z.distance([c.canvasX, c.canvasY], [this.pos[0] + this.center[0], this.pos[1] + this.center[1]]) > this.radius) { @@ -6464,7 +6467,7 @@ $jscomp.polyfill("Object.values", function(w) { this.captureInput(!0); return !0; }; - u.prototype.onMouseMove = function(c) { + r.prototype.onMouseMove = function(c) { if (this.oldmouse) { c = [c.canvasX - this.pos[0], c.canvasY - this.pos[1]]; var e = this.value; @@ -6476,15 +6479,15 @@ $jscomp.polyfill("Object.values", function(w) { this.setDirtyCanvas(!0); } }; - u.prototype.onMouseUp = function(c) { + r.prototype.onMouseUp = function(c) { this.oldmouse && (this.oldmouse = null, this.captureInput(!1)); }; - u.prototype.onPropertyChanged = function(c, g) { + r.prototype.onPropertyChanged = function(c, g) { if ("min" == c || "max" == c || "value" == c) { return this.properties[c] = parseFloat(g), !0; } }; - z.registerNodeType("widget/knob", u); + z.registerNodeType("widget/knob", r); l.title = "Inner Slider"; l.prototype.onPropertyChanged = function(c, g) { "value" == c && (this.slider.value = g); @@ -6552,10 +6555,10 @@ $jscomp.polyfill("Object.values", function(w) { c.fillRect(2, 2, (this.size[0] - 4) * e, this.size[1] - 4); }; z.registerNodeType("widget/progress", y); - v.title = "Text"; - v.desc = "Shows the input value"; - v.widgets = [{name:"resize", text:"Resize box", type:"button"}, {name:"led_text", text:"LED", type:"minibutton"}, {name:"normal_text", text:"Normal", type:"minibutton"}]; - v.prototype.onDrawForeground = function(c) { + w.title = "Text"; + w.desc = "Shows the input value"; + w.widgets = [{name:"resize", text:"Resize box", type:"button"}, {name:"led_text", text:"LED", type:"minibutton"}, {name:"normal_text", text:"Normal", type:"minibutton"}]; + w.prototype.onDrawForeground = function(c) { c.fillStyle = this.properties.color; var e = this.properties.value; this.properties.glowSize ? (c.shadowColor = this.properties.color, c.shadowOffsetX = 0, c.shadowOffsetY = 0, c.shadowBlur = this.properties.glowSize) : c.shadowColor = "transparent"; @@ -6573,11 +6576,11 @@ $jscomp.polyfill("Object.values", function(w) { this.last_ctx = c; c.textAlign = "left"; }; - v.prototype.onExecute = function() { + w.prototype.onExecute = function() { var c = this.getInputData(0); null != c && (this.properties.value = c); }; - v.prototype.resize = function() { + w.prototype.resize = function() { if (this.last_ctx) { var c = this.str.split("\\n"); this.last_ctx.font = this.properties.fontsize + "px " + this.properties.font; @@ -6591,12 +6594,12 @@ $jscomp.polyfill("Object.values", function(w) { this.setDirtyCanvas(!0); } }; - v.prototype.onPropertyChanged = function(c, g) { + w.prototype.onPropertyChanged = function(c, g) { this.properties[c] = g; this.str = "number" == typeof g ? g.toFixed(3) : g; return !0; }; - z.registerNodeType("widget/text", v); + z.registerNodeType("widget/text", w); E.title = "Panel"; E.desc = "Non interactive panel"; E.widgets = [{name:"update", text:"Update", type:"button"}]; @@ -6609,11 +6612,11 @@ $jscomp.polyfill("Object.values", function(w) { }; z.registerNodeType("widget/panel", E); })(this); -(function(w) { +(function(v) { function c() { this.addOutput("left_x_axis", "number"); this.addOutput("left_y_axis", "number"); - this.addOutput("button_pressed", p.EVENT); + this.addOutput("button_pressed", q.EVENT); this.properties = {gamepad_index:0, threshold:0.1}; this._left_axis = new Float32Array(2); this._right_axis = new Float32Array(2); @@ -6621,7 +6624,7 @@ $jscomp.polyfill("Object.values", function(w) { this._previous_buttons = new Uint8Array(17); this._current_buttons = new Uint8Array(17); } - var p = w.LiteGraph; + var q = v.LiteGraph; c.title = "Gamepad"; c.desc = "gets the input of the gamepad"; c.CENTER = 0; @@ -6636,11 +6639,11 @@ $jscomp.polyfill("Object.values", function(w) { m && (this._left_axis[0] = Math.abs(m.xbox.axes.lx) > g ? m.xbox.axes.lx : 0, this._left_axis[1] = Math.abs(m.xbox.axes.ly) > g ? m.xbox.axes.ly : 0, this._right_axis[0] = Math.abs(m.xbox.axes.rx) > g ? m.xbox.axes.rx : 0, this._right_axis[1] = Math.abs(m.xbox.axes.ry) > g ? m.xbox.axes.ry : 0, this._triggers[0] = Math.abs(m.xbox.axes.ltrigger) > g ? m.xbox.axes.ltrigger : 0, this._triggers[1] = Math.abs(m.xbox.axes.rtrigger) > g ? m.xbox.axes.rtrigger : 0); if (this.outputs) { for (g = 0; g < this.outputs.length; g++) { - var p = this.outputs[g]; - if (p.links && p.links.length) { + var r = this.outputs[g]; + if (r.links && r.links.length) { var l = null; if (m) { - switch(p.name) { + switch(r.name) { case "left_axis": l = this._left_axis; break; @@ -6711,12 +6714,12 @@ $jscomp.polyfill("Object.values", function(w) { l = m.xbox.buttons.back ? 1 : 0; break; case "button_pressed": - for (p = 0; p < this._current_buttons.length; ++p) { - this._current_buttons[p] && !this._previous_buttons[p] && this.triggerSlot(g, c.buttons[p]); + for (r = 0; r < this._current_buttons.length; ++r) { + this._current_buttons[r] && !this._previous_buttons[r] && this.triggerSlot(g, c.buttons[r]); } } } else { - switch(p.name) { + switch(r.name) { case "button_pressed": break; case "left_axis": @@ -6754,25 +6757,25 @@ $jscomp.polyfill("Object.values", function(w) { g.axes.rtrigger = m.buttons[7].value; g.hat = ""; g.hatmap = c.CENTER; - for (var p = 0; p < m.buttons.length; p++) { - if (this._current_buttons[p] = m.buttons[p].pressed, 12 > p) { - g.buttons[c.mapping_array[p]] = m.buttons[p].pressed, m.buttons[p].was_pressed && this.trigger(c.mapping_array[p] + "_button_event"); + for (var r = 0; r < m.buttons.length; r++) { + if (this._current_buttons[r] = m.buttons[r].pressed, 12 > r) { + g.buttons[c.mapping_array[r]] = m.buttons[r].pressed, m.buttons[r].was_pressed && this.trigger(c.mapping_array[r] + "_button_event"); } else { - switch(p) { + switch(r) { case 12: - m.buttons[p].pressed && (g.hat += "up", g.hatmap |= c.UP); + m.buttons[r].pressed && (g.hat += "up", g.hatmap |= c.UP); break; case 13: - m.buttons[p].pressed && (g.hat += "down", g.hatmap |= c.DOWN); + m.buttons[r].pressed && (g.hat += "down", g.hatmap |= c.DOWN); break; case 14: - m.buttons[p].pressed && (g.hat += "left", g.hatmap |= c.LEFT); + m.buttons[r].pressed && (g.hat += "left", g.hatmap |= c.LEFT); break; case 15: - m.buttons[p].pressed && (g.hat += "right", g.hatmap |= c.RIGHT); + m.buttons[r].pressed && (g.hat += "right", g.hatmap |= c.RIGHT); break; case 16: - g.buttons.home = m.buttons[p].pressed; + g.buttons.home = m.buttons[r].pressed; } } } @@ -6796,17 +6799,17 @@ $jscomp.polyfill("Object.values", function(w) { } }; c.prototype.onGetOutputs = function() { - return [["left_axis", "vec2"], ["right_axis", "vec2"], ["left_x_axis", "number"], ["left_y_axis", "number"], ["right_x_axis", "number"], ["right_y_axis", "number"], ["trigger_left", "number"], ["trigger_right", "number"], ["a_button", "number"], ["b_button", "number"], ["x_button", "number"], ["y_button", "number"], ["lb_button", "number"], ["rb_button", "number"], ["ls_button", "number"], ["rs_button", "number"], ["start_button", "number"], ["back_button", "number"], ["a_button_event", p.EVENT], - ["b_button_event", p.EVENT], ["x_button_event", p.EVENT], ["y_button_event", p.EVENT], ["lb_button_event", p.EVENT], ["rb_button_event", p.EVENT], ["ls_button_event", p.EVENT], ["rs_button_event", p.EVENT], ["start_button_event", p.EVENT], ["back_button_event", p.EVENT], ["hat_left", "number"], ["hat_right", "number"], ["hat_up", "number"], ["hat_down", "number"], ["hat", "number"], ["button_pressed", p.EVENT]]; + return [["left_axis", "vec2"], ["right_axis", "vec2"], ["left_x_axis", "number"], ["left_y_axis", "number"], ["right_x_axis", "number"], ["right_y_axis", "number"], ["trigger_left", "number"], ["trigger_right", "number"], ["a_button", "number"], ["b_button", "number"], ["x_button", "number"], ["y_button", "number"], ["lb_button", "number"], ["rb_button", "number"], ["ls_button", "number"], ["rs_button", "number"], ["start_button", "number"], ["back_button", "number"], ["a_button_event", q.EVENT], + ["b_button_event", q.EVENT], ["x_button_event", q.EVENT], ["y_button_event", q.EVENT], ["lb_button_event", q.EVENT], ["rb_button_event", q.EVENT], ["ls_button_event", q.EVENT], ["rs_button_event", q.EVENT], ["start_button_event", q.EVENT], ["back_button_event", q.EVENT], ["hat_left", "number"], ["hat_right", "number"], ["hat_up", "number"], ["hat_down", "number"], ["hat", "number"], ["button_pressed", q.EVENT]]; }; - p.registerNodeType("input/gamepad", c); + q.registerNodeType("input/gamepad", c); })(this); -(function(w) { +(function(v) { function c() { this.addInput("in", "*"); this.size = [80, 30]; } - function p() { + function q() { this.addInput("in"); this.addOutput("out"); this.size = [80, 30]; @@ -6826,7 +6829,7 @@ $jscomp.polyfill("Object.values", function(w) { this.addProperty("out_max", 1); this.size = [120, 50]; } - function u() { + function r() { this.addOutput("value", "number"); this.addProperty("min", 0); this.addProperty("max", 1); @@ -6855,7 +6858,7 @@ $jscomp.polyfill("Object.values", function(w) { this.addProperty("min", 0); this.addProperty("max", 1); } - function v() { + function w() { this.properties = {f:0.5}; this.addInput("A", "number"); this.addInput("B", "number"); @@ -6888,7 +6891,7 @@ $jscomp.polyfill("Object.values", function(w) { this.size = [80, 30]; this.addProperty("factor", 1); } - function t() { + function u() { this.addInput("v", "boolean"); this.addInput("A"); this.addInput("B"); @@ -6909,13 +6912,13 @@ $jscomp.polyfill("Object.values", function(w) { this.size = [80, 30]; this._value = null; } - function q() { + function p() { this.addInput("A", "number"); this.addInput("B", "number"); this.addOutput("=", "number"); this.addProperty("A", 1); this.addProperty("B", 1); - this.addProperty("OP", "+", "enum", {values:q.values}); + this.addProperty("OP", "+", "enum", {values:p.values}); } function k() { this.addInput("A", "number"); @@ -6991,13 +6994,13 @@ $jscomp.polyfill("Object.values", function(w) { this.addOutput("z", "number"); this.addOutput("w", "number"); } - function L() { + function K() { this.addInputs([["x", "number"], ["y", "number"], ["z", "number"], ["w", "number"]]); this.addOutput("vec4", "vec4"); this.properties = {x:0, y:0, z:0, w:0}; this._data = new Float32Array(4); } - var A = w.LiteGraph; + var A = v.LiteGraph; c.title = "Converter"; c.desc = "type A to type B"; c.prototype.onExecute = function() { @@ -7042,13 +7045,13 @@ $jscomp.polyfill("Object.values", function(w) { return [["number", "number"], ["vec2", "vec2"], ["vec3", "vec3"], ["vec4", "vec4"]]; }; A.registerNodeType("math/converter", c); - p.title = "Bypass"; - p.desc = "removes the type"; - p.prototype.onExecute = function() { + q.title = "Bypass"; + q.desc = "removes the type"; + q.prototype.onExecute = function() { var a = this.getInputData(0); this.setOutputData(0, a); }; - A.registerNodeType("math/bypass", p); + A.registerNodeType("math/bypass", q); m.title = "to Number"; m.desc = "Cast to number"; m.prototype.onExecute = function() { @@ -7086,9 +7089,9 @@ $jscomp.polyfill("Object.values", function(w) { return [["in_min", "number"], ["in_max", "number"], ["out_min", "number"], ["out_max", "number"]]; }; A.registerNodeType("math/range", g); - u.title = "Rand"; - u.desc = "Random number"; - u.prototype.onExecute = function() { + r.title = "Rand"; + r.desc = "Random number"; + r.prototype.onExecute = function() { if (this.inputs) { for (var a = 0; a < this.inputs.length; a++) { var b = this.inputs[a], d = this.getInputData(a); @@ -7099,13 +7102,13 @@ $jscomp.polyfill("Object.values", function(w) { this._last_v = Math.random() * (this.properties.max - a) + a; this.setOutputData(0, this._last_v); }; - u.prototype.onDrawBackground = function(a) { + r.prototype.onDrawBackground = function(a) { this.outputs[0].label = (this._last_v || 0).toFixed(3); }; - u.prototype.onGetInputs = function() { + r.prototype.onGetInputs = function() { return [["min", "number"], ["max", "number"]]; }; - A.registerNodeType("math/rand", u); + A.registerNodeType("math/rand", r); l.title = "Noise"; l.desc = "Random number with temporal continuity"; l.data = null; @@ -7160,9 +7163,9 @@ $jscomp.polyfill("Object.values", function(w) { return a; }; A.registerNodeType("math/clamp", y); - v.title = "Lerp"; - v.desc = "Linear Interpolation"; - v.prototype.onExecute = function() { + w.title = "Lerp"; + w.desc = "Linear Interpolation"; + w.prototype.onExecute = function() { var a = this.getInputData(0); null == a && (a = 0); var b = this.getInputData(1); @@ -7171,10 +7174,10 @@ $jscomp.polyfill("Object.values", function(w) { void 0 !== c && (d = c); this.setOutputData(0, a * (1 - d) + b * d); }; - v.prototype.onGetInputs = function() { + w.prototype.onGetInputs = function() { return [["f", "number"]]; }; - A.registerNodeType("math/lerp", v); + A.registerNodeType("math/lerp", w); E.title = "Abs"; E.desc = "Absolute"; E.prototype.onExecute = function() { @@ -7214,13 +7217,13 @@ $jscomp.polyfill("Object.values", function(w) { null != a && this.setOutputData(0, a * this.properties.factor); }; A.registerNodeType("math/scale", D); - t.title = "Gate"; - t.desc = "if v is true, then outputs A, otherwise B"; - t.prototype.onExecute = function() { + u.title = "Gate"; + u.desc = "if v is true, then outputs A, otherwise B"; + u.prototype.onExecute = function() { var a = this.getInputData(0); this.setOutputData(0, this.getInputData(a ? 1 : 2)); }; - A.registerNodeType("math/gate", t); + A.registerNodeType("math/gate", u); G.title = "Average"; G.desc = "Average Filter"; G.prototype.onExecute = function() { @@ -7253,19 +7256,19 @@ $jscomp.polyfill("Object.values", function(w) { this.setOutputData(0, this._value); }; A.registerNodeType("math/tendTo", n); - q.values = "+ - * / % ^ max min".split(" "); - q.title = "Operation"; - q.desc = "Easy math operators"; - q["@OP"] = {type:"enum", title:"operation", values:q.values}; - q.size = [100, 60]; - q.prototype.getTitle = function() { + p.values = "+ - * / % ^ max min".split(" "); + p.title = "Operation"; + p.desc = "Easy math operators"; + p["@OP"] = {type:"enum", title:"operation", values:p.values}; + p.size = [100, 60]; + p.prototype.getTitle = function() { return "max" == this.properties.OP || "min" == this.properties.OP ? this.properties.OP + "(A,B)" : "A " + this.properties.OP + " B"; }; - q.prototype.setValue = function(a) { + p.prototype.setValue = function(a) { "string" == typeof a && (a = parseFloat(a)); this.properties.value = a; }; - q.prototype.onExecute = function() { + p.prototype.onExecute = function() { var a = this.getInputData(0), b = this.getInputData(1); null != a ? this.properties.A = a : a = this.properties.A; null != b ? this.properties.B = b : b = this.properties.B; @@ -7302,10 +7305,10 @@ $jscomp.polyfill("Object.values", function(w) { } this.setOutputData(0, d); }; - q.prototype.onDrawBackground = function(a) { + p.prototype.onDrawBackground = function(a) { this.flags.collapsed || (a.font = "40px Arial", a.fillStyle = "#666", a.textAlign = "center", a.fillText(this.properties.OP, 0.5 * this.size[0], 0.5 * (this.size[1] + A.NODE_TITLE_HEIGHT)), a.textAlign = "left"); }; - A.registerNodeType("math/operation", q); + A.registerNodeType("math/operation", p); A.registerSearchboxExtra("math/operation", "MAX", {properties:{OP:"max"}, title:"MAX()"}); A.registerSearchboxExtra("math/operation", "MIN", {properties:{OP:"min"}, title:"MIN()"}); k.title = "Compare"; @@ -7460,7 +7463,7 @@ $jscomp.polyfill("Object.values", function(w) { this._func && this._func_code == this.properties.formula || (this._func = new Function("x", "y", "TIME", "return " + this.properties.formula), this._func_code = this.properties.formula); var d = this._func(a, b, this.graph.globaltime); this.boxcolor = null; - } catch (M) { + } catch (N) { this.boxcolor = "red"; } this.setOutputData(0, d); @@ -7524,9 +7527,9 @@ $jscomp.polyfill("Object.values", function(w) { null != a && (this.setOutputData(0, a[0]), this.setOutputData(1, a[1]), this.setOutputData(2, a[2]), this.setOutputData(3, a[3])); }; A.registerNodeType("math3d/vec4-to-xyzw", J); - L.title = "XYZW->Vec4"; - L.desc = "components to vector4"; - L.prototype.onExecute = function() { + K.title = "XYZW->Vec4"; + K.desc = "components to vector4"; + K.prototype.onExecute = function() { var a = this.getInputData(0); null == a && (a = this.properties.x); var b = this.getInputData(1); @@ -7542,9 +7545,9 @@ $jscomp.polyfill("Object.values", function(w) { k[3] = c; this.setOutputData(0, k); }; - A.registerNodeType("math3d/xyzw-to-vec4", L); + A.registerNodeType("math3d/xyzw-to-vec4", K); })(this); -(function(w) { +(function(v) { function c() { this.addInput("sel", "number"); this.addInput("A"); @@ -7554,7 +7557,7 @@ $jscomp.polyfill("Object.values", function(w) { this.addOutput("out"); this.selected = 0; } - function p() { + function q() { this.properties = {sequence:"A,B,C"}; this.addInput("index", "number"); this.addInput("seq"); @@ -7562,7 +7565,7 @@ $jscomp.polyfill("Object.values", function(w) { this.index = 0; this.values = this.properties.sequence.split(","); } - var m = w.LiteGraph; + var m = v.LiteGraph; c.title = "Selector"; c.desc = "selects an output"; c.prototype.onDrawBackground = function(c) { @@ -7589,12 +7592,12 @@ $jscomp.polyfill("Object.values", function(w) { return [["E", 0], ["F", 0], ["G", 0], ["H", 0]]; }; m.registerNodeType("logic/selector", c); - p.title = "Sequence"; - p.desc = "select one element from a sequence from a string"; - p.prototype.onPropertyChanged = function(c, m) { + q.title = "Sequence"; + q.desc = "select one element from a sequence from a string"; + q.prototype.onPropertyChanged = function(c, m) { "sequence" == c && (this.values = m.split(",")); }; - p.prototype.onExecute = function() { + q.prototype.onExecute = function() { var c = this.getInputData(1); c && c != this.current_sequence && (this.values = c.split(","), this.current_sequence = c); c = this.getInputData(0); @@ -7602,16 +7605,16 @@ $jscomp.polyfill("Object.values", function(w) { this.index = c = Math.round(c) % this.values.length; this.setOutputData(0, this.values[c]); }; - m.registerNodeType("logic/sequence", p); + m.registerNodeType("logic/sequence", q); })(this); -(function(w) { +(function(v) { function c() { this.addOutput("tex", "Texture"); this.addOutput("name", "string"); this.properties = {name:"", filter:!0}; this.size = [c.image_preview_size, c.image_preview_size]; } - function p() { + function q() { this.addInput("Texture", "Texture"); this.properties = {flipY:!1}; this.size = [c.image_preview_size, c.image_preview_size]; @@ -7631,10 +7634,10 @@ $jscomp.polyfill("Object.values", function(w) { this.properties = {value:1, pixelcode:"color + colorB * value", uvcode:"", precision:c.DEFAULT}; this.has_error = !1; } - function u() { + function r() { this.addOutput("out", "Texture"); this.properties = {code:"", u_value:1, u_color:[1, 1, 1, 1], width:512, height:512, precision:c.DEFAULT}; - this.properties.code = u.pixel_shader; + this.properties.code = r.pixel_shader; this._uniforms = {u_value:1, u_color:vec4.create(), in_texture:0, texSize:vec2.create(), time:0}; } function l() { @@ -7657,7 +7660,7 @@ $jscomp.polyfill("Object.values", function(w) { this.properties = {additive:!1, antialiasing:!1, filter:!0, disable_alpha:!1, gamma:1.0, viewport:[0, 0, 1, 1]}; this.size[0] = 130; } - function v() { + function w() { this.addInput("Texture", "Texture"); this.addOutput("", "Texture"); this.properties = {size:0, generate_mipmaps:!1, precision:c.DEFAULT}; @@ -7696,13 +7699,13 @@ $jscomp.polyfill("Object.values", function(w) { this.addOutput("", "Texture"); this.properties = {}; } - function t() { + function u() { this.addInput("Texture", "Texture"); this.addInput("LUT", "Texture"); this.addInput("Intensity", "number"); this.addOutput("", "Texture"); this.properties = {enabled:!0, intensity:1, precision:c.DEFAULT, texture:null}; - t._shader || (t._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, t.pixel_shader)); + u._shader || (u._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, u.pixel_shader)); } function G() { this.addInput("Texture", "Texture"); @@ -7722,7 +7725,7 @@ $jscomp.polyfill("Object.values", function(w) { this._color = vec4.create(); this._uniforms = {u_textureR:0, u_textureG:1, u_textureB:2, u_textureA:3, u_color:this._color}; } - function q() { + function p() { this.addOutput("Texture", "Texture"); this._tex_color = vec4.create(); this.properties = {color:vec4.create(), precision:c.DEFAULT}; @@ -7795,7 +7798,7 @@ $jscomp.polyfill("Object.values", function(w) { this.boxcolor = "black"; this.version = 0; } - function L() { + function K() { this.addInput("in", "Texture"); this.addInput("f", "number"); this.addOutput("out", "Texture"); @@ -7818,31 +7821,31 @@ $jscomp.polyfill("Object.values", function(w) { this.curve_offset = 68; this.size = [240, 160]; } - function r() { + function t() { this.addInput("in", "Texture"); this.addInput("exp", "number"); this.addOutput("out", "Texture"); this.properties = {exposition:1, precision:c.LOW}; this._uniforms = {u_texture:0, u_exposition:1}; } - function K() { + function L() { this.addInput("in", "Texture"); this.addInput("avg", "number,Texture"); this.addOutput("out", "Texture"); this.properties = {enabled:!0, scale:1, gamma:1, average_lum:1, lum_white:1, precision:c.LOW}; this._uniforms = {u_texture:0, u_lumwhite2:1, u_igamma:1, u_scale:1, u_average_lum:1}; } - function N() { + function M() { this.addOutput("out", "Texture"); this.properties = {width:512, height:512, seed:0, persistence:0.1, octaves:8, scale:1, offset:[0, 0], amplitude:1, precision:c.DEFAULT}; this._key = 0; this._texture = null; this._uniforms = {u_persistence:0.1, u_seed:0, u_offset:vec2.create(), u_scale:1, u_viewport:vec2.create()}; } - function M() { + function N() { this.addInput("v"); this.addOutput("out", "Texture"); - this.properties = {code:M.default_code, width:512, height:512, clear:!0, precision:c.DEFAULT, use_html_canvas:!1}; + this.properties = {code:N.default_code, width:512, height:512, clear:!0, precision:c.DEFAULT, use_html_canvas:!1}; this._temp_texture = this._func = null; this.compileCode(); } @@ -7857,9 +7860,9 @@ $jscomp.polyfill("Object.values", function(w) { this.addOutput("out", "texture"); this.properties = {yaw:0}; } - var F = w.LiteGraph; - w.LGraphTexture = null; - "undefined" != typeof GL && (LGraphCanvas.link_type_colors.Texture = "#987", w.LGraphTexture = c, c.title = "Texture", c.desc = "Texture", c.widgets_info = {name:{widget:"texture"}, filter:{widget:"checkbox"}}, c.loadTextureCallback = null, c.image_preview_size = 256, c.PASS_THROUGH = 1, c.COPY = 2, c.LOW = 3, c.HIGH = 4, c.REUSE = 5, c.DEFAULT = 2, c.MODE_VALUES = {"pass through":c.PASS_THROUGH, copy:c.COPY, low:c.LOW, high:c.HIGH, reuse:c.REUSE, default:c.DEFAULT}, c.getTexturesContainer = function() { + var F = v.LiteGraph; + v.LGraphTexture = null; + "undefined" != typeof GL && (LGraphCanvas.link_type_colors.Texture = "#987", v.LGraphTexture = c, c.title = "Texture", c.desc = "Texture", c.widgets_info = {name:{widget:"texture"}, filter:{widget:"checkbox"}}, c.loadTextureCallback = null, c.image_preview_size = 256, c.PASS_THROUGH = 1, c.COPY = 2, c.LOW = 3, c.HIGH = 4, c.REUSE = 5, c.DEFAULT = 2, c.MODE_VALUES = {"pass through":c.PASS_THROUGH, copy:c.COPY, low:c.LOW, high:c.HIGH, reuse:c.REUSE, default:c.DEFAULT}, c.getTexturesContainer = function() { return gl.textures; }, c.loadTexture = function(a, b) { b = b || {}; @@ -7991,12 +7994,12 @@ $jscomp.polyfill("Object.values", function(w) { a = a.replace(/[\{\}]/g, ""); return b[a] || ""; }); - }, F.registerNodeType("texture/texture", c), p.title = "Preview", p.desc = "Show a texture in the graph canvas", p.allow_preview = !1, p.prototype.onDrawBackground = function(a) { - if (!this.flags.collapsed && (a.webgl || p.allow_preview)) { + }, F.registerNodeType("texture/texture", c), q.title = "Preview", q.desc = "Show a texture in the graph canvas", q.allow_preview = !1, q.prototype.onDrawBackground = function(a) { + if (!this.flags.collapsed && (a.webgl || q.allow_preview)) { var b = this.getInputData(0); b && (b = !b.handle && a.webgl ? b : c.generateLowResTexturePreview(b), a.save(), this.properties.flipY && (a.translate(0, this.size[1]), a.scale(1, -1)), a.drawImage(b, 0, 0, this.size[0], this.size[1]), a.restore()); } - }, F.registerNodeType("texture/preview", p), m.title = "Save", m.desc = "Save a texture in the repository", m.prototype.getPreviewTexture = function() { + }, F.registerNodeType("texture/preview", q), m.title = "Save", m.desc = "Save a texture in the repository", m.prototype.getPreviewTexture = function() { return this._texture; }, m.prototype.onExecute = function() { var a = this.getInputData(0); @@ -8042,8 +8045,8 @@ $jscomp.polyfill("Object.values", function(w) { this._shader_code = f + "|" + h; } if (this._shader) { - var q = this.getInputData(2); - null != q ? this.properties.value = q : q = parseFloat(this.properties.value); + var p = this.getInputData(2); + null != p ? this.properties.value = p : p = parseFloat(this.properties.value); var l = this.graph.getTime(); this._tex.drawTo(function() { gl.disable(gl.DEPTH_TEST); @@ -8052,7 +8055,7 @@ $jscomp.polyfill("Object.values", function(w) { a && a.bind(0); b && b.bind(1); var c = Mesh.getScreenQuad(); - e.uniforms({u_texture:0, u_textureB:1, value:q, texSize:[d, k], time:l}).draw(c); + e.uniforms({u_texture:0, u_textureB:1, value:p, texSize:[d, k], time:l}).draw(c); }); this.setOutputData(0, this._tex); } @@ -8070,7 +8073,7 @@ $jscomp.polyfill("Object.values", function(w) { var c = g.presets[d]; c && (b.setProperty("pixelcode", c), b.title = d, a.refresh()); }}); - }, F.registerNodeType("texture/operation", g), u.title = "Shader", u.desc = "Texture shader", u.widgets_info = {code:{type:"code", lang:"glsl"}, precision:{widget:"combo", values:c.MODE_VALUES}}, u.prototype.onPropertyChanged = function(a, b) { + }, F.registerNodeType("texture/operation", g), r.title = "Shader", r.desc = "Texture shader", r.widgets_info = {code:{type:"code", lang:"glsl"}, precision:{widget:"combo", values:c.MODE_VALUES}}, r.prototype.onPropertyChanged = function(a, b) { if ("code" == a && (a = this.getShader())) { b = a.uniformInfo; if (this.inputs) { @@ -8118,14 +8121,14 @@ $jscomp.polyfill("Object.values", function(w) { } } } - }, u.prototype.getShader = function() { + }, r.prototype.getShader = function() { if (this._shader && this._shader_code == this.properties.code) { return this._shader; } this._shader_code = this.properties.code; this._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, this.properties.code), this.boxcolor = "green"; return this._shader; - }, u.prototype.onExecute = function() { + }, r.prototype.onExecute = function() { if (this.isOutputConnected(0)) { var a = this.getShader(); if (a) { @@ -8154,7 +8157,7 @@ $jscomp.polyfill("Object.values", function(w) { this.setOutputData(0, this._tex); } } - }, u.pixel_shader = "precision highp float;\n\r\n\n\r\nvarying vec2 v_coord;\n\r\nuniform float time; //time in seconds\n\r\nuniform vec2 texSize; //tex resolution\n\r\nuniform float u_value;\n\r\nuniform vec4 u_color;\n\n\r\nvoid main() {\n\r\n\tvec2 uv = v_coord;\n\r\n\tvec3 color = vec3(0.0);\n\r\n\t//your code here\n\r\n\tcolor.xy=uv;\n\n\r\n\tgl_FragColor = vec4(color, 1.0);\n\r\n}\n\r\n", F.registerNodeType("texture/shader", u), l.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, + }, r.pixel_shader = "precision highp float;\n\r\n\n\r\nvarying vec2 v_coord;\n\r\nuniform float time; //time in seconds\n\r\nuniform vec2 texSize; //tex resolution\n\r\nuniform float u_value;\n\r\nuniform vec4 u_color;\n\n\r\nvoid main() {\n\r\n\tvec2 uv = v_coord;\n\r\n\tvec3 color = vec3(0.0);\n\r\n\t//your code here\n\r\n\tcolor.xy=uv;\n\n\r\n\tgl_FragColor = vec4(color, 1.0);\n\r\n}\n\r\n", F.registerNodeType("texture/shader", r), l.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, l.title = "Scale/Offset", l.desc = "Applies an scaling and offseting", l.prototype.onExecute = function() { var a = this.getInputData(0); if (this.isOutputConnected(0) && a) { @@ -8231,8 +8234,8 @@ $jscomp.polyfill("Object.values", function(w) { }, y.prototype.onGetInputs = function() { return [["gamma", "number"]]; }, y.aa_pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec2 uViewportSize;\n\r\n\t\tuniform vec2 inverseVP;\n\r\n\t\tuniform float u_igamma;\n\r\n\t\t#define FXAA_REDUCE_MIN (1.0/ 128.0)\n\r\n\t\t#define FXAA_REDUCE_MUL (1.0 / 8.0)\n\r\n\t\t#define FXAA_SPAN_MAX 8.0\n\r\n\t\t\n\r\n\t\t/* from mitsuhiko/webgl-meincraft based on the code on geeks3d.com */\n\r\n\t\tvec4 applyFXAA(sampler2D tex, vec2 fragCoord)\n\r\n\t\t{\n\r\n\t\t\tvec4 color = vec4(0.0);\n\r\n\t\t\t/*vec2 inverseVP = vec2(1.0 / uViewportSize.x, 1.0 / uViewportSize.y);*/\n\r\n\t\t\tvec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * inverseVP).xyz;\n\r\n\t\t\tvec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * inverseVP).xyz;\n\r\n\t\t\tvec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * inverseVP).xyz;\n\r\n\t\t\tvec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * inverseVP).xyz;\n\r\n\t\t\tvec3 rgbM = texture2D(tex, fragCoord * inverseVP).xyz;\n\r\n\t\t\tvec3 luma = vec3(0.299, 0.587, 0.114);\n\r\n\t\t\tfloat lumaNW = dot(rgbNW, luma);\n\r\n\t\t\tfloat lumaNE = dot(rgbNE, luma);\n\r\n\t\t\tfloat lumaSW = dot(rgbSW, luma);\n\r\n\t\t\tfloat lumaSE = dot(rgbSE, luma);\n\r\n\t\t\tfloat lumaM = dot(rgbM, luma);\n\r\n\t\t\tfloat lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n\r\n\t\t\tfloat lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\r\n\t\t\t\n\r\n\t\t\tvec2 dir;\n\r\n\t\t\tdir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n\r\n\t\t\tdir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\r\n\t\t\t\n\r\n\t\t\tfloat dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\r\n\t\t\t\n\r\n\t\t\tfloat rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n\r\n\t\t\tdir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * inverseVP;\n\r\n\t\t\t\n\r\n\t\t\tvec3 rgbA = 0.5 * (texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + \n\r\n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz);\n\r\n\t\t\tvec3 rgbB = rgbA * 0.5 + 0.25 * (texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + \n\r\n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz);\n\r\n\t\t\t\n\r\n\t\t\t//return vec4(rgbA,1.0);\n\r\n\t\t\tfloat lumaB = dot(rgbB, luma);\n\r\n\t\t\tif ((lumaB < lumaMin) || (lumaB > lumaMax))\n\r\n\t\t\t\tcolor = vec4(rgbA, 1.0);\n\r\n\t\t\telse\n\r\n\t\t\t\tcolor = vec4(rgbB, 1.0);\n\r\n\t\t\tif(u_igamma != 1.0)\n\r\n\t\t\t\tcolor.xyz = pow( color.xyz, vec3(u_igamma) );\n\r\n\t\t\treturn color;\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\r\n\t\t}\n\r\n\t\t", - y.gamma_pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_igamma;\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\r\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\r\n\t\t gl_FragColor = color;\n\r\n\t\t}\n\r\n\t\t", F.registerNodeType("texture/toviewport", y), v.title = "Copy", v.desc = "Copy Texture", v.widgets_info = {size:{widget:"combo", - values:[0, 32, 64, 128, 256, 512, 1024, 2048]}, precision:{widget:"combo", values:c.MODE_VALUES}}, v.prototype.onExecute = function() { + y.gamma_pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_igamma;\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\r\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\r\n\t\t gl_FragColor = color;\n\r\n\t\t}\n\r\n\t\t", F.registerNodeType("texture/toviewport", y), w.title = "Copy", w.desc = "Copy Texture", w.widgets_info = {size:{widget:"combo", + values:[0, 32, 64, 128, 256, 512, 1024, 2048]}, precision:{widget:"combo", values:c.MODE_VALUES}}, w.prototype.onExecute = function() { var a = this.getInputData(0); if ((a || this._temp_texture) && this.isOutputConnected(0)) { if (a) { @@ -8246,7 +8249,7 @@ $jscomp.polyfill("Object.values", function(w) { } this.setOutputData(0, this._temp_texture); } - }, F.registerNodeType("texture/copy", v), E.title = "Downsample", E.desc = "Downsample Texture", E.widgets_info = {iterations:{type:"number", step:1, precision:0, min:0}, precision:{widget:"combo", values:c.MODE_VALUES}}, E.prototype.onExecute = function() { + }, F.registerNodeType("texture/copy", w), E.title = "Downsample", E.desc = "Downsample Texture", E.widgets_info = {iterations:{type:"number", step:1, precision:0, min:0}, precision:{widget:"combo", values:c.MODE_VALUES}}, E.prototype.onExecute = function() { var a = this.getInputData(0); if ((a || this._temp_texture) && this.isOutputConnected(0) && a && a.texture_type === GL.TEXTURE_2D) { if (1 > this.properties.iterations) { @@ -8258,7 +8261,7 @@ $jscomp.polyfill("Object.values", function(w) { this.properties.precision === c.LOW ? f = gl.UNSIGNED_BYTE : this.properties.precision === c.HIGH && (f = gl.HIGH_PRECISION_FORMAT); var h = this.properties.iterations || 1, e = a, n = []; f = {type:f, format:a.format}; - var g = vec2.create(), q = {u_offset:g}; + var g = vec2.create(), p = {u_offset:g}; this._texture && GL.Texture.releaseTemporary(this._texture); for (var l = 0; l < h; ++l) { g[0] = 1 / d; @@ -8268,7 +8271,7 @@ $jscomp.polyfill("Object.values", function(w) { a = GL.Texture.getTemporary(d, k, f); n.push(a); e.setParameter(GL.TEXTURE_MAG_FILTER, GL.NEAREST); - e.copyTo(a, b, q); + e.copyTo(a, b, p); if (1 == d && 1 == k) { break; } @@ -8395,7 +8398,7 @@ $jscomp.polyfill("Object.values", function(w) { this.setOutputData(0, this._temp_texture); } } - }, F.registerNodeType("texture/imageToTexture", D), t.widgets_info = {texture:{widget:"texture"}, precision:{widget:"combo", values:c.MODE_VALUES}}, t.title = "LUT", t.desc = "Apply LUT to Texture", t.prototype.onExecute = function() { + }, F.registerNodeType("texture/imageToTexture", D), u.widgets_info = {texture:{widget:"texture"}, precision:{widget:"combo", values:c.MODE_VALUES}}, u.title = "LUT", u.desc = "Apply LUT to Texture", u.prototype.onExecute = function() { if (this.isOutputConnected(0)) { var a = this.getInputData(0); if (this.properties.precision === c.PASS_THROUGH || !1 === this.properties.enabled) { @@ -8415,7 +8418,7 @@ $jscomp.polyfill("Object.values", function(w) { this._tex = c.getTargetTexture(a, this._tex, this.properties.precision); this._tex.drawTo(function() { b.bind(1); - a.toViewport(t._shader, {u_texture:0, u_textureB:1, u_amount:d}); + a.toViewport(u._shader, {u_texture:0, u_textureB:1, u_amount:d}); }); this.setOutputData(0, this._tex); } else { @@ -8424,8 +8427,8 @@ $jscomp.polyfill("Object.values", function(w) { } } } - }, t.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tuniform float u_amount;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\r\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\r\n\t\t\t mediump vec2 quad1;\n\r\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\r\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\r\n\t\t\t mediump vec2 quad2;\n\r\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\r\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\r\n\t\t\t highp vec2 texPos1;\n\r\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\r\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\r\n\t\t\t highp vec2 texPos2;\n\r\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\r\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\r\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\r\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\r\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\r\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\r\n\t\t}\n\r\n\t\t", - F.registerNodeType("texture/LUT", t), G.title = "Texture to Channels", G.desc = "Split texture channels", G.prototype.onExecute = function() { + }, u.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tuniform float u_amount;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\r\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\r\n\t\t\t mediump vec2 quad1;\n\r\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\r\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\r\n\t\t\t mediump vec2 quad2;\n\r\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\r\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\r\n\t\t\t highp vec2 texPos1;\n\r\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\r\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\r\n\t\t\t highp vec2 texPos2;\n\r\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\r\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\r\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\r\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\r\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\r\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\r\n\t\t}\n\r\n\t\t", + F.registerNodeType("texture/LUT", u), G.title = "Texture to Channels", G.desc = "Split texture channels", G.prototype.onExecute = function() { var a = this.getInputData(0); if (a) { this._channels || (this._channels = Array(4)); @@ -8453,8 +8456,8 @@ $jscomp.polyfill("Object.values", function(w) { n._shader || (n._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, n.pixel_shader)); var e = n._shader; a = Math.max(b.width, d.width, k.width, f.width); - var g = Math.max(b.height, d.height, k.height, f.height), q = this.properties.precision == c.HIGH ? c.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE; - this._texture && this._texture.width == a && this._texture.height == g && this._texture.type == q || (this._texture = new GL.Texture(a, g, {type:q, format:gl.RGBA, filter:gl.LINEAR})); + var g = Math.max(b.height, d.height, k.height, f.height), p = this.properties.precision == c.HIGH ? c.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE; + this._texture && this._texture.width == a && this._texture.height == g && this._texture.type == p || (this._texture = new GL.Texture(a, g, {type:p, format:gl.RGBA, filter:gl.LINEAR})); a = this._color; a[0] = this.properties.R; a[1] = this.properties.G; @@ -8470,11 +8473,11 @@ $jscomp.polyfill("Object.values", function(w) { }); this.setOutputData(0, this._texture); }, n.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_textureR;\n\r\n\t\tuniform sampler2D u_textureG;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tuniform sampler2D u_textureA;\n\r\n\t\tuniform vec4 u_color;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t gl_FragColor = u_color * vec4( \r\n\t\t\t\t\ttexture2D(u_textureR, v_coord).r,\r\n\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\r\n\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\r\n\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\r\n\t\t}\n\r\n\t\t", - F.registerNodeType("texture/channelsTexture", n), q.title = "Color", q.desc = "Generates a 1x1 texture with a constant color", q.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, q.prototype.onDrawBackground = function(a) { + F.registerNodeType("texture/channelsTexture", n), p.title = "Color", p.desc = "Generates a 1x1 texture with a constant color", p.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, p.prototype.onDrawBackground = function(a) { var b = this.properties.color; a.fillStyle = "rgb(" + Math.floor(255 * Math.clamp(b[0], 0, 1)) + "," + Math.floor(255 * Math.clamp(b[1], 0, 1)) + "," + Math.floor(255 * Math.clamp(b[2], 0, 1)) + ")"; this.flags.collapsed ? this.boxcolor = a.fillStyle : a.fillRect(0, 0, this.size[0], this.size[1]); - }, q.prototype.onExecute = function() { + }, p.prototype.onExecute = function() { var a = this.properties.precision == c.HIGH ? c.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE; this._tex && this._tex.type == a || (this._tex = new GL.Texture(1, 1, {format:gl.RGBA, type:a, minFilter:gl.NEAREST})); a = this.properties.color; @@ -8504,9 +8507,9 @@ $jscomp.polyfill("Object.values", function(w) { } 0.001 < vec4.sqrDist(this._tex_color, a) && (this._tex_color.set(a), this._tex.fill(a)); this.setOutputData(0, this._tex); - }, q.prototype.onGetInputs = function() { + }, p.prototype.onGetInputs = function() { return [["RGB", "vec3"], ["RGBA", "vec4"], ["R", "number"], ["G", "number"], ["B", "number"], ["A", "number"]]; - }, F.registerNodeType("texture/color", q), k.title = "Gradient", k.desc = "Generates a gradient", k["@A"] = {type:"color"}, k["@B"] = {type:"color"}, k["@texture_size"] = {type:"enum", values:[32, 64, 128, 256, 512]}, k.prototype.onExecute = function() { + }, F.registerNodeType("texture/color", p), k.title = "Gradient", k.desc = "Generates a gradient", k["@A"] = {type:"color"}, k["@B"] = {type:"color"}, k["@texture_size"] = {type:"enum", values:[32, 64, 128, 256, 512]}, k.prototype.onExecute = function() { gl.disable(gl.BLEND); gl.disable(gl.DEPTH_TEST); var a = GL.Mesh.getScreenQuad(), b = k._shader, d = this.getInputData(0); @@ -8679,51 +8682,51 @@ $jscomp.polyfill("Object.values", function(w) { h.u_threshold = this.getInputOrProperty("threshold"); var g = e[0] = GL.Texture.getTemporary(b, d, k); a.blit(g, n.uniforms(h)); - var q = g, l = this.getInputOrProperty("iterations"); + var p = g, l = this.getInputOrProperty("iterations"); l = Math.clamp(l, 1, 16) | 0; - var m = h.u_texel_size, p = this.getInputOrProperty("intensity"); + var m = h.u_texel_size, r = this.getInputOrProperty("intensity"); h.u_intensity = 1; h.u_delta = this.properties.scale; n = x._shader; n || (n = x._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, x.scale_pixel_shader)); - for (var r = 1; r < l; r++) { + for (var q = 1; q < l; q++) { b >>= 1; 1 < (d | 0) && (d >>= 1); if (2 > b) { break; } - g = e[r] = GL.Texture.getTemporary(b, d, k); - m[0] = 1 / q.width; - m[1] = 1 / q.height; - q.blit(g, n.uniforms(h)); - q = g; + g = e[q] = GL.Texture.getTemporary(b, d, k); + m[0] = 1 / p.width; + m[1] = 1 / p.height; + p.blit(g, n.uniforms(h)); + p = g; } - this.isOutputConnected(2) && (b = this._average_texture, b && b.type == a.type && b.format == a.format || (b = this._average_texture = new GL.Texture(1, 1, {type:a.type, format:a.format, filter:gl.LINEAR})), m[0] = 1 / q.width, m[1] = 1 / q.height, h.u_intensity = p, h.u_delta = 1, q.blit(b, n.uniforms(h)), this.setOutputData(2, b)); + this.isOutputConnected(2) && (b = this._average_texture, b && b.type == a.type && b.format == a.format || (b = this._average_texture = new GL.Texture(1, 1, {type:a.type, format:a.format, filter:gl.LINEAR})), m[0] = 1 / p.width, m[1] = 1 / p.height, h.u_intensity = r, h.u_delta = 1, p.blit(b, n.uniforms(h)), this.setOutputData(2, b)); gl.enable(gl.BLEND); gl.blendFunc(gl.ONE, gl.ONE); h.u_intensity = this.getInputOrProperty("persistence"); h.u_delta = 0.5; - for (r -= 2; 0 <= r; r--) { - g = e[r], e[r] = null, m[0] = 1 / q.width, m[1] = 1 / q.height, q.blit(g, n.uniforms(h)), GL.Texture.releaseTemporary(q), q = g; + for (q -= 2; 0 <= q; q--) { + g = e[q], e[q] = null, m[0] = 1 / p.width, m[1] = 1 / p.height, p.blit(g, n.uniforms(h)), GL.Texture.releaseTemporary(p), p = g; } gl.disable(gl.BLEND); - this.isOutputConnected(1) && (e = this._glow_texture, e && e.width == a.width && e.height == a.height && e.type == f && e.format == a.format || (e = this._glow_texture = new GL.Texture(a.width, a.height, {type:f, format:a.format, filter:gl.LINEAR})), q.blit(e), this.setOutputData(1, e)); + this.isOutputConnected(1) && (e = this._glow_texture, e && e.width == a.width && e.height == a.height && e.type == f && e.format == a.format || (e = this._glow_texture = new GL.Texture(a.width, a.height, {type:f, format:a.format, filter:gl.LINEAR})), p.blit(e), this.setOutputData(1, e)); if (this.isOutputConnected(0)) { e = this._final_texture; e && e.width == a.width && e.height == a.height && e.type == f && e.format == a.format || (e = this._final_texture = new GL.Texture(a.width, a.height, {type:f, format:a.format, filter:gl.LINEAR})); - var u = this.getInputData(1), t = this.getInputOrProperty("dirt_factor"); - h.u_intensity = p; - n = u ? x._dirt_final_shader : x._final_shader; - n || (n = u ? x._dirt_final_shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, x.final_pixel_shader, {USE_DIRT:""}) : x._final_shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, x.final_pixel_shader)); + var t = this.getInputData(1), u = this.getInputOrProperty("dirt_factor"); + h.u_intensity = r; + n = t ? x._dirt_final_shader : x._final_shader; + n || (n = t ? x._dirt_final_shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, x.final_pixel_shader, {USE_DIRT:""}) : x._final_shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, x.final_pixel_shader)); e.drawTo(function() { a.bind(0); - q.bind(1); - u && (n.setUniform("u_dirt_factor", t), n.setUniform("u_dirt_texture", u.bind(2))); + p.bind(1); + t && (n.setUniform("u_dirt_factor", u), n.setUniform("u_dirt_texture", t.bind(2))); n.toViewport(h); }); this.setOutputData(0, e); } - GL.Texture.releaseTemporary(q); + GL.Texture.releaseTemporary(p); } } }, x.cut_pixel_shader = "precision highp float;\n\r\n\tvarying vec2 v_coord;\n\r\n\tuniform sampler2D u_texture;\n\r\n\tuniform float u_threshold;\n\r\n\tvoid main() {\n\r\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\r\n\t}", x.scale_pixel_shader = "precision highp float;\n\r\n\tvarying vec2 v_coord;\n\r\n\tuniform sampler2D u_texture;\n\r\n\tuniform vec2 u_texel_size;\n\r\n\tuniform float u_delta;\n\r\n\tuniform float u_intensity;\n\r\n\t\n\r\n\tvec4 sampleBox(vec2 uv) {\n\r\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\r\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\r\n\t\treturn s * 0.25;\n\r\n\t}\n\r\n\tvoid main() {\n\r\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\r\n\t}", @@ -8837,9 +8840,9 @@ $jscomp.polyfill("Object.values", function(w) { } }, J.prototype.onGetOutputs = function() { return [["width", "number"], ["height", "number"], ["stream_ready", F.EVENT], ["stream_closed", F.EVENT], ["stream_error", F.EVENT]]; - }, F.registerNodeType("texture/webcam", J), L.title = "Lens FX", L.desc = "distortion and chromatic aberration", L.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, L.prototype.onGetInputs = function() { + }, F.registerNodeType("texture/webcam", J), K.title = "Lens FX", K.desc = "distortion and chromatic aberration", K.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, K.prototype.onGetInputs = function() { return [["enabled", "boolean"]]; - }, L.prototype.onExecute = function() { + }, K.prototype.onExecute = function() { var a = this.getInputData(0); if (a && this.isOutputConnected(0)) { if (this.properties.precision === c.PASS_THROUGH || !1 === this.getInputOrProperty("enabled")) { @@ -8847,8 +8850,8 @@ $jscomp.polyfill("Object.values", function(w) { } else { var b = this._temp_texture; b && b.width == a.width && b.height == a.height && b.type == a.type || (b = this._temp_texture = new GL.Texture(a.width, a.height, {type:a.type, format:gl.RGBA, filter:gl.LINEAR})); - var d = L._shader; - d || (d = L._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, L.pixel_shader)); + var d = K._shader; + d || (d = K._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, K.pixel_shader)); var k = this.getInputData(1); null == k && (k = this.properties.factor); var f = this._uniforms; @@ -8861,8 +8864,8 @@ $jscomp.polyfill("Object.values", function(w) { this.setOutputData(0, b); } } - }, L.pixel_shader = "precision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_factor;\n\r\n\t\tvec2 barrelDistortion(vec2 coord, float amt) {\n\r\n\t\t\tvec2 cc = coord - 0.5;\n\r\n\t\t\tfloat dist = dot(cc, cc);\n\r\n\t\t\treturn coord + cc * dist * amt;\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tfloat sat( float t )\n\r\n\t\t{\n\r\n\t\t\treturn clamp( t, 0.0, 1.0 );\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tfloat linterp( float t ) {\n\r\n\t\t\treturn sat( 1.0 - abs( 2.0*t - 1.0 ) );\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tfloat remap( float t, float a, float b ) {\n\r\n\t\t\treturn sat( (t - a) / (b - a) );\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tvec4 spectrum_offset( float t ) {\n\r\n\t\t\tvec4 ret;\n\r\n\t\t\tfloat lo = step(t,0.5);\n\r\n\t\t\tfloat hi = 1.0-lo;\n\r\n\t\t\tfloat w = linterp( remap( t, 1.0/6.0, 5.0/6.0 ) );\n\r\n\t\t\tret = vec4(lo,1.0,hi, 1.) * vec4(1.0-w, w, 1.0-w, 1.);\n\r\n\t\t\n\r\n\t\t\treturn pow( ret, vec4(1.0/2.2) );\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tconst float max_distort = 2.2;\n\r\n\t\tconst int num_iter = 12;\n\r\n\t\tconst float reci_num_iter_f = 1.0 / float(num_iter);\n\r\n\t\t\n\r\n\t\tvoid main()\n\r\n\t\t{\t\n\r\n\t\t\tvec2 uv=v_coord;\n\r\n\t\t\tvec4 sumcol = vec4(0.0);\n\r\n\t\t\tvec4 sumw = vec4(0.0);\t\n\r\n\t\t\tfor ( int i=0; i c; ++c) { this.valid_notes[c] = -1 != this.notes_pitches.indexOf(c); } @@ -9948,7 +9951,7 @@ $jscomp.polyfill("Object.values", function(w) { var c = this.getInputData(1); null != c && c != this._current_scale && this.processScale(c); }; - t.registerNodeType("midi/quantize", z); + u.registerNodeType("midi/quantize", z); e.title = "MIDI fromFile"; e.desc = "Plays a MIDI file"; e.color = "#243"; @@ -9984,7 +9987,7 @@ $jscomp.polyfill("Object.values", function(w) { }; e.prototype.loadMIDIFile = function(c) { var e = this; - t.fetchFile(c, "arraybuffer", function(c) { + u.fetchFile(c, "arraybuffer", function(c) { e.boxcolor = "#AFA"; e._midi = MidiParser.parse(new Uint8Array(c)); e.properties.autoplay && e.play(); @@ -9997,7 +10000,7 @@ $jscomp.polyfill("Object.values", function(w) { this.properties.url = ""; this.loadMIDIFile(c); }; - t.registerNodeType("midi/fromFile", e); + u.registerNodeType("midi/fromFile", e); C.title = "MIDI Play"; C.desc = "Plays a MIDI note"; C.color = "#243"; @@ -10019,7 +10022,7 @@ $jscomp.polyfill("Object.values", function(w) { c = this.getInputData(2); null != c && (this.properties.duration = c); }; - t.registerNodeType("midi/play", C); + u.registerNodeType("midi/play", C); D.title = "MIDI Keys"; D.desc = "Keyboard to play notes"; D.color = "#243"; @@ -10097,9 +10100,9 @@ $jscomp.polyfill("Object.values", function(w) { return e = this.getKeyIndex(g), this.keys[e] = !1, this._last_key = -1, e = 12 * (this.properties.start_octave - 1) + 29 + e, g = new c, g.setup([c.NOTEOFF, e, 100]), this.trigger("note", g), !0; } }; - t.registerNodeType("midi/keys", D); + u.registerNodeType("midi/keys", D); })(this); -(function(w) { +(function(v) { function c() { this.properties = {src:"", gain:0.5, loop:!0, autoplay:!0, playbackRate:1}; this._loading_audio = !1; @@ -10108,24 +10111,24 @@ $jscomp.polyfill("Object.values", function(w) { this._last_sourcenode = null; this.addOutput("out", "audio"); this.addInput("gain", "number"); - this.audionode = q.getAudioContext().createGain(); + this.audionode = p.getAudioContext().createGain(); this.audionode.graphnode = this; this.audionode.gain.value = this.properties.gain; this.properties.src && this.loadSound(this.properties.src); } - function p() { + function q() { this.properties = {gain:0.5}; this._audionodes = []; this._media_stream = null; this.addOutput("out", "audio"); this.addInput("gain", "number"); - this.audionode = q.getAudioContext().createGain(); + this.audionode = p.getAudioContext().createGain(); this.audionode.graphnode = this; this.audionode.gain.value = this.properties.gain; } function m() { this.properties = {fftSize:2048, minDecibels:-100, maxDecibels:-10, smoothingTimeConstant:0.5}; - this.audionode = q.getAudioContext().createAnalyser(); + this.audionode = p.getAudioContext().createAnalyser(); this.audionode.graphnode = this; this.audionode.fftSize = this.properties.fftSize; this.audionode.minDecibels = this.properties.minDecibels; @@ -10138,36 +10141,36 @@ $jscomp.polyfill("Object.values", function(w) { } function g() { this.properties = {gain:1}; - this.audionode = q.getAudioContext().createGain(); + this.audionode = p.getAudioContext().createGain(); this.addInput("in", "audio"); this.addInput("gain", "number"); this.addOutput("out", "audio"); } - function u() { + function r() { this.properties = {impulse_src:"", normalize:!0}; - this.audionode = q.getAudioContext().createConvolver(); + this.audionode = p.getAudioContext().createConvolver(); this.addInput("in", "audio"); this.addOutput("out", "audio"); } function l() { this.properties = {threshold:-50, knee:40, ratio:12, reduction:-20, attack:0, release:0.25}; - this.audionode = q.getAudioContext().createDynamicsCompressor(); + this.audionode = p.getAudioContext().createDynamicsCompressor(); this.addInput("in", "audio"); this.addOutput("out", "audio"); } function B() { this.properties = {}; - this.audionode = q.getAudioContext().createWaveShaper(); + this.audionode = p.getAudioContext().createWaveShaper(); this.addInput("in", "audio"); this.addInput("shape", "waveshape"); this.addOutput("out", "audio"); } function y() { this.properties = {gain1:0.5, gain2:0.5}; - this.audionode = q.getAudioContext().createGain(); - this.audionode1 = q.getAudioContext().createGain(); + this.audionode = p.getAudioContext().createGain(); + this.audionode1 = p.getAudioContext().createGain(); this.audionode1.gain.value = this.properties.gain1; - this.audionode2 = q.getAudioContext().createGain(); + this.audionode2 = p.getAudioContext().createGain(); this.audionode2.gain.value = this.properties.gain2; this.audionode1.connect(this.audionode); this.audionode2.connect(this.audionode); @@ -10177,9 +10180,9 @@ $jscomp.polyfill("Object.values", function(w) { this.addInput("in2 gain", "number"); this.addOutput("out", "audio"); } - function v() { + function w() { this.properties = {A:0.1, D:0.1, S:0.1, R:0.1}; - this.audionode = q.getAudioContext().createGain(); + this.audionode = p.getAudioContext().createGain(); this.audionode.gain.value = 0; this.addInput("in", "audio"); this.addInput("gate", "bool"); @@ -10188,7 +10191,7 @@ $jscomp.polyfill("Object.values", function(w) { } function E() { this.properties = {delayTime:0.5}; - this.audionode = q.getAudioContext().createDelay(10); + this.audionode = p.getAudioContext().createDelay(10); this.audionode.delayTime.value = this.properties.delayTime; this.addInput("in", "audio"); this.addInput("time", "number"); @@ -10197,14 +10200,14 @@ $jscomp.polyfill("Object.values", function(w) { function z() { this.properties = {frequency:350, detune:0, Q:1}; this.addProperty("type", "lowpass", "enum", {values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")}); - this.audionode = q.getAudioContext().createBiquadFilter(); + this.audionode = p.getAudioContext().createBiquadFilter(); this.addInput("in", "audio"); this.addOutput("out", "audio"); } function e() { this.properties = {frequency:440, detune:0, type:"sine"}; this.addProperty("type", "sine", "enum", {values:["sine", "square", "sawtooth", "triangle", "custom"]}); - this.audionode = q.getAudioContext().createOscillator(); + this.audionode = p.getAudioContext().createOscillator(); this.addOutput("out", "audio"); } function C() { @@ -10219,26 +10222,26 @@ $jscomp.polyfill("Object.values", function(w) { this.addInput("freqs", "array"); this.addOutput("signal", "number"); } - function t() { - if (!t.default_code) { - var c = t.default_function.toString(), a = c.indexOf("{") + 1, b = c.lastIndexOf("}"); - t.default_code = c.substr(a, b - a); + function u() { + if (!u.default_code) { + var c = u.default_function.toString(), a = c.indexOf("{") + 1, b = c.lastIndexOf("}"); + u.default_code = c.substr(a, b - a); } - this.properties = {code:t.default_code}; - c = q.getAudioContext(); + this.properties = {code:u.default_code}; + c = p.getAudioContext(); c.createScriptProcessor ? this.audionode = c.createScriptProcessor(4096, 1, 1) : (console.warn("ScriptProcessorNode deprecated"), this.audionode = c.createGain()); this.processCode(); - t._bypass_function || (t._bypass_function = this.audionode.onaudioprocess); + u._bypass_function || (u._bypass_function = this.audionode.onaudioprocess); this.addInput("in", "audio"); this.addOutput("out", "audio"); } function G() { - this.audionode = q.getAudioContext().destination; + this.audionode = p.getAudioContext().destination; this.addInput("in", "audio"); } - var n = w.LiteGraph, q = {}; - w.LGAudio = q; - q.getAudioContext = function() { + var n = v.LiteGraph, p = {}; + v.LGAudio = p; + p.getAudioContext = function() { if (!this._audio_context) { window.AudioContext = window.AudioContext || window.webkitAudioContext; if (!window.AudioContext) { @@ -10257,21 +10260,21 @@ $jscomp.polyfill("Object.values", function(w) { } return this._audio_context; }; - q.connect = function(c, a) { + p.connect = function(c, a) { try { c.connect(a); } catch (b) { console.warn("LGraphAudio:", b); } }; - q.disconnect = function(c, a) { + p.disconnect = function(c, a) { try { c.disconnect(a); } catch (b) { console.warn("LGraphAudio:", b); } }; - q.changeAllAudiosConnections = function(c, a) { + p.changeAllAudiosConnections = function(c, a) { if (c.inputs) { for (var b = 0; b < c.inputs.length; ++b) { var d = c.graph.links[c.inputs[b].link]; @@ -10279,7 +10282,7 @@ $jscomp.polyfill("Object.values", function(w) { var e = c.graph.getNodeById(d.origin_id); e = e.getAudioNodeInOutputSlot ? e.getAudioNodeInOutputSlot(d.origin_slot) : e.audionode; d = c.getAudioNodeInInputSlot ? c.getAudioNodeInInputSlot(b) : c.audionode; - a ? q.connect(e, d) : q.disconnect(e, d); + a ? p.connect(e, d) : p.disconnect(e, d); } } } @@ -10290,42 +10293,42 @@ $jscomp.polyfill("Object.values", function(w) { e = c.getAudioNodeInOutputSlot ? c.getAudioNodeInOutputSlot(b) : c.audionode; var l = c.graph.getNodeById(d.target_id); d = l.getAudioNodeInInputSlot ? l.getAudioNodeInInputSlot(d.target_slot) : l.audionode; - a ? q.connect(e, d) : q.disconnect(e, d); + a ? p.connect(e, d) : p.disconnect(e, d); } } } } }; - q.onConnectionsChange = function(c, a, b, d) { - c == n.OUTPUT && (c = null, d && (c = this.graph.getNodeById(d.target_id)), c && (a = this.getAudioNodeInOutputSlot ? this.getAudioNodeInOutputSlot(a) : this.audionode, d = c.getAudioNodeInInputSlot ? c.getAudioNodeInInputSlot(d.target_slot) : c.audionode, b ? q.connect(a, d) : q.disconnect(a, d))); + p.onConnectionsChange = function(c, a, b, d) { + c == n.OUTPUT && (c = null, d && (c = this.graph.getNodeById(d.target_id)), c && (a = this.getAudioNodeInOutputSlot ? this.getAudioNodeInOutputSlot(a) : this.audionode, d = c.getAudioNodeInInputSlot ? c.getAudioNodeInInputSlot(d.target_slot) : c.audionode, b ? p.connect(a, d) : p.disconnect(a, d))); }; - q.createAudioNodeWrapper = function(c) { + p.createAudioNodeWrapper = function(c) { var a = c.prototype.onPropertyChanged; c.prototype.onPropertyChanged = function(b, d) { a && a.call(this, b, d); this.audionode && void 0 !== this.audionode[b] && (void 0 !== this.audionode[b].value ? this.audionode[b].value = d : this.audionode[b] = d); }; - c.prototype.onConnectionsChange = q.onConnectionsChange; + c.prototype.onConnectionsChange = p.onConnectionsChange; }; - q.cached_audios = {}; - q.loadSound = function(c, a, b) { + p.cached_audios = {}; + p.loadSound = function(c, a, b) { function d(a) { console.log("Audio loading sample error:", a); b && b(a); } - if (q.cached_audios[c] && -1 == c.indexOf("blob:")) { - a && a(q.cached_audios[c]); + if (p.cached_audios[c] && -1 == c.indexOf("blob:")) { + a && a(p.cached_audios[c]); } else { - q.onProcessAudioURL && (c = q.onProcessAudioURL(c)); + p.onProcessAudioURL && (c = p.onProcessAudioURL(c)); var e = new XMLHttpRequest; e.open("GET", c, !0); e.responseType = "arraybuffer"; - var k = q.getAudioContext(); + var k = p.getAudioContext(); e.onload = function() { console.log("AudioSource loaded"); k.decodeAudioData(e.response, function(b) { console.log("AudioSource decoded"); - q.cached_audios[c] = b; + p.cached_audios[c] = b; a && a(b); }, d); }; @@ -10364,10 +10367,10 @@ $jscomp.polyfill("Object.values", function(w) { this._audionodes.length = 0; }; c.prototype.pauseAllSounds = function() { - q.getAudioContext().suspend(); + p.getAudioContext().suspend(); }; c.prototype.unpauseAllSounds = function() { - q.getAudioContext().resume(); + p.getAudioContext().resume(); }; c.prototype.onExecute = function() { if (this.inputs) { @@ -10418,7 +10421,7 @@ $jscomp.polyfill("Object.values", function(w) { } }; c.prototype.playBuffer = function(c) { - var a = this, b = q.getAudioContext().createBufferSource(); + var a = this, b = p.getAudioContext().createBufferSource(); this._last_sourcenode = b; b.graphnode = this; b.buffer = c; @@ -10441,7 +10444,7 @@ $jscomp.polyfill("Object.values", function(w) { this._request && (this._request.abort(), this._request = null); this._audiobuffer = null; this._loading_audio = !1; - c && (this._request = q.loadSound(c, function(b) { + c && (this._request = p.loadSound(c, function(b) { this.boxcolor = n.NODE_DEFAULT_BOXCOLOR; a._audiobuffer = b; a._loading_audio = !1; @@ -10450,7 +10453,7 @@ $jscomp.polyfill("Object.values", function(w) { } }), this._loading_audio = !0, this.boxcolor = "#AA4"); }; - c.prototype.onConnectionsChange = q.onConnectionsChange; + c.prototype.onConnectionsChange = p.onConnectionsChange; c.prototype.onGetInputs = function() { return [["playbackRate", "number"], ["src", "string"], ["Play", n.ACTION], ["Stop", n.ACTION]]; }; @@ -10467,24 +10470,24 @@ $jscomp.polyfill("Object.values", function(w) { c.title = "Source"; c.desc = "Plays audio"; n.registerNodeType("audio/source", c); - p.prototype.onAdded = function(c) { + q.prototype.onAdded = function(c) { if (c.status === LGraph.STATUS_RUNNING) { this.onStart(); } }; - p.prototype.onStart = function() { + q.prototype.onStart = function() { null != this._media_stream || this._waiting_confirmation || this.openStream(); }; - p.prototype.onStop = function() { + q.prototype.onStop = function() { this.audionode.gain.value = 0; }; - p.prototype.onPause = function() { + q.prototype.onPause = function() { this.audionode.gain.value = 0; }; - p.prototype.onUnpause = function() { + q.prototype.onUnpause = function() { this.audionode.gain.value = this.properties.gain; }; - p.prototype.onRemoved = function() { + q.prototype.onRemoved = function() { this.audionode.gain.value = 0; this.audiosource_node && (this.audiosource_node.disconnect(this.audionode), this.audiosource_node = null); if (this._media_stream) { @@ -10492,7 +10495,7 @@ $jscomp.polyfill("Object.values", function(w) { c.length && c[0].stop(); } }; - p.prototype.openStream = function() { + q.prototype.openStream = function() { if (navigator.mediaDevices) { this._waiting_confirmation = !0; navigator.mediaDevices.getUserMedia({audio:!0, video:!1}).then(this.streamReady.bind(this)).catch(function(a) { @@ -10505,15 +10508,15 @@ $jscomp.polyfill("Object.values", function(w) { console.log("getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags"); } }; - p.prototype.streamReady = function(c) { + q.prototype.streamReady = function(c) { this._media_stream = c; this.audiosource_node && this.audiosource_node.disconnect(this.audionode); - this.audiosource_node = q.getAudioContext().createMediaStreamSource(c); + this.audiosource_node = p.getAudioContext().createMediaStreamSource(c); this.audiosource_node.graphnode = this; this.audiosource_node.connect(this.audionode); this.boxcolor = "white"; }; - p.prototype.onExecute = function() { + q.prototype.onExecute = function() { null != this._media_stream || this._waiting_confirmation || this.openStream(); if (this.inputs) { for (var c = 0; c < this.inputs.length; ++c) { @@ -10525,19 +10528,19 @@ $jscomp.polyfill("Object.values", function(w) { } } }; - p.prototype.onAction = function(c) { + q.prototype.onAction = function(c) { "Play" == c ? this.audionode.gain.value = this.properties.gain : "Stop" == c && (this.audionode.gain.value = 0); }; - p.prototype.onPropertyChanged = function(c, a) { + q.prototype.onPropertyChanged = function(c, a) { "gain" == c && (this.audionode.gain.value = a); }; - p.prototype.onConnectionsChange = q.onConnectionsChange; - p.prototype.onGetInputs = function() { + q.prototype.onConnectionsChange = p.onConnectionsChange; + q.prototype.onGetInputs = function() { return [["playbackRate", "number"], ["Play", n.ACTION], ["Stop", n.ACTION]]; }; - p.title = "MediaSource"; - p.desc = "Plays microphone"; - n.registerNodeType("audio/media_source", p); + q.title = "MediaSource"; + q.desc = "Plays microphone"; + n.registerNodeType("audio/media_source", q); m.prototype.onPropertyChanged = function(c, a) { this.audionode[c] = a; }; @@ -10574,39 +10577,39 @@ $jscomp.polyfill("Object.values", function(w) { } } }; - q.createAudioNodeWrapper(g); + p.createAudioNodeWrapper(g); g.title = "Gain"; g.desc = "Audio gain"; n.registerNodeType("audio/gain", g); - q.createAudioNodeWrapper(u); - u.prototype.onRemove = function() { + p.createAudioNodeWrapper(r); + r.prototype.onRemove = function() { this._dropped_url && URL.revokeObjectURL(this._dropped_url); }; - u.prototype.onPropertyChanged = function(c, a) { + r.prototype.onPropertyChanged = function(c, a) { "impulse_src" == c ? this.loadImpulse(a) : "normalize" == c && (this.audionode.normalize = a); }; - u.prototype.onDropFile = function(c) { + r.prototype.onDropFile = function(c) { this._dropped_url && URL.revokeObjectURL(this._dropped_url); this._dropped_url = URL.createObjectURL(c); this.properties.impulse_src = this._dropped_url; this.loadImpulse(this._dropped_url); }; - u.prototype.loadImpulse = function(c) { + r.prototype.loadImpulse = function(c) { var a = this; this._request && (this._request.abort(), this._request = null); this._impulse_buffer = null; this._loading_impulse = !1; - c && (this._request = q.loadSound(c, function(b) { + c && (this._request = p.loadSound(c, function(b) { a._impulse_buffer = b; a.audionode.buffer = b; console.log("Impulse signal set"); a._loading_impulse = !1; }), this._loading_impulse = !0); }; - u.title = "Convolver"; - u.desc = "Convolves the signal (used for reverb)"; - n.registerNodeType("audio/convolver", u); - q.createAudioNodeWrapper(l); + r.title = "Convolver"; + r.desc = "Convolves the signal (used for reverb)"; + n.registerNodeType("audio/convolver", r); + p.createAudioNodeWrapper(l); l.prototype.onExecute = function() { if (this.inputs && this.inputs.length) { for (var c = 1; c < this.inputs.length; ++c) { @@ -10633,7 +10636,7 @@ $jscomp.polyfill("Object.values", function(w) { B.prototype.setWaveShape = function(c) { this.audionode.curve = c; }; - q.createAudioNodeWrapper(B); + p.createAudioNodeWrapper(B); y.prototype.getAudioNodeInInputSlot = function(c) { if (0 == c) { return this.audionode1; @@ -10653,23 +10656,23 @@ $jscomp.polyfill("Object.values", function(w) { } } }; - q.createAudioNodeWrapper(y); + p.createAudioNodeWrapper(y); y.title = "Mixer"; y.desc = "Audio mixer"; n.registerNodeType("audio/mixer", y); - v.prototype.onExecute = function() { - var c = q.getAudioContext().currentTime, a = this.audionode.gain, b = this.getInputData(1), d = this.getInputOrProperty("A"), e = this.getInputOrProperty("D"), f = this.getInputOrProperty("S"), g = this.getInputOrProperty("R"); + w.prototype.onExecute = function() { + var c = p.getAudioContext().currentTime, a = this.audionode.gain, b = this.getInputData(1), d = this.getInputOrProperty("A"), e = this.getInputOrProperty("D"), f = this.getInputOrProperty("S"), g = this.getInputOrProperty("R"); !this.gate && b ? (a.cancelScheduledValues(0), a.setValueAtTime(0, c), a.linearRampToValueAtTime(1, c + d), a.linearRampToValueAtTime(f, c + d + e)) : this.gate && !b && (a.cancelScheduledValues(0), a.setValueAtTime(a.value, c), a.linearRampToValueAtTime(0, c + g)); this.gate = b; }; - v.prototype.onGetInputs = function() { + w.prototype.onGetInputs = function() { return [["A", "number"], ["D", "number"], ["S", "number"], ["R", "number"]]; }; - q.createAudioNodeWrapper(v); - v.title = "ADSR"; - v.desc = "Audio envelope"; - n.registerNodeType("audio/adsr", v); - q.createAudioNodeWrapper(E); + p.createAudioNodeWrapper(w); + w.title = "ADSR"; + w.desc = "Audio envelope"; + n.registerNodeType("audio/adsr", w); + p.createAudioNodeWrapper(E); E.prototype.onExecute = function() { var c = this.getInputData(1); void 0 !== c && (this.audionode.delayTime.value = c); @@ -10691,7 +10694,7 @@ $jscomp.polyfill("Object.values", function(w) { z.prototype.onGetInputs = function() { return [["frequency", "number"], ["detune", "number"], ["Q", "number"]]; }; - q.createAudioNodeWrapper(z); + p.createAudioNodeWrapper(z); z.title = "BiquadFilter"; z.desc = "Audio filter"; n.registerNodeType("audio/biquadfilter", z); @@ -10727,7 +10730,7 @@ $jscomp.polyfill("Object.values", function(w) { e.prototype.onGetInputs = function() { return [["frequency", "number"], ["detune", "number"], ["type", "string"]]; }; - q.createAudioNodeWrapper(e); + p.createAudioNodeWrapper(e); e.title = "Oscillator"; e.desc = "Oscillator"; n.registerNodeType("audio/oscillator", e); @@ -10756,7 +10759,7 @@ $jscomp.polyfill("Object.values", function(w) { } } c.stroke(); - 0 <= this.properties.mark && (a = q.getAudioContext().sampleRate / a.length, e = this.properties.mark / a * 2 / b, e >= this.size[0] && (e = this.size[0] - 1), c.strokeStyle = "red", c.beginPath(), c.moveTo(e, d), c.lineTo(e, 0), c.stroke()); + 0 <= this.properties.mark && (a = p.getAudioContext().sampleRate / a.length, e = this.properties.mark / a * 2 / b, e >= this.size[0] && (e = this.size[0] - 1), c.strokeStyle = "red", c.beginPath(), c.moveTo(e, d), c.lineTo(e, 0), c.stroke()); } }; C.title = "Visualization"; @@ -10766,7 +10769,7 @@ $jscomp.polyfill("Object.values", function(w) { if (this._freqs = this.getInputData(0)) { var c = this.properties.band, a = this.getInputData(1); void 0 !== a && (c = a); - a = q.getAudioContext().sampleRate / this._freqs.length; + a = p.getAudioContext().sampleRate / this._freqs.length; a = c / a * 2; a >= this._freqs.length ? a = this._freqs[this._freqs.length - 1] : (c = a | 0, a -= c, a = this._freqs[c] * (1 - a) + this._freqs[c + 1] * a); this.setOutputData(0, a / 255 * this.properties.amplitude); @@ -10778,38 +10781,38 @@ $jscomp.polyfill("Object.values", function(w) { D.title = "Signal"; D.desc = "extract the signal of some frequency"; n.registerNodeType("audio/signal", D); - t.prototype.onAdded = function(c) { + u.prototype.onAdded = function(c) { c.status == LGraph.STATUS_RUNNING && (this.audionode.onaudioprocess = this._callback); }; - t["@code"] = {widget:"code", type:"code"}; - t.prototype.onStart = function() { + u["@code"] = {widget:"code", type:"code"}; + u.prototype.onStart = function() { this.audionode.onaudioprocess = this._callback; }; - t.prototype.onStop = function() { - this.audionode.onaudioprocess = t._bypass_function; + u.prototype.onStop = function() { + this.audionode.onaudioprocess = u._bypass_function; }; - t.prototype.onPause = function() { - this.audionode.onaudioprocess = t._bypass_function; + u.prototype.onPause = function() { + this.audionode.onaudioprocess = u._bypass_function; }; - t.prototype.onUnpause = function() { + u.prototype.onUnpause = function() { this.audionode.onaudioprocess = this._callback; }; - t.prototype.onExecute = function() { + u.prototype.onExecute = function() { }; - t.prototype.onRemoved = function() { - this.audionode.onaudioprocess = t._bypass_function; + u.prototype.onRemoved = function() { + this.audionode.onaudioprocess = u._bypass_function; }; - t.prototype.processCode = function() { + u.prototype.processCode = function() { try { this._script = new (new Function("properties", this.properties.code))(this.properties), this._old_code = this.properties.code, this._callback = this._script.onaudioprocess; } catch (k) { - console.error("Error in onaudioprocess code", k), this._callback = t._bypass_function, this.audionode.onaudioprocess = this._callback; + console.error("Error in onaudioprocess code", k), this._callback = u._bypass_function, this.audionode.onaudioprocess = this._callback; } }; - t.prototype.onPropertyChanged = function(c, a) { + u.prototype.onPropertyChanged = function(c, a) { "code" == c && (this.properties.code = a, this.processCode(), this.graph && this.graph.status == LGraph.STATUS_RUNNING && (this.audionode.onaudioprocess = this._callback)); }; - t.default_function = function() { + u.default_function = function() { this.onaudioprocess = function(c) { var a = c.inputBuffer; c = c.outputBuffer; @@ -10820,15 +10823,15 @@ $jscomp.polyfill("Object.values", function(w) { } }; }; - q.createAudioNodeWrapper(t); - t.title = "Script"; - t.desc = "apply script to signal"; - n.registerNodeType("audio/script", t); + p.createAudioNodeWrapper(u); + u.title = "Script"; + u.desc = "apply script to signal"; + n.registerNodeType("audio/script", u); G.title = "Destination"; G.desc = "Audio output"; n.registerNodeType("audio/destination", G); })(this); -(function(w) { +(function(v) { function c() { this.size = [60, 20]; this.addInput("send", m.ACTION); @@ -10840,7 +10843,7 @@ $jscomp.polyfill("Object.values", function(w) { this._last_sent_data = []; this._last_received_data = []; } - function p() { + function q() { this.room_widget = this.addWidget("text", "Room", "lgraph", this.setRoom.bind(this)); this.addWidget("button", "Reconnect", null, this.connectSocket.bind(this)); this.addInput("send", m.ACTION); @@ -10854,7 +10857,7 @@ $jscomp.polyfill("Object.values", function(w) { this._last_received_data = []; "undefined" == typeof SillyClient && console.warn("remember to add SillyClient.js to your project: https://tamats.com/projects/sillyserver/src/sillyclient.js"); } - var m = w.LiteGraph; + var m = v.LiteGraph; c.title = "WebSocket"; c.desc = "Send data through a websocket"; c.prototype.onPropertyChanged = function(c, m) { @@ -10864,14 +10867,14 @@ $jscomp.polyfill("Object.values", function(w) { !this._ws && this.properties.url && this.connectSocket(); if (this._ws && this._ws.readyState == WebSocket.OPEN) { for (var c = this.properties.room, m = this.properties.only_send_changes, l = 1; l < this.inputs.length; ++l) { - var p = this.getInputData(l); - if (null != p) { + var q = this.getInputData(l); + if (null != q) { try { - var w = JSON.stringify({type:0, room:c, channel:l, data:p}); - } catch (v) { + var v = JSON.stringify({type:0, room:c, channel:l, data:q}); + } catch (w) { continue; } - m && this._last_sent_data[l] == w || (this._last_sent_data[l] = w, this._ws.send(w)); + m && this._last_sent_data[l] == v || (this._last_sent_data[l] = v, this._ws.send(v)); } } for (l = 1; l < this.outputs.length; ++l) { @@ -10881,9 +10884,9 @@ $jscomp.polyfill("Object.values", function(w) { } }; c.prototype.connectSocket = function() { - var c = this, p = this.properties.url; - "ws" != p.substr(0, 2) && (p = "ws://" + p); - this._ws = new WebSocket(p); + var c = this, r = this.properties.url; + "ws" != r.substr(0, 2) && (r = "ws://" + r); + this._ws = new WebSocket(r); this._ws.onopen = function() { console.log("ready"); c.boxcolor = "#6C6"; @@ -10929,18 +10932,18 @@ $jscomp.polyfill("Object.values", function(w) { return [["out", 0]]; }; m.registerNodeType("network/websocket", c); - p.title = "SillyClient"; - p.desc = "Connects to SillyServer to broadcast messages"; - p.prototype.onPropertyChanged = function(c, m) { + q.title = "SillyClient"; + q.desc = "Connects to SillyServer to broadcast messages"; + q.prototype.onPropertyChanged = function(c, m) { "room" == c && (this.room_widget.value = m); this.connectSocket(); }; - p.prototype.setRoom = function(c) { + q.prototype.setRoom = function(c) { this.properties.room = c; this.room_widget.value = c; this.connectSocket(); }; - p.prototype.onDrawForeground = function() { + q.prototype.onDrawForeground = function() { for (var c = 1; c < this.inputs.length; ++c) { var m = this.inputs[c]; m.label = "in_" + c; @@ -10949,32 +10952,32 @@ $jscomp.polyfill("Object.values", function(w) { m = this.outputs[c], m.label = "out_" + c; } }; - p.prototype.onExecute = function() { + q.prototype.onExecute = function() { if (this._server && this._server.is_connected) { for (var c = this.properties.only_send_changes, m = 1; m < this.inputs.length; ++m) { - var l = this.getInputData(m), p = this._last_sent_data[m]; + var l = this.getInputData(m), q = this._last_sent_data[m]; if (null != l) { if (c) { - var w = !0; - if (l && l.length && p && p.length == l.length && l.constructor !== String) { - for (var v = 0; v < l.length; ++v) { - if (p[v] != l[v]) { - w = !1; + var v = !0; + if (l && l.length && q && q.length == l.length && l.constructor !== String) { + for (var w = 0; w < l.length; ++w) { + if (q[w] != l[w]) { + v = !1; break; } } } else { - this._last_sent_data[m] != l && (w = !1); + this._last_sent_data[m] != l && (v = !1); } - if (w) { + if (v) { continue; } } this._server.sendMessage({type:0, channel:m, data:l}); if (l.length && l.constructor !== String) { if (this._last_sent_data[m]) { - for (this._last_sent_data[m].length = l.length, v = 0; v < l.length; ++v) { - this._last_sent_data[m][v] = l[v]; + for (this._last_sent_data[m].length = l.length, w = 0; w < l.length; ++w) { + this._last_sent_data[m][w] = l[w]; } } else { this._last_sent_data[m] = l.constructor === Array ? l.concat() : new l.constructor(l); @@ -10990,7 +10993,7 @@ $jscomp.polyfill("Object.values", function(w) { "#AFA" == this.boxcolor && (this.boxcolor = "#6C6"); } }; - p.prototype.connectSocket = function() { + q.prototype.connectSocket = function() { var c = this; if ("undefined" == typeof SillyClient) { this._error || console.error("SillyClient node cannot be used, you must include SillyServer.js"), this._error = !0; @@ -11029,8 +11032,8 @@ $jscomp.polyfill("Object.values", function(w) { }, this.properties.url && this.properties.room) { try { this._server.connect(this.properties.url, this.properties.room); - } catch (u) { - console.error("SillyServer error: " + u); + } catch (r) { + console.error("SillyServer error: " + r); this._server = null; return; } @@ -11038,19 +11041,19 @@ $jscomp.polyfill("Object.values", function(w) { } } }; - p.prototype.send = function(c) { + q.prototype.send = function(c) { this._server && this._server.is_connected && this._server.sendMessage({type:1, data:c}); }; - p.prototype.onAction = function(c, m) { + q.prototype.onAction = function(c, m) { this._server && this._server.is_connected && this._server.sendMessage({type:1, action:c, data:m}); }; - p.prototype.onGetInputs = function() { + q.prototype.onGetInputs = function() { return [["in", 0]]; }; - p.prototype.onGetOutputs = function() { + q.prototype.onGetOutputs = function() { return [["out", 0]]; }; - m.registerNodeType("network/sillyclient", p); + m.registerNodeType("network/sillyclient", q); })(this); >>>>>>> custom widget custom size support diff --git a/src/litegraph.d.ts b/src/litegraph.d.ts index da611ae96..9e534315d 100644 --- a/src/litegraph.d.ts +++ b/src/litegraph.d.ts @@ -62,17 +62,16 @@ export interface IWidget { width: number, posY: number, height: number - ): number | undefined; + ): void; /** * Called by `LGraphCanvas.processNodeWidgets` * https://github.com/jagenjo/litegraph.js/issues/76 */ mouse?( - ctx: undefined, event: MouseEvent, pos: Vector2, node: LGraphNode - ): void; + ): boolean; /** Called by `LGraphNode.computeSize` */ computeSize?(width: number): [number, number]; } diff --git a/src/litegraph.js b/src/litegraph.js index 54b9a5ee2..2960c3bbf 100755 --- a/src/litegraph.js +++ b/src/litegraph.js @@ -8257,7 +8257,6 @@ LGraphNode.prototype.executeAction = function(action) var margin = 15; for (var i = 0; i < widgets.length; ++i) { - var h = H; var w = widgets[i]; var y = posY; if (w.y) { @@ -8428,11 +8427,11 @@ LGraphNode.prototype.executeAction = function(action) break; default: if (w.draw) { - h = w.draw(ctx, node, width, y, H) || H; + w.draw(ctx, node, width, y, H); } break; } - posY += h + 4; + posY += (w.computeSize ? w.computeSize(width)[1] : H) + 4; ctx.globalAlpha = this.editor_alpha; } @@ -8464,7 +8463,8 @@ LGraphNode.prototype.executeAction = function(action) var w = node.widgets[i]; if(!w || w.disabled) continue; - if ( w == active_widget || (x > 6 && x < width - 12 && y > w.last_y && y < w.last_y + LiteGraph.NODE_WIDGET_HEIGHT) ) { + var widget_height = w.computeSize ? w.computeSize(width)[1] : LiteGraph.NODE_WIDGET_HEIGHT; + if ( w == active_widget || (x > 6 && x < width - 12 && y > w.last_y && y < w.last_y + widget_height) ) { //inside widget switch (w.type) { case "button": @@ -8598,7 +8598,7 @@ LGraphNode.prototype.executeAction = function(action) break; default: if (w.mouse) { - w.mouse(ctx, event, [x, y], node); + this.dirty_canvas = w.mouse(event, [x, y], node); } break; } //end switch From 93ba405a23a412ea361becd4503f00caf44a890d Mon Sep 17 00:00:00 2001 From: altarfinch Date: Fri, 15 May 2020 10:57:17 +0200 Subject: [PATCH 37/63] fixed enableWebGL missing declarations --- build/litegraph.js | 18478 +------------------------------------- build/litegraph.min.js | 3013 +++---- src/litegraph.d.ts | 2 +- src/litegraph.js | 2 +- src/nodes/glfx.js | 1 + src/nodes/gltextures.js | 1 + 6 files changed, 1191 insertions(+), 20306 deletions(-) diff --git a/build/litegraph.js b/build/litegraph.js index 8659453b5..87134fdde 100644 --- a/build/litegraph.js +++ b/build/litegraph.js @@ -1,12254 +1,3 @@ -<<<<<<< HEAD -//packer version - -(function(global) { - // ************************************************************* - // LiteGraph CLASS ******* - // ************************************************************* - - /** - * The Global Scope. It contains all the registered node classes. - * - * @class LiteGraph - * @constructor - */ - - var LiteGraph = (global.LiteGraph = { - VERSION: 0.4, - - CANVAS_GRID_SIZE: 10, - - NODE_TITLE_HEIGHT: 30, - NODE_TITLE_TEXT_Y: 20, - NODE_SLOT_HEIGHT: 20, - NODE_WIDGET_HEIGHT: 20, - NODE_WIDTH: 140, - NODE_MIN_WIDTH: 50, - NODE_COLLAPSED_RADIUS: 10, - NODE_COLLAPSED_WIDTH: 80, - NODE_TITLE_COLOR: "#999", - NODE_TEXT_SIZE: 14, - NODE_TEXT_COLOR: "#AAA", - NODE_SUBTEXT_SIZE: 12, - NODE_DEFAULT_COLOR: "#333", - NODE_DEFAULT_BGCOLOR: "#353535", - NODE_DEFAULT_BOXCOLOR: "#666", - NODE_DEFAULT_SHAPE: "box", - DEFAULT_SHADOW_COLOR: "rgba(0,0,0,0.5)", - DEFAULT_GROUP_FONT: 24, - - WIDGET_BGCOLOR: "#222", - WIDGET_OUTLINE_COLOR: "#666", - WIDGET_TEXT_COLOR: "#DDD", - WIDGET_SECONDARY_TEXT_COLOR: "#999", - - LINK_COLOR: "#9A9", - EVENT_LINK_COLOR: "#A86", - CONNECTING_LINK_COLOR: "#AFA", - - MAX_NUMBER_OF_NODES: 1000, //avoid infinite loops - DEFAULT_POSITION: [100, 100], //default node position - VALID_SHAPES: ["default", "box", "round", "card"], //,"circle" - - //shapes are used for nodes but also for slots - BOX_SHAPE: 1, - ROUND_SHAPE: 2, - CIRCLE_SHAPE: 3, - CARD_SHAPE: 4, - ARROW_SHAPE: 5, - - //enums - INPUT: 1, - OUTPUT: 2, - - EVENT: -1, //for outputs - ACTION: -1, //for inputs - - ALWAYS: 0, - ON_EVENT: 1, - NEVER: 2, - ON_TRIGGER: 3, - - UP: 1, - DOWN: 2, - LEFT: 3, - RIGHT: 4, - CENTER: 5, - - STRAIGHT_LINK: 0, - LINEAR_LINK: 1, - SPLINE_LINK: 2, - - NORMAL_TITLE: 0, - NO_TITLE: 1, - TRANSPARENT_TITLE: 2, - AUTOHIDE_TITLE: 3, - - proxy: null, //used to redirect calls - node_images_path: "", - - debug: false, - catch_exceptions: true, - throw_errors: true, - allow_scripts: false, //if set to true some nodes like Formula would be allowed to evaluate code that comes from unsafe sources (like node configuration), which could lead to exploits - registered_node_types: {}, //nodetypes by string - node_types_by_file_extension: {}, //used for dropping files in the canvas - Nodes: {}, //node types by classname - - searchbox_extras: {}, //used to add extra features to the search box - - /** - * Register a node class so it can be listed when the user wants to create a new one - * @method registerNodeType - * @param {String} type name of the node and path - * @param {Class} base_class class containing the structure of a node - */ - - registerNodeType: function(type, base_class) { - if (!base_class.prototype) { - throw "Cannot register a simple object, it must be a class with a prototype"; - } - base_class.type = type; - - if (LiteGraph.debug) { - console.log("Node registered: " + type); - } - - var categories = type.split("/"); - var classname = base_class.name; - - var pos = type.lastIndexOf("/"); - base_class.category = type.substr(0, pos); - - if (!base_class.title) { - base_class.title = classname; - } - //info.name = name.substr(pos+1,name.length - pos); - - //extend class - if (base_class.prototype) { - //is a class - for (var i in LGraphNode.prototype) { - if (!base_class.prototype[i]) { - base_class.prototype[i] = LGraphNode.prototype[i]; - } - } - } - - var prev = this.registered_node_types[type]; - if(prev) - console.log("replacing node type: " + type); - else - { - if( !Object.hasOwnProperty( base_class.prototype, "shape") ) - Object.defineProperty(base_class.prototype, "shape", { - set: function(v) { - switch (v) { - case "default": - delete this._shape; - break; - case "box": - this._shape = LiteGraph.BOX_SHAPE; - break; - case "round": - this._shape = LiteGraph.ROUND_SHAPE; - break; - case "circle": - this._shape = LiteGraph.CIRCLE_SHAPE; - break; - case "card": - this._shape = LiteGraph.CARD_SHAPE; - break; - default: - this._shape = v; - } - }, - get: function(v) { - return this._shape; - }, - enumerable: true, - configurable: true - }); - - //warnings - if (base_class.prototype.onPropertyChange) { - console.warn( - "LiteGraph node class " + - type + - " has onPropertyChange method, it must be called onPropertyChanged with d at the end" - ); - } - - //used to know which nodes create when dragging files to the canvas - if (base_class.supported_extensions) { - for (var i in base_class.supported_extensions) { - var ext = base_class.supported_extensions[i]; - if(ext && ext.constructor === String) - this.node_types_by_file_extension[ ext.toLowerCase() ] = base_class; - } - } - } - - this.registered_node_types[type] = base_class; - if (base_class.constructor.name) { - this.Nodes[classname] = base_class; - } - if (LiteGraph.onNodeTypeRegistered) { - LiteGraph.onNodeTypeRegistered(type, base_class); - } - if (prev && LiteGraph.onNodeTypeReplaced) { - LiteGraph.onNodeTypeReplaced(type, base_class, prev); - } - }, - - /** - * removes a node type from the system - * @method unregisterNodeType - * @param {String|Object} type name of the node or the node constructor itself - */ - unregisterNodeType: function(type) { - var base_class = type.constructor === String ? this.registered_node_types[type] : type; - if(!base_class) - throw("node type not found: " + type ); - delete this.registered_node_types[base_class.type]; - if(base_class.constructor.name) - delete this.Nodes[base_class.constructor.name]; - }, - - /** - * Create a new nodetype by passing a function, it wraps it with a proper class and generates inputs according to the parameters of the function. - * Useful to wrap simple methods that do not require properties, and that only process some input to generate an output. - * @method wrapFunctionAsNode - * @param {String} name node name with namespace (p.e.: 'math/sum') - * @param {Function} func - * @param {Array} param_types [optional] an array containing the type of every parameter, otherwise parameters will accept any type - * @param {String} return_type [optional] string with the return type, otherwise it will be generic - * @param {Object} properties [optional] properties to be configurable - */ - wrapFunctionAsNode: function( - name, - func, - param_types, - return_type, - properties - ) { - var params = Array(func.length); - var code = ""; - var names = LiteGraph.getParameterNames(func); - for (var i = 0; i < names.length; ++i) { - code += - "this.addInput('" + - names[i] + - "'," + - (param_types && param_types[i] - ? "'" + param_types[i] + "'" - : "0") + - ");\n"; - } - code += - "this.addOutput('out'," + - (return_type ? "'" + return_type + "'" : 0) + - ");\n"; - if (properties) { - code += - "this.properties = " + JSON.stringify(properties) + ";\n"; - } - var classobj = Function(code); - classobj.title = name.split("/").pop(); - classobj.desc = "Generated from " + func.name; - classobj.prototype.onExecute = function onExecute() { - for (var i = 0; i < params.length; ++i) { - params[i] = this.getInputData(i); - } - var r = func.apply(this, params); - this.setOutputData(0, r); - }; - this.registerNodeType(name, classobj); - }, - - /** - * Adds this method to all nodetypes, existing and to be created - * (You can add it to LGraphNode.prototype but then existing node types wont have it) - * @method addNodeMethod - * @param {Function} func - */ - addNodeMethod: function(name, func) { - LGraphNode.prototype[name] = func; - for (var i in this.registered_node_types) { - var type = this.registered_node_types[i]; - if (type.prototype[name]) { - type.prototype["_" + name] = type.prototype[name]; - } //keep old in case of replacing - type.prototype[name] = func; - } - }, - - /** - * Create a node of a given type with a name. The node is not attached to any graph yet. - * @method createNode - * @param {String} type full name of the node class. p.e. "math/sin" - * @param {String} name a name to distinguish from other nodes - * @param {Object} options to set options - */ - - createNode: function(type, title, options) { - var base_class = this.registered_node_types[type]; - if (!base_class) { - if (LiteGraph.debug) { - console.log( - 'GraphNode type "' + type + '" not registered.' - ); - } - return null; - } - - var prototype = base_class.prototype || base_class; - - title = title || base_class.title || type; - - var node = null; - - if (LiteGraph.catch_exceptions) { - try { - node = new base_class(title); - } catch (err) { - console.error(err); - return null; - } - } else { - node = new base_class(title); - } - - node.type = type; - - if (!node.title && title) { - node.title = title; - } - if (!node.properties) { - node.properties = {}; - } - if (!node.properties_info) { - node.properties_info = []; - } - if (!node.flags) { - node.flags = {}; - } - if (!node.size) { - node.size = node.computeSize(); - } - if (!node.pos) { - node.pos = LiteGraph.DEFAULT_POSITION.concat(); - } - if (!node.mode) { - node.mode = LiteGraph.ALWAYS; - } - - //extra options - if (options) { - for (var i in options) { - node[i] = options[i]; - } - } - - return node; - }, - - /** - * Returns a registered node type with a given name - * @method getNodeType - * @param {String} type full name of the node class. p.e. "math/sin" - * @return {Class} the node class - */ - getNodeType: function(type) { - return this.registered_node_types[type]; - }, - - /** - * Returns a list of node types matching one category - * @method getNodeType - * @param {String} category category name - * @return {Array} array with all the node classes - */ - - getNodeTypesInCategory: function(category, filter) { - var r = []; - for (var i in this.registered_node_types) { - var type = this.registered_node_types[i]; - if (filter && type.filter && type.filter != filter) { - continue; - } - - if (category == "") { - if (type.category == null) { - r.push(type); - } - } else if (type.category == category) { - r.push(type); - } - } - - return r; - }, - - /** - * Returns a list with all the node type categories - * @method getNodeTypesCategories - * @return {Array} array with all the names of the categories - */ - getNodeTypesCategories: function( filter ) { - var categories = { "": 1 }; - for (var i in this.registered_node_types) { - var type = this.registered_node_types[i]; - if ( type.category && !type.skip_list ) - { - if(filter && type.filter != filter) - continue; - categories[type.category] = 1; - } - } - var result = []; - for (var i in categories) { - result.push(i); - } - return result; - }, - - //debug purposes: reloads all the js scripts that matches a wildcard - reloadNodes: function(folder_wildcard) { - var tmp = document.getElementsByTagName("script"); - //weird, this array changes by its own, so we use a copy - var script_files = []; - for (var i in tmp) { - script_files.push(tmp[i]); - } - - var docHeadObj = document.getElementsByTagName("head")[0]; - folder_wildcard = document.location.href + folder_wildcard; - - for (var i in script_files) { - var src = script_files[i].src; - if ( - !src || - src.substr(0, folder_wildcard.length) != folder_wildcard - ) { - continue; - } - - try { - if (LiteGraph.debug) { - console.log("Reloading: " + src); - } - var dynamicScript = document.createElement("script"); - dynamicScript.type = "text/javascript"; - dynamicScript.src = src; - docHeadObj.appendChild(dynamicScript); - docHeadObj.removeChild(script_files[i]); - } catch (err) { - if (LiteGraph.throw_errors) { - throw err; - } - if (LiteGraph.debug) { - console.log("Error while reloading " + src); - } - } - } - - if (LiteGraph.debug) { - console.log("Nodes reloaded"); - } - }, - - //separated just to improve if it doesn't work - cloneObject: function(obj, target) { - if (obj == null) { - return null; - } - var r = JSON.parse(JSON.stringify(obj)); - if (!target) { - return r; - } - - for (var i in r) { - target[i] = r[i]; - } - return target; - }, - - /** - * Returns if the types of two slots are compatible (taking into account wildcards, etc) - * @method isValidConnection - * @param {String} type_a - * @param {String} type_b - * @return {Boolean} true if they can be connected - */ - isValidConnection: function(type_a, type_b) { - if ( - !type_a || //generic output - !type_b || //generic input - type_a == type_b || //same type (is valid for triggers) - (type_a == LiteGraph.EVENT && type_b == LiteGraph.ACTION) - ) { - return true; - } - - // Enforce string type to handle toLowerCase call (-1 number not ok) - type_a = String(type_a); - type_b = String(type_b); - type_a = type_a.toLowerCase(); - type_b = type_b.toLowerCase(); - - // For nodes supporting multiple connection types - if (type_a.indexOf(",") == -1 && type_b.indexOf(",") == -1) { - return type_a == type_b; - } - - // Check all permutations to see if one is valid - var supported_types_a = type_a.split(","); - var supported_types_b = type_b.split(","); - for (var i = 0; i < supported_types_a.length; ++i) { - for (var j = 0; j < supported_types_b.length; ++j) { - if (supported_types_a[i] == supported_types_b[j]) { - return true; - } - } - } - - return false; - }, - - /** - * Register a string in the search box so when the user types it it will recommend this node - * @method registerSearchboxExtra - * @param {String} node_type the node recommended - * @param {String} description text to show next to it - * @param {Object} data it could contain info of how the node should be configured - * @return {Boolean} true if they can be connected - */ - registerSearchboxExtra: function(node_type, description, data) { - this.searchbox_extras[description.toLowerCase()] = { - type: node_type, - desc: description, - data: data - }; - }, - - /** - * Wrapper to load files (from url using fetch or from file using FileReader) - * @method fetchFile - * @param {String|File|Blob} url the url of the file (or the file itself) - * @param {String} type an string to know how to fetch it: "text","arraybuffer","json","blob" - * @param {Function} on_complete callback(data) - * @param {Function} on_error in case of an error - * @return {FileReader|Promise} returns the object used to - */ - fetchFile: function( url, type, on_complete, on_error ) { - var that = this; - if(!url) - return null; - - type = type || "text"; - if( url.constructor === String ) - { - if (url.substr(0, 4) == "http" && LiteGraph.proxy) { - url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); - } - return fetch(url) - .then(function(response) { - if(!response.ok) - throw new Error("File not found"); //it will be catch below - if(type == "arraybuffer") - return response.arrayBuffer(); - else if(type == "text" || type == "string") - return response.text(); - else if(type == "json") - return response.json(); - else if(type == "blob") - return response.blob(); - }) - .then(function(data) { - if(on_complete) - on_complete(data); - }) - .catch(function(error) { - console.error("error fetching file:",url); - if(on_error) - on_error(error); - }); - } - else if( url.constructor === File || url.constructor === Blob) - { - var reader = new FileReader(); - reader.onload = function(e) - { - var v = e.target.result; - if( type == "json" ) - v = JSON.parse(v); - if(on_complete) - on_complete(v); - } - if(type == "arraybuffer") - return reader.readAsArrayBuffer(url); - else if(type == "text" || type == "json") - return reader.readAsText(url); - else if(type == "blob") - return reader.readAsBinaryString(url); - } - return null; - } - }); - - //timer that works everywhere - if (typeof performance != "undefined") { - LiteGraph.getTime = performance.now.bind(performance); - } else if (typeof Date != "undefined" && Date.now) { - LiteGraph.getTime = Date.now.bind(Date); - } else if (typeof process != "undefined") { - LiteGraph.getTime = function() { - var t = process.hrtime(); - return t[0] * 0.001 + t[1] * 1e-6; - }; - } else { - LiteGraph.getTime = function getTime() { - return new Date().getTime(); - }; - } - - //********************************************************************************* - // LGraph CLASS - //********************************************************************************* - - /** - * LGraph is the class that contain a full graph. We instantiate one and add nodes to it, and then we can run the execution loop. - * - * @class LGraph - * @constructor - * @param {Object} o data from previous serialization [optional] - */ - - function LGraph(o) { - if (LiteGraph.debug) { - console.log("Graph created"); - } - this.list_of_graphcanvas = null; - this.clear(); - - if (o) { - this.configure(o); - } - } - - global.LGraph = LiteGraph.LGraph = LGraph; - - //default supported types - LGraph.supported_types = ["number", "string", "boolean"]; - - //used to know which types of connections support this graph (some graphs do not allow certain types) - LGraph.prototype.getSupportedTypes = function() { - return this.supported_types || LGraph.supported_types; - }; - - LGraph.STATUS_STOPPED = 1; - LGraph.STATUS_RUNNING = 2; - - /** - * Removes all nodes from this graph - * @method clear - */ - - LGraph.prototype.clear = function() { - this.stop(); - this.status = LGraph.STATUS_STOPPED; - - this.last_node_id = 0; - this.last_link_id = 0; - - this._version = -1; //used to detect changes - - //safe clear - if (this._nodes) { - for (var i = 0; i < this._nodes.length; ++i) { - var node = this._nodes[i]; - if (node.onRemoved) { - node.onRemoved(); - } - } - } - - //nodes - this._nodes = []; - this._nodes_by_id = {}; - this._nodes_in_order = []; //nodes that are executable sorted in execution order - this._nodes_executable = null; //nodes that contain onExecute - - //other scene stuff - this._groups = []; - - //links - this.links = {}; //container with all the links - - //iterations - this.iteration = 0; - - //custom data - this.config = {}; - this.vars = {}; - - //timing - this.globaltime = 0; - this.runningtime = 0; - this.fixedtime = 0; - this.fixedtime_lapse = 0.01; - this.elapsed_time = 0.01; - this.last_update_time = 0; - this.starttime = 0; - - this.catch_errors = true; - - //subgraph_data - this.inputs = {}; - this.outputs = {}; - - //notify canvas to redraw - this.change(); - - this.sendActionToCanvas("clear"); - }; - - /** - * Attach Canvas to this graph - * @method attachCanvas - * @param {GraphCanvas} graph_canvas - */ - - LGraph.prototype.attachCanvas = function(graphcanvas) { - if (graphcanvas.constructor != LGraphCanvas) { - throw "attachCanvas expects a LGraphCanvas instance"; - } - if (graphcanvas.graph && graphcanvas.graph != this) { - graphcanvas.graph.detachCanvas(graphcanvas); - } - - graphcanvas.graph = this; - if (!this.list_of_graphcanvas) { - this.list_of_graphcanvas = []; - } - this.list_of_graphcanvas.push(graphcanvas); - }; - - /** - * Detach Canvas from this graph - * @method detachCanvas - * @param {GraphCanvas} graph_canvas - */ - LGraph.prototype.detachCanvas = function(graphcanvas) { - if (!this.list_of_graphcanvas) { - return; - } - - var pos = this.list_of_graphcanvas.indexOf(graphcanvas); - if (pos == -1) { - return; - } - graphcanvas.graph = null; - this.list_of_graphcanvas.splice(pos, 1); - }; - - /** - * Starts running this graph every interval milliseconds. - * @method start - * @param {number} interval amount of milliseconds between executions, if 0 then it renders to the monitor refresh rate - */ - - LGraph.prototype.start = function(interval) { - if (this.status == LGraph.STATUS_RUNNING) { - return; - } - this.status = LGraph.STATUS_RUNNING; - - if (this.onPlayEvent) { - this.onPlayEvent(); - } - - this.sendEventToAllNodes("onStart"); - - //launch - this.starttime = LiteGraph.getTime(); - this.last_update_time = this.starttime; - interval = interval || 0; - var that = this; - - //execute once per frame - if ( interval == 0 && typeof window != "undefined" && window.requestAnimationFrame ) { - function on_frame() { - if (that.execution_timer_id != -1) { - return; - } - window.requestAnimationFrame(on_frame); - if(that.onBeforeStep) - that.onBeforeStep(); - that.runStep(1, !this.catch_errors); - if(that.onAfterStep) - that.onAfterStep(); - } - this.execution_timer_id = -1; - on_frame(); - } else { //execute every 'interval' ms - this.execution_timer_id = setInterval(function() { - //execute - if(that.onBeforeStep) - that.onBeforeStep(); - that.runStep(1, !this.catch_errors); - if(that.onAfterStep) - that.onAfterStep(); - }, interval); - } - }; - - /** - * Stops the execution loop of the graph - * @method stop execution - */ - - LGraph.prototype.stop = function() { - if (this.status == LGraph.STATUS_STOPPED) { - return; - } - - this.status = LGraph.STATUS_STOPPED; - - if (this.onStopEvent) { - this.onStopEvent(); - } - - if (this.execution_timer_id != null) { - if (this.execution_timer_id != -1) { - clearInterval(this.execution_timer_id); - } - this.execution_timer_id = null; - } - - this.sendEventToAllNodes("onStop"); - }; - - /** - * Run N steps (cycles) of the graph - * @method runStep - * @param {number} num number of steps to run, default is 1 - * @param {Boolean} do_not_catch_errors [optional] if you want to try/catch errors - * @param {number} limit max number of nodes to execute (used to execute from start to a node) - */ - - LGraph.prototype.runStep = function(num, do_not_catch_errors, limit ) { - num = num || 1; - - var start = LiteGraph.getTime(); - this.globaltime = 0.001 * (start - this.starttime); - - var nodes = this._nodes_executable - ? this._nodes_executable - : this._nodes; - if (!nodes) { - return; - } - - limit = limit || nodes.length; - - if (do_not_catch_errors) { - //iterations - for (var i = 0; i < num; i++) { - for (var j = 0; j < limit; ++j) { - var node = nodes[j]; - if (node.mode == LiteGraph.ALWAYS && node.onExecute) { - node.onExecute(); //hard to send elapsed time - } - } - - this.fixedtime += this.fixedtime_lapse; - if (this.onExecuteStep) { - this.onExecuteStep(); - } - } - - if (this.onAfterExecute) { - this.onAfterExecute(); - } - } else { - try { - //iterations - for (var i = 0; i < num; i++) { - for (var j = 0; j < limit; ++j) { - var node = nodes[j]; - if (node.mode == LiteGraph.ALWAYS && node.onExecute) { - node.onExecute(); - } - } - - this.fixedtime += this.fixedtime_lapse; - if (this.onExecuteStep) { - this.onExecuteStep(); - } - } - - if (this.onAfterExecute) { - this.onAfterExecute(); - } - this.errors_in_execution = false; - } catch (err) { - this.errors_in_execution = true; - if (LiteGraph.throw_errors) { - throw err; - } - if (LiteGraph.debug) { - console.log("Error during execution: " + err); - } - this.stop(); - } - } - - var now = LiteGraph.getTime(); - var elapsed = now - start; - if (elapsed == 0) { - elapsed = 1; - } - this.execution_time = 0.001 * elapsed; - this.globaltime += 0.001 * elapsed; - this.iteration += 1; - this.elapsed_time = (now - this.last_update_time) * 0.001; - this.last_update_time = now; - }; - - /** - * Updates the graph execution order according to relevance of the nodes (nodes with only outputs have more relevance than - * nodes with only inputs. - * @method updateExecutionOrder - */ - LGraph.prototype.updateExecutionOrder = function() { - this._nodes_in_order = this.computeExecutionOrder(false); - this._nodes_executable = []; - for (var i = 0; i < this._nodes_in_order.length; ++i) { - if (this._nodes_in_order[i].onExecute) { - this._nodes_executable.push(this._nodes_in_order[i]); - } - } - }; - - //This is more internal, it computes the executable nodes in order and returns it - LGraph.prototype.computeExecutionOrder = function( - only_onExecute, - set_level - ) { - var L = []; - var S = []; - var M = {}; - var visited_links = {}; //to avoid repeating links - var remaining_links = {}; //to a - - //search for the nodes without inputs (starting nodes) - for (var i = 0, l = this._nodes.length; i < l; ++i) { - var node = this._nodes[i]; - if (only_onExecute && !node.onExecute) { - continue; - } - - M[node.id] = node; //add to pending nodes - - var num = 0; //num of input connections - if (node.inputs) { - for (var j = 0, l2 = node.inputs.length; j < l2; j++) { - if (node.inputs[j] && node.inputs[j].link != null) { - num += 1; - } - } - } - - if (num == 0) { - //is a starting node - S.push(node); - if (set_level) { - node._level = 1; - } - } //num of input links - else { - if (set_level) { - node._level = 0; - } - remaining_links[node.id] = num; - } - } - - while (true) { - if (S.length == 0) { - break; - } - - //get an starting node - var node = S.shift(); - L.push(node); //add to ordered list - delete M[node.id]; //remove from the pending nodes - - if (!node.outputs) { - continue; - } - - //for every output - for (var i = 0; i < node.outputs.length; i++) { - var output = node.outputs[i]; - //not connected - if ( - output == null || - output.links == null || - output.links.length == 0 - ) { - continue; - } - - //for every connection - for (var j = 0; j < output.links.length; j++) { - var link_id = output.links[j]; - var link = this.links[link_id]; - if (!link) { - continue; - } - - //already visited link (ignore it) - if (visited_links[link.id]) { - continue; - } - - var target_node = this.getNodeById(link.target_id); - if (target_node == null) { - visited_links[link.id] = true; - continue; - } - - if ( - set_level && - (!target_node._level || - target_node._level <= node._level) - ) { - target_node._level = node._level + 1; - } - - visited_links[link.id] = true; //mark as visited - remaining_links[target_node.id] -= 1; //reduce the number of links remaining - if (remaining_links[target_node.id] == 0) { - S.push(target_node); - } //if no more links, then add to starters array - } - } - } - - //the remaining ones (loops) - for (var i in M) { - L.push(M[i]); - } - - if (L.length != this._nodes.length && LiteGraph.debug) { - console.warn("something went wrong, nodes missing"); - } - - var l = L.length; - - //save order number in the node - for (var i = 0; i < l; ++i) { - L[i].order = i; - } - - //sort now by priority - L = L.sort(function(A, B) { - var Ap = A.constructor.priority || A.priority || 0; - var Bp = B.constructor.priority || B.priority || 0; - if (Ap == Bp) { - //if same priority, sort by order - return A.order - B.order; - } - return Ap - Bp; //sort by priority - }); - - //save order number in the node, again... - for (var i = 0; i < l; ++i) { - L[i].order = i; - } - - return L; - }; - - /** - * Returns all the nodes that could affect this one (ancestors) by crawling all the inputs recursively. - * It doesn't include the node itself - * @method getAncestors - * @return {Array} an array with all the LGraphNodes that affect this node, in order of execution - */ - LGraph.prototype.getAncestors = function(node) { - var ancestors = []; - var pending = [node]; - var visited = {}; - - while (pending.length) { - var current = pending.shift(); - if (!current.inputs) { - continue; - } - if (!visited[current.id] && current != node) { - visited[current.id] = true; - ancestors.push(current); - } - - for (var i = 0; i < current.inputs.length; ++i) { - var input = current.getInputNode(i); - if (input && ancestors.indexOf(input) == -1) { - pending.push(input); - } - } - } - - ancestors.sort(function(a, b) { - return a.order - b.order; - }); - return ancestors; - }; - - /** - * Positions every node in a more readable manner - * @method arrange - */ - LGraph.prototype.arrange = function(margin) { - margin = margin || 100; - - var nodes = this.computeExecutionOrder(false, true); - var columns = []; - for (var i = 0; i < nodes.length; ++i) { - var node = nodes[i]; - var col = node._level || 1; - if (!columns[col]) { - columns[col] = []; - } - columns[col].push(node); - } - - var x = margin; - - for (var i = 0; i < columns.length; ++i) { - var column = columns[i]; - if (!column) { - continue; - } - var max_size = 100; - var y = margin + LiteGraph.NODE_TITLE_HEIGHT; - for (var j = 0; j < column.length; ++j) { - var node = column[j]; - node.pos[0] = x; - node.pos[1] = y; - if (node.size[0] > max_size) { - max_size = node.size[0]; - } - y += node.size[1] + margin + LiteGraph.NODE_TITLE_HEIGHT; - } - x += max_size + margin; - } - - this.setDirtyCanvas(true, true); - }; - - /** - * Returns the amount of time the graph has been running in milliseconds - * @method getTime - * @return {number} number of milliseconds the graph has been running - */ - LGraph.prototype.getTime = function() { - return this.globaltime; - }; - - /** - * Returns the amount of time accumulated using the fixedtime_lapse var. This is used in context where the time increments should be constant - * @method getFixedTime - * @return {number} number of milliseconds the graph has been running - */ - - LGraph.prototype.getFixedTime = function() { - return this.fixedtime; - }; - - /** - * Returns the amount of time it took to compute the latest iteration. Take into account that this number could be not correct - * if the nodes are using graphical actions - * @method getElapsedTime - * @return {number} number of milliseconds it took the last cycle - */ - - LGraph.prototype.getElapsedTime = function() { - return this.elapsed_time; - }; - - /** - * Sends an event to all the nodes, useful to trigger stuff - * @method sendEventToAllNodes - * @param {String} eventname the name of the event (function to be called) - * @param {Array} params parameters in array format - */ - LGraph.prototype.sendEventToAllNodes = function(eventname, params, mode) { - mode = mode || LiteGraph.ALWAYS; - - var nodes = this._nodes_in_order ? this._nodes_in_order : this._nodes; - if (!nodes) { - return; - } - - for (var j = 0, l = nodes.length; j < l; ++j) { - var node = nodes[j]; - - if ( - node.constructor === LiteGraph.Subgraph && - eventname != "onExecute" - ) { - if (node.mode == mode) { - node.sendEventToAllNodes(eventname, params, mode); - } - continue; - } - - if (!node[eventname] || node.mode != mode) { - continue; - } - if (params === undefined) { - node[eventname](); - } else if (params && params.constructor === Array) { - node[eventname].apply(node, params); - } else { - node[eventname](params); - } - } - }; - - LGraph.prototype.sendActionToCanvas = function(action, params) { - if (!this.list_of_graphcanvas) { - return; - } - - for (var i = 0; i < this.list_of_graphcanvas.length; ++i) { - var c = this.list_of_graphcanvas[i]; - if (c[action]) { - c[action].apply(c, params); - } - } - }; - - /** - * Adds a new node instance to this graph - * @method add - * @param {LGraphNode} node the instance of the node - */ - - LGraph.prototype.add = function(node, skip_compute_order) { - if (!node) { - return; - } - - //groups - if (node.constructor === LGraphGroup) { - this._groups.push(node); - this.setDirtyCanvas(true); - this.change(); - node.graph = this; - this._version++; - return; - } - - //nodes - if (node.id != -1 && this._nodes_by_id[node.id] != null) { - console.warn( - "LiteGraph: there is already a node with this ID, changing it" - ); - node.id = ++this.last_node_id; - } - - if (this._nodes.length >= LiteGraph.MAX_NUMBER_OF_NODES) { - throw "LiteGraph: max number of nodes in a graph reached"; - } - - //give him an id - if (node.id == null || node.id == -1) { - node.id = ++this.last_node_id; - } else if (this.last_node_id < node.id) { - this.last_node_id = node.id; - } - - node.graph = this; - this._version++; - - this._nodes.push(node); - this._nodes_by_id[node.id] = node; - - if (node.onAdded) { - node.onAdded(this); - } - - if (this.config.align_to_grid) { - node.alignToGrid(); - } - - if (!skip_compute_order) { - this.updateExecutionOrder(); - } - - if (this.onNodeAdded) { - this.onNodeAdded(node); - } - - this.setDirtyCanvas(true); - this.change(); - - return node; //to chain actions - }; - - /** - * Removes a node from the graph - * @method remove - * @param {LGraphNode} node the instance of the node - */ - - LGraph.prototype.remove = function(node) { - if (node.constructor === LiteGraph.LGraphGroup) { - var index = this._groups.indexOf(node); - if (index != -1) { - this._groups.splice(index, 1); - } - node.graph = null; - this._version++; - this.setDirtyCanvas(true, true); - this.change(); - return; - } - - if (this._nodes_by_id[node.id] == null) { - return; - } //not found - - if (node.ignore_remove) { - return; - } //cannot be removed - - //disconnect inputs - if (node.inputs) { - for (var i = 0; i < node.inputs.length; i++) { - var slot = node.inputs[i]; - if (slot.link != null) { - node.disconnectInput(i); - } - } - } - - //disconnect outputs - if (node.outputs) { - for (var i = 0; i < node.outputs.length; i++) { - var slot = node.outputs[i]; - if (slot.links != null && slot.links.length) { - node.disconnectOutput(i); - } - } - } - - //node.id = -1; //why? - - //callback - if (node.onRemoved) { - node.onRemoved(); - } - - node.graph = null; - this._version++; - - //remove from canvas render - if (this.list_of_graphcanvas) { - for (var i = 0; i < this.list_of_graphcanvas.length; ++i) { - var canvas = this.list_of_graphcanvas[i]; - if (canvas.selected_nodes[node.id]) { - delete canvas.selected_nodes[node.id]; - } - if (canvas.node_dragged == node) { - canvas.node_dragged = null; - } - } - } - - //remove from containers - var pos = this._nodes.indexOf(node); - if (pos != -1) { - this._nodes.splice(pos, 1); - } - delete this._nodes_by_id[node.id]; - - if (this.onNodeRemoved) { - this.onNodeRemoved(node); - } - - this.setDirtyCanvas(true, true); - this.change(); - - this.updateExecutionOrder(); - }; - - /** - * Returns a node by its id. - * @method getNodeById - * @param {Number} id - */ - - LGraph.prototype.getNodeById = function(id) { - if (id == null) { - return null; - } - return this._nodes_by_id[id]; - }; - - /** - * Returns a list of nodes that matches a class - * @method findNodesByClass - * @param {Class} classObject the class itself (not an string) - * @return {Array} a list with all the nodes of this type - */ - LGraph.prototype.findNodesByClass = function(classObject, result) { - result = result || []; - result.length = 0; - for (var i = 0, l = this._nodes.length; i < l; ++i) { - if (this._nodes[i].constructor === classObject) { - result.push(this._nodes[i]); - } - } - return result; - }; - - /** - * Returns a list of nodes that matches a type - * @method findNodesByType - * @param {String} type the name of the node type - * @return {Array} a list with all the nodes of this type - */ - LGraph.prototype.findNodesByType = function(type, result) { - var type = type.toLowerCase(); - result = result || []; - result.length = 0; - for (var i = 0, l = this._nodes.length; i < l; ++i) { - if (this._nodes[i].type.toLowerCase() == type) { - result.push(this._nodes[i]); - } - } - return result; - }; - - /** - * Returns the first node that matches a name in its title - * @method findNodeByTitle - * @param {String} name the name of the node to search - * @return {Node} the node or null - */ - LGraph.prototype.findNodeByTitle = function(title) { - for (var i = 0, l = this._nodes.length; i < l; ++i) { - if (this._nodes[i].title == title) { - return this._nodes[i]; - } - } - return null; - }; - - /** - * Returns a list of nodes that matches a name - * @method findNodesByTitle - * @param {String} name the name of the node to search - * @return {Array} a list with all the nodes with this name - */ - LGraph.prototype.findNodesByTitle = function(title) { - var result = []; - for (var i = 0, l = this._nodes.length; i < l; ++i) { - if (this._nodes[i].title == title) { - result.push(this._nodes[i]); - } - } - return result; - }; - - /** - * Returns the top-most node in this position of the canvas - * @method getNodeOnPos - * @param {number} x the x coordinate in canvas space - * @param {number} y the y coordinate in canvas space - * @param {Array} nodes_list a list with all the nodes to search from, by default is all the nodes in the graph - * @return {LGraphNode} the node at this position or null - */ - LGraph.prototype.getNodeOnPos = function(x, y, nodes_list, margin) { - nodes_list = nodes_list || this._nodes; - for (var i = nodes_list.length - 1; i >= 0; i--) { - var n = nodes_list[i]; - if (n.isPointInside(x, y, margin)) { - return n; - } - } - return null; - }; - - /** - * Returns the top-most group in that position - * @method getGroupOnPos - * @param {number} x the x coordinate in canvas space - * @param {number} y the y coordinate in canvas space - * @return {LGraphGroup} the group or null - */ - LGraph.prototype.getGroupOnPos = function(x, y) { - for (var i = this._groups.length - 1; i >= 0; i--) { - var g = this._groups[i]; - if (g.isPointInside(x, y, 2, true)) { - return g; - } - } - return null; - }; - - /** - * Checks that the node type matches the node type registered, used when replacing a nodetype by a newer version during execution - * this replaces the ones using the old version with the new version - * @method checkNodeTypes - */ - LGraph.prototype.checkNodeTypes = function() { - var changes = false; - for (var i = 0; i < this._nodes.length; i++) { - var node = this._nodes[i]; - var ctor = LiteGraph.registered_node_types[node.type]; - if (node.constructor == ctor) { - continue; - } - console.log("node being replaced by newer version: " + node.type); - var newnode = LiteGraph.createNode(node.type); - changes = true; - this._nodes[i] = newnode; - newnode.configure(node.serialize()); - newnode.graph = this; - this._nodes_by_id[newnode.id] = newnode; - if (node.inputs) { - newnode.inputs = node.inputs.concat(); - } - if (node.outputs) { - newnode.outputs = node.outputs.concat(); - } - } - this.updateExecutionOrder(); - }; - - // ********** GLOBALS ***************** - - LGraph.prototype.onAction = function(action, param) { - this._input_nodes = this.findNodesByClass( - LiteGraph.GraphInput, - this._input_nodes - ); - for (var i = 0; i < this._input_nodes.length; ++i) { - var node = this._input_nodes[i]; - if (node.properties.name != action) { - continue; - } - node.onAction(action, param); - break; - } - }; - - LGraph.prototype.trigger = function(action, param) { - if (this.onTrigger) { - this.onTrigger(action, param); - } - }; - - /** - * Tell this graph it has a global graph input of this type - * @method addGlobalInput - * @param {String} name - * @param {String} type - * @param {*} value [optional] - */ - LGraph.prototype.addInput = function(name, type, value) { - var input = this.inputs[name]; - if (input) { - //already exist - return; - } - - this.inputs[name] = { name: name, type: type, value: value }; - this._version++; - - if (this.onInputAdded) { - this.onInputAdded(name, type); - } - - if (this.onInputsOutputsChange) { - this.onInputsOutputsChange(); - } - }; - - /** - * Assign a data to the global graph input - * @method setGlobalInputData - * @param {String} name - * @param {*} data - */ - LGraph.prototype.setInputData = function(name, data) { - var input = this.inputs[name]; - if (!input) { - return; - } - input.value = data; - }; - - /** - * Returns the current value of a global graph input - * @method getInputData - * @param {String} name - * @return {*} the data - */ - LGraph.prototype.getInputData = function(name) { - var input = this.inputs[name]; - if (!input) { - return null; - } - return input.value; - }; - - /** - * Changes the name of a global graph input - * @method renameInput - * @param {String} old_name - * @param {String} new_name - */ - LGraph.prototype.renameInput = function(old_name, name) { - if (name == old_name) { - return; - } - - if (!this.inputs[old_name]) { - return false; - } - - if (this.inputs[name]) { - console.error("there is already one input with that name"); - return false; - } - - this.inputs[name] = this.inputs[old_name]; - delete this.inputs[old_name]; - this._version++; - - if (this.onInputRenamed) { - this.onInputRenamed(old_name, name); - } - - if (this.onInputsOutputsChange) { - this.onInputsOutputsChange(); - } - }; - - /** - * Changes the type of a global graph input - * @method changeInputType - * @param {String} name - * @param {String} type - */ - LGraph.prototype.changeInputType = function(name, type) { - if (!this.inputs[name]) { - return false; - } - - if ( - this.inputs[name].type && - String(this.inputs[name].type).toLowerCase() == - String(type).toLowerCase() - ) { - return; - } - - this.inputs[name].type = type; - this._version++; - if (this.onInputTypeChanged) { - this.onInputTypeChanged(name, type); - } - }; - - /** - * Removes a global graph input - * @method removeInput - * @param {String} name - * @param {String} type - */ - LGraph.prototype.removeInput = function(name) { - if (!this.inputs[name]) { - return false; - } - - delete this.inputs[name]; - this._version++; - - if (this.onInputRemoved) { - this.onInputRemoved(name); - } - - if (this.onInputsOutputsChange) { - this.onInputsOutputsChange(); - } - return true; - }; - - /** - * Creates a global graph output - * @method addOutput - * @param {String} name - * @param {String} type - * @param {*} value - */ - LGraph.prototype.addOutput = function(name, type, value) { - this.outputs[name] = { name: name, type: type, value: value }; - this._version++; - - if (this.onOutputAdded) { - this.onOutputAdded(name, type); - } - - if (this.onInputsOutputsChange) { - this.onInputsOutputsChange(); - } - }; - - /** - * Assign a data to the global output - * @method setOutputData - * @param {String} name - * @param {String} value - */ - LGraph.prototype.setOutputData = function(name, value) { - var output = this.outputs[name]; - if (!output) { - return; - } - output.value = value; - }; - - /** - * Returns the current value of a global graph output - * @method getOutputData - * @param {String} name - * @return {*} the data - */ - LGraph.prototype.getOutputData = function(name) { - var output = this.outputs[name]; - if (!output) { - return null; - } - return output.value; - }; - - /** - * Renames a global graph output - * @method renameOutput - * @param {String} old_name - * @param {String} new_name - */ - LGraph.prototype.renameOutput = function(old_name, name) { - if (!this.outputs[old_name]) { - return false; - } - - if (this.outputs[name]) { - console.error("there is already one output with that name"); - return false; - } - - this.outputs[name] = this.outputs[old_name]; - delete this.outputs[old_name]; - this._version++; - - if (this.onOutputRenamed) { - this.onOutputRenamed(old_name, name); - } - - if (this.onInputsOutputsChange) { - this.onInputsOutputsChange(); - } - }; - - /** - * Changes the type of a global graph output - * @method changeOutputType - * @param {String} name - * @param {String} type - */ - LGraph.prototype.changeOutputType = function(name, type) { - if (!this.outputs[name]) { - return false; - } - - if ( - this.outputs[name].type && - String(this.outputs[name].type).toLowerCase() == - String(type).toLowerCase() - ) { - return; - } - - this.outputs[name].type = type; - this._version++; - if (this.onOutputTypeChanged) { - this.onOutputTypeChanged(name, type); - } - }; - - /** - * Removes a global graph output - * @method removeOutput - * @param {String} name - */ - LGraph.prototype.removeOutput = function(name) { - if (!this.outputs[name]) { - return false; - } - delete this.outputs[name]; - this._version++; - - if (this.onOutputRemoved) { - this.onOutputRemoved(name); - } - - if (this.onInputsOutputsChange) { - this.onInputsOutputsChange(); - } - return true; - }; - - LGraph.prototype.triggerInput = function(name, value) { - var nodes = this.findNodesByTitle(name); - for (var i = 0; i < nodes.length; ++i) { - nodes[i].onTrigger(value); - } - }; - - LGraph.prototype.setCallback = function(name, func) { - var nodes = this.findNodesByTitle(name); - for (var i = 0; i < nodes.length; ++i) { - nodes[i].setTrigger(func); - } - }; - - LGraph.prototype.connectionChange = function(node, link_info) { - this.updateExecutionOrder(); - if (this.onConnectionChange) { - this.onConnectionChange(node); - } - this._version++; - this.sendActionToCanvas("onConnectionChange"); - }; - - /** - * returns if the graph is in live mode - * @method isLive - */ - - LGraph.prototype.isLive = function() { - if (!this.list_of_graphcanvas) { - return false; - } - - for (var i = 0; i < this.list_of_graphcanvas.length; ++i) { - var c = this.list_of_graphcanvas[i]; - if (c.live_mode) { - return true; - } - } - return false; - }; - - /** - * clears the triggered slot animation in all links (stop visual animation) - * @method clearTriggeredSlots - */ - LGraph.prototype.clearTriggeredSlots = function() { - for (var i in this.links) { - var link_info = this.links[i]; - if (!link_info) { - continue; - } - if (link_info._last_time) { - link_info._last_time = 0; - } - } - }; - - /* Called when something visually changed (not the graph!) */ - LGraph.prototype.change = function() { - if (LiteGraph.debug) { - console.log("Graph changed"); - } - this.sendActionToCanvas("setDirty", [true, true]); - if (this.on_change) { - this.on_change(this); - } - }; - - LGraph.prototype.setDirtyCanvas = function(fg, bg) { - this.sendActionToCanvas("setDirty", [fg, bg]); - }; - - /** - * Destroys a link - * @method removeLink - * @param {Number} link_id - */ - LGraph.prototype.removeLink = function(link_id) { - var link = this.links[link_id]; - if (!link) { - return; - } - var node = this.getNodeById(link.target_id); - if (node) { - node.disconnectInput(link.target_slot); - } - }; - - //save and recover app state *************************************** - /** - * Creates a Object containing all the info about this graph, it can be serialized - * @method serialize - * @return {Object} value of the node - */ - LGraph.prototype.serialize = function() { - var nodes_info = []; - for (var i = 0, l = this._nodes.length; i < l; ++i) { - nodes_info.push(this._nodes[i].serialize()); - } - - //pack link info into a non-verbose format - var links = []; - for (var i in this.links) { - //links is an OBJECT - var link = this.links[i]; - if (!link.serialize) { - //weird bug I havent solved yet - console.warn( - "weird LLink bug, link info is not a LLink but a regular object" - ); - var link2 = new LLink(); - for (var i in link) { - link2[i] = link[i]; - } - this.links[i] = link2; - link = link2; - } - - links.push(link.serialize()); - } - - var groups_info = []; - for (var i = 0; i < this._groups.length; ++i) { - groups_info.push(this._groups[i].serialize()); - } - - var data = { - last_node_id: this.last_node_id, - last_link_id: this.last_link_id, - nodes: nodes_info, - links: links, - groups: groups_info, - config: this.config, - version: LiteGraph.VERSION - }; - - return data; - }; - - /** - * Configure a graph from a JSON string - * @method configure - * @param {String} str configure a graph from a JSON string - * @param {Boolean} returns if there was any error parsing - */ - LGraph.prototype.configure = function(data, keep_old) { - if (!data) { - return; - } - - if (!keep_old) { - this.clear(); - } - - var nodes = data.nodes; - - //decode links info (they are very verbose) - if (data.links && data.links.constructor === Array) { - var links = []; - for (var i = 0; i < data.links.length; ++i) { - var link_data = data.links[i]; - if(!link_data) //weird bug - { - console.warn("serialized graph link data contains errors, skipping."); - continue; - } - var link = new LLink(); - link.configure(link_data); - links[link.id] = link; - } - data.links = links; - } - - //copy all stored fields - for (var i in data) { - if(i == "nodes" || i == "groups" ) //links must be accepted - continue; - this[i] = data[i]; - } - - var error = false; - - //create nodes - this._nodes = []; - if (nodes) { - for (var i = 0, l = nodes.length; i < l; ++i) { - var n_info = nodes[i]; //stored info - var node = LiteGraph.createNode(n_info.type, n_info.title); - if (!node) { - if (LiteGraph.debug) { - console.log( - "Node not found or has errors: " + n_info.type - ); - } - - //in case of error we create a replacement node to avoid losing info - node = new LGraphNode(); - node.last_serialization = n_info; - node.has_errors = true; - error = true; - //continue; - } - - node.id = n_info.id; //id it or it will create a new id - this.add(node, true); //add before configure, otherwise configure cannot create links - } - - //configure nodes afterwards so they can reach each other - for (var i = 0, l = nodes.length; i < l; ++i) { - var n_info = nodes[i]; - var node = this.getNodeById(n_info.id); - if (node) { - node.configure(n_info); - } - } - } - - //groups - this._groups.length = 0; - if (data.groups) { - for (var i = 0; i < data.groups.length; ++i) { - var group = new LiteGraph.LGraphGroup(); - group.configure(data.groups[i]); - this.add(group); - } - } - - this.updateExecutionOrder(); - this._version++; - this.setDirtyCanvas(true, true); - return error; - }; - - LGraph.prototype.load = function(url) { - var that = this; - var req = new XMLHttpRequest(); - req.open("GET", url, true); - req.send(null); - req.onload = function(oEvent) { - if (req.status !== 200) { - console.error("Error loading graph:", req.status, req.response); - return; - } - var data = JSON.parse(req.response); - that.configure(data); - }; - req.onerror = function(err) { - console.error("Error loading graph:", err); - }; - }; - - LGraph.prototype.onNodeTrace = function(node, msg, color) { - //TODO - }; - - //this is the class in charge of storing link information - function LLink(id, type, origin_id, origin_slot, target_id, target_slot) { - this.id = id; - this.type = type; - this.origin_id = origin_id; - this.origin_slot = origin_slot; - this.target_id = target_id; - this.target_slot = target_slot; - - this._data = null; - this._pos = new Float32Array(2); //center - } - - LLink.prototype.configure = function(o) { - if (o.constructor === Array) { - this.id = o[0]; - this.origin_id = o[1]; - this.origin_slot = o[2]; - this.target_id = o[3]; - this.target_slot = o[4]; - this.type = o[5]; - } else { - this.id = o.id; - this.type = o.type; - this.origin_id = o.origin_id; - this.origin_slot = o.origin_slot; - this.target_id = o.target_id; - this.target_slot = o.target_slot; - } - }; - - LLink.prototype.serialize = function() { - return [ - this.id, - this.origin_id, - this.origin_slot, - this.target_id, - this.target_slot, - this.type - ]; - }; - - LiteGraph.LLink = LLink; - - // ************************************************************* - // Node CLASS ******* - // ************************************************************* - - /* - title: string - pos: [x,y] - size: [x,y] - - input|output: every connection - + { name:string, type:string, pos: [x,y]=Optional, direction: "input"|"output", links: Array }); - - general properties: - + clip_area: if you render outside the node, it will be clipped - + unsafe_execution: not allowed for safe execution - + skip_repeated_outputs: when adding new outputs, it wont show if there is one already connected - + resizable: if set to false it wont be resizable with the mouse - + horizontal: slots are distributed horizontally - + widgets_start_y: widgets start at y distance from the top of the node - - flags object: - + collapsed: if it is collapsed - - supported callbacks: - + onAdded: when added to graph (warning: this is called BEFORE the node is configured when loading) - + onRemoved: when removed from graph - + onStart: when the graph starts playing - + onStop: when the graph stops playing - + onDrawForeground: render the inside widgets inside the node - + onDrawBackground: render the background area inside the node (only in edit mode) - + onMouseDown - + onMouseMove - + onMouseUp - + onMouseEnter - + onMouseLeave - + onExecute: execute the node - + onPropertyChanged: when a property is changed in the panel (return true to skip default behaviour) - + onGetInputs: returns an array of possible inputs - + onGetOutputs: returns an array of possible outputs - + onBounding: in case this node has a bigger bounding than the node itself (the callback receives the bounding as [x,y,w,h]) - + onDblClick: double clicked in the node - + onInputDblClick: input slot double clicked (can be used to automatically create a node connected) - + onOutputDblClick: output slot double clicked (can be used to automatically create a node connected) - + onConfigure: called after the node has been configured - + onSerialize: to add extra info when serializing (the callback receives the object that should be filled with the data) - + onSelected - + onDeselected - + onDropItem : DOM item dropped over the node - + onDropFile : file dropped over the node - + onConnectInput : if returns false the incoming connection will be canceled - + onConnectionsChange : a connection changed (new one or removed) (LiteGraph.INPUT or LiteGraph.OUTPUT, slot, true if connected, link_info, input_info ) - + onAction: action slot triggered - + getExtraMenuOptions: to add option to context menu -*/ - - /** - * Base Class for all the node type classes - * @class LGraphNode - * @param {String} name a name for the node - */ - - function LGraphNode(title) { - this._ctor(title); - } - - global.LGraphNode = LiteGraph.LGraphNode = LGraphNode; - - LGraphNode.prototype._ctor = function(title) { - this.title = title || "Unnamed"; - this.size = [LiteGraph.NODE_WIDTH, 60]; - this.graph = null; - - this._pos = new Float32Array(10, 10); - - Object.defineProperty(this, "pos", { - set: function(v) { - if (!v || v.length < 2) { - return; - } - this._pos[0] = v[0]; - this._pos[1] = v[1]; - }, - get: function() { - return this._pos; - }, - enumerable: true - }); - - this.id = -1; //not know till not added - this.type = null; - - //inputs available: array of inputs - this.inputs = []; - this.outputs = []; - this.connections = []; - - //local data - this.properties = {}; //for the values - this.properties_info = []; //for the info - - this.flags = {}; - }; - - /** - * configure a node from an object containing the serialized info - * @method configure - */ - LGraphNode.prototype.configure = function(info) { - if (this.graph) { - this.graph._version++; - } - for (var j in info) { - if (j == "properties") { - //i don't want to clone properties, I want to reuse the old container - for (var k in info.properties) { - this.properties[k] = info.properties[k]; - if (this.onPropertyChanged) { - this.onPropertyChanged( k, info.properties[k] ); - } - } - continue; - } - - if (info[j] == null) { - continue; - } else if (typeof info[j] == "object") { - //object - if (this[j] && this[j].configure) { - this[j].configure(info[j]); - } else { - this[j] = LiteGraph.cloneObject(info[j], this[j]); - } - } //value - else { - this[j] = info[j]; - } - } - - if (!info.title) { - this.title = this.constructor.title; - } - - if (this.onConnectionsChange) { - if (this.inputs) { - for (var i = 0; i < this.inputs.length; ++i) { - var input = this.inputs[i]; - var link_info = this.graph - ? this.graph.links[input.link] - : null; - this.onConnectionsChange( - LiteGraph.INPUT, - i, - true, - link_info, - input - ); //link_info has been created now, so its updated - } - } - - if (this.outputs) { - for (var i = 0; i < this.outputs.length; ++i) { - var output = this.outputs[i]; - if (!output.links) { - continue; - } - for (var j = 0; j < output.links.length; ++j) { - var link_info = this.graph - ? this.graph.links[output.links[j]] - : null; - this.onConnectionsChange( - LiteGraph.OUTPUT, - i, - true, - link_info, - output - ); //link_info has been created now, so its updated - } - } - } - } - - if( this.widgets ) - { - for (var i = 0; i < this.widgets.length; ++i) - { - var w = this.widgets[i]; - if(!w) - continue; - if(w.options && w.options.property && this.properties[ w.options.property ]) - w.value = JSON.parse( JSON.stringify( this.properties[ w.options.property ] ) ); - } - if (info.widgets_values) { - for (var i = 0; i < info.widgets_values.length; ++i) { - if (this.widgets[i]) { - this.widgets[i].value = info.widgets_values[i]; - } - } - } - } - - if (this.onConfigure) { - this.onConfigure(info); - } - }; - - /** - * serialize the content - * @method serialize - */ - - LGraphNode.prototype.serialize = function() { - //create serialization object - var o = { - id: this.id, - type: this.type, - pos: this.pos, - size: this.size, - flags: LiteGraph.cloneObject(this.flags), - order: this.order, - mode: this.mode - }; - - //special case for when there were errors - if (this.constructor === LGraphNode && this.last_serialization) { - return this.last_serialization; - } - - if (this.inputs) { - o.inputs = this.inputs; - } - - if (this.outputs) { - //clear outputs last data (because data in connections is never serialized but stored inside the outputs info) - for (var i = 0; i < this.outputs.length; i++) { - delete this.outputs[i]._data; - } - o.outputs = this.outputs; - } - - if (this.title && this.title != this.constructor.title) { - o.title = this.title; - } - - if (this.properties) { - o.properties = LiteGraph.cloneObject(this.properties); - } - - if (this.widgets && this.serialize_widgets) { - o.widgets_values = []; - for (var i = 0; i < this.widgets.length; ++i) { - if(this.widgets[i]) - o.widgets_values[i] = this.widgets[i].value; - else - o.widgets_values[i] = null; - } - } - - if (!o.type) { - o.type = this.constructor.type; - } - - if (this.color) { - o.color = this.color; - } - if (this.bgcolor) { - o.bgcolor = this.bgcolor; - } - if (this.boxcolor) { - o.boxcolor = this.boxcolor; - } - if (this.shape) { - o.shape = this.shape; - } - - if (this.onSerialize) { - if (this.onSerialize(o)) { - console.warn( - "node onSerialize shouldnt return anything, data should be stored in the object pass in the first parameter" - ); - } - } - - return o; - }; - - /* Creates a clone of this node */ - LGraphNode.prototype.clone = function() { - var node = LiteGraph.createNode(this.type); - if (!node) { - return null; - } - - //we clone it because serialize returns shared containers - var data = LiteGraph.cloneObject(this.serialize()); - - //remove links - if (data.inputs) { - for (var i = 0; i < data.inputs.length; ++i) { - data.inputs[i].link = null; - } - } - - if (data.outputs) { - for (var i = 0; i < data.outputs.length; ++i) { - if (data.outputs[i].links) { - data.outputs[i].links.length = 0; - } - } - } - - delete data["id"]; - //remove links - node.configure(data); - - return node; - }; - - /** - * serialize and stringify - * @method toString - */ - - LGraphNode.prototype.toString = function() { - return JSON.stringify(this.serialize()); - }; - //LGraphNode.prototype.deserialize = function(info) {} //this cannot be done from within, must be done in LiteGraph - - /** - * get the title string - * @method getTitle - */ - - LGraphNode.prototype.getTitle = function() { - return this.title || this.constructor.title; - }; - - /** - * sets the value of a property - * @method setProperty - * @param {String} name - * @param {*} value - */ - LGraphNode.prototype.setProperty = function(name, value) { - if (!this.properties) { - this.properties = {}; - } - if( value === this.properties[name] ) - return; - var prev_value = this.properties[name]; - this.properties[name] = value; - if (this.onPropertyChanged) { - if( this.onPropertyChanged(name, value, prev_value) === false ) //abort change - this.properties[name] = prev_value; - } - if(this.widgets) //widgets could be linked to properties - for(var i = 0; i < this.widgets.length; ++i) - { - var w = this.widgets[i]; - if(!w) - continue; - if(w.options.property == name) - { - w.value = value; - break; - } - } - }; - - // Execution ************************* - /** - * sets the output data - * @method setOutputData - * @param {number} slot - * @param {*} data - */ - LGraphNode.prototype.setOutputData = function(slot, data) { - if (!this.outputs) { - return; - } - - //this maybe slow and a niche case - //if(slot && slot.constructor === String) - // slot = this.findOutputSlot(slot); - - if (slot == -1 || slot >= this.outputs.length) { - return; - } - - var output_info = this.outputs[slot]; - if (!output_info) { - return; - } - - //store data in the output itself in case we want to debug - output_info._data = data; - - //if there are connections, pass the data to the connections - if (this.outputs[slot].links) { - for (var i = 0; i < this.outputs[slot].links.length; i++) { - var link_id = this.outputs[slot].links[i]; - var link = this.graph.links[link_id]; - if(link) - link.data = data; - } - } - }; - - /** - * sets the output data type, useful when you want to be able to overwrite the data type - * @method setOutputDataType - * @param {number} slot - * @param {String} datatype - */ - LGraphNode.prototype.setOutputDataType = function(slot, type) { - if (!this.outputs) { - return; - } - if (slot == -1 || slot >= this.outputs.length) { - return; - } - var output_info = this.outputs[slot]; - if (!output_info) { - return; - } - //store data in the output itself in case we want to debug - output_info.type = type; - - //if there are connections, pass the data to the connections - if (this.outputs[slot].links) { - for (var i = 0; i < this.outputs[slot].links.length; i++) { - var link_id = this.outputs[slot].links[i]; - this.graph.links[link_id].type = type; - } - } - }; - - /** - * Retrieves the input data (data traveling through the connection) from one slot - * @method getInputData - * @param {number} slot - * @param {boolean} force_update if set to true it will force the connected node of this slot to output data into this link - * @return {*} data or if it is not connected returns undefined - */ - LGraphNode.prototype.getInputData = function(slot, force_update) { - if (!this.inputs) { - return; - } //undefined; - - if (slot >= this.inputs.length || this.inputs[slot].link == null) { - return; - } - - var link_id = this.inputs[slot].link; - var link = this.graph.links[link_id]; - if (!link) { - //bug: weird case but it happens sometimes - return null; - } - - if (!force_update) { - return link.data; - } - - //special case: used to extract data from the incoming connection before the graph has been executed - var node = this.graph.getNodeById(link.origin_id); - if (!node) { - return link.data; - } - - if (node.updateOutputData) { - node.updateOutputData(link.origin_slot); - } else if (node.onExecute) { - node.onExecute(); - } - - return link.data; - }; - - /** - * Retrieves the input data type (in case this supports multiple input types) - * @method getInputDataType - * @param {number} slot - * @return {String} datatype in string format - */ - LGraphNode.prototype.getInputDataType = function(slot) { - if (!this.inputs) { - return null; - } //undefined; - - if (slot >= this.inputs.length || this.inputs[slot].link == null) { - return null; - } - var link_id = this.inputs[slot].link; - var link = this.graph.links[link_id]; - if (!link) { - //bug: weird case but it happens sometimes - return null; - } - var node = this.graph.getNodeById(link.origin_id); - if (!node) { - return link.type; - } - var output_info = node.outputs[link.origin_slot]; - if (output_info) { - return output_info.type; - } - return null; - }; - - /** - * Retrieves the input data from one slot using its name instead of slot number - * @method getInputDataByName - * @param {String} slot_name - * @param {boolean} force_update if set to true it will force the connected node of this slot to output data into this link - * @return {*} data or if it is not connected returns null - */ - LGraphNode.prototype.getInputDataByName = function( - slot_name, - force_update - ) { - var slot = this.findInputSlot(slot_name); - if (slot == -1) { - return null; - } - return this.getInputData(slot, force_update); - }; - - /** - * tells you if there is a connection in one input slot - * @method isInputConnected - * @param {number} slot - * @return {boolean} - */ - LGraphNode.prototype.isInputConnected = function(slot) { - if (!this.inputs) { - return false; - } - return slot < this.inputs.length && this.inputs[slot].link != null; - }; - - /** - * tells you info about an input connection (which node, type, etc) - * @method getInputInfo - * @param {number} slot - * @return {Object} object or null { link: id, name: string, type: string or 0 } - */ - LGraphNode.prototype.getInputInfo = function(slot) { - if (!this.inputs) { - return null; - } - if (slot < this.inputs.length) { - return this.inputs[slot]; - } - return null; - }; - - /** - * returns the node connected in the input slot - * @method getInputNode - * @param {number} slot - * @return {LGraphNode} node or null - */ - LGraphNode.prototype.getInputNode = function(slot) { - if (!this.inputs) { - return null; - } - if (slot >= this.inputs.length) { - return null; - } - var input = this.inputs[slot]; - if (!input || input.link === null) { - return null; - } - var link_info = this.graph.links[input.link]; - if (!link_info) { - return null; - } - return this.graph.getNodeById(link_info.origin_id); - }; - - /** - * returns the value of an input with this name, otherwise checks if there is a property with that name - * @method getInputOrProperty - * @param {string} name - * @return {*} value - */ - LGraphNode.prototype.getInputOrProperty = function(name) { - if (!this.inputs || !this.inputs.length) { - return this.properties ? this.properties[name] : null; - } - - for (var i = 0, l = this.inputs.length; i < l; ++i) { - var input_info = this.inputs[i]; - if (name == input_info.name && input_info.link != null) { - var link = this.graph.links[input_info.link]; - if (link) { - return link.data; - } - } - } - return this.properties[name]; - }; - - /** - * tells you the last output data that went in that slot - * @method getOutputData - * @param {number} slot - * @return {Object} object or null - */ - LGraphNode.prototype.getOutputData = function(slot) { - if (!this.outputs) { - return null; - } - if (slot >= this.outputs.length) { - return null; - } - - var info = this.outputs[slot]; - return info._data; - }; - - /** - * tells you info about an output connection (which node, type, etc) - * @method getOutputInfo - * @param {number} slot - * @return {Object} object or null { name: string, type: string, links: [ ids of links in number ] } - */ - LGraphNode.prototype.getOutputInfo = function(slot) { - if (!this.outputs) { - return null; - } - if (slot < this.outputs.length) { - return this.outputs[slot]; - } - return null; - }; - - /** - * tells you if there is a connection in one output slot - * @method isOutputConnected - * @param {number} slot - * @return {boolean} - */ - LGraphNode.prototype.isOutputConnected = function(slot) { - if (!this.outputs) { - return false; - } - return ( - slot < this.outputs.length && - this.outputs[slot].links && - this.outputs[slot].links.length - ); - }; - - /** - * tells you if there is any connection in the output slots - * @method isAnyOutputConnected - * @return {boolean} - */ - LGraphNode.prototype.isAnyOutputConnected = function() { - if (!this.outputs) { - return false; - } - for (var i = 0; i < this.outputs.length; ++i) { - if (this.outputs[i].links && this.outputs[i].links.length) { - return true; - } - } - return false; - }; - - /** - * retrieves all the nodes connected to this output slot - * @method getOutputNodes - * @param {number} slot - * @return {array} - */ - LGraphNode.prototype.getOutputNodes = function(slot) { - if (!this.outputs || this.outputs.length == 0) { - return null; - } - - if (slot >= this.outputs.length) { - return null; - } - - var output = this.outputs[slot]; - if (!output.links || output.links.length == 0) { - return null; - } - - var r = []; - for (var i = 0; i < output.links.length; i++) { - var link_id = output.links[i]; - var link = this.graph.links[link_id]; - if (link) { - var target_node = this.graph.getNodeById(link.target_id); - if (target_node) { - r.push(target_node); - } - } - } - return r; - }; - - /** - * Triggers an event in this node, this will trigger any output with the same name - * @method trigger - * @param {String} event name ( "on_play", ... ) if action is equivalent to false then the event is send to all - * @param {*} param - */ - LGraphNode.prototype.trigger = function(action, param) { - if (!this.outputs || !this.outputs.length) { - return; - } - - if (this.graph) - this.graph._last_trigger_time = LiteGraph.getTime(); - - for (var i = 0; i < this.outputs.length; ++i) { - var output = this.outputs[i]; - if ( !output || output.type !== LiteGraph.EVENT || (action && output.name != action) ) - continue; - this.triggerSlot(i, param); - } - }; - - /** - * Triggers an slot event in this node - * @method triggerSlot - * @param {Number} slot the index of the output slot - * @param {*} param - * @param {Number} link_id [optional] in case you want to trigger and specific output link in a slot - */ - LGraphNode.prototype.triggerSlot = function(slot, param, link_id) { - if (!this.outputs) { - return; - } - - var output = this.outputs[slot]; - if (!output) { - return; - } - - var links = output.links; - if (!links || !links.length) { - return; - } - - if (this.graph) { - this.graph._last_trigger_time = LiteGraph.getTime(); - } - - //for every link attached here - for (var k = 0; k < links.length; ++k) { - var id = links[k]; - if (link_id != null && link_id != id) { - //to skip links - continue; - } - var link_info = this.graph.links[links[k]]; - if (!link_info) { - //not connected - continue; - } - link_info._last_time = LiteGraph.getTime(); - var node = this.graph.getNodeById(link_info.target_id); - if (!node) { - //node not found? - continue; - } - - //used to mark events in graph - var target_connection = node.inputs[link_info.target_slot]; - - if (node.onAction) { - node.onAction(target_connection.name, param); - } else if (node.mode === LiteGraph.ON_TRIGGER) { - if (node.onExecute) { - node.onExecute(param); - } - } - } - }; - - /** - * clears the trigger slot animation - * @method clearTriggeredSlot - * @param {Number} slot the index of the output slot - * @param {Number} link_id [optional] in case you want to trigger and specific output link in a slot - */ - LGraphNode.prototype.clearTriggeredSlot = function(slot, link_id) { - if (!this.outputs) { - return; - } - - var output = this.outputs[slot]; - if (!output) { - return; - } - - var links = output.links; - if (!links || !links.length) { - return; - } - - //for every link attached here - for (var k = 0; k < links.length; ++k) { - var id = links[k]; - if (link_id != null && link_id != id) { - //to skip links - continue; - } - var link_info = this.graph.links[links[k]]; - if (!link_info) { - //not connected - continue; - } - link_info._last_time = 0; - } - }; - - /** - * add a new property to this node - * @method addProperty - * @param {string} name - * @param {*} default_value - * @param {string} type string defining the output type ("vec3","number",...) - * @param {Object} extra_info this can be used to have special properties of the property (like values, etc) - */ - LGraphNode.prototype.addProperty = function( - name, - default_value, - type, - extra_info - ) { - var o = { name: name, type: type, default_value: default_value }; - if (extra_info) { - for (var i in extra_info) { - o[i] = extra_info[i]; - } - } - if (!this.properties_info) { - this.properties_info = []; - } - this.properties_info.push(o); - if (!this.properties) { - this.properties = {}; - } - this.properties[name] = default_value; - return o; - }; - - //connections - - /** - * add a new output slot to use in this node - * @method addOutput - * @param {string} name - * @param {string} type string defining the output type ("vec3","number",...) - * @param {Object} extra_info this can be used to have special properties of an output (label, special color, position, etc) - */ - LGraphNode.prototype.addOutput = function(name, type, extra_info) { - var o = { name: name, type: type, links: null }; - if (extra_info) { - for (var i in extra_info) { - o[i] = extra_info[i]; - } - } - - if (!this.outputs) { - this.outputs = []; - } - this.outputs.push(o); - if (this.onOutputAdded) { - this.onOutputAdded(o); - } - this.size = this.computeSize(); - this.setDirtyCanvas(true, true); - return o; - }; - - /** - * add a new output slot to use in this node - * @method addOutputs - * @param {Array} array of triplets like [[name,type,extra_info],[...]] - */ - LGraphNode.prototype.addOutputs = function(array) { - for (var i = 0; i < array.length; ++i) { - var info = array[i]; - var o = { name: info[0], type: info[1], link: null }; - if (array[2]) { - for (var j in info[2]) { - o[j] = info[2][j]; - } - } - - if (!this.outputs) { - this.outputs = []; - } - this.outputs.push(o); - if (this.onOutputAdded) { - this.onOutputAdded(o); - } - } - - this.size = this.computeSize(); - this.setDirtyCanvas(true, true); - }; - - /** - * remove an existing output slot - * @method removeOutput - * @param {number} slot - */ - LGraphNode.prototype.removeOutput = function(slot) { - this.disconnectOutput(slot); - this.outputs.splice(slot, 1); - for (var i = slot; i < this.outputs.length; ++i) { - if (!this.outputs[i] || !this.outputs[i].links) { - continue; - } - var links = this.outputs[i].links; - for (var j = 0; j < links.length; ++j) { - var link = this.graph.links[links[j]]; - if (!link) { - continue; - } - link.origin_slot -= 1; - } - } - - this.size = this.computeSize(); - if (this.onOutputRemoved) { - this.onOutputRemoved(slot); - } - this.setDirtyCanvas(true, true); - }; - - /** - * add a new input slot to use in this node - * @method addInput - * @param {string} name - * @param {string} type string defining the input type ("vec3","number",...), it its a generic one use 0 - * @param {Object} extra_info this can be used to have special properties of an input (label, color, position, etc) - */ - LGraphNode.prototype.addInput = function(name, type, extra_info) { - type = type || 0; - var o = { name: name, type: type, link: null }; - if (extra_info) { - for (var i in extra_info) { - o[i] = extra_info[i]; - } - } - - if (!this.inputs) { - this.inputs = []; - } - this.inputs.push(o); - this.size = this.computeSize(); - if (this.onInputAdded) { - this.onInputAdded(o); - } - this.setDirtyCanvas(true, true); - return o; - }; - - /** - * add several new input slots in this node - * @method addInputs - * @param {Array} array of triplets like [[name,type,extra_info],[...]] - */ - LGraphNode.prototype.addInputs = function(array) { - for (var i = 0; i < array.length; ++i) { - var info = array[i]; - var o = { name: info[0], type: info[1], link: null }; - if (array[2]) { - for (var j in info[2]) { - o[j] = info[2][j]; - } - } - - if (!this.inputs) { - this.inputs = []; - } - this.inputs.push(o); - if (this.onInputAdded) { - this.onInputAdded(o); - } - } - - this.size = this.computeSize(); - this.setDirtyCanvas(true, true); - }; - - /** - * remove an existing input slot - * @method removeInput - * @param {number} slot - */ - LGraphNode.prototype.removeInput = function(slot) { - this.disconnectInput(slot); - this.inputs.splice(slot, 1); - for (var i = slot; i < this.inputs.length; ++i) { - if (!this.inputs[i]) { - continue; - } - var link = this.graph.links[this.inputs[i].link]; - if (!link) { - continue; - } - link.target_slot -= 1; - } - this.size = this.computeSize(); - if (this.onInputRemoved) { - this.onInputRemoved(slot); - } - this.setDirtyCanvas(true, true); - }; - - /** - * add an special connection to this node (used for special kinds of graphs) - * @method addConnection - * @param {string} name - * @param {string} type string defining the input type ("vec3","number",...) - * @param {[x,y]} pos position of the connection inside the node - * @param {string} direction if is input or output - */ - LGraphNode.prototype.addConnection = function(name, type, pos, direction) { - var o = { - name: name, - type: type, - pos: pos, - direction: direction, - links: null - }; - this.connections.push(o); - return o; - }; - - /** - * computes the size of a node according to its inputs and output slots - * @method computeSize - * @param {number} minHeight - * @return {number} the total size - */ - LGraphNode.prototype.computeSize = function(minHeight, out) { - if (this.constructor.size) { - return this.constructor.size.concat(); - } - - var rows = Math.max( - this.inputs ? this.inputs.length : 1, - this.outputs ? this.outputs.length : 1 - ); - var size = out || new Float32Array([0, 0]); - rows = Math.max(rows, 1); - var font_size = LiteGraph.NODE_TEXT_SIZE; //although it should be graphcanvas.inner_text_font size - size[1] = (this.constructor.slot_start_y || 0) + rows * LiteGraph.NODE_SLOT_HEIGHT; - - var widgets_height = 0; - if (this.widgets && this.widgets.length) { - widgets_height = this.widgets.length * (LiteGraph.NODE_WIDGET_HEIGHT + 4) + 8; - } - - //compute height using widgets height - if( this.widgets_up ) - size[1] = Math.max( size[1], widgets_height ); - else if( this.widgets_start_y != null ) - size[1] = Math.max( size[1], widgets_height + this.widgets_start_y ); - else - size[1] += widgets_height; - - var font_size = font_size; - var title_width = compute_text_size(this.title); - var input_width = 0; - var output_width = 0; - - if (this.inputs) { - for (var i = 0, l = this.inputs.length; i < l; ++i) { - var input = this.inputs[i]; - var text = input.label || input.name || ""; - var text_width = compute_text_size(text); - if (input_width < text_width) { - input_width = text_width; - } - } - } - - if (this.outputs) { - for (var i = 0, l = this.outputs.length; i < l; ++i) { - var output = this.outputs[i]; - var text = output.label || output.name || ""; - var text_width = compute_text_size(text); - if (output_width < text_width) { - output_width = text_width; - } - } - } - - size[0] = Math.max(input_width + output_width + 10, title_width); - size[0] = Math.max(size[0], LiteGraph.NODE_WIDTH); - if (this.widgets && this.widgets.length) { - size[0] = Math.max(size[0], LiteGraph.NODE_WIDTH * 1.5); - } - - if (this.onResize) { - this.onResize(size); - } - - function compute_text_size(text) { - if (!text) { - return 0; - } - return font_size * text.length * 0.6; - } - - if ( - this.constructor.min_height && - size[1] < this.constructor.min_height - ) { - size[1] = this.constructor.min_height; - } - - size[1] += 6; //margin - - return size; - }; - - /** - * returns all the info available about a property of this node. - * - * @method getPropertyInfo - * @param {String} property name of the property - * @return {Object} the object with all the available info - */ - LGraphNode.prototype.getPropertyInfo = function( property ) - { - var info = null; - - //there are several ways to define info about a property - //legacy mode - if (this.properties_info) { - for (var i = 0; i < this.properties_info.length; ++i) { - if (this.properties_info[i].name == property) { - info = this.properties_info[i]; - break; - } - } - } - //litescene mode using the constructor - if(this.constructor["@" + property]) - info = this.constructor["@" + property]; - - //litescene mode using the constructor - if (this.onGetPropertyInfo) { - info = this.onGetPropertyInfo(property); - } - - if (!info) - info = {}; - if(!info.type) - info.type = typeof this.properties[property]; - - return info; - } - - /** - * Defines a widget inside the node, it will be rendered on top of the node, you can control lots of properties - * - * @method addWidget - * @param {String} type the widget type (could be "number","string","combo" - * @param {String} name the text to show on the widget - * @param {String} value the default value - * @param {Function|String} callback function to call when it changes (optionally, it can be the name of the property to modify) - * @param {Object} options the object that contains special properties of this widget - * @return {Object} the created widget object - */ - LGraphNode.prototype.addWidget = function( type, name, value, callback, options ) - { - if (!this.widgets) { - this.widgets = []; - } - - if(!options && callback && callback.constructor === Object) - { - options = callback; - callback = null; - } - - if(options && options.constructor === String) //options can be the property name - options = { property: options }; - - if(callback && callback.constructor === String) //callback can be the property name - { - if(!options) - options = {}; - options.property = callback; - callback = null; - } - - if(callback && callback.constructor !== Function) - { - console.warn("addWidget: callback must be a function"); - callback = null; - } - - var w = { - type: type.toLowerCase(), - name: name, - value: value, - callback: callback, - options: options || {} - }; - - if (w.options.y !== undefined) { - w.y = w.options.y; - } - - if (!callback && !w.options.callback && !w.options.property) { - console.warn("LiteGraph addWidget(...) without a callback or property assigned"); - } - if (type == "combo" && !w.options.values) { - throw "LiteGraph addWidget('combo',...) requires to pass values in options: { values:['red','blue'] }"; - } - this.widgets.push(w); - this.size = this.computeSize(); - return w; - }; - - LGraphNode.prototype.addCustomWidget = function(custom_widget) { - if (!this.widgets) { - this.widgets = []; - } - this.widgets.push(custom_widget); - return custom_widget; - }; - - /** - * returns the bounding of the object, used for rendering purposes - * bounding is: [topleft_cornerx, topleft_cornery, width, height] - * @method getBounding - * @return {Float32Array[4]} the total size - */ - LGraphNode.prototype.getBounding = function(out) { - out = out || new Float32Array(4); - out[0] = this.pos[0] - 4; - out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT; - out[2] = this.size[0] + 4; - out[3] = this.size[1] + LiteGraph.NODE_TITLE_HEIGHT; - - if (this.onBounding) { - this.onBounding(out); - } - return out; - }; - - /** - * checks if a point is inside the shape of a node - * @method isPointInside - * @param {number} x - * @param {number} y - * @return {boolean} - */ - LGraphNode.prototype.isPointInside = function(x, y, margin, skip_title) { - margin = margin || 0; - - var margin_top = this.graph && this.graph.isLive() ? 0 : LiteGraph.NODE_TITLE_HEIGHT; - if (skip_title) { - margin_top = 0; - } - if (this.flags && this.flags.collapsed) { - //if ( distance([x,y], [this.pos[0] + this.size[0]*0.5, this.pos[1] + this.size[1]*0.5]) < LiteGraph.NODE_COLLAPSED_RADIUS) - if ( - isInsideRectangle( - x, - y, - this.pos[0] - margin, - this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT - margin, - (this._collapsed_width || LiteGraph.NODE_COLLAPSED_WIDTH) + - 2 * margin, - LiteGraph.NODE_TITLE_HEIGHT + 2 * margin - ) - ) { - return true; - } - } else if ( - this.pos[0] - 4 - margin < x && - this.pos[0] + this.size[0] + 4 + margin > x && - this.pos[1] - margin_top - margin < y && - this.pos[1] + this.size[1] + margin > y - ) { - return true; - } - return false; - }; - - /** - * checks if a point is inside a node slot, and returns info about which slot - * @method getSlotInPosition - * @param {number} x - * @param {number} y - * @return {Object} if found the object contains { input|output: slot object, slot: number, link_pos: [x,y] } - */ - LGraphNode.prototype.getSlotInPosition = function(x, y) { - //search for inputs - var link_pos = new Float32Array(2); - if (this.inputs) { - for (var i = 0, l = this.inputs.length; i < l; ++i) { - var input = this.inputs[i]; - this.getConnectionPos(true, i, link_pos); - if ( - isInsideRectangle( - x, - y, - link_pos[0] - 10, - link_pos[1] - 5, - 20, - 10 - ) - ) { - return { input: input, slot: i, link_pos: link_pos }; - } - } - } - - if (this.outputs) { - for (var i = 0, l = this.outputs.length; i < l; ++i) { - var output = this.outputs[i]; - this.getConnectionPos(false, i, link_pos); - if ( - isInsideRectangle( - x, - y, - link_pos[0] - 10, - link_pos[1] - 5, - 20, - 10 - ) - ) { - return { output: output, slot: i, link_pos: link_pos }; - } - } - } - - return null; - }; - - /** - * returns the input slot with a given name (used for dynamic slots), -1 if not found - * @method findInputSlot - * @param {string} name the name of the slot - * @return {number} the slot (-1 if not found) - */ - LGraphNode.prototype.findInputSlot = function(name) { - if (!this.inputs) { - return -1; - } - for (var i = 0, l = this.inputs.length; i < l; ++i) { - if (name == this.inputs[i].name) { - return i; - } - } - return -1; - }; - - /** - * returns the output slot with a given name (used for dynamic slots), -1 if not found - * @method findOutputSlot - * @param {string} name the name of the slot - * @return {number} the slot (-1 if not found) - */ - LGraphNode.prototype.findOutputSlot = function(name) { - if (!this.outputs) { - return -1; - } - for (var i = 0, l = this.outputs.length; i < l; ++i) { - if (name == this.outputs[i].name) { - return i; - } - } - return -1; - }; - - /** - * connect this node output to the input of another node - * @method connect - * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) - * @param {LGraphNode} node the target node - * @param {number_or_string} target_slot the input slot of the target node (could be the number of the slot or the string with the name of the slot, or -1 to connect a trigger) - * @return {Object} the link_info is created, otherwise null - */ - LGraphNode.prototype.connect = function(slot, target_node, target_slot) { - target_slot = target_slot || 0; - - if (!this.graph) { - //could be connected before adding it to a graph - console.log( - "Connect: Error, node doesn't belong to any graph. Nodes must be added first to a graph before connecting them." - ); //due to link ids being associated with graphs - return null; - } - - //seek for the output slot - if (slot.constructor === String) { - slot = this.findOutputSlot(slot); - if (slot == -1) { - if (LiteGraph.debug) { - console.log("Connect: Error, no slot of name " + slot); - } - return null; - } - } else if (!this.outputs || slot >= this.outputs.length) { - if (LiteGraph.debug) { - console.log("Connect: Error, slot number not found"); - } - return null; - } - - if (target_node && target_node.constructor === Number) { - target_node = this.graph.getNodeById(target_node); - } - if (!target_node) { - throw "target node is null"; - } - - //avoid loopback - if (target_node == this) { - return null; - } - - //you can specify the slot by name - if (target_slot.constructor === String) { - target_slot = target_node.findInputSlot(target_slot); - if (target_slot == -1) { - if (LiteGraph.debug) { - console.log( - "Connect: Error, no slot of name " + target_slot - ); - } - return null; - } - } else if (target_slot === LiteGraph.EVENT) { - //search for first slot with event? - /* - //create input for trigger - var input = target_node.addInput("onTrigger", LiteGraph.EVENT ); - target_slot = target_node.inputs.length - 1; //last one is the one created - target_node.mode = LiteGraph.ON_TRIGGER; - */ - return null; - } else if ( - !target_node.inputs || - target_slot >= target_node.inputs.length - ) { - if (LiteGraph.debug) { - console.log("Connect: Error, slot number not found"); - } - return null; - } - - //if there is something already plugged there, disconnect - if (target_node.inputs[target_slot].link != null) { - target_node.disconnectInput(target_slot); - } - - //why here?? - //this.setDirtyCanvas(false,true); - //this.graph.connectionChange( this ); - - var output = this.outputs[slot]; - - //allows nodes to block connection - if (target_node.onConnectInput) { - if ( target_node.onConnectInput(target_slot, output.type, output, this, slot) === false ) { - return null; - } - } - - var input = target_node.inputs[target_slot]; - var link_info = null; - - if (LiteGraph.isValidConnection(output.type, input.type)) { - link_info = new LLink( - ++this.graph.last_link_id, - input.type, - this.id, - slot, - target_node.id, - target_slot - ); - - //add to graph links list - this.graph.links[link_info.id] = link_info; - - //connect in output - if (output.links == null) { - output.links = []; - } - output.links.push(link_info.id); - //connect in input - target_node.inputs[target_slot].link = link_info.id; - if (this.graph) { - this.graph._version++; - } - if (this.onConnectionsChange) { - this.onConnectionsChange( - LiteGraph.OUTPUT, - slot, - true, - link_info, - output - ); - } //link_info has been created now, so its updated - if (target_node.onConnectionsChange) { - target_node.onConnectionsChange( - LiteGraph.INPUT, - target_slot, - true, - link_info, - input - ); - } - if (this.graph && this.graph.onNodeConnectionChange) { - this.graph.onNodeConnectionChange( - LiteGraph.INPUT, - target_node, - target_slot, - this, - slot - ); - this.graph.onNodeConnectionChange( - LiteGraph.OUTPUT, - this, - slot, - target_node, - target_slot - ); - } - } - - this.setDirtyCanvas(false, true); - this.graph.connectionChange(this, link_info); - - return link_info; - }; - - /** - * disconnect one output to an specific node - * @method disconnectOutput - * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) - * @param {LGraphNode} target_node the target node to which this slot is connected [Optional, if not target_node is specified all nodes will be disconnected] - * @return {boolean} if it was disconnected successfully - */ - LGraphNode.prototype.disconnectOutput = function(slot, target_node) { - if (slot.constructor === String) { - slot = this.findOutputSlot(slot); - if (slot == -1) { - if (LiteGraph.debug) { - console.log("Connect: Error, no slot of name " + slot); - } - return false; - } - } else if (!this.outputs || slot >= this.outputs.length) { - if (LiteGraph.debug) { - console.log("Connect: Error, slot number not found"); - } - return false; - } - - //get output slot - var output = this.outputs[slot]; - if (!output || !output.links || output.links.length == 0) { - return false; - } - - //one of the output links in this slot - if (target_node) { - if (target_node.constructor === Number) { - target_node = this.graph.getNodeById(target_node); - } - if (!target_node) { - throw "Target Node not found"; - } - - for (var i = 0, l = output.links.length; i < l; i++) { - var link_id = output.links[i]; - var link_info = this.graph.links[link_id]; - - //is the link we are searching for... - if (link_info.target_id == target_node.id) { - output.links.splice(i, 1); //remove here - var input = target_node.inputs[link_info.target_slot]; - input.link = null; //remove there - delete this.graph.links[link_id]; //remove the link from the links pool - if (this.graph) { - this.graph._version++; - } - if (target_node.onConnectionsChange) { - target_node.onConnectionsChange( - LiteGraph.INPUT, - link_info.target_slot, - false, - link_info, - input - ); - } //link_info hasn't been modified so its ok - if (this.onConnectionsChange) { - this.onConnectionsChange( - LiteGraph.OUTPUT, - slot, - false, - link_info, - output - ); - } - if (this.graph && this.graph.onNodeConnectionChange) { - this.graph.onNodeConnectionChange( - LiteGraph.OUTPUT, - this, - slot - ); - } - if (this.graph && this.graph.onNodeConnectionChange) { - this.graph.onNodeConnectionChange( - LiteGraph.OUTPUT, - this, - slot - ); - this.graph.onNodeConnectionChange( - LiteGraph.INPUT, - target_node, - link_info.target_slot - ); - } - break; - } - } - } //all the links in this output slot - else { - for (var i = 0, l = output.links.length; i < l; i++) { - var link_id = output.links[i]; - var link_info = this.graph.links[link_id]; - if (!link_info) { - //bug: it happens sometimes - continue; - } - - var target_node = this.graph.getNodeById(link_info.target_id); - var input = null; - if (this.graph) { - this.graph._version++; - } - if (target_node) { - input = target_node.inputs[link_info.target_slot]; - input.link = null; //remove other side link - if (target_node.onConnectionsChange) { - target_node.onConnectionsChange( - LiteGraph.INPUT, - link_info.target_slot, - false, - link_info, - input - ); - } //link_info hasn't been modified so its ok - if (this.graph && this.graph.onNodeConnectionChange) { - this.graph.onNodeConnectionChange( - LiteGraph.INPUT, - target_node, - link_info.target_slot - ); - } - } - delete this.graph.links[link_id]; //remove the link from the links pool - if (this.onConnectionsChange) { - this.onConnectionsChange( - LiteGraph.OUTPUT, - slot, - false, - link_info, - output - ); - } - if (this.graph && this.graph.onNodeConnectionChange) { - this.graph.onNodeConnectionChange( - LiteGraph.OUTPUT, - this, - slot - ); - this.graph.onNodeConnectionChange( - LiteGraph.INPUT, - target_node, - link_info.target_slot - ); - } - } - output.links = null; - } - - this.setDirtyCanvas(false, true); - this.graph.connectionChange(this); - return true; - }; - - /** - * disconnect one input - * @method disconnectInput - * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) - * @return {boolean} if it was disconnected successfully - */ - LGraphNode.prototype.disconnectInput = function(slot) { - //seek for the output slot - if (slot.constructor === String) { - slot = this.findInputSlot(slot); - if (slot == -1) { - if (LiteGraph.debug) { - console.log("Connect: Error, no slot of name " + slot); - } - return false; - } - } else if (!this.inputs || slot >= this.inputs.length) { - if (LiteGraph.debug) { - console.log("Connect: Error, slot number not found"); - } - return false; - } - - var input = this.inputs[slot]; - if (!input) { - return false; - } - - var link_id = this.inputs[slot].link; - this.inputs[slot].link = null; - - //remove other side - var link_info = this.graph.links[link_id]; - if (link_info) { - var target_node = this.graph.getNodeById(link_info.origin_id); - if (!target_node) { - return false; - } - - var output = target_node.outputs[link_info.origin_slot]; - if (!output || !output.links || output.links.length == 0) { - return false; - } - - //search in the inputs list for this link - for (var i = 0, l = output.links.length; i < l; i++) { - if (output.links[i] == link_id) { - output.links.splice(i, 1); - break; - } - } - - delete this.graph.links[link_id]; //remove from the pool - if (this.graph) { - this.graph._version++; - } - if (this.onConnectionsChange) { - this.onConnectionsChange( - LiteGraph.INPUT, - slot, - false, - link_info, - input - ); - } - if (target_node.onConnectionsChange) { - target_node.onConnectionsChange( - LiteGraph.OUTPUT, - i, - false, - link_info, - output - ); - } - if (this.graph && this.graph.onNodeConnectionChange) { - this.graph.onNodeConnectionChange( - LiteGraph.OUTPUT, - target_node, - i - ); - this.graph.onNodeConnectionChange(LiteGraph.INPUT, this, slot); - } - } - - this.setDirtyCanvas(false, true); - this.graph.connectionChange(this); - return true; - }; - - /** - * returns the center of a connection point in canvas coords - * @method getConnectionPos - * @param {boolean} is_input true if if a input slot, false if it is an output - * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) - * @param {vec2} out [optional] a place to store the output, to free garbage - * @return {[x,y]} the position - **/ - LGraphNode.prototype.getConnectionPos = function( - is_input, - slot_number, - out - ) { - out = out || new Float32Array(2); - var num_slots = 0; - if (is_input && this.inputs) { - num_slots = this.inputs.length; - } - if (!is_input && this.outputs) { - num_slots = this.outputs.length; - } - - var offset = LiteGraph.NODE_SLOT_HEIGHT * 0.5; - - if (this.flags.collapsed) { - var w = this._collapsed_width || LiteGraph.NODE_COLLAPSED_WIDTH; - if (this.horizontal) { - out[0] = this.pos[0] + w * 0.5; - if (is_input) { - out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT; - } else { - out[1] = this.pos[1]; - } - } else { - if (is_input) { - out[0] = this.pos[0]; - } else { - out[0] = this.pos[0] + w; - } - out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT * 0.5; - } - return out; - } - - //weird feature that never got finished - if (is_input && slot_number == -1) { - out[0] = this.pos[0] + LiteGraph.NODE_TITLE_HEIGHT * 0.5; - out[1] = this.pos[1] + LiteGraph.NODE_TITLE_HEIGHT * 0.5; - return out; - } - - //hard-coded pos - if ( - is_input && - num_slots > slot_number && - this.inputs[slot_number].pos - ) { - out[0] = this.pos[0] + this.inputs[slot_number].pos[0]; - out[1] = this.pos[1] + this.inputs[slot_number].pos[1]; - return out; - } else if ( - !is_input && - num_slots > slot_number && - this.outputs[slot_number].pos - ) { - out[0] = this.pos[0] + this.outputs[slot_number].pos[0]; - out[1] = this.pos[1] + this.outputs[slot_number].pos[1]; - return out; - } - - //horizontal distributed slots - if (this.horizontal) { - out[0] = - this.pos[0] + (slot_number + 0.5) * (this.size[0] / num_slots); - if (is_input) { - out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT; - } else { - out[1] = this.pos[1] + this.size[1]; - } - return out; - } - - //default vertical slots - if (is_input) { - out[0] = this.pos[0] + offset; - } else { - out[0] = this.pos[0] + this.size[0] + 1 - offset; - } - out[1] = - this.pos[1] + - (slot_number + 0.7) * LiteGraph.NODE_SLOT_HEIGHT + - (this.constructor.slot_start_y || 0); - return out; - }; - - /* Force align to grid */ - LGraphNode.prototype.alignToGrid = function() { - this.pos[0] = - LiteGraph.CANVAS_GRID_SIZE * - Math.round(this.pos[0] / LiteGraph.CANVAS_GRID_SIZE); - this.pos[1] = - LiteGraph.CANVAS_GRID_SIZE * - Math.round(this.pos[1] / LiteGraph.CANVAS_GRID_SIZE); - }; - - /* Console output */ - LGraphNode.prototype.trace = function(msg) { - if (!this.console) { - this.console = []; - } - this.console.push(msg); - if (this.console.length > LGraphNode.MAX_CONSOLE) { - this.console.shift(); - } - - this.graph.onNodeTrace(this, msg); - }; - - /* Forces to redraw or the main canvas (LGraphNode) or the bg canvas (links) */ - LGraphNode.prototype.setDirtyCanvas = function( - dirty_foreground, - dirty_background - ) { - if (!this.graph) { - return; - } - this.graph.sendActionToCanvas("setDirty", [ - dirty_foreground, - dirty_background - ]); - }; - - LGraphNode.prototype.loadImage = function(url) { - var img = new Image(); - img.src = LiteGraph.node_images_path + url; - img.ready = false; - - var that = this; - img.onload = function() { - this.ready = true; - that.setDirtyCanvas(true); - }; - return img; - }; - - //safe LGraphNode action execution (not sure if safe) - /* -LGraphNode.prototype.executeAction = function(action) -{ - if(action == "") return false; - - if( action.indexOf(";") != -1 || action.indexOf("}") != -1) - { - this.trace("Error: Action contains unsafe characters"); - return false; - } - - var tokens = action.split("("); - var func_name = tokens[0]; - if( typeof(this[func_name]) != "function") - { - this.trace("Error: Action not found on node: " + func_name); - return false; - } - - var code = action; - - try - { - var _foo = eval; - eval = null; - (new Function("with(this) { " + code + "}")).call(this); - eval = _foo; - } - catch (err) - { - this.trace("Error executing action {" + action + "} :" + err); - return false; - } - - return true; -} -*/ - - /* Allows to get onMouseMove and onMouseUp events even if the mouse is out of focus */ - LGraphNode.prototype.captureInput = function(v) { - if (!this.graph || !this.graph.list_of_graphcanvas) { - return; - } - - var list = this.graph.list_of_graphcanvas; - - for (var i = 0; i < list.length; ++i) { - var c = list[i]; - //releasing somebody elses capture?! - if (!v && c.node_capturing_input != this) { - continue; - } - - //change - c.node_capturing_input = v ? this : null; - } - }; - - /** - * Collapse the node to make it smaller on the canvas - * @method collapse - **/ - LGraphNode.prototype.collapse = function(force) { - this.graph._version++; - if (this.constructor.collapsable === false && !force) { - return; - } - if (!this.flags.collapsed) { - this.flags.collapsed = true; - } else { - this.flags.collapsed = false; - } - this.setDirtyCanvas(true, true); - }; - - /** - * Forces the node to do not move or realign on Z - * @method pin - **/ - - LGraphNode.prototype.pin = function(v) { - this.graph._version++; - if (v === undefined) { - this.flags.pinned = !this.flags.pinned; - } else { - this.flags.pinned = v; - } - }; - - LGraphNode.prototype.localToScreen = function(x, y, graphcanvas) { - return [ - (x + this.pos[0]) * graphcanvas.scale + graphcanvas.offset[0], - (y + this.pos[1]) * graphcanvas.scale + graphcanvas.offset[1] - ]; - }; - - function LGraphGroup(title) { - this._ctor(title); - } - - global.LGraphGroup = LiteGraph.LGraphGroup = LGraphGroup; - - LGraphGroup.prototype._ctor = function(title) { - this.title = title || "Group"; - this.font_size = 24; - this.color = LGraphCanvas.node_colors.pale_blue - ? LGraphCanvas.node_colors.pale_blue.groupcolor - : "#AAA"; - this._bounding = new Float32Array([10, 10, 140, 80]); - this._pos = this._bounding.subarray(0, 2); - this._size = this._bounding.subarray(2, 4); - this._nodes = []; - this.graph = null; - - Object.defineProperty(this, "pos", { - set: function(v) { - if (!v || v.length < 2) { - return; - } - this._pos[0] = v[0]; - this._pos[1] = v[1]; - }, - get: function() { - return this._pos; - }, - enumerable: true - }); - - Object.defineProperty(this, "size", { - set: function(v) { - if (!v || v.length < 2) { - return; - } - this._size[0] = Math.max(140, v[0]); - this._size[1] = Math.max(80, v[1]); - }, - get: function() { - return this._size; - }, - enumerable: true - }); - }; - - LGraphGroup.prototype.configure = function(o) { - this.title = o.title; - this._bounding.set(o.bounding); - this.color = o.color; - this.font = o.font; - }; - - LGraphGroup.prototype.serialize = function() { - var b = this._bounding; - return { - title: this.title, - bounding: [ - Math.round(b[0]), - Math.round(b[1]), - Math.round(b[2]), - Math.round(b[3]) - ], - color: this.color, - font: this.font - }; - }; - - LGraphGroup.prototype.move = function(deltax, deltay, ignore_nodes) { - this._pos[0] += deltax; - this._pos[1] += deltay; - if (ignore_nodes) { - return; - } - for (var i = 0; i < this._nodes.length; ++i) { - var node = this._nodes[i]; - node.pos[0] += deltax; - node.pos[1] += deltay; - } - }; - - LGraphGroup.prototype.recomputeInsideNodes = function() { - this._nodes.length = 0; - var nodes = this.graph._nodes; - var node_bounding = new Float32Array(4); - - for (var i = 0; i < nodes.length; ++i) { - var node = nodes[i]; - node.getBounding(node_bounding); - if (!overlapBounding(this._bounding, node_bounding)) { - continue; - } //out of the visible area - this._nodes.push(node); - } - }; - - LGraphGroup.prototype.isPointInside = LGraphNode.prototype.isPointInside; - LGraphGroup.prototype.setDirtyCanvas = LGraphNode.prototype.setDirtyCanvas; - - //**************************************** - - //Scale and Offset - function DragAndScale(element, skip_events) { - this.offset = new Float32Array([0, 0]); - this.scale = 1; - this.max_scale = 10; - this.min_scale = 0.1; - this.onredraw = null; - this.enabled = true; - this.last_mouse = [0, 0]; - this.element = null; - this.visible_area = new Float32Array(4); - - if (element) { - this.element = element; - if (!skip_events) { - this.bindEvents(element); - } - } - } - - LiteGraph.DragAndScale = DragAndScale; - - DragAndScale.prototype.bindEvents = function(element) { - this.last_mouse = new Float32Array(2); - - this._binded_mouse_callback = this.onMouse.bind(this); - - element.addEventListener("mousedown", this._binded_mouse_callback); - element.addEventListener("mousemove", this._binded_mouse_callback); - - element.addEventListener( - "mousewheel", - this._binded_mouse_callback, - false - ); - element.addEventListener("wheel", this._binded_mouse_callback, false); - }; - - DragAndScale.prototype.computeVisibleArea = function() { - if (!this.element) { - this.visible_area[0] = this.visible_area[1] = this.visible_area[2] = this.visible_area[3] = 0; - return; - } - var width = this.element.width; - var height = this.element.height; - var startx = -this.offset[0]; - var starty = -this.offset[1]; - var endx = startx + width / this.scale; - var endy = starty + height / this.scale; - this.visible_area[0] = startx; - this.visible_area[1] = starty; - this.visible_area[2] = endx - startx; - this.visible_area[3] = endy - starty; - }; - - DragAndScale.prototype.onMouse = function(e) { - if (!this.enabled) { - return; - } - - var canvas = this.element; - var rect = canvas.getBoundingClientRect(); - var x = e.clientX - rect.left; - var y = e.clientY - rect.top; - e.canvasx = x; - e.canvasy = y; - e.dragging = this.dragging; - - var ignore = false; - if (this.onmouse) { - ignore = this.onmouse(e); - } - - if (e.type == "mousedown") { - this.dragging = true; - canvas.removeEventListener( - "mousemove", - this._binded_mouse_callback - ); - document.body.addEventListener( - "mousemove", - this._binded_mouse_callback - ); - document.body.addEventListener( - "mouseup", - this._binded_mouse_callback - ); - } else if (e.type == "mousemove") { - if (!ignore) { - var deltax = x - this.last_mouse[0]; - var deltay = y - this.last_mouse[1]; - if (this.dragging) { - this.mouseDrag(deltax, deltay); - } - } - } else if (e.type == "mouseup") { - this.dragging = false; - document.body.removeEventListener( - "mousemove", - this._binded_mouse_callback - ); - document.body.removeEventListener( - "mouseup", - this._binded_mouse_callback - ); - canvas.addEventListener("mousemove", this._binded_mouse_callback); - } else if ( - e.type == "mousewheel" || - e.type == "wheel" || - e.type == "DOMMouseScroll" - ) { - e.eventType = "mousewheel"; - if (e.type == "wheel") { - e.wheel = -e.deltaY; - } else { - e.wheel = - e.wheelDeltaY != null ? e.wheelDeltaY : e.detail * -60; - } - - //from stack overflow - e.delta = e.wheelDelta - ? e.wheelDelta / 40 - : e.deltaY - ? -e.deltaY / 3 - : 0; - this.changeDeltaScale(1.0 + e.delta * 0.05); - } - - this.last_mouse[0] = x; - this.last_mouse[1] = y; - - e.preventDefault(); - e.stopPropagation(); - return false; - }; - - DragAndScale.prototype.toCanvasContext = function(ctx) { - ctx.scale(this.scale, this.scale); - ctx.translate(this.offset[0], this.offset[1]); - }; - - DragAndScale.prototype.convertOffsetToCanvas = function(pos) { - //return [pos[0] / this.scale - this.offset[0], pos[1] / this.scale - this.offset[1]]; - return [ - (pos[0] + this.offset[0]) * this.scale, - (pos[1] + this.offset[1]) * this.scale - ]; - }; - - DragAndScale.prototype.convertCanvasToOffset = function(pos, out) { - out = out || [0, 0]; - out[0] = pos[0] / this.scale - this.offset[0]; - out[1] = pos[1] / this.scale - this.offset[1]; - return out; - }; - - DragAndScale.prototype.mouseDrag = function(x, y) { - this.offset[0] += x / this.scale; - this.offset[1] += y / this.scale; - - if (this.onredraw) { - this.onredraw(this); - } - }; - - DragAndScale.prototype.changeScale = function(value, zooming_center) { - if (value < this.min_scale) { - value = this.min_scale; - } else if (value > this.max_scale) { - value = this.max_scale; - } - - if (value == this.scale) { - return; - } - - if (!this.element) { - return; - } - - var rect = this.element.getBoundingClientRect(); - if (!rect) { - return; - } - - zooming_center = zooming_center || [ - rect.width * 0.5, - rect.height * 0.5 - ]; - var center = this.convertCanvasToOffset(zooming_center); - this.scale = value; - if (Math.abs(this.scale - 1) < 0.01) { - this.scale = 1; - } - - var new_center = this.convertCanvasToOffset(zooming_center); - var delta_offset = [ - new_center[0] - center[0], - new_center[1] - center[1] - ]; - - this.offset[0] += delta_offset[0]; - this.offset[1] += delta_offset[1]; - - if (this.onredraw) { - this.onredraw(this); - } - }; - - DragAndScale.prototype.changeDeltaScale = function(value, zooming_center) { - this.changeScale(this.scale * value, zooming_center); - }; - - DragAndScale.prototype.reset = function() { - this.scale = 1; - this.offset[0] = 0; - this.offset[1] = 0; - }; - - //********************************************************************************* - // LGraphCanvas: LGraph renderer CLASS - //********************************************************************************* - - /** - * This class is in charge of rendering one graph inside a canvas. And provides all the interaction required. - * Valid callbacks are: onNodeSelected, onNodeDeselected, onShowNodePanel, onNodeDblClicked - * - * @class LGraphCanvas - * @constructor - * @param {HTMLCanvas} canvas the canvas where you want to render (it accepts a selector in string format or the canvas element itself) - * @param {LGraph} graph [optional] - * @param {Object} options [optional] { skip_rendering, autoresize } - */ - function LGraphCanvas(canvas, graph, options) { - options = options || {}; - - //if(graph === undefined) - // throw ("No graph assigned"); - this.background_image = - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; - - if (canvas && canvas.constructor === String) { - canvas = document.querySelector(canvas); - } - - this.ds = new DragAndScale(); - this.zoom_modify_alpha = true; //otherwise it generates ugly patterns when scaling down too much - - this.title_text_font = "" + LiteGraph.NODE_TEXT_SIZE + "px Arial"; - this.inner_text_font = - "normal " + LiteGraph.NODE_SUBTEXT_SIZE + "px Arial"; - this.node_title_color = LiteGraph.NODE_TITLE_COLOR; - this.default_link_color = LiteGraph.LINK_COLOR; - this.default_connection_color = { - input_off: "#778", - input_on: "#7F7", - output_off: "#778", - output_on: "#7F7" - }; - - this.highquality_render = true; - this.use_gradients = false; //set to true to render titlebar with gradients - this.editor_alpha = 1; //used for transition - this.pause_rendering = false; - this.clear_background = true; - - this.read_only = false; //if set to true users cannot modify the graph - this.render_only_selected = true; - this.live_mode = false; - this.show_info = true; - this.allow_dragcanvas = true; - this.allow_dragnodes = true; - this.allow_interaction = true; //allow to control widgets, buttons, collapse, etc - this.allow_searchbox = true; - this.allow_reconnect_links = false; //allows to change a connection with having to redo it again - - this.drag_mode = false; - this.dragging_rectangle = null; - - this.filter = null; //allows to filter to only accept some type of nodes in a graph - - this.always_render_background = false; - this.render_shadows = true; - this.render_canvas_border = true; - this.render_connections_shadows = false; //too much cpu - this.render_connections_border = true; - this.render_curved_connections = false; - this.render_connection_arrows = false; - this.render_collapsed_slots = true; - this.render_execution_order = false; - this.render_title_colored = true; - this.render_link_tooltip = true; - - this.links_render_mode = LiteGraph.SPLINE_LINK; - - this.canvas_mouse = [0, 0]; //mouse in canvas graph coordinates, where 0,0 is the top-left corner of the blue rectangle - - //to personalize the search box - this.onSearchBox = null; - this.onSearchBoxSelection = null; - - //callbacks - this.onMouse = null; - this.onDrawBackground = null; //to render background objects (behind nodes and connections) in the canvas affected by transform - this.onDrawForeground = null; //to render foreground objects (above nodes and connections) in the canvas affected by transform - this.onDrawOverlay = null; //to render foreground objects not affected by transform (for GUIs) - this.onDrawLinkTooltip = null; //called when rendering a tooltip - this.onNodeMoved = null; //called after moving a node - this.onSelectionChange = null; //called if the selection changes - - this.connections_width = 3; - this.round_radius = 8; - - this.current_node = null; - this.node_widget = null; //used for widgets - this.over_link_center = null; - this.last_mouse_position = [0, 0]; - this.visible_area = this.ds.visible_area; - this.visible_links = []; - - //link canvas and graph - if (graph) { - graph.attachCanvas(this); - } - - this.setCanvas(canvas); - this.clear(); - - if (!options.skip_render) { - this.startRendering(); - } - - this.autoresize = options.autoresize; - } - - global.LGraphCanvas = LiteGraph.LGraphCanvas = LGraphCanvas; - - LGraphCanvas.link_type_colors = { - "-1": LiteGraph.EVENT_LINK_COLOR, - number: "#AAA", - node: "#DCA" - }; - LGraphCanvas.gradients = {}; //cache of gradients - - /** - * clears all the data inside - * - * @method clear - */ - LGraphCanvas.prototype.clear = function() { - this.frame = 0; - this.last_draw_time = 0; - this.render_time = 0; - this.fps = 0; - - //this.scale = 1; - //this.offset = [0,0]; - - this.dragging_rectangle = null; - - this.selected_nodes = {}; - this.selected_group = null; - - this.visible_nodes = []; - this.node_dragged = null; - this.node_over = null; - this.node_capturing_input = null; - this.connecting_node = null; - this.highlighted_links = {}; - - this.dirty_canvas = true; - this.dirty_bgcanvas = true; - this.dirty_area = null; - - this.node_in_panel = null; - this.node_widget = null; - - this.last_mouse = [0, 0]; - this.last_mouseclick = 0; - this.visible_area.set([0, 0, 0, 0]); - - if (this.onClear) { - this.onClear(); - } - }; - - /** - * assigns a graph, you can reassign graphs to the same canvas - * - * @method setGraph - * @param {LGraph} graph - */ - LGraphCanvas.prototype.setGraph = function(graph, skip_clear) { - if (this.graph == graph) { - return; - } - - if (!skip_clear) { - this.clear(); - } - - if (!graph && this.graph) { - this.graph.detachCanvas(this); - return; - } - - graph.attachCanvas(this); - - //remove the graph stack in case a subgraph was open - if (this._graph_stack) - this._graph_stack = null; - - this.setDirty(true, true); - }; - - /** - * opens a graph contained inside a node in the current graph - * - * @method openSubgraph - * @param {LGraph} graph - */ - LGraphCanvas.prototype.openSubgraph = function(graph) { - if (!graph) { - throw "graph cannot be null"; - } - - if (this.graph == graph) { - throw "graph cannot be the same"; - } - - this.clear(); - - if (this.graph) { - if (!this._graph_stack) { - this._graph_stack = []; - } - this._graph_stack.push(this.graph); - } - - graph.attachCanvas(this); - this.setDirty(true, true); - }; - - /** - * closes a subgraph contained inside a node - * - * @method closeSubgraph - * @param {LGraph} assigns a graph - */ - LGraphCanvas.prototype.closeSubgraph = function() { - if (!this._graph_stack || this._graph_stack.length == 0) { - return; - } - var subgraph_node = this.graph._subgraph_node; - var graph = this._graph_stack.pop(); - this.selected_nodes = {}; - this.highlighted_links = {}; - graph.attachCanvas(this); - this.setDirty(true, true); - if (subgraph_node) { - this.centerOnNode(subgraph_node); - this.selectNodes([subgraph_node]); - } - }; - - /** - * returns the visualy active graph (in case there are more in the stack) - * @method getCurrentGraph - * @return {LGraph} the active graph - */ - LGraphCanvas.prototype.getCurrentGraph = function() { - return this.graph; - }; - - /** - * assigns a canvas - * - * @method setCanvas - * @param {Canvas} assigns a canvas (also accepts the ID of the element (not a selector) - */ - LGraphCanvas.prototype.setCanvas = function(canvas, skip_events) { - var that = this; - - if (canvas) { - if (canvas.constructor === String) { - canvas = document.getElementById(canvas); - if (!canvas) { - throw "Error creating LiteGraph canvas: Canvas not found"; - } - } - } - - if (canvas === this.canvas) { - return; - } - - if (!canvas && this.canvas) { - //maybe detach events from old_canvas - if (!skip_events) { - this.unbindEvents(); - } - } - - this.canvas = canvas; - this.ds.element = canvas; - - if (!canvas) { - return; - } - - //this.canvas.tabindex = "1000"; - canvas.className += " lgraphcanvas"; - canvas.data = this; - canvas.tabindex = "1"; //to allow key events - - //bg canvas: used for non changing stuff - this.bgcanvas = null; - if (!this.bgcanvas) { - this.bgcanvas = document.createElement("canvas"); - this.bgcanvas.width = this.canvas.width; - this.bgcanvas.height = this.canvas.height; - } - - if (canvas.getContext == null) { - if (canvas.localName != "canvas") { - throw "Element supplied for LGraphCanvas must be a element, you passed a " + - canvas.localName; - } - throw "This browser doesn't support Canvas"; - } - - var ctx = (this.ctx = canvas.getContext("2d")); - if (ctx == null) { - if (!canvas.webgl_enabled) { - console.warn( - "This canvas seems to be WebGL, enabling WebGL renderer" - ); - } - this.enableWebGL(); - } - - //input: (move and up could be unbinded) - this._mousemove_callback = this.processMouseMove.bind(this); - this._mouseup_callback = this.processMouseUp.bind(this); - - if (!skip_events) { - this.bindEvents(); - } - }; - - //used in some events to capture them - LGraphCanvas.prototype._doNothing = function doNothing(e) { - e.preventDefault(); - return false; - }; - LGraphCanvas.prototype._doReturnTrue = function doNothing(e) { - e.preventDefault(); - return true; - }; - - /** - * binds mouse, keyboard, touch and drag events to the canvas - * @method bindEvents - **/ - LGraphCanvas.prototype.bindEvents = function() { - if (this._events_binded) { - console.warn("LGraphCanvas: events already binded"); - return; - } - - var canvas = this.canvas; - var ref_window = this.getCanvasWindow(); - var document = ref_window.document; //hack used when moving canvas between windows - - this._mousedown_callback = this.processMouseDown.bind(this); - this._mousewheel_callback = this.processMouseWheel.bind(this); - - canvas.addEventListener("mousedown", this._mousedown_callback, true); //down do not need to store the binded - canvas.addEventListener("mousemove", this._mousemove_callback); - canvas.addEventListener("mousewheel", this._mousewheel_callback, false); - - canvas.addEventListener("contextmenu", this._doNothing); - canvas.addEventListener( - "DOMMouseScroll", - this._mousewheel_callback, - false - ); - - //touch events - //if( 'touchstart' in document.documentElement ) - { - canvas.addEventListener("touchstart", this.touchHandler, true); - canvas.addEventListener("touchmove", this.touchHandler, true); - canvas.addEventListener("touchend", this.touchHandler, true); - canvas.addEventListener("touchcancel", this.touchHandler, true); - } - - //Keyboard ****************** - this._key_callback = this.processKey.bind(this); - - canvas.addEventListener("keydown", this._key_callback, true); - document.addEventListener("keyup", this._key_callback, true); //in document, otherwise it doesn't fire keyup - - //Dropping Stuff over nodes ************************************ - this._ondrop_callback = this.processDrop.bind(this); - - canvas.addEventListener("dragover", this._doNothing, false); - canvas.addEventListener("dragend", this._doNothing, false); - canvas.addEventListener("drop", this._ondrop_callback, false); - canvas.addEventListener("dragenter", this._doReturnTrue, false); - - this._events_binded = true; - }; - - /** - * unbinds mouse events from the canvas - * @method unbindEvents - **/ - LGraphCanvas.prototype.unbindEvents = function() { - if (!this._events_binded) { - console.warn("LGraphCanvas: no events binded"); - return; - } - - var ref_window = this.getCanvasWindow(); - var document = ref_window.document; - - this.canvas.removeEventListener("mousedown", this._mousedown_callback); - this.canvas.removeEventListener( - "mousewheel", - this._mousewheel_callback - ); - this.canvas.removeEventListener( - "DOMMouseScroll", - this._mousewheel_callback - ); - this.canvas.removeEventListener("keydown", this._key_callback); - document.removeEventListener("keyup", this._key_callback); - this.canvas.removeEventListener("contextmenu", this._doNothing); - this.canvas.removeEventListener("drop", this._ondrop_callback); - this.canvas.removeEventListener("dragenter", this._doReturnTrue); - - this.canvas.removeEventListener("touchstart", this.touchHandler); - this.canvas.removeEventListener("touchmove", this.touchHandler); - this.canvas.removeEventListener("touchend", this.touchHandler); - this.canvas.removeEventListener("touchcancel", this.touchHandler); - - this._mousedown_callback = null; - this._mousewheel_callback = null; - this._key_callback = null; - this._ondrop_callback = null; - - this._events_binded = false; - }; - - LGraphCanvas.getFileExtension = function(url) { - var question = url.indexOf("?"); - if (question != -1) { - url = url.substr(0, question); - } - var point = url.lastIndexOf("."); - if (point == -1) { - return ""; - } - return url.substr(point + 1).toLowerCase(); - }; - - /** - * this function allows to render the canvas using WebGL instead of Canvas2D - * this is useful if you plant to render 3D objects inside your nodes, it uses litegl.js for webgl and canvas2DtoWebGL to emulate the Canvas2D calls in webGL - * @method enableWebGL - **/ - LGraphCanvas.prototype.enableWebGL = function() { - if (typeof GL === undefined) { - throw "litegl.js must be included to use a WebGL canvas"; - } - if (typeof enableWebGLCanvas === undefined) { - throw "webglCanvas.js must be included to use this feature"; - } - - this.gl = this.ctx = enableWebGLCanvas(this.canvas); - this.ctx.webgl = true; - this.bgcanvas = this.canvas; - this.bgctx = this.gl; - this.canvas.webgl_enabled = true; - - /* - GL.create({ canvas: this.bgcanvas }); - this.bgctx = enableWebGLCanvas( this.bgcanvas ); - window.gl = this.gl; - */ - }; - - /** - * marks as dirty the canvas, this way it will be rendered again - * - * @class LGraphCanvas - * @method setDirty - * @param {bool} fgcanvas if the foreground canvas is dirty (the one containing the nodes) - * @param {bool} bgcanvas if the background canvas is dirty (the one containing the wires) - */ - LGraphCanvas.prototype.setDirty = function(fgcanvas, bgcanvas) { - if (fgcanvas) { - this.dirty_canvas = true; - } - if (bgcanvas) { - this.dirty_bgcanvas = true; - } - }; - - /** - * Used to attach the canvas in a popup - * - * @method getCanvasWindow - * @return {window} returns the window where the canvas is attached (the DOM root node) - */ - LGraphCanvas.prototype.getCanvasWindow = function() { - if (!this.canvas) { - return window; - } - var doc = this.canvas.ownerDocument; - return doc.defaultView || doc.parentWindow; - }; - - /** - * starts rendering the content of the canvas when needed - * - * @method startRendering - */ - LGraphCanvas.prototype.startRendering = function() { - if (this.is_rendering) { - return; - } //already rendering - - this.is_rendering = true; - renderFrame.call(this); - - function renderFrame() { - if (!this.pause_rendering) { - this.draw(); - } - - var window = this.getCanvasWindow(); - if (this.is_rendering) { - window.requestAnimationFrame(renderFrame.bind(this)); - } - } - }; - - /** - * stops rendering the content of the canvas (to save resources) - * - * @method stopRendering - */ - LGraphCanvas.prototype.stopRendering = function() { - this.is_rendering = false; - /* - if(this.rendering_timer_id) - { - clearInterval(this.rendering_timer_id); - this.rendering_timer_id = null; - } - */ - }; - - /* LiteGraphCanvas input */ - - LGraphCanvas.prototype.processMouseDown = function(e) { - if (!this.graph) { - return; - } - - this.adjustMouseEvent(e); - - var ref_window = this.getCanvasWindow(); - var document = ref_window.document; - LGraphCanvas.active_canvas = this; - var that = this; - - //move mouse move event to the window in case it drags outside of the canvas - this.canvas.removeEventListener("mousemove", this._mousemove_callback); - ref_window.document.addEventListener( - "mousemove", - this._mousemove_callback, - true - ); //catch for the entire window - ref_window.document.addEventListener( - "mouseup", - this._mouseup_callback, - true - ); - - var node = this.graph.getNodeOnPos( - e.canvasX, - e.canvasY, - this.visible_nodes, - 5 - ); - var skip_dragging = false; - var skip_action = false; - var now = LiteGraph.getTime(); - var is_double_click = now - this.last_mouseclick < 300; - - this.canvas_mouse[0] = e.canvasX; - this.canvas_mouse[1] = e.canvasY; - this.canvas.focus(); - - LiteGraph.closeAllContextMenus(ref_window); - - if (this.onMouse) { - if (this.onMouse(e) == true) { - return; - } - } - - if (e.which == 1) { - //left button mouse - if (e.ctrlKey) { - this.dragging_rectangle = new Float32Array(4); - this.dragging_rectangle[0] = e.canvasX; - this.dragging_rectangle[1] = e.canvasY; - this.dragging_rectangle[2] = 1; - this.dragging_rectangle[3] = 1; - skip_action = true; - } - - var clicking_canvas_bg = false; - - //when clicked on top of a node - //and it is not interactive - if (node && this.allow_interaction && !skip_action && !this.read_only) { - if (!this.live_mode && !node.flags.pinned) { - this.bringToFront(node); - } //if it wasn't selected? - - //not dragging mouse to connect two slots - if ( - !this.connecting_node && - !node.flags.collapsed && - !this.live_mode - ) { - //Search for corner for resize - if ( - !skip_action && - node.resizable !== false && - isInsideRectangle( - e.canvasX, - e.canvasY, - node.pos[0] + node.size[0] - 5, - node.pos[1] + node.size[1] - 5, - 10, - 10 - ) - ) { - this.resizing_node = node; - this.canvas.style.cursor = "se-resize"; - skip_action = true; - } else { - //search for outputs - if (node.outputs) { - for ( - var i = 0, l = node.outputs.length; - i < l; - ++i - ) { - var output = node.outputs[i]; - var link_pos = node.getConnectionPos(false, i); - if ( - isInsideRectangle( - e.canvasX, - e.canvasY, - link_pos[0] - 15, - link_pos[1] - 10, - 30, - 20 - ) - ) { - this.connecting_node = node; - this.connecting_output = output; - this.connecting_pos = node.getConnectionPos( false, i ); - this.connecting_slot = i; - - if (e.shiftKey) { - node.disconnectOutput(i); - } - - if (is_double_click) { - if (node.onOutputDblClick) { - node.onOutputDblClick(i, e); - } - } else { - if (node.onOutputClick) { - node.onOutputClick(i, e); - } - } - - skip_action = true; - break; - } - } - } - - //search for inputs - if (node.inputs) { - for ( - var i = 0, l = node.inputs.length; - i < l; - ++i - ) { - var input = node.inputs[i]; - var link_pos = node.getConnectionPos(true, i); - if ( - isInsideRectangle( - e.canvasX, - e.canvasY, - link_pos[0] - 15, - link_pos[1] - 10, - 30, - 20 - ) - ) { - if (is_double_click) { - if (node.onInputDblClick) { - node.onInputDblClick(i, e); - } - } else { - if (node.onInputClick) { - node.onInputClick(i, e); - } - } - - if (input.link !== null) { - var link_info = this.graph.links[ - input.link - ]; //before disconnecting - node.disconnectInput(i); - - if ( - this.allow_reconnect_links || - e.shiftKey - ) { - this.connecting_node = this.graph._nodes_by_id[ - link_info.origin_id - ]; - this.connecting_slot = - link_info.origin_slot; - this.connecting_output = this.connecting_node.outputs[ - this.connecting_slot - ]; - this.connecting_pos = this.connecting_node.getConnectionPos( false, this.connecting_slot ); - } - - this.dirty_bgcanvas = true; - skip_action = true; - } - } - } - } - } //not resizing - } - - //Search for corner for collapsing - /* - if( !skip_action && isInsideRectangle( e.canvasX, e.canvasY, node.pos[0], node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT )) - { - node.collapse(); - skip_action = true; - } - */ - - //it wasn't clicked on the links boxes - if (!skip_action) { - var block_drag_node = false; - - //widgets - var widget = this.processNodeWidgets( node, this.canvas_mouse, e ); - if (widget) { - block_drag_node = true; - this.node_widget = [node, widget]; - } - - //double clicking - if (is_double_click && this.selected_nodes[node.id]) { - //double click node - if (node.onDblClick) { - node.onDblClick( e, [ e.canvasX - node.pos[0], e.canvasY - node.pos[1] ], this ); - } - this.processNodeDblClicked(node); - block_drag_node = true; - } - - //if do not capture mouse - if ( node.onMouseDown && node.onMouseDown( e, [e.canvasX - node.pos[0], e.canvasY - node.pos[1]], this ) ) { - block_drag_node = true; - } else if (this.live_mode) { - clicking_canvas_bg = true; - block_drag_node = true; - } - - if (!block_drag_node) { - if (this.allow_dragnodes) { - this.node_dragged = node; - } - if (!this.selected_nodes[node.id]) { - this.processNodeSelected(node, e); - } - } - - this.dirty_canvas = true; - } - } //clicked outside of nodes - else { - //search for link connector - if(!this.read_only) - for (var i = 0; i < this.visible_links.length; ++i) { - var link = this.visible_links[i]; - var center = link._pos; - if ( - !center || - e.canvasX < center[0] - 4 || - e.canvasX > center[0] + 4 || - e.canvasY < center[1] - 4 || - e.canvasY > center[1] + 4 - ) { - continue; - } - //link clicked - this.showLinkMenu(link, e); - this.over_link_center = null; //clear tooltip - break; - } - - this.selected_group = this.graph.getGroupOnPos( e.canvasX, e.canvasY ); - this.selected_group_resizing = false; - if (this.selected_group && !this.read_only ) { - if (e.ctrlKey) { - this.dragging_rectangle = null; - } - - var dist = distance( [e.canvasX, e.canvasY], [ this.selected_group.pos[0] + this.selected_group.size[0], this.selected_group.pos[1] + this.selected_group.size[1] ] ); - if (dist * this.ds.scale < 10) { - this.selected_group_resizing = true; - } else { - this.selected_group.recomputeInsideNodes(); - } - } - - if (is_double_click && !this.read_only && this.allow_searchbox) { - this.showSearchBox(e); - } - - clicking_canvas_bg = true; - } - - if (!skip_action && clicking_canvas_bg && this.allow_dragcanvas) { - this.dragging_canvas = true; - } - } else if (e.which == 2) { - //middle button - } else if (e.which == 3) { - //right button - if(!this.read_only) - this.processContextMenu(node, e); - } - - //TODO - //if(this.node_selected != prev_selected) - // this.onNodeSelectionChange(this.node_selected); - - this.last_mouse[0] = e.localX; - this.last_mouse[1] = e.localY; - this.last_mouseclick = LiteGraph.getTime(); - this.last_mouse_dragging = true; - - /* - if( (this.dirty_canvas || this.dirty_bgcanvas) && this.rendering_timer_id == null) - this.draw(); - */ - - this.graph.change(); - - //this is to ensure to defocus(blur) if a text input element is on focus - if ( - !ref_window.document.activeElement || - (ref_window.document.activeElement.nodeName.toLowerCase() != - "input" && - ref_window.document.activeElement.nodeName.toLowerCase() != - "textarea") - ) { - e.preventDefault(); - } - e.stopPropagation(); - - if (this.onMouseDown) { - this.onMouseDown(e); - } - - return false; - }; - - /** - * Called when a mouse move event has to be processed - * @method processMouseMove - **/ - LGraphCanvas.prototype.processMouseMove = function(e) { - if (this.autoresize) { - this.resize(); - } - - if (!this.graph) { - return; - } - - LGraphCanvas.active_canvas = this; - this.adjustMouseEvent(e); - var mouse = [e.localX, e.localY]; - var delta = [ - mouse[0] - this.last_mouse[0], - mouse[1] - this.last_mouse[1] - ]; - this.last_mouse = mouse; - this.canvas_mouse[0] = e.canvasX; - this.canvas_mouse[1] = e.canvasY; - e.dragging = this.last_mouse_dragging; - - if (this.node_widget) { - this.processNodeWidgets( - this.node_widget[0], - this.canvas_mouse, - e, - this.node_widget[1] - ); - this.dirty_canvas = true; - } - - if (this.dragging_rectangle) { - this.dragging_rectangle[2] = e.canvasX - this.dragging_rectangle[0]; - this.dragging_rectangle[3] = e.canvasY - this.dragging_rectangle[1]; - this.dirty_canvas = true; - } else if (this.selected_group && !this.read_only) { - //moving/resizing a group - if (this.selected_group_resizing) { - this.selected_group.size = [ - e.canvasX - this.selected_group.pos[0], - e.canvasY - this.selected_group.pos[1] - ]; - } else { - var deltax = delta[0] / this.ds.scale; - var deltay = delta[1] / this.ds.scale; - this.selected_group.move(deltax, deltay, e.ctrlKey); - if (this.selected_group._nodes.length) { - this.dirty_canvas = true; - } - } - this.dirty_bgcanvas = true; - } else if (this.dragging_canvas) { - this.ds.offset[0] += delta[0] / this.ds.scale; - this.ds.offset[1] += delta[1] / this.ds.scale; - this.dirty_canvas = true; - this.dirty_bgcanvas = true; - } else if (this.allow_interaction && !this.read_only) { - if (this.connecting_node) { - this.dirty_canvas = true; - } - - //get node over - var node = this.graph.getNodeOnPos( - e.canvasX, - e.canvasY, - this.visible_nodes - ); - - //remove mouseover flag - for (var i = 0, l = this.graph._nodes.length; i < l; ++i) { - if ( - this.graph._nodes[i].mouseOver && - node != this.graph._nodes[i] - ) { - //mouse leave - this.graph._nodes[i].mouseOver = false; - if (this.node_over && this.node_over.onMouseLeave) { - this.node_over.onMouseLeave(e); - } - this.node_over = null; - this.dirty_canvas = true; - } - } - - //mouse over a node - if (node) { - //this.canvas.style.cursor = "move"; - if (!node.mouseOver) { - //mouse enter - node.mouseOver = true; - this.node_over = node; - this.dirty_canvas = true; - - if (node.onMouseEnter) { - node.onMouseEnter(e); - } - } - - //in case the node wants to do something - if (node.onMouseMove) { - node.onMouseMove( - e, - [e.canvasX - node.pos[0], e.canvasY - node.pos[1]], - this - ); - } - - //if dragging a link - if (this.connecting_node) { - var pos = this._highlight_input || [0, 0]; //to store the output of isOverNodeInput - - //on top of input - if (this.isOverNodeBox(node, e.canvasX, e.canvasY)) { - //mouse on top of the corner box, don't know what to do - } else { - //check if I have a slot below de mouse - var slot = this.isOverNodeInput( - node, - e.canvasX, - e.canvasY, - pos - ); - if (slot != -1 && node.inputs[slot]) { - var slot_type = node.inputs[slot].type; - if ( - LiteGraph.isValidConnection( - this.connecting_output.type, - slot_type - ) - ) { - this._highlight_input = pos; - } - } else { - this._highlight_input = null; - } - } - } - - //Search for corner - if (this.canvas) { - if ( - isInsideRectangle( - e.canvasX, - e.canvasY, - node.pos[0] + node.size[0] - 5, - node.pos[1] + node.size[1] - 5, - 5, - 5 - ) - ) { - this.canvas.style.cursor = "se-resize"; - } else { - this.canvas.style.cursor = "crosshair"; - } - } - } else { //outside - - //search for link connector - var over_link = null; - for (var i = 0; i < this.visible_links.length; ++i) { - var link = this.visible_links[i]; - var center = link._pos; - if ( - !center || - e.canvasX < center[0] - 4 || - e.canvasX > center[0] + 4 || - e.canvasY < center[1] - 4 || - e.canvasY > center[1] + 4 - ) { - continue; - } - over_link = link; - break; - } - if( over_link != this.over_link_center ) - { - this.over_link_center = over_link; - this.dirty_canvas = true; - } - - if (this.canvas) { - this.canvas.style.cursor = ""; - } - } - - if ( this.node_capturing_input && this.node_capturing_input != node && this.node_capturing_input.onMouseMove ) { - this.node_capturing_input.onMouseMove(e,[e.canvasX - this.node_capturing_input.pos[0],e.canvasY - this.node_capturing_input.pos[1]], this); - } - - if (this.node_dragged && !this.live_mode) { - for (var i in this.selected_nodes) { - var n = this.selected_nodes[i]; - n.pos[0] += delta[0] / this.ds.scale; - n.pos[1] += delta[1] / this.ds.scale; - } - - this.dirty_canvas = true; - this.dirty_bgcanvas = true; - } - - if (this.resizing_node && !this.live_mode) { - //convert mouse to node space - this.resizing_node.size[0] = e.canvasX - this.resizing_node.pos[0]; - this.resizing_node.size[1] = e.canvasY - this.resizing_node.pos[1]; - - //constraint size - var max_slots = Math.max( - this.resizing_node.inputs ? this.resizing_node.inputs.length : 0, - this.resizing_node.outputs ? this.resizing_node.outputs.length : 0 - ); - var min_height = - max_slots * LiteGraph.NODE_SLOT_HEIGHT + - (this.resizing_node.widgets ? this.resizing_node.widgets.length : 0) * (LiteGraph.NODE_WIDGET_HEIGHT + 4) + 4; - if (this.resizing_node.size[1] < min_height) { - this.resizing_node.size[1] = min_height; - } - if (this.resizing_node.size[0] < LiteGraph.NODE_MIN_WIDTH) { - this.resizing_node.size[0] = LiteGraph.NODE_MIN_WIDTH; - } - - if (this.resizing_node.onResize) { - this.resizing_node.onResize(this.resizing_node.size); - } - - this.canvas.style.cursor = "se-resize"; - this.dirty_canvas = true; - this.dirty_bgcanvas = true; - } - } - - e.preventDefault(); - return false; - }; - - /** - * Called when a mouse up event has to be processed - * @method processMouseUp - **/ - LGraphCanvas.prototype.processMouseUp = function(e) { - if (!this.graph) { - return; - } - - var window = this.getCanvasWindow(); - var document = window.document; - LGraphCanvas.active_canvas = this; - - //restore the mousemove event back to the canvas - document.removeEventListener("mousemove",this._mousemove_callback,true); - this.canvas.addEventListener("mousemove",this._mousemove_callback,true); - document.removeEventListener("mouseup", this._mouseup_callback, true); - - this.adjustMouseEvent(e); - var now = LiteGraph.getTime(); - e.click_time = now - this.last_mouseclick; - this.last_mouse_dragging = false; - - if (e.which == 1) { - - if( this.node_widget ) - { - this.processNodeWidgets( this.node_widget[0], this.canvas_mouse, e ); - } - - //left button - this.node_widget = null; - - if (this.selected_group) { - var diffx = - this.selected_group.pos[0] - - Math.round(this.selected_group.pos[0]); - var diffy = - this.selected_group.pos[1] - - Math.round(this.selected_group.pos[1]); - this.selected_group.move(diffx, diffy, e.ctrlKey); - this.selected_group.pos[0] = Math.round( - this.selected_group.pos[0] - ); - this.selected_group.pos[1] = Math.round( - this.selected_group.pos[1] - ); - if (this.selected_group._nodes.length) { - this.dirty_canvas = true; - } - this.selected_group = null; - } - this.selected_group_resizing = false; - - if (this.dragging_rectangle) { - if (this.graph) { - var nodes = this.graph._nodes; - var node_bounding = new Float32Array(4); - this.deselectAllNodes(); - //compute bounding and flip if left to right - var w = Math.abs(this.dragging_rectangle[2]); - var h = Math.abs(this.dragging_rectangle[3]); - var startx = - this.dragging_rectangle[2] < 0 - ? this.dragging_rectangle[0] - w - : this.dragging_rectangle[0]; - var starty = - this.dragging_rectangle[3] < 0 - ? this.dragging_rectangle[1] - h - : this.dragging_rectangle[1]; - this.dragging_rectangle[0] = startx; - this.dragging_rectangle[1] = starty; - this.dragging_rectangle[2] = w; - this.dragging_rectangle[3] = h; - - //test against all nodes (not visible because the rectangle maybe start outside - var to_select = []; - for (var i = 0; i < nodes.length; ++i) { - var node = nodes[i]; - node.getBounding(node_bounding); - if ( - !overlapBounding( - this.dragging_rectangle, - node_bounding - ) - ) { - continue; - } //out of the visible area - to_select.push(node); - } - if (to_select.length) { - this.selectNodes(to_select); - } - } - this.dragging_rectangle = null; - } else if (this.connecting_node) { - //dragging a connection - this.dirty_canvas = true; - this.dirty_bgcanvas = true; - - var node = this.graph.getNodeOnPos( - e.canvasX, - e.canvasY, - this.visible_nodes - ); - - //node below mouse - if (node) { - if ( - this.connecting_output.type == LiteGraph.EVENT && - this.isOverNodeBox(node, e.canvasX, e.canvasY) - ) { - this.connecting_node.connect( - this.connecting_slot, - node, - LiteGraph.EVENT - ); - } else { - //slot below mouse? connect - var slot = this.isOverNodeInput( - node, - e.canvasX, - e.canvasY - ); - if (slot != -1) { - this.connecting_node.connect( - this.connecting_slot, - node, - slot - ); - } else { - //not on top of an input - var input = node.getInputInfo(0); - //auto connect - if ( - this.connecting_output.type == LiteGraph.EVENT - ) { - this.connecting_node.connect( - this.connecting_slot, - node, - LiteGraph.EVENT - ); - } else if ( - input && - !input.link && - LiteGraph.isValidConnection( - input.type && this.connecting_output.type - ) - ) { - this.connecting_node.connect( - this.connecting_slot, - node, - 0 - ); - } - } - } - } - - this.connecting_output = null; - this.connecting_pos = null; - this.connecting_node = null; - this.connecting_slot = -1; - } //not dragging connection - else if (this.resizing_node) { - this.dirty_canvas = true; - this.dirty_bgcanvas = true; - this.resizing_node = null; - } else if (this.node_dragged) { - //node being dragged? - var node = this.node_dragged; - if ( - node && - e.click_time < 300 && - isInsideRectangle( e.canvasX, e.canvasY, node.pos[0], node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT ) - ) { - node.collapse(); - } - - this.dirty_canvas = true; - this.dirty_bgcanvas = true; - this.node_dragged.pos[0] = Math.round(this.node_dragged.pos[0]); - this.node_dragged.pos[1] = Math.round(this.node_dragged.pos[1]); - if (this.graph.config.align_to_grid) { - this.node_dragged.alignToGrid(); - } - if( this.onNodeMoved ) - this.onNodeMoved( this.node_dragged ); - this.node_dragged = null; - } //no node being dragged - else { - //get node over - var node = this.graph.getNodeOnPos( - e.canvasX, - e.canvasY, - this.visible_nodes - ); - - if (!node && e.click_time < 300) { - this.deselectAllNodes(); - } - - this.dirty_canvas = true; - this.dragging_canvas = false; - - if (this.node_over && this.node_over.onMouseUp) { - this.node_over.onMouseUp( e, [ e.canvasX - this.node_over.pos[0], e.canvasY - this.node_over.pos[1] ], this ); - } - if ( - this.node_capturing_input && - this.node_capturing_input.onMouseUp - ) { - this.node_capturing_input.onMouseUp(e, [ - e.canvasX - this.node_capturing_input.pos[0], - e.canvasY - this.node_capturing_input.pos[1] - ]); - } - } - } else if (e.which == 2) { - //middle button - //trace("middle"); - this.dirty_canvas = true; - this.dragging_canvas = false; - } else if (e.which == 3) { - //right button - //trace("right"); - this.dirty_canvas = true; - this.dragging_canvas = false; - } - - /* - if((this.dirty_canvas || this.dirty_bgcanvas) && this.rendering_timer_id == null) - this.draw(); - */ - - this.graph.change(); - - e.stopPropagation(); - e.preventDefault(); - return false; - }; - - /** - * Called when a mouse wheel event has to be processed - * @method processMouseWheel - **/ - LGraphCanvas.prototype.processMouseWheel = function(e) { - if (!this.graph || !this.allow_dragcanvas) { - return; - } - - var delta = e.wheelDeltaY != null ? e.wheelDeltaY : e.detail * -60; - - this.adjustMouseEvent(e); - - var scale = this.ds.scale; - - if (delta > 0) { - scale *= 1.1; - } else if (delta < 0) { - scale *= 1 / 1.1; - } - - //this.setZoom( scale, [ e.localX, e.localY ] ); - this.ds.changeScale(scale, [e.localX, e.localY]); - - this.graph.change(); - - e.preventDefault(); - return false; // prevent default - }; - - /** - * returns true if a position (in graph space) is on top of a node little corner box - * @method isOverNodeBox - **/ - LGraphCanvas.prototype.isOverNodeBox = function(node, canvasx, canvasy) { - var title_height = LiteGraph.NODE_TITLE_HEIGHT; - if ( - isInsideRectangle( - canvasx, - canvasy, - node.pos[0] + 2, - node.pos[1] + 2 - title_height, - title_height - 4, - title_height - 4 - ) - ) { - return true; - } - return false; - }; - - /** - * returns true if a position (in graph space) is on top of a node input slot - * @method isOverNodeInput - **/ - LGraphCanvas.prototype.isOverNodeInput = function( - node, - canvasx, - canvasy, - slot_pos - ) { - if (node.inputs) { - for (var i = 0, l = node.inputs.length; i < l; ++i) { - var input = node.inputs[i]; - var link_pos = node.getConnectionPos(true, i); - var is_inside = false; - if (node.horizontal) { - is_inside = isInsideRectangle( - canvasx, - canvasy, - link_pos[0] - 5, - link_pos[1] - 10, - 10, - 20 - ); - } else { - is_inside = isInsideRectangle( - canvasx, - canvasy, - link_pos[0] - 10, - link_pos[1] - 5, - 40, - 10 - ); - } - if (is_inside) { - if (slot_pos) { - slot_pos[0] = link_pos[0]; - slot_pos[1] = link_pos[1]; - } - return i; - } - } - } - return -1; - }; - - /** - * process a key event - * @method processKey - **/ - LGraphCanvas.prototype.processKey = function(e) { - if (!this.graph) { - return; - } - - var block_default = false; - //console.log(e); //debug - - if (e.target.localName == "input") { - return; - } - - if (e.type == "keydown") { - if (e.keyCode == 32) { - //esc - this.dragging_canvas = true; - block_default = true; - } - - //select all Control A - if (e.keyCode == 65 && e.ctrlKey) { - this.selectNodes(); - block_default = true; - } - - if (e.code == "KeyC" && (e.metaKey || e.ctrlKey) && !e.shiftKey) { - //copy - if (this.selected_nodes) { - this.copyToClipboard(); - block_default = true; - } - } - - if (e.code == "KeyV" && (e.metaKey || e.ctrlKey) && !e.shiftKey) { - //paste - this.pasteFromClipboard(); - } - - //delete or backspace - if (e.keyCode == 46 || e.keyCode == 8) { - if ( - e.target.localName != "input" && - e.target.localName != "textarea" - ) { - this.deleteSelectedNodes(); - block_default = true; - } - } - - //collapse - //... - - //TODO - if (this.selected_nodes) { - for (var i in this.selected_nodes) { - if (this.selected_nodes[i].onKeyDown) { - this.selected_nodes[i].onKeyDown(e); - } - } - } - } else if (e.type == "keyup") { - if (e.keyCode == 32) { - this.dragging_canvas = false; - } - - if (this.selected_nodes) { - for (var i in this.selected_nodes) { - if (this.selected_nodes[i].onKeyUp) { - this.selected_nodes[i].onKeyUp(e); - } - } - } - } - - this.graph.change(); - - if (block_default) { - e.preventDefault(); - e.stopImmediatePropagation(); - return false; - } - }; - - LGraphCanvas.prototype.copyToClipboard = function() { - var clipboard_info = { - nodes: [], - links: [] - }; - var index = 0; - var selected_nodes_array = []; - for (var i in this.selected_nodes) { - var node = this.selected_nodes[i]; - node._relative_id = index; - selected_nodes_array.push(node); - index += 1; - } - - for (var i = 0; i < selected_nodes_array.length; ++i) { - var node = selected_nodes_array[i]; - var cloned = node.clone(); - if(!cloned) - { - console.warn("node type not found: " + node.type ); - continue; - } - clipboard_info.nodes.push(cloned.serialize()); - if (node.inputs && node.inputs.length) { - for (var j = 0; j < node.inputs.length; ++j) { - var input = node.inputs[j]; - if (!input || input.link == null) { - continue; - } - var link_info = this.graph.links[input.link]; - if (!link_info) { - continue; - } - var target_node = this.graph.getNodeById( - link_info.origin_id - ); - if (!target_node || !this.selected_nodes[target_node.id]) { - //improve this by allowing connections to non-selected nodes - continue; - } //not selected - clipboard_info.links.push([ - target_node._relative_id, - link_info.origin_slot, //j, - node._relative_id, - link_info.target_slot - ]); - } - } - } - localStorage.setItem( - "litegrapheditor_clipboard", - JSON.stringify(clipboard_info) - ); - }; - - LGraphCanvas.prototype.pasteFromClipboard = function() { - var data = localStorage.getItem("litegrapheditor_clipboard"); - if (!data) { - return; - } - - //create nodes - var clipboard_info = JSON.parse(data); - var nodes = []; - for (var i = 0; i < clipboard_info.nodes.length; ++i) { - var node_data = clipboard_info.nodes[i]; - var node = LiteGraph.createNode(node_data.type); - if (node) { - node.configure(node_data); - node.pos[0] += 5; - node.pos[1] += 5; - this.graph.add(node); - nodes.push(node); - } - } - - //create links - for (var i = 0; i < clipboard_info.links.length; ++i) { - var link_info = clipboard_info.links[i]; - var origin_node = nodes[link_info[0]]; - var target_node = nodes[link_info[2]]; - if( origin_node && target_node ) - origin_node.connect(link_info[1], target_node, link_info[3]); - else - console.warn("Warning, nodes missing on pasting"); - } - - this.selectNodes(nodes); - }; - - /** - * process a item drop event on top the canvas - * @method processDrop - **/ - LGraphCanvas.prototype.processDrop = function(e) { - e.preventDefault(); - this.adjustMouseEvent(e); - - var pos = [e.canvasX, e.canvasY]; - var node = this.graph.getNodeOnPos(pos[0], pos[1]); - - if (!node) { - var r = null; - if (this.onDropItem) { - r = this.onDropItem(event); - } - if (!r) { - this.checkDropItem(e); - } - return; - } - - if (node.onDropFile || node.onDropData) { - var files = e.dataTransfer.files; - if (files && files.length) { - for (var i = 0; i < files.length; i++) { - var file = e.dataTransfer.files[0]; - var filename = file.name; - var ext = LGraphCanvas.getFileExtension(filename); - //console.log(file); - - if (node.onDropFile) { - node.onDropFile(file); - } - - if (node.onDropData) { - //prepare reader - var reader = new FileReader(); - reader.onload = function(event) { - //console.log(event.target); - var data = event.target.result; - node.onDropData(data, filename, file); - }; - - //read data - var type = file.type.split("/")[0]; - if (type == "text" || type == "") { - reader.readAsText(file); - } else if (type == "image") { - reader.readAsDataURL(file); - } else { - reader.readAsArrayBuffer(file); - } - } - } - } - } - - if (node.onDropItem) { - if (node.onDropItem(event)) { - return true; - } - } - - if (this.onDropItem) { - return this.onDropItem(event); - } - - return false; - }; - - //called if the graph doesn't have a default drop item behaviour - LGraphCanvas.prototype.checkDropItem = function(e) { - if (e.dataTransfer.files.length) { - var file = e.dataTransfer.files[0]; - var ext = LGraphCanvas.getFileExtension(file.name).toLowerCase(); - var nodetype = LiteGraph.node_types_by_file_extension[ext]; - if (nodetype) { - var node = LiteGraph.createNode(nodetype.type); - node.pos = [e.canvasX, e.canvasY]; - this.graph.add(node); - if (node.onDropFile) { - node.onDropFile(file); - } - } - } - }; - - LGraphCanvas.prototype.processNodeDblClicked = function(n) { - if (this.onShowNodePanel) { - this.onShowNodePanel(n); - } - - if (this.onNodeDblClicked) { - this.onNodeDblClicked(n); - } - - this.setDirty(true); - }; - - LGraphCanvas.prototype.processNodeSelected = function(node, e) { - this.selectNode(node, e && e.shiftKey); - if (this.onNodeSelected) { - this.onNodeSelected(node); - } - }; - - /** - * selects a given node (or adds it to the current selection) - * @method selectNode - **/ - LGraphCanvas.prototype.selectNode = function( - node, - add_to_current_selection - ) { - if (node == null) { - this.deselectAllNodes(); - } else { - this.selectNodes([node], add_to_current_selection); - } - }; - - /** - * selects several nodes (or adds them to the current selection) - * @method selectNodes - **/ - LGraphCanvas.prototype.selectNodes = function( - nodes, - add_to_current_selection - ) { - if (!add_to_current_selection) { - this.deselectAllNodes(); - } - - nodes = nodes || this.graph._nodes; - for (var i = 0; i < nodes.length; ++i) { - var node = nodes[i]; - if (node.is_selected) { - continue; - } - - if (!node.is_selected && node.onSelected) { - node.onSelected(); - } - node.is_selected = true; - this.selected_nodes[node.id] = node; - - if (node.inputs) { - for (var j = 0; j < node.inputs.length; ++j) { - this.highlighted_links[node.inputs[j].link] = true; - } - } - if (node.outputs) { - for (var j = 0; j < node.outputs.length; ++j) { - var out = node.outputs[j]; - if (out.links) { - for (var k = 0; k < out.links.length; ++k) { - this.highlighted_links[out.links[k]] = true; - } - } - } - } - } - - if( this.onSelectionChange ) - this.onSelectionChange( this.selected_nodes ); - - this.setDirty(true); - }; - - /** - * removes a node from the current selection - * @method deselectNode - **/ - LGraphCanvas.prototype.deselectNode = function(node) { - if (!node.is_selected) { - return; - } - if (node.onDeselected) { - node.onDeselected(); - } - node.is_selected = false; - - if (this.onNodeDeselected) { - this.onNodeDeselected(node); - } - - //remove highlighted - if (node.inputs) { - for (var i = 0; i < node.inputs.length; ++i) { - delete this.highlighted_links[node.inputs[i].link]; - } - } - if (node.outputs) { - for (var i = 0; i < node.outputs.length; ++i) { - var out = node.outputs[i]; - if (out.links) { - for (var j = 0; j < out.links.length; ++j) { - delete this.highlighted_links[out.links[j]]; - } - } - } - } - }; - - /** - * removes all nodes from the current selection - * @method deselectAllNodes - **/ - LGraphCanvas.prototype.deselectAllNodes = function() { - if (!this.graph) { - return; - } - var nodes = this.graph._nodes; - for (var i = 0, l = nodes.length; i < l; ++i) { - var node = nodes[i]; - if (!node.is_selected) { - continue; - } - if (node.onDeselected) { - node.onDeselected(); - } - node.is_selected = false; - if (this.onNodeDeselected) { - this.onNodeDeselected(node); - } - } - this.selected_nodes = {}; - this.current_node = null; - this.highlighted_links = {}; - if( this.onSelectionChange ) - this.onSelectionChange( this.selected_nodes ); - this.setDirty(true); - }; - - /** - * deletes all nodes in the current selection from the graph - * @method deleteSelectedNodes - **/ - LGraphCanvas.prototype.deleteSelectedNodes = function() { - for (var i in this.selected_nodes) { - var node = this.selected_nodes[i]; - //autoconnect when possible (very basic, only takes into account first input-output) - if(node.inputs && node.inputs.length && node.outputs && node.outputs.length && LiteGraph.isValidConnection( node.inputs[0].type, node.outputs[0].type ) && node.inputs[0].link && node.outputs[0].links && node.outputs[0].links.length ) - { - var input_link = node.graph.links[ node.inputs[0].link ]; - var output_link = node.graph.links[ node.outputs[0].links[0] ]; - var input_node = node.getInputNode(0); - var output_node = node.getOutputNodes(0)[0]; - if(input_node && output_node) - input_node.connect( input_link.origin_slot, output_node, output_link.target_slot ); - } - this.graph.remove(node); - if (this.onNodeDeselected) { - this.onNodeDeselected(node); - } - } - this.selected_nodes = {}; - this.current_node = null; - this.highlighted_links = {}; - this.setDirty(true); - }; - - /** - * centers the camera on a given node - * @method centerOnNode - **/ - LGraphCanvas.prototype.centerOnNode = function(node) { - this.ds.offset[0] = - -node.pos[0] - - node.size[0] * 0.5 + - (this.canvas.width * 0.5) / this.ds.scale; - this.ds.offset[1] = - -node.pos[1] - - node.size[1] * 0.5 + - (this.canvas.height * 0.5) / this.ds.scale; - this.setDirty(true, true); - }; - - /** - * adds some useful properties to a mouse event, like the position in graph coordinates - * @method adjustMouseEvent - **/ - LGraphCanvas.prototype.adjustMouseEvent = function(e) { - if (this.canvas) { - var b = this.canvas.getBoundingClientRect(); - e.localX = e.clientX - b.left; - e.localY = e.clientY - b.top; - } else { - e.localX = e.clientX; - e.localY = e.clientY; - } - - e.deltaX = e.localX - this.last_mouse_position[0]; - e.deltaY = e.localY - this.last_mouse_position[1]; - - this.last_mouse_position[0] = e.localX; - this.last_mouse_position[1] = e.localY; - - e.canvasX = e.localX / this.ds.scale - this.ds.offset[0]; - e.canvasY = e.localY / this.ds.scale - this.ds.offset[1]; - }; - - /** - * changes the zoom level of the graph (default is 1), you can pass also a place used to pivot the zoom - * @method setZoom - **/ - LGraphCanvas.prototype.setZoom = function(value, zooming_center) { - this.ds.changeScale(value, zooming_center); - /* - if(!zooming_center && this.canvas) - zooming_center = [this.canvas.width * 0.5,this.canvas.height * 0.5]; - - var center = this.convertOffsetToCanvas( zooming_center ); - - this.ds.scale = value; - - if(this.scale > this.max_zoom) - this.scale = this.max_zoom; - else if(this.scale < this.min_zoom) - this.scale = this.min_zoom; - - var new_center = this.convertOffsetToCanvas( zooming_center ); - var delta_offset = [new_center[0] - center[0], new_center[1] - center[1]]; - - this.offset[0] += delta_offset[0]; - this.offset[1] += delta_offset[1]; - */ - - this.dirty_canvas = true; - this.dirty_bgcanvas = true; - }; - - /** - * converts a coordinate from graph coordinates to canvas2D coordinates - * @method convertOffsetToCanvas - **/ - LGraphCanvas.prototype.convertOffsetToCanvas = function(pos, out) { - return this.ds.convertOffsetToCanvas(pos, out); - }; - - /** - * converts a coordinate from Canvas2D coordinates to graph space - * @method convertCanvasToOffset - **/ - LGraphCanvas.prototype.convertCanvasToOffset = function(pos, out) { - return this.ds.convertCanvasToOffset(pos, out); - }; - - //converts event coordinates from canvas2D to graph coordinates - LGraphCanvas.prototype.convertEventToCanvasOffset = function(e) { - var rect = this.canvas.getBoundingClientRect(); - return this.convertCanvasToOffset([ - e.clientX - rect.left, - e.clientY - rect.top - ]); - }; - - /** - * brings a node to front (above all other nodes) - * @method bringToFront - **/ - LGraphCanvas.prototype.bringToFront = function(node) { - var i = this.graph._nodes.indexOf(node); - if (i == -1) { - return; - } - - this.graph._nodes.splice(i, 1); - this.graph._nodes.push(node); - }; - - /** - * sends a node to the back (below all other nodes) - * @method sendToBack - **/ - LGraphCanvas.prototype.sendToBack = function(node) { - var i = this.graph._nodes.indexOf(node); - if (i == -1) { - return; - } - - this.graph._nodes.splice(i, 1); - this.graph._nodes.unshift(node); - }; - - /* Interaction */ - - /* LGraphCanvas render */ - var temp = new Float32Array(4); - - /** - * checks which nodes are visible (inside the camera area) - * @method computeVisibleNodes - **/ - LGraphCanvas.prototype.computeVisibleNodes = function(nodes, out) { - var visible_nodes = out || []; - visible_nodes.length = 0; - nodes = nodes || this.graph._nodes; - for (var i = 0, l = nodes.length; i < l; ++i) { - var n = nodes[i]; - - //skip rendering nodes in live mode - if (this.live_mode && !n.onDrawBackground && !n.onDrawForeground) { - continue; - } - - if (!overlapBounding(this.visible_area, n.getBounding(temp))) { - continue; - } //out of the visible area - - visible_nodes.push(n); - } - return visible_nodes; - }; - - /** - * renders the whole canvas content, by rendering in two separated canvas, one containing the background grid and the connections, and one containing the nodes) - * @method draw - **/ - LGraphCanvas.prototype.draw = function(force_canvas, force_bgcanvas) { - if (!this.canvas) { - return; - } - - //fps counting - var now = LiteGraph.getTime(); - this.render_time = (now - this.last_draw_time) * 0.001; - this.last_draw_time = now; - - if (this.graph) { - this.ds.computeVisibleArea(); - } - - if ( - this.dirty_bgcanvas || - force_bgcanvas || - this.always_render_background || - (this.graph && - this.graph._last_trigger_time && - now - this.graph._last_trigger_time < 1000) - ) { - this.drawBackCanvas(); - } - - if (this.dirty_canvas || force_canvas) { - this.drawFrontCanvas(); - } - - this.fps = this.render_time ? 1.0 / this.render_time : 0; - this.frame += 1; - }; - - /** - * draws the front canvas (the one containing all the nodes) - * @method drawFrontCanvas - **/ - LGraphCanvas.prototype.drawFrontCanvas = function() { - this.dirty_canvas = false; - - if (!this.ctx) { - this.ctx = this.bgcanvas.getContext("2d"); - } - var ctx = this.ctx; - if (!ctx) { - //maybe is using webgl... - return; - } - - if (ctx.start2D) { - ctx.start2D(); - } - - var canvas = this.canvas; - - //reset in case of error - ctx.restore(); - ctx.setTransform(1, 0, 0, 1, 0, 0); - - //clip dirty area if there is one, otherwise work in full canvas - if (this.dirty_area) { - ctx.save(); - ctx.beginPath(); - ctx.rect( - this.dirty_area[0], - this.dirty_area[1], - this.dirty_area[2], - this.dirty_area[3] - ); - ctx.clip(); - } - - //clear - //canvas.width = canvas.width; - if (this.clear_background) { - ctx.clearRect(0, 0, canvas.width, canvas.height); - } - - //draw bg canvas - if (this.bgcanvas == this.canvas) { - this.drawBackCanvas(); - } else { - ctx.drawImage(this.bgcanvas, 0, 0); - } - - //rendering - if (this.onRender) { - this.onRender(canvas, ctx); - } - - //info widget - if (this.show_info) { - this.renderInfo(ctx); - } - - if (this.graph) { - //apply transformations - ctx.save(); - this.ds.toCanvasContext(ctx); - - //draw nodes - var drawn_nodes = 0; - var visible_nodes = this.computeVisibleNodes( - null, - this.visible_nodes - ); - - for (var i = 0; i < visible_nodes.length; ++i) { - var node = visible_nodes[i]; - - //transform coords system - ctx.save(); - ctx.translate(node.pos[0], node.pos[1]); - - //Draw - this.drawNode(node, ctx); - drawn_nodes += 1; - - //Restore - ctx.restore(); - } - - //on top (debug) - if (this.render_execution_order) { - this.drawExecutionOrder(ctx); - } - - //connections ontop? - if (this.graph.config.links_ontop) { - if (!this.live_mode) { - this.drawConnections(ctx); - } - } - - //current connection (the one being dragged by the mouse) - if (this.connecting_pos != null) { - ctx.lineWidth = this.connections_width; - var link_color = null; - - switch (this.connecting_output.type) { - case LiteGraph.EVENT: - link_color = LiteGraph.EVENT_LINK_COLOR; - break; - default: - link_color = LiteGraph.CONNECTING_LINK_COLOR; - } - - //the connection being dragged by the mouse - this.renderLink( - ctx, - this.connecting_pos, - [this.canvas_mouse[0], this.canvas_mouse[1]], - null, - false, - null, - link_color, - this.connecting_output.dir || - (this.connecting_node.horizontal - ? LiteGraph.DOWN - : LiteGraph.RIGHT), - LiteGraph.CENTER - ); - - ctx.beginPath(); - if ( - this.connecting_output.type === LiteGraph.EVENT || - this.connecting_output.shape === LiteGraph.BOX_SHAPE - ) { - ctx.rect( - this.connecting_pos[0] - 6 + 0.5, - this.connecting_pos[1] - 5 + 0.5, - 14, - 10 - ); - } else { - ctx.arc( - this.connecting_pos[0], - this.connecting_pos[1], - 4, - 0, - Math.PI * 2 - ); - } - ctx.fill(); - - ctx.fillStyle = "#ffcc00"; - if (this._highlight_input) { - ctx.beginPath(); - ctx.arc( - this._highlight_input[0], - this._highlight_input[1], - 6, - 0, - Math.PI * 2 - ); - ctx.fill(); - } - } - - //the selection rectangle - if (this.dragging_rectangle) { - ctx.strokeStyle = "#FFF"; - ctx.strokeRect( - this.dragging_rectangle[0], - this.dragging_rectangle[1], - this.dragging_rectangle[2], - this.dragging_rectangle[3] - ); - } - - //on top of link center - if(this.over_link_center && this.render_link_tooltip) - this.drawLinkTooltip( ctx, this.over_link_center ); - else - if(this.onDrawLinkTooltip) //to remove - this.onDrawLinkTooltip(ctx,null); - - //custom info - if (this.onDrawForeground) { - this.onDrawForeground(ctx, this.visible_rect); - } - - ctx.restore(); - } - - if (this.onDrawOverlay) { - this.onDrawOverlay(ctx); - } - - if (this.dirty_area) { - ctx.restore(); - //this.dirty_area = null; - } - - if (ctx.finish2D) { - //this is a function I use in webgl renderer - ctx.finish2D(); - } - }; - - /** - * draws some useful stats in the corner of the canvas - * @method renderInfo - **/ - LGraphCanvas.prototype.renderInfo = function(ctx, x, y) { - x = x || 0; - y = y || 0; - - ctx.save(); - ctx.translate(x, y); - - ctx.font = "10px Arial"; - ctx.fillStyle = "#888"; - if (this.graph) { - ctx.fillText( "T: " + this.graph.globaltime.toFixed(2) + "s", 5, 13 * 1 ); - ctx.fillText("I: " + this.graph.iteration, 5, 13 * 2 ); - ctx.fillText("N: " + this.graph._nodes.length + " [" + this.visible_nodes.length + "]", 5, 13 * 3 ); - ctx.fillText("V: " + this.graph._version, 5, 13 * 4); - ctx.fillText("FPS:" + this.fps.toFixed(2), 5, 13 * 5); - } else { - ctx.fillText("No graph selected", 5, 13 * 1); - } - ctx.restore(); - }; - - /** - * draws the back canvas (the one containing the background and the connections) - * @method drawBackCanvas - **/ - LGraphCanvas.prototype.drawBackCanvas = function() { - var canvas = this.bgcanvas; - if ( - canvas.width != this.canvas.width || - canvas.height != this.canvas.height - ) { - canvas.width = this.canvas.width; - canvas.height = this.canvas.height; - } - - if (!this.bgctx) { - this.bgctx = this.bgcanvas.getContext("2d"); - } - var ctx = this.bgctx; - if (ctx.start) { - ctx.start(); - } - - //clear - if (this.clear_background) { - ctx.clearRect(0, 0, canvas.width, canvas.height); - } - - if (this._graph_stack && this._graph_stack.length) { - ctx.save(); - var parent_graph = this._graph_stack[this._graph_stack.length - 1]; - var subgraph_node = this.graph._subgraph_node; - ctx.strokeStyle = subgraph_node.bgcolor; - ctx.lineWidth = 10; - ctx.strokeRect(1, 1, canvas.width - 2, canvas.height - 2); - ctx.lineWidth = 1; - ctx.font = "40px Arial"; - ctx.textAlign = "center"; - ctx.fillStyle = subgraph_node.bgcolor || "#AAA"; - var title = ""; - for (var i = 1; i < this._graph_stack.length; ++i) { - title += - this._graph_stack[i]._subgraph_node.getTitle() + " >> "; - } - ctx.fillText( - title + subgraph_node.getTitle(), - canvas.width * 0.5, - 40 - ); - ctx.restore(); - } - - var bg_already_painted = false; - if (this.onRenderBackground) { - bg_already_painted = this.onRenderBackground(canvas, ctx); - } - - //reset in case of error - ctx.restore(); - ctx.setTransform(1, 0, 0, 1, 0, 0); - this.visible_links.length = 0; - - if (this.graph) { - //apply transformations - ctx.save(); - this.ds.toCanvasContext(ctx); - - //render BG - if ( - this.background_image && - this.ds.scale > 0.5 && - !bg_already_painted - ) { - if (this.zoom_modify_alpha) { - ctx.globalAlpha = - (1.0 - 0.5 / this.ds.scale) * this.editor_alpha; - } else { - ctx.globalAlpha = this.editor_alpha; - } - ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.imageSmoothingEnabled = false; - if ( - !this._bg_img || - this._bg_img.name != this.background_image - ) { - this._bg_img = new Image(); - this._bg_img.name = this.background_image; - this._bg_img.src = this.background_image; - var that = this; - this._bg_img.onload = function() { - that.draw(true, true); - }; - } - - var pattern = null; - if (this._pattern == null && this._bg_img.width > 0) { - pattern = ctx.createPattern(this._bg_img, "repeat"); - this._pattern_img = this._bg_img; - this._pattern = pattern; - } else { - pattern = this._pattern; - } - if (pattern) { - ctx.fillStyle = pattern; - ctx.fillRect( - this.visible_area[0], - this.visible_area[1], - this.visible_area[2], - this.visible_area[3] - ); - ctx.fillStyle = "transparent"; - } - - ctx.globalAlpha = 1.0; - ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.imageSmoothingEnabled = true; - } - - //groups - if (this.graph._groups.length && !this.live_mode) { - this.drawGroups(canvas, ctx); - } - - if (this.onDrawBackground) { - this.onDrawBackground(ctx, this.visible_area); - } - if (this.onBackgroundRender) { - //LEGACY - console.error( - "WARNING! onBackgroundRender deprecated, now is named onDrawBackground " - ); - this.onBackgroundRender = null; - } - - //DEBUG: show clipping area - //ctx.fillStyle = "red"; - //ctx.fillRect( this.visible_area[0] + 10, this.visible_area[1] + 10, this.visible_area[2] - 20, this.visible_area[3] - 20); - - //bg - if (this.render_canvas_border) { - ctx.strokeStyle = "#235"; - ctx.strokeRect(0, 0, canvas.width, canvas.height); - } - - if (this.render_connections_shadows) { - ctx.shadowColor = "#000"; - ctx.shadowOffsetX = 0; - ctx.shadowOffsetY = 0; - ctx.shadowBlur = 6; - } else { - ctx.shadowColor = "rgba(0,0,0,0)"; - } - - //draw connections - if (!this.live_mode) { - this.drawConnections(ctx); - } - - ctx.shadowColor = "rgba(0,0,0,0)"; - - //restore state - ctx.restore(); - } - - if (ctx.finish) { - ctx.finish(); - } - - this.dirty_bgcanvas = false; - this.dirty_canvas = true; //to force to repaint the front canvas with the bgcanvas - }; - - var temp_vec2 = new Float32Array(2); - - /** - * draws the given node inside the canvas - * @method drawNode - **/ - LGraphCanvas.prototype.drawNode = function(node, ctx) { - var glow = false; - this.current_node = node; - - var color = - node.color || - node.constructor.color || - LiteGraph.NODE_DEFAULT_COLOR; - var bgcolor = - node.bgcolor || - node.constructor.bgcolor || - LiteGraph.NODE_DEFAULT_BGCOLOR; - - //shadow and glow - if (node.mouseOver) { - glow = true; - } - - var low_quality = this.ds.scale < 0.6; //zoomed out - - //only render if it forces it to do it - if (this.live_mode) { - if (!node.flags.collapsed) { - ctx.shadowColor = "transparent"; - if (node.onDrawForeground) { - node.onDrawForeground(ctx, this, this.canvas); - } - } - return; - } - - var editor_alpha = this.editor_alpha; - ctx.globalAlpha = editor_alpha; - - if (this.render_shadows && !low_quality) { - ctx.shadowColor = LiteGraph.DEFAULT_SHADOW_COLOR; - ctx.shadowOffsetX = 2 * this.ds.scale; - ctx.shadowOffsetY = 2 * this.ds.scale; - ctx.shadowBlur = 3 * this.ds.scale; - } else { - ctx.shadowColor = "transparent"; - } - - //custom draw collapsed method (draw after shadows because they are affected) - if ( - node.flags.collapsed && - node.onDrawCollapsed && - node.onDrawCollapsed(ctx, this) == true - ) { - return; - } - - //clip if required (mask) - var shape = node._shape || LiteGraph.BOX_SHAPE; - var size = temp_vec2; - temp_vec2.set(node.size); - var horizontal = node.horizontal; // || node.flags.horizontal; - - if (node.flags.collapsed) { - ctx.font = this.inner_text_font; - var title = node.getTitle ? node.getTitle() : node.title; - if (title != null) { - node._collapsed_width = Math.min( - node.size[0], - ctx.measureText(title).width + - LiteGraph.NODE_TITLE_HEIGHT * 2 - ); //LiteGraph.NODE_COLLAPSED_WIDTH; - size[0] = node._collapsed_width; - size[1] = 0; - } - } - - if (node.clip_area) { - //Start clipping - ctx.save(); - ctx.beginPath(); - if (shape == LiteGraph.BOX_SHAPE) { - ctx.rect(0, 0, size[0], size[1]); - } else if (shape == LiteGraph.ROUND_SHAPE) { - ctx.roundRect(0, 0, size[0], size[1], 10); - } else if (shape == LiteGraph.CIRCLE_SHAPE) { - ctx.arc( - size[0] * 0.5, - size[1] * 0.5, - size[0] * 0.5, - 0, - Math.PI * 2 - ); - } - ctx.clip(); - } - - //draw shape - if (node.has_errors) { - bgcolor = "red"; - } - this.drawNodeShape( - node, - ctx, - size, - color, - bgcolor, - node.is_selected, - node.mouseOver - ); - ctx.shadowColor = "transparent"; - - //draw foreground - if (node.onDrawForeground) { - node.onDrawForeground(ctx, this, this.canvas); - } - - //connection slots - ctx.textAlign = horizontal ? "center" : "left"; - ctx.font = this.inner_text_font; - - var render_text = !low_quality; - - var out_slot = this.connecting_output; - ctx.lineWidth = 1; - - var max_y = 0; - var slot_pos = new Float32Array(2); //to reuse - - //render inputs and outputs - if (!node.flags.collapsed) { - //input connection slots - if (node.inputs) { - for (var i = 0; i < node.inputs.length; i++) { - var slot = node.inputs[i]; - - ctx.globalAlpha = editor_alpha; - //change opacity of incompatible slots when dragging a connection - if ( this.connecting_node && !LiteGraph.isValidConnection( slot.type , out_slot.type) ) { - ctx.globalAlpha = 0.4 * editor_alpha; - } - - ctx.fillStyle = - slot.link != null - ? slot.color_on || - this.default_connection_color.input_on - : slot.color_off || - this.default_connection_color.input_off; - - var pos = node.getConnectionPos(true, i, slot_pos); - pos[0] -= node.pos[0]; - pos[1] -= node.pos[1]; - if (max_y < pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5) { - max_y = pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5; - } - - ctx.beginPath(); - - if ( - slot.type === LiteGraph.EVENT || - slot.shape === LiteGraph.BOX_SHAPE - ) { - if (horizontal) { - ctx.rect( - pos[0] - 5 + 0.5, - pos[1] - 8 + 0.5, - 10, - 14 - ); - } else { - ctx.rect( - pos[0] - 6 + 0.5, - pos[1] - 5 + 0.5, - 14, - 10 - ); - } - } else if (slot.shape === LiteGraph.ARROW_SHAPE) { - ctx.moveTo(pos[0] + 8, pos[1] + 0.5); - ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5); - ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5); - ctx.closePath(); - } else { - if(low_quality) - ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); //faster - else - ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); - } - ctx.fill(); - - //render name - if (render_text) { - var text = slot.label != null ? slot.label : slot.name; - if (text) { - ctx.fillStyle = LiteGraph.NODE_TEXT_COLOR; - if (horizontal || slot.dir == LiteGraph.UP) { - ctx.fillText(text, pos[0], pos[1] - 10); - } else { - ctx.fillText(text, pos[0] + 10, pos[1] + 5); - } - } - } - } - } - - //output connection slots - if (this.connecting_node) { - ctx.globalAlpha = 0.4 * editor_alpha; - } - - ctx.textAlign = horizontal ? "center" : "right"; - ctx.strokeStyle = "black"; - if (node.outputs) { - for (var i = 0; i < node.outputs.length; i++) { - var slot = node.outputs[i]; - - var pos = node.getConnectionPos(false, i, slot_pos); - pos[0] -= node.pos[0]; - pos[1] -= node.pos[1]; - if (max_y < pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5) { - max_y = pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5; - } - - ctx.fillStyle = - slot.links && slot.links.length - ? slot.color_on || - this.default_connection_color.output_on - : slot.color_off || - this.default_connection_color.output_off; - ctx.beginPath(); - //ctx.rect( node.size[0] - 14,i*14,10,10); - - if ( - slot.type === LiteGraph.EVENT || - slot.shape === LiteGraph.BOX_SHAPE - ) { - if (horizontal) { - ctx.rect( - pos[0] - 5 + 0.5, - pos[1] - 8 + 0.5, - 10, - 14 - ); - } else { - ctx.rect( - pos[0] - 6 + 0.5, - pos[1] - 5 + 0.5, - 14, - 10 - ); - } - } else if (slot.shape === LiteGraph.ARROW_SHAPE) { - ctx.moveTo(pos[0] + 8, pos[1] + 0.5); - ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5); - ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5); - ctx.closePath(); - } else { - if(low_quality) - ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); - else - ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); - } - - //trigger - //if(slot.node_id != null && slot.slot == -1) - // ctx.fillStyle = "#F85"; - - //if(slot.links != null && slot.links.length) - ctx.fill(); - if(!low_quality) - ctx.stroke(); - - //render output name - if (render_text) { - var text = slot.label != null ? slot.label : slot.name; - if (text) { - ctx.fillStyle = LiteGraph.NODE_TEXT_COLOR; - if (horizontal || slot.dir == LiteGraph.DOWN) { - ctx.fillText(text, pos[0], pos[1] - 8); - } else { - ctx.fillText(text, pos[0] - 10, pos[1] + 5); - } - } - } - } - } - - ctx.textAlign = "left"; - ctx.globalAlpha = 1; - - if (node.widgets) { - var widgets_y = max_y; - if (horizontal || node.widgets_up) { - widgets_y = 2; - } - if( node.widgets_start_y != null ) - widgets_y = node.widgets_start_y; - this.drawNodeWidgets( - node, - widgets_y, - ctx, - this.node_widget && this.node_widget[0] == node - ? this.node_widget[1] - : null - ); - } - } else if (this.render_collapsed_slots) { - //if collapsed - var input_slot = null; - var output_slot = null; - - //get first connected slot to render - if (node.inputs) { - for (var i = 0; i < node.inputs.length; i++) { - var slot = node.inputs[i]; - if (slot.link == null) { - continue; - } - input_slot = slot; - break; - } - } - if (node.outputs) { - for (var i = 0; i < node.outputs.length; i++) { - var slot = node.outputs[i]; - if (!slot.links || !slot.links.length) { - continue; - } - output_slot = slot; - } - } - - if (input_slot) { - var x = 0; - var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; //center - if (horizontal) { - x = node._collapsed_width * 0.5; - y = -LiteGraph.NODE_TITLE_HEIGHT; - } - ctx.fillStyle = "#686"; - ctx.beginPath(); - if ( - slot.type === LiteGraph.EVENT || - slot.shape === LiteGraph.BOX_SHAPE - ) { - ctx.rect(x - 7 + 0.5, y - 4, 14, 8); - } else if (slot.shape === LiteGraph.ARROW_SHAPE) { - ctx.moveTo(x + 8, y); - ctx.lineTo(x + -4, y - 4); - ctx.lineTo(x + -4, y + 4); - ctx.closePath(); - } else { - ctx.arc(x, y, 4, 0, Math.PI * 2); - } - ctx.fill(); - } - - if (output_slot) { - var x = node._collapsed_width; - var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; //center - if (horizontal) { - x = node._collapsed_width * 0.5; - y = 0; - } - ctx.fillStyle = "#686"; - ctx.strokeStyle = "black"; - ctx.beginPath(); - if ( - slot.type === LiteGraph.EVENT || - slot.shape === LiteGraph.BOX_SHAPE - ) { - ctx.rect(x - 7 + 0.5, y - 4, 14, 8); - } else if (slot.shape === LiteGraph.ARROW_SHAPE) { - ctx.moveTo(x + 6, y); - ctx.lineTo(x - 6, y - 4); - ctx.lineTo(x - 6, y + 4); - ctx.closePath(); - } else { - ctx.arc(x, y, 4, 0, Math.PI * 2); - } - ctx.fill(); - //ctx.stroke(); - } - } - - if (node.clip_area) { - ctx.restore(); - } - - ctx.globalAlpha = 1.0; - }; - - //used by this.over_link_center - LGraphCanvas.prototype.drawLinkTooltip = function( ctx, link ) - { - var pos = link._pos; - ctx.fillStyle = "black"; - ctx.beginPath(); - ctx.arc( pos[0], pos[1], 3, 0, Math.PI * 2 ); - ctx.fill(); - - if(link.data == null) - return; - - if(this.onDrawLinkTooltip) - if( this.onDrawLinkTooltip(ctx,link,this) == true ) - return; - - var data = link.data; - var text = null; - - if( data.constructor === Number ) - text = data.toFixed(2); - else if( data.constructor === String ) - text = "\"" + data + "\""; - else if( data.constructor === Boolean ) - text = String(data); - else if (data.toToolTip) - text = data.toToolTip(); - else - text = "[" + data.constructor.name + "]"; - - if(text == null) - return; - text = text.substr(0,30); //avoid weird - - ctx.font = "14px Courier New"; - var info = ctx.measureText(text); - var w = info.width + 20; - var h = 24; - ctx.shadowColor = "black"; - ctx.shadowOffsetX = 2; - ctx.shadowOffsetY = 2; - ctx.shadowBlur = 3; - ctx.fillStyle = "#454"; - ctx.beginPath(); - ctx.roundRect( pos[0] - w*0.5, pos[1] - 15 - h, w, h,3, 3); - ctx.moveTo( pos[0] - 10, pos[1] - 15 ); - ctx.lineTo( pos[0] + 10, pos[1] - 15 ); - ctx.lineTo( pos[0], pos[1] - 5 ); - ctx.fill(); - ctx.shadowColor = "transparent"; - ctx.textAlign = "center"; - ctx.fillStyle = "#CEC"; - ctx.fillText(text, pos[0], pos[1] - 15 - h * 0.3); - } - - /** - * draws the shape of the given node in the canvas - * @method drawNodeShape - **/ - var tmp_area = new Float32Array(4); - - LGraphCanvas.prototype.drawNodeShape = function( - node, - ctx, - size, - fgcolor, - bgcolor, - selected, - mouse_over - ) { - //bg rect - ctx.strokeStyle = fgcolor; - ctx.fillStyle = bgcolor; - - var title_height = LiteGraph.NODE_TITLE_HEIGHT; - var low_quality = this.ds.scale < 0.5; - - //render node area depending on shape - var shape = - node._shape || node.constructor.shape || LiteGraph.ROUND_SHAPE; - - var title_mode = node.constructor.title_mode; - - var render_title = true; - if (title_mode == LiteGraph.TRANSPARENT_TITLE) { - render_title = false; - } else if (title_mode == LiteGraph.AUTOHIDE_TITLE && mouse_over) { - render_title = true; - } - - var area = tmp_area; - area[0] = 0; //x - area[1] = render_title ? -title_height : 0; //y - area[2] = size[0] + 1; //w - area[3] = render_title ? size[1] + title_height : size[1]; //h - - var old_alpha = ctx.globalAlpha; - - //full node shape - //if(node.flags.collapsed) - { - ctx.beginPath(); - if (shape == LiteGraph.BOX_SHAPE || low_quality) { - ctx.fillRect(area[0], area[1], area[2], area[3]); - } else if ( - shape == LiteGraph.ROUND_SHAPE || - shape == LiteGraph.CARD_SHAPE - ) { - ctx.roundRect( - area[0], - area[1], - area[2], - area[3], - this.round_radius, - shape == LiteGraph.CARD_SHAPE ? 0 : this.round_radius - ); - } else if (shape == LiteGraph.CIRCLE_SHAPE) { - ctx.arc( - size[0] * 0.5, - size[1] * 0.5, - size[0] * 0.5, - 0, - Math.PI * 2 - ); - } - ctx.fill(); - - //separator - if(!node.flags.collapsed) - { - ctx.shadowColor = "transparent"; - ctx.fillStyle = "rgba(0,0,0,0.2)"; - ctx.fillRect(0, -1, area[2], 2); - } - } - ctx.shadowColor = "transparent"; - - if (node.onDrawBackground) { - node.onDrawBackground(ctx, this, this.canvas); - } - - //title bg (remember, it is rendered ABOVE the node) - if (render_title || title_mode == LiteGraph.TRANSPARENT_TITLE) { - //title bar - if (node.onDrawTitleBar) { - node.onDrawTitleBar( - ctx, - title_height, - size, - this.ds.scale, - fgcolor - ); - } else if ( - title_mode != LiteGraph.TRANSPARENT_TITLE && - (node.constructor.title_color || this.render_title_colored) - ) { - var title_color = node.constructor.title_color || fgcolor; - - if (node.flags.collapsed) { - ctx.shadowColor = LiteGraph.DEFAULT_SHADOW_COLOR; - } - - //* gradient test - if (this.use_gradients) { - var grad = LGraphCanvas.gradients[title_color]; - if (!grad) { - grad = LGraphCanvas.gradients[ title_color ] = ctx.createLinearGradient(0, 0, 400, 0); - grad.addColorStop(0, title_color); - grad.addColorStop(1, "#000"); - } - ctx.fillStyle = grad; - } else { - ctx.fillStyle = title_color; - } - - //ctx.globalAlpha = 0.5 * old_alpha; - ctx.beginPath(); - if (shape == LiteGraph.BOX_SHAPE || low_quality) { - ctx.rect(0, -title_height, size[0] + 1, title_height); - } else if ( shape == LiteGraph.ROUND_SHAPE || shape == LiteGraph.CARD_SHAPE ) { - ctx.roundRect( - 0, - -title_height, - size[0] + 1, - title_height, - this.round_radius, - node.flags.collapsed ? this.round_radius : 0 - ); - } - ctx.fill(); - ctx.shadowColor = "transparent"; - } - - //title box - var box_size = 10; - if (node.onDrawTitleBox) { - node.onDrawTitleBox(ctx, title_height, size, this.ds.scale); - } else if ( - shape == LiteGraph.ROUND_SHAPE || - shape == LiteGraph.CIRCLE_SHAPE || - shape == LiteGraph.CARD_SHAPE - ) { - if (low_quality) { - ctx.fillStyle = "black"; - ctx.beginPath(); - ctx.arc( - title_height * 0.5, - title_height * -0.5, - box_size * 0.5 + 1, - 0, - Math.PI * 2 - ); - ctx.fill(); - } - - ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR; - if(low_quality) - ctx.fillRect( title_height * 0.5 - box_size *0.5, title_height * -0.5 - box_size *0.5, box_size , box_size ); - else - { - ctx.beginPath(); - ctx.arc( - title_height * 0.5, - title_height * -0.5, - box_size * 0.5, - 0, - Math.PI * 2 - ); - ctx.fill(); - } - } else { - if (low_quality) { - ctx.fillStyle = "black"; - ctx.fillRect( - (title_height - box_size) * 0.5 - 1, - (title_height + box_size) * -0.5 - 1, - box_size + 2, - box_size + 2 - ); - } - ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR; - ctx.fillRect( - (title_height - box_size) * 0.5, - (title_height + box_size) * -0.5, - box_size, - box_size - ); - } - ctx.globalAlpha = old_alpha; - - //title text - if (node.onDrawTitleText) { - node.onDrawTitleText( - ctx, - title_height, - size, - this.ds.scale, - this.title_text_font, - selected - ); - } - if (!low_quality) { - ctx.font = this.title_text_font; - var title = String(node.getTitle()); - if (title) { - if (selected) { - ctx.fillStyle = "white"; - } else { - ctx.fillStyle = - node.constructor.title_text_color || - this.node_title_color; - } - if (node.flags.collapsed) { - ctx.textAlign = "left"; - var measure = ctx.measureText(title); - ctx.fillText( - title.substr(0,20), //avoid urls too long - title_height,// + measure.width * 0.5, - LiteGraph.NODE_TITLE_TEXT_Y - title_height - ); - ctx.textAlign = "left"; - } else { - ctx.textAlign = "left"; - ctx.fillText( - title, - title_height, - LiteGraph.NODE_TITLE_TEXT_Y - title_height - ); - } - } - } - - if (node.onDrawTitle) { - node.onDrawTitle(ctx); - } - } - - //render selection marker - if (selected) { - if (node.onBounding) { - node.onBounding(area); - } - - if (title_mode == LiteGraph.TRANSPARENT_TITLE) { - area[1] -= title_height; - area[3] += title_height; - } - ctx.lineWidth = 1; - ctx.globalAlpha = 0.8; - ctx.beginPath(); - if (shape == LiteGraph.BOX_SHAPE) { - ctx.rect( - -6 + area[0], - -6 + area[1], - 12 + area[2], - 12 + area[3] - ); - } else if ( - shape == LiteGraph.ROUND_SHAPE || - (shape == LiteGraph.CARD_SHAPE && node.flags.collapsed) - ) { - ctx.roundRect( - -6 + area[0], - -6 + area[1], - 12 + area[2], - 12 + area[3], - this.round_radius * 2 - ); - } else if (shape == LiteGraph.CARD_SHAPE) { - ctx.roundRect( - -6 + area[0], - -6 + area[1], - 12 + area[2], - 12 + area[3], - this.round_radius * 2, - 2 - ); - } else if (shape == LiteGraph.CIRCLE_SHAPE) { - ctx.arc( - size[0] * 0.5, - size[1] * 0.5, - size[0] * 0.5 + 6, - 0, - Math.PI * 2 - ); - } - ctx.strokeStyle = "#FFF"; - ctx.stroke(); - ctx.strokeStyle = fgcolor; - ctx.globalAlpha = 1; - } - }; - - var margin_area = new Float32Array(4); - var link_bounding = new Float32Array(4); - var tempA = new Float32Array(2); - var tempB = new Float32Array(2); - - /** - * draws every connection visible in the canvas - * OPTIMIZE THIS: pre-catch connections position instead of recomputing them every time - * @method drawConnections - **/ - LGraphCanvas.prototype.drawConnections = function(ctx) { - var now = LiteGraph.getTime(); - var visible_area = this.visible_area; - margin_area[0] = visible_area[0] - 20; - margin_area[1] = visible_area[1] - 20; - margin_area[2] = visible_area[2] + 40; - margin_area[3] = visible_area[3] + 40; - - //draw connections - ctx.lineWidth = this.connections_width; - - ctx.fillStyle = "#AAA"; - ctx.strokeStyle = "#AAA"; - ctx.globalAlpha = this.editor_alpha; - //for every node - var nodes = this.graph._nodes; - for (var n = 0, l = nodes.length; n < l; ++n) { - var node = nodes[n]; - //for every input (we render just inputs because it is easier as every slot can only have one input) - if (!node.inputs || !node.inputs.length) { - continue; - } - - for (var i = 0; i < node.inputs.length; ++i) { - var input = node.inputs[i]; - if (!input || input.link == null) { - continue; - } - var link_id = input.link; - var link = this.graph.links[link_id]; - if (!link) { - continue; - } - - //find link info - var start_node = this.graph.getNodeById(link.origin_id); - if (start_node == null) { - continue; - } - var start_node_slot = link.origin_slot; - var start_node_slotpos = null; - if (start_node_slot == -1) { - start_node_slotpos = [ - start_node.pos[0] + 10, - start_node.pos[1] + 10 - ]; - } else { - start_node_slotpos = start_node.getConnectionPos( - false, - start_node_slot, - tempA - ); - } - var end_node_slotpos = node.getConnectionPos(true, i, tempB); - - //compute link bounding - link_bounding[0] = start_node_slotpos[0]; - link_bounding[1] = start_node_slotpos[1]; - link_bounding[2] = end_node_slotpos[0] - start_node_slotpos[0]; - link_bounding[3] = end_node_slotpos[1] - start_node_slotpos[1]; - if (link_bounding[2] < 0) { - link_bounding[0] += link_bounding[2]; - link_bounding[2] = Math.abs(link_bounding[2]); - } - if (link_bounding[3] < 0) { - link_bounding[1] += link_bounding[3]; - link_bounding[3] = Math.abs(link_bounding[3]); - } - - //skip links outside of the visible area of the canvas - if (!overlapBounding(link_bounding, margin_area)) { - continue; - } - - var start_slot = start_node.outputs[start_node_slot]; - var end_slot = node.inputs[i]; - if (!start_slot || !end_slot) { - continue; - } - var start_dir = - start_slot.dir || - (start_node.horizontal ? LiteGraph.DOWN : LiteGraph.RIGHT); - var end_dir = - end_slot.dir || - (node.horizontal ? LiteGraph.UP : LiteGraph.LEFT); - - this.renderLink( - ctx, - start_node_slotpos, - end_node_slotpos, - link, - false, - 0, - null, - start_dir, - end_dir - ); - - //event triggered rendered on top - if (link && link._last_time && now - link._last_time < 1000) { - var f = 2.0 - (now - link._last_time) * 0.002; - var tmp = ctx.globalAlpha; - ctx.globalAlpha = tmp * f; - this.renderLink( - ctx, - start_node_slotpos, - end_node_slotpos, - link, - true, - f, - "white", - start_dir, - end_dir - ); - ctx.globalAlpha = tmp; - } - } - } - ctx.globalAlpha = 1; - }; - - /** - * draws a link between two points - * @method renderLink - * @param {vec2} a start pos - * @param {vec2} b end pos - * @param {Object} link the link object with all the link info - * @param {boolean} skip_border ignore the shadow of the link - * @param {boolean} flow show flow animation (for events) - * @param {string} color the color for the link - * @param {number} start_dir the direction enum - * @param {number} end_dir the direction enum - * @param {number} num_sublines number of sublines (useful to represent vec3 or rgb) - **/ - LGraphCanvas.prototype.renderLink = function( - ctx, - a, - b, - link, - skip_border, - flow, - color, - start_dir, - end_dir, - num_sublines - ) { - if (link) { - this.visible_links.push(link); - } - - //choose color - if (!color && link) { - color = link.color || LGraphCanvas.link_type_colors[link.type]; - } - if (!color) { - color = this.default_link_color; - } - if (link != null && this.highlighted_links[link.id]) { - color = "#FFF"; - } - - start_dir = start_dir || LiteGraph.RIGHT; - end_dir = end_dir || LiteGraph.LEFT; - - var dist = distance(a, b); - - if (this.render_connections_border && this.ds.scale > 0.6) { - ctx.lineWidth = this.connections_width + 4; - } - ctx.lineJoin = "round"; - num_sublines = num_sublines || 1; - if (num_sublines > 1) { - ctx.lineWidth = 0.5; - } - - //begin line shape - ctx.beginPath(); - for (var i = 0; i < num_sublines; i += 1) { - var offsety = (i - (num_sublines - 1) * 0.5) * 5; - - if (this.links_render_mode == LiteGraph.SPLINE_LINK) { - ctx.moveTo(a[0], a[1] + offsety); - var start_offset_x = 0; - var start_offset_y = 0; - var end_offset_x = 0; - var end_offset_y = 0; - switch (start_dir) { - case LiteGraph.LEFT: - start_offset_x = dist * -0.25; - break; - case LiteGraph.RIGHT: - start_offset_x = dist * 0.25; - break; - case LiteGraph.UP: - start_offset_y = dist * -0.25; - break; - case LiteGraph.DOWN: - start_offset_y = dist * 0.25; - break; - } - switch (end_dir) { - case LiteGraph.LEFT: - end_offset_x = dist * -0.25; - break; - case LiteGraph.RIGHT: - end_offset_x = dist * 0.25; - break; - case LiteGraph.UP: - end_offset_y = dist * -0.25; - break; - case LiteGraph.DOWN: - end_offset_y = dist * 0.25; - break; - } - ctx.bezierCurveTo( - a[0] + start_offset_x, - a[1] + start_offset_y + offsety, - b[0] + end_offset_x, - b[1] + end_offset_y + offsety, - b[0], - b[1] + offsety - ); - } else if (this.links_render_mode == LiteGraph.LINEAR_LINK) { - ctx.moveTo(a[0], a[1] + offsety); - var start_offset_x = 0; - var start_offset_y = 0; - var end_offset_x = 0; - var end_offset_y = 0; - switch (start_dir) { - case LiteGraph.LEFT: - start_offset_x = -1; - break; - case LiteGraph.RIGHT: - start_offset_x = 1; - break; - case LiteGraph.UP: - start_offset_y = -1; - break; - case LiteGraph.DOWN: - start_offset_y = 1; - break; - } - switch (end_dir) { - case LiteGraph.LEFT: - end_offset_x = -1; - break; - case LiteGraph.RIGHT: - end_offset_x = 1; - break; - case LiteGraph.UP: - end_offset_y = -1; - break; - case LiteGraph.DOWN: - end_offset_y = 1; - break; - } - var l = 15; - ctx.lineTo( - a[0] + start_offset_x * l, - a[1] + start_offset_y * l + offsety - ); - ctx.lineTo( - b[0] + end_offset_x * l, - b[1] + end_offset_y * l + offsety - ); - ctx.lineTo(b[0], b[1] + offsety); - } else if (this.links_render_mode == LiteGraph.STRAIGHT_LINK) { - ctx.moveTo(a[0], a[1]); - var start_x = a[0]; - var start_y = a[1]; - var end_x = b[0]; - var end_y = b[1]; - if (start_dir == LiteGraph.RIGHT) { - start_x += 10; - } else { - start_y += 10; - } - if (end_dir == LiteGraph.LEFT) { - end_x -= 10; - } else { - end_y -= 10; - } - ctx.lineTo(start_x, start_y); - ctx.lineTo((start_x + end_x) * 0.5, start_y); - ctx.lineTo((start_x + end_x) * 0.5, end_y); - ctx.lineTo(end_x, end_y); - ctx.lineTo(b[0], b[1]); - } else { - return; - } //unknown - } - - //rendering the outline of the connection can be a little bit slow - if ( - this.render_connections_border && - this.ds.scale > 0.6 && - !skip_border - ) { - ctx.strokeStyle = "rgba(0,0,0,0.5)"; - ctx.stroke(); - } - - ctx.lineWidth = this.connections_width; - ctx.fillStyle = ctx.strokeStyle = color; - ctx.stroke(); - //end line shape - - var pos = this.computeConnectionPoint(a, b, 0.5, start_dir, end_dir); - if (link && link._pos) { - link._pos[0] = pos[0]; - link._pos[1] = pos[1]; - } - - //render arrow in the middle - if ( - this.ds.scale >= 0.6 && - this.highquality_render && - end_dir != LiteGraph.CENTER - ) { - //render arrow - if (this.render_connection_arrows) { - //compute two points in the connection - var posA = this.computeConnectionPoint( - a, - b, - 0.25, - start_dir, - end_dir - ); - var posB = this.computeConnectionPoint( - a, - b, - 0.26, - start_dir, - end_dir - ); - var posC = this.computeConnectionPoint( - a, - b, - 0.75, - start_dir, - end_dir - ); - var posD = this.computeConnectionPoint( - a, - b, - 0.76, - start_dir, - end_dir - ); - - //compute the angle between them so the arrow points in the right direction - var angleA = 0; - var angleB = 0; - if (this.render_curved_connections) { - angleA = -Math.atan2(posB[0] - posA[0], posB[1] - posA[1]); - angleB = -Math.atan2(posD[0] - posC[0], posD[1] - posC[1]); - } else { - angleB = angleA = b[1] > a[1] ? 0 : Math.PI; - } - - //render arrow - ctx.save(); - ctx.translate(posA[0], posA[1]); - ctx.rotate(angleA); - ctx.beginPath(); - ctx.moveTo(-5, -3); - ctx.lineTo(0, +7); - ctx.lineTo(+5, -3); - ctx.fill(); - ctx.restore(); - ctx.save(); - ctx.translate(posC[0], posC[1]); - ctx.rotate(angleB); - ctx.beginPath(); - ctx.moveTo(-5, -3); - ctx.lineTo(0, +7); - ctx.lineTo(+5, -3); - ctx.fill(); - ctx.restore(); - } - - //circle - ctx.beginPath(); - ctx.arc(pos[0], pos[1], 5, 0, Math.PI * 2); - ctx.fill(); - } - - //render flowing points - if (flow) { - ctx.fillStyle = color; - for (var i = 0; i < 5; ++i) { - var f = (LiteGraph.getTime() * 0.001 + i * 0.2) % 1; - var pos = this.computeConnectionPoint( - a, - b, - f, - start_dir, - end_dir - ); - ctx.beginPath(); - ctx.arc(pos[0], pos[1], 5, 0, 2 * Math.PI); - ctx.fill(); - } - } - }; - - //returns the link center point based on curvature - LGraphCanvas.prototype.computeConnectionPoint = function( - a, - b, - t, - start_dir, - end_dir - ) { - start_dir = start_dir || LiteGraph.RIGHT; - end_dir = end_dir || LiteGraph.LEFT; - - var dist = distance(a, b); - var p0 = a; - var p1 = [a[0], a[1]]; - var p2 = [b[0], b[1]]; - var p3 = b; - - switch (start_dir) { - case LiteGraph.LEFT: - p1[0] += dist * -0.25; - break; - case LiteGraph.RIGHT: - p1[0] += dist * 0.25; - break; - case LiteGraph.UP: - p1[1] += dist * -0.25; - break; - case LiteGraph.DOWN: - p1[1] += dist * 0.25; - break; - } - switch (end_dir) { - case LiteGraph.LEFT: - p2[0] += dist * -0.25; - break; - case LiteGraph.RIGHT: - p2[0] += dist * 0.25; - break; - case LiteGraph.UP: - p2[1] += dist * -0.25; - break; - case LiteGraph.DOWN: - p2[1] += dist * 0.25; - break; - } - - var c1 = (1 - t) * (1 - t) * (1 - t); - var c2 = 3 * ((1 - t) * (1 - t)) * t; - var c3 = 3 * (1 - t) * (t * t); - var c4 = t * t * t; - - var x = c1 * p0[0] + c2 * p1[0] + c3 * p2[0] + c4 * p3[0]; - var y = c1 * p0[1] + c2 * p1[1] + c3 * p2[1] + c4 * p3[1]; - return [x, y]; - }; - - LGraphCanvas.prototype.drawExecutionOrder = function(ctx) { - ctx.shadowColor = "transparent"; - ctx.globalAlpha = 0.25; - - ctx.textAlign = "center"; - ctx.strokeStyle = "white"; - ctx.globalAlpha = 0.75; - - var visible_nodes = this.visible_nodes; - for (var i = 0; i < visible_nodes.length; ++i) { - var node = visible_nodes[i]; - ctx.fillStyle = "black"; - ctx.fillRect( - node.pos[0] - LiteGraph.NODE_TITLE_HEIGHT, - node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, - LiteGraph.NODE_TITLE_HEIGHT, - LiteGraph.NODE_TITLE_HEIGHT - ); - if (node.order == 0) { - ctx.strokeRect( - node.pos[0] - LiteGraph.NODE_TITLE_HEIGHT + 0.5, - node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5, - LiteGraph.NODE_TITLE_HEIGHT, - LiteGraph.NODE_TITLE_HEIGHT - ); - } - ctx.fillStyle = "#FFF"; - ctx.fillText( - node.order, - node.pos[0] + LiteGraph.NODE_TITLE_HEIGHT * -0.5, - node.pos[1] - 6 - ); - } - ctx.globalAlpha = 1; - }; - - /** - * draws the widgets stored inside a node - * @method drawNodeWidgets - **/ - LGraphCanvas.prototype.drawNodeWidgets = function( - node, - posY, - ctx, - active_widget - ) { - if (!node.widgets || !node.widgets.length) { - return 0; - } - var width = node.size[0]; - var widgets = node.widgets; - posY += 2; - var H = LiteGraph.NODE_WIDGET_HEIGHT; - var show_text = this.ds.scale > 0.5; - ctx.save(); - ctx.globalAlpha = this.editor_alpha; - var outline_color = LiteGraph.WIDGET_OUTLINE_COLOR; - var background_color = LiteGraph.WIDGET_BGCOLOR; - var text_color = LiteGraph.WIDGET_TEXT_COLOR; - var secondary_text_color = LiteGraph.WIDGET_SECONDARY_TEXT_COLOR; - var margin = 15; - - for (var i = 0; i < widgets.length; ++i) { - var w = widgets[i]; - var y = posY; - if (w.y) { - y = w.y; - } - w.last_y = y; - ctx.strokeStyle = outline_color; - ctx.fillStyle = "#222"; - ctx.textAlign = "left"; - if(w.disabled) - ctx.globalAlpha *= 0.5; - - switch (w.type) { - case "button": - if (w.clicked) { - ctx.fillStyle = "#AAA"; - w.clicked = false; - this.dirty_canvas = true; - } - ctx.fillRect(margin, y, width - margin * 2, H); - if(show_text) - ctx.strokeRect( margin, y, width - margin * 2, H ); - if (show_text) { - ctx.textAlign = "center"; - ctx.fillStyle = text_color; - ctx.fillText(w.name, width * 0.5, y + H * 0.7); - } - break; - case "toggle": - ctx.textAlign = "left"; - ctx.strokeStyle = outline_color; - ctx.fillStyle = background_color; - ctx.beginPath(); - if (show_text) - ctx.roundRect(margin, posY, width - margin * 2, H, H * 0.5); - else - ctx.rect(margin, posY, width - margin * 2, H ); - ctx.fill(); - if(show_text) - ctx.stroke(); - ctx.fillStyle = w.value ? "#89A" : "#333"; - ctx.beginPath(); - ctx.arc( width - margin * 2, y + H * 0.5, H * 0.36, 0, Math.PI * 2 ); - ctx.fill(); - if (show_text) { - ctx.fillStyle = secondary_text_color; - if (w.name != null) { - ctx.fillText(w.name, margin * 2, y + H * 0.7); - } - ctx.fillStyle = w.value ? text_color : secondary_text_color; - ctx.textAlign = "right"; - ctx.fillText( - w.value - ? w.options.on || "true" - : w.options.off || "false", - width - 40, - y + H * 0.7 - ); - } - break; - case "slider": - ctx.fillStyle = background_color; - ctx.fillRect(margin, y, width - margin * 2, H); - var range = w.options.max - w.options.min; - var nvalue = (w.value - w.options.min) / range; - ctx.fillStyle = active_widget == w ? "#89A" : "#678"; - ctx.fillRect(margin, y, nvalue * (width - margin * 2), H); - if(show_text) - ctx.strokeRect(margin, y, width - margin * 2, H); - if (w.marker) { - var marker_nvalue = (w.marker - w.options.min) / range; - ctx.fillStyle = "#AA9"; - ctx.fillRect( margin + marker_nvalue * (width - margin * 2), y, 2, H ); - } - if (show_text) { - ctx.textAlign = "center"; - ctx.fillStyle = text_color; - ctx.fillText( - w.name + " " + Number(w.value).toFixed(3), - width * 0.5, - y + H * 0.7 - ); - } - break; - case "number": - case "combo": - ctx.textAlign = "left"; - ctx.strokeStyle = outline_color; - ctx.fillStyle = background_color; - ctx.beginPath(); - if(show_text) - ctx.roundRect(margin, posY, width - margin * 2, H, H * 0.5); - else - ctx.rect(margin, posY, width - margin * 2, H ); - ctx.fill(); - if (show_text) { - ctx.stroke(); - ctx.fillStyle = text_color; - ctx.beginPath(); - ctx.moveTo(margin + 16, posY + 5); - ctx.lineTo(margin + 6, posY + H * 0.5); - ctx.lineTo(margin + 16, posY + H - 5); - ctx.fill(); - ctx.beginPath(); - ctx.moveTo(width - margin - 16, posY + 5); - ctx.lineTo(width - margin - 6, posY + H * 0.5); - ctx.lineTo(width - margin - 16, posY + H - 5); - ctx.fill(); - ctx.fillStyle = secondary_text_color; - ctx.fillText(w.name, margin * 2 + 5, y + H * 0.7); - ctx.fillStyle = text_color; - ctx.textAlign = "right"; - if (w.type == "number") { - ctx.fillText( - Number(w.value).toFixed( - w.options.precision !== undefined - ? w.options.precision - : 3 - ), - width - margin * 2 - 20, - y + H * 0.7 - ); - } else { - var v = w.value; - if( w.options.values ) - { - var values = w.options.values; - if( values.constructor === Function ) - values = values(); - if(values && values.constructor !== Array) - v = values[ w.value ]; - } - ctx.fillText( - v, - width - margin * 2 - 20, - y + H * 0.7 - ); - } - } - break; - case "string": - case "text": - ctx.textAlign = "left"; - ctx.strokeStyle = outline_color; - ctx.fillStyle = background_color; - ctx.beginPath(); - if (show_text) - ctx.roundRect(margin, posY, width - margin * 2, H, H * 0.5); - else - ctx.rect( margin, posY, width - margin * 2, H ); - ctx.fill(); - if (show_text) { - ctx.save(); - ctx.beginPath(); - ctx.rect(margin, posY, width - margin * 2, H); - ctx.clip(); - - ctx.stroke(); - ctx.fillStyle = secondary_text_color; - if (w.name != null) { - ctx.fillText(w.name, margin * 2, y + H * 0.7); - } - ctx.fillStyle = text_color; - ctx.textAlign = "right"; - ctx.fillText(String(w.value).substr(0,30), width - margin * 2, y + H * 0.7); //30 chars max - ctx.restore(); - } - break; - default: - if (w.draw) { - w.draw(ctx, node, w, y, H); - } - break; - } - posY += H + 4; - ctx.globalAlpha = this.editor_alpha; - - } - ctx.restore(); - ctx.textAlign = "left"; - }; - - /** - * process an event on widgets - * @method processNodeWidgets - **/ - LGraphCanvas.prototype.processNodeWidgets = function( - node, - pos, - event, - active_widget - ) { - if (!node.widgets || !node.widgets.length) { - return null; - } - - var x = pos[0] - node.pos[0]; - var y = pos[1] - node.pos[1]; - var width = node.size[0]; - var that = this; - var ref_window = this.getCanvasWindow(); - - for (var i = 0; i < node.widgets.length; ++i) { - var w = node.widgets[i]; - if(!w || w.disabled) - continue; - if ( w == active_widget || (x > 6 && x < width - 12 && y > w.last_y && y < w.last_y + LiteGraph.NODE_WIDGET_HEIGHT) ) { - //inside widget - switch (w.type) { - case "button": - if (event.type === "mousemove") { - break; - } - if (w.callback) { - setTimeout(function() { - w.callback(w, that, node, pos, event); - }, 20); - } - w.clicked = true; - this.dirty_canvas = true; - break; - case "slider": - var range = w.options.max - w.options.min; - var nvalue = Math.clamp((x - 10) / (width - 20), 0, 1); - w.value = - w.options.min + - (w.options.max - w.options.min) * nvalue; - if (w.callback) { - setTimeout(function() { - inner_value_change(w, w.value); - }, 20); - } - this.dirty_canvas = true; - break; - case "number": - case "combo": - var old_value = w.value; - if (event.type == "mousemove" && w.type == "number") { - w.value += event.deltaX * 0.1 * (w.options.step || 1); - if ( w.options.min != null && w.value < w.options.min ) { - w.value = w.options.min; - } - if ( w.options.max != null && w.value > w.options.max ) { - w.value = w.options.max; - } - } else if (event.type == "mousedown") { - var values = w.options.values; - if (values && values.constructor === Function) { - values = w.options.values(w, node); - } - var values_list = null; - - if( w.type != "number") - values_list = values.constructor === Array ? values : Object.keys(values); - - var delta = x < 40 ? -1 : x > width - 40 ? 1 : 0; - if (w.type == "number") { - w.value += delta * 0.1 * (w.options.step || 1); - if ( w.options.min != null && w.value < w.options.min ) { - w.value = w.options.min; - } - if ( w.options.max != null && w.value > w.options.max ) { - w.value = w.options.max; - } - } else if (delta) { //clicked in arrow, used for combos - var index = -1; - if(values.constructor === Object) - index = values_list.indexOf( String( w.value ) ) + delta; - else - index = values_list.indexOf( w.value ) + delta; - if (index >= values_list.length) { - index = values_list.length - 1; - } - if (index < 0) { - index = 0; - } - if( values.constructor === Array ) - w.value = values[index]; - else - w.value = index; - } else { //combo clicked - var text_values = values != values_list ? Object.values(values) : values; - var menu = new LiteGraph.ContextMenu(text_values, { - scale: Math.max(1, this.ds.scale), - event: event, - className: "dark", - callback: inner_clicked.bind(w) - }, - ref_window); - function inner_clicked(v, option, event) { - if(values != values_list) - v = text_values.indexOf(v); - this.value = v; - inner_value_change(this, v); - that.dirty_canvas = true; - return false; - } - } - } //end mousedown - else if(event.type == "mouseup" && w.type == "number") - { - var delta = x < 40 ? -1 : x > width - 40 ? 1 : 0; - if (event.click_time < 200 && delta == 0) { - this.prompt("Value",w.value,function(v) { - this.value = Number(v); - inner_value_change(this, this.value); - }.bind(w), - event); - } - } - - if( old_value != w.value ) - setTimeout( - function() { - inner_value_change(this, this.value); - }.bind(w), - 20 - ); - this.dirty_canvas = true; - break; - case "toggle": - if (event.type == "mousedown") { - w.value = !w.value; - setTimeout(function() { - inner_value_change(w, w.value); - }, 20); - } - break; - case "string": - case "text": - if (event.type == "mousedown") { - this.prompt("Value",w.value,function(v) { - this.value = v; - inner_value_change(this, v); - }.bind(w), - event); - } - break; - default: - if (w.mouse) { - w.mouse(ctx, event, [x, y], node); - } - break; - } //end switch - - return w; - } - } - - function inner_value_change(widget, value) { - widget.value = value; - if ( widget.options && widget.options.property && node.properties[widget.options.property] !== undefined ) { - node.setProperty( widget.options.property, value ); - } - if (widget.callback) { - widget.callback(widget.value, that, node, pos, event); - } - } - - return null; - }; - - /** - * draws every group area in the background - * @method drawGroups - **/ - LGraphCanvas.prototype.drawGroups = function(canvas, ctx) { - if (!this.graph) { - return; - } - - var groups = this.graph._groups; - - ctx.save(); - ctx.globalAlpha = 0.5 * this.editor_alpha; - - for (var i = 0; i < groups.length; ++i) { - var group = groups[i]; - - if (!overlapBounding(this.visible_area, group._bounding)) { - continue; - } //out of the visible area - - ctx.fillStyle = group.color || "#335"; - ctx.strokeStyle = group.color || "#335"; - var pos = group._pos; - var size = group._size; - ctx.globalAlpha = 0.25 * this.editor_alpha; - ctx.beginPath(); - ctx.rect(pos[0] + 0.5, pos[1] + 0.5, size[0], size[1]); - ctx.fill(); - ctx.globalAlpha = this.editor_alpha; - ctx.stroke(); - - ctx.beginPath(); - ctx.moveTo(pos[0] + size[0], pos[1] + size[1]); - ctx.lineTo(pos[0] + size[0] - 10, pos[1] + size[1]); - ctx.lineTo(pos[0] + size[0], pos[1] + size[1] - 10); - ctx.fill(); - - var font_size = - group.font_size || LiteGraph.DEFAULT_GROUP_FONT_SIZE; - ctx.font = font_size + "px Arial"; - ctx.fillText(group.title, pos[0] + 4, pos[1] + font_size); - } - - ctx.restore(); - }; - - LGraphCanvas.prototype.adjustNodesSize = function() { - var nodes = this.graph._nodes; - for (var i = 0; i < nodes.length; ++i) { - nodes[i].size = nodes[i].computeSize(); - } - this.setDirty(true, true); - }; - - /** - * resizes the canvas to a given size, if no size is passed, then it tries to fill the parentNode - * @method resize - **/ - LGraphCanvas.prototype.resize = function(width, height) { - if (!width && !height) { - var parent = this.canvas.parentNode; - width = parent.offsetWidth; - height = parent.offsetHeight; - } - - if (this.canvas.width == width && this.canvas.height == height) { - return; - } - - this.canvas.width = width; - this.canvas.height = height; - this.bgcanvas.width = this.canvas.width; - this.bgcanvas.height = this.canvas.height; - this.setDirty(true, true); - }; - - /** - * switches to live mode (node shapes are not rendered, only the content) - * this feature was designed when graphs where meant to create user interfaces - * @method switchLiveMode - **/ - LGraphCanvas.prototype.switchLiveMode = function(transition) { - if (!transition) { - this.live_mode = !this.live_mode; - this.dirty_canvas = true; - this.dirty_bgcanvas = true; - return; - } - - var self = this; - var delta = this.live_mode ? 1.1 : 0.9; - if (this.live_mode) { - this.live_mode = false; - this.editor_alpha = 0.1; - } - - var t = setInterval(function() { - self.editor_alpha *= delta; - self.dirty_canvas = true; - self.dirty_bgcanvas = true; - - if (delta < 1 && self.editor_alpha < 0.01) { - clearInterval(t); - if (delta < 1) { - self.live_mode = true; - } - } - if (delta > 1 && self.editor_alpha > 0.99) { - clearInterval(t); - self.editor_alpha = 1; - } - }, 1); - }; - - LGraphCanvas.prototype.onNodeSelectionChange = function(node) { - return; //disabled - }; - - LGraphCanvas.prototype.touchHandler = function(event) { - //alert("foo"); - var touches = event.changedTouches, - first = touches[0], - type = ""; - - switch (event.type) { - case "touchstart": - type = "mousedown"; - break; - case "touchmove": - type = "mousemove"; - break; - case "touchend": - type = "mouseup"; - break; - default: - return; - } - - //initMouseEvent(type, canBubble, cancelable, view, clickCount, - // screenX, screenY, clientX, clientY, ctrlKey, - // altKey, shiftKey, metaKey, button, relatedTarget); - - var window = this.getCanvasWindow(); - var document = window.document; - - var simulatedEvent = document.createEvent("MouseEvent"); - simulatedEvent.initMouseEvent( - type, - true, - true, - window, - 1, - first.screenX, - first.screenY, - first.clientX, - first.clientY, - false, - false, - false, - false, - 0 /*left*/, - null - ); - first.target.dispatchEvent(simulatedEvent); - event.preventDefault(); - }; - - /* CONTEXT MENU ********************/ - - LGraphCanvas.onGroupAdd = function(info, entry, mouse_event) { - var canvas = LGraphCanvas.active_canvas; - var ref_window = canvas.getCanvasWindow(); - - var group = new LiteGraph.LGraphGroup(); - group.pos = canvas.convertEventToCanvasOffset(mouse_event); - canvas.graph.add(group); - }; - - LGraphCanvas.onMenuAdd = function(node, options, e, prev_menu, callback) { - var canvas = LGraphCanvas.active_canvas; - var ref_window = canvas.getCanvasWindow(); - - var values = LiteGraph.getNodeTypesCategories( canvas.filter ); - var entries = []; - for (var i in values) { - if (values[i]) { - entries.push({ value: values[i], content: values[i], has_submenu: true }); - } - } - - //show categories - var menu = new LiteGraph.ContextMenu( entries, { event: e, callback: inner_clicked, parentMenu: prev_menu }, ref_window ); - - function inner_clicked(v, option, e) { - var category = v.value; - var node_types = LiteGraph.getNodeTypesInCategory( category, canvas.filter ); - var values = []; - for (var i in node_types) { - if (!node_types[i].skip_list) { - values.push({ - content: node_types[i].title, - value: node_types[i].type - }); - } - } - - new LiteGraph.ContextMenu( values, { event: e, callback: inner_create, parentMenu: menu }, ref_window ); - return false; - } - - function inner_create(v, e) { - var first_event = prev_menu.getFirstEvent(); - var node = LiteGraph.createNode(v.value); - if (node) { - node.pos = canvas.convertEventToCanvasOffset(first_event); - canvas.graph.add(node); - } - if(callback) - callback(node); - } - - return false; - }; - - LGraphCanvas.onMenuCollapseAll = function() {}; - - LGraphCanvas.onMenuNodeEdit = function() {}; - - LGraphCanvas.showMenuNodeOptionalInputs = function( - v, - options, - e, - prev_menu, - node - ) { - if (!node) { - return; - } - - var that = this; - var canvas = LGraphCanvas.active_canvas; - var ref_window = canvas.getCanvasWindow(); - - var options = node.optional_inputs; - if (node.onGetInputs) { - options = node.onGetInputs(); - } - - var entries = []; - if (options) { - for (var i in options) { - var entry = options[i]; - if (!entry) { - entries.push(null); - continue; - } - var label = entry[0]; - if (entry[2] && entry[2].label) { - label = entry[2].label; - } - var data = { content: label, value: entry }; - if (entry[1] == LiteGraph.ACTION) { - data.className = "event"; - } - entries.push(data); - } - } - - if (this.onMenuNodeInputs) { - entries = this.onMenuNodeInputs(entries); - } - - if (!entries.length) { - return; - } - - var menu = new LiteGraph.ContextMenu( - entries, - { - event: e, - callback: inner_clicked, - parentMenu: prev_menu, - node: node - }, - ref_window - ); - - function inner_clicked(v, e, prev) { - if (!node) { - return; - } - - if (v.callback) { - v.callback.call(that, node, v, e, prev); - } - - if (v.value) { - node.addInput(v.value[0], v.value[1], v.value[2]); - node.setDirtyCanvas(true, true); - } - } - - return false; - }; - - LGraphCanvas.showMenuNodeOptionalOutputs = function( - v, - options, - e, - prev_menu, - node - ) { - if (!node) { - return; - } - - var that = this; - var canvas = LGraphCanvas.active_canvas; - var ref_window = canvas.getCanvasWindow(); - - var options = node.optional_outputs; - if (node.onGetOutputs) { - options = node.onGetOutputs(); - } - - var entries = []; - if (options) { - for (var i in options) { - var entry = options[i]; - if (!entry) { - //separator? - entries.push(null); - continue; - } - - if ( - node.flags && - node.flags.skip_repeated_outputs && - node.findOutputSlot(entry[0]) != -1 - ) { - continue; - } //skip the ones already on - var label = entry[0]; - if (entry[2] && entry[2].label) { - label = entry[2].label; - } - var data = { content: label, value: entry }; - if (entry[1] == LiteGraph.EVENT) { - data.className = "event"; - } - entries.push(data); - } - } - - if (this.onMenuNodeOutputs) { - entries = this.onMenuNodeOutputs(entries); - } - - if (!entries.length) { - return; - } - - var menu = new LiteGraph.ContextMenu( - entries, - { - event: e, - callback: inner_clicked, - parentMenu: prev_menu, - node: node - }, - ref_window - ); - - function inner_clicked(v, e, prev) { - if (!node) { - return; - } - - if (v.callback) { - v.callback.call(that, node, v, e, prev); - } - - if (!v.value) { - return; - } - - var value = v.value[1]; - - if ( - value && - (value.constructor === Object || value.constructor === Array) - ) { - //submenu why? - var entries = []; - for (var i in value) { - entries.push({ content: i, value: value[i] }); - } - new LiteGraph.ContextMenu(entries, { - event: e, - callback: inner_clicked, - parentMenu: prev_menu, - node: node - }); - return false; - } else { - node.addOutput(v.value[0], v.value[1], v.value[2]); - node.setDirtyCanvas(true, true); - } - } - - return false; - }; - - LGraphCanvas.onShowMenuNodeProperties = function( - value, - options, - e, - prev_menu, - node - ) { - if (!node || !node.properties) { - return; - } - - var that = this; - var canvas = LGraphCanvas.active_canvas; - var ref_window = canvas.getCanvasWindow(); - - var entries = []; - for (var i in node.properties) { - var value = node.properties[i] !== undefined ? node.properties[i] : " "; - if( typeof value == "object" ) - value = JSON.stringify(value); - //value could contain invalid html characters, clean that - value = LGraphCanvas.decodeHTML(value); - entries.push({ - content: - "" + - i + - "" + - "" + - value + - "", - value: i - }); - } - if (!entries.length) { - return; - } - - var menu = new LiteGraph.ContextMenu( - entries, - { - event: e, - callback: inner_clicked, - parentMenu: prev_menu, - allow_html: true, - node: node - }, - ref_window - ); - - function inner_clicked(v, options, e, prev) { - if (!node) { - return; - } - var rect = this.getBoundingClientRect(); - canvas.showEditPropertyValue(node, v.value, { - position: [rect.left, rect.top] - }); - } - - return false; - }; - - LGraphCanvas.decodeHTML = function(str) { - var e = document.createElement("div"); - e.innerText = str; - return e.innerHTML; - }; - - LGraphCanvas.onResizeNode = function(value, options, e, menu, node) { - if (!node) { - return; - } - node.size = node.computeSize(); - node.setDirtyCanvas(true, true); - }; - - LGraphCanvas.prototype.showLinkMenu = function(link, e) { - var that = this; - console.log(link); - var options = ["Add Node",null,"Delete"]; - var menu = new LiteGraph.ContextMenu(options, { - event: e, - title: link.data != null ? link.data.constructor.name : null, - callback: inner_clicked - }); - - function inner_clicked(v,options,e) { - switch (v) { - case "Add Node": - LGraphCanvas.onMenuAdd(null, null, e, menu, function(node){ - console.log("node autoconnect"); - var node_left = that.graph.getNodeById( link.origin_id ); - var node_right = that.graph.getNodeById( link.target_id ); - if(!node.inputs || !node.inputs.length || !node.outputs || !node.outputs.length) - return; - if( node_left.outputs[ link.origin_slot ].type == node.inputs[0].type && node.outputs[0].type == node_right.inputs[0].type ) - { - node_left.connect( link.origin_slot, node, 0 ); - node.connect( 0, node_right, link.target_slot ); - node.pos[0] -= node.size[0] * 0.5; - } - }); - break; - case "Delete": - that.graph.removeLink(link.id); - break; - default: - } - } - - return false; - }; - - LGraphCanvas.onShowPropertyEditor = function(item, options, e, menu, node) { - var input_html = ""; - var property = item.property || "title"; - var value = node[property]; - - var dialog = document.createElement("div"); - dialog.className = "graphdialog"; - dialog.innerHTML = - ""; - var title = dialog.querySelector(".name"); - title.innerText = property; - var input = dialog.querySelector("input"); - if (input) { - input.value = value; - input.addEventListener("blur", function(e) { - this.focus(); - }); - input.addEventListener("keydown", function(e) { - if (e.keyCode != 13) { - return; - } - inner(); - e.preventDefault(); - e.stopPropagation(); - }); - } - - var graphcanvas = LGraphCanvas.active_canvas; - var canvas = graphcanvas.canvas; - - var rect = canvas.getBoundingClientRect(); - var offsetx = -20; - var offsety = -20; - if (rect) { - offsetx -= rect.left; - offsety -= rect.top; - } - - if (event) { - dialog.style.left = event.clientX + offsetx + "px"; - dialog.style.top = event.clientY + offsety + "px"; - } else { - dialog.style.left = canvas.width * 0.5 + offsetx + "px"; - dialog.style.top = canvas.height * 0.5 + offsety + "px"; - } - - var button = dialog.querySelector("button"); - button.addEventListener("click", inner); - canvas.parentNode.appendChild(dialog); - - function inner() { - setValue(input.value); - } - - function setValue(value) { - if (item.type == "Number") { - value = Number(value); - } else if (item.type == "Boolean") { - value = Boolean(value); - } - node[property] = value; - if (dialog.parentNode) { - dialog.parentNode.removeChild(dialog); - } - node.setDirtyCanvas(true, true); - } - }; - - LGraphCanvas.prototype.prompt = function(title, value, callback, event) { - var that = this; - var input_html = ""; - title = title || ""; - - var modified = false; - - var dialog = document.createElement("div"); - dialog.className = "graphdialog rounded"; - dialog.innerHTML = - " "; - dialog.close = function() { - that.prompt_box = null; - if (dialog.parentNode) { - dialog.parentNode.removeChild(dialog); - } - }; - - if (this.ds.scale > 1) { - dialog.style.transform = "scale(" + this.ds.scale + ")"; - } - - dialog.addEventListener("mouseleave", function(e) { - if (!modified) { - dialog.close(); - } - }); - - if (that.prompt_box) { - that.prompt_box.close(); - } - that.prompt_box = dialog; - - var first = null; - var timeout = null; - var selected = null; - - var name_element = dialog.querySelector(".name"); - name_element.innerText = title; - var value_element = dialog.querySelector(".value"); - value_element.value = value; - - var input = dialog.querySelector("input"); - input.addEventListener("keydown", function(e) { - modified = true; - if (e.keyCode == 27) { - //ESC - dialog.close(); - } else if (e.keyCode == 13) { - if (callback) { - callback(this.value); - } - dialog.close(); - } else { - return; - } - e.preventDefault(); - e.stopPropagation(); - }); - - var button = dialog.querySelector("button"); - button.addEventListener("click", function(e) { - if (callback) { - callback(input.value); - } - that.setDirty(true); - dialog.close(); - }); - - var graphcanvas = LGraphCanvas.active_canvas; - var canvas = graphcanvas.canvas; - - var rect = canvas.getBoundingClientRect(); - var offsetx = -20; - var offsety = -20; - if (rect) { - offsetx -= rect.left; - offsety -= rect.top; - } - - if (event) { - dialog.style.left = event.clientX + offsetx + "px"; - dialog.style.top = event.clientY + offsety + "px"; - } else { - dialog.style.left = canvas.width * 0.5 + offsetx + "px"; - dialog.style.top = canvas.height * 0.5 + offsety + "px"; - } - - canvas.parentNode.appendChild(dialog); - setTimeout(function() { - input.focus(); - }, 10); - - return dialog; - }; - - LGraphCanvas.search_limit = -1; - LGraphCanvas.prototype.showSearchBox = function(event) { - var that = this; - var input_html = ""; - var graphcanvas = LGraphCanvas.active_canvas; - var canvas = graphcanvas.canvas; - var root_document = canvas.ownerDocument || document; - - var dialog = document.createElement("div"); - dialog.className = "litegraph litesearchbox graphdialog rounded"; - dialog.innerHTML = - "Search
"; - dialog.close = function() { - that.search_box = null; - root_document.body.focus(); - root_document.body.style.overflow = ""; - - setTimeout(function() { - that.canvas.focus(); - }, 20); //important, if canvas loses focus keys wont be captured - if (dialog.parentNode) { - dialog.parentNode.removeChild(dialog); - } - }; - - var timeout_close = null; - - if (this.ds.scale > 1) { - dialog.style.transform = "scale(" + this.ds.scale + ")"; - } - - dialog.addEventListener("mouseenter", function(e) { - if (timeout_close) { - clearTimeout(timeout_close); - timeout_close = null; - } - }); - - dialog.addEventListener("mouseleave", function(e) { - //dialog.close(); - timeout_close = setTimeout(function() { - dialog.close(); - }, 500); - }); - - if (that.search_box) { - that.search_box.close(); - } - that.search_box = dialog; - - var helper = dialog.querySelector(".helper"); - - var first = null; - var timeout = null; - var selected = null; - - var input = dialog.querySelector("input"); - if (input) { - input.addEventListener("blur", function(e) { - this.focus(); - }); - input.addEventListener("keydown", function(e) { - if (e.keyCode == 38) { - //UP - changeSelection(false); - } else if (e.keyCode == 40) { - //DOWN - changeSelection(true); - } else if (e.keyCode == 27) { - //ESC - dialog.close(); - } else if (e.keyCode == 13) { - if (selected) { - select(selected.innerHTML); - } else if (first) { - select(first); - } else { - dialog.close(); - } - } else { - if (timeout) { - clearInterval(timeout); - } - timeout = setTimeout(refreshHelper, 10); - return; - } - e.preventDefault(); - e.stopPropagation(); - e.stopImmediatePropagation(); - return true; - }); - } - - if( root_document.fullscreenElement ) - root_document.fullscreenElement.appendChild(dialog); - else - { - root_document.body.appendChild(dialog); - root_document.body.style.overflow = "hidden"; - } - - //compute best position - var rect = canvas.getBoundingClientRect(); - - var left = ( event ? event.clientX : (rect.left + rect.width * 0.5) ) - 80; - var top = ( event ? event.clientY : (rect.top + rect.height * 0.5) ) - 20; - dialog.style.left = left + "px"; - dialog.style.top = top + "px"; - - //To avoid out of screen problems - if(event.layerY > (rect.height - 200)) - helper.style.maxHeight = (rect.height - event.layerY - 20) + "px"; - - /* - var offsetx = -20; - var offsety = -20; - if (rect) { - offsetx -= rect.left; - offsety -= rect.top; - } - - if (event) { - dialog.style.left = event.clientX + offsetx + "px"; - dialog.style.top = event.clientY + offsety + "px"; - } else { - dialog.style.left = canvas.width * 0.5 + offsetx + "px"; - dialog.style.top = canvas.height * 0.5 + offsety + "px"; - } - canvas.parentNode.appendChild(dialog); - */ - - input.focus(); - - function select(name) { - if (name) { - if (that.onSearchBoxSelection) { - that.onSearchBoxSelection(name, event, graphcanvas); - } else { - var extra = LiteGraph.searchbox_extras[name.toLowerCase()]; - if (extra) { - name = extra.type; - } - - var node = LiteGraph.createNode(name); - if (node) { - node.pos = graphcanvas.convertEventToCanvasOffset( - event - ); - graphcanvas.graph.add(node); - } - - if (extra && extra.data) { - if (extra.data.properties) { - for (var i in extra.data.properties) { - node.addProperty( i, extra.data.properties[i] ); - } - } - if (extra.data.inputs) { - node.inputs = []; - for (var i in extra.data.inputs) { - node.addOutput( - extra.data.inputs[i][0], - extra.data.inputs[i][1] - ); - } - } - if (extra.data.outputs) { - node.outputs = []; - for (var i in extra.data.outputs) { - node.addOutput( - extra.data.outputs[i][0], - extra.data.outputs[i][1] - ); - } - } - if (extra.data.title) { - node.title = extra.data.title; - } - if (extra.data.json) { - node.configure(extra.data.json); - } - } - } - } - - dialog.close(); - } - - function changeSelection(forward) { - var prev = selected; - if (selected) { - selected.classList.remove("selected"); - } - if (!selected) { - selected = forward - ? helper.childNodes[0] - : helper.childNodes[helper.childNodes.length]; - } else { - selected = forward - ? selected.nextSibling - : selected.previousSibling; - if (!selected) { - selected = prev; - } - } - if (!selected) { - return; - } - selected.classList.add("selected"); - selected.scrollIntoView({block: "end", behavior: "smooth"}); - } - - function refreshHelper() { - timeout = null; - var str = input.value; - first = null; - helper.innerHTML = ""; - if (!str) { - return; - } - - if (that.onSearchBox) { - var list = that.onSearchBox(helper, str, graphcanvas); - if (list) { - for (var i = 0; i < list.length; ++i) { - addResult(list[i]); - } - } - } else { - var c = 0; - str = str.toLowerCase(); - var filter = graphcanvas.filter || graphcanvas.graph.filter; - - //extras - for (var i in LiteGraph.searchbox_extras) { - var extra = LiteGraph.searchbox_extras[i]; - if (extra.desc.toLowerCase().indexOf(str) === -1) { - continue; - } - var ctor = LiteGraph.registered_node_types[ extra.type ]; - if( ctor && ctor.filter && ctor.filter != filter ) - continue; - addResult( extra.desc, "searchbox_extra" ); - if ( LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit ) { - break; - } - } - - var filtered = null; - if (Array.prototype.filter) { //filter supported - var keys = Object.keys( LiteGraph.registered_node_types ); //types - var filtered = keys.filter( inner_test_filter ); - } else { - filtered = []; - for (var i in LiteGraph.registered_node_types) { - if( inner_test_filter(i) ) - filtered.push(i); - } - } - - for (var i = 0; i < filtered.length; i++) { - addResult(filtered[i]); - if ( LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit ) { - break; - } - } - - function inner_test_filter( type ) - { - var ctor = LiteGraph.registered_node_types[ type ]; - if(filter && ctor.filter != filter ) - return false; - return type.toLowerCase().indexOf(str) !== -1; - } - } - - function addResult(type, className) { - var help = document.createElement("div"); - if (!first) { - first = type; - } - help.innerText = type; - help.dataset["type"] = escape(type); - help.className = "litegraph lite-search-item"; - if (className) { - help.className += " " + className; - } - help.addEventListener("click", function(e) { - select(unescape(this.dataset["type"])); - }); - helper.appendChild(help); - } - } - - return dialog; - }; - - LGraphCanvas.prototype.showEditPropertyValue = function( node, property, options ) { - if (!node || node.properties[property] === undefined) { - return; - } - - options = options || {}; - var that = this; - - var info = node.getPropertyInfo(property); - var type = info.type; - - var input_html = ""; - - if (type == "string" || type == "number" || type == "array" || type == "object") { - input_html = ""; - } else if (type == "enum" && info.values) { - input_html = ""; - } else if (type == "boolean") { - input_html = - ""; - } else { - console.warn("unknown type: " + type); - return; - } - - var dialog = this.createDialog( - "" + - property + - "" + - input_html + - "", - options - ); - - if (type == "enum" && info.values) { - var input = dialog.querySelector("select"); - input.addEventListener("change", function(e) { - setValue(e.target.value); - //var index = e.target.value; - //setValue( e.options[e.selectedIndex].value ); - }); - } else if (type == "boolean") { - var input = dialog.querySelector("input"); - if (input) { - input.addEventListener("click", function(e) { - setValue(!!input.checked); - }); - } - } else { - var input = dialog.querySelector("input"); - if (input) { - input.addEventListener("blur", function(e) { - this.focus(); - }); - var v = node.properties[property] !== undefined ? node.properties[property] : ""; - v = JSON.stringify(v); - input.value = v; - input.addEventListener("keydown", function(e) { - if (e.keyCode != 13) { - return; - } - inner(); - e.preventDefault(); - e.stopPropagation(); - }); - } - } - - var button = dialog.querySelector("button"); - button.addEventListener("click", inner); - - function inner() { - setValue(input.value); - } - - function setValue(value) { - if (typeof node.properties[property] == "number") { - value = Number(value); - } - if (type == "array" || type == "object") { - value = JSON.parse(value); - } - node.properties[property] = value; - if (node._graph) { - node._graph._version++; - } - if (node.onPropertyChanged) { - node.onPropertyChanged(property, value); - } - if(options.onclose) - options.onclose(); - dialog.close(); - node.setDirtyCanvas(true, true); - } - - return dialog; - }; - - LGraphCanvas.prototype.createDialog = function(html, options) { - options = options || {}; - - var dialog = document.createElement("div"); - dialog.className = "graphdialog"; - dialog.innerHTML = html; - - var rect = this.canvas.getBoundingClientRect(); - var offsetx = -20; - var offsety = -20; - if (rect) { - offsetx -= rect.left; - offsety -= rect.top; - } - - if (options.position) { - offsetx += options.position[0]; - offsety += options.position[1]; - } else if (options.event) { - offsetx += options.event.clientX; - offsety += options.event.clientY; - } //centered - else { - offsetx += this.canvas.width * 0.5; - offsety += this.canvas.height * 0.5; - } - - dialog.style.left = offsetx + "px"; - dialog.style.top = offsety + "px"; - - this.canvas.parentNode.appendChild(dialog); - - dialog.close = function() { - if (this.parentNode) { - this.parentNode.removeChild(this); - } - }; - - return dialog; - }; - - LGraphCanvas.onMenuNodeCollapse = function(value, options, e, menu, node) { - node.collapse(); - }; - - LGraphCanvas.onMenuNodePin = function(value, options, e, menu, node) { - node.pin(); - }; - - LGraphCanvas.onMenuNodeMode = function(value, options, e, menu, node) { - new LiteGraph.ContextMenu( - ["Always", "On Event", "On Trigger", "Never"], - { event: e, callback: inner_clicked, parentMenu: menu, node: node } - ); - - function inner_clicked(v) { - if (!node) { - return; - } - switch (v) { - case "On Event": - node.mode = LiteGraph.ON_EVENT; - break; - case "On Trigger": - node.mode = LiteGraph.ON_TRIGGER; - break; - case "Never": - node.mode = LiteGraph.NEVER; - break; - case "Always": - default: - node.mode = LiteGraph.ALWAYS; - break; - } - } - - return false; - }; - - LGraphCanvas.onMenuNodeColors = function(value, options, e, menu, node) { - if (!node) { - throw "no node for color"; - } - - var values = []; - values.push({ - value: null, - content: - "No color" - }); - - for (var i in LGraphCanvas.node_colors) { - var color = LGraphCanvas.node_colors[i]; - var value = { - value: i, - content: - "" + - i + - "" - }; - values.push(value); - } - new LiteGraph.ContextMenu(values, { - event: e, - callback: inner_clicked, - parentMenu: menu, - node: node - }); - - function inner_clicked(v) { - if (!node) { - return; - } - - var color = v.value ? LGraphCanvas.node_colors[v.value] : null; - if (color) { - if (node.constructor === LiteGraph.LGraphGroup) { - node.color = color.groupcolor; - } else { - node.color = color.color; - node.bgcolor = color.bgcolor; - } - } else { - delete node.color; - delete node.bgcolor; - } - node.setDirtyCanvas(true, true); - } - - return false; - }; - - LGraphCanvas.onMenuNodeShapes = function(value, options, e, menu, node) { - if (!node) { - throw "no node passed"; - } - - new LiteGraph.ContextMenu(LiteGraph.VALID_SHAPES, { - event: e, - callback: inner_clicked, - parentMenu: menu, - node: node - }); - - function inner_clicked(v) { - if (!node) { - return; - } - node.shape = v; - node.setDirtyCanvas(true); - } - - return false; - }; - - LGraphCanvas.onMenuNodeRemove = function(value, options, e, menu, node) { - if (!node) { - throw "no node passed"; - } - - if (node.removable === false) { - return; - } - - node.graph.remove(node); - node.setDirtyCanvas(true, true); - }; - - LGraphCanvas.onMenuNodeClone = function(value, options, e, menu, node) { - if (node.clonable == false) { - return; - } - var newnode = node.clone(); - if (!newnode) { - return; - } - newnode.pos = [node.pos[0] + 5, node.pos[1] + 5]; - node.graph.add(newnode); - node.setDirtyCanvas(true, true); - }; - - LGraphCanvas.node_colors = { - red: { color: "#322", bgcolor: "#533", groupcolor: "#A88" }, - brown: { color: "#332922", bgcolor: "#593930", groupcolor: "#b06634" }, - green: { color: "#232", bgcolor: "#353", groupcolor: "#8A8" }, - blue: { color: "#223", bgcolor: "#335", groupcolor: "#88A" }, - pale_blue: { - color: "#2a363b", - bgcolor: "#3f5159", - groupcolor: "#3f789e" - }, - cyan: { color: "#233", bgcolor: "#355", groupcolor: "#8AA" }, - purple: { color: "#323", bgcolor: "#535", groupcolor: "#a1309b" }, - yellow: { color: "#432", bgcolor: "#653", groupcolor: "#b58b2a" }, - black: { color: "#222", bgcolor: "#000", groupcolor: "#444" } - }; - - LGraphCanvas.prototype.getCanvasMenuOptions = function() { - var options = null; - if (this.getMenuOptions) { - options = this.getMenuOptions(); - } else { - options = [ - { - content: "Add Node", - has_submenu: true, - callback: LGraphCanvas.onMenuAdd - }, - { content: "Add Group", callback: LGraphCanvas.onGroupAdd } - //{content:"Collapse All", callback: LGraphCanvas.onMenuCollapseAll } - ]; - - if (this._graph_stack && this._graph_stack.length > 0) { - options.push(null, { - content: "Close subgraph", - callback: this.closeSubgraph.bind(this) - }); - } - } - - if (this.getExtraMenuOptions) { - var extra = this.getExtraMenuOptions(this, options); - if (extra) { - options = options.concat(extra); - } - } - - return options; - }; - - //called by processContextMenu to extract the menu list - LGraphCanvas.prototype.getNodeMenuOptions = function(node) { - var options = null; - - if (node.getMenuOptions) { - options = node.getMenuOptions(this); - } else { - options = [ - { - content: "Inputs", - has_submenu: true, - disabled: true, - callback: LGraphCanvas.showMenuNodeOptionalInputs - }, - { - content: "Outputs", - has_submenu: true, - disabled: true, - callback: LGraphCanvas.showMenuNodeOptionalOutputs - }, - null, - { - content: "Properties", - has_submenu: true, - callback: LGraphCanvas.onShowMenuNodeProperties - }, - null, - { - content: "Title", - callback: LGraphCanvas.onShowPropertyEditor - }, - { - content: "Mode", - has_submenu: true, - callback: LGraphCanvas.onMenuNodeMode - }, - { content: "Resize", callback: LGraphCanvas.onResizeNode }, - { - content: "Collapse", - callback: LGraphCanvas.onMenuNodeCollapse - }, - { content: "Pin", callback: LGraphCanvas.onMenuNodePin }, - { - content: "Colors", - has_submenu: true, - callback: LGraphCanvas.onMenuNodeColors - }, - { - content: "Shapes", - has_submenu: true, - callback: LGraphCanvas.onMenuNodeShapes - }, - null - ]; - } - - if (node.onGetInputs) { - var inputs = node.onGetInputs(); - if (inputs && inputs.length) { - options[0].disabled = false; - } - } - - if (node.onGetOutputs) { - var outputs = node.onGetOutputs(); - if (outputs && outputs.length) { - options[1].disabled = false; - } - } - - if (node.getExtraMenuOptions) { - var extra = node.getExtraMenuOptions(this); - if (extra) { - extra.push(null); - options = extra.concat(options); - } - } - - if (node.clonable !== false) { - options.push({ - content: "Clone", - callback: LGraphCanvas.onMenuNodeClone - }); - } - if (node.removable !== false) { - options.push(null, { - content: "Remove", - callback: LGraphCanvas.onMenuNodeRemove - }); - } - - if (node.graph && node.graph.onGetNodeMenuOptions) { - node.graph.onGetNodeMenuOptions(options, node); - } - - return options; - }; - - LGraphCanvas.prototype.getGroupMenuOptions = function(node) { - var o = [ - { content: "Title", callback: LGraphCanvas.onShowPropertyEditor }, - { - content: "Color", - has_submenu: true, - callback: LGraphCanvas.onMenuNodeColors - }, - { - content: "Font size", - property: "font_size", - type: "Number", - callback: LGraphCanvas.onShowPropertyEditor - }, - null, - { content: "Remove", callback: LGraphCanvas.onMenuNodeRemove } - ]; - - return o; - }; - - LGraphCanvas.prototype.processContextMenu = function(node, event) { - var that = this; - var canvas = LGraphCanvas.active_canvas; - var ref_window = canvas.getCanvasWindow(); - - var menu_info = null; - var options = { - event: event, - callback: inner_option_clicked, - extra: node - }; - - if(node) - options.title = node.type; - - //check if mouse is in input - var slot = null; - if (node) { - slot = node.getSlotInPosition(event.canvasX, event.canvasY); - LGraphCanvas.active_node = node; - } - - if (slot) { - //on slot - menu_info = []; - if ( - slot && - slot.output && - slot.output.links && - slot.output.links.length - ) { - menu_info.push({ content: "Disconnect Links", slot: slot }); - } - var _slot = slot.input || slot.output; - menu_info.push( - _slot.locked - ? "Cannot remove" - : { content: "Remove Slot", slot: slot } - ); - menu_info.push( - _slot.nameLocked - ? "Cannot rename" - : { content: "Rename Slot", slot: slot } - ); - options.title = - (slot.input ? slot.input.type : slot.output.type) || "*"; - if (slot.input && slot.input.type == LiteGraph.ACTION) { - options.title = "Action"; - } - if (slot.output && slot.output.type == LiteGraph.EVENT) { - options.title = "Event"; - } - } else { - if (node) { - //on node - menu_info = this.getNodeMenuOptions(node); - } else { - menu_info = this.getCanvasMenuOptions(); - var group = this.graph.getGroupOnPos( - event.canvasX, - event.canvasY - ); - if (group) { - //on group - menu_info.push(null, { - content: "Edit Group", - has_submenu: true, - submenu: { - title: "Group", - extra: group, - options: this.getGroupMenuOptions(group) - } - }); - } - } - } - - //show menu - if (!menu_info) { - return; - } - - var menu = new LiteGraph.ContextMenu(menu_info, options, ref_window); - - function inner_option_clicked(v, options, e) { - if (!v) { - return; - } - - if (v.content == "Remove Slot") { - var info = v.slot; - if (info.input) { - node.removeInput(info.slot); - } else if (info.output) { - node.removeOutput(info.slot); - } - return; - } else if (v.content == "Disconnect Links") { - var info = v.slot; - if (info.output) { - node.disconnectOutput(info.slot); - } else if (info.input) { - node.disconnectInput(info.slot); - } - return; - } else if (v.content == "Rename Slot") { - var info = v.slot; - var slot_info = info.input - ? node.getInputInfo(info.slot) - : node.getOutputInfo(info.slot); - var dialog = that.createDialog( - "Name", - options - ); - var input = dialog.querySelector("input"); - if (input && slot_info) { - input.value = slot_info.label || ""; - } - dialog - .querySelector("button") - .addEventListener("click", function(e) { - if (input.value) { - if (slot_info) { - slot_info.label = input.value; - } - that.setDirty(true); - } - dialog.close(); - }); - } - - //if(v.callback) - // return v.callback.call(that, node, options, e, menu, that, event ); - } - }; - - //API ************************************************* - //like rect but rounded corners - if (typeof(window) != "undefined" && window.CanvasRenderingContext2D) { - window.CanvasRenderingContext2D.prototype.roundRect = function( - x, - y, - width, - height, - radius, - radius_low - ) { - if (radius === undefined) { - radius = 5; - } - - if (radius_low === undefined) { - radius_low = radius; - } - - this.moveTo(x + radius, y); - this.lineTo(x + width - radius, y); - this.quadraticCurveTo(x + width, y, x + width, y + radius); - - this.lineTo(x + width, y + height - radius_low); - this.quadraticCurveTo( - x + width, - y + height, - x + width - radius_low, - y + height - ); - this.lineTo(x + radius_low, y + height); - this.quadraticCurveTo(x, y + height, x, y + height - radius_low); - this.lineTo(x, y + radius); - this.quadraticCurveTo(x, y, x + radius, y); - }; - } - - function compareObjects(a, b) { - for (var i in a) { - if (a[i] != b[i]) { - return false; - } - } - return true; - } - LiteGraph.compareObjects = compareObjects; - - function distance(a, b) { - return Math.sqrt( - (b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1]) - ); - } - LiteGraph.distance = distance; - - function colorToString(c) { - return ( - "rgba(" + - Math.round(c[0] * 255).toFixed() + - "," + - Math.round(c[1] * 255).toFixed() + - "," + - Math.round(c[2] * 255).toFixed() + - "," + - (c.length == 4 ? c[3].toFixed(2) : "1.0") + - ")" - ); - } - LiteGraph.colorToString = colorToString; - - function isInsideRectangle(x, y, left, top, width, height) { - if (left < x && left + width > x && top < y && top + height > y) { - return true; - } - return false; - } - LiteGraph.isInsideRectangle = isInsideRectangle; - - //[minx,miny,maxx,maxy] - function growBounding(bounding, x, y) { - if (x < bounding[0]) { - bounding[0] = x; - } else if (x > bounding[2]) { - bounding[2] = x; - } - - if (y < bounding[1]) { - bounding[1] = y; - } else if (y > bounding[3]) { - bounding[3] = y; - } - } - LiteGraph.growBounding = growBounding; - - //point inside bounding box - function isInsideBounding(p, bb) { - if ( - p[0] < bb[0][0] || - p[1] < bb[0][1] || - p[0] > bb[1][0] || - p[1] > bb[1][1] - ) { - return false; - } - return true; - } - LiteGraph.isInsideBounding = isInsideBounding; - - //bounding overlap, format: [ startx, starty, width, height ] - function overlapBounding(a, b) { - var A_end_x = a[0] + a[2]; - var A_end_y = a[1] + a[3]; - var B_end_x = b[0] + b[2]; - var B_end_y = b[1] + b[3]; - - if ( - a[0] > B_end_x || - a[1] > B_end_y || - A_end_x < b[0] || - A_end_y < b[1] - ) { - return false; - } - return true; - } - LiteGraph.overlapBounding = overlapBounding; - - //Convert a hex value to its decimal value - the inputted hex must be in the - // format of a hex triplet - the kind we use for HTML colours. The function - // will return an array with three values. - function hex2num(hex) { - if (hex.charAt(0) == "#") { - hex = hex.slice(1); - } //Remove the '#' char - if there is one. - hex = hex.toUpperCase(); - var hex_alphabets = "0123456789ABCDEF"; - var value = new Array(3); - var k = 0; - var int1, int2; - for (var i = 0; i < 6; i += 2) { - int1 = hex_alphabets.indexOf(hex.charAt(i)); - int2 = hex_alphabets.indexOf(hex.charAt(i + 1)); - value[k] = int1 * 16 + int2; - k++; - } - return value; - } - - LiteGraph.hex2num = hex2num; - - //Give a array with three values as the argument and the function will return - // the corresponding hex triplet. - function num2hex(triplet) { - var hex_alphabets = "0123456789ABCDEF"; - var hex = "#"; - var int1, int2; - for (var i = 0; i < 3; i++) { - int1 = triplet[i] / 16; - int2 = triplet[i] % 16; - - hex += hex_alphabets.charAt(int1) + hex_alphabets.charAt(int2); - } - return hex; - } - - LiteGraph.num2hex = num2hex; - - /* LiteGraph GUI elements used for canvas editing *************************************/ - - /** - * ContextMenu from LiteGUI - * - * @class ContextMenu - * @constructor - * @param {Array} values (allows object { title: "Nice text", callback: function ... }) - * @param {Object} options [optional] Some options:\ - * - title: title to show on top of the menu - * - callback: function to call when an option is clicked, it receives the item information - * - ignore_item_callbacks: ignores the callback inside the item, it just calls the options.callback - * - event: you can pass a MouseEvent, this way the ContextMenu appears in that position - */ - function ContextMenu(values, options) { - options = options || {}; - this.options = options; - var that = this; - - //to link a menu with its parent - if (options.parentMenu) { - if (options.parentMenu.constructor !== this.constructor) { - console.error( - "parentMenu must be of class ContextMenu, ignoring it" - ); - options.parentMenu = null; - } else { - this.parentMenu = options.parentMenu; - this.parentMenu.lock = true; - this.parentMenu.current_submenu = this; - } - } - - var eventClass = null; - if(options.event) //use strings because comparing classes between windows doesnt work - eventClass = options.event.constructor.name; - if ( eventClass !== "MouseEvent" && - eventClass !== "CustomEvent" && - eventClass !== "PointerEvent" - ) { - console.error( - "Event passed to ContextMenu is not of type MouseEvent or CustomEvent. Ignoring it." - ); - options.event = null; - } - - var root = document.createElement("div"); - root.className = "litegraph litecontextmenu litemenubar-panel"; - if (options.className) { - root.className += " " + options.className; - } - root.style.minWidth = 100; - root.style.minHeight = 100; - root.style.pointerEvents = "none"; - setTimeout(function() { - root.style.pointerEvents = "auto"; - }, 100); //delay so the mouse up event is not caught by this element - - //this prevents the default context browser menu to open in case this menu was created when pressing right button - root.addEventListener( - "mouseup", - function(e) { - e.preventDefault(); - return true; - }, - true - ); - root.addEventListener( - "contextmenu", - function(e) { - if (e.button != 2) { - //right button - return false; - } - e.preventDefault(); - return false; - }, - true - ); - - root.addEventListener( - "mousedown", - function(e) { - if (e.button == 2) { - that.close(); - e.preventDefault(); - return true; - } - }, - true - ); - - function on_mouse_wheel(e) { - var pos = parseInt(root.style.top); - root.style.top = - (pos + e.deltaY * options.scroll_speed).toFixed() + "px"; - e.preventDefault(); - return true; - } - - if (!options.scroll_speed) { - options.scroll_speed = 0.1; - } - - root.addEventListener("wheel", on_mouse_wheel, true); - root.addEventListener("mousewheel", on_mouse_wheel, true); - - this.root = root; - - //title - if (options.title) { - var element = document.createElement("div"); - element.className = "litemenu-title"; - element.innerHTML = options.title; - root.appendChild(element); - } - - //entries - var num = 0; - for (var i in values) { - var name = values.constructor == Array ? values[i] : i; - if (name != null && name.constructor !== String) { - name = name.content === undefined ? String(name) : name.content; - } - var value = values[i]; - this.addItem(name, value, options); - num++; - } - - //close on leave - root.addEventListener("mouseleave", function(e) { - if (that.lock) { - return; - } - if (root.closing_timer) { - clearTimeout(root.closing_timer); - } - root.closing_timer = setTimeout(that.close.bind(that, e), 500); - //that.close(e); - }); - - root.addEventListener("mouseenter", function(e) { - if (root.closing_timer) { - clearTimeout(root.closing_timer); - } - }); - - //insert before checking position - var root_document = document; - if (options.event) { - root_document = options.event.target.ownerDocument; - } - - if (!root_document) { - root_document = document; - } - - if( root_document.fullscreenElement ) - root_document.fullscreenElement.appendChild(root); - else - root_document.body.appendChild(root); - - //compute best position - var left = options.left || 0; - var top = options.top || 0; - if (options.event) { - left = options.event.clientX - 10; - top = options.event.clientY - 10; - if (options.title) { - top -= 20; - } - - if (options.parentMenu) { - var rect = options.parentMenu.root.getBoundingClientRect(); - left = rect.left + rect.width; - } - - var body_rect = document.body.getBoundingClientRect(); - var root_rect = root.getBoundingClientRect(); - if(body_rect.height == 0) - console.error("document.body height is 0. That is dangerous, set html,body { height: 100%; }"); - - if (body_rect.width && left > body_rect.width - root_rect.width - 10) { - left = body_rect.width - root_rect.width - 10; - } - if (body_rect.height && top > body_rect.height - root_rect.height - 10) { - top = body_rect.height - root_rect.height - 10; - } - } - - root.style.left = left + "px"; - root.style.top = top + "px"; - - if (options.scale) { - root.style.transform = "scale(" + options.scale + ")"; - } - } - - ContextMenu.prototype.addItem = function(name, value, options) { - var that = this; - options = options || {}; - - var element = document.createElement("div"); - element.className = "litemenu-entry submenu"; - - var disabled = false; - - if (value === null) { - element.classList.add("separator"); - //element.innerHTML = "
" - //continue; - } else { - element.innerHTML = value && value.title ? value.title : name; - element.value = value; - - if (value) { - if (value.disabled) { - disabled = true; - element.classList.add("disabled"); - } - if (value.submenu || value.has_submenu) { - element.classList.add("has_submenu"); - } - } - - if (typeof value == "function") { - element.dataset["value"] = name; - element.onclick_callback = value; - } else { - element.dataset["value"] = value; - } - - if (value.className) { - element.className += " " + value.className; - } - } - - this.root.appendChild(element); - if (!disabled) { - element.addEventListener("click", inner_onclick); - } - if (options.autoopen) { - element.addEventListener("mouseenter", inner_over); - } - - function inner_over(e) { - var value = this.value; - if (!value || !value.has_submenu) { - return; - } - //if it is a submenu, autoopen like the item was clicked - inner_onclick.call(this, e); - } - - //menu option clicked - function inner_onclick(e) { - var value = this.value; - var close_parent = true; - - if (that.current_submenu) { - that.current_submenu.close(e); - } - - //global callback - if (options.callback) { - var r = options.callback.call( - this, - value, - options, - e, - that, - options.node - ); - if (r === true) { - close_parent = false; - } - } - - //special cases - if (value) { - if ( - value.callback && - !options.ignore_item_callbacks && - value.disabled !== true - ) { - //item callback - var r = value.callback.call( - this, - value, - options, - e, - that, - options.extra - ); - if (r === true) { - close_parent = false; - } - } - if (value.submenu) { - if (!value.submenu.options) { - throw "ContextMenu submenu needs options"; - } - var submenu = new that.constructor(value.submenu.options, { - callback: value.submenu.callback, - event: e, - parentMenu: that, - ignore_item_callbacks: - value.submenu.ignore_item_callbacks, - title: value.submenu.title, - extra: value.submenu.extra, - autoopen: options.autoopen - }); - close_parent = false; - } - } - - if (close_parent && !that.lock) { - that.close(); - } - } - - return element; - }; - - ContextMenu.prototype.close = function(e, ignore_parent_menu) { - if (this.root.parentNode) { - this.root.parentNode.removeChild(this.root); - } - if (this.parentMenu && !ignore_parent_menu) { - this.parentMenu.lock = false; - this.parentMenu.current_submenu = null; - if (e === undefined) { - this.parentMenu.close(); - } else if ( - e && - !ContextMenu.isCursorOverElement(e, this.parentMenu.root) - ) { - ContextMenu.trigger(this.parentMenu.root, "mouseleave", e); - } - } - if (this.current_submenu) { - this.current_submenu.close(e, true); - } - - if (this.root.closing_timer) { - clearTimeout(this.root.closing_timer); - } - }; - - //this code is used to trigger events easily (used in the context menu mouseleave - ContextMenu.trigger = function(element, event_name, params, origin) { - var evt = document.createEvent("CustomEvent"); - evt.initCustomEvent(event_name, true, true, params); //canBubble, cancelable, detail - evt.srcElement = origin; - if (element.dispatchEvent) { - element.dispatchEvent(evt); - } else if (element.__events) { - element.__events.dispatchEvent(evt); - } - //else nothing seems binded here so nothing to do - return evt; - }; - - //returns the top most menu - ContextMenu.prototype.getTopMenu = function() { - if (this.options.parentMenu) { - return this.options.parentMenu.getTopMenu(); - } - return this; - }; - - ContextMenu.prototype.getFirstEvent = function() { - if (this.options.parentMenu) { - return this.options.parentMenu.getFirstEvent(); - } - return this.options.event; - }; - - ContextMenu.isCursorOverElement = function(event, element) { - var left = event.clientX; - var top = event.clientY; - var rect = element.getBoundingClientRect(); - if (!rect) { - return false; - } - if ( - top > rect.top && - top < rect.top + rect.height && - left > rect.left && - left < rect.left + rect.width - ) { - return true; - } - return false; - }; - - LiteGraph.ContextMenu = ContextMenu; - - LiteGraph.closeAllContextMenus = function(ref_window) { - ref_window = ref_window || window; - - var elements = ref_window.document.querySelectorAll(".litecontextmenu"); - if (!elements.length) { - return; - } - - var result = []; - for (var i = 0; i < elements.length; i++) { - result.push(elements[i]); - } - - for (var i in result) { - if (result[i].close) { - result[i].close(); - } else if (result[i].parentNode) { - result[i].parentNode.removeChild(result[i]); - } - } - }; - - LiteGraph.extendClass = function(target, origin) { - for (var i in origin) { - //copy class properties - if (target.hasOwnProperty(i)) { - continue; - } - target[i] = origin[i]; - } - - if (origin.prototype) { - //copy prototype properties - for (var i in origin.prototype) { - //only enumerable - if (!origin.prototype.hasOwnProperty(i)) { - continue; - } - - if (target.prototype.hasOwnProperty(i)) { - //avoid overwriting existing ones - continue; - } - - //copy getters - if (origin.prototype.__lookupGetter__(i)) { - target.prototype.__defineGetter__( - i, - origin.prototype.__lookupGetter__(i) - ); - } else { - target.prototype[i] = origin.prototype[i]; - } - - //and setters - if (origin.prototype.__lookupSetter__(i)) { - target.prototype.__defineSetter__( - i, - origin.prototype.__lookupSetter__(i) - ); - } - } - } - }; - - //used by some widgets to render a curve editor - function CurveEditor( points ) - { - this.points = points; - this.selected = -1; - this.nearest = -1; - this.size = null; //stores last size used - this.must_update = true; - this.margin = 5; - } - - CurveEditor.sampleCurve = function(f,points) - { - if(!points) - return; - for(var i = 0; i < points.length - 1; ++i) - { - var p = points[i]; - var pn = points[i+1]; - if(pn[0] < f) - continue; - var r = (pn[0] - p[0]); - if( Math.abs(r) < 0.00001 ) - return p[1]; - var local_f = (f - p[0]) / r; - return p[1] * (1.0 - local_f) + pn[1] * local_f; - } - return 0; - } - - CurveEditor.prototype.draw = function( ctx, size, graphcanvas, background_color, line_color, inactive ) - { - var points = this.points; - if(!points) - return; - this.size = size; - var w = size[0] - this.margin * 2; - var h = size[1] - this.margin * 2; - - line_color = line_color || "#666"; - - ctx.save(); - ctx.translate(this.margin,this.margin); - - if(background_color) - { - ctx.fillStyle = "#111"; - ctx.fillRect(0,0,w,h); - ctx.fillStyle = "#222"; - ctx.fillRect(w*0.5,0,1,h); - ctx.strokeStyle = "#333"; - ctx.strokeRect(0,0,w,h); - } - ctx.strokeStyle = line_color; - if(inactive) - ctx.globalAlpha = 0.5; - ctx.beginPath(); - for(var i = 0; i < points.length; ++i) - { - var p = points[i]; - ctx.lineTo( p[0] * w, (1.0 - p[1]) * h ); - } - ctx.stroke(); - ctx.globalAlpha = 1; - if(!inactive) - for(var i = 0; i < points.length; ++i) - { - var p = points[i]; - ctx.fillStyle = this.selected == i ? "#FFF" : (this.nearest == i ? "#DDD" : "#AAA"); - ctx.beginPath(); - ctx.arc( p[0] * w, (1.0 - p[1]) * h, 2, 0, Math.PI * 2 ); - ctx.fill(); - } - ctx.restore(); - } - - //localpos is mouse in curve editor space - CurveEditor.prototype.onMouseDown = function( localpos, graphcanvas ) - { - var points = this.points; - if(!points) - return; - if( localpos[1] < 0 ) - return; - - //this.captureInput(true); - var w = this.size[0] - this.margin * 2; - var h = this.size[1] - this.margin * 2; - var x = localpos[0] - this.margin; - var y = localpos[1] - this.margin; - var pos = [x,y]; - var max_dist = 30 / graphcanvas.ds.scale; - //search closer one - this.selected = this.getCloserPoint(pos, max_dist); - //create one - if(this.selected == -1) - { - var point = [x / w, 1 - y / h]; - points.push(point); - points.sort(function(a,b){ return a[0] - b[0]; }); - this.selected = points.indexOf(point); - this.must_update = true; - } - if(this.selected != -1) - return true; - } - - CurveEditor.prototype.onMouseMove = function( localpos, graphcanvas ) - { - var points = this.points; - if(!points) - return; - var s = this.selected; - if(s < 0) - return; - var x = (localpos[0] - this.margin) / (this.size[0] - this.margin * 2 ); - var y = (localpos[1] - this.margin) / (this.size[1] - this.margin * 2 ); - var curvepos = [(localpos[0] - this.margin),(localpos[1] - this.margin)]; - var max_dist = 30 / graphcanvas.ds.scale; - this._nearest = this.getCloserPoint(curvepos, max_dist); - var point = points[s]; - if(point) - { - var is_edge_point = s == 0 || s == points.length - 1; - if( !is_edge_point && (localpos[0] < -10 || localpos[0] > this.size[0] + 10 || localpos[1] < -10 || localpos[1] > this.size[1] + 10) ) - { - points.splice(s,1); - this.selected = -1; - return; - } - if( !is_edge_point ) //not edges - point[0] = Math.clamp(x,0,1); - else - point[0] = s == 0 ? 0 : 1; - point[1] = 1.0 - Math.clamp(y,0,1); - points.sort(function(a,b){ return a[0] - b[0]; }); - this.selected = points.indexOf(point); - this.must_update = true; - } - } - - CurveEditor.prototype.onMouseUp = function( localpos, graphcanvas ) - { - this.selected = -1; - return false; - } - - CurveEditor.prototype.getCloserPoint = function(pos, max_dist) - { - var points = this.points; - if(!points) - return -1; - max_dist = max_dist || 30; - var w = (this.size[0] - this.margin * 2); - var h = (this.size[1] - this.margin * 2); - var num = points.length; - var p2 = [0,0]; - var min_dist = 1000000; - var closest = -1; - var last_valid = -1; - for(var i = 0; i < num; ++i) - { - var p = points[i]; - p2[0] = p[0] * w; - p2[1] = (1.0 - p[1]) * h; - if(p2[0] < pos[0]) - last_valid = i; - var dist = vec2.distance(pos,p2); - if(dist > min_dist || dist > max_dist) - continue; - closest = i; - min_dist = dist; - } - return closest; - } - - LiteGraph.CurveEditor = CurveEditor; - - //used to create nodes from wrapping functions - LiteGraph.getParameterNames = function(func) { - return (func + "") - .replace(/[/][/].*$/gm, "") // strip single-line comments - .replace(/\s+/g, "") // strip white space - .replace(/[/][*][^/*]*[*][/]/g, "") // strip multi-line comments /**/ - .split("){", 1)[0] - .replace(/^[^(]*[(]/, "") // extract the parameters - .replace(/=[^,]+/g, "") // strip any ES6 defaults - .split(",") - .filter(Boolean); // split & filter [""] - }; - - Math.clamp = function(v, a, b) { - return a > v ? a : b < v ? b : v; - }; - - if (typeof window != "undefined" && !window["requestAnimationFrame"]) { - window.requestAnimationFrame = - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - function(callback) { - window.setTimeout(callback, 1000 / 60); - }; - } -})(this); - -if (typeof exports != "undefined") { - exports.LiteGraph = this.LiteGraph; -} - -//basic nodes -(function(global) { - var LiteGraph = global.LiteGraph; - - //Constant - function Time() { - this.addOutput("in ms", "number"); - this.addOutput("in sec", "number"); - } - - Time.title = "Time"; - Time.desc = "Time"; - - Time.prototype.onExecute = function() { - this.setOutputData(0, this.graph.globaltime * 1000); - this.setOutputData(1, this.graph.globaltime); - }; - - LiteGraph.registerNodeType("basic/time", Time); - - //Subgraph: a node that contains a graph - function Subgraph() { - var that = this; - this.size = [140, 80]; - this.properties = { enabled: true }; - this.enabled = true; - - //create inner graph - this.subgraph = new LiteGraph.LGraph(); - this.subgraph._subgraph_node = this; - this.subgraph._is_subgraph = true; - - this.subgraph.onTrigger = this.onSubgraphTrigger.bind(this); - - this.subgraph.onInputAdded = this.onSubgraphNewInput.bind(this); - this.subgraph.onInputRenamed = this.onSubgraphRenamedInput.bind(this); - this.subgraph.onInputTypeChanged = this.onSubgraphTypeChangeInput.bind( - this - ); - this.subgraph.onInputRemoved = this.onSubgraphRemovedInput.bind(this); - - this.subgraph.onOutputAdded = this.onSubgraphNewOutput.bind(this); - this.subgraph.onOutputRenamed = this.onSubgraphRenamedOutput.bind(this); - this.subgraph.onOutputTypeChanged = this.onSubgraphTypeChangeOutput.bind( - this - ); - this.subgraph.onOutputRemoved = this.onSubgraphRemovedOutput.bind(this); - } - - Subgraph.title = "Subgraph"; - Subgraph.desc = "Graph inside a node"; - Subgraph.title_color = "#334"; - - Subgraph.prototype.onGetInputs = function() { - return [["enabled", "boolean"]]; - }; - - Subgraph.prototype.onDrawTitle = function(ctx) { - if (this.flags.collapsed) { - return; - } - - ctx.fillStyle = "#555"; - var w = LiteGraph.NODE_TITLE_HEIGHT; - var x = this.size[0] - w; - ctx.fillRect(x, -w, w, w); - ctx.fillStyle = "#333"; - ctx.beginPath(); - ctx.moveTo(x + w * 0.2, -w * 0.6); - ctx.lineTo(x + w * 0.8, -w * 0.6); - ctx.lineTo(x + w * 0.5, -w * 0.3); - ctx.fill(); - }; - - Subgraph.prototype.onDblClick = function(e, pos, graphcanvas) { - var that = this; - setTimeout(function() { - graphcanvas.openSubgraph(that.subgraph); - }, 10); - }; - - Subgraph.prototype.onMouseDown = function(e, pos, graphcanvas) { - if ( - !this.flags.collapsed && - pos[0] > this.size[0] - LiteGraph.NODE_TITLE_HEIGHT && - pos[1] < 0 - ) { - var that = this; - setTimeout(function() { - graphcanvas.openSubgraph(that.subgraph); - }, 10); - } - }; - - Subgraph.prototype.onAction = function(action, param) { - this.subgraph.onAction(action, param); - }; - - Subgraph.prototype.onExecute = function() { - this.enabled = this.getInputOrProperty("enabled"); - if (!this.enabled) { - return; - } - - //send inputs to subgraph global inputs - if (this.inputs) { - for (var i = 0; i < this.inputs.length; i++) { - var input = this.inputs[i]; - var value = this.getInputData(i); - this.subgraph.setInputData(input.name, value); - } - } - - //execute - this.subgraph.runStep(); - - //send subgraph global outputs to outputs - if (this.outputs) { - for (var i = 0; i < this.outputs.length; i++) { - var output = this.outputs[i]; - var value = this.subgraph.getOutputData(output.name); - this.setOutputData(i, value); - } - } - }; - - Subgraph.prototype.sendEventToAllNodes = function(eventname, param, mode) { - if (this.enabled) { - this.subgraph.sendEventToAllNodes(eventname, param, mode); - } - }; - - //**** INPUTS *********************************** - Subgraph.prototype.onSubgraphTrigger = function(event, param) { - var slot = this.findOutputSlot(event); - if (slot != -1) { - this.triggerSlot(slot); - } - }; - - Subgraph.prototype.onSubgraphNewInput = function(name, type) { - var slot = this.findInputSlot(name); - if (slot == -1) { - //add input to the node - this.addInput(name, type); - } - }; - - Subgraph.prototype.onSubgraphRenamedInput = function(oldname, name) { - var slot = this.findInputSlot(oldname); - if (slot == -1) { - return; - } - var info = this.getInputInfo(slot); - info.name = name; - }; - - Subgraph.prototype.onSubgraphTypeChangeInput = function(name, type) { - var slot = this.findInputSlot(name); - if (slot == -1) { - return; - } - var info = this.getInputInfo(slot); - info.type = type; - }; - - Subgraph.prototype.onSubgraphRemovedInput = function(name) { - var slot = this.findInputSlot(name); - if (slot == -1) { - return; - } - this.removeInput(slot); - }; - - //**** OUTPUTS *********************************** - Subgraph.prototype.onSubgraphNewOutput = function(name, type) { - var slot = this.findOutputSlot(name); - if (slot == -1) { - this.addOutput(name, type); - } - }; - - Subgraph.prototype.onSubgraphRenamedOutput = function(oldname, name) { - var slot = this.findOutputSlot(oldname); - if (slot == -1) { - return; - } - var info = this.getOutputInfo(slot); - info.name = name; - }; - - Subgraph.prototype.onSubgraphTypeChangeOutput = function(name, type) { - var slot = this.findOutputSlot(name); - if (slot == -1) { - return; - } - var info = this.getOutputInfo(slot); - info.type = type; - }; - - Subgraph.prototype.onSubgraphRemovedOutput = function(name) { - var slot = this.findInputSlot(name); - if (slot == -1) { - return; - } - this.removeOutput(slot); - }; - // ***************************************************** - - Subgraph.prototype.getExtraMenuOptions = function(graphcanvas) { - var that = this; - return [ - { - content: "Open", - callback: function() { - graphcanvas.openSubgraph(that.subgraph); - } - } - ]; - }; - - Subgraph.prototype.onResize = function(size) { - size[1] += 20; - }; - - Subgraph.prototype.serialize = function() { - var data = LiteGraph.LGraphNode.prototype.serialize.call(this); - data.subgraph = this.subgraph.serialize(); - return data; - }; - //no need to define node.configure, the default method detects node.subgraph and passes the object to node.subgraph.configure() - - Subgraph.prototype.clone = function() { - var node = LiteGraph.createNode(this.type); - var data = this.serialize(); - delete data["id"]; - delete data["inputs"]; - delete data["outputs"]; - node.configure(data); - return node; - }; - - LiteGraph.Subgraph = Subgraph; - LiteGraph.registerNodeType("graph/subgraph", Subgraph); - - //Input for a subgraph - function GraphInput() { - this.addOutput("", "number"); - - this.name_in_graph = ""; - this.properties = { - name: "", - type: "number", - value: 0 - }; - - var that = this; - - this.name_widget = this.addWidget( - "text", - "Name", - this.properties.name, - function(v) { - if (!v) { - return; - } - that.setProperty("name",v); - } - ); - this.type_widget = this.addWidget( - "text", - "Type", - this.properties.type, - function(v) { - that.setProperty("type",v); - } - ); - - this.value_widget = this.addWidget( - "number", - "Value", - this.properties.value, - function(v) { - that.setProperty("value",v); - } - ); - - this.widgets_up = true; - this.size = [180, 90]; - } - - GraphInput.title = "Input"; - GraphInput.desc = "Input of the graph"; - - GraphInput.prototype.onConfigure = function() - { - this.updateType(); - } - - GraphInput.prototype.updateType = function() - { - var type = this.properties.type; - this.type_widget.value = type; - if(this.outputs[0].type != type) - { - this.outputs[0].type = type; - this.disconnectOutput(0); - } - if(type == "number") - { - this.value_widget.type = "number"; - this.value_widget.value = 0; - } - else if(type == "boolean") - { - this.value_widget.type = "toggle"; - this.value_widget.value = true; - } - else if(type == "string") - { - this.value_widget.type = "text"; - this.value_widget.value = ""; - } - else - { - this.value_widget.type = null; - this.value_widget.value = null; - } - this.properties.value = this.value_widget.value; - } - - GraphInput.prototype.onPropertyChanged = function(name,v) - { - if( name == "name" ) - { - if (v == "" || v == this.name_in_graph || v == "enabled") { - return false; - } - if(this.graph) - { - if (this.name_in_graph) { - //already added - this.graph.renameInput( this.name_in_graph, v ); - } else { - this.graph.addInput( v, this.properties.type ); - } - } //what if not?! - this.name_widget.value = v; - this.name_in_graph = v; - } - else if( name == "type" ) - { - v = v || ""; - this.updateType(v); - } - else if( name == "value" ) - { - } - } - - GraphInput.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this.properties.name; - } - return this.title; - }; - - GraphInput.prototype.onAction = function(action, param) { - if (this.properties.type == LiteGraph.EVENT) { - this.triggerSlot(0, param); - } - }; - - GraphInput.prototype.onExecute = function() { - var name = this.properties.name; - //read from global input - var data = this.graph.inputs[name]; - if (!data) { - this.setOutputData(0, this.properties.value ); - return; - } - - this.setOutputData(0, data.value !== undefined ? data.value : this.properties.value ); - }; - - GraphInput.prototype.onRemoved = function() { - if (this.name_in_graph) { - this.graph.removeInput(this.name_in_graph); - } - }; - - LiteGraph.GraphInput = GraphInput; - LiteGraph.registerNodeType("graph/input", GraphInput); - - //Output for a subgraph - function GraphOutput() { - this.addInput("", ""); - - this.name_in_graph = ""; - this.properties = {}; - var that = this; - - Object.defineProperty(this.properties, "name", { - get: function() { - return that.name_in_graph; - }, - set: function(v) { - if (v == "" || v == that.name_in_graph) { - return; - } - if (that.name_in_graph) { - //already added - that.graph.renameOutput(that.name_in_graph, v); - } else { - that.graph.addOutput(v, that.properties.type); - } - that.name_widget.value = v; - that.name_in_graph = v; - }, - enumerable: true - }); - - Object.defineProperty(this.properties, "type", { - get: function() { - return that.inputs[0].type; - }, - set: function(v) { - if (v == "action" || v == "event") { - v = LiteGraph.ACTION; - } - that.inputs[0].type = v; - if (that.name_in_graph) { - //already added - that.graph.changeOutputType( - that.name_in_graph, - that.inputs[0].type - ); - } - that.type_widget.value = v || ""; - }, - enumerable: true - }); - - this.name_widget = this.addWidget("text","Name",this.properties.name,"name"); - this.type_widget = this.addWidget("text","Type",this.properties.type,"type"); - this.widgets_up = true; - this.size = [180, 60]; - } - - GraphOutput.title = "Output"; - GraphOutput.desc = "Output of the graph"; - - GraphOutput.prototype.onExecute = function() { - this._value = this.getInputData(0); - this.graph.setOutputData(this.properties.name, this._value); - }; - - GraphOutput.prototype.onAction = function(action, param) { - if (this.properties.type == LiteGraph.ACTION) { - this.graph.trigger(this.properties.name, param); - } - }; - - GraphOutput.prototype.onRemoved = function() { - if (this.name_in_graph) { - this.graph.removeOutput(this.name_in_graph); - } - }; - - GraphOutput.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this.properties.name; - } - return this.title; - }; - - LiteGraph.GraphOutput = GraphOutput; - LiteGraph.registerNodeType("graph/output", GraphOutput); - - //Constant - function ConstantNumber() { - this.addOutput("value", "number"); - this.addProperty("value", 1.0); - this.widget = this.addWidget("number","value",1,"value"); - this.widgets_up = true; - this.size = [180, 30]; - } - - ConstantNumber.title = "Const Number"; - ConstantNumber.desc = "Constant number"; - - ConstantNumber.prototype.onExecute = function() { - this.setOutputData(0, parseFloat(this.properties["value"])); - }; - - ConstantNumber.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this.properties.value; - } - return this.title; - }; - - ConstantNumber.prototype.setValue = function(v) - { - this.setProperty("value",v); - } - - ConstantNumber.prototype.onDrawBackground = function(ctx) { - //show the current value - this.outputs[0].label = this.properties["value"].toFixed(3); - }; - - LiteGraph.registerNodeType("basic/const", ConstantNumber); - - function ConstantBoolean() { - this.addOutput("", "boolean"); - this.addProperty("value", true); - this.widget = this.addWidget("toggle","value",true,"value"); - this.widgets_up = true; - this.size = [140, 30]; - } - - ConstantBoolean.title = "Const Boolean"; - ConstantBoolean.desc = "Constant boolean"; - ConstantBoolean.prototype.getTitle = ConstantNumber.prototype.getTitle; - - ConstantBoolean.prototype.onExecute = function() { - this.setOutputData(0, this.properties["value"]); - }; - - ConstantBoolean.prototype.setValue = ConstantNumber.prototype.setValue; - - ConstantBoolean.prototype.onGetInputs = function() { - return [["toggle", LiteGraph.ACTION]]; - }; - - ConstantBoolean.prototype.onAction = function(action) - { - this.setValue( !this.properties.value ); - } - - LiteGraph.registerNodeType("basic/boolean", ConstantBoolean); - - function ConstantString() { - this.addOutput("", "string"); - this.addProperty("value", ""); - this.widget = this.addWidget("text","value","","value"); //link to property value - this.widgets_up = true; - this.size = [180, 30]; - } - - ConstantString.title = "Const String"; - ConstantString.desc = "Constant string"; - - ConstantString.prototype.getTitle = ConstantNumber.prototype.getTitle; - - ConstantString.prototype.onExecute = function() { - this.setOutputData(0, this.properties["value"]); - }; - - ConstantString.prototype.setValue = ConstantNumber.prototype.setValue; - - ConstantString.prototype.onDropFile = function(file) - { - var that = this; - var reader = new FileReader(); - reader.onload = function(e) - { - that.setProperty("value",e.target.result); - } - reader.readAsText(file); - } - - LiteGraph.registerNodeType("basic/string", ConstantString); - - function ConstantFile() { - this.addInput("url", ""); - this.addOutput("", ""); - this.addProperty("url", ""); - this.addProperty("type", "text"); - this.widget = this.addWidget("text","url","","url"); - this._data = null; - } - - ConstantFile.title = "Const File"; - ConstantFile.desc = "Fetches a file from an url"; - ConstantFile["@type"] = { type: "enum", values: ["text","arraybuffer","blob","json"] }; - - ConstantFile.prototype.onPropertyChanged = function(name, value) { - if (name == "url") - { - if( value == null || value == "") - this._data = null; - else - { - this.fetchFile(value); - } - } - } - - ConstantFile.prototype.onExecute = function() { - var url = this.getInputData(0) || this.properties.url; - if(url && (url != this._url || this._type != this.properties.type)) - this.fetchFile(url); - this.setOutputData(0, this._data ); - }; - - ConstantFile.prototype.setValue = ConstantNumber.prototype.setValue; - - ConstantFile.prototype.fetchFile = function(url) { - var that = this; - if(!url || url.constructor !== String) - { - that._data = null; - that.boxcolor = null; - return; - } - - this._url = url; - this._type = this.properties.type; - if (url.substr(0, 4) == "http" && LiteGraph.proxy) { - url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); - } - fetch(url) - .then(function(response) { - if(!response.ok) - throw new Error("File not found"); - - if(that.properties.type == "arraybuffer") - return response.arrayBuffer(); - else if(that.properties.type == "text") - return response.text(); - else if(that.properties.type == "json") - return response.json(); - else if(that.properties.type == "blob") - return response.blob(); - }) - .then(function(data) { - that._data = data; - that.boxcolor = "#AEA"; - }) - .catch(function(error) { - that._data = null; - that.boxcolor = "red"; - console.error("error fetching file:",url); - }); - }; - - ConstantFile.prototype.onDropFile = function(file) - { - var that = this; - this._url = file.name; - this._type = this.properties.type; - this.properties.url = file.name; - var reader = new FileReader(); - reader.onload = function(e) - { - that.boxcolor = "#AEA"; - var v = e.target.result; - if( that.properties.type == "json" ) - v = JSON.parse(v); - that._data = v; - } - if(that.properties.type == "arraybuffer") - reader.readAsArrayBuffer(file); - else if(that.properties.type == "text" || that.properties.type == "json") - reader.readAsText(file); - else if(that.properties.type == "blob") - return reader.readAsBinaryString(file); - } - - LiteGraph.registerNodeType("basic/file", ConstantFile); - - //to store json objects - function ConstantData() { - this.addOutput("", ""); - this.addProperty("value", ""); - this.widget = this.addWidget("text","json","","value"); - this.widgets_up = true; - this.size = [140, 30]; - this._value = null; - } - - ConstantData.title = "Const Data"; - ConstantData.desc = "Constant Data"; - - ConstantData.prototype.onPropertyChanged = function(name, value) { - this.widget.value = value; - if (value == null || value == "") { - return; - } - - try { - this._value = JSON.parse(value); - this.boxcolor = "#AEA"; - } catch (err) { - this.boxcolor = "red"; - } - }; - - ConstantData.prototype.onExecute = function() { - this.setOutputData(0, this._value); - }; - - ConstantData.prototype.setValue = ConstantNumber.prototype.setValue; - - LiteGraph.registerNodeType("basic/data", ConstantData); - - //to store json objects - function ConstantArray() { - this.addInput("", ""); - this.addOutput("", "array"); - this.addProperty("value", ""); - this.widget = this.addWidget("text","array","","value"); - this.widgets_up = true; - this.size = [140, 30]; - this._value = null; - } - - ConstantArray.title = "Const Array"; - ConstantArray.desc = "Constant Array"; - - ConstantArray.prototype.onPropertyChanged = function(name, value) { - this.widget.value = value; - if (value == null || value == "") { - return; - } - - try { - this._value = JSON.parse(value); - this.boxcolor = "#AEA"; - } catch (err) { - this.boxcolor = "red"; - } - }; - - ConstantArray.prototype.onExecute = function() { - var v = this.getInputData(0); - if(v && v.length) - { - if(!this._value) - this._value = new Array(); - this._value.length = v.length; - for(var i = 0; i < v.length; ++i) - this._value[i] = v[i]; - } - this.setOutputData(0, this._value); - }; - - ConstantArray.prototype.setValue = ConstantNumber.prototype.setValue; - - LiteGraph.registerNodeType("basic/array", ConstantArray); - - function ArrayElement() { - this.addInput("array", "array,table,string"); - this.addInput("index", "number"); - this.addOutput("value", ""); - this.addProperty("index",0); - } - - ArrayElement.title = "Array[i]"; - ArrayElement.desc = "Returns an element from an array"; - - ArrayElement.prototype.onExecute = function() { - var array = this.getInputData(0); - var index = this.getInputData(1); - if(index == null) - index = this.properties.index; - if(array == null || index == null ) - return; - this.setOutputData(0, array[Math.floor(Number(index))] ); - }; - - LiteGraph.registerNodeType("basic/array[]", ArrayElement); - - function TableElement() { - this.addInput("table", "table"); - this.addInput("row", "number"); - this.addInput("col", "number"); - this.addOutput("value", ""); - this.addProperty("row",0); - this.addProperty("column",0); - } - - TableElement.title = "Table[row][col]"; - TableElement.desc = "Returns an element from a table"; - - TableElement.prototype.onExecute = function() { - var table = this.getInputData(0); - var row = this.getInputData(1); - var col = this.getInputData(2); - if(row == null) - row = this.properties.row; - if(col == null) - col = this.properties.column; - if(table == null || row == null || col == null) - return; - var row = table[Math.floor(Number(row))]; - if(row) - this.setOutputData(0, row[Math.floor(Number(col))] ); - else - this.setOutputData(0, null ); - }; - - LiteGraph.registerNodeType("basic/table[][]", TableElement); - - function ObjectProperty() { - this.addInput("obj", ""); - this.addOutput("", ""); - this.addProperty("value", ""); - this.widget = this.addWidget("text","prop.","",this.setValue.bind(this) ); - this.widgets_up = true; - this.size = [140, 30]; - this._value = null; - } - - ObjectProperty.title = "Object property"; - ObjectProperty.desc = "Outputs the property of an object"; - - ObjectProperty.prototype.setValue = function(v) { - this.properties.value = v; - this.widget.value = v; - }; - - ObjectProperty.prototype.getTitle = function() { - if (this.flags.collapsed) { - return "in." + this.properties.value; - } - return this.title; - }; - - ObjectProperty.prototype.onPropertyChanged = function(name, value) { - this.widget.value = value; - }; - - ObjectProperty.prototype.onExecute = function() { - var data = this.getInputData(0); - if (data != null) { - this.setOutputData(0, data[this.properties.value]); - } - }; - - LiteGraph.registerNodeType("basic/object_property", ObjectProperty); - - function ObjectKeys() { - this.addInput("obj", ""); - this.addOutput("keys", "array"); - this.size = [140, 30]; - } - - ObjectKeys.title = "Object keys"; - ObjectKeys.desc = "Outputs an array with the keys of an object"; - - ObjectKeys.prototype.onExecute = function() { - var data = this.getInputData(0); - if (data != null) { - this.setOutputData(0, Object.keys(data) ); - } - }; - - LiteGraph.registerNodeType("basic/object_keys", ObjectKeys); - - function MergeObjects() { - this.addInput("A", "object"); - this.addInput("B", "object"); - this.addOutput("", "object"); - this._result = {}; - var that = this; - this.addWidget("button","clear","",function(){ - that._result = {}; - }); - this.size = this.computeSize(); - } - - MergeObjects.title = "Merge Objects"; - MergeObjects.desc = "Creates an object copying properties from others"; - - MergeObjects.prototype.onExecute = function() { - var A = this.getInputData(0); - var B = this.getInputData(1); - var C = this._result; - if(A) - for(var i in A) - C[i] = A[i]; - if(B) - for(var i in B) - C[i] = B[i]; - this.setOutputData(0,C); - }; - - LiteGraph.registerNodeType("basic/merge_objects", MergeObjects ); - - //Store as variable - function Variable() { - this.size = [60, 30]; - this.addInput("in"); - this.addOutput("out"); - this.properties = { varname: "myname", global: false }; - this.value = null; - } - - Variable.title = "Variable"; - Variable.desc = "store/read variable value"; - - Variable.prototype.onExecute = function() { - this.value = this.getInputData(0); - if(this.graph) - this.graph.vars[ this.properties.varname ] = this.value; - if(this.properties.global) - global[this.properties.varname] = this.value; - this.setOutputData(0, this.value ); - }; - - Variable.prototype.getTitle = function() { - return this.properties.varname; - }; - - LiteGraph.registerNodeType("basic/variable", Variable); - - function length(v) { - if(v && v.length != null) - return Number(v.length); - return 0; - } - - LiteGraph.wrapFunctionAsNode( - "basic/length", - length, - ["*"], - "number" - ); - - function DownloadData() { - this.size = [60, 30]; - this.addInput("data", 0 ); - this.addInput("download", LiteGraph.ACTION ); - this.properties = { filename: "data.json" }; - this.value = null; - var that = this; - this.addWidget("button","Download","", function(v){ - if(!that.value) - return; - that.downloadAsFile(); - }); - } - - DownloadData.title = "Download"; - DownloadData.desc = "Download some data"; - - DownloadData.prototype.downloadAsFile = function() - { - if(this.value == null) - return; - - var str = null; - if(this.value.constructor === String) - str = this.value; - else - str = JSON.stringify(this.value); - - var file = new Blob([str]); - var url = URL.createObjectURL( file ); - var element = document.createElement("a"); - element.setAttribute('href', url); - element.setAttribute('download', this.properties.filename ); - element.style.display = 'none'; - document.body.appendChild(element); - element.click(); - document.body.removeChild(element); - setTimeout( function(){ URL.revokeObjectURL( url ); }, 1000*60 ); //wait one minute to revoke url - } - - DownloadData.prototype.onAction = function(action, param) { - var that = this; - setTimeout( function(){ that.downloadAsFile(); }, 100); //deferred to avoid blocking the renderer with the popup - } - - DownloadData.prototype.onExecute = function() { - if (this.inputs[0]) { - this.value = this.getInputData(0); - } - }; - - DownloadData.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this.properties.filename; - } - return this.title; - }; - - LiteGraph.registerNodeType("basic/download", DownloadData); - - - - //Watch a value in the editor - function Watch() { - this.size = [60, 30]; - this.addInput("value", 0, { label: "" }); - this.value = 0; - } - - Watch.title = "Watch"; - Watch.desc = "Show value of input"; - - Watch.prototype.onExecute = function() { - if (this.inputs[0]) { - this.value = this.getInputData(0); - } - }; - - Watch.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this.inputs[0].label; - } - return this.title; - }; - - Watch.toString = function(o) { - if (o == null) { - return "null"; - } else if (o.constructor === Number) { - return o.toFixed(3); - } else if (o.constructor === Array) { - var str = "["; - for (var i = 0; i < o.length; ++i) { - str += Watch.toString(o[i]) + (i + 1 != o.length ? "," : ""); - } - str += "]"; - return str; - } else { - return String(o); - } - }; - - Watch.prototype.onDrawBackground = function(ctx) { - //show the current value - this.inputs[0].label = Watch.toString(this.value); - }; - - LiteGraph.registerNodeType("basic/watch", Watch); - - //in case one type doesnt match other type but you want to connect them anyway - function Cast() { - this.addInput("in", 0); - this.addOutput("out", 0); - this.size = [40, 30]; - } - - Cast.title = "Cast"; - Cast.desc = "Allows to connect different types"; - - Cast.prototype.onExecute = function() { - this.setOutputData(0, this.getInputData(0)); - }; - - LiteGraph.registerNodeType("basic/cast", Cast); - - //Show value inside the debug console - function Console() { - this.mode = LiteGraph.ON_EVENT; - this.size = [80, 30]; - this.addProperty("msg", ""); - this.addInput("log", LiteGraph.EVENT); - this.addInput("msg", 0); - } - - Console.title = "Console"; - Console.desc = "Show value inside the console"; - - Console.prototype.onAction = function(action, param) { - if (action == "log") { - console.log(param); - } else if (action == "warn") { - console.warn(param); - } else if (action == "error") { - console.error(param); - } - }; - - Console.prototype.onExecute = function() { - var msg = this.getInputData(1); - if (msg !== null) { - this.properties.msg = msg; - } - console.log(msg); - }; - - Console.prototype.onGetInputs = function() { - return [ - ["log", LiteGraph.ACTION], - ["warn", LiteGraph.ACTION], - ["error", LiteGraph.ACTION] - ]; - }; - - LiteGraph.registerNodeType("basic/console", Console); - - //Show value inside the debug console - function Alert() { - this.mode = LiteGraph.ON_EVENT; - this.addProperty("msg", ""); - this.addInput("", LiteGraph.EVENT); - var that = this; - this.widget = this.addWidget("text", "Text", "", function(v) { - that.properties.msg = v; - }); - this.widgets_up = true; - this.size = [200, 30]; - } - - Alert.title = "Alert"; - Alert.desc = "Show an alert window"; - Alert.color = "#510"; - - Alert.prototype.onConfigure = function(o) { - this.widget.value = o.properties.msg; - }; - - Alert.prototype.onAction = function(action, param) { - var msg = this.properties.msg; - setTimeout(function() { - alert(msg); - }, 10); - }; - - LiteGraph.registerNodeType("basic/alert", Alert); - - //Execites simple code - function NodeScript() { - this.size = [60, 30]; - this.addProperty("onExecute", "return A;"); - this.addInput("A", ""); - this.addInput("B", ""); - this.addOutput("out", ""); - - this._func = null; - this.data = {}; - } - - NodeScript.prototype.onConfigure = function(o) { - if (o.properties.onExecute && LiteGraph.allow_scripts) - this.compileCode(o.properties.onExecute); - else - console.warn("Script not compiled, LiteGraph.allow_scripts is false"); - }; - - NodeScript.title = "Script"; - NodeScript.desc = "executes a code (max 100 characters)"; - - NodeScript.widgets_info = { - onExecute: { type: "code" } - }; - - NodeScript.prototype.onPropertyChanged = function(name, value) { - if (name == "onExecute" && LiteGraph.allow_scripts) - this.compileCode(value); - else - console.warn("Script not compiled, LiteGraph.allow_scripts is false"); - }; - - NodeScript.prototype.compileCode = function(code) { - this._func = null; - if (code.length > 256) { - console.warn("Script too long, max 256 chars"); - } else { - var code_low = code.toLowerCase(); - var forbidden_words = [ - "script", - "body", - "document", - "eval", - "nodescript", - "function" - ]; //bad security solution - for (var i = 0; i < forbidden_words.length; ++i) { - if (code_low.indexOf(forbidden_words[i]) != -1) { - console.warn("invalid script"); - return; - } - } - try { - this._func = new Function("A", "B", "C", "DATA", "node", code); - } catch (err) { - console.error("Error parsing script"); - console.error(err); - } - } - }; - - NodeScript.prototype.onExecute = function() { - if (!this._func) { - return; - } - - try { - var A = this.getInputData(0); - var B = this.getInputData(1); - var C = this.getInputData(2); - this.setOutputData(0, this._func(A, B, C, this.data, this)); - } catch (err) { - console.error("Error in script"); - console.error(err); - } - }; - - NodeScript.prototype.onGetOutputs = function() { - return [["C", ""]]; - }; - - LiteGraph.registerNodeType("basic/script", NodeScript); -})(this); - -//event related nodes -======= ->>>>>>> custom widget custom size support (function(global) { // ************************************************************* // LiteGraph CLASS ******* @@ -15847,10 +3596,7 @@ if (typeof exports != "undefined") { //allows nodes to block connection if (target_node.onConnectInput) { - if ( - target_node.onConnectInput(target_slot, output.type, output) === - false - ) { + if ( target_node.onConnectInput(target_slot, output.type, output, this, slot) === false ) { return null; } } @@ -17844,8 +5590,8 @@ LGraphNode.prototype.executeAction = function(action) widgets_height += 8; } - var min_height = max_slots * LiteGraph.NODE_SLOT_HEIGHT + widgets_height; if (this.resizing_node.size[1] < min_height) { + var min_height = max_slots * LiteGraph.NODE_SLOT_HEIGHT + widgets_height; this.resizing_node.size[1] = min_height; } @@ -22352,25 +10098,30 @@ LGraphNode.prototype.executeAction = function(action) if (slot) { //on slot menu_info = []; - if ( - slot && - slot.output && - slot.output.links && - slot.output.links.length - ) { - menu_info.push({ content: "Disconnect Links", slot: slot }); + if (node.getSlotMenuOptions) { + menu_info = node.getSlotMenuOptions(slot); + } else { + if ( + slot && + slot.output && + slot.output.links && + slot.output.links.length + ) { + menu_info.push({ content: "Disconnect Links", slot: slot }); + } + var _slot = slot.input || slot.output; + menu_info.push( + _slot.locked + ? "Cannot remove" + : { content: "Remove Slot", slot: slot } + ); + menu_info.push( + _slot.nameLocked + ? "Cannot rename" + : { content: "Rename Slot", slot: slot } + ); + } - var _slot = slot.input || slot.output; - menu_info.push( - _slot.locked - ? "Cannot remove" - : { content: "Remove Slot", slot: slot } - ); - menu_info.push( - _slot.nameLocked - ? "Cannot rename" - : { content: "Rename Slot", slot: slot } - ); options.title = (slot.input ? slot.input.type : slot.output.type) || "*"; if (slot.input && slot.input.type == LiteGraph.ACTION) { @@ -22808,11 +10559,13 @@ LGraphNode.prototype.executeAction = function(action) var body_rect = document.body.getBoundingClientRect(); var root_rect = root.getBoundingClientRect(); + if(body_rect.height == 0) + console.error("document.body height is 0. That is dangerous, set html,body { height: 100%; }"); - if (left > body_rect.width - root_rect.width - 10) { + if (body_rect.width && left > body_rect.width - root_rect.width - 10) { left = body_rect.width - root_rect.width - 10; } - if (top > body_rect.height - root_rect.height - 10) { + if (body_rect.height && top > body_rect.height - root_rect.height - 10) { top = body_rect.height - root_rect.height - 10; } } @@ -27350,6 +15103,7 @@ if (typeof exports != "undefined") { (function(global) { var LiteGraph = global.LiteGraph; + var LGraphCanvas = global.LGraphCanvas; //Works with Litegl.js to create WebGL nodes global.LGraphTexture = null; @@ -29975,6156 +17729,6 @@ void main() {\n\ this.properties[input.name] = v; } -<<<<<<< HEAD -})(this); - -(function(global) { - var LiteGraph = global.LiteGraph; - - function Selector() { - this.addInput("sel", "number"); - this.addInput("A"); - this.addInput("B"); - this.addInput("C"); - this.addInput("D"); - this.addOutput("out"); - - this.selected = 0; - } - - Selector.title = "Selector"; - Selector.desc = "selects an output"; - - Selector.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } - ctx.fillStyle = "#AFB"; - var y = (this.selected + 1) * LiteGraph.NODE_SLOT_HEIGHT + 6; - ctx.beginPath(); - ctx.moveTo(50, y); - ctx.lineTo(50, y + LiteGraph.NODE_SLOT_HEIGHT); - ctx.lineTo(34, y + LiteGraph.NODE_SLOT_HEIGHT * 0.5); - ctx.fill(); - }; - - Selector.prototype.onExecute = function() { - var sel = this.getInputData(0); - if (sel == null || sel.constructor !== Number) - sel = 0; - this.selected = sel = Math.round(sel) % (this.inputs.length - 1); - var v = this.getInputData(sel + 1); - if (v !== undefined) { - this.setOutputData(0, v); - } - }; - - Selector.prototype.onGetInputs = function() { - return [["E", 0], ["F", 0], ["G", 0], ["H", 0]]; - }; - - LiteGraph.registerNodeType("logic/selector", Selector); - - function Sequence() { - this.properties = { - sequence: "A,B,C" - }; - this.addInput("index", "number"); - this.addInput("seq"); - this.addOutput("out"); - - this.index = 0; - this.values = this.properties.sequence.split(","); - } - - Sequence.title = "Sequence"; - Sequence.desc = "select one element from a sequence from a string"; - - Sequence.prototype.onPropertyChanged = function(name, value) { - if (name == "sequence") { - this.values = value.split(","); - } - }; - - Sequence.prototype.onExecute = function() { - var seq = this.getInputData(1); - if (seq && seq != this.current_sequence) { - this.values = seq.split(","); - this.current_sequence = seq; - } - var index = this.getInputData(0); - if (index == null) { - index = 0; - } - this.index = index = Math.round(index) % this.values.length; - - this.setOutputData(0, this.values[index]); - }; - - LiteGraph.registerNodeType("logic/sequence", Sequence); -})(this); - -(function(global) { - var LiteGraph = global.LiteGraph; - - function GraphicsPlot() { - this.addInput("A", "Number"); - this.addInput("B", "Number"); - this.addInput("C", "Number"); - this.addInput("D", "Number"); - - this.values = [[], [], [], []]; - this.properties = { scale: 2 }; - } - - GraphicsPlot.title = "Plot"; - GraphicsPlot.desc = "Plots data over time"; - GraphicsPlot.colors = ["#FFF", "#F99", "#9F9", "#99F"]; - - GraphicsPlot.prototype.onExecute = function(ctx) { - if (this.flags.collapsed) { - return; - } - - var size = this.size; - - for (var i = 0; i < 4; ++i) { - var v = this.getInputData(i); - if (v == null) { - continue; - } - var values = this.values[i]; - values.push(v); - if (values.length > size[0]) { - values.shift(); - } - } - }; - - GraphicsPlot.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } - - var size = this.size; - - var scale = (0.5 * size[1]) / this.properties.scale; - var colors = GraphicsPlot.colors; - var offset = size[1] * 0.5; - - ctx.fillStyle = "#000"; - ctx.fillRect(0, 0, size[0], size[1]); - ctx.strokeStyle = "#555"; - ctx.beginPath(); - ctx.moveTo(0, offset); - ctx.lineTo(size[0], offset); - ctx.stroke(); - - if (this.inputs) { - for (var i = 0; i < 4; ++i) { - var values = this.values[i]; - if (!this.inputs[i] || !this.inputs[i].link) { - continue; - } - ctx.strokeStyle = colors[i]; - ctx.beginPath(); - var v = values[0] * scale * -1 + offset; - ctx.moveTo(0, Math.clamp(v, 0, size[1])); - for (var j = 1; j < values.length && j < size[0]; ++j) { - var v = values[j] * scale * -1 + offset; - ctx.lineTo(j, Math.clamp(v, 0, size[1])); - } - ctx.stroke(); - } - } - }; - - LiteGraph.registerNodeType("graphics/plot", GraphicsPlot); - - function GraphicsImage() { - this.addOutput("frame", "image"); - this.properties = { url: "" }; - } - - GraphicsImage.title = "Image"; - GraphicsImage.desc = "Image loader"; - GraphicsImage.widgets = [{ name: "load", text: "Load", type: "button" }]; - - GraphicsImage.supported_extensions = ["jpg", "jpeg", "png", "gif"]; - - GraphicsImage.prototype.onAdded = function() { - if (this.properties["url"] != "" && this.img == null) { - this.loadImage(this.properties["url"]); - } - }; - - GraphicsImage.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } - if (this.img && this.size[0] > 5 && this.size[1] > 5 && this.img.width) { - ctx.drawImage(this.img, 0, 0, this.size[0], this.size[1]); - } - }; - - GraphicsImage.prototype.onExecute = function() { - if (!this.img) { - this.boxcolor = "#000"; - } - if (this.img && this.img.width) { - this.setOutputData(0, this.img); - } else { - this.setOutputData(0, null); - } - if (this.img && this.img.dirty) { - this.img.dirty = false; - } - }; - - GraphicsImage.prototype.onPropertyChanged = function(name, value) { - this.properties[name] = value; - if (name == "url" && value != "") { - this.loadImage(value); - } - - return true; - }; - - GraphicsImage.prototype.loadImage = function(url, callback) { - if (url == "") { - this.img = null; - return; - } - - this.img = document.createElement("img"); - - if (url.substr(0, 4) == "http" && LiteGraph.proxy) { - url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); - } - - this.img.src = url; - this.boxcolor = "#F95"; - var that = this; - this.img.onload = function() { - if (callback) { - callback(this); - } - console.log( "Image loaded, size: " + that.img.width + "x" + that.img.height ); - this.dirty = true; - that.boxcolor = "#9F9"; - that.setDirtyCanvas(true); - }; - this.img.onerror = function() { - console.log("error loading the image:" + url); - } - }; - - GraphicsImage.prototype.onWidget = function(e, widget) { - if (widget.name == "load") { - this.loadImage(this.properties["url"]); - } - }; - - GraphicsImage.prototype.onDropFile = function(file) { - var that = this; - if (this._url) { - URL.revokeObjectURL(this._url); - } - this._url = URL.createObjectURL(file); - this.properties.url = this._url; - this.loadImage(this._url, function(img) { - that.size[1] = (img.height / img.width) * that.size[0]; - }); - }; - - LiteGraph.registerNodeType("graphics/image", GraphicsImage); - - function ColorPalette() { - this.addInput("f", "number"); - this.addOutput("Color", "color"); - this.properties = { - colorA: "#444444", - colorB: "#44AAFF", - colorC: "#44FFAA", - colorD: "#FFFFFF" - }; - } - - ColorPalette.title = "Palette"; - ColorPalette.desc = "Generates a color"; - - ColorPalette.prototype.onExecute = function() { - var c = []; - - if (this.properties.colorA != null) { - c.push(hex2num(this.properties.colorA)); - } - if (this.properties.colorB != null) { - c.push(hex2num(this.properties.colorB)); - } - if (this.properties.colorC != null) { - c.push(hex2num(this.properties.colorC)); - } - if (this.properties.colorD != null) { - c.push(hex2num(this.properties.colorD)); - } - - var f = this.getInputData(0); - if (f == null) { - f = 0.5; - } - if (f > 1.0) { - f = 1.0; - } else if (f < 0.0) { - f = 0.0; - } - - if (c.length == 0) { - return; - } - - var result = [0, 0, 0]; - if (f == 0) { - result = c[0]; - } else if (f == 1) { - result = c[c.length - 1]; - } else { - var pos = (c.length - 1) * f; - var c1 = c[Math.floor(pos)]; - var c2 = c[Math.floor(pos) + 1]; - var t = pos - Math.floor(pos); - result[0] = c1[0] * (1 - t) + c2[0] * t; - result[1] = c1[1] * (1 - t) + c2[1] * t; - result[2] = c1[2] * (1 - t) + c2[2] * t; - } - - /* - c[0] = 1.0 - Math.abs( Math.sin( 0.1 * reModular.getTime() * Math.PI) ); - c[1] = Math.abs( Math.sin( 0.07 * reModular.getTime() * Math.PI) ); - c[2] = Math.abs( Math.sin( 0.01 * reModular.getTime() * Math.PI) ); - */ - - for (var i in result) { - result[i] /= 255; - } - - this.boxcolor = colorToString(result); - this.setOutputData(0, result); - }; - - LiteGraph.registerNodeType("color/palette", ColorPalette); - - function ImageFrame() { - this.addInput("", "image,canvas"); - this.size = [200, 200]; - } - - ImageFrame.title = "Frame"; - ImageFrame.desc = "Frame viewerew"; - ImageFrame.widgets = [ - { name: "resize", text: "Resize box", type: "button" }, - { name: "view", text: "View Image", type: "button" } - ]; - - ImageFrame.prototype.onDrawBackground = function(ctx) { - if (this.frame && !this.flags.collapsed) { - ctx.drawImage(this.frame, 0, 0, this.size[0], this.size[1]); - } - }; - - ImageFrame.prototype.onExecute = function() { - this.frame = this.getInputData(0); - this.setDirtyCanvas(true); - }; - - ImageFrame.prototype.onWidget = function(e, widget) { - if (widget.name == "resize" && this.frame) { - var width = this.frame.width; - var height = this.frame.height; - - if (!width && this.frame.videoWidth != null) { - width = this.frame.videoWidth; - height = this.frame.videoHeight; - } - - if (width && height) { - this.size = [width, height]; - } - this.setDirtyCanvas(true, true); - } else if (widget.name == "view") { - this.show(); - } - }; - - ImageFrame.prototype.show = function() { - //var str = this.canvas.toDataURL("image/png"); - if (showElement && this.frame) { - showElement(this.frame); - } - }; - - LiteGraph.registerNodeType("graphics/frame", ImageFrame); - - function ImageFade() { - this.addInputs([ - ["img1", "image"], - ["img2", "image"], - ["fade", "number"] - ]); - this.addOutput("", "image"); - this.properties = { fade: 0.5, width: 512, height: 512 }; - } - - ImageFade.title = "Image fade"; - ImageFade.desc = "Fades between images"; - ImageFade.widgets = [ - { name: "resizeA", text: "Resize to A", type: "button" }, - { name: "resizeB", text: "Resize to B", type: "button" } - ]; - - ImageFade.prototype.onAdded = function() { - this.createCanvas(); - var ctx = this.canvas.getContext("2d"); - ctx.fillStyle = "#000"; - ctx.fillRect(0, 0, this.properties["width"], this.properties["height"]); - }; - - ImageFade.prototype.createCanvas = function() { - this.canvas = document.createElement("canvas"); - this.canvas.width = this.properties["width"]; - this.canvas.height = this.properties["height"]; - }; - - ImageFade.prototype.onExecute = function() { - var ctx = this.canvas.getContext("2d"); - this.canvas.width = this.canvas.width; - - var A = this.getInputData(0); - if (A != null) { - ctx.drawImage(A, 0, 0, this.canvas.width, this.canvas.height); - } - - var fade = this.getInputData(2); - if (fade == null) { - fade = this.properties["fade"]; - } else { - this.properties["fade"] = fade; - } - - ctx.globalAlpha = fade; - var B = this.getInputData(1); - if (B != null) { - ctx.drawImage(B, 0, 0, this.canvas.width, this.canvas.height); - } - ctx.globalAlpha = 1.0; - - this.setOutputData(0, this.canvas); - this.setDirtyCanvas(true); - }; - - LiteGraph.registerNodeType("graphics/imagefade", ImageFade); - - function ImageCrop() { - this.addInput("", "image"); - this.addOutput("", "image"); - this.properties = { width: 256, height: 256, x: 0, y: 0, scale: 1.0 }; - this.size = [50, 20]; - } - - ImageCrop.title = "Crop"; - ImageCrop.desc = "Crop Image"; - - ImageCrop.prototype.onAdded = function() { - this.createCanvas(); - }; - - ImageCrop.prototype.createCanvas = function() { - this.canvas = document.createElement("canvas"); - this.canvas.width = this.properties["width"]; - this.canvas.height = this.properties["height"]; - }; - - ImageCrop.prototype.onExecute = function() { - var input = this.getInputData(0); - if (!input) { - return; - } - - if (input.width) { - var ctx = this.canvas.getContext("2d"); - - ctx.drawImage( - input, - -this.properties["x"], - -this.properties["y"], - input.width * this.properties["scale"], - input.height * this.properties["scale"] - ); - this.setOutputData(0, this.canvas); - } else { - this.setOutputData(0, null); - } - }; - - ImageCrop.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } - if (this.canvas) { - ctx.drawImage( - this.canvas, - 0, - 0, - this.canvas.width, - this.canvas.height, - 0, - 0, - this.size[0], - this.size[1] - ); - } - }; - - ImageCrop.prototype.onPropertyChanged = function(name, value) { - this.properties[name] = value; - - if (name == "scale") { - this.properties[name] = parseFloat(value); - if (this.properties[name] == 0) { - console.error("Error in scale"); - this.properties[name] = 1.0; - } - } else { - this.properties[name] = parseInt(value); - } - - this.createCanvas(); - - return true; - }; - - LiteGraph.registerNodeType("graphics/cropImage", ImageCrop); - - //CANVAS stuff - - function CanvasNode() { - this.addInput("clear", LiteGraph.ACTION); - this.addOutput("", "canvas"); - this.properties = { width: 512, height: 512, autoclear: true }; - - this.canvas = document.createElement("canvas"); - this.ctx = this.canvas.getContext("2d"); - } - - CanvasNode.title = "Canvas"; - CanvasNode.desc = "Canvas to render stuff"; - - CanvasNode.prototype.onExecute = function() { - var canvas = this.canvas; - var w = this.properties.width | 0; - var h = this.properties.height | 0; - if (canvas.width != w) { - canvas.width = w; - } - if (canvas.height != h) { - canvas.height = h; - } - - if (this.properties.autoclear) { - this.ctx.clearRect(0, 0, canvas.width, canvas.height); - } - this.setOutputData(0, canvas); - }; - - CanvasNode.prototype.onAction = function(action, param) { - if (action == "clear") { - this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); - } - }; - - LiteGraph.registerNodeType("graphics/canvas", CanvasNode); - - function DrawImageNode() { - this.addInput("canvas", "canvas"); - this.addInput("img", "image,canvas"); - this.addInput("x", "number"); - this.addInput("y", "number"); - this.properties = { x: 0, y: 0, opacity: 1 }; - } - - DrawImageNode.title = "DrawImage"; - DrawImageNode.desc = "Draws image into a canvas"; - - DrawImageNode.prototype.onExecute = function() { - var canvas = this.getInputData(0); - if (!canvas) { - return; - } - - var img = this.getInputOrProperty("img"); - if (!img) { - return; - } - - var x = this.getInputOrProperty("x"); - var y = this.getInputOrProperty("y"); - var ctx = canvas.getContext("2d"); - ctx.drawImage(img, x, y); - }; - - LiteGraph.registerNodeType("graphics/drawImage", DrawImageNode); - - function DrawRectangleNode() { - this.addInput("canvas", "canvas"); - this.addInput("x", "number"); - this.addInput("y", "number"); - this.addInput("w", "number"); - this.addInput("h", "number"); - this.properties = { - x: 0, - y: 0, - w: 10, - h: 10, - color: "white", - opacity: 1 - }; - } - - DrawRectangleNode.title = "DrawRectangle"; - DrawRectangleNode.desc = "Draws rectangle in canvas"; - - DrawRectangleNode.prototype.onExecute = function() { - var canvas = this.getInputData(0); - if (!canvas) { - return; - } - - var x = this.getInputOrProperty("x"); - var y = this.getInputOrProperty("y"); - var w = this.getInputOrProperty("w"); - var h = this.getInputOrProperty("h"); - var ctx = canvas.getContext("2d"); - ctx.fillRect(x, y, w, h); - }; - - LiteGraph.registerNodeType("graphics/drawRectangle", DrawRectangleNode); - - function ImageVideo() { - this.addInput("t", "number"); - this.addOutputs([["frame", "image"], ["t", "number"], ["d", "number"]]); - this.properties = { url: "", use_proxy: true }; - } - - ImageVideo.title = "Video"; - ImageVideo.desc = "Video playback"; - ImageVideo.widgets = [ - { name: "play", text: "PLAY", type: "minibutton" }, - { name: "stop", text: "STOP", type: "minibutton" }, - { name: "demo", text: "Demo video", type: "button" }, - { name: "mute", text: "Mute video", type: "button" } - ]; - - ImageVideo.prototype.onExecute = function() { - if (!this.properties.url) { - return; - } - - if (this.properties.url != this._video_url) { - this.loadVideo(this.properties.url); - } - - if (!this._video || this._video.width == 0) { - return; - } - - var t = this.getInputData(0); - if (t && t >= 0 && t <= 1.0) { - this._video.currentTime = t * this._video.duration; - this._video.pause(); - } - - this._video.dirty = true; - this.setOutputData(0, this._video); - this.setOutputData(1, this._video.currentTime); - this.setOutputData(2, this._video.duration); - this.setDirtyCanvas(true); - }; - - ImageVideo.prototype.onStart = function() { - this.play(); - }; - - ImageVideo.prototype.onStop = function() { - this.stop(); - }; - - ImageVideo.prototype.loadVideo = function(url) { - this._video_url = url; - - var pos = url.substr(0,10).indexOf(":"); - var protocol = ""; - if(pos != -1) - protocol = url.substr(0,pos); - - var host = ""; - if(protocol) - { - host = url.substr(0,url.indexOf("/",protocol.length + 3)); - host = host.substr(protocol.length+3); - } - - if ( - this.properties.use_proxy && - protocol && - LiteGraph.proxy && - host != location.host - ) { - url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); - } - - this._video = document.createElement("video"); - this._video.src = url; - this._video.type = "type=video/mp4"; - - this._video.muted = true; - this._video.autoplay = true; - - var that = this; - this._video.addEventListener("loadedmetadata", function(e) { - //onload - console.log("Duration: " + this.duration + " seconds"); - console.log("Size: " + this.videoWidth + "," + this.videoHeight); - that.setDirtyCanvas(true); - this.width = this.videoWidth; - this.height = this.videoHeight; - }); - this._video.addEventListener("progress", function(e) { - //onload - console.log("video loading..."); - }); - this._video.addEventListener("error", function(e) { - console.error("Error loading video: " + this.src); - if (this.error) { - switch (this.error.code) { - case this.error.MEDIA_ERR_ABORTED: - console.error("You stopped the video."); - break; - case this.error.MEDIA_ERR_NETWORK: - console.error("Network error - please try again later."); - break; - case this.error.MEDIA_ERR_DECODE: - console.error("Video is broken.."); - break; - case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED: - console.error("Sorry, your browser can't play this video."); - break; - } - } - }); - - this._video.addEventListener("ended", function(e) { - console.log("Video Ended."); - this.play(); //loop - }); - - //document.body.appendChild(this.video); - }; - - ImageVideo.prototype.onPropertyChanged = function(name, value) { - this.properties[name] = value; - if (name == "url" && value != "") { - this.loadVideo(value); - } - - return true; - }; - - ImageVideo.prototype.play = function() { - if (this._video && this._video.videoWidth ) { //is loaded - this._video.play(); - } - }; - - ImageVideo.prototype.playPause = function() { - if (!this._video) { - return; - } - if (this._video.paused) { - this.play(); - } else { - this.pause(); - } - }; - - ImageVideo.prototype.stop = function() { - if (!this._video) { - return; - } - this._video.pause(); - this._video.currentTime = 0; - }; - - ImageVideo.prototype.pause = function() { - if (!this._video) { - return; - } - console.log("Video paused"); - this._video.pause(); - }; - - ImageVideo.prototype.onWidget = function(e, widget) { - /* - if(widget.name == "demo") - { - this.loadVideo(); - } - else if(widget.name == "play") - { - if(this._video) - this.playPause(); - } - if(widget.name == "stop") - { - this.stop(); - } - else if(widget.name == "mute") - { - if(this._video) - this._video.muted = !this._video.muted; - } - */ - }; - - LiteGraph.registerNodeType("graphics/video", ImageVideo); - - // Texture Webcam ***************************************** - function ImageWebcam() { - this.addOutput("Webcam", "image"); - this.properties = { facingMode: "user" }; - this.boxcolor = "black"; - this.frame = 0; - } - - ImageWebcam.title = "Webcam"; - ImageWebcam.desc = "Webcam image"; - ImageWebcam.is_webcam_open = false; - - ImageWebcam.prototype.openStream = function() { - if (!navigator.getUserMedia) { - //console.log('getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags'); - return; - } - - this._waiting_confirmation = true; - - // Not showing vendor prefixes. - var constraints = { - audio: false, - video: { facingMode: this.properties.facingMode } - }; - navigator.mediaDevices - .getUserMedia(constraints) - .then(this.streamReady.bind(this)) - .catch(onFailSoHard); - - var that = this; - function onFailSoHard(e) { - console.log("Webcam rejected", e); - that._webcam_stream = false; - ImageWebcam.is_webcam_open = false; - that.boxcolor = "red"; - that.trigger("stream_error"); - } - }; - - ImageWebcam.prototype.closeStream = function() { - if (this._webcam_stream) { - var tracks = this._webcam_stream.getTracks(); - if (tracks.length) { - for (var i = 0; i < tracks.length; ++i) { - tracks[i].stop(); - } - } - ImageWebcam.is_webcam_open = false; - this._webcam_stream = null; - this._video = null; - this.boxcolor = "black"; - this.trigger("stream_closed"); - } - }; - - ImageWebcam.prototype.onPropertyChanged = function(name, value) { - if (name == "facingMode") { - this.properties.facingMode = value; - this.closeStream(); - this.openStream(); - } - }; - - ImageWebcam.prototype.onRemoved = function() { - this.closeStream(); - }; - - ImageWebcam.prototype.streamReady = function(localMediaStream) { - this._webcam_stream = localMediaStream; - //this._waiting_confirmation = false; - this.boxcolor = "green"; - - var video = this._video; - if (!video) { - video = document.createElement("video"); - video.autoplay = true; - video.srcObject = localMediaStream; - this._video = video; - //document.body.appendChild( video ); //debug - //when video info is loaded (size and so) - video.onloadedmetadata = function(e) { - // Ready to go. Do some stuff. - console.log(e); - ImageWebcam.is_webcam_open = true; - }; - } - - this.trigger("stream_ready", video); - }; - - ImageWebcam.prototype.onExecute = function() { - if (this._webcam_stream == null && !this._waiting_confirmation) { - this.openStream(); - } - - if (!this._video || !this._video.videoWidth) { - return; - } - - this._video.frame = ++this.frame; - this._video.width = this._video.videoWidth; - this._video.height = this._video.videoHeight; - this.setOutputData(0, this._video); - for (var i = 1; i < this.outputs.length; ++i) { - if (!this.outputs[i]) { - continue; - } - switch (this.outputs[i].name) { - case "width": - this.setOutputData(i, this._video.videoWidth); - break; - case "height": - this.setOutputData(i, this._video.videoHeight); - break; - } - } - }; - - ImageWebcam.prototype.getExtraMenuOptions = function(graphcanvas) { - var that = this; - var txt = !that.properties.show ? "Show Frame" : "Hide Frame"; - return [ - { - content: txt, - callback: function() { - that.properties.show = !that.properties.show; - } - } - ]; - }; - - ImageWebcam.prototype.onDrawBackground = function(ctx) { - if ( - this.flags.collapsed || - this.size[1] <= 20 || - !this.properties.show - ) { - return; - } - - if (!this._video) { - return; - } - - //render to graph canvas - ctx.save(); - ctx.drawImage(this._video, 0, 0, this.size[0], this.size[1]); - ctx.restore(); - }; - - ImageWebcam.prototype.onGetOutputs = function() { - return [ - ["width", "number"], - ["height", "number"], - ["stream_ready", LiteGraph.EVENT], - ["stream_closed", LiteGraph.EVENT], - ["stream_error", LiteGraph.EVENT] - ]; - }; - - LiteGraph.registerNodeType("graphics/webcam", ImageWebcam); -})(this); - -(function(global) { - var LiteGraph = global.LiteGraph; - - //Works with Litegl.js to create WebGL nodes - global.LGraphTexture = null; - - if (typeof GL == "undefined") - return; - - LGraphCanvas.link_type_colors["Texture"] = "#987"; - - function LGraphTexture() { - this.addOutput("tex", "Texture"); - this.addOutput("name", "string"); - this.properties = { name: "", filter: true }; - this.size = [ - LGraphTexture.image_preview_size, - LGraphTexture.image_preview_size - ]; - } - - global.LGraphTexture = LGraphTexture; - - LGraphTexture.title = "Texture"; - LGraphTexture.desc = "Texture"; - LGraphTexture.widgets_info = { - name: { widget: "texture" }, - filter: { widget: "checkbox" } - }; - - //REPLACE THIS TO INTEGRATE WITH YOUR FRAMEWORK - LGraphTexture.loadTextureCallback = null; //function in charge of loading textures when not present in the container - LGraphTexture.image_preview_size = 256; - - //flags to choose output texture type - LGraphTexture.PASS_THROUGH = 1; //do not apply FX - LGraphTexture.COPY = 2; //create new texture with the same properties as the origin texture - LGraphTexture.LOW = 3; //create new texture with low precision (byte) - LGraphTexture.HIGH = 4; //create new texture with high precision (half-float) - LGraphTexture.REUSE = 5; //reuse input texture - LGraphTexture.DEFAULT = 2; - - LGraphTexture.MODE_VALUES = { - "pass through": LGraphTexture.PASS_THROUGH, - copy: LGraphTexture.COPY, - low: LGraphTexture.LOW, - high: LGraphTexture.HIGH, - reuse: LGraphTexture.REUSE, - default: LGraphTexture.DEFAULT - }; - - //returns the container where all the loaded textures are stored (overwrite if you have a Resources Manager) - LGraphTexture.getTexturesContainer = function() { - return gl.textures; - }; - - //process the loading of a texture (overwrite it if you have a Resources Manager) - LGraphTexture.loadTexture = function(name, options) { - options = options || {}; - var url = name; - if (url.substr(0, 7) == "http://") { - if (LiteGraph.proxy) { - //proxy external files - url = LiteGraph.proxy + url.substr(7); - } - } - - var container = LGraphTexture.getTexturesContainer(); - var tex = (container[name] = GL.Texture.fromURL(url, options)); - return tex; - }; - - LGraphTexture.getTexture = function(name) { - var container = this.getTexturesContainer(); - - if (!container) { - throw "Cannot load texture, container of textures not found"; - } - - var tex = container[name]; - if (!tex && name && name[0] != ":") { - return this.loadTexture(name); - } - - return tex; - }; - - //used to compute the appropiate output texture - LGraphTexture.getTargetTexture = function(origin, target, mode) { - if (!origin) { - throw "LGraphTexture.getTargetTexture expects a reference texture"; - } - - var tex_type = null; - - switch (mode) { - case LGraphTexture.LOW: - tex_type = gl.UNSIGNED_BYTE; - break; - case LGraphTexture.HIGH: - tex_type = gl.HIGH_PRECISION_FORMAT; - break; - case LGraphTexture.REUSE: - return origin; - break; - case LGraphTexture.COPY: - default: - tex_type = origin ? origin.type : gl.UNSIGNED_BYTE; - break; - } - - if ( - !target || - target.width != origin.width || - target.height != origin.height || - target.type != tex_type - ) { - target = new GL.Texture(origin.width, origin.height, { - type: tex_type, - format: gl.RGBA, - filter: gl.LINEAR - }); - } - - return target; - }; - - LGraphTexture.getTextureType = function(precision, ref_texture) { - var type = ref_texture ? ref_texture.type : gl.UNSIGNED_BYTE; - switch (precision) { - case LGraphTexture.HIGH: - type = gl.HIGH_PRECISION_FORMAT; - break; - case LGraphTexture.LOW: - type = gl.UNSIGNED_BYTE; - break; - //no default - } - return type; - }; - - LGraphTexture.getWhiteTexture = function() { - if (this._white_texture) { - return this._white_texture; - } - var texture = (this._white_texture = GL.Texture.fromMemory( - 1, - 1, - [255, 255, 255, 255], - { format: gl.RGBA, wrap: gl.REPEAT, filter: gl.NEAREST } - )); - return texture; - }; - - LGraphTexture.getNoiseTexture = function() { - if (this._noise_texture) { - return this._noise_texture; - } - - var noise = new Uint8Array(512 * 512 * 4); - for (var i = 0; i < 512 * 512 * 4; ++i) { - noise[i] = Math.random() * 255; - } - - var texture = GL.Texture.fromMemory(512, 512, noise, { - format: gl.RGBA, - wrap: gl.REPEAT, - filter: gl.NEAREST - }); - this._noise_texture = texture; - return texture; - }; - - LGraphTexture.prototype.onDropFile = function(data, filename, file) { - if (!data) { - this._drop_texture = null; - this.properties.name = ""; - } else { - var texture = null; - if (typeof data == "string") { - texture = GL.Texture.fromURL(data); - } else if (filename.toLowerCase().indexOf(".dds") != -1) { - texture = GL.Texture.fromDDSInMemory(data); - } else { - var blob = new Blob([file]); - var url = URL.createObjectURL(blob); - texture = GL.Texture.fromURL(url); - } - - this._drop_texture = texture; - this.properties.name = filename; - } - }; - - LGraphTexture.prototype.getExtraMenuOptions = function(graphcanvas) { - var that = this; - if (!this._drop_texture) { - return; - } - return [ - { - content: "Clear", - callback: function() { - that._drop_texture = null; - that.properties.name = ""; - } - } - ]; - }; - - LGraphTexture.prototype.onExecute = function() { - var tex = null; - if (this.isOutputConnected(1)) { - tex = this.getInputData(0); - } - - if (!tex && this._drop_texture) { - tex = this._drop_texture; - } - - if (!tex && this.properties.name) { - tex = LGraphTexture.getTexture(this.properties.name); - } - - if (!tex) { - this.setOutputData( 0, null ); - this.setOutputData( 1, "" ); - return; - } - - this._last_tex = tex; - - if (this.properties.filter === false) { - tex.setParameter(gl.TEXTURE_MAG_FILTER, gl.NEAREST); - } else { - tex.setParameter(gl.TEXTURE_MAG_FILTER, gl.LINEAR); - } - - this.setOutputData( 0, tex ); - this.setOutputData( 1, tex.fullpath || tex.filename ); - - for (var i = 2; i < this.outputs.length; i++) { - var output = this.outputs[i]; - if (!output) { - continue; - } - var v = null; - if (output.name == "width") { - v = tex.width; - } else if (output.name == "height") { - v = tex.height; - } else if (output.name == "aspect") { - v = tex.width / tex.height; - } - this.setOutputData(i, v); - } - }; - - LGraphTexture.prototype.onResourceRenamed = function( - old_name, - new_name - ) { - if (this.properties.name == old_name) { - this.properties.name = new_name; - } - }; - - LGraphTexture.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed || this.size[1] <= 20) { - return; - } - - if (this._drop_texture && ctx.webgl) { - ctx.drawImage( - this._drop_texture, - 0, - 0, - this.size[0], - this.size[1] - ); - //this._drop_texture.renderQuad(this.pos[0],this.pos[1],this.size[0],this.size[1]); - return; - } - - //Different texture? then get it from the GPU - if (this._last_preview_tex != this._last_tex) { - if (ctx.webgl) { - this._canvas = this._last_tex; - } else { - var tex_canvas = LGraphTexture.generateLowResTexturePreview( - this._last_tex - ); - if (!tex_canvas) { - return; - } - - this._last_preview_tex = this._last_tex; - this._canvas = cloneCanvas(tex_canvas); - } - } - - if (!this._canvas) { - return; - } - - //render to graph canvas - ctx.save(); - if (!ctx.webgl) { - //reverse image - ctx.translate(0, this.size[1]); - ctx.scale(1, -1); - } - ctx.drawImage(this._canvas, 0, 0, this.size[0], this.size[1]); - ctx.restore(); - }; - - //very slow, used at your own risk - LGraphTexture.generateLowResTexturePreview = function(tex) { - if (!tex) { - return null; - } - - var size = LGraphTexture.image_preview_size; - var temp_tex = tex; - - if (tex.format == gl.DEPTH_COMPONENT) { - return null; - } //cannot generate from depth - - //Generate low-level version in the GPU to speed up - if (tex.width > size || tex.height > size) { - temp_tex = this._preview_temp_tex; - if (!this._preview_temp_tex) { - temp_tex = new GL.Texture(size, size, { - minFilter: gl.NEAREST - }); - this._preview_temp_tex = temp_tex; - } - - //copy - tex.copyTo(temp_tex); - tex = temp_tex; - } - - //create intermediate canvas with lowquality version - var tex_canvas = this._preview_canvas; - if (!tex_canvas) { - tex_canvas = createCanvas(size, size); - this._preview_canvas = tex_canvas; - } - - if (temp_tex) { - temp_tex.toCanvas(tex_canvas); - } - return tex_canvas; - }; - - LGraphTexture.prototype.getResources = function(res) { - if(this.properties.name) - res[this.properties.name] = GL.Texture; - return res; - }; - - LGraphTexture.prototype.onGetInputs = function() { - return [["in", "Texture"]]; - }; - - LGraphTexture.prototype.onGetOutputs = function() { - return [ - ["width", "number"], - ["height", "number"], - ["aspect", "number"] - ]; - }; - - //used to replace shader code - LGraphTexture.replaceCode = function( code, context ) - { - return code.replace(/\{\{[a-zA-Z0-9_]*\}\}/g, function(v){ - v = v.replace( /[\{\}]/g, "" ); - return context[v] || ""; - }); - } - - LiteGraph.registerNodeType("texture/texture", LGraphTexture); - - //************************** - function LGraphTexturePreview() { - this.addInput("Texture", "Texture"); - this.properties = { flipY: false }; - this.size = [ - LGraphTexture.image_preview_size, - LGraphTexture.image_preview_size - ]; - } - - LGraphTexturePreview.title = "Preview"; - LGraphTexturePreview.desc = "Show a texture in the graph canvas"; - LGraphTexturePreview.allow_preview = false; - - LGraphTexturePreview.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } - - if (!ctx.webgl && !LGraphTexturePreview.allow_preview) { - return; - } //not working well - - var tex = this.getInputData(0); - if (!tex) { - return; - } - - var tex_canvas = null; - - if (!tex.handle && ctx.webgl) { - tex_canvas = tex; - } else { - tex_canvas = LGraphTexture.generateLowResTexturePreview(tex); - } - - //render to graph canvas - ctx.save(); - if (this.properties.flipY) { - ctx.translate(0, this.size[1]); - ctx.scale(1, -1); - } - ctx.drawImage(tex_canvas, 0, 0, this.size[0], this.size[1]); - ctx.restore(); - }; - - LiteGraph.registerNodeType("texture/preview", LGraphTexturePreview); - - //************************************** - - function LGraphTextureSave() { - this.addInput("Texture", "Texture"); - this.addOutput("tex", "Texture"); - this.addOutput("name", "string"); - this.properties = { name: "", generate_mipmaps: false }; - } - - LGraphTextureSave.title = "Save"; - LGraphTextureSave.desc = "Save a texture in the repository"; - - LGraphTextureSave.prototype.getPreviewTexture = function() - { - return this._texture; - } - - LGraphTextureSave.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if (this.properties.generate_mipmaps) { - tex.bind(0); - tex.setParameter( gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR ); - gl.generateMipmap(tex.texture_type); - tex.unbind(0); - } - - if (this.properties.name) { - //for cases where we want to perform something when storing it - if (LGraphTexture.storeTexture) { - LGraphTexture.storeTexture(this.properties.name, tex); - } else { - var container = LGraphTexture.getTexturesContainer(); - container[this.properties.name] = tex; - } - } - - this._texture = tex; - this.setOutputData(0, tex); - this.setOutputData(1, this.properties.name); - }; - - LiteGraph.registerNodeType("texture/save", LGraphTextureSave); - - //**************************************************** - - function LGraphTextureOperation() { - this.addInput("Texture", "Texture"); - this.addInput("TextureB", "Texture"); - this.addInput("value", "number"); - this.addOutput("Texture", "Texture"); - this.help = "

pixelcode must be vec3, uvcode must be vec2, is optional

\ -

uv: tex. coords

color: texture colorB: textureB

time: scene time value: input value

For multiline you must type: result = ...

"; - - this.properties = { - value: 1, - pixelcode: "color + colorB * value", - uvcode: "", - precision: LGraphTexture.DEFAULT - }; - - this.has_error = false; - } - - LGraphTextureOperation.widgets_info = { - uvcode: { widget: "code" }, - pixelcode: { widget: "code" }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureOperation.title = "Operation"; - LGraphTextureOperation.desc = "Texture shader operation"; - - LGraphTextureOperation.presets = {}; - - LGraphTextureOperation.prototype.getExtraMenuOptions = function( - graphcanvas - ) { - var that = this; - var txt = !that.properties.show ? "Show Texture" : "Hide Texture"; - return [ - { - content: txt, - callback: function() { - that.properties.show = !that.properties.show; - } - } - ]; - }; - - LGraphTextureOperation.prototype.onPropertyChanged = function() - { - this.has_error = false; - } - - LGraphTextureOperation.prototype.onDrawBackground = function(ctx) { - if ( - this.flags.collapsed || - this.size[1] <= 20 || - !this.properties.show - ) { - return; - } - - if (!this._tex) { - return; - } - - //only works if using a webgl renderer - if (this._tex.gl != ctx) { - return; - } - - //render to graph canvas - ctx.save(); - ctx.drawImage(this._tex, 0, 0, this.size[0], this.size[1]); - ctx.restore(); - }; - - LGraphTextureOperation.prototype.onExecute = function() { - var tex = this.getInputData(0); - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, tex); - return; - } - - var texB = this.getInputData(1); - - if (!this.properties.uvcode && !this.properties.pixelcode) { - return; - } - - var width = 512; - var height = 512; - if (tex) { - width = tex.width; - height = tex.height; - } else if (texB) { - width = texB.width; - height = texB.height; - } - - if(!texB) - texB = GL.Texture.getWhiteTexture(); - - var type = LGraphTexture.getTextureType( this.properties.precision, tex ); - - if (!tex && !this._tex) { - this._tex = new GL.Texture(width, height, { type: type, format: gl.RGBA, filter: gl.LINEAR }); - } else { - this._tex = LGraphTexture.getTargetTexture( tex || this._tex, this._tex, this.properties.precision ); - } - - var uvcode = ""; - if (this.properties.uvcode) { - uvcode = "uv = " + this.properties.uvcode; - if (this.properties.uvcode.indexOf(";") != -1) { - //there are line breaks, means multiline code - uvcode = this.properties.uvcode; - } - } - - var pixelcode = ""; - if (this.properties.pixelcode) { - pixelcode = "result = " + this.properties.pixelcode; - if (this.properties.pixelcode.indexOf(";") != -1) { - //there are line breaks, means multiline code - pixelcode = this.properties.pixelcode; - } - } - - var shader = this._shader; - - if ( !this.has_error && (!shader || this._shader_code != uvcode + "|" + pixelcode) ) { - - var final_pixel_code = LGraphTexture.replaceCode( LGraphTextureOperation.pixel_shader, { UV_CODE:uvcode, PIXEL_CODE:pixelcode }); - - try { - shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, final_pixel_code ); - this.boxcolor = "#00FF00"; - } catch (err) { - //console.log("Error compiling shader: ", err, final_pixel_code ); - GL.Shader.dumpErrorToConsole(err,Shader.SCREEN_VERTEX_SHADER, final_pixel_code); - this.boxcolor = "#FF0000"; - this.has_error = true; - return; - } - this._shader = shader; - this._shader_code = uvcode + "|" + pixelcode; - } - - if(!this._shader) - return; - - var value = this.getInputData(2); - if (value != null) { - this.properties.value = value; - } else { - value = parseFloat(this.properties.value); - } - - var time = this.graph.getTime(); - - this._tex.drawTo(function() { - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.CULL_FACE); - gl.disable(gl.BLEND); - if (tex) { - tex.bind(0); - } - if (texB) { - texB.bind(1); - } - var mesh = Mesh.getScreenQuad(); - shader - .uniforms({ - u_texture: 0, - u_textureB: 1, - value: value, - texSize: [width, height], - time: time - }) - .draw(mesh); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureOperation.pixel_shader = - "precision highp float;\n\ - \n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - varying vec2 v_coord;\n\ - uniform vec2 texSize;\n\ - uniform float time;\n\ - uniform float value;\n\ - \n\ - void main() {\n\ - vec2 uv = v_coord;\n\ - {{UV_CODE}};\n\ - vec4 color4 = texture2D(u_texture, uv);\n\ - vec3 color = color4.rgb;\n\ - vec4 color4B = texture2D(u_textureB, uv);\n\ - vec3 colorB = color4B.rgb;\n\ - vec3 result = color;\n\ - float alpha = 1.0;\n\ - {{PIXEL_CODE}};\n\ - gl_FragColor = vec4(result, alpha);\n\ - }\n\ - "; - - LGraphTextureOperation.registerPreset = function ( name, code ) - { - LGraphTextureOperation.presets[name] = code; - } - - LGraphTextureOperation.registerPreset("",""); - LGraphTextureOperation.registerPreset("bypass","color"); - LGraphTextureOperation.registerPreset("add","color + colorB * value"); - LGraphTextureOperation.registerPreset("substract","(color - colorB) * value"); - LGraphTextureOperation.registerPreset("mate","mix( color, colorB, color4B.a * value)"); - LGraphTextureOperation.registerPreset("invert","vec3(1.0) - color"); - LGraphTextureOperation.registerPreset("multiply","color * colorB * value"); - LGraphTextureOperation.registerPreset("divide","(color / colorB) / value"); - LGraphTextureOperation.registerPreset("difference","abs(color - colorB) * value"); - LGraphTextureOperation.registerPreset("max","max(color, colorB) * value"); - LGraphTextureOperation.registerPreset("min","min(color, colorB) * value"); - LGraphTextureOperation.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"); - LGraphTextureOperation.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"); - LGraphTextureOperation.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"); - LGraphTextureOperation.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"); - - //webglstudio stuff... - LGraphTextureOperation.prototype.onInspect = function(widgets) - { - var that = this; - widgets.addCombo("Presets","",{ values: Object.keys(LGraphTextureOperation.presets), callback: function(v){ - var code = LGraphTextureOperation.presets[v]; - if(!code) - return; - that.setProperty("pixelcode",code); - that.title = v; - widgets.refresh(); - }}); - } - - LiteGraph.registerNodeType("texture/operation", LGraphTextureOperation); - - //**************************************************** - - function LGraphTextureShader() { - this.addOutput("out", "Texture"); - this.properties = { - code: "", - u_value: 1, - u_color: [1,1,1,1], - width: 512, - height: 512, - precision: LGraphTexture.DEFAULT - }; - - this.properties.code = LGraphTextureShader.pixel_shader; - this._uniforms = { u_value: 1, u_color: vec4.create(), in_texture: 0, texSize: vec2.create(), time: 0 }; - } - - LGraphTextureShader.title = "Shader"; - LGraphTextureShader.desc = "Texture shader"; - LGraphTextureShader.widgets_info = { - code: { type: "code", lang: "glsl" }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureShader.prototype.onPropertyChanged = function( - name, - value - ) { - if (name != "code") { - return; - } - - var shader = this.getShader(); - if (!shader) { - return; - } - - //update connections - var uniforms = shader.uniformInfo; - - //remove deprecated slots - if (this.inputs) { - var already = {}; - for (var i = 0; i < this.inputs.length; ++i) { - var info = this.getInputInfo(i); - if (!info) { - continue; - } - - if (uniforms[info.name] && !already[info.name]) { - already[info.name] = true; - continue; - } - this.removeInput(i); - i--; - } - } - - //update existing ones - for (var i in uniforms) { - var info = shader.uniformInfo[i]; - if (info.loc === null) { - continue; - } //is an attribute, not a uniform - if (i == "time") { - //default one - continue; - } - - var type = "number"; - if (this._shader.samplers[i]) { - type = "texture"; - } else { - switch (info.size) { - case 1: - type = "number"; - break; - case 2: - type = "vec2"; - break; - case 3: - type = "vec3"; - break; - case 4: - type = "vec4"; - break; - case 9: - type = "mat3"; - break; - case 16: - type = "mat4"; - break; - default: - continue; - } - } - - var slot = this.findInputSlot(i); - if (slot == -1) { - this.addInput(i, type); - continue; - } - - var input_info = this.getInputInfo(slot); - if (!input_info) { - this.addInput(i, type); - } else { - if (input_info.type == type) { - continue; - } - this.removeInput(slot, type); - this.addInput(i, type); - } - } - }; - - LGraphTextureShader.prototype.getShader = function() { - //replug - if (this._shader && this._shader_code == this.properties.code) { - return this._shader; - } - - this._shader_code = this.properties.code; - this._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, this.properties.code ); - if (!this._shader) { - this.boxcolor = "red"; - return null; - } else { - this.boxcolor = "green"; - } - return this._shader; - }; - - LGraphTextureShader.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var shader = this.getShader(); - if (!shader) { - return; - } - - var tex_slot = 0; - var in_tex = null; - - //set uniforms - if(this.inputs) - for (var i = 0; i < this.inputs.length; ++i) { - var info = this.getInputInfo(i); - var data = this.getInputData(i); - if (data == null) { - continue; - } - - if (data.constructor === GL.Texture) { - data.bind(tex_slot); - if (!in_tex) { - in_tex = data; - } - data = tex_slot; - tex_slot++; - } - shader.setUniform(info.name, data); //data is tex_slot - } - - var uniforms = this._uniforms; - var type = LGraphTexture.getTextureType( this.properties.precision, in_tex ); - - //render to texture - var w = this.properties.width | 0; - var h = this.properties.height | 0; - if (w == 0) { - w = in_tex ? in_tex.width : gl.canvas.width; - } - if (h == 0) { - h = in_tex ? in_tex.height : gl.canvas.height; - } - uniforms.texSize[0] = w; - uniforms.texSize[1] = h; - uniforms.time = this.graph.getTime(); - uniforms.u_value = this.properties.u_value; - uniforms.u_color.set( this.properties.u_color ); - - if ( !this._tex || this._tex.type != type || this._tex.width != w || this._tex.height != h ) { - this._tex = new GL.Texture(w, h, { type: type, format: gl.RGBA, filter: gl.LINEAR }); - } - var tex = this._tex; - tex.drawTo(function() { - shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad()); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureShader.pixel_shader = -"precision highp float;\n\ -\n\ -varying vec2 v_coord;\n\ -uniform float time; //time in seconds\n\ -uniform vec2 texSize; //tex resolution\n\ -uniform float u_value;\n\ -uniform vec4 u_color;\n\n\ -void main() {\n\ - vec2 uv = v_coord;\n\ - vec3 color = vec3(0.0);\n\ - //your code here\n\ - color.xy=uv;\n\n\ - gl_FragColor = vec4(color, 1.0);\n\ -}\n\ -"; - - LiteGraph.registerNodeType("texture/shader", LGraphTextureShader); - - // Texture Scale Offset - - function LGraphTextureScaleOffset() { - this.addInput("in", "Texture"); - this.addInput("scale", "vec2"); - this.addInput("offset", "vec2"); - this.addOutput("out", "Texture"); - this.properties = { - offset: vec2.fromValues(0, 0), - scale: vec2.fromValues(1, 1), - precision: LGraphTexture.DEFAULT - }; - } - - LGraphTextureScaleOffset.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureScaleOffset.title = "Scale/Offset"; - LGraphTextureScaleOffset.desc = "Applies an scaling and offseting"; - - LGraphTextureScaleOffset.prototype.onExecute = function() { - var tex = this.getInputData(0); - - if (!this.isOutputConnected(0) || !tex) { - return; - } //saves work - - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, tex); - return; - } - - var width = tex.width; - var height = tex.height; - var type = this.precision === LGraphTexture.LOW ? gl.UNSIGNED_BYTE : gl.HIGH_PRECISION_FORMAT; - if (this.precision === LGraphTexture.DEFAULT) { - type = tex.type; - } - - if ( - !this._tex || - this._tex.width != width || - this._tex.height != height || - this._tex.type != type - ) { - this._tex = new GL.Texture(width, height, { - type: type, - format: gl.RGBA, - filter: gl.LINEAR - }); - } - - var shader = this._shader; - - if (!shader) { - shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureScaleOffset.pixel_shader - ); - } - - var scale = this.getInputData(1); - if (scale) { - this.properties.scale[0] = scale[0]; - this.properties.scale[1] = scale[1]; - } else { - scale = this.properties.scale; - } - - var offset = this.getInputData(2); - if (offset) { - this.properties.offset[0] = offset[0]; - this.properties.offset[1] = offset[1]; - } else { - offset = this.properties.offset; - } - - this._tex.drawTo(function() { - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.CULL_FACE); - gl.disable(gl.BLEND); - tex.bind(0); - var mesh = Mesh.getScreenQuad(); - shader - .uniforms({ - u_texture: 0, - u_scale: scale, - u_offset: offset - }) - .draw(mesh); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureScaleOffset.pixel_shader = - "precision highp float;\n\ - \n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - varying vec2 v_coord;\n\ - uniform vec2 u_scale;\n\ - uniform vec2 u_offset;\n\ - \n\ - void main() {\n\ - vec2 uv = v_coord;\n\ - uv = uv / u_scale - u_offset;\n\ - gl_FragColor = texture2D(u_texture, uv);\n\ - }\n\ - "; - - LiteGraph.registerNodeType( - "texture/scaleOffset", - LGraphTextureScaleOffset - ); - - // Warp (distort a texture) ************************* - - function LGraphTextureWarp() { - this.addInput("in", "Texture"); - this.addInput("warp", "Texture"); - this.addInput("factor", "number"); - this.addOutput("out", "Texture"); - this.properties = { - factor: 0.01, - scale: [1,1], - offset: [0,0], - precision: LGraphTexture.DEFAULT - }; - - this._uniforms = { - u_texture: 0, - u_textureB: 1, - u_factor: 1, - u_scale: vec2.create(), - u_offset: vec2.create() - }; - } - - LGraphTextureWarp.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureWarp.title = "Warp"; - LGraphTextureWarp.desc = "Texture warp operation"; - - LGraphTextureWarp.prototype.onExecute = function() { - var tex = this.getInputData(0); - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, tex); - return; - } - - var texB = this.getInputData(1); - - var width = 512; - var height = 512; - var type = gl.UNSIGNED_BYTE; - if (tex) { - width = tex.width; - height = tex.height; - type = tex.type; - } else if (texB) { - width = texB.width; - height = texB.height; - type = texB.type; - } - - if (!tex && !this._tex) { - this._tex = new GL.Texture(width, height, { - type: - this.precision === LGraphTexture.LOW - ? gl.UNSIGNED_BYTE - : gl.HIGH_PRECISION_FORMAT, - format: gl.RGBA, - filter: gl.LINEAR - }); - } else { - this._tex = LGraphTexture.getTargetTexture( - tex || this._tex, - this._tex, - this.properties.precision - ); - } - - var shader = this._shader; - - if (!shader) { - shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureWarp.pixel_shader - ); - } - - var factor = this.getInputData(2); - if (factor != null) { - this.properties.factor = factor; - } else { - factor = parseFloat(this.properties.factor); - } - var uniforms = this._uniforms; - uniforms.u_factor = factor; - uniforms.u_scale.set( this.properties.scale ); - uniforms.u_offset.set( this.properties.offset ); - - this._tex.drawTo(function() { - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.CULL_FACE); - gl.disable(gl.BLEND); - if (tex) { - tex.bind(0); - } - if (texB) { - texB.bind(1); - } - var mesh = Mesh.getScreenQuad(); - shader - .uniforms( uniforms ) - .draw(mesh); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureWarp.pixel_shader = - "precision highp float;\n\ - \n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - varying vec2 v_coord;\n\ - uniform float u_factor;\n\ - uniform vec2 u_scale;\n\ - uniform vec2 u_offset;\n\ - \n\ - void main() {\n\ - vec2 uv = v_coord;\n\ - uv += ( texture2D(u_textureB, uv).rg - vec2(0.5)) * u_factor * u_scale + u_offset;\n\ - gl_FragColor = texture2D(u_texture, uv);\n\ - }\n\ - "; - - LiteGraph.registerNodeType("texture/warp", LGraphTextureWarp); - - //**************************************************** - - // Texture to Viewport ***************************************** - function LGraphTextureToViewport() { - this.addInput("Texture", "Texture"); - this.properties = { - additive: false, - antialiasing: false, - filter: true, - disable_alpha: false, - gamma: 1.0, - viewport: [0,0,1,1] - }; - this.size[0] = 130; - } - - LGraphTextureToViewport.title = "to Viewport"; - LGraphTextureToViewport.desc = "Texture to viewport"; - - LGraphTextureToViewport._prev_viewport = new Float32Array(4); - LGraphTextureToViewport.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if (this.properties.disable_alpha) { - gl.disable(gl.BLEND); - } else { - gl.enable(gl.BLEND); - if (this.properties.additive) { - gl.blendFunc(gl.SRC_ALPHA, gl.ONE); - } else { - gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); - } - } - - gl.disable(gl.DEPTH_TEST); - var gamma = this.properties.gamma || 1.0; - if (this.isInputConnected(1)) { - gamma = this.getInputData(1); - } - - tex.setParameter( - gl.TEXTURE_MAG_FILTER, - this.properties.filter ? gl.LINEAR : gl.NEAREST - ); - - var old_viewport = LGraphTextureToViewport._prev_viewport; - old_viewport.set( gl.viewport_data ); - var new_view = this.properties.viewport; - gl.viewport( old_viewport[0] + old_viewport[2] * new_view[0], old_viewport[1] + old_viewport[3] * new_view[1], old_viewport[2] * new_view[2], old_viewport[3] * new_view[3] ); - var viewport = gl.getViewport(); //gl.getParameter(gl.VIEWPORT); - - if (this.properties.antialiasing) { - if (!LGraphTextureToViewport._shader) { - LGraphTextureToViewport._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureToViewport.aa_pixel_shader - ); - } - - var mesh = Mesh.getScreenQuad(); - tex.bind(0); - LGraphTextureToViewport._shader - .uniforms({ - u_texture: 0, - uViewportSize: [tex.width, tex.height], - u_igamma: 1 / gamma, - inverseVP: [1 / tex.width, 1 / tex.height] - }) - .draw(mesh); - } else { - if (gamma != 1.0) { - if (!LGraphTextureToViewport._gamma_shader) { - LGraphTextureToViewport._gamma_shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureToViewport.gamma_pixel_shader - ); - } - tex.toViewport(LGraphTextureToViewport._gamma_shader, { - u_texture: 0, - u_igamma: 1 / gamma - }); - } else { - tex.toViewport(); - } - } - - gl.viewport( old_viewport[0], old_viewport[1], old_viewport[2], old_viewport[3] ); - }; - - LGraphTextureToViewport.prototype.onGetInputs = function() { - return [["gamma", "number"]]; - }; - - LGraphTextureToViewport.aa_pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 uViewportSize;\n\ - uniform vec2 inverseVP;\n\ - uniform float u_igamma;\n\ - #define FXAA_REDUCE_MIN (1.0/ 128.0)\n\ - #define FXAA_REDUCE_MUL (1.0 / 8.0)\n\ - #define FXAA_SPAN_MAX 8.0\n\ - \n\ - /* from mitsuhiko/webgl-meincraft based on the code on geeks3d.com */\n\ - vec4 applyFXAA(sampler2D tex, vec2 fragCoord)\n\ - {\n\ - vec4 color = vec4(0.0);\n\ - /*vec2 inverseVP = vec2(1.0 / uViewportSize.x, 1.0 / uViewportSize.y);*/\n\ - vec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * inverseVP).xyz;\n\ - vec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * inverseVP).xyz;\n\ - vec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * inverseVP).xyz;\n\ - vec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * inverseVP).xyz;\n\ - vec3 rgbM = texture2D(tex, fragCoord * inverseVP).xyz;\n\ - vec3 luma = vec3(0.299, 0.587, 0.114);\n\ - float lumaNW = dot(rgbNW, luma);\n\ - float lumaNE = dot(rgbNE, luma);\n\ - float lumaSW = dot(rgbSW, luma);\n\ - float lumaSE = dot(rgbSE, luma);\n\ - float lumaM = dot(rgbM, luma);\n\ - float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n\ - float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\ - \n\ - vec2 dir;\n\ - dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n\ - dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\ - \n\ - float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\ - \n\ - float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n\ - dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * inverseVP;\n\ - \n\ - vec3 rgbA = 0.5 * (texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + \n\ - texture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz);\n\ - vec3 rgbB = rgbA * 0.5 + 0.25 * (texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + \n\ - texture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz);\n\ - \n\ - //return vec4(rgbA,1.0);\n\ - float lumaB = dot(rgbB, luma);\n\ - if ((lumaB < lumaMin) || (lumaB > lumaMax))\n\ - color = vec4(rgbA, 1.0);\n\ - else\n\ - color = vec4(rgbB, 1.0);\n\ - if(u_igamma != 1.0)\n\ - color.xyz = pow( color.xyz, vec3(u_igamma) );\n\ - return color;\n\ - }\n\ - \n\ - void main() {\n\ - gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\ - }\n\ - "; - - LGraphTextureToViewport.gamma_pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_igamma;\n\ - void main() {\n\ - vec4 color = texture2D( u_texture, v_coord);\n\ - color.xyz = pow(color.xyz, vec3(u_igamma) );\n\ - gl_FragColor = color;\n\ - }\n\ - "; - - LiteGraph.registerNodeType( - "texture/toviewport", - LGraphTextureToViewport - ); - - // Texture Copy ***************************************** - function LGraphTextureCopy() { - this.addInput("Texture", "Texture"); - this.addOutput("", "Texture"); - this.properties = { - size: 0, - generate_mipmaps: false, - precision: LGraphTexture.DEFAULT - }; - } - - LGraphTextureCopy.title = "Copy"; - LGraphTextureCopy.desc = "Copy Texture"; - LGraphTextureCopy.widgets_info = { - size: { - widget: "combo", - values: [0, 32, 64, 128, 256, 512, 1024, 2048] - }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureCopy.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex && !this._temp_texture) { - return; - } - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - //copy the texture - if (tex) { - var width = tex.width; - var height = tex.height; - - if (this.properties.size != 0) { - width = this.properties.size; - height = this.properties.size; - } - - var temp = this._temp_texture; - - var type = tex.type; - if (this.properties.precision === LGraphTexture.LOW) { - type = gl.UNSIGNED_BYTE; - } else if (this.properties.precision === LGraphTexture.HIGH) { - type = gl.HIGH_PRECISION_FORMAT; - } - - if ( - !temp || - temp.width != width || - temp.height != height || - temp.type != type - ) { - var minFilter = gl.LINEAR; - if ( - this.properties.generate_mipmaps && - isPowerOfTwo(width) && - isPowerOfTwo(height) - ) { - minFilter = gl.LINEAR_MIPMAP_LINEAR; - } - this._temp_texture = new GL.Texture(width, height, { - type: type, - format: gl.RGBA, - minFilter: minFilter, - magFilter: gl.LINEAR - }); - } - tex.copyTo(this._temp_texture); - - if (this.properties.generate_mipmaps) { - this._temp_texture.bind(0); - gl.generateMipmap(this._temp_texture.texture_type); - this._temp_texture.unbind(0); - } - } - - this.setOutputData(0, this._temp_texture); - }; - - LiteGraph.registerNodeType("texture/copy", LGraphTextureCopy); - - // Texture Downsample ***************************************** - function LGraphTextureDownsample() { - this.addInput("Texture", "Texture"); - this.addOutput("", "Texture"); - this.properties = { - iterations: 1, - generate_mipmaps: false, - precision: LGraphTexture.DEFAULT - }; - } - - LGraphTextureDownsample.title = "Downsample"; - LGraphTextureDownsample.desc = "Downsample Texture"; - LGraphTextureDownsample.widgets_info = { - iterations: { type: "number", step: 1, precision: 0, min: 0 }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureDownsample.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex && !this._temp_texture) { - return; - } - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - //we do not allow any texture different than texture 2D - if (!tex || tex.texture_type !== GL.TEXTURE_2D) { - return; - } - - if (this.properties.iterations < 1) { - this.setOutputData(0, tex); - return; - } - - var shader = LGraphTextureDownsample._shader; - if (!shader) { - LGraphTextureDownsample._shader = shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureDownsample.pixel_shader - ); - } - - var width = tex.width | 0; - var height = tex.height | 0; - var type = tex.type; - if (this.properties.precision === LGraphTexture.LOW) { - type = gl.UNSIGNED_BYTE; - } else if (this.properties.precision === LGraphTexture.HIGH) { - type = gl.HIGH_PRECISION_FORMAT; - } - var iterations = this.properties.iterations || 1; - - var origin = tex; - var target = null; - - var temp = []; - var options = { - type: type, - format: tex.format - }; - - var offset = vec2.create(); - var uniforms = { - u_offset: offset - }; - - if (this._texture) { - GL.Texture.releaseTemporary(this._texture); - } - - for (var i = 0; i < iterations; ++i) { - offset[0] = 1 / width; - offset[1] = 1 / height; - width = width >> 1 || 0; - height = height >> 1 || 0; - target = GL.Texture.getTemporary(width, height, options); - temp.push(target); - origin.setParameter(GL.TEXTURE_MAG_FILTER, GL.NEAREST); - origin.copyTo(target, shader, uniforms); - if (width == 1 && height == 1) { - break; - } //nothing else to do - origin = target; - } - - //keep the last texture used - this._texture = temp.pop(); - - //free the rest - for (var i = 0; i < temp.length; ++i) { - GL.Texture.releaseTemporary(temp[i]); - } - - if (this.properties.generate_mipmaps) { - this._texture.bind(0); - gl.generateMipmap(this._texture.texture_type); - this._texture.unbind(0); - } - - this.setOutputData(0, this._texture); - }; - - LGraphTextureDownsample.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_offset;\n\ - varying vec2 v_coord;\n\ - \n\ - void main() {\n\ - vec4 color = texture2D(u_texture, v_coord );\n\ - color += texture2D(u_texture, v_coord + vec2( u_offset.x, 0.0 ) );\n\ - color += texture2D(u_texture, v_coord + vec2( 0.0, u_offset.y ) );\n\ - color += texture2D(u_texture, v_coord + vec2( u_offset.x, u_offset.y ) );\n\ - gl_FragColor = color * 0.25;\n\ - }\n\ - "; - - LiteGraph.registerNodeType( - "texture/downsample", - LGraphTextureDownsample - ); - - // Texture Average ***************************************** - function LGraphTextureAverage() { - this.addInput("Texture", "Texture"); - this.addOutput("tex", "Texture"); - this.addOutput("avg", "vec4"); - this.addOutput("lum", "number"); - this.properties = { - use_previous_frame: true, //to avoid stalls - high_quality: false //to use as much pixels as possible - }; - - this._uniforms = { - u_texture: 0, - u_mipmap_offset: 0 - }; - this._luminance = new Float32Array(4); - } - - LGraphTextureAverage.title = "Average"; - LGraphTextureAverage.desc = - "Compute a partial average (32 random samples) of a texture and stores it as a 1x1 pixel texture.\n If high_quality is true, then it generates the mipmaps first and reads from the lower one."; - - LGraphTextureAverage.prototype.onExecute = function() { - if (!this.properties.use_previous_frame) { - this.updateAverage(); - } - - var v = this._luminance; - this.setOutputData(0, this._temp_texture); - this.setOutputData(1, v); - this.setOutputData(2, (v[0] + v[1] + v[2]) / 3); - }; - - //executed before rendering the frame - LGraphTextureAverage.prototype.onPreRenderExecute = function() { - this.updateAverage(); - }; - - LGraphTextureAverage.prototype.updateAverage = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if ( - !this.isOutputConnected(0) && - !this.isOutputConnected(1) && - !this.isOutputConnected(2) - ) { - return; - } //saves work - - if (!LGraphTextureAverage._shader) { - LGraphTextureAverage._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureAverage.pixel_shader - ); - //creates 256 random numbers and stores them in two mat4 - var samples = new Float32Array(16); - for (var i = 0; i < samples.length; ++i) { - samples[i] = Math.random(); //poorly distributed samples - } - //upload only once - LGraphTextureAverage._shader.uniforms({ - u_samples_a: samples.subarray(0, 16), - u_samples_b: samples.subarray(16, 32) - }); - } - - var temp = this._temp_texture; - var type = gl.UNSIGNED_BYTE; - if (tex.type != type) { - //force floats, half floats cannot be read with gl.readPixels - type = gl.FLOAT; - } - - if (!temp || temp.type != type) { - this._temp_texture = new GL.Texture(1, 1, { - type: type, - format: gl.RGBA, - filter: gl.NEAREST - }); - } - - this._uniforms.u_mipmap_offset = 0; - - if(this.properties.high_quality) - { - if( !this._temp_pot2_texture || this._temp_pot2_texture.type != type ) - this._temp_pot2_texture = new GL.Texture(512, 512, { - type: type, - format: gl.RGBA, - minFilter: gl.LINEAR_MIPMAP_LINEAR, - magFilter: gl.LINEAR - }); - - tex.copyTo( this._temp_pot2_texture ); - tex = this._temp_pot2_texture; - tex.bind(0); - gl.generateMipmap(GL.TEXTURE_2D); - this._uniforms.u_mipmap_offset = 9; - } - - var shader = LGraphTextureAverage._shader; - var uniforms = this._uniforms; - uniforms.u_mipmap_offset = this.properties.mipmap_offset; - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.BLEND); - this._temp_texture.drawTo(function() { - tex.toViewport(shader, uniforms); - }); - - if (this.isOutputConnected(1) || this.isOutputConnected(2)) { - var pixel = this._temp_texture.getPixels(); - if (pixel) { - var v = this._luminance; - var type = this._temp_texture.type; - v.set(pixel); - if (type == gl.UNSIGNED_BYTE) { - vec4.scale(v, v, 1 / 255); - } else if ( - type == GL.HALF_FLOAT || - type == GL.HALF_FLOAT_OES - ) { - //no half floats possible, hard to read back unless copyed to a FLOAT texture, so temp_texture is always forced to FLOAT - } - } - } - }; - - LGraphTextureAverage.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - uniform mat4 u_samples_a;\n\ - uniform mat4 u_samples_b;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_mipmap_offset;\n\ - varying vec2 v_coord;\n\ - \n\ - void main() {\n\ - vec4 color = vec4(0.0);\n\ - //random average\n\ - for(int i = 0; i < 4; ++i)\n\ - for(int j = 0; j < 4; ++j)\n\ - {\n\ - color += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\ - color += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\ - }\n\ - gl_FragColor = color * 0.03125;\n\ - }\n\ - "; - - LiteGraph.registerNodeType("texture/average", LGraphTextureAverage); - - - - // Computes operation between pixels (max, min) ***************************************** - function LGraphTextureMinMax() { - this.addInput("Texture", "Texture"); - this.addOutput("min_t", "Texture"); - this.addOutput("max_t", "Texture"); - this.addOutput("min", "vec4"); - this.addOutput("max", "vec4"); - this.properties = { - mode: "max", - use_previous_frame: true //to avoid stalls - }; - - this._uniforms = { - u_texture: 0 - }; - - this._max = new Float32Array(4); - this._min = new Float32Array(4); - - this._textures_chain = []; - } - - LGraphTextureMinMax.widgets_info = { - mode: { widget: "combo", values: ["min","max","avg"] } - }; - - LGraphTextureMinMax.title = "MinMax"; - LGraphTextureMinMax.desc = "Compute the scene min max"; - - LGraphTextureMinMax.prototype.onExecute = function() { - if (!this.properties.use_previous_frame) { - this.update(); - } - - this.setOutputData(0, this._temp_texture); - this.setOutputData(1, this._luminance); - }; - - //executed before rendering the frame - LGraphTextureMinMax.prototype.onPreRenderExecute = function() { - this.update(); - }; - - LGraphTextureMinMax.prototype.update = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if ( !this.isOutputConnected(0) && !this.isOutputConnected(1) ) { - return; - } //saves work - - if (!LGraphTextureMinMax._shader) { - LGraphTextureMinMax._shader = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureMinMax.pixel_shader ); - } - - var temp = this._temp_texture; - var type = gl.UNSIGNED_BYTE; - if (tex.type != type) { - //force floats, half floats cannot be read with gl.readPixels - type = gl.FLOAT; - } - - var size = 512; - - if( !this._textures_chain.length || this._textures_chain[0].type != type ) - { - var index = 0; - while(i) - { - this._textures_chain[i] = new GL.Texture( size, size, { - type: type, - format: gl.RGBA, - filter: gl.NEAREST - }); - size = size >> 2; - i++; - if(size == 1) - break; - } - } - - tex.copyTo( this._textures_chain[0] ); - var prev = this._textures_chain[0]; - for(var i = 1; i <= this._textures_chain.length; ++i) - { - var tex = this._textures_chain[i]; - - prev = tex; - } - - var shader = LGraphTextureMinMax._shader; - var uniforms = this._uniforms; - uniforms.u_mipmap_offset = this.properties.mipmap_offset; - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.BLEND); - this._temp_texture.drawTo(function() { - tex.toViewport(shader, uniforms); - }); - }; - - LGraphTextureMinMax.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - uniform mat4 u_samples_a;\n\ - uniform mat4 u_samples_b;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_mipmap_offset;\n\ - varying vec2 v_coord;\n\ - \n\ - void main() {\n\ - vec4 color = vec4(0.0);\n\ - //random average\n\ - for(int i = 0; i < 4; ++i)\n\ - for(int j = 0; j < 4; ++j)\n\ - {\n\ - color += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\ - color += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\ - }\n\ - gl_FragColor = color * 0.03125;\n\ - }\n\ - "; - - //LiteGraph.registerNodeType("texture/clustered_operation", LGraphTextureClusteredOperation); - - - function LGraphTextureTemporalSmooth() { - this.addInput("in", "Texture"); - this.addInput("factor", "Number"); - this.addOutput("out", "Texture"); - this.properties = { factor: 0.5 }; - this._uniforms = { - u_texture: 0, - u_textureB: 1, - u_factor: this.properties.factor - }; - } - - LGraphTextureTemporalSmooth.title = "Smooth"; - LGraphTextureTemporalSmooth.desc = "Smooth texture over time"; - - LGraphTextureTemporalSmooth.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex || !this.isOutputConnected(0)) { - return; - } - - if (!LGraphTextureTemporalSmooth._shader) { - LGraphTextureTemporalSmooth._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureTemporalSmooth.pixel_shader - ); - } - - var temp = this._temp_texture; - if ( - !temp || - temp.type != tex.type || - temp.width != tex.width || - temp.height != tex.height - ) { - var options = { - type: tex.type, - format: gl.RGBA, - filter: gl.NEAREST - }; - this._temp_texture = new GL.Texture(tex.width, tex.height, options ); - this._temp_texture2 = new GL.Texture(tex.width, tex.height, options ); - tex.copyTo(this._temp_texture2); - } - - var tempA = this._temp_texture; - var tempB = this._temp_texture2; - - var shader = LGraphTextureTemporalSmooth._shader; - var uniforms = this._uniforms; - uniforms.u_factor = 1.0 - this.getInputOrProperty("factor"); - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - tempA.drawTo(function() { - tempB.bind(1); - tex.toViewport(shader, uniforms); - }); - - this.setOutputData(0, tempA); - - //swap - this._temp_texture = tempB; - this._temp_texture2 = tempA; - }; - - LGraphTextureTemporalSmooth.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - uniform float u_factor;\n\ - varying vec2 v_coord;\n\ - \n\ - void main() {\n\ - gl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\ - }\n\ - "; - - LiteGraph.registerNodeType( "texture/temporal_smooth", LGraphTextureTemporalSmooth ); - - - function LGraphTextureLinearAvgSmooth() { - this.addInput("in", "Texture"); - this.addOutput("avg", "Texture"); - this.addOutput("array", "Texture"); - this.properties = { samples: 64, frames_interval: 1 }; - this._uniforms = { - u_texture: 0, - u_textureB: 1, - u_samples: this.properties.samples, - u_isamples: 1/this.properties.samples - }; - this.frame = 0; - } - - LGraphTextureLinearAvgSmooth.title = "Lineal Avg Smooth"; - LGraphTextureLinearAvgSmooth.desc = "Smooth texture linearly over time"; - - LGraphTextureLinearAvgSmooth["@samples"] = { type: "number", min: 1, max: 64, step: 1, precision: 1 }; - - LGraphTextureLinearAvgSmooth.prototype.getPreviewTexture = function() - { - return this._temp_texture2; - } - - LGraphTextureLinearAvgSmooth.prototype.onExecute = function() { - - var tex = this.getInputData(0); - if (!tex || !this.isOutputConnected(0)) { - return; - } - - if (!LGraphTextureLinearAvgSmooth._shader) { - LGraphTextureLinearAvgSmooth._shader_copy = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureLinearAvgSmooth.pixel_shader_copy ); - LGraphTextureLinearAvgSmooth._shader_avg = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureLinearAvgSmooth.pixel_shader_avg ); - } - - var samples = Math.clamp(this.properties.samples,0,64); - var frame = this.frame; - var interval = this.properties.frames_interval; - - if( interval == 0 || frame % interval == 0 ) - { - var temp = this._temp_texture; - if ( !temp || temp.type != tex.type || temp.width != samples ) { - var options = { - type: tex.type, - format: gl.RGBA, - filter: gl.NEAREST - }; - this._temp_texture = new GL.Texture( samples, 1, options ); - this._temp_texture2 = new GL.Texture( samples, 1, options ); - this._temp_texture_out = new GL.Texture( 1, 1, options ); - } - - var tempA = this._temp_texture; - var tempB = this._temp_texture2; - - var shader_copy = LGraphTextureLinearAvgSmooth._shader_copy; - var shader_avg = LGraphTextureLinearAvgSmooth._shader_avg; - var uniforms = this._uniforms; - uniforms.u_samples = samples; - uniforms.u_isamples = 1.0 / samples; - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - tempA.drawTo(function() { - tempB.bind(1); - tex.toViewport( shader_copy, uniforms ); - }); - - this._temp_texture_out.drawTo(function() { - tempA.toViewport( shader_avg, uniforms ); - }); - - this.setOutputData( 0, this._temp_texture_out ); - - //swap - this._temp_texture = tempB; - this._temp_texture2 = tempA; - } - else - this.setOutputData(0, this._temp_texture_out); - this.setOutputData(1, this._temp_texture2); - this.frame++; - }; - - LGraphTextureLinearAvgSmooth.pixel_shader_copy = - "precision highp float;\n\ - precision highp float;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - uniform float u_isamples;\n\ - varying vec2 v_coord;\n\ - \n\ - void main() {\n\ - if( v_coord.x <= u_isamples )\n\ - gl_FragColor = texture2D( u_texture, vec2(0.5) );\n\ - else\n\ - gl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\ - }\n\ - "; - - LGraphTextureLinearAvgSmooth.pixel_shader_avg = - "precision highp float;\n\ - precision highp float;\n\ - uniform sampler2D u_texture;\n\ - uniform int u_samples;\n\ - uniform float u_isamples;\n\ - varying vec2 v_coord;\n\ - \n\ - void main() {\n\ - vec4 color = vec4(0.0);\n\ - for(int i = 0; i < 64; ++i)\n\ - {\n\ - color += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\ - if(i == (u_samples - 1))\n\ - break;\n\ - }\n\ - gl_FragColor = color * u_isamples;\n\ - }\n\ - "; - - - LiteGraph.registerNodeType( "texture/linear_avg_smooth", LGraphTextureLinearAvgSmooth ); - - // Image To Texture ***************************************** - function LGraphImageToTexture() { - this.addInput("Image", "image"); - this.addOutput("", "Texture"); - this.properties = {}; - } - - LGraphImageToTexture.title = "Image to Texture"; - LGraphImageToTexture.desc = "Uploads an image to the GPU"; - //LGraphImageToTexture.widgets_info = { size: { widget:"combo", values:[0,32,64,128,256,512,1024,2048]} }; - - LGraphImageToTexture.prototype.onExecute = function() { - var img = this.getInputData(0); - if (!img) { - return; - } - - var width = img.videoWidth || img.width; - var height = img.videoHeight || img.height; - - //this is in case we are using a webgl canvas already, no need to reupload it - if (img.gltexture) { - this.setOutputData(0, img.gltexture); - return; - } - - var temp = this._temp_texture; - if (!temp || temp.width != width || temp.height != height) { - this._temp_texture = new GL.Texture(width, height, { - format: gl.RGBA, - filter: gl.LINEAR - }); - } - - try { - this._temp_texture.uploadImage(img); - } catch (err) { - console.error( - "image comes from an unsafe location, cannot be uploaded to webgl: " + - err - ); - return; - } - - this.setOutputData(0, this._temp_texture); - }; - - LiteGraph.registerNodeType( - "texture/imageToTexture", - LGraphImageToTexture - ); - - // Texture LUT ***************************************** - function LGraphTextureLUT() { - this.addInput("Texture", "Texture"); - this.addInput("LUT", "Texture"); - this.addInput("Intensity", "number"); - this.addOutput("", "Texture"); - this.properties = { enabled: true, intensity: 1, precision: LGraphTexture.DEFAULT, texture: null }; - - if (!LGraphTextureLUT._shader) { - LGraphTextureLUT._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, LGraphTextureLUT.pixel_shader ); - } - } - - LGraphTextureLUT.widgets_info = { - texture: { widget: "texture" }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureLUT.title = "LUT"; - LGraphTextureLUT.desc = "Apply LUT to Texture"; - - LGraphTextureLUT.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var tex = this.getInputData(0); - - if (this.properties.precision === LGraphTexture.PASS_THROUGH || this.properties.enabled === false) { - this.setOutputData(0, tex); - return; - } - - if (!tex) { - return; - } - - var lut_tex = this.getInputData(1); - - if (!lut_tex) { - lut_tex = LGraphTexture.getTexture(this.properties.texture); - } - - if (!lut_tex) { - this.setOutputData(0, tex); - return; - } - - lut_tex.bind(0); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - gl.texParameteri( - gl.TEXTURE_2D, - gl.TEXTURE_WRAP_S, - gl.CLAMP_TO_EDGE - ); - gl.texParameteri( - gl.TEXTURE_2D, - gl.TEXTURE_WRAP_T, - gl.CLAMP_TO_EDGE - ); - gl.bindTexture(gl.TEXTURE_2D, null); - - var intensity = this.properties.intensity; - if (this.isInputConnected(2)) { - this.properties.intensity = intensity = this.getInputData(2); - } - - this._tex = LGraphTexture.getTargetTexture( - tex, - this._tex, - this.properties.precision - ); - - //var mesh = Mesh.getScreenQuad(); - - this._tex.drawTo(function() { - lut_tex.bind(1); - tex.toViewport(LGraphTextureLUT._shader, { - u_texture: 0, - u_textureB: 1, - u_amount: intensity - }); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureLUT.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - uniform float u_amount;\n\ - \n\ - void main() {\n\ - lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\ - mediump float blueColor = textureColor.b * 63.0;\n\ - mediump vec2 quad1;\n\ - quad1.y = floor(floor(blueColor) / 8.0);\n\ - quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\ - mediump vec2 quad2;\n\ - quad2.y = floor(ceil(blueColor) / 8.0);\n\ - quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\ - highp vec2 texPos1;\n\ - texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\ - texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\ - highp vec2 texPos2;\n\ - texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\ - texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\ - lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\ - lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\ - lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\ - gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\ - }\n\ - "; - - LiteGraph.registerNodeType("texture/LUT", LGraphTextureLUT); - - // Texture Channels ***************************************** - function LGraphTextureChannels() { - this.addInput("Texture", "Texture"); - - this.addOutput("R", "Texture"); - this.addOutput("G", "Texture"); - this.addOutput("B", "Texture"); - this.addOutput("A", "Texture"); - - //this.properties = { use_single_channel: true }; - if (!LGraphTextureChannels._shader) { - LGraphTextureChannels._shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureChannels.pixel_shader - ); - } - } - - LGraphTextureChannels.title = "Texture to Channels"; - LGraphTextureChannels.desc = "Split texture channels"; - - LGraphTextureChannels.prototype.onExecute = function() { - var texA = this.getInputData(0); - if (!texA) { - return; - } - - if (!this._channels) { - this._channels = Array(4); - } - - //var format = this.properties.use_single_channel ? gl.LUMINANCE : gl.RGBA; //not supported by WebGL1 - var format = gl.RGB; - var connections = 0; - for (var i = 0; i < 4; i++) { - if (this.isOutputConnected(i)) { - if ( - !this._channels[i] || - this._channels[i].width != texA.width || - this._channels[i].height != texA.height || - this._channels[i].type != texA.type || - this._channels[i].format != format - ) { - this._channels[i] = new GL.Texture( - texA.width, - texA.height, - { - type: texA.type, - format: format, - filter: gl.LINEAR - } - ); - } - connections++; - } else { - this._channels[i] = null; - } - } - - if (!connections) { - return; - } - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - - var mesh = Mesh.getScreenQuad(); - var shader = LGraphTextureChannels._shader; - var masks = [ - [1, 0, 0, 0], - [0, 1, 0, 0], - [0, 0, 1, 0], - [0, 0, 0, 1] - ]; - - for (var i = 0; i < 4; i++) { - if (!this._channels[i]) { - continue; - } - - this._channels[i].drawTo(function() { - texA.bind(0); - shader - .uniforms({ u_texture: 0, u_mask: masks[i] }) - .draw(mesh); - }); - this.setOutputData(i, this._channels[i]); - } - }; - - LGraphTextureChannels.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec4 u_mask;\n\ - \n\ - void main() {\n\ - gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\ - }\n\ - "; - - LiteGraph.registerNodeType( - "texture/textureChannels", - LGraphTextureChannels - ); - - // Texture Channels to Texture ***************************************** - function LGraphChannelsTexture() { - this.addInput("R", "Texture"); - this.addInput("G", "Texture"); - this.addInput("B", "Texture"); - this.addInput("A", "Texture"); - - this.addOutput("Texture", "Texture"); - - this.properties = { - precision: LGraphTexture.DEFAULT, - R: 1, - G: 1, - B: 1, - A: 1 - }; - this._color = vec4.create(); - this._uniforms = { - u_textureR: 0, - u_textureG: 1, - u_textureB: 2, - u_textureA: 3, - u_color: this._color - }; - } - - LGraphChannelsTexture.title = "Channels to Texture"; - LGraphChannelsTexture.desc = "Split texture channels"; - LGraphChannelsTexture.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphChannelsTexture.prototype.onExecute = function() { - var white = LGraphTexture.getWhiteTexture(); - var texR = this.getInputData(0) || white; - var texG = this.getInputData(1) || white; - var texB = this.getInputData(2) || white; - var texA = this.getInputData(3) || white; - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - - var mesh = Mesh.getScreenQuad(); - if (!LGraphChannelsTexture._shader) { - LGraphChannelsTexture._shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphChannelsTexture.pixel_shader - ); - } - var shader = LGraphChannelsTexture._shader; - - var w = Math.max(texR.width, texG.width, texB.width, texA.width); - var h = Math.max( - texR.height, - texG.height, - texB.height, - texA.height - ); - var type = - this.properties.precision == LGraphTexture.HIGH - ? LGraphTexture.HIGH_PRECISION_FORMAT - : gl.UNSIGNED_BYTE; - - if ( - !this._texture || - this._texture.width != w || - this._texture.height != h || - this._texture.type != type - ) { - this._texture = new GL.Texture(w, h, { - type: type, - format: gl.RGBA, - filter: gl.LINEAR - }); - } - - var color = this._color; - color[0] = this.properties.R; - color[1] = this.properties.G; - color[2] = this.properties.B; - color[3] = this.properties.A; - var uniforms = this._uniforms; - - this._texture.drawTo(function() { - texR.bind(0); - texG.bind(1); - texB.bind(2); - texA.bind(3); - shader.uniforms(uniforms).draw(mesh); - }); - this.setOutputData(0, this._texture); - }; - - LGraphChannelsTexture.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_textureR;\n\ - uniform sampler2D u_textureG;\n\ - uniform sampler2D u_textureB;\n\ - uniform sampler2D u_textureA;\n\ - uniform vec4 u_color;\n\ - \n\ - void main() {\n\ - gl_FragColor = u_color * vec4( \ - texture2D(u_textureR, v_coord).r,\ - texture2D(u_textureG, v_coord).r,\ - texture2D(u_textureB, v_coord).r,\ - texture2D(u_textureA, v_coord).r);\n\ - }\n\ - "; - - LiteGraph.registerNodeType( - "texture/channelsTexture", - LGraphChannelsTexture - ); - - // Texture Color ***************************************** - function LGraphTextureColor() { - this.addOutput("Texture", "Texture"); - - this._tex_color = vec4.create(); - this.properties = { - color: vec4.create(), - precision: LGraphTexture.DEFAULT - }; - } - - LGraphTextureColor.title = "Color"; - LGraphTextureColor.desc = - "Generates a 1x1 texture with a constant color"; - - LGraphTextureColor.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureColor.prototype.onDrawBackground = function(ctx) { - var c = this.properties.color; - ctx.fillStyle = - "rgb(" + - Math.floor(Math.clamp(c[0], 0, 1) * 255) + - "," + - Math.floor(Math.clamp(c[1], 0, 1) * 255) + - "," + - Math.floor(Math.clamp(c[2], 0, 1) * 255) + - ")"; - if (this.flags.collapsed) { - this.boxcolor = ctx.fillStyle; - } else { - ctx.fillRect(0, 0, this.size[0], this.size[1]); - } - }; - - LGraphTextureColor.prototype.onExecute = function() { - var type = - this.properties.precision == LGraphTexture.HIGH - ? LGraphTexture.HIGH_PRECISION_FORMAT - : gl.UNSIGNED_BYTE; - - if (!this._tex || this._tex.type != type) { - this._tex = new GL.Texture(1, 1, { - format: gl.RGBA, - type: type, - minFilter: gl.NEAREST - }); - } - var color = this.properties.color; - - if (this.inputs) { - for (var i = 0; i < this.inputs.length; i++) { - var input = this.inputs[i]; - var v = this.getInputData(i); - if (v === undefined) { - continue; - } - switch (input.name) { - case "RGB": - case "RGBA": - color.set(v); - break; - case "R": - color[0] = v; - break; - case "G": - color[1] = v; - break; - case "B": - color[2] = v; - break; - case "A": - color[3] = v; - break; - } - } - } - - if (vec4.sqrDist(this._tex_color, color) > 0.001) { - this._tex_color.set(color); - this._tex.fill(color); - } - this.setOutputData(0, this._tex); - }; - - LGraphTextureColor.prototype.onGetInputs = function() { - return [ - ["RGB", "vec3"], - ["RGBA", "vec4"], - ["R", "number"], - ["G", "number"], - ["B", "number"], - ["A", "number"] - ]; - }; - - LiteGraph.registerNodeType("texture/color", LGraphTextureColor); - - // Texture Channels to Texture ***************************************** - function LGraphTextureGradient() { - this.addInput("A", "color"); - this.addInput("B", "color"); - this.addOutput("Texture", "Texture"); - - this.properties = { - angle: 0, - scale: 1, - A: [0, 0, 0], - B: [1, 1, 1], - texture_size: 32 - }; - if (!LGraphTextureGradient._shader) { - LGraphTextureGradient._shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureGradient.pixel_shader - ); - } - - this._uniforms = { - u_angle: 0, - u_colorA: vec3.create(), - u_colorB: vec3.create() - }; - } - - LGraphTextureGradient.title = "Gradient"; - LGraphTextureGradient.desc = "Generates a gradient"; - LGraphTextureGradient["@A"] = { type: "color" }; - LGraphTextureGradient["@B"] = { type: "color" }; - LGraphTextureGradient["@texture_size"] = { - type: "enum", - values: [32, 64, 128, 256, 512] - }; - - LGraphTextureGradient.prototype.onExecute = function() { - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - - var mesh = GL.Mesh.getScreenQuad(); - var shader = LGraphTextureGradient._shader; - - var A = this.getInputData(0); - if (!A) { - A = this.properties.A; - } - var B = this.getInputData(1); - if (!B) { - B = this.properties.B; - } - - //angle and scale - for (var i = 2; i < this.inputs.length; i++) { - var input = this.inputs[i]; - var v = this.getInputData(i); - if (v === undefined) { - continue; - } - this.properties[input.name] = v; - } - - var uniforms = this._uniforms; - this._uniforms.u_angle = this.properties.angle * DEG2RAD; - this._uniforms.u_scale = this.properties.scale; - vec3.copy(uniforms.u_colorA, A); - vec3.copy(uniforms.u_colorB, B); - - var size = parseInt(this.properties.texture_size); - if (!this._tex || this._tex.width != size) { - this._tex = new GL.Texture(size, size, { - format: gl.RGB, - filter: gl.LINEAR - }); - } - - this._tex.drawTo(function() { - shader.uniforms(uniforms).draw(mesh); - }); - this.setOutputData(0, this._tex); - }; - - LGraphTextureGradient.prototype.onGetInputs = function() { - return [["angle", "number"], ["scale", "number"]]; - }; - - LGraphTextureGradient.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform float u_angle;\n\ - uniform float u_scale;\n\ - uniform vec3 u_colorA;\n\ - uniform vec3 u_colorB;\n\ - \n\ - vec2 rotate(vec2 v, float angle)\n\ - {\n\ - vec2 result;\n\ - float _cos = cos(angle);\n\ - float _sin = sin(angle);\n\ - result.x = v.x * _cos - v.y * _sin;\n\ - result.y = v.x * _sin + v.y * _cos;\n\ - return result;\n\ - }\n\ - void main() {\n\ - float f = (rotate(u_scale * (v_coord - vec2(0.5)), u_angle) + vec2(0.5)).x;\n\ - vec3 color = mix(u_colorA,u_colorB,clamp(f,0.0,1.0));\n\ - gl_FragColor = vec4(color,1.0);\n\ - }\n\ - "; - - LiteGraph.registerNodeType("texture/gradient", LGraphTextureGradient); - - // Texture Mix ***************************************** - function LGraphTextureMix() { - this.addInput("A", "Texture"); - this.addInput("B", "Texture"); - this.addInput("Mixer", "Texture"); - - this.addOutput("Texture", "Texture"); - this.properties = { factor: 0.5, size_from_biggest: true, invert: false, precision: LGraphTexture.DEFAULT }; - this._uniforms = { - u_textureA: 0, - u_textureB: 1, - u_textureMix: 2, - u_mix: vec4.create() - }; - } - - LGraphTextureMix.title = "Mix"; - LGraphTextureMix.desc = "Generates a texture mixing two textures"; - - LGraphTextureMix.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureMix.prototype.onExecute = function() { - var texA = this.getInputData(0); - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, texA); - return; - } - - var texB = this.getInputData(1); - if (!texA || !texB) { - return; - } - - var texMix = this.getInputData(2); - - var factor = this.getInputData(3); - - this._tex = LGraphTexture.getTargetTexture( - this.properties.size_from_biggest && texB.width > texA.width ? texB : texA, - this._tex, - this.properties.precision - ); - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - - var mesh = Mesh.getScreenQuad(); - var shader = null; - var uniforms = this._uniforms; - if (texMix) { - shader = LGraphTextureMix._shader_tex; - if (!shader) { - shader = LGraphTextureMix._shader_tex = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureMix.pixel_shader, - { MIX_TEX: "" } - ); - } - } else { - shader = LGraphTextureMix._shader_factor; - if (!shader) { - shader = LGraphTextureMix._shader_factor = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureMix.pixel_shader - ); - } - var f = factor == null ? this.properties.factor : factor; - uniforms.u_mix.set([f, f, f, f]); - } - - var invert = this.properties.invert; - - this._tex.drawTo(function() { - texA.bind( invert ? 1 : 0 ); - texB.bind( invert ? 0 : 1 ); - if (texMix) { - texMix.bind(2); - } - shader.uniforms(uniforms).draw(mesh); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureMix.prototype.onGetInputs = function() { - return [["factor", "number"]]; - }; - - LGraphTextureMix.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_textureA;\n\ - uniform sampler2D u_textureB;\n\ - #ifdef MIX_TEX\n\ - uniform sampler2D u_textureMix;\n\ - #else\n\ - uniform vec4 u_mix;\n\ - #endif\n\ - \n\ - void main() {\n\ - #ifdef MIX_TEX\n\ - vec4 f = texture2D(u_textureMix, v_coord);\n\ - #else\n\ - vec4 f = u_mix;\n\ - #endif\n\ - gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\ - }\n\ - "; - - LiteGraph.registerNodeType("texture/mix", LGraphTextureMix); - - // Texture Edges detection ***************************************** - function LGraphTextureEdges() { - this.addInput("Tex.", "Texture"); - - this.addOutput("Edges", "Texture"); - this.properties = { - invert: true, - threshold: false, - factor: 1, - precision: LGraphTexture.DEFAULT - }; - - if (!LGraphTextureEdges._shader) { - LGraphTextureEdges._shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureEdges.pixel_shader - ); - } - } - - LGraphTextureEdges.title = "Edges"; - LGraphTextureEdges.desc = "Detects edges"; - - LGraphTextureEdges.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureEdges.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var tex = this.getInputData(0); - - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, tex); - return; - } - - if (!tex) { - return; - } - - this._tex = LGraphTexture.getTargetTexture( - tex, - this._tex, - this.properties.precision - ); - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - - var mesh = Mesh.getScreenQuad(); - var shader = LGraphTextureEdges._shader; - var invert = this.properties.invert; - var factor = this.properties.factor; - var threshold = this.properties.threshold ? 1 : 0; - - this._tex.drawTo(function() { - tex.bind(0); - shader - .uniforms({ - u_texture: 0, - u_isize: [1 / tex.width, 1 / tex.height], - u_factor: factor, - u_threshold: threshold, - u_invert: invert ? 1 : 0 - }) - .draw(mesh); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureEdges.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_isize;\n\ - uniform int u_invert;\n\ - uniform float u_factor;\n\ - uniform float u_threshold;\n\ - \n\ - void main() {\n\ - vec4 center = texture2D(u_texture, v_coord);\n\ - vec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\ - vec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\ - vec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\ - vec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\ - vec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\ - diff *= u_factor;\n\ - if(u_invert == 1)\n\ - diff.xyz = vec3(1.0) - diff.xyz;\n\ - if( u_threshold == 0.0 )\n\ - gl_FragColor = vec4( diff.xyz, center.a );\n\ - else\n\ - gl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\ - }\n\ - "; - - LiteGraph.registerNodeType("texture/edges", LGraphTextureEdges); - - // Texture Depth ***************************************** - function LGraphTextureDepthRange() { - this.addInput("Texture", "Texture"); - this.addInput("Distance", "number"); - this.addInput("Range", "number"); - this.addOutput("Texture", "Texture"); - this.properties = { - distance: 100, - range: 50, - only_depth: false, - high_precision: false - }; - this._uniforms = { - u_texture: 0, - u_distance: 100, - u_range: 50, - u_camera_planes: null - }; - } - - LGraphTextureDepthRange.title = "Depth Range"; - LGraphTextureDepthRange.desc = "Generates a texture with a depth range"; - - LGraphTextureDepthRange.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var tex = this.getInputData(0); - if (!tex) { - return; - } - - var precision = gl.UNSIGNED_BYTE; - if (this.properties.high_precision) { - precision = gl.half_float_ext ? gl.HALF_FLOAT_OES : gl.FLOAT; - } - - if ( - !this._temp_texture || - this._temp_texture.type != precision || - this._temp_texture.width != tex.width || - this._temp_texture.height != tex.height - ) { - this._temp_texture = new GL.Texture(tex.width, tex.height, { - type: precision, - format: gl.RGBA, - filter: gl.LINEAR - }); - } - - var uniforms = this._uniforms; - - //iterations - var distance = this.properties.distance; - if (this.isInputConnected(1)) { - distance = this.getInputData(1); - this.properties.distance = distance; - } - - var range = this.properties.range; - if (this.isInputConnected(2)) { - range = this.getInputData(2); - this.properties.range = range; - } - - uniforms.u_distance = distance; - uniforms.u_range = range; - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - var mesh = Mesh.getScreenQuad(); - if (!LGraphTextureDepthRange._shader) { - LGraphTextureDepthRange._shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureDepthRange.pixel_shader - ); - LGraphTextureDepthRange._shader_onlydepth = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureDepthRange.pixel_shader, - { ONLY_DEPTH: "" } - ); - } - var shader = this.properties.only_depth - ? LGraphTextureDepthRange._shader_onlydepth - : LGraphTextureDepthRange._shader; - - //NEAR AND FAR PLANES - var planes = null; - if (tex.near_far_planes) { - planes = tex.near_far_planes; - } else if (window.LS && LS.Renderer._main_camera) { - planes = LS.Renderer._main_camera._uniforms.u_camera_planes; - } else { - planes = [0.1, 1000]; - } //hardcoded - uniforms.u_camera_planes = planes; - - this._temp_texture.drawTo(function() { - tex.bind(0); - shader.uniforms(uniforms).draw(mesh); - }); - - this._temp_texture.near_far_planes = planes; - this.setOutputData(0, this._temp_texture); - }; - - LGraphTextureDepthRange.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_camera_planes;\n\ - uniform float u_distance;\n\ - uniform float u_range;\n\ - \n\ - float LinearDepth()\n\ - {\n\ - float zNear = u_camera_planes.x;\n\ - float zFar = u_camera_planes.y;\n\ - float depth = texture2D(u_texture, v_coord).x;\n\ - depth = depth * 2.0 - 1.0;\n\ - return zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\ - }\n\ - \n\ - void main() {\n\ - float depth = LinearDepth();\n\ - #ifdef ONLY_DEPTH\n\ - gl_FragColor = vec4(depth);\n\ - #else\n\ - float diff = abs(depth * u_camera_planes.y - u_distance);\n\ - float dof = 1.0;\n\ - if(diff <= u_range)\n\ - dof = diff / u_range;\n\ - gl_FragColor = vec4(dof);\n\ - #endif\n\ - }\n\ - "; - - LiteGraph.registerNodeType( "texture/depth_range", LGraphTextureDepthRange ); - - - // Texture Depth ***************************************** - function LGraphTextureLinearDepth() { - this.addInput("Texture", "Texture"); - this.addOutput("Texture", "Texture"); - this.properties = { - precision: LGraphTexture.DEFAULT, - invert: false - }; - this._uniforms = { - u_texture: 0, - u_camera_planes: null, //filled later - u_ires: vec2.create() - }; - } - - LGraphTextureLinearDepth.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureLinearDepth.title = "Linear Depth"; - LGraphTextureLinearDepth.desc = "Creates a color texture with linear depth"; - - LGraphTextureLinearDepth.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var tex = this.getInputData(0); - if (!tex || (tex.format != gl.DEPTH_COMPONENT && tex.format != gl.DEPTH_STENCIL) ) { - return; - } - - var precision = this.properties.precision == LGraphTexture.HIGH ? gl.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE; - - if ( !this._temp_texture || this._temp_texture.type != precision || this._temp_texture.width != tex.width || this._temp_texture.height != tex.height ) { - this._temp_texture = new GL.Texture(tex.width, tex.height, { - type: precision, - format: gl.RGB, - filter: gl.LINEAR - }); - } - - var uniforms = this._uniforms; - uniforms.u_invert = this.properties.invert ? 1 : 0; - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - var mesh = Mesh.getScreenQuad(); - if(!LGraphTextureLinearDepth._shader) - LGraphTextureLinearDepth._shader = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureLinearDepth.pixel_shader); - var shader = LGraphTextureLinearDepth._shader; - - //NEAR AND FAR PLANES - var planes = null; - if (tex.near_far_planes) { - planes = tex.near_far_planes; - } else if (window.LS && LS.Renderer._main_camera) { - planes = LS.Renderer._main_camera._uniforms.u_camera_planes; - } else { - planes = [0.1, 1000]; - } //hardcoded - uniforms.u_camera_planes = planes; - //uniforms.u_ires.set([1/tex.width, 1/tex.height]); - uniforms.u_ires.set([0,0]); - - this._temp_texture.drawTo(function() { - tex.bind(0); - shader.uniforms(uniforms).draw(mesh); - }); - - this._temp_texture.near_far_planes = planes; - this.setOutputData(0, this._temp_texture); - }; - - LGraphTextureLinearDepth.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_camera_planes;\n\ - uniform int u_invert;\n\ - uniform vec2 u_ires;\n\ - \n\ - void main() {\n\ - float zNear = u_camera_planes.x;\n\ - float zFar = u_camera_planes.y;\n\ - float depth = texture2D(u_texture, v_coord + u_ires*0.5).x * 2.0 - 1.0;\n\ - float f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\ - if( u_invert == 1 )\n\ - f = 1.0 - f;\n\ - gl_FragColor = vec4(vec3(f),1.0);\n\ - }\n\ - "; - - LiteGraph.registerNodeType( "texture/linear_depth", LGraphTextureLinearDepth ); - - // Texture Blur ***************************************** - function LGraphTextureBlur() { - this.addInput("Texture", "Texture"); - this.addInput("Iterations", "number"); - this.addInput("Intensity", "number"); - this.addOutput("Blurred", "Texture"); - this.properties = { - intensity: 1, - iterations: 1, - preserve_aspect: false, - scale: [1, 1], - precision: LGraphTexture.DEFAULT - }; - } - - LGraphTextureBlur.title = "Blur"; - LGraphTextureBlur.desc = "Blur a texture"; - - LGraphTextureBlur.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureBlur.max_iterations = 20; - - LGraphTextureBlur.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var temp = this._final_texture; - - if ( - !temp || - temp.width != tex.width || - temp.height != tex.height || - temp.type != tex.type - ) { - //we need two textures to do the blurring - //this._temp_texture = new GL.Texture( tex.width, tex.height, { type: tex.type, format: gl.RGBA, filter: gl.LINEAR }); - temp = this._final_texture = new GL.Texture( - tex.width, - tex.height, - { type: tex.type, format: gl.RGBA, filter: gl.LINEAR } - ); - } - - //iterations - var iterations = this.properties.iterations; - if (this.isInputConnected(1)) { - iterations = this.getInputData(1); - this.properties.iterations = iterations; - } - iterations = Math.min( - Math.floor(iterations), - LGraphTextureBlur.max_iterations - ); - if (iterations == 0) { - //skip blurring - this.setOutputData(0, tex); - return; - } - - var intensity = this.properties.intensity; - if (this.isInputConnected(2)) { - intensity = this.getInputData(2); - this.properties.intensity = intensity; - } - - //blur sometimes needs an aspect correction - var aspect = LiteGraph.camera_aspect; - if (!aspect && window.gl !== undefined) { - aspect = gl.canvas.height / gl.canvas.width; - } - if (!aspect) { - aspect = 1; - } - aspect = this.properties.preserve_aspect ? aspect : 1; - - var scale = this.properties.scale || [1, 1]; - tex.applyBlur(aspect * scale[0], scale[1], intensity, temp); - for (var i = 1; i < iterations; ++i) { - temp.applyBlur( - aspect * scale[0] * (i + 1), - scale[1] * (i + 1), - intensity - ); - } - - this.setOutputData(0, temp); - }; - - /* -LGraphTextureBlur.pixel_shader = "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_offset;\n\ - uniform float u_intensity;\n\ - void main() {\n\ - vec4 sum = vec4(0.0);\n\ - vec4 center = texture2D(u_texture, v_coord);\n\ - sum += texture2D(u_texture, v_coord + u_offset * -4.0) * 0.05/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * -3.0) * 0.09/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * -2.0) * 0.12/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * -1.0) * 0.15/0.98;\n\ - sum += center * 0.16/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * 4.0) * 0.05/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * 3.0) * 0.09/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * 2.0) * 0.12/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * 1.0) * 0.15/0.98;\n\ - gl_FragColor = u_intensity * sum;\n\ - }\n\ - "; -*/ - - LiteGraph.registerNodeType("texture/blur", LGraphTextureBlur); - - // Texture Glow ***************************************** - //based in https://catlikecoding.com/unity/tutorials/advanced-rendering/bloom/ - function LGraphTextureGlow() { - this.addInput("in", "Texture"); - this.addInput("dirt", "Texture"); - this.addOutput("out", "Texture"); - this.addOutput("glow", "Texture"); - this.properties = { - enabled: true, - intensity: 1, - persistence: 0.99, - iterations: 16, - threshold: 0, - scale: 1, - dirt_factor: 0.5, - precision: LGraphTexture.DEFAULT - }; - this._textures = []; - this._uniforms = { - u_intensity: 1, - u_texture: 0, - u_glow_texture: 1, - u_threshold: 0, - u_texel_size: vec2.create() - }; - } - - LGraphTextureGlow.title = "Glow"; - LGraphTextureGlow.desc = "Filters a texture giving it a glow effect"; - LGraphTextureGlow.weights = new Float32Array([0.5, 0.4, 0.3, 0.2]); - - LGraphTextureGlow.widgets_info = { - iterations: { - type: "number", - min: 0, - max: 16, - step: 1, - precision: 0 - }, - threshold: { - type: "number", - min: 0, - max: 10, - step: 0.01, - precision: 2 - }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureGlow.prototype.onGetInputs = function() { - return [ - ["enabled", "boolean"], - ["threshold", "number"], - ["intensity", "number"], - ["persistence", "number"], - ["iterations", "number"], - ["dirt_factor", "number"] - ]; - }; - - LGraphTextureGlow.prototype.onGetOutputs = function() { - return [["average", "Texture"]]; - }; - - LGraphTextureGlow.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if (!this.isAnyOutputConnected()) { - return; - } //saves work - - if ( - this.properties.precision === LGraphTexture.PASS_THROUGH || - this.getInputOrProperty("enabled") === false - ) { - this.setOutputData(0, tex); - return; - } - - var width = tex.width; - var height = tex.height; - - var texture_info = { - format: tex.format, - type: tex.type, - minFilter: GL.LINEAR, - magFilter: GL.LINEAR, - wrap: gl.CLAMP_TO_EDGE - }; - var type = LGraphTexture.getTextureType( - this.properties.precision, - tex - ); - - var uniforms = this._uniforms; - var textures = this._textures; - - //cut - var shader = LGraphTextureGlow._cut_shader; - if (!shader) { - shader = LGraphTextureGlow._cut_shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureGlow.cut_pixel_shader - ); - } - - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.BLEND); - - uniforms.u_threshold = this.getInputOrProperty("threshold"); - var currentDestination = (textures[0] = GL.Texture.getTemporary( - width, - height, - texture_info - )); - tex.blit(currentDestination, shader.uniforms(uniforms)); - var currentSource = currentDestination; - - var iterations = this.getInputOrProperty("iterations"); - iterations = Math.clamp(iterations, 1, 16) | 0; - var texel_size = uniforms.u_texel_size; - var intensity = this.getInputOrProperty("intensity"); - - uniforms.u_intensity = 1; - uniforms.u_delta = this.properties.scale; //1 - - //downscale/upscale shader - var shader = LGraphTextureGlow._shader; - if (!shader) { - shader = LGraphTextureGlow._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureGlow.scale_pixel_shader - ); - } - - var i = 1; - //downscale - for (; i < iterations; i++) { - width = width >> 1; - if ((height | 0) > 1) { - height = height >> 1; - } - if (width < 2) { - break; - } - currentDestination = textures[i] = GL.Texture.getTemporary( - width, - height, - texture_info - ); - texel_size[0] = 1 / currentSource.width; - texel_size[1] = 1 / currentSource.height; - currentSource.blit( - currentDestination, - shader.uniforms(uniforms) - ); - currentSource = currentDestination; - } - - //average - if (this.isOutputConnected(2)) { - var average_texture = this._average_texture; - if ( - !average_texture || - average_texture.type != tex.type || - average_texture.format != tex.format - ) { - average_texture = this._average_texture = new GL.Texture( - 1, - 1, - { - type: tex.type, - format: tex.format, - filter: gl.LINEAR - } - ); - } - texel_size[0] = 1 / currentSource.width; - texel_size[1] = 1 / currentSource.height; - uniforms.u_intensity = intensity; - uniforms.u_delta = 1; - currentSource.blit(average_texture, shader.uniforms(uniforms)); - this.setOutputData(2, average_texture); - } - - //upscale and blend - gl.enable(gl.BLEND); - gl.blendFunc(gl.ONE, gl.ONE); - uniforms.u_intensity = this.getInputOrProperty("persistence"); - uniforms.u_delta = 0.5; - - for ( - i -= 2; - i >= 0; - i-- // i-=2 => -1 to point to last element in array, -1 to go to texture above - ) { - currentDestination = textures[i]; - textures[i] = null; - texel_size[0] = 1 / currentSource.width; - texel_size[1] = 1 / currentSource.height; - currentSource.blit( - currentDestination, - shader.uniforms(uniforms) - ); - GL.Texture.releaseTemporary(currentSource); - currentSource = currentDestination; - } - gl.disable(gl.BLEND); - - //glow - if (this.isOutputConnected(1)) { - var glow_texture = this._glow_texture; - if ( - !glow_texture || - glow_texture.width != tex.width || - glow_texture.height != tex.height || - glow_texture.type != type || - glow_texture.format != tex.format - ) { - glow_texture = this._glow_texture = new GL.Texture( - tex.width, - tex.height, - { type: type, format: tex.format, filter: gl.LINEAR } - ); - } - currentSource.blit(glow_texture); - this.setOutputData(1, glow_texture); - } - - //final composition - if (this.isOutputConnected(0)) { - var final_texture = this._final_texture; - if ( - !final_texture || - final_texture.width != tex.width || - final_texture.height != tex.height || - final_texture.type != type || - final_texture.format != tex.format - ) { - final_texture = this._final_texture = new GL.Texture( - tex.width, - tex.height, - { type: type, format: tex.format, filter: gl.LINEAR } - ); - } - - var dirt_texture = this.getInputData(1); - var dirt_factor = this.getInputOrProperty("dirt_factor"); - - uniforms.u_intensity = intensity; - - shader = dirt_texture - ? LGraphTextureGlow._dirt_final_shader - : LGraphTextureGlow._final_shader; - if (!shader) { - if (dirt_texture) { - shader = LGraphTextureGlow._dirt_final_shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureGlow.final_pixel_shader, - { USE_DIRT: "" } - ); - } else { - shader = LGraphTextureGlow._final_shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureGlow.final_pixel_shader - ); - } - } - - final_texture.drawTo(function() { - tex.bind(0); - currentSource.bind(1); - if (dirt_texture) { - shader.setUniform("u_dirt_factor", dirt_factor); - shader.setUniform( - "u_dirt_texture", - dirt_texture.bind(2) - ); - } - shader.toViewport(uniforms); - }); - this.setOutputData(0, final_texture); - } - - GL.Texture.releaseTemporary(currentSource); - }; - - LGraphTextureGlow.cut_pixel_shader = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_threshold;\n\ - void main() {\n\ - gl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\ - }"; - - LGraphTextureGlow.scale_pixel_shader = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_texel_size;\n\ - uniform float u_delta;\n\ - uniform float u_intensity;\n\ - \n\ - vec4 sampleBox(vec2 uv) {\n\ - vec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\ - vec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\ - return s * 0.25;\n\ - }\n\ - void main() {\n\ - gl_FragColor = u_intensity * sampleBox( v_coord );\n\ - }"; - - LGraphTextureGlow.final_pixel_shader = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_glow_texture;\n\ - #ifdef USE_DIRT\n\ - uniform sampler2D u_dirt_texture;\n\ - #endif\n\ - uniform vec2 u_texel_size;\n\ - uniform float u_delta;\n\ - uniform float u_intensity;\n\ - uniform float u_dirt_factor;\n\ - \n\ - vec4 sampleBox(vec2 uv) {\n\ - vec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\ - vec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\ - return s * 0.25;\n\ - }\n\ - void main() {\n\ - vec4 glow = sampleBox( v_coord );\n\ - #ifdef USE_DIRT\n\ - glow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\ - #endif\n\ - gl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\ - }"; - - LiteGraph.registerNodeType("texture/glow", LGraphTextureGlow); - - // Texture Filter ***************************************** - function LGraphTextureKuwaharaFilter() { - this.addInput("Texture", "Texture"); - this.addOutput("Filtered", "Texture"); - this.properties = { intensity: 1, radius: 5 }; - } - - LGraphTextureKuwaharaFilter.title = "Kuwahara Filter"; - LGraphTextureKuwaharaFilter.desc = - "Filters a texture giving an artistic oil canvas painting"; - - LGraphTextureKuwaharaFilter.max_radius = 10; - LGraphTextureKuwaharaFilter._shaders = []; - - LGraphTextureKuwaharaFilter.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var temp = this._temp_texture; - - if ( - !temp || - temp.width != tex.width || - temp.height != tex.height || - temp.type != tex.type - ) { - this._temp_texture = new GL.Texture(tex.width, tex.height, { - type: tex.type, - format: gl.RGBA, - filter: gl.LINEAR - }); - } - - //iterations - var radius = this.properties.radius; - radius = Math.min( - Math.floor(radius), - LGraphTextureKuwaharaFilter.max_radius - ); - if (radius == 0) { - //skip blurring - this.setOutputData(0, tex); - return; - } - - var intensity = this.properties.intensity; - - //blur sometimes needs an aspect correction - var aspect = LiteGraph.camera_aspect; - if (!aspect && window.gl !== undefined) { - aspect = gl.canvas.height / gl.canvas.width; - } - if (!aspect) { - aspect = 1; - } - aspect = this.properties.preserve_aspect ? aspect : 1; - - if (!LGraphTextureKuwaharaFilter._shaders[radius]) { - LGraphTextureKuwaharaFilter._shaders[radius] = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureKuwaharaFilter.pixel_shader, - { RADIUS: radius.toFixed(0) } - ); - } - - var shader = LGraphTextureKuwaharaFilter._shaders[radius]; - var mesh = GL.Mesh.getScreenQuad(); - tex.bind(0); - - this._temp_texture.drawTo(function() { - shader - .uniforms({ - u_texture: 0, - u_intensity: intensity, - u_resolution: [tex.width, tex.height], - u_iResolution: [1 / tex.width, 1 / tex.height] - }) - .draw(mesh); - }); - - this.setOutputData(0, this._temp_texture); - }; - - //from https://www.shadertoy.com/view/MsXSz4 - LGraphTextureKuwaharaFilter.pixel_shader = - "\n\ -precision highp float;\n\ -varying vec2 v_coord;\n\ -uniform sampler2D u_texture;\n\ -uniform float u_intensity;\n\ -uniform vec2 u_resolution;\n\ -uniform vec2 u_iResolution;\n\ -#ifndef RADIUS\n\ - #define RADIUS 7\n\ -#endif\n\ -void main() {\n\ -\n\ - const int radius = RADIUS;\n\ - vec2 fragCoord = v_coord;\n\ - vec2 src_size = u_iResolution;\n\ - vec2 uv = v_coord;\n\ - float n = float((radius + 1) * (radius + 1));\n\ - int i;\n\ - int j;\n\ - vec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\ - vec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\ - vec3 c;\n\ - \n\ - for (int j = -radius; j <= 0; ++j) {\n\ - for (int i = -radius; i <= 0; ++i) {\n\ - c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ - m0 += c;\n\ - s0 += c * c;\n\ - }\n\ - }\n\ - \n\ - for (int j = -radius; j <= 0; ++j) {\n\ - for (int i = 0; i <= radius; ++i) {\n\ - c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ - m1 += c;\n\ - s1 += c * c;\n\ - }\n\ - }\n\ - \n\ - for (int j = 0; j <= radius; ++j) {\n\ - for (int i = 0; i <= radius; ++i) {\n\ - c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ - m2 += c;\n\ - s2 += c * c;\n\ - }\n\ - }\n\ - \n\ - for (int j = 0; j <= radius; ++j) {\n\ - for (int i = -radius; i <= 0; ++i) {\n\ - c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ - m3 += c;\n\ - s3 += c * c;\n\ - }\n\ - }\n\ - \n\ - float min_sigma2 = 1e+2;\n\ - m0 /= n;\n\ - s0 = abs(s0 / n - m0 * m0);\n\ - \n\ - float sigma2 = s0.r + s0.g + s0.b;\n\ - if (sigma2 < min_sigma2) {\n\ - min_sigma2 = sigma2;\n\ - gl_FragColor = vec4(m0, 1.0);\n\ - }\n\ - \n\ - m1 /= n;\n\ - s1 = abs(s1 / n - m1 * m1);\n\ - \n\ - sigma2 = s1.r + s1.g + s1.b;\n\ - if (sigma2 < min_sigma2) {\n\ - min_sigma2 = sigma2;\n\ - gl_FragColor = vec4(m1, 1.0);\n\ - }\n\ - \n\ - m2 /= n;\n\ - s2 = abs(s2 / n - m2 * m2);\n\ - \n\ - sigma2 = s2.r + s2.g + s2.b;\n\ - if (sigma2 < min_sigma2) {\n\ - min_sigma2 = sigma2;\n\ - gl_FragColor = vec4(m2, 1.0);\n\ - }\n\ - \n\ - m3 /= n;\n\ - s3 = abs(s3 / n - m3 * m3);\n\ - \n\ - sigma2 = s3.r + s3.g + s3.b;\n\ - if (sigma2 < min_sigma2) {\n\ - min_sigma2 = sigma2;\n\ - gl_FragColor = vec4(m3, 1.0);\n\ - }\n\ -}\n\ -"; - - LiteGraph.registerNodeType( - "texture/kuwahara", - LGraphTextureKuwaharaFilter - ); - - // Texture ***************************************** - function LGraphTextureXDoGFilter() { - this.addInput("Texture", "Texture"); - this.addOutput("Filtered", "Texture"); - this.properties = { - sigma: 1.4, - k: 1.6, - p: 21.7, - epsilon: 79, - phi: 0.017 - }; - } - - LGraphTextureXDoGFilter.title = "XDoG Filter"; - LGraphTextureXDoGFilter.desc = - "Filters a texture giving an artistic ink style"; - - LGraphTextureXDoGFilter.max_radius = 10; - LGraphTextureXDoGFilter._shaders = []; - - LGraphTextureXDoGFilter.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var temp = this._temp_texture; - if ( - !temp || - temp.width != tex.width || - temp.height != tex.height || - temp.type != tex.type - ) { - this._temp_texture = new GL.Texture(tex.width, tex.height, { - type: tex.type, - format: gl.RGBA, - filter: gl.LINEAR - }); - } - - if (!LGraphTextureXDoGFilter._xdog_shader) { - LGraphTextureXDoGFilter._xdog_shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureXDoGFilter.xdog_pixel_shader - ); - } - var shader = LGraphTextureXDoGFilter._xdog_shader; - var mesh = GL.Mesh.getScreenQuad(); - - var sigma = this.properties.sigma; - var k = this.properties.k; - var p = this.properties.p; - var epsilon = this.properties.epsilon; - var phi = this.properties.phi; - tex.bind(0); - this._temp_texture.drawTo(function() { - shader - .uniforms({ - src: 0, - sigma: sigma, - k: k, - p: p, - epsilon: epsilon, - phi: phi, - cvsWidth: tex.width, - cvsHeight: tex.height - }) - .draw(mesh); - }); - - this.setOutputData(0, this._temp_texture); - }; - - //from https://github.com/RaymondMcGuire/GPU-Based-Image-Processing-Tools/blob/master/lib_webgl/scripts/main.js - LGraphTextureXDoGFilter.xdog_pixel_shader = - "\n\ -precision highp float;\n\ -uniform sampler2D src;\n\n\ -uniform float cvsHeight;\n\ -uniform float cvsWidth;\n\n\ -uniform float sigma;\n\ -uniform float k;\n\ -uniform float p;\n\ -uniform float epsilon;\n\ -uniform float phi;\n\ -varying vec2 v_coord;\n\n\ -float cosh(float val)\n\ -{\n\ - float tmp = exp(val);\n\ - float cosH = (tmp + 1.0 / tmp) / 2.0;\n\ - return cosH;\n\ -}\n\n\ -float tanh(float val)\n\ -{\n\ - float tmp = exp(val);\n\ - float tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\ - return tanH;\n\ -}\n\n\ -float sinh(float val)\n\ -{\n\ - float tmp = exp(val);\n\ - float sinH = (tmp - 1.0 / tmp) / 2.0;\n\ - return sinH;\n\ -}\n\n\ -void main(void){\n\ - vec3 destColor = vec3(0.0);\n\ - float tFrag = 1.0 / cvsHeight;\n\ - float sFrag = 1.0 / cvsWidth;\n\ - vec2 Frag = vec2(sFrag,tFrag);\n\ - vec2 uv = gl_FragCoord.st;\n\ - float twoSigmaESquared = 2.0 * sigma * sigma;\n\ - float twoSigmaRSquared = twoSigmaESquared * k * k;\n\ - int halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\ - const int MAX_NUM_ITERATION = 99999;\n\ - vec2 sum = vec2(0.0);\n\ - vec2 norm = vec2(0.0);\n\n\ - for(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\ - int i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\ - int j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\ - float d = length(vec2(i,j));\n\ - vec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\ - exp( -d * d / twoSigmaRSquared ));\n\n\ - vec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\ - norm += kernel;\n\ - sum += kernel * L;\n\ - }\n\n\ - sum /= norm;\n\n\ - float H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\ - float edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\ - destColor = vec3(edge);\n\ - gl_FragColor = vec4(destColor, 1.0);\n\ -}"; - - LiteGraph.registerNodeType("texture/xDoG", LGraphTextureXDoGFilter); - - // Texture Webcam ***************************************** - function LGraphTextureWebcam() { - this.addOutput("Webcam", "Texture"); - this.properties = { texture_name: "", facingMode: "user" }; - this.boxcolor = "black"; - this.version = 0; - } - - LGraphTextureWebcam.title = "Webcam"; - LGraphTextureWebcam.desc = "Webcam texture"; - - LGraphTextureWebcam.is_webcam_open = false; - - LGraphTextureWebcam.prototype.openStream = function() { - if (!navigator.getUserMedia) { - //console.log('getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags'); - return; - } - - this._waiting_confirmation = true; - - // Not showing vendor prefixes. - var constraints = { - audio: false, - video: { facingMode: this.properties.facingMode } - }; - navigator.mediaDevices - .getUserMedia(constraints) - .then(this.streamReady.bind(this)) - .catch(onFailSoHard); - - var that = this; - function onFailSoHard(e) { - LGraphTextureWebcam.is_webcam_open = false; - console.log("Webcam rejected", e); - that._webcam_stream = false; - that.boxcolor = "red"; - that.trigger("stream_error"); - } - }; - - LGraphTextureWebcam.prototype.closeStream = function() { - if (this._webcam_stream) { - var tracks = this._webcam_stream.getTracks(); - if (tracks.length) { - for (var i = 0; i < tracks.length; ++i) { - tracks[i].stop(); - } - } - LGraphTextureWebcam.is_webcam_open = false; - this._webcam_stream = null; - this._video = null; - this.boxcolor = "black"; - this.trigger("stream_closed"); - } - }; - - LGraphTextureWebcam.prototype.streamReady = function(localMediaStream) { - this._webcam_stream = localMediaStream; - //this._waiting_confirmation = false; - this.boxcolor = "green"; - var video = this._video; - if (!video) { - video = document.createElement("video"); - video.autoplay = true; - video.srcObject = localMediaStream; - this._video = video; - //document.body.appendChild( video ); //debug - //when video info is loaded (size and so) - video.onloadedmetadata = function(e) { - // Ready to go. Do some stuff. - LGraphTextureWebcam.is_webcam_open = true; - console.log(e); - }; - } - this.trigger("stream_ready", video); - }; - - LGraphTextureWebcam.prototype.onPropertyChanged = function( - name, - value - ) { - if (name == "facingMode") { - this.properties.facingMode = value; - this.closeStream(); - this.openStream(); - } - }; - - LGraphTextureWebcam.prototype.onRemoved = function() { - if (!this._webcam_stream) { - return; - } - - var tracks = this._webcam_stream.getTracks(); - if (tracks.length) { - for (var i = 0; i < tracks.length; ++i) { - tracks[i].stop(); - } - } - - this._webcam_stream = null; - this._video = null; - }; - - LGraphTextureWebcam.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed || this.size[1] <= 20) { - return; - } - - if (!this._video) { - return; - } - - //render to graph canvas - ctx.save(); - if (!ctx.webgl) { - //reverse image - ctx.drawImage(this._video, 0, 0, this.size[0], this.size[1]); - } else { - if (this._video_texture) { - ctx.drawImage( - this._video_texture, - 0, - 0, - this.size[0], - this.size[1] - ); - } - } - ctx.restore(); - }; - - LGraphTextureWebcam.prototype.onExecute = function() { - if (this._webcam_stream == null && !this._waiting_confirmation) { - this.openStream(); - } - - if (!this._video || !this._video.videoWidth) { - return; - } - - var width = this._video.videoWidth; - var height = this._video.videoHeight; - - var temp = this._video_texture; - if (!temp || temp.width != width || temp.height != height) { - this._video_texture = new GL.Texture(width, height, { - format: gl.RGB, - filter: gl.LINEAR - }); - } - - this._video_texture.uploadImage(this._video); - this._video_texture.version = ++this.version; - - if (this.properties.texture_name) { - var container = LGraphTexture.getTexturesContainer(); - container[this.properties.texture_name] = this._video_texture; - } - - this.setOutputData(0, this._video_texture); - for (var i = 1; i < this.outputs.length; ++i) { - if (!this.outputs[i]) { - continue; - } - switch (this.outputs[i].name) { - case "width": - this.setOutputData(i, this._video.videoWidth); - break; - case "height": - this.setOutputData(i, this._video.videoHeight); - break; - } - } - }; - - LGraphTextureWebcam.prototype.onGetOutputs = function() { - return [ - ["width", "number"], - ["height", "number"], - ["stream_ready", LiteGraph.EVENT], - ["stream_closed", LiteGraph.EVENT], - ["stream_error", LiteGraph.EVENT] - ]; - }; - - LiteGraph.registerNodeType("texture/webcam", LGraphTextureWebcam); - - //from https://github.com/spite/Wagner - function LGraphLensFX() { - this.addInput("in", "Texture"); - this.addInput("f", "number"); - this.addOutput("out", "Texture"); - this.properties = { - enabled: true, - factor: 1, - precision: LGraphTexture.LOW - }; - - this._uniforms = { u_texture: 0, u_factor: 1 }; - } - - LGraphLensFX.title = "Lens FX"; - LGraphLensFX.desc = "distortion and chromatic aberration"; - - LGraphLensFX.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphLensFX.prototype.onGetInputs = function() { - return [["enabled", "boolean"]]; - }; - - LGraphLensFX.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - if ( - this.properties.precision === LGraphTexture.PASS_THROUGH || - this.getInputOrProperty("enabled") === false - ) { - this.setOutputData(0, tex); - return; - } - - var temp = this._temp_texture; - if ( - !temp || - temp.width != tex.width || - temp.height != tex.height || - temp.type != tex.type - ) { - temp = this._temp_texture = new GL.Texture( - tex.width, - tex.height, - { type: tex.type, format: gl.RGBA, filter: gl.LINEAR } - ); - } - - var shader = LGraphLensFX._shader; - if (!shader) { - shader = LGraphLensFX._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphLensFX.pixel_shader - ); - } - - var factor = this.getInputData(1); - if (factor == null) { - factor = this.properties.factor; - } - - var uniforms = this._uniforms; - uniforms.u_factor = factor; - - //apply shader - gl.disable(gl.DEPTH_TEST); - temp.drawTo(function() { - tex.bind(0); - shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad()); - }); - - this.setOutputData(0, temp); - }; - - LGraphLensFX.pixel_shader = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_factor;\n\ - vec2 barrelDistortion(vec2 coord, float amt) {\n\ - vec2 cc = coord - 0.5;\n\ - float dist = dot(cc, cc);\n\ - return coord + cc * dist * amt;\n\ - }\n\ - \n\ - float sat( float t )\n\ - {\n\ - return clamp( t, 0.0, 1.0 );\n\ - }\n\ - \n\ - float linterp( float t ) {\n\ - return sat( 1.0 - abs( 2.0*t - 1.0 ) );\n\ - }\n\ - \n\ - float remap( float t, float a, float b ) {\n\ - return sat( (t - a) / (b - a) );\n\ - }\n\ - \n\ - vec4 spectrum_offset( float t ) {\n\ - vec4 ret;\n\ - float lo = step(t,0.5);\n\ - float hi = 1.0-lo;\n\ - float w = linterp( remap( t, 1.0/6.0, 5.0/6.0 ) );\n\ - ret = vec4(lo,1.0,hi, 1.) * vec4(1.0-w, w, 1.0-w, 1.);\n\ - \n\ - return pow( ret, vec4(1.0/2.2) );\n\ - }\n\ - \n\ - const float max_distort = 2.2;\n\ - const int num_iter = 12;\n\ - const float reci_num_iter_f = 1.0 / float(num_iter);\n\ - \n\ - void main()\n\ - { \n\ - vec2 uv=v_coord;\n\ - vec4 sumcol = vec4(0.0);\n\ - vec4 sumw = vec4(0.0); \n\ - for ( int i=0; i= res)\n\ - break;\n\ - iCount++;\n\ - }\n\ - float nf = n/normK;\n\ - return nf*nf*nf*nf;\n\ - }\n\ - void main() {\n\ - vec2 uv = v_coord * u_scale * u_viewport + u_offset * u_scale;\n\ - vec4 color = vec4( pNoise( uv, u_octaves ) * u_amplitude );\n\ - gl_FragColor = color;\n\ - }"; - - LiteGraph.registerNodeType("texture/perlin", LGraphTexturePerlin); - - function LGraphTextureCanvas2D() { - this.addInput("v"); - this.addOutput("out", "Texture"); - this.properties = { - code: LGraphTextureCanvas2D.default_code, - width: 512, - height: 512, - clear: true, - precision: LGraphTexture.DEFAULT, - use_html_canvas: false - }; - this._func = null; - this._temp_texture = null; - this.compileCode(); - } - - LGraphTextureCanvas2D.title = "Canvas2D"; - LGraphTextureCanvas2D.desc = "Executes Canvas2D code inside a texture or the viewport."; - LGraphTextureCanvas2D.help = "Set width and height to 0 to match viewport size."; - - LGraphTextureCanvas2D.default_code = "//vars: canvas,ctx,time\nctx.fillStyle='red';\nctx.fillRect(0,0,50,50);\n"; - - LGraphTextureCanvas2D.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }, - code: { type: "code" }, - width: { type: "Number", precision: 0, step: 1 }, - height: { type: "Number", precision: 0, step: 1 } - }; - - LGraphTextureCanvas2D.prototype.onPropertyChanged = function( name, value ) { - if (name == "code" ) - this.compileCode( value ); - } - - LGraphTextureCanvas2D.prototype.compileCode = function( code ) { - this._func = null; - if( !LiteGraph.allow_scripts ) - return; - - try { - this._func = new Function( "canvas", "ctx", "time", "script","v", code ); - this.boxcolor = "#00FF00"; - } catch (err) { - this.boxcolor = "#FF0000"; - console.error("Error parsing script"); - console.error(err); - } - }; - - LGraphTextureCanvas2D.prototype.onExecute = function() { - var func = this._func; - if (!func || !this.isOutputConnected(0)) { - return; - } - this.executeDraw( func ); - } - - LGraphTextureCanvas2D.prototype.executeDraw = function( func_context ) { - - var width = this.properties.width || gl.canvas.width; - var height = this.properties.height || gl.canvas.height; - var temp = this._temp_texture; - var type = LGraphTexture.getTextureType( this.properties.precision ); - if (!temp || temp.width != width || temp.height != height || temp.type != type ) { - temp = this._temp_texture = new GL.Texture(width, height, { - format: gl.RGBA, - filter: gl.LINEAR, - type: type - }); - } - - var v = this.getInputData(0); - - var properties = this.properties; - var that = this; - var time = this.graph.getTime(); - var ctx = gl; - var canvas = gl.canvas; - if( this.properties.use_html_canvas || !global.enableWebGLCanvas ) - { - if(!this._canvas) - { - canvas = this._canvas = createCanvas(width.height); - ctx = this._ctx = canvas.getContext("2d"); - } - else - { - canvas = this._canvas; - ctx = this._ctx; - } - canvas.width = width; - canvas.height = height; - } - - if(ctx == gl) //using Canvas2DtoWebGL - temp.drawTo(function() { - gl.start2D(); - if(properties.clear) - { - gl.clearColor(0,0,0,0); - gl.clear( gl.COLOR_BUFFER_BIT ); - } - - try { - if (func_context.draw) { - func_context.draw.call(that, canvas, ctx, time, func_context, v); - } else { - func_context.call(that, canvas, ctx, time, func_context,v); - } - that.boxcolor = "#00FF00"; - } catch (err) { - that.boxcolor = "#FF0000"; - console.error("Error executing script"); - console.error(err); - } - gl.finish2D(); - }); - else //rendering to offscren canvas and uploading to texture - { - if(properties.clear) - ctx.clearRect(0,0,canvas.width,canvas.height); - - try { - if (func_context.draw) { - func_context.draw.call(this, canvas, ctx, time, func_context, v); - } else { - func_context.call(this, canvas, ctx, time, func_context,v); - } - this.boxcolor = "#00FF00"; - } catch (err) { - this.boxcolor = "#FF0000"; - console.error("Error executing script"); - console.error(err); - } - temp.uploadImage( canvas ); - } - - this.setOutputData(0, temp); - }; - - LiteGraph.registerNodeType("texture/canvas2D", LGraphTextureCanvas2D); - - // To do chroma keying ***************** - - function LGraphTextureMatte() { - this.addInput("in", "Texture"); - - this.addOutput("out", "Texture"); - this.properties = { - key_color: vec3.fromValues(0, 1, 0), - threshold: 0.8, - slope: 0.2, - precision: LGraphTexture.DEFAULT - }; - } - - LGraphTextureMatte.title = "Matte"; - LGraphTextureMatte.desc = "Extracts background"; - - LGraphTextureMatte.widgets_info = { - key_color: { widget: "color" }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureMatte.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var tex = this.getInputData(0); - - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, tex); - return; - } - - if (!tex) { - return; - } - - this._tex = LGraphTexture.getTargetTexture( - tex, - this._tex, - this.properties.precision - ); - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - - if (!this._uniforms) { - this._uniforms = { - u_texture: 0, - u_key_color: this.properties.key_color, - u_threshold: 1, - u_slope: 1 - }; - } - var uniforms = this._uniforms; - - var mesh = Mesh.getScreenQuad(); - var shader = LGraphTextureMatte._shader; - if (!shader) { - shader = LGraphTextureMatte._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureMatte.pixel_shader - ); - } - - uniforms.u_key_color = this.properties.key_color; - uniforms.u_threshold = this.properties.threshold; - uniforms.u_slope = this.properties.slope; - - this._tex.drawTo(function() { - tex.bind(0); - shader.uniforms(uniforms).draw(mesh); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureMatte.pixel_shader = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec3 u_key_color;\n\ - uniform float u_threshold;\n\ - uniform float u_slope;\n\ - \n\ - void main() {\n\ - vec3 color = texture2D( u_texture, v_coord ).xyz;\n\ - float diff = length( normalize(color) - normalize(u_key_color) );\n\ - float edge = u_threshold * (1.0 - u_slope);\n\ - float alpha = smoothstep( edge, u_threshold, diff);\n\ - gl_FragColor = vec4( color, alpha );\n\ - }"; - - LiteGraph.registerNodeType("texture/matte", LGraphTextureMatte); - - //*********************************** - function LGraphCubemapToTexture2D() { - this.addInput("in", "texture"); - this.addInput("yaw", "number"); - this.addOutput("out", "texture"); - this.properties = { yaw: 0 }; - } - - LGraphCubemapToTexture2D.title = "CubemapToTexture2D"; - LGraphCubemapToTexture2D.desc = "Transforms a CUBEMAP texture into a TEXTURE2D in Polar Representation"; - - LGraphCubemapToTexture2D.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) - return; - - var tex = this.getInputData(0); - if ( !tex || tex.texture_type != GL.TEXTURE_CUBE_MAP ) - return; - if( this._last_tex && ( this._last_tex.height != tex.height || this._last_tex.type != tex.type )) - this._last_tex = null; - var yaw = this.getInputOrProperty("yaw"); - this._last_tex = GL.Texture.cubemapToTexture2D( tex, tex.height, this._last_tex, true, yaw ); - this.setOutputData( 0, this._last_tex ); - }; - - LiteGraph.registerNodeType( "texture/cubemapToTexture2D", LGraphCubemapToTexture2D ); -})(this); - -(function(global) { - var LiteGraph = global.LiteGraph; -======= var uniforms = this._uniforms; this._uniforms.u_angle = this.properties.angle * DEG2RAD; this._uniforms.u_scale = this.properties.scale; @@ -36138,7 +17742,6 @@ void main(void){\n\ filter: gl.LINEAR }); } ->>>>>>> custom widget custom size support this._tex.drawTo(function() { shader.uniforms(uniforms).draw(mesh); @@ -36551,8 +18154,8 @@ void main(void){\n\ }; this._uniforms = { u_texture: 0, - u_near: 0.1, - u_far: 10000 + u_camera_planes: null, //filled later + u_ires: vec2.create() }; } @@ -36584,9 +18187,6 @@ void main(void){\n\ } var uniforms = this._uniforms; - - uniforms.u_near = tex.near_far_planes[0]; - uniforms.u_far = tex.near_far_planes[1]; uniforms.u_invert = this.properties.invert ? 1 : 0; gl.disable(gl.BLEND); @@ -36606,6 +18206,8 @@ void main(void){\n\ planes = [0.1, 1000]; } //hardcoded uniforms.u_camera_planes = planes; + //uniforms.u_ires.set([1/tex.width, 1/tex.height]); + uniforms.u_ires.set([0,0]); this._temp_texture.drawTo(function() { tex.bind(0); @@ -36621,15 +18223,14 @@ void main(void){\n\ precision highp float;\n\ varying vec2 v_coord;\n\ uniform sampler2D u_texture;\n\ - uniform float u_near;\n\ - uniform float u_far;\n\ + uniform vec2 u_camera_planes;\n\ uniform int u_invert;\n\ + uniform vec2 u_ires;\n\ \n\ void main() {\n\ - float zNear = u_near;\n\ - float zFar = u_far;\n\ - float depth = texture2D(u_texture, v_coord).x;\n\ - depth = depth * 2.0 - 1.0;\n\ + float zNear = u_camera_planes.x;\n\ + float zFar = u_camera_planes.y;\n\ + float depth = texture2D(u_texture, v_coord + u_ires*0.5).x * 2.0 - 1.0;\n\ float f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\ if( u_invert == 1 )\n\ f = 1.0 - f;\n\ @@ -38664,6 +20265,7 @@ void main(void){\n\ (function(global) { var LiteGraph = global.LiteGraph; + var LGraphTexture = global.LGraphTexture; //Works with Litegl.js to create WebGL nodes if (typeof GL != "undefined") { diff --git a/build/litegraph.min.js b/build/litegraph.min.js index 4f672d6da..c09008e3b 100755 --- a/build/litegraph.min.js +++ b/build/litegraph.min.js @@ -1,723 +1,3 @@ -<<<<<<< HEAD -(function(B){function c(a){h.debug&&console.log("Graph created");this.list_of_graphcanvas=null;this.clear();a&&this.configure(a)}function m(a,b,d,g,f,r){this.id=a;this.type=b;this.origin_id=d;this.origin_slot=g;this.target_id=f;this.target_slot=r;this._data=null;this._pos=new Float32Array(2)}function n(a){this._ctor(a)}function l(a){this._ctor(a)}function x(a,b){this.offset=new Float32Array([0,0]);this.scale=1;this.max_scale=10;this.min_scale=0.1;this.onredraw=null;this.enabled=!0;this.last_mouse= -[0,0];this.element=null;this.visible_area=new Float32Array(4);a&&(this.element=a,b||this.bindEvents(a))}function e(a,b,d){d=d||{};this.background_image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; -a&&a.constructor===String&&(a=document.querySelector(a));this.ds=new x;this.zoom_modify_alpha=!0;this.title_text_font=""+h.NODE_TEXT_SIZE+"px Arial";this.inner_text_font="normal "+h.NODE_SUBTEXT_SIZE+"px Arial";this.node_title_color=h.NODE_TITLE_COLOR;this.default_link_color=h.LINK_COLOR;this.default_connection_color={input_off:"#778",input_on:"#7F7",output_off:"#778",output_on:"#7F7"};this.highquality_render=!0;this.use_gradients=!1;this.editor_alpha=1;this.pause_rendering=!1;this.clear_background= -!0;this.read_only=!1;this.render_only_selected=!0;this.live_mode=!1;this.allow_searchbox=this.allow_interaction=this.allow_dragnodes=this.allow_dragcanvas=this.show_info=!0;this.drag_mode=this.allow_reconnect_links=!1;this.filter=this.dragging_rectangle=null;this.always_render_background=!1;this.render_canvas_border=this.render_shadows=!0;this.render_connections_shadows=!1;this.render_connections_border=!0;this.render_connection_arrows=this.render_curved_connections=!1;this.render_collapsed_slots= -!0;this.render_execution_order=!1;this.render_link_tooltip=this.render_title_colored=!0;this.links_render_mode=h.SPLINE_LINK;this.canvas_mouse=[0,0];this.onSelectionChange=this.onNodeMoved=this.onDrawLinkTooltip=this.onDrawOverlay=this.onDrawForeground=this.onDrawBackground=this.onMouse=this.onSearchBoxSelection=this.onSearchBox=null;this.connections_width=3;this.round_radius=8;this.over_link_center=this.node_widget=this.current_node=null;this.last_mouse_position=[0,0];this.visible_area=this.ds.visible_area; -this.visible_links=[];b&&b.attachCanvas(this);this.setCanvas(a);this.clear();d.skip_render||this.startRendering();this.autoresize=d.autoresize}function C(a,b){return Math.sqrt((b[0]-a[0])*(b[0]-a[0])+(b[1]-a[1])*(b[1]-a[1]))}function w(a,b,d,g,f,r){return da&&gb?!0:!1}function u(a,b){var d=a[0]+a[2],g=a[1]+a[3],f=b[1]+b[3];return a[0]>b[0]+b[2]||a[1]>f||dc.width-k.width-10&&(f=c.width-k.width-10);c.height&&h>c.height-k.height-10&&(h=c.height-k.height-10)}r.style.left=f+"px";r.style.top=h+"px";b.scale&&(r.style.transform="scale("+b.scale+")")}function A(a){this.points=a;this.nearest=this.selected=-1;this.size=null;this.must_update=!0;this.margin=5}var h=B.LiteGraph={VERSION:0.4,CANVAS_GRID_SIZE:10,NODE_TITLE_HEIGHT:30, -NODE_TITLE_TEXT_Y:20,NODE_SLOT_HEIGHT:20,NODE_WIDGET_HEIGHT:20,NODE_WIDTH:140,NODE_MIN_WIDTH:50,NODE_COLLAPSED_RADIUS:10,NODE_COLLAPSED_WIDTH:80,NODE_TITLE_COLOR:"#999",NODE_TEXT_SIZE:14,NODE_TEXT_COLOR:"#AAA",NODE_SUBTEXT_SIZE:12,NODE_DEFAULT_COLOR:"#333",NODE_DEFAULT_BGCOLOR:"#353535",NODE_DEFAULT_BOXCOLOR:"#666",NODE_DEFAULT_SHAPE:"box",DEFAULT_SHADOW_COLOR:"rgba(0,0,0,0.5)",DEFAULT_GROUP_FONT:24,WIDGET_BGCOLOR:"#222",WIDGET_OUTLINE_COLOR:"#666",WIDGET_TEXT_COLOR:"#DDD",WIDGET_SECONDARY_TEXT_COLOR:"#999", -LINK_COLOR:"#9A9",EVENT_LINK_COLOR:"#A86",CONNECTING_LINK_COLOR:"#AFA",MAX_NUMBER_OF_NODES:1E3,DEFAULT_POSITION:[100,100],VALID_SHAPES:["default","box","round","card"],BOX_SHAPE:1,ROUND_SHAPE:2,CIRCLE_SHAPE:3,CARD_SHAPE:4,ARROW_SHAPE:5,INPUT:1,OUTPUT:2,EVENT:-1,ACTION:-1,ALWAYS:0,ON_EVENT:1,NEVER:2,ON_TRIGGER:3,UP:1,DOWN:2,LEFT:3,RIGHT:4,CENTER:5,STRAIGHT_LINK:0,LINEAR_LINK:1,SPLINE_LINK:2,NORMAL_TITLE:0,NO_TITLE:1,TRANSPARENT_TITLE:2,AUTOHIDE_TITLE:3,proxy:null,node_images_path:"",debug:!1,catch_exceptions:!0, -throw_errors:!0,allow_scripts:!1,registered_node_types:{},node_types_by_file_extension:{},Nodes:{},searchbox_extras:{},registerNodeType:function(a,b){if(!b.prototype)throw"Cannot register a simple object, it must be a class with a prototype";b.type=a;h.debug&&console.log("Node registered: "+a);a.split("/");var d=b.name,g=a.lastIndexOf("/");b.category=a.substr(0,g);b.title||(b.title=d);if(b.prototype)for(var f in n.prototype)b.prototype[f]||(b.prototype[f]=n.prototype[f]);if(g=this.registered_node_types[a])console.log("replacing node type: "+ -a);else if(Object.hasOwnProperty(b.prototype,"shape")||Object.defineProperty(b.prototype,"shape",{set:function(a){switch(a){case "default":delete this._shape;break;case "box":this._shape=h.BOX_SHAPE;break;case "round":this._shape=h.ROUND_SHAPE;break;case "circle":this._shape=h.CIRCLE_SHAPE;break;case "card":this._shape=h.CARD_SHAPE;break;default:this._shape=a}},get:function(a){return this._shape},enumerable:!0,configurable:!0}),b.prototype.onPropertyChange&&console.warn("LiteGraph node class "+a+ -" has onPropertyChange method, it must be called onPropertyChanged with d at the end"),b.supported_extensions)for(f in b.supported_extensions){var r=b.supported_extensions[f];r&&r.constructor===String&&(this.node_types_by_file_extension[r.toLowerCase()]=b)}this.registered_node_types[a]=b;b.constructor.name&&(this.Nodes[d]=b);if(h.onNodeTypeRegistered)h.onNodeTypeRegistered(a,b);if(g&&h.onNodeTypeReplaced)h.onNodeTypeReplaced(a,b,g)},unregisterNodeType:function(a){var b=a.constructor===String?this.registered_node_types[a]: -a;if(!b)throw"node type not found: "+a;delete this.registered_node_types[b.type];b.constructor.name&&delete this.Nodes[b.constructor.name]},wrapFunctionAsNode:function(a,b,d,g,f){for(var r=Array(b.length),s="",c=h.getParameterNames(b),k=0;ks&&(s=f.size[0]),c+=f.size[1]+a+h.NODE_TITLE_HEIGHT;b+=s+a}this.setDirtyCanvas(!0,!0)};c.prototype.getTime=function(){return this.globaltime};c.prototype.getFixedTime=function(){return this.fixedtime};c.prototype.getElapsedTime= -function(){return this.elapsed_time};c.prototype.sendEventToAllNodes=function(a,b,d){d=d||h.ALWAYS;var g=this._nodes_in_order?this._nodes_in_order:this._nodes;if(g)for(var f=0,r=g.length;f=h.MAX_NUMBER_OF_NODES)throw"LiteGraph: max number of nodes in a graph reached";null==a.id||-1==a.id?a.id=++this.last_node_id:this.last_node_id< -a.id&&(this.last_node_id=a.id);a.graph=this;this._version++;this._nodes.push(a);this._nodes_by_id[a.id]=a;if(a.onAdded)a.onAdded(this);this.config.align_to_grid&&a.alignToGrid();b||this.updateExecutionOrder();if(this.onNodeAdded)this.onNodeAdded(a);this.setDirtyCanvas(!0);this.change();return a}};c.prototype.remove=function(a){if(a.constructor===h.LGraphGroup){var b=this._groups.indexOf(a);-1!=b&&this._groups.splice(b,1);a.graph=null;this._version++;this.setDirtyCanvas(!0,!0);this.change()}else if(null!= -this._nodes_by_id[a.id]&&!a.ignore_remove){if(a.inputs)for(b=0;ba.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});this.id=-1;this.type=null;this.inputs=[];this.outputs=[];this.connections=[];this.properties={};this.properties_info=[];this.flags={}};n.prototype.configure=function(a){this.graph&&this.graph._version++;for(var b in a)if("properties"==b)for(var d in a.properties){if(this.properties[d]=a.properties[d],this.onPropertyChanged)this.onPropertyChanged(d, -a.properties[d])}else null!=a[b]&&("object"==typeof a[b]?this[b]&&this[b].configure?this[b].configure(a[b]):this[b]=h.cloneObject(a[b],this[b]):this[b]=a[b]);a.title||(this.title=this.constructor.title);if(this.onConnectionsChange){if(this.inputs)for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d._data=b,this.outputs[a].links))for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d.type=b,this.outputs[a].links))for(d=0;d=this.inputs.length||null==this.inputs[a].link)){var d=this.graph.links[this.inputs[a].link];if(!d)return null;if(!b)return d.data;var g=this.graph.getNodeById(d.origin_id);if(!g)return d.data; -if(g.updateOutputData)g.updateOutputData(d.origin_slot);else if(g.onExecute)g.onExecute();return d.data}};n.prototype.getInputDataType=function(a){if(!this.inputs||a>=this.inputs.length||null==this.inputs[a].link)return null;a=this.graph.links[this.inputs[a].link];if(!a)return null;var b=this.graph.getNodeById(a.origin_id);return b?(a=b.outputs[a.origin_slot])?a.type:null:a.type};n.prototype.getInputDataByName=function(a,b){var d=this.findInputSlot(a);return-1==d?null:this.getInputData(d,b)};n.prototype.isInputConnected= -function(a){return this.inputs?a=this.inputs.length)return null;a=this.inputs[a];return a&&null!==a.link?(a=this.graph.links[a.link])?this.graph.getNodeById(a.origin_id):null:null};n.prototype.getInputOrProperty=function(a){if(!this.inputs||!this.inputs.length)return this.properties?this.properties[a]: -null;for(var b=0,d=this.inputs.length;b=this.outputs.length?null:this.outputs[a]._data};n.prototype.getOutputInfo=function(a){return this.outputs?a=this.outputs.length)return null;a=this.outputs[a];if(!a.links||0==a.links.length)return null;for(var b=[],d=0;da&&this.pos[1]-f-db)return!0;return!1};n.prototype.getSlotInPosition=function(a,b){var d=new Float32Array(2);if(this.inputs)for(var g=0,f=this.inputs.length;g=this.outputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),null;b&&b.constructor===Number&&(b=this.graph.getNodeById(b)); -if(!b)throw"target node is null";if(b==this)return null;if(d.constructor===String){if(d=b.findInputSlot(d),-1==d)return h.debug&&console.log("Connect: Error, no slot of name "+d),null}else{if(d===h.EVENT)return null;if(!b.inputs||d>=b.inputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),null}null!=b.inputs[d].link&&b.disconnectInput(d);var g=this.outputs[a];if(b.onConnectInput&&!1===b.onConnectInput(d,g.type,g,this,a))return null;var f=b.inputs[d],r=null;if(h.isValidConnection(g.type, -f.type)){r=new m(++this.graph.last_link_id,f.type,this.id,a,b.id,d);this.graph.links[r.id]=r;null==g.links&&(g.links=[]);g.links.push(r.id);b.inputs[d].link=r.id;this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(h.OUTPUT,a,!0,r,g);if(b.onConnectionsChange)b.onConnectionsChange(h.INPUT,d,!0,r,f);this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(h.INPUT,b,d,this,a),this.graph.onNodeConnectionChange(h.OUTPUT,this,a,b,d))}this.setDirtyCanvas(!1, -!0);this.graph.connectionChange(this,r);return r};n.prototype.disconnectOutput=function(a,b){if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return h.debug&&console.log("Connect: Error, no slot of name "+a),!1}else if(!this.outputs||a>=this.outputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),!1;var d=this.outputs[a];if(!d||!d.links||0==d.links.length)return!1;if(b){b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"Target Node not found"; -for(var g=0,f=d.links.length;g=this.inputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),!1;var b=this.inputs[a];if(!b)return!1;var d=this.inputs[a].link;this.inputs[a].link=null;var g=this.graph.links[d];if(g){var f=this.graph.getNodeById(g.origin_id);if(!f)return!1;var r=f.outputs[g.origin_slot];if(!r||!r.links||0==r.links.length)return!1;for(var s=0,c=r.links.length;sb&&this.inputs[b].pos)return d[0]=this.pos[0]+this.inputs[b].pos[0],d[1]=this.pos[1]+this.inputs[b].pos[1],d;if(!a&&g>b&&this.outputs[b].pos)return d[0]=this.pos[0]+this.outputs[b].pos[0], -d[1]=this.pos[1]+this.outputs[b].pos[1],d;if(this.horizontal)return d[0]=this.pos[0]+this.size[0]/g*(b+0.5),d[1]=a?this.pos[1]-h.NODE_TITLE_HEIGHT:this.pos[1]+this.size[1],d;d[0]=a?this.pos[0]+f:this.pos[0]+this.size[0]+1-f;d[1]=this.pos[1]+(b+0.7)*h.NODE_SLOT_HEIGHT+(this.constructor.slot_start_y||0);return d};n.prototype.alignToGrid=function(){this.pos[0]=h.CANVAS_GRID_SIZE*Math.round(this.pos[0]/h.CANVAS_GRID_SIZE);this.pos[1]=h.CANVAS_GRID_SIZE*Math.round(this.pos[1]/h.CANVAS_GRID_SIZE)};n.prototype.trace= -function(a){this.console||(this.console=[]);this.console.push(a);this.console.length>n.MAX_CONSOLE&&this.console.shift();this.graph.onNodeTrace(this,a)};n.prototype.setDirtyCanvas=function(a,b){this.graph&&this.graph.sendActionToCanvas("setDirty",[a,b])};n.prototype.loadImage=function(a){var b=new Image;b.src=h.node_images_path+a;b.ready=!1;var d=this;b.onload=function(){this.ready=!0;d.setDirtyCanvas(!0)};return b};n.prototype.captureInput=function(a){if(this.graph&&this.graph.list_of_graphcanvas)for(var b= -this.graph.list_of_graphcanvas,d=0;da.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0}); -Object.defineProperty(this,"size",{set:function(a){!a||2>a.length||(this._size[0]=Math.max(140,a[0]),this._size[1]=Math.max(80,a[1]))},get:function(){return this._size},enumerable:!0})};l.prototype.configure=function(a){this.title=a.title;this._bounding.set(a.bounding);this.color=a.color;this.font=a.font};l.prototype.serialize=function(){var a=this._bounding;return{title:this.title,bounding:[Math.round(a[0]),Math.round(a[1]),Math.round(a[2]),Math.round(a[3])],color:this.color,font:this.font}};l.prototype.move= -function(a,b,d){this._pos[0]+=a;this._pos[1]+=b;if(!d)for(d=0;dthis.max_scale&&(a=this.max_scale);if(a!=this.scale&&this.element){var d=this.element.getBoundingClientRect(); -if(d){b=b||[0.5*d.width,0.5*d.height];d=this.convertCanvasToOffset(b);this.scale=a;0.01>Math.abs(this.scale-1)&&(this.scale=1);var g=this.convertCanvasToOffset(b),d=[g[0]-d[0],g[1]-d[1]];this.offset[0]+=d[0];this.offset[1]+=d[1];if(this.onredraw)this.onredraw(this)}}};x.prototype.changeDeltaScale=function(a,b){this.changeScale(this.scale*a,b)};x.prototype.reset=function(){this.scale=1;this.offset[0]=0;this.offset[1]=0};B.LGraphCanvas=h.LGraphCanvas=e;e.link_type_colors={"-1":h.EVENT_LINK_COLOR,number:"#AAA", -node:"#DCA"};e.gradients={};e.prototype.clear=function(){this.fps=this.render_time=this.last_draw_time=this.frame=0;this.dragging_rectangle=null;this.selected_nodes={};this.selected_group=null;this.visible_nodes=[];this.connecting_node=this.node_capturing_input=this.node_over=this.node_dragged=null;this.highlighted_links={};this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_widget=this.node_in_panel=this.dirty_area=null;this.last_mouse=[0,0];this.last_mouseclick=0;this.visible_area.set([0,0,0,0]); -if(this.onClear)this.onClear()};e.prototype.setGraph=function(a,b){this.graph!=a&&(b||this.clear(),!a&&this.graph?this.graph.detachCanvas(this):(a.attachCanvas(this),this._graph_stack&&(this._graph_stack=null),this.setDirty(!0,!0)))};e.prototype.openSubgraph=function(a){if(!a)throw"graph cannot be null";if(this.graph==a)throw"graph cannot be the same";this.clear();this.graph&&(this._graph_stack||(this._graph_stack=[]),this._graph_stack.push(this.graph));a.attachCanvas(this);this.setDirty(!0,!0)}; -e.prototype.closeSubgraph=function(){if(this._graph_stack&&0!=this._graph_stack.length){var a=this.graph._subgraph_node,b=this._graph_stack.pop();this.selected_nodes={};this.highlighted_links={};b.attachCanvas(this);this.setDirty(!0,!0);a&&(this.centerOnNode(a),this.selectNodes([a]))}};e.prototype.getCurrentGraph=function(){return this.graph};e.prototype.setCanvas=function(a,b){if(a&&a.constructor===String&&(a=document.getElementById(a),!a))throw"Error creating LiteGraph canvas: Canvas not found"; -if(a!==this.canvas&&(!a&&this.canvas&&(b||this.unbindEvents()),this.canvas=a,this.ds.element=a)){a.className+=" lgraphcanvas";a.data=this;a.tabindex="1";this.bgcanvas=null;this.bgcanvas||(this.bgcanvas=document.createElement("canvas"),this.bgcanvas.width=this.canvas.width,this.bgcanvas.height=this.canvas.height);if(null==a.getContext){if("canvas"!=a.localName)throw"Element supplied for LGraphCanvas must be a element, you passed a "+a.localName;throw"This browser doesn't support Canvas";}null== -(this.ctx=a.getContext("2d"))&&(a.webgl_enabled||console.warn("This canvas seems to be WebGL, enabling WebGL renderer"),this.enableWebGL());this._mousemove_callback=this.processMouseMove.bind(this);this._mouseup_callback=this.processMouseUp.bind(this);b||this.bindEvents()}};e.prototype._doNothing=function(a){a.preventDefault();return!1};e.prototype._doReturnTrue=function(a){a.preventDefault();return!0};e.prototype.bindEvents=function(){if(this._events_binded)console.warn("LGraphCanvas: events already binded"); -else{var a=this.canvas,b=this.getCanvasWindow().document;this._mousedown_callback=this.processMouseDown.bind(this);this._mousewheel_callback=this.processMouseWheel.bind(this);a.addEventListener("mousedown",this._mousedown_callback,!0);a.addEventListener("mousemove",this._mousemove_callback);a.addEventListener("mousewheel",this._mousewheel_callback,!1);a.addEventListener("contextmenu",this._doNothing);a.addEventListener("DOMMouseScroll",this._mousewheel_callback,!1);a.addEventListener("touchstart", -this.touchHandler,!0);a.addEventListener("touchmove",this.touchHandler,!0);a.addEventListener("touchend",this.touchHandler,!0);a.addEventListener("touchcancel",this.touchHandler,!0);this._key_callback=this.processKey.bind(this);a.addEventListener("keydown",this._key_callback,!0);b.addEventListener("keyup",this._key_callback,!0);this._ondrop_callback=this.processDrop.bind(this);a.addEventListener("dragover",this._doNothing,!1);a.addEventListener("dragend",this._doNothing,!1);a.addEventListener("drop", -this._ondrop_callback,!1);a.addEventListener("dragenter",this._doReturnTrue,!1);this._events_binded=!0}};e.prototype.unbindEvents=function(){if(this._events_binded){var a=this.getCanvasWindow().document;this.canvas.removeEventListener("mousedown",this._mousedown_callback);this.canvas.removeEventListener("mousewheel",this._mousewheel_callback);this.canvas.removeEventListener("DOMMouseScroll",this._mousewheel_callback);this.canvas.removeEventListener("keydown",this._key_callback);a.removeEventListener("keyup", -this._key_callback);this.canvas.removeEventListener("contextmenu",this._doNothing);this.canvas.removeEventListener("drop",this._ondrop_callback);this.canvas.removeEventListener("dragenter",this._doReturnTrue);this.canvas.removeEventListener("touchstart",this.touchHandler);this.canvas.removeEventListener("touchmove",this.touchHandler);this.canvas.removeEventListener("touchend",this.touchHandler);this.canvas.removeEventListener("touchcancel",this.touchHandler);this._ondrop_callback=this._key_callback= -this._mousewheel_callback=this._mousedown_callback=null;this._events_binded=!1}else console.warn("LGraphCanvas: no events binded")};e.getFileExtension=function(a){var b=a.indexOf("?");-1!=b&&(a=a.substr(0,b));b=a.lastIndexOf(".");return-1==b?"":a.substr(b+1).toLowerCase()};e.prototype.enableWebGL=function(){if(void 0===typeof GL)throw"litegl.js must be included to use a WebGL canvas";if(void 0===typeof enableWebGLCanvas)throw"webglCanvas.js must be included to use this feature";this.gl=this.ctx=enableWebGLCanvas(this.canvas); -this.ctx.webgl=!0;this.bgcanvas=this.canvas;this.bgctx=this.gl;this.canvas.webgl_enabled=!0};e.prototype.setDirty=function(a,b){a&&(this.dirty_canvas=!0);b&&(this.dirty_bgcanvas=!0)};e.prototype.getCanvasWindow=function(){if(!this.canvas)return window;var a=this.canvas.ownerDocument;return a.defaultView||a.parentWindow};e.prototype.startRendering=function(){function a(){this.pause_rendering||this.draw();var b=this.getCanvasWindow();this.is_rendering&&b.requestAnimationFrame(a.bind(this))}this.is_rendering|| -(this.is_rendering=!0,a.call(this))};e.prototype.stopRendering=function(){this.is_rendering=!1};e.prototype.processMouseDown=function(a){if(this.graph){this.adjustMouseEvent(a);var b=this.getCanvasWindow();e.active_canvas=this;this.canvas.removeEventListener("mousemove",this._mousemove_callback);b.document.addEventListener("mousemove",this._mousemove_callback,!0);b.document.addEventListener("mouseup",this._mouseup_callback,!0);var d=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes,5), -g=!1,f=300>h.getTime()-this.last_mouseclick;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;this.canvas.focus();h.closeAllContextMenus(b);if(!this.onMouse||!0!=this.onMouse(a)){if(1==a.which){a.ctrlKey&&(this.dragging_rectangle=new Float32Array(4),this.dragging_rectangle[0]=a.canvasX,this.dragging_rectangle[1]=a.canvasY,this.dragging_rectangle[2]=1,this.dragging_rectangle[3]=1,g=!0);var r=!1;if(d&&this.allow_interaction&&!g&&!this.read_only){this.live_mode||d.flags.pinned||this.bringToFront(d); -if(!this.connecting_node&&!d.flags.collapsed&&!this.live_mode)if(!g&&!1!==d.resizable&&w(a.canvasX,a.canvasY,d.pos[0]+d.size[0]-5,d.pos[1]+d.size[1]-5,10,10))this.resizing_node=d,this.canvas.style.cursor="se-resize",g=!0;else{if(d.outputs)for(var s=0,c=d.outputs.length;sr[0]+4||a.canvasYr[1]+4)){this.showLinkMenu(d,a);this.over_link_center=null;break}this.selected_group=this.graph.getGroupOnPos(a.canvasX,a.canvasY);this.selected_group_resizing= -!1;this.selected_group&&!this.read_only&&(a.ctrlKey&&(this.dragging_rectangle=null),10>C([a.canvasX,a.canvasY],[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])*this.ds.scale?this.selected_group_resizing=!0:this.selected_group.recomputeInsideNodes());f&&!this.read_only&&this.allow_searchbox&&this.showSearchBox(a);r=!0}!g&&r&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}else 2!=a.which&&3==a.which&&(this.read_only||this.processContextMenu(d, -a));this.last_mouse[0]=a.localX;this.last_mouse[1]=a.localY;this.last_mouseclick=h.getTime();this.last_mouse_dragging=!0;this.graph.change();(!b.document.activeElement||"input"!=b.document.activeElement.nodeName.toLowerCase()&&"textarea"!=b.document.activeElement.nodeName.toLowerCase())&&a.preventDefault();a.stopPropagation();if(this.onMouseDown)this.onMouseDown(a);return!1}}};e.prototype.processMouseMove=function(a){this.autoresize&&this.resize();if(this.graph){e.active_canvas=this;this.adjustMouseEvent(a); -var b=[a.localX,a.localY],d=[b[0]-this.last_mouse[0],b[1]-this.last_mouse[1]];this.last_mouse=b;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;a.dragging=this.last_mouse_dragging;this.node_widget&&(this.processNodeWidgets(this.node_widget[0],this.canvas_mouse,a,this.node_widget[1]),this.dirty_canvas=!0);if(this.dragging_rectangle)this.dragging_rectangle[2]=a.canvasX-this.dragging_rectangle[0],this.dragging_rectangle[3]=a.canvasY-this.dragging_rectangle[1],this.dirty_canvas=!0;else if(this.selected_group&& -!this.read_only)this.selected_group_resizing?this.selected_group.size=[a.canvasX-this.selected_group.pos[0],a.canvasY-this.selected_group.pos[1]]:(this.selected_group.move(d[0]/this.ds.scale,d[1]/this.ds.scale,a.ctrlKey),this.selected_group._nodes.length&&(this.dirty_canvas=!0)),this.dirty_bgcanvas=!0;else if(this.dragging_canvas)this.ds.offset[0]+=d[0]/this.ds.scale,this.ds.offset[1]+=d[1]/this.ds.scale,this.dirty_bgcanvas=this.dirty_canvas=!0;else if(this.allow_interaction&&!this.read_only){this.connecting_node&& -(this.dirty_canvas=!0);for(var g=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes),b=0,f=this.graph._nodes.length;bs[0]+4||a.canvasYs[1]+4)){f=r;break}}f!=this.over_link_center&&(this.over_link_center=f,this.dirty_canvas=!0);this.canvas&&(this.canvas.style.cursor="")}if(this.node_capturing_input&&this.node_capturing_input!=g&&this.node_capturing_input.onMouseMove)this.node_capturing_input.onMouseMove(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]], -this);if(this.node_dragged&&!this.live_mode){for(b in this.selected_nodes)g=this.selected_nodes[b],g.pos[0]+=d[0]/this.ds.scale,g.pos[1]+=d[1]/this.ds.scale;this.dirty_bgcanvas=this.dirty_canvas=!0}if(this.resizing_node&&!this.live_mode){this.resizing_node.size[0]=a.canvasX-this.resizing_node.pos[0];this.resizing_node.size[1]=a.canvasY-this.resizing_node.pos[1];d=Math.max(this.resizing_node.inputs?this.resizing_node.inputs.length:0,this.resizing_node.outputs?this.resizing_node.outputs.length:0)*h.NODE_SLOT_HEIGHT+ -(this.resizing_node.widgets?this.resizing_node.widgets.length:0)*(h.NODE_WIDGET_HEIGHT+4)+4;this.resizing_node.size[1]this.dragging_rectangle[3]?this.dragging_rectangle[1]-f:this.dragging_rectangle[1];this.dragging_rectangle[0]=0>this.dragging_rectangle[2]?this.dragging_rectangle[0]-g:this.dragging_rectangle[0];this.dragging_rectangle[1]=r;this.dragging_rectangle[2]=g;this.dragging_rectangle[3]=f;f=[];for(r=0;ra.click_time&&w(a.canvasX,a.canvasY,g.pos[0],g.pos[1]-h.NODE_TITLE_HEIGHT,h.NODE_TITLE_HEIGHT,h.NODE_TITLE_HEIGHT)&&g.collapse();this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_dragged.pos[0]=Math.round(this.node_dragged.pos[0]); -this.node_dragged.pos[1]=Math.round(this.node_dragged.pos[1]);this.graph.config.align_to_grid&&this.node_dragged.alignToGrid();if(this.onNodeMoved)this.onNodeMoved(this.node_dragged);this.node_dragged=null}else{g=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes);!g&&300>a.click_time&&this.deselectAllNodes();this.dirty_canvas=!0;this.dragging_canvas=!1;if(this.node_over&&this.node_over.onMouseUp)this.node_over.onMouseUp(a,[a.canvasX-this.node_over.pos[0],a.canvasY-this.node_over.pos[1]], -this);if(this.node_capturing_input&&this.node_capturing_input.onMouseUp)this.node_capturing_input.onMouseUp(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]])}}else 2==a.which?(this.dirty_canvas=!0,this.dragging_canvas=!1):3==a.which&&(this.dirty_canvas=!0,this.dragging_canvas=!1);this.graph.change();a.stopPropagation();a.preventDefault();return!1}};e.prototype.processMouseWheel=function(a){if(this.graph&&this.allow_dragcanvas){var b=null!=a.wheelDeltaY?a.wheelDeltaY: --60*a.detail;this.adjustMouseEvent(a);var d=this.ds.scale;0b&&(d*=1/1.1);this.ds.changeScale(d,[a.localX,a.localY]);this.graph.change();a.preventDefault();return!1}};e.prototype.isOverNodeBox=function(a,b,d){var g=h.NODE_TITLE_HEIGHT;return w(b,d,a.pos[0]+2,a.pos[1]+2-g,g-4,g-4)?!0:!1};e.prototype.isOverNodeInput=function(a,b,d,g){if(a.inputs)for(var f=0,h=a.inputs.length;fd-this.graph._last_trigger_time)&&this.drawBackCanvas();(this.dirty_canvas||a)&&this.drawFrontCanvas();this.fps=this.render_time?1/this.render_time:0;this.frame+=1}};e.prototype.drawFrontCanvas=function(){this.dirty_canvas=!1;this.ctx||(this.ctx=this.bgcanvas.getContext("2d"));var a=this.ctx;if(a){a.start2D&&a.start2D();var b=this.canvas;a.restore();a.setTransform(1,0,0,1,0,0);this.dirty_area&&(a.save(), -a.beginPath(),a.rect(this.dirty_area[0],this.dirty_area[1],this.dirty_area[2],this.dirty_area[3]),a.clip());this.clear_background&&a.clearRect(0,0,b.width,b.height);this.bgcanvas==this.canvas?this.drawBackCanvas():a.drawImage(this.bgcanvas,0,0);if(this.onRender)this.onRender(b,a);this.show_info&&this.renderInfo(a);if(this.graph){a.save();this.ds.toCanvasContext(a);for(var b=this.computeVisibleNodes(null,this.visible_nodes),d=0;d> ";b.fillText(g+d.getTitle(), -0.5*a.width,40);b.restore()}d=!1;this.onRenderBackground&&(d=this.onRenderBackground(a,b));b.restore();b.setTransform(1,0,0,1,0,0);this.visible_links.length=0;if(this.graph){b.save();this.ds.toCanvasContext(b);if(this.background_image&&0.5this.ds.scale;if(this.live_mode){if(!a.flags.collapsed&&(b.shadowColor="transparent",a.onDrawForeground))a.onDrawForeground(b,this,this.canvas)}else{var r= -this.editor_alpha;b.globalAlpha=r;this.render_shadows&&!f?(b.shadowColor=h.DEFAULT_SHADOW_COLOR,b.shadowOffsetX=2*this.ds.scale,b.shadowOffsetY=2*this.ds.scale,b.shadowBlur=3*this.ds.scale):b.shadowColor="transparent";if(!a.flags.collapsed||!a.onDrawCollapsed||!0!=a.onDrawCollapsed(b,this)){var s=a._shape||h.BOX_SHAPE;y.set(a.size);var c=a.horizontal;if(a.flags.collapsed){b.font=this.inner_text_font;var k=a.getTitle?a.getTitle():a.title;null!=k&&(a._collapsed_width=Math.min(a.size[0],b.measureText(k).width+ -2*h.NODE_TITLE_HEIGHT),y[0]=a._collapsed_width,y[1]=0)}a.clip_area&&(b.save(),b.beginPath(),s==h.BOX_SHAPE?b.rect(0,0,y[0],y[1]):s==h.ROUND_SHAPE?b.roundRect(0,0,y[0],y[1],10):s==h.CIRCLE_SHAPE&&b.arc(0.5*y[0],0.5*y[1],0.5*y[0],0,2*Math.PI),b.clip());a.has_errors&&(g="red");this.drawNodeShape(a,b,y,d,g,a.is_selected,a.mouseOver);b.shadowColor="transparent";if(a.onDrawForeground)a.onDrawForeground(b,this,this.canvas);b.textAlign=c?"center":"left";b.font=this.inner_text_font;g=!f;s=this.connecting_output; -b.lineWidth=1;var k=0,e=new Float32Array(2);if(!a.flags.collapsed){if(a.inputs)for(d=0;dthis.ds.scale,k=a._shape||a.constructor.shape||h.ROUND_SHAPE,p=a.constructor.title_mode,q=!0;p==h.TRANSPARENT_TITLE?q=!1:p==h.AUTOHIDE_TITLE&& -s&&(q=!0);v[0]=0;v[1]=q?-f:0;v[2]=d[0]+1;v[3]=q?d[1]+f:d[1];s=b.globalAlpha;b.beginPath();k==h.BOX_SHAPE||c?b.fillRect(v[0],v[1],v[2],v[3]):k==h.ROUND_SHAPE||k==h.CARD_SHAPE?b.roundRect(v[0],v[1],v[2],v[3],this.round_radius,k==h.CARD_SHAPE?0:this.round_radius):k==h.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0],0,2*Math.PI);b.fill();a.flags.collapsed||(b.shadowColor="transparent",b.fillStyle="rgba(0,0,0,0.2)",b.fillRect(0,-1,v[2],2));b.shadowColor="transparent";if(a.onDrawBackground)a.onDrawBackground(b, -this,this.canvas);if(q||p==h.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,f,d,this.ds.scale,g);else if(p!=h.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){q=a.constructor.title_color||g;a.flags.collapsed&&(b.shadowColor=h.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var t=e.gradients[q];t||(t=e.gradients[q]=b.createLinearGradient(0,0,400,0),t.addColorStop(0,q),t.addColorStop(1,"#000"));b.fillStyle=t}else b.fillStyle=q;b.beginPath();k==h.BOX_SHAPE||c?b.rect(0, --f,d[0]+1,f):k!=h.ROUND_SHAPE&&k!=h.CARD_SHAPE||b.roundRect(0,-f,d[0]+1,f,this.round_radius,a.flags.collapsed?this.round_radius:0);b.fill();b.shadowColor="transparent"}if(a.onDrawTitleBox)a.onDrawTitleBox(b,f,d,this.ds.scale);else k==h.ROUND_SHAPE||k==h.CIRCLE_SHAPE||k==h.CARD_SHAPE?(c&&(b.fillStyle="black",b.beginPath(),b.arc(0.5*f,-0.5*f,6,0,2*Math.PI),b.fill()),b.fillStyle=a.boxcolor||h.NODE_DEFAULT_BOXCOLOR,c?b.fillRect(0.5*f-5,-0.5*f-5,10,10):(b.beginPath(),b.arc(0.5*f,-0.5*f,5,0,2*Math.PI), -b.fill())):(c&&(b.fillStyle="black",b.fillRect(0.5*(f-10)-1,-0.5*(f+10)-1,12,12)),b.fillStyle=a.boxcolor||h.NODE_DEFAULT_BOXCOLOR,b.fillRect(0.5*(f-10),-0.5*(f+10),10,10));b.globalAlpha=s;if(a.onDrawTitleText)a.onDrawTitleText(b,f,d,this.ds.scale,this.title_text_font,r);!c&&(b.font=this.title_text_font,c=String(a.getTitle()))&&(b.fillStyle=r?"white":a.constructor.title_text_color||this.node_title_color,a.flags.collapsed?(b.textAlign="left",b.measureText(c),b.fillText(c.substr(0,20),f,h.NODE_TITLE_TEXT_Y- -f),b.textAlign="left"):(b.textAlign="left",b.fillText(c,f,h.NODE_TITLE_TEXT_Y-f)));if(a.onDrawTitle)a.onDrawTitle(b)}if(r){if(a.onBounding)a.onBounding(v);p==h.TRANSPARENT_TITLE&&(v[1]-=f,v[3]+=f);b.lineWidth=1;b.globalAlpha=0.8;b.beginPath();k==h.BOX_SHAPE?b.rect(-6+v[0],-6+v[1],12+v[2],12+v[3]):k==h.ROUND_SHAPE||k==h.CARD_SHAPE&&a.flags.collapsed?b.roundRect(-6+v[0],-6+v[1],12+v[2],12+v[3],2*this.round_radius):k==h.CARD_SHAPE?b.roundRect(-6+v[0],-6+v[1],12+v[2],12+v[3],2*this.round_radius,2):k== -h.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0]+6,0,2*Math.PI);b.strokeStyle="#FFF";b.stroke();b.strokeStyle=g;b.globalAlpha=1}};var G=new Float32Array(4),p=new Float32Array(4),q=new Float32Array(2),k=new Float32Array(2);e.prototype.drawConnections=function(a){var b=h.getTime(),d=this.visible_area;G[0]=d[0]-20;G[1]=d[1]-20;G[2]=d[2]+40;G[3]=d[3]+40;a.lineWidth=this.connections_width;a.fillStyle="#AAA";a.strokeStyle="#AAA";a.globalAlpha=this.editor_alpha;for(var d=this.graph._nodes,g=0,f=d.length;g< -f;++g){var r=d[g];if(r.inputs&&r.inputs.length)for(var s=0;sp[2]&&(p[0]+=p[2],p[2]=Math.abs(p[2]));0>p[3]&&(p[1]+=p[3],p[3]=Math.abs(p[3]));if(u(p,G)){var n=e.outputs[z],z=r.inputs[s];if(n&& -z&&(e=n.dir||(e.horizontal?h.DOWN:h.RIGHT),z=z.dir||(r.horizontal?h.UP:h.LEFT),this.renderLink(a,l,t,c,!1,0,null,e,z),c&&c._last_time&&1E3>b-c._last_time)){var n=2-0.002*(b-c._last_time),K=a.globalAlpha;a.globalAlpha=K*n;this.renderLink(a,l,t,c,!0,n,"white",e,z);a.globalAlpha=K}}}}}}a.globalAlpha=1};e.prototype.renderLink=function(a,b,d,g,f,r,c,k,p,q){g&&this.visible_links.push(g);!c&&g&&(c=g.color||e.link_type_colors[g.type]);c||(c=this.default_link_color);null!=g&&this.highlighted_links[g.id]&& -(c="#FFF");k=k||h.RIGHT;p=p||h.LEFT;var z=C(b,d);this.render_connections_border&&0.6b[1]?0:Math.PI,a.save(),a.translate(t[0],t[1]),a.rotate(l),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore(),a.save(),a.translate(q[0],q[1]),a.rotate(n), -a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill());if(r)for(a.fillStyle=c,t=0;5>t;++t)r=(0.001*h.getTime()+0.2*t)%1,f=this.computeConnectionPoint(b,d,r,k,p),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill()};e.prototype.computeConnectionPoint=function(a,b,d,g,f){g=g||h.RIGHT;f=f||h.LEFT;var r=C(a,b),c=[a[0],a[1]],k=[b[0],b[1]];switch(g){case h.LEFT:c[0]+=-0.25*r;break;case h.RIGHT:c[0]+=0.25*r;break;case h.UP:c[1]+= --0.25*r;break;case h.DOWN:c[1]+=0.25*r}switch(f){case h.LEFT:k[0]+=-0.25*r;break;case h.RIGHT:k[0]+=0.25*r;break;case h.UP:k[1]+=-0.25*r;break;case h.DOWN:k[1]+=0.25*r}g=(1-d)*(1-d)*(1-d);f=3*(1-d)*(1-d)*d;r=3*(1-d)*d*d;d*=d*d;return[g*a[0]+f*c[0]+r*k[0]+d*b[0],g*a[1]+f*c[1]+r*k[1]+d*b[1]]};e.prototype.drawExecutionOrder=function(a){a.shadowColor="transparent";a.globalAlpha=0.25;a.textAlign="center";a.strokeStyle="white";a.globalAlpha=0.75;for(var b=this.visible_nodes,d=0;dt.last_y&&s -t.options.max&&(t.value=t.options.max);else if("mousedown"==d.type){var l=t.options.values;l&&l.constructor===Function&&(l=t.options.values(t,a));var z=null;"number"!=t.type&&(z=l.constructor===Array?l:Object.keys(l));c=40>c?-1:c>k-40?1:0;if("number"==t.type)t.value+=0.1*c*(t.options.step||1),null!=t.options.min&&t.valuet.options.max&&(t.value=t.options.max);else if(c)e=-1,e=l.constructor===Object?z.indexOf(String(t.value))+c:z.indexOf(t.value)+ -c,e>=z.length&&(e=z.length-1),0>e&&(e=0),t.value=l.constructor===Array?l[e]:e;else{var n=l!=z?Object.values(l):l;new h.ContextMenu(n,{scale:Math.max(1,this.ds.scale),event:d,className:"dark",callback:y.bind(t)},e);var y=function(a,b,d){l!=z&&(a=n.indexOf(a));this.value=a;f(this,a);p.dirty_canvas=!0;return!1}}}else"mouseup"==d.type&&"number"==t.type&&(c=40>c?-1:c>k-40?1:0,200>d.click_time&&0==c&&this.prompt("Value",t.value,function(a){this.value=Number(a);f(this,this.value)}.bind(t),d));g!=t.value&& -setTimeout(function(){f(this,this.value)}.bind(t),20);this.dirty_canvas=!0;break;case "toggle":"mousedown"==d.type&&(t.value=!t.value,setTimeout(function(){f(t,t.value)},20));break;case "string":case "text":"mousedown"==d.type&&this.prompt("Value",t.value,function(a){this.value=a;f(this,a)}.bind(t),d);break;default:t.mouse&&t.mouse(ctx,d,[c,s],a)}return t}}return null};e.prototype.drawGroups=function(a,b){if(this.graph){var d=this.graph._groups;b.save();b.globalAlpha=0.5*this.editor_alpha;for(var g= -0;gd&&0.01>b.editor_alpha&&(clearInterval(g),1>d&&(b.live_mode=!0));1"+ -q+""+a+"",value:q});if(p.length)return new h.ContextMenu(p,{event:d,callback:c,parentMenu:g,allow_html:!0,node:f},b),!1}};e.decodeHTML=function(a){var b=document.createElement("div");b.innerText=a;return b.innerHTML};e.onResizeNode=function(a,b,d,g,f){f&&(f.size=f.computeSize(),f.setDirtyCanvas(!0,!0))};e.prototype.showLinkMenu=function(a,b){var d=this;console.log(a);var g=new h.ContextMenu(["Add Node",null,"Delete"],{event:b,title:null!=a.data?a.data.constructor.name: -null,callback:function(b,h,c){switch(b){case "Add Node":e.onMenuAdd(null,null,c,g,function(b){console.log("node autoconnect");var f=d.graph.getNodeById(a.origin_id),g=d.graph.getNodeById(a.target_id);b.inputs&&b.inputs.length&&b.outputs&&b.outputs.length&&f.outputs[a.origin_slot].type==b.inputs[0].type&&b.outputs[0].type==g.inputs[0].type&&(f.connect(a.origin_slot,b,0),b.connect(0,g,a.target_slot),b.pos[0]-=0.5*b.size[0])});break;case "Delete":d.graph.removeLink(a.id)}}});return!1};e.onShowPropertyEditor= -function(a,b,d,g,f){function h(){var b=p.value;"Number"==a.type?b=Number(b):"Boolean"==a.type&&(b=Boolean(b));f[c]=b;k.parentNode&&k.parentNode.removeChild(k);f.setDirtyCanvas(!0,!0)}var c=a.property||"title";b=f[c];var k=document.createElement("div");k.className="graphdialog";k.innerHTML="";k.querySelector(".name").innerText=c;var p=k.querySelector("input");p&&(p.value=b,p.addEventListener("blur",function(a){this.focus()}), -p.addEventListener("keydown",function(a){13==a.keyCode&&(h(),a.preventDefault(),a.stopPropagation())}));b=e.active_canvas.canvas;d=b.getBoundingClientRect();var q=g=-20;d&&(g-=d.left,q-=d.top);event?(k.style.left=event.clientX+g+"px",k.style.top=event.clientY+q+"px"):(k.style.left=0.5*b.width+g+"px",k.style.top=0.5*b.height+q+"px");k.querySelector("button").addEventListener("click",h);b.parentNode.appendChild(k)};e.prototype.prompt=function(a,b,d,g){var f=this;a=a||"";var h=!1,c=document.createElement("div"); -c.className="graphdialog rounded";c.innerHTML=" ";c.close=function(){f.prompt_box=null;c.parentNode&&c.parentNode.removeChild(c)};1e.search_limit)break}}s=null;if(Array.prototype.filter)s=Object.keys(h.registered_node_types).filter(l);else for(k in s=[],h.registered_node_types)l(k)&&s.push(k);for(k=0;ke.search_limit);k++);var l=function(a){var b=h.registered_node_types[a];return p&&b.filter!=p?!1:-1!==a.toLowerCase().indexOf(d)}}}var f=this,c=e.active_canvas,k=c.canvas,p=k.ownerDocument||document, -q=document.createElement("div");q.className="litegraph litesearchbox graphdialog rounded";q.innerHTML="Search
";q.close=function(){f.search_box=null;p.body.focus();p.body.style.overflow="";setTimeout(function(){f.canvas.focus()},20);q.parentNode&&q.parentNode.removeChild(q)};var l=null;1k.height-200&&(z.style.maxHeight=k.height-a.layerY-20+"px");u.focus();return q};e.prototype.showEditPropertyValue=function(a,b,d){function g(){f(t.value)} -function f(f){"number"==typeof a.properties[b]&&(f=Number(f));if("array"==h||"object"==h)f=JSON.parse(f);a.properties[b]=f;a._graph&&a._graph._version++;if(a.onPropertyChanged)a.onPropertyChanged(b,f);if(d.onclose)d.onclose();e.close();a.setDirtyCanvas(!0,!0)}if(a&&void 0!==a.properties[b]){d=d||{};var c=a.getPropertyInfo(b),h=c.type,k="";if("string"==h||"number"==h||"array"==h||"object"==h)k="";else if("enum"==h&&c.values){var k=""}else if("boolean"==h)k="";else{console.warn("unknown type: "+h);return}var e=this.createDialog(""+b+""+k+"",d);if("enum"==h&&c.values){var t=e.querySelector("select");t.addEventListener("change", -function(a){f(a.target.value)})}else if("boolean"==h)(t=e.querySelector("input"))&&t.addEventListener("click",function(a){f(!!t.checked)});else if(t=e.querySelector("input"))t.addEventListener("blur",function(a){this.focus()}),q=void 0!==a.properties[b]?a.properties[b]:"",q=JSON.stringify(q),t.value=q,t.addEventListener("keydown",function(a){13==a.keyCode&&(g(),a.preventDefault(),a.stopPropagation())});e.querySelector("button").addEventListener("click",g);return e}};e.prototype.createDialog=function(a, -b){b=b||{};var d=document.createElement("div");d.className="graphdialog";d.innerHTML=a;var g=this.canvas.getBoundingClientRect(),f=-20,c=-20;g&&(f-=g.left,c-=g.top);b.position?(f+=b.position[0],c+=b.position[1]):b.event?(f+=b.event.clientX,c+=b.event.clientY):(f+=0.5*this.canvas.width,c+=0.5*this.canvas.height);d.style.left=f+"px";d.style.top=c+"px";this.canvas.parentNode.appendChild(d);d.close=function(){this.parentNode&&this.parentNode.removeChild(this)};return d};e.onMenuNodeCollapse=function(a, -b,d,g,f){f.collapse()};e.onMenuNodePin=function(a,b,d,g,f){f.pin()};e.onMenuNodeMode=function(a,b,d,g,f){new h.ContextMenu(["Always","On Event","On Trigger","Never"],{event:d,callback:function(a){if(f)switch(a){case "On Event":f.mode=h.ON_EVENT;break;case "On Trigger":f.mode=h.ON_TRIGGER;break;case "Never":f.mode=h.NEVER;break;default:f.mode=h.ALWAYS}},parentMenu:g,node:f});return!1};e.onMenuNodeColors=function(a,b,d,g,f){if(!f)throw"no node for color";b=[];b.push({value:null,content:"No color"}); -for(var c in e.node_colors)a=e.node_colors[c],a={value:c,content:""+c+""},b.push(a);new h.ContextMenu(b,{event:d,callback:function(a){f&&((a=a.value?e.node_colors[a.value]:null)?f.constructor===h.LGraphGroup?f.color=a.groupcolor:(f.color=a.color,f.bgcolor=a.bgcolor):(delete f.color,delete f.bgcolor),f.setDirtyCanvas(!0,!0))},parentMenu:g,node:f});return!1};e.onMenuNodeShapes= -function(a,b,d,g,f){if(!f)throw"no node passed";new h.ContextMenu(h.VALID_SHAPES,{event:d,callback:function(a){f&&(f.shape=a,f.setDirtyCanvas(!0))},parentMenu:g,node:f});return!1};e.onMenuNodeRemove=function(a,b,d,g,f){if(!f)throw"no node passed";!1!==f.removable&&(f.graph.remove(f),f.setDirtyCanvas(!0,!0))};e.onMenuNodeClone=function(a,b,d,g,f){!1!=f.clonable&&(a=f.clone())&&(a.pos=[f.pos[0]+5,f.pos[1]+5],f.graph.add(a),f.setDirtyCanvas(!0,!0))};e.node_colors={red:{color:"#322",bgcolor:"#533",groupcolor:"#A88"}, -brown:{color:"#332922",bgcolor:"#593930",groupcolor:"#b06634"},green:{color:"#232",bgcolor:"#353",groupcolor:"#8A8"},blue:{color:"#223",bgcolor:"#335",groupcolor:"#88A"},pale_blue:{color:"#2a363b",bgcolor:"#3f5159",groupcolor:"#3f789e"},cyan:{color:"#233",bgcolor:"#355",groupcolor:"#8AA"},purple:{color:"#323",bgcolor:"#535",groupcolor:"#a1309b"},yellow:{color:"#432",bgcolor:"#653",groupcolor:"#b58b2a"},black:{color:"#222",bgcolor:"#000",groupcolor:"#444"}};e.prototype.getCanvasMenuOptions=function(){var a= -null;this.getMenuOptions?a=this.getMenuOptions():(a=[{content:"Add Node",has_submenu:!0,callback:e.onMenuAdd},{content:"Add Group",callback:e.onGroupAdd}],this._graph_stack&&0Name",f),k=h.querySelector("input");k&&c&&(k.value=c.label||"");h.querySelector("button").addEventListener("click",function(a){k.value&& -(c&&(c.label=k.value),d.setDirty(!0));h.close()})}},extra:a};a&&(c.title=a.type);var k=null;a&&(k=a.getSlotInPosition(b.canvasX,b.canvasY),e.active_node=a);if(k){f=[];k&&k.output&&k.output.links&&k.output.links.length&&f.push({content:"Disconnect Links",slot:k});var p=k.input||k.output;f.push(p.locked?"Cannot remove":{content:"Remove Slot",slot:k});f.push(p.nameLocked?"Cannot rename":{content:"Rename Slot",slot:k});c.title=(k.input?k.input.type:k.output.type)||"*";k.input&&k.input.type==h.ACTION&& -(c.title="Action");k.output&&k.output.type==h.EVENT&&(c.title="Event")}else a?f=this.getNodeMenuOptions(a):(f=this.getCanvasMenuOptions(),(k=this.graph.getGroupOnPos(b.canvasX,b.canvasY))&&f.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:k,options:this.getGroupMenuOptions(k)}}));f&&new h.ContextMenu(f,c,g)};"undefined"!=typeof window&&window.CanvasRenderingContext2D&&(window.CanvasRenderingContext2D.prototype.roundRect=function(a,b,d,g,f,c){void 0===f&&(f=5);void 0=== -c&&(c=f);this.moveTo(a+f,b);this.lineTo(a+d-f,b);this.quadraticCurveTo(a+d,b,a+d,b+f);this.lineTo(a+d,b+g-c);this.quadraticCurveTo(a+d,b+g,a+d-c,b+g);this.lineTo(a+c,b+g);this.quadraticCurveTo(a,b+g,a,b+g-c);this.lineTo(a,b+f);this.quadraticCurveTo(a,b,a+f,b)});h.compareObjects=function(a,b){for(var d in a)if(a[d]!=b[d])return!1;return!0};h.distance=C;h.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+","+Math.round(255*a[2]).toFixed()+","+ -(4==a.length?a[3].toFixed(2):"1.0")+")"};h.isInsideRectangle=w;h.growBounding=function(a,b,d){ba[2]&&(a[2]=b);da[3]&&(a[3]=d)};h.isInsideBounding=function(a,b){return a[0]b[1][0]||a[1]>b[1][1]?!1:!0};h.overlapBounding=u;h.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),d=0,g,f,c=0;6>c;c+=2)g="0123456789ABCDEF".indexOf(a.charAt(c)),f="0123456789ABCDEF".indexOf(a.charAt(c+1)),b[d]=16*g+f,d++;return b}; -h.num2hex=function(a){for(var b="#",d,g,f=0;3>f;f++)d=a[f]/16,g=a[f]%16,b+="0123456789ABCDEF".charAt(d)+"0123456789ABCDEF".charAt(g);return b};D.prototype.addItem=function(a,b,d){function g(a){var b=this.value;b&&b.has_submenu&&f.call(this,a)}function f(a){var b=this.value,f=!0;c.current_submenu&&c.current_submenu.close(a);if(d.callback){var g=d.callback.call(this,b,d,a,c,d.node);!0===g&&(f=!1)}if(b&&(b.callback&&!d.ignore_item_callbacks&&!0!==b.disabled&&(g=b.callback.call(this,b,d,a,c,d.extra), -!0===g&&(f=!1)),b.submenu)){if(!b.submenu.options)throw"ContextMenu submenu needs options";new c.constructor(b.submenu.options,{callback:b.submenu.callback,event:a,parentMenu:c,ignore_item_callbacks:b.submenu.ignore_item_callbacks,title:b.submenu.title,extra:b.submenu.extra,autoopen:d.autoopen});f=!1}f&&!c.lock&&c.close()}var c=this;d=d||{};var h=document.createElement("div");h.className="litemenu-entry submenu";var k=!1;if(null===b)h.classList.add("separator");else{h.innerHTML=b&&b.title?b.title: -a;if(h.value=b)b.disabled&&(k=!0,h.classList.add("disabled")),(b.submenu||b.has_submenu)&&h.classList.add("has_submenu");"function"==typeof b?(h.dataset.value=a,h.onclick_callback=b):h.dataset.value=b;b.className&&(h.className+=" "+b.className)}this.root.appendChild(h);k||h.addEventListener("click",f);d.autoopen&&h.addEventListener("mouseenter",g);return h};D.prototype.close=function(a,b){this.root.parentNode&&this.root.parentNode.removeChild(this.root);this.parentMenu&&!b&&(this.parentMenu.lock= -!1,this.parentMenu.current_submenu=null,void 0===a?this.parentMenu.close():a&&!D.isCursorOverElement(a,this.parentMenu.root)&&D.trigger(this.parentMenu.root,"mouseleave",a));this.current_submenu&&this.current_submenu.close(a,!0);this.root.closing_timer&&clearTimeout(this.root.closing_timer)};D.trigger=function(a,b,d,g){var f=document.createEvent("CustomEvent");f.initCustomEvent(b,!0,!0,d);f.srcElement=g;a.dispatchEvent?a.dispatchEvent(f):a.__events&&a.__events.dispatchEvent(f);return f};D.prototype.getTopMenu= -function(){return this.options.parentMenu?this.options.parentMenu.getTopMenu():this};D.prototype.getFirstEvent=function(){return this.options.parentMenu?this.options.parentMenu.getFirstEvent():this.options.event};D.isCursorOverElement=function(a,b){var d=a.clientX,g=a.clientY,f=b.getBoundingClientRect();return f?g>f.top&&gf.left&&dMath.abs(d))return g[1];d=(a-g[0])/d;return g[1]*(1-d)+f[1]*d}}return 0}};A.prototype.draw=function(a,b,d,g,f,c){if(d=this.points){this.size=b;var h=b[0]-2*this.margin;b=b[1]-2*this.margin;f=f||"#666";a.save();a.translate(this.margin,this.margin);g&&(a.fillStyle="#111",a.fillRect(0,0,h,b),a.fillStyle="#222",a.fillRect(0.5*h,0,1,b),a.strokeStyle="#333",a.strokeRect(0,0,h,b));a.strokeStyle= -f;c&&(a.globalAlpha=0.5);a.beginPath();for(g=0;ga[1])){var g=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=a[0]-this.margin,h=a[1]-this.margin;this.selected=this.getCloserPoint([c, -h],30/b.ds.scale);-1==this.selected&&(g=[c/g,1-h/f],d.push(g),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(g),this.must_update=!0);if(-1!=this.selected)return!0}};A.prototype.onMouseMove=function(a,b){var d=this.points;if(d){var g=this.selected;if(!(0>g)){var f=(a[0]-this.margin)/(this.size[0]-2*this.margin),c=(a[1]-this.margin)/(this.size[1]-2*this.margin);this._nearest=this.getCloserPoint([a[0]-this.margin,a[1]-this.margin],30/b.ds.scale);var h=d[g];if(h){var k=0==g||g==d.length- -1;!k&&(-10>a[0]||a[0]>this.size[0]+10||-10>a[1]||a[1]>this.size[1]+10)?(d.splice(g,1),this.selected=-1):(h[0]=k?0==g?0:1:Math.clamp(f,0,1),h[1]=1-Math.clamp(c,0,1),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(h),this.must_update=!0)}}}};A.prototype.onMouseUp=function(a,b){this.selected=-1;return!1};A.prototype.getCloserPoint=function(a,b){var d=this.points;if(!d)return-1;b=b||30;for(var g=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=d.length,h=[0,0],k=1E6,p=-1,q=0;q< -c;++q){var e=d[q];h[0]=e[0]*g;h[1]=(1-e[1])*f;e=vec2.distance(a,h);e>k||e>b||(p=q,k=e)}return p};h.CurveEditor=A;h.getParameterNames=function(a){return(a+"").replace(/[/][/].*$/gm,"").replace(/\s+/g,"").replace(/[/][*][^/*]*[*][/]/g,"").split("){",1)[0].replace(/^[^(]*[(]/,"").replace(/=[^,]+/g,"").split(",").filter(Boolean)};Math.clamp=function(a,b,d){return b>a?b:dthis.size[0]-g.NODE_TITLE_HEIGHT&&0>b[1]){var c=this;setTimeout(function(){d.openSubgraph(c.subgraph)},10)}};m.prototype.onAction=function(a,b){this.subgraph.onAction(a,b)};m.prototype.onExecute=function(){if(this.enabled= -this.getInputOrProperty("enabled")){if(this.inputs)for(var a=0;a=h?this.trigger(null,e):this._pending.push([h,e])};e.prototype.onExecute=function(){var c=1E3* -this.graph.elapsed_time;this.isInputConnected(1)&&(this.properties.time_in_ms=this.getInputData(1));for(var e=0;ee[1]))return this.old_y=c.canvasY,this.captureInput(!0),this.mouse_captured=!0};n.prototype.onMouseMove=function(c){if(this.mouse_captured){var e=this.old_y-c.canvasY;c.shiftKey&&(e*=10);if(c.metaKey||c.altKey)e*=0.1;this.old_y=c.canvasY;c=this._remainder+e/n.pixels_threshold;this._remainder=c%1;c=Math.clamp(this.properties.value+ -(c|0)*this.properties.step,this.properties.min,this.properties.max);this.properties.value=c;this.graph._version++;this.setDirtyCanvas(!0)}};n.prototype.onMouseUp=function(c,e){200>c.click_time&&(this.properties.value=Math.clamp(this.properties.value+(e[1]>0.5*this.size[1]?-1:1)*this.properties.step,this.properties.min,this.properties.max),this.graph._version++,this.setDirtyCanvas(!0));this.mouse_captured&&(this.mouse_captured=!1,this.captureInput(!1))};A.registerNodeType("widget/number",n);l.title= -"Combo";l.desc="Widget to select from a list";l.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};l.prototype.onPropertyChanged=function(c,e){"values"==c?(this._values=e.split(";"),this.widget.options.values=this._values):"value"==c&&(this.widget.value=e)};A.registerNodeType("widget/combo",l);x.title="Knob";x.desc="Circular controller";x.size=[80,100];x.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/ -(this.properties.max-this.properties.min));var e=0.5*this.size[0],l=0.5*this.size[1],n=0.5*Math.min(this.size[0],this.size[1])-5;c.globalAlpha=1;c.save();c.translate(e,l);c.rotate(0.75*Math.PI);c.fillStyle="rgba(0,0,0,0.5)";c.beginPath();c.moveTo(0,0);c.arc(0,0,n,0,1.5*Math.PI);c.fill();c.strokeStyle="black";c.fillStyle=this.properties.color;c.lineWidth=2;c.beginPath();c.moveTo(0,0);c.arc(0,0,n-4,0,1.5*Math.PI*Math.max(0.01,this.value));c.closePath();c.fill();c.lineWidth=1;c.globalAlpha=1;c.restore(); -c.fillStyle="black";c.beginPath();c.arc(e,l,0.75*n,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":this.properties.color;c.beginPath();var u=this.value*Math.PI*1.5+0.75*Math.PI;c.arc(e+Math.cos(u)*n*0.65,l+Math.sin(u)*n*0.65,0.05*n,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":"#AAA";c.font=Math.floor(0.5*n)+"px Arial";c.textAlign="center";c.fillText(this.properties.value.toFixed(this.properties.precision),e,l+0.15*n)}};x.prototype.onExecute=function(){this.setOutputData(0, -this.properties.value);this.boxcolor=A.colorToString([this.value,this.value,this.value])};x.prototype.onMouseDown=function(c){this.center=[0.5*this.size[0],0.5*this.size[1]+20];this.radius=0.5*this.size[0];if(20>c.canvasY-this.pos[1]||A.distance([c.canvasX,c.canvasY],[this.pos[0]+this.center[0],this.pos[1]+this.center[1]])>this.radius)return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];this.captureInput(!0);return!0};x.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX- -this.pos[0],c.canvasY-this.pos[1]];var e=this.value,e=e-0.01*(c[1]-this.oldmouse[1]);1e&&(e=0);this.value=e;this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.oldmouse=c;this.setDirtyCanvas(!0)}};x.prototype.onMouseUp=function(c){this.oldmouse&&(this.oldmouse=null,this.captureInput(!1))};x.prototype.onPropertyChanged=function(c,e){if("min"==c||"max"==c||"value"==c)return this.properties[c]=parseFloat(e),!0};A.registerNodeType("widget/knob", -x);e.title="Inner Slider";e.prototype.onPropertyChanged=function(c,e){"value"==c&&(this.slider.value=e)};e.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};A.registerNodeType("widget/internal_slider",e);C.title="H.Slider";C.desc="Linear slider controller";C.prototype.onDrawForeground=function(c){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min));c.globalAlpha=1;c.lineWidth=1;c.fillStyle="#000";c.fillRect(2, -2,this.size[0]-4,this.size[1]-4);c.fillStyle=this.properties.color;c.beginPath();c.rect(4,4,(this.size[0]-8)*this.value,this.size[1]-8);c.fill()};C.prototype.onExecute=function(){this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.setOutputData(0,this.properties.value);this.boxcolor=A.colorToString([this.value,this.value,this.value])};C.prototype.onMouseDown=function(c){if(0>c.canvasY-this.pos[1])return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY- -this.pos[1]];this.captureInput(!0);return!0};C.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];var e=this.value,e=e+(c[0]-this.oldmouse[0])/this.size[0];1e&&(e=0);this.value=e;this.oldmouse=c;this.setDirtyCanvas(!0)}};C.prototype.onMouseUp=function(c){this.oldmouse=null;this.captureInput(!1)};C.prototype.onMouseLeave=function(c){};A.registerNodeType("widget/hslider",C);w.title="Progress";w.desc="Shows data in linear progress";w.prototype.onExecute= -function(){var c=this.getInputData(0);void 0!=c&&(this.properties.value=c)};w.prototype.onDrawForeground=function(c){c.lineWidth=1;c.fillStyle=this.properties.color;var e=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min),e=Math.min(1,e),e=Math.max(0,e);c.fillRect(2,2,(this.size[0]-4)*e,this.size[1]-4)};A.registerNodeType("widget/progress",w);u.title="Text";u.desc="Shows the input value";u.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"led_text", -text:"LED",type:"minibutton"},{name:"normal_text",text:"Normal",type:"minibutton"}];u.prototype.onDrawForeground=function(c){c.fillStyle=this.properties.color;var e=this.properties.value;this.properties.glowSize?(c.shadowColor=this.properties.color,c.shadowOffsetX=0,c.shadowOffsetY=0,c.shadowBlur=this.properties.glowSize):c.shadowColor="transparent";var l=this.properties.fontsize;c.textAlign=this.properties.align;c.font=l.toString()+"px "+this.properties.font;this.str="number"==typeof e?e.toFixed(this.properties.decimals): -e;if("string"==typeof this.str){var e=this.str.split("\\n"),n;for(n in e)c.fillText(e[n],"left"==this.properties.align?15:this.size[0]-15,-0.15*l+l*(parseInt(n)+1))}c.shadowColor="transparent";this.last_ctx=c;c.textAlign="left"};u.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(this.properties.value=c)};u.prototype.resize=function(){if(this.last_ctx){var c=this.str.split("\\n");this.last_ctx.font=this.properties.fontsize+"px "+this.properties.font;var e=0,l;for(l in c){var n=this.last_ctx.measureText(c[l]).width; -el?n.xbox.axes.lx:0,this._left_axis[1]=Math.abs(n.xbox.axes.ly)>l?n.xbox.axes.ly:0,this._right_axis[0]=Math.abs(n.xbox.axes.rx)>l?n.xbox.axes.rx:0,this._right_axis[1]=Math.abs(n.xbox.axes.ry)>l?n.xbox.axes.ry:0,this._triggers[0]=Math.abs(n.xbox.axes.ltrigger)>l?n.xbox.axes.ltrigger: -0,this._triggers[1]=Math.abs(n.xbox.axes.rtrigger)>l?n.xbox.axes.rtrigger:0);if(this.outputs)for(l=0;ln;n++)if(l[n]){n=l[n];l=this.xbox_mapping;l||(l=this.xbox_mapping={axes:[], -buttons:{},hat:"",hatmap:c.CENTER});l.axes.lx=n.axes[0];l.axes.ly=n.axes[1];l.axes.rx=n.axes[2];l.axes.ry=n.axes[3];l.axes.ltrigger=n.buttons[6].value;l.axes.rtrigger=n.buttons[7].value;l.hat="";l.hatmap=c.CENTER;for(var m=0;mm)l.buttons[c.mapping_array[m]]=n.buttons[m].pressed,n.buttons[m].was_pressed&&this.trigger(c.mapping_array[m]+"_button_event");else switch(m){case 12:n.buttons[m].pressed&&(l.hat+="up",l.hatmap|=c.UP); -break;case 13:n.buttons[m].pressed&&(l.hat+="down",l.hatmap|=c.DOWN);break;case 14:n.buttons[m].pressed&&(l.hat+="left",l.hatmap|=c.LEFT);break;case 15:n.buttons[m].pressed&&(l.hat+="right",l.hatmap|=c.RIGHT);break;case 16:l.buttons.home=n.buttons[m].pressed}n.xbox=l;return n}};c.prototype.onDrawBackground=function(c){if(!this.flags.collapsed){var l=this._left_axis,m=this._right_axis;c.strokeStyle="#88A";c.strokeRect(0.5*(l[0]+1)*this.size[0]-4,0.5*(l[1]+1)*this.size[1]-4,8,8);c.strokeStyle="#8A8"; -c.strokeRect(0.5*(m[0]+1)*this.size[0]-4,0.5*(m[1]+1)*this.size[1]-4,8,8);l=this.size[1]/this._current_buttons.length;c.fillStyle="#AEB";for(m=0;m","enum",{values:a.values});this.size=[80,60]}function b(){this.addInput("inc","number");this.addOutput("total","number");this.addProperty("increment",1);this.addProperty("value",0)}function d(){this.addInput("v","number");this.addOutput("sin","number");this.addProperty("amplitude", -1);this.addProperty("offset",0);this.bgImageUrl="nodes/imgs/icon-sin.png"}function g(){this.addInput("x","number");this.addInput("y","number");this.addOutput("","number");this.properties={x:1,y:1,formula:"x+y"};this.code_widget=this.addWidget("text","F(x,y)",this.properties.formula,function(a,b,d){d.properties.formula=a});this.addWidget("toggle","allow",E.allow_scripts,function(a){E.allow_scripts=a});this._func=null}function f(){this.addInput("vec2","vec2");this.addOutput("x","number");this.addOutput("y", -"number")}function r(){this.addInputs([["x","number"],["y","number"]]);this.addOutput("vec2","vec2");this.properties={x:0,y:0};this._data=new Float32Array(2)}function s(){this.addInput("vec3","vec3");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number")}function I(){this.addInputs([["x","number"],["y","number"],["z","number"]]);this.addOutput("vec3","vec3");this.properties={x:0,y:0,z:0};this._data=new Float32Array(3)}function J(){this.addInput("vec4","vec4");this.addOutput("x", -"number");this.addOutput("y","number");this.addOutput("z","number");this.addOutput("w","number")}function H(){this.addInputs([["x","number"],["y","number"],["z","number"],["w","number"]]);this.addOutput("vec4","vec4");this.properties={x:0,y:0,z:0,w:0};this._data=new Float32Array(4)}var E=B.LiteGraph;c.title="Converter";c.desc="type A to type B";c.prototype.onExecute=function(){var a=this.getInputData(0);if(null!=a&&this.outputs)for(var b=0;ba&&(a+=1024);var c=Math.floor(a);a-=c;d=e.data[c];c=e.data[1023==c?0:c+1]; -b&&(a=a*a*a*(a*(6*a-15)+10));return d*(1-a)+c*a};e.prototype.onExecute=function(){var a=this.getInputData(0)||0,a=e.getValue(a,this.properties.smooth),b=this.properties.min;this._last_v=a*(this.properties.max-b)+b;this.setOutputData(0,this._last_v)};e.prototype.onDrawBackground=function(a){this.outputs[0].label=(this._last_v||0).toFixed(3)};E.registerNodeType("math/noise",e);C.title="Spikes";C.desc="spike every random time";C.prototype.onExecute=function(){var a=this.graph.elapsed_time;this._remaining_time-= -a;this._blink_time-=a;a=0;0this._remaining_time?(this._remaining_time=Math.random()*(this.properties.max_time-this.properties.min_time)+this.properties.min_time,this._blink_time=this.properties.duration,this.boxcolor="#FFF"):this.boxcolor="#000";this.setOutputData(0,a)};E.registerNodeType("math/spikes",C);w.title="Clamp";w.desc="Clamp number between min and max";w.prototype.onExecute=function(){var a=this.getInputData(0); -null!=a&&(a=Math.max(this.properties.min,a),a=Math.min(this.properties.max,a),this.setOutputData(0,a))};w.prototype.getCode=function(a){a="";this.isInputConnected(0)&&(a+="clamp({{0}},"+this.properties.min+","+this.properties.max+")");return a};E.registerNodeType("math/clamp",w);u.title="Lerp";u.desc="Linear Interpolation";u.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.getInputData(1);null==b&&(b=0);var d=this.properties.f,c=this.getInputData(2);void 0!==c&& -(d=c);this.setOutputData(0,a*(1-d)+b*d)};u.prototype.onGetInputs=function(){return[["f","number"]]};E.registerNodeType("math/lerp",u);D.title="Abs";D.desc="Absolute";D.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.abs(a))};E.registerNodeType("math/abs",D);A.title="Floor";A.desc="Floor number to remove fractional part";A.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.floor(a))};E.registerNodeType("math/floor", -A);h.title="Frac";h.desc="Returns fractional part";h.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a%1)};E.registerNodeType("math/frac",h);z.title="Smoothstep";z.desc="Smoothstep";z.prototype.onExecute=function(){var a=this.getInputData(0);if(void 0!==a){var b=this.properties.A,a=Math.clamp((a-b)/(this.properties.B-b),0,1);this.setOutputData(0,a*a*(3-2*a))}};E.registerNodeType("math/smoothstep",z);y.title="Scale";y.desc="v * factor";y.prototype.onExecute= -function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a*this.properties.factor)};E.registerNodeType("math/scale",y);v.title="Gate";v.desc="if v is true, then outputs A, otherwise B";v.prototype.onExecute=function(){var a=this.getInputData(0);this.setOutputData(0,this.getInputData(a?1:2))};E.registerNodeType("math/gate",v);G.title="Average";G.desc="Average Filter";G.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this._values.length;this._values[this._current% -b]=a;this._current+=1;this._current>b&&(this._current=0);for(var d=a=0;db&&(b=1);this.properties.samples=Math.round(b);var d=this._values;this._values=new Float32Array(this.properties.samples);d.length<=this._values.length?this._values.set(d):this._values.set(d.subarray(0,this._values.length))};E.registerNodeType("math/average",G);p.title="TendTo";p.desc="moves the output value always closer to the input"; -p.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.factor;this._value=null==this._value?a:this._value*(1-b)+a*b;this.setOutputData(0,this._value)};E.registerNodeType("math/tendTo",p);q.values="+ - * / % ^ max min".split(" ");q.title="Operation";q.desc="Easy math operators";q["@OP"]={type:"enum",title:"operation",values:q.values};q.size=[100,60];q.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+ -"(A,B)":"A "+this.properties.OP+" B"};q.prototype.setValue=function(a){"string"==typeof a&&(a=parseFloat(a));this.properties.value=a};q.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);null!=a?this.properties.A=a:a=this.properties.A;null!=b?this.properties.B=b:b=this.properties.B;var d=0;switch(this.properties.OP){case "+":d=a+b;break;case "-":d=a-b;break;case "x":case "X":case "*":d=a*b;break;case "/":d=a/b;break;case "%":d=a%b;break;case "^":d=Math.pow(a,b);break; -case "max":d=Math.max(a,b);break;case "min":d=Math.min(a,b);break;default:console.warn("Unknown operation: "+this.properties.OP)}this.setOutputData(0,d)};q.prototype.onDrawBackground=function(a){this.flags.collapsed||(a.font="40px Arial",a.fillStyle="#666",a.textAlign="center",a.fillText(this.properties.OP,0.5*this.size[0],0.5*(this.size[1]+E.NODE_TITLE_HEIGHT)),a.textAlign="left")};E.registerNodeType("math/operation",q);E.registerSearchboxExtra("math/operation","MAX",{properties:{OP:"max"},title:"MAX()"}); -E.registerSearchboxExtra("math/operation","MIN",{properties:{OP:"min"},title:"MIN()"});k.title="Compare";k.desc="compares between two values";k.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);void 0!==a?this.properties.A=a:a=this.properties.A;void 0!==b?this.properties.B=b:b=this.properties.B;for(var d=0,c=this.outputs.length;dB":f= -a>b;break;case "A=B":f=a>=b}this.setOutputData(d,f)}}};k.prototype.onGetOutputs=function(){return[["A==B","boolean"],["A!=B","boolean"],["A>B","boolean"],["A=B","boolean"],["A<=B","boolean"]]};E.registerNodeType("math/compare",k);E.registerSearchboxExtra("math/compare","==",{outputs:[["A==B","boolean"]],title:"A==B"});E.registerSearchboxExtra("math/compare","!=",{outputs:[["A!=B","boolean"]],title:"A!=B"});E.registerSearchboxExtra("math/compare", -">",{outputs:[["A>B","boolean"]],title:"A>B"});E.registerSearchboxExtra("math/compare","<",{outputs:[["A=",{outputs:[["A>=B","boolean"]],title:"A>=B"});E.registerSearchboxExtra("math/compare","<=",{outputs:[["A<=B","boolean"]],title:"A<=B"});a.values="> < == != <= >= || &&".split(" ");a["@OP"]={type:"enum",title:"operation",values:a.values};a.title="Condition";a.desc="evaluates condition between A and B";a.prototype.getTitle= -function(){return"A "+this.properties.OP+" B"};a.prototype.onExecute=function(){var a=this.getInputData(0);void 0===a?a=this.properties.A:this.properties.A=a;var b=this.getInputData(1);void 0===b?b=this.properties.B:this.properties.B=b;var d=!0;switch(this.properties.OP){case ">":d=a>b;break;case "<":d=a=":d=a>=b;break;case "||":d=a||b;break;case "&&":d=a&&b}this.setOutputData(0,d);this.setOutputData(1,!d)};E.registerNodeType("math/condition", -a);b.title="Accumulate";b.desc="Increments a value every time";b.prototype.onExecute=function(){null===this.properties.value&&(this.properties.value=0);var a=this.getInputData(0);this.properties.value=null!==a?this.properties.value+a:this.properties.value+this.properties.increment;this.setOutputData(0,this.properties.value)};E.registerNodeType("math/accumulate",b);d.title="Trigonometry";d.desc="Sin Cos Tan";d.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.amplitude, -d=this.findInputSlot("amplitude");-1!=d&&(b=this.getInputData(d));var c=this.properties.offset,d=this.findInputSlot("offset");-1!=d&&(c=this.getInputData(d));for(var d=0,g=this.outputs.length;dXY";f.desc="vector 2 to components";f.prototype.onExecute=function(){var a=this.getInputData(0); -null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]))};E.registerNodeType("math3d/vec2-to-xy",f);r.title="XY->Vec2";r.desc="components to vector2";r.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this._data;d[0]=a;d[1]=b;this.setOutputData(0,d)};E.registerNodeType("math3d/xy-to-vec2",r);s.title="Vec3->XYZ";s.desc="vector 3 to components";s.prototype.onExecute=function(){var a=this.getInputData(0); -null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]))};E.registerNodeType("math3d/vec3-to-xyz",s);I.title="XYZ->Vec3";I.desc="components to vector3";I.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this._data;c[0]=a;c[1]=b;c[2]=d;this.setOutputData(0,c)};E.registerNodeType("math3d/xyz-to-vec3",I);J.title= -"Vec4->XYZW";J.desc="vector 4 to components";J.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]),this.setOutputData(3,a[3]))};E.registerNodeType("math3d/vec4-to-xyzw",J);H.title="XYZW->Vec4";H.desc="components to vector4";H.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null== -d&&(d=this.properties.z);var c=this.getInputData(3);null==c&&(c=this.properties.w);var g=this._data;g[0]=a;g[1]=b;g[2]=d;g[3]=c;this.setOutputData(0,g)};E.registerNodeType("math3d/xyzw-to-vec4",H)})(this); -(function(B){function c(){this.addInput("T","vec3");this.addInput("R","vec3");this.addInput("S","vec3");this.addOutput("mat4","mat4");this.properties={T:[0,0,0],R:[0,0,0],S:[1,1,1],R_in_degrees:!0};this._result=mat4.create();this._must_update=!0}function m(){this.addInput("A","number,vec3");this.addInput("B","number,vec3");this.addOutput("=","vec3");this.addProperty("OP","+","enum",{values:m.values});this._result=vec3.create()}function n(){this.addInput("in","vec3");this.addInput("f","number");this.addOutput("out", -"vec3");this.properties={f:1};this._data=new Float32Array(3)}function l(){this.addInput("in","vec3");this.addOutput("out","number")}function x(){this.addInput("in","vec3");this.addOutput("out","vec3");this._data=new Float32Array(3)}function e(){this.addInput("A","vec3");this.addInput("B","vec3");this.addInput("f","vec3");this.addOutput("out","vec3");this.properties={f:0.5};this._data=new Float32Array(3)}function C(){this.addInput("A","vec3");this.addInput("B","vec3");this.addOutput("out","number")} -var w=B.LiteGraph;c.title="mat4";c.temp_quat=new Float32Array([0,0,0,1]);c.temp_mat4=new Float32Array(16);c.temp_vec3=new Float32Array(3);c.prototype.onPropertyChanged=function(c,e){this._must_update=!0};c.prototype.onExecute=function(){var e=this._result,l=c.temp_quat,n=c.temp_mat4,h=c.temp_vec3,m=this.getInputData(0),y=this.getInputData(1),v=this.getInputData(2);if(this._must_update||m||y||v)m=m||this.properties.T,y=y||this.properties.R,v=v||this.properties.S,mat4.identity(e),mat4.translate(e,e, -m),this.properties.R_in_degrees?(h.set(y),vec3.scale(h,h,DEG2RAD),quat.fromEuler(l,h)):quat.fromEuler(l,y),mat4.fromQuat(n,l),mat4.multiply(e,e,n),mat4.scale(e,e,v);this.setOutputData(0,e)};w.registerNodeType("math3d/mat4",c);m.values="+ - * / % ^ max min".split(" ");m.title="Operation";m.desc="Easy math 3D operators";m["@OP"]={type:"enum",title:"operation",values:m.values};m.size=[100,60];m.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+ -"(A,B)":"A "+this.properties.OP+" B"};m.prototype.onExecute=function(){var c=this.getInputData(0),e=this.getInputData(1);if(null!=c&&null!=e){c.constructor===Number&&(c=[c,c,c]);e.constructor===Number&&(e=[e,e,e]);var l=this._result;switch(this.properties.OP){case "+":l=vec3.add(l,c,e);break;case "-":l=vec3.sub(l,c,e);break;case "x":case "X":case "*":l=vec3.mul(l,c,e);break;case "/":l=vec3.div(l,c,e);break;case "%":l[0]=c[0]%e[0];l[1]=c[1]%e[1];l[2]=c[2]%e[2];break;case "^":l[0]=Math.pow(c[0],e[0]); -l[1]=Math.pow(c[1],e[1]);l[2]=Math.pow(c[2],e[2]);break;case "max":l[0]=Math.max(c[0],e[0]);l[1]=Math.max(c[1],e[1]);l[2]=Math.max(c[2],e[2]);break;case "min":l[0]=Math.min(c[0],e[0]);l[1]=Math.min(c[1],e[1]);l[2]=Math.min(c[2],e[2]);break;default:console.warn("Unknown operation: "+this.properties.OP)}this.setOutputData(0,l)}};m.prototype.onDrawBackground=function(c){this.flags.collapsed||(c.font="40px Arial",c.fillStyle="#666",c.textAlign="center",c.fillText(this.properties.OP,0.5*this.size[0],0.5* -(this.size[1]+w.NODE_TITLE_HEIGHT)),c.textAlign="left")};w.registerNodeType("math3d/operation",m);n.title="vec3_scale";n.desc="scales the components of a vec3";n.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null==e&&(e=this.properties.f);var l=this._data;l[0]=c[0]*e;l[1]=c[1]*e;l[2]=c[2]*e;this.setOutputData(0,l)}};w.registerNodeType("math3d/vec3-scale",n);l.title="vec3_length";l.desc="returns the module of a vector";l.prototype.onExecute=function(){var c= -this.getInputData(0);null!=c&&(c=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),this.setOutputData(0,c))};w.registerNodeType("math3d/vec3-length",l);x.title="vec3_normalize";x.desc="returns the vector normalized";x.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),l=this._data;l[0]=c[0]/e;l[1]=c[1]/e;l[2]=c[2]/e;this.setOutputData(0,l)}};w.registerNodeType("math3d/vec3-normalize",x);e.title="vec3_lerp";e.desc="returns the interpolated vector"; -e.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);if(null!=e){var l=this.getInputOrProperty("f"),h=this._data;h[0]=c[0]*(1-l)+e[0]*l;h[1]=c[1]*(1-l)+e[1]*l;h[2]=c[2]*(1-l)+e[2]*l;this.setOutputData(0,h)}}};w.registerNodeType("math3d/vec3-lerp",e);C.title="vec3_dot";C.desc="returns the dot product";C.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null!=e&&this.setOutputData(0,c[0]*e[0]+c[1]*e[1]+c[2]* -e[2])}};w.registerNodeType("math3d/vec3-dot",C);B.glMatrix?(B=function(){this.addOutput("quat","quat");this.properties={x:0,y:0,z:0,w:1,normalize:!1};this._value=quat.create()},B.title="Quaternion",B.desc="quaternion",B.prototype.onExecute=function(){this._value[0]=this.getInputOrProperty("x");this._value[1]=this.getInputOrProperty("y");this._value[2]=this.getInputOrProperty("z");this._value[3]=this.getInputOrProperty("w");this.properties.normalize&&quat.normalize(this._value,this._value);this.setOutputData(0, -this._value)},B.prototype.onGetInputs=function(){return[["x","number"],["y","number"],["z","number"],["w","number"]]},w.registerNodeType("math3d/quaternion",B),B=function(){this.addInputs([["degrees","number"],["axis","vec3"]]);this.addOutput("quat","quat");this.properties={angle:90,axis:vec3.fromValues(0,1,0)};this._value=quat.create()},B.title="Rotation",B.desc="quaternion rotation",B.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.angle);var e=this.getInputData(1); -null==e&&(e=this.properties.axis);c=quat.setAxisAngle(this._value,e,0.0174532925*c);this.setOutputData(0,c)},w.registerNodeType("math3d/rotation",B),B=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},B.title="Rot. Vec3",B.desc="rotate a point",B.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.vec);var e=this.getInputData(1);null==e?this.setOutputData(c):this.setOutputData(0,vec3.transformQuat(vec3.create(), -c,e))},w.registerNodeType("math3d/rotate_vec3",B),B=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},B.title="Mult. Quat",B.desc="rotate quaternion",B.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null!=e&&(c=quat.multiply(this._value,c,e),this.setOutputData(0,c))}},w.registerNodeType("math3d/mult-quat",B),B=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp", -"quat");this.addProperty("factor",0.5);this._value=quat.create()},B.title="Quat Slerp",B.desc="quaternion spherical interpolation",B.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);if(null!=e){var l=this.properties.factor;null!=this.getInputData(2)&&(l=this.getInputData(2));c=quat.slerp(this._value,c,e,l);this.setOutputData(0,c)}}},w.registerNodeType("math3d/quat-slerp",B),B=function(){this.addInput("vec3","vec3");this.addOutput("remap","vec3");this.addOutput("clamped", -"vec3");this.properties={clamp:!0,range_min:[-1,-1,0],range_max:[1,1,0],target_min:[-1,-1,0],target_max:[1,1,0]};this._value=vec3.create();this._clamped=vec3.create()},B.title="Remap Range",B.desc="remap a 3D range",B.prototype.onExecute=function(){var c=this.getInputData(0);c&&this._value.set(c);for(var c=this.properties.range_min,e=this.properties.range_max,l=this.properties.target_min,h=this.properties.target_max,n=0;3>n;++n){var m=e[n]-c[n];this._clamped[n]=Math.clamp(this._value[n],c[n],e[n]); -0==m?this._value[n]=0.5*(l[n]+h[n]):(m=(this._value[n]-c[n])/m,this.properties.clamp&&(m=Math.clamp(m,0,1)),this._value[n]=l[n]+m*(h[n]-l[n]))}this.setOutputData(0,this._value);this.setOutputData(1,this._clamped)},w.registerNodeType("math3d/remap_range",B)):console.warn("No glmatrix found, some Math3D nodes may not work")})(this); -(function(B){function c(c,l){return c==l}function m(){this.addInput("","string");this.addOutput("table","table");this.addOutput("rows","number");this.addProperty("value","");this.addProperty("separator",",");this._table=null}B=B.LiteGraph;B.wrapFunctionAsNode("string/toString",c,["*"],"String");B.wrapFunctionAsNode("string/compare",c,["string","string"],"boolean");B.wrapFunctionAsNode("string/concatenate",function(c,l){return void 0===c?l:void 0===l?c:c+l},["string","string"],"string");B.wrapFunctionAsNode("string/contains", -function(c,l){return void 0===c||void 0===l?!1:-1!=c.indexOf(l)},["string","string"],"boolean");B.wrapFunctionAsNode("string/toUpperCase",function(c){return null!=c&&c.constructor===String?c.toUpperCase():c},["string"],"string");B.wrapFunctionAsNode("string/split",function(c,l){null==l&&(l=this.properties.separator);if(null==c)return[];if(c.constructor===String)return c.split(l||" ");if(c.constructor===Array){for(var m=[],e=0;ee;++e){var h=this.getInputData(e);if(null!=h){var l=this.values[e];l.push(h);l.length>c[0]&&l.shift()}}}};c.prototype.onDrawBackground=function(e){if(!this.flags.collapsed){var h=this.size,l=0.5*h[1]/ -this.properties.scale,n=c.colors,p=0.5*h[1];e.fillStyle="#000";e.fillRect(0,0,h[0],h[1]);e.strokeStyle="#555";e.beginPath();e.moveTo(0,p);e.lineTo(h[0],p);e.stroke();if(this.inputs)for(var q=0;4>q;++q){var k=this.values[q];if(this.inputs[q]&&this.inputs[q].link){e.strokeStyle=n[q];e.beginPath();var a=k[0]*l*-1+p;e.moveTo(0,Math.clamp(a,0,h[1]));for(var b=1;be&&(e=0);if(0!=c.length){var h=[0,0,0];if(0==e)h=c[0];else if(1==e)h=c[c.length-1];else{var l= -(c.length-1)*e,e=c[Math.floor(l)],c=c[Math.floor(l)+1],l=l-Math.floor(l);h[0]=e[0]*(1-l)+c[0]*l;h[1]=e[1]*(1-l)+c[1]*l;h[2]=e[2]*(1-l)+c[2]*l}for(var p in h)h[p]/=255;this.boxcolor=colorToString(h);this.setOutputData(0,h)}};h.registerNodeType("color/palette",n);l.title="Frame";l.desc="Frame viewerew";l.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}];l.prototype.onDrawBackground=function(c){this.frame&&!this.flags.collapsed&&c.drawImage(this.frame, -0,0,this.size[0],this.size[1])};l.prototype.onExecute=function(){this.frame=this.getInputData(0);this.setDirtyCanvas(!0)};l.prototype.onWidget=function(c,e){if("resize"==e.name&&this.frame){var h=this.frame.width,l=this.frame.height;h||null==this.frame.videoWidth||(h=this.frame.videoWidth,l=this.frame.videoHeight);h&&l&&(this.size=[h,l]);this.setDirtyCanvas(!0,!0)}else"view"==e.name&&this.show()};l.prototype.show=function(){showElement&&this.frame&&showElement(this.frame)};h.registerNodeType("graphics/frame", -l);x.title="Image fade";x.desc="Fades between images";x.widgets=[{name:"resizeA",text:"Resize to A",type:"button"},{name:"resizeB",text:"Resize to B",type:"button"}];x.prototype.onAdded=function(){this.createCanvas();var c=this.canvas.getContext("2d");c.fillStyle="#000";c.fillRect(0,0,this.properties.width,this.properties.height)};x.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};x.prototype.onExecute= -function(){var c=this.canvas.getContext("2d");this.canvas.width=this.canvas.width;var e=this.getInputData(0);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);e=this.getInputData(2);null==e?e=this.properties.fade:this.properties.fade=e;c.globalAlpha=e;e=this.getInputData(1);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);c.globalAlpha=1;this.setOutputData(0,this.canvas);this.setDirtyCanvas(!0)};h.registerNodeType("graphics/imagefade",x);e.title="Crop";e.desc="Crop Image"; -e.prototype.onAdded=function(){this.createCanvas()};e.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};e.prototype.onExecute=function(){var c=this.getInputData(0);c&&(c.width?(this.canvas.getContext("2d").drawImage(c,-this.properties.x,-this.properties.y,c.width*this.properties.scale,c.height*this.properties.scale),this.setOutputData(0,this.canvas)):this.setOutputData(0,null))};e.prototype.onDrawBackground= -function(c){this.flags.collapsed||this.canvas&&c.drawImage(this.canvas,0,0,this.canvas.width,this.canvas.height,0,0,this.size[0],this.size[1])};e.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"scale"==c?(this.properties[c]=parseFloat(e),0==this.properties[c]&&(console.error("Error in scale"),this.properties[c]=1)):this.properties[c]=parseInt(e);this.createCanvas();return!0};h.registerNodeType("graphics/cropImage",e);C.title="Canvas";C.desc="Canvas to render stuff";C.prototype.onExecute= -function(){var c=this.canvas,e=this.properties.width|0,h=this.properties.height|0;c.width!=e&&(c.width=e);c.height!=h&&(c.height=h);this.properties.autoclear&&this.ctx.clearRect(0,0,c.width,c.height);this.setOutputData(0,c)};C.prototype.onAction=function(c,e){"clear"==c&&this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)};h.registerNodeType("graphics/canvas",C);w.title="DrawImage";w.desc="Draws image into a canvas";w.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e= -this.getInputOrProperty("img");if(e){var h=this.getInputOrProperty("x"),l=this.getInputOrProperty("y");c.getContext("2d").drawImage(e,h,l)}}};h.registerNodeType("graphics/drawImage",w);u.title="DrawRectangle";u.desc="Draws rectangle in canvas";u.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e=this.getInputOrProperty("x"),h=this.getInputOrProperty("y"),l=this.getInputOrProperty("w"),p=this.getInputOrProperty("h");c.getContext("2d").fillRect(e,h,l,p)}};h.registerNodeType("graphics/drawRectangle", -u);D.title="Video";D.desc="Video playback";D.widgets=[{name:"play",text:"PLAY",type:"minibutton"},{name:"stop",text:"STOP",type:"minibutton"},{name:"demo",text:"Demo video",type:"button"},{name:"mute",text:"Mute video",type:"button"}];D.prototype.onExecute=function(){if(this.properties.url&&(this.properties.url!=this._video_url&&this.loadVideo(this.properties.url),this._video&&0!=this._video.width)){var c=this.getInputData(0);c&&0<=c&&1>=c&&(this._video.currentTime=c*this._video.duration,this._video.pause()); -this._video.dirty=!0;this.setOutputData(0,this._video);this.setOutputData(1,this._video.currentTime);this.setOutputData(2,this._video.duration);this.setDirtyCanvas(!0)}};D.prototype.onStart=function(){this.play()};D.prototype.onStop=function(){this.stop()};D.prototype.loadVideo=function(c){this._video_url=c;var e=c.substr(0,10).indexOf(":"),l="";-1!=e&&(l=c.substr(0,e));e="";l&&(e=c.substr(0,c.indexOf("/",l.length+3)),e=e.substr(l.length+3));this.properties.use_proxy&&l&&h.proxy&&e!=location.host&& -(c=h.proxy+c.substr(c.indexOf(":")+3));this._video=document.createElement("video");this._video.src=c;this._video.type="type=video/mp4";this._video.muted=!0;this._video.autoplay=!0;var n=this;this._video.addEventListener("loadedmetadata",function(c){console.log("Duration: "+this.duration+" seconds");console.log("Size: "+this.videoWidth+","+this.videoHeight);n.setDirtyCanvas(!0);this.width=this.videoWidth;this.height=this.videoHeight});this._video.addEventListener("progress",function(c){console.log("video loading...")}); -this._video.addEventListener("error",function(c){console.error("Error loading video: "+this.src);if(this.error)switch(this.error.code){case this.error.MEDIA_ERR_ABORTED:console.error("You stopped the video.");break;case this.error.MEDIA_ERR_NETWORK:console.error("Network error - please try again later.");break;case this.error.MEDIA_ERR_DECODE:console.error("Video is broken..");break;case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED:console.error("Sorry, your browser can't play this video.")}});this._video.addEventListener("ended", -function(c){console.log("Video Ended.");this.play()})};D.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"url"==c&&""!=e&&this.loadVideo(e);return!0};D.prototype.play=function(){this._video&&this._video.videoWidth&&this._video.play()};D.prototype.playPause=function(){this._video&&(this._video.paused?this.play():this.pause())};D.prototype.stop=function(){this._video&&(this._video.pause(),this._video.currentTime=0)};D.prototype.pause=function(){this._video&&(console.log("Video paused"), -this._video.pause())};D.prototype.onWidget=function(c,e){};h.registerNodeType("graphics/video",D);A.title="Webcam";A.desc="Webcam image";A.is_webcam_open=!1;A.prototype.openStream=function(){function c(h){console.log("Webcam rejected",h);e._webcam_stream=!1;A.is_webcam_open=!1;e.boxcolor="red";e.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](c); -var e=this}};A.prototype.closeStream=function(){if(this._webcam_stream){var c=this._webcam_stream.getTracks();if(c.length)for(var e=0;e=this.size[1]||!this.properties.show|| -!this._video||(c.save(),c.drawImage(this._video,0,0,this.size[0],this.size[1]),c.restore())};A.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["stream_ready",h.EVENT],["stream_closed",h.EVENT],["stream_error",h.EVENT]]};h.registerNodeType("graphics/webcam",A)})(this); -(function(B){function c(){this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",filter:!0};this.size=[c.image_preview_size,c.image_preview_size]}function m(){this.addInput("Texture","Texture");this.properties={flipY:!1};this.size=[c.image_preview_size,c.image_preview_size]}function n(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",generate_mipmaps:!1}}function l(){this.addInput("Texture", -"Texture");this.addInput("TextureB","Texture");this.addInput("value","number");this.addOutput("Texture","Texture");this.help="

pixelcode must be vec3, uvcode must be vec2, is optional

\t\t

uv: tex. coords

color: texture colorB: textureB

time: scene time value: input value

For multiline you must type: result = ...

";this.properties={value:1,pixelcode:"color + colorB * value",uvcode:"",precision:c.DEFAULT}; -this.has_error=!1}function x(){this.addOutput("out","Texture");this.properties={code:"",u_value:1,u_color:[1,1,1,1],width:512,height:512,precision:c.DEFAULT};this.properties.code=x.pixel_shader;this._uniforms={u_value:1,u_color:vec4.create(),in_texture:0,texSize:vec2.create(),time:0}}function e(){this.addInput("in","Texture");this.addInput("scale","vec2");this.addInput("offset","vec2");this.addOutput("out","Texture");this.properties={offset:vec2.fromValues(0,0),scale:vec2.fromValues(1,1),precision:c.DEFAULT}} -function C(){this.addInput("in","Texture");this.addInput("warp","Texture");this.addInput("factor","number");this.addOutput("out","Texture");this.properties={factor:0.01,scale:[1,1],offset:[0,0],precision:c.DEFAULT};this._uniforms={u_texture:0,u_textureB:1,u_factor:1,u_scale:vec2.create(),u_offset:vec2.create()}}function w(){this.addInput("Texture","Texture");this.properties={additive:!1,antialiasing:!1,filter:!0,disable_alpha:!1,gamma:1,viewport:[0,0,1,1]};this.size[0]=130}function u(){this.addInput("Texture", -"Texture");this.addOutput("","Texture");this.properties={size:0,generate_mipmaps:!1,precision:c.DEFAULT}}function D(){this.addInput("Texture","Texture");this.addOutput("","Texture");this.properties={iterations:1,generate_mipmaps:!1,precision:c.DEFAULT}}function A(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("avg","vec4");this.addOutput("lum","number");this.properties={use_previous_frame:!0,high_quality:!1};this._uniforms={u_texture:0,u_mipmap_offset:0};this._luminance= -new Float32Array(4)}function h(){this.addInput("Texture","Texture");this.addOutput("min_t","Texture");this.addOutput("max_t","Texture");this.addOutput("min","vec4");this.addOutput("max","vec4");this.properties={mode:"max",use_previous_frame:!0};this._uniforms={u_texture:0};this._max=new Float32Array(4);this._min=new Float32Array(4);this._textures_chain=[]}function z(){this.addInput("in","Texture");this.addInput("factor","Number");this.addOutput("out","Texture");this.properties={factor:0.5};this._uniforms= -{u_texture:0,u_textureB:1,u_factor:this.properties.factor}}function y(){this.addInput("in","Texture");this.addOutput("avg","Texture");this.addOutput("array","Texture");this.properties={samples:64,frames_interval:1};this._uniforms={u_texture:0,u_textureB:1,u_samples:this.properties.samples,u_isamples:1/this.properties.samples};this.frame=0}function v(){this.addInput("Image","image");this.addOutput("","Texture");this.properties={}}function G(){this.addInput("Texture","Texture");this.addInput("LUT", -"Texture");this.addInput("Intensity","number");this.addOutput("","Texture");this.properties={enabled:!0,intensity:1,precision:c.DEFAULT,texture:null};G._shader||(G._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,G.pixel_shader))}function p(){this.addInput("Texture","Texture");this.addOutput("R","Texture");this.addOutput("G","Texture");this.addOutput("B","Texture");this.addOutput("A","Texture");p._shader||(p._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,p.pixel_shader))}function q(){this.addInput("R", -"Texture");this.addInput("G","Texture");this.addInput("B","Texture");this.addInput("A","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,R:1,G:1,B:1,A:1};this._color=vec4.create();this._uniforms={u_textureR:0,u_textureG:1,u_textureB:2,u_textureA:3,u_color:this._color}}function k(){this.addOutput("Texture","Texture");this._tex_color=vec4.create();this.properties={color:vec4.create(),precision:c.DEFAULT}}function a(){this.addInput("A","color");this.addInput("B","color"); -this.addOutput("Texture","Texture");this.properties={angle:0,scale:1,A:[0,0,0],B:[1,1,1],texture_size:32};a._shader||(a._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,a.pixel_shader));this._uniforms={u_angle:0,u_colorA:vec3.create(),u_colorB:vec3.create()}}function b(){this.addInput("A","Texture");this.addInput("B","Texture");this.addInput("Mixer","Texture");this.addOutput("Texture","Texture");this.properties={factor:0.5,size_from_biggest:!0,invert:!1,precision:c.DEFAULT};this._uniforms={u_textureA:0, -u_textureB:1,u_textureMix:2,u_mix:vec4.create()}}function d(){this.addInput("Tex.","Texture");this.addOutput("Edges","Texture");this.properties={invert:!0,threshold:!1,factor:1,precision:c.DEFAULT};d._shader||(d._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,d.pixel_shader))}function g(){this.addInput("Texture","Texture");this.addInput("Distance","number");this.addInput("Range","number");this.addOutput("Texture","Texture");this.properties={distance:100,range:50,only_depth:!1,high_precision:!1}; -this._uniforms={u_texture:0,u_distance:100,u_range:50,u_camera_planes:null}}function f(){this.addInput("Texture","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,invert:!1};this._uniforms={u_texture:0,u_camera_planes:null,u_ires:vec2.create()}}function r(){this.addInput("Texture","Texture");this.addInput("Iterations","number");this.addInput("Intensity","number");this.addOutput("Blurred","Texture");this.properties={intensity:1,iterations:1,preserve_aspect:!1,scale:[1, -1],precision:c.DEFAULT}}function s(){this.addInput("in","Texture");this.addInput("dirt","Texture");this.addOutput("out","Texture");this.addOutput("glow","Texture");this.properties={enabled:!0,intensity:1,persistence:0.99,iterations:16,threshold:0,scale:1,dirt_factor:0.5,precision:c.DEFAULT};this._textures=[];this._uniforms={u_intensity:1,u_texture:0,u_glow_texture:1,u_threshold:0,u_texel_size:vec2.create()}}function I(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties= -{intensity:1,radius:5}}function J(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={sigma:1.4,k:1.6,p:21.7,epsilon:79,phi:0.017}}function H(){this.addOutput("Webcam","Texture");this.properties={texture_name:"",facingMode:"user"};this.boxcolor="black";this.version=0}function E(){this.addInput("in","Texture");this.addInput("f","number");this.addOutput("out","Texture");this.properties={enabled:!0,factor:1,precision:c.LOW};this._uniforms={u_texture:0,u_factor:1}} -function t(){this.addInput("in","Texture");this.addOutput("out","Texture");this.properties={precision:c.LOW,split_channels:!1};this._values=new Uint8Array(1024);this._values.fill(255);this._curve_texture=null;this._uniforms={u_texture:0,u_curve:1,u_range:1};this._must_update=!0;this._points={RGB:[[0,0],[1,1]],R:[[0,0],[1,1]],G:[[0,0],[1,1]],B:[[0,0],[1,1]]};this.curve_editor=null;this.addWidget("toggle","Split Channels",!1,"split_channels");this.addWidget("combo","Channel","RGB",{values:["RGB","R", -"G","B"]});this.curve_offset=68;this.size=[240,160]}function N(){this.addInput("in","Texture");this.addInput("exp","number");this.addOutput("out","Texture");this.properties={exposition:1,precision:c.LOW};this._uniforms={u_texture:0,u_exposition:1}}function K(){this.addInput("in","Texture");this.addInput("avg","number,Texture");this.addOutput("out","Texture");this.properties={enabled:!0,scale:1,gamma:1,average_lum:1,lum_white:1,precision:c.LOW};this._uniforms={u_texture:0,u_lumwhite2:1,u_igamma:1, -u_scale:1,u_average_lum:1}}function M(){this.addOutput("out","Texture");this.properties={width:512,height:512,seed:0,persistence:0.1,octaves:8,scale:1,offset:[0,0],amplitude:1,precision:c.DEFAULT};this._key=0;this._texture=null;this._uniforms={u_persistence:0.1,u_seed:0,u_offset:vec2.create(),u_scale:1,u_viewport:vec2.create()}}function L(){this.addInput("v");this.addOutput("out","Texture");this.properties={code:L.default_code,width:512,height:512,clear:!0,precision:c.DEFAULT,use_html_canvas:!1}; -this._temp_texture=this._func=null;this.compileCode()}function O(){this.addInput("in","Texture");this.addOutput("out","Texture");this.properties={key_color:vec3.fromValues(0,1,0),threshold:0.8,slope:0.2,precision:c.DEFAULT}}function P(){this.addInput("in","texture");this.addInput("yaw","number");this.addOutput("out","texture");this.properties={yaw:0}}var F=B.LiteGraph;B.LGraphTexture=null;"undefined"!=typeof GL&&(LGraphCanvas.link_type_colors.Texture="#987",B.LGraphTexture=c,c.title="Texture",c.desc= -"Texture",c.widgets_info={name:{widget:"texture"},filter:{widget:"checkbox"}},c.loadTextureCallback=null,c.image_preview_size=256,c.PASS_THROUGH=1,c.COPY=2,c.LOW=3,c.HIGH=4,c.REUSE=5,c.DEFAULT=2,c.MODE_VALUES={"pass through":c.PASS_THROUGH,copy:c.COPY,low:c.LOW,high:c.HIGH,reuse:c.REUSE,"default":c.DEFAULT},c.getTexturesContainer=function(){return gl.textures},c.loadTexture=function(a,b){b=b||{};var d=a;"http://"==d.substr(0,7)&&F.proxy&&(d=F.proxy+d.substr(7));return c.getTexturesContainer()[a]= -GL.Texture.fromURL(d,b)},c.getTexture=function(a){var b=this.getTexturesContainer();if(!b)throw"Cannot load texture, container of textures not found";b=b[a];return!b&&a&&":"!=a[0]?this.loadTexture(a):b},c.getTargetTexture=function(a,b,d){if(!a)throw"LGraphTexture.getTargetTexture expects a reference texture";var g=null;switch(d){case c.LOW:g=gl.UNSIGNED_BYTE;break;case c.HIGH:g=gl.HIGH_PRECISION_FORMAT;break;case c.REUSE:return a;default:g=a?a.type:gl.UNSIGNED_BYTE}b&&b.width==a.width&&b.height== -a.height&&b.type==g||(b=new GL.Texture(a.width,a.height,{type:g,format:gl.RGBA,filter:gl.LINEAR}));return b},c.getTextureType=function(a,b){var d=b?b.type:gl.UNSIGNED_BYTE;switch(a){case c.HIGH:d=gl.HIGH_PRECISION_FORMAT;break;case c.LOW:d=gl.UNSIGNED_BYTE}return d},c.getWhiteTexture=function(){return this._white_texture?this._white_texture:this._white_texture=GL.Texture.fromMemory(1,1,[255,255,255,255],{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.getNoiseTexture=function(){if(this._noise_texture)return this._noise_texture; -for(var a=new Uint8Array(1048576),b=0;1048576>b;++b)a[b]=255*Math.random();return this._noise_texture=a=GL.Texture.fromMemory(512,512,a,{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.prototype.onDropFile=function(a,b,d){if(a){var c=null;"string"==typeof a?c=GL.Texture.fromURL(a):-1!=b.toLowerCase().indexOf(".dds")?c=GL.Texture.fromDDSInMemory(a):(a=new Blob([d]),a=URL.createObjectURL(a),c=GL.Texture.fromURL(a));this._drop_texture=c;this.properties.name=b}else this._drop_texture=null,this.properties.name= -""},c.prototype.getExtraMenuOptions=function(a){var b=this;if(this._drop_texture)return[{content:"Clear",callback:function(){b._drop_texture=null;b.properties.name=""}}]},c.prototype.onExecute=function(){var a=null;this.isOutputConnected(1)&&(a=this.getInputData(0));!a&&this._drop_texture&&(a=this._drop_texture);!a&&this.properties.name&&(a=c.getTexture(this.properties.name));if(a){this._last_tex=a;!1===this.properties.filter?a.setParameter(gl.TEXTURE_MAG_FILTER,gl.NEAREST):a.setParameter(gl.TEXTURE_MAG_FILTER, -gl.LINEAR);this.setOutputData(0,a);this.setOutputData(1,a.fullpath||a.filename);for(var b=2;b= -this.size[1]))if(this._drop_texture&&a.webgl)a.drawImage(this._drop_texture,0,0,this.size[0],this.size[1]);else{if(this._last_preview_tex!=this._last_tex)if(a.webgl)this._canvas=this._last_tex;else{var b=c.generateLowResTexturePreview(this._last_tex);if(!b)return;this._last_preview_tex=this._last_tex;this._canvas=cloneCanvas(b)}this._canvas&&(a.save(),a.webgl||(a.translate(0,this.size[1]),a.scale(1,-1)),a.drawImage(this._canvas,0,0,this.size[0],this.size[1]),a.restore())}},c.generateLowResTexturePreview= -function(a){if(!a)return null;var b=c.image_preview_size,d=a;if(a.format==gl.DEPTH_COMPONENT)return null;if(a.width>b||a.height>b)d=this._preview_temp_tex,this._preview_temp_tex||(this._preview_temp_tex=d=new GL.Texture(b,b,{minFilter:gl.NEAREST})),a.copyTo(d);a=this._preview_canvas;a||(this._preview_canvas=a=createCanvas(b,b));d&&d.toCanvas(a);return a},c.prototype.getResources=function(a){this.properties.name&&(a[this.properties.name]=GL.Texture);return a},c.prototype.onGetInputs=function(){return[["in", -"Texture"]]},c.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["aspect","number"]]},c.replaceCode=function(a,b){return a.replace(/\{\{[a-zA-Z0-9_]*\}\}/g,function(a){a=a.replace(/[\{\}]/g,"");return b[a]||""})},F.registerNodeType("texture/texture",c),m.title="Preview",m.desc="Show a texture in the graph canvas",m.allow_preview=!1,m.prototype.onDrawBackground=function(a){if(!this.flags.collapsed&&(a.webgl||m.allow_preview)){var b=this.getInputData(0);if(b){var d=null, -d=!b.handle&&a.webgl?b:c.generateLowResTexturePreview(b);a.save();this.properties.flipY&&(a.translate(0,this.size[1]),a.scale(1,-1));a.drawImage(d,0,0,this.size[0],this.size[1]);a.restore()}}},F.registerNodeType("texture/preview",m),n.title="Save",n.desc="Save a texture in the repository",n.prototype.getPreviewTexture=function(){return this._texture},n.prototype.onExecute=function(){var a=this.getInputData(0);a&&(this.properties.generate_mipmaps&&(a.bind(0),a.setParameter(gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_LINEAR), -gl.generateMipmap(a.texture_type),a.unbind(0)),this.properties.name&&(c.storeTexture?c.storeTexture(this.properties.name,a):c.getTexturesContainer()[this.properties.name]=a),this._texture=a,this.setOutputData(0,a),this.setOutputData(1,this.properties.name))},F.registerNodeType("texture/save",n),l.widgets_info={uvcode:{widget:"code"},pixelcode:{widget:"code"},precision:{widget:"combo",values:c.MODE_VALUES}},l.title="Operation",l.desc="Texture shader operation",l.presets={},l.prototype.getExtraMenuOptions= -function(a){var b=this;return[{content:b.properties.show?"Hide Texture":"Show Texture",callback:function(){b.properties.show=!b.properties.show}}]},l.prototype.onPropertyChanged=function(){this.has_error=!1},l.prototype.onDrawBackground=function(a){this.flags.collapsed||20>=this.size[1]||!this.properties.show||!this._tex||this._tex.gl!=a||(a.save(),a.drawImage(this._tex,0,0,this.size[0],this.size[1]),a.restore())},l.prototype.onExecute=function(){var a=this.getInputData(0);if(this.isOutputConnected(0))if(this.properties.precision=== -c.PASS_THROUGH)this.setOutputData(0,a);else{var b=this.getInputData(1);if(this.properties.uvcode||this.properties.pixelcode){var d=512,g=512;a?(d=a.width,g=a.height):b&&(d=b.width,g=b.height);b||(b=GL.Texture.getWhiteTexture());var f=c.getTextureType(this.properties.precision,a);this._tex=a||this._tex?c.getTargetTexture(a||this._tex,this._tex,this.properties.precision):new GL.Texture(d,g,{type:f,format:gl.RGBA,filter:gl.LINEAR});f="";this.properties.uvcode&&(f="uv = "+this.properties.uvcode,-1!=this.properties.uvcode.indexOf(";")&& -(f=this.properties.uvcode));var e="";this.properties.pixelcode&&(e="result = "+this.properties.pixelcode,-1!=this.properties.pixelcode.indexOf(";")&&(e=this.properties.pixelcode));var k=this._shader;if(!(this.has_error||k&&this._shader_code==f+"|"+e)){var h=c.replaceCode(l.pixel_shader,{UV_CODE:f,PIXEL_CODE:e});try{k=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,h),this.boxcolor="#00FF00"}catch(p){GL.Shader.dumpErrorToConsole(p,Shader.SCREEN_VERTEX_SHADER,h);this.boxcolor="#FF0000";this.has_error=!0; -return}this._shader=k;this._shader_code=f+"|"+e}if(this._shader){var q=this.getInputData(2);null!=q?this.properties.value=q:q=parseFloat(this.properties.value);var r=this.graph.getTime();this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);gl.disable(gl.BLEND);a&&a.bind(0);b&&b.bind(1);var c=Mesh.getScreenQuad();k.uniforms({u_texture:0,u_textureB:1,value:q,texSize:[d,g],time:r}).draw(c)});this.setOutputData(0,this._tex)}}}},l.pixel_shader="precision highp float;\n\t\t\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec2 texSize;\n\t\tuniform float time;\n\t\tuniform float value;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord;\n\t\t\t{{UV_CODE}};\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\tvec3 color = color4.rgb;\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\tvec3 colorB = color4B.rgb;\n\t\t\tvec3 result = color;\n\t\t\tfloat alpha = 1.0;\n\t\t\t{{PIXEL_CODE}};\n\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t}\n\t\t", -l.registerPreset=function(a,b){l.presets[a]=b},l.registerPreset("",""),l.registerPreset("bypass","color"),l.registerPreset("add","color + colorB * value"),l.registerPreset("substract","(color - colorB) * value"),l.registerPreset("mate","mix( color, colorB, color4B.a * value)"),l.registerPreset("invert","vec3(1.0) - color"),l.registerPreset("multiply","color * colorB * value"),l.registerPreset("divide","(color / colorB) / value"),l.registerPreset("difference","abs(color - colorB) * value"),l.registerPreset("max", -"max(color, colorB) * value"),l.registerPreset("min","min(color, colorB) * value"),l.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"),l.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"),l.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"),l.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"), -l.prototype.onInspect=function(a){var b=this;a.addCombo("Presets","",{values:Object.keys(l.presets),callback:function(d){var c=l.presets[d];c&&(b.setProperty("pixelcode",c),b.title=d,a.refresh())}})},F.registerNodeType("texture/operation",l),x.title="Shader",x.desc="Texture shader",x.widgets_info={code:{type:"code",lang:"glsl"},precision:{widget:"combo",values:c.MODE_VALUES}},x.prototype.onPropertyChanged=function(a,b){if("code"==a){var d=this.getShader();if(d){var c=d.uniformInfo;if(this.inputs)for(var g= -{},f=0;f lumaMax))\n\t\t\t\tcolor = vec4(rgbA, 1.0);\n\t\t\telse\n\t\t\t\tcolor = vec4(rgbB, 1.0);\n\t\t\tif(u_igamma != 1.0)\n\t\t\t\tcolor.xyz = pow( color.xyz, vec3(u_igamma) );\n\t\t\treturn color;\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\t\t}\n\t\t", -w.gamma_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_igamma;\n\t\tvoid main() {\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\t\t gl_FragColor = color;\n\t\t}\n\t\t",F.registerNodeType("texture/toviewport",w),u.title="Copy",u.desc="Copy Texture",u.widgets_info={size:{widget:"combo",values:[0,32,64,128,256,512,1024,2048]},precision:{widget:"combo", -values:c.MODE_VALUES}},u.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)){if(a){var b=a.width,d=a.height;0!=this.properties.size&&(d=b=this.properties.size);var g=this._temp_texture,f=a.type;this.properties.precision===c.LOW?f=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(f=gl.HIGH_PRECISION_FORMAT);g&&g.width==b&&g.height==d&&g.type==f||(g=gl.LINEAR,this.properties.generate_mipmaps&&isPowerOfTwo(b)&&isPowerOfTwo(d)&&(g=gl.LINEAR_MIPMAP_LINEAR), -this._temp_texture=new GL.Texture(b,d,{type:f,format:gl.RGBA,minFilter:g,magFilter:gl.LINEAR}));a.copyTo(this._temp_texture);this.properties.generate_mipmaps&&(this._temp_texture.bind(0),gl.generateMipmap(this._temp_texture.texture_type),this._temp_texture.unbind(0))}this.setOutputData(0,this._temp_texture)}},F.registerNodeType("texture/copy",u),D.title="Downsample",D.desc="Downsample Texture",D.widgets_info={iterations:{type:"number",step:1,precision:0,min:0},precision:{widget:"combo",values:c.MODE_VALUES}}, -D.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)&&a&&a.texture_type===GL.TEXTURE_2D)if(1>this.properties.iterations)this.setOutputData(0,a);else{var b=D._shader;b||(D._shader=b=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,D.pixel_shader));var d=a.width|0,g=a.height|0,f=a.type;this.properties.precision===c.LOW?f=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(f=gl.HIGH_PRECISION_FORMAT);var e=this.properties.iterations||1,k=a,h= -null,l=[],a={type:f,format:a.format},f=vec2.create(),p={u_offset:f};this._texture&&GL.Texture.releaseTemporary(this._texture);for(var q=0;q>1||0;g=g>>1||0;h=GL.Texture.getTemporary(d,g,a);l.push(h);k.setParameter(GL.TEXTURE_MAG_FILTER,GL.NEAREST);k.copyTo(h,b,p);if(1==d&&1==g)break;k=h}this._texture=l.pop();for(q=0;q>=2,c++,1!=d););a.copyTo(this._textures_chain[0]);for(var c=1;c<=this._textures_chain.length;++c)a=this._textures_chain[c];var g=h._shader,f=this._uniforms;f.u_mipmap_offset=this.properties.mipmap_offset;gl.disable(gl.DEPTH_TEST); -gl.disable(gl.BLEND);this._temp_texture.drawTo(function(){a.toViewport(g,f)})}},h.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform mat4 u_samples_a;\n\t\tuniform mat4 u_samples_b;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_mipmap_offset;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t//random average\n\t\t\tfor(int i = 0; i < 4; ++i)\n\t\t\t\tfor(int j = 0; j < 4; ++j)\n\t\t\t\t{\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t}\n\t\t gl_FragColor = color * 0.03125;\n\t\t}\n\t\t", -z.title="Smooth",z.desc="Smooth texture over time",z.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){z._shader||(z._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,z.pixel_shader));var b=this._temp_texture;b&&b.type==a.type&&b.width==a.width&&b.height==a.height||(b={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(a.width,a.height,b),this._temp_texture2=new GL.Texture(a.width,a.height,b),a.copyTo(this._temp_texture2));var b= -this._temp_texture,d=this._temp_texture2,c=z._shader,g=this._uniforms;g.u_factor=1-this.getInputOrProperty("factor");gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);b.drawTo(function(){d.bind(1);a.toViewport(c,g)});this.setOutputData(0,b);this._temp_texture=d;this._temp_texture2=b}},z.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_factor;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tgl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\t\t}\n\t\t", -F.registerNodeType("texture/temporal_smooth",z),y.title="Lineal Avg Smooth",y.desc="Smooth texture linearly over time",y["@samples"]={type:"number",min:1,max:64,step:1,precision:1},y.prototype.getPreviewTexture=function(){return this._temp_texture2},y.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){y._shader||(y._shader_copy=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,y.pixel_shader_copy),y._shader_avg=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,y.pixel_shader_avg)); -var b=Math.clamp(this.properties.samples,0,64),d=this.frame,c=this.properties.frames_interval;if(0==c||0==d%c){d=this._temp_texture;d&&d.type==a.type&&d.width==b||(d={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(b,1,d),this._temp_texture2=new GL.Texture(b,1,d),this._temp_texture_out=new GL.Texture(1,1,d));var g=this._temp_texture,f=this._temp_texture2,e=y._shader_copy,k=y._shader_avg,h=this._uniforms;h.u_samples=b;h.u_isamples=1/b;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST); -g.drawTo(function(){f.bind(1);a.toViewport(e,h)});this._temp_texture_out.drawTo(function(){g.toViewport(k,h)});this.setOutputData(0,this._temp_texture_out);this._temp_texture=f;this._temp_texture2=g}else this.setOutputData(0,this._temp_texture_out);this.setOutputData(1,this._temp_texture2);this.frame++}},y.pixel_shader_copy="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tif( v_coord.x <= u_isamples )\n\t\t\t\tgl_FragColor = texture2D( u_texture, vec2(0.5) );\n\t\t\telse\n\t\t\t\tgl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\t\t}\n\t\t", -y.pixel_shader_avg="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform int u_samples;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\tfor(int i = 0; i < 64; ++i)\n\t\t\t{\n\t\t\t\tcolor += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\t\t\t\tif(i == (u_samples - 1))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tgl_FragColor = color * u_isamples;\n\t\t}\n\t\t",F.registerNodeType("texture/linear_avg_smooth", -y),v.title="Image to Texture",v.desc="Uploads an image to the GPU",v.prototype.onExecute=function(){var a=this.getInputData(0);if(a){var b=a.videoWidth||a.width,d=a.videoHeight||a.height;if(a.gltexture)this.setOutputData(0,a.gltexture);else{var c=this._temp_texture;c&&c.width==b&&c.height==d||(this._temp_texture=new GL.Texture(b,d,{format:gl.RGBA,filter:gl.LINEAR}));try{this._temp_texture.uploadImage(a)}catch(g){console.error("image comes from an unsafe location, cannot be uploaded to webgl: "+g); -return}this.setOutputData(0,this._temp_texture)}}},F.registerNodeType("texture/imageToTexture",v),G.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},G.title="LUT",G.desc="Apply LUT to Texture",G.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture)); -if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.bindTexture(gl.TEXTURE_2D,null);var d=this.properties.intensity;this.isInputConnected(2)&&(this.properties.intensity=d=this.getInputData(2));this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);this._tex.drawTo(function(){b.bind(1);a.toViewport(G._shader,{u_texture:0,u_textureB:1, -u_amount:d})});this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}},G.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_amount;\n\t\t\n\t\tvoid main() {\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\t\t\t mediump vec2 quad1;\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\t\t\t mediump vec2 quad2;\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\t\t\t highp vec2 texPos1;\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t highp vec2 texPos2;\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\t\t}\n\t\t", -F.registerNodeType("texture/LUT",G),p.title="Texture to Channels",p.desc="Split texture channels",p.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this._channels||(this._channels=Array(4));for(var b=gl.RGB,d=0,c=0;4>c;c++)this.isOutputConnected(c)?(this._channels[c]&&this._channels[c].width==a.width&&this._channels[c].height==a.height&&this._channels[c].type==a.type&&this._channels[c].format==b||(this._channels[c]=new GL.Texture(a.width,a.height,{type:a.type,format:b,filter:gl.LINEAR})), -d++):this._channels[c]=null;if(d){gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);for(var g=Mesh.getScreenQuad(),f=p._shader,e=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],c=0;4>c;c++)this._channels[c]&&(this._channels[c].drawTo(function(){a.bind(0);f.uniforms({u_texture:0,u_mask:e[c]}).draw(g)}),this.setOutputData(c,this._channels[c]))}}},p.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec4 u_mask;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\t\t}\n\t\t", -F.registerNodeType("texture/textureChannels",p),q.title="Channels to Texture",q.desc="Split texture channels",q.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},q.prototype.onExecute=function(){var a=c.getWhiteTexture(),b=this.getInputData(0)||a,d=this.getInputData(1)||a,g=this.getInputData(2)||a,f=this.getInputData(3)||a;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad();q._shader||(q._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,q.pixel_shader));var k=q._shader, -a=Math.max(b.width,d.width,g.width,f.width),h=Math.max(b.height,d.height,g.height,f.height),l=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._texture&&this._texture.width==a&&this._texture.height==h&&this._texture.type==l||(this._texture=new GL.Texture(a,h,{type:l,format:gl.RGBA,filter:gl.LINEAR}));a=this._color;a[0]=this.properties.R;a[1]=this.properties.G;a[2]=this.properties.B;a[3]=this.properties.A;var p=this._uniforms;this._texture.drawTo(function(){b.bind(0); -d.bind(1);g.bind(2);f.bind(3);k.uniforms(p).draw(e)});this.setOutputData(0,this._texture)},q.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureR;\n\t\tuniform sampler2D u_textureG;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform vec4 u_color;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = u_color * vec4( \t\t\t\t\ttexture2D(u_textureR, v_coord).r,\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\t\t}\n\t\t", -F.registerNodeType("texture/channelsTexture",q),k.title="Color",k.desc="Generates a 1x1 texture with a constant color",k.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},k.prototype.onDrawBackground=function(a){var b=this.properties.color;a.fillStyle="rgb("+Math.floor(255*Math.clamp(b[0],0,1))+","+Math.floor(255*Math.clamp(b[1],0,1))+","+Math.floor(255*Math.clamp(b[2],0,1))+")";this.flags.collapsed?this.boxcolor=a.fillStyle:a.fillRect(0,0,this.size[0],this.size[1])},k.prototype.onExecute= -function(){var a=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._tex&&this._tex.type==a||(this._tex=new GL.Texture(1,1,{format:gl.RGBA,type:a,minFilter:gl.NEAREST}));a=this.properties.color;if(this.inputs)for(var b=0;ba.width?d: -a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad(),k=null,h=this._uniforms;g?(k=b._shader_tex,k||(k=b._shader_tex=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader,{MIX_TEX:""}))):(k=b._shader_factor,k||(k=b._shader_factor=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader)),f=null==f?this.properties.factor:f,h.u_mix.set([f,f,f,f]));var l=this.properties.invert;this._tex.drawTo(function(){a.bind(l?1:0);d.bind(l?0:1);g&&g.bind(2); -k.uniforms(h).draw(e)});this.setOutputData(0,this._tex)}}},b.prototype.onGetInputs=function(){return[["factor","number"]]},b.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform sampler2D u_textureB;\n\t\t#ifdef MIX_TEX\n\t\t\tuniform sampler2D u_textureMix;\n\t\t#else\n\t\t\tuniform vec4 u_mix;\n\t\t#endif\n\t\t\n\t\tvoid main() {\n\t\t\t#ifdef MIX_TEX\n\t\t\t vec4 f = texture2D(u_textureMix, v_coord);\n\t\t\t#else\n\t\t\t vec4 f = u_mix;\n\t\t\t#endif\n\t\t gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\t\t}\n\t\t", -F.registerNodeType("texture/mix",b),d.title="Edges",d.desc="Detects edges",d.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},d.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else if(a){this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var b=Mesh.getScreenQuad(),g=d._shader,f=this.properties.invert,e=this.properties.factor, -k=this.properties.threshold?1:0;this._tex.drawTo(function(){a.bind(0);g.uniforms({u_texture:0,u_isize:[1/a.width,1/a.height],u_factor:e,u_threshold:k,u_invert:f?1:0}).draw(b)});this.setOutputData(0,this._tex)}}},d.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_isize;\n\t\tuniform int u_invert;\n\t\tuniform float u_factor;\n\t\tuniform float u_threshold;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 center = texture2D(u_texture, v_coord);\n\t\t\tvec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\t\t\tvec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\t\t\tvec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\t\t\tvec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\t\t\tvec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\t\t\tdiff *= u_factor;\n\t\t\tif(u_invert == 1)\n\t\t\t\tdiff.xyz = vec3(1.0) - diff.xyz;\n\t\t\tif( u_threshold == 0.0 )\n\t\t\t\tgl_FragColor = vec4( diff.xyz, center.a );\n\t\t\telse\n\t\t\t\tgl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\t\t}\n\t\t", -F.registerNodeType("texture/edges",d),g.title="Depth Range",g.desc="Generates a texture with a depth range",g.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a){var b=gl.UNSIGNED_BYTE;this.properties.high_precision&&(b=gl.half_float_ext?gl.HALF_FLOAT_OES:gl.FLOAT);this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&&this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGBA, -filter:gl.LINEAR}));var d=this._uniforms,b=this.properties.distance;this.isInputConnected(1)&&(b=this.getInputData(1),this.properties.distance=b);var c=this.properties.range;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.range=c);d.u_distance=b;d.u_range=c;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var f=Mesh.getScreenQuad();g._shader||(g._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,g.pixel_shader),g._shader_onlydepth=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,g.pixel_shader, -{ONLY_DEPTH:""}));var e=this.properties.only_depth?g._shader_onlydepth:g._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1,1E3];d.u_camera_planes=b;this._temp_texture.drawTo(function(){a.bind(0);e.uniforms(d).draw(f)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},g.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform float u_distance;\n\t\tuniform float u_range;\n\t\t\n\t\tfloat LinearDepth()\n\t\t{\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\t\t\tdepth = depth * 2.0 - 1.0;\n\t\t\treturn zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t\tfloat depth = LinearDepth();\n\t\t\t#ifdef ONLY_DEPTH\n\t\t\t gl_FragColor = vec4(depth);\n\t\t\t#else\n\t\t\t\tfloat diff = abs(depth * u_camera_planes.y - u_distance);\n\t\t\t\tfloat dof = 1.0;\n\t\t\t\tif(diff <= u_range)\n\t\t\t\t\tdof = diff / u_range;\n\t\t\t gl_FragColor = vec4(dof);\n\t\t\t#endif\n\t\t}\n\t\t", -F.registerNodeType("texture/depth_range",g),f.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},f.title="Linear Depth",f.desc="Creates a color texture with linear depth",f.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a&&(a.format==gl.DEPTH_COMPONENT||a.format==gl.DEPTH_STENCIL)){var b=this.properties.precision==c.HIGH?gl.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&& -this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGB,filter:gl.LINEAR}));var d=this._uniforms;d.u_invert=this.properties.invert?1:0;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var g=Mesh.getScreenQuad();f._shader||(f._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,f.pixel_shader));var e=f._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1, -1E3];d.u_camera_planes=b;d.u_ires.set([0,0]);this._temp_texture.drawTo(function(){a.bind(0);e.uniforms(d).draw(g)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},f.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform int u_invert;\n\t\tuniform vec2 u_ires;\n\t\t\n\t\tvoid main() {\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord + u_ires*0.5).x * 2.0 - 1.0;\n\t\t\tfloat f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t\tif( u_invert == 1 )\n\t\t\t\tf = 1.0 - f;\n\t\t\tgl_FragColor = vec4(vec3(f),1.0);\n\t\t}\n\t\t", -F.registerNodeType("texture/linear_depth",f),r.title="Blur",r.desc="Blur a texture",r.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},r.max_iterations=20,r.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._final_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(b=this._final_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));var d=this.properties.iterations;this.isInputConnected(1)&& -(d=this.getInputData(1),this.properties.iterations=d);d=Math.min(Math.floor(d),r.max_iterations);if(0==d)this.setOutputData(0,a);else{var c=this.properties.intensity;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.intensity=c);var g=F.camera_aspect;g||void 0===window.gl||(g=gl.canvas.height/gl.canvas.width);g||(g=1);var g=this.properties.preserve_aspect?g:1,f=this.properties.scale||[1,1];a.applyBlur(g*f[0],f[1],c,b);for(a=1;a>=1;1<(d|0)&&(d>>=1);if(2>b)break;l=k[m]=GL.Texture.getTemporary(b,d,g);r[0]=1/p.width;r[1]=1/p.height;p.blit(l,h.uniforms(e));p=l}this.isOutputConnected(2)&&(b=this._average_texture,b&&b.type==a.type&&b.format==a.format||(b=this._average_texture=new GL.Texture(1,1,{type:a.type,format:a.format,filter:gl.LINEAR})),r[0]=1/p.width,r[1]=1/p.height,e.u_intensity=n,e.u_delta=1,p.blit(b,h.uniforms(e)),this.setOutputData(2,b));gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE);e.u_intensity= -this.getInputOrProperty("persistence");e.u_delta=0.5;for(m-=2;0<=m;m--)l=k[m],k[m]=null,r[0]=1/p.width,r[1]=1/p.height,p.blit(l,h.uniforms(e)),GL.Texture.releaseTemporary(p),p=l;gl.disable(gl.BLEND);this.isOutputConnected(1)&&(k=this._glow_texture,k&&k.width==a.width&&k.height==a.height&&k.type==f&&k.format==a.format||(k=this._glow_texture=new GL.Texture(a.width,a.height,{type:f,format:a.format,filter:gl.LINEAR})),p.blit(k),this.setOutputData(1,k));if(this.isOutputConnected(0)){k=this._final_texture; -k&&k.width==a.width&&k.height==a.height&&k.type==f&&k.format==a.format||(k=this._final_texture=new GL.Texture(a.width,a.height,{type:f,format:a.format,filter:gl.LINEAR}));var t=this.getInputData(1),u=this.getInputOrProperty("dirt_factor");e.u_intensity=n;h=t?s._dirt_final_shader:s._final_shader;h||(h=t?s._dirt_final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,s.final_pixel_shader,{USE_DIRT:""}):s._final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,s.final_pixel_shader));k.drawTo(function(){a.bind(0); -p.bind(1);t&&(h.setUniform("u_dirt_factor",u),h.setUniform("u_dirt_texture",t.bind(2)));h.toViewport(e)});this.setOutputData(0,k)}GL.Texture.releaseTemporary(p)}},s.cut_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform float u_threshold;\n\tvoid main() {\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\t}",s.scale_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\t}", -s.final_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform sampler2D u_glow_texture;\n\t#ifdef USE_DIRT\n\t\tuniform sampler2D u_dirt_texture;\n\t#endif\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\tuniform float u_dirt_factor;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tvec4 glow = sampleBox( v_coord );\n\t\t#ifdef USE_DIRT\n\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\t\t#endif\n\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\t}", -F.registerNodeType("texture/glow",s),I.title="Kuwahara Filter",I.desc="Filters a texture giving an artistic oil canvas painting",I.max_radius=10,I._shaders=[],I.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));b=this.properties.radius;b=Math.min(Math.floor(b),I.max_radius);if(0== -b)this.setOutputData(0,a);else{var d=this.properties.intensity,c=F.camera_aspect;c||void 0===window.gl||(c=gl.canvas.height/gl.canvas.width);c||(c=1);c=this.properties.preserve_aspect?c:1;I._shaders[b]||(I._shaders[b]=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,I.pixel_shader,{RADIUS:b.toFixed(0)}));var g=I._shaders[b],f=GL.Mesh.getScreenQuad();a.bind(0);this._temp_texture.drawTo(function(){g.uniforms({u_texture:0,u_intensity:d,u_resolution:[a.width,a.height],u_iResolution:[1/a.width,1/a.height]}).draw(f)}); -this.setOutputData(0,this._temp_texture)}}},I.pixel_shader="\nprecision highp float;\nvarying vec2 v_coord;\nuniform sampler2D u_texture;\nuniform float u_intensity;\nuniform vec2 u_resolution;\nuniform vec2 u_iResolution;\n#ifndef RADIUS\n\t#define RADIUS 7\n#endif\nvoid main() {\n\n\tconst int radius = RADIUS;\n\tvec2 fragCoord = v_coord;\n\tvec2 src_size = u_iResolution;\n\tvec2 uv = v_coord;\n\tfloat n = float((radius + 1) * (radius + 1));\n\tint i;\n\tint j;\n\tvec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\tvec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\tvec3 c;\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm0 += c;\n\t\t\ts0 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm1 += c;\n\t\t\ts1 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm2 += c;\n\t\t\ts2 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm3 += c;\n\t\t\ts3 += c * c;\n\t\t}\n\t}\n\t\n\tfloat min_sigma2 = 1e+2;\n\tm0 /= n;\n\ts0 = abs(s0 / n - m0 * m0);\n\t\n\tfloat sigma2 = s0.r + s0.g + s0.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m0, 1.0);\n\t}\n\t\n\tm1 /= n;\n\ts1 = abs(s1 / n - m1 * m1);\n\t\n\tsigma2 = s1.r + s1.g + s1.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m1, 1.0);\n\t}\n\t\n\tm2 /= n;\n\ts2 = abs(s2 / n - m2 * m2);\n\t\n\tsigma2 = s2.r + s2.g + s2.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m2, 1.0);\n\t}\n\t\n\tm3 /= n;\n\ts3 = abs(s3 / n - m3 * m3);\n\t\n\tsigma2 = s3.r + s3.g + s3.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m3, 1.0);\n\t}\n}\n", -F.registerNodeType("texture/kuwahara",I),J.title="XDoG Filter",J.desc="Filters a texture giving an artistic ink style",J.max_radius=10,J._shaders=[],J.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));J._xdog_shader||(J._xdog_shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,J.xdog_pixel_shader)); -var d=J._xdog_shader,c=GL.Mesh.getScreenQuad(),g=this.properties.sigma,f=this.properties.k,e=this.properties.p,k=this.properties.epsilon,h=this.properties.phi;a.bind(0);this._temp_texture.drawTo(function(){d.uniforms({src:0,sigma:g,k:f,p:e,epsilon:k,phi:h,cvsWidth:a.width,cvsHeight:a.height}).draw(c)});this.setOutputData(0,this._temp_texture)}},J.xdog_pixel_shader="\nprecision highp float;\nuniform sampler2D src;\n\nuniform float cvsHeight;\nuniform float cvsWidth;\n\nuniform float sigma;\nuniform float k;\nuniform float p;\nuniform float epsilon;\nuniform float phi;\nvarying vec2 v_coord;\n\nfloat cosh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat cosH = (tmp + 1.0 / tmp) / 2.0;\n\treturn cosH;\n}\n\nfloat tanh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\treturn tanH;\n}\n\nfloat sinh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat sinH = (tmp - 1.0 / tmp) / 2.0;\n\treturn sinH;\n}\n\nvoid main(void){\n\tvec3 destColor = vec3(0.0);\n\tfloat tFrag = 1.0 / cvsHeight;\n\tfloat sFrag = 1.0 / cvsWidth;\n\tvec2 Frag = vec2(sFrag,tFrag);\n\tvec2 uv = gl_FragCoord.st;\n\tfloat twoSigmaESquared = 2.0 * sigma * sigma;\n\tfloat twoSigmaRSquared = twoSigmaESquared * k * k;\n\tint halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\tconst int MAX_NUM_ITERATION = 99999;\n\tvec2 sum = vec2(0.0);\n\tvec2 norm = vec2(0.0);\n\n\tfor(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\t\tint i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\t\tint j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\t\tfloat d = length(vec2(i,j));\n\t\tvec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\t\t\t\t\t\t\texp( -d * d / twoSigmaRSquared ));\n\n\t\tvec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\t\tnorm += kernel;\n\t\tsum += kernel * L;\n\t}\n\n\tsum /= norm;\n\n\tfloat H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\tfloat edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\tdestColor = vec3(edge);\n\tgl_FragColor = vec4(destColor, 1.0);\n}", -F.registerNodeType("texture/xDoG",J),H.title="Webcam",H.desc="Webcam texture",H.is_webcam_open=!1,H.prototype.openStream=function(){function a(d){H.is_webcam_open=!1;console.log("Webcam rejected",d);b._webcam_stream=!1;b.boxcolor="red";b.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](a);var b=this}},H.prototype.closeStream=function(){if(this._webcam_stream){var a= -this._webcam_stream.getTracks();if(a.length)for(var b=0;b=this.size[1]||!this._video||(a.save(),a.webgl?this._video_texture&&a.drawImage(this._video_texture,0,0,this.size[0],this.size[1]):a.drawImage(this._video, -0,0,this.size[0],this.size[1]),a.restore())},H.prototype.onExecute=function(){null!=this._webcam_stream||this._waiting_confirmation||this.openStream();if(this._video&&this._video.videoWidth){var a=this._video.videoWidth,b=this._video.videoHeight,d=this._video_texture;d&&d.width==a&&d.height==b||(this._video_texture=new GL.Texture(a,b,{format:gl.RGB,filter:gl.LINEAR}));this._video_texture.uploadImage(this._video);this._video_texture.version=++this.version;this.properties.texture_name&&(c.getTexturesContainer()[this.properties.texture_name]= -this._video_texture);this.setOutputData(0,this._video_texture);for(a=1;aMath.abs(d))return c[1];d=(a-c[0])/d;return c[1]*(1-d)+g[1]*d}}return 0}},t.prototype.updateCurve=function(){for(var a=this._values,b=a.length/4,d=this.properties.split_channels,c=0;c=f;){e=0.5*(k+f)|0;c=a[e];if(c==d)break;if(f==k-1)return f;ca&&(a=1);this.points&&this.points.length==3*a||(this.points=new Float32Array(3*a));this.properties.generate_normals?this.normals&&this.normals.length==this.points.length||(this.normals=new Float32Array(this.points.length)):this.normals=null;var d=this._last_radius||this.properties.radius,c=this.properties.mode,f=this.getInputData(0);this._old_obj_version=f?f._version:null;this.points=m.generatePoints(d,a,c,this.points, -this.normals,this.properties.regular,f);this.version++};m.generatePoints=function(a,d,c,f,e,k,h){var p=3*d;f&&f.length==p||(f=new Float32Array(p));var l=new Float32Array(3),q=new Float32Array([0,1,0]);if(k)if(c==m.RECTANGLE){p=Math.floor(Math.sqrt(d));for(d=0;de||wk&&kh))break}this.geometry.indices=this.indices=new Uint32Array(p)}this.indices&&this.indices.length?(this.geometry.indices=this.indices,this.setOutputData(0,this.geometry)):this.setOutputData(0,null)}};v.registerNodeType("geometry/connectPoints",u);"undefined"!=typeof GL&&(D.title="to geometry",D.desc="converts a mesh to geometry",D.prototype.onExecute= -function(){var a=this.getInputData(0);if(a){if(a!=this.last_mesh){this.last_mesh=a;for(i in a.vertexBuffers)this.geometry[i]=a.vertexBuffers[i].data;a.indexBuffers.triangles&&(this.geometry.indices=a.indexBuffers.triangles.data);this.geometry._id=c();this.geometry._version=0}this.setOutputData(0,this.geometry);this.geometry&&this.setOutputData(1,this.geometry.vertices)}},v.registerNodeType("geometry/toGeometry",D),A.title="Geo to Mesh",A.prototype.updateMesh=function(a){this.mesh||(this.mesh=new GL.Mesh); -for(var d in a)if("_"!=d[0]){var c=a[d],f=GL.Mesh.common_buffers[d];if(f||"indices"==d){var f=f?f.spacing:3,e=this.mesh.vertexBuffers[d];e&&e.data.length==c.length?(e.data.set(c),e.upload(GL.DYNAMIC_DRAW)):e=new GL.Buffer("indices"==d?GL.ELEMENT_ARRAY_BUFFER:GL.ARRAY_BUFFER,c,f,GL.DYNAMIC_DRAW);this.mesh.addBuffer(d,e)}}if(this.mesh.vertexBuffers.normals&&this.mesh.vertexBuffers.normals.data.length!=this.mesh.vertexBuffers.vertices.data.length){c=new Float32Array([0,1,0]);f=new Float32Array(this.mesh.vertexBuffers.vertices.data.length); -for(d=0;d=c.NOTEON||h<=c.NOTEOFF)this.channel=e&15};Object.defineProperty(c.prototype,"velocity",{get:function(){return this.cmd==c.NOTEON?this.data[2]:-1},set:function(c){this.data[2]=c},enumerable:!0});c.notes="A A# B C C# D D# E F F# G G#".split(" ");c.note_to_index={A:0,"A#":1,B:2,C:3,"C#":4,D:5,"D#":6,E:7,F:8,"F#":9,G:10,"G#":11};Object.defineProperty(c.prototype,"note",{get:function(){return this.cmd!= -c.NOTEON?-1:c.toNoteString(this.data[1],!0)},set:function(c){throw"notes cannot be assigned this way, must modify the data[1]";},enumerable:!0});Object.defineProperty(c.prototype,"octave",{get:function(){return this.cmd!=c.NOTEON?-1:Math.floor((this.data[1]-24)/12+1)},set:function(c){throw"octave cannot be assigned this way, must modify the data[1]";},enumerable:!0});c.prototype.getPitch=function(){return 440*Math.pow(2,(this.data[1]-69)/12)};c.computePitch=function(c){return 440*Math.pow(2,(c-69)/ -12)};c.prototype.getCC=function(){return this.data[1]};c.prototype.getCCValue=function(){return this.data[2]};c.prototype.getPitchBend=function(){return this.data[1]+(this.data[2]<<7)-8192};c.computePitchBend=function(c,e){return c+(e<<7)-8192};c.prototype.setCommandFromString=function(e){this.cmd=c.computeCommandFromString(e)};c.computeCommandFromString=function(e){if(!e)return 0;if(e&&e.constructor===Number)return e;e=e.toUpperCase();switch(e){case "NOTE ON":case "NOTEON":return c.NOTEON;case "NOTE OFF":case "NOTEOFF":return c.NOTEON; -case "KEY PRESSURE":case "KEYPRESSURE":return c.KEYPRESSURE;case "CONTROLLER CHANGE":case "CONTROLLERCHANGE":case "CC":return c.CONTROLLERCHANGE;case "PROGRAM CHANGE":case "PROGRAMCHANGE":case "PC":return c.PROGRAMCHANGE;case "CHANNEL PRESSURE":case "CHANNELPRESSURE":return c.CHANNELPRESSURE;case "PITCH BEND":case "PITCHBEND":return c.PITCHBEND;case "TIME TICK":case "TIMETICK":return c.TIMETICK;default:return Number(e)}};c.toNoteString=function(e,h){e=Math.round(e);var k,a=Math.floor((e-24)/12+1); -k=(e-21)%12;0>k&&(k=12+k);return c.notes[k]+(h?"":a)};c.NoteStringToPitch=function(e){e=e.toUpperCase();var h=e[0],k=4;"#"==e[1]?(h+="#",2this.properties.max_value)return;this.trigger("on_midi",h)}};v.registerNodeType("midi/filter",e);C.title="MIDIEvent";C.desc="Create a MIDI Event";C.color="#243";C.prototype.onAction=function(e,h){"assign"==e?(this.properties.channel=h.channel,this.properties.cmd=h.cmd,this.properties.value1=h.data[1],this.properties.value2=h.data[2],h.cmd== -c.NOTEON?this.gate=!0:h.cmd==c.NOTEOFF&&(this.gate=!1)):(h=this.midi_event,h.channel=this.properties.channel,this.properties.cmd&&this.properties.cmd.constructor===String?h.setCommandFromString(this.properties.cmd):h.cmd=this.properties.cmd,h.data[0]=h.cmd|h.channel,h.data[1]=Number(this.properties.value1),h.data[2]=Number(this.properties.value2),this.trigger("on_midi",h))};C.prototype.onExecute=function(){var e=this.properties;if(this.inputs)for(var h=0;hc;++c)this.valid_notes[c]=-1!=this.notes_pitches.indexOf(c);for(c=0;12>c;++c)if(this.valid_notes[c])this.offset_notes[c]=0;else for(var e=1;12>e;++e){if(this.valid_notes[(c-e)%12]){this.offset_notes[c]=-e;break}if(this.valid_notes[(c+e)%12]){this.offset_notes[c]=e;break}}};A.prototype.onAction=function(e,h){h&&h.constructor===c&&(h.data[0]==c.NOTEON||h.data[0]==c.NOTEOFF?(this.midi_event=new c,this.midi_event.setup(h.data),this.midi_event.data[1]+=this.offset_notes[c.note_to_index[h.note]], -this.trigger("out",this.midi_event)):this.trigger("out",h))};A.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&c!=this._current_scale&&this.processScale(c)};v.registerNodeType("midi/quantize",A);h.title="MIDI fromFile";h.desc="Plays a MIDI file";h.color="#243";h.prototype.onAction=function(c){"play"==c?this.play():"pause"==c&&(this._playing=!this._playing)};h.prototype.onPropertyChanged=function(c,e){"url"==c&&this.loadMIDIFile(e)};h.prototype.onExecute=function(){if(this._midi&& -this._playing){this._current_time+=this.graph.elapsed_time;for(var e=100*this._current_time,h=0;hb;b++)for(var d=0;dg+f||c[1]>d))return b}}return-1};y.prototype.onAction=function(e,h){if("reset"==e)for(var k=0;kh[1])){var k=this.getKeyIndex(h);this.keys[k]=!0;this._last_key=k;var k=12*(this.properties.start_octave-1)+29+k,a=new c;a.setup([c.NOTEON,k,100]);this.trigger("note",a);return!0}};y.prototype.onMouseMove=function(e,h){if(!(0>h[1]||-1==this._last_key)){this.setDirtyCanvas(!0); -var k=this.getKeyIndex(h);if(this._last_key==k)return!0;this.keys[this._last_key]=!1;var a=12*(this.properties.start_octave-1)+29+this._last_key,b=new c;b.setup([c.NOTEOFF,a,100]);this.trigger("note",b);this.keys[k]=!0;a=12*(this.properties.start_octave-1)+29+k;b=new c;b.setup([c.NOTEON,a,100]);this.trigger("note",b);this._last_key=k;return!0}};y.prototype.onMouseUp=function(e,h){if(!(0>h[1])){var k=this.getKeyIndex(h);this.keys[k]=!1;this._last_key=-1;var k=12*(this.properties.start_octave-1)+29+ -k,a=new c;a.setup([c.NOTEOFF,k,100]);this.trigger("note",a);return!0}};v.registerNodeType("midi/keys",y)})(this); -(function(B){function c(){this.properties={src:"",gain:0.5,loop:!0,autoplay:!0,playbackRate:1};this._loading_audio=!1;this._audiobuffer=null;this._audionodes=[];this._last_sourcenode=null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=q.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain;this.properties.src&&this.loadSound(this.properties.src)}function m(){this.properties={gain:0.5};this._audionodes=[];this._media_stream= -null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=q.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain}function n(){this.properties={fftSize:2048,minDecibels:-100,maxDecibels:-10,smoothingTimeConstant:0.5};this.audionode=q.getAudioContext().createAnalyser();this.audionode.graphnode=this;this.audionode.fftSize=this.properties.fftSize;this.audionode.minDecibels=this.properties.minDecibels;this.audionode.maxDecibels= -this.properties.maxDecibels;this.audionode.smoothingTimeConstant=this.properties.smoothingTimeConstant;this.addInput("in","audio");this.addOutput("freqs","array");this.addOutput("samples","array");this._time_bin=this._freq_bin=null}function l(){this.properties={gain:1};this.audionode=q.getAudioContext().createGain();this.addInput("in","audio");this.addInput("gain","number");this.addOutput("out","audio")}function x(){this.properties={impulse_src:"",normalize:!0};this.audionode=q.getAudioContext().createConvolver(); -this.addInput("in","audio");this.addOutput("out","audio")}function e(){this.properties={threshold:-50,knee:40,ratio:12,reduction:-20,attack:0,release:0.25};this.audionode=q.getAudioContext().createDynamicsCompressor();this.addInput("in","audio");this.addOutput("out","audio")}function C(){this.properties={};this.audionode=q.getAudioContext().createWaveShaper();this.addInput("in","audio");this.addInput("shape","waveshape");this.addOutput("out","audio")}function w(){this.properties={gain1:0.5,gain2:0.5}; -this.audionode=q.getAudioContext().createGain();this.audionode1=q.getAudioContext().createGain();this.audionode1.gain.value=this.properties.gain1;this.audionode2=q.getAudioContext().createGain();this.audionode2.gain.value=this.properties.gain2;this.audionode1.connect(this.audionode);this.audionode2.connect(this.audionode);this.addInput("in1","audio");this.addInput("in1 gain","number");this.addInput("in2","audio");this.addInput("in2 gain","number");this.addOutput("out","audio")}function u(){this.properties= -{A:0.1,D:0.1,S:0.1,R:0.1};this.audionode=q.getAudioContext().createGain();this.audionode.gain.value=0;this.addInput("in","audio");this.addInput("gate","bool");this.addOutput("out","audio");this.gate=!1}function D(){this.properties={delayTime:0.5};this.audionode=q.getAudioContext().createDelay(10);this.audionode.delayTime.value=this.properties.delayTime;this.addInput("in","audio");this.addInput("time","number");this.addOutput("out","audio")}function A(){this.properties={frequency:350,detune:0,Q:1}; -this.addProperty("type","lowpass","enum",{values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")});this.audionode=q.getAudioContext().createBiquadFilter();this.addInput("in","audio");this.addOutput("out","audio")}function h(){this.properties={frequency:440,detune:0,type:"sine"};this.addProperty("type","sine","enum",{values:["sine","square","sawtooth","triangle","custom"]});this.audionode=q.getAudioContext().createOscillator();this.addOutput("out","audio")}function z(){this.properties= -{continuous:!0,mark:-1};this.addInput("data","array");this.addInput("mark","number");this.size=[300,200];this._last_buffer=null}function y(){this.properties={band:440,amplitude:1};this.addInput("freqs","array");this.addOutput("signal","number")}function v(){if(!v.default_code){var c=v.default_function.toString(),a=c.indexOf("{")+1,b=c.lastIndexOf("}");v.default_code=c.substr(a,b-a)}this.properties={code:v.default_code};c=q.getAudioContext();c.createScriptProcessor?this.audionode=c.createScriptProcessor(4096, -1,1):(console.warn("ScriptProcessorNode deprecated"),this.audionode=c.createGain());this.processCode();v._bypass_function||(v._bypass_function=this.audionode.onaudioprocess);this.addInput("in","audio");this.addOutput("out","audio")}function G(){this.audionode=q.getAudioContext().destination;this.addInput("in","audio")}var p=B.LiteGraph,q={};B.LGAudio=q;q.getAudioContext=function(){if(!this._audio_context){window.AudioContext=window.AudioContext||window.webkitAudioContext;if(!window.AudioContext)return console.error("AudioContext not supported by browser"), -null;this._audio_context=new AudioContext;this._audio_context.onmessage=function(c){console.log("msg",c)};this._audio_context.onended=function(c){console.log("ended",c)};this._audio_context.oncomplete=function(c){console.log("complete",c)}}return this._audio_context};q.connect=function(c,a){try{c.connect(a)}catch(b){console.warn("LGraphAudio:",b)}};q.disconnect=function(c,a){try{c.disconnect(a)}catch(b){console.warn("LGraphAudio:",b)}};q.changeAllAudiosConnections=function(c,a){if(c.inputs)for(var b= -0;b=this.size[0]&&(e=this.size[0]-1),c.strokeStyle="red",c.beginPath(),c.moveTo(e,d),c.lineTo(e,0),c.stroke())}};z.title="Visualization";z.desc="Audio Visualization";p.registerNodeType("audio/visualization",z);y.prototype.onExecute=function(){if(this._freqs=this.getInputData(0)){var c=this.properties.band,a=this.getInputData(1); -void 0!==a&&(c=a);a=q.getAudioContext().sampleRate/this._freqs.length;a=c/a*2;a>=this._freqs.length?a=this._freqs[this._freqs.length-1]:(c=a|0,a-=c,a=this._freqs[c]*(1-a)+this._freqs[c+1]*a);this.setOutputData(0,a/255*this.properties.amplitude)}};y.prototype.onGetInputs=function(){return[["band","number"]]};y.title="Signal";y.desc="extract the signal of some frequency";p.registerNodeType("audio/signal",y);v.prototype.onAdded=function(c){c.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess= -this._callback)};v["@code"]={widget:"code",type:"code"};v.prototype.onStart=function(){this.audionode.onaudioprocess=this._callback};v.prototype.onStop=function(){this.audionode.onaudioprocess=v._bypass_function};v.prototype.onPause=function(){this.audionode.onaudioprocess=v._bypass_function};v.prototype.onUnpause=function(){this.audionode.onaudioprocess=this._callback};v.prototype.onExecute=function(){};v.prototype.onRemoved=function(){this.audionode.onaudioprocess=v._bypass_function};v.prototype.processCode= -function(){try{this._script=new new Function("properties",this.properties.code)(this.properties),this._old_code=this.properties.code,this._callback=this._script.onaudioprocess}catch(c){console.error("Error in onaudioprocess code",c),this._callback=v._bypass_function,this.audionode.onaudioprocess=this._callback}};v.prototype.onPropertyChanged=function(c,a){"code"==c&&(this.properties.code=a,this.processCode(),this.graph&&this.graph.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback))}; -v.default_function=function(){this.onaudioprocess=function(c){var a=c.inputBuffer;c=c.outputBuffer;for(var b=0;b q && (q = Math.max(0, g + q)); - if (null == m || m > g) { - m = g; + var h = this.length || 0; + 0 > q && (q = Math.max(0, h + q)); + if (null == m || m > h) { + m = h; } m = Number(m); - 0 > m && (m = Math.max(0, g + m)); + 0 > m && (m = Math.max(0, h + m)); for (q = Number(q || 0); q < m; q++) { this[q] = c; } @@ -801,8 +81,8 @@ $jscomp.iteratorFromArray = function(v, c) { v instanceof String && (v += ""); var q = 0, m = {next:function() { if (q < v.length) { - var g = q++; - return {value:c(g, v[g]), done:!1}; + var h = q++; + return {value:c(h, v[h]), done:!1}; } m.next = function() { return {done:!0, value:void 0}; @@ -847,11 +127,11 @@ $jscomp.polyfill("Object.values", function(v) { this.clear(); a && this.configure(a); } - function q(a, b, d, h, f, e) { + function q(a, b, d, g, f, e) { this.id = a; this.type = b; this.origin_id = d; - this.origin_slot = h; + this.origin_slot = g; this.target_id = f; this.target_slot = e; this._data = null; @@ -860,7 +140,7 @@ $jscomp.polyfill("Object.values", function(v) { function m(a) { this._ctor(a); } - function g(a) { + function h(a) { this._ctor(a); } function r(a, b) { @@ -923,12 +203,12 @@ $jscomp.polyfill("Object.values", function(v) { function B(a, b) { return Math.sqrt((b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1])); } - function y(a, b, d, h, f, e) { - return d < a && d + f > a && h < b && h + e > b ? !0 : !1; + function y(a, b, d, g, f, e) { + return d < a && d + f > a && g < b && g + e > b ? !0 : !1; } function w(a, b) { - var d = a[0] + a[2], h = a[1] + a[3], f = b[1] + b[3]; - return a[0] > b[0] + b[2] || a[1] > f || d < b[0] || h < b[1] ? !1 : !0; + var d = a[0] + a[2], g = a[1] + a[3], f = b[1] + b[3]; + return a[0] > b[0] + b[2] || a[1] > f || d < b[0] || g < b[1] ? !1 : !0; } function E(a, b) { function d(a) { @@ -938,7 +218,7 @@ $jscomp.polyfill("Object.values", function(v) { return !0; } this.options = b = b || {}; - var h = this; + var g = this; b.parentMenu && (b.parentMenu.constructor !== this.constructor ? (console.error("parentMenu must be of class ContextMenu, ignoring it"), b.parentMenu = null) : (this.parentMenu = b.parentMenu, this.parentMenu.lock = !0, this.parentMenu.current_submenu = this)); var f = null; b.event && (f = b.event.constructor.name); @@ -965,7 +245,7 @@ $jscomp.polyfill("Object.values", function(v) { }, !0); e.addEventListener("mousedown", function(a) { if (2 == a.button) { - return h.close(), a.preventDefault(), !0; + return g.close(), a.preventDefault(), !0; } }, !0); b.scroll_speed || (b.scroll_speed = 0.1); @@ -981,7 +261,7 @@ $jscomp.polyfill("Object.values", function(v) { f++; } e.addEventListener("mouseleave", function(a) { - h.lock || (e.closing_timer && clearTimeout(e.closing_timer), e.closing_timer = setTimeout(h.close.bind(h, a), 500)); + g.lock || (e.closing_timer && clearTimeout(e.closing_timer), e.closing_timer = setTimeout(g.close.bind(g, a), 500)); }); e.addEventListener("mouseenter", function(a) { e.closing_timer && clearTimeout(e.closing_timer); @@ -992,12 +272,13 @@ $jscomp.polyfill("Object.values", function(v) { a.fullscreenElement ? a.fullscreenElement.appendChild(e) : a.body.appendChild(e); c = b.left || 0; a = b.top || 0; - b.event && (c = b.event.clientX - 10, a = b.event.clientY - 10, b.title && (a -= 20), b.parentMenu && (c = b.parentMenu.root.getBoundingClientRect(), c = c.left + c.width), f = document.body.getBoundingClientRect(), k = e.getBoundingClientRect(), c > f.width - k.width - 10 && (c = f.width - k.width - 10), a > f.height - k.height - 10 && (a = f.height - k.height - 10)); + b.event && (c = b.event.clientX - 10, a = b.event.clientY - 10, b.title && (a -= 20), b.parentMenu && (c = b.parentMenu.root.getBoundingClientRect(), c = c.left + c.width), f = document.body.getBoundingClientRect(), k = e.getBoundingClientRect(), 0 == f.height && console.error("document.body height is 0. That is dangerous, set html,body { height: 100%; }"), f.width && c > f.width - k.width - 10 && (c = f.width - k.width - 10), f.height && a > f.height - k.height - 10 && (a = f.height - k.height - + 10)); e.style.left = c + "px"; e.style.top = a + "px"; b.scale && (e.style.transform = "scale(" + b.scale + ")"); } - function z(a) { + function A(a) { this.points = a; this.nearest = this.selected = -1; this.size = null; @@ -1013,15 +294,15 @@ $jscomp.polyfill("Object.values", function(v) { b.type = a; e.debug && console.log("Node registered: " + a); a.split("/"); - var d = b.name, h = a.lastIndexOf("/"); - b.category = a.substr(0, h); + var d = b.name, g = a.lastIndexOf("/"); + b.category = a.substr(0, g); b.title || (b.title = d); if (b.prototype) { for (var f in m.prototype) { b.prototype[f] || (b.prototype[f] = m.prototype[f]); } } - if (h = this.registered_node_types[a]) { + if (g = this.registered_node_types[a]) { console.log("replacing node type: " + a); } else { if (Object.hasOwnProperty(b.prototype, "shape") || Object.defineProperty(b.prototype, "shape", {set:function(a) { @@ -1058,8 +339,8 @@ $jscomp.polyfill("Object.values", function(v) { if (e.onNodeTypeRegistered) { e.onNodeTypeRegistered(a, b); } - if (h && e.onNodeTypeReplaced) { - e.onNodeTypeReplaced(a, b, h); + if (g && e.onNodeTypeReplaced) { + e.onNodeTypeReplaced(a, b, g); } }, unregisterNodeType:function(a) { var b = a.constructor === String ? this.registered_node_types[a] : a; @@ -1068,11 +349,11 @@ $jscomp.polyfill("Object.values", function(v) { } delete this.registered_node_types[b.type]; b.constructor.name && delete this.Nodes[b.constructor.name]; - }, wrapFunctionAsNode:function(a, b, d, h, f) { + }, wrapFunctionAsNode:function(a, b, d, g, f) { for (var x = Array(b.length), c = "", k = e.getParameterNames(b), n = 0; n < k.length; ++n) { c += "this.addInput('" + k[n] + "'," + (d && d[n] ? "'" + d[n] + "'" : "0") + ");\n"; } - c += "this.addOutput('out'," + (h ? "'" + h + "'" : 0) + ");\n"; + c += "this.addOutput('out'," + (g ? "'" + g + "'" : 0) + ");\n"; f && (c += "this.properties = " + JSON.stringify(f) + ";\n"); d = Function(c); d.title = a.split("/").pop(); @@ -1088,25 +369,25 @@ $jscomp.polyfill("Object.values", function(v) { }, addNodeMethod:function(a, b) { m.prototype[a] = b; for (var d in this.registered_node_types) { - var h = this.registered_node_types[d]; - h.prototype[a] && (h.prototype["_" + a] = h.prototype[a]); - h.prototype[a] = b; + var g = this.registered_node_types[d]; + g.prototype[a] && (g.prototype["_" + a] = g.prototype[a]); + g.prototype[a] = b; } }, createNode:function(a, b, d) { - var h = this.registered_node_types[a]; - if (!h) { + var g = this.registered_node_types[a]; + if (!g) { return e.debug && console.log('GraphNode type "' + a + '" not registered.'), null; } - b = b || h.title || a; + b = b || g.title || a; var f = null; if (e.catch_exceptions) { try { - f = new h(b); - } catch (H) { - return console.error(H), null; + f = new g(b); + } catch (G) { + return console.error(G), null; } } else { - f = new h(b); + f = new g(b); } f.type = a; !f.title && b && (f.title = b); @@ -1125,17 +406,17 @@ $jscomp.polyfill("Object.values", function(v) { }, getNodeType:function(a) { return this.registered_node_types[a]; }, getNodeTypesInCategory:function(a, b) { - var d = [], h; - for (h in this.registered_node_types) { - var f = this.registered_node_types[h]; + var d = [], g; + for (g in this.registered_node_types) { + var f = this.registered_node_types[g]; b && f.filter && f.filter != b || ("" == a ? null == f.category && d.push(f) : f.category == a && d.push(f)); } return d; }, getNodeTypesCategories:function(a) { var b = {"":1}, d; for (d in this.registered_node_types) { - var h = this.registered_node_types[d]; - !h.category || h.skip_list || a && h.filter != a || (b[h.category] = 1); + var g = this.registered_node_types[d]; + !g.category || g.skip_list || a && g.filter != a || (b[g.category] = 1); } a = []; for (d in b) { @@ -1143,14 +424,14 @@ $jscomp.polyfill("Object.values", function(v) { } return a; }, reloadNodes:function(a) { - var b = document.getElementsByTagName("script"), d = [], h; - for (h in b) { - d.push(b[h]); + var b = document.getElementsByTagName("script"), d = [], g; + for (g in b) { + d.push(b[g]); } b = document.getElementsByTagName("head")[0]; a = document.location.href + a; - for (h in d) { - var f = d[h].src; + for (g in d) { + var f = d[g].src; if (f && f.substr(0, a.length) == a) { try { e.debug && console.log("Reloading: " + f); @@ -1158,10 +439,10 @@ $jscomp.polyfill("Object.values", function(v) { x.type = "text/javascript"; x.src = f; b.appendChild(x); - b.removeChild(d[h]); - } catch (H) { + b.removeChild(d[g]); + } catch (G) { if (e.throw_errors) { - throw H; + throw G; } e.debug && console.log("Error while reloading " + f); } @@ -1194,8 +475,8 @@ $jscomp.polyfill("Object.values", function(v) { a = a.split(","); b = b.split(","); for (var d = 0; d < a.length; ++d) { - for (var h = 0; h < b.length; ++h) { - if (a[d] == b[h]) { + for (var g = 0; g < b.length; ++g) { + if (a[d] == b[g]) { return !0; } } @@ -1203,7 +484,7 @@ $jscomp.polyfill("Object.values", function(v) { return !1; }, registerSearchboxExtra:function(a, b, d) { this.searchbox_extras[b.toLowerCase()] = {type:a, desc:b, data:d}; - }, fetchFile:function(a, b, d, h) { + }, fetchFile:function(a, b, d, g) { if (!a) { return null; } @@ -1229,7 +510,7 @@ $jscomp.polyfill("Object.values", function(v) { d && d(a); }).catch(function(b) { console.error("error fetching file:", a); - h && h(b); + g && g(b); }); } if (a.constructor === File || a.constructor === Blob) { @@ -1360,8 +641,8 @@ $jscomp.polyfill("Object.values", function(v) { }; c.prototype.runStep = function(a, b, d) { a = a || 1; - var h = e.getTime(); - this.globaltime = 0.001 * (h - this.starttime); + var g = e.getTime(); + this.globaltime = 0.001 * (g - this.starttime); var f = this._nodes_executable ? this._nodes_executable : this._nodes; if (f) { d = d || f.length; @@ -1408,10 +689,10 @@ $jscomp.polyfill("Object.values", function(v) { } } a = e.getTime(); - h = a - h; - 0 == h && (h = 1); - this.execution_time = 0.001 * h; - this.globaltime += 0.001 * h; + g = a - g; + 0 == g && (g = 1); + this.execution_time = 0.001 * g; + this.globaltime += 0.001 * g; this.iteration += 1; this.elapsed_time = 0.001 * (a - this.last_update_time); this.last_update_time = a; @@ -1425,25 +706,25 @@ $jscomp.polyfill("Object.values", function(v) { } }; c.prototype.computeExecutionOrder = function(a, b) { - for (var d = [], h = [], f = {}, x = {}, c = {}, k = 0, n = this._nodes.length; k < n; ++k) { + for (var d = [], g = [], f = {}, x = {}, c = {}, k = 0, n = this._nodes.length; k < n; ++k) { var p = this._nodes[k]; if (!a || p.onExecute) { f[p.id] = p; var l = 0; if (p.inputs) { - for (var t = 0, g = p.inputs.length; t < g; t++) { + for (var t = 0, h = p.inputs.length; t < h; t++) { p.inputs[t] && null != p.inputs[t].link && (l += 1); } } - 0 == l ? (h.push(p), b && (p._level = 1)) : (b && (p._level = 0), c[p.id] = l); + 0 == l ? (g.push(p), b && (p._level = 1)) : (b && (p._level = 0), c[p.id] = l); } } - for (; 0 != h.length;) { - if (p = h.shift(), d.push(p), delete f[p.id], p.outputs) { + for (; 0 != g.length;) { + if (p = g.shift(), d.push(p), delete f[p.id], p.outputs) { for (k = 0; k < p.outputs.length; k++) { if (a = p.outputs[k], null != a && null != a.links && 0 != a.links.length) { for (t = 0; t < a.links.length; t++) { - (n = this.links[a.links[t]]) && !x[n.id] && (l = this.getNodeById(n.target_id), null == l ? x[n.id] = !0 : (b && (!l._level || l._level <= p._level) && (l._level = p._level + 1), x[n.id] = !0, --c[l.id], 0 == c[l.id] && h.push(l))); + (n = this.links[a.links[t]]) && !x[n.id] && (l = this.getNodeById(n.target_id), null == l ? x[n.id] = !0 : (b && (!l._level || l._level <= p._level) && (l._level = p._level + 1), x[n.id] = !0, --c[l.id], 0 == c[l.id] && g.push(l))); } } } @@ -1467,10 +748,10 @@ $jscomp.polyfill("Object.values", function(v) { return d; }; c.prototype.getAncestors = function(a) { - for (var b = [], d = [a], h = {}; d.length;) { + for (var b = [], d = [a], g = {}; d.length;) { var f = d.shift(); if (f.inputs) { - h[f.id] || f == a || (h[f.id] = !0, b.push(f)); + g[f.id] || f == a || (g[f.id] = !0, b.push(f)); for (var e = 0; e < f.inputs.length; ++e) { var c = f.getInputNode(e); c && -1 == b.indexOf(c) && d.push(c); @@ -1484,14 +765,14 @@ $jscomp.polyfill("Object.values", function(v) { }; c.prototype.arrange = function(a) { a = a || 100; - for (var b = this.computeExecutionOrder(!1, !0), d = [], h = 0; h < b.length; ++h) { - var f = b[h], x = f._level || 1; + for (var b = this.computeExecutionOrder(!1, !0), d = [], g = 0; g < b.length; ++g) { + var f = b[g], x = f._level || 1; d[x] || (d[x] = []); d[x].push(f); } b = a; - for (h = 0; h < d.length; ++h) { - if (x = d[h]) { + for (g = 0; g < d.length; ++g) { + if (x = d[g]) { for (var c = 100, k = a + e.NODE_TITLE_HEIGHT, n = 0; n < x.length; ++n) { f = x[n], f.pos[0] = b, f.pos[1] = k, f.size[0] > c && (c = f.size[0]), k += f.size[1] + a + e.NODE_TITLE_HEIGHT; } @@ -1511,10 +792,10 @@ $jscomp.polyfill("Object.values", function(v) { }; c.prototype.sendEventToAllNodes = function(a, b, d) { d = d || e.ALWAYS; - var h = this._nodes_in_order ? this._nodes_in_order : this._nodes; - if (h) { - for (var f = 0, x = h.length; f < x; ++f) { - var c = h[f]; + var g = this._nodes_in_order ? this._nodes_in_order : this._nodes; + if (g) { + for (var f = 0, x = g.length; f < x; ++f) { + var c = g[f]; if (c.constructor === e.Subgraph && "onExecute" != a) { c.mode == d && c.sendEventToAllNodes(a, b, d); } else { @@ -1536,14 +817,14 @@ $jscomp.polyfill("Object.values", function(v) { c.prototype.sendActionToCanvas = function(a, b) { if (this.list_of_graphcanvas) { for (var d = 0; d < this.list_of_graphcanvas.length; ++d) { - var h = this.list_of_graphcanvas[d]; - h[a] && h[a].apply(h, b); + var g = this.list_of_graphcanvas[d]; + g[a] && g[a].apply(g, b); } } }; c.prototype.add = function(a, b) { if (a) { - if (a.constructor === g) { + if (a.constructor === h) { this._groups.push(a), this.setDirtyCanvas(!0), this.change(), a.graph = this, this._version++; } else { -1 != a.id && null != this._nodes_by_id[a.id] && (console.warn("LiteGraph: there is already a node with this ID, changing it"), a.id = ++this.last_node_id); @@ -1617,7 +898,7 @@ $jscomp.polyfill("Object.values", function(v) { }; c.prototype.findNodesByClass = function(a, b) { b = b || []; - for (var d = b.length = 0, h = this._nodes.length; d < h; ++d) { + for (var d = b.length = 0, g = this._nodes.length; d < g; ++d) { this._nodes[d].constructor === a && b.push(this._nodes[d]); } return b; @@ -1625,7 +906,7 @@ $jscomp.polyfill("Object.values", function(v) { c.prototype.findNodesByType = function(a, b) { a = a.toLowerCase(); b = b || []; - for (var d = b.length = 0, h = this._nodes.length; d < h; ++d) { + for (var d = b.length = 0, g = this._nodes.length; d < g; ++d) { this._nodes[d].type.toLowerCase() == a && b.push(this._nodes[d]); } return b; @@ -1639,16 +920,16 @@ $jscomp.polyfill("Object.values", function(v) { return null; }; c.prototype.findNodesByTitle = function(a) { - for (var b = [], d = 0, h = this._nodes.length; d < h; ++d) { + for (var b = [], d = 0, g = this._nodes.length; d < g; ++d) { this._nodes[d].title == a && b.push(this._nodes[d]); } return b; }; - c.prototype.getNodeOnPos = function(a, b, d, h) { + c.prototype.getNodeOnPos = function(a, b, d, g) { d = d || this._nodes; for (var f = d.length - 1; 0 <= f; f--) { var e = d[f]; - if (e.isPointInside(a, b, h)) { + if (e.isPointInside(a, b, g)) { return e; } } @@ -1656,9 +937,9 @@ $jscomp.polyfill("Object.values", function(v) { }; c.prototype.getGroupOnPos = function(a, b) { for (var d = this._groups.length - 1; 0 <= d; d--) { - var h = this._groups[d]; - if (h.isPointInside(a, b, 2, !0)) { - return h; + var g = this._groups[d]; + if (g.isPointInside(a, b, 2, !0)) { + return g; } } return null; @@ -1682,9 +963,9 @@ $jscomp.polyfill("Object.values", function(v) { c.prototype.onAction = function(a, b) { this._input_nodes = this.findNodesByClass(e.GraphInput, this._input_nodes); for (var d = 0; d < this._input_nodes.length; ++d) { - var h = this._input_nodes[d]; - if (h.properties.name == a) { - h.onAction(a, b); + var g = this._input_nodes[d]; + if (g.properties.name == a) { + g.onAction(a, b); break; } } @@ -1875,30 +1156,30 @@ $jscomp.polyfill("Object.values", function(v) { } d = []; for (b in this.links) { - var h = this.links[b]; - if (!h.serialize) { + var g = this.links[b]; + if (!g.serialize) { console.warn("weird LLink bug, link info is not a LLink but a regular object"); var f = new q; - for (b in h) { - f[b] = h[b]; + for (b in g) { + f[b] = g[b]; } - h = this.links[b] = f; + g = this.links[b] = f; } - d.push(h.serialize()); + d.push(g.serialize()); } - h = []; + g = []; for (b = 0; b < this._groups.length; ++b) { - h.push(this._groups[b].serialize()); + g.push(this._groups[b].serialize()); } - return {last_node_id:this.last_node_id, last_link_id:this.last_link_id, nodes:a, links:d, groups:h, config:this.config, version:e.VERSION}; + return {last_node_id:this.last_node_id, last_link_id:this.last_link_id, nodes:a, links:d, groups:g, config:this.config, version:e.VERSION}; }; c.prototype.configure = function(a, b) { if (a) { b || this.clear(); b = a.nodes; if (a.links && a.links.constructor === Array) { - for (var d = [], h = 0; h < a.links.length; ++h) { - var f = a.links[h]; + for (var d = [], g = 0; g < a.links.length; ++g) { + var f = a.links[g]; if (f) { var c = new q; c.configure(f); @@ -1909,29 +1190,29 @@ $jscomp.polyfill("Object.values", function(v) { } a.links = d; } - for (h in a) { - "nodes" != h && "groups" != h && (this[h] = a[h]); + for (g in a) { + "nodes" != g && "groups" != g && (this[g] = a[g]); } d = !1; this._nodes = []; if (b) { - h = 0; - for (f = b.length; h < f; ++h) { - c = b[h]; + g = 0; + for (f = b.length; g < f; ++g) { + c = b[g]; var k = e.createNode(c.type, c.title); k || (e.debug && console.log("Node not found or has errors: " + c.type), k = new m, k.last_serialization = c, d = k.has_errors = !0); k.id = c.id; this.add(k, !0); } - h = 0; - for (f = b.length; h < f; ++h) { - c = b[h], (k = this.getNodeById(c.id)) && k.configure(c); + g = 0; + for (f = b.length; g < f; ++g) { + c = b[g], (k = this.getNodeById(c.id)) && k.configure(c); } } this._groups.length = 0; if (a.groups) { - for (h = 0; h < a.groups.length; ++h) { - b = new e.LGraphGroup, b.configure(a.groups[h]), this.add(b); + for (g = 0; g < a.groups.length; ++g) { + b = new e.LGraphGroup, b.configure(a.groups[g]), this.add(b); } } this.updateExecutionOrder(); @@ -1998,8 +1279,8 @@ $jscomp.polyfill("Object.values", function(v) { if (this.inputs) { for (d = 0; d < this.inputs.length; ++d) { b = this.inputs[d]; - var h = this.graph ? this.graph.links[b.link] : null; - this.onConnectionsChange(e.INPUT, d, !0, h, b); + var g = this.graph ? this.graph.links[b.link] : null; + this.onConnectionsChange(e.INPUT, d, !0, g, b); } } if (this.outputs) { @@ -2007,7 +1288,7 @@ $jscomp.polyfill("Object.values", function(v) { var f = this.outputs[d]; if (f.links) { for (b = 0; b < f.links.length; ++b) { - h = this.graph ? this.graph.links[f.links[b]] : null, this.onConnectionsChange(e.OUTPUT, d, !0, h, f); + g = this.graph ? this.graph.links[f.links[b]] : null, this.onConnectionsChange(e.OUTPUT, d, !0, g, f); } } } @@ -2088,9 +1369,9 @@ $jscomp.polyfill("Object.values", function(v) { this.onPropertyChanged && !1 === this.onPropertyChanged(a, b, d) && (this.properties[a] = d); if (this.widgets) { for (d = 0; d < this.widgets.length; ++d) { - var h = this.widgets[d]; - if (h && h.options.property == a) { - h.value = b; + var g = this.widgets[d]; + if (g && g.options.property == a) { + g.value = b; break; } } @@ -2102,8 +1383,8 @@ $jscomp.polyfill("Object.values", function(v) { var d = this.outputs[a]; if (d && (d._data = b, this.outputs[a].links)) { for (d = 0; d < this.outputs[a].links.length; d++) { - var h = this.graph.links[this.outputs[a].links[d]]; - h && (h.data = b); + var g = this.graph.links[this.outputs[a].links[d]]; + g && (g.data = b); } } } @@ -2174,9 +1455,9 @@ $jscomp.polyfill("Object.values", function(v) { return this.properties ? this.properties[a] : null; } for (var b = 0, d = this.inputs.length; b < d; ++b) { - var h = this.inputs[b]; - if (a == h.name && null != h.link && (h = this.graph.links[h.link])) { - return h.data; + var g = this.inputs[b]; + if (a == g.name && null != g.link && (g = this.graph.links[g.link])) { + return g.data; } } return this.properties[a]; @@ -2210,8 +1491,8 @@ $jscomp.polyfill("Object.values", function(v) { return null; } for (var b = [], d = 0; d < a.links.length; d++) { - var h = this.graph.links[a.links[d]]; - h && (h = this.graph.getNodeById(h.target_id)) && b.push(h); + var g = this.graph.links[a.links[d]]; + g && (g = this.graph.getNodeById(g.target_id)) && b.push(g); } return b; }; @@ -2219,18 +1500,18 @@ $jscomp.polyfill("Object.values", function(v) { if (this.outputs && this.outputs.length) { this.graph && (this.graph._last_trigger_time = e.getTime()); for (var d = 0; d < this.outputs.length; ++d) { - var h = this.outputs[d]; - !h || h.type !== e.EVENT || a && h.name != a || this.triggerSlot(d, b); + var g = this.outputs[d]; + !g || g.type !== e.EVENT || a && g.name != a || this.triggerSlot(d, b); } } }; m.prototype.triggerSlot = function(a, b, d) { if (this.outputs && (a = this.outputs[a]) && (a = a.links) && a.length) { this.graph && (this.graph._last_trigger_time = e.getTime()); - for (var h = 0; h < a.length; ++h) { - var f = a[h]; + for (var g = 0; g < a.length; ++g) { + var f = a[g]; if (null == d || d == f) { - var c = this.graph.links[a[h]]; + var c = this.graph.links[a[g]]; if (c && (c._last_time = e.getTime(), f = this.graph.getNodeById(c.target_id))) { if (c = f.inputs[c.target_slot], f.onAction) { f.onAction(c.name, b); @@ -2247,20 +1528,20 @@ $jscomp.polyfill("Object.values", function(v) { m.prototype.clearTriggeredSlot = function(a, b) { if (this.outputs && (a = this.outputs[a]) && (a = a.links) && a.length) { for (var d = 0; d < a.length; ++d) { - var h = a[d]; - if (null == b || b == h) { - if (h = this.graph.links[a[d]]) { - h._last_time = 0; + var g = a[d]; + if (null == b || b == g) { + if (g = this.graph.links[a[d]]) { + g._last_time = 0; } } } } }; - m.prototype.addProperty = function(a, b, d, h) { + m.prototype.addProperty = function(a, b, d, g) { d = {name:a, type:d, default_value:b}; - if (h) { - for (var f in h) { - d[f] = h[f]; + if (g) { + for (var f in g) { + d[f] = g[f]; } } this.properties_info || (this.properties_info = []); @@ -2272,8 +1553,8 @@ $jscomp.polyfill("Object.values", function(v) { m.prototype.addOutput = function(a, b, d) { a = {name:a, type:b, links:null}; if (d) { - for (var h in d) { - a[h] = d[h]; + for (var g in d) { + a[g] = d[g]; } } this.outputs || (this.outputs = []); @@ -2287,16 +1568,16 @@ $jscomp.polyfill("Object.values", function(v) { }; m.prototype.addOutputs = function(a) { for (var b = 0; b < a.length; ++b) { - var d = a[b], h = {name:d[0], type:d[1], link:null}; + var d = a[b], g = {name:d[0], type:d[1], link:null}; if (a[2]) { for (var f in d[2]) { - h[f] = d[2][f]; + g[f] = d[2][f]; } } this.outputs || (this.outputs = []); - this.outputs.push(h); + this.outputs.push(g); if (this.onOutputAdded) { - this.onOutputAdded(h); + this.onOutputAdded(g); } } this.size = this.computeSize(); @@ -2307,8 +1588,8 @@ $jscomp.polyfill("Object.values", function(v) { this.outputs.splice(a, 1); for (var b = a; b < this.outputs.length; ++b) { if (this.outputs[b] && this.outputs[b].links) { - for (var d = this.outputs[b].links, h = 0; h < d.length; ++h) { - var f = this.graph.links[d[h]]; + for (var d = this.outputs[b].links, g = 0; g < d.length; ++g) { + var f = this.graph.links[d[g]]; f && --f.origin_slot; } } @@ -2322,8 +1603,8 @@ $jscomp.polyfill("Object.values", function(v) { m.prototype.addInput = function(a, b, d) { a = {name:a, type:b || 0, link:null}; if (d) { - for (var h in d) { - a[h] = d[h]; + for (var g in d) { + a[g] = d[g]; } } this.inputs || (this.inputs = []); @@ -2337,16 +1618,16 @@ $jscomp.polyfill("Object.values", function(v) { }; m.prototype.addInputs = function(a) { for (var b = 0; b < a.length; ++b) { - var d = a[b], h = {name:d[0], type:d[1], link:null}; + var d = a[b], g = {name:d[0], type:d[1], link:null}; if (a[2]) { for (var f in d[2]) { - h[f] = d[2][f]; + g[f] = d[2][f]; } } this.inputs || (this.inputs = []); - this.inputs.push(h); + this.inputs.push(g); if (this.onInputAdded) { - this.onInputAdded(h); + this.onInputAdded(g); } } this.size = this.computeSize(); @@ -2367,14 +1648,14 @@ $jscomp.polyfill("Object.values", function(v) { } this.setDirtyCanvas(!0, !0); }; - m.prototype.addConnection = function(a, b, d, h) { - a = {name:a, type:b, pos:d, direction:h, links:null}; + m.prototype.addConnection = function(a, b, d, g) { + a = {name:a, type:b, pos:d, direction:g, links:null}; this.connections.push(a); return a; }; m.prototype.computeSize = function(a, b) { function d(a) { - return a ? h * a.length * 0.6 : 0; + return a ? g * a.length * 0.6 : 0; } if (this.constructor.size) { return this.constructor.size.concat(); @@ -2382,7 +1663,7 @@ $jscomp.polyfill("Object.values", function(v) { a = Math.max(this.inputs ? this.inputs.length : 1, this.outputs ? this.outputs.length : 1); b = b || new Float32Array([0, 0]); a = Math.max(a, 1); - var h = e.NODE_TEXT_SIZE, f = d(this.title), c = 0, k = 0; + var g = e.NODE_TEXT_SIZE, f = d(this.title), c = 0, k = 0; if (this.inputs) { for (var n = 0, p = this.inputs.length; n < p; ++n) { var l = this.inputs[n]; @@ -2432,15 +1713,15 @@ $jscomp.polyfill("Object.values", function(v) { b.type || (b.type = typeof this.properties[a]); return b; }; - m.prototype.addWidget = function(a, b, d, h, f) { + m.prototype.addWidget = function(a, b, d, g, f) { this.widgets || (this.widgets = []); - !f && h && h.constructor === Object && (f = h, h = null); + !f && g && g.constructor === Object && (f = g, g = null); f && f.constructor === String && (f = {property:f}); - h && h.constructor === String && (f || (f = {}), f.property = h, h = null); - h && h.constructor !== Function && (console.warn("addWidget: callback must be a function"), h = null); - b = {type:a.toLowerCase(), name:b, value:d, callback:h, options:f || {}}; + g && g.constructor === String && (f || (f = {}), f.property = g, g = null); + g && g.constructor !== Function && (console.warn("addWidget: callback must be a function"), g = null); + b = {type:a.toLowerCase(), name:b, value:d, callback:g, options:f || {}}; void 0 !== b.options.y && (b.y = b.options.y); - h || b.options.callback || b.options.property || console.warn("LiteGraph addWidget(...) without a callback or property assigned"); + g || b.options.callback || b.options.property || console.warn("LiteGraph addWidget(...) without a callback or property assigned"); if ("combo" == a && !b.options.values) { throw "LiteGraph addWidget('combo',...) requires to pass values in options: { values:['red','blue'] }"; } @@ -2464,10 +1745,10 @@ $jscomp.polyfill("Object.values", function(v) { } return a; }; - m.prototype.isPointInside = function(a, b, d, h) { + m.prototype.isPointInside = function(a, b, d, g) { d = d || 0; var f = this.graph && this.graph.isLive() ? 0 : e.NODE_TITLE_HEIGHT; - h && (f = 0); + g && (f = 0); if (this.flags && this.flags.collapsed) { if (y(a, b, this.pos[0] - d, this.pos[1] - e.NODE_TITLE_HEIGHT - d, (this._collapsed_width || e.NODE_COLLAPSED_WIDTH) + 2 * d, e.NODE_TITLE_HEIGHT + 2 * d)) { return !0; @@ -2482,18 +1763,18 @@ $jscomp.polyfill("Object.values", function(v) { m.prototype.getSlotInPosition = function(a, b) { var d = new Float32Array(2); if (this.inputs) { - for (var h = 0, f = this.inputs.length; h < f; ++h) { - var e = this.inputs[h]; - this.getConnectionPos(!0, h, d); + for (var g = 0, f = this.inputs.length; g < f; ++g) { + var e = this.inputs[g]; + this.getConnectionPos(!0, g, d); if (y(a, b, d[0] - 10, d[1] - 5, 20, 10)) { - return {input:e, slot:h, link_pos:d}; + return {input:e, slot:g, link_pos:d}; } } } if (this.outputs) { - for (h = 0, f = this.outputs.length; h < f; ++h) { - if (e = this.outputs[h], this.getConnectionPos(!1, h, d), y(a, b, d[0] - 10, d[1] - 5, 20, 10)) { - return {output:e, slot:h, link_pos:d}; + for (g = 0, f = this.outputs.length; g < f; ++g) { + if (e = this.outputs[g], this.getConnectionPos(!1, g, d), y(a, b, d[0] - 10, d[1] - 5, 20, 10)) { + return {output:e, slot:g, link_pos:d}; } } } @@ -2555,20 +1836,20 @@ $jscomp.polyfill("Object.values", function(v) { } } null != b.inputs[d].link && b.disconnectInput(d); - var h = this.outputs[a]; - if (b.onConnectInput && !1 === b.onConnectInput(d, h.type, h)) { + var g = this.outputs[a]; + if (b.onConnectInput && !1 === b.onConnectInput(d, g.type, g, this, a)) { return null; } var f = b.inputs[d], c = null; - if (e.isValidConnection(h.type, f.type)) { + if (e.isValidConnection(g.type, f.type)) { c = new q(++this.graph.last_link_id, f.type, this.id, a, b.id, d); this.graph.links[c.id] = c; - null == h.links && (h.links = []); - h.links.push(c.id); + null == g.links && (g.links = []); + g.links.push(c.id); b.inputs[d].link = c.id; this.graph && this.graph._version++; if (this.onConnectionsChange) { - this.onConnectionsChange(e.OUTPUT, a, !0, c, h); + this.onConnectionsChange(e.OUTPUT, a, !0, c, g); } if (b.onConnectionsChange) { b.onConnectionsChange(e.INPUT, d, !0, c, f); @@ -2598,10 +1879,10 @@ $jscomp.polyfill("Object.values", function(v) { if (!b) { throw "Target Node not found"; } - for (var h = 0, f = d.links.length; h < f; h++) { - var c = d.links[h], k = this.graph.links[c]; + for (var g = 0, f = d.links.length; g < f; g++) { + var c = d.links[g], k = this.graph.links[c]; if (k.target_id == b.id) { - d.links.splice(h, 1); + d.links.splice(g, 1); var n = b.inputs[k.target_slot]; n.link = null; delete this.graph.links[c]; @@ -2620,9 +1901,9 @@ $jscomp.polyfill("Object.values", function(v) { } } } else { - h = 0; - for (f = d.links.length; h < f; h++) { - if (c = d.links[h], k = this.graph.links[c]) { + g = 0; + for (f = d.links.length; g < f; g++) { + if (c = d.links[g], k = this.graph.links[c]) { b = this.graph.getNodeById(k.target_id); this.graph && this.graph._version++; if (b) { @@ -2664,13 +1945,13 @@ $jscomp.polyfill("Object.values", function(v) { } var d = this.inputs[a].link; this.inputs[a].link = null; - var h = this.graph.links[d]; - if (h) { - var f = this.graph.getNodeById(h.origin_id); + var g = this.graph.links[d]; + if (g) { + var f = this.graph.getNodeById(g.origin_id); if (!f) { return !1; } - var c = f.outputs[h.origin_slot]; + var c = f.outputs[g.origin_slot]; if (!c || !c.links || 0 == c.links.length) { return !1; } @@ -2683,10 +1964,10 @@ $jscomp.polyfill("Object.values", function(v) { delete this.graph.links[d]; this.graph && this.graph._version++; if (this.onConnectionsChange) { - this.onConnectionsChange(e.INPUT, a, !1, h, b); + this.onConnectionsChange(e.INPUT, a, !1, g, b); } if (f.onConnectionsChange) { - f.onConnectionsChange(e.OUTPUT, k, !1, h, c); + f.onConnectionsChange(e.OUTPUT, k, !1, g, c); } this.graph && this.graph.onNodeConnectionChange && (this.graph.onNodeConnectionChange(e.OUTPUT, f, k), this.graph.onNodeConnectionChange(e.INPUT, this, a)); } @@ -2696,9 +1977,9 @@ $jscomp.polyfill("Object.values", function(v) { }; m.prototype.getConnectionPos = function(a, b, d) { d = d || new Float32Array(2); - var h = 0; - a && this.inputs && (h = this.inputs.length); - !a && this.outputs && (h = this.outputs.length); + var g = 0; + a && this.inputs && (g = this.inputs.length); + !a && this.outputs && (g = this.outputs.length); var f = 0.5 * e.NODE_SLOT_HEIGHT; if (this.flags.collapsed) { return b = this._collapsed_width || e.NODE_COLLAPSED_WIDTH, this.horizontal ? (d[0] = this.pos[0] + 0.5 * b, d[1] = a ? this.pos[1] - e.NODE_TITLE_HEIGHT : this.pos[1]) : (d[0] = a ? this.pos[0] : this.pos[0] + b, d[1] = this.pos[1] - 0.5 * e.NODE_TITLE_HEIGHT), d; @@ -2706,14 +1987,14 @@ $jscomp.polyfill("Object.values", function(v) { if (a && -1 == b) { return d[0] = this.pos[0] + 0.5 * e.NODE_TITLE_HEIGHT, d[1] = this.pos[1] + 0.5 * e.NODE_TITLE_HEIGHT, d; } - if (a && h > b && this.inputs[b].pos) { + if (a && g > b && this.inputs[b].pos) { return d[0] = this.pos[0] + this.inputs[b].pos[0], d[1] = this.pos[1] + this.inputs[b].pos[1], d; } - if (!a && h > b && this.outputs[b].pos) { + if (!a && g > b && this.outputs[b].pos) { return d[0] = this.pos[0] + this.outputs[b].pos[0], d[1] = this.pos[1] + this.outputs[b].pos[1], d; } if (this.horizontal) { - return d[0] = this.pos[0] + this.size[0] / h * (b + 0.5), d[1] = a ? this.pos[1] - e.NODE_TITLE_HEIGHT : this.pos[1] + this.size[1], d; + return d[0] = this.pos[0] + this.size[0] / g * (b + 0.5), d[1] = a ? this.pos[1] - e.NODE_TITLE_HEIGHT : this.pos[1] + this.size[1], d; } d[0] = a ? this.pos[0] + f : this.pos[0] + this.size[0] + 1 - f; d[1] = this.pos[1] + (b + 0.7) * e.NODE_SLOT_HEIGHT + (this.constructor.slot_start_y || 0); @@ -2746,9 +2027,9 @@ $jscomp.polyfill("Object.values", function(v) { m.prototype.captureInput = function(a) { if (this.graph && this.graph.list_of_graphcanvas) { for (var b = this.graph.list_of_graphcanvas, d = 0; d < b.length; ++d) { - var h = b[d]; - if (a || h.node_capturing_input == this) { - h.node_capturing_input = a ? this : null; + var g = b[d]; + if (a || g.node_capturing_input == this) { + g.node_capturing_input = a ? this : null; } } } @@ -2766,8 +2047,8 @@ $jscomp.polyfill("Object.values", function(v) { m.prototype.localToScreen = function(a, b, d) { return [(a + this.pos[0]) * d.scale + d.offset[0], (b + this.pos[1]) * d.scale + d.offset[1]]; }; - v.LGraphGroup = e.LGraphGroup = g; - g.prototype._ctor = function(a) { + v.LGraphGroup = e.LGraphGroup = h; + h.prototype._ctor = function(a) { this.title = a || "Group"; this.font_size = 24; this.color = l.node_colors.pale_blue ? l.node_colors.pale_blue.groupcolor : "#AAA"; @@ -2787,37 +2068,37 @@ $jscomp.polyfill("Object.values", function(v) { return this._size; }, enumerable:!0}); }; - g.prototype.configure = function(a) { + h.prototype.configure = function(a) { this.title = a.title; this._bounding.set(a.bounding); this.color = a.color; this.font = a.font; }; - g.prototype.serialize = function() { + h.prototype.serialize = function() { var a = this._bounding; return {title:this.title, bounding:[Math.round(a[0]), Math.round(a[1]), Math.round(a[2]), Math.round(a[3])], color:this.color, font:this.font}; }; - g.prototype.move = function(a, b, d) { + h.prototype.move = function(a, b, d) { this._pos[0] += a; this._pos[1] += b; if (!d) { for (d = 0; d < this._nodes.length; ++d) { - var h = this._nodes[d]; - h.pos[0] += a; - h.pos[1] += b; + var g = this._nodes[d]; + g.pos[0] += a; + g.pos[1] += b; } } }; - g.prototype.recomputeInsideNodes = function() { + h.prototype.recomputeInsideNodes = function() { this._nodes.length = 0; for (var a = this.graph._nodes, b = new Float32Array(4), d = 0; d < a.length; ++d) { - var h = a[d]; - h.getBounding(b); - w(this._bounding, b) && this._nodes.push(h); + var g = a[d]; + g.getBounding(b); + w(this._bounding, b) && this._nodes.push(g); } }; - g.prototype.isPointInside = m.prototype.isPointInside; - g.prototype.setDirtyCanvas = m.prototype.setDirtyCanvas; + h.prototype.isPointInside = m.prototype.isPointInside; + h.prototype.setDirtyCanvas = m.prototype.setDirtyCanvas; e.DragAndScale = r; r.prototype.bindEvents = function(a) { this.last_mouse = new Float32Array(2); @@ -2829,20 +2110,20 @@ $jscomp.polyfill("Object.values", function(v) { }; r.prototype.computeVisibleArea = function() { if (this.element) { - var a = -this.offset[0], b = -this.offset[1], d = a + this.element.width / this.scale, h = b + this.element.height / this.scale; + var a = -this.offset[0], b = -this.offset[1], d = a + this.element.width / this.scale, g = b + this.element.height / this.scale; this.visible_area[0] = a; this.visible_area[1] = b; this.visible_area[2] = d - a; - this.visible_area[3] = h - b; + this.visible_area[3] = g - b; } else { this.visible_area[0] = this.visible_area[1] = this.visible_area[2] = this.visible_area[3] = 0; } }; r.prototype.onMouse = function(a) { if (this.enabled) { - var b = this.element, d = b.getBoundingClientRect(), h = a.clientX - d.left; + var b = this.element, d = b.getBoundingClientRect(), g = a.clientX - d.left; d = a.clientY - d.top; - a.canvasx = h; + a.canvasx = g; a.canvasy = d; a.dragging = this.dragging; var f = !1; @@ -2851,7 +2132,7 @@ $jscomp.polyfill("Object.values", function(v) { this.dragging = !0, b.removeEventListener("mousemove", this._binded_mouse_callback), document.body.addEventListener("mousemove", this._binded_mouse_callback), document.body.addEventListener("mouseup", this._binded_mouse_callback); } else { if ("mousemove" == a.type) { - f || (b = h - this.last_mouse[0], f = d - this.last_mouse[1], this.dragging && this.mouseDrag(b, f)); + f || (b = g - this.last_mouse[0], f = d - this.last_mouse[1], this.dragging && this.mouseDrag(b, f)); } else { if ("mouseup" == a.type) { this.dragging = !1, document.body.removeEventListener("mousemove", this._binded_mouse_callback), document.body.removeEventListener("mouseup", this._binded_mouse_callback), b.addEventListener("mousemove", this._binded_mouse_callback); @@ -2862,7 +2143,7 @@ $jscomp.polyfill("Object.values", function(v) { } } } - this.last_mouse[0] = h; + this.last_mouse[0] = g; this.last_mouse[1] = d; a.preventDefault(); a.stopPropagation(); @@ -3075,25 +2356,25 @@ $jscomp.polyfill("Object.values", function(v) { this.canvas.removeEventListener("mousemove", this._mousemove_callback); b.document.addEventListener("mousemove", this._mousemove_callback, !0); b.document.addEventListener("mouseup", this._mouseup_callback, !0); - var d = this.graph.getNodeOnPos(a.canvasX, a.canvasY, this.visible_nodes, 5), h = !1, f = 300 > e.getTime() - this.last_mouseclick; + var d = this.graph.getNodeOnPos(a.canvasX, a.canvasY, this.visible_nodes, 5), g = !1, f = 300 > e.getTime() - this.last_mouseclick; this.canvas_mouse[0] = a.canvasX; this.canvas_mouse[1] = a.canvasY; this.canvas.focus(); e.closeAllContextMenus(b); if (!this.onMouse || 1 != this.onMouse(a)) { if (1 == a.which) { - a.ctrlKey && (this.dragging_rectangle = new Float32Array(4), this.dragging_rectangle[0] = a.canvasX, this.dragging_rectangle[1] = a.canvasY, this.dragging_rectangle[2] = 1, this.dragging_rectangle[3] = 1, h = !0); + a.ctrlKey && (this.dragging_rectangle = new Float32Array(4), this.dragging_rectangle[0] = a.canvasX, this.dragging_rectangle[1] = a.canvasY, this.dragging_rectangle[2] = 1, this.dragging_rectangle[3] = 1, g = !0); var c = !1; - if (d && this.allow_interaction && !h && !this.read_only) { + if (d && this.allow_interaction && !g && !this.read_only) { this.live_mode || d.flags.pinned || this.bringToFront(d); if (!this.connecting_node && !d.flags.collapsed && !this.live_mode) { - if (!h && !1 !== d.resizable && y(a.canvasX, a.canvasY, d.pos[0] + d.size[0] - 5, d.pos[1] + d.size[1] - 5, 10, 10)) { - this.resizing_node = d, this.canvas.style.cursor = "se-resize", h = !0; + if (!g && !1 !== d.resizable && y(a.canvasX, a.canvasY, d.pos[0] + d.size[0] - 5, d.pos[1] + d.size[1] - 5, 10, 10)) { + this.resizing_node = d, this.canvas.style.cursor = "se-resize", g = !0; } else { if (d.outputs) { for (var k = 0, n = d.outputs.length; k < n; ++k) { - var p = d.outputs[k], g = d.getConnectionPos(!1, k); - if (y(a.canvasX, a.canvasY, g[0] - 15, g[1] - 10, 30, 20)) { + var p = d.outputs[k], h = d.getConnectionPos(!1, k); + if (y(a.canvasX, a.canvasY, h[0] - 15, h[1] - 10, 30, 20)) { this.connecting_node = d; this.connecting_output = p; this.connecting_pos = d.getConnectionPos(!1, k); @@ -3108,14 +2389,14 @@ $jscomp.polyfill("Object.values", function(v) { d.onOutputClick(k, a); } } - h = !0; + g = !0; break; } } } if (d.inputs) { for (k = 0, n = d.inputs.length; k < n; ++k) { - if (p = d.inputs[k], g = d.getConnectionPos(!0, k), y(a.canvasX, a.canvasY, g[0] - 15, g[1] - 10, 30, 20)) { + if (p = d.inputs[k], h = d.getConnectionPos(!0, k), y(a.canvasX, a.canvasY, h[0] - 15, h[1] - 10, 30, 20)) { if (f) { if (d.onInputDblClick) { d.onInputDblClick(k, a); @@ -3126,19 +2407,19 @@ $jscomp.polyfill("Object.values", function(v) { } } if (null !== p.link) { - h = this.graph.links[p.link]; + g = this.graph.links[p.link]; d.disconnectInput(k); if (this.allow_reconnect_links || a.shiftKey) { - this.connecting_node = this.graph._nodes_by_id[h.origin_id], this.connecting_slot = h.origin_slot, this.connecting_output = this.connecting_node.outputs[this.connecting_slot], this.connecting_pos = this.connecting_node.getConnectionPos(!1, this.connecting_slot); + this.connecting_node = this.graph._nodes_by_id[g.origin_id], this.connecting_slot = g.origin_slot, this.connecting_output = this.connecting_node.outputs[this.connecting_slot], this.connecting_pos = this.connecting_node.getConnectionPos(!1, this.connecting_slot); } - h = this.dirty_bgcanvas = !0; + g = this.dirty_bgcanvas = !0; } } } } } } - if (!h) { + if (!g) { k = !1; if (n = this.processNodeWidgets(d, this.canvas_mouse, a)) { k = !0, this.node_widget = [d, n]; @@ -3170,7 +2451,7 @@ $jscomp.polyfill("Object.values", function(v) { f && !this.read_only && this.allow_searchbox && this.showSearchBox(a); c = !0; } - !h && c && this.allow_dragcanvas && (this.dragging_canvas = !0); + !g && c && this.allow_dragcanvas && (this.dragging_canvas = !0); } else { 2 != a.which && 3 == a.which && (this.read_only || this.processContextMenu(d, a)); } @@ -3210,10 +2491,10 @@ $jscomp.polyfill("Object.values", function(v) { } else { if (this.allow_interaction && !this.read_only) { this.connecting_node && (this.dirty_canvas = !0); - var h = this.graph.getNodeOnPos(a.canvasX, a.canvasY, this.visible_nodes); + var g = this.graph.getNodeOnPos(a.canvasX, a.canvasY, this.visible_nodes); b = 0; for (var f = this.graph._nodes.length; b < f; ++b) { - if (this.graph._nodes[b].mouseOver && h != this.graph._nodes[b]) { + if (this.graph._nodes[b].mouseOver && g != this.graph._nodes[b]) { this.graph._nodes[b].mouseOver = !1; if (this.node_over && this.node_over.onMouseLeave) { this.node_over.onMouseLeave(a); @@ -3222,18 +2503,18 @@ $jscomp.polyfill("Object.values", function(v) { this.dirty_canvas = !0; } } - if (h) { - if (!h.mouseOver && (h.mouseOver = !0, this.node_over = h, this.dirty_canvas = !0, h.onMouseEnter)) { - h.onMouseEnter(a); + if (g) { + if (!g.mouseOver && (g.mouseOver = !0, this.node_over = g, this.dirty_canvas = !0, g.onMouseEnter)) { + g.onMouseEnter(a); } - if (h.onMouseMove) { - h.onMouseMove(a, [a.canvasX - h.pos[0], a.canvasY - h.pos[1]], this); + if (g.onMouseMove) { + g.onMouseMove(a, [a.canvasX - g.pos[0], a.canvasY - g.pos[1]], this); } - if (this.connecting_node && (f = this._highlight_input || [0, 0], !this.isOverNodeBox(h, a.canvasX, a.canvasY))) { - var c = this.isOverNodeInput(h, a.canvasX, a.canvasY, f); - -1 != c && h.inputs[c] ? e.isValidConnection(this.connecting_output.type, h.inputs[c].type) && (this._highlight_input = f) : this._highlight_input = null; + if (this.connecting_node && (f = this._highlight_input || [0, 0], !this.isOverNodeBox(g, a.canvasX, a.canvasY))) { + var c = this.isOverNodeInput(g, a.canvasX, a.canvasY, f); + -1 != c && g.inputs[c] ? e.isValidConnection(this.connecting_output.type, g.inputs[c].type) && (this._highlight_input = f) : this._highlight_input = null; } - this.canvas && (y(a.canvasX, a.canvasY, h.pos[0] + h.size[0] - 5, h.pos[1] + h.size[1] - 5, 5, 5) ? this.canvas.style.cursor = "se-resize" : this.canvas.style.cursor = "crosshair"); + this.canvas && (y(a.canvasX, a.canvasY, g.pos[0] + g.size[0] - 5, g.pos[1] + g.size[1] - 5, 5, 5) ? this.canvas.style.cursor = "se-resize" : this.canvas.style.cursor = "crosshair"); } else { f = null; for (b = 0; b < this.visible_links.length; ++b) { @@ -3247,12 +2528,12 @@ $jscomp.polyfill("Object.values", function(v) { f != this.over_link_center && (this.over_link_center = f, this.dirty_canvas = !0); this.canvas && (this.canvas.style.cursor = ""); } - if (this.node_capturing_input && this.node_capturing_input != h && this.node_capturing_input.onMouseMove) { + if (this.node_capturing_input && this.node_capturing_input != g && this.node_capturing_input.onMouseMove) { this.node_capturing_input.onMouseMove(a, [a.canvasX - this.node_capturing_input.pos[0], a.canvasY - this.node_capturing_input.pos[1]], this); } if (this.node_dragged && !this.live_mode) { for (b in this.selected_nodes) { - h = this.selected_nodes[b], h.pos[0] += d[0] / this.ds.scale, h.pos[1] += d[1] / this.ds.scale; + g = this.selected_nodes[b], g.pos[0] += d[0] / this.ds.scale, g.pos[1] += d[1] / this.ds.scale; } this.dirty_bgcanvas = this.dirty_canvas = !0; } @@ -3261,17 +2542,19 @@ $jscomp.polyfill("Object.values", function(v) { this.resizing_node.size[1] = a.canvasY - this.resizing_node.pos[1]; d = Math.max(this.resizing_node.inputs ? this.resizing_node.inputs.length : 0, this.resizing_node.outputs ? this.resizing_node.outputs.length : 0); this.resizing_node.size[0] < e.NODE_MIN_WIDTH && (this.resizing_node.size[0] = e.NODE_MIN_WIDTH); - h = this.resizing_node.widgets; + g = this.resizing_node.widgets; c = 0; - if (h && h.length) { + if (g && g.length) { b = 0; - for (f = h.length; b < f; ++b) { - c = h[b].computeSize ? c + (h[b].computeSize(this.resizing_node.size[0])[1] + 4) : c + (e.NODE_WIDGET_HEIGHT + 4); + for (f = g.length; b < f; ++b) { + c = g[b].computeSize ? c + (g[b].computeSize(this.resizing_node.size[0])[1] + 4) : c + (e.NODE_WIDGET_HEIGHT + 4); } c += 8; } - b = d * e.NODE_SLOT_HEIGHT + c; - this.resizing_node.size[1] < b && (this.resizing_node.size[1] = b); + if (this.resizing_node.size[1] < n) { + var n = d * e.NODE_SLOT_HEIGHT + c; + this.resizing_node.size[1] = n; + } this.canvas.style.cursor = "se-resize"; this.dirty_bgcanvas = this.dirty_canvas = !0; } @@ -3301,14 +2584,14 @@ $jscomp.polyfill("Object.values", function(v) { b = this.graph._nodes; var d = new Float32Array(4); this.deselectAllNodes(); - var h = Math.abs(this.dragging_rectangle[2]), f = Math.abs(this.dragging_rectangle[3]), c = 0 > this.dragging_rectangle[3] ? this.dragging_rectangle[1] - f : this.dragging_rectangle[1]; - this.dragging_rectangle[0] = 0 > this.dragging_rectangle[2] ? this.dragging_rectangle[0] - h : this.dragging_rectangle[0]; + var g = Math.abs(this.dragging_rectangle[2]), f = Math.abs(this.dragging_rectangle[3]), c = 0 > this.dragging_rectangle[3] ? this.dragging_rectangle[1] - f : this.dragging_rectangle[1]; + this.dragging_rectangle[0] = 0 > this.dragging_rectangle[2] ? this.dragging_rectangle[0] - g : this.dragging_rectangle[0]; this.dragging_rectangle[1] = c; - this.dragging_rectangle[2] = h; + this.dragging_rectangle[2] = g; this.dragging_rectangle[3] = f; f = []; for (c = 0; c < b.length; ++c) { - h = b[c], h.getBounding(d), w(this.dragging_rectangle, d) && f.push(h); + g = b[c], g.getBounding(d), w(this.dragging_rectangle, d) && f.push(g); } f.length && this.selectNodes(f); } @@ -3316,9 +2599,9 @@ $jscomp.polyfill("Object.values", function(v) { } else { if (this.connecting_node) { this.dirty_bgcanvas = this.dirty_canvas = !0; - if (h = this.graph.getNodeOnPos(a.canvasX, a.canvasY, this.visible_nodes)) { - this.connecting_output.type == e.EVENT && this.isOverNodeBox(h, a.canvasX, a.canvasY) ? this.connecting_node.connect(this.connecting_slot, h, e.EVENT) : (b = this.isOverNodeInput(h, a.canvasX, a.canvasY), -1 != b ? this.connecting_node.connect(this.connecting_slot, h, b) : (b = h.getInputInfo(0), this.connecting_output.type == e.EVENT ? this.connecting_node.connect(this.connecting_slot, h, e.EVENT) : b && !b.link && e.isValidConnection(b.type && this.connecting_output.type) && this.connecting_node.connect(this.connecting_slot, - h, 0))); + if (g = this.graph.getNodeOnPos(a.canvasX, a.canvasY, this.visible_nodes)) { + this.connecting_output.type == e.EVENT && this.isOverNodeBox(g, a.canvasX, a.canvasY) ? this.connecting_node.connect(this.connecting_slot, g, e.EVENT) : (b = this.isOverNodeInput(g, a.canvasX, a.canvasY), -1 != b ? this.connecting_node.connect(this.connecting_slot, g, b) : (b = g.getInputInfo(0), this.connecting_output.type == e.EVENT ? this.connecting_node.connect(this.connecting_slot, g, e.EVENT) : b && !b.link && e.isValidConnection(b.type && this.connecting_output.type) && this.connecting_node.connect(this.connecting_slot, + g, 0))); } this.connecting_node = this.connecting_pos = this.connecting_output = null; this.connecting_slot = -1; @@ -3327,7 +2610,7 @@ $jscomp.polyfill("Object.values", function(v) { this.dirty_bgcanvas = this.dirty_canvas = !0, this.resizing_node = null; } else { if (this.node_dragged) { - (h = this.node_dragged) && 300 > a.click_time && y(a.canvasX, a.canvasY, h.pos[0], h.pos[1] - e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT) && h.collapse(); + (g = this.node_dragged) && 300 > a.click_time && y(a.canvasX, a.canvasY, g.pos[0], g.pos[1] - e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT) && g.collapse(); this.dirty_bgcanvas = this.dirty_canvas = !0; this.node_dragged.pos[0] = Math.round(this.node_dragged.pos[0]); this.node_dragged.pos[1] = Math.round(this.node_dragged.pos[1]); @@ -3337,8 +2620,8 @@ $jscomp.polyfill("Object.values", function(v) { } this.node_dragged = null; } else { - h = this.graph.getNodeOnPos(a.canvasX, a.canvasY, this.visible_nodes); - !h && 300 > a.click_time && this.deselectAllNodes(); + g = this.graph.getNodeOnPos(a.canvasX, a.canvasY, this.visible_nodes); + !g && 300 > a.click_time && this.deselectAllNodes(); this.dirty_canvas = !0; this.dragging_canvas = !1; if (this.node_over && this.node_over.onMouseUp) { @@ -3373,15 +2656,15 @@ $jscomp.polyfill("Object.values", function(v) { } }; l.prototype.isOverNodeBox = function(a, b, d) { - var h = e.NODE_TITLE_HEIGHT; - return y(b, d, a.pos[0] + 2, a.pos[1] + 2 - h, h - 4, h - 4) ? !0 : !1; + var g = e.NODE_TITLE_HEIGHT; + return y(b, d, a.pos[0] + 2, a.pos[1] + 2 - g, g - 4, g - 4) ? !0 : !1; }; - l.prototype.isOverNodeInput = function(a, b, d, h) { + l.prototype.isOverNodeInput = function(a, b, d, g) { if (a.inputs) { for (var f = 0, e = a.inputs.length; f < e; ++f) { var c = a.getConnectionPos(!0, f); if (a.horizontal ? y(b, d, c[0] - 5, c[1] - 10, 10, 20) : y(b, d, c[0] - 10, c[1] - 5, 40, 10)) { - return h && (h[0] = c[0], h[1] = c[1]), f; + return g && (g[0] = c[0], g[1] = c[1]), f; } } } @@ -3416,15 +2699,15 @@ $jscomp.polyfill("Object.values", function(v) { } }; l.prototype.copyToClipboard = function() { - var a = {nodes:[], links:[]}, b = 0, d = [], h; - for (h in this.selected_nodes) { - var f = this.selected_nodes[h]; + var a = {nodes:[], links:[]}, b = 0, d = [], g; + for (g in this.selected_nodes) { + var f = this.selected_nodes[g]; f._relative_id = b; d.push(f); b += 1; } - for (h = 0; h < d.length; ++h) { - if (f = d[h], b = f.clone()) { + for (g = 0; g < d.length; ++g) { + if (f = d[g], b = f.clone()) { if (a.nodes.push(b.serialize()), f.inputs && f.inputs.length) { for (b = 0; b < f.inputs.length; ++b) { var e = f.inputs[b]; @@ -3445,14 +2728,14 @@ $jscomp.polyfill("Object.values", function(v) { if (a) { a = JSON.parse(a); for (var b = [], d = 0; d < a.nodes.length; ++d) { - var h = a.nodes[d], f = e.createNode(h.type); - f && (f.configure(h), f.pos[0] += 5, f.pos[1] += 5, this.graph.add(f), b.push(f)); + var g = a.nodes[d], f = e.createNode(g.type); + f && (f.configure(g), f.pos[0] += 5, f.pos[1] += 5, this.graph.add(f), b.push(f)); } for (d = 0; d < a.links.length; ++d) { - h = a.links[d]; - f = b[h[0]]; - var c = b[h[2]]; - f && c ? f.connect(h[1], c, h[3]) : console.warn("Warning, nodes missing on pasting"); + g = a.links[d]; + f = b[g[0]]; + var c = b[g[2]]; + f && c ? f.connect(g[1], c, g[3]) : console.warn("Warning, nodes missing on pasting"); } this.selectNodes(b); } @@ -3463,7 +2746,7 @@ $jscomp.polyfill("Object.values", function(v) { var b = [a.canvasX, a.canvasY], d = this.graph.getNodeOnPos(b[0], b[1]); if (d) { if ((d.onDropFile || d.onDropData) && (b = a.dataTransfer.files) && b.length) { - for (var h = 0; h < b.length; h++) { + for (var g = 0; g < b.length; g++) { var f = a.dataTransfer.files[0], e = f.name; l.getFileExtension(e); if (d.onDropFile) { @@ -3525,13 +2808,13 @@ $jscomp.polyfill("Object.values", function(v) { d.is_selected = !0; this.selected_nodes[d.id] = d; if (d.inputs) { - for (var h = 0; h < d.inputs.length; ++h) { - this.highlighted_links[d.inputs[h].link] = !0; + for (var g = 0; g < d.inputs.length; ++g) { + this.highlighted_links[d.inputs[g].link] = !0; } } if (d.outputs) { - for (h = 0; h < d.outputs.length; ++h) { - var f = d.outputs[h]; + for (g = 0; g < d.outputs.length; ++g) { + var f = d.outputs[g]; if (f.links) { for (var e = 0; e < f.links.length; ++e) { this.highlighted_links[f.links[e]] = !0; @@ -3564,8 +2847,8 @@ $jscomp.polyfill("Object.values", function(v) { for (b = 0; b < a.outputs.length; ++b) { var d = a.outputs[b]; if (d.links) { - for (var h = 0; h < d.links.length; ++h) { - delete this.highlighted_links[d.links[h]]; + for (var g = 0; g < d.links.length; ++g) { + delete this.highlighted_links[d.links[g]]; } } } @@ -3575,14 +2858,14 @@ $jscomp.polyfill("Object.values", function(v) { l.prototype.deselectAllNodes = function() { if (this.graph) { for (var a = this.graph._nodes, b = 0, d = a.length; b < d; ++b) { - var h = a[b]; - if (h.is_selected) { - if (h.onDeselected) { - h.onDeselected(); + var g = a[b]; + if (g.is_selected) { + if (g.onDeselected) { + g.onDeselected(); } - h.is_selected = !1; + g.is_selected = !1; if (this.onNodeDeselected) { - this.onNodeDeselected(h); + this.onNodeDeselected(g); } } } @@ -3599,8 +2882,8 @@ $jscomp.polyfill("Object.values", function(v) { for (var a in this.selected_nodes) { var b = this.selected_nodes[a]; if (b.inputs && b.inputs.length && b.outputs && b.outputs.length && e.isValidConnection(b.inputs[0].type, b.outputs[0].type) && b.inputs[0].link && b.outputs[0].links && b.outputs[0].links.length) { - var d = b.graph.links[b.inputs[0].link], h = b.graph.links[b.outputs[0].links[0]], f = b.getInputNode(0), c = b.getOutputNodes(0)[0]; - f && c && f.connect(d.origin_slot, c, h.target_slot); + var d = b.graph.links[b.inputs[0].link], g = b.graph.links[b.outputs[0].links[0]], f = b.getInputNode(0), c = b.getOutputNodes(0)[0]; + f && c && f.connect(d.origin_slot, c, g.target_slot); } this.graph.remove(b); if (this.onNodeDeselected) { @@ -3659,7 +2942,7 @@ $jscomp.polyfill("Object.values", function(v) { b = b || []; b.length = 0; a = a || this.graph._nodes; - for (var d = 0, h = a.length; d < h; ++d) { + for (var d = 0, g = a.length; d < g; ++d) { var f = a[d]; (!this.live_mode || f.onDrawBackground || f.onDrawForeground) && w(this.visible_area, f.getBounding(C)) && b.push(f); } @@ -3698,10 +2981,10 @@ $jscomp.polyfill("Object.values", function(v) { this.ds.toCanvasContext(a); b = this.computeVisibleNodes(null, this.visible_nodes); for (var d = 0; d < b.length; ++d) { - var h = b[d]; + var g = b[d]; a.save(); - a.translate(h.pos[0], h.pos[1]); - this.drawNode(h, a); + a.translate(g.pos[0], g.pos[1]); + this.drawNode(g, a); a.restore(); } this.render_execution_order && this.drawExecutionOrder(a); @@ -3771,10 +3054,10 @@ $jscomp.polyfill("Object.values", function(v) { b.font = "40px Arial"; b.textAlign = "center"; b.fillStyle = d.bgcolor || "#AAA"; - for (var h = "", f = 1; f < this._graph_stack.length; ++f) { - h += this._graph_stack[f]._subgraph_node.getTitle() + " >> "; + for (var g = "", f = 1; f < this._graph_stack.length; ++f) { + g += this._graph_stack[f]._subgraph_node.getTitle() + " >> "; } - b.fillText(h + d.getTitle(), 0.5 * a.width, 40); + b.fillText(g + d.getTitle(), 0.5 * a.width, 40); b.restore(); } d = !1; @@ -3821,7 +3104,7 @@ $jscomp.polyfill("Object.values", function(v) { var D = new Float32Array(2); l.prototype.drawNode = function(a, b) { this.current_node = a; - var d = a.color || a.constructor.color || e.NODE_DEFAULT_COLOR, h = a.bgcolor || a.constructor.bgcolor || e.NODE_DEFAULT_BGCOLOR, f = 0.6 > this.ds.scale; + var d = a.color || a.constructor.color || e.NODE_DEFAULT_COLOR, g = a.bgcolor || a.constructor.bgcolor || e.NODE_DEFAULT_BGCOLOR, f = 0.6 > this.ds.scale; if (this.live_mode) { if (!a.flags.collapsed && (b.shadowColor = "transparent", a.onDrawForeground)) { a.onDrawForeground(b, this, this.canvas); @@ -3840,15 +3123,15 @@ $jscomp.polyfill("Object.values", function(v) { null != p && (a._collapsed_width = Math.min(a.size[0], b.measureText(p).width + 2 * e.NODE_TITLE_HEIGHT), D[0] = a._collapsed_width, D[1] = 0); } a.clip_area && (b.save(), b.beginPath(), k == e.BOX_SHAPE ? b.rect(0, 0, D[0], D[1]) : k == e.ROUND_SHAPE ? b.roundRect(0, 0, D[0], D[1], 10) : k == e.CIRCLE_SHAPE && b.arc(0.5 * D[0], 0.5 * D[1], 0.5 * D[0], 0, 2 * Math.PI), b.clip()); - a.has_errors && (h = "red"); - this.drawNodeShape(a, b, D, d, h, a.is_selected, a.mouseOver); + a.has_errors && (g = "red"); + this.drawNodeShape(a, b, D, d, g, a.is_selected, a.mouseOver); b.shadowColor = "transparent"; if (a.onDrawForeground) { a.onDrawForeground(b, this, this.canvas); } b.textAlign = n ? "center" : "left"; b.font = this.inner_text_font; - h = !f; + g = !f; k = this.connecting_output; b.lineWidth = 1; p = 0; @@ -3856,20 +3139,20 @@ $jscomp.polyfill("Object.values", function(v) { if (!a.flags.collapsed) { if (a.inputs) { for (d = 0; d < a.inputs.length; d++) { - var g = a.inputs[d]; + var h = a.inputs[d]; b.globalAlpha = c; - this.connecting_node && !e.isValidConnection(g.type, k.type) && (b.globalAlpha = 0.4 * c); - b.fillStyle = null != g.link ? g.color_on || this.default_connection_color.input_on : g.color_off || this.default_connection_color.input_off; + this.connecting_node && !e.isValidConnection(h.type, k.type) && (b.globalAlpha = 0.4 * c); + b.fillStyle = null != h.link ? h.color_on || this.default_connection_color.input_on : h.color_off || this.default_connection_color.input_off; var t = a.getConnectionPos(!0, d, l); t[0] -= a.pos[0]; t[1] -= a.pos[1]; p < t[1] + 0.5 * e.NODE_SLOT_HEIGHT && (p = t[1] + 0.5 * e.NODE_SLOT_HEIGHT); b.beginPath(); - g.type === e.EVENT || g.shape === e.BOX_SHAPE ? n ? b.rect(t[0] - 5 + 0.5, t[1] - 8 + 0.5, 10, 14) : b.rect(t[0] - 6 + 0.5, t[1] - 5 + 0.5, 14, 10) : g.shape === e.ARROW_SHAPE ? (b.moveTo(t[0] + 8, t[1] + 0.5), b.lineTo(t[0] - 4, t[1] + 6 + 0.5), b.lineTo(t[0] - 4, t[1] - 6 + 0.5), b.closePath()) : f ? b.rect(t[0] - 4, t[1] - 4, 8, 8) : b.arc(t[0], t[1], 4, 0, 2 * Math.PI); + h.type === e.EVENT || h.shape === e.BOX_SHAPE ? n ? b.rect(t[0] - 5 + 0.5, t[1] - 8 + 0.5, 10, 14) : b.rect(t[0] - 6 + 0.5, t[1] - 5 + 0.5, 14, 10) : h.shape === e.ARROW_SHAPE ? (b.moveTo(t[0] + 8, t[1] + 0.5), b.lineTo(t[0] - 4, t[1] + 6 + 0.5), b.lineTo(t[0] - 4, t[1] - 6 + 0.5), b.closePath()) : f ? b.rect(t[0] - 4, t[1] - 4, 8, 8) : b.arc(t[0], t[1], 4, 0, 2 * Math.PI); b.fill(); - if (h) { - var m = null != g.label ? g.label : g.name; - m && (b.fillStyle = e.NODE_TEXT_COLOR, n || g.dir == e.UP ? b.fillText(m, t[0], t[1] - 10) : b.fillText(m, t[0] + 10, t[1] + 5)); + if (g) { + var m = null != h.label ? h.label : h.name; + m && (b.fillStyle = e.NODE_TEXT_COLOR, n || h.dir == e.UP ? b.fillText(m, t[0], t[1] - 10) : b.fillText(m, t[0] + 10, t[1] + 5)); } } } @@ -3878,40 +3161,40 @@ $jscomp.polyfill("Object.values", function(v) { b.strokeStyle = "black"; if (a.outputs) { for (d = 0; d < a.outputs.length; d++) { - if (g = a.outputs[d], t = a.getConnectionPos(!1, d, l), t[0] -= a.pos[0], t[1] -= a.pos[1], p < t[1] + 0.5 * e.NODE_SLOT_HEIGHT && (p = t[1] + 0.5 * e.NODE_SLOT_HEIGHT), b.fillStyle = g.links && g.links.length ? g.color_on || this.default_connection_color.output_on : g.color_off || this.default_connection_color.output_off, b.beginPath(), g.type === e.EVENT || g.shape === e.BOX_SHAPE ? n ? b.rect(t[0] - 5 + 0.5, t[1] - 8 + 0.5, 10, 14) : b.rect(t[0] - 6 + 0.5, t[1] - 5 + 0.5, 14, 10) : - g.shape === e.ARROW_SHAPE ? (b.moveTo(t[0] + 8, t[1] + 0.5), b.lineTo(t[0] - 4, t[1] + 6 + 0.5), b.lineTo(t[0] - 4, t[1] - 6 + 0.5), b.closePath()) : f ? b.rect(t[0] - 4, t[1] - 4, 8, 8) : b.arc(t[0], t[1], 4, 0, 2 * Math.PI), b.fill(), f || b.stroke(), h && (m = null != g.label ? g.label : g.name)) { - b.fillStyle = e.NODE_TEXT_COLOR, n || g.dir == e.DOWN ? b.fillText(m, t[0], t[1] - 8) : b.fillText(m, t[0] - 10, t[1] + 5); + if (h = a.outputs[d], t = a.getConnectionPos(!1, d, l), t[0] -= a.pos[0], t[1] -= a.pos[1], p < t[1] + 0.5 * e.NODE_SLOT_HEIGHT && (p = t[1] + 0.5 * e.NODE_SLOT_HEIGHT), b.fillStyle = h.links && h.links.length ? h.color_on || this.default_connection_color.output_on : h.color_off || this.default_connection_color.output_off, b.beginPath(), h.type === e.EVENT || h.shape === e.BOX_SHAPE ? n ? b.rect(t[0] - 5 + 0.5, t[1] - 8 + 0.5, 10, 14) : b.rect(t[0] - 6 + 0.5, t[1] - 5 + 0.5, 14, 10) : + h.shape === e.ARROW_SHAPE ? (b.moveTo(t[0] + 8, t[1] + 0.5), b.lineTo(t[0] - 4, t[1] + 6 + 0.5), b.lineTo(t[0] - 4, t[1] - 6 + 0.5), b.closePath()) : f ? b.rect(t[0] - 4, t[1] - 4, 8, 8) : b.arc(t[0], t[1], 4, 0, 2 * Math.PI), b.fill(), f || b.stroke(), g && (m = null != h.label ? h.label : h.name)) { + b.fillStyle = e.NODE_TEXT_COLOR, n || h.dir == e.DOWN ? b.fillText(m, t[0], t[1] - 8) : b.fillText(m, t[0] - 10, t[1] + 5); } } } b.textAlign = "left"; b.globalAlpha = 1; if (a.widgets) { - g = p; + h = p; if (n || a.widgets_up) { - g = 2; + h = 2; } - null != a.widgets_start_y && (g = a.widgets_start_y); - this.drawNodeWidgets(a, g, b, this.node_widget && this.node_widget[0] == a ? this.node_widget[1] : null); + null != a.widgets_start_y && (h = a.widgets_start_y); + this.drawNodeWidgets(a, h, b, this.node_widget && this.node_widget[0] == a ? this.node_widget[1] : null); } } else { if (this.render_collapsed_slots) { f = c = null; if (a.inputs) { for (d = 0; d < a.inputs.length; d++) { - if (g = a.inputs[d], null != g.link) { - c = g; + if (h = a.inputs[d], null != h.link) { + c = h; break; } } } if (a.outputs) { for (d = 0; d < a.outputs.length; d++) { - g = a.outputs[d], g.links && g.links.length && (f = g); + h = a.outputs[d], h.links && h.links.length && (f = h); } } - c && (c = 0, d = -0.5 * e.NODE_TITLE_HEIGHT, n && (c = 0.5 * a._collapsed_width, d = -e.NODE_TITLE_HEIGHT), b.fillStyle = "#686", b.beginPath(), g.type === e.EVENT || g.shape === e.BOX_SHAPE ? b.rect(c - 7 + 0.5, d - 4, 14, 8) : g.shape === e.ARROW_SHAPE ? (b.moveTo(c + 8, d), b.lineTo(c + -4, d - 4), b.lineTo(c + -4, d + 4), b.closePath()) : b.arc(c, d, 4, 0, 2 * Math.PI), b.fill()); - f && (c = a._collapsed_width, d = -0.5 * e.NODE_TITLE_HEIGHT, n && (c = 0.5 * a._collapsed_width, d = 0), b.fillStyle = "#686", b.strokeStyle = "black", b.beginPath(), g.type === e.EVENT || g.shape === e.BOX_SHAPE ? b.rect(c - 7 + 0.5, d - 4, 14, 8) : g.shape === e.ARROW_SHAPE ? (b.moveTo(c + 6, d), b.lineTo(c - 6, d - 4), b.lineTo(c - 6, d + 4), b.closePath()) : b.arc(c, d, 4, 0, 2 * Math.PI), b.fill()); + c && (c = 0, d = -0.5 * e.NODE_TITLE_HEIGHT, n && (c = 0.5 * a._collapsed_width, d = -e.NODE_TITLE_HEIGHT), b.fillStyle = "#686", b.beginPath(), h.type === e.EVENT || h.shape === e.BOX_SHAPE ? b.rect(c - 7 + 0.5, d - 4, 14, 8) : h.shape === e.ARROW_SHAPE ? (b.moveTo(c + 8, d), b.lineTo(c + -4, d - 4), b.lineTo(c + -4, d + 4), b.closePath()) : b.arc(c, d, 4, 0, 2 * Math.PI), b.fill()); + f && (c = a._collapsed_width, d = -0.5 * e.NODE_TITLE_HEIGHT, n && (c = 0.5 * a._collapsed_width, d = 0), b.fillStyle = "#686", b.strokeStyle = "black", b.beginPath(), h.type === e.EVENT || h.shape === e.BOX_SHAPE ? b.rect(c - 7 + 0.5, d - 4, 14, 8) : h.shape === e.ARROW_SHAPE ? (b.moveTo(c + 6, d), b.lineTo(c - 6, d - 4), b.lineTo(c - 6, d + 4), b.closePath()) : b.arc(c, d, 4, 0, 2 * Math.PI), b.fill()); } } a.clip_area && b.restore(); @@ -3928,14 +3211,14 @@ $jscomp.polyfill("Object.values", function(v) { if (null != b.data && (!this.onDrawLinkTooltip || 1 != this.onDrawLinkTooltip(a, b, this)) && (b = b.data, b = b.constructor === Number ? b.toFixed(2) : b.constructor === String ? '"' + b + '"' : b.constructor === Boolean ? String(b) : b.toToolTip ? b.toToolTip() : "[" + b.constructor.name + "]", null != b)) { b = b.substr(0, 30); a.font = "14px Courier New"; - var h = a.measureText(b).width + 20; + var g = a.measureText(b).width + 20; a.shadowColor = "black"; a.shadowOffsetX = 2; a.shadowOffsetY = 2; a.shadowBlur = 3; a.fillStyle = "#454"; a.beginPath(); - a.roundRect(d[0] - 0.5 * h, d[1] - 15 - 24, h, 24, 3, 3); + a.roundRect(d[0] - 0.5 * g, d[1] - 15 - 24, g, 24, 3, 3); a.moveTo(d[0] - 10, d[1] - 15); a.lineTo(d[0] + 10, d[1] - 15); a.lineTo(d[0], d[1] - 5); @@ -3947,16 +3230,16 @@ $jscomp.polyfill("Object.values", function(v) { } }; var u = new Float32Array(4); - l.prototype.drawNodeShape = function(a, b, d, h, f, c, k) { - b.strokeStyle = h; + l.prototype.drawNodeShape = function(a, b, d, g, f, c, k) { + b.strokeStyle = g; b.fillStyle = f; f = e.NODE_TITLE_HEIGHT; - var n = 0.5 > this.ds.scale, p = a._shape || a.constructor.shape || e.ROUND_SHAPE, x = a.constructor.title_mode, g = !0; - x == e.TRANSPARENT_TITLE ? g = !1 : x == e.AUTOHIDE_TITLE && k && (g = !0); + var n = 0.5 > this.ds.scale, p = a._shape || a.constructor.shape || e.ROUND_SHAPE, x = a.constructor.title_mode, h = !0; + x == e.TRANSPARENT_TITLE ? h = !1 : x == e.AUTOHIDE_TITLE && k && (h = !0); u[0] = 0; - u[1] = g ? -f : 0; + u[1] = h ? -f : 0; u[2] = d[0] + 1; - u[3] = g ? d[1] + f : d[1]; + u[3] = h ? d[1] + f : d[1]; k = b.globalAlpha; b.beginPath(); p == e.BOX_SHAPE || n ? b.fillRect(u[0], u[1], u[2], u[3]) : p == e.ROUND_SHAPE || p == e.CARD_SHAPE ? b.roundRect(u[0], u[1], u[2], u[3], this.round_radius, p == e.CARD_SHAPE ? 0 : this.round_radius) : p == e.CIRCLE_SHAPE && b.arc(0.5 * d[0], 0.5 * d[1], 0.5 * d[0], 0, 2 * Math.PI); @@ -3966,19 +3249,19 @@ $jscomp.polyfill("Object.values", function(v) { if (a.onDrawBackground) { a.onDrawBackground(b, this, this.canvas); } - if (g || x == e.TRANSPARENT_TITLE) { + if (h || x == e.TRANSPARENT_TITLE) { if (a.onDrawTitleBar) { - a.onDrawTitleBar(b, f, d, this.ds.scale, h); + a.onDrawTitleBar(b, f, d, this.ds.scale, g); } else { if (x != e.TRANSPARENT_TITLE && (a.constructor.title_color || this.render_title_colored)) { - g = a.constructor.title_color || h; + h = a.constructor.title_color || g; a.flags.collapsed && (b.shadowColor = e.DEFAULT_SHADOW_COLOR); if (this.use_gradients) { - var t = l.gradients[g]; - t || (t = l.gradients[g] = b.createLinearGradient(0, 0, 400, 0), t.addColorStop(0, g), t.addColorStop(1, "#000")); + var t = l.gradients[h]; + t || (t = l.gradients[h] = b.createLinearGradient(0, 0, 400, 0), t.addColorStop(0, h), t.addColorStop(1, "#000")); b.fillStyle = t; } else { - b.fillStyle = g; + b.fillStyle = h; } b.beginPath(); p == e.BOX_SHAPE || n ? b.rect(0, -f, d[0] + 1, f) : (p == e.ROUND_SHAPE || p == e.CARD_SHAPE) && b.roundRect(0, -f, d[0] + 1, f, this.round_radius, a.flags.collapsed ? this.round_radius : 0); @@ -4012,47 +3295,47 @@ $jscomp.polyfill("Object.values", function(v) { p == e.BOX_SHAPE ? b.rect(-6 + u[0], -6 + u[1], 12 + u[2], 12 + u[3]) : p == e.ROUND_SHAPE || p == e.CARD_SHAPE && a.flags.collapsed ? b.roundRect(-6 + u[0], -6 + u[1], 12 + u[2], 12 + u[3], 2 * this.round_radius) : p == e.CARD_SHAPE ? b.roundRect(-6 + u[0], -6 + u[1], 12 + u[2], 12 + u[3], 2 * this.round_radius, 2) : p == e.CIRCLE_SHAPE && b.arc(0.5 * d[0], 0.5 * d[1], 0.5 * d[0] + 6, 0, 2 * Math.PI); b.strokeStyle = "#FFF"; b.stroke(); - b.strokeStyle = h; + b.strokeStyle = g; b.globalAlpha = 1; } }; - var G = new Float32Array(4), n = new Float32Array(4), p = new Float32Array(2), k = new Float32Array(2); + var H = new Float32Array(4), n = new Float32Array(4), p = new Float32Array(2), k = new Float32Array(2); l.prototype.drawConnections = function(a) { var b = e.getTime(), d = this.visible_area; - G[0] = d[0] - 20; - G[1] = d[1] - 20; - G[2] = d[2] + 40; - G[3] = d[3] + 40; + H[0] = d[0] - 20; + H[1] = d[1] - 20; + H[2] = d[2] + 40; + H[3] = d[3] + 40; a.lineWidth = this.connections_width; a.fillStyle = "#AAA"; a.strokeStyle = "#AAA"; a.globalAlpha = this.editor_alpha; d = this.graph._nodes; - for (var h = 0, f = d.length; h < f; ++h) { - var c = d[h]; + for (var g = 0, f = d.length; g < f; ++g) { + var c = d[g]; if (c.inputs && c.inputs.length) { for (var l = 0; l < c.inputs.length; ++l) { - var g = c.inputs[l]; - if (g && null != g.link && (g = this.graph.links[g.link])) { - var m = this.graph.getNodeById(g.origin_id); + var h = c.inputs[l]; + if (h && null != h.link && (h = this.graph.links[h.link])) { + var m = this.graph.getNodeById(h.origin_id); if (null != m) { - var r = g.origin_slot; - var A = -1 == r ? [m.pos[0] + 10, m.pos[1] + 10] : m.getConnectionPos(!1, r, p); + var r = h.origin_slot; + var z = -1 == r ? [m.pos[0] + 10, m.pos[1] + 10] : m.getConnectionPos(!1, r, p); var t = c.getConnectionPos(!0, l, k); - n[0] = A[0]; - n[1] = A[1]; - n[2] = t[0] - A[0]; - n[3] = t[1] - A[1]; + n[0] = z[0]; + n[1] = z[1]; + n[2] = t[0] - z[0]; + n[3] = t[1] - z[1]; 0 > n[2] && (n[0] += n[2], n[2] = Math.abs(n[2])); 0 > n[3] && (n[1] += n[3], n[3] = Math.abs(n[3])); - if (w(n, G)) { + if (w(n, H)) { var q = m.outputs[r]; r = c.inputs[l]; - if (q && r && (m = q.dir || (m.horizontal ? e.DOWN : e.RIGHT), r = r.dir || (c.horizontal ? e.UP : e.LEFT), this.renderLink(a, A, t, g, !1, 0, null, m, r), g && g._last_time && 1000 > b - g._last_time)) { - q = 2.0 - 0.002 * (b - g._last_time); + if (q && r && (m = q.dir || (m.horizontal ? e.DOWN : e.RIGHT), r = r.dir || (c.horizontal ? e.UP : e.LEFT), this.renderLink(a, z, t, h, !1, 0, null, m, r), h && h._last_time && 1000 > b - h._last_time)) { + q = 2.0 - 0.002 * (b - h._last_time); var M = a.globalAlpha; a.globalAlpha = M * q; - this.renderLink(a, A, t, g, !0, q, "white", m, r); + this.renderLink(a, z, t, h, !0, q, "white", m, r); a.globalAlpha = M; } } @@ -4063,30 +3346,30 @@ $jscomp.polyfill("Object.values", function(v) { } a.globalAlpha = 1; }; - l.prototype.renderLink = function(a, b, d, h, f, c, k, n, p, g) { - h && this.visible_links.push(h); - !k && h && (k = h.color || l.link_type_colors[h.type]); + l.prototype.renderLink = function(a, b, d, g, f, c, k, n, p, h) { + g && this.visible_links.push(g); + !k && g && (k = g.color || l.link_type_colors[g.type]); k || (k = this.default_link_color); - null != h && this.highlighted_links[h.id] && (k = "#FFF"); + null != g && this.highlighted_links[g.id] && (k = "#FFF"); n = n || e.RIGHT; p = p || e.LEFT; var x = B(b, d); this.render_connections_border && 0.6 < this.ds.scale && (a.lineWidth = this.connections_width + 4); a.lineJoin = "round"; - g = g || 1; - 1 < g && (a.lineWidth = 0.5); + h = h || 1; + 1 < h && (a.lineWidth = 0.5); a.beginPath(); - for (var t = 0; t < g; t += 1) { - var m = 5 * (t - 0.5 * (g - 1)); + for (var t = 0; t < h; t += 1) { + var G = 5 * (t - 0.5 * (h - 1)); if (this.links_render_mode == e.SPLINE_LINK) { - a.moveTo(b[0], b[1] + m); - var H = 0, r = 0, q = 0, I = 0; + a.moveTo(b[0], b[1] + G); + var m = 0, r = 0, q = 0, I = 0; switch(n) { case e.LEFT: - H = -0.25 * x; + m = -0.25 * x; break; case e.RIGHT: - H = 0.25 * x; + m = 0.25 * x; break; case e.UP: r = -0.25 * x; @@ -4107,17 +3390,17 @@ $jscomp.polyfill("Object.values", function(v) { case e.DOWN: I = 0.25 * x; } - a.bezierCurveTo(b[0] + H, b[1] + r + m, d[0] + q, d[1] + I + m, d[0], d[1] + m); + a.bezierCurveTo(b[0] + m, b[1] + r + G, d[0] + q, d[1] + I + G, d[0], d[1] + G); } else { if (this.links_render_mode == e.LINEAR_LINK) { - a.moveTo(b[0], b[1] + m); - I = q = r = H = 0; + a.moveTo(b[0], b[1] + G); + I = q = r = m = 0; switch(n) { case e.LEFT: - H = -1; + m = -1; break; case e.RIGHT: - H = 1; + m = 1; break; case e.UP: r = -1; @@ -4138,12 +3421,12 @@ $jscomp.polyfill("Object.values", function(v) { case e.DOWN: I = 1; } - a.lineTo(b[0] + 15 * H, b[1] + 15 * r + m); - a.lineTo(d[0] + 15 * q, d[1] + 15 * I + m); - a.lineTo(d[0], d[1] + m); + a.lineTo(b[0] + 15 * m, b[1] + 15 * r + G); + a.lineTo(d[0] + 15 * q, d[1] + 15 * I + G); + a.lineTo(d[0], d[1] + G); } else { if (this.links_render_mode == e.STRAIGHT_LINK) { - a.moveTo(b[0], b[1]), m = b[0], H = b[1], r = d[0], q = d[1], n == e.RIGHT ? m += 10 : H += 10, p == e.LEFT ? r -= 10 : q -= 10, a.lineTo(m, H), a.lineTo(0.5 * (m + r), H), a.lineTo(0.5 * (m + r), q), a.lineTo(r, q), a.lineTo(d[0], d[1]); + a.moveTo(b[0], b[1]), G = b[0], m = b[1], r = d[0], q = d[1], n == e.RIGHT ? G += 10 : m += 10, p == e.LEFT ? r -= 10 : q -= 10, a.lineTo(G, m), a.lineTo(0.5 * (G + r), m), a.lineTo(0.5 * (G + r), q), a.lineTo(r, q), a.lineTo(d[0], d[1]); } else { return; } @@ -4155,20 +3438,20 @@ $jscomp.polyfill("Object.values", function(v) { a.fillStyle = a.strokeStyle = k; a.stroke(); f = this.computeConnectionPoint(b, d, 0.5, n, p); - h && h._pos && (h._pos[0] = f[0], h._pos[1] = f[1]); - 0.6 <= this.ds.scale && this.highquality_render && p != e.CENTER && (this.render_connection_arrows && (t = this.computeConnectionPoint(b, d, 0.25, n, p), x = this.computeConnectionPoint(b, d, 0.26, n, p), h = this.computeConnectionPoint(b, d, 0.75, n, p), g = this.computeConnectionPoint(b, d, 0.76, n, p), this.render_curved_connections ? (x = -Math.atan2(x[0] - t[0], x[1] - t[1]), g = -Math.atan2(g[0] - h[0], g[1] - h[1])) : g = x = d[1] > b[1] ? 0 : Math.PI, a.save(), a.translate(t[0], t[1]), - a.rotate(x), a.beginPath(), a.moveTo(-5, -3), a.lineTo(0, 7), a.lineTo(5, -3), a.fill(), a.restore(), a.save(), a.translate(h[0], h[1]), a.rotate(g), a.beginPath(), a.moveTo(-5, -3), a.lineTo(0, 7), a.lineTo(5, -3), a.fill(), a.restore()), a.beginPath(), a.arc(f[0], f[1], 5, 0, 2 * Math.PI), a.fill()); + g && g._pos && (g._pos[0] = f[0], g._pos[1] = f[1]); + 0.6 <= this.ds.scale && this.highquality_render && p != e.CENTER && (this.render_connection_arrows && (t = this.computeConnectionPoint(b, d, 0.25, n, p), x = this.computeConnectionPoint(b, d, 0.26, n, p), g = this.computeConnectionPoint(b, d, 0.75, n, p), h = this.computeConnectionPoint(b, d, 0.76, n, p), this.render_curved_connections ? (x = -Math.atan2(x[0] - t[0], x[1] - t[1]), h = -Math.atan2(h[0] - g[0], h[1] - g[1])) : h = x = d[1] > b[1] ? 0 : Math.PI, a.save(), a.translate(t[0], t[1]), + a.rotate(x), a.beginPath(), a.moveTo(-5, -3), a.lineTo(0, 7), a.lineTo(5, -3), a.fill(), a.restore(), a.save(), a.translate(g[0], g[1]), a.rotate(h), a.beginPath(), a.moveTo(-5, -3), a.lineTo(0, 7), a.lineTo(5, -3), a.fill(), a.restore()), a.beginPath(), a.arc(f[0], f[1], 5, 0, 2 * Math.PI), a.fill()); if (c) { for (a.fillStyle = k, t = 0; 5 > t; ++t) { c = (0.001 * e.getTime() + 0.2 * t) % 1, f = this.computeConnectionPoint(b, d, c, n, p), a.beginPath(), a.arc(f[0], f[1], 5, 0, 2 * Math.PI), a.fill(); } } }; - l.prototype.computeConnectionPoint = function(a, b, d, h, f) { - h = h || e.RIGHT; + l.prototype.computeConnectionPoint = function(a, b, d, g, f) { + g = g || e.RIGHT; f = f || e.LEFT; var c = B(a, b), k = [a[0], a[1]], n = [b[0], b[1]]; - switch(h) { + switch(g) { case e.LEFT: k[0] += -0.25 * c; break; @@ -4194,11 +3477,11 @@ $jscomp.polyfill("Object.values", function(v) { case e.DOWN: n[1] += 0.25 * c; } - h = (1 - d) * (1 - d) * (1 - d); + g = (1 - d) * (1 - d) * (1 - d); f = 3 * (1 - d) * (1 - d) * d; c = 3 * (1 - d) * d * d; d *= d * d; - return [h * a[0] + f * k[0] + c * n[0] + d * b[0], h * a[1] + f * k[1] + c * n[1] + d * b[1]]; + return [g * a[0] + f * k[0] + c * n[0] + d * b[0], g * a[1] + f * k[1] + c * n[1] + d * b[1]]; }; l.prototype.drawExecutionOrder = function(a) { a.shadowColor = "transparent"; @@ -4207,16 +3490,16 @@ $jscomp.polyfill("Object.values", function(v) { a.strokeStyle = "white"; a.globalAlpha = 0.75; for (var b = this.visible_nodes, d = 0; d < b.length; ++d) { - var h = b[d]; + var g = b[d]; a.fillStyle = "black"; - a.fillRect(h.pos[0] - e.NODE_TITLE_HEIGHT, h.pos[1] - e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT); - 0 == h.order && a.strokeRect(h.pos[0] - e.NODE_TITLE_HEIGHT + 0.5, h.pos[1] - e.NODE_TITLE_HEIGHT + 0.5, e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT); + a.fillRect(g.pos[0] - e.NODE_TITLE_HEIGHT, g.pos[1] - e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT); + 0 == g.order && a.strokeRect(g.pos[0] - e.NODE_TITLE_HEIGHT + 0.5, g.pos[1] - e.NODE_TITLE_HEIGHT + 0.5, e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT); a.fillStyle = "#FFF"; - a.fillText(h.order, h.pos[0] + -0.5 * e.NODE_TITLE_HEIGHT, h.pos[1] - 6); + a.fillText(g.order, g.pos[0] + -0.5 * e.NODE_TITLE_HEIGHT, g.pos[1] - 6); } a.globalAlpha = 1; }; - l.prototype.drawNodeWidgets = function(a, b, d, h) { + l.prototype.drawNodeWidgets = function(a, b, d, g) { if (!a.widgets || !a.widgets.length) { return 0; } @@ -4225,7 +3508,7 @@ $jscomp.polyfill("Object.values", function(v) { var k = e.NODE_WIDGET_HEIGHT, n = 0.5 < this.ds.scale; d.save(); d.globalAlpha = this.editor_alpha; - for (var p = e.WIDGET_OUTLINE_COLOR, l = e.WIDGET_BGCOLOR, g = e.WIDGET_TEXT_COLOR, t = e.WIDGET_SECONDARY_TEXT_COLOR, m = 0; m < c.length; ++m) { + for (var p = e.WIDGET_OUTLINE_COLOR, h = e.WIDGET_BGCOLOR, l = e.WIDGET_TEXT_COLOR, t = e.WIDGET_SECONDARY_TEXT_COLOR, m = 0; m < c.length; ++m) { var r = c[m], q = b; r.y && (q = r.y); r.last_y = q; @@ -4238,12 +3521,12 @@ $jscomp.polyfill("Object.values", function(v) { r.clicked && (d.fillStyle = "#AAA", r.clicked = !1, this.dirty_canvas = !0); d.fillRect(15, q, f - 30, k); n && d.strokeRect(15, q, f - 30, k); - n && (d.textAlign = "center", d.fillStyle = g, d.fillText(r.name, 0.5 * f, q + 0.7 * k)); + n && (d.textAlign = "center", d.fillStyle = l, d.fillText(r.name, 0.5 * f, q + 0.7 * k)); break; case "toggle": d.textAlign = "left"; d.strokeStyle = p; - d.fillStyle = l; + d.fillStyle = h; d.beginPath(); n ? d.roundRect(15, b, f - 30, k, 0.5 * k) : d.rect(15, b, f - 30, k); d.fill(); @@ -4252,38 +3535,38 @@ $jscomp.polyfill("Object.values", function(v) { d.beginPath(); d.arc(f - 30, q + 0.5 * k, 0.36 * k, 0, 2 * Math.PI); d.fill(); - n && (d.fillStyle = t, null != r.name && d.fillText(r.name, 30, q + 0.7 * k), d.fillStyle = r.value ? g : t, d.textAlign = "right", d.fillText(r.value ? r.options.on || "true" : r.options.off || "false", f - 40, q + 0.7 * k)); + n && (d.fillStyle = t, null != r.name && d.fillText(r.name, 30, q + 0.7 * k), d.fillStyle = r.value ? l : t, d.textAlign = "right", d.fillText(r.value ? r.options.on || "true" : r.options.off || "false", f - 40, q + 0.7 * k)); break; case "slider": - d.fillStyle = l; + d.fillStyle = h; d.fillRect(15, q, f - 30, k); var u = r.options.max - r.options.min, C = (r.value - r.options.min) / u; - d.fillStyle = h == r ? "#89A" : "#678"; + d.fillStyle = g == r ? "#89A" : "#678"; d.fillRect(15, q, C * (f - 30), k); n && d.strokeRect(15, q, f - 30, k); r.marker && (u = (r.marker - r.options.min) / u, d.fillStyle = "#AA9", d.fillRect(15 + u * (f - 30), q, 2, k)); - n && (d.textAlign = "center", d.fillStyle = g, d.fillText(r.name + " " + Number(r.value).toFixed(3), 0.5 * f, q + 0.7 * k)); + n && (d.textAlign = "center", d.fillStyle = l, d.fillText(r.name + " " + Number(r.value).toFixed(3), 0.5 * f, q + 0.7 * k)); break; case "number": case "combo": d.textAlign = "left"; d.strokeStyle = p; - d.fillStyle = l; + d.fillStyle = h; d.beginPath(); n ? d.roundRect(15, b, f - 30, k, 0.5 * k) : d.rect(15, b, f - 30, k); d.fill(); - n && (d.stroke(), d.fillStyle = g, d.beginPath(), d.moveTo(31, b + 5), d.lineTo(21, b + 0.5 * k), d.lineTo(31, b + k - 5), d.fill(), d.beginPath(), d.moveTo(f - 15 - 16, b + 5), d.lineTo(f - 15 - 6, b + 0.5 * k), d.lineTo(f - 15 - 16, b + k - 5), d.fill(), d.fillStyle = t, d.fillText(r.name, 35, q + 0.7 * k), d.fillStyle = g, d.textAlign = "right", "number" == r.type ? d.fillText(Number(r.value).toFixed(void 0 !== r.options.precision ? r.options.precision : 3), f - 30 - 20, q + 0.7 * k) : + n && (d.stroke(), d.fillStyle = l, d.beginPath(), d.moveTo(31, b + 5), d.lineTo(21, b + 0.5 * k), d.lineTo(31, b + k - 5), d.fill(), d.beginPath(), d.moveTo(f - 15 - 16, b + 5), d.lineTo(f - 15 - 6, b + 0.5 * k), d.lineTo(f - 15 - 16, b + k - 5), d.fill(), d.fillStyle = t, d.fillText(r.name, 35, q + 0.7 * k), d.fillStyle = l, d.textAlign = "right", "number" == r.type ? d.fillText(Number(r.value).toFixed(void 0 !== r.options.precision ? r.options.precision : 3), f - 30 - 20, q + 0.7 * k) : (u = r.value, r.options.values && (C = r.options.values, C.constructor === Function && (C = C()), C && C.constructor !== Array && (u = C[r.value])), d.fillText(u, f - 30 - 20, q + 0.7 * k))); break; case "string": case "text": d.textAlign = "left"; d.strokeStyle = p; - d.fillStyle = l; + d.fillStyle = h; d.beginPath(); n ? d.roundRect(15, b, f - 30, k, 0.5 * k) : d.rect(15, b, f - 30, k); d.fill(); - n && (d.save(), d.beginPath(), d.rect(15, b, f - 30, k), d.clip(), d.stroke(), d.fillStyle = t, null != r.name && d.fillText(r.name, 30, q + 0.7 * k), d.fillStyle = g, d.textAlign = "right", d.fillText(String(r.value).substr(0, 30), f - 30, q + 0.7 * k), d.restore()); + n && (d.save(), d.beginPath(), d.rect(15, b, f - 30, k), d.clip(), d.stroke(), d.fillStyle = t, null != r.name && d.fillText(r.name, 30, q + 0.7 * k), d.fillStyle = l, d.textAlign = "right", d.fillText(String(r.value).substr(0, 30), f - 30, q + 0.7 * k), d.restore()); break; default: r.draw && r.draw(d, a, f, q, k); @@ -4295,19 +3578,19 @@ $jscomp.polyfill("Object.values", function(v) { d.textAlign = "left"; }; l.prototype.processNodeWidgets = function(a, b, d, c) { - function f(f, h) { - f.value = h; - f.options && f.options.property && void 0 !== a.properties[f.options.property] && a.setProperty(f.options.property, h); + function f(f, g) { + f.value = g; + f.options && f.options.property && void 0 !== a.properties[f.options.property] && a.setProperty(f.options.property, g); f.callback && f.callback(f.value, p, a, b, d); } if (!a.widgets || !a.widgets.length) { return null; } - for (var h = b[0] - a.pos[0], k = b[1] - a.pos[1], n = a.size[0], p = this, l = this.getCanvasWindow(), g = 0; g < a.widgets.length; ++g) { - var t = a.widgets[g]; + for (var g = b[0] - a.pos[0], k = b[1] - a.pos[1], n = a.size[0], p = this, l = this.getCanvasWindow(), h = 0; h < a.widgets.length; ++h) { + var t = a.widgets[h]; if (t && !t.disabled) { var m = t.computeSize ? t.computeSize(n)[1] : e.NODE_WIDGET_HEIGHT; - if (t == c || 6 < h && h < n - 12 && k > t.last_y && k < t.last_y + m) { + if (t == c || 6 < g && g < n - 12 && k > t.last_y && k < t.last_y + m) { switch(t.type) { case "button": if ("mousemove" === d.type) { @@ -4319,7 +3602,7 @@ $jscomp.polyfill("Object.values", function(v) { this.dirty_canvas = t.clicked = !0; break; case "slider": - l = Math.clamp((h - 10) / (n - 20), 0, 1); + l = Math.clamp((g - 10) / (n - 20), 0, 1); t.value = t.options.min + (t.options.max - t.options.min) * l; t.callback && setTimeout(function() { f(t, t.value); @@ -4337,12 +3620,12 @@ $jscomp.polyfill("Object.values", function(v) { r && r.constructor === Function && (r = t.options.values(t, a)); var q = null; "number" != t.type && (q = r.constructor === Array ? r : Object.keys(r)); - h = 40 > h ? -1 : h > n - 40 ? 1 : 0; + g = 40 > g ? -1 : g > n - 40 ? 1 : 0; if ("number" == t.type) { - t.value += 0.1 * h * (t.options.step || 1), null != t.options.min && t.value < t.options.min && (t.value = t.options.min), null != t.options.max && t.value > t.options.max && (t.value = t.options.max); + t.value += 0.1 * g * (t.options.step || 1), null != t.options.min && t.value < t.options.min && (t.value = t.options.min), null != t.options.max && t.value > t.options.max && (t.value = t.options.max); } else { - if (h) { - l = -1, l = r.constructor === Object ? q.indexOf(String(t.value)) + h : q.indexOf(t.value) + h, l >= q.length && (l = q.length - 1), 0 > l && (l = 0), t.value = r.constructor === Array ? r[l] : l; + if (g) { + l = -1, l = r.constructor === Object ? q.indexOf(String(t.value)) + g : q.indexOf(t.value) + g, l >= q.length && (l = q.length - 1), 0 > l && (l = 0), t.value = r.constructor === Array ? r[l] : l; } else { var u = r != q ? Object.values(r) : r; new e.ContextMenu(u, {scale:Math.max(1, this.ds.scale), event:d, className:"dark", callback:function(a, b, d) { @@ -4355,7 +3638,7 @@ $jscomp.polyfill("Object.values", function(v) { } } } else { - "mouseup" == d.type && "number" == t.type && (h = 40 > h ? -1 : h > n - 40 ? 1 : 0, 200 > d.click_time && 0 == h && this.prompt("Value", t.value, function(a) { + "mouseup" == d.type && "number" == t.type && (g = 40 > g ? -1 : g > n - 40 ? 1 : 0, 200 > d.click_time && 0 == g && this.prompt("Value", t.value, function(a) { this.value = Number(a); f(this, this.value); }.bind(t), d)); @@ -4379,7 +3662,7 @@ $jscomp.polyfill("Object.values", function(v) { }.bind(t), d); break; default: - t.mouse && (this.dirty_canvas = t.mouse(d, [h, k], a)); + t.mouse && (this.dirty_canvas = t.mouse(d, [g, k], a)); } return t; } @@ -4393,11 +3676,11 @@ $jscomp.polyfill("Object.values", function(v) { b.save(); b.globalAlpha = 0.5 * this.editor_alpha; for (var d = 0; d < a.length; ++d) { - var h = a[d]; - if (w(this.visible_area, h._bounding)) { - b.fillStyle = h.color || "#335"; - b.strokeStyle = h.color || "#335"; - var f = h._pos, c = h._size; + var g = a[d]; + if (w(this.visible_area, g._bounding)) { + b.fillStyle = g.color || "#335"; + b.strokeStyle = g.color || "#335"; + var f = g._pos, c = g._size; b.globalAlpha = 0.25 * this.editor_alpha; b.beginPath(); b.rect(f[0] + 0.5, f[1] + 0.5, c[0], c[1]); @@ -4409,9 +3692,9 @@ $jscomp.polyfill("Object.values", function(v) { b.lineTo(f[0] + c[0] - 10, f[1] + c[1]); b.lineTo(f[0] + c[0], f[1] + c[1] - 10); b.fill(); - c = h.font_size || e.DEFAULT_GROUP_FONT_SIZE; + c = g.font_size || e.DEFAULT_GROUP_FONT_SIZE; b.font = c + "px Arial"; - b.fillText(h.title, f[0] + 4, f[1] + c); + b.fillText(g.title, f[0] + 4, f[1] + c); } } b.restore(); @@ -4474,7 +3757,7 @@ $jscomp.polyfill("Object.values", function(v) { a.graph.add(b); }; l.onMenuAdd = function(a, b, d, c, f) { - function h(a, b) { + function g(a, b) { b = c.getFirstEvent(); if (a = e.createNode(a.value)) { a.pos = k.convertEventToCanvasOffset(b), k.graph.add(a); @@ -4487,13 +3770,13 @@ $jscomp.polyfill("Object.values", function(v) { for (var p in a) { a[p] && b.push({value:a[p], content:a[p], has_submenu:!0}); } - var g = new e.ContextMenu(b, {event:d, callback:function(a, b, d) { + var h = new e.ContextMenu(b, {event:d, callback:function(a, b, d) { a = e.getNodeTypesInCategory(a.value, k.filter); b = []; for (var f in a) { a[f].skip_list || b.push({content:a[f].title, value:a[f].type}); } - new e.ContextMenu(b, {event:d, callback:h, parentMenu:g}, n); + new e.ContextMenu(b, {event:d, callback:g, parentMenu:h}, n); return !1; }, parentMenu:c}, n); return !1; @@ -4504,7 +3787,7 @@ $jscomp.polyfill("Object.values", function(v) { }; l.showMenuNodeOptionalInputs = function(a, b, d, c, f) { if (f) { - var h = this; + var g = this; a = l.active_canvas.getCanvasWindow(); b = f.optional_inputs; f.onGetInputs && (b = f.onGetInputs()); @@ -4513,11 +3796,11 @@ $jscomp.polyfill("Object.values", function(v) { for (var n in b) { var p = b[n]; if (p) { - var g = p[0]; - p[2] && p[2].label && (g = p[2].label); - g = {content:g, value:p}; - p[1] == e.ACTION && (g.className = "event"); - k.push(g); + var h = p[0]; + p[2] && p[2].label && (h = p[2].label); + h = {content:h, value:p}; + p[1] == e.ACTION && (h.className = "event"); + k.push(h); } else { k.push(null); } @@ -4526,13 +3809,13 @@ $jscomp.polyfill("Object.values", function(v) { this.onMenuNodeInputs && (k = this.onMenuNodeInputs(k)); if (k.length) { return new e.ContextMenu(k, {event:d, callback:function(a, b, d) { - f && (a.callback && a.callback.call(h, f, a, b, d), a.value && (f.addInput(a.value[0], a.value[1], a.value[2]), f.setDirtyCanvas(!0, !0))); + f && (a.callback && a.callback.call(g, f, a, b, d), a.value && (f.addInput(a.value[0], a.value[1], a.value[2]), f.setDirtyCanvas(!0, !0))); }, parentMenu:c, node:f}, a), !1; } } }; l.showMenuNodeOptionalOutputs = function(a, b, d, c, f) { - function h(a, b, d) { + function g(a, b, d) { if (f && (a.callback && a.callback.call(k, f, a, b, d), a.value)) { if (d = a.value[1], !d || d.constructor !== Object && d.constructor !== Array) { f.addOutput(a.value[0], a.value[1], a.value[2]), f.setDirtyCanvas(!0, !0); @@ -4541,7 +3824,7 @@ $jscomp.polyfill("Object.values", function(v) { for (var n in d) { a.push({content:n, value:d[n]}); } - new e.ContextMenu(a, {event:b, callback:h, parentMenu:c, node:f}); + new e.ContextMenu(a, {event:b, callback:g, parentMenu:c, node:f}); return !1; } } @@ -4554,15 +3837,15 @@ $jscomp.polyfill("Object.values", function(v) { var n = []; if (b) { for (var p in b) { - var g = b[p]; - if (!g) { + var h = b[p]; + if (!h) { n.push(null); } else { - if (!f.flags || !f.flags.skip_repeated_outputs || -1 == f.findOutputSlot(g[0])) { - var m = g[0]; - g[2] && g[2].label && (m = g[2].label); - m = {content:m, value:g}; - g[1] == e.EVENT && (m.className = "event"); + if (!f.flags || !f.flags.skip_repeated_outputs || -1 == f.findOutputSlot(h[0])) { + var m = h[0]; + h[2] && h[2].label && (m = h[2].label); + m = {content:m, value:h}; + h[1] == e.EVENT && (m.className = "event"); n.push(m); } } @@ -4570,21 +3853,21 @@ $jscomp.polyfill("Object.values", function(v) { } this.onMenuNodeOutputs && (n = this.onMenuNodeOutputs(n)); if (n.length) { - return new e.ContextMenu(n, {event:d, callback:h, parentMenu:c, node:f}, a), !1; + return new e.ContextMenu(n, {event:d, callback:g, parentMenu:c, node:f}, a), !1; } } }; l.onShowMenuNodeProperties = function(a, b, d, c, f) { if (f && f.properties) { - var h = l.active_canvas; - b = h.getCanvasWindow(); + var g = l.active_canvas; + b = g.getCanvasWindow(); var k = [], n; for (n in f.properties) { a = void 0 !== f.properties[n] ? f.properties[n] : " ", "object" == typeof a && (a = JSON.stringify(a)), a = l.decodeHTML(a), k.push({content:"" + n + "" + a + "", value:n}); } if (k.length) { return new e.ContextMenu(k, {event:d, callback:function(a, b, d, c) { - f && (b = this.getBoundingClientRect(), h.showEditPropertyValue(f, a.value, {position:[b.left, b.top]})); + f && (b = this.getBoundingClientRect(), g.showEditPropertyValue(f, a.value, {position:[b.left, b.top]})); }, parentMenu:c, allow_html:!0, node:f}, b), !1; } } @@ -4600,7 +3883,7 @@ $jscomp.polyfill("Object.values", function(v) { l.prototype.showLinkMenu = function(a, b) { var d = this; console.log(a); - var c = new e.ContextMenu(["Add Node", null, "Delete"], {event:b, title:null != a.data ? a.data.constructor.name : null, callback:function(b, h, e) { + var c = new e.ContextMenu(["Add Node", null, "Delete"], {event:b, title:null != a.data ? a.data.constructor.name : null, callback:function(b, g, e) { switch(b) { case "Add Node": l.onMenuAdd(null, null, e, c, function(b) { @@ -4616,7 +3899,7 @@ $jscomp.polyfill("Object.values", function(v) { return !1; }; l.onShowPropertyEditor = function(a, b, d, c, f) { - function h() { + function g() { var b = n.value; "Number" == a.type ? b = Number(b) : "Boolean" == a.type && (b = !!b); f[e] = b; @@ -4633,20 +3916,20 @@ $jscomp.polyfill("Object.values", function(v) { n && (n.value = b, n.addEventListener("blur", function(a) { this.focus(); }), n.addEventListener("keydown", function(a) { - 13 == a.keyCode && (h(), a.preventDefault(), a.stopPropagation()); + 13 == a.keyCode && (g(), a.preventDefault(), a.stopPropagation()); })); b = l.active_canvas.canvas; d = b.getBoundingClientRect(); var p = c = -20; d && (c -= d.left, p -= d.top); event ? (k.style.left = event.clientX + c + "px", k.style.top = event.clientY + p + "px") : (k.style.left = 0.5 * b.width + c + "px", k.style.top = 0.5 * b.height + p + "px"); - k.querySelector("button").addEventListener("click", h); + k.querySelector("button").addEventListener("click", g); b.parentNode.appendChild(k); }; l.prototype.prompt = function(a, b, d, c) { var f = this; a = a || ""; - var h = !1, e = document.createElement("div"); + var g = !1, e = document.createElement("div"); e.className = "graphdialog rounded"; e.innerHTML = " "; e.close = function() { @@ -4655,7 +3938,7 @@ $jscomp.polyfill("Object.values", function(v) { }; 1 < this.ds.scale && (e.style.transform = "scale(" + this.ds.scale + ")"); e.addEventListener("mouseleave", function(a) { - h || e.close(); + g || e.close(); }); f.prompt_box && f.prompt_box.close(); f.prompt_box = e; @@ -4663,7 +3946,7 @@ $jscomp.polyfill("Object.values", function(v) { e.querySelector(".value").value = b; var k = e.querySelector("input"); k.addEventListener("keydown", function(a) { - h = !0; + g = !0; if (27 == a.keyCode) { e.close(); } else { @@ -4725,7 +4008,7 @@ $jscomp.polyfill("Object.values", function(v) { } } } - g.close(); + h.close(); } function d(a) { var b = u; @@ -4754,8 +4037,8 @@ $jscomp.polyfill("Object.values", function(v) { if (f.onSearchBox) { var c = f.onSearchBox(r, d, k); if (c) { - for (var h = 0; h < c.length; ++h) { - a(c[h]); + for (var g = 0; g < c.length; ++g) { + a(c[g]); } } } else { @@ -4766,55 +4049,55 @@ $jscomp.polyfill("Object.values", function(v) { var n = 0; d = d.toLowerCase(); var p = k.filter || k.graph.filter; - for (h in e.searchbox_extras) { - var g = e.searchbox_extras[h]; - if (-1 !== g.desc.toLowerCase().indexOf(d)) { - var x = e.registered_node_types[g.type]; + for (g in e.searchbox_extras) { + var h = e.searchbox_extras[g]; + if (-1 !== h.desc.toLowerCase().indexOf(d)) { + var x = e.registered_node_types[h.type]; if (!x || !x.filter || x.filter == p) { - if (a(g.desc, "searchbox_extra"), -1 !== l.search_limit && n++ > l.search_limit) { + if (a(h.desc, "searchbox_extra"), -1 !== l.search_limit && n++ > l.search_limit) { break; } } } } - g = null; + h = null; if (Array.prototype.filter) { - g = Object.keys(e.registered_node_types).filter(c); + h = Object.keys(e.registered_node_types).filter(c); } else { - for (h in g = [], e.registered_node_types) { - c(h) && g.push(h); + for (g in h = [], e.registered_node_types) { + c(g) && h.push(g); } } - for (h = 0; h < g.length && !(a(g[h]), -1 !== l.search_limit && n++ > l.search_limit); h++) { + for (g = 0; g < h.length && !(a(h[g]), -1 !== l.search_limit && n++ > l.search_limit); g++) { } } } } - var f = this, k = l.active_canvas, n = k.canvas, p = n.ownerDocument || document, g = document.createElement("div"); - g.className = "litegraph litesearchbox graphdialog rounded"; - g.innerHTML = "Search
"; - g.close = function() { + var f = this, k = l.active_canvas, n = k.canvas, p = n.ownerDocument || document, h = document.createElement("div"); + h.className = "litegraph litesearchbox graphdialog rounded"; + h.innerHTML = "Search
"; + h.close = function() { f.search_box = null; p.body.focus(); p.body.style.overflow = ""; setTimeout(function() { f.canvas.focus(); }, 20); - g.parentNode && g.parentNode.removeChild(g); + h.parentNode && h.parentNode.removeChild(h); }; var m = null; - 1 < this.ds.scale && (g.style.transform = "scale(" + this.ds.scale + ")"); - g.addEventListener("mouseenter", function(a) { + 1 < this.ds.scale && (h.style.transform = "scale(" + this.ds.scale + ")"); + h.addEventListener("mouseenter", function(a) { m && (clearTimeout(m), m = null); }); - g.addEventListener("mouseleave", function(a) { + h.addEventListener("mouseleave", function(a) { m = setTimeout(function() { - g.close(); + h.close(); }, 500); }); f.search_box && f.search_box.close(); - f.search_box = g; - var r = g.querySelector(".helper"), t = null, q = null, u = null, C = g.querySelector("input"); + f.search_box = h; + var r = h.querySelector(".helper"), t = null, q = null, u = null, C = h.querySelector("input"); C && (C.addEventListener("blur", function(a) { this.focus(); }), C.addEventListener("keydown", function(a) { @@ -4825,10 +4108,10 @@ $jscomp.polyfill("Object.values", function(v) { d(!0); } else { if (27 == a.keyCode) { - g.close(); + h.close(); } else { if (13 == a.keyCode) { - u ? b(u.innerHTML) : t ? b(t) : g.close(); + u ? b(u.innerHTML) : t ? b(t) : h.close(); } else { q && clearInterval(q); q = setTimeout(c, 10); @@ -4842,14 +4125,14 @@ $jscomp.polyfill("Object.values", function(v) { a.stopImmediatePropagation(); return !0; })); - p.fullscreenElement ? p.fullscreenElement.appendChild(g) : (p.body.appendChild(g), p.body.style.overflow = "hidden"); + p.fullscreenElement ? p.fullscreenElement.appendChild(h) : (p.body.appendChild(h), p.body.style.overflow = "hidden"); n = n.getBoundingClientRect(); var w = (a ? a.clientY : n.top + 0.5 * n.height) - 20; - g.style.left = (a ? a.clientX : n.left + 0.5 * n.width) - 80 + "px"; - g.style.top = w + "px"; + h.style.left = (a ? a.clientX : n.left + 0.5 * n.width) - 80 + "px"; + h.style.top = w + "px"; a.layerY > n.height - 200 && (r.style.maxHeight = n.height - a.layerY - 20 + "px"); C.focus(); - return g; + return h; }; l.prototype.showEditPropertyValue = function(a, b, d) { function c() { @@ -4880,8 +4163,8 @@ $jscomp.polyfill("Object.values", function(v) { if ("enum" == k && e.values) { n = ""; } else { @@ -4908,7 +4191,7 @@ $jscomp.polyfill("Object.values", function(v) { if (t = l.querySelector("input")) { t.addEventListener("blur", function(a) { this.focus(); - }), g = void 0 !== a.properties[b] ? a.properties[b] : "", g = JSON.stringify(g), t.value = g, t.addEventListener("keydown", function(a) { + }), h = void 0 !== a.properties[b] ? a.properties[b] : "", h = JSON.stringify(h), t.value = h, t.addEventListener("keydown", function(a) { 13 == a.keyCode && (c(), a.preventDefault(), a.stopPropagation()); }); } @@ -4967,8 +4250,8 @@ $jscomp.polyfill("Object.values", function(v) { } b = []; b.push({value:null, content:"No color"}); - for (var h in l.node_colors) { - a = l.node_colors[h], a = {value:h, content:"" + h + ""}, b.push(a); + for (var g in l.node_colors) { + a = l.node_colors[g], a = {value:g, content:"" + g + ""}, b.push(a); } new e.ContextMenu(b, {event:d, callback:function(a) { f && ((a = a.value ? l.node_colors[a.value] : null) ? f.constructor === e.LGraphGroup ? f.color = a.groupcolor : (f.color = a.color, f.bgcolor = a.bgcolor) : (delete f.color, delete f.bgcolor), f.setDirtyCanvas(!0, !0)); @@ -5037,11 +4320,11 @@ $jscomp.polyfill("Object.values", function(v) { } else { if ("Rename Slot" == b.content) { b = b.slot; - var e = b.input ? a.getInputInfo(b.slot) : a.getOutputInfo(b.slot), h = d.createDialog("Name", f), k = h.querySelector("input"); + var e = b.input ? a.getInputInfo(b.slot) : a.getOutputInfo(b.slot), g = d.createDialog("Name", f), k = g.querySelector("input"); k && e && (k.value = e.label || ""); - h.querySelector("button").addEventListener("click", function(a) { + g.querySelector("button").addEventListener("click", function(a) { k.value && (e && (e.label = k.value), d.setDirty(!0)); - h.close(); + g.close(); }); } } @@ -5051,8 +4334,8 @@ $jscomp.polyfill("Object.values", function(v) { a && (k.title = a.type); var n = null; a && (n = a.getSlotInPosition(b.canvasX, b.canvasY), l.active_node = a); - n ? (f = [], n && n.output && n.output.links && n.output.links.length && f.push({content:"Disconnect Links", slot:n}), b = n.input || n.output, f.push(b.locked ? "Cannot remove" : {content:"Remove Slot", slot:n}), f.push(b.nameLocked ? "Cannot rename" : {content:"Rename Slot", slot:n}), k.title = (n.input ? n.input.type : n.output.type) || "*", n.input && n.input.type == e.ACTION && (k.title = "Action"), n.output && n.output.type == e.EVENT && (k.title = "Event")) : a ? f = this.getNodeMenuOptions(a) : - (f = this.getCanvasMenuOptions(), (n = this.graph.getGroupOnPos(b.canvasX, b.canvasY)) && f.push(null, {content:"Edit Group", has_submenu:!0, submenu:{title:"Group", extra:n, options:this.getGroupMenuOptions(n)}})); + n ? (f = [], a.getSlotMenuOptions ? f = a.getSlotMenuOptions(n) : (n && n.output && n.output.links && n.output.links.length && f.push({content:"Disconnect Links", slot:n}), b = n.input || n.output, f.push(b.locked ? "Cannot remove" : {content:"Remove Slot", slot:n}), f.push(b.nameLocked ? "Cannot rename" : {content:"Rename Slot", slot:n})), k.title = (n.input ? n.input.type : n.output.type) || "*", n.input && n.input.type == e.ACTION && (k.title = "Action"), n.output && n.output.type == e.EVENT && + (k.title = "Event")) : a ? f = this.getNodeMenuOptions(a) : (f = this.getCanvasMenuOptions(), (n = this.graph.getGroupOnPos(b.canvasX, b.canvasY)) && f.push(null, {content:"Edit Group", has_submenu:!0, submenu:{title:"Group", extra:n, options:this.getGroupMenuOptions(n)}})); f && new e.ContextMenu(f, k, c); }; "undefined" != typeof window && window.CanvasRenderingContext2D && (window.CanvasRenderingContext2D.prototype.roundRect = function(a, b, d, c, f, e) { @@ -5191,7 +4474,7 @@ $jscomp.polyfill("Object.values", function(v) { } } }; - z.sampleCurve = function(a, b) { + A.sampleCurve = function(a, b) { if (b) { for (var d = 0; d < b.length - 1; ++d) { var c = b[d], f = b[d + 1]; @@ -5207,7 +4490,7 @@ $jscomp.polyfill("Object.values", function(v) { return 0; } }; - z.prototype.draw = function(a, b, d, c, f, e) { + A.prototype.draw = function(a, b, d, c, f, e) { if (d = this.points) { this.size = b; var k = b[0] - 2 * this.margin; @@ -5232,7 +4515,7 @@ $jscomp.polyfill("Object.values", function(v) { a.restore(); } }; - z.prototype.onMouseDown = function(a, b) { + A.prototype.onMouseDown = function(a, b) { var d = this.points; if (d && !(0 > a[1])) { var c = this.size[0] - 2 * this.margin, f = this.size[1] - 2 * this.margin, e = a[0] - this.margin; @@ -5246,7 +4529,7 @@ $jscomp.polyfill("Object.values", function(v) { } } }; - z.prototype.onMouseMove = function(a, b) { + A.prototype.onMouseMove = function(a, b) { var d = this.points; if (d) { var c = this.selected; @@ -5262,26 +4545,26 @@ $jscomp.polyfill("Object.values", function(v) { } } }; - z.prototype.onMouseUp = function(a, b) { + A.prototype.onMouseUp = function(a, b) { this.selected = -1; return !1; }; - z.prototype.getCloserPoint = function(a, b) { + A.prototype.getCloserPoint = function(a, b) { var d = this.points; if (!d) { return -1; } b = b || 30; - for (var c = this.size[0] - 2 * this.margin, f = this.size[1] - 2 * this.margin, e = d.length, k = [0, 0], n = 1000000, p = -1, g = 0; g < e; ++g) { - var l = d[g]; + for (var c = this.size[0] - 2 * this.margin, f = this.size[1] - 2 * this.margin, e = d.length, k = [0, 0], n = 1000000, p = -1, h = 0; h < e; ++h) { + var l = d[h]; k[0] = l[0] * c; k[1] = (1.0 - l[1]) * f; l = vec2.distance(a, k); - l > n || l > b || (p = g, n = l); + l > n || l > b || (p = h, n = l); } return p; }; - e.CurveEditor = z; + e.CurveEditor = A; e.getParameterNames = function(a) { return (a + "").replace(/[/][/].*$/gm, "").replace(/\s+/g, "").replace(/[/][*][^/*]*[*][/]/g, "").split("){", 1)[0].replace(/^[^(]*[(]/, "").replace(/=[^,]+/g, "").split(",").filter(Boolean); }; @@ -5302,7 +4585,7 @@ $jscomp.polyfill("Object.values", function(v) { this.size = [140, 80]; this.properties = {enabled:!0}; this.enabled = !0; - this.subgraph = new h.LGraph; + this.subgraph = new g.LGraph; this.subgraph._subgraph_node = this; this.subgraph._is_subgraph = !0; this.subgraph.onTrigger = this.onSubgraphTrigger.bind(this); @@ -5332,7 +4615,7 @@ $jscomp.polyfill("Object.values", function(v) { this.widgets_up = !0; this.size = [180, 90]; } - function g() { + function h() { this.addInput("", ""); this.name_in_graph = ""; this.properties = {}; @@ -5346,7 +4629,7 @@ $jscomp.polyfill("Object.values", function(v) { return a.inputs[0].type; }, set:function(b) { if ("action" == b || "event" == b) { - b = h.ACTION; + b = g.ACTION; } a.inputs[0].type = b; a.name_in_graph && a.graph.changeOutputType(a.name_in_graph, a.inputs[0].type); @@ -5403,7 +4686,7 @@ $jscomp.polyfill("Object.values", function(v) { this.size = [140, 30]; this._value = null; } - function z() { + function A() { this.addInput("array", "array,table,string"); this.addInput("index", "number"); this.addOutput("value", ""); @@ -5442,7 +4725,7 @@ $jscomp.polyfill("Object.values", function(v) { }); this.size = this.computeSize(); } - function G() { + function H() { this.size = [60, 30]; this.addInput("in"); this.addOutput("out"); @@ -5452,7 +4735,7 @@ $jscomp.polyfill("Object.values", function(v) { function n() { this.size = [60, 30]; this.addInput("data", 0); - this.addInput("download", h.ACTION); + this.addInput("download", g.ACTION); this.properties = {filename:"data.json"}; this.value = null; var a = this; @@ -5471,16 +4754,16 @@ $jscomp.polyfill("Object.values", function(v) { this.size = [40, 30]; } function a() { - this.mode = h.ON_EVENT; + this.mode = g.ON_EVENT; this.size = [80, 30]; this.addProperty("msg", ""); - this.addInput("log", h.EVENT); + this.addInput("log", g.EVENT); this.addInput("msg", 0); } function b() { - this.mode = h.ON_EVENT; + this.mode = g.ON_EVENT; this.addProperty("msg", ""); - this.addInput("", h.EVENT); + this.addInput("", g.EVENT); var a = this; this.widget = this.addWidget("text", "Text", "", function(b) { a.properties.msg = b; @@ -5497,14 +4780,14 @@ $jscomp.polyfill("Object.values", function(v) { this._func = null; this.data = {}; } - var h = v.LiteGraph; + var g = v.LiteGraph; c.title = "Time"; c.desc = "Time"; c.prototype.onExecute = function() { this.setOutputData(0, 1000 * this.graph.globaltime); this.setOutputData(1, this.graph.globaltime); }; - h.registerNodeType("basic/time", c); + g.registerNodeType("basic/time", c); q.title = "Subgraph"; q.desc = "Graph inside a node"; q.title_color = "#334"; @@ -5514,7 +4797,7 @@ $jscomp.polyfill("Object.values", function(v) { q.prototype.onDrawTitle = function(a) { if (!this.flags.collapsed) { a.fillStyle = "#555"; - var b = h.NODE_TITLE_HEIGHT, d = this.size[0] - b; + var b = g.NODE_TITLE_HEIGHT, d = this.size[0] - b; a.fillRect(d, -b, b, b); a.fillStyle = "#333"; a.beginPath(); @@ -5531,7 +4814,7 @@ $jscomp.polyfill("Object.values", function(v) { }, 10); }; q.prototype.onMouseDown = function(a, b, d) { - if (!this.flags.collapsed && b[0] > this.size[0] - h.NODE_TITLE_HEIGHT && 0 > b[1]) { + if (!this.flags.collapsed && b[0] > this.size[0] - g.NODE_TITLE_HEIGHT && 0 > b[1]) { var f = this; setTimeout(function() { d.openSubgraph(f.subgraph); @@ -5604,20 +4887,20 @@ $jscomp.polyfill("Object.values", function(v) { a[1] += 20; }; q.prototype.serialize = function() { - var a = h.LGraphNode.prototype.serialize.call(this); + var a = g.LGraphNode.prototype.serialize.call(this); a.subgraph = this.subgraph.serialize(); return a; }; q.prototype.clone = function() { - var a = h.createNode(this.type), b = this.serialize(); + var a = g.createNode(this.type), b = this.serialize(); delete b.id; delete b.inputs; delete b.outputs; a.configure(b); return a; }; - h.Subgraph = q; - h.registerNodeType("graph/subgraph", q); + g.Subgraph = q; + g.registerNodeType("graph/subgraph", q); m.title = "Input"; m.desc = "Input of the graph"; m.prototype.onConfigure = function() { @@ -5645,7 +4928,7 @@ $jscomp.polyfill("Object.values", function(v) { return this.flags.collapsed ? this.properties.name : this.title; }; m.prototype.onAction = function(a, b) { - this.properties.type == h.EVENT && this.triggerSlot(0, b); + this.properties.type == g.EVENT && this.triggerSlot(0, b); }; m.prototype.onExecute = function() { var a = this.graph.inputs[this.properties.name]; @@ -5654,25 +4937,25 @@ $jscomp.polyfill("Object.values", function(v) { m.prototype.onRemoved = function() { this.name_in_graph && this.graph.removeInput(this.name_in_graph); }; - h.GraphInput = m; - h.registerNodeType("graph/input", m); - g.title = "Output"; - g.desc = "Output of the graph"; - g.prototype.onExecute = function() { + g.GraphInput = m; + g.registerNodeType("graph/input", m); + h.title = "Output"; + h.desc = "Output of the graph"; + h.prototype.onExecute = function() { this._value = this.getInputData(0); this.graph.setOutputData(this.properties.name, this._value); }; - g.prototype.onAction = function(a, b) { - this.properties.type == h.ACTION && this.graph.trigger(this.properties.name, b); + h.prototype.onAction = function(a, b) { + this.properties.type == g.ACTION && this.graph.trigger(this.properties.name, b); }; - g.prototype.onRemoved = function() { + h.prototype.onRemoved = function() { this.name_in_graph && this.graph.removeOutput(this.name_in_graph); }; - g.prototype.getTitle = function() { + h.prototype.getTitle = function() { return this.flags.collapsed ? this.properties.name : this.title; }; - h.GraphOutput = g; - h.registerNodeType("graph/output", g); + g.GraphOutput = h; + g.registerNodeType("graph/output", h); r.title = "Const Number"; r.desc = "Constant number"; r.prototype.onExecute = function() { @@ -5687,7 +4970,7 @@ $jscomp.polyfill("Object.values", function(v) { r.prototype.onDrawBackground = function(a) { this.outputs[0].label = this.properties.value.toFixed(3); }; - h.registerNodeType("basic/const", r); + g.registerNodeType("basic/const", r); l.title = "Const Boolean"; l.desc = "Constant boolean"; l.prototype.getTitle = r.prototype.getTitle; @@ -5696,12 +4979,12 @@ $jscomp.polyfill("Object.values", function(v) { }; l.prototype.setValue = r.prototype.setValue; l.prototype.onGetInputs = function() { - return [["toggle", h.ACTION]]; + return [["toggle", g.ACTION]]; }; l.prototype.onAction = function(a) { this.setValue(!this.properties.value); }; - h.registerNodeType("basic/boolean", l); + g.registerNodeType("basic/boolean", l); B.title = "Const String"; B.desc = "Constant string"; B.prototype.getTitle = r.prototype.getTitle; @@ -5716,7 +4999,7 @@ $jscomp.polyfill("Object.values", function(v) { }; d.readAsText(a); }; - h.registerNodeType("basic/string", B); + g.registerNodeType("basic/string", B); y.title = "Const File"; y.desc = "Fetches a file from an url"; y["@type"] = {type:"enum", values:["text", "arraybuffer", "blob", "json"]}; @@ -5731,7 +5014,7 @@ $jscomp.polyfill("Object.values", function(v) { y.prototype.setValue = r.prototype.setValue; y.prototype.fetchFile = function(a) { var b = this; - a && a.constructor === String ? (this._url = a, this._type = this.properties.type, "http" == a.substr(0, 4) && h.proxy && (a = h.proxy + a.substr(a.indexOf(":") + 3)), fetch(a).then(function(a) { + a && a.constructor === String ? (this._url = a, this._type = this.properties.type, "http" == a.substr(0, 4) && g.proxy && (a = g.proxy + a.substr(a.indexOf(":") + 3)), fetch(a).then(function(a) { if (!a.ok) { throw Error("File not found"); } @@ -5780,7 +5063,7 @@ $jscomp.polyfill("Object.values", function(v) { } } }; - h.registerNodeType("basic/file", y); + g.registerNodeType("basic/file", y); w.title = "Const Data"; w.desc = "Constant Data"; w.prototype.onPropertyChanged = function(a, b) { @@ -5788,7 +5071,7 @@ $jscomp.polyfill("Object.values", function(v) { if (null != b && "" != b) { try { this._value = JSON.parse(b), this.boxcolor = "#AEA"; - } catch (H) { + } catch (G) { this.boxcolor = "red"; } } @@ -5797,7 +5080,7 @@ $jscomp.polyfill("Object.values", function(v) { this.setOutputData(0, this._value); }; w.prototype.setValue = r.prototype.setValue; - h.registerNodeType("basic/data", w); + g.registerNodeType("basic/data", w); E.title = "Const Array"; E.desc = "Constant Array"; E.prototype.onPropertyChanged = function(a, b) { @@ -5805,7 +5088,7 @@ $jscomp.polyfill("Object.values", function(v) { if (null != b && "" != b) { try { this._value = JSON.parse(b), this.boxcolor = "#AEA"; - } catch (H) { + } catch (G) { this.boxcolor = "red"; } } @@ -5822,15 +5105,15 @@ $jscomp.polyfill("Object.values", function(v) { this.setOutputData(0, this._value); }; E.prototype.setValue = r.prototype.setValue; - h.registerNodeType("basic/array", E); - z.title = "Array[i]"; - z.desc = "Returns an element from an array"; - z.prototype.onExecute = function() { + g.registerNodeType("basic/array", E); + A.title = "Array[i]"; + A.desc = "Returns an element from an array"; + A.prototype.onExecute = function() { var a = this.getInputData(0), b = this.getInputData(1); null == b && (b = this.properties.index); null != a && null != b && this.setOutputData(0, a[Math.floor(Number(b))]); }; - h.registerNodeType("basic/array[]", z); + g.registerNodeType("basic/array[]", A); e.title = "Table[row][col]"; e.desc = "Returns an element from a table"; e.prototype.onExecute = function() { @@ -5839,7 +5122,7 @@ $jscomp.polyfill("Object.values", function(v) { null == d && (d = this.properties.column); null != a && null != b && null != d && ((b = a[Math.floor(Number(b))]) ? this.setOutputData(0, b[Math.floor(Number(d))]) : this.setOutputData(0, null)); }; - h.registerNodeType("basic/table[][]", e); + g.registerNodeType("basic/table[][]", e); C.title = "Object property"; C.desc = "Outputs the property of an object"; C.prototype.setValue = function(a) { @@ -5856,14 +5139,14 @@ $jscomp.polyfill("Object.values", function(v) { var a = this.getInputData(0); null != a && this.setOutputData(0, a[this.properties.value]); }; - h.registerNodeType("basic/object_property", C); + g.registerNodeType("basic/object_property", C); D.title = "Object keys"; D.desc = "Outputs an array with the keys of an object"; D.prototype.onExecute = function() { var a = this.getInputData(0); null != a && this.setOutputData(0, Object.keys(a)); }; - h.registerNodeType("basic/object_keys", D); + g.registerNodeType("basic/object_keys", D); u.title = "Merge Objects"; u.desc = "Creates an object copying properties from others"; u.prototype.onExecute = function() { @@ -5880,20 +5163,20 @@ $jscomp.polyfill("Object.values", function(v) { } this.setOutputData(0, d); }; - h.registerNodeType("basic/merge_objects", u); - G.title = "Variable"; - G.desc = "store/read variable value"; - G.prototype.onExecute = function() { + g.registerNodeType("basic/merge_objects", u); + H.title = "Variable"; + H.desc = "store/read variable value"; + H.prototype.onExecute = function() { this.value = this.getInputData(0); this.graph && (this.graph.vars[this.properties.varname] = this.value); this.properties.global && (v[this.properties.varname] = this.value); this.setOutputData(0, this.value); }; - G.prototype.getTitle = function() { + H.prototype.getTitle = function() { return this.properties.varname; }; - h.registerNodeType("basic/variable", G); - h.wrapFunctionAsNode("basic/length", function(a) { + g.registerNodeType("basic/variable", H); + g.wrapFunctionAsNode("basic/length", function(a) { return a && null != a.length ? Number(a.length) : 0; }, ["*"], "number"); n.title = "Download"; @@ -5928,7 +5211,7 @@ $jscomp.polyfill("Object.values", function(v) { n.prototype.getTitle = function() { return this.flags.collapsed ? this.properties.filename : this.title; }; - h.registerNodeType("basic/download", n); + g.registerNodeType("basic/download", n); p.title = "Watch"; p.desc = "Show value of input"; p.prototype.onExecute = function() { @@ -5955,13 +5238,13 @@ $jscomp.polyfill("Object.values", function(v) { p.prototype.onDrawBackground = function(a) { this.inputs[0].label = p.toString(this.value); }; - h.registerNodeType("basic/watch", p); + g.registerNodeType("basic/watch", p); k.title = "Cast"; k.desc = "Allows to connect different types"; k.prototype.onExecute = function() { this.setOutputData(0, this.getInputData(0)); }; - h.registerNodeType("basic/cast", k); + g.registerNodeType("basic/cast", k); a.title = "Console"; a.desc = "Show value inside the console"; a.prototype.onAction = function(a, b) { @@ -5973,9 +5256,9 @@ $jscomp.polyfill("Object.values", function(v) { console.log(a); }; a.prototype.onGetInputs = function() { - return [["log", h.ACTION], ["warn", h.ACTION], ["error", h.ACTION]]; + return [["log", g.ACTION], ["warn", g.ACTION], ["error", g.ACTION]]; }; - h.registerNodeType("basic/console", a); + g.registerNodeType("basic/console", a); b.title = "Alert"; b.desc = "Show an alert window"; b.color = "#510"; @@ -5988,15 +5271,15 @@ $jscomp.polyfill("Object.values", function(v) { alert(d); }, 10); }; - h.registerNodeType("basic/alert", b); + g.registerNodeType("basic/alert", b); d.prototype.onConfigure = function(a) { - a.properties.onExecute && h.allow_scripts ? this.compileCode(a.properties.onExecute) : console.warn("Script not compiled, LiteGraph.allow_scripts is false"); + a.properties.onExecute && g.allow_scripts ? this.compileCode(a.properties.onExecute) : console.warn("Script not compiled, LiteGraph.allow_scripts is false"); }; d.title = "Script"; d.desc = "executes a code (max 100 characters)"; d.widgets_info = {onExecute:{type:"code"}}; d.prototype.onPropertyChanged = function(a, b) { - "onExecute" == a && h.allow_scripts ? this.compileCode(b) : console.warn("Script not compiled, LiteGraph.allow_scripts is false"); + "onExecute" == a && g.allow_scripts ? this.compileCode(b) : console.warn("Script not compiled, LiteGraph.allow_scripts is false"); }; d.prototype.compileCode = function(a) { this._func = null; @@ -6029,7 +5312,7 @@ $jscomp.polyfill("Object.values", function(v) { d.prototype.onGetOutputs = function() { return [["C", ""]]; }; - h.registerNodeType("basic/script", d); + g.registerNodeType("basic/script", d); })(this); (function(v) { function c() { @@ -6061,7 +5344,7 @@ $jscomp.polyfill("Object.values", function(v) { this.size = [120, 30]; this.flags = {horizontal:!0, render_box:!1}; } - function g() { + function h() { this.size = [60, 30]; this.addInput("event", w.ACTION); this.addOutput("event", w.EVENT); @@ -6104,20 +5387,20 @@ $jscomp.polyfill("Object.values", function(v) { var w = v.LiteGraph; c.title = "Log Event"; c.desc = "Log event in console"; - c.prototype.onAction = function(c, g) { - console.log(c, g); + c.prototype.onAction = function(c, h) { + console.log(c, h); }; w.registerNodeType("events/log", c); q.title = "TriggerEvent"; q.desc = "Triggers event if input evaluates to true"; - q.prototype.onExecute = function(c, g) { + q.prototype.onExecute = function(c, h) { c = this.getInputData(0); var e = c != this.prev; 0 === this.prev && (e = !1); var l = e && this.properties.only_on_change || !e && !this.properties.only_on_change; - c && l && this.triggerSlot(0, g); - !c && l && this.triggerSlot(2, g); - e && this.triggerSlot(1, g); + c && l && this.triggerSlot(0, h); + !c && l && this.triggerSlot(2, h); + e && this.triggerSlot(1, h); this.prev = c; }; w.registerNodeType("events/trigger", q); @@ -6126,34 +5409,34 @@ $jscomp.polyfill("Object.values", function(v) { m.prototype.getTitle = function() { return ""; }; - m.prototype.onAction = function(c, g) { + m.prototype.onAction = function(c, h) { if (this.outputs) { for (c = 0; c < this.outputs.length; ++c) { - this.triggerSlot(c, g); + this.triggerSlot(c, h); } } }; w.registerNodeType("events/sequencer", m); - g.title = "Filter Event"; - g.desc = "Blocks events that do not match the filter"; - g.prototype.onAction = function(c, g) { - if (null != g && (!this.properties.equal_to || this.properties.equal_to == g)) { - if (this.properties.has_property && (c = g[this.properties.has_property], null == c || this.properties.property_equal_to && this.properties.property_equal_to != c)) { + h.title = "Filter Event"; + h.desc = "Blocks events that do not match the filter"; + h.prototype.onAction = function(c, h) { + if (null != h && (!this.properties.equal_to || this.properties.equal_to == h)) { + if (this.properties.has_property && (c = h[this.properties.has_property], null == c || this.properties.property_equal_to && this.properties.property_equal_to != c)) { return; } - this.triggerSlot(0, g); + this.triggerSlot(0, h); } }; - w.registerNodeType("events/filter", g); + w.registerNodeType("events/filter", h); r.title = "Counter"; r.desc = "Counts events"; r.prototype.getTitle = function() { return this.flags.collapsed ? String(this.num) : this.title; }; - r.prototype.onAction = function(c, g) { - g = this.num; + r.prototype.onAction = function(c, h) { + h = this.num; "inc" == c ? this.num += 1 : "dec" == c ? --this.num : "reset" == c && (this.num = 0); - this.num != g && this.trigger("change", this.num); + this.num != h && this.trigger("change", this.num); }; r.prototype.onDrawBackground = function(c) { this.flags.collapsed || (c.fillStyle = "#AAA", c.font = "20px Arial", c.textAlign = "center", c.fillText(this.num, 0.5 * this.size[0], 0.5 * this.size[1])); @@ -6164,17 +5447,17 @@ $jscomp.polyfill("Object.values", function(v) { w.registerNodeType("events/counter", r); l.title = "Delay"; l.desc = "Delays one event"; - l.prototype.onAction = function(c, g) { + l.prototype.onAction = function(c, h) { c = this.properties.time_in_ms; - 0 >= c ? this.trigger(null, g) : this._pending.push([c, g]); + 0 >= c ? this.trigger(null, h) : this._pending.push([c, h]); }; l.prototype.onExecute = function() { var c = 1000 * this.graph.elapsed_time; this.isInputConnected(1) && (this.properties.time_in_ms = this.getInputData(1)); - for (var g = 0; g < this._pending.length; ++g) { - var e = this._pending[g]; + for (var h = 0; h < this._pending.length; ++h) { + var e = this._pending[h]; e[0] -= c; - 0 < e[0] || (this._pending.splice(g, 1), --g, this.trigger(null, e[1])); + 0 < e[0] || (this._pending.splice(h, 1), --h, this.trigger(null, e[1])); } }; l.prototype.onGetInputs = function() { @@ -6214,7 +5497,7 @@ $jscomp.polyfill("Object.values", function(v) { this._last_value = this.getInputData(0); this.setOutputData(0, this.properties.data); }; - y.prototype.onAction = function(c, g) { + y.prototype.onAction = function(c, h) { this.properties.data = this._last_value; }; y.prototype.onSerialize = function(c) { @@ -6224,7 +5507,7 @@ $jscomp.polyfill("Object.values", function(v) { })(this); (function(v) { function c() { - this.addOutput("", z.EVENT); + this.addOutput("", A.EVENT); this.addOutput("", "boolean"); this.addProperty("text", "click me"); this.addProperty("font_size", 30); @@ -6234,9 +5517,9 @@ $jscomp.polyfill("Object.values", function(v) { } function q() { this.addInput("", "boolean"); - this.addInput("e", z.ACTION); + this.addInput("e", A.ACTION); this.addOutput("v", "boolean"); - this.addOutput("e", z.EVENT); + this.addOutput("e", A.EVENT); this.properties = {font:"", value:!1}; this.size = [160, 44]; } @@ -6248,9 +5531,9 @@ $jscomp.polyfill("Object.values", function(v) { this._precision = this._remainder = 0; this.mouse_captured = !1; } - function g() { + function h() { this.addOutput("", "string"); - this.addOutput("change", z.EVENT); + this.addOutput("change", A.EVENT); this.size = [80, 60]; this.properties = {value:"A", values:"A;B;C"}; this.old_y = -1; @@ -6298,22 +5581,22 @@ $jscomp.polyfill("Object.values", function(v) { this.size = [200, 100]; this.properties = {borderColor:"#ffffff", bgcolorTop:"#f0f0f0", bgcolorBottom:"#e0e0e0", shadowSize:2, borderRadius:3}; } - var z = v.LiteGraph; + var A = v.LiteGraph; c.title = "Button"; c.desc = "Triggers an event"; c.font = "Arial"; c.prototype.onDrawForeground = function(e) { if (!this.flags.collapsed && (e.fillStyle = "black", e.fillRect(11, 11, this.size[0] - 20, this.size[1] - 20), e.fillStyle = "#AAF", e.fillRect(9, 9, this.size[0] - 20, this.size[1] - 20), e.fillStyle = this.clicked ? "white" : this.mouseOver ? "#668" : "#334", e.fillRect(10, 10, this.size[0] - 20, this.size[1] - 20), this.properties.text || 0 === this.properties.text)) { - var g = this.properties.font_size || 30; + var h = this.properties.font_size || 30; e.textAlign = "center"; e.fillStyle = this.clicked ? "black" : "white"; - e.font = g + "px " + c.font; - e.fillText(this.properties.text, 0.5 * this.size[0], 0.5 * this.size[1] + 0.3 * g); + e.font = h + "px " + c.font; + e.fillText(this.properties.text, 0.5 * this.size[0], 0.5 * this.size[1] + 0.3 * h); e.textAlign = "left"; } }; - c.prototype.onMouseDown = function(c, g) { - if (1 < g[0] && 1 < g[1] && g[0] < this.size[0] - 2 && g[1] < this.size[1] - 2) { + c.prototype.onMouseDown = function(c, h) { + if (1 < h[0] && 1 < h[1] && h[0] < this.size[0] - 2 && h[1] < this.size[1] - 2) { return this.clicked = !0, this.triggerSlot(0, this.properties.message), !0; } }; @@ -6323,22 +5606,22 @@ $jscomp.polyfill("Object.values", function(v) { c.prototype.onMouseUp = function(c) { this.clicked = !1; }; - z.registerNodeType("widget/button", c); + A.registerNodeType("widget/button", c); q.title = "Toggle"; q.desc = "Toggles between true or false"; q.prototype.onDrawForeground = function(c) { if (!this.flags.collapsed) { - var e = 0.5 * this.size[1], g = 0.8 * this.size[1]; + var e = 0.5 * this.size[1], h = 0.8 * this.size[1]; c.font = this.properties.font || (0.8 * e).toFixed(0) + "px Arial"; var l = c.measureText(this.title).width; l = 0.5 * (this.size[0] - (l + e)); c.fillStyle = "#AAA"; - c.fillRect(l, g - e, e, e); + c.fillRect(l, h - e, e, e); c.fillStyle = this.properties.value ? "#AEF" : "#000"; - c.fillRect(l + 0.25 * e, g - e + 0.25 * e, .5 * e, .5 * e); + c.fillRect(l + 0.25 * e, h - e + 0.25 * e, .5 * e, .5 * e); c.textAlign = "left"; c.fillStyle = "#AAA"; - c.fillText(this.title, 1.2 * e + l, 0.85 * g); + c.fillText(this.title, 1.2 * e + l, 0.85 * h); c.textAlign = "left"; } }; @@ -6351,33 +5634,33 @@ $jscomp.polyfill("Object.values", function(v) { null != c && (this.properties.value = c); this.setOutputData(0, this.properties.value); }; - q.prototype.onMouseDown = function(c, g) { - if (1 < g[0] && 1 < g[1] && g[0] < this.size[0] - 2 && g[1] < this.size[1] - 2) { + q.prototype.onMouseDown = function(c, h) { + if (1 < h[0] && 1 < h[1] && h[0] < this.size[0] - 2 && h[1] < this.size[1] - 2) { return this.properties.value = !this.properties.value, this.graph._version++, this.trigger("e", this.properties.value), !0; } }; - z.registerNodeType("widget/toggle", q); + A.registerNodeType("widget/toggle", q); m.title = "Number"; m.desc = "Widget to select number value"; m.pixels_threshold = 10; m.markers_color = "#666"; m.prototype.onDrawForeground = function(c) { - var e = 0.5 * this.size[0], g = this.size[1]; - 30 < g ? (c.fillStyle = m.markers_color, c.beginPath(), c.moveTo(e, 0.1 * g), c.lineTo(e + 0.1 * g, 0.2 * g), c.lineTo(e + -0.1 * g, 0.2 * g), c.fill(), c.beginPath(), c.moveTo(e, 0.9 * g), c.lineTo(e + 0.1 * g, 0.8 * g), c.lineTo(e + -0.1 * g, 0.8 * g), c.fill(), c.font = (0.7 * g).toFixed(1) + "px Arial") : c.font = (0.8 * g).toFixed(1) + "px Arial"; + var e = 0.5 * this.size[0], h = this.size[1]; + 30 < h ? (c.fillStyle = m.markers_color, c.beginPath(), c.moveTo(e, 0.1 * h), c.lineTo(e + 0.1 * h, 0.2 * h), c.lineTo(e + -0.1 * h, 0.2 * h), c.fill(), c.beginPath(), c.moveTo(e, 0.9 * h), c.lineTo(e + 0.1 * h, 0.8 * h), c.lineTo(e + -0.1 * h, 0.8 * h), c.fill(), c.font = (0.7 * h).toFixed(1) + "px Arial") : c.font = (0.8 * h).toFixed(1) + "px Arial"; c.textAlign = "center"; - c.font = (0.7 * g).toFixed(1) + "px Arial"; + c.font = (0.7 * h).toFixed(1) + "px Arial"; c.fillStyle = "#EEE"; - c.fillText(this.properties.value.toFixed(this._precision), e, 0.75 * g); + c.fillText(this.properties.value.toFixed(this._precision), e, 0.75 * h); }; m.prototype.onExecute = function() { this.setOutputData(0, this.properties.value); }; - m.prototype.onPropertyChanged = function(c, g) { + m.prototype.onPropertyChanged = function(c, h) { c = (this.properties.step + "").split("."); this._precision = 1 < c.length ? c[1].length : 0; }; - m.prototype.onMouseDown = function(c, g) { - if (!(0 > g[1])) { + m.prototype.onMouseDown = function(c, h) { + if (!(0 > h[1])) { return this.old_y = c.canvasY, this.captureInput(!0), this.mouse_captured = !0; } }; @@ -6397,30 +5680,30 @@ $jscomp.polyfill("Object.values", function(v) { this.setDirtyCanvas(!0); } }; - m.prototype.onMouseUp = function(c, g) { - 200 > c.click_time && (this.properties.value = Math.clamp(this.properties.value + (g[1] > 0.5 * this.size[1] ? -1 : 1) * this.properties.step, this.properties.min, this.properties.max), this.graph._version++, this.setDirtyCanvas(!0)); + m.prototype.onMouseUp = function(c, h) { + 200 > c.click_time && (this.properties.value = Math.clamp(this.properties.value + (h[1] > 0.5 * this.size[1] ? -1 : 1) * this.properties.step, this.properties.min, this.properties.max), this.graph._version++, this.setDirtyCanvas(!0)); this.mouse_captured && (this.mouse_captured = !1, this.captureInput(!1)); }; - z.registerNodeType("widget/number", m); - g.title = "Combo"; - g.desc = "Widget to select from a list"; - g.prototype.onExecute = function() { + A.registerNodeType("widget/number", m); + h.title = "Combo"; + h.desc = "Widget to select from a list"; + h.prototype.onExecute = function() { this.setOutputData(0, this.properties.value); }; - g.prototype.onPropertyChanged = function(c, g) { - "values" == c ? (this._values = g.split(";"), this.widget.options.values = this._values) : "value" == c && (this.widget.value = g); + h.prototype.onPropertyChanged = function(c, h) { + "values" == c ? (this._values = h.split(";"), this.widget.options.values = this._values) : "value" == c && (this.widget.value = h); }; - z.registerNodeType("widget/combo", g); + A.registerNodeType("widget/combo", h); r.title = "Knob"; r.desc = "Circular controller"; r.size = [80, 100]; r.prototype.onDrawForeground = function(c) { if (!this.flags.collapsed) { -1 == this.value && (this.value = (this.properties.value - this.properties.min) / (this.properties.max - this.properties.min)); - var e = 0.5 * this.size[0], g = 0.5 * this.size[1], l = 0.5 * Math.min(this.size[0], this.size[1]) - 5; + var e = 0.5 * this.size[0], h = 0.5 * this.size[1], l = 0.5 * Math.min(this.size[0], this.size[1]) - 5; c.globalAlpha = 1; c.save(); - c.translate(e, g); + c.translate(e, h); c.rotate(0.75 * Math.PI); c.fillStyle = "rgba(0,0,0,0.5)"; c.beginPath(); @@ -6440,27 +5723,27 @@ $jscomp.polyfill("Object.values", function(v) { c.restore(); c.fillStyle = "black"; c.beginPath(); - c.arc(e, g, 0.75 * l, 0, 2 * Math.PI, !0); + c.arc(e, h, 0.75 * l, 0, 2 * Math.PI, !0); c.fill(); c.fillStyle = this.mouseOver ? "white" : this.properties.color; c.beginPath(); var m = this.value * Math.PI * 1.5 + 0.75 * Math.PI; - c.arc(e + Math.cos(m) * l * 0.65, g + Math.sin(m) * l * 0.65, 0.05 * l, 0, 2 * Math.PI, !0); + c.arc(e + Math.cos(m) * l * 0.65, h + Math.sin(m) * l * 0.65, 0.05 * l, 0, 2 * Math.PI, !0); c.fill(); c.fillStyle = this.mouseOver ? "white" : "#AAA"; c.font = Math.floor(0.5 * l) + "px Arial"; c.textAlign = "center"; - c.fillText(this.properties.value.toFixed(this.properties.precision), e, g + 0.15 * l); + c.fillText(this.properties.value.toFixed(this.properties.precision), e, h + 0.15 * l); } }; r.prototype.onExecute = function() { this.setOutputData(0, this.properties.value); - this.boxcolor = z.colorToString([this.value, this.value, this.value]); + this.boxcolor = A.colorToString([this.value, this.value, this.value]); }; r.prototype.onMouseDown = function(c) { this.center = [0.5 * this.size[0], 0.5 * this.size[1] + 20]; this.radius = 0.5 * this.size[0]; - if (20 > c.canvasY - this.pos[1] || z.distance([c.canvasX, c.canvasY], [this.pos[0] + this.center[0], this.pos[1] + this.center[1]]) > this.radius) { + if (20 > c.canvasY - this.pos[1] || A.distance([c.canvasX, c.canvasY], [this.pos[0] + this.center[0], this.pos[1] + this.center[1]]) > this.radius) { return !1; } this.oldmouse = [c.canvasX - this.pos[0], c.canvasY - this.pos[1]]; @@ -6482,20 +5765,20 @@ $jscomp.polyfill("Object.values", function(v) { r.prototype.onMouseUp = function(c) { this.oldmouse && (this.oldmouse = null, this.captureInput(!1)); }; - r.prototype.onPropertyChanged = function(c, g) { + r.prototype.onPropertyChanged = function(c, h) { if ("min" == c || "max" == c || "value" == c) { - return this.properties[c] = parseFloat(g), !0; + return this.properties[c] = parseFloat(h), !0; } }; - z.registerNodeType("widget/knob", r); + A.registerNodeType("widget/knob", r); l.title = "Inner Slider"; - l.prototype.onPropertyChanged = function(c, g) { - "value" == c && (this.slider.value = g); + l.prototype.onPropertyChanged = function(c, h) { + "value" == c && (this.slider.value = h); }; l.prototype.onExecute = function() { this.setOutputData(0, this.properties.value); }; - z.registerNodeType("widget/internal_slider", l); + A.registerNodeType("widget/internal_slider", l); B.title = "H.Slider"; B.desc = "Linear slider controller"; B.prototype.onDrawForeground = function(c) { @@ -6512,7 +5795,7 @@ $jscomp.polyfill("Object.values", function(v) { B.prototype.onExecute = function() { this.properties.value = this.properties.min + (this.properties.max - this.properties.min) * this.value; this.setOutputData(0, this.properties.value); - this.boxcolor = z.colorToString([this.value, this.value, this.value]); + this.boxcolor = A.colorToString([this.value, this.value, this.value]); }; B.prototype.onMouseDown = function(c) { if (0 > c.canvasY - this.pos[1]) { @@ -6539,7 +5822,7 @@ $jscomp.polyfill("Object.values", function(v) { }; B.prototype.onMouseLeave = function(c) { }; - z.registerNodeType("widget/hslider", B); + A.registerNodeType("widget/hslider", B); y.title = "Progress"; y.desc = "Shows data in linear progress"; y.prototype.onExecute = function() { @@ -6554,7 +5837,7 @@ $jscomp.polyfill("Object.values", function(v) { e = Math.max(0, e); c.fillRect(2, 2, (this.size[0] - 4) * e, this.size[1] - 4); }; - z.registerNodeType("widget/progress", y); + A.registerNodeType("widget/progress", y); w.title = "Text"; w.desc = "Shows the input value"; w.widgets = [{name:"resize", text:"Resize box", type:"button"}, {name:"led_text", text:"LED", type:"minibutton"}, {name:"normal_text", text:"Normal", type:"minibutton"}]; @@ -6562,14 +5845,14 @@ $jscomp.polyfill("Object.values", function(v) { c.fillStyle = this.properties.color; var e = this.properties.value; this.properties.glowSize ? (c.shadowColor = this.properties.color, c.shadowOffsetX = 0, c.shadowOffsetY = 0, c.shadowBlur = this.properties.glowSize) : c.shadowColor = "transparent"; - var g = this.properties.fontsize; + var h = this.properties.fontsize; c.textAlign = this.properties.align; - c.font = g.toString() + "px " + this.properties.font; + c.font = h.toString() + "px " + this.properties.font; this.str = "number" == typeof e ? e.toFixed(this.properties.decimals) : e; if ("string" == typeof this.str) { e = this.str.split("\\n"); for (var l in e) { - c.fillText(e[l], "left" == this.properties.align ? 15 : this.size[0] - 15, -0.15 * g + g * (parseInt(l) + 1)); + c.fillText(e[l], "left" == this.properties.align ? 15 : this.size[0] - 15, -0.15 * h + h * (parseInt(l) + 1)); } } c.shadowColor = "transparent"; @@ -6584,22 +5867,22 @@ $jscomp.polyfill("Object.values", function(v) { if (this.last_ctx) { var c = this.str.split("\\n"); this.last_ctx.font = this.properties.fontsize + "px " + this.properties.font; - var g = 0, l; + var h = 0, l; for (l in c) { var m = this.last_ctx.measureText(c[l]).width; - g < m && (g = m); + h < m && (h = m); } - this.size[0] = g + 20; + this.size[0] = h + 20; this.size[1] = 4 + c.length * this.properties.fontsize; this.setDirtyCanvas(!0); } }; - w.prototype.onPropertyChanged = function(c, g) { - this.properties[c] = g; - this.str = "number" == typeof g ? g.toFixed(3) : g; + w.prototype.onPropertyChanged = function(c, h) { + this.properties[c] = h; + this.str = "number" == typeof h ? h.toFixed(3) : h; return !0; }; - z.registerNodeType("widget/text", w); + A.registerNodeType("widget/text", w); E.title = "Panel"; E.desc = "Non interactive panel"; E.widgets = [{name:"update", text:"Update", type:"button"}]; @@ -6610,7 +5893,7 @@ $jscomp.polyfill("Object.values", function(v) { this.flags.collapsed || (null == this.lineargradient && this.createGradient(c), this.lineargradient && (c.lineWidth = 1, c.strokeStyle = this.properties.borderColor, c.fillStyle = this.lineargradient, this.properties.shadowSize ? (c.shadowColor = "#000", c.shadowOffsetX = 0, c.shadowOffsetY = 0, c.shadowBlur = this.properties.shadowSize) : c.shadowColor = "transparent", c.roundRect(0, 0, this.size[0] - 1, this.size[1] - 1, this.properties.shadowSize), c.fill(), c.shadowColor = "transparent", c.stroke())); }; - z.registerNodeType("widget/panel", E); + A.registerNodeType("widget/panel", E); })(this); (function(v) { function c() { @@ -6635,11 +5918,11 @@ $jscomp.polyfill("Object.values", function(v) { c.zero = new Float32Array(2); c.buttons = "a b x y lb rb lt rt back start ls rs home".split(" "); c.prototype.onExecute = function() { - var m = this.getGamepad(), g = this.properties.threshold || 0.0; - m && (this._left_axis[0] = Math.abs(m.xbox.axes.lx) > g ? m.xbox.axes.lx : 0, this._left_axis[1] = Math.abs(m.xbox.axes.ly) > g ? m.xbox.axes.ly : 0, this._right_axis[0] = Math.abs(m.xbox.axes.rx) > g ? m.xbox.axes.rx : 0, this._right_axis[1] = Math.abs(m.xbox.axes.ry) > g ? m.xbox.axes.ry : 0, this._triggers[0] = Math.abs(m.xbox.axes.ltrigger) > g ? m.xbox.axes.ltrigger : 0, this._triggers[1] = Math.abs(m.xbox.axes.rtrigger) > g ? m.xbox.axes.rtrigger : 0); + var m = this.getGamepad(), h = this.properties.threshold || 0.0; + m && (this._left_axis[0] = Math.abs(m.xbox.axes.lx) > h ? m.xbox.axes.lx : 0, this._left_axis[1] = Math.abs(m.xbox.axes.ly) > h ? m.xbox.axes.ly : 0, this._right_axis[0] = Math.abs(m.xbox.axes.rx) > h ? m.xbox.axes.rx : 0, this._right_axis[1] = Math.abs(m.xbox.axes.ry) > h ? m.xbox.axes.ry : 0, this._triggers[0] = Math.abs(m.xbox.axes.ltrigger) > h ? m.xbox.axes.ltrigger : 0, this._triggers[1] = Math.abs(m.xbox.axes.rtrigger) > h ? m.xbox.axes.rtrigger : 0); if (this.outputs) { - for (g = 0; g < this.outputs.length; g++) { - var r = this.outputs[g]; + for (h = 0; h < this.outputs.length; h++) { + var r = this.outputs[h]; if (r.links && r.links.length) { var l = null; if (m) { @@ -6715,7 +5998,7 @@ $jscomp.polyfill("Object.values", function(v) { break; case "button_pressed": for (r = 0; r < this._current_buttons.length; ++r) { - this._current_buttons[r] && !this._previous_buttons[r] && this.triggerSlot(g, c.buttons[r]); + this._current_buttons[r] && !this._previous_buttons[r] && this.triggerSlot(h, c.buttons[r]); } } } else { @@ -6730,7 +6013,7 @@ $jscomp.polyfill("Object.values", function(v) { l = 0; } } - this.setOutputData(g, l); + this.setOutputData(h, l); } } } @@ -6744,57 +6027,57 @@ $jscomp.polyfill("Object.values", function(v) { } m = m.call(navigator); this._previous_buttons.set(this._current_buttons); - for (var g = this.properties.gamepad_index; 4 > g; g++) { - if (m[g]) { - m = m[g]; - g = this.xbox_mapping; - g || (g = this.xbox_mapping = {axes:[], buttons:{}, hat:"", hatmap:c.CENTER}); - g.axes.lx = m.axes[0]; - g.axes.ly = m.axes[1]; - g.axes.rx = m.axes[2]; - g.axes.ry = m.axes[3]; - g.axes.ltrigger = m.buttons[6].value; - g.axes.rtrigger = m.buttons[7].value; - g.hat = ""; - g.hatmap = c.CENTER; + for (var h = this.properties.gamepad_index; 4 > h; h++) { + if (m[h]) { + m = m[h]; + h = this.xbox_mapping; + h || (h = this.xbox_mapping = {axes:[], buttons:{}, hat:"", hatmap:c.CENTER}); + h.axes.lx = m.axes[0]; + h.axes.ly = m.axes[1]; + h.axes.rx = m.axes[2]; + h.axes.ry = m.axes[3]; + h.axes.ltrigger = m.buttons[6].value; + h.axes.rtrigger = m.buttons[7].value; + h.hat = ""; + h.hatmap = c.CENTER; for (var r = 0; r < m.buttons.length; r++) { if (this._current_buttons[r] = m.buttons[r].pressed, 12 > r) { - g.buttons[c.mapping_array[r]] = m.buttons[r].pressed, m.buttons[r].was_pressed && this.trigger(c.mapping_array[r] + "_button_event"); + h.buttons[c.mapping_array[r]] = m.buttons[r].pressed, m.buttons[r].was_pressed && this.trigger(c.mapping_array[r] + "_button_event"); } else { switch(r) { case 12: - m.buttons[r].pressed && (g.hat += "up", g.hatmap |= c.UP); + m.buttons[r].pressed && (h.hat += "up", h.hatmap |= c.UP); break; case 13: - m.buttons[r].pressed && (g.hat += "down", g.hatmap |= c.DOWN); + m.buttons[r].pressed && (h.hat += "down", h.hatmap |= c.DOWN); break; case 14: - m.buttons[r].pressed && (g.hat += "left", g.hatmap |= c.LEFT); + m.buttons[r].pressed && (h.hat += "left", h.hatmap |= c.LEFT); break; case 15: - m.buttons[r].pressed && (g.hat += "right", g.hatmap |= c.RIGHT); + m.buttons[r].pressed && (h.hat += "right", h.hatmap |= c.RIGHT); break; case 16: - g.buttons.home = m.buttons[r].pressed; + h.buttons.home = m.buttons[r].pressed; } } } - m.xbox = g; + m.xbox = h; return m; } } }; c.prototype.onDrawBackground = function(c) { if (!this.flags.collapsed) { - var g = this._left_axis, m = this._right_axis; + var h = this._left_axis, m = this._right_axis; c.strokeStyle = "#88A"; - c.strokeRect(0.5 * (g[0] + 1) * this.size[0] - 4, 0.5 * (g[1] + 1) * this.size[1] - 4, 8, 8); + c.strokeRect(0.5 * (h[0] + 1) * this.size[0] - 4, 0.5 * (h[1] + 1) * this.size[1] - 4, 8, 8); c.strokeStyle = "#8A8"; c.strokeRect(0.5 * (m[0] + 1) * this.size[0] - 4, 0.5 * (m[1] + 1) * this.size[1] - 4, 8, 8); - g = this.size[1] / this._current_buttons.length; + h = this.size[1] / this._current_buttons.length; c.fillStyle = "#AEB"; for (m = 0; m < this._current_buttons.length; ++m) { - this._current_buttons[m] && c.fillRect(0, g * m, 6, g); + this._current_buttons[m] && c.fillRect(0, h * m, 6, h); } } }; @@ -6818,7 +6101,7 @@ $jscomp.polyfill("Object.values", function(v) { this.addInput("in"); this.addOutput("out"); } - function g() { + function h() { this.addInput("in", "number", {locked:!0}); this.addOutput("out", "number", {locked:!0}); this.addOutput("clamped", "number", {locked:!0}); @@ -6869,7 +6152,7 @@ $jscomp.polyfill("Object.values", function(v) { this.addOutput("out", "number"); this.size = [80, 30]; } - function z() { + function A() { this.addInput("in", "number"); this.addOutput("out", "number"); this.size = [80, 30]; @@ -6897,7 +6180,7 @@ $jscomp.polyfill("Object.values", function(v) { this.addInput("B"); this.addOutput("out"); } - function G() { + function H() { this.addInput("in", "number"); this.addOutput("out", "number"); this.size = [80, 30]; @@ -6951,7 +6234,7 @@ $jscomp.polyfill("Object.values", function(v) { this.addProperty("offset", 0); this.bgImageUrl = "nodes/imgs/icon-sin.png"; } - function h() { + function g() { this.addInput("x", "number"); this.addInput("y", "number"); this.addOutput("", "number"); @@ -6959,8 +6242,8 @@ $jscomp.polyfill("Object.values", function(v) { this.code_widget = this.addWidget("text", "F(x,y)", this.properties.formula, function(a, b, d) { d.properties.formula = a; }); - this.addWidget("toggle", "allow", A.allow_scripts, function(a) { - A.allow_scripts = a; + this.addWidget("toggle", "allow", z.allow_scripts, function(a) { + z.allow_scripts = a; }); this._func = null; } @@ -6975,7 +6258,7 @@ $jscomp.polyfill("Object.values", function(v) { this.properties = {x:0, y:0}; this._data = new Float32Array(2); } - function H() { + function G() { this.addInput("vec3", "vec3"); this.addOutput("x", "number"); this.addOutput("y", "number"); @@ -7000,7 +6283,7 @@ $jscomp.polyfill("Object.values", function(v) { this.properties = {x:0, y:0, z:0, w:0}; this._data = new Float32Array(4); } - var A = v.LiteGraph; + var z = v.LiteGraph; c.title = "Converter"; c.desc = "type A to type B"; c.prototype.onExecute = function() { @@ -7044,27 +6327,27 @@ $jscomp.polyfill("Object.values", function(v) { c.prototype.onGetOutputs = function() { return [["number", "number"], ["vec2", "vec2"], ["vec3", "vec3"], ["vec4", "vec4"]]; }; - A.registerNodeType("math/converter", c); + z.registerNodeType("math/converter", c); q.title = "Bypass"; q.desc = "removes the type"; q.prototype.onExecute = function() { var a = this.getInputData(0); this.setOutputData(0, a); }; - A.registerNodeType("math/bypass", q); + z.registerNodeType("math/bypass", q); m.title = "to Number"; m.desc = "Cast to number"; m.prototype.onExecute = function() { var a = this.getInputData(0); this.setOutputData(0, Number(a)); }; - A.registerNodeType("math/to_number", m); - g.title = "Range"; - g.desc = "Convert a number from one range to another"; - g.prototype.getTitle = function() { + z.registerNodeType("math/to_number", m); + h.title = "Range"; + h.desc = "Convert a number from one range to another"; + h.prototype.getTitle = function() { return this.flags.collapsed ? (this._last_v || 0).toFixed(2) : this.title; }; - g.prototype.onExecute = function() { + h.prototype.onExecute = function() { if (this.inputs) { for (var a = 0; a < this.inputs.length; a++) { var b = this.inputs[a], d = this.getInputData(a); @@ -7082,13 +6365,13 @@ $jscomp.polyfill("Object.values", function(v) { this.setOutputData(0, this._last_v); this.setOutputData(1, Math.clamp(this._last_v, b, c)); }; - g.prototype.onDrawBackground = function(a) { + h.prototype.onDrawBackground = function(a) { this.outputs[0].label = this._last_v ? this._last_v.toFixed(3) : "?"; }; - g.prototype.onGetInputs = function() { + h.prototype.onGetInputs = function() { return [["in_min", "number"], ["in_max", "number"], ["out_min", "number"], ["out_max", "number"]]; }; - A.registerNodeType("math/range", g); + z.registerNodeType("math/range", h); r.title = "Rand"; r.desc = "Random number"; r.prototype.onExecute = function() { @@ -7108,7 +6391,7 @@ $jscomp.polyfill("Object.values", function(v) { r.prototype.onGetInputs = function() { return [["min", "number"], ["max", "number"]]; }; - A.registerNodeType("math/rand", r); + z.registerNodeType("math/rand", r); l.title = "Noise"; l.desc = "Random number with temporal continuity"; l.data = null; @@ -7138,7 +6421,7 @@ $jscomp.polyfill("Object.values", function(v) { l.prototype.onDrawBackground = function(a) { this.outputs[0].label = (this._last_v || 0).toFixed(3); }; - A.registerNodeType("math/noise", l); + z.registerNodeType("math/noise", l); B.title = "Spikes"; B.desc = "spike every random time"; B.prototype.onExecute = function() { @@ -7150,7 +6433,7 @@ $jscomp.polyfill("Object.values", function(v) { 0 > this._remaining_time ? (this._remaining_time = Math.random() * (this.properties.max_time - this.properties.min_time) + this.properties.min_time, this._blink_time = this.properties.duration, this.boxcolor = "#FFF") : this.boxcolor = "#000"; this.setOutputData(0, a); }; - A.registerNodeType("math/spikes", B); + z.registerNodeType("math/spikes", B); y.title = "Clamp"; y.desc = "Clamp number between min and max"; y.prototype.onExecute = function() { @@ -7162,7 +6445,7 @@ $jscomp.polyfill("Object.values", function(v) { this.isInputConnected(0) && (a += "clamp({{0}}," + this.properties.min + "," + this.properties.max + ")"); return a; }; - A.registerNodeType("math/clamp", y); + z.registerNodeType("math/clamp", y); w.title = "Lerp"; w.desc = "Linear Interpolation"; w.prototype.onExecute = function() { @@ -7177,28 +6460,28 @@ $jscomp.polyfill("Object.values", function(v) { w.prototype.onGetInputs = function() { return [["f", "number"]]; }; - A.registerNodeType("math/lerp", w); + z.registerNodeType("math/lerp", w); E.title = "Abs"; E.desc = "Absolute"; E.prototype.onExecute = function() { var a = this.getInputData(0); null != a && this.setOutputData(0, Math.abs(a)); }; - A.registerNodeType("math/abs", E); - z.title = "Floor"; - z.desc = "Floor number to remove fractional part"; - z.prototype.onExecute = function() { + z.registerNodeType("math/abs", E); + A.title = "Floor"; + A.desc = "Floor number to remove fractional part"; + A.prototype.onExecute = function() { var a = this.getInputData(0); null != a && this.setOutputData(0, Math.floor(a)); }; - A.registerNodeType("math/floor", z); + z.registerNodeType("math/floor", A); e.title = "Frac"; e.desc = "Returns fractional part"; e.prototype.onExecute = function() { var a = this.getInputData(0); null != a && this.setOutputData(0, a % 1); }; - A.registerNodeType("math/frac", e); + z.registerNodeType("math/frac", e); C.title = "Smoothstep"; C.desc = "Smoothstep"; C.prototype.onExecute = function() { @@ -7209,24 +6492,24 @@ $jscomp.polyfill("Object.values", function(v) { this.setOutputData(0, a * a * (3 - 2 * a)); } }; - A.registerNodeType("math/smoothstep", C); + z.registerNodeType("math/smoothstep", C); D.title = "Scale"; D.desc = "v * factor"; D.prototype.onExecute = function() { var a = this.getInputData(0); null != a && this.setOutputData(0, a * this.properties.factor); }; - A.registerNodeType("math/scale", D); + z.registerNodeType("math/scale", D); u.title = "Gate"; u.desc = "if v is true, then outputs A, otherwise B"; u.prototype.onExecute = function() { var a = this.getInputData(0); this.setOutputData(0, this.getInputData(a ? 1 : 2)); }; - A.registerNodeType("math/gate", u); - G.title = "Average"; - G.desc = "Average Filter"; - G.prototype.onExecute = function() { + z.registerNodeType("math/gate", u); + H.title = "Average"; + H.desc = "Average Filter"; + H.prototype.onExecute = function() { var a = this.getInputData(0); null == a && (a = 0); var b = this._values.length; @@ -7238,14 +6521,14 @@ $jscomp.polyfill("Object.values", function(v) { } this.setOutputData(0, a / b); }; - G.prototype.onPropertyChanged = function(a, b) { + H.prototype.onPropertyChanged = function(a, b) { 1 > b && (b = 1); this.properties.samples = Math.round(b); a = this._values; this._values = new Float32Array(this.properties.samples); a.length <= this._values.length ? this._values.set(a) : this._values.set(a.subarray(0, this._values.length)); }; - A.registerNodeType("math/average", G); + z.registerNodeType("math/average", H); n.title = "TendTo"; n.desc = "moves the output value always closer to the input"; n.prototype.onExecute = function() { @@ -7255,7 +6538,7 @@ $jscomp.polyfill("Object.values", function(v) { this._value = null == this._value ? a : this._value * (1 - b) + a * b; this.setOutputData(0, this._value); }; - A.registerNodeType("math/tendTo", n); + z.registerNodeType("math/tendTo", n); p.values = "+ - * / % ^ max min".split(" "); p.title = "Operation"; p.desc = "Easy math operators"; @@ -7306,11 +6589,11 @@ $jscomp.polyfill("Object.values", function(v) { this.setOutputData(0, d); }; p.prototype.onDrawBackground = function(a) { - this.flags.collapsed || (a.font = "40px Arial", a.fillStyle = "#666", a.textAlign = "center", a.fillText(this.properties.OP, 0.5 * this.size[0], 0.5 * (this.size[1] + A.NODE_TITLE_HEIGHT)), a.textAlign = "left"); + this.flags.collapsed || (a.font = "40px Arial", a.fillStyle = "#666", a.textAlign = "center", a.fillText(this.properties.OP, 0.5 * this.size[0], 0.5 * (this.size[1] + z.NODE_TITLE_HEIGHT)), a.textAlign = "left"); }; - A.registerNodeType("math/operation", p); - A.registerSearchboxExtra("math/operation", "MAX", {properties:{OP:"max"}, title:"MAX()"}); - A.registerSearchboxExtra("math/operation", "MIN", {properties:{OP:"min"}, title:"MIN()"}); + z.registerNodeType("math/operation", p); + z.registerSearchboxExtra("math/operation", "MAX", {properties:{OP:"max"}, title:"MAX()"}); + z.registerSearchboxExtra("math/operation", "MIN", {properties:{OP:"min"}, title:"MIN()"}); k.title = "Compare"; k.desc = "compares between two values"; k.prototype.onExecute = function() { @@ -7346,13 +6629,13 @@ $jscomp.polyfill("Object.values", function(v) { k.prototype.onGetOutputs = function() { return [["A==B", "boolean"], ["A!=B", "boolean"], ["A>B", "boolean"], ["A=B", "boolean"], ["A<=B", "boolean"]]; }; - A.registerNodeType("math/compare", k); - A.registerSearchboxExtra("math/compare", "==", {outputs:[["A==B", "boolean"]], title:"A==B"}); - A.registerSearchboxExtra("math/compare", "!=", {outputs:[["A!=B", "boolean"]], title:"A!=B"}); - A.registerSearchboxExtra("math/compare", ">", {outputs:[["A>B", "boolean"]], title:"A>B"}); - A.registerSearchboxExtra("math/compare", "<", {outputs:[["A=", {outputs:[["A>=B", "boolean"]], title:"A>=B"}); - A.registerSearchboxExtra("math/compare", "<=", {outputs:[["A<=B", "boolean"]], title:"A<=B"}); + z.registerNodeType("math/compare", k); + z.registerSearchboxExtra("math/compare", "==", {outputs:[["A==B", "boolean"]], title:"A==B"}); + z.registerSearchboxExtra("math/compare", "!=", {outputs:[["A!=B", "boolean"]], title:"A!=B"}); + z.registerSearchboxExtra("math/compare", ">", {outputs:[["A>B", "boolean"]], title:"A>B"}); + z.registerSearchboxExtra("math/compare", "<", {outputs:[["A=", {outputs:[["A>=B", "boolean"]], title:"A>=B"}); + z.registerSearchboxExtra("math/compare", "<=", {outputs:[["A<=B", "boolean"]], title:"A<=B"}); a.values = "> < == != <= >= || &&".split(" "); a["@OP"] = {type:"enum", title:"operation", values:a.values}; a.title = "Condition"; @@ -7394,7 +6677,7 @@ $jscomp.polyfill("Object.values", function(v) { this.setOutputData(0, d); this.setOutputData(1, !d); }; - A.registerNodeType("math/condition", a); + z.registerNodeType("math/condition", a); b.title = "Accumulate"; b.desc = "Increments a value every time"; b.prototype.onExecute = function() { @@ -7403,7 +6686,7 @@ $jscomp.polyfill("Object.values", function(v) { this.properties.value = null !== a ? this.properties.value + a : this.properties.value + this.properties.increment; this.setOutputData(0, this.properties.value); }; - A.registerNodeType("math/accumulate", b); + z.registerNodeType("math/accumulate", b); d.title = "Trigonometry"; d.desc = "Sin Cos Tan"; d.prototype.onExecute = function() { @@ -7444,18 +6727,18 @@ $jscomp.polyfill("Object.values", function(v) { d.prototype.onGetOutputs = function() { return [["sin", "number"], ["cos", "number"], ["tan", "number"], ["asin", "number"], ["acos", "number"], ["atan", "number"]]; }; - A.registerNodeType("math/trigonometry", d); - A.registerSearchboxExtra("math/trigonometry", "SIN()", {outputs:[["sin", "number"]], title:"SIN()"}); - A.registerSearchboxExtra("math/trigonometry", "COS()", {outputs:[["cos", "number"]], title:"COS()"}); - A.registerSearchboxExtra("math/trigonometry", "TAN()", {outputs:[["tan", "number"]], title:"TAN()"}); - h.title = "Formula"; - h.desc = "Compute formula"; - h.size = [160, 100]; - G.prototype.onPropertyChanged = function(a, b) { + z.registerNodeType("math/trigonometry", d); + z.registerSearchboxExtra("math/trigonometry", "SIN()", {outputs:[["sin", "number"]], title:"SIN()"}); + z.registerSearchboxExtra("math/trigonometry", "COS()", {outputs:[["cos", "number"]], title:"COS()"}); + z.registerSearchboxExtra("math/trigonometry", "TAN()", {outputs:[["tan", "number"]], title:"TAN()"}); + g.title = "Formula"; + g.desc = "Compute formula"; + g.size = [160, 100]; + H.prototype.onPropertyChanged = function(a, b) { "formula" == a && (this.code_widget.value = b); }; - h.prototype.onExecute = function() { - if (A.allow_scripts) { + g.prototype.onExecute = function() { + if (z.allow_scripts) { var a = this.getInputData(0), b = this.getInputData(1); null != a ? this.properties.x = a : a = this.properties.x; null != b ? this.properties.y = b : b = this.properties.y; @@ -7469,21 +6752,21 @@ $jscomp.polyfill("Object.values", function(v) { this.setOutputData(0, d); } }; - h.prototype.getTitle = function() { + g.prototype.getTitle = function() { return this._func_code || "Formula"; }; - h.prototype.onDrawBackground = function() { + g.prototype.onDrawBackground = function() { var a = this.properties.formula; this.outputs && this.outputs.length && (this.outputs[0].label = a); }; - A.registerNodeType("math/formula", h); + z.registerNodeType("math/formula", g); f.title = "Vec2->XY"; f.desc = "vector 2 to components"; f.prototype.onExecute = function() { var a = this.getInputData(0); null != a && (this.setOutputData(0, a[0]), this.setOutputData(1, a[1])); }; - A.registerNodeType("math3d/vec2-to-xy", f); + z.registerNodeType("math3d/vec2-to-xy", f); x.title = "XY->Vec2"; x.desc = "components to vector2"; x.prototype.onExecute = function() { @@ -7496,14 +6779,14 @@ $jscomp.polyfill("Object.values", function(v) { d[1] = b; this.setOutputData(0, d); }; - A.registerNodeType("math3d/xy-to-vec2", x); - H.title = "Vec3->XYZ"; - H.desc = "vector 3 to components"; - H.prototype.onExecute = function() { + z.registerNodeType("math3d/xy-to-vec2", x); + G.title = "Vec3->XYZ"; + G.desc = "vector 3 to components"; + G.prototype.onExecute = function() { var a = this.getInputData(0); null != a && (this.setOutputData(0, a[0]), this.setOutputData(1, a[1]), this.setOutputData(2, a[2])); }; - A.registerNodeType("math3d/vec3-to-xyz", H); + z.registerNodeType("math3d/vec3-to-xyz", G); I.title = "XYZ->Vec3"; I.desc = "components to vector3"; I.prototype.onExecute = function() { @@ -7519,14 +6802,14 @@ $jscomp.polyfill("Object.values", function(v) { c[2] = d; this.setOutputData(0, c); }; - A.registerNodeType("math3d/xyz-to-vec3", I); + z.registerNodeType("math3d/xyz-to-vec3", I); J.title = "Vec4->XYZW"; J.desc = "vector 4 to components"; J.prototype.onExecute = function() { var a = this.getInputData(0); null != a && (this.setOutputData(0, a[0]), this.setOutputData(1, a[1]), this.setOutputData(2, a[2]), this.setOutputData(3, a[3])); }; - A.registerNodeType("math3d/vec4-to-xyzw", J); + z.registerNodeType("math3d/vec4-to-xyzw", J); K.title = "XYZW->Vec4"; K.desc = "components to vector4"; K.prototype.onExecute = function() { @@ -7545,7 +6828,7 @@ $jscomp.polyfill("Object.values", function(v) { k[3] = c; this.setOutputData(0, k); }; - A.registerNodeType("math3d/xyzw-to-vec4", K); + z.registerNodeType("math3d/xyzw-to-vec4", K); })(this); (function(v) { function c() { @@ -7571,11 +6854,11 @@ $jscomp.polyfill("Object.values", function(v) { c.prototype.onDrawBackground = function(c) { if (!this.flags.collapsed) { c.fillStyle = "#AFB"; - var g = (this.selected + 1) * m.NODE_SLOT_HEIGHT + 6; + var h = (this.selected + 1) * m.NODE_SLOT_HEIGHT + 6; c.beginPath(); - c.moveTo(50, g); - c.lineTo(50, g + m.NODE_SLOT_HEIGHT); - c.lineTo(34, g + 0.5 * m.NODE_SLOT_HEIGHT); + c.moveTo(50, h); + c.lineTo(50, h + m.NODE_SLOT_HEIGHT); + c.lineTo(34, h + 0.5 * m.NODE_SLOT_HEIGHT); c.fill(); } }; @@ -7625,7 +6908,7 @@ $jscomp.polyfill("Object.values", function(v) { this.addOutput("name", "string"); this.properties = {name:"", generate_mipmaps:!1}; } - function g() { + function h() { this.addInput("Texture", "Texture"); this.addInput("TextureB", "Texture"); this.addInput("value", "number"); @@ -7670,7 +6953,7 @@ $jscomp.polyfill("Object.values", function(v) { this.addOutput("", "Texture"); this.properties = {iterations:1, generate_mipmaps:!1, precision:c.DEFAULT}; } - function z() { + function A() { this.addInput("Texture", "Texture"); this.addOutput("tex", "Texture"); this.addOutput("avg", "vec4"); @@ -7707,13 +6990,13 @@ $jscomp.polyfill("Object.values", function(v) { this.properties = {enabled:!0, intensity:1, precision:c.DEFAULT, texture:null}; u._shader || (u._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, u.pixel_shader)); } - function G() { + function H() { this.addInput("Texture", "Texture"); this.addOutput("R", "Texture"); this.addOutput("G", "Texture"); this.addOutput("B", "Texture"); this.addOutput("A", "Texture"); - G._shader || (G._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, G.pixel_shader)); + H._shader || (H._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, H.pixel_shader)); } function n() { this.addInput("R", "Texture"); @@ -7760,11 +7043,11 @@ $jscomp.polyfill("Object.values", function(v) { this.properties = {distance:100, range:50, only_depth:!1, high_precision:!1}; this._uniforms = {u_texture:0, u_distance:100, u_range:50, u_camera_planes:null}; } - function h() { + function g() { this.addInput("Texture", "Texture"); this.addOutput("Texture", "Texture"); this.properties = {precision:c.DEFAULT, invert:!1}; - this._uniforms = {u_texture:0, u_near:0.1, u_far:10000}; + this._uniforms = {u_texture:0, u_camera_planes:null, u_ires:vec2.create()}; } function f() { this.addInput("Texture", "Texture"); @@ -7782,7 +7065,7 @@ $jscomp.polyfill("Object.values", function(v) { this._textures = []; this._uniforms = {u_intensity:1, u_texture:0, u_glow_texture:1, u_threshold:0, u_texel_size:vec2.create()}; } - function H() { + function G() { this.addInput("Texture", "Texture"); this.addOutput("Filtered", "Texture"); this.properties = {intensity:1, radius:5}; @@ -7805,7 +7088,7 @@ $jscomp.polyfill("Object.values", function(v) { this.properties = {enabled:!0, factor:1, precision:c.LOW}; this._uniforms = {u_texture:0, u_factor:1}; } - function A() { + function z() { this.addInput("in", "Texture"); this.addOutput("out", "Texture"); this.properties = {precision:c.LOW, split_channels:!1}; @@ -7860,9 +7143,9 @@ $jscomp.polyfill("Object.values", function(v) { this.addOutput("out", "texture"); this.properties = {yaw:0}; } - var F = v.LiteGraph; + var F = v.LiteGraph, R = v.LGraphCanvas; v.LGraphTexture = null; - "undefined" != typeof GL && (LGraphCanvas.link_type_colors.Texture = "#987", v.LGraphTexture = c, c.title = "Texture", c.desc = "Texture", c.widgets_info = {name:{widget:"texture"}, filter:{widget:"checkbox"}}, c.loadTextureCallback = null, c.image_preview_size = 256, c.PASS_THROUGH = 1, c.COPY = 2, c.LOW = 3, c.HIGH = 4, c.REUSE = 5, c.DEFAULT = 2, c.MODE_VALUES = {"pass through":c.PASS_THROUGH, copy:c.COPY, low:c.LOW, high:c.HIGH, reuse:c.REUSE, default:c.DEFAULT}, c.getTexturesContainer = function() { + "undefined" != typeof GL && (R.link_type_colors.Texture = "#987", v.LGraphTexture = c, c.title = "Texture", c.desc = "Texture", c.widgets_info = {name:{widget:"texture"}, filter:{widget:"checkbox"}}, c.loadTextureCallback = null, c.image_preview_size = 256, c.PASS_THROUGH = 1, c.COPY = 2, c.LOW = 3, c.HIGH = 4, c.REUSE = 5, c.DEFAULT = 2, c.MODE_VALUES = {"pass through":c.PASS_THROUGH, copy:c.COPY, low:c.LOW, high:c.HIGH, reuse:c.REUSE, default:c.DEFAULT}, c.getTexturesContainer = function() { return gl.textures; }, c.loadTexture = function(a, b) { b = b || {}; @@ -8004,16 +7287,16 @@ $jscomp.polyfill("Object.values", function(v) { }, m.prototype.onExecute = function() { var a = this.getInputData(0); a && (this.properties.generate_mipmaps && (a.bind(0), a.setParameter(gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR), gl.generateMipmap(a.texture_type), a.unbind(0)), this.properties.name && (c.storeTexture ? c.storeTexture(this.properties.name, a) : c.getTexturesContainer()[this.properties.name] = a), this._texture = a, this.setOutputData(0, a), this.setOutputData(1, this.properties.name)); - }, F.registerNodeType("texture/save", m), g.widgets_info = {uvcode:{widget:"code"}, pixelcode:{widget:"code"}, precision:{widget:"combo", values:c.MODE_VALUES}}, g.title = "Operation", g.desc = "Texture shader operation", g.presets = {}, g.prototype.getExtraMenuOptions = function(a) { + }, F.registerNodeType("texture/save", m), h.widgets_info = {uvcode:{widget:"code"}, pixelcode:{widget:"code"}, precision:{widget:"combo", values:c.MODE_VALUES}}, h.title = "Operation", h.desc = "Texture shader operation", h.presets = {}, h.prototype.getExtraMenuOptions = function(a) { var b = this; return [{content:b.properties.show ? "Hide Texture" : "Show Texture", callback:function() { b.properties.show = !b.properties.show; }}]; - }, g.prototype.onPropertyChanged = function() { + }, h.prototype.onPropertyChanged = function() { this.has_error = !1; - }, g.prototype.onDrawBackground = function(a) { + }, h.prototype.onDrawBackground = function(a) { this.flags.collapsed || 20 >= this.size[1] || !this.properties.show || !this._tex || this._tex.gl != a || (a.save(), a.drawImage(this._tex, 0, 0, this.size[0], this.size[1]), a.restore()); - }, g.prototype.onExecute = function() { + }, h.prototype.onExecute = function() { var a = this.getInputData(0); if (this.isOutputConnected(0)) { if (this.properties.precision === c.PASS_THROUGH) { @@ -8028,21 +7311,21 @@ $jscomp.polyfill("Object.values", function(v) { this._tex = a || this._tex ? c.getTargetTexture(a || this._tex, this._tex, this.properties.precision) : new GL.Texture(d, k, {type:f, format:gl.RGBA, filter:gl.LINEAR}); f = ""; this.properties.uvcode && (f = "uv = " + this.properties.uvcode, -1 != this.properties.uvcode.indexOf(";") && (f = this.properties.uvcode)); - var h = ""; - this.properties.pixelcode && (h = "result = " + this.properties.pixelcode, -1 != this.properties.pixelcode.indexOf(";") && (h = this.properties.pixelcode)); + var g = ""; + this.properties.pixelcode && (g = "result = " + this.properties.pixelcode, -1 != this.properties.pixelcode.indexOf(";") && (g = this.properties.pixelcode)); var e = this._shader; - if (!(this.has_error || e && this._shader_code == f + "|" + h)) { - var n = c.replaceCode(g.pixel_shader, {UV_CODE:f, PIXEL_CODE:h}); + if (!(this.has_error || e && this._shader_code == f + "|" + g)) { + var n = c.replaceCode(h.pixel_shader, {UV_CODE:f, PIXEL_CODE:g}); try { e = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, n), this.boxcolor = "#00FF00"; - } catch (T) { - GL.Shader.dumpErrorToConsole(T, Shader.SCREEN_VERTEX_SHADER, n); + } catch (U) { + GL.Shader.dumpErrorToConsole(U, Shader.SCREEN_VERTEX_SHADER, n); this.boxcolor = "#FF0000"; this.has_error = !0; return; } this._shader = e; - this._shader_code = f + "|" + h; + this._shader_code = f + "|" + g; } if (this._shader) { var p = this.getInputData(2); @@ -8062,18 +7345,18 @@ $jscomp.polyfill("Object.values", function(v) { } } } - }, g.pixel_shader = "precision highp float;\n\r\n\t\t\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform vec2 texSize;\n\r\n\t\tuniform float time;\n\r\n\t\tuniform float value;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec2 uv = v_coord;\n\r\n\t\t\t{{UV_CODE}};\n\r\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\r\n\t\t\tvec3 color = color4.rgb;\n\r\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\r\n\t\t\tvec3 colorB = color4B.rgb;\n\r\n\t\t\tvec3 result = color;\n\r\n\t\t\tfloat alpha = 1.0;\n\r\n\t\t\t{{PIXEL_CODE}};\n\r\n\t\t\tgl_FragColor = vec4(result, alpha);\n\r\n\t\t}\n\r\n\t\t", - g.registerPreset = function(a, b) { - g.presets[a] = b; - }, g.registerPreset("", ""), g.registerPreset("bypass", "color"), g.registerPreset("add", "color + colorB * value"), g.registerPreset("substract", "(color - colorB) * value"), g.registerPreset("mate", "mix( color, colorB, color4B.a * value)"), g.registerPreset("invert", "vec3(1.0) - color"), g.registerPreset("multiply", "color * colorB * value"), g.registerPreset("divide", "(color / colorB) / value"), g.registerPreset("difference", "abs(color - colorB) * value"), g.registerPreset("max", "max(color, colorB) * value"), - g.registerPreset("min", "min(color, colorB) * value"), g.registerPreset("displace", "texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"), g.registerPreset("grayscale", "vec3(color.x + color.y + color.z) * value / 3.0"), g.registerPreset("saturation", "mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"), g.registerPreset("threshold", "vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"), g.prototype.onInspect = + }, h.pixel_shader = "precision highp float;\n\r\n\t\t\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform vec2 texSize;\n\r\n\t\tuniform float time;\n\r\n\t\tuniform float value;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec2 uv = v_coord;\n\r\n\t\t\t{{UV_CODE}};\n\r\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\r\n\t\t\tvec3 color = color4.rgb;\n\r\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\r\n\t\t\tvec3 colorB = color4B.rgb;\n\r\n\t\t\tvec3 result = color;\n\r\n\t\t\tfloat alpha = 1.0;\n\r\n\t\t\t{{PIXEL_CODE}};\n\r\n\t\t\tgl_FragColor = vec4(result, alpha);\n\r\n\t\t}\n\r\n\t\t", + h.registerPreset = function(a, b) { + h.presets[a] = b; + }, h.registerPreset("", ""), h.registerPreset("bypass", "color"), h.registerPreset("add", "color + colorB * value"), h.registerPreset("substract", "(color - colorB) * value"), h.registerPreset("mate", "mix( color, colorB, color4B.a * value)"), h.registerPreset("invert", "vec3(1.0) - color"), h.registerPreset("multiply", "color * colorB * value"), h.registerPreset("divide", "(color / colorB) / value"), h.registerPreset("difference", "abs(color - colorB) * value"), h.registerPreset("max", "max(color, colorB) * value"), + h.registerPreset("min", "min(color, colorB) * value"), h.registerPreset("displace", "texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"), h.registerPreset("grayscale", "vec3(color.x + color.y + color.z) * value / 3.0"), h.registerPreset("saturation", "mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"), h.registerPreset("threshold", "vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"), h.prototype.onInspect = function(a) { var b = this; - a.addCombo("Presets", "", {values:Object.keys(g.presets), callback:function(d) { - var c = g.presets[d]; + a.addCombo("Presets", "", {values:Object.keys(h.presets), callback:function(d) { + var c = h.presets[d]; c && (b.setProperty("pixelcode", c), b.title = d, a.refresh()); }}); - }, F.registerNodeType("texture/operation", g), r.title = "Shader", r.desc = "Texture shader", r.widgets_info = {code:{type:"code", lang:"glsl"}, precision:{widget:"combo", values:c.MODE_VALUES}}, r.prototype.onPropertyChanged = function(a, b) { + }, F.registerNodeType("texture/operation", h), r.title = "Shader", r.desc = "Texture shader", r.widgets_info = {code:{type:"code", lang:"glsl"}, precision:{widget:"combo", values:c.MODE_VALUES}}, r.prototype.onPropertyChanged = function(a, b) { if ("code" == a && (a = this.getShader())) { b = a.uniformInfo; if (this.inputs) { @@ -8135,8 +7418,8 @@ $jscomp.polyfill("Object.values", function(v) { var b = 0, d = null; if (this.inputs) { for (var k = 0; k < this.inputs.length; ++k) { - var f = this.getInputInfo(k), h = this.getInputData(k); - null != h && (h.constructor === GL.Texture && (h.bind(b), d || (d = h), h = b, b++), a.setUniform(f.name, h)); + var f = this.getInputInfo(k), g = this.getInputData(k); + null != g && (g.constructor === GL.Texture && (g.bind(b), d || (d = g), g = b, b++), a.setUniform(f.name, g)); } } var e = this._uniforms; @@ -8169,8 +7452,8 @@ $jscomp.polyfill("Object.values", function(v) { this._tex && this._tex.width == b && this._tex.height == d && this._tex.type == k || (this._tex = new GL.Texture(b, d, {type:k, format:gl.RGBA, filter:gl.LINEAR})); var f = this._shader; f || (f = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, l.pixel_shader)); - var h = this.getInputData(1); - h ? (this.properties.scale[0] = h[0], this.properties.scale[1] = h[1]) : h = this.properties.scale; + var g = this.getInputData(1); + g ? (this.properties.scale[0] = g[0], this.properties.scale[1] = g[1]) : g = this.properties.scale; var e = this.getInputData(2); e ? (this.properties.offset[0] = e[0], this.properties.offset[1] = e[1]) : e = this.properties.offset; this._tex.drawTo(function() { @@ -8179,7 +7462,7 @@ $jscomp.polyfill("Object.values", function(v) { gl.disable(gl.BLEND); a.bind(0); var b = Mesh.getScreenQuad(); - f.uniforms({u_texture:0, u_scale:h, u_offset:e}).draw(b); + f.uniforms({u_texture:0, u_scale:g, u_offset:e}).draw(b); }); this.setOutputData(0, this._tex); } @@ -8198,10 +7481,10 @@ $jscomp.polyfill("Object.values", function(v) { f || (f = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, B.pixel_shader)); d = this.getInputData(2); null != d ? this.properties.factor = d : d = parseFloat(this.properties.factor); - var h = this._uniforms; - h.u_factor = d; - h.u_scale.set(this.properties.scale); - h.u_offset.set(this.properties.offset); + var g = this._uniforms; + g.u_factor = d; + g.u_scale.set(this.properties.scale); + g.u_offset.set(this.properties.offset); this._tex.drawTo(function() { gl.disable(gl.DEPTH_TEST); gl.disable(gl.CULL_FACE); @@ -8209,7 +7492,7 @@ $jscomp.polyfill("Object.values", function(v) { a && a.bind(0); b && b.bind(1); var d = Mesh.getScreenQuad(); - f.uniforms(h).draw(d); + f.uniforms(g).draw(d); }); this.setOutputData(0, this._tex); } @@ -8259,13 +7542,13 @@ $jscomp.polyfill("Object.values", function(v) { b || (E._shader = b = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, E.pixel_shader)); var d = a.width | 0, k = a.height | 0, f = a.type; this.properties.precision === c.LOW ? f = gl.UNSIGNED_BYTE : this.properties.precision === c.HIGH && (f = gl.HIGH_PRECISION_FORMAT); - var h = this.properties.iterations || 1, e = a, n = []; + var g = this.properties.iterations || 1, e = a, n = []; f = {type:f, format:a.format}; - var g = vec2.create(), p = {u_offset:g}; + var h = vec2.create(), p = {u_offset:h}; this._texture && GL.Texture.releaseTemporary(this._texture); - for (var l = 0; l < h; ++l) { - g[0] = 1 / d; - g[1] = 1 / k; + for (var l = 0; l < g; ++l) { + h[0] = 1 / d; + h[1] = 1 / k; d = d >> 1 || 0; k = k >> 1 || 0; a = GL.Texture.getTemporary(d, k, f); @@ -8286,23 +7569,23 @@ $jscomp.polyfill("Object.values", function(v) { } } }, E.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec2 u_offset;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = texture2D(u_texture, v_coord );\n\r\n\t\t\tcolor += texture2D(u_texture, v_coord + vec2( u_offset.x, 0.0 ) );\n\r\n\t\t\tcolor += texture2D(u_texture, v_coord + vec2( 0.0, u_offset.y ) );\n\r\n\t\t\tcolor += texture2D(u_texture, v_coord + vec2( u_offset.x, u_offset.y ) );\n\r\n\t\t gl_FragColor = color * 0.25;\n\r\n\t\t}\n\r\n\t\t", - F.registerNodeType("texture/downsample", E), z.title = "Average", z.desc = "Compute a partial average (32 random samples) of a texture and stores it as a 1x1 pixel texture.\n If high_quality is true, then it generates the mipmaps first and reads from the lower one.", z.prototype.onExecute = function() { + F.registerNodeType("texture/downsample", E), A.title = "Average", A.desc = "Compute a partial average (32 random samples) of a texture and stores it as a 1x1 pixel texture.\n If high_quality is true, then it generates the mipmaps first and reads from the lower one.", A.prototype.onExecute = function() { this.properties.use_previous_frame || this.updateAverage(); var a = this._luminance; this.setOutputData(0, this._temp_texture); this.setOutputData(1, a); this.setOutputData(2, (a[0] + a[1] + a[2]) / 3); - }, z.prototype.onPreRenderExecute = function() { + }, A.prototype.onPreRenderExecute = function() { this.updateAverage(); - }, z.prototype.updateAverage = function() { + }, A.prototype.updateAverage = function() { var a = this.getInputData(0); if (a && (this.isOutputConnected(0) || this.isOutputConnected(1) || this.isOutputConnected(2))) { - if (!z._shader) { - z._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, z.pixel_shader); + if (!A._shader) { + A._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, A.pixel_shader); for (var b = new Float32Array(16), d = 0; d < b.length; ++d) { b[d] = Math.random(); } - z._shader.uniforms({u_samples_a:b.subarray(0, 16), u_samples_b:b.subarray(16, 32)}); + A._shader.uniforms({u_samples_a:b.subarray(0, 16), u_samples_b:b.subarray(16, 32)}); } d = this._temp_texture; b = gl.UNSIGNED_BYTE; @@ -8310,7 +7593,7 @@ $jscomp.polyfill("Object.values", function(v) { d && d.type == b || (this._temp_texture = new GL.Texture(1, 1, {type:b, format:gl.RGBA, filter:gl.NEAREST})); this._uniforms.u_mipmap_offset = 0; this.properties.high_quality && (this._temp_pot2_texture && this._temp_pot2_texture.type == b || (this._temp_pot2_texture = new GL.Texture(512, 512, {type:b, format:gl.RGBA, minFilter:gl.LINEAR_MIPMAP_LINEAR, magFilter:gl.LINEAR})), a.copyTo(this._temp_pot2_texture), a = this._temp_pot2_texture, a.bind(0), gl.generateMipmap(GL.TEXTURE_2D), this._uniforms.u_mipmap_offset = 9); - var c = z._shader, k = this._uniforms; + var c = A._shader, k = this._uniforms; k.u_mipmap_offset = this.properties.mipmap_offset; gl.disable(gl.DEPTH_TEST); gl.disable(gl.BLEND); @@ -8326,8 +7609,8 @@ $jscomp.polyfill("Object.values", function(v) { } } } - }, z.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tuniform mat4 u_samples_a;\n\r\n\t\tuniform mat4 u_samples_b;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_mipmap_offset;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = vec4(0.0);\n\r\n\t\t\t//random average\n\r\n\t\t\tfor(int i = 0; i < 4; ++i)\n\r\n\t\t\t\tfor(int j = 0; j < 4; ++j)\n\r\n\t\t\t\t{\n\r\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\r\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\r\n\t\t\t\t}\n\r\n\t\t gl_FragColor = color * 0.03125;\n\r\n\t\t}\n\r\n\t\t", - F.registerNodeType("texture/average", z), e.title = "Smooth", e.desc = "Smooth texture over time", e.prototype.onExecute = function() { + }, A.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tuniform mat4 u_samples_a;\n\r\n\t\tuniform mat4 u_samples_b;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_mipmap_offset;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = vec4(0.0);\n\r\n\t\t\t//random average\n\r\n\t\t\tfor(int i = 0; i < 4; ++i)\n\r\n\t\t\t\tfor(int j = 0; j < 4; ++j)\n\r\n\t\t\t\t{\n\r\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\r\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\r\n\t\t\t\t}\n\r\n\t\t gl_FragColor = color * 0.03125;\n\r\n\t\t}\n\r\n\t\t", + F.registerNodeType("texture/average", A), e.title = "Smooth", e.desc = "Smooth texture over time", e.prototype.onExecute = function() { var a = this.getInputData(0); if (a && this.isOutputConnected(0)) { e._shader || (e._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, e.pixel_shader)); @@ -8357,14 +7640,14 @@ $jscomp.polyfill("Object.values", function(v) { if (0 == c || 0 == d % c) { d = this._temp_texture; d && d.type == a.type && d.width == b || (d = {type:a.type, format:gl.RGBA, filter:gl.NEAREST}, this._temp_texture = new GL.Texture(b, 1, d), this._temp_texture2 = new GL.Texture(b, 1, d), this._temp_texture_out = new GL.Texture(1, 1, d)); - var k = this._temp_texture, f = this._temp_texture2, h = C._shader_copy, e = C._shader_avg, n = this._uniforms; + var k = this._temp_texture, f = this._temp_texture2, g = C._shader_copy, e = C._shader_avg, n = this._uniforms; n.u_samples = b; n.u_isamples = 1.0 / b; gl.disable(gl.BLEND); gl.disable(gl.DEPTH_TEST); k.drawTo(function() { f.bind(1); - a.toViewport(h, n); + a.toViewport(g, n); }); this._temp_texture_out.drawTo(function() { k.toViewport(e, n); @@ -8391,8 +7674,8 @@ $jscomp.polyfill("Object.values", function(v) { c && c.width == b && c.height == d || (this._temp_texture = new GL.Texture(b, d, {format:gl.RGBA, filter:gl.LINEAR})); try { this._temp_texture.uploadImage(a); - } catch (S) { - console.error("image comes from an unsafe location, cannot be uploaded to webgl: " + S); + } catch (T) { + console.error("image comes from an unsafe location, cannot be uploaded to webgl: " + T); return; } this.setOutputData(0, this._temp_texture); @@ -8428,7 +7711,7 @@ $jscomp.polyfill("Object.values", function(v) { } } }, u.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tuniform float u_amount;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\r\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\r\n\t\t\t mediump vec2 quad1;\n\r\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\r\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\r\n\t\t\t mediump vec2 quad2;\n\r\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\r\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\r\n\t\t\t highp vec2 texPos1;\n\r\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\r\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\r\n\t\t\t highp vec2 texPos2;\n\r\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\r\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\r\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\r\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\r\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\r\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\r\n\t\t}\n\r\n\t\t", - F.registerNodeType("texture/LUT", u), G.title = "Texture to Channels", G.desc = "Split texture channels", G.prototype.onExecute = function() { + F.registerNodeType("texture/LUT", u), H.title = "Texture to Channels", H.desc = "Split texture channels", H.prototype.onExecute = function() { var a = this.getInputData(0); if (a) { this._channels || (this._channels = Array(4)); @@ -8438,26 +7721,26 @@ $jscomp.polyfill("Object.values", function(v) { if (d) { gl.disable(gl.BLEND); gl.disable(gl.DEPTH_TEST); - var k = Mesh.getScreenQuad(), f = G._shader, h = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]; + var k = Mesh.getScreenQuad(), f = H._shader, g = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]; for (c = 0; 4 > c; c++) { this._channels[c] && (this._channels[c].drawTo(function() { a.bind(0); - f.uniforms({u_texture:0, u_mask:h[c]}).draw(k); + f.uniforms({u_texture:0, u_mask:g[c]}).draw(k); }), this.setOutputData(c, this._channels[c])); } } } - }, G.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec4 u_mask;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\r\n\t\t}\n\r\n\t\t", F.registerNodeType("texture/textureChannels", G), n.title = "Channels to Texture", n.desc = "Split texture channels", n.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, + }, H.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec4 u_mask;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\r\n\t\t}\n\r\n\t\t", F.registerNodeType("texture/textureChannels", H), n.title = "Channels to Texture", n.desc = "Split texture channels", n.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, n.prototype.onExecute = function() { var a = c.getWhiteTexture(), b = this.getInputData(0) || a, d = this.getInputData(1) || a, k = this.getInputData(2) || a, f = this.getInputData(3) || a; gl.disable(gl.BLEND); gl.disable(gl.DEPTH_TEST); - var h = Mesh.getScreenQuad(); + var g = Mesh.getScreenQuad(); n._shader || (n._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, n.pixel_shader)); var e = n._shader; a = Math.max(b.width, d.width, k.width, f.width); - var g = Math.max(b.height, d.height, k.height, f.height), p = this.properties.precision == c.HIGH ? c.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE; - this._texture && this._texture.width == a && this._texture.height == g && this._texture.type == p || (this._texture = new GL.Texture(a, g, {type:p, format:gl.RGBA, filter:gl.LINEAR})); + var h = Math.max(b.height, d.height, k.height, f.height), p = this.properties.precision == c.HIGH ? c.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE; + this._texture && this._texture.width == a && this._texture.height == h && this._texture.type == p || (this._texture = new GL.Texture(a, h, {type:p, format:gl.RGBA, filter:gl.LINEAR})); a = this._color; a[0] = this.properties.R; a[1] = this.properties.G; @@ -8469,7 +7752,7 @@ $jscomp.polyfill("Object.values", function(v) { d.bind(1); k.bind(2); f.bind(3); - e.uniforms(l).draw(h); + e.uniforms(l).draw(g); }); this.setOutputData(0, this._texture); }, n.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_textureR;\n\r\n\t\tuniform sampler2D u_textureG;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tuniform sampler2D u_textureA;\n\r\n\t\tuniform vec4 u_color;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t gl_FragColor = u_color * vec4( \r\n\t\t\t\t\ttexture2D(u_textureR, v_coord).r,\r\n\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\r\n\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\r\n\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\r\n\t\t}\n\r\n\t\t", @@ -8517,8 +7800,8 @@ $jscomp.polyfill("Object.values", function(v) { var c = this.getInputData(1); c || (c = this.properties.B); for (var f = 2; f < this.inputs.length; f++) { - var h = this.inputs[f], e = this.getInputData(f); - void 0 !== e && (this.properties[h.name] = e); + var g = this.inputs[f], e = this.getInputData(f); + void 0 !== e && (this.properties[g.name] = e); } var n = this._uniforms; this._uniforms.u_angle = this.properties.angle * DEG2RAD; @@ -8546,14 +7829,14 @@ $jscomp.polyfill("Object.values", function(v) { this._tex = c.getTargetTexture(this.properties.size_from_biggest && d.width > b.width ? d : b, this._tex, this.properties.precision); gl.disable(gl.BLEND); gl.disable(gl.DEPTH_TEST); - var h = Mesh.getScreenQuad(), e = null, n = this._uniforms; + var g = Mesh.getScreenQuad(), e = null, n = this._uniforms; k ? (e = a._shader_tex, e || (e = a._shader_tex = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, a.pixel_shader, {MIX_TEX:""}))) : (e = a._shader_factor, e || (e = a._shader_factor = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, a.pixel_shader)), f = null == f ? this.properties.factor : f, n.u_mix.set([f, f, f, f])); - var g = this.properties.invert; + var h = this.properties.invert; this._tex.drawTo(function() { - b.bind(g ? 1 : 0); - d.bind(g ? 0 : 1); + b.bind(h ? 1 : 0); + d.bind(h ? 0 : 1); k && k.bind(2); - e.uniforms(n).draw(h); + e.uniforms(n).draw(g); }); this.setOutputData(0, this._tex); } @@ -8572,10 +7855,10 @@ $jscomp.polyfill("Object.values", function(v) { this._tex = c.getTargetTexture(a, this._tex, this.properties.precision); gl.disable(gl.BLEND); gl.disable(gl.DEPTH_TEST); - var d = Mesh.getScreenQuad(), k = b._shader, f = this.properties.invert, h = this.properties.factor, e = this.properties.threshold ? 1 : 0; + var d = Mesh.getScreenQuad(), k = b._shader, f = this.properties.invert, g = this.properties.factor, e = this.properties.threshold ? 1 : 0; this._tex.drawTo(function() { a.bind(0); - k.uniforms({u_texture:0, u_isize:[1 / a.width, 1 / a.height], u_factor:h, u_threshold:e, u_invert:f ? 1 : 0}).draw(d); + k.uniforms({u_texture:0, u_isize:[1 / a.width, 1 / a.height], u_factor:g, u_threshold:e, u_invert:f ? 1 : 0}).draw(d); }); this.setOutputData(0, this._tex); } @@ -8600,37 +7883,36 @@ $jscomp.polyfill("Object.values", function(v) { gl.disable(gl.DEPTH_TEST); var f = Mesh.getScreenQuad(); d._shader || (d._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, d.pixel_shader), d._shader_onlydepth = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, d.pixel_shader, {ONLY_DEPTH:""})); - var h = this.properties.only_depth ? d._shader_onlydepth : d._shader; + var g = this.properties.only_depth ? d._shader_onlydepth : d._shader; b = null; b = a.near_far_planes ? a.near_far_planes : window.LS && LS.Renderer._main_camera ? LS.Renderer._main_camera._uniforms.u_camera_planes : [0.1, 1000]; c.u_camera_planes = b; this._temp_texture.drawTo(function() { a.bind(0); - h.uniforms(c).draw(f); + g.uniforms(c).draw(f); }); this._temp_texture.near_far_planes = b; this.setOutputData(0, this._temp_texture); } } }, d.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec2 u_camera_planes;\n\r\n\t\tuniform float u_distance;\n\r\n\t\tuniform float u_range;\n\r\n\t\t\n\r\n\t\tfloat LinearDepth()\n\r\n\t\t{\n\r\n\t\t\tfloat zNear = u_camera_planes.x;\n\r\n\t\t\tfloat zFar = u_camera_planes.y;\n\r\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\r\n\t\t\tdepth = depth * 2.0 - 1.0;\n\r\n\t\t\treturn zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tfloat depth = LinearDepth();\n\r\n\t\t\t#ifdef ONLY_DEPTH\n\r\n\t\t\t gl_FragColor = vec4(depth);\n\r\n\t\t\t#else\n\r\n\t\t\t\tfloat diff = abs(depth * u_camera_planes.y - u_distance);\n\r\n\t\t\t\tfloat dof = 1.0;\n\r\n\t\t\t\tif(diff <= u_range)\n\r\n\t\t\t\t\tdof = diff / u_range;\n\r\n\t\t\t gl_FragColor = vec4(dof);\n\r\n\t\t\t#endif\n\r\n\t\t}\n\r\n\t\t", - F.registerNodeType("texture/depth_range", d), h.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, h.title = "Linear Depth", h.desc = "Creates a color texture with linear depth", h.prototype.onExecute = function() { + F.registerNodeType("texture/depth_range", d), g.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, g.title = "Linear Depth", g.desc = "Creates a color texture with linear depth", g.prototype.onExecute = function() { if (this.isOutputConnected(0)) { var a = this.getInputData(0); if (a && (a.format == gl.DEPTH_COMPONENT || a.format == gl.DEPTH_STENCIL)) { var b = this.properties.precision == c.HIGH ? gl.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE; this._temp_texture && this._temp_texture.type == b && this._temp_texture.width == a.width && this._temp_texture.height == a.height || (this._temp_texture = new GL.Texture(a.width, a.height, {type:b, format:gl.RGB, filter:gl.LINEAR})); var d = this._uniforms; - d.u_near = a.near_far_planes[0]; - d.u_far = a.near_far_planes[1]; d.u_invert = this.properties.invert ? 1 : 0; gl.disable(gl.BLEND); gl.disable(gl.DEPTH_TEST); var k = Mesh.getScreenQuad(); - h._shader || (h._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, h.pixel_shader)); - var f = h._shader; + g._shader || (g._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, g.pixel_shader)); + var f = g._shader; b = null; b = a.near_far_planes ? a.near_far_planes : window.LS && LS.Renderer._main_camera ? LS.Renderer._main_camera._uniforms.u_camera_planes : [0.1, 1000]; d.u_camera_planes = b; + d.u_ires.set([0, 0]); this._temp_texture.drawTo(function() { a.bind(0); f.uniforms(d).draw(k); @@ -8639,8 +7921,8 @@ $jscomp.polyfill("Object.values", function(v) { this.setOutputData(0, this._temp_texture); } } - }, h.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_near;\n\r\n\t\tuniform float u_far;\n\r\n\t\tuniform int u_invert;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tfloat zNear = u_near;\n\r\n\t\t\tfloat zFar = u_far;\n\r\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\r\n\t\t\tdepth = depth * 2.0 - 1.0;\n\r\n\t\t\tfloat f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\r\n\t\t\tif( u_invert == 1 )\n\r\n\t\t\t\tf = 1.0 - f;\n\r\n\t\t\tgl_FragColor = vec4(vec3(f),1.0);\n\r\n\t\t}\n\r\n\t\t", - F.registerNodeType("texture/linear_depth", h), f.title = "Blur", f.desc = "Blur a texture", f.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, f.max_iterations = 20, f.prototype.onExecute = function() { + }, g.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec2 u_camera_planes;\n\r\n\t\tuniform int u_invert;\n\r\n\t\tuniform vec2 u_ires;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tfloat zNear = u_camera_planes.x;\n\r\n\t\t\tfloat zFar = u_camera_planes.y;\n\r\n\t\t\tfloat depth = texture2D(u_texture, v_coord + u_ires*0.5).x * 2.0 - 1.0;\n\r\n\t\t\tfloat f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\r\n\t\t\tif( u_invert == 1 )\n\r\n\t\t\t\tf = 1.0 - f;\n\r\n\t\t\tgl_FragColor = vec4(vec3(f),1.0);\n\r\n\t\t}\n\r\n\t\t", + F.registerNodeType("texture/linear_depth", g), f.title = "Blur", f.desc = "Blur a texture", f.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, f.max_iterations = 20, f.prototype.onExecute = function() { var a = this.getInputData(0); if (a && this.isOutputConnected(0)) { var b = this._final_texture; @@ -8657,10 +7939,10 @@ $jscomp.polyfill("Object.values", function(v) { k || void 0 === window.gl || (k = gl.canvas.height / gl.canvas.width); k || (k = 1); k = this.properties.preserve_aspect ? k : 1; - var h = this.properties.scale || [1, 1]; - a.applyBlur(k * h[0], h[1], c, b); + var g = this.properties.scale || [1, 1]; + a.applyBlur(k * g[0], g[1], c, b); for (a = 1; a < d; ++a) { - b.applyBlur(k * h[0] * (a + 1), h[1] * (a + 1), c); + b.applyBlur(k * g[0] * (a + 1), g[1] * (a + 1), c); } this.setOutputData(0, b); } @@ -8675,18 +7957,18 @@ $jscomp.polyfill("Object.values", function(v) { if (this.properties.precision === c.PASS_THROUGH || !1 === this.getInputOrProperty("enabled")) { this.setOutputData(0, a); } else { - var b = a.width, d = a.height, k = {format:a.format, type:a.type, minFilter:GL.LINEAR, magFilter:GL.LINEAR, wrap:gl.CLAMP_TO_EDGE}, f = c.getTextureType(this.properties.precision, a), h = this._uniforms, e = this._textures, n = x._cut_shader; + var b = a.width, d = a.height, k = {format:a.format, type:a.type, minFilter:GL.LINEAR, magFilter:GL.LINEAR, wrap:gl.CLAMP_TO_EDGE}, f = c.getTextureType(this.properties.precision, a), g = this._uniforms, e = this._textures, n = x._cut_shader; n || (n = x._cut_shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, x.cut_pixel_shader)); gl.disable(gl.DEPTH_TEST); gl.disable(gl.BLEND); - h.u_threshold = this.getInputOrProperty("threshold"); - var g = e[0] = GL.Texture.getTemporary(b, d, k); - a.blit(g, n.uniforms(h)); - var p = g, l = this.getInputOrProperty("iterations"); + g.u_threshold = this.getInputOrProperty("threshold"); + var h = e[0] = GL.Texture.getTemporary(b, d, k); + a.blit(h, n.uniforms(g)); + var p = h, l = this.getInputOrProperty("iterations"); l = Math.clamp(l, 1, 16) | 0; - var m = h.u_texel_size, r = this.getInputOrProperty("intensity"); - h.u_intensity = 1; - h.u_delta = this.properties.scale; + var m = g.u_texel_size, r = this.getInputOrProperty("intensity"); + g.u_intensity = 1; + g.u_delta = this.properties.scale; n = x._shader; n || (n = x._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, x.scale_pixel_shader)); for (var q = 1; q < l; q++) { @@ -8695,19 +7977,19 @@ $jscomp.polyfill("Object.values", function(v) { if (2 > b) { break; } - g = e[q] = GL.Texture.getTemporary(b, d, k); + h = e[q] = GL.Texture.getTemporary(b, d, k); m[0] = 1 / p.width; m[1] = 1 / p.height; - p.blit(g, n.uniforms(h)); - p = g; + p.blit(h, n.uniforms(g)); + p = h; } - this.isOutputConnected(2) && (b = this._average_texture, b && b.type == a.type && b.format == a.format || (b = this._average_texture = new GL.Texture(1, 1, {type:a.type, format:a.format, filter:gl.LINEAR})), m[0] = 1 / p.width, m[1] = 1 / p.height, h.u_intensity = r, h.u_delta = 1, p.blit(b, n.uniforms(h)), this.setOutputData(2, b)); + this.isOutputConnected(2) && (b = this._average_texture, b && b.type == a.type && b.format == a.format || (b = this._average_texture = new GL.Texture(1, 1, {type:a.type, format:a.format, filter:gl.LINEAR})), m[0] = 1 / p.width, m[1] = 1 / p.height, g.u_intensity = r, g.u_delta = 1, p.blit(b, n.uniforms(g)), this.setOutputData(2, b)); gl.enable(gl.BLEND); gl.blendFunc(gl.ONE, gl.ONE); - h.u_intensity = this.getInputOrProperty("persistence"); - h.u_delta = 0.5; + g.u_intensity = this.getInputOrProperty("persistence"); + g.u_delta = 0.5; for (q -= 2; 0 <= q; q--) { - g = e[q], e[q] = null, m[0] = 1 / p.width, m[1] = 1 / p.height, p.blit(g, n.uniforms(h)), GL.Texture.releaseTemporary(p), p = g; + h = e[q], e[q] = null, m[0] = 1 / p.width, m[1] = 1 / p.height, p.blit(h, n.uniforms(g)), GL.Texture.releaseTemporary(p), p = h; } gl.disable(gl.BLEND); this.isOutputConnected(1) && (e = this._glow_texture, e && e.width == a.width && e.height == a.height && e.type == f && e.format == a.format || (e = this._glow_texture = new GL.Texture(a.width, a.height, {type:f, format:a.format, filter:gl.LINEAR})), p.blit(e), this.setOutputData(1, e)); @@ -8715,14 +7997,14 @@ $jscomp.polyfill("Object.values", function(v) { e = this._final_texture; e && e.width == a.width && e.height == a.height && e.type == f && e.format == a.format || (e = this._final_texture = new GL.Texture(a.width, a.height, {type:f, format:a.format, filter:gl.LINEAR})); var t = this.getInputData(1), u = this.getInputOrProperty("dirt_factor"); - h.u_intensity = r; + g.u_intensity = r; n = t ? x._dirt_final_shader : x._final_shader; n || (n = t ? x._dirt_final_shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, x.final_pixel_shader, {USE_DIRT:""}) : x._final_shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, x.final_pixel_shader)); e.drawTo(function() { a.bind(0); p.bind(1); t && (n.setUniform("u_dirt_factor", u), n.setUniform("u_dirt_texture", t.bind(2))); - n.toViewport(h); + n.toViewport(g); }); this.setOutputData(0, e); } @@ -8731,13 +8013,13 @@ $jscomp.polyfill("Object.values", function(v) { } }, x.cut_pixel_shader = "precision highp float;\n\r\n\tvarying vec2 v_coord;\n\r\n\tuniform sampler2D u_texture;\n\r\n\tuniform float u_threshold;\n\r\n\tvoid main() {\n\r\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\r\n\t}", x.scale_pixel_shader = "precision highp float;\n\r\n\tvarying vec2 v_coord;\n\r\n\tuniform sampler2D u_texture;\n\r\n\tuniform vec2 u_texel_size;\n\r\n\tuniform float u_delta;\n\r\n\tuniform float u_intensity;\n\r\n\t\n\r\n\tvec4 sampleBox(vec2 uv) {\n\r\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\r\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\r\n\t\treturn s * 0.25;\n\r\n\t}\n\r\n\tvoid main() {\n\r\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\r\n\t}", x.final_pixel_shader = "precision highp float;\n\r\n\tvarying vec2 v_coord;\n\r\n\tuniform sampler2D u_texture;\n\r\n\tuniform sampler2D u_glow_texture;\n\r\n\t#ifdef USE_DIRT\n\r\n\t\tuniform sampler2D u_dirt_texture;\n\r\n\t#endif\n\r\n\tuniform vec2 u_texel_size;\n\r\n\tuniform float u_delta;\n\r\n\tuniform float u_intensity;\n\r\n\tuniform float u_dirt_factor;\n\r\n\t\n\r\n\tvec4 sampleBox(vec2 uv) {\n\r\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\r\n\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\r\n\t\treturn s * 0.25;\n\r\n\t}\n\r\n\tvoid main() {\n\r\n\t\tvec4 glow = sampleBox( v_coord );\n\r\n\t\t#ifdef USE_DIRT\n\r\n\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\r\n\t\t#endif\n\r\n\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\r\n\t}", - F.registerNodeType("texture/glow", x), H.title = "Kuwahara Filter", H.desc = "Filters a texture giving an artistic oil canvas painting", H.max_radius = 10, H._shaders = [], H.prototype.onExecute = function() { + F.registerNodeType("texture/glow", x), G.title = "Kuwahara Filter", G.desc = "Filters a texture giving an artistic oil canvas painting", G.max_radius = 10, G._shaders = [], G.prototype.onExecute = function() { var a = this.getInputData(0); if (a && this.isOutputConnected(0)) { var b = this._temp_texture; b && b.width == a.width && b.height == a.height && b.type == a.type || (this._temp_texture = new GL.Texture(a.width, a.height, {type:a.type, format:gl.RGBA, filter:gl.LINEAR})); b = this.properties.radius; - b = Math.min(Math.floor(b), H.max_radius); + b = Math.min(Math.floor(b), G.max_radius); if (0 == b) { this.setOutputData(0, a); } else { @@ -8745,8 +8027,8 @@ $jscomp.polyfill("Object.values", function(v) { c || void 0 === window.gl || (c = gl.canvas.height / gl.canvas.width); c || (c = 1); c = this.properties.preserve_aspect ? c : 1; - H._shaders[b] || (H._shaders[b] = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, H.pixel_shader, {RADIUS:b.toFixed(0)})); - var k = H._shaders[b], f = GL.Mesh.getScreenQuad(); + G._shaders[b] || (G._shaders[b] = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, G.pixel_shader, {RADIUS:b.toFixed(0)})); + var k = G._shaders[b], f = GL.Mesh.getScreenQuad(); a.bind(0); this._temp_texture.drawTo(function() { k.uniforms({u_texture:0, u_intensity:d, u_resolution:[a.width, a.height], u_iResolution:[1 / a.width, 1 / a.height]}).draw(f); @@ -8754,17 +8036,17 @@ $jscomp.polyfill("Object.values", function(v) { this.setOutputData(0, this._temp_texture); } } - }, H.pixel_shader = "\n\r\nprecision highp float;\n\r\nvarying vec2 v_coord;\n\r\nuniform sampler2D u_texture;\n\r\nuniform float u_intensity;\n\r\nuniform vec2 u_resolution;\n\r\nuniform vec2 u_iResolution;\n\r\n#ifndef RADIUS\n\r\n\t#define RADIUS 7\n\r\n#endif\n\r\nvoid main() {\n\r\n\n\r\n\tconst int radius = RADIUS;\n\r\n\tvec2 fragCoord = v_coord;\n\r\n\tvec2 src_size = u_iResolution;\n\r\n\tvec2 uv = v_coord;\n\r\n\tfloat n = float((radius + 1) * (radius + 1));\n\r\n\tint i;\n\r\n\tint j;\n\r\n\tvec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\r\n\tvec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\r\n\tvec3 c;\n\r\n\t\n\r\n\tfor (int j = -radius; j <= 0; ++j) {\n\r\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\r\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\r\n\t\t\tm0 += c;\n\r\n\t\t\ts0 += c * c;\n\r\n\t\t}\n\r\n\t}\n\r\n\t\n\r\n\tfor (int j = -radius; j <= 0; ++j) {\n\r\n\t\tfor (int i = 0; i <= radius; ++i) {\n\r\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\r\n\t\t\tm1 += c;\n\r\n\t\t\ts1 += c * c;\n\r\n\t\t}\n\r\n\t}\n\r\n\t\n\r\n\tfor (int j = 0; j <= radius; ++j) {\n\r\n\t\tfor (int i = 0; i <= radius; ++i) {\n\r\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\r\n\t\t\tm2 += c;\n\r\n\t\t\ts2 += c * c;\n\r\n\t\t}\n\r\n\t}\n\r\n\t\n\r\n\tfor (int j = 0; j <= radius; ++j) {\n\r\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\r\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\r\n\t\t\tm3 += c;\n\r\n\t\t\ts3 += c * c;\n\r\n\t\t}\n\r\n\t}\n\r\n\t\n\r\n\tfloat min_sigma2 = 1e+2;\n\r\n\tm0 /= n;\n\r\n\ts0 = abs(s0 / n - m0 * m0);\n\r\n\t\n\r\n\tfloat sigma2 = s0.r + s0.g + s0.b;\n\r\n\tif (sigma2 < min_sigma2) {\n\r\n\t\tmin_sigma2 = sigma2;\n\r\n\t\tgl_FragColor = vec4(m0, 1.0);\n\r\n\t}\n\r\n\t\n\r\n\tm1 /= n;\n\r\n\ts1 = abs(s1 / n - m1 * m1);\n\r\n\t\n\r\n\tsigma2 = s1.r + s1.g + s1.b;\n\r\n\tif (sigma2 < min_sigma2) {\n\r\n\t\tmin_sigma2 = sigma2;\n\r\n\t\tgl_FragColor = vec4(m1, 1.0);\n\r\n\t}\n\r\n\t\n\r\n\tm2 /= n;\n\r\n\ts2 = abs(s2 / n - m2 * m2);\n\r\n\t\n\r\n\tsigma2 = s2.r + s2.g + s2.b;\n\r\n\tif (sigma2 < min_sigma2) {\n\r\n\t\tmin_sigma2 = sigma2;\n\r\n\t\tgl_FragColor = vec4(m2, 1.0);\n\r\n\t}\n\r\n\t\n\r\n\tm3 /= n;\n\r\n\ts3 = abs(s3 / n - m3 * m3);\n\r\n\t\n\r\n\tsigma2 = s3.r + s3.g + s3.b;\n\r\n\tif (sigma2 < min_sigma2) {\n\r\n\t\tmin_sigma2 = sigma2;\n\r\n\t\tgl_FragColor = vec4(m3, 1.0);\n\r\n\t}\n\r\n}\n\r\n", - F.registerNodeType("texture/kuwahara", H), I.title = "XDoG Filter", I.desc = "Filters a texture giving an artistic ink style", I.max_radius = 10, I._shaders = [], I.prototype.onExecute = function() { + }, G.pixel_shader = "\n\r\nprecision highp float;\n\r\nvarying vec2 v_coord;\n\r\nuniform sampler2D u_texture;\n\r\nuniform float u_intensity;\n\r\nuniform vec2 u_resolution;\n\r\nuniform vec2 u_iResolution;\n\r\n#ifndef RADIUS\n\r\n\t#define RADIUS 7\n\r\n#endif\n\r\nvoid main() {\n\r\n\n\r\n\tconst int radius = RADIUS;\n\r\n\tvec2 fragCoord = v_coord;\n\r\n\tvec2 src_size = u_iResolution;\n\r\n\tvec2 uv = v_coord;\n\r\n\tfloat n = float((radius + 1) * (radius + 1));\n\r\n\tint i;\n\r\n\tint j;\n\r\n\tvec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\r\n\tvec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\r\n\tvec3 c;\n\r\n\t\n\r\n\tfor (int j = -radius; j <= 0; ++j) {\n\r\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\r\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\r\n\t\t\tm0 += c;\n\r\n\t\t\ts0 += c * c;\n\r\n\t\t}\n\r\n\t}\n\r\n\t\n\r\n\tfor (int j = -radius; j <= 0; ++j) {\n\r\n\t\tfor (int i = 0; i <= radius; ++i) {\n\r\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\r\n\t\t\tm1 += c;\n\r\n\t\t\ts1 += c * c;\n\r\n\t\t}\n\r\n\t}\n\r\n\t\n\r\n\tfor (int j = 0; j <= radius; ++j) {\n\r\n\t\tfor (int i = 0; i <= radius; ++i) {\n\r\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\r\n\t\t\tm2 += c;\n\r\n\t\t\ts2 += c * c;\n\r\n\t\t}\n\r\n\t}\n\r\n\t\n\r\n\tfor (int j = 0; j <= radius; ++j) {\n\r\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\r\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\r\n\t\t\tm3 += c;\n\r\n\t\t\ts3 += c * c;\n\r\n\t\t}\n\r\n\t}\n\r\n\t\n\r\n\tfloat min_sigma2 = 1e+2;\n\r\n\tm0 /= n;\n\r\n\ts0 = abs(s0 / n - m0 * m0);\n\r\n\t\n\r\n\tfloat sigma2 = s0.r + s0.g + s0.b;\n\r\n\tif (sigma2 < min_sigma2) {\n\r\n\t\tmin_sigma2 = sigma2;\n\r\n\t\tgl_FragColor = vec4(m0, 1.0);\n\r\n\t}\n\r\n\t\n\r\n\tm1 /= n;\n\r\n\ts1 = abs(s1 / n - m1 * m1);\n\r\n\t\n\r\n\tsigma2 = s1.r + s1.g + s1.b;\n\r\n\tif (sigma2 < min_sigma2) {\n\r\n\t\tmin_sigma2 = sigma2;\n\r\n\t\tgl_FragColor = vec4(m1, 1.0);\n\r\n\t}\n\r\n\t\n\r\n\tm2 /= n;\n\r\n\ts2 = abs(s2 / n - m2 * m2);\n\r\n\t\n\r\n\tsigma2 = s2.r + s2.g + s2.b;\n\r\n\tif (sigma2 < min_sigma2) {\n\r\n\t\tmin_sigma2 = sigma2;\n\r\n\t\tgl_FragColor = vec4(m2, 1.0);\n\r\n\t}\n\r\n\t\n\r\n\tm3 /= n;\n\r\n\ts3 = abs(s3 / n - m3 * m3);\n\r\n\t\n\r\n\tsigma2 = s3.r + s3.g + s3.b;\n\r\n\tif (sigma2 < min_sigma2) {\n\r\n\t\tmin_sigma2 = sigma2;\n\r\n\t\tgl_FragColor = vec4(m3, 1.0);\n\r\n\t}\n\r\n}\n\r\n", + F.registerNodeType("texture/kuwahara", G), I.title = "XDoG Filter", I.desc = "Filters a texture giving an artistic ink style", I.max_radius = 10, I._shaders = [], I.prototype.onExecute = function() { var a = this.getInputData(0); if (a && this.isOutputConnected(0)) { var b = this._temp_texture; b && b.width == a.width && b.height == a.height && b.type == a.type || (this._temp_texture = new GL.Texture(a.width, a.height, {type:a.type, format:gl.RGBA, filter:gl.LINEAR})); I._xdog_shader || (I._xdog_shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, I.xdog_pixel_shader)); - var d = I._xdog_shader, c = GL.Mesh.getScreenQuad(), k = this.properties.sigma, f = this.properties.k, h = this.properties.p, e = this.properties.epsilon, n = this.properties.phi; + var d = I._xdog_shader, c = GL.Mesh.getScreenQuad(), k = this.properties.sigma, f = this.properties.k, g = this.properties.p, e = this.properties.epsilon, n = this.properties.phi; a.bind(0); this._temp_texture.drawTo(function() { - d.uniforms({src:0, sigma:k, k:f, p:h, epsilon:e, phi:n, cvsWidth:a.width, cvsHeight:a.height}).draw(c); + d.uniforms({src:0, sigma:k, k:f, p:g, epsilon:e, phi:n, cvsWidth:a.width, cvsHeight:a.height}).draw(c); }); this.setOutputData(0, this._temp_texture); } @@ -8865,13 +8147,13 @@ $jscomp.polyfill("Object.values", function(v) { } } }, K.pixel_shader = "precision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_factor;\n\r\n\t\tvec2 barrelDistortion(vec2 coord, float amt) {\n\r\n\t\t\tvec2 cc = coord - 0.5;\n\r\n\t\t\tfloat dist = dot(cc, cc);\n\r\n\t\t\treturn coord + cc * dist * amt;\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tfloat sat( float t )\n\r\n\t\t{\n\r\n\t\t\treturn clamp( t, 0.0, 1.0 );\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tfloat linterp( float t ) {\n\r\n\t\t\treturn sat( 1.0 - abs( 2.0*t - 1.0 ) );\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tfloat remap( float t, float a, float b ) {\n\r\n\t\t\treturn sat( (t - a) / (b - a) );\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tvec4 spectrum_offset( float t ) {\n\r\n\t\t\tvec4 ret;\n\r\n\t\t\tfloat lo = step(t,0.5);\n\r\n\t\t\tfloat hi = 1.0-lo;\n\r\n\t\t\tfloat w = linterp( remap( t, 1.0/6.0, 5.0/6.0 ) );\n\r\n\t\t\tret = vec4(lo,1.0,hi, 1.) * vec4(1.0-w, w, 1.0-w, 1.);\n\r\n\t\t\n\r\n\t\t\treturn pow( ret, vec4(1.0/2.2) );\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tconst float max_distort = 2.2;\n\r\n\t\tconst int num_iter = 12;\n\r\n\t\tconst float reci_num_iter_f = 1.0 / float(num_iter);\n\r\n\t\t\n\r\n\t\tvoid main()\n\r\n\t\t{\t\n\r\n\t\t\tvec2 uv=v_coord;\n\r\n\t\t\tvec4 sumcol = vec4(0.0);\n\r\n\t\t\tvec4 sumw = vec4(0.0);\t\n\r\n\t\t\tfor ( int i=0; i e && (e = 12 + e); - return c.notes[e] + (g ? "" : k); + return c.notes[e] + (h ? "" : k); }; c.NoteStringToPitch = function(e) { e = e.toUpperCase(); @@ -9569,8 +8851,8 @@ $jscomp.polyfill("Object.values", function(v) { c.commands = {128:"note off", 144:"note on", 160:"key pressure", 176:"controller change", 192:"program change", 208:"channel pressure", 224:"pitch bend", 240:"system", 242:"Song pos", 243:"Song select", 246:"Tune request", 248:"time tick", 250:"Start Song", 251:"Continue Song", 252:"Stop Song", 254:"Sensing", 255:"Reset"}; c.commands_short = {128:"NOTEOFF", 144:"NOTEOFF", 160:"KEYP", 176:"CC", 192:"PC", 208:"CP", 224:"PB", 240:"SYS", 242:"POS", 243:"SELECT", 246:"TUNEREQ", 248:"TT", 250:"START", 251:"CONTINUE", 252:"STOP", 254:"SENS", 255:"RESET"}; c.commands_reversed = {}; - for (var G in c.commands) { - c.commands_reversed[c.commands[G]] = G; + for (var H in c.commands) { + c.commands_reversed[c.commands[H]] = H; } q.input = null; q.MIDIEvent = c; @@ -9604,7 +8886,7 @@ $jscomp.polyfill("Object.values", function(v) { q.prototype.onMIDIFailure = function(c) { console.error("Failed to get MIDI access - " + c); }; - q.prototype.openInputPort = function(e, g) { + q.prototype.openInputPort = function(e, h) { e = this.input_ports.get("input-" + e); if (!e) { return !1; @@ -9614,7 +8896,7 @@ $jscomp.polyfill("Object.values", function(v) { e.onmidimessage = function(a) { var b = new c(a.data); k.updateState(b); - g && g(a.data, b); + h && h(a.data, b); if (q.on_message) { q.on_message(a.data, b); } @@ -9636,8 +8918,8 @@ $jscomp.polyfill("Object.values", function(v) { this.state.cc[e.getCC()] = e.getCCValue(); } }; - q.prototype.sendMIDI = function(e, g) { - g && (e = this.output_ports_info[e]) && (q.output = this, g.constructor === c ? e.send(g.data) : e.send(g)); + q.prototype.sendMIDI = function(e, h) { + h && (e = this.output_ports_info[e]) && (q.output = this, h.constructor === c ? e.send(h.data) : e.send(h)); }; m.MIDIInterface = q; m.title = "MIDI Input"; @@ -9656,12 +8938,12 @@ $jscomp.polyfill("Object.values", function(v) { m.prototype.onStart = function() { this._midi ? this._midi.openInputPort(this.properties.port, this.onMIDIEvent.bind(this)) : this._waiting = !0; }; - m.prototype.onMIDIEvent = function(e, g) { - this._last_midi_event = g; + m.prototype.onMIDIEvent = function(e, h) { + this._last_midi_event = h; this.boxcolor = "#AFA"; this._last_time = u.getTime(); - this.trigger("on_midi", g); - g.cmd == c.NOTEON ? this.trigger("on_noteon", g) : g.cmd == c.NOTEOFF ? this.trigger("on_noteoff", g) : g.cmd == c.CONTROLLERCHANGE ? this.trigger("on_cc", g) : g.cmd == c.PROGRAMCHANGE ? this.trigger("on_pc", g) : g.cmd == c.PITCHBEND && this.trigger("on_pitchbend", g); + this.trigger("on_midi", h); + h.cmd == c.NOTEON ? this.trigger("on_noteon", h) : h.cmd == c.NOTEOFF ? this.trigger("on_noteoff", h) : h.cmd == c.CONTROLLERCHANGE ? this.trigger("on_cc", h) : h.cmd == c.PROGRAMCHANGE ? this.trigger("on_pc", h) : h.cmd == c.PITCHBEND && this.trigger("on_pitchbend", h); }; m.prototype.onDrawBackground = function(c) { this.boxcolor = "#AAA"; @@ -9699,20 +8981,20 @@ $jscomp.polyfill("Object.values", function(v) { return [["last_midi", "midi"], ["on_midi", u.EVENT], ["on_noteon", u.EVENT], ["on_noteoff", u.EVENT], ["on_cc", u.EVENT], ["on_pc", u.EVENT], ["on_pitchbend", u.EVENT]]; }; u.registerNodeType("midi/input", m); - g.MIDIInterface = q; - g.title = "MIDI Output"; - g.desc = "Sends MIDI to output channel"; - g.color = "#243"; - g.prototype.onGetPropertyInfo = function(c) { + h.MIDIInterface = q; + h.title = "MIDI Output"; + h.desc = "Sends MIDI to output channel"; + h.color = "#243"; + h.prototype.onGetPropertyInfo = function(c) { if (this._midi && "port" == c) { return {type:"enum", values:this.getMIDIOutputs()}; } }; - g.default_ports = {0:"unknown"}; - g.prototype.getMIDIOutputs = function() { + h.default_ports = {0:"unknown"}; + h.prototype.getMIDIOutputs = function() { var c = {}; if (!this._midi) { - return g.default_ports; + return h.default_ports; } if (this._midi.output_ports_info) { for (var e = 0; e < this._midi.output_ports_info.length; ++e) { @@ -9722,24 +9004,24 @@ $jscomp.polyfill("Object.values", function(v) { } return c; }; - g.prototype.onAction = function(c, e) { + h.prototype.onAction = function(c, e) { this._midi && ("send" == c && this._midi.sendMIDI(this.properties.port, e), this.trigger("midi", e)); }; - g.prototype.onGetInputs = function() { + h.prototype.onGetInputs = function() { return [["send", u.ACTION]]; }; - g.prototype.onGetOutputs = function() { + h.prototype.onGetOutputs = function() { return [["on_midi", u.EVENT]]; }; - u.registerNodeType("midi/output", g); + u.registerNodeType("midi/output", h); r.title = "MIDI Show"; r.desc = "Shows MIDI in the graph"; r.color = "#243"; r.prototype.getTitle = function() { return this.flags.collapsed ? this._str : this.title; }; - r.prototype.onAction = function(e, g) { - g && (this._str = g.constructor === c ? g.toString() : "???"); + r.prototype.onAction = function(e, h) { + h && (this._str = h.constructor === c ? h.toString() : "???"); }; r.prototype.onDrawForeground = function(c) { this._str && !this.flags.collapsed && (c.font = "30px Arial", c.fillText(this._str, 10, 0.8 * this.size[1])); @@ -9760,57 +9042,57 @@ $jscomp.polyfill("Object.values", function(v) { -1 != this.properties.min_value && -1 != this.properties.max_value && (e += " " + (this.properties.min_value == this.properties.max_value ? this.properties.max_value : this.properties.min_value + ".." + this.properties.max_value)); return "Filter: " + e; }; - l.prototype.onPropertyChanged = function(e, g) { - "cmd" == e && (e = Number(g), isNaN(e) && (e = c.commands[g] || 0), this.properties.cmd = e); + l.prototype.onPropertyChanged = function(e, h) { + "cmd" == e && (e = Number(h), isNaN(e) && (e = c.commands[h] || 0), this.properties.cmd = e); }; - l.prototype.onAction = function(e, g) { - if (g && g.constructor === c) { + l.prototype.onAction = function(e, h) { + if (h && h.constructor === c) { if (this._learning) { - this._learning = !1, this.boxcolor = "#AAA", this.properties.channel = g.channel, this.properties.cmd = g.cmd, this.properties.min_value = this.properties.max_value = g.data[1]; + this._learning = !1, this.boxcolor = "#AAA", this.properties.channel = h.channel, this.properties.cmd = h.cmd, this.properties.min_value = this.properties.max_value = h.data[1]; } else { - if (-1 != this.properties.channel && g.channel != this.properties.channel || -1 != this.properties.cmd && g.cmd != this.properties.cmd || -1 != this.properties.min_value && g.data[1] < this.properties.min_value || -1 != this.properties.max_value && g.data[1] > this.properties.max_value) { + if (-1 != this.properties.channel && h.channel != this.properties.channel || -1 != this.properties.cmd && h.cmd != this.properties.cmd || -1 != this.properties.min_value && h.data[1] < this.properties.min_value || -1 != this.properties.max_value && h.data[1] > this.properties.max_value) { return; } } - this.trigger("on_midi", g); + this.trigger("on_midi", h); } }; u.registerNodeType("midi/filter", l); B.title = "MIDIEvent"; B.desc = "Create a MIDI Event"; B.color = "#243"; - B.prototype.onAction = function(e, g) { - "assign" == e ? (this.properties.channel = g.channel, this.properties.cmd = g.cmd, this.properties.value1 = g.data[1], this.properties.value2 = g.data[2], g.cmd == c.NOTEON ? this.gate = !0 : g.cmd == c.NOTEOFF && (this.gate = !1)) : (g = this.midi_event, g.channel = this.properties.channel, this.properties.cmd && this.properties.cmd.constructor === String ? g.setCommandFromString(this.properties.cmd) : g.cmd = this.properties.cmd, g.data[0] = g.cmd | g.channel, g.data[1] = Number(this.properties.value1), - g.data[2] = Number(this.properties.value2), this.trigger("on_midi", g)); + B.prototype.onAction = function(e, h) { + "assign" == e ? (this.properties.channel = h.channel, this.properties.cmd = h.cmd, this.properties.value1 = h.data[1], this.properties.value2 = h.data[2], h.cmd == c.NOTEON ? this.gate = !0 : h.cmd == c.NOTEOFF && (this.gate = !1)) : (h = this.midi_event, h.channel = this.properties.channel, this.properties.cmd && this.properties.cmd.constructor === String ? h.setCommandFromString(this.properties.cmd) : h.cmd = this.properties.cmd, h.data[0] = h.cmd | h.channel, h.data[1] = Number(this.properties.value1), + h.data[2] = Number(this.properties.value2), this.trigger("on_midi", h)); }; B.prototype.onExecute = function() { var e = this.properties; if (this.inputs) { - for (var g = 0; g < this.inputs.length; ++g) { - var k = this.inputs[g]; + for (var h = 0; h < this.inputs.length; ++h) { + var k = this.inputs[h]; if (-1 != k.link) { switch(k.name) { case "note": - k = this.getInputData(g); + k = this.getInputData(h); null != k && (k.constructor === String && (k = c.NoteStringToPitch(k)), this.properties.value1 = (k | 0) % 255); break; case "cmd": - k = this.getInputData(g); + k = this.getInputData(h); null != k && (this.properties.cmd = k); break; case "value1": - k = this.getInputData(g); + k = this.getInputData(h); null != k && (this.properties.value1 = Math.clamp(k | 0, 0, 127)); break; case "value2": - k = this.getInputData(g), null != k && (this.properties.value2 = Math.clamp(k | 0, 0, 127)); + k = this.getInputData(h), null != k && (this.properties.value2 = Math.clamp(k | 0, 0, 127)); } } } } if (this.outputs) { - for (g = 0; g < this.outputs.length; ++g) { - switch(this.outputs[g].name) { + for (h = 0; h < this.outputs.length; ++h) { + switch(this.outputs[h].name) { case "midi": k = new c; k.setup([e.cmd, e.value1, e.value2]); @@ -9843,12 +9125,12 @@ $jscomp.polyfill("Object.values", function(v) { default: continue; } - null !== k && this.setOutputData(g, k); + null !== k && this.setOutputData(h, k); } } }; - B.prototype.onPropertyChanged = function(e, g) { - "cmd" == e && (this.properties.cmd = c.computeCommandFromString(g)); + B.prototype.onPropertyChanged = function(e, h) { + "cmd" == e && (this.properties.cmd = c.computeCommandFromString(h)); }; B.prototype.onGetInputs = function() { return [["cmd", "number"], ["note", "number"], ["value1", "number"], ["value2", "number"]]; @@ -9870,9 +9152,9 @@ $jscomp.polyfill("Object.values", function(v) { w.color = "#243"; w.processScale = function(e) { e = e.split(","); - for (var g = 0; g < e.length; ++g) { - var k = e[g]; - e[g] = 2 == k.length && "#" != k[1] || 2 < k.length ? -u.MIDIEvent.NoteStringToPitch(k) : c.note_to_index[k] || 0; + for (var h = 0; h < e.length; ++h) { + var k = e[h]; + e[h] = 2 == k.length && "#" != k[1] || 2 < k.length ? -u.MIDIEvent.NoteStringToPitch(k) : c.note_to_index[k] || 0; } return e; }; @@ -9886,17 +9168,17 @@ $jscomp.polyfill("Object.values", function(v) { this.notes_pitches = w.processScale(c); } }; - w.prototype.onAction = function(e, g) { + w.prototype.onAction = function(e, h) { var k = 0; - g = this.notes_pitches.length; + h = this.notes_pitches.length; e = 0; - "sequence" == this.properties.mode ? e = this.sequence_index = (this.sequence_index + 1) % g : "random" == this.properties.mode && (e = Math.floor(Math.random() * g)); - g = this.notes_pitches[e]; - k = 0 <= g ? g + 12 * (this.properties.octave - 1) + 33 : -g; - g = new c; - g.setup([c.NOTEON, k, 10]); + "sequence" == this.properties.mode ? e = this.sequence_index = (this.sequence_index + 1) % h : "random" == this.properties.mode && (e = Math.floor(Math.random() * h)); + h = this.notes_pitches[e]; + k = 0 <= h ? h + 12 * (this.properties.octave - 1) + 33 : -h; + h = new c; + h.setup([c.NOTEON, k, 10]); e = this.properties.duration || 1; - this.trigger("note", g); + this.trigger("note", h); setTimeout(function() { var a = new c; a.setup([c.NOTEOFF, k, 0]); @@ -9907,21 +9189,21 @@ $jscomp.polyfill("Object.values", function(v) { E.title = "MIDI Transpose"; E.desc = "Transpose a MIDI note"; E.color = "#243"; - E.prototype.onAction = function(e, g) { - g && g.constructor === c && (g.data[0] == c.NOTEON || g.data[0] == c.NOTEOFF ? (this.midi_event = new c, this.midi_event.setup(g.data), this.midi_event.data[1] = Math.round(this.midi_event.data[1] + this.properties.amount), this.trigger("out", this.midi_event)) : this.trigger("out", g)); + E.prototype.onAction = function(e, h) { + h && h.constructor === c && (h.data[0] == c.NOTEON || h.data[0] == c.NOTEOFF ? (this.midi_event = new c, this.midi_event.setup(h.data), this.midi_event.data[1] = Math.round(this.midi_event.data[1] + this.properties.amount), this.trigger("out", this.midi_event)) : this.trigger("out", h)); }; E.prototype.onExecute = function() { var c = this.getInputData(1); null != c && (this.properties.amount = c); }; u.registerNodeType("midi/transpose", E); - z.title = "MIDI Quantize Pitch"; - z.desc = "Transpose a MIDI note tp fit an scale"; - z.color = "#243"; - z.prototype.onPropertyChanged = function(c, e) { + A.title = "MIDI Quantize Pitch"; + A.desc = "Transpose a MIDI note tp fit an scale"; + A.color = "#243"; + A.prototype.onPropertyChanged = function(c, e) { "scale" == c && this.processScale(e); }; - z.prototype.processScale = function(c) { + A.prototype.processScale = function(c) { this._current_scale = c; this.notes_pitches = w.processScale(c); for (c = 0; 12 > c; ++c) { @@ -9944,14 +9226,14 @@ $jscomp.polyfill("Object.values", function(v) { } } }; - z.prototype.onAction = function(e, g) { - g && g.constructor === c && (g.data[0] == c.NOTEON || g.data[0] == c.NOTEOFF ? (this.midi_event = new c, this.midi_event.setup(g.data), this.midi_event.data[1] += this.offset_notes[c.note_to_index[g.note]], this.trigger("out", this.midi_event)) : this.trigger("out", g)); + A.prototype.onAction = function(e, h) { + h && h.constructor === c && (h.data[0] == c.NOTEON || h.data[0] == c.NOTEOFF ? (this.midi_event = new c, this.midi_event.setup(h.data), this.midi_event.data[1] += this.offset_notes[c.note_to_index[h.note]], this.trigger("out", this.midi_event)) : this.trigger("out", h)); }; - z.prototype.onExecute = function() { + A.prototype.onExecute = function() { var c = this.getInputData(1); null != c && c != this._current_scale && this.processScale(c); }; - u.registerNodeType("midi/quantize", z); + u.registerNodeType("midi/quantize", A); e.title = "MIDI fromFile"; e.desc = "Plays a MIDI file"; e.color = "#243"; @@ -9964,8 +9246,8 @@ $jscomp.polyfill("Object.values", function(v) { e.prototype.onExecute = function() { if (this._midi && this._playing) { this._current_time += this.graph.elapsed_time; - for (var e = 100 * this._current_time, g = 0; g < this._midi.tracks; ++g) { - var k = this._midi.track[g]; + for (var e = 100 * this._current_time, h = 0; h < this._midi.tracks; ++h) { + var k = this._midi.track[h]; k._last_pos || (k._last_pos = 0, k._time = 0); var a = k.event[k._last_pos]; if (a && k._time + a.deltaTime <= e && (k._last_pos++, k._time += a.deltaTime, a.data)) { @@ -10004,16 +9286,16 @@ $jscomp.polyfill("Object.values", function(v) { C.title = "MIDI Play"; C.desc = "Plays a MIDI note"; C.color = "#243"; - C.prototype.onAction = function(e, g) { - if (g && g.constructor === c) { - if (this.instrument && g.data[0] == c.NOTEON) { - e = g.note; + C.prototype.onAction = function(e, h) { + if (h && h.constructor === c) { + if (this.instrument && h.data[0] == c.NOTEON) { + e = h.note; if (!e || "undefined" == e || e.constructor !== String) { return; } - this.instrument.play(e, g.octave, this.properties.duration, this.properties.volume); + this.instrument.play(e, h.octave, this.properties.duration, this.properties.volume); } - this.trigger("note", g); + this.trigger("note", h); } }; C.prototype.onExecute = function() { @@ -10035,11 +9317,11 @@ $jscomp.polyfill("Object.values", function(v) { c.globalAlpha = 1; for (var b = 0; 2 > b; b++) { for (var d = 0; d < e; ++d) { - var h = D.keys[d % 12]; - if (h.t == b) { - var f = 7 * Math.floor(d / 12) * k + h.x * k; + var g = D.keys[d % 12]; + if (g.t == b) { + var f = 7 * Math.floor(d / 12) * k + g.x * k; c.fillStyle = 0 == b ? this.keys[d] ? "#CCC" : "white" : this.keys[d] ? "#333" : "black"; - c.fillRect(f + 1, 0, k * h.w - 2, a * h.h); + c.fillRect(f + 1, 0, k * g.w - 2, a * g.h); } } } @@ -10050,9 +9332,9 @@ $jscomp.polyfill("Object.values", function(v) { for (var b = 0; b < this.keys.length; ++b) { var d = D.keys[b % 12]; if (d.t == a) { - var h = 7 * Math.floor(b / 12) * e + d.x * e, f = e * d.w; + var g = 7 * Math.floor(b / 12) * e + d.x * e, f = e * d.w; d = k * d.h; - if (!(c[0] < h || c[0] > h + f || c[1] > d)) { + if (!(c[0] < g || c[0] > g + f || c[1] > d)) { return b; } } @@ -10060,44 +9342,44 @@ $jscomp.polyfill("Object.values", function(v) { } return -1; }; - D.prototype.onAction = function(e, g) { + D.prototype.onAction = function(e, h) { if ("reset" == e) { - for (g = 0; g < this.keys.length; ++g) { - this.keys[g] = !1; + for (h = 0; h < this.keys.length; ++h) { + this.keys[h] = !1; } } else { - g && g.constructor === c && (e = g.data[1] - (12 * (this.properties.start_octave - 1) + 29), 0 <= e && e < this.keys.length && (g.data[0] == c.NOTEON ? this.keys[e] = !0 : g.data[0] == c.NOTEOFF && (this.keys[e] = !1)), this.trigger("note", g)); + h && h.constructor === c && (e = h.data[1] - (12 * (this.properties.start_octave - 1) + 29), 0 <= e && e < this.keys.length && (h.data[0] == c.NOTEON ? this.keys[e] = !0 : h.data[0] == c.NOTEOFF && (this.keys[e] = !1)), this.trigger("note", h)); } }; - D.prototype.onMouseDown = function(e, g) { - if (!(0 > g[1])) { - return e = this.getKeyIndex(g), this.keys[e] = !0, this._last_key = e, e = 12 * (this.properties.start_octave - 1) + 29 + e, g = new c, g.setup([c.NOTEON, e, 100]), this.trigger("note", g), !0; + D.prototype.onMouseDown = function(e, h) { + if (!(0 > h[1])) { + return e = this.getKeyIndex(h), this.keys[e] = !0, this._last_key = e, e = 12 * (this.properties.start_octave - 1) + 29 + e, h = new c, h.setup([c.NOTEON, e, 100]), this.trigger("note", h), !0; } }; - D.prototype.onMouseMove = function(e, g) { - if (!(0 > g[1] || -1 == this._last_key)) { + D.prototype.onMouseMove = function(e, h) { + if (!(0 > h[1] || -1 == this._last_key)) { this.setDirtyCanvas(!0); - e = this.getKeyIndex(g); + e = this.getKeyIndex(h); if (this._last_key == e) { return !0; } this.keys[this._last_key] = !1; - g = 12 * (this.properties.start_octave - 1) + 29 + this._last_key; + h = 12 * (this.properties.start_octave - 1) + 29 + this._last_key; var k = new c; - k.setup([c.NOTEOFF, g, 100]); + k.setup([c.NOTEOFF, h, 100]); this.trigger("note", k); this.keys[e] = !0; - g = 12 * (this.properties.start_octave - 1) + 29 + e; + h = 12 * (this.properties.start_octave - 1) + 29 + e; k = new c; - k.setup([c.NOTEON, g, 100]); + k.setup([c.NOTEON, h, 100]); this.trigger("note", k); this._last_key = e; return !0; } }; - D.prototype.onMouseUp = function(e, g) { - if (!(0 > g[1])) { - return e = this.getKeyIndex(g), this.keys[e] = !1, this._last_key = -1, e = 12 * (this.properties.start_octave - 1) + 29 + e, g = new c, g.setup([c.NOTEOFF, e, 100]), this.trigger("note", g), !0; + D.prototype.onMouseUp = function(e, h) { + if (!(0 > h[1])) { + return e = this.getKeyIndex(h), this.keys[e] = !1, this._last_key = -1, e = 12 * (this.properties.start_octave - 1) + 29 + e, h = new c, h.setup([c.NOTEOFF, e, 100]), this.trigger("note", h), !0; } }; u.registerNodeType("midi/keys", D); @@ -10139,7 +9421,7 @@ $jscomp.polyfill("Object.values", function(v) { this.addOutput("samples", "array"); this._time_bin = this._freq_bin = null; } - function g() { + function h() { this.properties = {gain:1}; this.audionode = p.getAudioContext().createGain(); this.addInput("in", "audio"); @@ -10197,7 +9479,7 @@ $jscomp.polyfill("Object.values", function(v) { this.addInput("time", "number"); this.addOutput("out", "audio"); } - function z() { + function A() { this.properties = {frequency:350, detune:0, Q:1}; this.addProperty("type", "lowpass", "enum", {values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")}); this.audionode = p.getAudioContext().createBiquadFilter(); @@ -10235,7 +9517,7 @@ $jscomp.polyfill("Object.values", function(v) { this.addInput("in", "audio"); this.addOutput("out", "audio"); } - function G() { + function H() { this.audionode = p.getAudioContext().destination; this.addInput("in", "audio"); } @@ -10288,8 +9570,8 @@ $jscomp.polyfill("Object.values", function(v) { } if (c.outputs) { for (b = 0; b < c.outputs.length; ++b) { - for (var k = c.outputs[b], g = 0; g < k.links.length; ++g) { - if (d = c.graph.links[k.links[g]]) { + for (var k = c.outputs[b], h = 0; h < k.links.length; ++h) { + if (d = c.graph.links[k.links[h]]) { e = c.getAudioNodeInOutputSlot ? c.getAudioNodeInOutputSlot(b) : c.audionode; var l = c.graph.getNodeById(d.target_id); d = l.getAudioNodeInInputSlot ? l.getAudioNodeInInputSlot(d.target_slot) : l.audionode; @@ -10569,7 +9851,7 @@ $jscomp.polyfill("Object.values", function(v) { m.title = "Analyser"; m.desc = "Audio Analyser"; n.registerNodeType("audio/analyser", m); - g.prototype.onExecute = function() { + h.prototype.onExecute = function() { if (this.inputs && this.inputs.length) { for (var c = 1; c < this.inputs.length; ++c) { var a = this.inputs[c], b = this.getInputData(c); @@ -10577,10 +9859,10 @@ $jscomp.polyfill("Object.values", function(v) { } } }; - p.createAudioNodeWrapper(g); - g.title = "Gain"; - g.desc = "Audio gain"; - n.registerNodeType("audio/gain", g); + p.createAudioNodeWrapper(h); + h.title = "Gain"; + h.desc = "Audio gain"; + n.registerNodeType("audio/gain", h); p.createAudioNodeWrapper(r); r.prototype.onRemove = function() { this._dropped_url && URL.revokeObjectURL(this._dropped_url); @@ -10661,8 +9943,8 @@ $jscomp.polyfill("Object.values", function(v) { y.desc = "Audio mixer"; n.registerNodeType("audio/mixer", y); w.prototype.onExecute = function() { - var c = p.getAudioContext().currentTime, a = this.audionode.gain, b = this.getInputData(1), d = this.getInputOrProperty("A"), e = this.getInputOrProperty("D"), f = this.getInputOrProperty("S"), g = this.getInputOrProperty("R"); - !this.gate && b ? (a.cancelScheduledValues(0), a.setValueAtTime(0, c), a.linearRampToValueAtTime(1, c + d), a.linearRampToValueAtTime(f, c + d + e)) : this.gate && !b && (a.cancelScheduledValues(0), a.setValueAtTime(a.value, c), a.linearRampToValueAtTime(0, c + g)); + var c = p.getAudioContext().currentTime, a = this.audionode.gain, b = this.getInputData(1), d = this.getInputOrProperty("A"), e = this.getInputOrProperty("D"), f = this.getInputOrProperty("S"), h = this.getInputOrProperty("R"); + !this.gate && b ? (a.cancelScheduledValues(0), a.setValueAtTime(0, c), a.linearRampToValueAtTime(1, c + d), a.linearRampToValueAtTime(f, c + d + e)) : this.gate && !b && (a.cancelScheduledValues(0), a.setValueAtTime(a.value, c), a.linearRampToValueAtTime(0, c + h)); this.gate = b; }; w.prototype.onGetInputs = function() { @@ -10680,7 +9962,7 @@ $jscomp.polyfill("Object.values", function(v) { E.title = "Delay"; E.desc = "Audio delay"; n.registerNodeType("audio/delay", E); - z.prototype.onExecute = function() { + A.prototype.onExecute = function() { if (this.inputs && this.inputs.length) { for (var c = 1; c < this.inputs.length; ++c) { var a = this.inputs[c]; @@ -10691,13 +9973,13 @@ $jscomp.polyfill("Object.values", function(v) { } } }; - z.prototype.onGetInputs = function() { + A.prototype.onGetInputs = function() { return [["frequency", "number"], ["detune", "number"], ["Q", "number"]]; }; - p.createAudioNodeWrapper(z); - z.title = "BiquadFilter"; - z.desc = "Audio filter"; - n.registerNodeType("audio/biquadfilter", z); + p.createAudioNodeWrapper(A); + A.title = "BiquadFilter"; + A.desc = "Audio filter"; + n.registerNodeType("audio/biquadfilter", A); e.prototype.onStart = function() { if (!this.audionode.started) { this.audionode.started = !0; @@ -10827,9 +10109,9 @@ $jscomp.polyfill("Object.values", function(v) { u.title = "Script"; u.desc = "apply script to signal"; n.registerNodeType("audio/script", u); - G.title = "Destination"; - G.desc = "Audio output"; - n.registerNodeType("audio/destination", G); + H.title = "Destination"; + H.desc = "Audio output"; + n.registerNodeType("audio/destination", H); })(this); (function(v) { function c() { @@ -10891,30 +10173,30 @@ $jscomp.polyfill("Object.values", function(v) { console.log("ready"); c.boxcolor = "#6C6"; }; - this._ws.onmessage = function(g) { + this._ws.onmessage = function(h) { c.boxcolor = "#AFA"; - g = JSON.parse(g.data); - if (!g.room || g.room == c.properties.room) { - if (1 == g.type) { - if (g.data.object_class && m[g.data.object_class]) { + h = JSON.parse(h.data); + if (!h.room || h.room == c.properties.room) { + if (1 == h.type) { + if (h.data.object_class && m[h.data.object_class]) { var l = null; try { - l = new m[g.data.object_class](g.data), c.triggerSlot(0, l); + l = new m[h.data.object_class](h.data), c.triggerSlot(0, l); } catch (y) { } } else { - c.triggerSlot(0, g.data); + c.triggerSlot(0, h.data); } } else { - c._last_received_data[g.channel || 0] = g.data; + c._last_received_data[h.channel || 0] = h.data; } } }; - this._ws.onerror = function(g) { + this._ws.onerror = function(h) { console.log("couldnt connect to websocket"); c.boxcolor = "#E88"; }; - this._ws.onclose = function(g) { + this._ws.onclose = function(h) { console.log("connection closed"); c.boxcolor = "#000"; }; @@ -11001,32 +10283,32 @@ $jscomp.polyfill("Object.values", function(v) { if (this._server = new SillyClient, this._server.on_ready = function() { console.log("ready"); c.boxcolor = "#6C6"; - }, this._server.on_message = function(g, l) { - g = null; + }, this._server.on_message = function(h, l) { + h = null; try { - g = JSON.parse(l); + h = JSON.parse(l); } catch (B) { return; } - if (1 == g.type) { - if (g.data.object_class && m[g.data.object_class]) { + if (1 == h.type) { + if (h.data.object_class && m[h.data.object_class]) { l = null; try { - l = new m[g.data.object_class](g.data), c.triggerSlot(0, l); + l = new m[h.data.object_class](h.data), c.triggerSlot(0, l); } catch (B) { return; } } else { - c.triggerSlot(0, g.data); + c.triggerSlot(0, h.data); } } else { - c._last_received_data[g.channel || 0] = g.data; + c._last_received_data[h.channel || 0] = h.data; } c.boxcolor = "#AFA"; - }, this._server.on_error = function(g) { + }, this._server.on_error = function(h) { console.log("couldnt connect to websocket"); c.boxcolor = "#E88"; - }, this._server.on_close = function(g) { + }, this._server.on_close = function(h) { console.log("connection closed"); c.boxcolor = "#000"; }, this.properties.url && this.properties.room) { @@ -11056,4 +10338,3 @@ $jscomp.polyfill("Object.values", function(v) { m.registerNodeType("network/sillyclient", q); })(this); ->>>>>>> custom widget custom size support diff --git a/src/litegraph.d.ts b/src/litegraph.d.ts index 9e534315d..ec4cbaf95 100644 --- a/src/litegraph.d.ts +++ b/src/litegraph.d.ts @@ -1146,7 +1146,7 @@ export declare class LGraphCanvas { /** Called by `LGraphCanvas.processNodeDblClicked` */ onNodeDblClicked: ((node: LGraphNode) => void) | null; /** Called by `LGraphCanvas.selectNodes` */ - onSelectionChange: ((nodes) => void) | null; + onSelectionChange: ((nodes: Record) => void) | null; /** Called by `LGraphCanvas.showSearchBox` */ onSearchBox: | (( diff --git a/src/litegraph.js b/src/litegraph.js index 2960c3bbf..5d1d9dad2 100755 --- a/src/litegraph.js +++ b/src/litegraph.js @@ -5585,8 +5585,8 @@ LGraphNode.prototype.executeAction = function(action) if (widgets[i].computeSize) widgets_height += widgets[i].computeSize(this.resizing_node.size[0])[1] + 4; else - } widgets_height += LiteGraph.NODE_WIDGET_HEIGHT + 4; + } widgets_height += 8; } diff --git a/src/nodes/glfx.js b/src/nodes/glfx.js index 681623881..69b9ec3e5 100755 --- a/src/nodes/glfx.js +++ b/src/nodes/glfx.js @@ -1,5 +1,6 @@ (function(global) { var LiteGraph = global.LiteGraph; + var LGraphTexture = global.LGraphTexture; //Works with Litegl.js to create WebGL nodes if (typeof GL != "undefined") { diff --git a/src/nodes/gltextures.js b/src/nodes/gltextures.js index 1080a2122..6b7dbe201 100755 --- a/src/nodes/gltextures.js +++ b/src/nodes/gltextures.js @@ -1,5 +1,6 @@ (function(global) { var LiteGraph = global.LiteGraph; + var LGraphCanvas = global.LGraphCanvas; //Works with Litegl.js to create WebGL nodes global.LGraphTexture = null; From 4cb1d9c0e5c8dfd02f38bb2afe9b7f2a2a41ff5f Mon Sep 17 00:00:00 2001 From: tamat Date: Fri, 15 May 2020 12:15:33 +0200 Subject: [PATCH 38/63] fixes --- build/litegraph.js | 52 ++++++++++++++++++++++++++++++++--------- demo/index.html | 1 + demo/js/code.js | 2 +- src/litegraph-editor.js | 2 +- src/litegraph.js | 4 ++-- src/nodes/gltextures.js | 27 ++++++++++++++------- src/nodes/math.js | 21 ++++++++++++++++- 7 files changed, 85 insertions(+), 24 deletions(-) diff --git a/build/litegraph.js b/build/litegraph.js index 0fd7b38f4..ffc33fffe 100644 --- a/build/litegraph.js +++ b/build/litegraph.js @@ -6117,7 +6117,7 @@ LGraphNode.prototype.executeAction = function(action) this.adjustMouseEvent(e); var pos = [e.canvasX, e.canvasY]; - var node = this.graph.getNodeOnPos(pos[0], pos[1]); + var node = this.graph ? this.graph.getNodeOnPos(pos[0], pos[1]) : null; if (!node) { var r = null; @@ -8414,7 +8414,7 @@ LGraphNode.prototype.executeAction = function(action) break; default: if (w.draw) { - w.draw(ctx, node, w, y, H); + w.draw(ctx, node, width, y, H); } break; } @@ -13970,6 +13970,10 @@ if (typeof exports != "undefined") { this.addProperty("min", 0); this.addProperty("max", 1); this.addProperty("smooth", true); + this.addProperty("seed", 0); + this.addProperty("octaves", 1); + this.addProperty("persistence", 0.8); + this.addProperty("speed", 1); this.size = [90, 30]; } @@ -14000,7 +14004,22 @@ if (typeof exports != "undefined") { MathNoise.prototype.onExecute = function() { var f = this.getInputData(0) || 0; - var r = MathNoise.getValue(f, this.properties.smooth); + var iterations = this.properties.octaves || 1; + var r = 0; + var amp = 1; + var seed = this.properties.seed || 0; + f += seed; + var speed = this.properties.speed || 1; + var total_amp = 0; + for(var i = 0; i < iterations; ++i) + { + r += MathNoise.getValue(f * (1+i) * speed, this.properties.smooth) * amp; + total_amp += amp; + amp *= this.properties.persistence; + if(amp < 0.001) + break; + } + r /= total_amp; var min = this.properties.min; var max = this.properties.max; this._last_v = r * (max - min) + min; @@ -20890,7 +20909,7 @@ void main(void){\n\ LiteGraph.registerNodeType("texture/lensfx", LGraphLensFX); - + //applies a curve (or generates one) function LGraphTextureCurve() { this.addInput("in", "Texture"); this.addOutput("out", "Texture"); @@ -20914,21 +20933,32 @@ void main(void){\n\ } LGraphTextureCurve.title = "Curve"; + LGraphTextureCurve.desc = "Generates or applies a curve to a texture"; + LGraphTextureCurve.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; LGraphTextureCurve.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - if (!this.isOutputConnected(0)) { return; } //saves work + var tex = this.getInputData(0); + var temp = this._temp_texture; - if ( !temp || temp.width != tex.width || temp.height != tex.height || temp.type != tex.type ) { - temp = this._temp_texture = new GL.Texture( tex.width, tex.height, { type: tex.type, format: gl.RGBA, filter: gl.LINEAR } ); + var type = LGraphTexture.getTextureType(this.properties.precision); + + if(!tex) //generate one texture, nothing else + { + if(this._must_update || !this._curve_texture ) + this.updateCurve(); + this.setOutputData(0, this._curve_texture); + return; } + + //apply curve to input texture + if ( !temp || temp.type != type ) + temp = this._temp_texture = new GL.Texture( 256, 1, { type: type, format: gl.RGBA, filter: gl.LINEAR } ); var shader = LGraphTextureCurve._shader; if (!shader) { diff --git a/demo/index.html b/demo/index.html index 24667f13b..01dce5054 100755 --- a/demo/index.html +++ b/demo/index.html @@ -31,6 +31,7 @@ + diff --git a/demo/js/code.js b/demo/js/code.js index cf7fd00bf..7038567a6 100644 --- a/demo/js/code.js +++ b/demo/js/code.js @@ -1,7 +1,7 @@ var webgl_canvas = null; LiteGraph.node_images_path = "../nodes_data/"; -var editor = new LiteGraph.Editor("main"); +var editor = new LiteGraph.Editor("main",{miniwindow:true}); window.graphcanvas = editor.graphcanvas; window.graph = editor.graph; window.addEventListener("resize", function() { editor.graphcanvas.resize(); } ); diff --git a/src/litegraph-editor.js b/src/litegraph-editor.js index 8f300e11e..25a40f7bf 100755 --- a/src/litegraph-editor.js +++ b/src/litegraph-editor.js @@ -504,7 +504,7 @@ Editor.prototype.addMiniWindow = function(w, h) { var close_button = document.createElement("div"); close_button.className = "corner-button"; - close_button.innerHTML = "X"; + close_button.innerHTML = "❌"; close_button.addEventListener("click", function(e) { graphcanvas.setGraph(null); miniwindow.parentNode.removeChild(miniwindow); diff --git a/src/litegraph.js b/src/litegraph.js index 94a2058b3..8aa5e09e1 100755 --- a/src/litegraph.js +++ b/src/litegraph.js @@ -6115,7 +6115,7 @@ LGraphNode.prototype.executeAction = function(action) this.adjustMouseEvent(e); var pos = [e.canvasX, e.canvasY]; - var node = this.graph.getNodeOnPos(pos[0], pos[1]); + var node = this.graph ? this.graph.getNodeOnPos(pos[0], pos[1]) : null; if (!node) { var r = null; @@ -8412,7 +8412,7 @@ LGraphNode.prototype.executeAction = function(action) break; default: if (w.draw) { - w.draw(ctx, node, w, y, H); + w.draw(ctx, node, width, y, H); } break; } diff --git a/src/nodes/gltextures.js b/src/nodes/gltextures.js index 1080a2122..3771b5f36 100755 --- a/src/nodes/gltextures.js +++ b/src/nodes/gltextures.js @@ -4259,7 +4259,7 @@ void main(void){\n\ LiteGraph.registerNodeType("texture/lensfx", LGraphLensFX); - + //applies a curve (or generates one) function LGraphTextureCurve() { this.addInput("in", "Texture"); this.addOutput("out", "Texture"); @@ -4283,21 +4283,32 @@ void main(void){\n\ } LGraphTextureCurve.title = "Curve"; + LGraphTextureCurve.desc = "Generates or applies a curve to a texture"; + LGraphTextureCurve.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; LGraphTextureCurve.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - if (!this.isOutputConnected(0)) { return; } //saves work + var tex = this.getInputData(0); + var temp = this._temp_texture; - if ( !temp || temp.width != tex.width || temp.height != tex.height || temp.type != tex.type ) { - temp = this._temp_texture = new GL.Texture( tex.width, tex.height, { type: tex.type, format: gl.RGBA, filter: gl.LINEAR } ); + var type = LGraphTexture.getTextureType(this.properties.precision); + + if(!tex) //generate one texture, nothing else + { + if(this._must_update || !this._curve_texture ) + this.updateCurve(); + this.setOutputData(0, this._curve_texture); + return; } + + //apply curve to input texture + if ( !temp || temp.type != type ) + temp = this._temp_texture = new GL.Texture( 256, 1, { type: type, format: gl.RGBA, filter: gl.LINEAR } ); var shader = LGraphTextureCurve._shader; if (!shader) { diff --git a/src/nodes/math.js b/src/nodes/math.js index 2761eafa8..cfc7cd02d 100755 --- a/src/nodes/math.js +++ b/src/nodes/math.js @@ -236,6 +236,10 @@ this.addProperty("min", 0); this.addProperty("max", 1); this.addProperty("smooth", true); + this.addProperty("seed", 0); + this.addProperty("octaves", 1); + this.addProperty("persistence", 0.8); + this.addProperty("speed", 1); this.size = [90, 30]; } @@ -266,7 +270,22 @@ MathNoise.prototype.onExecute = function() { var f = this.getInputData(0) || 0; - var r = MathNoise.getValue(f, this.properties.smooth); + var iterations = this.properties.octaves || 1; + var r = 0; + var amp = 1; + var seed = this.properties.seed || 0; + f += seed; + var speed = this.properties.speed || 1; + var total_amp = 0; + for(var i = 0; i < iterations; ++i) + { + r += MathNoise.getValue(f * (1+i) * speed, this.properties.smooth) * amp; + total_amp += amp; + amp *= this.properties.persistence; + if(amp < 0.001) + break; + } + r /= total_amp; var min = this.properties.min; var max = this.properties.max; this._last_v = r * (max - min) + min; From e6bb688e3f64a0cc039230319bb7f99aa228f2cd Mon Sep 17 00:00:00 2001 From: harshadchavan Date: Mon, 18 May 2020 22:16:42 -0700 Subject: [PATCH 39/63] fixed this pointer in the code Getting continuous errors in a loop, because of this error. It says catch_errors of undefined --- src/litegraph.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/litegraph.js b/src/litegraph.js index 31af8cdb0..efd0890a2 100755 --- a/src/litegraph.js +++ b/src/litegraph.js @@ -783,7 +783,7 @@ window.requestAnimationFrame(on_frame); if(that.onBeforeStep) that.onBeforeStep(); - that.runStep(1, !this.catch_errors); + that.runStep(1, !that.catch_errors); if(that.onAfterStep) that.onAfterStep(); } @@ -794,7 +794,7 @@ //execute if(that.onBeforeStep) that.onBeforeStep(); - that.runStep(1, !this.catch_errors); + that.runStep(1, !that.catch_errors); if(that.onAfterStep) that.onAfterStep(); }, interval); From e6c5474155a83967ade46fe6e588951978707516 Mon Sep 17 00:00:00 2001 From: tamat Date: Sun, 24 May 2020 00:01:42 +0200 Subject: [PATCH 40/63] better disabled display --- build/litegraph.js | 369 ++++++++++++---- build/litegraph.min.js | 955 ++++++++++++++++++++-------------------- demo/js/demos.js | 2 + src/litegraph.js | 130 +++--- src/nodes/base.js | 113 ++++- src/nodes/gltextures.js | 58 ++- src/nodes/math3d.js | 66 ++- src/nodes/strings.js | 2 +- 8 files changed, 1073 insertions(+), 622 deletions(-) diff --git a/build/litegraph.js b/build/litegraph.js index 75b777819..9bd16aab1 100644 --- a/build/litegraph.js +++ b/build/litegraph.js @@ -334,6 +334,7 @@ } if (!node.size) { node.size = node.computeSize(); + //call onresize? } if (!node.pos) { node.pos = LiteGraph.DEFAULT_POSITION.concat(); @@ -2939,6 +2940,18 @@ } }; + /** + * changes node size and triggers callback + * @method setSize + * @param {vec2} size + */ + LGraphNode.prototype.setSize = function(size) + { + this.size = size; + if(this.onResize) + this.onResize(this.size); + } + /** * add a new property to this node * @method addProperty @@ -2994,7 +3007,7 @@ if (this.onOutputAdded) { this.onOutputAdded(o); } - this.size = this.computeSize(); + this.setSize( this.computeSize() ); this.setDirtyCanvas(true, true); return o; }; @@ -3023,7 +3036,7 @@ } } - this.size = this.computeSize(); + this.setSize( this.computeSize() ); this.setDirtyCanvas(true, true); }; @@ -3049,7 +3062,7 @@ } } - this.size = this.computeSize(); + this.setSize( this.computeSize() ); if (this.onOutputRemoved) { this.onOutputRemoved(slot); } @@ -3076,7 +3089,7 @@ this.inputs = []; } this.inputs.push(o); - this.size = this.computeSize(); + this.setSize( this.computeSize() ); if (this.onInputAdded) { this.onInputAdded(o); } @@ -3108,7 +3121,7 @@ } } - this.size = this.computeSize(); + this.setSize( this.computeSize() ); this.setDirtyCanvas(true, true); }; @@ -3130,7 +3143,7 @@ } link.target_slot -= 1; } - this.size = this.computeSize(); + this.setSize( this.computeSize() ); if (this.onInputRemoved) { this.onInputRemoved(slot); } @@ -3158,12 +3171,12 @@ }; /** - * computes the size of a node according to its inputs and output slots + * computes the minimum size of a node according to its inputs and output slots * @method computeSize * @param {number} minHeight * @return {number} the total size */ - LGraphNode.prototype.computeSize = function(minHeight, out) { + LGraphNode.prototype.computeSize = function(out) { if (this.constructor.size) { return this.constructor.size.concat(); } @@ -3230,10 +3243,6 @@ else size[1] += widgets_height; - if (this.onResize) { - this.onResize(size); - } - function compute_text_size(text) { if (!text) { return 0; @@ -3350,7 +3359,7 @@ throw "LiteGraph addWidget('combo',...) requires to pass values in options: { values:['red','blue'] }"; } this.widgets.push(w); - this.size = this.computeSize(); + this.setSize( this.computeSize() ); return w; }; @@ -5567,35 +5576,11 @@ LGraphNode.prototype.executeAction = function(action) if (this.resizing_node && !this.live_mode) { //convert mouse to node space - this.resizing_node.size[0] = e.canvasX - this.resizing_node.pos[0]; - this.resizing_node.size[1] = e.canvasY - this.resizing_node.pos[1]; - - //constraint size - var max_slots = Math.max( - this.resizing_node.inputs ? this.resizing_node.inputs.length : 0, - this.resizing_node.outputs ? this.resizing_node.outputs.length : 0 - ); - - if (this.resizing_node.size[0] < LiteGraph.NODE_MIN_WIDTH) { - this.resizing_node.size[0] = LiteGraph.NODE_MIN_WIDTH; - } - - var widgets = this.resizing_node.widgets; - var widgets_height = 0; - if (widgets && widgets.length) { - for (var i = 0, l = widgets.length; i < l; ++i) { - if (widgets[i].computeSize) - widgets_height += widgets[i].computeSize(this.resizing_node.size[0])[1] + 4; - else - widgets_height += LiteGraph.NODE_WIDGET_HEIGHT + 4; - } - widgets_height += 8; - } - - var min_height = max_slots * LiteGraph.NODE_SLOT_HEIGHT + widgets_height; - if (this.resizing_node.size[1] < min_height) { - this.resizing_node.size[1] = min_height; - } + var desired_size = [ e.canvasX - this.resizing_node.pos[0], e.canvasY - this.resizing_node.pos[1] ]; + var min_size = this.resizing_node.computeSize(); + desired_size[0] = Math.max( min_size[0], desired_size[0] ); + desired_size[1] = Math.max( min_size[1], desired_size[1] ); + this.resizing_node.setSize( desired_size ); this.canvas.style.cursor = "se-resize"; this.dirty_canvas = true; @@ -8279,7 +8264,7 @@ LGraphNode.prototype.executeAction = function(action) this.dirty_canvas = true; } ctx.fillRect(margin, y, width - margin * 2, H); - if(show_text) + if(show_text && !w.disabled) ctx.strokeRect( margin, y, width - margin * 2, H ); if (show_text) { ctx.textAlign = "center"; @@ -8297,7 +8282,7 @@ LGraphNode.prototype.executeAction = function(action) else ctx.rect(margin, posY, width - margin * 2, H ); ctx.fill(); - if(show_text) + if(show_text && !w.disabled) ctx.stroke(); ctx.fillStyle = w.value ? "#89A" : "#333"; ctx.beginPath(); @@ -8326,7 +8311,7 @@ LGraphNode.prototype.executeAction = function(action) var nvalue = (w.value - w.options.min) / range; ctx.fillStyle = active_widget == w ? "#89A" : "#678"; ctx.fillRect(margin, y, nvalue * (width - margin * 2), H); - if(show_text) + if(show_text && !w.disabled) ctx.strokeRect(margin, y, width - margin * 2, H); if (w.marker) { var marker_nvalue = (w.marker - w.options.min) / range; @@ -8355,18 +8340,22 @@ LGraphNode.prototype.executeAction = function(action) ctx.rect(margin, posY, width - margin * 2, H ); ctx.fill(); if (show_text) { - ctx.stroke(); + if(!w.disabled) + ctx.stroke(); ctx.fillStyle = text_color; - ctx.beginPath(); - ctx.moveTo(margin + 16, posY + 5); - ctx.lineTo(margin + 6, posY + H * 0.5); - ctx.lineTo(margin + 16, posY + H - 5); - ctx.fill(); - ctx.beginPath(); - ctx.moveTo(width - margin - 16, posY + 5); - ctx.lineTo(width - margin - 6, posY + H * 0.5); - ctx.lineTo(width - margin - 16, posY + H - 5); - ctx.fill(); + if(!w.disabled) + { + ctx.beginPath(); + ctx.moveTo(margin + 16, posY + 5); + ctx.lineTo(margin + 6, posY + H * 0.5); + ctx.lineTo(margin + 16, posY + H - 5); + ctx.fill(); + ctx.beginPath(); + ctx.moveTo(width - margin - 16, posY + 5); + ctx.lineTo(width - margin - 6, posY + H * 0.5); + ctx.lineTo(width - margin - 16, posY + H - 5); + ctx.fill(); + } ctx.fillStyle = secondary_text_color; ctx.fillText(w.name, margin * 2 + 5, y + H * 0.7); ctx.fillStyle = text_color; @@ -9109,6 +9098,8 @@ LGraphNode.prototype.executeAction = function(action) return; } node.size = node.computeSize(); + if (node.onResize) + node.onResize(node.size); node.setDirtyCanvas(true, true); }; @@ -9894,6 +9885,26 @@ LGraphNode.prototype.executeAction = function(action) node.setDirtyCanvas(true, true); }; + LGraphCanvas.onMenuNodeToSubgraph = function(value, options, e, menu, node) { + var graph = node.graph; + var graphcanvas = LGraphCanvas.active_canvas; + if(!graphcanvas) //?? + return; + + var nodes_list = Object.values( graphcanvas.selected_nodes || {} ); + if( !nodes_list.length ) + nodes_list = [ node ]; + + var subgraph_node = LiteGraph.createNode("graph/subgraph"); + subgraph_node.pos = node.pos.concat(); + graph.add(subgraph_node); + + subgraph_node.buildFromNodes( nodes_list ); + + graphcanvas.deselectAllNodes(); + node.setDirtyCanvas(true, true); + }; + LGraphCanvas.onMenuNodeClone = function(value, options, e, menu, node) { if (node.clonable == false) { return; @@ -10040,6 +10051,13 @@ LGraphNode.prototype.executeAction = function(action) callback: LGraphCanvas.onMenuNodeClone }); } + + if(0) //TODO + options.push({ + content: "To Subgraph", + callback: LGraphCanvas.onMenuNodeToSubgraph + }); + if (node.removable !== false) { options.push(null, { content: "Remove", @@ -11088,18 +11106,15 @@ if (typeof exports != "undefined") { this.subgraph.onTrigger = this.onSubgraphTrigger.bind(this); + //nodes input node added inside this.subgraph.onInputAdded = this.onSubgraphNewInput.bind(this); this.subgraph.onInputRenamed = this.onSubgraphRenamedInput.bind(this); - this.subgraph.onInputTypeChanged = this.onSubgraphTypeChangeInput.bind( - this - ); + this.subgraph.onInputTypeChanged = this.onSubgraphTypeChangeInput.bind(this); this.subgraph.onInputRemoved = this.onSubgraphRemovedInput.bind(this); this.subgraph.onOutputAdded = this.onSubgraphNewOutput.bind(this); this.subgraph.onOutputRenamed = this.onSubgraphRenamedOutput.bind(this); - this.subgraph.onOutputTypeChanged = this.onSubgraphTypeChangeOutput.bind( - this - ); + this.subgraph.onOutputTypeChanged = this.onSubgraphTypeChangeOutput.bind(this); this.subgraph.onOutputRemoved = this.onSubgraphRemovedOutput.bind(this); } @@ -11296,6 +11311,95 @@ if (typeof exports != "undefined") { return node; }; + Subgraph.prototype.buildFromNodes = function(nodes) + { + //clear all? + //TODO + + //nodes that connect data between parent graph and subgraph + var subgraph_inputs = []; + var subgraph_outputs = []; + + //mark inner nodes + var ids = {}; + var min_x = 0; + var max_x = 0; + for(var i = 0; i < nodes.length; ++i) + { + var node = nodes[i]; + ids[ node.id ] = node; + min_x = Math.min( node.pos[0], min_x ); + max_x = Math.max( node.pos[0], min_x ); + } + + var last_input_y = 0; + var last_output_y = 0; + + for(var i = 0; i < nodes.length; ++i) + { + var node = nodes[i]; + //check inputs + if( node.inputs ) + for(var j = 0; j < node.inputs.length; ++j) + { + var input = node.inputs[j]; + if( !input || !input.link ) + continue; + var link = node.graph.links[ input.link ]; + if(!link) + continue; + if( ids[ link.origin_id ] ) + continue; + //this.addInput(input.name,link.type); + this.subgraph.addInput(input.name,link.type); + /* + var input_node = LiteGraph.createNode("graph/input"); + this.subgraph.add( input_node ); + input_node.pos = [min_x - 200, last_input_y ]; + last_input_y += 100; + */ + } + + //check outputs + if( node.outputs ) + for(var j = 0; j < node.outputs.length; ++j) + { + var output = node.outputs[j]; + if( !output || !output.links || !output.links.length ) + continue; + var is_external = false; + for(var k = 0; k < output.links.length; ++k) + { + var link = node.graph.links[ output.links[k] ]; + if(!link) + continue; + if( ids[ link.target_id ] ) + continue; + is_external = true; + break; + } + if(!is_external) + continue; + //this.addOutput(output.name,output.type); + /* + var output_node = LiteGraph.createNode("graph/output"); + this.subgraph.add( output_node ); + output_node.pos = [max_x + 50, last_output_y ]; + last_output_y += 100; + */ + } + } + + //detect inputs and outputs + //split every connection in two data_connection nodes + //keep track of internal connections + //connect external connections + + //clone nodes inside subgraph and try to reconnect them + + //connect edge subgraph nodes to extarnal connections nodes + } + LiteGraph.Subgraph = Subgraph; LiteGraph.registerNodeType("graph/subgraph", Subgraph); @@ -11766,10 +11870,11 @@ if (typeof exports != "undefined") { function ConstantArray() { this.addInput("", ""); this.addOutput("", "array"); + this.addOutput("length", "number"); this.addProperty("value", ""); this.widget = this.addWidget("text","array","","value"); this.widgets_up = true; - this.size = [140, 30]; + this.size = [140, 50]; this._value = null; } @@ -11783,7 +11888,10 @@ if (typeof exports != "undefined") { } try { - this._value = JSON.parse(value); + if(value[0] != "[") + this._value = JSON.parse("[" + value + "]"); + else + this._value = JSON.parse(value); this.boxcolor = "#AEA"; } catch (err) { this.boxcolor = "red"; @@ -11792,7 +11900,7 @@ if (typeof exports != "undefined") { ConstantArray.prototype.onExecute = function() { var v = this.getInputData(0); - if(v && v.length) + if(v && v.length) //clone { if(!this._value) this._value = new Array(); @@ -11801,6 +11909,7 @@ if (typeof exports != "undefined") { this._value[i] = v[i]; } this.setOutputData(0, this._value); + this.setOutputData(1, this._value ? ( this._value.length || 0) : 0 ); }; ConstantArray.prototype.setValue = ConstantNumber.prototype.setValue; @@ -11829,6 +11938,8 @@ if (typeof exports != "undefined") { LiteGraph.registerNodeType("basic/array[]", ArrayElement); + + function TableElement() { this.addInput("table", "table"); this.addInput("row", "number"); @@ -11982,7 +12093,7 @@ if (typeof exports != "undefined") { LiteGraph.wrapFunctionAsNode( "basic/length", length, - ["*"], + [""], "number" ); @@ -15106,12 +15217,22 @@ if (typeof exports != "undefined") { function Math3DOperation() { this.addInput("A", "number,vec3"); this.addInput("B", "number,vec3"); - this.addOutput("=", "vec3"); + this.addOutput("=", "number,vec3"); this.addProperty("OP", "+", "enum", { values: Math3DOperation.values }); this._result = vec3.create(); } - Math3DOperation.values = ["+", "-", "*", "/", "%", "^", "max", "min"]; + Math3DOperation.values = ["+", "-", "*", "/", "%", "^", "max", "min","dot","cross"]; + + LiteGraph.registerSearchboxExtra("math3d/operation", "CROSS()", { + properties: {"OP":"cross"}, + title: "CROSS()" + }); + + LiteGraph.registerSearchboxExtra("math3d/operation", "DOT()", { + properties: {"OP":"dot"}, + title: "DOT()" + }); Math3DOperation.title = "Operation"; Math3DOperation.desc = "Easy math 3D operators"; @@ -15173,6 +15294,11 @@ if (typeof exports != "undefined") { result[0] = Math.min(A[0],B[0]); result[1] = Math.min(A[1],B[1]); result[2] = Math.min(A[2],B[2]); + case "dot": + result = vec3.dot(A,B); + break; + case "cross": + vec3.cross(result,A,B); break; default: console.warn("Unknown operation: " + this.properties.OP); @@ -15390,6 +15516,53 @@ if (typeof exports != "undefined") { LiteGraph.registerNodeType("math3d/rotation", Math3DRotation); + + function MathEulerToQuat() { + this.addInput("euler", "vec3"); + this.addOutput("quat", "quat"); + this.properties = { euler:[0,0,0], use_yaw_pitch_roll: false }; + this._degs = vec3.create(); + this._value = quat.create(); + } + + MathEulerToQuat.title = "Euler->Quat"; + MathEulerToQuat.desc = "Converts euler angles (in degrees) to quaternion"; + + MathEulerToQuat.prototype.onExecute = function() { + var euler = this.getInputData(0); + if (euler == null) { + euler = this.properties.euler; + } + vec3.scale( this._degs, euler, DEG2RAD ); + if(this.properties.use_yaw_pitch_roll) + this._degs = [this._degs[2],this._degs[0],this._degs[1]]; + var R = quat.fromEuler(this._value, this._degs); + this.setOutputData(0, R); + }; + + LiteGraph.registerNodeType("math3d/euler_to_quat", MathEulerToQuat); + + function MathQuatToEuler() { + this.addInput(["quat", "quat"]); + this.addOutput("euler", "vec3"); + this._value = vec3.create(); + } + + MathQuatToEuler.title = "Euler->Quat"; + MathQuatToEuler.desc = "Converts rotX,rotY,rotZ in degrees to quat"; + + MathQuatToEuler.prototype.onExecute = function() { + var q = this.getInputData(0); + if(!q) + return; + var R = quat.toEuler(this._value, q); + vec3.scale( this._value, this._value, DEG2RAD ); + this.setOutputData(0, this._value); + }; + + LiteGraph.registerNodeType("math3d/quat_to_euler", MathQuatToEuler); + + //Math3D rotate vec3 function Math3DRotateVec3() { this.addInputs([["vec3", "vec3"], ["quat", "quat"]]); @@ -15556,7 +15729,7 @@ if (typeof exports != "undefined") { return String(a); } - LiteGraph.wrapFunctionAsNode("string/toString", compare, ["*"], "String"); + LiteGraph.wrapFunctionAsNode("string/toString", compare, [""], "String"); function compare(a, b) { return a == b; @@ -20931,6 +21104,54 @@ void main(void){\n\ LiteGraph.registerNodeType("texture/lensfx", LGraphLensFX); + + function LGraphTextureFromData() { + this.addInput("in", ""); + this.properties = { precision: LGraphTexture.LOW, width: 0, height: 0, channels: 1 }; + this.addOutput("out", "Texture"); + } + + LGraphTextureFromData.title = "Data->Tex"; + LGraphTextureFromData.desc = "Generates or applies a curve to a texture"; + LGraphTextureFromData.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureFromData.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { + return; + } //saves work + + var data = this.getInputData(0); + if(!data) + return; + + var channels = this.properties.channels; + var w = this.properties.width; + var h = this.properties.height; + if(!w || !h) + { + w = Math.floor(data.length / channels); + h = 1; + } + var format = gl.RGBA; + if( channels == 3 ) + format = gl.RGB; + else if( channels == 1 ) + format = gl.LUMINANCE; + + var temp = this._temp_texture; + var type = LGraphTexture.getTextureType( this.properties.precision ); + if ( !temp || temp.width != w || temp.height != h || temp.type != type ) { + temp = this._temp_texture = new GL.Texture( w, h, { type: type, format: format, filter: gl.LINEAR } ); + } + + temp.uploadData( data ); + this.setOutputData(0, temp); + } + + LiteGraph.registerNodeType("texture/fromdata", LGraphTextureFromData); + //applies a curve (or generates one) function LGraphTextureCurve() { this.addInput("in", "Texture"); @@ -20968,8 +21189,6 @@ void main(void){\n\ var tex = this.getInputData(0); var temp = this._temp_texture; - var type = LGraphTexture.getTextureType(this.properties.precision); - if(!tex) //generate one texture, nothing else { if(this._must_update || !this._curve_texture ) @@ -20977,10 +21196,12 @@ void main(void){\n\ this.setOutputData(0, this._curve_texture); return; } + + var type = LGraphTexture.getTextureType( this.properties.precision, tex ); //apply curve to input texture - if ( !temp || temp.type != type ) - temp = this._temp_texture = new GL.Texture( 256, 1, { type: type, format: gl.RGBA, filter: gl.LINEAR } ); + if ( !temp || temp.type != type || temp.width != tex.width || temp.height != tex.height || temp.format != tex.format) + temp = this._temp_texture = new GL.Texture( tex.width, tex.height, { type: type, format: tex.format, filter: gl.LINEAR } ); var shader = LGraphTextureCurve._shader; if (!shader) { @@ -21002,7 +21223,7 @@ void main(void){\n\ }); this.setOutputData(0, temp); - }; + } LGraphTextureCurve.prototype.sampleCurve = function(f,points) { diff --git a/build/litegraph.min.js b/build/litegraph.min.js index fe807e9eb..9ea2d0110 100755 --- a/build/litegraph.min.js +++ b/build/litegraph.min.js @@ -1,38 +1,38 @@ -(function(B){function c(a){e.debug&&console.log("Graph created");this.list_of_graphcanvas=null;this.clear();a&&this.configure(a)}function m(a,b,d,g,f,r){this.id=a;this.type=b;this.origin_id=d;this.origin_slot=g;this.target_id=f;this.target_slot=r;this._data=null;this._pos=new Float32Array(2)}function n(a){this._ctor(a)}function h(a){this._ctor(a)}function v(a,b){this.offset=new Float32Array([0,0]);this.scale=1;this.max_scale=10;this.min_scale=0.1;this.onredraw=null;this.enabled=!0;this.last_mouse= +(function(z){function c(a){e.debug&&console.log("Graph created");this.list_of_graphcanvas=null;this.clear();a&&this.configure(a)}function m(a,b,d,g,f,r){this.id=a;this.type=b;this.origin_id=d;this.origin_slot=g;this.target_id=f;this.target_slot=r;this._data=null;this._pos=new Float32Array(2)}function n(a){this._ctor(a)}function h(a){this._ctor(a)}function x(a,b){this.offset=new Float32Array([0,0]);this.scale=1;this.max_scale=10;this.min_scale=0.1;this.onredraw=null;this.enabled=!0;this.last_mouse= [0,0];this.element=null;this.visible_area=new Float32Array(4);a&&(this.element=a,b||this.bindEvents(a))}function k(a,b,d){d=d||{};this.background_image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; -a&&a.constructor===String&&(a=document.querySelector(a));this.ds=new v;this.zoom_modify_alpha=!0;this.title_text_font=""+e.NODE_TEXT_SIZE+"px Arial";this.inner_text_font="normal "+e.NODE_SUBTEXT_SIZE+"px Arial";this.node_title_color=e.NODE_TITLE_COLOR;this.default_link_color=e.LINK_COLOR;this.default_connection_color={input_off:"#778",input_on:"#7F7",output_off:"#778",output_on:"#7F7"};this.highquality_render=!0;this.use_gradients=!1;this.editor_alpha=1;this.pause_rendering=!1;this.clear_background= +a&&a.constructor===String&&(a=document.querySelector(a));this.ds=new x;this.zoom_modify_alpha=!0;this.title_text_font=""+e.NODE_TEXT_SIZE+"px Arial";this.inner_text_font="normal "+e.NODE_SUBTEXT_SIZE+"px Arial";this.node_title_color=e.NODE_TITLE_COLOR;this.default_link_color=e.LINK_COLOR;this.default_connection_color={input_off:"#778",input_on:"#7F7",output_off:"#778",output_on:"#7F7"};this.highquality_render=!0;this.use_gradients=!1;this.editor_alpha=1;this.pause_rendering=!1;this.clear_background= !0;this.read_only=!1;this.render_only_selected=!0;this.live_mode=!1;this.allow_searchbox=this.allow_interaction=this.allow_dragnodes=this.allow_dragcanvas=this.show_info=!0;this.drag_mode=this.allow_reconnect_links=!1;this.filter=this.dragging_rectangle=null;this.always_render_background=!1;this.render_canvas_border=this.render_shadows=!0;this.render_connections_shadows=!1;this.render_connections_border=!0;this.render_connection_arrows=this.render_curved_connections=!1;this.render_collapsed_slots= !0;this.render_execution_order=!1;this.render_link_tooltip=this.render_title_colored=!0;this.links_render_mode=e.SPLINE_LINK;this.canvas_mouse=[0,0];this.onSelectionChange=this.onNodeMoved=this.onDrawLinkTooltip=this.onDrawOverlay=this.onDrawForeground=this.onDrawBackground=this.onMouse=this.onSearchBoxSelection=this.onSearchBox=null;this.connections_width=3;this.round_radius=8;this.over_link_center=this.node_widget=this.current_node=null;this.last_mouse_position=[0,0];this.visible_area=this.ds.visible_area; -this.visible_links=[];b&&b.attachCanvas(this);this.setCanvas(a);this.clear();d.skip_render||this.startRendering();this.autoresize=d.autoresize}function C(a,b){return Math.sqrt((b[0]-a[0])*(b[0]-a[0])+(b[1]-a[1])*(b[1]-a[1]))}function x(a,b,d,g,f,r){return da&&gb?!0:!1}function w(a,b){var d=a[0]+a[2],g=a[1]+a[3],f=b[1]+b[3];return a[0]>b[0]+b[2]||a[1]>f||da&&gb?!0:!1}function t(a,b){var d=a[0]+a[2],g=a[1]+a[3],f=b[1]+b[3];return a[0]>b[0]+b[2]||a[1]>f||dc.width-l.width-10&&(f=c.width-l.width-10);c.height&&e>c.height-l.height-10&&(e=c.height-l.height-10)}r.style.left=f+"px";r.style.top=e+"px";b.scale&&(r.style.transform="scale("+b.scale+")")}function A(a){this.points=a;this.nearest=this.selected=-1;this.size=null;this.must_update=!0;this.margin=5}var e=B.LiteGraph={VERSION:0.4,CANVAS_GRID_SIZE:10,NODE_TITLE_HEIGHT:30, +l=r.getBoundingClientRect();0==c.height&&console.error("document.body height is 0. That is dangerous, set html,body { height: 100%; }");c.width&&f>c.width-l.width-10&&(f=c.width-l.width-10);c.height&&e>c.height-l.height-10&&(e=c.height-l.height-10)}r.style.left=f+"px";r.style.top=e+"px";b.scale&&(r.style.transform="scale("+b.scale+")")}function A(a){this.points=a;this.nearest=this.selected=-1;this.size=null;this.must_update=!0;this.margin=5}var e=z.LiteGraph={VERSION:0.4,CANVAS_GRID_SIZE:10,NODE_TITLE_HEIGHT:30, NODE_TITLE_TEXT_Y:20,NODE_SLOT_HEIGHT:20,NODE_WIDGET_HEIGHT:20,NODE_WIDTH:140,NODE_MIN_WIDTH:50,NODE_COLLAPSED_RADIUS:10,NODE_COLLAPSED_WIDTH:80,NODE_TITLE_COLOR:"#999",NODE_TEXT_SIZE:14,NODE_TEXT_COLOR:"#AAA",NODE_SUBTEXT_SIZE:12,NODE_DEFAULT_COLOR:"#333",NODE_DEFAULT_BGCOLOR:"#353535",NODE_DEFAULT_BOXCOLOR:"#666",NODE_DEFAULT_SHAPE:"box",DEFAULT_SHADOW_COLOR:"rgba(0,0,0,0.5)",DEFAULT_GROUP_FONT:24,WIDGET_BGCOLOR:"#222",WIDGET_OUTLINE_COLOR:"#666",WIDGET_TEXT_COLOR:"#DDD",WIDGET_SECONDARY_TEXT_COLOR:"#999", LINK_COLOR:"#9A9",EVENT_LINK_COLOR:"#A86",CONNECTING_LINK_COLOR:"#AFA",MAX_NUMBER_OF_NODES:1E3,DEFAULT_POSITION:[100,100],VALID_SHAPES:["default","box","round","card"],BOX_SHAPE:1,ROUND_SHAPE:2,CIRCLE_SHAPE:3,CARD_SHAPE:4,ARROW_SHAPE:5,INPUT:1,OUTPUT:2,EVENT:-1,ACTION:-1,ALWAYS:0,ON_EVENT:1,NEVER:2,ON_TRIGGER:3,UP:1,DOWN:2,LEFT:3,RIGHT:4,CENTER:5,STRAIGHT_LINK:0,LINEAR_LINK:1,SPLINE_LINK:2,NORMAL_TITLE:0,NO_TITLE:1,TRANSPARENT_TITLE:2,AUTOHIDE_TITLE:3,proxy:null,node_images_path:"",debug:!1,catch_exceptions:!0, throw_errors:!0,allow_scripts:!1,registered_node_types:{},node_types_by_file_extension:{},Nodes:{},searchbox_extras:{},registerNodeType:function(a,b){if(!b.prototype)throw"Cannot register a simple object, it must be a class with a prototype";b.type=a;e.debug&&console.log("Node registered: "+a);a.split("/");var d=b.name,g=a.lastIndexOf("/");b.category=a.substr(0,g);b.title||(b.title=d);if(b.prototype)for(var f in n.prototype)b.prototype[f]||(b.prototype[f]=n.prototype[f]);if(g=this.registered_node_types[a])console.log("replacing node type: "+ a);else if(Object.hasOwnProperty(b.prototype,"shape")||Object.defineProperty(b.prototype,"shape",{set:function(a){switch(a){case "default":delete this._shape;break;case "box":this._shape=e.BOX_SHAPE;break;case "round":this._shape=e.ROUND_SHAPE;break;case "circle":this._shape=e.CIRCLE_SHAPE;break;case "card":this._shape=e.CARD_SHAPE;break;default:this._shape=a}},get:function(a){return this._shape},enumerable:!0,configurable:!0}),b.prototype.onPropertyChange&&console.warn("LiteGraph node class "+a+ " has onPropertyChange method, it must be called onPropertyChanged with d at the end"),b.supported_extensions)for(f in b.supported_extensions){var r=b.supported_extensions[f];r&&r.constructor===String&&(this.node_types_by_file_extension[r.toLowerCase()]=b)}this.registered_node_types[a]=b;b.constructor.name&&(this.Nodes[d]=b);if(e.onNodeTypeRegistered)e.onNodeTypeRegistered(a,b);if(g&&e.onNodeTypeReplaced)e.onNodeTypeReplaced(a,b,g)},unregisterNodeType:function(a){var b=a.constructor===String?this.registered_node_types[a]: -a;if(!b)throw"node type not found: "+a;delete this.registered_node_types[b.type];b.constructor.name&&delete this.Nodes[b.constructor.name]},wrapFunctionAsNode:function(a,b,d,g,f){for(var r=Array(b.length),t="",c=e.getParameterNames(b),l=0;lt&&(t=f.size[0]),c+=f.size[1]+a+e.NODE_TITLE_HEIGHT;b+=t+a}this.setDirtyCanvas(!0,!0)};c.prototype.getTime=function(){return this.globaltime};c.prototype.getFixedTime=function(){return this.fixedtime};c.prototype.getElapsedTime= -function(){return this.elapsed_time};c.prototype.sendEventToAllNodes=function(a,b,d){d=d||e.ALWAYS;var g=this._nodes_in_order?this._nodes_in_order:this._nodes;if(g)for(var f=0,r=g.length;fs&&(s=f.size[0]),c+=f.size[1]+a+e.NODE_TITLE_HEIGHT;b+=s+a}this.setDirtyCanvas(!0,!0)};c.prototype.getTime=function(){return this.globaltime};c.prototype.getFixedTime=function(){return this.fixedtime};c.prototype.getElapsedTime= +function(){return this.elapsed_time};c.prototype.sendEventToAllNodes=function(a,b,d){d=d||e.ALWAYS;var g=this._nodes_in_order?this._nodes_in_order:this._nodes;if(g)for(var f=0,r=g.length;f=e.MAX_NUMBER_OF_NODES)throw"LiteGraph: max number of nodes in a graph reached";null==a.id||-1==a.id?a.id=++this.last_node_id:this.last_node_id< a.id&&(this.last_node_id=a.id);a.graph=this;this._version++;this._nodes.push(a);this._nodes_by_id[a.id]=a;if(a.onAdded)a.onAdded(this);this.config.align_to_grid&&a.alignToGrid();b||this.updateExecutionOrder();if(this.onNodeAdded)this.onNodeAdded(a);this.setDirtyCanvas(!0);this.change();return a}};c.prototype.remove=function(a){if(a.constructor===e.LGraphGroup){var b=this._groups.indexOf(a);-1!=b&&this._groups.splice(b,1);a.graph=null;this._version++;this.setDirtyCanvas(!0,!0);this.change()}else if(null!= this._nodes_by_id[a.id]&&!a.ignore_remove){if(a.inputs)for(b=0;ba.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});this.id=-1;this.type=null;this.inputs=[];this.outputs=[];this.connections=[];this.properties={};this.properties_info=[];this.flags={}};n.prototype.configure=function(a){this.graph&&this.graph._version++;for(var b in a)if("properties"==b)for(var d in a.properties){if(this.properties[d]=a.properties[d],this.onPropertyChanged)this.onPropertyChanged(d, a.properties[d])}else null!=a[b]&&("object"==typeof a[b]?this[b]&&this[b].configure?this[b].configure(a[b]):this[b]=e.cloneObject(a[b],this[b]):this[b]=a[b]);a.title||(this.title=this.constructor.title);if(this.onConnectionsChange){if(this.inputs)for(d=0;d=this.outputs.length?null:this.outputs[a]._data};n.prototype.getOutputInfo=function(a){return this.outputs?a=this.outputs.length)return null;a=this.outputs[a];if(!a.links||0==a.links.length)return null;for(var b=[],d=0;da&&this.pos[1]-f-db)return!0;return!1};n.prototype.getSlotInPosition=function(a,b){var d=new Float32Array(2); -if(this.inputs)for(var g=0,f=this.inputs.length;g=this.outputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),null;b&&b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"target node is null";if(b==this)return null;if(d.constructor===String){if(d=b.findInputSlot(d),-1==d)return e.debug&&console.log("Connect: Error, no slot of name "+d),null}else{if(d===e.EVENT)return null;if(!b.inputs||d>=b.inputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),null}null!=b.inputs[d].link&& -b.disconnectInput(d);var g=this.outputs[a];if(b.onConnectInput&&!1===b.onConnectInput(d,g.type,g,this,a))return null;var f=b.inputs[d],r=null;if(e.isValidConnection(g.type,f.type)){r=new m(++this.graph.last_link_id,f.type,this.id,a,b.id,d);this.graph.links[r.id]=r;null==g.links&&(g.links=[]);g.links.push(r.id);b.inputs[d].link=r.id;this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(e.OUTPUT,a,!0,r,g);if(b.onConnectionsChange)b.onConnectionsChange(e.INPUT,d,!0,r, -f);this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(e.INPUT,b,d,this,a),this.graph.onNodeConnectionChange(e.OUTPUT,this,a,b,d))}this.setDirtyCanvas(!1,!0);this.graph.connectionChange(this,r);return r};n.prototype.disconnectOutput=function(a,b){if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return e.debug&&console.log("Connect: Error, no slot of name "+a),!1}else if(!this.outputs||a>=this.outputs.length)return e.debug&&console.log("Connect: Error, slot number not found"), -!1;var d=this.outputs[a];if(!d||!d.links||0==d.links.length)return!1;if(b){b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"Target Node not found";for(var g=0,f=d.links.length;g=this.inputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),!1;var b=this.inputs[a];if(!b)return!1;var d=this.inputs[a].link;this.inputs[a].link=null;var g=this.graph.links[d];if(g){var f=this.graph.getNodeById(g.origin_id);if(!f)return!1;var r=f.outputs[g.origin_slot];if(!r||!r.links||0==r.links.length)return!1;for(var t=0,c=r.links.length;t< -c;t++)if(r.links[t]==d){r.links.splice(t,1);break}delete this.graph.links[d];this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(e.INPUT,a,!1,g,b);if(f.onConnectionsChange)f.onConnectionsChange(e.OUTPUT,t,!1,g,r);this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(e.OUTPUT,f,t),this.graph.onNodeConnectionChange(e.INPUT,this,a))}this.setDirtyCanvas(!1,!0);this.graph.connectionChange(this);return!0};n.prototype.getConnectionPos=function(a, -b,d){d=d||new Float32Array(2);var g=0;a&&this.inputs&&(g=this.inputs.length);!a&&this.outputs&&(g=this.outputs.length);var f=0.5*e.NODE_SLOT_HEIGHT;if(this.flags.collapsed)return b=this._collapsed_width||e.NODE_COLLAPSED_WIDTH,this.horizontal?(d[0]=this.pos[0]+0.5*b,d[1]=a?this.pos[1]-e.NODE_TITLE_HEIGHT:this.pos[1]):(d[0]=a?this.pos[0]:this.pos[0]+b,d[1]=this.pos[1]-0.5*e.NODE_TITLE_HEIGHT),d;if(a&&-1==b)return d[0]=this.pos[0]+0.5*e.NODE_TITLE_HEIGHT,d[1]=this.pos[1]+0.5*e.NODE_TITLE_HEIGHT,d;if(a&& -g>b&&this.inputs[b].pos)return d[0]=this.pos[0]+this.inputs[b].pos[0],d[1]=this.pos[1]+this.inputs[b].pos[1],d;if(!a&&g>b&&this.outputs[b].pos)return d[0]=this.pos[0]+this.outputs[b].pos[0],d[1]=this.pos[1]+this.outputs[b].pos[1],d;if(this.horizontal)return d[0]=this.pos[0]+this.size[0]/g*(b+0.5),d[1]=a?this.pos[1]-e.NODE_TITLE_HEIGHT:this.pos[1]+this.size[1],d;d[0]=a?this.pos[0]+f:this.pos[0]+this.size[0]+1-f;d[1]=this.pos[1]+(b+0.7)*e.NODE_SLOT_HEIGHT+(this.constructor.slot_start_y||0);return d}; -n.prototype.alignToGrid=function(){this.pos[0]=e.CANVAS_GRID_SIZE*Math.round(this.pos[0]/e.CANVAS_GRID_SIZE);this.pos[1]=e.CANVAS_GRID_SIZE*Math.round(this.pos[1]/e.CANVAS_GRID_SIZE)};n.prototype.trace=function(a){this.console||(this.console=[]);this.console.push(a);this.console.length>n.MAX_CONSOLE&&this.console.shift();this.graph.onNodeTrace(this,a)};n.prototype.setDirtyCanvas=function(a,b){this.graph&&this.graph.sendActionToCanvas("setDirty",[a,b])};n.prototype.loadImage=function(a){var b=new Image; -b.src=e.node_images_path+a;b.ready=!1;var d=this;b.onload=function(){this.ready=!0;d.setDirtyCanvas(!0)};return b};n.prototype.captureInput=function(a){if(this.graph&&this.graph.list_of_graphcanvas)for(var b=this.graph.list_of_graphcanvas,d=0;da.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});Object.defineProperty(this,"size",{set:function(a){!a||2>a.length||(this._size[0]=Math.max(140,a[0]),this._size[1]=Math.max(80,a[1]))},get:function(){return this._size},enumerable:!0})};h.prototype.configure=function(a){this.title=a.title;this._bounding.set(a.bounding);this.color=a.color; -this.font=a.font};h.prototype.serialize=function(){var a=this._bounding;return{title:this.title,bounding:[Math.round(a[0]),Math.round(a[1]),Math.round(a[2]),Math.round(a[3])],color:this.color,font:this.font}};h.prototype.move=function(a,b,d){this._pos[0]+=a;this._pos[1]+=b;if(!d)for(d=0;dthis.max_scale&&(a=this.max_scale);if(a!=this.scale&&this.element){var d=this.element.getBoundingClientRect();if(d){b=b||[0.5*d.width,0.5*d.height];d=this.convertCanvasToOffset(b);this.scale=a;0.01>Math.abs(this.scale-1)&&(this.scale=1);var g=this.convertCanvasToOffset(b),d=[g[0]-d[0],g[1]-d[1]];this.offset[0]+=d[0];this.offset[1]+=d[1];if(this.onredraw)this.onredraw(this)}}}; -v.prototype.changeDeltaScale=function(a,b){this.changeScale(this.scale*a,b)};v.prototype.reset=function(){this.scale=1;this.offset[0]=0;this.offset[1]=0};B.LGraphCanvas=e.LGraphCanvas=k;k.link_type_colors={"-1":e.EVENT_LINK_COLOR,number:"#AAA",node:"#DCA"};k.gradients={};k.prototype.clear=function(){this.fps=this.render_time=this.last_draw_time=this.frame=0;this.dragging_rectangle=null;this.selected_nodes={};this.selected_group=null;this.visible_nodes=[];this.connecting_node=this.node_capturing_input= -this.node_over=this.node_dragged=null;this.highlighted_links={};this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_widget=this.node_in_panel=this.dirty_area=null;this.last_mouse=[0,0];this.last_mouseclick=0;this.visible_area.set([0,0,0,0]);if(this.onClear)this.onClear()};k.prototype.setGraph=function(a,b){this.graph!=a&&(b||this.clear(),!a&&this.graph?this.graph.detachCanvas(this):(a.attachCanvas(this),this._graph_stack&&(this._graph_stack=null),this.setDirty(!0,!0)))};k.prototype.openSubgraph=function(a){if(!a)throw"graph cannot be null"; -if(this.graph==a)throw"graph cannot be the same";this.clear();this.graph&&(this._graph_stack||(this._graph_stack=[]),this._graph_stack.push(this.graph));a.attachCanvas(this);this.setDirty(!0,!0)};k.prototype.closeSubgraph=function(){if(this._graph_stack&&0!=this._graph_stack.length){var a=this.graph._subgraph_node,b=this._graph_stack.pop();this.selected_nodes={};this.highlighted_links={};b.attachCanvas(this);this.setDirty(!0,!0);a&&(this.centerOnNode(a),this.selectNodes([a]))}};k.prototype.getCurrentGraph= -function(){return this.graph};k.prototype.setCanvas=function(a,b){if(a&&a.constructor===String&&(a=document.getElementById(a),!a))throw"Error creating LiteGraph canvas: Canvas not found";if(a!==this.canvas&&(!a&&this.canvas&&(b||this.unbindEvents()),this.canvas=a,this.ds.element=a)){a.className+=" lgraphcanvas";a.data=this;a.tabindex="1";this.bgcanvas=null;this.bgcanvas||(this.bgcanvas=document.createElement("canvas"),this.bgcanvas.width=this.canvas.width,this.bgcanvas.height=this.canvas.height); -if(null==a.getContext){if("canvas"!=a.localName)throw"Element supplied for LGraphCanvas must be a element, you passed a "+a.localName;throw"This browser doesn't support Canvas";}null==(this.ctx=a.getContext("2d"))&&(a.webgl_enabled||console.warn("This canvas seems to be WebGL, enabling WebGL renderer"),this.enableWebGL());this._mousemove_callback=this.processMouseMove.bind(this);this._mouseup_callback=this.processMouseUp.bind(this);b||this.bindEvents()}};k.prototype._doNothing=function(a){a.preventDefault(); -return!1};k.prototype._doReturnTrue=function(a){a.preventDefault();return!0};k.prototype.bindEvents=function(){if(this._events_binded)console.warn("LGraphCanvas: events already binded");else{var a=this.canvas,b=this.getCanvasWindow().document;this._mousedown_callback=this.processMouseDown.bind(this);this._mousewheel_callback=this.processMouseWheel.bind(this);a.addEventListener("mousedown",this._mousedown_callback,!0);a.addEventListener("mousemove",this._mousemove_callback);a.addEventListener("mousewheel", -this._mousewheel_callback,!1);a.addEventListener("contextmenu",this._doNothing);a.addEventListener("DOMMouseScroll",this._mousewheel_callback,!1);a.addEventListener("touchstart",this.touchHandler,!0);a.addEventListener("touchmove",this.touchHandler,!0);a.addEventListener("touchend",this.touchHandler,!0);a.addEventListener("touchcancel",this.touchHandler,!0);this._key_callback=this.processKey.bind(this);a.addEventListener("keydown",this._key_callback,!0);b.addEventListener("keyup",this._key_callback, -!0);this._ondrop_callback=this.processDrop.bind(this);a.addEventListener("dragover",this._doNothing,!1);a.addEventListener("dragend",this._doNothing,!1);a.addEventListener("drop",this._ondrop_callback,!1);a.addEventListener("dragenter",this._doReturnTrue,!1);this._events_binded=!0}};k.prototype.unbindEvents=function(){if(this._events_binded){var a=this.getCanvasWindow().document;this.canvas.removeEventListener("mousedown",this._mousedown_callback);this.canvas.removeEventListener("mousewheel",this._mousewheel_callback); -this.canvas.removeEventListener("DOMMouseScroll",this._mousewheel_callback);this.canvas.removeEventListener("keydown",this._key_callback);a.removeEventListener("keyup",this._key_callback);this.canvas.removeEventListener("contextmenu",this._doNothing);this.canvas.removeEventListener("drop",this._ondrop_callback);this.canvas.removeEventListener("dragenter",this._doReturnTrue);this.canvas.removeEventListener("touchstart",this.touchHandler);this.canvas.removeEventListener("touchmove",this.touchHandler); -this.canvas.removeEventListener("touchend",this.touchHandler);this.canvas.removeEventListener("touchcancel",this.touchHandler);this._ondrop_callback=this._key_callback=this._mousewheel_callback=this._mousedown_callback=null;this._events_binded=!1}else console.warn("LGraphCanvas: no events binded")};k.getFileExtension=function(a){var b=a.indexOf("?");-1!=b&&(a=a.substr(0,b));b=a.lastIndexOf(".");return-1==b?"":a.substr(b+1).toLowerCase()};k.prototype.enableWebGL=function(){if(void 0===typeof GL)throw"litegl.js must be included to use a WebGL canvas"; -if(void 0===typeof enableWebGLCanvas)throw"webglCanvas.js must be included to use this feature";this.gl=this.ctx=enableWebGLCanvas(this.canvas);this.ctx.webgl=!0;this.bgcanvas=this.canvas;this.bgctx=this.gl;this.canvas.webgl_enabled=!0};k.prototype.setDirty=function(a,b){a&&(this.dirty_canvas=!0);b&&(this.dirty_bgcanvas=!0)};k.prototype.getCanvasWindow=function(){if(!this.canvas)return window;var a=this.canvas.ownerDocument;return a.defaultView||a.parentWindow};k.prototype.startRendering=function(){function a(){this.pause_rendering|| -this.draw();var b=this.getCanvasWindow();this.is_rendering&&b.requestAnimationFrame(a.bind(this))}this.is_rendering||(this.is_rendering=!0,a.call(this))};k.prototype.stopRendering=function(){this.is_rendering=!1};k.prototype.processMouseDown=function(a){if(this.graph){this.adjustMouseEvent(a);var b=this.getCanvasWindow();k.active_canvas=this;this.canvas.removeEventListener("mousemove",this._mousemove_callback);b.document.addEventListener("mousemove",this._mousemove_callback,!0);b.document.addEventListener("mouseup", -this._mouseup_callback,!0);var d=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes,5),g=!1,f=300>e.getTime()-this.last_mouseclick;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;this.canvas.focus();e.closeAllContextMenus(b);if(!this.onMouse||!0!=this.onMouse(a)){if(1==a.which){a.ctrlKey&&(this.dragging_rectangle=new Float32Array(4),this.dragging_rectangle[0]=a.canvasX,this.dragging_rectangle[1]=a.canvasY,this.dragging_rectangle[2]=1,this.dragging_rectangle[3]=1,g=!0);var r= -!1;if(d&&this.allow_interaction&&!g&&!this.read_only){this.live_mode||d.flags.pinned||this.bringToFront(d);if(!this.connecting_node&&!d.flags.collapsed&&!this.live_mode)if(!g&&!1!==d.resizable&&x(a.canvasX,a.canvasY,d.pos[0]+d.size[0]-5,d.pos[1]+d.size[1]-5,10,10))this.resizing_node=d,this.canvas.style.cursor="se-resize",g=!0;else{if(d.outputs)for(var t=0,c=d.outputs.length;tr[0]+4||a.canvasYr[1]+4)){this.showLinkMenu(d, -a);this.over_link_center=null;break}this.selected_group=this.graph.getGroupOnPos(a.canvasX,a.canvasY);this.selected_group_resizing=!1;this.selected_group&&!this.read_only&&(a.ctrlKey&&(this.dragging_rectangle=null),10>C([a.canvasX,a.canvasY],[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])*this.ds.scale?this.selected_group_resizing=!0:this.selected_group.recomputeInsideNodes());f&&!this.read_only&&this.allow_searchbox&&this.showSearchBox(a); -r=!0}!g&&r&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}else 2!=a.which&&3==a.which&&(this.read_only||this.processContextMenu(d,a));this.last_mouse[0]=a.localX;this.last_mouse[1]=a.localY;this.last_mouseclick=e.getTime();this.last_mouse_dragging=!0;this.graph.change();(!b.document.activeElement||"input"!=b.document.activeElement.nodeName.toLowerCase()&&"textarea"!=b.document.activeElement.nodeName.toLowerCase())&&a.preventDefault();a.stopPropagation();if(this.onMouseDown)this.onMouseDown(a); -return!1}}};k.prototype.processMouseMove=function(a){this.autoresize&&this.resize();if(this.graph){k.active_canvas=this;this.adjustMouseEvent(a);var b=[a.localX,a.localY],d=[b[0]-this.last_mouse[0],b[1]-this.last_mouse[1]];this.last_mouse=b;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;a.dragging=this.last_mouse_dragging;this.node_widget&&(this.processNodeWidgets(this.node_widget[0],this.canvas_mouse,a,this.node_widget[1]),this.dirty_canvas=!0);if(this.dragging_rectangle)this.dragging_rectangle[2]= -a.canvasX-this.dragging_rectangle[0],this.dragging_rectangle[3]=a.canvasY-this.dragging_rectangle[1],this.dirty_canvas=!0;else if(this.selected_group&&!this.read_only)this.selected_group_resizing?this.selected_group.size=[a.canvasX-this.selected_group.pos[0],a.canvasY-this.selected_group.pos[1]]:(this.selected_group.move(d[0]/this.ds.scale,d[1]/this.ds.scale,a.ctrlKey),this.selected_group._nodes.length&&(this.dirty_canvas=!0)),this.dirty_bgcanvas=!0;else if(this.dragging_canvas)this.ds.offset[0]+= -d[0]/this.ds.scale,this.ds.offset[1]+=d[1]/this.ds.scale,this.dirty_bgcanvas=this.dirty_canvas=!0;else if(this.allow_interaction&&!this.read_only){this.connecting_node&&(this.dirty_canvas=!0);for(var g=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes),b=0,f=this.graph._nodes.length;bt[0]+4||a.canvasYt[1]+4)){f=r;break}}f!=this.over_link_center&&(this.over_link_center=f,this.dirty_canvas=!0);this.canvas&&(this.canvas.style.cursor="")}if(this.node_capturing_input&&this.node_capturing_input!=g&&this.node_capturing_input.onMouseMove)this.node_capturing_input.onMouseMove(a, -[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]],this);if(this.node_dragged&&!this.live_mode){for(b in this.selected_nodes)g=this.selected_nodes[b],g.pos[0]+=d[0]/this.ds.scale,g.pos[1]+=d[1]/this.ds.scale;this.dirty_bgcanvas=this.dirty_canvas=!0}if(this.resizing_node&&!this.live_mode){this.resizing_node.size[0]=a.canvasX-this.resizing_node.pos[0];this.resizing_node.size[1]=a.canvasY-this.resizing_node.pos[1];d=Math.max(this.resizing_node.inputs?this.resizing_node.inputs.length: -0,this.resizing_node.outputs?this.resizing_node.outputs.length:0);this.resizing_node.size[0]this.dragging_rectangle[3]?this.dragging_rectangle[1]-f:this.dragging_rectangle[1];this.dragging_rectangle[0]=0>this.dragging_rectangle[2]?this.dragging_rectangle[0]-g:this.dragging_rectangle[0];this.dragging_rectangle[1]=r;this.dragging_rectangle[2]=g;this.dragging_rectangle[3]=f;f=[];for(r=0;ra.click_time&&x(a.canvasX,a.canvasY,g.pos[0],g.pos[1]-e.NODE_TITLE_HEIGHT, -e.NODE_TITLE_HEIGHT,e.NODE_TITLE_HEIGHT)&&g.collapse();this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_dragged.pos[0]=Math.round(this.node_dragged.pos[0]);this.node_dragged.pos[1]=Math.round(this.node_dragged.pos[1]);this.graph.config.align_to_grid&&this.node_dragged.alignToGrid();if(this.onNodeMoved)this.onNodeMoved(this.node_dragged);this.node_dragged=null}else{g=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes);!g&&300>a.click_time&&this.deselectAllNodes();this.dirty_canvas=!0; -this.dragging_canvas=!1;if(this.node_over&&this.node_over.onMouseUp)this.node_over.onMouseUp(a,[a.canvasX-this.node_over.pos[0],a.canvasY-this.node_over.pos[1]],this);if(this.node_capturing_input&&this.node_capturing_input.onMouseUp)this.node_capturing_input.onMouseUp(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]])}}else 2==a.which?(this.dirty_canvas=!0,this.dragging_canvas=!1):3==a.which&&(this.dirty_canvas=!0,this.dragging_canvas=!1);this.graph.change(); -a.stopPropagation();a.preventDefault();return!1}};k.prototype.processMouseWheel=function(a){if(this.graph&&this.allow_dragcanvas){var b=null!=a.wheelDeltaY?a.wheelDeltaY:-60*a.detail;this.adjustMouseEvent(a);var d=this.ds.scale;0b&&(d*=1/1.1);this.ds.changeScale(d,[a.localX,a.localY]);this.graph.change();a.preventDefault();return!1}};k.prototype.isOverNodeBox=function(a,b,d){var g=e.NODE_TITLE_HEIGHT;return x(b,d,a.pos[0]+2,a.pos[1]+2-g,g-4,g-4)?!0:!1};k.prototype.isOverNodeInput=function(a, -b,d,g){if(a.inputs)for(var f=0,e=a.inputs.length;fd-this.graph._last_trigger_time)&&this.drawBackCanvas();(this.dirty_canvas||a)&&this.drawFrontCanvas();this.fps=this.render_time?1/this.render_time:0;this.frame+=1}}; -k.prototype.drawFrontCanvas=function(){this.dirty_canvas=!1;this.ctx||(this.ctx=this.bgcanvas.getContext("2d"));var a=this.ctx;if(a){a.start2D&&a.start2D();var b=this.canvas;a.restore();a.setTransform(1,0,0,1,0,0);this.dirty_area&&(a.save(),a.beginPath(),a.rect(this.dirty_area[0],this.dirty_area[1],this.dirty_area[2],this.dirty_area[3]),a.clip());this.clear_background&&a.clearRect(0,0,b.width,b.height);this.bgcanvas==this.canvas?this.drawBackCanvas():a.drawImage(this.bgcanvas,0,0);if(this.onRender)this.onRender(b, -a);this.show_info&&this.renderInfo(a);if(this.graph){a.save();this.ds.toCanvasContext(a);for(var b=this.computeVisibleNodes(null,this.visible_nodes),d=0;d> ";b.fillText(g+d.getTitle(),0.5*a.width,40);b.restore()}d=!1;this.onRenderBackground&&(d=this.onRenderBackground(a,b));b.restore();b.setTransform(1,0,0,1,0,0);this.visible_links.length=0;if(this.graph){b.save();this.ds.toCanvasContext(b);if(this.background_image&&0.5this.ds.scale;if(this.live_mode){if(!a.flags.collapsed&&(b.shadowColor="transparent",a.onDrawForeground))a.onDrawForeground(b,this,this.canvas)}else{var r=this.editor_alpha;b.globalAlpha=r;this.render_shadows&&!f?(b.shadowColor=e.DEFAULT_SHADOW_COLOR,b.shadowOffsetX=2*this.ds.scale,b.shadowOffsetY=2*this.ds.scale,b.shadowBlur=3*this.ds.scale):b.shadowColor="transparent";if(!a.flags.collapsed||!a.onDrawCollapsed|| -!0!=a.onDrawCollapsed(b,this)){var t=a._shape||e.BOX_SHAPE;y.set(a.size);var c=a.horizontal;if(a.flags.collapsed){b.font=this.inner_text_font;var l=a.getTitle?a.getTitle():a.title;null!=l&&(a._collapsed_width=Math.min(a.size[0],b.measureText(l).width+2*e.NODE_TITLE_HEIGHT),y[0]=a._collapsed_width,y[1]=0)}a.clip_area&&(b.save(),b.beginPath(),t==e.BOX_SHAPE?b.rect(0,0,y[0],y[1]):t==e.ROUND_SHAPE?b.roundRect(0,0,y[0],y[1],10):t==e.CIRCLE_SHAPE&&b.arc(0.5*y[0],0.5*y[1],0.5*y[0],0,2*Math.PI),b.clip()); -a.has_errors&&(g="red");this.drawNodeShape(a,b,y,d,g,a.is_selected,a.mouseOver);b.shadowColor="transparent";if(a.onDrawForeground)a.onDrawForeground(b,this,this.canvas);b.textAlign=c?"center":"left";b.font=this.inner_text_font;g=!f;t=this.connecting_output;b.lineWidth=1;var l=0,q=new Float32Array(2);if(!a.flags.collapsed){if(a.inputs)for(d=0;dthis.ds.scale,l=a._shape||a.constructor.shape||e.ROUND_SHAPE,p=a.constructor.title_mode,q=!0;p==e.TRANSPARENT_TITLE?q=!1:p==e.AUTOHIDE_TITLE&&t&&(q=!0);u[0]=0;u[1]=q?-f:0;u[2]=d[0]+1;u[3]=q?d[1]+f:d[1];t=b.globalAlpha;b.beginPath();l==e.BOX_SHAPE||c?b.fillRect(u[0],u[1],u[2],u[3]):l==e.ROUND_SHAPE||l==e.CARD_SHAPE?b.roundRect(u[0],u[1],u[2],u[3],this.round_radius,l==e.CARD_SHAPE?0:this.round_radius): -l==e.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0],0,2*Math.PI);b.fill();a.flags.collapsed||(b.shadowColor="transparent",b.fillStyle="rgba(0,0,0,0.2)",b.fillRect(0,-1,u[2],2));b.shadowColor="transparent";if(a.onDrawBackground)a.onDrawBackground(b,this,this.canvas);if(q||p==e.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,f,d,this.ds.scale,g);else if(p!=e.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){q=a.constructor.title_color||g;a.flags.collapsed&&(b.shadowColor= -e.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var s=k.gradients[q];s||(s=k.gradients[q]=b.createLinearGradient(0,0,400,0),s.addColorStop(0,q),s.addColorStop(1,"#000"));b.fillStyle=s}else b.fillStyle=q;b.beginPath();l==e.BOX_SHAPE||c?b.rect(0,-f,d[0]+1,f):l!=e.ROUND_SHAPE&&l!=e.CARD_SHAPE||b.roundRect(0,-f,d[0]+1,f,this.round_radius,a.flags.collapsed?this.round_radius:0);b.fill();b.shadowColor="transparent"}if(a.onDrawTitleBox)a.onDrawTitleBox(b,f,d,this.ds.scale);else l==e.ROUND_SHAPE||l==e.CIRCLE_SHAPE|| -l==e.CARD_SHAPE?(c&&(b.fillStyle="black",b.beginPath(),b.arc(0.5*f,-0.5*f,6,0,2*Math.PI),b.fill()),b.fillStyle=a.boxcolor||e.NODE_DEFAULT_BOXCOLOR,c?b.fillRect(0.5*f-5,-0.5*f-5,10,10):(b.beginPath(),b.arc(0.5*f,-0.5*f,5,0,2*Math.PI),b.fill())):(c&&(b.fillStyle="black",b.fillRect(0.5*(f-10)-1,-0.5*(f+10)-1,12,12)),b.fillStyle=a.boxcolor||e.NODE_DEFAULT_BOXCOLOR,b.fillRect(0.5*(f-10),-0.5*(f+10),10,10));b.globalAlpha=t;if(a.onDrawTitleText)a.onDrawTitleText(b,f,d,this.ds.scale,this.title_text_font, -r);!c&&(b.font=this.title_text_font,c=String(a.getTitle()))&&(b.fillStyle=r?"white":a.constructor.title_text_color||this.node_title_color,a.flags.collapsed?(b.textAlign="left",b.measureText(c),b.fillText(c.substr(0,20),f,e.NODE_TITLE_TEXT_Y-f),b.textAlign="left"):(b.textAlign="left",b.fillText(c,f,e.NODE_TITLE_TEXT_Y-f)));if(a.onDrawTitle)a.onDrawTitle(b)}if(r){if(a.onBounding)a.onBounding(u);p==e.TRANSPARENT_TITLE&&(u[1]-=f,u[3]+=f);b.lineWidth=1;b.globalAlpha=0.8;b.beginPath();l==e.BOX_SHAPE?b.rect(-6+ -u[0],-6+u[1],12+u[2],12+u[3]):l==e.ROUND_SHAPE||l==e.CARD_SHAPE&&a.flags.collapsed?b.roundRect(-6+u[0],-6+u[1],12+u[2],12+u[3],2*this.round_radius):l==e.CARD_SHAPE?b.roundRect(-6+u[0],-6+u[1],12+u[2],12+u[3],2*this.round_radius,2):l==e.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0]+6,0,2*Math.PI);b.strokeStyle="#FFF";b.stroke();b.strokeStyle=g;b.globalAlpha=1}};var G=new Float32Array(4),q=new Float32Array(4),p=new Float32Array(2),l=new Float32Array(2);k.prototype.drawConnections=function(a){var b= -e.getTime(),d=this.visible_area;G[0]=d[0]-20;G[1]=d[1]-20;G[2]=d[2]+40;G[3]=d[3]+40;a.lineWidth=this.connections_width;a.fillStyle="#AAA";a.strokeStyle="#AAA";a.globalAlpha=this.editor_alpha;for(var d=this.graph._nodes,g=0,f=d.length;gq[2]&&(q[0]+=q[2],q[2]=Math.abs(q[2]));0>q[3]&&(q[1]+=q[3],q[3]=Math.abs(q[3]));if(w(q,G)){var n=k.outputs[z],z=r.inputs[t];if(n&&z&&(k=n.dir||(k.horizontal?e.DOWN:e.RIGHT),z=z.dir||(r.horizontal?e.UP:e.LEFT),this.renderLink(a,h,s,c,!1,0,null,k,z),c&&c._last_time&&1E3>b-c._last_time)){var n=2-0.002*(b-c._last_time),K=a.globalAlpha;a.globalAlpha=K*n;this.renderLink(a,h,s,c,!0,n,"white",k,z); -a.globalAlpha=K}}}}}}a.globalAlpha=1};k.prototype.renderLink=function(a,b,d,g,f,r,c,l,p,q){g&&this.visible_links.push(g);!c&&g&&(c=g.color||k.link_type_colors[g.type]);c||(c=this.default_link_color);null!=g&&this.highlighted_links[g.id]&&(c="#FFF");l=l||e.RIGHT;p=p||e.LEFT;var z=C(b,d);this.render_connections_border&&0.6b[1]?0:Math.PI,a.save(),a.translate(s[0],s[1]),a.rotate(h),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore(),a.save(),a.translate(q[0],q[1]),a.rotate(n),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill());if(r)for(a.fillStyle=c,s=0;5>s;++s)r=(0.001*e.getTime()+0.2*s)%1,f=this.computeConnectionPoint(b,d,r,l,p),a.beginPath(),a.arc(f[0], -f[1],5,0,2*Math.PI),a.fill()};k.prototype.computeConnectionPoint=function(a,b,d,g,f){g=g||e.RIGHT;f=f||e.LEFT;var r=C(a,b),c=[a[0],a[1]],l=[b[0],b[1]];switch(g){case e.LEFT:c[0]+=-0.25*r;break;case e.RIGHT:c[0]+=0.25*r;break;case e.UP:c[1]+=-0.25*r;break;case e.DOWN:c[1]+=0.25*r}switch(f){case e.LEFT:l[0]+=-0.25*r;break;case e.RIGHT:l[0]+=0.25*r;break;case e.UP:l[1]+=-0.25*r;break;case e.DOWN:l[1]+=0.25*r}g=(1-d)*(1-d)*(1-d);f=3*(1-d)*(1-d)*d;r=3*(1-d)*d*d;d*=d*d;return[g*a[0]+f*c[0]+r*l[0]+d*b[0], -g*a[1]+f*c[1]+r*l[1]+d*b[1]]};k.prototype.drawExecutionOrder=function(a){a.shadowColor="transparent";a.globalAlpha=0.25;a.textAlign="center";a.strokeStyle="white";a.globalAlpha=0.75;for(var b=this.visible_nodes,d=0;ds.last_y&&ts.options.max&&(s.value=s.options.max);else if("mousedown"==d.type){var z=s.options.values;z&&z.constructor=== -Function&&(z=s.options.values(s,a));var n=null;"number"!=s.type&&(n=z.constructor===Array?z:Object.keys(z));c=40>c?-1:c>l-40?1:0;if("number"==s.type)s.value+=0.1*c*(s.options.step||1),null!=s.options.min&&s.values.options.max&&(s.value=s.options.max);else if(c)q=-1,q=z.constructor===Object?n.indexOf(String(s.value))+c:n.indexOf(s.value)+c,q>=n.length&&(q=n.length-1),0>q&&(q=0),s.value=z.constructor===Array?z[q]:q;else{var y=z!=n? -Object.values(z):z;new e.ContextMenu(y,{scale:Math.max(1,this.ds.scale),event:d,className:"dark",callback:m.bind(s)},q);var m=function(a,b,d){z!=n&&(a=y.indexOf(a));this.value=a;f(this,a);p.dirty_canvas=!0;return!1}}}else"mouseup"==d.type&&"number"==s.type&&(c=40>c?-1:c>l-40?1:0,200>d.click_time&&0==c&&this.prompt("Value",s.value,function(a){this.value=Number(a);f(this,this.value)}.bind(s),d));g!=s.value&&setTimeout(function(){f(this,this.value)}.bind(s),20);this.dirty_canvas=!0;break;case "toggle":"mousedown"== -d.type&&(s.value=!s.value,setTimeout(function(){f(s,s.value)},20));break;case "string":case "text":"mousedown"==d.type&&this.prompt("Value",s.value,function(a){this.value=a;f(this,a)}.bind(s),d);break;default:s.mouse&&(this.dirty_canvas=s.mouse(d,[c,t],a))}return s}}}return null};k.prototype.drawGroups=function(a,b){if(this.graph){var d=this.graph._groups;b.save();b.globalAlpha=0.5*this.editor_alpha;for(var g=0;gd&&0.01>b.editor_alpha&&(clearInterval(g),1>d&&(b.live_mode=!0));1"+p+""+a+"",value:p});if(q.length)return new e.ContextMenu(q,{event:d,callback:c,parentMenu:g,allow_html:!0, -node:f},b),!1}};k.decodeHTML=function(a){var b=document.createElement("div");b.innerText=a;return b.innerHTML};k.onResizeNode=function(a,b,d,g,f){f&&(f.size=f.computeSize(),f.setDirtyCanvas(!0,!0))};k.prototype.showLinkMenu=function(a,b){var d=this;console.log(a);var g=new e.ContextMenu(["Add Node",null,"Delete"],{event:b,title:null!=a.data?a.data.constructor.name:null,callback:function(b,e,c){switch(b){case "Add Node":k.onMenuAdd(null,null,c,g,function(b){console.log("node autoconnect");var f=d.graph.getNodeById(a.origin_id), -g=d.graph.getNodeById(a.target_id);b.inputs&&b.inputs.length&&b.outputs&&b.outputs.length&&f.outputs[a.origin_slot].type==b.inputs[0].type&&b.outputs[0].type==g.inputs[0].type&&(f.connect(a.origin_slot,b,0),b.connect(0,g,a.target_slot),b.pos[0]-=0.5*b.size[0])});break;case "Delete":d.graph.removeLink(a.id)}}});return!1};k.onShowPropertyEditor=function(a,b,d,g,f){function e(){var b=q.value;"Number"==a.type?b=Number(b):"Boolean"==a.type&&(b=Boolean(b));f[c]=b;l.parentNode&&l.parentNode.removeChild(l); -f.setDirtyCanvas(!0,!0)}var c=a.property||"title";b=f[c];var l=document.createElement("div");l.className="graphdialog";l.innerHTML="";l.querySelector(".name").innerText=c;var q=l.querySelector("input");q&&(q.value=b,q.addEventListener("blur",function(a){this.focus()}),q.addEventListener("keydown",function(a){13==a.keyCode&&(e(),a.preventDefault(),a.stopPropagation())}));b=k.active_canvas.canvas;d=b.getBoundingClientRect(); -var p=g=-20;d&&(g-=d.left,p-=d.top);event?(l.style.left=event.clientX+g+"px",l.style.top=event.clientY+p+"px"):(l.style.left=0.5*b.width+g+"px",l.style.top=0.5*b.height+p+"px");l.querySelector("button").addEventListener("click",e);b.parentNode.appendChild(l)};k.prototype.prompt=function(a,b,d,g){var f=this;a=a||"";var e=!1,c=document.createElement("div");c.className="graphdialog rounded";c.innerHTML=" "; -c.close=function(){f.prompt_box=null;c.parentNode&&c.parentNode.removeChild(c)};1k.search_limit)break}}t=null;if(Array.prototype.filter)t=Object.keys(e.registered_node_types).filter(p); -else for(l in t=[],e.registered_node_types)p(l)&&t.push(l);for(l=0;lk.search_limit);l++);var p=function(a){var b=e.registered_node_types[a];return Q&&b.filter!=Q?!1:-1!==a.toLowerCase().indexOf(d)}}}var f=this,c=k.active_canvas,l=c.canvas,q=l.ownerDocument||document,p=document.createElement("div");p.className="litegraph litesearchbox graphdialog rounded";p.innerHTML="Search
"; -p.close=function(){f.search_box=null;q.body.focus();q.body.style.overflow="";setTimeout(function(){f.canvas.focus()},20);p.parentNode&&p.parentNode.removeChild(p)};var h=null;1l.height-200&&(z.style.maxHeight=l.height-a.layerY-20+"px");m.focus();return p};k.prototype.showEditPropertyValue=function(a,b,d){function g(){f(s.value)}function f(f){"number"==typeof a.properties[b]&&(f=Number(f));if("array"==e||"object"==e)f=JSON.parse(f);a.properties[b]=f;a._graph&&a._graph._version++;if(a.onPropertyChanged)a.onPropertyChanged(b, -f);if(d.onclose)d.onclose();h.close();a.setDirtyCanvas(!0,!0)}if(a&&void 0!==a.properties[b]){d=d||{};var c=a.getPropertyInfo(b),e=c.type,l="";if("string"==e||"number"==e||"array"==e||"object"==e)l="";else if("enum"==e&&c.values){var l=""}else if("boolean"== -e)l="";else{console.warn("unknown type: "+e);return}var h=this.createDialog(""+b+""+l+"",d);if("enum"==e&&c.values){var s=h.querySelector("select");s.addEventListener("change",function(a){f(a.target.value)})}else if("boolean"==e)(s=h.querySelector("input"))&&s.addEventListener("click",function(a){f(!!s.checked)});else if(s=h.querySelector("input"))s.addEventListener("blur", -function(a){this.focus()}),p=void 0!==a.properties[b]?a.properties[b]:"",p=JSON.stringify(p),s.value=p,s.addEventListener("keydown",function(a){13==a.keyCode&&(g(),a.preventDefault(),a.stopPropagation())});h.querySelector("button").addEventListener("click",g);return h}};k.prototype.createDialog=function(a,b){b=b||{};var d=document.createElement("div");d.className="graphdialog";d.innerHTML=a;var g=this.canvas.getBoundingClientRect(),f=-20,c=-20;g&&(f-=g.left,c-=g.top);b.position?(f+=b.position[0], -c+=b.position[1]):b.event?(f+=b.event.clientX,c+=b.event.clientY):(f+=0.5*this.canvas.width,c+=0.5*this.canvas.height);d.style.left=f+"px";d.style.top=c+"px";this.canvas.parentNode.appendChild(d);d.close=function(){this.parentNode&&this.parentNode.removeChild(this)};return d};k.onMenuNodeCollapse=function(a,b,d,g,f){f.collapse()};k.onMenuNodePin=function(a,b,d,g,f){f.pin()};k.onMenuNodeMode=function(a,b,d,g,f){new e.ContextMenu(["Always","On Event","On Trigger","Never"],{event:d,callback:function(a){if(f)switch(a){case "On Event":f.mode= -e.ON_EVENT;break;case "On Trigger":f.mode=e.ON_TRIGGER;break;case "Never":f.mode=e.NEVER;break;default:f.mode=e.ALWAYS}},parentMenu:g,node:f});return!1};k.onMenuNodeColors=function(a,b,d,g,f){if(!f)throw"no node for color";b=[];b.push({value:null,content:"No color"});for(var c in k.node_colors)a=k.node_colors[c],a={value:c,content:""+c+""},b.push(a);new e.ContextMenu(b,{event:d,callback:function(a){f&&((a=a.value?k.node_colors[a.value]:null)?f.constructor===e.LGraphGroup?f.color=a.groupcolor:(f.color=a.color,f.bgcolor=a.bgcolor):(delete f.color,delete f.bgcolor),f.setDirtyCanvas(!0,!0))},parentMenu:g,node:f});return!1};k.onMenuNodeShapes=function(a,b,d,g,f){if(!f)throw"no node passed";new e.ContextMenu(e.VALID_SHAPES,{event:d,callback:function(a){f&&(f.shape=a,f.setDirtyCanvas(!0))},parentMenu:g,node:f}); -return!1};k.onMenuNodeRemove=function(a,b,d,g,f){if(!f)throw"no node passed";!1!==f.removable&&(f.graph.remove(f),f.setDirtyCanvas(!0,!0))};k.onMenuNodeClone=function(a,b,d,g,f){!1!=f.clonable&&(a=f.clone())&&(a.pos=[f.pos[0]+5,f.pos[1]+5],f.graph.add(a),f.setDirtyCanvas(!0,!0))};k.node_colors={red:{color:"#322",bgcolor:"#533",groupcolor:"#A88"},brown:{color:"#332922",bgcolor:"#593930",groupcolor:"#b06634"},green:{color:"#232",bgcolor:"#353",groupcolor:"#8A8"},blue:{color:"#223",bgcolor:"#335",groupcolor:"#88A"}, -pale_blue:{color:"#2a363b",bgcolor:"#3f5159",groupcolor:"#3f789e"},cyan:{color:"#233",bgcolor:"#355",groupcolor:"#8AA"},purple:{color:"#323",bgcolor:"#535",groupcolor:"#a1309b"},yellow:{color:"#432",bgcolor:"#653",groupcolor:"#b58b2a"},black:{color:"#222",bgcolor:"#000",groupcolor:"#444"}};k.prototype.getCanvasMenuOptions=function(){var a=null;this.getMenuOptions?a=this.getMenuOptions():(a=[{content:"Add Node",has_submenu:!0,callback:k.onMenuAdd},{content:"Add Group",callback:k.onGroupAdd}],this._graph_stack&& -0a&&this.pos[1]-f-db)return!0;return!1};n.prototype.getSlotInPosition=function(a,b){var d=new Float32Array(2);if(this.inputs)for(var g=0,f=this.inputs.length;g=this.outputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),null;b&&b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"target node is null";if(b==this)return null;if(d.constructor===String){if(d=b.findInputSlot(d),-1==d)return e.debug&&console.log("Connect: Error, no slot of name "+d),null}else{if(d===e.EVENT)return null; +if(!b.inputs||d>=b.inputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),null}null!=b.inputs[d].link&&b.disconnectInput(d);var g=this.outputs[a];if(b.onConnectInput&&!1===b.onConnectInput(d,g.type,g,this,a))return null;var f=b.inputs[d],r=null;if(e.isValidConnection(g.type,f.type)){r=new m(++this.graph.last_link_id,f.type,this.id,a,b.id,d);this.graph.links[r.id]=r;null==g.links&&(g.links=[]);g.links.push(r.id);b.inputs[d].link=r.id;this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(e.OUTPUT, +a,!0,r,g);if(b.onConnectionsChange)b.onConnectionsChange(e.INPUT,d,!0,r,f);this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(e.INPUT,b,d,this,a),this.graph.onNodeConnectionChange(e.OUTPUT,this,a,b,d))}this.setDirtyCanvas(!1,!0);this.graph.connectionChange(this,r);return r};n.prototype.disconnectOutput=function(a,b){if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return e.debug&&console.log("Connect: Error, no slot of name "+a),!1}else if(!this.outputs|| +a>=this.outputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),!1;var d=this.outputs[a];if(!d||!d.links||0==d.links.length)return!1;if(b){b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"Target Node not found";for(var g=0,f=d.links.length;g=this.inputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),!1;var b=this.inputs[a];if(!b)return!1;var d=this.inputs[a].link;this.inputs[a].link=null;var g=this.graph.links[d];if(g){var f=this.graph.getNodeById(g.origin_id);if(!f)return!1; +var r=f.outputs[g.origin_slot];if(!r||!r.links||0==r.links.length)return!1;for(var s=0,c=r.links.length;sb&&this.inputs[b].pos)return d[0]=this.pos[0]+this.inputs[b].pos[0],d[1]=this.pos[1]+this.inputs[b].pos[1],d;if(!a&&g>b&&this.outputs[b].pos)return d[0]=this.pos[0]+this.outputs[b].pos[0],d[1]=this.pos[1]+this.outputs[b].pos[1],d;if(this.horizontal)return d[0]=this.pos[0]+this.size[0]/g*(b+0.5),d[1]=a?this.pos[1]-e.NODE_TITLE_HEIGHT:this.pos[1]+this.size[1],d;d[0]=a?this.pos[0]+f:this.pos[0]+this.size[0]+1-f;d[1]= +this.pos[1]+(b+0.7)*e.NODE_SLOT_HEIGHT+(this.constructor.slot_start_y||0);return d};n.prototype.alignToGrid=function(){this.pos[0]=e.CANVAS_GRID_SIZE*Math.round(this.pos[0]/e.CANVAS_GRID_SIZE);this.pos[1]=e.CANVAS_GRID_SIZE*Math.round(this.pos[1]/e.CANVAS_GRID_SIZE)};n.prototype.trace=function(a){this.console||(this.console=[]);this.console.push(a);this.console.length>n.MAX_CONSOLE&&this.console.shift();this.graph.onNodeTrace(this,a)};n.prototype.setDirtyCanvas=function(a,b){this.graph&&this.graph.sendActionToCanvas("setDirty", +[a,b])};n.prototype.loadImage=function(a){var b=new Image;b.src=e.node_images_path+a;b.ready=!1;var d=this;b.onload=function(){this.ready=!0;d.setDirtyCanvas(!0)};return b};n.prototype.captureInput=function(a){if(this.graph&&this.graph.list_of_graphcanvas)for(var b=this.graph.list_of_graphcanvas,d=0;da.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});Object.defineProperty(this,"size",{set:function(a){!a||2>a.length||(this._size[0]=Math.max(140,a[0]),this._size[1]=Math.max(80,a[1]))},get:function(){return this._size},enumerable:!0})};h.prototype.configure=function(a){this.title=a.title; +this._bounding.set(a.bounding);this.color=a.color;this.font=a.font};h.prototype.serialize=function(){var a=this._bounding;return{title:this.title,bounding:[Math.round(a[0]),Math.round(a[1]),Math.round(a[2]),Math.round(a[3])],color:this.color,font:this.font}};h.prototype.move=function(a,b,d){this._pos[0]+=a;this._pos[1]+=b;if(!d)for(d=0;dthis.max_scale&&(a=this.max_scale);if(a!=this.scale&&this.element){var d=this.element.getBoundingClientRect();if(d){b=b||[0.5*d.width,0.5*d.height];d=this.convertCanvasToOffset(b);this.scale=a;0.01>Math.abs(this.scale-1)&&(this.scale=1);var g=this.convertCanvasToOffset(b),d=[g[0]-d[0], +g[1]-d[1]];this.offset[0]+=d[0];this.offset[1]+=d[1];if(this.onredraw)this.onredraw(this)}}};x.prototype.changeDeltaScale=function(a,b){this.changeScale(this.scale*a,b)};x.prototype.reset=function(){this.scale=1;this.offset[0]=0;this.offset[1]=0};z.LGraphCanvas=e.LGraphCanvas=k;k.link_type_colors={"-1":e.EVENT_LINK_COLOR,number:"#AAA",node:"#DCA"};k.gradients={};k.prototype.clear=function(){this.fps=this.render_time=this.last_draw_time=this.frame=0;this.dragging_rectangle=null;this.selected_nodes= +{};this.selected_group=null;this.visible_nodes=[];this.connecting_node=this.node_capturing_input=this.node_over=this.node_dragged=null;this.highlighted_links={};this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_widget=this.node_in_panel=this.dirty_area=null;this.last_mouse=[0,0];this.last_mouseclick=0;this.visible_area.set([0,0,0,0]);if(this.onClear)this.onClear()};k.prototype.setGraph=function(a,b){this.graph!=a&&(b||this.clear(),!a&&this.graph?this.graph.detachCanvas(this):(a.attachCanvas(this), +this._graph_stack&&(this._graph_stack=null),this.setDirty(!0,!0)))};k.prototype.openSubgraph=function(a){if(!a)throw"graph cannot be null";if(this.graph==a)throw"graph cannot be the same";this.clear();this.graph&&(this._graph_stack||(this._graph_stack=[]),this._graph_stack.push(this.graph));a.attachCanvas(this);this.setDirty(!0,!0)};k.prototype.closeSubgraph=function(){if(this._graph_stack&&0!=this._graph_stack.length){var a=this.graph._subgraph_node,b=this._graph_stack.pop();this.selected_nodes= +{};this.highlighted_links={};b.attachCanvas(this);this.setDirty(!0,!0);a&&(this.centerOnNode(a),this.selectNodes([a]))}};k.prototype.getCurrentGraph=function(){return this.graph};k.prototype.setCanvas=function(a,b){if(a&&a.constructor===String&&(a=document.getElementById(a),!a))throw"Error creating LiteGraph canvas: Canvas not found";if(a!==this.canvas&&(!a&&this.canvas&&(b||this.unbindEvents()),this.canvas=a,this.ds.element=a)){a.className+=" lgraphcanvas";a.data=this;a.tabindex="1";this.bgcanvas= +null;this.bgcanvas||(this.bgcanvas=document.createElement("canvas"),this.bgcanvas.width=this.canvas.width,this.bgcanvas.height=this.canvas.height);if(null==a.getContext){if("canvas"!=a.localName)throw"Element supplied for LGraphCanvas must be a element, you passed a "+a.localName;throw"This browser doesn't support Canvas";}null==(this.ctx=a.getContext("2d"))&&(a.webgl_enabled||console.warn("This canvas seems to be WebGL, enabling WebGL renderer"),this.enableWebGL());this._mousemove_callback= +this.processMouseMove.bind(this);this._mouseup_callback=this.processMouseUp.bind(this);b||this.bindEvents()}};k.prototype._doNothing=function(a){a.preventDefault();return!1};k.prototype._doReturnTrue=function(a){a.preventDefault();return!0};k.prototype.bindEvents=function(){if(this._events_binded)console.warn("LGraphCanvas: events already binded");else{var a=this.canvas,b=this.getCanvasWindow().document;this._mousedown_callback=this.processMouseDown.bind(this);this._mousewheel_callback=this.processMouseWheel.bind(this); +a.addEventListener("mousedown",this._mousedown_callback,!0);a.addEventListener("mousemove",this._mousemove_callback);a.addEventListener("mousewheel",this._mousewheel_callback,!1);a.addEventListener("contextmenu",this._doNothing);a.addEventListener("DOMMouseScroll",this._mousewheel_callback,!1);a.addEventListener("touchstart",this.touchHandler,!0);a.addEventListener("touchmove",this.touchHandler,!0);a.addEventListener("touchend",this.touchHandler,!0);a.addEventListener("touchcancel",this.touchHandler, +!0);this._key_callback=this.processKey.bind(this);a.addEventListener("keydown",this._key_callback,!0);b.addEventListener("keyup",this._key_callback,!0);this._ondrop_callback=this.processDrop.bind(this);a.addEventListener("dragover",this._doNothing,!1);a.addEventListener("dragend",this._doNothing,!1);a.addEventListener("drop",this._ondrop_callback,!1);a.addEventListener("dragenter",this._doReturnTrue,!1);this._events_binded=!0}};k.prototype.unbindEvents=function(){if(this._events_binded){var a=this.getCanvasWindow().document; +this.canvas.removeEventListener("mousedown",this._mousedown_callback);this.canvas.removeEventListener("mousewheel",this._mousewheel_callback);this.canvas.removeEventListener("DOMMouseScroll",this._mousewheel_callback);this.canvas.removeEventListener("keydown",this._key_callback);a.removeEventListener("keyup",this._key_callback);this.canvas.removeEventListener("contextmenu",this._doNothing);this.canvas.removeEventListener("drop",this._ondrop_callback);this.canvas.removeEventListener("dragenter",this._doReturnTrue); +this.canvas.removeEventListener("touchstart",this.touchHandler);this.canvas.removeEventListener("touchmove",this.touchHandler);this.canvas.removeEventListener("touchend",this.touchHandler);this.canvas.removeEventListener("touchcancel",this.touchHandler);this._ondrop_callback=this._key_callback=this._mousewheel_callback=this._mousedown_callback=null;this._events_binded=!1}else console.warn("LGraphCanvas: no events binded")};k.getFileExtension=function(a){var b=a.indexOf("?");-1!=b&&(a=a.substr(0,b)); +b=a.lastIndexOf(".");return-1==b?"":a.substr(b+1).toLowerCase()};k.prototype.enableWebGL=function(){if(void 0===typeof GL)throw"litegl.js must be included to use a WebGL canvas";if(void 0===typeof enableWebGLCanvas)throw"webglCanvas.js must be included to use this feature";this.gl=this.ctx=enableWebGLCanvas(this.canvas);this.ctx.webgl=!0;this.bgcanvas=this.canvas;this.bgctx=this.gl;this.canvas.webgl_enabled=!0};k.prototype.setDirty=function(a,b){a&&(this.dirty_canvas=!0);b&&(this.dirty_bgcanvas=!0)}; +k.prototype.getCanvasWindow=function(){if(!this.canvas)return window;var a=this.canvas.ownerDocument;return a.defaultView||a.parentWindow};k.prototype.startRendering=function(){function a(){this.pause_rendering||this.draw();var b=this.getCanvasWindow();this.is_rendering&&b.requestAnimationFrame(a.bind(this))}this.is_rendering||(this.is_rendering=!0,a.call(this))};k.prototype.stopRendering=function(){this.is_rendering=!1};k.prototype.processMouseDown=function(a){if(this.graph){this.adjustMouseEvent(a); +var b=this.getCanvasWindow();k.active_canvas=this;this.canvas.removeEventListener("mousemove",this._mousemove_callback);b.document.addEventListener("mousemove",this._mousemove_callback,!0);b.document.addEventListener("mouseup",this._mouseup_callback,!0);var d=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes,5),g=!1,f=300>e.getTime()-this.last_mouseclick;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;this.canvas.focus();e.closeAllContextMenus(b);if(!this.onMouse||!0!=this.onMouse(a)){if(1== +a.which){a.ctrlKey&&(this.dragging_rectangle=new Float32Array(4),this.dragging_rectangle[0]=a.canvasX,this.dragging_rectangle[1]=a.canvasY,this.dragging_rectangle[2]=1,this.dragging_rectangle[3]=1,g=!0);var r=!1;if(d&&this.allow_interaction&&!g&&!this.read_only){this.live_mode||d.flags.pinned||this.bringToFront(d);if(!this.connecting_node&&!d.flags.collapsed&&!this.live_mode)if(!g&&!1!==d.resizable&&w(a.canvasX,a.canvasY,d.pos[0]+d.size[0]-5,d.pos[1]+d.size[1]-5,10,10))this.resizing_node=d,this.canvas.style.cursor= +"se-resize",g=!0;else{if(d.outputs)for(var s=0,c=d.outputs.length;sr[0]+4||a.canvasYr[1]+4)){this.showLinkMenu(d,a);this.over_link_center=null;break}this.selected_group=this.graph.getGroupOnPos(a.canvasX,a.canvasY);this.selected_group_resizing=!1;this.selected_group&&!this.read_only&&(a.ctrlKey&&(this.dragging_rectangle=null),10>C([a.canvasX,a.canvasY],[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])* +this.ds.scale?this.selected_group_resizing=!0:this.selected_group.recomputeInsideNodes());f&&!this.read_only&&this.allow_searchbox&&this.showSearchBox(a);r=!0}!g&&r&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}else 2!=a.which&&3==a.which&&(this.read_only||this.processContextMenu(d,a));this.last_mouse[0]=a.localX;this.last_mouse[1]=a.localY;this.last_mouseclick=e.getTime();this.last_mouse_dragging=!0;this.graph.change();(!b.document.activeElement||"input"!=b.document.activeElement.nodeName.toLowerCase()&& +"textarea"!=b.document.activeElement.nodeName.toLowerCase())&&a.preventDefault();a.stopPropagation();if(this.onMouseDown)this.onMouseDown(a);return!1}}};k.prototype.processMouseMove=function(a){this.autoresize&&this.resize();if(this.graph){k.active_canvas=this;this.adjustMouseEvent(a);var b=[a.localX,a.localY],d=[b[0]-this.last_mouse[0],b[1]-this.last_mouse[1]];this.last_mouse=b;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;a.dragging=this.last_mouse_dragging;this.node_widget&&(this.processNodeWidgets(this.node_widget[0], +this.canvas_mouse,a,this.node_widget[1]),this.dirty_canvas=!0);if(this.dragging_rectangle)this.dragging_rectangle[2]=a.canvasX-this.dragging_rectangle[0],this.dragging_rectangle[3]=a.canvasY-this.dragging_rectangle[1],this.dirty_canvas=!0;else if(this.selected_group&&!this.read_only)this.selected_group_resizing?this.selected_group.size=[a.canvasX-this.selected_group.pos[0],a.canvasY-this.selected_group.pos[1]]:(this.selected_group.move(d[0]/this.ds.scale,d[1]/this.ds.scale,a.ctrlKey),this.selected_group._nodes.length&& +(this.dirty_canvas=!0)),this.dirty_bgcanvas=!0;else if(this.dragging_canvas)this.ds.offset[0]+=d[0]/this.ds.scale,this.ds.offset[1]+=d[1]/this.ds.scale,this.dirty_bgcanvas=this.dirty_canvas=!0;else if(this.allow_interaction&&!this.read_only){this.connecting_node&&(this.dirty_canvas=!0);for(var g=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes),b=0,f=this.graph._nodes.length;bs[0]+4||a.canvasYs[1]+4)){f=r;break}}f!=this.over_link_center&&(this.over_link_center=f,this.dirty_canvas=!0);this.canvas&&(this.canvas.style.cursor= +"")}if(this.node_capturing_input&&this.node_capturing_input!=g&&this.node_capturing_input.onMouseMove)this.node_capturing_input.onMouseMove(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]],this);if(this.node_dragged&&!this.live_mode){for(b in this.selected_nodes)g=this.selected_nodes[b],g.pos[0]+=d[0]/this.ds.scale,g.pos[1]+=d[1]/this.ds.scale;this.dirty_bgcanvas=this.dirty_canvas=!0}this.resizing_node&&!this.live_mode&&(d=[a.canvasX-this.resizing_node.pos[0], +a.canvasY-this.resizing_node.pos[1]],b=this.resizing_node.computeSize(),d[0]=Math.max(b[0],d[0]),d[1]=Math.max(b[1],d[1]),this.resizing_node.setSize(d),this.canvas.style.cursor="se-resize",this.dirty_bgcanvas=this.dirty_canvas=!0)}a.preventDefault();return!1}};k.prototype.processMouseUp=function(a){if(this.graph){var b=this.getCanvasWindow().document;k.active_canvas=this;b.removeEventListener("mousemove",this._mousemove_callback,!0);this.canvas.addEventListener("mousemove",this._mousemove_callback, +!0);b.removeEventListener("mouseup",this._mouseup_callback,!0);this.adjustMouseEvent(a);b=e.getTime();a.click_time=b-this.last_mouseclick;this.last_mouse_dragging=!1;if(1==a.which){this.node_widget&&this.processNodeWidgets(this.node_widget[0],this.canvas_mouse,a);this.node_widget=null;if(this.selected_group){var b=this.selected_group.pos[0]-Math.round(this.selected_group.pos[0]),d=this.selected_group.pos[1]-Math.round(this.selected_group.pos[1]);this.selected_group.move(b,d,a.ctrlKey);this.selected_group.pos[0]= +Math.round(this.selected_group.pos[0]);this.selected_group.pos[1]=Math.round(this.selected_group.pos[1]);this.selected_group._nodes.length&&(this.dirty_canvas=!0);this.selected_group=null}this.selected_group_resizing=!1;if(this.dragging_rectangle){if(this.graph){b=this.graph._nodes;d=new Float32Array(4);this.deselectAllNodes();var g=Math.abs(this.dragging_rectangle[2]),f=Math.abs(this.dragging_rectangle[3]),r=0>this.dragging_rectangle[3]?this.dragging_rectangle[1]-f:this.dragging_rectangle[1];this.dragging_rectangle[0]= +0>this.dragging_rectangle[2]?this.dragging_rectangle[0]-g:this.dragging_rectangle[0];this.dragging_rectangle[1]=r;this.dragging_rectangle[2]=g;this.dragging_rectangle[3]=f;f=[];for(r=0;ra.click_time&&w(a.canvasX,a.canvasY,g.pos[0],g.pos[1]-e.NODE_TITLE_HEIGHT,e.NODE_TITLE_HEIGHT,e.NODE_TITLE_HEIGHT)&&g.collapse();this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_dragged.pos[0]=Math.round(this.node_dragged.pos[0]);this.node_dragged.pos[1]=Math.round(this.node_dragged.pos[1]);this.graph.config.align_to_grid&& +this.node_dragged.alignToGrid();if(this.onNodeMoved)this.onNodeMoved(this.node_dragged);this.node_dragged=null}else{g=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes);!g&&300>a.click_time&&this.deselectAllNodes();this.dirty_canvas=!0;this.dragging_canvas=!1;if(this.node_over&&this.node_over.onMouseUp)this.node_over.onMouseUp(a,[a.canvasX-this.node_over.pos[0],a.canvasY-this.node_over.pos[1]],this);if(this.node_capturing_input&&this.node_capturing_input.onMouseUp)this.node_capturing_input.onMouseUp(a, +[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]])}}else 2==a.which?(this.dirty_canvas=!0,this.dragging_canvas=!1):3==a.which&&(this.dirty_canvas=!0,this.dragging_canvas=!1);this.graph.change();a.stopPropagation();a.preventDefault();return!1}};k.prototype.processMouseWheel=function(a){if(this.graph&&this.allow_dragcanvas){var b=null!=a.wheelDeltaY?a.wheelDeltaY:-60*a.detail;this.adjustMouseEvent(a);var d=this.ds.scale;0b&&(d*=1/1.1);this.ds.changeScale(d, +[a.localX,a.localY]);this.graph.change();a.preventDefault();return!1}};k.prototype.isOverNodeBox=function(a,b,d){var g=e.NODE_TITLE_HEIGHT;return w(b,d,a.pos[0]+2,a.pos[1]+2-g,g-4,g-4)?!0:!1};k.prototype.isOverNodeInput=function(a,b,d,g){if(a.inputs)for(var f=0,e=a.inputs.length;fd- +this.graph._last_trigger_time)&&this.drawBackCanvas();(this.dirty_canvas||a)&&this.drawFrontCanvas();this.fps=this.render_time?1/this.render_time:0;this.frame+=1}};k.prototype.drawFrontCanvas=function(){this.dirty_canvas=!1;this.ctx||(this.ctx=this.bgcanvas.getContext("2d"));var a=this.ctx;if(a){a.start2D&&a.start2D();var b=this.canvas;a.restore();a.setTransform(1,0,0,1,0,0);this.dirty_area&&(a.save(),a.beginPath(),a.rect(this.dirty_area[0],this.dirty_area[1],this.dirty_area[2],this.dirty_area[3]), +a.clip());this.clear_background&&a.clearRect(0,0,b.width,b.height);this.bgcanvas==this.canvas?this.drawBackCanvas():a.drawImage(this.bgcanvas,0,0);if(this.onRender)this.onRender(b,a);this.show_info&&this.renderInfo(a);if(this.graph){a.save();this.ds.toCanvasContext(a);for(var b=this.computeVisibleNodes(null,this.visible_nodes),d=0;d> ";b.fillText(g+d.getTitle(),0.5*a.width,40);b.restore()}d=!1;this.onRenderBackground&&(d=this.onRenderBackground(a,b));b.restore(); +b.setTransform(1,0,0,1,0,0);this.visible_links.length=0;if(this.graph){b.save();this.ds.toCanvasContext(b);if(this.background_image&&0.5this.ds.scale;if(this.live_mode){if(!a.flags.collapsed&&(b.shadowColor="transparent",a.onDrawForeground))a.onDrawForeground(b,this,this.canvas)}else{var r=this.editor_alpha;b.globalAlpha=r;this.render_shadows&&!f?(b.shadowColor= +e.DEFAULT_SHADOW_COLOR,b.shadowOffsetX=2*this.ds.scale,b.shadowOffsetY=2*this.ds.scale,b.shadowBlur=3*this.ds.scale):b.shadowColor="transparent";if(!a.flags.collapsed||!a.onDrawCollapsed||!0!=a.onDrawCollapsed(b,this)){var s=a._shape||e.BOX_SHAPE;B.set(a.size);var c=a.horizontal;if(a.flags.collapsed){b.font=this.inner_text_font;var l=a.getTitle?a.getTitle():a.title;null!=l&&(a._collapsed_width=Math.min(a.size[0],b.measureText(l).width+2*e.NODE_TITLE_HEIGHT),B[0]=a._collapsed_width,B[1]=0)}a.clip_area&& +(b.save(),b.beginPath(),s==e.BOX_SHAPE?b.rect(0,0,B[0],B[1]):s==e.ROUND_SHAPE?b.roundRect(0,0,B[0],B[1],10):s==e.CIRCLE_SHAPE&&b.arc(0.5*B[0],0.5*B[1],0.5*B[0],0,2*Math.PI),b.clip());a.has_errors&&(g="red");this.drawNodeShape(a,b,B,d,g,a.is_selected,a.mouseOver);b.shadowColor="transparent";if(a.onDrawForeground)a.onDrawForeground(b,this,this.canvas);b.textAlign=c?"center":"left";b.font=this.inner_text_font;g=!f;s=this.connecting_output;b.lineWidth=1;var l=0,q=new Float32Array(2);if(!a.flags.collapsed){if(a.inputs)for(d= +0;dthis.ds.scale,l=a._shape||a.constructor.shape||e.ROUND_SHAPE,q=a.constructor.title_mode,p=!0;q==e.TRANSPARENT_TITLE?p=!1:q==e.AUTOHIDE_TITLE&&s&&(p=!0);u[0]=0;u[1]=p?-f:0;u[2]=d[0]+1;u[3]=p?d[1]+f:d[1];s=b.globalAlpha;b.beginPath();l==e.BOX_SHAPE|| +c?b.fillRect(u[0],u[1],u[2],u[3]):l==e.ROUND_SHAPE||l==e.CARD_SHAPE?b.roundRect(u[0],u[1],u[2],u[3],this.round_radius,l==e.CARD_SHAPE?0:this.round_radius):l==e.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0],0,2*Math.PI);b.fill();a.flags.collapsed||(b.shadowColor="transparent",b.fillStyle="rgba(0,0,0,0.2)",b.fillRect(0,-1,u[2],2));b.shadowColor="transparent";if(a.onDrawBackground)a.onDrawBackground(b,this,this.canvas);if(p||q==e.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,f,d,this.ds.scale, +g);else if(q!=e.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){p=a.constructor.title_color||g;a.flags.collapsed&&(b.shadowColor=e.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var v=k.gradients[p];v||(v=k.gradients[p]=b.createLinearGradient(0,0,400,0),v.addColorStop(0,p),v.addColorStop(1,"#000"));b.fillStyle=v}else b.fillStyle=p;b.beginPath();l==e.BOX_SHAPE||c?b.rect(0,-f,d[0]+1,f):l!=e.ROUND_SHAPE&&l!=e.CARD_SHAPE||b.roundRect(0,-f,d[0]+1,f,this.round_radius,a.flags.collapsed? +this.round_radius:0);b.fill();b.shadowColor="transparent"}if(a.onDrawTitleBox)a.onDrawTitleBox(b,f,d,this.ds.scale);else l==e.ROUND_SHAPE||l==e.CIRCLE_SHAPE||l==e.CARD_SHAPE?(c&&(b.fillStyle="black",b.beginPath(),b.arc(0.5*f,-0.5*f,6,0,2*Math.PI),b.fill()),b.fillStyle=a.boxcolor||e.NODE_DEFAULT_BOXCOLOR,c?b.fillRect(0.5*f-5,-0.5*f-5,10,10):(b.beginPath(),b.arc(0.5*f,-0.5*f,5,0,2*Math.PI),b.fill())):(c&&(b.fillStyle="black",b.fillRect(0.5*(f-10)-1,-0.5*(f+10)-1,12,12)),b.fillStyle=a.boxcolor||e.NODE_DEFAULT_BOXCOLOR, +b.fillRect(0.5*(f-10),-0.5*(f+10),10,10));b.globalAlpha=s;if(a.onDrawTitleText)a.onDrawTitleText(b,f,d,this.ds.scale,this.title_text_font,r);!c&&(b.font=this.title_text_font,c=String(a.getTitle()))&&(b.fillStyle=r?"white":a.constructor.title_text_color||this.node_title_color,a.flags.collapsed?(b.textAlign="left",b.measureText(c),b.fillText(c.substr(0,20),f,e.NODE_TITLE_TEXT_Y-f),b.textAlign="left"):(b.textAlign="left",b.fillText(c,f,e.NODE_TITLE_TEXT_Y-f)));if(a.onDrawTitle)a.onDrawTitle(b)}if(r){if(a.onBounding)a.onBounding(u); +q==e.TRANSPARENT_TITLE&&(u[1]-=f,u[3]+=f);b.lineWidth=1;b.globalAlpha=0.8;b.beginPath();l==e.BOX_SHAPE?b.rect(-6+u[0],-6+u[1],12+u[2],12+u[3]):l==e.ROUND_SHAPE||l==e.CARD_SHAPE&&a.flags.collapsed?b.roundRect(-6+u[0],-6+u[1],12+u[2],12+u[3],2*this.round_radius):l==e.CARD_SHAPE?b.roundRect(-6+u[0],-6+u[1],12+u[2],12+u[3],2*this.round_radius,2):l==e.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0]+6,0,2*Math.PI);b.strokeStyle="#FFF";b.stroke();b.strokeStyle=g;b.globalAlpha=1}};var F=new Float32Array(4), +q=new Float32Array(4),p=new Float32Array(2),l=new Float32Array(2);k.prototype.drawConnections=function(a){var b=e.getTime(),d=this.visible_area;F[0]=d[0]-20;F[1]=d[1]-20;F[2]=d[2]+40;F[3]=d[3]+40;a.lineWidth=this.connections_width;a.fillStyle="#AAA";a.strokeStyle="#AAA";a.globalAlpha=this.editor_alpha;for(var d=this.graph._nodes,g=0,f=d.length;gq[2]&&(q[0]+=q[2],q[2]=Math.abs(q[2]));0>q[3]&&(q[1]+=q[3],q[3]=Math.abs(q[3]));if(t(q,F)){var n=k.outputs[y],y=r.inputs[s];if(n&&y&&(k=n.dir||(k.horizontal?e.DOWN:e.RIGHT),y=y.dir||(r.horizontal?e.UP:e.LEFT),this.renderLink(a,h,v,c,!1,0,null,k,y),c&&c._last_time&&1E3>b-c._last_time)){var n= +2-0.002*(b-c._last_time),M=a.globalAlpha;a.globalAlpha=M*n;this.renderLink(a,h,v,c,!0,n,"white",k,y);a.globalAlpha=M}}}}}}a.globalAlpha=1};k.prototype.renderLink=function(a,b,d,g,f,r,c,l,p,q){g&&this.visible_links.push(g);!c&&g&&(c=g.color||k.link_type_colors[g.type]);c||(c=this.default_link_color);null!=g&&this.highlighted_links[g.id]&&(c="#FFF");l=l||e.RIGHT;p=p||e.LEFT;var y=C(b,d);this.render_connections_border&&0.6b[1]?0:Math.PI,a.save(),a.translate(v[0],v[1]),a.rotate(h),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore(),a.save(),a.translate(q[0],q[1]),a.rotate(n),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill());if(r)for(a.fillStyle= +c,v=0;5>v;++v)r=(0.001*e.getTime()+0.2*v)%1,f=this.computeConnectionPoint(b,d,r,l,p),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill()};k.prototype.computeConnectionPoint=function(a,b,d,g,f){g=g||e.RIGHT;f=f||e.LEFT;var r=C(a,b),c=[a[0],a[1]],l=[b[0],b[1]];switch(g){case e.LEFT:c[0]+=-0.25*r;break;case e.RIGHT:c[0]+=0.25*r;break;case e.UP:c[1]+=-0.25*r;break;case e.DOWN:c[1]+=0.25*r}switch(f){case e.LEFT:l[0]+=-0.25*r;break;case e.RIGHT:l[0]+=0.25*r;break;case e.UP:l[1]+=-0.25*r;break;case e.DOWN:l[1]+= +0.25*r}g=(1-d)*(1-d)*(1-d);f=3*(1-d)*(1-d)*d;r=3*(1-d)*d*d;d*=d*d;return[g*a[0]+f*c[0]+r*l[0]+d*b[0],g*a[1]+f*c[1]+r*l[1]+d*b[1]]};k.prototype.drawExecutionOrder=function(a){a.shadowColor="transparent";a.globalAlpha=0.25;a.textAlign="center";a.strokeStyle="white";a.globalAlpha=0.75;for(var b=this.visible_nodes,d=0;dv.last_y&&s +v.options.max&&(v.value=v.options.max);else if("mousedown"==d.type){var y=v.options.values;y&&y.constructor===Function&&(y=v.options.values(v,a));var n=null;"number"!=v.type&&(n=y.constructor===Array?y:Object.keys(y));c=40>c?-1:c>l-40?1:0;if("number"==v.type)v.value+=0.1*c*(v.options.step||1),null!=v.options.min&&v.valuev.options.max&&(v.value=v.options.max);else if(c)q=-1,q=y.constructor===Object?n.indexOf(String(v.value))+c:n.indexOf(v.value)+ +c,q>=n.length&&(q=n.length-1),0>q&&(q=0),v.value=y.constructor===Array?y[q]:q;else{var t=y!=n?Object.values(y):y;new e.ContextMenu(t,{scale:Math.max(1,this.ds.scale),event:d,className:"dark",callback:m.bind(v)},q);var m=function(a,b,d){y!=n&&(a=t.indexOf(a));this.value=a;f(this,a);p.dirty_canvas=!0;return!1}}}else"mouseup"==d.type&&"number"==v.type&&(c=40>c?-1:c>l-40?1:0,200>d.click_time&&0==c&&this.prompt("Value",v.value,function(a){this.value=Number(a);f(this,this.value)}.bind(v),d));g!=v.value&& +setTimeout(function(){f(this,this.value)}.bind(v),20);this.dirty_canvas=!0;break;case "toggle":"mousedown"==d.type&&(v.value=!v.value,setTimeout(function(){f(v,v.value)},20));break;case "string":case "text":"mousedown"==d.type&&this.prompt("Value",v.value,function(a){this.value=a;f(this,a)}.bind(v),d);break;default:v.mouse&&(this.dirty_canvas=v.mouse(d,[c,s],a))}return v}}}return null};k.prototype.drawGroups=function(a,b){if(this.graph){var d=this.graph._groups;b.save();b.globalAlpha=0.5*this.editor_alpha; +for(var g=0;gd&&0.01>b.editor_alpha&&(clearInterval(g),1>d&&(b.live_mode=!0));1"+ +q+""+a+"",value:q});if(p.length)return new e.ContextMenu(p,{event:d,callback:c,parentMenu:g,allow_html:!0,node:f},b),!1}};k.decodeHTML=function(a){var b=document.createElement("div");b.innerText=a;return b.innerHTML};k.onResizeNode=function(a,b,d,g,f){if(f){f.size=f.computeSize();if(f.onResize)f.onResize(f.size);f.setDirtyCanvas(!0,!0)}};k.prototype.showLinkMenu=function(a,b){var d=this;console.log(a);var g=new e.ContextMenu(["Add Node",null,"Delete"],{event:b, +title:null!=a.data?a.data.constructor.name:null,callback:function(b,e,c){switch(b){case "Add Node":k.onMenuAdd(null,null,c,g,function(b){console.log("node autoconnect");var f=d.graph.getNodeById(a.origin_id),g=d.graph.getNodeById(a.target_id);b.inputs&&b.inputs.length&&b.outputs&&b.outputs.length&&f.outputs[a.origin_slot].type==b.inputs[0].type&&b.outputs[0].type==g.inputs[0].type&&(f.connect(a.origin_slot,b,0),b.connect(0,g,a.target_slot),b.pos[0]-=0.5*b.size[0])});break;case "Delete":d.graph.removeLink(a.id)}}}); +return!1};k.onShowPropertyEditor=function(a,b,d,g,f){function e(){var b=p.value;"Number"==a.type?b=Number(b):"Boolean"==a.type&&(b=Boolean(b));f[c]=b;l.parentNode&&l.parentNode.removeChild(l);f.setDirtyCanvas(!0,!0)}var c=a.property||"title";b=f[c];var l=document.createElement("div");l.className="graphdialog";l.innerHTML="";l.querySelector(".name").innerText=c;var p=l.querySelector("input");p&&(p.value=b,p.addEventListener("blur", +function(a){this.focus()}),p.addEventListener("keydown",function(a){13==a.keyCode&&(e(),a.preventDefault(),a.stopPropagation())}));b=k.active_canvas.canvas;d=b.getBoundingClientRect();var q=g=-20;d&&(g-=d.left,q-=d.top);event?(l.style.left=event.clientX+g+"px",l.style.top=event.clientY+q+"px"):(l.style.left=0.5*b.width+g+"px",l.style.top=0.5*b.height+q+"px");l.querySelector("button").addEventListener("click",e);b.parentNode.appendChild(l)};k.prototype.prompt=function(a,b,d,g){var f=this;a=a||"";var e= +!1,c=document.createElement("div");c.className="graphdialog rounded";c.innerHTML=" ";c.close=function(){f.prompt_box=null;c.parentNode&&c.parentNode.removeChild(c)};1k.search_limit)break}}Q=null;if(Array.prototype.filter)Q=Object.keys(e.registered_node_types).filter(p);else for(l in Q=[],e.registered_node_types)p(l)&&Q.push(l);for(l=0;lk.search_limit);l++);var p=function(a){var b=e.registered_node_types[a];return s&&b.filter!=s?!1:-1!==a.toLowerCase().indexOf(d)}}} +var f=this,c=k.active_canvas,l=c.canvas,p=l.ownerDocument||document,q=document.createElement("div");q.className="litegraph litesearchbox graphdialog rounded";q.innerHTML="Search
";q.close=function(){f.search_box=null;p.body.focus();p.body.style.overflow="";setTimeout(function(){f.canvas.focus()},20);q.parentNode&&q.parentNode.removeChild(q)};var h=null;1l.height-200&&(y.style.maxHeight=l.height-a.layerY-20+"px");m.focus(); +return q};k.prototype.showEditPropertyValue=function(a,b,d){function g(){f(v.value)}function f(f){"number"==typeof a.properties[b]&&(f=Number(f));if("array"==e||"object"==e)f=JSON.parse(f);a.properties[b]=f;a._graph&&a._graph._version++;if(a.onPropertyChanged)a.onPropertyChanged(b,f);if(d.onclose)d.onclose();h.close();a.setDirtyCanvas(!0,!0)}if(a&&void 0!==a.properties[b]){d=d||{};var c=a.getPropertyInfo(b),e=c.type,l="";if("string"==e||"number"==e||"array"==e||"object"==e)l=""; +else if("enum"==e&&c.values){var l=""}else if("boolean"==e)l="";else{console.warn("unknown type: "+e);return}var h=this.createDialog(""+b+""+l+"",d);if("enum"== +e&&c.values){var v=h.querySelector("select");v.addEventListener("change",function(a){f(a.target.value)})}else if("boolean"==e)(v=h.querySelector("input"))&&v.addEventListener("click",function(a){f(!!v.checked)});else if(v=h.querySelector("input"))v.addEventListener("blur",function(a){this.focus()}),p=void 0!==a.properties[b]?a.properties[b]:"",p=JSON.stringify(p),v.value=p,v.addEventListener("keydown",function(a){13==a.keyCode&&(g(),a.preventDefault(),a.stopPropagation())});h.querySelector("button").addEventListener("click", +g);return h}};k.prototype.createDialog=function(a,b){b=b||{};var d=document.createElement("div");d.className="graphdialog";d.innerHTML=a;var g=this.canvas.getBoundingClientRect(),f=-20,c=-20;g&&(f-=g.left,c-=g.top);b.position?(f+=b.position[0],c+=b.position[1]):b.event?(f+=b.event.clientX,c+=b.event.clientY):(f+=0.5*this.canvas.width,c+=0.5*this.canvas.height);d.style.left=f+"px";d.style.top=c+"px";this.canvas.parentNode.appendChild(d);d.close=function(){this.parentNode&&this.parentNode.removeChild(this)}; +return d};k.onMenuNodeCollapse=function(a,b,d,g,f){f.collapse()};k.onMenuNodePin=function(a,b,d,g,f){f.pin()};k.onMenuNodeMode=function(a,b,d,g,f){new e.ContextMenu(["Always","On Event","On Trigger","Never"],{event:d,callback:function(a){if(f)switch(a){case "On Event":f.mode=e.ON_EVENT;break;case "On Trigger":f.mode=e.ON_TRIGGER;break;case "Never":f.mode=e.NEVER;break;default:f.mode=e.ALWAYS}},parentMenu:g,node:f});return!1};k.onMenuNodeColors=function(a,b,d,g,f){if(!f)throw"no node for color";b= +[];b.push({value:null,content:"No color"});for(var c in k.node_colors)a=k.node_colors[c],a={value:c,content:""+c+""},b.push(a);new e.ContextMenu(b,{event:d,callback:function(a){f&&((a=a.value?k.node_colors[a.value]:null)?f.constructor===e.LGraphGroup?f.color=a.groupcolor:(f.color=a.color,f.bgcolor=a.bgcolor): +(delete f.color,delete f.bgcolor),f.setDirtyCanvas(!0,!0))},parentMenu:g,node:f});return!1};k.onMenuNodeShapes=function(a,b,d,g,f){if(!f)throw"no node passed";new e.ContextMenu(e.VALID_SHAPES,{event:d,callback:function(a){f&&(f.shape=a,f.setDirtyCanvas(!0))},parentMenu:g,node:f});return!1};k.onMenuNodeRemove=function(a,b,d,g,f){if(!f)throw"no node passed";!1!==f.removable&&(f.graph.remove(f),f.setDirtyCanvas(!0,!0))};k.onMenuNodeToSubgraph=function(a,b,d,g,f){a=f.graph;if(b=k.active_canvas)d=Object.values(b.selected_nodes|| +{}),d.length||(d=[f]),g=e.createNode("graph/subgraph"),g.pos=f.pos.concat(),a.add(g),g.buildFromNodes(d),b.deselectAllNodes(),f.setDirtyCanvas(!0,!0)};k.onMenuNodeClone=function(a,b,d,g,f){!1!=f.clonable&&(a=f.clone())&&(a.pos=[f.pos[0]+5,f.pos[1]+5],f.graph.add(a),f.setDirtyCanvas(!0,!0))};k.node_colors={red:{color:"#322",bgcolor:"#533",groupcolor:"#A88"},brown:{color:"#332922",bgcolor:"#593930",groupcolor:"#b06634"},green:{color:"#232",bgcolor:"#353",groupcolor:"#8A8"},blue:{color:"#223",bgcolor:"#335", +groupcolor:"#88A"},pale_blue:{color:"#2a363b",bgcolor:"#3f5159",groupcolor:"#3f789e"},cyan:{color:"#233",bgcolor:"#355",groupcolor:"#8AA"},purple:{color:"#323",bgcolor:"#535",groupcolor:"#a1309b"},yellow:{color:"#432",bgcolor:"#653",groupcolor:"#b58b2a"},black:{color:"#222",bgcolor:"#000",groupcolor:"#444"}};k.prototype.getCanvasMenuOptions=function(){var a=null;this.getMenuOptions?a=this.getMenuOptions():(a=[{content:"Add Node",has_submenu:!0,callback:k.onMenuAdd},{content:"Add Group",callback:k.onGroupAdd}], +this._graph_stack&&0Name",f),l=e.querySelector("input");l&&c&&(l.value=c.label||"");e.querySelector("button").addEventListener("click",function(a){l.value&&(c&&(c.label=l.value),d.setDirty(!0));e.close()})}},extra:a};a&&(c.title=a.type);var l=null;a&&(l=a.getSlotInPosition(b.canvasX,b.canvasY),k.active_node= a);if(l){f=[];if(a.getSlotMenuOptions)f=a.getSlotMenuOptions(l);else{l&&l.output&&l.output.links&&l.output.links.length&&f.push({content:"Disconnect Links",slot:l});var q=l.input||l.output;f.push(q.locked?"Cannot remove":{content:"Remove Slot",slot:l});f.push(q.nameLocked?"Cannot rename":{content:"Rename Slot",slot:l})}c.title=(l.input?l.input.type:l.output.type)||"*";l.input&&l.input.type==e.ACTION&&(c.title="Action");l.output&&l.output.type==e.EVENT&&(c.title="Event")}else a?f=this.getNodeMenuOptions(a): (f=this.getCanvasMenuOptions(),(l=this.graph.getGroupOnPos(b.canvasX,b.canvasY))&&f.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:l,options:this.getGroupMenuOptions(l)}}));f&&new e.ContextMenu(f,c,g)};"undefined"!=typeof window&&window.CanvasRenderingContext2D&&(window.CanvasRenderingContext2D.prototype.roundRect=function(a,b,d,g,f,c){void 0===f&&(f=5);void 0===c&&(c=f);this.moveTo(a+f,b);this.lineTo(a+d-f,b);this.quadraticCurveTo(a+d,b,a+d,b+f);this.lineTo(a+d,b+g-c); -this.quadraticCurveTo(a+d,b+g,a+d-c,b+g);this.lineTo(a+c,b+g);this.quadraticCurveTo(a,b+g,a,b+g-c);this.lineTo(a,b+f);this.quadraticCurveTo(a,b,a+f,b)});e.compareObjects=function(a,b){for(var d in a)if(a[d]!=b[d])return!1;return!0};e.distance=C;e.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+","+Math.round(255*a[2]).toFixed()+","+(4==a.length?a[3].toFixed(2):"1.0")+")"};e.isInsideRectangle=x;e.growBounding=function(a,b,d){b -a[2]&&(a[2]=b);da[3]&&(a[3]=d)};e.isInsideBounding=function(a,b){return a[0]b[1][0]||a[1]>b[1][1]?!1:!0};e.overlapBounding=w;e.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),d=0,g,f,c=0;6>c;c+=2)g="0123456789ABCDEF".indexOf(a.charAt(c)),f="0123456789ABCDEF".indexOf(a.charAt(c+1)),b[d]=16*g+f,d++;return b};e.num2hex=function(a){for(var b="#",d,g,f=0;3>f;f++)d=a[f]/16,g=a[f]%16,b+="0123456789ABCDEF".charAt(d)+"0123456789ABCDEF".charAt(g); +this.quadraticCurveTo(a+d,b+g,a+d-c,b+g);this.lineTo(a+c,b+g);this.quadraticCurveTo(a,b+g,a,b+g-c);this.lineTo(a,b+f);this.quadraticCurveTo(a,b,a+f,b)});e.compareObjects=function(a,b){for(var d in a)if(a[d]!=b[d])return!1;return!0};e.distance=C;e.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+","+Math.round(255*a[2]).toFixed()+","+(4==a.length?a[3].toFixed(2):"1.0")+")"};e.isInsideRectangle=w;e.growBounding=function(a,b,d){b +a[2]&&(a[2]=b);da[3]&&(a[3]=d)};e.isInsideBounding=function(a,b){return a[0]b[1][0]||a[1]>b[1][1]?!1:!0};e.overlapBounding=t;e.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),d=0,g,f,c=0;6>c;c+=2)g="0123456789ABCDEF".indexOf(a.charAt(c)),f="0123456789ABCDEF".indexOf(a.charAt(c+1)),b[d]=16*g+f,d++;return b};e.num2hex=function(a){for(var b="#",d,g,f=0;3>f;f++)d=a[f]/16,g=a[f]%16,b+="0123456789ABCDEF".charAt(d)+"0123456789ABCDEF".charAt(g); return b};D.prototype.addItem=function(a,b,d){function g(a){var b=this.value;b&&b.has_submenu&&f.call(this,a)}function f(a){var b=this.value,f=!0;c.current_submenu&&c.current_submenu.close(a);if(d.callback){var g=d.callback.call(this,b,d,a,c,d.node);!0===g&&(f=!1)}if(b&&(b.callback&&!d.ignore_item_callbacks&&!0!==b.disabled&&(g=b.callback.call(this,b,d,a,c,d.extra),!0===g&&(f=!1)),b.submenu)){if(!b.submenu.options)throw"ContextMenu submenu needs options";new c.constructor(b.submenu.options,{callback:b.submenu.callback, event:a,parentMenu:c,ignore_item_callbacks:b.submenu.ignore_item_callbacks,title:b.submenu.title,extra:b.submenu.extra,autoopen:d.autoopen});f=!1}f&&!c.lock&&c.close()}var c=this;d=d||{};var e=document.createElement("div");e.className="litemenu-entry submenu";var l=!1;if(null===b)e.classList.add("separator");else{e.innerHTML=b&&b.title?b.title:a;if(e.value=b)b.disabled&&(l=!0,e.classList.add("disabled")),(b.submenu||b.has_submenu)&&e.classList.add("has_submenu");"function"==typeof b?(e.dataset.value= a,e.onclick_callback=b):e.dataset.value=b;b.className&&(e.className+=" "+b.className)}this.root.appendChild(e);l||e.addEventListener("click",f);d.autoopen&&e.addEventListener("mouseenter",g);return e};D.prototype.close=function(a,b){this.root.parentNode&&this.root.parentNode.removeChild(this.root);this.parentMenu&&!b&&(this.parentMenu.lock=!1,this.parentMenu.current_submenu=null,void 0===a?this.parentMenu.close():a&&!D.isCursorOverElement(a,this.parentMenu.root)&&D.trigger(this.parentMenu.root,"mouseleave", @@ -243,59 +243,60 @@ a.fillStyle=this.selected==g?"#FFF":this.nearest==g?"#DDD":"#AAA",a.beginPath(), if(-1!=this.selected)return!0}};A.prototype.onMouseMove=function(a,b){var d=this.points;if(d){var g=this.selected;if(!(0>g)){var f=(a[0]-this.margin)/(this.size[0]-2*this.margin),c=(a[1]-this.margin)/(this.size[1]-2*this.margin);this._nearest=this.getCloserPoint([a[0]-this.margin,a[1]-this.margin],30/b.ds.scale);var e=d[g];if(e){var l=0==g||g==d.length-1;!l&&(-10>a[0]||a[0]>this.size[0]+10||-10>a[1]||a[1]>this.size[1]+10)?(d.splice(g,1),this.selected=-1):(e[0]=l?0==g?0:1:Math.clamp(f,0,1),e[1]=1- Math.clamp(c,0,1),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(e),this.must_update=!0)}}}};A.prototype.onMouseUp=function(a,b){this.selected=-1;return!1};A.prototype.getCloserPoint=function(a,b){var d=this.points;if(!d)return-1;b=b||30;for(var g=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=d.length,e=[0,0],l=1E6,q=-1,p=0;pl||h>b||(q=p,l=h)}return q};e.CurveEditor=A;e.getParameterNames=function(a){return(a+ "").replace(/[/][/].*$/gm,"").replace(/\s+/g,"").replace(/[/][*][^/*]*[*][/]/g,"").split("){",1)[0].replace(/^[^(]*[(]/,"").replace(/=[^,]+/g,"").split(",").filter(Boolean)};Math.clamp=function(a,b,d){return b>a?b:dthis.size[0]-g.NODE_TITLE_HEIGHT&&0>b[1]){var c=this;setTimeout(function(){d.openSubgraph(c.subgraph)},10)}};m.prototype.onAction=function(a,b){this.subgraph.onAction(a,b)};m.prototype.onExecute=function(){if(this.enabled= -this.getInputOrProperty("enabled")){if(this.inputs)for(var a=0;a=e?this.trigger(null,h):this._pending.push([e,h])};k.prototype.onExecute=function(){var c=1E3* -this.graph.elapsed_time;this.isInputConnected(1)&&(this.properties.time_in_ms=this.getInputData(1));for(var h=0;hthis.size[0]-g.NODE_TITLE_HEIGHT&&0>b[1]){var c=this;setTimeout(function(){d.openSubgraph(c.subgraph)},10)}};m.prototype.onAction=function(a,b){this.subgraph.onAction(a, +b)};m.prototype.onExecute=function(){if(this.enabled=this.getInputOrProperty("enabled")){if(this.inputs)for(var a=0;a=e?this.trigger(null,h):this._pending.push([e,h])};k.prototype.onExecute=function(){var c=1E3* +this.graph.elapsed_time;this.isInputConnected(1)&&(this.properties.time_in_ms=this.getInputData(1));for(var h=0;hh[1]))return this.old_y=c.canvasY,this.captureInput(!0),this.mouse_captured=!0};n.prototype.onMouseMove=function(c){if(this.mouse_captured){var h=this.old_y-c.canvasY;c.shiftKey&&(h*=10);if(c.metaKey||c.altKey)h*=0.1;this.old_y=c.canvasY;c=this._remainder+h/n.pixels_threshold;this._remainder=c%1;c=Math.clamp(this.properties.value+ (c|0)*this.properties.step,this.properties.min,this.properties.max);this.properties.value=c;this.graph._version++;this.setDirtyCanvas(!0)}};n.prototype.onMouseUp=function(c,h){200>c.click_time&&(this.properties.value=Math.clamp(this.properties.value+(h[1]>0.5*this.size[1]?-1:1)*this.properties.step,this.properties.min,this.properties.max),this.graph._version++,this.setDirtyCanvas(!0));this.mouse_captured&&(this.mouse_captured=!1,this.captureInput(!1))};A.registerNodeType("widget/number",n);h.title= -"Combo";h.desc="Widget to select from a list";h.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};h.prototype.onPropertyChanged=function(c,h){"values"==c?(this._values=h.split(";"),this.widget.options.values=this._values):"value"==c&&(this.widget.value=h)};A.registerNodeType("widget/combo",h);v.title="Knob";v.desc="Circular controller";v.size=[80,100];v.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/ +"Combo";h.desc="Widget to select from a list";h.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};h.prototype.onPropertyChanged=function(c,h){"values"==c?(this._values=h.split(";"),this.widget.options.values=this._values):"value"==c&&(this.widget.value=h)};A.registerNodeType("widget/combo",h);x.title="Knob";x.desc="Circular controller";x.size=[80,100];x.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/ (this.properties.max-this.properties.min));var h=0.5*this.size[0],k=0.5*this.size[1],n=0.5*Math.min(this.size[0],this.size[1])-5;c.globalAlpha=1;c.save();c.translate(h,k);c.rotate(0.75*Math.PI);c.fillStyle="rgba(0,0,0,0.5)";c.beginPath();c.moveTo(0,0);c.arc(0,0,n,0,1.5*Math.PI);c.fill();c.strokeStyle="black";c.fillStyle=this.properties.color;c.lineWidth=2;c.beginPath();c.moveTo(0,0);c.arc(0,0,n-4,0,1.5*Math.PI*Math.max(0.01,this.value));c.closePath();c.fill();c.lineWidth=1;c.globalAlpha=1;c.restore(); -c.fillStyle="black";c.beginPath();c.arc(h,k,0.75*n,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":this.properties.color;c.beginPath();var m=this.value*Math.PI*1.5+0.75*Math.PI;c.arc(h+Math.cos(m)*n*0.65,k+Math.sin(m)*n*0.65,0.05*n,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":"#AAA";c.font=Math.floor(0.5*n)+"px Arial";c.textAlign="center";c.fillText(this.properties.value.toFixed(this.properties.precision),h,k+0.15*n)}};v.prototype.onExecute=function(){this.setOutputData(0, -this.properties.value);this.boxcolor=A.colorToString([this.value,this.value,this.value])};v.prototype.onMouseDown=function(c){this.center=[0.5*this.size[0],0.5*this.size[1]+20];this.radius=0.5*this.size[0];if(20>c.canvasY-this.pos[1]||A.distance([c.canvasX,c.canvasY],[this.pos[0]+this.center[0],this.pos[1]+this.center[1]])>this.radius)return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];this.captureInput(!0);return!0};v.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX- -this.pos[0],c.canvasY-this.pos[1]];var h=this.value,h=h-0.01*(c[1]-this.oldmouse[1]);1h&&(h=0);this.value=h;this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.oldmouse=c;this.setDirtyCanvas(!0)}};v.prototype.onMouseUp=function(c){this.oldmouse&&(this.oldmouse=null,this.captureInput(!1))};v.prototype.onPropertyChanged=function(c,h){if("min"==c||"max"==c||"value"==c)return this.properties[c]=parseFloat(h),!0};A.registerNodeType("widget/knob", -v);k.title="Inner Slider";k.prototype.onPropertyChanged=function(c,h){"value"==c&&(this.slider.value=h)};k.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};A.registerNodeType("widget/internal_slider",k);C.title="H.Slider";C.desc="Linear slider controller";C.prototype.onDrawForeground=function(c){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min));c.globalAlpha=1;c.lineWidth=1;c.fillStyle="#000";c.fillRect(2, +c.fillStyle="black";c.beginPath();c.arc(h,k,0.75*n,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":this.properties.color;c.beginPath();var t=this.value*Math.PI*1.5+0.75*Math.PI;c.arc(h+Math.cos(t)*n*0.65,k+Math.sin(t)*n*0.65,0.05*n,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":"#AAA";c.font=Math.floor(0.5*n)+"px Arial";c.textAlign="center";c.fillText(this.properties.value.toFixed(this.properties.precision),h,k+0.15*n)}};x.prototype.onExecute=function(){this.setOutputData(0, +this.properties.value);this.boxcolor=A.colorToString([this.value,this.value,this.value])};x.prototype.onMouseDown=function(c){this.center=[0.5*this.size[0],0.5*this.size[1]+20];this.radius=0.5*this.size[0];if(20>c.canvasY-this.pos[1]||A.distance([c.canvasX,c.canvasY],[this.pos[0]+this.center[0],this.pos[1]+this.center[1]])>this.radius)return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];this.captureInput(!0);return!0};x.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX- +this.pos[0],c.canvasY-this.pos[1]];var h=this.value,h=h-0.01*(c[1]-this.oldmouse[1]);1h&&(h=0);this.value=h;this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.oldmouse=c;this.setDirtyCanvas(!0)}};x.prototype.onMouseUp=function(c){this.oldmouse&&(this.oldmouse=null,this.captureInput(!1))};x.prototype.onPropertyChanged=function(c,h){if("min"==c||"max"==c||"value"==c)return this.properties[c]=parseFloat(h),!0};A.registerNodeType("widget/knob", +x);k.title="Inner Slider";k.prototype.onPropertyChanged=function(c,h){"value"==c&&(this.slider.value=h)};k.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};A.registerNodeType("widget/internal_slider",k);C.title="H.Slider";C.desc="Linear slider controller";C.prototype.onDrawForeground=function(c){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min));c.globalAlpha=1;c.lineWidth=1;c.fillStyle="#000";c.fillRect(2, 2,this.size[0]-4,this.size[1]-4);c.fillStyle=this.properties.color;c.beginPath();c.rect(4,4,(this.size[0]-8)*this.value,this.size[1]-8);c.fill()};C.prototype.onExecute=function(){this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.setOutputData(0,this.properties.value);this.boxcolor=A.colorToString([this.value,this.value,this.value])};C.prototype.onMouseDown=function(c){if(0>c.canvasY-this.pos[1])return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY- -this.pos[1]];this.captureInput(!0);return!0};C.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];var h=this.value,h=h+(c[0]-this.oldmouse[0])/this.size[0];1h&&(h=0);this.value=h;this.oldmouse=c;this.setDirtyCanvas(!0)}};C.prototype.onMouseUp=function(c){this.oldmouse=null;this.captureInput(!1)};C.prototype.onMouseLeave=function(c){};A.registerNodeType("widget/hslider",C);x.title="Progress";x.desc="Shows data in linear progress";x.prototype.onExecute= -function(){var c=this.getInputData(0);void 0!=c&&(this.properties.value=c)};x.prototype.onDrawForeground=function(c){c.lineWidth=1;c.fillStyle=this.properties.color;var h=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min),h=Math.min(1,h),h=Math.max(0,h);c.fillRect(2,2,(this.size[0]-4)*h,this.size[1]-4)};A.registerNodeType("widget/progress",x);w.title="Text";w.desc="Shows the input value";w.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"led_text", -text:"LED",type:"minibutton"},{name:"normal_text",text:"Normal",type:"minibutton"}];w.prototype.onDrawForeground=function(c){c.fillStyle=this.properties.color;var h=this.properties.value;this.properties.glowSize?(c.shadowColor=this.properties.color,c.shadowOffsetX=0,c.shadowOffsetY=0,c.shadowBlur=this.properties.glowSize):c.shadowColor="transparent";var k=this.properties.fontsize;c.textAlign=this.properties.align;c.font=k.toString()+"px "+this.properties.font;this.str="number"==typeof h?h.toFixed(this.properties.decimals): -h;if("string"==typeof this.str){var h=this.str.split("\\n"),n;for(n in h)c.fillText(h[n],"left"==this.properties.align?15:this.size[0]-15,-0.15*k+k*(parseInt(n)+1))}c.shadowColor="transparent";this.last_ctx=c;c.textAlign="left"};w.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(this.properties.value=c)};w.prototype.resize=function(){if(this.last_ctx){var c=this.str.split("\\n");this.last_ctx.font=this.properties.fontsize+"px "+this.properties.font;var h=0,k;for(k in c){var n=this.last_ctx.measureText(c[k]).width; -hh&&(h=0);this.value=h;this.oldmouse=c;this.setDirtyCanvas(!0)}};C.prototype.onMouseUp=function(c){this.oldmouse=null;this.captureInput(!1)};C.prototype.onMouseLeave=function(c){};A.registerNodeType("widget/hslider",C);w.title="Progress";w.desc="Shows data in linear progress";w.prototype.onExecute= +function(){var c=this.getInputData(0);void 0!=c&&(this.properties.value=c)};w.prototype.onDrawForeground=function(c){c.lineWidth=1;c.fillStyle=this.properties.color;var h=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min),h=Math.min(1,h),h=Math.max(0,h);c.fillRect(2,2,(this.size[0]-4)*h,this.size[1]-4)};A.registerNodeType("widget/progress",w);t.title="Text";t.desc="Shows the input value";t.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"led_text", +text:"LED",type:"minibutton"},{name:"normal_text",text:"Normal",type:"minibutton"}];t.prototype.onDrawForeground=function(c){c.fillStyle=this.properties.color;var h=this.properties.value;this.properties.glowSize?(c.shadowColor=this.properties.color,c.shadowOffsetX=0,c.shadowOffsetY=0,c.shadowBlur=this.properties.glowSize):c.shadowColor="transparent";var k=this.properties.fontsize;c.textAlign=this.properties.align;c.font=k.toString()+"px "+this.properties.font;this.str="number"==typeof h?h.toFixed(this.properties.decimals): +h;if("string"==typeof this.str){var h=this.str.split("\\n"),n;for(n in h)c.fillText(h[n],"left"==this.properties.align?15:this.size[0]-15,-0.15*k+k*(parseInt(n)+1))}c.shadowColor="transparent";this.last_ctx=c;c.textAlign="left"};t.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(this.properties.value=c)};t.prototype.resize=function(){if(this.last_ctx){var c=this.str.split("\\n");this.last_ctx.font=this.properties.fontsize+"px "+this.properties.font;var h=0,k;for(k in c){var n=this.last_ctx.measureText(c[k]).width; +hh?n.xbox.axes.lx:0,this._left_axis[1]=Math.abs(n.xbox.axes.ly)>h?n.xbox.axes.ly:0,this._right_axis[0]=Math.abs(n.xbox.axes.rx)>h?n.xbox.axes.rx:0,this._right_axis[1]=Math.abs(n.xbox.axes.ry)>h?n.xbox.axes.ry:0,this._triggers[0]=Math.abs(n.xbox.axes.ltrigger)>h?n.xbox.axes.ltrigger: 0,this._triggers[1]=Math.abs(n.xbox.axes.rtrigger)>h?n.xbox.axes.rtrigger:0);if(this.outputs)for(h=0;h","enum",{values:a.values});this.size=[80,60]}function b(){this.addInput("inc","number");this.addOutput("total","number");this.addProperty("increment", 1);this.addProperty("value",0)}function d(){this.addInput("v","number");this.addOutput("sin","number");this.addProperty("amplitude",1);this.addProperty("offset",0);this.bgImageUrl="nodes/imgs/icon-sin.png"}function g(){this.addInput("x","number");this.addInput("y","number");this.addOutput("","number");this.properties={x:1,y:1,formula:"x+y"};this.code_widget=this.addWidget("text","F(x,y)",this.properties.formula,function(a,b,d){d.properties.formula=a});this.addWidget("toggle","allow",E.allow_scripts, -function(a){E.allow_scripts=a});this._func=null}function f(){this.addInput("vec2","vec2");this.addOutput("x","number");this.addOutput("y","number")}function r(){this.addInputs([["x","number"],["y","number"]]);this.addOutput("vec2","vec2");this.properties={x:0,y:0};this._data=new Float32Array(2)}function t(){this.addInput("vec3","vec3");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number")}function I(){this.addInputs([["x","number"],["y","number"],["z","number"]]); -this.addOutput("vec3","vec3");this.properties={x:0,y:0,z:0};this._data=new Float32Array(3)}function J(){this.addInput("vec4","vec4");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number");this.addOutput("w","number")}function H(){this.addInputs([["x","number"],["y","number"],["z","number"],["w","number"]]);this.addOutput("vec4","vec4");this.properties={x:0,y:0,z:0,w:0};this._data=new Float32Array(4)}var E=B.LiteGraph;c.title="Converter";c.desc="type A to type B";c.prototype.onExecute= +function(a){E.allow_scripts=a});this._func=null}function f(){this.addInput("vec2","vec2");this.addOutput("x","number");this.addOutput("y","number")}function r(){this.addInputs([["x","number"],["y","number"]]);this.addOutput("vec2","vec2");this.properties={x:0,y:0};this._data=new Float32Array(2)}function s(){this.addInput("vec3","vec3");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number")}function J(){this.addInputs([["x","number"],["y","number"],["z","number"]]); +this.addOutput("vec3","vec3");this.properties={x:0,y:0,z:0};this._data=new Float32Array(3)}function K(){this.addInput("vec4","vec4");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number");this.addOutput("w","number")}function I(){this.addInputs([["x","number"],["y","number"],["z","number"],["w","number"]]);this.addOutput("vec4","vec4");this.properties={x:0,y:0,z:0,w:0};this._data=new Float32Array(4)}var E=z.LiteGraph;c.title="Converter";c.desc="type A to type B";c.prototype.onExecute= function(){var a=this.getInputData(0);if(null!=a&&this.outputs)for(var b=0;ba&&(a+=1024);var c=Math.floor(a);a-=c;d=k.data[c];c=k.data[1023==c?0:c+1];b&&(a=a*a*a*(a*(6*a-15)+10));return d*(1-a)+c*a};k.prototype.onExecute=function(){for(var a=this.getInputData(0)||0,b=this.properties.octaves||1,d=0,c=1,a=a+(this.properties.seed||0),g=this.properties.speed||1,f=0,l=0;lc);++l);a=this.properties.min;this._last_v=d/f*(this.properties.max- a)+a;this.setOutputData(0,this._last_v)};k.prototype.onDrawBackground=function(a){this.outputs[0].label=(this._last_v||0).toFixed(3)};E.registerNodeType("math/noise",k);C.title="Spikes";C.desc="spike every random time";C.prototype.onExecute=function(){var a=this.graph.elapsed_time;this._remaining_time-=a;this._blink_time-=a;a=0;0this._remaining_time?(this._remaining_time=Math.random()*(this.properties.max_time- -this.properties.min_time)+this.properties.min_time,this._blink_time=this.properties.duration,this.boxcolor="#FFF"):this.boxcolor="#000";this.setOutputData(0,a)};E.registerNodeType("math/spikes",C);x.title="Clamp";x.desc="Clamp number between min and max";x.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(a=Math.max(this.properties.min,a),a=Math.min(this.properties.max,a),this.setOutputData(0,a))};x.prototype.getCode=function(a){a="";this.isInputConnected(0)&&(a+="clamp({{0}},"+ -this.properties.min+","+this.properties.max+")");return a};E.registerNodeType("math/clamp",x);w.title="Lerp";w.desc="Linear Interpolation";w.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.getInputData(1);null==b&&(b=0);var d=this.properties.f,c=this.getInputData(2);void 0!==c&&(d=c);this.setOutputData(0,a*(1-d)+b*d)};w.prototype.onGetInputs=function(){return[["f","number"]]};E.registerNodeType("math/lerp",w);D.title="Abs";D.desc="Absolute";D.prototype.onExecute= -function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.abs(a))};E.registerNodeType("math/abs",D);A.title="Floor";A.desc="Floor number to remove fractional part";A.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.floor(a))};E.registerNodeType("math/floor",A);e.title="Frac";e.desc="Returns fractional part";e.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a%1)};E.registerNodeType("math/frac",e);z.title= -"Smoothstep";z.desc="Smoothstep";z.prototype.onExecute=function(){var a=this.getInputData(0);if(void 0!==a){var b=this.properties.A,a=Math.clamp((a-b)/(this.properties.B-b),0,1);this.setOutputData(0,a*a*(3-2*a))}};E.registerNodeType("math/smoothstep",z);y.title="Scale";y.desc="v * factor";y.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a*this.properties.factor)};E.registerNodeType("math/scale",y);u.title="Gate";u.desc="if v is true, then outputs A, otherwise B"; -u.prototype.onExecute=function(){var a=this.getInputData(0);this.setOutputData(0,this.getInputData(a?1:2))};E.registerNodeType("math/gate",u);G.title="Average";G.desc="Average Filter";G.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this._values.length;this._values[this._current%b]=a;this._current+=1;this._current>b&&(this._current=0);for(var d=a=0;db&&(b=1);this.properties.samples= -Math.round(b);var d=this._values;this._values=new Float32Array(this.properties.samples);d.length<=this._values.length?this._values.set(d):this._values.set(d.subarray(0,this._values.length))};E.registerNodeType("math/average",G);q.title="TendTo";q.desc="moves the output value always closer to the input";q.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.factor;this._value=null==this._value?a:this._value*(1-b)+a*b;this.setOutputData(0,this._value)};E.registerNodeType("math/tendTo", +this.properties.min_time)+this.properties.min_time,this._blink_time=this.properties.duration,this.boxcolor="#FFF"):this.boxcolor="#000";this.setOutputData(0,a)};E.registerNodeType("math/spikes",C);w.title="Clamp";w.desc="Clamp number between min and max";w.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(a=Math.max(this.properties.min,a),a=Math.min(this.properties.max,a),this.setOutputData(0,a))};w.prototype.getCode=function(a){a="";this.isInputConnected(0)&&(a+="clamp({{0}},"+ +this.properties.min+","+this.properties.max+")");return a};E.registerNodeType("math/clamp",w);t.title="Lerp";t.desc="Linear Interpolation";t.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.getInputData(1);null==b&&(b=0);var d=this.properties.f,c=this.getInputData(2);void 0!==c&&(d=c);this.setOutputData(0,a*(1-d)+b*d)};t.prototype.onGetInputs=function(){return[["f","number"]]};E.registerNodeType("math/lerp",t);D.title="Abs";D.desc="Absolute";D.prototype.onExecute= +function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.abs(a))};E.registerNodeType("math/abs",D);A.title="Floor";A.desc="Floor number to remove fractional part";A.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.floor(a))};E.registerNodeType("math/floor",A);e.title="Frac";e.desc="Returns fractional part";e.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a%1)};E.registerNodeType("math/frac",e);y.title= +"Smoothstep";y.desc="Smoothstep";y.prototype.onExecute=function(){var a=this.getInputData(0);if(void 0!==a){var b=this.properties.A,a=Math.clamp((a-b)/(this.properties.B-b),0,1);this.setOutputData(0,a*a*(3-2*a))}};E.registerNodeType("math/smoothstep",y);B.title="Scale";B.desc="v * factor";B.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a*this.properties.factor)};E.registerNodeType("math/scale",B);u.title="Gate";u.desc="if v is true, then outputs A, otherwise B"; +u.prototype.onExecute=function(){var a=this.getInputData(0);this.setOutputData(0,this.getInputData(a?1:2))};E.registerNodeType("math/gate",u);F.title="Average";F.desc="Average Filter";F.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this._values.length;this._values[this._current%b]=a;this._current+=1;this._current>b&&(this._current=0);for(var d=a=0;db&&(b=1);this.properties.samples= +Math.round(b);var d=this._values;this._values=new Float32Array(this.properties.samples);d.length<=this._values.length?this._values.set(d):this._values.set(d.subarray(0,this._values.length))};E.registerNodeType("math/average",F);q.title="TendTo";q.desc="moves the output value always closer to the input";q.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.factor;this._value=null==this._value?a:this._value*(1-b)+a*b;this.setOutputData(0,this._value)};E.registerNodeType("math/tendTo", q);p.values="+ - * / % ^ max min".split(" ");p.title="Operation";p.desc="Easy math operators";p["@OP"]={type:"enum",title:"operation",values:p.values};p.size=[100,60];p.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"};p.prototype.setValue=function(a){"string"==typeof a&&(a=parseFloat(a));this.properties.value=a};p.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);null!=a? this.properties.A=a:a=this.properties.A;null!=b?this.properties.B=b:b=this.properties.B;var d=0;switch(this.properties.OP){case "+":d=a+b;break;case "-":d=a-b;break;case "x":case "X":case "*":d=a*b;break;case "/":d=a/b;break;case "%":d=a%b;break;case "^":d=Math.pow(a,b);break;case "max":d=Math.max(a,b);break;case "min":d=Math.min(a,b);break;default:console.warn("Unknown operation: "+this.properties.OP)}this.setOutputData(0,d)};p.prototype.onDrawBackground=function(a){this.flags.collapsed||(a.font= "40px Arial",a.fillStyle="#666",a.textAlign="center",a.fillText(this.properties.OP,0.5*this.size[0],0.5*(this.size[1]+E.NODE_TITLE_HEIGHT)),a.textAlign="left")};E.registerNodeType("math/operation",p);E.registerSearchboxExtra("math/operation","MAX",{properties:{OP:"max"},title:"MAX()"});E.registerSearchboxExtra("math/operation","MIN",{properties:{OP:"min"},title:"MIN()"});l.title="Compare";l.desc="compares between two values";l.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1); @@ -359,39 +360,42 @@ E.registerSearchboxExtra("math/compare","<=",{outputs:[["A<=B","boolean"]],title b;var d=!0;switch(this.properties.OP){case ">":d=a>b;break;case "<":d=a=":d=a>=b;break;case "||":d=a||b;break;case "&&":d=a&&b}this.setOutputData(0,d);this.setOutputData(1,!d)};E.registerNodeType("math/condition",a);b.title="Accumulate";b.desc="Increments a value every time";b.prototype.onExecute=function(){null===this.properties.value&&(this.properties.value=0);var a=this.getInputData(0);this.properties.value=null!== a?this.properties.value+a:this.properties.value+this.properties.increment;this.setOutputData(0,this.properties.value)};E.registerNodeType("math/accumulate",b);d.title="Trigonometry";d.desc="Sin Cos Tan";d.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.amplitude,d=this.findInputSlot("amplitude");-1!=d&&(b=this.getInputData(d));var c=this.properties.offset,d=this.findInputSlot("offset");-1!=d&&(c=this.getInputData(d));for(var d=0,g=this.outputs.length;d< g;++d){var f;switch(this.outputs[d].name){case "sin":f=Math.sin(a);break;case "cos":f=Math.cos(a);break;case "tan":f=Math.tan(a);break;case "asin":f=Math.asin(a);break;case "acos":f=Math.acos(a);break;case "atan":f=Math.atan(a)}this.setOutputData(d,b*f+c)}};d.prototype.onGetInputs=function(){return[["v","number"],["amplitude","number"],["offset","number"]]};d.prototype.onGetOutputs=function(){return[["sin","number"],["cos","number"],["tan","number"],["asin","number"],["acos","number"],["atan","number"]]}; -E.registerNodeType("math/trigonometry",d);E.registerSearchboxExtra("math/trigonometry","SIN()",{outputs:[["sin","number"]],title:"SIN()"});E.registerSearchboxExtra("math/trigonometry","COS()",{outputs:[["cos","number"]],title:"COS()"});E.registerSearchboxExtra("math/trigonometry","TAN()",{outputs:[["tan","number"]],title:"TAN()"});g.title="Formula";g.desc="Compute formula";g.size=[160,100];G.prototype.onPropertyChanged=function(a,b){"formula"==a&&(this.code_widget.value=b)};g.prototype.onExecute= +E.registerNodeType("math/trigonometry",d);E.registerSearchboxExtra("math/trigonometry","SIN()",{outputs:[["sin","number"]],title:"SIN()"});E.registerSearchboxExtra("math/trigonometry","COS()",{outputs:[["cos","number"]],title:"COS()"});E.registerSearchboxExtra("math/trigonometry","TAN()",{outputs:[["tan","number"]],title:"TAN()"});g.title="Formula";g.desc="Compute formula";g.size=[160,100];F.prototype.onPropertyChanged=function(a,b){"formula"==a&&(this.code_widget.value=b)};g.prototype.onExecute= function(){if(E.allow_scripts){var a=this.getInputData(0),b=this.getInputData(1);null!=a?this.properties.x=a:a=this.properties.x;null!=b?this.properties.y=b:b=this.properties.y;var d;try{this._func&&this._func_code==this.properties.formula||(this._func=new Function("x","y","TIME","return "+this.properties.formula),this._func_code=this.properties.formula),d=this._func(a,b,this.graph.globaltime),this.boxcolor=null}catch(c){this.boxcolor="red"}this.setOutputData(0,d)}};g.prototype.getTitle=function(){return this._func_code|| "Formula"};g.prototype.onDrawBackground=function(){var a=this.properties.formula;this.outputs&&this.outputs.length&&(this.outputs[0].label=a)};E.registerNodeType("math/formula",g);f.title="Vec2->XY";f.desc="vector 2 to components";f.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]))};E.registerNodeType("math3d/vec2-to-xy",f);r.title="XY->Vec2";r.desc="components to vector2";r.prototype.onExecute=function(){var a=this.getInputData(0); -null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this._data;d[0]=a;d[1]=b;this.setOutputData(0,d)};E.registerNodeType("math3d/xy-to-vec2",r);t.title="Vec3->XYZ";t.desc="vector 3 to components";t.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]))};E.registerNodeType("math3d/vec3-to-xyz",t);I.title="XYZ->Vec3";I.desc="components to vector3";I.prototype.onExecute= -function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this._data;c[0]=a;c[1]=b;c[2]=d;this.setOutputData(0,c)};E.registerNodeType("math3d/xyz-to-vec3",I);J.title="Vec4->XYZW";J.desc="vector 4 to components";J.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]),this.setOutputData(3, -a[3]))};E.registerNodeType("math3d/vec4-to-xyzw",J);H.title="XYZW->Vec4";H.desc="components to vector4";H.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this.getInputData(3);null==c&&(c=this.properties.w);var g=this._data;g[0]=a;g[1]=b;g[2]=d;g[3]=c;this.setOutputData(0,g)};E.registerNodeType("math3d/xyzw-to-vec4",H)})(this); -(function(B){function c(){this.addInput("T","vec3");this.addInput("R","vec3");this.addInput("S","vec3");this.addOutput("mat4","mat4");this.properties={T:[0,0,0],R:[0,0,0],S:[1,1,1],R_in_degrees:!0};this._result=mat4.create();this._must_update=!0}function m(){this.addInput("A","number,vec3");this.addInput("B","number,vec3");this.addOutput("=","vec3");this.addProperty("OP","+","enum",{values:m.values});this._result=vec3.create()}function n(){this.addInput("in","vec3");this.addInput("f","number");this.addOutput("out", -"vec3");this.properties={f:1};this._data=new Float32Array(3)}function h(){this.addInput("in","vec3");this.addOutput("out","number")}function v(){this.addInput("in","vec3");this.addOutput("out","vec3");this._data=new Float32Array(3)}function k(){this.addInput("A","vec3");this.addInput("B","vec3");this.addInput("f","vec3");this.addOutput("out","vec3");this.properties={f:0.5};this._data=new Float32Array(3)}function C(){this.addInput("A","vec3");this.addInput("B","vec3");this.addOutput("out","number")} -var x=B.LiteGraph;c.title="mat4";c.temp_quat=new Float32Array([0,0,0,1]);c.temp_mat4=new Float32Array(16);c.temp_vec3=new Float32Array(3);c.prototype.onPropertyChanged=function(c,h){this._must_update=!0};c.prototype.onExecute=function(){var h=this._result,k=c.temp_quat,n=c.temp_mat4,e=c.temp_vec3,m=this.getInputData(0),y=this.getInputData(1),u=this.getInputData(2);if(this._must_update||m||y||u)m=m||this.properties.T,y=y||this.properties.R,u=u||this.properties.S,mat4.identity(h),mat4.translate(h,h, -m),this.properties.R_in_degrees?(e.set(y),vec3.scale(e,e,DEG2RAD),quat.fromEuler(k,e)):quat.fromEuler(k,y),mat4.fromQuat(n,k),mat4.multiply(h,h,n),mat4.scale(h,h,u);this.setOutputData(0,h)};x.registerNodeType("math3d/mat4",c);m.values="+ - * / % ^ max min".split(" ");m.title="Operation";m.desc="Easy math 3D operators";m["@OP"]={type:"enum",title:"operation",values:m.values};m.size=[100,60];m.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+ -"(A,B)":"A "+this.properties.OP+" B"};m.prototype.onExecute=function(){var c=this.getInputData(0),h=this.getInputData(1);if(null!=c&&null!=h){c.constructor===Number&&(c=[c,c,c]);h.constructor===Number&&(h=[h,h,h]);var k=this._result;switch(this.properties.OP){case "+":k=vec3.add(k,c,h);break;case "-":k=vec3.sub(k,c,h);break;case "x":case "X":case "*":k=vec3.mul(k,c,h);break;case "/":k=vec3.div(k,c,h);break;case "%":k[0]=c[0]%h[0];k[1]=c[1]%h[1];k[2]=c[2]%h[2];break;case "^":k[0]=Math.pow(c[0],h[0]); -k[1]=Math.pow(c[1],h[1]);k[2]=Math.pow(c[2],h[2]);break;case "max":k[0]=Math.max(c[0],h[0]);k[1]=Math.max(c[1],h[1]);k[2]=Math.max(c[2],h[2]);break;case "min":k[0]=Math.min(c[0],h[0]);k[1]=Math.min(c[1],h[1]);k[2]=Math.min(c[2],h[2]);break;default:console.warn("Unknown operation: "+this.properties.OP)}this.setOutputData(0,k)}};m.prototype.onDrawBackground=function(c){this.flags.collapsed||(c.font="40px Arial",c.fillStyle="#666",c.textAlign="center",c.fillText(this.properties.OP,0.5*this.size[0],0.5* -(this.size[1]+x.NODE_TITLE_HEIGHT)),c.textAlign="left")};x.registerNodeType("math3d/operation",m);n.title="vec3_scale";n.desc="scales the components of a vec3";n.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);null==h&&(h=this.properties.f);var k=this._data;k[0]=c[0]*h;k[1]=c[1]*h;k[2]=c[2]*h;this.setOutputData(0,k)}};x.registerNodeType("math3d/vec3-scale",n);h.title="vec3_length";h.desc="returns the module of a vector";h.prototype.onExecute=function(){var c= -this.getInputData(0);null!=c&&(c=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),this.setOutputData(0,c))};x.registerNodeType("math3d/vec3-length",h);v.title="vec3_normalize";v.desc="returns the vector normalized";v.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),k=this._data;k[0]=c[0]/h;k[1]=c[1]/h;k[2]=c[2]/h;this.setOutputData(0,k)}};x.registerNodeType("math3d/vec3-normalize",v);k.title="vec3_lerp";k.desc="returns the interpolated vector"; -k.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);if(null!=h){var k=this.getInputOrProperty("f"),e=this._data;e[0]=c[0]*(1-k)+h[0]*k;e[1]=c[1]*(1-k)+h[1]*k;e[2]=c[2]*(1-k)+h[2]*k;this.setOutputData(0,e)}}};x.registerNodeType("math3d/vec3-lerp",k);C.title="vec3_dot";C.desc="returns the dot product";C.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);null!=h&&this.setOutputData(0,c[0]*h[0]+c[1]*h[1]+c[2]* -h[2])}};x.registerNodeType("math3d/vec3-dot",C);B.glMatrix?(B=function(){this.addOutput("quat","quat");this.properties={x:0,y:0,z:0,w:1,normalize:!1};this._value=quat.create()},B.title="Quaternion",B.desc="quaternion",B.prototype.onExecute=function(){this._value[0]=this.getInputOrProperty("x");this._value[1]=this.getInputOrProperty("y");this._value[2]=this.getInputOrProperty("z");this._value[3]=this.getInputOrProperty("w");this.properties.normalize&&quat.normalize(this._value,this._value);this.setOutputData(0, -this._value)},B.prototype.onGetInputs=function(){return[["x","number"],["y","number"],["z","number"],["w","number"]]},x.registerNodeType("math3d/quaternion",B),B=function(){this.addInputs([["degrees","number"],["axis","vec3"]]);this.addOutput("quat","quat");this.properties={angle:90,axis:vec3.fromValues(0,1,0)};this._value=quat.create()},B.title="Rotation",B.desc="quaternion rotation",B.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.angle);var h=this.getInputData(1); -null==h&&(h=this.properties.axis);c=quat.setAxisAngle(this._value,h,0.0174532925*c);this.setOutputData(0,c)},x.registerNodeType("math3d/rotation",B),B=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},B.title="Rot. Vec3",B.desc="rotate a point",B.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.vec);var h=this.getInputData(1);null==h?this.setOutputData(c):this.setOutputData(0,vec3.transformQuat(vec3.create(), -c,h))},x.registerNodeType("math3d/rotate_vec3",B),B=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},B.title="Mult. Quat",B.desc="rotate quaternion",B.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);null!=h&&(c=quat.multiply(this._value,c,h),this.setOutputData(0,c))}},x.registerNodeType("math3d/mult-quat",B),B=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp", -"quat");this.addProperty("factor",0.5);this._value=quat.create()},B.title="Quat Slerp",B.desc="quaternion spherical interpolation",B.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);if(null!=h){var k=this.properties.factor;null!=this.getInputData(2)&&(k=this.getInputData(2));c=quat.slerp(this._value,c,h,k);this.setOutputData(0,c)}}},x.registerNodeType("math3d/quat-slerp",B),B=function(){this.addInput("vec3","vec3");this.addOutput("remap","vec3");this.addOutput("clamped", -"vec3");this.properties={clamp:!0,range_min:[-1,-1,0],range_max:[1,1,0],target_min:[-1,-1,0],target_max:[1,1,0]};this._value=vec3.create();this._clamped=vec3.create()},B.title="Remap Range",B.desc="remap a 3D range",B.prototype.onExecute=function(){var c=this.getInputData(0);c&&this._value.set(c);for(var c=this.properties.range_min,h=this.properties.range_max,k=this.properties.target_min,e=this.properties.target_max,n=0;3>n;++n){var m=h[n]-c[n];this._clamped[n]=Math.clamp(this._value[n],c[n],h[n]); -0==m?this._value[n]=0.5*(k[n]+e[n]):(m=(this._value[n]-c[n])/m,this.properties.clamp&&(m=Math.clamp(m,0,1)),this._value[n]=k[n]+m*(e[n]-k[n]))}this.setOutputData(0,this._value);this.setOutputData(1,this._clamped)},x.registerNodeType("math3d/remap_range",B)):console.warn("No glmatrix found, some Math3D nodes may not work")})(this); -(function(B){function c(c,h){return c==h}function m(){this.addInput("","string");this.addOutput("table","table");this.addOutput("rows","number");this.addProperty("value","");this.addProperty("separator",",");this._table=null}B=B.LiteGraph;B.wrapFunctionAsNode("string/toString",c,["*"],"String");B.wrapFunctionAsNode("string/compare",c,["string","string"],"boolean");B.wrapFunctionAsNode("string/concatenate",function(c,h){return void 0===c?h:void 0===h?c:c+h},["string","string"],"string");B.wrapFunctionAsNode("string/contains", -function(c,h){return void 0===c||void 0===h?!1:-1!=c.indexOf(h)},["string","string"],"boolean");B.wrapFunctionAsNode("string/toUpperCase",function(c){return null!=c&&c.constructor===String?c.toUpperCase():c},["string"],"string");B.wrapFunctionAsNode("string/split",function(c,h){null==h&&(h=this.properties.separator);if(null==c)return[];if(c.constructor===String)return c.split(h||" ");if(c.constructor===Array){for(var m=[],k=0;kXYZ";s.desc="vector 3 to components";s.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]))};E.registerNodeType("math3d/vec3-to-xyz",s);J.title="XYZ->Vec3";J.desc="components to vector3";J.prototype.onExecute= +function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this._data;c[0]=a;c[1]=b;c[2]=d;this.setOutputData(0,c)};E.registerNodeType("math3d/xyz-to-vec3",J);K.title="Vec4->XYZW";K.desc="vector 4 to components";K.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]),this.setOutputData(3, +a[3]))};E.registerNodeType("math3d/vec4-to-xyzw",K);I.title="XYZW->Vec4";I.desc="components to vector4";I.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this.getInputData(3);null==c&&(c=this.properties.w);var g=this._data;g[0]=a;g[1]=b;g[2]=d;g[3]=c;this.setOutputData(0,g)};E.registerNodeType("math3d/xyzw-to-vec4",I)})(this); +(function(z){function c(){this.addInput("T","vec3");this.addInput("R","vec3");this.addInput("S","vec3");this.addOutput("mat4","mat4");this.properties={T:[0,0,0],R:[0,0,0],S:[1,1,1],R_in_degrees:!0};this._result=mat4.create();this._must_update=!0}function m(){this.addInput("A","number,vec3");this.addInput("B","number,vec3");this.addOutput("=","number,vec3");this.addProperty("OP","+","enum",{values:m.values});this._result=vec3.create()}function n(){this.addInput("in","vec3");this.addInput("f","number"); +this.addOutput("out","vec3");this.properties={f:1};this._data=new Float32Array(3)}function h(){this.addInput("in","vec3");this.addOutput("out","number")}function x(){this.addInput("in","vec3");this.addOutput("out","vec3");this._data=new Float32Array(3)}function k(){this.addInput("A","vec3");this.addInput("B","vec3");this.addInput("f","vec3");this.addOutput("out","vec3");this.properties={f:0.5};this._data=new Float32Array(3)}function C(){this.addInput("A","vec3");this.addInput("B","vec3");this.addOutput("out", +"number")}var w=z.LiteGraph;c.title="mat4";c.temp_quat=new Float32Array([0,0,0,1]);c.temp_mat4=new Float32Array(16);c.temp_vec3=new Float32Array(3);c.prototype.onPropertyChanged=function(c,h){this._must_update=!0};c.prototype.onExecute=function(){var h=this._result,k=c.temp_quat,n=c.temp_mat4,e=c.temp_vec3,m=this.getInputData(0),B=this.getInputData(1),u=this.getInputData(2);if(this._must_update||m||B||u)m=m||this.properties.T,B=B||this.properties.R,u=u||this.properties.S,mat4.identity(h),mat4.translate(h, +h,m),this.properties.R_in_degrees?(e.set(B),vec3.scale(e,e,DEG2RAD),quat.fromEuler(k,e)):quat.fromEuler(k,B),mat4.fromQuat(n,k),mat4.multiply(h,h,n),mat4.scale(h,h,u);this.setOutputData(0,h)};w.registerNodeType("math3d/mat4",c);m.values="+ - * / % ^ max min dot cross".split(" ");w.registerSearchboxExtra("math3d/operation","CROSS()",{properties:{OP:"cross"},title:"CROSS()"});w.registerSearchboxExtra("math3d/operation","DOT()",{properties:{OP:"dot"},title:"DOT()"});m.title="Operation";m.desc="Easy math 3D operators"; +m["@OP"]={type:"enum",title:"operation",values:m.values};m.size=[100,60];m.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"};m.prototype.onExecute=function(){var c=this.getInputData(0),h=this.getInputData(1);if(null!=c&&null!=h){c.constructor===Number&&(c=[c,c,c]);h.constructor===Number&&(h=[h,h,h]);var k=this._result;switch(this.properties.OP){case "+":k=vec3.add(k,c,h);break;case "-":k=vec3.sub(k,c,h); +break;case "x":case "X":case "*":k=vec3.mul(k,c,h);break;case "/":k=vec3.div(k,c,h);break;case "%":k[0]=c[0]%h[0];k[1]=c[1]%h[1];k[2]=c[2]%h[2];break;case "^":k[0]=Math.pow(c[0],h[0]);k[1]=Math.pow(c[1],h[1]);k[2]=Math.pow(c[2],h[2]);break;case "max":k[0]=Math.max(c[0],h[0]);k[1]=Math.max(c[1],h[1]);k[2]=Math.max(c[2],h[2]);break;case "min":k[0]=Math.min(c[0],h[0]),k[1]=Math.min(c[1],h[1]),k[2]=Math.min(c[2],h[2]);case "dot":k=vec3.dot(c,h);break;case "cross":vec3.cross(k,c,h);break;default:console.warn("Unknown operation: "+ +this.properties.OP)}this.setOutputData(0,k)}};m.prototype.onDrawBackground=function(c){this.flags.collapsed||(c.font="40px Arial",c.fillStyle="#666",c.textAlign="center",c.fillText(this.properties.OP,0.5*this.size[0],0.5*(this.size[1]+w.NODE_TITLE_HEIGHT)),c.textAlign="left")};w.registerNodeType("math3d/operation",m);n.title="vec3_scale";n.desc="scales the components of a vec3";n.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);null==h&&(h=this.properties.f); +var k=this._data;k[0]=c[0]*h;k[1]=c[1]*h;k[2]=c[2]*h;this.setOutputData(0,k)}};w.registerNodeType("math3d/vec3-scale",n);h.title="vec3_length";h.desc="returns the module of a vector";h.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(c=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),this.setOutputData(0,c))};w.registerNodeType("math3d/vec3-length",h);x.title="vec3_normalize";x.desc="returns the vector normalized";x.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h= +Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),k=this._data;k[0]=c[0]/h;k[1]=c[1]/h;k[2]=c[2]/h;this.setOutputData(0,k)}};w.registerNodeType("math3d/vec3-normalize",x);k.title="vec3_lerp";k.desc="returns the interpolated vector";k.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);if(null!=h){var k=this.getInputOrProperty("f"),e=this._data;e[0]=c[0]*(1-k)+h[0]*k;e[1]=c[1]*(1-k)+h[1]*k;e[2]=c[2]*(1-k)+h[2]*k;this.setOutputData(0,e)}}};w.registerNodeType("math3d/vec3-lerp", +k);C.title="vec3_dot";C.desc="returns the dot product";C.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);null!=h&&this.setOutputData(0,c[0]*h[0]+c[1]*h[1]+c[2]*h[2])}};w.registerNodeType("math3d/vec3-dot",C);z.glMatrix?(z=function(){this.addOutput("quat","quat");this.properties={x:0,y:0,z:0,w:1,normalize:!1};this._value=quat.create()},z.title="Quaternion",z.desc="quaternion",z.prototype.onExecute=function(){this._value[0]=this.getInputOrProperty("x"); +this._value[1]=this.getInputOrProperty("y");this._value[2]=this.getInputOrProperty("z");this._value[3]=this.getInputOrProperty("w");this.properties.normalize&&quat.normalize(this._value,this._value);this.setOutputData(0,this._value)},z.prototype.onGetInputs=function(){return[["x","number"],["y","number"],["z","number"],["w","number"]]},w.registerNodeType("math3d/quaternion",z),z=function(){this.addInputs([["degrees","number"],["axis","vec3"]]);this.addOutput("quat","quat");this.properties={angle:90, +axis:vec3.fromValues(0,1,0)};this._value=quat.create()},z.title="Rotation",z.desc="quaternion rotation",z.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.angle);var h=this.getInputData(1);null==h&&(h=this.properties.axis);c=quat.setAxisAngle(this._value,h,0.0174532925*c);this.setOutputData(0,c)},w.registerNodeType("math3d/rotation",z),z=function(){this.addInput("euler","vec3");this.addOutput("quat","quat");this.properties={euler:[0,0,0],use_yaw_pitch_roll:!1}; +this._degs=vec3.create();this._value=quat.create()},z.title="Euler->Quat",z.desc="Converts euler angles (in degrees) to quaternion",z.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.euler);vec3.scale(this._degs,c,DEG2RAD);this.properties.use_yaw_pitch_roll&&(this._degs=[this._degs[2],this._degs[0],this._degs[1]]);c=quat.fromEuler(this._value,this._degs);this.setOutputData(0,c)},w.registerNodeType("math3d/euler_to_quat",z),z=function(){this.addInput(["quat","quat"]); +this.addOutput("euler","vec3");this._value=vec3.create()},z.title="Euler->Quat",z.desc="Converts rotX,rotY,rotZ in degrees to quat",z.prototype.onExecute=function(){var c=this.getInputData(0);c&&(quat.toEuler(this._value,c),vec3.scale(this._value,this._value,DEG2RAD),this.setOutputData(0,this._value))},w.registerNodeType("math3d/quat_to_euler",z),z=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},z.title="Rot. Vec3",z.desc= +"rotate a point",z.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.vec);var h=this.getInputData(1);null==h?this.setOutputData(c):this.setOutputData(0,vec3.transformQuat(vec3.create(),c,h))},w.registerNodeType("math3d/rotate_vec3",z),z=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},z.title="Mult. Quat",z.desc="rotate quaternion",z.prototype.onExecute=function(){var c=this.getInputData(0);if(null!= +c){var h=this.getInputData(1);null!=h&&(c=quat.multiply(this._value,c,h),this.setOutputData(0,c))}},w.registerNodeType("math3d/mult-quat",z),z=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp","quat");this.addProperty("factor",0.5);this._value=quat.create()},z.title="Quat Slerp",z.desc="quaternion spherical interpolation",z.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);if(null!=h){var k=this.properties.factor; +null!=this.getInputData(2)&&(k=this.getInputData(2));c=quat.slerp(this._value,c,h,k);this.setOutputData(0,c)}}},w.registerNodeType("math3d/quat-slerp",z),z=function(){this.addInput("vec3","vec3");this.addOutput("remap","vec3");this.addOutput("clamped","vec3");this.properties={clamp:!0,range_min:[-1,-1,0],range_max:[1,1,0],target_min:[-1,-1,0],target_max:[1,1,0]};this._value=vec3.create();this._clamped=vec3.create()},z.title="Remap Range",z.desc="remap a 3D range",z.prototype.onExecute=function(){var c= +this.getInputData(0);c&&this._value.set(c);for(var c=this.properties.range_min,h=this.properties.range_max,k=this.properties.target_min,e=this.properties.target_max,n=0;3>n;++n){var m=h[n]-c[n];this._clamped[n]=Math.clamp(this._value[n],c[n],h[n]);0==m?this._value[n]=0.5*(k[n]+e[n]):(m=(this._value[n]-c[n])/m,this.properties.clamp&&(m=Math.clamp(m,0,1)),this._value[n]=k[n]+m*(e[n]-k[n]))}this.setOutputData(0,this._value);this.setOutputData(1,this._clamped)},w.registerNodeType("math3d/remap_range", +z)):console.warn("No glmatrix found, some Math3D nodes may not work")})(this); +(function(z){function c(c,h){return c==h}function m(){this.addInput("","string");this.addOutput("table","table");this.addOutput("rows","number");this.addProperty("value","");this.addProperty("separator",",");this._table=null}z=z.LiteGraph;z.wrapFunctionAsNode("string/toString",c,[""],"String");z.wrapFunctionAsNode("string/compare",c,["string","string"],"boolean");z.wrapFunctionAsNode("string/concatenate",function(c,h){return void 0===c?h:void 0===h?c:c+h},["string","string"],"string");z.wrapFunctionAsNode("string/contains", +function(c,h){return void 0===c||void 0===h?!1:-1!=c.indexOf(h)},["string","string"],"boolean");z.wrapFunctionAsNode("string/toUpperCase",function(c){return null!=c&&c.constructor===String?c.toUpperCase():c},["string"],"string");z.wrapFunctionAsNode("string/split",function(c,h){null==h&&(h=this.properties.separator);if(null==c)return[];if(c.constructor===String)return c.split(h||" ");if(c.constructor===Array){for(var m=[],k=0;ke;++e){var h=this.getInputData(e);if(null!=h){var k=this.values[e];k.push(h);k.length>c[0]&&k.shift()}}}};c.prototype.onDrawBackground=function(e){if(!this.flags.collapsed){var h=this.size,k=0.5*h[1]/ +(function(z){function c(){this.addInput("A","Number");this.addInput("B","Number");this.addInput("C","Number");this.addInput("D","Number");this.values=[[],[],[],[]];this.properties={scale:2}}function m(){this.addOutput("frame","image");this.properties={url:""}}function n(){this.addInput("f","number");this.addOutput("Color","color");this.properties={colorA:"#444444",colorB:"#44AAFF",colorC:"#44FFAA",colorD:"#FFFFFF"}}function h(){this.addInput("","image,canvas");this.size=[200,200]}function x(){this.addInputs([["img1", +"image"],["img2","image"],["fade","number"]]);this.addOutput("","image");this.properties={fade:0.5,width:512,height:512}}function k(){this.addInput("","image");this.addOutput("","image");this.properties={width:256,height:256,x:0,y:0,scale:1};this.size=[50,20]}function C(){this.addInput("clear",e.ACTION);this.addOutput("","canvas");this.properties={width:512,height:512,autoclear:!0};this.canvas=document.createElement("canvas");this.ctx=this.canvas.getContext("2d")}function w(){this.addInput("canvas", +"canvas");this.addInput("img","image,canvas");this.addInput("x","number");this.addInput("y","number");this.properties={x:0,y:0,opacity:1}}function t(){this.addInput("canvas","canvas");this.addInput("x","number");this.addInput("y","number");this.addInput("w","number");this.addInput("h","number");this.properties={x:0,y:0,w:10,h:10,color:"white",opacity:1}}function D(){this.addInput("t","number");this.addOutputs([["frame","image"],["t","number"],["d","number"]]);this.properties={url:"",use_proxy:!0}} +function A(){this.addOutput("Webcam","image");this.properties={facingMode:"user"};this.boxcolor="black";this.frame=0}var e=z.LiteGraph;c.title="Plot";c.desc="Plots data over time";c.colors=["#FFF","#F99","#9F9","#99F"];c.prototype.onExecute=function(c){if(!this.flags.collapsed){c=this.size;for(var e=0;4>e;++e){var h=this.getInputData(e);if(null!=h){var k=this.values[e];k.push(h);k.length>c[0]&&k.shift()}}}};c.prototype.onDrawBackground=function(e){if(!this.flags.collapsed){var h=this.size,k=0.5*h[1]/ this.properties.scale,n=c.colors,q=0.5*h[1];e.fillStyle="#000";e.fillRect(0,0,h[0],h[1]);e.strokeStyle="#555";e.beginPath();e.moveTo(0,q);e.lineTo(h[0],q);e.stroke();if(this.inputs)for(var p=0;4>p;++p){var l=this.values[p];if(this.inputs[p]&&this.inputs[p].link){e.strokeStyle=n[p];e.beginPath();var a=l[0]*k*-1+q;e.moveTo(0,Math.clamp(a,0,h[1]));for(var b=1;be&&(e=0);if(0!=c.length){var h=[0,0,0];if(0==e)h=c[0];else if(1==e)h=c[c.length-1];else{var k= (c.length-1)*e,e=c[Math.floor(k)],c=c[Math.floor(k)+1],k=k-Math.floor(k);h[0]=e[0]*(1-k)+c[0]*k;h[1]=e[1]*(1-k)+c[1]*k;h[2]=e[2]*(1-k)+c[2]*k}for(var q in h)h[q]/=255;this.boxcolor=colorToString(h);this.setOutputData(0,h)}};e.registerNodeType("color/palette",n);h.title="Frame";h.desc="Frame viewerew";h.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}];h.prototype.onDrawBackground=function(c){this.frame&&!this.flags.collapsed&&c.drawImage(this.frame, 0,0,this.size[0],this.size[1])};h.prototype.onExecute=function(){this.frame=this.getInputData(0);this.setDirtyCanvas(!0)};h.prototype.onWidget=function(c,e){if("resize"==e.name&&this.frame){var h=this.frame.width,k=this.frame.height;h||null==this.frame.videoWidth||(h=this.frame.videoWidth,k=this.frame.videoHeight);h&&k&&(this.size=[h,k]);this.setDirtyCanvas(!0,!0)}else"view"==e.name&&this.show()};h.prototype.show=function(){showElement&&this.frame&&showElement(this.frame)};e.registerNodeType("graphics/frame", -h);v.title="Image fade";v.desc="Fades between images";v.widgets=[{name:"resizeA",text:"Resize to A",type:"button"},{name:"resizeB",text:"Resize to B",type:"button"}];v.prototype.onAdded=function(){this.createCanvas();var c=this.canvas.getContext("2d");c.fillStyle="#000";c.fillRect(0,0,this.properties.width,this.properties.height)};v.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};v.prototype.onExecute= -function(){var c=this.canvas.getContext("2d");this.canvas.width=this.canvas.width;var e=this.getInputData(0);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);e=this.getInputData(2);null==e?e=this.properties.fade:this.properties.fade=e;c.globalAlpha=e;e=this.getInputData(1);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);c.globalAlpha=1;this.setOutputData(0,this.canvas);this.setDirtyCanvas(!0)};e.registerNodeType("graphics/imagefade",v);k.title="Crop";k.desc="Crop Image"; +h);x.title="Image fade";x.desc="Fades between images";x.widgets=[{name:"resizeA",text:"Resize to A",type:"button"},{name:"resizeB",text:"Resize to B",type:"button"}];x.prototype.onAdded=function(){this.createCanvas();var c=this.canvas.getContext("2d");c.fillStyle="#000";c.fillRect(0,0,this.properties.width,this.properties.height)};x.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};x.prototype.onExecute= +function(){var c=this.canvas.getContext("2d");this.canvas.width=this.canvas.width;var e=this.getInputData(0);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);e=this.getInputData(2);null==e?e=this.properties.fade:this.properties.fade=e;c.globalAlpha=e;e=this.getInputData(1);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);c.globalAlpha=1;this.setOutputData(0,this.canvas);this.setDirtyCanvas(!0)};e.registerNodeType("graphics/imagefade",x);k.title="Crop";k.desc="Crop Image"; k.prototype.onAdded=function(){this.createCanvas()};k.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};k.prototype.onExecute=function(){var c=this.getInputData(0);c&&(c.width?(this.canvas.getContext("2d").drawImage(c,-this.properties.x,-this.properties.y,c.width*this.properties.scale,c.height*this.properties.scale),this.setOutputData(0,this.canvas)):this.setOutputData(0,null))};k.prototype.onDrawBackground= function(c){this.flags.collapsed||this.canvas&&c.drawImage(this.canvas,0,0,this.canvas.width,this.canvas.height,0,0,this.size[0],this.size[1])};k.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"scale"==c?(this.properties[c]=parseFloat(e),0==this.properties[c]&&(console.error("Error in scale"),this.properties[c]=1)):this.properties[c]=parseInt(e);this.createCanvas();return!0};e.registerNodeType("graphics/cropImage",k);C.title="Canvas";C.desc="Canvas to render stuff";C.prototype.onExecute= -function(){var c=this.canvas,e=this.properties.width|0,h=this.properties.height|0;c.width!=e&&(c.width=e);c.height!=h&&(c.height=h);this.properties.autoclear&&this.ctx.clearRect(0,0,c.width,c.height);this.setOutputData(0,c)};C.prototype.onAction=function(c,e){"clear"==c&&this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)};e.registerNodeType("graphics/canvas",C);x.title="DrawImage";x.desc="Draws image into a canvas";x.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e= -this.getInputOrProperty("img");if(e){var h=this.getInputOrProperty("x"),k=this.getInputOrProperty("y");c.getContext("2d").drawImage(e,h,k)}}};e.registerNodeType("graphics/drawImage",x);w.title="DrawRectangle";w.desc="Draws rectangle in canvas";w.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e=this.getInputOrProperty("x"),h=this.getInputOrProperty("y"),k=this.getInputOrProperty("w"),q=this.getInputOrProperty("h");c.getContext("2d").fillRect(e,h,k,q)}};e.registerNodeType("graphics/drawRectangle", -w);D.title="Video";D.desc="Video playback";D.widgets=[{name:"play",text:"PLAY",type:"minibutton"},{name:"stop",text:"STOP",type:"minibutton"},{name:"demo",text:"Demo video",type:"button"},{name:"mute",text:"Mute video",type:"button"}];D.prototype.onExecute=function(){if(this.properties.url&&(this.properties.url!=this._video_url&&this.loadVideo(this.properties.url),this._video&&0!=this._video.width)){var c=this.getInputData(0);c&&0<=c&&1>=c&&(this._video.currentTime=c*this._video.duration,this._video.pause()); +function(){var c=this.canvas,e=this.properties.width|0,h=this.properties.height|0;c.width!=e&&(c.width=e);c.height!=h&&(c.height=h);this.properties.autoclear&&this.ctx.clearRect(0,0,c.width,c.height);this.setOutputData(0,c)};C.prototype.onAction=function(c,e){"clear"==c&&this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)};e.registerNodeType("graphics/canvas",C);w.title="DrawImage";w.desc="Draws image into a canvas";w.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e= +this.getInputOrProperty("img");if(e){var h=this.getInputOrProperty("x"),k=this.getInputOrProperty("y");c.getContext("2d").drawImage(e,h,k)}}};e.registerNodeType("graphics/drawImage",w);t.title="DrawRectangle";t.desc="Draws rectangle in canvas";t.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e=this.getInputOrProperty("x"),h=this.getInputOrProperty("y"),k=this.getInputOrProperty("w"),q=this.getInputOrProperty("h");c.getContext("2d").fillRect(e,h,k,q)}};e.registerNodeType("graphics/drawRectangle", +t);D.title="Video";D.desc="Video playback";D.widgets=[{name:"play",text:"PLAY",type:"minibutton"},{name:"stop",text:"STOP",type:"minibutton"},{name:"demo",text:"Demo video",type:"button"},{name:"mute",text:"Mute video",type:"button"}];D.prototype.onExecute=function(){if(this.properties.url&&(this.properties.url!=this._video_url&&this.loadVideo(this.properties.url),this._video&&0!=this._video.width)){var c=this.getInputData(0);c&&0<=c&&1>=c&&(this._video.currentTime=c*this._video.duration,this._video.pause()); this._video.dirty=!0;this.setOutputData(0,this._video);this.setOutputData(1,this._video.currentTime);this.setOutputData(2,this._video.duration);this.setDirtyCanvas(!0)}};D.prototype.onStart=function(){this.play()};D.prototype.onStop=function(){this.stop()};D.prototype.loadVideo=function(c){this._video_url=c;var h=c.substr(0,10).indexOf(":"),k="";-1!=h&&(k=c.substr(0,h));h="";k&&(h=c.substr(0,c.indexOf("/",k.length+3)),h=h.substr(k.length+3));this.properties.use_proxy&&k&&e.proxy&&h!=location.host&& (c=e.proxy+c.substr(c.indexOf(":")+3));this._video=document.createElement("video");this._video.src=c;this._video.type="type=video/mp4";this._video.muted=!0;this._video.autoplay=!0;var n=this;this._video.addEventListener("loadedmetadata",function(c){console.log("Duration: "+this.duration+" seconds");console.log("Size: "+this.videoWidth+","+this.videoHeight);n.setDirtyCanvas(!0);this.width=this.videoWidth;this.height=this.videoHeight});this._video.addEventListener("progress",function(c){console.log("video loading...")}); this._video.addEventListener("error",function(c){console.error("Error loading video: "+this.src);if(this.error)switch(this.error.code){case this.error.MEDIA_ERR_ABORTED:console.error("You stopped the video.");break;case this.error.MEDIA_ERR_NETWORK:console.error("Network error - please try again later.");break;case this.error.MEDIA_ERR_DECODE:console.error("Video is broken..");break;case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED:console.error("Sorry, your browser can't play this video.")}});this._video.addEventListener("ended", @@ -415,151 +419,154 @@ var e=this}};A.prototype.closeStream=function(){if(this._webcam_stream){var c=th c;this.boxcolor="green";var e=this._video;e||(e=document.createElement("video"),e.autoplay=!0,e.srcObject=c,this._video=e,e.onloadedmetadata=function(c){console.log(c);A.is_webcam_open=!0});this.trigger("stream_ready",e)};A.prototype.onExecute=function(){null!=this._webcam_stream||this._waiting_confirmation||this.openStream();if(this._video&&this._video.videoWidth){this._video.frame=++this.frame;this._video.width=this._video.videoWidth;this._video.height=this._video.videoHeight;this.setOutputData(0, this._video);for(var c=1;c=this.size[1]||!this.properties.show|| !this._video||(c.save(),c.drawImage(this._video,0,0,this.size[0],this.size[1]),c.restore())};A.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["stream_ready",e.EVENT],["stream_closed",e.EVENT],["stream_error",e.EVENT]]};e.registerNodeType("graphics/webcam",A)})(this); -(function(B){function c(){this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",filter:!0};this.size=[c.image_preview_size,c.image_preview_size]}function m(){this.addInput("Texture","Texture");this.properties={flipY:!1};this.size=[c.image_preview_size,c.image_preview_size]}function n(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",generate_mipmaps:!1}}function h(){this.addInput("Texture", +(function(z){function c(){this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",filter:!0};this.size=[c.image_preview_size,c.image_preview_size]}function m(){this.addInput("Texture","Texture");this.properties={flipY:!1};this.size=[c.image_preview_size,c.image_preview_size]}function n(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",generate_mipmaps:!1}}function h(){this.addInput("Texture", "Texture");this.addInput("TextureB","Texture");this.addInput("value","number");this.addOutput("Texture","Texture");this.help="

pixelcode must be vec3, uvcode must be vec2, is optional

\t\t

uv: tex. coords

color: texture colorB: textureB

time: scene time value: input value

For multiline you must type: result = ...

";this.properties={value:1,pixelcode:"color + colorB * value",uvcode:"",precision:c.DEFAULT}; -this.has_error=!1}function v(){this.addOutput("out","Texture");this.properties={code:"",u_value:1,u_color:[1,1,1,1],width:512,height:512,precision:c.DEFAULT};this.properties.code=v.pixel_shader;this._uniforms={u_value:1,u_color:vec4.create(),in_texture:0,texSize:vec2.create(),time:0}}function k(){this.addInput("in","Texture");this.addInput("scale","vec2");this.addInput("offset","vec2");this.addOutput("out","Texture");this.properties={offset:vec2.fromValues(0,0),scale:vec2.fromValues(1,1),precision:c.DEFAULT}} -function C(){this.addInput("in","Texture");this.addInput("warp","Texture");this.addInput("factor","number");this.addOutput("out","Texture");this.properties={factor:0.01,scale:[1,1],offset:[0,0],precision:c.DEFAULT};this._uniforms={u_texture:0,u_textureB:1,u_factor:1,u_scale:vec2.create(),u_offset:vec2.create()}}function x(){this.addInput("Texture","Texture");this.properties={additive:!1,antialiasing:!1,filter:!0,disable_alpha:!1,gamma:1,viewport:[0,0,1,1]};this.size[0]=130}function w(){this.addInput("Texture", +this.has_error=!1}function x(){this.addOutput("out","Texture");this.properties={code:"",u_value:1,u_color:[1,1,1,1],width:512,height:512,precision:c.DEFAULT};this.properties.code=x.pixel_shader;this._uniforms={u_value:1,u_color:vec4.create(),in_texture:0,texSize:vec2.create(),time:0}}function k(){this.addInput("in","Texture");this.addInput("scale","vec2");this.addInput("offset","vec2");this.addOutput("out","Texture");this.properties={offset:vec2.fromValues(0,0),scale:vec2.fromValues(1,1),precision:c.DEFAULT}} +function C(){this.addInput("in","Texture");this.addInput("warp","Texture");this.addInput("factor","number");this.addOutput("out","Texture");this.properties={factor:0.01,scale:[1,1],offset:[0,0],precision:c.DEFAULT};this._uniforms={u_texture:0,u_textureB:1,u_factor:1,u_scale:vec2.create(),u_offset:vec2.create()}}function w(){this.addInput("Texture","Texture");this.properties={additive:!1,antialiasing:!1,filter:!0,disable_alpha:!1,gamma:1,viewport:[0,0,1,1]};this.size[0]=130}function t(){this.addInput("Texture", "Texture");this.addOutput("","Texture");this.properties={size:0,generate_mipmaps:!1,precision:c.DEFAULT}}function D(){this.addInput("Texture","Texture");this.addOutput("","Texture");this.properties={iterations:1,generate_mipmaps:!1,precision:c.DEFAULT}}function A(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("avg","vec4");this.addOutput("lum","number");this.properties={use_previous_frame:!0,high_quality:!1};this._uniforms={u_texture:0,u_mipmap_offset:0};this._luminance= -new Float32Array(4)}function e(){this.addInput("Texture","Texture");this.addOutput("min_t","Texture");this.addOutput("max_t","Texture");this.addOutput("min","vec4");this.addOutput("max","vec4");this.properties={mode:"max",use_previous_frame:!0};this._uniforms={u_texture:0};this._max=new Float32Array(4);this._min=new Float32Array(4);this._textures_chain=[]}function z(){this.addInput("in","Texture");this.addInput("factor","Number");this.addOutput("out","Texture");this.properties={factor:0.5};this._uniforms= -{u_texture:0,u_textureB:1,u_factor:this.properties.factor}}function y(){this.addInput("in","Texture");this.addOutput("avg","Texture");this.addOutput("array","Texture");this.properties={samples:64,frames_interval:1};this._uniforms={u_texture:0,u_textureB:1,u_samples:this.properties.samples,u_isamples:1/this.properties.samples};this.frame=0}function u(){this.addInput("Image","image");this.addOutput("","Texture");this.properties={}}function G(){this.addInput("Texture","Texture");this.addInput("LUT", -"Texture");this.addInput("Intensity","number");this.addOutput("","Texture");this.properties={enabled:!0,intensity:1,precision:c.DEFAULT,texture:null};G._shader||(G._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,G.pixel_shader))}function q(){this.addInput("Texture","Texture");this.addOutput("R","Texture");this.addOutput("G","Texture");this.addOutput("B","Texture");this.addOutput("A","Texture");q._shader||(q._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,q.pixel_shader))}function p(){this.addInput("R", +new Float32Array(4)}function e(){this.addInput("Texture","Texture");this.addOutput("min_t","Texture");this.addOutput("max_t","Texture");this.addOutput("min","vec4");this.addOutput("max","vec4");this.properties={mode:"max",use_previous_frame:!0};this._uniforms={u_texture:0};this._max=new Float32Array(4);this._min=new Float32Array(4);this._textures_chain=[]}function y(){this.addInput("in","Texture");this.addInput("factor","Number");this.addOutput("out","Texture");this.properties={factor:0.5};this._uniforms= +{u_texture:0,u_textureB:1,u_factor:this.properties.factor}}function B(){this.addInput("in","Texture");this.addOutput("avg","Texture");this.addOutput("array","Texture");this.properties={samples:64,frames_interval:1};this._uniforms={u_texture:0,u_textureB:1,u_samples:this.properties.samples,u_isamples:1/this.properties.samples};this.frame=0}function u(){this.addInput("Image","image");this.addOutput("","Texture");this.properties={}}function F(){this.addInput("Texture","Texture");this.addInput("LUT", +"Texture");this.addInput("Intensity","number");this.addOutput("","Texture");this.properties={enabled:!0,intensity:1,precision:c.DEFAULT,texture:null};F._shader||(F._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,F.pixel_shader))}function q(){this.addInput("Texture","Texture");this.addOutput("R","Texture");this.addOutput("G","Texture");this.addOutput("B","Texture");this.addOutput("A","Texture");q._shader||(q._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,q.pixel_shader))}function p(){this.addInput("R", "Texture");this.addInput("G","Texture");this.addInput("B","Texture");this.addInput("A","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,R:1,G:1,B:1,A:1};this._color=vec4.create();this._uniforms={u_textureR:0,u_textureG:1,u_textureB:2,u_textureA:3,u_color:this._color}}function l(){this.addOutput("Texture","Texture");this._tex_color=vec4.create();this.properties={color:vec4.create(),precision:c.DEFAULT}}function a(){this.addInput("A","color");this.addInput("B","color"); this.addOutput("Texture","Texture");this.properties={angle:0,scale:1,A:[0,0,0],B:[1,1,1],texture_size:32};a._shader||(a._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,a.pixel_shader));this._uniforms={u_angle:0,u_colorA:vec3.create(),u_colorB:vec3.create()}}function b(){this.addInput("A","Texture");this.addInput("B","Texture");this.addInput("Mixer","Texture");this.addOutput("Texture","Texture");this.properties={factor:0.5,size_from_biggest:!0,invert:!1,precision:c.DEFAULT};this._uniforms={u_textureA:0, u_textureB:1,u_textureMix:2,u_mix:vec4.create()}}function d(){this.addInput("Tex.","Texture");this.addOutput("Edges","Texture");this.properties={invert:!0,threshold:!1,factor:1,precision:c.DEFAULT};d._shader||(d._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,d.pixel_shader))}function g(){this.addInput("Texture","Texture");this.addInput("Distance","number");this.addInput("Range","number");this.addOutput("Texture","Texture");this.properties={distance:100,range:50,only_depth:!1,high_precision:!1}; this._uniforms={u_texture:0,u_distance:100,u_range:50,u_camera_planes:null}}function f(){this.addInput("Texture","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,invert:!1};this._uniforms={u_texture:0,u_camera_planes:null,u_ires:vec2.create()}}function r(){this.addInput("Texture","Texture");this.addInput("Iterations","number");this.addInput("Intensity","number");this.addOutput("Blurred","Texture");this.properties={intensity:1,iterations:1,preserve_aspect:!1,scale:[1, -1],precision:c.DEFAULT}}function t(){this.addInput("in","Texture");this.addInput("dirt","Texture");this.addOutput("out","Texture");this.addOutput("glow","Texture");this.properties={enabled:!0,intensity:1,persistence:0.99,iterations:16,threshold:0,scale:1,dirt_factor:0.5,precision:c.DEFAULT};this._textures=[];this._uniforms={u_intensity:1,u_texture:0,u_glow_texture:1,u_threshold:0,u_texel_size:vec2.create()}}function I(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties= -{intensity:1,radius:5}}function J(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={sigma:1.4,k:1.6,p:21.7,epsilon:79,phi:0.017}}function H(){this.addOutput("Webcam","Texture");this.properties={texture_name:"",facingMode:"user"};this.boxcolor="black";this.version=0}function E(){this.addInput("in","Texture");this.addInput("f","number");this.addOutput("out","Texture");this.properties={enabled:!0,factor:1,precision:c.LOW};this._uniforms={u_texture:0,u_factor:1}} -function s(){this.addInput("in","Texture");this.addOutput("out","Texture");this.properties={precision:c.LOW,split_channels:!1};this._values=new Uint8Array(1024);this._values.fill(255);this._curve_texture=null;this._uniforms={u_texture:0,u_curve:1,u_range:1};this._must_update=!0;this._points={RGB:[[0,0],[1,1]],R:[[0,0],[1,1]],G:[[0,0],[1,1]],B:[[0,0],[1,1]]};this.curve_editor=null;this.addWidget("toggle","Split Channels",!1,"split_channels");this.addWidget("combo","Channel","RGB",{values:["RGB","R", -"G","B"]});this.curve_offset=68;this.size=[240,160]}function N(){this.addInput("in","Texture");this.addInput("exp","number");this.addOutput("out","Texture");this.properties={exposition:1,precision:c.LOW};this._uniforms={u_texture:0,u_exposition:1}}function K(){this.addInput("in","Texture");this.addInput("avg","number,Texture");this.addOutput("out","Texture");this.properties={enabled:!0,scale:1,gamma:1,average_lum:1,lum_white:1,precision:c.LOW};this._uniforms={u_texture:0,u_lumwhite2:1,u_igamma:1, -u_scale:1,u_average_lum:1}}function M(){this.addOutput("out","Texture");this.properties={width:512,height:512,seed:0,persistence:0.1,octaves:8,scale:1,offset:[0,0],amplitude:1,precision:c.DEFAULT};this._key=0;this._texture=null;this._uniforms={u_persistence:0.1,u_seed:0,u_offset:vec2.create(),u_scale:1,u_viewport:vec2.create()}}function L(){this.addInput("v");this.addOutput("out","Texture");this.properties={code:L.default_code,width:512,height:512,clear:!0,precision:c.DEFAULT,use_html_canvas:!1}; -this._temp_texture=this._func=null;this.compileCode()}function O(){this.addInput("in","Texture");this.addOutput("out","Texture");this.properties={key_color:vec3.fromValues(0,1,0),threshold:0.8,slope:0.2,precision:c.DEFAULT}}function P(){this.addInput("in","texture");this.addInput("yaw","number");this.addOutput("out","texture");this.properties={yaw:0}}var F=B.LiteGraph,R=B.LGraphCanvas;B.LGraphTexture=null;"undefined"!=typeof GL&&(R.link_type_colors.Texture="#987",B.LGraphTexture=c,c.title="Texture", -c.desc="Texture",c.widgets_info={name:{widget:"texture"},filter:{widget:"checkbox"}},c.loadTextureCallback=null,c.image_preview_size=256,c.PASS_THROUGH=1,c.COPY=2,c.LOW=3,c.HIGH=4,c.REUSE=5,c.DEFAULT=2,c.MODE_VALUES={"pass through":c.PASS_THROUGH,copy:c.COPY,low:c.LOW,high:c.HIGH,reuse:c.REUSE,"default":c.DEFAULT},c.getTexturesContainer=function(){return gl.textures},c.loadTexture=function(a,b){b=b||{};var d=a;"http://"==d.substr(0,7)&&F.proxy&&(d=F.proxy+d.substr(7));return c.getTexturesContainer()[a]= -GL.Texture.fromURL(d,b)},c.getTexture=function(a){var b=this.getTexturesContainer();if(!b)throw"Cannot load texture, container of textures not found";b=b[a];return!b&&a&&":"!=a[0]?this.loadTexture(a):b},c.getTargetTexture=function(a,b,d){if(!a)throw"LGraphTexture.getTargetTexture expects a reference texture";var g=null;switch(d){case c.LOW:g=gl.UNSIGNED_BYTE;break;case c.HIGH:g=gl.HIGH_PRECISION_FORMAT;break;case c.REUSE:return a;default:g=a?a.type:gl.UNSIGNED_BYTE}b&&b.width==a.width&&b.height== -a.height&&b.type==g||(b=new GL.Texture(a.width,a.height,{type:g,format:gl.RGBA,filter:gl.LINEAR}));return b},c.getTextureType=function(a,b){var d=b?b.type:gl.UNSIGNED_BYTE;switch(a){case c.HIGH:d=gl.HIGH_PRECISION_FORMAT;break;case c.LOW:d=gl.UNSIGNED_BYTE}return d},c.getWhiteTexture=function(){return this._white_texture?this._white_texture:this._white_texture=GL.Texture.fromMemory(1,1,[255,255,255,255],{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.getNoiseTexture=function(){if(this._noise_texture)return this._noise_texture; -for(var a=new Uint8Array(1048576),b=0;1048576>b;++b)a[b]=255*Math.random();return this._noise_texture=a=GL.Texture.fromMemory(512,512,a,{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.prototype.onDropFile=function(a,b,d){if(a){var c=null;"string"==typeof a?c=GL.Texture.fromURL(a):-1!=b.toLowerCase().indexOf(".dds")?c=GL.Texture.fromDDSInMemory(a):(a=new Blob([d]),a=URL.createObjectURL(a),c=GL.Texture.fromURL(a));this._drop_texture=c;this.properties.name=b}else this._drop_texture=null,this.properties.name= -""},c.prototype.getExtraMenuOptions=function(a){var b=this;if(this._drop_texture)return[{content:"Clear",callback:function(){b._drop_texture=null;b.properties.name=""}}]},c.prototype.onExecute=function(){var a=null;this.isOutputConnected(1)&&(a=this.getInputData(0));!a&&this._drop_texture&&(a=this._drop_texture);!a&&this.properties.name&&(a=c.getTexture(this.properties.name));if(a){this._last_tex=a;!1===this.properties.filter?a.setParameter(gl.TEXTURE_MAG_FILTER,gl.NEAREST):a.setParameter(gl.TEXTURE_MAG_FILTER, -gl.LINEAR);this.setOutputData(0,a);this.setOutputData(1,a.fullpath||a.filename);for(var b=2;b= -this.size[1]))if(this._drop_texture&&a.webgl)a.drawImage(this._drop_texture,0,0,this.size[0],this.size[1]);else{if(this._last_preview_tex!=this._last_tex)if(a.webgl)this._canvas=this._last_tex;else{var b=c.generateLowResTexturePreview(this._last_tex);if(!b)return;this._last_preview_tex=this._last_tex;this._canvas=cloneCanvas(b)}this._canvas&&(a.save(),a.webgl||(a.translate(0,this.size[1]),a.scale(1,-1)),a.drawImage(this._canvas,0,0,this.size[0],this.size[1]),a.restore())}},c.generateLowResTexturePreview= -function(a){if(!a)return null;var b=c.image_preview_size,d=a;if(a.format==gl.DEPTH_COMPONENT)return null;if(a.width>b||a.height>b)d=this._preview_temp_tex,this._preview_temp_tex||(this._preview_temp_tex=d=new GL.Texture(b,b,{minFilter:gl.NEAREST})),a.copyTo(d);a=this._preview_canvas;a||(this._preview_canvas=a=createCanvas(b,b));d&&d.toCanvas(a);return a},c.prototype.getResources=function(a){this.properties.name&&(a[this.properties.name]=GL.Texture);return a},c.prototype.onGetInputs=function(){return[["in", -"Texture"]]},c.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["aspect","number"]]},c.replaceCode=function(a,b){return a.replace(/\{\{[a-zA-Z0-9_]*\}\}/g,function(a){a=a.replace(/[\{\}]/g,"");return b[a]||""})},F.registerNodeType("texture/texture",c),m.title="Preview",m.desc="Show a texture in the graph canvas",m.allow_preview=!1,m.prototype.onDrawBackground=function(a){if(!this.flags.collapsed&&(a.webgl||m.allow_preview)){var b=this.getInputData(0);if(b){var d=null, -d=!b.handle&&a.webgl?b:c.generateLowResTexturePreview(b);a.save();this.properties.flipY&&(a.translate(0,this.size[1]),a.scale(1,-1));a.drawImage(d,0,0,this.size[0],this.size[1]);a.restore()}}},F.registerNodeType("texture/preview",m),n.title="Save",n.desc="Save a texture in the repository",n.prototype.getPreviewTexture=function(){return this._texture},n.prototype.onExecute=function(){var a=this.getInputData(0);a&&(this.properties.generate_mipmaps&&(a.bind(0),a.setParameter(gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_LINEAR), -gl.generateMipmap(a.texture_type),a.unbind(0)),this.properties.name&&(c.storeTexture?c.storeTexture(this.properties.name,a):c.getTexturesContainer()[this.properties.name]=a),this._texture=a,this.setOutputData(0,a),this.setOutputData(1,this.properties.name))},F.registerNodeType("texture/save",n),h.widgets_info={uvcode:{widget:"code"},pixelcode:{widget:"code"},precision:{widget:"combo",values:c.MODE_VALUES}},h.title="Operation",h.desc="Texture shader operation",h.presets={},h.prototype.getExtraMenuOptions= -function(a){var b=this;return[{content:b.properties.show?"Hide Texture":"Show Texture",callback:function(){b.properties.show=!b.properties.show}}]},h.prototype.onPropertyChanged=function(){this.has_error=!1},h.prototype.onDrawBackground=function(a){this.flags.collapsed||20>=this.size[1]||!this.properties.show||!this._tex||this._tex.gl!=a||(a.save(),a.drawImage(this._tex,0,0,this.size[0],this.size[1]),a.restore())},h.prototype.onExecute=function(){var a=this.getInputData(0);if(this.isOutputConnected(0))if(this.properties.precision=== -c.PASS_THROUGH)this.setOutputData(0,a);else{var b=this.getInputData(1);if(this.properties.uvcode||this.properties.pixelcode){var d=512,g=512;a?(d=a.width,g=a.height):b&&(d=b.width,g=b.height);b||(b=GL.Texture.getWhiteTexture());var f=c.getTextureType(this.properties.precision,a);this._tex=a||this._tex?c.getTargetTexture(a||this._tex,this._tex,this.properties.precision):new GL.Texture(d,g,{type:f,format:gl.RGBA,filter:gl.LINEAR});f="";this.properties.uvcode&&(f="uv = "+this.properties.uvcode,-1!=this.properties.uvcode.indexOf(";")&& -(f=this.properties.uvcode));var e="";this.properties.pixelcode&&(e="result = "+this.properties.pixelcode,-1!=this.properties.pixelcode.indexOf(";")&&(e=this.properties.pixelcode));var l=this._shader;if(!(this.has_error||l&&this._shader_code==f+"|"+e)){var k=c.replaceCode(h.pixel_shader,{UV_CODE:f,PIXEL_CODE:e});try{l=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,k),this.boxcolor="#00FF00"}catch(q){GL.Shader.dumpErrorToConsole(q,Shader.SCREEN_VERTEX_SHADER,k);this.boxcolor="#FF0000";this.has_error=!0; -return}this._shader=l;this._shader_code=f+"|"+e}if(this._shader){var p=this.getInputData(2);null!=p?this.properties.value=p:p=parseFloat(this.properties.value);var r=this.graph.getTime();this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);gl.disable(gl.BLEND);a&&a.bind(0);b&&b.bind(1);var c=Mesh.getScreenQuad();l.uniforms({u_texture:0,u_textureB:1,value:p,texSize:[d,g],time:r}).draw(c)});this.setOutputData(0,this._tex)}}}},h.pixel_shader="precision highp float;\n\t\t\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec2 texSize;\n\t\tuniform float time;\n\t\tuniform float value;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord;\n\t\t\t{{UV_CODE}};\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\tvec3 color = color4.rgb;\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\tvec3 colorB = color4B.rgb;\n\t\t\tvec3 result = color;\n\t\t\tfloat alpha = 1.0;\n\t\t\t{{PIXEL_CODE}};\n\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t}\n\t\t", +1],precision:c.DEFAULT}}function s(){this.addInput("in","Texture");this.addInput("dirt","Texture");this.addOutput("out","Texture");this.addOutput("glow","Texture");this.properties={enabled:!0,intensity:1,persistence:0.99,iterations:16,threshold:0,scale:1,dirt_factor:0.5,precision:c.DEFAULT};this._textures=[];this._uniforms={u_intensity:1,u_texture:0,u_glow_texture:1,u_threshold:0,u_texel_size:vec2.create()}}function J(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties= +{intensity:1,radius:5}}function K(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={sigma:1.4,k:1.6,p:21.7,epsilon:79,phi:0.017}}function I(){this.addOutput("Webcam","Texture");this.properties={texture_name:"",facingMode:"user"};this.boxcolor="black";this.version=0}function E(){this.addInput("in","Texture");this.addInput("f","number");this.addOutput("out","Texture");this.properties={enabled:!0,factor:1,precision:c.LOW};this._uniforms={u_texture:0,u_factor:1}} +function v(){this.addInput("in","");this.properties={precision:c.LOW,width:0,height:0,channels:1};this.addOutput("out","Texture")}function H(){this.addInput("in","Texture");this.addOutput("out","Texture");this.properties={precision:c.LOW,split_channels:!1};this._values=new Uint8Array(1024);this._values.fill(255);this._curve_texture=null;this._uniforms={u_texture:0,u_curve:1,u_range:1};this._must_update=!0;this._points={RGB:[[0,0],[1,1]],R:[[0,0],[1,1]],G:[[0,0],[1,1]],B:[[0,0],[1,1]]};this.curve_editor= +null;this.addWidget("toggle","Split Channels",!1,"split_channels");this.addWidget("combo","Channel","RGB",{values:["RGB","R","G","B"]});this.curve_offset=68;this.size=[240,160]}function M(){this.addInput("in","Texture");this.addInput("exp","number");this.addOutput("out","Texture");this.properties={exposition:1,precision:c.LOW};this._uniforms={u_texture:0,u_exposition:1}}function L(){this.addInput("in","Texture");this.addInput("avg","number,Texture");this.addOutput("out","Texture");this.properties= +{enabled:!0,scale:1,gamma:1,average_lum:1,lum_white:1,precision:c.LOW};this._uniforms={u_texture:0,u_lumwhite2:1,u_igamma:1,u_scale:1,u_average_lum:1}}function O(){this.addOutput("out","Texture");this.properties={width:512,height:512,seed:0,persistence:0.1,octaves:8,scale:1,offset:[0,0],amplitude:1,precision:c.DEFAULT};this._key=0;this._texture=null;this._uniforms={u_persistence:0.1,u_seed:0,u_offset:vec2.create(),u_scale:1,u_viewport:vec2.create()}}function N(){this.addInput("v");this.addOutput("out", +"Texture");this.properties={code:N.default_code,width:512,height:512,clear:!0,precision:c.DEFAULT,use_html_canvas:!1};this._temp_texture=this._func=null;this.compileCode()}function P(){this.addInput("in","Texture");this.addOutput("out","Texture");this.properties={key_color:vec3.fromValues(0,1,0),threshold:0.8,slope:0.2,precision:c.DEFAULT}}function R(){this.addInput("in","texture");this.addInput("yaw","number");this.addOutput("out","texture");this.properties={yaw:0}}var G=z.LiteGraph,S=z.LGraphCanvas; +z.LGraphTexture=null;"undefined"!=typeof GL&&(S.link_type_colors.Texture="#987",z.LGraphTexture=c,c.title="Texture",c.desc="Texture",c.widgets_info={name:{widget:"texture"},filter:{widget:"checkbox"}},c.loadTextureCallback=null,c.image_preview_size=256,c.PASS_THROUGH=1,c.COPY=2,c.LOW=3,c.HIGH=4,c.REUSE=5,c.DEFAULT=2,c.MODE_VALUES={"pass through":c.PASS_THROUGH,copy:c.COPY,low:c.LOW,high:c.HIGH,reuse:c.REUSE,"default":c.DEFAULT},c.getTexturesContainer=function(){return gl.textures},c.loadTexture=function(a, +b){b=b||{};var d=a;"http://"==d.substr(0,7)&&G.proxy&&(d=G.proxy+d.substr(7));return c.getTexturesContainer()[a]=GL.Texture.fromURL(d,b)},c.getTexture=function(a){var b=this.getTexturesContainer();if(!b)throw"Cannot load texture, container of textures not found";b=b[a];return!b&&a&&":"!=a[0]?this.loadTexture(a):b},c.getTargetTexture=function(a,b,d){if(!a)throw"LGraphTexture.getTargetTexture expects a reference texture";var g=null;switch(d){case c.LOW:g=gl.UNSIGNED_BYTE;break;case c.HIGH:g=gl.HIGH_PRECISION_FORMAT; +break;case c.REUSE:return a;default:g=a?a.type:gl.UNSIGNED_BYTE}b&&b.width==a.width&&b.height==a.height&&b.type==g||(b=new GL.Texture(a.width,a.height,{type:g,format:gl.RGBA,filter:gl.LINEAR}));return b},c.getTextureType=function(a,b){var d=b?b.type:gl.UNSIGNED_BYTE;switch(a){case c.HIGH:d=gl.HIGH_PRECISION_FORMAT;break;case c.LOW:d=gl.UNSIGNED_BYTE}return d},c.getWhiteTexture=function(){return this._white_texture?this._white_texture:this._white_texture=GL.Texture.fromMemory(1,1,[255,255,255,255], +{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.getNoiseTexture=function(){if(this._noise_texture)return this._noise_texture;for(var a=new Uint8Array(1048576),b=0;1048576>b;++b)a[b]=255*Math.random();return this._noise_texture=a=GL.Texture.fromMemory(512,512,a,{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.prototype.onDropFile=function(a,b,d){if(a){var c=null;"string"==typeof a?c=GL.Texture.fromURL(a):-1!=b.toLowerCase().indexOf(".dds")?c=GL.Texture.fromDDSInMemory(a):(a=new Blob([d]), +a=URL.createObjectURL(a),c=GL.Texture.fromURL(a));this._drop_texture=c;this.properties.name=b}else this._drop_texture=null,this.properties.name=""},c.prototype.getExtraMenuOptions=function(a){var b=this;if(this._drop_texture)return[{content:"Clear",callback:function(){b._drop_texture=null;b.properties.name=""}}]},c.prototype.onExecute=function(){var a=null;this.isOutputConnected(1)&&(a=this.getInputData(0));!a&&this._drop_texture&&(a=this._drop_texture);!a&&this.properties.name&&(a=c.getTexture(this.properties.name)); +if(a){this._last_tex=a;!1===this.properties.filter?a.setParameter(gl.TEXTURE_MAG_FILTER,gl.NEAREST):a.setParameter(gl.TEXTURE_MAG_FILTER,gl.LINEAR);this.setOutputData(0,a);this.setOutputData(1,a.fullpath||a.filename);for(var b=2;b=this.size[1]))if(this._drop_texture&&a.webgl)a.drawImage(this._drop_texture,0,0,this.size[0],this.size[1]);else{if(this._last_preview_tex!=this._last_tex)if(a.webgl)this._canvas=this._last_tex;else{var b=c.generateLowResTexturePreview(this._last_tex);if(!b)return;this._last_preview_tex=this._last_tex;this._canvas=cloneCanvas(b)}this._canvas&&(a.save(),a.webgl||(a.translate(0, +this.size[1]),a.scale(1,-1)),a.drawImage(this._canvas,0,0,this.size[0],this.size[1]),a.restore())}},c.generateLowResTexturePreview=function(a){if(!a)return null;var b=c.image_preview_size,d=a;if(a.format==gl.DEPTH_COMPONENT)return null;if(a.width>b||a.height>b)d=this._preview_temp_tex,this._preview_temp_tex||(this._preview_temp_tex=d=new GL.Texture(b,b,{minFilter:gl.NEAREST})),a.copyTo(d);a=this._preview_canvas;a||(this._preview_canvas=a=createCanvas(b,b));d&&d.toCanvas(a);return a},c.prototype.getResources= +function(a){this.properties.name&&(a[this.properties.name]=GL.Texture);return a},c.prototype.onGetInputs=function(){return[["in","Texture"]]},c.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["aspect","number"]]},c.replaceCode=function(a,b){return a.replace(/\{\{[a-zA-Z0-9_]*\}\}/g,function(a){a=a.replace(/[\{\}]/g,"");return b[a]||""})},G.registerNodeType("texture/texture",c),m.title="Preview",m.desc="Show a texture in the graph canvas",m.allow_preview=!1,m.prototype.onDrawBackground= +function(a){if(!this.flags.collapsed&&(a.webgl||m.allow_preview)){var b=this.getInputData(0);if(b){var d=null,d=!b.handle&&a.webgl?b:c.generateLowResTexturePreview(b);a.save();this.properties.flipY&&(a.translate(0,this.size[1]),a.scale(1,-1));a.drawImage(d,0,0,this.size[0],this.size[1]);a.restore()}}},G.registerNodeType("texture/preview",m),n.title="Save",n.desc="Save a texture in the repository",n.prototype.getPreviewTexture=function(){return this._texture},n.prototype.onExecute=function(){var a= +this.getInputData(0);a&&(this.properties.generate_mipmaps&&(a.bind(0),a.setParameter(gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_LINEAR),gl.generateMipmap(a.texture_type),a.unbind(0)),this.properties.name&&(c.storeTexture?c.storeTexture(this.properties.name,a):c.getTexturesContainer()[this.properties.name]=a),this._texture=a,this.setOutputData(0,a),this.setOutputData(1,this.properties.name))},G.registerNodeType("texture/save",n),h.widgets_info={uvcode:{widget:"code"},pixelcode:{widget:"code"},precision:{widget:"combo", +values:c.MODE_VALUES}},h.title="Operation",h.desc="Texture shader operation",h.presets={},h.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:b.properties.show?"Hide Texture":"Show Texture",callback:function(){b.properties.show=!b.properties.show}}]},h.prototype.onPropertyChanged=function(){this.has_error=!1},h.prototype.onDrawBackground=function(a){this.flags.collapsed||20>=this.size[1]||!this.properties.show||!this._tex||this._tex.gl!=a||(a.save(),a.drawImage(this._tex,0,0,this.size[0], +this.size[1]),a.restore())},h.prototype.onExecute=function(){var a=this.getInputData(0);if(this.isOutputConnected(0))if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else{var b=this.getInputData(1);if(this.properties.uvcode||this.properties.pixelcode){var d=512,g=512;a?(d=a.width,g=a.height):b&&(d=b.width,g=b.height);b||(b=GL.Texture.getWhiteTexture());var f=c.getTextureType(this.properties.precision,a);this._tex=a||this._tex?c.getTargetTexture(a||this._tex,this._tex,this.properties.precision): +new GL.Texture(d,g,{type:f,format:gl.RGBA,filter:gl.LINEAR});f="";this.properties.uvcode&&(f="uv = "+this.properties.uvcode,-1!=this.properties.uvcode.indexOf(";")&&(f=this.properties.uvcode));var e="";this.properties.pixelcode&&(e="result = "+this.properties.pixelcode,-1!=this.properties.pixelcode.indexOf(";")&&(e=this.properties.pixelcode));var l=this._shader;if(!(this.has_error||l&&this._shader_code==f+"|"+e)){var k=c.replaceCode(h.pixel_shader,{UV_CODE:f,PIXEL_CODE:e});try{l=new GL.Shader(Shader.SCREEN_VERTEX_SHADER, +k),this.boxcolor="#00FF00"}catch(q){GL.Shader.dumpErrorToConsole(q,Shader.SCREEN_VERTEX_SHADER,k);this.boxcolor="#FF0000";this.has_error=!0;return}this._shader=l;this._shader_code=f+"|"+e}if(this._shader){var p=this.getInputData(2);null!=p?this.properties.value=p:p=parseFloat(this.properties.value);var r=this.graph.getTime();this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);gl.disable(gl.BLEND);a&&a.bind(0);b&&b.bind(1);var c=Mesh.getScreenQuad();l.uniforms({u_texture:0, +u_textureB:1,value:p,texSize:[d,g],time:r}).draw(c)});this.setOutputData(0,this._tex)}}}},h.pixel_shader="precision highp float;\n\t\t\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec2 texSize;\n\t\tuniform float time;\n\t\tuniform float value;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord;\n\t\t\t{{UV_CODE}};\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\tvec3 color = color4.rgb;\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\tvec3 colorB = color4B.rgb;\n\t\t\tvec3 result = color;\n\t\t\tfloat alpha = 1.0;\n\t\t\t{{PIXEL_CODE}};\n\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t}\n\t\t", h.registerPreset=function(a,b){h.presets[a]=b},h.registerPreset("",""),h.registerPreset("bypass","color"),h.registerPreset("add","color + colorB * value"),h.registerPreset("substract","(color - colorB) * value"),h.registerPreset("mate","mix( color, colorB, color4B.a * value)"),h.registerPreset("invert","vec3(1.0) - color"),h.registerPreset("multiply","color * colorB * value"),h.registerPreset("divide","(color / colorB) / value"),h.registerPreset("difference","abs(color - colorB) * value"),h.registerPreset("max", "max(color, colorB) * value"),h.registerPreset("min","min(color, colorB) * value"),h.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"),h.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"),h.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"),h.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"), -h.prototype.onInspect=function(a){var b=this;a.addCombo("Presets","",{values:Object.keys(h.presets),callback:function(d){var c=h.presets[d];c&&(b.setProperty("pixelcode",c),b.title=d,a.refresh())}})},F.registerNodeType("texture/operation",h),v.title="Shader",v.desc="Texture shader",v.widgets_info={code:{type:"code",lang:"glsl"},precision:{widget:"combo",values:c.MODE_VALUES}},v.prototype.onPropertyChanged=function(a,b){if("code"==a){var d=this.getShader();if(d){var c=d.uniformInfo;if(this.inputs)for(var g= +h.prototype.onInspect=function(a){var b=this;a.addCombo("Presets","",{values:Object.keys(h.presets),callback:function(d){var c=h.presets[d];c&&(b.setProperty("pixelcode",c),b.title=d,a.refresh())}})},G.registerNodeType("texture/operation",h),x.title="Shader",x.desc="Texture shader",x.widgets_info={code:{type:"code",lang:"glsl"},precision:{widget:"combo",values:c.MODE_VALUES}},x.prototype.onPropertyChanged=function(a,b){if("code"==a){var d=this.getShader();if(d){var c=d.uniformInfo;if(this.inputs)for(var g= {},f=0;f lumaMax))\n\t\t\t\tcolor = vec4(rgbA, 1.0);\n\t\t\telse\n\t\t\t\tcolor = vec4(rgbB, 1.0);\n\t\t\tif(u_igamma != 1.0)\n\t\t\t\tcolor.xyz = pow( color.xyz, vec3(u_igamma) );\n\t\t\treturn color;\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\t\t}\n\t\t", -x.gamma_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_igamma;\n\t\tvoid main() {\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\t\t gl_FragColor = color;\n\t\t}\n\t\t",F.registerNodeType("texture/toviewport",x),w.title="Copy",w.desc="Copy Texture",w.widgets_info={size:{widget:"combo",values:[0,32,64,128,256,512,1024,2048]},precision:{widget:"combo", -values:c.MODE_VALUES}},w.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)){if(a){var b=a.width,d=a.height;0!=this.properties.size&&(d=b=this.properties.size);var g=this._temp_texture,f=a.type;this.properties.precision===c.LOW?f=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(f=gl.HIGH_PRECISION_FORMAT);g&&g.width==b&&g.height==d&&g.type==f||(g=gl.LINEAR,this.properties.generate_mipmaps&&isPowerOfTwo(b)&&isPowerOfTwo(d)&&(g=gl.LINEAR_MIPMAP_LINEAR), -this._temp_texture=new GL.Texture(b,d,{type:f,format:gl.RGBA,minFilter:g,magFilter:gl.LINEAR}));a.copyTo(this._temp_texture);this.properties.generate_mipmaps&&(this._temp_texture.bind(0),gl.generateMipmap(this._temp_texture.texture_type),this._temp_texture.unbind(0))}this.setOutputData(0,this._temp_texture)}},F.registerNodeType("texture/copy",w),D.title="Downsample",D.desc="Downsample Texture",D.widgets_info={iterations:{type:"number",step:1,precision:0,min:0},precision:{widget:"combo",values:c.MODE_VALUES}}, +G.registerNodeType("texture/warp",C),w.title="to Viewport",w.desc="Texture to viewport",w._prev_viewport=new Float32Array(4),w.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this.properties.disable_alpha?gl.disable(gl.BLEND):(gl.enable(gl.BLEND),this.properties.additive?gl.blendFunc(gl.SRC_ALPHA,gl.ONE):gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA));gl.disable(gl.DEPTH_TEST);var b=this.properties.gamma||1;this.isInputConnected(1)&&(b=this.getInputData(1));a.setParameter(gl.TEXTURE_MAG_FILTER, +this.properties.filter?gl.LINEAR:gl.NEAREST);var d=w._prev_viewport;d.set(gl.viewport_data);var c=this.properties.viewport;gl.viewport(d[0]+d[2]*c[0],d[1]+d[3]*c[1],d[2]*c[2],d[3]*c[3]);gl.getViewport();this.properties.antialiasing?(w._shader||(w._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,w.aa_pixel_shader)),c=Mesh.getScreenQuad(),a.bind(0),w._shader.uniforms({u_texture:0,uViewportSize:[a.width,a.height],u_igamma:1/b,inverseVP:[1/a.width,1/a.height]}).draw(c)):1!=b?(w._gamma_shader||(w._gamma_shader= +new GL.Shader(Shader.SCREEN_VERTEX_SHADER,w.gamma_pixel_shader)),a.toViewport(w._gamma_shader,{u_texture:0,u_igamma:1/b})):a.toViewport();gl.viewport(d[0],d[1],d[2],d[3])}},w.prototype.onGetInputs=function(){return[["gamma","number"]]},w.aa_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 uViewportSize;\n\t\tuniform vec2 inverseVP;\n\t\tuniform float u_igamma;\n\t\t#define FXAA_REDUCE_MIN (1.0/ 128.0)\n\t\t#define FXAA_REDUCE_MUL (1.0 / 8.0)\n\t\t#define FXAA_SPAN_MAX 8.0\n\t\t\n\t\t/* from mitsuhiko/webgl-meincraft based on the code on geeks3d.com */\n\t\tvec4 applyFXAA(sampler2D tex, vec2 fragCoord)\n\t\t{\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t/*vec2 inverseVP = vec2(1.0 / uViewportSize.x, 1.0 / uViewportSize.y);*/\n\t\t\tvec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbM = texture2D(tex, fragCoord * inverseVP).xyz;\n\t\t\tvec3 luma = vec3(0.299, 0.587, 0.114);\n\t\t\tfloat lumaNW = dot(rgbNW, luma);\n\t\t\tfloat lumaNE = dot(rgbNE, luma);\n\t\t\tfloat lumaSW = dot(rgbSW, luma);\n\t\t\tfloat lumaSE = dot(rgbSE, luma);\n\t\t\tfloat lumaM = dot(rgbM, luma);\n\t\t\tfloat lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n\t\t\tfloat lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\t\t\t\n\t\t\tvec2 dir;\n\t\t\tdir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n\t\t\tdir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\t\t\t\n\t\t\tfloat dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\t\t\t\n\t\t\tfloat rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n\t\t\tdir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * inverseVP;\n\t\t\t\n\t\t\tvec3 rgbA = 0.5 * (texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + \n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz);\n\t\t\tvec3 rgbB = rgbA * 0.5 + 0.25 * (texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + \n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz);\n\t\t\t\n\t\t\t//return vec4(rgbA,1.0);\n\t\t\tfloat lumaB = dot(rgbB, luma);\n\t\t\tif ((lumaB < lumaMin) || (lumaB > lumaMax))\n\t\t\t\tcolor = vec4(rgbA, 1.0);\n\t\t\telse\n\t\t\t\tcolor = vec4(rgbB, 1.0);\n\t\t\tif(u_igamma != 1.0)\n\t\t\t\tcolor.xyz = pow( color.xyz, vec3(u_igamma) );\n\t\t\treturn color;\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\t\t}\n\t\t", +w.gamma_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_igamma;\n\t\tvoid main() {\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\t\t gl_FragColor = color;\n\t\t}\n\t\t",G.registerNodeType("texture/toviewport",w),t.title="Copy",t.desc="Copy Texture",t.widgets_info={size:{widget:"combo",values:[0,32,64,128,256,512,1024,2048]},precision:{widget:"combo", +values:c.MODE_VALUES}},t.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)){if(a){var b=a.width,d=a.height;0!=this.properties.size&&(d=b=this.properties.size);var g=this._temp_texture,f=a.type;this.properties.precision===c.LOW?f=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(f=gl.HIGH_PRECISION_FORMAT);g&&g.width==b&&g.height==d&&g.type==f||(g=gl.LINEAR,this.properties.generate_mipmaps&&isPowerOfTwo(b)&&isPowerOfTwo(d)&&(g=gl.LINEAR_MIPMAP_LINEAR), +this._temp_texture=new GL.Texture(b,d,{type:f,format:gl.RGBA,minFilter:g,magFilter:gl.LINEAR}));a.copyTo(this._temp_texture);this.properties.generate_mipmaps&&(this._temp_texture.bind(0),gl.generateMipmap(this._temp_texture.texture_type),this._temp_texture.unbind(0))}this.setOutputData(0,this._temp_texture)}},G.registerNodeType("texture/copy",t),D.title="Downsample",D.desc="Downsample Texture",D.widgets_info={iterations:{type:"number",step:1,precision:0,min:0},precision:{widget:"combo",values:c.MODE_VALUES}}, D.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)&&a&&a.texture_type===GL.TEXTURE_2D)if(1>this.properties.iterations)this.setOutputData(0,a);else{var b=D._shader;b||(D._shader=b=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,D.pixel_shader));var d=a.width|0,g=a.height|0,f=a.type;this.properties.precision===c.LOW?f=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(f=gl.HIGH_PRECISION_FORMAT);var e=this.properties.iterations||1,l=a,h= -null,k=[],a={type:f,format:a.format},f=vec2.create(),p={u_offset:f};this._texture&&GL.Texture.releaseTemporary(this._texture);for(var q=0;q>1||0;g=g>>1||0;h=GL.Texture.getTemporary(d,g,a);k.push(h);l.setParameter(GL.TEXTURE_MAG_FILTER,GL.NEAREST);l.copyTo(h,b,p);if(1==d&&1==g)break;l=h}this._texture=k.pop();for(q=0;q>1||0;g=g>>1||0;h=GL.Texture.getTemporary(d,g,a);k.push(h);l.setParameter(GL.TEXTURE_MAG_FILTER,GL.NEAREST);l.copyTo(h,b,q);if(1==d&&1==g)break;l=h}this._texture=k.pop();for(p=0;p>=2,c++,1!=d););a.copyTo(this._textures_chain[0]);for(var c=1;c<=this._textures_chain.length;++c)a=this._textures_chain[c];var g=e._shader,f=this._uniforms;f.u_mipmap_offset=this.properties.mipmap_offset;gl.disable(gl.DEPTH_TEST); gl.disable(gl.BLEND);this._temp_texture.drawTo(function(){a.toViewport(g,f)})}},e.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform mat4 u_samples_a;\n\t\tuniform mat4 u_samples_b;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_mipmap_offset;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t//random average\n\t\t\tfor(int i = 0; i < 4; ++i)\n\t\t\t\tfor(int j = 0; j < 4; ++j)\n\t\t\t\t{\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t}\n\t\t gl_FragColor = color * 0.03125;\n\t\t}\n\t\t", -z.title="Smooth",z.desc="Smooth texture over time",z.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){z._shader||(z._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,z.pixel_shader));var b=this._temp_texture;b&&b.type==a.type&&b.width==a.width&&b.height==a.height||(b={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(a.width,a.height,b),this._temp_texture2=new GL.Texture(a.width,a.height,b),a.copyTo(this._temp_texture2));var b= -this._temp_texture,d=this._temp_texture2,c=z._shader,g=this._uniforms;g.u_factor=1-this.getInputOrProperty("factor");gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);b.drawTo(function(){d.bind(1);a.toViewport(c,g)});this.setOutputData(0,b);this._temp_texture=d;this._temp_texture2=b}},z.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_factor;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tgl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\t\t}\n\t\t", -F.registerNodeType("texture/temporal_smooth",z),y.title="Lineal Avg Smooth",y.desc="Smooth texture linearly over time",y["@samples"]={type:"number",min:1,max:64,step:1,precision:1},y.prototype.getPreviewTexture=function(){return this._temp_texture2},y.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){y._shader||(y._shader_copy=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,y.pixel_shader_copy),y._shader_avg=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,y.pixel_shader_avg)); -var b=Math.clamp(this.properties.samples,0,64),d=this.frame,c=this.properties.frames_interval;if(0==c||0==d%c){d=this._temp_texture;d&&d.type==a.type&&d.width==b||(d={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(b,1,d),this._temp_texture2=new GL.Texture(b,1,d),this._temp_texture_out=new GL.Texture(1,1,d));var g=this._temp_texture,f=this._temp_texture2,e=y._shader_copy,l=y._shader_avg,h=this._uniforms;h.u_samples=b;h.u_isamples=1/b;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST); -g.drawTo(function(){f.bind(1);a.toViewport(e,h)});this._temp_texture_out.drawTo(function(){g.toViewport(l,h)});this.setOutputData(0,this._temp_texture_out);this._temp_texture=f;this._temp_texture2=g}else this.setOutputData(0,this._temp_texture_out);this.setOutputData(1,this._temp_texture2);this.frame++}},y.pixel_shader_copy="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tif( v_coord.x <= u_isamples )\n\t\t\t\tgl_FragColor = texture2D( u_texture, vec2(0.5) );\n\t\t\telse\n\t\t\t\tgl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\t\t}\n\t\t", -y.pixel_shader_avg="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform int u_samples;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\tfor(int i = 0; i < 64; ++i)\n\t\t\t{\n\t\t\t\tcolor += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\t\t\t\tif(i == (u_samples - 1))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tgl_FragColor = color * u_isamples;\n\t\t}\n\t\t",F.registerNodeType("texture/linear_avg_smooth", -y),u.title="Image to Texture",u.desc="Uploads an image to the GPU",u.prototype.onExecute=function(){var a=this.getInputData(0);if(a){var b=a.videoWidth||a.width,d=a.videoHeight||a.height;if(a.gltexture)this.setOutputData(0,a.gltexture);else{var c=this._temp_texture;c&&c.width==b&&c.height==d||(this._temp_texture=new GL.Texture(b,d,{format:gl.RGBA,filter:gl.LINEAR}));try{this._temp_texture.uploadImage(a)}catch(g){console.error("image comes from an unsafe location, cannot be uploaded to webgl: "+g); -return}this.setOutputData(0,this._temp_texture)}}},F.registerNodeType("texture/imageToTexture",u),G.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},G.title="LUT",G.desc="Apply LUT to Texture",G.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture)); -if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.bindTexture(gl.TEXTURE_2D,null);var d=this.properties.intensity;this.isInputConnected(2)&&(this.properties.intensity=d=this.getInputData(2));this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);this._tex.drawTo(function(){b.bind(1);a.toViewport(G._shader,{u_texture:0,u_textureB:1, -u_amount:d})});this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}},G.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_amount;\n\t\t\n\t\tvoid main() {\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\t\t\t mediump vec2 quad1;\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\t\t\t mediump vec2 quad2;\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\t\t\t highp vec2 texPos1;\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t highp vec2 texPos2;\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\t\t}\n\t\t", -F.registerNodeType("texture/LUT",G),q.title="Texture to Channels",q.desc="Split texture channels",q.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this._channels||(this._channels=Array(4));for(var b=gl.RGB,d=0,c=0;4>c;c++)this.isOutputConnected(c)?(this._channels[c]&&this._channels[c].width==a.width&&this._channels[c].height==a.height&&this._channels[c].type==a.type&&this._channels[c].format==b||(this._channels[c]=new GL.Texture(a.width,a.height,{type:a.type,format:b,filter:gl.LINEAR})), +y.title="Smooth",y.desc="Smooth texture over time",y.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){y._shader||(y._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,y.pixel_shader));var b=this._temp_texture;b&&b.type==a.type&&b.width==a.width&&b.height==a.height||(b={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(a.width,a.height,b),this._temp_texture2=new GL.Texture(a.width,a.height,b),a.copyTo(this._temp_texture2));var b= +this._temp_texture,d=this._temp_texture2,c=y._shader,g=this._uniforms;g.u_factor=1-this.getInputOrProperty("factor");gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);b.drawTo(function(){d.bind(1);a.toViewport(c,g)});this.setOutputData(0,b);this._temp_texture=d;this._temp_texture2=b}},y.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_factor;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tgl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\t\t}\n\t\t", +G.registerNodeType("texture/temporal_smooth",y),B.title="Lineal Avg Smooth",B.desc="Smooth texture linearly over time",B["@samples"]={type:"number",min:1,max:64,step:1,precision:1},B.prototype.getPreviewTexture=function(){return this._temp_texture2},B.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){B._shader||(B._shader_copy=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,B.pixel_shader_copy),B._shader_avg=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,B.pixel_shader_avg)); +var b=Math.clamp(this.properties.samples,0,64),d=this.frame,c=this.properties.frames_interval;if(0==c||0==d%c){d=this._temp_texture;d&&d.type==a.type&&d.width==b||(d={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(b,1,d),this._temp_texture2=new GL.Texture(b,1,d),this._temp_texture_out=new GL.Texture(1,1,d));var g=this._temp_texture,f=this._temp_texture2,e=B._shader_copy,l=B._shader_avg,h=this._uniforms;h.u_samples=b;h.u_isamples=1/b;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST); +g.drawTo(function(){f.bind(1);a.toViewport(e,h)});this._temp_texture_out.drawTo(function(){g.toViewport(l,h)});this.setOutputData(0,this._temp_texture_out);this._temp_texture=f;this._temp_texture2=g}else this.setOutputData(0,this._temp_texture_out);this.setOutputData(1,this._temp_texture2);this.frame++}},B.pixel_shader_copy="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tif( v_coord.x <= u_isamples )\n\t\t\t\tgl_FragColor = texture2D( u_texture, vec2(0.5) );\n\t\t\telse\n\t\t\t\tgl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\t\t}\n\t\t", +B.pixel_shader_avg="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform int u_samples;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\tfor(int i = 0; i < 64; ++i)\n\t\t\t{\n\t\t\t\tcolor += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\t\t\t\tif(i == (u_samples - 1))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tgl_FragColor = color * u_isamples;\n\t\t}\n\t\t",G.registerNodeType("texture/linear_avg_smooth", +B),u.title="Image to Texture",u.desc="Uploads an image to the GPU",u.prototype.onExecute=function(){var a=this.getInputData(0);if(a){var b=a.videoWidth||a.width,d=a.videoHeight||a.height;if(a.gltexture)this.setOutputData(0,a.gltexture);else{var c=this._temp_texture;c&&c.width==b&&c.height==d||(this._temp_texture=new GL.Texture(b,d,{format:gl.RGBA,filter:gl.LINEAR}));try{this._temp_texture.uploadImage(a)}catch(g){console.error("image comes from an unsafe location, cannot be uploaded to webgl: "+g); +return}this.setOutputData(0,this._temp_texture)}}},G.registerNodeType("texture/imageToTexture",u),F.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},F.title="LUT",F.desc="Apply LUT to Texture",F.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture)); +if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.bindTexture(gl.TEXTURE_2D,null);var d=this.properties.intensity;this.isInputConnected(2)&&(this.properties.intensity=d=this.getInputData(2));this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);this._tex.drawTo(function(){b.bind(1);a.toViewport(F._shader,{u_texture:0,u_textureB:1, +u_amount:d})});this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}},F.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_amount;\n\t\t\n\t\tvoid main() {\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\t\t\t mediump vec2 quad1;\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\t\t\t mediump vec2 quad2;\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\t\t\t highp vec2 texPos1;\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t highp vec2 texPos2;\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\t\t}\n\t\t", +G.registerNodeType("texture/LUT",F),q.title="Texture to Channels",q.desc="Split texture channels",q.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this._channels||(this._channels=Array(4));for(var b=gl.RGB,d=0,c=0;4>c;c++)this.isOutputConnected(c)?(this._channels[c]&&this._channels[c].width==a.width&&this._channels[c].height==a.height&&this._channels[c].type==a.type&&this._channels[c].format==b||(this._channels[c]=new GL.Texture(a.width,a.height,{type:a.type,format:b,filter:gl.LINEAR})), d++):this._channels[c]=null;if(d){gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);for(var g=Mesh.getScreenQuad(),f=q._shader,e=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],c=0;4>c;c++)this._channels[c]&&(this._channels[c].drawTo(function(){a.bind(0);f.uniforms({u_texture:0,u_mask:e[c]}).draw(g)}),this.setOutputData(c,this._channels[c]))}}},q.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec4 u_mask;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\t\t}\n\t\t", -F.registerNodeType("texture/textureChannels",q),p.title="Channels to Texture",p.desc="Split texture channels",p.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},p.prototype.onExecute=function(){var a=c.getWhiteTexture(),b=this.getInputData(0)||a,d=this.getInputData(1)||a,g=this.getInputData(2)||a,f=this.getInputData(3)||a;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad();p._shader||(p._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,p.pixel_shader));var l=p._shader, +G.registerNodeType("texture/textureChannels",q),p.title="Channels to Texture",p.desc="Split texture channels",p.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},p.prototype.onExecute=function(){var a=c.getWhiteTexture(),b=this.getInputData(0)||a,d=this.getInputData(1)||a,g=this.getInputData(2)||a,f=this.getInputData(3)||a;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad();p._shader||(p._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,p.pixel_shader));var l=p._shader, a=Math.max(b.width,d.width,g.width,f.width),h=Math.max(b.height,d.height,g.height,f.height),k=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._texture&&this._texture.width==a&&this._texture.height==h&&this._texture.type==k||(this._texture=new GL.Texture(a,h,{type:k,format:gl.RGBA,filter:gl.LINEAR}));a=this._color;a[0]=this.properties.R;a[1]=this.properties.G;a[2]=this.properties.B;a[3]=this.properties.A;var q=this._uniforms;this._texture.drawTo(function(){b.bind(0); d.bind(1);g.bind(2);f.bind(3);l.uniforms(q).draw(e)});this.setOutputData(0,this._texture)},p.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureR;\n\t\tuniform sampler2D u_textureG;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform vec4 u_color;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = u_color * vec4( \t\t\t\t\ttexture2D(u_textureR, v_coord).r,\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\t\t}\n\t\t", -F.registerNodeType("texture/channelsTexture",p),l.title="Color",l.desc="Generates a 1x1 texture with a constant color",l.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},l.prototype.onDrawBackground=function(a){var b=this.properties.color;a.fillStyle="rgb("+Math.floor(255*Math.clamp(b[0],0,1))+","+Math.floor(255*Math.clamp(b[1],0,1))+","+Math.floor(255*Math.clamp(b[2],0,1))+")";this.flags.collapsed?this.boxcolor=a.fillStyle:a.fillRect(0,0,this.size[0],this.size[1])},l.prototype.onExecute= +G.registerNodeType("texture/channelsTexture",p),l.title="Color",l.desc="Generates a 1x1 texture with a constant color",l.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},l.prototype.onDrawBackground=function(a){var b=this.properties.color;a.fillStyle="rgb("+Math.floor(255*Math.clamp(b[0],0,1))+","+Math.floor(255*Math.clamp(b[1],0,1))+","+Math.floor(255*Math.clamp(b[2],0,1))+")";this.flags.collapsed?this.boxcolor=a.fillStyle:a.fillRect(0,0,this.size[0],this.size[1])},l.prototype.onExecute= function(){var a=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._tex&&this._tex.type==a||(this._tex=new GL.Texture(1,1,{format:gl.RGBA,type:a,minFilter:gl.NEAREST}));a=this.properties.color;if(this.inputs)for(var b=0;ba.width?d: +G.registerNodeType("texture/gradient",a),b.title="Mix",b.desc="Generates a texture mixing two textures",b.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},b.prototype.onExecute=function(){var a=this.getInputData(0);if(this.isOutputConnected(0))if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else{var d=this.getInputData(1);if(a&&d){var g=this.getInputData(2),f=this.getInputData(3);this._tex=c.getTargetTexture(this.properties.size_from_biggest&&d.width>a.width?d: a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad(),l=null,h=this._uniforms;g?(l=b._shader_tex,l||(l=b._shader_tex=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader,{MIX_TEX:""}))):(l=b._shader_factor,l||(l=b._shader_factor=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader)),f=null==f?this.properties.factor:f,h.u_mix.set([f,f,f,f]));var k=this.properties.invert;this._tex.drawTo(function(){a.bind(k?1:0);d.bind(k?0:1);g&&g.bind(2); l.uniforms(h).draw(e)});this.setOutputData(0,this._tex)}}},b.prototype.onGetInputs=function(){return[["factor","number"]]},b.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform sampler2D u_textureB;\n\t\t#ifdef MIX_TEX\n\t\t\tuniform sampler2D u_textureMix;\n\t\t#else\n\t\t\tuniform vec4 u_mix;\n\t\t#endif\n\t\t\n\t\tvoid main() {\n\t\t\t#ifdef MIX_TEX\n\t\t\t vec4 f = texture2D(u_textureMix, v_coord);\n\t\t\t#else\n\t\t\t vec4 f = u_mix;\n\t\t\t#endif\n\t\t gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\t\t}\n\t\t", -F.registerNodeType("texture/mix",b),d.title="Edges",d.desc="Detects edges",d.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},d.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else if(a){this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var b=Mesh.getScreenQuad(),g=d._shader,f=this.properties.invert,e=this.properties.factor, +G.registerNodeType("texture/mix",b),d.title="Edges",d.desc="Detects edges",d.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},d.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else if(a){this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var b=Mesh.getScreenQuad(),g=d._shader,f=this.properties.invert,e=this.properties.factor, l=this.properties.threshold?1:0;this._tex.drawTo(function(){a.bind(0);g.uniforms({u_texture:0,u_isize:[1/a.width,1/a.height],u_factor:e,u_threshold:l,u_invert:f?1:0}).draw(b)});this.setOutputData(0,this._tex)}}},d.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_isize;\n\t\tuniform int u_invert;\n\t\tuniform float u_factor;\n\t\tuniform float u_threshold;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 center = texture2D(u_texture, v_coord);\n\t\t\tvec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\t\t\tvec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\t\t\tvec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\t\t\tvec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\t\t\tvec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\t\t\tdiff *= u_factor;\n\t\t\tif(u_invert == 1)\n\t\t\t\tdiff.xyz = vec3(1.0) - diff.xyz;\n\t\t\tif( u_threshold == 0.0 )\n\t\t\t\tgl_FragColor = vec4( diff.xyz, center.a );\n\t\t\telse\n\t\t\t\tgl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\t\t}\n\t\t", -F.registerNodeType("texture/edges",d),g.title="Depth Range",g.desc="Generates a texture with a depth range",g.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a){var b=gl.UNSIGNED_BYTE;this.properties.high_precision&&(b=gl.half_float_ext?gl.HALF_FLOAT_OES:gl.FLOAT);this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&&this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGBA, +G.registerNodeType("texture/edges",d),g.title="Depth Range",g.desc="Generates a texture with a depth range",g.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a){var b=gl.UNSIGNED_BYTE;this.properties.high_precision&&(b=gl.half_float_ext?gl.HALF_FLOAT_OES:gl.FLOAT);this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&&this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGBA, filter:gl.LINEAR}));var d=this._uniforms,b=this.properties.distance;this.isInputConnected(1)&&(b=this.getInputData(1),this.properties.distance=b);var c=this.properties.range;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.range=c);d.u_distance=b;d.u_range=c;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var f=Mesh.getScreenQuad();g._shader||(g._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,g.pixel_shader),g._shader_onlydepth=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,g.pixel_shader, {ONLY_DEPTH:""}));var e=this.properties.only_depth?g._shader_onlydepth:g._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1,1E3];d.u_camera_planes=b;this._temp_texture.drawTo(function(){a.bind(0);e.uniforms(d).draw(f)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},g.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform float u_distance;\n\t\tuniform float u_range;\n\t\t\n\t\tfloat LinearDepth()\n\t\t{\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\t\t\tdepth = depth * 2.0 - 1.0;\n\t\t\treturn zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t\tfloat depth = LinearDepth();\n\t\t\t#ifdef ONLY_DEPTH\n\t\t\t gl_FragColor = vec4(depth);\n\t\t\t#else\n\t\t\t\tfloat diff = abs(depth * u_camera_planes.y - u_distance);\n\t\t\t\tfloat dof = 1.0;\n\t\t\t\tif(diff <= u_range)\n\t\t\t\t\tdof = diff / u_range;\n\t\t\t gl_FragColor = vec4(dof);\n\t\t\t#endif\n\t\t}\n\t\t", -F.registerNodeType("texture/depth_range",g),f.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},f.title="Linear Depth",f.desc="Creates a color texture with linear depth",f.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a&&(a.format==gl.DEPTH_COMPONENT||a.format==gl.DEPTH_STENCIL)){var b=this.properties.precision==c.HIGH?gl.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&& +G.registerNodeType("texture/depth_range",g),f.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},f.title="Linear Depth",f.desc="Creates a color texture with linear depth",f.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a&&(a.format==gl.DEPTH_COMPONENT||a.format==gl.DEPTH_STENCIL)){var b=this.properties.precision==c.HIGH?gl.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&& this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGB,filter:gl.LINEAR}));var d=this._uniforms;d.u_invert=this.properties.invert?1:0;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var g=Mesh.getScreenQuad();f._shader||(f._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,f.pixel_shader));var e=f._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1, 1E3];d.u_camera_planes=b;d.u_ires.set([0,0]);this._temp_texture.drawTo(function(){a.bind(0);e.uniforms(d).draw(g)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},f.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform int u_invert;\n\t\tuniform vec2 u_ires;\n\t\t\n\t\tvoid main() {\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord + u_ires*0.5).x * 2.0 - 1.0;\n\t\t\tfloat f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t\tif( u_invert == 1 )\n\t\t\t\tf = 1.0 - f;\n\t\t\tgl_FragColor = vec4(vec3(f),1.0);\n\t\t}\n\t\t", -F.registerNodeType("texture/linear_depth",f),r.title="Blur",r.desc="Blur a texture",r.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},r.max_iterations=20,r.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._final_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(b=this._final_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));var d=this.properties.iterations;this.isInputConnected(1)&& -(d=this.getInputData(1),this.properties.iterations=d);d=Math.min(Math.floor(d),r.max_iterations);if(0==d)this.setOutputData(0,a);else{var c=this.properties.intensity;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.intensity=c);var g=F.camera_aspect;g||void 0===window.gl||(g=gl.canvas.height/gl.canvas.width);g||(g=1);var g=this.properties.preserve_aspect?g:1,f=this.properties.scale||[1,1];a.applyBlur(g*f[0],f[1],c,b);for(a=1;a>=1;1<(d|0)&&(d>>=1);if(2>b)break;k=l[m]=GL.Texture.getTemporary(b,d,g);r[0]=1/q.width;r[1]=1/q.height;q.blit(k,h.uniforms(e));q=k}this.isOutputConnected(2)&&(b=this._average_texture,b&&b.type==a.type&&b.format==a.format||(b=this._average_texture=new GL.Texture(1,1,{type:a.type,format:a.format,filter:gl.LINEAR})),r[0]=1/q.width,r[1]=1/q.height,e.u_intensity=n,e.u_delta=1,q.blit(b,h.uniforms(e)),this.setOutputData(2,b));gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE);e.u_intensity= this.getInputOrProperty("persistence");e.u_delta=0.5;for(m-=2;0<=m;m--)k=l[m],l[m]=null,r[0]=1/q.width,r[1]=1/q.height,q.blit(k,h.uniforms(e)),GL.Texture.releaseTemporary(q),q=k;gl.disable(gl.BLEND);this.isOutputConnected(1)&&(l=this._glow_texture,l&&l.width==a.width&&l.height==a.height&&l.type==f&&l.format==a.format||(l=this._glow_texture=new GL.Texture(a.width,a.height,{type:f,format:a.format,filter:gl.LINEAR})),q.blit(l),this.setOutputData(1,l));if(this.isOutputConnected(0)){l=this._final_texture; -l&&l.width==a.width&&l.height==a.height&&l.type==f&&l.format==a.format||(l=this._final_texture=new GL.Texture(a.width,a.height,{type:f,format:a.format,filter:gl.LINEAR}));var s=this.getInputData(1),u=this.getInputOrProperty("dirt_factor");e.u_intensity=n;h=s?t._dirt_final_shader:t._final_shader;h||(h=s?t._dirt_final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,t.final_pixel_shader,{USE_DIRT:""}):t._final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,t.final_pixel_shader));l.drawTo(function(){a.bind(0); -q.bind(1);s&&(h.setUniform("u_dirt_factor",u),h.setUniform("u_dirt_texture",s.bind(2)));h.toViewport(e)});this.setOutputData(0,l)}GL.Texture.releaseTemporary(q)}},t.cut_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform float u_threshold;\n\tvoid main() {\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\t}",t.scale_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\t}", -t.final_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform sampler2D u_glow_texture;\n\t#ifdef USE_DIRT\n\t\tuniform sampler2D u_dirt_texture;\n\t#endif\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\tuniform float u_dirt_factor;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tvec4 glow = sampleBox( v_coord );\n\t\t#ifdef USE_DIRT\n\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\t\t#endif\n\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\t}", -F.registerNodeType("texture/glow",t),I.title="Kuwahara Filter",I.desc="Filters a texture giving an artistic oil canvas painting",I.max_radius=10,I._shaders=[],I.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));b=this.properties.radius;b=Math.min(Math.floor(b),I.max_radius);if(0== -b)this.setOutputData(0,a);else{var d=this.properties.intensity,c=F.camera_aspect;c||void 0===window.gl||(c=gl.canvas.height/gl.canvas.width);c||(c=1);c=this.properties.preserve_aspect?c:1;I._shaders[b]||(I._shaders[b]=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,I.pixel_shader,{RADIUS:b.toFixed(0)}));var g=I._shaders[b],f=GL.Mesh.getScreenQuad();a.bind(0);this._temp_texture.drawTo(function(){g.uniforms({u_texture:0,u_intensity:d,u_resolution:[a.width,a.height],u_iResolution:[1/a.width,1/a.height]}).draw(f)}); -this.setOutputData(0,this._temp_texture)}}},I.pixel_shader="\nprecision highp float;\nvarying vec2 v_coord;\nuniform sampler2D u_texture;\nuniform float u_intensity;\nuniform vec2 u_resolution;\nuniform vec2 u_iResolution;\n#ifndef RADIUS\n\t#define RADIUS 7\n#endif\nvoid main() {\n\n\tconst int radius = RADIUS;\n\tvec2 fragCoord = v_coord;\n\tvec2 src_size = u_iResolution;\n\tvec2 uv = v_coord;\n\tfloat n = float((radius + 1) * (radius + 1));\n\tint i;\n\tint j;\n\tvec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\tvec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\tvec3 c;\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm0 += c;\n\t\t\ts0 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm1 += c;\n\t\t\ts1 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm2 += c;\n\t\t\ts2 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm3 += c;\n\t\t\ts3 += c * c;\n\t\t}\n\t}\n\t\n\tfloat min_sigma2 = 1e+2;\n\tm0 /= n;\n\ts0 = abs(s0 / n - m0 * m0);\n\t\n\tfloat sigma2 = s0.r + s0.g + s0.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m0, 1.0);\n\t}\n\t\n\tm1 /= n;\n\ts1 = abs(s1 / n - m1 * m1);\n\t\n\tsigma2 = s1.r + s1.g + s1.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m1, 1.0);\n\t}\n\t\n\tm2 /= n;\n\ts2 = abs(s2 / n - m2 * m2);\n\t\n\tsigma2 = s2.r + s2.g + s2.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m2, 1.0);\n\t}\n\t\n\tm3 /= n;\n\ts3 = abs(s3 / n - m3 * m3);\n\t\n\tsigma2 = s3.r + s3.g + s3.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m3, 1.0);\n\t}\n}\n", -F.registerNodeType("texture/kuwahara",I),J.title="XDoG Filter",J.desc="Filters a texture giving an artistic ink style",J.max_radius=10,J._shaders=[],J.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));J._xdog_shader||(J._xdog_shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,J.xdog_pixel_shader)); -var d=J._xdog_shader,c=GL.Mesh.getScreenQuad(),g=this.properties.sigma,f=this.properties.k,e=this.properties.p,l=this.properties.epsilon,h=this.properties.phi;a.bind(0);this._temp_texture.drawTo(function(){d.uniforms({src:0,sigma:g,k:f,p:e,epsilon:l,phi:h,cvsWidth:a.width,cvsHeight:a.height}).draw(c)});this.setOutputData(0,this._temp_texture)}},J.xdog_pixel_shader="\nprecision highp float;\nuniform sampler2D src;\n\nuniform float cvsHeight;\nuniform float cvsWidth;\n\nuniform float sigma;\nuniform float k;\nuniform float p;\nuniform float epsilon;\nuniform float phi;\nvarying vec2 v_coord;\n\nfloat cosh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat cosH = (tmp + 1.0 / tmp) / 2.0;\n\treturn cosH;\n}\n\nfloat tanh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\treturn tanH;\n}\n\nfloat sinh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat sinH = (tmp - 1.0 / tmp) / 2.0;\n\treturn sinH;\n}\n\nvoid main(void){\n\tvec3 destColor = vec3(0.0);\n\tfloat tFrag = 1.0 / cvsHeight;\n\tfloat sFrag = 1.0 / cvsWidth;\n\tvec2 Frag = vec2(sFrag,tFrag);\n\tvec2 uv = gl_FragCoord.st;\n\tfloat twoSigmaESquared = 2.0 * sigma * sigma;\n\tfloat twoSigmaRSquared = twoSigmaESquared * k * k;\n\tint halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\tconst int MAX_NUM_ITERATION = 99999;\n\tvec2 sum = vec2(0.0);\n\tvec2 norm = vec2(0.0);\n\n\tfor(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\t\tint i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\t\tint j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\t\tfloat d = length(vec2(i,j));\n\t\tvec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\t\t\t\t\t\t\texp( -d * d / twoSigmaRSquared ));\n\n\t\tvec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\t\tnorm += kernel;\n\t\tsum += kernel * L;\n\t}\n\n\tsum /= norm;\n\n\tfloat H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\tfloat edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\tdestColor = vec3(edge);\n\tgl_FragColor = vec4(destColor, 1.0);\n}", -F.registerNodeType("texture/xDoG",J),H.title="Webcam",H.desc="Webcam texture",H.is_webcam_open=!1,H.prototype.openStream=function(){function a(d){H.is_webcam_open=!1;console.log("Webcam rejected",d);b._webcam_stream=!1;b.boxcolor="red";b.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](a);var b=this}},H.prototype.closeStream=function(){if(this._webcam_stream){var a= -this._webcam_stream.getTracks();if(a.length)for(var b=0;b=this.size[1]||!this._video||(a.save(),a.webgl?this._video_texture&&a.drawImage(this._video_texture,0,0,this.size[0],this.size[1]):a.drawImage(this._video, -0,0,this.size[0],this.size[1]),a.restore())},H.prototype.onExecute=function(){null!=this._webcam_stream||this._waiting_confirmation||this.openStream();if(this._video&&this._video.videoWidth){var a=this._video.videoWidth,b=this._video.videoHeight,d=this._video_texture;d&&d.width==a&&d.height==b||(this._video_texture=new GL.Texture(a,b,{format:gl.RGB,filter:gl.LINEAR}));this._video_texture.uploadImage(this._video);this._video_texture.version=++this.version;this.properties.texture_name&&(c.getTexturesContainer()[this.properties.texture_name]= -this._video_texture);this.setOutputData(0,this._video_texture);for(a=1;a (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\t\tint i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\t\tint j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\t\tfloat d = length(vec2(i,j));\n\t\tvec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\t\t\t\t\t\t\texp( -d * d / twoSigmaRSquared ));\n\n\t\tvec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\t\tnorm += kernel;\n\t\tsum += kernel * L;\n\t}\n\n\tsum /= norm;\n\n\tfloat H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\tfloat edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\tdestColor = vec3(edge);\n\tgl_FragColor = vec4(destColor, 1.0);\n}", +G.registerNodeType("texture/xDoG",K),I.title="Webcam",I.desc="Webcam texture",I.is_webcam_open=!1,I.prototype.openStream=function(){function a(d){I.is_webcam_open=!1;console.log("Webcam rejected",d);b._webcam_stream=!1;b.boxcolor="red";b.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](a);var b=this}},I.prototype.closeStream=function(){if(this._webcam_stream){var a= +this._webcam_stream.getTracks();if(a.length)for(var b=0;b=this.size[1]||!this._video||(a.save(),a.webgl?this._video_texture&&a.drawImage(this._video_texture,0,0,this.size[0],this.size[1]):a.drawImage(this._video, +0,0,this.size[0],this.size[1]),a.restore())},I.prototype.onExecute=function(){null!=this._webcam_stream||this._waiting_confirmation||this.openStream();if(this._video&&this._video.videoWidth){var a=this._video.videoWidth,b=this._video.videoHeight,d=this._video_texture;d&&d.width==a&&d.height==b||(this._video_texture=new GL.Texture(a,b,{format:gl.RGB,filter:gl.LINEAR}));this._video_texture.uploadImage(this._video);this._video_texture.version=++this.version;this.properties.texture_name&&(c.getTexturesContainer()[this.properties.texture_name]= +this._video_texture);this.setOutputData(0,this._video_texture);for(a=1;aMath.abs(d))return c[1];d=(a-c[0])/d;return c[1]*(1-d)+g[1]*d}}return 0}},s.prototype.updateCurve=function(){for(var a=this._values,b=a.length/4,d=this.properties.split_channels,c=0;cTex",v.desc="Generates or applies a curve to a texture",v.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},v.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a){var b=this.properties.channels,d=this.properties.width,g=this.properties.height;d&&g||(d=Math.floor(a.length/b),g=1);var f=gl.RGBA;3==b?f=gl.RGB:1==b&&(f=gl.LUMINANCE);var b=this._temp_texture,e=c.getTextureType(this.properties.precision); +b&&b.width==d&&b.height==g&&b.type==e||(b=this._temp_texture=new GL.Texture(d,g,{type:e,format:f,filter:gl.LINEAR}));b.uploadData(a);this.setOutputData(0,b)}}},G.registerNodeType("texture/fromdata",v),H.title="Curve",H.desc="Generates or applies a curve to a texture",H.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},H.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0),b=this._temp_texture;if(a){var d=c.getTextureType(this.properties.precision,a); +b&&b.type==d&&b.width==a.width&&b.height==a.height&&b.format==a.format||(b=this._temp_texture=new GL.Texture(a.width,a.height,{type:d,format:a.format,filter:gl.LINEAR}));var g=H._shader;g||(g=H._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,H.pixel_shader));!this._must_update&&this._curve_texture||this.updateCurve();var f=this._uniforms,e=this._curve_texture;b.drawTo(function(){gl.disable(gl.DEPTH_TEST);a.bind(0);e.bind(1);g.uniforms(f).draw(GL.Mesh.getScreenQuad())});this.setOutputData(0,b)}else!this._must_update&& +this._curve_texture||this.updateCurve(),this.setOutputData(0,this._curve_texture)}},H.prototype.sampleCurve=function(a,b){if(b=b||this._points.RGB){for(var d=0;dMath.abs(d))return c[1];d=(a-c[0])/d;return c[1]*(1-d)+g[1]*d}}return 0}},H.prototype.updateCurve=function(){for(var a=this._values,b=a.length/4,d=this.properties.split_channels,c=0;c=f;){e=0.5*(l+f)|0;c=a[e];if(c==d)break;if(f==l-1)return f;c=f;){e=0.5*(l+f)|0;c=a[e];if(c==d)break;if(f==l-1)return f;ca&&(a=1);this.points&&this.points.length==3*a||(this.points=new Float32Array(3*a));this.properties.generate_normals?this.normals&&this.normals.length==this.points.length||(this.normals=new Float32Array(this.points.length)):this.normals=null;var d=this._last_radius||this.properties.radius,c=this.properties.mode,f=this.getInputData(0);this._old_obj_version=f?f._version:null;this.points=m.generatePoints(d,a,c,this.points, @@ -569,64 +576,64 @@ this.normals,this.properties.regular,f);this.version++};m.generatePoints=functio c==m.OBJECT_UNIFORMLY?m.generateFromObject(f,e,k,h,!0):c==m.OBJECT_INSIDE?m.generateFromInsideObject(f,k,h):console.warn("wrong mode in LGraphPoints3D");return f};m.generateSphericalNormals=function(a,d){for(var c=new Float32Array(3),f=0;fe||vl&&lh))break}this.geometry.indices=this.indices=new Uint32Array(k)}this.indices&&this.indices.length?(this.geometry.indices=this.indices,this.setOutputData(0,this.geometry)):this.setOutputData(0,null)}};u.registerNodeType("geometry/connectPoints",w);"undefined"!=typeof GL&&(D.title="to geometry",D.desc="converts a mesh to geometry",D.prototype.onExecute= +l=this.vertices;l&&this.vertices.length==a.vertices.length?l.set(a.vertices):l=this.vertices=new Float32Array(a.vertices);for(f=0;fe||wl&&lh))break}this.geometry.indices=this.indices=new Uint32Array(k)}this.indices&&this.indices.length?(this.geometry.indices=this.indices,this.setOutputData(0,this.geometry)):this.setOutputData(0,null)}};u.registerNodeType("geometry/connectPoints",t);"undefined"!=typeof GL&&(D.title="to geometry",D.desc="converts a mesh to geometry",D.prototype.onExecute= function(){var a=this.getInputData(0);if(a){if(a!=this.last_mesh){this.last_mesh=a;for(i in a.vertexBuffers)this.geometry[i]=a.vertexBuffers[i].data;a.indexBuffers.triangles&&(this.geometry.indices=a.indexBuffers.triangles.data);this.geometry._id=c();this.geometry._version=0}this.setOutputData(0,this.geometry);this.geometry&&this.setOutputData(1,this.geometry.vertices)}},u.registerNodeType("geometry/toGeometry",D),A.title="Geo to Mesh",A.prototype.updateMesh=function(a){this.mesh||(this.mesh=new GL.Mesh); for(var d in a)if("_"!=d[0]){var c=a[d],f=GL.Mesh.common_buffers[d];if(f||"indices"==d){var f=f?f.spacing:3,e=this.mesh.vertexBuffers[d];e&&e.data.length==c.length?(e.data.set(c),e.upload(GL.DYNAMIC_DRAW)):e=new GL.Buffer("indices"==d?GL.ELEMENT_ARRAY_BUFFER:GL.ARRAY_BUFFER,c,f,GL.DYNAMIC_DRAW);this.mesh.addBuffer(d,e)}}if(this.mesh.vertexBuffers.normals&&this.mesh.vertexBuffers.normals.data.length!=this.mesh.vertexBuffers.vertices.data.length){c=new Float32Array([0,1,0]);f=new Float32Array(this.mesh.vertexBuffers.vertices.data.length); for(d=0;d=c.NOTEON||h<=c.NOTEOFF)this.channel=e&15};Object.defineProperty(c.prototype,"velocity",{get:function(){return this.cmd==c.NOTEON?this.data[2]:-1},set:function(c){this.data[2]=c},enumerable:!0});c.notes="A A# B C C# D D# E F F# G G#".split(" ");c.note_to_index={A:0,"A#":1,B:2,C:3,"C#":4,D:5,"D#":6,E:7,F:8,"F#":9,G:10,"G#":11};Object.defineProperty(c.prototype,"note",{get:function(){return this.cmd!= c.NOTEON?-1:c.toNoteString(this.data[1],!0)},set:function(c){throw"notes cannot be assigned this way, must modify the data[1]";},enumerable:!0});Object.defineProperty(c.prototype,"octave",{get:function(){return this.cmd!=c.NOTEON?-1:Math.floor((this.data[1]-24)/12+1)},set:function(c){throw"octave cannot be assigned this way, must modify the data[1]";},enumerable:!0});c.prototype.getPitch=function(){return 440*Math.pow(2,(this.data[1]-69)/12)};c.computePitch=function(c){return 440*Math.pow(2,(c-69)/ 12)};c.prototype.getCC=function(){return this.data[1]};c.prototype.getCCValue=function(){return this.data[2]};c.prototype.getPitchBend=function(){return this.data[1]+(this.data[2]<<7)-8192};c.computePitchBend=function(c,e){return c+(e<<7)-8192};c.prototype.setCommandFromString=function(e){this.cmd=c.computeCommandFromString(e)};c.computeCommandFromString=function(e){if(!e)return 0;if(e&&e.constructor===Number)return e;e=e.toUpperCase();switch(e){case "NOTE ON":case "NOTEON":return c.NOTEON;case "NOTE OFF":case "NOTEOFF":return c.NOTEON; @@ -634,7 +641,7 @@ case "KEY PRESSURE":case "KEYPRESSURE":return c.KEYPRESSURE;case "CONTROLLER CHA l=(e-21)%12;0>l&&(l=12+l);return c.notes[l]+(h?"":a)};c.NoteStringToPitch=function(e){e=e.toUpperCase();var h=e[0],l=4;"#"==e[1]?(h+="#",2this.properties.max_value)return;this.trigger("on_midi",h)}};u.registerNodeType("midi/filter",k);C.title="MIDIEvent";C.desc="Create a MIDI Event";C.color="#243";C.prototype.onAction=function(e,h){"assign"==e?(this.properties.channel=h.channel,this.properties.cmd=h.cmd,this.properties.value1=h.data[1],this.properties.value2=h.data[2],h.cmd== c.NOTEON?this.gate=!0:h.cmd==c.NOTEOFF&&(this.gate=!1)):(h=this.midi_event,h.channel=this.properties.channel,this.properties.cmd&&this.properties.cmd.constructor===String?h.setCommandFromString(this.properties.cmd):h.cmd=this.properties.cmd,h.data[0]=h.cmd|h.channel,h.data[1]=Number(this.properties.value1),h.data[2]=Number(this.properties.value2),this.trigger("on_midi",h))};C.prototype.onExecute=function(){var e=this.properties;if(this.inputs)for(var h=0;hc;++c)this.valid_notes[c]=-1!=this.notes_pitches.indexOf(c);for(c=0;12>c;++c)if(this.valid_notes[c])this.offset_notes[c]=0;else for(var e=1;12>e;++e){if(this.valid_notes[(c-e)%12]){this.offset_notes[c]=-e;break}if(this.valid_notes[(c+e)%12]){this.offset_notes[c]=e;break}}};A.prototype.onAction=function(e,h){h&&h.constructor===c&&(h.data[0]==c.NOTEON||h.data[0]==c.NOTEOFF?(this.midi_event=new c,this.midi_event.setup(h.data),this.midi_event.data[1]+=this.offset_notes[c.note_to_index[h.note]], +t.processScale(c);for(c=0;12>c;++c)this.valid_notes[c]=-1!=this.notes_pitches.indexOf(c);for(c=0;12>c;++c)if(this.valid_notes[c])this.offset_notes[c]=0;else for(var e=1;12>e;++e){if(this.valid_notes[(c-e)%12]){this.offset_notes[c]=-e;break}if(this.valid_notes[(c+e)%12]){this.offset_notes[c]=e;break}}};A.prototype.onAction=function(e,h){h&&h.constructor===c&&(h.data[0]==c.NOTEON||h.data[0]==c.NOTEOFF?(this.midi_event=new c,this.midi_event.setup(h.data),this.midi_event.data[1]+=this.offset_notes[c.note_to_index[h.note]], this.trigger("out",this.midi_event)):this.trigger("out",h))};A.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&c!=this._current_scale&&this.processScale(c)};u.registerNodeType("midi/quantize",A);e.title="MIDI fromFile";e.desc="Plays a MIDI file";e.color="#243";e.prototype.onAction=function(c){"play"==c?this.play():"pause"==c&&(this._playing=!this._playing)};e.prototype.onPropertyChanged=function(c,e){"url"==c&&this.loadMIDIFile(e)};e.prototype.onExecute=function(){if(this._midi&& this._playing){this._current_time+=this.graph.elapsed_time;for(var e=100*this._current_time,h=0;hb;b++)for(var d=0;dg+f||c[1]>d))return b}}return-1};y.prototype.onAction=function(e,h){if("reset"==e)for(var l=0;lh[1])){var l=this.getKeyIndex(h);this.keys[l]=!0;this._last_key=l;var l=12*(this.properties.start_octave-1)+29+l,a=new c;a.setup([c.NOTEON,l,100]);this.trigger("note",a);return!0}};y.prototype.onMouseMove=function(e,h){if(!(0>h[1]||-1==this._last_key)){this.setDirtyCanvas(!0); -var l=this.getKeyIndex(h);if(this._last_key==l)return!0;this.keys[this._last_key]=!1;var a=12*(this.properties.start_octave-1)+29+this._last_key,b=new c;b.setup([c.NOTEOFF,a,100]);this.trigger("note",b);this.keys[l]=!0;a=12*(this.properties.start_octave-1)+29+l;b=new c;b.setup([c.NOTEON,a,100]);this.trigger("note",b);this._last_key=l;return!0}};y.prototype.onMouseUp=function(e,h){if(!(0>h[1])){var l=this.getKeyIndex(h);this.keys[l]=!1;this._last_key=-1;var l=12*(this.properties.start_octave-1)+29+ -l,a=new c;a.setup([c.NOTEOFF,l,100]);this.trigger("note",a);return!0}};u.registerNodeType("midi/keys",y)})(this); -(function(B){function c(){this.properties={src:"",gain:0.5,loop:!0,autoplay:!0,playbackRate:1};this._loading_audio=!1;this._audiobuffer=null;this._audionodes=[];this._last_sourcenode=null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=p.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain;this.properties.src&&this.loadSound(this.properties.src)}function m(){this.properties={gain:0.5};this._audionodes=[];this._media_stream= +e._last_pos=0;e._time=0}};e.prototype.loadMIDIFile=function(c){var e=this;u.fetchFile(c,"arraybuffer",function(c){e.boxcolor="#AFA";e._midi=MidiParser.parse(new Uint8Array(c));e.properties.autoplay&&e.play()},function(c){e.boxcolor="#FAA";e._midi=null})};e.prototype.onDropFile=function(c){this.properties.url="";this.loadMIDIFile(c)};u.registerNodeType("midi/fromFile",e);y.title="MIDI Play";y.desc="Plays a MIDI note";y.color="#243";y.prototype.onAction=function(e,h){if(h&&h.constructor===c){if(this.instrument&& +h.data[0]==c.NOTEON){var l=h.note;if(!l||"undefined"==l||l.constructor!==String)return;this.instrument.play(l,h.octave,this.properties.duration,this.properties.volume)}this.trigger("note",h)}};y.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&(this.properties.volume=c);c=this.getInputData(2);null!=c&&(this.properties.duration=c)};u.registerNodeType("midi/play",y);B.title="MIDI Keys";B.desc="Keyboard to play notes";B.color="#243";B.keys=[{x:0,w:1,h:1,t:0},{x:0.75,w:0.5,h:0.6,t:1}, +{x:1,w:1,h:1,t:0},{x:1.75,w:0.5,h:0.6,t:1},{x:2,w:1,h:1,t:0},{x:2.75,w:0.5,h:0.6,t:1},{x:3,w:1,h:1,t:0},{x:4,w:1,h:1,t:0},{x:4.75,w:0.5,h:0.6,t:1},{x:5,w:1,h:1,t:0},{x:5.75,w:0.5,h:0.6,t:1},{x:6,w:1,h:1,t:0}];B.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){var e=12*this.properties.num_octaves;this.keys.length=e;var h=this.size[0]/(7*this.properties.num_octaves),a=this.size[1];c.globalAlpha=1;for(var b=0;2>b;b++)for(var d=0;dg+f||c[1]>d))return b}}return-1};B.prototype.onAction=function(e,h){if("reset"==e)for(var l=0;lh[1])){var l=this.getKeyIndex(h);this.keys[l]=!0;this._last_key=l;var l=12*(this.properties.start_octave-1)+29+l,a=new c;a.setup([c.NOTEON,l,100]);this.trigger("note",a);return!0}};B.prototype.onMouseMove=function(e,h){if(!(0>h[1]||-1==this._last_key)){this.setDirtyCanvas(!0); +var l=this.getKeyIndex(h);if(this._last_key==l)return!0;this.keys[this._last_key]=!1;var a=12*(this.properties.start_octave-1)+29+this._last_key,b=new c;b.setup([c.NOTEOFF,a,100]);this.trigger("note",b);this.keys[l]=!0;a=12*(this.properties.start_octave-1)+29+l;b=new c;b.setup([c.NOTEON,a,100]);this.trigger("note",b);this._last_key=l;return!0}};B.prototype.onMouseUp=function(e,h){if(!(0>h[1])){var l=this.getKeyIndex(h);this.keys[l]=!1;this._last_key=-1;var l=12*(this.properties.start_octave-1)+29+ +l,a=new c;a.setup([c.NOTEOFF,l,100]);this.trigger("note",a);return!0}};u.registerNodeType("midi/keys",B)})(this); +(function(z){function c(){this.properties={src:"",gain:0.5,loop:!0,autoplay:!0,playbackRate:1};this._loading_audio=!1;this._audiobuffer=null;this._audionodes=[];this._last_sourcenode=null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=p.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain;this.properties.src&&this.loadSound(this.properties.src)}function m(){this.properties={gain:0.5};this._audionodes=[];this._media_stream= null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=p.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain}function n(){this.properties={fftSize:2048,minDecibels:-100,maxDecibels:-10,smoothingTimeConstant:0.5};this.audionode=p.getAudioContext().createAnalyser();this.audionode.graphnode=this;this.audionode.fftSize=this.properties.fftSize;this.audionode.minDecibels=this.properties.minDecibels;this.audionode.maxDecibels= -this.properties.maxDecibels;this.audionode.smoothingTimeConstant=this.properties.smoothingTimeConstant;this.addInput("in","audio");this.addOutput("freqs","array");this.addOutput("samples","array");this._time_bin=this._freq_bin=null}function h(){this.properties={gain:1};this.audionode=p.getAudioContext().createGain();this.addInput("in","audio");this.addInput("gain","number");this.addOutput("out","audio")}function v(){this.properties={impulse_src:"",normalize:!0};this.audionode=p.getAudioContext().createConvolver(); -this.addInput("in","audio");this.addOutput("out","audio")}function k(){this.properties={threshold:-50,knee:40,ratio:12,reduction:-20,attack:0,release:0.25};this.audionode=p.getAudioContext().createDynamicsCompressor();this.addInput("in","audio");this.addOutput("out","audio")}function C(){this.properties={};this.audionode=p.getAudioContext().createWaveShaper();this.addInput("in","audio");this.addInput("shape","waveshape");this.addOutput("out","audio")}function x(){this.properties={gain1:0.5,gain2:0.5}; -this.audionode=p.getAudioContext().createGain();this.audionode1=p.getAudioContext().createGain();this.audionode1.gain.value=this.properties.gain1;this.audionode2=p.getAudioContext().createGain();this.audionode2.gain.value=this.properties.gain2;this.audionode1.connect(this.audionode);this.audionode2.connect(this.audionode);this.addInput("in1","audio");this.addInput("in1 gain","number");this.addInput("in2","audio");this.addInput("in2 gain","number");this.addOutput("out","audio")}function w(){this.properties= +this.properties.maxDecibels;this.audionode.smoothingTimeConstant=this.properties.smoothingTimeConstant;this.addInput("in","audio");this.addOutput("freqs","array");this.addOutput("samples","array");this._time_bin=this._freq_bin=null}function h(){this.properties={gain:1};this.audionode=p.getAudioContext().createGain();this.addInput("in","audio");this.addInput("gain","number");this.addOutput("out","audio")}function x(){this.properties={impulse_src:"",normalize:!0};this.audionode=p.getAudioContext().createConvolver(); +this.addInput("in","audio");this.addOutput("out","audio")}function k(){this.properties={threshold:-50,knee:40,ratio:12,reduction:-20,attack:0,release:0.25};this.audionode=p.getAudioContext().createDynamicsCompressor();this.addInput("in","audio");this.addOutput("out","audio")}function C(){this.properties={};this.audionode=p.getAudioContext().createWaveShaper();this.addInput("in","audio");this.addInput("shape","waveshape");this.addOutput("out","audio")}function w(){this.properties={gain1:0.5,gain2:0.5}; +this.audionode=p.getAudioContext().createGain();this.audionode1=p.getAudioContext().createGain();this.audionode1.gain.value=this.properties.gain1;this.audionode2=p.getAudioContext().createGain();this.audionode2.gain.value=this.properties.gain2;this.audionode1.connect(this.audionode);this.audionode2.connect(this.audionode);this.addInput("in1","audio");this.addInput("in1 gain","number");this.addInput("in2","audio");this.addInput("in2 gain","number");this.addOutput("out","audio")}function t(){this.properties= {A:0.1,D:0.1,S:0.1,R:0.1};this.audionode=p.getAudioContext().createGain();this.audionode.gain.value=0;this.addInput("in","audio");this.addInput("gate","bool");this.addOutput("out","audio");this.gate=!1}function D(){this.properties={delayTime:0.5};this.audionode=p.getAudioContext().createDelay(10);this.audionode.delayTime.value=this.properties.delayTime;this.addInput("in","audio");this.addInput("time","number");this.addOutput("out","audio")}function A(){this.properties={frequency:350,detune:0,Q:1}; -this.addProperty("type","lowpass","enum",{values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")});this.audionode=p.getAudioContext().createBiquadFilter();this.addInput("in","audio");this.addOutput("out","audio")}function e(){this.properties={frequency:440,detune:0,type:"sine"};this.addProperty("type","sine","enum",{values:["sine","square","sawtooth","triangle","custom"]});this.audionode=p.getAudioContext().createOscillator();this.addOutput("out","audio")}function z(){this.properties= -{continuous:!0,mark:-1};this.addInput("data","array");this.addInput("mark","number");this.size=[300,200];this._last_buffer=null}function y(){this.properties={band:440,amplitude:1};this.addInput("freqs","array");this.addOutput("signal","number")}function u(){if(!u.default_code){var c=u.default_function.toString(),a=c.indexOf("{")+1,b=c.lastIndexOf("}");u.default_code=c.substr(a,b-a)}this.properties={code:u.default_code};c=p.getAudioContext();c.createScriptProcessor?this.audionode=c.createScriptProcessor(4096, -1,1):(console.warn("ScriptProcessorNode deprecated"),this.audionode=c.createGain());this.processCode();u._bypass_function||(u._bypass_function=this.audionode.onaudioprocess);this.addInput("in","audio");this.addOutput("out","audio")}function G(){this.audionode=p.getAudioContext().destination;this.addInput("in","audio")}var q=B.LiteGraph,p={};B.LGAudio=p;p.getAudioContext=function(){if(!this._audio_context){window.AudioContext=window.AudioContext||window.webkitAudioContext;if(!window.AudioContext)return console.error("AudioContext not supported by browser"), +this.addProperty("type","lowpass","enum",{values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")});this.audionode=p.getAudioContext().createBiquadFilter();this.addInput("in","audio");this.addOutput("out","audio")}function e(){this.properties={frequency:440,detune:0,type:"sine"};this.addProperty("type","sine","enum",{values:["sine","square","sawtooth","triangle","custom"]});this.audionode=p.getAudioContext().createOscillator();this.addOutput("out","audio")}function y(){this.properties= +{continuous:!0,mark:-1};this.addInput("data","array");this.addInput("mark","number");this.size=[300,200];this._last_buffer=null}function B(){this.properties={band:440,amplitude:1};this.addInput("freqs","array");this.addOutput("signal","number")}function u(){if(!u.default_code){var c=u.default_function.toString(),a=c.indexOf("{")+1,b=c.lastIndexOf("}");u.default_code=c.substr(a,b-a)}this.properties={code:u.default_code};c=p.getAudioContext();c.createScriptProcessor?this.audionode=c.createScriptProcessor(4096, +1,1):(console.warn("ScriptProcessorNode deprecated"),this.audionode=c.createGain());this.processCode();u._bypass_function||(u._bypass_function=this.audionode.onaudioprocess);this.addInput("in","audio");this.addOutput("out","audio")}function F(){this.audionode=p.getAudioContext().destination;this.addInput("in","audio")}var q=z.LiteGraph,p={};z.LGAudio=p;p.getAudioContext=function(){if(!this._audio_context){window.AudioContext=window.AudioContext||window.webkitAudioContext;if(!window.AudioContext)return console.error("AudioContext not supported by browser"), null;this._audio_context=new AudioContext;this._audio_context.onmessage=function(c){console.log("msg",c)};this._audio_context.onended=function(c){console.log("ended",c)};this._audio_context.oncomplete=function(c){console.log("complete",c)}}return this._audio_context};p.connect=function(c,a){try{c.connect(a)}catch(b){console.warn("LGraphAudio:",b)}};p.disconnect=function(c,a){try{c.disconnect(a)}catch(b){console.warn("LGraphAudio:",b)}};p.changeAllAudiosConnections=function(c,a){if(c.inputs)for(var b= 0;b=this.size[0]&&(e=this.size[0]-1),c.strokeStyle="red",c.beginPath(),c.moveTo(e,d),c.lineTo(e,0),c.stroke())}};z.title="Visualization";z.desc="Audio Visualization";q.registerNodeType("audio/visualization",z);y.prototype.onExecute=function(){if(this._freqs=this.getInputData(0)){var c=this.properties.band,a=this.getInputData(1); -void 0!==a&&(c=a);a=p.getAudioContext().sampleRate/this._freqs.length;a=c/a*2;a>=this._freqs.length?a=this._freqs[this._freqs.length-1]:(c=a|0,a-=c,a=this._freqs[c]*(1-a)+this._freqs[c+1]*a);this.setOutputData(0,a/255*this.properties.amplitude)}};y.prototype.onGetInputs=function(){return[["band","number"]]};y.title="Signal";y.desc="extract the signal of some frequency";q.registerNodeType("audio/signal",y);u.prototype.onAdded=function(c){c.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess= +e);y.prototype.onExecute=function(){this._last_buffer=this.getInputData(0);var c=this.getInputData(1);void 0!==c&&(this.properties.mark=c);this.setDirtyCanvas(!0,!1)};y.prototype.onDrawForeground=function(c){if(this._last_buffer){var a=this._last_buffer,b=a.length/this.size[0],d=this.size[1];c.fillStyle="black";c.fillRect(0,0,this.size[0],this.size[1]);c.strokeStyle="white";c.beginPath();var e=0;if(this.properties.continuous){c.moveTo(e,d);for(var f=0;f=this.size[0]&&(e=this.size[0]-1),c.strokeStyle="red",c.beginPath(),c.moveTo(e,d),c.lineTo(e,0),c.stroke())}};y.title="Visualization";y.desc="Audio Visualization";q.registerNodeType("audio/visualization",y);B.prototype.onExecute=function(){if(this._freqs=this.getInputData(0)){var c=this.properties.band,a=this.getInputData(1); +void 0!==a&&(c=a);a=p.getAudioContext().sampleRate/this._freqs.length;a=c/a*2;a>=this._freqs.length?a=this._freqs[this._freqs.length-1]:(c=a|0,a-=c,a=this._freqs[c]*(1-a)+this._freqs[c+1]*a);this.setOutputData(0,a/255*this.properties.amplitude)}};B.prototype.onGetInputs=function(){return[["band","number"]]};B.title="Signal";B.desc="extract the signal of some frequency";q.registerNodeType("audio/signal",B);u.prototype.onAdded=function(c){c.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess= this._callback)};u["@code"]={widget:"code",type:"code"};u.prototype.onStart=function(){this.audionode.onaudioprocess=this._callback};u.prototype.onStop=function(){this.audionode.onaudioprocess=u._bypass_function};u.prototype.onPause=function(){this.audionode.onaudioprocess=u._bypass_function};u.prototype.onUnpause=function(){this.audionode.onaudioprocess=this._callback};u.prototype.onExecute=function(){};u.prototype.onRemoved=function(){this.audionode.onaudioprocess=u._bypass_function};u.prototype.processCode= function(){try{this._script=new new Function("properties",this.properties.code)(this.properties),this._old_code=this.properties.code,this._callback=this._script.onaudioprocess}catch(c){console.error("Error in onaudioprocess code",c),this._callback=u._bypass_function,this.audionode.onaudioprocess=this._callback}};u.prototype.onPropertyChanged=function(c,a){"code"==c&&(this.properties.code=a,this.processCode(),this.graph&&this.graph.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback))}; -u.default_function=function(){this.onaudioprocess=function(c){var a=c.inputBuffer;c=c.outputBuffer;for(var b=0;b Date: Sun, 24 May 2020 00:02:32 +0200 Subject: [PATCH 41/63] merge --- build/litegraph.js | 4 ++-- build/litegraph.min.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/litegraph.js b/build/litegraph.js index 9bd16aab1..25f32a859 100644 --- a/build/litegraph.js +++ b/build/litegraph.js @@ -786,7 +786,7 @@ window.requestAnimationFrame(on_frame); if(that.onBeforeStep) that.onBeforeStep(); - that.runStep(1, !this.catch_errors); + that.runStep(1, !that.catch_errors); if(that.onAfterStep) that.onAfterStep(); } @@ -797,7 +797,7 @@ //execute if(that.onBeforeStep) that.onBeforeStep(); - that.runStep(1, !this.catch_errors); + that.runStep(1, !that.catch_errors); if(that.onAfterStep) that.onAfterStep(); }, interval); diff --git a/build/litegraph.min.js b/build/litegraph.min.js index 9ea2d0110..70f79282a 100755 --- a/build/litegraph.min.js +++ b/build/litegraph.min.js @@ -24,8 +24,8 @@ e.EVENT&&b==e.ACTION)return!0;a=String(a);b=String(b);a=a.toLowerCase();b=b.toLo if("text"==b||"json"==b)return f.readAsText(a);if("blob"==b)return f.readAsBinaryString(a)}return null}};e.getTime="undefined"!=typeof performance?performance.now.bind(performance):"undefined"!=typeof Date&&Date.now?Date.now.bind(Date):"undefined"!=typeof process?function(){var a=process.hrtime();return 0.001*a[0]+1E-6*a[1]}:function(){return(new Date).getTime()};z.LGraph=e.LGraph=c;c.supported_types=["number","string","boolean"];c.prototype.getSupportedTypes=function(){return this.supported_types|| c.supported_types};c.STATUS_STOPPED=1;c.STATUS_RUNNING=2;c.prototype.clear=function(){this.stop();this.status=c.STATUS_STOPPED;this.last_link_id=this.last_node_id=0;this._version=-1;if(this._nodes)for(var a=0;a Date: Tue, 2 Jun 2020 14:37:05 +0200 Subject: [PATCH 42/63] Update README.md --- guides/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/guides/README.md b/guides/README.md index 4059a7290..6f61a9fce 100644 --- a/guides/README.md +++ b/guides/README.md @@ -12,7 +12,7 @@ And in ```the src/``` folder there is also another class included: ## LGraphNode LGraphNode is the base class used for all the nodes classes. -To extend the other classes all the methods contained in LGraphNode.prototype are copyed to the classes when registered. +To extend the other classes all the methods contained in LGraphNode.prototype are copied to the classes when registered. When you create a new node type you do not have to inherit from that class, when the node is registered all the methods are copied to your node prototype. This is done inside the functions ```LiteGraph.registerNodeType(...)```. @@ -59,7 +59,7 @@ LiteGraph.registerNodeType("basic/sum", MyAddNode ); There are several settings that could be defined or modified per node: * **size**: ```[width,height]``` the size of the area inside the node (excluding title). Every row is LiteGraph.NODE_SLOT_HEIGHT pixels height. * **properties**: object containing the properties that could be configured by the user, and serialized when saving the graph -* **shape**: the shape of the object (could be LiteGraph.BOX,LiteGraph.ROUND,LiteGraph.CARD) +* **shape**: the shape of the object (could be LiteGraph.BOX_SHAPE,LiteGraph.ROUND_SHAPE,LiteGraph.CARD_SHAPE) * **flags**: several flags * **resizable**: if it can be resized dragging the corner * **horizontal**: if the slots should be placed horizontally on the top and bottom of the node @@ -102,7 +102,7 @@ Slots have the next information: * **name**: string with the name of the slot (used also to show in the canvas) * **type**: string specifying the data type traveling through this link - * **link or links**: depending if the slot is input or ouput contains the id of the link or an array of ids + * **link or links**: depending if the slot is input or output contains the id of the link or an array of ids * **label**: optional, string used to rename the name as shown in the canvas. * **dir**: optional, could be LiteGraph.UP, LiteGraph.RIGHT, LiteGraph.DOWN, LiteGraph.LEFT * **color_on**: color to render when it is connected From 7847e4e08e94b29aefb2cd5f5a4dc6575c11e962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Gotthardt?= <34188924+GotthardtZ@users.noreply.github.com> Date: Tue, 2 Jun 2020 15:29:01 +0200 Subject: [PATCH 43/63] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c21e75114..347d4bde6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "litegraph.js", "version": "0.7.8", - "description": "A graph node editor similar to PD or UDK Blueprints, it works in a HTML5 Canvas and allow to exported graphs to be included in applications.", + "description": "A graph node editor similar to PD or UDK Blueprints. It works in an HTML5 Canvas and allows to export graphs to be included in applications.", "main": "build/litegraph.js", "types": "src/litegraph.d.ts", "directories": { From 5d7e1af147470f901d1c4f59d394bbc19170b14c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Gotthardt?= <34188924+GotthardtZ@users.noreply.github.com> Date: Tue, 2 Jun 2020 15:56:51 +0200 Subject: [PATCH 44/63] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 347d4bde6..60ebc934b 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "bugs": { "url": "https://github.com/jagenjo/litegraph.js/issues" }, - "homepage": "https://github.com/kriffe/litegraph.js#readme", + "homepage": "https://github.com/jagenjo/litegraph.js#readme", "devDependencies": { "express": "^4.17.1", "google-closure-compiler": "^20171112.0.0", From ab7bc960a0f5c0c914d6de72f4b0937bae6bbec7 Mon Sep 17 00:00:00 2001 From: tamat Date: Thu, 11 Jun 2020 17:07:47 +0200 Subject: [PATCH 45/63] math node supports array and objects as A --- build/litegraph.js | 205 +++++++-- build/litegraph.min.js | 961 ++++++++++++++++++++-------------------- src/nodes/gltextures.js | 118 +++++ src/nodes/math.js | 84 ++-- src/nodes/midi.js | 3 + 5 files changed, 823 insertions(+), 548 deletions(-) diff --git a/build/litegraph.js b/build/litegraph.js index 25f32a859..048fedcef 100644 --- a/build/litegraph.js +++ b/build/litegraph.js @@ -14493,12 +14493,14 @@ if (typeof exports != "undefined") { //Math operation function MathOperation() { - this.addInput("A", "number"); + this.addInput("A", "number,array,object"); this.addInput("B", "number"); this.addOutput("=", "number"); this.addProperty("A", 1); this.addProperty("B", 1); this.addProperty("OP", "+", "enum", { values: MathOperation.values }); + this._func = function(A,B) { return A + B; }; + this._result = []; //only used for arrays } MathOperation.values = ["+", "-", "*", "/", "%", "^", "max", "min"]; @@ -14525,11 +14527,34 @@ if (typeof exports != "undefined") { this.properties["value"] = v; }; + MathOperation.prototype.onPropertyChanged = function(name, value) + { + if (name != "OP") + return; + switch (this.properties.OP) { + case "+": this._func = function(A,B) { return A + B; }; break; + case "-": this._func = function(A,B) { return A - B; }; break; + case "x": + case "X": + case "*": this._func = function(A,B) { return A * B; }; break; + case "/": this._func = function(A,B) { return A / B; }; break; + case "%": this._func = function(A,B) { return A % B; }; break; + case "^": this._func = function(A,B) { return Math.pow(A, B); }; break; + case "max": this._func = function(A,B) { return Math.max(A, B); }; break; + case "min": this._func = function(A,B) { return Math.min(A, B); }; break; + default: + console.warn("Unknown operation: " + this.properties.OP); + this._func = function(A) { return A; }; + break; + } + } + MathOperation.prototype.onExecute = function() { var A = this.getInputData(0); var B = this.getInputData(1); - if (A != null) { - this.properties["A"] = A; + if ( A != null ) { + if( A.constructor === Number ) + this.properties["A"] = A; } else { A = this.properties["A"]; } @@ -14540,38 +14565,26 @@ if (typeof exports != "undefined") { B = this.properties["B"]; } - var result = 0; - switch (this.properties.OP) { - case "+": - result = A + B; - break; - case "-": - result = A - B; - break; - case "x": - case "X": - case "*": - result = A * B; - break; - case "/": - result = A / B; - break; - case "%": - result = A % B; - break; - case "^": - result = Math.pow(A, B); - break; - case "max": - result = Math.max(A, B); - break; - case "min": - result = Math.min(A, B); - break; - default: - console.warn("Unknown operation: " + this.properties.OP); - } - this.setOutputData(0, result); + var result; + if(A.constructor === Number) + { + result = 0; + result = this._func(A,B); + } + else if(A.constructor === Array) + { + result = this._result; + result.length = A.length; + for(var i = 0; i < A.length; ++i) + result[i] = this._func(A[i],B); + } + else + { + result = {}; + for(var i in A) + result[i] = this._func(A[i],B); + } + this.setOutputData(0, result); }; MathOperation.prototype.onDrawBackground = function(ctx) { @@ -14602,6 +14615,7 @@ if (typeof exports != "undefined") { title: "MIN()" }); + //Math compare function MathCompare() { this.addInput("A", "number"); @@ -19085,6 +19099,124 @@ void main() {\n\ LiteGraph.registerNodeType("texture/LUT", LGraphTextureLUT); + + // Texture LUT ***************************************** + function LGraphTextureEncode() { + this.addInput("Texture", "Texture"); + this.addInput("Atlas", "Texture"); + this.addOutput("", "Texture"); + this.properties = { enabled: true, num_row_symbols: 4, symbol_size: 16, brightness: 1, colorize: false, filter: false, invert: false, precision: LGraphTexture.DEFAULT, texture: null }; + + if (!LGraphTextureEncode._shader) { + LGraphTextureEncode._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, LGraphTextureEncode.pixel_shader ); + } + + this._uniforms = { + u_texture: 0, + u_textureB: 1, + u_row_simbols: 4, + u_simbol_size: 16, + u_res: vec2.create() + }; + } + + LGraphTextureEncode.widgets_info = { + texture: { widget: "texture" }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureEncode.title = "Encode"; + LGraphTextureEncode.desc = "Apply a texture atlas to encode a texture"; + + LGraphTextureEncode.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { + return; + } //saves work + + var tex = this.getInputData(0); + + if (this.properties.precision === LGraphTexture.PASS_THROUGH || this.properties.enabled === false) { + this.setOutputData(0, tex); + return; + } + + if (!tex) { + return; + } + + var symbols_tex = this.getInputData(1); + + if (!symbols_tex) { + symbols_tex = LGraphTexture.getTexture(this.properties.texture); + } + + if (!symbols_tex) { + this.setOutputData(0, tex); + return; + } + + symbols_tex.bind(0); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, this.properties.filter ? gl.LINEAR : gl.NEAREST ); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, this.properties.filter ? gl.LINEAR : gl.NEAREST ); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE ); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE ); + gl.bindTexture(gl.TEXTURE_2D, null); + + var uniforms = this._uniforms; + uniforms.u_row_simbols = Math.floor(this.properties.num_row_symbols); + uniforms.u_symbol_size = this.properties.symbol_size; + uniforms.u_brightness = this.properties.brightness; + uniforms.u_invert = this.properties.invert ? 1 : 0; + uniforms.u_colorize = this.properties.colorize ? 1 : 0; + + this._tex = LGraphTexture.getTargetTexture( tex, this._tex, this.properties.precision ); + uniforms.u_res[0] = this._tex.width; + uniforms.u_res[1] = this._tex.height; + this._tex.bind(0); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); + + this._tex.drawTo(function() { + symbols_tex.bind(1); + tex.toViewport(LGraphTextureEncode._shader, uniforms); + }); + + this.setOutputData(0, this._tex); + }; + + LGraphTextureEncode.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_textureB;\n\ + uniform float u_row_simbols;\n\ + uniform float u_symbol_size;\n\ + uniform float u_brightness;\n\ + uniform float u_invert;\n\ + uniform float u_colorize;\n\ + uniform vec2 u_res;\n\ + \n\ + void main() {\n\ + vec2 total_symbols = u_res / u_symbol_size;\n\ + vec2 uv = floor(v_coord * total_symbols) / total_symbols; //pixelate \n\ + vec2 local_uv = mod(v_coord * u_res, u_symbol_size) / u_symbol_size;\n\ + lowp vec4 textureColor = texture2D(u_texture, uv );\n\ + float lum = clamp(u_brightness * (textureColor.x + textureColor.y + textureColor.z)/3.0,0.0,1.0);\n\ + if( u_invert == 1.0 ) lum = 1.0 - lum;\n\ + float index = floor( lum * (u_row_simbols * u_row_simbols - 1.0));\n\ + float col = mod( index, u_row_simbols );\n\ + float row = u_row_simbols - floor( index / u_row_simbols ) - 1.0;\n\ + vec2 simbol_uv = ( vec2( col, row ) + local_uv ) / u_row_simbols;\n\ + vec4 color = texture2D( u_textureB, simbol_uv );\n\ + if(u_colorize == 1.0)\n\ + color *= textureColor;\n\ + gl_FragColor = color;\n\ + }\n\ + "; + + LiteGraph.registerNodeType("texture/encode", LGraphTextureEncode); + // Texture Channels ***************************************** function LGraphTextureChannels() { this.addInput("Texture", "Texture"); @@ -26070,6 +26202,9 @@ function LGraphGeometryDisplace() { { this._playing = true; this._current_time = 0; + if(!this._midi) + return; + for(var i = 0; i < this._midi.tracks; ++i) { var track = this._midi.track[i]; diff --git a/build/litegraph.min.js b/build/litegraph.min.js index 70f79282a..0e8bc779b 100755 --- a/build/litegraph.min.js +++ b/build/litegraph.min.js @@ -1,104 +1,104 @@ -(function(z){function c(a){e.debug&&console.log("Graph created");this.list_of_graphcanvas=null;this.clear();a&&this.configure(a)}function m(a,b,d,g,f,r){this.id=a;this.type=b;this.origin_id=d;this.origin_slot=g;this.target_id=f;this.target_slot=r;this._data=null;this._pos=new Float32Array(2)}function n(a){this._ctor(a)}function h(a){this._ctor(a)}function x(a,b){this.offset=new Float32Array([0,0]);this.scale=1;this.max_scale=10;this.min_scale=0.1;this.onredraw=null;this.enabled=!0;this.last_mouse= +(function(z){function c(a){h.debug&&console.log("Graph created");this.list_of_graphcanvas=null;this.clear();a&&this.configure(a)}function m(a,b,d,g,f,r){this.id=a;this.type=b;this.origin_id=d;this.origin_slot=g;this.target_id=f;this.target_slot=r;this._data=null;this._pos=new Float32Array(2)}function n(a){this._ctor(a)}function e(a){this._ctor(a)}function w(a,b){this.offset=new Float32Array([0,0]);this.scale=1;this.max_scale=10;this.min_scale=0.1;this.onredraw=null;this.enabled=!0;this.last_mouse= [0,0];this.element=null;this.visible_area=new Float32Array(4);a&&(this.element=a,b||this.bindEvents(a))}function k(a,b,d){d=d||{};this.background_image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; -a&&a.constructor===String&&(a=document.querySelector(a));this.ds=new x;this.zoom_modify_alpha=!0;this.title_text_font=""+e.NODE_TEXT_SIZE+"px Arial";this.inner_text_font="normal "+e.NODE_SUBTEXT_SIZE+"px Arial";this.node_title_color=e.NODE_TITLE_COLOR;this.default_link_color=e.LINK_COLOR;this.default_connection_color={input_off:"#778",input_on:"#7F7",output_off:"#778",output_on:"#7F7"};this.highquality_render=!0;this.use_gradients=!1;this.editor_alpha=1;this.pause_rendering=!1;this.clear_background= +a&&a.constructor===String&&(a=document.querySelector(a));this.ds=new w;this.zoom_modify_alpha=!0;this.title_text_font=""+h.NODE_TEXT_SIZE+"px Arial";this.inner_text_font="normal "+h.NODE_SUBTEXT_SIZE+"px Arial";this.node_title_color=h.NODE_TITLE_COLOR;this.default_link_color=h.LINK_COLOR;this.default_connection_color={input_off:"#778",input_on:"#7F7",output_off:"#778",output_on:"#7F7"};this.highquality_render=!0;this.use_gradients=!1;this.editor_alpha=1;this.pause_rendering=!1;this.clear_background= !0;this.read_only=!1;this.render_only_selected=!0;this.live_mode=!1;this.allow_searchbox=this.allow_interaction=this.allow_dragnodes=this.allow_dragcanvas=this.show_info=!0;this.drag_mode=this.allow_reconnect_links=!1;this.filter=this.dragging_rectangle=null;this.always_render_background=!1;this.render_canvas_border=this.render_shadows=!0;this.render_connections_shadows=!1;this.render_connections_border=!0;this.render_connection_arrows=this.render_curved_connections=!1;this.render_collapsed_slots= -!0;this.render_execution_order=!1;this.render_link_tooltip=this.render_title_colored=!0;this.links_render_mode=e.SPLINE_LINK;this.canvas_mouse=[0,0];this.onSelectionChange=this.onNodeMoved=this.onDrawLinkTooltip=this.onDrawOverlay=this.onDrawForeground=this.onDrawBackground=this.onMouse=this.onSearchBoxSelection=this.onSearchBox=null;this.connections_width=3;this.round_radius=8;this.over_link_center=this.node_widget=this.current_node=null;this.last_mouse_position=[0,0];this.visible_area=this.ds.visible_area; -this.visible_links=[];b&&b.attachCanvas(this);this.setCanvas(a);this.clear();d.skip_render||this.startRendering();this.autoresize=d.autoresize}function C(a,b){return Math.sqrt((b[0]-a[0])*(b[0]-a[0])+(b[1]-a[1])*(b[1]-a[1]))}function w(a,b,d,g,f,r){return da&&gb?!0:!1}function t(a,b){var d=a[0]+a[2],g=a[1]+a[3],f=b[1]+b[3];return a[0]>b[0]+b[2]||a[1]>f||da&&gb?!0:!1}function s(a,b){var d=a[0]+a[2],g=a[1]+a[3],f=b[1]+b[3];return a[0]>b[0]+b[2]||a[1]>f||dc.width-l.width-10&&(f=c.width-l.width-10);c.height&&e>c.height-l.height-10&&(e=c.height-l.height-10)}r.style.left=f+"px";r.style.top=e+"px";b.scale&&(r.style.transform="scale("+b.scale+")")}function A(a){this.points=a;this.nearest=this.selected=-1;this.size=null;this.must_update=!0;this.margin=5}var e=z.LiteGraph={VERSION:0.4,CANVAS_GRID_SIZE:10,NODE_TITLE_HEIGHT:30, +a.button)return g.close(),a.preventDefault(),!0},!0);b.scroll_speed||(b.scroll_speed=0.1);r.addEventListener("wheel",d,!0);r.addEventListener("mousewheel",d,!0);this.root=r;b.title&&(f=document.createElement("div"),f.className="litemenu-title",f.innerHTML=b.title,r.appendChild(f));var f=0,h;for(h in a){var c=a.constructor==Array?a[h]:h;null!=c&&c.constructor!==String&&(c=void 0===c.content?String(c):c.content);this.addItem(c,a[h],b);f++}r.addEventListener("mouseleave",function(a){g.lock||(r.closing_timer&& +clearTimeout(r.closing_timer),r.closing_timer=setTimeout(g.close.bind(g,a),500))});r.addEventListener("mouseenter",function(a){r.closing_timer&&clearTimeout(r.closing_timer)});h=document;b.event&&(h=b.event.target.ownerDocument);h||(h=document);h.fullscreenElement?h.fullscreenElement.appendChild(r):h.body.appendChild(r);f=b.left||0;h=b.top||0;if(b.event){f=b.event.clientX-10;h=b.event.clientY-10;b.title&&(h-=20);b.parentMenu&&(f=b.parentMenu.root.getBoundingClientRect(),f=f.left+f.width);var c=document.body.getBoundingClientRect(), +l=r.getBoundingClientRect();0==c.height&&console.error("document.body height is 0. That is dangerous, set html,body { height: 100%; }");c.width&&f>c.width-l.width-10&&(f=c.width-l.width-10);c.height&&h>c.height-l.height-10&&(h=c.height-l.height-10)}r.style.left=f+"px";r.style.top=h+"px";b.scale&&(r.style.transform="scale("+b.scale+")")}function A(a){this.points=a;this.nearest=this.selected=-1;this.size=null;this.must_update=!0;this.margin=5}var h=z.LiteGraph={VERSION:0.4,CANVAS_GRID_SIZE:10,NODE_TITLE_HEIGHT:30, NODE_TITLE_TEXT_Y:20,NODE_SLOT_HEIGHT:20,NODE_WIDGET_HEIGHT:20,NODE_WIDTH:140,NODE_MIN_WIDTH:50,NODE_COLLAPSED_RADIUS:10,NODE_COLLAPSED_WIDTH:80,NODE_TITLE_COLOR:"#999",NODE_TEXT_SIZE:14,NODE_TEXT_COLOR:"#AAA",NODE_SUBTEXT_SIZE:12,NODE_DEFAULT_COLOR:"#333",NODE_DEFAULT_BGCOLOR:"#353535",NODE_DEFAULT_BOXCOLOR:"#666",NODE_DEFAULT_SHAPE:"box",DEFAULT_SHADOW_COLOR:"rgba(0,0,0,0.5)",DEFAULT_GROUP_FONT:24,WIDGET_BGCOLOR:"#222",WIDGET_OUTLINE_COLOR:"#666",WIDGET_TEXT_COLOR:"#DDD",WIDGET_SECONDARY_TEXT_COLOR:"#999", LINK_COLOR:"#9A9",EVENT_LINK_COLOR:"#A86",CONNECTING_LINK_COLOR:"#AFA",MAX_NUMBER_OF_NODES:1E3,DEFAULT_POSITION:[100,100],VALID_SHAPES:["default","box","round","card"],BOX_SHAPE:1,ROUND_SHAPE:2,CIRCLE_SHAPE:3,CARD_SHAPE:4,ARROW_SHAPE:5,INPUT:1,OUTPUT:2,EVENT:-1,ACTION:-1,ALWAYS:0,ON_EVENT:1,NEVER:2,ON_TRIGGER:3,UP:1,DOWN:2,LEFT:3,RIGHT:4,CENTER:5,STRAIGHT_LINK:0,LINEAR_LINK:1,SPLINE_LINK:2,NORMAL_TITLE:0,NO_TITLE:1,TRANSPARENT_TITLE:2,AUTOHIDE_TITLE:3,proxy:null,node_images_path:"",debug:!1,catch_exceptions:!0, -throw_errors:!0,allow_scripts:!1,registered_node_types:{},node_types_by_file_extension:{},Nodes:{},searchbox_extras:{},registerNodeType:function(a,b){if(!b.prototype)throw"Cannot register a simple object, it must be a class with a prototype";b.type=a;e.debug&&console.log("Node registered: "+a);a.split("/");var d=b.name,g=a.lastIndexOf("/");b.category=a.substr(0,g);b.title||(b.title=d);if(b.prototype)for(var f in n.prototype)b.prototype[f]||(b.prototype[f]=n.prototype[f]);if(g=this.registered_node_types[a])console.log("replacing node type: "+ -a);else if(Object.hasOwnProperty(b.prototype,"shape")||Object.defineProperty(b.prototype,"shape",{set:function(a){switch(a){case "default":delete this._shape;break;case "box":this._shape=e.BOX_SHAPE;break;case "round":this._shape=e.ROUND_SHAPE;break;case "circle":this._shape=e.CIRCLE_SHAPE;break;case "card":this._shape=e.CARD_SHAPE;break;default:this._shape=a}},get:function(a){return this._shape},enumerable:!0,configurable:!0}),b.prototype.onPropertyChange&&console.warn("LiteGraph node class "+a+ -" has onPropertyChange method, it must be called onPropertyChanged with d at the end"),b.supported_extensions)for(f in b.supported_extensions){var r=b.supported_extensions[f];r&&r.constructor===String&&(this.node_types_by_file_extension[r.toLowerCase()]=b)}this.registered_node_types[a]=b;b.constructor.name&&(this.Nodes[d]=b);if(e.onNodeTypeRegistered)e.onNodeTypeRegistered(a,b);if(g&&e.onNodeTypeReplaced)e.onNodeTypeReplaced(a,b,g)},unregisterNodeType:function(a){var b=a.constructor===String?this.registered_node_types[a]: -a;if(!b)throw"node type not found: "+a;delete this.registered_node_types[b.type];b.constructor.name&&delete this.Nodes[b.constructor.name]},wrapFunctionAsNode:function(a,b,d,g,f){for(var r=Array(b.length),s="",c=e.getParameterNames(b),l=0;ls&&(s=f.size[0]),c+=f.size[1]+a+e.NODE_TITLE_HEIGHT;b+=s+a}this.setDirtyCanvas(!0,!0)};c.prototype.getTime=function(){return this.globaltime};c.prototype.getFixedTime=function(){return this.fixedtime};c.prototype.getElapsedTime= -function(){return this.elapsed_time};c.prototype.sendEventToAllNodes=function(a,b,d){d=d||e.ALWAYS;var g=this._nodes_in_order?this._nodes_in_order:this._nodes;if(g)for(var f=0,r=g.length;f=e.MAX_NUMBER_OF_NODES)throw"LiteGraph: max number of nodes in a graph reached";null==a.id||-1==a.id?a.id=++this.last_node_id:this.last_node_id< -a.id&&(this.last_node_id=a.id);a.graph=this;this._version++;this._nodes.push(a);this._nodes_by_id[a.id]=a;if(a.onAdded)a.onAdded(this);this.config.align_to_grid&&a.alignToGrid();b||this.updateExecutionOrder();if(this.onNodeAdded)this.onNodeAdded(a);this.setDirtyCanvas(!0);this.change();return a}};c.prototype.remove=function(a){if(a.constructor===e.LGraphGroup){var b=this._groups.indexOf(a);-1!=b&&this._groups.splice(b,1);a.graph=null;this._version++;this.setDirtyCanvas(!0,!0);this.change()}else if(null!= +c.prototype.runStep=function(a,b,d){a=a||1;var g=h.getTime();this.globaltime=0.001*(g-this.starttime);var f=this._nodes_executable?this._nodes_executable:this._nodes;if(f){d=d||f.length;if(b){for(var r=0;rx&&(x=f.size[0]),c+=f.size[1]+a+h.NODE_TITLE_HEIGHT;b+=x+a}this.setDirtyCanvas(!0,!0)};c.prototype.getTime=function(){return this.globaltime};c.prototype.getFixedTime=function(){return this.fixedtime};c.prototype.getElapsedTime= +function(){return this.elapsed_time};c.prototype.sendEventToAllNodes=function(a,b,d){d=d||h.ALWAYS;var g=this._nodes_in_order?this._nodes_in_order:this._nodes;if(g)for(var f=0,r=g.length;f=h.MAX_NUMBER_OF_NODES)throw"LiteGraph: max number of nodes in a graph reached";null==a.id||-1==a.id?a.id=++this.last_node_id:this.last_node_id< +a.id&&(this.last_node_id=a.id);a.graph=this;this._version++;this._nodes.push(a);this._nodes_by_id[a.id]=a;if(a.onAdded)a.onAdded(this);this.config.align_to_grid&&a.alignToGrid();b||this.updateExecutionOrder();if(this.onNodeAdded)this.onNodeAdded(a);this.setDirtyCanvas(!0);this.change();return a}};c.prototype.remove=function(a){if(a.constructor===h.LGraphGroup){var b=this._groups.indexOf(a);-1!=b&&this._groups.splice(b,1);a.graph=null;this._version++;this.setDirtyCanvas(!0,!0);this.change()}else if(null!= this._nodes_by_id[a.id]&&!a.ignore_remove){if(a.inputs)for(b=0;ba.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});this.id=-1;this.type=null;this.inputs=[];this.outputs=[];this.connections=[];this.properties={};this.properties_info=[];this.flags={}};n.prototype.configure=function(a){this.graph&&this.graph._version++;for(var b in a)if("properties"==b)for(var d in a.properties){if(this.properties[d]=a.properties[d],this.onPropertyChanged)this.onPropertyChanged(d, -a.properties[d])}else null!=a[b]&&("object"==typeof a[b]?this[b]&&this[b].configure?this[b].configure(a[b]):this[b]=e.cloneObject(a[b],this[b]):this[b]=a[b]);a.title||(this.title=this.constructor.title);if(this.onConnectionsChange){if(this.inputs)for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d._data=b,this.outputs[a].links))for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d.type=b,this.outputs[a].links))for(d=0;d=this.inputs.length||null==this.inputs[a].link)){var d=this.graph.links[this.inputs[a].link];if(!d)return null;if(!b)return d.data;var g=this.graph.getNodeById(d.origin_id);if(!g)return d.data; if(g.updateOutputData)g.updateOutputData(d.origin_slot);else if(g.onExecute)g.onExecute();return d.data}};n.prototype.getInputDataType=function(a){if(!this.inputs||a>=this.inputs.length||null==this.inputs[a].link)return null;a=this.graph.links[this.inputs[a].link];if(!a)return null;var b=this.graph.getNodeById(a.origin_id);return b?(a=b.outputs[a.origin_slot])?a.type:null:a.type};n.prototype.getInputDataByName=function(a,b){var d=this.findInputSlot(a);return-1==d?null:this.getInputData(d,b)};n.prototype.isInputConnected= function(a){return this.inputs?a=this.inputs.length)return null;a=this.inputs[a];return a&&null!==a.link?(a=this.graph.links[a.link])?this.graph.getNodeById(a.origin_id):null:null};n.prototype.getInputOrProperty=function(a){if(!this.inputs||!this.inputs.length)return this.properties?this.properties[a]: null;for(var b=0,d=this.inputs.length;b=this.outputs.length?null:this.outputs[a]._data};n.prototype.getOutputInfo=function(a){return this.outputs?a=this.outputs.length)return null;a=this.outputs[a];if(!a.links||0==a.links.length)return null;for(var b=[],d=0;da&&this.pos[1]-f-db)return!0;return!1};n.prototype.getSlotInPosition=function(a,b){var d=new Float32Array(2);if(this.inputs)for(var g=0,f=this.inputs.length;ga&&this.pos[1]-f-db)return!0;return!1};n.prototype.getSlotInPosition=function(a,b){var d=new Float32Array(2);if(this.inputs)for(var g=0,f=this.inputs.length;g=this.outputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),null;b&&b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"target node is null";if(b==this)return null;if(d.constructor===String){if(d=b.findInputSlot(d),-1==d)return e.debug&&console.log("Connect: Error, no slot of name "+d),null}else{if(d===e.EVENT)return null; -if(!b.inputs||d>=b.inputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),null}null!=b.inputs[d].link&&b.disconnectInput(d);var g=this.outputs[a];if(b.onConnectInput&&!1===b.onConnectInput(d,g.type,g,this,a))return null;var f=b.inputs[d],r=null;if(e.isValidConnection(g.type,f.type)){r=new m(++this.graph.last_link_id,f.type,this.id,a,b.id,d);this.graph.links[r.id]=r;null==g.links&&(g.links=[]);g.links.push(r.id);b.inputs[d].link=r.id;this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(e.OUTPUT, -a,!0,r,g);if(b.onConnectionsChange)b.onConnectionsChange(e.INPUT,d,!0,r,f);this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(e.INPUT,b,d,this,a),this.graph.onNodeConnectionChange(e.OUTPUT,this,a,b,d))}this.setDirtyCanvas(!1,!0);this.graph.connectionChange(this,r);return r};n.prototype.disconnectOutput=function(a,b){if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return e.debug&&console.log("Connect: Error, no slot of name "+a),!1}else if(!this.outputs|| -a>=this.outputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),!1;var d=this.outputs[a];if(!d||!d.links||0==d.links.length)return!1;if(b){b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"Target Node not found";for(var g=0,f=d.links.length;g=this.inputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),!1;var b=this.inputs[a];if(!b)return!1;var d=this.inputs[a].link;this.inputs[a].link=null;var g=this.graph.links[d];if(g){var f=this.graph.getNodeById(g.origin_id);if(!f)return!1; -var r=f.outputs[g.origin_slot];if(!r||!r.links||0==r.links.length)return!1;for(var s=0,c=r.links.length;sb&&this.inputs[b].pos)return d[0]=this.pos[0]+this.inputs[b].pos[0],d[1]=this.pos[1]+this.inputs[b].pos[1],d;if(!a&&g>b&&this.outputs[b].pos)return d[0]=this.pos[0]+this.outputs[b].pos[0],d[1]=this.pos[1]+this.outputs[b].pos[1],d;if(this.horizontal)return d[0]=this.pos[0]+this.size[0]/g*(b+0.5),d[1]=a?this.pos[1]-e.NODE_TITLE_HEIGHT:this.pos[1]+this.size[1],d;d[0]=a?this.pos[0]+f:this.pos[0]+this.size[0]+1-f;d[1]= -this.pos[1]+(b+0.7)*e.NODE_SLOT_HEIGHT+(this.constructor.slot_start_y||0);return d};n.prototype.alignToGrid=function(){this.pos[0]=e.CANVAS_GRID_SIZE*Math.round(this.pos[0]/e.CANVAS_GRID_SIZE);this.pos[1]=e.CANVAS_GRID_SIZE*Math.round(this.pos[1]/e.CANVAS_GRID_SIZE)};n.prototype.trace=function(a){this.console||(this.console=[]);this.console.push(a);this.console.length>n.MAX_CONSOLE&&this.console.shift();this.graph.onNodeTrace(this,a)};n.prototype.setDirtyCanvas=function(a,b){this.graph&&this.graph.sendActionToCanvas("setDirty", -[a,b])};n.prototype.loadImage=function(a){var b=new Image;b.src=e.node_images_path+a;b.ready=!1;var d=this;b.onload=function(){this.ready=!0;d.setDirtyCanvas(!0)};return b};n.prototype.captureInput=function(a){if(this.graph&&this.graph.list_of_graphcanvas)for(var b=this.graph.list_of_graphcanvas,d=0;da.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});Object.defineProperty(this,"size",{set:function(a){!a||2>a.length||(this._size[0]=Math.max(140,a[0]),this._size[1]=Math.max(80,a[1]))},get:function(){return this._size},enumerable:!0})};h.prototype.configure=function(a){this.title=a.title; -this._bounding.set(a.bounding);this.color=a.color;this.font=a.font};h.prototype.serialize=function(){var a=this._bounding;return{title:this.title,bounding:[Math.round(a[0]),Math.round(a[1]),Math.round(a[2]),Math.round(a[3])],color:this.color,font:this.font}};h.prototype.move=function(a,b,d){this._pos[0]+=a;this._pos[1]+=b;if(!d)for(d=0;d=this.outputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),null;b&&b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"target node is null";if(b==this)return null;if(d.constructor===String){if(d=b.findInputSlot(d),-1==d)return h.debug&&console.log("Connect: Error, no slot of name "+d),null}else{if(d===h.EVENT)return null; +if(!b.inputs||d>=b.inputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),null}null!=b.inputs[d].link&&b.disconnectInput(d);var g=this.outputs[a];if(b.onConnectInput&&!1===b.onConnectInput(d,g.type,g,this,a))return null;var f=b.inputs[d],r=null;if(h.isValidConnection(g.type,f.type)){r=new m(++this.graph.last_link_id,f.type,this.id,a,b.id,d);this.graph.links[r.id]=r;null==g.links&&(g.links=[]);g.links.push(r.id);b.inputs[d].link=r.id;this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(h.OUTPUT, +a,!0,r,g);if(b.onConnectionsChange)b.onConnectionsChange(h.INPUT,d,!0,r,f);this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(h.INPUT,b,d,this,a),this.graph.onNodeConnectionChange(h.OUTPUT,this,a,b,d))}this.setDirtyCanvas(!1,!0);this.graph.connectionChange(this,r);return r};n.prototype.disconnectOutput=function(a,b){if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return h.debug&&console.log("Connect: Error, no slot of name "+a),!1}else if(!this.outputs|| +a>=this.outputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),!1;var d=this.outputs[a];if(!d||!d.links||0==d.links.length)return!1;if(b){b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"Target Node not found";for(var g=0,f=d.links.length;g=this.inputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),!1;var b=this.inputs[a];if(!b)return!1;var d=this.inputs[a].link;this.inputs[a].link=null;var g=this.graph.links[d];if(g){var f=this.graph.getNodeById(g.origin_id);if(!f)return!1; +var r=f.outputs[g.origin_slot];if(!r||!r.links||0==r.links.length)return!1;for(var x=0,c=r.links.length;xb&&this.inputs[b].pos)return d[0]=this.pos[0]+this.inputs[b].pos[0],d[1]=this.pos[1]+this.inputs[b].pos[1],d;if(!a&&g>b&&this.outputs[b].pos)return d[0]=this.pos[0]+this.outputs[b].pos[0],d[1]=this.pos[1]+this.outputs[b].pos[1],d;if(this.horizontal)return d[0]=this.pos[0]+this.size[0]/g*(b+0.5),d[1]=a?this.pos[1]-h.NODE_TITLE_HEIGHT:this.pos[1]+this.size[1],d;d[0]=a?this.pos[0]+f:this.pos[0]+this.size[0]+1-f;d[1]= +this.pos[1]+(b+0.7)*h.NODE_SLOT_HEIGHT+(this.constructor.slot_start_y||0);return d};n.prototype.alignToGrid=function(){this.pos[0]=h.CANVAS_GRID_SIZE*Math.round(this.pos[0]/h.CANVAS_GRID_SIZE);this.pos[1]=h.CANVAS_GRID_SIZE*Math.round(this.pos[1]/h.CANVAS_GRID_SIZE)};n.prototype.trace=function(a){this.console||(this.console=[]);this.console.push(a);this.console.length>n.MAX_CONSOLE&&this.console.shift();this.graph.onNodeTrace(this,a)};n.prototype.setDirtyCanvas=function(a,b){this.graph&&this.graph.sendActionToCanvas("setDirty", +[a,b])};n.prototype.loadImage=function(a){var b=new Image;b.src=h.node_images_path+a;b.ready=!1;var d=this;b.onload=function(){this.ready=!0;d.setDirtyCanvas(!0)};return b};n.prototype.captureInput=function(a){if(this.graph&&this.graph.list_of_graphcanvas)for(var b=this.graph.list_of_graphcanvas,d=0;da.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});Object.defineProperty(this,"size",{set:function(a){!a||2>a.length||(this._size[0]=Math.max(140,a[0]),this._size[1]=Math.max(80,a[1]))},get:function(){return this._size},enumerable:!0})};e.prototype.configure=function(a){this.title=a.title; +this._bounding.set(a.bounding);this.color=a.color;this.font=a.font};e.prototype.serialize=function(){var a=this._bounding;return{title:this.title,bounding:[Math.round(a[0]),Math.round(a[1]),Math.round(a[2]),Math.round(a[3])],color:this.color,font:this.font}};e.prototype.move=function(a,b,d){this._pos[0]+=a;this._pos[1]+=b;if(!d)for(d=0;dthis.max_scale&&(a=this.max_scale);if(a!=this.scale&&this.element){var d=this.element.getBoundingClientRect();if(d){b=b||[0.5*d.width,0.5*d.height];d=this.convertCanvasToOffset(b);this.scale=a;0.01>Math.abs(this.scale-1)&&(this.scale=1);var g=this.convertCanvasToOffset(b),d=[g[0]-d[0], -g[1]-d[1]];this.offset[0]+=d[0];this.offset[1]+=d[1];if(this.onredraw)this.onredraw(this)}}};x.prototype.changeDeltaScale=function(a,b){this.changeScale(this.scale*a,b)};x.prototype.reset=function(){this.scale=1;this.offset[0]=0;this.offset[1]=0};z.LGraphCanvas=e.LGraphCanvas=k;k.link_type_colors={"-1":e.EVENT_LINK_COLOR,number:"#AAA",node:"#DCA"};k.gradients={};k.prototype.clear=function(){this.fps=this.render_time=this.last_draw_time=this.frame=0;this.dragging_rectangle=null;this.selected_nodes= +3:0,this.changeDeltaScale(1+0.05*a.delta);this.last_mouse[0]=g;this.last_mouse[1]=d;a.preventDefault();a.stopPropagation();return!1}};w.prototype.toCanvasContext=function(a){a.scale(this.scale,this.scale);a.translate(this.offset[0],this.offset[1])};w.prototype.convertOffsetToCanvas=function(a){return[(a[0]+this.offset[0])*this.scale,(a[1]+this.offset[1])*this.scale]};w.prototype.convertCanvasToOffset=function(a,b){b=b||[0,0];b[0]=a[0]/this.scale-this.offset[0];b[1]=a[1]/this.scale-this.offset[1]; +return b};w.prototype.mouseDrag=function(a,b){this.offset[0]+=a/this.scale;this.offset[1]+=b/this.scale;if(this.onredraw)this.onredraw(this)};w.prototype.changeScale=function(a,b){athis.max_scale&&(a=this.max_scale);if(a!=this.scale&&this.element){var d=this.element.getBoundingClientRect();if(d){b=b||[0.5*d.width,0.5*d.height];d=this.convertCanvasToOffset(b);this.scale=a;0.01>Math.abs(this.scale-1)&&(this.scale=1);var g=this.convertCanvasToOffset(b),d=[g[0]-d[0], +g[1]-d[1]];this.offset[0]+=d[0];this.offset[1]+=d[1];if(this.onredraw)this.onredraw(this)}}};w.prototype.changeDeltaScale=function(a,b){this.changeScale(this.scale*a,b)};w.prototype.reset=function(){this.scale=1;this.offset[0]=0;this.offset[1]=0};z.LGraphCanvas=h.LGraphCanvas=k;k.link_type_colors={"-1":h.EVENT_LINK_COLOR,number:"#AAA",node:"#DCA"};k.gradients={};k.prototype.clear=function(){this.fps=this.render_time=this.last_draw_time=this.frame=0;this.dragging_rectangle=null;this.selected_nodes= {};this.selected_group=null;this.visible_nodes=[];this.connecting_node=this.node_capturing_input=this.node_over=this.node_dragged=null;this.highlighted_links={};this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_widget=this.node_in_panel=this.dirty_area=null;this.last_mouse=[0,0];this.last_mouseclick=0;this.visible_area.set([0,0,0,0]);if(this.onClear)this.onClear()};k.prototype.setGraph=function(a,b){this.graph!=a&&(b||this.clear(),!a&&this.graph?this.graph.detachCanvas(this):(a.attachCanvas(this), this._graph_stack&&(this._graph_stack=null),this.setDirty(!0,!0)))};k.prototype.openSubgraph=function(a){if(!a)throw"graph cannot be null";if(this.graph==a)throw"graph cannot be the same";this.clear();this.graph&&(this._graph_stack||(this._graph_stack=[]),this._graph_stack.push(this.graph));a.attachCanvas(this);this.setDirty(!0,!0)};k.prototype.closeSubgraph=function(){if(this._graph_stack&&0!=this._graph_stack.length){var a=this.graph._subgraph_node,b=this._graph_stack.pop();this.selected_nodes= {};this.highlighted_links={};b.attachCanvas(this);this.setDirty(!0,!0);a&&(this.centerOnNode(a),this.selectNodes([a]))}};k.prototype.getCurrentGraph=function(){return this.graph};k.prototype.setCanvas=function(a,b){if(a&&a.constructor===String&&(a=document.getElementById(a),!a))throw"Error creating LiteGraph canvas: Canvas not found";if(a!==this.canvas&&(!a&&this.canvas&&(b||this.unbindEvents()),this.canvas=a,this.ds.element=a)){a.className+=" lgraphcanvas";a.data=this;a.tabindex="1";this.bgcanvas= @@ -110,148 +110,148 @@ this.canvas.removeEventListener("mousedown",this._mousedown_callback);this.canva this.canvas.removeEventListener("touchstart",this.touchHandler);this.canvas.removeEventListener("touchmove",this.touchHandler);this.canvas.removeEventListener("touchend",this.touchHandler);this.canvas.removeEventListener("touchcancel",this.touchHandler);this._ondrop_callback=this._key_callback=this._mousewheel_callback=this._mousedown_callback=null;this._events_binded=!1}else console.warn("LGraphCanvas: no events binded")};k.getFileExtension=function(a){var b=a.indexOf("?");-1!=b&&(a=a.substr(0,b)); b=a.lastIndexOf(".");return-1==b?"":a.substr(b+1).toLowerCase()};k.prototype.enableWebGL=function(){if(void 0===typeof GL)throw"litegl.js must be included to use a WebGL canvas";if(void 0===typeof enableWebGLCanvas)throw"webglCanvas.js must be included to use this feature";this.gl=this.ctx=enableWebGLCanvas(this.canvas);this.ctx.webgl=!0;this.bgcanvas=this.canvas;this.bgctx=this.gl;this.canvas.webgl_enabled=!0};k.prototype.setDirty=function(a,b){a&&(this.dirty_canvas=!0);b&&(this.dirty_bgcanvas=!0)}; k.prototype.getCanvasWindow=function(){if(!this.canvas)return window;var a=this.canvas.ownerDocument;return a.defaultView||a.parentWindow};k.prototype.startRendering=function(){function a(){this.pause_rendering||this.draw();var b=this.getCanvasWindow();this.is_rendering&&b.requestAnimationFrame(a.bind(this))}this.is_rendering||(this.is_rendering=!0,a.call(this))};k.prototype.stopRendering=function(){this.is_rendering=!1};k.prototype.processMouseDown=function(a){if(this.graph){this.adjustMouseEvent(a); -var b=this.getCanvasWindow();k.active_canvas=this;this.canvas.removeEventListener("mousemove",this._mousemove_callback);b.document.addEventListener("mousemove",this._mousemove_callback,!0);b.document.addEventListener("mouseup",this._mouseup_callback,!0);var d=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes,5),g=!1,f=300>e.getTime()-this.last_mouseclick;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;this.canvas.focus();e.closeAllContextMenus(b);if(!this.onMouse||!0!=this.onMouse(a)){if(1== -a.which){a.ctrlKey&&(this.dragging_rectangle=new Float32Array(4),this.dragging_rectangle[0]=a.canvasX,this.dragging_rectangle[1]=a.canvasY,this.dragging_rectangle[2]=1,this.dragging_rectangle[3]=1,g=!0);var r=!1;if(d&&this.allow_interaction&&!g&&!this.read_only){this.live_mode||d.flags.pinned||this.bringToFront(d);if(!this.connecting_node&&!d.flags.collapsed&&!this.live_mode)if(!g&&!1!==d.resizable&&w(a.canvasX,a.canvasY,d.pos[0]+d.size[0]-5,d.pos[1]+d.size[1]-5,10,10))this.resizing_node=d,this.canvas.style.cursor= -"se-resize",g=!0;else{if(d.outputs)for(var s=0,c=d.outputs.length;sr[0]+4||a.canvasYr[1]+4)){this.showLinkMenu(d,a);this.over_link_center=null;break}this.selected_group=this.graph.getGroupOnPos(a.canvasX,a.canvasY);this.selected_group_resizing=!1;this.selected_group&&!this.read_only&&(a.ctrlKey&&(this.dragging_rectangle=null),10>C([a.canvasX,a.canvasY],[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])* -this.ds.scale?this.selected_group_resizing=!0:this.selected_group.recomputeInsideNodes());f&&!this.read_only&&this.allow_searchbox&&this.showSearchBox(a);r=!0}!g&&r&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}else 2!=a.which&&3==a.which&&(this.read_only||this.processContextMenu(d,a));this.last_mouse[0]=a.localX;this.last_mouse[1]=a.localY;this.last_mouseclick=e.getTime();this.last_mouse_dragging=!0;this.graph.change();(!b.document.activeElement||"input"!=b.document.activeElement.nodeName.toLowerCase()&& +var b=this.getCanvasWindow();k.active_canvas=this;this.canvas.removeEventListener("mousemove",this._mousemove_callback);b.document.addEventListener("mousemove",this._mousemove_callback,!0);b.document.addEventListener("mouseup",this._mouseup_callback,!0);var d=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes,5),g=!1,f=300>h.getTime()-this.last_mouseclick;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;this.canvas.focus();h.closeAllContextMenus(b);if(!this.onMouse||!0!=this.onMouse(a)){if(1== +a.which){a.ctrlKey&&(this.dragging_rectangle=new Float32Array(4),this.dragging_rectangle[0]=a.canvasX,this.dragging_rectangle[1]=a.canvasY,this.dragging_rectangle[2]=1,this.dragging_rectangle[3]=1,g=!0);var r=!1;if(d&&this.allow_interaction&&!g&&!this.read_only){this.live_mode||d.flags.pinned||this.bringToFront(d);if(!this.connecting_node&&!d.flags.collapsed&&!this.live_mode)if(!g&&!1!==d.resizable&&v(a.canvasX,a.canvasY,d.pos[0]+d.size[0]-5,d.pos[1]+d.size[1]-5,10,10))this.resizing_node=d,this.canvas.style.cursor= +"se-resize",g=!0;else{if(d.outputs)for(var x=0,c=d.outputs.length;xr[0]+4||a.canvasYr[1]+4)){this.showLinkMenu(d,a);this.over_link_center=null;break}this.selected_group=this.graph.getGroupOnPos(a.canvasX,a.canvasY);this.selected_group_resizing=!1;this.selected_group&&!this.read_only&&(a.ctrlKey&&(this.dragging_rectangle=null),10>C([a.canvasX,a.canvasY],[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])* +this.ds.scale?this.selected_group_resizing=!0:this.selected_group.recomputeInsideNodes());f&&!this.read_only&&this.allow_searchbox&&this.showSearchBox(a);r=!0}!g&&r&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}else 2!=a.which&&3==a.which&&(this.read_only||this.processContextMenu(d,a));this.last_mouse[0]=a.localX;this.last_mouse[1]=a.localY;this.last_mouseclick=h.getTime();this.last_mouse_dragging=!0;this.graph.change();(!b.document.activeElement||"input"!=b.document.activeElement.nodeName.toLowerCase()&& "textarea"!=b.document.activeElement.nodeName.toLowerCase())&&a.preventDefault();a.stopPropagation();if(this.onMouseDown)this.onMouseDown(a);return!1}}};k.prototype.processMouseMove=function(a){this.autoresize&&this.resize();if(this.graph){k.active_canvas=this;this.adjustMouseEvent(a);var b=[a.localX,a.localY],d=[b[0]-this.last_mouse[0],b[1]-this.last_mouse[1]];this.last_mouse=b;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;a.dragging=this.last_mouse_dragging;this.node_widget&&(this.processNodeWidgets(this.node_widget[0], this.canvas_mouse,a,this.node_widget[1]),this.dirty_canvas=!0);if(this.dragging_rectangle)this.dragging_rectangle[2]=a.canvasX-this.dragging_rectangle[0],this.dragging_rectangle[3]=a.canvasY-this.dragging_rectangle[1],this.dirty_canvas=!0;else if(this.selected_group&&!this.read_only)this.selected_group_resizing?this.selected_group.size=[a.canvasX-this.selected_group.pos[0],a.canvasY-this.selected_group.pos[1]]:(this.selected_group.move(d[0]/this.ds.scale,d[1]/this.ds.scale,a.ctrlKey),this.selected_group._nodes.length&& (this.dirty_canvas=!0)),this.dirty_bgcanvas=!0;else if(this.dragging_canvas)this.ds.offset[0]+=d[0]/this.ds.scale,this.ds.offset[1]+=d[1]/this.ds.scale,this.dirty_bgcanvas=this.dirty_canvas=!0;else if(this.allow_interaction&&!this.read_only){this.connecting_node&&(this.dirty_canvas=!0);for(var g=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes),b=0,f=this.graph._nodes.length;bs[0]+4||a.canvasYs[1]+4)){f=r;break}}f!=this.over_link_center&&(this.over_link_center=f,this.dirty_canvas=!0);this.canvas&&(this.canvas.style.cursor= +this.node_over.onMouseLeave)this.node_over.onMouseLeave(a);this.node_over=null;this.dirty_canvas=!0}if(g){if(!g.mouseOver&&(g.mouseOver=!0,this.node_over=g,this.dirty_canvas=!0,g.onMouseEnter))g.onMouseEnter(a);if(g.onMouseMove)g.onMouseMove(a,[a.canvasX-g.pos[0],a.canvasY-g.pos[1]],this);if(this.connecting_node&&(f=this._highlight_input||[0,0],!this.isOverNodeBox(g,a.canvasX,a.canvasY))){var r=this.isOverNodeInput(g,a.canvasX,a.canvasY,f);-1!=r&&g.inputs[r]?h.isValidConnection(this.connecting_output.type, +g.inputs[r].type)&&(this._highlight_input=f):this._highlight_input=null}this.canvas&&(v(a.canvasX,a.canvasY,g.pos[0]+g.size[0]-5,g.pos[1]+g.size[1]-5,5,5)?this.canvas.style.cursor="se-resize":this.canvas.style.cursor="crosshair")}else{f=null;for(b=0;bx[0]+4||a.canvasYx[1]+4)){f=r;break}}f!=this.over_link_center&&(this.over_link_center=f,this.dirty_canvas=!0);this.canvas&&(this.canvas.style.cursor= "")}if(this.node_capturing_input&&this.node_capturing_input!=g&&this.node_capturing_input.onMouseMove)this.node_capturing_input.onMouseMove(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]],this);if(this.node_dragged&&!this.live_mode){for(b in this.selected_nodes)g=this.selected_nodes[b],g.pos[0]+=d[0]/this.ds.scale,g.pos[1]+=d[1]/this.ds.scale;this.dirty_bgcanvas=this.dirty_canvas=!0}this.resizing_node&&!this.live_mode&&(d=[a.canvasX-this.resizing_node.pos[0], a.canvasY-this.resizing_node.pos[1]],b=this.resizing_node.computeSize(),d[0]=Math.max(b[0],d[0]),d[1]=Math.max(b[1],d[1]),this.resizing_node.setSize(d),this.canvas.style.cursor="se-resize",this.dirty_bgcanvas=this.dirty_canvas=!0)}a.preventDefault();return!1}};k.prototype.processMouseUp=function(a){if(this.graph){var b=this.getCanvasWindow().document;k.active_canvas=this;b.removeEventListener("mousemove",this._mousemove_callback,!0);this.canvas.addEventListener("mousemove",this._mousemove_callback, -!0);b.removeEventListener("mouseup",this._mouseup_callback,!0);this.adjustMouseEvent(a);b=e.getTime();a.click_time=b-this.last_mouseclick;this.last_mouse_dragging=!1;if(1==a.which){this.node_widget&&this.processNodeWidgets(this.node_widget[0],this.canvas_mouse,a);this.node_widget=null;if(this.selected_group){var b=this.selected_group.pos[0]-Math.round(this.selected_group.pos[0]),d=this.selected_group.pos[1]-Math.round(this.selected_group.pos[1]);this.selected_group.move(b,d,a.ctrlKey);this.selected_group.pos[0]= +!0);b.removeEventListener("mouseup",this._mouseup_callback,!0);this.adjustMouseEvent(a);b=h.getTime();a.click_time=b-this.last_mouseclick;this.last_mouse_dragging=!1;if(1==a.which){this.node_widget&&this.processNodeWidgets(this.node_widget[0],this.canvas_mouse,a);this.node_widget=null;if(this.selected_group){var b=this.selected_group.pos[0]-Math.round(this.selected_group.pos[0]),d=this.selected_group.pos[1]-Math.round(this.selected_group.pos[1]);this.selected_group.move(b,d,a.ctrlKey);this.selected_group.pos[0]= Math.round(this.selected_group.pos[0]);this.selected_group.pos[1]=Math.round(this.selected_group.pos[1]);this.selected_group._nodes.length&&(this.dirty_canvas=!0);this.selected_group=null}this.selected_group_resizing=!1;if(this.dragging_rectangle){if(this.graph){b=this.graph._nodes;d=new Float32Array(4);this.deselectAllNodes();var g=Math.abs(this.dragging_rectangle[2]),f=Math.abs(this.dragging_rectangle[3]),r=0>this.dragging_rectangle[3]?this.dragging_rectangle[1]-f:this.dragging_rectangle[1];this.dragging_rectangle[0]= -0>this.dragging_rectangle[2]?this.dragging_rectangle[0]-g:this.dragging_rectangle[0];this.dragging_rectangle[1]=r;this.dragging_rectangle[2]=g;this.dragging_rectangle[3]=f;f=[];for(r=0;ra.click_time&&w(a.canvasX,a.canvasY,g.pos[0],g.pos[1]-e.NODE_TITLE_HEIGHT,e.NODE_TITLE_HEIGHT,e.NODE_TITLE_HEIGHT)&&g.collapse();this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_dragged.pos[0]=Math.round(this.node_dragged.pos[0]);this.node_dragged.pos[1]=Math.round(this.node_dragged.pos[1]);this.graph.config.align_to_grid&& +0>this.dragging_rectangle[2]?this.dragging_rectangle[0]-g:this.dragging_rectangle[0];this.dragging_rectangle[1]=r;this.dragging_rectangle[2]=g;this.dragging_rectangle[3]=f;f=[];for(r=0;ra.click_time&&v(a.canvasX,a.canvasY,g.pos[0],g.pos[1]-h.NODE_TITLE_HEIGHT,h.NODE_TITLE_HEIGHT,h.NODE_TITLE_HEIGHT)&&g.collapse();this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_dragged.pos[0]=Math.round(this.node_dragged.pos[0]);this.node_dragged.pos[1]=Math.round(this.node_dragged.pos[1]);this.graph.config.align_to_grid&& this.node_dragged.alignToGrid();if(this.onNodeMoved)this.onNodeMoved(this.node_dragged);this.node_dragged=null}else{g=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes);!g&&300>a.click_time&&this.deselectAllNodes();this.dirty_canvas=!0;this.dragging_canvas=!1;if(this.node_over&&this.node_over.onMouseUp)this.node_over.onMouseUp(a,[a.canvasX-this.node_over.pos[0],a.canvasY-this.node_over.pos[1]],this);if(this.node_capturing_input&&this.node_capturing_input.onMouseUp)this.node_capturing_input.onMouseUp(a, [a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]])}}else 2==a.which?(this.dirty_canvas=!0,this.dragging_canvas=!1):3==a.which&&(this.dirty_canvas=!0,this.dragging_canvas=!1);this.graph.change();a.stopPropagation();a.preventDefault();return!1}};k.prototype.processMouseWheel=function(a){if(this.graph&&this.allow_dragcanvas){var b=null!=a.wheelDeltaY?a.wheelDeltaY:-60*a.detail;this.adjustMouseEvent(a);var d=this.ds.scale;0b&&(d*=1/1.1);this.ds.changeScale(d, -[a.localX,a.localY]);this.graph.change();a.preventDefault();return!1}};k.prototype.isOverNodeBox=function(a,b,d){var g=e.NODE_TITLE_HEIGHT;return w(b,d,a.pos[0]+2,a.pos[1]+2-g,g-4,g-4)?!0:!1};k.prototype.isOverNodeInput=function(a,b,d,g){if(a.inputs)for(var f=0,e=a.inputs.length;fd- +function(a,b){var d=b||[];d.length=0;a=a||this.graph._nodes;for(var g=0,f=a.length;gd- this.graph._last_trigger_time)&&this.drawBackCanvas();(this.dirty_canvas||a)&&this.drawFrontCanvas();this.fps=this.render_time?1/this.render_time:0;this.frame+=1}};k.prototype.drawFrontCanvas=function(){this.dirty_canvas=!1;this.ctx||(this.ctx=this.bgcanvas.getContext("2d"));var a=this.ctx;if(a){a.start2D&&a.start2D();var b=this.canvas;a.restore();a.setTransform(1,0,0,1,0,0);this.dirty_area&&(a.save(),a.beginPath(),a.rect(this.dirty_area[0],this.dirty_area[1],this.dirty_area[2],this.dirty_area[3]), a.clip());this.clear_background&&a.clearRect(0,0,b.width,b.height);this.bgcanvas==this.canvas?this.drawBackCanvas():a.drawImage(this.bgcanvas,0,0);if(this.onRender)this.onRender(b,a);this.show_info&&this.renderInfo(a);if(this.graph){a.save();this.ds.toCanvasContext(a);for(var b=this.computeVisibleNodes(null,this.visible_nodes),d=0;d> ";b.fillText(g+d.getTitle(),0.5*a.width,40);b.restore()}d=!1;this.onRenderBackground&&(d=this.onRenderBackground(a,b));b.restore(); -b.setTransform(1,0,0,1,0,0);this.visible_links.length=0;if(this.graph){b.save();this.ds.toCanvasContext(b);if(this.background_image&&0.5this.ds.scale;if(this.live_mode){if(!a.flags.collapsed&&(b.shadowColor="transparent",a.onDrawForeground))a.onDrawForeground(b,this,this.canvas)}else{var r=this.editor_alpha;b.globalAlpha=r;this.render_shadows&&!f?(b.shadowColor= -e.DEFAULT_SHADOW_COLOR,b.shadowOffsetX=2*this.ds.scale,b.shadowOffsetY=2*this.ds.scale,b.shadowBlur=3*this.ds.scale):b.shadowColor="transparent";if(!a.flags.collapsed||!a.onDrawCollapsed||!0!=a.onDrawCollapsed(b,this)){var s=a._shape||e.BOX_SHAPE;B.set(a.size);var c=a.horizontal;if(a.flags.collapsed){b.font=this.inner_text_font;var l=a.getTitle?a.getTitle():a.title;null!=l&&(a._collapsed_width=Math.min(a.size[0],b.measureText(l).width+2*e.NODE_TITLE_HEIGHT),B[0]=a._collapsed_width,B[1]=0)}a.clip_area&& -(b.save(),b.beginPath(),s==e.BOX_SHAPE?b.rect(0,0,B[0],B[1]):s==e.ROUND_SHAPE?b.roundRect(0,0,B[0],B[1],10):s==e.CIRCLE_SHAPE&&b.arc(0.5*B[0],0.5*B[1],0.5*B[0],0,2*Math.PI),b.clip());a.has_errors&&(g="red");this.drawNodeShape(a,b,B,d,g,a.is_selected,a.mouseOver);b.shadowColor="transparent";if(a.onDrawForeground)a.onDrawForeground(b,this,this.canvas);b.textAlign=c?"center":"left";b.font=this.inner_text_font;g=!f;s=this.connecting_output;b.lineWidth=1;var l=0,q=new Float32Array(2);if(!a.flags.collapsed){if(a.inputs)for(d= -0;dthis.ds.scale;if(this.live_mode){if(!a.flags.collapsed&&(b.shadowColor="transparent",a.onDrawForeground))a.onDrawForeground(b,this,this.canvas)}else{var r=this.editor_alpha;b.globalAlpha=r;this.render_shadows&&!f?(b.shadowColor= +h.DEFAULT_SHADOW_COLOR,b.shadowOffsetX=2*this.ds.scale,b.shadowOffsetY=2*this.ds.scale,b.shadowBlur=3*this.ds.scale):b.shadowColor="transparent";if(!a.flags.collapsed||!a.onDrawCollapsed||!0!=a.onDrawCollapsed(b,this)){var x=a._shape||h.BOX_SHAPE;B.set(a.size);var c=a.horizontal;if(a.flags.collapsed){b.font=this.inner_text_font;var l=a.getTitle?a.getTitle():a.title;null!=l&&(a._collapsed_width=Math.min(a.size[0],b.measureText(l).width+2*h.NODE_TITLE_HEIGHT),B[0]=a._collapsed_width,B[1]=0)}a.clip_area&& +(b.save(),b.beginPath(),x==h.BOX_SHAPE?b.rect(0,0,B[0],B[1]):x==h.ROUND_SHAPE?b.roundRect(0,0,B[0],B[1],10):x==h.CIRCLE_SHAPE&&b.arc(0.5*B[0],0.5*B[1],0.5*B[0],0,2*Math.PI),b.clip());a.has_errors&&(g="red");this.drawNodeShape(a,b,B,d,g,a.is_selected,a.mouseOver);b.shadowColor="transparent";if(a.onDrawForeground)a.onDrawForeground(b,this,this.canvas);b.textAlign=c?"center":"left";b.font=this.inner_text_font;g=!f;x=this.connecting_output;b.lineWidth=1;var l=0,q=new Float32Array(2);if(!a.flags.collapsed){if(a.inputs)for(d= +0;dthis.ds.scale,l=a._shape||a.constructor.shape||e.ROUND_SHAPE,q=a.constructor.title_mode,p=!0;q==e.TRANSPARENT_TITLE?p=!1:q==e.AUTOHIDE_TITLE&&s&&(p=!0);u[0]=0;u[1]=p?-f:0;u[2]=d[0]+1;u[3]=p?d[1]+f:d[1];s=b.globalAlpha;b.beginPath();l==e.BOX_SHAPE|| -c?b.fillRect(u[0],u[1],u[2],u[3]):l==e.ROUND_SHAPE||l==e.CARD_SHAPE?b.roundRect(u[0],u[1],u[2],u[3],this.round_radius,l==e.CARD_SHAPE?0:this.round_radius):l==e.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0],0,2*Math.PI);b.fill();a.flags.collapsed||(b.shadowColor="transparent",b.fillStyle="rgba(0,0,0,0.2)",b.fillRect(0,-1,u[2],2));b.shadowColor="transparent";if(a.onDrawBackground)a.onDrawBackground(b,this,this.canvas);if(p||q==e.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,f,d,this.ds.scale, -g);else if(q!=e.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){p=a.constructor.title_color||g;a.flags.collapsed&&(b.shadowColor=e.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var v=k.gradients[p];v||(v=k.gradients[p]=b.createLinearGradient(0,0,400,0),v.addColorStop(0,p),v.addColorStop(1,"#000"));b.fillStyle=v}else b.fillStyle=p;b.beginPath();l==e.BOX_SHAPE||c?b.rect(0,-f,d[0]+1,f):l!=e.ROUND_SHAPE&&l!=e.CARD_SHAPE||b.roundRect(0,-f,d[0]+1,f,this.round_radius,a.flags.collapsed? -this.round_radius:0);b.fill();b.shadowColor="transparent"}if(a.onDrawTitleBox)a.onDrawTitleBox(b,f,d,this.ds.scale);else l==e.ROUND_SHAPE||l==e.CIRCLE_SHAPE||l==e.CARD_SHAPE?(c&&(b.fillStyle="black",b.beginPath(),b.arc(0.5*f,-0.5*f,6,0,2*Math.PI),b.fill()),b.fillStyle=a.boxcolor||e.NODE_DEFAULT_BOXCOLOR,c?b.fillRect(0.5*f-5,-0.5*f-5,10,10):(b.beginPath(),b.arc(0.5*f,-0.5*f,5,0,2*Math.PI),b.fill())):(c&&(b.fillStyle="black",b.fillRect(0.5*(f-10)-1,-0.5*(f+10)-1,12,12)),b.fillStyle=a.boxcolor||e.NODE_DEFAULT_BOXCOLOR, -b.fillRect(0.5*(f-10),-0.5*(f+10),10,10));b.globalAlpha=s;if(a.onDrawTitleText)a.onDrawTitleText(b,f,d,this.ds.scale,this.title_text_font,r);!c&&(b.font=this.title_text_font,c=String(a.getTitle()))&&(b.fillStyle=r?"white":a.constructor.title_text_color||this.node_title_color,a.flags.collapsed?(b.textAlign="left",b.measureText(c),b.fillText(c.substr(0,20),f,e.NODE_TITLE_TEXT_Y-f),b.textAlign="left"):(b.textAlign="left",b.fillText(c,f,e.NODE_TITLE_TEXT_Y-f)));if(a.onDrawTitle)a.onDrawTitle(b)}if(r){if(a.onBounding)a.onBounding(u); -q==e.TRANSPARENT_TITLE&&(u[1]-=f,u[3]+=f);b.lineWidth=1;b.globalAlpha=0.8;b.beginPath();l==e.BOX_SHAPE?b.rect(-6+u[0],-6+u[1],12+u[2],12+u[3]):l==e.ROUND_SHAPE||l==e.CARD_SHAPE&&a.flags.collapsed?b.roundRect(-6+u[0],-6+u[1],12+u[2],12+u[3],2*this.round_radius):l==e.CARD_SHAPE?b.roundRect(-6+u[0],-6+u[1],12+u[2],12+u[3],2*this.round_radius,2):l==e.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0]+6,0,2*Math.PI);b.strokeStyle="#FFF";b.stroke();b.strokeStyle=g;b.globalAlpha=1}};var F=new Float32Array(4), -q=new Float32Array(4),p=new Float32Array(2),l=new Float32Array(2);k.prototype.drawConnections=function(a){var b=e.getTime(),d=this.visible_area;F[0]=d[0]-20;F[1]=d[1]-20;F[2]=d[2]+40;F[3]=d[3]+40;a.lineWidth=this.connections_width;a.fillStyle="#AAA";a.strokeStyle="#AAA";a.globalAlpha=this.editor_alpha;for(var d=this.graph._nodes,g=0,f=d.length;gq[2]&&(q[0]+=q[2],q[2]=Math.abs(q[2]));0>q[3]&&(q[1]+=q[3],q[3]=Math.abs(q[3]));if(t(q,F)){var n=k.outputs[y],y=r.inputs[s];if(n&&y&&(k=n.dir||(k.horizontal?e.DOWN:e.RIGHT),y=y.dir||(r.horizontal?e.UP:e.LEFT),this.renderLink(a,h,v,c,!1,0,null,k,y),c&&c._last_time&&1E3>b-c._last_time)){var n= -2-0.002*(b-c._last_time),M=a.globalAlpha;a.globalAlpha=M*n;this.renderLink(a,h,v,c,!0,n,"white",k,y);a.globalAlpha=M}}}}}}a.globalAlpha=1};k.prototype.renderLink=function(a,b,d,g,f,r,c,l,p,q){g&&this.visible_links.push(g);!c&&g&&(c=g.color||k.link_type_colors[g.type]);c||(c=this.default_link_color);null!=g&&this.highlighted_links[g.id]&&(c="#FFF");l=l||e.RIGHT;p=p||e.LEFT;var y=C(b,d);this.render_connections_border&&0.6b[1]?0:Math.PI,a.save(),a.translate(v[0],v[1]),a.rotate(h),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore(),a.save(),a.translate(q[0],q[1]),a.rotate(n),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill());if(r)for(a.fillStyle= -c,v=0;5>v;++v)r=(0.001*e.getTime()+0.2*v)%1,f=this.computeConnectionPoint(b,d,r,l,p),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill()};k.prototype.computeConnectionPoint=function(a,b,d,g,f){g=g||e.RIGHT;f=f||e.LEFT;var r=C(a,b),c=[a[0],a[1]],l=[b[0],b[1]];switch(g){case e.LEFT:c[0]+=-0.25*r;break;case e.RIGHT:c[0]+=0.25*r;break;case e.UP:c[1]+=-0.25*r;break;case e.DOWN:c[1]+=0.25*r}switch(f){case e.LEFT:l[0]+=-0.25*r;break;case e.RIGHT:l[0]+=0.25*r;break;case e.UP:l[1]+=-0.25*r;break;case e.DOWN:l[1]+= -0.25*r}g=(1-d)*(1-d)*(1-d);f=3*(1-d)*(1-d)*d;r=3*(1-d)*d*d;d*=d*d;return[g*a[0]+f*c[0]+r*l[0]+d*b[0],g*a[1]+f*c[1]+r*l[1]+d*b[1]]};k.prototype.drawExecutionOrder=function(a){a.shadowColor="transparent";a.globalAlpha=0.25;a.textAlign="center";a.strokeStyle="white";a.globalAlpha=0.75;for(var b=this.visible_nodes,d=0;dv.last_y&&s -v.options.max&&(v.value=v.options.max);else if("mousedown"==d.type){var y=v.options.values;y&&y.constructor===Function&&(y=v.options.values(v,a));var n=null;"number"!=v.type&&(n=y.constructor===Array?y:Object.keys(y));c=40>c?-1:c>l-40?1:0;if("number"==v.type)v.value+=0.1*c*(v.options.step||1),null!=v.options.min&&v.valuev.options.max&&(v.value=v.options.max);else if(c)q=-1,q=y.constructor===Object?n.indexOf(String(v.value))+c:n.indexOf(v.value)+ -c,q>=n.length&&(q=n.length-1),0>q&&(q=0),v.value=y.constructor===Array?y[q]:q;else{var t=y!=n?Object.values(y):y;new e.ContextMenu(t,{scale:Math.max(1,this.ds.scale),event:d,className:"dark",callback:m.bind(v)},q);var m=function(a,b,d){y!=n&&(a=t.indexOf(a));this.value=a;f(this,a);p.dirty_canvas=!0;return!1}}}else"mouseup"==d.type&&"number"==v.type&&(c=40>c?-1:c>l-40?1:0,200>d.click_time&&0==c&&this.prompt("Value",v.value,function(a){this.value=Number(a);f(this,this.value)}.bind(v),d));g!=v.value&& -setTimeout(function(){f(this,this.value)}.bind(v),20);this.dirty_canvas=!0;break;case "toggle":"mousedown"==d.type&&(v.value=!v.value,setTimeout(function(){f(v,v.value)},20));break;case "string":case "text":"mousedown"==d.type&&this.prompt("Value",v.value,function(a){this.value=a;f(this,a)}.bind(v),d);break;default:v.mouse&&(this.dirty_canvas=v.mouse(d,[c,s],a))}return v}}}return null};k.prototype.drawGroups=function(a,b){if(this.graph){var d=this.graph._groups;b.save();b.globalAlpha=0.5*this.editor_alpha; -for(var g=0;gthis.ds.scale,q=a._shape||a.constructor.shape||h.ROUND_SHAPE,p=a.constructor.title_mode,y=!0;p==h.TRANSPARENT_TITLE?y=!1:p==h.AUTOHIDE_TITLE&&c&&(y=!0);u[0]=0;u[1]=y?-f:0;u[2]=d[0]+1;u[3]=y?d[1]+f:d[1];c=b.globalAlpha;b.beginPath();q==h.BOX_SHAPE|| +l?b.fillRect(u[0],u[1],u[2],u[3]):q==h.ROUND_SHAPE||q==h.CARD_SHAPE?b.roundRect(u[0],u[1],u[2],u[3],this.round_radius,q==h.CARD_SHAPE?0:this.round_radius):q==h.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0],0,2*Math.PI);b.fill();a.flags.collapsed||(b.shadowColor="transparent",b.fillStyle="rgba(0,0,0,0.2)",b.fillRect(0,-1,u[2],2));b.shadowColor="transparent";if(a.onDrawBackground)a.onDrawBackground(b,this,this.canvas);if(y||p==h.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,f,d,this.ds.scale, +g);else if(p!=h.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){y=a.constructor.title_color||g;a.flags.collapsed&&(b.shadowColor=h.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var t=k.gradients[y];t||(t=k.gradients[y]=b.createLinearGradient(0,0,400,0),t.addColorStop(0,y),t.addColorStop(1,"#000"));b.fillStyle=t}else b.fillStyle=y;b.beginPath();q==h.BOX_SHAPE||l?b.rect(0,-f,d[0]+1,f):q!=h.ROUND_SHAPE&&q!=h.CARD_SHAPE||b.roundRect(0,-f,d[0]+1,f,this.round_radius,a.flags.collapsed? +this.round_radius:0);b.fill();b.shadowColor="transparent"}if(a.onDrawTitleBox)a.onDrawTitleBox(b,f,d,this.ds.scale);else q==h.ROUND_SHAPE||q==h.CIRCLE_SHAPE||q==h.CARD_SHAPE?(l&&(b.fillStyle="black",b.beginPath(),b.arc(0.5*f,-0.5*f,6,0,2*Math.PI),b.fill()),b.fillStyle=a.boxcolor||h.NODE_DEFAULT_BOXCOLOR,l?b.fillRect(0.5*f-5,-0.5*f-5,10,10):(b.beginPath(),b.arc(0.5*f,-0.5*f,5,0,2*Math.PI),b.fill())):(l&&(b.fillStyle="black",b.fillRect(0.5*(f-10)-1,-0.5*(f+10)-1,12,12)),b.fillStyle=a.boxcolor||h.NODE_DEFAULT_BOXCOLOR, +b.fillRect(0.5*(f-10),-0.5*(f+10),10,10));b.globalAlpha=c;if(a.onDrawTitleText)a.onDrawTitleText(b,f,d,this.ds.scale,this.title_text_font,r);!l&&(b.font=this.title_text_font,l=String(a.getTitle()))&&(b.fillStyle=r?"white":a.constructor.title_text_color||this.node_title_color,a.flags.collapsed?(b.textAlign="left",b.measureText(l),b.fillText(l.substr(0,20),f,h.NODE_TITLE_TEXT_Y-f),b.textAlign="left"):(b.textAlign="left",b.fillText(l,f,h.NODE_TITLE_TEXT_Y-f)));if(a.onDrawTitle)a.onDrawTitle(b)}if(r){if(a.onBounding)a.onBounding(u); +p==h.TRANSPARENT_TITLE&&(u[1]-=f,u[3]+=f);b.lineWidth=1;b.globalAlpha=0.8;b.beginPath();q==h.BOX_SHAPE?b.rect(-6+u[0],-6+u[1],12+u[2],12+u[3]):q==h.ROUND_SHAPE||q==h.CARD_SHAPE&&a.flags.collapsed?b.roundRect(-6+u[0],-6+u[1],12+u[2],12+u[3],2*this.round_radius):q==h.CARD_SHAPE?b.roundRect(-6+u[0],-6+u[1],12+u[2],12+u[3],2*this.round_radius,2):q==h.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0]+6,0,2*Math.PI);b.strokeStyle="#FFF";b.stroke();b.strokeStyle=g;b.globalAlpha=1}};var G=new Float32Array(4), +q=new Float32Array(4),p=new Float32Array(2),l=new Float32Array(2);k.prototype.drawConnections=function(a){var b=h.getTime(),d=this.visible_area;G[0]=d[0]-20;G[1]=d[1]-20;G[2]=d[2]+40;G[3]=d[3]+40;a.lineWidth=this.connections_width;a.fillStyle="#AAA";a.strokeStyle="#AAA";a.globalAlpha=this.editor_alpha;for(var d=this.graph._nodes,g=0,f=d.length;gq[2]&&(q[0]+=q[2],q[2]=Math.abs(q[2]));0>q[3]&&(q[1]+=q[3],q[3]=Math.abs(q[3]));if(s(q,G)){var m=y.outputs[e],e=r.inputs[c];if(m&&e&&(y=m.dir||(y.horizontal?h.DOWN:h.RIGHT),e=e.dir||(r.horizontal?h.UP:h.LEFT),this.renderLink(a,n,t,k,!1,0,null,y,e),k&&k._last_time&&1E3>b-k._last_time)){var m= +2-0.002*(b-k._last_time),H=a.globalAlpha;a.globalAlpha=H*m;this.renderLink(a,n,t,k,!0,m,"white",y,e);a.globalAlpha=H}}}}}}a.globalAlpha=1};k.prototype.renderLink=function(a,b,d,g,f,r,c,l,q,p){g&&this.visible_links.push(g);!c&&g&&(c=g.color||k.link_type_colors[g.type]);c||(c=this.default_link_color);null!=g&&this.highlighted_links[g.id]&&(c="#FFF");l=l||h.RIGHT;q=q||h.LEFT;var y=C(b,d);this.render_connections_border&&0.6b[1]?0:Math.PI,a.save(),a.translate(t[0],t[1]),a.rotate(e),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore(),a.save(),a.translate(p[0],p[1]),a.rotate(H),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill());if(r)for(a.fillStyle= +c,t=0;5>t;++t)r=(0.001*h.getTime()+0.2*t)%1,f=this.computeConnectionPoint(b,d,r,l,q),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill()};k.prototype.computeConnectionPoint=function(a,b,d,g,f){g=g||h.RIGHT;f=f||h.LEFT;var r=C(a,b),c=[a[0],a[1]],l=[b[0],b[1]];switch(g){case h.LEFT:c[0]+=-0.25*r;break;case h.RIGHT:c[0]+=0.25*r;break;case h.UP:c[1]+=-0.25*r;break;case h.DOWN:c[1]+=0.25*r}switch(f){case h.LEFT:l[0]+=-0.25*r;break;case h.RIGHT:l[0]+=0.25*r;break;case h.UP:l[1]+=-0.25*r;break;case h.DOWN:l[1]+= +0.25*r}g=(1-d)*(1-d)*(1-d);f=3*(1-d)*(1-d)*d;r=3*(1-d)*d*d;d*=d*d;return[g*a[0]+f*c[0]+r*l[0]+d*b[0],g*a[1]+f*c[1]+r*l[1]+d*b[1]]};k.prototype.drawExecutionOrder=function(a){a.shadowColor="transparent";a.globalAlpha=0.25;a.textAlign="center";a.strokeStyle="white";a.globalAlpha=0.75;for(var b=this.visible_nodes,d=0;dt.last_y&&l +t.options.max&&(t.value=t.options.max);else if("mousedown"==d.type){var n=t.options.values;n&&n.constructor===Function&&(n=t.options.values(t,a));var s=null;"number"!=t.type&&(s=n.constructor===Array?n:Object.keys(n));c=40>c?-1:c>q-40?1:0;if("number"==t.type)t.value+=0.1*c*(t.options.step||1),null!=t.options.min&&t.valuet.options.max&&(t.value=t.options.max);else if(c)e=-1,e=n.constructor===Object?s.indexOf(String(t.value))+c:s.indexOf(t.value)+ +c,e>=s.length&&(e=s.length-1),0>e&&(e=0),t.value=n.constructor===Array?n[e]:e;else{var m=n!=s?Object.values(n):n;new h.ContextMenu(m,{scale:Math.max(1,this.ds.scale),event:d,className:"dark",callback:B.bind(t)},e);var B=function(a,b,d){n!=s&&(a=m.indexOf(a));this.value=a;f(this,a);p.dirty_canvas=!0;return!1}}}else"mouseup"==d.type&&"number"==t.type&&(c=40>c?-1:c>q-40?1:0,200>d.click_time&&0==c&&this.prompt("Value",t.value,function(a){this.value=Number(a);f(this,this.value)}.bind(t),d));g!=t.value&& +setTimeout(function(){f(this,this.value)}.bind(t),20);this.dirty_canvas=!0;break;case "toggle":"mousedown"==d.type&&(t.value=!t.value,setTimeout(function(){f(t,t.value)},20));break;case "string":case "text":"mousedown"==d.type&&this.prompt("Value",t.value,function(a){this.value=a;f(this,a)}.bind(t),d);break;default:t.mouse&&(this.dirty_canvas=t.mouse(d,[c,l],a))}return t}}}return null};k.prototype.drawGroups=function(a,b){if(this.graph){var d=this.graph._groups;b.save();b.globalAlpha=0.5*this.editor_alpha; +for(var g=0;gd&&0.01>b.editor_alpha&&(clearInterval(g),1>d&&(b.live_mode=!0));1"+ -q+""+a+"",value:q});if(p.length)return new e.ContextMenu(p,{event:d,callback:c,parentMenu:g,allow_html:!0,node:f},b),!1}};k.decodeHTML=function(a){var b=document.createElement("div");b.innerText=a;return b.innerHTML};k.onResizeNode=function(a,b,d,g,f){if(f){f.size=f.computeSize();if(f.onResize)f.onResize(f.size);f.setDirtyCanvas(!0,!0)}};k.prototype.showLinkMenu=function(a,b){var d=this;console.log(a);var g=new e.ContextMenu(["Add Node",null,"Delete"],{event:b, -title:null!=a.data?a.data.constructor.name:null,callback:function(b,e,c){switch(b){case "Add Node":k.onMenuAdd(null,null,c,g,function(b){console.log("node autoconnect");var f=d.graph.getNodeById(a.origin_id),g=d.graph.getNodeById(a.target_id);b.inputs&&b.inputs.length&&b.outputs&&b.outputs.length&&f.outputs[a.origin_slot].type==b.inputs[0].type&&b.outputs[0].type==g.inputs[0].type&&(f.connect(a.origin_slot,b,0),b.connect(0,g,a.target_slot),b.pos[0]-=0.5*b.size[0])});break;case "Delete":d.graph.removeLink(a.id)}}}); -return!1};k.onShowPropertyEditor=function(a,b,d,g,f){function e(){var b=p.value;"Number"==a.type?b=Number(b):"Boolean"==a.type&&(b=Boolean(b));f[c]=b;l.parentNode&&l.parentNode.removeChild(l);f.setDirtyCanvas(!0,!0)}var c=a.property||"title";b=f[c];var l=document.createElement("div");l.className="graphdialog";l.innerHTML="";l.querySelector(".name").innerText=c;var p=l.querySelector("input");p&&(p.value=b,p.addEventListener("blur", -function(a){this.focus()}),p.addEventListener("keydown",function(a){13==a.keyCode&&(e(),a.preventDefault(),a.stopPropagation())}));b=k.active_canvas.canvas;d=b.getBoundingClientRect();var q=g=-20;d&&(g-=d.left,q-=d.top);event?(l.style.left=event.clientX+g+"px",l.style.top=event.clientY+q+"px"):(l.style.left=0.5*b.width+g+"px",l.style.top=0.5*b.height+q+"px");l.querySelector("button").addEventListener("click",e);b.parentNode.appendChild(l)};k.prototype.prompt=function(a,b,d,g){var f=this;a=a||"";var e= -!1,c=document.createElement("div");c.className="graphdialog rounded";c.innerHTML=" ";c.close=function(){f.prompt_box=null;c.parentNode&&c.parentNode.removeChild(c)};1k.search_limit)break}}Q=null;if(Array.prototype.filter)Q=Object.keys(e.registered_node_types).filter(p);else for(l in Q=[],e.registered_node_types)p(l)&&Q.push(l);for(l=0;lk.search_limit);l++);var p=function(a){var b=e.registered_node_types[a];return s&&b.filter!=s?!1:-1!==a.toLowerCase().indexOf(d)}}} -var f=this,c=k.active_canvas,l=c.canvas,p=l.ownerDocument||document,q=document.createElement("div");q.className="litegraph litesearchbox graphdialog rounded";q.innerHTML="Search
";q.close=function(){f.search_box=null;p.body.focus();p.body.style.overflow="";setTimeout(function(){f.canvas.focus()},20);q.parentNode&&q.parentNode.removeChild(q)};var h=null;1l.height-200&&(y.style.maxHeight=l.height-a.layerY-20+"px");m.focus(); -return q};k.prototype.showEditPropertyValue=function(a,b,d){function g(){f(v.value)}function f(f){"number"==typeof a.properties[b]&&(f=Number(f));if("array"==e||"object"==e)f=JSON.parse(f);a.properties[b]=f;a._graph&&a._graph._version++;if(a.onPropertyChanged)a.onPropertyChanged(b,f);if(d.onclose)d.onclose();h.close();a.setDirtyCanvas(!0,!0)}if(a&&void 0!==a.properties[b]){d=d||{};var c=a.getPropertyInfo(b),e=c.type,l="";if("string"==e||"number"==e||"array"==e||"object"==e)l=""; -else if("enum"==e&&c.values){var l=""}else if("boolean"==e)l="";else{console.warn("unknown type: "+e);return}var h=this.createDialog(""+b+""+l+"",d);if("enum"== -e&&c.values){var v=h.querySelector("select");v.addEventListener("change",function(a){f(a.target.value)})}else if("boolean"==e)(v=h.querySelector("input"))&&v.addEventListener("click",function(a){f(!!v.checked)});else if(v=h.querySelector("input"))v.addEventListener("blur",function(a){this.focus()}),p=void 0!==a.properties[b]?a.properties[b]:"",p=JSON.stringify(p),v.value=p,v.addEventListener("keydown",function(a){13==a.keyCode&&(g(),a.preventDefault(),a.stopPropagation())});h.querySelector("button").addEventListener("click", -g);return h}};k.prototype.createDialog=function(a,b){b=b||{};var d=document.createElement("div");d.className="graphdialog";d.innerHTML=a;var g=this.canvas.getBoundingClientRect(),f=-20,c=-20;g&&(f-=g.left,c-=g.top);b.position?(f+=b.position[0],c+=b.position[1]):b.event?(f+=b.event.clientX,c+=b.event.clientY):(f+=0.5*this.canvas.width,c+=0.5*this.canvas.height);d.style.left=f+"px";d.style.top=c+"px";this.canvas.parentNode.appendChild(d);d.close=function(){this.parentNode&&this.parentNode.removeChild(this)}; -return d};k.onMenuNodeCollapse=function(a,b,d,g,f){f.collapse()};k.onMenuNodePin=function(a,b,d,g,f){f.pin()};k.onMenuNodeMode=function(a,b,d,g,f){new e.ContextMenu(["Always","On Event","On Trigger","Never"],{event:d,callback:function(a){if(f)switch(a){case "On Event":f.mode=e.ON_EVENT;break;case "On Trigger":f.mode=e.ON_TRIGGER;break;case "Never":f.mode=e.NEVER;break;default:f.mode=e.ALWAYS}},parentMenu:g,node:f});return!1};k.onMenuNodeColors=function(a,b,d,g,f){if(!f)throw"no node for color";b= -[];b.push({value:null,content:"No color"});for(var c in k.node_colors)a=k.node_colors[c],a={value:c,content:""+c+""},b.push(a);new e.ContextMenu(b,{event:d,callback:function(a){f&&((a=a.value?k.node_colors[a.value]:null)?f.constructor===e.LGraphGroup?f.color=a.groupcolor:(f.color=a.color,f.bgcolor=a.bgcolor): -(delete f.color,delete f.bgcolor),f.setDirtyCanvas(!0,!0))},parentMenu:g,node:f});return!1};k.onMenuNodeShapes=function(a,b,d,g,f){if(!f)throw"no node passed";new e.ContextMenu(e.VALID_SHAPES,{event:d,callback:function(a){f&&(f.shape=a,f.setDirtyCanvas(!0))},parentMenu:g,node:f});return!1};k.onMenuNodeRemove=function(a,b,d,g,f){if(!f)throw"no node passed";!1!==f.removable&&(f.graph.remove(f),f.setDirtyCanvas(!0,!0))};k.onMenuNodeToSubgraph=function(a,b,d,g,f){a=f.graph;if(b=k.active_canvas)d=Object.values(b.selected_nodes|| -{}),d.length||(d=[f]),g=e.createNode("graph/subgraph"),g.pos=f.pos.concat(),a.add(g),g.buildFromNodes(d),b.deselectAllNodes(),f.setDirtyCanvas(!0,!0)};k.onMenuNodeClone=function(a,b,d,g,f){!1!=f.clonable&&(a=f.clone())&&(a.pos=[f.pos[0]+5,f.pos[1]+5],f.graph.add(a),f.setDirtyCanvas(!0,!0))};k.node_colors={red:{color:"#322",bgcolor:"#533",groupcolor:"#A88"},brown:{color:"#332922",bgcolor:"#593930",groupcolor:"#b06634"},green:{color:"#232",bgcolor:"#353",groupcolor:"#8A8"},blue:{color:"#223",bgcolor:"#335", +"mousedown";break;case "touchmove":d="mousemove";break;case "touchend":d="mouseup";break;default:return}var g=this.getCanvasWindow(),f=g.document.createEvent("MouseEvent");f.initMouseEvent(d,!0,!0,g,1,b.screenX,b.screenY,b.clientX,b.clientY,!1,!1,!1,!1,0,null);b.target.dispatchEvent(f);a.preventDefault()};k.onGroupAdd=function(a,b,d){a=k.active_canvas;a.getCanvasWindow();b=new h.LGraphGroup;b.pos=a.convertEventToCanvasOffset(d);a.graph.add(b)};k.onMenuAdd=function(a,b,d,g,f){function c(a,b){var d= +g.getFirstEvent(),r=h.createNode(a.value);r&&(r.pos=l.convertEventToCanvasOffset(d),l.graph.add(r));f&&f(r)}var l=k.active_canvas,q=l.getCanvasWindow();a=h.getNodeTypesCategories(l.filter);b=[];for(var p in a)a[p]&&b.push({value:a[p],content:a[p],has_submenu:!0});var e=new h.ContextMenu(b,{event:d,callback:function(a,b,d){a=h.getNodeTypesInCategory(a.value,l.filter);b=[];for(var f in a)a[f].skip_list||b.push({content:a[f].title,value:a[f].type});new h.ContextMenu(b,{event:d,callback:c,parentMenu:e}, +q);return!1},parentMenu:g},q);return!1};k.onMenuCollapseAll=function(){};k.onMenuNodeEdit=function(){};k.showMenuNodeOptionalInputs=function(a,b,d,g,f){function c(a,b,d){f&&(a.callback&&a.callback.call(l,f,a,b,d),a.value&&(f.addInput(a.value[0],a.value[1],a.value[2]),f.setDirtyCanvas(!0,!0)))}if(f){var l=this;a=k.active_canvas.getCanvasWindow();b=f.optional_inputs;f.onGetInputs&&(b=f.onGetInputs());var q=[];if(b)for(var p in b){var e=b[p];if(e){var y=e[0];e[2]&&e[2].label&&(y=e[2].label);y={content:y, +value:e};e[1]==h.ACTION&&(y.className="event");q.push(y)}else q.push(null)}this.onMenuNodeInputs&&(q=this.onMenuNodeInputs(q));if(q.length)return new h.ContextMenu(q,{event:d,callback:c,parentMenu:g,node:f},a),!1}};k.showMenuNodeOptionalOutputs=function(a,b,d,g,f){function c(a,b,d){if(f&&(a.callback&&a.callback.call(l,f,a,b,d),a.value))if(d=a.value[1],!d||d.constructor!==Object&&d.constructor!==Array)f.addOutput(a.value[0],a.value[1],a.value[2]),f.setDirtyCanvas(!0,!0);else{a=[];for(var q in d)a.push({content:q, +value:d[q]});new h.ContextMenu(a,{event:b,callback:c,parentMenu:g,node:f});return!1}}if(f){var l=this;a=k.active_canvas.getCanvasWindow();b=f.optional_outputs;f.onGetOutputs&&(b=f.onGetOutputs());var q=[];if(b)for(var p in b){var e=b[p];if(!e)q.push(null);else if(!f.flags||!f.flags.skip_repeated_outputs||-1==f.findOutputSlot(e[0])){var y=e[0];e[2]&&e[2].label&&(y=e[2].label);y={content:y,value:e};e[1]==h.EVENT&&(y.className="event");q.push(y)}}this.onMenuNodeOutputs&&(q=this.onMenuNodeOutputs(q)); +if(q.length)return new h.ContextMenu(q,{event:d,callback:c,parentMenu:g,node:f},a),!1}};k.onShowMenuNodeProperties=function(a,b,d,g,f){function c(a,b,d,g){f&&(b=this.getBoundingClientRect(),l.showEditPropertyValue(f,a.value,{position:[b.left,b.top]}))}if(f&&f.properties){var l=k.active_canvas;b=l.getCanvasWindow();var q=[],p;for(p in f.properties)a=void 0!==f.properties[p]?f.properties[p]:" ","object"==typeof a&&(a=JSON.stringify(a)),a=k.decodeHTML(a),q.push({content:""+ +p+""+a+"",value:p});if(q.length)return new h.ContextMenu(q,{event:d,callback:c,parentMenu:g,allow_html:!0,node:f},b),!1}};k.decodeHTML=function(a){var b=document.createElement("div");b.innerText=a;return b.innerHTML};k.onResizeNode=function(a,b,d,g,f){if(f){f.size=f.computeSize();if(f.onResize)f.onResize(f.size);f.setDirtyCanvas(!0,!0)}};k.prototype.showLinkMenu=function(a,b){var d=this;console.log(a);var g=new h.ContextMenu(["Add Node",null,"Delete"],{event:b, +title:null!=a.data?a.data.constructor.name:null,callback:function(b,h,c){switch(b){case "Add Node":k.onMenuAdd(null,null,c,g,function(b){console.log("node autoconnect");var f=d.graph.getNodeById(a.origin_id),g=d.graph.getNodeById(a.target_id);b.inputs&&b.inputs.length&&b.outputs&&b.outputs.length&&f.outputs[a.origin_slot].type==b.inputs[0].type&&b.outputs[0].type==g.inputs[0].type&&(f.connect(a.origin_slot,b,0),b.connect(0,g,a.target_slot),b.pos[0]-=0.5*b.size[0])});break;case "Delete":d.graph.removeLink(a.id)}}}); +return!1};k.onShowPropertyEditor=function(a,b,d,g,f){function h(){var b=q.value;"Number"==a.type?b=Number(b):"Boolean"==a.type&&(b=Boolean(b));f[c]=b;l.parentNode&&l.parentNode.removeChild(l);f.setDirtyCanvas(!0,!0)}var c=a.property||"title";b=f[c];var l=document.createElement("div");l.className="graphdialog";l.innerHTML="";l.querySelector(".name").innerText=c;var q=l.querySelector("input");q&&(q.value=b,q.addEventListener("blur", +function(a){this.focus()}),q.addEventListener("keydown",function(a){13==a.keyCode&&(h(),a.preventDefault(),a.stopPropagation())}));b=k.active_canvas.canvas;d=b.getBoundingClientRect();var p=g=-20;d&&(g-=d.left,p-=d.top);event?(l.style.left=event.clientX+g+"px",l.style.top=event.clientY+p+"px"):(l.style.left=0.5*b.width+g+"px",l.style.top=0.5*b.height+p+"px");l.querySelector("button").addEventListener("click",h);b.parentNode.appendChild(l)};k.prototype.prompt=function(a,b,d,g){var f=this;a=a||"";var h= +!1,c=document.createElement("div");c.className="graphdialog rounded";c.innerHTML=" ";c.close=function(){f.prompt_box=null;c.parentNode&&c.parentNode.removeChild(c)};1k.search_limit)break}}p=null;if(Array.prototype.filter)p=Object.keys(h.registered_node_types).filter(x);else for(l in p=[],h.registered_node_types)x(l)&&p.push(l);for(l=0;lk.search_limit);l++);var x=function(a){var b=h.registered_node_types[a];return q&&b.filter!=q?!1:-1!==a.toLowerCase().indexOf(d)}}} +var f=this,c=k.active_canvas,l=c.canvas,q=l.ownerDocument||document,p=document.createElement("div");p.className="litegraph litesearchbox graphdialog rounded";p.innerHTML="Search
";p.close=function(){f.search_box=null;q.body.focus();q.body.style.overflow="";setTimeout(function(){f.canvas.focus()},20);p.parentNode&&p.parentNode.removeChild(p)};var e=null;1l.height-200&&(y.style.maxHeight=l.height-a.layerY-20+"px");m.focus(); +return p};k.prototype.showEditPropertyValue=function(a,b,d){function g(){f(t.value)}function f(f){"number"==typeof a.properties[b]&&(f=Number(f));if("array"==h||"object"==h)f=JSON.parse(f);a.properties[b]=f;a._graph&&a._graph._version++;if(a.onPropertyChanged)a.onPropertyChanged(b,f);if(d.onclose)d.onclose();e.close();a.setDirtyCanvas(!0,!0)}if(a&&void 0!==a.properties[b]){d=d||{};var c=a.getPropertyInfo(b),h=c.type,l="";if("string"==h||"number"==h||"array"==h||"object"==h)l=""; +else if("enum"==h&&c.values){var l=""}else if("boolean"==h)l="";else{console.warn("unknown type: "+h);return}var e=this.createDialog(""+b+""+l+"",d);if("enum"== +h&&c.values){var t=e.querySelector("select");t.addEventListener("change",function(a){f(a.target.value)})}else if("boolean"==h)(t=e.querySelector("input"))&&t.addEventListener("click",function(a){f(!!t.checked)});else if(t=e.querySelector("input"))t.addEventListener("blur",function(a){this.focus()}),p=void 0!==a.properties[b]?a.properties[b]:"",p=JSON.stringify(p),t.value=p,t.addEventListener("keydown",function(a){13==a.keyCode&&(g(),a.preventDefault(),a.stopPropagation())});e.querySelector("button").addEventListener("click", +g);return e}};k.prototype.createDialog=function(a,b){b=b||{};var d=document.createElement("div");d.className="graphdialog";d.innerHTML=a;var g=this.canvas.getBoundingClientRect(),f=-20,c=-20;g&&(f-=g.left,c-=g.top);b.position?(f+=b.position[0],c+=b.position[1]):b.event?(f+=b.event.clientX,c+=b.event.clientY):(f+=0.5*this.canvas.width,c+=0.5*this.canvas.height);d.style.left=f+"px";d.style.top=c+"px";this.canvas.parentNode.appendChild(d);d.close=function(){this.parentNode&&this.parentNode.removeChild(this)}; +return d};k.onMenuNodeCollapse=function(a,b,d,g,f){f.collapse()};k.onMenuNodePin=function(a,b,d,g,f){f.pin()};k.onMenuNodeMode=function(a,b,d,g,f){new h.ContextMenu(["Always","On Event","On Trigger","Never"],{event:d,callback:function(a){if(f)switch(a){case "On Event":f.mode=h.ON_EVENT;break;case "On Trigger":f.mode=h.ON_TRIGGER;break;case "Never":f.mode=h.NEVER;break;default:f.mode=h.ALWAYS}},parentMenu:g,node:f});return!1};k.onMenuNodeColors=function(a,b,d,g,f){if(!f)throw"no node for color";b= +[];b.push({value:null,content:"No color"});for(var c in k.node_colors)a=k.node_colors[c],a={value:c,content:""+c+""},b.push(a);new h.ContextMenu(b,{event:d,callback:function(a){f&&((a=a.value?k.node_colors[a.value]:null)?f.constructor===h.LGraphGroup?f.color=a.groupcolor:(f.color=a.color,f.bgcolor=a.bgcolor): +(delete f.color,delete f.bgcolor),f.setDirtyCanvas(!0,!0))},parentMenu:g,node:f});return!1};k.onMenuNodeShapes=function(a,b,d,g,f){if(!f)throw"no node passed";new h.ContextMenu(h.VALID_SHAPES,{event:d,callback:function(a){f&&(f.shape=a,f.setDirtyCanvas(!0))},parentMenu:g,node:f});return!1};k.onMenuNodeRemove=function(a,b,d,g,f){if(!f)throw"no node passed";!1!==f.removable&&(f.graph.remove(f),f.setDirtyCanvas(!0,!0))};k.onMenuNodeToSubgraph=function(a,b,d,g,f){a=f.graph;if(b=k.active_canvas)d=Object.values(b.selected_nodes|| +{}),d.length||(d=[f]),g=h.createNode("graph/subgraph"),g.pos=f.pos.concat(),a.add(g),g.buildFromNodes(d),b.deselectAllNodes(),f.setDirtyCanvas(!0,!0)};k.onMenuNodeClone=function(a,b,d,g,f){!1!=f.clonable&&(a=f.clone())&&(a.pos=[f.pos[0]+5,f.pos[1]+5],f.graph.add(a),f.setDirtyCanvas(!0,!0))};k.node_colors={red:{color:"#322",bgcolor:"#533",groupcolor:"#A88"},brown:{color:"#332922",bgcolor:"#593930",groupcolor:"#b06634"},green:{color:"#232",bgcolor:"#353",groupcolor:"#8A8"},blue:{color:"#223",bgcolor:"#335", groupcolor:"#88A"},pale_blue:{color:"#2a363b",bgcolor:"#3f5159",groupcolor:"#3f789e"},cyan:{color:"#233",bgcolor:"#355",groupcolor:"#8AA"},purple:{color:"#323",bgcolor:"#535",groupcolor:"#a1309b"},yellow:{color:"#432",bgcolor:"#653",groupcolor:"#b58b2a"},black:{color:"#222",bgcolor:"#000",groupcolor:"#444"}};k.prototype.getCanvasMenuOptions=function(){var a=null;this.getMenuOptions?a=this.getMenuOptions():(a=[{content:"Add Node",has_submenu:!0,callback:k.onMenuAdd},{content:"Add Group",callback:k.onGroupAdd}], this._graph_stack&&0Name",f),l=e.querySelector("input");l&&c&&(l.value=c.label||"");e.querySelector("button").addEventListener("click",function(a){l.value&&(c&&(c.label=l.value),d.setDirty(!0));e.close()})}},extra:a};a&&(c.title=a.type);var l=null;a&&(l=a.getSlotInPosition(b.canvasX,b.canvasY),k.active_node= -a);if(l){f=[];if(a.getSlotMenuOptions)f=a.getSlotMenuOptions(l);else{l&&l.output&&l.output.links&&l.output.links.length&&f.push({content:"Disconnect Links",slot:l});var q=l.input||l.output;f.push(q.locked?"Cannot remove":{content:"Remove Slot",slot:l});f.push(q.nameLocked?"Cannot rename":{content:"Rename Slot",slot:l})}c.title=(l.input?l.input.type:l.output.type)||"*";l.input&&l.input.type==e.ACTION&&(c.title="Action");l.output&&l.output.type==e.EVENT&&(c.title="Event")}else a?f=this.getNodeMenuOptions(a): -(f=this.getCanvasMenuOptions(),(l=this.graph.getGroupOnPos(b.canvasX,b.canvasY))&&f.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:l,options:this.getGroupMenuOptions(l)}}));f&&new e.ContextMenu(f,c,g)};"undefined"!=typeof window&&window.CanvasRenderingContext2D&&(window.CanvasRenderingContext2D.prototype.roundRect=function(a,b,d,g,f,c){void 0===f&&(f=5);void 0===c&&(c=f);this.moveTo(a+f,b);this.lineTo(a+d-f,b);this.quadraticCurveTo(a+d,b,a+d,b+f);this.lineTo(a+d,b+g-c); -this.quadraticCurveTo(a+d,b+g,a+d-c,b+g);this.lineTo(a+c,b+g);this.quadraticCurveTo(a,b+g,a,b+g-c);this.lineTo(a,b+f);this.quadraticCurveTo(a,b,a+f,b)});e.compareObjects=function(a,b){for(var d in a)if(a[d]!=b[d])return!1;return!0};e.distance=C;e.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+","+Math.round(255*a[2]).toFixed()+","+(4==a.length?a[3].toFixed(2):"1.0")+")"};e.isInsideRectangle=w;e.growBounding=function(a,b,d){b -a[2]&&(a[2]=b);da[3]&&(a[3]=d)};e.isInsideBounding=function(a,b){return a[0]b[1][0]||a[1]>b[1][1]?!1:!0};e.overlapBounding=t;e.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),d=0,g,f,c=0;6>c;c+=2)g="0123456789ABCDEF".indexOf(a.charAt(c)),f="0123456789ABCDEF".indexOf(a.charAt(c+1)),b[d]=16*g+f,d++;return b};e.num2hex=function(a){for(var b="#",d,g,f=0;3>f;f++)d=a[f]/16,g=a[f]%16,b+="0123456789ABCDEF".charAt(d)+"0123456789ABCDEF".charAt(g); +a.disconnectInput(b.slot);else if("Rename Slot"==b.content){b=b.slot;var c=b.input?a.getInputInfo(b.slot):a.getOutputInfo(b.slot),h=d.createDialog("Name",f),l=h.querySelector("input");l&&c&&(l.value=c.label||"");h.querySelector("button").addEventListener("click",function(a){l.value&&(c&&(c.label=l.value),d.setDirty(!0));h.close()})}},extra:a};a&&(c.title=a.type);var l=null;a&&(l=a.getSlotInPosition(b.canvasX,b.canvasY),k.active_node= +a);if(l){f=[];if(a.getSlotMenuOptions)f=a.getSlotMenuOptions(l);else{l&&l.output&&l.output.links&&l.output.links.length&&f.push({content:"Disconnect Links",slot:l});var q=l.input||l.output;f.push(q.locked?"Cannot remove":{content:"Remove Slot",slot:l});f.push(q.nameLocked?"Cannot rename":{content:"Rename Slot",slot:l})}c.title=(l.input?l.input.type:l.output.type)||"*";l.input&&l.input.type==h.ACTION&&(c.title="Action");l.output&&l.output.type==h.EVENT&&(c.title="Event")}else a?f=this.getNodeMenuOptions(a): +(f=this.getCanvasMenuOptions(),(l=this.graph.getGroupOnPos(b.canvasX,b.canvasY))&&f.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:l,options:this.getGroupMenuOptions(l)}}));f&&new h.ContextMenu(f,c,g)};"undefined"!=typeof window&&window.CanvasRenderingContext2D&&(window.CanvasRenderingContext2D.prototype.roundRect=function(a,b,d,g,f,c){void 0===f&&(f=5);void 0===c&&(c=f);this.moveTo(a+f,b);this.lineTo(a+d-f,b);this.quadraticCurveTo(a+d,b,a+d,b+f);this.lineTo(a+d,b+g-c); +this.quadraticCurveTo(a+d,b+g,a+d-c,b+g);this.lineTo(a+c,b+g);this.quadraticCurveTo(a,b+g,a,b+g-c);this.lineTo(a,b+f);this.quadraticCurveTo(a,b,a+f,b)});h.compareObjects=function(a,b){for(var d in a)if(a[d]!=b[d])return!1;return!0};h.distance=C;h.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+","+Math.round(255*a[2]).toFixed()+","+(4==a.length?a[3].toFixed(2):"1.0")+")"};h.isInsideRectangle=v;h.growBounding=function(a,b,d){b +a[2]&&(a[2]=b);da[3]&&(a[3]=d)};h.isInsideBounding=function(a,b){return a[0]b[1][0]||a[1]>b[1][1]?!1:!0};h.overlapBounding=s;h.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),d=0,g,f,c=0;6>c;c+=2)g="0123456789ABCDEF".indexOf(a.charAt(c)),f="0123456789ABCDEF".indexOf(a.charAt(c+1)),b[d]=16*g+f,d++;return b};h.num2hex=function(a){for(var b="#",d,g,f=0;3>f;f++)d=a[f]/16,g=a[f]%16,b+="0123456789ABCDEF".charAt(d)+"0123456789ABCDEF".charAt(g); return b};D.prototype.addItem=function(a,b,d){function g(a){var b=this.value;b&&b.has_submenu&&f.call(this,a)}function f(a){var b=this.value,f=!0;c.current_submenu&&c.current_submenu.close(a);if(d.callback){var g=d.callback.call(this,b,d,a,c,d.node);!0===g&&(f=!1)}if(b&&(b.callback&&!d.ignore_item_callbacks&&!0!==b.disabled&&(g=b.callback.call(this,b,d,a,c,d.extra),!0===g&&(f=!1)),b.submenu)){if(!b.submenu.options)throw"ContextMenu submenu needs options";new c.constructor(b.submenu.options,{callback:b.submenu.callback, -event:a,parentMenu:c,ignore_item_callbacks:b.submenu.ignore_item_callbacks,title:b.submenu.title,extra:b.submenu.extra,autoopen:d.autoopen});f=!1}f&&!c.lock&&c.close()}var c=this;d=d||{};var e=document.createElement("div");e.className="litemenu-entry submenu";var l=!1;if(null===b)e.classList.add("separator");else{e.innerHTML=b&&b.title?b.title:a;if(e.value=b)b.disabled&&(l=!0,e.classList.add("disabled")),(b.submenu||b.has_submenu)&&e.classList.add("has_submenu");"function"==typeof b?(e.dataset.value= -a,e.onclick_callback=b):e.dataset.value=b;b.className&&(e.className+=" "+b.className)}this.root.appendChild(e);l||e.addEventListener("click",f);d.autoopen&&e.addEventListener("mouseenter",g);return e};D.prototype.close=function(a,b){this.root.parentNode&&this.root.parentNode.removeChild(this.root);this.parentMenu&&!b&&(this.parentMenu.lock=!1,this.parentMenu.current_submenu=null,void 0===a?this.parentMenu.close():a&&!D.isCursorOverElement(a,this.parentMenu.root)&&D.trigger(this.parentMenu.root,"mouseleave", +event:a,parentMenu:c,ignore_item_callbacks:b.submenu.ignore_item_callbacks,title:b.submenu.title,extra:b.submenu.extra,autoopen:d.autoopen});f=!1}f&&!c.lock&&c.close()}var c=this;d=d||{};var h=document.createElement("div");h.className="litemenu-entry submenu";var l=!1;if(null===b)h.classList.add("separator");else{h.innerHTML=b&&b.title?b.title:a;if(h.value=b)b.disabled&&(l=!0,h.classList.add("disabled")),(b.submenu||b.has_submenu)&&h.classList.add("has_submenu");"function"==typeof b?(h.dataset.value= +a,h.onclick_callback=b):h.dataset.value=b;b.className&&(h.className+=" "+b.className)}this.root.appendChild(h);l||h.addEventListener("click",f);d.autoopen&&h.addEventListener("mouseenter",g);return h};D.prototype.close=function(a,b){this.root.parentNode&&this.root.parentNode.removeChild(this.root);this.parentMenu&&!b&&(this.parentMenu.lock=!1,this.parentMenu.current_submenu=null,void 0===a?this.parentMenu.close():a&&!D.isCursorOverElement(a,this.parentMenu.root)&&D.trigger(this.parentMenu.root,"mouseleave", a));this.current_submenu&&this.current_submenu.close(a,!0);this.root.closing_timer&&clearTimeout(this.root.closing_timer)};D.trigger=function(a,b,d,g){var f=document.createEvent("CustomEvent");f.initCustomEvent(b,!0,!0,d);f.srcElement=g;a.dispatchEvent?a.dispatchEvent(f):a.__events&&a.__events.dispatchEvent(f);return f};D.prototype.getTopMenu=function(){return this.options.parentMenu?this.options.parentMenu.getTopMenu():this};D.prototype.getFirstEvent=function(){return this.options.parentMenu?this.options.parentMenu.getFirstEvent(): -this.options.event};D.isCursorOverElement=function(a,b){var d=a.clientX,g=a.clientY,f=b.getBoundingClientRect();return f?g>f.top&&gf.left&&df.top&&gf.left&&dMath.abs(d))return g[1];d=(a-g[0])/d;return g[1]* -(1-d)+f[1]*d}}return 0}};A.prototype.draw=function(a,b,d,g,f,c){if(d=this.points){this.size=b;var e=b[0]-2*this.margin;b=b[1]-2*this.margin;f=f||"#666";a.save();a.translate(this.margin,this.margin);g&&(a.fillStyle="#111",a.fillRect(0,0,e,b),a.fillStyle="#222",a.fillRect(0.5*e,0,1,b),a.strokeStyle="#333",a.strokeRect(0,0,e,b));a.strokeStyle=f;c&&(a.globalAlpha=0.5);a.beginPath();for(g=0;ga[1])){var g=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=a[0]-this.margin,e=a[1]-this.margin;this.selected=this.getCloserPoint([c,e],30/b.ds.scale);-1==this.selected&&(g=[c/g,1-e/f],d.push(g),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(g),this.must_update=!0); -if(-1!=this.selected)return!0}};A.prototype.onMouseMove=function(a,b){var d=this.points;if(d){var g=this.selected;if(!(0>g)){var f=(a[0]-this.margin)/(this.size[0]-2*this.margin),c=(a[1]-this.margin)/(this.size[1]-2*this.margin);this._nearest=this.getCloserPoint([a[0]-this.margin,a[1]-this.margin],30/b.ds.scale);var e=d[g];if(e){var l=0==g||g==d.length-1;!l&&(-10>a[0]||a[0]>this.size[0]+10||-10>a[1]||a[1]>this.size[1]+10)?(d.splice(g,1),this.selected=-1):(e[0]=l?0==g?0:1:Math.clamp(f,0,1),e[1]=1- -Math.clamp(c,0,1),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(e),this.must_update=!0)}}}};A.prototype.onMouseUp=function(a,b){this.selected=-1;return!1};A.prototype.getCloserPoint=function(a,b){var d=this.points;if(!d)return-1;b=b||30;for(var g=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=d.length,e=[0,0],l=1E6,q=-1,p=0;pl||h>b||(q=p,l=h)}return q};e.CurveEditor=A;e.getParameterNames=function(a){return(a+ +(1-d)+f[1]*d}}return 0}};A.prototype.draw=function(a,b,d,g,f,c){if(d=this.points){this.size=b;var h=b[0]-2*this.margin;b=b[1]-2*this.margin;f=f||"#666";a.save();a.translate(this.margin,this.margin);g&&(a.fillStyle="#111",a.fillRect(0,0,h,b),a.fillStyle="#222",a.fillRect(0.5*h,0,1,b),a.strokeStyle="#333",a.strokeRect(0,0,h,b));a.strokeStyle=f;c&&(a.globalAlpha=0.5);a.beginPath();for(g=0;ga[1])){var g=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=a[0]-this.margin,h=a[1]-this.margin;this.selected=this.getCloserPoint([c,h],30/b.ds.scale);-1==this.selected&&(g=[c/g,1-h/f],d.push(g),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(g),this.must_update=!0); +if(-1!=this.selected)return!0}};A.prototype.onMouseMove=function(a,b){var d=this.points;if(d){var g=this.selected;if(!(0>g)){var f=(a[0]-this.margin)/(this.size[0]-2*this.margin),c=(a[1]-this.margin)/(this.size[1]-2*this.margin);this._nearest=this.getCloserPoint([a[0]-this.margin,a[1]-this.margin],30/b.ds.scale);var h=d[g];if(h){var l=0==g||g==d.length-1;!l&&(-10>a[0]||a[0]>this.size[0]+10||-10>a[1]||a[1]>this.size[1]+10)?(d.splice(g,1),this.selected=-1):(h[0]=l?0==g?0:1:Math.clamp(f,0,1),h[1]=1- +Math.clamp(c,0,1),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(h),this.must_update=!0)}}}};A.prototype.onMouseUp=function(a,b){this.selected=-1;return!1};A.prototype.getCloserPoint=function(a,b){var d=this.points;if(!d)return-1;b=b||30;for(var g=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=d.length,h=[0,0],l=1E6,q=-1,p=0;pl||e>b||(q=p,l=e)}return q};h.CurveEditor=A;h.getParameterNames=function(a){return(a+ "").replace(/[/][/].*$/gm,"").replace(/\s+/g,"").replace(/[/][*][^/*]*[*][/]/g,"").split("){",1)[0].replace(/^[^(]*[(]/,"").replace(/=[^,]+/g,"").split(",").filter(Boolean)};Math.clamp=function(a,b,d){return b>a?b:dthis.size[0]-g.NODE_TITLE_HEIGHT&&0>b[1]){var c=this;setTimeout(function(){d.openSubgraph(c.subgraph)},10)}};m.prototype.onAction=function(a,b){this.subgraph.onAction(a, @@ -259,22 +259,22 @@ b)};m.prototype.onExecute=function(){if(this.enabled=this.getInputOrProperty("en function(a,b){var d=this.findOutputSlot(a);-1!=d&&this.triggerSlot(d)};m.prototype.onSubgraphNewInput=function(a,b){-1==this.findInputSlot(a)&&this.addInput(a,b)};m.prototype.onSubgraphRenamedInput=function(a,b){var d=this.findInputSlot(a);-1!=d&&(this.getInputInfo(d).name=b)};m.prototype.onSubgraphTypeChangeInput=function(a,b){var d=this.findInputSlot(a);-1!=d&&(this.getInputInfo(d).type=b)};m.prototype.onSubgraphRemovedInput=function(a){a=this.findInputSlot(a);-1!=a&&this.removeInput(a)};m.prototype.onSubgraphNewOutput= function(a,b){-1==this.findOutputSlot(a)&&this.addOutput(a,b)};m.prototype.onSubgraphRenamedOutput=function(a,b){var d=this.findOutputSlot(a);-1!=d&&(this.getOutputInfo(d).name=b)};m.prototype.onSubgraphTypeChangeOutput=function(a,b){var d=this.findOutputSlot(a);-1!=d&&(this.getOutputInfo(d).type=b)};m.prototype.onSubgraphRemovedOutput=function(a){a=this.findInputSlot(a);-1!=a&&this.removeOutput(a)};m.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:"Open",callback:function(){a.openSubgraph(b.subgraph)}}]}; m.prototype.onResize=function(a){a[1]+=20};m.prototype.serialize=function(){var a=g.LGraphNode.prototype.serialize.call(this);a.subgraph=this.subgraph.serialize();return a};m.prototype.clone=function(){var a=g.createNode(this.type),b=this.serialize();delete b.id;delete b.inputs;delete b.outputs;a.configure(b);return a};m.prototype.buildFromNodes=function(a){for(var b={},d=0,g=0;g=e?this.trigger(null,h):this._pending.push([e,h])};k.prototype.onExecute=function(){var c=1E3* -this.graph.elapsed_time;this.isInputConnected(1)&&(this.properties.time_in_ms=this.getInputData(1));for(var h=0;h=h?this.trigger(null,e):this._pending.push([h,e])};k.prototype.onExecute=function(){var c=1E3* +this.graph.elapsed_time;this.isInputConnected(1)&&(this.properties.time_in_ms=this.getInputData(1));for(var e=0;eh[1]))return this.old_y=c.canvasY,this.captureInput(!0),this.mouse_captured=!0};n.prototype.onMouseMove=function(c){if(this.mouse_captured){var h=this.old_y-c.canvasY;c.shiftKey&&(h*=10);if(c.metaKey||c.altKey)h*=0.1;this.old_y=c.canvasY;c=this._remainder+h/n.pixels_threshold;this._remainder=c%1;c=Math.clamp(this.properties.value+ -(c|0)*this.properties.step,this.properties.min,this.properties.max);this.properties.value=c;this.graph._version++;this.setDirtyCanvas(!0)}};n.prototype.onMouseUp=function(c,h){200>c.click_time&&(this.properties.value=Math.clamp(this.properties.value+(h[1]>0.5*this.size[1]?-1:1)*this.properties.step,this.properties.min,this.properties.max),this.graph._version++,this.setDirtyCanvas(!0));this.mouse_captured&&(this.mouse_captured=!1,this.captureInput(!1))};A.registerNodeType("widget/number",n);h.title= -"Combo";h.desc="Widget to select from a list";h.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};h.prototype.onPropertyChanged=function(c,h){"values"==c?(this._values=h.split(";"),this.widget.options.values=this._values):"value"==c&&(this.widget.value=h)};A.registerNodeType("widget/combo",h);x.title="Knob";x.desc="Circular controller";x.size=[80,100];x.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/ -(this.properties.max-this.properties.min));var h=0.5*this.size[0],k=0.5*this.size[1],n=0.5*Math.min(this.size[0],this.size[1])-5;c.globalAlpha=1;c.save();c.translate(h,k);c.rotate(0.75*Math.PI);c.fillStyle="rgba(0,0,0,0.5)";c.beginPath();c.moveTo(0,0);c.arc(0,0,n,0,1.5*Math.PI);c.fill();c.strokeStyle="black";c.fillStyle=this.properties.color;c.lineWidth=2;c.beginPath();c.moveTo(0,0);c.arc(0,0,n-4,0,1.5*Math.PI*Math.max(0.01,this.value));c.closePath();c.fill();c.lineWidth=1;c.globalAlpha=1;c.restore(); -c.fillStyle="black";c.beginPath();c.arc(h,k,0.75*n,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":this.properties.color;c.beginPath();var t=this.value*Math.PI*1.5+0.75*Math.PI;c.arc(h+Math.cos(t)*n*0.65,k+Math.sin(t)*n*0.65,0.05*n,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":"#AAA";c.font=Math.floor(0.5*n)+"px Arial";c.textAlign="center";c.fillText(this.properties.value.toFixed(this.properties.precision),h,k+0.15*n)}};x.prototype.onExecute=function(){this.setOutputData(0, -this.properties.value);this.boxcolor=A.colorToString([this.value,this.value,this.value])};x.prototype.onMouseDown=function(c){this.center=[0.5*this.size[0],0.5*this.size[1]+20];this.radius=0.5*this.size[0];if(20>c.canvasY-this.pos[1]||A.distance([c.canvasX,c.canvasY],[this.pos[0]+this.center[0],this.pos[1]+this.center[1]])>this.radius)return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];this.captureInput(!0);return!0};x.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX- -this.pos[0],c.canvasY-this.pos[1]];var h=this.value,h=h-0.01*(c[1]-this.oldmouse[1]);1h&&(h=0);this.value=h;this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.oldmouse=c;this.setDirtyCanvas(!0)}};x.prototype.onMouseUp=function(c){this.oldmouse&&(this.oldmouse=null,this.captureInput(!1))};x.prototype.onPropertyChanged=function(c,h){if("min"==c||"max"==c||"value"==c)return this.properties[c]=parseFloat(h),!0};A.registerNodeType("widget/knob", -x);k.title="Inner Slider";k.prototype.onPropertyChanged=function(c,h){"value"==c&&(this.slider.value=h)};k.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};A.registerNodeType("widget/internal_slider",k);C.title="H.Slider";C.desc="Linear slider controller";C.prototype.onDrawForeground=function(c){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min));c.globalAlpha=1;c.lineWidth=1;c.fillStyle="#000";c.fillRect(2, +this.old_y=-1;this._precision=this._remainder=0;this.mouse_captured=!1}function e(){this.addOutput("","string");this.addOutput("change",A.EVENT);this.size=[80,60];this.properties={value:"A",values:"A;B;C"};this.old_y=-1;this.mouse_captured=!1;this._values=this.properties.values.split(";");var c=this;this.widgets_up=!0;this.widget=this.addWidget("combo","",this.properties.value,function(e){c.properties.value=e;c.triggerSlot(1,e)},{property:"value",values:this._values})}function w(){this.addOutput("", +"number");this.size=[64,84];this.properties={min:0,max:1,value:0.5,color:"#7AF",precision:2};this.value=-1}function k(){this.addOutput("","number");this.properties={value:0.5,min:0,max:1,text:"V"};var c=this;this.size=[140,40];this.slider=this.addWidget("slider","V",this.properties.value,function(e){c.properties.value=e},this.properties);this.widgets_up=!0}function C(){this.size=[160,26];this.addOutput("","number");this.properties={color:"#7AF",min:0,max:1,value:0.5};this.value=-1}function v(){this.size= +[160,26];this.addInput("","number");this.properties={min:0,max:1,value:0,color:"#AAF"}}function s(){this.addInputs("",0);this.properties={value:"...",font:"Arial",fontsize:18,color:"#AAA",align:"left",glowSize:0,decimals:1}}function D(){this.size=[200,100];this.properties={borderColor:"#ffffff",bgcolorTop:"#f0f0f0",bgcolorBottom:"#e0e0e0",shadowSize:2,borderRadius:3}}var A=z.LiteGraph;c.title="Button";c.desc="Triggers an event";c.font="Arial";c.prototype.onDrawForeground=function(h){if(!this.flags.collapsed&& +(h.fillStyle="black",h.fillRect(11,11,this.size[0]-20,this.size[1]-20),h.fillStyle="#AAF",h.fillRect(9,9,this.size[0]-20,this.size[1]-20),h.fillStyle=this.clicked?"white":this.mouseOver?"#668":"#334",h.fillRect(10,10,this.size[0]-20,this.size[1]-20),this.properties.text||0===this.properties.text)){var e=this.properties.font_size||30;h.textAlign="center";h.fillStyle=this.clicked?"black":"white";h.font=e+"px "+c.font;h.fillText(this.properties.text,0.5*this.size[0],0.5*this.size[1]+0.3*e);h.textAlign= +"left"}};c.prototype.onMouseDown=function(c,e){if(1e[1]))return this.old_y=c.canvasY,this.captureInput(!0),this.mouse_captured=!0};n.prototype.onMouseMove=function(c){if(this.mouse_captured){var e=this.old_y-c.canvasY;c.shiftKey&&(e*=10);if(c.metaKey||c.altKey)e*=0.1;this.old_y=c.canvasY;c=this._remainder+e/n.pixels_threshold;this._remainder=c%1;c=Math.clamp(this.properties.value+ +(c|0)*this.properties.step,this.properties.min,this.properties.max);this.properties.value=c;this.graph._version++;this.setDirtyCanvas(!0)}};n.prototype.onMouseUp=function(c,e){200>c.click_time&&(this.properties.value=Math.clamp(this.properties.value+(e[1]>0.5*this.size[1]?-1:1)*this.properties.step,this.properties.min,this.properties.max),this.graph._version++,this.setDirtyCanvas(!0));this.mouse_captured&&(this.mouse_captured=!1,this.captureInput(!1))};A.registerNodeType("widget/number",n);e.title= +"Combo";e.desc="Widget to select from a list";e.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};e.prototype.onPropertyChanged=function(c,e){"values"==c?(this._values=e.split(";"),this.widget.options.values=this._values):"value"==c&&(this.widget.value=e)};A.registerNodeType("widget/combo",e);w.title="Knob";w.desc="Circular controller";w.size=[80,100];w.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/ +(this.properties.max-this.properties.min));var e=0.5*this.size[0],k=0.5*this.size[1],n=0.5*Math.min(this.size[0],this.size[1])-5;c.globalAlpha=1;c.save();c.translate(e,k);c.rotate(0.75*Math.PI);c.fillStyle="rgba(0,0,0,0.5)";c.beginPath();c.moveTo(0,0);c.arc(0,0,n,0,1.5*Math.PI);c.fill();c.strokeStyle="black";c.fillStyle=this.properties.color;c.lineWidth=2;c.beginPath();c.moveTo(0,0);c.arc(0,0,n-4,0,1.5*Math.PI*Math.max(0.01,this.value));c.closePath();c.fill();c.lineWidth=1;c.globalAlpha=1;c.restore(); +c.fillStyle="black";c.beginPath();c.arc(e,k,0.75*n,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":this.properties.color;c.beginPath();var s=this.value*Math.PI*1.5+0.75*Math.PI;c.arc(e+Math.cos(s)*n*0.65,k+Math.sin(s)*n*0.65,0.05*n,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":"#AAA";c.font=Math.floor(0.5*n)+"px Arial";c.textAlign="center";c.fillText(this.properties.value.toFixed(this.properties.precision),e,k+0.15*n)}};w.prototype.onExecute=function(){this.setOutputData(0, +this.properties.value);this.boxcolor=A.colorToString([this.value,this.value,this.value])};w.prototype.onMouseDown=function(c){this.center=[0.5*this.size[0],0.5*this.size[1]+20];this.radius=0.5*this.size[0];if(20>c.canvasY-this.pos[1]||A.distance([c.canvasX,c.canvasY],[this.pos[0]+this.center[0],this.pos[1]+this.center[1]])>this.radius)return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];this.captureInput(!0);return!0};w.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX- +this.pos[0],c.canvasY-this.pos[1]];var e=this.value,e=e-0.01*(c[1]-this.oldmouse[1]);1e&&(e=0);this.value=e;this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.oldmouse=c;this.setDirtyCanvas(!0)}};w.prototype.onMouseUp=function(c){this.oldmouse&&(this.oldmouse=null,this.captureInput(!1))};w.prototype.onPropertyChanged=function(c,e){if("min"==c||"max"==c||"value"==c)return this.properties[c]=parseFloat(e),!0};A.registerNodeType("widget/knob", +w);k.title="Inner Slider";k.prototype.onPropertyChanged=function(c,e){"value"==c&&(this.slider.value=e)};k.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};A.registerNodeType("widget/internal_slider",k);C.title="H.Slider";C.desc="Linear slider controller";C.prototype.onDrawForeground=function(c){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min));c.globalAlpha=1;c.lineWidth=1;c.fillStyle="#000";c.fillRect(2, 2,this.size[0]-4,this.size[1]-4);c.fillStyle=this.properties.color;c.beginPath();c.rect(4,4,(this.size[0]-8)*this.value,this.size[1]-8);c.fill()};C.prototype.onExecute=function(){this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.setOutputData(0,this.properties.value);this.boxcolor=A.colorToString([this.value,this.value,this.value])};C.prototype.onMouseDown=function(c){if(0>c.canvasY-this.pos[1])return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY- -this.pos[1]];this.captureInput(!0);return!0};C.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];var h=this.value,h=h+(c[0]-this.oldmouse[0])/this.size[0];1h&&(h=0);this.value=h;this.oldmouse=c;this.setDirtyCanvas(!0)}};C.prototype.onMouseUp=function(c){this.oldmouse=null;this.captureInput(!1)};C.prototype.onMouseLeave=function(c){};A.registerNodeType("widget/hslider",C);w.title="Progress";w.desc="Shows data in linear progress";w.prototype.onExecute= -function(){var c=this.getInputData(0);void 0!=c&&(this.properties.value=c)};w.prototype.onDrawForeground=function(c){c.lineWidth=1;c.fillStyle=this.properties.color;var h=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min),h=Math.min(1,h),h=Math.max(0,h);c.fillRect(2,2,(this.size[0]-4)*h,this.size[1]-4)};A.registerNodeType("widget/progress",w);t.title="Text";t.desc="Shows the input value";t.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"led_text", -text:"LED",type:"minibutton"},{name:"normal_text",text:"Normal",type:"minibutton"}];t.prototype.onDrawForeground=function(c){c.fillStyle=this.properties.color;var h=this.properties.value;this.properties.glowSize?(c.shadowColor=this.properties.color,c.shadowOffsetX=0,c.shadowOffsetY=0,c.shadowBlur=this.properties.glowSize):c.shadowColor="transparent";var k=this.properties.fontsize;c.textAlign=this.properties.align;c.font=k.toString()+"px "+this.properties.font;this.str="number"==typeof h?h.toFixed(this.properties.decimals): -h;if("string"==typeof this.str){var h=this.str.split("\\n"),n;for(n in h)c.fillText(h[n],"left"==this.properties.align?15:this.size[0]-15,-0.15*k+k*(parseInt(n)+1))}c.shadowColor="transparent";this.last_ctx=c;c.textAlign="left"};t.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(this.properties.value=c)};t.prototype.resize=function(){if(this.last_ctx){var c=this.str.split("\\n");this.last_ctx.font=this.properties.fontsize+"px "+this.properties.font;var h=0,k;for(k in c){var n=this.last_ctx.measureText(c[k]).width; -he&&(e=0);this.value=e;this.oldmouse=c;this.setDirtyCanvas(!0)}};C.prototype.onMouseUp=function(c){this.oldmouse=null;this.captureInput(!1)};C.prototype.onMouseLeave=function(c){};A.registerNodeType("widget/hslider",C);v.title="Progress";v.desc="Shows data in linear progress";v.prototype.onExecute= +function(){var c=this.getInputData(0);void 0!=c&&(this.properties.value=c)};v.prototype.onDrawForeground=function(c){c.lineWidth=1;c.fillStyle=this.properties.color;var e=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min),e=Math.min(1,e),e=Math.max(0,e);c.fillRect(2,2,(this.size[0]-4)*e,this.size[1]-4)};A.registerNodeType("widget/progress",v);s.title="Text";s.desc="Shows the input value";s.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"led_text", +text:"LED",type:"minibutton"},{name:"normal_text",text:"Normal",type:"minibutton"}];s.prototype.onDrawForeground=function(c){c.fillStyle=this.properties.color;var e=this.properties.value;this.properties.glowSize?(c.shadowColor=this.properties.color,c.shadowOffsetX=0,c.shadowOffsetY=0,c.shadowBlur=this.properties.glowSize):c.shadowColor="transparent";var k=this.properties.fontsize;c.textAlign=this.properties.align;c.font=k.toString()+"px "+this.properties.font;this.str="number"==typeof e?e.toFixed(this.properties.decimals): +e;if("string"==typeof this.str){var e=this.str.split("\\n"),n;for(n in e)c.fillText(e[n],"left"==this.properties.align?15:this.size[0]-15,-0.15*k+k*(parseInt(n)+1))}c.shadowColor="transparent";this.last_ctx=c;c.textAlign="left"};s.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(this.properties.value=c)};s.prototype.resize=function(){if(this.last_ctx){var c=this.str.split("\\n");this.last_ctx.font=this.properties.fontsize+"px "+this.properties.font;var e=0,k;for(k in c){var n=this.last_ctx.measureText(c[k]).width; +eh?n.xbox.axes.lx:0,this._left_axis[1]=Math.abs(n.xbox.axes.ly)>h?n.xbox.axes.ly:0,this._right_axis[0]=Math.abs(n.xbox.axes.rx)>h?n.xbox.axes.rx:0,this._right_axis[1]=Math.abs(n.xbox.axes.ry)>h?n.xbox.axes.ry:0,this._triggers[0]=Math.abs(n.xbox.axes.ltrigger)>h?n.xbox.axes.ltrigger: -0,this._triggers[1]=Math.abs(n.xbox.axes.rtrigger)>h?n.xbox.axes.rtrigger:0);if(this.outputs)for(h=0;he?n.xbox.axes.lx:0,this._left_axis[1]=Math.abs(n.xbox.axes.ly)>e?n.xbox.axes.ly:0,this._right_axis[0]=Math.abs(n.xbox.axes.rx)>e?n.xbox.axes.rx:0,this._right_axis[1]=Math.abs(n.xbox.axes.ry)>e?n.xbox.axes.ry:0,this._triggers[0]=Math.abs(n.xbox.axes.ltrigger)>e?n.xbox.axes.ltrigger: +0,this._triggers[1]=Math.abs(n.xbox.axes.rtrigger)>e?n.xbox.axes.rtrigger:0);if(this.outputs)for(e=0;en;n++)if(h[n]){n=h[n];h=this.xbox_mapping;h||(h=this.xbox_mapping={axes:[], -buttons:{},hat:"",hatmap:c.CENTER});h.axes.lx=n.axes[0];h.axes.ly=n.axes[1];h.axes.rx=n.axes[2];h.axes.ry=n.axes[3];h.axes.ltrigger=n.buttons[6].value;h.axes.rtrigger=n.buttons[7].value;h.hat="";h.hatmap=c.CENTER;for(var m=0;mm)h.buttons[c.mapping_array[m]]=n.buttons[m].pressed,n.buttons[m].was_pressed&&this.trigger(c.mapping_array[m]+"_button_event");else switch(m){case 12:n.buttons[m].pressed&&(h.hat+="up",h.hatmap|=c.UP); -break;case 13:n.buttons[m].pressed&&(h.hat+="down",h.hatmap|=c.DOWN);break;case 14:n.buttons[m].pressed&&(h.hat+="left",h.hatmap|=c.LEFT);break;case 15:n.buttons[m].pressed&&(h.hat+="right",h.hatmap|=c.RIGHT);break;case 16:h.buttons.home=n.buttons[m].pressed}n.xbox=h;return n}};c.prototype.onDrawBackground=function(c){if(!this.flags.collapsed){var h=this._left_axis,m=this._right_axis;c.strokeStyle="#88A";c.strokeRect(0.5*(h[0]+1)*this.size[0]-4,0.5*(h[1]+1)*this.size[1]-4,8,8);c.strokeStyle="#8A8"; -c.strokeRect(0.5*(m[0]+1)*this.size[0]-4,0.5*(m[1]+1)*this.size[1]-4,8,8);h=this.size[1]/this._current_buttons.length;c.fillStyle="#AEB";for(m=0;mn;n++)if(e[n]){n=e[n];e=this.xbox_mapping;e||(e=this.xbox_mapping={axes:[], +buttons:{},hat:"",hatmap:c.CENTER});e.axes.lx=n.axes[0];e.axes.ly=n.axes[1];e.axes.rx=n.axes[2];e.axes.ry=n.axes[3];e.axes.ltrigger=n.buttons[6].value;e.axes.rtrigger=n.buttons[7].value;e.hat="";e.hatmap=c.CENTER;for(var m=0;mm)e.buttons[c.mapping_array[m]]=n.buttons[m].pressed,n.buttons[m].was_pressed&&this.trigger(c.mapping_array[m]+"_button_event");else switch(m){case 12:n.buttons[m].pressed&&(e.hat+="up",e.hatmap|=c.UP); +break;case 13:n.buttons[m].pressed&&(e.hat+="down",e.hatmap|=c.DOWN);break;case 14:n.buttons[m].pressed&&(e.hat+="left",e.hatmap|=c.LEFT);break;case 15:n.buttons[m].pressed&&(e.hat+="right",e.hatmap|=c.RIGHT);break;case 16:e.buttons.home=n.buttons[m].pressed}n.xbox=e;return n}};c.prototype.onDrawBackground=function(c){if(!this.flags.collapsed){var e=this._left_axis,m=this._right_axis;c.strokeStyle="#88A";c.strokeRect(0.5*(e[0]+1)*this.size[0]-4,0.5*(e[1]+1)*this.size[1]-4,8,8);c.strokeStyle="#8A8"; +c.strokeRect(0.5*(m[0]+1)*this.size[0]-4,0.5*(m[1]+1)*this.size[1]-4,8,8);e=this.size[1]/this._current_buttons.length;c.fillStyle="#AEB";for(m=0;m","enum",{values:a.values});this.size=[80,60]}function b(){this.addInput("inc","number");this.addOutput("total","number");this.addProperty("increment", -1);this.addProperty("value",0)}function d(){this.addInput("v","number");this.addOutput("sin","number");this.addProperty("amplitude",1);this.addProperty("offset",0);this.bgImageUrl="nodes/imgs/icon-sin.png"}function g(){this.addInput("x","number");this.addInput("y","number");this.addOutput("","number");this.properties={x:1,y:1,formula:"x+y"};this.code_widget=this.addWidget("text","F(x,y)",this.properties.formula,function(a,b,d){d.properties.formula=a});this.addWidget("toggle","allow",E.allow_scripts, -function(a){E.allow_scripts=a});this._func=null}function f(){this.addInput("vec2","vec2");this.addOutput("x","number");this.addOutput("y","number")}function r(){this.addInputs([["x","number"],["y","number"]]);this.addOutput("vec2","vec2");this.properties={x:0,y:0};this._data=new Float32Array(2)}function s(){this.addInput("vec3","vec3");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number")}function J(){this.addInputs([["x","number"],["y","number"],["z","number"]]); -this.addOutput("vec3","vec3");this.properties={x:0,y:0,z:0};this._data=new Float32Array(3)}function K(){this.addInput("vec4","vec4");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number");this.addOutput("w","number")}function I(){this.addInputs([["x","number"],["y","number"],["z","number"],["w","number"]]);this.addOutput("vec4","vec4");this.properties={x:0,y:0,z:0,w:0};this._data=new Float32Array(4)}var E=z.LiteGraph;c.title="Converter";c.desc="type A to type B";c.prototype.onExecute= -function(){var a=this.getInputData(0);if(null!=a&&this.outputs)for(var b=0;ba&&(a+=1024);var c=Math.floor(a);a-=c;d=k.data[c];c=k.data[1023==c?0:c+1];b&&(a=a*a*a*(a*(6*a-15)+10));return d*(1-a)+c*a};k.prototype.onExecute=function(){for(var a=this.getInputData(0)||0,b=this.properties.octaves||1,d=0,c=1,a=a+(this.properties.seed||0),g=this.properties.speed||1,f=0,l=0;lc);++l);a=this.properties.min;this._last_v=d/f*(this.properties.max- -a)+a;this.setOutputData(0,this._last_v)};k.prototype.onDrawBackground=function(a){this.outputs[0].label=(this._last_v||0).toFixed(3)};E.registerNodeType("math/noise",k);C.title="Spikes";C.desc="spike every random time";C.prototype.onExecute=function(){var a=this.graph.elapsed_time;this._remaining_time-=a;this._blink_time-=a;a=0;0this._remaining_time?(this._remaining_time=Math.random()*(this.properties.max_time- -this.properties.min_time)+this.properties.min_time,this._blink_time=this.properties.duration,this.boxcolor="#FFF"):this.boxcolor="#000";this.setOutputData(0,a)};E.registerNodeType("math/spikes",C);w.title="Clamp";w.desc="Clamp number between min and max";w.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(a=Math.max(this.properties.min,a),a=Math.min(this.properties.max,a),this.setOutputData(0,a))};w.prototype.getCode=function(a){a="";this.isInputConnected(0)&&(a+="clamp({{0}},"+ -this.properties.min+","+this.properties.max+")");return a};E.registerNodeType("math/clamp",w);t.title="Lerp";t.desc="Linear Interpolation";t.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.getInputData(1);null==b&&(b=0);var d=this.properties.f,c=this.getInputData(2);void 0!==c&&(d=c);this.setOutputData(0,a*(1-d)+b*d)};t.prototype.onGetInputs=function(){return[["f","number"]]};E.registerNodeType("math/lerp",t);D.title="Abs";D.desc="Absolute";D.prototype.onExecute= -function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.abs(a))};E.registerNodeType("math/abs",D);A.title="Floor";A.desc="Floor number to remove fractional part";A.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.floor(a))};E.registerNodeType("math/floor",A);e.title="Frac";e.desc="Returns fractional part";e.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a%1)};E.registerNodeType("math/frac",e);y.title= +(function(z){function c(){this.addInput("in","*");this.size=[80,30]}function m(){this.addInput("in");this.addOutput("out");this.size=[80,30]}function n(){this.addInput("in");this.addOutput("out")}function e(){this.addInput("in","number",{locked:!0});this.addOutput("out","number",{locked:!0});this.addOutput("clamped","number",{locked:!0});this.addProperty("in",0);this.addProperty("in_min",0);this.addProperty("in_max",1);this.addProperty("out_min",0);this.addProperty("out_max",1);this.size=[120,50]} +function w(){this.addOutput("value","number");this.addProperty("min",0);this.addProperty("max",1);this.size=[80,30]}function k(){this.addInput("in","number");this.addOutput("out","number");this.addProperty("min",0);this.addProperty("max",1);this.addProperty("smooth",!0);this.addProperty("seed",0);this.addProperty("octaves",1);this.addProperty("persistence",0.8);this.addProperty("speed",1);this.size=[90,30]}function C(){this.addOutput("out","number");this.addProperty("min_time",1);this.addProperty("max_time", +2);this.addProperty("duration",0.2);this.size=[90,30];this._blink_time=this._remaining_time=0}function v(){this.addInput("in","number");this.addOutput("out","number");this.size=[80,30];this.addProperty("min",0);this.addProperty("max",1)}function s(){this.properties={f:0.5};this.addInput("A","number");this.addInput("B","number");this.addOutput("out","number")}function D(){this.addInput("in","number");this.addOutput("out","number");this.size=[80,30]}function A(){this.addInput("in","number");this.addOutput("out", +"number");this.size=[80,30]}function h(){this.addInput("in","number");this.addOutput("out","number");this.size=[80,30]}function y(){this.addInput("in","number");this.addOutput("out","number");this.size=[80,30];this.properties={A:0,B:1}}function B(){this.addInput("in","number",{label:""});this.addOutput("out","number",{label:""});this.size=[80,30];this.addProperty("factor",1)}function u(){this.addInput("v","boolean");this.addInput("A");this.addInput("B");this.addOutput("out")}function G(){this.addInput("in", +"number");this.addOutput("out","number");this.size=[80,30];this.addProperty("samples",10);this._values=new Float32Array(10);this._current=0}function q(){this.addInput("in","number");this.addOutput("out","number");this.addProperty("factor",0.1);this.size=[80,30];this._value=null}function p(){this.addInput("A","number,array,object");this.addInput("B","number");this.addOutput("=","number");this.addProperty("A",1);this.addProperty("B",1);this.addProperty("OP","+","enum",{values:p.values});this._func= +function(a,b){return a+b};this._result=[]}function l(){this.addInput("A","number");this.addInput("B","number");this.addOutput("A==B","boolean");this.addOutput("A!=B","boolean");this.addProperty("A",0);this.addProperty("B",0)}function a(){this.addInput("A","number");this.addInput("B","number");this.addOutput("true","boolean");this.addOutput("false","boolean");this.addProperty("A",1);this.addProperty("B",1);this.addProperty("OP",">","enum",{values:a.values});this.size=[80,60]}function b(){this.addInput("inc", +"number");this.addOutput("total","number");this.addProperty("increment",1);this.addProperty("value",0)}function d(){this.addInput("v","number");this.addOutput("sin","number");this.addProperty("amplitude",1);this.addProperty("offset",0);this.bgImageUrl="nodes/imgs/icon-sin.png"}function g(){this.addInput("x","number");this.addInput("y","number");this.addOutput("","number");this.properties={x:1,y:1,formula:"x+y"};this.code_widget=this.addWidget("text","F(x,y)",this.properties.formula,function(a,b,d){d.properties.formula= +a});this.addWidget("toggle","allow",E.allow_scripts,function(a){E.allow_scripts=a});this._func=null}function f(){this.addInput("vec2","vec2");this.addOutput("x","number");this.addOutput("y","number")}function r(){this.addInputs([["x","number"],["y","number"]]);this.addOutput("vec2","vec2");this.properties={x:0,y:0};this._data=new Float32Array(2)}function x(){this.addInput("vec3","vec3");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number")}function I(){this.addInputs([["x", +"number"],["y","number"],["z","number"]]);this.addOutput("vec3","vec3");this.properties={x:0,y:0,z:0};this._data=new Float32Array(3)}function J(){this.addInput("vec4","vec4");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number");this.addOutput("w","number")}function K(){this.addInputs([["x","number"],["y","number"],["z","number"],["w","number"]]);this.addOutput("vec4","vec4");this.properties={x:0,y:0,z:0,w:0};this._data=new Float32Array(4)}var E=z.LiteGraph;c.title= +"Converter";c.desc="type A to type B";c.prototype.onExecute=function(){var a=this.getInputData(0);if(null!=a&&this.outputs)for(var b=0;ba&&(a+=1024);var c=Math.floor(a);a-=c;d=k.data[c];c=k.data[1023==c?0:c+1];b&&(a=a*a*a*(a*(6*a-15)+10));return d*(1-a)+c*a};k.prototype.onExecute=function(){for(var a=this.getInputData(0)||0,b=this.properties.octaves||1,d=0,c=1,a=a+(this.properties.seed||0),g=this.properties.speed||1,f=0,l=0;lc);++l);a=this.properties.min; +this._last_v=d/f*(this.properties.max-a)+a;this.setOutputData(0,this._last_v)};k.prototype.onDrawBackground=function(a){this.outputs[0].label=(this._last_v||0).toFixed(3)};E.registerNodeType("math/noise",k);C.title="Spikes";C.desc="spike every random time";C.prototype.onExecute=function(){var a=this.graph.elapsed_time;this._remaining_time-=a;this._blink_time-=a;a=0;0this._remaining_time?(this._remaining_time= +Math.random()*(this.properties.max_time-this.properties.min_time)+this.properties.min_time,this._blink_time=this.properties.duration,this.boxcolor="#FFF"):this.boxcolor="#000";this.setOutputData(0,a)};E.registerNodeType("math/spikes",C);v.title="Clamp";v.desc="Clamp number between min and max";v.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(a=Math.max(this.properties.min,a),a=Math.min(this.properties.max,a),this.setOutputData(0,a))};v.prototype.getCode=function(a){a="";this.isInputConnected(0)&& +(a+="clamp({{0}},"+this.properties.min+","+this.properties.max+")");return a};E.registerNodeType("math/clamp",v);s.title="Lerp";s.desc="Linear Interpolation";s.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.getInputData(1);null==b&&(b=0);var d=this.properties.f,c=this.getInputData(2);void 0!==c&&(d=c);this.setOutputData(0,a*(1-d)+b*d)};s.prototype.onGetInputs=function(){return[["f","number"]]};E.registerNodeType("math/lerp",s);D.title="Abs";D.desc="Absolute";D.prototype.onExecute= +function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.abs(a))};E.registerNodeType("math/abs",D);A.title="Floor";A.desc="Floor number to remove fractional part";A.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.floor(a))};E.registerNodeType("math/floor",A);h.title="Frac";h.desc="Returns fractional part";h.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a%1)};E.registerNodeType("math/frac",h);y.title= "Smoothstep";y.desc="Smoothstep";y.prototype.onExecute=function(){var a=this.getInputData(0);if(void 0!==a){var b=this.properties.A,a=Math.clamp((a-b)/(this.properties.B-b),0,1);this.setOutputData(0,a*a*(3-2*a))}};E.registerNodeType("math/smoothstep",y);B.title="Scale";B.desc="v * factor";B.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a*this.properties.factor)};E.registerNodeType("math/scale",B);u.title="Gate";u.desc="if v is true, then outputs A, otherwise B"; -u.prototype.onExecute=function(){var a=this.getInputData(0);this.setOutputData(0,this.getInputData(a?1:2))};E.registerNodeType("math/gate",u);F.title="Average";F.desc="Average Filter";F.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this._values.length;this._values[this._current%b]=a;this._current+=1;this._current>b&&(this._current=0);for(var d=a=0;db&&(b=1);this.properties.samples= -Math.round(b);var d=this._values;this._values=new Float32Array(this.properties.samples);d.length<=this._values.length?this._values.set(d):this._values.set(d.subarray(0,this._values.length))};E.registerNodeType("math/average",F);q.title="TendTo";q.desc="moves the output value always closer to the input";q.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.factor;this._value=null==this._value?a:this._value*(1-b)+a*b;this.setOutputData(0,this._value)};E.registerNodeType("math/tendTo", -q);p.values="+ - * / % ^ max min".split(" ");p.title="Operation";p.desc="Easy math operators";p["@OP"]={type:"enum",title:"operation",values:p.values};p.size=[100,60];p.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"};p.prototype.setValue=function(a){"string"==typeof a&&(a=parseFloat(a));this.properties.value=a};p.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);null!=a? -this.properties.A=a:a=this.properties.A;null!=b?this.properties.B=b:b=this.properties.B;var d=0;switch(this.properties.OP){case "+":d=a+b;break;case "-":d=a-b;break;case "x":case "X":case "*":d=a*b;break;case "/":d=a/b;break;case "%":d=a%b;break;case "^":d=Math.pow(a,b);break;case "max":d=Math.max(a,b);break;case "min":d=Math.min(a,b);break;default:console.warn("Unknown operation: "+this.properties.OP)}this.setOutputData(0,d)};p.prototype.onDrawBackground=function(a){this.flags.collapsed||(a.font= -"40px Arial",a.fillStyle="#666",a.textAlign="center",a.fillText(this.properties.OP,0.5*this.size[0],0.5*(this.size[1]+E.NODE_TITLE_HEIGHT)),a.textAlign="left")};E.registerNodeType("math/operation",p);E.registerSearchboxExtra("math/operation","MAX",{properties:{OP:"max"},title:"MAX()"});E.registerSearchboxExtra("math/operation","MIN",{properties:{OP:"min"},title:"MIN()"});l.title="Compare";l.desc="compares between two values";l.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1); -void 0!==a?this.properties.A=a:a=this.properties.A;void 0!==b?this.properties.B=b:b=this.properties.B;for(var d=0,c=this.outputs.length;dB":f=a>b;break;case "A=B":f=a>=b}this.setOutputData(d,f)}}};l.prototype.onGetOutputs=function(){return[["A==B","boolean"],["A!=B","boolean"],["A>B","boolean"],["A=B", -"boolean"],["A<=B","boolean"]]};E.registerNodeType("math/compare",l);E.registerSearchboxExtra("math/compare","==",{outputs:[["A==B","boolean"]],title:"A==B"});E.registerSearchboxExtra("math/compare","!=",{outputs:[["A!=B","boolean"]],title:"A!=B"});E.registerSearchboxExtra("math/compare",">",{outputs:[["A>B","boolean"]],title:"A>B"});E.registerSearchboxExtra("math/compare","<",{outputs:[["A=",{outputs:[["A>=B","boolean"]],title:"A>=B"}); -E.registerSearchboxExtra("math/compare","<=",{outputs:[["A<=B","boolean"]],title:"A<=B"});a.values="> < == != <= >= || &&".split(" ");a["@OP"]={type:"enum",title:"operation",values:a.values};a.title="Condition";a.desc="evaluates condition between A and B";a.prototype.getTitle=function(){return"A "+this.properties.OP+" B"};a.prototype.onExecute=function(){var a=this.getInputData(0);void 0===a?a=this.properties.A:this.properties.A=a;var b=this.getInputData(1);void 0===b?b=this.properties.B:this.properties.B= -b;var d=!0;switch(this.properties.OP){case ">":d=a>b;break;case "<":d=a=":d=a>=b;break;case "||":d=a||b;break;case "&&":d=a&&b}this.setOutputData(0,d);this.setOutputData(1,!d)};E.registerNodeType("math/condition",a);b.title="Accumulate";b.desc="Increments a value every time";b.prototype.onExecute=function(){null===this.properties.value&&(this.properties.value=0);var a=this.getInputData(0);this.properties.value=null!== -a?this.properties.value+a:this.properties.value+this.properties.increment;this.setOutputData(0,this.properties.value)};E.registerNodeType("math/accumulate",b);d.title="Trigonometry";d.desc="Sin Cos Tan";d.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.amplitude,d=this.findInputSlot("amplitude");-1!=d&&(b=this.getInputData(d));var c=this.properties.offset,d=this.findInputSlot("offset");-1!=d&&(c=this.getInputData(d));for(var d=0,g=this.outputs.length;d< -g;++d){var f;switch(this.outputs[d].name){case "sin":f=Math.sin(a);break;case "cos":f=Math.cos(a);break;case "tan":f=Math.tan(a);break;case "asin":f=Math.asin(a);break;case "acos":f=Math.acos(a);break;case "atan":f=Math.atan(a)}this.setOutputData(d,b*f+c)}};d.prototype.onGetInputs=function(){return[["v","number"],["amplitude","number"],["offset","number"]]};d.prototype.onGetOutputs=function(){return[["sin","number"],["cos","number"],["tan","number"],["asin","number"],["acos","number"],["atan","number"]]}; -E.registerNodeType("math/trigonometry",d);E.registerSearchboxExtra("math/trigonometry","SIN()",{outputs:[["sin","number"]],title:"SIN()"});E.registerSearchboxExtra("math/trigonometry","COS()",{outputs:[["cos","number"]],title:"COS()"});E.registerSearchboxExtra("math/trigonometry","TAN()",{outputs:[["tan","number"]],title:"TAN()"});g.title="Formula";g.desc="Compute formula";g.size=[160,100];F.prototype.onPropertyChanged=function(a,b){"formula"==a&&(this.code_widget.value=b)};g.prototype.onExecute= -function(){if(E.allow_scripts){var a=this.getInputData(0),b=this.getInputData(1);null!=a?this.properties.x=a:a=this.properties.x;null!=b?this.properties.y=b:b=this.properties.y;var d;try{this._func&&this._func_code==this.properties.formula||(this._func=new Function("x","y","TIME","return "+this.properties.formula),this._func_code=this.properties.formula),d=this._func(a,b,this.graph.globaltime),this.boxcolor=null}catch(c){this.boxcolor="red"}this.setOutputData(0,d)}};g.prototype.getTitle=function(){return this._func_code|| -"Formula"};g.prototype.onDrawBackground=function(){var a=this.properties.formula;this.outputs&&this.outputs.length&&(this.outputs[0].label=a)};E.registerNodeType("math/formula",g);f.title="Vec2->XY";f.desc="vector 2 to components";f.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]))};E.registerNodeType("math3d/vec2-to-xy",f);r.title="XY->Vec2";r.desc="components to vector2";r.prototype.onExecute=function(){var a=this.getInputData(0); -null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this._data;d[0]=a;d[1]=b;this.setOutputData(0,d)};E.registerNodeType("math3d/xy-to-vec2",r);s.title="Vec3->XYZ";s.desc="vector 3 to components";s.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]))};E.registerNodeType("math3d/vec3-to-xyz",s);J.title="XYZ->Vec3";J.desc="components to vector3";J.prototype.onExecute= -function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this._data;c[0]=a;c[1]=b;c[2]=d;this.setOutputData(0,c)};E.registerNodeType("math3d/xyz-to-vec3",J);K.title="Vec4->XYZW";K.desc="vector 4 to components";K.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]),this.setOutputData(3, -a[3]))};E.registerNodeType("math3d/vec4-to-xyzw",K);I.title="XYZW->Vec4";I.desc="components to vector4";I.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this.getInputData(3);null==c&&(c=this.properties.w);var g=this._data;g[0]=a;g[1]=b;g[2]=d;g[3]=c;this.setOutputData(0,g)};E.registerNodeType("math3d/xyzw-to-vec4",I)})(this); +u.prototype.onExecute=function(){var a=this.getInputData(0);this.setOutputData(0,this.getInputData(a?1:2))};E.registerNodeType("math/gate",u);G.title="Average";G.desc="Average Filter";G.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this._values.length;this._values[this._current%b]=a;this._current+=1;this._current>b&&(this._current=0);for(var d=a=0;db&&(b=1);this.properties.samples= +Math.round(b);var d=this._values;this._values=new Float32Array(this.properties.samples);d.length<=this._values.length?this._values.set(d):this._values.set(d.subarray(0,this._values.length))};E.registerNodeType("math/average",G);q.title="TendTo";q.desc="moves the output value always closer to the input";q.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.factor;this._value=null==this._value?a:this._value*(1-b)+a*b;this.setOutputData(0,this._value)};E.registerNodeType("math/tendTo", +q);p.values="+ - * / % ^ max min".split(" ");p.title="Operation";p.desc="Easy math operators";p["@OP"]={type:"enum",title:"operation",values:p.values};p.size=[100,60];p.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"};p.prototype.setValue=function(a){"string"==typeof a&&(a=parseFloat(a));this.properties.value=a};p.prototype.onPropertyChanged=function(a,b){if("OP"==a)switch(this.properties.OP){case "+":this._func= +function(a,b){return a+b};break;case "-":this._func=function(a,b){return a-b};break;case "x":case "X":case "*":this._func=function(a,b){return a*b};break;case "/":this._func=function(a,b){return a/b};break;case "%":this._func=function(a,b){return a%b};break;case "^":this._func=function(a,b){return Math.pow(a,b)};break;case "max":this._func=function(a,b){return Math.max(a,b)};break;case "min":this._func=function(a,b){return Math.min(a,b)};break;default:console.warn("Unknown operation: "+this.properties.OP), +this._func=function(a){return a}}};p.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);null!=a?a.constructor===Number&&(this.properties.A=a):a=this.properties.A;null!=b?this.properties.B=b:b=this.properties.B;var d;if(a.constructor===Number)d=this._func(a,b);else if(a.constructor===Array){d=this._result;d.length=a.length;for(var c=0;cB":f=a>b;break;case "A=B":f=a>=b}this.setOutputData(d,f)}}};l.prototype.onGetOutputs=function(){return[["A==B","boolean"],["A!=B","boolean"], +["A>B","boolean"],["A=B","boolean"],["A<=B","boolean"]]};E.registerNodeType("math/compare",l);E.registerSearchboxExtra("math/compare","==",{outputs:[["A==B","boolean"]],title:"A==B"});E.registerSearchboxExtra("math/compare","!=",{outputs:[["A!=B","boolean"]],title:"A!=B"});E.registerSearchboxExtra("math/compare",">",{outputs:[["A>B","boolean"]],title:"A>B"});E.registerSearchboxExtra("math/compare","<",{outputs:[["A=",{outputs:[["A>=B","boolean"]],title:"A>=B"});E.registerSearchboxExtra("math/compare","<=",{outputs:[["A<=B","boolean"]],title:"A<=B"});a.values="> < == != <= >= || &&".split(" ");a["@OP"]={type:"enum",title:"operation",values:a.values};a.title="Condition";a.desc="evaluates condition between A and B";a.prototype.getTitle=function(){return"A "+this.properties.OP+" B"};a.prototype.onExecute=function(){var a=this.getInputData(0);void 0===a?a=this.properties.A:this.properties.A=a;var b=this.getInputData(1); +void 0===b?b=this.properties.B:this.properties.B=b;var d=!0;switch(this.properties.OP){case ">":d=a>b;break;case "<":d=a=":d=a>=b;break;case "||":d=a||b;break;case "&&":d=a&&b}this.setOutputData(0,d);this.setOutputData(1,!d)};E.registerNodeType("math/condition",a);b.title="Accumulate";b.desc="Increments a value every time";b.prototype.onExecute=function(){null===this.properties.value&&(this.properties.value=0);var a= +this.getInputData(0);this.properties.value=null!==a?this.properties.value+a:this.properties.value+this.properties.increment;this.setOutputData(0,this.properties.value)};E.registerNodeType("math/accumulate",b);d.title="Trigonometry";d.desc="Sin Cos Tan";d.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.amplitude,d=this.findInputSlot("amplitude");-1!=d&&(b=this.getInputData(d));var c=this.properties.offset,d=this.findInputSlot("offset");-1!=d&&(c=this.getInputData(d)); +for(var d=0,g=this.outputs.length;dXY";f.desc="vector 2 to components";f.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]))};E.registerNodeType("math3d/vec2-to-xy",f);r.title="XY->Vec2";r.desc="components to vector2";r.prototype.onExecute= +function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this._data;d[0]=a;d[1]=b;this.setOutputData(0,d)};E.registerNodeType("math3d/xy-to-vec2",r);x.title="Vec3->XYZ";x.desc="vector 3 to components";x.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]))};E.registerNodeType("math3d/vec3-to-xyz",x);I.title="XYZ->Vec3";I.desc="components to vector3"; +I.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this._data;c[0]=a;c[1]=b;c[2]=d;this.setOutputData(0,c)};E.registerNodeType("math3d/xyz-to-vec3",I);J.title="Vec4->XYZW";J.desc="vector 4 to components";J.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2, +a[2]),this.setOutputData(3,a[3]))};E.registerNodeType("math3d/vec4-to-xyzw",J);K.title="XYZW->Vec4";K.desc="components to vector4";K.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this.getInputData(3);null==c&&(c=this.properties.w);var g=this._data;g[0]=a;g[1]=b;g[2]=d;g[3]=c;this.setOutputData(0,g)};E.registerNodeType("math3d/xyzw-to-vec4", +K)})(this); (function(z){function c(){this.addInput("T","vec3");this.addInput("R","vec3");this.addInput("S","vec3");this.addOutput("mat4","mat4");this.properties={T:[0,0,0],R:[0,0,0],S:[1,1,1],R_in_degrees:!0};this._result=mat4.create();this._must_update=!0}function m(){this.addInput("A","number,vec3");this.addInput("B","number,vec3");this.addOutput("=","number,vec3");this.addProperty("OP","+","enum",{values:m.values});this._result=vec3.create()}function n(){this.addInput("in","vec3");this.addInput("f","number"); -this.addOutput("out","vec3");this.properties={f:1};this._data=new Float32Array(3)}function h(){this.addInput("in","vec3");this.addOutput("out","number")}function x(){this.addInput("in","vec3");this.addOutput("out","vec3");this._data=new Float32Array(3)}function k(){this.addInput("A","vec3");this.addInput("B","vec3");this.addInput("f","vec3");this.addOutput("out","vec3");this.properties={f:0.5};this._data=new Float32Array(3)}function C(){this.addInput("A","vec3");this.addInput("B","vec3");this.addOutput("out", -"number")}var w=z.LiteGraph;c.title="mat4";c.temp_quat=new Float32Array([0,0,0,1]);c.temp_mat4=new Float32Array(16);c.temp_vec3=new Float32Array(3);c.prototype.onPropertyChanged=function(c,h){this._must_update=!0};c.prototype.onExecute=function(){var h=this._result,k=c.temp_quat,n=c.temp_mat4,e=c.temp_vec3,m=this.getInputData(0),B=this.getInputData(1),u=this.getInputData(2);if(this._must_update||m||B||u)m=m||this.properties.T,B=B||this.properties.R,u=u||this.properties.S,mat4.identity(h),mat4.translate(h, -h,m),this.properties.R_in_degrees?(e.set(B),vec3.scale(e,e,DEG2RAD),quat.fromEuler(k,e)):quat.fromEuler(k,B),mat4.fromQuat(n,k),mat4.multiply(h,h,n),mat4.scale(h,h,u);this.setOutputData(0,h)};w.registerNodeType("math3d/mat4",c);m.values="+ - * / % ^ max min dot cross".split(" ");w.registerSearchboxExtra("math3d/operation","CROSS()",{properties:{OP:"cross"},title:"CROSS()"});w.registerSearchboxExtra("math3d/operation","DOT()",{properties:{OP:"dot"},title:"DOT()"});m.title="Operation";m.desc="Easy math 3D operators"; -m["@OP"]={type:"enum",title:"operation",values:m.values};m.size=[100,60];m.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"};m.prototype.onExecute=function(){var c=this.getInputData(0),h=this.getInputData(1);if(null!=c&&null!=h){c.constructor===Number&&(c=[c,c,c]);h.constructor===Number&&(h=[h,h,h]);var k=this._result;switch(this.properties.OP){case "+":k=vec3.add(k,c,h);break;case "-":k=vec3.sub(k,c,h); -break;case "x":case "X":case "*":k=vec3.mul(k,c,h);break;case "/":k=vec3.div(k,c,h);break;case "%":k[0]=c[0]%h[0];k[1]=c[1]%h[1];k[2]=c[2]%h[2];break;case "^":k[0]=Math.pow(c[0],h[0]);k[1]=Math.pow(c[1],h[1]);k[2]=Math.pow(c[2],h[2]);break;case "max":k[0]=Math.max(c[0],h[0]);k[1]=Math.max(c[1],h[1]);k[2]=Math.max(c[2],h[2]);break;case "min":k[0]=Math.min(c[0],h[0]),k[1]=Math.min(c[1],h[1]),k[2]=Math.min(c[2],h[2]);case "dot":k=vec3.dot(c,h);break;case "cross":vec3.cross(k,c,h);break;default:console.warn("Unknown operation: "+ -this.properties.OP)}this.setOutputData(0,k)}};m.prototype.onDrawBackground=function(c){this.flags.collapsed||(c.font="40px Arial",c.fillStyle="#666",c.textAlign="center",c.fillText(this.properties.OP,0.5*this.size[0],0.5*(this.size[1]+w.NODE_TITLE_HEIGHT)),c.textAlign="left")};w.registerNodeType("math3d/operation",m);n.title="vec3_scale";n.desc="scales the components of a vec3";n.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);null==h&&(h=this.properties.f); -var k=this._data;k[0]=c[0]*h;k[1]=c[1]*h;k[2]=c[2]*h;this.setOutputData(0,k)}};w.registerNodeType("math3d/vec3-scale",n);h.title="vec3_length";h.desc="returns the module of a vector";h.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(c=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),this.setOutputData(0,c))};w.registerNodeType("math3d/vec3-length",h);x.title="vec3_normalize";x.desc="returns the vector normalized";x.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h= -Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),k=this._data;k[0]=c[0]/h;k[1]=c[1]/h;k[2]=c[2]/h;this.setOutputData(0,k)}};w.registerNodeType("math3d/vec3-normalize",x);k.title="vec3_lerp";k.desc="returns the interpolated vector";k.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);if(null!=h){var k=this.getInputOrProperty("f"),e=this._data;e[0]=c[0]*(1-k)+h[0]*k;e[1]=c[1]*(1-k)+h[1]*k;e[2]=c[2]*(1-k)+h[2]*k;this.setOutputData(0,e)}}};w.registerNodeType("math3d/vec3-lerp", -k);C.title="vec3_dot";C.desc="returns the dot product";C.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);null!=h&&this.setOutputData(0,c[0]*h[0]+c[1]*h[1]+c[2]*h[2])}};w.registerNodeType("math3d/vec3-dot",C);z.glMatrix?(z=function(){this.addOutput("quat","quat");this.properties={x:0,y:0,z:0,w:1,normalize:!1};this._value=quat.create()},z.title="Quaternion",z.desc="quaternion",z.prototype.onExecute=function(){this._value[0]=this.getInputOrProperty("x"); -this._value[1]=this.getInputOrProperty("y");this._value[2]=this.getInputOrProperty("z");this._value[3]=this.getInputOrProperty("w");this.properties.normalize&&quat.normalize(this._value,this._value);this.setOutputData(0,this._value)},z.prototype.onGetInputs=function(){return[["x","number"],["y","number"],["z","number"],["w","number"]]},w.registerNodeType("math3d/quaternion",z),z=function(){this.addInputs([["degrees","number"],["axis","vec3"]]);this.addOutput("quat","quat");this.properties={angle:90, -axis:vec3.fromValues(0,1,0)};this._value=quat.create()},z.title="Rotation",z.desc="quaternion rotation",z.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.angle);var h=this.getInputData(1);null==h&&(h=this.properties.axis);c=quat.setAxisAngle(this._value,h,0.0174532925*c);this.setOutputData(0,c)},w.registerNodeType("math3d/rotation",z),z=function(){this.addInput("euler","vec3");this.addOutput("quat","quat");this.properties={euler:[0,0,0],use_yaw_pitch_roll:!1}; -this._degs=vec3.create();this._value=quat.create()},z.title="Euler->Quat",z.desc="Converts euler angles (in degrees) to quaternion",z.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.euler);vec3.scale(this._degs,c,DEG2RAD);this.properties.use_yaw_pitch_roll&&(this._degs=[this._degs[2],this._degs[0],this._degs[1]]);c=quat.fromEuler(this._value,this._degs);this.setOutputData(0,c)},w.registerNodeType("math3d/euler_to_quat",z),z=function(){this.addInput(["quat","quat"]); -this.addOutput("euler","vec3");this._value=vec3.create()},z.title="Euler->Quat",z.desc="Converts rotX,rotY,rotZ in degrees to quat",z.prototype.onExecute=function(){var c=this.getInputData(0);c&&(quat.toEuler(this._value,c),vec3.scale(this._value,this._value,DEG2RAD),this.setOutputData(0,this._value))},w.registerNodeType("math3d/quat_to_euler",z),z=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},z.title="Rot. Vec3",z.desc= -"rotate a point",z.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.vec);var h=this.getInputData(1);null==h?this.setOutputData(c):this.setOutputData(0,vec3.transformQuat(vec3.create(),c,h))},w.registerNodeType("math3d/rotate_vec3",z),z=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},z.title="Mult. Quat",z.desc="rotate quaternion",z.prototype.onExecute=function(){var c=this.getInputData(0);if(null!= -c){var h=this.getInputData(1);null!=h&&(c=quat.multiply(this._value,c,h),this.setOutputData(0,c))}},w.registerNodeType("math3d/mult-quat",z),z=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp","quat");this.addProperty("factor",0.5);this._value=quat.create()},z.title="Quat Slerp",z.desc="quaternion spherical interpolation",z.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);if(null!=h){var k=this.properties.factor; -null!=this.getInputData(2)&&(k=this.getInputData(2));c=quat.slerp(this._value,c,h,k);this.setOutputData(0,c)}}},w.registerNodeType("math3d/quat-slerp",z),z=function(){this.addInput("vec3","vec3");this.addOutput("remap","vec3");this.addOutput("clamped","vec3");this.properties={clamp:!0,range_min:[-1,-1,0],range_max:[1,1,0],target_min:[-1,-1,0],target_max:[1,1,0]};this._value=vec3.create();this._clamped=vec3.create()},z.title="Remap Range",z.desc="remap a 3D range",z.prototype.onExecute=function(){var c= -this.getInputData(0);c&&this._value.set(c);for(var c=this.properties.range_min,h=this.properties.range_max,k=this.properties.target_min,e=this.properties.target_max,n=0;3>n;++n){var m=h[n]-c[n];this._clamped[n]=Math.clamp(this._value[n],c[n],h[n]);0==m?this._value[n]=0.5*(k[n]+e[n]):(m=(this._value[n]-c[n])/m,this.properties.clamp&&(m=Math.clamp(m,0,1)),this._value[n]=k[n]+m*(e[n]-k[n]))}this.setOutputData(0,this._value);this.setOutputData(1,this._clamped)},w.registerNodeType("math3d/remap_range", +this.addOutput("out","vec3");this.properties={f:1};this._data=new Float32Array(3)}function e(){this.addInput("in","vec3");this.addOutput("out","number")}function w(){this.addInput("in","vec3");this.addOutput("out","vec3");this._data=new Float32Array(3)}function k(){this.addInput("A","vec3");this.addInput("B","vec3");this.addInput("f","vec3");this.addOutput("out","vec3");this.properties={f:0.5};this._data=new Float32Array(3)}function C(){this.addInput("A","vec3");this.addInput("B","vec3");this.addOutput("out", +"number")}var v=z.LiteGraph;c.title="mat4";c.temp_quat=new Float32Array([0,0,0,1]);c.temp_mat4=new Float32Array(16);c.temp_vec3=new Float32Array(3);c.prototype.onPropertyChanged=function(c,e){this._must_update=!0};c.prototype.onExecute=function(){var e=this._result,k=c.temp_quat,n=c.temp_mat4,h=c.temp_vec3,m=this.getInputData(0),B=this.getInputData(1),u=this.getInputData(2);if(this._must_update||m||B||u)m=m||this.properties.T,B=B||this.properties.R,u=u||this.properties.S,mat4.identity(e),mat4.translate(e, +e,m),this.properties.R_in_degrees?(h.set(B),vec3.scale(h,h,DEG2RAD),quat.fromEuler(k,h)):quat.fromEuler(k,B),mat4.fromQuat(n,k),mat4.multiply(e,e,n),mat4.scale(e,e,u);this.setOutputData(0,e)};v.registerNodeType("math3d/mat4",c);m.values="+ - * / % ^ max min dot cross".split(" ");v.registerSearchboxExtra("math3d/operation","CROSS()",{properties:{OP:"cross"},title:"CROSS()"});v.registerSearchboxExtra("math3d/operation","DOT()",{properties:{OP:"dot"},title:"DOT()"});m.title="Operation";m.desc="Easy math 3D operators"; +m["@OP"]={type:"enum",title:"operation",values:m.values};m.size=[100,60];m.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"};m.prototype.onExecute=function(){var c=this.getInputData(0),e=this.getInputData(1);if(null!=c&&null!=e){c.constructor===Number&&(c=[c,c,c]);e.constructor===Number&&(e=[e,e,e]);var k=this._result;switch(this.properties.OP){case "+":k=vec3.add(k,c,e);break;case "-":k=vec3.sub(k,c,e); +break;case "x":case "X":case "*":k=vec3.mul(k,c,e);break;case "/":k=vec3.div(k,c,e);break;case "%":k[0]=c[0]%e[0];k[1]=c[1]%e[1];k[2]=c[2]%e[2];break;case "^":k[0]=Math.pow(c[0],e[0]);k[1]=Math.pow(c[1],e[1]);k[2]=Math.pow(c[2],e[2]);break;case "max":k[0]=Math.max(c[0],e[0]);k[1]=Math.max(c[1],e[1]);k[2]=Math.max(c[2],e[2]);break;case "min":k[0]=Math.min(c[0],e[0]),k[1]=Math.min(c[1],e[1]),k[2]=Math.min(c[2],e[2]);case "dot":k=vec3.dot(c,e);break;case "cross":vec3.cross(k,c,e);break;default:console.warn("Unknown operation: "+ +this.properties.OP)}this.setOutputData(0,k)}};m.prototype.onDrawBackground=function(c){this.flags.collapsed||(c.font="40px Arial",c.fillStyle="#666",c.textAlign="center",c.fillText(this.properties.OP,0.5*this.size[0],0.5*(this.size[1]+v.NODE_TITLE_HEIGHT)),c.textAlign="left")};v.registerNodeType("math3d/operation",m);n.title="vec3_scale";n.desc="scales the components of a vec3";n.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null==e&&(e=this.properties.f); +var k=this._data;k[0]=c[0]*e;k[1]=c[1]*e;k[2]=c[2]*e;this.setOutputData(0,k)}};v.registerNodeType("math3d/vec3-scale",n);e.title="vec3_length";e.desc="returns the module of a vector";e.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(c=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),this.setOutputData(0,c))};v.registerNodeType("math3d/vec3-length",e);w.title="vec3_normalize";w.desc="returns the vector normalized";w.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e= +Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),k=this._data;k[0]=c[0]/e;k[1]=c[1]/e;k[2]=c[2]/e;this.setOutputData(0,k)}};v.registerNodeType("math3d/vec3-normalize",w);k.title="vec3_lerp";k.desc="returns the interpolated vector";k.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);if(null!=e){var k=this.getInputOrProperty("f"),h=this._data;h[0]=c[0]*(1-k)+e[0]*k;h[1]=c[1]*(1-k)+e[1]*k;h[2]=c[2]*(1-k)+e[2]*k;this.setOutputData(0,h)}}};v.registerNodeType("math3d/vec3-lerp", +k);C.title="vec3_dot";C.desc="returns the dot product";C.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null!=e&&this.setOutputData(0,c[0]*e[0]+c[1]*e[1]+c[2]*e[2])}};v.registerNodeType("math3d/vec3-dot",C);z.glMatrix?(z=function(){this.addOutput("quat","quat");this.properties={x:0,y:0,z:0,w:1,normalize:!1};this._value=quat.create()},z.title="Quaternion",z.desc="quaternion",z.prototype.onExecute=function(){this._value[0]=this.getInputOrProperty("x"); +this._value[1]=this.getInputOrProperty("y");this._value[2]=this.getInputOrProperty("z");this._value[3]=this.getInputOrProperty("w");this.properties.normalize&&quat.normalize(this._value,this._value);this.setOutputData(0,this._value)},z.prototype.onGetInputs=function(){return[["x","number"],["y","number"],["z","number"],["w","number"]]},v.registerNodeType("math3d/quaternion",z),z=function(){this.addInputs([["degrees","number"],["axis","vec3"]]);this.addOutput("quat","quat");this.properties={angle:90, +axis:vec3.fromValues(0,1,0)};this._value=quat.create()},z.title="Rotation",z.desc="quaternion rotation",z.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.angle);var e=this.getInputData(1);null==e&&(e=this.properties.axis);c=quat.setAxisAngle(this._value,e,0.0174532925*c);this.setOutputData(0,c)},v.registerNodeType("math3d/rotation",z),z=function(){this.addInput("euler","vec3");this.addOutput("quat","quat");this.properties={euler:[0,0,0],use_yaw_pitch_roll:!1}; +this._degs=vec3.create();this._value=quat.create()},z.title="Euler->Quat",z.desc="Converts euler angles (in degrees) to quaternion",z.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.euler);vec3.scale(this._degs,c,DEG2RAD);this.properties.use_yaw_pitch_roll&&(this._degs=[this._degs[2],this._degs[0],this._degs[1]]);c=quat.fromEuler(this._value,this._degs);this.setOutputData(0,c)},v.registerNodeType("math3d/euler_to_quat",z),z=function(){this.addInput(["quat","quat"]); +this.addOutput("euler","vec3");this._value=vec3.create()},z.title="Euler->Quat",z.desc="Converts rotX,rotY,rotZ in degrees to quat",z.prototype.onExecute=function(){var c=this.getInputData(0);c&&(quat.toEuler(this._value,c),vec3.scale(this._value,this._value,DEG2RAD),this.setOutputData(0,this._value))},v.registerNodeType("math3d/quat_to_euler",z),z=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},z.title="Rot. Vec3",z.desc= +"rotate a point",z.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.vec);var e=this.getInputData(1);null==e?this.setOutputData(c):this.setOutputData(0,vec3.transformQuat(vec3.create(),c,e))},v.registerNodeType("math3d/rotate_vec3",z),z=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},z.title="Mult. Quat",z.desc="rotate quaternion",z.prototype.onExecute=function(){var c=this.getInputData(0);if(null!= +c){var e=this.getInputData(1);null!=e&&(c=quat.multiply(this._value,c,e),this.setOutputData(0,c))}},v.registerNodeType("math3d/mult-quat",z),z=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp","quat");this.addProperty("factor",0.5);this._value=quat.create()},z.title="Quat Slerp",z.desc="quaternion spherical interpolation",z.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);if(null!=e){var k=this.properties.factor; +null!=this.getInputData(2)&&(k=this.getInputData(2));c=quat.slerp(this._value,c,e,k);this.setOutputData(0,c)}}},v.registerNodeType("math3d/quat-slerp",z),z=function(){this.addInput("vec3","vec3");this.addOutput("remap","vec3");this.addOutput("clamped","vec3");this.properties={clamp:!0,range_min:[-1,-1,0],range_max:[1,1,0],target_min:[-1,-1,0],target_max:[1,1,0]};this._value=vec3.create();this._clamped=vec3.create()},z.title="Remap Range",z.desc="remap a 3D range",z.prototype.onExecute=function(){var c= +this.getInputData(0);c&&this._value.set(c);for(var c=this.properties.range_min,e=this.properties.range_max,k=this.properties.target_min,h=this.properties.target_max,n=0;3>n;++n){var m=e[n]-c[n];this._clamped[n]=Math.clamp(this._value[n],c[n],e[n]);0==m?this._value[n]=0.5*(k[n]+h[n]):(m=(this._value[n]-c[n])/m,this.properties.clamp&&(m=Math.clamp(m,0,1)),this._value[n]=k[n]+m*(h[n]-k[n]))}this.setOutputData(0,this._value);this.setOutputData(1,this._clamped)},v.registerNodeType("math3d/remap_range", z)):console.warn("No glmatrix found, some Math3D nodes may not work")})(this); -(function(z){function c(c,h){return c==h}function m(){this.addInput("","string");this.addOutput("table","table");this.addOutput("rows","number");this.addProperty("value","");this.addProperty("separator",",");this._table=null}z=z.LiteGraph;z.wrapFunctionAsNode("string/toString",c,[""],"String");z.wrapFunctionAsNode("string/compare",c,["string","string"],"boolean");z.wrapFunctionAsNode("string/concatenate",function(c,h){return void 0===c?h:void 0===h?c:c+h},["string","string"],"string");z.wrapFunctionAsNode("string/contains", -function(c,h){return void 0===c||void 0===h?!1:-1!=c.indexOf(h)},["string","string"],"boolean");z.wrapFunctionAsNode("string/toUpperCase",function(c){return null!=c&&c.constructor===String?c.toUpperCase():c},["string"],"string");z.wrapFunctionAsNode("string/split",function(c,h){null==h&&(h=this.properties.separator);if(null==c)return[];if(c.constructor===String)return c.split(h||" ");if(c.constructor===Array){for(var m=[],k=0;ke;++e){var h=this.getInputData(e);if(null!=h){var k=this.values[e];k.push(h);k.length>c[0]&&k.shift()}}}};c.prototype.onDrawBackground=function(e){if(!this.flags.collapsed){var h=this.size,k=0.5*h[1]/ -this.properties.scale,n=c.colors,q=0.5*h[1];e.fillStyle="#000";e.fillRect(0,0,h[0],h[1]);e.strokeStyle="#555";e.beginPath();e.moveTo(0,q);e.lineTo(h[0],q);e.stroke();if(this.inputs)for(var p=0;4>p;++p){var l=this.values[p];if(this.inputs[p]&&this.inputs[p].link){e.strokeStyle=n[p];e.beginPath();var a=l[0]*k*-1+q;e.moveTo(0,Math.clamp(a,0,h[1]));for(var b=1;be;++e){var h=this.getInputData(e);if(null!=h){var k=this.values[e];k.push(h);k.length>c[0]&&k.shift()}}}};c.prototype.onDrawBackground=function(e){if(!this.flags.collapsed){var h=this.size,k=0.5*h[1]/ +this.properties.scale,n=c.colors,q=0.5*h[1];e.fillStyle="#000";e.fillRect(0,0,h[0],h[1]);e.strokeStyle="#555";e.beginPath();e.moveTo(0,q);e.lineTo(h[0],q);e.stroke();if(this.inputs)for(var p=0;4>p;++p){var l=this.values[p];if(this.inputs[p]&&this.inputs[p].link){e.strokeStyle=n[p];e.beginPath();var a=l[0]*k*-1+q;e.moveTo(0,Math.clamp(a,0,h[1]));for(var b=1;be&&(e=0);if(0!=c.length){var h=[0,0,0];if(0==e)h=c[0];else if(1==e)h=c[c.length-1];else{var k= -(c.length-1)*e,e=c[Math.floor(k)],c=c[Math.floor(k)+1],k=k-Math.floor(k);h[0]=e[0]*(1-k)+c[0]*k;h[1]=e[1]*(1-k)+c[1]*k;h[2]=e[2]*(1-k)+c[2]*k}for(var q in h)h[q]/=255;this.boxcolor=colorToString(h);this.setOutputData(0,h)}};e.registerNodeType("color/palette",n);h.title="Frame";h.desc="Frame viewerew";h.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}];h.prototype.onDrawBackground=function(c){this.frame&&!this.flags.collapsed&&c.drawImage(this.frame, -0,0,this.size[0],this.size[1])};h.prototype.onExecute=function(){this.frame=this.getInputData(0);this.setDirtyCanvas(!0)};h.prototype.onWidget=function(c,e){if("resize"==e.name&&this.frame){var h=this.frame.width,k=this.frame.height;h||null==this.frame.videoWidth||(h=this.frame.videoWidth,k=this.frame.videoHeight);h&&k&&(this.size=[h,k]);this.setDirtyCanvas(!0,!0)}else"view"==e.name&&this.show()};h.prototype.show=function(){showElement&&this.frame&&showElement(this.frame)};e.registerNodeType("graphics/frame", -h);x.title="Image fade";x.desc="Fades between images";x.widgets=[{name:"resizeA",text:"Resize to A",type:"button"},{name:"resizeB",text:"Resize to B",type:"button"}];x.prototype.onAdded=function(){this.createCanvas();var c=this.canvas.getContext("2d");c.fillStyle="#000";c.fillRect(0,0,this.properties.width,this.properties.height)};x.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};x.prototype.onExecute= -function(){var c=this.canvas.getContext("2d");this.canvas.width=this.canvas.width;var e=this.getInputData(0);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);e=this.getInputData(2);null==e?e=this.properties.fade:this.properties.fade=e;c.globalAlpha=e;e=this.getInputData(1);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);c.globalAlpha=1;this.setOutputData(0,this.canvas);this.setDirtyCanvas(!0)};e.registerNodeType("graphics/imagefade",x);k.title="Crop";k.desc="Crop Image"; +(c.length-1)*e,e=c[Math.floor(k)],c=c[Math.floor(k)+1],k=k-Math.floor(k);h[0]=e[0]*(1-k)+c[0]*k;h[1]=e[1]*(1-k)+c[1]*k;h[2]=e[2]*(1-k)+c[2]*k}for(var q in h)h[q]/=255;this.boxcolor=colorToString(h);this.setOutputData(0,h)}};h.registerNodeType("color/palette",n);e.title="Frame";e.desc="Frame viewerew";e.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}];e.prototype.onDrawBackground=function(c){this.frame&&!this.flags.collapsed&&c.drawImage(this.frame, +0,0,this.size[0],this.size[1])};e.prototype.onExecute=function(){this.frame=this.getInputData(0);this.setDirtyCanvas(!0)};e.prototype.onWidget=function(c,e){if("resize"==e.name&&this.frame){var h=this.frame.width,k=this.frame.height;h||null==this.frame.videoWidth||(h=this.frame.videoWidth,k=this.frame.videoHeight);h&&k&&(this.size=[h,k]);this.setDirtyCanvas(!0,!0)}else"view"==e.name&&this.show()};e.prototype.show=function(){showElement&&this.frame&&showElement(this.frame)};h.registerNodeType("graphics/frame", +e);w.title="Image fade";w.desc="Fades between images";w.widgets=[{name:"resizeA",text:"Resize to A",type:"button"},{name:"resizeB",text:"Resize to B",type:"button"}];w.prototype.onAdded=function(){this.createCanvas();var c=this.canvas.getContext("2d");c.fillStyle="#000";c.fillRect(0,0,this.properties.width,this.properties.height)};w.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};w.prototype.onExecute= +function(){var c=this.canvas.getContext("2d");this.canvas.width=this.canvas.width;var e=this.getInputData(0);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);e=this.getInputData(2);null==e?e=this.properties.fade:this.properties.fade=e;c.globalAlpha=e;e=this.getInputData(1);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);c.globalAlpha=1;this.setOutputData(0,this.canvas);this.setDirtyCanvas(!0)};h.registerNodeType("graphics/imagefade",w);k.title="Crop";k.desc="Crop Image"; k.prototype.onAdded=function(){this.createCanvas()};k.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};k.prototype.onExecute=function(){var c=this.getInputData(0);c&&(c.width?(this.canvas.getContext("2d").drawImage(c,-this.properties.x,-this.properties.y,c.width*this.properties.scale,c.height*this.properties.scale),this.setOutputData(0,this.canvas)):this.setOutputData(0,null))};k.prototype.onDrawBackground= -function(c){this.flags.collapsed||this.canvas&&c.drawImage(this.canvas,0,0,this.canvas.width,this.canvas.height,0,0,this.size[0],this.size[1])};k.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"scale"==c?(this.properties[c]=parseFloat(e),0==this.properties[c]&&(console.error("Error in scale"),this.properties[c]=1)):this.properties[c]=parseInt(e);this.createCanvas();return!0};e.registerNodeType("graphics/cropImage",k);C.title="Canvas";C.desc="Canvas to render stuff";C.prototype.onExecute= -function(){var c=this.canvas,e=this.properties.width|0,h=this.properties.height|0;c.width!=e&&(c.width=e);c.height!=h&&(c.height=h);this.properties.autoclear&&this.ctx.clearRect(0,0,c.width,c.height);this.setOutputData(0,c)};C.prototype.onAction=function(c,e){"clear"==c&&this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)};e.registerNodeType("graphics/canvas",C);w.title="DrawImage";w.desc="Draws image into a canvas";w.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e= -this.getInputOrProperty("img");if(e){var h=this.getInputOrProperty("x"),k=this.getInputOrProperty("y");c.getContext("2d").drawImage(e,h,k)}}};e.registerNodeType("graphics/drawImage",w);t.title="DrawRectangle";t.desc="Draws rectangle in canvas";t.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e=this.getInputOrProperty("x"),h=this.getInputOrProperty("y"),k=this.getInputOrProperty("w"),q=this.getInputOrProperty("h");c.getContext("2d").fillRect(e,h,k,q)}};e.registerNodeType("graphics/drawRectangle", -t);D.title="Video";D.desc="Video playback";D.widgets=[{name:"play",text:"PLAY",type:"minibutton"},{name:"stop",text:"STOP",type:"minibutton"},{name:"demo",text:"Demo video",type:"button"},{name:"mute",text:"Mute video",type:"button"}];D.prototype.onExecute=function(){if(this.properties.url&&(this.properties.url!=this._video_url&&this.loadVideo(this.properties.url),this._video&&0!=this._video.width)){var c=this.getInputData(0);c&&0<=c&&1>=c&&(this._video.currentTime=c*this._video.duration,this._video.pause()); -this._video.dirty=!0;this.setOutputData(0,this._video);this.setOutputData(1,this._video.currentTime);this.setOutputData(2,this._video.duration);this.setDirtyCanvas(!0)}};D.prototype.onStart=function(){this.play()};D.prototype.onStop=function(){this.stop()};D.prototype.loadVideo=function(c){this._video_url=c;var h=c.substr(0,10).indexOf(":"),k="";-1!=h&&(k=c.substr(0,h));h="";k&&(h=c.substr(0,c.indexOf("/",k.length+3)),h=h.substr(k.length+3));this.properties.use_proxy&&k&&e.proxy&&h!=location.host&& -(c=e.proxy+c.substr(c.indexOf(":")+3));this._video=document.createElement("video");this._video.src=c;this._video.type="type=video/mp4";this._video.muted=!0;this._video.autoplay=!0;var n=this;this._video.addEventListener("loadedmetadata",function(c){console.log("Duration: "+this.duration+" seconds");console.log("Size: "+this.videoWidth+","+this.videoHeight);n.setDirtyCanvas(!0);this.width=this.videoWidth;this.height=this.videoHeight});this._video.addEventListener("progress",function(c){console.log("video loading...")}); +function(c){this.flags.collapsed||this.canvas&&c.drawImage(this.canvas,0,0,this.canvas.width,this.canvas.height,0,0,this.size[0],this.size[1])};k.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"scale"==c?(this.properties[c]=parseFloat(e),0==this.properties[c]&&(console.error("Error in scale"),this.properties[c]=1)):this.properties[c]=parseInt(e);this.createCanvas();return!0};h.registerNodeType("graphics/cropImage",k);C.title="Canvas";C.desc="Canvas to render stuff";C.prototype.onExecute= +function(){var c=this.canvas,e=this.properties.width|0,h=this.properties.height|0;c.width!=e&&(c.width=e);c.height!=h&&(c.height=h);this.properties.autoclear&&this.ctx.clearRect(0,0,c.width,c.height);this.setOutputData(0,c)};C.prototype.onAction=function(c,e){"clear"==c&&this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)};h.registerNodeType("graphics/canvas",C);v.title="DrawImage";v.desc="Draws image into a canvas";v.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e= +this.getInputOrProperty("img");if(e){var h=this.getInputOrProperty("x"),k=this.getInputOrProperty("y");c.getContext("2d").drawImage(e,h,k)}}};h.registerNodeType("graphics/drawImage",v);s.title="DrawRectangle";s.desc="Draws rectangle in canvas";s.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e=this.getInputOrProperty("x"),h=this.getInputOrProperty("y"),k=this.getInputOrProperty("w"),q=this.getInputOrProperty("h");c.getContext("2d").fillRect(e,h,k,q)}};h.registerNodeType("graphics/drawRectangle", +s);D.title="Video";D.desc="Video playback";D.widgets=[{name:"play",text:"PLAY",type:"minibutton"},{name:"stop",text:"STOP",type:"minibutton"},{name:"demo",text:"Demo video",type:"button"},{name:"mute",text:"Mute video",type:"button"}];D.prototype.onExecute=function(){if(this.properties.url&&(this.properties.url!=this._video_url&&this.loadVideo(this.properties.url),this._video&&0!=this._video.width)){var c=this.getInputData(0);c&&0<=c&&1>=c&&(this._video.currentTime=c*this._video.duration,this._video.pause()); +this._video.dirty=!0;this.setOutputData(0,this._video);this.setOutputData(1,this._video.currentTime);this.setOutputData(2,this._video.duration);this.setDirtyCanvas(!0)}};D.prototype.onStart=function(){this.play()};D.prototype.onStop=function(){this.stop()};D.prototype.loadVideo=function(c){this._video_url=c;var e=c.substr(0,10).indexOf(":"),k="";-1!=e&&(k=c.substr(0,e));e="";k&&(e=c.substr(0,c.indexOf("/",k.length+3)),e=e.substr(k.length+3));this.properties.use_proxy&&k&&h.proxy&&e!=location.host&& +(c=h.proxy+c.substr(c.indexOf(":")+3));this._video=document.createElement("video");this._video.src=c;this._video.type="type=video/mp4";this._video.muted=!0;this._video.autoplay=!0;var n=this;this._video.addEventListener("loadedmetadata",function(c){console.log("Duration: "+this.duration+" seconds");console.log("Size: "+this.videoWidth+","+this.videoHeight);n.setDirtyCanvas(!0);this.width=this.videoWidth;this.height=this.videoHeight});this._video.addEventListener("progress",function(c){console.log("video loading...")}); this._video.addEventListener("error",function(c){console.error("Error loading video: "+this.src);if(this.error)switch(this.error.code){case this.error.MEDIA_ERR_ABORTED:console.error("You stopped the video.");break;case this.error.MEDIA_ERR_NETWORK:console.error("Network error - please try again later.");break;case this.error.MEDIA_ERR_DECODE:console.error("Video is broken..");break;case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED:console.error("Sorry, your browser can't play this video.")}});this._video.addEventListener("ended", function(c){console.log("Video Ended.");this.play()})};D.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"url"==c&&""!=e&&this.loadVideo(e);return!0};D.prototype.play=function(){this._video&&this._video.videoWidth&&this._video.play()};D.prototype.playPause=function(){this._video&&(this._video.paused?this.play():this.pause())};D.prototype.stop=function(){this._video&&(this._video.pause(),this._video.currentTime=0)};D.prototype.pause=function(){this._video&&(console.log("Video paused"), -this._video.pause())};D.prototype.onWidget=function(c,e){};e.registerNodeType("graphics/video",D);A.title="Webcam";A.desc="Webcam image";A.is_webcam_open=!1;A.prototype.openStream=function(){function c(h){console.log("Webcam rejected",h);e._webcam_stream=!1;A.is_webcam_open=!1;e.boxcolor="red";e.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](c); +this._video.pause())};D.prototype.onWidget=function(c,e){};h.registerNodeType("graphics/video",D);A.title="Webcam";A.desc="Webcam image";A.is_webcam_open=!1;A.prototype.openStream=function(){function c(h){console.log("Webcam rejected",h);e._webcam_stream=!1;A.is_webcam_open=!1;e.boxcolor="red";e.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](c); var e=this}};A.prototype.closeStream=function(){if(this._webcam_stream){var c=this._webcam_stream.getTracks();if(c.length)for(var e=0;e=this.size[1]||!this.properties.show|| -!this._video||(c.save(),c.drawImage(this._video,0,0,this.size[0],this.size[1]),c.restore())};A.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["stream_ready",e.EVENT],["stream_closed",e.EVENT],["stream_error",e.EVENT]]};e.registerNodeType("graphics/webcam",A)})(this); -(function(z){function c(){this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",filter:!0};this.size=[c.image_preview_size,c.image_preview_size]}function m(){this.addInput("Texture","Texture");this.properties={flipY:!1};this.size=[c.image_preview_size,c.image_preview_size]}function n(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",generate_mipmaps:!1}}function h(){this.addInput("Texture", +!this._video||(c.save(),c.drawImage(this._video,0,0,this.size[0],this.size[1]),c.restore())};A.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["stream_ready",h.EVENT],["stream_closed",h.EVENT],["stream_error",h.EVENT]]};h.registerNodeType("graphics/webcam",A)})(this); +(function(z){function c(){this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",filter:!0};this.size=[c.image_preview_size,c.image_preview_size]}function m(){this.addInput("Texture","Texture");this.properties={flipY:!1};this.size=[c.image_preview_size,c.image_preview_size]}function n(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",generate_mipmaps:!1}}function e(){this.addInput("Texture", "Texture");this.addInput("TextureB","Texture");this.addInput("value","number");this.addOutput("Texture","Texture");this.help="

pixelcode must be vec3, uvcode must be vec2, is optional

\t\t

uv: tex. coords

color: texture colorB: textureB

time: scene time value: input value

For multiline you must type: result = ...

";this.properties={value:1,pixelcode:"color + colorB * value",uvcode:"",precision:c.DEFAULT}; -this.has_error=!1}function x(){this.addOutput("out","Texture");this.properties={code:"",u_value:1,u_color:[1,1,1,1],width:512,height:512,precision:c.DEFAULT};this.properties.code=x.pixel_shader;this._uniforms={u_value:1,u_color:vec4.create(),in_texture:0,texSize:vec2.create(),time:0}}function k(){this.addInput("in","Texture");this.addInput("scale","vec2");this.addInput("offset","vec2");this.addOutput("out","Texture");this.properties={offset:vec2.fromValues(0,0),scale:vec2.fromValues(1,1),precision:c.DEFAULT}} -function C(){this.addInput("in","Texture");this.addInput("warp","Texture");this.addInput("factor","number");this.addOutput("out","Texture");this.properties={factor:0.01,scale:[1,1],offset:[0,0],precision:c.DEFAULT};this._uniforms={u_texture:0,u_textureB:1,u_factor:1,u_scale:vec2.create(),u_offset:vec2.create()}}function w(){this.addInput("Texture","Texture");this.properties={additive:!1,antialiasing:!1,filter:!0,disable_alpha:!1,gamma:1,viewport:[0,0,1,1]};this.size[0]=130}function t(){this.addInput("Texture", +this.has_error=!1}function w(){this.addOutput("out","Texture");this.properties={code:"",u_value:1,u_color:[1,1,1,1],width:512,height:512,precision:c.DEFAULT};this.properties.code=w.pixel_shader;this._uniforms={u_value:1,u_color:vec4.create(),in_texture:0,texSize:vec2.create(),time:0}}function k(){this.addInput("in","Texture");this.addInput("scale","vec2");this.addInput("offset","vec2");this.addOutput("out","Texture");this.properties={offset:vec2.fromValues(0,0),scale:vec2.fromValues(1,1),precision:c.DEFAULT}} +function C(){this.addInput("in","Texture");this.addInput("warp","Texture");this.addInput("factor","number");this.addOutput("out","Texture");this.properties={factor:0.01,scale:[1,1],offset:[0,0],precision:c.DEFAULT};this._uniforms={u_texture:0,u_textureB:1,u_factor:1,u_scale:vec2.create(),u_offset:vec2.create()}}function v(){this.addInput("Texture","Texture");this.properties={additive:!1,antialiasing:!1,filter:!0,disable_alpha:!1,gamma:1,viewport:[0,0,1,1]};this.size[0]=130}function s(){this.addInput("Texture", "Texture");this.addOutput("","Texture");this.properties={size:0,generate_mipmaps:!1,precision:c.DEFAULT}}function D(){this.addInput("Texture","Texture");this.addOutput("","Texture");this.properties={iterations:1,generate_mipmaps:!1,precision:c.DEFAULT}}function A(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("avg","vec4");this.addOutput("lum","number");this.properties={use_previous_frame:!0,high_quality:!1};this._uniforms={u_texture:0,u_mipmap_offset:0};this._luminance= -new Float32Array(4)}function e(){this.addInput("Texture","Texture");this.addOutput("min_t","Texture");this.addOutput("max_t","Texture");this.addOutput("min","vec4");this.addOutput("max","vec4");this.properties={mode:"max",use_previous_frame:!0};this._uniforms={u_texture:0};this._max=new Float32Array(4);this._min=new Float32Array(4);this._textures_chain=[]}function y(){this.addInput("in","Texture");this.addInput("factor","Number");this.addOutput("out","Texture");this.properties={factor:0.5};this._uniforms= -{u_texture:0,u_textureB:1,u_factor:this.properties.factor}}function B(){this.addInput("in","Texture");this.addOutput("avg","Texture");this.addOutput("array","Texture");this.properties={samples:64,frames_interval:1};this._uniforms={u_texture:0,u_textureB:1,u_samples:this.properties.samples,u_isamples:1/this.properties.samples};this.frame=0}function u(){this.addInput("Image","image");this.addOutput("","Texture");this.properties={}}function F(){this.addInput("Texture","Texture");this.addInput("LUT", -"Texture");this.addInput("Intensity","number");this.addOutput("","Texture");this.properties={enabled:!0,intensity:1,precision:c.DEFAULT,texture:null};F._shader||(F._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,F.pixel_shader))}function q(){this.addInput("Texture","Texture");this.addOutput("R","Texture");this.addOutput("G","Texture");this.addOutput("B","Texture");this.addOutput("A","Texture");q._shader||(q._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,q.pixel_shader))}function p(){this.addInput("R", -"Texture");this.addInput("G","Texture");this.addInput("B","Texture");this.addInput("A","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,R:1,G:1,B:1,A:1};this._color=vec4.create();this._uniforms={u_textureR:0,u_textureG:1,u_textureB:2,u_textureA:3,u_color:this._color}}function l(){this.addOutput("Texture","Texture");this._tex_color=vec4.create();this.properties={color:vec4.create(),precision:c.DEFAULT}}function a(){this.addInput("A","color");this.addInput("B","color"); -this.addOutput("Texture","Texture");this.properties={angle:0,scale:1,A:[0,0,0],B:[1,1,1],texture_size:32};a._shader||(a._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,a.pixel_shader));this._uniforms={u_angle:0,u_colorA:vec3.create(),u_colorB:vec3.create()}}function b(){this.addInput("A","Texture");this.addInput("B","Texture");this.addInput("Mixer","Texture");this.addOutput("Texture","Texture");this.properties={factor:0.5,size_from_biggest:!0,invert:!1,precision:c.DEFAULT};this._uniforms={u_textureA:0, -u_textureB:1,u_textureMix:2,u_mix:vec4.create()}}function d(){this.addInput("Tex.","Texture");this.addOutput("Edges","Texture");this.properties={invert:!0,threshold:!1,factor:1,precision:c.DEFAULT};d._shader||(d._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,d.pixel_shader))}function g(){this.addInput("Texture","Texture");this.addInput("Distance","number");this.addInput("Range","number");this.addOutput("Texture","Texture");this.properties={distance:100,range:50,only_depth:!1,high_precision:!1}; -this._uniforms={u_texture:0,u_distance:100,u_range:50,u_camera_planes:null}}function f(){this.addInput("Texture","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,invert:!1};this._uniforms={u_texture:0,u_camera_planes:null,u_ires:vec2.create()}}function r(){this.addInput("Texture","Texture");this.addInput("Iterations","number");this.addInput("Intensity","number");this.addOutput("Blurred","Texture");this.properties={intensity:1,iterations:1,preserve_aspect:!1,scale:[1, -1],precision:c.DEFAULT}}function s(){this.addInput("in","Texture");this.addInput("dirt","Texture");this.addOutput("out","Texture");this.addOutput("glow","Texture");this.properties={enabled:!0,intensity:1,persistence:0.99,iterations:16,threshold:0,scale:1,dirt_factor:0.5,precision:c.DEFAULT};this._textures=[];this._uniforms={u_intensity:1,u_texture:0,u_glow_texture:1,u_threshold:0,u_texel_size:vec2.create()}}function J(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties= -{intensity:1,radius:5}}function K(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={sigma:1.4,k:1.6,p:21.7,epsilon:79,phi:0.017}}function I(){this.addOutput("Webcam","Texture");this.properties={texture_name:"",facingMode:"user"};this.boxcolor="black";this.version=0}function E(){this.addInput("in","Texture");this.addInput("f","number");this.addOutput("out","Texture");this.properties={enabled:!0,factor:1,precision:c.LOW};this._uniforms={u_texture:0,u_factor:1}} -function v(){this.addInput("in","");this.properties={precision:c.LOW,width:0,height:0,channels:1};this.addOutput("out","Texture")}function H(){this.addInput("in","Texture");this.addOutput("out","Texture");this.properties={precision:c.LOW,split_channels:!1};this._values=new Uint8Array(1024);this._values.fill(255);this._curve_texture=null;this._uniforms={u_texture:0,u_curve:1,u_range:1};this._must_update=!0;this._points={RGB:[[0,0],[1,1]],R:[[0,0],[1,1]],G:[[0,0],[1,1]],B:[[0,0],[1,1]]};this.curve_editor= -null;this.addWidget("toggle","Split Channels",!1,"split_channels");this.addWidget("combo","Channel","RGB",{values:["RGB","R","G","B"]});this.curve_offset=68;this.size=[240,160]}function M(){this.addInput("in","Texture");this.addInput("exp","number");this.addOutput("out","Texture");this.properties={exposition:1,precision:c.LOW};this._uniforms={u_texture:0,u_exposition:1}}function L(){this.addInput("in","Texture");this.addInput("avg","number,Texture");this.addOutput("out","Texture");this.properties= -{enabled:!0,scale:1,gamma:1,average_lum:1,lum_white:1,precision:c.LOW};this._uniforms={u_texture:0,u_lumwhite2:1,u_igamma:1,u_scale:1,u_average_lum:1}}function O(){this.addOutput("out","Texture");this.properties={width:512,height:512,seed:0,persistence:0.1,octaves:8,scale:1,offset:[0,0],amplitude:1,precision:c.DEFAULT};this._key=0;this._texture=null;this._uniforms={u_persistence:0.1,u_seed:0,u_offset:vec2.create(),u_scale:1,u_viewport:vec2.create()}}function N(){this.addInput("v");this.addOutput("out", -"Texture");this.properties={code:N.default_code,width:512,height:512,clear:!0,precision:c.DEFAULT,use_html_canvas:!1};this._temp_texture=this._func=null;this.compileCode()}function P(){this.addInput("in","Texture");this.addOutput("out","Texture");this.properties={key_color:vec3.fromValues(0,1,0),threshold:0.8,slope:0.2,precision:c.DEFAULT}}function R(){this.addInput("in","texture");this.addInput("yaw","number");this.addOutput("out","texture");this.properties={yaw:0}}var G=z.LiteGraph,S=z.LGraphCanvas; -z.LGraphTexture=null;"undefined"!=typeof GL&&(S.link_type_colors.Texture="#987",z.LGraphTexture=c,c.title="Texture",c.desc="Texture",c.widgets_info={name:{widget:"texture"},filter:{widget:"checkbox"}},c.loadTextureCallback=null,c.image_preview_size=256,c.PASS_THROUGH=1,c.COPY=2,c.LOW=3,c.HIGH=4,c.REUSE=5,c.DEFAULT=2,c.MODE_VALUES={"pass through":c.PASS_THROUGH,copy:c.COPY,low:c.LOW,high:c.HIGH,reuse:c.REUSE,"default":c.DEFAULT},c.getTexturesContainer=function(){return gl.textures},c.loadTexture=function(a, -b){b=b||{};var d=a;"http://"==d.substr(0,7)&&G.proxy&&(d=G.proxy+d.substr(7));return c.getTexturesContainer()[a]=GL.Texture.fromURL(d,b)},c.getTexture=function(a){var b=this.getTexturesContainer();if(!b)throw"Cannot load texture, container of textures not found";b=b[a];return!b&&a&&":"!=a[0]?this.loadTexture(a):b},c.getTargetTexture=function(a,b,d){if(!a)throw"LGraphTexture.getTargetTexture expects a reference texture";var g=null;switch(d){case c.LOW:g=gl.UNSIGNED_BYTE;break;case c.HIGH:g=gl.HIGH_PRECISION_FORMAT; -break;case c.REUSE:return a;default:g=a?a.type:gl.UNSIGNED_BYTE}b&&b.width==a.width&&b.height==a.height&&b.type==g||(b=new GL.Texture(a.width,a.height,{type:g,format:gl.RGBA,filter:gl.LINEAR}));return b},c.getTextureType=function(a,b){var d=b?b.type:gl.UNSIGNED_BYTE;switch(a){case c.HIGH:d=gl.HIGH_PRECISION_FORMAT;break;case c.LOW:d=gl.UNSIGNED_BYTE}return d},c.getWhiteTexture=function(){return this._white_texture?this._white_texture:this._white_texture=GL.Texture.fromMemory(1,1,[255,255,255,255], -{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.getNoiseTexture=function(){if(this._noise_texture)return this._noise_texture;for(var a=new Uint8Array(1048576),b=0;1048576>b;++b)a[b]=255*Math.random();return this._noise_texture=a=GL.Texture.fromMemory(512,512,a,{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.prototype.onDropFile=function(a,b,d){if(a){var c=null;"string"==typeof a?c=GL.Texture.fromURL(a):-1!=b.toLowerCase().indexOf(".dds")?c=GL.Texture.fromDDSInMemory(a):(a=new Blob([d]), -a=URL.createObjectURL(a),c=GL.Texture.fromURL(a));this._drop_texture=c;this.properties.name=b}else this._drop_texture=null,this.properties.name=""},c.prototype.getExtraMenuOptions=function(a){var b=this;if(this._drop_texture)return[{content:"Clear",callback:function(){b._drop_texture=null;b.properties.name=""}}]},c.prototype.onExecute=function(){var a=null;this.isOutputConnected(1)&&(a=this.getInputData(0));!a&&this._drop_texture&&(a=this._drop_texture);!a&&this.properties.name&&(a=c.getTexture(this.properties.name)); -if(a){this._last_tex=a;!1===this.properties.filter?a.setParameter(gl.TEXTURE_MAG_FILTER,gl.NEAREST):a.setParameter(gl.TEXTURE_MAG_FILTER,gl.LINEAR);this.setOutputData(0,a);this.setOutputData(1,a.fullpath||a.filename);for(var b=2;b=this.size[1]))if(this._drop_texture&&a.webgl)a.drawImage(this._drop_texture,0,0,this.size[0],this.size[1]);else{if(this._last_preview_tex!=this._last_tex)if(a.webgl)this._canvas=this._last_tex;else{var b=c.generateLowResTexturePreview(this._last_tex);if(!b)return;this._last_preview_tex=this._last_tex;this._canvas=cloneCanvas(b)}this._canvas&&(a.save(),a.webgl||(a.translate(0, -this.size[1]),a.scale(1,-1)),a.drawImage(this._canvas,0,0,this.size[0],this.size[1]),a.restore())}},c.generateLowResTexturePreview=function(a){if(!a)return null;var b=c.image_preview_size,d=a;if(a.format==gl.DEPTH_COMPONENT)return null;if(a.width>b||a.height>b)d=this._preview_temp_tex,this._preview_temp_tex||(this._preview_temp_tex=d=new GL.Texture(b,b,{minFilter:gl.NEAREST})),a.copyTo(d);a=this._preview_canvas;a||(this._preview_canvas=a=createCanvas(b,b));d&&d.toCanvas(a);return a},c.prototype.getResources= -function(a){this.properties.name&&(a[this.properties.name]=GL.Texture);return a},c.prototype.onGetInputs=function(){return[["in","Texture"]]},c.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["aspect","number"]]},c.replaceCode=function(a,b){return a.replace(/\{\{[a-zA-Z0-9_]*\}\}/g,function(a){a=a.replace(/[\{\}]/g,"");return b[a]||""})},G.registerNodeType("texture/texture",c),m.title="Preview",m.desc="Show a texture in the graph canvas",m.allow_preview=!1,m.prototype.onDrawBackground= -function(a){if(!this.flags.collapsed&&(a.webgl||m.allow_preview)){var b=this.getInputData(0);if(b){var d=null,d=!b.handle&&a.webgl?b:c.generateLowResTexturePreview(b);a.save();this.properties.flipY&&(a.translate(0,this.size[1]),a.scale(1,-1));a.drawImage(d,0,0,this.size[0],this.size[1]);a.restore()}}},G.registerNodeType("texture/preview",m),n.title="Save",n.desc="Save a texture in the repository",n.prototype.getPreviewTexture=function(){return this._texture},n.prototype.onExecute=function(){var a= -this.getInputData(0);a&&(this.properties.generate_mipmaps&&(a.bind(0),a.setParameter(gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_LINEAR),gl.generateMipmap(a.texture_type),a.unbind(0)),this.properties.name&&(c.storeTexture?c.storeTexture(this.properties.name,a):c.getTexturesContainer()[this.properties.name]=a),this._texture=a,this.setOutputData(0,a),this.setOutputData(1,this.properties.name))},G.registerNodeType("texture/save",n),h.widgets_info={uvcode:{widget:"code"},pixelcode:{widget:"code"},precision:{widget:"combo", -values:c.MODE_VALUES}},h.title="Operation",h.desc="Texture shader operation",h.presets={},h.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:b.properties.show?"Hide Texture":"Show Texture",callback:function(){b.properties.show=!b.properties.show}}]},h.prototype.onPropertyChanged=function(){this.has_error=!1},h.prototype.onDrawBackground=function(a){this.flags.collapsed||20>=this.size[1]||!this.properties.show||!this._tex||this._tex.gl!=a||(a.save(),a.drawImage(this._tex,0,0,this.size[0], -this.size[1]),a.restore())},h.prototype.onExecute=function(){var a=this.getInputData(0);if(this.isOutputConnected(0))if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else{var b=this.getInputData(1);if(this.properties.uvcode||this.properties.pixelcode){var d=512,g=512;a?(d=a.width,g=a.height):b&&(d=b.width,g=b.height);b||(b=GL.Texture.getWhiteTexture());var f=c.getTextureType(this.properties.precision,a);this._tex=a||this._tex?c.getTargetTexture(a||this._tex,this._tex,this.properties.precision): -new GL.Texture(d,g,{type:f,format:gl.RGBA,filter:gl.LINEAR});f="";this.properties.uvcode&&(f="uv = "+this.properties.uvcode,-1!=this.properties.uvcode.indexOf(";")&&(f=this.properties.uvcode));var e="";this.properties.pixelcode&&(e="result = "+this.properties.pixelcode,-1!=this.properties.pixelcode.indexOf(";")&&(e=this.properties.pixelcode));var l=this._shader;if(!(this.has_error||l&&this._shader_code==f+"|"+e)){var k=c.replaceCode(h.pixel_shader,{UV_CODE:f,PIXEL_CODE:e});try{l=new GL.Shader(Shader.SCREEN_VERTEX_SHADER, -k),this.boxcolor="#00FF00"}catch(q){GL.Shader.dumpErrorToConsole(q,Shader.SCREEN_VERTEX_SHADER,k);this.boxcolor="#FF0000";this.has_error=!0;return}this._shader=l;this._shader_code=f+"|"+e}if(this._shader){var p=this.getInputData(2);null!=p?this.properties.value=p:p=parseFloat(this.properties.value);var r=this.graph.getTime();this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);gl.disable(gl.BLEND);a&&a.bind(0);b&&b.bind(1);var c=Mesh.getScreenQuad();l.uniforms({u_texture:0, -u_textureB:1,value:p,texSize:[d,g],time:r}).draw(c)});this.setOutputData(0,this._tex)}}}},h.pixel_shader="precision highp float;\n\t\t\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec2 texSize;\n\t\tuniform float time;\n\t\tuniform float value;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord;\n\t\t\t{{UV_CODE}};\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\tvec3 color = color4.rgb;\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\tvec3 colorB = color4B.rgb;\n\t\t\tvec3 result = color;\n\t\t\tfloat alpha = 1.0;\n\t\t\t{{PIXEL_CODE}};\n\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t}\n\t\t", -h.registerPreset=function(a,b){h.presets[a]=b},h.registerPreset("",""),h.registerPreset("bypass","color"),h.registerPreset("add","color + colorB * value"),h.registerPreset("substract","(color - colorB) * value"),h.registerPreset("mate","mix( color, colorB, color4B.a * value)"),h.registerPreset("invert","vec3(1.0) - color"),h.registerPreset("multiply","color * colorB * value"),h.registerPreset("divide","(color / colorB) / value"),h.registerPreset("difference","abs(color - colorB) * value"),h.registerPreset("max", -"max(color, colorB) * value"),h.registerPreset("min","min(color, colorB) * value"),h.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"),h.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"),h.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"),h.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"), -h.prototype.onInspect=function(a){var b=this;a.addCombo("Presets","",{values:Object.keys(h.presets),callback:function(d){var c=h.presets[d];c&&(b.setProperty("pixelcode",c),b.title=d,a.refresh())}})},G.registerNodeType("texture/operation",h),x.title="Shader",x.desc="Texture shader",x.widgets_info={code:{type:"code",lang:"glsl"},precision:{widget:"combo",values:c.MODE_VALUES}},x.prototype.onPropertyChanged=function(a,b){if("code"==a){var d=this.getShader();if(d){var c=d.uniformInfo;if(this.inputs)for(var g= +new Float32Array(4)}function h(){this.addInput("Texture","Texture");this.addOutput("min_t","Texture");this.addOutput("max_t","Texture");this.addOutput("min","vec4");this.addOutput("max","vec4");this.properties={mode:"max",use_previous_frame:!0};this._uniforms={u_texture:0};this._max=new Float32Array(4);this._min=new Float32Array(4);this._textures_chain=[]}function y(){this.addInput("in","Texture");this.addInput("factor","Number");this.addOutput("out","Texture");this.properties={factor:0.5};this._uniforms= +{u_texture:0,u_textureB:1,u_factor:this.properties.factor}}function B(){this.addInput("in","Texture");this.addOutput("avg","Texture");this.addOutput("array","Texture");this.properties={samples:64,frames_interval:1};this._uniforms={u_texture:0,u_textureB:1,u_samples:this.properties.samples,u_isamples:1/this.properties.samples};this.frame=0}function u(){this.addInput("Image","image");this.addOutput("","Texture");this.properties={}}function G(){this.addInput("Texture","Texture");this.addInput("LUT", +"Texture");this.addInput("Intensity","number");this.addOutput("","Texture");this.properties={enabled:!0,intensity:1,precision:c.DEFAULT,texture:null};G._shader||(G._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,G.pixel_shader))}function q(){this.addInput("Texture","Texture");this.addInput("Atlas","Texture");this.addOutput("","Texture");this.properties={enabled:!0,num_row_symbols:4,symbol_size:16,brightness:1,colorize:!1,filter:!1,invert:!1,precision:c.DEFAULT,texture:null};q._shader||(q._shader= +new GL.Shader(Shader.SCREEN_VERTEX_SHADER,q.pixel_shader));this._uniforms={u_texture:0,u_textureB:1,u_row_simbols:4,u_simbol_size:16,u_res:vec2.create()}}function p(){this.addInput("Texture","Texture");this.addOutput("R","Texture");this.addOutput("G","Texture");this.addOutput("B","Texture");this.addOutput("A","Texture");p._shader||(p._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,p.pixel_shader))}function l(){this.addInput("R","Texture");this.addInput("G","Texture");this.addInput("B","Texture"); +this.addInput("A","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,R:1,G:1,B:1,A:1};this._color=vec4.create();this._uniforms={u_textureR:0,u_textureG:1,u_textureB:2,u_textureA:3,u_color:this._color}}function a(){this.addOutput("Texture","Texture");this._tex_color=vec4.create();this.properties={color:vec4.create(),precision:c.DEFAULT}}function b(){this.addInput("A","color");this.addInput("B","color");this.addOutput("Texture","Texture");this.properties={angle:0,scale:1, +A:[0,0,0],B:[1,1,1],texture_size:32};b._shader||(b._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader));this._uniforms={u_angle:0,u_colorA:vec3.create(),u_colorB:vec3.create()}}function d(){this.addInput("A","Texture");this.addInput("B","Texture");this.addInput("Mixer","Texture");this.addOutput("Texture","Texture");this.properties={factor:0.5,size_from_biggest:!0,invert:!1,precision:c.DEFAULT};this._uniforms={u_textureA:0,u_textureB:1,u_textureMix:2,u_mix:vec4.create()}}function g(){this.addInput("Tex.", +"Texture");this.addOutput("Edges","Texture");this.properties={invert:!0,threshold:!1,factor:1,precision:c.DEFAULT};g._shader||(g._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,g.pixel_shader))}function f(){this.addInput("Texture","Texture");this.addInput("Distance","number");this.addInput("Range","number");this.addOutput("Texture","Texture");this.properties={distance:100,range:50,only_depth:!1,high_precision:!1};this._uniforms={u_texture:0,u_distance:100,u_range:50,u_camera_planes:null}}function r(){this.addInput("Texture", +"Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,invert:!1};this._uniforms={u_texture:0,u_camera_planes:null,u_ires:vec2.create()}}function x(){this.addInput("Texture","Texture");this.addInput("Iterations","number");this.addInput("Intensity","number");this.addOutput("Blurred","Texture");this.properties={intensity:1,iterations:1,preserve_aspect:!1,scale:[1,1],precision:c.DEFAULT}}function I(){this.addInput("in","Texture");this.addInput("dirt","Texture");this.addOutput("out", +"Texture");this.addOutput("glow","Texture");this.properties={enabled:!0,intensity:1,persistence:0.99,iterations:16,threshold:0,scale:1,dirt_factor:0.5,precision:c.DEFAULT};this._textures=[];this._uniforms={u_intensity:1,u_texture:0,u_glow_texture:1,u_threshold:0,u_texel_size:vec2.create()}}function J(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={intensity:1,radius:5}}function K(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties= +{sigma:1.4,k:1.6,p:21.7,epsilon:79,phi:0.017}}function E(){this.addOutput("Webcam","Texture");this.properties={texture_name:"",facingMode:"user"};this.boxcolor="black";this.version=0}function t(){this.addInput("in","Texture");this.addInput("f","number");this.addOutput("out","Texture");this.properties={enabled:!0,factor:1,precision:c.LOW};this._uniforms={u_texture:0,u_factor:1}}function Q(){this.addInput("in","");this.properties={precision:c.LOW,width:0,height:0,channels:1};this.addOutput("out","Texture")} +function H(){this.addInput("in","Texture");this.addOutput("out","Texture");this.properties={precision:c.LOW,split_channels:!1};this._values=new Uint8Array(1024);this._values.fill(255);this._curve_texture=null;this._uniforms={u_texture:0,u_curve:1,u_range:1};this._must_update=!0;this._points={RGB:[[0,0],[1,1]],R:[[0,0],[1,1]],G:[[0,0],[1,1]],B:[[0,0],[1,1]]};this.curve_editor=null;this.addWidget("toggle","Split Channels",!1,"split_channels");this.addWidget("combo","Channel","RGB",{values:["RGB","R", +"G","B"]});this.curve_offset=68;this.size=[240,160]}function O(){this.addInput("in","Texture");this.addInput("exp","number");this.addOutput("out","Texture");this.properties={exposition:1,precision:c.LOW};this._uniforms={u_texture:0,u_exposition:1}}function L(){this.addInput("in","Texture");this.addInput("avg","number,Texture");this.addOutput("out","Texture");this.properties={enabled:!0,scale:1,gamma:1,average_lum:1,lum_white:1,precision:c.LOW};this._uniforms={u_texture:0,u_lumwhite2:1,u_igamma:1, +u_scale:1,u_average_lum:1}}function N(){this.addOutput("out","Texture");this.properties={width:512,height:512,seed:0,persistence:0.1,octaves:8,scale:1,offset:[0,0],amplitude:1,precision:c.DEFAULT};this._key=0;this._texture=null;this._uniforms={u_persistence:0.1,u_seed:0,u_offset:vec2.create(),u_scale:1,u_viewport:vec2.create()}}function M(){this.addInput("v");this.addOutput("out","Texture");this.properties={code:M.default_code,width:512,height:512,clear:!0,precision:c.DEFAULT,use_html_canvas:!1}; +this._temp_texture=this._func=null;this.compileCode()}function P(){this.addInput("in","Texture");this.addOutput("out","Texture");this.properties={key_color:vec3.fromValues(0,1,0),threshold:0.8,slope:0.2,precision:c.DEFAULT}}function R(){this.addInput("in","texture");this.addInput("yaw","number");this.addOutput("out","texture");this.properties={yaw:0}}var F=z.LiteGraph,T=z.LGraphCanvas;z.LGraphTexture=null;"undefined"!=typeof GL&&(T.link_type_colors.Texture="#987",z.LGraphTexture=c,c.title="Texture", +c.desc="Texture",c.widgets_info={name:{widget:"texture"},filter:{widget:"checkbox"}},c.loadTextureCallback=null,c.image_preview_size=256,c.PASS_THROUGH=1,c.COPY=2,c.LOW=3,c.HIGH=4,c.REUSE=5,c.DEFAULT=2,c.MODE_VALUES={"pass through":c.PASS_THROUGH,copy:c.COPY,low:c.LOW,high:c.HIGH,reuse:c.REUSE,"default":c.DEFAULT},c.getTexturesContainer=function(){return gl.textures},c.loadTexture=function(a,b){b=b||{};var d=a;"http://"==d.substr(0,7)&&F.proxy&&(d=F.proxy+d.substr(7));return c.getTexturesContainer()[a]= +GL.Texture.fromURL(d,b)},c.getTexture=function(a){var b=this.getTexturesContainer();if(!b)throw"Cannot load texture, container of textures not found";b=b[a];return!b&&a&&":"!=a[0]?this.loadTexture(a):b},c.getTargetTexture=function(a,b,d){if(!a)throw"LGraphTexture.getTargetTexture expects a reference texture";var g=null;switch(d){case c.LOW:g=gl.UNSIGNED_BYTE;break;case c.HIGH:g=gl.HIGH_PRECISION_FORMAT;break;case c.REUSE:return a;default:g=a?a.type:gl.UNSIGNED_BYTE}b&&b.width==a.width&&b.height== +a.height&&b.type==g||(b=new GL.Texture(a.width,a.height,{type:g,format:gl.RGBA,filter:gl.LINEAR}));return b},c.getTextureType=function(a,b){var d=b?b.type:gl.UNSIGNED_BYTE;switch(a){case c.HIGH:d=gl.HIGH_PRECISION_FORMAT;break;case c.LOW:d=gl.UNSIGNED_BYTE}return d},c.getWhiteTexture=function(){return this._white_texture?this._white_texture:this._white_texture=GL.Texture.fromMemory(1,1,[255,255,255,255],{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.getNoiseTexture=function(){if(this._noise_texture)return this._noise_texture; +for(var a=new Uint8Array(1048576),b=0;1048576>b;++b)a[b]=255*Math.random();return this._noise_texture=a=GL.Texture.fromMemory(512,512,a,{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.prototype.onDropFile=function(a,b,d){if(a){var c=null;"string"==typeof a?c=GL.Texture.fromURL(a):-1!=b.toLowerCase().indexOf(".dds")?c=GL.Texture.fromDDSInMemory(a):(a=new Blob([d]),a=URL.createObjectURL(a),c=GL.Texture.fromURL(a));this._drop_texture=c;this.properties.name=b}else this._drop_texture=null,this.properties.name= +""},c.prototype.getExtraMenuOptions=function(a){var b=this;if(this._drop_texture)return[{content:"Clear",callback:function(){b._drop_texture=null;b.properties.name=""}}]},c.prototype.onExecute=function(){var a=null;this.isOutputConnected(1)&&(a=this.getInputData(0));!a&&this._drop_texture&&(a=this._drop_texture);!a&&this.properties.name&&(a=c.getTexture(this.properties.name));if(a){this._last_tex=a;!1===this.properties.filter?a.setParameter(gl.TEXTURE_MAG_FILTER,gl.NEAREST):a.setParameter(gl.TEXTURE_MAG_FILTER, +gl.LINEAR);this.setOutputData(0,a);this.setOutputData(1,a.fullpath||a.filename);for(var b=2;b= +this.size[1]))if(this._drop_texture&&a.webgl)a.drawImage(this._drop_texture,0,0,this.size[0],this.size[1]);else{if(this._last_preview_tex!=this._last_tex)if(a.webgl)this._canvas=this._last_tex;else{var b=c.generateLowResTexturePreview(this._last_tex);if(!b)return;this._last_preview_tex=this._last_tex;this._canvas=cloneCanvas(b)}this._canvas&&(a.save(),a.webgl||(a.translate(0,this.size[1]),a.scale(1,-1)),a.drawImage(this._canvas,0,0,this.size[0],this.size[1]),a.restore())}},c.generateLowResTexturePreview= +function(a){if(!a)return null;var b=c.image_preview_size,d=a;if(a.format==gl.DEPTH_COMPONENT)return null;if(a.width>b||a.height>b)d=this._preview_temp_tex,this._preview_temp_tex||(this._preview_temp_tex=d=new GL.Texture(b,b,{minFilter:gl.NEAREST})),a.copyTo(d);a=this._preview_canvas;a||(this._preview_canvas=a=createCanvas(b,b));d&&d.toCanvas(a);return a},c.prototype.getResources=function(a){this.properties.name&&(a[this.properties.name]=GL.Texture);return a},c.prototype.onGetInputs=function(){return[["in", +"Texture"]]},c.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["aspect","number"]]},c.replaceCode=function(a,b){return a.replace(/\{\{[a-zA-Z0-9_]*\}\}/g,function(a){a=a.replace(/[\{\}]/g,"");return b[a]||""})},F.registerNodeType("texture/texture",c),m.title="Preview",m.desc="Show a texture in the graph canvas",m.allow_preview=!1,m.prototype.onDrawBackground=function(a){if(!this.flags.collapsed&&(a.webgl||m.allow_preview)){var b=this.getInputData(0);if(b){var d=null, +d=!b.handle&&a.webgl?b:c.generateLowResTexturePreview(b);a.save();this.properties.flipY&&(a.translate(0,this.size[1]),a.scale(1,-1));a.drawImage(d,0,0,this.size[0],this.size[1]);a.restore()}}},F.registerNodeType("texture/preview",m),n.title="Save",n.desc="Save a texture in the repository",n.prototype.getPreviewTexture=function(){return this._texture},n.prototype.onExecute=function(){var a=this.getInputData(0);a&&(this.properties.generate_mipmaps&&(a.bind(0),a.setParameter(gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_LINEAR), +gl.generateMipmap(a.texture_type),a.unbind(0)),this.properties.name&&(c.storeTexture?c.storeTexture(this.properties.name,a):c.getTexturesContainer()[this.properties.name]=a),this._texture=a,this.setOutputData(0,a),this.setOutputData(1,this.properties.name))},F.registerNodeType("texture/save",n),e.widgets_info={uvcode:{widget:"code"},pixelcode:{widget:"code"},precision:{widget:"combo",values:c.MODE_VALUES}},e.title="Operation",e.desc="Texture shader operation",e.presets={},e.prototype.getExtraMenuOptions= +function(a){var b=this;return[{content:b.properties.show?"Hide Texture":"Show Texture",callback:function(){b.properties.show=!b.properties.show}}]},e.prototype.onPropertyChanged=function(){this.has_error=!1},e.prototype.onDrawBackground=function(a){this.flags.collapsed||20>=this.size[1]||!this.properties.show||!this._tex||this._tex.gl!=a||(a.save(),a.drawImage(this._tex,0,0,this.size[0],this.size[1]),a.restore())},e.prototype.onExecute=function(){var a=this.getInputData(0);if(this.isOutputConnected(0))if(this.properties.precision=== +c.PASS_THROUGH)this.setOutputData(0,a);else{var b=this.getInputData(1);if(this.properties.uvcode||this.properties.pixelcode){var d=512,g=512;a?(d=a.width,g=a.height):b&&(d=b.width,g=b.height);b||(b=GL.Texture.getWhiteTexture());var f=c.getTextureType(this.properties.precision,a);this._tex=a||this._tex?c.getTargetTexture(a||this._tex,this._tex,this.properties.precision):new GL.Texture(d,g,{type:f,format:gl.RGBA,filter:gl.LINEAR});f="";this.properties.uvcode&&(f="uv = "+this.properties.uvcode,-1!=this.properties.uvcode.indexOf(";")&& +(f=this.properties.uvcode));var l="";this.properties.pixelcode&&(l="result = "+this.properties.pixelcode,-1!=this.properties.pixelcode.indexOf(";")&&(l=this.properties.pixelcode));var h=this._shader;if(!(this.has_error||h&&this._shader_code==f+"|"+l)){var k=c.replaceCode(e.pixel_shader,{UV_CODE:f,PIXEL_CODE:l});try{h=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,k),this.boxcolor="#00FF00"}catch(q){GL.Shader.dumpErrorToConsole(q,Shader.SCREEN_VERTEX_SHADER,k);this.boxcolor="#FF0000";this.has_error=!0; +return}this._shader=h;this._shader_code=f+"|"+l}if(this._shader){var p=this.getInputData(2);null!=p?this.properties.value=p:p=parseFloat(this.properties.value);var r=this.graph.getTime();this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);gl.disable(gl.BLEND);a&&a.bind(0);b&&b.bind(1);var c=Mesh.getScreenQuad();h.uniforms({u_texture:0,u_textureB:1,value:p,texSize:[d,g],time:r}).draw(c)});this.setOutputData(0,this._tex)}}}},e.pixel_shader="precision highp float;\n\t\t\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec2 texSize;\n\t\tuniform float time;\n\t\tuniform float value;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord;\n\t\t\t{{UV_CODE}};\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\tvec3 color = color4.rgb;\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\tvec3 colorB = color4B.rgb;\n\t\t\tvec3 result = color;\n\t\t\tfloat alpha = 1.0;\n\t\t\t{{PIXEL_CODE}};\n\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t}\n\t\t", +e.registerPreset=function(a,b){e.presets[a]=b},e.registerPreset("",""),e.registerPreset("bypass","color"),e.registerPreset("add","color + colorB * value"),e.registerPreset("substract","(color - colorB) * value"),e.registerPreset("mate","mix( color, colorB, color4B.a * value)"),e.registerPreset("invert","vec3(1.0) - color"),e.registerPreset("multiply","color * colorB * value"),e.registerPreset("divide","(color / colorB) / value"),e.registerPreset("difference","abs(color - colorB) * value"),e.registerPreset("max", +"max(color, colorB) * value"),e.registerPreset("min","min(color, colorB) * value"),e.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"),e.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"),e.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"),e.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"), +e.prototype.onInspect=function(a){var b=this;a.addCombo("Presets","",{values:Object.keys(e.presets),callback:function(d){var c=e.presets[d];c&&(b.setProperty("pixelcode",c),b.title=d,a.refresh())}})},F.registerNodeType("texture/operation",e),w.title="Shader",w.desc="Texture shader",w.widgets_info={code:{type:"code",lang:"glsl"},precision:{widget:"combo",values:c.MODE_VALUES}},w.prototype.onPropertyChanged=function(a,b){if("code"==a){var d=this.getShader();if(d){var c=d.uniformInfo;if(this.inputs)for(var g= {},f=0;f lumaMax))\n\t\t\t\tcolor = vec4(rgbA, 1.0);\n\t\t\telse\n\t\t\t\tcolor = vec4(rgbB, 1.0);\n\t\t\tif(u_igamma != 1.0)\n\t\t\t\tcolor.xyz = pow( color.xyz, vec3(u_igamma) );\n\t\t\treturn color;\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\t\t}\n\t\t", -w.gamma_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_igamma;\n\t\tvoid main() {\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\t\t gl_FragColor = color;\n\t\t}\n\t\t",G.registerNodeType("texture/toviewport",w),t.title="Copy",t.desc="Copy Texture",t.widgets_info={size:{widget:"combo",values:[0,32,64,128,256,512,1024,2048]},precision:{widget:"combo", -values:c.MODE_VALUES}},t.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)){if(a){var b=a.width,d=a.height;0!=this.properties.size&&(d=b=this.properties.size);var g=this._temp_texture,f=a.type;this.properties.precision===c.LOW?f=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(f=gl.HIGH_PRECISION_FORMAT);g&&g.width==b&&g.height==d&&g.type==f||(g=gl.LINEAR,this.properties.generate_mipmaps&&isPowerOfTwo(b)&&isPowerOfTwo(d)&&(g=gl.LINEAR_MIPMAP_LINEAR), -this._temp_texture=new GL.Texture(b,d,{type:f,format:gl.RGBA,minFilter:g,magFilter:gl.LINEAR}));a.copyTo(this._temp_texture);this.properties.generate_mipmaps&&(this._temp_texture.bind(0),gl.generateMipmap(this._temp_texture.texture_type),this._temp_texture.unbind(0))}this.setOutputData(0,this._temp_texture)}},G.registerNodeType("texture/copy",t),D.title="Downsample",D.desc="Downsample Texture",D.widgets_info={iterations:{type:"number",step:1,precision:0,min:0},precision:{widget:"combo",values:c.MODE_VALUES}}, +F.registerNodeType("texture/warp",C),v.title="to Viewport",v.desc="Texture to viewport",v._prev_viewport=new Float32Array(4),v.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this.properties.disable_alpha?gl.disable(gl.BLEND):(gl.enable(gl.BLEND),this.properties.additive?gl.blendFunc(gl.SRC_ALPHA,gl.ONE):gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA));gl.disable(gl.DEPTH_TEST);var b=this.properties.gamma||1;this.isInputConnected(1)&&(b=this.getInputData(1));a.setParameter(gl.TEXTURE_MAG_FILTER, +this.properties.filter?gl.LINEAR:gl.NEAREST);var d=v._prev_viewport;d.set(gl.viewport_data);var c=this.properties.viewport;gl.viewport(d[0]+d[2]*c[0],d[1]+d[3]*c[1],d[2]*c[2],d[3]*c[3]);gl.getViewport();this.properties.antialiasing?(v._shader||(v._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,v.aa_pixel_shader)),c=Mesh.getScreenQuad(),a.bind(0),v._shader.uniforms({u_texture:0,uViewportSize:[a.width,a.height],u_igamma:1/b,inverseVP:[1/a.width,1/a.height]}).draw(c)):1!=b?(v._gamma_shader||(v._gamma_shader= +new GL.Shader(Shader.SCREEN_VERTEX_SHADER,v.gamma_pixel_shader)),a.toViewport(v._gamma_shader,{u_texture:0,u_igamma:1/b})):a.toViewport();gl.viewport(d[0],d[1],d[2],d[3])}},v.prototype.onGetInputs=function(){return[["gamma","number"]]},v.aa_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 uViewportSize;\n\t\tuniform vec2 inverseVP;\n\t\tuniform float u_igamma;\n\t\t#define FXAA_REDUCE_MIN (1.0/ 128.0)\n\t\t#define FXAA_REDUCE_MUL (1.0 / 8.0)\n\t\t#define FXAA_SPAN_MAX 8.0\n\t\t\n\t\t/* from mitsuhiko/webgl-meincraft based on the code on geeks3d.com */\n\t\tvec4 applyFXAA(sampler2D tex, vec2 fragCoord)\n\t\t{\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t/*vec2 inverseVP = vec2(1.0 / uViewportSize.x, 1.0 / uViewportSize.y);*/\n\t\t\tvec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbM = texture2D(tex, fragCoord * inverseVP).xyz;\n\t\t\tvec3 luma = vec3(0.299, 0.587, 0.114);\n\t\t\tfloat lumaNW = dot(rgbNW, luma);\n\t\t\tfloat lumaNE = dot(rgbNE, luma);\n\t\t\tfloat lumaSW = dot(rgbSW, luma);\n\t\t\tfloat lumaSE = dot(rgbSE, luma);\n\t\t\tfloat lumaM = dot(rgbM, luma);\n\t\t\tfloat lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n\t\t\tfloat lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\t\t\t\n\t\t\tvec2 dir;\n\t\t\tdir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n\t\t\tdir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\t\t\t\n\t\t\tfloat dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\t\t\t\n\t\t\tfloat rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n\t\t\tdir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * inverseVP;\n\t\t\t\n\t\t\tvec3 rgbA = 0.5 * (texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + \n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz);\n\t\t\tvec3 rgbB = rgbA * 0.5 + 0.25 * (texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + \n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz);\n\t\t\t\n\t\t\t//return vec4(rgbA,1.0);\n\t\t\tfloat lumaB = dot(rgbB, luma);\n\t\t\tif ((lumaB < lumaMin) || (lumaB > lumaMax))\n\t\t\t\tcolor = vec4(rgbA, 1.0);\n\t\t\telse\n\t\t\t\tcolor = vec4(rgbB, 1.0);\n\t\t\tif(u_igamma != 1.0)\n\t\t\t\tcolor.xyz = pow( color.xyz, vec3(u_igamma) );\n\t\t\treturn color;\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\t\t}\n\t\t", +v.gamma_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_igamma;\n\t\tvoid main() {\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\t\t gl_FragColor = color;\n\t\t}\n\t\t",F.registerNodeType("texture/toviewport",v),s.title="Copy",s.desc="Copy Texture",s.widgets_info={size:{widget:"combo",values:[0,32,64,128,256,512,1024,2048]},precision:{widget:"combo", +values:c.MODE_VALUES}},s.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)){if(a){var b=a.width,d=a.height;0!=this.properties.size&&(d=b=this.properties.size);var g=this._temp_texture,f=a.type;this.properties.precision===c.LOW?f=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(f=gl.HIGH_PRECISION_FORMAT);g&&g.width==b&&g.height==d&&g.type==f||(g=gl.LINEAR,this.properties.generate_mipmaps&&isPowerOfTwo(b)&&isPowerOfTwo(d)&&(g=gl.LINEAR_MIPMAP_LINEAR), +this._temp_texture=new GL.Texture(b,d,{type:f,format:gl.RGBA,minFilter:g,magFilter:gl.LINEAR}));a.copyTo(this._temp_texture);this.properties.generate_mipmaps&&(this._temp_texture.bind(0),gl.generateMipmap(this._temp_texture.texture_type),this._temp_texture.unbind(0))}this.setOutputData(0,this._temp_texture)}},F.registerNodeType("texture/copy",s),D.title="Downsample",D.desc="Downsample Texture",D.widgets_info={iterations:{type:"number",step:1,precision:0,min:0},precision:{widget:"combo",values:c.MODE_VALUES}}, D.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)&&a&&a.texture_type===GL.TEXTURE_2D)if(1>this.properties.iterations)this.setOutputData(0,a);else{var b=D._shader;b||(D._shader=b=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,D.pixel_shader));var d=a.width|0,g=a.height|0,f=a.type;this.properties.precision===c.LOW?f=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(f=gl.HIGH_PRECISION_FORMAT);var e=this.properties.iterations||1,l=a,h= null,k=[],a={type:f,format:a.format},f=vec2.create(),q={u_offset:f};this._texture&&GL.Texture.releaseTemporary(this._texture);for(var p=0;p>1||0;g=g>>1||0;h=GL.Texture.getTemporary(d,g,a);k.push(h);l.setParameter(GL.TEXTURE_MAG_FILTER,GL.NEAREST);l.copyTo(h,b,q);if(1==d&&1==g)break;l=h}this._texture=k.pop();for(p=0;p>=2,c++,1!=d););a.copyTo(this._textures_chain[0]);for(var c=1;c<=this._textures_chain.length;++c)a=this._textures_chain[c];var g=e._shader,f=this._uniforms;f.u_mipmap_offset=this.properties.mipmap_offset;gl.disable(gl.DEPTH_TEST); -gl.disable(gl.BLEND);this._temp_texture.drawTo(function(){a.toViewport(g,f)})}},e.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform mat4 u_samples_a;\n\t\tuniform mat4 u_samples_b;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_mipmap_offset;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t//random average\n\t\t\tfor(int i = 0; i < 4; ++i)\n\t\t\t\tfor(int j = 0; j < 4; ++j)\n\t\t\t\t{\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t}\n\t\t gl_FragColor = color * 0.03125;\n\t\t}\n\t\t", +F.registerNodeType("texture/average",A),h.widgets_info={mode:{widget:"combo",values:["min","max","avg"]}},h.title="MinMax",h.desc="Compute the scene min max",h.prototype.onExecute=function(){this.properties.use_previous_frame||this.update();this.setOutputData(0,this._temp_texture);this.setOutputData(1,this._luminance)},h.prototype.onPreRenderExecute=function(){this.update()},h.prototype.update=function(){var a=this.getInputData(0);if(a&&(this.isOutputConnected(0)||this.isOutputConnected(1))){h._shader|| +(h._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,h.pixel_shader));var b=gl.UNSIGNED_BYTE;a.type!=b&&(b=gl.FLOAT);var d=512;if(!this._textures_chain.length||this._textures_chain[0].type!=b)for(;c&&(this._textures_chain[c]=new GL.Texture(d,d,{type:b,format:gl.RGBA,filter:gl.NEAREST}),d>>=2,c++,1!=d););a.copyTo(this._textures_chain[0]);for(var c=1;c<=this._textures_chain.length;++c)a=this._textures_chain[c];var g=h._shader,f=this._uniforms;f.u_mipmap_offset=this.properties.mipmap_offset;gl.disable(gl.DEPTH_TEST); +gl.disable(gl.BLEND);this._temp_texture.drawTo(function(){a.toViewport(g,f)})}},h.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform mat4 u_samples_a;\n\t\tuniform mat4 u_samples_b;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_mipmap_offset;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t//random average\n\t\t\tfor(int i = 0; i < 4; ++i)\n\t\t\t\tfor(int j = 0; j < 4; ++j)\n\t\t\t\t{\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t}\n\t\t gl_FragColor = color * 0.03125;\n\t\t}\n\t\t", y.title="Smooth",y.desc="Smooth texture over time",y.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){y._shader||(y._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,y.pixel_shader));var b=this._temp_texture;b&&b.type==a.type&&b.width==a.width&&b.height==a.height||(b={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(a.width,a.height,b),this._temp_texture2=new GL.Texture(a.width,a.height,b),a.copyTo(this._temp_texture2));var b= this._temp_texture,d=this._temp_texture2,c=y._shader,g=this._uniforms;g.u_factor=1-this.getInputOrProperty("factor");gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);b.drawTo(function(){d.bind(1);a.toViewport(c,g)});this.setOutputData(0,b);this._temp_texture=d;this._temp_texture2=b}},y.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_factor;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tgl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\t\t}\n\t\t", -G.registerNodeType("texture/temporal_smooth",y),B.title="Lineal Avg Smooth",B.desc="Smooth texture linearly over time",B["@samples"]={type:"number",min:1,max:64,step:1,precision:1},B.prototype.getPreviewTexture=function(){return this._temp_texture2},B.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){B._shader||(B._shader_copy=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,B.pixel_shader_copy),B._shader_avg=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,B.pixel_shader_avg)); +F.registerNodeType("texture/temporal_smooth",y),B.title="Lineal Avg Smooth",B.desc="Smooth texture linearly over time",B["@samples"]={type:"number",min:1,max:64,step:1,precision:1},B.prototype.getPreviewTexture=function(){return this._temp_texture2},B.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){B._shader||(B._shader_copy=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,B.pixel_shader_copy),B._shader_avg=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,B.pixel_shader_avg)); var b=Math.clamp(this.properties.samples,0,64),d=this.frame,c=this.properties.frames_interval;if(0==c||0==d%c){d=this._temp_texture;d&&d.type==a.type&&d.width==b||(d={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(b,1,d),this._temp_texture2=new GL.Texture(b,1,d),this._temp_texture_out=new GL.Texture(1,1,d));var g=this._temp_texture,f=this._temp_texture2,e=B._shader_copy,l=B._shader_avg,h=this._uniforms;h.u_samples=b;h.u_isamples=1/b;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST); g.drawTo(function(){f.bind(1);a.toViewport(e,h)});this._temp_texture_out.drawTo(function(){g.toViewport(l,h)});this.setOutputData(0,this._temp_texture_out);this._temp_texture=f;this._temp_texture2=g}else this.setOutputData(0,this._temp_texture_out);this.setOutputData(1,this._temp_texture2);this.frame++}},B.pixel_shader_copy="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tif( v_coord.x <= u_isamples )\n\t\t\t\tgl_FragColor = texture2D( u_texture, vec2(0.5) );\n\t\t\telse\n\t\t\t\tgl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\t\t}\n\t\t", -B.pixel_shader_avg="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform int u_samples;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\tfor(int i = 0; i < 64; ++i)\n\t\t\t{\n\t\t\t\tcolor += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\t\t\t\tif(i == (u_samples - 1))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tgl_FragColor = color * u_isamples;\n\t\t}\n\t\t",G.registerNodeType("texture/linear_avg_smooth", +B.pixel_shader_avg="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform int u_samples;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\tfor(int i = 0; i < 64; ++i)\n\t\t\t{\n\t\t\t\tcolor += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\t\t\t\tif(i == (u_samples - 1))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tgl_FragColor = color * u_isamples;\n\t\t}\n\t\t",F.registerNodeType("texture/linear_avg_smooth", B),u.title="Image to Texture",u.desc="Uploads an image to the GPU",u.prototype.onExecute=function(){var a=this.getInputData(0);if(a){var b=a.videoWidth||a.width,d=a.videoHeight||a.height;if(a.gltexture)this.setOutputData(0,a.gltexture);else{var c=this._temp_texture;c&&c.width==b&&c.height==d||(this._temp_texture=new GL.Texture(b,d,{format:gl.RGBA,filter:gl.LINEAR}));try{this._temp_texture.uploadImage(a)}catch(g){console.error("image comes from an unsafe location, cannot be uploaded to webgl: "+g); -return}this.setOutputData(0,this._temp_texture)}}},G.registerNodeType("texture/imageToTexture",u),F.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},F.title="LUT",F.desc="Apply LUT to Texture",F.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture)); -if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.bindTexture(gl.TEXTURE_2D,null);var d=this.properties.intensity;this.isInputConnected(2)&&(this.properties.intensity=d=this.getInputData(2));this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);this._tex.drawTo(function(){b.bind(1);a.toViewport(F._shader,{u_texture:0,u_textureB:1, -u_amount:d})});this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}},F.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_amount;\n\t\t\n\t\tvoid main() {\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\t\t\t mediump vec2 quad1;\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\t\t\t mediump vec2 quad2;\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\t\t\t highp vec2 texPos1;\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t highp vec2 texPos2;\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\t\t}\n\t\t", -G.registerNodeType("texture/LUT",F),q.title="Texture to Channels",q.desc="Split texture channels",q.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this._channels||(this._channels=Array(4));for(var b=gl.RGB,d=0,c=0;4>c;c++)this.isOutputConnected(c)?(this._channels[c]&&this._channels[c].width==a.width&&this._channels[c].height==a.height&&this._channels[c].type==a.type&&this._channels[c].format==b||(this._channels[c]=new GL.Texture(a.width,a.height,{type:a.type,format:b,filter:gl.LINEAR})), -d++):this._channels[c]=null;if(d){gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);for(var g=Mesh.getScreenQuad(),f=q._shader,e=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],c=0;4>c;c++)this._channels[c]&&(this._channels[c].drawTo(function(){a.bind(0);f.uniforms({u_texture:0,u_mask:e[c]}).draw(g)}),this.setOutputData(c,this._channels[c]))}}},q.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec4 u_mask;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\t\t}\n\t\t", -G.registerNodeType("texture/textureChannels",q),p.title="Channels to Texture",p.desc="Split texture channels",p.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},p.prototype.onExecute=function(){var a=c.getWhiteTexture(),b=this.getInputData(0)||a,d=this.getInputData(1)||a,g=this.getInputData(2)||a,f=this.getInputData(3)||a;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad();p._shader||(p._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,p.pixel_shader));var l=p._shader, -a=Math.max(b.width,d.width,g.width,f.width),h=Math.max(b.height,d.height,g.height,f.height),k=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._texture&&this._texture.width==a&&this._texture.height==h&&this._texture.type==k||(this._texture=new GL.Texture(a,h,{type:k,format:gl.RGBA,filter:gl.LINEAR}));a=this._color;a[0]=this.properties.R;a[1]=this.properties.G;a[2]=this.properties.B;a[3]=this.properties.A;var q=this._uniforms;this._texture.drawTo(function(){b.bind(0); -d.bind(1);g.bind(2);f.bind(3);l.uniforms(q).draw(e)});this.setOutputData(0,this._texture)},p.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureR;\n\t\tuniform sampler2D u_textureG;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform vec4 u_color;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = u_color * vec4( \t\t\t\t\ttexture2D(u_textureR, v_coord).r,\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\t\t}\n\t\t", -G.registerNodeType("texture/channelsTexture",p),l.title="Color",l.desc="Generates a 1x1 texture with a constant color",l.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},l.prototype.onDrawBackground=function(a){var b=this.properties.color;a.fillStyle="rgb("+Math.floor(255*Math.clamp(b[0],0,1))+","+Math.floor(255*Math.clamp(b[1],0,1))+","+Math.floor(255*Math.clamp(b[2],0,1))+")";this.flags.collapsed?this.boxcolor=a.fillStyle:a.fillRect(0,0,this.size[0],this.size[1])},l.prototype.onExecute= +return}this.setOutputData(0,this._temp_texture)}}},F.registerNodeType("texture/imageToTexture",u),G.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},G.title="LUT",G.desc="Apply LUT to Texture",G.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture)); +if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.bindTexture(gl.TEXTURE_2D,null);var d=this.properties.intensity;this.isInputConnected(2)&&(this.properties.intensity=d=this.getInputData(2));this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);this._tex.drawTo(function(){b.bind(1);a.toViewport(G._shader,{u_texture:0,u_textureB:1, +u_amount:d})});this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}},G.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_amount;\n\t\t\n\t\tvoid main() {\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\t\t\t mediump vec2 quad1;\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\t\t\t mediump vec2 quad2;\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\t\t\t highp vec2 texPos1;\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t highp vec2 texPos2;\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\t\t}\n\t\t", +F.registerNodeType("texture/LUT",G),q.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},q.title="Encode",q.desc="Apply a texture atlas to encode a texture",q.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture));if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D, +gl.TEXTURE_MAG_FILTER,this.properties.filter?gl.LINEAR:gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,this.properties.filter?gl.LINEAR:gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.bindTexture(gl.TEXTURE_2D,null);var d=this._uniforms;d.u_row_simbols=Math.floor(this.properties.num_row_symbols);d.u_symbol_size=this.properties.symbol_size;d.u_brightness=this.properties.brightness; +d.u_invert=this.properties.invert?1:0;d.u_colorize=this.properties.colorize?1:0;this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);d.u_res[0]=this._tex.width;d.u_res[1]=this._tex.height;this._tex.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.NEAREST);this._tex.drawTo(function(){b.bind(1);a.toViewport(q._shader,d)});this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}},q.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_row_simbols;\n\t\tuniform float u_symbol_size;\n\t\tuniform float u_brightness;\n\t\tuniform float u_invert;\n\t\tuniform float u_colorize;\n\t\tuniform vec2 u_res;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 total_symbols = u_res / u_symbol_size;\n\t\t\tvec2 uv = floor(v_coord * total_symbols) / total_symbols; //pixelate \n\t\t\tvec2 local_uv = mod(v_coord * u_res, u_symbol_size) / u_symbol_size;\n\t\t\tlowp vec4 textureColor = texture2D(u_texture, uv );\n\t\t\tfloat lum = clamp(u_brightness * (textureColor.x + textureColor.y + textureColor.z)/3.0,0.0,1.0);\n\t\t\tif( u_invert == 1.0 ) lum = 1.0 - lum;\n\t\t\tfloat index = floor( lum * (u_row_simbols * u_row_simbols - 1.0));\n\t\t\tfloat col = mod( index, u_row_simbols );\n\t\t\tfloat row = u_row_simbols - floor( index / u_row_simbols ) - 1.0;\n\t\t\tvec2 simbol_uv = ( vec2( col, row ) + local_uv ) / u_row_simbols;\n\t\t\tvec4 color = texture2D( u_textureB, simbol_uv );\n\t\t\tif(u_colorize == 1.0)\n\t\t\t\tcolor *= textureColor;\n\t\t\tgl_FragColor = color;\n\t\t}\n\t\t", +F.registerNodeType("texture/encode",q),p.title="Texture to Channels",p.desc="Split texture channels",p.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this._channels||(this._channels=Array(4));for(var b=gl.RGB,d=0,c=0;4>c;c++)this.isOutputConnected(c)?(this._channels[c]&&this._channels[c].width==a.width&&this._channels[c].height==a.height&&this._channels[c].type==a.type&&this._channels[c].format==b||(this._channels[c]=new GL.Texture(a.width,a.height,{type:a.type,format:b,filter:gl.LINEAR})), +d++):this._channels[c]=null;if(d){gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);for(var g=Mesh.getScreenQuad(),f=p._shader,e=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],c=0;4>c;c++)this._channels[c]&&(this._channels[c].drawTo(function(){a.bind(0);f.uniforms({u_texture:0,u_mask:e[c]}).draw(g)}),this.setOutputData(c,this._channels[c]))}}},p.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec4 u_mask;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\t\t}\n\t\t", +F.registerNodeType("texture/textureChannels",p),l.title="Channels to Texture",l.desc="Split texture channels",l.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},l.prototype.onExecute=function(){var a=c.getWhiteTexture(),b=this.getInputData(0)||a,d=this.getInputData(1)||a,g=this.getInputData(2)||a,f=this.getInputData(3)||a;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad();l._shader||(l._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,l.pixel_shader));var h=l._shader, +a=Math.max(b.width,d.width,g.width,f.width),k=Math.max(b.height,d.height,g.height,f.height),q=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._texture&&this._texture.width==a&&this._texture.height==k&&this._texture.type==q||(this._texture=new GL.Texture(a,k,{type:q,format:gl.RGBA,filter:gl.LINEAR}));a=this._color;a[0]=this.properties.R;a[1]=this.properties.G;a[2]=this.properties.B;a[3]=this.properties.A;var p=this._uniforms;this._texture.drawTo(function(){b.bind(0); +d.bind(1);g.bind(2);f.bind(3);h.uniforms(p).draw(e)});this.setOutputData(0,this._texture)},l.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureR;\n\t\tuniform sampler2D u_textureG;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform vec4 u_color;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = u_color * vec4( \t\t\t\t\ttexture2D(u_textureR, v_coord).r,\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\t\t}\n\t\t", +F.registerNodeType("texture/channelsTexture",l),a.title="Color",a.desc="Generates a 1x1 texture with a constant color",a.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},a.prototype.onDrawBackground=function(a){var b=this.properties.color;a.fillStyle="rgb("+Math.floor(255*Math.clamp(b[0],0,1))+","+Math.floor(255*Math.clamp(b[1],0,1))+","+Math.floor(255*Math.clamp(b[2],0,1))+")";this.flags.collapsed?this.boxcolor=a.fillStyle:a.fillRect(0,0,this.size[0],this.size[1])},a.prototype.onExecute= function(){var a=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._tex&&this._tex.type==a||(this._tex=new GL.Texture(1,1,{format:gl.RGBA,type:a,minFilter:gl.NEAREST}));a=this.properties.color;if(this.inputs)for(var b=0;ba.width?d: -a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad(),l=null,h=this._uniforms;g?(l=b._shader_tex,l||(l=b._shader_tex=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader,{MIX_TEX:""}))):(l=b._shader_factor,l||(l=b._shader_factor=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader)),f=null==f?this.properties.factor:f,h.u_mix.set([f,f,f,f]));var k=this.properties.invert;this._tex.drawTo(function(){a.bind(k?1:0);d.bind(k?0:1);g&&g.bind(2); -l.uniforms(h).draw(e)});this.setOutputData(0,this._tex)}}},b.prototype.onGetInputs=function(){return[["factor","number"]]},b.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform sampler2D u_textureB;\n\t\t#ifdef MIX_TEX\n\t\t\tuniform sampler2D u_textureMix;\n\t\t#else\n\t\t\tuniform vec4 u_mix;\n\t\t#endif\n\t\t\n\t\tvoid main() {\n\t\t\t#ifdef MIX_TEX\n\t\t\t vec4 f = texture2D(u_textureMix, v_coord);\n\t\t\t#else\n\t\t\t vec4 f = u_mix;\n\t\t\t#endif\n\t\t gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\t\t}\n\t\t", -G.registerNodeType("texture/mix",b),d.title="Edges",d.desc="Detects edges",d.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},d.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else if(a){this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var b=Mesh.getScreenQuad(),g=d._shader,f=this.properties.invert,e=this.properties.factor, -l=this.properties.threshold?1:0;this._tex.drawTo(function(){a.bind(0);g.uniforms({u_texture:0,u_isize:[1/a.width,1/a.height],u_factor:e,u_threshold:l,u_invert:f?1:0}).draw(b)});this.setOutputData(0,this._tex)}}},d.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_isize;\n\t\tuniform int u_invert;\n\t\tuniform float u_factor;\n\t\tuniform float u_threshold;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 center = texture2D(u_texture, v_coord);\n\t\t\tvec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\t\t\tvec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\t\t\tvec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\t\t\tvec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\t\t\tvec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\t\t\tdiff *= u_factor;\n\t\t\tif(u_invert == 1)\n\t\t\t\tdiff.xyz = vec3(1.0) - diff.xyz;\n\t\t\tif( u_threshold == 0.0 )\n\t\t\t\tgl_FragColor = vec4( diff.xyz, center.a );\n\t\t\telse\n\t\t\t\tgl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\t\t}\n\t\t", -G.registerNodeType("texture/edges",d),g.title="Depth Range",g.desc="Generates a texture with a depth range",g.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a){var b=gl.UNSIGNED_BYTE;this.properties.high_precision&&(b=gl.half_float_ext?gl.HALF_FLOAT_OES:gl.FLOAT);this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&&this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGBA, -filter:gl.LINEAR}));var d=this._uniforms,b=this.properties.distance;this.isInputConnected(1)&&(b=this.getInputData(1),this.properties.distance=b);var c=this.properties.range;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.range=c);d.u_distance=b;d.u_range=c;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var f=Mesh.getScreenQuad();g._shader||(g._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,g.pixel_shader),g._shader_onlydepth=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,g.pixel_shader, -{ONLY_DEPTH:""}));var e=this.properties.only_depth?g._shader_onlydepth:g._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1,1E3];d.u_camera_planes=b;this._temp_texture.drawTo(function(){a.bind(0);e.uniforms(d).draw(f)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},g.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform float u_distance;\n\t\tuniform float u_range;\n\t\t\n\t\tfloat LinearDepth()\n\t\t{\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\t\t\tdepth = depth * 2.0 - 1.0;\n\t\t\treturn zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t\tfloat depth = LinearDepth();\n\t\t\t#ifdef ONLY_DEPTH\n\t\t\t gl_FragColor = vec4(depth);\n\t\t\t#else\n\t\t\t\tfloat diff = abs(depth * u_camera_planes.y - u_distance);\n\t\t\t\tfloat dof = 1.0;\n\t\t\t\tif(diff <= u_range)\n\t\t\t\t\tdof = diff / u_range;\n\t\t\t gl_FragColor = vec4(dof);\n\t\t\t#endif\n\t\t}\n\t\t", -G.registerNodeType("texture/depth_range",g),f.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},f.title="Linear Depth",f.desc="Creates a color texture with linear depth",f.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a&&(a.format==gl.DEPTH_COMPONENT||a.format==gl.DEPTH_STENCIL)){var b=this.properties.precision==c.HIGH?gl.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&& -this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGB,filter:gl.LINEAR}));var d=this._uniforms;d.u_invert=this.properties.invert?1:0;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var g=Mesh.getScreenQuad();f._shader||(f._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,f.pixel_shader));var e=f._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1, -1E3];d.u_camera_planes=b;d.u_ires.set([0,0]);this._temp_texture.drawTo(function(){a.bind(0);e.uniforms(d).draw(g)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},f.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform int u_invert;\n\t\tuniform vec2 u_ires;\n\t\t\n\t\tvoid main() {\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord + u_ires*0.5).x * 2.0 - 1.0;\n\t\t\tfloat f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t\tif( u_invert == 1 )\n\t\t\t\tf = 1.0 - f;\n\t\t\tgl_FragColor = vec4(vec3(f),1.0);\n\t\t}\n\t\t", -G.registerNodeType("texture/linear_depth",f),r.title="Blur",r.desc="Blur a texture",r.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},r.max_iterations=20,r.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._final_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(b=this._final_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));var d=this.properties.iterations;this.isInputConnected(1)&& -(d=this.getInputData(1),this.properties.iterations=d);d=Math.min(Math.floor(d),r.max_iterations);if(0==d)this.setOutputData(0,a);else{var c=this.properties.intensity;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.intensity=c);var g=G.camera_aspect;g||void 0===window.gl||(g=gl.canvas.height/gl.canvas.width);g||(g=1);var g=this.properties.preserve_aspect?g:1,f=this.properties.scale||[1,1];a.applyBlur(g*f[0],f[1],c,b);for(a=1;aa.width?b: +a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad(),l=null,h=this._uniforms;g?(l=d._shader_tex,l||(l=d._shader_tex=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,d.pixel_shader,{MIX_TEX:""}))):(l=d._shader_factor,l||(l=d._shader_factor=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,d.pixel_shader)),f=null==f?this.properties.factor:f,h.u_mix.set([f,f,f,f]));var k=this.properties.invert;this._tex.drawTo(function(){a.bind(k?1:0);b.bind(k?0:1);g&&g.bind(2); +l.uniforms(h).draw(e)});this.setOutputData(0,this._tex)}}},d.prototype.onGetInputs=function(){return[["factor","number"]]},d.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform sampler2D u_textureB;\n\t\t#ifdef MIX_TEX\n\t\t\tuniform sampler2D u_textureMix;\n\t\t#else\n\t\t\tuniform vec4 u_mix;\n\t\t#endif\n\t\t\n\t\tvoid main() {\n\t\t\t#ifdef MIX_TEX\n\t\t\t vec4 f = texture2D(u_textureMix, v_coord);\n\t\t\t#else\n\t\t\t vec4 f = u_mix;\n\t\t\t#endif\n\t\t gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\t\t}\n\t\t", +F.registerNodeType("texture/mix",d),g.title="Edges",g.desc="Detects edges",g.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},g.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else if(a){this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var b=Mesh.getScreenQuad(),d=g._shader,f=this.properties.invert,e=this.properties.factor, +l=this.properties.threshold?1:0;this._tex.drawTo(function(){a.bind(0);d.uniforms({u_texture:0,u_isize:[1/a.width,1/a.height],u_factor:e,u_threshold:l,u_invert:f?1:0}).draw(b)});this.setOutputData(0,this._tex)}}},g.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_isize;\n\t\tuniform int u_invert;\n\t\tuniform float u_factor;\n\t\tuniform float u_threshold;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 center = texture2D(u_texture, v_coord);\n\t\t\tvec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\t\t\tvec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\t\t\tvec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\t\t\tvec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\t\t\tvec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\t\t\tdiff *= u_factor;\n\t\t\tif(u_invert == 1)\n\t\t\t\tdiff.xyz = vec3(1.0) - diff.xyz;\n\t\t\tif( u_threshold == 0.0 )\n\t\t\t\tgl_FragColor = vec4( diff.xyz, center.a );\n\t\t\telse\n\t\t\t\tgl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\t\t}\n\t\t", +F.registerNodeType("texture/edges",g),f.title="Depth Range",f.desc="Generates a texture with a depth range",f.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a){var b=gl.UNSIGNED_BYTE;this.properties.high_precision&&(b=gl.half_float_ext?gl.HALF_FLOAT_OES:gl.FLOAT);this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&&this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGBA, +filter:gl.LINEAR}));var d=this._uniforms,b=this.properties.distance;this.isInputConnected(1)&&(b=this.getInputData(1),this.properties.distance=b);var c=this.properties.range;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.range=c);d.u_distance=b;d.u_range=c;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var g=Mesh.getScreenQuad();f._shader||(f._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,f.pixel_shader),f._shader_onlydepth=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,f.pixel_shader, +{ONLY_DEPTH:""}));var e=this.properties.only_depth?f._shader_onlydepth:f._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1,1E3];d.u_camera_planes=b;this._temp_texture.drawTo(function(){a.bind(0);e.uniforms(d).draw(g)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},f.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform float u_distance;\n\t\tuniform float u_range;\n\t\t\n\t\tfloat LinearDepth()\n\t\t{\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\t\t\tdepth = depth * 2.0 - 1.0;\n\t\t\treturn zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t\tfloat depth = LinearDepth();\n\t\t\t#ifdef ONLY_DEPTH\n\t\t\t gl_FragColor = vec4(depth);\n\t\t\t#else\n\t\t\t\tfloat diff = abs(depth * u_camera_planes.y - u_distance);\n\t\t\t\tfloat dof = 1.0;\n\t\t\t\tif(diff <= u_range)\n\t\t\t\t\tdof = diff / u_range;\n\t\t\t gl_FragColor = vec4(dof);\n\t\t\t#endif\n\t\t}\n\t\t", +F.registerNodeType("texture/depth_range",f),r.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},r.title="Linear Depth",r.desc="Creates a color texture with linear depth",r.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a&&(a.format==gl.DEPTH_COMPONENT||a.format==gl.DEPTH_STENCIL)){var b=this.properties.precision==c.HIGH?gl.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&& +this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGB,filter:gl.LINEAR}));var d=this._uniforms;d.u_invert=this.properties.invert?1:0;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var g=Mesh.getScreenQuad();r._shader||(r._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,r.pixel_shader));var f=r._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1, +1E3];d.u_camera_planes=b;d.u_ires.set([0,0]);this._temp_texture.drawTo(function(){a.bind(0);f.uniforms(d).draw(g)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},r.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform int u_invert;\n\t\tuniform vec2 u_ires;\n\t\t\n\t\tvoid main() {\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord + u_ires*0.5).x * 2.0 - 1.0;\n\t\t\tfloat f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t\tif( u_invert == 1 )\n\t\t\t\tf = 1.0 - f;\n\t\t\tgl_FragColor = vec4(vec3(f),1.0);\n\t\t}\n\t\t", +F.registerNodeType("texture/linear_depth",r),x.title="Blur",x.desc="Blur a texture",x.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},x.max_iterations=20,x.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._final_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(b=this._final_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));var d=this.properties.iterations;this.isInputConnected(1)&& +(d=this.getInputData(1),this.properties.iterations=d);d=Math.min(Math.floor(d),x.max_iterations);if(0==d)this.setOutputData(0,a);else{var c=this.properties.intensity;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.intensity=c);var g=F.camera_aspect;g||void 0===window.gl||(g=gl.canvas.height/gl.canvas.width);g||(g=1);var g=this.properties.preserve_aspect?g:1,f=this.properties.scale||[1,1];a.applyBlur(g*f[0],f[1],c,b);for(a=1;a>=1;1<(d|0)&&(d>>=1);if(2>b)break;k=l[m]=GL.Texture.getTemporary(b,d,g);r[0]=1/q.width;r[1]=1/q.height;q.blit(k,h.uniforms(e));q=k}this.isOutputConnected(2)&&(b=this._average_texture,b&&b.type==a.type&&b.format==a.format||(b=this._average_texture=new GL.Texture(1,1,{type:a.type,format:a.format,filter:gl.LINEAR})),r[0]=1/q.width,r[1]=1/q.height,e.u_intensity=n,e.u_delta=1,q.blit(b,h.uniforms(e)),this.setOutputData(2,b));gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE);e.u_intensity= this.getInputOrProperty("persistence");e.u_delta=0.5;for(m-=2;0<=m;m--)k=l[m],l[m]=null,r[0]=1/q.width,r[1]=1/q.height,q.blit(k,h.uniforms(e)),GL.Texture.releaseTemporary(q),q=k;gl.disable(gl.BLEND);this.isOutputConnected(1)&&(l=this._glow_texture,l&&l.width==a.width&&l.height==a.height&&l.type==f&&l.format==a.format||(l=this._glow_texture=new GL.Texture(a.width,a.height,{type:f,format:a.format,filter:gl.LINEAR})),q.blit(l),this.setOutputData(1,l));if(this.isOutputConnected(0)){l=this._final_texture; -l&&l.width==a.width&&l.height==a.height&&l.type==f&&l.format==a.format||(l=this._final_texture=new GL.Texture(a.width,a.height,{type:f,format:a.format,filter:gl.LINEAR}));var v=this.getInputData(1),t=this.getInputOrProperty("dirt_factor");e.u_intensity=n;h=v?s._dirt_final_shader:s._final_shader;h||(h=v?s._dirt_final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,s.final_pixel_shader,{USE_DIRT:""}):s._final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,s.final_pixel_shader));l.drawTo(function(){a.bind(0); -q.bind(1);v&&(h.setUniform("u_dirt_factor",t),h.setUniform("u_dirt_texture",v.bind(2)));h.toViewport(e)});this.setOutputData(0,l)}GL.Texture.releaseTemporary(q)}},s.cut_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform float u_threshold;\n\tvoid main() {\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\t}",s.scale_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\t}", -s.final_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform sampler2D u_glow_texture;\n\t#ifdef USE_DIRT\n\t\tuniform sampler2D u_dirt_texture;\n\t#endif\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\tuniform float u_dirt_factor;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tvec4 glow = sampleBox( v_coord );\n\t\t#ifdef USE_DIRT\n\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\t\t#endif\n\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\t}", -G.registerNodeType("texture/glow",s),J.title="Kuwahara Filter",J.desc="Filters a texture giving an artistic oil canvas painting",J.max_radius=10,J._shaders=[],J.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));b=this.properties.radius;b=Math.min(Math.floor(b),J.max_radius);if(0== -b)this.setOutputData(0,a);else{var d=this.properties.intensity,c=G.camera_aspect;c||void 0===window.gl||(c=gl.canvas.height/gl.canvas.width);c||(c=1);c=this.properties.preserve_aspect?c:1;J._shaders[b]||(J._shaders[b]=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,J.pixel_shader,{RADIUS:b.toFixed(0)}));var g=J._shaders[b],f=GL.Mesh.getScreenQuad();a.bind(0);this._temp_texture.drawTo(function(){g.uniforms({u_texture:0,u_intensity:d,u_resolution:[a.width,a.height],u_iResolution:[1/a.width,1/a.height]}).draw(f)}); +l&&l.width==a.width&&l.height==a.height&&l.type==f&&l.format==a.format||(l=this._final_texture=new GL.Texture(a.width,a.height,{type:f,format:a.format,filter:gl.LINEAR}));var x=this.getInputData(1),t=this.getInputOrProperty("dirt_factor");e.u_intensity=n;h=x?I._dirt_final_shader:I._final_shader;h||(h=x?I._dirt_final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,I.final_pixel_shader,{USE_DIRT:""}):I._final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,I.final_pixel_shader));l.drawTo(function(){a.bind(0); +q.bind(1);x&&(h.setUniform("u_dirt_factor",t),h.setUniform("u_dirt_texture",x.bind(2)));h.toViewport(e)});this.setOutputData(0,l)}GL.Texture.releaseTemporary(q)}},I.cut_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform float u_threshold;\n\tvoid main() {\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\t}",I.scale_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\t}", +I.final_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform sampler2D u_glow_texture;\n\t#ifdef USE_DIRT\n\t\tuniform sampler2D u_dirt_texture;\n\t#endif\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\tuniform float u_dirt_factor;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tvec4 glow = sampleBox( v_coord );\n\t\t#ifdef USE_DIRT\n\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\t\t#endif\n\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\t}", +F.registerNodeType("texture/glow",I),J.title="Kuwahara Filter",J.desc="Filters a texture giving an artistic oil canvas painting",J.max_radius=10,J._shaders=[],J.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));b=this.properties.radius;b=Math.min(Math.floor(b),J.max_radius);if(0== +b)this.setOutputData(0,a);else{var d=this.properties.intensity,c=F.camera_aspect;c||void 0===window.gl||(c=gl.canvas.height/gl.canvas.width);c||(c=1);c=this.properties.preserve_aspect?c:1;J._shaders[b]||(J._shaders[b]=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,J.pixel_shader,{RADIUS:b.toFixed(0)}));var g=J._shaders[b],f=GL.Mesh.getScreenQuad();a.bind(0);this._temp_texture.drawTo(function(){g.uniforms({u_texture:0,u_intensity:d,u_resolution:[a.width,a.height],u_iResolution:[1/a.width,1/a.height]}).draw(f)}); this.setOutputData(0,this._temp_texture)}}},J.pixel_shader="\nprecision highp float;\nvarying vec2 v_coord;\nuniform sampler2D u_texture;\nuniform float u_intensity;\nuniform vec2 u_resolution;\nuniform vec2 u_iResolution;\n#ifndef RADIUS\n\t#define RADIUS 7\n#endif\nvoid main() {\n\n\tconst int radius = RADIUS;\n\tvec2 fragCoord = v_coord;\n\tvec2 src_size = u_iResolution;\n\tvec2 uv = v_coord;\n\tfloat n = float((radius + 1) * (radius + 1));\n\tint i;\n\tint j;\n\tvec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\tvec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\tvec3 c;\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm0 += c;\n\t\t\ts0 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm1 += c;\n\t\t\ts1 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm2 += c;\n\t\t\ts2 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm3 += c;\n\t\t\ts3 += c * c;\n\t\t}\n\t}\n\t\n\tfloat min_sigma2 = 1e+2;\n\tm0 /= n;\n\ts0 = abs(s0 / n - m0 * m0);\n\t\n\tfloat sigma2 = s0.r + s0.g + s0.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m0, 1.0);\n\t}\n\t\n\tm1 /= n;\n\ts1 = abs(s1 / n - m1 * m1);\n\t\n\tsigma2 = s1.r + s1.g + s1.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m1, 1.0);\n\t}\n\t\n\tm2 /= n;\n\ts2 = abs(s2 / n - m2 * m2);\n\t\n\tsigma2 = s2.r + s2.g + s2.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m2, 1.0);\n\t}\n\t\n\tm3 /= n;\n\ts3 = abs(s3 / n - m3 * m3);\n\t\n\tsigma2 = s3.r + s3.g + s3.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m3, 1.0);\n\t}\n}\n", -G.registerNodeType("texture/kuwahara",J),K.title="XDoG Filter",K.desc="Filters a texture giving an artistic ink style",K.max_radius=10,K._shaders=[],K.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));K._xdog_shader||(K._xdog_shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,K.xdog_pixel_shader)); +F.registerNodeType("texture/kuwahara",J),K.title="XDoG Filter",K.desc="Filters a texture giving an artistic ink style",K.max_radius=10,K._shaders=[],K.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));K._xdog_shader||(K._xdog_shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,K.xdog_pixel_shader)); var d=K._xdog_shader,c=GL.Mesh.getScreenQuad(),g=this.properties.sigma,f=this.properties.k,e=this.properties.p,l=this.properties.epsilon,h=this.properties.phi;a.bind(0);this._temp_texture.drawTo(function(){d.uniforms({src:0,sigma:g,k:f,p:e,epsilon:l,phi:h,cvsWidth:a.width,cvsHeight:a.height}).draw(c)});this.setOutputData(0,this._temp_texture)}},K.xdog_pixel_shader="\nprecision highp float;\nuniform sampler2D src;\n\nuniform float cvsHeight;\nuniform float cvsWidth;\n\nuniform float sigma;\nuniform float k;\nuniform float p;\nuniform float epsilon;\nuniform float phi;\nvarying vec2 v_coord;\n\nfloat cosh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat cosH = (tmp + 1.0 / tmp) / 2.0;\n\treturn cosH;\n}\n\nfloat tanh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\treturn tanH;\n}\n\nfloat sinh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat sinH = (tmp - 1.0 / tmp) / 2.0;\n\treturn sinH;\n}\n\nvoid main(void){\n\tvec3 destColor = vec3(0.0);\n\tfloat tFrag = 1.0 / cvsHeight;\n\tfloat sFrag = 1.0 / cvsWidth;\n\tvec2 Frag = vec2(sFrag,tFrag);\n\tvec2 uv = gl_FragCoord.st;\n\tfloat twoSigmaESquared = 2.0 * sigma * sigma;\n\tfloat twoSigmaRSquared = twoSigmaESquared * k * k;\n\tint halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\tconst int MAX_NUM_ITERATION = 99999;\n\tvec2 sum = vec2(0.0);\n\tvec2 norm = vec2(0.0);\n\n\tfor(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\t\tint i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\t\tint j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\t\tfloat d = length(vec2(i,j));\n\t\tvec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\t\t\t\t\t\t\texp( -d * d / twoSigmaRSquared ));\n\n\t\tvec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\t\tnorm += kernel;\n\t\tsum += kernel * L;\n\t}\n\n\tsum /= norm;\n\n\tfloat H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\tfloat edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\tdestColor = vec3(edge);\n\tgl_FragColor = vec4(destColor, 1.0);\n}", -G.registerNodeType("texture/xDoG",K),I.title="Webcam",I.desc="Webcam texture",I.is_webcam_open=!1,I.prototype.openStream=function(){function a(d){I.is_webcam_open=!1;console.log("Webcam rejected",d);b._webcam_stream=!1;b.boxcolor="red";b.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](a);var b=this}},I.prototype.closeStream=function(){if(this._webcam_stream){var a= -this._webcam_stream.getTracks();if(a.length)for(var b=0;b=this.size[1]||!this._video||(a.save(),a.webgl?this._video_texture&&a.drawImage(this._video_texture,0,0,this.size[0],this.size[1]):a.drawImage(this._video, -0,0,this.size[0],this.size[1]),a.restore())},I.prototype.onExecute=function(){null!=this._webcam_stream||this._waiting_confirmation||this.openStream();if(this._video&&this._video.videoWidth){var a=this._video.videoWidth,b=this._video.videoHeight,d=this._video_texture;d&&d.width==a&&d.height==b||(this._video_texture=new GL.Texture(a,b,{format:gl.RGB,filter:gl.LINEAR}));this._video_texture.uploadImage(this._video);this._video_texture.version=++this.version;this.properties.texture_name&&(c.getTexturesContainer()[this.properties.texture_name]= -this._video_texture);this.setOutputData(0,this._video_texture);for(a=1;a=this.size[1]||!this._video||(a.save(),a.webgl?this._video_texture&&a.drawImage(this._video_texture,0,0,this.size[0],this.size[1]):a.drawImage(this._video, +0,0,this.size[0],this.size[1]),a.restore())},E.prototype.onExecute=function(){null!=this._webcam_stream||this._waiting_confirmation||this.openStream();if(this._video&&this._video.videoWidth){var a=this._video.videoWidth,b=this._video.videoHeight,d=this._video_texture;d&&d.width==a&&d.height==b||(this._video_texture=new GL.Texture(a,b,{format:gl.RGB,filter:gl.LINEAR}));this._video_texture.uploadImage(this._video);this._video_texture.version=++this.version;this.properties.texture_name&&(c.getTexturesContainer()[this.properties.texture_name]= +this._video_texture);this.setOutputData(0,this._video_texture);for(a=1;aMath.abs(d))return c[1];d=(a-c[0])/d;return c[1]*(1-d)+g[1]*d}}return 0}},H.prototype.updateCurve=function(){for(var a=this._values,b=a.length/4,d=this.properties.split_channels,c=0;c=f;){e=0.5*(l+f)|0;c=a[e];if(c==d)break;if(f==l-1)return f;c=f;){e=0.5*(l+f)|0;c=a[e];if(c==d)break;if(f==l-1)return f;ca&&(a=1);this.points&&this.points.length==3*a||(this.points=new Float32Array(3*a));this.properties.generate_normals?this.normals&&this.normals.length==this.points.length||(this.normals=new Float32Array(this.points.length)):this.normals=null;var d=this._last_radius||this.properties.radius,c=this.properties.mode,f=this.getInputData(0);this._old_obj_version=f?f._version:null;this.points=m.generatePoints(d,a,c,this.points, @@ -576,62 +581,62 @@ this.normals,this.properties.regular,f);this.version++};m.generatePoints=functio c==m.OBJECT_UNIFORMLY?m.generateFromObject(f,e,k,h,!0):c==m.OBJECT_INSIDE?m.generateFromInsideObject(f,k,h):console.warn("wrong mode in LGraphPoints3D");return f};m.generateSphericalNormals=function(a,d){for(var c=new Float32Array(3),f=0;fe||wl&&lh))break}this.geometry.indices=this.indices=new Uint32Array(k)}this.indices&&this.indices.length?(this.geometry.indices=this.indices,this.setOutputData(0,this.geometry)):this.setOutputData(0,null)}};u.registerNodeType("geometry/connectPoints",t);"undefined"!=typeof GL&&(D.title="to geometry",D.desc="converts a mesh to geometry",D.prototype.onExecute= +n=0,s=d+3;se||vl&&lh))break}this.geometry.indices=this.indices=new Uint32Array(k)}this.indices&&this.indices.length?(this.geometry.indices=this.indices,this.setOutputData(0,this.geometry)):this.setOutputData(0,null)}};u.registerNodeType("geometry/connectPoints",s);"undefined"!=typeof GL&&(D.title="to geometry",D.desc="converts a mesh to geometry",D.prototype.onExecute= function(){var a=this.getInputData(0);if(a){if(a!=this.last_mesh){this.last_mesh=a;for(i in a.vertexBuffers)this.geometry[i]=a.vertexBuffers[i].data;a.indexBuffers.triangles&&(this.geometry.indices=a.indexBuffers.triangles.data);this.geometry._id=c();this.geometry._version=0}this.setOutputData(0,this.geometry);this.geometry&&this.setOutputData(1,this.geometry.vertices)}},u.registerNodeType("geometry/toGeometry",D),A.title="Geo to Mesh",A.prototype.updateMesh=function(a){this.mesh||(this.mesh=new GL.Mesh); for(var d in a)if("_"!=d[0]){var c=a[d],f=GL.Mesh.common_buffers[d];if(f||"indices"==d){var f=f?f.spacing:3,e=this.mesh.vertexBuffers[d];e&&e.data.length==c.length?(e.data.set(c),e.upload(GL.DYNAMIC_DRAW)):e=new GL.Buffer("indices"==d?GL.ELEMENT_ARRAY_BUFFER:GL.ARRAY_BUFFER,c,f,GL.DYNAMIC_DRAW);this.mesh.addBuffer(d,e)}}if(this.mesh.vertexBuffers.normals&&this.mesh.vertexBuffers.normals.data.length!=this.mesh.vertexBuffers.vertices.data.length){c=new Float32Array([0,1,0]);f=new Float32Array(this.mesh.vertexBuffers.vertices.data.length); -for(d=0;d=c.NOTEON||h<=c.NOTEOFF)this.channel=e&15};Object.defineProperty(c.prototype,"velocity",{get:function(){return this.cmd==c.NOTEON?this.data[2]:-1},set:function(c){this.data[2]=c},enumerable:!0});c.notes="A A# B C C# D D# E F F# G G#".split(" ");c.note_to_index={A:0,"A#":1,B:2,C:3,"C#":4,D:5,"D#":6,E:7,F:8,"F#":9,G:10,"G#":11};Object.defineProperty(c.prototype,"note",{get:function(){return this.cmd!= @@ -641,45 +646,45 @@ case "KEY PRESSURE":case "KEYPRESSURE":return c.KEYPRESSURE;case "CONTROLLER CHA l=(e-21)%12;0>l&&(l=12+l);return c.notes[l]+(h?"":a)};c.NoteStringToPitch=function(e){e=e.toUpperCase();var h=e[0],l=4;"#"==e[1]?(h+="#",2this.properties.max_value)return;this.trigger("on_midi",h)}};u.registerNodeType("midi/filter",k);C.title="MIDIEvent";C.desc="Create a MIDI Event";C.color="#243";C.prototype.onAction=function(e,h){"assign"==e?(this.properties.channel=h.channel,this.properties.cmd=h.cmd,this.properties.value1=h.data[1],this.properties.value2=h.data[2],h.cmd== c.NOTEON?this.gate=!0:h.cmd==c.NOTEOFF&&(this.gate=!1)):(h=this.midi_event,h.channel=this.properties.channel,this.properties.cmd&&this.properties.cmd.constructor===String?h.setCommandFromString(this.properties.cmd):h.cmd=this.properties.cmd,h.data[0]=h.cmd|h.channel,h.data[1]=Number(this.properties.value1),h.data[2]=Number(this.properties.value2),this.trigger("on_midi",h))};C.prototype.onExecute=function(){var e=this.properties;if(this.inputs)for(var h=0;hc;++c)this.valid_notes[c]=-1!=this.notes_pitches.indexOf(c);for(c=0;12>c;++c)if(this.valid_notes[c])this.offset_notes[c]=0;else for(var e=1;12>e;++e){if(this.valid_notes[(c-e)%12]){this.offset_notes[c]=-e;break}if(this.valid_notes[(c+e)%12]){this.offset_notes[c]=e;break}}};A.prototype.onAction=function(e,h){h&&h.constructor===c&&(h.data[0]==c.NOTEON||h.data[0]==c.NOTEOFF?(this.midi_event=new c,this.midi_event.setup(h.data),this.midi_event.data[1]+=this.offset_notes[c.note_to_index[h.note]], -this.trigger("out",this.midi_event)):this.trigger("out",h))};A.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&c!=this._current_scale&&this.processScale(c)};u.registerNodeType("midi/quantize",A);e.title="MIDI fromFile";e.desc="Plays a MIDI file";e.color="#243";e.prototype.onAction=function(c){"play"==c?this.play():"pause"==c&&(this._playing=!this._playing)};e.prototype.onPropertyChanged=function(c,e){"url"==c&&this.loadMIDIFile(e)};e.prototype.onExecute=function(){if(this._midi&& -this._playing){this._current_time+=this.graph.elapsed_time;for(var e=100*this._current_time,h=0;hb;b++)for(var d=0;dg+f||c[1]>d))return b}}return-1};B.prototype.onAction=function(e,h){if("reset"==e)for(var l=0;lc;++c)this.valid_notes[c]=-1!=this.notes_pitches.indexOf(c);for(c=0;12>c;++c)if(this.valid_notes[c])this.offset_notes[c]=0;else for(var e=1;12>e;++e){if(this.valid_notes[(c-e)%12]){this.offset_notes[c]=-e;break}if(this.valid_notes[(c+e)%12]){this.offset_notes[c]=e;break}}};A.prototype.onAction=function(e,h){h&&h.constructor===c&&(h.data[0]==c.NOTEON||h.data[0]==c.NOTEOFF?(this.midi_event=new c,this.midi_event.setup(h.data),this.midi_event.data[1]+=this.offset_notes[c.note_to_index[h.note]], +this.trigger("out",this.midi_event)):this.trigger("out",h))};A.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&c!=this._current_scale&&this.processScale(c)};u.registerNodeType("midi/quantize",A);h.title="MIDI fromFile";h.desc="Plays a MIDI file";h.color="#243";h.prototype.onAction=function(c){"play"==c?this.play():"pause"==c&&(this._playing=!this._playing)};h.prototype.onPropertyChanged=function(c,e){"url"==c&&this.loadMIDIFile(e)};h.prototype.onExecute=function(){if(this._midi&& +this._playing){this._current_time+=this.graph.elapsed_time;for(var e=100*this._current_time,h=0;hb;b++)for(var d=0;dg+f||c[1]>d))return b}}return-1};B.prototype.onAction=function(e,h){if("reset"==e)for(var l=0;lh[1])){var l=this.getKeyIndex(h);this.keys[l]=!0;this._last_key=l;var l=12*(this.properties.start_octave-1)+29+l,a=new c;a.setup([c.NOTEON,l,100]);this.trigger("note",a);return!0}};B.prototype.onMouseMove=function(e,h){if(!(0>h[1]||-1==this._last_key)){this.setDirtyCanvas(!0); var l=this.getKeyIndex(h);if(this._last_key==l)return!0;this.keys[this._last_key]=!1;var a=12*(this.properties.start_octave-1)+29+this._last_key,b=new c;b.setup([c.NOTEOFF,a,100]);this.trigger("note",b);this.keys[l]=!0;a=12*(this.properties.start_octave-1)+29+l;b=new c;b.setup([c.NOTEON,a,100]);this.trigger("note",b);this._last_key=l;return!0}};B.prototype.onMouseUp=function(e,h){if(!(0>h[1])){var l=this.getKeyIndex(h);this.keys[l]=!1;this._last_key=-1;var l=12*(this.properties.start_octave-1)+29+ l,a=new c;a.setup([c.NOTEOFF,l,100]);this.trigger("note",a);return!0}};u.registerNodeType("midi/keys",B)})(this); (function(z){function c(){this.properties={src:"",gain:0.5,loop:!0,autoplay:!0,playbackRate:1};this._loading_audio=!1;this._audiobuffer=null;this._audionodes=[];this._last_sourcenode=null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=p.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain;this.properties.src&&this.loadSound(this.properties.src)}function m(){this.properties={gain:0.5};this._audionodes=[];this._media_stream= null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=p.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain}function n(){this.properties={fftSize:2048,minDecibels:-100,maxDecibels:-10,smoothingTimeConstant:0.5};this.audionode=p.getAudioContext().createAnalyser();this.audionode.graphnode=this;this.audionode.fftSize=this.properties.fftSize;this.audionode.minDecibels=this.properties.minDecibels;this.audionode.maxDecibels= -this.properties.maxDecibels;this.audionode.smoothingTimeConstant=this.properties.smoothingTimeConstant;this.addInput("in","audio");this.addOutput("freqs","array");this.addOutput("samples","array");this._time_bin=this._freq_bin=null}function h(){this.properties={gain:1};this.audionode=p.getAudioContext().createGain();this.addInput("in","audio");this.addInput("gain","number");this.addOutput("out","audio")}function x(){this.properties={impulse_src:"",normalize:!0};this.audionode=p.getAudioContext().createConvolver(); -this.addInput("in","audio");this.addOutput("out","audio")}function k(){this.properties={threshold:-50,knee:40,ratio:12,reduction:-20,attack:0,release:0.25};this.audionode=p.getAudioContext().createDynamicsCompressor();this.addInput("in","audio");this.addOutput("out","audio")}function C(){this.properties={};this.audionode=p.getAudioContext().createWaveShaper();this.addInput("in","audio");this.addInput("shape","waveshape");this.addOutput("out","audio")}function w(){this.properties={gain1:0.5,gain2:0.5}; -this.audionode=p.getAudioContext().createGain();this.audionode1=p.getAudioContext().createGain();this.audionode1.gain.value=this.properties.gain1;this.audionode2=p.getAudioContext().createGain();this.audionode2.gain.value=this.properties.gain2;this.audionode1.connect(this.audionode);this.audionode2.connect(this.audionode);this.addInput("in1","audio");this.addInput("in1 gain","number");this.addInput("in2","audio");this.addInput("in2 gain","number");this.addOutput("out","audio")}function t(){this.properties= +this.properties.maxDecibels;this.audionode.smoothingTimeConstant=this.properties.smoothingTimeConstant;this.addInput("in","audio");this.addOutput("freqs","array");this.addOutput("samples","array");this._time_bin=this._freq_bin=null}function e(){this.properties={gain:1};this.audionode=p.getAudioContext().createGain();this.addInput("in","audio");this.addInput("gain","number");this.addOutput("out","audio")}function w(){this.properties={impulse_src:"",normalize:!0};this.audionode=p.getAudioContext().createConvolver(); +this.addInput("in","audio");this.addOutput("out","audio")}function k(){this.properties={threshold:-50,knee:40,ratio:12,reduction:-20,attack:0,release:0.25};this.audionode=p.getAudioContext().createDynamicsCompressor();this.addInput("in","audio");this.addOutput("out","audio")}function C(){this.properties={};this.audionode=p.getAudioContext().createWaveShaper();this.addInput("in","audio");this.addInput("shape","waveshape");this.addOutput("out","audio")}function v(){this.properties={gain1:0.5,gain2:0.5}; +this.audionode=p.getAudioContext().createGain();this.audionode1=p.getAudioContext().createGain();this.audionode1.gain.value=this.properties.gain1;this.audionode2=p.getAudioContext().createGain();this.audionode2.gain.value=this.properties.gain2;this.audionode1.connect(this.audionode);this.audionode2.connect(this.audionode);this.addInput("in1","audio");this.addInput("in1 gain","number");this.addInput("in2","audio");this.addInput("in2 gain","number");this.addOutput("out","audio")}function s(){this.properties= {A:0.1,D:0.1,S:0.1,R:0.1};this.audionode=p.getAudioContext().createGain();this.audionode.gain.value=0;this.addInput("in","audio");this.addInput("gate","bool");this.addOutput("out","audio");this.gate=!1}function D(){this.properties={delayTime:0.5};this.audionode=p.getAudioContext().createDelay(10);this.audionode.delayTime.value=this.properties.delayTime;this.addInput("in","audio");this.addInput("time","number");this.addOutput("out","audio")}function A(){this.properties={frequency:350,detune:0,Q:1}; -this.addProperty("type","lowpass","enum",{values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")});this.audionode=p.getAudioContext().createBiquadFilter();this.addInput("in","audio");this.addOutput("out","audio")}function e(){this.properties={frequency:440,detune:0,type:"sine"};this.addProperty("type","sine","enum",{values:["sine","square","sawtooth","triangle","custom"]});this.audionode=p.getAudioContext().createOscillator();this.addOutput("out","audio")}function y(){this.properties= +this.addProperty("type","lowpass","enum",{values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")});this.audionode=p.getAudioContext().createBiquadFilter();this.addInput("in","audio");this.addOutput("out","audio")}function h(){this.properties={frequency:440,detune:0,type:"sine"};this.addProperty("type","sine","enum",{values:["sine","square","sawtooth","triangle","custom"]});this.audionode=p.getAudioContext().createOscillator();this.addOutput("out","audio")}function y(){this.properties= {continuous:!0,mark:-1};this.addInput("data","array");this.addInput("mark","number");this.size=[300,200];this._last_buffer=null}function B(){this.properties={band:440,amplitude:1};this.addInput("freqs","array");this.addOutput("signal","number")}function u(){if(!u.default_code){var c=u.default_function.toString(),a=c.indexOf("{")+1,b=c.lastIndexOf("}");u.default_code=c.substr(a,b-a)}this.properties={code:u.default_code};c=p.getAudioContext();c.createScriptProcessor?this.audionode=c.createScriptProcessor(4096, -1,1):(console.warn("ScriptProcessorNode deprecated"),this.audionode=c.createGain());this.processCode();u._bypass_function||(u._bypass_function=this.audionode.onaudioprocess);this.addInput("in","audio");this.addOutput("out","audio")}function F(){this.audionode=p.getAudioContext().destination;this.addInput("in","audio")}var q=z.LiteGraph,p={};z.LGAudio=p;p.getAudioContext=function(){if(!this._audio_context){window.AudioContext=window.AudioContext||window.webkitAudioContext;if(!window.AudioContext)return console.error("AudioContext not supported by browser"), +1,1):(console.warn("ScriptProcessorNode deprecated"),this.audionode=c.createGain());this.processCode();u._bypass_function||(u._bypass_function=this.audionode.onaudioprocess);this.addInput("in","audio");this.addOutput("out","audio")}function G(){this.audionode=p.getAudioContext().destination;this.addInput("in","audio")}var q=z.LiteGraph,p={};z.LGAudio=p;p.getAudioContext=function(){if(!this._audio_context){window.AudioContext=window.AudioContext||window.webkitAudioContext;if(!window.AudioContext)return console.error("AudioContext not supported by browser"), null;this._audio_context=new AudioContext;this._audio_context.onmessage=function(c){console.log("msg",c)};this._audio_context.onended=function(c){console.log("ended",c)};this._audio_context.oncomplete=function(c){console.log("complete",c)}}return this._audio_context};p.connect=function(c,a){try{c.connect(a)}catch(b){console.warn("LGraphAudio:",b)}};p.disconnect=function(c,a){try{c.disconnect(a)}catch(b){console.warn("LGraphAudio:",b)}};p.changeAllAudiosConnections=function(c,a){if(c.inputs)for(var b= 0;b=this.size[0]&&(e=this.size[0]-1),c.strokeStyle="red",c.beginPath(),c.moveTo(e,d),c.lineTo(e,0),c.stroke())}};y.title="Visualization";y.desc="Audio Visualization";q.registerNodeType("audio/visualization",y);B.prototype.onExecute=function(){if(this._freqs=this.getInputData(0)){var c=this.properties.band,a=this.getInputData(1); void 0!==a&&(c=a);a=p.getAudioContext().sampleRate/this._freqs.length;a=c/a*2;a>=this._freqs.length?a=this._freqs[this._freqs.length-1]:(c=a|0,a-=c,a=this._freqs[c]*(1-a)+this._freqs[c+1]*a);this.setOutputData(0,a/255*this.properties.amplitude)}};B.prototype.onGetInputs=function(){return[["band","number"]]};B.title="Signal";B.desc="extract the signal of some frequency";q.registerNodeType("audio/signal",B);u.prototype.onAdded=function(c){c.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess= this._callback)};u["@code"]={widget:"code",type:"code"};u.prototype.onStart=function(){this.audionode.onaudioprocess=this._callback};u.prototype.onStop=function(){this.audionode.onaudioprocess=u._bypass_function};u.prototype.onPause=function(){this.audionode.onaudioprocess=u._bypass_function};u.prototype.onUnpause=function(){this.audionode.onaudioprocess=this._callback};u.prototype.onExecute=function(){};u.prototype.onRemoved=function(){this.audionode.onaudioprocess=u._bypass_function};u.prototype.processCode= function(){try{this._script=new new Function("properties",this.properties.code)(this.properties),this._old_code=this.properties.code,this._callback=this._script.onaudioprocess}catch(c){console.error("Error in onaudioprocess code",c),this._callback=u._bypass_function,this.audionode.onaudioprocess=this._callback}};u.prototype.onPropertyChanged=function(c,a){"code"==c&&(this.properties.code=a,this.processCode(),this.graph&&this.graph.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback))}; -u.default_function=function(){this.onaudioprocess=function(c){var a=c.inputBuffer;c=c.outputBuffer;for(var b=0;b Date: Thu, 18 Jun 2020 14:51:31 +0200 Subject: [PATCH 46/63] Widgets with custom size function don't overflow anymore --- build/litegraph.js | 40288 ++++++++++++++++++--------------------- build/litegraph.min.js | 11214 ++++++++++- src/litegraph.js | 6 +- 3 files changed, 28870 insertions(+), 22638 deletions(-) diff --git a/build/litegraph.js b/build/litegraph.js index 048fedcef..fa2b81803 100644 --- a/build/litegraph.js +++ b/build/litegraph.js @@ -1,12382 +1,12380 @@ -//packer version - -(function(global) { - // ************************************************************* - // LiteGraph CLASS ******* - // ************************************************************* - - /** - * The Global Scope. It contains all the registered node classes. - * - * @class LiteGraph - * @constructor - */ - - var LiteGraph = (global.LiteGraph = { - VERSION: 0.4, - - CANVAS_GRID_SIZE: 10, - - NODE_TITLE_HEIGHT: 30, - NODE_TITLE_TEXT_Y: 20, - NODE_SLOT_HEIGHT: 20, - NODE_WIDGET_HEIGHT: 20, - NODE_WIDTH: 140, - NODE_MIN_WIDTH: 50, - NODE_COLLAPSED_RADIUS: 10, - NODE_COLLAPSED_WIDTH: 80, - NODE_TITLE_COLOR: "#999", - NODE_TEXT_SIZE: 14, - NODE_TEXT_COLOR: "#AAA", - NODE_SUBTEXT_SIZE: 12, - NODE_DEFAULT_COLOR: "#333", - NODE_DEFAULT_BGCOLOR: "#353535", - NODE_DEFAULT_BOXCOLOR: "#666", - NODE_DEFAULT_SHAPE: "box", - DEFAULT_SHADOW_COLOR: "rgba(0,0,0,0.5)", - DEFAULT_GROUP_FONT: 24, - - WIDGET_BGCOLOR: "#222", - WIDGET_OUTLINE_COLOR: "#666", - WIDGET_TEXT_COLOR: "#DDD", - WIDGET_SECONDARY_TEXT_COLOR: "#999", - - LINK_COLOR: "#9A9", - EVENT_LINK_COLOR: "#A86", - CONNECTING_LINK_COLOR: "#AFA", - - MAX_NUMBER_OF_NODES: 1000, //avoid infinite loops - DEFAULT_POSITION: [100, 100], //default node position - VALID_SHAPES: ["default", "box", "round", "card"], //,"circle" - - //shapes are used for nodes but also for slots - BOX_SHAPE: 1, - ROUND_SHAPE: 2, - CIRCLE_SHAPE: 3, - CARD_SHAPE: 4, - ARROW_SHAPE: 5, - - //enums - INPUT: 1, - OUTPUT: 2, - - EVENT: -1, //for outputs - ACTION: -1, //for inputs - - ALWAYS: 0, - ON_EVENT: 1, - NEVER: 2, - ON_TRIGGER: 3, - - UP: 1, - DOWN: 2, - LEFT: 3, - RIGHT: 4, - CENTER: 5, - - STRAIGHT_LINK: 0, - LINEAR_LINK: 1, - SPLINE_LINK: 2, - - NORMAL_TITLE: 0, - NO_TITLE: 1, - TRANSPARENT_TITLE: 2, - AUTOHIDE_TITLE: 3, - - proxy: null, //used to redirect calls - node_images_path: "", - - debug: false, - catch_exceptions: true, - throw_errors: true, - allow_scripts: false, //if set to true some nodes like Formula would be allowed to evaluate code that comes from unsafe sources (like node configuration), which could lead to exploits - registered_node_types: {}, //nodetypes by string - node_types_by_file_extension: {}, //used for dropping files in the canvas - Nodes: {}, //node types by classname - - searchbox_extras: {}, //used to add extra features to the search box - - /** - * Register a node class so it can be listed when the user wants to create a new one - * @method registerNodeType - * @param {String} type name of the node and path - * @param {Class} base_class class containing the structure of a node - */ - - registerNodeType: function(type, base_class) { - if (!base_class.prototype) { - throw "Cannot register a simple object, it must be a class with a prototype"; - } - base_class.type = type; - - if (LiteGraph.debug) { - console.log("Node registered: " + type); - } - - var categories = type.split("/"); - var classname = base_class.name; - - var pos = type.lastIndexOf("/"); - base_class.category = type.substr(0, pos); - - if (!base_class.title) { - base_class.title = classname; - } - //info.name = name.substr(pos+1,name.length - pos); - - //extend class - if (base_class.prototype) { - //is a class - for (var i in LGraphNode.prototype) { - if (!base_class.prototype[i]) { - base_class.prototype[i] = LGraphNode.prototype[i]; - } - } - } - - var prev = this.registered_node_types[type]; - if(prev) - console.log("replacing node type: " + type); - else - { - if( !Object.hasOwnProperty( base_class.prototype, "shape") ) - Object.defineProperty(base_class.prototype, "shape", { - set: function(v) { - switch (v) { - case "default": - delete this._shape; - break; - case "box": - this._shape = LiteGraph.BOX_SHAPE; - break; - case "round": - this._shape = LiteGraph.ROUND_SHAPE; - break; - case "circle": - this._shape = LiteGraph.CIRCLE_SHAPE; - break; - case "card": - this._shape = LiteGraph.CARD_SHAPE; - break; - default: - this._shape = v; - } - }, - get: function(v) { - return this._shape; - }, - enumerable: true, - configurable: true - }); - - //warnings - if (base_class.prototype.onPropertyChange) { - console.warn( - "LiteGraph node class " + - type + - " has onPropertyChange method, it must be called onPropertyChanged with d at the end" - ); - } - - //used to know which nodes create when dragging files to the canvas - if (base_class.supported_extensions) { - for (var i in base_class.supported_extensions) { - var ext = base_class.supported_extensions[i]; - if(ext && ext.constructor === String) - this.node_types_by_file_extension[ ext.toLowerCase() ] = base_class; - } - } - } - - this.registered_node_types[type] = base_class; - if (base_class.constructor.name) { - this.Nodes[classname] = base_class; - } - if (LiteGraph.onNodeTypeRegistered) { - LiteGraph.onNodeTypeRegistered(type, base_class); - } - if (prev && LiteGraph.onNodeTypeReplaced) { - LiteGraph.onNodeTypeReplaced(type, base_class, prev); - } - }, - - /** - * removes a node type from the system - * @method unregisterNodeType - * @param {String|Object} type name of the node or the node constructor itself - */ - unregisterNodeType: function(type) { - var base_class = type.constructor === String ? this.registered_node_types[type] : type; - if(!base_class) - throw("node type not found: " + type ); - delete this.registered_node_types[base_class.type]; - if(base_class.constructor.name) - delete this.Nodes[base_class.constructor.name]; - }, - - /** - * Create a new nodetype by passing a function, it wraps it with a proper class and generates inputs according to the parameters of the function. - * Useful to wrap simple methods that do not require properties, and that only process some input to generate an output. - * @method wrapFunctionAsNode - * @param {String} name node name with namespace (p.e.: 'math/sum') - * @param {Function} func - * @param {Array} param_types [optional] an array containing the type of every parameter, otherwise parameters will accept any type - * @param {String} return_type [optional] string with the return type, otherwise it will be generic - * @param {Object} properties [optional] properties to be configurable - */ - wrapFunctionAsNode: function( - name, - func, - param_types, - return_type, - properties - ) { - var params = Array(func.length); - var code = ""; - var names = LiteGraph.getParameterNames(func); - for (var i = 0; i < names.length; ++i) { - code += - "this.addInput('" + - names[i] + - "'," + - (param_types && param_types[i] - ? "'" + param_types[i] + "'" - : "0") + - ");\n"; - } - code += - "this.addOutput('out'," + - (return_type ? "'" + return_type + "'" : 0) + - ");\n"; - if (properties) { - code += - "this.properties = " + JSON.stringify(properties) + ";\n"; - } - var classobj = Function(code); - classobj.title = name.split("/").pop(); - classobj.desc = "Generated from " + func.name; - classobj.prototype.onExecute = function onExecute() { - for (var i = 0; i < params.length; ++i) { - params[i] = this.getInputData(i); - } - var r = func.apply(this, params); - this.setOutputData(0, r); - }; - this.registerNodeType(name, classobj); - }, - - /** - * Adds this method to all nodetypes, existing and to be created - * (You can add it to LGraphNode.prototype but then existing node types wont have it) - * @method addNodeMethod - * @param {Function} func - */ - addNodeMethod: function(name, func) { - LGraphNode.prototype[name] = func; - for (var i in this.registered_node_types) { - var type = this.registered_node_types[i]; - if (type.prototype[name]) { - type.prototype["_" + name] = type.prototype[name]; - } //keep old in case of replacing - type.prototype[name] = func; - } - }, - - /** - * Create a node of a given type with a name. The node is not attached to any graph yet. - * @method createNode - * @param {String} type full name of the node class. p.e. "math/sin" - * @param {String} name a name to distinguish from other nodes - * @param {Object} options to set options - */ - - createNode: function(type, title, options) { - var base_class = this.registered_node_types[type]; - if (!base_class) { - if (LiteGraph.debug) { - console.log( - 'GraphNode type "' + type + '" not registered.' - ); - } - return null; - } - - var prototype = base_class.prototype || base_class; - - title = title || base_class.title || type; - - var node = null; - - if (LiteGraph.catch_exceptions) { - try { - node = new base_class(title); - } catch (err) { - console.error(err); - return null; - } - } else { - node = new base_class(title); - } - - node.type = type; - - if (!node.title && title) { - node.title = title; - } - if (!node.properties) { - node.properties = {}; - } - if (!node.properties_info) { - node.properties_info = []; - } - if (!node.flags) { - node.flags = {}; - } - if (!node.size) { - node.size = node.computeSize(); - //call onresize? - } - if (!node.pos) { - node.pos = LiteGraph.DEFAULT_POSITION.concat(); - } - if (!node.mode) { - node.mode = LiteGraph.ALWAYS; - } - - //extra options - if (options) { - for (var i in options) { - node[i] = options[i]; - } - } - - return node; - }, - - /** - * Returns a registered node type with a given name - * @method getNodeType - * @param {String} type full name of the node class. p.e. "math/sin" - * @return {Class} the node class - */ - getNodeType: function(type) { - return this.registered_node_types[type]; - }, - - /** - * Returns a list of node types matching one category - * @method getNodeType - * @param {String} category category name - * @return {Array} array with all the node classes - */ - - getNodeTypesInCategory: function(category, filter) { - var r = []; - for (var i in this.registered_node_types) { - var type = this.registered_node_types[i]; - if (filter && type.filter && type.filter != filter) { - continue; - } - - if (category == "") { - if (type.category == null) { - r.push(type); - } - } else if (type.category == category) { - r.push(type); - } - } - - return r; - }, - - /** - * Returns a list with all the node type categories - * @method getNodeTypesCategories - * @return {Array} array with all the names of the categories - */ - getNodeTypesCategories: function( filter ) { - var categories = { "": 1 }; - for (var i in this.registered_node_types) { - var type = this.registered_node_types[i]; - if ( type.category && !type.skip_list ) - { - if(filter && type.filter != filter) - continue; - categories[type.category] = 1; - } - } - var result = []; - for (var i in categories) { - result.push(i); - } - return result; - }, - - //debug purposes: reloads all the js scripts that matches a wildcard - reloadNodes: function(folder_wildcard) { - var tmp = document.getElementsByTagName("script"); - //weird, this array changes by its own, so we use a copy - var script_files = []; - for (var i in tmp) { - script_files.push(tmp[i]); - } - - var docHeadObj = document.getElementsByTagName("head")[0]; - folder_wildcard = document.location.href + folder_wildcard; - - for (var i in script_files) { - var src = script_files[i].src; - if ( - !src || - src.substr(0, folder_wildcard.length) != folder_wildcard - ) { - continue; - } - - try { - if (LiteGraph.debug) { - console.log("Reloading: " + src); - } - var dynamicScript = document.createElement("script"); - dynamicScript.type = "text/javascript"; - dynamicScript.src = src; - docHeadObj.appendChild(dynamicScript); - docHeadObj.removeChild(script_files[i]); - } catch (err) { - if (LiteGraph.throw_errors) { - throw err; - } - if (LiteGraph.debug) { - console.log("Error while reloading " + src); - } - } - } - - if (LiteGraph.debug) { - console.log("Nodes reloaded"); - } - }, - - //separated just to improve if it doesn't work - cloneObject: function(obj, target) { - if (obj == null) { - return null; - } - var r = JSON.parse(JSON.stringify(obj)); - if (!target) { - return r; - } - - for (var i in r) { - target[i] = r[i]; - } - return target; - }, - - /** - * Returns if the types of two slots are compatible (taking into account wildcards, etc) - * @method isValidConnection - * @param {String} type_a - * @param {String} type_b - * @return {Boolean} true if they can be connected - */ - isValidConnection: function(type_a, type_b) { - if ( - !type_a || //generic output - !type_b || //generic input - type_a == type_b || //same type (is valid for triggers) - (type_a == LiteGraph.EVENT && type_b == LiteGraph.ACTION) - ) { - return true; - } - - // Enforce string type to handle toLowerCase call (-1 number not ok) - type_a = String(type_a); - type_b = String(type_b); - type_a = type_a.toLowerCase(); - type_b = type_b.toLowerCase(); - - // For nodes supporting multiple connection types - if (type_a.indexOf(",") == -1 && type_b.indexOf(",") == -1) { - return type_a == type_b; - } - - // Check all permutations to see if one is valid - var supported_types_a = type_a.split(","); - var supported_types_b = type_b.split(","); - for (var i = 0; i < supported_types_a.length; ++i) { - for (var j = 0; j < supported_types_b.length; ++j) { - if (supported_types_a[i] == supported_types_b[j]) { - return true; - } - } - } - - return false; - }, - - /** - * Register a string in the search box so when the user types it it will recommend this node - * @method registerSearchboxExtra - * @param {String} node_type the node recommended - * @param {String} description text to show next to it - * @param {Object} data it could contain info of how the node should be configured - * @return {Boolean} true if they can be connected - */ - registerSearchboxExtra: function(node_type, description, data) { - this.searchbox_extras[description.toLowerCase()] = { - type: node_type, - desc: description, - data: data - }; - }, - - /** - * Wrapper to load files (from url using fetch or from file using FileReader) - * @method fetchFile - * @param {String|File|Blob} url the url of the file (or the file itself) - * @param {String} type an string to know how to fetch it: "text","arraybuffer","json","blob" - * @param {Function} on_complete callback(data) - * @param {Function} on_error in case of an error - * @return {FileReader|Promise} returns the object used to - */ - fetchFile: function( url, type, on_complete, on_error ) { - var that = this; - if(!url) - return null; - - type = type || "text"; - if( url.constructor === String ) - { - if (url.substr(0, 4) == "http" && LiteGraph.proxy) { - url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); - } - return fetch(url) - .then(function(response) { - if(!response.ok) - throw new Error("File not found"); //it will be catch below - if(type == "arraybuffer") - return response.arrayBuffer(); - else if(type == "text" || type == "string") - return response.text(); - else if(type == "json") - return response.json(); - else if(type == "blob") - return response.blob(); - }) - .then(function(data) { - if(on_complete) - on_complete(data); - }) - .catch(function(error) { - console.error("error fetching file:",url); - if(on_error) - on_error(error); - }); - } - else if( url.constructor === File || url.constructor === Blob) - { - var reader = new FileReader(); - reader.onload = function(e) - { - var v = e.target.result; - if( type == "json" ) - v = JSON.parse(v); - if(on_complete) - on_complete(v); - } - if(type == "arraybuffer") - return reader.readAsArrayBuffer(url); - else if(type == "text" || type == "json") - return reader.readAsText(url); - else if(type == "blob") - return reader.readAsBinaryString(url); - } - return null; - } - }); - - //timer that works everywhere - if (typeof performance != "undefined") { - LiteGraph.getTime = performance.now.bind(performance); - } else if (typeof Date != "undefined" && Date.now) { - LiteGraph.getTime = Date.now.bind(Date); - } else if (typeof process != "undefined") { - LiteGraph.getTime = function() { - var t = process.hrtime(); - return t[0] * 0.001 + t[1] * 1e-6; - }; - } else { - LiteGraph.getTime = function getTime() { - return new Date().getTime(); - }; - } - - //********************************************************************************* - // LGraph CLASS - //********************************************************************************* - - /** - * LGraph is the class that contain a full graph. We instantiate one and add nodes to it, and then we can run the execution loop. - * - * @class LGraph - * @constructor - * @param {Object} o data from previous serialization [optional] - */ - - function LGraph(o) { - if (LiteGraph.debug) { - console.log("Graph created"); - } - this.list_of_graphcanvas = null; - this.clear(); - - if (o) { - this.configure(o); - } - } - - global.LGraph = LiteGraph.LGraph = LGraph; - - //default supported types - LGraph.supported_types = ["number", "string", "boolean"]; - - //used to know which types of connections support this graph (some graphs do not allow certain types) - LGraph.prototype.getSupportedTypes = function() { - return this.supported_types || LGraph.supported_types; - }; - - LGraph.STATUS_STOPPED = 1; - LGraph.STATUS_RUNNING = 2; - - /** - * Removes all nodes from this graph - * @method clear - */ - - LGraph.prototype.clear = function() { - this.stop(); - this.status = LGraph.STATUS_STOPPED; - - this.last_node_id = 0; - this.last_link_id = 0; - - this._version = -1; //used to detect changes - - //safe clear - if (this._nodes) { - for (var i = 0; i < this._nodes.length; ++i) { - var node = this._nodes[i]; - if (node.onRemoved) { - node.onRemoved(); - } - } - } - - //nodes - this._nodes = []; - this._nodes_by_id = {}; - this._nodes_in_order = []; //nodes that are executable sorted in execution order - this._nodes_executable = null; //nodes that contain onExecute - - //other scene stuff - this._groups = []; - - //links - this.links = {}; //container with all the links - - //iterations - this.iteration = 0; - - //custom data - this.config = {}; - this.vars = {}; - - //timing - this.globaltime = 0; - this.runningtime = 0; - this.fixedtime = 0; - this.fixedtime_lapse = 0.01; - this.elapsed_time = 0.01; - this.last_update_time = 0; - this.starttime = 0; - - this.catch_errors = true; - - //subgraph_data - this.inputs = {}; - this.outputs = {}; - - //notify canvas to redraw - this.change(); - - this.sendActionToCanvas("clear"); - }; - - /** - * Attach Canvas to this graph - * @method attachCanvas - * @param {GraphCanvas} graph_canvas - */ - - LGraph.prototype.attachCanvas = function(graphcanvas) { - if (graphcanvas.constructor != LGraphCanvas) { - throw "attachCanvas expects a LGraphCanvas instance"; - } - if (graphcanvas.graph && graphcanvas.graph != this) { - graphcanvas.graph.detachCanvas(graphcanvas); - } - - graphcanvas.graph = this; - if (!this.list_of_graphcanvas) { - this.list_of_graphcanvas = []; - } - this.list_of_graphcanvas.push(graphcanvas); - }; - - /** - * Detach Canvas from this graph - * @method detachCanvas - * @param {GraphCanvas} graph_canvas - */ - LGraph.prototype.detachCanvas = function(graphcanvas) { - if (!this.list_of_graphcanvas) { - return; - } - - var pos = this.list_of_graphcanvas.indexOf(graphcanvas); - if (pos == -1) { - return; - } - graphcanvas.graph = null; - this.list_of_graphcanvas.splice(pos, 1); - }; - - /** - * Starts running this graph every interval milliseconds. - * @method start - * @param {number} interval amount of milliseconds between executions, if 0 then it renders to the monitor refresh rate - */ - - LGraph.prototype.start = function(interval) { - if (this.status == LGraph.STATUS_RUNNING) { - return; - } - this.status = LGraph.STATUS_RUNNING; - - if (this.onPlayEvent) { - this.onPlayEvent(); - } - - this.sendEventToAllNodes("onStart"); - - //launch - this.starttime = LiteGraph.getTime(); - this.last_update_time = this.starttime; - interval = interval || 0; - var that = this; - - //execute once per frame - if ( interval == 0 && typeof window != "undefined" && window.requestAnimationFrame ) { - function on_frame() { - if (that.execution_timer_id != -1) { - return; - } - window.requestAnimationFrame(on_frame); - if(that.onBeforeStep) - that.onBeforeStep(); - that.runStep(1, !that.catch_errors); - if(that.onAfterStep) - that.onAfterStep(); - } - this.execution_timer_id = -1; - on_frame(); - } else { //execute every 'interval' ms - this.execution_timer_id = setInterval(function() { - //execute - if(that.onBeforeStep) - that.onBeforeStep(); - that.runStep(1, !that.catch_errors); - if(that.onAfterStep) - that.onAfterStep(); - }, interval); - } - }; - - /** - * Stops the execution loop of the graph - * @method stop execution - */ - - LGraph.prototype.stop = function() { - if (this.status == LGraph.STATUS_STOPPED) { - return; - } - - this.status = LGraph.STATUS_STOPPED; - - if (this.onStopEvent) { - this.onStopEvent(); - } - - if (this.execution_timer_id != null) { - if (this.execution_timer_id != -1) { - clearInterval(this.execution_timer_id); - } - this.execution_timer_id = null; - } - - this.sendEventToAllNodes("onStop"); - }; - - /** - * Run N steps (cycles) of the graph - * @method runStep - * @param {number} num number of steps to run, default is 1 - * @param {Boolean} do_not_catch_errors [optional] if you want to try/catch errors - * @param {number} limit max number of nodes to execute (used to execute from start to a node) - */ - - LGraph.prototype.runStep = function(num, do_not_catch_errors, limit ) { - num = num || 1; - - var start = LiteGraph.getTime(); - this.globaltime = 0.001 * (start - this.starttime); - - var nodes = this._nodes_executable - ? this._nodes_executable - : this._nodes; - if (!nodes) { - return; - } - - limit = limit || nodes.length; - - if (do_not_catch_errors) { - //iterations - for (var i = 0; i < num; i++) { - for (var j = 0; j < limit; ++j) { - var node = nodes[j]; - if (node.mode == LiteGraph.ALWAYS && node.onExecute) { - node.onExecute(); //hard to send elapsed time - } - } - - this.fixedtime += this.fixedtime_lapse; - if (this.onExecuteStep) { - this.onExecuteStep(); - } - } - - if (this.onAfterExecute) { - this.onAfterExecute(); - } - } else { - try { - //iterations - for (var i = 0; i < num; i++) { - for (var j = 0; j < limit; ++j) { - var node = nodes[j]; - if (node.mode == LiteGraph.ALWAYS && node.onExecute) { - node.onExecute(); - } - } - - this.fixedtime += this.fixedtime_lapse; - if (this.onExecuteStep) { - this.onExecuteStep(); - } - } - - if (this.onAfterExecute) { - this.onAfterExecute(); - } - this.errors_in_execution = false; - } catch (err) { - this.errors_in_execution = true; - if (LiteGraph.throw_errors) { - throw err; - } - if (LiteGraph.debug) { - console.log("Error during execution: " + err); - } - this.stop(); - } - } - - var now = LiteGraph.getTime(); - var elapsed = now - start; - if (elapsed == 0) { - elapsed = 1; - } - this.execution_time = 0.001 * elapsed; - this.globaltime += 0.001 * elapsed; - this.iteration += 1; - this.elapsed_time = (now - this.last_update_time) * 0.001; - this.last_update_time = now; - }; - - /** - * Updates the graph execution order according to relevance of the nodes (nodes with only outputs have more relevance than - * nodes with only inputs. - * @method updateExecutionOrder - */ - LGraph.prototype.updateExecutionOrder = function() { - this._nodes_in_order = this.computeExecutionOrder(false); - this._nodes_executable = []; - for (var i = 0; i < this._nodes_in_order.length; ++i) { - if (this._nodes_in_order[i].onExecute) { - this._nodes_executable.push(this._nodes_in_order[i]); - } - } - }; - - //This is more internal, it computes the executable nodes in order and returns it - LGraph.prototype.computeExecutionOrder = function( - only_onExecute, - set_level - ) { - var L = []; - var S = []; - var M = {}; - var visited_links = {}; //to avoid repeating links - var remaining_links = {}; //to a - - //search for the nodes without inputs (starting nodes) - for (var i = 0, l = this._nodes.length; i < l; ++i) { - var node = this._nodes[i]; - if (only_onExecute && !node.onExecute) { - continue; - } - - M[node.id] = node; //add to pending nodes - - var num = 0; //num of input connections - if (node.inputs) { - for (var j = 0, l2 = node.inputs.length; j < l2; j++) { - if (node.inputs[j] && node.inputs[j].link != null) { - num += 1; - } - } - } - - if (num == 0) { - //is a starting node - S.push(node); - if (set_level) { - node._level = 1; - } - } //num of input links - else { - if (set_level) { - node._level = 0; - } - remaining_links[node.id] = num; - } - } - - while (true) { - if (S.length == 0) { - break; - } - - //get an starting node - var node = S.shift(); - L.push(node); //add to ordered list - delete M[node.id]; //remove from the pending nodes - - if (!node.outputs) { - continue; - } - - //for every output - for (var i = 0; i < node.outputs.length; i++) { - var output = node.outputs[i]; - //not connected - if ( - output == null || - output.links == null || - output.links.length == 0 - ) { - continue; - } - - //for every connection - for (var j = 0; j < output.links.length; j++) { - var link_id = output.links[j]; - var link = this.links[link_id]; - if (!link) { - continue; - } - - //already visited link (ignore it) - if (visited_links[link.id]) { - continue; - } - - var target_node = this.getNodeById(link.target_id); - if (target_node == null) { - visited_links[link.id] = true; - continue; - } - - if ( - set_level && - (!target_node._level || - target_node._level <= node._level) - ) { - target_node._level = node._level + 1; - } - - visited_links[link.id] = true; //mark as visited - remaining_links[target_node.id] -= 1; //reduce the number of links remaining - if (remaining_links[target_node.id] == 0) { - S.push(target_node); - } //if no more links, then add to starters array - } - } - } - - //the remaining ones (loops) - for (var i in M) { - L.push(M[i]); - } - - if (L.length != this._nodes.length && LiteGraph.debug) { - console.warn("something went wrong, nodes missing"); - } - - var l = L.length; - - //save order number in the node - for (var i = 0; i < l; ++i) { - L[i].order = i; - } - - //sort now by priority - L = L.sort(function(A, B) { - var Ap = A.constructor.priority || A.priority || 0; - var Bp = B.constructor.priority || B.priority || 0; - if (Ap == Bp) { - //if same priority, sort by order - return A.order - B.order; - } - return Ap - Bp; //sort by priority - }); - - //save order number in the node, again... - for (var i = 0; i < l; ++i) { - L[i].order = i; - } - - return L; - }; - - /** - * Returns all the nodes that could affect this one (ancestors) by crawling all the inputs recursively. - * It doesn't include the node itself - * @method getAncestors - * @return {Array} an array with all the LGraphNodes that affect this node, in order of execution - */ - LGraph.prototype.getAncestors = function(node) { - var ancestors = []; - var pending = [node]; - var visited = {}; - - while (pending.length) { - var current = pending.shift(); - if (!current.inputs) { - continue; - } - if (!visited[current.id] && current != node) { - visited[current.id] = true; - ancestors.push(current); - } - - for (var i = 0; i < current.inputs.length; ++i) { - var input = current.getInputNode(i); - if (input && ancestors.indexOf(input) == -1) { - pending.push(input); - } - } - } - - ancestors.sort(function(a, b) { - return a.order - b.order; - }); - return ancestors; - }; - - /** - * Positions every node in a more readable manner - * @method arrange - */ - LGraph.prototype.arrange = function(margin) { - margin = margin || 100; - - var nodes = this.computeExecutionOrder(false, true); - var columns = []; - for (var i = 0; i < nodes.length; ++i) { - var node = nodes[i]; - var col = node._level || 1; - if (!columns[col]) { - columns[col] = []; - } - columns[col].push(node); - } - - var x = margin; - - for (var i = 0; i < columns.length; ++i) { - var column = columns[i]; - if (!column) { - continue; - } - var max_size = 100; - var y = margin + LiteGraph.NODE_TITLE_HEIGHT; - for (var j = 0; j < column.length; ++j) { - var node = column[j]; - node.pos[0] = x; - node.pos[1] = y; - if (node.size[0] > max_size) { - max_size = node.size[0]; - } - y += node.size[1] + margin + LiteGraph.NODE_TITLE_HEIGHT; - } - x += max_size + margin; - } - - this.setDirtyCanvas(true, true); - }; - - /** - * Returns the amount of time the graph has been running in milliseconds - * @method getTime - * @return {number} number of milliseconds the graph has been running - */ - LGraph.prototype.getTime = function() { - return this.globaltime; - }; - - /** - * Returns the amount of time accumulated using the fixedtime_lapse var. This is used in context where the time increments should be constant - * @method getFixedTime - * @return {number} number of milliseconds the graph has been running - */ - - LGraph.prototype.getFixedTime = function() { - return this.fixedtime; - }; - - /** - * Returns the amount of time it took to compute the latest iteration. Take into account that this number could be not correct - * if the nodes are using graphical actions - * @method getElapsedTime - * @return {number} number of milliseconds it took the last cycle - */ - - LGraph.prototype.getElapsedTime = function() { - return this.elapsed_time; - }; - - /** - * Sends an event to all the nodes, useful to trigger stuff - * @method sendEventToAllNodes - * @param {String} eventname the name of the event (function to be called) - * @param {Array} params parameters in array format - */ - LGraph.prototype.sendEventToAllNodes = function(eventname, params, mode) { - mode = mode || LiteGraph.ALWAYS; - - var nodes = this._nodes_in_order ? this._nodes_in_order : this._nodes; - if (!nodes) { - return; - } - - for (var j = 0, l = nodes.length; j < l; ++j) { - var node = nodes[j]; - - if ( - node.constructor === LiteGraph.Subgraph && - eventname != "onExecute" - ) { - if (node.mode == mode) { - node.sendEventToAllNodes(eventname, params, mode); - } - continue; - } - - if (!node[eventname] || node.mode != mode) { - continue; - } - if (params === undefined) { - node[eventname](); - } else if (params && params.constructor === Array) { - node[eventname].apply(node, params); - } else { - node[eventname](params); - } - } - }; - - LGraph.prototype.sendActionToCanvas = function(action, params) { - if (!this.list_of_graphcanvas) { - return; - } - - for (var i = 0; i < this.list_of_graphcanvas.length; ++i) { - var c = this.list_of_graphcanvas[i]; - if (c[action]) { - c[action].apply(c, params); - } - } - }; - - /** - * Adds a new node instance to this graph - * @method add - * @param {LGraphNode} node the instance of the node - */ - - LGraph.prototype.add = function(node, skip_compute_order) { - if (!node) { - return; - } - - //groups - if (node.constructor === LGraphGroup) { - this._groups.push(node); - this.setDirtyCanvas(true); - this.change(); - node.graph = this; - this._version++; - return; - } - - //nodes - if (node.id != -1 && this._nodes_by_id[node.id] != null) { - console.warn( - "LiteGraph: there is already a node with this ID, changing it" - ); - node.id = ++this.last_node_id; - } - - if (this._nodes.length >= LiteGraph.MAX_NUMBER_OF_NODES) { - throw "LiteGraph: max number of nodes in a graph reached"; - } - - //give him an id - if (node.id == null || node.id == -1) { - node.id = ++this.last_node_id; - } else if (this.last_node_id < node.id) { - this.last_node_id = node.id; - } - - node.graph = this; - this._version++; - - this._nodes.push(node); - this._nodes_by_id[node.id] = node; - - if (node.onAdded) { - node.onAdded(this); - } - - if (this.config.align_to_grid) { - node.alignToGrid(); - } - - if (!skip_compute_order) { - this.updateExecutionOrder(); - } - - if (this.onNodeAdded) { - this.onNodeAdded(node); - } - - this.setDirtyCanvas(true); - this.change(); - - return node; //to chain actions - }; - - /** - * Removes a node from the graph - * @method remove - * @param {LGraphNode} node the instance of the node - */ - - LGraph.prototype.remove = function(node) { - if (node.constructor === LiteGraph.LGraphGroup) { - var index = this._groups.indexOf(node); - if (index != -1) { - this._groups.splice(index, 1); - } - node.graph = null; - this._version++; - this.setDirtyCanvas(true, true); - this.change(); - return; - } - - if (this._nodes_by_id[node.id] == null) { - return; - } //not found - - if (node.ignore_remove) { - return; - } //cannot be removed - - //disconnect inputs - if (node.inputs) { - for (var i = 0; i < node.inputs.length; i++) { - var slot = node.inputs[i]; - if (slot.link != null) { - node.disconnectInput(i); - } - } - } - - //disconnect outputs - if (node.outputs) { - for (var i = 0; i < node.outputs.length; i++) { - var slot = node.outputs[i]; - if (slot.links != null && slot.links.length) { - node.disconnectOutput(i); - } - } - } - - //node.id = -1; //why? - - //callback - if (node.onRemoved) { - node.onRemoved(); - } - - node.graph = null; - this._version++; - - //remove from canvas render - if (this.list_of_graphcanvas) { - for (var i = 0; i < this.list_of_graphcanvas.length; ++i) { - var canvas = this.list_of_graphcanvas[i]; - if (canvas.selected_nodes[node.id]) { - delete canvas.selected_nodes[node.id]; - } - if (canvas.node_dragged == node) { - canvas.node_dragged = null; - } - } - } - - //remove from containers - var pos = this._nodes.indexOf(node); - if (pos != -1) { - this._nodes.splice(pos, 1); - } - delete this._nodes_by_id[node.id]; - - if (this.onNodeRemoved) { - this.onNodeRemoved(node); - } - - this.setDirtyCanvas(true, true); - this.change(); - - this.updateExecutionOrder(); - }; - - /** - * Returns a node by its id. - * @method getNodeById - * @param {Number} id - */ - - LGraph.prototype.getNodeById = function(id) { - if (id == null) { - return null; - } - return this._nodes_by_id[id]; - }; - - /** - * Returns a list of nodes that matches a class - * @method findNodesByClass - * @param {Class} classObject the class itself (not an string) - * @return {Array} a list with all the nodes of this type - */ - LGraph.prototype.findNodesByClass = function(classObject, result) { - result = result || []; - result.length = 0; - for (var i = 0, l = this._nodes.length; i < l; ++i) { - if (this._nodes[i].constructor === classObject) { - result.push(this._nodes[i]); - } - } - return result; - }; - - /** - * Returns a list of nodes that matches a type - * @method findNodesByType - * @param {String} type the name of the node type - * @return {Array} a list with all the nodes of this type - */ - LGraph.prototype.findNodesByType = function(type, result) { - var type = type.toLowerCase(); - result = result || []; - result.length = 0; - for (var i = 0, l = this._nodes.length; i < l; ++i) { - if (this._nodes[i].type.toLowerCase() == type) { - result.push(this._nodes[i]); - } - } - return result; - }; - - /** - * Returns the first node that matches a name in its title - * @method findNodeByTitle - * @param {String} name the name of the node to search - * @return {Node} the node or null - */ - LGraph.prototype.findNodeByTitle = function(title) { - for (var i = 0, l = this._nodes.length; i < l; ++i) { - if (this._nodes[i].title == title) { - return this._nodes[i]; - } - } - return null; - }; - - /** - * Returns a list of nodes that matches a name - * @method findNodesByTitle - * @param {String} name the name of the node to search - * @return {Array} a list with all the nodes with this name - */ - LGraph.prototype.findNodesByTitle = function(title) { - var result = []; - for (var i = 0, l = this._nodes.length; i < l; ++i) { - if (this._nodes[i].title == title) { - result.push(this._nodes[i]); - } - } - return result; - }; - - /** - * Returns the top-most node in this position of the canvas - * @method getNodeOnPos - * @param {number} x the x coordinate in canvas space - * @param {number} y the y coordinate in canvas space - * @param {Array} nodes_list a list with all the nodes to search from, by default is all the nodes in the graph - * @return {LGraphNode} the node at this position or null - */ - LGraph.prototype.getNodeOnPos = function(x, y, nodes_list, margin) { - nodes_list = nodes_list || this._nodes; - for (var i = nodes_list.length - 1; i >= 0; i--) { - var n = nodes_list[i]; - if (n.isPointInside(x, y, margin)) { - return n; - } - } - return null; - }; - - /** - * Returns the top-most group in that position - * @method getGroupOnPos - * @param {number} x the x coordinate in canvas space - * @param {number} y the y coordinate in canvas space - * @return {LGraphGroup} the group or null - */ - LGraph.prototype.getGroupOnPos = function(x, y) { - for (var i = this._groups.length - 1; i >= 0; i--) { - var g = this._groups[i]; - if (g.isPointInside(x, y, 2, true)) { - return g; - } - } - return null; - }; - - /** - * Checks that the node type matches the node type registered, used when replacing a nodetype by a newer version during execution - * this replaces the ones using the old version with the new version - * @method checkNodeTypes - */ - LGraph.prototype.checkNodeTypes = function() { - var changes = false; - for (var i = 0; i < this._nodes.length; i++) { - var node = this._nodes[i]; - var ctor = LiteGraph.registered_node_types[node.type]; - if (node.constructor == ctor) { - continue; - } - console.log("node being replaced by newer version: " + node.type); - var newnode = LiteGraph.createNode(node.type); - changes = true; - this._nodes[i] = newnode; - newnode.configure(node.serialize()); - newnode.graph = this; - this._nodes_by_id[newnode.id] = newnode; - if (node.inputs) { - newnode.inputs = node.inputs.concat(); - } - if (node.outputs) { - newnode.outputs = node.outputs.concat(); - } - } - this.updateExecutionOrder(); - }; - - // ********** GLOBALS ***************** - - LGraph.prototype.onAction = function(action, param) { - this._input_nodes = this.findNodesByClass( - LiteGraph.GraphInput, - this._input_nodes - ); - for (var i = 0; i < this._input_nodes.length; ++i) { - var node = this._input_nodes[i]; - if (node.properties.name != action) { - continue; - } - node.onAction(action, param); - break; - } - }; - - LGraph.prototype.trigger = function(action, param) { - if (this.onTrigger) { - this.onTrigger(action, param); - } - }; - - /** - * Tell this graph it has a global graph input of this type - * @method addGlobalInput - * @param {String} name - * @param {String} type - * @param {*} value [optional] - */ - LGraph.prototype.addInput = function(name, type, value) { - var input = this.inputs[name]; - if (input) { - //already exist - return; - } - - this.inputs[name] = { name: name, type: type, value: value }; - this._version++; - - if (this.onInputAdded) { - this.onInputAdded(name, type); - } - - if (this.onInputsOutputsChange) { - this.onInputsOutputsChange(); - } - }; - - /** - * Assign a data to the global graph input - * @method setGlobalInputData - * @param {String} name - * @param {*} data - */ - LGraph.prototype.setInputData = function(name, data) { - var input = this.inputs[name]; - if (!input) { - return; - } - input.value = data; - }; - - /** - * Returns the current value of a global graph input - * @method getInputData - * @param {String} name - * @return {*} the data - */ - LGraph.prototype.getInputData = function(name) { - var input = this.inputs[name]; - if (!input) { - return null; - } - return input.value; - }; - - /** - * Changes the name of a global graph input - * @method renameInput - * @param {String} old_name - * @param {String} new_name - */ - LGraph.prototype.renameInput = function(old_name, name) { - if (name == old_name) { - return; - } - - if (!this.inputs[old_name]) { - return false; - } - - if (this.inputs[name]) { - console.error("there is already one input with that name"); - return false; - } - - this.inputs[name] = this.inputs[old_name]; - delete this.inputs[old_name]; - this._version++; - - if (this.onInputRenamed) { - this.onInputRenamed(old_name, name); - } - - if (this.onInputsOutputsChange) { - this.onInputsOutputsChange(); - } - }; - - /** - * Changes the type of a global graph input - * @method changeInputType - * @param {String} name - * @param {String} type - */ - LGraph.prototype.changeInputType = function(name, type) { - if (!this.inputs[name]) { - return false; - } - - if ( - this.inputs[name].type && - String(this.inputs[name].type).toLowerCase() == - String(type).toLowerCase() - ) { - return; - } - - this.inputs[name].type = type; - this._version++; - if (this.onInputTypeChanged) { - this.onInputTypeChanged(name, type); - } - }; - - /** - * Removes a global graph input - * @method removeInput - * @param {String} name - * @param {String} type - */ - LGraph.prototype.removeInput = function(name) { - if (!this.inputs[name]) { - return false; - } - - delete this.inputs[name]; - this._version++; - - if (this.onInputRemoved) { - this.onInputRemoved(name); - } - - if (this.onInputsOutputsChange) { - this.onInputsOutputsChange(); - } - return true; - }; - - /** - * Creates a global graph output - * @method addOutput - * @param {String} name - * @param {String} type - * @param {*} value - */ - LGraph.prototype.addOutput = function(name, type, value) { - this.outputs[name] = { name: name, type: type, value: value }; - this._version++; - - if (this.onOutputAdded) { - this.onOutputAdded(name, type); - } - - if (this.onInputsOutputsChange) { - this.onInputsOutputsChange(); - } - }; - - /** - * Assign a data to the global output - * @method setOutputData - * @param {String} name - * @param {String} value - */ - LGraph.prototype.setOutputData = function(name, value) { - var output = this.outputs[name]; - if (!output) { - return; - } - output.value = value; - }; - - /** - * Returns the current value of a global graph output - * @method getOutputData - * @param {String} name - * @return {*} the data - */ - LGraph.prototype.getOutputData = function(name) { - var output = this.outputs[name]; - if (!output) { - return null; - } - return output.value; - }; - - /** - * Renames a global graph output - * @method renameOutput - * @param {String} old_name - * @param {String} new_name - */ - LGraph.prototype.renameOutput = function(old_name, name) { - if (!this.outputs[old_name]) { - return false; - } - - if (this.outputs[name]) { - console.error("there is already one output with that name"); - return false; - } - - this.outputs[name] = this.outputs[old_name]; - delete this.outputs[old_name]; - this._version++; - - if (this.onOutputRenamed) { - this.onOutputRenamed(old_name, name); - } - - if (this.onInputsOutputsChange) { - this.onInputsOutputsChange(); - } - }; - - /** - * Changes the type of a global graph output - * @method changeOutputType - * @param {String} name - * @param {String} type - */ - LGraph.prototype.changeOutputType = function(name, type) { - if (!this.outputs[name]) { - return false; - } - - if ( - this.outputs[name].type && - String(this.outputs[name].type).toLowerCase() == - String(type).toLowerCase() - ) { - return; - } - - this.outputs[name].type = type; - this._version++; - if (this.onOutputTypeChanged) { - this.onOutputTypeChanged(name, type); - } - }; - - /** - * Removes a global graph output - * @method removeOutput - * @param {String} name - */ - LGraph.prototype.removeOutput = function(name) { - if (!this.outputs[name]) { - return false; - } - delete this.outputs[name]; - this._version++; - - if (this.onOutputRemoved) { - this.onOutputRemoved(name); - } - - if (this.onInputsOutputsChange) { - this.onInputsOutputsChange(); - } - return true; - }; - - LGraph.prototype.triggerInput = function(name, value) { - var nodes = this.findNodesByTitle(name); - for (var i = 0; i < nodes.length; ++i) { - nodes[i].onTrigger(value); - } - }; - - LGraph.prototype.setCallback = function(name, func) { - var nodes = this.findNodesByTitle(name); - for (var i = 0; i < nodes.length; ++i) { - nodes[i].setTrigger(func); - } - }; - - LGraph.prototype.connectionChange = function(node, link_info) { - this.updateExecutionOrder(); - if (this.onConnectionChange) { - this.onConnectionChange(node); - } - this._version++; - this.sendActionToCanvas("onConnectionChange"); - }; - - /** - * returns if the graph is in live mode - * @method isLive - */ - - LGraph.prototype.isLive = function() { - if (!this.list_of_graphcanvas) { - return false; - } - - for (var i = 0; i < this.list_of_graphcanvas.length; ++i) { - var c = this.list_of_graphcanvas[i]; - if (c.live_mode) { - return true; - } - } - return false; - }; - - /** - * clears the triggered slot animation in all links (stop visual animation) - * @method clearTriggeredSlots - */ - LGraph.prototype.clearTriggeredSlots = function() { - for (var i in this.links) { - var link_info = this.links[i]; - if (!link_info) { - continue; - } - if (link_info._last_time) { - link_info._last_time = 0; - } - } - }; - - /* Called when something visually changed (not the graph!) */ - LGraph.prototype.change = function() { - if (LiteGraph.debug) { - console.log("Graph changed"); - } - this.sendActionToCanvas("setDirty", [true, true]); - if (this.on_change) { - this.on_change(this); - } - }; - - LGraph.prototype.setDirtyCanvas = function(fg, bg) { - this.sendActionToCanvas("setDirty", [fg, bg]); - }; - - /** - * Destroys a link - * @method removeLink - * @param {Number} link_id - */ - LGraph.prototype.removeLink = function(link_id) { - var link = this.links[link_id]; - if (!link) { - return; - } - var node = this.getNodeById(link.target_id); - if (node) { - node.disconnectInput(link.target_slot); - } - }; - - //save and recover app state *************************************** - /** - * Creates a Object containing all the info about this graph, it can be serialized - * @method serialize - * @return {Object} value of the node - */ - LGraph.prototype.serialize = function() { - var nodes_info = []; - for (var i = 0, l = this._nodes.length; i < l; ++i) { - nodes_info.push(this._nodes[i].serialize()); - } - - //pack link info into a non-verbose format - var links = []; - for (var i in this.links) { - //links is an OBJECT - var link = this.links[i]; - if (!link.serialize) { - //weird bug I havent solved yet - console.warn( - "weird LLink bug, link info is not a LLink but a regular object" - ); - var link2 = new LLink(); - for (var i in link) { - link2[i] = link[i]; - } - this.links[i] = link2; - link = link2; - } - - links.push(link.serialize()); - } - - var groups_info = []; - for (var i = 0; i < this._groups.length; ++i) { - groups_info.push(this._groups[i].serialize()); - } - - var data = { - last_node_id: this.last_node_id, - last_link_id: this.last_link_id, - nodes: nodes_info, - links: links, - groups: groups_info, - config: this.config, - version: LiteGraph.VERSION - }; - - return data; - }; - - /** - * Configure a graph from a JSON string - * @method configure - * @param {String} str configure a graph from a JSON string - * @param {Boolean} returns if there was any error parsing - */ - LGraph.prototype.configure = function(data, keep_old) { - if (!data) { - return; - } - - if (!keep_old) { - this.clear(); - } - - var nodes = data.nodes; - - //decode links info (they are very verbose) - if (data.links && data.links.constructor === Array) { - var links = []; - for (var i = 0; i < data.links.length; ++i) { - var link_data = data.links[i]; - if(!link_data) //weird bug - { - console.warn("serialized graph link data contains errors, skipping."); - continue; - } - var link = new LLink(); - link.configure(link_data); - links[link.id] = link; - } - data.links = links; - } - - //copy all stored fields - for (var i in data) { - if(i == "nodes" || i == "groups" ) //links must be accepted - continue; - this[i] = data[i]; - } - - var error = false; - - //create nodes - this._nodes = []; - if (nodes) { - for (var i = 0, l = nodes.length; i < l; ++i) { - var n_info = nodes[i]; //stored info - var node = LiteGraph.createNode(n_info.type, n_info.title); - if (!node) { - if (LiteGraph.debug) { - console.log( - "Node not found or has errors: " + n_info.type - ); - } - - //in case of error we create a replacement node to avoid losing info - node = new LGraphNode(); - node.last_serialization = n_info; - node.has_errors = true; - error = true; - //continue; - } - - node.id = n_info.id; //id it or it will create a new id - this.add(node, true); //add before configure, otherwise configure cannot create links - } - - //configure nodes afterwards so they can reach each other - for (var i = 0, l = nodes.length; i < l; ++i) { - var n_info = nodes[i]; - var node = this.getNodeById(n_info.id); - if (node) { - node.configure(n_info); - } - } - } - - //groups - this._groups.length = 0; - if (data.groups) { - for (var i = 0; i < data.groups.length; ++i) { - var group = new LiteGraph.LGraphGroup(); - group.configure(data.groups[i]); - this.add(group); - } - } - - this.updateExecutionOrder(); - this._version++; - this.setDirtyCanvas(true, true); - return error; - }; - - LGraph.prototype.load = function(url) { - var that = this; - var req = new XMLHttpRequest(); - req.open("GET", url, true); - req.send(null); - req.onload = function(oEvent) { - if (req.status !== 200) { - console.error("Error loading graph:", req.status, req.response); - return; - } - var data = JSON.parse(req.response); - that.configure(data); - }; - req.onerror = function(err) { - console.error("Error loading graph:", err); - }; - }; - - LGraph.prototype.onNodeTrace = function(node, msg, color) { - //TODO - }; - - //this is the class in charge of storing link information - function LLink(id, type, origin_id, origin_slot, target_id, target_slot) { - this.id = id; - this.type = type; - this.origin_id = origin_id; - this.origin_slot = origin_slot; - this.target_id = target_id; - this.target_slot = target_slot; - - this._data = null; - this._pos = new Float32Array(2); //center - } - - LLink.prototype.configure = function(o) { - if (o.constructor === Array) { - this.id = o[0]; - this.origin_id = o[1]; - this.origin_slot = o[2]; - this.target_id = o[3]; - this.target_slot = o[4]; - this.type = o[5]; - } else { - this.id = o.id; - this.type = o.type; - this.origin_id = o.origin_id; - this.origin_slot = o.origin_slot; - this.target_id = o.target_id; - this.target_slot = o.target_slot; - } - }; - - LLink.prototype.serialize = function() { - return [ - this.id, - this.origin_id, - this.origin_slot, - this.target_id, - this.target_slot, - this.type - ]; - }; - - LiteGraph.LLink = LLink; - - // ************************************************************* - // Node CLASS ******* - // ************************************************************* - - /* - title: string - pos: [x,y] - size: [x,y] - - input|output: every connection - + { name:string, type:string, pos: [x,y]=Optional, direction: "input"|"output", links: Array }); - - general properties: - + clip_area: if you render outside the node, it will be clipped - + unsafe_execution: not allowed for safe execution - + skip_repeated_outputs: when adding new outputs, it wont show if there is one already connected - + resizable: if set to false it wont be resizable with the mouse - + horizontal: slots are distributed horizontally - + widgets_start_y: widgets start at y distance from the top of the node - - flags object: - + collapsed: if it is collapsed - - supported callbacks: - + onAdded: when added to graph (warning: this is called BEFORE the node is configured when loading) - + onRemoved: when removed from graph - + onStart: when the graph starts playing - + onStop: when the graph stops playing - + onDrawForeground: render the inside widgets inside the node - + onDrawBackground: render the background area inside the node (only in edit mode) - + onMouseDown - + onMouseMove - + onMouseUp - + onMouseEnter - + onMouseLeave - + onExecute: execute the node - + onPropertyChanged: when a property is changed in the panel (return true to skip default behaviour) - + onGetInputs: returns an array of possible inputs - + onGetOutputs: returns an array of possible outputs - + onBounding: in case this node has a bigger bounding than the node itself (the callback receives the bounding as [x,y,w,h]) - + onDblClick: double clicked in the node - + onInputDblClick: input slot double clicked (can be used to automatically create a node connected) - + onOutputDblClick: output slot double clicked (can be used to automatically create a node connected) - + onConfigure: called after the node has been configured - + onSerialize: to add extra info when serializing (the callback receives the object that should be filled with the data) - + onSelected - + onDeselected - + onDropItem : DOM item dropped over the node - + onDropFile : file dropped over the node - + onConnectInput : if returns false the incoming connection will be canceled - + onConnectionsChange : a connection changed (new one or removed) (LiteGraph.INPUT or LiteGraph.OUTPUT, slot, true if connected, link_info, input_info ) - + onAction: action slot triggered - + getExtraMenuOptions: to add option to context menu -*/ - - /** - * Base Class for all the node type classes - * @class LGraphNode - * @param {String} name a name for the node - */ - - function LGraphNode(title) { - this._ctor(title); - } - - global.LGraphNode = LiteGraph.LGraphNode = LGraphNode; - - LGraphNode.prototype._ctor = function(title) { - this.title = title || "Unnamed"; - this.size = [LiteGraph.NODE_WIDTH, 60]; - this.graph = null; - - this._pos = new Float32Array(10, 10); - - Object.defineProperty(this, "pos", { - set: function(v) { - if (!v || v.length < 2) { - return; - } - this._pos[0] = v[0]; - this._pos[1] = v[1]; - }, - get: function() { - return this._pos; - }, - enumerable: true - }); - - this.id = -1; //not know till not added - this.type = null; - - //inputs available: array of inputs - this.inputs = []; - this.outputs = []; - this.connections = []; - - //local data - this.properties = {}; //for the values - this.properties_info = []; //for the info - - this.flags = {}; - }; - - /** - * configure a node from an object containing the serialized info - * @method configure - */ - LGraphNode.prototype.configure = function(info) { - if (this.graph) { - this.graph._version++; - } - for (var j in info) { - if (j == "properties") { - //i don't want to clone properties, I want to reuse the old container - for (var k in info.properties) { - this.properties[k] = info.properties[k]; - if (this.onPropertyChanged) { - this.onPropertyChanged( k, info.properties[k] ); - } - } - continue; - } - - if (info[j] == null) { - continue; - } else if (typeof info[j] == "object") { - //object - if (this[j] && this[j].configure) { - this[j].configure(info[j]); - } else { - this[j] = LiteGraph.cloneObject(info[j], this[j]); - } - } //value - else { - this[j] = info[j]; - } - } - - if (!info.title) { - this.title = this.constructor.title; - } - - if (this.onConnectionsChange) { - if (this.inputs) { - for (var i = 0; i < this.inputs.length; ++i) { - var input = this.inputs[i]; - var link_info = this.graph - ? this.graph.links[input.link] - : null; - this.onConnectionsChange( - LiteGraph.INPUT, - i, - true, - link_info, - input - ); //link_info has been created now, so its updated - } - } - - if (this.outputs) { - for (var i = 0; i < this.outputs.length; ++i) { - var output = this.outputs[i]; - if (!output.links) { - continue; - } - for (var j = 0; j < output.links.length; ++j) { - var link_info = this.graph - ? this.graph.links[output.links[j]] - : null; - this.onConnectionsChange( - LiteGraph.OUTPUT, - i, - true, - link_info, - output - ); //link_info has been created now, so its updated - } - } - } - } - - if( this.widgets ) - { - for (var i = 0; i < this.widgets.length; ++i) - { - var w = this.widgets[i]; - if(!w) - continue; - if(w.options && w.options.property && this.properties[ w.options.property ]) - w.value = JSON.parse( JSON.stringify( this.properties[ w.options.property ] ) ); - } - if (info.widgets_values) { - for (var i = 0; i < info.widgets_values.length; ++i) { - if (this.widgets[i]) { - this.widgets[i].value = info.widgets_values[i]; - } - } - } - } - - if (this.onConfigure) { - this.onConfigure(info); - } - }; - - /** - * serialize the content - * @method serialize - */ - - LGraphNode.prototype.serialize = function() { - //create serialization object - var o = { - id: this.id, - type: this.type, - pos: this.pos, - size: this.size, - flags: LiteGraph.cloneObject(this.flags), - order: this.order, - mode: this.mode - }; - - //special case for when there were errors - if (this.constructor === LGraphNode && this.last_serialization) { - return this.last_serialization; - } - - if (this.inputs) { - o.inputs = this.inputs; - } - - if (this.outputs) { - //clear outputs last data (because data in connections is never serialized but stored inside the outputs info) - for (var i = 0; i < this.outputs.length; i++) { - delete this.outputs[i]._data; - } - o.outputs = this.outputs; - } - - if (this.title && this.title != this.constructor.title) { - o.title = this.title; - } - - if (this.properties) { - o.properties = LiteGraph.cloneObject(this.properties); - } - - if (this.widgets && this.serialize_widgets) { - o.widgets_values = []; - for (var i = 0; i < this.widgets.length; ++i) { - if(this.widgets[i]) - o.widgets_values[i] = this.widgets[i].value; - else - o.widgets_values[i] = null; - } - } - - if (!o.type) { - o.type = this.constructor.type; - } - - if (this.color) { - o.color = this.color; - } - if (this.bgcolor) { - o.bgcolor = this.bgcolor; - } - if (this.boxcolor) { - o.boxcolor = this.boxcolor; - } - if (this.shape) { - o.shape = this.shape; - } - - if (this.onSerialize) { - if (this.onSerialize(o)) { - console.warn( - "node onSerialize shouldnt return anything, data should be stored in the object pass in the first parameter" - ); - } - } - - return o; - }; - - /* Creates a clone of this node */ - LGraphNode.prototype.clone = function() { - var node = LiteGraph.createNode(this.type); - if (!node) { - return null; - } - - //we clone it because serialize returns shared containers - var data = LiteGraph.cloneObject(this.serialize()); - - //remove links - if (data.inputs) { - for (var i = 0; i < data.inputs.length; ++i) { - data.inputs[i].link = null; - } - } - - if (data.outputs) { - for (var i = 0; i < data.outputs.length; ++i) { - if (data.outputs[i].links) { - data.outputs[i].links.length = 0; - } - } - } - - delete data["id"]; - //remove links - node.configure(data); - - return node; - }; - - /** - * serialize and stringify - * @method toString - */ - - LGraphNode.prototype.toString = function() { - return JSON.stringify(this.serialize()); - }; - //LGraphNode.prototype.deserialize = function(info) {} //this cannot be done from within, must be done in LiteGraph - - /** - * get the title string - * @method getTitle - */ - - LGraphNode.prototype.getTitle = function() { - return this.title || this.constructor.title; - }; - - /** - * sets the value of a property - * @method setProperty - * @param {String} name - * @param {*} value - */ - LGraphNode.prototype.setProperty = function(name, value) { - if (!this.properties) { - this.properties = {}; - } - if( value === this.properties[name] ) - return; - var prev_value = this.properties[name]; - this.properties[name] = value; - if (this.onPropertyChanged) { - if( this.onPropertyChanged(name, value, prev_value) === false ) //abort change - this.properties[name] = prev_value; - } - if(this.widgets) //widgets could be linked to properties - for(var i = 0; i < this.widgets.length; ++i) - { - var w = this.widgets[i]; - if(!w) - continue; - if(w.options.property == name) - { - w.value = value; - break; - } - } - }; - - // Execution ************************* - /** - * sets the output data - * @method setOutputData - * @param {number} slot - * @param {*} data - */ - LGraphNode.prototype.setOutputData = function(slot, data) { - if (!this.outputs) { - return; - } - - //this maybe slow and a niche case - //if(slot && slot.constructor === String) - // slot = this.findOutputSlot(slot); - - if (slot == -1 || slot >= this.outputs.length) { - return; - } - - var output_info = this.outputs[slot]; - if (!output_info) { - return; - } - - //store data in the output itself in case we want to debug - output_info._data = data; - - //if there are connections, pass the data to the connections - if (this.outputs[slot].links) { - for (var i = 0; i < this.outputs[slot].links.length; i++) { - var link_id = this.outputs[slot].links[i]; - var link = this.graph.links[link_id]; - if(link) - link.data = data; - } - } - }; - - /** - * sets the output data type, useful when you want to be able to overwrite the data type - * @method setOutputDataType - * @param {number} slot - * @param {String} datatype - */ - LGraphNode.prototype.setOutputDataType = function(slot, type) { - if (!this.outputs) { - return; - } - if (slot == -1 || slot >= this.outputs.length) { - return; - } - var output_info = this.outputs[slot]; - if (!output_info) { - return; - } - //store data in the output itself in case we want to debug - output_info.type = type; - - //if there are connections, pass the data to the connections - if (this.outputs[slot].links) { - for (var i = 0; i < this.outputs[slot].links.length; i++) { - var link_id = this.outputs[slot].links[i]; - this.graph.links[link_id].type = type; - } - } - }; - - /** - * Retrieves the input data (data traveling through the connection) from one slot - * @method getInputData - * @param {number} slot - * @param {boolean} force_update if set to true it will force the connected node of this slot to output data into this link - * @return {*} data or if it is not connected returns undefined - */ - LGraphNode.prototype.getInputData = function(slot, force_update) { - if (!this.inputs) { - return; - } //undefined; - - if (slot >= this.inputs.length || this.inputs[slot].link == null) { - return; - } - - var link_id = this.inputs[slot].link; - var link = this.graph.links[link_id]; - if (!link) { - //bug: weird case but it happens sometimes - return null; - } - - if (!force_update) { - return link.data; - } - - //special case: used to extract data from the incoming connection before the graph has been executed - var node = this.graph.getNodeById(link.origin_id); - if (!node) { - return link.data; - } - - if (node.updateOutputData) { - node.updateOutputData(link.origin_slot); - } else if (node.onExecute) { - node.onExecute(); - } - - return link.data; - }; - - /** - * Retrieves the input data type (in case this supports multiple input types) - * @method getInputDataType - * @param {number} slot - * @return {String} datatype in string format - */ - LGraphNode.prototype.getInputDataType = function(slot) { - if (!this.inputs) { - return null; - } //undefined; - - if (slot >= this.inputs.length || this.inputs[slot].link == null) { - return null; - } - var link_id = this.inputs[slot].link; - var link = this.graph.links[link_id]; - if (!link) { - //bug: weird case but it happens sometimes - return null; - } - var node = this.graph.getNodeById(link.origin_id); - if (!node) { - return link.type; - } - var output_info = node.outputs[link.origin_slot]; - if (output_info) { - return output_info.type; - } - return null; - }; - - /** - * Retrieves the input data from one slot using its name instead of slot number - * @method getInputDataByName - * @param {String} slot_name - * @param {boolean} force_update if set to true it will force the connected node of this slot to output data into this link - * @return {*} data or if it is not connected returns null - */ - LGraphNode.prototype.getInputDataByName = function( - slot_name, - force_update - ) { - var slot = this.findInputSlot(slot_name); - if (slot == -1) { - return null; - } - return this.getInputData(slot, force_update); - }; - - /** - * tells you if there is a connection in one input slot - * @method isInputConnected - * @param {number} slot - * @return {boolean} - */ - LGraphNode.prototype.isInputConnected = function(slot) { - if (!this.inputs) { - return false; - } - return slot < this.inputs.length && this.inputs[slot].link != null; - }; - - /** - * tells you info about an input connection (which node, type, etc) - * @method getInputInfo - * @param {number} slot - * @return {Object} object or null { link: id, name: string, type: string or 0 } - */ - LGraphNode.prototype.getInputInfo = function(slot) { - if (!this.inputs) { - return null; - } - if (slot < this.inputs.length) { - return this.inputs[slot]; - } - return null; - }; - - /** - * returns the node connected in the input slot - * @method getInputNode - * @param {number} slot - * @return {LGraphNode} node or null - */ - LGraphNode.prototype.getInputNode = function(slot) { - if (!this.inputs) { - return null; - } - if (slot >= this.inputs.length) { - return null; - } - var input = this.inputs[slot]; - if (!input || input.link === null) { - return null; - } - var link_info = this.graph.links[input.link]; - if (!link_info) { - return null; - } - return this.graph.getNodeById(link_info.origin_id); - }; - - /** - * returns the value of an input with this name, otherwise checks if there is a property with that name - * @method getInputOrProperty - * @param {string} name - * @return {*} value - */ - LGraphNode.prototype.getInputOrProperty = function(name) { - if (!this.inputs || !this.inputs.length) { - return this.properties ? this.properties[name] : null; - } - - for (var i = 0, l = this.inputs.length; i < l; ++i) { - var input_info = this.inputs[i]; - if (name == input_info.name && input_info.link != null) { - var link = this.graph.links[input_info.link]; - if (link) { - return link.data; - } - } - } - return this.properties[name]; - }; - - /** - * tells you the last output data that went in that slot - * @method getOutputData - * @param {number} slot - * @return {Object} object or null - */ - LGraphNode.prototype.getOutputData = function(slot) { - if (!this.outputs) { - return null; - } - if (slot >= this.outputs.length) { - return null; - } - - var info = this.outputs[slot]; - return info._data; - }; - - /** - * tells you info about an output connection (which node, type, etc) - * @method getOutputInfo - * @param {number} slot - * @return {Object} object or null { name: string, type: string, links: [ ids of links in number ] } - */ - LGraphNode.prototype.getOutputInfo = function(slot) { - if (!this.outputs) { - return null; - } - if (slot < this.outputs.length) { - return this.outputs[slot]; - } - return null; - }; - - /** - * tells you if there is a connection in one output slot - * @method isOutputConnected - * @param {number} slot - * @return {boolean} - */ - LGraphNode.prototype.isOutputConnected = function(slot) { - if (!this.outputs) { - return false; - } - return ( - slot < this.outputs.length && - this.outputs[slot].links && - this.outputs[slot].links.length - ); - }; - - /** - * tells you if there is any connection in the output slots - * @method isAnyOutputConnected - * @return {boolean} - */ - LGraphNode.prototype.isAnyOutputConnected = function() { - if (!this.outputs) { - return false; - } - for (var i = 0; i < this.outputs.length; ++i) { - if (this.outputs[i].links && this.outputs[i].links.length) { - return true; - } - } - return false; - }; - - /** - * retrieves all the nodes connected to this output slot - * @method getOutputNodes - * @param {number} slot - * @return {array} - */ - LGraphNode.prototype.getOutputNodes = function(slot) { - if (!this.outputs || this.outputs.length == 0) { - return null; - } - - if (slot >= this.outputs.length) { - return null; - } - - var output = this.outputs[slot]; - if (!output.links || output.links.length == 0) { - return null; - } - - var r = []; - for (var i = 0; i < output.links.length; i++) { - var link_id = output.links[i]; - var link = this.graph.links[link_id]; - if (link) { - var target_node = this.graph.getNodeById(link.target_id); - if (target_node) { - r.push(target_node); - } - } - } - return r; - }; - - /** - * Triggers an event in this node, this will trigger any output with the same name - * @method trigger - * @param {String} event name ( "on_play", ... ) if action is equivalent to false then the event is send to all - * @param {*} param - */ - LGraphNode.prototype.trigger = function(action, param) { - if (!this.outputs || !this.outputs.length) { - return; - } - - if (this.graph) - this.graph._last_trigger_time = LiteGraph.getTime(); - - for (var i = 0; i < this.outputs.length; ++i) { - var output = this.outputs[i]; - if ( !output || output.type !== LiteGraph.EVENT || (action && output.name != action) ) - continue; - this.triggerSlot(i, param); - } - }; - - /** - * Triggers an slot event in this node - * @method triggerSlot - * @param {Number} slot the index of the output slot - * @param {*} param - * @param {Number} link_id [optional] in case you want to trigger and specific output link in a slot - */ - LGraphNode.prototype.triggerSlot = function(slot, param, link_id) { - if (!this.outputs) { - return; - } - - var output = this.outputs[slot]; - if (!output) { - return; - } - - var links = output.links; - if (!links || !links.length) { - return; - } - - if (this.graph) { - this.graph._last_trigger_time = LiteGraph.getTime(); - } - - //for every link attached here - for (var k = 0; k < links.length; ++k) { - var id = links[k]; - if (link_id != null && link_id != id) { - //to skip links - continue; - } - var link_info = this.graph.links[links[k]]; - if (!link_info) { - //not connected - continue; - } - link_info._last_time = LiteGraph.getTime(); - var node = this.graph.getNodeById(link_info.target_id); - if (!node) { - //node not found? - continue; - } - - //used to mark events in graph - var target_connection = node.inputs[link_info.target_slot]; - - if (node.onAction) { - node.onAction(target_connection.name, param); - } else if (node.mode === LiteGraph.ON_TRIGGER) { - if (node.onExecute) { - node.onExecute(param); - } - } - } - }; - - /** - * clears the trigger slot animation - * @method clearTriggeredSlot - * @param {Number} slot the index of the output slot - * @param {Number} link_id [optional] in case you want to trigger and specific output link in a slot - */ - LGraphNode.prototype.clearTriggeredSlot = function(slot, link_id) { - if (!this.outputs) { - return; - } - - var output = this.outputs[slot]; - if (!output) { - return; - } - - var links = output.links; - if (!links || !links.length) { - return; - } - - //for every link attached here - for (var k = 0; k < links.length; ++k) { - var id = links[k]; - if (link_id != null && link_id != id) { - //to skip links - continue; - } - var link_info = this.graph.links[links[k]]; - if (!link_info) { - //not connected - continue; - } - link_info._last_time = 0; - } - }; - - /** - * changes node size and triggers callback - * @method setSize - * @param {vec2} size - */ - LGraphNode.prototype.setSize = function(size) - { - this.size = size; - if(this.onResize) - this.onResize(this.size); - } - - /** - * add a new property to this node - * @method addProperty - * @param {string} name - * @param {*} default_value - * @param {string} type string defining the output type ("vec3","number",...) - * @param {Object} extra_info this can be used to have special properties of the property (like values, etc) - */ - LGraphNode.prototype.addProperty = function( - name, - default_value, - type, - extra_info - ) { - var o = { name: name, type: type, default_value: default_value }; - if (extra_info) { - for (var i in extra_info) { - o[i] = extra_info[i]; - } - } - if (!this.properties_info) { - this.properties_info = []; - } - this.properties_info.push(o); - if (!this.properties) { - this.properties = {}; - } - this.properties[name] = default_value; - return o; - }; - - //connections - - /** - * add a new output slot to use in this node - * @method addOutput - * @param {string} name - * @param {string} type string defining the output type ("vec3","number",...) - * @param {Object} extra_info this can be used to have special properties of an output (label, special color, position, etc) - */ - LGraphNode.prototype.addOutput = function(name, type, extra_info) { - var o = { name: name, type: type, links: null }; - if (extra_info) { - for (var i in extra_info) { - o[i] = extra_info[i]; - } - } - - if (!this.outputs) { - this.outputs = []; - } - this.outputs.push(o); - if (this.onOutputAdded) { - this.onOutputAdded(o); - } - this.setSize( this.computeSize() ); - this.setDirtyCanvas(true, true); - return o; - }; - - /** - * add a new output slot to use in this node - * @method addOutputs - * @param {Array} array of triplets like [[name,type,extra_info],[...]] - */ - LGraphNode.prototype.addOutputs = function(array) { - for (var i = 0; i < array.length; ++i) { - var info = array[i]; - var o = { name: info[0], type: info[1], link: null }; - if (array[2]) { - for (var j in info[2]) { - o[j] = info[2][j]; - } - } - - if (!this.outputs) { - this.outputs = []; - } - this.outputs.push(o); - if (this.onOutputAdded) { - this.onOutputAdded(o); - } - } - - this.setSize( this.computeSize() ); - this.setDirtyCanvas(true, true); - }; - - /** - * remove an existing output slot - * @method removeOutput - * @param {number} slot - */ - LGraphNode.prototype.removeOutput = function(slot) { - this.disconnectOutput(slot); - this.outputs.splice(slot, 1); - for (var i = slot; i < this.outputs.length; ++i) { - if (!this.outputs[i] || !this.outputs[i].links) { - continue; - } - var links = this.outputs[i].links; - for (var j = 0; j < links.length; ++j) { - var link = this.graph.links[links[j]]; - if (!link) { - continue; - } - link.origin_slot -= 1; - } - } - - this.setSize( this.computeSize() ); - if (this.onOutputRemoved) { - this.onOutputRemoved(slot); - } - this.setDirtyCanvas(true, true); - }; - - /** - * add a new input slot to use in this node - * @method addInput - * @param {string} name - * @param {string} type string defining the input type ("vec3","number",...), it its a generic one use 0 - * @param {Object} extra_info this can be used to have special properties of an input (label, color, position, etc) - */ - LGraphNode.prototype.addInput = function(name, type, extra_info) { - type = type || 0; - var o = { name: name, type: type, link: null }; - if (extra_info) { - for (var i in extra_info) { - o[i] = extra_info[i]; - } - } - - if (!this.inputs) { - this.inputs = []; - } - this.inputs.push(o); - this.setSize( this.computeSize() ); - if (this.onInputAdded) { - this.onInputAdded(o); - } - this.setDirtyCanvas(true, true); - return o; - }; - - /** - * add several new input slots in this node - * @method addInputs - * @param {Array} array of triplets like [[name,type,extra_info],[...]] - */ - LGraphNode.prototype.addInputs = function(array) { - for (var i = 0; i < array.length; ++i) { - var info = array[i]; - var o = { name: info[0], type: info[1], link: null }; - if (array[2]) { - for (var j in info[2]) { - o[j] = info[2][j]; - } - } - - if (!this.inputs) { - this.inputs = []; - } - this.inputs.push(o); - if (this.onInputAdded) { - this.onInputAdded(o); - } - } - - this.setSize( this.computeSize() ); - this.setDirtyCanvas(true, true); - }; - - /** - * remove an existing input slot - * @method removeInput - * @param {number} slot - */ - LGraphNode.prototype.removeInput = function(slot) { - this.disconnectInput(slot); - this.inputs.splice(slot, 1); - for (var i = slot; i < this.inputs.length; ++i) { - if (!this.inputs[i]) { - continue; - } - var link = this.graph.links[this.inputs[i].link]; - if (!link) { - continue; - } - link.target_slot -= 1; - } - this.setSize( this.computeSize() ); - if (this.onInputRemoved) { - this.onInputRemoved(slot); - } - this.setDirtyCanvas(true, true); - }; - - /** - * add an special connection to this node (used for special kinds of graphs) - * @method addConnection - * @param {string} name - * @param {string} type string defining the input type ("vec3","number",...) - * @param {[x,y]} pos position of the connection inside the node - * @param {string} direction if is input or output - */ - LGraphNode.prototype.addConnection = function(name, type, pos, direction) { - var o = { - name: name, - type: type, - pos: pos, - direction: direction, - links: null - }; - this.connections.push(o); - return o; - }; - - /** - * computes the minimum size of a node according to its inputs and output slots - * @method computeSize - * @param {number} minHeight - * @return {number} the total size - */ - LGraphNode.prototype.computeSize = function(out) { - if (this.constructor.size) { - return this.constructor.size.concat(); - } - - var rows = Math.max( - this.inputs ? this.inputs.length : 1, - this.outputs ? this.outputs.length : 1 - ); - var size = out || new Float32Array([0, 0]); - rows = Math.max(rows, 1); - var font_size = LiteGraph.NODE_TEXT_SIZE; //although it should be graphcanvas.inner_text_font size - - var font_size = font_size; - var title_width = compute_text_size(this.title); - var input_width = 0; - var output_width = 0; - - if (this.inputs) { - for (var i = 0, l = this.inputs.length; i < l; ++i) { - var input = this.inputs[i]; - var text = input.label || input.name || ""; - var text_width = compute_text_size(text); - if (input_width < text_width) { - input_width = text_width; - } - } - } - - if (this.outputs) { - for (var i = 0, l = this.outputs.length; i < l; ++i) { - var output = this.outputs[i]; - var text = output.label || output.name || ""; - var text_width = compute_text_size(text); - if (output_width < text_width) { - output_width = text_width; - } - } - } - - size[0] = Math.max(input_width + output_width + 10, title_width); - size[0] = Math.max(size[0], LiteGraph.NODE_WIDTH); - if (this.widgets && this.widgets.length) { - size[0] = Math.max(size[0], LiteGraph.NODE_WIDTH * 1.5); - } - - size[1] = (this.constructor.slot_start_y || 0) + rows * LiteGraph.NODE_SLOT_HEIGHT; - - var widgets_height = 0; - if (this.widgets && this.widgets.length) { - for (var i = 0, l = this.widgets.length; i < l; ++i) { - if (this.widgets[i].computeSize) - widgets_height += this.widgets[i].computeSize(size[0])[1] + 4; - else - widgets_height += LiteGraph.NODE_WIDGET_HEIGHT + 4; - } - widgets_height += 8; - } - - //compute height using widgets height - if( this.widgets_up ) - size[1] = Math.max( size[1], widgets_height ); - else if( this.widgets_start_y != null ) - size[1] = Math.max( size[1], widgets_height + this.widgets_start_y ); - else - size[1] += widgets_height; - - function compute_text_size(text) { - if (!text) { - return 0; - } - return font_size * text.length * 0.6; - } - - if ( - this.constructor.min_height && - size[1] < this.constructor.min_height - ) { - size[1] = this.constructor.min_height; - } - - size[1] += 6; //margin - - return size; - }; - - /** - * returns all the info available about a property of this node. - * - * @method getPropertyInfo - * @param {String} property name of the property - * @return {Object} the object with all the available info - */ - LGraphNode.prototype.getPropertyInfo = function( property ) - { - var info = null; - - //there are several ways to define info about a property - //legacy mode - if (this.properties_info) { - for (var i = 0; i < this.properties_info.length; ++i) { - if (this.properties_info[i].name == property) { - info = this.properties_info[i]; - break; - } - } - } - //litescene mode using the constructor - if(this.constructor["@" + property]) - info = this.constructor["@" + property]; - - //litescene mode using the constructor - if (this.onGetPropertyInfo) { - info = this.onGetPropertyInfo(property); - } - - if (!info) - info = {}; - if(!info.type) - info.type = typeof this.properties[property]; - - return info; - } - - /** - * Defines a widget inside the node, it will be rendered on top of the node, you can control lots of properties - * - * @method addWidget - * @param {String} type the widget type (could be "number","string","combo" - * @param {String} name the text to show on the widget - * @param {String} value the default value - * @param {Function|String} callback function to call when it changes (optionally, it can be the name of the property to modify) - * @param {Object} options the object that contains special properties of this widget - * @return {Object} the created widget object - */ - LGraphNode.prototype.addWidget = function( type, name, value, callback, options ) - { - if (!this.widgets) { - this.widgets = []; - } - - if(!options && callback && callback.constructor === Object) - { - options = callback; - callback = null; - } - - if(options && options.constructor === String) //options can be the property name - options = { property: options }; - - if(callback && callback.constructor === String) //callback can be the property name - { - if(!options) - options = {}; - options.property = callback; - callback = null; - } - - if(callback && callback.constructor !== Function) - { - console.warn("addWidget: callback must be a function"); - callback = null; - } - - var w = { - type: type.toLowerCase(), - name: name, - value: value, - callback: callback, - options: options || {} - }; - - if (w.options.y !== undefined) { - w.y = w.options.y; - } - - if (!callback && !w.options.callback && !w.options.property) { - console.warn("LiteGraph addWidget(...) without a callback or property assigned"); - } - if (type == "combo" && !w.options.values) { - throw "LiteGraph addWidget('combo',...) requires to pass values in options: { values:['red','blue'] }"; - } - this.widgets.push(w); - this.setSize( this.computeSize() ); - return w; - }; - - LGraphNode.prototype.addCustomWidget = function(custom_widget) { - if (!this.widgets) { - this.widgets = []; - } - this.widgets.push(custom_widget); - return custom_widget; - }; - - /** - * returns the bounding of the object, used for rendering purposes - * bounding is: [topleft_cornerx, topleft_cornery, width, height] - * @method getBounding - * @return {Float32Array[4]} the total size - */ - LGraphNode.prototype.getBounding = function(out) { - out = out || new Float32Array(4); - out[0] = this.pos[0] - 4; - out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT; - out[2] = this.size[0] + 4; - out[3] = this.size[1] + LiteGraph.NODE_TITLE_HEIGHT; - - if (this.onBounding) { - this.onBounding(out); - } - return out; - }; - - /** - * checks if a point is inside the shape of a node - * @method isPointInside - * @param {number} x - * @param {number} y - * @return {boolean} - */ - LGraphNode.prototype.isPointInside = function(x, y, margin, skip_title) { - margin = margin || 0; - - var margin_top = this.graph && this.graph.isLive() ? 0 : LiteGraph.NODE_TITLE_HEIGHT; - if (skip_title) { - margin_top = 0; - } - if (this.flags && this.flags.collapsed) { - //if ( distance([x,y], [this.pos[0] + this.size[0]*0.5, this.pos[1] + this.size[1]*0.5]) < LiteGraph.NODE_COLLAPSED_RADIUS) - if ( - isInsideRectangle( - x, - y, - this.pos[0] - margin, - this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT - margin, - (this._collapsed_width || LiteGraph.NODE_COLLAPSED_WIDTH) + - 2 * margin, - LiteGraph.NODE_TITLE_HEIGHT + 2 * margin - ) - ) { - return true; - } - } else if ( - this.pos[0] - 4 - margin < x && - this.pos[0] + this.size[0] + 4 + margin > x && - this.pos[1] - margin_top - margin < y && - this.pos[1] + this.size[1] + margin > y - ) { - return true; - } - return false; - }; - - /** - * checks if a point is inside a node slot, and returns info about which slot - * @method getSlotInPosition - * @param {number} x - * @param {number} y - * @return {Object} if found the object contains { input|output: slot object, slot: number, link_pos: [x,y] } - */ - LGraphNode.prototype.getSlotInPosition = function(x, y) { - //search for inputs - var link_pos = new Float32Array(2); - if (this.inputs) { - for (var i = 0, l = this.inputs.length; i < l; ++i) { - var input = this.inputs[i]; - this.getConnectionPos(true, i, link_pos); - if ( - isInsideRectangle( - x, - y, - link_pos[0] - 10, - link_pos[1] - 5, - 20, - 10 - ) - ) { - return { input: input, slot: i, link_pos: link_pos }; - } - } - } - - if (this.outputs) { - for (var i = 0, l = this.outputs.length; i < l; ++i) { - var output = this.outputs[i]; - this.getConnectionPos(false, i, link_pos); - if ( - isInsideRectangle( - x, - y, - link_pos[0] - 10, - link_pos[1] - 5, - 20, - 10 - ) - ) { - return { output: output, slot: i, link_pos: link_pos }; - } - } - } - - return null; - }; - - /** - * returns the input slot with a given name (used for dynamic slots), -1 if not found - * @method findInputSlot - * @param {string} name the name of the slot - * @return {number} the slot (-1 if not found) - */ - LGraphNode.prototype.findInputSlot = function(name) { - if (!this.inputs) { - return -1; - } - for (var i = 0, l = this.inputs.length; i < l; ++i) { - if (name == this.inputs[i].name) { - return i; - } - } - return -1; - }; - - /** - * returns the output slot with a given name (used for dynamic slots), -1 if not found - * @method findOutputSlot - * @param {string} name the name of the slot - * @return {number} the slot (-1 if not found) - */ - LGraphNode.prototype.findOutputSlot = function(name) { - if (!this.outputs) { - return -1; - } - for (var i = 0, l = this.outputs.length; i < l; ++i) { - if (name == this.outputs[i].name) { - return i; - } - } - return -1; - }; - - /** - * connect this node output to the input of another node - * @method connect - * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) - * @param {LGraphNode} node the target node - * @param {number_or_string} target_slot the input slot of the target node (could be the number of the slot or the string with the name of the slot, or -1 to connect a trigger) - * @return {Object} the link_info is created, otherwise null - */ - LGraphNode.prototype.connect = function(slot, target_node, target_slot) { - target_slot = target_slot || 0; - - if (!this.graph) { - //could be connected before adding it to a graph - console.log( - "Connect: Error, node doesn't belong to any graph. Nodes must be added first to a graph before connecting them." - ); //due to link ids being associated with graphs - return null; - } - - //seek for the output slot - if (slot.constructor === String) { - slot = this.findOutputSlot(slot); - if (slot == -1) { - if (LiteGraph.debug) { - console.log("Connect: Error, no slot of name " + slot); - } - return null; - } - } else if (!this.outputs || slot >= this.outputs.length) { - if (LiteGraph.debug) { - console.log("Connect: Error, slot number not found"); - } - return null; - } - - if (target_node && target_node.constructor === Number) { - target_node = this.graph.getNodeById(target_node); - } - if (!target_node) { - throw "target node is null"; - } - - //avoid loopback - if (target_node == this) { - return null; - } - - //you can specify the slot by name - if (target_slot.constructor === String) { - target_slot = target_node.findInputSlot(target_slot); - if (target_slot == -1) { - if (LiteGraph.debug) { - console.log( - "Connect: Error, no slot of name " + target_slot - ); - } - return null; - } - } else if (target_slot === LiteGraph.EVENT) { - //search for first slot with event? - /* - //create input for trigger - var input = target_node.addInput("onTrigger", LiteGraph.EVENT ); - target_slot = target_node.inputs.length - 1; //last one is the one created - target_node.mode = LiteGraph.ON_TRIGGER; - */ - return null; - } else if ( - !target_node.inputs || - target_slot >= target_node.inputs.length - ) { - if (LiteGraph.debug) { - console.log("Connect: Error, slot number not found"); - } - return null; - } - - //if there is something already plugged there, disconnect - if (target_node.inputs[target_slot].link != null) { - target_node.disconnectInput(target_slot); - } - - //why here?? - //this.setDirtyCanvas(false,true); - //this.graph.connectionChange( this ); - - var output = this.outputs[slot]; - - //allows nodes to block connection - if (target_node.onConnectInput) { - if ( target_node.onConnectInput(target_slot, output.type, output, this, slot) === false ) { - return null; - } - } - - var input = target_node.inputs[target_slot]; - var link_info = null; - - if (LiteGraph.isValidConnection(output.type, input.type)) { - link_info = new LLink( - ++this.graph.last_link_id, - input.type, - this.id, - slot, - target_node.id, - target_slot - ); - - //add to graph links list - this.graph.links[link_info.id] = link_info; - - //connect in output - if (output.links == null) { - output.links = []; - } - output.links.push(link_info.id); - //connect in input - target_node.inputs[target_slot].link = link_info.id; - if (this.graph) { - this.graph._version++; - } - if (this.onConnectionsChange) { - this.onConnectionsChange( - LiteGraph.OUTPUT, - slot, - true, - link_info, - output - ); - } //link_info has been created now, so its updated - if (target_node.onConnectionsChange) { - target_node.onConnectionsChange( - LiteGraph.INPUT, - target_slot, - true, - link_info, - input - ); - } - if (this.graph && this.graph.onNodeConnectionChange) { - this.graph.onNodeConnectionChange( - LiteGraph.INPUT, - target_node, - target_slot, - this, - slot - ); - this.graph.onNodeConnectionChange( - LiteGraph.OUTPUT, - this, - slot, - target_node, - target_slot - ); - } - } - - this.setDirtyCanvas(false, true); - this.graph.connectionChange(this, link_info); - - return link_info; - }; - - /** - * disconnect one output to an specific node - * @method disconnectOutput - * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) - * @param {LGraphNode} target_node the target node to which this slot is connected [Optional, if not target_node is specified all nodes will be disconnected] - * @return {boolean} if it was disconnected successfully - */ - LGraphNode.prototype.disconnectOutput = function(slot, target_node) { - if (slot.constructor === String) { - slot = this.findOutputSlot(slot); - if (slot == -1) { - if (LiteGraph.debug) { - console.log("Connect: Error, no slot of name " + slot); - } - return false; - } - } else if (!this.outputs || slot >= this.outputs.length) { - if (LiteGraph.debug) { - console.log("Connect: Error, slot number not found"); - } - return false; - } - - //get output slot - var output = this.outputs[slot]; - if (!output || !output.links || output.links.length == 0) { - return false; - } - - //one of the output links in this slot - if (target_node) { - if (target_node.constructor === Number) { - target_node = this.graph.getNodeById(target_node); - } - if (!target_node) { - throw "Target Node not found"; - } - - for (var i = 0, l = output.links.length; i < l; i++) { - var link_id = output.links[i]; - var link_info = this.graph.links[link_id]; - - //is the link we are searching for... - if (link_info.target_id == target_node.id) { - output.links.splice(i, 1); //remove here - var input = target_node.inputs[link_info.target_slot]; - input.link = null; //remove there - delete this.graph.links[link_id]; //remove the link from the links pool - if (this.graph) { - this.graph._version++; - } - if (target_node.onConnectionsChange) { - target_node.onConnectionsChange( - LiteGraph.INPUT, - link_info.target_slot, - false, - link_info, - input - ); - } //link_info hasn't been modified so its ok - if (this.onConnectionsChange) { - this.onConnectionsChange( - LiteGraph.OUTPUT, - slot, - false, - link_info, - output - ); - } - if (this.graph && this.graph.onNodeConnectionChange) { - this.graph.onNodeConnectionChange( - LiteGraph.OUTPUT, - this, - slot - ); - } - if (this.graph && this.graph.onNodeConnectionChange) { - this.graph.onNodeConnectionChange( - LiteGraph.OUTPUT, - this, - slot - ); - this.graph.onNodeConnectionChange( - LiteGraph.INPUT, - target_node, - link_info.target_slot - ); - } - break; - } - } - } //all the links in this output slot - else { - for (var i = 0, l = output.links.length; i < l; i++) { - var link_id = output.links[i]; - var link_info = this.graph.links[link_id]; - if (!link_info) { - //bug: it happens sometimes - continue; - } - - var target_node = this.graph.getNodeById(link_info.target_id); - var input = null; - if (this.graph) { - this.graph._version++; - } - if (target_node) { - input = target_node.inputs[link_info.target_slot]; - input.link = null; //remove other side link - if (target_node.onConnectionsChange) { - target_node.onConnectionsChange( - LiteGraph.INPUT, - link_info.target_slot, - false, - link_info, - input - ); - } //link_info hasn't been modified so its ok - if (this.graph && this.graph.onNodeConnectionChange) { - this.graph.onNodeConnectionChange( - LiteGraph.INPUT, - target_node, - link_info.target_slot - ); - } - } - delete this.graph.links[link_id]; //remove the link from the links pool - if (this.onConnectionsChange) { - this.onConnectionsChange( - LiteGraph.OUTPUT, - slot, - false, - link_info, - output - ); - } - if (this.graph && this.graph.onNodeConnectionChange) { - this.graph.onNodeConnectionChange( - LiteGraph.OUTPUT, - this, - slot - ); - this.graph.onNodeConnectionChange( - LiteGraph.INPUT, - target_node, - link_info.target_slot - ); - } - } - output.links = null; - } - - this.setDirtyCanvas(false, true); - this.graph.connectionChange(this); - return true; - }; - - /** - * disconnect one input - * @method disconnectInput - * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) - * @return {boolean} if it was disconnected successfully - */ - LGraphNode.prototype.disconnectInput = function(slot) { - //seek for the output slot - if (slot.constructor === String) { - slot = this.findInputSlot(slot); - if (slot == -1) { - if (LiteGraph.debug) { - console.log("Connect: Error, no slot of name " + slot); - } - return false; - } - } else if (!this.inputs || slot >= this.inputs.length) { - if (LiteGraph.debug) { - console.log("Connect: Error, slot number not found"); - } - return false; - } - - var input = this.inputs[slot]; - if (!input) { - return false; - } - - var link_id = this.inputs[slot].link; - this.inputs[slot].link = null; - - //remove other side - var link_info = this.graph.links[link_id]; - if (link_info) { - var target_node = this.graph.getNodeById(link_info.origin_id); - if (!target_node) { - return false; - } - - var output = target_node.outputs[link_info.origin_slot]; - if (!output || !output.links || output.links.length == 0) { - return false; - } - - //search in the inputs list for this link - for (var i = 0, l = output.links.length; i < l; i++) { - if (output.links[i] == link_id) { - output.links.splice(i, 1); - break; - } - } - - delete this.graph.links[link_id]; //remove from the pool - if (this.graph) { - this.graph._version++; - } - if (this.onConnectionsChange) { - this.onConnectionsChange( - LiteGraph.INPUT, - slot, - false, - link_info, - input - ); - } - if (target_node.onConnectionsChange) { - target_node.onConnectionsChange( - LiteGraph.OUTPUT, - i, - false, - link_info, - output - ); - } - if (this.graph && this.graph.onNodeConnectionChange) { - this.graph.onNodeConnectionChange( - LiteGraph.OUTPUT, - target_node, - i - ); - this.graph.onNodeConnectionChange(LiteGraph.INPUT, this, slot); - } - } - - this.setDirtyCanvas(false, true); - this.graph.connectionChange(this); - return true; - }; - - /** - * returns the center of a connection point in canvas coords - * @method getConnectionPos - * @param {boolean} is_input true if if a input slot, false if it is an output - * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) - * @param {vec2} out [optional] a place to store the output, to free garbage - * @return {[x,y]} the position - **/ - LGraphNode.prototype.getConnectionPos = function( - is_input, - slot_number, - out - ) { - out = out || new Float32Array(2); - var num_slots = 0; - if (is_input && this.inputs) { - num_slots = this.inputs.length; - } - if (!is_input && this.outputs) { - num_slots = this.outputs.length; - } - - var offset = LiteGraph.NODE_SLOT_HEIGHT * 0.5; - - if (this.flags.collapsed) { - var w = this._collapsed_width || LiteGraph.NODE_COLLAPSED_WIDTH; - if (this.horizontal) { - out[0] = this.pos[0] + w * 0.5; - if (is_input) { - out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT; - } else { - out[1] = this.pos[1]; - } - } else { - if (is_input) { - out[0] = this.pos[0]; - } else { - out[0] = this.pos[0] + w; - } - out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT * 0.5; - } - return out; - } - - //weird feature that never got finished - if (is_input && slot_number == -1) { - out[0] = this.pos[0] + LiteGraph.NODE_TITLE_HEIGHT * 0.5; - out[1] = this.pos[1] + LiteGraph.NODE_TITLE_HEIGHT * 0.5; - return out; - } - - //hard-coded pos - if ( - is_input && - num_slots > slot_number && - this.inputs[slot_number].pos - ) { - out[0] = this.pos[0] + this.inputs[slot_number].pos[0]; - out[1] = this.pos[1] + this.inputs[slot_number].pos[1]; - return out; - } else if ( - !is_input && - num_slots > slot_number && - this.outputs[slot_number].pos - ) { - out[0] = this.pos[0] + this.outputs[slot_number].pos[0]; - out[1] = this.pos[1] + this.outputs[slot_number].pos[1]; - return out; - } - - //horizontal distributed slots - if (this.horizontal) { - out[0] = - this.pos[0] + (slot_number + 0.5) * (this.size[0] / num_slots); - if (is_input) { - out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT; - } else { - out[1] = this.pos[1] + this.size[1]; - } - return out; - } - - //default vertical slots - if (is_input) { - out[0] = this.pos[0] + offset; - } else { - out[0] = this.pos[0] + this.size[0] + 1 - offset; - } - out[1] = - this.pos[1] + - (slot_number + 0.7) * LiteGraph.NODE_SLOT_HEIGHT + - (this.constructor.slot_start_y || 0); - return out; - }; - - /* Force align to grid */ - LGraphNode.prototype.alignToGrid = function() { - this.pos[0] = - LiteGraph.CANVAS_GRID_SIZE * - Math.round(this.pos[0] / LiteGraph.CANVAS_GRID_SIZE); - this.pos[1] = - LiteGraph.CANVAS_GRID_SIZE * - Math.round(this.pos[1] / LiteGraph.CANVAS_GRID_SIZE); - }; - - /* Console output */ - LGraphNode.prototype.trace = function(msg) { - if (!this.console) { - this.console = []; - } - this.console.push(msg); - if (this.console.length > LGraphNode.MAX_CONSOLE) { - this.console.shift(); - } - - this.graph.onNodeTrace(this, msg); - }; - - /* Forces to redraw or the main canvas (LGraphNode) or the bg canvas (links) */ - LGraphNode.prototype.setDirtyCanvas = function( - dirty_foreground, - dirty_background - ) { - if (!this.graph) { - return; - } - this.graph.sendActionToCanvas("setDirty", [ - dirty_foreground, - dirty_background - ]); - }; - - LGraphNode.prototype.loadImage = function(url) { - var img = new Image(); - img.src = LiteGraph.node_images_path + url; - img.ready = false; - - var that = this; - img.onload = function() { - this.ready = true; - that.setDirtyCanvas(true); - }; - return img; - }; - - //safe LGraphNode action execution (not sure if safe) - /* -LGraphNode.prototype.executeAction = function(action) -{ - if(action == "") return false; - - if( action.indexOf(";") != -1 || action.indexOf("}") != -1) - { - this.trace("Error: Action contains unsafe characters"); - return false; - } - - var tokens = action.split("("); - var func_name = tokens[0]; - if( typeof(this[func_name]) != "function") - { - this.trace("Error: Action not found on node: " + func_name); - return false; - } - - var code = action; - - try - { - var _foo = eval; - eval = null; - (new Function("with(this) { " + code + "}")).call(this); - eval = _foo; - } - catch (err) - { - this.trace("Error executing action {" + action + "} :" + err); - return false; - } - - return true; -} -*/ - - /* Allows to get onMouseMove and onMouseUp events even if the mouse is out of focus */ - LGraphNode.prototype.captureInput = function(v) { - if (!this.graph || !this.graph.list_of_graphcanvas) { - return; - } - - var list = this.graph.list_of_graphcanvas; - - for (var i = 0; i < list.length; ++i) { - var c = list[i]; - //releasing somebody elses capture?! - if (!v && c.node_capturing_input != this) { - continue; - } - - //change - c.node_capturing_input = v ? this : null; - } - }; - - /** - * Collapse the node to make it smaller on the canvas - * @method collapse - **/ - LGraphNode.prototype.collapse = function(force) { - this.graph._version++; - if (this.constructor.collapsable === false && !force) { - return; - } - if (!this.flags.collapsed) { - this.flags.collapsed = true; - } else { - this.flags.collapsed = false; - } - this.setDirtyCanvas(true, true); - }; - - /** - * Forces the node to do not move or realign on Z - * @method pin - **/ - - LGraphNode.prototype.pin = function(v) { - this.graph._version++; - if (v === undefined) { - this.flags.pinned = !this.flags.pinned; - } else { - this.flags.pinned = v; - } - }; - - LGraphNode.prototype.localToScreen = function(x, y, graphcanvas) { - return [ - (x + this.pos[0]) * graphcanvas.scale + graphcanvas.offset[0], - (y + this.pos[1]) * graphcanvas.scale + graphcanvas.offset[1] - ]; - }; - - function LGraphGroup(title) { - this._ctor(title); - } - - global.LGraphGroup = LiteGraph.LGraphGroup = LGraphGroup; - - LGraphGroup.prototype._ctor = function(title) { - this.title = title || "Group"; - this.font_size = 24; - this.color = LGraphCanvas.node_colors.pale_blue - ? LGraphCanvas.node_colors.pale_blue.groupcolor - : "#AAA"; - this._bounding = new Float32Array([10, 10, 140, 80]); - this._pos = this._bounding.subarray(0, 2); - this._size = this._bounding.subarray(2, 4); - this._nodes = []; - this.graph = null; - - Object.defineProperty(this, "pos", { - set: function(v) { - if (!v || v.length < 2) { - return; - } - this._pos[0] = v[0]; - this._pos[1] = v[1]; - }, - get: function() { - return this._pos; - }, - enumerable: true - }); - - Object.defineProperty(this, "size", { - set: function(v) { - if (!v || v.length < 2) { - return; - } - this._size[0] = Math.max(140, v[0]); - this._size[1] = Math.max(80, v[1]); - }, - get: function() { - return this._size; - }, - enumerable: true - }); - }; - - LGraphGroup.prototype.configure = function(o) { - this.title = o.title; - this._bounding.set(o.bounding); - this.color = o.color; - this.font = o.font; - }; - - LGraphGroup.prototype.serialize = function() { - var b = this._bounding; - return { - title: this.title, - bounding: [ - Math.round(b[0]), - Math.round(b[1]), - Math.round(b[2]), - Math.round(b[3]) - ], - color: this.color, - font: this.font - }; - }; - - LGraphGroup.prototype.move = function(deltax, deltay, ignore_nodes) { - this._pos[0] += deltax; - this._pos[1] += deltay; - if (ignore_nodes) { - return; - } - for (var i = 0; i < this._nodes.length; ++i) { - var node = this._nodes[i]; - node.pos[0] += deltax; - node.pos[1] += deltay; - } - }; - - LGraphGroup.prototype.recomputeInsideNodes = function() { - this._nodes.length = 0; - var nodes = this.graph._nodes; - var node_bounding = new Float32Array(4); - - for (var i = 0; i < nodes.length; ++i) { - var node = nodes[i]; - node.getBounding(node_bounding); - if (!overlapBounding(this._bounding, node_bounding)) { - continue; - } //out of the visible area - this._nodes.push(node); - } - }; - - LGraphGroup.prototype.isPointInside = LGraphNode.prototype.isPointInside; - LGraphGroup.prototype.setDirtyCanvas = LGraphNode.prototype.setDirtyCanvas; - - //**************************************** - - //Scale and Offset - function DragAndScale(element, skip_events) { - this.offset = new Float32Array([0, 0]); - this.scale = 1; - this.max_scale = 10; - this.min_scale = 0.1; - this.onredraw = null; - this.enabled = true; - this.last_mouse = [0, 0]; - this.element = null; - this.visible_area = new Float32Array(4); - - if (element) { - this.element = element; - if (!skip_events) { - this.bindEvents(element); - } - } - } - - LiteGraph.DragAndScale = DragAndScale; - - DragAndScale.prototype.bindEvents = function(element) { - this.last_mouse = new Float32Array(2); - - this._binded_mouse_callback = this.onMouse.bind(this); - - element.addEventListener("mousedown", this._binded_mouse_callback); - element.addEventListener("mousemove", this._binded_mouse_callback); - - element.addEventListener( - "mousewheel", - this._binded_mouse_callback, - false - ); - element.addEventListener("wheel", this._binded_mouse_callback, false); - }; - - DragAndScale.prototype.computeVisibleArea = function() { - if (!this.element) { - this.visible_area[0] = this.visible_area[1] = this.visible_area[2] = this.visible_area[3] = 0; - return; - } - var width = this.element.width; - var height = this.element.height; - var startx = -this.offset[0]; - var starty = -this.offset[1]; - var endx = startx + width / this.scale; - var endy = starty + height / this.scale; - this.visible_area[0] = startx; - this.visible_area[1] = starty; - this.visible_area[2] = endx - startx; - this.visible_area[3] = endy - starty; - }; - - DragAndScale.prototype.onMouse = function(e) { - if (!this.enabled) { - return; - } - - var canvas = this.element; - var rect = canvas.getBoundingClientRect(); - var x = e.clientX - rect.left; - var y = e.clientY - rect.top; - e.canvasx = x; - e.canvasy = y; - e.dragging = this.dragging; - - var ignore = false; - if (this.onmouse) { - ignore = this.onmouse(e); - } - - if (e.type == "mousedown") { - this.dragging = true; - canvas.removeEventListener( - "mousemove", - this._binded_mouse_callback - ); - document.body.addEventListener( - "mousemove", - this._binded_mouse_callback - ); - document.body.addEventListener( - "mouseup", - this._binded_mouse_callback - ); - } else if (e.type == "mousemove") { - if (!ignore) { - var deltax = x - this.last_mouse[0]; - var deltay = y - this.last_mouse[1]; - if (this.dragging) { - this.mouseDrag(deltax, deltay); - } - } - } else if (e.type == "mouseup") { - this.dragging = false; - document.body.removeEventListener( - "mousemove", - this._binded_mouse_callback - ); - document.body.removeEventListener( - "mouseup", - this._binded_mouse_callback - ); - canvas.addEventListener("mousemove", this._binded_mouse_callback); - } else if ( - e.type == "mousewheel" || - e.type == "wheel" || - e.type == "DOMMouseScroll" - ) { - e.eventType = "mousewheel"; - if (e.type == "wheel") { - e.wheel = -e.deltaY; - } else { - e.wheel = - e.wheelDeltaY != null ? e.wheelDeltaY : e.detail * -60; - } - - //from stack overflow - e.delta = e.wheelDelta - ? e.wheelDelta / 40 - : e.deltaY - ? -e.deltaY / 3 - : 0; - this.changeDeltaScale(1.0 + e.delta * 0.05); - } - - this.last_mouse[0] = x; - this.last_mouse[1] = y; - - e.preventDefault(); - e.stopPropagation(); - return false; - }; - - DragAndScale.prototype.toCanvasContext = function(ctx) { - ctx.scale(this.scale, this.scale); - ctx.translate(this.offset[0], this.offset[1]); - }; - - DragAndScale.prototype.convertOffsetToCanvas = function(pos) { - //return [pos[0] / this.scale - this.offset[0], pos[1] / this.scale - this.offset[1]]; - return [ - (pos[0] + this.offset[0]) * this.scale, - (pos[1] + this.offset[1]) * this.scale - ]; - }; - - DragAndScale.prototype.convertCanvasToOffset = function(pos, out) { - out = out || [0, 0]; - out[0] = pos[0] / this.scale - this.offset[0]; - out[1] = pos[1] / this.scale - this.offset[1]; - return out; - }; - - DragAndScale.prototype.mouseDrag = function(x, y) { - this.offset[0] += x / this.scale; - this.offset[1] += y / this.scale; - - if (this.onredraw) { - this.onredraw(this); - } - }; - - DragAndScale.prototype.changeScale = function(value, zooming_center) { - if (value < this.min_scale) { - value = this.min_scale; - } else if (value > this.max_scale) { - value = this.max_scale; - } - - if (value == this.scale) { - return; - } - - if (!this.element) { - return; - } - - var rect = this.element.getBoundingClientRect(); - if (!rect) { - return; - } - - zooming_center = zooming_center || [ - rect.width * 0.5, - rect.height * 0.5 - ]; - var center = this.convertCanvasToOffset(zooming_center); - this.scale = value; - if (Math.abs(this.scale - 1) < 0.01) { - this.scale = 1; - } - - var new_center = this.convertCanvasToOffset(zooming_center); - var delta_offset = [ - new_center[0] - center[0], - new_center[1] - center[1] - ]; - - this.offset[0] += delta_offset[0]; - this.offset[1] += delta_offset[1]; - - if (this.onredraw) { - this.onredraw(this); - } - }; - - DragAndScale.prototype.changeDeltaScale = function(value, zooming_center) { - this.changeScale(this.scale * value, zooming_center); - }; - - DragAndScale.prototype.reset = function() { - this.scale = 1; - this.offset[0] = 0; - this.offset[1] = 0; - }; - - //********************************************************************************* - // LGraphCanvas: LGraph renderer CLASS - //********************************************************************************* - - /** - * This class is in charge of rendering one graph inside a canvas. And provides all the interaction required. - * Valid callbacks are: onNodeSelected, onNodeDeselected, onShowNodePanel, onNodeDblClicked - * - * @class LGraphCanvas - * @constructor - * @param {HTMLCanvas} canvas the canvas where you want to render (it accepts a selector in string format or the canvas element itself) - * @param {LGraph} graph [optional] - * @param {Object} options [optional] { skip_rendering, autoresize } - */ - function LGraphCanvas(canvas, graph, options) { - options = options || {}; - - //if(graph === undefined) - // throw ("No graph assigned"); - this.background_image = - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; - - if (canvas && canvas.constructor === String) { - canvas = document.querySelector(canvas); - } - - this.ds = new DragAndScale(); - this.zoom_modify_alpha = true; //otherwise it generates ugly patterns when scaling down too much - - this.title_text_font = "" + LiteGraph.NODE_TEXT_SIZE + "px Arial"; - this.inner_text_font = - "normal " + LiteGraph.NODE_SUBTEXT_SIZE + "px Arial"; - this.node_title_color = LiteGraph.NODE_TITLE_COLOR; - this.default_link_color = LiteGraph.LINK_COLOR; - this.default_connection_color = { - input_off: "#778", - input_on: "#7F7", - output_off: "#778", - output_on: "#7F7" - }; - - this.highquality_render = true; - this.use_gradients = false; //set to true to render titlebar with gradients - this.editor_alpha = 1; //used for transition - this.pause_rendering = false; - this.clear_background = true; - - this.read_only = false; //if set to true users cannot modify the graph - this.render_only_selected = true; - this.live_mode = false; - this.show_info = true; - this.allow_dragcanvas = true; - this.allow_dragnodes = true; - this.allow_interaction = true; //allow to control widgets, buttons, collapse, etc - this.allow_searchbox = true; - this.allow_reconnect_links = false; //allows to change a connection with having to redo it again - - this.drag_mode = false; - this.dragging_rectangle = null; - - this.filter = null; //allows to filter to only accept some type of nodes in a graph - - this.always_render_background = false; - this.render_shadows = true; - this.render_canvas_border = true; - this.render_connections_shadows = false; //too much cpu - this.render_connections_border = true; - this.render_curved_connections = false; - this.render_connection_arrows = false; - this.render_collapsed_slots = true; - this.render_execution_order = false; - this.render_title_colored = true; - this.render_link_tooltip = true; - - this.links_render_mode = LiteGraph.SPLINE_LINK; - - this.canvas_mouse = [0, 0]; //mouse in canvas graph coordinates, where 0,0 is the top-left corner of the blue rectangle - - //to personalize the search box - this.onSearchBox = null; - this.onSearchBoxSelection = null; - - //callbacks - this.onMouse = null; - this.onDrawBackground = null; //to render background objects (behind nodes and connections) in the canvas affected by transform - this.onDrawForeground = null; //to render foreground objects (above nodes and connections) in the canvas affected by transform - this.onDrawOverlay = null; //to render foreground objects not affected by transform (for GUIs) - this.onDrawLinkTooltip = null; //called when rendering a tooltip - this.onNodeMoved = null; //called after moving a node - this.onSelectionChange = null; //called if the selection changes - - this.connections_width = 3; - this.round_radius = 8; - - this.current_node = null; - this.node_widget = null; //used for widgets - this.over_link_center = null; - this.last_mouse_position = [0, 0]; - this.visible_area = this.ds.visible_area; - this.visible_links = []; - - //link canvas and graph - if (graph) { - graph.attachCanvas(this); - } - - this.setCanvas(canvas); - this.clear(); - - if (!options.skip_render) { - this.startRendering(); - } - - this.autoresize = options.autoresize; - } - - global.LGraphCanvas = LiteGraph.LGraphCanvas = LGraphCanvas; - - LGraphCanvas.link_type_colors = { - "-1": LiteGraph.EVENT_LINK_COLOR, - number: "#AAA", - node: "#DCA" - }; - LGraphCanvas.gradients = {}; //cache of gradients - - /** - * clears all the data inside - * - * @method clear - */ - LGraphCanvas.prototype.clear = function() { - this.frame = 0; - this.last_draw_time = 0; - this.render_time = 0; - this.fps = 0; - - //this.scale = 1; - //this.offset = [0,0]; - - this.dragging_rectangle = null; - - this.selected_nodes = {}; - this.selected_group = null; - - this.visible_nodes = []; - this.node_dragged = null; - this.node_over = null; - this.node_capturing_input = null; - this.connecting_node = null; - this.highlighted_links = {}; - - this.dirty_canvas = true; - this.dirty_bgcanvas = true; - this.dirty_area = null; - - this.node_in_panel = null; - this.node_widget = null; - - this.last_mouse = [0, 0]; - this.last_mouseclick = 0; - this.visible_area.set([0, 0, 0, 0]); - - if (this.onClear) { - this.onClear(); - } - }; - - /** - * assigns a graph, you can reassign graphs to the same canvas - * - * @method setGraph - * @param {LGraph} graph - */ - LGraphCanvas.prototype.setGraph = function(graph, skip_clear) { - if (this.graph == graph) { - return; - } - - if (!skip_clear) { - this.clear(); - } - - if (!graph && this.graph) { - this.graph.detachCanvas(this); - return; - } - - graph.attachCanvas(this); - - //remove the graph stack in case a subgraph was open - if (this._graph_stack) - this._graph_stack = null; - - this.setDirty(true, true); - }; - - /** - * opens a graph contained inside a node in the current graph - * - * @method openSubgraph - * @param {LGraph} graph - */ - LGraphCanvas.prototype.openSubgraph = function(graph) { - if (!graph) { - throw "graph cannot be null"; - } - - if (this.graph == graph) { - throw "graph cannot be the same"; - } - - this.clear(); - - if (this.graph) { - if (!this._graph_stack) { - this._graph_stack = []; - } - this._graph_stack.push(this.graph); - } - - graph.attachCanvas(this); - this.setDirty(true, true); - }; - - /** - * closes a subgraph contained inside a node - * - * @method closeSubgraph - * @param {LGraph} assigns a graph - */ - LGraphCanvas.prototype.closeSubgraph = function() { - if (!this._graph_stack || this._graph_stack.length == 0) { - return; - } - var subgraph_node = this.graph._subgraph_node; - var graph = this._graph_stack.pop(); - this.selected_nodes = {}; - this.highlighted_links = {}; - graph.attachCanvas(this); - this.setDirty(true, true); - if (subgraph_node) { - this.centerOnNode(subgraph_node); - this.selectNodes([subgraph_node]); - } - }; - - /** - * returns the visualy active graph (in case there are more in the stack) - * @method getCurrentGraph - * @return {LGraph} the active graph - */ - LGraphCanvas.prototype.getCurrentGraph = function() { - return this.graph; - }; - - /** - * assigns a canvas - * - * @method setCanvas - * @param {Canvas} assigns a canvas (also accepts the ID of the element (not a selector) - */ - LGraphCanvas.prototype.setCanvas = function(canvas, skip_events) { - var that = this; - - if (canvas) { - if (canvas.constructor === String) { - canvas = document.getElementById(canvas); - if (!canvas) { - throw "Error creating LiteGraph canvas: Canvas not found"; - } - } - } - - if (canvas === this.canvas) { - return; - } - - if (!canvas && this.canvas) { - //maybe detach events from old_canvas - if (!skip_events) { - this.unbindEvents(); - } - } - - this.canvas = canvas; - this.ds.element = canvas; - - if (!canvas) { - return; - } - - //this.canvas.tabindex = "1000"; - canvas.className += " lgraphcanvas"; - canvas.data = this; - canvas.tabindex = "1"; //to allow key events - - //bg canvas: used for non changing stuff - this.bgcanvas = null; - if (!this.bgcanvas) { - this.bgcanvas = document.createElement("canvas"); - this.bgcanvas.width = this.canvas.width; - this.bgcanvas.height = this.canvas.height; - } - - if (canvas.getContext == null) { - if (canvas.localName != "canvas") { - throw "Element supplied for LGraphCanvas must be a element, you passed a " + - canvas.localName; - } - throw "This browser doesn't support Canvas"; - } - - var ctx = (this.ctx = canvas.getContext("2d")); - if (ctx == null) { - if (!canvas.webgl_enabled) { - console.warn( - "This canvas seems to be WebGL, enabling WebGL renderer" - ); - } - this.enableWebGL(); - } - - //input: (move and up could be unbinded) - this._mousemove_callback = this.processMouseMove.bind(this); - this._mouseup_callback = this.processMouseUp.bind(this); - - if (!skip_events) { - this.bindEvents(); - } - }; - - //used in some events to capture them - LGraphCanvas.prototype._doNothing = function doNothing(e) { - e.preventDefault(); - return false; - }; - LGraphCanvas.prototype._doReturnTrue = function doNothing(e) { - e.preventDefault(); - return true; - }; - - /** - * binds mouse, keyboard, touch and drag events to the canvas - * @method bindEvents - **/ - LGraphCanvas.prototype.bindEvents = function() { - if (this._events_binded) { - console.warn("LGraphCanvas: events already binded"); - return; - } - - var canvas = this.canvas; - var ref_window = this.getCanvasWindow(); - var document = ref_window.document; //hack used when moving canvas between windows - - this._mousedown_callback = this.processMouseDown.bind(this); - this._mousewheel_callback = this.processMouseWheel.bind(this); - - canvas.addEventListener("mousedown", this._mousedown_callback, true); //down do not need to store the binded - canvas.addEventListener("mousemove", this._mousemove_callback); - canvas.addEventListener("mousewheel", this._mousewheel_callback, false); - - canvas.addEventListener("contextmenu", this._doNothing); - canvas.addEventListener( - "DOMMouseScroll", - this._mousewheel_callback, - false - ); - - //touch events - //if( 'touchstart' in document.documentElement ) - { - canvas.addEventListener("touchstart", this.touchHandler, true); - canvas.addEventListener("touchmove", this.touchHandler, true); - canvas.addEventListener("touchend", this.touchHandler, true); - canvas.addEventListener("touchcancel", this.touchHandler, true); - } - - //Keyboard ****************** - this._key_callback = this.processKey.bind(this); - - canvas.addEventListener("keydown", this._key_callback, true); - document.addEventListener("keyup", this._key_callback, true); //in document, otherwise it doesn't fire keyup - - //Dropping Stuff over nodes ************************************ - this._ondrop_callback = this.processDrop.bind(this); - - canvas.addEventListener("dragover", this._doNothing, false); - canvas.addEventListener("dragend", this._doNothing, false); - canvas.addEventListener("drop", this._ondrop_callback, false); - canvas.addEventListener("dragenter", this._doReturnTrue, false); - - this._events_binded = true; - }; - - /** - * unbinds mouse events from the canvas - * @method unbindEvents - **/ - LGraphCanvas.prototype.unbindEvents = function() { - if (!this._events_binded) { - console.warn("LGraphCanvas: no events binded"); - return; - } - - var ref_window = this.getCanvasWindow(); - var document = ref_window.document; - - this.canvas.removeEventListener("mousedown", this._mousedown_callback); - this.canvas.removeEventListener( - "mousewheel", - this._mousewheel_callback - ); - this.canvas.removeEventListener( - "DOMMouseScroll", - this._mousewheel_callback - ); - this.canvas.removeEventListener("keydown", this._key_callback); - document.removeEventListener("keyup", this._key_callback); - this.canvas.removeEventListener("contextmenu", this._doNothing); - this.canvas.removeEventListener("drop", this._ondrop_callback); - this.canvas.removeEventListener("dragenter", this._doReturnTrue); - - this.canvas.removeEventListener("touchstart", this.touchHandler); - this.canvas.removeEventListener("touchmove", this.touchHandler); - this.canvas.removeEventListener("touchend", this.touchHandler); - this.canvas.removeEventListener("touchcancel", this.touchHandler); - - this._mousedown_callback = null; - this._mousewheel_callback = null; - this._key_callback = null; - this._ondrop_callback = null; - - this._events_binded = false; - }; - - LGraphCanvas.getFileExtension = function(url) { - var question = url.indexOf("?"); - if (question != -1) { - url = url.substr(0, question); - } - var point = url.lastIndexOf("."); - if (point == -1) { - return ""; - } - return url.substr(point + 1).toLowerCase(); - }; - - /** - * this function allows to render the canvas using WebGL instead of Canvas2D - * this is useful if you plant to render 3D objects inside your nodes, it uses litegl.js for webgl and canvas2DtoWebGL to emulate the Canvas2D calls in webGL - * @method enableWebGL - **/ - LGraphCanvas.prototype.enableWebGL = function() { - if (typeof GL === undefined) { - throw "litegl.js must be included to use a WebGL canvas"; - } - if (typeof enableWebGLCanvas === undefined) { - throw "webglCanvas.js must be included to use this feature"; - } - - this.gl = this.ctx = enableWebGLCanvas(this.canvas); - this.ctx.webgl = true; - this.bgcanvas = this.canvas; - this.bgctx = this.gl; - this.canvas.webgl_enabled = true; - - /* - GL.create({ canvas: this.bgcanvas }); - this.bgctx = enableWebGLCanvas( this.bgcanvas ); - window.gl = this.gl; - */ - }; - - /** - * marks as dirty the canvas, this way it will be rendered again - * - * @class LGraphCanvas - * @method setDirty - * @param {bool} fgcanvas if the foreground canvas is dirty (the one containing the nodes) - * @param {bool} bgcanvas if the background canvas is dirty (the one containing the wires) - */ - LGraphCanvas.prototype.setDirty = function(fgcanvas, bgcanvas) { - if (fgcanvas) { - this.dirty_canvas = true; - } - if (bgcanvas) { - this.dirty_bgcanvas = true; - } - }; - - /** - * Used to attach the canvas in a popup - * - * @method getCanvasWindow - * @return {window} returns the window where the canvas is attached (the DOM root node) - */ - LGraphCanvas.prototype.getCanvasWindow = function() { - if (!this.canvas) { - return window; - } - var doc = this.canvas.ownerDocument; - return doc.defaultView || doc.parentWindow; - }; - - /** - * starts rendering the content of the canvas when needed - * - * @method startRendering - */ - LGraphCanvas.prototype.startRendering = function() { - if (this.is_rendering) { - return; - } //already rendering - - this.is_rendering = true; - renderFrame.call(this); - - function renderFrame() { - if (!this.pause_rendering) { - this.draw(); - } - - var window = this.getCanvasWindow(); - if (this.is_rendering) { - window.requestAnimationFrame(renderFrame.bind(this)); - } - } - }; - - /** - * stops rendering the content of the canvas (to save resources) - * - * @method stopRendering - */ - LGraphCanvas.prototype.stopRendering = function() { - this.is_rendering = false; - /* - if(this.rendering_timer_id) - { - clearInterval(this.rendering_timer_id); - this.rendering_timer_id = null; - } - */ - }; - - /* LiteGraphCanvas input */ - - LGraphCanvas.prototype.processMouseDown = function(e) { - if (!this.graph) { - return; - } - - this.adjustMouseEvent(e); - - var ref_window = this.getCanvasWindow(); - var document = ref_window.document; - LGraphCanvas.active_canvas = this; - var that = this; - - //move mouse move event to the window in case it drags outside of the canvas - this.canvas.removeEventListener("mousemove", this._mousemove_callback); - ref_window.document.addEventListener( - "mousemove", - this._mousemove_callback, - true - ); //catch for the entire window - ref_window.document.addEventListener( - "mouseup", - this._mouseup_callback, - true - ); - - var node = this.graph.getNodeOnPos( - e.canvasX, - e.canvasY, - this.visible_nodes, - 5 - ); - var skip_dragging = false; - var skip_action = false; - var now = LiteGraph.getTime(); - var is_double_click = now - this.last_mouseclick < 300; - - this.canvas_mouse[0] = e.canvasX; - this.canvas_mouse[1] = e.canvasY; - this.canvas.focus(); - - LiteGraph.closeAllContextMenus(ref_window); - - if (this.onMouse) { - if (this.onMouse(e) == true) { - return; - } - } - - if (e.which == 1) { - //left button mouse - if (e.ctrlKey) { - this.dragging_rectangle = new Float32Array(4); - this.dragging_rectangle[0] = e.canvasX; - this.dragging_rectangle[1] = e.canvasY; - this.dragging_rectangle[2] = 1; - this.dragging_rectangle[3] = 1; - skip_action = true; - } - - var clicking_canvas_bg = false; - - //when clicked on top of a node - //and it is not interactive - if (node && this.allow_interaction && !skip_action && !this.read_only) { - if (!this.live_mode && !node.flags.pinned) { - this.bringToFront(node); - } //if it wasn't selected? - - //not dragging mouse to connect two slots - if ( - !this.connecting_node && - !node.flags.collapsed && - !this.live_mode - ) { - //Search for corner for resize - if ( - !skip_action && - node.resizable !== false && - isInsideRectangle( - e.canvasX, - e.canvasY, - node.pos[0] + node.size[0] - 5, - node.pos[1] + node.size[1] - 5, - 10, - 10 - ) - ) { - this.resizing_node = node; - this.canvas.style.cursor = "se-resize"; - skip_action = true; - } else { - //search for outputs - if (node.outputs) { - for ( - var i = 0, l = node.outputs.length; - i < l; - ++i - ) { - var output = node.outputs[i]; - var link_pos = node.getConnectionPos(false, i); - if ( - isInsideRectangle( - e.canvasX, - e.canvasY, - link_pos[0] - 15, - link_pos[1] - 10, - 30, - 20 - ) - ) { - this.connecting_node = node; - this.connecting_output = output; - this.connecting_pos = node.getConnectionPos( false, i ); - this.connecting_slot = i; - - if (e.shiftKey) { - node.disconnectOutput(i); - } - - if (is_double_click) { - if (node.onOutputDblClick) { - node.onOutputDblClick(i, e); - } - } else { - if (node.onOutputClick) { - node.onOutputClick(i, e); - } - } - - skip_action = true; - break; - } - } - } - - //search for inputs - if (node.inputs) { - for ( - var i = 0, l = node.inputs.length; - i < l; - ++i - ) { - var input = node.inputs[i]; - var link_pos = node.getConnectionPos(true, i); - if ( - isInsideRectangle( - e.canvasX, - e.canvasY, - link_pos[0] - 15, - link_pos[1] - 10, - 30, - 20 - ) - ) { - if (is_double_click) { - if (node.onInputDblClick) { - node.onInputDblClick(i, e); - } - } else { - if (node.onInputClick) { - node.onInputClick(i, e); - } - } - - if (input.link !== null) { - var link_info = this.graph.links[ - input.link - ]; //before disconnecting - node.disconnectInput(i); - - if ( - this.allow_reconnect_links || - e.shiftKey - ) { - this.connecting_node = this.graph._nodes_by_id[ - link_info.origin_id - ]; - this.connecting_slot = - link_info.origin_slot; - this.connecting_output = this.connecting_node.outputs[ - this.connecting_slot - ]; - this.connecting_pos = this.connecting_node.getConnectionPos( false, this.connecting_slot ); - } - - this.dirty_bgcanvas = true; - skip_action = true; - } - } - } - } - } //not resizing - } - - //Search for corner for collapsing - /* - if( !skip_action && isInsideRectangle( e.canvasX, e.canvasY, node.pos[0], node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT )) - { - node.collapse(); - skip_action = true; - } - */ - - //it wasn't clicked on the links boxes - if (!skip_action) { - var block_drag_node = false; - - //widgets - var widget = this.processNodeWidgets( node, this.canvas_mouse, e ); - if (widget) { - block_drag_node = true; - this.node_widget = [node, widget]; - } - - //double clicking - if (is_double_click && this.selected_nodes[node.id]) { - //double click node - if (node.onDblClick) { - node.onDblClick( e, [ e.canvasX - node.pos[0], e.canvasY - node.pos[1] ], this ); - } - this.processNodeDblClicked(node); - block_drag_node = true; - } - - //if do not capture mouse - if ( node.onMouseDown && node.onMouseDown( e, [e.canvasX - node.pos[0], e.canvasY - node.pos[1]], this ) ) { - block_drag_node = true; - } else if (this.live_mode) { - clicking_canvas_bg = true; - block_drag_node = true; - } - - if (!block_drag_node) { - if (this.allow_dragnodes) { - this.node_dragged = node; - } - if (!this.selected_nodes[node.id]) { - this.processNodeSelected(node, e); - } - } - - this.dirty_canvas = true; - } - } //clicked outside of nodes - else { - //search for link connector - if(!this.read_only) - for (var i = 0; i < this.visible_links.length; ++i) { - var link = this.visible_links[i]; - var center = link._pos; - if ( - !center || - e.canvasX < center[0] - 4 || - e.canvasX > center[0] + 4 || - e.canvasY < center[1] - 4 || - e.canvasY > center[1] + 4 - ) { - continue; - } - //link clicked - this.showLinkMenu(link, e); - this.over_link_center = null; //clear tooltip - break; - } - - this.selected_group = this.graph.getGroupOnPos( e.canvasX, e.canvasY ); - this.selected_group_resizing = false; - if (this.selected_group && !this.read_only ) { - if (e.ctrlKey) { - this.dragging_rectangle = null; - } - - var dist = distance( [e.canvasX, e.canvasY], [ this.selected_group.pos[0] + this.selected_group.size[0], this.selected_group.pos[1] + this.selected_group.size[1] ] ); - if (dist * this.ds.scale < 10) { - this.selected_group_resizing = true; - } else { - this.selected_group.recomputeInsideNodes(); - } - } - - if (is_double_click && !this.read_only && this.allow_searchbox) { - this.showSearchBox(e); - } - - clicking_canvas_bg = true; - } - - if (!skip_action && clicking_canvas_bg && this.allow_dragcanvas) { - this.dragging_canvas = true; - } - } else if (e.which == 2) { - //middle button - } else if (e.which == 3) { - //right button - if(!this.read_only) - this.processContextMenu(node, e); - } - - //TODO - //if(this.node_selected != prev_selected) - // this.onNodeSelectionChange(this.node_selected); - - this.last_mouse[0] = e.localX; - this.last_mouse[1] = e.localY; - this.last_mouseclick = LiteGraph.getTime(); - this.last_mouse_dragging = true; - - /* - if( (this.dirty_canvas || this.dirty_bgcanvas) && this.rendering_timer_id == null) - this.draw(); - */ - - this.graph.change(); - - //this is to ensure to defocus(blur) if a text input element is on focus - if ( - !ref_window.document.activeElement || - (ref_window.document.activeElement.nodeName.toLowerCase() != - "input" && - ref_window.document.activeElement.nodeName.toLowerCase() != - "textarea") - ) { - e.preventDefault(); - } - e.stopPropagation(); - - if (this.onMouseDown) { - this.onMouseDown(e); - } - - return false; - }; - - /** - * Called when a mouse move event has to be processed - * @method processMouseMove - **/ - LGraphCanvas.prototype.processMouseMove = function(e) { - if (this.autoresize) { - this.resize(); - } - - if (!this.graph) { - return; - } - - LGraphCanvas.active_canvas = this; - this.adjustMouseEvent(e); - var mouse = [e.localX, e.localY]; - var delta = [ - mouse[0] - this.last_mouse[0], - mouse[1] - this.last_mouse[1] - ]; - this.last_mouse = mouse; - this.canvas_mouse[0] = e.canvasX; - this.canvas_mouse[1] = e.canvasY; - e.dragging = this.last_mouse_dragging; - - if (this.node_widget) { - this.processNodeWidgets( - this.node_widget[0], - this.canvas_mouse, - e, - this.node_widget[1] - ); - this.dirty_canvas = true; - } - - if (this.dragging_rectangle) { - this.dragging_rectangle[2] = e.canvasX - this.dragging_rectangle[0]; - this.dragging_rectangle[3] = e.canvasY - this.dragging_rectangle[1]; - this.dirty_canvas = true; - } else if (this.selected_group && !this.read_only) { - //moving/resizing a group - if (this.selected_group_resizing) { - this.selected_group.size = [ - e.canvasX - this.selected_group.pos[0], - e.canvasY - this.selected_group.pos[1] - ]; - } else { - var deltax = delta[0] / this.ds.scale; - var deltay = delta[1] / this.ds.scale; - this.selected_group.move(deltax, deltay, e.ctrlKey); - if (this.selected_group._nodes.length) { - this.dirty_canvas = true; - } - } - this.dirty_bgcanvas = true; - } else if (this.dragging_canvas) { - this.ds.offset[0] += delta[0] / this.ds.scale; - this.ds.offset[1] += delta[1] / this.ds.scale; - this.dirty_canvas = true; - this.dirty_bgcanvas = true; - } else if (this.allow_interaction && !this.read_only) { - if (this.connecting_node) { - this.dirty_canvas = true; - } - - //get node over - var node = this.graph.getNodeOnPos( - e.canvasX, - e.canvasY, - this.visible_nodes - ); - - //remove mouseover flag - for (var i = 0, l = this.graph._nodes.length; i < l; ++i) { - if ( - this.graph._nodes[i].mouseOver && - node != this.graph._nodes[i] - ) { - //mouse leave - this.graph._nodes[i].mouseOver = false; - if (this.node_over && this.node_over.onMouseLeave) { - this.node_over.onMouseLeave(e); - } - this.node_over = null; - this.dirty_canvas = true; - } - } - - //mouse over a node - if (node) { - //this.canvas.style.cursor = "move"; - if (!node.mouseOver) { - //mouse enter - node.mouseOver = true; - this.node_over = node; - this.dirty_canvas = true; - - if (node.onMouseEnter) { - node.onMouseEnter(e); - } - } - - //in case the node wants to do something - if (node.onMouseMove) { - node.onMouseMove( - e, - [e.canvasX - node.pos[0], e.canvasY - node.pos[1]], - this - ); - } - - //if dragging a link - if (this.connecting_node) { - var pos = this._highlight_input || [0, 0]; //to store the output of isOverNodeInput - - //on top of input - if (this.isOverNodeBox(node, e.canvasX, e.canvasY)) { - //mouse on top of the corner box, don't know what to do - } else { - //check if I have a slot below de mouse - var slot = this.isOverNodeInput( - node, - e.canvasX, - e.canvasY, - pos - ); - if (slot != -1 && node.inputs[slot]) { - var slot_type = node.inputs[slot].type; - if ( - LiteGraph.isValidConnection( - this.connecting_output.type, - slot_type - ) - ) { - this._highlight_input = pos; - } - } else { - this._highlight_input = null; - } - } - } - - //Search for corner - if (this.canvas) { - if ( - isInsideRectangle( - e.canvasX, - e.canvasY, - node.pos[0] + node.size[0] - 5, - node.pos[1] + node.size[1] - 5, - 5, - 5 - ) - ) { - this.canvas.style.cursor = "se-resize"; - } else { - this.canvas.style.cursor = "crosshair"; - } - } - } else { //outside - - //search for link connector - var over_link = null; - for (var i = 0; i < this.visible_links.length; ++i) { - var link = this.visible_links[i]; - var center = link._pos; - if ( - !center || - e.canvasX < center[0] - 4 || - e.canvasX > center[0] + 4 || - e.canvasY < center[1] - 4 || - e.canvasY > center[1] + 4 - ) { - continue; - } - over_link = link; - break; - } - if( over_link != this.over_link_center ) - { - this.over_link_center = over_link; - this.dirty_canvas = true; - } - - if (this.canvas) { - this.canvas.style.cursor = ""; - } - } - - if ( this.node_capturing_input && this.node_capturing_input != node && this.node_capturing_input.onMouseMove ) { - this.node_capturing_input.onMouseMove(e,[e.canvasX - this.node_capturing_input.pos[0],e.canvasY - this.node_capturing_input.pos[1]], this); - } - - if (this.node_dragged && !this.live_mode) { - for (var i in this.selected_nodes) { - var n = this.selected_nodes[i]; - n.pos[0] += delta[0] / this.ds.scale; - n.pos[1] += delta[1] / this.ds.scale; - } - - this.dirty_canvas = true; - this.dirty_bgcanvas = true; - } - - if (this.resizing_node && !this.live_mode) { - //convert mouse to node space - var desired_size = [ e.canvasX - this.resizing_node.pos[0], e.canvasY - this.resizing_node.pos[1] ]; - var min_size = this.resizing_node.computeSize(); - desired_size[0] = Math.max( min_size[0], desired_size[0] ); - desired_size[1] = Math.max( min_size[1], desired_size[1] ); - this.resizing_node.setSize( desired_size ); - - this.canvas.style.cursor = "se-resize"; - this.dirty_canvas = true; - this.dirty_bgcanvas = true; - } - } - - e.preventDefault(); - return false; - }; - - /** - * Called when a mouse up event has to be processed - * @method processMouseUp - **/ - LGraphCanvas.prototype.processMouseUp = function(e) { - if (!this.graph) { - return; - } - - var window = this.getCanvasWindow(); - var document = window.document; - LGraphCanvas.active_canvas = this; - - //restore the mousemove event back to the canvas - document.removeEventListener("mousemove",this._mousemove_callback,true); - this.canvas.addEventListener("mousemove",this._mousemove_callback,true); - document.removeEventListener("mouseup", this._mouseup_callback, true); - - this.adjustMouseEvent(e); - var now = LiteGraph.getTime(); - e.click_time = now - this.last_mouseclick; - this.last_mouse_dragging = false; - - if (e.which == 1) { - - if( this.node_widget ) - { - this.processNodeWidgets( this.node_widget[0], this.canvas_mouse, e ); - } - - //left button - this.node_widget = null; - - if (this.selected_group) { - var diffx = - this.selected_group.pos[0] - - Math.round(this.selected_group.pos[0]); - var diffy = - this.selected_group.pos[1] - - Math.round(this.selected_group.pos[1]); - this.selected_group.move(diffx, diffy, e.ctrlKey); - this.selected_group.pos[0] = Math.round( - this.selected_group.pos[0] - ); - this.selected_group.pos[1] = Math.round( - this.selected_group.pos[1] - ); - if (this.selected_group._nodes.length) { - this.dirty_canvas = true; - } - this.selected_group = null; - } - this.selected_group_resizing = false; - - if (this.dragging_rectangle) { - if (this.graph) { - var nodes = this.graph._nodes; - var node_bounding = new Float32Array(4); - this.deselectAllNodes(); - //compute bounding and flip if left to right - var w = Math.abs(this.dragging_rectangle[2]); - var h = Math.abs(this.dragging_rectangle[3]); - var startx = - this.dragging_rectangle[2] < 0 - ? this.dragging_rectangle[0] - w - : this.dragging_rectangle[0]; - var starty = - this.dragging_rectangle[3] < 0 - ? this.dragging_rectangle[1] - h - : this.dragging_rectangle[1]; - this.dragging_rectangle[0] = startx; - this.dragging_rectangle[1] = starty; - this.dragging_rectangle[2] = w; - this.dragging_rectangle[3] = h; - - //test against all nodes (not visible because the rectangle maybe start outside - var to_select = []; - for (var i = 0; i < nodes.length; ++i) { - var node = nodes[i]; - node.getBounding(node_bounding); - if ( - !overlapBounding( - this.dragging_rectangle, - node_bounding - ) - ) { - continue; - } //out of the visible area - to_select.push(node); - } - if (to_select.length) { - this.selectNodes(to_select); - } - } - this.dragging_rectangle = null; - } else if (this.connecting_node) { - //dragging a connection - this.dirty_canvas = true; - this.dirty_bgcanvas = true; - - var node = this.graph.getNodeOnPos( - e.canvasX, - e.canvasY, - this.visible_nodes - ); - - //node below mouse - if (node) { - if ( - this.connecting_output.type == LiteGraph.EVENT && - this.isOverNodeBox(node, e.canvasX, e.canvasY) - ) { - this.connecting_node.connect( - this.connecting_slot, - node, - LiteGraph.EVENT - ); - } else { - //slot below mouse? connect - var slot = this.isOverNodeInput( - node, - e.canvasX, - e.canvasY - ); - if (slot != -1) { - this.connecting_node.connect( - this.connecting_slot, - node, - slot - ); - } else { - //not on top of an input - var input = node.getInputInfo(0); - //auto connect - if ( - this.connecting_output.type == LiteGraph.EVENT - ) { - this.connecting_node.connect( - this.connecting_slot, - node, - LiteGraph.EVENT - ); - } else if ( - input && - !input.link && - LiteGraph.isValidConnection( - input.type && this.connecting_output.type - ) - ) { - this.connecting_node.connect( - this.connecting_slot, - node, - 0 - ); - } - } - } - } - - this.connecting_output = null; - this.connecting_pos = null; - this.connecting_node = null; - this.connecting_slot = -1; - } //not dragging connection - else if (this.resizing_node) { - this.dirty_canvas = true; - this.dirty_bgcanvas = true; - this.resizing_node = null; - } else if (this.node_dragged) { - //node being dragged? - var node = this.node_dragged; - if ( - node && - e.click_time < 300 && - isInsideRectangle( e.canvasX, e.canvasY, node.pos[0], node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT ) - ) { - node.collapse(); - } - - this.dirty_canvas = true; - this.dirty_bgcanvas = true; - this.node_dragged.pos[0] = Math.round(this.node_dragged.pos[0]); - this.node_dragged.pos[1] = Math.round(this.node_dragged.pos[1]); - if (this.graph.config.align_to_grid) { - this.node_dragged.alignToGrid(); - } - if( this.onNodeMoved ) - this.onNodeMoved( this.node_dragged ); - this.node_dragged = null; - } //no node being dragged - else { - //get node over - var node = this.graph.getNodeOnPos( - e.canvasX, - e.canvasY, - this.visible_nodes - ); - - if (!node && e.click_time < 300) { - this.deselectAllNodes(); - } - - this.dirty_canvas = true; - this.dragging_canvas = false; - - if (this.node_over && this.node_over.onMouseUp) { - this.node_over.onMouseUp( e, [ e.canvasX - this.node_over.pos[0], e.canvasY - this.node_over.pos[1] ], this ); - } - if ( - this.node_capturing_input && - this.node_capturing_input.onMouseUp - ) { - this.node_capturing_input.onMouseUp(e, [ - e.canvasX - this.node_capturing_input.pos[0], - e.canvasY - this.node_capturing_input.pos[1] - ]); - } - } - } else if (e.which == 2) { - //middle button - //trace("middle"); - this.dirty_canvas = true; - this.dragging_canvas = false; - } else if (e.which == 3) { - //right button - //trace("right"); - this.dirty_canvas = true; - this.dragging_canvas = false; - } - - /* - if((this.dirty_canvas || this.dirty_bgcanvas) && this.rendering_timer_id == null) - this.draw(); - */ - - this.graph.change(); - - e.stopPropagation(); - e.preventDefault(); - return false; - }; - - /** - * Called when a mouse wheel event has to be processed - * @method processMouseWheel - **/ - LGraphCanvas.prototype.processMouseWheel = function(e) { - if (!this.graph || !this.allow_dragcanvas) { - return; - } - - var delta = e.wheelDeltaY != null ? e.wheelDeltaY : e.detail * -60; - - this.adjustMouseEvent(e); - - var scale = this.ds.scale; - - if (delta > 0) { - scale *= 1.1; - } else if (delta < 0) { - scale *= 1 / 1.1; - } - - //this.setZoom( scale, [ e.localX, e.localY ] ); - this.ds.changeScale(scale, [e.localX, e.localY]); - - this.graph.change(); - - e.preventDefault(); - return false; // prevent default - }; - - /** - * returns true if a position (in graph space) is on top of a node little corner box - * @method isOverNodeBox - **/ - LGraphCanvas.prototype.isOverNodeBox = function(node, canvasx, canvasy) { - var title_height = LiteGraph.NODE_TITLE_HEIGHT; - if ( - isInsideRectangle( - canvasx, - canvasy, - node.pos[0] + 2, - node.pos[1] + 2 - title_height, - title_height - 4, - title_height - 4 - ) - ) { - return true; - } - return false; - }; - - /** - * returns true if a position (in graph space) is on top of a node input slot - * @method isOverNodeInput - **/ - LGraphCanvas.prototype.isOverNodeInput = function( - node, - canvasx, - canvasy, - slot_pos - ) { - if (node.inputs) { - for (var i = 0, l = node.inputs.length; i < l; ++i) { - var input = node.inputs[i]; - var link_pos = node.getConnectionPos(true, i); - var is_inside = false; - if (node.horizontal) { - is_inside = isInsideRectangle( - canvasx, - canvasy, - link_pos[0] - 5, - link_pos[1] - 10, - 10, - 20 - ); - } else { - is_inside = isInsideRectangle( - canvasx, - canvasy, - link_pos[0] - 10, - link_pos[1] - 5, - 40, - 10 - ); - } - if (is_inside) { - if (slot_pos) { - slot_pos[0] = link_pos[0]; - slot_pos[1] = link_pos[1]; - } - return i; - } - } - } - return -1; - }; - - /** - * process a key event - * @method processKey - **/ - LGraphCanvas.prototype.processKey = function(e) { - if (!this.graph) { - return; - } - - var block_default = false; - //console.log(e); //debug - - if (e.target.localName == "input") { - return; - } - - if (e.type == "keydown") { - if (e.keyCode == 32) { - //esc - this.dragging_canvas = true; - block_default = true; - } - - //select all Control A - if (e.keyCode == 65 && e.ctrlKey) { - this.selectNodes(); - block_default = true; - } - - if (e.code == "KeyC" && (e.metaKey || e.ctrlKey) && !e.shiftKey) { - //copy - if (this.selected_nodes) { - this.copyToClipboard(); - block_default = true; - } - } - - if (e.code == "KeyV" && (e.metaKey || e.ctrlKey) && !e.shiftKey) { - //paste - this.pasteFromClipboard(); - } - - //delete or backspace - if (e.keyCode == 46 || e.keyCode == 8) { - if ( - e.target.localName != "input" && - e.target.localName != "textarea" - ) { - this.deleteSelectedNodes(); - block_default = true; - } - } - - //collapse - //... - - //TODO - if (this.selected_nodes) { - for (var i in this.selected_nodes) { - if (this.selected_nodes[i].onKeyDown) { - this.selected_nodes[i].onKeyDown(e); - } - } - } - } else if (e.type == "keyup") { - if (e.keyCode == 32) { - this.dragging_canvas = false; - } - - if (this.selected_nodes) { - for (var i in this.selected_nodes) { - if (this.selected_nodes[i].onKeyUp) { - this.selected_nodes[i].onKeyUp(e); - } - } - } - } - - this.graph.change(); - - if (block_default) { - e.preventDefault(); - e.stopImmediatePropagation(); - return false; - } - }; - - LGraphCanvas.prototype.copyToClipboard = function() { - var clipboard_info = { - nodes: [], - links: [] - }; - var index = 0; - var selected_nodes_array = []; - for (var i in this.selected_nodes) { - var node = this.selected_nodes[i]; - node._relative_id = index; - selected_nodes_array.push(node); - index += 1; - } - - for (var i = 0; i < selected_nodes_array.length; ++i) { - var node = selected_nodes_array[i]; - var cloned = node.clone(); - if(!cloned) - { - console.warn("node type not found: " + node.type ); - continue; - } - clipboard_info.nodes.push(cloned.serialize()); - if (node.inputs && node.inputs.length) { - for (var j = 0; j < node.inputs.length; ++j) { - var input = node.inputs[j]; - if (!input || input.link == null) { - continue; - } - var link_info = this.graph.links[input.link]; - if (!link_info) { - continue; - } - var target_node = this.graph.getNodeById( - link_info.origin_id - ); - if (!target_node || !this.selected_nodes[target_node.id]) { - //improve this by allowing connections to non-selected nodes - continue; - } //not selected - clipboard_info.links.push([ - target_node._relative_id, - link_info.origin_slot, //j, - node._relative_id, - link_info.target_slot - ]); - } - } - } - localStorage.setItem( - "litegrapheditor_clipboard", - JSON.stringify(clipboard_info) - ); - }; - - LGraphCanvas.prototype.pasteFromClipboard = function() { - var data = localStorage.getItem("litegrapheditor_clipboard"); - if (!data) { - return; - } - - //create nodes - var clipboard_info = JSON.parse(data); - var nodes = []; - for (var i = 0; i < clipboard_info.nodes.length; ++i) { - var node_data = clipboard_info.nodes[i]; - var node = LiteGraph.createNode(node_data.type); - if (node) { - node.configure(node_data); - node.pos[0] += 5; - node.pos[1] += 5; - this.graph.add(node); - nodes.push(node); - } - } - - //create links - for (var i = 0; i < clipboard_info.links.length; ++i) { - var link_info = clipboard_info.links[i]; - var origin_node = nodes[link_info[0]]; - var target_node = nodes[link_info[2]]; - if( origin_node && target_node ) - origin_node.connect(link_info[1], target_node, link_info[3]); - else - console.warn("Warning, nodes missing on pasting"); - } - - this.selectNodes(nodes); - }; - - /** - * process a item drop event on top the canvas - * @method processDrop - **/ - LGraphCanvas.prototype.processDrop = function(e) { - e.preventDefault(); - this.adjustMouseEvent(e); - - var pos = [e.canvasX, e.canvasY]; - var node = this.graph ? this.graph.getNodeOnPos(pos[0], pos[1]) : null; - - if (!node) { - var r = null; - if (this.onDropItem) { - r = this.onDropItem(event); - } - if (!r) { - this.checkDropItem(e); - } - return; - } - - if (node.onDropFile || node.onDropData) { - var files = e.dataTransfer.files; - if (files && files.length) { - for (var i = 0; i < files.length; i++) { - var file = e.dataTransfer.files[0]; - var filename = file.name; - var ext = LGraphCanvas.getFileExtension(filename); - //console.log(file); - - if (node.onDropFile) { - node.onDropFile(file); - } - - if (node.onDropData) { - //prepare reader - var reader = new FileReader(); - reader.onload = function(event) { - //console.log(event.target); - var data = event.target.result; - node.onDropData(data, filename, file); - }; - - //read data - var type = file.type.split("/")[0]; - if (type == "text" || type == "") { - reader.readAsText(file); - } else if (type == "image") { - reader.readAsDataURL(file); - } else { - reader.readAsArrayBuffer(file); - } - } - } - } - } - - if (node.onDropItem) { - if (node.onDropItem(event)) { - return true; - } - } - - if (this.onDropItem) { - return this.onDropItem(event); - } - - return false; - }; - - //called if the graph doesn't have a default drop item behaviour - LGraphCanvas.prototype.checkDropItem = function(e) { - if (e.dataTransfer.files.length) { - var file = e.dataTransfer.files[0]; - var ext = LGraphCanvas.getFileExtension(file.name).toLowerCase(); - var nodetype = LiteGraph.node_types_by_file_extension[ext]; - if (nodetype) { - var node = LiteGraph.createNode(nodetype.type); - node.pos = [e.canvasX, e.canvasY]; - this.graph.add(node); - if (node.onDropFile) { - node.onDropFile(file); - } - } - } - }; - - LGraphCanvas.prototype.processNodeDblClicked = function(n) { - if (this.onShowNodePanel) { - this.onShowNodePanel(n); - } - - if (this.onNodeDblClicked) { - this.onNodeDblClicked(n); - } - - this.setDirty(true); - }; - - LGraphCanvas.prototype.processNodeSelected = function(node, e) { - this.selectNode(node, e && e.shiftKey); - if (this.onNodeSelected) { - this.onNodeSelected(node); - } - }; - - /** - * selects a given node (or adds it to the current selection) - * @method selectNode - **/ - LGraphCanvas.prototype.selectNode = function( - node, - add_to_current_selection - ) { - if (node == null) { - this.deselectAllNodes(); - } else { - this.selectNodes([node], add_to_current_selection); - } - }; - - /** - * selects several nodes (or adds them to the current selection) - * @method selectNodes - **/ - LGraphCanvas.prototype.selectNodes = function( - nodes, - add_to_current_selection - ) { - if (!add_to_current_selection) { - this.deselectAllNodes(); - } - - nodes = nodes || this.graph._nodes; - for (var i = 0; i < nodes.length; ++i) { - var node = nodes[i]; - if (node.is_selected) { - continue; - } - - if (!node.is_selected && node.onSelected) { - node.onSelected(); - } - node.is_selected = true; - this.selected_nodes[node.id] = node; - - if (node.inputs) { - for (var j = 0; j < node.inputs.length; ++j) { - this.highlighted_links[node.inputs[j].link] = true; - } - } - if (node.outputs) { - for (var j = 0; j < node.outputs.length; ++j) { - var out = node.outputs[j]; - if (out.links) { - for (var k = 0; k < out.links.length; ++k) { - this.highlighted_links[out.links[k]] = true; - } - } - } - } - } - - if( this.onSelectionChange ) - this.onSelectionChange( this.selected_nodes ); - - this.setDirty(true); - }; - - /** - * removes a node from the current selection - * @method deselectNode - **/ - LGraphCanvas.prototype.deselectNode = function(node) { - if (!node.is_selected) { - return; - } - if (node.onDeselected) { - node.onDeselected(); - } - node.is_selected = false; - - if (this.onNodeDeselected) { - this.onNodeDeselected(node); - } - - //remove highlighted - if (node.inputs) { - for (var i = 0; i < node.inputs.length; ++i) { - delete this.highlighted_links[node.inputs[i].link]; - } - } - if (node.outputs) { - for (var i = 0; i < node.outputs.length; ++i) { - var out = node.outputs[i]; - if (out.links) { - for (var j = 0; j < out.links.length; ++j) { - delete this.highlighted_links[out.links[j]]; - } - } - } - } - }; - - /** - * removes all nodes from the current selection - * @method deselectAllNodes - **/ - LGraphCanvas.prototype.deselectAllNodes = function() { - if (!this.graph) { - return; - } - var nodes = this.graph._nodes; - for (var i = 0, l = nodes.length; i < l; ++i) { - var node = nodes[i]; - if (!node.is_selected) { - continue; - } - if (node.onDeselected) { - node.onDeselected(); - } - node.is_selected = false; - if (this.onNodeDeselected) { - this.onNodeDeselected(node); - } - } - this.selected_nodes = {}; - this.current_node = null; - this.highlighted_links = {}; - if( this.onSelectionChange ) - this.onSelectionChange( this.selected_nodes ); - this.setDirty(true); - }; - - /** - * deletes all nodes in the current selection from the graph - * @method deleteSelectedNodes - **/ - LGraphCanvas.prototype.deleteSelectedNodes = function() { - for (var i in this.selected_nodes) { - var node = this.selected_nodes[i]; - //autoconnect when possible (very basic, only takes into account first input-output) - if(node.inputs && node.inputs.length && node.outputs && node.outputs.length && LiteGraph.isValidConnection( node.inputs[0].type, node.outputs[0].type ) && node.inputs[0].link && node.outputs[0].links && node.outputs[0].links.length ) - { - var input_link = node.graph.links[ node.inputs[0].link ]; - var output_link = node.graph.links[ node.outputs[0].links[0] ]; - var input_node = node.getInputNode(0); - var output_node = node.getOutputNodes(0)[0]; - if(input_node && output_node) - input_node.connect( input_link.origin_slot, output_node, output_link.target_slot ); - } - this.graph.remove(node); - if (this.onNodeDeselected) { - this.onNodeDeselected(node); - } - } - this.selected_nodes = {}; - this.current_node = null; - this.highlighted_links = {}; - this.setDirty(true); - }; - - /** - * centers the camera on a given node - * @method centerOnNode - **/ - LGraphCanvas.prototype.centerOnNode = function(node) { - this.ds.offset[0] = - -node.pos[0] - - node.size[0] * 0.5 + - (this.canvas.width * 0.5) / this.ds.scale; - this.ds.offset[1] = - -node.pos[1] - - node.size[1] * 0.5 + - (this.canvas.height * 0.5) / this.ds.scale; - this.setDirty(true, true); - }; - - /** - * adds some useful properties to a mouse event, like the position in graph coordinates - * @method adjustMouseEvent - **/ - LGraphCanvas.prototype.adjustMouseEvent = function(e) { - if (this.canvas) { - var b = this.canvas.getBoundingClientRect(); - e.localX = e.clientX - b.left; - e.localY = e.clientY - b.top; - } else { - e.localX = e.clientX; - e.localY = e.clientY; - } - - e.deltaX = e.localX - this.last_mouse_position[0]; - e.deltaY = e.localY - this.last_mouse_position[1]; - - this.last_mouse_position[0] = e.localX; - this.last_mouse_position[1] = e.localY; - - e.canvasX = e.localX / this.ds.scale - this.ds.offset[0]; - e.canvasY = e.localY / this.ds.scale - this.ds.offset[1]; - }; - - /** - * changes the zoom level of the graph (default is 1), you can pass also a place used to pivot the zoom - * @method setZoom - **/ - LGraphCanvas.prototype.setZoom = function(value, zooming_center) { - this.ds.changeScale(value, zooming_center); - /* - if(!zooming_center && this.canvas) - zooming_center = [this.canvas.width * 0.5,this.canvas.height * 0.5]; - - var center = this.convertOffsetToCanvas( zooming_center ); - - this.ds.scale = value; - - if(this.scale > this.max_zoom) - this.scale = this.max_zoom; - else if(this.scale < this.min_zoom) - this.scale = this.min_zoom; - - var new_center = this.convertOffsetToCanvas( zooming_center ); - var delta_offset = [new_center[0] - center[0], new_center[1] - center[1]]; - - this.offset[0] += delta_offset[0]; - this.offset[1] += delta_offset[1]; - */ - - this.dirty_canvas = true; - this.dirty_bgcanvas = true; - }; - - /** - * converts a coordinate from graph coordinates to canvas2D coordinates - * @method convertOffsetToCanvas - **/ - LGraphCanvas.prototype.convertOffsetToCanvas = function(pos, out) { - return this.ds.convertOffsetToCanvas(pos, out); - }; - - /** - * converts a coordinate from Canvas2D coordinates to graph space - * @method convertCanvasToOffset - **/ - LGraphCanvas.prototype.convertCanvasToOffset = function(pos, out) { - return this.ds.convertCanvasToOffset(pos, out); - }; - - //converts event coordinates from canvas2D to graph coordinates - LGraphCanvas.prototype.convertEventToCanvasOffset = function(e) { - var rect = this.canvas.getBoundingClientRect(); - return this.convertCanvasToOffset([ - e.clientX - rect.left, - e.clientY - rect.top - ]); - }; - - /** - * brings a node to front (above all other nodes) - * @method bringToFront - **/ - LGraphCanvas.prototype.bringToFront = function(node) { - var i = this.graph._nodes.indexOf(node); - if (i == -1) { - return; - } - - this.graph._nodes.splice(i, 1); - this.graph._nodes.push(node); - }; - - /** - * sends a node to the back (below all other nodes) - * @method sendToBack - **/ - LGraphCanvas.prototype.sendToBack = function(node) { - var i = this.graph._nodes.indexOf(node); - if (i == -1) { - return; - } - - this.graph._nodes.splice(i, 1); - this.graph._nodes.unshift(node); - }; - - /* Interaction */ - - /* LGraphCanvas render */ - var temp = new Float32Array(4); - - /** - * checks which nodes are visible (inside the camera area) - * @method computeVisibleNodes - **/ - LGraphCanvas.prototype.computeVisibleNodes = function(nodes, out) { - var visible_nodes = out || []; - visible_nodes.length = 0; - nodes = nodes || this.graph._nodes; - for (var i = 0, l = nodes.length; i < l; ++i) { - var n = nodes[i]; - - //skip rendering nodes in live mode - if (this.live_mode && !n.onDrawBackground && !n.onDrawForeground) { - continue; - } - - if (!overlapBounding(this.visible_area, n.getBounding(temp))) { - continue; - } //out of the visible area - - visible_nodes.push(n); - } - return visible_nodes; - }; - - /** - * renders the whole canvas content, by rendering in two separated canvas, one containing the background grid and the connections, and one containing the nodes) - * @method draw - **/ - LGraphCanvas.prototype.draw = function(force_canvas, force_bgcanvas) { - if (!this.canvas) { - return; - } - - //fps counting - var now = LiteGraph.getTime(); - this.render_time = (now - this.last_draw_time) * 0.001; - this.last_draw_time = now; - - if (this.graph) { - this.ds.computeVisibleArea(); - } - - if ( - this.dirty_bgcanvas || - force_bgcanvas || - this.always_render_background || - (this.graph && - this.graph._last_trigger_time && - now - this.graph._last_trigger_time < 1000) - ) { - this.drawBackCanvas(); - } - - if (this.dirty_canvas || force_canvas) { - this.drawFrontCanvas(); - } - - this.fps = this.render_time ? 1.0 / this.render_time : 0; - this.frame += 1; - }; - - /** - * draws the front canvas (the one containing all the nodes) - * @method drawFrontCanvas - **/ - LGraphCanvas.prototype.drawFrontCanvas = function() { - this.dirty_canvas = false; - - if (!this.ctx) { - this.ctx = this.bgcanvas.getContext("2d"); - } - var ctx = this.ctx; - if (!ctx) { - //maybe is using webgl... - return; - } - - if (ctx.start2D) { - ctx.start2D(); - } - - var canvas = this.canvas; - - //reset in case of error - ctx.restore(); - ctx.setTransform(1, 0, 0, 1, 0, 0); - - //clip dirty area if there is one, otherwise work in full canvas - if (this.dirty_area) { - ctx.save(); - ctx.beginPath(); - ctx.rect( - this.dirty_area[0], - this.dirty_area[1], - this.dirty_area[2], - this.dirty_area[3] - ); - ctx.clip(); - } - - //clear - //canvas.width = canvas.width; - if (this.clear_background) { - ctx.clearRect(0, 0, canvas.width, canvas.height); - } - - //draw bg canvas - if (this.bgcanvas == this.canvas) { - this.drawBackCanvas(); - } else { - ctx.drawImage(this.bgcanvas, 0, 0); - } - - //rendering - if (this.onRender) { - this.onRender(canvas, ctx); - } - - //info widget - if (this.show_info) { - this.renderInfo(ctx); - } - - if (this.graph) { - //apply transformations - ctx.save(); - this.ds.toCanvasContext(ctx); - - //draw nodes - var drawn_nodes = 0; - var visible_nodes = this.computeVisibleNodes( - null, - this.visible_nodes - ); - - for (var i = 0; i < visible_nodes.length; ++i) { - var node = visible_nodes[i]; - - //transform coords system - ctx.save(); - ctx.translate(node.pos[0], node.pos[1]); - - //Draw - this.drawNode(node, ctx); - drawn_nodes += 1; - - //Restore - ctx.restore(); - } - - //on top (debug) - if (this.render_execution_order) { - this.drawExecutionOrder(ctx); - } - - //connections ontop? - if (this.graph.config.links_ontop) { - if (!this.live_mode) { - this.drawConnections(ctx); - } - } - - //current connection (the one being dragged by the mouse) - if (this.connecting_pos != null) { - ctx.lineWidth = this.connections_width; - var link_color = null; - - switch (this.connecting_output.type) { - case LiteGraph.EVENT: - link_color = LiteGraph.EVENT_LINK_COLOR; - break; - default: - link_color = LiteGraph.CONNECTING_LINK_COLOR; - } - - //the connection being dragged by the mouse - this.renderLink( - ctx, - this.connecting_pos, - [this.canvas_mouse[0], this.canvas_mouse[1]], - null, - false, - null, - link_color, - this.connecting_output.dir || - (this.connecting_node.horizontal - ? LiteGraph.DOWN - : LiteGraph.RIGHT), - LiteGraph.CENTER - ); - - ctx.beginPath(); - if ( - this.connecting_output.type === LiteGraph.EVENT || - this.connecting_output.shape === LiteGraph.BOX_SHAPE - ) { - ctx.rect( - this.connecting_pos[0] - 6 + 0.5, - this.connecting_pos[1] - 5 + 0.5, - 14, - 10 - ); - } else { - ctx.arc( - this.connecting_pos[0], - this.connecting_pos[1], - 4, - 0, - Math.PI * 2 - ); - } - ctx.fill(); - - ctx.fillStyle = "#ffcc00"; - if (this._highlight_input) { - ctx.beginPath(); - ctx.arc( - this._highlight_input[0], - this._highlight_input[1], - 6, - 0, - Math.PI * 2 - ); - ctx.fill(); - } - } - - //the selection rectangle - if (this.dragging_rectangle) { - ctx.strokeStyle = "#FFF"; - ctx.strokeRect( - this.dragging_rectangle[0], - this.dragging_rectangle[1], - this.dragging_rectangle[2], - this.dragging_rectangle[3] - ); - } - - //on top of link center - if(this.over_link_center && this.render_link_tooltip) - this.drawLinkTooltip( ctx, this.over_link_center ); - else - if(this.onDrawLinkTooltip) //to remove - this.onDrawLinkTooltip(ctx,null); - - //custom info - if (this.onDrawForeground) { - this.onDrawForeground(ctx, this.visible_rect); - } - - ctx.restore(); - } - - if (this.onDrawOverlay) { - this.onDrawOverlay(ctx); - } - - if (this.dirty_area) { - ctx.restore(); - //this.dirty_area = null; - } - - if (ctx.finish2D) { - //this is a function I use in webgl renderer - ctx.finish2D(); - } - }; - - /** - * draws some useful stats in the corner of the canvas - * @method renderInfo - **/ - LGraphCanvas.prototype.renderInfo = function(ctx, x, y) { - x = x || 0; - y = y || 0; - - ctx.save(); - ctx.translate(x, y); - - ctx.font = "10px Arial"; - ctx.fillStyle = "#888"; - if (this.graph) { - ctx.fillText( "T: " + this.graph.globaltime.toFixed(2) + "s", 5, 13 * 1 ); - ctx.fillText("I: " + this.graph.iteration, 5, 13 * 2 ); - ctx.fillText("N: " + this.graph._nodes.length + " [" + this.visible_nodes.length + "]", 5, 13 * 3 ); - ctx.fillText("V: " + this.graph._version, 5, 13 * 4); - ctx.fillText("FPS:" + this.fps.toFixed(2), 5, 13 * 5); - } else { - ctx.fillText("No graph selected", 5, 13 * 1); - } - ctx.restore(); - }; - - /** - * draws the back canvas (the one containing the background and the connections) - * @method drawBackCanvas - **/ - LGraphCanvas.prototype.drawBackCanvas = function() { - var canvas = this.bgcanvas; - if ( - canvas.width != this.canvas.width || - canvas.height != this.canvas.height - ) { - canvas.width = this.canvas.width; - canvas.height = this.canvas.height; - } - - if (!this.bgctx) { - this.bgctx = this.bgcanvas.getContext("2d"); - } - var ctx = this.bgctx; - if (ctx.start) { - ctx.start(); - } - - //clear - if (this.clear_background) { - ctx.clearRect(0, 0, canvas.width, canvas.height); - } - - if (this._graph_stack && this._graph_stack.length) { - ctx.save(); - var parent_graph = this._graph_stack[this._graph_stack.length - 1]; - var subgraph_node = this.graph._subgraph_node; - ctx.strokeStyle = subgraph_node.bgcolor; - ctx.lineWidth = 10; - ctx.strokeRect(1, 1, canvas.width - 2, canvas.height - 2); - ctx.lineWidth = 1; - ctx.font = "40px Arial"; - ctx.textAlign = "center"; - ctx.fillStyle = subgraph_node.bgcolor || "#AAA"; - var title = ""; - for (var i = 1; i < this._graph_stack.length; ++i) { - title += - this._graph_stack[i]._subgraph_node.getTitle() + " >> "; - } - ctx.fillText( - title + subgraph_node.getTitle(), - canvas.width * 0.5, - 40 - ); - ctx.restore(); - } - - var bg_already_painted = false; - if (this.onRenderBackground) { - bg_already_painted = this.onRenderBackground(canvas, ctx); - } - - //reset in case of error - ctx.restore(); - ctx.setTransform(1, 0, 0, 1, 0, 0); - this.visible_links.length = 0; - - if (this.graph) { - //apply transformations - ctx.save(); - this.ds.toCanvasContext(ctx); - - //render BG - if ( - this.background_image && - this.ds.scale > 0.5 && - !bg_already_painted - ) { - if (this.zoom_modify_alpha) { - ctx.globalAlpha = - (1.0 - 0.5 / this.ds.scale) * this.editor_alpha; - } else { - ctx.globalAlpha = this.editor_alpha; - } - ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.imageSmoothingEnabled = false; - if ( - !this._bg_img || - this._bg_img.name != this.background_image - ) { - this._bg_img = new Image(); - this._bg_img.name = this.background_image; - this._bg_img.src = this.background_image; - var that = this; - this._bg_img.onload = function() { - that.draw(true, true); - }; - } - - var pattern = null; - if (this._pattern == null && this._bg_img.width > 0) { - pattern = ctx.createPattern(this._bg_img, "repeat"); - this._pattern_img = this._bg_img; - this._pattern = pattern; - } else { - pattern = this._pattern; - } - if (pattern) { - ctx.fillStyle = pattern; - ctx.fillRect( - this.visible_area[0], - this.visible_area[1], - this.visible_area[2], - this.visible_area[3] - ); - ctx.fillStyle = "transparent"; - } - - ctx.globalAlpha = 1.0; - ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.imageSmoothingEnabled = true; - } - - //groups - if (this.graph._groups.length && !this.live_mode) { - this.drawGroups(canvas, ctx); - } - - if (this.onDrawBackground) { - this.onDrawBackground(ctx, this.visible_area); - } - if (this.onBackgroundRender) { - //LEGACY - console.error( - "WARNING! onBackgroundRender deprecated, now is named onDrawBackground " - ); - this.onBackgroundRender = null; - } - - //DEBUG: show clipping area - //ctx.fillStyle = "red"; - //ctx.fillRect( this.visible_area[0] + 10, this.visible_area[1] + 10, this.visible_area[2] - 20, this.visible_area[3] - 20); - - //bg - if (this.render_canvas_border) { - ctx.strokeStyle = "#235"; - ctx.strokeRect(0, 0, canvas.width, canvas.height); - } - - if (this.render_connections_shadows) { - ctx.shadowColor = "#000"; - ctx.shadowOffsetX = 0; - ctx.shadowOffsetY = 0; - ctx.shadowBlur = 6; - } else { - ctx.shadowColor = "rgba(0,0,0,0)"; - } - - //draw connections - if (!this.live_mode) { - this.drawConnections(ctx); - } - - ctx.shadowColor = "rgba(0,0,0,0)"; - - //restore state - ctx.restore(); - } - - if (ctx.finish) { - ctx.finish(); - } - - this.dirty_bgcanvas = false; - this.dirty_canvas = true; //to force to repaint the front canvas with the bgcanvas - }; - - var temp_vec2 = new Float32Array(2); - - /** - * draws the given node inside the canvas - * @method drawNode - **/ - LGraphCanvas.prototype.drawNode = function(node, ctx) { - var glow = false; - this.current_node = node; - - var color = - node.color || - node.constructor.color || - LiteGraph.NODE_DEFAULT_COLOR; - var bgcolor = - node.bgcolor || - node.constructor.bgcolor || - LiteGraph.NODE_DEFAULT_BGCOLOR; - - //shadow and glow - if (node.mouseOver) { - glow = true; - } - - var low_quality = this.ds.scale < 0.6; //zoomed out - - //only render if it forces it to do it - if (this.live_mode) { - if (!node.flags.collapsed) { - ctx.shadowColor = "transparent"; - if (node.onDrawForeground) { - node.onDrawForeground(ctx, this, this.canvas); - } - } - return; - } - - var editor_alpha = this.editor_alpha; - ctx.globalAlpha = editor_alpha; - - if (this.render_shadows && !low_quality) { - ctx.shadowColor = LiteGraph.DEFAULT_SHADOW_COLOR; - ctx.shadowOffsetX = 2 * this.ds.scale; - ctx.shadowOffsetY = 2 * this.ds.scale; - ctx.shadowBlur = 3 * this.ds.scale; - } else { - ctx.shadowColor = "transparent"; - } - - //custom draw collapsed method (draw after shadows because they are affected) - if ( - node.flags.collapsed && - node.onDrawCollapsed && - node.onDrawCollapsed(ctx, this) == true - ) { - return; - } - - //clip if required (mask) - var shape = node._shape || LiteGraph.BOX_SHAPE; - var size = temp_vec2; - temp_vec2.set(node.size); - var horizontal = node.horizontal; // || node.flags.horizontal; - - if (node.flags.collapsed) { - ctx.font = this.inner_text_font; - var title = node.getTitle ? node.getTitle() : node.title; - if (title != null) { - node._collapsed_width = Math.min( - node.size[0], - ctx.measureText(title).width + - LiteGraph.NODE_TITLE_HEIGHT * 2 - ); //LiteGraph.NODE_COLLAPSED_WIDTH; - size[0] = node._collapsed_width; - size[1] = 0; - } - } - - if (node.clip_area) { - //Start clipping - ctx.save(); - ctx.beginPath(); - if (shape == LiteGraph.BOX_SHAPE) { - ctx.rect(0, 0, size[0], size[1]); - } else if (shape == LiteGraph.ROUND_SHAPE) { - ctx.roundRect(0, 0, size[0], size[1], 10); - } else if (shape == LiteGraph.CIRCLE_SHAPE) { - ctx.arc( - size[0] * 0.5, - size[1] * 0.5, - size[0] * 0.5, - 0, - Math.PI * 2 - ); - } - ctx.clip(); - } - - //draw shape - if (node.has_errors) { - bgcolor = "red"; - } - this.drawNodeShape( - node, - ctx, - size, - color, - bgcolor, - node.is_selected, - node.mouseOver - ); - ctx.shadowColor = "transparent"; - - //draw foreground - if (node.onDrawForeground) { - node.onDrawForeground(ctx, this, this.canvas); - } - - //connection slots - ctx.textAlign = horizontal ? "center" : "left"; - ctx.font = this.inner_text_font; - - var render_text = !low_quality; - - var out_slot = this.connecting_output; - ctx.lineWidth = 1; - - var max_y = 0; - var slot_pos = new Float32Array(2); //to reuse - - //render inputs and outputs - if (!node.flags.collapsed) { - //input connection slots - if (node.inputs) { - for (var i = 0; i < node.inputs.length; i++) { - var slot = node.inputs[i]; - - ctx.globalAlpha = editor_alpha; - //change opacity of incompatible slots when dragging a connection - if ( this.connecting_node && !LiteGraph.isValidConnection( slot.type , out_slot.type) ) { - ctx.globalAlpha = 0.4 * editor_alpha; - } - - ctx.fillStyle = - slot.link != null - ? slot.color_on || - this.default_connection_color.input_on - : slot.color_off || - this.default_connection_color.input_off; - - var pos = node.getConnectionPos(true, i, slot_pos); - pos[0] -= node.pos[0]; - pos[1] -= node.pos[1]; - if (max_y < pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5) { - max_y = pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5; - } - - ctx.beginPath(); - - if ( - slot.type === LiteGraph.EVENT || - slot.shape === LiteGraph.BOX_SHAPE - ) { - if (horizontal) { - ctx.rect( - pos[0] - 5 + 0.5, - pos[1] - 8 + 0.5, - 10, - 14 - ); - } else { - ctx.rect( - pos[0] - 6 + 0.5, - pos[1] - 5 + 0.5, - 14, - 10 - ); - } - } else if (slot.shape === LiteGraph.ARROW_SHAPE) { - ctx.moveTo(pos[0] + 8, pos[1] + 0.5); - ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5); - ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5); - ctx.closePath(); - } else { - if(low_quality) - ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); //faster - else - ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); - } - ctx.fill(); - - //render name - if (render_text) { - var text = slot.label != null ? slot.label : slot.name; - if (text) { - ctx.fillStyle = LiteGraph.NODE_TEXT_COLOR; - if (horizontal || slot.dir == LiteGraph.UP) { - ctx.fillText(text, pos[0], pos[1] - 10); - } else { - ctx.fillText(text, pos[0] + 10, pos[1] + 5); - } - } - } - } - } - - //output connection slots - if (this.connecting_node) { - ctx.globalAlpha = 0.4 * editor_alpha; - } - - ctx.textAlign = horizontal ? "center" : "right"; - ctx.strokeStyle = "black"; - if (node.outputs) { - for (var i = 0; i < node.outputs.length; i++) { - var slot = node.outputs[i]; - - var pos = node.getConnectionPos(false, i, slot_pos); - pos[0] -= node.pos[0]; - pos[1] -= node.pos[1]; - if (max_y < pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5) { - max_y = pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5; - } - - ctx.fillStyle = - slot.links && slot.links.length - ? slot.color_on || - this.default_connection_color.output_on - : slot.color_off || - this.default_connection_color.output_off; - ctx.beginPath(); - //ctx.rect( node.size[0] - 14,i*14,10,10); - - if ( - slot.type === LiteGraph.EVENT || - slot.shape === LiteGraph.BOX_SHAPE - ) { - if (horizontal) { - ctx.rect( - pos[0] - 5 + 0.5, - pos[1] - 8 + 0.5, - 10, - 14 - ); - } else { - ctx.rect( - pos[0] - 6 + 0.5, - pos[1] - 5 + 0.5, - 14, - 10 - ); - } - } else if (slot.shape === LiteGraph.ARROW_SHAPE) { - ctx.moveTo(pos[0] + 8, pos[1] + 0.5); - ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5); - ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5); - ctx.closePath(); - } else { - if(low_quality) - ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); - else - ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); - } - - //trigger - //if(slot.node_id != null && slot.slot == -1) - // ctx.fillStyle = "#F85"; - - //if(slot.links != null && slot.links.length) - ctx.fill(); - if(!low_quality) - ctx.stroke(); - - //render output name - if (render_text) { - var text = slot.label != null ? slot.label : slot.name; - if (text) { - ctx.fillStyle = LiteGraph.NODE_TEXT_COLOR; - if (horizontal || slot.dir == LiteGraph.DOWN) { - ctx.fillText(text, pos[0], pos[1] - 8); - } else { - ctx.fillText(text, pos[0] - 10, pos[1] + 5); - } - } - } - } - } - - ctx.textAlign = "left"; - ctx.globalAlpha = 1; - - if (node.widgets) { - var widgets_y = max_y; - if (horizontal || node.widgets_up) { - widgets_y = 2; - } - if( node.widgets_start_y != null ) - widgets_y = node.widgets_start_y; - this.drawNodeWidgets( - node, - widgets_y, - ctx, - this.node_widget && this.node_widget[0] == node - ? this.node_widget[1] - : null - ); - } - } else if (this.render_collapsed_slots) { - //if collapsed - var input_slot = null; - var output_slot = null; - - //get first connected slot to render - if (node.inputs) { - for (var i = 0; i < node.inputs.length; i++) { - var slot = node.inputs[i]; - if (slot.link == null) { - continue; - } - input_slot = slot; - break; - } - } - if (node.outputs) { - for (var i = 0; i < node.outputs.length; i++) { - var slot = node.outputs[i]; - if (!slot.links || !slot.links.length) { - continue; - } - output_slot = slot; - } - } - - if (input_slot) { - var x = 0; - var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; //center - if (horizontal) { - x = node._collapsed_width * 0.5; - y = -LiteGraph.NODE_TITLE_HEIGHT; - } - ctx.fillStyle = "#686"; - ctx.beginPath(); - if ( - slot.type === LiteGraph.EVENT || - slot.shape === LiteGraph.BOX_SHAPE - ) { - ctx.rect(x - 7 + 0.5, y - 4, 14, 8); - } else if (slot.shape === LiteGraph.ARROW_SHAPE) { - ctx.moveTo(x + 8, y); - ctx.lineTo(x + -4, y - 4); - ctx.lineTo(x + -4, y + 4); - ctx.closePath(); - } else { - ctx.arc(x, y, 4, 0, Math.PI * 2); - } - ctx.fill(); - } - - if (output_slot) { - var x = node._collapsed_width; - var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; //center - if (horizontal) { - x = node._collapsed_width * 0.5; - y = 0; - } - ctx.fillStyle = "#686"; - ctx.strokeStyle = "black"; - ctx.beginPath(); - if ( - slot.type === LiteGraph.EVENT || - slot.shape === LiteGraph.BOX_SHAPE - ) { - ctx.rect(x - 7 + 0.5, y - 4, 14, 8); - } else if (slot.shape === LiteGraph.ARROW_SHAPE) { - ctx.moveTo(x + 6, y); - ctx.lineTo(x - 6, y - 4); - ctx.lineTo(x - 6, y + 4); - ctx.closePath(); - } else { - ctx.arc(x, y, 4, 0, Math.PI * 2); - } - ctx.fill(); - //ctx.stroke(); - } - } - - if (node.clip_area) { - ctx.restore(); - } - - ctx.globalAlpha = 1.0; - }; - - //used by this.over_link_center - LGraphCanvas.prototype.drawLinkTooltip = function( ctx, link ) - { - var pos = link._pos; - ctx.fillStyle = "black"; - ctx.beginPath(); - ctx.arc( pos[0], pos[1], 3, 0, Math.PI * 2 ); - ctx.fill(); - - if(link.data == null) - return; - - if(this.onDrawLinkTooltip) - if( this.onDrawLinkTooltip(ctx,link,this) == true ) - return; - - var data = link.data; - var text = null; - - if( data.constructor === Number ) - text = data.toFixed(2); - else if( data.constructor === String ) - text = "\"" + data + "\""; - else if( data.constructor === Boolean ) - text = String(data); - else if (data.toToolTip) - text = data.toToolTip(); - else - text = "[" + data.constructor.name + "]"; - - if(text == null) - return; - text = text.substr(0,30); //avoid weird - - ctx.font = "14px Courier New"; - var info = ctx.measureText(text); - var w = info.width + 20; - var h = 24; - ctx.shadowColor = "black"; - ctx.shadowOffsetX = 2; - ctx.shadowOffsetY = 2; - ctx.shadowBlur = 3; - ctx.fillStyle = "#454"; - ctx.beginPath(); - ctx.roundRect( pos[0] - w*0.5, pos[1] - 15 - h, w, h,3, 3); - ctx.moveTo( pos[0] - 10, pos[1] - 15 ); - ctx.lineTo( pos[0] + 10, pos[1] - 15 ); - ctx.lineTo( pos[0], pos[1] - 5 ); - ctx.fill(); - ctx.shadowColor = "transparent"; - ctx.textAlign = "center"; - ctx.fillStyle = "#CEC"; - ctx.fillText(text, pos[0], pos[1] - 15 - h * 0.3); - } - - /** - * draws the shape of the given node in the canvas - * @method drawNodeShape - **/ - var tmp_area = new Float32Array(4); - - LGraphCanvas.prototype.drawNodeShape = function( - node, - ctx, - size, - fgcolor, - bgcolor, - selected, - mouse_over - ) { - //bg rect - ctx.strokeStyle = fgcolor; - ctx.fillStyle = bgcolor; - - var title_height = LiteGraph.NODE_TITLE_HEIGHT; - var low_quality = this.ds.scale < 0.5; - - //render node area depending on shape - var shape = - node._shape || node.constructor.shape || LiteGraph.ROUND_SHAPE; - - var title_mode = node.constructor.title_mode; - - var render_title = true; - if (title_mode == LiteGraph.TRANSPARENT_TITLE) { - render_title = false; - } else if (title_mode == LiteGraph.AUTOHIDE_TITLE && mouse_over) { - render_title = true; - } - - var area = tmp_area; - area[0] = 0; //x - area[1] = render_title ? -title_height : 0; //y - area[2] = size[0] + 1; //w - area[3] = render_title ? size[1] + title_height : size[1]; //h - - var old_alpha = ctx.globalAlpha; - - //full node shape - //if(node.flags.collapsed) - { - ctx.beginPath(); - if (shape == LiteGraph.BOX_SHAPE || low_quality) { - ctx.fillRect(area[0], area[1], area[2], area[3]); - } else if ( - shape == LiteGraph.ROUND_SHAPE || - shape == LiteGraph.CARD_SHAPE - ) { - ctx.roundRect( - area[0], - area[1], - area[2], - area[3], - this.round_radius, - shape == LiteGraph.CARD_SHAPE ? 0 : this.round_radius - ); - } else if (shape == LiteGraph.CIRCLE_SHAPE) { - ctx.arc( - size[0] * 0.5, - size[1] * 0.5, - size[0] * 0.5, - 0, - Math.PI * 2 - ); - } - ctx.fill(); - - //separator - if(!node.flags.collapsed) - { - ctx.shadowColor = "transparent"; - ctx.fillStyle = "rgba(0,0,0,0.2)"; - ctx.fillRect(0, -1, area[2], 2); - } - } - ctx.shadowColor = "transparent"; - - if (node.onDrawBackground) { - node.onDrawBackground(ctx, this, this.canvas); - } - - //title bg (remember, it is rendered ABOVE the node) - if (render_title || title_mode == LiteGraph.TRANSPARENT_TITLE) { - //title bar - if (node.onDrawTitleBar) { - node.onDrawTitleBar( - ctx, - title_height, - size, - this.ds.scale, - fgcolor - ); - } else if ( - title_mode != LiteGraph.TRANSPARENT_TITLE && - (node.constructor.title_color || this.render_title_colored) - ) { - var title_color = node.constructor.title_color || fgcolor; - - if (node.flags.collapsed) { - ctx.shadowColor = LiteGraph.DEFAULT_SHADOW_COLOR; - } - - //* gradient test - if (this.use_gradients) { - var grad = LGraphCanvas.gradients[title_color]; - if (!grad) { - grad = LGraphCanvas.gradients[ title_color ] = ctx.createLinearGradient(0, 0, 400, 0); - grad.addColorStop(0, title_color); - grad.addColorStop(1, "#000"); - } - ctx.fillStyle = grad; - } else { - ctx.fillStyle = title_color; - } - - //ctx.globalAlpha = 0.5 * old_alpha; - ctx.beginPath(); - if (shape == LiteGraph.BOX_SHAPE || low_quality) { - ctx.rect(0, -title_height, size[0] + 1, title_height); - } else if ( shape == LiteGraph.ROUND_SHAPE || shape == LiteGraph.CARD_SHAPE ) { - ctx.roundRect( - 0, - -title_height, - size[0] + 1, - title_height, - this.round_radius, - node.flags.collapsed ? this.round_radius : 0 - ); - } - ctx.fill(); - ctx.shadowColor = "transparent"; - } - - //title box - var box_size = 10; - if (node.onDrawTitleBox) { - node.onDrawTitleBox(ctx, title_height, size, this.ds.scale); - } else if ( - shape == LiteGraph.ROUND_SHAPE || - shape == LiteGraph.CIRCLE_SHAPE || - shape == LiteGraph.CARD_SHAPE - ) { - if (low_quality) { - ctx.fillStyle = "black"; - ctx.beginPath(); - ctx.arc( - title_height * 0.5, - title_height * -0.5, - box_size * 0.5 + 1, - 0, - Math.PI * 2 - ); - ctx.fill(); - } - - ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR; - if(low_quality) - ctx.fillRect( title_height * 0.5 - box_size *0.5, title_height * -0.5 - box_size *0.5, box_size , box_size ); - else - { - ctx.beginPath(); - ctx.arc( - title_height * 0.5, - title_height * -0.5, - box_size * 0.5, - 0, - Math.PI * 2 - ); - ctx.fill(); - } - } else { - if (low_quality) { - ctx.fillStyle = "black"; - ctx.fillRect( - (title_height - box_size) * 0.5 - 1, - (title_height + box_size) * -0.5 - 1, - box_size + 2, - box_size + 2 - ); - } - ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR; - ctx.fillRect( - (title_height - box_size) * 0.5, - (title_height + box_size) * -0.5, - box_size, - box_size - ); - } - ctx.globalAlpha = old_alpha; - - //title text - if (node.onDrawTitleText) { - node.onDrawTitleText( - ctx, - title_height, - size, - this.ds.scale, - this.title_text_font, - selected - ); - } - if (!low_quality) { - ctx.font = this.title_text_font; - var title = String(node.getTitle()); - if (title) { - if (selected) { - ctx.fillStyle = "white"; - } else { - ctx.fillStyle = - node.constructor.title_text_color || - this.node_title_color; - } - if (node.flags.collapsed) { - ctx.textAlign = "left"; - var measure = ctx.measureText(title); - ctx.fillText( - title.substr(0,20), //avoid urls too long - title_height,// + measure.width * 0.5, - LiteGraph.NODE_TITLE_TEXT_Y - title_height - ); - ctx.textAlign = "left"; - } else { - ctx.textAlign = "left"; - ctx.fillText( - title, - title_height, - LiteGraph.NODE_TITLE_TEXT_Y - title_height - ); - } - } - } - - if (node.onDrawTitle) { - node.onDrawTitle(ctx); - } - } - - //render selection marker - if (selected) { - if (node.onBounding) { - node.onBounding(area); - } - - if (title_mode == LiteGraph.TRANSPARENT_TITLE) { - area[1] -= title_height; - area[3] += title_height; - } - ctx.lineWidth = 1; - ctx.globalAlpha = 0.8; - ctx.beginPath(); - if (shape == LiteGraph.BOX_SHAPE) { - ctx.rect( - -6 + area[0], - -6 + area[1], - 12 + area[2], - 12 + area[3] - ); - } else if ( - shape == LiteGraph.ROUND_SHAPE || - (shape == LiteGraph.CARD_SHAPE && node.flags.collapsed) - ) { - ctx.roundRect( - -6 + area[0], - -6 + area[1], - 12 + area[2], - 12 + area[3], - this.round_radius * 2 - ); - } else if (shape == LiteGraph.CARD_SHAPE) { - ctx.roundRect( - -6 + area[0], - -6 + area[1], - 12 + area[2], - 12 + area[3], - this.round_radius * 2, - 2 - ); - } else if (shape == LiteGraph.CIRCLE_SHAPE) { - ctx.arc( - size[0] * 0.5, - size[1] * 0.5, - size[0] * 0.5 + 6, - 0, - Math.PI * 2 - ); - } - ctx.strokeStyle = "#FFF"; - ctx.stroke(); - ctx.strokeStyle = fgcolor; - ctx.globalAlpha = 1; - } - }; - - var margin_area = new Float32Array(4); - var link_bounding = new Float32Array(4); - var tempA = new Float32Array(2); - var tempB = new Float32Array(2); - - /** - * draws every connection visible in the canvas - * OPTIMIZE THIS: pre-catch connections position instead of recomputing them every time - * @method drawConnections - **/ - LGraphCanvas.prototype.drawConnections = function(ctx) { - var now = LiteGraph.getTime(); - var visible_area = this.visible_area; - margin_area[0] = visible_area[0] - 20; - margin_area[1] = visible_area[1] - 20; - margin_area[2] = visible_area[2] + 40; - margin_area[3] = visible_area[3] + 40; - - //draw connections - ctx.lineWidth = this.connections_width; - - ctx.fillStyle = "#AAA"; - ctx.strokeStyle = "#AAA"; - ctx.globalAlpha = this.editor_alpha; - //for every node - var nodes = this.graph._nodes; - for (var n = 0, l = nodes.length; n < l; ++n) { - var node = nodes[n]; - //for every input (we render just inputs because it is easier as every slot can only have one input) - if (!node.inputs || !node.inputs.length) { - continue; - } - - for (var i = 0; i < node.inputs.length; ++i) { - var input = node.inputs[i]; - if (!input || input.link == null) { - continue; - } - var link_id = input.link; - var link = this.graph.links[link_id]; - if (!link) { - continue; - } - - //find link info - var start_node = this.graph.getNodeById(link.origin_id); - if (start_node == null) { - continue; - } - var start_node_slot = link.origin_slot; - var start_node_slotpos = null; - if (start_node_slot == -1) { - start_node_slotpos = [ - start_node.pos[0] + 10, - start_node.pos[1] + 10 - ]; - } else { - start_node_slotpos = start_node.getConnectionPos( - false, - start_node_slot, - tempA - ); - } - var end_node_slotpos = node.getConnectionPos(true, i, tempB); - - //compute link bounding - link_bounding[0] = start_node_slotpos[0]; - link_bounding[1] = start_node_slotpos[1]; - link_bounding[2] = end_node_slotpos[0] - start_node_slotpos[0]; - link_bounding[3] = end_node_slotpos[1] - start_node_slotpos[1]; - if (link_bounding[2] < 0) { - link_bounding[0] += link_bounding[2]; - link_bounding[2] = Math.abs(link_bounding[2]); - } - if (link_bounding[3] < 0) { - link_bounding[1] += link_bounding[3]; - link_bounding[3] = Math.abs(link_bounding[3]); - } - - //skip links outside of the visible area of the canvas - if (!overlapBounding(link_bounding, margin_area)) { - continue; - } - - var start_slot = start_node.outputs[start_node_slot]; - var end_slot = node.inputs[i]; - if (!start_slot || !end_slot) { - continue; - } - var start_dir = - start_slot.dir || - (start_node.horizontal ? LiteGraph.DOWN : LiteGraph.RIGHT); - var end_dir = - end_slot.dir || - (node.horizontal ? LiteGraph.UP : LiteGraph.LEFT); - - this.renderLink( - ctx, - start_node_slotpos, - end_node_slotpos, - link, - false, - 0, - null, - start_dir, - end_dir - ); - - //event triggered rendered on top - if (link && link._last_time && now - link._last_time < 1000) { - var f = 2.0 - (now - link._last_time) * 0.002; - var tmp = ctx.globalAlpha; - ctx.globalAlpha = tmp * f; - this.renderLink( - ctx, - start_node_slotpos, - end_node_slotpos, - link, - true, - f, - "white", - start_dir, - end_dir - ); - ctx.globalAlpha = tmp; - } - } - } - ctx.globalAlpha = 1; - }; - - /** - * draws a link between two points - * @method renderLink - * @param {vec2} a start pos - * @param {vec2} b end pos - * @param {Object} link the link object with all the link info - * @param {boolean} skip_border ignore the shadow of the link - * @param {boolean} flow show flow animation (for events) - * @param {string} color the color for the link - * @param {number} start_dir the direction enum - * @param {number} end_dir the direction enum - * @param {number} num_sublines number of sublines (useful to represent vec3 or rgb) - **/ - LGraphCanvas.prototype.renderLink = function( - ctx, - a, - b, - link, - skip_border, - flow, - color, - start_dir, - end_dir, - num_sublines - ) { - if (link) { - this.visible_links.push(link); - } - - //choose color - if (!color && link) { - color = link.color || LGraphCanvas.link_type_colors[link.type]; - } - if (!color) { - color = this.default_link_color; - } - if (link != null && this.highlighted_links[link.id]) { - color = "#FFF"; - } - - start_dir = start_dir || LiteGraph.RIGHT; - end_dir = end_dir || LiteGraph.LEFT; - - var dist = distance(a, b); - - if (this.render_connections_border && this.ds.scale > 0.6) { - ctx.lineWidth = this.connections_width + 4; - } - ctx.lineJoin = "round"; - num_sublines = num_sublines || 1; - if (num_sublines > 1) { - ctx.lineWidth = 0.5; - } - - //begin line shape - ctx.beginPath(); - for (var i = 0; i < num_sublines; i += 1) { - var offsety = (i - (num_sublines - 1) * 0.5) * 5; - - if (this.links_render_mode == LiteGraph.SPLINE_LINK) { - ctx.moveTo(a[0], a[1] + offsety); - var start_offset_x = 0; - var start_offset_y = 0; - var end_offset_x = 0; - var end_offset_y = 0; - switch (start_dir) { - case LiteGraph.LEFT: - start_offset_x = dist * -0.25; - break; - case LiteGraph.RIGHT: - start_offset_x = dist * 0.25; - break; - case LiteGraph.UP: - start_offset_y = dist * -0.25; - break; - case LiteGraph.DOWN: - start_offset_y = dist * 0.25; - break; - } - switch (end_dir) { - case LiteGraph.LEFT: - end_offset_x = dist * -0.25; - break; - case LiteGraph.RIGHT: - end_offset_x = dist * 0.25; - break; - case LiteGraph.UP: - end_offset_y = dist * -0.25; - break; - case LiteGraph.DOWN: - end_offset_y = dist * 0.25; - break; - } - ctx.bezierCurveTo( - a[0] + start_offset_x, - a[1] + start_offset_y + offsety, - b[0] + end_offset_x, - b[1] + end_offset_y + offsety, - b[0], - b[1] + offsety - ); - } else if (this.links_render_mode == LiteGraph.LINEAR_LINK) { - ctx.moveTo(a[0], a[1] + offsety); - var start_offset_x = 0; - var start_offset_y = 0; - var end_offset_x = 0; - var end_offset_y = 0; - switch (start_dir) { - case LiteGraph.LEFT: - start_offset_x = -1; - break; - case LiteGraph.RIGHT: - start_offset_x = 1; - break; - case LiteGraph.UP: - start_offset_y = -1; - break; - case LiteGraph.DOWN: - start_offset_y = 1; - break; - } - switch (end_dir) { - case LiteGraph.LEFT: - end_offset_x = -1; - break; - case LiteGraph.RIGHT: - end_offset_x = 1; - break; - case LiteGraph.UP: - end_offset_y = -1; - break; - case LiteGraph.DOWN: - end_offset_y = 1; - break; - } - var l = 15; - ctx.lineTo( - a[0] + start_offset_x * l, - a[1] + start_offset_y * l + offsety - ); - ctx.lineTo( - b[0] + end_offset_x * l, - b[1] + end_offset_y * l + offsety - ); - ctx.lineTo(b[0], b[1] + offsety); - } else if (this.links_render_mode == LiteGraph.STRAIGHT_LINK) { - ctx.moveTo(a[0], a[1]); - var start_x = a[0]; - var start_y = a[1]; - var end_x = b[0]; - var end_y = b[1]; - if (start_dir == LiteGraph.RIGHT) { - start_x += 10; - } else { - start_y += 10; - } - if (end_dir == LiteGraph.LEFT) { - end_x -= 10; - } else { - end_y -= 10; - } - ctx.lineTo(start_x, start_y); - ctx.lineTo((start_x + end_x) * 0.5, start_y); - ctx.lineTo((start_x + end_x) * 0.5, end_y); - ctx.lineTo(end_x, end_y); - ctx.lineTo(b[0], b[1]); - } else { - return; - } //unknown - } - - //rendering the outline of the connection can be a little bit slow - if ( - this.render_connections_border && - this.ds.scale > 0.6 && - !skip_border - ) { - ctx.strokeStyle = "rgba(0,0,0,0.5)"; - ctx.stroke(); - } - - ctx.lineWidth = this.connections_width; - ctx.fillStyle = ctx.strokeStyle = color; - ctx.stroke(); - //end line shape - - var pos = this.computeConnectionPoint(a, b, 0.5, start_dir, end_dir); - if (link && link._pos) { - link._pos[0] = pos[0]; - link._pos[1] = pos[1]; - } - - //render arrow in the middle - if ( - this.ds.scale >= 0.6 && - this.highquality_render && - end_dir != LiteGraph.CENTER - ) { - //render arrow - if (this.render_connection_arrows) { - //compute two points in the connection - var posA = this.computeConnectionPoint( - a, - b, - 0.25, - start_dir, - end_dir - ); - var posB = this.computeConnectionPoint( - a, - b, - 0.26, - start_dir, - end_dir - ); - var posC = this.computeConnectionPoint( - a, - b, - 0.75, - start_dir, - end_dir - ); - var posD = this.computeConnectionPoint( - a, - b, - 0.76, - start_dir, - end_dir - ); - - //compute the angle between them so the arrow points in the right direction - var angleA = 0; - var angleB = 0; - if (this.render_curved_connections) { - angleA = -Math.atan2(posB[0] - posA[0], posB[1] - posA[1]); - angleB = -Math.atan2(posD[0] - posC[0], posD[1] - posC[1]); - } else { - angleB = angleA = b[1] > a[1] ? 0 : Math.PI; - } - - //render arrow - ctx.save(); - ctx.translate(posA[0], posA[1]); - ctx.rotate(angleA); - ctx.beginPath(); - ctx.moveTo(-5, -3); - ctx.lineTo(0, +7); - ctx.lineTo(+5, -3); - ctx.fill(); - ctx.restore(); - ctx.save(); - ctx.translate(posC[0], posC[1]); - ctx.rotate(angleB); - ctx.beginPath(); - ctx.moveTo(-5, -3); - ctx.lineTo(0, +7); - ctx.lineTo(+5, -3); - ctx.fill(); - ctx.restore(); - } - - //circle - ctx.beginPath(); - ctx.arc(pos[0], pos[1], 5, 0, Math.PI * 2); - ctx.fill(); - } - - //render flowing points - if (flow) { - ctx.fillStyle = color; - for (var i = 0; i < 5; ++i) { - var f = (LiteGraph.getTime() * 0.001 + i * 0.2) % 1; - var pos = this.computeConnectionPoint( - a, - b, - f, - start_dir, - end_dir - ); - ctx.beginPath(); - ctx.arc(pos[0], pos[1], 5, 0, 2 * Math.PI); - ctx.fill(); - } - } - }; - - //returns the link center point based on curvature - LGraphCanvas.prototype.computeConnectionPoint = function( - a, - b, - t, - start_dir, - end_dir - ) { - start_dir = start_dir || LiteGraph.RIGHT; - end_dir = end_dir || LiteGraph.LEFT; - - var dist = distance(a, b); - var p0 = a; - var p1 = [a[0], a[1]]; - var p2 = [b[0], b[1]]; - var p3 = b; - - switch (start_dir) { - case LiteGraph.LEFT: - p1[0] += dist * -0.25; - break; - case LiteGraph.RIGHT: - p1[0] += dist * 0.25; - break; - case LiteGraph.UP: - p1[1] += dist * -0.25; - break; - case LiteGraph.DOWN: - p1[1] += dist * 0.25; - break; - } - switch (end_dir) { - case LiteGraph.LEFT: - p2[0] += dist * -0.25; - break; - case LiteGraph.RIGHT: - p2[0] += dist * 0.25; - break; - case LiteGraph.UP: - p2[1] += dist * -0.25; - break; - case LiteGraph.DOWN: - p2[1] += dist * 0.25; - break; - } - - var c1 = (1 - t) * (1 - t) * (1 - t); - var c2 = 3 * ((1 - t) * (1 - t)) * t; - var c3 = 3 * (1 - t) * (t * t); - var c4 = t * t * t; - - var x = c1 * p0[0] + c2 * p1[0] + c3 * p2[0] + c4 * p3[0]; - var y = c1 * p0[1] + c2 * p1[1] + c3 * p2[1] + c4 * p3[1]; - return [x, y]; - }; - - LGraphCanvas.prototype.drawExecutionOrder = function(ctx) { - ctx.shadowColor = "transparent"; - ctx.globalAlpha = 0.25; - - ctx.textAlign = "center"; - ctx.strokeStyle = "white"; - ctx.globalAlpha = 0.75; - - var visible_nodes = this.visible_nodes; - for (var i = 0; i < visible_nodes.length; ++i) { - var node = visible_nodes[i]; - ctx.fillStyle = "black"; - ctx.fillRect( - node.pos[0] - LiteGraph.NODE_TITLE_HEIGHT, - node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, - LiteGraph.NODE_TITLE_HEIGHT, - LiteGraph.NODE_TITLE_HEIGHT - ); - if (node.order == 0) { - ctx.strokeRect( - node.pos[0] - LiteGraph.NODE_TITLE_HEIGHT + 0.5, - node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5, - LiteGraph.NODE_TITLE_HEIGHT, - LiteGraph.NODE_TITLE_HEIGHT - ); - } - ctx.fillStyle = "#FFF"; - ctx.fillText( - node.order, - node.pos[0] + LiteGraph.NODE_TITLE_HEIGHT * -0.5, - node.pos[1] - 6 - ); - } - ctx.globalAlpha = 1; - }; - - /** - * draws the widgets stored inside a node - * @method drawNodeWidgets - **/ - LGraphCanvas.prototype.drawNodeWidgets = function( - node, - posY, - ctx, - active_widget - ) { - if (!node.widgets || !node.widgets.length) { - return 0; - } - var width = node.size[0]; - var widgets = node.widgets; - posY += 2; - var H = LiteGraph.NODE_WIDGET_HEIGHT; - var show_text = this.ds.scale > 0.5; - ctx.save(); - ctx.globalAlpha = this.editor_alpha; - var outline_color = LiteGraph.WIDGET_OUTLINE_COLOR; - var background_color = LiteGraph.WIDGET_BGCOLOR; - var text_color = LiteGraph.WIDGET_TEXT_COLOR; - var secondary_text_color = LiteGraph.WIDGET_SECONDARY_TEXT_COLOR; - var margin = 15; - - for (var i = 0; i < widgets.length; ++i) { - var w = widgets[i]; - var y = posY; - if (w.y) { - y = w.y; - } - w.last_y = y; - ctx.strokeStyle = outline_color; - ctx.fillStyle = "#222"; - ctx.textAlign = "left"; - if(w.disabled) - ctx.globalAlpha *= 0.5; - - switch (w.type) { - case "button": - if (w.clicked) { - ctx.fillStyle = "#AAA"; - w.clicked = false; - this.dirty_canvas = true; - } - ctx.fillRect(margin, y, width - margin * 2, H); - if(show_text && !w.disabled) - ctx.strokeRect( margin, y, width - margin * 2, H ); - if (show_text) { - ctx.textAlign = "center"; - ctx.fillStyle = text_color; - ctx.fillText(w.name, width * 0.5, y + H * 0.7); - } - break; - case "toggle": - ctx.textAlign = "left"; - ctx.strokeStyle = outline_color; - ctx.fillStyle = background_color; - ctx.beginPath(); - if (show_text) - ctx.roundRect(margin, posY, width - margin * 2, H, H * 0.5); - else - ctx.rect(margin, posY, width - margin * 2, H ); - ctx.fill(); - if(show_text && !w.disabled) - ctx.stroke(); - ctx.fillStyle = w.value ? "#89A" : "#333"; - ctx.beginPath(); - ctx.arc( width - margin * 2, y + H * 0.5, H * 0.36, 0, Math.PI * 2 ); - ctx.fill(); - if (show_text) { - ctx.fillStyle = secondary_text_color; - if (w.name != null) { - ctx.fillText(w.name, margin * 2, y + H * 0.7); - } - ctx.fillStyle = w.value ? text_color : secondary_text_color; - ctx.textAlign = "right"; - ctx.fillText( - w.value - ? w.options.on || "true" - : w.options.off || "false", - width - 40, - y + H * 0.7 - ); - } - break; - case "slider": - ctx.fillStyle = background_color; - ctx.fillRect(margin, y, width - margin * 2, H); - var range = w.options.max - w.options.min; - var nvalue = (w.value - w.options.min) / range; - ctx.fillStyle = active_widget == w ? "#89A" : "#678"; - ctx.fillRect(margin, y, nvalue * (width - margin * 2), H); - if(show_text && !w.disabled) - ctx.strokeRect(margin, y, width - margin * 2, H); - if (w.marker) { - var marker_nvalue = (w.marker - w.options.min) / range; - ctx.fillStyle = "#AA9"; - ctx.fillRect( margin + marker_nvalue * (width - margin * 2), y, 2, H ); - } - if (show_text) { - ctx.textAlign = "center"; - ctx.fillStyle = text_color; - ctx.fillText( - w.name + " " + Number(w.value).toFixed(3), - width * 0.5, - y + H * 0.7 - ); - } - break; - case "number": - case "combo": - ctx.textAlign = "left"; - ctx.strokeStyle = outline_color; - ctx.fillStyle = background_color; - ctx.beginPath(); - if(show_text) - ctx.roundRect(margin, posY, width - margin * 2, H, H * 0.5); - else - ctx.rect(margin, posY, width - margin * 2, H ); - ctx.fill(); - if (show_text) { - if(!w.disabled) - ctx.stroke(); - ctx.fillStyle = text_color; - if(!w.disabled) - { - ctx.beginPath(); - ctx.moveTo(margin + 16, posY + 5); - ctx.lineTo(margin + 6, posY + H * 0.5); - ctx.lineTo(margin + 16, posY + H - 5); - ctx.fill(); - ctx.beginPath(); - ctx.moveTo(width - margin - 16, posY + 5); - ctx.lineTo(width - margin - 6, posY + H * 0.5); - ctx.lineTo(width - margin - 16, posY + H - 5); - ctx.fill(); - } - ctx.fillStyle = secondary_text_color; - ctx.fillText(w.name, margin * 2 + 5, y + H * 0.7); - ctx.fillStyle = text_color; - ctx.textAlign = "right"; - if (w.type == "number") { - ctx.fillText( - Number(w.value).toFixed( - w.options.precision !== undefined - ? w.options.precision - : 3 - ), - width - margin * 2 - 20, - y + H * 0.7 - ); - } else { - var v = w.value; - if( w.options.values ) - { - var values = w.options.values; - if( values.constructor === Function ) - values = values(); - if(values && values.constructor !== Array) - v = values[ w.value ]; - } - ctx.fillText( - v, - width - margin * 2 - 20, - y + H * 0.7 - ); - } - } - break; - case "string": - case "text": - ctx.textAlign = "left"; - ctx.strokeStyle = outline_color; - ctx.fillStyle = background_color; - ctx.beginPath(); - if (show_text) - ctx.roundRect(margin, posY, width - margin * 2, H, H * 0.5); - else - ctx.rect( margin, posY, width - margin * 2, H ); - ctx.fill(); - if (show_text) { - ctx.save(); - ctx.beginPath(); - ctx.rect(margin, posY, width - margin * 2, H); - ctx.clip(); - - ctx.stroke(); - ctx.fillStyle = secondary_text_color; - if (w.name != null) { - ctx.fillText(w.name, margin * 2, y + H * 0.7); - } - ctx.fillStyle = text_color; - ctx.textAlign = "right"; - ctx.fillText(String(w.value).substr(0,30), width - margin * 2, y + H * 0.7); //30 chars max - ctx.restore(); - } - break; - default: - if (w.draw) { - w.draw(ctx, node, width, y, H); - } - break; - } - posY += (w.computeSize ? w.computeSize(width)[1] : H) + 4; - ctx.globalAlpha = this.editor_alpha; - - } - ctx.restore(); - ctx.textAlign = "left"; - }; - - /** - * process an event on widgets - * @method processNodeWidgets - **/ - LGraphCanvas.prototype.processNodeWidgets = function( - node, - pos, - event, - active_widget - ) { - if (!node.widgets || !node.widgets.length) { - return null; - } - - var x = pos[0] - node.pos[0]; - var y = pos[1] - node.pos[1]; - var width = node.size[0]; - var that = this; - var ref_window = this.getCanvasWindow(); - - for (var i = 0; i < node.widgets.length; ++i) { - var w = node.widgets[i]; - if(!w || w.disabled) - continue; - var widget_height = w.computeSize ? w.computeSize(width)[1] : LiteGraph.NODE_WIDGET_HEIGHT; - if ( w == active_widget || (x > 6 && x < width - 12 && y > w.last_y && y < w.last_y + widget_height) ) { - //inside widget - switch (w.type) { - case "button": - if (event.type === "mousemove") { - break; - } - if (w.callback) { - setTimeout(function() { - w.callback(w, that, node, pos, event); - }, 20); - } - w.clicked = true; - this.dirty_canvas = true; - break; - case "slider": - var range = w.options.max - w.options.min; - var nvalue = Math.clamp((x - 10) / (width - 20), 0, 1); - w.value = - w.options.min + - (w.options.max - w.options.min) * nvalue; - if (w.callback) { - setTimeout(function() { - inner_value_change(w, w.value); - }, 20); - } - this.dirty_canvas = true; - break; - case "number": - case "combo": - var old_value = w.value; - if (event.type == "mousemove" && w.type == "number") { - w.value += event.deltaX * 0.1 * (w.options.step || 1); - if ( w.options.min != null && w.value < w.options.min ) { - w.value = w.options.min; - } - if ( w.options.max != null && w.value > w.options.max ) { - w.value = w.options.max; - } - } else if (event.type == "mousedown") { - var values = w.options.values; - if (values && values.constructor === Function) { - values = w.options.values(w, node); - } - var values_list = null; - - if( w.type != "number") - values_list = values.constructor === Array ? values : Object.keys(values); - - var delta = x < 40 ? -1 : x > width - 40 ? 1 : 0; - if (w.type == "number") { - w.value += delta * 0.1 * (w.options.step || 1); - if ( w.options.min != null && w.value < w.options.min ) { - w.value = w.options.min; - } - if ( w.options.max != null && w.value > w.options.max ) { - w.value = w.options.max; - } - } else if (delta) { //clicked in arrow, used for combos - var index = -1; - if(values.constructor === Object) - index = values_list.indexOf( String( w.value ) ) + delta; - else - index = values_list.indexOf( w.value ) + delta; - if (index >= values_list.length) { - index = values_list.length - 1; - } - if (index < 0) { - index = 0; - } - if( values.constructor === Array ) - w.value = values[index]; - else - w.value = index; - } else { //combo clicked - var text_values = values != values_list ? Object.values(values) : values; - var menu = new LiteGraph.ContextMenu(text_values, { - scale: Math.max(1, this.ds.scale), - event: event, - className: "dark", - callback: inner_clicked.bind(w) - }, - ref_window); - function inner_clicked(v, option, event) { - if(values != values_list) - v = text_values.indexOf(v); - this.value = v; - inner_value_change(this, v); - that.dirty_canvas = true; - return false; - } - } - } //end mousedown - else if(event.type == "mouseup" && w.type == "number") - { - var delta = x < 40 ? -1 : x > width - 40 ? 1 : 0; - if (event.click_time < 200 && delta == 0) { - this.prompt("Value",w.value,function(v) { - this.value = Number(v); - inner_value_change(this, this.value); - }.bind(w), - event); - } - } - - if( old_value != w.value ) - setTimeout( - function() { - inner_value_change(this, this.value); - }.bind(w), - 20 - ); - this.dirty_canvas = true; - break; - case "toggle": - if (event.type == "mousedown") { - w.value = !w.value; - setTimeout(function() { - inner_value_change(w, w.value); - }, 20); - } - break; - case "string": - case "text": - if (event.type == "mousedown") { - this.prompt("Value",w.value,function(v) { - this.value = v; - inner_value_change(this, v); - }.bind(w), - event); - } - break; - default: - if (w.mouse) { - this.dirty_canvas = w.mouse(event, [x, y], node); - } - break; - } //end switch - - return w; - } - } - - function inner_value_change(widget, value) { - widget.value = value; - if ( widget.options && widget.options.property && node.properties[widget.options.property] !== undefined ) { - node.setProperty( widget.options.property, value ); - } - if (widget.callback) { - widget.callback(widget.value, that, node, pos, event); - } - } - - return null; - }; - - /** - * draws every group area in the background - * @method drawGroups - **/ - LGraphCanvas.prototype.drawGroups = function(canvas, ctx) { - if (!this.graph) { - return; - } - - var groups = this.graph._groups; - - ctx.save(); - ctx.globalAlpha = 0.5 * this.editor_alpha; - - for (var i = 0; i < groups.length; ++i) { - var group = groups[i]; - - if (!overlapBounding(this.visible_area, group._bounding)) { - continue; - } //out of the visible area - - ctx.fillStyle = group.color || "#335"; - ctx.strokeStyle = group.color || "#335"; - var pos = group._pos; - var size = group._size; - ctx.globalAlpha = 0.25 * this.editor_alpha; - ctx.beginPath(); - ctx.rect(pos[0] + 0.5, pos[1] + 0.5, size[0], size[1]); - ctx.fill(); - ctx.globalAlpha = this.editor_alpha; - ctx.stroke(); - - ctx.beginPath(); - ctx.moveTo(pos[0] + size[0], pos[1] + size[1]); - ctx.lineTo(pos[0] + size[0] - 10, pos[1] + size[1]); - ctx.lineTo(pos[0] + size[0], pos[1] + size[1] - 10); - ctx.fill(); - - var font_size = - group.font_size || LiteGraph.DEFAULT_GROUP_FONT_SIZE; - ctx.font = font_size + "px Arial"; - ctx.fillText(group.title, pos[0] + 4, pos[1] + font_size); - } - - ctx.restore(); - }; - - LGraphCanvas.prototype.adjustNodesSize = function() { - var nodes = this.graph._nodes; - for (var i = 0; i < nodes.length; ++i) { - nodes[i].size = nodes[i].computeSize(); - } - this.setDirty(true, true); - }; - - /** - * resizes the canvas to a given size, if no size is passed, then it tries to fill the parentNode - * @method resize - **/ - LGraphCanvas.prototype.resize = function(width, height) { - if (!width && !height) { - var parent = this.canvas.parentNode; - width = parent.offsetWidth; - height = parent.offsetHeight; - } - - if (this.canvas.width == width && this.canvas.height == height) { - return; - } - - this.canvas.width = width; - this.canvas.height = height; - this.bgcanvas.width = this.canvas.width; - this.bgcanvas.height = this.canvas.height; - this.setDirty(true, true); - }; - - /** - * switches to live mode (node shapes are not rendered, only the content) - * this feature was designed when graphs where meant to create user interfaces - * @method switchLiveMode - **/ - LGraphCanvas.prototype.switchLiveMode = function(transition) { - if (!transition) { - this.live_mode = !this.live_mode; - this.dirty_canvas = true; - this.dirty_bgcanvas = true; - return; - } - - var self = this; - var delta = this.live_mode ? 1.1 : 0.9; - if (this.live_mode) { - this.live_mode = false; - this.editor_alpha = 0.1; - } - - var t = setInterval(function() { - self.editor_alpha *= delta; - self.dirty_canvas = true; - self.dirty_bgcanvas = true; - - if (delta < 1 && self.editor_alpha < 0.01) { - clearInterval(t); - if (delta < 1) { - self.live_mode = true; - } - } - if (delta > 1 && self.editor_alpha > 0.99) { - clearInterval(t); - self.editor_alpha = 1; - } - }, 1); - }; - - LGraphCanvas.prototype.onNodeSelectionChange = function(node) { - return; //disabled - }; - - LGraphCanvas.prototype.touchHandler = function(event) { - //alert("foo"); - var touches = event.changedTouches, - first = touches[0], - type = ""; - - switch (event.type) { - case "touchstart": - type = "mousedown"; - break; - case "touchmove": - type = "mousemove"; - break; - case "touchend": - type = "mouseup"; - break; - default: - return; - } - - //initMouseEvent(type, canBubble, cancelable, view, clickCount, - // screenX, screenY, clientX, clientY, ctrlKey, - // altKey, shiftKey, metaKey, button, relatedTarget); - - var window = this.getCanvasWindow(); - var document = window.document; - - var simulatedEvent = document.createEvent("MouseEvent"); - simulatedEvent.initMouseEvent( - type, - true, - true, - window, - 1, - first.screenX, - first.screenY, - first.clientX, - first.clientY, - false, - false, - false, - false, - 0 /*left*/, - null - ); - first.target.dispatchEvent(simulatedEvent); - event.preventDefault(); - }; - - /* CONTEXT MENU ********************/ - - LGraphCanvas.onGroupAdd = function(info, entry, mouse_event) { - var canvas = LGraphCanvas.active_canvas; - var ref_window = canvas.getCanvasWindow(); - - var group = new LiteGraph.LGraphGroup(); - group.pos = canvas.convertEventToCanvasOffset(mouse_event); - canvas.graph.add(group); - }; - - LGraphCanvas.onMenuAdd = function(node, options, e, prev_menu, callback) { - var canvas = LGraphCanvas.active_canvas; - var ref_window = canvas.getCanvasWindow(); - - var values = LiteGraph.getNodeTypesCategories( canvas.filter ); - var entries = []; - for (var i in values) { - if (values[i]) { - entries.push({ value: values[i], content: values[i], has_submenu: true }); - } - } - - //show categories - var menu = new LiteGraph.ContextMenu( entries, { event: e, callback: inner_clicked, parentMenu: prev_menu }, ref_window ); - - function inner_clicked(v, option, e) { - var category = v.value; - var node_types = LiteGraph.getNodeTypesInCategory( category, canvas.filter ); - var values = []; - for (var i in node_types) { - if (!node_types[i].skip_list) { - values.push({ - content: node_types[i].title, - value: node_types[i].type - }); - } - } - - new LiteGraph.ContextMenu( values, { event: e, callback: inner_create, parentMenu: menu }, ref_window ); - return false; - } - - function inner_create(v, e) { - var first_event = prev_menu.getFirstEvent(); - var node = LiteGraph.createNode(v.value); - if (node) { - node.pos = canvas.convertEventToCanvasOffset(first_event); - canvas.graph.add(node); - } - if(callback) - callback(node); - } - - return false; - }; - - LGraphCanvas.onMenuCollapseAll = function() {}; - - LGraphCanvas.onMenuNodeEdit = function() {}; - - LGraphCanvas.showMenuNodeOptionalInputs = function( - v, - options, - e, - prev_menu, - node - ) { - if (!node) { - return; - } - - var that = this; - var canvas = LGraphCanvas.active_canvas; - var ref_window = canvas.getCanvasWindow(); - - var options = node.optional_inputs; - if (node.onGetInputs) { - options = node.onGetInputs(); - } - - var entries = []; - if (options) { - for (var i in options) { - var entry = options[i]; - if (!entry) { - entries.push(null); - continue; - } - var label = entry[0]; - if (entry[2] && entry[2].label) { - label = entry[2].label; - } - var data = { content: label, value: entry }; - if (entry[1] == LiteGraph.ACTION) { - data.className = "event"; - } - entries.push(data); - } - } - - if (this.onMenuNodeInputs) { - entries = this.onMenuNodeInputs(entries); - } - - if (!entries.length) { - return; - } - - var menu = new LiteGraph.ContextMenu( - entries, - { - event: e, - callback: inner_clicked, - parentMenu: prev_menu, - node: node - }, - ref_window - ); - - function inner_clicked(v, e, prev) { - if (!node) { - return; - } - - if (v.callback) { - v.callback.call(that, node, v, e, prev); - } - - if (v.value) { - node.addInput(v.value[0], v.value[1], v.value[2]); - node.setDirtyCanvas(true, true); - } - } - - return false; - }; - - LGraphCanvas.showMenuNodeOptionalOutputs = function( - v, - options, - e, - prev_menu, - node - ) { - if (!node) { - return; - } - - var that = this; - var canvas = LGraphCanvas.active_canvas; - var ref_window = canvas.getCanvasWindow(); - - var options = node.optional_outputs; - if (node.onGetOutputs) { - options = node.onGetOutputs(); - } - - var entries = []; - if (options) { - for (var i in options) { - var entry = options[i]; - if (!entry) { - //separator? - entries.push(null); - continue; - } - - if ( - node.flags && - node.flags.skip_repeated_outputs && - node.findOutputSlot(entry[0]) != -1 - ) { - continue; - } //skip the ones already on - var label = entry[0]; - if (entry[2] && entry[2].label) { - label = entry[2].label; - } - var data = { content: label, value: entry }; - if (entry[1] == LiteGraph.EVENT) { - data.className = "event"; - } - entries.push(data); - } - } - - if (this.onMenuNodeOutputs) { - entries = this.onMenuNodeOutputs(entries); - } - - if (!entries.length) { - return; - } - - var menu = new LiteGraph.ContextMenu( - entries, - { - event: e, - callback: inner_clicked, - parentMenu: prev_menu, - node: node - }, - ref_window - ); - - function inner_clicked(v, e, prev) { - if (!node) { - return; - } - - if (v.callback) { - v.callback.call(that, node, v, e, prev); - } - - if (!v.value) { - return; - } - - var value = v.value[1]; - - if ( - value && - (value.constructor === Object || value.constructor === Array) - ) { - //submenu why? - var entries = []; - for (var i in value) { - entries.push({ content: i, value: value[i] }); - } - new LiteGraph.ContextMenu(entries, { - event: e, - callback: inner_clicked, - parentMenu: prev_menu, - node: node - }); - return false; - } else { - node.addOutput(v.value[0], v.value[1], v.value[2]); - node.setDirtyCanvas(true, true); - } - } - - return false; - }; - - LGraphCanvas.onShowMenuNodeProperties = function( - value, - options, - e, - prev_menu, - node - ) { - if (!node || !node.properties) { - return; - } - - var that = this; - var canvas = LGraphCanvas.active_canvas; - var ref_window = canvas.getCanvasWindow(); - - var entries = []; - for (var i in node.properties) { - var value = node.properties[i] !== undefined ? node.properties[i] : " "; - if( typeof value == "object" ) - value = JSON.stringify(value); - //value could contain invalid html characters, clean that - value = LGraphCanvas.decodeHTML(value); - entries.push({ - content: - "" + - i + - "" + - "" + - value + - "", - value: i - }); - } - if (!entries.length) { - return; - } - - var menu = new LiteGraph.ContextMenu( - entries, - { - event: e, - callback: inner_clicked, - parentMenu: prev_menu, - allow_html: true, - node: node - }, - ref_window - ); - - function inner_clicked(v, options, e, prev) { - if (!node) { - return; - } - var rect = this.getBoundingClientRect(); - canvas.showEditPropertyValue(node, v.value, { - position: [rect.left, rect.top] - }); - } - - return false; - }; - - LGraphCanvas.decodeHTML = function(str) { - var e = document.createElement("div"); - e.innerText = str; - return e.innerHTML; - }; - - LGraphCanvas.onResizeNode = function(value, options, e, menu, node) { - if (!node) { - return; - } - node.size = node.computeSize(); - if (node.onResize) - node.onResize(node.size); - node.setDirtyCanvas(true, true); - }; - - LGraphCanvas.prototype.showLinkMenu = function(link, e) { - var that = this; - console.log(link); - var options = ["Add Node",null,"Delete"]; - var menu = new LiteGraph.ContextMenu(options, { - event: e, - title: link.data != null ? link.data.constructor.name : null, - callback: inner_clicked - }); - - function inner_clicked(v,options,e) { - switch (v) { - case "Add Node": - LGraphCanvas.onMenuAdd(null, null, e, menu, function(node){ - console.log("node autoconnect"); - var node_left = that.graph.getNodeById( link.origin_id ); - var node_right = that.graph.getNodeById( link.target_id ); - if(!node.inputs || !node.inputs.length || !node.outputs || !node.outputs.length) - return; - if( node_left.outputs[ link.origin_slot ].type == node.inputs[0].type && node.outputs[0].type == node_right.inputs[0].type ) - { - node_left.connect( link.origin_slot, node, 0 ); - node.connect( 0, node_right, link.target_slot ); - node.pos[0] -= node.size[0] * 0.5; - } - }); - break; - case "Delete": - that.graph.removeLink(link.id); - break; - default: - } - } - - return false; - }; - - LGraphCanvas.onShowPropertyEditor = function(item, options, e, menu, node) { - var input_html = ""; - var property = item.property || "title"; - var value = node[property]; - - var dialog = document.createElement("div"); - dialog.className = "graphdialog"; - dialog.innerHTML = - ""; - var title = dialog.querySelector(".name"); - title.innerText = property; - var input = dialog.querySelector("input"); - if (input) { - input.value = value; - input.addEventListener("blur", function(e) { - this.focus(); - }); - input.addEventListener("keydown", function(e) { - if (e.keyCode != 13) { - return; - } - inner(); - e.preventDefault(); - e.stopPropagation(); - }); - } - - var graphcanvas = LGraphCanvas.active_canvas; - var canvas = graphcanvas.canvas; - - var rect = canvas.getBoundingClientRect(); - var offsetx = -20; - var offsety = -20; - if (rect) { - offsetx -= rect.left; - offsety -= rect.top; - } - - if (event) { - dialog.style.left = event.clientX + offsetx + "px"; - dialog.style.top = event.clientY + offsety + "px"; - } else { - dialog.style.left = canvas.width * 0.5 + offsetx + "px"; - dialog.style.top = canvas.height * 0.5 + offsety + "px"; - } - - var button = dialog.querySelector("button"); - button.addEventListener("click", inner); - canvas.parentNode.appendChild(dialog); - - function inner() { - setValue(input.value); - } - - function setValue(value) { - if (item.type == "Number") { - value = Number(value); - } else if (item.type == "Boolean") { - value = Boolean(value); - } - node[property] = value; - if (dialog.parentNode) { - dialog.parentNode.removeChild(dialog); - } - node.setDirtyCanvas(true, true); - } - }; - - LGraphCanvas.prototype.prompt = function(title, value, callback, event) { - var that = this; - var input_html = ""; - title = title || ""; - - var modified = false; - - var dialog = document.createElement("div"); - dialog.className = "graphdialog rounded"; - dialog.innerHTML = - " "; - dialog.close = function() { - that.prompt_box = null; - if (dialog.parentNode) { - dialog.parentNode.removeChild(dialog); - } - }; - - if (this.ds.scale > 1) { - dialog.style.transform = "scale(" + this.ds.scale + ")"; - } - - dialog.addEventListener("mouseleave", function(e) { - if (!modified) { - dialog.close(); - } - }); - - if (that.prompt_box) { - that.prompt_box.close(); - } - that.prompt_box = dialog; - - var first = null; - var timeout = null; - var selected = null; - - var name_element = dialog.querySelector(".name"); - name_element.innerText = title; - var value_element = dialog.querySelector(".value"); - value_element.value = value; - - var input = dialog.querySelector("input"); - input.addEventListener("keydown", function(e) { - modified = true; - if (e.keyCode == 27) { - //ESC - dialog.close(); - } else if (e.keyCode == 13) { - if (callback) { - callback(this.value); - } - dialog.close(); - } else { - return; - } - e.preventDefault(); - e.stopPropagation(); - }); - - var button = dialog.querySelector("button"); - button.addEventListener("click", function(e) { - if (callback) { - callback(input.value); - } - that.setDirty(true); - dialog.close(); - }); - - var graphcanvas = LGraphCanvas.active_canvas; - var canvas = graphcanvas.canvas; - - var rect = canvas.getBoundingClientRect(); - var offsetx = -20; - var offsety = -20; - if (rect) { - offsetx -= rect.left; - offsety -= rect.top; - } - - if (event) { - dialog.style.left = event.clientX + offsetx + "px"; - dialog.style.top = event.clientY + offsety + "px"; - } else { - dialog.style.left = canvas.width * 0.5 + offsetx + "px"; - dialog.style.top = canvas.height * 0.5 + offsety + "px"; - } - - canvas.parentNode.appendChild(dialog); - setTimeout(function() { - input.focus(); - }, 10); - - return dialog; - }; - - LGraphCanvas.search_limit = -1; - LGraphCanvas.prototype.showSearchBox = function(event) { - var that = this; - var input_html = ""; - var graphcanvas = LGraphCanvas.active_canvas; - var canvas = graphcanvas.canvas; - var root_document = canvas.ownerDocument || document; - - var dialog = document.createElement("div"); - dialog.className = "litegraph litesearchbox graphdialog rounded"; - dialog.innerHTML = - "Search
"; - dialog.close = function() { - that.search_box = null; - root_document.body.focus(); - root_document.body.style.overflow = ""; - - setTimeout(function() { - that.canvas.focus(); - }, 20); //important, if canvas loses focus keys wont be captured - if (dialog.parentNode) { - dialog.parentNode.removeChild(dialog); - } - }; - - var timeout_close = null; - - if (this.ds.scale > 1) { - dialog.style.transform = "scale(" + this.ds.scale + ")"; - } - - dialog.addEventListener("mouseenter", function(e) { - if (timeout_close) { - clearTimeout(timeout_close); - timeout_close = null; - } - }); - - dialog.addEventListener("mouseleave", function(e) { - //dialog.close(); - timeout_close = setTimeout(function() { - dialog.close(); - }, 500); - }); - - if (that.search_box) { - that.search_box.close(); - } - that.search_box = dialog; - - var helper = dialog.querySelector(".helper"); - - var first = null; - var timeout = null; - var selected = null; - - var input = dialog.querySelector("input"); - if (input) { - input.addEventListener("blur", function(e) { - this.focus(); - }); - input.addEventListener("keydown", function(e) { - if (e.keyCode == 38) { - //UP - changeSelection(false); - } else if (e.keyCode == 40) { - //DOWN - changeSelection(true); - } else if (e.keyCode == 27) { - //ESC - dialog.close(); - } else if (e.keyCode == 13) { - if (selected) { - select(selected.innerHTML); - } else if (first) { - select(first); - } else { - dialog.close(); - } - } else { - if (timeout) { - clearInterval(timeout); - } - timeout = setTimeout(refreshHelper, 10); - return; - } - e.preventDefault(); - e.stopPropagation(); - e.stopImmediatePropagation(); - return true; - }); - } - - if( root_document.fullscreenElement ) - root_document.fullscreenElement.appendChild(dialog); - else - { - root_document.body.appendChild(dialog); - root_document.body.style.overflow = "hidden"; - } - - //compute best position - var rect = canvas.getBoundingClientRect(); - - var left = ( event ? event.clientX : (rect.left + rect.width * 0.5) ) - 80; - var top = ( event ? event.clientY : (rect.top + rect.height * 0.5) ) - 20; - dialog.style.left = left + "px"; - dialog.style.top = top + "px"; - - //To avoid out of screen problems - if(event.layerY > (rect.height - 200)) - helper.style.maxHeight = (rect.height - event.layerY - 20) + "px"; - - /* - var offsetx = -20; - var offsety = -20; - if (rect) { - offsetx -= rect.left; - offsety -= rect.top; - } - - if (event) { - dialog.style.left = event.clientX + offsetx + "px"; - dialog.style.top = event.clientY + offsety + "px"; - } else { - dialog.style.left = canvas.width * 0.5 + offsetx + "px"; - dialog.style.top = canvas.height * 0.5 + offsety + "px"; - } - canvas.parentNode.appendChild(dialog); - */ - - input.focus(); - - function select(name) { - if (name) { - if (that.onSearchBoxSelection) { - that.onSearchBoxSelection(name, event, graphcanvas); - } else { - var extra = LiteGraph.searchbox_extras[name.toLowerCase()]; - if (extra) { - name = extra.type; - } - - var node = LiteGraph.createNode(name); - if (node) { - node.pos = graphcanvas.convertEventToCanvasOffset( - event - ); - graphcanvas.graph.add(node); - } - - if (extra && extra.data) { - if (extra.data.properties) { - for (var i in extra.data.properties) { - node.addProperty( i, extra.data.properties[i] ); - } - } - if (extra.data.inputs) { - node.inputs = []; - for (var i in extra.data.inputs) { - node.addOutput( - extra.data.inputs[i][0], - extra.data.inputs[i][1] - ); - } - } - if (extra.data.outputs) { - node.outputs = []; - for (var i in extra.data.outputs) { - node.addOutput( - extra.data.outputs[i][0], - extra.data.outputs[i][1] - ); - } - } - if (extra.data.title) { - node.title = extra.data.title; - } - if (extra.data.json) { - node.configure(extra.data.json); - } - } - } - } - - dialog.close(); - } - - function changeSelection(forward) { - var prev = selected; - if (selected) { - selected.classList.remove("selected"); - } - if (!selected) { - selected = forward - ? helper.childNodes[0] - : helper.childNodes[helper.childNodes.length]; - } else { - selected = forward - ? selected.nextSibling - : selected.previousSibling; - if (!selected) { - selected = prev; - } - } - if (!selected) { - return; - } - selected.classList.add("selected"); - selected.scrollIntoView({block: "end", behavior: "smooth"}); - } - - function refreshHelper() { - timeout = null; - var str = input.value; - first = null; - helper.innerHTML = ""; - if (!str) { - return; - } - - if (that.onSearchBox) { - var list = that.onSearchBox(helper, str, graphcanvas); - if (list) { - for (var i = 0; i < list.length; ++i) { - addResult(list[i]); - } - } - } else { - var c = 0; - str = str.toLowerCase(); - var filter = graphcanvas.filter || graphcanvas.graph.filter; - - //extras - for (var i in LiteGraph.searchbox_extras) { - var extra = LiteGraph.searchbox_extras[i]; - if (extra.desc.toLowerCase().indexOf(str) === -1) { - continue; - } - var ctor = LiteGraph.registered_node_types[ extra.type ]; - if( ctor && ctor.filter && ctor.filter != filter ) - continue; - addResult( extra.desc, "searchbox_extra" ); - if ( LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit ) { - break; - } - } - - var filtered = null; - if (Array.prototype.filter) { //filter supported - var keys = Object.keys( LiteGraph.registered_node_types ); //types - var filtered = keys.filter( inner_test_filter ); - } else { - filtered = []; - for (var i in LiteGraph.registered_node_types) { - if( inner_test_filter(i) ) - filtered.push(i); - } - } - - for (var i = 0; i < filtered.length; i++) { - addResult(filtered[i]); - if ( LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit ) { - break; - } - } - - function inner_test_filter( type ) - { - var ctor = LiteGraph.registered_node_types[ type ]; - if(filter && ctor.filter != filter ) - return false; - return type.toLowerCase().indexOf(str) !== -1; - } - } - - function addResult(type, className) { - var help = document.createElement("div"); - if (!first) { - first = type; - } - help.innerText = type; - help.dataset["type"] = escape(type); - help.className = "litegraph lite-search-item"; - if (className) { - help.className += " " + className; - } - help.addEventListener("click", function(e) { - select(unescape(this.dataset["type"])); - }); - helper.appendChild(help); - } - } - - return dialog; - }; - - LGraphCanvas.prototype.showEditPropertyValue = function( node, property, options ) { - if (!node || node.properties[property] === undefined) { - return; - } - - options = options || {}; - var that = this; - - var info = node.getPropertyInfo(property); - var type = info.type; - - var input_html = ""; - - if (type == "string" || type == "number" || type == "array" || type == "object") { - input_html = ""; - } else if (type == "enum" && info.values) { - input_html = ""; - } else if (type == "boolean") { - input_html = - ""; - } else { - console.warn("unknown type: " + type); - return; - } - - var dialog = this.createDialog( - "" + - property + - "" + - input_html + - "", - options - ); - - if (type == "enum" && info.values) { - var input = dialog.querySelector("select"); - input.addEventListener("change", function(e) { - setValue(e.target.value); - //var index = e.target.value; - //setValue( e.options[e.selectedIndex].value ); - }); - } else if (type == "boolean") { - var input = dialog.querySelector("input"); - if (input) { - input.addEventListener("click", function(e) { - setValue(!!input.checked); - }); - } - } else { - var input = dialog.querySelector("input"); - if (input) { - input.addEventListener("blur", function(e) { - this.focus(); - }); - var v = node.properties[property] !== undefined ? node.properties[property] : ""; - v = JSON.stringify(v); - input.value = v; - input.addEventListener("keydown", function(e) { - if (e.keyCode != 13) { - return; - } - inner(); - e.preventDefault(); - e.stopPropagation(); - }); - } - } - - var button = dialog.querySelector("button"); - button.addEventListener("click", inner); - - function inner() { - setValue(input.value); - } - - function setValue(value) { - if (typeof node.properties[property] == "number") { - value = Number(value); - } - if (type == "array" || type == "object") { - value = JSON.parse(value); - } - node.properties[property] = value; - if (node._graph) { - node._graph._version++; - } - if (node.onPropertyChanged) { - node.onPropertyChanged(property, value); - } - if(options.onclose) - options.onclose(); - dialog.close(); - node.setDirtyCanvas(true, true); - } - - return dialog; - }; - - LGraphCanvas.prototype.createDialog = function(html, options) { - options = options || {}; - - var dialog = document.createElement("div"); - dialog.className = "graphdialog"; - dialog.innerHTML = html; - - var rect = this.canvas.getBoundingClientRect(); - var offsetx = -20; - var offsety = -20; - if (rect) { - offsetx -= rect.left; - offsety -= rect.top; - } - - if (options.position) { - offsetx += options.position[0]; - offsety += options.position[1]; - } else if (options.event) { - offsetx += options.event.clientX; - offsety += options.event.clientY; - } //centered - else { - offsetx += this.canvas.width * 0.5; - offsety += this.canvas.height * 0.5; - } - - dialog.style.left = offsetx + "px"; - dialog.style.top = offsety + "px"; - - this.canvas.parentNode.appendChild(dialog); - - dialog.close = function() { - if (this.parentNode) { - this.parentNode.removeChild(this); - } - }; - - return dialog; - }; - - LGraphCanvas.onMenuNodeCollapse = function(value, options, e, menu, node) { - node.collapse(); - }; - - LGraphCanvas.onMenuNodePin = function(value, options, e, menu, node) { - node.pin(); - }; - - LGraphCanvas.onMenuNodeMode = function(value, options, e, menu, node) { - new LiteGraph.ContextMenu( - ["Always", "On Event", "On Trigger", "Never"], - { event: e, callback: inner_clicked, parentMenu: menu, node: node } - ); - - function inner_clicked(v) { - if (!node) { - return; - } - switch (v) { - case "On Event": - node.mode = LiteGraph.ON_EVENT; - break; - case "On Trigger": - node.mode = LiteGraph.ON_TRIGGER; - break; - case "Never": - node.mode = LiteGraph.NEVER; - break; - case "Always": - default: - node.mode = LiteGraph.ALWAYS; - break; - } - } - - return false; - }; - - LGraphCanvas.onMenuNodeColors = function(value, options, e, menu, node) { - if (!node) { - throw "no node for color"; - } - - var values = []; - values.push({ - value: null, - content: - "No color" - }); - - for (var i in LGraphCanvas.node_colors) { - var color = LGraphCanvas.node_colors[i]; - var value = { - value: i, - content: - "" + - i + - "" - }; - values.push(value); - } - new LiteGraph.ContextMenu(values, { - event: e, - callback: inner_clicked, - parentMenu: menu, - node: node - }); - - function inner_clicked(v) { - if (!node) { - return; - } - - var color = v.value ? LGraphCanvas.node_colors[v.value] : null; - if (color) { - if (node.constructor === LiteGraph.LGraphGroup) { - node.color = color.groupcolor; - } else { - node.color = color.color; - node.bgcolor = color.bgcolor; - } - } else { - delete node.color; - delete node.bgcolor; - } - node.setDirtyCanvas(true, true); - } - - return false; - }; - - LGraphCanvas.onMenuNodeShapes = function(value, options, e, menu, node) { - if (!node) { - throw "no node passed"; - } - - new LiteGraph.ContextMenu(LiteGraph.VALID_SHAPES, { - event: e, - callback: inner_clicked, - parentMenu: menu, - node: node - }); - - function inner_clicked(v) { - if (!node) { - return; - } - node.shape = v; - node.setDirtyCanvas(true); - } - - return false; - }; - - LGraphCanvas.onMenuNodeRemove = function(value, options, e, menu, node) { - if (!node) { - throw "no node passed"; - } - - if (node.removable === false) { - return; - } - - node.graph.remove(node); - node.setDirtyCanvas(true, true); - }; - - LGraphCanvas.onMenuNodeToSubgraph = function(value, options, e, menu, node) { - var graph = node.graph; - var graphcanvas = LGraphCanvas.active_canvas; - if(!graphcanvas) //?? - return; - - var nodes_list = Object.values( graphcanvas.selected_nodes || {} ); - if( !nodes_list.length ) - nodes_list = [ node ]; - - var subgraph_node = LiteGraph.createNode("graph/subgraph"); - subgraph_node.pos = node.pos.concat(); - graph.add(subgraph_node); - - subgraph_node.buildFromNodes( nodes_list ); - - graphcanvas.deselectAllNodes(); - node.setDirtyCanvas(true, true); - }; - - LGraphCanvas.onMenuNodeClone = function(value, options, e, menu, node) { - if (node.clonable == false) { - return; - } - var newnode = node.clone(); - if (!newnode) { - return; - } - newnode.pos = [node.pos[0] + 5, node.pos[1] + 5]; - node.graph.add(newnode); - node.setDirtyCanvas(true, true); - }; - - LGraphCanvas.node_colors = { - red: { color: "#322", bgcolor: "#533", groupcolor: "#A88" }, - brown: { color: "#332922", bgcolor: "#593930", groupcolor: "#b06634" }, - green: { color: "#232", bgcolor: "#353", groupcolor: "#8A8" }, - blue: { color: "#223", bgcolor: "#335", groupcolor: "#88A" }, - pale_blue: { - color: "#2a363b", - bgcolor: "#3f5159", - groupcolor: "#3f789e" - }, - cyan: { color: "#233", bgcolor: "#355", groupcolor: "#8AA" }, - purple: { color: "#323", bgcolor: "#535", groupcolor: "#a1309b" }, - yellow: { color: "#432", bgcolor: "#653", groupcolor: "#b58b2a" }, - black: { color: "#222", bgcolor: "#000", groupcolor: "#444" } - }; - - LGraphCanvas.prototype.getCanvasMenuOptions = function() { - var options = null; - if (this.getMenuOptions) { - options = this.getMenuOptions(); - } else { - options = [ - { - content: "Add Node", - has_submenu: true, - callback: LGraphCanvas.onMenuAdd - }, - { content: "Add Group", callback: LGraphCanvas.onGroupAdd } - //{content:"Collapse All", callback: LGraphCanvas.onMenuCollapseAll } - ]; - - if (this._graph_stack && this._graph_stack.length > 0) { - options.push(null, { - content: "Close subgraph", - callback: this.closeSubgraph.bind(this) - }); - } - } - - if (this.getExtraMenuOptions) { - var extra = this.getExtraMenuOptions(this, options); - if (extra) { - options = options.concat(extra); - } - } - - return options; - }; - - //called by processContextMenu to extract the menu list - LGraphCanvas.prototype.getNodeMenuOptions = function(node) { - var options = null; - - if (node.getMenuOptions) { - options = node.getMenuOptions(this); - } else { - options = [ - { - content: "Inputs", - has_submenu: true, - disabled: true, - callback: LGraphCanvas.showMenuNodeOptionalInputs - }, - { - content: "Outputs", - has_submenu: true, - disabled: true, - callback: LGraphCanvas.showMenuNodeOptionalOutputs - }, - null, - { - content: "Properties", - has_submenu: true, - callback: LGraphCanvas.onShowMenuNodeProperties - }, - null, - { - content: "Title", - callback: LGraphCanvas.onShowPropertyEditor - }, - { - content: "Mode", - has_submenu: true, - callback: LGraphCanvas.onMenuNodeMode - }, - { content: "Resize", callback: LGraphCanvas.onResizeNode }, - { - content: "Collapse", - callback: LGraphCanvas.onMenuNodeCollapse - }, - { content: "Pin", callback: LGraphCanvas.onMenuNodePin }, - { - content: "Colors", - has_submenu: true, - callback: LGraphCanvas.onMenuNodeColors - }, - { - content: "Shapes", - has_submenu: true, - callback: LGraphCanvas.onMenuNodeShapes - }, - null - ]; - } - - if (node.onGetInputs) { - var inputs = node.onGetInputs(); - if (inputs && inputs.length) { - options[0].disabled = false; - } - } - - if (node.onGetOutputs) { - var outputs = node.onGetOutputs(); - if (outputs && outputs.length) { - options[1].disabled = false; - } - } - - if (node.getExtraMenuOptions) { - var extra = node.getExtraMenuOptions(this); - if (extra) { - extra.push(null); - options = extra.concat(options); - } - } - - if (node.clonable !== false) { - options.push({ - content: "Clone", - callback: LGraphCanvas.onMenuNodeClone - }); - } - - if(0) //TODO - options.push({ - content: "To Subgraph", - callback: LGraphCanvas.onMenuNodeToSubgraph - }); - - if (node.removable !== false) { - options.push(null, { - content: "Remove", - callback: LGraphCanvas.onMenuNodeRemove - }); - } - - if (node.graph && node.graph.onGetNodeMenuOptions) { - node.graph.onGetNodeMenuOptions(options, node); - } - - return options; - }; - - LGraphCanvas.prototype.getGroupMenuOptions = function(node) { - var o = [ - { content: "Title", callback: LGraphCanvas.onShowPropertyEditor }, - { - content: "Color", - has_submenu: true, - callback: LGraphCanvas.onMenuNodeColors - }, - { - content: "Font size", - property: "font_size", - type: "Number", - callback: LGraphCanvas.onShowPropertyEditor - }, - null, - { content: "Remove", callback: LGraphCanvas.onMenuNodeRemove } - ]; - - return o; - }; - - LGraphCanvas.prototype.processContextMenu = function(node, event) { - var that = this; - var canvas = LGraphCanvas.active_canvas; - var ref_window = canvas.getCanvasWindow(); - - var menu_info = null; - var options = { - event: event, - callback: inner_option_clicked, - extra: node - }; - - if(node) - options.title = node.type; - - //check if mouse is in input - var slot = null; - if (node) { - slot = node.getSlotInPosition(event.canvasX, event.canvasY); - LGraphCanvas.active_node = node; - } - - if (slot) { - //on slot - menu_info = []; - if (node.getSlotMenuOptions) { - menu_info = node.getSlotMenuOptions(slot); - } else { - if ( - slot && - slot.output && - slot.output.links && - slot.output.links.length - ) { - menu_info.push({ content: "Disconnect Links", slot: slot }); - } - var _slot = slot.input || slot.output; - menu_info.push( - _slot.locked - ? "Cannot remove" - : { content: "Remove Slot", slot: slot } - ); - menu_info.push( - _slot.nameLocked - ? "Cannot rename" - : { content: "Rename Slot", slot: slot } - ); - - } - options.title = - (slot.input ? slot.input.type : slot.output.type) || "*"; - if (slot.input && slot.input.type == LiteGraph.ACTION) { - options.title = "Action"; - } - if (slot.output && slot.output.type == LiteGraph.EVENT) { - options.title = "Event"; - } - } else { - if (node) { - //on node - menu_info = this.getNodeMenuOptions(node); - } else { - menu_info = this.getCanvasMenuOptions(); - var group = this.graph.getGroupOnPos( - event.canvasX, - event.canvasY - ); - if (group) { - //on group - menu_info.push(null, { - content: "Edit Group", - has_submenu: true, - submenu: { - title: "Group", - extra: group, - options: this.getGroupMenuOptions(group) - } - }); - } - } - } - - //show menu - if (!menu_info) { - return; - } - - var menu = new LiteGraph.ContextMenu(menu_info, options, ref_window); - - function inner_option_clicked(v, options, e) { - if (!v) { - return; - } - - if (v.content == "Remove Slot") { - var info = v.slot; - if (info.input) { - node.removeInput(info.slot); - } else if (info.output) { - node.removeOutput(info.slot); - } - return; - } else if (v.content == "Disconnect Links") { - var info = v.slot; - if (info.output) { - node.disconnectOutput(info.slot); - } else if (info.input) { - node.disconnectInput(info.slot); - } - return; - } else if (v.content == "Rename Slot") { - var info = v.slot; - var slot_info = info.input - ? node.getInputInfo(info.slot) - : node.getOutputInfo(info.slot); - var dialog = that.createDialog( - "Name", - options - ); - var input = dialog.querySelector("input"); - if (input && slot_info) { - input.value = slot_info.label || ""; - } - dialog - .querySelector("button") - .addEventListener("click", function(e) { - if (input.value) { - if (slot_info) { - slot_info.label = input.value; - } - that.setDirty(true); - } - dialog.close(); - }); - } - - //if(v.callback) - // return v.callback.call(that, node, options, e, menu, that, event ); - } - }; - - //API ************************************************* - //like rect but rounded corners - if (typeof(window) != "undefined" && window.CanvasRenderingContext2D) { - window.CanvasRenderingContext2D.prototype.roundRect = function( - x, - y, - width, - height, - radius, - radius_low - ) { - if (radius === undefined) { - radius = 5; - } - - if (radius_low === undefined) { - radius_low = radius; - } - - this.moveTo(x + radius, y); - this.lineTo(x + width - radius, y); - this.quadraticCurveTo(x + width, y, x + width, y + radius); - - this.lineTo(x + width, y + height - radius_low); - this.quadraticCurveTo( - x + width, - y + height, - x + width - radius_low, - y + height - ); - this.lineTo(x + radius_low, y + height); - this.quadraticCurveTo(x, y + height, x, y + height - radius_low); - this.lineTo(x, y + radius); - this.quadraticCurveTo(x, y, x + radius, y); - }; - } - - function compareObjects(a, b) { - for (var i in a) { - if (a[i] != b[i]) { - return false; - } - } - return true; - } - LiteGraph.compareObjects = compareObjects; - - function distance(a, b) { - return Math.sqrt( - (b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1]) - ); - } - LiteGraph.distance = distance; - - function colorToString(c) { - return ( - "rgba(" + - Math.round(c[0] * 255).toFixed() + - "," + - Math.round(c[1] * 255).toFixed() + - "," + - Math.round(c[2] * 255).toFixed() + - "," + - (c.length == 4 ? c[3].toFixed(2) : "1.0") + - ")" - ); - } - LiteGraph.colorToString = colorToString; - - function isInsideRectangle(x, y, left, top, width, height) { - if (left < x && left + width > x && top < y && top + height > y) { - return true; - } - return false; - } - LiteGraph.isInsideRectangle = isInsideRectangle; - - //[minx,miny,maxx,maxy] - function growBounding(bounding, x, y) { - if (x < bounding[0]) { - bounding[0] = x; - } else if (x > bounding[2]) { - bounding[2] = x; - } - - if (y < bounding[1]) { - bounding[1] = y; - } else if (y > bounding[3]) { - bounding[3] = y; - } - } - LiteGraph.growBounding = growBounding; - - //point inside bounding box - function isInsideBounding(p, bb) { - if ( - p[0] < bb[0][0] || - p[1] < bb[0][1] || - p[0] > bb[1][0] || - p[1] > bb[1][1] - ) { - return false; - } - return true; - } - LiteGraph.isInsideBounding = isInsideBounding; - - //bounding overlap, format: [ startx, starty, width, height ] - function overlapBounding(a, b) { - var A_end_x = a[0] + a[2]; - var A_end_y = a[1] + a[3]; - var B_end_x = b[0] + b[2]; - var B_end_y = b[1] + b[3]; - - if ( - a[0] > B_end_x || - a[1] > B_end_y || - A_end_x < b[0] || - A_end_y < b[1] - ) { - return false; - } - return true; - } - LiteGraph.overlapBounding = overlapBounding; - - //Convert a hex value to its decimal value - the inputted hex must be in the - // format of a hex triplet - the kind we use for HTML colours. The function - // will return an array with three values. - function hex2num(hex) { - if (hex.charAt(0) == "#") { - hex = hex.slice(1); - } //Remove the '#' char - if there is one. - hex = hex.toUpperCase(); - var hex_alphabets = "0123456789ABCDEF"; - var value = new Array(3); - var k = 0; - var int1, int2; - for (var i = 0; i < 6; i += 2) { - int1 = hex_alphabets.indexOf(hex.charAt(i)); - int2 = hex_alphabets.indexOf(hex.charAt(i + 1)); - value[k] = int1 * 16 + int2; - k++; - } - return value; - } - - LiteGraph.hex2num = hex2num; - - //Give a array with three values as the argument and the function will return - // the corresponding hex triplet. - function num2hex(triplet) { - var hex_alphabets = "0123456789ABCDEF"; - var hex = "#"; - var int1, int2; - for (var i = 0; i < 3; i++) { - int1 = triplet[i] / 16; - int2 = triplet[i] % 16; - - hex += hex_alphabets.charAt(int1) + hex_alphabets.charAt(int2); - } - return hex; - } - - LiteGraph.num2hex = num2hex; - - /* LiteGraph GUI elements used for canvas editing *************************************/ - - /** - * ContextMenu from LiteGUI - * - * @class ContextMenu - * @constructor - * @param {Array} values (allows object { title: "Nice text", callback: function ... }) - * @param {Object} options [optional] Some options:\ - * - title: title to show on top of the menu - * - callback: function to call when an option is clicked, it receives the item information - * - ignore_item_callbacks: ignores the callback inside the item, it just calls the options.callback - * - event: you can pass a MouseEvent, this way the ContextMenu appears in that position - */ - function ContextMenu(values, options) { - options = options || {}; - this.options = options; - var that = this; - - //to link a menu with its parent - if (options.parentMenu) { - if (options.parentMenu.constructor !== this.constructor) { - console.error( - "parentMenu must be of class ContextMenu, ignoring it" - ); - options.parentMenu = null; - } else { - this.parentMenu = options.parentMenu; - this.parentMenu.lock = true; - this.parentMenu.current_submenu = this; - } - } - - var eventClass = null; - if(options.event) //use strings because comparing classes between windows doesnt work - eventClass = options.event.constructor.name; - if ( eventClass !== "MouseEvent" && - eventClass !== "CustomEvent" && - eventClass !== "PointerEvent" - ) { - console.error( - "Event passed to ContextMenu is not of type MouseEvent or CustomEvent. Ignoring it." - ); - options.event = null; - } - - var root = document.createElement("div"); - root.className = "litegraph litecontextmenu litemenubar-panel"; - if (options.className) { - root.className += " " + options.className; - } - root.style.minWidth = 100; - root.style.minHeight = 100; - root.style.pointerEvents = "none"; - setTimeout(function() { - root.style.pointerEvents = "auto"; - }, 100); //delay so the mouse up event is not caught by this element - - //this prevents the default context browser menu to open in case this menu was created when pressing right button - root.addEventListener( - "mouseup", - function(e) { - e.preventDefault(); - return true; - }, - true - ); - root.addEventListener( - "contextmenu", - function(e) { - if (e.button != 2) { - //right button - return false; - } - e.preventDefault(); - return false; - }, - true - ); - - root.addEventListener( - "mousedown", - function(e) { - if (e.button == 2) { - that.close(); - e.preventDefault(); - return true; - } - }, - true - ); - - function on_mouse_wheel(e) { - var pos = parseInt(root.style.top); - root.style.top = - (pos + e.deltaY * options.scroll_speed).toFixed() + "px"; - e.preventDefault(); - return true; - } - - if (!options.scroll_speed) { - options.scroll_speed = 0.1; - } - - root.addEventListener("wheel", on_mouse_wheel, true); - root.addEventListener("mousewheel", on_mouse_wheel, true); - - this.root = root; - - //title - if (options.title) { - var element = document.createElement("div"); - element.className = "litemenu-title"; - element.innerHTML = options.title; - root.appendChild(element); - } - - //entries - var num = 0; - for (var i in values) { - var name = values.constructor == Array ? values[i] : i; - if (name != null && name.constructor !== String) { - name = name.content === undefined ? String(name) : name.content; - } - var value = values[i]; - this.addItem(name, value, options); - num++; - } - - //close on leave - root.addEventListener("mouseleave", function(e) { - if (that.lock) { - return; - } - if (root.closing_timer) { - clearTimeout(root.closing_timer); - } - root.closing_timer = setTimeout(that.close.bind(that, e), 500); - //that.close(e); - }); - - root.addEventListener("mouseenter", function(e) { - if (root.closing_timer) { - clearTimeout(root.closing_timer); - } - }); - - //insert before checking position - var root_document = document; - if (options.event) { - root_document = options.event.target.ownerDocument; - } - - if (!root_document) { - root_document = document; - } - - if( root_document.fullscreenElement ) - root_document.fullscreenElement.appendChild(root); - else - root_document.body.appendChild(root); - - //compute best position - var left = options.left || 0; - var top = options.top || 0; - if (options.event) { - left = options.event.clientX - 10; - top = options.event.clientY - 10; - if (options.title) { - top -= 20; - } - - if (options.parentMenu) { - var rect = options.parentMenu.root.getBoundingClientRect(); - left = rect.left + rect.width; - } - - var body_rect = document.body.getBoundingClientRect(); - var root_rect = root.getBoundingClientRect(); - if(body_rect.height == 0) - console.error("document.body height is 0. That is dangerous, set html,body { height: 100%; }"); - - if (body_rect.width && left > body_rect.width - root_rect.width - 10) { - left = body_rect.width - root_rect.width - 10; - } - if (body_rect.height && top > body_rect.height - root_rect.height - 10) { - top = body_rect.height - root_rect.height - 10; - } - } - - root.style.left = left + "px"; - root.style.top = top + "px"; - - if (options.scale) { - root.style.transform = "scale(" + options.scale + ")"; - } - } - - ContextMenu.prototype.addItem = function(name, value, options) { - var that = this; - options = options || {}; - - var element = document.createElement("div"); - element.className = "litemenu-entry submenu"; - - var disabled = false; - - if (value === null) { - element.classList.add("separator"); - //element.innerHTML = "
" - //continue; - } else { - element.innerHTML = value && value.title ? value.title : name; - element.value = value; - - if (value) { - if (value.disabled) { - disabled = true; - element.classList.add("disabled"); - } - if (value.submenu || value.has_submenu) { - element.classList.add("has_submenu"); - } - } - - if (typeof value == "function") { - element.dataset["value"] = name; - element.onclick_callback = value; - } else { - element.dataset["value"] = value; - } - - if (value.className) { - element.className += " " + value.className; - } - } - - this.root.appendChild(element); - if (!disabled) { - element.addEventListener("click", inner_onclick); - } - if (options.autoopen) { - element.addEventListener("mouseenter", inner_over); - } - - function inner_over(e) { - var value = this.value; - if (!value || !value.has_submenu) { - return; - } - //if it is a submenu, autoopen like the item was clicked - inner_onclick.call(this, e); - } - - //menu option clicked - function inner_onclick(e) { - var value = this.value; - var close_parent = true; - - if (that.current_submenu) { - that.current_submenu.close(e); - } - - //global callback - if (options.callback) { - var r = options.callback.call( - this, - value, - options, - e, - that, - options.node - ); - if (r === true) { - close_parent = false; - } - } - - //special cases - if (value) { - if ( - value.callback && - !options.ignore_item_callbacks && - value.disabled !== true - ) { - //item callback - var r = value.callback.call( - this, - value, - options, - e, - that, - options.extra - ); - if (r === true) { - close_parent = false; - } - } - if (value.submenu) { - if (!value.submenu.options) { - throw "ContextMenu submenu needs options"; - } - var submenu = new that.constructor(value.submenu.options, { - callback: value.submenu.callback, - event: e, - parentMenu: that, - ignore_item_callbacks: - value.submenu.ignore_item_callbacks, - title: value.submenu.title, - extra: value.submenu.extra, - autoopen: options.autoopen - }); - close_parent = false; - } - } - - if (close_parent && !that.lock) { - that.close(); - } - } - - return element; - }; - - ContextMenu.prototype.close = function(e, ignore_parent_menu) { - if (this.root.parentNode) { - this.root.parentNode.removeChild(this.root); - } - if (this.parentMenu && !ignore_parent_menu) { - this.parentMenu.lock = false; - this.parentMenu.current_submenu = null; - if (e === undefined) { - this.parentMenu.close(); - } else if ( - e && - !ContextMenu.isCursorOverElement(e, this.parentMenu.root) - ) { - ContextMenu.trigger(this.parentMenu.root, "mouseleave", e); - } - } - if (this.current_submenu) { - this.current_submenu.close(e, true); - } - - if (this.root.closing_timer) { - clearTimeout(this.root.closing_timer); - } - }; - - //this code is used to trigger events easily (used in the context menu mouseleave - ContextMenu.trigger = function(element, event_name, params, origin) { - var evt = document.createEvent("CustomEvent"); - evt.initCustomEvent(event_name, true, true, params); //canBubble, cancelable, detail - evt.srcElement = origin; - if (element.dispatchEvent) { - element.dispatchEvent(evt); - } else if (element.__events) { - element.__events.dispatchEvent(evt); - } - //else nothing seems binded here so nothing to do - return evt; - }; - - //returns the top most menu - ContextMenu.prototype.getTopMenu = function() { - if (this.options.parentMenu) { - return this.options.parentMenu.getTopMenu(); - } - return this; - }; - - ContextMenu.prototype.getFirstEvent = function() { - if (this.options.parentMenu) { - return this.options.parentMenu.getFirstEvent(); - } - return this.options.event; - }; - - ContextMenu.isCursorOverElement = function(event, element) { - var left = event.clientX; - var top = event.clientY; - var rect = element.getBoundingClientRect(); - if (!rect) { - return false; - } - if ( - top > rect.top && - top < rect.top + rect.height && - left > rect.left && - left < rect.left + rect.width - ) { - return true; - } - return false; - }; - - LiteGraph.ContextMenu = ContextMenu; - - LiteGraph.closeAllContextMenus = function(ref_window) { - ref_window = ref_window || window; - - var elements = ref_window.document.querySelectorAll(".litecontextmenu"); - if (!elements.length) { - return; - } - - var result = []; - for (var i = 0; i < elements.length; i++) { - result.push(elements[i]); - } - - for (var i in result) { - if (result[i].close) { - result[i].close(); - } else if (result[i].parentNode) { - result[i].parentNode.removeChild(result[i]); - } - } - }; - - LiteGraph.extendClass = function(target, origin) { - for (var i in origin) { - //copy class properties - if (target.hasOwnProperty(i)) { - continue; - } - target[i] = origin[i]; - } - - if (origin.prototype) { - //copy prototype properties - for (var i in origin.prototype) { - //only enumerable - if (!origin.prototype.hasOwnProperty(i)) { - continue; - } - - if (target.prototype.hasOwnProperty(i)) { - //avoid overwriting existing ones - continue; - } - - //copy getters - if (origin.prototype.__lookupGetter__(i)) { - target.prototype.__defineGetter__( - i, - origin.prototype.__lookupGetter__(i) - ); - } else { - target.prototype[i] = origin.prototype[i]; - } - - //and setters - if (origin.prototype.__lookupSetter__(i)) { - target.prototype.__defineSetter__( - i, - origin.prototype.__lookupSetter__(i) - ); - } - } - } - }; - - //used by some widgets to render a curve editor - function CurveEditor( points ) - { - this.points = points; - this.selected = -1; - this.nearest = -1; - this.size = null; //stores last size used - this.must_update = true; - this.margin = 5; - } - - CurveEditor.sampleCurve = function(f,points) - { - if(!points) - return; - for(var i = 0; i < points.length - 1; ++i) - { - var p = points[i]; - var pn = points[i+1]; - if(pn[0] < f) - continue; - var r = (pn[0] - p[0]); - if( Math.abs(r) < 0.00001 ) - return p[1]; - var local_f = (f - p[0]) / r; - return p[1] * (1.0 - local_f) + pn[1] * local_f; - } - return 0; - } - - CurveEditor.prototype.draw = function( ctx, size, graphcanvas, background_color, line_color, inactive ) - { - var points = this.points; - if(!points) - return; - this.size = size; - var w = size[0] - this.margin * 2; - var h = size[1] - this.margin * 2; - - line_color = line_color || "#666"; - - ctx.save(); - ctx.translate(this.margin,this.margin); - - if(background_color) - { - ctx.fillStyle = "#111"; - ctx.fillRect(0,0,w,h); - ctx.fillStyle = "#222"; - ctx.fillRect(w*0.5,0,1,h); - ctx.strokeStyle = "#333"; - ctx.strokeRect(0,0,w,h); - } - ctx.strokeStyle = line_color; - if(inactive) - ctx.globalAlpha = 0.5; - ctx.beginPath(); - for(var i = 0; i < points.length; ++i) - { - var p = points[i]; - ctx.lineTo( p[0] * w, (1.0 - p[1]) * h ); - } - ctx.stroke(); - ctx.globalAlpha = 1; - if(!inactive) - for(var i = 0; i < points.length; ++i) - { - var p = points[i]; - ctx.fillStyle = this.selected == i ? "#FFF" : (this.nearest == i ? "#DDD" : "#AAA"); - ctx.beginPath(); - ctx.arc( p[0] * w, (1.0 - p[1]) * h, 2, 0, Math.PI * 2 ); - ctx.fill(); - } - ctx.restore(); - } - - //localpos is mouse in curve editor space - CurveEditor.prototype.onMouseDown = function( localpos, graphcanvas ) - { - var points = this.points; - if(!points) - return; - if( localpos[1] < 0 ) - return; - - //this.captureInput(true); - var w = this.size[0] - this.margin * 2; - var h = this.size[1] - this.margin * 2; - var x = localpos[0] - this.margin; - var y = localpos[1] - this.margin; - var pos = [x,y]; - var max_dist = 30 / graphcanvas.ds.scale; - //search closer one - this.selected = this.getCloserPoint(pos, max_dist); - //create one - if(this.selected == -1) - { - var point = [x / w, 1 - y / h]; - points.push(point); - points.sort(function(a,b){ return a[0] - b[0]; }); - this.selected = points.indexOf(point); - this.must_update = true; - } - if(this.selected != -1) - return true; - } - - CurveEditor.prototype.onMouseMove = function( localpos, graphcanvas ) - { - var points = this.points; - if(!points) - return; - var s = this.selected; - if(s < 0) - return; - var x = (localpos[0] - this.margin) / (this.size[0] - this.margin * 2 ); - var y = (localpos[1] - this.margin) / (this.size[1] - this.margin * 2 ); - var curvepos = [(localpos[0] - this.margin),(localpos[1] - this.margin)]; - var max_dist = 30 / graphcanvas.ds.scale; - this._nearest = this.getCloserPoint(curvepos, max_dist); - var point = points[s]; - if(point) - { - var is_edge_point = s == 0 || s == points.length - 1; - if( !is_edge_point && (localpos[0] < -10 || localpos[0] > this.size[0] + 10 || localpos[1] < -10 || localpos[1] > this.size[1] + 10) ) - { - points.splice(s,1); - this.selected = -1; - return; - } - if( !is_edge_point ) //not edges - point[0] = Math.clamp(x,0,1); - else - point[0] = s == 0 ? 0 : 1; - point[1] = 1.0 - Math.clamp(y,0,1); - points.sort(function(a,b){ return a[0] - b[0]; }); - this.selected = points.indexOf(point); - this.must_update = true; - } - } - - CurveEditor.prototype.onMouseUp = function( localpos, graphcanvas ) - { - this.selected = -1; - return false; - } - - CurveEditor.prototype.getCloserPoint = function(pos, max_dist) - { - var points = this.points; - if(!points) - return -1; - max_dist = max_dist || 30; - var w = (this.size[0] - this.margin * 2); - var h = (this.size[1] - this.margin * 2); - var num = points.length; - var p2 = [0,0]; - var min_dist = 1000000; - var closest = -1; - var last_valid = -1; - for(var i = 0; i < num; ++i) - { - var p = points[i]; - p2[0] = p[0] * w; - p2[1] = (1.0 - p[1]) * h; - if(p2[0] < pos[0]) - last_valid = i; - var dist = vec2.distance(pos,p2); - if(dist > min_dist || dist > max_dist) - continue; - closest = i; - min_dist = dist; - } - return closest; - } - - LiteGraph.CurveEditor = CurveEditor; - - //used to create nodes from wrapping functions - LiteGraph.getParameterNames = function(func) { - return (func + "") - .replace(/[/][/].*$/gm, "") // strip single-line comments - .replace(/\s+/g, "") // strip white space - .replace(/[/][*][^/*]*[*][/]/g, "") // strip multi-line comments /**/ - .split("){", 1)[0] - .replace(/^[^(]*[(]/, "") // extract the parameters - .replace(/=[^,]+/g, "") // strip any ES6 defaults - .split(",") - .filter(Boolean); // split & filter [""] - }; - - Math.clamp = function(v, a, b) { - return a > v ? a : b < v ? b : v; - }; - - if (typeof window != "undefined" && !window["requestAnimationFrame"]) { - window.requestAnimationFrame = - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - function(callback) { - window.setTimeout(callback, 1000 / 60); - }; - } -})(this); - -if (typeof exports != "undefined") { - exports.LiteGraph = this.LiteGraph; -} - -//basic nodes -(function(global) { - var LiteGraph = global.LiteGraph; - - //Constant - function Time() { - this.addOutput("in ms", "number"); - this.addOutput("in sec", "number"); - } - - Time.title = "Time"; - Time.desc = "Time"; - - Time.prototype.onExecute = function() { - this.setOutputData(0, this.graph.globaltime * 1000); - this.setOutputData(1, this.graph.globaltime); - }; - - LiteGraph.registerNodeType("basic/time", Time); - - //Subgraph: a node that contains a graph - function Subgraph() { - var that = this; - this.size = [140, 80]; - this.properties = { enabled: true }; - this.enabled = true; - - //create inner graph - this.subgraph = new LiteGraph.LGraph(); - this.subgraph._subgraph_node = this; - this.subgraph._is_subgraph = true; - - this.subgraph.onTrigger = this.onSubgraphTrigger.bind(this); - - //nodes input node added inside - this.subgraph.onInputAdded = this.onSubgraphNewInput.bind(this); - this.subgraph.onInputRenamed = this.onSubgraphRenamedInput.bind(this); - this.subgraph.onInputTypeChanged = this.onSubgraphTypeChangeInput.bind(this); - this.subgraph.onInputRemoved = this.onSubgraphRemovedInput.bind(this); - - this.subgraph.onOutputAdded = this.onSubgraphNewOutput.bind(this); - this.subgraph.onOutputRenamed = this.onSubgraphRenamedOutput.bind(this); - this.subgraph.onOutputTypeChanged = this.onSubgraphTypeChangeOutput.bind(this); - this.subgraph.onOutputRemoved = this.onSubgraphRemovedOutput.bind(this); - } - - Subgraph.title = "Subgraph"; - Subgraph.desc = "Graph inside a node"; - Subgraph.title_color = "#334"; - - Subgraph.prototype.onGetInputs = function() { - return [["enabled", "boolean"]]; - }; - - Subgraph.prototype.onDrawTitle = function(ctx) { - if (this.flags.collapsed) { - return; - } - - ctx.fillStyle = "#555"; - var w = LiteGraph.NODE_TITLE_HEIGHT; - var x = this.size[0] - w; - ctx.fillRect(x, -w, w, w); - ctx.fillStyle = "#333"; - ctx.beginPath(); - ctx.moveTo(x + w * 0.2, -w * 0.6); - ctx.lineTo(x + w * 0.8, -w * 0.6); - ctx.lineTo(x + w * 0.5, -w * 0.3); - ctx.fill(); - }; - - Subgraph.prototype.onDblClick = function(e, pos, graphcanvas) { - var that = this; - setTimeout(function() { - graphcanvas.openSubgraph(that.subgraph); - }, 10); - }; - - Subgraph.prototype.onMouseDown = function(e, pos, graphcanvas) { - if ( - !this.flags.collapsed && - pos[0] > this.size[0] - LiteGraph.NODE_TITLE_HEIGHT && - pos[1] < 0 - ) { - var that = this; - setTimeout(function() { - graphcanvas.openSubgraph(that.subgraph); - }, 10); - } - }; - - Subgraph.prototype.onAction = function(action, param) { - this.subgraph.onAction(action, param); - }; - - Subgraph.prototype.onExecute = function() { - this.enabled = this.getInputOrProperty("enabled"); - if (!this.enabled) { - return; - } - - //send inputs to subgraph global inputs - if (this.inputs) { - for (var i = 0; i < this.inputs.length; i++) { - var input = this.inputs[i]; - var value = this.getInputData(i); - this.subgraph.setInputData(input.name, value); - } - } - - //execute - this.subgraph.runStep(); - - //send subgraph global outputs to outputs - if (this.outputs) { - for (var i = 0; i < this.outputs.length; i++) { - var output = this.outputs[i]; - var value = this.subgraph.getOutputData(output.name); - this.setOutputData(i, value); - } - } - }; - - Subgraph.prototype.sendEventToAllNodes = function(eventname, param, mode) { - if (this.enabled) { - this.subgraph.sendEventToAllNodes(eventname, param, mode); - } - }; - - //**** INPUTS *********************************** - Subgraph.prototype.onSubgraphTrigger = function(event, param) { - var slot = this.findOutputSlot(event); - if (slot != -1) { - this.triggerSlot(slot); - } - }; - - Subgraph.prototype.onSubgraphNewInput = function(name, type) { - var slot = this.findInputSlot(name); - if (slot == -1) { - //add input to the node - this.addInput(name, type); - } - }; - - Subgraph.prototype.onSubgraphRenamedInput = function(oldname, name) { - var slot = this.findInputSlot(oldname); - if (slot == -1) { - return; - } - var info = this.getInputInfo(slot); - info.name = name; - }; - - Subgraph.prototype.onSubgraphTypeChangeInput = function(name, type) { - var slot = this.findInputSlot(name); - if (slot == -1) { - return; - } - var info = this.getInputInfo(slot); - info.type = type; - }; - - Subgraph.prototype.onSubgraphRemovedInput = function(name) { - var slot = this.findInputSlot(name); - if (slot == -1) { - return; - } - this.removeInput(slot); - }; - - //**** OUTPUTS *********************************** - Subgraph.prototype.onSubgraphNewOutput = function(name, type) { - var slot = this.findOutputSlot(name); - if (slot == -1) { - this.addOutput(name, type); - } - }; - - Subgraph.prototype.onSubgraphRenamedOutput = function(oldname, name) { - var slot = this.findOutputSlot(oldname); - if (slot == -1) { - return; - } - var info = this.getOutputInfo(slot); - info.name = name; - }; - - Subgraph.prototype.onSubgraphTypeChangeOutput = function(name, type) { - var slot = this.findOutputSlot(name); - if (slot == -1) { - return; - } - var info = this.getOutputInfo(slot); - info.type = type; - }; - - Subgraph.prototype.onSubgraphRemovedOutput = function(name) { - var slot = this.findInputSlot(name); - if (slot == -1) { - return; - } - this.removeOutput(slot); - }; - // ***************************************************** - - Subgraph.prototype.getExtraMenuOptions = function(graphcanvas) { - var that = this; - return [ - { - content: "Open", - callback: function() { - graphcanvas.openSubgraph(that.subgraph); - } - } - ]; - }; - - Subgraph.prototype.onResize = function(size) { - size[1] += 20; - }; - - Subgraph.prototype.serialize = function() { - var data = LiteGraph.LGraphNode.prototype.serialize.call(this); - data.subgraph = this.subgraph.serialize(); - return data; - }; - //no need to define node.configure, the default method detects node.subgraph and passes the object to node.subgraph.configure() - - Subgraph.prototype.clone = function() { - var node = LiteGraph.createNode(this.type); - var data = this.serialize(); - delete data["id"]; - delete data["inputs"]; - delete data["outputs"]; - node.configure(data); - return node; - }; - - Subgraph.prototype.buildFromNodes = function(nodes) - { - //clear all? - //TODO - - //nodes that connect data between parent graph and subgraph - var subgraph_inputs = []; - var subgraph_outputs = []; - - //mark inner nodes - var ids = {}; - var min_x = 0; - var max_x = 0; - for(var i = 0; i < nodes.length; ++i) - { - var node = nodes[i]; - ids[ node.id ] = node; - min_x = Math.min( node.pos[0], min_x ); - max_x = Math.max( node.pos[0], min_x ); - } - - var last_input_y = 0; - var last_output_y = 0; - - for(var i = 0; i < nodes.length; ++i) - { - var node = nodes[i]; - //check inputs - if( node.inputs ) - for(var j = 0; j < node.inputs.length; ++j) - { - var input = node.inputs[j]; - if( !input || !input.link ) - continue; - var link = node.graph.links[ input.link ]; - if(!link) - continue; - if( ids[ link.origin_id ] ) - continue; - //this.addInput(input.name,link.type); - this.subgraph.addInput(input.name,link.type); - /* - var input_node = LiteGraph.createNode("graph/input"); - this.subgraph.add( input_node ); - input_node.pos = [min_x - 200, last_input_y ]; - last_input_y += 100; - */ - } - - //check outputs - if( node.outputs ) - for(var j = 0; j < node.outputs.length; ++j) - { - var output = node.outputs[j]; - if( !output || !output.links || !output.links.length ) - continue; - var is_external = false; - for(var k = 0; k < output.links.length; ++k) - { - var link = node.graph.links[ output.links[k] ]; - if(!link) - continue; - if( ids[ link.target_id ] ) - continue; - is_external = true; - break; - } - if(!is_external) - continue; - //this.addOutput(output.name,output.type); - /* - var output_node = LiteGraph.createNode("graph/output"); - this.subgraph.add( output_node ); - output_node.pos = [max_x + 50, last_output_y ]; - last_output_y += 100; - */ - } - } - - //detect inputs and outputs - //split every connection in two data_connection nodes - //keep track of internal connections - //connect external connections - - //clone nodes inside subgraph and try to reconnect them - - //connect edge subgraph nodes to extarnal connections nodes - } - - LiteGraph.Subgraph = Subgraph; - LiteGraph.registerNodeType("graph/subgraph", Subgraph); - - //Input for a subgraph - function GraphInput() { - this.addOutput("", "number"); - - this.name_in_graph = ""; - this.properties = { - name: "", - type: "number", - value: 0 - }; - - var that = this; - - this.name_widget = this.addWidget( - "text", - "Name", - this.properties.name, - function(v) { - if (!v) { - return; - } - that.setProperty("name",v); - } - ); - this.type_widget = this.addWidget( - "text", - "Type", - this.properties.type, - function(v) { - that.setProperty("type",v); - } - ); - - this.value_widget = this.addWidget( - "number", - "Value", - this.properties.value, - function(v) { - that.setProperty("value",v); - } - ); - - this.widgets_up = true; - this.size = [180, 90]; - } - - GraphInput.title = "Input"; - GraphInput.desc = "Input of the graph"; - - GraphInput.prototype.onConfigure = function() - { - this.updateType(); - } - - GraphInput.prototype.updateType = function() - { - var type = this.properties.type; - this.type_widget.value = type; - if(this.outputs[0].type != type) - { - this.outputs[0].type = type; - this.disconnectOutput(0); - } - if(type == "number") - { - this.value_widget.type = "number"; - this.value_widget.value = 0; - } - else if(type == "boolean") - { - this.value_widget.type = "toggle"; - this.value_widget.value = true; - } - else if(type == "string") - { - this.value_widget.type = "text"; - this.value_widget.value = ""; - } - else - { - this.value_widget.type = null; - this.value_widget.value = null; - } - this.properties.value = this.value_widget.value; - } - - GraphInput.prototype.onPropertyChanged = function(name,v) - { - if( name == "name" ) - { - if (v == "" || v == this.name_in_graph || v == "enabled") { - return false; - } - if(this.graph) - { - if (this.name_in_graph) { - //already added - this.graph.renameInput( this.name_in_graph, v ); - } else { - this.graph.addInput( v, this.properties.type ); - } - } //what if not?! - this.name_widget.value = v; - this.name_in_graph = v; - } - else if( name == "type" ) - { - v = v || ""; - this.updateType(v); - } - else if( name == "value" ) - { - } - } - - GraphInput.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this.properties.name; - } - return this.title; - }; - - GraphInput.prototype.onAction = function(action, param) { - if (this.properties.type == LiteGraph.EVENT) { - this.triggerSlot(0, param); - } - }; - - GraphInput.prototype.onExecute = function() { - var name = this.properties.name; - //read from global input - var data = this.graph.inputs[name]; - if (!data) { - this.setOutputData(0, this.properties.value ); - return; - } - - this.setOutputData(0, data.value !== undefined ? data.value : this.properties.value ); - }; - - GraphInput.prototype.onRemoved = function() { - if (this.name_in_graph) { - this.graph.removeInput(this.name_in_graph); - } - }; - - LiteGraph.GraphInput = GraphInput; - LiteGraph.registerNodeType("graph/input", GraphInput); - - //Output for a subgraph - function GraphOutput() { - this.addInput("", ""); - - this.name_in_graph = ""; - this.properties = {}; - var that = this; - - Object.defineProperty(this.properties, "name", { - get: function() { - return that.name_in_graph; - }, - set: function(v) { - if (v == "" || v == that.name_in_graph) { - return; - } - if (that.name_in_graph) { - //already added - that.graph.renameOutput(that.name_in_graph, v); - } else { - that.graph.addOutput(v, that.properties.type); - } - that.name_widget.value = v; - that.name_in_graph = v; - }, - enumerable: true - }); - - Object.defineProperty(this.properties, "type", { - get: function() { - return that.inputs[0].type; - }, - set: function(v) { - if (v == "action" || v == "event") { - v = LiteGraph.ACTION; - } - that.inputs[0].type = v; - if (that.name_in_graph) { - //already added - that.graph.changeOutputType( - that.name_in_graph, - that.inputs[0].type - ); - } - that.type_widget.value = v || ""; - }, - enumerable: true - }); - - this.name_widget = this.addWidget("text","Name",this.properties.name,"name"); - this.type_widget = this.addWidget("text","Type",this.properties.type,"type"); - this.widgets_up = true; - this.size = [180, 60]; - } - - GraphOutput.title = "Output"; - GraphOutput.desc = "Output of the graph"; - - GraphOutput.prototype.onExecute = function() { - this._value = this.getInputData(0); - this.graph.setOutputData(this.properties.name, this._value); - }; - - GraphOutput.prototype.onAction = function(action, param) { - if (this.properties.type == LiteGraph.ACTION) { - this.graph.trigger(this.properties.name, param); - } - }; - - GraphOutput.prototype.onRemoved = function() { - if (this.name_in_graph) { - this.graph.removeOutput(this.name_in_graph); - } - }; - - GraphOutput.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this.properties.name; - } - return this.title; - }; - - LiteGraph.GraphOutput = GraphOutput; - LiteGraph.registerNodeType("graph/output", GraphOutput); - - //Constant - function ConstantNumber() { - this.addOutput("value", "number"); - this.addProperty("value", 1.0); - this.widget = this.addWidget("number","value",1,"value"); - this.widgets_up = true; - this.size = [180, 30]; - } - - ConstantNumber.title = "Const Number"; - ConstantNumber.desc = "Constant number"; - - ConstantNumber.prototype.onExecute = function() { - this.setOutputData(0, parseFloat(this.properties["value"])); - }; - - ConstantNumber.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this.properties.value; - } - return this.title; - }; - - ConstantNumber.prototype.setValue = function(v) - { - this.setProperty("value",v); - } - - ConstantNumber.prototype.onDrawBackground = function(ctx) { - //show the current value - this.outputs[0].label = this.properties["value"].toFixed(3); - }; - - LiteGraph.registerNodeType("basic/const", ConstantNumber); - - function ConstantBoolean() { - this.addOutput("", "boolean"); - this.addProperty("value", true); - this.widget = this.addWidget("toggle","value",true,"value"); - this.widgets_up = true; - this.size = [140, 30]; - } - - ConstantBoolean.title = "Const Boolean"; - ConstantBoolean.desc = "Constant boolean"; - ConstantBoolean.prototype.getTitle = ConstantNumber.prototype.getTitle; - - ConstantBoolean.prototype.onExecute = function() { - this.setOutputData(0, this.properties["value"]); - }; - - ConstantBoolean.prototype.setValue = ConstantNumber.prototype.setValue; - - ConstantBoolean.prototype.onGetInputs = function() { - return [["toggle", LiteGraph.ACTION]]; - }; - - ConstantBoolean.prototype.onAction = function(action) - { - this.setValue( !this.properties.value ); - } - - LiteGraph.registerNodeType("basic/boolean", ConstantBoolean); - - function ConstantString() { - this.addOutput("", "string"); - this.addProperty("value", ""); - this.widget = this.addWidget("text","value","","value"); //link to property value - this.widgets_up = true; - this.size = [180, 30]; - } - - ConstantString.title = "Const String"; - ConstantString.desc = "Constant string"; - - ConstantString.prototype.getTitle = ConstantNumber.prototype.getTitle; - - ConstantString.prototype.onExecute = function() { - this.setOutputData(0, this.properties["value"]); - }; - - ConstantString.prototype.setValue = ConstantNumber.prototype.setValue; - - ConstantString.prototype.onDropFile = function(file) - { - var that = this; - var reader = new FileReader(); - reader.onload = function(e) - { - that.setProperty("value",e.target.result); - } - reader.readAsText(file); - } - - LiteGraph.registerNodeType("basic/string", ConstantString); - - function ConstantFile() { - this.addInput("url", ""); - this.addOutput("", ""); - this.addProperty("url", ""); - this.addProperty("type", "text"); - this.widget = this.addWidget("text","url","","url"); - this._data = null; - } - - ConstantFile.title = "Const File"; - ConstantFile.desc = "Fetches a file from an url"; - ConstantFile["@type"] = { type: "enum", values: ["text","arraybuffer","blob","json"] }; - - ConstantFile.prototype.onPropertyChanged = function(name, value) { - if (name == "url") - { - if( value == null || value == "") - this._data = null; - else - { - this.fetchFile(value); - } - } - } - - ConstantFile.prototype.onExecute = function() { - var url = this.getInputData(0) || this.properties.url; - if(url && (url != this._url || this._type != this.properties.type)) - this.fetchFile(url); - this.setOutputData(0, this._data ); - }; - - ConstantFile.prototype.setValue = ConstantNumber.prototype.setValue; - - ConstantFile.prototype.fetchFile = function(url) { - var that = this; - if(!url || url.constructor !== String) - { - that._data = null; - that.boxcolor = null; - return; - } - - this._url = url; - this._type = this.properties.type; - if (url.substr(0, 4) == "http" && LiteGraph.proxy) { - url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); - } - fetch(url) - .then(function(response) { - if(!response.ok) - throw new Error("File not found"); - - if(that.properties.type == "arraybuffer") - return response.arrayBuffer(); - else if(that.properties.type == "text") - return response.text(); - else if(that.properties.type == "json") - return response.json(); - else if(that.properties.type == "blob") - return response.blob(); - }) - .then(function(data) { - that._data = data; - that.boxcolor = "#AEA"; - }) - .catch(function(error) { - that._data = null; - that.boxcolor = "red"; - console.error("error fetching file:",url); - }); - }; - - ConstantFile.prototype.onDropFile = function(file) - { - var that = this; - this._url = file.name; - this._type = this.properties.type; - this.properties.url = file.name; - var reader = new FileReader(); - reader.onload = function(e) - { - that.boxcolor = "#AEA"; - var v = e.target.result; - if( that.properties.type == "json" ) - v = JSON.parse(v); - that._data = v; - } - if(that.properties.type == "arraybuffer") - reader.readAsArrayBuffer(file); - else if(that.properties.type == "text" || that.properties.type == "json") - reader.readAsText(file); - else if(that.properties.type == "blob") - return reader.readAsBinaryString(file); - } - - LiteGraph.registerNodeType("basic/file", ConstantFile); - - //to store json objects - function ConstantData() { - this.addOutput("", ""); - this.addProperty("value", ""); - this.widget = this.addWidget("text","json","","value"); - this.widgets_up = true; - this.size = [140, 30]; - this._value = null; - } - - ConstantData.title = "Const Data"; - ConstantData.desc = "Constant Data"; - - ConstantData.prototype.onPropertyChanged = function(name, value) { - this.widget.value = value; - if (value == null || value == "") { - return; - } - - try { - this._value = JSON.parse(value); - this.boxcolor = "#AEA"; - } catch (err) { - this.boxcolor = "red"; - } - }; - - ConstantData.prototype.onExecute = function() { - this.setOutputData(0, this._value); - }; - - ConstantData.prototype.setValue = ConstantNumber.prototype.setValue; - - LiteGraph.registerNodeType("basic/data", ConstantData); - - //to store json objects - function ConstantArray() { - this.addInput("", ""); - this.addOutput("", "array"); - this.addOutput("length", "number"); - this.addProperty("value", ""); - this.widget = this.addWidget("text","array","","value"); - this.widgets_up = true; - this.size = [140, 50]; - this._value = null; - } - - ConstantArray.title = "Const Array"; - ConstantArray.desc = "Constant Array"; - - ConstantArray.prototype.onPropertyChanged = function(name, value) { - this.widget.value = value; - if (value == null || value == "") { - return; - } - - try { - if(value[0] != "[") - this._value = JSON.parse("[" + value + "]"); - else - this._value = JSON.parse(value); - this.boxcolor = "#AEA"; - } catch (err) { - this.boxcolor = "red"; - } - }; - - ConstantArray.prototype.onExecute = function() { - var v = this.getInputData(0); - if(v && v.length) //clone - { - if(!this._value) - this._value = new Array(); - this._value.length = v.length; - for(var i = 0; i < v.length; ++i) - this._value[i] = v[i]; - } - this.setOutputData(0, this._value); - this.setOutputData(1, this._value ? ( this._value.length || 0) : 0 ); - }; - - ConstantArray.prototype.setValue = ConstantNumber.prototype.setValue; - - LiteGraph.registerNodeType("basic/array", ConstantArray); - - function ArrayElement() { - this.addInput("array", "array,table,string"); - this.addInput("index", "number"); - this.addOutput("value", ""); - this.addProperty("index",0); - } - - ArrayElement.title = "Array[i]"; - ArrayElement.desc = "Returns an element from an array"; - - ArrayElement.prototype.onExecute = function() { - var array = this.getInputData(0); - var index = this.getInputData(1); - if(index == null) - index = this.properties.index; - if(array == null || index == null ) - return; - this.setOutputData(0, array[Math.floor(Number(index))] ); - }; - - LiteGraph.registerNodeType("basic/array[]", ArrayElement); - - - - function TableElement() { - this.addInput("table", "table"); - this.addInput("row", "number"); - this.addInput("col", "number"); - this.addOutput("value", ""); - this.addProperty("row",0); - this.addProperty("column",0); - } - - TableElement.title = "Table[row][col]"; - TableElement.desc = "Returns an element from a table"; - - TableElement.prototype.onExecute = function() { - var table = this.getInputData(0); - var row = this.getInputData(1); - var col = this.getInputData(2); - if(row == null) - row = this.properties.row; - if(col == null) - col = this.properties.column; - if(table == null || row == null || col == null) - return; - var row = table[Math.floor(Number(row))]; - if(row) - this.setOutputData(0, row[Math.floor(Number(col))] ); - else - this.setOutputData(0, null ); - }; - - LiteGraph.registerNodeType("basic/table[][]", TableElement); - - function ObjectProperty() { - this.addInput("obj", ""); - this.addOutput("", ""); - this.addProperty("value", ""); - this.widget = this.addWidget("text","prop.","",this.setValue.bind(this) ); - this.widgets_up = true; - this.size = [140, 30]; - this._value = null; - } - - ObjectProperty.title = "Object property"; - ObjectProperty.desc = "Outputs the property of an object"; - - ObjectProperty.prototype.setValue = function(v) { - this.properties.value = v; - this.widget.value = v; - }; - - ObjectProperty.prototype.getTitle = function() { - if (this.flags.collapsed) { - return "in." + this.properties.value; - } - return this.title; - }; - - ObjectProperty.prototype.onPropertyChanged = function(name, value) { - this.widget.value = value; - }; - - ObjectProperty.prototype.onExecute = function() { - var data = this.getInputData(0); - if (data != null) { - this.setOutputData(0, data[this.properties.value]); - } - }; - - LiteGraph.registerNodeType("basic/object_property", ObjectProperty); - - function ObjectKeys() { - this.addInput("obj", ""); - this.addOutput("keys", "array"); - this.size = [140, 30]; - } - - ObjectKeys.title = "Object keys"; - ObjectKeys.desc = "Outputs an array with the keys of an object"; - - ObjectKeys.prototype.onExecute = function() { - var data = this.getInputData(0); - if (data != null) { - this.setOutputData(0, Object.keys(data) ); - } - }; - - LiteGraph.registerNodeType("basic/object_keys", ObjectKeys); - - function MergeObjects() { - this.addInput("A", "object"); - this.addInput("B", "object"); - this.addOutput("", "object"); - this._result = {}; - var that = this; - this.addWidget("button","clear","",function(){ - that._result = {}; - }); - this.size = this.computeSize(); - } - - MergeObjects.title = "Merge Objects"; - MergeObjects.desc = "Creates an object copying properties from others"; - - MergeObjects.prototype.onExecute = function() { - var A = this.getInputData(0); - var B = this.getInputData(1); - var C = this._result; - if(A) - for(var i in A) - C[i] = A[i]; - if(B) - for(var i in B) - C[i] = B[i]; - this.setOutputData(0,C); - }; - - LiteGraph.registerNodeType("basic/merge_objects", MergeObjects ); - - //Store as variable - function Variable() { - this.size = [60, 30]; - this.addInput("in"); - this.addOutput("out"); - this.properties = { varname: "myname", global: false }; - this.value = null; - } - - Variable.title = "Variable"; - Variable.desc = "store/read variable value"; - - Variable.prototype.onExecute = function() { - this.value = this.getInputData(0); - if(this.graph) - this.graph.vars[ this.properties.varname ] = this.value; - if(this.properties.global) - global[this.properties.varname] = this.value; - this.setOutputData(0, this.value ); - }; - - Variable.prototype.getTitle = function() { - return this.properties.varname; - }; - - LiteGraph.registerNodeType("basic/variable", Variable); - - function length(v) { - if(v && v.length != null) - return Number(v.length); - return 0; - } - - LiteGraph.wrapFunctionAsNode( - "basic/length", - length, - [""], - "number" - ); - - function DownloadData() { - this.size = [60, 30]; - this.addInput("data", 0 ); - this.addInput("download", LiteGraph.ACTION ); - this.properties = { filename: "data.json" }; - this.value = null; - var that = this; - this.addWidget("button","Download","", function(v){ - if(!that.value) - return; - that.downloadAsFile(); - }); - } - - DownloadData.title = "Download"; - DownloadData.desc = "Download some data"; - - DownloadData.prototype.downloadAsFile = function() - { - if(this.value == null) - return; - - var str = null; - if(this.value.constructor === String) - str = this.value; - else - str = JSON.stringify(this.value); - - var file = new Blob([str]); - var url = URL.createObjectURL( file ); - var element = document.createElement("a"); - element.setAttribute('href', url); - element.setAttribute('download', this.properties.filename ); - element.style.display = 'none'; - document.body.appendChild(element); - element.click(); - document.body.removeChild(element); - setTimeout( function(){ URL.revokeObjectURL( url ); }, 1000*60 ); //wait one minute to revoke url - } - - DownloadData.prototype.onAction = function(action, param) { - var that = this; - setTimeout( function(){ that.downloadAsFile(); }, 100); //deferred to avoid blocking the renderer with the popup - } - - DownloadData.prototype.onExecute = function() { - if (this.inputs[0]) { - this.value = this.getInputData(0); - } - }; - - DownloadData.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this.properties.filename; - } - return this.title; - }; - - LiteGraph.registerNodeType("basic/download", DownloadData); - - - - //Watch a value in the editor - function Watch() { - this.size = [60, 30]; - this.addInput("value", 0, { label: "" }); - this.value = 0; - } - - Watch.title = "Watch"; - Watch.desc = "Show value of input"; - - Watch.prototype.onExecute = function() { - if (this.inputs[0]) { - this.value = this.getInputData(0); - } - }; - - Watch.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this.inputs[0].label; - } - return this.title; - }; - - Watch.toString = function(o) { - if (o == null) { - return "null"; - } else if (o.constructor === Number) { - return o.toFixed(3); - } else if (o.constructor === Array) { - var str = "["; - for (var i = 0; i < o.length; ++i) { - str += Watch.toString(o[i]) + (i + 1 != o.length ? "," : ""); - } - str += "]"; - return str; - } else { - return String(o); - } - }; - - Watch.prototype.onDrawBackground = function(ctx) { - //show the current value - this.inputs[0].label = Watch.toString(this.value); - }; - - LiteGraph.registerNodeType("basic/watch", Watch); - - //in case one type doesnt match other type but you want to connect them anyway - function Cast() { - this.addInput("in", 0); - this.addOutput("out", 0); - this.size = [40, 30]; - } - - Cast.title = "Cast"; - Cast.desc = "Allows to connect different types"; - - Cast.prototype.onExecute = function() { - this.setOutputData(0, this.getInputData(0)); - }; - - LiteGraph.registerNodeType("basic/cast", Cast); - - //Show value inside the debug console - function Console() { - this.mode = LiteGraph.ON_EVENT; - this.size = [80, 30]; - this.addProperty("msg", ""); - this.addInput("log", LiteGraph.EVENT); - this.addInput("msg", 0); - } - - Console.title = "Console"; - Console.desc = "Show value inside the console"; - - Console.prototype.onAction = function(action, param) { - if (action == "log") { - console.log(param); - } else if (action == "warn") { - console.warn(param); - } else if (action == "error") { - console.error(param); - } - }; - - Console.prototype.onExecute = function() { - var msg = this.getInputData(1); - if (msg !== null) { - this.properties.msg = msg; - } - console.log(msg); - }; - - Console.prototype.onGetInputs = function() { - return [ - ["log", LiteGraph.ACTION], - ["warn", LiteGraph.ACTION], - ["error", LiteGraph.ACTION] - ]; - }; - - LiteGraph.registerNodeType("basic/console", Console); - - //Show value inside the debug console - function Alert() { - this.mode = LiteGraph.ON_EVENT; - this.addProperty("msg", ""); - this.addInput("", LiteGraph.EVENT); - var that = this; - this.widget = this.addWidget("text", "Text", "", function(v) { - that.properties.msg = v; - }); - this.widgets_up = true; - this.size = [200, 30]; - } - - Alert.title = "Alert"; - Alert.desc = "Show an alert window"; - Alert.color = "#510"; - - Alert.prototype.onConfigure = function(o) { - this.widget.value = o.properties.msg; - }; - - Alert.prototype.onAction = function(action, param) { - var msg = this.properties.msg; - setTimeout(function() { - alert(msg); - }, 10); - }; - - LiteGraph.registerNodeType("basic/alert", Alert); - - //Execites simple code - function NodeScript() { - this.size = [60, 30]; - this.addProperty("onExecute", "return A;"); - this.addInput("A", ""); - this.addInput("B", ""); - this.addOutput("out", ""); - - this._func = null; - this.data = {}; - } - - NodeScript.prototype.onConfigure = function(o) { - if (o.properties.onExecute && LiteGraph.allow_scripts) - this.compileCode(o.properties.onExecute); - else - console.warn("Script not compiled, LiteGraph.allow_scripts is false"); - }; - - NodeScript.title = "Script"; - NodeScript.desc = "executes a code (max 100 characters)"; - - NodeScript.widgets_info = { - onExecute: { type: "code" } - }; - - NodeScript.prototype.onPropertyChanged = function(name, value) { - if (name == "onExecute" && LiteGraph.allow_scripts) - this.compileCode(value); - else - console.warn("Script not compiled, LiteGraph.allow_scripts is false"); - }; - - NodeScript.prototype.compileCode = function(code) { - this._func = null; - if (code.length > 256) { - console.warn("Script too long, max 256 chars"); - } else { - var code_low = code.toLowerCase(); - var forbidden_words = [ - "script", - "body", - "document", - "eval", - "nodescript", - "function" - ]; //bad security solution - for (var i = 0; i < forbidden_words.length; ++i) { - if (code_low.indexOf(forbidden_words[i]) != -1) { - console.warn("invalid script"); - return; - } - } - try { - this._func = new Function("A", "B", "C", "DATA", "node", code); - } catch (err) { - console.error("Error parsing script"); - console.error(err); - } - } - }; - - NodeScript.prototype.onExecute = function() { - if (!this._func) { - return; - } - - try { - var A = this.getInputData(0); - var B = this.getInputData(1); - var C = this.getInputData(2); - this.setOutputData(0, this._func(A, B, C, this.data, this)); - } catch (err) { - console.error("Error in script"); - console.error(err); - } - }; - - NodeScript.prototype.onGetOutputs = function() { - return [["C", ""]]; - }; - - LiteGraph.registerNodeType("basic/script", NodeScript); -})(this); - +(function(global) { + // ************************************************************* + // LiteGraph CLASS ******* + // ************************************************************* + + /** + * The Global Scope. It contains all the registered node classes. + * + * @class LiteGraph + * @constructor + */ + + var LiteGraph = (global.LiteGraph = { + VERSION: 0.4, + + CANVAS_GRID_SIZE: 10, + + NODE_TITLE_HEIGHT: 30, + NODE_TITLE_TEXT_Y: 20, + NODE_SLOT_HEIGHT: 20, + NODE_WIDGET_HEIGHT: 20, + NODE_WIDTH: 140, + NODE_MIN_WIDTH: 50, + NODE_COLLAPSED_RADIUS: 10, + NODE_COLLAPSED_WIDTH: 80, + NODE_TITLE_COLOR: "#999", + NODE_TEXT_SIZE: 14, + NODE_TEXT_COLOR: "#AAA", + NODE_SUBTEXT_SIZE: 12, + NODE_DEFAULT_COLOR: "#333", + NODE_DEFAULT_BGCOLOR: "#353535", + NODE_DEFAULT_BOXCOLOR: "#666", + NODE_DEFAULT_SHAPE: "box", + DEFAULT_SHADOW_COLOR: "rgba(0,0,0,0.5)", + DEFAULT_GROUP_FONT: 24, + + WIDGET_BGCOLOR: "#222", + WIDGET_OUTLINE_COLOR: "#666", + WIDGET_TEXT_COLOR: "#DDD", + WIDGET_SECONDARY_TEXT_COLOR: "#999", + + LINK_COLOR: "#9A9", + EVENT_LINK_COLOR: "#A86", + CONNECTING_LINK_COLOR: "#AFA", + + MAX_NUMBER_OF_NODES: 1000, //avoid infinite loops + DEFAULT_POSITION: [100, 100], //default node position + VALID_SHAPES: ["default", "box", "round", "card"], //,"circle" + + //shapes are used for nodes but also for slots + BOX_SHAPE: 1, + ROUND_SHAPE: 2, + CIRCLE_SHAPE: 3, + CARD_SHAPE: 4, + ARROW_SHAPE: 5, + + //enums + INPUT: 1, + OUTPUT: 2, + + EVENT: -1, //for outputs + ACTION: -1, //for inputs + + ALWAYS: 0, + ON_EVENT: 1, + NEVER: 2, + ON_TRIGGER: 3, + + UP: 1, + DOWN: 2, + LEFT: 3, + RIGHT: 4, + CENTER: 5, + + STRAIGHT_LINK: 0, + LINEAR_LINK: 1, + SPLINE_LINK: 2, + + NORMAL_TITLE: 0, + NO_TITLE: 1, + TRANSPARENT_TITLE: 2, + AUTOHIDE_TITLE: 3, + + proxy: null, //used to redirect calls + node_images_path: "", + + debug: false, + catch_exceptions: true, + throw_errors: true, + allow_scripts: false, //if set to true some nodes like Formula would be allowed to evaluate code that comes from unsafe sources (like node configuration), which could lead to exploits + registered_node_types: {}, //nodetypes by string + node_types_by_file_extension: {}, //used for dropping files in the canvas + Nodes: {}, //node types by classname + + searchbox_extras: {}, //used to add extra features to the search box + + /** + * Register a node class so it can be listed when the user wants to create a new one + * @method registerNodeType + * @param {String} type name of the node and path + * @param {Class} base_class class containing the structure of a node + */ + + registerNodeType: function(type, base_class) { + if (!base_class.prototype) { + throw "Cannot register a simple object, it must be a class with a prototype"; + } + base_class.type = type; + + if (LiteGraph.debug) { + console.log("Node registered: " + type); + } + + var categories = type.split("/"); + var classname = base_class.name; + + var pos = type.lastIndexOf("/"); + base_class.category = type.substr(0, pos); + + if (!base_class.title) { + base_class.title = classname; + } + //info.name = name.substr(pos+1,name.length - pos); + + //extend class + if (base_class.prototype) { + //is a class + for (var i in LGraphNode.prototype) { + if (!base_class.prototype[i]) { + base_class.prototype[i] = LGraphNode.prototype[i]; + } + } + } + + var prev = this.registered_node_types[type]; + if(prev) + console.log("replacing node type: " + type); + else + { + if( !Object.hasOwnProperty( base_class.prototype, "shape") ) + Object.defineProperty(base_class.prototype, "shape", { + set: function(v) { + switch (v) { + case "default": + delete this._shape; + break; + case "box": + this._shape = LiteGraph.BOX_SHAPE; + break; + case "round": + this._shape = LiteGraph.ROUND_SHAPE; + break; + case "circle": + this._shape = LiteGraph.CIRCLE_SHAPE; + break; + case "card": + this._shape = LiteGraph.CARD_SHAPE; + break; + default: + this._shape = v; + } + }, + get: function(v) { + return this._shape; + }, + enumerable: true, + configurable: true + }); + + //warnings + if (base_class.prototype.onPropertyChange) { + console.warn( + "LiteGraph node class " + + type + + " has onPropertyChange method, it must be called onPropertyChanged with d at the end" + ); + } + + //used to know which nodes create when dragging files to the canvas + if (base_class.supported_extensions) { + for (var i in base_class.supported_extensions) { + var ext = base_class.supported_extensions[i]; + if(ext && ext.constructor === String) + this.node_types_by_file_extension[ ext.toLowerCase() ] = base_class; + } + } + } + + this.registered_node_types[type] = base_class; + if (base_class.constructor.name) { + this.Nodes[classname] = base_class; + } + if (LiteGraph.onNodeTypeRegistered) { + LiteGraph.onNodeTypeRegistered(type, base_class); + } + if (prev && LiteGraph.onNodeTypeReplaced) { + LiteGraph.onNodeTypeReplaced(type, base_class, prev); + } + }, + + /** + * removes a node type from the system + * @method unregisterNodeType + * @param {String|Object} type name of the node or the node constructor itself + */ + unregisterNodeType: function(type) { + var base_class = type.constructor === String ? this.registered_node_types[type] : type; + if(!base_class) + throw("node type not found: " + type ); + delete this.registered_node_types[base_class.type]; + if(base_class.constructor.name) + delete this.Nodes[base_class.constructor.name]; + }, + + /** + * Create a new nodetype by passing a function, it wraps it with a proper class and generates inputs according to the parameters of the function. + * Useful to wrap simple methods that do not require properties, and that only process some input to generate an output. + * @method wrapFunctionAsNode + * @param {String} name node name with namespace (p.e.: 'math/sum') + * @param {Function} func + * @param {Array} param_types [optional] an array containing the type of every parameter, otherwise parameters will accept any type + * @param {String} return_type [optional] string with the return type, otherwise it will be generic + * @param {Object} properties [optional] properties to be configurable + */ + wrapFunctionAsNode: function( + name, + func, + param_types, + return_type, + properties + ) { + var params = Array(func.length); + var code = ""; + var names = LiteGraph.getParameterNames(func); + for (var i = 0; i < names.length; ++i) { + code += + "this.addInput('" + + names[i] + + "'," + + (param_types && param_types[i] + ? "'" + param_types[i] + "'" + : "0") + + ");\n"; + } + code += + "this.addOutput('out'," + + (return_type ? "'" + return_type + "'" : 0) + + ");\n"; + if (properties) { + code += + "this.properties = " + JSON.stringify(properties) + ";\n"; + } + var classobj = Function(code); + classobj.title = name.split("/").pop(); + classobj.desc = "Generated from " + func.name; + classobj.prototype.onExecute = function onExecute() { + for (var i = 0; i < params.length; ++i) { + params[i] = this.getInputData(i); + } + var r = func.apply(this, params); + this.setOutputData(0, r); + }; + this.registerNodeType(name, classobj); + }, + + /** + * Adds this method to all nodetypes, existing and to be created + * (You can add it to LGraphNode.prototype but then existing node types wont have it) + * @method addNodeMethod + * @param {Function} func + */ + addNodeMethod: function(name, func) { + LGraphNode.prototype[name] = func; + for (var i in this.registered_node_types) { + var type = this.registered_node_types[i]; + if (type.prototype[name]) { + type.prototype["_" + name] = type.prototype[name]; + } //keep old in case of replacing + type.prototype[name] = func; + } + }, + + /** + * Create a node of a given type with a name. The node is not attached to any graph yet. + * @method createNode + * @param {String} type full name of the node class. p.e. "math/sin" + * @param {String} name a name to distinguish from other nodes + * @param {Object} options to set options + */ + + createNode: function(type, title, options) { + var base_class = this.registered_node_types[type]; + if (!base_class) { + if (LiteGraph.debug) { + console.log( + 'GraphNode type "' + type + '" not registered.' + ); + } + return null; + } + + var prototype = base_class.prototype || base_class; + + title = title || base_class.title || type; + + var node = null; + + if (LiteGraph.catch_exceptions) { + try { + node = new base_class(title); + } catch (err) { + console.error(err); + return null; + } + } else { + node = new base_class(title); + } + + node.type = type; + + if (!node.title && title) { + node.title = title; + } + if (!node.properties) { + node.properties = {}; + } + if (!node.properties_info) { + node.properties_info = []; + } + if (!node.flags) { + node.flags = {}; + } + if (!node.size) { + node.size = node.computeSize(); + //call onresize? + } + if (!node.pos) { + node.pos = LiteGraph.DEFAULT_POSITION.concat(); + } + if (!node.mode) { + node.mode = LiteGraph.ALWAYS; + } + + //extra options + if (options) { + for (var i in options) { + node[i] = options[i]; + } + } + + return node; + }, + + /** + * Returns a registered node type with a given name + * @method getNodeType + * @param {String} type full name of the node class. p.e. "math/sin" + * @return {Class} the node class + */ + getNodeType: function(type) { + return this.registered_node_types[type]; + }, + + /** + * Returns a list of node types matching one category + * @method getNodeType + * @param {String} category category name + * @return {Array} array with all the node classes + */ + + getNodeTypesInCategory: function(category, filter) { + var r = []; + for (var i in this.registered_node_types) { + var type = this.registered_node_types[i]; + if (filter && type.filter && type.filter != filter) { + continue; + } + + if (category == "") { + if (type.category == null) { + r.push(type); + } + } else if (type.category == category) { + r.push(type); + } + } + + return r; + }, + + /** + * Returns a list with all the node type categories + * @method getNodeTypesCategories + * @return {Array} array with all the names of the categories + */ + getNodeTypesCategories: function( filter ) { + var categories = { "": 1 }; + for (var i in this.registered_node_types) { + var type = this.registered_node_types[i]; + if ( type.category && !type.skip_list ) + { + if(filter && type.filter != filter) + continue; + categories[type.category] = 1; + } + } + var result = []; + for (var i in categories) { + result.push(i); + } + return result; + }, + + //debug purposes: reloads all the js scripts that matches a wildcard + reloadNodes: function(folder_wildcard) { + var tmp = document.getElementsByTagName("script"); + //weird, this array changes by its own, so we use a copy + var script_files = []; + for (var i in tmp) { + script_files.push(tmp[i]); + } + + var docHeadObj = document.getElementsByTagName("head")[0]; + folder_wildcard = document.location.href + folder_wildcard; + + for (var i in script_files) { + var src = script_files[i].src; + if ( + !src || + src.substr(0, folder_wildcard.length) != folder_wildcard + ) { + continue; + } + + try { + if (LiteGraph.debug) { + console.log("Reloading: " + src); + } + var dynamicScript = document.createElement("script"); + dynamicScript.type = "text/javascript"; + dynamicScript.src = src; + docHeadObj.appendChild(dynamicScript); + docHeadObj.removeChild(script_files[i]); + } catch (err) { + if (LiteGraph.throw_errors) { + throw err; + } + if (LiteGraph.debug) { + console.log("Error while reloading " + src); + } + } + } + + if (LiteGraph.debug) { + console.log("Nodes reloaded"); + } + }, + + //separated just to improve if it doesn't work + cloneObject: function(obj, target) { + if (obj == null) { + return null; + } + var r = JSON.parse(JSON.stringify(obj)); + if (!target) { + return r; + } + + for (var i in r) { + target[i] = r[i]; + } + return target; + }, + + /** + * Returns if the types of two slots are compatible (taking into account wildcards, etc) + * @method isValidConnection + * @param {String} type_a + * @param {String} type_b + * @return {Boolean} true if they can be connected + */ + isValidConnection: function(type_a, type_b) { + if ( + !type_a || //generic output + !type_b || //generic input + type_a == type_b || //same type (is valid for triggers) + (type_a == LiteGraph.EVENT && type_b == LiteGraph.ACTION) + ) { + return true; + } + + // Enforce string type to handle toLowerCase call (-1 number not ok) + type_a = String(type_a); + type_b = String(type_b); + type_a = type_a.toLowerCase(); + type_b = type_b.toLowerCase(); + + // For nodes supporting multiple connection types + if (type_a.indexOf(",") == -1 && type_b.indexOf(",") == -1) { + return type_a == type_b; + } + + // Check all permutations to see if one is valid + var supported_types_a = type_a.split(","); + var supported_types_b = type_b.split(","); + for (var i = 0; i < supported_types_a.length; ++i) { + for (var j = 0; j < supported_types_b.length; ++j) { + if (supported_types_a[i] == supported_types_b[j]) { + return true; + } + } + } + + return false; + }, + + /** + * Register a string in the search box so when the user types it it will recommend this node + * @method registerSearchboxExtra + * @param {String} node_type the node recommended + * @param {String} description text to show next to it + * @param {Object} data it could contain info of how the node should be configured + * @return {Boolean} true if they can be connected + */ + registerSearchboxExtra: function(node_type, description, data) { + this.searchbox_extras[description.toLowerCase()] = { + type: node_type, + desc: description, + data: data + }; + }, + + /** + * Wrapper to load files (from url using fetch or from file using FileReader) + * @method fetchFile + * @param {String|File|Blob} url the url of the file (or the file itself) + * @param {String} type an string to know how to fetch it: "text","arraybuffer","json","blob" + * @param {Function} on_complete callback(data) + * @param {Function} on_error in case of an error + * @return {FileReader|Promise} returns the object used to + */ + fetchFile: function( url, type, on_complete, on_error ) { + var that = this; + if(!url) + return null; + + type = type || "text"; + if( url.constructor === String ) + { + if (url.substr(0, 4) == "http" && LiteGraph.proxy) { + url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); + } + return fetch(url) + .then(function(response) { + if(!response.ok) + throw new Error("File not found"); //it will be catch below + if(type == "arraybuffer") + return response.arrayBuffer(); + else if(type == "text" || type == "string") + return response.text(); + else if(type == "json") + return response.json(); + else if(type == "blob") + return response.blob(); + }) + .then(function(data) { + if(on_complete) + on_complete(data); + }) + .catch(function(error) { + console.error("error fetching file:",url); + if(on_error) + on_error(error); + }); + } + else if( url.constructor === File || url.constructor === Blob) + { + var reader = new FileReader(); + reader.onload = function(e) + { + var v = e.target.result; + if( type == "json" ) + v = JSON.parse(v); + if(on_complete) + on_complete(v); + } + if(type == "arraybuffer") + return reader.readAsArrayBuffer(url); + else if(type == "text" || type == "json") + return reader.readAsText(url); + else if(type == "blob") + return reader.readAsBinaryString(url); + } + return null; + } + }); + + //timer that works everywhere + if (typeof performance != "undefined") { + LiteGraph.getTime = performance.now.bind(performance); + } else if (typeof Date != "undefined" && Date.now) { + LiteGraph.getTime = Date.now.bind(Date); + } else if (typeof process != "undefined") { + LiteGraph.getTime = function() { + var t = process.hrtime(); + return t[0] * 0.001 + t[1] * 1e-6; + }; + } else { + LiteGraph.getTime = function getTime() { + return new Date().getTime(); + }; + } + + //********************************************************************************* + // LGraph CLASS + //********************************************************************************* + + /** + * LGraph is the class that contain a full graph. We instantiate one and add nodes to it, and then we can run the execution loop. + * + * @class LGraph + * @constructor + * @param {Object} o data from previous serialization [optional] + */ + + function LGraph(o) { + if (LiteGraph.debug) { + console.log("Graph created"); + } + this.list_of_graphcanvas = null; + this.clear(); + + if (o) { + this.configure(o); + } + } + + global.LGraph = LiteGraph.LGraph = LGraph; + + //default supported types + LGraph.supported_types = ["number", "string", "boolean"]; + + //used to know which types of connections support this graph (some graphs do not allow certain types) + LGraph.prototype.getSupportedTypes = function() { + return this.supported_types || LGraph.supported_types; + }; + + LGraph.STATUS_STOPPED = 1; + LGraph.STATUS_RUNNING = 2; + + /** + * Removes all nodes from this graph + * @method clear + */ + + LGraph.prototype.clear = function() { + this.stop(); + this.status = LGraph.STATUS_STOPPED; + + this.last_node_id = 0; + this.last_link_id = 0; + + this._version = -1; //used to detect changes + + //safe clear + if (this._nodes) { + for (var i = 0; i < this._nodes.length; ++i) { + var node = this._nodes[i]; + if (node.onRemoved) { + node.onRemoved(); + } + } + } + + //nodes + this._nodes = []; + this._nodes_by_id = {}; + this._nodes_in_order = []; //nodes that are executable sorted in execution order + this._nodes_executable = null; //nodes that contain onExecute + + //other scene stuff + this._groups = []; + + //links + this.links = {}; //container with all the links + + //iterations + this.iteration = 0; + + //custom data + this.config = {}; + this.vars = {}; + + //timing + this.globaltime = 0; + this.runningtime = 0; + this.fixedtime = 0; + this.fixedtime_lapse = 0.01; + this.elapsed_time = 0.01; + this.last_update_time = 0; + this.starttime = 0; + + this.catch_errors = true; + + //subgraph_data + this.inputs = {}; + this.outputs = {}; + + //notify canvas to redraw + this.change(); + + this.sendActionToCanvas("clear"); + }; + + /** + * Attach Canvas to this graph + * @method attachCanvas + * @param {GraphCanvas} graph_canvas + */ + + LGraph.prototype.attachCanvas = function(graphcanvas) { + if (graphcanvas.constructor != LGraphCanvas) { + throw "attachCanvas expects a LGraphCanvas instance"; + } + if (graphcanvas.graph && graphcanvas.graph != this) { + graphcanvas.graph.detachCanvas(graphcanvas); + } + + graphcanvas.graph = this; + if (!this.list_of_graphcanvas) { + this.list_of_graphcanvas = []; + } + this.list_of_graphcanvas.push(graphcanvas); + }; + + /** + * Detach Canvas from this graph + * @method detachCanvas + * @param {GraphCanvas} graph_canvas + */ + LGraph.prototype.detachCanvas = function(graphcanvas) { + if (!this.list_of_graphcanvas) { + return; + } + + var pos = this.list_of_graphcanvas.indexOf(graphcanvas); + if (pos == -1) { + return; + } + graphcanvas.graph = null; + this.list_of_graphcanvas.splice(pos, 1); + }; + + /** + * Starts running this graph every interval milliseconds. + * @method start + * @param {number} interval amount of milliseconds between executions, if 0 then it renders to the monitor refresh rate + */ + + LGraph.prototype.start = function(interval) { + if (this.status == LGraph.STATUS_RUNNING) { + return; + } + this.status = LGraph.STATUS_RUNNING; + + if (this.onPlayEvent) { + this.onPlayEvent(); + } + + this.sendEventToAllNodes("onStart"); + + //launch + this.starttime = LiteGraph.getTime(); + this.last_update_time = this.starttime; + interval = interval || 0; + var that = this; + + //execute once per frame + if ( interval == 0 && typeof window != "undefined" && window.requestAnimationFrame ) { + function on_frame() { + if (that.execution_timer_id != -1) { + return; + } + window.requestAnimationFrame(on_frame); + if(that.onBeforeStep) + that.onBeforeStep(); + that.runStep(1, !that.catch_errors); + if(that.onAfterStep) + that.onAfterStep(); + } + this.execution_timer_id = -1; + on_frame(); + } else { //execute every 'interval' ms + this.execution_timer_id = setInterval(function() { + //execute + if(that.onBeforeStep) + that.onBeforeStep(); + that.runStep(1, !that.catch_errors); + if(that.onAfterStep) + that.onAfterStep(); + }, interval); + } + }; + + /** + * Stops the execution loop of the graph + * @method stop execution + */ + + LGraph.prototype.stop = function() { + if (this.status == LGraph.STATUS_STOPPED) { + return; + } + + this.status = LGraph.STATUS_STOPPED; + + if (this.onStopEvent) { + this.onStopEvent(); + } + + if (this.execution_timer_id != null) { + if (this.execution_timer_id != -1) { + clearInterval(this.execution_timer_id); + } + this.execution_timer_id = null; + } + + this.sendEventToAllNodes("onStop"); + }; + + /** + * Run N steps (cycles) of the graph + * @method runStep + * @param {number} num number of steps to run, default is 1 + * @param {Boolean} do_not_catch_errors [optional] if you want to try/catch errors + * @param {number} limit max number of nodes to execute (used to execute from start to a node) + */ + + LGraph.prototype.runStep = function(num, do_not_catch_errors, limit ) { + num = num || 1; + + var start = LiteGraph.getTime(); + this.globaltime = 0.001 * (start - this.starttime); + + var nodes = this._nodes_executable + ? this._nodes_executable + : this._nodes; + if (!nodes) { + return; + } + + limit = limit || nodes.length; + + if (do_not_catch_errors) { + //iterations + for (var i = 0; i < num; i++) { + for (var j = 0; j < limit; ++j) { + var node = nodes[j]; + if (node.mode == LiteGraph.ALWAYS && node.onExecute) { + node.onExecute(); //hard to send elapsed time + } + } + + this.fixedtime += this.fixedtime_lapse; + if (this.onExecuteStep) { + this.onExecuteStep(); + } + } + + if (this.onAfterExecute) { + this.onAfterExecute(); + } + } else { + try { + //iterations + for (var i = 0; i < num; i++) { + for (var j = 0; j < limit; ++j) { + var node = nodes[j]; + if (node.mode == LiteGraph.ALWAYS && node.onExecute) { + node.onExecute(); + } + } + + this.fixedtime += this.fixedtime_lapse; + if (this.onExecuteStep) { + this.onExecuteStep(); + } + } + + if (this.onAfterExecute) { + this.onAfterExecute(); + } + this.errors_in_execution = false; + } catch (err) { + this.errors_in_execution = true; + if (LiteGraph.throw_errors) { + throw err; + } + if (LiteGraph.debug) { + console.log("Error during execution: " + err); + } + this.stop(); + } + } + + var now = LiteGraph.getTime(); + var elapsed = now - start; + if (elapsed == 0) { + elapsed = 1; + } + this.execution_time = 0.001 * elapsed; + this.globaltime += 0.001 * elapsed; + this.iteration += 1; + this.elapsed_time = (now - this.last_update_time) * 0.001; + this.last_update_time = now; + }; + + /** + * Updates the graph execution order according to relevance of the nodes (nodes with only outputs have more relevance than + * nodes with only inputs. + * @method updateExecutionOrder + */ + LGraph.prototype.updateExecutionOrder = function() { + this._nodes_in_order = this.computeExecutionOrder(false); + this._nodes_executable = []; + for (var i = 0; i < this._nodes_in_order.length; ++i) { + if (this._nodes_in_order[i].onExecute) { + this._nodes_executable.push(this._nodes_in_order[i]); + } + } + }; + + //This is more internal, it computes the executable nodes in order and returns it + LGraph.prototype.computeExecutionOrder = function( + only_onExecute, + set_level + ) { + var L = []; + var S = []; + var M = {}; + var visited_links = {}; //to avoid repeating links + var remaining_links = {}; //to a + + //search for the nodes without inputs (starting nodes) + for (var i = 0, l = this._nodes.length; i < l; ++i) { + var node = this._nodes[i]; + if (only_onExecute && !node.onExecute) { + continue; + } + + M[node.id] = node; //add to pending nodes + + var num = 0; //num of input connections + if (node.inputs) { + for (var j = 0, l2 = node.inputs.length; j < l2; j++) { + if (node.inputs[j] && node.inputs[j].link != null) { + num += 1; + } + } + } + + if (num == 0) { + //is a starting node + S.push(node); + if (set_level) { + node._level = 1; + } + } //num of input links + else { + if (set_level) { + node._level = 0; + } + remaining_links[node.id] = num; + } + } + + while (true) { + if (S.length == 0) { + break; + } + + //get an starting node + var node = S.shift(); + L.push(node); //add to ordered list + delete M[node.id]; //remove from the pending nodes + + if (!node.outputs) { + continue; + } + + //for every output + for (var i = 0; i < node.outputs.length; i++) { + var output = node.outputs[i]; + //not connected + if ( + output == null || + output.links == null || + output.links.length == 0 + ) { + continue; + } + + //for every connection + for (var j = 0; j < output.links.length; j++) { + var link_id = output.links[j]; + var link = this.links[link_id]; + if (!link) { + continue; + } + + //already visited link (ignore it) + if (visited_links[link.id]) { + continue; + } + + var target_node = this.getNodeById(link.target_id); + if (target_node == null) { + visited_links[link.id] = true; + continue; + } + + if ( + set_level && + (!target_node._level || + target_node._level <= node._level) + ) { + target_node._level = node._level + 1; + } + + visited_links[link.id] = true; //mark as visited + remaining_links[target_node.id] -= 1; //reduce the number of links remaining + if (remaining_links[target_node.id] == 0) { + S.push(target_node); + } //if no more links, then add to starters array + } + } + } + + //the remaining ones (loops) + for (var i in M) { + L.push(M[i]); + } + + if (L.length != this._nodes.length && LiteGraph.debug) { + console.warn("something went wrong, nodes missing"); + } + + var l = L.length; + + //save order number in the node + for (var i = 0; i < l; ++i) { + L[i].order = i; + } + + //sort now by priority + L = L.sort(function(A, B) { + var Ap = A.constructor.priority || A.priority || 0; + var Bp = B.constructor.priority || B.priority || 0; + if (Ap == Bp) { + //if same priority, sort by order + return A.order - B.order; + } + return Ap - Bp; //sort by priority + }); + + //save order number in the node, again... + for (var i = 0; i < l; ++i) { + L[i].order = i; + } + + return L; + }; + + /** + * Returns all the nodes that could affect this one (ancestors) by crawling all the inputs recursively. + * It doesn't include the node itself + * @method getAncestors + * @return {Array} an array with all the LGraphNodes that affect this node, in order of execution + */ + LGraph.prototype.getAncestors = function(node) { + var ancestors = []; + var pending = [node]; + var visited = {}; + + while (pending.length) { + var current = pending.shift(); + if (!current.inputs) { + continue; + } + if (!visited[current.id] && current != node) { + visited[current.id] = true; + ancestors.push(current); + } + + for (var i = 0; i < current.inputs.length; ++i) { + var input = current.getInputNode(i); + if (input && ancestors.indexOf(input) == -1) { + pending.push(input); + } + } + } + + ancestors.sort(function(a, b) { + return a.order - b.order; + }); + return ancestors; + }; + + /** + * Positions every node in a more readable manner + * @method arrange + */ + LGraph.prototype.arrange = function(margin) { + margin = margin || 100; + + var nodes = this.computeExecutionOrder(false, true); + var columns = []; + for (var i = 0; i < nodes.length; ++i) { + var node = nodes[i]; + var col = node._level || 1; + if (!columns[col]) { + columns[col] = []; + } + columns[col].push(node); + } + + var x = margin; + + for (var i = 0; i < columns.length; ++i) { + var column = columns[i]; + if (!column) { + continue; + } + var max_size = 100; + var y = margin + LiteGraph.NODE_TITLE_HEIGHT; + for (var j = 0; j < column.length; ++j) { + var node = column[j]; + node.pos[0] = x; + node.pos[1] = y; + if (node.size[0] > max_size) { + max_size = node.size[0]; + } + y += node.size[1] + margin + LiteGraph.NODE_TITLE_HEIGHT; + } + x += max_size + margin; + } + + this.setDirtyCanvas(true, true); + }; + + /** + * Returns the amount of time the graph has been running in milliseconds + * @method getTime + * @return {number} number of milliseconds the graph has been running + */ + LGraph.prototype.getTime = function() { + return this.globaltime; + }; + + /** + * Returns the amount of time accumulated using the fixedtime_lapse var. This is used in context where the time increments should be constant + * @method getFixedTime + * @return {number} number of milliseconds the graph has been running + */ + + LGraph.prototype.getFixedTime = function() { + return this.fixedtime; + }; + + /** + * Returns the amount of time it took to compute the latest iteration. Take into account that this number could be not correct + * if the nodes are using graphical actions + * @method getElapsedTime + * @return {number} number of milliseconds it took the last cycle + */ + + LGraph.prototype.getElapsedTime = function() { + return this.elapsed_time; + }; + + /** + * Sends an event to all the nodes, useful to trigger stuff + * @method sendEventToAllNodes + * @param {String} eventname the name of the event (function to be called) + * @param {Array} params parameters in array format + */ + LGraph.prototype.sendEventToAllNodes = function(eventname, params, mode) { + mode = mode || LiteGraph.ALWAYS; + + var nodes = this._nodes_in_order ? this._nodes_in_order : this._nodes; + if (!nodes) { + return; + } + + for (var j = 0, l = nodes.length; j < l; ++j) { + var node = nodes[j]; + + if ( + node.constructor === LiteGraph.Subgraph && + eventname != "onExecute" + ) { + if (node.mode == mode) { + node.sendEventToAllNodes(eventname, params, mode); + } + continue; + } + + if (!node[eventname] || node.mode != mode) { + continue; + } + if (params === undefined) { + node[eventname](); + } else if (params && params.constructor === Array) { + node[eventname].apply(node, params); + } else { + node[eventname](params); + } + } + }; + + LGraph.prototype.sendActionToCanvas = function(action, params) { + if (!this.list_of_graphcanvas) { + return; + } + + for (var i = 0; i < this.list_of_graphcanvas.length; ++i) { + var c = this.list_of_graphcanvas[i]; + if (c[action]) { + c[action].apply(c, params); + } + } + }; + + /** + * Adds a new node instance to this graph + * @method add + * @param {LGraphNode} node the instance of the node + */ + + LGraph.prototype.add = function(node, skip_compute_order) { + if (!node) { + return; + } + + //groups + if (node.constructor === LGraphGroup) { + this._groups.push(node); + this.setDirtyCanvas(true); + this.change(); + node.graph = this; + this._version++; + return; + } + + //nodes + if (node.id != -1 && this._nodes_by_id[node.id] != null) { + console.warn( + "LiteGraph: there is already a node with this ID, changing it" + ); + node.id = ++this.last_node_id; + } + + if (this._nodes.length >= LiteGraph.MAX_NUMBER_OF_NODES) { + throw "LiteGraph: max number of nodes in a graph reached"; + } + + //give him an id + if (node.id == null || node.id == -1) { + node.id = ++this.last_node_id; + } else if (this.last_node_id < node.id) { + this.last_node_id = node.id; + } + + node.graph = this; + this._version++; + + this._nodes.push(node); + this._nodes_by_id[node.id] = node; + + if (node.onAdded) { + node.onAdded(this); + } + + if (this.config.align_to_grid) { + node.alignToGrid(); + } + + if (!skip_compute_order) { + this.updateExecutionOrder(); + } + + if (this.onNodeAdded) { + this.onNodeAdded(node); + } + + this.setDirtyCanvas(true); + this.change(); + + return node; //to chain actions + }; + + /** + * Removes a node from the graph + * @method remove + * @param {LGraphNode} node the instance of the node + */ + + LGraph.prototype.remove = function(node) { + if (node.constructor === LiteGraph.LGraphGroup) { + var index = this._groups.indexOf(node); + if (index != -1) { + this._groups.splice(index, 1); + } + node.graph = null; + this._version++; + this.setDirtyCanvas(true, true); + this.change(); + return; + } + + if (this._nodes_by_id[node.id] == null) { + return; + } //not found + + if (node.ignore_remove) { + return; + } //cannot be removed + + //disconnect inputs + if (node.inputs) { + for (var i = 0; i < node.inputs.length; i++) { + var slot = node.inputs[i]; + if (slot.link != null) { + node.disconnectInput(i); + } + } + } + + //disconnect outputs + if (node.outputs) { + for (var i = 0; i < node.outputs.length; i++) { + var slot = node.outputs[i]; + if (slot.links != null && slot.links.length) { + node.disconnectOutput(i); + } + } + } + + //node.id = -1; //why? + + //callback + if (node.onRemoved) { + node.onRemoved(); + } + + node.graph = null; + this._version++; + + //remove from canvas render + if (this.list_of_graphcanvas) { + for (var i = 0; i < this.list_of_graphcanvas.length; ++i) { + var canvas = this.list_of_graphcanvas[i]; + if (canvas.selected_nodes[node.id]) { + delete canvas.selected_nodes[node.id]; + } + if (canvas.node_dragged == node) { + canvas.node_dragged = null; + } + } + } + + //remove from containers + var pos = this._nodes.indexOf(node); + if (pos != -1) { + this._nodes.splice(pos, 1); + } + delete this._nodes_by_id[node.id]; + + if (this.onNodeRemoved) { + this.onNodeRemoved(node); + } + + this.setDirtyCanvas(true, true); + this.change(); + + this.updateExecutionOrder(); + }; + + /** + * Returns a node by its id. + * @method getNodeById + * @param {Number} id + */ + + LGraph.prototype.getNodeById = function(id) { + if (id == null) { + return null; + } + return this._nodes_by_id[id]; + }; + + /** + * Returns a list of nodes that matches a class + * @method findNodesByClass + * @param {Class} classObject the class itself (not an string) + * @return {Array} a list with all the nodes of this type + */ + LGraph.prototype.findNodesByClass = function(classObject, result) { + result = result || []; + result.length = 0; + for (var i = 0, l = this._nodes.length; i < l; ++i) { + if (this._nodes[i].constructor === classObject) { + result.push(this._nodes[i]); + } + } + return result; + }; + + /** + * Returns a list of nodes that matches a type + * @method findNodesByType + * @param {String} type the name of the node type + * @return {Array} a list with all the nodes of this type + */ + LGraph.prototype.findNodesByType = function(type, result) { + var type = type.toLowerCase(); + result = result || []; + result.length = 0; + for (var i = 0, l = this._nodes.length; i < l; ++i) { + if (this._nodes[i].type.toLowerCase() == type) { + result.push(this._nodes[i]); + } + } + return result; + }; + + /** + * Returns the first node that matches a name in its title + * @method findNodeByTitle + * @param {String} name the name of the node to search + * @return {Node} the node or null + */ + LGraph.prototype.findNodeByTitle = function(title) { + for (var i = 0, l = this._nodes.length; i < l; ++i) { + if (this._nodes[i].title == title) { + return this._nodes[i]; + } + } + return null; + }; + + /** + * Returns a list of nodes that matches a name + * @method findNodesByTitle + * @param {String} name the name of the node to search + * @return {Array} a list with all the nodes with this name + */ + LGraph.prototype.findNodesByTitle = function(title) { + var result = []; + for (var i = 0, l = this._nodes.length; i < l; ++i) { + if (this._nodes[i].title == title) { + result.push(this._nodes[i]); + } + } + return result; + }; + + /** + * Returns the top-most node in this position of the canvas + * @method getNodeOnPos + * @param {number} x the x coordinate in canvas space + * @param {number} y the y coordinate in canvas space + * @param {Array} nodes_list a list with all the nodes to search from, by default is all the nodes in the graph + * @return {LGraphNode} the node at this position or null + */ + LGraph.prototype.getNodeOnPos = function(x, y, nodes_list, margin) { + nodes_list = nodes_list || this._nodes; + for (var i = nodes_list.length - 1; i >= 0; i--) { + var n = nodes_list[i]; + if (n.isPointInside(x, y, margin)) { + return n; + } + } + return null; + }; + + /** + * Returns the top-most group in that position + * @method getGroupOnPos + * @param {number} x the x coordinate in canvas space + * @param {number} y the y coordinate in canvas space + * @return {LGraphGroup} the group or null + */ + LGraph.prototype.getGroupOnPos = function(x, y) { + for (var i = this._groups.length - 1; i >= 0; i--) { + var g = this._groups[i]; + if (g.isPointInside(x, y, 2, true)) { + return g; + } + } + return null; + }; + + /** + * Checks that the node type matches the node type registered, used when replacing a nodetype by a newer version during execution + * this replaces the ones using the old version with the new version + * @method checkNodeTypes + */ + LGraph.prototype.checkNodeTypes = function() { + var changes = false; + for (var i = 0; i < this._nodes.length; i++) { + var node = this._nodes[i]; + var ctor = LiteGraph.registered_node_types[node.type]; + if (node.constructor == ctor) { + continue; + } + console.log("node being replaced by newer version: " + node.type); + var newnode = LiteGraph.createNode(node.type); + changes = true; + this._nodes[i] = newnode; + newnode.configure(node.serialize()); + newnode.graph = this; + this._nodes_by_id[newnode.id] = newnode; + if (node.inputs) { + newnode.inputs = node.inputs.concat(); + } + if (node.outputs) { + newnode.outputs = node.outputs.concat(); + } + } + this.updateExecutionOrder(); + }; + + // ********** GLOBALS ***************** + + LGraph.prototype.onAction = function(action, param) { + this._input_nodes = this.findNodesByClass( + LiteGraph.GraphInput, + this._input_nodes + ); + for (var i = 0; i < this._input_nodes.length; ++i) { + var node = this._input_nodes[i]; + if (node.properties.name != action) { + continue; + } + node.onAction(action, param); + break; + } + }; + + LGraph.prototype.trigger = function(action, param) { + if (this.onTrigger) { + this.onTrigger(action, param); + } + }; + + /** + * Tell this graph it has a global graph input of this type + * @method addGlobalInput + * @param {String} name + * @param {String} type + * @param {*} value [optional] + */ + LGraph.prototype.addInput = function(name, type, value) { + var input = this.inputs[name]; + if (input) { + //already exist + return; + } + + this.inputs[name] = { name: name, type: type, value: value }; + this._version++; + + if (this.onInputAdded) { + this.onInputAdded(name, type); + } + + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + }; + + /** + * Assign a data to the global graph input + * @method setGlobalInputData + * @param {String} name + * @param {*} data + */ + LGraph.prototype.setInputData = function(name, data) { + var input = this.inputs[name]; + if (!input) { + return; + } + input.value = data; + }; + + /** + * Returns the current value of a global graph input + * @method getInputData + * @param {String} name + * @return {*} the data + */ + LGraph.prototype.getInputData = function(name) { + var input = this.inputs[name]; + if (!input) { + return null; + } + return input.value; + }; + + /** + * Changes the name of a global graph input + * @method renameInput + * @param {String} old_name + * @param {String} new_name + */ + LGraph.prototype.renameInput = function(old_name, name) { + if (name == old_name) { + return; + } + + if (!this.inputs[old_name]) { + return false; + } + + if (this.inputs[name]) { + console.error("there is already one input with that name"); + return false; + } + + this.inputs[name] = this.inputs[old_name]; + delete this.inputs[old_name]; + this._version++; + + if (this.onInputRenamed) { + this.onInputRenamed(old_name, name); + } + + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + }; + + /** + * Changes the type of a global graph input + * @method changeInputType + * @param {String} name + * @param {String} type + */ + LGraph.prototype.changeInputType = function(name, type) { + if (!this.inputs[name]) { + return false; + } + + if ( + this.inputs[name].type && + String(this.inputs[name].type).toLowerCase() == + String(type).toLowerCase() + ) { + return; + } + + this.inputs[name].type = type; + this._version++; + if (this.onInputTypeChanged) { + this.onInputTypeChanged(name, type); + } + }; + + /** + * Removes a global graph input + * @method removeInput + * @param {String} name + * @param {String} type + */ + LGraph.prototype.removeInput = function(name) { + if (!this.inputs[name]) { + return false; + } + + delete this.inputs[name]; + this._version++; + + if (this.onInputRemoved) { + this.onInputRemoved(name); + } + + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + return true; + }; + + /** + * Creates a global graph output + * @method addOutput + * @param {String} name + * @param {String} type + * @param {*} value + */ + LGraph.prototype.addOutput = function(name, type, value) { + this.outputs[name] = { name: name, type: type, value: value }; + this._version++; + + if (this.onOutputAdded) { + this.onOutputAdded(name, type); + } + + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + }; + + /** + * Assign a data to the global output + * @method setOutputData + * @param {String} name + * @param {String} value + */ + LGraph.prototype.setOutputData = function(name, value) { + var output = this.outputs[name]; + if (!output) { + return; + } + output.value = value; + }; + + /** + * Returns the current value of a global graph output + * @method getOutputData + * @param {String} name + * @return {*} the data + */ + LGraph.prototype.getOutputData = function(name) { + var output = this.outputs[name]; + if (!output) { + return null; + } + return output.value; + }; + + /** + * Renames a global graph output + * @method renameOutput + * @param {String} old_name + * @param {String} new_name + */ + LGraph.prototype.renameOutput = function(old_name, name) { + if (!this.outputs[old_name]) { + return false; + } + + if (this.outputs[name]) { + console.error("there is already one output with that name"); + return false; + } + + this.outputs[name] = this.outputs[old_name]; + delete this.outputs[old_name]; + this._version++; + + if (this.onOutputRenamed) { + this.onOutputRenamed(old_name, name); + } + + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + }; + + /** + * Changes the type of a global graph output + * @method changeOutputType + * @param {String} name + * @param {String} type + */ + LGraph.prototype.changeOutputType = function(name, type) { + if (!this.outputs[name]) { + return false; + } + + if ( + this.outputs[name].type && + String(this.outputs[name].type).toLowerCase() == + String(type).toLowerCase() + ) { + return; + } + + this.outputs[name].type = type; + this._version++; + if (this.onOutputTypeChanged) { + this.onOutputTypeChanged(name, type); + } + }; + + /** + * Removes a global graph output + * @method removeOutput + * @param {String} name + */ + LGraph.prototype.removeOutput = function(name) { + if (!this.outputs[name]) { + return false; + } + delete this.outputs[name]; + this._version++; + + if (this.onOutputRemoved) { + this.onOutputRemoved(name); + } + + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + return true; + }; + + LGraph.prototype.triggerInput = function(name, value) { + var nodes = this.findNodesByTitle(name); + for (var i = 0; i < nodes.length; ++i) { + nodes[i].onTrigger(value); + } + }; + + LGraph.prototype.setCallback = function(name, func) { + var nodes = this.findNodesByTitle(name); + for (var i = 0; i < nodes.length; ++i) { + nodes[i].setTrigger(func); + } + }; + + LGraph.prototype.connectionChange = function(node, link_info) { + this.updateExecutionOrder(); + if (this.onConnectionChange) { + this.onConnectionChange(node); + } + this._version++; + this.sendActionToCanvas("onConnectionChange"); + }; + + /** + * returns if the graph is in live mode + * @method isLive + */ + + LGraph.prototype.isLive = function() { + if (!this.list_of_graphcanvas) { + return false; + } + + for (var i = 0; i < this.list_of_graphcanvas.length; ++i) { + var c = this.list_of_graphcanvas[i]; + if (c.live_mode) { + return true; + } + } + return false; + }; + + /** + * clears the triggered slot animation in all links (stop visual animation) + * @method clearTriggeredSlots + */ + LGraph.prototype.clearTriggeredSlots = function() { + for (var i in this.links) { + var link_info = this.links[i]; + if (!link_info) { + continue; + } + if (link_info._last_time) { + link_info._last_time = 0; + } + } + }; + + /* Called when something visually changed (not the graph!) */ + LGraph.prototype.change = function() { + if (LiteGraph.debug) { + console.log("Graph changed"); + } + this.sendActionToCanvas("setDirty", [true, true]); + if (this.on_change) { + this.on_change(this); + } + }; + + LGraph.prototype.setDirtyCanvas = function(fg, bg) { + this.sendActionToCanvas("setDirty", [fg, bg]); + }; + + /** + * Destroys a link + * @method removeLink + * @param {Number} link_id + */ + LGraph.prototype.removeLink = function(link_id) { + var link = this.links[link_id]; + if (!link) { + return; + } + var node = this.getNodeById(link.target_id); + if (node) { + node.disconnectInput(link.target_slot); + } + }; + + //save and recover app state *************************************** + /** + * Creates a Object containing all the info about this graph, it can be serialized + * @method serialize + * @return {Object} value of the node + */ + LGraph.prototype.serialize = function() { + var nodes_info = []; + for (var i = 0, l = this._nodes.length; i < l; ++i) { + nodes_info.push(this._nodes[i].serialize()); + } + + //pack link info into a non-verbose format + var links = []; + for (var i in this.links) { + //links is an OBJECT + var link = this.links[i]; + if (!link.serialize) { + //weird bug I havent solved yet + console.warn( + "weird LLink bug, link info is not a LLink but a regular object" + ); + var link2 = new LLink(); + for (var i in link) { + link2[i] = link[i]; + } + this.links[i] = link2; + link = link2; + } + + links.push(link.serialize()); + } + + var groups_info = []; + for (var i = 0; i < this._groups.length; ++i) { + groups_info.push(this._groups[i].serialize()); + } + + var data = { + last_node_id: this.last_node_id, + last_link_id: this.last_link_id, + nodes: nodes_info, + links: links, + groups: groups_info, + config: this.config, + version: LiteGraph.VERSION + }; + + return data; + }; + + /** + * Configure a graph from a JSON string + * @method configure + * @param {String} str configure a graph from a JSON string + * @param {Boolean} returns if there was any error parsing + */ + LGraph.prototype.configure = function(data, keep_old) { + if (!data) { + return; + } + + if (!keep_old) { + this.clear(); + } + + var nodes = data.nodes; + + //decode links info (they are very verbose) + if (data.links && data.links.constructor === Array) { + var links = []; + for (var i = 0; i < data.links.length; ++i) { + var link_data = data.links[i]; + if(!link_data) //weird bug + { + console.warn("serialized graph link data contains errors, skipping."); + continue; + } + var link = new LLink(); + link.configure(link_data); + links[link.id] = link; + } + data.links = links; + } + + //copy all stored fields + for (var i in data) { + if(i == "nodes" || i == "groups" ) //links must be accepted + continue; + this[i] = data[i]; + } + + var error = false; + + //create nodes + this._nodes = []; + if (nodes) { + for (var i = 0, l = nodes.length; i < l; ++i) { + var n_info = nodes[i]; //stored info + var node = LiteGraph.createNode(n_info.type, n_info.title); + if (!node) { + if (LiteGraph.debug) { + console.log( + "Node not found or has errors: " + n_info.type + ); + } + + //in case of error we create a replacement node to avoid losing info + node = new LGraphNode(); + node.last_serialization = n_info; + node.has_errors = true; + error = true; + //continue; + } + + node.id = n_info.id; //id it or it will create a new id + this.add(node, true); //add before configure, otherwise configure cannot create links + } + + //configure nodes afterwards so they can reach each other + for (var i = 0, l = nodes.length; i < l; ++i) { + var n_info = nodes[i]; + var node = this.getNodeById(n_info.id); + if (node) { + node.configure(n_info); + } + } + } + + //groups + this._groups.length = 0; + if (data.groups) { + for (var i = 0; i < data.groups.length; ++i) { + var group = new LiteGraph.LGraphGroup(); + group.configure(data.groups[i]); + this.add(group); + } + } + + this.updateExecutionOrder(); + this._version++; + this.setDirtyCanvas(true, true); + return error; + }; + + LGraph.prototype.load = function(url) { + var that = this; + var req = new XMLHttpRequest(); + req.open("GET", url, true); + req.send(null); + req.onload = function(oEvent) { + if (req.status !== 200) { + console.error("Error loading graph:", req.status, req.response); + return; + } + var data = JSON.parse(req.response); + that.configure(data); + }; + req.onerror = function(err) { + console.error("Error loading graph:", err); + }; + }; + + LGraph.prototype.onNodeTrace = function(node, msg, color) { + //TODO + }; + + //this is the class in charge of storing link information + function LLink(id, type, origin_id, origin_slot, target_id, target_slot) { + this.id = id; + this.type = type; + this.origin_id = origin_id; + this.origin_slot = origin_slot; + this.target_id = target_id; + this.target_slot = target_slot; + + this._data = null; + this._pos = new Float32Array(2); //center + } + + LLink.prototype.configure = function(o) { + if (o.constructor === Array) { + this.id = o[0]; + this.origin_id = o[1]; + this.origin_slot = o[2]; + this.target_id = o[3]; + this.target_slot = o[4]; + this.type = o[5]; + } else { + this.id = o.id; + this.type = o.type; + this.origin_id = o.origin_id; + this.origin_slot = o.origin_slot; + this.target_id = o.target_id; + this.target_slot = o.target_slot; + } + }; + + LLink.prototype.serialize = function() { + return [ + this.id, + this.origin_id, + this.origin_slot, + this.target_id, + this.target_slot, + this.type + ]; + }; + + LiteGraph.LLink = LLink; + + // ************************************************************* + // Node CLASS ******* + // ************************************************************* + + /* + title: string + pos: [x,y] + size: [x,y] + + input|output: every connection + + { name:string, type:string, pos: [x,y]=Optional, direction: "input"|"output", links: Array }); + + general properties: + + clip_area: if you render outside the node, it will be clipped + + unsafe_execution: not allowed for safe execution + + skip_repeated_outputs: when adding new outputs, it wont show if there is one already connected + + resizable: if set to false it wont be resizable with the mouse + + horizontal: slots are distributed horizontally + + widgets_start_y: widgets start at y distance from the top of the node + + flags object: + + collapsed: if it is collapsed + + supported callbacks: + + onAdded: when added to graph (warning: this is called BEFORE the node is configured when loading) + + onRemoved: when removed from graph + + onStart: when the graph starts playing + + onStop: when the graph stops playing + + onDrawForeground: render the inside widgets inside the node + + onDrawBackground: render the background area inside the node (only in edit mode) + + onMouseDown + + onMouseMove + + onMouseUp + + onMouseEnter + + onMouseLeave + + onExecute: execute the node + + onPropertyChanged: when a property is changed in the panel (return true to skip default behaviour) + + onGetInputs: returns an array of possible inputs + + onGetOutputs: returns an array of possible outputs + + onBounding: in case this node has a bigger bounding than the node itself (the callback receives the bounding as [x,y,w,h]) + + onDblClick: double clicked in the node + + onInputDblClick: input slot double clicked (can be used to automatically create a node connected) + + onOutputDblClick: output slot double clicked (can be used to automatically create a node connected) + + onConfigure: called after the node has been configured + + onSerialize: to add extra info when serializing (the callback receives the object that should be filled with the data) + + onSelected + + onDeselected + + onDropItem : DOM item dropped over the node + + onDropFile : file dropped over the node + + onConnectInput : if returns false the incoming connection will be canceled + + onConnectionsChange : a connection changed (new one or removed) (LiteGraph.INPUT or LiteGraph.OUTPUT, slot, true if connected, link_info, input_info ) + + onAction: action slot triggered + + getExtraMenuOptions: to add option to context menu +*/ + + /** + * Base Class for all the node type classes + * @class LGraphNode + * @param {String} name a name for the node + */ + + function LGraphNode(title) { + this._ctor(title); + } + + global.LGraphNode = LiteGraph.LGraphNode = LGraphNode; + + LGraphNode.prototype._ctor = function(title) { + this.title = title || "Unnamed"; + this.size = [LiteGraph.NODE_WIDTH, 60]; + this.graph = null; + + this._pos = new Float32Array(10, 10); + + Object.defineProperty(this, "pos", { + set: function(v) { + if (!v || v.length < 2) { + return; + } + this._pos[0] = v[0]; + this._pos[1] = v[1]; + }, + get: function() { + return this._pos; + }, + enumerable: true + }); + + this.id = -1; //not know till not added + this.type = null; + + //inputs available: array of inputs + this.inputs = []; + this.outputs = []; + this.connections = []; + + //local data + this.properties = {}; //for the values + this.properties_info = []; //for the info + + this.flags = {}; + }; + + /** + * configure a node from an object containing the serialized info + * @method configure + */ + LGraphNode.prototype.configure = function(info) { + if (this.graph) { + this.graph._version++; + } + for (var j in info) { + if (j == "properties") { + //i don't want to clone properties, I want to reuse the old container + for (var k in info.properties) { + this.properties[k] = info.properties[k]; + if (this.onPropertyChanged) { + this.onPropertyChanged( k, info.properties[k] ); + } + } + continue; + } + + if (info[j] == null) { + continue; + } else if (typeof info[j] == "object") { + //object + if (this[j] && this[j].configure) { + this[j].configure(info[j]); + } else { + this[j] = LiteGraph.cloneObject(info[j], this[j]); + } + } //value + else { + this[j] = info[j]; + } + } + + if (!info.title) { + this.title = this.constructor.title; + } + + if (this.onConnectionsChange) { + if (this.inputs) { + for (var i = 0; i < this.inputs.length; ++i) { + var input = this.inputs[i]; + var link_info = this.graph + ? this.graph.links[input.link] + : null; + this.onConnectionsChange( + LiteGraph.INPUT, + i, + true, + link_info, + input + ); //link_info has been created now, so its updated + } + } + + if (this.outputs) { + for (var i = 0; i < this.outputs.length; ++i) { + var output = this.outputs[i]; + if (!output.links) { + continue; + } + for (var j = 0; j < output.links.length; ++j) { + var link_info = this.graph + ? this.graph.links[output.links[j]] + : null; + this.onConnectionsChange( + LiteGraph.OUTPUT, + i, + true, + link_info, + output + ); //link_info has been created now, so its updated + } + } + } + } + + if( this.widgets ) + { + for (var i = 0; i < this.widgets.length; ++i) + { + var w = this.widgets[i]; + if(!w) + continue; + if(w.options && w.options.property && this.properties[ w.options.property ]) + w.value = JSON.parse( JSON.stringify( this.properties[ w.options.property ] ) ); + } + if (info.widgets_values) { + for (var i = 0; i < info.widgets_values.length; ++i) { + if (this.widgets[i]) { + this.widgets[i].value = info.widgets_values[i]; + } + } + } + } + + if (this.onConfigure) { + this.onConfigure(info); + } + }; + + /** + * serialize the content + * @method serialize + */ + + LGraphNode.prototype.serialize = function() { + //create serialization object + var o = { + id: this.id, + type: this.type, + pos: this.pos, + size: this.size, + flags: LiteGraph.cloneObject(this.flags), + order: this.order, + mode: this.mode + }; + + //special case for when there were errors + if (this.constructor === LGraphNode && this.last_serialization) { + return this.last_serialization; + } + + if (this.inputs) { + o.inputs = this.inputs; + } + + if (this.outputs) { + //clear outputs last data (because data in connections is never serialized but stored inside the outputs info) + for (var i = 0; i < this.outputs.length; i++) { + delete this.outputs[i]._data; + } + o.outputs = this.outputs; + } + + if (this.title && this.title != this.constructor.title) { + o.title = this.title; + } + + if (this.properties) { + o.properties = LiteGraph.cloneObject(this.properties); + } + + if (this.widgets && this.serialize_widgets) { + o.widgets_values = []; + for (var i = 0; i < this.widgets.length; ++i) { + if(this.widgets[i]) + o.widgets_values[i] = this.widgets[i].value; + else + o.widgets_values[i] = null; + } + } + + if (!o.type) { + o.type = this.constructor.type; + } + + if (this.color) { + o.color = this.color; + } + if (this.bgcolor) { + o.bgcolor = this.bgcolor; + } + if (this.boxcolor) { + o.boxcolor = this.boxcolor; + } + if (this.shape) { + o.shape = this.shape; + } + + if (this.onSerialize) { + if (this.onSerialize(o)) { + console.warn( + "node onSerialize shouldnt return anything, data should be stored in the object pass in the first parameter" + ); + } + } + + return o; + }; + + /* Creates a clone of this node */ + LGraphNode.prototype.clone = function() { + var node = LiteGraph.createNode(this.type); + if (!node) { + return null; + } + + //we clone it because serialize returns shared containers + var data = LiteGraph.cloneObject(this.serialize()); + + //remove links + if (data.inputs) { + for (var i = 0; i < data.inputs.length; ++i) { + data.inputs[i].link = null; + } + } + + if (data.outputs) { + for (var i = 0; i < data.outputs.length; ++i) { + if (data.outputs[i].links) { + data.outputs[i].links.length = 0; + } + } + } + + delete data["id"]; + //remove links + node.configure(data); + + return node; + }; + + /** + * serialize and stringify + * @method toString + */ + + LGraphNode.prototype.toString = function() { + return JSON.stringify(this.serialize()); + }; + //LGraphNode.prototype.deserialize = function(info) {} //this cannot be done from within, must be done in LiteGraph + + /** + * get the title string + * @method getTitle + */ + + LGraphNode.prototype.getTitle = function() { + return this.title || this.constructor.title; + }; + + /** + * sets the value of a property + * @method setProperty + * @param {String} name + * @param {*} value + */ + LGraphNode.prototype.setProperty = function(name, value) { + if (!this.properties) { + this.properties = {}; + } + if( value === this.properties[name] ) + return; + var prev_value = this.properties[name]; + this.properties[name] = value; + if (this.onPropertyChanged) { + if( this.onPropertyChanged(name, value, prev_value) === false ) //abort change + this.properties[name] = prev_value; + } + if(this.widgets) //widgets could be linked to properties + for(var i = 0; i < this.widgets.length; ++i) + { + var w = this.widgets[i]; + if(!w) + continue; + if(w.options.property == name) + { + w.value = value; + break; + } + } + }; + + // Execution ************************* + /** + * sets the output data + * @method setOutputData + * @param {number} slot + * @param {*} data + */ + LGraphNode.prototype.setOutputData = function(slot, data) { + if (!this.outputs) { + return; + } + + //this maybe slow and a niche case + //if(slot && slot.constructor === String) + // slot = this.findOutputSlot(slot); + + if (slot == -1 || slot >= this.outputs.length) { + return; + } + + var output_info = this.outputs[slot]; + if (!output_info) { + return; + } + + //store data in the output itself in case we want to debug + output_info._data = data; + + //if there are connections, pass the data to the connections + if (this.outputs[slot].links) { + for (var i = 0; i < this.outputs[slot].links.length; i++) { + var link_id = this.outputs[slot].links[i]; + var link = this.graph.links[link_id]; + if(link) + link.data = data; + } + } + }; + + /** + * sets the output data type, useful when you want to be able to overwrite the data type + * @method setOutputDataType + * @param {number} slot + * @param {String} datatype + */ + LGraphNode.prototype.setOutputDataType = function(slot, type) { + if (!this.outputs) { + return; + } + if (slot == -1 || slot >= this.outputs.length) { + return; + } + var output_info = this.outputs[slot]; + if (!output_info) { + return; + } + //store data in the output itself in case we want to debug + output_info.type = type; + + //if there are connections, pass the data to the connections + if (this.outputs[slot].links) { + for (var i = 0; i < this.outputs[slot].links.length; i++) { + var link_id = this.outputs[slot].links[i]; + this.graph.links[link_id].type = type; + } + } + }; + + /** + * Retrieves the input data (data traveling through the connection) from one slot + * @method getInputData + * @param {number} slot + * @param {boolean} force_update if set to true it will force the connected node of this slot to output data into this link + * @return {*} data or if it is not connected returns undefined + */ + LGraphNode.prototype.getInputData = function(slot, force_update) { + if (!this.inputs) { + return; + } //undefined; + + if (slot >= this.inputs.length || this.inputs[slot].link == null) { + return; + } + + var link_id = this.inputs[slot].link; + var link = this.graph.links[link_id]; + if (!link) { + //bug: weird case but it happens sometimes + return null; + } + + if (!force_update) { + return link.data; + } + + //special case: used to extract data from the incoming connection before the graph has been executed + var node = this.graph.getNodeById(link.origin_id); + if (!node) { + return link.data; + } + + if (node.updateOutputData) { + node.updateOutputData(link.origin_slot); + } else if (node.onExecute) { + node.onExecute(); + } + + return link.data; + }; + + /** + * Retrieves the input data type (in case this supports multiple input types) + * @method getInputDataType + * @param {number} slot + * @return {String} datatype in string format + */ + LGraphNode.prototype.getInputDataType = function(slot) { + if (!this.inputs) { + return null; + } //undefined; + + if (slot >= this.inputs.length || this.inputs[slot].link == null) { + return null; + } + var link_id = this.inputs[slot].link; + var link = this.graph.links[link_id]; + if (!link) { + //bug: weird case but it happens sometimes + return null; + } + var node = this.graph.getNodeById(link.origin_id); + if (!node) { + return link.type; + } + var output_info = node.outputs[link.origin_slot]; + if (output_info) { + return output_info.type; + } + return null; + }; + + /** + * Retrieves the input data from one slot using its name instead of slot number + * @method getInputDataByName + * @param {String} slot_name + * @param {boolean} force_update if set to true it will force the connected node of this slot to output data into this link + * @return {*} data or if it is not connected returns null + */ + LGraphNode.prototype.getInputDataByName = function( + slot_name, + force_update + ) { + var slot = this.findInputSlot(slot_name); + if (slot == -1) { + return null; + } + return this.getInputData(slot, force_update); + }; + + /** + * tells you if there is a connection in one input slot + * @method isInputConnected + * @param {number} slot + * @return {boolean} + */ + LGraphNode.prototype.isInputConnected = function(slot) { + if (!this.inputs) { + return false; + } + return slot < this.inputs.length && this.inputs[slot].link != null; + }; + + /** + * tells you info about an input connection (which node, type, etc) + * @method getInputInfo + * @param {number} slot + * @return {Object} object or null { link: id, name: string, type: string or 0 } + */ + LGraphNode.prototype.getInputInfo = function(slot) { + if (!this.inputs) { + return null; + } + if (slot < this.inputs.length) { + return this.inputs[slot]; + } + return null; + }; + + /** + * returns the node connected in the input slot + * @method getInputNode + * @param {number} slot + * @return {LGraphNode} node or null + */ + LGraphNode.prototype.getInputNode = function(slot) { + if (!this.inputs) { + return null; + } + if (slot >= this.inputs.length) { + return null; + } + var input = this.inputs[slot]; + if (!input || input.link === null) { + return null; + } + var link_info = this.graph.links[input.link]; + if (!link_info) { + return null; + } + return this.graph.getNodeById(link_info.origin_id); + }; + + /** + * returns the value of an input with this name, otherwise checks if there is a property with that name + * @method getInputOrProperty + * @param {string} name + * @return {*} value + */ + LGraphNode.prototype.getInputOrProperty = function(name) { + if (!this.inputs || !this.inputs.length) { + return this.properties ? this.properties[name] : null; + } + + for (var i = 0, l = this.inputs.length; i < l; ++i) { + var input_info = this.inputs[i]; + if (name == input_info.name && input_info.link != null) { + var link = this.graph.links[input_info.link]; + if (link) { + return link.data; + } + } + } + return this.properties[name]; + }; + + /** + * tells you the last output data that went in that slot + * @method getOutputData + * @param {number} slot + * @return {Object} object or null + */ + LGraphNode.prototype.getOutputData = function(slot) { + if (!this.outputs) { + return null; + } + if (slot >= this.outputs.length) { + return null; + } + + var info = this.outputs[slot]; + return info._data; + }; + + /** + * tells you info about an output connection (which node, type, etc) + * @method getOutputInfo + * @param {number} slot + * @return {Object} object or null { name: string, type: string, links: [ ids of links in number ] } + */ + LGraphNode.prototype.getOutputInfo = function(slot) { + if (!this.outputs) { + return null; + } + if (slot < this.outputs.length) { + return this.outputs[slot]; + } + return null; + }; + + /** + * tells you if there is a connection in one output slot + * @method isOutputConnected + * @param {number} slot + * @return {boolean} + */ + LGraphNode.prototype.isOutputConnected = function(slot) { + if (!this.outputs) { + return false; + } + return ( + slot < this.outputs.length && + this.outputs[slot].links && + this.outputs[slot].links.length + ); + }; + + /** + * tells you if there is any connection in the output slots + * @method isAnyOutputConnected + * @return {boolean} + */ + LGraphNode.prototype.isAnyOutputConnected = function() { + if (!this.outputs) { + return false; + } + for (var i = 0; i < this.outputs.length; ++i) { + if (this.outputs[i].links && this.outputs[i].links.length) { + return true; + } + } + return false; + }; + + /** + * retrieves all the nodes connected to this output slot + * @method getOutputNodes + * @param {number} slot + * @return {array} + */ + LGraphNode.prototype.getOutputNodes = function(slot) { + if (!this.outputs || this.outputs.length == 0) { + return null; + } + + if (slot >= this.outputs.length) { + return null; + } + + var output = this.outputs[slot]; + if (!output.links || output.links.length == 0) { + return null; + } + + var r = []; + for (var i = 0; i < output.links.length; i++) { + var link_id = output.links[i]; + var link = this.graph.links[link_id]; + if (link) { + var target_node = this.graph.getNodeById(link.target_id); + if (target_node) { + r.push(target_node); + } + } + } + return r; + }; + + /** + * Triggers an event in this node, this will trigger any output with the same name + * @method trigger + * @param {String} event name ( "on_play", ... ) if action is equivalent to false then the event is send to all + * @param {*} param + */ + LGraphNode.prototype.trigger = function(action, param) { + if (!this.outputs || !this.outputs.length) { + return; + } + + if (this.graph) + this.graph._last_trigger_time = LiteGraph.getTime(); + + for (var i = 0; i < this.outputs.length; ++i) { + var output = this.outputs[i]; + if ( !output || output.type !== LiteGraph.EVENT || (action && output.name != action) ) + continue; + this.triggerSlot(i, param); + } + }; + + /** + * Triggers an slot event in this node + * @method triggerSlot + * @param {Number} slot the index of the output slot + * @param {*} param + * @param {Number} link_id [optional] in case you want to trigger and specific output link in a slot + */ + LGraphNode.prototype.triggerSlot = function(slot, param, link_id) { + if (!this.outputs) { + return; + } + + var output = this.outputs[slot]; + if (!output) { + return; + } + + var links = output.links; + if (!links || !links.length) { + return; + } + + if (this.graph) { + this.graph._last_trigger_time = LiteGraph.getTime(); + } + + //for every link attached here + for (var k = 0; k < links.length; ++k) { + var id = links[k]; + if (link_id != null && link_id != id) { + //to skip links + continue; + } + var link_info = this.graph.links[links[k]]; + if (!link_info) { + //not connected + continue; + } + link_info._last_time = LiteGraph.getTime(); + var node = this.graph.getNodeById(link_info.target_id); + if (!node) { + //node not found? + continue; + } + + //used to mark events in graph + var target_connection = node.inputs[link_info.target_slot]; + + if (node.onAction) { + node.onAction(target_connection.name, param); + } else if (node.mode === LiteGraph.ON_TRIGGER) { + if (node.onExecute) { + node.onExecute(param); + } + } + } + }; + + /** + * clears the trigger slot animation + * @method clearTriggeredSlot + * @param {Number} slot the index of the output slot + * @param {Number} link_id [optional] in case you want to trigger and specific output link in a slot + */ + LGraphNode.prototype.clearTriggeredSlot = function(slot, link_id) { + if (!this.outputs) { + return; + } + + var output = this.outputs[slot]; + if (!output) { + return; + } + + var links = output.links; + if (!links || !links.length) { + return; + } + + //for every link attached here + for (var k = 0; k < links.length; ++k) { + var id = links[k]; + if (link_id != null && link_id != id) { + //to skip links + continue; + } + var link_info = this.graph.links[links[k]]; + if (!link_info) { + //not connected + continue; + } + link_info._last_time = 0; + } + }; + + /** + * changes node size and triggers callback + * @method setSize + * @param {vec2} size + */ + LGraphNode.prototype.setSize = function(size) + { + this.size = size; + if(this.onResize) + this.onResize(this.size); + } + + /** + * add a new property to this node + * @method addProperty + * @param {string} name + * @param {*} default_value + * @param {string} type string defining the output type ("vec3","number",...) + * @param {Object} extra_info this can be used to have special properties of the property (like values, etc) + */ + LGraphNode.prototype.addProperty = function( + name, + default_value, + type, + extra_info + ) { + var o = { name: name, type: type, default_value: default_value }; + if (extra_info) { + for (var i in extra_info) { + o[i] = extra_info[i]; + } + } + if (!this.properties_info) { + this.properties_info = []; + } + this.properties_info.push(o); + if (!this.properties) { + this.properties = {}; + } + this.properties[name] = default_value; + return o; + }; + + //connections + + /** + * add a new output slot to use in this node + * @method addOutput + * @param {string} name + * @param {string} type string defining the output type ("vec3","number",...) + * @param {Object} extra_info this can be used to have special properties of an output (label, special color, position, etc) + */ + LGraphNode.prototype.addOutput = function(name, type, extra_info) { + var o = { name: name, type: type, links: null }; + if (extra_info) { + for (var i in extra_info) { + o[i] = extra_info[i]; + } + } + + if (!this.outputs) { + this.outputs = []; + } + this.outputs.push(o); + if (this.onOutputAdded) { + this.onOutputAdded(o); + } + this.setSize( this.computeSize() ); + this.setDirtyCanvas(true, true); + return o; + }; + + /** + * add a new output slot to use in this node + * @method addOutputs + * @param {Array} array of triplets like [[name,type,extra_info],[...]] + */ + LGraphNode.prototype.addOutputs = function(array) { + for (var i = 0; i < array.length; ++i) { + var info = array[i]; + var o = { name: info[0], type: info[1], link: null }; + if (array[2]) { + for (var j in info[2]) { + o[j] = info[2][j]; + } + } + + if (!this.outputs) { + this.outputs = []; + } + this.outputs.push(o); + if (this.onOutputAdded) { + this.onOutputAdded(o); + } + } + + this.setSize( this.computeSize() ); + this.setDirtyCanvas(true, true); + }; + + /** + * remove an existing output slot + * @method removeOutput + * @param {number} slot + */ + LGraphNode.prototype.removeOutput = function(slot) { + this.disconnectOutput(slot); + this.outputs.splice(slot, 1); + for (var i = slot; i < this.outputs.length; ++i) { + if (!this.outputs[i] || !this.outputs[i].links) { + continue; + } + var links = this.outputs[i].links; + for (var j = 0; j < links.length; ++j) { + var link = this.graph.links[links[j]]; + if (!link) { + continue; + } + link.origin_slot -= 1; + } + } + + this.setSize( this.computeSize() ); + if (this.onOutputRemoved) { + this.onOutputRemoved(slot); + } + this.setDirtyCanvas(true, true); + }; + + /** + * add a new input slot to use in this node + * @method addInput + * @param {string} name + * @param {string} type string defining the input type ("vec3","number",...), it its a generic one use 0 + * @param {Object} extra_info this can be used to have special properties of an input (label, color, position, etc) + */ + LGraphNode.prototype.addInput = function(name, type, extra_info) { + type = type || 0; + var o = { name: name, type: type, link: null }; + if (extra_info) { + for (var i in extra_info) { + o[i] = extra_info[i]; + } + } + + if (!this.inputs) { + this.inputs = []; + } + this.inputs.push(o); + this.setSize( this.computeSize() ); + if (this.onInputAdded) { + this.onInputAdded(o); + } + this.setDirtyCanvas(true, true); + return o; + }; + + /** + * add several new input slots in this node + * @method addInputs + * @param {Array} array of triplets like [[name,type,extra_info],[...]] + */ + LGraphNode.prototype.addInputs = function(array) { + for (var i = 0; i < array.length; ++i) { + var info = array[i]; + var o = { name: info[0], type: info[1], link: null }; + if (array[2]) { + for (var j in info[2]) { + o[j] = info[2][j]; + } + } + + if (!this.inputs) { + this.inputs = []; + } + this.inputs.push(o); + if (this.onInputAdded) { + this.onInputAdded(o); + } + } + + this.setSize( this.computeSize() ); + this.setDirtyCanvas(true, true); + }; + + /** + * remove an existing input slot + * @method removeInput + * @param {number} slot + */ + LGraphNode.prototype.removeInput = function(slot) { + this.disconnectInput(slot); + this.inputs.splice(slot, 1); + for (var i = slot; i < this.inputs.length; ++i) { + if (!this.inputs[i]) { + continue; + } + var link = this.graph.links[this.inputs[i].link]; + if (!link) { + continue; + } + link.target_slot -= 1; + } + this.setSize( this.computeSize() ); + if (this.onInputRemoved) { + this.onInputRemoved(slot); + } + this.setDirtyCanvas(true, true); + }; + + /** + * add an special connection to this node (used for special kinds of graphs) + * @method addConnection + * @param {string} name + * @param {string} type string defining the input type ("vec3","number",...) + * @param {[x,y]} pos position of the connection inside the node + * @param {string} direction if is input or output + */ + LGraphNode.prototype.addConnection = function(name, type, pos, direction) { + var o = { + name: name, + type: type, + pos: pos, + direction: direction, + links: null + }; + this.connections.push(o); + return o; + }; + + /** + * computes the minimum size of a node according to its inputs and output slots + * @method computeSize + * @param {number} the optional target width + * @return {number} the total size + */ + LGraphNode.prototype.computeSize = function(width, out) { + if (this.constructor.size) { + return this.constructor.size.concat(); + } + + var rows = Math.max( + this.inputs ? this.inputs.length : 1, + this.outputs ? this.outputs.length : 1 + ); + var size = out || new Float32Array([0, 0]); + rows = Math.max(rows, 1); + var font_size = LiteGraph.NODE_TEXT_SIZE; //although it should be graphcanvas.inner_text_font size + + var font_size = font_size; + var title_width = compute_text_size(this.title); + var input_width = 0; + var output_width = 0; + + if (this.inputs) { + for (var i = 0, l = this.inputs.length; i < l; ++i) { + var input = this.inputs[i]; + var text = input.label || input.name || ""; + var text_width = compute_text_size(text); + if (input_width < text_width) { + input_width = text_width; + } + } + } + + if (this.outputs) { + for (var i = 0, l = this.outputs.length; i < l; ++i) { + var output = this.outputs[i]; + var text = output.label || output.name || ""; + var text_width = compute_text_size(text); + if (output_width < text_width) { + output_width = text_width; + } + } + } + + size[0] = Math.max(input_width + output_width + 10, title_width); + size[0] = Math.max(size[0], LiteGraph.NODE_WIDTH); + if (this.widgets && this.widgets.length) { + size[0] = Math.max(size[0], LiteGraph.NODE_WIDTH * 1.5); + } + + size[1] = (this.constructor.slot_start_y || 0) + rows * LiteGraph.NODE_SLOT_HEIGHT; + + var widgets_height = 0; + if (this.widgets && this.widgets.length) { + for (var i = 0, l = this.widgets.length; i < l; ++i) { + if (this.widgets[i].computeSize) + widgets_height += this.widgets[i].computeSize(Math.max(size[0], width || 0))[1] + 4; + else + widgets_height += LiteGraph.NODE_WIDGET_HEIGHT + 4; + } + widgets_height += 8; + } + + //compute height using widgets height + if( this.widgets_up ) + size[1] = Math.max( size[1], widgets_height ); + else if( this.widgets_start_y != null ) + size[1] = Math.max( size[1], widgets_height + this.widgets_start_y ); + else + size[1] += widgets_height; + + function compute_text_size(text) { + if (!text) { + return 0; + } + return font_size * text.length * 0.6; + } + + if ( + this.constructor.min_height && + size[1] < this.constructor.min_height + ) { + size[1] = this.constructor.min_height; + } + + size[1] += 6; //margin + + return size; + }; + + /** + * returns all the info available about a property of this node. + * + * @method getPropertyInfo + * @param {String} property name of the property + * @return {Object} the object with all the available info + */ + LGraphNode.prototype.getPropertyInfo = function( property ) + { + var info = null; + + //there are several ways to define info about a property + //legacy mode + if (this.properties_info) { + for (var i = 0; i < this.properties_info.length; ++i) { + if (this.properties_info[i].name == property) { + info = this.properties_info[i]; + break; + } + } + } + //litescene mode using the constructor + if(this.constructor["@" + property]) + info = this.constructor["@" + property]; + + //litescene mode using the constructor + if (this.onGetPropertyInfo) { + info = this.onGetPropertyInfo(property); + } + + if (!info) + info = {}; + if(!info.type) + info.type = typeof this.properties[property]; + + return info; + } + + /** + * Defines a widget inside the node, it will be rendered on top of the node, you can control lots of properties + * + * @method addWidget + * @param {String} type the widget type (could be "number","string","combo" + * @param {String} name the text to show on the widget + * @param {String} value the default value + * @param {Function|String} callback function to call when it changes (optionally, it can be the name of the property to modify) + * @param {Object} options the object that contains special properties of this widget + * @return {Object} the created widget object + */ + LGraphNode.prototype.addWidget = function( type, name, value, callback, options ) + { + if (!this.widgets) { + this.widgets = []; + } + + if(!options && callback && callback.constructor === Object) + { + options = callback; + callback = null; + } + + if(options && options.constructor === String) //options can be the property name + options = { property: options }; + + if(callback && callback.constructor === String) //callback can be the property name + { + if(!options) + options = {}; + options.property = callback; + callback = null; + } + + if(callback && callback.constructor !== Function) + { + console.warn("addWidget: callback must be a function"); + callback = null; + } + + var w = { + type: type.toLowerCase(), + name: name, + value: value, + callback: callback, + options: options || {} + }; + + if (w.options.y !== undefined) { + w.y = w.options.y; + } + + if (!callback && !w.options.callback && !w.options.property) { + console.warn("LiteGraph addWidget(...) without a callback or property assigned"); + } + if (type == "combo" && !w.options.values) { + throw "LiteGraph addWidget('combo',...) requires to pass values in options: { values:['red','blue'] }"; + } + this.widgets.push(w); + this.setSize( this.computeSize() ); + return w; + }; + + LGraphNode.prototype.addCustomWidget = function(custom_widget) { + if (!this.widgets) { + this.widgets = []; + } + this.widgets.push(custom_widget); + return custom_widget; + }; + + /** + * returns the bounding of the object, used for rendering purposes + * bounding is: [topleft_cornerx, topleft_cornery, width, height] + * @method getBounding + * @return {Float32Array[4]} the total size + */ + LGraphNode.prototype.getBounding = function(out) { + out = out || new Float32Array(4); + out[0] = this.pos[0] - 4; + out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT; + out[2] = this.size[0] + 4; + out[3] = this.size[1] + LiteGraph.NODE_TITLE_HEIGHT; + + if (this.onBounding) { + this.onBounding(out); + } + return out; + }; + + /** + * checks if a point is inside the shape of a node + * @method isPointInside + * @param {number} x + * @param {number} y + * @return {boolean} + */ + LGraphNode.prototype.isPointInside = function(x, y, margin, skip_title) { + margin = margin || 0; + + var margin_top = this.graph && this.graph.isLive() ? 0 : LiteGraph.NODE_TITLE_HEIGHT; + if (skip_title) { + margin_top = 0; + } + if (this.flags && this.flags.collapsed) { + //if ( distance([x,y], [this.pos[0] + this.size[0]*0.5, this.pos[1] + this.size[1]*0.5]) < LiteGraph.NODE_COLLAPSED_RADIUS) + if ( + isInsideRectangle( + x, + y, + this.pos[0] - margin, + this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT - margin, + (this._collapsed_width || LiteGraph.NODE_COLLAPSED_WIDTH) + + 2 * margin, + LiteGraph.NODE_TITLE_HEIGHT + 2 * margin + ) + ) { + return true; + } + } else if ( + this.pos[0] - 4 - margin < x && + this.pos[0] + this.size[0] + 4 + margin > x && + this.pos[1] - margin_top - margin < y && + this.pos[1] + this.size[1] + margin > y + ) { + return true; + } + return false; + }; + + /** + * checks if a point is inside a node slot, and returns info about which slot + * @method getSlotInPosition + * @param {number} x + * @param {number} y + * @return {Object} if found the object contains { input|output: slot object, slot: number, link_pos: [x,y] } + */ + LGraphNode.prototype.getSlotInPosition = function(x, y) { + //search for inputs + var link_pos = new Float32Array(2); + if (this.inputs) { + for (var i = 0, l = this.inputs.length; i < l; ++i) { + var input = this.inputs[i]; + this.getConnectionPos(true, i, link_pos); + if ( + isInsideRectangle( + x, + y, + link_pos[0] - 10, + link_pos[1] - 5, + 20, + 10 + ) + ) { + return { input: input, slot: i, link_pos: link_pos }; + } + } + } + + if (this.outputs) { + for (var i = 0, l = this.outputs.length; i < l; ++i) { + var output = this.outputs[i]; + this.getConnectionPos(false, i, link_pos); + if ( + isInsideRectangle( + x, + y, + link_pos[0] - 10, + link_pos[1] - 5, + 20, + 10 + ) + ) { + return { output: output, slot: i, link_pos: link_pos }; + } + } + } + + return null; + }; + + /** + * returns the input slot with a given name (used for dynamic slots), -1 if not found + * @method findInputSlot + * @param {string} name the name of the slot + * @return {number} the slot (-1 if not found) + */ + LGraphNode.prototype.findInputSlot = function(name) { + if (!this.inputs) { + return -1; + } + for (var i = 0, l = this.inputs.length; i < l; ++i) { + if (name == this.inputs[i].name) { + return i; + } + } + return -1; + }; + + /** + * returns the output slot with a given name (used for dynamic slots), -1 if not found + * @method findOutputSlot + * @param {string} name the name of the slot + * @return {number} the slot (-1 if not found) + */ + LGraphNode.prototype.findOutputSlot = function(name) { + if (!this.outputs) { + return -1; + } + for (var i = 0, l = this.outputs.length; i < l; ++i) { + if (name == this.outputs[i].name) { + return i; + } + } + return -1; + }; + + /** + * connect this node output to the input of another node + * @method connect + * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) + * @param {LGraphNode} node the target node + * @param {number_or_string} target_slot the input slot of the target node (could be the number of the slot or the string with the name of the slot, or -1 to connect a trigger) + * @return {Object} the link_info is created, otherwise null + */ + LGraphNode.prototype.connect = function(slot, target_node, target_slot) { + target_slot = target_slot || 0; + + if (!this.graph) { + //could be connected before adding it to a graph + console.log( + "Connect: Error, node doesn't belong to any graph. Nodes must be added first to a graph before connecting them." + ); //due to link ids being associated with graphs + return null; + } + + //seek for the output slot + if (slot.constructor === String) { + slot = this.findOutputSlot(slot); + if (slot == -1) { + if (LiteGraph.debug) { + console.log("Connect: Error, no slot of name " + slot); + } + return null; + } + } else if (!this.outputs || slot >= this.outputs.length) { + if (LiteGraph.debug) { + console.log("Connect: Error, slot number not found"); + } + return null; + } + + if (target_node && target_node.constructor === Number) { + target_node = this.graph.getNodeById(target_node); + } + if (!target_node) { + throw "target node is null"; + } + + //avoid loopback + if (target_node == this) { + return null; + } + + //you can specify the slot by name + if (target_slot.constructor === String) { + target_slot = target_node.findInputSlot(target_slot); + if (target_slot == -1) { + if (LiteGraph.debug) { + console.log( + "Connect: Error, no slot of name " + target_slot + ); + } + return null; + } + } else if (target_slot === LiteGraph.EVENT) { + //search for first slot with event? + /* + //create input for trigger + var input = target_node.addInput("onTrigger", LiteGraph.EVENT ); + target_slot = target_node.inputs.length - 1; //last one is the one created + target_node.mode = LiteGraph.ON_TRIGGER; + */ + return null; + } else if ( + !target_node.inputs || + target_slot >= target_node.inputs.length + ) { + if (LiteGraph.debug) { + console.log("Connect: Error, slot number not found"); + } + return null; + } + + //if there is something already plugged there, disconnect + if (target_node.inputs[target_slot].link != null) { + target_node.disconnectInput(target_slot); + } + + //why here?? + //this.setDirtyCanvas(false,true); + //this.graph.connectionChange( this ); + + var output = this.outputs[slot]; + + //allows nodes to block connection + if (target_node.onConnectInput) { + if ( target_node.onConnectInput(target_slot, output.type, output, this, slot) === false ) { + return null; + } + } + + var input = target_node.inputs[target_slot]; + var link_info = null; + + if (LiteGraph.isValidConnection(output.type, input.type)) { + link_info = new LLink( + ++this.graph.last_link_id, + input.type, + this.id, + slot, + target_node.id, + target_slot + ); + + //add to graph links list + this.graph.links[link_info.id] = link_info; + + //connect in output + if (output.links == null) { + output.links = []; + } + output.links.push(link_info.id); + //connect in input + target_node.inputs[target_slot].link = link_info.id; + if (this.graph) { + this.graph._version++; + } + if (this.onConnectionsChange) { + this.onConnectionsChange( + LiteGraph.OUTPUT, + slot, + true, + link_info, + output + ); + } //link_info has been created now, so its updated + if (target_node.onConnectionsChange) { + target_node.onConnectionsChange( + LiteGraph.INPUT, + target_slot, + true, + link_info, + input + ); + } + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange( + LiteGraph.INPUT, + target_node, + target_slot, + this, + slot + ); + this.graph.onNodeConnectionChange( + LiteGraph.OUTPUT, + this, + slot, + target_node, + target_slot + ); + } + } + + this.setDirtyCanvas(false, true); + this.graph.connectionChange(this, link_info); + + return link_info; + }; + + /** + * disconnect one output to an specific node + * @method disconnectOutput + * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) + * @param {LGraphNode} target_node the target node to which this slot is connected [Optional, if not target_node is specified all nodes will be disconnected] + * @return {boolean} if it was disconnected successfully + */ + LGraphNode.prototype.disconnectOutput = function(slot, target_node) { + if (slot.constructor === String) { + slot = this.findOutputSlot(slot); + if (slot == -1) { + if (LiteGraph.debug) { + console.log("Connect: Error, no slot of name " + slot); + } + return false; + } + } else if (!this.outputs || slot >= this.outputs.length) { + if (LiteGraph.debug) { + console.log("Connect: Error, slot number not found"); + } + return false; + } + + //get output slot + var output = this.outputs[slot]; + if (!output || !output.links || output.links.length == 0) { + return false; + } + + //one of the output links in this slot + if (target_node) { + if (target_node.constructor === Number) { + target_node = this.graph.getNodeById(target_node); + } + if (!target_node) { + throw "Target Node not found"; + } + + for (var i = 0, l = output.links.length; i < l; i++) { + var link_id = output.links[i]; + var link_info = this.graph.links[link_id]; + + //is the link we are searching for... + if (link_info.target_id == target_node.id) { + output.links.splice(i, 1); //remove here + var input = target_node.inputs[link_info.target_slot]; + input.link = null; //remove there + delete this.graph.links[link_id]; //remove the link from the links pool + if (this.graph) { + this.graph._version++; + } + if (target_node.onConnectionsChange) { + target_node.onConnectionsChange( + LiteGraph.INPUT, + link_info.target_slot, + false, + link_info, + input + ); + } //link_info hasn't been modified so its ok + if (this.onConnectionsChange) { + this.onConnectionsChange( + LiteGraph.OUTPUT, + slot, + false, + link_info, + output + ); + } + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange( + LiteGraph.OUTPUT, + this, + slot + ); + } + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange( + LiteGraph.OUTPUT, + this, + slot + ); + this.graph.onNodeConnectionChange( + LiteGraph.INPUT, + target_node, + link_info.target_slot + ); + } + break; + } + } + } //all the links in this output slot + else { + for (var i = 0, l = output.links.length; i < l; i++) { + var link_id = output.links[i]; + var link_info = this.graph.links[link_id]; + if (!link_info) { + //bug: it happens sometimes + continue; + } + + var target_node = this.graph.getNodeById(link_info.target_id); + var input = null; + if (this.graph) { + this.graph._version++; + } + if (target_node) { + input = target_node.inputs[link_info.target_slot]; + input.link = null; //remove other side link + if (target_node.onConnectionsChange) { + target_node.onConnectionsChange( + LiteGraph.INPUT, + link_info.target_slot, + false, + link_info, + input + ); + } //link_info hasn't been modified so its ok + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange( + LiteGraph.INPUT, + target_node, + link_info.target_slot + ); + } + } + delete this.graph.links[link_id]; //remove the link from the links pool + if (this.onConnectionsChange) { + this.onConnectionsChange( + LiteGraph.OUTPUT, + slot, + false, + link_info, + output + ); + } + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange( + LiteGraph.OUTPUT, + this, + slot + ); + this.graph.onNodeConnectionChange( + LiteGraph.INPUT, + target_node, + link_info.target_slot + ); + } + } + output.links = null; + } + + this.setDirtyCanvas(false, true); + this.graph.connectionChange(this); + return true; + }; + + /** + * disconnect one input + * @method disconnectInput + * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) + * @return {boolean} if it was disconnected successfully + */ + LGraphNode.prototype.disconnectInput = function(slot) { + //seek for the output slot + if (slot.constructor === String) { + slot = this.findInputSlot(slot); + if (slot == -1) { + if (LiteGraph.debug) { + console.log("Connect: Error, no slot of name " + slot); + } + return false; + } + } else if (!this.inputs || slot >= this.inputs.length) { + if (LiteGraph.debug) { + console.log("Connect: Error, slot number not found"); + } + return false; + } + + var input = this.inputs[slot]; + if (!input) { + return false; + } + + var link_id = this.inputs[slot].link; + this.inputs[slot].link = null; + + //remove other side + var link_info = this.graph.links[link_id]; + if (link_info) { + var target_node = this.graph.getNodeById(link_info.origin_id); + if (!target_node) { + return false; + } + + var output = target_node.outputs[link_info.origin_slot]; + if (!output || !output.links || output.links.length == 0) { + return false; + } + + //search in the inputs list for this link + for (var i = 0, l = output.links.length; i < l; i++) { + if (output.links[i] == link_id) { + output.links.splice(i, 1); + break; + } + } + + delete this.graph.links[link_id]; //remove from the pool + if (this.graph) { + this.graph._version++; + } + if (this.onConnectionsChange) { + this.onConnectionsChange( + LiteGraph.INPUT, + slot, + false, + link_info, + input + ); + } + if (target_node.onConnectionsChange) { + target_node.onConnectionsChange( + LiteGraph.OUTPUT, + i, + false, + link_info, + output + ); + } + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange( + LiteGraph.OUTPUT, + target_node, + i + ); + this.graph.onNodeConnectionChange(LiteGraph.INPUT, this, slot); + } + } + + this.setDirtyCanvas(false, true); + this.graph.connectionChange(this); + return true; + }; + + /** + * returns the center of a connection point in canvas coords + * @method getConnectionPos + * @param {boolean} is_input true if if a input slot, false if it is an output + * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) + * @param {vec2} out [optional] a place to store the output, to free garbage + * @return {[x,y]} the position + **/ + LGraphNode.prototype.getConnectionPos = function( + is_input, + slot_number, + out + ) { + out = out || new Float32Array(2); + var num_slots = 0; + if (is_input && this.inputs) { + num_slots = this.inputs.length; + } + if (!is_input && this.outputs) { + num_slots = this.outputs.length; + } + + var offset = LiteGraph.NODE_SLOT_HEIGHT * 0.5; + + if (this.flags.collapsed) { + var w = this._collapsed_width || LiteGraph.NODE_COLLAPSED_WIDTH; + if (this.horizontal) { + out[0] = this.pos[0] + w * 0.5; + if (is_input) { + out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT; + } else { + out[1] = this.pos[1]; + } + } else { + if (is_input) { + out[0] = this.pos[0]; + } else { + out[0] = this.pos[0] + w; + } + out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT * 0.5; + } + return out; + } + + //weird feature that never got finished + if (is_input && slot_number == -1) { + out[0] = this.pos[0] + LiteGraph.NODE_TITLE_HEIGHT * 0.5; + out[1] = this.pos[1] + LiteGraph.NODE_TITLE_HEIGHT * 0.5; + return out; + } + + //hard-coded pos + if ( + is_input && + num_slots > slot_number && + this.inputs[slot_number].pos + ) { + out[0] = this.pos[0] + this.inputs[slot_number].pos[0]; + out[1] = this.pos[1] + this.inputs[slot_number].pos[1]; + return out; + } else if ( + !is_input && + num_slots > slot_number && + this.outputs[slot_number].pos + ) { + out[0] = this.pos[0] + this.outputs[slot_number].pos[0]; + out[1] = this.pos[1] + this.outputs[slot_number].pos[1]; + return out; + } + + //horizontal distributed slots + if (this.horizontal) { + out[0] = + this.pos[0] + (slot_number + 0.5) * (this.size[0] / num_slots); + if (is_input) { + out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT; + } else { + out[1] = this.pos[1] + this.size[1]; + } + return out; + } + + //default vertical slots + if (is_input) { + out[0] = this.pos[0] + offset; + } else { + out[0] = this.pos[0] + this.size[0] + 1 - offset; + } + out[1] = + this.pos[1] + + (slot_number + 0.7) * LiteGraph.NODE_SLOT_HEIGHT + + (this.constructor.slot_start_y || 0); + return out; + }; + + /* Force align to grid */ + LGraphNode.prototype.alignToGrid = function() { + this.pos[0] = + LiteGraph.CANVAS_GRID_SIZE * + Math.round(this.pos[0] / LiteGraph.CANVAS_GRID_SIZE); + this.pos[1] = + LiteGraph.CANVAS_GRID_SIZE * + Math.round(this.pos[1] / LiteGraph.CANVAS_GRID_SIZE); + }; + + /* Console output */ + LGraphNode.prototype.trace = function(msg) { + if (!this.console) { + this.console = []; + } + this.console.push(msg); + if (this.console.length > LGraphNode.MAX_CONSOLE) { + this.console.shift(); + } + + this.graph.onNodeTrace(this, msg); + }; + + /* Forces to redraw or the main canvas (LGraphNode) or the bg canvas (links) */ + LGraphNode.prototype.setDirtyCanvas = function( + dirty_foreground, + dirty_background + ) { + if (!this.graph) { + return; + } + this.graph.sendActionToCanvas("setDirty", [ + dirty_foreground, + dirty_background + ]); + }; + + LGraphNode.prototype.loadImage = function(url) { + var img = new Image(); + img.src = LiteGraph.node_images_path + url; + img.ready = false; + + var that = this; + img.onload = function() { + this.ready = true; + that.setDirtyCanvas(true); + }; + return img; + }; + + //safe LGraphNode action execution (not sure if safe) + /* +LGraphNode.prototype.executeAction = function(action) +{ + if(action == "") return false; + + if( action.indexOf(";") != -1 || action.indexOf("}") != -1) + { + this.trace("Error: Action contains unsafe characters"); + return false; + } + + var tokens = action.split("("); + var func_name = tokens[0]; + if( typeof(this[func_name]) != "function") + { + this.trace("Error: Action not found on node: " + func_name); + return false; + } + + var code = action; + + try + { + var _foo = eval; + eval = null; + (new Function("with(this) { " + code + "}")).call(this); + eval = _foo; + } + catch (err) + { + this.trace("Error executing action {" + action + "} :" + err); + return false; + } + + return true; +} +*/ + + /* Allows to get onMouseMove and onMouseUp events even if the mouse is out of focus */ + LGraphNode.prototype.captureInput = function(v) { + if (!this.graph || !this.graph.list_of_graphcanvas) { + return; + } + + var list = this.graph.list_of_graphcanvas; + + for (var i = 0; i < list.length; ++i) { + var c = list[i]; + //releasing somebody elses capture?! + if (!v && c.node_capturing_input != this) { + continue; + } + + //change + c.node_capturing_input = v ? this : null; + } + }; + + /** + * Collapse the node to make it smaller on the canvas + * @method collapse + **/ + LGraphNode.prototype.collapse = function(force) { + this.graph._version++; + if (this.constructor.collapsable === false && !force) { + return; + } + if (!this.flags.collapsed) { + this.flags.collapsed = true; + } else { + this.flags.collapsed = false; + } + this.setDirtyCanvas(true, true); + }; + + /** + * Forces the node to do not move or realign on Z + * @method pin + **/ + + LGraphNode.prototype.pin = function(v) { + this.graph._version++; + if (v === undefined) { + this.flags.pinned = !this.flags.pinned; + } else { + this.flags.pinned = v; + } + }; + + LGraphNode.prototype.localToScreen = function(x, y, graphcanvas) { + return [ + (x + this.pos[0]) * graphcanvas.scale + graphcanvas.offset[0], + (y + this.pos[1]) * graphcanvas.scale + graphcanvas.offset[1] + ]; + }; + + function LGraphGroup(title) { + this._ctor(title); + } + + global.LGraphGroup = LiteGraph.LGraphGroup = LGraphGroup; + + LGraphGroup.prototype._ctor = function(title) { + this.title = title || "Group"; + this.font_size = 24; + this.color = LGraphCanvas.node_colors.pale_blue + ? LGraphCanvas.node_colors.pale_blue.groupcolor + : "#AAA"; + this._bounding = new Float32Array([10, 10, 140, 80]); + this._pos = this._bounding.subarray(0, 2); + this._size = this._bounding.subarray(2, 4); + this._nodes = []; + this.graph = null; + + Object.defineProperty(this, "pos", { + set: function(v) { + if (!v || v.length < 2) { + return; + } + this._pos[0] = v[0]; + this._pos[1] = v[1]; + }, + get: function() { + return this._pos; + }, + enumerable: true + }); + + Object.defineProperty(this, "size", { + set: function(v) { + if (!v || v.length < 2) { + return; + } + this._size[0] = Math.max(140, v[0]); + this._size[1] = Math.max(80, v[1]); + }, + get: function() { + return this._size; + }, + enumerable: true + }); + }; + + LGraphGroup.prototype.configure = function(o) { + this.title = o.title; + this._bounding.set(o.bounding); + this.color = o.color; + this.font = o.font; + }; + + LGraphGroup.prototype.serialize = function() { + var b = this._bounding; + return { + title: this.title, + bounding: [ + Math.round(b[0]), + Math.round(b[1]), + Math.round(b[2]), + Math.round(b[3]) + ], + color: this.color, + font: this.font + }; + }; + + LGraphGroup.prototype.move = function(deltax, deltay, ignore_nodes) { + this._pos[0] += deltax; + this._pos[1] += deltay; + if (ignore_nodes) { + return; + } + for (var i = 0; i < this._nodes.length; ++i) { + var node = this._nodes[i]; + node.pos[0] += deltax; + node.pos[1] += deltay; + } + }; + + LGraphGroup.prototype.recomputeInsideNodes = function() { + this._nodes.length = 0; + var nodes = this.graph._nodes; + var node_bounding = new Float32Array(4); + + for (var i = 0; i < nodes.length; ++i) { + var node = nodes[i]; + node.getBounding(node_bounding); + if (!overlapBounding(this._bounding, node_bounding)) { + continue; + } //out of the visible area + this._nodes.push(node); + } + }; + + LGraphGroup.prototype.isPointInside = LGraphNode.prototype.isPointInside; + LGraphGroup.prototype.setDirtyCanvas = LGraphNode.prototype.setDirtyCanvas; + + //**************************************** + + //Scale and Offset + function DragAndScale(element, skip_events) { + this.offset = new Float32Array([0, 0]); + this.scale = 1; + this.max_scale = 10; + this.min_scale = 0.1; + this.onredraw = null; + this.enabled = true; + this.last_mouse = [0, 0]; + this.element = null; + this.visible_area = new Float32Array(4); + + if (element) { + this.element = element; + if (!skip_events) { + this.bindEvents(element); + } + } + } + + LiteGraph.DragAndScale = DragAndScale; + + DragAndScale.prototype.bindEvents = function(element) { + this.last_mouse = new Float32Array(2); + + this._binded_mouse_callback = this.onMouse.bind(this); + + element.addEventListener("mousedown", this._binded_mouse_callback); + element.addEventListener("mousemove", this._binded_mouse_callback); + + element.addEventListener( + "mousewheel", + this._binded_mouse_callback, + false + ); + element.addEventListener("wheel", this._binded_mouse_callback, false); + }; + + DragAndScale.prototype.computeVisibleArea = function() { + if (!this.element) { + this.visible_area[0] = this.visible_area[1] = this.visible_area[2] = this.visible_area[3] = 0; + return; + } + var width = this.element.width; + var height = this.element.height; + var startx = -this.offset[0]; + var starty = -this.offset[1]; + var endx = startx + width / this.scale; + var endy = starty + height / this.scale; + this.visible_area[0] = startx; + this.visible_area[1] = starty; + this.visible_area[2] = endx - startx; + this.visible_area[3] = endy - starty; + }; + + DragAndScale.prototype.onMouse = function(e) { + if (!this.enabled) { + return; + } + + var canvas = this.element; + var rect = canvas.getBoundingClientRect(); + var x = e.clientX - rect.left; + var y = e.clientY - rect.top; + e.canvasx = x; + e.canvasy = y; + e.dragging = this.dragging; + + var ignore = false; + if (this.onmouse) { + ignore = this.onmouse(e); + } + + if (e.type == "mousedown") { + this.dragging = true; + canvas.removeEventListener( + "mousemove", + this._binded_mouse_callback + ); + document.body.addEventListener( + "mousemove", + this._binded_mouse_callback + ); + document.body.addEventListener( + "mouseup", + this._binded_mouse_callback + ); + } else if (e.type == "mousemove") { + if (!ignore) { + var deltax = x - this.last_mouse[0]; + var deltay = y - this.last_mouse[1]; + if (this.dragging) { + this.mouseDrag(deltax, deltay); + } + } + } else if (e.type == "mouseup") { + this.dragging = false; + document.body.removeEventListener( + "mousemove", + this._binded_mouse_callback + ); + document.body.removeEventListener( + "mouseup", + this._binded_mouse_callback + ); + canvas.addEventListener("mousemove", this._binded_mouse_callback); + } else if ( + e.type == "mousewheel" || + e.type == "wheel" || + e.type == "DOMMouseScroll" + ) { + e.eventType = "mousewheel"; + if (e.type == "wheel") { + e.wheel = -e.deltaY; + } else { + e.wheel = + e.wheelDeltaY != null ? e.wheelDeltaY : e.detail * -60; + } + + //from stack overflow + e.delta = e.wheelDelta + ? e.wheelDelta / 40 + : e.deltaY + ? -e.deltaY / 3 + : 0; + this.changeDeltaScale(1.0 + e.delta * 0.05); + } + + this.last_mouse[0] = x; + this.last_mouse[1] = y; + + e.preventDefault(); + e.stopPropagation(); + return false; + }; + + DragAndScale.prototype.toCanvasContext = function(ctx) { + ctx.scale(this.scale, this.scale); + ctx.translate(this.offset[0], this.offset[1]); + }; + + DragAndScale.prototype.convertOffsetToCanvas = function(pos) { + //return [pos[0] / this.scale - this.offset[0], pos[1] / this.scale - this.offset[1]]; + return [ + (pos[0] + this.offset[0]) * this.scale, + (pos[1] + this.offset[1]) * this.scale + ]; + }; + + DragAndScale.prototype.convertCanvasToOffset = function(pos, out) { + out = out || [0, 0]; + out[0] = pos[0] / this.scale - this.offset[0]; + out[1] = pos[1] / this.scale - this.offset[1]; + return out; + }; + + DragAndScale.prototype.mouseDrag = function(x, y) { + this.offset[0] += x / this.scale; + this.offset[1] += y / this.scale; + + if (this.onredraw) { + this.onredraw(this); + } + }; + + DragAndScale.prototype.changeScale = function(value, zooming_center) { + if (value < this.min_scale) { + value = this.min_scale; + } else if (value > this.max_scale) { + value = this.max_scale; + } + + if (value == this.scale) { + return; + } + + if (!this.element) { + return; + } + + var rect = this.element.getBoundingClientRect(); + if (!rect) { + return; + } + + zooming_center = zooming_center || [ + rect.width * 0.5, + rect.height * 0.5 + ]; + var center = this.convertCanvasToOffset(zooming_center); + this.scale = value; + if (Math.abs(this.scale - 1) < 0.01) { + this.scale = 1; + } + + var new_center = this.convertCanvasToOffset(zooming_center); + var delta_offset = [ + new_center[0] - center[0], + new_center[1] - center[1] + ]; + + this.offset[0] += delta_offset[0]; + this.offset[1] += delta_offset[1]; + + if (this.onredraw) { + this.onredraw(this); + } + }; + + DragAndScale.prototype.changeDeltaScale = function(value, zooming_center) { + this.changeScale(this.scale * value, zooming_center); + }; + + DragAndScale.prototype.reset = function() { + this.scale = 1; + this.offset[0] = 0; + this.offset[1] = 0; + }; + + //********************************************************************************* + // LGraphCanvas: LGraph renderer CLASS + //********************************************************************************* + + /** + * This class is in charge of rendering one graph inside a canvas. And provides all the interaction required. + * Valid callbacks are: onNodeSelected, onNodeDeselected, onShowNodePanel, onNodeDblClicked + * + * @class LGraphCanvas + * @constructor + * @param {HTMLCanvas} canvas the canvas where you want to render (it accepts a selector in string format or the canvas element itself) + * @param {LGraph} graph [optional] + * @param {Object} options [optional] { skip_rendering, autoresize } + */ + function LGraphCanvas(canvas, graph, options) { + options = options || {}; + + //if(graph === undefined) + // throw ("No graph assigned"); + this.background_image = + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; + + if (canvas && canvas.constructor === String) { + canvas = document.querySelector(canvas); + } + + this.ds = new DragAndScale(); + this.zoom_modify_alpha = true; //otherwise it generates ugly patterns when scaling down too much + + this.title_text_font = "" + LiteGraph.NODE_TEXT_SIZE + "px Arial"; + this.inner_text_font = + "normal " + LiteGraph.NODE_SUBTEXT_SIZE + "px Arial"; + this.node_title_color = LiteGraph.NODE_TITLE_COLOR; + this.default_link_color = LiteGraph.LINK_COLOR; + this.default_connection_color = { + input_off: "#778", + input_on: "#7F7", + output_off: "#778", + output_on: "#7F7" + }; + + this.highquality_render = true; + this.use_gradients = false; //set to true to render titlebar with gradients + this.editor_alpha = 1; //used for transition + this.pause_rendering = false; + this.clear_background = true; + + this.read_only = false; //if set to true users cannot modify the graph + this.render_only_selected = true; + this.live_mode = false; + this.show_info = true; + this.allow_dragcanvas = true; + this.allow_dragnodes = true; + this.allow_interaction = true; //allow to control widgets, buttons, collapse, etc + this.allow_searchbox = true; + this.allow_reconnect_links = false; //allows to change a connection with having to redo it again + + this.drag_mode = false; + this.dragging_rectangle = null; + + this.filter = null; //allows to filter to only accept some type of nodes in a graph + + this.always_render_background = false; + this.render_shadows = true; + this.render_canvas_border = true; + this.render_connections_shadows = false; //too much cpu + this.render_connections_border = true; + this.render_curved_connections = false; + this.render_connection_arrows = false; + this.render_collapsed_slots = true; + this.render_execution_order = false; + this.render_title_colored = true; + this.render_link_tooltip = true; + + this.links_render_mode = LiteGraph.SPLINE_LINK; + + this.canvas_mouse = [0, 0]; //mouse in canvas graph coordinates, where 0,0 is the top-left corner of the blue rectangle + + //to personalize the search box + this.onSearchBox = null; + this.onSearchBoxSelection = null; + + //callbacks + this.onMouse = null; + this.onDrawBackground = null; //to render background objects (behind nodes and connections) in the canvas affected by transform + this.onDrawForeground = null; //to render foreground objects (above nodes and connections) in the canvas affected by transform + this.onDrawOverlay = null; //to render foreground objects not affected by transform (for GUIs) + this.onDrawLinkTooltip = null; //called when rendering a tooltip + this.onNodeMoved = null; //called after moving a node + this.onSelectionChange = null; //called if the selection changes + + this.connections_width = 3; + this.round_radius = 8; + + this.current_node = null; + this.node_widget = null; //used for widgets + this.over_link_center = null; + this.last_mouse_position = [0, 0]; + this.visible_area = this.ds.visible_area; + this.visible_links = []; + + //link canvas and graph + if (graph) { + graph.attachCanvas(this); + } + + this.setCanvas(canvas); + this.clear(); + + if (!options.skip_render) { + this.startRendering(); + } + + this.autoresize = options.autoresize; + } + + global.LGraphCanvas = LiteGraph.LGraphCanvas = LGraphCanvas; + + LGraphCanvas.link_type_colors = { + "-1": LiteGraph.EVENT_LINK_COLOR, + number: "#AAA", + node: "#DCA" + }; + LGraphCanvas.gradients = {}; //cache of gradients + + /** + * clears all the data inside + * + * @method clear + */ + LGraphCanvas.prototype.clear = function() { + this.frame = 0; + this.last_draw_time = 0; + this.render_time = 0; + this.fps = 0; + + //this.scale = 1; + //this.offset = [0,0]; + + this.dragging_rectangle = null; + + this.selected_nodes = {}; + this.selected_group = null; + + this.visible_nodes = []; + this.node_dragged = null; + this.node_over = null; + this.node_capturing_input = null; + this.connecting_node = null; + this.highlighted_links = {}; + + this.dirty_canvas = true; + this.dirty_bgcanvas = true; + this.dirty_area = null; + + this.node_in_panel = null; + this.node_widget = null; + + this.last_mouse = [0, 0]; + this.last_mouseclick = 0; + this.visible_area.set([0, 0, 0, 0]); + + if (this.onClear) { + this.onClear(); + } + }; + + /** + * assigns a graph, you can reassign graphs to the same canvas + * + * @method setGraph + * @param {LGraph} graph + */ + LGraphCanvas.prototype.setGraph = function(graph, skip_clear) { + if (this.graph == graph) { + return; + } + + if (!skip_clear) { + this.clear(); + } + + if (!graph && this.graph) { + this.graph.detachCanvas(this); + return; + } + + graph.attachCanvas(this); + + //remove the graph stack in case a subgraph was open + if (this._graph_stack) + this._graph_stack = null; + + this.setDirty(true, true); + }; + + /** + * opens a graph contained inside a node in the current graph + * + * @method openSubgraph + * @param {LGraph} graph + */ + LGraphCanvas.prototype.openSubgraph = function(graph) { + if (!graph) { + throw "graph cannot be null"; + } + + if (this.graph == graph) { + throw "graph cannot be the same"; + } + + this.clear(); + + if (this.graph) { + if (!this._graph_stack) { + this._graph_stack = []; + } + this._graph_stack.push(this.graph); + } + + graph.attachCanvas(this); + this.setDirty(true, true); + }; + + /** + * closes a subgraph contained inside a node + * + * @method closeSubgraph + * @param {LGraph} assigns a graph + */ + LGraphCanvas.prototype.closeSubgraph = function() { + if (!this._graph_stack || this._graph_stack.length == 0) { + return; + } + var subgraph_node = this.graph._subgraph_node; + var graph = this._graph_stack.pop(); + this.selected_nodes = {}; + this.highlighted_links = {}; + graph.attachCanvas(this); + this.setDirty(true, true); + if (subgraph_node) { + this.centerOnNode(subgraph_node); + this.selectNodes([subgraph_node]); + } + }; + + /** + * returns the visualy active graph (in case there are more in the stack) + * @method getCurrentGraph + * @return {LGraph} the active graph + */ + LGraphCanvas.prototype.getCurrentGraph = function() { + return this.graph; + }; + + /** + * assigns a canvas + * + * @method setCanvas + * @param {Canvas} assigns a canvas (also accepts the ID of the element (not a selector) + */ + LGraphCanvas.prototype.setCanvas = function(canvas, skip_events) { + var that = this; + + if (canvas) { + if (canvas.constructor === String) { + canvas = document.getElementById(canvas); + if (!canvas) { + throw "Error creating LiteGraph canvas: Canvas not found"; + } + } + } + + if (canvas === this.canvas) { + return; + } + + if (!canvas && this.canvas) { + //maybe detach events from old_canvas + if (!skip_events) { + this.unbindEvents(); + } + } + + this.canvas = canvas; + this.ds.element = canvas; + + if (!canvas) { + return; + } + + //this.canvas.tabindex = "1000"; + canvas.className += " lgraphcanvas"; + canvas.data = this; + canvas.tabindex = "1"; //to allow key events + + //bg canvas: used for non changing stuff + this.bgcanvas = null; + if (!this.bgcanvas) { + this.bgcanvas = document.createElement("canvas"); + this.bgcanvas.width = this.canvas.width; + this.bgcanvas.height = this.canvas.height; + } + + if (canvas.getContext == null) { + if (canvas.localName != "canvas") { + throw "Element supplied for LGraphCanvas must be a element, you passed a " + + canvas.localName; + } + throw "This browser doesn't support Canvas"; + } + + var ctx = (this.ctx = canvas.getContext("2d")); + if (ctx == null) { + if (!canvas.webgl_enabled) { + console.warn( + "This canvas seems to be WebGL, enabling WebGL renderer" + ); + } + this.enableWebGL(); + } + + //input: (move and up could be unbinded) + this._mousemove_callback = this.processMouseMove.bind(this); + this._mouseup_callback = this.processMouseUp.bind(this); + + if (!skip_events) { + this.bindEvents(); + } + }; + + //used in some events to capture them + LGraphCanvas.prototype._doNothing = function doNothing(e) { + e.preventDefault(); + return false; + }; + LGraphCanvas.prototype._doReturnTrue = function doNothing(e) { + e.preventDefault(); + return true; + }; + + /** + * binds mouse, keyboard, touch and drag events to the canvas + * @method bindEvents + **/ + LGraphCanvas.prototype.bindEvents = function() { + if (this._events_binded) { + console.warn("LGraphCanvas: events already binded"); + return; + } + + var canvas = this.canvas; + var ref_window = this.getCanvasWindow(); + var document = ref_window.document; //hack used when moving canvas between windows + + this._mousedown_callback = this.processMouseDown.bind(this); + this._mousewheel_callback = this.processMouseWheel.bind(this); + + canvas.addEventListener("mousedown", this._mousedown_callback, true); //down do not need to store the binded + canvas.addEventListener("mousemove", this._mousemove_callback); + canvas.addEventListener("mousewheel", this._mousewheel_callback, false); + + canvas.addEventListener("contextmenu", this._doNothing); + canvas.addEventListener( + "DOMMouseScroll", + this._mousewheel_callback, + false + ); + + //touch events + //if( 'touchstart' in document.documentElement ) + { + canvas.addEventListener("touchstart", this.touchHandler, true); + canvas.addEventListener("touchmove", this.touchHandler, true); + canvas.addEventListener("touchend", this.touchHandler, true); + canvas.addEventListener("touchcancel", this.touchHandler, true); + } + + //Keyboard ****************** + this._key_callback = this.processKey.bind(this); + + canvas.addEventListener("keydown", this._key_callback, true); + document.addEventListener("keyup", this._key_callback, true); //in document, otherwise it doesn't fire keyup + + //Dropping Stuff over nodes ************************************ + this._ondrop_callback = this.processDrop.bind(this); + + canvas.addEventListener("dragover", this._doNothing, false); + canvas.addEventListener("dragend", this._doNothing, false); + canvas.addEventListener("drop", this._ondrop_callback, false); + canvas.addEventListener("dragenter", this._doReturnTrue, false); + + this._events_binded = true; + }; + + /** + * unbinds mouse events from the canvas + * @method unbindEvents + **/ + LGraphCanvas.prototype.unbindEvents = function() { + if (!this._events_binded) { + console.warn("LGraphCanvas: no events binded"); + return; + } + + var ref_window = this.getCanvasWindow(); + var document = ref_window.document; + + this.canvas.removeEventListener("mousedown", this._mousedown_callback); + this.canvas.removeEventListener( + "mousewheel", + this._mousewheel_callback + ); + this.canvas.removeEventListener( + "DOMMouseScroll", + this._mousewheel_callback + ); + this.canvas.removeEventListener("keydown", this._key_callback); + document.removeEventListener("keyup", this._key_callback); + this.canvas.removeEventListener("contextmenu", this._doNothing); + this.canvas.removeEventListener("drop", this._ondrop_callback); + this.canvas.removeEventListener("dragenter", this._doReturnTrue); + + this.canvas.removeEventListener("touchstart", this.touchHandler); + this.canvas.removeEventListener("touchmove", this.touchHandler); + this.canvas.removeEventListener("touchend", this.touchHandler); + this.canvas.removeEventListener("touchcancel", this.touchHandler); + + this._mousedown_callback = null; + this._mousewheel_callback = null; + this._key_callback = null; + this._ondrop_callback = null; + + this._events_binded = false; + }; + + LGraphCanvas.getFileExtension = function(url) { + var question = url.indexOf("?"); + if (question != -1) { + url = url.substr(0, question); + } + var point = url.lastIndexOf("."); + if (point == -1) { + return ""; + } + return url.substr(point + 1).toLowerCase(); + }; + + /** + * this function allows to render the canvas using WebGL instead of Canvas2D + * this is useful if you plant to render 3D objects inside your nodes, it uses litegl.js for webgl and canvas2DtoWebGL to emulate the Canvas2D calls in webGL + * @method enableWebGL + **/ + LGraphCanvas.prototype.enableWebGL = function() { + if (typeof GL === undefined) { + throw "litegl.js must be included to use a WebGL canvas"; + } + if (typeof enableWebGLCanvas === undefined) { + throw "webglCanvas.js must be included to use this feature"; + } + + this.gl = this.ctx = enableWebGLCanvas(this.canvas); + this.ctx.webgl = true; + this.bgcanvas = this.canvas; + this.bgctx = this.gl; + this.canvas.webgl_enabled = true; + + /* + GL.create({ canvas: this.bgcanvas }); + this.bgctx = enableWebGLCanvas( this.bgcanvas ); + window.gl = this.gl; + */ + }; + + /** + * marks as dirty the canvas, this way it will be rendered again + * + * @class LGraphCanvas + * @method setDirty + * @param {bool} fgcanvas if the foreground canvas is dirty (the one containing the nodes) + * @param {bool} bgcanvas if the background canvas is dirty (the one containing the wires) + */ + LGraphCanvas.prototype.setDirty = function(fgcanvas, bgcanvas) { + if (fgcanvas) { + this.dirty_canvas = true; + } + if (bgcanvas) { + this.dirty_bgcanvas = true; + } + }; + + /** + * Used to attach the canvas in a popup + * + * @method getCanvasWindow + * @return {window} returns the window where the canvas is attached (the DOM root node) + */ + LGraphCanvas.prototype.getCanvasWindow = function() { + if (!this.canvas) { + return window; + } + var doc = this.canvas.ownerDocument; + return doc.defaultView || doc.parentWindow; + }; + + /** + * starts rendering the content of the canvas when needed + * + * @method startRendering + */ + LGraphCanvas.prototype.startRendering = function() { + if (this.is_rendering) { + return; + } //already rendering + + this.is_rendering = true; + renderFrame.call(this); + + function renderFrame() { + if (!this.pause_rendering) { + this.draw(); + } + + var window = this.getCanvasWindow(); + if (this.is_rendering) { + window.requestAnimationFrame(renderFrame.bind(this)); + } + } + }; + + /** + * stops rendering the content of the canvas (to save resources) + * + * @method stopRendering + */ + LGraphCanvas.prototype.stopRendering = function() { + this.is_rendering = false; + /* + if(this.rendering_timer_id) + { + clearInterval(this.rendering_timer_id); + this.rendering_timer_id = null; + } + */ + }; + + /* LiteGraphCanvas input */ + + LGraphCanvas.prototype.processMouseDown = function(e) { + if (!this.graph) { + return; + } + + this.adjustMouseEvent(e); + + var ref_window = this.getCanvasWindow(); + var document = ref_window.document; + LGraphCanvas.active_canvas = this; + var that = this; + + //move mouse move event to the window in case it drags outside of the canvas + this.canvas.removeEventListener("mousemove", this._mousemove_callback); + ref_window.document.addEventListener( + "mousemove", + this._mousemove_callback, + true + ); //catch for the entire window + ref_window.document.addEventListener( + "mouseup", + this._mouseup_callback, + true + ); + + var node = this.graph.getNodeOnPos( + e.canvasX, + e.canvasY, + this.visible_nodes, + 5 + ); + var skip_dragging = false; + var skip_action = false; + var now = LiteGraph.getTime(); + var is_double_click = now - this.last_mouseclick < 300; + + this.canvas_mouse[0] = e.canvasX; + this.canvas_mouse[1] = e.canvasY; + this.canvas.focus(); + + LiteGraph.closeAllContextMenus(ref_window); + + if (this.onMouse) { + if (this.onMouse(e) == true) { + return; + } + } + + if (e.which == 1) { + //left button mouse + if (e.ctrlKey) { + this.dragging_rectangle = new Float32Array(4); + this.dragging_rectangle[0] = e.canvasX; + this.dragging_rectangle[1] = e.canvasY; + this.dragging_rectangle[2] = 1; + this.dragging_rectangle[3] = 1; + skip_action = true; + } + + var clicking_canvas_bg = false; + + //when clicked on top of a node + //and it is not interactive + if (node && this.allow_interaction && !skip_action && !this.read_only) { + if (!this.live_mode && !node.flags.pinned) { + this.bringToFront(node); + } //if it wasn't selected? + + //not dragging mouse to connect two slots + if ( + !this.connecting_node && + !node.flags.collapsed && + !this.live_mode + ) { + //Search for corner for resize + if ( + !skip_action && + node.resizable !== false && + isInsideRectangle( + e.canvasX, + e.canvasY, + node.pos[0] + node.size[0] - 5, + node.pos[1] + node.size[1] - 5, + 10, + 10 + ) + ) { + this.resizing_node = node; + this.canvas.style.cursor = "se-resize"; + skip_action = true; + } else { + //search for outputs + if (node.outputs) { + for ( + var i = 0, l = node.outputs.length; + i < l; + ++i + ) { + var output = node.outputs[i]; + var link_pos = node.getConnectionPos(false, i); + if ( + isInsideRectangle( + e.canvasX, + e.canvasY, + link_pos[0] - 15, + link_pos[1] - 10, + 30, + 20 + ) + ) { + this.connecting_node = node; + this.connecting_output = output; + this.connecting_pos = node.getConnectionPos( false, i ); + this.connecting_slot = i; + + if (e.shiftKey) { + node.disconnectOutput(i); + } + + if (is_double_click) { + if (node.onOutputDblClick) { + node.onOutputDblClick(i, e); + } + } else { + if (node.onOutputClick) { + node.onOutputClick(i, e); + } + } + + skip_action = true; + break; + } + } + } + + //search for inputs + if (node.inputs) { + for ( + var i = 0, l = node.inputs.length; + i < l; + ++i + ) { + var input = node.inputs[i]; + var link_pos = node.getConnectionPos(true, i); + if ( + isInsideRectangle( + e.canvasX, + e.canvasY, + link_pos[0] - 15, + link_pos[1] - 10, + 30, + 20 + ) + ) { + if (is_double_click) { + if (node.onInputDblClick) { + node.onInputDblClick(i, e); + } + } else { + if (node.onInputClick) { + node.onInputClick(i, e); + } + } + + if (input.link !== null) { + var link_info = this.graph.links[ + input.link + ]; //before disconnecting + node.disconnectInput(i); + + if ( + this.allow_reconnect_links || + e.shiftKey + ) { + this.connecting_node = this.graph._nodes_by_id[ + link_info.origin_id + ]; + this.connecting_slot = + link_info.origin_slot; + this.connecting_output = this.connecting_node.outputs[ + this.connecting_slot + ]; + this.connecting_pos = this.connecting_node.getConnectionPos( false, this.connecting_slot ); + } + + this.dirty_bgcanvas = true; + skip_action = true; + } + } + } + } + } //not resizing + } + + //Search for corner for collapsing + /* + if( !skip_action && isInsideRectangle( e.canvasX, e.canvasY, node.pos[0], node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT )) + { + node.collapse(); + skip_action = true; + } + */ + + //it wasn't clicked on the links boxes + if (!skip_action) { + var block_drag_node = false; + + //widgets + var widget = this.processNodeWidgets( node, this.canvas_mouse, e ); + if (widget) { + block_drag_node = true; + this.node_widget = [node, widget]; + } + + //double clicking + if (is_double_click && this.selected_nodes[node.id]) { + //double click node + if (node.onDblClick) { + node.onDblClick( e, [ e.canvasX - node.pos[0], e.canvasY - node.pos[1] ], this ); + } + this.processNodeDblClicked(node); + block_drag_node = true; + } + + //if do not capture mouse + if ( node.onMouseDown && node.onMouseDown( e, [e.canvasX - node.pos[0], e.canvasY - node.pos[1]], this ) ) { + block_drag_node = true; + } else if (this.live_mode) { + clicking_canvas_bg = true; + block_drag_node = true; + } + + if (!block_drag_node) { + if (this.allow_dragnodes) { + this.node_dragged = node; + } + if (!this.selected_nodes[node.id]) { + this.processNodeSelected(node, e); + } + } + + this.dirty_canvas = true; + } + } //clicked outside of nodes + else { + //search for link connector + if(!this.read_only) + for (var i = 0; i < this.visible_links.length; ++i) { + var link = this.visible_links[i]; + var center = link._pos; + if ( + !center || + e.canvasX < center[0] - 4 || + e.canvasX > center[0] + 4 || + e.canvasY < center[1] - 4 || + e.canvasY > center[1] + 4 + ) { + continue; + } + //link clicked + this.showLinkMenu(link, e); + this.over_link_center = null; //clear tooltip + break; + } + + this.selected_group = this.graph.getGroupOnPos( e.canvasX, e.canvasY ); + this.selected_group_resizing = false; + if (this.selected_group && !this.read_only ) { + if (e.ctrlKey) { + this.dragging_rectangle = null; + } + + var dist = distance( [e.canvasX, e.canvasY], [ this.selected_group.pos[0] + this.selected_group.size[0], this.selected_group.pos[1] + this.selected_group.size[1] ] ); + if (dist * this.ds.scale < 10) { + this.selected_group_resizing = true; + } else { + this.selected_group.recomputeInsideNodes(); + } + } + + if (is_double_click && !this.read_only && this.allow_searchbox) { + this.showSearchBox(e); + } + + clicking_canvas_bg = true; + } + + if (!skip_action && clicking_canvas_bg && this.allow_dragcanvas) { + this.dragging_canvas = true; + } + } else if (e.which == 2) { + //middle button + } else if (e.which == 3) { + //right button + if(!this.read_only) + this.processContextMenu(node, e); + } + + //TODO + //if(this.node_selected != prev_selected) + // this.onNodeSelectionChange(this.node_selected); + + this.last_mouse[0] = e.localX; + this.last_mouse[1] = e.localY; + this.last_mouseclick = LiteGraph.getTime(); + this.last_mouse_dragging = true; + + /* + if( (this.dirty_canvas || this.dirty_bgcanvas) && this.rendering_timer_id == null) + this.draw(); + */ + + this.graph.change(); + + //this is to ensure to defocus(blur) if a text input element is on focus + if ( + !ref_window.document.activeElement || + (ref_window.document.activeElement.nodeName.toLowerCase() != + "input" && + ref_window.document.activeElement.nodeName.toLowerCase() != + "textarea") + ) { + e.preventDefault(); + } + e.stopPropagation(); + + if (this.onMouseDown) { + this.onMouseDown(e); + } + + return false; + }; + + /** + * Called when a mouse move event has to be processed + * @method processMouseMove + **/ + LGraphCanvas.prototype.processMouseMove = function(e) { + if (this.autoresize) { + this.resize(); + } + + if (!this.graph) { + return; + } + + LGraphCanvas.active_canvas = this; + this.adjustMouseEvent(e); + var mouse = [e.localX, e.localY]; + var delta = [ + mouse[0] - this.last_mouse[0], + mouse[1] - this.last_mouse[1] + ]; + this.last_mouse = mouse; + this.canvas_mouse[0] = e.canvasX; + this.canvas_mouse[1] = e.canvasY; + e.dragging = this.last_mouse_dragging; + + if (this.node_widget) { + this.processNodeWidgets( + this.node_widget[0], + this.canvas_mouse, + e, + this.node_widget[1] + ); + this.dirty_canvas = true; + } + + if (this.dragging_rectangle) { + this.dragging_rectangle[2] = e.canvasX - this.dragging_rectangle[0]; + this.dragging_rectangle[3] = e.canvasY - this.dragging_rectangle[1]; + this.dirty_canvas = true; + } else if (this.selected_group && !this.read_only) { + //moving/resizing a group + if (this.selected_group_resizing) { + this.selected_group.size = [ + e.canvasX - this.selected_group.pos[0], + e.canvasY - this.selected_group.pos[1] + ]; + } else { + var deltax = delta[0] / this.ds.scale; + var deltay = delta[1] / this.ds.scale; + this.selected_group.move(deltax, deltay, e.ctrlKey); + if (this.selected_group._nodes.length) { + this.dirty_canvas = true; + } + } + this.dirty_bgcanvas = true; + } else if (this.dragging_canvas) { + this.ds.offset[0] += delta[0] / this.ds.scale; + this.ds.offset[1] += delta[1] / this.ds.scale; + this.dirty_canvas = true; + this.dirty_bgcanvas = true; + } else if (this.allow_interaction && !this.read_only) { + if (this.connecting_node) { + this.dirty_canvas = true; + } + + //get node over + var node = this.graph.getNodeOnPos( + e.canvasX, + e.canvasY, + this.visible_nodes + ); + + //remove mouseover flag + for (var i = 0, l = this.graph._nodes.length; i < l; ++i) { + if ( + this.graph._nodes[i].mouseOver && + node != this.graph._nodes[i] + ) { + //mouse leave + this.graph._nodes[i].mouseOver = false; + if (this.node_over && this.node_over.onMouseLeave) { + this.node_over.onMouseLeave(e); + } + this.node_over = null; + this.dirty_canvas = true; + } + } + + //mouse over a node + if (node) { + //this.canvas.style.cursor = "move"; + if (!node.mouseOver) { + //mouse enter + node.mouseOver = true; + this.node_over = node; + this.dirty_canvas = true; + + if (node.onMouseEnter) { + node.onMouseEnter(e); + } + } + + //in case the node wants to do something + if (node.onMouseMove) { + node.onMouseMove( + e, + [e.canvasX - node.pos[0], e.canvasY - node.pos[1]], + this + ); + } + + //if dragging a link + if (this.connecting_node) { + var pos = this._highlight_input || [0, 0]; //to store the output of isOverNodeInput + + //on top of input + if (this.isOverNodeBox(node, e.canvasX, e.canvasY)) { + //mouse on top of the corner box, don't know what to do + } else { + //check if I have a slot below de mouse + var slot = this.isOverNodeInput( + node, + e.canvasX, + e.canvasY, + pos + ); + if (slot != -1 && node.inputs[slot]) { + var slot_type = node.inputs[slot].type; + if ( + LiteGraph.isValidConnection( + this.connecting_output.type, + slot_type + ) + ) { + this._highlight_input = pos; + } + } else { + this._highlight_input = null; + } + } + } + + //Search for corner + if (this.canvas) { + if ( + isInsideRectangle( + e.canvasX, + e.canvasY, + node.pos[0] + node.size[0] - 5, + node.pos[1] + node.size[1] - 5, + 5, + 5 + ) + ) { + this.canvas.style.cursor = "se-resize"; + } else { + this.canvas.style.cursor = "crosshair"; + } + } + } else { //outside + + //search for link connector + var over_link = null; + for (var i = 0; i < this.visible_links.length; ++i) { + var link = this.visible_links[i]; + var center = link._pos; + if ( + !center || + e.canvasX < center[0] - 4 || + e.canvasX > center[0] + 4 || + e.canvasY < center[1] - 4 || + e.canvasY > center[1] + 4 + ) { + continue; + } + over_link = link; + break; + } + if( over_link != this.over_link_center ) + { + this.over_link_center = over_link; + this.dirty_canvas = true; + } + + if (this.canvas) { + this.canvas.style.cursor = ""; + } + } + + if ( this.node_capturing_input && this.node_capturing_input != node && this.node_capturing_input.onMouseMove ) { + this.node_capturing_input.onMouseMove(e,[e.canvasX - this.node_capturing_input.pos[0],e.canvasY - this.node_capturing_input.pos[1]], this); + } + + if (this.node_dragged && !this.live_mode) { + for (var i in this.selected_nodes) { + var n = this.selected_nodes[i]; + n.pos[0] += delta[0] / this.ds.scale; + n.pos[1] += delta[1] / this.ds.scale; + } + + this.dirty_canvas = true; + this.dirty_bgcanvas = true; + } + + if (this.resizing_node && !this.live_mode) { + //convert mouse to node space + var desired_size = [ e.canvasX - this.resizing_node.pos[0], e.canvasY - this.resizing_node.pos[1] ]; + var min_size = this.resizing_node.computeSize(); + desired_size[0] = Math.max( min_size[0], desired_size[0] ); + desired_size[1] = Math.max( min_size[1], desired_size[1] ); + this.resizing_node.setSize( desired_size ); + + this.canvas.style.cursor = "se-resize"; + this.dirty_canvas = true; + this.dirty_bgcanvas = true; + } + } + + e.preventDefault(); + return false; + }; + + /** + * Called when a mouse up event has to be processed + * @method processMouseUp + **/ + LGraphCanvas.prototype.processMouseUp = function(e) { + if (!this.graph) { + return; + } + + var window = this.getCanvasWindow(); + var document = window.document; + LGraphCanvas.active_canvas = this; + + //restore the mousemove event back to the canvas + document.removeEventListener("mousemove",this._mousemove_callback,true); + this.canvas.addEventListener("mousemove",this._mousemove_callback,true); + document.removeEventListener("mouseup", this._mouseup_callback, true); + + this.adjustMouseEvent(e); + var now = LiteGraph.getTime(); + e.click_time = now - this.last_mouseclick; + this.last_mouse_dragging = false; + + if (e.which == 1) { + + if( this.node_widget ) + { + this.processNodeWidgets( this.node_widget[0], this.canvas_mouse, e ); + } + + //left button + this.node_widget = null; + + if (this.selected_group) { + var diffx = + this.selected_group.pos[0] - + Math.round(this.selected_group.pos[0]); + var diffy = + this.selected_group.pos[1] - + Math.round(this.selected_group.pos[1]); + this.selected_group.move(diffx, diffy, e.ctrlKey); + this.selected_group.pos[0] = Math.round( + this.selected_group.pos[0] + ); + this.selected_group.pos[1] = Math.round( + this.selected_group.pos[1] + ); + if (this.selected_group._nodes.length) { + this.dirty_canvas = true; + } + this.selected_group = null; + } + this.selected_group_resizing = false; + + if (this.dragging_rectangle) { + if (this.graph) { + var nodes = this.graph._nodes; + var node_bounding = new Float32Array(4); + this.deselectAllNodes(); + //compute bounding and flip if left to right + var w = Math.abs(this.dragging_rectangle[2]); + var h = Math.abs(this.dragging_rectangle[3]); + var startx = + this.dragging_rectangle[2] < 0 + ? this.dragging_rectangle[0] - w + : this.dragging_rectangle[0]; + var starty = + this.dragging_rectangle[3] < 0 + ? this.dragging_rectangle[1] - h + : this.dragging_rectangle[1]; + this.dragging_rectangle[0] = startx; + this.dragging_rectangle[1] = starty; + this.dragging_rectangle[2] = w; + this.dragging_rectangle[3] = h; + + //test against all nodes (not visible because the rectangle maybe start outside + var to_select = []; + for (var i = 0; i < nodes.length; ++i) { + var node = nodes[i]; + node.getBounding(node_bounding); + if ( + !overlapBounding( + this.dragging_rectangle, + node_bounding + ) + ) { + continue; + } //out of the visible area + to_select.push(node); + } + if (to_select.length) { + this.selectNodes(to_select); + } + } + this.dragging_rectangle = null; + } else if (this.connecting_node) { + //dragging a connection + this.dirty_canvas = true; + this.dirty_bgcanvas = true; + + var node = this.graph.getNodeOnPos( + e.canvasX, + e.canvasY, + this.visible_nodes + ); + + //node below mouse + if (node) { + if ( + this.connecting_output.type == LiteGraph.EVENT && + this.isOverNodeBox(node, e.canvasX, e.canvasY) + ) { + this.connecting_node.connect( + this.connecting_slot, + node, + LiteGraph.EVENT + ); + } else { + //slot below mouse? connect + var slot = this.isOverNodeInput( + node, + e.canvasX, + e.canvasY + ); + if (slot != -1) { + this.connecting_node.connect( + this.connecting_slot, + node, + slot + ); + } else { + //not on top of an input + var input = node.getInputInfo(0); + //auto connect + if ( + this.connecting_output.type == LiteGraph.EVENT + ) { + this.connecting_node.connect( + this.connecting_slot, + node, + LiteGraph.EVENT + ); + } else if ( + input && + !input.link && + LiteGraph.isValidConnection( + input.type && this.connecting_output.type + ) + ) { + this.connecting_node.connect( + this.connecting_slot, + node, + 0 + ); + } + } + } + } + + this.connecting_output = null; + this.connecting_pos = null; + this.connecting_node = null; + this.connecting_slot = -1; + } //not dragging connection + else if (this.resizing_node) { + this.dirty_canvas = true; + this.dirty_bgcanvas = true; + this.resizing_node = null; + } else if (this.node_dragged) { + //node being dragged? + var node = this.node_dragged; + if ( + node && + e.click_time < 300 && + isInsideRectangle( e.canvasX, e.canvasY, node.pos[0], node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT ) + ) { + node.collapse(); + } + + this.dirty_canvas = true; + this.dirty_bgcanvas = true; + this.node_dragged.pos[0] = Math.round(this.node_dragged.pos[0]); + this.node_dragged.pos[1] = Math.round(this.node_dragged.pos[1]); + if (this.graph.config.align_to_grid) { + this.node_dragged.alignToGrid(); + } + if( this.onNodeMoved ) + this.onNodeMoved( this.node_dragged ); + this.node_dragged = null; + } //no node being dragged + else { + //get node over + var node = this.graph.getNodeOnPos( + e.canvasX, + e.canvasY, + this.visible_nodes + ); + + if (!node && e.click_time < 300) { + this.deselectAllNodes(); + } + + this.dirty_canvas = true; + this.dragging_canvas = false; + + if (this.node_over && this.node_over.onMouseUp) { + this.node_over.onMouseUp( e, [ e.canvasX - this.node_over.pos[0], e.canvasY - this.node_over.pos[1] ], this ); + } + if ( + this.node_capturing_input && + this.node_capturing_input.onMouseUp + ) { + this.node_capturing_input.onMouseUp(e, [ + e.canvasX - this.node_capturing_input.pos[0], + e.canvasY - this.node_capturing_input.pos[1] + ]); + } + } + } else if (e.which == 2) { + //middle button + //trace("middle"); + this.dirty_canvas = true; + this.dragging_canvas = false; + } else if (e.which == 3) { + //right button + //trace("right"); + this.dirty_canvas = true; + this.dragging_canvas = false; + } + + /* + if((this.dirty_canvas || this.dirty_bgcanvas) && this.rendering_timer_id == null) + this.draw(); + */ + + this.graph.change(); + + e.stopPropagation(); + e.preventDefault(); + return false; + }; + + /** + * Called when a mouse wheel event has to be processed + * @method processMouseWheel + **/ + LGraphCanvas.prototype.processMouseWheel = function(e) { + if (!this.graph || !this.allow_dragcanvas) { + return; + } + + var delta = e.wheelDeltaY != null ? e.wheelDeltaY : e.detail * -60; + + this.adjustMouseEvent(e); + + var scale = this.ds.scale; + + if (delta > 0) { + scale *= 1.1; + } else if (delta < 0) { + scale *= 1 / 1.1; + } + + //this.setZoom( scale, [ e.localX, e.localY ] ); + this.ds.changeScale(scale, [e.localX, e.localY]); + + this.graph.change(); + + e.preventDefault(); + return false; // prevent default + }; + + /** + * returns true if a position (in graph space) is on top of a node little corner box + * @method isOverNodeBox + **/ + LGraphCanvas.prototype.isOverNodeBox = function(node, canvasx, canvasy) { + var title_height = LiteGraph.NODE_TITLE_HEIGHT; + if ( + isInsideRectangle( + canvasx, + canvasy, + node.pos[0] + 2, + node.pos[1] + 2 - title_height, + title_height - 4, + title_height - 4 + ) + ) { + return true; + } + return false; + }; + + /** + * returns true if a position (in graph space) is on top of a node input slot + * @method isOverNodeInput + **/ + LGraphCanvas.prototype.isOverNodeInput = function( + node, + canvasx, + canvasy, + slot_pos + ) { + if (node.inputs) { + for (var i = 0, l = node.inputs.length; i < l; ++i) { + var input = node.inputs[i]; + var link_pos = node.getConnectionPos(true, i); + var is_inside = false; + if (node.horizontal) { + is_inside = isInsideRectangle( + canvasx, + canvasy, + link_pos[0] - 5, + link_pos[1] - 10, + 10, + 20 + ); + } else { + is_inside = isInsideRectangle( + canvasx, + canvasy, + link_pos[0] - 10, + link_pos[1] - 5, + 40, + 10 + ); + } + if (is_inside) { + if (slot_pos) { + slot_pos[0] = link_pos[0]; + slot_pos[1] = link_pos[1]; + } + return i; + } + } + } + return -1; + }; + + /** + * process a key event + * @method processKey + **/ + LGraphCanvas.prototype.processKey = function(e) { + if (!this.graph) { + return; + } + + var block_default = false; + //console.log(e); //debug + + if (e.target.localName == "input") { + return; + } + + if (e.type == "keydown") { + if (e.keyCode == 32) { + //esc + this.dragging_canvas = true; + block_default = true; + } + + //select all Control A + if (e.keyCode == 65 && e.ctrlKey) { + this.selectNodes(); + block_default = true; + } + + if (e.code == "KeyC" && (e.metaKey || e.ctrlKey) && !e.shiftKey) { + //copy + if (this.selected_nodes) { + this.copyToClipboard(); + block_default = true; + } + } + + if (e.code == "KeyV" && (e.metaKey || e.ctrlKey) && !e.shiftKey) { + //paste + this.pasteFromClipboard(); + } + + //delete or backspace + if (e.keyCode == 46 || e.keyCode == 8) { + if ( + e.target.localName != "input" && + e.target.localName != "textarea" + ) { + this.deleteSelectedNodes(); + block_default = true; + } + } + + //collapse + //... + + //TODO + if (this.selected_nodes) { + for (var i in this.selected_nodes) { + if (this.selected_nodes[i].onKeyDown) { + this.selected_nodes[i].onKeyDown(e); + } + } + } + } else if (e.type == "keyup") { + if (e.keyCode == 32) { + this.dragging_canvas = false; + } + + if (this.selected_nodes) { + for (var i in this.selected_nodes) { + if (this.selected_nodes[i].onKeyUp) { + this.selected_nodes[i].onKeyUp(e); + } + } + } + } + + this.graph.change(); + + if (block_default) { + e.preventDefault(); + e.stopImmediatePropagation(); + return false; + } + }; + + LGraphCanvas.prototype.copyToClipboard = function() { + var clipboard_info = { + nodes: [], + links: [] + }; + var index = 0; + var selected_nodes_array = []; + for (var i in this.selected_nodes) { + var node = this.selected_nodes[i]; + node._relative_id = index; + selected_nodes_array.push(node); + index += 1; + } + + for (var i = 0; i < selected_nodes_array.length; ++i) { + var node = selected_nodes_array[i]; + var cloned = node.clone(); + if(!cloned) + { + console.warn("node type not found: " + node.type ); + continue; + } + clipboard_info.nodes.push(cloned.serialize()); + if (node.inputs && node.inputs.length) { + for (var j = 0; j < node.inputs.length; ++j) { + var input = node.inputs[j]; + if (!input || input.link == null) { + continue; + } + var link_info = this.graph.links[input.link]; + if (!link_info) { + continue; + } + var target_node = this.graph.getNodeById( + link_info.origin_id + ); + if (!target_node || !this.selected_nodes[target_node.id]) { + //improve this by allowing connections to non-selected nodes + continue; + } //not selected + clipboard_info.links.push([ + target_node._relative_id, + link_info.origin_slot, //j, + node._relative_id, + link_info.target_slot + ]); + } + } + } + localStorage.setItem( + "litegrapheditor_clipboard", + JSON.stringify(clipboard_info) + ); + }; + + LGraphCanvas.prototype.pasteFromClipboard = function() { + var data = localStorage.getItem("litegrapheditor_clipboard"); + if (!data) { + return; + } + + //create nodes + var clipboard_info = JSON.parse(data); + var nodes = []; + for (var i = 0; i < clipboard_info.nodes.length; ++i) { + var node_data = clipboard_info.nodes[i]; + var node = LiteGraph.createNode(node_data.type); + if (node) { + node.configure(node_data); + node.pos[0] += 5; + node.pos[1] += 5; + this.graph.add(node); + nodes.push(node); + } + } + + //create links + for (var i = 0; i < clipboard_info.links.length; ++i) { + var link_info = clipboard_info.links[i]; + var origin_node = nodes[link_info[0]]; + var target_node = nodes[link_info[2]]; + if( origin_node && target_node ) + origin_node.connect(link_info[1], target_node, link_info[3]); + else + console.warn("Warning, nodes missing on pasting"); + } + + this.selectNodes(nodes); + }; + + /** + * process a item drop event on top the canvas + * @method processDrop + **/ + LGraphCanvas.prototype.processDrop = function(e) { + e.preventDefault(); + this.adjustMouseEvent(e); + + var pos = [e.canvasX, e.canvasY]; + var node = this.graph ? this.graph.getNodeOnPos(pos[0], pos[1]) : null; + + if (!node) { + var r = null; + if (this.onDropItem) { + r = this.onDropItem(event); + } + if (!r) { + this.checkDropItem(e); + } + return; + } + + if (node.onDropFile || node.onDropData) { + var files = e.dataTransfer.files; + if (files && files.length) { + for (var i = 0; i < files.length; i++) { + var file = e.dataTransfer.files[0]; + var filename = file.name; + var ext = LGraphCanvas.getFileExtension(filename); + //console.log(file); + + if (node.onDropFile) { + node.onDropFile(file); + } + + if (node.onDropData) { + //prepare reader + var reader = new FileReader(); + reader.onload = function(event) { + //console.log(event.target); + var data = event.target.result; + node.onDropData(data, filename, file); + }; + + //read data + var type = file.type.split("/")[0]; + if (type == "text" || type == "") { + reader.readAsText(file); + } else if (type == "image") { + reader.readAsDataURL(file); + } else { + reader.readAsArrayBuffer(file); + } + } + } + } + } + + if (node.onDropItem) { + if (node.onDropItem(event)) { + return true; + } + } + + if (this.onDropItem) { + return this.onDropItem(event); + } + + return false; + }; + + //called if the graph doesn't have a default drop item behaviour + LGraphCanvas.prototype.checkDropItem = function(e) { + if (e.dataTransfer.files.length) { + var file = e.dataTransfer.files[0]; + var ext = LGraphCanvas.getFileExtension(file.name).toLowerCase(); + var nodetype = LiteGraph.node_types_by_file_extension[ext]; + if (nodetype) { + var node = LiteGraph.createNode(nodetype.type); + node.pos = [e.canvasX, e.canvasY]; + this.graph.add(node); + if (node.onDropFile) { + node.onDropFile(file); + } + } + } + }; + + LGraphCanvas.prototype.processNodeDblClicked = function(n) { + if (this.onShowNodePanel) { + this.onShowNodePanel(n); + } + + if (this.onNodeDblClicked) { + this.onNodeDblClicked(n); + } + + this.setDirty(true); + }; + + LGraphCanvas.prototype.processNodeSelected = function(node, e) { + this.selectNode(node, e && e.shiftKey); + if (this.onNodeSelected) { + this.onNodeSelected(node); + } + }; + + /** + * selects a given node (or adds it to the current selection) + * @method selectNode + **/ + LGraphCanvas.prototype.selectNode = function( + node, + add_to_current_selection + ) { + if (node == null) { + this.deselectAllNodes(); + } else { + this.selectNodes([node], add_to_current_selection); + } + }; + + /** + * selects several nodes (or adds them to the current selection) + * @method selectNodes + **/ + LGraphCanvas.prototype.selectNodes = function( + nodes, + add_to_current_selection + ) { + if (!add_to_current_selection) { + this.deselectAllNodes(); + } + + nodes = nodes || this.graph._nodes; + for (var i = 0; i < nodes.length; ++i) { + var node = nodes[i]; + if (node.is_selected) { + continue; + } + + if (!node.is_selected && node.onSelected) { + node.onSelected(); + } + node.is_selected = true; + this.selected_nodes[node.id] = node; + + if (node.inputs) { + for (var j = 0; j < node.inputs.length; ++j) { + this.highlighted_links[node.inputs[j].link] = true; + } + } + if (node.outputs) { + for (var j = 0; j < node.outputs.length; ++j) { + var out = node.outputs[j]; + if (out.links) { + for (var k = 0; k < out.links.length; ++k) { + this.highlighted_links[out.links[k]] = true; + } + } + } + } + } + + if( this.onSelectionChange ) + this.onSelectionChange( this.selected_nodes ); + + this.setDirty(true); + }; + + /** + * removes a node from the current selection + * @method deselectNode + **/ + LGraphCanvas.prototype.deselectNode = function(node) { + if (!node.is_selected) { + return; + } + if (node.onDeselected) { + node.onDeselected(); + } + node.is_selected = false; + + if (this.onNodeDeselected) { + this.onNodeDeselected(node); + } + + //remove highlighted + if (node.inputs) { + for (var i = 0; i < node.inputs.length; ++i) { + delete this.highlighted_links[node.inputs[i].link]; + } + } + if (node.outputs) { + for (var i = 0; i < node.outputs.length; ++i) { + var out = node.outputs[i]; + if (out.links) { + for (var j = 0; j < out.links.length; ++j) { + delete this.highlighted_links[out.links[j]]; + } + } + } + } + }; + + /** + * removes all nodes from the current selection + * @method deselectAllNodes + **/ + LGraphCanvas.prototype.deselectAllNodes = function() { + if (!this.graph) { + return; + } + var nodes = this.graph._nodes; + for (var i = 0, l = nodes.length; i < l; ++i) { + var node = nodes[i]; + if (!node.is_selected) { + continue; + } + if (node.onDeselected) { + node.onDeselected(); + } + node.is_selected = false; + if (this.onNodeDeselected) { + this.onNodeDeselected(node); + } + } + this.selected_nodes = {}; + this.current_node = null; + this.highlighted_links = {}; + if( this.onSelectionChange ) + this.onSelectionChange( this.selected_nodes ); + this.setDirty(true); + }; + + /** + * deletes all nodes in the current selection from the graph + * @method deleteSelectedNodes + **/ + LGraphCanvas.prototype.deleteSelectedNodes = function() { + for (var i in this.selected_nodes) { + var node = this.selected_nodes[i]; + //autoconnect when possible (very basic, only takes into account first input-output) + if(node.inputs && node.inputs.length && node.outputs && node.outputs.length && LiteGraph.isValidConnection( node.inputs[0].type, node.outputs[0].type ) && node.inputs[0].link && node.outputs[0].links && node.outputs[0].links.length ) + { + var input_link = node.graph.links[ node.inputs[0].link ]; + var output_link = node.graph.links[ node.outputs[0].links[0] ]; + var input_node = node.getInputNode(0); + var output_node = node.getOutputNodes(0)[0]; + if(input_node && output_node) + input_node.connect( input_link.origin_slot, output_node, output_link.target_slot ); + } + this.graph.remove(node); + if (this.onNodeDeselected) { + this.onNodeDeselected(node); + } + } + this.selected_nodes = {}; + this.current_node = null; + this.highlighted_links = {}; + this.setDirty(true); + }; + + /** + * centers the camera on a given node + * @method centerOnNode + **/ + LGraphCanvas.prototype.centerOnNode = function(node) { + this.ds.offset[0] = + -node.pos[0] - + node.size[0] * 0.5 + + (this.canvas.width * 0.5) / this.ds.scale; + this.ds.offset[1] = + -node.pos[1] - + node.size[1] * 0.5 + + (this.canvas.height * 0.5) / this.ds.scale; + this.setDirty(true, true); + }; + + /** + * adds some useful properties to a mouse event, like the position in graph coordinates + * @method adjustMouseEvent + **/ + LGraphCanvas.prototype.adjustMouseEvent = function(e) { + if (this.canvas) { + var b = this.canvas.getBoundingClientRect(); + e.localX = e.clientX - b.left; + e.localY = e.clientY - b.top; + } else { + e.localX = e.clientX; + e.localY = e.clientY; + } + + e.deltaX = e.localX - this.last_mouse_position[0]; + e.deltaY = e.localY - this.last_mouse_position[1]; + + this.last_mouse_position[0] = e.localX; + this.last_mouse_position[1] = e.localY; + + e.canvasX = e.localX / this.ds.scale - this.ds.offset[0]; + e.canvasY = e.localY / this.ds.scale - this.ds.offset[1]; + }; + + /** + * changes the zoom level of the graph (default is 1), you can pass also a place used to pivot the zoom + * @method setZoom + **/ + LGraphCanvas.prototype.setZoom = function(value, zooming_center) { + this.ds.changeScale(value, zooming_center); + /* + if(!zooming_center && this.canvas) + zooming_center = [this.canvas.width * 0.5,this.canvas.height * 0.5]; + + var center = this.convertOffsetToCanvas( zooming_center ); + + this.ds.scale = value; + + if(this.scale > this.max_zoom) + this.scale = this.max_zoom; + else if(this.scale < this.min_zoom) + this.scale = this.min_zoom; + + var new_center = this.convertOffsetToCanvas( zooming_center ); + var delta_offset = [new_center[0] - center[0], new_center[1] - center[1]]; + + this.offset[0] += delta_offset[0]; + this.offset[1] += delta_offset[1]; + */ + + this.dirty_canvas = true; + this.dirty_bgcanvas = true; + }; + + /** + * converts a coordinate from graph coordinates to canvas2D coordinates + * @method convertOffsetToCanvas + **/ + LGraphCanvas.prototype.convertOffsetToCanvas = function(pos, out) { + return this.ds.convertOffsetToCanvas(pos, out); + }; + + /** + * converts a coordinate from Canvas2D coordinates to graph space + * @method convertCanvasToOffset + **/ + LGraphCanvas.prototype.convertCanvasToOffset = function(pos, out) { + return this.ds.convertCanvasToOffset(pos, out); + }; + + //converts event coordinates from canvas2D to graph coordinates + LGraphCanvas.prototype.convertEventToCanvasOffset = function(e) { + var rect = this.canvas.getBoundingClientRect(); + return this.convertCanvasToOffset([ + e.clientX - rect.left, + e.clientY - rect.top + ]); + }; + + /** + * brings a node to front (above all other nodes) + * @method bringToFront + **/ + LGraphCanvas.prototype.bringToFront = function(node) { + var i = this.graph._nodes.indexOf(node); + if (i == -1) { + return; + } + + this.graph._nodes.splice(i, 1); + this.graph._nodes.push(node); + }; + + /** + * sends a node to the back (below all other nodes) + * @method sendToBack + **/ + LGraphCanvas.prototype.sendToBack = function(node) { + var i = this.graph._nodes.indexOf(node); + if (i == -1) { + return; + } + + this.graph._nodes.splice(i, 1); + this.graph._nodes.unshift(node); + }; + + /* Interaction */ + + /* LGraphCanvas render */ + var temp = new Float32Array(4); + + /** + * checks which nodes are visible (inside the camera area) + * @method computeVisibleNodes + **/ + LGraphCanvas.prototype.computeVisibleNodes = function(nodes, out) { + var visible_nodes = out || []; + visible_nodes.length = 0; + nodes = nodes || this.graph._nodes; + for (var i = 0, l = nodes.length; i < l; ++i) { + var n = nodes[i]; + + //skip rendering nodes in live mode + if (this.live_mode && !n.onDrawBackground && !n.onDrawForeground) { + continue; + } + + if (!overlapBounding(this.visible_area, n.getBounding(temp))) { + continue; + } //out of the visible area + + visible_nodes.push(n); + } + return visible_nodes; + }; + + /** + * renders the whole canvas content, by rendering in two separated canvas, one containing the background grid and the connections, and one containing the nodes) + * @method draw + **/ + LGraphCanvas.prototype.draw = function(force_canvas, force_bgcanvas) { + if (!this.canvas) { + return; + } + + //fps counting + var now = LiteGraph.getTime(); + this.render_time = (now - this.last_draw_time) * 0.001; + this.last_draw_time = now; + + if (this.graph) { + this.ds.computeVisibleArea(); + } + + if ( + this.dirty_bgcanvas || + force_bgcanvas || + this.always_render_background || + (this.graph && + this.graph._last_trigger_time && + now - this.graph._last_trigger_time < 1000) + ) { + this.drawBackCanvas(); + } + + if (this.dirty_canvas || force_canvas) { + this.drawFrontCanvas(); + } + + this.fps = this.render_time ? 1.0 / this.render_time : 0; + this.frame += 1; + }; + + /** + * draws the front canvas (the one containing all the nodes) + * @method drawFrontCanvas + **/ + LGraphCanvas.prototype.drawFrontCanvas = function() { + this.dirty_canvas = false; + + if (!this.ctx) { + this.ctx = this.bgcanvas.getContext("2d"); + } + var ctx = this.ctx; + if (!ctx) { + //maybe is using webgl... + return; + } + + if (ctx.start2D) { + ctx.start2D(); + } + + var canvas = this.canvas; + + //reset in case of error + ctx.restore(); + ctx.setTransform(1, 0, 0, 1, 0, 0); + + //clip dirty area if there is one, otherwise work in full canvas + if (this.dirty_area) { + ctx.save(); + ctx.beginPath(); + ctx.rect( + this.dirty_area[0], + this.dirty_area[1], + this.dirty_area[2], + this.dirty_area[3] + ); + ctx.clip(); + } + + //clear + //canvas.width = canvas.width; + if (this.clear_background) { + ctx.clearRect(0, 0, canvas.width, canvas.height); + } + + //draw bg canvas + if (this.bgcanvas == this.canvas) { + this.drawBackCanvas(); + } else { + ctx.drawImage(this.bgcanvas, 0, 0); + } + + //rendering + if (this.onRender) { + this.onRender(canvas, ctx); + } + + //info widget + if (this.show_info) { + this.renderInfo(ctx); + } + + if (this.graph) { + //apply transformations + ctx.save(); + this.ds.toCanvasContext(ctx); + + //draw nodes + var drawn_nodes = 0; + var visible_nodes = this.computeVisibleNodes( + null, + this.visible_nodes + ); + + for (var i = 0; i < visible_nodes.length; ++i) { + var node = visible_nodes[i]; + + //transform coords system + ctx.save(); + ctx.translate(node.pos[0], node.pos[1]); + + //Draw + this.drawNode(node, ctx); + drawn_nodes += 1; + + //Restore + ctx.restore(); + } + + //on top (debug) + if (this.render_execution_order) { + this.drawExecutionOrder(ctx); + } + + //connections ontop? + if (this.graph.config.links_ontop) { + if (!this.live_mode) { + this.drawConnections(ctx); + } + } + + //current connection (the one being dragged by the mouse) + if (this.connecting_pos != null) { + ctx.lineWidth = this.connections_width; + var link_color = null; + + switch (this.connecting_output.type) { + case LiteGraph.EVENT: + link_color = LiteGraph.EVENT_LINK_COLOR; + break; + default: + link_color = LiteGraph.CONNECTING_LINK_COLOR; + } + + //the connection being dragged by the mouse + this.renderLink( + ctx, + this.connecting_pos, + [this.canvas_mouse[0], this.canvas_mouse[1]], + null, + false, + null, + link_color, + this.connecting_output.dir || + (this.connecting_node.horizontal + ? LiteGraph.DOWN + : LiteGraph.RIGHT), + LiteGraph.CENTER + ); + + ctx.beginPath(); + if ( + this.connecting_output.type === LiteGraph.EVENT || + this.connecting_output.shape === LiteGraph.BOX_SHAPE + ) { + ctx.rect( + this.connecting_pos[0] - 6 + 0.5, + this.connecting_pos[1] - 5 + 0.5, + 14, + 10 + ); + } else { + ctx.arc( + this.connecting_pos[0], + this.connecting_pos[1], + 4, + 0, + Math.PI * 2 + ); + } + ctx.fill(); + + ctx.fillStyle = "#ffcc00"; + if (this._highlight_input) { + ctx.beginPath(); + ctx.arc( + this._highlight_input[0], + this._highlight_input[1], + 6, + 0, + Math.PI * 2 + ); + ctx.fill(); + } + } + + //the selection rectangle + if (this.dragging_rectangle) { + ctx.strokeStyle = "#FFF"; + ctx.strokeRect( + this.dragging_rectangle[0], + this.dragging_rectangle[1], + this.dragging_rectangle[2], + this.dragging_rectangle[3] + ); + } + + //on top of link center + if(this.over_link_center && this.render_link_tooltip) + this.drawLinkTooltip( ctx, this.over_link_center ); + else + if(this.onDrawLinkTooltip) //to remove + this.onDrawLinkTooltip(ctx,null); + + //custom info + if (this.onDrawForeground) { + this.onDrawForeground(ctx, this.visible_rect); + } + + ctx.restore(); + } + + if (this.onDrawOverlay) { + this.onDrawOverlay(ctx); + } + + if (this.dirty_area) { + ctx.restore(); + //this.dirty_area = null; + } + + if (ctx.finish2D) { + //this is a function I use in webgl renderer + ctx.finish2D(); + } + }; + + /** + * draws some useful stats in the corner of the canvas + * @method renderInfo + **/ + LGraphCanvas.prototype.renderInfo = function(ctx, x, y) { + x = x || 0; + y = y || 0; + + ctx.save(); + ctx.translate(x, y); + + ctx.font = "10px Arial"; + ctx.fillStyle = "#888"; + if (this.graph) { + ctx.fillText( "T: " + this.graph.globaltime.toFixed(2) + "s", 5, 13 * 1 ); + ctx.fillText("I: " + this.graph.iteration, 5, 13 * 2 ); + ctx.fillText("N: " + this.graph._nodes.length + " [" + this.visible_nodes.length + "]", 5, 13 * 3 ); + ctx.fillText("V: " + this.graph._version, 5, 13 * 4); + ctx.fillText("FPS:" + this.fps.toFixed(2), 5, 13 * 5); + } else { + ctx.fillText("No graph selected", 5, 13 * 1); + } + ctx.restore(); + }; + + /** + * draws the back canvas (the one containing the background and the connections) + * @method drawBackCanvas + **/ + LGraphCanvas.prototype.drawBackCanvas = function() { + var canvas = this.bgcanvas; + if ( + canvas.width != this.canvas.width || + canvas.height != this.canvas.height + ) { + canvas.width = this.canvas.width; + canvas.height = this.canvas.height; + } + + if (!this.bgctx) { + this.bgctx = this.bgcanvas.getContext("2d"); + } + var ctx = this.bgctx; + if (ctx.start) { + ctx.start(); + } + + //clear + if (this.clear_background) { + ctx.clearRect(0, 0, canvas.width, canvas.height); + } + + if (this._graph_stack && this._graph_stack.length) { + ctx.save(); + var parent_graph = this._graph_stack[this._graph_stack.length - 1]; + var subgraph_node = this.graph._subgraph_node; + ctx.strokeStyle = subgraph_node.bgcolor; + ctx.lineWidth = 10; + ctx.strokeRect(1, 1, canvas.width - 2, canvas.height - 2); + ctx.lineWidth = 1; + ctx.font = "40px Arial"; + ctx.textAlign = "center"; + ctx.fillStyle = subgraph_node.bgcolor || "#AAA"; + var title = ""; + for (var i = 1; i < this._graph_stack.length; ++i) { + title += + this._graph_stack[i]._subgraph_node.getTitle() + " >> "; + } + ctx.fillText( + title + subgraph_node.getTitle(), + canvas.width * 0.5, + 40 + ); + ctx.restore(); + } + + var bg_already_painted = false; + if (this.onRenderBackground) { + bg_already_painted = this.onRenderBackground(canvas, ctx); + } + + //reset in case of error + ctx.restore(); + ctx.setTransform(1, 0, 0, 1, 0, 0); + this.visible_links.length = 0; + + if (this.graph) { + //apply transformations + ctx.save(); + this.ds.toCanvasContext(ctx); + + //render BG + if ( + this.background_image && + this.ds.scale > 0.5 && + !bg_already_painted + ) { + if (this.zoom_modify_alpha) { + ctx.globalAlpha = + (1.0 - 0.5 / this.ds.scale) * this.editor_alpha; + } else { + ctx.globalAlpha = this.editor_alpha; + } + ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.imageSmoothingEnabled = false; + if ( + !this._bg_img || + this._bg_img.name != this.background_image + ) { + this._bg_img = new Image(); + this._bg_img.name = this.background_image; + this._bg_img.src = this.background_image; + var that = this; + this._bg_img.onload = function() { + that.draw(true, true); + }; + } + + var pattern = null; + if (this._pattern == null && this._bg_img.width > 0) { + pattern = ctx.createPattern(this._bg_img, "repeat"); + this._pattern_img = this._bg_img; + this._pattern = pattern; + } else { + pattern = this._pattern; + } + if (pattern) { + ctx.fillStyle = pattern; + ctx.fillRect( + this.visible_area[0], + this.visible_area[1], + this.visible_area[2], + this.visible_area[3] + ); + ctx.fillStyle = "transparent"; + } + + ctx.globalAlpha = 1.0; + ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.imageSmoothingEnabled = true; + } + + //groups + if (this.graph._groups.length && !this.live_mode) { + this.drawGroups(canvas, ctx); + } + + if (this.onDrawBackground) { + this.onDrawBackground(ctx, this.visible_area); + } + if (this.onBackgroundRender) { + //LEGACY + console.error( + "WARNING! onBackgroundRender deprecated, now is named onDrawBackground " + ); + this.onBackgroundRender = null; + } + + //DEBUG: show clipping area + //ctx.fillStyle = "red"; + //ctx.fillRect( this.visible_area[0] + 10, this.visible_area[1] + 10, this.visible_area[2] - 20, this.visible_area[3] - 20); + + //bg + if (this.render_canvas_border) { + ctx.strokeStyle = "#235"; + ctx.strokeRect(0, 0, canvas.width, canvas.height); + } + + if (this.render_connections_shadows) { + ctx.shadowColor = "#000"; + ctx.shadowOffsetX = 0; + ctx.shadowOffsetY = 0; + ctx.shadowBlur = 6; + } else { + ctx.shadowColor = "rgba(0,0,0,0)"; + } + + //draw connections + if (!this.live_mode) { + this.drawConnections(ctx); + } + + ctx.shadowColor = "rgba(0,0,0,0)"; + + //restore state + ctx.restore(); + } + + if (ctx.finish) { + ctx.finish(); + } + + this.dirty_bgcanvas = false; + this.dirty_canvas = true; //to force to repaint the front canvas with the bgcanvas + }; + + var temp_vec2 = new Float32Array(2); + + /** + * draws the given node inside the canvas + * @method drawNode + **/ + LGraphCanvas.prototype.drawNode = function(node, ctx) { + var glow = false; + this.current_node = node; + + var color = + node.color || + node.constructor.color || + LiteGraph.NODE_DEFAULT_COLOR; + var bgcolor = + node.bgcolor || + node.constructor.bgcolor || + LiteGraph.NODE_DEFAULT_BGCOLOR; + + //shadow and glow + if (node.mouseOver) { + glow = true; + } + + var low_quality = this.ds.scale < 0.6; //zoomed out + + //only render if it forces it to do it + if (this.live_mode) { + if (!node.flags.collapsed) { + ctx.shadowColor = "transparent"; + if (node.onDrawForeground) { + node.onDrawForeground(ctx, this, this.canvas); + } + } + return; + } + + var editor_alpha = this.editor_alpha; + ctx.globalAlpha = editor_alpha; + + if (this.render_shadows && !low_quality) { + ctx.shadowColor = LiteGraph.DEFAULT_SHADOW_COLOR; + ctx.shadowOffsetX = 2 * this.ds.scale; + ctx.shadowOffsetY = 2 * this.ds.scale; + ctx.shadowBlur = 3 * this.ds.scale; + } else { + ctx.shadowColor = "transparent"; + } + + //custom draw collapsed method (draw after shadows because they are affected) + if ( + node.flags.collapsed && + node.onDrawCollapsed && + node.onDrawCollapsed(ctx, this) == true + ) { + return; + } + + //clip if required (mask) + var shape = node._shape || LiteGraph.BOX_SHAPE; + var size = temp_vec2; + temp_vec2.set(node.size); + var horizontal = node.horizontal; // || node.flags.horizontal; + + if (node.flags.collapsed) { + ctx.font = this.inner_text_font; + var title = node.getTitle ? node.getTitle() : node.title; + if (title != null) { + node._collapsed_width = Math.min( + node.size[0], + ctx.measureText(title).width + + LiteGraph.NODE_TITLE_HEIGHT * 2 + ); //LiteGraph.NODE_COLLAPSED_WIDTH; + size[0] = node._collapsed_width; + size[1] = 0; + } + } + + if (node.clip_area) { + //Start clipping + ctx.save(); + ctx.beginPath(); + if (shape == LiteGraph.BOX_SHAPE) { + ctx.rect(0, 0, size[0], size[1]); + } else if (shape == LiteGraph.ROUND_SHAPE) { + ctx.roundRect(0, 0, size[0], size[1], 10); + } else if (shape == LiteGraph.CIRCLE_SHAPE) { + ctx.arc( + size[0] * 0.5, + size[1] * 0.5, + size[0] * 0.5, + 0, + Math.PI * 2 + ); + } + ctx.clip(); + } + + //draw shape + if (node.has_errors) { + bgcolor = "red"; + } + this.drawNodeShape( + node, + ctx, + size, + color, + bgcolor, + node.is_selected, + node.mouseOver + ); + ctx.shadowColor = "transparent"; + + //draw foreground + if (node.onDrawForeground) { + node.onDrawForeground(ctx, this, this.canvas); + } + + //connection slots + ctx.textAlign = horizontal ? "center" : "left"; + ctx.font = this.inner_text_font; + + var render_text = !low_quality; + + var out_slot = this.connecting_output; + ctx.lineWidth = 1; + + var max_y = 0; + var slot_pos = new Float32Array(2); //to reuse + + //render inputs and outputs + if (!node.flags.collapsed) { + //input connection slots + if (node.inputs) { + for (var i = 0; i < node.inputs.length; i++) { + var slot = node.inputs[i]; + + ctx.globalAlpha = editor_alpha; + //change opacity of incompatible slots when dragging a connection + if ( this.connecting_node && !LiteGraph.isValidConnection( slot.type , out_slot.type) ) { + ctx.globalAlpha = 0.4 * editor_alpha; + } + + ctx.fillStyle = + slot.link != null + ? slot.color_on || + this.default_connection_color.input_on + : slot.color_off || + this.default_connection_color.input_off; + + var pos = node.getConnectionPos(true, i, slot_pos); + pos[0] -= node.pos[0]; + pos[1] -= node.pos[1]; + if (max_y < pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5) { + max_y = pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5; + } + + ctx.beginPath(); + + if ( + slot.type === LiteGraph.EVENT || + slot.shape === LiteGraph.BOX_SHAPE + ) { + if (horizontal) { + ctx.rect( + pos[0] - 5 + 0.5, + pos[1] - 8 + 0.5, + 10, + 14 + ); + } else { + ctx.rect( + pos[0] - 6 + 0.5, + pos[1] - 5 + 0.5, + 14, + 10 + ); + } + } else if (slot.shape === LiteGraph.ARROW_SHAPE) { + ctx.moveTo(pos[0] + 8, pos[1] + 0.5); + ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5); + ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5); + ctx.closePath(); + } else { + if(low_quality) + ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); //faster + else + ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); + } + ctx.fill(); + + //render name + if (render_text) { + var text = slot.label != null ? slot.label : slot.name; + if (text) { + ctx.fillStyle = LiteGraph.NODE_TEXT_COLOR; + if (horizontal || slot.dir == LiteGraph.UP) { + ctx.fillText(text, pos[0], pos[1] - 10); + } else { + ctx.fillText(text, pos[0] + 10, pos[1] + 5); + } + } + } + } + } + + //output connection slots + if (this.connecting_node) { + ctx.globalAlpha = 0.4 * editor_alpha; + } + + ctx.textAlign = horizontal ? "center" : "right"; + ctx.strokeStyle = "black"; + if (node.outputs) { + for (var i = 0; i < node.outputs.length; i++) { + var slot = node.outputs[i]; + + var pos = node.getConnectionPos(false, i, slot_pos); + pos[0] -= node.pos[0]; + pos[1] -= node.pos[1]; + if (max_y < pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5) { + max_y = pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5; + } + + ctx.fillStyle = + slot.links && slot.links.length + ? slot.color_on || + this.default_connection_color.output_on + : slot.color_off || + this.default_connection_color.output_off; + ctx.beginPath(); + //ctx.rect( node.size[0] - 14,i*14,10,10); + + if ( + slot.type === LiteGraph.EVENT || + slot.shape === LiteGraph.BOX_SHAPE + ) { + if (horizontal) { + ctx.rect( + pos[0] - 5 + 0.5, + pos[1] - 8 + 0.5, + 10, + 14 + ); + } else { + ctx.rect( + pos[0] - 6 + 0.5, + pos[1] - 5 + 0.5, + 14, + 10 + ); + } + } else if (slot.shape === LiteGraph.ARROW_SHAPE) { + ctx.moveTo(pos[0] + 8, pos[1] + 0.5); + ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5); + ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5); + ctx.closePath(); + } else { + if(low_quality) + ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); + else + ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); + } + + //trigger + //if(slot.node_id != null && slot.slot == -1) + // ctx.fillStyle = "#F85"; + + //if(slot.links != null && slot.links.length) + ctx.fill(); + if(!low_quality) + ctx.stroke(); + + //render output name + if (render_text) { + var text = slot.label != null ? slot.label : slot.name; + if (text) { + ctx.fillStyle = LiteGraph.NODE_TEXT_COLOR; + if (horizontal || slot.dir == LiteGraph.DOWN) { + ctx.fillText(text, pos[0], pos[1] - 8); + } else { + ctx.fillText(text, pos[0] - 10, pos[1] + 5); + } + } + } + } + } + + ctx.textAlign = "left"; + ctx.globalAlpha = 1; + + if (node.widgets) { + var widgets_y = max_y; + if (horizontal || node.widgets_up) { + widgets_y = 2; + } + if( node.widgets_start_y != null ) + widgets_y = node.widgets_start_y; + this.drawNodeWidgets( + node, + widgets_y, + ctx, + this.node_widget && this.node_widget[0] == node + ? this.node_widget[1] + : null + ); + } + } else if (this.render_collapsed_slots) { + //if collapsed + var input_slot = null; + var output_slot = null; + + //get first connected slot to render + if (node.inputs) { + for (var i = 0; i < node.inputs.length; i++) { + var slot = node.inputs[i]; + if (slot.link == null) { + continue; + } + input_slot = slot; + break; + } + } + if (node.outputs) { + for (var i = 0; i < node.outputs.length; i++) { + var slot = node.outputs[i]; + if (!slot.links || !slot.links.length) { + continue; + } + output_slot = slot; + } + } + + if (input_slot) { + var x = 0; + var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; //center + if (horizontal) { + x = node._collapsed_width * 0.5; + y = -LiteGraph.NODE_TITLE_HEIGHT; + } + ctx.fillStyle = "#686"; + ctx.beginPath(); + if ( + slot.type === LiteGraph.EVENT || + slot.shape === LiteGraph.BOX_SHAPE + ) { + ctx.rect(x - 7 + 0.5, y - 4, 14, 8); + } else if (slot.shape === LiteGraph.ARROW_SHAPE) { + ctx.moveTo(x + 8, y); + ctx.lineTo(x + -4, y - 4); + ctx.lineTo(x + -4, y + 4); + ctx.closePath(); + } else { + ctx.arc(x, y, 4, 0, Math.PI * 2); + } + ctx.fill(); + } + + if (output_slot) { + var x = node._collapsed_width; + var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; //center + if (horizontal) { + x = node._collapsed_width * 0.5; + y = 0; + } + ctx.fillStyle = "#686"; + ctx.strokeStyle = "black"; + ctx.beginPath(); + if ( + slot.type === LiteGraph.EVENT || + slot.shape === LiteGraph.BOX_SHAPE + ) { + ctx.rect(x - 7 + 0.5, y - 4, 14, 8); + } else if (slot.shape === LiteGraph.ARROW_SHAPE) { + ctx.moveTo(x + 6, y); + ctx.lineTo(x - 6, y - 4); + ctx.lineTo(x - 6, y + 4); + ctx.closePath(); + } else { + ctx.arc(x, y, 4, 0, Math.PI * 2); + } + ctx.fill(); + //ctx.stroke(); + } + } + + if (node.clip_area) { + ctx.restore(); + } + + ctx.globalAlpha = 1.0; + }; + + //used by this.over_link_center + LGraphCanvas.prototype.drawLinkTooltip = function( ctx, link ) + { + var pos = link._pos; + ctx.fillStyle = "black"; + ctx.beginPath(); + ctx.arc( pos[0], pos[1], 3, 0, Math.PI * 2 ); + ctx.fill(); + + if(link.data == null) + return; + + if(this.onDrawLinkTooltip) + if( this.onDrawLinkTooltip(ctx,link,this) == true ) + return; + + var data = link.data; + var text = null; + + if( data.constructor === Number ) + text = data.toFixed(2); + else if( data.constructor === String ) + text = "\"" + data + "\""; + else if( data.constructor === Boolean ) + text = String(data); + else if (data.toToolTip) + text = data.toToolTip(); + else + text = "[" + data.constructor.name + "]"; + + if(text == null) + return; + text = text.substr(0,30); //avoid weird + + ctx.font = "14px Courier New"; + var info = ctx.measureText(text); + var w = info.width + 20; + var h = 24; + ctx.shadowColor = "black"; + ctx.shadowOffsetX = 2; + ctx.shadowOffsetY = 2; + ctx.shadowBlur = 3; + ctx.fillStyle = "#454"; + ctx.beginPath(); + ctx.roundRect( pos[0] - w*0.5, pos[1] - 15 - h, w, h,3, 3); + ctx.moveTo( pos[0] - 10, pos[1] - 15 ); + ctx.lineTo( pos[0] + 10, pos[1] - 15 ); + ctx.lineTo( pos[0], pos[1] - 5 ); + ctx.fill(); + ctx.shadowColor = "transparent"; + ctx.textAlign = "center"; + ctx.fillStyle = "#CEC"; + ctx.fillText(text, pos[0], pos[1] - 15 - h * 0.3); + } + + /** + * draws the shape of the given node in the canvas + * @method drawNodeShape + **/ + var tmp_area = new Float32Array(4); + + LGraphCanvas.prototype.drawNodeShape = function( + node, + ctx, + size, + fgcolor, + bgcolor, + selected, + mouse_over + ) { + //bg rect + ctx.strokeStyle = fgcolor; + ctx.fillStyle = bgcolor; + + var title_height = LiteGraph.NODE_TITLE_HEIGHT; + var low_quality = this.ds.scale < 0.5; + + //render node area depending on shape + var shape = + node._shape || node.constructor.shape || LiteGraph.ROUND_SHAPE; + + var title_mode = node.constructor.title_mode; + + var render_title = true; + if (title_mode == LiteGraph.TRANSPARENT_TITLE) { + render_title = false; + } else if (title_mode == LiteGraph.AUTOHIDE_TITLE && mouse_over) { + render_title = true; + } + + var area = tmp_area; + area[0] = 0; //x + area[1] = render_title ? -title_height : 0; //y + area[2] = size[0] + 1; //w + area[3] = render_title ? size[1] + title_height : size[1]; //h + + var old_alpha = ctx.globalAlpha; + + //full node shape + //if(node.flags.collapsed) + { + ctx.beginPath(); + if (shape == LiteGraph.BOX_SHAPE || low_quality) { + ctx.fillRect(area[0], area[1], area[2], area[3]); + } else if ( + shape == LiteGraph.ROUND_SHAPE || + shape == LiteGraph.CARD_SHAPE + ) { + ctx.roundRect( + area[0], + area[1], + area[2], + area[3], + this.round_radius, + shape == LiteGraph.CARD_SHAPE ? 0 : this.round_radius + ); + } else if (shape == LiteGraph.CIRCLE_SHAPE) { + ctx.arc( + size[0] * 0.5, + size[1] * 0.5, + size[0] * 0.5, + 0, + Math.PI * 2 + ); + } + ctx.fill(); + + //separator + if(!node.flags.collapsed) + { + ctx.shadowColor = "transparent"; + ctx.fillStyle = "rgba(0,0,0,0.2)"; + ctx.fillRect(0, -1, area[2], 2); + } + } + ctx.shadowColor = "transparent"; + + if (node.onDrawBackground) { + node.onDrawBackground(ctx, this, this.canvas); + } + + //title bg (remember, it is rendered ABOVE the node) + if (render_title || title_mode == LiteGraph.TRANSPARENT_TITLE) { + //title bar + if (node.onDrawTitleBar) { + node.onDrawTitleBar( + ctx, + title_height, + size, + this.ds.scale, + fgcolor + ); + } else if ( + title_mode != LiteGraph.TRANSPARENT_TITLE && + (node.constructor.title_color || this.render_title_colored) + ) { + var title_color = node.constructor.title_color || fgcolor; + + if (node.flags.collapsed) { + ctx.shadowColor = LiteGraph.DEFAULT_SHADOW_COLOR; + } + + //* gradient test + if (this.use_gradients) { + var grad = LGraphCanvas.gradients[title_color]; + if (!grad) { + grad = LGraphCanvas.gradients[ title_color ] = ctx.createLinearGradient(0, 0, 400, 0); + grad.addColorStop(0, title_color); + grad.addColorStop(1, "#000"); + } + ctx.fillStyle = grad; + } else { + ctx.fillStyle = title_color; + } + + //ctx.globalAlpha = 0.5 * old_alpha; + ctx.beginPath(); + if (shape == LiteGraph.BOX_SHAPE || low_quality) { + ctx.rect(0, -title_height, size[0] + 1, title_height); + } else if ( shape == LiteGraph.ROUND_SHAPE || shape == LiteGraph.CARD_SHAPE ) { + ctx.roundRect( + 0, + -title_height, + size[0] + 1, + title_height, + this.round_radius, + node.flags.collapsed ? this.round_radius : 0 + ); + } + ctx.fill(); + ctx.shadowColor = "transparent"; + } + + //title box + var box_size = 10; + if (node.onDrawTitleBox) { + node.onDrawTitleBox(ctx, title_height, size, this.ds.scale); + } else if ( + shape == LiteGraph.ROUND_SHAPE || + shape == LiteGraph.CIRCLE_SHAPE || + shape == LiteGraph.CARD_SHAPE + ) { + if (low_quality) { + ctx.fillStyle = "black"; + ctx.beginPath(); + ctx.arc( + title_height * 0.5, + title_height * -0.5, + box_size * 0.5 + 1, + 0, + Math.PI * 2 + ); + ctx.fill(); + } + + ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR; + if(low_quality) + ctx.fillRect( title_height * 0.5 - box_size *0.5, title_height * -0.5 - box_size *0.5, box_size , box_size ); + else + { + ctx.beginPath(); + ctx.arc( + title_height * 0.5, + title_height * -0.5, + box_size * 0.5, + 0, + Math.PI * 2 + ); + ctx.fill(); + } + } else { + if (low_quality) { + ctx.fillStyle = "black"; + ctx.fillRect( + (title_height - box_size) * 0.5 - 1, + (title_height + box_size) * -0.5 - 1, + box_size + 2, + box_size + 2 + ); + } + ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR; + ctx.fillRect( + (title_height - box_size) * 0.5, + (title_height + box_size) * -0.5, + box_size, + box_size + ); + } + ctx.globalAlpha = old_alpha; + + //title text + if (node.onDrawTitleText) { + node.onDrawTitleText( + ctx, + title_height, + size, + this.ds.scale, + this.title_text_font, + selected + ); + } + if (!low_quality) { + ctx.font = this.title_text_font; + var title = String(node.getTitle()); + if (title) { + if (selected) { + ctx.fillStyle = "white"; + } else { + ctx.fillStyle = + node.constructor.title_text_color || + this.node_title_color; + } + if (node.flags.collapsed) { + ctx.textAlign = "left"; + var measure = ctx.measureText(title); + ctx.fillText( + title.substr(0,20), //avoid urls too long + title_height,// + measure.width * 0.5, + LiteGraph.NODE_TITLE_TEXT_Y - title_height + ); + ctx.textAlign = "left"; + } else { + ctx.textAlign = "left"; + ctx.fillText( + title, + title_height, + LiteGraph.NODE_TITLE_TEXT_Y - title_height + ); + } + } + } + + if (node.onDrawTitle) { + node.onDrawTitle(ctx); + } + } + + //render selection marker + if (selected) { + if (node.onBounding) { + node.onBounding(area); + } + + if (title_mode == LiteGraph.TRANSPARENT_TITLE) { + area[1] -= title_height; + area[3] += title_height; + } + ctx.lineWidth = 1; + ctx.globalAlpha = 0.8; + ctx.beginPath(); + if (shape == LiteGraph.BOX_SHAPE) { + ctx.rect( + -6 + area[0], + -6 + area[1], + 12 + area[2], + 12 + area[3] + ); + } else if ( + shape == LiteGraph.ROUND_SHAPE || + (shape == LiteGraph.CARD_SHAPE && node.flags.collapsed) + ) { + ctx.roundRect( + -6 + area[0], + -6 + area[1], + 12 + area[2], + 12 + area[3], + this.round_radius * 2 + ); + } else if (shape == LiteGraph.CARD_SHAPE) { + ctx.roundRect( + -6 + area[0], + -6 + area[1], + 12 + area[2], + 12 + area[3], + this.round_radius * 2, + 2 + ); + } else if (shape == LiteGraph.CIRCLE_SHAPE) { + ctx.arc( + size[0] * 0.5, + size[1] * 0.5, + size[0] * 0.5 + 6, + 0, + Math.PI * 2 + ); + } + ctx.strokeStyle = "#FFF"; + ctx.stroke(); + ctx.strokeStyle = fgcolor; + ctx.globalAlpha = 1; + } + }; + + var margin_area = new Float32Array(4); + var link_bounding = new Float32Array(4); + var tempA = new Float32Array(2); + var tempB = new Float32Array(2); + + /** + * draws every connection visible in the canvas + * OPTIMIZE THIS: pre-catch connections position instead of recomputing them every time + * @method drawConnections + **/ + LGraphCanvas.prototype.drawConnections = function(ctx) { + var now = LiteGraph.getTime(); + var visible_area = this.visible_area; + margin_area[0] = visible_area[0] - 20; + margin_area[1] = visible_area[1] - 20; + margin_area[2] = visible_area[2] + 40; + margin_area[3] = visible_area[3] + 40; + + //draw connections + ctx.lineWidth = this.connections_width; + + ctx.fillStyle = "#AAA"; + ctx.strokeStyle = "#AAA"; + ctx.globalAlpha = this.editor_alpha; + //for every node + var nodes = this.graph._nodes; + for (var n = 0, l = nodes.length; n < l; ++n) { + var node = nodes[n]; + //for every input (we render just inputs because it is easier as every slot can only have one input) + if (!node.inputs || !node.inputs.length) { + continue; + } + + for (var i = 0; i < node.inputs.length; ++i) { + var input = node.inputs[i]; + if (!input || input.link == null) { + continue; + } + var link_id = input.link; + var link = this.graph.links[link_id]; + if (!link) { + continue; + } + + //find link info + var start_node = this.graph.getNodeById(link.origin_id); + if (start_node == null) { + continue; + } + var start_node_slot = link.origin_slot; + var start_node_slotpos = null; + if (start_node_slot == -1) { + start_node_slotpos = [ + start_node.pos[0] + 10, + start_node.pos[1] + 10 + ]; + } else { + start_node_slotpos = start_node.getConnectionPos( + false, + start_node_slot, + tempA + ); + } + var end_node_slotpos = node.getConnectionPos(true, i, tempB); + + //compute link bounding + link_bounding[0] = start_node_slotpos[0]; + link_bounding[1] = start_node_slotpos[1]; + link_bounding[2] = end_node_slotpos[0] - start_node_slotpos[0]; + link_bounding[3] = end_node_slotpos[1] - start_node_slotpos[1]; + if (link_bounding[2] < 0) { + link_bounding[0] += link_bounding[2]; + link_bounding[2] = Math.abs(link_bounding[2]); + } + if (link_bounding[3] < 0) { + link_bounding[1] += link_bounding[3]; + link_bounding[3] = Math.abs(link_bounding[3]); + } + + //skip links outside of the visible area of the canvas + if (!overlapBounding(link_bounding, margin_area)) { + continue; + } + + var start_slot = start_node.outputs[start_node_slot]; + var end_slot = node.inputs[i]; + if (!start_slot || !end_slot) { + continue; + } + var start_dir = + start_slot.dir || + (start_node.horizontal ? LiteGraph.DOWN : LiteGraph.RIGHT); + var end_dir = + end_slot.dir || + (node.horizontal ? LiteGraph.UP : LiteGraph.LEFT); + + this.renderLink( + ctx, + start_node_slotpos, + end_node_slotpos, + link, + false, + 0, + null, + start_dir, + end_dir + ); + + //event triggered rendered on top + if (link && link._last_time && now - link._last_time < 1000) { + var f = 2.0 - (now - link._last_time) * 0.002; + var tmp = ctx.globalAlpha; + ctx.globalAlpha = tmp * f; + this.renderLink( + ctx, + start_node_slotpos, + end_node_slotpos, + link, + true, + f, + "white", + start_dir, + end_dir + ); + ctx.globalAlpha = tmp; + } + } + } + ctx.globalAlpha = 1; + }; + + /** + * draws a link between two points + * @method renderLink + * @param {vec2} a start pos + * @param {vec2} b end pos + * @param {Object} link the link object with all the link info + * @param {boolean} skip_border ignore the shadow of the link + * @param {boolean} flow show flow animation (for events) + * @param {string} color the color for the link + * @param {number} start_dir the direction enum + * @param {number} end_dir the direction enum + * @param {number} num_sublines number of sublines (useful to represent vec3 or rgb) + **/ + LGraphCanvas.prototype.renderLink = function( + ctx, + a, + b, + link, + skip_border, + flow, + color, + start_dir, + end_dir, + num_sublines + ) { + if (link) { + this.visible_links.push(link); + } + + //choose color + if (!color && link) { + color = link.color || LGraphCanvas.link_type_colors[link.type]; + } + if (!color) { + color = this.default_link_color; + } + if (link != null && this.highlighted_links[link.id]) { + color = "#FFF"; + } + + start_dir = start_dir || LiteGraph.RIGHT; + end_dir = end_dir || LiteGraph.LEFT; + + var dist = distance(a, b); + + if (this.render_connections_border && this.ds.scale > 0.6) { + ctx.lineWidth = this.connections_width + 4; + } + ctx.lineJoin = "round"; + num_sublines = num_sublines || 1; + if (num_sublines > 1) { + ctx.lineWidth = 0.5; + } + + //begin line shape + ctx.beginPath(); + for (var i = 0; i < num_sublines; i += 1) { + var offsety = (i - (num_sublines - 1) * 0.5) * 5; + + if (this.links_render_mode == LiteGraph.SPLINE_LINK) { + ctx.moveTo(a[0], a[1] + offsety); + var start_offset_x = 0; + var start_offset_y = 0; + var end_offset_x = 0; + var end_offset_y = 0; + switch (start_dir) { + case LiteGraph.LEFT: + start_offset_x = dist * -0.25; + break; + case LiteGraph.RIGHT: + start_offset_x = dist * 0.25; + break; + case LiteGraph.UP: + start_offset_y = dist * -0.25; + break; + case LiteGraph.DOWN: + start_offset_y = dist * 0.25; + break; + } + switch (end_dir) { + case LiteGraph.LEFT: + end_offset_x = dist * -0.25; + break; + case LiteGraph.RIGHT: + end_offset_x = dist * 0.25; + break; + case LiteGraph.UP: + end_offset_y = dist * -0.25; + break; + case LiteGraph.DOWN: + end_offset_y = dist * 0.25; + break; + } + ctx.bezierCurveTo( + a[0] + start_offset_x, + a[1] + start_offset_y + offsety, + b[0] + end_offset_x, + b[1] + end_offset_y + offsety, + b[0], + b[1] + offsety + ); + } else if (this.links_render_mode == LiteGraph.LINEAR_LINK) { + ctx.moveTo(a[0], a[1] + offsety); + var start_offset_x = 0; + var start_offset_y = 0; + var end_offset_x = 0; + var end_offset_y = 0; + switch (start_dir) { + case LiteGraph.LEFT: + start_offset_x = -1; + break; + case LiteGraph.RIGHT: + start_offset_x = 1; + break; + case LiteGraph.UP: + start_offset_y = -1; + break; + case LiteGraph.DOWN: + start_offset_y = 1; + break; + } + switch (end_dir) { + case LiteGraph.LEFT: + end_offset_x = -1; + break; + case LiteGraph.RIGHT: + end_offset_x = 1; + break; + case LiteGraph.UP: + end_offset_y = -1; + break; + case LiteGraph.DOWN: + end_offset_y = 1; + break; + } + var l = 15; + ctx.lineTo( + a[0] + start_offset_x * l, + a[1] + start_offset_y * l + offsety + ); + ctx.lineTo( + b[0] + end_offset_x * l, + b[1] + end_offset_y * l + offsety + ); + ctx.lineTo(b[0], b[1] + offsety); + } else if (this.links_render_mode == LiteGraph.STRAIGHT_LINK) { + ctx.moveTo(a[0], a[1]); + var start_x = a[0]; + var start_y = a[1]; + var end_x = b[0]; + var end_y = b[1]; + if (start_dir == LiteGraph.RIGHT) { + start_x += 10; + } else { + start_y += 10; + } + if (end_dir == LiteGraph.LEFT) { + end_x -= 10; + } else { + end_y -= 10; + } + ctx.lineTo(start_x, start_y); + ctx.lineTo((start_x + end_x) * 0.5, start_y); + ctx.lineTo((start_x + end_x) * 0.5, end_y); + ctx.lineTo(end_x, end_y); + ctx.lineTo(b[0], b[1]); + } else { + return; + } //unknown + } + + //rendering the outline of the connection can be a little bit slow + if ( + this.render_connections_border && + this.ds.scale > 0.6 && + !skip_border + ) { + ctx.strokeStyle = "rgba(0,0,0,0.5)"; + ctx.stroke(); + } + + ctx.lineWidth = this.connections_width; + ctx.fillStyle = ctx.strokeStyle = color; + ctx.stroke(); + //end line shape + + var pos = this.computeConnectionPoint(a, b, 0.5, start_dir, end_dir); + if (link && link._pos) { + link._pos[0] = pos[0]; + link._pos[1] = pos[1]; + } + + //render arrow in the middle + if ( + this.ds.scale >= 0.6 && + this.highquality_render && + end_dir != LiteGraph.CENTER + ) { + //render arrow + if (this.render_connection_arrows) { + //compute two points in the connection + var posA = this.computeConnectionPoint( + a, + b, + 0.25, + start_dir, + end_dir + ); + var posB = this.computeConnectionPoint( + a, + b, + 0.26, + start_dir, + end_dir + ); + var posC = this.computeConnectionPoint( + a, + b, + 0.75, + start_dir, + end_dir + ); + var posD = this.computeConnectionPoint( + a, + b, + 0.76, + start_dir, + end_dir + ); + + //compute the angle between them so the arrow points in the right direction + var angleA = 0; + var angleB = 0; + if (this.render_curved_connections) { + angleA = -Math.atan2(posB[0] - posA[0], posB[1] - posA[1]); + angleB = -Math.atan2(posD[0] - posC[0], posD[1] - posC[1]); + } else { + angleB = angleA = b[1] > a[1] ? 0 : Math.PI; + } + + //render arrow + ctx.save(); + ctx.translate(posA[0], posA[1]); + ctx.rotate(angleA); + ctx.beginPath(); + ctx.moveTo(-5, -3); + ctx.lineTo(0, +7); + ctx.lineTo(+5, -3); + ctx.fill(); + ctx.restore(); + ctx.save(); + ctx.translate(posC[0], posC[1]); + ctx.rotate(angleB); + ctx.beginPath(); + ctx.moveTo(-5, -3); + ctx.lineTo(0, +7); + ctx.lineTo(+5, -3); + ctx.fill(); + ctx.restore(); + } + + //circle + ctx.beginPath(); + ctx.arc(pos[0], pos[1], 5, 0, Math.PI * 2); + ctx.fill(); + } + + //render flowing points + if (flow) { + ctx.fillStyle = color; + for (var i = 0; i < 5; ++i) { + var f = (LiteGraph.getTime() * 0.001 + i * 0.2) % 1; + var pos = this.computeConnectionPoint( + a, + b, + f, + start_dir, + end_dir + ); + ctx.beginPath(); + ctx.arc(pos[0], pos[1], 5, 0, 2 * Math.PI); + ctx.fill(); + } + } + }; + + //returns the link center point based on curvature + LGraphCanvas.prototype.computeConnectionPoint = function( + a, + b, + t, + start_dir, + end_dir + ) { + start_dir = start_dir || LiteGraph.RIGHT; + end_dir = end_dir || LiteGraph.LEFT; + + var dist = distance(a, b); + var p0 = a; + var p1 = [a[0], a[1]]; + var p2 = [b[0], b[1]]; + var p3 = b; + + switch (start_dir) { + case LiteGraph.LEFT: + p1[0] += dist * -0.25; + break; + case LiteGraph.RIGHT: + p1[0] += dist * 0.25; + break; + case LiteGraph.UP: + p1[1] += dist * -0.25; + break; + case LiteGraph.DOWN: + p1[1] += dist * 0.25; + break; + } + switch (end_dir) { + case LiteGraph.LEFT: + p2[0] += dist * -0.25; + break; + case LiteGraph.RIGHT: + p2[0] += dist * 0.25; + break; + case LiteGraph.UP: + p2[1] += dist * -0.25; + break; + case LiteGraph.DOWN: + p2[1] += dist * 0.25; + break; + } + + var c1 = (1 - t) * (1 - t) * (1 - t); + var c2 = 3 * ((1 - t) * (1 - t)) * t; + var c3 = 3 * (1 - t) * (t * t); + var c4 = t * t * t; + + var x = c1 * p0[0] + c2 * p1[0] + c3 * p2[0] + c4 * p3[0]; + var y = c1 * p0[1] + c2 * p1[1] + c3 * p2[1] + c4 * p3[1]; + return [x, y]; + }; + + LGraphCanvas.prototype.drawExecutionOrder = function(ctx) { + ctx.shadowColor = "transparent"; + ctx.globalAlpha = 0.25; + + ctx.textAlign = "center"; + ctx.strokeStyle = "white"; + ctx.globalAlpha = 0.75; + + var visible_nodes = this.visible_nodes; + for (var i = 0; i < visible_nodes.length; ++i) { + var node = visible_nodes[i]; + ctx.fillStyle = "black"; + ctx.fillRect( + node.pos[0] - LiteGraph.NODE_TITLE_HEIGHT, + node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, + LiteGraph.NODE_TITLE_HEIGHT, + LiteGraph.NODE_TITLE_HEIGHT + ); + if (node.order == 0) { + ctx.strokeRect( + node.pos[0] - LiteGraph.NODE_TITLE_HEIGHT + 0.5, + node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5, + LiteGraph.NODE_TITLE_HEIGHT, + LiteGraph.NODE_TITLE_HEIGHT + ); + } + ctx.fillStyle = "#FFF"; + ctx.fillText( + node.order, + node.pos[0] + LiteGraph.NODE_TITLE_HEIGHT * -0.5, + node.pos[1] - 6 + ); + } + ctx.globalAlpha = 1; + }; + + /** + * draws the widgets stored inside a node + * @method drawNodeWidgets + **/ + LGraphCanvas.prototype.drawNodeWidgets = function( + node, + posY, + ctx, + active_widget + ) { + if (!node.widgets || !node.widgets.length) { + return 0; + } + var width = node.size[0]; + var widgets = node.widgets; + posY += 2; + var H = LiteGraph.NODE_WIDGET_HEIGHT; + var show_text = this.ds.scale > 0.5; + ctx.save(); + ctx.globalAlpha = this.editor_alpha; + var outline_color = LiteGraph.WIDGET_OUTLINE_COLOR; + var background_color = LiteGraph.WIDGET_BGCOLOR; + var text_color = LiteGraph.WIDGET_TEXT_COLOR; + var secondary_text_color = LiteGraph.WIDGET_SECONDARY_TEXT_COLOR; + var margin = 15; + + for (var i = 0; i < widgets.length; ++i) { + var w = widgets[i]; + var y = posY; + if (w.y) { + y = w.y; + } + w.last_y = y; + ctx.strokeStyle = outline_color; + ctx.fillStyle = "#222"; + ctx.textAlign = "left"; + if(w.disabled) + ctx.globalAlpha *= 0.5; + + switch (w.type) { + case "button": + if (w.clicked) { + ctx.fillStyle = "#AAA"; + w.clicked = false; + this.dirty_canvas = true; + } + ctx.fillRect(margin, y, width - margin * 2, H); + if(show_text && !w.disabled) + ctx.strokeRect( margin, y, width - margin * 2, H ); + if (show_text) { + ctx.textAlign = "center"; + ctx.fillStyle = text_color; + ctx.fillText(w.name, width * 0.5, y + H * 0.7); + } + break; + case "toggle": + ctx.textAlign = "left"; + ctx.strokeStyle = outline_color; + ctx.fillStyle = background_color; + ctx.beginPath(); + if (show_text) + ctx.roundRect(margin, posY, width - margin * 2, H, H * 0.5); + else + ctx.rect(margin, posY, width - margin * 2, H ); + ctx.fill(); + if(show_text && !w.disabled) + ctx.stroke(); + ctx.fillStyle = w.value ? "#89A" : "#333"; + ctx.beginPath(); + ctx.arc( width - margin * 2, y + H * 0.5, H * 0.36, 0, Math.PI * 2 ); + ctx.fill(); + if (show_text) { + ctx.fillStyle = secondary_text_color; + if (w.name != null) { + ctx.fillText(w.name, margin * 2, y + H * 0.7); + } + ctx.fillStyle = w.value ? text_color : secondary_text_color; + ctx.textAlign = "right"; + ctx.fillText( + w.value + ? w.options.on || "true" + : w.options.off || "false", + width - 40, + y + H * 0.7 + ); + } + break; + case "slider": + ctx.fillStyle = background_color; + ctx.fillRect(margin, y, width - margin * 2, H); + var range = w.options.max - w.options.min; + var nvalue = (w.value - w.options.min) / range; + ctx.fillStyle = active_widget == w ? "#89A" : "#678"; + ctx.fillRect(margin, y, nvalue * (width - margin * 2), H); + if(show_text && !w.disabled) + ctx.strokeRect(margin, y, width - margin * 2, H); + if (w.marker) { + var marker_nvalue = (w.marker - w.options.min) / range; + ctx.fillStyle = "#AA9"; + ctx.fillRect( margin + marker_nvalue * (width - margin * 2), y, 2, H ); + } + if (show_text) { + ctx.textAlign = "center"; + ctx.fillStyle = text_color; + ctx.fillText( + w.name + " " + Number(w.value).toFixed(3), + width * 0.5, + y + H * 0.7 + ); + } + break; + case "number": + case "combo": + ctx.textAlign = "left"; + ctx.strokeStyle = outline_color; + ctx.fillStyle = background_color; + ctx.beginPath(); + if(show_text) + ctx.roundRect(margin, posY, width - margin * 2, H, H * 0.5); + else + ctx.rect(margin, posY, width - margin * 2, H ); + ctx.fill(); + if (show_text) { + if(!w.disabled) + ctx.stroke(); + ctx.fillStyle = text_color; + if(!w.disabled) + { + ctx.beginPath(); + ctx.moveTo(margin + 16, posY + 5); + ctx.lineTo(margin + 6, posY + H * 0.5); + ctx.lineTo(margin + 16, posY + H - 5); + ctx.fill(); + ctx.beginPath(); + ctx.moveTo(width - margin - 16, posY + 5); + ctx.lineTo(width - margin - 6, posY + H * 0.5); + ctx.lineTo(width - margin - 16, posY + H - 5); + ctx.fill(); + } + ctx.fillStyle = secondary_text_color; + ctx.fillText(w.name, margin * 2 + 5, y + H * 0.7); + ctx.fillStyle = text_color; + ctx.textAlign = "right"; + if (w.type == "number") { + ctx.fillText( + Number(w.value).toFixed( + w.options.precision !== undefined + ? w.options.precision + : 3 + ), + width - margin * 2 - 20, + y + H * 0.7 + ); + } else { + var v = w.value; + if( w.options.values ) + { + var values = w.options.values; + if( values.constructor === Function ) + values = values(); + if(values && values.constructor !== Array) + v = values[ w.value ]; + } + ctx.fillText( + v, + width - margin * 2 - 20, + y + H * 0.7 + ); + } + } + break; + case "string": + case "text": + ctx.textAlign = "left"; + ctx.strokeStyle = outline_color; + ctx.fillStyle = background_color; + ctx.beginPath(); + if (show_text) + ctx.roundRect(margin, posY, width - margin * 2, H, H * 0.5); + else + ctx.rect( margin, posY, width - margin * 2, H ); + ctx.fill(); + if (show_text) { + ctx.save(); + ctx.beginPath(); + ctx.rect(margin, posY, width - margin * 2, H); + ctx.clip(); + + ctx.stroke(); + ctx.fillStyle = secondary_text_color; + if (w.name != null) { + ctx.fillText(w.name, margin * 2, y + H * 0.7); + } + ctx.fillStyle = text_color; + ctx.textAlign = "right"; + ctx.fillText(String(w.value).substr(0,30), width - margin * 2, y + H * 0.7); //30 chars max + ctx.restore(); + } + break; + default: + if (w.draw) { + w.draw(ctx, node, width, y, H); + } + break; + } + posY += (w.computeSize ? w.computeSize(width)[1] : H) + 4; + ctx.globalAlpha = this.editor_alpha; + + } + ctx.restore(); + ctx.textAlign = "left"; + }; + + /** + * process an event on widgets + * @method processNodeWidgets + **/ + LGraphCanvas.prototype.processNodeWidgets = function( + node, + pos, + event, + active_widget + ) { + if (!node.widgets || !node.widgets.length) { + return null; + } + + var x = pos[0] - node.pos[0]; + var y = pos[1] - node.pos[1]; + var width = node.size[0]; + var that = this; + var ref_window = this.getCanvasWindow(); + + for (var i = 0; i < node.widgets.length; ++i) { + var w = node.widgets[i]; + if(!w || w.disabled) + continue; + var widget_height = w.computeSize ? w.computeSize(width)[1] : LiteGraph.NODE_WIDGET_HEIGHT; + if ( w == active_widget || (x > 6 && x < width - 12 && y > w.last_y && y < w.last_y + widget_height) ) { + //inside widget + switch (w.type) { + case "button": + if (event.type === "mousemove") { + break; + } + if (w.callback) { + setTimeout(function() { + w.callback(w, that, node, pos, event); + }, 20); + } + w.clicked = true; + this.dirty_canvas = true; + break; + case "slider": + var range = w.options.max - w.options.min; + var nvalue = Math.clamp((x - 10) / (width - 20), 0, 1); + w.value = + w.options.min + + (w.options.max - w.options.min) * nvalue; + if (w.callback) { + setTimeout(function() { + inner_value_change(w, w.value); + }, 20); + } + this.dirty_canvas = true; + break; + case "number": + case "combo": + var old_value = w.value; + if (event.type == "mousemove" && w.type == "number") { + w.value += event.deltaX * 0.1 * (w.options.step || 1); + if ( w.options.min != null && w.value < w.options.min ) { + w.value = w.options.min; + } + if ( w.options.max != null && w.value > w.options.max ) { + w.value = w.options.max; + } + } else if (event.type == "mousedown") { + var values = w.options.values; + if (values && values.constructor === Function) { + values = w.options.values(w, node); + } + var values_list = null; + + if( w.type != "number") + values_list = values.constructor === Array ? values : Object.keys(values); + + var delta = x < 40 ? -1 : x > width - 40 ? 1 : 0; + if (w.type == "number") { + w.value += delta * 0.1 * (w.options.step || 1); + if ( w.options.min != null && w.value < w.options.min ) { + w.value = w.options.min; + } + if ( w.options.max != null && w.value > w.options.max ) { + w.value = w.options.max; + } + } else if (delta) { //clicked in arrow, used for combos + var index = -1; + if(values.constructor === Object) + index = values_list.indexOf( String( w.value ) ) + delta; + else + index = values_list.indexOf( w.value ) + delta; + if (index >= values_list.length) { + index = values_list.length - 1; + } + if (index < 0) { + index = 0; + } + if( values.constructor === Array ) + w.value = values[index]; + else + w.value = index; + } else { //combo clicked + var text_values = values != values_list ? Object.values(values) : values; + var menu = new LiteGraph.ContextMenu(text_values, { + scale: Math.max(1, this.ds.scale), + event: event, + className: "dark", + callback: inner_clicked.bind(w) + }, + ref_window); + function inner_clicked(v, option, event) { + if(values != values_list) + v = text_values.indexOf(v); + this.value = v; + inner_value_change(this, v); + that.dirty_canvas = true; + return false; + } + } + } //end mousedown + else if(event.type == "mouseup" && w.type == "number") + { + var delta = x < 40 ? -1 : x > width - 40 ? 1 : 0; + if (event.click_time < 200 && delta == 0) { + this.prompt("Value",w.value,function(v) { + this.value = Number(v); + inner_value_change(this, this.value); + }.bind(w), + event); + } + } + + if( old_value != w.value ) + setTimeout( + function() { + inner_value_change(this, this.value); + }.bind(w), + 20 + ); + this.dirty_canvas = true; + break; + case "toggle": + if (event.type == "mousedown") { + w.value = !w.value; + setTimeout(function() { + inner_value_change(w, w.value); + }, 20); + } + break; + case "string": + case "text": + if (event.type == "mousedown") { + this.prompt("Value",w.value,function(v) { + this.value = v; + inner_value_change(this, v); + }.bind(w), + event); + } + break; + default: + if (w.mouse) { + this.dirty_canvas = w.mouse(event, [x, y], node); + } + break; + } //end switch + + return w; + } + } + + function inner_value_change(widget, value) { + widget.value = value; + if ( widget.options && widget.options.property && node.properties[widget.options.property] !== undefined ) { + node.setProperty( widget.options.property, value ); + } + if (widget.callback) { + widget.callback(widget.value, that, node, pos, event); + } + } + + return null; + }; + + /** + * draws every group area in the background + * @method drawGroups + **/ + LGraphCanvas.prototype.drawGroups = function(canvas, ctx) { + if (!this.graph) { + return; + } + + var groups = this.graph._groups; + + ctx.save(); + ctx.globalAlpha = 0.5 * this.editor_alpha; + + for (var i = 0; i < groups.length; ++i) { + var group = groups[i]; + + if (!overlapBounding(this.visible_area, group._bounding)) { + continue; + } //out of the visible area + + ctx.fillStyle = group.color || "#335"; + ctx.strokeStyle = group.color || "#335"; + var pos = group._pos; + var size = group._size; + ctx.globalAlpha = 0.25 * this.editor_alpha; + ctx.beginPath(); + ctx.rect(pos[0] + 0.5, pos[1] + 0.5, size[0], size[1]); + ctx.fill(); + ctx.globalAlpha = this.editor_alpha; + ctx.stroke(); + + ctx.beginPath(); + ctx.moveTo(pos[0] + size[0], pos[1] + size[1]); + ctx.lineTo(pos[0] + size[0] - 10, pos[1] + size[1]); + ctx.lineTo(pos[0] + size[0], pos[1] + size[1] - 10); + ctx.fill(); + + var font_size = + group.font_size || LiteGraph.DEFAULT_GROUP_FONT_SIZE; + ctx.font = font_size + "px Arial"; + ctx.fillText(group.title, pos[0] + 4, pos[1] + font_size); + } + + ctx.restore(); + }; + + LGraphCanvas.prototype.adjustNodesSize = function() { + var nodes = this.graph._nodes; + for (var i = 0; i < nodes.length; ++i) { + nodes[i].size = nodes[i].computeSize(); + } + this.setDirty(true, true); + }; + + /** + * resizes the canvas to a given size, if no size is passed, then it tries to fill the parentNode + * @method resize + **/ + LGraphCanvas.prototype.resize = function(width, height) { + if (!width && !height) { + var parent = this.canvas.parentNode; + width = parent.offsetWidth; + height = parent.offsetHeight; + } + + if (this.canvas.width == width && this.canvas.height == height) { + return; + } + + this.canvas.width = width; + this.canvas.height = height; + this.bgcanvas.width = this.canvas.width; + this.bgcanvas.height = this.canvas.height; + this.setDirty(true, true); + }; + + /** + * switches to live mode (node shapes are not rendered, only the content) + * this feature was designed when graphs where meant to create user interfaces + * @method switchLiveMode + **/ + LGraphCanvas.prototype.switchLiveMode = function(transition) { + if (!transition) { + this.live_mode = !this.live_mode; + this.dirty_canvas = true; + this.dirty_bgcanvas = true; + return; + } + + var self = this; + var delta = this.live_mode ? 1.1 : 0.9; + if (this.live_mode) { + this.live_mode = false; + this.editor_alpha = 0.1; + } + + var t = setInterval(function() { + self.editor_alpha *= delta; + self.dirty_canvas = true; + self.dirty_bgcanvas = true; + + if (delta < 1 && self.editor_alpha < 0.01) { + clearInterval(t); + if (delta < 1) { + self.live_mode = true; + } + } + if (delta > 1 && self.editor_alpha > 0.99) { + clearInterval(t); + self.editor_alpha = 1; + } + }, 1); + }; + + LGraphCanvas.prototype.onNodeSelectionChange = function(node) { + return; //disabled + }; + + LGraphCanvas.prototype.touchHandler = function(event) { + //alert("foo"); + var touches = event.changedTouches, + first = touches[0], + type = ""; + + switch (event.type) { + case "touchstart": + type = "mousedown"; + break; + case "touchmove": + type = "mousemove"; + break; + case "touchend": + type = "mouseup"; + break; + default: + return; + } + + //initMouseEvent(type, canBubble, cancelable, view, clickCount, + // screenX, screenY, clientX, clientY, ctrlKey, + // altKey, shiftKey, metaKey, button, relatedTarget); + + var window = this.getCanvasWindow(); + var document = window.document; + + var simulatedEvent = document.createEvent("MouseEvent"); + simulatedEvent.initMouseEvent( + type, + true, + true, + window, + 1, + first.screenX, + first.screenY, + first.clientX, + first.clientY, + false, + false, + false, + false, + 0 /*left*/, + null + ); + first.target.dispatchEvent(simulatedEvent); + event.preventDefault(); + }; + + /* CONTEXT MENU ********************/ + + LGraphCanvas.onGroupAdd = function(info, entry, mouse_event) { + var canvas = LGraphCanvas.active_canvas; + var ref_window = canvas.getCanvasWindow(); + + var group = new LiteGraph.LGraphGroup(); + group.pos = canvas.convertEventToCanvasOffset(mouse_event); + canvas.graph.add(group); + }; + + LGraphCanvas.onMenuAdd = function(node, options, e, prev_menu, callback) { + var canvas = LGraphCanvas.active_canvas; + var ref_window = canvas.getCanvasWindow(); + + var values = LiteGraph.getNodeTypesCategories( canvas.filter ); + var entries = []; + for (var i in values) { + if (values[i]) { + entries.push({ value: values[i], content: values[i], has_submenu: true }); + } + } + + //show categories + var menu = new LiteGraph.ContextMenu( entries, { event: e, callback: inner_clicked, parentMenu: prev_menu }, ref_window ); + + function inner_clicked(v, option, e) { + var category = v.value; + var node_types = LiteGraph.getNodeTypesInCategory( category, canvas.filter ); + var values = []; + for (var i in node_types) { + if (!node_types[i].skip_list) { + values.push({ + content: node_types[i].title, + value: node_types[i].type + }); + } + } + + new LiteGraph.ContextMenu( values, { event: e, callback: inner_create, parentMenu: menu }, ref_window ); + return false; + } + + function inner_create(v, e) { + var first_event = prev_menu.getFirstEvent(); + var node = LiteGraph.createNode(v.value); + if (node) { + node.pos = canvas.convertEventToCanvasOffset(first_event); + canvas.graph.add(node); + } + if(callback) + callback(node); + } + + return false; + }; + + LGraphCanvas.onMenuCollapseAll = function() {}; + + LGraphCanvas.onMenuNodeEdit = function() {}; + + LGraphCanvas.showMenuNodeOptionalInputs = function( + v, + options, + e, + prev_menu, + node + ) { + if (!node) { + return; + } + + var that = this; + var canvas = LGraphCanvas.active_canvas; + var ref_window = canvas.getCanvasWindow(); + + var options = node.optional_inputs; + if (node.onGetInputs) { + options = node.onGetInputs(); + } + + var entries = []; + if (options) { + for (var i in options) { + var entry = options[i]; + if (!entry) { + entries.push(null); + continue; + } + var label = entry[0]; + if (entry[2] && entry[2].label) { + label = entry[2].label; + } + var data = { content: label, value: entry }; + if (entry[1] == LiteGraph.ACTION) { + data.className = "event"; + } + entries.push(data); + } + } + + if (this.onMenuNodeInputs) { + entries = this.onMenuNodeInputs(entries); + } + + if (!entries.length) { + return; + } + + var menu = new LiteGraph.ContextMenu( + entries, + { + event: e, + callback: inner_clicked, + parentMenu: prev_menu, + node: node + }, + ref_window + ); + + function inner_clicked(v, e, prev) { + if (!node) { + return; + } + + if (v.callback) { + v.callback.call(that, node, v, e, prev); + } + + if (v.value) { + node.addInput(v.value[0], v.value[1], v.value[2]); + node.setDirtyCanvas(true, true); + } + } + + return false; + }; + + LGraphCanvas.showMenuNodeOptionalOutputs = function( + v, + options, + e, + prev_menu, + node + ) { + if (!node) { + return; + } + + var that = this; + var canvas = LGraphCanvas.active_canvas; + var ref_window = canvas.getCanvasWindow(); + + var options = node.optional_outputs; + if (node.onGetOutputs) { + options = node.onGetOutputs(); + } + + var entries = []; + if (options) { + for (var i in options) { + var entry = options[i]; + if (!entry) { + //separator? + entries.push(null); + continue; + } + + if ( + node.flags && + node.flags.skip_repeated_outputs && + node.findOutputSlot(entry[0]) != -1 + ) { + continue; + } //skip the ones already on + var label = entry[0]; + if (entry[2] && entry[2].label) { + label = entry[2].label; + } + var data = { content: label, value: entry }; + if (entry[1] == LiteGraph.EVENT) { + data.className = "event"; + } + entries.push(data); + } + } + + if (this.onMenuNodeOutputs) { + entries = this.onMenuNodeOutputs(entries); + } + + if (!entries.length) { + return; + } + + var menu = new LiteGraph.ContextMenu( + entries, + { + event: e, + callback: inner_clicked, + parentMenu: prev_menu, + node: node + }, + ref_window + ); + + function inner_clicked(v, e, prev) { + if (!node) { + return; + } + + if (v.callback) { + v.callback.call(that, node, v, e, prev); + } + + if (!v.value) { + return; + } + + var value = v.value[1]; + + if ( + value && + (value.constructor === Object || value.constructor === Array) + ) { + //submenu why? + var entries = []; + for (var i in value) { + entries.push({ content: i, value: value[i] }); + } + new LiteGraph.ContextMenu(entries, { + event: e, + callback: inner_clicked, + parentMenu: prev_menu, + node: node + }); + return false; + } else { + node.addOutput(v.value[0], v.value[1], v.value[2]); + node.setDirtyCanvas(true, true); + } + } + + return false; + }; + + LGraphCanvas.onShowMenuNodeProperties = function( + value, + options, + e, + prev_menu, + node + ) { + if (!node || !node.properties) { + return; + } + + var that = this; + var canvas = LGraphCanvas.active_canvas; + var ref_window = canvas.getCanvasWindow(); + + var entries = []; + for (var i in node.properties) { + var value = node.properties[i] !== undefined ? node.properties[i] : " "; + if( typeof value == "object" ) + value = JSON.stringify(value); + //value could contain invalid html characters, clean that + value = LGraphCanvas.decodeHTML(value); + entries.push({ + content: + "" + + i + + "" + + "" + + value + + "", + value: i + }); + } + if (!entries.length) { + return; + } + + var menu = new LiteGraph.ContextMenu( + entries, + { + event: e, + callback: inner_clicked, + parentMenu: prev_menu, + allow_html: true, + node: node + }, + ref_window + ); + + function inner_clicked(v, options, e, prev) { + if (!node) { + return; + } + var rect = this.getBoundingClientRect(); + canvas.showEditPropertyValue(node, v.value, { + position: [rect.left, rect.top] + }); + } + + return false; + }; + + LGraphCanvas.decodeHTML = function(str) { + var e = document.createElement("div"); + e.innerText = str; + return e.innerHTML; + }; + + LGraphCanvas.onResizeNode = function(value, options, e, menu, node) { + if (!node) { + return; + } + node.size = node.computeSize(); + if (node.onResize) + node.onResize(node.size); + node.setDirtyCanvas(true, true); + }; + + LGraphCanvas.prototype.showLinkMenu = function(link, e) { + var that = this; + console.log(link); + var options = ["Add Node",null,"Delete"]; + var menu = new LiteGraph.ContextMenu(options, { + event: e, + title: link.data != null ? link.data.constructor.name : null, + callback: inner_clicked + }); + + function inner_clicked(v,options,e) { + switch (v) { + case "Add Node": + LGraphCanvas.onMenuAdd(null, null, e, menu, function(node){ + console.log("node autoconnect"); + var node_left = that.graph.getNodeById( link.origin_id ); + var node_right = that.graph.getNodeById( link.target_id ); + if(!node.inputs || !node.inputs.length || !node.outputs || !node.outputs.length) + return; + if( node_left.outputs[ link.origin_slot ].type == node.inputs[0].type && node.outputs[0].type == node_right.inputs[0].type ) + { + node_left.connect( link.origin_slot, node, 0 ); + node.connect( 0, node_right, link.target_slot ); + node.pos[0] -= node.size[0] * 0.5; + } + }); + break; + case "Delete": + that.graph.removeLink(link.id); + break; + default: + } + } + + return false; + }; + + LGraphCanvas.onShowPropertyEditor = function(item, options, e, menu, node) { + var input_html = ""; + var property = item.property || "title"; + var value = node[property]; + + var dialog = document.createElement("div"); + dialog.className = "graphdialog"; + dialog.innerHTML = + ""; + var title = dialog.querySelector(".name"); + title.innerText = property; + var input = dialog.querySelector("input"); + if (input) { + input.value = value; + input.addEventListener("blur", function(e) { + this.focus(); + }); + input.addEventListener("keydown", function(e) { + if (e.keyCode != 13) { + return; + } + inner(); + e.preventDefault(); + e.stopPropagation(); + }); + } + + var graphcanvas = LGraphCanvas.active_canvas; + var canvas = graphcanvas.canvas; + + var rect = canvas.getBoundingClientRect(); + var offsetx = -20; + var offsety = -20; + if (rect) { + offsetx -= rect.left; + offsety -= rect.top; + } + + if (event) { + dialog.style.left = event.clientX + offsetx + "px"; + dialog.style.top = event.clientY + offsety + "px"; + } else { + dialog.style.left = canvas.width * 0.5 + offsetx + "px"; + dialog.style.top = canvas.height * 0.5 + offsety + "px"; + } + + var button = dialog.querySelector("button"); + button.addEventListener("click", inner); + canvas.parentNode.appendChild(dialog); + + function inner() { + setValue(input.value); + } + + function setValue(value) { + if (item.type == "Number") { + value = Number(value); + } else if (item.type == "Boolean") { + value = Boolean(value); + } + node[property] = value; + if (dialog.parentNode) { + dialog.parentNode.removeChild(dialog); + } + node.setDirtyCanvas(true, true); + } + }; + + LGraphCanvas.prototype.prompt = function(title, value, callback, event) { + var that = this; + var input_html = ""; + title = title || ""; + + var modified = false; + + var dialog = document.createElement("div"); + dialog.className = "graphdialog rounded"; + dialog.innerHTML = + " "; + dialog.close = function() { + that.prompt_box = null; + if (dialog.parentNode) { + dialog.parentNode.removeChild(dialog); + } + }; + + if (this.ds.scale > 1) { + dialog.style.transform = "scale(" + this.ds.scale + ")"; + } + + dialog.addEventListener("mouseleave", function(e) { + if (!modified) { + dialog.close(); + } + }); + + if (that.prompt_box) { + that.prompt_box.close(); + } + that.prompt_box = dialog; + + var first = null; + var timeout = null; + var selected = null; + + var name_element = dialog.querySelector(".name"); + name_element.innerText = title; + var value_element = dialog.querySelector(".value"); + value_element.value = value; + + var input = dialog.querySelector("input"); + input.addEventListener("keydown", function(e) { + modified = true; + if (e.keyCode == 27) { + //ESC + dialog.close(); + } else if (e.keyCode == 13) { + if (callback) { + callback(this.value); + } + dialog.close(); + } else { + return; + } + e.preventDefault(); + e.stopPropagation(); + }); + + var button = dialog.querySelector("button"); + button.addEventListener("click", function(e) { + if (callback) { + callback(input.value); + } + that.setDirty(true); + dialog.close(); + }); + + var graphcanvas = LGraphCanvas.active_canvas; + var canvas = graphcanvas.canvas; + + var rect = canvas.getBoundingClientRect(); + var offsetx = -20; + var offsety = -20; + if (rect) { + offsetx -= rect.left; + offsety -= rect.top; + } + + if (event) { + dialog.style.left = event.clientX + offsetx + "px"; + dialog.style.top = event.clientY + offsety + "px"; + } else { + dialog.style.left = canvas.width * 0.5 + offsetx + "px"; + dialog.style.top = canvas.height * 0.5 + offsety + "px"; + } + + canvas.parentNode.appendChild(dialog); + setTimeout(function() { + input.focus(); + }, 10); + + return dialog; + }; + + LGraphCanvas.search_limit = -1; + LGraphCanvas.prototype.showSearchBox = function(event) { + var that = this; + var input_html = ""; + var graphcanvas = LGraphCanvas.active_canvas; + var canvas = graphcanvas.canvas; + var root_document = canvas.ownerDocument || document; + + var dialog = document.createElement("div"); + dialog.className = "litegraph litesearchbox graphdialog rounded"; + dialog.innerHTML = + "Search
"; + dialog.close = function() { + that.search_box = null; + root_document.body.focus(); + root_document.body.style.overflow = ""; + + setTimeout(function() { + that.canvas.focus(); + }, 20); //important, if canvas loses focus keys wont be captured + if (dialog.parentNode) { + dialog.parentNode.removeChild(dialog); + } + }; + + var timeout_close = null; + + if (this.ds.scale > 1) { + dialog.style.transform = "scale(" + this.ds.scale + ")"; + } + + dialog.addEventListener("mouseenter", function(e) { + if (timeout_close) { + clearTimeout(timeout_close); + timeout_close = null; + } + }); + + dialog.addEventListener("mouseleave", function(e) { + //dialog.close(); + timeout_close = setTimeout(function() { + dialog.close(); + }, 500); + }); + + if (that.search_box) { + that.search_box.close(); + } + that.search_box = dialog; + + var helper = dialog.querySelector(".helper"); + + var first = null; + var timeout = null; + var selected = null; + + var input = dialog.querySelector("input"); + if (input) { + input.addEventListener("blur", function(e) { + this.focus(); + }); + input.addEventListener("keydown", function(e) { + if (e.keyCode == 38) { + //UP + changeSelection(false); + } else if (e.keyCode == 40) { + //DOWN + changeSelection(true); + } else if (e.keyCode == 27) { + //ESC + dialog.close(); + } else if (e.keyCode == 13) { + if (selected) { + select(selected.innerHTML); + } else if (first) { + select(first); + } else { + dialog.close(); + } + } else { + if (timeout) { + clearInterval(timeout); + } + timeout = setTimeout(refreshHelper, 10); + return; + } + e.preventDefault(); + e.stopPropagation(); + e.stopImmediatePropagation(); + return true; + }); + } + + if( root_document.fullscreenElement ) + root_document.fullscreenElement.appendChild(dialog); + else + { + root_document.body.appendChild(dialog); + root_document.body.style.overflow = "hidden"; + } + + //compute best position + var rect = canvas.getBoundingClientRect(); + + var left = ( event ? event.clientX : (rect.left + rect.width * 0.5) ) - 80; + var top = ( event ? event.clientY : (rect.top + rect.height * 0.5) ) - 20; + dialog.style.left = left + "px"; + dialog.style.top = top + "px"; + + //To avoid out of screen problems + if(event.layerY > (rect.height - 200)) + helper.style.maxHeight = (rect.height - event.layerY - 20) + "px"; + + /* + var offsetx = -20; + var offsety = -20; + if (rect) { + offsetx -= rect.left; + offsety -= rect.top; + } + + if (event) { + dialog.style.left = event.clientX + offsetx + "px"; + dialog.style.top = event.clientY + offsety + "px"; + } else { + dialog.style.left = canvas.width * 0.5 + offsetx + "px"; + dialog.style.top = canvas.height * 0.5 + offsety + "px"; + } + canvas.parentNode.appendChild(dialog); + */ + + input.focus(); + + function select(name) { + if (name) { + if (that.onSearchBoxSelection) { + that.onSearchBoxSelection(name, event, graphcanvas); + } else { + var extra = LiteGraph.searchbox_extras[name.toLowerCase()]; + if (extra) { + name = extra.type; + } + + var node = LiteGraph.createNode(name); + if (node) { + node.pos = graphcanvas.convertEventToCanvasOffset( + event + ); + graphcanvas.graph.add(node); + } + + if (extra && extra.data) { + if (extra.data.properties) { + for (var i in extra.data.properties) { + node.addProperty( i, extra.data.properties[i] ); + } + } + if (extra.data.inputs) { + node.inputs = []; + for (var i in extra.data.inputs) { + node.addOutput( + extra.data.inputs[i][0], + extra.data.inputs[i][1] + ); + } + } + if (extra.data.outputs) { + node.outputs = []; + for (var i in extra.data.outputs) { + node.addOutput( + extra.data.outputs[i][0], + extra.data.outputs[i][1] + ); + } + } + if (extra.data.title) { + node.title = extra.data.title; + } + if (extra.data.json) { + node.configure(extra.data.json); + } + } + } + } + + dialog.close(); + } + + function changeSelection(forward) { + var prev = selected; + if (selected) { + selected.classList.remove("selected"); + } + if (!selected) { + selected = forward + ? helper.childNodes[0] + : helper.childNodes[helper.childNodes.length]; + } else { + selected = forward + ? selected.nextSibling + : selected.previousSibling; + if (!selected) { + selected = prev; + } + } + if (!selected) { + return; + } + selected.classList.add("selected"); + selected.scrollIntoView({block: "end", behavior: "smooth"}); + } + + function refreshHelper() { + timeout = null; + var str = input.value; + first = null; + helper.innerHTML = ""; + if (!str) { + return; + } + + if (that.onSearchBox) { + var list = that.onSearchBox(helper, str, graphcanvas); + if (list) { + for (var i = 0; i < list.length; ++i) { + addResult(list[i]); + } + } + } else { + var c = 0; + str = str.toLowerCase(); + var filter = graphcanvas.filter || graphcanvas.graph.filter; + + //extras + for (var i in LiteGraph.searchbox_extras) { + var extra = LiteGraph.searchbox_extras[i]; + if (extra.desc.toLowerCase().indexOf(str) === -1) { + continue; + } + var ctor = LiteGraph.registered_node_types[ extra.type ]; + if( ctor && ctor.filter && ctor.filter != filter ) + continue; + addResult( extra.desc, "searchbox_extra" ); + if ( LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit ) { + break; + } + } + + var filtered = null; + if (Array.prototype.filter) { //filter supported + var keys = Object.keys( LiteGraph.registered_node_types ); //types + var filtered = keys.filter( inner_test_filter ); + } else { + filtered = []; + for (var i in LiteGraph.registered_node_types) { + if( inner_test_filter(i) ) + filtered.push(i); + } + } + + for (var i = 0; i < filtered.length; i++) { + addResult(filtered[i]); + if ( LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit ) { + break; + } + } + + function inner_test_filter( type ) + { + var ctor = LiteGraph.registered_node_types[ type ]; + if(filter && ctor.filter != filter ) + return false; + return type.toLowerCase().indexOf(str) !== -1; + } + } + + function addResult(type, className) { + var help = document.createElement("div"); + if (!first) { + first = type; + } + help.innerText = type; + help.dataset["type"] = escape(type); + help.className = "litegraph lite-search-item"; + if (className) { + help.className += " " + className; + } + help.addEventListener("click", function(e) { + select(unescape(this.dataset["type"])); + }); + helper.appendChild(help); + } + } + + return dialog; + }; + + LGraphCanvas.prototype.showEditPropertyValue = function( node, property, options ) { + if (!node || node.properties[property] === undefined) { + return; + } + + options = options || {}; + var that = this; + + var info = node.getPropertyInfo(property); + var type = info.type; + + var input_html = ""; + + if (type == "string" || type == "number" || type == "array" || type == "object") { + input_html = ""; + } else if (type == "enum" && info.values) { + input_html = ""; + } else if (type == "boolean") { + input_html = + ""; + } else { + console.warn("unknown type: " + type); + return; + } + + var dialog = this.createDialog( + "" + + property + + "" + + input_html + + "", + options + ); + + if (type == "enum" && info.values) { + var input = dialog.querySelector("select"); + input.addEventListener("change", function(e) { + setValue(e.target.value); + //var index = e.target.value; + //setValue( e.options[e.selectedIndex].value ); + }); + } else if (type == "boolean") { + var input = dialog.querySelector("input"); + if (input) { + input.addEventListener("click", function(e) { + setValue(!!input.checked); + }); + } + } else { + var input = dialog.querySelector("input"); + if (input) { + input.addEventListener("blur", function(e) { + this.focus(); + }); + var v = node.properties[property] !== undefined ? node.properties[property] : ""; + v = JSON.stringify(v); + input.value = v; + input.addEventListener("keydown", function(e) { + if (e.keyCode != 13) { + return; + } + inner(); + e.preventDefault(); + e.stopPropagation(); + }); + } + } + + var button = dialog.querySelector("button"); + button.addEventListener("click", inner); + + function inner() { + setValue(input.value); + } + + function setValue(value) { + if (typeof node.properties[property] == "number") { + value = Number(value); + } + if (type == "array" || type == "object") { + value = JSON.parse(value); + } + node.properties[property] = value; + if (node._graph) { + node._graph._version++; + } + if (node.onPropertyChanged) { + node.onPropertyChanged(property, value); + } + if(options.onclose) + options.onclose(); + dialog.close(); + node.setDirtyCanvas(true, true); + } + + return dialog; + }; + + LGraphCanvas.prototype.createDialog = function(html, options) { + options = options || {}; + + var dialog = document.createElement("div"); + dialog.className = "graphdialog"; + dialog.innerHTML = html; + + var rect = this.canvas.getBoundingClientRect(); + var offsetx = -20; + var offsety = -20; + if (rect) { + offsetx -= rect.left; + offsety -= rect.top; + } + + if (options.position) { + offsetx += options.position[0]; + offsety += options.position[1]; + } else if (options.event) { + offsetx += options.event.clientX; + offsety += options.event.clientY; + } //centered + else { + offsetx += this.canvas.width * 0.5; + offsety += this.canvas.height * 0.5; + } + + dialog.style.left = offsetx + "px"; + dialog.style.top = offsety + "px"; + + this.canvas.parentNode.appendChild(dialog); + + dialog.close = function() { + if (this.parentNode) { + this.parentNode.removeChild(this); + } + }; + + return dialog; + }; + + LGraphCanvas.onMenuNodeCollapse = function(value, options, e, menu, node) { + node.collapse(); + }; + + LGraphCanvas.onMenuNodePin = function(value, options, e, menu, node) { + node.pin(); + }; + + LGraphCanvas.onMenuNodeMode = function(value, options, e, menu, node) { + new LiteGraph.ContextMenu( + ["Always", "On Event", "On Trigger", "Never"], + { event: e, callback: inner_clicked, parentMenu: menu, node: node } + ); + + function inner_clicked(v) { + if (!node) { + return; + } + switch (v) { + case "On Event": + node.mode = LiteGraph.ON_EVENT; + break; + case "On Trigger": + node.mode = LiteGraph.ON_TRIGGER; + break; + case "Never": + node.mode = LiteGraph.NEVER; + break; + case "Always": + default: + node.mode = LiteGraph.ALWAYS; + break; + } + } + + return false; + }; + + LGraphCanvas.onMenuNodeColors = function(value, options, e, menu, node) { + if (!node) { + throw "no node for color"; + } + + var values = []; + values.push({ + value: null, + content: + "No color" + }); + + for (var i in LGraphCanvas.node_colors) { + var color = LGraphCanvas.node_colors[i]; + var value = { + value: i, + content: + "" + + i + + "" + }; + values.push(value); + } + new LiteGraph.ContextMenu(values, { + event: e, + callback: inner_clicked, + parentMenu: menu, + node: node + }); + + function inner_clicked(v) { + if (!node) { + return; + } + + var color = v.value ? LGraphCanvas.node_colors[v.value] : null; + if (color) { + if (node.constructor === LiteGraph.LGraphGroup) { + node.color = color.groupcolor; + } else { + node.color = color.color; + node.bgcolor = color.bgcolor; + } + } else { + delete node.color; + delete node.bgcolor; + } + node.setDirtyCanvas(true, true); + } + + return false; + }; + + LGraphCanvas.onMenuNodeShapes = function(value, options, e, menu, node) { + if (!node) { + throw "no node passed"; + } + + new LiteGraph.ContextMenu(LiteGraph.VALID_SHAPES, { + event: e, + callback: inner_clicked, + parentMenu: menu, + node: node + }); + + function inner_clicked(v) { + if (!node) { + return; + } + node.shape = v; + node.setDirtyCanvas(true); + } + + return false; + }; + + LGraphCanvas.onMenuNodeRemove = function(value, options, e, menu, node) { + if (!node) { + throw "no node passed"; + } + + if (node.removable === false) { + return; + } + + node.graph.remove(node); + node.setDirtyCanvas(true, true); + }; + + LGraphCanvas.onMenuNodeToSubgraph = function(value, options, e, menu, node) { + var graph = node.graph; + var graphcanvas = LGraphCanvas.active_canvas; + if(!graphcanvas) //?? + return; + + var nodes_list = Object.values( graphcanvas.selected_nodes || {} ); + if( !nodes_list.length ) + nodes_list = [ node ]; + + var subgraph_node = LiteGraph.createNode("graph/subgraph"); + subgraph_node.pos = node.pos.concat(); + graph.add(subgraph_node); + + subgraph_node.buildFromNodes( nodes_list ); + + graphcanvas.deselectAllNodes(); + node.setDirtyCanvas(true, true); + }; + + LGraphCanvas.onMenuNodeClone = function(value, options, e, menu, node) { + if (node.clonable == false) { + return; + } + var newnode = node.clone(); + if (!newnode) { + return; + } + newnode.pos = [node.pos[0] + 5, node.pos[1] + 5]; + node.graph.add(newnode); + node.setDirtyCanvas(true, true); + }; + + LGraphCanvas.node_colors = { + red: { color: "#322", bgcolor: "#533", groupcolor: "#A88" }, + brown: { color: "#332922", bgcolor: "#593930", groupcolor: "#b06634" }, + green: { color: "#232", bgcolor: "#353", groupcolor: "#8A8" }, + blue: { color: "#223", bgcolor: "#335", groupcolor: "#88A" }, + pale_blue: { + color: "#2a363b", + bgcolor: "#3f5159", + groupcolor: "#3f789e" + }, + cyan: { color: "#233", bgcolor: "#355", groupcolor: "#8AA" }, + purple: { color: "#323", bgcolor: "#535", groupcolor: "#a1309b" }, + yellow: { color: "#432", bgcolor: "#653", groupcolor: "#b58b2a" }, + black: { color: "#222", bgcolor: "#000", groupcolor: "#444" } + }; + + LGraphCanvas.prototype.getCanvasMenuOptions = function() { + var options = null; + if (this.getMenuOptions) { + options = this.getMenuOptions(); + } else { + options = [ + { + content: "Add Node", + has_submenu: true, + callback: LGraphCanvas.onMenuAdd + }, + { content: "Add Group", callback: LGraphCanvas.onGroupAdd } + //{content:"Collapse All", callback: LGraphCanvas.onMenuCollapseAll } + ]; + + if (this._graph_stack && this._graph_stack.length > 0) { + options.push(null, { + content: "Close subgraph", + callback: this.closeSubgraph.bind(this) + }); + } + } + + if (this.getExtraMenuOptions) { + var extra = this.getExtraMenuOptions(this, options); + if (extra) { + options = options.concat(extra); + } + } + + return options; + }; + + //called by processContextMenu to extract the menu list + LGraphCanvas.prototype.getNodeMenuOptions = function(node) { + var options = null; + + if (node.getMenuOptions) { + options = node.getMenuOptions(this); + } else { + options = [ + { + content: "Inputs", + has_submenu: true, + disabled: true, + callback: LGraphCanvas.showMenuNodeOptionalInputs + }, + { + content: "Outputs", + has_submenu: true, + disabled: true, + callback: LGraphCanvas.showMenuNodeOptionalOutputs + }, + null, + { + content: "Properties", + has_submenu: true, + callback: LGraphCanvas.onShowMenuNodeProperties + }, + null, + { + content: "Title", + callback: LGraphCanvas.onShowPropertyEditor + }, + { + content: "Mode", + has_submenu: true, + callback: LGraphCanvas.onMenuNodeMode + }, + { content: "Resize", callback: LGraphCanvas.onResizeNode }, + { + content: "Collapse", + callback: LGraphCanvas.onMenuNodeCollapse + }, + { content: "Pin", callback: LGraphCanvas.onMenuNodePin }, + { + content: "Colors", + has_submenu: true, + callback: LGraphCanvas.onMenuNodeColors + }, + { + content: "Shapes", + has_submenu: true, + callback: LGraphCanvas.onMenuNodeShapes + }, + null + ]; + } + + if (node.onGetInputs) { + var inputs = node.onGetInputs(); + if (inputs && inputs.length) { + options[0].disabled = false; + } + } + + if (node.onGetOutputs) { + var outputs = node.onGetOutputs(); + if (outputs && outputs.length) { + options[1].disabled = false; + } + } + + if (node.getExtraMenuOptions) { + var extra = node.getExtraMenuOptions(this); + if (extra) { + extra.push(null); + options = extra.concat(options); + } + } + + if (node.clonable !== false) { + options.push({ + content: "Clone", + callback: LGraphCanvas.onMenuNodeClone + }); + } + + if(0) //TODO + options.push({ + content: "To Subgraph", + callback: LGraphCanvas.onMenuNodeToSubgraph + }); + + if (node.removable !== false) { + options.push(null, { + content: "Remove", + callback: LGraphCanvas.onMenuNodeRemove + }); + } + + if (node.graph && node.graph.onGetNodeMenuOptions) { + node.graph.onGetNodeMenuOptions(options, node); + } + + return options; + }; + + LGraphCanvas.prototype.getGroupMenuOptions = function(node) { + var o = [ + { content: "Title", callback: LGraphCanvas.onShowPropertyEditor }, + { + content: "Color", + has_submenu: true, + callback: LGraphCanvas.onMenuNodeColors + }, + { + content: "Font size", + property: "font_size", + type: "Number", + callback: LGraphCanvas.onShowPropertyEditor + }, + null, + { content: "Remove", callback: LGraphCanvas.onMenuNodeRemove } + ]; + + return o; + }; + + LGraphCanvas.prototype.processContextMenu = function(node, event) { + var that = this; + var canvas = LGraphCanvas.active_canvas; + var ref_window = canvas.getCanvasWindow(); + + var menu_info = null; + var options = { + event: event, + callback: inner_option_clicked, + extra: node + }; + + if(node) + options.title = node.type; + + //check if mouse is in input + var slot = null; + if (node) { + slot = node.getSlotInPosition(event.canvasX, event.canvasY); + LGraphCanvas.active_node = node; + } + + if (slot) { + //on slot + menu_info = []; + if (node.getSlotMenuOptions) { + menu_info = node.getSlotMenuOptions(slot); + } else { + if ( + slot && + slot.output && + slot.output.links && + slot.output.links.length + ) { + menu_info.push({ content: "Disconnect Links", slot: slot }); + } + var _slot = slot.input || slot.output; + menu_info.push( + _slot.locked + ? "Cannot remove" + : { content: "Remove Slot", slot: slot } + ); + menu_info.push( + _slot.nameLocked + ? "Cannot rename" + : { content: "Rename Slot", slot: slot } + ); + + } + options.title = + (slot.input ? slot.input.type : slot.output.type) || "*"; + if (slot.input && slot.input.type == LiteGraph.ACTION) { + options.title = "Action"; + } + if (slot.output && slot.output.type == LiteGraph.EVENT) { + options.title = "Event"; + } + } else { + if (node) { + //on node + menu_info = this.getNodeMenuOptions(node); + } else { + menu_info = this.getCanvasMenuOptions(); + var group = this.graph.getGroupOnPos( + event.canvasX, + event.canvasY + ); + if (group) { + //on group + menu_info.push(null, { + content: "Edit Group", + has_submenu: true, + submenu: { + title: "Group", + extra: group, + options: this.getGroupMenuOptions(group) + } + }); + } + } + } + + //show menu + if (!menu_info) { + return; + } + + var menu = new LiteGraph.ContextMenu(menu_info, options, ref_window); + + function inner_option_clicked(v, options, e) { + if (!v) { + return; + } + + if (v.content == "Remove Slot") { + var info = v.slot; + if (info.input) { + node.removeInput(info.slot); + } else if (info.output) { + node.removeOutput(info.slot); + } + return; + } else if (v.content == "Disconnect Links") { + var info = v.slot; + if (info.output) { + node.disconnectOutput(info.slot); + } else if (info.input) { + node.disconnectInput(info.slot); + } + return; + } else if (v.content == "Rename Slot") { + var info = v.slot; + var slot_info = info.input + ? node.getInputInfo(info.slot) + : node.getOutputInfo(info.slot); + var dialog = that.createDialog( + "Name", + options + ); + var input = dialog.querySelector("input"); + if (input && slot_info) { + input.value = slot_info.label || ""; + } + dialog + .querySelector("button") + .addEventListener("click", function(e) { + if (input.value) { + if (slot_info) { + slot_info.label = input.value; + } + that.setDirty(true); + } + dialog.close(); + }); + } + + //if(v.callback) + // return v.callback.call(that, node, options, e, menu, that, event ); + } + }; + + //API ************************************************* + //like rect but rounded corners + if (typeof(window) != "undefined" && window.CanvasRenderingContext2D) { + window.CanvasRenderingContext2D.prototype.roundRect = function( + x, + y, + width, + height, + radius, + radius_low + ) { + if (radius === undefined) { + radius = 5; + } + + if (radius_low === undefined) { + radius_low = radius; + } + + this.moveTo(x + radius, y); + this.lineTo(x + width - radius, y); + this.quadraticCurveTo(x + width, y, x + width, y + radius); + + this.lineTo(x + width, y + height - radius_low); + this.quadraticCurveTo( + x + width, + y + height, + x + width - radius_low, + y + height + ); + this.lineTo(x + radius_low, y + height); + this.quadraticCurveTo(x, y + height, x, y + height - radius_low); + this.lineTo(x, y + radius); + this.quadraticCurveTo(x, y, x + radius, y); + }; + } + + function compareObjects(a, b) { + for (var i in a) { + if (a[i] != b[i]) { + return false; + } + } + return true; + } + LiteGraph.compareObjects = compareObjects; + + function distance(a, b) { + return Math.sqrt( + (b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1]) + ); + } + LiteGraph.distance = distance; + + function colorToString(c) { + return ( + "rgba(" + + Math.round(c[0] * 255).toFixed() + + "," + + Math.round(c[1] * 255).toFixed() + + "," + + Math.round(c[2] * 255).toFixed() + + "," + + (c.length == 4 ? c[3].toFixed(2) : "1.0") + + ")" + ); + } + LiteGraph.colorToString = colorToString; + + function isInsideRectangle(x, y, left, top, width, height) { + if (left < x && left + width > x && top < y && top + height > y) { + return true; + } + return false; + } + LiteGraph.isInsideRectangle = isInsideRectangle; + + //[minx,miny,maxx,maxy] + function growBounding(bounding, x, y) { + if (x < bounding[0]) { + bounding[0] = x; + } else if (x > bounding[2]) { + bounding[2] = x; + } + + if (y < bounding[1]) { + bounding[1] = y; + } else if (y > bounding[3]) { + bounding[3] = y; + } + } + LiteGraph.growBounding = growBounding; + + //point inside bounding box + function isInsideBounding(p, bb) { + if ( + p[0] < bb[0][0] || + p[1] < bb[0][1] || + p[0] > bb[1][0] || + p[1] > bb[1][1] + ) { + return false; + } + return true; + } + LiteGraph.isInsideBounding = isInsideBounding; + + //bounding overlap, format: [ startx, starty, width, height ] + function overlapBounding(a, b) { + var A_end_x = a[0] + a[2]; + var A_end_y = a[1] + a[3]; + var B_end_x = b[0] + b[2]; + var B_end_y = b[1] + b[3]; + + if ( + a[0] > B_end_x || + a[1] > B_end_y || + A_end_x < b[0] || + A_end_y < b[1] + ) { + return false; + } + return true; + } + LiteGraph.overlapBounding = overlapBounding; + + //Convert a hex value to its decimal value - the inputted hex must be in the + // format of a hex triplet - the kind we use for HTML colours. The function + // will return an array with three values. + function hex2num(hex) { + if (hex.charAt(0) == "#") { + hex = hex.slice(1); + } //Remove the '#' char - if there is one. + hex = hex.toUpperCase(); + var hex_alphabets = "0123456789ABCDEF"; + var value = new Array(3); + var k = 0; + var int1, int2; + for (var i = 0; i < 6; i += 2) { + int1 = hex_alphabets.indexOf(hex.charAt(i)); + int2 = hex_alphabets.indexOf(hex.charAt(i + 1)); + value[k] = int1 * 16 + int2; + k++; + } + return value; + } + + LiteGraph.hex2num = hex2num; + + //Give a array with three values as the argument and the function will return + // the corresponding hex triplet. + function num2hex(triplet) { + var hex_alphabets = "0123456789ABCDEF"; + var hex = "#"; + var int1, int2; + for (var i = 0; i < 3; i++) { + int1 = triplet[i] / 16; + int2 = triplet[i] % 16; + + hex += hex_alphabets.charAt(int1) + hex_alphabets.charAt(int2); + } + return hex; + } + + LiteGraph.num2hex = num2hex; + + /* LiteGraph GUI elements used for canvas editing *************************************/ + + /** + * ContextMenu from LiteGUI + * + * @class ContextMenu + * @constructor + * @param {Array} values (allows object { title: "Nice text", callback: function ... }) + * @param {Object} options [optional] Some options:\ + * - title: title to show on top of the menu + * - callback: function to call when an option is clicked, it receives the item information + * - ignore_item_callbacks: ignores the callback inside the item, it just calls the options.callback + * - event: you can pass a MouseEvent, this way the ContextMenu appears in that position + */ + function ContextMenu(values, options) { + options = options || {}; + this.options = options; + var that = this; + + //to link a menu with its parent + if (options.parentMenu) { + if (options.parentMenu.constructor !== this.constructor) { + console.error( + "parentMenu must be of class ContextMenu, ignoring it" + ); + options.parentMenu = null; + } else { + this.parentMenu = options.parentMenu; + this.parentMenu.lock = true; + this.parentMenu.current_submenu = this; + } + } + + var eventClass = null; + if(options.event) //use strings because comparing classes between windows doesnt work + eventClass = options.event.constructor.name; + if ( eventClass !== "MouseEvent" && + eventClass !== "CustomEvent" && + eventClass !== "PointerEvent" + ) { + console.error( + "Event passed to ContextMenu is not of type MouseEvent or CustomEvent. Ignoring it." + ); + options.event = null; + } + + var root = document.createElement("div"); + root.className = "litegraph litecontextmenu litemenubar-panel"; + if (options.className) { + root.className += " " + options.className; + } + root.style.minWidth = 100; + root.style.minHeight = 100; + root.style.pointerEvents = "none"; + setTimeout(function() { + root.style.pointerEvents = "auto"; + }, 100); //delay so the mouse up event is not caught by this element + + //this prevents the default context browser menu to open in case this menu was created when pressing right button + root.addEventListener( + "mouseup", + function(e) { + e.preventDefault(); + return true; + }, + true + ); + root.addEventListener( + "contextmenu", + function(e) { + if (e.button != 2) { + //right button + return false; + } + e.preventDefault(); + return false; + }, + true + ); + + root.addEventListener( + "mousedown", + function(e) { + if (e.button == 2) { + that.close(); + e.preventDefault(); + return true; + } + }, + true + ); + + function on_mouse_wheel(e) { + var pos = parseInt(root.style.top); + root.style.top = + (pos + e.deltaY * options.scroll_speed).toFixed() + "px"; + e.preventDefault(); + return true; + } + + if (!options.scroll_speed) { + options.scroll_speed = 0.1; + } + + root.addEventListener("wheel", on_mouse_wheel, true); + root.addEventListener("mousewheel", on_mouse_wheel, true); + + this.root = root; + + //title + if (options.title) { + var element = document.createElement("div"); + element.className = "litemenu-title"; + element.innerHTML = options.title; + root.appendChild(element); + } + + //entries + var num = 0; + for (var i in values) { + var name = values.constructor == Array ? values[i] : i; + if (name != null && name.constructor !== String) { + name = name.content === undefined ? String(name) : name.content; + } + var value = values[i]; + this.addItem(name, value, options); + num++; + } + + //close on leave + root.addEventListener("mouseleave", function(e) { + if (that.lock) { + return; + } + if (root.closing_timer) { + clearTimeout(root.closing_timer); + } + root.closing_timer = setTimeout(that.close.bind(that, e), 500); + //that.close(e); + }); + + root.addEventListener("mouseenter", function(e) { + if (root.closing_timer) { + clearTimeout(root.closing_timer); + } + }); + + //insert before checking position + var root_document = document; + if (options.event) { + root_document = options.event.target.ownerDocument; + } + + if (!root_document) { + root_document = document; + } + + if( root_document.fullscreenElement ) + root_document.fullscreenElement.appendChild(root); + else + root_document.body.appendChild(root); + + //compute best position + var left = options.left || 0; + var top = options.top || 0; + if (options.event) { + left = options.event.clientX - 10; + top = options.event.clientY - 10; + if (options.title) { + top -= 20; + } + + if (options.parentMenu) { + var rect = options.parentMenu.root.getBoundingClientRect(); + left = rect.left + rect.width; + } + + var body_rect = document.body.getBoundingClientRect(); + var root_rect = root.getBoundingClientRect(); + if(body_rect.height == 0) + console.error("document.body height is 0. That is dangerous, set html,body { height: 100%; }"); + + if (body_rect.width && left > body_rect.width - root_rect.width - 10) { + left = body_rect.width - root_rect.width - 10; + } + if (body_rect.height && top > body_rect.height - root_rect.height - 10) { + top = body_rect.height - root_rect.height - 10; + } + } + + root.style.left = left + "px"; + root.style.top = top + "px"; + + if (options.scale) { + root.style.transform = "scale(" + options.scale + ")"; + } + } + + ContextMenu.prototype.addItem = function(name, value, options) { + var that = this; + options = options || {}; + + var element = document.createElement("div"); + element.className = "litemenu-entry submenu"; + + var disabled = false; + + if (value === null) { + element.classList.add("separator"); + //element.innerHTML = "
" + //continue; + } else { + element.innerHTML = value && value.title ? value.title : name; + element.value = value; + + if (value) { + if (value.disabled) { + disabled = true; + element.classList.add("disabled"); + } + if (value.submenu || value.has_submenu) { + element.classList.add("has_submenu"); + } + } + + if (typeof value == "function") { + element.dataset["value"] = name; + element.onclick_callback = value; + } else { + element.dataset["value"] = value; + } + + if (value.className) { + element.className += " " + value.className; + } + } + + this.root.appendChild(element); + if (!disabled) { + element.addEventListener("click", inner_onclick); + } + if (options.autoopen) { + element.addEventListener("mouseenter", inner_over); + } + + function inner_over(e) { + var value = this.value; + if (!value || !value.has_submenu) { + return; + } + //if it is a submenu, autoopen like the item was clicked + inner_onclick.call(this, e); + } + + //menu option clicked + function inner_onclick(e) { + var value = this.value; + var close_parent = true; + + if (that.current_submenu) { + that.current_submenu.close(e); + } + + //global callback + if (options.callback) { + var r = options.callback.call( + this, + value, + options, + e, + that, + options.node + ); + if (r === true) { + close_parent = false; + } + } + + //special cases + if (value) { + if ( + value.callback && + !options.ignore_item_callbacks && + value.disabled !== true + ) { + //item callback + var r = value.callback.call( + this, + value, + options, + e, + that, + options.extra + ); + if (r === true) { + close_parent = false; + } + } + if (value.submenu) { + if (!value.submenu.options) { + throw "ContextMenu submenu needs options"; + } + var submenu = new that.constructor(value.submenu.options, { + callback: value.submenu.callback, + event: e, + parentMenu: that, + ignore_item_callbacks: + value.submenu.ignore_item_callbacks, + title: value.submenu.title, + extra: value.submenu.extra, + autoopen: options.autoopen + }); + close_parent = false; + } + } + + if (close_parent && !that.lock) { + that.close(); + } + } + + return element; + }; + + ContextMenu.prototype.close = function(e, ignore_parent_menu) { + if (this.root.parentNode) { + this.root.parentNode.removeChild(this.root); + } + if (this.parentMenu && !ignore_parent_menu) { + this.parentMenu.lock = false; + this.parentMenu.current_submenu = null; + if (e === undefined) { + this.parentMenu.close(); + } else if ( + e && + !ContextMenu.isCursorOverElement(e, this.parentMenu.root) + ) { + ContextMenu.trigger(this.parentMenu.root, "mouseleave", e); + } + } + if (this.current_submenu) { + this.current_submenu.close(e, true); + } + + if (this.root.closing_timer) { + clearTimeout(this.root.closing_timer); + } + }; + + //this code is used to trigger events easily (used in the context menu mouseleave + ContextMenu.trigger = function(element, event_name, params, origin) { + var evt = document.createEvent("CustomEvent"); + evt.initCustomEvent(event_name, true, true, params); //canBubble, cancelable, detail + evt.srcElement = origin; + if (element.dispatchEvent) { + element.dispatchEvent(evt); + } else if (element.__events) { + element.__events.dispatchEvent(evt); + } + //else nothing seems binded here so nothing to do + return evt; + }; + + //returns the top most menu + ContextMenu.prototype.getTopMenu = function() { + if (this.options.parentMenu) { + return this.options.parentMenu.getTopMenu(); + } + return this; + }; + + ContextMenu.prototype.getFirstEvent = function() { + if (this.options.parentMenu) { + return this.options.parentMenu.getFirstEvent(); + } + return this.options.event; + }; + + ContextMenu.isCursorOverElement = function(event, element) { + var left = event.clientX; + var top = event.clientY; + var rect = element.getBoundingClientRect(); + if (!rect) { + return false; + } + if ( + top > rect.top && + top < rect.top + rect.height && + left > rect.left && + left < rect.left + rect.width + ) { + return true; + } + return false; + }; + + LiteGraph.ContextMenu = ContextMenu; + + LiteGraph.closeAllContextMenus = function(ref_window) { + ref_window = ref_window || window; + + var elements = ref_window.document.querySelectorAll(".litecontextmenu"); + if (!elements.length) { + return; + } + + var result = []; + for (var i = 0; i < elements.length; i++) { + result.push(elements[i]); + } + + for (var i in result) { + if (result[i].close) { + result[i].close(); + } else if (result[i].parentNode) { + result[i].parentNode.removeChild(result[i]); + } + } + }; + + LiteGraph.extendClass = function(target, origin) { + for (var i in origin) { + //copy class properties + if (target.hasOwnProperty(i)) { + continue; + } + target[i] = origin[i]; + } + + if (origin.prototype) { + //copy prototype properties + for (var i in origin.prototype) { + //only enumerable + if (!origin.prototype.hasOwnProperty(i)) { + continue; + } + + if (target.prototype.hasOwnProperty(i)) { + //avoid overwriting existing ones + continue; + } + + //copy getters + if (origin.prototype.__lookupGetter__(i)) { + target.prototype.__defineGetter__( + i, + origin.prototype.__lookupGetter__(i) + ); + } else { + target.prototype[i] = origin.prototype[i]; + } + + //and setters + if (origin.prototype.__lookupSetter__(i)) { + target.prototype.__defineSetter__( + i, + origin.prototype.__lookupSetter__(i) + ); + } + } + } + }; + + //used by some widgets to render a curve editor + function CurveEditor( points ) + { + this.points = points; + this.selected = -1; + this.nearest = -1; + this.size = null; //stores last size used + this.must_update = true; + this.margin = 5; + } + + CurveEditor.sampleCurve = function(f,points) + { + if(!points) + return; + for(var i = 0; i < points.length - 1; ++i) + { + var p = points[i]; + var pn = points[i+1]; + if(pn[0] < f) + continue; + var r = (pn[0] - p[0]); + if( Math.abs(r) < 0.00001 ) + return p[1]; + var local_f = (f - p[0]) / r; + return p[1] * (1.0 - local_f) + pn[1] * local_f; + } + return 0; + } + + CurveEditor.prototype.draw = function( ctx, size, graphcanvas, background_color, line_color, inactive ) + { + var points = this.points; + if(!points) + return; + this.size = size; + var w = size[0] - this.margin * 2; + var h = size[1] - this.margin * 2; + + line_color = line_color || "#666"; + + ctx.save(); + ctx.translate(this.margin,this.margin); + + if(background_color) + { + ctx.fillStyle = "#111"; + ctx.fillRect(0,0,w,h); + ctx.fillStyle = "#222"; + ctx.fillRect(w*0.5,0,1,h); + ctx.strokeStyle = "#333"; + ctx.strokeRect(0,0,w,h); + } + ctx.strokeStyle = line_color; + if(inactive) + ctx.globalAlpha = 0.5; + ctx.beginPath(); + for(var i = 0; i < points.length; ++i) + { + var p = points[i]; + ctx.lineTo( p[0] * w, (1.0 - p[1]) * h ); + } + ctx.stroke(); + ctx.globalAlpha = 1; + if(!inactive) + for(var i = 0; i < points.length; ++i) + { + var p = points[i]; + ctx.fillStyle = this.selected == i ? "#FFF" : (this.nearest == i ? "#DDD" : "#AAA"); + ctx.beginPath(); + ctx.arc( p[0] * w, (1.0 - p[1]) * h, 2, 0, Math.PI * 2 ); + ctx.fill(); + } + ctx.restore(); + } + + //localpos is mouse in curve editor space + CurveEditor.prototype.onMouseDown = function( localpos, graphcanvas ) + { + var points = this.points; + if(!points) + return; + if( localpos[1] < 0 ) + return; + + //this.captureInput(true); + var w = this.size[0] - this.margin * 2; + var h = this.size[1] - this.margin * 2; + var x = localpos[0] - this.margin; + var y = localpos[1] - this.margin; + var pos = [x,y]; + var max_dist = 30 / graphcanvas.ds.scale; + //search closer one + this.selected = this.getCloserPoint(pos, max_dist); + //create one + if(this.selected == -1) + { + var point = [x / w, 1 - y / h]; + points.push(point); + points.sort(function(a,b){ return a[0] - b[0]; }); + this.selected = points.indexOf(point); + this.must_update = true; + } + if(this.selected != -1) + return true; + } + + CurveEditor.prototype.onMouseMove = function( localpos, graphcanvas ) + { + var points = this.points; + if(!points) + return; + var s = this.selected; + if(s < 0) + return; + var x = (localpos[0] - this.margin) / (this.size[0] - this.margin * 2 ); + var y = (localpos[1] - this.margin) / (this.size[1] - this.margin * 2 ); + var curvepos = [(localpos[0] - this.margin),(localpos[1] - this.margin)]; + var max_dist = 30 / graphcanvas.ds.scale; + this._nearest = this.getCloserPoint(curvepos, max_dist); + var point = points[s]; + if(point) + { + var is_edge_point = s == 0 || s == points.length - 1; + if( !is_edge_point && (localpos[0] < -10 || localpos[0] > this.size[0] + 10 || localpos[1] < -10 || localpos[1] > this.size[1] + 10) ) + { + points.splice(s,1); + this.selected = -1; + return; + } + if( !is_edge_point ) //not edges + point[0] = Math.clamp(x,0,1); + else + point[0] = s == 0 ? 0 : 1; + point[1] = 1.0 - Math.clamp(y,0,1); + points.sort(function(a,b){ return a[0] - b[0]; }); + this.selected = points.indexOf(point); + this.must_update = true; + } + } + + CurveEditor.prototype.onMouseUp = function( localpos, graphcanvas ) + { + this.selected = -1; + return false; + } + + CurveEditor.prototype.getCloserPoint = function(pos, max_dist) + { + var points = this.points; + if(!points) + return -1; + max_dist = max_dist || 30; + var w = (this.size[0] - this.margin * 2); + var h = (this.size[1] - this.margin * 2); + var num = points.length; + var p2 = [0,0]; + var min_dist = 1000000; + var closest = -1; + var last_valid = -1; + for(var i = 0; i < num; ++i) + { + var p = points[i]; + p2[0] = p[0] * w; + p2[1] = (1.0 - p[1]) * h; + if(p2[0] < pos[0]) + last_valid = i; + var dist = vec2.distance(pos,p2); + if(dist > min_dist || dist > max_dist) + continue; + closest = i; + min_dist = dist; + } + return closest; + } + + LiteGraph.CurveEditor = CurveEditor; + + //used to create nodes from wrapping functions + LiteGraph.getParameterNames = function(func) { + return (func + "") + .replace(/[/][/].*$/gm, "") // strip single-line comments + .replace(/\s+/g, "") // strip white space + .replace(/[/][*][^/*]*[*][/]/g, "") // strip multi-line comments /**/ + .split("){", 1)[0] + .replace(/^[^(]*[(]/, "") // extract the parameters + .replace(/=[^,]+/g, "") // strip any ES6 defaults + .split(",") + .filter(Boolean); // split & filter [""] + }; + + Math.clamp = function(v, a, b) { + return a > v ? a : b < v ? b : v; + }; + + if (typeof window != "undefined" && !window["requestAnimationFrame"]) { + window.requestAnimationFrame = + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + function(callback) { + window.setTimeout(callback, 1000 / 60); + }; + } +})(this); + +if (typeof exports != "undefined") { + exports.LiteGraph = this.LiteGraph; +} + +//basic nodes +(function(global) { + var LiteGraph = global.LiteGraph; + + //Constant + function Time() { + this.addOutput("in ms", "number"); + this.addOutput("in sec", "number"); + } + + Time.title = "Time"; + Time.desc = "Time"; + + Time.prototype.onExecute = function() { + this.setOutputData(0, this.graph.globaltime * 1000); + this.setOutputData(1, this.graph.globaltime); + }; + + LiteGraph.registerNodeType("basic/time", Time); + + //Subgraph: a node that contains a graph + function Subgraph() { + var that = this; + this.size = [140, 80]; + this.properties = { enabled: true }; + this.enabled = true; + + //create inner graph + this.subgraph = new LiteGraph.LGraph(); + this.subgraph._subgraph_node = this; + this.subgraph._is_subgraph = true; + + this.subgraph.onTrigger = this.onSubgraphTrigger.bind(this); + + //nodes input node added inside + this.subgraph.onInputAdded = this.onSubgraphNewInput.bind(this); + this.subgraph.onInputRenamed = this.onSubgraphRenamedInput.bind(this); + this.subgraph.onInputTypeChanged = this.onSubgraphTypeChangeInput.bind(this); + this.subgraph.onInputRemoved = this.onSubgraphRemovedInput.bind(this); + + this.subgraph.onOutputAdded = this.onSubgraphNewOutput.bind(this); + this.subgraph.onOutputRenamed = this.onSubgraphRenamedOutput.bind(this); + this.subgraph.onOutputTypeChanged = this.onSubgraphTypeChangeOutput.bind(this); + this.subgraph.onOutputRemoved = this.onSubgraphRemovedOutput.bind(this); + } + + Subgraph.title = "Subgraph"; + Subgraph.desc = "Graph inside a node"; + Subgraph.title_color = "#334"; + + Subgraph.prototype.onGetInputs = function() { + return [["enabled", "boolean"]]; + }; + + Subgraph.prototype.onDrawTitle = function(ctx) { + if (this.flags.collapsed) { + return; + } + + ctx.fillStyle = "#555"; + var w = LiteGraph.NODE_TITLE_HEIGHT; + var x = this.size[0] - w; + ctx.fillRect(x, -w, w, w); + ctx.fillStyle = "#333"; + ctx.beginPath(); + ctx.moveTo(x + w * 0.2, -w * 0.6); + ctx.lineTo(x + w * 0.8, -w * 0.6); + ctx.lineTo(x + w * 0.5, -w * 0.3); + ctx.fill(); + }; + + Subgraph.prototype.onDblClick = function(e, pos, graphcanvas) { + var that = this; + setTimeout(function() { + graphcanvas.openSubgraph(that.subgraph); + }, 10); + }; + + Subgraph.prototype.onMouseDown = function(e, pos, graphcanvas) { + if ( + !this.flags.collapsed && + pos[0] > this.size[0] - LiteGraph.NODE_TITLE_HEIGHT && + pos[1] < 0 + ) { + var that = this; + setTimeout(function() { + graphcanvas.openSubgraph(that.subgraph); + }, 10); + } + }; + + Subgraph.prototype.onAction = function(action, param) { + this.subgraph.onAction(action, param); + }; + + Subgraph.prototype.onExecute = function() { + this.enabled = this.getInputOrProperty("enabled"); + if (!this.enabled) { + return; + } + + //send inputs to subgraph global inputs + if (this.inputs) { + for (var i = 0; i < this.inputs.length; i++) { + var input = this.inputs[i]; + var value = this.getInputData(i); + this.subgraph.setInputData(input.name, value); + } + } + + //execute + this.subgraph.runStep(); + + //send subgraph global outputs to outputs + if (this.outputs) { + for (var i = 0; i < this.outputs.length; i++) { + var output = this.outputs[i]; + var value = this.subgraph.getOutputData(output.name); + this.setOutputData(i, value); + } + } + }; + + Subgraph.prototype.sendEventToAllNodes = function(eventname, param, mode) { + if (this.enabled) { + this.subgraph.sendEventToAllNodes(eventname, param, mode); + } + }; + + //**** INPUTS *********************************** + Subgraph.prototype.onSubgraphTrigger = function(event, param) { + var slot = this.findOutputSlot(event); + if (slot != -1) { + this.triggerSlot(slot); + } + }; + + Subgraph.prototype.onSubgraphNewInput = function(name, type) { + var slot = this.findInputSlot(name); + if (slot == -1) { + //add input to the node + this.addInput(name, type); + } + }; + + Subgraph.prototype.onSubgraphRenamedInput = function(oldname, name) { + var slot = this.findInputSlot(oldname); + if (slot == -1) { + return; + } + var info = this.getInputInfo(slot); + info.name = name; + }; + + Subgraph.prototype.onSubgraphTypeChangeInput = function(name, type) { + var slot = this.findInputSlot(name); + if (slot == -1) { + return; + } + var info = this.getInputInfo(slot); + info.type = type; + }; + + Subgraph.prototype.onSubgraphRemovedInput = function(name) { + var slot = this.findInputSlot(name); + if (slot == -1) { + return; + } + this.removeInput(slot); + }; + + //**** OUTPUTS *********************************** + Subgraph.prototype.onSubgraphNewOutput = function(name, type) { + var slot = this.findOutputSlot(name); + if (slot == -1) { + this.addOutput(name, type); + } + }; + + Subgraph.prototype.onSubgraphRenamedOutput = function(oldname, name) { + var slot = this.findOutputSlot(oldname); + if (slot == -1) { + return; + } + var info = this.getOutputInfo(slot); + info.name = name; + }; + + Subgraph.prototype.onSubgraphTypeChangeOutput = function(name, type) { + var slot = this.findOutputSlot(name); + if (slot == -1) { + return; + } + var info = this.getOutputInfo(slot); + info.type = type; + }; + + Subgraph.prototype.onSubgraphRemovedOutput = function(name) { + var slot = this.findInputSlot(name); + if (slot == -1) { + return; + } + this.removeOutput(slot); + }; + // ***************************************************** + + Subgraph.prototype.getExtraMenuOptions = function(graphcanvas) { + var that = this; + return [ + { + content: "Open", + callback: function() { + graphcanvas.openSubgraph(that.subgraph); + } + } + ]; + }; + + Subgraph.prototype.onResize = function(size) { + size[1] += 20; + }; + + Subgraph.prototype.serialize = function() { + var data = LiteGraph.LGraphNode.prototype.serialize.call(this); + data.subgraph = this.subgraph.serialize(); + return data; + }; + //no need to define node.configure, the default method detects node.subgraph and passes the object to node.subgraph.configure() + + Subgraph.prototype.clone = function() { + var node = LiteGraph.createNode(this.type); + var data = this.serialize(); + delete data["id"]; + delete data["inputs"]; + delete data["outputs"]; + node.configure(data); + return node; + }; + + Subgraph.prototype.buildFromNodes = function(nodes) + { + //clear all? + //TODO + + //nodes that connect data between parent graph and subgraph + var subgraph_inputs = []; + var subgraph_outputs = []; + + //mark inner nodes + var ids = {}; + var min_x = 0; + var max_x = 0; + for(var i = 0; i < nodes.length; ++i) + { + var node = nodes[i]; + ids[ node.id ] = node; + min_x = Math.min( node.pos[0], min_x ); + max_x = Math.max( node.pos[0], min_x ); + } + + var last_input_y = 0; + var last_output_y = 0; + + for(var i = 0; i < nodes.length; ++i) + { + var node = nodes[i]; + //check inputs + if( node.inputs ) + for(var j = 0; j < node.inputs.length; ++j) + { + var input = node.inputs[j]; + if( !input || !input.link ) + continue; + var link = node.graph.links[ input.link ]; + if(!link) + continue; + if( ids[ link.origin_id ] ) + continue; + //this.addInput(input.name,link.type); + this.subgraph.addInput(input.name,link.type); + /* + var input_node = LiteGraph.createNode("graph/input"); + this.subgraph.add( input_node ); + input_node.pos = [min_x - 200, last_input_y ]; + last_input_y += 100; + */ + } + + //check outputs + if( node.outputs ) + for(var j = 0; j < node.outputs.length; ++j) + { + var output = node.outputs[j]; + if( !output || !output.links || !output.links.length ) + continue; + var is_external = false; + for(var k = 0; k < output.links.length; ++k) + { + var link = node.graph.links[ output.links[k] ]; + if(!link) + continue; + if( ids[ link.target_id ] ) + continue; + is_external = true; + break; + } + if(!is_external) + continue; + //this.addOutput(output.name,output.type); + /* + var output_node = LiteGraph.createNode("graph/output"); + this.subgraph.add( output_node ); + output_node.pos = [max_x + 50, last_output_y ]; + last_output_y += 100; + */ + } + } + + //detect inputs and outputs + //split every connection in two data_connection nodes + //keep track of internal connections + //connect external connections + + //clone nodes inside subgraph and try to reconnect them + + //connect edge subgraph nodes to extarnal connections nodes + } + + LiteGraph.Subgraph = Subgraph; + LiteGraph.registerNodeType("graph/subgraph", Subgraph); + + //Input for a subgraph + function GraphInput() { + this.addOutput("", "number"); + + this.name_in_graph = ""; + this.properties = { + name: "", + type: "number", + value: 0 + }; + + var that = this; + + this.name_widget = this.addWidget( + "text", + "Name", + this.properties.name, + function(v) { + if (!v) { + return; + } + that.setProperty("name",v); + } + ); + this.type_widget = this.addWidget( + "text", + "Type", + this.properties.type, + function(v) { + that.setProperty("type",v); + } + ); + + this.value_widget = this.addWidget( + "number", + "Value", + this.properties.value, + function(v) { + that.setProperty("value",v); + } + ); + + this.widgets_up = true; + this.size = [180, 90]; + } + + GraphInput.title = "Input"; + GraphInput.desc = "Input of the graph"; + + GraphInput.prototype.onConfigure = function() + { + this.updateType(); + } + + GraphInput.prototype.updateType = function() + { + var type = this.properties.type; + this.type_widget.value = type; + if(this.outputs[0].type != type) + { + this.outputs[0].type = type; + this.disconnectOutput(0); + } + if(type == "number") + { + this.value_widget.type = "number"; + this.value_widget.value = 0; + } + else if(type == "boolean") + { + this.value_widget.type = "toggle"; + this.value_widget.value = true; + } + else if(type == "string") + { + this.value_widget.type = "text"; + this.value_widget.value = ""; + } + else + { + this.value_widget.type = null; + this.value_widget.value = null; + } + this.properties.value = this.value_widget.value; + } + + GraphInput.prototype.onPropertyChanged = function(name,v) + { + if( name == "name" ) + { + if (v == "" || v == this.name_in_graph || v == "enabled") { + return false; + } + if(this.graph) + { + if (this.name_in_graph) { + //already added + this.graph.renameInput( this.name_in_graph, v ); + } else { + this.graph.addInput( v, this.properties.type ); + } + } //what if not?! + this.name_widget.value = v; + this.name_in_graph = v; + } + else if( name == "type" ) + { + v = v || ""; + this.updateType(v); + } + else if( name == "value" ) + { + } + } + + GraphInput.prototype.getTitle = function() { + if (this.flags.collapsed) { + return this.properties.name; + } + return this.title; + }; + + GraphInput.prototype.onAction = function(action, param) { + if (this.properties.type == LiteGraph.EVENT) { + this.triggerSlot(0, param); + } + }; + + GraphInput.prototype.onExecute = function() { + var name = this.properties.name; + //read from global input + var data = this.graph.inputs[name]; + if (!data) { + this.setOutputData(0, this.properties.value ); + return; + } + + this.setOutputData(0, data.value !== undefined ? data.value : this.properties.value ); + }; + + GraphInput.prototype.onRemoved = function() { + if (this.name_in_graph) { + this.graph.removeInput(this.name_in_graph); + } + }; + + LiteGraph.GraphInput = GraphInput; + LiteGraph.registerNodeType("graph/input", GraphInput); + + //Output for a subgraph + function GraphOutput() { + this.addInput("", ""); + + this.name_in_graph = ""; + this.properties = {}; + var that = this; + + Object.defineProperty(this.properties, "name", { + get: function() { + return that.name_in_graph; + }, + set: function(v) { + if (v == "" || v == that.name_in_graph) { + return; + } + if (that.name_in_graph) { + //already added + that.graph.renameOutput(that.name_in_graph, v); + } else { + that.graph.addOutput(v, that.properties.type); + } + that.name_widget.value = v; + that.name_in_graph = v; + }, + enumerable: true + }); + + Object.defineProperty(this.properties, "type", { + get: function() { + return that.inputs[0].type; + }, + set: function(v) { + if (v == "action" || v == "event") { + v = LiteGraph.ACTION; + } + that.inputs[0].type = v; + if (that.name_in_graph) { + //already added + that.graph.changeOutputType( + that.name_in_graph, + that.inputs[0].type + ); + } + that.type_widget.value = v || ""; + }, + enumerable: true + }); + + this.name_widget = this.addWidget("text","Name",this.properties.name,"name"); + this.type_widget = this.addWidget("text","Type",this.properties.type,"type"); + this.widgets_up = true; + this.size = [180, 60]; + } + + GraphOutput.title = "Output"; + GraphOutput.desc = "Output of the graph"; + + GraphOutput.prototype.onExecute = function() { + this._value = this.getInputData(0); + this.graph.setOutputData(this.properties.name, this._value); + }; + + GraphOutput.prototype.onAction = function(action, param) { + if (this.properties.type == LiteGraph.ACTION) { + this.graph.trigger(this.properties.name, param); + } + }; + + GraphOutput.prototype.onRemoved = function() { + if (this.name_in_graph) { + this.graph.removeOutput(this.name_in_graph); + } + }; + + GraphOutput.prototype.getTitle = function() { + if (this.flags.collapsed) { + return this.properties.name; + } + return this.title; + }; + + LiteGraph.GraphOutput = GraphOutput; + LiteGraph.registerNodeType("graph/output", GraphOutput); + + //Constant + function ConstantNumber() { + this.addOutput("value", "number"); + this.addProperty("value", 1.0); + this.widget = this.addWidget("number","value",1,"value"); + this.widgets_up = true; + this.size = [180, 30]; + } + + ConstantNumber.title = "Const Number"; + ConstantNumber.desc = "Constant number"; + + ConstantNumber.prototype.onExecute = function() { + this.setOutputData(0, parseFloat(this.properties["value"])); + }; + + ConstantNumber.prototype.getTitle = function() { + if (this.flags.collapsed) { + return this.properties.value; + } + return this.title; + }; + + ConstantNumber.prototype.setValue = function(v) + { + this.setProperty("value",v); + } + + ConstantNumber.prototype.onDrawBackground = function(ctx) { + //show the current value + this.outputs[0].label = this.properties["value"].toFixed(3); + }; + + LiteGraph.registerNodeType("basic/const", ConstantNumber); + + function ConstantBoolean() { + this.addOutput("", "boolean"); + this.addProperty("value", true); + this.widget = this.addWidget("toggle","value",true,"value"); + this.widgets_up = true; + this.size = [140, 30]; + } + + ConstantBoolean.title = "Const Boolean"; + ConstantBoolean.desc = "Constant boolean"; + ConstantBoolean.prototype.getTitle = ConstantNumber.prototype.getTitle; + + ConstantBoolean.prototype.onExecute = function() { + this.setOutputData(0, this.properties["value"]); + }; + + ConstantBoolean.prototype.setValue = ConstantNumber.prototype.setValue; + + ConstantBoolean.prototype.onGetInputs = function() { + return [["toggle", LiteGraph.ACTION]]; + }; + + ConstantBoolean.prototype.onAction = function(action) + { + this.setValue( !this.properties.value ); + } + + LiteGraph.registerNodeType("basic/boolean", ConstantBoolean); + + function ConstantString() { + this.addOutput("", "string"); + this.addProperty("value", ""); + this.widget = this.addWidget("text","value","","value"); //link to property value + this.widgets_up = true; + this.size = [180, 30]; + } + + ConstantString.title = "Const String"; + ConstantString.desc = "Constant string"; + + ConstantString.prototype.getTitle = ConstantNumber.prototype.getTitle; + + ConstantString.prototype.onExecute = function() { + this.setOutputData(0, this.properties["value"]); + }; + + ConstantString.prototype.setValue = ConstantNumber.prototype.setValue; + + ConstantString.prototype.onDropFile = function(file) + { + var that = this; + var reader = new FileReader(); + reader.onload = function(e) + { + that.setProperty("value",e.target.result); + } + reader.readAsText(file); + } + + LiteGraph.registerNodeType("basic/string", ConstantString); + + function ConstantFile() { + this.addInput("url", ""); + this.addOutput("", ""); + this.addProperty("url", ""); + this.addProperty("type", "text"); + this.widget = this.addWidget("text","url","","url"); + this._data = null; + } + + ConstantFile.title = "Const File"; + ConstantFile.desc = "Fetches a file from an url"; + ConstantFile["@type"] = { type: "enum", values: ["text","arraybuffer","blob","json"] }; + + ConstantFile.prototype.onPropertyChanged = function(name, value) { + if (name == "url") + { + if( value == null || value == "") + this._data = null; + else + { + this.fetchFile(value); + } + } + } + + ConstantFile.prototype.onExecute = function() { + var url = this.getInputData(0) || this.properties.url; + if(url && (url != this._url || this._type != this.properties.type)) + this.fetchFile(url); + this.setOutputData(0, this._data ); + }; + + ConstantFile.prototype.setValue = ConstantNumber.prototype.setValue; + + ConstantFile.prototype.fetchFile = function(url) { + var that = this; + if(!url || url.constructor !== String) + { + that._data = null; + that.boxcolor = null; + return; + } + + this._url = url; + this._type = this.properties.type; + if (url.substr(0, 4) == "http" && LiteGraph.proxy) { + url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); + } + fetch(url) + .then(function(response) { + if(!response.ok) + throw new Error("File not found"); + + if(that.properties.type == "arraybuffer") + return response.arrayBuffer(); + else if(that.properties.type == "text") + return response.text(); + else if(that.properties.type == "json") + return response.json(); + else if(that.properties.type == "blob") + return response.blob(); + }) + .then(function(data) { + that._data = data; + that.boxcolor = "#AEA"; + }) + .catch(function(error) { + that._data = null; + that.boxcolor = "red"; + console.error("error fetching file:",url); + }); + }; + + ConstantFile.prototype.onDropFile = function(file) + { + var that = this; + this._url = file.name; + this._type = this.properties.type; + this.properties.url = file.name; + var reader = new FileReader(); + reader.onload = function(e) + { + that.boxcolor = "#AEA"; + var v = e.target.result; + if( that.properties.type == "json" ) + v = JSON.parse(v); + that._data = v; + } + if(that.properties.type == "arraybuffer") + reader.readAsArrayBuffer(file); + else if(that.properties.type == "text" || that.properties.type == "json") + reader.readAsText(file); + else if(that.properties.type == "blob") + return reader.readAsBinaryString(file); + } + + LiteGraph.registerNodeType("basic/file", ConstantFile); + + //to store json objects + function ConstantData() { + this.addOutput("", ""); + this.addProperty("value", ""); + this.widget = this.addWidget("text","json","","value"); + this.widgets_up = true; + this.size = [140, 30]; + this._value = null; + } + + ConstantData.title = "Const Data"; + ConstantData.desc = "Constant Data"; + + ConstantData.prototype.onPropertyChanged = function(name, value) { + this.widget.value = value; + if (value == null || value == "") { + return; + } + + try { + this._value = JSON.parse(value); + this.boxcolor = "#AEA"; + } catch (err) { + this.boxcolor = "red"; + } + }; + + ConstantData.prototype.onExecute = function() { + this.setOutputData(0, this._value); + }; + + ConstantData.prototype.setValue = ConstantNumber.prototype.setValue; + + LiteGraph.registerNodeType("basic/data", ConstantData); + + //to store json objects + function ConstantArray() { + this.addInput("", ""); + this.addOutput("", "array"); + this.addOutput("length", "number"); + this.addProperty("value", ""); + this.widget = this.addWidget("text","array","","value"); + this.widgets_up = true; + this.size = [140, 50]; + this._value = null; + } + + ConstantArray.title = "Const Array"; + ConstantArray.desc = "Constant Array"; + + ConstantArray.prototype.onPropertyChanged = function(name, value) { + this.widget.value = value; + if (value == null || value == "") { + return; + } + + try { + if(value[0] != "[") + this._value = JSON.parse("[" + value + "]"); + else + this._value = JSON.parse(value); + this.boxcolor = "#AEA"; + } catch (err) { + this.boxcolor = "red"; + } + }; + + ConstantArray.prototype.onExecute = function() { + var v = this.getInputData(0); + if(v && v.length) //clone + { + if(!this._value) + this._value = new Array(); + this._value.length = v.length; + for(var i = 0; i < v.length; ++i) + this._value[i] = v[i]; + } + this.setOutputData(0, this._value); + this.setOutputData(1, this._value ? ( this._value.length || 0) : 0 ); + }; + + ConstantArray.prototype.setValue = ConstantNumber.prototype.setValue; + + LiteGraph.registerNodeType("basic/array", ConstantArray); + + function ArrayElement() { + this.addInput("array", "array,table,string"); + this.addInput("index", "number"); + this.addOutput("value", ""); + this.addProperty("index",0); + } + + ArrayElement.title = "Array[i]"; + ArrayElement.desc = "Returns an element from an array"; + + ArrayElement.prototype.onExecute = function() { + var array = this.getInputData(0); + var index = this.getInputData(1); + if(index == null) + index = this.properties.index; + if(array == null || index == null ) + return; + this.setOutputData(0, array[Math.floor(Number(index))] ); + }; + + LiteGraph.registerNodeType("basic/array[]", ArrayElement); + + + + function TableElement() { + this.addInput("table", "table"); + this.addInput("row", "number"); + this.addInput("col", "number"); + this.addOutput("value", ""); + this.addProperty("row",0); + this.addProperty("column",0); + } + + TableElement.title = "Table[row][col]"; + TableElement.desc = "Returns an element from a table"; + + TableElement.prototype.onExecute = function() { + var table = this.getInputData(0); + var row = this.getInputData(1); + var col = this.getInputData(2); + if(row == null) + row = this.properties.row; + if(col == null) + col = this.properties.column; + if(table == null || row == null || col == null) + return; + var row = table[Math.floor(Number(row))]; + if(row) + this.setOutputData(0, row[Math.floor(Number(col))] ); + else + this.setOutputData(0, null ); + }; + + LiteGraph.registerNodeType("basic/table[][]", TableElement); + + function ObjectProperty() { + this.addInput("obj", ""); + this.addOutput("", ""); + this.addProperty("value", ""); + this.widget = this.addWidget("text","prop.","",this.setValue.bind(this) ); + this.widgets_up = true; + this.size = [140, 30]; + this._value = null; + } + + ObjectProperty.title = "Object property"; + ObjectProperty.desc = "Outputs the property of an object"; + + ObjectProperty.prototype.setValue = function(v) { + this.properties.value = v; + this.widget.value = v; + }; + + ObjectProperty.prototype.getTitle = function() { + if (this.flags.collapsed) { + return "in." + this.properties.value; + } + return this.title; + }; + + ObjectProperty.prototype.onPropertyChanged = function(name, value) { + this.widget.value = value; + }; + + ObjectProperty.prototype.onExecute = function() { + var data = this.getInputData(0); + if (data != null) { + this.setOutputData(0, data[this.properties.value]); + } + }; + + LiteGraph.registerNodeType("basic/object_property", ObjectProperty); + + function ObjectKeys() { + this.addInput("obj", ""); + this.addOutput("keys", "array"); + this.size = [140, 30]; + } + + ObjectKeys.title = "Object keys"; + ObjectKeys.desc = "Outputs an array with the keys of an object"; + + ObjectKeys.prototype.onExecute = function() { + var data = this.getInputData(0); + if (data != null) { + this.setOutputData(0, Object.keys(data) ); + } + }; + + LiteGraph.registerNodeType("basic/object_keys", ObjectKeys); + + function MergeObjects() { + this.addInput("A", "object"); + this.addInput("B", "object"); + this.addOutput("", "object"); + this._result = {}; + var that = this; + this.addWidget("button","clear","",function(){ + that._result = {}; + }); + this.size = this.computeSize(); + } + + MergeObjects.title = "Merge Objects"; + MergeObjects.desc = "Creates an object copying properties from others"; + + MergeObjects.prototype.onExecute = function() { + var A = this.getInputData(0); + var B = this.getInputData(1); + var C = this._result; + if(A) + for(var i in A) + C[i] = A[i]; + if(B) + for(var i in B) + C[i] = B[i]; + this.setOutputData(0,C); + }; + + LiteGraph.registerNodeType("basic/merge_objects", MergeObjects ); + + //Store as variable + function Variable() { + this.size = [60, 30]; + this.addInput("in"); + this.addOutput("out"); + this.properties = { varname: "myname", global: false }; + this.value = null; + } + + Variable.title = "Variable"; + Variable.desc = "store/read variable value"; + + Variable.prototype.onExecute = function() { + this.value = this.getInputData(0); + if(this.graph) + this.graph.vars[ this.properties.varname ] = this.value; + if(this.properties.global) + global[this.properties.varname] = this.value; + this.setOutputData(0, this.value ); + }; + + Variable.prototype.getTitle = function() { + return this.properties.varname; + }; + + LiteGraph.registerNodeType("basic/variable", Variable); + + function length(v) { + if(v && v.length != null) + return Number(v.length); + return 0; + } + + LiteGraph.wrapFunctionAsNode( + "basic/length", + length, + [""], + "number" + ); + + function DownloadData() { + this.size = [60, 30]; + this.addInput("data", 0 ); + this.addInput("download", LiteGraph.ACTION ); + this.properties = { filename: "data.json" }; + this.value = null; + var that = this; + this.addWidget("button","Download","", function(v){ + if(!that.value) + return; + that.downloadAsFile(); + }); + } + + DownloadData.title = "Download"; + DownloadData.desc = "Download some data"; + + DownloadData.prototype.downloadAsFile = function() + { + if(this.value == null) + return; + + var str = null; + if(this.value.constructor === String) + str = this.value; + else + str = JSON.stringify(this.value); + + var file = new Blob([str]); + var url = URL.createObjectURL( file ); + var element = document.createElement("a"); + element.setAttribute('href', url); + element.setAttribute('download', this.properties.filename ); + element.style.display = 'none'; + document.body.appendChild(element); + element.click(); + document.body.removeChild(element); + setTimeout( function(){ URL.revokeObjectURL( url ); }, 1000*60 ); //wait one minute to revoke url + } + + DownloadData.prototype.onAction = function(action, param) { + var that = this; + setTimeout( function(){ that.downloadAsFile(); }, 100); //deferred to avoid blocking the renderer with the popup + } + + DownloadData.prototype.onExecute = function() { + if (this.inputs[0]) { + this.value = this.getInputData(0); + } + }; + + DownloadData.prototype.getTitle = function() { + if (this.flags.collapsed) { + return this.properties.filename; + } + return this.title; + }; + + LiteGraph.registerNodeType("basic/download", DownloadData); + + + + //Watch a value in the editor + function Watch() { + this.size = [60, 30]; + this.addInput("value", 0, { label: "" }); + this.value = 0; + } + + Watch.title = "Watch"; + Watch.desc = "Show value of input"; + + Watch.prototype.onExecute = function() { + if (this.inputs[0]) { + this.value = this.getInputData(0); + } + }; + + Watch.prototype.getTitle = function() { + if (this.flags.collapsed) { + return this.inputs[0].label; + } + return this.title; + }; + + Watch.toString = function(o) { + if (o == null) { + return "null"; + } else if (o.constructor === Number) { + return o.toFixed(3); + } else if (o.constructor === Array) { + var str = "["; + for (var i = 0; i < o.length; ++i) { + str += Watch.toString(o[i]) + (i + 1 != o.length ? "," : ""); + } + str += "]"; + return str; + } else { + return String(o); + } + }; + + Watch.prototype.onDrawBackground = function(ctx) { + //show the current value + this.inputs[0].label = Watch.toString(this.value); + }; + + LiteGraph.registerNodeType("basic/watch", Watch); + + //in case one type doesnt match other type but you want to connect them anyway + function Cast() { + this.addInput("in", 0); + this.addOutput("out", 0); + this.size = [40, 30]; + } + + Cast.title = "Cast"; + Cast.desc = "Allows to connect different types"; + + Cast.prototype.onExecute = function() { + this.setOutputData(0, this.getInputData(0)); + }; + + LiteGraph.registerNodeType("basic/cast", Cast); + + //Show value inside the debug console + function Console() { + this.mode = LiteGraph.ON_EVENT; + this.size = [80, 30]; + this.addProperty("msg", ""); + this.addInput("log", LiteGraph.EVENT); + this.addInput("msg", 0); + } + + Console.title = "Console"; + Console.desc = "Show value inside the console"; + + Console.prototype.onAction = function(action, param) { + if (action == "log") { + console.log(param); + } else if (action == "warn") { + console.warn(param); + } else if (action == "error") { + console.error(param); + } + }; + + Console.prototype.onExecute = function() { + var msg = this.getInputData(1); + if (msg !== null) { + this.properties.msg = msg; + } + console.log(msg); + }; + + Console.prototype.onGetInputs = function() { + return [ + ["log", LiteGraph.ACTION], + ["warn", LiteGraph.ACTION], + ["error", LiteGraph.ACTION] + ]; + }; + + LiteGraph.registerNodeType("basic/console", Console); + + //Show value inside the debug console + function Alert() { + this.mode = LiteGraph.ON_EVENT; + this.addProperty("msg", ""); + this.addInput("", LiteGraph.EVENT); + var that = this; + this.widget = this.addWidget("text", "Text", "", function(v) { + that.properties.msg = v; + }); + this.widgets_up = true; + this.size = [200, 30]; + } + + Alert.title = "Alert"; + Alert.desc = "Show an alert window"; + Alert.color = "#510"; + + Alert.prototype.onConfigure = function(o) { + this.widget.value = o.properties.msg; + }; + + Alert.prototype.onAction = function(action, param) { + var msg = this.properties.msg; + setTimeout(function() { + alert(msg); + }, 10); + }; + + LiteGraph.registerNodeType("basic/alert", Alert); + + //Execites simple code + function NodeScript() { + this.size = [60, 30]; + this.addProperty("onExecute", "return A;"); + this.addInput("A", ""); + this.addInput("B", ""); + this.addOutput("out", ""); + + this._func = null; + this.data = {}; + } + + NodeScript.prototype.onConfigure = function(o) { + if (o.properties.onExecute && LiteGraph.allow_scripts) + this.compileCode(o.properties.onExecute); + else + console.warn("Script not compiled, LiteGraph.allow_scripts is false"); + }; + + NodeScript.title = "Script"; + NodeScript.desc = "executes a code (max 100 characters)"; + + NodeScript.widgets_info = { + onExecute: { type: "code" } + }; + + NodeScript.prototype.onPropertyChanged = function(name, value) { + if (name == "onExecute" && LiteGraph.allow_scripts) + this.compileCode(value); + else + console.warn("Script not compiled, LiteGraph.allow_scripts is false"); + }; + + NodeScript.prototype.compileCode = function(code) { + this._func = null; + if (code.length > 256) { + console.warn("Script too long, max 256 chars"); + } else { + var code_low = code.toLowerCase(); + var forbidden_words = [ + "script", + "body", + "document", + "eval", + "nodescript", + "function" + ]; //bad security solution + for (var i = 0; i < forbidden_words.length; ++i) { + if (code_low.indexOf(forbidden_words[i]) != -1) { + console.warn("invalid script"); + return; + } + } + try { + this._func = new Function("A", "B", "C", "DATA", "node", code); + } catch (err) { + console.error("Error parsing script"); + console.error(err); + } + } + }; + + NodeScript.prototype.onExecute = function() { + if (!this._func) { + return; + } + + try { + var A = this.getInputData(0); + var B = this.getInputData(1); + var C = this.getInputData(2); + this.setOutputData(0, this._func(A, B, C, this.data, this)); + } catch (err) { + console.error("Error in script"); + console.error(err); + } + }; + + NodeScript.prototype.onGetOutputs = function() { + return [["C", ""]]; + }; + + LiteGraph.registerNodeType("basic/script", NodeScript); +})(this); + //event related nodes (function(global) { var LiteGraph = global.LiteGraph; @@ -12711,805 +12709,805 @@ if (typeof exports != "undefined") { LiteGraph.registerNodeType("basic/data_store", DataStore); })(this); - -//widgets -(function(global) { - var LiteGraph = global.LiteGraph; - - /* Button ****************/ - - function WidgetButton() { - this.addOutput("", LiteGraph.EVENT); - this.addOutput("", "boolean"); - this.addProperty("text", "click me"); - this.addProperty("font_size", 30); - this.addProperty("message", ""); - this.size = [164, 84]; - this.clicked = false; - } - - WidgetButton.title = "Button"; - WidgetButton.desc = "Triggers an event"; - - WidgetButton.font = "Arial"; - WidgetButton.prototype.onDrawForeground = function(ctx) { - if (this.flags.collapsed) { - return; - } - var margin = 10; - ctx.fillStyle = "black"; - ctx.fillRect( - margin + 1, - margin + 1, - this.size[0] - margin * 2, - this.size[1] - margin * 2 - ); - ctx.fillStyle = "#AAF"; - ctx.fillRect( - margin - 1, - margin - 1, - this.size[0] - margin * 2, - this.size[1] - margin * 2 - ); - ctx.fillStyle = this.clicked - ? "white" - : this.mouseOver - ? "#668" - : "#334"; - ctx.fillRect( - margin, - margin, - this.size[0] - margin * 2, - this.size[1] - margin * 2 - ); - - if (this.properties.text || this.properties.text === 0) { - var font_size = this.properties.font_size || 30; - ctx.textAlign = "center"; - ctx.fillStyle = this.clicked ? "black" : "white"; - ctx.font = font_size + "px " + WidgetButton.font; - ctx.fillText( - this.properties.text, - this.size[0] * 0.5, - this.size[1] * 0.5 + font_size * 0.3 - ); - ctx.textAlign = "left"; - } - }; - - WidgetButton.prototype.onMouseDown = function(e, local_pos) { - if ( - local_pos[0] > 1 && - local_pos[1] > 1 && - local_pos[0] < this.size[0] - 2 && - local_pos[1] < this.size[1] - 2 - ) { - this.clicked = true; - this.triggerSlot(0, this.properties.message); - return true; - } - }; - - WidgetButton.prototype.onExecute = function() { - this.setOutputData(1, this.clicked); - }; - - WidgetButton.prototype.onMouseUp = function(e) { - this.clicked = false; - }; - - LiteGraph.registerNodeType("widget/button", WidgetButton); - - function WidgetToggle() { - this.addInput("", "boolean"); - this.addInput("e", LiteGraph.ACTION); - this.addOutput("v", "boolean"); - this.addOutput("e", LiteGraph.EVENT); - this.properties = { font: "", value: false }; - this.size = [160, 44]; - } - - WidgetToggle.title = "Toggle"; - WidgetToggle.desc = "Toggles between true or false"; - - WidgetToggle.prototype.onDrawForeground = function(ctx) { - if (this.flags.collapsed) { - return; - } - - var size = this.size[1] * 0.5; - var margin = 0.25; - var h = this.size[1] * 0.8; - ctx.font = this.properties.font || (size * 0.8).toFixed(0) + "px Arial"; - var w = ctx.measureText(this.title).width; - var x = (this.size[0] - (w + size)) * 0.5; - - ctx.fillStyle = "#AAA"; - ctx.fillRect(x, h - size, size, size); - - ctx.fillStyle = this.properties.value ? "#AEF" : "#000"; - ctx.fillRect( - x + size * margin, - h - size + size * margin, - size * (1 - margin * 2), - size * (1 - margin * 2) - ); - - ctx.textAlign = "left"; - ctx.fillStyle = "#AAA"; - ctx.fillText(this.title, size * 1.2 + x, h * 0.85); - ctx.textAlign = "left"; - }; - - WidgetToggle.prototype.onAction = function(action) { - this.properties.value = !this.properties.value; - this.trigger("e", this.properties.value); - }; - - WidgetToggle.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v != null) { - this.properties.value = v; - } - this.setOutputData(0, this.properties.value); - }; - - WidgetToggle.prototype.onMouseDown = function(e, local_pos) { - if ( - local_pos[0] > 1 && - local_pos[1] > 1 && - local_pos[0] < this.size[0] - 2 && - local_pos[1] < this.size[1] - 2 - ) { - this.properties.value = !this.properties.value; - this.graph._version++; - this.trigger("e", this.properties.value); - return true; - } - }; - - LiteGraph.registerNodeType("widget/toggle", WidgetToggle); - - /* Number ****************/ - - function WidgetNumber() { - this.addOutput("", "number"); - this.size = [80, 60]; - this.properties = { min: -1000, max: 1000, value: 1, step: 1 }; - this.old_y = -1; - this._remainder = 0; - this._precision = 0; - this.mouse_captured = false; - } - - WidgetNumber.title = "Number"; - WidgetNumber.desc = "Widget to select number value"; - - WidgetNumber.pixels_threshold = 10; - WidgetNumber.markers_color = "#666"; - - WidgetNumber.prototype.onDrawForeground = function(ctx) { - var x = this.size[0] * 0.5; - var h = this.size[1]; - if (h > 30) { - ctx.fillStyle = WidgetNumber.markers_color; - ctx.beginPath(); - ctx.moveTo(x, h * 0.1); - ctx.lineTo(x + h * 0.1, h * 0.2); - ctx.lineTo(x + h * -0.1, h * 0.2); - ctx.fill(); - ctx.beginPath(); - ctx.moveTo(x, h * 0.9); - ctx.lineTo(x + h * 0.1, h * 0.8); - ctx.lineTo(x + h * -0.1, h * 0.8); - ctx.fill(); - ctx.font = (h * 0.7).toFixed(1) + "px Arial"; - } else { - ctx.font = (h * 0.8).toFixed(1) + "px Arial"; - } - - ctx.textAlign = "center"; - ctx.font = (h * 0.7).toFixed(1) + "px Arial"; - ctx.fillStyle = "#EEE"; - ctx.fillText( - this.properties.value.toFixed(this._precision), - x, - h * 0.75 - ); - }; - - WidgetNumber.prototype.onExecute = function() { - this.setOutputData(0, this.properties.value); - }; - - WidgetNumber.prototype.onPropertyChanged = function(name, value) { - var t = (this.properties.step + "").split("."); - this._precision = t.length > 1 ? t[1].length : 0; - }; - - WidgetNumber.prototype.onMouseDown = function(e, pos) { - if (pos[1] < 0) { - return; - } - - this.old_y = e.canvasY; - this.captureInput(true); - this.mouse_captured = true; - - return true; - }; - - WidgetNumber.prototype.onMouseMove = function(e) { - if (!this.mouse_captured) { - return; - } - - var delta = this.old_y - e.canvasY; - if (e.shiftKey) { - delta *= 10; - } - if (e.metaKey || e.altKey) { - delta *= 0.1; - } - this.old_y = e.canvasY; - - var steps = this._remainder + delta / WidgetNumber.pixels_threshold; - this._remainder = steps % 1; - steps = steps | 0; - - var v = Math.clamp( - this.properties.value + steps * this.properties.step, - this.properties.min, - this.properties.max - ); - this.properties.value = v; - this.graph._version++; - this.setDirtyCanvas(true); - }; - - WidgetNumber.prototype.onMouseUp = function(e, pos) { - if (e.click_time < 200) { - var steps = pos[1] > this.size[1] * 0.5 ? -1 : 1; - this.properties.value = Math.clamp( - this.properties.value + steps * this.properties.step, - this.properties.min, - this.properties.max - ); - this.graph._version++; - this.setDirtyCanvas(true); - } - - if (this.mouse_captured) { - this.mouse_captured = false; - this.captureInput(false); - } - }; - - LiteGraph.registerNodeType("widget/number", WidgetNumber); - - - /* Combo ****************/ - - function WidgetCombo() { - this.addOutput("", "string"); - this.addOutput("change", LiteGraph.EVENT); - this.size = [80, 60]; - this.properties = { value: "A", values:"A;B;C" }; - this.old_y = -1; - this.mouse_captured = false; - this._values = this.properties.values.split(";"); - var that = this; - this.widgets_up = true; - this.widget = this.addWidget("combo","", this.properties.value, function(v){ - that.properties.value = v; - that.triggerSlot(1, v); - }, { property: "value", values: this._values } ); - } - - WidgetCombo.title = "Combo"; - WidgetCombo.desc = "Widget to select from a list"; - - WidgetCombo.prototype.onExecute = function() { - this.setOutputData( 0, this.properties.value ); - }; - - WidgetCombo.prototype.onPropertyChanged = function(name, value) { - if(name == "values") - { - this._values = value.split(";"); - this.widget.options.values = this._values; - } - else if(name == "value") - { - this.widget.value = value; - } - }; - - LiteGraph.registerNodeType("widget/combo", WidgetCombo); - - - /* Knob ****************/ - - function WidgetKnob() { - this.addOutput("", "number"); - this.size = [64, 84]; - this.properties = { - min: 0, - max: 1, - value: 0.5, - color: "#7AF", - precision: 2 - }; - this.value = -1; - } - - WidgetKnob.title = "Knob"; - WidgetKnob.desc = "Circular controller"; - WidgetKnob.size = [80, 100]; - - WidgetKnob.prototype.onDrawForeground = function(ctx) { - if (this.flags.collapsed) { - return; - } - - if (this.value == -1) { - this.value = - (this.properties.value - this.properties.min) / - (this.properties.max - this.properties.min); - } - - var center_x = this.size[0] * 0.5; - var center_y = this.size[1] * 0.5; - var radius = Math.min(this.size[0], this.size[1]) * 0.5 - 5; - var w = Math.floor(radius * 0.05); - - ctx.globalAlpha = 1; - ctx.save(); - ctx.translate(center_x, center_y); - ctx.rotate(Math.PI * 0.75); - - //bg - ctx.fillStyle = "rgba(0,0,0,0.5)"; - ctx.beginPath(); - ctx.moveTo(0, 0); - ctx.arc(0, 0, radius, 0, Math.PI * 1.5); - ctx.fill(); - - //value - ctx.strokeStyle = "black"; - ctx.fillStyle = this.properties.color; - ctx.lineWidth = 2; - ctx.beginPath(); - ctx.moveTo(0, 0); - ctx.arc( - 0, - 0, - radius - 4, - 0, - Math.PI * 1.5 * Math.max(0.01, this.value) - ); - ctx.closePath(); - ctx.fill(); - //ctx.stroke(); - ctx.lineWidth = 1; - ctx.globalAlpha = 1; - ctx.restore(); - - //inner - ctx.fillStyle = "black"; - ctx.beginPath(); - ctx.arc(center_x, center_y, radius * 0.75, 0, Math.PI * 2, true); - ctx.fill(); - - //miniball - ctx.fillStyle = this.mouseOver ? "white" : this.properties.color; - ctx.beginPath(); - var angle = this.value * Math.PI * 1.5 + Math.PI * 0.75; - ctx.arc( - center_x + Math.cos(angle) * radius * 0.65, - center_y + Math.sin(angle) * radius * 0.65, - radius * 0.05, - 0, - Math.PI * 2, - true - ); - ctx.fill(); - - //text - ctx.fillStyle = this.mouseOver ? "white" : "#AAA"; - ctx.font = Math.floor(radius * 0.5) + "px Arial"; - ctx.textAlign = "center"; - ctx.fillText( - this.properties.value.toFixed(this.properties.precision), - center_x, - center_y + radius * 0.15 - ); - }; - - WidgetKnob.prototype.onExecute = function() { - this.setOutputData(0, this.properties.value); - this.boxcolor = LiteGraph.colorToString([ - this.value, - this.value, - this.value - ]); - }; - - WidgetKnob.prototype.onMouseDown = function(e) { - this.center = [this.size[0] * 0.5, this.size[1] * 0.5 + 20]; - this.radius = this.size[0] * 0.5; - if ( - e.canvasY - this.pos[1] < 20 || - LiteGraph.distance( - [e.canvasX, e.canvasY], - [this.pos[0] + this.center[0], this.pos[1] + this.center[1]] - ) > this.radius - ) { - return false; - } - this.oldmouse = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; - this.captureInput(true); - return true; - }; - - WidgetKnob.prototype.onMouseMove = function(e) { - if (!this.oldmouse) { - return; - } - - var m = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; - - var v = this.value; - v -= (m[1] - this.oldmouse[1]) * 0.01; - if (v > 1.0) { - v = 1.0; - } else if (v < 0.0) { - v = 0.0; - } - this.value = v; - this.properties.value = - this.properties.min + - (this.properties.max - this.properties.min) * this.value; - this.oldmouse = m; - this.setDirtyCanvas(true); - }; - - WidgetKnob.prototype.onMouseUp = function(e) { - if (this.oldmouse) { - this.oldmouse = null; - this.captureInput(false); - } - }; - - WidgetKnob.prototype.onPropertyChanged = function(name, value) { - if (name == "min" || name == "max" || name == "value") { - this.properties[name] = parseFloat(value); - return true; //block - } - }; - - LiteGraph.registerNodeType("widget/knob", WidgetKnob); - - //Show value inside the debug console - function WidgetSliderGUI() { - this.addOutput("", "number"); - this.properties = { - value: 0.5, - min: 0, - max: 1, - text: "V" - }; - var that = this; - this.size = [140, 40]; - this.slider = this.addWidget( - "slider", - "V", - this.properties.value, - function(v) { - that.properties.value = v; - }, - this.properties - ); - this.widgets_up = true; - } - - WidgetSliderGUI.title = "Inner Slider"; - - WidgetSliderGUI.prototype.onPropertyChanged = function(name, value) { - if (name == "value") { - this.slider.value = value; - } - }; - - WidgetSliderGUI.prototype.onExecute = function() { - this.setOutputData(0, this.properties.value); - }; - - LiteGraph.registerNodeType("widget/internal_slider", WidgetSliderGUI); - - //Widget H SLIDER - function WidgetHSlider() { - this.size = [160, 26]; - this.addOutput("", "number"); - this.properties = { color: "#7AF", min: 0, max: 1, value: 0.5 }; - this.value = -1; - } - - WidgetHSlider.title = "H.Slider"; - WidgetHSlider.desc = "Linear slider controller"; - - WidgetHSlider.prototype.onDrawForeground = function(ctx) { - if (this.value == -1) { - this.value = - (this.properties.value - this.properties.min) / - (this.properties.max - this.properties.min); - } - - //border - ctx.globalAlpha = 1; - ctx.lineWidth = 1; - ctx.fillStyle = "#000"; - ctx.fillRect(2, 2, this.size[0] - 4, this.size[1] - 4); - - ctx.fillStyle = this.properties.color; - ctx.beginPath(); - ctx.rect(4, 4, (this.size[0] - 8) * this.value, this.size[1] - 8); - ctx.fill(); - }; - - WidgetHSlider.prototype.onExecute = function() { - this.properties.value = - this.properties.min + - (this.properties.max - this.properties.min) * this.value; - this.setOutputData(0, this.properties.value); - this.boxcolor = LiteGraph.colorToString([ - this.value, - this.value, - this.value - ]); - }; - - WidgetHSlider.prototype.onMouseDown = function(e) { - if (e.canvasY - this.pos[1] < 0) { - return false; - } - - this.oldmouse = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; - this.captureInput(true); - return true; - }; - - WidgetHSlider.prototype.onMouseMove = function(e) { - if (!this.oldmouse) { - return; - } - - var m = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; - - var v = this.value; - var delta = m[0] - this.oldmouse[0]; - v += delta / this.size[0]; - if (v > 1.0) { - v = 1.0; - } else if (v < 0.0) { - v = 0.0; - } - - this.value = v; - - this.oldmouse = m; - this.setDirtyCanvas(true); - }; - - WidgetHSlider.prototype.onMouseUp = function(e) { - this.oldmouse = null; - this.captureInput(false); - }; - - WidgetHSlider.prototype.onMouseLeave = function(e) { - //this.oldmouse = null; - }; - - LiteGraph.registerNodeType("widget/hslider", WidgetHSlider); - - function WidgetProgress() { - this.size = [160, 26]; - this.addInput("", "number"); - this.properties = { min: 0, max: 1, value: 0, color: "#AAF" }; - } - - WidgetProgress.title = "Progress"; - WidgetProgress.desc = "Shows data in linear progress"; - - WidgetProgress.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v != undefined) { - this.properties["value"] = v; - } - }; - - WidgetProgress.prototype.onDrawForeground = function(ctx) { - //border - ctx.lineWidth = 1; - ctx.fillStyle = this.properties.color; - var v = - (this.properties.value - this.properties.min) / - (this.properties.max - this.properties.min); - v = Math.min(1, v); - v = Math.max(0, v); - ctx.fillRect(2, 2, (this.size[0] - 4) * v, this.size[1] - 4); - }; - - LiteGraph.registerNodeType("widget/progress", WidgetProgress); - - function WidgetText() { - this.addInputs("", 0); - this.properties = { - value: "...", - font: "Arial", - fontsize: 18, - color: "#AAA", - align: "left", - glowSize: 0, - decimals: 1 - }; - } - - WidgetText.title = "Text"; - WidgetText.desc = "Shows the input value"; - WidgetText.widgets = [ - { name: "resize", text: "Resize box", type: "button" }, - { name: "led_text", text: "LED", type: "minibutton" }, - { name: "normal_text", text: "Normal", type: "minibutton" } - ]; - - WidgetText.prototype.onDrawForeground = function(ctx) { - //ctx.fillStyle="#000"; - //ctx.fillRect(0,0,100,60); - ctx.fillStyle = this.properties["color"]; - var v = this.properties["value"]; - - if (this.properties["glowSize"]) { - ctx.shadowColor = this.properties.color; - ctx.shadowOffsetX = 0; - ctx.shadowOffsetY = 0; - ctx.shadowBlur = this.properties["glowSize"]; - } else { - ctx.shadowColor = "transparent"; - } - - var fontsize = this.properties["fontsize"]; - - ctx.textAlign = this.properties["align"]; - ctx.font = fontsize.toString() + "px " + this.properties["font"]; - this.str = - typeof v == "number" ? v.toFixed(this.properties["decimals"]) : v; - - if (typeof this.str == "string") { - var lines = this.str.split("\\n"); - for (var i in lines) { - ctx.fillText( - lines[i], - this.properties["align"] == "left" ? 15 : this.size[0] - 15, - fontsize * -0.15 + fontsize * (parseInt(i) + 1) - ); - } - } - - ctx.shadowColor = "transparent"; - this.last_ctx = ctx; - ctx.textAlign = "left"; - }; - - WidgetText.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v != null) { - this.properties["value"] = v; - } - //this.setDirtyCanvas(true); - }; - - WidgetText.prototype.resize = function() { - if (!this.last_ctx) { - return; - } - - var lines = this.str.split("\\n"); - this.last_ctx.font = - this.properties["fontsize"] + "px " + this.properties["font"]; - var max = 0; - for (var i in lines) { - var w = this.last_ctx.measureText(lines[i]).width; - if (max < w) { - max = w; - } - } - this.size[0] = max + 20; - this.size[1] = 4 + lines.length * this.properties["fontsize"]; - - this.setDirtyCanvas(true); - }; - - WidgetText.prototype.onPropertyChanged = function(name, value) { - this.properties[name] = value; - this.str = typeof value == "number" ? value.toFixed(3) : value; - //this.resize(); - return true; - }; - - LiteGraph.registerNodeType("widget/text", WidgetText); - - function WidgetPanel() { - this.size = [200, 100]; - this.properties = { - borderColor: "#ffffff", - bgcolorTop: "#f0f0f0", - bgcolorBottom: "#e0e0e0", - shadowSize: 2, - borderRadius: 3 - }; - } - - WidgetPanel.title = "Panel"; - WidgetPanel.desc = "Non interactive panel"; - WidgetPanel.widgets = [{ name: "update", text: "Update", type: "button" }]; - - WidgetPanel.prototype.createGradient = function(ctx) { - if ( - this.properties["bgcolorTop"] == "" || - this.properties["bgcolorBottom"] == "" - ) { - this.lineargradient = 0; - return; - } - - this.lineargradient = ctx.createLinearGradient(0, 0, 0, this.size[1]); - this.lineargradient.addColorStop(0, this.properties["bgcolorTop"]); - this.lineargradient.addColorStop(1, this.properties["bgcolorBottom"]); - }; - - WidgetPanel.prototype.onDrawForeground = function(ctx) { - if (this.flags.collapsed) { - return; - } - - if (this.lineargradient == null) { - this.createGradient(ctx); - } - - if (!this.lineargradient) { - return; - } - - ctx.lineWidth = 1; - ctx.strokeStyle = this.properties["borderColor"]; - //ctx.fillStyle = "#ebebeb"; - ctx.fillStyle = this.lineargradient; - - if (this.properties["shadowSize"]) { - ctx.shadowColor = "#000"; - ctx.shadowOffsetX = 0; - ctx.shadowOffsetY = 0; - ctx.shadowBlur = this.properties["shadowSize"]; - } else { - ctx.shadowColor = "transparent"; - } - - ctx.roundRect( - 0, - 0, - this.size[0] - 1, - this.size[1] - 1, - this.properties["shadowSize"] - ); - ctx.fill(); - ctx.shadowColor = "transparent"; - ctx.stroke(); - }; - - LiteGraph.registerNodeType("widget/panel", WidgetPanel); -})(this); - + +//widgets +(function(global) { + var LiteGraph = global.LiteGraph; + + /* Button ****************/ + + function WidgetButton() { + this.addOutput("", LiteGraph.EVENT); + this.addOutput("", "boolean"); + this.addProperty("text", "click me"); + this.addProperty("font_size", 30); + this.addProperty("message", ""); + this.size = [164, 84]; + this.clicked = false; + } + + WidgetButton.title = "Button"; + WidgetButton.desc = "Triggers an event"; + + WidgetButton.font = "Arial"; + WidgetButton.prototype.onDrawForeground = function(ctx) { + if (this.flags.collapsed) { + return; + } + var margin = 10; + ctx.fillStyle = "black"; + ctx.fillRect( + margin + 1, + margin + 1, + this.size[0] - margin * 2, + this.size[1] - margin * 2 + ); + ctx.fillStyle = "#AAF"; + ctx.fillRect( + margin - 1, + margin - 1, + this.size[0] - margin * 2, + this.size[1] - margin * 2 + ); + ctx.fillStyle = this.clicked + ? "white" + : this.mouseOver + ? "#668" + : "#334"; + ctx.fillRect( + margin, + margin, + this.size[0] - margin * 2, + this.size[1] - margin * 2 + ); + + if (this.properties.text || this.properties.text === 0) { + var font_size = this.properties.font_size || 30; + ctx.textAlign = "center"; + ctx.fillStyle = this.clicked ? "black" : "white"; + ctx.font = font_size + "px " + WidgetButton.font; + ctx.fillText( + this.properties.text, + this.size[0] * 0.5, + this.size[1] * 0.5 + font_size * 0.3 + ); + ctx.textAlign = "left"; + } + }; + + WidgetButton.prototype.onMouseDown = function(e, local_pos) { + if ( + local_pos[0] > 1 && + local_pos[1] > 1 && + local_pos[0] < this.size[0] - 2 && + local_pos[1] < this.size[1] - 2 + ) { + this.clicked = true; + this.triggerSlot(0, this.properties.message); + return true; + } + }; + + WidgetButton.prototype.onExecute = function() { + this.setOutputData(1, this.clicked); + }; + + WidgetButton.prototype.onMouseUp = function(e) { + this.clicked = false; + }; + + LiteGraph.registerNodeType("widget/button", WidgetButton); + + function WidgetToggle() { + this.addInput("", "boolean"); + this.addInput("e", LiteGraph.ACTION); + this.addOutput("v", "boolean"); + this.addOutput("e", LiteGraph.EVENT); + this.properties = { font: "", value: false }; + this.size = [160, 44]; + } + + WidgetToggle.title = "Toggle"; + WidgetToggle.desc = "Toggles between true or false"; + + WidgetToggle.prototype.onDrawForeground = function(ctx) { + if (this.flags.collapsed) { + return; + } + + var size = this.size[1] * 0.5; + var margin = 0.25; + var h = this.size[1] * 0.8; + ctx.font = this.properties.font || (size * 0.8).toFixed(0) + "px Arial"; + var w = ctx.measureText(this.title).width; + var x = (this.size[0] - (w + size)) * 0.5; + + ctx.fillStyle = "#AAA"; + ctx.fillRect(x, h - size, size, size); + + ctx.fillStyle = this.properties.value ? "#AEF" : "#000"; + ctx.fillRect( + x + size * margin, + h - size + size * margin, + size * (1 - margin * 2), + size * (1 - margin * 2) + ); + + ctx.textAlign = "left"; + ctx.fillStyle = "#AAA"; + ctx.fillText(this.title, size * 1.2 + x, h * 0.85); + ctx.textAlign = "left"; + }; + + WidgetToggle.prototype.onAction = function(action) { + this.properties.value = !this.properties.value; + this.trigger("e", this.properties.value); + }; + + WidgetToggle.prototype.onExecute = function() { + var v = this.getInputData(0); + if (v != null) { + this.properties.value = v; + } + this.setOutputData(0, this.properties.value); + }; + + WidgetToggle.prototype.onMouseDown = function(e, local_pos) { + if ( + local_pos[0] > 1 && + local_pos[1] > 1 && + local_pos[0] < this.size[0] - 2 && + local_pos[1] < this.size[1] - 2 + ) { + this.properties.value = !this.properties.value; + this.graph._version++; + this.trigger("e", this.properties.value); + return true; + } + }; + + LiteGraph.registerNodeType("widget/toggle", WidgetToggle); + + /* Number ****************/ + + function WidgetNumber() { + this.addOutput("", "number"); + this.size = [80, 60]; + this.properties = { min: -1000, max: 1000, value: 1, step: 1 }; + this.old_y = -1; + this._remainder = 0; + this._precision = 0; + this.mouse_captured = false; + } + + WidgetNumber.title = "Number"; + WidgetNumber.desc = "Widget to select number value"; + + WidgetNumber.pixels_threshold = 10; + WidgetNumber.markers_color = "#666"; + + WidgetNumber.prototype.onDrawForeground = function(ctx) { + var x = this.size[0] * 0.5; + var h = this.size[1]; + if (h > 30) { + ctx.fillStyle = WidgetNumber.markers_color; + ctx.beginPath(); + ctx.moveTo(x, h * 0.1); + ctx.lineTo(x + h * 0.1, h * 0.2); + ctx.lineTo(x + h * -0.1, h * 0.2); + ctx.fill(); + ctx.beginPath(); + ctx.moveTo(x, h * 0.9); + ctx.lineTo(x + h * 0.1, h * 0.8); + ctx.lineTo(x + h * -0.1, h * 0.8); + ctx.fill(); + ctx.font = (h * 0.7).toFixed(1) + "px Arial"; + } else { + ctx.font = (h * 0.8).toFixed(1) + "px Arial"; + } + + ctx.textAlign = "center"; + ctx.font = (h * 0.7).toFixed(1) + "px Arial"; + ctx.fillStyle = "#EEE"; + ctx.fillText( + this.properties.value.toFixed(this._precision), + x, + h * 0.75 + ); + }; + + WidgetNumber.prototype.onExecute = function() { + this.setOutputData(0, this.properties.value); + }; + + WidgetNumber.prototype.onPropertyChanged = function(name, value) { + var t = (this.properties.step + "").split("."); + this._precision = t.length > 1 ? t[1].length : 0; + }; + + WidgetNumber.prototype.onMouseDown = function(e, pos) { + if (pos[1] < 0) { + return; + } + + this.old_y = e.canvasY; + this.captureInput(true); + this.mouse_captured = true; + + return true; + }; + + WidgetNumber.prototype.onMouseMove = function(e) { + if (!this.mouse_captured) { + return; + } + + var delta = this.old_y - e.canvasY; + if (e.shiftKey) { + delta *= 10; + } + if (e.metaKey || e.altKey) { + delta *= 0.1; + } + this.old_y = e.canvasY; + + var steps = this._remainder + delta / WidgetNumber.pixels_threshold; + this._remainder = steps % 1; + steps = steps | 0; + + var v = Math.clamp( + this.properties.value + steps * this.properties.step, + this.properties.min, + this.properties.max + ); + this.properties.value = v; + this.graph._version++; + this.setDirtyCanvas(true); + }; + + WidgetNumber.prototype.onMouseUp = function(e, pos) { + if (e.click_time < 200) { + var steps = pos[1] > this.size[1] * 0.5 ? -1 : 1; + this.properties.value = Math.clamp( + this.properties.value + steps * this.properties.step, + this.properties.min, + this.properties.max + ); + this.graph._version++; + this.setDirtyCanvas(true); + } + + if (this.mouse_captured) { + this.mouse_captured = false; + this.captureInput(false); + } + }; + + LiteGraph.registerNodeType("widget/number", WidgetNumber); + + + /* Combo ****************/ + + function WidgetCombo() { + this.addOutput("", "string"); + this.addOutput("change", LiteGraph.EVENT); + this.size = [80, 60]; + this.properties = { value: "A", values:"A;B;C" }; + this.old_y = -1; + this.mouse_captured = false; + this._values = this.properties.values.split(";"); + var that = this; + this.widgets_up = true; + this.widget = this.addWidget("combo","", this.properties.value, function(v){ + that.properties.value = v; + that.triggerSlot(1, v); + }, { property: "value", values: this._values } ); + } + + WidgetCombo.title = "Combo"; + WidgetCombo.desc = "Widget to select from a list"; + + WidgetCombo.prototype.onExecute = function() { + this.setOutputData( 0, this.properties.value ); + }; + + WidgetCombo.prototype.onPropertyChanged = function(name, value) { + if(name == "values") + { + this._values = value.split(";"); + this.widget.options.values = this._values; + } + else if(name == "value") + { + this.widget.value = value; + } + }; + + LiteGraph.registerNodeType("widget/combo", WidgetCombo); + + + /* Knob ****************/ + + function WidgetKnob() { + this.addOutput("", "number"); + this.size = [64, 84]; + this.properties = { + min: 0, + max: 1, + value: 0.5, + color: "#7AF", + precision: 2 + }; + this.value = -1; + } + + WidgetKnob.title = "Knob"; + WidgetKnob.desc = "Circular controller"; + WidgetKnob.size = [80, 100]; + + WidgetKnob.prototype.onDrawForeground = function(ctx) { + if (this.flags.collapsed) { + return; + } + + if (this.value == -1) { + this.value = + (this.properties.value - this.properties.min) / + (this.properties.max - this.properties.min); + } + + var center_x = this.size[0] * 0.5; + var center_y = this.size[1] * 0.5; + var radius = Math.min(this.size[0], this.size[1]) * 0.5 - 5; + var w = Math.floor(radius * 0.05); + + ctx.globalAlpha = 1; + ctx.save(); + ctx.translate(center_x, center_y); + ctx.rotate(Math.PI * 0.75); + + //bg + ctx.fillStyle = "rgba(0,0,0,0.5)"; + ctx.beginPath(); + ctx.moveTo(0, 0); + ctx.arc(0, 0, radius, 0, Math.PI * 1.5); + ctx.fill(); + + //value + ctx.strokeStyle = "black"; + ctx.fillStyle = this.properties.color; + ctx.lineWidth = 2; + ctx.beginPath(); + ctx.moveTo(0, 0); + ctx.arc( + 0, + 0, + radius - 4, + 0, + Math.PI * 1.5 * Math.max(0.01, this.value) + ); + ctx.closePath(); + ctx.fill(); + //ctx.stroke(); + ctx.lineWidth = 1; + ctx.globalAlpha = 1; + ctx.restore(); + + //inner + ctx.fillStyle = "black"; + ctx.beginPath(); + ctx.arc(center_x, center_y, radius * 0.75, 0, Math.PI * 2, true); + ctx.fill(); + + //miniball + ctx.fillStyle = this.mouseOver ? "white" : this.properties.color; + ctx.beginPath(); + var angle = this.value * Math.PI * 1.5 + Math.PI * 0.75; + ctx.arc( + center_x + Math.cos(angle) * radius * 0.65, + center_y + Math.sin(angle) * radius * 0.65, + radius * 0.05, + 0, + Math.PI * 2, + true + ); + ctx.fill(); + + //text + ctx.fillStyle = this.mouseOver ? "white" : "#AAA"; + ctx.font = Math.floor(radius * 0.5) + "px Arial"; + ctx.textAlign = "center"; + ctx.fillText( + this.properties.value.toFixed(this.properties.precision), + center_x, + center_y + radius * 0.15 + ); + }; + + WidgetKnob.prototype.onExecute = function() { + this.setOutputData(0, this.properties.value); + this.boxcolor = LiteGraph.colorToString([ + this.value, + this.value, + this.value + ]); + }; + + WidgetKnob.prototype.onMouseDown = function(e) { + this.center = [this.size[0] * 0.5, this.size[1] * 0.5 + 20]; + this.radius = this.size[0] * 0.5; + if ( + e.canvasY - this.pos[1] < 20 || + LiteGraph.distance( + [e.canvasX, e.canvasY], + [this.pos[0] + this.center[0], this.pos[1] + this.center[1]] + ) > this.radius + ) { + return false; + } + this.oldmouse = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; + this.captureInput(true); + return true; + }; + + WidgetKnob.prototype.onMouseMove = function(e) { + if (!this.oldmouse) { + return; + } + + var m = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; + + var v = this.value; + v -= (m[1] - this.oldmouse[1]) * 0.01; + if (v > 1.0) { + v = 1.0; + } else if (v < 0.0) { + v = 0.0; + } + this.value = v; + this.properties.value = + this.properties.min + + (this.properties.max - this.properties.min) * this.value; + this.oldmouse = m; + this.setDirtyCanvas(true); + }; + + WidgetKnob.prototype.onMouseUp = function(e) { + if (this.oldmouse) { + this.oldmouse = null; + this.captureInput(false); + } + }; + + WidgetKnob.prototype.onPropertyChanged = function(name, value) { + if (name == "min" || name == "max" || name == "value") { + this.properties[name] = parseFloat(value); + return true; //block + } + }; + + LiteGraph.registerNodeType("widget/knob", WidgetKnob); + + //Show value inside the debug console + function WidgetSliderGUI() { + this.addOutput("", "number"); + this.properties = { + value: 0.5, + min: 0, + max: 1, + text: "V" + }; + var that = this; + this.size = [140, 40]; + this.slider = this.addWidget( + "slider", + "V", + this.properties.value, + function(v) { + that.properties.value = v; + }, + this.properties + ); + this.widgets_up = true; + } + + WidgetSliderGUI.title = "Inner Slider"; + + WidgetSliderGUI.prototype.onPropertyChanged = function(name, value) { + if (name == "value") { + this.slider.value = value; + } + }; + + WidgetSliderGUI.prototype.onExecute = function() { + this.setOutputData(0, this.properties.value); + }; + + LiteGraph.registerNodeType("widget/internal_slider", WidgetSliderGUI); + + //Widget H SLIDER + function WidgetHSlider() { + this.size = [160, 26]; + this.addOutput("", "number"); + this.properties = { color: "#7AF", min: 0, max: 1, value: 0.5 }; + this.value = -1; + } + + WidgetHSlider.title = "H.Slider"; + WidgetHSlider.desc = "Linear slider controller"; + + WidgetHSlider.prototype.onDrawForeground = function(ctx) { + if (this.value == -1) { + this.value = + (this.properties.value - this.properties.min) / + (this.properties.max - this.properties.min); + } + + //border + ctx.globalAlpha = 1; + ctx.lineWidth = 1; + ctx.fillStyle = "#000"; + ctx.fillRect(2, 2, this.size[0] - 4, this.size[1] - 4); + + ctx.fillStyle = this.properties.color; + ctx.beginPath(); + ctx.rect(4, 4, (this.size[0] - 8) * this.value, this.size[1] - 8); + ctx.fill(); + }; + + WidgetHSlider.prototype.onExecute = function() { + this.properties.value = + this.properties.min + + (this.properties.max - this.properties.min) * this.value; + this.setOutputData(0, this.properties.value); + this.boxcolor = LiteGraph.colorToString([ + this.value, + this.value, + this.value + ]); + }; + + WidgetHSlider.prototype.onMouseDown = function(e) { + if (e.canvasY - this.pos[1] < 0) { + return false; + } + + this.oldmouse = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; + this.captureInput(true); + return true; + }; + + WidgetHSlider.prototype.onMouseMove = function(e) { + if (!this.oldmouse) { + return; + } + + var m = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; + + var v = this.value; + var delta = m[0] - this.oldmouse[0]; + v += delta / this.size[0]; + if (v > 1.0) { + v = 1.0; + } else if (v < 0.0) { + v = 0.0; + } + + this.value = v; + + this.oldmouse = m; + this.setDirtyCanvas(true); + }; + + WidgetHSlider.prototype.onMouseUp = function(e) { + this.oldmouse = null; + this.captureInput(false); + }; + + WidgetHSlider.prototype.onMouseLeave = function(e) { + //this.oldmouse = null; + }; + + LiteGraph.registerNodeType("widget/hslider", WidgetHSlider); + + function WidgetProgress() { + this.size = [160, 26]; + this.addInput("", "number"); + this.properties = { min: 0, max: 1, value: 0, color: "#AAF" }; + } + + WidgetProgress.title = "Progress"; + WidgetProgress.desc = "Shows data in linear progress"; + + WidgetProgress.prototype.onExecute = function() { + var v = this.getInputData(0); + if (v != undefined) { + this.properties["value"] = v; + } + }; + + WidgetProgress.prototype.onDrawForeground = function(ctx) { + //border + ctx.lineWidth = 1; + ctx.fillStyle = this.properties.color; + var v = + (this.properties.value - this.properties.min) / + (this.properties.max - this.properties.min); + v = Math.min(1, v); + v = Math.max(0, v); + ctx.fillRect(2, 2, (this.size[0] - 4) * v, this.size[1] - 4); + }; + + LiteGraph.registerNodeType("widget/progress", WidgetProgress); + + function WidgetText() { + this.addInputs("", 0); + this.properties = { + value: "...", + font: "Arial", + fontsize: 18, + color: "#AAA", + align: "left", + glowSize: 0, + decimals: 1 + }; + } + + WidgetText.title = "Text"; + WidgetText.desc = "Shows the input value"; + WidgetText.widgets = [ + { name: "resize", text: "Resize box", type: "button" }, + { name: "led_text", text: "LED", type: "minibutton" }, + { name: "normal_text", text: "Normal", type: "minibutton" } + ]; + + WidgetText.prototype.onDrawForeground = function(ctx) { + //ctx.fillStyle="#000"; + //ctx.fillRect(0,0,100,60); + ctx.fillStyle = this.properties["color"]; + var v = this.properties["value"]; + + if (this.properties["glowSize"]) { + ctx.shadowColor = this.properties.color; + ctx.shadowOffsetX = 0; + ctx.shadowOffsetY = 0; + ctx.shadowBlur = this.properties["glowSize"]; + } else { + ctx.shadowColor = "transparent"; + } + + var fontsize = this.properties["fontsize"]; + + ctx.textAlign = this.properties["align"]; + ctx.font = fontsize.toString() + "px " + this.properties["font"]; + this.str = + typeof v == "number" ? v.toFixed(this.properties["decimals"]) : v; + + if (typeof this.str == "string") { + var lines = this.str.split("\\n"); + for (var i in lines) { + ctx.fillText( + lines[i], + this.properties["align"] == "left" ? 15 : this.size[0] - 15, + fontsize * -0.15 + fontsize * (parseInt(i) + 1) + ); + } + } + + ctx.shadowColor = "transparent"; + this.last_ctx = ctx; + ctx.textAlign = "left"; + }; + + WidgetText.prototype.onExecute = function() { + var v = this.getInputData(0); + if (v != null) { + this.properties["value"] = v; + } + //this.setDirtyCanvas(true); + }; + + WidgetText.prototype.resize = function() { + if (!this.last_ctx) { + return; + } + + var lines = this.str.split("\\n"); + this.last_ctx.font = + this.properties["fontsize"] + "px " + this.properties["font"]; + var max = 0; + for (var i in lines) { + var w = this.last_ctx.measureText(lines[i]).width; + if (max < w) { + max = w; + } + } + this.size[0] = max + 20; + this.size[1] = 4 + lines.length * this.properties["fontsize"]; + + this.setDirtyCanvas(true); + }; + + WidgetText.prototype.onPropertyChanged = function(name, value) { + this.properties[name] = value; + this.str = typeof value == "number" ? value.toFixed(3) : value; + //this.resize(); + return true; + }; + + LiteGraph.registerNodeType("widget/text", WidgetText); + + function WidgetPanel() { + this.size = [200, 100]; + this.properties = { + borderColor: "#ffffff", + bgcolorTop: "#f0f0f0", + bgcolorBottom: "#e0e0e0", + shadowSize: 2, + borderRadius: 3 + }; + } + + WidgetPanel.title = "Panel"; + WidgetPanel.desc = "Non interactive panel"; + WidgetPanel.widgets = [{ name: "update", text: "Update", type: "button" }]; + + WidgetPanel.prototype.createGradient = function(ctx) { + if ( + this.properties["bgcolorTop"] == "" || + this.properties["bgcolorBottom"] == "" + ) { + this.lineargradient = 0; + return; + } + + this.lineargradient = ctx.createLinearGradient(0, 0, 0, this.size[1]); + this.lineargradient.addColorStop(0, this.properties["bgcolorTop"]); + this.lineargradient.addColorStop(1, this.properties["bgcolorBottom"]); + }; + + WidgetPanel.prototype.onDrawForeground = function(ctx) { + if (this.flags.collapsed) { + return; + } + + if (this.lineargradient == null) { + this.createGradient(ctx); + } + + if (!this.lineargradient) { + return; + } + + ctx.lineWidth = 1; + ctx.strokeStyle = this.properties["borderColor"]; + //ctx.fillStyle = "#ebebeb"; + ctx.fillStyle = this.lineargradient; + + if (this.properties["shadowSize"]) { + ctx.shadowColor = "#000"; + ctx.shadowOffsetX = 0; + ctx.shadowOffsetY = 0; + ctx.shadowBlur = this.properties["shadowSize"]; + } else { + ctx.shadowColor = "transparent"; + } + + ctx.roundRect( + 0, + 0, + this.size[0] - 1, + this.size[1] - 1, + this.properties["shadowSize"] + ); + ctx.fill(); + ctx.shadowColor = "transparent"; + ctx.stroke(); + }; + + LiteGraph.registerNodeType("widget/panel", WidgetPanel); +})(this); + (function(global) { var LiteGraph = global.LiteGraph; @@ -13863,7 +13861,7 @@ if (typeof exports != "undefined") { LiteGraph.registerNodeType("input/gamepad", GamepadInput); })(this); - + (function(global) { var LiteGraph = global.LiteGraph; @@ -15160,8951 +15158,5432 @@ if (typeof exports != "undefined") { LiteGraph.registerNodeType("math3d/xyzw-to-vec4", Math3DXYZWToVec4); })(this); - + (function(global) { var LiteGraph = global.LiteGraph; + function Selector() { + this.addInput("sel", "number"); + this.addInput("A"); + this.addInput("B"); + this.addInput("C"); + this.addInput("D"); + this.addOutput("out"); - function Math3DMat4() - { - this.addInput("T", "vec3"); - this.addInput("R", "vec3"); - this.addInput("S", "vec3"); - this.addOutput("mat4", "mat4"); - this.properties = { - "T":[0,0,0], - "R":[0,0,0], - "S":[1,1,1], - R_in_degrees: true - }; - this._result = mat4.create(); - this._must_update = true; - } - - Math3DMat4.title = "mat4"; - Math3DMat4.temp_quat = new Float32Array([0,0,0,1]); - Math3DMat4.temp_mat4 = new Float32Array(16); - Math3DMat4.temp_vec3 = new Float32Array(3); - - Math3DMat4.prototype.onPropertyChanged = function(name, value) - { - this._must_update = true; - } - - Math3DMat4.prototype.onExecute = function() - { - var M = this._result; - var Q = Math3DMat4.temp_quat; - var temp_mat4 = Math3DMat4.temp_mat4; - var temp_vec3 = Math3DMat4.temp_vec3; - - var T = this.getInputData(0); - var R = this.getInputData(1); - var S = this.getInputData(2); - - if( this._must_update || T || R || S ) - { - T = T || this.properties.T; - R = R || this.properties.R; - S = S || this.properties.S; - mat4.identity( M ); - mat4.translate( M, M, T ); - if(this.properties.R_in_degrees) - { - temp_vec3.set( R ); - vec3.scale(temp_vec3,temp_vec3,DEG2RAD); - quat.fromEuler( Q, temp_vec3 ); - } - else - quat.fromEuler( Q, R ); - mat4.fromQuat( temp_mat4, Q ); - mat4.multiply( M, M, temp_mat4 ); - mat4.scale( M, M, S ); - } - - this.setOutputData(0, M); - } - - LiteGraph.registerNodeType("math3d/mat4", Math3DMat4); - - //Math 3D operation - function Math3DOperation() { - this.addInput("A", "number,vec3"); - this.addInput("B", "number,vec3"); - this.addOutput("=", "number,vec3"); - this.addProperty("OP", "+", "enum", { values: Math3DOperation.values }); - this._result = vec3.create(); + this.selected = 0; } - Math3DOperation.values = ["+", "-", "*", "/", "%", "^", "max", "min","dot","cross"]; + Selector.title = "Selector"; + Selector.desc = "selects an output"; - LiteGraph.registerSearchboxExtra("math3d/operation", "CROSS()", { - properties: {"OP":"cross"}, - title: "CROSS()" - }); - - LiteGraph.registerSearchboxExtra("math3d/operation", "DOT()", { - properties: {"OP":"dot"}, - title: "DOT()" - }); - - Math3DOperation.title = "Operation"; - Math3DOperation.desc = "Easy math 3D operators"; - Math3DOperation["@OP"] = { - type: "enum", - title: "operation", - values: Math3DOperation.values - }; - Math3DOperation.size = [100, 60]; - - Math3DOperation.prototype.getTitle = function() { - if(this.properties.OP == "max" || this.properties.OP == "min" ) - return this.properties.OP + "(A,B)"; - return "A " + this.properties.OP + " B"; - }; - - Math3DOperation.prototype.onExecute = function() { - var A = this.getInputData(0); - var B = this.getInputData(1); - if(A == null || B == null) - return; - if(A.constructor === Number) - A = [A,A,A]; - if(B.constructor === Number) - B = [B,B,B]; - - var result = this._result; - switch (this.properties.OP) { - case "+": - result = vec3.add(result,A,B); - break; - case "-": - result = vec3.sub(result,A,B); - break; - case "x": - case "X": - case "*": - result = vec3.mul(result,A,B); - break; - case "/": - result = vec3.div(result,A,B); - break; - case "%": - result[0] = A[0]%B[0]; - result[1] = A[1]%B[1]; - result[2] = A[2]%B[2]; - break; - case "^": - result[0] = Math.pow(A[0],B[0]); - result[1] = Math.pow(A[1],B[1]); - result[2] = Math.pow(A[2],B[2]); - break; - case "max": - result[0] = Math.max(A[0],B[0]); - result[1] = Math.max(A[1],B[1]); - result[2] = Math.max(A[2],B[2]); - break; - case "min": - result[0] = Math.min(A[0],B[0]); - result[1] = Math.min(A[1],B[1]); - result[2] = Math.min(A[2],B[2]); - case "dot": - result = vec3.dot(A,B); - break; - case "cross": - vec3.cross(result,A,B); - break; - default: - console.warn("Unknown operation: " + this.properties.OP); - } - this.setOutputData(0, result); - }; - - Math3DOperation.prototype.onDrawBackground = function(ctx) { + Selector.prototype.onDrawBackground = function(ctx) { if (this.flags.collapsed) { return; } - - ctx.font = "40px Arial"; - ctx.fillStyle = "#666"; - ctx.textAlign = "center"; - ctx.fillText( - this.properties.OP, - this.size[0] * 0.5, - (this.size[1] + LiteGraph.NODE_TITLE_HEIGHT) * 0.5 - ); - ctx.textAlign = "left"; + ctx.fillStyle = "#AFB"; + var y = (this.selected + 1) * LiteGraph.NODE_SLOT_HEIGHT + 6; + ctx.beginPath(); + ctx.moveTo(50, y); + ctx.lineTo(50, y + LiteGraph.NODE_SLOT_HEIGHT); + ctx.lineTo(34, y + LiteGraph.NODE_SLOT_HEIGHT * 0.5); + ctx.fill(); }; - LiteGraph.registerNodeType("math3d/operation", Math3DOperation); - - function Math3DVec3Scale() { - this.addInput("in", "vec3"); - this.addInput("f", "number"); - this.addOutput("out", "vec3"); - this.properties = { f: 1 }; - this._data = new Float32Array(3); - } - - Math3DVec3Scale.title = "vec3_scale"; - Math3DVec3Scale.desc = "scales the components of a vec3"; - - Math3DVec3Scale.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - return; + Selector.prototype.onExecute = function() { + var sel = this.getInputData(0); + if (sel == null || sel.constructor !== Number) + sel = 0; + this.selected = sel = Math.round(sel) % (this.inputs.length - 1); + var v = this.getInputData(sel + 1); + if (v !== undefined) { + this.setOutputData(0, v); } - var f = this.getInputData(1); - if (f == null) { - f = this.properties.f; - } - - var data = this._data; - data[0] = v[0] * f; - data[1] = v[1] * f; - data[2] = v[2] * f; - this.setOutputData(0, data); }; - LiteGraph.registerNodeType("math3d/vec3-scale", Math3DVec3Scale); - - function Math3DVec3Length() { - this.addInput("in", "vec3"); - this.addOutput("out", "number"); - } - - Math3DVec3Length.title = "vec3_length"; - Math3DVec3Length.desc = "returns the module of a vector"; - - Math3DVec3Length.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - return; - } - var dist = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); - this.setOutputData(0, dist); + Selector.prototype.onGetInputs = function() { + return [["E", 0], ["F", 0], ["G", 0], ["H", 0]]; }; - LiteGraph.registerNodeType("math3d/vec3-length", Math3DVec3Length); + LiteGraph.registerNodeType("logic/selector", Selector); - function Math3DVec3Normalize() { - this.addInput("in", "vec3"); - this.addOutput("out", "vec3"); - this._data = new Float32Array(3); - } - - Math3DVec3Normalize.title = "vec3_normalize"; - Math3DVec3Normalize.desc = "returns the vector normalized"; - - Math3DVec3Normalize.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - return; - } - var dist = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); - var data = this._data; - data[0] = v[0] / dist; - data[1] = v[1] / dist; - data[2] = v[2] / dist; - - this.setOutputData(0, data); - }; - - LiteGraph.registerNodeType("math3d/vec3-normalize", Math3DVec3Normalize); - - function Math3DVec3Lerp() { - this.addInput("A", "vec3"); - this.addInput("B", "vec3"); - this.addInput("f", "vec3"); - this.addOutput("out", "vec3"); - this.properties = { f: 0.5 }; - this._data = new Float32Array(3); - } - - Math3DVec3Lerp.title = "vec3_lerp"; - Math3DVec3Lerp.desc = "returns the interpolated vector"; - - Math3DVec3Lerp.prototype.onExecute = function() { - var A = this.getInputData(0); - if (A == null) { - return; - } - var B = this.getInputData(1); - if (B == null) { - return; - } - var f = this.getInputOrProperty("f"); - - var data = this._data; - data[0] = A[0] * (1 - f) + B[0] * f; - data[1] = A[1] * (1 - f) + B[1] * f; - data[2] = A[2] * (1 - f) + B[2] * f; - - this.setOutputData(0, data); - }; - - LiteGraph.registerNodeType("math3d/vec3-lerp", Math3DVec3Lerp); - - function Math3DVec3Dot() { - this.addInput("A", "vec3"); - this.addInput("B", "vec3"); - this.addOutput("out", "number"); - } - - Math3DVec3Dot.title = "vec3_dot"; - Math3DVec3Dot.desc = "returns the dot product"; - - Math3DVec3Dot.prototype.onExecute = function() { - var A = this.getInputData(0); - if (A == null) { - return; - } - var B = this.getInputData(1); - if (B == null) { - return; - } - - var dot = A[0] * B[0] + A[1] * B[1] + A[2] * B[2]; - this.setOutputData(0, dot); - }; - - LiteGraph.registerNodeType("math3d/vec3-dot", Math3DVec3Dot); - - //if glMatrix is installed... - if (global.glMatrix) { - function Math3DQuaternion() { - this.addOutput("quat", "quat"); - this.properties = { x: 0, y: 0, z: 0, w: 1, normalize: false }; - this._value = quat.create(); - } - - Math3DQuaternion.title = "Quaternion"; - Math3DQuaternion.desc = "quaternion"; - - Math3DQuaternion.prototype.onExecute = function() { - this._value[0] = this.getInputOrProperty("x"); - this._value[1] = this.getInputOrProperty("y"); - this._value[2] = this.getInputOrProperty("z"); - this._value[3] = this.getInputOrProperty("w"); - if (this.properties.normalize) { - quat.normalize(this._value, this._value); - } - this.setOutputData(0, this._value); + function Sequence() { + this.properties = { + sequence: "A,B,C" }; + this.addInput("index", "number"); + this.addInput("seq"); + this.addOutput("out"); - Math3DQuaternion.prototype.onGetInputs = function() { - return [ - ["x", "number"], - ["y", "number"], - ["z", "number"], - ["w", "number"] - ]; - }; + this.index = 0; + this.values = this.properties.sequence.split(","); + } - LiteGraph.registerNodeType("math3d/quaternion", Math3DQuaternion); + Sequence.title = "Sequence"; + Sequence.desc = "select one element from a sequence from a string"; - function Math3DRotation() { - this.addInputs([["degrees", "number"], ["axis", "vec3"]]); - this.addOutput("quat", "quat"); - this.properties = { angle: 90.0, axis: vec3.fromValues(0, 1, 0) }; - - this._value = quat.create(); + Sequence.prototype.onPropertyChanged = function(name, value) { + if (name == "sequence") { + this.values = value.split(","); } + }; - Math3DRotation.title = "Rotation"; - Math3DRotation.desc = "quaternion rotation"; - - Math3DRotation.prototype.onExecute = function() { - var angle = this.getInputData(0); - if (angle == null) { - angle = this.properties.angle; - } - var axis = this.getInputData(1); - if (axis == null) { - axis = this.properties.axis; - } - - var R = quat.setAxisAngle(this._value, axis, angle * 0.0174532925); - this.setOutputData(0, R); - }; - - LiteGraph.registerNodeType("math3d/rotation", Math3DRotation); - - - function MathEulerToQuat() { - this.addInput("euler", "vec3"); - this.addOutput("quat", "quat"); - this.properties = { euler:[0,0,0], use_yaw_pitch_roll: false }; - this._degs = vec3.create(); - this._value = quat.create(); + Sequence.prototype.onExecute = function() { + var seq = this.getInputData(1); + if (seq && seq != this.current_sequence) { + this.values = seq.split(","); + this.current_sequence = seq; } - - MathEulerToQuat.title = "Euler->Quat"; - MathEulerToQuat.desc = "Converts euler angles (in degrees) to quaternion"; - - MathEulerToQuat.prototype.onExecute = function() { - var euler = this.getInputData(0); - if (euler == null) { - euler = this.properties.euler; - } - vec3.scale( this._degs, euler, DEG2RAD ); - if(this.properties.use_yaw_pitch_roll) - this._degs = [this._degs[2],this._degs[0],this._degs[1]]; - var R = quat.fromEuler(this._value, this._degs); - this.setOutputData(0, R); - }; - - LiteGraph.registerNodeType("math3d/euler_to_quat", MathEulerToQuat); - - function MathQuatToEuler() { - this.addInput(["quat", "quat"]); - this.addOutput("euler", "vec3"); - this._value = vec3.create(); + var index = this.getInputData(0); + if (index == null) { + index = 0; } + this.index = index = Math.round(index) % this.values.length; - MathQuatToEuler.title = "Euler->Quat"; - MathQuatToEuler.desc = "Converts rotX,rotY,rotZ in degrees to quat"; + this.setOutputData(0, this.values[index]); + }; - MathQuatToEuler.prototype.onExecute = function() { - var q = this.getInputData(0); - if(!q) + LiteGraph.registerNodeType("logic/sequence", Sequence); +})(this); + +(function(global) { + var LiteGraph = global.LiteGraph; + var LGraphCanvas = global.LGraphCanvas; + + //Works with Litegl.js to create WebGL nodes + global.LGraphTexture = null; + + if (typeof GL == "undefined") + return; + + LGraphCanvas.link_type_colors["Texture"] = "#987"; + + function LGraphTexture() { + this.addOutput("tex", "Texture"); + this.addOutput("name", "string"); + this.properties = { name: "", filter: true }; + this.size = [ + LGraphTexture.image_preview_size, + LGraphTexture.image_preview_size + ]; + } + + global.LGraphTexture = LGraphTexture; + + LGraphTexture.title = "Texture"; + LGraphTexture.desc = "Texture"; + LGraphTexture.widgets_info = { + name: { widget: "texture" }, + filter: { widget: "checkbox" } + }; + + //REPLACE THIS TO INTEGRATE WITH YOUR FRAMEWORK + LGraphTexture.loadTextureCallback = null; //function in charge of loading textures when not present in the container + LGraphTexture.image_preview_size = 256; + + //flags to choose output texture type + LGraphTexture.PASS_THROUGH = 1; //do not apply FX + LGraphTexture.COPY = 2; //create new texture with the same properties as the origin texture + LGraphTexture.LOW = 3; //create new texture with low precision (byte) + LGraphTexture.HIGH = 4; //create new texture with high precision (half-float) + LGraphTexture.REUSE = 5; //reuse input texture + LGraphTexture.DEFAULT = 2; + + LGraphTexture.MODE_VALUES = { + "pass through": LGraphTexture.PASS_THROUGH, + copy: LGraphTexture.COPY, + low: LGraphTexture.LOW, + high: LGraphTexture.HIGH, + reuse: LGraphTexture.REUSE, + default: LGraphTexture.DEFAULT + }; + + //returns the container where all the loaded textures are stored (overwrite if you have a Resources Manager) + LGraphTexture.getTexturesContainer = function() { + return gl.textures; + }; + + //process the loading of a texture (overwrite it if you have a Resources Manager) + LGraphTexture.loadTexture = function(name, options) { + options = options || {}; + var url = name; + if (url.substr(0, 7) == "http://") { + if (LiteGraph.proxy) { + //proxy external files + url = LiteGraph.proxy + url.substr(7); + } + } + + var container = LGraphTexture.getTexturesContainer(); + var tex = (container[name] = GL.Texture.fromURL(url, options)); + return tex; + }; + + LGraphTexture.getTexture = function(name) { + var container = this.getTexturesContainer(); + + if (!container) { + throw "Cannot load texture, container of textures not found"; + } + + var tex = container[name]; + if (!tex && name && name[0] != ":") { + return this.loadTexture(name); + } + + return tex; + }; + + //used to compute the appropiate output texture + LGraphTexture.getTargetTexture = function(origin, target, mode) { + if (!origin) { + throw "LGraphTexture.getTargetTexture expects a reference texture"; + } + + var tex_type = null; + + switch (mode) { + case LGraphTexture.LOW: + tex_type = gl.UNSIGNED_BYTE; + break; + case LGraphTexture.HIGH: + tex_type = gl.HIGH_PRECISION_FORMAT; + break; + case LGraphTexture.REUSE: + return origin; + break; + case LGraphTexture.COPY: + default: + tex_type = origin ? origin.type : gl.UNSIGNED_BYTE; + break; + } + + if ( + !target || + target.width != origin.width || + target.height != origin.height || + target.type != tex_type + ) { + target = new GL.Texture(origin.width, origin.height, { + type: tex_type, + format: gl.RGBA, + filter: gl.LINEAR + }); + } + + return target; + }; + + LGraphTexture.getTextureType = function(precision, ref_texture) { + var type = ref_texture ? ref_texture.type : gl.UNSIGNED_BYTE; + switch (precision) { + case LGraphTexture.HIGH: + type = gl.HIGH_PRECISION_FORMAT; + break; + case LGraphTexture.LOW: + type = gl.UNSIGNED_BYTE; + break; + //no default + } + return type; + }; + + LGraphTexture.getWhiteTexture = function() { + if (this._white_texture) { + return this._white_texture; + } + var texture = (this._white_texture = GL.Texture.fromMemory( + 1, + 1, + [255, 255, 255, 255], + { format: gl.RGBA, wrap: gl.REPEAT, filter: gl.NEAREST } + )); + return texture; + }; + + LGraphTexture.getNoiseTexture = function() { + if (this._noise_texture) { + return this._noise_texture; + } + + var noise = new Uint8Array(512 * 512 * 4); + for (var i = 0; i < 512 * 512 * 4; ++i) { + noise[i] = Math.random() * 255; + } + + var texture = GL.Texture.fromMemory(512, 512, noise, { + format: gl.RGBA, + wrap: gl.REPEAT, + filter: gl.NEAREST + }); + this._noise_texture = texture; + return texture; + }; + + LGraphTexture.prototype.onDropFile = function(data, filename, file) { + if (!data) { + this._drop_texture = null; + this.properties.name = ""; + } else { + var texture = null; + if (typeof data == "string") { + texture = GL.Texture.fromURL(data); + } else if (filename.toLowerCase().indexOf(".dds") != -1) { + texture = GL.Texture.fromDDSInMemory(data); + } else { + var blob = new Blob([file]); + var url = URL.createObjectURL(blob); + texture = GL.Texture.fromURL(url); + } + + this._drop_texture = texture; + this.properties.name = filename; + } + }; + + LGraphTexture.prototype.getExtraMenuOptions = function(graphcanvas) { + var that = this; + if (!this._drop_texture) { + return; + } + return [ + { + content: "Clear", + callback: function() { + that._drop_texture = null; + that.properties.name = ""; + } + } + ]; + }; + + LGraphTexture.prototype.onExecute = function() { + var tex = null; + if (this.isOutputConnected(1)) { + tex = this.getInputData(0); + } + + if (!tex && this._drop_texture) { + tex = this._drop_texture; + } + + if (!tex && this.properties.name) { + tex = LGraphTexture.getTexture(this.properties.name); + } + + if (!tex) { + this.setOutputData( 0, null ); + this.setOutputData( 1, "" ); + return; + } + + this._last_tex = tex; + + if (this.properties.filter === false) { + tex.setParameter(gl.TEXTURE_MAG_FILTER, gl.NEAREST); + } else { + tex.setParameter(gl.TEXTURE_MAG_FILTER, gl.LINEAR); + } + + this.setOutputData( 0, tex ); + this.setOutputData( 1, tex.fullpath || tex.filename ); + + for (var i = 2; i < this.outputs.length; i++) { + var output = this.outputs[i]; + if (!output) { + continue; + } + var v = null; + if (output.name == "width") { + v = tex.width; + } else if (output.name == "height") { + v = tex.height; + } else if (output.name == "aspect") { + v = tex.width / tex.height; + } + this.setOutputData(i, v); + } + }; + + LGraphTexture.prototype.onResourceRenamed = function( + old_name, + new_name + ) { + if (this.properties.name == old_name) { + this.properties.name = new_name; + } + }; + + LGraphTexture.prototype.onDrawBackground = function(ctx) { + if (this.flags.collapsed || this.size[1] <= 20) { + return; + } + + if (this._drop_texture && ctx.webgl) { + ctx.drawImage( + this._drop_texture, + 0, + 0, + this.size[0], + this.size[1] + ); + //this._drop_texture.renderQuad(this.pos[0],this.pos[1],this.size[0],this.size[1]); + return; + } + + //Different texture? then get it from the GPU + if (this._last_preview_tex != this._last_tex) { + if (ctx.webgl) { + this._canvas = this._last_tex; + } else { + var tex_canvas = LGraphTexture.generateLowResTexturePreview( + this._last_tex + ); + if (!tex_canvas) { + return; + } + + this._last_preview_tex = this._last_tex; + this._canvas = cloneCanvas(tex_canvas); + } + } + + if (!this._canvas) { + return; + } + + //render to graph canvas + ctx.save(); + if (!ctx.webgl) { + //reverse image + ctx.translate(0, this.size[1]); + ctx.scale(1, -1); + } + ctx.drawImage(this._canvas, 0, 0, this.size[0], this.size[1]); + ctx.restore(); + }; + + //very slow, used at your own risk + LGraphTexture.generateLowResTexturePreview = function(tex) { + if (!tex) { + return null; + } + + var size = LGraphTexture.image_preview_size; + var temp_tex = tex; + + if (tex.format == gl.DEPTH_COMPONENT) { + return null; + } //cannot generate from depth + + //Generate low-level version in the GPU to speed up + if (tex.width > size || tex.height > size) { + temp_tex = this._preview_temp_tex; + if (!this._preview_temp_tex) { + temp_tex = new GL.Texture(size, size, { + minFilter: gl.NEAREST + }); + this._preview_temp_tex = temp_tex; + } + + //copy + tex.copyTo(temp_tex); + tex = temp_tex; + } + + //create intermediate canvas with lowquality version + var tex_canvas = this._preview_canvas; + if (!tex_canvas) { + tex_canvas = createCanvas(size, size); + this._preview_canvas = tex_canvas; + } + + if (temp_tex) { + temp_tex.toCanvas(tex_canvas); + } + return tex_canvas; + }; + + LGraphTexture.prototype.getResources = function(res) { + if(this.properties.name) + res[this.properties.name] = GL.Texture; + return res; + }; + + LGraphTexture.prototype.onGetInputs = function() { + return [["in", "Texture"]]; + }; + + LGraphTexture.prototype.onGetOutputs = function() { + return [ + ["width", "number"], + ["height", "number"], + ["aspect", "number"] + ]; + }; + + //used to replace shader code + LGraphTexture.replaceCode = function( code, context ) + { + return code.replace(/\{\{[a-zA-Z0-9_]*\}\}/g, function(v){ + v = v.replace( /[\{\}]/g, "" ); + return context[v] || ""; + }); + } + + LiteGraph.registerNodeType("texture/texture", LGraphTexture); + + //************************** + function LGraphTexturePreview() { + this.addInput("Texture", "Texture"); + this.properties = { flipY: false }; + this.size = [ + LGraphTexture.image_preview_size, + LGraphTexture.image_preview_size + ]; + } + + LGraphTexturePreview.title = "Preview"; + LGraphTexturePreview.desc = "Show a texture in the graph canvas"; + LGraphTexturePreview.allow_preview = false; + + LGraphTexturePreview.prototype.onDrawBackground = function(ctx) { + if (this.flags.collapsed) { + return; + } + + if (!ctx.webgl && !LGraphTexturePreview.allow_preview) { + return; + } //not working well + + var tex = this.getInputData(0); + if (!tex) { + return; + } + + var tex_canvas = null; + + if (!tex.handle && ctx.webgl) { + tex_canvas = tex; + } else { + tex_canvas = LGraphTexture.generateLowResTexturePreview(tex); + } + + //render to graph canvas + ctx.save(); + if (this.properties.flipY) { + ctx.translate(0, this.size[1]); + ctx.scale(1, -1); + } + ctx.drawImage(tex_canvas, 0, 0, this.size[0], this.size[1]); + ctx.restore(); + }; + + LiteGraph.registerNodeType("texture/preview", LGraphTexturePreview); + + //************************************** + + function LGraphTextureSave() { + this.addInput("Texture", "Texture"); + this.addOutput("tex", "Texture"); + this.addOutput("name", "string"); + this.properties = { name: "", generate_mipmaps: false }; + } + + LGraphTextureSave.title = "Save"; + LGraphTextureSave.desc = "Save a texture in the repository"; + + LGraphTextureSave.prototype.getPreviewTexture = function() + { + return this._texture; + } + + LGraphTextureSave.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (this.properties.generate_mipmaps) { + tex.bind(0); + tex.setParameter( gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR ); + gl.generateMipmap(tex.texture_type); + tex.unbind(0); + } + + if (this.properties.name) { + //for cases where we want to perform something when storing it + if (LGraphTexture.storeTexture) { + LGraphTexture.storeTexture(this.properties.name, tex); + } else { + var container = LGraphTexture.getTexturesContainer(); + container[this.properties.name] = tex; + } + } + + this._texture = tex; + this.setOutputData(0, tex); + this.setOutputData(1, this.properties.name); + }; + + LiteGraph.registerNodeType("texture/save", LGraphTextureSave); + + //**************************************************** + + function LGraphTextureOperation() { + this.addInput("Texture", "Texture"); + this.addInput("TextureB", "Texture"); + this.addInput("value", "number"); + this.addOutput("Texture", "Texture"); + this.help = "

pixelcode must be vec3, uvcode must be vec2, is optional

\ +

uv: tex. coords

color: texture colorB: textureB

time: scene time value: input value

For multiline you must type: result = ...

"; + + this.properties = { + value: 1, + pixelcode: "color + colorB * value", + uvcode: "", + precision: LGraphTexture.DEFAULT + }; + + this.has_error = false; + } + + LGraphTextureOperation.widgets_info = { + uvcode: { widget: "code" }, + pixelcode: { widget: "code" }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureOperation.title = "Operation"; + LGraphTextureOperation.desc = "Texture shader operation"; + + LGraphTextureOperation.presets = {}; + + LGraphTextureOperation.prototype.getExtraMenuOptions = function( + graphcanvas + ) { + var that = this; + var txt = !that.properties.show ? "Show Texture" : "Hide Texture"; + return [ + { + content: txt, + callback: function() { + that.properties.show = !that.properties.show; + } + } + ]; + }; + + LGraphTextureOperation.prototype.onPropertyChanged = function() + { + this.has_error = false; + } + + LGraphTextureOperation.prototype.onDrawBackground = function(ctx) { + if ( + this.flags.collapsed || + this.size[1] <= 20 || + !this.properties.show + ) { + return; + } + + if (!this._tex) { + return; + } + + //only works if using a webgl renderer + if (this._tex.gl != ctx) { + return; + } + + //render to graph canvas + ctx.save(); + ctx.drawImage(this._tex, 0, 0, this.size[0], this.size[1]); + ctx.restore(); + }; + + LGraphTextureOperation.prototype.onExecute = function() { + var tex = this.getInputData(0); + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, tex); + return; + } + + var texB = this.getInputData(1); + + if (!this.properties.uvcode && !this.properties.pixelcode) { + return; + } + + var width = 512; + var height = 512; + if (tex) { + width = tex.width; + height = tex.height; + } else if (texB) { + width = texB.width; + height = texB.height; + } + + if(!texB) + texB = GL.Texture.getWhiteTexture(); + + var type = LGraphTexture.getTextureType( this.properties.precision, tex ); + + if (!tex && !this._tex) { + this._tex = new GL.Texture(width, height, { type: type, format: gl.RGBA, filter: gl.LINEAR }); + } else { + this._tex = LGraphTexture.getTargetTexture( tex || this._tex, this._tex, this.properties.precision ); + } + + var uvcode = ""; + if (this.properties.uvcode) { + uvcode = "uv = " + this.properties.uvcode; + if (this.properties.uvcode.indexOf(";") != -1) { + //there are line breaks, means multiline code + uvcode = this.properties.uvcode; + } + } + + var pixelcode = ""; + if (this.properties.pixelcode) { + pixelcode = "result = " + this.properties.pixelcode; + if (this.properties.pixelcode.indexOf(";") != -1) { + //there are line breaks, means multiline code + pixelcode = this.properties.pixelcode; + } + } + + var shader = this._shader; + + if ( !this.has_error && (!shader || this._shader_code != uvcode + "|" + pixelcode) ) { + + var final_pixel_code = LGraphTexture.replaceCode( LGraphTextureOperation.pixel_shader, { UV_CODE:uvcode, PIXEL_CODE:pixelcode }); + + try { + shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, final_pixel_code ); + this.boxcolor = "#00FF00"; + } catch (err) { + //console.log("Error compiling shader: ", err, final_pixel_code ); + GL.Shader.dumpErrorToConsole(err,Shader.SCREEN_VERTEX_SHADER, final_pixel_code); + this.boxcolor = "#FF0000"; + this.has_error = true; return; - var R = quat.toEuler(this._value, q); - vec3.scale( this._value, this._value, DEG2RAD ); - this.setOutputData(0, this._value); - }; - - LiteGraph.registerNodeType("math3d/quat_to_euler", MathQuatToEuler); - - - //Math3D rotate vec3 - function Math3DRotateVec3() { - this.addInputs([["vec3", "vec3"], ["quat", "quat"]]); - this.addOutput("result", "vec3"); - this.properties = { vec: [0, 0, 1] }; - } - - Math3DRotateVec3.title = "Rot. Vec3"; - Math3DRotateVec3.desc = "rotate a point"; - - Math3DRotateVec3.prototype.onExecute = function() { - var vec = this.getInputData(0); - if (vec == null) { - vec = this.properties.vec; - } - var quat = this.getInputData(1); - if (quat == null) { - this.setOutputData(vec); - } else { - this.setOutputData( - 0, - vec3.transformQuat(vec3.create(), vec, quat) - ); - } - }; - - LiteGraph.registerNodeType("math3d/rotate_vec3", Math3DRotateVec3); - - function Math3DMultQuat() { - this.addInputs([["A", "quat"], ["B", "quat"]]); - this.addOutput("A*B", "quat"); - - this._value = quat.create(); - } - - Math3DMultQuat.title = "Mult. Quat"; - Math3DMultQuat.desc = "rotate quaternion"; - - Math3DMultQuat.prototype.onExecute = function() { - var A = this.getInputData(0); - if (A == null) { - return; - } - var B = this.getInputData(1); - if (B == null) { - return; - } - - var R = quat.multiply(this._value, A, B); - this.setOutputData(0, R); - }; - - LiteGraph.registerNodeType("math3d/mult-quat", Math3DMultQuat); - - function Math3DQuatSlerp() { - this.addInputs([ - ["A", "quat"], - ["B", "quat"], - ["factor", "number"] - ]); - this.addOutput("slerp", "quat"); - this.addProperty("factor", 0.5); - - this._value = quat.create(); - } - - Math3DQuatSlerp.title = "Quat Slerp"; - Math3DQuatSlerp.desc = "quaternion spherical interpolation"; - - Math3DQuatSlerp.prototype.onExecute = function() { - var A = this.getInputData(0); - if (A == null) { - return; - } - var B = this.getInputData(1); - if (B == null) { - return; - } - var factor = this.properties.factor; - if (this.getInputData(2) != null) { - factor = this.getInputData(2); - } - - var R = quat.slerp(this._value, A, B, factor); - this.setOutputData(0, R); - }; - - LiteGraph.registerNodeType("math3d/quat-slerp", Math3DQuatSlerp); - - - //Math3D rotate vec3 - function Math3DRemapRange() { - this.addInput("vec3", "vec3"); - this.addOutput("remap", "vec3"); - this.addOutput("clamped", "vec3"); - this.properties = { clamp: true, range_min: [-1, -1, 0], range_max: [1, 1, 0], target_min: [-1,-1,0], target_max:[1,1,0] }; - this._value = vec3.create(); - this._clamped = vec3.create(); - } - - Math3DRemapRange.title = "Remap Range"; - Math3DRemapRange.desc = "remap a 3D range"; - - Math3DRemapRange.prototype.onExecute = function() { - var vec = this.getInputData(0); - if(vec) - this._value.set(vec); - var range_min = this.properties.range_min; - var range_max = this.properties.range_max; - var target_min = this.properties.target_min; - var target_max = this.properties.target_max; - - //swap to avoid errors - /* - if(range_min > range_max) - { - range_min = range_max; - range_max = this.properties.range_min; } + this._shader = shader; + this._shader_code = uvcode + "|" + pixelcode; + } - if(target_min > target_max) - { - target_min = target_max; - target_max = this.properties.target_min; + if(!this._shader) + return; + + var value = this.getInputData(2); + if (value != null) { + this.properties.value = value; + } else { + value = parseFloat(this.properties.value); + } + + var time = this.graph.getTime(); + + this._tex.drawTo(function() { + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + gl.disable(gl.BLEND); + if (tex) { + tex.bind(0); } - */ + if (texB) { + texB.bind(1); + } + var mesh = Mesh.getScreenQuad(); + shader + .uniforms({ + u_texture: 0, + u_textureB: 1, + value: value, + texSize: [width, height], + time: time + }) + .draw(mesh); + }); - for(var i = 0; i < 3; ++i) - { - var r = range_max[i] - range_min[i]; - this._clamped[i] = Math.clamp( this._value[i], range_min[i], range_max[i] ); - if(r == 0) - { - this._value[i] = (target_min[i] + target_max[i]) * 0.5; + this.setOutputData(0, this._tex); + }; + + LGraphTextureOperation.pixel_shader = + "precision highp float;\n\ + \n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_textureB;\n\ + varying vec2 v_coord;\n\ + uniform vec2 texSize;\n\ + uniform float time;\n\ + uniform float value;\n\ + \n\ + void main() {\n\ + vec2 uv = v_coord;\n\ + {{UV_CODE}};\n\ + vec4 color4 = texture2D(u_texture, uv);\n\ + vec3 color = color4.rgb;\n\ + vec4 color4B = texture2D(u_textureB, uv);\n\ + vec3 colorB = color4B.rgb;\n\ + vec3 result = color;\n\ + float alpha = 1.0;\n\ + {{PIXEL_CODE}};\n\ + gl_FragColor = vec4(result, alpha);\n\ + }\n\ + "; + + LGraphTextureOperation.registerPreset = function ( name, code ) + { + LGraphTextureOperation.presets[name] = code; + } + + LGraphTextureOperation.registerPreset("",""); + LGraphTextureOperation.registerPreset("bypass","color"); + LGraphTextureOperation.registerPreset("add","color + colorB * value"); + LGraphTextureOperation.registerPreset("substract","(color - colorB) * value"); + LGraphTextureOperation.registerPreset("mate","mix( color, colorB, color4B.a * value)"); + LGraphTextureOperation.registerPreset("invert","vec3(1.0) - color"); + LGraphTextureOperation.registerPreset("multiply","color * colorB * value"); + LGraphTextureOperation.registerPreset("divide","(color / colorB) / value"); + LGraphTextureOperation.registerPreset("difference","abs(color - colorB) * value"); + LGraphTextureOperation.registerPreset("max","max(color, colorB) * value"); + LGraphTextureOperation.registerPreset("min","min(color, colorB) * value"); + LGraphTextureOperation.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"); + LGraphTextureOperation.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"); + LGraphTextureOperation.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"); + LGraphTextureOperation.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"); + + //webglstudio stuff... + LGraphTextureOperation.prototype.onInspect = function(widgets) + { + var that = this; + widgets.addCombo("Presets","",{ values: Object.keys(LGraphTextureOperation.presets), callback: function(v){ + var code = LGraphTextureOperation.presets[v]; + if(!code) + return; + that.setProperty("pixelcode",code); + that.title = v; + widgets.refresh(); + }}); + } + + LiteGraph.registerNodeType("texture/operation", LGraphTextureOperation); + + //**************************************************** + + function LGraphTextureShader() { + this.addOutput("out", "Texture"); + this.properties = { + code: "", + u_value: 1, + u_color: [1,1,1,1], + width: 512, + height: 512, + precision: LGraphTexture.DEFAULT + }; + + this.properties.code = LGraphTextureShader.pixel_shader; + this._uniforms = { u_value: 1, u_color: vec4.create(), in_texture: 0, texSize: vec2.create(), time: 0 }; + } + + LGraphTextureShader.title = "Shader"; + LGraphTextureShader.desc = "Texture shader"; + LGraphTextureShader.widgets_info = { + code: { type: "code", lang: "glsl" }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureShader.prototype.onPropertyChanged = function( + name, + value + ) { + if (name != "code") { + return; + } + + var shader = this.getShader(); + if (!shader) { + return; + } + + //update connections + var uniforms = shader.uniformInfo; + + //remove deprecated slots + if (this.inputs) { + var already = {}; + for (var i = 0; i < this.inputs.length; ++i) { + var info = this.getInputInfo(i); + if (!info) { continue; } - var n = (this._value[i] - range_min[i]) / r; - if(this.properties.clamp) - n = Math.clamp(n,0,1); - var t = target_max[i] - target_min[i]; - this._value[i] = target_min[i] + n * t; + if (uniforms[info.name] && !already[info.name]) { + already[info.name] = true; + continue; + } + this.removeInput(i); + i--; + } + } + + //update existing ones + for (var i in uniforms) { + var info = shader.uniformInfo[i]; + if (info.loc === null) { + continue; + } //is an attribute, not a uniform + if (i == "time") { + //default one + continue; } - this.setOutputData(0,this._value); - this.setOutputData(1,this._clamped); - }; + var type = "number"; + if (this._shader.samplers[i]) { + type = "texture"; + } else { + switch (info.size) { + case 1: + type = "number"; + break; + case 2: + type = "vec2"; + break; + case 3: + type = "vec3"; + break; + case 4: + type = "vec4"; + break; + case 9: + type = "mat3"; + break; + case 16: + type = "mat4"; + break; + default: + continue; + } + } - LiteGraph.registerNodeType("math3d/remap_range", Math3DRemapRange); + var slot = this.findInputSlot(i); + if (slot == -1) { + this.addInput(i, type); + continue; + } - - - } //glMatrix - else - console.warn("No glmatrix found, some Math3D nodes may not work"); - -})(this); - -//basic nodes -(function(global) { - var LiteGraph = global.LiteGraph; - - function toString(a) { - return String(a); - } - - LiteGraph.wrapFunctionAsNode("string/toString", compare, [""], "String"); - - function compare(a, b) { - return a == b; - } - - LiteGraph.wrapFunctionAsNode( - "string/compare", - compare, - ["string", "string"], - "boolean" - ); - - function concatenate(a, b) { - if (a === undefined) { - return b; - } - if (b === undefined) { - return a; - } - return a + b; - } - - LiteGraph.wrapFunctionAsNode( - "string/concatenate", - concatenate, - ["string", "string"], - "string" - ); - - function contains(a, b) { - if (a === undefined || b === undefined) { - return false; - } - return a.indexOf(b) != -1; - } - - LiteGraph.wrapFunctionAsNode( - "string/contains", - contains, - ["string", "string"], - "boolean" - ); - - function toUpperCase(a) { - if (a != null && a.constructor === String) { - return a.toUpperCase(); - } - return a; - } - - LiteGraph.wrapFunctionAsNode( - "string/toUpperCase", - toUpperCase, - ["string"], - "string" - ); - - function split(str, separator) { - if(separator == null) - separator = this.properties.separator; - if (str == null ) - return []; - if( str.constructor === String ) - return str.split(separator || " "); - else if( str.constructor === Array ) - { - var r = []; - for(var i = 0; i < str.length; ++i) - r[i] = str[i].split(separator || " "); - return r; + var input_info = this.getInputInfo(slot); + if (!input_info) { + this.addInput(i, type); + } else { + if (input_info.type == type) { + continue; + } + this.removeInput(slot, type); + this.addInput(i, type); + } } - return null; - } + }; - LiteGraph.wrapFunctionAsNode( - "string/split", - split, - ["string,array", "string"], - "array", - { separator: "," } - ); + LGraphTextureShader.prototype.getShader = function() { + //replug + if (this._shader && this._shader_code == this.properties.code) { + return this._shader; + } - function toFixed(a) { - if (a != null && a.constructor === Number) { - return a.toFixed(this.properties.precision); - } - return a; - } + this._shader_code = this.properties.code; + this._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, this.properties.code ); + if (!this._shader) { + this.boxcolor = "red"; + return null; + } else { + this.boxcolor = "green"; + } + return this._shader; + }; - LiteGraph.wrapFunctionAsNode( - "string/toFixed", - toFixed, - ["number"], - "string", - { precision: 0 } - ); - - - function StringToTable() { - this.addInput("", "string"); - this.addOutput("table", "table"); - this.addOutput("rows", "number"); - this.addProperty("value", ""); - this.addProperty("separator", ","); - this._table = null; - } - - StringToTable.title = "toTable"; - StringToTable.desc = "Splits a string to table"; - - StringToTable.prototype.onExecute = function() { - var input = this.getInputData(0); - if(!input) + LGraphTextureShader.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { + return; + } //saves work + + var shader = this.getShader(); + if (!shader) { return; - var separator = this.properties.separator || ","; - if(input != this._str || separator != this._last_separator ) - { - this._last_separator = separator; - this._str = input; - this._table = input.split("\n").map(function(a){ return a.trim().split(separator)}); } - this.setOutputData(0, this._table ); - this.setOutputData(1, this._table ? this._table.length : 0 ); - }; - LiteGraph.registerNodeType("string/toTable", StringToTable); + var tex_slot = 0; + var in_tex = null; -})(this); - -(function(global) { - var LiteGraph = global.LiteGraph; - - function Selector() { - this.addInput("sel", "number"); - this.addInput("A"); - this.addInput("B"); - this.addInput("C"); - this.addInput("D"); - this.addOutput("out"); - - this.selected = 0; - } - - Selector.title = "Selector"; - Selector.desc = "selects an output"; - - Selector.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } - ctx.fillStyle = "#AFB"; - var y = (this.selected + 1) * LiteGraph.NODE_SLOT_HEIGHT + 6; - ctx.beginPath(); - ctx.moveTo(50, y); - ctx.lineTo(50, y + LiteGraph.NODE_SLOT_HEIGHT); - ctx.lineTo(34, y + LiteGraph.NODE_SLOT_HEIGHT * 0.5); - ctx.fill(); - }; - - Selector.prototype.onExecute = function() { - var sel = this.getInputData(0); - if (sel == null || sel.constructor !== Number) - sel = 0; - this.selected = sel = Math.round(sel) % (this.inputs.length - 1); - var v = this.getInputData(sel + 1); - if (v !== undefined) { - this.setOutputData(0, v); - } - }; - - Selector.prototype.onGetInputs = function() { - return [["E", 0], ["F", 0], ["G", 0], ["H", 0]]; - }; - - LiteGraph.registerNodeType("logic/selector", Selector); - - function Sequence() { - this.properties = { - sequence: "A,B,C" - }; - this.addInput("index", "number"); - this.addInput("seq"); - this.addOutput("out"); - - this.index = 0; - this.values = this.properties.sequence.split(","); - } - - Sequence.title = "Sequence"; - Sequence.desc = "select one element from a sequence from a string"; - - Sequence.prototype.onPropertyChanged = function(name, value) { - if (name == "sequence") { - this.values = value.split(","); - } - }; - - Sequence.prototype.onExecute = function() { - var seq = this.getInputData(1); - if (seq && seq != this.current_sequence) { - this.values = seq.split(","); - this.current_sequence = seq; - } - var index = this.getInputData(0); - if (index == null) { - index = 0; - } - this.index = index = Math.round(index) % this.values.length; - - this.setOutputData(0, this.values[index]); - }; - - LiteGraph.registerNodeType("logic/sequence", Sequence); -})(this); - -(function(global) { - var LiteGraph = global.LiteGraph; - - function GraphicsPlot() { - this.addInput("A", "Number"); - this.addInput("B", "Number"); - this.addInput("C", "Number"); - this.addInput("D", "Number"); - - this.values = [[], [], [], []]; - this.properties = { scale: 2 }; - } - - GraphicsPlot.title = "Plot"; - GraphicsPlot.desc = "Plots data over time"; - GraphicsPlot.colors = ["#FFF", "#F99", "#9F9", "#99F"]; - - GraphicsPlot.prototype.onExecute = function(ctx) { - if (this.flags.collapsed) { - return; - } - - var size = this.size; - - for (var i = 0; i < 4; ++i) { - var v = this.getInputData(i); - if (v == null) { - continue; - } - var values = this.values[i]; - values.push(v); - if (values.length > size[0]) { - values.shift(); - } - } - }; - - GraphicsPlot.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } - - var size = this.size; - - var scale = (0.5 * size[1]) / this.properties.scale; - var colors = GraphicsPlot.colors; - var offset = size[1] * 0.5; - - ctx.fillStyle = "#000"; - ctx.fillRect(0, 0, size[0], size[1]); - ctx.strokeStyle = "#555"; - ctx.beginPath(); - ctx.moveTo(0, offset); - ctx.lineTo(size[0], offset); - ctx.stroke(); - - if (this.inputs) { - for (var i = 0; i < 4; ++i) { - var values = this.values[i]; - if (!this.inputs[i] || !this.inputs[i].link) { - continue; - } - ctx.strokeStyle = colors[i]; - ctx.beginPath(); - var v = values[0] * scale * -1 + offset; - ctx.moveTo(0, Math.clamp(v, 0, size[1])); - for (var j = 1; j < values.length && j < size[0]; ++j) { - var v = values[j] * scale * -1 + offset; - ctx.lineTo(j, Math.clamp(v, 0, size[1])); - } - ctx.stroke(); - } - } - }; - - LiteGraph.registerNodeType("graphics/plot", GraphicsPlot); - - function GraphicsImage() { - this.addOutput("frame", "image"); - this.properties = { url: "" }; - } - - GraphicsImage.title = "Image"; - GraphicsImage.desc = "Image loader"; - GraphicsImage.widgets = [{ name: "load", text: "Load", type: "button" }]; - - GraphicsImage.supported_extensions = ["jpg", "jpeg", "png", "gif"]; - - GraphicsImage.prototype.onAdded = function() { - if (this.properties["url"] != "" && this.img == null) { - this.loadImage(this.properties["url"]); - } - }; - - GraphicsImage.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } - if (this.img && this.size[0] > 5 && this.size[1] > 5 && this.img.width) { - ctx.drawImage(this.img, 0, 0, this.size[0], this.size[1]); - } - }; - - GraphicsImage.prototype.onExecute = function() { - if (!this.img) { - this.boxcolor = "#000"; - } - if (this.img && this.img.width) { - this.setOutputData(0, this.img); - } else { - this.setOutputData(0, null); - } - if (this.img && this.img.dirty) { - this.img.dirty = false; - } - }; - - GraphicsImage.prototype.onPropertyChanged = function(name, value) { - this.properties[name] = value; - if (name == "url" && value != "") { - this.loadImage(value); - } - - return true; - }; - - GraphicsImage.prototype.loadImage = function(url, callback) { - if (url == "") { - this.img = null; - return; - } - - this.img = document.createElement("img"); - - if (url.substr(0, 4) == "http" && LiteGraph.proxy) { - url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); - } - - this.img.src = url; - this.boxcolor = "#F95"; - var that = this; - this.img.onload = function() { - if (callback) { - callback(this); - } - console.log( "Image loaded, size: " + that.img.width + "x" + that.img.height ); - this.dirty = true; - that.boxcolor = "#9F9"; - that.setDirtyCanvas(true); - }; - this.img.onerror = function() { - console.log("error loading the image:" + url); - } - }; - - GraphicsImage.prototype.onWidget = function(e, widget) { - if (widget.name == "load") { - this.loadImage(this.properties["url"]); - } - }; - - GraphicsImage.prototype.onDropFile = function(file) { - var that = this; - if (this._url) { - URL.revokeObjectURL(this._url); - } - this._url = URL.createObjectURL(file); - this.properties.url = this._url; - this.loadImage(this._url, function(img) { - that.size[1] = (img.height / img.width) * that.size[0]; - }); - }; - - LiteGraph.registerNodeType("graphics/image", GraphicsImage); - - function ColorPalette() { - this.addInput("f", "number"); - this.addOutput("Color", "color"); - this.properties = { - colorA: "#444444", - colorB: "#44AAFF", - colorC: "#44FFAA", - colorD: "#FFFFFF" - }; - } - - ColorPalette.title = "Palette"; - ColorPalette.desc = "Generates a color"; - - ColorPalette.prototype.onExecute = function() { - var c = []; - - if (this.properties.colorA != null) { - c.push(hex2num(this.properties.colorA)); - } - if (this.properties.colorB != null) { - c.push(hex2num(this.properties.colorB)); - } - if (this.properties.colorC != null) { - c.push(hex2num(this.properties.colorC)); - } - if (this.properties.colorD != null) { - c.push(hex2num(this.properties.colorD)); - } - - var f = this.getInputData(0); - if (f == null) { - f = 0.5; - } - if (f > 1.0) { - f = 1.0; - } else if (f < 0.0) { - f = 0.0; - } - - if (c.length == 0) { - return; - } - - var result = [0, 0, 0]; - if (f == 0) { - result = c[0]; - } else if (f == 1) { - result = c[c.length - 1]; - } else { - var pos = (c.length - 1) * f; - var c1 = c[Math.floor(pos)]; - var c2 = c[Math.floor(pos) + 1]; - var t = pos - Math.floor(pos); - result[0] = c1[0] * (1 - t) + c2[0] * t; - result[1] = c1[1] * (1 - t) + c2[1] * t; - result[2] = c1[2] * (1 - t) + c2[2] * t; - } - - /* - c[0] = 1.0 - Math.abs( Math.sin( 0.1 * reModular.getTime() * Math.PI) ); - c[1] = Math.abs( Math.sin( 0.07 * reModular.getTime() * Math.PI) ); - c[2] = Math.abs( Math.sin( 0.01 * reModular.getTime() * Math.PI) ); - */ - - for (var i in result) { - result[i] /= 255; - } - - this.boxcolor = colorToString(result); - this.setOutputData(0, result); - }; - - LiteGraph.registerNodeType("color/palette", ColorPalette); - - function ImageFrame() { - this.addInput("", "image,canvas"); - this.size = [200, 200]; - } - - ImageFrame.title = "Frame"; - ImageFrame.desc = "Frame viewerew"; - ImageFrame.widgets = [ - { name: "resize", text: "Resize box", type: "button" }, - { name: "view", text: "View Image", type: "button" } - ]; - - ImageFrame.prototype.onDrawBackground = function(ctx) { - if (this.frame && !this.flags.collapsed) { - ctx.drawImage(this.frame, 0, 0, this.size[0], this.size[1]); - } - }; - - ImageFrame.prototype.onExecute = function() { - this.frame = this.getInputData(0); - this.setDirtyCanvas(true); - }; - - ImageFrame.prototype.onWidget = function(e, widget) { - if (widget.name == "resize" && this.frame) { - var width = this.frame.width; - var height = this.frame.height; - - if (!width && this.frame.videoWidth != null) { - width = this.frame.videoWidth; - height = this.frame.videoHeight; - } - - if (width && height) { - this.size = [width, height]; - } - this.setDirtyCanvas(true, true); - } else if (widget.name == "view") { - this.show(); - } - }; - - ImageFrame.prototype.show = function() { - //var str = this.canvas.toDataURL("image/png"); - if (showElement && this.frame) { - showElement(this.frame); - } - }; - - LiteGraph.registerNodeType("graphics/frame", ImageFrame); - - function ImageFade() { - this.addInputs([ - ["img1", "image"], - ["img2", "image"], - ["fade", "number"] - ]); - this.addOutput("", "image"); - this.properties = { fade: 0.5, width: 512, height: 512 }; - } - - ImageFade.title = "Image fade"; - ImageFade.desc = "Fades between images"; - ImageFade.widgets = [ - { name: "resizeA", text: "Resize to A", type: "button" }, - { name: "resizeB", text: "Resize to B", type: "button" } - ]; - - ImageFade.prototype.onAdded = function() { - this.createCanvas(); - var ctx = this.canvas.getContext("2d"); - ctx.fillStyle = "#000"; - ctx.fillRect(0, 0, this.properties["width"], this.properties["height"]); - }; - - ImageFade.prototype.createCanvas = function() { - this.canvas = document.createElement("canvas"); - this.canvas.width = this.properties["width"]; - this.canvas.height = this.properties["height"]; - }; - - ImageFade.prototype.onExecute = function() { - var ctx = this.canvas.getContext("2d"); - this.canvas.width = this.canvas.width; - - var A = this.getInputData(0); - if (A != null) { - ctx.drawImage(A, 0, 0, this.canvas.width, this.canvas.height); - } - - var fade = this.getInputData(2); - if (fade == null) { - fade = this.properties["fade"]; - } else { - this.properties["fade"] = fade; - } - - ctx.globalAlpha = fade; - var B = this.getInputData(1); - if (B != null) { - ctx.drawImage(B, 0, 0, this.canvas.width, this.canvas.height); - } - ctx.globalAlpha = 1.0; - - this.setOutputData(0, this.canvas); - this.setDirtyCanvas(true); - }; - - LiteGraph.registerNodeType("graphics/imagefade", ImageFade); - - function ImageCrop() { - this.addInput("", "image"); - this.addOutput("", "image"); - this.properties = { width: 256, height: 256, x: 0, y: 0, scale: 1.0 }; - this.size = [50, 20]; - } - - ImageCrop.title = "Crop"; - ImageCrop.desc = "Crop Image"; - - ImageCrop.prototype.onAdded = function() { - this.createCanvas(); - }; - - ImageCrop.prototype.createCanvas = function() { - this.canvas = document.createElement("canvas"); - this.canvas.width = this.properties["width"]; - this.canvas.height = this.properties["height"]; - }; - - ImageCrop.prototype.onExecute = function() { - var input = this.getInputData(0); - if (!input) { - return; - } - - if (input.width) { - var ctx = this.canvas.getContext("2d"); - - ctx.drawImage( - input, - -this.properties["x"], - -this.properties["y"], - input.width * this.properties["scale"], - input.height * this.properties["scale"] - ); - this.setOutputData(0, this.canvas); - } else { - this.setOutputData(0, null); - } - }; - - ImageCrop.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } - if (this.canvas) { - ctx.drawImage( - this.canvas, - 0, - 0, - this.canvas.width, - this.canvas.height, - 0, - 0, - this.size[0], - this.size[1] - ); - } - }; - - ImageCrop.prototype.onPropertyChanged = function(name, value) { - this.properties[name] = value; - - if (name == "scale") { - this.properties[name] = parseFloat(value); - if (this.properties[name] == 0) { - console.error("Error in scale"); - this.properties[name] = 1.0; - } - } else { - this.properties[name] = parseInt(value); - } - - this.createCanvas(); - - return true; - }; - - LiteGraph.registerNodeType("graphics/cropImage", ImageCrop); - - //CANVAS stuff - - function CanvasNode() { - this.addInput("clear", LiteGraph.ACTION); - this.addOutput("", "canvas"); - this.properties = { width: 512, height: 512, autoclear: true }; - - this.canvas = document.createElement("canvas"); - this.ctx = this.canvas.getContext("2d"); - } - - CanvasNode.title = "Canvas"; - CanvasNode.desc = "Canvas to render stuff"; - - CanvasNode.prototype.onExecute = function() { - var canvas = this.canvas; - var w = this.properties.width | 0; - var h = this.properties.height | 0; - if (canvas.width != w) { - canvas.width = w; - } - if (canvas.height != h) { - canvas.height = h; - } - - if (this.properties.autoclear) { - this.ctx.clearRect(0, 0, canvas.width, canvas.height); - } - this.setOutputData(0, canvas); - }; - - CanvasNode.prototype.onAction = function(action, param) { - if (action == "clear") { - this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); - } - }; - - LiteGraph.registerNodeType("graphics/canvas", CanvasNode); - - function DrawImageNode() { - this.addInput("canvas", "canvas"); - this.addInput("img", "image,canvas"); - this.addInput("x", "number"); - this.addInput("y", "number"); - this.properties = { x: 0, y: 0, opacity: 1 }; - } - - DrawImageNode.title = "DrawImage"; - DrawImageNode.desc = "Draws image into a canvas"; - - DrawImageNode.prototype.onExecute = function() { - var canvas = this.getInputData(0); - if (!canvas) { - return; - } - - var img = this.getInputOrProperty("img"); - if (!img) { - return; - } - - var x = this.getInputOrProperty("x"); - var y = this.getInputOrProperty("y"); - var ctx = canvas.getContext("2d"); - ctx.drawImage(img, x, y); - }; - - LiteGraph.registerNodeType("graphics/drawImage", DrawImageNode); - - function DrawRectangleNode() { - this.addInput("canvas", "canvas"); - this.addInput("x", "number"); - this.addInput("y", "number"); - this.addInput("w", "number"); - this.addInput("h", "number"); - this.properties = { - x: 0, - y: 0, - w: 10, - h: 10, - color: "white", - opacity: 1 - }; - } - - DrawRectangleNode.title = "DrawRectangle"; - DrawRectangleNode.desc = "Draws rectangle in canvas"; - - DrawRectangleNode.prototype.onExecute = function() { - var canvas = this.getInputData(0); - if (!canvas) { - return; - } - - var x = this.getInputOrProperty("x"); - var y = this.getInputOrProperty("y"); - var w = this.getInputOrProperty("w"); - var h = this.getInputOrProperty("h"); - var ctx = canvas.getContext("2d"); - ctx.fillRect(x, y, w, h); - }; - - LiteGraph.registerNodeType("graphics/drawRectangle", DrawRectangleNode); - - function ImageVideo() { - this.addInput("t", "number"); - this.addOutputs([["frame", "image"], ["t", "number"], ["d", "number"]]); - this.properties = { url: "", use_proxy: true }; - } - - ImageVideo.title = "Video"; - ImageVideo.desc = "Video playback"; - ImageVideo.widgets = [ - { name: "play", text: "PLAY", type: "minibutton" }, - { name: "stop", text: "STOP", type: "minibutton" }, - { name: "demo", text: "Demo video", type: "button" }, - { name: "mute", text: "Mute video", type: "button" } - ]; - - ImageVideo.prototype.onExecute = function() { - if (!this.properties.url) { - return; - } - - if (this.properties.url != this._video_url) { - this.loadVideo(this.properties.url); - } - - if (!this._video || this._video.width == 0) { - return; - } - - var t = this.getInputData(0); - if (t && t >= 0 && t <= 1.0) { - this._video.currentTime = t * this._video.duration; - this._video.pause(); - } - - this._video.dirty = true; - this.setOutputData(0, this._video); - this.setOutputData(1, this._video.currentTime); - this.setOutputData(2, this._video.duration); - this.setDirtyCanvas(true); - }; - - ImageVideo.prototype.onStart = function() { - this.play(); - }; - - ImageVideo.prototype.onStop = function() { - this.stop(); - }; - - ImageVideo.prototype.loadVideo = function(url) { - this._video_url = url; - - var pos = url.substr(0,10).indexOf(":"); - var protocol = ""; - if(pos != -1) - protocol = url.substr(0,pos); - - var host = ""; - if(protocol) - { - host = url.substr(0,url.indexOf("/",protocol.length + 3)); - host = host.substr(protocol.length+3); - } - - if ( - this.properties.use_proxy && - protocol && - LiteGraph.proxy && - host != location.host - ) { - url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); - } - - this._video = document.createElement("video"); - this._video.src = url; - this._video.type = "type=video/mp4"; - - this._video.muted = true; - this._video.autoplay = true; - - var that = this; - this._video.addEventListener("loadedmetadata", function(e) { - //onload - console.log("Duration: " + this.duration + " seconds"); - console.log("Size: " + this.videoWidth + "," + this.videoHeight); - that.setDirtyCanvas(true); - this.width = this.videoWidth; - this.height = this.videoHeight; - }); - this._video.addEventListener("progress", function(e) { - //onload - console.log("video loading..."); - }); - this._video.addEventListener("error", function(e) { - console.error("Error loading video: " + this.src); - if (this.error) { - switch (this.error.code) { - case this.error.MEDIA_ERR_ABORTED: - console.error("You stopped the video."); - break; - case this.error.MEDIA_ERR_NETWORK: - console.error("Network error - please try again later."); - break; - case this.error.MEDIA_ERR_DECODE: - console.error("Video is broken.."); - break; - case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED: - console.error("Sorry, your browser can't play this video."); - break; - } - } - }); - - this._video.addEventListener("ended", function(e) { - console.log("Video Ended."); - this.play(); //loop - }); - - //document.body.appendChild(this.video); - }; - - ImageVideo.prototype.onPropertyChanged = function(name, value) { - this.properties[name] = value; - if (name == "url" && value != "") { - this.loadVideo(value); - } - - return true; - }; - - ImageVideo.prototype.play = function() { - if (this._video && this._video.videoWidth ) { //is loaded - this._video.play(); - } - }; - - ImageVideo.prototype.playPause = function() { - if (!this._video) { - return; - } - if (this._video.paused) { - this.play(); - } else { - this.pause(); - } - }; - - ImageVideo.prototype.stop = function() { - if (!this._video) { - return; - } - this._video.pause(); - this._video.currentTime = 0; - }; - - ImageVideo.prototype.pause = function() { - if (!this._video) { - return; - } - console.log("Video paused"); - this._video.pause(); - }; - - ImageVideo.prototype.onWidget = function(e, widget) { - /* - if(widget.name == "demo") - { - this.loadVideo(); - } - else if(widget.name == "play") - { - if(this._video) - this.playPause(); - } - if(widget.name == "stop") - { - this.stop(); - } - else if(widget.name == "mute") - { - if(this._video) - this._video.muted = !this._video.muted; - } - */ - }; - - LiteGraph.registerNodeType("graphics/video", ImageVideo); - - // Texture Webcam ***************************************** - function ImageWebcam() { - this.addOutput("Webcam", "image"); - this.properties = { facingMode: "user" }; - this.boxcolor = "black"; - this.frame = 0; - } - - ImageWebcam.title = "Webcam"; - ImageWebcam.desc = "Webcam image"; - ImageWebcam.is_webcam_open = false; - - ImageWebcam.prototype.openStream = function() { - if (!navigator.getUserMedia) { - //console.log('getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags'); - return; - } - - this._waiting_confirmation = true; - - // Not showing vendor prefixes. - var constraints = { - audio: false, - video: { facingMode: this.properties.facingMode } - }; - navigator.mediaDevices - .getUserMedia(constraints) - .then(this.streamReady.bind(this)) - .catch(onFailSoHard); - - var that = this; - function onFailSoHard(e) { - console.log("Webcam rejected", e); - that._webcam_stream = false; - ImageWebcam.is_webcam_open = false; - that.boxcolor = "red"; - that.trigger("stream_error"); - } - }; - - ImageWebcam.prototype.closeStream = function() { - if (this._webcam_stream) { - var tracks = this._webcam_stream.getTracks(); - if (tracks.length) { - for (var i = 0; i < tracks.length; ++i) { - tracks[i].stop(); - } - } - ImageWebcam.is_webcam_open = false; - this._webcam_stream = null; - this._video = null; - this.boxcolor = "black"; - this.trigger("stream_closed"); - } - }; - - ImageWebcam.prototype.onPropertyChanged = function(name, value) { - if (name == "facingMode") { - this.properties.facingMode = value; - this.closeStream(); - this.openStream(); - } - }; - - ImageWebcam.prototype.onRemoved = function() { - this.closeStream(); - }; - - ImageWebcam.prototype.streamReady = function(localMediaStream) { - this._webcam_stream = localMediaStream; - //this._waiting_confirmation = false; - this.boxcolor = "green"; - - var video = this._video; - if (!video) { - video = document.createElement("video"); - video.autoplay = true; - video.srcObject = localMediaStream; - this._video = video; - //document.body.appendChild( video ); //debug - //when video info is loaded (size and so) - video.onloadedmetadata = function(e) { - // Ready to go. Do some stuff. - console.log(e); - ImageWebcam.is_webcam_open = true; - }; - } - - this.trigger("stream_ready", video); - }; - - ImageWebcam.prototype.onExecute = function() { - if (this._webcam_stream == null && !this._waiting_confirmation) { - this.openStream(); - } - - if (!this._video || !this._video.videoWidth) { - return; - } - - this._video.frame = ++this.frame; - this._video.width = this._video.videoWidth; - this._video.height = this._video.videoHeight; - this.setOutputData(0, this._video); - for (var i = 1; i < this.outputs.length; ++i) { - if (!this.outputs[i]) { - continue; - } - switch (this.outputs[i].name) { - case "width": - this.setOutputData(i, this._video.videoWidth); - break; - case "height": - this.setOutputData(i, this._video.videoHeight); - break; - } - } - }; - - ImageWebcam.prototype.getExtraMenuOptions = function(graphcanvas) { - var that = this; - var txt = !that.properties.show ? "Show Frame" : "Hide Frame"; - return [ - { - content: txt, - callback: function() { - that.properties.show = !that.properties.show; - } - } - ]; - }; - - ImageWebcam.prototype.onDrawBackground = function(ctx) { - if ( - this.flags.collapsed || - this.size[1] <= 20 || - !this.properties.show - ) { - return; - } - - if (!this._video) { - return; - } - - //render to graph canvas - ctx.save(); - ctx.drawImage(this._video, 0, 0, this.size[0], this.size[1]); - ctx.restore(); - }; - - ImageWebcam.prototype.onGetOutputs = function() { - return [ - ["width", "number"], - ["height", "number"], - ["stream_ready", LiteGraph.EVENT], - ["stream_closed", LiteGraph.EVENT], - ["stream_error", LiteGraph.EVENT] - ]; - }; - - LiteGraph.registerNodeType("graphics/webcam", ImageWebcam); -})(this); - -(function(global) { - var LiteGraph = global.LiteGraph; - var LGraphCanvas = global.LGraphCanvas; - - //Works with Litegl.js to create WebGL nodes - global.LGraphTexture = null; - - if (typeof GL == "undefined") - return; - - LGraphCanvas.link_type_colors["Texture"] = "#987"; - - function LGraphTexture() { - this.addOutput("tex", "Texture"); - this.addOutput("name", "string"); - this.properties = { name: "", filter: true }; - this.size = [ - LGraphTexture.image_preview_size, - LGraphTexture.image_preview_size - ]; - } - - global.LGraphTexture = LGraphTexture; - - LGraphTexture.title = "Texture"; - LGraphTexture.desc = "Texture"; - LGraphTexture.widgets_info = { - name: { widget: "texture" }, - filter: { widget: "checkbox" } - }; - - //REPLACE THIS TO INTEGRATE WITH YOUR FRAMEWORK - LGraphTexture.loadTextureCallback = null; //function in charge of loading textures when not present in the container - LGraphTexture.image_preview_size = 256; - - //flags to choose output texture type - LGraphTexture.PASS_THROUGH = 1; //do not apply FX - LGraphTexture.COPY = 2; //create new texture with the same properties as the origin texture - LGraphTexture.LOW = 3; //create new texture with low precision (byte) - LGraphTexture.HIGH = 4; //create new texture with high precision (half-float) - LGraphTexture.REUSE = 5; //reuse input texture - LGraphTexture.DEFAULT = 2; - - LGraphTexture.MODE_VALUES = { - "pass through": LGraphTexture.PASS_THROUGH, - copy: LGraphTexture.COPY, - low: LGraphTexture.LOW, - high: LGraphTexture.HIGH, - reuse: LGraphTexture.REUSE, - default: LGraphTexture.DEFAULT - }; - - //returns the container where all the loaded textures are stored (overwrite if you have a Resources Manager) - LGraphTexture.getTexturesContainer = function() { - return gl.textures; - }; - - //process the loading of a texture (overwrite it if you have a Resources Manager) - LGraphTexture.loadTexture = function(name, options) { - options = options || {}; - var url = name; - if (url.substr(0, 7) == "http://") { - if (LiteGraph.proxy) { - //proxy external files - url = LiteGraph.proxy + url.substr(7); - } - } - - var container = LGraphTexture.getTexturesContainer(); - var tex = (container[name] = GL.Texture.fromURL(url, options)); - return tex; - }; - - LGraphTexture.getTexture = function(name) { - var container = this.getTexturesContainer(); - - if (!container) { - throw "Cannot load texture, container of textures not found"; - } - - var tex = container[name]; - if (!tex && name && name[0] != ":") { - return this.loadTexture(name); - } - - return tex; - }; - - //used to compute the appropiate output texture - LGraphTexture.getTargetTexture = function(origin, target, mode) { - if (!origin) { - throw "LGraphTexture.getTargetTexture expects a reference texture"; - } - - var tex_type = null; - - switch (mode) { - case LGraphTexture.LOW: - tex_type = gl.UNSIGNED_BYTE; - break; - case LGraphTexture.HIGH: - tex_type = gl.HIGH_PRECISION_FORMAT; - break; - case LGraphTexture.REUSE: - return origin; - break; - case LGraphTexture.COPY: - default: - tex_type = origin ? origin.type : gl.UNSIGNED_BYTE; - break; - } - - if ( - !target || - target.width != origin.width || - target.height != origin.height || - target.type != tex_type - ) { - target = new GL.Texture(origin.width, origin.height, { - type: tex_type, - format: gl.RGBA, - filter: gl.LINEAR - }); - } - - return target; - }; - - LGraphTexture.getTextureType = function(precision, ref_texture) { - var type = ref_texture ? ref_texture.type : gl.UNSIGNED_BYTE; - switch (precision) { - case LGraphTexture.HIGH: - type = gl.HIGH_PRECISION_FORMAT; - break; - case LGraphTexture.LOW: - type = gl.UNSIGNED_BYTE; - break; - //no default - } - return type; - }; - - LGraphTexture.getWhiteTexture = function() { - if (this._white_texture) { - return this._white_texture; - } - var texture = (this._white_texture = GL.Texture.fromMemory( - 1, - 1, - [255, 255, 255, 255], - { format: gl.RGBA, wrap: gl.REPEAT, filter: gl.NEAREST } - )); - return texture; - }; - - LGraphTexture.getNoiseTexture = function() { - if (this._noise_texture) { - return this._noise_texture; - } - - var noise = new Uint8Array(512 * 512 * 4); - for (var i = 0; i < 512 * 512 * 4; ++i) { - noise[i] = Math.random() * 255; - } - - var texture = GL.Texture.fromMemory(512, 512, noise, { - format: gl.RGBA, - wrap: gl.REPEAT, - filter: gl.NEAREST - }); - this._noise_texture = texture; - return texture; - }; - - LGraphTexture.prototype.onDropFile = function(data, filename, file) { - if (!data) { - this._drop_texture = null; - this.properties.name = ""; - } else { - var texture = null; - if (typeof data == "string") { - texture = GL.Texture.fromURL(data); - } else if (filename.toLowerCase().indexOf(".dds") != -1) { - texture = GL.Texture.fromDDSInMemory(data); - } else { - var blob = new Blob([file]); - var url = URL.createObjectURL(blob); - texture = GL.Texture.fromURL(url); - } - - this._drop_texture = texture; - this.properties.name = filename; - } - }; - - LGraphTexture.prototype.getExtraMenuOptions = function(graphcanvas) { - var that = this; - if (!this._drop_texture) { - return; - } - return [ - { - content: "Clear", - callback: function() { - that._drop_texture = null; - that.properties.name = ""; - } - } - ]; - }; - - LGraphTexture.prototype.onExecute = function() { - var tex = null; - if (this.isOutputConnected(1)) { - tex = this.getInputData(0); - } - - if (!tex && this._drop_texture) { - tex = this._drop_texture; - } - - if (!tex && this.properties.name) { - tex = LGraphTexture.getTexture(this.properties.name); - } - - if (!tex) { - this.setOutputData( 0, null ); - this.setOutputData( 1, "" ); - return; - } - - this._last_tex = tex; - - if (this.properties.filter === false) { - tex.setParameter(gl.TEXTURE_MAG_FILTER, gl.NEAREST); - } else { - tex.setParameter(gl.TEXTURE_MAG_FILTER, gl.LINEAR); - } - - this.setOutputData( 0, tex ); - this.setOutputData( 1, tex.fullpath || tex.filename ); - - for (var i = 2; i < this.outputs.length; i++) { - var output = this.outputs[i]; - if (!output) { - continue; - } - var v = null; - if (output.name == "width") { - v = tex.width; - } else if (output.name == "height") { - v = tex.height; - } else if (output.name == "aspect") { - v = tex.width / tex.height; - } - this.setOutputData(i, v); - } - }; - - LGraphTexture.prototype.onResourceRenamed = function( - old_name, - new_name - ) { - if (this.properties.name == old_name) { - this.properties.name = new_name; - } - }; - - LGraphTexture.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed || this.size[1] <= 20) { - return; - } - - if (this._drop_texture && ctx.webgl) { - ctx.drawImage( - this._drop_texture, - 0, - 0, - this.size[0], - this.size[1] - ); - //this._drop_texture.renderQuad(this.pos[0],this.pos[1],this.size[0],this.size[1]); - return; - } - - //Different texture? then get it from the GPU - if (this._last_preview_tex != this._last_tex) { - if (ctx.webgl) { - this._canvas = this._last_tex; - } else { - var tex_canvas = LGraphTexture.generateLowResTexturePreview( - this._last_tex - ); - if (!tex_canvas) { - return; - } - - this._last_preview_tex = this._last_tex; - this._canvas = cloneCanvas(tex_canvas); - } - } - - if (!this._canvas) { - return; - } - - //render to graph canvas - ctx.save(); - if (!ctx.webgl) { - //reverse image - ctx.translate(0, this.size[1]); - ctx.scale(1, -1); - } - ctx.drawImage(this._canvas, 0, 0, this.size[0], this.size[1]); - ctx.restore(); - }; - - //very slow, used at your own risk - LGraphTexture.generateLowResTexturePreview = function(tex) { - if (!tex) { - return null; - } - - var size = LGraphTexture.image_preview_size; - var temp_tex = tex; - - if (tex.format == gl.DEPTH_COMPONENT) { - return null; - } //cannot generate from depth - - //Generate low-level version in the GPU to speed up - if (tex.width > size || tex.height > size) { - temp_tex = this._preview_temp_tex; - if (!this._preview_temp_tex) { - temp_tex = new GL.Texture(size, size, { - minFilter: gl.NEAREST - }); - this._preview_temp_tex = temp_tex; - } - - //copy - tex.copyTo(temp_tex); - tex = temp_tex; - } - - //create intermediate canvas with lowquality version - var tex_canvas = this._preview_canvas; - if (!tex_canvas) { - tex_canvas = createCanvas(size, size); - this._preview_canvas = tex_canvas; - } - - if (temp_tex) { - temp_tex.toCanvas(tex_canvas); - } - return tex_canvas; - }; - - LGraphTexture.prototype.getResources = function(res) { - if(this.properties.name) - res[this.properties.name] = GL.Texture; - return res; - }; - - LGraphTexture.prototype.onGetInputs = function() { - return [["in", "Texture"]]; - }; - - LGraphTexture.prototype.onGetOutputs = function() { - return [ - ["width", "number"], - ["height", "number"], - ["aspect", "number"] - ]; - }; - - //used to replace shader code - LGraphTexture.replaceCode = function( code, context ) - { - return code.replace(/\{\{[a-zA-Z0-9_]*\}\}/g, function(v){ - v = v.replace( /[\{\}]/g, "" ); - return context[v] || ""; - }); - } - - LiteGraph.registerNodeType("texture/texture", LGraphTexture); - - //************************** - function LGraphTexturePreview() { - this.addInput("Texture", "Texture"); - this.properties = { flipY: false }; - this.size = [ - LGraphTexture.image_preview_size, - LGraphTexture.image_preview_size - ]; - } - - LGraphTexturePreview.title = "Preview"; - LGraphTexturePreview.desc = "Show a texture in the graph canvas"; - LGraphTexturePreview.allow_preview = false; - - LGraphTexturePreview.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } - - if (!ctx.webgl && !LGraphTexturePreview.allow_preview) { - return; - } //not working well - - var tex = this.getInputData(0); - if (!tex) { - return; - } - - var tex_canvas = null; - - if (!tex.handle && ctx.webgl) { - tex_canvas = tex; - } else { - tex_canvas = LGraphTexture.generateLowResTexturePreview(tex); - } - - //render to graph canvas - ctx.save(); - if (this.properties.flipY) { - ctx.translate(0, this.size[1]); - ctx.scale(1, -1); - } - ctx.drawImage(tex_canvas, 0, 0, this.size[0], this.size[1]); - ctx.restore(); - }; - - LiteGraph.registerNodeType("texture/preview", LGraphTexturePreview); - - //************************************** - - function LGraphTextureSave() { - this.addInput("Texture", "Texture"); - this.addOutput("tex", "Texture"); - this.addOutput("name", "string"); - this.properties = { name: "", generate_mipmaps: false }; - } - - LGraphTextureSave.title = "Save"; - LGraphTextureSave.desc = "Save a texture in the repository"; - - LGraphTextureSave.prototype.getPreviewTexture = function() - { - return this._texture; - } - - LGraphTextureSave.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if (this.properties.generate_mipmaps) { - tex.bind(0); - tex.setParameter( gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR ); - gl.generateMipmap(tex.texture_type); - tex.unbind(0); - } - - if (this.properties.name) { - //for cases where we want to perform something when storing it - if (LGraphTexture.storeTexture) { - LGraphTexture.storeTexture(this.properties.name, tex); - } else { - var container = LGraphTexture.getTexturesContainer(); - container[this.properties.name] = tex; - } - } - - this._texture = tex; - this.setOutputData(0, tex); - this.setOutputData(1, this.properties.name); - }; - - LiteGraph.registerNodeType("texture/save", LGraphTextureSave); - - //**************************************************** - - function LGraphTextureOperation() { - this.addInput("Texture", "Texture"); - this.addInput("TextureB", "Texture"); - this.addInput("value", "number"); - this.addOutput("Texture", "Texture"); - this.help = "

pixelcode must be vec3, uvcode must be vec2, is optional

\ -

uv: tex. coords

color: texture colorB: textureB

time: scene time value: input value

For multiline you must type: result = ...

"; - - this.properties = { - value: 1, - pixelcode: "color + colorB * value", - uvcode: "", - precision: LGraphTexture.DEFAULT - }; - - this.has_error = false; - } - - LGraphTextureOperation.widgets_info = { - uvcode: { widget: "code" }, - pixelcode: { widget: "code" }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureOperation.title = "Operation"; - LGraphTextureOperation.desc = "Texture shader operation"; - - LGraphTextureOperation.presets = {}; - - LGraphTextureOperation.prototype.getExtraMenuOptions = function( - graphcanvas - ) { - var that = this; - var txt = !that.properties.show ? "Show Texture" : "Hide Texture"; - return [ - { - content: txt, - callback: function() { - that.properties.show = !that.properties.show; - } - } - ]; - }; - - LGraphTextureOperation.prototype.onPropertyChanged = function() - { - this.has_error = false; - } - - LGraphTextureOperation.prototype.onDrawBackground = function(ctx) { - if ( - this.flags.collapsed || - this.size[1] <= 20 || - !this.properties.show - ) { - return; - } - - if (!this._tex) { - return; - } - - //only works if using a webgl renderer - if (this._tex.gl != ctx) { - return; - } - - //render to graph canvas - ctx.save(); - ctx.drawImage(this._tex, 0, 0, this.size[0], this.size[1]); - ctx.restore(); - }; - - LGraphTextureOperation.prototype.onExecute = function() { - var tex = this.getInputData(0); - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, tex); - return; - } - - var texB = this.getInputData(1); - - if (!this.properties.uvcode && !this.properties.pixelcode) { - return; - } - - var width = 512; - var height = 512; - if (tex) { - width = tex.width; - height = tex.height; - } else if (texB) { - width = texB.width; - height = texB.height; - } - - if(!texB) - texB = GL.Texture.getWhiteTexture(); - - var type = LGraphTexture.getTextureType( this.properties.precision, tex ); - - if (!tex && !this._tex) { - this._tex = new GL.Texture(width, height, { type: type, format: gl.RGBA, filter: gl.LINEAR }); - } else { - this._tex = LGraphTexture.getTargetTexture( tex || this._tex, this._tex, this.properties.precision ); - } - - var uvcode = ""; - if (this.properties.uvcode) { - uvcode = "uv = " + this.properties.uvcode; - if (this.properties.uvcode.indexOf(";") != -1) { - //there are line breaks, means multiline code - uvcode = this.properties.uvcode; - } - } - - var pixelcode = ""; - if (this.properties.pixelcode) { - pixelcode = "result = " + this.properties.pixelcode; - if (this.properties.pixelcode.indexOf(";") != -1) { - //there are line breaks, means multiline code - pixelcode = this.properties.pixelcode; - } - } - - var shader = this._shader; - - if ( !this.has_error && (!shader || this._shader_code != uvcode + "|" + pixelcode) ) { - - var final_pixel_code = LGraphTexture.replaceCode( LGraphTextureOperation.pixel_shader, { UV_CODE:uvcode, PIXEL_CODE:pixelcode }); - - try { - shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, final_pixel_code ); - this.boxcolor = "#00FF00"; - } catch (err) { - //console.log("Error compiling shader: ", err, final_pixel_code ); - GL.Shader.dumpErrorToConsole(err,Shader.SCREEN_VERTEX_SHADER, final_pixel_code); - this.boxcolor = "#FF0000"; - this.has_error = true; - return; - } - this._shader = shader; - this._shader_code = uvcode + "|" + pixelcode; - } - - if(!this._shader) - return; - - var value = this.getInputData(2); - if (value != null) { - this.properties.value = value; - } else { - value = parseFloat(this.properties.value); - } - - var time = this.graph.getTime(); - - this._tex.drawTo(function() { - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.CULL_FACE); - gl.disable(gl.BLEND); - if (tex) { - tex.bind(0); - } - if (texB) { - texB.bind(1); - } - var mesh = Mesh.getScreenQuad(); - shader - .uniforms({ - u_texture: 0, - u_textureB: 1, - value: value, - texSize: [width, height], - time: time - }) - .draw(mesh); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureOperation.pixel_shader = - "precision highp float;\n\ - \n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - varying vec2 v_coord;\n\ - uniform vec2 texSize;\n\ - uniform float time;\n\ - uniform float value;\n\ - \n\ - void main() {\n\ - vec2 uv = v_coord;\n\ - {{UV_CODE}};\n\ - vec4 color4 = texture2D(u_texture, uv);\n\ - vec3 color = color4.rgb;\n\ - vec4 color4B = texture2D(u_textureB, uv);\n\ - vec3 colorB = color4B.rgb;\n\ - vec3 result = color;\n\ - float alpha = 1.0;\n\ - {{PIXEL_CODE}};\n\ - gl_FragColor = vec4(result, alpha);\n\ - }\n\ - "; - - LGraphTextureOperation.registerPreset = function ( name, code ) - { - LGraphTextureOperation.presets[name] = code; - } - - LGraphTextureOperation.registerPreset("",""); - LGraphTextureOperation.registerPreset("bypass","color"); - LGraphTextureOperation.registerPreset("add","color + colorB * value"); - LGraphTextureOperation.registerPreset("substract","(color - colorB) * value"); - LGraphTextureOperation.registerPreset("mate","mix( color, colorB, color4B.a * value)"); - LGraphTextureOperation.registerPreset("invert","vec3(1.0) - color"); - LGraphTextureOperation.registerPreset("multiply","color * colorB * value"); - LGraphTextureOperation.registerPreset("divide","(color / colorB) / value"); - LGraphTextureOperation.registerPreset("difference","abs(color - colorB) * value"); - LGraphTextureOperation.registerPreset("max","max(color, colorB) * value"); - LGraphTextureOperation.registerPreset("min","min(color, colorB) * value"); - LGraphTextureOperation.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"); - LGraphTextureOperation.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"); - LGraphTextureOperation.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"); - LGraphTextureOperation.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"); - - //webglstudio stuff... - LGraphTextureOperation.prototype.onInspect = function(widgets) - { - var that = this; - widgets.addCombo("Presets","",{ values: Object.keys(LGraphTextureOperation.presets), callback: function(v){ - var code = LGraphTextureOperation.presets[v]; - if(!code) - return; - that.setProperty("pixelcode",code); - that.title = v; - widgets.refresh(); - }}); - } - - LiteGraph.registerNodeType("texture/operation", LGraphTextureOperation); - - //**************************************************** - - function LGraphTextureShader() { - this.addOutput("out", "Texture"); - this.properties = { - code: "", - u_value: 1, - u_color: [1,1,1,1], - width: 512, - height: 512, - precision: LGraphTexture.DEFAULT - }; - - this.properties.code = LGraphTextureShader.pixel_shader; - this._uniforms = { u_value: 1, u_color: vec4.create(), in_texture: 0, texSize: vec2.create(), time: 0 }; - } - - LGraphTextureShader.title = "Shader"; - LGraphTextureShader.desc = "Texture shader"; - LGraphTextureShader.widgets_info = { - code: { type: "code", lang: "glsl" }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureShader.prototype.onPropertyChanged = function( - name, - value - ) { - if (name != "code") { - return; - } - - var shader = this.getShader(); - if (!shader) { - return; - } - - //update connections - var uniforms = shader.uniformInfo; - - //remove deprecated slots - if (this.inputs) { - var already = {}; - for (var i = 0; i < this.inputs.length; ++i) { - var info = this.getInputInfo(i); - if (!info) { - continue; - } - - if (uniforms[info.name] && !already[info.name]) { - already[info.name] = true; - continue; - } - this.removeInput(i); - i--; - } - } - - //update existing ones - for (var i in uniforms) { - var info = shader.uniformInfo[i]; - if (info.loc === null) { - continue; - } //is an attribute, not a uniform - if (i == "time") { - //default one - continue; - } - - var type = "number"; - if (this._shader.samplers[i]) { - type = "texture"; - } else { - switch (info.size) { - case 1: - type = "number"; - break; - case 2: - type = "vec2"; - break; - case 3: - type = "vec3"; - break; - case 4: - type = "vec4"; - break; - case 9: - type = "mat3"; - break; - case 16: - type = "mat4"; - break; - default: - continue; - } - } - - var slot = this.findInputSlot(i); - if (slot == -1) { - this.addInput(i, type); - continue; - } - - var input_info = this.getInputInfo(slot); - if (!input_info) { - this.addInput(i, type); - } else { - if (input_info.type == type) { - continue; - } - this.removeInput(slot, type); - this.addInput(i, type); - } - } - }; - - LGraphTextureShader.prototype.getShader = function() { - //replug - if (this._shader && this._shader_code == this.properties.code) { - return this._shader; - } - - this._shader_code = this.properties.code; - this._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, this.properties.code ); - if (!this._shader) { - this.boxcolor = "red"; - return null; - } else { - this.boxcolor = "green"; - } - return this._shader; - }; - - LGraphTextureShader.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var shader = this.getShader(); - if (!shader) { - return; - } - - var tex_slot = 0; - var in_tex = null; - - //set uniforms - if(this.inputs) - for (var i = 0; i < this.inputs.length; ++i) { - var info = this.getInputInfo(i); - var data = this.getInputData(i); - if (data == null) { - continue; - } - - if (data.constructor === GL.Texture) { - data.bind(tex_slot); - if (!in_tex) { - in_tex = data; - } - data = tex_slot; - tex_slot++; - } - shader.setUniform(info.name, data); //data is tex_slot - } - - var uniforms = this._uniforms; - var type = LGraphTexture.getTextureType( this.properties.precision, in_tex ); - - //render to texture - var w = this.properties.width | 0; - var h = this.properties.height | 0; - if (w == 0) { - w = in_tex ? in_tex.width : gl.canvas.width; - } - if (h == 0) { - h = in_tex ? in_tex.height : gl.canvas.height; - } - uniforms.texSize[0] = w; - uniforms.texSize[1] = h; - uniforms.time = this.graph.getTime(); - uniforms.u_value = this.properties.u_value; - uniforms.u_color.set( this.properties.u_color ); - - if ( !this._tex || this._tex.type != type || this._tex.width != w || this._tex.height != h ) { - this._tex = new GL.Texture(w, h, { type: type, format: gl.RGBA, filter: gl.LINEAR }); - } - var tex = this._tex; - tex.drawTo(function() { - shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad()); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureShader.pixel_shader = -"precision highp float;\n\ -\n\ -varying vec2 v_coord;\n\ -uniform float time; //time in seconds\n\ -uniform vec2 texSize; //tex resolution\n\ -uniform float u_value;\n\ -uniform vec4 u_color;\n\n\ -void main() {\n\ - vec2 uv = v_coord;\n\ - vec3 color = vec3(0.0);\n\ - //your code here\n\ - color.xy=uv;\n\n\ - gl_FragColor = vec4(color, 1.0);\n\ -}\n\ -"; - - LiteGraph.registerNodeType("texture/shader", LGraphTextureShader); - - // Texture Scale Offset - - function LGraphTextureScaleOffset() { - this.addInput("in", "Texture"); - this.addInput("scale", "vec2"); - this.addInput("offset", "vec2"); - this.addOutput("out", "Texture"); - this.properties = { - offset: vec2.fromValues(0, 0), - scale: vec2.fromValues(1, 1), - precision: LGraphTexture.DEFAULT - }; - } - - LGraphTextureScaleOffset.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureScaleOffset.title = "Scale/Offset"; - LGraphTextureScaleOffset.desc = "Applies an scaling and offseting"; - - LGraphTextureScaleOffset.prototype.onExecute = function() { - var tex = this.getInputData(0); - - if (!this.isOutputConnected(0) || !tex) { - return; - } //saves work - - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, tex); - return; - } - - var width = tex.width; - var height = tex.height; - var type = this.precision === LGraphTexture.LOW ? gl.UNSIGNED_BYTE : gl.HIGH_PRECISION_FORMAT; - if (this.precision === LGraphTexture.DEFAULT) { - type = tex.type; - } - - if ( - !this._tex || - this._tex.width != width || - this._tex.height != height || - this._tex.type != type - ) { - this._tex = new GL.Texture(width, height, { - type: type, - format: gl.RGBA, - filter: gl.LINEAR - }); - } - - var shader = this._shader; - - if (!shader) { - shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureScaleOffset.pixel_shader - ); - } - - var scale = this.getInputData(1); - if (scale) { - this.properties.scale[0] = scale[0]; - this.properties.scale[1] = scale[1]; - } else { - scale = this.properties.scale; - } - - var offset = this.getInputData(2); - if (offset) { - this.properties.offset[0] = offset[0]; - this.properties.offset[1] = offset[1]; - } else { - offset = this.properties.offset; - } - - this._tex.drawTo(function() { - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.CULL_FACE); - gl.disable(gl.BLEND); - tex.bind(0); - var mesh = Mesh.getScreenQuad(); - shader - .uniforms({ - u_texture: 0, - u_scale: scale, - u_offset: offset - }) - .draw(mesh); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureScaleOffset.pixel_shader = - "precision highp float;\n\ - \n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - varying vec2 v_coord;\n\ - uniform vec2 u_scale;\n\ - uniform vec2 u_offset;\n\ - \n\ - void main() {\n\ - vec2 uv = v_coord;\n\ - uv = uv / u_scale - u_offset;\n\ - gl_FragColor = texture2D(u_texture, uv);\n\ - }\n\ - "; - - LiteGraph.registerNodeType( - "texture/scaleOffset", - LGraphTextureScaleOffset - ); - - // Warp (distort a texture) ************************* - - function LGraphTextureWarp() { - this.addInput("in", "Texture"); - this.addInput("warp", "Texture"); - this.addInput("factor", "number"); - this.addOutput("out", "Texture"); - this.properties = { - factor: 0.01, - scale: [1,1], - offset: [0,0], - precision: LGraphTexture.DEFAULT - }; - - this._uniforms = { - u_texture: 0, - u_textureB: 1, - u_factor: 1, - u_scale: vec2.create(), - u_offset: vec2.create() - }; - } - - LGraphTextureWarp.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureWarp.title = "Warp"; - LGraphTextureWarp.desc = "Texture warp operation"; - - LGraphTextureWarp.prototype.onExecute = function() { - var tex = this.getInputData(0); - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, tex); - return; - } - - var texB = this.getInputData(1); - - var width = 512; - var height = 512; - var type = gl.UNSIGNED_BYTE; - if (tex) { - width = tex.width; - height = tex.height; - type = tex.type; - } else if (texB) { - width = texB.width; - height = texB.height; - type = texB.type; - } - - if (!tex && !this._tex) { - this._tex = new GL.Texture(width, height, { - type: - this.precision === LGraphTexture.LOW - ? gl.UNSIGNED_BYTE - : gl.HIGH_PRECISION_FORMAT, - format: gl.RGBA, - filter: gl.LINEAR - }); - } else { - this._tex = LGraphTexture.getTargetTexture( - tex || this._tex, - this._tex, - this.properties.precision - ); - } - - var shader = this._shader; - - if (!shader) { - shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureWarp.pixel_shader - ); - } - - var factor = this.getInputData(2); - if (factor != null) { - this.properties.factor = factor; - } else { - factor = parseFloat(this.properties.factor); - } - var uniforms = this._uniforms; - uniforms.u_factor = factor; - uniforms.u_scale.set( this.properties.scale ); - uniforms.u_offset.set( this.properties.offset ); - - this._tex.drawTo(function() { - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.CULL_FACE); - gl.disable(gl.BLEND); - if (tex) { - tex.bind(0); - } - if (texB) { - texB.bind(1); - } - var mesh = Mesh.getScreenQuad(); - shader - .uniforms( uniforms ) - .draw(mesh); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureWarp.pixel_shader = - "precision highp float;\n\ - \n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - varying vec2 v_coord;\n\ - uniform float u_factor;\n\ - uniform vec2 u_scale;\n\ - uniform vec2 u_offset;\n\ - \n\ - void main() {\n\ - vec2 uv = v_coord;\n\ - uv += ( texture2D(u_textureB, uv).rg - vec2(0.5)) * u_factor * u_scale + u_offset;\n\ - gl_FragColor = texture2D(u_texture, uv);\n\ - }\n\ - "; - - LiteGraph.registerNodeType("texture/warp", LGraphTextureWarp); - - //**************************************************** - - // Texture to Viewport ***************************************** - function LGraphTextureToViewport() { - this.addInput("Texture", "Texture"); - this.properties = { - additive: false, - antialiasing: false, - filter: true, - disable_alpha: false, - gamma: 1.0, - viewport: [0,0,1,1] - }; - this.size[0] = 130; - } - - LGraphTextureToViewport.title = "to Viewport"; - LGraphTextureToViewport.desc = "Texture to viewport"; - - LGraphTextureToViewport._prev_viewport = new Float32Array(4); - LGraphTextureToViewport.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if (this.properties.disable_alpha) { - gl.disable(gl.BLEND); - } else { - gl.enable(gl.BLEND); - if (this.properties.additive) { - gl.blendFunc(gl.SRC_ALPHA, gl.ONE); - } else { - gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); - } - } - - gl.disable(gl.DEPTH_TEST); - var gamma = this.properties.gamma || 1.0; - if (this.isInputConnected(1)) { - gamma = this.getInputData(1); - } - - tex.setParameter( - gl.TEXTURE_MAG_FILTER, - this.properties.filter ? gl.LINEAR : gl.NEAREST - ); - - var old_viewport = LGraphTextureToViewport._prev_viewport; - old_viewport.set( gl.viewport_data ); - var new_view = this.properties.viewport; - gl.viewport( old_viewport[0] + old_viewport[2] * new_view[0], old_viewport[1] + old_viewport[3] * new_view[1], old_viewport[2] * new_view[2], old_viewport[3] * new_view[3] ); - var viewport = gl.getViewport(); //gl.getParameter(gl.VIEWPORT); - - if (this.properties.antialiasing) { - if (!LGraphTextureToViewport._shader) { - LGraphTextureToViewport._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureToViewport.aa_pixel_shader - ); - } - - var mesh = Mesh.getScreenQuad(); - tex.bind(0); - LGraphTextureToViewport._shader - .uniforms({ - u_texture: 0, - uViewportSize: [tex.width, tex.height], - u_igamma: 1 / gamma, - inverseVP: [1 / tex.width, 1 / tex.height] - }) - .draw(mesh); - } else { - if (gamma != 1.0) { - if (!LGraphTextureToViewport._gamma_shader) { - LGraphTextureToViewport._gamma_shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureToViewport.gamma_pixel_shader - ); - } - tex.toViewport(LGraphTextureToViewport._gamma_shader, { - u_texture: 0, - u_igamma: 1 / gamma - }); - } else { - tex.toViewport(); - } - } - - gl.viewport( old_viewport[0], old_viewport[1], old_viewport[2], old_viewport[3] ); - }; - - LGraphTextureToViewport.prototype.onGetInputs = function() { - return [["gamma", "number"]]; - }; - - LGraphTextureToViewport.aa_pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 uViewportSize;\n\ - uniform vec2 inverseVP;\n\ - uniform float u_igamma;\n\ - #define FXAA_REDUCE_MIN (1.0/ 128.0)\n\ - #define FXAA_REDUCE_MUL (1.0 / 8.0)\n\ - #define FXAA_SPAN_MAX 8.0\n\ - \n\ - /* from mitsuhiko/webgl-meincraft based on the code on geeks3d.com */\n\ - vec4 applyFXAA(sampler2D tex, vec2 fragCoord)\n\ - {\n\ - vec4 color = vec4(0.0);\n\ - /*vec2 inverseVP = vec2(1.0 / uViewportSize.x, 1.0 / uViewportSize.y);*/\n\ - vec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * inverseVP).xyz;\n\ - vec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * inverseVP).xyz;\n\ - vec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * inverseVP).xyz;\n\ - vec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * inverseVP).xyz;\n\ - vec3 rgbM = texture2D(tex, fragCoord * inverseVP).xyz;\n\ - vec3 luma = vec3(0.299, 0.587, 0.114);\n\ - float lumaNW = dot(rgbNW, luma);\n\ - float lumaNE = dot(rgbNE, luma);\n\ - float lumaSW = dot(rgbSW, luma);\n\ - float lumaSE = dot(rgbSE, luma);\n\ - float lumaM = dot(rgbM, luma);\n\ - float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n\ - float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\ - \n\ - vec2 dir;\n\ - dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n\ - dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\ - \n\ - float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\ - \n\ - float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n\ - dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * inverseVP;\n\ - \n\ - vec3 rgbA = 0.5 * (texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + \n\ - texture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz);\n\ - vec3 rgbB = rgbA * 0.5 + 0.25 * (texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + \n\ - texture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz);\n\ - \n\ - //return vec4(rgbA,1.0);\n\ - float lumaB = dot(rgbB, luma);\n\ - if ((lumaB < lumaMin) || (lumaB > lumaMax))\n\ - color = vec4(rgbA, 1.0);\n\ - else\n\ - color = vec4(rgbB, 1.0);\n\ - if(u_igamma != 1.0)\n\ - color.xyz = pow( color.xyz, vec3(u_igamma) );\n\ - return color;\n\ - }\n\ - \n\ - void main() {\n\ - gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\ - }\n\ - "; - - LGraphTextureToViewport.gamma_pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_igamma;\n\ - void main() {\n\ - vec4 color = texture2D( u_texture, v_coord);\n\ - color.xyz = pow(color.xyz, vec3(u_igamma) );\n\ - gl_FragColor = color;\n\ - }\n\ - "; - - LiteGraph.registerNodeType( - "texture/toviewport", - LGraphTextureToViewport - ); - - // Texture Copy ***************************************** - function LGraphTextureCopy() { - this.addInput("Texture", "Texture"); - this.addOutput("", "Texture"); - this.properties = { - size: 0, - generate_mipmaps: false, - precision: LGraphTexture.DEFAULT - }; - } - - LGraphTextureCopy.title = "Copy"; - LGraphTextureCopy.desc = "Copy Texture"; - LGraphTextureCopy.widgets_info = { - size: { - widget: "combo", - values: [0, 32, 64, 128, 256, 512, 1024, 2048] - }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureCopy.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex && !this._temp_texture) { - return; - } - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - //copy the texture - if (tex) { - var width = tex.width; - var height = tex.height; - - if (this.properties.size != 0) { - width = this.properties.size; - height = this.properties.size; - } - - var temp = this._temp_texture; - - var type = tex.type; - if (this.properties.precision === LGraphTexture.LOW) { - type = gl.UNSIGNED_BYTE; - } else if (this.properties.precision === LGraphTexture.HIGH) { - type = gl.HIGH_PRECISION_FORMAT; - } - - if ( - !temp || - temp.width != width || - temp.height != height || - temp.type != type - ) { - var minFilter = gl.LINEAR; - if ( - this.properties.generate_mipmaps && - isPowerOfTwo(width) && - isPowerOfTwo(height) - ) { - minFilter = gl.LINEAR_MIPMAP_LINEAR; - } - this._temp_texture = new GL.Texture(width, height, { - type: type, - format: gl.RGBA, - minFilter: minFilter, - magFilter: gl.LINEAR - }); - } - tex.copyTo(this._temp_texture); - - if (this.properties.generate_mipmaps) { - this._temp_texture.bind(0); - gl.generateMipmap(this._temp_texture.texture_type); - this._temp_texture.unbind(0); - } - } - - this.setOutputData(0, this._temp_texture); - }; - - LiteGraph.registerNodeType("texture/copy", LGraphTextureCopy); - - // Texture Downsample ***************************************** - function LGraphTextureDownsample() { - this.addInput("Texture", "Texture"); - this.addOutput("", "Texture"); - this.properties = { - iterations: 1, - generate_mipmaps: false, - precision: LGraphTexture.DEFAULT - }; - } - - LGraphTextureDownsample.title = "Downsample"; - LGraphTextureDownsample.desc = "Downsample Texture"; - LGraphTextureDownsample.widgets_info = { - iterations: { type: "number", step: 1, precision: 0, min: 0 }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureDownsample.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex && !this._temp_texture) { - return; - } - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - //we do not allow any texture different than texture 2D - if (!tex || tex.texture_type !== GL.TEXTURE_2D) { - return; - } - - if (this.properties.iterations < 1) { - this.setOutputData(0, tex); - return; - } - - var shader = LGraphTextureDownsample._shader; - if (!shader) { - LGraphTextureDownsample._shader = shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureDownsample.pixel_shader - ); - } - - var width = tex.width | 0; - var height = tex.height | 0; - var type = tex.type; - if (this.properties.precision === LGraphTexture.LOW) { - type = gl.UNSIGNED_BYTE; - } else if (this.properties.precision === LGraphTexture.HIGH) { - type = gl.HIGH_PRECISION_FORMAT; - } - var iterations = this.properties.iterations || 1; - - var origin = tex; - var target = null; - - var temp = []; - var options = { - type: type, - format: tex.format - }; - - var offset = vec2.create(); - var uniforms = { - u_offset: offset - }; - - if (this._texture) { - GL.Texture.releaseTemporary(this._texture); - } - - for (var i = 0; i < iterations; ++i) { - offset[0] = 1 / width; - offset[1] = 1 / height; - width = width >> 1 || 0; - height = height >> 1 || 0; - target = GL.Texture.getTemporary(width, height, options); - temp.push(target); - origin.setParameter(GL.TEXTURE_MAG_FILTER, GL.NEAREST); - origin.copyTo(target, shader, uniforms); - if (width == 1 && height == 1) { - break; - } //nothing else to do - origin = target; - } - - //keep the last texture used - this._texture = temp.pop(); - - //free the rest - for (var i = 0; i < temp.length; ++i) { - GL.Texture.releaseTemporary(temp[i]); - } - - if (this.properties.generate_mipmaps) { - this._texture.bind(0); - gl.generateMipmap(this._texture.texture_type); - this._texture.unbind(0); - } - - this.setOutputData(0, this._texture); - }; - - LGraphTextureDownsample.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_offset;\n\ - varying vec2 v_coord;\n\ - \n\ - void main() {\n\ - vec4 color = texture2D(u_texture, v_coord );\n\ - color += texture2D(u_texture, v_coord + vec2( u_offset.x, 0.0 ) );\n\ - color += texture2D(u_texture, v_coord + vec2( 0.0, u_offset.y ) );\n\ - color += texture2D(u_texture, v_coord + vec2( u_offset.x, u_offset.y ) );\n\ - gl_FragColor = color * 0.25;\n\ - }\n\ - "; - - LiteGraph.registerNodeType( - "texture/downsample", - LGraphTextureDownsample - ); - - // Texture Average ***************************************** - function LGraphTextureAverage() { - this.addInput("Texture", "Texture"); - this.addOutput("tex", "Texture"); - this.addOutput("avg", "vec4"); - this.addOutput("lum", "number"); - this.properties = { - use_previous_frame: true, //to avoid stalls - high_quality: false //to use as much pixels as possible - }; - - this._uniforms = { - u_texture: 0, - u_mipmap_offset: 0 - }; - this._luminance = new Float32Array(4); - } - - LGraphTextureAverage.title = "Average"; - LGraphTextureAverage.desc = - "Compute a partial average (32 random samples) of a texture and stores it as a 1x1 pixel texture.\n If high_quality is true, then it generates the mipmaps first and reads from the lower one."; - - LGraphTextureAverage.prototype.onExecute = function() { - if (!this.properties.use_previous_frame) { - this.updateAverage(); - } - - var v = this._luminance; - this.setOutputData(0, this._temp_texture); - this.setOutputData(1, v); - this.setOutputData(2, (v[0] + v[1] + v[2]) / 3); - }; - - //executed before rendering the frame - LGraphTextureAverage.prototype.onPreRenderExecute = function() { - this.updateAverage(); - }; - - LGraphTextureAverage.prototype.updateAverage = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if ( - !this.isOutputConnected(0) && - !this.isOutputConnected(1) && - !this.isOutputConnected(2) - ) { - return; - } //saves work - - if (!LGraphTextureAverage._shader) { - LGraphTextureAverage._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureAverage.pixel_shader - ); - //creates 256 random numbers and stores them in two mat4 - var samples = new Float32Array(16); - for (var i = 0; i < samples.length; ++i) { - samples[i] = Math.random(); //poorly distributed samples - } - //upload only once - LGraphTextureAverage._shader.uniforms({ - u_samples_a: samples.subarray(0, 16), - u_samples_b: samples.subarray(16, 32) - }); - } - - var temp = this._temp_texture; - var type = gl.UNSIGNED_BYTE; - if (tex.type != type) { - //force floats, half floats cannot be read with gl.readPixels - type = gl.FLOAT; - } - - if (!temp || temp.type != type) { - this._temp_texture = new GL.Texture(1, 1, { - type: type, - format: gl.RGBA, - filter: gl.NEAREST - }); - } - - this._uniforms.u_mipmap_offset = 0; - - if(this.properties.high_quality) - { - if( !this._temp_pot2_texture || this._temp_pot2_texture.type != type ) - this._temp_pot2_texture = new GL.Texture(512, 512, { - type: type, - format: gl.RGBA, - minFilter: gl.LINEAR_MIPMAP_LINEAR, - magFilter: gl.LINEAR - }); - - tex.copyTo( this._temp_pot2_texture ); - tex = this._temp_pot2_texture; - tex.bind(0); - gl.generateMipmap(GL.TEXTURE_2D); - this._uniforms.u_mipmap_offset = 9; - } - - var shader = LGraphTextureAverage._shader; - var uniforms = this._uniforms; - uniforms.u_mipmap_offset = this.properties.mipmap_offset; - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.BLEND); - this._temp_texture.drawTo(function() { - tex.toViewport(shader, uniforms); - }); - - if (this.isOutputConnected(1) || this.isOutputConnected(2)) { - var pixel = this._temp_texture.getPixels(); - if (pixel) { - var v = this._luminance; - var type = this._temp_texture.type; - v.set(pixel); - if (type == gl.UNSIGNED_BYTE) { - vec4.scale(v, v, 1 / 255); - } else if ( - type == GL.HALF_FLOAT || - type == GL.HALF_FLOAT_OES - ) { - //no half floats possible, hard to read back unless copyed to a FLOAT texture, so temp_texture is always forced to FLOAT - } - } - } - }; - - LGraphTextureAverage.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - uniform mat4 u_samples_a;\n\ - uniform mat4 u_samples_b;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_mipmap_offset;\n\ - varying vec2 v_coord;\n\ - \n\ - void main() {\n\ - vec4 color = vec4(0.0);\n\ - //random average\n\ - for(int i = 0; i < 4; ++i)\n\ - for(int j = 0; j < 4; ++j)\n\ - {\n\ - color += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\ - color += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\ - }\n\ - gl_FragColor = color * 0.03125;\n\ - }\n\ - "; - - LiteGraph.registerNodeType("texture/average", LGraphTextureAverage); - - - - // Computes operation between pixels (max, min) ***************************************** - function LGraphTextureMinMax() { - this.addInput("Texture", "Texture"); - this.addOutput("min_t", "Texture"); - this.addOutput("max_t", "Texture"); - this.addOutput("min", "vec4"); - this.addOutput("max", "vec4"); - this.properties = { - mode: "max", - use_previous_frame: true //to avoid stalls - }; - - this._uniforms = { - u_texture: 0 - }; - - this._max = new Float32Array(4); - this._min = new Float32Array(4); - - this._textures_chain = []; - } - - LGraphTextureMinMax.widgets_info = { - mode: { widget: "combo", values: ["min","max","avg"] } - }; - - LGraphTextureMinMax.title = "MinMax"; - LGraphTextureMinMax.desc = "Compute the scene min max"; - - LGraphTextureMinMax.prototype.onExecute = function() { - if (!this.properties.use_previous_frame) { - this.update(); - } - - this.setOutputData(0, this._temp_texture); - this.setOutputData(1, this._luminance); - }; - - //executed before rendering the frame - LGraphTextureMinMax.prototype.onPreRenderExecute = function() { - this.update(); - }; - - LGraphTextureMinMax.prototype.update = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if ( !this.isOutputConnected(0) && !this.isOutputConnected(1) ) { - return; - } //saves work - - if (!LGraphTextureMinMax._shader) { - LGraphTextureMinMax._shader = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureMinMax.pixel_shader ); - } - - var temp = this._temp_texture; - var type = gl.UNSIGNED_BYTE; - if (tex.type != type) { - //force floats, half floats cannot be read with gl.readPixels - type = gl.FLOAT; - } - - var size = 512; - - if( !this._textures_chain.length || this._textures_chain[0].type != type ) - { - var index = 0; - while(i) - { - this._textures_chain[i] = new GL.Texture( size, size, { - type: type, - format: gl.RGBA, - filter: gl.NEAREST - }); - size = size >> 2; - i++; - if(size == 1) - break; - } - } - - tex.copyTo( this._textures_chain[0] ); - var prev = this._textures_chain[0]; - for(var i = 1; i <= this._textures_chain.length; ++i) - { - var tex = this._textures_chain[i]; - - prev = tex; - } - - var shader = LGraphTextureMinMax._shader; - var uniforms = this._uniforms; - uniforms.u_mipmap_offset = this.properties.mipmap_offset; - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.BLEND); - this._temp_texture.drawTo(function() { - tex.toViewport(shader, uniforms); - }); - }; - - LGraphTextureMinMax.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - uniform mat4 u_samples_a;\n\ - uniform mat4 u_samples_b;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_mipmap_offset;\n\ - varying vec2 v_coord;\n\ - \n\ - void main() {\n\ - vec4 color = vec4(0.0);\n\ - //random average\n\ - for(int i = 0; i < 4; ++i)\n\ - for(int j = 0; j < 4; ++j)\n\ - {\n\ - color += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\ - color += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\ - }\n\ - gl_FragColor = color * 0.03125;\n\ - }\n\ - "; - - //LiteGraph.registerNodeType("texture/clustered_operation", LGraphTextureClusteredOperation); - - - function LGraphTextureTemporalSmooth() { - this.addInput("in", "Texture"); - this.addInput("factor", "Number"); - this.addOutput("out", "Texture"); - this.properties = { factor: 0.5 }; - this._uniforms = { - u_texture: 0, - u_textureB: 1, - u_factor: this.properties.factor - }; - } - - LGraphTextureTemporalSmooth.title = "Smooth"; - LGraphTextureTemporalSmooth.desc = "Smooth texture over time"; - - LGraphTextureTemporalSmooth.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex || !this.isOutputConnected(0)) { - return; - } - - if (!LGraphTextureTemporalSmooth._shader) { - LGraphTextureTemporalSmooth._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureTemporalSmooth.pixel_shader - ); - } - - var temp = this._temp_texture; - if ( - !temp || - temp.type != tex.type || - temp.width != tex.width || - temp.height != tex.height - ) { - var options = { - type: tex.type, - format: gl.RGBA, - filter: gl.NEAREST - }; - this._temp_texture = new GL.Texture(tex.width, tex.height, options ); - this._temp_texture2 = new GL.Texture(tex.width, tex.height, options ); - tex.copyTo(this._temp_texture2); - } - - var tempA = this._temp_texture; - var tempB = this._temp_texture2; - - var shader = LGraphTextureTemporalSmooth._shader; - var uniforms = this._uniforms; - uniforms.u_factor = 1.0 - this.getInputOrProperty("factor"); - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - tempA.drawTo(function() { - tempB.bind(1); - tex.toViewport(shader, uniforms); - }); - - this.setOutputData(0, tempA); - - //swap - this._temp_texture = tempB; - this._temp_texture2 = tempA; - }; - - LGraphTextureTemporalSmooth.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - uniform float u_factor;\n\ - varying vec2 v_coord;\n\ - \n\ - void main() {\n\ - gl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\ - }\n\ - "; - - LiteGraph.registerNodeType( "texture/temporal_smooth", LGraphTextureTemporalSmooth ); - - - function LGraphTextureLinearAvgSmooth() { - this.addInput("in", "Texture"); - this.addOutput("avg", "Texture"); - this.addOutput("array", "Texture"); - this.properties = { samples: 64, frames_interval: 1 }; - this._uniforms = { - u_texture: 0, - u_textureB: 1, - u_samples: this.properties.samples, - u_isamples: 1/this.properties.samples - }; - this.frame = 0; - } - - LGraphTextureLinearAvgSmooth.title = "Lineal Avg Smooth"; - LGraphTextureLinearAvgSmooth.desc = "Smooth texture linearly over time"; - - LGraphTextureLinearAvgSmooth["@samples"] = { type: "number", min: 1, max: 64, step: 1, precision: 1 }; - - LGraphTextureLinearAvgSmooth.prototype.getPreviewTexture = function() - { - return this._temp_texture2; - } - - LGraphTextureLinearAvgSmooth.prototype.onExecute = function() { - - var tex = this.getInputData(0); - if (!tex || !this.isOutputConnected(0)) { - return; - } - - if (!LGraphTextureLinearAvgSmooth._shader) { - LGraphTextureLinearAvgSmooth._shader_copy = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureLinearAvgSmooth.pixel_shader_copy ); - LGraphTextureLinearAvgSmooth._shader_avg = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureLinearAvgSmooth.pixel_shader_avg ); - } - - var samples = Math.clamp(this.properties.samples,0,64); - var frame = this.frame; - var interval = this.properties.frames_interval; - - if( interval == 0 || frame % interval == 0 ) - { - var temp = this._temp_texture; - if ( !temp || temp.type != tex.type || temp.width != samples ) { - var options = { - type: tex.type, - format: gl.RGBA, - filter: gl.NEAREST - }; - this._temp_texture = new GL.Texture( samples, 1, options ); - this._temp_texture2 = new GL.Texture( samples, 1, options ); - this._temp_texture_out = new GL.Texture( 1, 1, options ); - } - - var tempA = this._temp_texture; - var tempB = this._temp_texture2; - - var shader_copy = LGraphTextureLinearAvgSmooth._shader_copy; - var shader_avg = LGraphTextureLinearAvgSmooth._shader_avg; - var uniforms = this._uniforms; - uniforms.u_samples = samples; - uniforms.u_isamples = 1.0 / samples; - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - tempA.drawTo(function() { - tempB.bind(1); - tex.toViewport( shader_copy, uniforms ); - }); - - this._temp_texture_out.drawTo(function() { - tempA.toViewport( shader_avg, uniforms ); - }); - - this.setOutputData( 0, this._temp_texture_out ); - - //swap - this._temp_texture = tempB; - this._temp_texture2 = tempA; - } - else - this.setOutputData(0, this._temp_texture_out); - this.setOutputData(1, this._temp_texture2); - this.frame++; - }; - - LGraphTextureLinearAvgSmooth.pixel_shader_copy = - "precision highp float;\n\ - precision highp float;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - uniform float u_isamples;\n\ - varying vec2 v_coord;\n\ - \n\ - void main() {\n\ - if( v_coord.x <= u_isamples )\n\ - gl_FragColor = texture2D( u_texture, vec2(0.5) );\n\ - else\n\ - gl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\ - }\n\ - "; - - LGraphTextureLinearAvgSmooth.pixel_shader_avg = - "precision highp float;\n\ - precision highp float;\n\ - uniform sampler2D u_texture;\n\ - uniform int u_samples;\n\ - uniform float u_isamples;\n\ - varying vec2 v_coord;\n\ - \n\ - void main() {\n\ - vec4 color = vec4(0.0);\n\ - for(int i = 0; i < 64; ++i)\n\ - {\n\ - color += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\ - if(i == (u_samples - 1))\n\ - break;\n\ - }\n\ - gl_FragColor = color * u_isamples;\n\ - }\n\ - "; - - - LiteGraph.registerNodeType( "texture/linear_avg_smooth", LGraphTextureLinearAvgSmooth ); - - // Image To Texture ***************************************** - function LGraphImageToTexture() { - this.addInput("Image", "image"); - this.addOutput("", "Texture"); - this.properties = {}; - } - - LGraphImageToTexture.title = "Image to Texture"; - LGraphImageToTexture.desc = "Uploads an image to the GPU"; - //LGraphImageToTexture.widgets_info = { size: { widget:"combo", values:[0,32,64,128,256,512,1024,2048]} }; - - LGraphImageToTexture.prototype.onExecute = function() { - var img = this.getInputData(0); - if (!img) { - return; - } - - var width = img.videoWidth || img.width; - var height = img.videoHeight || img.height; - - //this is in case we are using a webgl canvas already, no need to reupload it - if (img.gltexture) { - this.setOutputData(0, img.gltexture); - return; - } - - var temp = this._temp_texture; - if (!temp || temp.width != width || temp.height != height) { - this._temp_texture = new GL.Texture(width, height, { - format: gl.RGBA, - filter: gl.LINEAR - }); - } - - try { - this._temp_texture.uploadImage(img); - } catch (err) { - console.error( - "image comes from an unsafe location, cannot be uploaded to webgl: " + - err - ); - return; - } - - this.setOutputData(0, this._temp_texture); - }; - - LiteGraph.registerNodeType( - "texture/imageToTexture", - LGraphImageToTexture - ); - - // Texture LUT ***************************************** - function LGraphTextureLUT() { - this.addInput("Texture", "Texture"); - this.addInput("LUT", "Texture"); - this.addInput("Intensity", "number"); - this.addOutput("", "Texture"); - this.properties = { enabled: true, intensity: 1, precision: LGraphTexture.DEFAULT, texture: null }; - - if (!LGraphTextureLUT._shader) { - LGraphTextureLUT._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, LGraphTextureLUT.pixel_shader ); - } - } - - LGraphTextureLUT.widgets_info = { - texture: { widget: "texture" }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureLUT.title = "LUT"; - LGraphTextureLUT.desc = "Apply LUT to Texture"; - - LGraphTextureLUT.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var tex = this.getInputData(0); - - if (this.properties.precision === LGraphTexture.PASS_THROUGH || this.properties.enabled === false) { - this.setOutputData(0, tex); - return; - } - - if (!tex) { - return; - } - - var lut_tex = this.getInputData(1); - - if (!lut_tex) { - lut_tex = LGraphTexture.getTexture(this.properties.texture); - } - - if (!lut_tex) { - this.setOutputData(0, tex); - return; - } - - lut_tex.bind(0); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - gl.texParameteri( - gl.TEXTURE_2D, - gl.TEXTURE_WRAP_S, - gl.CLAMP_TO_EDGE - ); - gl.texParameteri( - gl.TEXTURE_2D, - gl.TEXTURE_WRAP_T, - gl.CLAMP_TO_EDGE - ); - gl.bindTexture(gl.TEXTURE_2D, null); - - var intensity = this.properties.intensity; - if (this.isInputConnected(2)) { - this.properties.intensity = intensity = this.getInputData(2); - } - - this._tex = LGraphTexture.getTargetTexture( - tex, - this._tex, - this.properties.precision - ); - - //var mesh = Mesh.getScreenQuad(); - - this._tex.drawTo(function() { - lut_tex.bind(1); - tex.toViewport(LGraphTextureLUT._shader, { - u_texture: 0, - u_textureB: 1, - u_amount: intensity - }); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureLUT.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - uniform float u_amount;\n\ - \n\ - void main() {\n\ - lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\ - mediump float blueColor = textureColor.b * 63.0;\n\ - mediump vec2 quad1;\n\ - quad1.y = floor(floor(blueColor) / 8.0);\n\ - quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\ - mediump vec2 quad2;\n\ - quad2.y = floor(ceil(blueColor) / 8.0);\n\ - quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\ - highp vec2 texPos1;\n\ - texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\ - texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\ - highp vec2 texPos2;\n\ - texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\ - texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\ - lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\ - lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\ - lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\ - gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\ - }\n\ - "; - - LiteGraph.registerNodeType("texture/LUT", LGraphTextureLUT); - - - // Texture LUT ***************************************** - function LGraphTextureEncode() { - this.addInput("Texture", "Texture"); - this.addInput("Atlas", "Texture"); - this.addOutput("", "Texture"); - this.properties = { enabled: true, num_row_symbols: 4, symbol_size: 16, brightness: 1, colorize: false, filter: false, invert: false, precision: LGraphTexture.DEFAULT, texture: null }; - - if (!LGraphTextureEncode._shader) { - LGraphTextureEncode._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, LGraphTextureEncode.pixel_shader ); - } - - this._uniforms = { - u_texture: 0, - u_textureB: 1, - u_row_simbols: 4, - u_simbol_size: 16, - u_res: vec2.create() - }; - } - - LGraphTextureEncode.widgets_info = { - texture: { widget: "texture" }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureEncode.title = "Encode"; - LGraphTextureEncode.desc = "Apply a texture atlas to encode a texture"; - - LGraphTextureEncode.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var tex = this.getInputData(0); - - if (this.properties.precision === LGraphTexture.PASS_THROUGH || this.properties.enabled === false) { - this.setOutputData(0, tex); - return; - } - - if (!tex) { - return; - } - - var symbols_tex = this.getInputData(1); - - if (!symbols_tex) { - symbols_tex = LGraphTexture.getTexture(this.properties.texture); - } - - if (!symbols_tex) { - this.setOutputData(0, tex); - return; - } - - symbols_tex.bind(0); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, this.properties.filter ? gl.LINEAR : gl.NEAREST ); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, this.properties.filter ? gl.LINEAR : gl.NEAREST ); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE ); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE ); - gl.bindTexture(gl.TEXTURE_2D, null); - - var uniforms = this._uniforms; - uniforms.u_row_simbols = Math.floor(this.properties.num_row_symbols); - uniforms.u_symbol_size = this.properties.symbol_size; - uniforms.u_brightness = this.properties.brightness; - uniforms.u_invert = this.properties.invert ? 1 : 0; - uniforms.u_colorize = this.properties.colorize ? 1 : 0; - - this._tex = LGraphTexture.getTargetTexture( tex, this._tex, this.properties.precision ); - uniforms.u_res[0] = this._tex.width; - uniforms.u_res[1] = this._tex.height; - this._tex.bind(0); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); - - this._tex.drawTo(function() { - symbols_tex.bind(1); - tex.toViewport(LGraphTextureEncode._shader, uniforms); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureEncode.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - uniform float u_row_simbols;\n\ - uniform float u_symbol_size;\n\ - uniform float u_brightness;\n\ - uniform float u_invert;\n\ - uniform float u_colorize;\n\ - uniform vec2 u_res;\n\ - \n\ - void main() {\n\ - vec2 total_symbols = u_res / u_symbol_size;\n\ - vec2 uv = floor(v_coord * total_symbols) / total_symbols; //pixelate \n\ - vec2 local_uv = mod(v_coord * u_res, u_symbol_size) / u_symbol_size;\n\ - lowp vec4 textureColor = texture2D(u_texture, uv );\n\ - float lum = clamp(u_brightness * (textureColor.x + textureColor.y + textureColor.z)/3.0,0.0,1.0);\n\ - if( u_invert == 1.0 ) lum = 1.0 - lum;\n\ - float index = floor( lum * (u_row_simbols * u_row_simbols - 1.0));\n\ - float col = mod( index, u_row_simbols );\n\ - float row = u_row_simbols - floor( index / u_row_simbols ) - 1.0;\n\ - vec2 simbol_uv = ( vec2( col, row ) + local_uv ) / u_row_simbols;\n\ - vec4 color = texture2D( u_textureB, simbol_uv );\n\ - if(u_colorize == 1.0)\n\ - color *= textureColor;\n\ - gl_FragColor = color;\n\ - }\n\ - "; - - LiteGraph.registerNodeType("texture/encode", LGraphTextureEncode); - - // Texture Channels ***************************************** - function LGraphTextureChannels() { - this.addInput("Texture", "Texture"); - - this.addOutput("R", "Texture"); - this.addOutput("G", "Texture"); - this.addOutput("B", "Texture"); - this.addOutput("A", "Texture"); - - //this.properties = { use_single_channel: true }; - if (!LGraphTextureChannels._shader) { - LGraphTextureChannels._shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureChannels.pixel_shader - ); - } - } - - LGraphTextureChannels.title = "Texture to Channels"; - LGraphTextureChannels.desc = "Split texture channels"; - - LGraphTextureChannels.prototype.onExecute = function() { - var texA = this.getInputData(0); - if (!texA) { - return; - } - - if (!this._channels) { - this._channels = Array(4); - } - - //var format = this.properties.use_single_channel ? gl.LUMINANCE : gl.RGBA; //not supported by WebGL1 - var format = gl.RGB; - var connections = 0; - for (var i = 0; i < 4; i++) { - if (this.isOutputConnected(i)) { - if ( - !this._channels[i] || - this._channels[i].width != texA.width || - this._channels[i].height != texA.height || - this._channels[i].type != texA.type || - this._channels[i].format != format - ) { - this._channels[i] = new GL.Texture( - texA.width, - texA.height, - { - type: texA.type, - format: format, - filter: gl.LINEAR - } - ); - } - connections++; - } else { - this._channels[i] = null; - } - } - - if (!connections) { - return; - } - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - - var mesh = Mesh.getScreenQuad(); - var shader = LGraphTextureChannels._shader; - var masks = [ - [1, 0, 0, 0], - [0, 1, 0, 0], - [0, 0, 1, 0], - [0, 0, 0, 1] - ]; - - for (var i = 0; i < 4; i++) { - if (!this._channels[i]) { - continue; - } - - this._channels[i].drawTo(function() { - texA.bind(0); - shader - .uniforms({ u_texture: 0, u_mask: masks[i] }) - .draw(mesh); - }); - this.setOutputData(i, this._channels[i]); - } - }; - - LGraphTextureChannels.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec4 u_mask;\n\ - \n\ - void main() {\n\ - gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\ - }\n\ - "; - - LiteGraph.registerNodeType( - "texture/textureChannels", - LGraphTextureChannels - ); - - // Texture Channels to Texture ***************************************** - function LGraphChannelsTexture() { - this.addInput("R", "Texture"); - this.addInput("G", "Texture"); - this.addInput("B", "Texture"); - this.addInput("A", "Texture"); - - this.addOutput("Texture", "Texture"); - - this.properties = { - precision: LGraphTexture.DEFAULT, - R: 1, - G: 1, - B: 1, - A: 1 - }; - this._color = vec4.create(); - this._uniforms = { - u_textureR: 0, - u_textureG: 1, - u_textureB: 2, - u_textureA: 3, - u_color: this._color - }; - } - - LGraphChannelsTexture.title = "Channels to Texture"; - LGraphChannelsTexture.desc = "Split texture channels"; - LGraphChannelsTexture.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphChannelsTexture.prototype.onExecute = function() { - var white = LGraphTexture.getWhiteTexture(); - var texR = this.getInputData(0) || white; - var texG = this.getInputData(1) || white; - var texB = this.getInputData(2) || white; - var texA = this.getInputData(3) || white; - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - - var mesh = Mesh.getScreenQuad(); - if (!LGraphChannelsTexture._shader) { - LGraphChannelsTexture._shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphChannelsTexture.pixel_shader - ); - } - var shader = LGraphChannelsTexture._shader; - - var w = Math.max(texR.width, texG.width, texB.width, texA.width); - var h = Math.max( - texR.height, - texG.height, - texB.height, - texA.height - ); - var type = - this.properties.precision == LGraphTexture.HIGH - ? LGraphTexture.HIGH_PRECISION_FORMAT - : gl.UNSIGNED_BYTE; - - if ( - !this._texture || - this._texture.width != w || - this._texture.height != h || - this._texture.type != type - ) { - this._texture = new GL.Texture(w, h, { - type: type, - format: gl.RGBA, - filter: gl.LINEAR - }); - } - - var color = this._color; - color[0] = this.properties.R; - color[1] = this.properties.G; - color[2] = this.properties.B; - color[3] = this.properties.A; - var uniforms = this._uniforms; - - this._texture.drawTo(function() { - texR.bind(0); - texG.bind(1); - texB.bind(2); - texA.bind(3); - shader.uniforms(uniforms).draw(mesh); - }); - this.setOutputData(0, this._texture); - }; - - LGraphChannelsTexture.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_textureR;\n\ - uniform sampler2D u_textureG;\n\ - uniform sampler2D u_textureB;\n\ - uniform sampler2D u_textureA;\n\ - uniform vec4 u_color;\n\ - \n\ - void main() {\n\ - gl_FragColor = u_color * vec4( \ - texture2D(u_textureR, v_coord).r,\ - texture2D(u_textureG, v_coord).r,\ - texture2D(u_textureB, v_coord).r,\ - texture2D(u_textureA, v_coord).r);\n\ - }\n\ - "; - - LiteGraph.registerNodeType( - "texture/channelsTexture", - LGraphChannelsTexture - ); - - // Texture Color ***************************************** - function LGraphTextureColor() { - this.addOutput("Texture", "Texture"); - - this._tex_color = vec4.create(); - this.properties = { - color: vec4.create(), - precision: LGraphTexture.DEFAULT - }; - } - - LGraphTextureColor.title = "Color"; - LGraphTextureColor.desc = - "Generates a 1x1 texture with a constant color"; - - LGraphTextureColor.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureColor.prototype.onDrawBackground = function(ctx) { - var c = this.properties.color; - ctx.fillStyle = - "rgb(" + - Math.floor(Math.clamp(c[0], 0, 1) * 255) + - "," + - Math.floor(Math.clamp(c[1], 0, 1) * 255) + - "," + - Math.floor(Math.clamp(c[2], 0, 1) * 255) + - ")"; - if (this.flags.collapsed) { - this.boxcolor = ctx.fillStyle; - } else { - ctx.fillRect(0, 0, this.size[0], this.size[1]); - } - }; - - LGraphTextureColor.prototype.onExecute = function() { - var type = - this.properties.precision == LGraphTexture.HIGH - ? LGraphTexture.HIGH_PRECISION_FORMAT - : gl.UNSIGNED_BYTE; - - if (!this._tex || this._tex.type != type) { - this._tex = new GL.Texture(1, 1, { - format: gl.RGBA, - type: type, - minFilter: gl.NEAREST - }); - } - var color = this.properties.color; - - if (this.inputs) { - for (var i = 0; i < this.inputs.length; i++) { - var input = this.inputs[i]; - var v = this.getInputData(i); - if (v === undefined) { - continue; - } - switch (input.name) { - case "RGB": - case "RGBA": - color.set(v); - break; - case "R": - color[0] = v; - break; - case "G": - color[1] = v; - break; - case "B": - color[2] = v; - break; - case "A": - color[3] = v; - break; - } - } - } - - if (vec4.sqrDist(this._tex_color, color) > 0.001) { - this._tex_color.set(color); - this._tex.fill(color); - } - this.setOutputData(0, this._tex); - }; - - LGraphTextureColor.prototype.onGetInputs = function() { - return [ - ["RGB", "vec3"], - ["RGBA", "vec4"], - ["R", "number"], - ["G", "number"], - ["B", "number"], - ["A", "number"] - ]; - }; - - LiteGraph.registerNodeType("texture/color", LGraphTextureColor); - - // Texture Channels to Texture ***************************************** - function LGraphTextureGradient() { - this.addInput("A", "color"); - this.addInput("B", "color"); - this.addOutput("Texture", "Texture"); - - this.properties = { - angle: 0, - scale: 1, - A: [0, 0, 0], - B: [1, 1, 1], - texture_size: 32 - }; - if (!LGraphTextureGradient._shader) { - LGraphTextureGradient._shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureGradient.pixel_shader - ); - } - - this._uniforms = { - u_angle: 0, - u_colorA: vec3.create(), - u_colorB: vec3.create() - }; - } - - LGraphTextureGradient.title = "Gradient"; - LGraphTextureGradient.desc = "Generates a gradient"; - LGraphTextureGradient["@A"] = { type: "color" }; - LGraphTextureGradient["@B"] = { type: "color" }; - LGraphTextureGradient["@texture_size"] = { - type: "enum", - values: [32, 64, 128, 256, 512] - }; - - LGraphTextureGradient.prototype.onExecute = function() { - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - - var mesh = GL.Mesh.getScreenQuad(); - var shader = LGraphTextureGradient._shader; - - var A = this.getInputData(0); - if (!A) { - A = this.properties.A; - } - var B = this.getInputData(1); - if (!B) { - B = this.properties.B; - } - - //angle and scale - for (var i = 2; i < this.inputs.length; i++) { - var input = this.inputs[i]; - var v = this.getInputData(i); - if (v === undefined) { - continue; - } - this.properties[input.name] = v; - } - - var uniforms = this._uniforms; - this._uniforms.u_angle = this.properties.angle * DEG2RAD; - this._uniforms.u_scale = this.properties.scale; - vec3.copy(uniforms.u_colorA, A); - vec3.copy(uniforms.u_colorB, B); - - var size = parseInt(this.properties.texture_size); - if (!this._tex || this._tex.width != size) { - this._tex = new GL.Texture(size, size, { - format: gl.RGB, - filter: gl.LINEAR - }); - } - - this._tex.drawTo(function() { - shader.uniforms(uniforms).draw(mesh); - }); - this.setOutputData(0, this._tex); - }; - - LGraphTextureGradient.prototype.onGetInputs = function() { - return [["angle", "number"], ["scale", "number"]]; - }; - - LGraphTextureGradient.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform float u_angle;\n\ - uniform float u_scale;\n\ - uniform vec3 u_colorA;\n\ - uniform vec3 u_colorB;\n\ - \n\ - vec2 rotate(vec2 v, float angle)\n\ - {\n\ - vec2 result;\n\ - float _cos = cos(angle);\n\ - float _sin = sin(angle);\n\ - result.x = v.x * _cos - v.y * _sin;\n\ - result.y = v.x * _sin + v.y * _cos;\n\ - return result;\n\ - }\n\ - void main() {\n\ - float f = (rotate(u_scale * (v_coord - vec2(0.5)), u_angle) + vec2(0.5)).x;\n\ - vec3 color = mix(u_colorA,u_colorB,clamp(f,0.0,1.0));\n\ - gl_FragColor = vec4(color,1.0);\n\ - }\n\ - "; - - LiteGraph.registerNodeType("texture/gradient", LGraphTextureGradient); - - // Texture Mix ***************************************** - function LGraphTextureMix() { - this.addInput("A", "Texture"); - this.addInput("B", "Texture"); - this.addInput("Mixer", "Texture"); - - this.addOutput("Texture", "Texture"); - this.properties = { factor: 0.5, size_from_biggest: true, invert: false, precision: LGraphTexture.DEFAULT }; - this._uniforms = { - u_textureA: 0, - u_textureB: 1, - u_textureMix: 2, - u_mix: vec4.create() - }; - } - - LGraphTextureMix.title = "Mix"; - LGraphTextureMix.desc = "Generates a texture mixing two textures"; - - LGraphTextureMix.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureMix.prototype.onExecute = function() { - var texA = this.getInputData(0); - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, texA); - return; - } - - var texB = this.getInputData(1); - if (!texA || !texB) { - return; - } - - var texMix = this.getInputData(2); - - var factor = this.getInputData(3); - - this._tex = LGraphTexture.getTargetTexture( - this.properties.size_from_biggest && texB.width > texA.width ? texB : texA, - this._tex, - this.properties.precision - ); - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - - var mesh = Mesh.getScreenQuad(); - var shader = null; - var uniforms = this._uniforms; - if (texMix) { - shader = LGraphTextureMix._shader_tex; - if (!shader) { - shader = LGraphTextureMix._shader_tex = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureMix.pixel_shader, - { MIX_TEX: "" } - ); - } - } else { - shader = LGraphTextureMix._shader_factor; - if (!shader) { - shader = LGraphTextureMix._shader_factor = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureMix.pixel_shader - ); - } - var f = factor == null ? this.properties.factor : factor; - uniforms.u_mix.set([f, f, f, f]); - } - - var invert = this.properties.invert; - - this._tex.drawTo(function() { - texA.bind( invert ? 1 : 0 ); - texB.bind( invert ? 0 : 1 ); - if (texMix) { - texMix.bind(2); - } - shader.uniforms(uniforms).draw(mesh); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureMix.prototype.onGetInputs = function() { - return [["factor", "number"]]; - }; - - LGraphTextureMix.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_textureA;\n\ - uniform sampler2D u_textureB;\n\ - #ifdef MIX_TEX\n\ - uniform sampler2D u_textureMix;\n\ - #else\n\ - uniform vec4 u_mix;\n\ - #endif\n\ - \n\ - void main() {\n\ - #ifdef MIX_TEX\n\ - vec4 f = texture2D(u_textureMix, v_coord);\n\ - #else\n\ - vec4 f = u_mix;\n\ - #endif\n\ - gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\ - }\n\ - "; - - LiteGraph.registerNodeType("texture/mix", LGraphTextureMix); - - // Texture Edges detection ***************************************** - function LGraphTextureEdges() { - this.addInput("Tex.", "Texture"); - - this.addOutput("Edges", "Texture"); - this.properties = { - invert: true, - threshold: false, - factor: 1, - precision: LGraphTexture.DEFAULT - }; - - if (!LGraphTextureEdges._shader) { - LGraphTextureEdges._shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureEdges.pixel_shader - ); - } - } - - LGraphTextureEdges.title = "Edges"; - LGraphTextureEdges.desc = "Detects edges"; - - LGraphTextureEdges.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureEdges.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var tex = this.getInputData(0); - - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, tex); - return; - } - - if (!tex) { - return; - } - - this._tex = LGraphTexture.getTargetTexture( - tex, - this._tex, - this.properties.precision - ); - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - - var mesh = Mesh.getScreenQuad(); - var shader = LGraphTextureEdges._shader; - var invert = this.properties.invert; - var factor = this.properties.factor; - var threshold = this.properties.threshold ? 1 : 0; - - this._tex.drawTo(function() { - tex.bind(0); - shader - .uniforms({ - u_texture: 0, - u_isize: [1 / tex.width, 1 / tex.height], - u_factor: factor, - u_threshold: threshold, - u_invert: invert ? 1 : 0 - }) - .draw(mesh); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureEdges.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_isize;\n\ - uniform int u_invert;\n\ - uniform float u_factor;\n\ - uniform float u_threshold;\n\ - \n\ - void main() {\n\ - vec4 center = texture2D(u_texture, v_coord);\n\ - vec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\ - vec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\ - vec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\ - vec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\ - vec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\ - diff *= u_factor;\n\ - if(u_invert == 1)\n\ - diff.xyz = vec3(1.0) - diff.xyz;\n\ - if( u_threshold == 0.0 )\n\ - gl_FragColor = vec4( diff.xyz, center.a );\n\ - else\n\ - gl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\ - }\n\ - "; - - LiteGraph.registerNodeType("texture/edges", LGraphTextureEdges); - - // Texture Depth ***************************************** - function LGraphTextureDepthRange() { - this.addInput("Texture", "Texture"); - this.addInput("Distance", "number"); - this.addInput("Range", "number"); - this.addOutput("Texture", "Texture"); - this.properties = { - distance: 100, - range: 50, - only_depth: false, - high_precision: false - }; - this._uniforms = { - u_texture: 0, - u_distance: 100, - u_range: 50, - u_camera_planes: null - }; - } - - LGraphTextureDepthRange.title = "Depth Range"; - LGraphTextureDepthRange.desc = "Generates a texture with a depth range"; - - LGraphTextureDepthRange.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var tex = this.getInputData(0); - if (!tex) { - return; - } - - var precision = gl.UNSIGNED_BYTE; - if (this.properties.high_precision) { - precision = gl.half_float_ext ? gl.HALF_FLOAT_OES : gl.FLOAT; - } - - if ( - !this._temp_texture || - this._temp_texture.type != precision || - this._temp_texture.width != tex.width || - this._temp_texture.height != tex.height - ) { - this._temp_texture = new GL.Texture(tex.width, tex.height, { - type: precision, - format: gl.RGBA, - filter: gl.LINEAR - }); - } - - var uniforms = this._uniforms; - - //iterations - var distance = this.properties.distance; - if (this.isInputConnected(1)) { - distance = this.getInputData(1); - this.properties.distance = distance; - } - - var range = this.properties.range; - if (this.isInputConnected(2)) { - range = this.getInputData(2); - this.properties.range = range; - } - - uniforms.u_distance = distance; - uniforms.u_range = range; - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - var mesh = Mesh.getScreenQuad(); - if (!LGraphTextureDepthRange._shader) { - LGraphTextureDepthRange._shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureDepthRange.pixel_shader - ); - LGraphTextureDepthRange._shader_onlydepth = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureDepthRange.pixel_shader, - { ONLY_DEPTH: "" } - ); - } - var shader = this.properties.only_depth - ? LGraphTextureDepthRange._shader_onlydepth - : LGraphTextureDepthRange._shader; - - //NEAR AND FAR PLANES - var planes = null; - if (tex.near_far_planes) { - planes = tex.near_far_planes; - } else if (window.LS && LS.Renderer._main_camera) { - planes = LS.Renderer._main_camera._uniforms.u_camera_planes; - } else { - planes = [0.1, 1000]; - } //hardcoded - uniforms.u_camera_planes = planes; - - this._temp_texture.drawTo(function() { - tex.bind(0); - shader.uniforms(uniforms).draw(mesh); - }); - - this._temp_texture.near_far_planes = planes; - this.setOutputData(0, this._temp_texture); - }; - - LGraphTextureDepthRange.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_camera_planes;\n\ - uniform float u_distance;\n\ - uniform float u_range;\n\ - \n\ - float LinearDepth()\n\ - {\n\ - float zNear = u_camera_planes.x;\n\ - float zFar = u_camera_planes.y;\n\ - float depth = texture2D(u_texture, v_coord).x;\n\ - depth = depth * 2.0 - 1.0;\n\ - return zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\ - }\n\ - \n\ - void main() {\n\ - float depth = LinearDepth();\n\ - #ifdef ONLY_DEPTH\n\ - gl_FragColor = vec4(depth);\n\ - #else\n\ - float diff = abs(depth * u_camera_planes.y - u_distance);\n\ - float dof = 1.0;\n\ - if(diff <= u_range)\n\ - dof = diff / u_range;\n\ - gl_FragColor = vec4(dof);\n\ - #endif\n\ - }\n\ - "; - - LiteGraph.registerNodeType( "texture/depth_range", LGraphTextureDepthRange ); - - - // Texture Depth ***************************************** - function LGraphTextureLinearDepth() { - this.addInput("Texture", "Texture"); - this.addOutput("Texture", "Texture"); - this.properties = { - precision: LGraphTexture.DEFAULT, - invert: false - }; - this._uniforms = { - u_texture: 0, - u_camera_planes: null, //filled later - u_ires: vec2.create() - }; - } - - LGraphTextureLinearDepth.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureLinearDepth.title = "Linear Depth"; - LGraphTextureLinearDepth.desc = "Creates a color texture with linear depth"; - - LGraphTextureLinearDepth.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var tex = this.getInputData(0); - if (!tex || (tex.format != gl.DEPTH_COMPONENT && tex.format != gl.DEPTH_STENCIL) ) { - return; - } - - var precision = this.properties.precision == LGraphTexture.HIGH ? gl.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE; - - if ( !this._temp_texture || this._temp_texture.type != precision || this._temp_texture.width != tex.width || this._temp_texture.height != tex.height ) { - this._temp_texture = new GL.Texture(tex.width, tex.height, { - type: precision, - format: gl.RGB, - filter: gl.LINEAR - }); - } - - var uniforms = this._uniforms; - uniforms.u_invert = this.properties.invert ? 1 : 0; - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - var mesh = Mesh.getScreenQuad(); - if(!LGraphTextureLinearDepth._shader) - LGraphTextureLinearDepth._shader = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureLinearDepth.pixel_shader); - var shader = LGraphTextureLinearDepth._shader; - - //NEAR AND FAR PLANES - var planes = null; - if (tex.near_far_planes) { - planes = tex.near_far_planes; - } else if (window.LS && LS.Renderer._main_camera) { - planes = LS.Renderer._main_camera._uniforms.u_camera_planes; - } else { - planes = [0.1, 1000]; - } //hardcoded - uniforms.u_camera_planes = planes; - //uniforms.u_ires.set([1/tex.width, 1/tex.height]); - uniforms.u_ires.set([0,0]); - - this._temp_texture.drawTo(function() { - tex.bind(0); - shader.uniforms(uniforms).draw(mesh); - }); - - this._temp_texture.near_far_planes = planes; - this.setOutputData(0, this._temp_texture); - }; - - LGraphTextureLinearDepth.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_camera_planes;\n\ - uniform int u_invert;\n\ - uniform vec2 u_ires;\n\ - \n\ - void main() {\n\ - float zNear = u_camera_planes.x;\n\ - float zFar = u_camera_planes.y;\n\ - float depth = texture2D(u_texture, v_coord + u_ires*0.5).x * 2.0 - 1.0;\n\ - float f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\ - if( u_invert == 1 )\n\ - f = 1.0 - f;\n\ - gl_FragColor = vec4(vec3(f),1.0);\n\ - }\n\ - "; - - LiteGraph.registerNodeType( "texture/linear_depth", LGraphTextureLinearDepth ); - - // Texture Blur ***************************************** - function LGraphTextureBlur() { - this.addInput("Texture", "Texture"); - this.addInput("Iterations", "number"); - this.addInput("Intensity", "number"); - this.addOutput("Blurred", "Texture"); - this.properties = { - intensity: 1, - iterations: 1, - preserve_aspect: false, - scale: [1, 1], - precision: LGraphTexture.DEFAULT - }; - } - - LGraphTextureBlur.title = "Blur"; - LGraphTextureBlur.desc = "Blur a texture"; - - LGraphTextureBlur.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureBlur.max_iterations = 20; - - LGraphTextureBlur.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var temp = this._final_texture; - - if ( - !temp || - temp.width != tex.width || - temp.height != tex.height || - temp.type != tex.type - ) { - //we need two textures to do the blurring - //this._temp_texture = new GL.Texture( tex.width, tex.height, { type: tex.type, format: gl.RGBA, filter: gl.LINEAR }); - temp = this._final_texture = new GL.Texture( - tex.width, - tex.height, - { type: tex.type, format: gl.RGBA, filter: gl.LINEAR } - ); - } - - //iterations - var iterations = this.properties.iterations; - if (this.isInputConnected(1)) { - iterations = this.getInputData(1); - this.properties.iterations = iterations; - } - iterations = Math.min( - Math.floor(iterations), - LGraphTextureBlur.max_iterations - ); - if (iterations == 0) { - //skip blurring - this.setOutputData(0, tex); - return; - } - - var intensity = this.properties.intensity; - if (this.isInputConnected(2)) { - intensity = this.getInputData(2); - this.properties.intensity = intensity; - } - - //blur sometimes needs an aspect correction - var aspect = LiteGraph.camera_aspect; - if (!aspect && window.gl !== undefined) { - aspect = gl.canvas.height / gl.canvas.width; - } - if (!aspect) { - aspect = 1; - } - aspect = this.properties.preserve_aspect ? aspect : 1; - - var scale = this.properties.scale || [1, 1]; - tex.applyBlur(aspect * scale[0], scale[1], intensity, temp); - for (var i = 1; i < iterations; ++i) { - temp.applyBlur( - aspect * scale[0] * (i + 1), - scale[1] * (i + 1), - intensity - ); - } - - this.setOutputData(0, temp); - }; - - /* -LGraphTextureBlur.pixel_shader = "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_offset;\n\ - uniform float u_intensity;\n\ - void main() {\n\ - vec4 sum = vec4(0.0);\n\ - vec4 center = texture2D(u_texture, v_coord);\n\ - sum += texture2D(u_texture, v_coord + u_offset * -4.0) * 0.05/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * -3.0) * 0.09/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * -2.0) * 0.12/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * -1.0) * 0.15/0.98;\n\ - sum += center * 0.16/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * 4.0) * 0.05/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * 3.0) * 0.09/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * 2.0) * 0.12/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * 1.0) * 0.15/0.98;\n\ - gl_FragColor = u_intensity * sum;\n\ - }\n\ - "; -*/ - - LiteGraph.registerNodeType("texture/blur", LGraphTextureBlur); - - // Texture Glow ***************************************** - //based in https://catlikecoding.com/unity/tutorials/advanced-rendering/bloom/ - function LGraphTextureGlow() { - this.addInput("in", "Texture"); - this.addInput("dirt", "Texture"); - this.addOutput("out", "Texture"); - this.addOutput("glow", "Texture"); - this.properties = { - enabled: true, - intensity: 1, - persistence: 0.99, - iterations: 16, - threshold: 0, - scale: 1, - dirt_factor: 0.5, - precision: LGraphTexture.DEFAULT - }; - this._textures = []; - this._uniforms = { - u_intensity: 1, - u_texture: 0, - u_glow_texture: 1, - u_threshold: 0, - u_texel_size: vec2.create() - }; - } - - LGraphTextureGlow.title = "Glow"; - LGraphTextureGlow.desc = "Filters a texture giving it a glow effect"; - LGraphTextureGlow.weights = new Float32Array([0.5, 0.4, 0.3, 0.2]); - - LGraphTextureGlow.widgets_info = { - iterations: { - type: "number", - min: 0, - max: 16, - step: 1, - precision: 0 - }, - threshold: { - type: "number", - min: 0, - max: 10, - step: 0.01, - precision: 2 - }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureGlow.prototype.onGetInputs = function() { - return [ - ["enabled", "boolean"], - ["threshold", "number"], - ["intensity", "number"], - ["persistence", "number"], - ["iterations", "number"], - ["dirt_factor", "number"] - ]; - }; - - LGraphTextureGlow.prototype.onGetOutputs = function() { - return [["average", "Texture"]]; - }; - - LGraphTextureGlow.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if (!this.isAnyOutputConnected()) { - return; - } //saves work - - if ( - this.properties.precision === LGraphTexture.PASS_THROUGH || - this.getInputOrProperty("enabled") === false - ) { - this.setOutputData(0, tex); - return; - } - - var width = tex.width; - var height = tex.height; - - var texture_info = { - format: tex.format, - type: tex.type, - minFilter: GL.LINEAR, - magFilter: GL.LINEAR, - wrap: gl.CLAMP_TO_EDGE - }; - var type = LGraphTexture.getTextureType( - this.properties.precision, - tex - ); - - var uniforms = this._uniforms; - var textures = this._textures; - - //cut - var shader = LGraphTextureGlow._cut_shader; - if (!shader) { - shader = LGraphTextureGlow._cut_shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureGlow.cut_pixel_shader - ); - } - - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.BLEND); - - uniforms.u_threshold = this.getInputOrProperty("threshold"); - var currentDestination = (textures[0] = GL.Texture.getTemporary( - width, - height, - texture_info - )); - tex.blit(currentDestination, shader.uniforms(uniforms)); - var currentSource = currentDestination; - - var iterations = this.getInputOrProperty("iterations"); - iterations = Math.clamp(iterations, 1, 16) | 0; - var texel_size = uniforms.u_texel_size; - var intensity = this.getInputOrProperty("intensity"); - - uniforms.u_intensity = 1; - uniforms.u_delta = this.properties.scale; //1 - - //downscale/upscale shader - var shader = LGraphTextureGlow._shader; - if (!shader) { - shader = LGraphTextureGlow._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureGlow.scale_pixel_shader - ); - } - - var i = 1; - //downscale - for (; i < iterations; i++) { - width = width >> 1; - if ((height | 0) > 1) { - height = height >> 1; - } - if (width < 2) { - break; - } - currentDestination = textures[i] = GL.Texture.getTemporary( - width, - height, - texture_info - ); - texel_size[0] = 1 / currentSource.width; - texel_size[1] = 1 / currentSource.height; - currentSource.blit( - currentDestination, - shader.uniforms(uniforms) - ); - currentSource = currentDestination; - } - - //average - if (this.isOutputConnected(2)) { - var average_texture = this._average_texture; - if ( - !average_texture || - average_texture.type != tex.type || - average_texture.format != tex.format - ) { - average_texture = this._average_texture = new GL.Texture( - 1, - 1, - { - type: tex.type, - format: tex.format, - filter: gl.LINEAR - } - ); - } - texel_size[0] = 1 / currentSource.width; - texel_size[1] = 1 / currentSource.height; - uniforms.u_intensity = intensity; - uniforms.u_delta = 1; - currentSource.blit(average_texture, shader.uniforms(uniforms)); - this.setOutputData(2, average_texture); - } - - //upscale and blend - gl.enable(gl.BLEND); - gl.blendFunc(gl.ONE, gl.ONE); - uniforms.u_intensity = this.getInputOrProperty("persistence"); - uniforms.u_delta = 0.5; - - for ( - i -= 2; - i >= 0; - i-- // i-=2 => -1 to point to last element in array, -1 to go to texture above - ) { - currentDestination = textures[i]; - textures[i] = null; - texel_size[0] = 1 / currentSource.width; - texel_size[1] = 1 / currentSource.height; - currentSource.blit( - currentDestination, - shader.uniforms(uniforms) - ); - GL.Texture.releaseTemporary(currentSource); - currentSource = currentDestination; - } - gl.disable(gl.BLEND); - - //glow - if (this.isOutputConnected(1)) { - var glow_texture = this._glow_texture; - if ( - !glow_texture || - glow_texture.width != tex.width || - glow_texture.height != tex.height || - glow_texture.type != type || - glow_texture.format != tex.format - ) { - glow_texture = this._glow_texture = new GL.Texture( - tex.width, - tex.height, - { type: type, format: tex.format, filter: gl.LINEAR } - ); - } - currentSource.blit(glow_texture); - this.setOutputData(1, glow_texture); - } - - //final composition - if (this.isOutputConnected(0)) { - var final_texture = this._final_texture; - if ( - !final_texture || - final_texture.width != tex.width || - final_texture.height != tex.height || - final_texture.type != type || - final_texture.format != tex.format - ) { - final_texture = this._final_texture = new GL.Texture( - tex.width, - tex.height, - { type: type, format: tex.format, filter: gl.LINEAR } - ); - } - - var dirt_texture = this.getInputData(1); - var dirt_factor = this.getInputOrProperty("dirt_factor"); - - uniforms.u_intensity = intensity; - - shader = dirt_texture - ? LGraphTextureGlow._dirt_final_shader - : LGraphTextureGlow._final_shader; - if (!shader) { - if (dirt_texture) { - shader = LGraphTextureGlow._dirt_final_shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureGlow.final_pixel_shader, - { USE_DIRT: "" } - ); - } else { - shader = LGraphTextureGlow._final_shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureGlow.final_pixel_shader - ); - } - } - - final_texture.drawTo(function() { - tex.bind(0); - currentSource.bind(1); - if (dirt_texture) { - shader.setUniform("u_dirt_factor", dirt_factor); - shader.setUniform( - "u_dirt_texture", - dirt_texture.bind(2) - ); - } - shader.toViewport(uniforms); - }); - this.setOutputData(0, final_texture); - } - - GL.Texture.releaseTemporary(currentSource); - }; - - LGraphTextureGlow.cut_pixel_shader = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_threshold;\n\ - void main() {\n\ - gl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\ - }"; - - LGraphTextureGlow.scale_pixel_shader = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_texel_size;\n\ - uniform float u_delta;\n\ - uniform float u_intensity;\n\ - \n\ - vec4 sampleBox(vec2 uv) {\n\ - vec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\ - vec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\ - return s * 0.25;\n\ - }\n\ - void main() {\n\ - gl_FragColor = u_intensity * sampleBox( v_coord );\n\ - }"; - - LGraphTextureGlow.final_pixel_shader = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_glow_texture;\n\ - #ifdef USE_DIRT\n\ - uniform sampler2D u_dirt_texture;\n\ - #endif\n\ - uniform vec2 u_texel_size;\n\ - uniform float u_delta;\n\ - uniform float u_intensity;\n\ - uniform float u_dirt_factor;\n\ - \n\ - vec4 sampleBox(vec2 uv) {\n\ - vec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\ - vec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\ - return s * 0.25;\n\ - }\n\ - void main() {\n\ - vec4 glow = sampleBox( v_coord );\n\ - #ifdef USE_DIRT\n\ - glow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\ - #endif\n\ - gl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\ - }"; - - LiteGraph.registerNodeType("texture/glow", LGraphTextureGlow); - - // Texture Filter ***************************************** - function LGraphTextureKuwaharaFilter() { - this.addInput("Texture", "Texture"); - this.addOutput("Filtered", "Texture"); - this.properties = { intensity: 1, radius: 5 }; - } - - LGraphTextureKuwaharaFilter.title = "Kuwahara Filter"; - LGraphTextureKuwaharaFilter.desc = - "Filters a texture giving an artistic oil canvas painting"; - - LGraphTextureKuwaharaFilter.max_radius = 10; - LGraphTextureKuwaharaFilter._shaders = []; - - LGraphTextureKuwaharaFilter.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var temp = this._temp_texture; - - if ( - !temp || - temp.width != tex.width || - temp.height != tex.height || - temp.type != tex.type - ) { - this._temp_texture = new GL.Texture(tex.width, tex.height, { - type: tex.type, - format: gl.RGBA, - filter: gl.LINEAR - }); - } - - //iterations - var radius = this.properties.radius; - radius = Math.min( - Math.floor(radius), - LGraphTextureKuwaharaFilter.max_radius - ); - if (radius == 0) { - //skip blurring - this.setOutputData(0, tex); - return; - } - - var intensity = this.properties.intensity; - - //blur sometimes needs an aspect correction - var aspect = LiteGraph.camera_aspect; - if (!aspect && window.gl !== undefined) { - aspect = gl.canvas.height / gl.canvas.width; - } - if (!aspect) { - aspect = 1; - } - aspect = this.properties.preserve_aspect ? aspect : 1; - - if (!LGraphTextureKuwaharaFilter._shaders[radius]) { - LGraphTextureKuwaharaFilter._shaders[radius] = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureKuwaharaFilter.pixel_shader, - { RADIUS: radius.toFixed(0) } - ); - } - - var shader = LGraphTextureKuwaharaFilter._shaders[radius]; - var mesh = GL.Mesh.getScreenQuad(); - tex.bind(0); - - this._temp_texture.drawTo(function() { - shader - .uniforms({ - u_texture: 0, - u_intensity: intensity, - u_resolution: [tex.width, tex.height], - u_iResolution: [1 / tex.width, 1 / tex.height] - }) - .draw(mesh); - }); - - this.setOutputData(0, this._temp_texture); - }; - - //from https://www.shadertoy.com/view/MsXSz4 - LGraphTextureKuwaharaFilter.pixel_shader = - "\n\ -precision highp float;\n\ -varying vec2 v_coord;\n\ -uniform sampler2D u_texture;\n\ -uniform float u_intensity;\n\ -uniform vec2 u_resolution;\n\ -uniform vec2 u_iResolution;\n\ -#ifndef RADIUS\n\ - #define RADIUS 7\n\ -#endif\n\ -void main() {\n\ -\n\ - const int radius = RADIUS;\n\ - vec2 fragCoord = v_coord;\n\ - vec2 src_size = u_iResolution;\n\ - vec2 uv = v_coord;\n\ - float n = float((radius + 1) * (radius + 1));\n\ - int i;\n\ - int j;\n\ - vec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\ - vec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\ - vec3 c;\n\ - \n\ - for (int j = -radius; j <= 0; ++j) {\n\ - for (int i = -radius; i <= 0; ++i) {\n\ - c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ - m0 += c;\n\ - s0 += c * c;\n\ - }\n\ - }\n\ - \n\ - for (int j = -radius; j <= 0; ++j) {\n\ - for (int i = 0; i <= radius; ++i) {\n\ - c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ - m1 += c;\n\ - s1 += c * c;\n\ - }\n\ - }\n\ - \n\ - for (int j = 0; j <= radius; ++j) {\n\ - for (int i = 0; i <= radius; ++i) {\n\ - c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ - m2 += c;\n\ - s2 += c * c;\n\ - }\n\ - }\n\ - \n\ - for (int j = 0; j <= radius; ++j) {\n\ - for (int i = -radius; i <= 0; ++i) {\n\ - c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ - m3 += c;\n\ - s3 += c * c;\n\ - }\n\ - }\n\ - \n\ - float min_sigma2 = 1e+2;\n\ - m0 /= n;\n\ - s0 = abs(s0 / n - m0 * m0);\n\ - \n\ - float sigma2 = s0.r + s0.g + s0.b;\n\ - if (sigma2 < min_sigma2) {\n\ - min_sigma2 = sigma2;\n\ - gl_FragColor = vec4(m0, 1.0);\n\ - }\n\ - \n\ - m1 /= n;\n\ - s1 = abs(s1 / n - m1 * m1);\n\ - \n\ - sigma2 = s1.r + s1.g + s1.b;\n\ - if (sigma2 < min_sigma2) {\n\ - min_sigma2 = sigma2;\n\ - gl_FragColor = vec4(m1, 1.0);\n\ - }\n\ - \n\ - m2 /= n;\n\ - s2 = abs(s2 / n - m2 * m2);\n\ - \n\ - sigma2 = s2.r + s2.g + s2.b;\n\ - if (sigma2 < min_sigma2) {\n\ - min_sigma2 = sigma2;\n\ - gl_FragColor = vec4(m2, 1.0);\n\ - }\n\ - \n\ - m3 /= n;\n\ - s3 = abs(s3 / n - m3 * m3);\n\ - \n\ - sigma2 = s3.r + s3.g + s3.b;\n\ - if (sigma2 < min_sigma2) {\n\ - min_sigma2 = sigma2;\n\ - gl_FragColor = vec4(m3, 1.0);\n\ - }\n\ -}\n\ -"; - - LiteGraph.registerNodeType( - "texture/kuwahara", - LGraphTextureKuwaharaFilter - ); - - // Texture ***************************************** - function LGraphTextureXDoGFilter() { - this.addInput("Texture", "Texture"); - this.addOutput("Filtered", "Texture"); - this.properties = { - sigma: 1.4, - k: 1.6, - p: 21.7, - epsilon: 79, - phi: 0.017 - }; - } - - LGraphTextureXDoGFilter.title = "XDoG Filter"; - LGraphTextureXDoGFilter.desc = - "Filters a texture giving an artistic ink style"; - - LGraphTextureXDoGFilter.max_radius = 10; - LGraphTextureXDoGFilter._shaders = []; - - LGraphTextureXDoGFilter.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var temp = this._temp_texture; - if ( - !temp || - temp.width != tex.width || - temp.height != tex.height || - temp.type != tex.type - ) { - this._temp_texture = new GL.Texture(tex.width, tex.height, { - type: tex.type, - format: gl.RGBA, - filter: gl.LINEAR - }); - } - - if (!LGraphTextureXDoGFilter._xdog_shader) { - LGraphTextureXDoGFilter._xdog_shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureXDoGFilter.xdog_pixel_shader - ); - } - var shader = LGraphTextureXDoGFilter._xdog_shader; - var mesh = GL.Mesh.getScreenQuad(); - - var sigma = this.properties.sigma; - var k = this.properties.k; - var p = this.properties.p; - var epsilon = this.properties.epsilon; - var phi = this.properties.phi; - tex.bind(0); - this._temp_texture.drawTo(function() { - shader - .uniforms({ - src: 0, - sigma: sigma, - k: k, - p: p, - epsilon: epsilon, - phi: phi, - cvsWidth: tex.width, - cvsHeight: tex.height - }) - .draw(mesh); - }); - - this.setOutputData(0, this._temp_texture); - }; - - //from https://github.com/RaymondMcGuire/GPU-Based-Image-Processing-Tools/blob/master/lib_webgl/scripts/main.js - LGraphTextureXDoGFilter.xdog_pixel_shader = - "\n\ -precision highp float;\n\ -uniform sampler2D src;\n\n\ -uniform float cvsHeight;\n\ -uniform float cvsWidth;\n\n\ -uniform float sigma;\n\ -uniform float k;\n\ -uniform float p;\n\ -uniform float epsilon;\n\ -uniform float phi;\n\ -varying vec2 v_coord;\n\n\ -float cosh(float val)\n\ -{\n\ - float tmp = exp(val);\n\ - float cosH = (tmp + 1.0 / tmp) / 2.0;\n\ - return cosH;\n\ -}\n\n\ -float tanh(float val)\n\ -{\n\ - float tmp = exp(val);\n\ - float tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\ - return tanH;\n\ -}\n\n\ -float sinh(float val)\n\ -{\n\ - float tmp = exp(val);\n\ - float sinH = (tmp - 1.0 / tmp) / 2.0;\n\ - return sinH;\n\ -}\n\n\ -void main(void){\n\ - vec3 destColor = vec3(0.0);\n\ - float tFrag = 1.0 / cvsHeight;\n\ - float sFrag = 1.0 / cvsWidth;\n\ - vec2 Frag = vec2(sFrag,tFrag);\n\ - vec2 uv = gl_FragCoord.st;\n\ - float twoSigmaESquared = 2.0 * sigma * sigma;\n\ - float twoSigmaRSquared = twoSigmaESquared * k * k;\n\ - int halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\ - const int MAX_NUM_ITERATION = 99999;\n\ - vec2 sum = vec2(0.0);\n\ - vec2 norm = vec2(0.0);\n\n\ - for(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\ - int i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\ - int j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\ - float d = length(vec2(i,j));\n\ - vec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\ - exp( -d * d / twoSigmaRSquared ));\n\n\ - vec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\ - norm += kernel;\n\ - sum += kernel * L;\n\ - }\n\n\ - sum /= norm;\n\n\ - float H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\ - float edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\ - destColor = vec3(edge);\n\ - gl_FragColor = vec4(destColor, 1.0);\n\ -}"; - - LiteGraph.registerNodeType("texture/xDoG", LGraphTextureXDoGFilter); - - // Texture Webcam ***************************************** - function LGraphTextureWebcam() { - this.addOutput("Webcam", "Texture"); - this.properties = { texture_name: "", facingMode: "user" }; - this.boxcolor = "black"; - this.version = 0; - } - - LGraphTextureWebcam.title = "Webcam"; - LGraphTextureWebcam.desc = "Webcam texture"; - - LGraphTextureWebcam.is_webcam_open = false; - - LGraphTextureWebcam.prototype.openStream = function() { - if (!navigator.getUserMedia) { - //console.log('getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags'); - return; - } - - this._waiting_confirmation = true; - - // Not showing vendor prefixes. - var constraints = { - audio: false, - video: { facingMode: this.properties.facingMode } - }; - navigator.mediaDevices - .getUserMedia(constraints) - .then(this.streamReady.bind(this)) - .catch(onFailSoHard); - - var that = this; - function onFailSoHard(e) { - LGraphTextureWebcam.is_webcam_open = false; - console.log("Webcam rejected", e); - that._webcam_stream = false; - that.boxcolor = "red"; - that.trigger("stream_error"); - } - }; - - LGraphTextureWebcam.prototype.closeStream = function() { - if (this._webcam_stream) { - var tracks = this._webcam_stream.getTracks(); - if (tracks.length) { - for (var i = 0; i < tracks.length; ++i) { - tracks[i].stop(); - } - } - LGraphTextureWebcam.is_webcam_open = false; - this._webcam_stream = null; - this._video = null; - this.boxcolor = "black"; - this.trigger("stream_closed"); - } - }; - - LGraphTextureWebcam.prototype.streamReady = function(localMediaStream) { - this._webcam_stream = localMediaStream; - //this._waiting_confirmation = false; - this.boxcolor = "green"; - var video = this._video; - if (!video) { - video = document.createElement("video"); - video.autoplay = true; - video.srcObject = localMediaStream; - this._video = video; - //document.body.appendChild( video ); //debug - //when video info is loaded (size and so) - video.onloadedmetadata = function(e) { - // Ready to go. Do some stuff. - LGraphTextureWebcam.is_webcam_open = true; - console.log(e); - }; - } - this.trigger("stream_ready", video); - }; - - LGraphTextureWebcam.prototype.onPropertyChanged = function( - name, - value - ) { - if (name == "facingMode") { - this.properties.facingMode = value; - this.closeStream(); - this.openStream(); - } - }; - - LGraphTextureWebcam.prototype.onRemoved = function() { - if (!this._webcam_stream) { - return; - } - - var tracks = this._webcam_stream.getTracks(); - if (tracks.length) { - for (var i = 0; i < tracks.length; ++i) { - tracks[i].stop(); - } - } - - this._webcam_stream = null; - this._video = null; - }; - - LGraphTextureWebcam.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed || this.size[1] <= 20) { - return; - } - - if (!this._video) { - return; - } - - //render to graph canvas - ctx.save(); - if (!ctx.webgl) { - //reverse image - ctx.drawImage(this._video, 0, 0, this.size[0], this.size[1]); - } else { - if (this._video_texture) { - ctx.drawImage( - this._video_texture, - 0, - 0, - this.size[0], - this.size[1] - ); - } - } - ctx.restore(); - }; - - LGraphTextureWebcam.prototype.onExecute = function() { - if (this._webcam_stream == null && !this._waiting_confirmation) { - this.openStream(); - } - - if (!this._video || !this._video.videoWidth) { - return; - } - - var width = this._video.videoWidth; - var height = this._video.videoHeight; - - var temp = this._video_texture; - if (!temp || temp.width != width || temp.height != height) { - this._video_texture = new GL.Texture(width, height, { - format: gl.RGB, - filter: gl.LINEAR - }); - } - - this._video_texture.uploadImage(this._video); - this._video_texture.version = ++this.version; - - if (this.properties.texture_name) { - var container = LGraphTexture.getTexturesContainer(); - container[this.properties.texture_name] = this._video_texture; - } - - this.setOutputData(0, this._video_texture); - for (var i = 1; i < this.outputs.length; ++i) { - if (!this.outputs[i]) { - continue; - } - switch (this.outputs[i].name) { - case "width": - this.setOutputData(i, this._video.videoWidth); - break; - case "height": - this.setOutputData(i, this._video.videoHeight); - break; - } - } - }; - - LGraphTextureWebcam.prototype.onGetOutputs = function() { - return [ - ["width", "number"], - ["height", "number"], - ["stream_ready", LiteGraph.EVENT], - ["stream_closed", LiteGraph.EVENT], - ["stream_error", LiteGraph.EVENT] - ]; - }; - - LiteGraph.registerNodeType("texture/webcam", LGraphTextureWebcam); - - //from https://github.com/spite/Wagner - function LGraphLensFX() { - this.addInput("in", "Texture"); - this.addInput("f", "number"); - this.addOutput("out", "Texture"); - this.properties = { - enabled: true, - factor: 1, - precision: LGraphTexture.LOW - }; - - this._uniforms = { u_texture: 0, u_factor: 1 }; - } - - LGraphLensFX.title = "Lens FX"; - LGraphLensFX.desc = "distortion and chromatic aberration"; - - LGraphLensFX.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphLensFX.prototype.onGetInputs = function() { - return [["enabled", "boolean"]]; - }; - - LGraphLensFX.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - if ( - this.properties.precision === LGraphTexture.PASS_THROUGH || - this.getInputOrProperty("enabled") === false - ) { - this.setOutputData(0, tex); - return; - } - - var temp = this._temp_texture; - if ( - !temp || - temp.width != tex.width || - temp.height != tex.height || - temp.type != tex.type - ) { - temp = this._temp_texture = new GL.Texture( - tex.width, - tex.height, - { type: tex.type, format: gl.RGBA, filter: gl.LINEAR } - ); - } - - var shader = LGraphLensFX._shader; - if (!shader) { - shader = LGraphLensFX._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphLensFX.pixel_shader - ); - } - - var factor = this.getInputData(1); - if (factor == null) { - factor = this.properties.factor; - } - - var uniforms = this._uniforms; - uniforms.u_factor = factor; - - //apply shader - gl.disable(gl.DEPTH_TEST); - temp.drawTo(function() { - tex.bind(0); - shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad()); - }); - - this.setOutputData(0, temp); - }; - - LGraphLensFX.pixel_shader = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_factor;\n\ - vec2 barrelDistortion(vec2 coord, float amt) {\n\ - vec2 cc = coord - 0.5;\n\ - float dist = dot(cc, cc);\n\ - return coord + cc * dist * amt;\n\ - }\n\ - \n\ - float sat( float t )\n\ - {\n\ - return clamp( t, 0.0, 1.0 );\n\ - }\n\ - \n\ - float linterp( float t ) {\n\ - return sat( 1.0 - abs( 2.0*t - 1.0 ) );\n\ - }\n\ - \n\ - float remap( float t, float a, float b ) {\n\ - return sat( (t - a) / (b - a) );\n\ - }\n\ - \n\ - vec4 spectrum_offset( float t ) {\n\ - vec4 ret;\n\ - float lo = step(t,0.5);\n\ - float hi = 1.0-lo;\n\ - float w = linterp( remap( t, 1.0/6.0, 5.0/6.0 ) );\n\ - ret = vec4(lo,1.0,hi, 1.) * vec4(1.0-w, w, 1.0-w, 1.);\n\ - \n\ - return pow( ret, vec4(1.0/2.2) );\n\ - }\n\ - \n\ - const float max_distort = 2.2;\n\ - const int num_iter = 12;\n\ - const float reci_num_iter_f = 1.0 / float(num_iter);\n\ - \n\ - void main()\n\ - { \n\ - vec2 uv=v_coord;\n\ - vec4 sumcol = vec4(0.0);\n\ - vec4 sumw = vec4(0.0); \n\ - for ( int i=0; i= res)\n\ - break;\n\ - iCount++;\n\ - }\n\ - float nf = n/normK;\n\ - return nf*nf*nf*nf;\n\ - }\n\ - void main() {\n\ - vec2 uv = v_coord * u_scale * u_viewport + u_offset * u_scale;\n\ - vec4 color = vec4( pNoise( uv, u_octaves ) * u_amplitude );\n\ - gl_FragColor = color;\n\ - }"; - - LiteGraph.registerNodeType("texture/perlin", LGraphTexturePerlin); - - function LGraphTextureCanvas2D() { - this.addInput("v"); - this.addOutput("out", "Texture"); - this.properties = { - code: LGraphTextureCanvas2D.default_code, - width: 512, - height: 512, - clear: true, - precision: LGraphTexture.DEFAULT, - use_html_canvas: false - }; - this._func = null; - this._temp_texture = null; - this.compileCode(); - } - - LGraphTextureCanvas2D.title = "Canvas2D"; - LGraphTextureCanvas2D.desc = "Executes Canvas2D code inside a texture or the viewport."; - LGraphTextureCanvas2D.help = "Set width and height to 0 to match viewport size."; - - LGraphTextureCanvas2D.default_code = "//vars: canvas,ctx,time\nctx.fillStyle='red';\nctx.fillRect(0,0,50,50);\n"; - - LGraphTextureCanvas2D.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }, - code: { type: "code" }, - width: { type: "Number", precision: 0, step: 1 }, - height: { type: "Number", precision: 0, step: 1 } - }; - - LGraphTextureCanvas2D.prototype.onPropertyChanged = function( name, value ) { - if (name == "code" ) - this.compileCode( value ); - } - - LGraphTextureCanvas2D.prototype.compileCode = function( code ) { - this._func = null; - if( !LiteGraph.allow_scripts ) - return; - - try { - this._func = new Function( "canvas", "ctx", "time", "script","v", code ); - this.boxcolor = "#00FF00"; - } catch (err) { - this.boxcolor = "#FF0000"; - console.error("Error parsing script"); - console.error(err); - } - }; - - LGraphTextureCanvas2D.prototype.onExecute = function() { - var func = this._func; - if (!func || !this.isOutputConnected(0)) { - return; - } - this.executeDraw( func ); - } - - LGraphTextureCanvas2D.prototype.executeDraw = function( func_context ) { - - var width = this.properties.width || gl.canvas.width; - var height = this.properties.height || gl.canvas.height; - var temp = this._temp_texture; - var type = LGraphTexture.getTextureType( this.properties.precision ); - if (!temp || temp.width != width || temp.height != height || temp.type != type ) { - temp = this._temp_texture = new GL.Texture(width, height, { - format: gl.RGBA, - filter: gl.LINEAR, - type: type - }); - } - - var v = this.getInputData(0); - - var properties = this.properties; - var that = this; - var time = this.graph.getTime(); - var ctx = gl; - var canvas = gl.canvas; - if( this.properties.use_html_canvas || !global.enableWebGLCanvas ) - { - if(!this._canvas) - { - canvas = this._canvas = createCanvas(width.height); - ctx = this._ctx = canvas.getContext("2d"); - } - else - { - canvas = this._canvas; - ctx = this._ctx; - } - canvas.width = width; - canvas.height = height; - } - - if(ctx == gl) //using Canvas2DtoWebGL - temp.drawTo(function() { - gl.start2D(); - if(properties.clear) - { - gl.clearColor(0,0,0,0); - gl.clear( gl.COLOR_BUFFER_BIT ); - } - - try { - if (func_context.draw) { - func_context.draw.call(that, canvas, ctx, time, func_context, v); - } else { - func_context.call(that, canvas, ctx, time, func_context,v); - } - that.boxcolor = "#00FF00"; - } catch (err) { - that.boxcolor = "#FF0000"; - console.error("Error executing script"); - console.error(err); - } - gl.finish2D(); - }); - else //rendering to offscren canvas and uploading to texture - { - if(properties.clear) - ctx.clearRect(0,0,canvas.width,canvas.height); - - try { - if (func_context.draw) { - func_context.draw.call(this, canvas, ctx, time, func_context, v); - } else { - func_context.call(this, canvas, ctx, time, func_context,v); - } - this.boxcolor = "#00FF00"; - } catch (err) { - this.boxcolor = "#FF0000"; - console.error("Error executing script"); - console.error(err); - } - temp.uploadImage( canvas ); - } - - this.setOutputData(0, temp); - }; - - LiteGraph.registerNodeType("texture/canvas2D", LGraphTextureCanvas2D); - - // To do chroma keying ***************** - - function LGraphTextureMatte() { - this.addInput("in", "Texture"); - - this.addOutput("out", "Texture"); - this.properties = { - key_color: vec3.fromValues(0, 1, 0), - threshold: 0.8, - slope: 0.2, - precision: LGraphTexture.DEFAULT - }; - } - - LGraphTextureMatte.title = "Matte"; - LGraphTextureMatte.desc = "Extracts background"; - - LGraphTextureMatte.widgets_info = { - key_color: { widget: "color" }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureMatte.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var tex = this.getInputData(0); - - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, tex); - return; - } - - if (!tex) { - return; - } - - this._tex = LGraphTexture.getTargetTexture( - tex, - this._tex, - this.properties.precision - ); - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - - if (!this._uniforms) { - this._uniforms = { - u_texture: 0, - u_key_color: this.properties.key_color, - u_threshold: 1, - u_slope: 1 - }; - } - var uniforms = this._uniforms; - - var mesh = Mesh.getScreenQuad(); - var shader = LGraphTextureMatte._shader; - if (!shader) { - shader = LGraphTextureMatte._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureMatte.pixel_shader - ); - } - - uniforms.u_key_color = this.properties.key_color; - uniforms.u_threshold = this.properties.threshold; - uniforms.u_slope = this.properties.slope; - - this._tex.drawTo(function() { - tex.bind(0); - shader.uniforms(uniforms).draw(mesh); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureMatte.pixel_shader = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec3 u_key_color;\n\ - uniform float u_threshold;\n\ - uniform float u_slope;\n\ - \n\ - void main() {\n\ - vec3 color = texture2D( u_texture, v_coord ).xyz;\n\ - float diff = length( normalize(color) - normalize(u_key_color) );\n\ - float edge = u_threshold * (1.0 - u_slope);\n\ - float alpha = smoothstep( edge, u_threshold, diff);\n\ - gl_FragColor = vec4( color, alpha );\n\ - }"; - - LiteGraph.registerNodeType("texture/matte", LGraphTextureMatte); - - //*********************************** - function LGraphCubemapToTexture2D() { - this.addInput("in", "texture"); - this.addInput("yaw", "number"); - this.addOutput("out", "texture"); - this.properties = { yaw: 0 }; - } - - LGraphCubemapToTexture2D.title = "CubemapToTexture2D"; - LGraphCubemapToTexture2D.desc = "Transforms a CUBEMAP texture into a TEXTURE2D in Polar Representation"; - - LGraphCubemapToTexture2D.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) - return; - - var tex = this.getInputData(0); - if ( !tex || tex.texture_type != GL.TEXTURE_CUBE_MAP ) - return; - if( this._last_tex && ( this._last_tex.height != tex.height || this._last_tex.type != tex.type )) - this._last_tex = null; - var yaw = this.getInputOrProperty("yaw"); - this._last_tex = GL.Texture.cubemapToTexture2D( tex, tex.height, this._last_tex, true, yaw ); - this.setOutputData( 0, this._last_tex ); - }; - - LiteGraph.registerNodeType( "texture/cubemapToTexture2D", LGraphCubemapToTexture2D ); -})(this); - -(function(global) { - var LiteGraph = global.LiteGraph; + //set uniforms + if(this.inputs) + for (var i = 0; i < this.inputs.length; ++i) { + var info = this.getInputInfo(i); + var data = this.getInputData(i); + if (data == null) { + continue; + } - var view_matrix = new Float32Array(16); - var projection_matrix = new Float32Array(16); - var viewprojection_matrix = new Float32Array(16); - var model_matrix = new Float32Array(16); - var global_uniforms = { - u_view: view_matrix, - u_projection: projection_matrix, - u_viewprojection: viewprojection_matrix, - u_model: model_matrix + if (data.constructor === GL.Texture) { + data.bind(tex_slot); + if (!in_tex) { + in_tex = data; + } + data = tex_slot; + tex_slot++; + } + shader.setUniform(info.name, data); //data is tex_slot + } + + var uniforms = this._uniforms; + var type = LGraphTexture.getTextureType( this.properties.precision, in_tex ); + + //render to texture + var w = this.properties.width | 0; + var h = this.properties.height | 0; + if (w == 0) { + w = in_tex ? in_tex.width : gl.canvas.width; + } + if (h == 0) { + h = in_tex ? in_tex.height : gl.canvas.height; + } + uniforms.texSize[0] = w; + uniforms.texSize[1] = h; + uniforms.time = this.graph.getTime(); + uniforms.u_value = this.properties.u_value; + uniforms.u_color.set( this.properties.u_color ); + + if ( !this._tex || this._tex.type != type || this._tex.width != w || this._tex.height != h ) { + this._tex = new GL.Texture(w, h, { type: type, format: gl.RGBA, filter: gl.LINEAR }); + } + var tex = this._tex; + tex.drawTo(function() { + shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad()); + }); + + this.setOutputData(0, this._tex); }; - LiteGraph.LGraphRender = { - onRequestCameraMatrices: null //overwrite with your 3D engine specifics, it will receive (view_matrix, projection_matrix,viewprojection_matrix) and must be filled - }; + LGraphTextureShader.pixel_shader = +"precision highp float;\n\ +\n\ +varying vec2 v_coord;\n\ +uniform float time; //time in seconds\n\ +uniform vec2 texSize; //tex resolution\n\ +uniform float u_value;\n\ +uniform vec4 u_color;\n\n\ +void main() {\n\ + vec2 uv = v_coord;\n\ + vec3 color = vec3(0.0);\n\ + //your code here\n\ + color.xy=uv;\n\n\ + gl_FragColor = vec4(color, 1.0);\n\ +}\n\ +"; - function generateGeometryId() { - return (Math.random() * 100000)|0; + LiteGraph.registerNodeType("texture/shader", LGraphTextureShader); + + // Texture Scale Offset + + function LGraphTextureScaleOffset() { + this.addInput("in", "Texture"); + this.addInput("scale", "vec2"); + this.addInput("offset", "vec2"); + this.addOutput("out", "Texture"); + this.properties = { + offset: vec2.fromValues(0, 0), + scale: vec2.fromValues(1, 1), + precision: LGraphTexture.DEFAULT + }; } - function LGraphPoints3D() { + LGraphTextureScaleOffset.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; - this.addInput("obj", ""); - this.addInput("radius", "number"); + LGraphTextureScaleOffset.title = "Scale/Offset"; + LGraphTextureScaleOffset.desc = "Applies an scaling and offseting"; - this.addOutput("out", "geometry"); - this.addOutput("points", "[vec3]"); + LGraphTextureScaleOffset.prototype.onExecute = function() { + var tex = this.getInputData(0); + + if (!this.isOutputConnected(0) || !tex) { + return; + } //saves work + + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, tex); + return; + } + + var width = tex.width; + var height = tex.height; + var type = this.precision === LGraphTexture.LOW ? gl.UNSIGNED_BYTE : gl.HIGH_PRECISION_FORMAT; + if (this.precision === LGraphTexture.DEFAULT) { + type = tex.type; + } + + if ( + !this._tex || + this._tex.width != width || + this._tex.height != height || + this._tex.type != type + ) { + this._tex = new GL.Texture(width, height, { + type: type, + format: gl.RGBA, + filter: gl.LINEAR + }); + } + + var shader = this._shader; + + if (!shader) { + shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureScaleOffset.pixel_shader + ); + } + + var scale = this.getInputData(1); + if (scale) { + this.properties.scale[0] = scale[0]; + this.properties.scale[1] = scale[1]; + } else { + scale = this.properties.scale; + } + + var offset = this.getInputData(2); + if (offset) { + this.properties.offset[0] = offset[0]; + this.properties.offset[1] = offset[1]; + } else { + offset = this.properties.offset; + } + + this._tex.drawTo(function() { + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + gl.disable(gl.BLEND); + tex.bind(0); + var mesh = Mesh.getScreenQuad(); + shader + .uniforms({ + u_texture: 0, + u_scale: scale, + u_offset: offset + }) + .draw(mesh); + }); + + this.setOutputData(0, this._tex); + }; + + LGraphTextureScaleOffset.pixel_shader = + "precision highp float;\n\ + \n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_textureB;\n\ + varying vec2 v_coord;\n\ + uniform vec2 u_scale;\n\ + uniform vec2 u_offset;\n\ + \n\ + void main() {\n\ + vec2 uv = v_coord;\n\ + uv = uv / u_scale - u_offset;\n\ + gl_FragColor = texture2D(u_texture, uv);\n\ + }\n\ + "; + + LiteGraph.registerNodeType( + "texture/scaleOffset", + LGraphTextureScaleOffset + ); + + // Warp (distort a texture) ************************* + + function LGraphTextureWarp() { + this.addInput("in", "Texture"); + this.addInput("warp", "Texture"); + this.addInput("factor", "number"); + this.addOutput("out", "Texture"); this.properties = { - radius: 1, - num_points: 4096, - generate_normals: true, - regular: false, - mode: LGraphPoints3D.SPHERE, - force_update: false + factor: 0.01, + scale: [1,1], + offset: [0,0], + precision: LGraphTexture.DEFAULT }; - this.points = new Float32Array( this.properties.num_points * 3 ); - this.normals = new Float32Array( this.properties.num_points * 3 ); - this.must_update = true; - this.version = 0; - - var that = this; - this.addWidget("button","update",null, function(){ that.must_update = true; }); - - this.geometry = { - vertices: null, - _id: generateGeometryId() - } - - this._old_obj = null; - this._last_radius = null; + this._uniforms = { + u_texture: 0, + u_textureB: 1, + u_factor: 1, + u_scale: vec2.create(), + u_offset: vec2.create() + }; } - global.LGraphPoints3D = LGraphPoints3D; - - LGraphPoints3D.RECTANGLE = 1; - LGraphPoints3D.CIRCLE = 2; - - LGraphPoints3D.CUBE = 10; - LGraphPoints3D.SPHERE = 11; - LGraphPoints3D.HEMISPHERE = 12; - LGraphPoints3D.INSIDE_SPHERE = 13; - - LGraphPoints3D.OBJECT = 20; - LGraphPoints3D.OBJECT_UNIFORMLY = 21; - LGraphPoints3D.OBJECT_INSIDE = 22; - - LGraphPoints3D.MODE_VALUES = { "rectangle":LGraphPoints3D.RECTANGLE, "circle":LGraphPoints3D.CIRCLE, "cube":LGraphPoints3D.CUBE, "sphere":LGraphPoints3D.SPHERE, "hemisphere":LGraphPoints3D.HEMISPHERE, "inside_sphere":LGraphPoints3D.INSIDE_SPHERE, "object":LGraphPoints3D.OBJECT, "object_uniformly":LGraphPoints3D.OBJECT_UNIFORMLY, "object_inside":LGraphPoints3D.OBJECT_INSIDE }; - - LGraphPoints3D.widgets_info = { - mode: { widget: "combo", values: LGraphPoints3D.MODE_VALUES } + LGraphTextureWarp.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } }; - LGraphPoints3D.title = "list of points"; - LGraphPoints3D.desc = "returns an array of points"; + LGraphTextureWarp.title = "Warp"; + LGraphTextureWarp.desc = "Texture warp operation"; - LGraphPoints3D.prototype.onPropertyChanged = function(name,value) + LGraphTextureWarp.prototype.onExecute = function() { + var tex = this.getInputData(0); + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, tex); + return; + } + + var texB = this.getInputData(1); + + var width = 512; + var height = 512; + var type = gl.UNSIGNED_BYTE; + if (tex) { + width = tex.width; + height = tex.height; + type = tex.type; + } else if (texB) { + width = texB.width; + height = texB.height; + type = texB.type; + } + + if (!tex && !this._tex) { + this._tex = new GL.Texture(width, height, { + type: + this.precision === LGraphTexture.LOW + ? gl.UNSIGNED_BYTE + : gl.HIGH_PRECISION_FORMAT, + format: gl.RGBA, + filter: gl.LINEAR + }); + } else { + this._tex = LGraphTexture.getTargetTexture( + tex || this._tex, + this._tex, + this.properties.precision + ); + } + + var shader = this._shader; + + if (!shader) { + shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureWarp.pixel_shader + ); + } + + var factor = this.getInputData(2); + if (factor != null) { + this.properties.factor = factor; + } else { + factor = parseFloat(this.properties.factor); + } + var uniforms = this._uniforms; + uniforms.u_factor = factor; + uniforms.u_scale.set( this.properties.scale ); + uniforms.u_offset.set( this.properties.offset ); + + this._tex.drawTo(function() { + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + gl.disable(gl.BLEND); + if (tex) { + tex.bind(0); + } + if (texB) { + texB.bind(1); + } + var mesh = Mesh.getScreenQuad(); + shader + .uniforms( uniforms ) + .draw(mesh); + }); + + this.setOutputData(0, this._tex); + }; + + LGraphTextureWarp.pixel_shader = + "precision highp float;\n\ + \n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_textureB;\n\ + varying vec2 v_coord;\n\ + uniform float u_factor;\n\ + uniform vec2 u_scale;\n\ + uniform vec2 u_offset;\n\ + \n\ + void main() {\n\ + vec2 uv = v_coord;\n\ + uv += ( texture2D(u_textureB, uv).rg - vec2(0.5)) * u_factor * u_scale + u_offset;\n\ + gl_FragColor = texture2D(u_texture, uv);\n\ + }\n\ + "; + + LiteGraph.registerNodeType("texture/warp", LGraphTextureWarp); + + //**************************************************** + + // Texture to Viewport ***************************************** + function LGraphTextureToViewport() { + this.addInput("Texture", "Texture"); + this.properties = { + additive: false, + antialiasing: false, + filter: true, + disable_alpha: false, + gamma: 1.0, + viewport: [0,0,1,1] + }; + this.size[0] = 130; + } + + LGraphTextureToViewport.title = "to Viewport"; + LGraphTextureToViewport.desc = "Texture to viewport"; + + LGraphTextureToViewport._prev_viewport = new Float32Array(4); + LGraphTextureToViewport.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (this.properties.disable_alpha) { + gl.disable(gl.BLEND); + } else { + gl.enable(gl.BLEND); + if (this.properties.additive) { + gl.blendFunc(gl.SRC_ALPHA, gl.ONE); + } else { + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + } + } + + gl.disable(gl.DEPTH_TEST); + var gamma = this.properties.gamma || 1.0; + if (this.isInputConnected(1)) { + gamma = this.getInputData(1); + } + + tex.setParameter( + gl.TEXTURE_MAG_FILTER, + this.properties.filter ? gl.LINEAR : gl.NEAREST + ); + + var old_viewport = LGraphTextureToViewport._prev_viewport; + old_viewport.set( gl.viewport_data ); + var new_view = this.properties.viewport; + gl.viewport( old_viewport[0] + old_viewport[2] * new_view[0], old_viewport[1] + old_viewport[3] * new_view[1], old_viewport[2] * new_view[2], old_viewport[3] * new_view[3] ); + var viewport = gl.getViewport(); //gl.getParameter(gl.VIEWPORT); + + if (this.properties.antialiasing) { + if (!LGraphTextureToViewport._shader) { + LGraphTextureToViewport._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureToViewport.aa_pixel_shader + ); + } + + var mesh = Mesh.getScreenQuad(); + tex.bind(0); + LGraphTextureToViewport._shader + .uniforms({ + u_texture: 0, + uViewportSize: [tex.width, tex.height], + u_igamma: 1 / gamma, + inverseVP: [1 / tex.width, 1 / tex.height] + }) + .draw(mesh); + } else { + if (gamma != 1.0) { + if (!LGraphTextureToViewport._gamma_shader) { + LGraphTextureToViewport._gamma_shader = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureToViewport.gamma_pixel_shader + ); + } + tex.toViewport(LGraphTextureToViewport._gamma_shader, { + u_texture: 0, + u_igamma: 1 / gamma + }); + } else { + tex.toViewport(); + } + } + + gl.viewport( old_viewport[0], old_viewport[1], old_viewport[2], old_viewport[3] ); + }; + + LGraphTextureToViewport.prototype.onGetInputs = function() { + return [["gamma", "number"]]; + }; + + LGraphTextureToViewport.aa_pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 uViewportSize;\n\ + uniform vec2 inverseVP;\n\ + uniform float u_igamma;\n\ + #define FXAA_REDUCE_MIN (1.0/ 128.0)\n\ + #define FXAA_REDUCE_MUL (1.0 / 8.0)\n\ + #define FXAA_SPAN_MAX 8.0\n\ + \n\ + /* from mitsuhiko/webgl-meincraft based on the code on geeks3d.com */\n\ + vec4 applyFXAA(sampler2D tex, vec2 fragCoord)\n\ + {\n\ + vec4 color = vec4(0.0);\n\ + /*vec2 inverseVP = vec2(1.0 / uViewportSize.x, 1.0 / uViewportSize.y);*/\n\ + vec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * inverseVP).xyz;\n\ + vec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * inverseVP).xyz;\n\ + vec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * inverseVP).xyz;\n\ + vec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * inverseVP).xyz;\n\ + vec3 rgbM = texture2D(tex, fragCoord * inverseVP).xyz;\n\ + vec3 luma = vec3(0.299, 0.587, 0.114);\n\ + float lumaNW = dot(rgbNW, luma);\n\ + float lumaNE = dot(rgbNE, luma);\n\ + float lumaSW = dot(rgbSW, luma);\n\ + float lumaSE = dot(rgbSE, luma);\n\ + float lumaM = dot(rgbM, luma);\n\ + float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n\ + float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\ + \n\ + vec2 dir;\n\ + dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n\ + dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\ + \n\ + float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\ + \n\ + float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n\ + dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * inverseVP;\n\ + \n\ + vec3 rgbA = 0.5 * (texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + \n\ + texture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz);\n\ + vec3 rgbB = rgbA * 0.5 + 0.25 * (texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + \n\ + texture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz);\n\ + \n\ + //return vec4(rgbA,1.0);\n\ + float lumaB = dot(rgbB, luma);\n\ + if ((lumaB < lumaMin) || (lumaB > lumaMax))\n\ + color = vec4(rgbA, 1.0);\n\ + else\n\ + color = vec4(rgbB, 1.0);\n\ + if(u_igamma != 1.0)\n\ + color.xyz = pow( color.xyz, vec3(u_igamma) );\n\ + return color;\n\ + }\n\ + \n\ + void main() {\n\ + gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\ + }\n\ + "; + + LGraphTextureToViewport.gamma_pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform float u_igamma;\n\ + void main() {\n\ + vec4 color = texture2D( u_texture, v_coord);\n\ + color.xyz = pow(color.xyz, vec3(u_igamma) );\n\ + gl_FragColor = color;\n\ + }\n\ + "; + + LiteGraph.registerNodeType( + "texture/toviewport", + LGraphTextureToViewport + ); + + // Texture Copy ***************************************** + function LGraphTextureCopy() { + this.addInput("Texture", "Texture"); + this.addOutput("", "Texture"); + this.properties = { + size: 0, + generate_mipmaps: false, + precision: LGraphTexture.DEFAULT + }; + } + + LGraphTextureCopy.title = "Copy"; + LGraphTextureCopy.desc = "Copy Texture"; + LGraphTextureCopy.widgets_info = { + size: { + widget: "combo", + values: [0, 32, 64, 128, 256, 512, 1024, 2048] + }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureCopy.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex && !this._temp_texture) { + return; + } + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + //copy the texture + if (tex) { + var width = tex.width; + var height = tex.height; + + if (this.properties.size != 0) { + width = this.properties.size; + height = this.properties.size; + } + + var temp = this._temp_texture; + + var type = tex.type; + if (this.properties.precision === LGraphTexture.LOW) { + type = gl.UNSIGNED_BYTE; + } else if (this.properties.precision === LGraphTexture.HIGH) { + type = gl.HIGH_PRECISION_FORMAT; + } + + if ( + !temp || + temp.width != width || + temp.height != height || + temp.type != type + ) { + var minFilter = gl.LINEAR; + if ( + this.properties.generate_mipmaps && + isPowerOfTwo(width) && + isPowerOfTwo(height) + ) { + minFilter = gl.LINEAR_MIPMAP_LINEAR; + } + this._temp_texture = new GL.Texture(width, height, { + type: type, + format: gl.RGBA, + minFilter: minFilter, + magFilter: gl.LINEAR + }); + } + tex.copyTo(this._temp_texture); + + if (this.properties.generate_mipmaps) { + this._temp_texture.bind(0); + gl.generateMipmap(this._temp_texture.texture_type); + this._temp_texture.unbind(0); + } + } + + this.setOutputData(0, this._temp_texture); + }; + + LiteGraph.registerNodeType("texture/copy", LGraphTextureCopy); + + // Texture Downsample ***************************************** + function LGraphTextureDownsample() { + this.addInput("Texture", "Texture"); + this.addOutput("", "Texture"); + this.properties = { + iterations: 1, + generate_mipmaps: false, + precision: LGraphTexture.DEFAULT + }; + } + + LGraphTextureDownsample.title = "Downsample"; + LGraphTextureDownsample.desc = "Downsample Texture"; + LGraphTextureDownsample.widgets_info = { + iterations: { type: "number", step: 1, precision: 0, min: 0 }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureDownsample.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex && !this._temp_texture) { + return; + } + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + //we do not allow any texture different than texture 2D + if (!tex || tex.texture_type !== GL.TEXTURE_2D) { + return; + } + + if (this.properties.iterations < 1) { + this.setOutputData(0, tex); + return; + } + + var shader = LGraphTextureDownsample._shader; + if (!shader) { + LGraphTextureDownsample._shader = shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureDownsample.pixel_shader + ); + } + + var width = tex.width | 0; + var height = tex.height | 0; + var type = tex.type; + if (this.properties.precision === LGraphTexture.LOW) { + type = gl.UNSIGNED_BYTE; + } else if (this.properties.precision === LGraphTexture.HIGH) { + type = gl.HIGH_PRECISION_FORMAT; + } + var iterations = this.properties.iterations || 1; + + var origin = tex; + var target = null; + + var temp = []; + var options = { + type: type, + format: tex.format + }; + + var offset = vec2.create(); + var uniforms = { + u_offset: offset + }; + + if (this._texture) { + GL.Texture.releaseTemporary(this._texture); + } + + for (var i = 0; i < iterations; ++i) { + offset[0] = 1 / width; + offset[1] = 1 / height; + width = width >> 1 || 0; + height = height >> 1 || 0; + target = GL.Texture.getTemporary(width, height, options); + temp.push(target); + origin.setParameter(GL.TEXTURE_MAG_FILTER, GL.NEAREST); + origin.copyTo(target, shader, uniforms); + if (width == 1 && height == 1) { + break; + } //nothing else to do + origin = target; + } + + //keep the last texture used + this._texture = temp.pop(); + + //free the rest + for (var i = 0; i < temp.length; ++i) { + GL.Texture.releaseTemporary(temp[i]); + } + + if (this.properties.generate_mipmaps) { + this._texture.bind(0); + gl.generateMipmap(this._texture.texture_type); + this._texture.unbind(0); + } + + this.setOutputData(0, this._texture); + }; + + LGraphTextureDownsample.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 u_offset;\n\ + varying vec2 v_coord;\n\ + \n\ + void main() {\n\ + vec4 color = texture2D(u_texture, v_coord );\n\ + color += texture2D(u_texture, v_coord + vec2( u_offset.x, 0.0 ) );\n\ + color += texture2D(u_texture, v_coord + vec2( 0.0, u_offset.y ) );\n\ + color += texture2D(u_texture, v_coord + vec2( u_offset.x, u_offset.y ) );\n\ + gl_FragColor = color * 0.25;\n\ + }\n\ + "; + + LiteGraph.registerNodeType( + "texture/downsample", + LGraphTextureDownsample + ); + + // Texture Average ***************************************** + function LGraphTextureAverage() { + this.addInput("Texture", "Texture"); + this.addOutput("tex", "Texture"); + this.addOutput("avg", "vec4"); + this.addOutput("lum", "number"); + this.properties = { + use_previous_frame: true, //to avoid stalls + high_quality: false //to use as much pixels as possible + }; + + this._uniforms = { + u_texture: 0, + u_mipmap_offset: 0 + }; + this._luminance = new Float32Array(4); + } + + LGraphTextureAverage.title = "Average"; + LGraphTextureAverage.desc = + "Compute a partial average (32 random samples) of a texture and stores it as a 1x1 pixel texture.\n If high_quality is true, then it generates the mipmaps first and reads from the lower one."; + + LGraphTextureAverage.prototype.onExecute = function() { + if (!this.properties.use_previous_frame) { + this.updateAverage(); + } + + var v = this._luminance; + this.setOutputData(0, this._temp_texture); + this.setOutputData(1, v); + this.setOutputData(2, (v[0] + v[1] + v[2]) / 3); + }; + + //executed before rendering the frame + LGraphTextureAverage.prototype.onPreRenderExecute = function() { + this.updateAverage(); + }; + + LGraphTextureAverage.prototype.updateAverage = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if ( + !this.isOutputConnected(0) && + !this.isOutputConnected(1) && + !this.isOutputConnected(2) + ) { + return; + } //saves work + + if (!LGraphTextureAverage._shader) { + LGraphTextureAverage._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureAverage.pixel_shader + ); + //creates 256 random numbers and stores them in two mat4 + var samples = new Float32Array(16); + for (var i = 0; i < samples.length; ++i) { + samples[i] = Math.random(); //poorly distributed samples + } + //upload only once + LGraphTextureAverage._shader.uniforms({ + u_samples_a: samples.subarray(0, 16), + u_samples_b: samples.subarray(16, 32) + }); + } + + var temp = this._temp_texture; + var type = gl.UNSIGNED_BYTE; + if (tex.type != type) { + //force floats, half floats cannot be read with gl.readPixels + type = gl.FLOAT; + } + + if (!temp || temp.type != type) { + this._temp_texture = new GL.Texture(1, 1, { + type: type, + format: gl.RGBA, + filter: gl.NEAREST + }); + } + + this._uniforms.u_mipmap_offset = 0; + + if(this.properties.high_quality) + { + if( !this._temp_pot2_texture || this._temp_pot2_texture.type != type ) + this._temp_pot2_texture = new GL.Texture(512, 512, { + type: type, + format: gl.RGBA, + minFilter: gl.LINEAR_MIPMAP_LINEAR, + magFilter: gl.LINEAR + }); + + tex.copyTo( this._temp_pot2_texture ); + tex = this._temp_pot2_texture; + tex.bind(0); + gl.generateMipmap(GL.TEXTURE_2D); + this._uniforms.u_mipmap_offset = 9; + } + + var shader = LGraphTextureAverage._shader; + var uniforms = this._uniforms; + uniforms.u_mipmap_offset = this.properties.mipmap_offset; + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.BLEND); + this._temp_texture.drawTo(function() { + tex.toViewport(shader, uniforms); + }); + + if (this.isOutputConnected(1) || this.isOutputConnected(2)) { + var pixel = this._temp_texture.getPixels(); + if (pixel) { + var v = this._luminance; + var type = this._temp_texture.type; + v.set(pixel); + if (type == gl.UNSIGNED_BYTE) { + vec4.scale(v, v, 1 / 255); + } else if ( + type == GL.HALF_FLOAT || + type == GL.HALF_FLOAT_OES + ) { + //no half floats possible, hard to read back unless copyed to a FLOAT texture, so temp_texture is always forced to FLOAT + } + } + } + }; + + LGraphTextureAverage.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + uniform mat4 u_samples_a;\n\ + uniform mat4 u_samples_b;\n\ + uniform sampler2D u_texture;\n\ + uniform float u_mipmap_offset;\n\ + varying vec2 v_coord;\n\ + \n\ + void main() {\n\ + vec4 color = vec4(0.0);\n\ + //random average\n\ + for(int i = 0; i < 4; ++i)\n\ + for(int j = 0; j < 4; ++j)\n\ + {\n\ + color += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\ + color += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\ + }\n\ + gl_FragColor = color * 0.03125;\n\ + }\n\ + "; + + LiteGraph.registerNodeType("texture/average", LGraphTextureAverage); + + + + // Computes operation between pixels (max, min) ***************************************** + function LGraphTextureMinMax() { + this.addInput("Texture", "Texture"); + this.addOutput("min_t", "Texture"); + this.addOutput("max_t", "Texture"); + this.addOutput("min", "vec4"); + this.addOutput("max", "vec4"); + this.properties = { + mode: "max", + use_previous_frame: true //to avoid stalls + }; + + this._uniforms = { + u_texture: 0 + }; + + this._max = new Float32Array(4); + this._min = new Float32Array(4); + + this._textures_chain = []; + } + + LGraphTextureMinMax.widgets_info = { + mode: { widget: "combo", values: ["min","max","avg"] } + }; + + LGraphTextureMinMax.title = "MinMax"; + LGraphTextureMinMax.desc = "Compute the scene min max"; + + LGraphTextureMinMax.prototype.onExecute = function() { + if (!this.properties.use_previous_frame) { + this.update(); + } + + this.setOutputData(0, this._temp_texture); + this.setOutputData(1, this._luminance); + }; + + //executed before rendering the frame + LGraphTextureMinMax.prototype.onPreRenderExecute = function() { + this.update(); + }; + + LGraphTextureMinMax.prototype.update = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if ( !this.isOutputConnected(0) && !this.isOutputConnected(1) ) { + return; + } //saves work + + if (!LGraphTextureMinMax._shader) { + LGraphTextureMinMax._shader = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureMinMax.pixel_shader ); + } + + var temp = this._temp_texture; + var type = gl.UNSIGNED_BYTE; + if (tex.type != type) { + //force floats, half floats cannot be read with gl.readPixels + type = gl.FLOAT; + } + + var size = 512; + + if( !this._textures_chain.length || this._textures_chain[0].type != type ) + { + var index = 0; + while(i) + { + this._textures_chain[i] = new GL.Texture( size, size, { + type: type, + format: gl.RGBA, + filter: gl.NEAREST + }); + size = size >> 2; + i++; + if(size == 1) + break; + } + } + + tex.copyTo( this._textures_chain[0] ); + var prev = this._textures_chain[0]; + for(var i = 1; i <= this._textures_chain.length; ++i) + { + var tex = this._textures_chain[i]; + + prev = tex; + } + + var shader = LGraphTextureMinMax._shader; + var uniforms = this._uniforms; + uniforms.u_mipmap_offset = this.properties.mipmap_offset; + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.BLEND); + this._temp_texture.drawTo(function() { + tex.toViewport(shader, uniforms); + }); + }; + + LGraphTextureMinMax.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + uniform mat4 u_samples_a;\n\ + uniform mat4 u_samples_b;\n\ + uniform sampler2D u_texture;\n\ + uniform float u_mipmap_offset;\n\ + varying vec2 v_coord;\n\ + \n\ + void main() {\n\ + vec4 color = vec4(0.0);\n\ + //random average\n\ + for(int i = 0; i < 4; ++i)\n\ + for(int j = 0; j < 4; ++j)\n\ + {\n\ + color += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\ + color += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\ + }\n\ + gl_FragColor = color * 0.03125;\n\ + }\n\ + "; + + //LiteGraph.registerNodeType("texture/clustered_operation", LGraphTextureClusteredOperation); + + + function LGraphTextureTemporalSmooth() { + this.addInput("in", "Texture"); + this.addInput("factor", "Number"); + this.addOutput("out", "Texture"); + this.properties = { factor: 0.5 }; + this._uniforms = { + u_texture: 0, + u_textureB: 1, + u_factor: this.properties.factor + }; + } + + LGraphTextureTemporalSmooth.title = "Smooth"; + LGraphTextureTemporalSmooth.desc = "Smooth texture over time"; + + LGraphTextureTemporalSmooth.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex || !this.isOutputConnected(0)) { + return; + } + + if (!LGraphTextureTemporalSmooth._shader) { + LGraphTextureTemporalSmooth._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureTemporalSmooth.pixel_shader + ); + } + + var temp = this._temp_texture; + if ( + !temp || + temp.type != tex.type || + temp.width != tex.width || + temp.height != tex.height + ) { + var options = { + type: tex.type, + format: gl.RGBA, + filter: gl.NEAREST + }; + this._temp_texture = new GL.Texture(tex.width, tex.height, options ); + this._temp_texture2 = new GL.Texture(tex.width, tex.height, options ); + tex.copyTo(this._temp_texture2); + } + + var tempA = this._temp_texture; + var tempB = this._temp_texture2; + + var shader = LGraphTextureTemporalSmooth._shader; + var uniforms = this._uniforms; + uniforms.u_factor = 1.0 - this.getInputOrProperty("factor"); + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + tempA.drawTo(function() { + tempB.bind(1); + tex.toViewport(shader, uniforms); + }); + + this.setOutputData(0, tempA); + + //swap + this._temp_texture = tempB; + this._temp_texture2 = tempA; + }; + + LGraphTextureTemporalSmooth.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_textureB;\n\ + uniform float u_factor;\n\ + varying vec2 v_coord;\n\ + \n\ + void main() {\n\ + gl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\ + }\n\ + "; + + LiteGraph.registerNodeType( "texture/temporal_smooth", LGraphTextureTemporalSmooth ); + + + function LGraphTextureLinearAvgSmooth() { + this.addInput("in", "Texture"); + this.addOutput("avg", "Texture"); + this.addOutput("array", "Texture"); + this.properties = { samples: 64, frames_interval: 1 }; + this._uniforms = { + u_texture: 0, + u_textureB: 1, + u_samples: this.properties.samples, + u_isamples: 1/this.properties.samples + }; + this.frame = 0; + } + + LGraphTextureLinearAvgSmooth.title = "Lineal Avg Smooth"; + LGraphTextureLinearAvgSmooth.desc = "Smooth texture linearly over time"; + + LGraphTextureLinearAvgSmooth["@samples"] = { type: "number", min: 1, max: 64, step: 1, precision: 1 }; + + LGraphTextureLinearAvgSmooth.prototype.getPreviewTexture = function() { - this.must_update = true; + return this._temp_texture2; } - LGraphPoints3D.prototype.onExecute = function() { + LGraphTextureLinearAvgSmooth.prototype.onExecute = function() { - var obj = this.getInputData(0); - if( obj != this._old_obj || (obj && obj._version != this._old_obj_version) ) - { - this._old_obj = obj; - this.must_update = true; + var tex = this.getInputData(0); + if (!tex || !this.isOutputConnected(0)) { + return; } - var radius = this.getInputData(1); - if(radius == null) - radius = this.properties.radius; - if( this._last_radius != radius ) - { - this._last_radius = radius; - this.must_update = true; + if (!LGraphTextureLinearAvgSmooth._shader) { + LGraphTextureLinearAvgSmooth._shader_copy = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureLinearAvgSmooth.pixel_shader_copy ); + LGraphTextureLinearAvgSmooth._shader_avg = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureLinearAvgSmooth.pixel_shader_avg ); } - if(this.must_update || this.properties.force_update ) + var samples = Math.clamp(this.properties.samples,0,64); + var frame = this.frame; + var interval = this.properties.frames_interval; + + if( interval == 0 || frame % interval == 0 ) { - this.must_update = false; - this.updatePoints(); - } + var temp = this._temp_texture; + if ( !temp || temp.type != tex.type || temp.width != samples ) { + var options = { + type: tex.type, + format: gl.RGBA, + filter: gl.NEAREST + }; + this._temp_texture = new GL.Texture( samples, 1, options ); + this._temp_texture2 = new GL.Texture( samples, 1, options ); + this._temp_texture_out = new GL.Texture( 1, 1, options ); + } - this.geometry.vertices = this.points; - this.geometry.normals = this.normals; - this.geometry._version = this.version; + var tempA = this._temp_texture; + var tempB = this._temp_texture2; - this.setOutputData( 0, this.geometry ); - } + var shader_copy = LGraphTextureLinearAvgSmooth._shader_copy; + var shader_avg = LGraphTextureLinearAvgSmooth._shader_avg; + var uniforms = this._uniforms; + uniforms.u_samples = samples; + uniforms.u_isamples = 1.0 / samples; - LGraphPoints3D.prototype.updatePoints = function() { - var num_points = this.properties.num_points|0; - if(num_points < 1) - num_points = 1; + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + tempA.drawTo(function() { + tempB.bind(1); + tex.toViewport( shader_copy, uniforms ); + }); - if(!this.points || this.points.length != num_points * 3) - this.points = new Float32Array( num_points * 3 ); + this._temp_texture_out.drawTo(function() { + tempA.toViewport( shader_avg, uniforms ); + }); - if(this.properties.generate_normals) - { - if (!this.normals || this.normals.length != this.points.length) - this.normals = new Float32Array( this.points.length ); + this.setOutputData( 0, this._temp_texture_out ); + + //swap + this._temp_texture = tempB; + this._temp_texture2 = tempA; } else - this.normals = null; - - var radius = this._last_radius || this.properties.radius; - var mode = this.properties.mode; - - var obj = this.getInputData(0); - this._old_obj_version = obj ? obj._version : null; - - this.points = LGraphPoints3D.generatePoints( radius, num_points, mode, this.points, this.normals, this.properties.regular, obj ); - - this.version++; - } - - //global - LGraphPoints3D.generatePoints = function( radius, num_points, mode, points, normals, regular, obj ) - { - var size = num_points * 3; - if(!points || points.length != size) - points = new Float32Array( size ); - var temp = new Float32Array(3); - var UP = new Float32Array([0,1,0]); - - if(regular) - { - if( mode == LGraphPoints3D.RECTANGLE) - { - var side = Math.floor(Math.sqrt(num_points)); - for(var i = 0; i < side; ++i) - for(var j = 0; j < side; ++j) - { - var pos = i*3 + j*3*side; - points[pos] = ((i/side) - 0.5) * radius * 2; - points[pos+1] = 0; - points[pos+2] = ((j/side) - 0.5) * radius * 2; - } - points = new Float32Array( points.subarray(0,side*side*3) ); - if(normals) - { - for(var i = 0; i < normals.length; i+=3) - normals.set(UP, i); - } - } - else if( mode == LGraphPoints3D.SPHERE) - { - var side = Math.floor(Math.sqrt(num_points)); - for(var i = 0; i < side; ++i) - for(var j = 0; j < side; ++j) - { - var pos = i*3 + j*3*side; - polarToCartesian( temp, (i/side) * 2 * Math.PI, ((j/side) - 0.5) * 2 * Math.PI, radius ); - points[pos] = temp[0]; - points[pos+1] = temp[1]; - points[pos+2] = temp[2]; - } - points = new Float32Array( points.subarray(0,side*side*3) ); - if(normals) - LGraphPoints3D.generateSphericalNormals( points, normals ); - } - else if( mode == LGraphPoints3D.CIRCLE) - { - for(var i = 0; i < size; i+=3) - { - var angle = 2 * Math.PI * (i/size); - points[i] = Math.cos( angle ) * radius; - points[i+1] = 0; - points[i+2] = Math.sin( angle ) * radius; - } - if(normals) - { - for(var i = 0; i < normals.length; i+=3) - normals.set(UP, i); - } - } - } - else //non regular - { - if( mode == LGraphPoints3D.RECTANGLE) - { - for(var i = 0; i < size; i+=3) - { - points[i] = (Math.random() - 0.5) * radius * 2; - points[i+1] = 0; - points[i+2] = (Math.random() - 0.5) * radius * 2; - } - if(normals) - { - for(var i = 0; i < normals.length; i+=3) - normals.set(UP, i); - } - } - else if( mode == LGraphPoints3D.CUBE) - { - for(var i = 0; i < size; i+=3) - { - points[i] = (Math.random() - 0.5) * radius * 2; - points[i+1] = (Math.random() - 0.5) * radius * 2; - points[i+2] = (Math.random() - 0.5) * radius * 2; - } - if(normals) - { - for(var i = 0; i < normals.length; i+=3) - normals.set(UP, i); - } - } - else if( mode == LGraphPoints3D.SPHERE) - { - LGraphPoints3D.generateSphere( points, size, radius ); - if(normals) - LGraphPoints3D.generateSphericalNormals( points, normals ); - } - else if( mode == LGraphPoints3D.HEMISPHERE) - { - LGraphPoints3D.generateHemisphere( points, size, radius ); - if(normals) - LGraphPoints3D.generateSphericalNormals( points, normals ); - } - else if( mode == LGraphPoints3D.CIRCLE) - { - LGraphPoints3D.generateInsideCircle( points, size, radius ); - if(normals) - LGraphPoints3D.generateSphericalNormals( points, normals ); - } - else if( mode == LGraphPoints3D.INSIDE_SPHERE) - { - LGraphPoints3D.generateInsideSphere( points, size, radius ); - if(normals) - LGraphPoints3D.generateSphericalNormals( points, normals ); - } - else if( mode == LGraphPoints3D.OBJECT) - { - LGraphPoints3D.generateFromObject( points, normals, size, obj, false ); - } - else if( mode == LGraphPoints3D.OBJECT_UNIFORMLY) - { - LGraphPoints3D.generateFromObject( points, normals, size, obj, true ); - } - else if( mode == LGraphPoints3D.OBJECT_INSIDE) - { - LGraphPoints3D.generateFromInsideObject( points, size, obj ); - //if(normals) - // LGraphPoints3D.generateSphericalNormals( points, normals ); - } - else - console.warn("wrong mode in LGraphPoints3D"); - } - - return points; - } - - LGraphPoints3D.generateSphericalNormals = function(points, normals) - { - var temp = new Float32Array(3); - for(var i = 0; i < normals.length; i+=3) - { - temp[0] = points[i]; - temp[1] = points[i+1]; - temp[2] = points[i+2]; - vec3.normalize(temp,temp); - normals.set(temp,i); - } - } - - LGraphPoints3D.generateSphere = function (points, size, radius) - { - for(var i = 0; i < size; i+=3) - { - var r1 = Math.random(); - var r2 = Math.random(); - var x = 2 * Math.cos( 2 * Math.PI * r1 ) * Math.sqrt( r2 * (1-r2) ); - var y = 1 - 2 * r2; - var z = 2 * Math.sin( 2 * Math.PI * r1 ) * Math.sqrt( r2 * (1-r2) ); - points[i] = x * radius; - points[i+1] = y * radius; - points[i+2] = z * radius; - } - } - - LGraphPoints3D.generateHemisphere = function (points, size, radius) - { - for(var i = 0; i < size; i+=3) - { - var r1 = Math.random(); - var r2 = Math.random(); - var x = Math.cos( 2 * Math.PI * r1 ) * Math.sqrt(1 - r2*r2 ); - var y = r2; - var z = Math.sin( 2 * Math.PI * r1 ) * Math.sqrt(1 - r2*r2 ); - points[i] = x * radius; - points[i+1] = y * radius; - points[i+2] = z * radius; - } - } - - LGraphPoints3D.generateInsideCircle = function (points, size, radius) - { - for(var i = 0; i < size; i+=3) - { - var r1 = Math.random(); - var r2 = Math.random(); - var x = Math.cos( 2 * Math.PI * r1 ) * Math.sqrt(1 - r2*r2 ); - var y = r2; - var z = Math.sin( 2 * Math.PI * r1 ) * Math.sqrt(1 - r2*r2 ); - points[i] = x * radius; - points[i+1] = 0; - points[i+2] = z * radius; - } - } - - LGraphPoints3D.generateInsideSphere = function (points, size, radius) - { - for(var i = 0; i < size; i+=3) - { - var u = Math.random(); - var v = Math.random(); - var theta = u * 2.0 * Math.PI; - var phi = Math.acos(2.0 * v - 1.0); - var r = Math.cbrt(Math.random()) * radius; - var sinTheta = Math.sin(theta); - var cosTheta = Math.cos(theta); - var sinPhi = Math.sin(phi); - var cosPhi = Math.cos(phi); - points[i] = r * sinPhi * cosTheta; - points[i+1] = r * sinPhi * sinTheta; - points[i+2] = r * cosPhi; - } - } - - function findRandomTriangle( areas, f ) - { - var l = areas.length; - var imin = 0; - var imid = 0; - var imax = l; - - if(l == 0) - return -1; - if(l == 1) - return 0; - //dichotimic search - while (imax >= imin) - { - imid = ((imax + imin)*0.5)|0; - var t = areas[ imid ]; - if( t == f ) - return imid; - if( imin == (imax - 1) ) - return imin; - if (t < f) - imin = imid; - else - imax = imid; - } - return imid; - } - - LGraphPoints3D.generateFromObject = function( points, normals, size, obj, evenly ) - { - if(!obj) - return; - - var vertices = null; - var mesh_normals = null; - var indices = null; - var areas = null; - if( obj.constructor === GL.Mesh ) - { - vertices = obj.vertexBuffers.vertices.data; - mesh_normals = obj.vertexBuffers.normals ? obj.vertexBuffers.normals.data : null; - indices = obj.indexBuffers.indices ? obj.indexBuffers.indices.data : null; - if(!indices) - indices = obj.indexBuffers.triangles ? obj.indexBuffers.triangles.data : null; - } - if(!vertices) - return null; - var num_triangles = indices ? indices.length / 3 : vertices.length / (3*3); - var total_area = 0; //sum of areas of all triangles - - if(evenly) - { - areas = new Float32Array(num_triangles); //accum - for(var i = 0; i < num_triangles; ++i) - { - if(indices) - { - a = indices[i*3]*3; - b = indices[i*3+1]*3; - c = indices[i*3+2]*3; - } - else - { - a = i*9; - b = i*9+3; - c = i*9+6; - } - var P1 = vertices.subarray(a,a+3); - var P2 = vertices.subarray(b,b+3); - var P3 = vertices.subarray(c,c+3); - var aL = vec3.distance( P1, P2 ); - var bL = vec3.distance( P2, P3 ); - var cL = vec3.distance( P3, P1 ); - var s = (aL + bL+ cL) / 2; - total_area += Math.sqrt(s * (s - aL) * (s - bL) * (s - cL)); - areas[i] = total_area; - } - for(var i = 0; i < num_triangles; ++i) //normalize - areas[i] /= total_area; - } - - for(var i = 0; i < size; i+=3) - { - var r = Math.random(); - var index = evenly ? findRandomTriangle( areas, r ) : Math.floor(r * num_triangles ); - //get random triangle - var a = 0; - var b = 0; - var c = 0; - if(indices) - { - a = indices[index*3]*3; - b = indices[index*3+1]*3; - c = indices[index*3+2]*3; - } - else - { - a = index*9; - b = index*9+3; - c = index*9+6; - } - var s = Math.random(); - var t = Math.random(); - var sqrt_s = Math.sqrt(s); - var af = 1 - sqrt_s; - var bf = sqrt_s * ( 1 - t); - var cf = t * sqrt_s; - points[i] = af * vertices[a] + bf*vertices[b] + cf*vertices[c]; - points[i+1] = af * vertices[a+1] + bf*vertices[b+1] + cf*vertices[c+1]; - points[i+2] = af * vertices[a+2] + bf*vertices[b+2] + cf*vertices[c+2]; - if(normals && mesh_normals) - { - normals[i] = af * mesh_normals[a] + bf*mesh_normals[b] + cf*mesh_normals[c]; - normals[i+1] = af * mesh_normals[a+1] + bf*mesh_normals[b+1] + cf*mesh_normals[c+1]; - normals[i+2] = af * mesh_normals[a+2] + bf*mesh_normals[b+2] + cf*mesh_normals[c+2]; - var N = normals.subarray(i,i+3); - vec3.normalize(N,N); - } - } - } - - LGraphPoints3D.generateFromInsideObject = function( points, size, mesh ) - { - if(!mesh || mesh.constructor !== GL.Mesh) - return; - - var aabb = mesh.getBoundingBox(); - if(!mesh.octree) - mesh.octree = new GL.Octree( mesh ); - var octree = mesh.octree; - var origin = vec3.create(); - var direction = vec3.fromValues(1,0,0); - var temp = vec3.create(); - var i = 0; - var tries = 0; - while(i < size && tries < points.length * 10) //limit to avoid problems - { - tries += 1 - var r = vec3.random(temp); //random point inside the aabb - r[0] = (r[0] * 2 - 1) * aabb[3] + aabb[0]; - r[1] = (r[1] * 2 - 1) * aabb[4] + aabb[1]; - r[2] = (r[2] * 2 - 1) * aabb[5] + aabb[2]; - origin.set(r); - var hit = octree.testRay( origin, direction, 0, 10000, true, GL.Octree.ALL ); - if(!hit || hit.length % 2 == 0) //not inside - continue; - points.set( r, i ); - i+=3; - } - } - - LiteGraph.registerNodeType( "geometry/points3D", LGraphPoints3D ); - - - - function LGraphPointsToInstances() { - this.addInput("points", "geometry"); - this.addOutput("instances", "[mat4]"); - this.properties = { - mode: 1, - autoupdate: true - }; - - this.must_update = true; - this.matrices = []; - this.first_time = true; - } - - LGraphPointsToInstances.NORMAL = 0; - LGraphPointsToInstances.VERTICAL = 1; - LGraphPointsToInstances.SPHERICAL = 2; - LGraphPointsToInstances.RANDOM = 3; - LGraphPointsToInstances.RANDOM_VERTICAL = 4; - - LGraphPointsToInstances.modes = {"normal":0,"vertical":1,"spherical":2,"random":3,"random_vertical":4}; - LGraphPointsToInstances.widgets_info = { - mode: { widget: "combo", values: LGraphPointsToInstances.modes } + this.setOutputData(0, this._temp_texture_out); + this.setOutputData(1, this._temp_texture2); + this.frame++; }; - LGraphPointsToInstances.title = "points to inst"; + LGraphTextureLinearAvgSmooth.pixel_shader_copy = + "precision highp float;\n\ + precision highp float;\n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_textureB;\n\ + uniform float u_isamples;\n\ + varying vec2 v_coord;\n\ + \n\ + void main() {\n\ + if( v_coord.x <= u_isamples )\n\ + gl_FragColor = texture2D( u_texture, vec2(0.5) );\n\ + else\n\ + gl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\ + }\n\ + "; - LGraphPointsToInstances.prototype.onExecute = function() - { - var geo = this.getInputData(0); - if( !geo ) - { - this.setOutputData(0,null); - return; - } - - if( !this.isOutputConnected(0) ) - return; - - var has_changed = (geo._version != this._version || geo._id != this._geometry_id); - - if( has_changed && this.properties.autoupdate || this.first_time ) - { - this.first_time = false; - this.updateInstances( geo ); - } - - this.setOutputData( 0, this.matrices ); - } - - LGraphPointsToInstances.prototype.updateInstances = function( geometry ) - { - var vertices = geometry.vertices; - if(!vertices) - return null; - var normals = geometry.normals; - - var matrices = this.matrices; - var num_points = vertices.length / 3; - if( matrices.length != num_points) - matrices.length = num_points; - var identity = mat4.create(); - var temp = vec3.create(); - var zero = vec3.create(); - var UP = vec3.fromValues(0,1,0); - var FRONT = vec3.fromValues(0,0,-1); - var RIGHT = vec3.fromValues(1,0,0); - var R = quat.create(); - - var front = vec3.create(); - var right = vec3.create(); - var top = vec3.create(); - - for(var i = 0; i < vertices.length; i += 3) - { - var index = i/3; - var m = matrices[index]; - if(!m) - m = matrices[index] = mat4.create(); - m.set( identity ); - var point = vertices.subarray(i,i+3); - - switch(this.properties.mode) - { - case LGraphPointsToInstances.NORMAL: - mat4.setTranslation( m, point ); - if(normals) - { - var normal = normals.subarray(i,i+3); - top.set( normal ); - vec3.normalize( top, top ); - vec3.cross( right, FRONT, top ); - vec3.normalize( right, right ); - vec3.cross( front, right, top ); - vec3.normalize( front, front ); - m.set(right,0); - m.set(top,4); - m.set(front,8); - mat4.setTranslation( m, point ); - } - break; - case LGraphPointsToInstances.VERTICAL: - mat4.setTranslation( m, point ); - break; - case LGraphPointsToInstances.SPHERICAL: - front.set( point ); - vec3.normalize( front, front ); - vec3.cross( right, UP, front ); - vec3.normalize( right, right ); - vec3.cross( top, front, right ); - vec3.normalize( top, top ); - m.set(right,0); - m.set(top,4); - m.set(front,8); - mat4.setTranslation( m, point ); - break; - case LGraphPointsToInstances.RANDOM: - temp[0] = Math.random()*2 - 1; - temp[1] = Math.random()*2 - 1; - temp[2] = Math.random()*2 - 1; - vec3.normalize( temp, temp ); - quat.setAxisAngle( R, temp, Math.random() * 2 * Math.PI ); - mat4.fromQuat(m, R); - mat4.setTranslation( m, point ); - break; - case LGraphPointsToInstances.RANDOM_VERTICAL: - quat.setAxisAngle( R, UP, Math.random() * 2 * Math.PI ); - mat4.fromQuat(m, R); - mat4.setTranslation( m, point ); - break; - } - } - - this._version = geometry._version; - this._geometry_id = geometry._id; - } - - LiteGraph.registerNodeType( "geometry/points_to_instances", LGraphPointsToInstances ); + LGraphTextureLinearAvgSmooth.pixel_shader_avg = + "precision highp float;\n\ + precision highp float;\n\ + uniform sampler2D u_texture;\n\ + uniform int u_samples;\n\ + uniform float u_isamples;\n\ + varying vec2 v_coord;\n\ + \n\ + void main() {\n\ + vec4 color = vec4(0.0);\n\ + for(int i = 0; i < 64; ++i)\n\ + {\n\ + color += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\ + if(i == (u_samples - 1))\n\ + break;\n\ + }\n\ + gl_FragColor = color * u_isamples;\n\ + }\n\ + "; - function LGraphGeometryTransform() { - this.addInput("in", "geometry,[mat4]"); - this.addInput("mat4", "mat4"); - this.addOutput("out", "geometry"); + LiteGraph.registerNodeType( "texture/linear_avg_smooth", LGraphTextureLinearAvgSmooth ); + + // Image To Texture ***************************************** + function LGraphImageToTexture() { + this.addInput("Image", "image"); + this.addOutput("", "Texture"); this.properties = {}; - - this.geometry = { - type: "triangles", - vertices: null, - _id: generateGeometryId(), - _version: 0 - }; - - this._last_geometry_id = -1; - this._last_version = -1; - this._last_key = ""; - - this.must_update = true; } - LGraphGeometryTransform.title = "Transform"; + LGraphImageToTexture.title = "Image to Texture"; + LGraphImageToTexture.desc = "Uploads an image to the GPU"; + //LGraphImageToTexture.widgets_info = { size: { widget:"combo", values:[0,32,64,128,256,512,1024,2048]} }; - LGraphGeometryTransform.prototype.onExecute = function() { - - var input = this.getInputData(0); - var model = this.getInputData(1); - - if(!input) - return; - - //array of matrices - if(input.constructor === Array) - { - if(input.length == 0) - return; - this.outputs[0].type = "[mat4]"; - if( !this.isOutputConnected(0) ) - return; - - if(!model) - { - this.setOutputData(0,input); - return; - } - - if(!this._output) - this._output = new Array(); - if(this._output.length != input.length) - this._output.length = input.length; - for(var i = 0; i < input.length; ++i) - { - var m = this._output[i]; - if(!m) - m = this._output[i] = mat4.create(); - mat4.multiply(m,input[i],model); - } - this.setOutputData(0,this._output); + LGraphImageToTexture.prototype.onExecute = function() { + var img = this.getInputData(0); + if (!img) { return; } - //geometry - if(!input.vertices || !input.vertices.length) - return; - var geo = input; - this.outputs[0].type = "geometry"; - if( !this.isOutputConnected(0) ) - return; - if(!model) - { - this.setOutputData(0,geo); + var width = img.videoWidth || img.width; + var height = img.videoHeight || img.height; + + //this is in case we are using a webgl canvas already, no need to reupload it + if (img.gltexture) { + this.setOutputData(0, img.gltexture); return; } - var key = typedArrayToArray(model).join(","); - - if( this.must_update || geo._id != this._last_geometry_id || geo._version != this._last_version || key != this._last_key ) - { - this.updateGeometry(geo, model); - this._last_key = key; - this._last_version = geo._version; - this._last_geometry_id = geo._id; - this.must_update = false; + var temp = this._temp_texture; + if (!temp || temp.width != width || temp.height != height) { + this._temp_texture = new GL.Texture(width, height, { + format: gl.RGBA, + filter: gl.LINEAR + }); } - this.setOutputData(0,this.geometry); - } - - LGraphGeometryTransform.prototype.updateGeometry = function(geometry, model) { - var old_vertices = geometry.vertices; - var vertices = this.geometry.vertices; - if( !vertices || vertices.length != old_vertices.length ) - vertices = this.geometry.vertices = new Float32Array( old_vertices.length ); - var temp = vec3.create(); - - for(var i = 0, l = vertices.length; i < l; i+=3) - { - temp[0] = old_vertices[i]; temp[1] = old_vertices[i+1]; temp[2] = old_vertices[i+2]; - mat4.multiplyVec3( temp, model, temp ); - vertices[i] = temp[0]; vertices[i+1] = temp[1]; vertices[i+2] = temp[2]; - } - - if(geometry.normals) - { - if( !this.geometry.normals || this.geometry.normals.length != geometry.normals.length ) - this.geometry.normals = new Float32Array( geometry.normals.length ); - var normals = this.geometry.normals; - var normal_model = mat4.invert(mat4.create(), model); - if(normal_model) - mat4.transpose(normal_model, normal_model); - var old_normals = geometry.normals; - for(var i = 0, l = normals.length; i < l; i+=3) - { - temp[0] = old_normals[i]; temp[1] = old_normals[i+1]; temp[2] = old_normals[i+2]; - mat4.multiplyVec3( temp, normal_model, temp ); - normals[i] = temp[0]; normals[i+1] = temp[1]; normals[i+2] = temp[2]; - } - } - - this.geometry.type = geometry.type; - this.geometry._version++; - } - - LiteGraph.registerNodeType( "geometry/transform", LGraphGeometryTransform ); - - - function LGraphGeometryPolygon() { - this.addInput("sides", "number"); - this.addInput("radius", "number"); - this.addOutput("out", "geometry"); - this.properties = { sides: 6, radius: 1, uvs: false } - - this.geometry = { - type: "line_loop", - vertices: null, - _id: generateGeometryId() - }; - this.geometry_id = -1; - this.version = -1; - this.must_update = true; - - this.last_info = { sides: -1, radius: -1 }; - } - - LGraphGeometryPolygon.title = "Polygon"; - - LGraphGeometryPolygon.prototype.onExecute = function() { - - if( !this.isOutputConnected(0) ) + try { + this._temp_texture.uploadImage(img); + } catch (err) { + console.error( + "image comes from an unsafe location, cannot be uploaded to webgl: " + + err + ); return; - - var sides = this.getInputOrProperty("sides"); - var radius = this.getInputOrProperty("radius"); - sides = Math.max(3,sides)|0; - - //update - if( this.last_info.sides != sides || this.last_info.radius != radius ) - this.updateGeometry(sides, radius); - - this.setOutputData(0,this.geometry); - } - - LGraphGeometryPolygon.prototype.updateGeometry = function(sides, radius) { - var num = 3*sides; - var vertices = this.geometry.vertices; - if( !vertices || vertices.length != num ) - vertices = this.geometry.vertices = new Float32Array( 3*sides ); - var delta = (Math.PI * 2) / sides; - var gen_uvs = this.properties.uvs; - if(gen_uvs) - { - uvs = this.geometry.coords = new Float32Array( 3*sides ); } - - for(var i = 0; i < sides; ++i) - { - var angle = delta * -i; - var x = Math.cos( angle ) * radius; - var y = 0; - var z = Math.sin( angle ) * radius; - vertices[i*3] = x; - vertices[i*3+1] = y; - vertices[i*3+2] = z; - - if(gen_uvs) - { - - - } - } - this.geometry._id = ++this.geometry_id; - this.geometry._version = ++this.version; - this.last_info.sides = sides; - this.last_info.radius = radius; - } - - LiteGraph.registerNodeType( "geometry/polygon", LGraphGeometryPolygon ); - - - function LGraphGeometryExtrude() { - - this.addInput("", "geometry"); - this.addOutput("", "geometry"); - this.properties = { top_cap: true, bottom_cap: true, offset: [0,100,0] }; - this.version = -1; - - this._last_geo_version = -1; - this._must_update = true; - } - - LGraphGeometryExtrude.title = "extrude"; - - LGraphGeometryExtrude.prototype.onPropertyChanged = function(name, value) - { - this._must_update = true; - } - - LGraphGeometryExtrude.prototype.onExecute = function() - { - var geo = this.getInputData(0); - if( !geo || !this.isOutputConnected(0) ) - return; - - if(geo.version != this._last_geo_version || this._must_update) - { - this._geo = this.extrudeGeometry( geo, this._geo ); - if(this._geo) - this._geo.version = this.version++; - this._must_update = false; - } - - this.setOutputData(0, this._geo); - } - - LGraphGeometryExtrude.prototype.extrudeGeometry = function( geo ) - { - //for every pair of vertices - var vertices = geo.vertices; - var num_points = vertices.length / 3; - - var tempA = vec3.create(); - var tempB = vec3.create(); - var tempC = vec3.create(); - var tempD = vec3.create(); - var offset = new Float32Array( this.properties.offset ); - - if(geo.type == "line_loop") - { - var new_vertices = new Float32Array( num_points * 6 * 3 ); //every points become 6 ( caps not included ) - var npos = 0; - for(var i = 0, l = vertices.length; i < l; i += 3) - { - tempA[0] = vertices[i]; tempA[1] = vertices[i+1]; tempA[2] = vertices[i+2]; - - if( i+3 < l ) //loop - { - tempB[0] = vertices[i+3]; tempB[1] = vertices[i+4]; tempB[2] = vertices[i+5]; - } - else - { - tempB[0] = vertices[0]; tempB[1] = vertices[1]; tempB[2] = vertices[2]; - } - - vec3.add( tempC, tempA, offset ); - vec3.add( tempD, tempB, offset ); - - new_vertices.set( tempA, npos ); npos += 3; - new_vertices.set( tempB, npos ); npos += 3; - new_vertices.set( tempC, npos ); npos += 3; - - new_vertices.set( tempB, npos ); npos += 3; - new_vertices.set( tempD, npos ); npos += 3; - new_vertices.set( tempC, npos ); npos += 3; - } - } - - var out_geo = { - _id: generateGeometryId(), - type: "triangles", - vertices: new_vertices - }; - - return out_geo; - } - - LiteGraph.registerNodeType( "geometry/extrude", LGraphGeometryExtrude ); - - - function LGraphGeometryEval() { - this.addInput("in", "geometry"); - this.addOutput("out", "geometry"); - - this.properties = { - code: "V[1] += 0.01 * Math.sin(I + T*0.001);", - execute_every_frame: false - }; - - this.geometry = null; - this.geometry_id = -1; - this.version = -1; - this.must_update = true; - - this.vertices = null; - this.func = null; - } - - LGraphGeometryEval.title = "geoeval"; - LGraphGeometryEval.desc = "eval code"; - - LGraphGeometryEval.widgets_info = { - code: { widget: "code" } + this.setOutputData(0, this._temp_texture); }; - LGraphGeometryEval.prototype.onConfigure = function(o) - { - this.compileCode(); + LiteGraph.registerNodeType( + "texture/imageToTexture", + LGraphImageToTexture + ); + + // Texture LUT ***************************************** + function LGraphTextureLUT() { + this.addInput("Texture", "Texture"); + this.addInput("LUT", "Texture"); + this.addInput("Intensity", "number"); + this.addOutput("", "Texture"); + this.properties = { enabled: true, intensity: 1, precision: LGraphTexture.DEFAULT, texture: null }; + + if (!LGraphTextureLUT._shader) { + LGraphTextureLUT._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, LGraphTextureLUT.pixel_shader ); + } } - LGraphGeometryEval.prototype.compileCode = function() - { - if(!this.properties.code) + LGraphTextureLUT.widgets_info = { + texture: { widget: "texture" }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureLUT.title = "LUT"; + LGraphTextureLUT.desc = "Apply LUT to Texture"; + + LGraphTextureLUT.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { return; + } //saves work - try - { - this.func = new Function("V","I","T", this.properties.code); - this.boxcolor = "#AFA"; - this.must_update = true; - } - catch (err) - { - this.boxcolor = "red"; - } - } + var tex = this.getInputData(0); - LGraphGeometryEval.prototype.onPropertyChanged = function(name, value) - { - if(name == "code") - { - this.properties.code = value; - this.compileCode(); - } - } - - LGraphGeometryEval.prototype.onExecute = function() { - var geometry = this.getInputData(0); - if(!geometry) - return; - - if(!this.func) - { - this.setOutputData(0,geometry); + if (this.properties.precision === LGraphTexture.PASS_THROUGH || this.properties.enabled === false) { + this.setOutputData(0, tex); return; } - if( this.geometry_id != geometry._id || this.version != geometry._version || this.must_update || this.properties.execute_every_frame ) - { - this.must_update = false; - this.geometry_id = geometry._id; - if(this.properties.execute_every_frame) - this.version++; - else - this.version = geometry._version; - var func = this.func; - var T = getTime(); - - //clone - if(!this.geometry) - this.geometry = {}; - for(var i in geometry) - { - if(geometry[i] == null) - continue; - if( geometry[i].constructor == Float32Array ) - this.geometry[i] = new Float32Array( geometry[i] ); - else - this.geometry[i] = geometry[i]; - } - this.geometry._id = geometry._id; - if(this.properties.execute_every_frame) - this.geometry._version = this.version; - else - this.geometry._version = geometry._version + 1; - - var V = vec3.create(); - var vertices = this.vertices; - if(!vertices || this.vertices.length != geometry.vertices.length) - vertices = this.vertices = new Float32Array( geometry.vertices ); - else - vertices.set( geometry.vertices ); - for(var i = 0; i < vertices.length; i+=3) - { - V[0] = vertices[i]; - V[1] = vertices[i+1]; - V[2] = vertices[i+2]; - func(V,i/3,T); - vertices[i] = V[0]; - vertices[i+1] = V[1]; - vertices[i+2] = V[2]; - } - this.geometry.vertices = vertices; + if (!tex) { + return; } - this.setOutputData(0,this.geometry); - } + var lut_tex = this.getInputData(1); - LiteGraph.registerNodeType( "geometry/eval", LGraphGeometryEval ); + if (!lut_tex) { + lut_tex = LGraphTexture.getTexture(this.properties.texture); + } -/* -function LGraphGeometryDisplace() { - this.addInput("in", "geometry"); - this.addInput("img", "image"); - this.addOutput("out", "geometry"); + if (!lut_tex) { + this.setOutputData(0, tex); + return; + } - this.properties = { - grid_size: 1 + lut_tex.bind(0); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri( + gl.TEXTURE_2D, + gl.TEXTURE_WRAP_S, + gl.CLAMP_TO_EDGE + ); + gl.texParameteri( + gl.TEXTURE_2D, + gl.TEXTURE_WRAP_T, + gl.CLAMP_TO_EDGE + ); + gl.bindTexture(gl.TEXTURE_2D, null); + + var intensity = this.properties.intensity; + if (this.isInputConnected(2)) { + this.properties.intensity = intensity = this.getInputData(2); + } + + this._tex = LGraphTexture.getTargetTexture( + tex, + this._tex, + this.properties.precision + ); + + //var mesh = Mesh.getScreenQuad(); + + this._tex.drawTo(function() { + lut_tex.bind(1); + tex.toViewport(LGraphTextureLUT._shader, { + u_texture: 0, + u_textureB: 1, + u_amount: intensity + }); + }); + + this.setOutputData(0, this._tex); + }; + + LGraphTextureLUT.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_textureB;\n\ + uniform float u_amount;\n\ + \n\ + void main() {\n\ + lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\ + mediump float blueColor = textureColor.b * 63.0;\n\ + mediump vec2 quad1;\n\ + quad1.y = floor(floor(blueColor) / 8.0);\n\ + quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\ + mediump vec2 quad2;\n\ + quad2.y = floor(ceil(blueColor) / 8.0);\n\ + quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\ + highp vec2 texPos1;\n\ + texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\ + texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\ + highp vec2 texPos2;\n\ + texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\ + texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\ + lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\ + lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\ + lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\ + gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\ + }\n\ + "; + + LiteGraph.registerNodeType("texture/LUT", LGraphTextureLUT); + + + // Texture LUT ***************************************** + function LGraphTextureEncode() { + this.addInput("Texture", "Texture"); + this.addInput("Atlas", "Texture"); + this.addOutput("", "Texture"); + this.properties = { enabled: true, num_row_symbols: 4, symbol_size: 16, brightness: 1, colorize: false, filter: false, invert: false, precision: LGraphTexture.DEFAULT, texture: null }; + + if (!LGraphTextureEncode._shader) { + LGraphTextureEncode._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, LGraphTextureEncode.pixel_shader ); + } + + this._uniforms = { + u_texture: 0, + u_textureB: 1, + u_row_simbols: 4, + u_simbol_size: 16, + u_res: vec2.create() }; - - this.geometry = null; - this.geometry_id = -1; - this.version = -1; - this.must_update = true; - - this.vertices = null; } - LGraphGeometryDisplace.title = "displace"; - LGraphGeometryDisplace.desc = "displace points"; + LGraphTextureEncode.widgets_info = { + texture: { widget: "texture" }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; - LGraphGeometryDisplace.prototype.onExecute = function() { - var geometry = this.getInputData(0); - var image = this.getInputData(1); - if(!geometry) + LGraphTextureEncode.title = "Encode"; + LGraphTextureEncode.desc = "Apply a texture atlas to encode a texture"; + + LGraphTextureEncode.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { return; + } //saves work - if(!image) - { - this.setOutputData(0,geometry); + var tex = this.getInputData(0); + + if (this.properties.precision === LGraphTexture.PASS_THROUGH || this.properties.enabled === false) { + this.setOutputData(0, tex); return; } - if( this.geometry_id != geometry._id || this.version != geometry._version || this.must_update ) - { - this.must_update = false; - this.geometry_id = geometry._id; - this.version = geometry._version; + if (!tex) { + return; + } - //copy - this.geometry = {}; - for(var i in geometry) - this.geometry[i] = geometry[i]; - this.geometry._id = geometry._id; - this.geometry._version = geometry._version + 1; + var symbols_tex = this.getInputData(1); - var grid_size = this.properties.grid_size; - if(grid_size != 0) - { - var vertices = this.vertices; - if(!vertices || this.vertices.length != this.geometry.vertices.length) - vertices = this.vertices = new Float32Array( this.geometry.vertices ); - for(var i = 0; i < vertices.length; i+=3) - { - vertices[i] = Math.round(vertices[i]/grid_size) * grid_size; - vertices[i+1] = Math.round(vertices[i+1]/grid_size) * grid_size; - vertices[i+2] = Math.round(vertices[i+2]/grid_size) * grid_size; + if (!symbols_tex) { + symbols_tex = LGraphTexture.getTexture(this.properties.texture); + } + + if (!symbols_tex) { + this.setOutputData(0, tex); + return; + } + + symbols_tex.bind(0); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, this.properties.filter ? gl.LINEAR : gl.NEAREST ); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, this.properties.filter ? gl.LINEAR : gl.NEAREST ); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE ); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE ); + gl.bindTexture(gl.TEXTURE_2D, null); + + var uniforms = this._uniforms; + uniforms.u_row_simbols = Math.floor(this.properties.num_row_symbols); + uniforms.u_symbol_size = this.properties.symbol_size; + uniforms.u_brightness = this.properties.brightness; + uniforms.u_invert = this.properties.invert ? 1 : 0; + uniforms.u_colorize = this.properties.colorize ? 1 : 0; + + this._tex = LGraphTexture.getTargetTexture( tex, this._tex, this.properties.precision ); + uniforms.u_res[0] = this._tex.width; + uniforms.u_res[1] = this._tex.height; + this._tex.bind(0); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); + + this._tex.drawTo(function() { + symbols_tex.bind(1); + tex.toViewport(LGraphTextureEncode._shader, uniforms); + }); + + this.setOutputData(0, this._tex); + }; + + LGraphTextureEncode.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_textureB;\n\ + uniform float u_row_simbols;\n\ + uniform float u_symbol_size;\n\ + uniform float u_brightness;\n\ + uniform float u_invert;\n\ + uniform float u_colorize;\n\ + uniform vec2 u_res;\n\ + \n\ + void main() {\n\ + vec2 total_symbols = u_res / u_symbol_size;\n\ + vec2 uv = floor(v_coord * total_symbols) / total_symbols; //pixelate \n\ + vec2 local_uv = mod(v_coord * u_res, u_symbol_size) / u_symbol_size;\n\ + lowp vec4 textureColor = texture2D(u_texture, uv );\n\ + float lum = clamp(u_brightness * (textureColor.x + textureColor.y + textureColor.z)/3.0,0.0,1.0);\n\ + if( u_invert == 1.0 ) lum = 1.0 - lum;\n\ + float index = floor( lum * (u_row_simbols * u_row_simbols - 1.0));\n\ + float col = mod( index, u_row_simbols );\n\ + float row = u_row_simbols - floor( index / u_row_simbols ) - 1.0;\n\ + vec2 simbol_uv = ( vec2( col, row ) + local_uv ) / u_row_simbols;\n\ + vec4 color = texture2D( u_textureB, simbol_uv );\n\ + if(u_colorize == 1.0)\n\ + color *= textureColor;\n\ + gl_FragColor = color;\n\ + }\n\ + "; + + LiteGraph.registerNodeType("texture/encode", LGraphTextureEncode); + + // Texture Channels ***************************************** + function LGraphTextureChannels() { + this.addInput("Texture", "Texture"); + + this.addOutput("R", "Texture"); + this.addOutput("G", "Texture"); + this.addOutput("B", "Texture"); + this.addOutput("A", "Texture"); + + //this.properties = { use_single_channel: true }; + if (!LGraphTextureChannels._shader) { + LGraphTextureChannels._shader = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureChannels.pixel_shader + ); + } + } + + LGraphTextureChannels.title = "Texture to Channels"; + LGraphTextureChannels.desc = "Split texture channels"; + + LGraphTextureChannels.prototype.onExecute = function() { + var texA = this.getInputData(0); + if (!texA) { + return; + } + + if (!this._channels) { + this._channels = Array(4); + } + + //var format = this.properties.use_single_channel ? gl.LUMINANCE : gl.RGBA; //not supported by WebGL1 + var format = gl.RGB; + var connections = 0; + for (var i = 0; i < 4; i++) { + if (this.isOutputConnected(i)) { + if ( + !this._channels[i] || + this._channels[i].width != texA.width || + this._channels[i].height != texA.height || + this._channels[i].type != texA.type || + this._channels[i].format != format + ) { + this._channels[i] = new GL.Texture( + texA.width, + texA.height, + { + type: texA.type, + format: format, + filter: gl.LINEAR + } + ); } - this.geometry.vertices = vertices; + connections++; + } else { + this._channels[i] = null; } } - this.setOutputData(0,this.geometry); - } + if (!connections) { + return; + } - LiteGraph.registerNodeType( "geometry/displace", LGraphGeometryDisplace ); -*/ + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); - function LGraphConnectPoints() { - this.addInput("in", "geometry"); - this.addOutput("out", "geometry"); + var mesh = Mesh.getScreenQuad(); + var shader = LGraphTextureChannels._shader; + var masks = [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1] + ]; + + for (var i = 0; i < 4; i++) { + if (!this._channels[i]) { + continue; + } + + this._channels[i].drawTo(function() { + texA.bind(0); + shader + .uniforms({ u_texture: 0, u_mask: masks[i] }) + .draw(mesh); + }); + this.setOutputData(i, this._channels[i]); + } + }; + + LGraphTextureChannels.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec4 u_mask;\n\ + \n\ + void main() {\n\ + gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\ + }\n\ + "; + + LiteGraph.registerNodeType( + "texture/textureChannels", + LGraphTextureChannels + ); + + // Texture Channels to Texture ***************************************** + function LGraphChannelsTexture() { + this.addInput("R", "Texture"); + this.addInput("G", "Texture"); + this.addInput("B", "Texture"); + this.addInput("A", "Texture"); + + this.addOutput("Texture", "Texture"); this.properties = { - min_dist: 0.4, - max_dist: 0.5, - max_connections: 0, - probability: 1 + precision: LGraphTexture.DEFAULT, + R: 1, + G: 1, + B: 1, + A: 1 + }; + this._color = vec4.create(); + this._uniforms = { + u_textureR: 0, + u_textureG: 1, + u_textureB: 2, + u_textureA: 3, + u_color: this._color }; - - this.geometry_id = -1; - this.version = -1; - this.my_version = 1; - this.must_update = true; } - LGraphConnectPoints.title = "connect points"; - LGraphConnectPoints.desc = "adds indices between near points"; + LGraphChannelsTexture.title = "Channels to Texture"; + LGraphChannelsTexture.desc = "Split texture channels"; + LGraphChannelsTexture.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; - LGraphConnectPoints.prototype.onPropertyChanged = function(name,value) - { - this.must_update = true; + LGraphChannelsTexture.prototype.onExecute = function() { + var white = LGraphTexture.getWhiteTexture(); + var texR = this.getInputData(0) || white; + var texG = this.getInputData(1) || white; + var texB = this.getInputData(2) || white; + var texA = this.getInputData(3) || white; + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + + var mesh = Mesh.getScreenQuad(); + if (!LGraphChannelsTexture._shader) { + LGraphChannelsTexture._shader = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphChannelsTexture.pixel_shader + ); + } + var shader = LGraphChannelsTexture._shader; + + var w = Math.max(texR.width, texG.width, texB.width, texA.width); + var h = Math.max( + texR.height, + texG.height, + texB.height, + texA.height + ); + var type = + this.properties.precision == LGraphTexture.HIGH + ? LGraphTexture.HIGH_PRECISION_FORMAT + : gl.UNSIGNED_BYTE; + + if ( + !this._texture || + this._texture.width != w || + this._texture.height != h || + this._texture.type != type + ) { + this._texture = new GL.Texture(w, h, { + type: type, + format: gl.RGBA, + filter: gl.LINEAR + }); + } + + var color = this._color; + color[0] = this.properties.R; + color[1] = this.properties.G; + color[2] = this.properties.B; + color[3] = this.properties.A; + var uniforms = this._uniforms; + + this._texture.drawTo(function() { + texR.bind(0); + texG.bind(1); + texB.bind(2); + texA.bind(3); + shader.uniforms(uniforms).draw(mesh); + }); + this.setOutputData(0, this._texture); + }; + + LGraphChannelsTexture.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_textureR;\n\ + uniform sampler2D u_textureG;\n\ + uniform sampler2D u_textureB;\n\ + uniform sampler2D u_textureA;\n\ + uniform vec4 u_color;\n\ + \n\ + void main() {\n\ + gl_FragColor = u_color * vec4( \ + texture2D(u_textureR, v_coord).r,\ + texture2D(u_textureG, v_coord).r,\ + texture2D(u_textureB, v_coord).r,\ + texture2D(u_textureA, v_coord).r);\n\ + }\n\ + "; + + LiteGraph.registerNodeType( + "texture/channelsTexture", + LGraphChannelsTexture + ); + + // Texture Color ***************************************** + function LGraphTextureColor() { + this.addOutput("Texture", "Texture"); + + this._tex_color = vec4.create(); + this.properties = { + color: vec4.create(), + precision: LGraphTexture.DEFAULT + }; } - LGraphConnectPoints.prototype.onExecute = function() { - var geometry = this.getInputData(0); - if(!geometry) - return; + LGraphTextureColor.title = "Color"; + LGraphTextureColor.desc = + "Generates a 1x1 texture with a constant color"; - if( this.geometry_id != geometry._id || this.version != geometry._version || this.must_update ) - { - this.must_update = false; - this.geometry_id = geometry._id; - this.version = geometry._version; + LGraphTextureColor.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; - //copy - this.geometry = {}; - for(var i in geometry) - this.geometry[i] = geometry[i]; - this.geometry._id = generateGeometryId(); - this.geometry._version = this.my_version++; + LGraphTextureColor.prototype.onDrawBackground = function(ctx) { + var c = this.properties.color; + ctx.fillStyle = + "rgb(" + + Math.floor(Math.clamp(c[0], 0, 1) * 255) + + "," + + Math.floor(Math.clamp(c[1], 0, 1) * 255) + + "," + + Math.floor(Math.clamp(c[2], 0, 1) * 255) + + ")"; + if (this.flags.collapsed) { + this.boxcolor = ctx.fillStyle; + } else { + ctx.fillRect(0, 0, this.size[0], this.size[1]); + } + }; - var vertices = geometry.vertices; - var l = vertices.length; - var min_dist = this.properties.min_dist; - var max_dist = this.properties.max_dist; - var probability = this.properties.probability; - var max_connections = this.properties.max_connections; - var indices = []; - - for(var i = 0; i < l; i+=3) - { - var x = vertices[i]; - var y = vertices[i+1]; - var z = vertices[i+2]; - var connections = 0; - for(var j = i+3; j < l; j+=3) - { - var x2 = vertices[j]; - var y2 = vertices[j+1]; - var z2 = vertices[j+2]; - var dist = Math.sqrt( (x-x2)*(x-x2) + (y-y2)*(y-y2) + (z-z2)*(z-z2)); - if(dist > max_dist || dist < min_dist || (probability < 1 && probability < Math.random()) ) - continue; - indices.push(i/3,j/3); - connections += 1; - if(max_connections && connections > max_connections) + LGraphTextureColor.prototype.onExecute = function() { + var type = + this.properties.precision == LGraphTexture.HIGH + ? LGraphTexture.HIGH_PRECISION_FORMAT + : gl.UNSIGNED_BYTE; + + if (!this._tex || this._tex.type != type) { + this._tex = new GL.Texture(1, 1, { + format: gl.RGBA, + type: type, + minFilter: gl.NEAREST + }); + } + var color = this.properties.color; + + if (this.inputs) { + for (var i = 0; i < this.inputs.length; i++) { + var input = this.inputs[i]; + var v = this.getInputData(i); + if (v === undefined) { + continue; + } + switch (input.name) { + case "RGB": + case "RGBA": + color.set(v); + break; + case "R": + color[0] = v; + break; + case "G": + color[1] = v; + break; + case "B": + color[2] = v; + break; + case "A": + color[3] = v; break; } } - this.geometry.indices = this.indices = new Uint32Array(indices); } - if(this.indices && this.indices.length) - { - this.geometry.indices = this.indices; - this.setOutputData( 0, this.geometry ); + if (vec4.sqrDist(this._tex_color, color) > 0.001) { + this._tex_color.set(color); + this._tex.fill(color); } - else - this.setOutputData( 0, null ); + this.setOutputData(0, this._tex); + }; + + LGraphTextureColor.prototype.onGetInputs = function() { + return [ + ["RGB", "vec3"], + ["RGBA", "vec4"], + ["R", "number"], + ["G", "number"], + ["B", "number"], + ["A", "number"] + ]; + }; + + LiteGraph.registerNodeType("texture/color", LGraphTextureColor); + + // Texture Channels to Texture ***************************************** + function LGraphTextureGradient() { + this.addInput("A", "color"); + this.addInput("B", "color"); + this.addOutput("Texture", "Texture"); + + this.properties = { + angle: 0, + scale: 1, + A: [0, 0, 0], + B: [1, 1, 1], + texture_size: 32 + }; + if (!LGraphTextureGradient._shader) { + LGraphTextureGradient._shader = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureGradient.pixel_shader + ); + } + + this._uniforms = { + u_angle: 0, + u_colorA: vec3.create(), + u_colorB: vec3.create() + }; } - LiteGraph.registerNodeType( "geometry/connectPoints", LGraphConnectPoints ); + LGraphTextureGradient.title = "Gradient"; + LGraphTextureGradient.desc = "Generates a gradient"; + LGraphTextureGradient["@A"] = { type: "color" }; + LGraphTextureGradient["@B"] = { type: "color" }; + LGraphTextureGradient["@texture_size"] = { + type: "enum", + values: [32, 64, 128, 256, 512] + }; - //Works with Litegl.js to create WebGL nodes - if (typeof GL == "undefined") //LiteGL RELATED ********************************************** - return; + LGraphTextureGradient.prototype.onExecute = function() { + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); - function LGraphToGeometry() { - this.addInput("mesh", "mesh"); - this.addOutput("out", "geometry"); + var mesh = GL.Mesh.getScreenQuad(); + var shader = LGraphTextureGradient._shader; - this.geometry = {}; - this.last_mesh = null; + var A = this.getInputData(0); + if (!A) { + A = this.properties.A; + } + var B = this.getInputData(1); + if (!B) { + B = this.properties.B; + } + + //angle and scale + for (var i = 2; i < this.inputs.length; i++) { + var input = this.inputs[i]; + var v = this.getInputData(i); + if (v === undefined) { + continue; + } + this.properties[input.name] = v; + } + + var uniforms = this._uniforms; + this._uniforms.u_angle = this.properties.angle * DEG2RAD; + this._uniforms.u_scale = this.properties.scale; + vec3.copy(uniforms.u_colorA, A); + vec3.copy(uniforms.u_colorB, B); + + var size = parseInt(this.properties.texture_size); + if (!this._tex || this._tex.width != size) { + this._tex = new GL.Texture(size, size, { + format: gl.RGB, + filter: gl.LINEAR + }); + } + + this._tex.drawTo(function() { + shader.uniforms(uniforms).draw(mesh); + }); + this.setOutputData(0, this._tex); + }; + + LGraphTextureGradient.prototype.onGetInputs = function() { + return [["angle", "number"], ["scale", "number"]]; + }; + + LGraphTextureGradient.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform float u_angle;\n\ + uniform float u_scale;\n\ + uniform vec3 u_colorA;\n\ + uniform vec3 u_colorB;\n\ + \n\ + vec2 rotate(vec2 v, float angle)\n\ + {\n\ + vec2 result;\n\ + float _cos = cos(angle);\n\ + float _sin = sin(angle);\n\ + result.x = v.x * _cos - v.y * _sin;\n\ + result.y = v.x * _sin + v.y * _cos;\n\ + return result;\n\ + }\n\ + void main() {\n\ + float f = (rotate(u_scale * (v_coord - vec2(0.5)), u_angle) + vec2(0.5)).x;\n\ + vec3 color = mix(u_colorA,u_colorB,clamp(f,0.0,1.0));\n\ + gl_FragColor = vec4(color,1.0);\n\ + }\n\ + "; + + LiteGraph.registerNodeType("texture/gradient", LGraphTextureGradient); + + // Texture Mix ***************************************** + function LGraphTextureMix() { + this.addInput("A", "Texture"); + this.addInput("B", "Texture"); + this.addInput("Mixer", "Texture"); + + this.addOutput("Texture", "Texture"); + this.properties = { factor: 0.5, size_from_biggest: true, invert: false, precision: LGraphTexture.DEFAULT }; + this._uniforms = { + u_textureA: 0, + u_textureB: 1, + u_textureMix: 2, + u_mix: vec4.create() + }; } - LGraphToGeometry.title = "to geometry"; - LGraphToGeometry.desc = "converts a mesh to geometry"; + LGraphTextureMix.title = "Mix"; + LGraphTextureMix.desc = "Generates a texture mixing two textures"; - LGraphToGeometry.prototype.onExecute = function() { - var mesh = this.getInputData(0); - if(!mesh) + LGraphTextureMix.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureMix.prototype.onExecute = function() { + var texA = this.getInputData(0); + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, texA); + return; + } + + var texB = this.getInputData(1); + if (!texA || !texB) { + return; + } + + var texMix = this.getInputData(2); + + var factor = this.getInputData(3); + + this._tex = LGraphTexture.getTargetTexture( + this.properties.size_from_biggest && texB.width > texA.width ? texB : texA, + this._tex, + this.properties.precision + ); + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + + var mesh = Mesh.getScreenQuad(); + var shader = null; + var uniforms = this._uniforms; + if (texMix) { + shader = LGraphTextureMix._shader_tex; + if (!shader) { + shader = LGraphTextureMix._shader_tex = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureMix.pixel_shader, + { MIX_TEX: "" } + ); + } + } else { + shader = LGraphTextureMix._shader_factor; + if (!shader) { + shader = LGraphTextureMix._shader_factor = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureMix.pixel_shader + ); + } + var f = factor == null ? this.properties.factor : factor; + uniforms.u_mix.set([f, f, f, f]); + } + + var invert = this.properties.invert; + + this._tex.drawTo(function() { + texA.bind( invert ? 1 : 0 ); + texB.bind( invert ? 0 : 1 ); + if (texMix) { + texMix.bind(2); + } + shader.uniforms(uniforms).draw(mesh); + }); + + this.setOutputData(0, this._tex); + }; + + LGraphTextureMix.prototype.onGetInputs = function() { + return [["factor", "number"]]; + }; + + LGraphTextureMix.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_textureA;\n\ + uniform sampler2D u_textureB;\n\ + #ifdef MIX_TEX\n\ + uniform sampler2D u_textureMix;\n\ + #else\n\ + uniform vec4 u_mix;\n\ + #endif\n\ + \n\ + void main() {\n\ + #ifdef MIX_TEX\n\ + vec4 f = texture2D(u_textureMix, v_coord);\n\ + #else\n\ + vec4 f = u_mix;\n\ + #endif\n\ + gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\ + }\n\ + "; + + LiteGraph.registerNodeType("texture/mix", LGraphTextureMix); + + // Texture Edges detection ***************************************** + function LGraphTextureEdges() { + this.addInput("Tex.", "Texture"); + + this.addOutput("Edges", "Texture"); + this.properties = { + invert: true, + threshold: false, + factor: 1, + precision: LGraphTexture.DEFAULT + }; + + if (!LGraphTextureEdges._shader) { + LGraphTextureEdges._shader = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureEdges.pixel_shader + ); + } + } + + LGraphTextureEdges.title = "Edges"; + LGraphTextureEdges.desc = "Detects edges"; + + LGraphTextureEdges.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureEdges.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { + return; + } //saves work + + var tex = this.getInputData(0); + + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, tex); + return; + } + + if (!tex) { + return; + } + + this._tex = LGraphTexture.getTargetTexture( + tex, + this._tex, + this.properties.precision + ); + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + + var mesh = Mesh.getScreenQuad(); + var shader = LGraphTextureEdges._shader; + var invert = this.properties.invert; + var factor = this.properties.factor; + var threshold = this.properties.threshold ? 1 : 0; + + this._tex.drawTo(function() { + tex.bind(0); + shader + .uniforms({ + u_texture: 0, + u_isize: [1 / tex.width, 1 / tex.height], + u_factor: factor, + u_threshold: threshold, + u_invert: invert ? 1 : 0 + }) + .draw(mesh); + }); + + this.setOutputData(0, this._tex); + }; + + LGraphTextureEdges.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 u_isize;\n\ + uniform int u_invert;\n\ + uniform float u_factor;\n\ + uniform float u_threshold;\n\ + \n\ + void main() {\n\ + vec4 center = texture2D(u_texture, v_coord);\n\ + vec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\ + vec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\ + vec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\ + vec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\ + vec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\ + diff *= u_factor;\n\ + if(u_invert == 1)\n\ + diff.xyz = vec3(1.0) - diff.xyz;\n\ + if( u_threshold == 0.0 )\n\ + gl_FragColor = vec4( diff.xyz, center.a );\n\ + else\n\ + gl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\ + }\n\ + "; + + LiteGraph.registerNodeType("texture/edges", LGraphTextureEdges); + + // Texture Depth ***************************************** + function LGraphTextureDepthRange() { + this.addInput("Texture", "Texture"); + this.addInput("Distance", "number"); + this.addInput("Range", "number"); + this.addOutput("Texture", "Texture"); + this.properties = { + distance: 100, + range: 50, + only_depth: false, + high_precision: false + }; + this._uniforms = { + u_texture: 0, + u_distance: 100, + u_range: 50, + u_camera_planes: null + }; + } + + LGraphTextureDepthRange.title = "Depth Range"; + LGraphTextureDepthRange.desc = "Generates a texture with a depth range"; + + LGraphTextureDepthRange.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { + return; + } //saves work + + var tex = this.getInputData(0); + if (!tex) { + return; + } + + var precision = gl.UNSIGNED_BYTE; + if (this.properties.high_precision) { + precision = gl.half_float_ext ? gl.HALF_FLOAT_OES : gl.FLOAT; + } + + if ( + !this._temp_texture || + this._temp_texture.type != precision || + this._temp_texture.width != tex.width || + this._temp_texture.height != tex.height + ) { + this._temp_texture = new GL.Texture(tex.width, tex.height, { + type: precision, + format: gl.RGBA, + filter: gl.LINEAR + }); + } + + var uniforms = this._uniforms; + + //iterations + var distance = this.properties.distance; + if (this.isInputConnected(1)) { + distance = this.getInputData(1); + this.properties.distance = distance; + } + + var range = this.properties.range; + if (this.isInputConnected(2)) { + range = this.getInputData(2); + this.properties.range = range; + } + + uniforms.u_distance = distance; + uniforms.u_range = range; + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var mesh = Mesh.getScreenQuad(); + if (!LGraphTextureDepthRange._shader) { + LGraphTextureDepthRange._shader = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureDepthRange.pixel_shader + ); + LGraphTextureDepthRange._shader_onlydepth = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureDepthRange.pixel_shader, + { ONLY_DEPTH: "" } + ); + } + var shader = this.properties.only_depth + ? LGraphTextureDepthRange._shader_onlydepth + : LGraphTextureDepthRange._shader; + + //NEAR AND FAR PLANES + var planes = null; + if (tex.near_far_planes) { + planes = tex.near_far_planes; + } else if (window.LS && LS.Renderer._main_camera) { + planes = LS.Renderer._main_camera._uniforms.u_camera_planes; + } else { + planes = [0.1, 1000]; + } //hardcoded + uniforms.u_camera_planes = planes; + + this._temp_texture.drawTo(function() { + tex.bind(0); + shader.uniforms(uniforms).draw(mesh); + }); + + this._temp_texture.near_far_planes = planes; + this.setOutputData(0, this._temp_texture); + }; + + LGraphTextureDepthRange.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 u_camera_planes;\n\ + uniform float u_distance;\n\ + uniform float u_range;\n\ + \n\ + float LinearDepth()\n\ + {\n\ + float zNear = u_camera_planes.x;\n\ + float zFar = u_camera_planes.y;\n\ + float depth = texture2D(u_texture, v_coord).x;\n\ + depth = depth * 2.0 - 1.0;\n\ + return zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\ + }\n\ + \n\ + void main() {\n\ + float depth = LinearDepth();\n\ + #ifdef ONLY_DEPTH\n\ + gl_FragColor = vec4(depth);\n\ + #else\n\ + float diff = abs(depth * u_camera_planes.y - u_distance);\n\ + float dof = 1.0;\n\ + if(diff <= u_range)\n\ + dof = diff / u_range;\n\ + gl_FragColor = vec4(dof);\n\ + #endif\n\ + }\n\ + "; + + LiteGraph.registerNodeType( "texture/depth_range", LGraphTextureDepthRange ); + + + // Texture Depth ***************************************** + function LGraphTextureLinearDepth() { + this.addInput("Texture", "Texture"); + this.addOutput("Texture", "Texture"); + this.properties = { + precision: LGraphTexture.DEFAULT, + invert: false + }; + this._uniforms = { + u_texture: 0, + u_camera_planes: null, //filled later + u_ires: vec2.create() + }; + } + + LGraphTextureLinearDepth.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureLinearDepth.title = "Linear Depth"; + LGraphTextureLinearDepth.desc = "Creates a color texture with linear depth"; + + LGraphTextureLinearDepth.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { + return; + } //saves work + + var tex = this.getInputData(0); + if (!tex || (tex.format != gl.DEPTH_COMPONENT && tex.format != gl.DEPTH_STENCIL) ) { + return; + } + + var precision = this.properties.precision == LGraphTexture.HIGH ? gl.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE; + + if ( !this._temp_texture || this._temp_texture.type != precision || this._temp_texture.width != tex.width || this._temp_texture.height != tex.height ) { + this._temp_texture = new GL.Texture(tex.width, tex.height, { + type: precision, + format: gl.RGB, + filter: gl.LINEAR + }); + } + + var uniforms = this._uniforms; + uniforms.u_invert = this.properties.invert ? 1 : 0; + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var mesh = Mesh.getScreenQuad(); + if(!LGraphTextureLinearDepth._shader) + LGraphTextureLinearDepth._shader = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureLinearDepth.pixel_shader); + var shader = LGraphTextureLinearDepth._shader; + + //NEAR AND FAR PLANES + var planes = null; + if (tex.near_far_planes) { + planes = tex.near_far_planes; + } else if (window.LS && LS.Renderer._main_camera) { + planes = LS.Renderer._main_camera._uniforms.u_camera_planes; + } else { + planes = [0.1, 1000]; + } //hardcoded + uniforms.u_camera_planes = planes; + //uniforms.u_ires.set([1/tex.width, 1/tex.height]); + uniforms.u_ires.set([0,0]); + + this._temp_texture.drawTo(function() { + tex.bind(0); + shader.uniforms(uniforms).draw(mesh); + }); + + this._temp_texture.near_far_planes = planes; + this.setOutputData(0, this._temp_texture); + }; + + LGraphTextureLinearDepth.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 u_camera_planes;\n\ + uniform int u_invert;\n\ + uniform vec2 u_ires;\n\ + \n\ + void main() {\n\ + float zNear = u_camera_planes.x;\n\ + float zFar = u_camera_planes.y;\n\ + float depth = texture2D(u_texture, v_coord + u_ires*0.5).x * 2.0 - 1.0;\n\ + float f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\ + if( u_invert == 1 )\n\ + f = 1.0 - f;\n\ + gl_FragColor = vec4(vec3(f),1.0);\n\ + }\n\ + "; + + LiteGraph.registerNodeType( "texture/linear_depth", LGraphTextureLinearDepth ); + + // Texture Blur ***************************************** + function LGraphTextureBlur() { + this.addInput("Texture", "Texture"); + this.addInput("Iterations", "number"); + this.addInput("Intensity", "number"); + this.addOutput("Blurred", "Texture"); + this.properties = { + intensity: 1, + iterations: 1, + preserve_aspect: false, + scale: [1, 1], + precision: LGraphTexture.DEFAULT + }; + } + + LGraphTextureBlur.title = "Blur"; + LGraphTextureBlur.desc = "Blur a texture"; + + LGraphTextureBlur.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureBlur.max_iterations = 20; + + LGraphTextureBlur.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + var temp = this._final_texture; + + if ( + !temp || + temp.width != tex.width || + temp.height != tex.height || + temp.type != tex.type + ) { + //we need two textures to do the blurring + //this._temp_texture = new GL.Texture( tex.width, tex.height, { type: tex.type, format: gl.RGBA, filter: gl.LINEAR }); + temp = this._final_texture = new GL.Texture( + tex.width, + tex.height, + { type: tex.type, format: gl.RGBA, filter: gl.LINEAR } + ); + } + + //iterations + var iterations = this.properties.iterations; + if (this.isInputConnected(1)) { + iterations = this.getInputData(1); + this.properties.iterations = iterations; + } + iterations = Math.min( + Math.floor(iterations), + LGraphTextureBlur.max_iterations + ); + if (iterations == 0) { + //skip blurring + this.setOutputData(0, tex); + return; + } + + var intensity = this.properties.intensity; + if (this.isInputConnected(2)) { + intensity = this.getInputData(2); + this.properties.intensity = intensity; + } + + //blur sometimes needs an aspect correction + var aspect = LiteGraph.camera_aspect; + if (!aspect && window.gl !== undefined) { + aspect = gl.canvas.height / gl.canvas.width; + } + if (!aspect) { + aspect = 1; + } + aspect = this.properties.preserve_aspect ? aspect : 1; + + var scale = this.properties.scale || [1, 1]; + tex.applyBlur(aspect * scale[0], scale[1], intensity, temp); + for (var i = 1; i < iterations; ++i) { + temp.applyBlur( + aspect * scale[0] * (i + 1), + scale[1] * (i + 1), + intensity + ); + } + + this.setOutputData(0, temp); + }; + + /* +LGraphTextureBlur.pixel_shader = "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 u_offset;\n\ + uniform float u_intensity;\n\ + void main() {\n\ + vec4 sum = vec4(0.0);\n\ + vec4 center = texture2D(u_texture, v_coord);\n\ + sum += texture2D(u_texture, v_coord + u_offset * -4.0) * 0.05/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * -3.0) * 0.09/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * -2.0) * 0.12/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * -1.0) * 0.15/0.98;\n\ + sum += center * 0.16/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * 4.0) * 0.05/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * 3.0) * 0.09/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * 2.0) * 0.12/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * 1.0) * 0.15/0.98;\n\ + gl_FragColor = u_intensity * sum;\n\ + }\n\ + "; +*/ + + LiteGraph.registerNodeType("texture/blur", LGraphTextureBlur); + + // Texture Glow ***************************************** + //based in https://catlikecoding.com/unity/tutorials/advanced-rendering/bloom/ + function LGraphTextureGlow() { + this.addInput("in", "Texture"); + this.addInput("dirt", "Texture"); + this.addOutput("out", "Texture"); + this.addOutput("glow", "Texture"); + this.properties = { + enabled: true, + intensity: 1, + persistence: 0.99, + iterations: 16, + threshold: 0, + scale: 1, + dirt_factor: 0.5, + precision: LGraphTexture.DEFAULT + }; + this._textures = []; + this._uniforms = { + u_intensity: 1, + u_texture: 0, + u_glow_texture: 1, + u_threshold: 0, + u_texel_size: vec2.create() + }; + } + + LGraphTextureGlow.title = "Glow"; + LGraphTextureGlow.desc = "Filters a texture giving it a glow effect"; + LGraphTextureGlow.weights = new Float32Array([0.5, 0.4, 0.3, 0.2]); + + LGraphTextureGlow.widgets_info = { + iterations: { + type: "number", + min: 0, + max: 16, + step: 1, + precision: 0 + }, + threshold: { + type: "number", + min: 0, + max: 10, + step: 0.01, + precision: 2 + }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureGlow.prototype.onGetInputs = function() { + return [ + ["enabled", "boolean"], + ["threshold", "number"], + ["intensity", "number"], + ["persistence", "number"], + ["iterations", "number"], + ["dirt_factor", "number"] + ]; + }; + + LGraphTextureGlow.prototype.onGetOutputs = function() { + return [["average", "Texture"]]; + }; + + LGraphTextureGlow.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (!this.isAnyOutputConnected()) { + return; + } //saves work + + if ( + this.properties.precision === LGraphTexture.PASS_THROUGH || + this.getInputOrProperty("enabled") === false + ) { + this.setOutputData(0, tex); + return; + } + + var width = tex.width; + var height = tex.height; + + var texture_info = { + format: tex.format, + type: tex.type, + minFilter: GL.LINEAR, + magFilter: GL.LINEAR, + wrap: gl.CLAMP_TO_EDGE + }; + var type = LGraphTexture.getTextureType( + this.properties.precision, + tex + ); + + var uniforms = this._uniforms; + var textures = this._textures; + + //cut + var shader = LGraphTextureGlow._cut_shader; + if (!shader) { + shader = LGraphTextureGlow._cut_shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureGlow.cut_pixel_shader + ); + } + + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.BLEND); + + uniforms.u_threshold = this.getInputOrProperty("threshold"); + var currentDestination = (textures[0] = GL.Texture.getTemporary( + width, + height, + texture_info + )); + tex.blit(currentDestination, shader.uniforms(uniforms)); + var currentSource = currentDestination; + + var iterations = this.getInputOrProperty("iterations"); + iterations = Math.clamp(iterations, 1, 16) | 0; + var texel_size = uniforms.u_texel_size; + var intensity = this.getInputOrProperty("intensity"); + + uniforms.u_intensity = 1; + uniforms.u_delta = this.properties.scale; //1 + + //downscale/upscale shader + var shader = LGraphTextureGlow._shader; + if (!shader) { + shader = LGraphTextureGlow._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureGlow.scale_pixel_shader + ); + } + + var i = 1; + //downscale + for (; i < iterations; i++) { + width = width >> 1; + if ((height | 0) > 1) { + height = height >> 1; + } + if (width < 2) { + break; + } + currentDestination = textures[i] = GL.Texture.getTemporary( + width, + height, + texture_info + ); + texel_size[0] = 1 / currentSource.width; + texel_size[1] = 1 / currentSource.height; + currentSource.blit( + currentDestination, + shader.uniforms(uniforms) + ); + currentSource = currentDestination; + } + + //average + if (this.isOutputConnected(2)) { + var average_texture = this._average_texture; + if ( + !average_texture || + average_texture.type != tex.type || + average_texture.format != tex.format + ) { + average_texture = this._average_texture = new GL.Texture( + 1, + 1, + { + type: tex.type, + format: tex.format, + filter: gl.LINEAR + } + ); + } + texel_size[0] = 1 / currentSource.width; + texel_size[1] = 1 / currentSource.height; + uniforms.u_intensity = intensity; + uniforms.u_delta = 1; + currentSource.blit(average_texture, shader.uniforms(uniforms)); + this.setOutputData(2, average_texture); + } + + //upscale and blend + gl.enable(gl.BLEND); + gl.blendFunc(gl.ONE, gl.ONE); + uniforms.u_intensity = this.getInputOrProperty("persistence"); + uniforms.u_delta = 0.5; + + for ( + i -= 2; + i >= 0; + i-- // i-=2 => -1 to point to last element in array, -1 to go to texture above + ) { + currentDestination = textures[i]; + textures[i] = null; + texel_size[0] = 1 / currentSource.width; + texel_size[1] = 1 / currentSource.height; + currentSource.blit( + currentDestination, + shader.uniforms(uniforms) + ); + GL.Texture.releaseTemporary(currentSource); + currentSource = currentDestination; + } + gl.disable(gl.BLEND); + + //glow + if (this.isOutputConnected(1)) { + var glow_texture = this._glow_texture; + if ( + !glow_texture || + glow_texture.width != tex.width || + glow_texture.height != tex.height || + glow_texture.type != type || + glow_texture.format != tex.format + ) { + glow_texture = this._glow_texture = new GL.Texture( + tex.width, + tex.height, + { type: type, format: tex.format, filter: gl.LINEAR } + ); + } + currentSource.blit(glow_texture); + this.setOutputData(1, glow_texture); + } + + //final composition + if (this.isOutputConnected(0)) { + var final_texture = this._final_texture; + if ( + !final_texture || + final_texture.width != tex.width || + final_texture.height != tex.height || + final_texture.type != type || + final_texture.format != tex.format + ) { + final_texture = this._final_texture = new GL.Texture( + tex.width, + tex.height, + { type: type, format: tex.format, filter: gl.LINEAR } + ); + } + + var dirt_texture = this.getInputData(1); + var dirt_factor = this.getInputOrProperty("dirt_factor"); + + uniforms.u_intensity = intensity; + + shader = dirt_texture + ? LGraphTextureGlow._dirt_final_shader + : LGraphTextureGlow._final_shader; + if (!shader) { + if (dirt_texture) { + shader = LGraphTextureGlow._dirt_final_shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureGlow.final_pixel_shader, + { USE_DIRT: "" } + ); + } else { + shader = LGraphTextureGlow._final_shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureGlow.final_pixel_shader + ); + } + } + + final_texture.drawTo(function() { + tex.bind(0); + currentSource.bind(1); + if (dirt_texture) { + shader.setUniform("u_dirt_factor", dirt_factor); + shader.setUniform( + "u_dirt_texture", + dirt_texture.bind(2) + ); + } + shader.toViewport(uniforms); + }); + this.setOutputData(0, final_texture); + } + + GL.Texture.releaseTemporary(currentSource); + }; + + LGraphTextureGlow.cut_pixel_shader = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform float u_threshold;\n\ + void main() {\n\ + gl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\ + }"; + + LGraphTextureGlow.scale_pixel_shader = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 u_texel_size;\n\ + uniform float u_delta;\n\ + uniform float u_intensity;\n\ + \n\ + vec4 sampleBox(vec2 uv) {\n\ + vec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\ + vec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\ + return s * 0.25;\n\ + }\n\ + void main() {\n\ + gl_FragColor = u_intensity * sampleBox( v_coord );\n\ + }"; + + LGraphTextureGlow.final_pixel_shader = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_glow_texture;\n\ + #ifdef USE_DIRT\n\ + uniform sampler2D u_dirt_texture;\n\ + #endif\n\ + uniform vec2 u_texel_size;\n\ + uniform float u_delta;\n\ + uniform float u_intensity;\n\ + uniform float u_dirt_factor;\n\ + \n\ + vec4 sampleBox(vec2 uv) {\n\ + vec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\ + vec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\ + return s * 0.25;\n\ + }\n\ + void main() {\n\ + vec4 glow = sampleBox( v_coord );\n\ + #ifdef USE_DIRT\n\ + glow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\ + #endif\n\ + gl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\ + }"; + + LiteGraph.registerNodeType("texture/glow", LGraphTextureGlow); + + // Texture Filter ***************************************** + function LGraphTextureKuwaharaFilter() { + this.addInput("Texture", "Texture"); + this.addOutput("Filtered", "Texture"); + this.properties = { intensity: 1, radius: 5 }; + } + + LGraphTextureKuwaharaFilter.title = "Kuwahara Filter"; + LGraphTextureKuwaharaFilter.desc = + "Filters a texture giving an artistic oil canvas painting"; + + LGraphTextureKuwaharaFilter.max_radius = 10; + LGraphTextureKuwaharaFilter._shaders = []; + + LGraphTextureKuwaharaFilter.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + var temp = this._temp_texture; + + if ( + !temp || + temp.width != tex.width || + temp.height != tex.height || + temp.type != tex.type + ) { + this._temp_texture = new GL.Texture(tex.width, tex.height, { + type: tex.type, + format: gl.RGBA, + filter: gl.LINEAR + }); + } + + //iterations + var radius = this.properties.radius; + radius = Math.min( + Math.floor(radius), + LGraphTextureKuwaharaFilter.max_radius + ); + if (radius == 0) { + //skip blurring + this.setOutputData(0, tex); + return; + } + + var intensity = this.properties.intensity; + + //blur sometimes needs an aspect correction + var aspect = LiteGraph.camera_aspect; + if (!aspect && window.gl !== undefined) { + aspect = gl.canvas.height / gl.canvas.width; + } + if (!aspect) { + aspect = 1; + } + aspect = this.properties.preserve_aspect ? aspect : 1; + + if (!LGraphTextureKuwaharaFilter._shaders[radius]) { + LGraphTextureKuwaharaFilter._shaders[radius] = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureKuwaharaFilter.pixel_shader, + { RADIUS: radius.toFixed(0) } + ); + } + + var shader = LGraphTextureKuwaharaFilter._shaders[radius]; + var mesh = GL.Mesh.getScreenQuad(); + tex.bind(0); + + this._temp_texture.drawTo(function() { + shader + .uniforms({ + u_texture: 0, + u_intensity: intensity, + u_resolution: [tex.width, tex.height], + u_iResolution: [1 / tex.width, 1 / tex.height] + }) + .draw(mesh); + }); + + this.setOutputData(0, this._temp_texture); + }; + + //from https://www.shadertoy.com/view/MsXSz4 + LGraphTextureKuwaharaFilter.pixel_shader = + "\n\ +precision highp float;\n\ +varying vec2 v_coord;\n\ +uniform sampler2D u_texture;\n\ +uniform float u_intensity;\n\ +uniform vec2 u_resolution;\n\ +uniform vec2 u_iResolution;\n\ +#ifndef RADIUS\n\ + #define RADIUS 7\n\ +#endif\n\ +void main() {\n\ +\n\ + const int radius = RADIUS;\n\ + vec2 fragCoord = v_coord;\n\ + vec2 src_size = u_iResolution;\n\ + vec2 uv = v_coord;\n\ + float n = float((radius + 1) * (radius + 1));\n\ + int i;\n\ + int j;\n\ + vec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\ + vec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\ + vec3 c;\n\ + \n\ + for (int j = -radius; j <= 0; ++j) {\n\ + for (int i = -radius; i <= 0; ++i) {\n\ + c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ + m0 += c;\n\ + s0 += c * c;\n\ + }\n\ + }\n\ + \n\ + for (int j = -radius; j <= 0; ++j) {\n\ + for (int i = 0; i <= radius; ++i) {\n\ + c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ + m1 += c;\n\ + s1 += c * c;\n\ + }\n\ + }\n\ + \n\ + for (int j = 0; j <= radius; ++j) {\n\ + for (int i = 0; i <= radius; ++i) {\n\ + c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ + m2 += c;\n\ + s2 += c * c;\n\ + }\n\ + }\n\ + \n\ + for (int j = 0; j <= radius; ++j) {\n\ + for (int i = -radius; i <= 0; ++i) {\n\ + c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ + m3 += c;\n\ + s3 += c * c;\n\ + }\n\ + }\n\ + \n\ + float min_sigma2 = 1e+2;\n\ + m0 /= n;\n\ + s0 = abs(s0 / n - m0 * m0);\n\ + \n\ + float sigma2 = s0.r + s0.g + s0.b;\n\ + if (sigma2 < min_sigma2) {\n\ + min_sigma2 = sigma2;\n\ + gl_FragColor = vec4(m0, 1.0);\n\ + }\n\ + \n\ + m1 /= n;\n\ + s1 = abs(s1 / n - m1 * m1);\n\ + \n\ + sigma2 = s1.r + s1.g + s1.b;\n\ + if (sigma2 < min_sigma2) {\n\ + min_sigma2 = sigma2;\n\ + gl_FragColor = vec4(m1, 1.0);\n\ + }\n\ + \n\ + m2 /= n;\n\ + s2 = abs(s2 / n - m2 * m2);\n\ + \n\ + sigma2 = s2.r + s2.g + s2.b;\n\ + if (sigma2 < min_sigma2) {\n\ + min_sigma2 = sigma2;\n\ + gl_FragColor = vec4(m2, 1.0);\n\ + }\n\ + \n\ + m3 /= n;\n\ + s3 = abs(s3 / n - m3 * m3);\n\ + \n\ + sigma2 = s3.r + s3.g + s3.b;\n\ + if (sigma2 < min_sigma2) {\n\ + min_sigma2 = sigma2;\n\ + gl_FragColor = vec4(m3, 1.0);\n\ + }\n\ +}\n\ +"; + + LiteGraph.registerNodeType( + "texture/kuwahara", + LGraphTextureKuwaharaFilter + ); + + // Texture ***************************************** + function LGraphTextureXDoGFilter() { + this.addInput("Texture", "Texture"); + this.addOutput("Filtered", "Texture"); + this.properties = { + sigma: 1.4, + k: 1.6, + p: 21.7, + epsilon: 79, + phi: 0.017 + }; + } + + LGraphTextureXDoGFilter.title = "XDoG Filter"; + LGraphTextureXDoGFilter.desc = + "Filters a texture giving an artistic ink style"; + + LGraphTextureXDoGFilter.max_radius = 10; + LGraphTextureXDoGFilter._shaders = []; + + LGraphTextureXDoGFilter.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + var temp = this._temp_texture; + if ( + !temp || + temp.width != tex.width || + temp.height != tex.height || + temp.type != tex.type + ) { + this._temp_texture = new GL.Texture(tex.width, tex.height, { + type: tex.type, + format: gl.RGBA, + filter: gl.LINEAR + }); + } + + if (!LGraphTextureXDoGFilter._xdog_shader) { + LGraphTextureXDoGFilter._xdog_shader = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureXDoGFilter.xdog_pixel_shader + ); + } + var shader = LGraphTextureXDoGFilter._xdog_shader; + var mesh = GL.Mesh.getScreenQuad(); + + var sigma = this.properties.sigma; + var k = this.properties.k; + var p = this.properties.p; + var epsilon = this.properties.epsilon; + var phi = this.properties.phi; + tex.bind(0); + this._temp_texture.drawTo(function() { + shader + .uniforms({ + src: 0, + sigma: sigma, + k: k, + p: p, + epsilon: epsilon, + phi: phi, + cvsWidth: tex.width, + cvsHeight: tex.height + }) + .draw(mesh); + }); + + this.setOutputData(0, this._temp_texture); + }; + + //from https://github.com/RaymondMcGuire/GPU-Based-Image-Processing-Tools/blob/master/lib_webgl/scripts/main.js + LGraphTextureXDoGFilter.xdog_pixel_shader = + "\n\ +precision highp float;\n\ +uniform sampler2D src;\n\n\ +uniform float cvsHeight;\n\ +uniform float cvsWidth;\n\n\ +uniform float sigma;\n\ +uniform float k;\n\ +uniform float p;\n\ +uniform float epsilon;\n\ +uniform float phi;\n\ +varying vec2 v_coord;\n\n\ +float cosh(float val)\n\ +{\n\ + float tmp = exp(val);\n\ + float cosH = (tmp + 1.0 / tmp) / 2.0;\n\ + return cosH;\n\ +}\n\n\ +float tanh(float val)\n\ +{\n\ + float tmp = exp(val);\n\ + float tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\ + return tanH;\n\ +}\n\n\ +float sinh(float val)\n\ +{\n\ + float tmp = exp(val);\n\ + float sinH = (tmp - 1.0 / tmp) / 2.0;\n\ + return sinH;\n\ +}\n\n\ +void main(void){\n\ + vec3 destColor = vec3(0.0);\n\ + float tFrag = 1.0 / cvsHeight;\n\ + float sFrag = 1.0 / cvsWidth;\n\ + vec2 Frag = vec2(sFrag,tFrag);\n\ + vec2 uv = gl_FragCoord.st;\n\ + float twoSigmaESquared = 2.0 * sigma * sigma;\n\ + float twoSigmaRSquared = twoSigmaESquared * k * k;\n\ + int halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\ + const int MAX_NUM_ITERATION = 99999;\n\ + vec2 sum = vec2(0.0);\n\ + vec2 norm = vec2(0.0);\n\n\ + for(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\ + int i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\ + int j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\ + float d = length(vec2(i,j));\n\ + vec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\ + exp( -d * d / twoSigmaRSquared ));\n\n\ + vec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\ + norm += kernel;\n\ + sum += kernel * L;\n\ + }\n\n\ + sum /= norm;\n\n\ + float H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\ + float edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\ + destColor = vec3(edge);\n\ + gl_FragColor = vec4(destColor, 1.0);\n\ +}"; + + LiteGraph.registerNodeType("texture/xDoG", LGraphTextureXDoGFilter); + + // Texture Webcam ***************************************** + function LGraphTextureWebcam() { + this.addOutput("Webcam", "Texture"); + this.properties = { texture_name: "", facingMode: "user" }; + this.boxcolor = "black"; + this.version = 0; + } + + LGraphTextureWebcam.title = "Webcam"; + LGraphTextureWebcam.desc = "Webcam texture"; + + LGraphTextureWebcam.is_webcam_open = false; + + LGraphTextureWebcam.prototype.openStream = function() { + if (!navigator.getUserMedia) { + //console.log('getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags'); + return; + } + + this._waiting_confirmation = true; + + // Not showing vendor prefixes. + var constraints = { + audio: false, + video: { facingMode: this.properties.facingMode } + }; + navigator.mediaDevices + .getUserMedia(constraints) + .then(this.streamReady.bind(this)) + .catch(onFailSoHard); + + var that = this; + function onFailSoHard(e) { + LGraphTextureWebcam.is_webcam_open = false; + console.log("Webcam rejected", e); + that._webcam_stream = false; + that.boxcolor = "red"; + that.trigger("stream_error"); + } + }; + + LGraphTextureWebcam.prototype.closeStream = function() { + if (this._webcam_stream) { + var tracks = this._webcam_stream.getTracks(); + if (tracks.length) { + for (var i = 0; i < tracks.length; ++i) { + tracks[i].stop(); + } + } + LGraphTextureWebcam.is_webcam_open = false; + this._webcam_stream = null; + this._video = null; + this.boxcolor = "black"; + this.trigger("stream_closed"); + } + }; + + LGraphTextureWebcam.prototype.streamReady = function(localMediaStream) { + this._webcam_stream = localMediaStream; + //this._waiting_confirmation = false; + this.boxcolor = "green"; + var video = this._video; + if (!video) { + video = document.createElement("video"); + video.autoplay = true; + video.srcObject = localMediaStream; + this._video = video; + //document.body.appendChild( video ); //debug + //when video info is loaded (size and so) + video.onloadedmetadata = function(e) { + // Ready to go. Do some stuff. + LGraphTextureWebcam.is_webcam_open = true; + console.log(e); + }; + } + this.trigger("stream_ready", video); + }; + + LGraphTextureWebcam.prototype.onPropertyChanged = function( + name, + value + ) { + if (name == "facingMode") { + this.properties.facingMode = value; + this.closeStream(); + this.openStream(); + } + }; + + LGraphTextureWebcam.prototype.onRemoved = function() { + if (!this._webcam_stream) { + return; + } + + var tracks = this._webcam_stream.getTracks(); + if (tracks.length) { + for (var i = 0; i < tracks.length; ++i) { + tracks[i].stop(); + } + } + + this._webcam_stream = null; + this._video = null; + }; + + LGraphTextureWebcam.prototype.onDrawBackground = function(ctx) { + if (this.flags.collapsed || this.size[1] <= 20) { + return; + } + + if (!this._video) { + return; + } + + //render to graph canvas + ctx.save(); + if (!ctx.webgl) { + //reverse image + ctx.drawImage(this._video, 0, 0, this.size[0], this.size[1]); + } else { + if (this._video_texture) { + ctx.drawImage( + this._video_texture, + 0, + 0, + this.size[0], + this.size[1] + ); + } + } + ctx.restore(); + }; + + LGraphTextureWebcam.prototype.onExecute = function() { + if (this._webcam_stream == null && !this._waiting_confirmation) { + this.openStream(); + } + + if (!this._video || !this._video.videoWidth) { + return; + } + + var width = this._video.videoWidth; + var height = this._video.videoHeight; + + var temp = this._video_texture; + if (!temp || temp.width != width || temp.height != height) { + this._video_texture = new GL.Texture(width, height, { + format: gl.RGB, + filter: gl.LINEAR + }); + } + + this._video_texture.uploadImage(this._video); + this._video_texture.version = ++this.version; + + if (this.properties.texture_name) { + var container = LGraphTexture.getTexturesContainer(); + container[this.properties.texture_name] = this._video_texture; + } + + this.setOutputData(0, this._video_texture); + for (var i = 1; i < this.outputs.length; ++i) { + if (!this.outputs[i]) { + continue; + } + switch (this.outputs[i].name) { + case "width": + this.setOutputData(i, this._video.videoWidth); + break; + case "height": + this.setOutputData(i, this._video.videoHeight); + break; + } + } + }; + + LGraphTextureWebcam.prototype.onGetOutputs = function() { + return [ + ["width", "number"], + ["height", "number"], + ["stream_ready", LiteGraph.EVENT], + ["stream_closed", LiteGraph.EVENT], + ["stream_error", LiteGraph.EVENT] + ]; + }; + + LiteGraph.registerNodeType("texture/webcam", LGraphTextureWebcam); + + //from https://github.com/spite/Wagner + function LGraphLensFX() { + this.addInput("in", "Texture"); + this.addInput("f", "number"); + this.addOutput("out", "Texture"); + this.properties = { + enabled: true, + factor: 1, + precision: LGraphTexture.LOW + }; + + this._uniforms = { u_texture: 0, u_factor: 1 }; + } + + LGraphLensFX.title = "Lens FX"; + LGraphLensFX.desc = "distortion and chromatic aberration"; + + LGraphLensFX.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphLensFX.prototype.onGetInputs = function() { + return [["enabled", "boolean"]]; + }; + + LGraphLensFX.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + if ( + this.properties.precision === LGraphTexture.PASS_THROUGH || + this.getInputOrProperty("enabled") === false + ) { + this.setOutputData(0, tex); + return; + } + + var temp = this._temp_texture; + if ( + !temp || + temp.width != tex.width || + temp.height != tex.height || + temp.type != tex.type + ) { + temp = this._temp_texture = new GL.Texture( + tex.width, + tex.height, + { type: tex.type, format: gl.RGBA, filter: gl.LINEAR } + ); + } + + var shader = LGraphLensFX._shader; + if (!shader) { + shader = LGraphLensFX._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphLensFX.pixel_shader + ); + } + + var factor = this.getInputData(1); + if (factor == null) { + factor = this.properties.factor; + } + + var uniforms = this._uniforms; + uniforms.u_factor = factor; + + //apply shader + gl.disable(gl.DEPTH_TEST); + temp.drawTo(function() { + tex.bind(0); + shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad()); + }); + + this.setOutputData(0, temp); + }; + + LGraphLensFX.pixel_shader = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform float u_factor;\n\ + vec2 barrelDistortion(vec2 coord, float amt) {\n\ + vec2 cc = coord - 0.5;\n\ + float dist = dot(cc, cc);\n\ + return coord + cc * dist * amt;\n\ + }\n\ + \n\ + float sat( float t )\n\ + {\n\ + return clamp( t, 0.0, 1.0 );\n\ + }\n\ + \n\ + float linterp( float t ) {\n\ + return sat( 1.0 - abs( 2.0*t - 1.0 ) );\n\ + }\n\ + \n\ + float remap( float t, float a, float b ) {\n\ + return sat( (t - a) / (b - a) );\n\ + }\n\ + \n\ + vec4 spectrum_offset( float t ) {\n\ + vec4 ret;\n\ + float lo = step(t,0.5);\n\ + float hi = 1.0-lo;\n\ + float w = linterp( remap( t, 1.0/6.0, 5.0/6.0 ) );\n\ + ret = vec4(lo,1.0,hi, 1.) * vec4(1.0-w, w, 1.0-w, 1.);\n\ + \n\ + return pow( ret, vec4(1.0/2.2) );\n\ + }\n\ + \n\ + const float max_distort = 2.2;\n\ + const int num_iter = 12;\n\ + const float reci_num_iter_f = 1.0 / float(num_iter);\n\ + \n\ + void main()\n\ + { \n\ + vec2 uv=v_coord;\n\ + vec4 sumcol = vec4(0.0);\n\ + vec4 sumw = vec4(0.0); \n\ + for ( int i=0; i= 1) - gl.disable( gl.BLEND ); - else - gl.enable( gl.BLEND ); - gl.enable( gl.DEPTH_TEST ); - if( this.properties.additive ) - { - gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); - gl.depthMask( false ); - } - else - gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA ); - - var indices = "indices"; - if( mesh.indexBuffers.triangles ) - indices = "triangles"; - shader.draw( mesh, primitive, indices ); - gl.disable( gl.BLEND ); - gl.depthMask( true ); - } - - LiteGraph.registerNodeType( "geometry/render_mesh", LGraphRenderMesh ); - - //************************** - - - function LGraphGeometryPrimitive() { - this.addInput("size", "number"); - this.addOutput("out", "mesh"); - this.properties = { type: 1, size: 1, subdivisions: 32 }; - - this.version = (Math.random() * 100000)|0; - this.last_info = { type: -1, size: -1, subdivisions: -1 }; - } - - LGraphGeometryPrimitive.title = "Primitive"; - - LGraphGeometryPrimitive.VALID = { "CUBE":1, "PLANE":2, "CYLINDER":3, "SPHERE":4, "CIRCLE":5, "HEMISPHERE":6, "ICOSAHEDRON":7, "CONE":8, "QUAD":9 }; - LGraphGeometryPrimitive.widgets_info = { - type: { widget: "combo", values: LGraphGeometryPrimitive.VALID } - }; - - LGraphGeometryPrimitive.prototype.onExecute = function() { - - if( !this.isOutputConnected(0) ) - return; - - var size = this.getInputOrProperty("size"); - - //update - if( this.last_info.type != this.properties.type || this.last_info.size != size || this.last_info.subdivisions != this.properties.subdivisions ) - this.updateMesh( this.properties.type, size, this.properties.subdivisions ); - - this.setOutputData(0,this._mesh); - } - - LGraphGeometryPrimitive.prototype.updateMesh = function(type, size, subdivisions) + LGraphTextureCurve.prototype.onSerialize = function(o) { - subdivisions = Math.max(0,subdivisions)|0; - - switch (type) - { - case 1: //CUBE: - this._mesh = GL.Mesh.cube({size: size, normals:true,coords:true}); - break; - case 2: //PLANE: - this._mesh = GL.Mesh.plane({size: size, xz: true, detail: subdivisions, normals:true,coords:true}); - break; - case 3: //CYLINDER: - this._mesh = GL.Mesh.cylinder({size: size, subdivisions: subdivisions, normals:true,coords:true}); - break; - case 4: //SPHERE: - this._mesh = GL.Mesh.sphere({size: size, "long": subdivisions, lat: subdivisions, normals:true,coords:true}); - break; - case 5: //CIRCLE: - this._mesh = GL.Mesh.circle({size: size, slices: subdivisions, normals:true, coords:true}); - break; - case 6: //HEMISPHERE: - this._mesh = GL.Mesh.sphere({size: size, "long": subdivisions, lat: subdivisions, normals:true, coords:true, hemi: true}); - break; - case 7: //ICOSAHEDRON: - this._mesh = GL.Mesh.icosahedron({size: size, subdivisions:subdivisions }); - break; - case 8: //CONE: - this._mesh = GL.Mesh.cone({radius: size, height: size, subdivisions:subdivisions }); - break; - case 9: //QUAD: - this._mesh = GL.Mesh.plane({size: size, xz: false, detail: subdivisions, normals:true, coords:true }); - break; - } - - this.last_info.type = type; - this.last_info.size = size; - this.last_info.subdivisions = subdivisions; - this._mesh.version = this.version++; + var curves = {}; + for(var i in this._points) + curves[i] = this._points[i].concat(); + o.curves = curves; } - LiteGraph.registerNodeType( "geometry/mesh_primitive", LGraphGeometryPrimitive ); - - - function LGraphRenderPoints() { - this.addInput("in", "geometry"); - this.addInput("mat4", "mat4"); - this.addInput("tex", "texture"); - this.properties = { - enabled: true, - point_size: 0.1, - fixed_size: false, - additive: true, - color: [1,1,1], - opacity: 1 - }; - - this.color = vec4.create([1,1,1,1]); - - this.uniforms = { - u_point_size: 1, - u_perspective: 1, - u_point_perspective: 1, - u_color: this.color - }; - - this.geometry_id = -1; - this.version = -1; - this.mesh = null; - } - - LGraphRenderPoints.title = "renderPoints"; - LGraphRenderPoints.desc = "render points with a texture"; - - LGraphRenderPoints.widgets_info = { - color: { widget: "color" } - }; - - LGraphRenderPoints.prototype.updateMesh = function(geometry) + LGraphTextureCurve.prototype.onConfigure = function(o) { - var buffer = this.buffer; - if(!this.buffer || !this.buffer.data || this.buffer.data.length != geometry.vertices.length) - this.buffer = new GL.Buffer( GL.ARRAY_BUFFER, geometry.vertices,3,GL.DYNAMIC_DRAW); - else - { - this.buffer.data.set( geometry.vertices ); - this.buffer.upload(GL.DYNAMIC_DRAW); - } - - if(!this.mesh) - this.mesh = new GL.Mesh(); - - this.mesh.addBuffer("vertices",this.buffer); - this.geometry_id = this.mesh.id = geometry._id; - this.version = this.mesh.version = geometry._version; + this._points = o.curves; + if(this.curve_editor) + curve_editor.points = this._points; + this._must_update = true; } - LGraphRenderPoints.prototype.onExecute = function() { - - if(!this.properties.enabled) - return; - - var geometry = this.getInputData(0); - if(!geometry) - return; - if(this.version != geometry._version || this.geometry_id != geometry._id ) - this.updateMesh( geometry ); - - if(!LiteGraph.LGraphRender.onRequestCameraMatrices) + LGraphTextureCurve.prototype.onMouseDown = function(e, localpos, graphcanvas) + { + if(this.curve_editor) { - console.warn("cannot render geometry, LiteGraph.onRequestCameraMatrices is null, remember to fill this with a callback(view_matrix, projection_matrix,viewprojection_matrix) to use 3D rendering from the graph"); - return; + var r = this.curve_editor.onMouseDown([localpos[0],localpos[1]-this.curve_offset], graphcanvas); + if(r) + this.captureInput(true); + return r; } - - LiteGraph.LGraphRender.onRequestCameraMatrices( view_matrix, projection_matrix,viewprojection_matrix ); - var shader = null; - - var texture = this.getInputData(2); - - if(texture) - { - shader = gl.shaders["textured_points"]; - if(!shader) - shader = gl.shaders["textured_points"] = new GL.Shader( LGraphRenderPoints.vertex_shader_code, LGraphRenderPoints.fragment_shader_code, { USE_TEXTURED_POINTS:"" }); - } - else - { - shader = gl.shaders["points"]; - if(!shader) - shader = gl.shaders["points"] = new GL.Shader( LGraphRenderPoints.vertex_shader_code, LGraphRenderPoints.fragment_shader_code, { USE_POINTS: "" }); - } - - this.color.set( this.properties.color ); - this.color[3] = this.properties.opacity; - - var m = this.getInputData(1); - if(m) - model_matrix.set(m); - else - mat4.identity( model_matrix ); - - this.uniforms.u_point_size = this.properties.point_size; - this.uniforms.u_point_perspective = this.properties.fixed_size ? 0 : 1; - this.uniforms.u_perspective = gl.viewport_data[3] * projection_matrix[5]; - - shader.uniforms( global_uniforms ); - shader.uniforms( this.uniforms ); - - if(this.properties.opacity >= 1) - gl.disable( gl.BLEND ); - else - gl.enable( gl.BLEND ); - - gl.enable( gl.DEPTH_TEST ); - if( this.properties.additive ) - { - gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); - gl.depthMask( false ); - } - else - gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA ); - - shader.draw( this.mesh, GL.POINTS ); - - gl.disable( gl.BLEND ); - gl.depthMask( true ); } - LiteGraph.registerNodeType( "geometry/render_points", LGraphRenderPoints ); + LGraphTextureCurve.prototype.onMouseMove = function(e, localpos, graphcanvas) + { + if(this.curve_editor) + return this.curve_editor.onMouseMove([localpos[0],localpos[1]-this.curve_offset], graphcanvas); + } - LGraphRenderPoints.vertex_shader_code = '\ - precision mediump float;\n\ - attribute vec3 a_vertex;\n\ - varying vec3 v_vertex;\n\ - attribute vec3 a_normal;\n\ - varying vec3 v_normal;\n\ - #ifdef USE_COLOR\n\ - attribute vec4 a_color;\n\ - varying vec4 v_color;\n\ - #endif\n\ - attribute vec2 a_coord;\n\ - varying vec2 v_coord;\n\ - #ifdef USE_SIZE\n\ - attribute float a_extra;\n\ - #endif\n\ - #ifdef USE_INSTANCING\n\ - attribute mat4 u_model;\n\ - #else\n\ - uniform mat4 u_model;\n\ - #endif\n\ - uniform mat4 u_viewprojection;\n\ - uniform float u_point_size;\n\ - uniform float u_perspective;\n\ - uniform float u_point_perspective;\n\ - float computePointSize(float radius, float w)\n\ - {\n\ - if(radius < 0.0)\n\ - return -radius;\n\ - return u_perspective * radius / w;\n\ - }\n\ - void main() {\n\ - v_coord = a_coord;\n\ - #ifdef USE_COLOR\n\ - v_color = a_color;\n\ - #endif\n\ - v_vertex = ( u_model * vec4( a_vertex, 1.0 )).xyz;\n\ - v_normal = ( u_model * vec4( a_normal, 0.0 )).xyz;\n\ - gl_Position = u_viewprojection * vec4(v_vertex,1.0);\n\ - gl_PointSize = u_point_size;\n\ - #ifdef USE_SIZE\n\ - gl_PointSize = a_extra;\n\ - #endif\n\ - if(u_point_perspective != 0.0)\n\ - gl_PointSize = computePointSize( gl_PointSize, gl_Position.w );\n\ - }\ - '; + LGraphTextureCurve.prototype.onMouseUp = function(e, localpos, graphcanvas) + { + if(this.curve_editor) + return this.curve_editor.onMouseUp([localpos[0],localpos[1]-this.curve_offset], graphcanvas); + this.captureInput(false); + } - LGraphRenderPoints.fragment_shader_code = '\ - precision mediump float;\n\ - uniform vec4 u_color;\n\ - #ifdef USE_COLOR\n\ - varying vec4 v_color;\n\ - #endif\n\ + LGraphTextureCurve.channel_line_colors = { "RGB":"#666","R":"#F33","G":"#3F3","B":"#33F" }; + + LGraphTextureCurve.prototype.onDrawBackground = function(ctx, graphcanvas) + { + if(this.flags.collapsed) + return; + + if(!this.curve_editor) + this.curve_editor = new LiteGraph.CurveEditor(this._points.R); + ctx.save(); + ctx.translate(0,this.curve_offset); + var channel = this.widgets[1].value; + + if(this.properties.split_channels) + { + if(channel == "RGB") + { + this.widgets[1].value = channel = "R"; + this.widgets[1].disabled = false; + } + this.curve_editor.points = this._points.R; + this.curve_editor.draw( ctx, [this.size[0],this.size[1] - this.curve_offset], graphcanvas, "#111", LGraphTextureCurve.channel_line_colors.R, true ); + ctx.globalCompositeOperation = "lighten"; + this.curve_editor.points = this._points.G; + this.curve_editor.draw( ctx, [this.size[0],this.size[1] - this.curve_offset], graphcanvas, null, LGraphTextureCurve.channel_line_colors.G, true ); + this.curve_editor.points = this._points.B; + this.curve_editor.draw( ctx, [this.size[0],this.size[1] - this.curve_offset], graphcanvas, null, LGraphTextureCurve.channel_line_colors.B, true ); + ctx.globalCompositeOperation = "source-over"; + } + else + { + this.widgets[1].value = channel = "RGB"; + this.widgets[1].disabled = true; + } + + this.curve_editor.points = this._points[channel]; + this.curve_editor.draw( ctx, [this.size[0],this.size[1] - this.curve_offset], graphcanvas, this.properties.split_channels ? null : "#111", LGraphTextureCurve.channel_line_colors[channel] ); + ctx.restore(); + } + + LGraphTextureCurve.pixel_shader = + "precision highp float;\n\ varying vec2 v_coord;\n\ uniform sampler2D u_texture;\n\ + uniform sampler2D u_curve;\n\ + uniform float u_range;\n\ + \n\ void main() {\n\ - vec4 color = u_color;\n\ - #ifdef USE_TEXTURED_POINTS\n\ - color *= texture2D(u_texture, gl_PointCoord.xy);\n\ - #else\n\ - #ifdef USE_TEXTURE\n\ - color *= texture2D(u_texture, v_coord);\n\ - if(color.a < 0.1)\n\ - discard;\n\ - #endif\n\ - #ifdef USE_POINTS\n\ - float dist = length( gl_PointCoord.xy - vec2(0.5) );\n\ - if( dist > 0.45 )\n\ - discard;\n\ - #endif\n\ - #endif\n\ - #ifdef USE_COLOR\n\ - color *= v_color;\n\ - #endif\n\ + vec4 color = texture2D( u_texture, v_coord ) * u_range;\n\ + color.x = texture2D( u_curve, vec2( color.x, 0.5 ) ).x;\n\ + color.y = texture2D( u_curve, vec2( color.y, 0.5 ) ).y;\n\ + color.z = texture2D( u_curve, vec2( color.z, 0.5 ) ).z;\n\ + //color.w = texture2D( u_curve, vec2( color.w, 0.5 ) ).w;\n\ gl_FragColor = color;\n\ - }\ - '; + }"; - //based on https://inconvergent.net/2019/depth-of-field/ - /* - function LGraphRenderGeometryDOF() { - this.addInput("in", "geometry"); - this.addInput("mat4", "mat4"); - this.addInput("tex", "texture"); - this.properties = { - enabled: true, - lines: true, - point_size: 0.1, - fixed_size: false, - additive: true, - color: [1,1,1], - opacity: 1 - }; + LiteGraph.registerNodeType("texture/curve", LGraphTextureCurve); - this.color = vec4.create([1,1,1,1]); - - this.uniforms = { - u_point_size: 1, - u_perspective: 1, - u_point_perspective: 1, - u_color: this.color - }; - - this.geometry_id = -1; - this.version = -1; - this.mesh = null; + //simple exposition, but plan to expand it to support different gamma curves + function LGraphExposition() { + this.addInput("in", "Texture"); + this.addInput("exp", "number"); + this.addOutput("out", "Texture"); + this.properties = { exposition: 1, precision: LGraphTexture.LOW }; + this._uniforms = { u_texture: 0, u_exposition: 1 }; } - LGraphRenderGeometryDOF.widgets_info = { - color: { widget: "color" } + LGraphExposition.title = "Exposition"; + LGraphExposition.desc = "Controls texture exposition"; + + LGraphExposition.widgets_info = { + exposition: { widget: "slider", min: 0, max: 3 }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } }; - LGraphRenderGeometryDOF.prototype.updateMesh = function(geometry) - { - var buffer = this.buffer; - if(!this.buffer || this.buffer.data.length != geometry.vertices.length) - this.buffer = new GL.Buffer( GL.ARRAY_BUFFER, geometry.vertices,3,GL.DYNAMIC_DRAW); - else - { - this.buffer.data.set( geometry.vertices ); - this.buffer.upload(GL.DYNAMIC_DRAW); - } - - if(!this.mesh) - this.mesh = new GL.Mesh(); - - this.mesh.addBuffer("vertices",this.buffer); - this.geometry_id = this.mesh.id = geometry._id; - this.version = this.mesh.version = geometry._version; - } - - LGraphRenderGeometryDOF.prototype.onExecute = function() { - - if(!this.properties.enabled) - return; - - var geometry = this.getInputData(0); - if(!geometry) - return; - if(this.version != geometry._version || this.geometry_id != geometry._id ) - this.updateMesh( geometry ); - - if(!LiteGraph.LGraphRender.onRequestCameraMatrices) - { - console.warn("cannot render geometry, LiteGraph.onRequestCameraMatrices is null, remember to fill this with a callback(view_matrix, projection_matrix,viewprojection_matrix) to use 3D rendering from the graph"); + LGraphExposition.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { return; } - LiteGraph.LGraphRender.onRequestCameraMatrices( view_matrix, projection_matrix,viewprojection_matrix ); - var shader = null; + if (!this.isOutputConnected(0)) { + return; + } //saves work - var texture = this.getInputData(2); - - if(texture) - { - shader = gl.shaders["textured_points"]; - if(!shader) - shader = gl.shaders["textured_points"] = new GL.Shader( LGraphRenderGeometryDOF.vertex_shader_code, LGraphRenderGeometryDOF.fragment_shader_code, { USE_TEXTURED_POINTS:"" }); - } - else - { - shader = gl.shaders["points"]; - if(!shader) - shader = gl.shaders["points"] = new GL.Shader( LGraphRenderGeometryDOF.vertex_shader_code, LGraphRenderGeometryDOF.fragment_shader_code, { USE_POINTS: "" }); + var temp = this._temp_texture; + if ( + !temp || + temp.width != tex.width || + temp.height != tex.height || + temp.type != tex.type + ) { + temp = this._temp_texture = new GL.Texture( + tex.width, + tex.height, + { type: tex.type, format: gl.RGBA, filter: gl.LINEAR } + ); } - this.color.set( this.properties.color ); - this.color[3] = this.properties.opacity; - - var m = this.getInputData(1); - if(m) - model_matrix.set(m); - else - mat4.identity( model_matrix ); - - this.uniforms.u_point_size = this.properties.point_size; - this.uniforms.u_point_perspective = this.properties.fixed_size ? 0 : 1; - this.uniforms.u_perspective = gl.viewport_data[3] * projection_matrix[5]; - - shader.uniforms( global_uniforms ); - shader.uniforms( this.uniforms ); - - if(this.properties.opacity >= 1) - gl.disable( gl.BLEND ); - else - gl.enable( gl.BLEND ); - - gl.enable( gl.DEPTH_TEST ); - if( this.properties.additive ) - { - gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); - gl.depthMask( false ); + var shader = LGraphExposition._shader; + if (!shader) { + shader = LGraphExposition._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphExposition.pixel_shader + ); } - else - gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA ); - shader.draw( this.mesh, GL.POINTS ); + var exp = this.properties.exposition; + var exp_input = this.getInputData(1); + if (exp_input != null) { + exp = this.properties.exposition = exp_input; + } + var uniforms = this._uniforms; - gl.disable( gl.BLEND ); - gl.depthMask( true ); - } + //apply shader + temp.drawTo(function() { + gl.disable(gl.DEPTH_TEST); + tex.bind(0); + shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad()); + }); - LiteGraph.registerNodeType( "geometry/render_dof", LGraphRenderGeometryDOF ); + this.setOutputData(0, temp); + }; - LGraphRenderGeometryDOF.vertex_shader_code = '\ - precision mediump float;\n\ - attribute vec3 a_vertex;\n\ - varying vec3 v_vertex;\n\ - attribute vec3 a_normal;\n\ - varying vec3 v_normal;\n\ - #ifdef USE_COLOR\n\ - attribute vec4 a_color;\n\ - varying vec4 v_color;\n\ - #endif\n\ - attribute vec2 a_coord;\n\ - varying vec2 v_coord;\n\ - #ifdef USE_SIZE\n\ - attribute float a_extra;\n\ - #endif\n\ - #ifdef USE_INSTANCING\n\ - attribute mat4 u_model;\n\ - #else\n\ - uniform mat4 u_model;\n\ - #endif\n\ - uniform mat4 u_viewprojection;\n\ - uniform float u_point_size;\n\ - uniform float u_perspective;\n\ - uniform float u_point_perspective;\n\ - float computePointSize(float radius, float w)\n\ - {\n\ - if(radius < 0.0)\n\ - return -radius;\n\ - return u_perspective * radius / w;\n\ - }\n\ - void main() {\n\ - v_coord = a_coord;\n\ - #ifdef USE_COLOR\n\ - v_color = a_color;\n\ - #endif\n\ - v_vertex = ( u_model * vec4( a_vertex, 1.0 )).xyz;\n\ - v_normal = ( u_model * vec4( a_normal, 0.0 )).xyz;\n\ - gl_Position = u_viewprojection * vec4(v_vertex,1.0);\n\ - gl_PointSize = u_point_size;\n\ - #ifdef USE_SIZE\n\ - gl_PointSize = a_extra;\n\ - #endif\n\ - if(u_point_perspective != 0.0)\n\ - gl_PointSize = computePointSize( gl_PointSize, gl_Position.w );\n\ - }\ - '; - - LGraphRenderGeometryDOF.fragment_shader_code = '\ - precision mediump float;\n\ - uniform vec4 u_color;\n\ - #ifdef USE_COLOR\n\ - varying vec4 v_color;\n\ - #endif\n\ + LGraphExposition.pixel_shader = + "precision highp float;\n\ varying vec2 v_coord;\n\ uniform sampler2D u_texture;\n\ + uniform float u_exposition;\n\ + \n\ void main() {\n\ - vec4 color = u_color;\n\ - #ifdef USE_TEXTURED_POINTS\n\ - color *= texture2D(u_texture, gl_PointCoord.xy);\n\ + vec4 color = texture2D( u_texture, v_coord );\n\ + gl_FragColor = vec4( color.xyz * u_exposition, color.a );\n\ + }"; + + LiteGraph.registerNodeType("texture/exposition", LGraphExposition); + + function LGraphToneMapping() { + this.addInput("in", "Texture"); + this.addInput("avg", "number,Texture"); + this.addOutput("out", "Texture"); + this.properties = { + enabled: true, + scale: 1, + gamma: 1, + average_lum: 1, + lum_white: 1, + precision: LGraphTexture.LOW + }; + + this._uniforms = { + u_texture: 0, + u_lumwhite2: 1, + u_igamma: 1, + u_scale: 1, + u_average_lum: 1 + }; + } + + LGraphToneMapping.title = "Tone Mapping"; + LGraphToneMapping.desc = + "Applies Tone Mapping to convert from high to low"; + + LGraphToneMapping.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphToneMapping.prototype.onGetInputs = function() { + return [["enabled", "boolean"]]; + }; + + LGraphToneMapping.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + if ( + this.properties.precision === LGraphTexture.PASS_THROUGH || + this.getInputOrProperty("enabled") === false + ) { + this.setOutputData(0, tex); + return; + } + + var temp = this._temp_texture; + + if ( + !temp || + temp.width != tex.width || + temp.height != tex.height || + temp.type != tex.type + ) { + temp = this._temp_texture = new GL.Texture( + tex.width, + tex.height, + { type: tex.type, format: gl.RGBA, filter: gl.LINEAR } + ); + } + + var avg = this.getInputData(1); + if (avg == null) { + avg = this.properties.average_lum; + } + + var uniforms = this._uniforms; + var shader = null; + + if (avg.constructor === Number) { + this.properties.average_lum = avg; + uniforms.u_average_lum = this.properties.average_lum; + shader = LGraphToneMapping._shader; + if (!shader) { + shader = LGraphToneMapping._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphToneMapping.pixel_shader + ); + } + } else if (avg.constructor === GL.Texture) { + uniforms.u_average_texture = avg.bind(1); + shader = LGraphToneMapping._shader_texture; + if (!shader) { + shader = LGraphToneMapping._shader_texture = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphToneMapping.pixel_shader, + { AVG_TEXTURE: "" } + ); + } + } + + uniforms.u_lumwhite2 = + this.properties.lum_white * this.properties.lum_white; + uniforms.u_scale = this.properties.scale; + uniforms.u_igamma = 1 / this.properties.gamma; + + //apply shader + gl.disable(gl.DEPTH_TEST); + temp.drawTo(function() { + tex.bind(0); + shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad()); + }); + + this.setOutputData(0, this._temp_texture); + }; + + LGraphToneMapping.pixel_shader = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform float u_scale;\n\ + #ifdef AVG_TEXTURE\n\ + uniform sampler2D u_average_texture;\n\ + #else\n\ + uniform float u_average_lum;\n\ + #endif\n\ + uniform float u_lumwhite2;\n\ + uniform float u_igamma;\n\ + vec3 RGB2xyY (vec3 rgb)\n\ + {\n\ + const mat3 RGB2XYZ = mat3(0.4124, 0.3576, 0.1805,\n\ + 0.2126, 0.7152, 0.0722,\n\ + 0.0193, 0.1192, 0.9505);\n\ + vec3 XYZ = RGB2XYZ * rgb;\n\ + \n\ + float f = (XYZ.x + XYZ.y + XYZ.z);\n\ + return vec3(XYZ.x / f,\n\ + XYZ.y / f,\n\ + XYZ.y);\n\ + }\n\ + \n\ + void main() {\n\ + vec4 color = texture2D( u_texture, v_coord );\n\ + vec3 rgb = color.xyz;\n\ + float average_lum = 0.0;\n\ + #ifdef AVG_TEXTURE\n\ + vec3 pixel = texture2D(u_average_texture,vec2(0.5)).xyz;\n\ + average_lum = (pixel.x + pixel.y + pixel.z) / 3.0;\n\ #else\n\ - #ifdef USE_TEXTURE\n\ - color *= texture2D(u_texture, v_coord);\n\ - if(color.a < 0.1)\n\ - discard;\n\ - #endif\n\ - #ifdef USE_POINTS\n\ - float dist = length( gl_PointCoord.xy - vec2(0.5) );\n\ - if( dist > 0.45 )\n\ - discard;\n\ - #endif\n\ - #endif\n\ - #ifdef USE_COLOR\n\ - color *= v_color;\n\ + average_lum = u_average_lum;\n\ #endif\n\ + //Ld - this part of the code is the same for both versions\n\ + float lum = dot(rgb, vec3(0.2126, 0.7152, 0.0722));\n\ + float L = (u_scale / average_lum) * lum;\n\ + float Ld = (L * (1.0 + L / u_lumwhite2)) / (1.0 + L);\n\ + //first\n\ + //vec3 xyY = RGB2xyY(rgb);\n\ + //xyY.z *= Ld;\n\ + //rgb = xyYtoRGB(xyY);\n\ + //second\n\ + rgb = (rgb / lum) * Ld;\n\ + rgb = max(rgb,vec3(0.001));\n\ + rgb = pow( rgb, vec3( u_igamma ) );\n\ + gl_FragColor = vec4( rgb, color.a );\n\ + }"; + + LiteGraph.registerNodeType("texture/tonemapping", LGraphToneMapping); + + function LGraphTexturePerlin() { + this.addOutput("out", "Texture"); + this.properties = { + width: 512, + height: 512, + seed: 0, + persistence: 0.1, + octaves: 8, + scale: 1, + offset: [0, 0], + amplitude: 1, + precision: LGraphTexture.DEFAULT + }; + this._key = 0; + this._texture = null; + this._uniforms = { + u_persistence: 0.1, + u_seed: 0, + u_offset: vec2.create(), + u_scale: 1, + u_viewport: vec2.create() + }; + } + + LGraphTexturePerlin.title = "Perlin"; + LGraphTexturePerlin.desc = "Generates a perlin noise texture"; + + LGraphTexturePerlin.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }, + width: { type: "Number", precision: 0, step: 1 }, + height: { type: "Number", precision: 0, step: 1 }, + octaves: { type: "Number", precision: 0, step: 1, min: 1, max: 50 } + }; + + LGraphTexturePerlin.prototype.onGetInputs = function() { + return [ + ["seed", "Number"], + ["persistence", "Number"], + ["octaves", "Number"], + ["scale", "Number"], + ["amplitude", "Number"], + ["offset", "vec2"] + ]; + }; + + LGraphTexturePerlin.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { + return; + } //saves work + + var w = this.properties.width | 0; + var h = this.properties.height | 0; + if (w == 0) { + w = gl.viewport_data[2]; + } //0 means default + if (h == 0) { + h = gl.viewport_data[3]; + } //0 means default + var type = LGraphTexture.getTextureType(this.properties.precision); + + var temp = this._texture; + if ( + !temp || + temp.width != w || + temp.height != h || + temp.type != type + ) { + temp = this._texture = new GL.Texture(w, h, { + type: type, + format: gl.RGB, + filter: gl.LINEAR + }); + } + + var persistence = this.getInputOrProperty("persistence"); + var octaves = this.getInputOrProperty("octaves"); + var offset = this.getInputOrProperty("offset"); + var scale = this.getInputOrProperty("scale"); + var amplitude = this.getInputOrProperty("amplitude"); + var seed = this.getInputOrProperty("seed"); + + //reusing old texture + var key = + "" + + w + + h + + type + + persistence + + octaves + + scale + + seed + + offset[0] + + offset[1] + + amplitude; + if (key == this._key) { + this.setOutputData(0, temp); + return; + } + this._key = key; + + //gather uniforms + var uniforms = this._uniforms; + uniforms.u_persistence = persistence; + uniforms.u_octaves = octaves; + uniforms.u_offset.set(offset); + uniforms.u_scale = scale; + uniforms.u_amplitude = amplitude; + uniforms.u_seed = seed * 128; + uniforms.u_viewport[0] = w; + uniforms.u_viewport[1] = h; + + //render + var shader = LGraphTexturePerlin._shader; + if (!shader) { + shader = LGraphTexturePerlin._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTexturePerlin.pixel_shader + ); + } + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + + temp.drawTo(function() { + shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad()); + }); + + this.setOutputData(0, temp); + }; + + LGraphTexturePerlin.pixel_shader = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform vec2 u_offset;\n\ + uniform float u_scale;\n\ + uniform float u_persistence;\n\ + uniform int u_octaves;\n\ + uniform float u_amplitude;\n\ + uniform vec2 u_viewport;\n\ + uniform float u_seed;\n\ + #define M_PI 3.14159265358979323846\n\ + \n\ + float rand(vec2 c){ return fract(sin(dot(c.xy ,vec2( 12.9898 + u_seed,78.233 + u_seed))) * 43758.5453); }\n\ + \n\ + float noise(vec2 p, float freq ){\n\ + float unit = u_viewport.x/freq;\n\ + vec2 ij = floor(p/unit);\n\ + vec2 xy = mod(p,unit)/unit;\n\ + //xy = 3.*xy*xy-2.*xy*xy*xy;\n\ + xy = .5*(1.-cos(M_PI*xy));\n\ + float a = rand((ij+vec2(0.,0.)));\n\ + float b = rand((ij+vec2(1.,0.)));\n\ + float c = rand((ij+vec2(0.,1.)));\n\ + float d = rand((ij+vec2(1.,1.)));\n\ + float x1 = mix(a, b, xy.x);\n\ + float x2 = mix(c, d, xy.x);\n\ + return mix(x1, x2, xy.y);\n\ + }\n\ + \n\ + float pNoise(vec2 p, int res){\n\ + float persistance = u_persistence;\n\ + float n = 0.;\n\ + float normK = 0.;\n\ + float f = 4.;\n\ + float amp = 1.0;\n\ + int iCount = 0;\n\ + for (int i = 0; i<50; i++){\n\ + n+=amp*noise(p, f);\n\ + f*=2.;\n\ + normK+=amp;\n\ + amp*=persistance;\n\ + if (iCount >= res)\n\ + break;\n\ + iCount++;\n\ + }\n\ + float nf = n/normK;\n\ + return nf*nf*nf*nf;\n\ + }\n\ + void main() {\n\ + vec2 uv = v_coord * u_scale * u_viewport + u_offset * u_scale;\n\ + vec4 color = vec4( pNoise( uv, u_octaves ) * u_amplitude );\n\ gl_FragColor = color;\n\ - }\ - '; - */ + }"; + LiteGraph.registerNodeType("texture/perlin", LGraphTexturePerlin); + function LGraphTextureCanvas2D() { + this.addInput("v"); + this.addOutput("out", "Texture"); + this.properties = { + code: LGraphTextureCanvas2D.default_code, + width: 512, + height: 512, + clear: true, + precision: LGraphTexture.DEFAULT, + use_html_canvas: false + }; + this._func = null; + this._temp_texture = null; + this.compileCode(); + } + + LGraphTextureCanvas2D.title = "Canvas2D"; + LGraphTextureCanvas2D.desc = "Executes Canvas2D code inside a texture or the viewport."; + LGraphTextureCanvas2D.help = "Set width and height to 0 to match viewport size."; + + LGraphTextureCanvas2D.default_code = "//vars: canvas,ctx,time\nctx.fillStyle='red';\nctx.fillRect(0,0,50,50);\n"; + + LGraphTextureCanvas2D.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }, + code: { type: "code" }, + width: { type: "Number", precision: 0, step: 1 }, + height: { type: "Number", precision: 0, step: 1 } + }; + + LGraphTextureCanvas2D.prototype.onPropertyChanged = function( name, value ) { + if (name == "code" ) + this.compileCode( value ); + } + + LGraphTextureCanvas2D.prototype.compileCode = function( code ) { + this._func = null; + if( !LiteGraph.allow_scripts ) + return; + + try { + this._func = new Function( "canvas", "ctx", "time", "script","v", code ); + this.boxcolor = "#00FF00"; + } catch (err) { + this.boxcolor = "#FF0000"; + console.error("Error parsing script"); + console.error(err); + } + }; + + LGraphTextureCanvas2D.prototype.onExecute = function() { + var func = this._func; + if (!func || !this.isOutputConnected(0)) { + return; + } + this.executeDraw( func ); + } + + LGraphTextureCanvas2D.prototype.executeDraw = function( func_context ) { + + var width = this.properties.width || gl.canvas.width; + var height = this.properties.height || gl.canvas.height; + var temp = this._temp_texture; + var type = LGraphTexture.getTextureType( this.properties.precision ); + if (!temp || temp.width != width || temp.height != height || temp.type != type ) { + temp = this._temp_texture = new GL.Texture(width, height, { + format: gl.RGBA, + filter: gl.LINEAR, + type: type + }); + } + + var v = this.getInputData(0); + + var properties = this.properties; + var that = this; + var time = this.graph.getTime(); + var ctx = gl; + var canvas = gl.canvas; + if( this.properties.use_html_canvas || !global.enableWebGLCanvas ) + { + if(!this._canvas) + { + canvas = this._canvas = createCanvas(width.height); + ctx = this._ctx = canvas.getContext("2d"); + } + else + { + canvas = this._canvas; + ctx = this._ctx; + } + canvas.width = width; + canvas.height = height; + } + + if(ctx == gl) //using Canvas2DtoWebGL + temp.drawTo(function() { + gl.start2D(); + if(properties.clear) + { + gl.clearColor(0,0,0,0); + gl.clear( gl.COLOR_BUFFER_BIT ); + } + + try { + if (func_context.draw) { + func_context.draw.call(that, canvas, ctx, time, func_context, v); + } else { + func_context.call(that, canvas, ctx, time, func_context,v); + } + that.boxcolor = "#00FF00"; + } catch (err) { + that.boxcolor = "#FF0000"; + console.error("Error executing script"); + console.error(err); + } + gl.finish2D(); + }); + else //rendering to offscren canvas and uploading to texture + { + if(properties.clear) + ctx.clearRect(0,0,canvas.width,canvas.height); + + try { + if (func_context.draw) { + func_context.draw.call(this, canvas, ctx, time, func_context, v); + } else { + func_context.call(this, canvas, ctx, time, func_context,v); + } + this.boxcolor = "#00FF00"; + } catch (err) { + this.boxcolor = "#FF0000"; + console.error("Error executing script"); + console.error(err); + } + temp.uploadImage( canvas ); + } + + this.setOutputData(0, temp); + }; + + LiteGraph.registerNodeType("texture/canvas2D", LGraphTextureCanvas2D); + + // To do chroma keying ***************** + + function LGraphTextureMatte() { + this.addInput("in", "Texture"); + + this.addOutput("out", "Texture"); + this.properties = { + key_color: vec3.fromValues(0, 1, 0), + threshold: 0.8, + slope: 0.2, + precision: LGraphTexture.DEFAULT + }; + } + + LGraphTextureMatte.title = "Matte"; + LGraphTextureMatte.desc = "Extracts background"; + + LGraphTextureMatte.widgets_info = { + key_color: { widget: "color" }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureMatte.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { + return; + } //saves work + + var tex = this.getInputData(0); + + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, tex); + return; + } + + if (!tex) { + return; + } + + this._tex = LGraphTexture.getTargetTexture( + tex, + this._tex, + this.properties.precision + ); + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + + if (!this._uniforms) { + this._uniforms = { + u_texture: 0, + u_key_color: this.properties.key_color, + u_threshold: 1, + u_slope: 1 + }; + } + var uniforms = this._uniforms; + + var mesh = Mesh.getScreenQuad(); + var shader = LGraphTextureMatte._shader; + if (!shader) { + shader = LGraphTextureMatte._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureMatte.pixel_shader + ); + } + + uniforms.u_key_color = this.properties.key_color; + uniforms.u_threshold = this.properties.threshold; + uniforms.u_slope = this.properties.slope; + + this._tex.drawTo(function() { + tex.bind(0); + shader.uniforms(uniforms).draw(mesh); + }); + + this.setOutputData(0, this._tex); + }; + + LGraphTextureMatte.pixel_shader = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec3 u_key_color;\n\ + uniform float u_threshold;\n\ + uniform float u_slope;\n\ + \n\ + void main() {\n\ + vec3 color = texture2D( u_texture, v_coord ).xyz;\n\ + float diff = length( normalize(color) - normalize(u_key_color) );\n\ + float edge = u_threshold * (1.0 - u_slope);\n\ + float alpha = smoothstep( edge, u_threshold, diff);\n\ + gl_FragColor = vec4( color, alpha );\n\ + }"; + + LiteGraph.registerNodeType("texture/matte", LGraphTextureMatte); + + //*********************************** + function LGraphCubemapToTexture2D() { + this.addInput("in", "texture"); + this.addInput("yaw", "number"); + this.addOutput("out", "texture"); + this.properties = { yaw: 0 }; + } + + LGraphCubemapToTexture2D.title = "CubemapToTexture2D"; + LGraphCubemapToTexture2D.desc = "Transforms a CUBEMAP texture into a TEXTURE2D in Polar Representation"; + + LGraphCubemapToTexture2D.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) + return; + + var tex = this.getInputData(0); + if ( !tex || tex.texture_type != GL.TEXTURE_CUBE_MAP ) + return; + if( this._last_tex && ( this._last_tex.height != tex.height || this._last_tex.type != tex.type )) + this._last_tex = null; + var yaw = this.getInputOrProperty("yaw"); + this._last_tex = GL.Texture.cubemapToTexture2D( tex, tex.height, this._last_tex, true, yaw ); + this.setOutputData( 0, this._last_tex ); + }; + + LiteGraph.registerNodeType( "texture/cubemapToTexture2D", LGraphCubemapToTexture2D ); +})(this); -})(this); (function(global) { var LiteGraph = global.LiteGraph; var LGraphTexture = global.LGraphTexture; @@ -24893,7 +21372,7 @@ function LGraphGeometryDisplace() { global.LGraphFXVigneting = LGraphFXVigneting; } })(this); - + (function(global) { var LiteGraph = global.LiteGraph; var MIDI_COLOR = "#243"; @@ -26480,7 +22959,7 @@ function LGraphGeometryDisplace() { return window.performance.now(); } })(this); - + (function(global) { var LiteGraph = global.LiteGraph; @@ -27937,7 +24416,7 @@ LiteGraph.registerNodeType("audio/waveShaper", LGAudioWaveShaper); LGAudioDestination.desc = "Audio output"; LiteGraph.registerNodeType("audio/destination", LGAudioDestination); })(this); - + //event related nodes (function(global) { var LiteGraph = global.LiteGraph; @@ -28303,4 +24782,3 @@ LiteGraph.registerNodeType("audio/waveShaper", LGAudioWaveShaper); LiteGraph.registerNodeType("network/sillyclient", LGSillyClient); })(this); - diff --git a/build/litegraph.min.js b/build/litegraph.min.js index 0e8bc779b..9ff56320c 100755 --- a/build/litegraph.min.js +++ b/build/litegraph.min.js @@ -1,730 +1,10484 @@ -(function(z){function c(a){h.debug&&console.log("Graph created");this.list_of_graphcanvas=null;this.clear();a&&this.configure(a)}function m(a,b,d,g,f,r){this.id=a;this.type=b;this.origin_id=d;this.origin_slot=g;this.target_id=f;this.target_slot=r;this._data=null;this._pos=new Float32Array(2)}function n(a){this._ctor(a)}function e(a){this._ctor(a)}function w(a,b){this.offset=new Float32Array([0,0]);this.scale=1;this.max_scale=10;this.min_scale=0.1;this.onredraw=null;this.enabled=!0;this.last_mouse= -[0,0];this.element=null;this.visible_area=new Float32Array(4);a&&(this.element=a,b||this.bindEvents(a))}function k(a,b,d){d=d||{};this.background_image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; -a&&a.constructor===String&&(a=document.querySelector(a));this.ds=new w;this.zoom_modify_alpha=!0;this.title_text_font=""+h.NODE_TEXT_SIZE+"px Arial";this.inner_text_font="normal "+h.NODE_SUBTEXT_SIZE+"px Arial";this.node_title_color=h.NODE_TITLE_COLOR;this.default_link_color=h.LINK_COLOR;this.default_connection_color={input_off:"#778",input_on:"#7F7",output_off:"#778",output_on:"#7F7"};this.highquality_render=!0;this.use_gradients=!1;this.editor_alpha=1;this.pause_rendering=!1;this.clear_background= -!0;this.read_only=!1;this.render_only_selected=!0;this.live_mode=!1;this.allow_searchbox=this.allow_interaction=this.allow_dragnodes=this.allow_dragcanvas=this.show_info=!0;this.drag_mode=this.allow_reconnect_links=!1;this.filter=this.dragging_rectangle=null;this.always_render_background=!1;this.render_canvas_border=this.render_shadows=!0;this.render_connections_shadows=!1;this.render_connections_border=!0;this.render_connection_arrows=this.render_curved_connections=!1;this.render_collapsed_slots= -!0;this.render_execution_order=!1;this.render_link_tooltip=this.render_title_colored=!0;this.links_render_mode=h.SPLINE_LINK;this.canvas_mouse=[0,0];this.onSelectionChange=this.onNodeMoved=this.onDrawLinkTooltip=this.onDrawOverlay=this.onDrawForeground=this.onDrawBackground=this.onMouse=this.onSearchBoxSelection=this.onSearchBox=null;this.connections_width=3;this.round_radius=8;this.over_link_center=this.node_widget=this.current_node=null;this.last_mouse_position=[0,0];this.visible_area=this.ds.visible_area; -this.visible_links=[];b&&b.attachCanvas(this);this.setCanvas(a);this.clear();d.skip_render||this.startRendering();this.autoresize=d.autoresize}function C(a,b){return Math.sqrt((b[0]-a[0])*(b[0]-a[0])+(b[1]-a[1])*(b[1]-a[1]))}function v(a,b,d,g,f,r){return da&&gb?!0:!1}function s(a,b){var d=a[0]+a[2],g=a[1]+a[3],f=b[1]+b[3];return a[0]>b[0]+b[2]||a[1]>f||dc.width-l.width-10&&(f=c.width-l.width-10);c.height&&h>c.height-l.height-10&&(h=c.height-l.height-10)}r.style.left=f+"px";r.style.top=h+"px";b.scale&&(r.style.transform="scale("+b.scale+")")}function A(a){this.points=a;this.nearest=this.selected=-1;this.size=null;this.must_update=!0;this.margin=5}var h=z.LiteGraph={VERSION:0.4,CANVAS_GRID_SIZE:10,NODE_TITLE_HEIGHT:30, -NODE_TITLE_TEXT_Y:20,NODE_SLOT_HEIGHT:20,NODE_WIDGET_HEIGHT:20,NODE_WIDTH:140,NODE_MIN_WIDTH:50,NODE_COLLAPSED_RADIUS:10,NODE_COLLAPSED_WIDTH:80,NODE_TITLE_COLOR:"#999",NODE_TEXT_SIZE:14,NODE_TEXT_COLOR:"#AAA",NODE_SUBTEXT_SIZE:12,NODE_DEFAULT_COLOR:"#333",NODE_DEFAULT_BGCOLOR:"#353535",NODE_DEFAULT_BOXCOLOR:"#666",NODE_DEFAULT_SHAPE:"box",DEFAULT_SHADOW_COLOR:"rgba(0,0,0,0.5)",DEFAULT_GROUP_FONT:24,WIDGET_BGCOLOR:"#222",WIDGET_OUTLINE_COLOR:"#666",WIDGET_TEXT_COLOR:"#DDD",WIDGET_SECONDARY_TEXT_COLOR:"#999", -LINK_COLOR:"#9A9",EVENT_LINK_COLOR:"#A86",CONNECTING_LINK_COLOR:"#AFA",MAX_NUMBER_OF_NODES:1E3,DEFAULT_POSITION:[100,100],VALID_SHAPES:["default","box","round","card"],BOX_SHAPE:1,ROUND_SHAPE:2,CIRCLE_SHAPE:3,CARD_SHAPE:4,ARROW_SHAPE:5,INPUT:1,OUTPUT:2,EVENT:-1,ACTION:-1,ALWAYS:0,ON_EVENT:1,NEVER:2,ON_TRIGGER:3,UP:1,DOWN:2,LEFT:3,RIGHT:4,CENTER:5,STRAIGHT_LINK:0,LINEAR_LINK:1,SPLINE_LINK:2,NORMAL_TITLE:0,NO_TITLE:1,TRANSPARENT_TITLE:2,AUTOHIDE_TITLE:3,proxy:null,node_images_path:"",debug:!1,catch_exceptions:!0, -throw_errors:!0,allow_scripts:!1,registered_node_types:{},node_types_by_file_extension:{},Nodes:{},searchbox_extras:{},registerNodeType:function(a,b){if(!b.prototype)throw"Cannot register a simple object, it must be a class with a prototype";b.type=a;h.debug&&console.log("Node registered: "+a);a.split("/");var d=b.name,g=a.lastIndexOf("/");b.category=a.substr(0,g);b.title||(b.title=d);if(b.prototype)for(var f in n.prototype)b.prototype[f]||(b.prototype[f]=n.prototype[f]);if(g=this.registered_node_types[a])console.log("replacing node type: "+ -a);else if(Object.hasOwnProperty(b.prototype,"shape")||Object.defineProperty(b.prototype,"shape",{set:function(a){switch(a){case "default":delete this._shape;break;case "box":this._shape=h.BOX_SHAPE;break;case "round":this._shape=h.ROUND_SHAPE;break;case "circle":this._shape=h.CIRCLE_SHAPE;break;case "card":this._shape=h.CARD_SHAPE;break;default:this._shape=a}},get:function(a){return this._shape},enumerable:!0,configurable:!0}),b.prototype.onPropertyChange&&console.warn("LiteGraph node class "+a+ -" has onPropertyChange method, it must be called onPropertyChanged with d at the end"),b.supported_extensions)for(f in b.supported_extensions){var r=b.supported_extensions[f];r&&r.constructor===String&&(this.node_types_by_file_extension[r.toLowerCase()]=b)}this.registered_node_types[a]=b;b.constructor.name&&(this.Nodes[d]=b);if(h.onNodeTypeRegistered)h.onNodeTypeRegistered(a,b);if(g&&h.onNodeTypeReplaced)h.onNodeTypeReplaced(a,b,g)},unregisterNodeType:function(a){var b=a.constructor===String?this.registered_node_types[a]: -a;if(!b)throw"node type not found: "+a;delete this.registered_node_types[b.type];b.constructor.name&&delete this.Nodes[b.constructor.name]},wrapFunctionAsNode:function(a,b,d,g,f){for(var r=Array(b.length),x="",c=h.getParameterNames(b),l=0;lx&&(x=f.size[0]),c+=f.size[1]+a+h.NODE_TITLE_HEIGHT;b+=x+a}this.setDirtyCanvas(!0,!0)};c.prototype.getTime=function(){return this.globaltime};c.prototype.getFixedTime=function(){return this.fixedtime};c.prototype.getElapsedTime= -function(){return this.elapsed_time};c.prototype.sendEventToAllNodes=function(a,b,d){d=d||h.ALWAYS;var g=this._nodes_in_order?this._nodes_in_order:this._nodes;if(g)for(var f=0,r=g.length;f=h.MAX_NUMBER_OF_NODES)throw"LiteGraph: max number of nodes in a graph reached";null==a.id||-1==a.id?a.id=++this.last_node_id:this.last_node_id< -a.id&&(this.last_node_id=a.id);a.graph=this;this._version++;this._nodes.push(a);this._nodes_by_id[a.id]=a;if(a.onAdded)a.onAdded(this);this.config.align_to_grid&&a.alignToGrid();b||this.updateExecutionOrder();if(this.onNodeAdded)this.onNodeAdded(a);this.setDirtyCanvas(!0);this.change();return a}};c.prototype.remove=function(a){if(a.constructor===h.LGraphGroup){var b=this._groups.indexOf(a);-1!=b&&this._groups.splice(b,1);a.graph=null;this._version++;this.setDirtyCanvas(!0,!0);this.change()}else if(null!= -this._nodes_by_id[a.id]&&!a.ignore_remove){if(a.inputs)for(b=0;ba.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});this.id=-1;this.type=null;this.inputs=[];this.outputs=[];this.connections=[];this.properties={};this.properties_info=[];this.flags={}};n.prototype.configure=function(a){this.graph&&this.graph._version++;for(var b in a)if("properties"==b)for(var d in a.properties){if(this.properties[d]=a.properties[d],this.onPropertyChanged)this.onPropertyChanged(d, -a.properties[d])}else null!=a[b]&&("object"==typeof a[b]?this[b]&&this[b].configure?this[b].configure(a[b]):this[b]=h.cloneObject(a[b],this[b]):this[b]=a[b]);a.title||(this.title=this.constructor.title);if(this.onConnectionsChange){if(this.inputs)for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d._data=b,this.outputs[a].links))for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d.type=b,this.outputs[a].links))for(d=0;d=this.inputs.length||null==this.inputs[a].link)){var d=this.graph.links[this.inputs[a].link];if(!d)return null;if(!b)return d.data;var g=this.graph.getNodeById(d.origin_id);if(!g)return d.data; -if(g.updateOutputData)g.updateOutputData(d.origin_slot);else if(g.onExecute)g.onExecute();return d.data}};n.prototype.getInputDataType=function(a){if(!this.inputs||a>=this.inputs.length||null==this.inputs[a].link)return null;a=this.graph.links[this.inputs[a].link];if(!a)return null;var b=this.graph.getNodeById(a.origin_id);return b?(a=b.outputs[a.origin_slot])?a.type:null:a.type};n.prototype.getInputDataByName=function(a,b){var d=this.findInputSlot(a);return-1==d?null:this.getInputData(d,b)};n.prototype.isInputConnected= -function(a){return this.inputs?a=this.inputs.length)return null;a=this.inputs[a];return a&&null!==a.link?(a=this.graph.links[a.link])?this.graph.getNodeById(a.origin_id):null:null};n.prototype.getInputOrProperty=function(a){if(!this.inputs||!this.inputs.length)return this.properties?this.properties[a]: -null;for(var b=0,d=this.inputs.length;b=this.outputs.length?null:this.outputs[a]._data};n.prototype.getOutputInfo=function(a){return this.outputs?a=this.outputs.length)return null;a=this.outputs[a];if(!a.links||0==a.links.length)return null;for(var b=[],d=0;da&&this.pos[1]-f-db)return!0;return!1};n.prototype.getSlotInPosition=function(a,b){var d=new Float32Array(2);if(this.inputs)for(var g=0,f=this.inputs.length;g=this.outputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),null;b&&b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"target node is null";if(b==this)return null;if(d.constructor===String){if(d=b.findInputSlot(d),-1==d)return h.debug&&console.log("Connect: Error, no slot of name "+d),null}else{if(d===h.EVENT)return null; -if(!b.inputs||d>=b.inputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),null}null!=b.inputs[d].link&&b.disconnectInput(d);var g=this.outputs[a];if(b.onConnectInput&&!1===b.onConnectInput(d,g.type,g,this,a))return null;var f=b.inputs[d],r=null;if(h.isValidConnection(g.type,f.type)){r=new m(++this.graph.last_link_id,f.type,this.id,a,b.id,d);this.graph.links[r.id]=r;null==g.links&&(g.links=[]);g.links.push(r.id);b.inputs[d].link=r.id;this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(h.OUTPUT, -a,!0,r,g);if(b.onConnectionsChange)b.onConnectionsChange(h.INPUT,d,!0,r,f);this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(h.INPUT,b,d,this,a),this.graph.onNodeConnectionChange(h.OUTPUT,this,a,b,d))}this.setDirtyCanvas(!1,!0);this.graph.connectionChange(this,r);return r};n.prototype.disconnectOutput=function(a,b){if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return h.debug&&console.log("Connect: Error, no slot of name "+a),!1}else if(!this.outputs|| -a>=this.outputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),!1;var d=this.outputs[a];if(!d||!d.links||0==d.links.length)return!1;if(b){b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"Target Node not found";for(var g=0,f=d.links.length;g=this.inputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),!1;var b=this.inputs[a];if(!b)return!1;var d=this.inputs[a].link;this.inputs[a].link=null;var g=this.graph.links[d];if(g){var f=this.graph.getNodeById(g.origin_id);if(!f)return!1; -var r=f.outputs[g.origin_slot];if(!r||!r.links||0==r.links.length)return!1;for(var x=0,c=r.links.length;xb&&this.inputs[b].pos)return d[0]=this.pos[0]+this.inputs[b].pos[0],d[1]=this.pos[1]+this.inputs[b].pos[1],d;if(!a&&g>b&&this.outputs[b].pos)return d[0]=this.pos[0]+this.outputs[b].pos[0],d[1]=this.pos[1]+this.outputs[b].pos[1],d;if(this.horizontal)return d[0]=this.pos[0]+this.size[0]/g*(b+0.5),d[1]=a?this.pos[1]-h.NODE_TITLE_HEIGHT:this.pos[1]+this.size[1],d;d[0]=a?this.pos[0]+f:this.pos[0]+this.size[0]+1-f;d[1]= -this.pos[1]+(b+0.7)*h.NODE_SLOT_HEIGHT+(this.constructor.slot_start_y||0);return d};n.prototype.alignToGrid=function(){this.pos[0]=h.CANVAS_GRID_SIZE*Math.round(this.pos[0]/h.CANVAS_GRID_SIZE);this.pos[1]=h.CANVAS_GRID_SIZE*Math.round(this.pos[1]/h.CANVAS_GRID_SIZE)};n.prototype.trace=function(a){this.console||(this.console=[]);this.console.push(a);this.console.length>n.MAX_CONSOLE&&this.console.shift();this.graph.onNodeTrace(this,a)};n.prototype.setDirtyCanvas=function(a,b){this.graph&&this.graph.sendActionToCanvas("setDirty", -[a,b])};n.prototype.loadImage=function(a){var b=new Image;b.src=h.node_images_path+a;b.ready=!1;var d=this;b.onload=function(){this.ready=!0;d.setDirtyCanvas(!0)};return b};n.prototype.captureInput=function(a){if(this.graph&&this.graph.list_of_graphcanvas)for(var b=this.graph.list_of_graphcanvas,d=0;da.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});Object.defineProperty(this,"size",{set:function(a){!a||2>a.length||(this._size[0]=Math.max(140,a[0]),this._size[1]=Math.max(80,a[1]))},get:function(){return this._size},enumerable:!0})};e.prototype.configure=function(a){this.title=a.title; -this._bounding.set(a.bounding);this.color=a.color;this.font=a.font};e.prototype.serialize=function(){var a=this._bounding;return{title:this.title,bounding:[Math.round(a[0]),Math.round(a[1]),Math.round(a[2]),Math.round(a[3])],color:this.color,font:this.font}};e.prototype.move=function(a,b,d){this._pos[0]+=a;this._pos[1]+=b;if(!d)for(d=0;dthis.max_scale&&(a=this.max_scale);if(a!=this.scale&&this.element){var d=this.element.getBoundingClientRect();if(d){b=b||[0.5*d.width,0.5*d.height];d=this.convertCanvasToOffset(b);this.scale=a;0.01>Math.abs(this.scale-1)&&(this.scale=1);var g=this.convertCanvasToOffset(b),d=[g[0]-d[0], -g[1]-d[1]];this.offset[0]+=d[0];this.offset[1]+=d[1];if(this.onredraw)this.onredraw(this)}}};w.prototype.changeDeltaScale=function(a,b){this.changeScale(this.scale*a,b)};w.prototype.reset=function(){this.scale=1;this.offset[0]=0;this.offset[1]=0};z.LGraphCanvas=h.LGraphCanvas=k;k.link_type_colors={"-1":h.EVENT_LINK_COLOR,number:"#AAA",node:"#DCA"};k.gradients={};k.prototype.clear=function(){this.fps=this.render_time=this.last_draw_time=this.frame=0;this.dragging_rectangle=null;this.selected_nodes= -{};this.selected_group=null;this.visible_nodes=[];this.connecting_node=this.node_capturing_input=this.node_over=this.node_dragged=null;this.highlighted_links={};this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_widget=this.node_in_panel=this.dirty_area=null;this.last_mouse=[0,0];this.last_mouseclick=0;this.visible_area.set([0,0,0,0]);if(this.onClear)this.onClear()};k.prototype.setGraph=function(a,b){this.graph!=a&&(b||this.clear(),!a&&this.graph?this.graph.detachCanvas(this):(a.attachCanvas(this), -this._graph_stack&&(this._graph_stack=null),this.setDirty(!0,!0)))};k.prototype.openSubgraph=function(a){if(!a)throw"graph cannot be null";if(this.graph==a)throw"graph cannot be the same";this.clear();this.graph&&(this._graph_stack||(this._graph_stack=[]),this._graph_stack.push(this.graph));a.attachCanvas(this);this.setDirty(!0,!0)};k.prototype.closeSubgraph=function(){if(this._graph_stack&&0!=this._graph_stack.length){var a=this.graph._subgraph_node,b=this._graph_stack.pop();this.selected_nodes= -{};this.highlighted_links={};b.attachCanvas(this);this.setDirty(!0,!0);a&&(this.centerOnNode(a),this.selectNodes([a]))}};k.prototype.getCurrentGraph=function(){return this.graph};k.prototype.setCanvas=function(a,b){if(a&&a.constructor===String&&(a=document.getElementById(a),!a))throw"Error creating LiteGraph canvas: Canvas not found";if(a!==this.canvas&&(!a&&this.canvas&&(b||this.unbindEvents()),this.canvas=a,this.ds.element=a)){a.className+=" lgraphcanvas";a.data=this;a.tabindex="1";this.bgcanvas= -null;this.bgcanvas||(this.bgcanvas=document.createElement("canvas"),this.bgcanvas.width=this.canvas.width,this.bgcanvas.height=this.canvas.height);if(null==a.getContext){if("canvas"!=a.localName)throw"Element supplied for LGraphCanvas must be a element, you passed a "+a.localName;throw"This browser doesn't support Canvas";}null==(this.ctx=a.getContext("2d"))&&(a.webgl_enabled||console.warn("This canvas seems to be WebGL, enabling WebGL renderer"),this.enableWebGL());this._mousemove_callback= -this.processMouseMove.bind(this);this._mouseup_callback=this.processMouseUp.bind(this);b||this.bindEvents()}};k.prototype._doNothing=function(a){a.preventDefault();return!1};k.prototype._doReturnTrue=function(a){a.preventDefault();return!0};k.prototype.bindEvents=function(){if(this._events_binded)console.warn("LGraphCanvas: events already binded");else{var a=this.canvas,b=this.getCanvasWindow().document;this._mousedown_callback=this.processMouseDown.bind(this);this._mousewheel_callback=this.processMouseWheel.bind(this); -a.addEventListener("mousedown",this._mousedown_callback,!0);a.addEventListener("mousemove",this._mousemove_callback);a.addEventListener("mousewheel",this._mousewheel_callback,!1);a.addEventListener("contextmenu",this._doNothing);a.addEventListener("DOMMouseScroll",this._mousewheel_callback,!1);a.addEventListener("touchstart",this.touchHandler,!0);a.addEventListener("touchmove",this.touchHandler,!0);a.addEventListener("touchend",this.touchHandler,!0);a.addEventListener("touchcancel",this.touchHandler, -!0);this._key_callback=this.processKey.bind(this);a.addEventListener("keydown",this._key_callback,!0);b.addEventListener("keyup",this._key_callback,!0);this._ondrop_callback=this.processDrop.bind(this);a.addEventListener("dragover",this._doNothing,!1);a.addEventListener("dragend",this._doNothing,!1);a.addEventListener("drop",this._ondrop_callback,!1);a.addEventListener("dragenter",this._doReturnTrue,!1);this._events_binded=!0}};k.prototype.unbindEvents=function(){if(this._events_binded){var a=this.getCanvasWindow().document; -this.canvas.removeEventListener("mousedown",this._mousedown_callback);this.canvas.removeEventListener("mousewheel",this._mousewheel_callback);this.canvas.removeEventListener("DOMMouseScroll",this._mousewheel_callback);this.canvas.removeEventListener("keydown",this._key_callback);a.removeEventListener("keyup",this._key_callback);this.canvas.removeEventListener("contextmenu",this._doNothing);this.canvas.removeEventListener("drop",this._ondrop_callback);this.canvas.removeEventListener("dragenter",this._doReturnTrue); -this.canvas.removeEventListener("touchstart",this.touchHandler);this.canvas.removeEventListener("touchmove",this.touchHandler);this.canvas.removeEventListener("touchend",this.touchHandler);this.canvas.removeEventListener("touchcancel",this.touchHandler);this._ondrop_callback=this._key_callback=this._mousewheel_callback=this._mousedown_callback=null;this._events_binded=!1}else console.warn("LGraphCanvas: no events binded")};k.getFileExtension=function(a){var b=a.indexOf("?");-1!=b&&(a=a.substr(0,b)); -b=a.lastIndexOf(".");return-1==b?"":a.substr(b+1).toLowerCase()};k.prototype.enableWebGL=function(){if(void 0===typeof GL)throw"litegl.js must be included to use a WebGL canvas";if(void 0===typeof enableWebGLCanvas)throw"webglCanvas.js must be included to use this feature";this.gl=this.ctx=enableWebGLCanvas(this.canvas);this.ctx.webgl=!0;this.bgcanvas=this.canvas;this.bgctx=this.gl;this.canvas.webgl_enabled=!0};k.prototype.setDirty=function(a,b){a&&(this.dirty_canvas=!0);b&&(this.dirty_bgcanvas=!0)}; -k.prototype.getCanvasWindow=function(){if(!this.canvas)return window;var a=this.canvas.ownerDocument;return a.defaultView||a.parentWindow};k.prototype.startRendering=function(){function a(){this.pause_rendering||this.draw();var b=this.getCanvasWindow();this.is_rendering&&b.requestAnimationFrame(a.bind(this))}this.is_rendering||(this.is_rendering=!0,a.call(this))};k.prototype.stopRendering=function(){this.is_rendering=!1};k.prototype.processMouseDown=function(a){if(this.graph){this.adjustMouseEvent(a); -var b=this.getCanvasWindow();k.active_canvas=this;this.canvas.removeEventListener("mousemove",this._mousemove_callback);b.document.addEventListener("mousemove",this._mousemove_callback,!0);b.document.addEventListener("mouseup",this._mouseup_callback,!0);var d=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes,5),g=!1,f=300>h.getTime()-this.last_mouseclick;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;this.canvas.focus();h.closeAllContextMenus(b);if(!this.onMouse||!0!=this.onMouse(a)){if(1== -a.which){a.ctrlKey&&(this.dragging_rectangle=new Float32Array(4),this.dragging_rectangle[0]=a.canvasX,this.dragging_rectangle[1]=a.canvasY,this.dragging_rectangle[2]=1,this.dragging_rectangle[3]=1,g=!0);var r=!1;if(d&&this.allow_interaction&&!g&&!this.read_only){this.live_mode||d.flags.pinned||this.bringToFront(d);if(!this.connecting_node&&!d.flags.collapsed&&!this.live_mode)if(!g&&!1!==d.resizable&&v(a.canvasX,a.canvasY,d.pos[0]+d.size[0]-5,d.pos[1]+d.size[1]-5,10,10))this.resizing_node=d,this.canvas.style.cursor= -"se-resize",g=!0;else{if(d.outputs)for(var x=0,c=d.outputs.length;xr[0]+4||a.canvasYr[1]+4)){this.showLinkMenu(d,a);this.over_link_center=null;break}this.selected_group=this.graph.getGroupOnPos(a.canvasX,a.canvasY);this.selected_group_resizing=!1;this.selected_group&&!this.read_only&&(a.ctrlKey&&(this.dragging_rectangle=null),10>C([a.canvasX,a.canvasY],[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])* -this.ds.scale?this.selected_group_resizing=!0:this.selected_group.recomputeInsideNodes());f&&!this.read_only&&this.allow_searchbox&&this.showSearchBox(a);r=!0}!g&&r&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}else 2!=a.which&&3==a.which&&(this.read_only||this.processContextMenu(d,a));this.last_mouse[0]=a.localX;this.last_mouse[1]=a.localY;this.last_mouseclick=h.getTime();this.last_mouse_dragging=!0;this.graph.change();(!b.document.activeElement||"input"!=b.document.activeElement.nodeName.toLowerCase()&& -"textarea"!=b.document.activeElement.nodeName.toLowerCase())&&a.preventDefault();a.stopPropagation();if(this.onMouseDown)this.onMouseDown(a);return!1}}};k.prototype.processMouseMove=function(a){this.autoresize&&this.resize();if(this.graph){k.active_canvas=this;this.adjustMouseEvent(a);var b=[a.localX,a.localY],d=[b[0]-this.last_mouse[0],b[1]-this.last_mouse[1]];this.last_mouse=b;this.canvas_mouse[0]=a.canvasX;this.canvas_mouse[1]=a.canvasY;a.dragging=this.last_mouse_dragging;this.node_widget&&(this.processNodeWidgets(this.node_widget[0], -this.canvas_mouse,a,this.node_widget[1]),this.dirty_canvas=!0);if(this.dragging_rectangle)this.dragging_rectangle[2]=a.canvasX-this.dragging_rectangle[0],this.dragging_rectangle[3]=a.canvasY-this.dragging_rectangle[1],this.dirty_canvas=!0;else if(this.selected_group&&!this.read_only)this.selected_group_resizing?this.selected_group.size=[a.canvasX-this.selected_group.pos[0],a.canvasY-this.selected_group.pos[1]]:(this.selected_group.move(d[0]/this.ds.scale,d[1]/this.ds.scale,a.ctrlKey),this.selected_group._nodes.length&& -(this.dirty_canvas=!0)),this.dirty_bgcanvas=!0;else if(this.dragging_canvas)this.ds.offset[0]+=d[0]/this.ds.scale,this.ds.offset[1]+=d[1]/this.ds.scale,this.dirty_bgcanvas=this.dirty_canvas=!0;else if(this.allow_interaction&&!this.read_only){this.connecting_node&&(this.dirty_canvas=!0);for(var g=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes),b=0,f=this.graph._nodes.length;bx[0]+4||a.canvasYx[1]+4)){f=r;break}}f!=this.over_link_center&&(this.over_link_center=f,this.dirty_canvas=!0);this.canvas&&(this.canvas.style.cursor= -"")}if(this.node_capturing_input&&this.node_capturing_input!=g&&this.node_capturing_input.onMouseMove)this.node_capturing_input.onMouseMove(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]],this);if(this.node_dragged&&!this.live_mode){for(b in this.selected_nodes)g=this.selected_nodes[b],g.pos[0]+=d[0]/this.ds.scale,g.pos[1]+=d[1]/this.ds.scale;this.dirty_bgcanvas=this.dirty_canvas=!0}this.resizing_node&&!this.live_mode&&(d=[a.canvasX-this.resizing_node.pos[0], -a.canvasY-this.resizing_node.pos[1]],b=this.resizing_node.computeSize(),d[0]=Math.max(b[0],d[0]),d[1]=Math.max(b[1],d[1]),this.resizing_node.setSize(d),this.canvas.style.cursor="se-resize",this.dirty_bgcanvas=this.dirty_canvas=!0)}a.preventDefault();return!1}};k.prototype.processMouseUp=function(a){if(this.graph){var b=this.getCanvasWindow().document;k.active_canvas=this;b.removeEventListener("mousemove",this._mousemove_callback,!0);this.canvas.addEventListener("mousemove",this._mousemove_callback, -!0);b.removeEventListener("mouseup",this._mouseup_callback,!0);this.adjustMouseEvent(a);b=h.getTime();a.click_time=b-this.last_mouseclick;this.last_mouse_dragging=!1;if(1==a.which){this.node_widget&&this.processNodeWidgets(this.node_widget[0],this.canvas_mouse,a);this.node_widget=null;if(this.selected_group){var b=this.selected_group.pos[0]-Math.round(this.selected_group.pos[0]),d=this.selected_group.pos[1]-Math.round(this.selected_group.pos[1]);this.selected_group.move(b,d,a.ctrlKey);this.selected_group.pos[0]= -Math.round(this.selected_group.pos[0]);this.selected_group.pos[1]=Math.round(this.selected_group.pos[1]);this.selected_group._nodes.length&&(this.dirty_canvas=!0);this.selected_group=null}this.selected_group_resizing=!1;if(this.dragging_rectangle){if(this.graph){b=this.graph._nodes;d=new Float32Array(4);this.deselectAllNodes();var g=Math.abs(this.dragging_rectangle[2]),f=Math.abs(this.dragging_rectangle[3]),r=0>this.dragging_rectangle[3]?this.dragging_rectangle[1]-f:this.dragging_rectangle[1];this.dragging_rectangle[0]= -0>this.dragging_rectangle[2]?this.dragging_rectangle[0]-g:this.dragging_rectangle[0];this.dragging_rectangle[1]=r;this.dragging_rectangle[2]=g;this.dragging_rectangle[3]=f;f=[];for(r=0;ra.click_time&&v(a.canvasX,a.canvasY,g.pos[0],g.pos[1]-h.NODE_TITLE_HEIGHT,h.NODE_TITLE_HEIGHT,h.NODE_TITLE_HEIGHT)&&g.collapse();this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_dragged.pos[0]=Math.round(this.node_dragged.pos[0]);this.node_dragged.pos[1]=Math.round(this.node_dragged.pos[1]);this.graph.config.align_to_grid&& -this.node_dragged.alignToGrid();if(this.onNodeMoved)this.onNodeMoved(this.node_dragged);this.node_dragged=null}else{g=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes);!g&&300>a.click_time&&this.deselectAllNodes();this.dirty_canvas=!0;this.dragging_canvas=!1;if(this.node_over&&this.node_over.onMouseUp)this.node_over.onMouseUp(a,[a.canvasX-this.node_over.pos[0],a.canvasY-this.node_over.pos[1]],this);if(this.node_capturing_input&&this.node_capturing_input.onMouseUp)this.node_capturing_input.onMouseUp(a, -[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]])}}else 2==a.which?(this.dirty_canvas=!0,this.dragging_canvas=!1):3==a.which&&(this.dirty_canvas=!0,this.dragging_canvas=!1);this.graph.change();a.stopPropagation();a.preventDefault();return!1}};k.prototype.processMouseWheel=function(a){if(this.graph&&this.allow_dragcanvas){var b=null!=a.wheelDeltaY?a.wheelDeltaY:-60*a.detail;this.adjustMouseEvent(a);var d=this.ds.scale;0b&&(d*=1/1.1);this.ds.changeScale(d, -[a.localX,a.localY]);this.graph.change();a.preventDefault();return!1}};k.prototype.isOverNodeBox=function(a,b,d){var g=h.NODE_TITLE_HEIGHT;return v(b,d,a.pos[0]+2,a.pos[1]+2-g,g-4,g-4)?!0:!1};k.prototype.isOverNodeInput=function(a,b,d,g){if(a.inputs)for(var f=0,h=a.inputs.length;fd- -this.graph._last_trigger_time)&&this.drawBackCanvas();(this.dirty_canvas||a)&&this.drawFrontCanvas();this.fps=this.render_time?1/this.render_time:0;this.frame+=1}};k.prototype.drawFrontCanvas=function(){this.dirty_canvas=!1;this.ctx||(this.ctx=this.bgcanvas.getContext("2d"));var a=this.ctx;if(a){a.start2D&&a.start2D();var b=this.canvas;a.restore();a.setTransform(1,0,0,1,0,0);this.dirty_area&&(a.save(),a.beginPath(),a.rect(this.dirty_area[0],this.dirty_area[1],this.dirty_area[2],this.dirty_area[3]), -a.clip());this.clear_background&&a.clearRect(0,0,b.width,b.height);this.bgcanvas==this.canvas?this.drawBackCanvas():a.drawImage(this.bgcanvas,0,0);if(this.onRender)this.onRender(b,a);this.show_info&&this.renderInfo(a);if(this.graph){a.save();this.ds.toCanvasContext(a);for(var b=this.computeVisibleNodes(null,this.visible_nodes),d=0;d> ";b.fillText(g+d.getTitle(),0.5*a.width,40);b.restore()}d=!1;this.onRenderBackground&&(d=this.onRenderBackground(a,b));b.restore(); -b.setTransform(1,0,0,1,0,0);this.visible_links.length=0;if(this.graph){b.save();this.ds.toCanvasContext(b);if(this.background_image&&0.5this.ds.scale;if(this.live_mode){if(!a.flags.collapsed&&(b.shadowColor="transparent",a.onDrawForeground))a.onDrawForeground(b,this,this.canvas)}else{var r=this.editor_alpha;b.globalAlpha=r;this.render_shadows&&!f?(b.shadowColor= -h.DEFAULT_SHADOW_COLOR,b.shadowOffsetX=2*this.ds.scale,b.shadowOffsetY=2*this.ds.scale,b.shadowBlur=3*this.ds.scale):b.shadowColor="transparent";if(!a.flags.collapsed||!a.onDrawCollapsed||!0!=a.onDrawCollapsed(b,this)){var x=a._shape||h.BOX_SHAPE;B.set(a.size);var c=a.horizontal;if(a.flags.collapsed){b.font=this.inner_text_font;var l=a.getTitle?a.getTitle():a.title;null!=l&&(a._collapsed_width=Math.min(a.size[0],b.measureText(l).width+2*h.NODE_TITLE_HEIGHT),B[0]=a._collapsed_width,B[1]=0)}a.clip_area&& -(b.save(),b.beginPath(),x==h.BOX_SHAPE?b.rect(0,0,B[0],B[1]):x==h.ROUND_SHAPE?b.roundRect(0,0,B[0],B[1],10):x==h.CIRCLE_SHAPE&&b.arc(0.5*B[0],0.5*B[1],0.5*B[0],0,2*Math.PI),b.clip());a.has_errors&&(g="red");this.drawNodeShape(a,b,B,d,g,a.is_selected,a.mouseOver);b.shadowColor="transparent";if(a.onDrawForeground)a.onDrawForeground(b,this,this.canvas);b.textAlign=c?"center":"left";b.font=this.inner_text_font;g=!f;x=this.connecting_output;b.lineWidth=1;var l=0,q=new Float32Array(2);if(!a.flags.collapsed){if(a.inputs)for(d= -0;dthis.ds.scale,q=a._shape||a.constructor.shape||h.ROUND_SHAPE,p=a.constructor.title_mode,y=!0;p==h.TRANSPARENT_TITLE?y=!1:p==h.AUTOHIDE_TITLE&&c&&(y=!0);u[0]=0;u[1]=y?-f:0;u[2]=d[0]+1;u[3]=y?d[1]+f:d[1];c=b.globalAlpha;b.beginPath();q==h.BOX_SHAPE|| -l?b.fillRect(u[0],u[1],u[2],u[3]):q==h.ROUND_SHAPE||q==h.CARD_SHAPE?b.roundRect(u[0],u[1],u[2],u[3],this.round_radius,q==h.CARD_SHAPE?0:this.round_radius):q==h.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0],0,2*Math.PI);b.fill();a.flags.collapsed||(b.shadowColor="transparent",b.fillStyle="rgba(0,0,0,0.2)",b.fillRect(0,-1,u[2],2));b.shadowColor="transparent";if(a.onDrawBackground)a.onDrawBackground(b,this,this.canvas);if(y||p==h.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,f,d,this.ds.scale, -g);else if(p!=h.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){y=a.constructor.title_color||g;a.flags.collapsed&&(b.shadowColor=h.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var t=k.gradients[y];t||(t=k.gradients[y]=b.createLinearGradient(0,0,400,0),t.addColorStop(0,y),t.addColorStop(1,"#000"));b.fillStyle=t}else b.fillStyle=y;b.beginPath();q==h.BOX_SHAPE||l?b.rect(0,-f,d[0]+1,f):q!=h.ROUND_SHAPE&&q!=h.CARD_SHAPE||b.roundRect(0,-f,d[0]+1,f,this.round_radius,a.flags.collapsed? -this.round_radius:0);b.fill();b.shadowColor="transparent"}if(a.onDrawTitleBox)a.onDrawTitleBox(b,f,d,this.ds.scale);else q==h.ROUND_SHAPE||q==h.CIRCLE_SHAPE||q==h.CARD_SHAPE?(l&&(b.fillStyle="black",b.beginPath(),b.arc(0.5*f,-0.5*f,6,0,2*Math.PI),b.fill()),b.fillStyle=a.boxcolor||h.NODE_DEFAULT_BOXCOLOR,l?b.fillRect(0.5*f-5,-0.5*f-5,10,10):(b.beginPath(),b.arc(0.5*f,-0.5*f,5,0,2*Math.PI),b.fill())):(l&&(b.fillStyle="black",b.fillRect(0.5*(f-10)-1,-0.5*(f+10)-1,12,12)),b.fillStyle=a.boxcolor||h.NODE_DEFAULT_BOXCOLOR, -b.fillRect(0.5*(f-10),-0.5*(f+10),10,10));b.globalAlpha=c;if(a.onDrawTitleText)a.onDrawTitleText(b,f,d,this.ds.scale,this.title_text_font,r);!l&&(b.font=this.title_text_font,l=String(a.getTitle()))&&(b.fillStyle=r?"white":a.constructor.title_text_color||this.node_title_color,a.flags.collapsed?(b.textAlign="left",b.measureText(l),b.fillText(l.substr(0,20),f,h.NODE_TITLE_TEXT_Y-f),b.textAlign="left"):(b.textAlign="left",b.fillText(l,f,h.NODE_TITLE_TEXT_Y-f)));if(a.onDrawTitle)a.onDrawTitle(b)}if(r){if(a.onBounding)a.onBounding(u); -p==h.TRANSPARENT_TITLE&&(u[1]-=f,u[3]+=f);b.lineWidth=1;b.globalAlpha=0.8;b.beginPath();q==h.BOX_SHAPE?b.rect(-6+u[0],-6+u[1],12+u[2],12+u[3]):q==h.ROUND_SHAPE||q==h.CARD_SHAPE&&a.flags.collapsed?b.roundRect(-6+u[0],-6+u[1],12+u[2],12+u[3],2*this.round_radius):q==h.CARD_SHAPE?b.roundRect(-6+u[0],-6+u[1],12+u[2],12+u[3],2*this.round_radius,2):q==h.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0]+6,0,2*Math.PI);b.strokeStyle="#FFF";b.stroke();b.strokeStyle=g;b.globalAlpha=1}};var G=new Float32Array(4), -q=new Float32Array(4),p=new Float32Array(2),l=new Float32Array(2);k.prototype.drawConnections=function(a){var b=h.getTime(),d=this.visible_area;G[0]=d[0]-20;G[1]=d[1]-20;G[2]=d[2]+40;G[3]=d[3]+40;a.lineWidth=this.connections_width;a.fillStyle="#AAA";a.strokeStyle="#AAA";a.globalAlpha=this.editor_alpha;for(var d=this.graph._nodes,g=0,f=d.length;gq[2]&&(q[0]+=q[2],q[2]=Math.abs(q[2]));0>q[3]&&(q[1]+=q[3],q[3]=Math.abs(q[3]));if(s(q,G)){var m=y.outputs[e],e=r.inputs[c];if(m&&e&&(y=m.dir||(y.horizontal?h.DOWN:h.RIGHT),e=e.dir||(r.horizontal?h.UP:h.LEFT),this.renderLink(a,n,t,k,!1,0,null,y,e),k&&k._last_time&&1E3>b-k._last_time)){var m= -2-0.002*(b-k._last_time),H=a.globalAlpha;a.globalAlpha=H*m;this.renderLink(a,n,t,k,!0,m,"white",y,e);a.globalAlpha=H}}}}}}a.globalAlpha=1};k.prototype.renderLink=function(a,b,d,g,f,r,c,l,q,p){g&&this.visible_links.push(g);!c&&g&&(c=g.color||k.link_type_colors[g.type]);c||(c=this.default_link_color);null!=g&&this.highlighted_links[g.id]&&(c="#FFF");l=l||h.RIGHT;q=q||h.LEFT;var y=C(b,d);this.render_connections_border&&0.6b[1]?0:Math.PI,a.save(),a.translate(t[0],t[1]),a.rotate(e),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore(),a.save(),a.translate(p[0],p[1]),a.rotate(H),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill());if(r)for(a.fillStyle= -c,t=0;5>t;++t)r=(0.001*h.getTime()+0.2*t)%1,f=this.computeConnectionPoint(b,d,r,l,q),a.beginPath(),a.arc(f[0],f[1],5,0,2*Math.PI),a.fill()};k.prototype.computeConnectionPoint=function(a,b,d,g,f){g=g||h.RIGHT;f=f||h.LEFT;var r=C(a,b),c=[a[0],a[1]],l=[b[0],b[1]];switch(g){case h.LEFT:c[0]+=-0.25*r;break;case h.RIGHT:c[0]+=0.25*r;break;case h.UP:c[1]+=-0.25*r;break;case h.DOWN:c[1]+=0.25*r}switch(f){case h.LEFT:l[0]+=-0.25*r;break;case h.RIGHT:l[0]+=0.25*r;break;case h.UP:l[1]+=-0.25*r;break;case h.DOWN:l[1]+= -0.25*r}g=(1-d)*(1-d)*(1-d);f=3*(1-d)*(1-d)*d;r=3*(1-d)*d*d;d*=d*d;return[g*a[0]+f*c[0]+r*l[0]+d*b[0],g*a[1]+f*c[1]+r*l[1]+d*b[1]]};k.prototype.drawExecutionOrder=function(a){a.shadowColor="transparent";a.globalAlpha=0.25;a.textAlign="center";a.strokeStyle="white";a.globalAlpha=0.75;for(var b=this.visible_nodes,d=0;dt.last_y&&l -t.options.max&&(t.value=t.options.max);else if("mousedown"==d.type){var n=t.options.values;n&&n.constructor===Function&&(n=t.options.values(t,a));var s=null;"number"!=t.type&&(s=n.constructor===Array?n:Object.keys(n));c=40>c?-1:c>q-40?1:0;if("number"==t.type)t.value+=0.1*c*(t.options.step||1),null!=t.options.min&&t.valuet.options.max&&(t.value=t.options.max);else if(c)e=-1,e=n.constructor===Object?s.indexOf(String(t.value))+c:s.indexOf(t.value)+ -c,e>=s.length&&(e=s.length-1),0>e&&(e=0),t.value=n.constructor===Array?n[e]:e;else{var m=n!=s?Object.values(n):n;new h.ContextMenu(m,{scale:Math.max(1,this.ds.scale),event:d,className:"dark",callback:B.bind(t)},e);var B=function(a,b,d){n!=s&&(a=m.indexOf(a));this.value=a;f(this,a);p.dirty_canvas=!0;return!1}}}else"mouseup"==d.type&&"number"==t.type&&(c=40>c?-1:c>q-40?1:0,200>d.click_time&&0==c&&this.prompt("Value",t.value,function(a){this.value=Number(a);f(this,this.value)}.bind(t),d));g!=t.value&& -setTimeout(function(){f(this,this.value)}.bind(t),20);this.dirty_canvas=!0;break;case "toggle":"mousedown"==d.type&&(t.value=!t.value,setTimeout(function(){f(t,t.value)},20));break;case "string":case "text":"mousedown"==d.type&&this.prompt("Value",t.value,function(a){this.value=a;f(this,a)}.bind(t),d);break;default:t.mouse&&(this.dirty_canvas=t.mouse(d,[c,l],a))}return t}}}return null};k.prototype.drawGroups=function(a,b){if(this.graph){var d=this.graph._groups;b.save();b.globalAlpha=0.5*this.editor_alpha; -for(var g=0;gd&&0.01>b.editor_alpha&&(clearInterval(g),1>d&&(b.live_mode=!0));1"+ -p+""+a+"",value:p});if(q.length)return new h.ContextMenu(q,{event:d,callback:c,parentMenu:g,allow_html:!0,node:f},b),!1}};k.decodeHTML=function(a){var b=document.createElement("div");b.innerText=a;return b.innerHTML};k.onResizeNode=function(a,b,d,g,f){if(f){f.size=f.computeSize();if(f.onResize)f.onResize(f.size);f.setDirtyCanvas(!0,!0)}};k.prototype.showLinkMenu=function(a,b){var d=this;console.log(a);var g=new h.ContextMenu(["Add Node",null,"Delete"],{event:b, -title:null!=a.data?a.data.constructor.name:null,callback:function(b,h,c){switch(b){case "Add Node":k.onMenuAdd(null,null,c,g,function(b){console.log("node autoconnect");var f=d.graph.getNodeById(a.origin_id),g=d.graph.getNodeById(a.target_id);b.inputs&&b.inputs.length&&b.outputs&&b.outputs.length&&f.outputs[a.origin_slot].type==b.inputs[0].type&&b.outputs[0].type==g.inputs[0].type&&(f.connect(a.origin_slot,b,0),b.connect(0,g,a.target_slot),b.pos[0]-=0.5*b.size[0])});break;case "Delete":d.graph.removeLink(a.id)}}}); -return!1};k.onShowPropertyEditor=function(a,b,d,g,f){function h(){var b=q.value;"Number"==a.type?b=Number(b):"Boolean"==a.type&&(b=Boolean(b));f[c]=b;l.parentNode&&l.parentNode.removeChild(l);f.setDirtyCanvas(!0,!0)}var c=a.property||"title";b=f[c];var l=document.createElement("div");l.className="graphdialog";l.innerHTML="";l.querySelector(".name").innerText=c;var q=l.querySelector("input");q&&(q.value=b,q.addEventListener("blur", -function(a){this.focus()}),q.addEventListener("keydown",function(a){13==a.keyCode&&(h(),a.preventDefault(),a.stopPropagation())}));b=k.active_canvas.canvas;d=b.getBoundingClientRect();var p=g=-20;d&&(g-=d.left,p-=d.top);event?(l.style.left=event.clientX+g+"px",l.style.top=event.clientY+p+"px"):(l.style.left=0.5*b.width+g+"px",l.style.top=0.5*b.height+p+"px");l.querySelector("button").addEventListener("click",h);b.parentNode.appendChild(l)};k.prototype.prompt=function(a,b,d,g){var f=this;a=a||"";var h= -!1,c=document.createElement("div");c.className="graphdialog rounded";c.innerHTML=" ";c.close=function(){f.prompt_box=null;c.parentNode&&c.parentNode.removeChild(c)};1k.search_limit)break}}p=null;if(Array.prototype.filter)p=Object.keys(h.registered_node_types).filter(x);else for(l in p=[],h.registered_node_types)x(l)&&p.push(l);for(l=0;lk.search_limit);l++);var x=function(a){var b=h.registered_node_types[a];return q&&b.filter!=q?!1:-1!==a.toLowerCase().indexOf(d)}}} -var f=this,c=k.active_canvas,l=c.canvas,q=l.ownerDocument||document,p=document.createElement("div");p.className="litegraph litesearchbox graphdialog rounded";p.innerHTML="Search
";p.close=function(){f.search_box=null;q.body.focus();q.body.style.overflow="";setTimeout(function(){f.canvas.focus()},20);p.parentNode&&p.parentNode.removeChild(p)};var e=null;1l.height-200&&(y.style.maxHeight=l.height-a.layerY-20+"px");m.focus(); -return p};k.prototype.showEditPropertyValue=function(a,b,d){function g(){f(t.value)}function f(f){"number"==typeof a.properties[b]&&(f=Number(f));if("array"==h||"object"==h)f=JSON.parse(f);a.properties[b]=f;a._graph&&a._graph._version++;if(a.onPropertyChanged)a.onPropertyChanged(b,f);if(d.onclose)d.onclose();e.close();a.setDirtyCanvas(!0,!0)}if(a&&void 0!==a.properties[b]){d=d||{};var c=a.getPropertyInfo(b),h=c.type,l="";if("string"==h||"number"==h||"array"==h||"object"==h)l=""; -else if("enum"==h&&c.values){var l=""}else if("boolean"==h)l="";else{console.warn("unknown type: "+h);return}var e=this.createDialog(""+b+""+l+"",d);if("enum"== -h&&c.values){var t=e.querySelector("select");t.addEventListener("change",function(a){f(a.target.value)})}else if("boolean"==h)(t=e.querySelector("input"))&&t.addEventListener("click",function(a){f(!!t.checked)});else if(t=e.querySelector("input"))t.addEventListener("blur",function(a){this.focus()}),p=void 0!==a.properties[b]?a.properties[b]:"",p=JSON.stringify(p),t.value=p,t.addEventListener("keydown",function(a){13==a.keyCode&&(g(),a.preventDefault(),a.stopPropagation())});e.querySelector("button").addEventListener("click", -g);return e}};k.prototype.createDialog=function(a,b){b=b||{};var d=document.createElement("div");d.className="graphdialog";d.innerHTML=a;var g=this.canvas.getBoundingClientRect(),f=-20,c=-20;g&&(f-=g.left,c-=g.top);b.position?(f+=b.position[0],c+=b.position[1]):b.event?(f+=b.event.clientX,c+=b.event.clientY):(f+=0.5*this.canvas.width,c+=0.5*this.canvas.height);d.style.left=f+"px";d.style.top=c+"px";this.canvas.parentNode.appendChild(d);d.close=function(){this.parentNode&&this.parentNode.removeChild(this)}; -return d};k.onMenuNodeCollapse=function(a,b,d,g,f){f.collapse()};k.onMenuNodePin=function(a,b,d,g,f){f.pin()};k.onMenuNodeMode=function(a,b,d,g,f){new h.ContextMenu(["Always","On Event","On Trigger","Never"],{event:d,callback:function(a){if(f)switch(a){case "On Event":f.mode=h.ON_EVENT;break;case "On Trigger":f.mode=h.ON_TRIGGER;break;case "Never":f.mode=h.NEVER;break;default:f.mode=h.ALWAYS}},parentMenu:g,node:f});return!1};k.onMenuNodeColors=function(a,b,d,g,f){if(!f)throw"no node for color";b= -[];b.push({value:null,content:"No color"});for(var c in k.node_colors)a=k.node_colors[c],a={value:c,content:""+c+""},b.push(a);new h.ContextMenu(b,{event:d,callback:function(a){f&&((a=a.value?k.node_colors[a.value]:null)?f.constructor===h.LGraphGroup?f.color=a.groupcolor:(f.color=a.color,f.bgcolor=a.bgcolor): -(delete f.color,delete f.bgcolor),f.setDirtyCanvas(!0,!0))},parentMenu:g,node:f});return!1};k.onMenuNodeShapes=function(a,b,d,g,f){if(!f)throw"no node passed";new h.ContextMenu(h.VALID_SHAPES,{event:d,callback:function(a){f&&(f.shape=a,f.setDirtyCanvas(!0))},parentMenu:g,node:f});return!1};k.onMenuNodeRemove=function(a,b,d,g,f){if(!f)throw"no node passed";!1!==f.removable&&(f.graph.remove(f),f.setDirtyCanvas(!0,!0))};k.onMenuNodeToSubgraph=function(a,b,d,g,f){a=f.graph;if(b=k.active_canvas)d=Object.values(b.selected_nodes|| -{}),d.length||(d=[f]),g=h.createNode("graph/subgraph"),g.pos=f.pos.concat(),a.add(g),g.buildFromNodes(d),b.deselectAllNodes(),f.setDirtyCanvas(!0,!0)};k.onMenuNodeClone=function(a,b,d,g,f){!1!=f.clonable&&(a=f.clone())&&(a.pos=[f.pos[0]+5,f.pos[1]+5],f.graph.add(a),f.setDirtyCanvas(!0,!0))};k.node_colors={red:{color:"#322",bgcolor:"#533",groupcolor:"#A88"},brown:{color:"#332922",bgcolor:"#593930",groupcolor:"#b06634"},green:{color:"#232",bgcolor:"#353",groupcolor:"#8A8"},blue:{color:"#223",bgcolor:"#335", -groupcolor:"#88A"},pale_blue:{color:"#2a363b",bgcolor:"#3f5159",groupcolor:"#3f789e"},cyan:{color:"#233",bgcolor:"#355",groupcolor:"#8AA"},purple:{color:"#323",bgcolor:"#535",groupcolor:"#a1309b"},yellow:{color:"#432",bgcolor:"#653",groupcolor:"#b58b2a"},black:{color:"#222",bgcolor:"#000",groupcolor:"#444"}};k.prototype.getCanvasMenuOptions=function(){var a=null;this.getMenuOptions?a=this.getMenuOptions():(a=[{content:"Add Node",has_submenu:!0,callback:k.onMenuAdd},{content:"Add Group",callback:k.onGroupAdd}], -this._graph_stack&&0Name",f),l=h.querySelector("input");l&&c&&(l.value=c.label||"");h.querySelector("button").addEventListener("click",function(a){l.value&&(c&&(c.label=l.value),d.setDirty(!0));h.close()})}},extra:a};a&&(c.title=a.type);var l=null;a&&(l=a.getSlotInPosition(b.canvasX,b.canvasY),k.active_node= -a);if(l){f=[];if(a.getSlotMenuOptions)f=a.getSlotMenuOptions(l);else{l&&l.output&&l.output.links&&l.output.links.length&&f.push({content:"Disconnect Links",slot:l});var q=l.input||l.output;f.push(q.locked?"Cannot remove":{content:"Remove Slot",slot:l});f.push(q.nameLocked?"Cannot rename":{content:"Rename Slot",slot:l})}c.title=(l.input?l.input.type:l.output.type)||"*";l.input&&l.input.type==h.ACTION&&(c.title="Action");l.output&&l.output.type==h.EVENT&&(c.title="Event")}else a?f=this.getNodeMenuOptions(a): -(f=this.getCanvasMenuOptions(),(l=this.graph.getGroupOnPos(b.canvasX,b.canvasY))&&f.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:l,options:this.getGroupMenuOptions(l)}}));f&&new h.ContextMenu(f,c,g)};"undefined"!=typeof window&&window.CanvasRenderingContext2D&&(window.CanvasRenderingContext2D.prototype.roundRect=function(a,b,d,g,f,c){void 0===f&&(f=5);void 0===c&&(c=f);this.moveTo(a+f,b);this.lineTo(a+d-f,b);this.quadraticCurveTo(a+d,b,a+d,b+f);this.lineTo(a+d,b+g-c); -this.quadraticCurveTo(a+d,b+g,a+d-c,b+g);this.lineTo(a+c,b+g);this.quadraticCurveTo(a,b+g,a,b+g-c);this.lineTo(a,b+f);this.quadraticCurveTo(a,b,a+f,b)});h.compareObjects=function(a,b){for(var d in a)if(a[d]!=b[d])return!1;return!0};h.distance=C;h.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+","+Math.round(255*a[2]).toFixed()+","+(4==a.length?a[3].toFixed(2):"1.0")+")"};h.isInsideRectangle=v;h.growBounding=function(a,b,d){b -a[2]&&(a[2]=b);da[3]&&(a[3]=d)};h.isInsideBounding=function(a,b){return a[0]b[1][0]||a[1]>b[1][1]?!1:!0};h.overlapBounding=s;h.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),d=0,g,f,c=0;6>c;c+=2)g="0123456789ABCDEF".indexOf(a.charAt(c)),f="0123456789ABCDEF".indexOf(a.charAt(c+1)),b[d]=16*g+f,d++;return b};h.num2hex=function(a){for(var b="#",d,g,f=0;3>f;f++)d=a[f]/16,g=a[f]%16,b+="0123456789ABCDEF".charAt(d)+"0123456789ABCDEF".charAt(g); -return b};D.prototype.addItem=function(a,b,d){function g(a){var b=this.value;b&&b.has_submenu&&f.call(this,a)}function f(a){var b=this.value,f=!0;c.current_submenu&&c.current_submenu.close(a);if(d.callback){var g=d.callback.call(this,b,d,a,c,d.node);!0===g&&(f=!1)}if(b&&(b.callback&&!d.ignore_item_callbacks&&!0!==b.disabled&&(g=b.callback.call(this,b,d,a,c,d.extra),!0===g&&(f=!1)),b.submenu)){if(!b.submenu.options)throw"ContextMenu submenu needs options";new c.constructor(b.submenu.options,{callback:b.submenu.callback, -event:a,parentMenu:c,ignore_item_callbacks:b.submenu.ignore_item_callbacks,title:b.submenu.title,extra:b.submenu.extra,autoopen:d.autoopen});f=!1}f&&!c.lock&&c.close()}var c=this;d=d||{};var h=document.createElement("div");h.className="litemenu-entry submenu";var l=!1;if(null===b)h.classList.add("separator");else{h.innerHTML=b&&b.title?b.title:a;if(h.value=b)b.disabled&&(l=!0,h.classList.add("disabled")),(b.submenu||b.has_submenu)&&h.classList.add("has_submenu");"function"==typeof b?(h.dataset.value= -a,h.onclick_callback=b):h.dataset.value=b;b.className&&(h.className+=" "+b.className)}this.root.appendChild(h);l||h.addEventListener("click",f);d.autoopen&&h.addEventListener("mouseenter",g);return h};D.prototype.close=function(a,b){this.root.parentNode&&this.root.parentNode.removeChild(this.root);this.parentMenu&&!b&&(this.parentMenu.lock=!1,this.parentMenu.current_submenu=null,void 0===a?this.parentMenu.close():a&&!D.isCursorOverElement(a,this.parentMenu.root)&&D.trigger(this.parentMenu.root,"mouseleave", -a));this.current_submenu&&this.current_submenu.close(a,!0);this.root.closing_timer&&clearTimeout(this.root.closing_timer)};D.trigger=function(a,b,d,g){var f=document.createEvent("CustomEvent");f.initCustomEvent(b,!0,!0,d);f.srcElement=g;a.dispatchEvent?a.dispatchEvent(f):a.__events&&a.__events.dispatchEvent(f);return f};D.prototype.getTopMenu=function(){return this.options.parentMenu?this.options.parentMenu.getTopMenu():this};D.prototype.getFirstEvent=function(){return this.options.parentMenu?this.options.parentMenu.getFirstEvent(): -this.options.event};D.isCursorOverElement=function(a,b){var d=a.clientX,g=a.clientY,f=b.getBoundingClientRect();return f?g>f.top&&gf.left&&dMath.abs(d))return g[1];d=(a-g[0])/d;return g[1]* -(1-d)+f[1]*d}}return 0}};A.prototype.draw=function(a,b,d,g,f,c){if(d=this.points){this.size=b;var h=b[0]-2*this.margin;b=b[1]-2*this.margin;f=f||"#666";a.save();a.translate(this.margin,this.margin);g&&(a.fillStyle="#111",a.fillRect(0,0,h,b),a.fillStyle="#222",a.fillRect(0.5*h,0,1,b),a.strokeStyle="#333",a.strokeRect(0,0,h,b));a.strokeStyle=f;c&&(a.globalAlpha=0.5);a.beginPath();for(g=0;ga[1])){var g=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=a[0]-this.margin,h=a[1]-this.margin;this.selected=this.getCloserPoint([c,h],30/b.ds.scale);-1==this.selected&&(g=[c/g,1-h/f],d.push(g),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(g),this.must_update=!0); -if(-1!=this.selected)return!0}};A.prototype.onMouseMove=function(a,b){var d=this.points;if(d){var g=this.selected;if(!(0>g)){var f=(a[0]-this.margin)/(this.size[0]-2*this.margin),c=(a[1]-this.margin)/(this.size[1]-2*this.margin);this._nearest=this.getCloserPoint([a[0]-this.margin,a[1]-this.margin],30/b.ds.scale);var h=d[g];if(h){var l=0==g||g==d.length-1;!l&&(-10>a[0]||a[0]>this.size[0]+10||-10>a[1]||a[1]>this.size[1]+10)?(d.splice(g,1),this.selected=-1):(h[0]=l?0==g?0:1:Math.clamp(f,0,1),h[1]=1- -Math.clamp(c,0,1),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(h),this.must_update=!0)}}}};A.prototype.onMouseUp=function(a,b){this.selected=-1;return!1};A.prototype.getCloserPoint=function(a,b){var d=this.points;if(!d)return-1;b=b||30;for(var g=this.size[0]-2*this.margin,f=this.size[1]-2*this.margin,c=d.length,h=[0,0],l=1E6,q=-1,p=0;pl||e>b||(q=p,l=e)}return q};h.CurveEditor=A;h.getParameterNames=function(a){return(a+ -"").replace(/[/][/].*$/gm,"").replace(/\s+/g,"").replace(/[/][*][^/*]*[*][/]/g,"").split("){",1)[0].replace(/^[^(]*[(]/,"").replace(/=[^,]+/g,"").split(",").filter(Boolean)};Math.clamp=function(a,b,d){return b>a?b:dthis.size[0]-g.NODE_TITLE_HEIGHT&&0>b[1]){var c=this;setTimeout(function(){d.openSubgraph(c.subgraph)},10)}};m.prototype.onAction=function(a,b){this.subgraph.onAction(a, -b)};m.prototype.onExecute=function(){if(this.enabled=this.getInputOrProperty("enabled")){if(this.inputs)for(var a=0;a=h?this.trigger(null,e):this._pending.push([h,e])};k.prototype.onExecute=function(){var c=1E3* -this.graph.elapsed_time;this.isInputConnected(1)&&(this.properties.time_in_ms=this.getInputData(1));for(var e=0;ee[1]))return this.old_y=c.canvasY,this.captureInput(!0),this.mouse_captured=!0};n.prototype.onMouseMove=function(c){if(this.mouse_captured){var e=this.old_y-c.canvasY;c.shiftKey&&(e*=10);if(c.metaKey||c.altKey)e*=0.1;this.old_y=c.canvasY;c=this._remainder+e/n.pixels_threshold;this._remainder=c%1;c=Math.clamp(this.properties.value+ -(c|0)*this.properties.step,this.properties.min,this.properties.max);this.properties.value=c;this.graph._version++;this.setDirtyCanvas(!0)}};n.prototype.onMouseUp=function(c,e){200>c.click_time&&(this.properties.value=Math.clamp(this.properties.value+(e[1]>0.5*this.size[1]?-1:1)*this.properties.step,this.properties.min,this.properties.max),this.graph._version++,this.setDirtyCanvas(!0));this.mouse_captured&&(this.mouse_captured=!1,this.captureInput(!1))};A.registerNodeType("widget/number",n);e.title= -"Combo";e.desc="Widget to select from a list";e.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};e.prototype.onPropertyChanged=function(c,e){"values"==c?(this._values=e.split(";"),this.widget.options.values=this._values):"value"==c&&(this.widget.value=e)};A.registerNodeType("widget/combo",e);w.title="Knob";w.desc="Circular controller";w.size=[80,100];w.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/ -(this.properties.max-this.properties.min));var e=0.5*this.size[0],k=0.5*this.size[1],n=0.5*Math.min(this.size[0],this.size[1])-5;c.globalAlpha=1;c.save();c.translate(e,k);c.rotate(0.75*Math.PI);c.fillStyle="rgba(0,0,0,0.5)";c.beginPath();c.moveTo(0,0);c.arc(0,0,n,0,1.5*Math.PI);c.fill();c.strokeStyle="black";c.fillStyle=this.properties.color;c.lineWidth=2;c.beginPath();c.moveTo(0,0);c.arc(0,0,n-4,0,1.5*Math.PI*Math.max(0.01,this.value));c.closePath();c.fill();c.lineWidth=1;c.globalAlpha=1;c.restore(); -c.fillStyle="black";c.beginPath();c.arc(e,k,0.75*n,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":this.properties.color;c.beginPath();var s=this.value*Math.PI*1.5+0.75*Math.PI;c.arc(e+Math.cos(s)*n*0.65,k+Math.sin(s)*n*0.65,0.05*n,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":"#AAA";c.font=Math.floor(0.5*n)+"px Arial";c.textAlign="center";c.fillText(this.properties.value.toFixed(this.properties.precision),e,k+0.15*n)}};w.prototype.onExecute=function(){this.setOutputData(0, -this.properties.value);this.boxcolor=A.colorToString([this.value,this.value,this.value])};w.prototype.onMouseDown=function(c){this.center=[0.5*this.size[0],0.5*this.size[1]+20];this.radius=0.5*this.size[0];if(20>c.canvasY-this.pos[1]||A.distance([c.canvasX,c.canvasY],[this.pos[0]+this.center[0],this.pos[1]+this.center[1]])>this.radius)return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];this.captureInput(!0);return!0};w.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX- -this.pos[0],c.canvasY-this.pos[1]];var e=this.value,e=e-0.01*(c[1]-this.oldmouse[1]);1e&&(e=0);this.value=e;this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.oldmouse=c;this.setDirtyCanvas(!0)}};w.prototype.onMouseUp=function(c){this.oldmouse&&(this.oldmouse=null,this.captureInput(!1))};w.prototype.onPropertyChanged=function(c,e){if("min"==c||"max"==c||"value"==c)return this.properties[c]=parseFloat(e),!0};A.registerNodeType("widget/knob", -w);k.title="Inner Slider";k.prototype.onPropertyChanged=function(c,e){"value"==c&&(this.slider.value=e)};k.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};A.registerNodeType("widget/internal_slider",k);C.title="H.Slider";C.desc="Linear slider controller";C.prototype.onDrawForeground=function(c){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min));c.globalAlpha=1;c.lineWidth=1;c.fillStyle="#000";c.fillRect(2, -2,this.size[0]-4,this.size[1]-4);c.fillStyle=this.properties.color;c.beginPath();c.rect(4,4,(this.size[0]-8)*this.value,this.size[1]-8);c.fill()};C.prototype.onExecute=function(){this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.setOutputData(0,this.properties.value);this.boxcolor=A.colorToString([this.value,this.value,this.value])};C.prototype.onMouseDown=function(c){if(0>c.canvasY-this.pos[1])return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY- -this.pos[1]];this.captureInput(!0);return!0};C.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];var e=this.value,e=e+(c[0]-this.oldmouse[0])/this.size[0];1e&&(e=0);this.value=e;this.oldmouse=c;this.setDirtyCanvas(!0)}};C.prototype.onMouseUp=function(c){this.oldmouse=null;this.captureInput(!1)};C.prototype.onMouseLeave=function(c){};A.registerNodeType("widget/hslider",C);v.title="Progress";v.desc="Shows data in linear progress";v.prototype.onExecute= -function(){var c=this.getInputData(0);void 0!=c&&(this.properties.value=c)};v.prototype.onDrawForeground=function(c){c.lineWidth=1;c.fillStyle=this.properties.color;var e=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min),e=Math.min(1,e),e=Math.max(0,e);c.fillRect(2,2,(this.size[0]-4)*e,this.size[1]-4)};A.registerNodeType("widget/progress",v);s.title="Text";s.desc="Shows the input value";s.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"led_text", -text:"LED",type:"minibutton"},{name:"normal_text",text:"Normal",type:"minibutton"}];s.prototype.onDrawForeground=function(c){c.fillStyle=this.properties.color;var e=this.properties.value;this.properties.glowSize?(c.shadowColor=this.properties.color,c.shadowOffsetX=0,c.shadowOffsetY=0,c.shadowBlur=this.properties.glowSize):c.shadowColor="transparent";var k=this.properties.fontsize;c.textAlign=this.properties.align;c.font=k.toString()+"px "+this.properties.font;this.str="number"==typeof e?e.toFixed(this.properties.decimals): -e;if("string"==typeof this.str){var e=this.str.split("\\n"),n;for(n in e)c.fillText(e[n],"left"==this.properties.align?15:this.size[0]-15,-0.15*k+k*(parseInt(n)+1))}c.shadowColor="transparent";this.last_ctx=c;c.textAlign="left"};s.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(this.properties.value=c)};s.prototype.resize=function(){if(this.last_ctx){var c=this.str.split("\\n");this.last_ctx.font=this.properties.fontsize+"px "+this.properties.font;var e=0,k;for(k in c){var n=this.last_ctx.measureText(c[k]).width; -ee?n.xbox.axes.lx:0,this._left_axis[1]=Math.abs(n.xbox.axes.ly)>e?n.xbox.axes.ly:0,this._right_axis[0]=Math.abs(n.xbox.axes.rx)>e?n.xbox.axes.rx:0,this._right_axis[1]=Math.abs(n.xbox.axes.ry)>e?n.xbox.axes.ry:0,this._triggers[0]=Math.abs(n.xbox.axes.ltrigger)>e?n.xbox.axes.ltrigger: -0,this._triggers[1]=Math.abs(n.xbox.axes.rtrigger)>e?n.xbox.axes.rtrigger:0);if(this.outputs)for(e=0;en;n++)if(e[n]){n=e[n];e=this.xbox_mapping;e||(e=this.xbox_mapping={axes:[], -buttons:{},hat:"",hatmap:c.CENTER});e.axes.lx=n.axes[0];e.axes.ly=n.axes[1];e.axes.rx=n.axes[2];e.axes.ry=n.axes[3];e.axes.ltrigger=n.buttons[6].value;e.axes.rtrigger=n.buttons[7].value;e.hat="";e.hatmap=c.CENTER;for(var m=0;mm)e.buttons[c.mapping_array[m]]=n.buttons[m].pressed,n.buttons[m].was_pressed&&this.trigger(c.mapping_array[m]+"_button_event");else switch(m){case 12:n.buttons[m].pressed&&(e.hat+="up",e.hatmap|=c.UP); -break;case 13:n.buttons[m].pressed&&(e.hat+="down",e.hatmap|=c.DOWN);break;case 14:n.buttons[m].pressed&&(e.hat+="left",e.hatmap|=c.LEFT);break;case 15:n.buttons[m].pressed&&(e.hat+="right",e.hatmap|=c.RIGHT);break;case 16:e.buttons.home=n.buttons[m].pressed}n.xbox=e;return n}};c.prototype.onDrawBackground=function(c){if(!this.flags.collapsed){var e=this._left_axis,m=this._right_axis;c.strokeStyle="#88A";c.strokeRect(0.5*(e[0]+1)*this.size[0]-4,0.5*(e[1]+1)*this.size[1]-4,8,8);c.strokeStyle="#8A8"; -c.strokeRect(0.5*(m[0]+1)*this.size[0]-4,0.5*(m[1]+1)*this.size[1]-4,8,8);e=this.size[1]/this._current_buttons.length;c.fillStyle="#AEB";for(m=0;m","enum",{values:a.values});this.size=[80,60]}function b(){this.addInput("inc", -"number");this.addOutput("total","number");this.addProperty("increment",1);this.addProperty("value",0)}function d(){this.addInput("v","number");this.addOutput("sin","number");this.addProperty("amplitude",1);this.addProperty("offset",0);this.bgImageUrl="nodes/imgs/icon-sin.png"}function g(){this.addInput("x","number");this.addInput("y","number");this.addOutput("","number");this.properties={x:1,y:1,formula:"x+y"};this.code_widget=this.addWidget("text","F(x,y)",this.properties.formula,function(a,b,d){d.properties.formula= -a});this.addWidget("toggle","allow",E.allow_scripts,function(a){E.allow_scripts=a});this._func=null}function f(){this.addInput("vec2","vec2");this.addOutput("x","number");this.addOutput("y","number")}function r(){this.addInputs([["x","number"],["y","number"]]);this.addOutput("vec2","vec2");this.properties={x:0,y:0};this._data=new Float32Array(2)}function x(){this.addInput("vec3","vec3");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number")}function I(){this.addInputs([["x", -"number"],["y","number"],["z","number"]]);this.addOutput("vec3","vec3");this.properties={x:0,y:0,z:0};this._data=new Float32Array(3)}function J(){this.addInput("vec4","vec4");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number");this.addOutput("w","number")}function K(){this.addInputs([["x","number"],["y","number"],["z","number"],["w","number"]]);this.addOutput("vec4","vec4");this.properties={x:0,y:0,z:0,w:0};this._data=new Float32Array(4)}var E=z.LiteGraph;c.title= -"Converter";c.desc="type A to type B";c.prototype.onExecute=function(){var a=this.getInputData(0);if(null!=a&&this.outputs)for(var b=0;ba&&(a+=1024);var c=Math.floor(a);a-=c;d=k.data[c];c=k.data[1023==c?0:c+1];b&&(a=a*a*a*(a*(6*a-15)+10));return d*(1-a)+c*a};k.prototype.onExecute=function(){for(var a=this.getInputData(0)||0,b=this.properties.octaves||1,d=0,c=1,a=a+(this.properties.seed||0),g=this.properties.speed||1,f=0,l=0;lc);++l);a=this.properties.min; -this._last_v=d/f*(this.properties.max-a)+a;this.setOutputData(0,this._last_v)};k.prototype.onDrawBackground=function(a){this.outputs[0].label=(this._last_v||0).toFixed(3)};E.registerNodeType("math/noise",k);C.title="Spikes";C.desc="spike every random time";C.prototype.onExecute=function(){var a=this.graph.elapsed_time;this._remaining_time-=a;this._blink_time-=a;a=0;0this._remaining_time?(this._remaining_time= -Math.random()*(this.properties.max_time-this.properties.min_time)+this.properties.min_time,this._blink_time=this.properties.duration,this.boxcolor="#FFF"):this.boxcolor="#000";this.setOutputData(0,a)};E.registerNodeType("math/spikes",C);v.title="Clamp";v.desc="Clamp number between min and max";v.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(a=Math.max(this.properties.min,a),a=Math.min(this.properties.max,a),this.setOutputData(0,a))};v.prototype.getCode=function(a){a="";this.isInputConnected(0)&& -(a+="clamp({{0}},"+this.properties.min+","+this.properties.max+")");return a};E.registerNodeType("math/clamp",v);s.title="Lerp";s.desc="Linear Interpolation";s.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.getInputData(1);null==b&&(b=0);var d=this.properties.f,c=this.getInputData(2);void 0!==c&&(d=c);this.setOutputData(0,a*(1-d)+b*d)};s.prototype.onGetInputs=function(){return[["f","number"]]};E.registerNodeType("math/lerp",s);D.title="Abs";D.desc="Absolute";D.prototype.onExecute= -function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.abs(a))};E.registerNodeType("math/abs",D);A.title="Floor";A.desc="Floor number to remove fractional part";A.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.floor(a))};E.registerNodeType("math/floor",A);h.title="Frac";h.desc="Returns fractional part";h.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a%1)};E.registerNodeType("math/frac",h);y.title= -"Smoothstep";y.desc="Smoothstep";y.prototype.onExecute=function(){var a=this.getInputData(0);if(void 0!==a){var b=this.properties.A,a=Math.clamp((a-b)/(this.properties.B-b),0,1);this.setOutputData(0,a*a*(3-2*a))}};E.registerNodeType("math/smoothstep",y);B.title="Scale";B.desc="v * factor";B.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a*this.properties.factor)};E.registerNodeType("math/scale",B);u.title="Gate";u.desc="if v is true, then outputs A, otherwise B"; -u.prototype.onExecute=function(){var a=this.getInputData(0);this.setOutputData(0,this.getInputData(a?1:2))};E.registerNodeType("math/gate",u);G.title="Average";G.desc="Average Filter";G.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this._values.length;this._values[this._current%b]=a;this._current+=1;this._current>b&&(this._current=0);for(var d=a=0;db&&(b=1);this.properties.samples= -Math.round(b);var d=this._values;this._values=new Float32Array(this.properties.samples);d.length<=this._values.length?this._values.set(d):this._values.set(d.subarray(0,this._values.length))};E.registerNodeType("math/average",G);q.title="TendTo";q.desc="moves the output value always closer to the input";q.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.factor;this._value=null==this._value?a:this._value*(1-b)+a*b;this.setOutputData(0,this._value)};E.registerNodeType("math/tendTo", -q);p.values="+ - * / % ^ max min".split(" ");p.title="Operation";p.desc="Easy math operators";p["@OP"]={type:"enum",title:"operation",values:p.values};p.size=[100,60];p.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"};p.prototype.setValue=function(a){"string"==typeof a&&(a=parseFloat(a));this.properties.value=a};p.prototype.onPropertyChanged=function(a,b){if("OP"==a)switch(this.properties.OP){case "+":this._func= -function(a,b){return a+b};break;case "-":this._func=function(a,b){return a-b};break;case "x":case "X":case "*":this._func=function(a,b){return a*b};break;case "/":this._func=function(a,b){return a/b};break;case "%":this._func=function(a,b){return a%b};break;case "^":this._func=function(a,b){return Math.pow(a,b)};break;case "max":this._func=function(a,b){return Math.max(a,b)};break;case "min":this._func=function(a,b){return Math.min(a,b)};break;default:console.warn("Unknown operation: "+this.properties.OP), -this._func=function(a){return a}}};p.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);null!=a?a.constructor===Number&&(this.properties.A=a):a=this.properties.A;null!=b?this.properties.B=b:b=this.properties.B;var d;if(a.constructor===Number)d=this._func(a,b);else if(a.constructor===Array){d=this._result;d.length=a.length;for(var c=0;cB":f=a>b;break;case "A=B":f=a>=b}this.setOutputData(d,f)}}};l.prototype.onGetOutputs=function(){return[["A==B","boolean"],["A!=B","boolean"], -["A>B","boolean"],["A=B","boolean"],["A<=B","boolean"]]};E.registerNodeType("math/compare",l);E.registerSearchboxExtra("math/compare","==",{outputs:[["A==B","boolean"]],title:"A==B"});E.registerSearchboxExtra("math/compare","!=",{outputs:[["A!=B","boolean"]],title:"A!=B"});E.registerSearchboxExtra("math/compare",">",{outputs:[["A>B","boolean"]],title:"A>B"});E.registerSearchboxExtra("math/compare","<",{outputs:[["A=",{outputs:[["A>=B","boolean"]],title:"A>=B"});E.registerSearchboxExtra("math/compare","<=",{outputs:[["A<=B","boolean"]],title:"A<=B"});a.values="> < == != <= >= || &&".split(" ");a["@OP"]={type:"enum",title:"operation",values:a.values};a.title="Condition";a.desc="evaluates condition between A and B";a.prototype.getTitle=function(){return"A "+this.properties.OP+" B"};a.prototype.onExecute=function(){var a=this.getInputData(0);void 0===a?a=this.properties.A:this.properties.A=a;var b=this.getInputData(1); -void 0===b?b=this.properties.B:this.properties.B=b;var d=!0;switch(this.properties.OP){case ">":d=a>b;break;case "<":d=a=":d=a>=b;break;case "||":d=a||b;break;case "&&":d=a&&b}this.setOutputData(0,d);this.setOutputData(1,!d)};E.registerNodeType("math/condition",a);b.title="Accumulate";b.desc="Increments a value every time";b.prototype.onExecute=function(){null===this.properties.value&&(this.properties.value=0);var a= -this.getInputData(0);this.properties.value=null!==a?this.properties.value+a:this.properties.value+this.properties.increment;this.setOutputData(0,this.properties.value)};E.registerNodeType("math/accumulate",b);d.title="Trigonometry";d.desc="Sin Cos Tan";d.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.amplitude,d=this.findInputSlot("amplitude");-1!=d&&(b=this.getInputData(d));var c=this.properties.offset,d=this.findInputSlot("offset");-1!=d&&(c=this.getInputData(d)); -for(var d=0,g=this.outputs.length;dXY";f.desc="vector 2 to components";f.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]))};E.registerNodeType("math3d/vec2-to-xy",f);r.title="XY->Vec2";r.desc="components to vector2";r.prototype.onExecute= -function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this._data;d[0]=a;d[1]=b;this.setOutputData(0,d)};E.registerNodeType("math3d/xy-to-vec2",r);x.title="Vec3->XYZ";x.desc="vector 3 to components";x.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]))};E.registerNodeType("math3d/vec3-to-xyz",x);I.title="XYZ->Vec3";I.desc="components to vector3"; -I.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this._data;c[0]=a;c[1]=b;c[2]=d;this.setOutputData(0,c)};E.registerNodeType("math3d/xyz-to-vec3",I);J.title="Vec4->XYZW";J.desc="vector 4 to components";J.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2, -a[2]),this.setOutputData(3,a[3]))};E.registerNodeType("math3d/vec4-to-xyzw",J);K.title="XYZW->Vec4";K.desc="components to vector4";K.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this.getInputData(3);null==c&&(c=this.properties.w);var g=this._data;g[0]=a;g[1]=b;g[2]=d;g[3]=c;this.setOutputData(0,g)};E.registerNodeType("math3d/xyzw-to-vec4", -K)})(this); -(function(z){function c(){this.addInput("T","vec3");this.addInput("R","vec3");this.addInput("S","vec3");this.addOutput("mat4","mat4");this.properties={T:[0,0,0],R:[0,0,0],S:[1,1,1],R_in_degrees:!0};this._result=mat4.create();this._must_update=!0}function m(){this.addInput("A","number,vec3");this.addInput("B","number,vec3");this.addOutput("=","number,vec3");this.addProperty("OP","+","enum",{values:m.values});this._result=vec3.create()}function n(){this.addInput("in","vec3");this.addInput("f","number"); -this.addOutput("out","vec3");this.properties={f:1};this._data=new Float32Array(3)}function e(){this.addInput("in","vec3");this.addOutput("out","number")}function w(){this.addInput("in","vec3");this.addOutput("out","vec3");this._data=new Float32Array(3)}function k(){this.addInput("A","vec3");this.addInput("B","vec3");this.addInput("f","vec3");this.addOutput("out","vec3");this.properties={f:0.5};this._data=new Float32Array(3)}function C(){this.addInput("A","vec3");this.addInput("B","vec3");this.addOutput("out", -"number")}var v=z.LiteGraph;c.title="mat4";c.temp_quat=new Float32Array([0,0,0,1]);c.temp_mat4=new Float32Array(16);c.temp_vec3=new Float32Array(3);c.prototype.onPropertyChanged=function(c,e){this._must_update=!0};c.prototype.onExecute=function(){var e=this._result,k=c.temp_quat,n=c.temp_mat4,h=c.temp_vec3,m=this.getInputData(0),B=this.getInputData(1),u=this.getInputData(2);if(this._must_update||m||B||u)m=m||this.properties.T,B=B||this.properties.R,u=u||this.properties.S,mat4.identity(e),mat4.translate(e, -e,m),this.properties.R_in_degrees?(h.set(B),vec3.scale(h,h,DEG2RAD),quat.fromEuler(k,h)):quat.fromEuler(k,B),mat4.fromQuat(n,k),mat4.multiply(e,e,n),mat4.scale(e,e,u);this.setOutputData(0,e)};v.registerNodeType("math3d/mat4",c);m.values="+ - * / % ^ max min dot cross".split(" ");v.registerSearchboxExtra("math3d/operation","CROSS()",{properties:{OP:"cross"},title:"CROSS()"});v.registerSearchboxExtra("math3d/operation","DOT()",{properties:{OP:"dot"},title:"DOT()"});m.title="Operation";m.desc="Easy math 3D operators"; -m["@OP"]={type:"enum",title:"operation",values:m.values};m.size=[100,60];m.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"};m.prototype.onExecute=function(){var c=this.getInputData(0),e=this.getInputData(1);if(null!=c&&null!=e){c.constructor===Number&&(c=[c,c,c]);e.constructor===Number&&(e=[e,e,e]);var k=this._result;switch(this.properties.OP){case "+":k=vec3.add(k,c,e);break;case "-":k=vec3.sub(k,c,e); -break;case "x":case "X":case "*":k=vec3.mul(k,c,e);break;case "/":k=vec3.div(k,c,e);break;case "%":k[0]=c[0]%e[0];k[1]=c[1]%e[1];k[2]=c[2]%e[2];break;case "^":k[0]=Math.pow(c[0],e[0]);k[1]=Math.pow(c[1],e[1]);k[2]=Math.pow(c[2],e[2]);break;case "max":k[0]=Math.max(c[0],e[0]);k[1]=Math.max(c[1],e[1]);k[2]=Math.max(c[2],e[2]);break;case "min":k[0]=Math.min(c[0],e[0]),k[1]=Math.min(c[1],e[1]),k[2]=Math.min(c[2],e[2]);case "dot":k=vec3.dot(c,e);break;case "cross":vec3.cross(k,c,e);break;default:console.warn("Unknown operation: "+ -this.properties.OP)}this.setOutputData(0,k)}};m.prototype.onDrawBackground=function(c){this.flags.collapsed||(c.font="40px Arial",c.fillStyle="#666",c.textAlign="center",c.fillText(this.properties.OP,0.5*this.size[0],0.5*(this.size[1]+v.NODE_TITLE_HEIGHT)),c.textAlign="left")};v.registerNodeType("math3d/operation",m);n.title="vec3_scale";n.desc="scales the components of a vec3";n.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null==e&&(e=this.properties.f); -var k=this._data;k[0]=c[0]*e;k[1]=c[1]*e;k[2]=c[2]*e;this.setOutputData(0,k)}};v.registerNodeType("math3d/vec3-scale",n);e.title="vec3_length";e.desc="returns the module of a vector";e.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(c=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),this.setOutputData(0,c))};v.registerNodeType("math3d/vec3-length",e);w.title="vec3_normalize";w.desc="returns the vector normalized";w.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e= -Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),k=this._data;k[0]=c[0]/e;k[1]=c[1]/e;k[2]=c[2]/e;this.setOutputData(0,k)}};v.registerNodeType("math3d/vec3-normalize",w);k.title="vec3_lerp";k.desc="returns the interpolated vector";k.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);if(null!=e){var k=this.getInputOrProperty("f"),h=this._data;h[0]=c[0]*(1-k)+e[0]*k;h[1]=c[1]*(1-k)+e[1]*k;h[2]=c[2]*(1-k)+e[2]*k;this.setOutputData(0,h)}}};v.registerNodeType("math3d/vec3-lerp", -k);C.title="vec3_dot";C.desc="returns the dot product";C.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null!=e&&this.setOutputData(0,c[0]*e[0]+c[1]*e[1]+c[2]*e[2])}};v.registerNodeType("math3d/vec3-dot",C);z.glMatrix?(z=function(){this.addOutput("quat","quat");this.properties={x:0,y:0,z:0,w:1,normalize:!1};this._value=quat.create()},z.title="Quaternion",z.desc="quaternion",z.prototype.onExecute=function(){this._value[0]=this.getInputOrProperty("x"); -this._value[1]=this.getInputOrProperty("y");this._value[2]=this.getInputOrProperty("z");this._value[3]=this.getInputOrProperty("w");this.properties.normalize&&quat.normalize(this._value,this._value);this.setOutputData(0,this._value)},z.prototype.onGetInputs=function(){return[["x","number"],["y","number"],["z","number"],["w","number"]]},v.registerNodeType("math3d/quaternion",z),z=function(){this.addInputs([["degrees","number"],["axis","vec3"]]);this.addOutput("quat","quat");this.properties={angle:90, -axis:vec3.fromValues(0,1,0)};this._value=quat.create()},z.title="Rotation",z.desc="quaternion rotation",z.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.angle);var e=this.getInputData(1);null==e&&(e=this.properties.axis);c=quat.setAxisAngle(this._value,e,0.0174532925*c);this.setOutputData(0,c)},v.registerNodeType("math3d/rotation",z),z=function(){this.addInput("euler","vec3");this.addOutput("quat","quat");this.properties={euler:[0,0,0],use_yaw_pitch_roll:!1}; -this._degs=vec3.create();this._value=quat.create()},z.title="Euler->Quat",z.desc="Converts euler angles (in degrees) to quaternion",z.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.euler);vec3.scale(this._degs,c,DEG2RAD);this.properties.use_yaw_pitch_roll&&(this._degs=[this._degs[2],this._degs[0],this._degs[1]]);c=quat.fromEuler(this._value,this._degs);this.setOutputData(0,c)},v.registerNodeType("math3d/euler_to_quat",z),z=function(){this.addInput(["quat","quat"]); -this.addOutput("euler","vec3");this._value=vec3.create()},z.title="Euler->Quat",z.desc="Converts rotX,rotY,rotZ in degrees to quat",z.prototype.onExecute=function(){var c=this.getInputData(0);c&&(quat.toEuler(this._value,c),vec3.scale(this._value,this._value,DEG2RAD),this.setOutputData(0,this._value))},v.registerNodeType("math3d/quat_to_euler",z),z=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},z.title="Rot. Vec3",z.desc= -"rotate a point",z.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.vec);var e=this.getInputData(1);null==e?this.setOutputData(c):this.setOutputData(0,vec3.transformQuat(vec3.create(),c,e))},v.registerNodeType("math3d/rotate_vec3",z),z=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},z.title="Mult. Quat",z.desc="rotate quaternion",z.prototype.onExecute=function(){var c=this.getInputData(0);if(null!= -c){var e=this.getInputData(1);null!=e&&(c=quat.multiply(this._value,c,e),this.setOutputData(0,c))}},v.registerNodeType("math3d/mult-quat",z),z=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp","quat");this.addProperty("factor",0.5);this._value=quat.create()},z.title="Quat Slerp",z.desc="quaternion spherical interpolation",z.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);if(null!=e){var k=this.properties.factor; -null!=this.getInputData(2)&&(k=this.getInputData(2));c=quat.slerp(this._value,c,e,k);this.setOutputData(0,c)}}},v.registerNodeType("math3d/quat-slerp",z),z=function(){this.addInput("vec3","vec3");this.addOutput("remap","vec3");this.addOutput("clamped","vec3");this.properties={clamp:!0,range_min:[-1,-1,0],range_max:[1,1,0],target_min:[-1,-1,0],target_max:[1,1,0]};this._value=vec3.create();this._clamped=vec3.create()},z.title="Remap Range",z.desc="remap a 3D range",z.prototype.onExecute=function(){var c= -this.getInputData(0);c&&this._value.set(c);for(var c=this.properties.range_min,e=this.properties.range_max,k=this.properties.target_min,h=this.properties.target_max,n=0;3>n;++n){var m=e[n]-c[n];this._clamped[n]=Math.clamp(this._value[n],c[n],e[n]);0==m?this._value[n]=0.5*(k[n]+h[n]):(m=(this._value[n]-c[n])/m,this.properties.clamp&&(m=Math.clamp(m,0,1)),this._value[n]=k[n]+m*(h[n]-k[n]))}this.setOutputData(0,this._value);this.setOutputData(1,this._clamped)},v.registerNodeType("math3d/remap_range", -z)):console.warn("No glmatrix found, some Math3D nodes may not work")})(this); -(function(z){function c(c,e){return c==e}function m(){this.addInput("","string");this.addOutput("table","table");this.addOutput("rows","number");this.addProperty("value","");this.addProperty("separator",",");this._table=null}z=z.LiteGraph;z.wrapFunctionAsNode("string/toString",c,[""],"String");z.wrapFunctionAsNode("string/compare",c,["string","string"],"boolean");z.wrapFunctionAsNode("string/concatenate",function(c,e){return void 0===c?e:void 0===e?c:c+e},["string","string"],"string");z.wrapFunctionAsNode("string/contains", -function(c,e){return void 0===c||void 0===e?!1:-1!=c.indexOf(e)},["string","string"],"boolean");z.wrapFunctionAsNode("string/toUpperCase",function(c){return null!=c&&c.constructor===String?c.toUpperCase():c},["string"],"string");z.wrapFunctionAsNode("string/split",function(c,e){null==e&&(e=this.properties.separator);if(null==c)return[];if(c.constructor===String)return c.split(e||" ");if(c.constructor===Array){for(var m=[],k=0;ke;++e){var h=this.getInputData(e);if(null!=h){var k=this.values[e];k.push(h);k.length>c[0]&&k.shift()}}}};c.prototype.onDrawBackground=function(e){if(!this.flags.collapsed){var h=this.size,k=0.5*h[1]/ -this.properties.scale,n=c.colors,q=0.5*h[1];e.fillStyle="#000";e.fillRect(0,0,h[0],h[1]);e.strokeStyle="#555";e.beginPath();e.moveTo(0,q);e.lineTo(h[0],q);e.stroke();if(this.inputs)for(var p=0;4>p;++p){var l=this.values[p];if(this.inputs[p]&&this.inputs[p].link){e.strokeStyle=n[p];e.beginPath();var a=l[0]*k*-1+q;e.moveTo(0,Math.clamp(a,0,h[1]));for(var b=1;be&&(e=0);if(0!=c.length){var h=[0,0,0];if(0==e)h=c[0];else if(1==e)h=c[c.length-1];else{var k= -(c.length-1)*e,e=c[Math.floor(k)],c=c[Math.floor(k)+1],k=k-Math.floor(k);h[0]=e[0]*(1-k)+c[0]*k;h[1]=e[1]*(1-k)+c[1]*k;h[2]=e[2]*(1-k)+c[2]*k}for(var q in h)h[q]/=255;this.boxcolor=colorToString(h);this.setOutputData(0,h)}};h.registerNodeType("color/palette",n);e.title="Frame";e.desc="Frame viewerew";e.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}];e.prototype.onDrawBackground=function(c){this.frame&&!this.flags.collapsed&&c.drawImage(this.frame, -0,0,this.size[0],this.size[1])};e.prototype.onExecute=function(){this.frame=this.getInputData(0);this.setDirtyCanvas(!0)};e.prototype.onWidget=function(c,e){if("resize"==e.name&&this.frame){var h=this.frame.width,k=this.frame.height;h||null==this.frame.videoWidth||(h=this.frame.videoWidth,k=this.frame.videoHeight);h&&k&&(this.size=[h,k]);this.setDirtyCanvas(!0,!0)}else"view"==e.name&&this.show()};e.prototype.show=function(){showElement&&this.frame&&showElement(this.frame)};h.registerNodeType("graphics/frame", -e);w.title="Image fade";w.desc="Fades between images";w.widgets=[{name:"resizeA",text:"Resize to A",type:"button"},{name:"resizeB",text:"Resize to B",type:"button"}];w.prototype.onAdded=function(){this.createCanvas();var c=this.canvas.getContext("2d");c.fillStyle="#000";c.fillRect(0,0,this.properties.width,this.properties.height)};w.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};w.prototype.onExecute= -function(){var c=this.canvas.getContext("2d");this.canvas.width=this.canvas.width;var e=this.getInputData(0);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);e=this.getInputData(2);null==e?e=this.properties.fade:this.properties.fade=e;c.globalAlpha=e;e=this.getInputData(1);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);c.globalAlpha=1;this.setOutputData(0,this.canvas);this.setDirtyCanvas(!0)};h.registerNodeType("graphics/imagefade",w);k.title="Crop";k.desc="Crop Image"; -k.prototype.onAdded=function(){this.createCanvas()};k.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};k.prototype.onExecute=function(){var c=this.getInputData(0);c&&(c.width?(this.canvas.getContext("2d").drawImage(c,-this.properties.x,-this.properties.y,c.width*this.properties.scale,c.height*this.properties.scale),this.setOutputData(0,this.canvas)):this.setOutputData(0,null))};k.prototype.onDrawBackground= -function(c){this.flags.collapsed||this.canvas&&c.drawImage(this.canvas,0,0,this.canvas.width,this.canvas.height,0,0,this.size[0],this.size[1])};k.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"scale"==c?(this.properties[c]=parseFloat(e),0==this.properties[c]&&(console.error("Error in scale"),this.properties[c]=1)):this.properties[c]=parseInt(e);this.createCanvas();return!0};h.registerNodeType("graphics/cropImage",k);C.title="Canvas";C.desc="Canvas to render stuff";C.prototype.onExecute= -function(){var c=this.canvas,e=this.properties.width|0,h=this.properties.height|0;c.width!=e&&(c.width=e);c.height!=h&&(c.height=h);this.properties.autoclear&&this.ctx.clearRect(0,0,c.width,c.height);this.setOutputData(0,c)};C.prototype.onAction=function(c,e){"clear"==c&&this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)};h.registerNodeType("graphics/canvas",C);v.title="DrawImage";v.desc="Draws image into a canvas";v.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e= -this.getInputOrProperty("img");if(e){var h=this.getInputOrProperty("x"),k=this.getInputOrProperty("y");c.getContext("2d").drawImage(e,h,k)}}};h.registerNodeType("graphics/drawImage",v);s.title="DrawRectangle";s.desc="Draws rectangle in canvas";s.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e=this.getInputOrProperty("x"),h=this.getInputOrProperty("y"),k=this.getInputOrProperty("w"),q=this.getInputOrProperty("h");c.getContext("2d").fillRect(e,h,k,q)}};h.registerNodeType("graphics/drawRectangle", -s);D.title="Video";D.desc="Video playback";D.widgets=[{name:"play",text:"PLAY",type:"minibutton"},{name:"stop",text:"STOP",type:"minibutton"},{name:"demo",text:"Demo video",type:"button"},{name:"mute",text:"Mute video",type:"button"}];D.prototype.onExecute=function(){if(this.properties.url&&(this.properties.url!=this._video_url&&this.loadVideo(this.properties.url),this._video&&0!=this._video.width)){var c=this.getInputData(0);c&&0<=c&&1>=c&&(this._video.currentTime=c*this._video.duration,this._video.pause()); -this._video.dirty=!0;this.setOutputData(0,this._video);this.setOutputData(1,this._video.currentTime);this.setOutputData(2,this._video.duration);this.setDirtyCanvas(!0)}};D.prototype.onStart=function(){this.play()};D.prototype.onStop=function(){this.stop()};D.prototype.loadVideo=function(c){this._video_url=c;var e=c.substr(0,10).indexOf(":"),k="";-1!=e&&(k=c.substr(0,e));e="";k&&(e=c.substr(0,c.indexOf("/",k.length+3)),e=e.substr(k.length+3));this.properties.use_proxy&&k&&h.proxy&&e!=location.host&& -(c=h.proxy+c.substr(c.indexOf(":")+3));this._video=document.createElement("video");this._video.src=c;this._video.type="type=video/mp4";this._video.muted=!0;this._video.autoplay=!0;var n=this;this._video.addEventListener("loadedmetadata",function(c){console.log("Duration: "+this.duration+" seconds");console.log("Size: "+this.videoWidth+","+this.videoHeight);n.setDirtyCanvas(!0);this.width=this.videoWidth;this.height=this.videoHeight});this._video.addEventListener("progress",function(c){console.log("video loading...")}); -this._video.addEventListener("error",function(c){console.error("Error loading video: "+this.src);if(this.error)switch(this.error.code){case this.error.MEDIA_ERR_ABORTED:console.error("You stopped the video.");break;case this.error.MEDIA_ERR_NETWORK:console.error("Network error - please try again later.");break;case this.error.MEDIA_ERR_DECODE:console.error("Video is broken..");break;case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED:console.error("Sorry, your browser can't play this video.")}});this._video.addEventListener("ended", -function(c){console.log("Video Ended.");this.play()})};D.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"url"==c&&""!=e&&this.loadVideo(e);return!0};D.prototype.play=function(){this._video&&this._video.videoWidth&&this._video.play()};D.prototype.playPause=function(){this._video&&(this._video.paused?this.play():this.pause())};D.prototype.stop=function(){this._video&&(this._video.pause(),this._video.currentTime=0)};D.prototype.pause=function(){this._video&&(console.log("Video paused"), -this._video.pause())};D.prototype.onWidget=function(c,e){};h.registerNodeType("graphics/video",D);A.title="Webcam";A.desc="Webcam image";A.is_webcam_open=!1;A.prototype.openStream=function(){function c(h){console.log("Webcam rejected",h);e._webcam_stream=!1;A.is_webcam_open=!1;e.boxcolor="red";e.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](c); -var e=this}};A.prototype.closeStream=function(){if(this._webcam_stream){var c=this._webcam_stream.getTracks();if(c.length)for(var e=0;e=this.size[1]||!this.properties.show|| -!this._video||(c.save(),c.drawImage(this._video,0,0,this.size[0],this.size[1]),c.restore())};A.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["stream_ready",h.EVENT],["stream_closed",h.EVENT],["stream_error",h.EVENT]]};h.registerNodeType("graphics/webcam",A)})(this); -(function(z){function c(){this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",filter:!0};this.size=[c.image_preview_size,c.image_preview_size]}function m(){this.addInput("Texture","Texture");this.properties={flipY:!1};this.size=[c.image_preview_size,c.image_preview_size]}function n(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",generate_mipmaps:!1}}function e(){this.addInput("Texture", -"Texture");this.addInput("TextureB","Texture");this.addInput("value","number");this.addOutput("Texture","Texture");this.help="

pixelcode must be vec3, uvcode must be vec2, is optional

\t\t

uv: tex. coords

color: texture colorB: textureB

time: scene time value: input value

For multiline you must type: result = ...

";this.properties={value:1,pixelcode:"color + colorB * value",uvcode:"",precision:c.DEFAULT}; -this.has_error=!1}function w(){this.addOutput("out","Texture");this.properties={code:"",u_value:1,u_color:[1,1,1,1],width:512,height:512,precision:c.DEFAULT};this.properties.code=w.pixel_shader;this._uniforms={u_value:1,u_color:vec4.create(),in_texture:0,texSize:vec2.create(),time:0}}function k(){this.addInput("in","Texture");this.addInput("scale","vec2");this.addInput("offset","vec2");this.addOutput("out","Texture");this.properties={offset:vec2.fromValues(0,0),scale:vec2.fromValues(1,1),precision:c.DEFAULT}} -function C(){this.addInput("in","Texture");this.addInput("warp","Texture");this.addInput("factor","number");this.addOutput("out","Texture");this.properties={factor:0.01,scale:[1,1],offset:[0,0],precision:c.DEFAULT};this._uniforms={u_texture:0,u_textureB:1,u_factor:1,u_scale:vec2.create(),u_offset:vec2.create()}}function v(){this.addInput("Texture","Texture");this.properties={additive:!1,antialiasing:!1,filter:!0,disable_alpha:!1,gamma:1,viewport:[0,0,1,1]};this.size[0]=130}function s(){this.addInput("Texture", -"Texture");this.addOutput("","Texture");this.properties={size:0,generate_mipmaps:!1,precision:c.DEFAULT}}function D(){this.addInput("Texture","Texture");this.addOutput("","Texture");this.properties={iterations:1,generate_mipmaps:!1,precision:c.DEFAULT}}function A(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("avg","vec4");this.addOutput("lum","number");this.properties={use_previous_frame:!0,high_quality:!1};this._uniforms={u_texture:0,u_mipmap_offset:0};this._luminance= -new Float32Array(4)}function h(){this.addInput("Texture","Texture");this.addOutput("min_t","Texture");this.addOutput("max_t","Texture");this.addOutput("min","vec4");this.addOutput("max","vec4");this.properties={mode:"max",use_previous_frame:!0};this._uniforms={u_texture:0};this._max=new Float32Array(4);this._min=new Float32Array(4);this._textures_chain=[]}function y(){this.addInput("in","Texture");this.addInput("factor","Number");this.addOutput("out","Texture");this.properties={factor:0.5};this._uniforms= -{u_texture:0,u_textureB:1,u_factor:this.properties.factor}}function B(){this.addInput("in","Texture");this.addOutput("avg","Texture");this.addOutput("array","Texture");this.properties={samples:64,frames_interval:1};this._uniforms={u_texture:0,u_textureB:1,u_samples:this.properties.samples,u_isamples:1/this.properties.samples};this.frame=0}function u(){this.addInput("Image","image");this.addOutput("","Texture");this.properties={}}function G(){this.addInput("Texture","Texture");this.addInput("LUT", -"Texture");this.addInput("Intensity","number");this.addOutput("","Texture");this.properties={enabled:!0,intensity:1,precision:c.DEFAULT,texture:null};G._shader||(G._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,G.pixel_shader))}function q(){this.addInput("Texture","Texture");this.addInput("Atlas","Texture");this.addOutput("","Texture");this.properties={enabled:!0,num_row_symbols:4,symbol_size:16,brightness:1,colorize:!1,filter:!1,invert:!1,precision:c.DEFAULT,texture:null};q._shader||(q._shader= -new GL.Shader(Shader.SCREEN_VERTEX_SHADER,q.pixel_shader));this._uniforms={u_texture:0,u_textureB:1,u_row_simbols:4,u_simbol_size:16,u_res:vec2.create()}}function p(){this.addInput("Texture","Texture");this.addOutput("R","Texture");this.addOutput("G","Texture");this.addOutput("B","Texture");this.addOutput("A","Texture");p._shader||(p._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,p.pixel_shader))}function l(){this.addInput("R","Texture");this.addInput("G","Texture");this.addInput("B","Texture"); -this.addInput("A","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,R:1,G:1,B:1,A:1};this._color=vec4.create();this._uniforms={u_textureR:0,u_textureG:1,u_textureB:2,u_textureA:3,u_color:this._color}}function a(){this.addOutput("Texture","Texture");this._tex_color=vec4.create();this.properties={color:vec4.create(),precision:c.DEFAULT}}function b(){this.addInput("A","color");this.addInput("B","color");this.addOutput("Texture","Texture");this.properties={angle:0,scale:1, -A:[0,0,0],B:[1,1,1],texture_size:32};b._shader||(b._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader));this._uniforms={u_angle:0,u_colorA:vec3.create(),u_colorB:vec3.create()}}function d(){this.addInput("A","Texture");this.addInput("B","Texture");this.addInput("Mixer","Texture");this.addOutput("Texture","Texture");this.properties={factor:0.5,size_from_biggest:!0,invert:!1,precision:c.DEFAULT};this._uniforms={u_textureA:0,u_textureB:1,u_textureMix:2,u_mix:vec4.create()}}function g(){this.addInput("Tex.", -"Texture");this.addOutput("Edges","Texture");this.properties={invert:!0,threshold:!1,factor:1,precision:c.DEFAULT};g._shader||(g._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,g.pixel_shader))}function f(){this.addInput("Texture","Texture");this.addInput("Distance","number");this.addInput("Range","number");this.addOutput("Texture","Texture");this.properties={distance:100,range:50,only_depth:!1,high_precision:!1};this._uniforms={u_texture:0,u_distance:100,u_range:50,u_camera_planes:null}}function r(){this.addInput("Texture", -"Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,invert:!1};this._uniforms={u_texture:0,u_camera_planes:null,u_ires:vec2.create()}}function x(){this.addInput("Texture","Texture");this.addInput("Iterations","number");this.addInput("Intensity","number");this.addOutput("Blurred","Texture");this.properties={intensity:1,iterations:1,preserve_aspect:!1,scale:[1,1],precision:c.DEFAULT}}function I(){this.addInput("in","Texture");this.addInput("dirt","Texture");this.addOutput("out", -"Texture");this.addOutput("glow","Texture");this.properties={enabled:!0,intensity:1,persistence:0.99,iterations:16,threshold:0,scale:1,dirt_factor:0.5,precision:c.DEFAULT};this._textures=[];this._uniforms={u_intensity:1,u_texture:0,u_glow_texture:1,u_threshold:0,u_texel_size:vec2.create()}}function J(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={intensity:1,radius:5}}function K(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties= -{sigma:1.4,k:1.6,p:21.7,epsilon:79,phi:0.017}}function E(){this.addOutput("Webcam","Texture");this.properties={texture_name:"",facingMode:"user"};this.boxcolor="black";this.version=0}function t(){this.addInput("in","Texture");this.addInput("f","number");this.addOutput("out","Texture");this.properties={enabled:!0,factor:1,precision:c.LOW};this._uniforms={u_texture:0,u_factor:1}}function Q(){this.addInput("in","");this.properties={precision:c.LOW,width:0,height:0,channels:1};this.addOutput("out","Texture")} -function H(){this.addInput("in","Texture");this.addOutput("out","Texture");this.properties={precision:c.LOW,split_channels:!1};this._values=new Uint8Array(1024);this._values.fill(255);this._curve_texture=null;this._uniforms={u_texture:0,u_curve:1,u_range:1};this._must_update=!0;this._points={RGB:[[0,0],[1,1]],R:[[0,0],[1,1]],G:[[0,0],[1,1]],B:[[0,0],[1,1]]};this.curve_editor=null;this.addWidget("toggle","Split Channels",!1,"split_channels");this.addWidget("combo","Channel","RGB",{values:["RGB","R", -"G","B"]});this.curve_offset=68;this.size=[240,160]}function O(){this.addInput("in","Texture");this.addInput("exp","number");this.addOutput("out","Texture");this.properties={exposition:1,precision:c.LOW};this._uniforms={u_texture:0,u_exposition:1}}function L(){this.addInput("in","Texture");this.addInput("avg","number,Texture");this.addOutput("out","Texture");this.properties={enabled:!0,scale:1,gamma:1,average_lum:1,lum_white:1,precision:c.LOW};this._uniforms={u_texture:0,u_lumwhite2:1,u_igamma:1, -u_scale:1,u_average_lum:1}}function N(){this.addOutput("out","Texture");this.properties={width:512,height:512,seed:0,persistence:0.1,octaves:8,scale:1,offset:[0,0],amplitude:1,precision:c.DEFAULT};this._key=0;this._texture=null;this._uniforms={u_persistence:0.1,u_seed:0,u_offset:vec2.create(),u_scale:1,u_viewport:vec2.create()}}function M(){this.addInput("v");this.addOutput("out","Texture");this.properties={code:M.default_code,width:512,height:512,clear:!0,precision:c.DEFAULT,use_html_canvas:!1}; -this._temp_texture=this._func=null;this.compileCode()}function P(){this.addInput("in","Texture");this.addOutput("out","Texture");this.properties={key_color:vec3.fromValues(0,1,0),threshold:0.8,slope:0.2,precision:c.DEFAULT}}function R(){this.addInput("in","texture");this.addInput("yaw","number");this.addOutput("out","texture");this.properties={yaw:0}}var F=z.LiteGraph,T=z.LGraphCanvas;z.LGraphTexture=null;"undefined"!=typeof GL&&(T.link_type_colors.Texture="#987",z.LGraphTexture=c,c.title="Texture", -c.desc="Texture",c.widgets_info={name:{widget:"texture"},filter:{widget:"checkbox"}},c.loadTextureCallback=null,c.image_preview_size=256,c.PASS_THROUGH=1,c.COPY=2,c.LOW=3,c.HIGH=4,c.REUSE=5,c.DEFAULT=2,c.MODE_VALUES={"pass through":c.PASS_THROUGH,copy:c.COPY,low:c.LOW,high:c.HIGH,reuse:c.REUSE,"default":c.DEFAULT},c.getTexturesContainer=function(){return gl.textures},c.loadTexture=function(a,b){b=b||{};var d=a;"http://"==d.substr(0,7)&&F.proxy&&(d=F.proxy+d.substr(7));return c.getTexturesContainer()[a]= -GL.Texture.fromURL(d,b)},c.getTexture=function(a){var b=this.getTexturesContainer();if(!b)throw"Cannot load texture, container of textures not found";b=b[a];return!b&&a&&":"!=a[0]?this.loadTexture(a):b},c.getTargetTexture=function(a,b,d){if(!a)throw"LGraphTexture.getTargetTexture expects a reference texture";var g=null;switch(d){case c.LOW:g=gl.UNSIGNED_BYTE;break;case c.HIGH:g=gl.HIGH_PRECISION_FORMAT;break;case c.REUSE:return a;default:g=a?a.type:gl.UNSIGNED_BYTE}b&&b.width==a.width&&b.height== -a.height&&b.type==g||(b=new GL.Texture(a.width,a.height,{type:g,format:gl.RGBA,filter:gl.LINEAR}));return b},c.getTextureType=function(a,b){var d=b?b.type:gl.UNSIGNED_BYTE;switch(a){case c.HIGH:d=gl.HIGH_PRECISION_FORMAT;break;case c.LOW:d=gl.UNSIGNED_BYTE}return d},c.getWhiteTexture=function(){return this._white_texture?this._white_texture:this._white_texture=GL.Texture.fromMemory(1,1,[255,255,255,255],{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.getNoiseTexture=function(){if(this._noise_texture)return this._noise_texture; -for(var a=new Uint8Array(1048576),b=0;1048576>b;++b)a[b]=255*Math.random();return this._noise_texture=a=GL.Texture.fromMemory(512,512,a,{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.prototype.onDropFile=function(a,b,d){if(a){var c=null;"string"==typeof a?c=GL.Texture.fromURL(a):-1!=b.toLowerCase().indexOf(".dds")?c=GL.Texture.fromDDSInMemory(a):(a=new Blob([d]),a=URL.createObjectURL(a),c=GL.Texture.fromURL(a));this._drop_texture=c;this.properties.name=b}else this._drop_texture=null,this.properties.name= -""},c.prototype.getExtraMenuOptions=function(a){var b=this;if(this._drop_texture)return[{content:"Clear",callback:function(){b._drop_texture=null;b.properties.name=""}}]},c.prototype.onExecute=function(){var a=null;this.isOutputConnected(1)&&(a=this.getInputData(0));!a&&this._drop_texture&&(a=this._drop_texture);!a&&this.properties.name&&(a=c.getTexture(this.properties.name));if(a){this._last_tex=a;!1===this.properties.filter?a.setParameter(gl.TEXTURE_MAG_FILTER,gl.NEAREST):a.setParameter(gl.TEXTURE_MAG_FILTER, -gl.LINEAR);this.setOutputData(0,a);this.setOutputData(1,a.fullpath||a.filename);for(var b=2;b= -this.size[1]))if(this._drop_texture&&a.webgl)a.drawImage(this._drop_texture,0,0,this.size[0],this.size[1]);else{if(this._last_preview_tex!=this._last_tex)if(a.webgl)this._canvas=this._last_tex;else{var b=c.generateLowResTexturePreview(this._last_tex);if(!b)return;this._last_preview_tex=this._last_tex;this._canvas=cloneCanvas(b)}this._canvas&&(a.save(),a.webgl||(a.translate(0,this.size[1]),a.scale(1,-1)),a.drawImage(this._canvas,0,0,this.size[0],this.size[1]),a.restore())}},c.generateLowResTexturePreview= -function(a){if(!a)return null;var b=c.image_preview_size,d=a;if(a.format==gl.DEPTH_COMPONENT)return null;if(a.width>b||a.height>b)d=this._preview_temp_tex,this._preview_temp_tex||(this._preview_temp_tex=d=new GL.Texture(b,b,{minFilter:gl.NEAREST})),a.copyTo(d);a=this._preview_canvas;a||(this._preview_canvas=a=createCanvas(b,b));d&&d.toCanvas(a);return a},c.prototype.getResources=function(a){this.properties.name&&(a[this.properties.name]=GL.Texture);return a},c.prototype.onGetInputs=function(){return[["in", -"Texture"]]},c.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["aspect","number"]]},c.replaceCode=function(a,b){return a.replace(/\{\{[a-zA-Z0-9_]*\}\}/g,function(a){a=a.replace(/[\{\}]/g,"");return b[a]||""})},F.registerNodeType("texture/texture",c),m.title="Preview",m.desc="Show a texture in the graph canvas",m.allow_preview=!1,m.prototype.onDrawBackground=function(a){if(!this.flags.collapsed&&(a.webgl||m.allow_preview)){var b=this.getInputData(0);if(b){var d=null, -d=!b.handle&&a.webgl?b:c.generateLowResTexturePreview(b);a.save();this.properties.flipY&&(a.translate(0,this.size[1]),a.scale(1,-1));a.drawImage(d,0,0,this.size[0],this.size[1]);a.restore()}}},F.registerNodeType("texture/preview",m),n.title="Save",n.desc="Save a texture in the repository",n.prototype.getPreviewTexture=function(){return this._texture},n.prototype.onExecute=function(){var a=this.getInputData(0);a&&(this.properties.generate_mipmaps&&(a.bind(0),a.setParameter(gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_LINEAR), -gl.generateMipmap(a.texture_type),a.unbind(0)),this.properties.name&&(c.storeTexture?c.storeTexture(this.properties.name,a):c.getTexturesContainer()[this.properties.name]=a),this._texture=a,this.setOutputData(0,a),this.setOutputData(1,this.properties.name))},F.registerNodeType("texture/save",n),e.widgets_info={uvcode:{widget:"code"},pixelcode:{widget:"code"},precision:{widget:"combo",values:c.MODE_VALUES}},e.title="Operation",e.desc="Texture shader operation",e.presets={},e.prototype.getExtraMenuOptions= -function(a){var b=this;return[{content:b.properties.show?"Hide Texture":"Show Texture",callback:function(){b.properties.show=!b.properties.show}}]},e.prototype.onPropertyChanged=function(){this.has_error=!1},e.prototype.onDrawBackground=function(a){this.flags.collapsed||20>=this.size[1]||!this.properties.show||!this._tex||this._tex.gl!=a||(a.save(),a.drawImage(this._tex,0,0,this.size[0],this.size[1]),a.restore())},e.prototype.onExecute=function(){var a=this.getInputData(0);if(this.isOutputConnected(0))if(this.properties.precision=== -c.PASS_THROUGH)this.setOutputData(0,a);else{var b=this.getInputData(1);if(this.properties.uvcode||this.properties.pixelcode){var d=512,g=512;a?(d=a.width,g=a.height):b&&(d=b.width,g=b.height);b||(b=GL.Texture.getWhiteTexture());var f=c.getTextureType(this.properties.precision,a);this._tex=a||this._tex?c.getTargetTexture(a||this._tex,this._tex,this.properties.precision):new GL.Texture(d,g,{type:f,format:gl.RGBA,filter:gl.LINEAR});f="";this.properties.uvcode&&(f="uv = "+this.properties.uvcode,-1!=this.properties.uvcode.indexOf(";")&& -(f=this.properties.uvcode));var l="";this.properties.pixelcode&&(l="result = "+this.properties.pixelcode,-1!=this.properties.pixelcode.indexOf(";")&&(l=this.properties.pixelcode));var h=this._shader;if(!(this.has_error||h&&this._shader_code==f+"|"+l)){var k=c.replaceCode(e.pixel_shader,{UV_CODE:f,PIXEL_CODE:l});try{h=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,k),this.boxcolor="#00FF00"}catch(q){GL.Shader.dumpErrorToConsole(q,Shader.SCREEN_VERTEX_SHADER,k);this.boxcolor="#FF0000";this.has_error=!0; -return}this._shader=h;this._shader_code=f+"|"+l}if(this._shader){var p=this.getInputData(2);null!=p?this.properties.value=p:p=parseFloat(this.properties.value);var r=this.graph.getTime();this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);gl.disable(gl.BLEND);a&&a.bind(0);b&&b.bind(1);var c=Mesh.getScreenQuad();h.uniforms({u_texture:0,u_textureB:1,value:p,texSize:[d,g],time:r}).draw(c)});this.setOutputData(0,this._tex)}}}},e.pixel_shader="precision highp float;\n\t\t\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec2 texSize;\n\t\tuniform float time;\n\t\tuniform float value;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord;\n\t\t\t{{UV_CODE}};\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\tvec3 color = color4.rgb;\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\tvec3 colorB = color4B.rgb;\n\t\t\tvec3 result = color;\n\t\t\tfloat alpha = 1.0;\n\t\t\t{{PIXEL_CODE}};\n\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t}\n\t\t", -e.registerPreset=function(a,b){e.presets[a]=b},e.registerPreset("",""),e.registerPreset("bypass","color"),e.registerPreset("add","color + colorB * value"),e.registerPreset("substract","(color - colorB) * value"),e.registerPreset("mate","mix( color, colorB, color4B.a * value)"),e.registerPreset("invert","vec3(1.0) - color"),e.registerPreset("multiply","color * colorB * value"),e.registerPreset("divide","(color / colorB) / value"),e.registerPreset("difference","abs(color - colorB) * value"),e.registerPreset("max", -"max(color, colorB) * value"),e.registerPreset("min","min(color, colorB) * value"),e.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"),e.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"),e.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"),e.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"), -e.prototype.onInspect=function(a){var b=this;a.addCombo("Presets","",{values:Object.keys(e.presets),callback:function(d){var c=e.presets[d];c&&(b.setProperty("pixelcode",c),b.title=d,a.refresh())}})},F.registerNodeType("texture/operation",e),w.title="Shader",w.desc="Texture shader",w.widgets_info={code:{type:"code",lang:"glsl"},precision:{widget:"combo",values:c.MODE_VALUES}},w.prototype.onPropertyChanged=function(a,b){if("code"==a){var d=this.getShader();if(d){var c=d.uniformInfo;if(this.inputs)for(var g= -{},f=0;f lumaMax))\n\t\t\t\tcolor = vec4(rgbA, 1.0);\n\t\t\telse\n\t\t\t\tcolor = vec4(rgbB, 1.0);\n\t\t\tif(u_igamma != 1.0)\n\t\t\t\tcolor.xyz = pow( color.xyz, vec3(u_igamma) );\n\t\t\treturn color;\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\t\t}\n\t\t", -v.gamma_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_igamma;\n\t\tvoid main() {\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\t\t gl_FragColor = color;\n\t\t}\n\t\t",F.registerNodeType("texture/toviewport",v),s.title="Copy",s.desc="Copy Texture",s.widgets_info={size:{widget:"combo",values:[0,32,64,128,256,512,1024,2048]},precision:{widget:"combo", -values:c.MODE_VALUES}},s.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)){if(a){var b=a.width,d=a.height;0!=this.properties.size&&(d=b=this.properties.size);var g=this._temp_texture,f=a.type;this.properties.precision===c.LOW?f=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(f=gl.HIGH_PRECISION_FORMAT);g&&g.width==b&&g.height==d&&g.type==f||(g=gl.LINEAR,this.properties.generate_mipmaps&&isPowerOfTwo(b)&&isPowerOfTwo(d)&&(g=gl.LINEAR_MIPMAP_LINEAR), -this._temp_texture=new GL.Texture(b,d,{type:f,format:gl.RGBA,minFilter:g,magFilter:gl.LINEAR}));a.copyTo(this._temp_texture);this.properties.generate_mipmaps&&(this._temp_texture.bind(0),gl.generateMipmap(this._temp_texture.texture_type),this._temp_texture.unbind(0))}this.setOutputData(0,this._temp_texture)}},F.registerNodeType("texture/copy",s),D.title="Downsample",D.desc="Downsample Texture",D.widgets_info={iterations:{type:"number",step:1,precision:0,min:0},precision:{widget:"combo",values:c.MODE_VALUES}}, -D.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)&&a&&a.texture_type===GL.TEXTURE_2D)if(1>this.properties.iterations)this.setOutputData(0,a);else{var b=D._shader;b||(D._shader=b=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,D.pixel_shader));var d=a.width|0,g=a.height|0,f=a.type;this.properties.precision===c.LOW?f=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(f=gl.HIGH_PRECISION_FORMAT);var e=this.properties.iterations||1,l=a,h= -null,k=[],a={type:f,format:a.format},f=vec2.create(),q={u_offset:f};this._texture&&GL.Texture.releaseTemporary(this._texture);for(var p=0;p>1||0;g=g>>1||0;h=GL.Texture.getTemporary(d,g,a);k.push(h);l.setParameter(GL.TEXTURE_MAG_FILTER,GL.NEAREST);l.copyTo(h,b,q);if(1==d&&1==g)break;l=h}this._texture=k.pop();for(p=0;p>=2,c++,1!=d););a.copyTo(this._textures_chain[0]);for(var c=1;c<=this._textures_chain.length;++c)a=this._textures_chain[c];var g=h._shader,f=this._uniforms;f.u_mipmap_offset=this.properties.mipmap_offset;gl.disable(gl.DEPTH_TEST); -gl.disable(gl.BLEND);this._temp_texture.drawTo(function(){a.toViewport(g,f)})}},h.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform mat4 u_samples_a;\n\t\tuniform mat4 u_samples_b;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_mipmap_offset;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t//random average\n\t\t\tfor(int i = 0; i < 4; ++i)\n\t\t\t\tfor(int j = 0; j < 4; ++j)\n\t\t\t\t{\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t}\n\t\t gl_FragColor = color * 0.03125;\n\t\t}\n\t\t", -y.title="Smooth",y.desc="Smooth texture over time",y.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){y._shader||(y._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,y.pixel_shader));var b=this._temp_texture;b&&b.type==a.type&&b.width==a.width&&b.height==a.height||(b={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(a.width,a.height,b),this._temp_texture2=new GL.Texture(a.width,a.height,b),a.copyTo(this._temp_texture2));var b= -this._temp_texture,d=this._temp_texture2,c=y._shader,g=this._uniforms;g.u_factor=1-this.getInputOrProperty("factor");gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);b.drawTo(function(){d.bind(1);a.toViewport(c,g)});this.setOutputData(0,b);this._temp_texture=d;this._temp_texture2=b}},y.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_factor;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tgl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\t\t}\n\t\t", -F.registerNodeType("texture/temporal_smooth",y),B.title="Lineal Avg Smooth",B.desc="Smooth texture linearly over time",B["@samples"]={type:"number",min:1,max:64,step:1,precision:1},B.prototype.getPreviewTexture=function(){return this._temp_texture2},B.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){B._shader||(B._shader_copy=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,B.pixel_shader_copy),B._shader_avg=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,B.pixel_shader_avg)); -var b=Math.clamp(this.properties.samples,0,64),d=this.frame,c=this.properties.frames_interval;if(0==c||0==d%c){d=this._temp_texture;d&&d.type==a.type&&d.width==b||(d={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(b,1,d),this._temp_texture2=new GL.Texture(b,1,d),this._temp_texture_out=new GL.Texture(1,1,d));var g=this._temp_texture,f=this._temp_texture2,e=B._shader_copy,l=B._shader_avg,h=this._uniforms;h.u_samples=b;h.u_isamples=1/b;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST); -g.drawTo(function(){f.bind(1);a.toViewport(e,h)});this._temp_texture_out.drawTo(function(){g.toViewport(l,h)});this.setOutputData(0,this._temp_texture_out);this._temp_texture=f;this._temp_texture2=g}else this.setOutputData(0,this._temp_texture_out);this.setOutputData(1,this._temp_texture2);this.frame++}},B.pixel_shader_copy="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tif( v_coord.x <= u_isamples )\n\t\t\t\tgl_FragColor = texture2D( u_texture, vec2(0.5) );\n\t\t\telse\n\t\t\t\tgl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\t\t}\n\t\t", -B.pixel_shader_avg="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform int u_samples;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\tfor(int i = 0; i < 64; ++i)\n\t\t\t{\n\t\t\t\tcolor += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\t\t\t\tif(i == (u_samples - 1))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tgl_FragColor = color * u_isamples;\n\t\t}\n\t\t",F.registerNodeType("texture/linear_avg_smooth", -B),u.title="Image to Texture",u.desc="Uploads an image to the GPU",u.prototype.onExecute=function(){var a=this.getInputData(0);if(a){var b=a.videoWidth||a.width,d=a.videoHeight||a.height;if(a.gltexture)this.setOutputData(0,a.gltexture);else{var c=this._temp_texture;c&&c.width==b&&c.height==d||(this._temp_texture=new GL.Texture(b,d,{format:gl.RGBA,filter:gl.LINEAR}));try{this._temp_texture.uploadImage(a)}catch(g){console.error("image comes from an unsafe location, cannot be uploaded to webgl: "+g); -return}this.setOutputData(0,this._temp_texture)}}},F.registerNodeType("texture/imageToTexture",u),G.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},G.title="LUT",G.desc="Apply LUT to Texture",G.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture)); -if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.bindTexture(gl.TEXTURE_2D,null);var d=this.properties.intensity;this.isInputConnected(2)&&(this.properties.intensity=d=this.getInputData(2));this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);this._tex.drawTo(function(){b.bind(1);a.toViewport(G._shader,{u_texture:0,u_textureB:1, -u_amount:d})});this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}},G.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_amount;\n\t\t\n\t\tvoid main() {\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\t\t\t mediump vec2 quad1;\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\t\t\t mediump vec2 quad2;\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\t\t\t highp vec2 texPos1;\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t highp vec2 texPos2;\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\t\t}\n\t\t", -F.registerNodeType("texture/LUT",G),q.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},q.title="Encode",q.desc="Apply a texture atlas to encode a texture",q.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture));if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D, -gl.TEXTURE_MAG_FILTER,this.properties.filter?gl.LINEAR:gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,this.properties.filter?gl.LINEAR:gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.bindTexture(gl.TEXTURE_2D,null);var d=this._uniforms;d.u_row_simbols=Math.floor(this.properties.num_row_symbols);d.u_symbol_size=this.properties.symbol_size;d.u_brightness=this.properties.brightness; -d.u_invert=this.properties.invert?1:0;d.u_colorize=this.properties.colorize?1:0;this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);d.u_res[0]=this._tex.width;d.u_res[1]=this._tex.height;this._tex.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.NEAREST);this._tex.drawTo(function(){b.bind(1);a.toViewport(q._shader,d)});this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}},q.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_row_simbols;\n\t\tuniform float u_symbol_size;\n\t\tuniform float u_brightness;\n\t\tuniform float u_invert;\n\t\tuniform float u_colorize;\n\t\tuniform vec2 u_res;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 total_symbols = u_res / u_symbol_size;\n\t\t\tvec2 uv = floor(v_coord * total_symbols) / total_symbols; //pixelate \n\t\t\tvec2 local_uv = mod(v_coord * u_res, u_symbol_size) / u_symbol_size;\n\t\t\tlowp vec4 textureColor = texture2D(u_texture, uv );\n\t\t\tfloat lum = clamp(u_brightness * (textureColor.x + textureColor.y + textureColor.z)/3.0,0.0,1.0);\n\t\t\tif( u_invert == 1.0 ) lum = 1.0 - lum;\n\t\t\tfloat index = floor( lum * (u_row_simbols * u_row_simbols - 1.0));\n\t\t\tfloat col = mod( index, u_row_simbols );\n\t\t\tfloat row = u_row_simbols - floor( index / u_row_simbols ) - 1.0;\n\t\t\tvec2 simbol_uv = ( vec2( col, row ) + local_uv ) / u_row_simbols;\n\t\t\tvec4 color = texture2D( u_textureB, simbol_uv );\n\t\t\tif(u_colorize == 1.0)\n\t\t\t\tcolor *= textureColor;\n\t\t\tgl_FragColor = color;\n\t\t}\n\t\t", -F.registerNodeType("texture/encode",q),p.title="Texture to Channels",p.desc="Split texture channels",p.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this._channels||(this._channels=Array(4));for(var b=gl.RGB,d=0,c=0;4>c;c++)this.isOutputConnected(c)?(this._channels[c]&&this._channels[c].width==a.width&&this._channels[c].height==a.height&&this._channels[c].type==a.type&&this._channels[c].format==b||(this._channels[c]=new GL.Texture(a.width,a.height,{type:a.type,format:b,filter:gl.LINEAR})), -d++):this._channels[c]=null;if(d){gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);for(var g=Mesh.getScreenQuad(),f=p._shader,e=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],c=0;4>c;c++)this._channels[c]&&(this._channels[c].drawTo(function(){a.bind(0);f.uniforms({u_texture:0,u_mask:e[c]}).draw(g)}),this.setOutputData(c,this._channels[c]))}}},p.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec4 u_mask;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\t\t}\n\t\t", -F.registerNodeType("texture/textureChannels",p),l.title="Channels to Texture",l.desc="Split texture channels",l.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},l.prototype.onExecute=function(){var a=c.getWhiteTexture(),b=this.getInputData(0)||a,d=this.getInputData(1)||a,g=this.getInputData(2)||a,f=this.getInputData(3)||a;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad();l._shader||(l._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,l.pixel_shader));var h=l._shader, -a=Math.max(b.width,d.width,g.width,f.width),k=Math.max(b.height,d.height,g.height,f.height),q=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._texture&&this._texture.width==a&&this._texture.height==k&&this._texture.type==q||(this._texture=new GL.Texture(a,k,{type:q,format:gl.RGBA,filter:gl.LINEAR}));a=this._color;a[0]=this.properties.R;a[1]=this.properties.G;a[2]=this.properties.B;a[3]=this.properties.A;var p=this._uniforms;this._texture.drawTo(function(){b.bind(0); -d.bind(1);g.bind(2);f.bind(3);h.uniforms(p).draw(e)});this.setOutputData(0,this._texture)},l.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureR;\n\t\tuniform sampler2D u_textureG;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform vec4 u_color;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = u_color * vec4( \t\t\t\t\ttexture2D(u_textureR, v_coord).r,\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\t\t}\n\t\t", -F.registerNodeType("texture/channelsTexture",l),a.title="Color",a.desc="Generates a 1x1 texture with a constant color",a.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},a.prototype.onDrawBackground=function(a){var b=this.properties.color;a.fillStyle="rgb("+Math.floor(255*Math.clamp(b[0],0,1))+","+Math.floor(255*Math.clamp(b[1],0,1))+","+Math.floor(255*Math.clamp(b[2],0,1))+")";this.flags.collapsed?this.boxcolor=a.fillStyle:a.fillRect(0,0,this.size[0],this.size[1])},a.prototype.onExecute= -function(){var a=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._tex&&this._tex.type==a||(this._tex=new GL.Texture(1,1,{format:gl.RGBA,type:a,minFilter:gl.NEAREST}));a=this.properties.color;if(this.inputs)for(var b=0;ba.width?b: -a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad(),l=null,h=this._uniforms;g?(l=d._shader_tex,l||(l=d._shader_tex=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,d.pixel_shader,{MIX_TEX:""}))):(l=d._shader_factor,l||(l=d._shader_factor=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,d.pixel_shader)),f=null==f?this.properties.factor:f,h.u_mix.set([f,f,f,f]));var k=this.properties.invert;this._tex.drawTo(function(){a.bind(k?1:0);b.bind(k?0:1);g&&g.bind(2); -l.uniforms(h).draw(e)});this.setOutputData(0,this._tex)}}},d.prototype.onGetInputs=function(){return[["factor","number"]]},d.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform sampler2D u_textureB;\n\t\t#ifdef MIX_TEX\n\t\t\tuniform sampler2D u_textureMix;\n\t\t#else\n\t\t\tuniform vec4 u_mix;\n\t\t#endif\n\t\t\n\t\tvoid main() {\n\t\t\t#ifdef MIX_TEX\n\t\t\t vec4 f = texture2D(u_textureMix, v_coord);\n\t\t\t#else\n\t\t\t vec4 f = u_mix;\n\t\t\t#endif\n\t\t gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\t\t}\n\t\t", -F.registerNodeType("texture/mix",d),g.title="Edges",g.desc="Detects edges",g.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},g.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else if(a){this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var b=Mesh.getScreenQuad(),d=g._shader,f=this.properties.invert,e=this.properties.factor, -l=this.properties.threshold?1:0;this._tex.drawTo(function(){a.bind(0);d.uniforms({u_texture:0,u_isize:[1/a.width,1/a.height],u_factor:e,u_threshold:l,u_invert:f?1:0}).draw(b)});this.setOutputData(0,this._tex)}}},g.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_isize;\n\t\tuniform int u_invert;\n\t\tuniform float u_factor;\n\t\tuniform float u_threshold;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 center = texture2D(u_texture, v_coord);\n\t\t\tvec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\t\t\tvec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\t\t\tvec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\t\t\tvec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\t\t\tvec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\t\t\tdiff *= u_factor;\n\t\t\tif(u_invert == 1)\n\t\t\t\tdiff.xyz = vec3(1.0) - diff.xyz;\n\t\t\tif( u_threshold == 0.0 )\n\t\t\t\tgl_FragColor = vec4( diff.xyz, center.a );\n\t\t\telse\n\t\t\t\tgl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\t\t}\n\t\t", -F.registerNodeType("texture/edges",g),f.title="Depth Range",f.desc="Generates a texture with a depth range",f.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a){var b=gl.UNSIGNED_BYTE;this.properties.high_precision&&(b=gl.half_float_ext?gl.HALF_FLOAT_OES:gl.FLOAT);this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&&this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGBA, -filter:gl.LINEAR}));var d=this._uniforms,b=this.properties.distance;this.isInputConnected(1)&&(b=this.getInputData(1),this.properties.distance=b);var c=this.properties.range;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.range=c);d.u_distance=b;d.u_range=c;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var g=Mesh.getScreenQuad();f._shader||(f._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,f.pixel_shader),f._shader_onlydepth=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,f.pixel_shader, -{ONLY_DEPTH:""}));var e=this.properties.only_depth?f._shader_onlydepth:f._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1,1E3];d.u_camera_planes=b;this._temp_texture.drawTo(function(){a.bind(0);e.uniforms(d).draw(g)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},f.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform float u_distance;\n\t\tuniform float u_range;\n\t\t\n\t\tfloat LinearDepth()\n\t\t{\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\t\t\tdepth = depth * 2.0 - 1.0;\n\t\t\treturn zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t\tfloat depth = LinearDepth();\n\t\t\t#ifdef ONLY_DEPTH\n\t\t\t gl_FragColor = vec4(depth);\n\t\t\t#else\n\t\t\t\tfloat diff = abs(depth * u_camera_planes.y - u_distance);\n\t\t\t\tfloat dof = 1.0;\n\t\t\t\tif(diff <= u_range)\n\t\t\t\t\tdof = diff / u_range;\n\t\t\t gl_FragColor = vec4(dof);\n\t\t\t#endif\n\t\t}\n\t\t", -F.registerNodeType("texture/depth_range",f),r.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},r.title="Linear Depth",r.desc="Creates a color texture with linear depth",r.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a&&(a.format==gl.DEPTH_COMPONENT||a.format==gl.DEPTH_STENCIL)){var b=this.properties.precision==c.HIGH?gl.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&& -this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGB,filter:gl.LINEAR}));var d=this._uniforms;d.u_invert=this.properties.invert?1:0;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var g=Mesh.getScreenQuad();r._shader||(r._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,r.pixel_shader));var f=r._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1, -1E3];d.u_camera_planes=b;d.u_ires.set([0,0]);this._temp_texture.drawTo(function(){a.bind(0);f.uniforms(d).draw(g)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},r.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform int u_invert;\n\t\tuniform vec2 u_ires;\n\t\t\n\t\tvoid main() {\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord + u_ires*0.5).x * 2.0 - 1.0;\n\t\t\tfloat f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t\tif( u_invert == 1 )\n\t\t\t\tf = 1.0 - f;\n\t\t\tgl_FragColor = vec4(vec3(f),1.0);\n\t\t}\n\t\t", -F.registerNodeType("texture/linear_depth",r),x.title="Blur",x.desc="Blur a texture",x.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},x.max_iterations=20,x.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._final_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(b=this._final_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));var d=this.properties.iterations;this.isInputConnected(1)&& -(d=this.getInputData(1),this.properties.iterations=d);d=Math.min(Math.floor(d),x.max_iterations);if(0==d)this.setOutputData(0,a);else{var c=this.properties.intensity;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.intensity=c);var g=F.camera_aspect;g||void 0===window.gl||(g=gl.canvas.height/gl.canvas.width);g||(g=1);var g=this.properties.preserve_aspect?g:1,f=this.properties.scale||[1,1];a.applyBlur(g*f[0],f[1],c,b);for(a=1;a>=1;1<(d|0)&&(d>>=1);if(2>b)break;k=l[m]=GL.Texture.getTemporary(b,d,g);r[0]=1/q.width;r[1]=1/q.height;q.blit(k,h.uniforms(e));q=k}this.isOutputConnected(2)&&(b=this._average_texture,b&&b.type==a.type&&b.format==a.format||(b=this._average_texture=new GL.Texture(1,1,{type:a.type,format:a.format,filter:gl.LINEAR})),r[0]=1/q.width,r[1]=1/q.height,e.u_intensity=n,e.u_delta=1,q.blit(b,h.uniforms(e)),this.setOutputData(2,b));gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE);e.u_intensity= -this.getInputOrProperty("persistence");e.u_delta=0.5;for(m-=2;0<=m;m--)k=l[m],l[m]=null,r[0]=1/q.width,r[1]=1/q.height,q.blit(k,h.uniforms(e)),GL.Texture.releaseTemporary(q),q=k;gl.disable(gl.BLEND);this.isOutputConnected(1)&&(l=this._glow_texture,l&&l.width==a.width&&l.height==a.height&&l.type==f&&l.format==a.format||(l=this._glow_texture=new GL.Texture(a.width,a.height,{type:f,format:a.format,filter:gl.LINEAR})),q.blit(l),this.setOutputData(1,l));if(this.isOutputConnected(0)){l=this._final_texture; -l&&l.width==a.width&&l.height==a.height&&l.type==f&&l.format==a.format||(l=this._final_texture=new GL.Texture(a.width,a.height,{type:f,format:a.format,filter:gl.LINEAR}));var x=this.getInputData(1),t=this.getInputOrProperty("dirt_factor");e.u_intensity=n;h=x?I._dirt_final_shader:I._final_shader;h||(h=x?I._dirt_final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,I.final_pixel_shader,{USE_DIRT:""}):I._final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,I.final_pixel_shader));l.drawTo(function(){a.bind(0); -q.bind(1);x&&(h.setUniform("u_dirt_factor",t),h.setUniform("u_dirt_texture",x.bind(2)));h.toViewport(e)});this.setOutputData(0,l)}GL.Texture.releaseTemporary(q)}},I.cut_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform float u_threshold;\n\tvoid main() {\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\t}",I.scale_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\t}", -I.final_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform sampler2D u_glow_texture;\n\t#ifdef USE_DIRT\n\t\tuniform sampler2D u_dirt_texture;\n\t#endif\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\tuniform float u_dirt_factor;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tvec4 glow = sampleBox( v_coord );\n\t\t#ifdef USE_DIRT\n\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\t\t#endif\n\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\t}", -F.registerNodeType("texture/glow",I),J.title="Kuwahara Filter",J.desc="Filters a texture giving an artistic oil canvas painting",J.max_radius=10,J._shaders=[],J.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));b=this.properties.radius;b=Math.min(Math.floor(b),J.max_radius);if(0== -b)this.setOutputData(0,a);else{var d=this.properties.intensity,c=F.camera_aspect;c||void 0===window.gl||(c=gl.canvas.height/gl.canvas.width);c||(c=1);c=this.properties.preserve_aspect?c:1;J._shaders[b]||(J._shaders[b]=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,J.pixel_shader,{RADIUS:b.toFixed(0)}));var g=J._shaders[b],f=GL.Mesh.getScreenQuad();a.bind(0);this._temp_texture.drawTo(function(){g.uniforms({u_texture:0,u_intensity:d,u_resolution:[a.width,a.height],u_iResolution:[1/a.width,1/a.height]}).draw(f)}); -this.setOutputData(0,this._temp_texture)}}},J.pixel_shader="\nprecision highp float;\nvarying vec2 v_coord;\nuniform sampler2D u_texture;\nuniform float u_intensity;\nuniform vec2 u_resolution;\nuniform vec2 u_iResolution;\n#ifndef RADIUS\n\t#define RADIUS 7\n#endif\nvoid main() {\n\n\tconst int radius = RADIUS;\n\tvec2 fragCoord = v_coord;\n\tvec2 src_size = u_iResolution;\n\tvec2 uv = v_coord;\n\tfloat n = float((radius + 1) * (radius + 1));\n\tint i;\n\tint j;\n\tvec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\tvec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\tvec3 c;\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm0 += c;\n\t\t\ts0 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm1 += c;\n\t\t\ts1 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm2 += c;\n\t\t\ts2 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm3 += c;\n\t\t\ts3 += c * c;\n\t\t}\n\t}\n\t\n\tfloat min_sigma2 = 1e+2;\n\tm0 /= n;\n\ts0 = abs(s0 / n - m0 * m0);\n\t\n\tfloat sigma2 = s0.r + s0.g + s0.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m0, 1.0);\n\t}\n\t\n\tm1 /= n;\n\ts1 = abs(s1 / n - m1 * m1);\n\t\n\tsigma2 = s1.r + s1.g + s1.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m1, 1.0);\n\t}\n\t\n\tm2 /= n;\n\ts2 = abs(s2 / n - m2 * m2);\n\t\n\tsigma2 = s2.r + s2.g + s2.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m2, 1.0);\n\t}\n\t\n\tm3 /= n;\n\ts3 = abs(s3 / n - m3 * m3);\n\t\n\tsigma2 = s3.r + s3.g + s3.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m3, 1.0);\n\t}\n}\n", -F.registerNodeType("texture/kuwahara",J),K.title="XDoG Filter",K.desc="Filters a texture giving an artistic ink style",K.max_radius=10,K._shaders=[],K.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));K._xdog_shader||(K._xdog_shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,K.xdog_pixel_shader)); -var d=K._xdog_shader,c=GL.Mesh.getScreenQuad(),g=this.properties.sigma,f=this.properties.k,e=this.properties.p,l=this.properties.epsilon,h=this.properties.phi;a.bind(0);this._temp_texture.drawTo(function(){d.uniforms({src:0,sigma:g,k:f,p:e,epsilon:l,phi:h,cvsWidth:a.width,cvsHeight:a.height}).draw(c)});this.setOutputData(0,this._temp_texture)}},K.xdog_pixel_shader="\nprecision highp float;\nuniform sampler2D src;\n\nuniform float cvsHeight;\nuniform float cvsWidth;\n\nuniform float sigma;\nuniform float k;\nuniform float p;\nuniform float epsilon;\nuniform float phi;\nvarying vec2 v_coord;\n\nfloat cosh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat cosH = (tmp + 1.0 / tmp) / 2.0;\n\treturn cosH;\n}\n\nfloat tanh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\treturn tanH;\n}\n\nfloat sinh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat sinH = (tmp - 1.0 / tmp) / 2.0;\n\treturn sinH;\n}\n\nvoid main(void){\n\tvec3 destColor = vec3(0.0);\n\tfloat tFrag = 1.0 / cvsHeight;\n\tfloat sFrag = 1.0 / cvsWidth;\n\tvec2 Frag = vec2(sFrag,tFrag);\n\tvec2 uv = gl_FragCoord.st;\n\tfloat twoSigmaESquared = 2.0 * sigma * sigma;\n\tfloat twoSigmaRSquared = twoSigmaESquared * k * k;\n\tint halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\tconst int MAX_NUM_ITERATION = 99999;\n\tvec2 sum = vec2(0.0);\n\tvec2 norm = vec2(0.0);\n\n\tfor(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\t\tint i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\t\tint j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\t\tfloat d = length(vec2(i,j));\n\t\tvec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\t\t\t\t\t\t\texp( -d * d / twoSigmaRSquared ));\n\n\t\tvec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\t\tnorm += kernel;\n\t\tsum += kernel * L;\n\t}\n\n\tsum /= norm;\n\n\tfloat H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\tfloat edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\tdestColor = vec3(edge);\n\tgl_FragColor = vec4(destColor, 1.0);\n}", -F.registerNodeType("texture/xDoG",K),E.title="Webcam",E.desc="Webcam texture",E.is_webcam_open=!1,E.prototype.openStream=function(){function a(d){E.is_webcam_open=!1;console.log("Webcam rejected",d);b._webcam_stream=!1;b.boxcolor="red";b.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](a);var b=this}},E.prototype.closeStream=function(){if(this._webcam_stream){var a= -this._webcam_stream.getTracks();if(a.length)for(var b=0;b=this.size[1]||!this._video||(a.save(),a.webgl?this._video_texture&&a.drawImage(this._video_texture,0,0,this.size[0],this.size[1]):a.drawImage(this._video, -0,0,this.size[0],this.size[1]),a.restore())},E.prototype.onExecute=function(){null!=this._webcam_stream||this._waiting_confirmation||this.openStream();if(this._video&&this._video.videoWidth){var a=this._video.videoWidth,b=this._video.videoHeight,d=this._video_texture;d&&d.width==a&&d.height==b||(this._video_texture=new GL.Texture(a,b,{format:gl.RGB,filter:gl.LINEAR}));this._video_texture.uploadImage(this._video);this._video_texture.version=++this.version;this.properties.texture_name&&(c.getTexturesContainer()[this.properties.texture_name]= -this._video_texture);this.setOutputData(0,this._video_texture);for(a=1;aMath.abs(d))return c[1];d=(a-c[0])/d;return c[1]*(1-d)+g[1]*d}}return 0}},H.prototype.updateCurve=function(){for(var a=this._values,b=a.length/4,d=this.properties.split_channels,c=0;c=f;){e=0.5*(l+f)|0;c=a[e];if(c==d)break;if(f==l-1)return f;ca&&(a=1);this.points&&this.points.length==3*a||(this.points=new Float32Array(3*a));this.properties.generate_normals?this.normals&&this.normals.length==this.points.length||(this.normals=new Float32Array(this.points.length)):this.normals=null;var d=this._last_radius||this.properties.radius,c=this.properties.mode,f=this.getInputData(0);this._old_obj_version=f?f._version:null;this.points=m.generatePoints(d,a,c,this.points, -this.normals,this.properties.regular,f);this.version++};m.generatePoints=function(a,d,c,f,e,l,h){var k=3*d;f&&f.length==k||(f=new Float32Array(k));var q=new Float32Array(3),p=new Float32Array([0,1,0]);if(l)if(c==m.RECTANGLE){k=Math.floor(Math.sqrt(d));for(d=0;de||vl&&lh))break}this.geometry.indices=this.indices=new Uint32Array(k)}this.indices&&this.indices.length?(this.geometry.indices=this.indices,this.setOutputData(0,this.geometry)):this.setOutputData(0,null)}};u.registerNodeType("geometry/connectPoints",s);"undefined"!=typeof GL&&(D.title="to geometry",D.desc="converts a mesh to geometry",D.prototype.onExecute= -function(){var a=this.getInputData(0);if(a){if(a!=this.last_mesh){this.last_mesh=a;for(i in a.vertexBuffers)this.geometry[i]=a.vertexBuffers[i].data;a.indexBuffers.triangles&&(this.geometry.indices=a.indexBuffers.triangles.data);this.geometry._id=c();this.geometry._version=0}this.setOutputData(0,this.geometry);this.geometry&&this.setOutputData(1,this.geometry.vertices)}},u.registerNodeType("geometry/toGeometry",D),A.title="Geo to Mesh",A.prototype.updateMesh=function(a){this.mesh||(this.mesh=new GL.Mesh); -for(var d in a)if("_"!=d[0]){var c=a[d],f=GL.Mesh.common_buffers[d];if(f||"indices"==d){var f=f?f.spacing:3,e=this.mesh.vertexBuffers[d];e&&e.data.length==c.length?(e.data.set(c),e.upload(GL.DYNAMIC_DRAW)):e=new GL.Buffer("indices"==d?GL.ELEMENT_ARRAY_BUFFER:GL.ARRAY_BUFFER,c,f,GL.DYNAMIC_DRAW);this.mesh.addBuffer(d,e)}}if(this.mesh.vertexBuffers.normals&&this.mesh.vertexBuffers.normals.data.length!=this.mesh.vertexBuffers.vertices.data.length){c=new Float32Array([0,1,0]);f=new Float32Array(this.mesh.vertexBuffers.vertices.data.length); -for(d=0;d=c.NOTEON||h<=c.NOTEOFF)this.channel=e&15};Object.defineProperty(c.prototype,"velocity",{get:function(){return this.cmd==c.NOTEON?this.data[2]:-1},set:function(c){this.data[2]=c},enumerable:!0});c.notes="A A# B C C# D D# E F F# G G#".split(" ");c.note_to_index={A:0,"A#":1,B:2,C:3,"C#":4,D:5,"D#":6,E:7,F:8,"F#":9,G:10,"G#":11};Object.defineProperty(c.prototype,"note",{get:function(){return this.cmd!= -c.NOTEON?-1:c.toNoteString(this.data[1],!0)},set:function(c){throw"notes cannot be assigned this way, must modify the data[1]";},enumerable:!0});Object.defineProperty(c.prototype,"octave",{get:function(){return this.cmd!=c.NOTEON?-1:Math.floor((this.data[1]-24)/12+1)},set:function(c){throw"octave cannot be assigned this way, must modify the data[1]";},enumerable:!0});c.prototype.getPitch=function(){return 440*Math.pow(2,(this.data[1]-69)/12)};c.computePitch=function(c){return 440*Math.pow(2,(c-69)/ -12)};c.prototype.getCC=function(){return this.data[1]};c.prototype.getCCValue=function(){return this.data[2]};c.prototype.getPitchBend=function(){return this.data[1]+(this.data[2]<<7)-8192};c.computePitchBend=function(c,e){return c+(e<<7)-8192};c.prototype.setCommandFromString=function(e){this.cmd=c.computeCommandFromString(e)};c.computeCommandFromString=function(e){if(!e)return 0;if(e&&e.constructor===Number)return e;e=e.toUpperCase();switch(e){case "NOTE ON":case "NOTEON":return c.NOTEON;case "NOTE OFF":case "NOTEOFF":return c.NOTEON; -case "KEY PRESSURE":case "KEYPRESSURE":return c.KEYPRESSURE;case "CONTROLLER CHANGE":case "CONTROLLERCHANGE":case "CC":return c.CONTROLLERCHANGE;case "PROGRAM CHANGE":case "PROGRAMCHANGE":case "PC":return c.PROGRAMCHANGE;case "CHANNEL PRESSURE":case "CHANNELPRESSURE":return c.CHANNELPRESSURE;case "PITCH BEND":case "PITCHBEND":return c.PITCHBEND;case "TIME TICK":case "TIMETICK":return c.TIMETICK;default:return Number(e)}};c.toNoteString=function(e,h){e=Math.round(e);var l,a=Math.floor((e-24)/12+1); -l=(e-21)%12;0>l&&(l=12+l);return c.notes[l]+(h?"":a)};c.NoteStringToPitch=function(e){e=e.toUpperCase();var h=e[0],l=4;"#"==e[1]?(h+="#",2this.properties.max_value)return;this.trigger("on_midi",h)}};u.registerNodeType("midi/filter",k);C.title="MIDIEvent";C.desc="Create a MIDI Event";C.color="#243";C.prototype.onAction=function(e,h){"assign"==e?(this.properties.channel=h.channel,this.properties.cmd=h.cmd,this.properties.value1=h.data[1],this.properties.value2=h.data[2],h.cmd== -c.NOTEON?this.gate=!0:h.cmd==c.NOTEOFF&&(this.gate=!1)):(h=this.midi_event,h.channel=this.properties.channel,this.properties.cmd&&this.properties.cmd.constructor===String?h.setCommandFromString(this.properties.cmd):h.cmd=this.properties.cmd,h.data[0]=h.cmd|h.channel,h.data[1]=Number(this.properties.value1),h.data[2]=Number(this.properties.value2),this.trigger("on_midi",h))};C.prototype.onExecute=function(){var e=this.properties;if(this.inputs)for(var h=0;hc;++c)this.valid_notes[c]=-1!=this.notes_pitches.indexOf(c);for(c=0;12>c;++c)if(this.valid_notes[c])this.offset_notes[c]=0;else for(var e=1;12>e;++e){if(this.valid_notes[(c-e)%12]){this.offset_notes[c]=-e;break}if(this.valid_notes[(c+e)%12]){this.offset_notes[c]=e;break}}};A.prototype.onAction=function(e,h){h&&h.constructor===c&&(h.data[0]==c.NOTEON||h.data[0]==c.NOTEOFF?(this.midi_event=new c,this.midi_event.setup(h.data),this.midi_event.data[1]+=this.offset_notes[c.note_to_index[h.note]], -this.trigger("out",this.midi_event)):this.trigger("out",h))};A.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&c!=this._current_scale&&this.processScale(c)};u.registerNodeType("midi/quantize",A);h.title="MIDI fromFile";h.desc="Plays a MIDI file";h.color="#243";h.prototype.onAction=function(c){"play"==c?this.play():"pause"==c&&(this._playing=!this._playing)};h.prototype.onPropertyChanged=function(c,e){"url"==c&&this.loadMIDIFile(e)};h.prototype.onExecute=function(){if(this._midi&& -this._playing){this._current_time+=this.graph.elapsed_time;for(var e=100*this._current_time,h=0;hb;b++)for(var d=0;dg+f||c[1]>d))return b}}return-1};B.prototype.onAction=function(e,h){if("reset"==e)for(var l=0;lh[1])){var l=this.getKeyIndex(h);this.keys[l]=!0;this._last_key=l;var l=12*(this.properties.start_octave-1)+29+l,a=new c;a.setup([c.NOTEON,l,100]);this.trigger("note",a);return!0}};B.prototype.onMouseMove=function(e,h){if(!(0>h[1]||-1==this._last_key)){this.setDirtyCanvas(!0); -var l=this.getKeyIndex(h);if(this._last_key==l)return!0;this.keys[this._last_key]=!1;var a=12*(this.properties.start_octave-1)+29+this._last_key,b=new c;b.setup([c.NOTEOFF,a,100]);this.trigger("note",b);this.keys[l]=!0;a=12*(this.properties.start_octave-1)+29+l;b=new c;b.setup([c.NOTEON,a,100]);this.trigger("note",b);this._last_key=l;return!0}};B.prototype.onMouseUp=function(e,h){if(!(0>h[1])){var l=this.getKeyIndex(h);this.keys[l]=!1;this._last_key=-1;var l=12*(this.properties.start_octave-1)+29+ -l,a=new c;a.setup([c.NOTEOFF,l,100]);this.trigger("note",a);return!0}};u.registerNodeType("midi/keys",B)})(this); -(function(z){function c(){this.properties={src:"",gain:0.5,loop:!0,autoplay:!0,playbackRate:1};this._loading_audio=!1;this._audiobuffer=null;this._audionodes=[];this._last_sourcenode=null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=p.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain;this.properties.src&&this.loadSound(this.properties.src)}function m(){this.properties={gain:0.5};this._audionodes=[];this._media_stream= -null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=p.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain}function n(){this.properties={fftSize:2048,minDecibels:-100,maxDecibels:-10,smoothingTimeConstant:0.5};this.audionode=p.getAudioContext().createAnalyser();this.audionode.graphnode=this;this.audionode.fftSize=this.properties.fftSize;this.audionode.minDecibels=this.properties.minDecibels;this.audionode.maxDecibels= -this.properties.maxDecibels;this.audionode.smoothingTimeConstant=this.properties.smoothingTimeConstant;this.addInput("in","audio");this.addOutput("freqs","array");this.addOutput("samples","array");this._time_bin=this._freq_bin=null}function e(){this.properties={gain:1};this.audionode=p.getAudioContext().createGain();this.addInput("in","audio");this.addInput("gain","number");this.addOutput("out","audio")}function w(){this.properties={impulse_src:"",normalize:!0};this.audionode=p.getAudioContext().createConvolver(); -this.addInput("in","audio");this.addOutput("out","audio")}function k(){this.properties={threshold:-50,knee:40,ratio:12,reduction:-20,attack:0,release:0.25};this.audionode=p.getAudioContext().createDynamicsCompressor();this.addInput("in","audio");this.addOutput("out","audio")}function C(){this.properties={};this.audionode=p.getAudioContext().createWaveShaper();this.addInput("in","audio");this.addInput("shape","waveshape");this.addOutput("out","audio")}function v(){this.properties={gain1:0.5,gain2:0.5}; -this.audionode=p.getAudioContext().createGain();this.audionode1=p.getAudioContext().createGain();this.audionode1.gain.value=this.properties.gain1;this.audionode2=p.getAudioContext().createGain();this.audionode2.gain.value=this.properties.gain2;this.audionode1.connect(this.audionode);this.audionode2.connect(this.audionode);this.addInput("in1","audio");this.addInput("in1 gain","number");this.addInput("in2","audio");this.addInput("in2 gain","number");this.addOutput("out","audio")}function s(){this.properties= -{A:0.1,D:0.1,S:0.1,R:0.1};this.audionode=p.getAudioContext().createGain();this.audionode.gain.value=0;this.addInput("in","audio");this.addInput("gate","bool");this.addOutput("out","audio");this.gate=!1}function D(){this.properties={delayTime:0.5};this.audionode=p.getAudioContext().createDelay(10);this.audionode.delayTime.value=this.properties.delayTime;this.addInput("in","audio");this.addInput("time","number");this.addOutput("out","audio")}function A(){this.properties={frequency:350,detune:0,Q:1}; -this.addProperty("type","lowpass","enum",{values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")});this.audionode=p.getAudioContext().createBiquadFilter();this.addInput("in","audio");this.addOutput("out","audio")}function h(){this.properties={frequency:440,detune:0,type:"sine"};this.addProperty("type","sine","enum",{values:["sine","square","sawtooth","triangle","custom"]});this.audionode=p.getAudioContext().createOscillator();this.addOutput("out","audio")}function y(){this.properties= -{continuous:!0,mark:-1};this.addInput("data","array");this.addInput("mark","number");this.size=[300,200];this._last_buffer=null}function B(){this.properties={band:440,amplitude:1};this.addInput("freqs","array");this.addOutput("signal","number")}function u(){if(!u.default_code){var c=u.default_function.toString(),a=c.indexOf("{")+1,b=c.lastIndexOf("}");u.default_code=c.substr(a,b-a)}this.properties={code:u.default_code};c=p.getAudioContext();c.createScriptProcessor?this.audionode=c.createScriptProcessor(4096, -1,1):(console.warn("ScriptProcessorNode deprecated"),this.audionode=c.createGain());this.processCode();u._bypass_function||(u._bypass_function=this.audionode.onaudioprocess);this.addInput("in","audio");this.addOutput("out","audio")}function G(){this.audionode=p.getAudioContext().destination;this.addInput("in","audio")}var q=z.LiteGraph,p={};z.LGAudio=p;p.getAudioContext=function(){if(!this._audio_context){window.AudioContext=window.AudioContext||window.webkitAudioContext;if(!window.AudioContext)return console.error("AudioContext not supported by browser"), -null;this._audio_context=new AudioContext;this._audio_context.onmessage=function(c){console.log("msg",c)};this._audio_context.onended=function(c){console.log("ended",c)};this._audio_context.oncomplete=function(c){console.log("complete",c)}}return this._audio_context};p.connect=function(c,a){try{c.connect(a)}catch(b){console.warn("LGraphAudio:",b)}};p.disconnect=function(c,a){try{c.disconnect(a)}catch(b){console.warn("LGraphAudio:",b)}};p.changeAllAudiosConnections=function(c,a){if(c.inputs)for(var b= -0;b=this.size[0]&&(e=this.size[0]-1),c.strokeStyle="red",c.beginPath(),c.moveTo(e,d),c.lineTo(e,0),c.stroke())}};y.title="Visualization";y.desc="Audio Visualization";q.registerNodeType("audio/visualization",y);B.prototype.onExecute=function(){if(this._freqs=this.getInputData(0)){var c=this.properties.band,a=this.getInputData(1); -void 0!==a&&(c=a);a=p.getAudioContext().sampleRate/this._freqs.length;a=c/a*2;a>=this._freqs.length?a=this._freqs[this._freqs.length-1]:(c=a|0,a-=c,a=this._freqs[c]*(1-a)+this._freqs[c+1]*a);this.setOutputData(0,a/255*this.properties.amplitude)}};B.prototype.onGetInputs=function(){return[["band","number"]]};B.title="Signal";B.desc="extract the signal of some frequency";q.registerNodeType("audio/signal",B);u.prototype.onAdded=function(c){c.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess= -this._callback)};u["@code"]={widget:"code",type:"code"};u.prototype.onStart=function(){this.audionode.onaudioprocess=this._callback};u.prototype.onStop=function(){this.audionode.onaudioprocess=u._bypass_function};u.prototype.onPause=function(){this.audionode.onaudioprocess=u._bypass_function};u.prototype.onUnpause=function(){this.audionode.onaudioprocess=this._callback};u.prototype.onExecute=function(){};u.prototype.onRemoved=function(){this.audionode.onaudioprocess=u._bypass_function};u.prototype.processCode= -function(){try{this._script=new new Function("properties",this.properties.code)(this.properties),this._old_code=this.properties.code,this._callback=this._script.onaudioprocess}catch(c){console.error("Error in onaudioprocess code",c),this._callback=u._bypass_function,this.audionode.onaudioprocess=this._callback}};u.prototype.onPropertyChanged=function(c,a){"code"==c&&(this.properties.code=a,this.processCode(),this.graph&&this.graph.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback))}; -u.default_function=function(){this.onaudioprocess=function(c){var a=c.inputBuffer;c=c.outputBuffer;for(var b=0;b r && (r = Math.max(0, h + r)); + if (null == m || m > h) { + m = h; + } + m = Number(m); + 0 > m && (m = Math.max(0, h + m)); + for (r = Number(r || 0); r < m; r++) { + this[r] = c; + } + return this; + }; +}, "es6", "es3"); +$jscomp.SYMBOL_PREFIX = "jscomp_symbol_"; +$jscomp.initSymbol = function() { + $jscomp.initSymbol = function() { + }; + $jscomp.global.Symbol || ($jscomp.global.Symbol = $jscomp.Symbol); +}; +$jscomp.Symbol = function() { + var v = 0; + return function(c) { + return $jscomp.SYMBOL_PREFIX + (c || "") + v++; + }; +}(); +$jscomp.initSymbolIterator = function() { + $jscomp.initSymbol(); + var v = $jscomp.global.Symbol.iterator; + v || (v = $jscomp.global.Symbol.iterator = $jscomp.global.Symbol("iterator")); + "function" != typeof Array.prototype[v] && $jscomp.defineProperty(Array.prototype, v, {configurable:!0, writable:!0, value:function() { + return $jscomp.arrayIterator(this); + }}); + $jscomp.initSymbolIterator = function() { + }; +}; +$jscomp.arrayIterator = function(v) { + var c = 0; + return $jscomp.iteratorPrototype(function() { + return c < v.length ? {done:!1, value:v[c++]} : {done:!0}; + }); +}; +$jscomp.iteratorPrototype = function(v) { + $jscomp.initSymbolIterator(); + v = {next:v}; + v[$jscomp.global.Symbol.iterator] = function() { + return this; + }; + return v; +}; +$jscomp.iteratorFromArray = function(v, c) { + $jscomp.initSymbolIterator(); + v instanceof String && (v += ""); + var r = 0, m = {next:function() { + if (r < v.length) { + var h = r++; + return {value:c(h, v[h]), done:!1}; + } + m.next = function() { + return {done:!0, value:void 0}; + }; + return m.next(); + }}; + m[Symbol.iterator] = function() { + return m; + }; + return m; +}; +$jscomp.polyfill("Array.prototype.values", function(v) { + return v ? v : function() { + return $jscomp.iteratorFromArray(this, function(c, r) { + return r; + }); + }; +}, "es8", "es3"); +$jscomp.polyfill("Array.prototype.keys", function(v) { + return v ? v : function() { + return $jscomp.iteratorFromArray(this, function(c) { + return c; + }); + }; +}, "es6", "es3"); +$jscomp.owns = function(v, c) { + return Object.prototype.hasOwnProperty.call(v, c); +}; +$jscomp.polyfill("Object.values", function(v) { + return v ? v : function(c) { + var r = [], m; + for (m in c) { + $jscomp.owns(c, m) && r.push(c[m]); + } + return r; + }; +}, "es8", "es3"); +(function(v) { + function c(a) { + e.debug && console.log("Graph created"); + this.list_of_graphcanvas = null; + this.clear(); + a && this.configure(a); + } + function r(a, b, d, g, f, e) { + this.id = a; + this.type = b; + this.origin_id = d; + this.origin_slot = g; + this.target_id = f; + this.target_slot = e; + this._data = null; + this._pos = new Float32Array(2); + } + function m(a) { + this._ctor(a); + } + function h(a) { + this._ctor(a); + } + function q(a, b) { + this.offset = new Float32Array([0, 0]); + this.scale = 1; + this.max_scale = 10; + this.min_scale = 0.1; + this.onredraw = null; + this.enabled = !0; + this.last_mouse = [0, 0]; + this.element = null; + this.visible_area = new Float32Array(4); + a && (this.element = a, b || this.bindEvents(a)); + } + function l(a, b, d) { + d = d || {}; + this.background_image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; + a && a.constructor === String && (a = document.querySelector(a)); + this.ds = new q; + this.zoom_modify_alpha = !0; + this.title_text_font = "" + e.NODE_TEXT_SIZE + "px Arial"; + this.inner_text_font = "normal " + e.NODE_SUBTEXT_SIZE + "px Arial"; + this.node_title_color = e.NODE_TITLE_COLOR; + this.default_link_color = e.LINK_COLOR; + this.default_connection_color = {input_off:"#778", input_on:"#7F7", output_off:"#778", output_on:"#7F7"}; + this.highquality_render = !0; + this.use_gradients = !1; + this.editor_alpha = 1; + this.pause_rendering = !1; + this.clear_background = !0; + this.read_only = !1; + this.render_only_selected = !0; + this.live_mode = !1; + this.allow_searchbox = this.allow_interaction = this.allow_dragnodes = this.allow_dragcanvas = this.show_info = !0; + this.drag_mode = this.allow_reconnect_links = !1; + this.filter = this.dragging_rectangle = null; + this.always_render_background = !1; + this.render_canvas_border = this.render_shadows = !0; + this.render_connections_shadows = !1; + this.render_connections_border = !0; + this.render_connection_arrows = this.render_curved_connections = !1; + this.render_collapsed_slots = !0; + this.render_execution_order = !1; + this.render_link_tooltip = this.render_title_colored = !0; + this.links_render_mode = e.SPLINE_LINK; + this.canvas_mouse = [0, 0]; + this.onSelectionChange = this.onNodeMoved = this.onDrawLinkTooltip = this.onDrawOverlay = this.onDrawForeground = this.onDrawBackground = this.onMouse = this.onSearchBoxSelection = this.onSearchBox = null; + this.connections_width = 3; + this.round_radius = 8; + this.over_link_center = this.node_widget = this.current_node = null; + this.last_mouse_position = [0, 0]; + this.visible_area = this.ds.visible_area; + this.visible_links = []; + b && b.attachCanvas(this); + this.setCanvas(a); + this.clear(); + d.skip_render || this.startRendering(); + this.autoresize = d.autoresize; + } + function A(a, b) { + return Math.sqrt((b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1])); + } + function y(a, b, d, g, f, e) { + return d < a && d + f > a && g < b && g + e > b ? !0 : !1; + } + function x(a, b) { + var d = a[0] + a[2], g = a[1] + a[3], f = b[1] + b[3]; + return a[0] > b[0] + b[2] || a[1] > f || d < b[0] || g < b[1] ? !1 : !0; + } + function F(a, b) { + function d(a) { + var d = parseInt(e.style.top); + e.style.top = (d + a.deltaY * b.scroll_speed).toFixed() + "px"; + a.preventDefault(); + return !0; + } + this.options = b = b || {}; + var g = this; + b.parentMenu && (b.parentMenu.constructor !== this.constructor ? (console.error("parentMenu must be of class ContextMenu, ignoring it"), b.parentMenu = null) : (this.parentMenu = b.parentMenu, this.parentMenu.lock = !0, this.parentMenu.current_submenu = this)); + var f = null; + b.event && (f = b.event.constructor.name); + "MouseEvent" !== f && "CustomEvent" !== f && "PointerEvent" !== f && (console.error("Event passed to ContextMenu is not of type MouseEvent or CustomEvent. Ignoring it."), b.event = null); + var e = document.createElement("div"); + e.className = "litegraph litecontextmenu litemenubar-panel"; + b.className && (e.className += " " + b.className); + e.style.minWidth = 100; + e.style.minHeight = 100; + e.style.pointerEvents = "none"; + setTimeout(function() { + e.style.pointerEvents = "auto"; + }, 100); + e.addEventListener("mouseup", function(a) { + a.preventDefault(); + return !0; + }, !0); + e.addEventListener("contextmenu", function(a) { + if (2 != a.button) { + return !1; + } + a.preventDefault(); + return !1; + }, !0); + e.addEventListener("mousedown", function(a) { + if (2 == a.button) { + return g.close(), a.preventDefault(), !0; + } + }, !0); + b.scroll_speed || (b.scroll_speed = 0.1); + e.addEventListener("wheel", d, !0); + e.addEventListener("mousewheel", d, !0); + this.root = e; + b.title && (f = document.createElement("div"), f.className = "litemenu-title", f.innerHTML = b.title, e.appendChild(f)); + f = 0; + for (var c in a) { + var k = a.constructor == Array ? a[c] : c; + null != k && k.constructor !== String && (k = void 0 === k.content ? String(k) : k.content); + this.addItem(k, a[c], b); + f++; + } + e.addEventListener("mouseleave", function(a) { + g.lock || (e.closing_timer && clearTimeout(e.closing_timer), e.closing_timer = setTimeout(g.close.bind(g, a), 500)); + }); + e.addEventListener("mouseenter", function(a) { + e.closing_timer && clearTimeout(e.closing_timer); + }); + a = document; + b.event && (a = b.event.target.ownerDocument); + a || (a = document); + a.fullscreenElement ? a.fullscreenElement.appendChild(e) : a.body.appendChild(e); + c = b.left || 0; + a = b.top || 0; + b.event && (c = b.event.clientX - 10, a = b.event.clientY - 10, b.title && (a -= 20), b.parentMenu && (c = b.parentMenu.root.getBoundingClientRect(), c = c.left + c.width), f = document.body.getBoundingClientRect(), k = e.getBoundingClientRect(), 0 == f.height && console.error("document.body height is 0. That is dangerous, set html,body { height: 100%; }"), f.width && c > f.width - k.width - 10 && (c = f.width - k.width - 10), f.height && a > f.height - k.height - 10 && (a = f.height - k.height - + 10)); + e.style.left = c + "px"; + e.style.top = a + "px"; + b.scale && (e.style.transform = "scale(" + b.scale + ")"); + } + function z(a) { + this.points = a; + this.nearest = this.selected = -1; + this.size = null; + this.must_update = !0; + this.margin = 5; + } + var e = v.LiteGraph = {VERSION:0.4, CANVAS_GRID_SIZE:10, NODE_TITLE_HEIGHT:30, NODE_TITLE_TEXT_Y:20, NODE_SLOT_HEIGHT:20, NODE_WIDGET_HEIGHT:20, NODE_WIDTH:140, NODE_MIN_WIDTH:50, NODE_COLLAPSED_RADIUS:10, NODE_COLLAPSED_WIDTH:80, NODE_TITLE_COLOR:"#999", NODE_TEXT_SIZE:14, NODE_TEXT_COLOR:"#AAA", NODE_SUBTEXT_SIZE:12, NODE_DEFAULT_COLOR:"#333", NODE_DEFAULT_BGCOLOR:"#353535", NODE_DEFAULT_BOXCOLOR:"#666", NODE_DEFAULT_SHAPE:"box", DEFAULT_SHADOW_COLOR:"rgba(0,0,0,0.5)", DEFAULT_GROUP_FONT:24, + WIDGET_BGCOLOR:"#222", WIDGET_OUTLINE_COLOR:"#666", WIDGET_TEXT_COLOR:"#DDD", WIDGET_SECONDARY_TEXT_COLOR:"#999", LINK_COLOR:"#9A9", EVENT_LINK_COLOR:"#A86", CONNECTING_LINK_COLOR:"#AFA", MAX_NUMBER_OF_NODES:1000, DEFAULT_POSITION:[100, 100], VALID_SHAPES:["default", "box", "round", "card"], BOX_SHAPE:1, ROUND_SHAPE:2, CIRCLE_SHAPE:3, CARD_SHAPE:4, ARROW_SHAPE:5, INPUT:1, OUTPUT:2, EVENT:-1, ACTION:-1, ALWAYS:0, ON_EVENT:1, NEVER:2, ON_TRIGGER:3, UP:1, DOWN:2, LEFT:3, RIGHT:4, CENTER:5, STRAIGHT_LINK:0, + LINEAR_LINK:1, SPLINE_LINK:2, NORMAL_TITLE:0, NO_TITLE:1, TRANSPARENT_TITLE:2, AUTOHIDE_TITLE:3, proxy:null, node_images_path:"", debug:!1, catch_exceptions:!0, throw_errors:!0, allow_scripts:!1, registered_node_types:{}, node_types_by_file_extension:{}, Nodes:{}, searchbox_extras:{}, registerNodeType:function(a, b) { + if (!b.prototype) { + throw "Cannot register a simple object, it must be a class with a prototype"; + } + b.type = a; + e.debug && console.log("Node registered: " + a); + a.split("/"); + var d = b.name, g = a.lastIndexOf("/"); + b.category = a.substr(0, g); + b.title || (b.title = d); + if (b.prototype) { + for (var f in m.prototype) { + b.prototype[f] || (b.prototype[f] = m.prototype[f]); + } + } + if (g = this.registered_node_types[a]) { + console.log("replacing node type: " + a); + } else { + if (Object.hasOwnProperty(b.prototype, "shape") || Object.defineProperty(b.prototype, "shape", {set:function(a) { + switch(a) { + case "default": + delete this._shape; + break; + case "box": + this._shape = e.BOX_SHAPE; + break; + case "round": + this._shape = e.ROUND_SHAPE; + break; + case "circle": + this._shape = e.CIRCLE_SHAPE; + break; + case "card": + this._shape = e.CARD_SHAPE; + break; + default: + this._shape = a; + } + }, get:function(a) { + return this._shape; + }, enumerable:!0, configurable:!0}), b.prototype.onPropertyChange && console.warn("LiteGraph node class " + a + " has onPropertyChange method, it must be called onPropertyChanged with d at the end"), b.supported_extensions) { + for (f in b.supported_extensions) { + var C = b.supported_extensions[f]; + C && C.constructor === String && (this.node_types_by_file_extension[C.toLowerCase()] = b); + } + } + } + this.registered_node_types[a] = b; + b.constructor.name && (this.Nodes[d] = b); + if (e.onNodeTypeRegistered) { + e.onNodeTypeRegistered(a, b); + } + if (g && e.onNodeTypeReplaced) { + e.onNodeTypeReplaced(a, b, g); + } + }, unregisterNodeType:function(a) { + var b = a.constructor === String ? this.registered_node_types[a] : a; + if (!b) { + throw "node type not found: " + a; + } + delete this.registered_node_types[b.type]; + b.constructor.name && delete this.Nodes[b.constructor.name]; + }, wrapFunctionAsNode:function(a, b, d, g, f) { + for (var C = Array(b.length), c = "", k = e.getParameterNames(b), n = 0; n < k.length; ++n) { + c += "this.addInput('" + k[n] + "'," + (d && d[n] ? "'" + d[n] + "'" : "0") + ");\n"; + } + c += "this.addOutput('out'," + (g ? "'" + g + "'" : 0) + ");\n"; + f && (c += "this.properties = " + JSON.stringify(f) + ";\n"); + d = Function(c); + d.title = a.split("/").pop(); + d.desc = "Generated from " + b.name; + d.prototype.onExecute = function() { + for (var a = 0; a < C.length; ++a) { + C[a] = this.getInputData(a); + } + a = b.apply(this, C); + this.setOutputData(0, a); + }; + this.registerNodeType(a, d); + }, addNodeMethod:function(a, b) { + m.prototype[a] = b; + for (var d in this.registered_node_types) { + var g = this.registered_node_types[d]; + g.prototype[a] && (g.prototype["_" + a] = g.prototype[a]); + g.prototype[a] = b; + } + }, createNode:function(a, b, d) { + var g = this.registered_node_types[a]; + if (!g) { + return e.debug && console.log('GraphNode type "' + a + '" not registered.'), null; + } + b = b || g.title || a; + var f = null; + if (e.catch_exceptions) { + try { + f = new g(b); + } catch (w) { + return console.error(w), null; + } + } else { + f = new g(b); + } + f.type = a; + !f.title && b && (f.title = b); + f.properties || (f.properties = {}); + f.properties_info || (f.properties_info = []); + f.flags || (f.flags = {}); + f.size || (f.size = f.computeSize()); + f.pos || (f.pos = e.DEFAULT_POSITION.concat()); + f.mode || (f.mode = e.ALWAYS); + if (d) { + for (var C in d) { + f[C] = d[C]; + } + } + return f; + }, getNodeType:function(a) { + return this.registered_node_types[a]; + }, getNodeTypesInCategory:function(a, b) { + var d = [], g; + for (g in this.registered_node_types) { + var f = this.registered_node_types[g]; + b && f.filter && f.filter != b || ("" == a ? null == f.category && d.push(f) : f.category == a && d.push(f)); + } + return d; + }, getNodeTypesCategories:function(a) { + var b = {"":1}, d; + for (d in this.registered_node_types) { + var g = this.registered_node_types[d]; + !g.category || g.skip_list || a && g.filter != a || (b[g.category] = 1); + } + a = []; + for (d in b) { + a.push(d); + } + return a; + }, reloadNodes:function(a) { + var b = document.getElementsByTagName("script"), d = [], g; + for (g in b) { + d.push(b[g]); + } + b = document.getElementsByTagName("head")[0]; + a = document.location.href + a; + for (g in d) { + var f = d[g].src; + if (f && f.substr(0, a.length) == a) { + try { + e.debug && console.log("Reloading: " + f); + var C = document.createElement("script"); + C.type = "text/javascript"; + C.src = f; + b.appendChild(C); + b.removeChild(d[g]); + } catch (w) { + if (e.throw_errors) { + throw w; + } + e.debug && console.log("Error while reloading " + f); + } + } + } + e.debug && console.log("Nodes reloaded"); + }, cloneObject:function(a, b) { + if (null == a) { + return null; + } + a = JSON.parse(JSON.stringify(a)); + if (!b) { + return a; + } + for (var d in a) { + b[d] = a[d]; + } + return b; + }, isValidConnection:function(a, b) { + if (!a || !b || a == b || a == e.EVENT && b == e.ACTION) { + return !0; + } + a = String(a); + b = String(b); + a = a.toLowerCase(); + b = b.toLowerCase(); + if (-1 == a.indexOf(",") && -1 == b.indexOf(",")) { + return a == b; + } + a = a.split(","); + b = b.split(","); + for (var d = 0; d < a.length; ++d) { + for (var g = 0; g < b.length; ++g) { + if (a[d] == b[g]) { + return !0; + } + } + } + return !1; + }, registerSearchboxExtra:function(a, b, d) { + this.searchbox_extras[b.toLowerCase()] = {type:a, desc:b, data:d}; + }, fetchFile:function(a, b, d, g) { + if (!a) { + return null; + } + b = b || "text"; + if (a.constructor === String) { + return "http" == a.substr(0, 4) && e.proxy && (a = e.proxy + a.substr(a.indexOf(":") + 3)), fetch(a).then(function(a) { + if (!a.ok) { + throw Error("File not found"); + } + if ("arraybuffer" == b) { + return a.arrayBuffer(); + } + if ("text" == b || "string" == b) { + return a.text(); + } + if ("json" == b) { + return a.json(); + } + if ("blob" == b) { + return a.blob(); + } + }).then(function(a) { + d && d(a); + }).catch(function(b) { + console.error("error fetching file:", a); + g && g(b); + }); + } + if (a.constructor === File || a.constructor === Blob) { + var f = new FileReader; + f.onload = function(a) { + a = a.target.result; + "json" == b && (a = JSON.parse(a)); + d && d(a); + }; + if ("arraybuffer" == b) { + return f.readAsArrayBuffer(a); + } + if ("text" == b || "json" == b) { + return f.readAsText(a); + } + if ("blob" == b) { + return f.readAsBinaryString(a); + } + } + return null; + }}; + e.getTime = "undefined" != typeof performance ? performance.now.bind(performance) : "undefined" != typeof Date && Date.now ? Date.now.bind(Date) : "undefined" != typeof process ? function() { + var a = process.hrtime(); + return 0.001 * a[0] + 1e-6 * a[1]; + } : function() { + return (new Date).getTime(); + }; + v.LGraph = e.LGraph = c; + c.supported_types = ["number", "string", "boolean"]; + c.prototype.getSupportedTypes = function() { + return this.supported_types || c.supported_types; + }; + c.STATUS_STOPPED = 1; + c.STATUS_RUNNING = 2; + c.prototype.clear = function() { + this.stop(); + this.status = c.STATUS_STOPPED; + this.last_link_id = this.last_node_id = 0; + this._version = -1; + if (this._nodes) { + for (var a = 0; a < this._nodes.length; ++a) { + var b = this._nodes[a]; + if (b.onRemoved) { + b.onRemoved(); + } + } + } + this._nodes = []; + this._nodes_by_id = {}; + this._nodes_in_order = []; + this._nodes_executable = null; + this._groups = []; + this.links = {}; + this.iteration = 0; + this.config = {}; + this.vars = {}; + this.fixedtime = this.runningtime = this.globaltime = 0; + this.elapsed_time = this.fixedtime_lapse = 0.01; + this.starttime = this.last_update_time = 0; + this.catch_errors = !0; + this.inputs = {}; + this.outputs = {}; + this.change(); + this.sendActionToCanvas("clear"); + }; + c.prototype.attachCanvas = function(a) { + if (a.constructor != l) { + throw "attachCanvas expects a LGraphCanvas instance"; + } + a.graph && a.graph != this && a.graph.detachCanvas(a); + a.graph = this; + this.list_of_graphcanvas || (this.list_of_graphcanvas = []); + this.list_of_graphcanvas.push(a); + }; + c.prototype.detachCanvas = function(a) { + if (this.list_of_graphcanvas) { + var b = this.list_of_graphcanvas.indexOf(a); + -1 != b && (a.graph = null, this.list_of_graphcanvas.splice(b, 1)); + } + }; + c.prototype.start = function(a) { + if (this.status != c.STATUS_RUNNING) { + this.status = c.STATUS_RUNNING; + if (this.onPlayEvent) { + this.onPlayEvent(); + } + this.sendEventToAllNodes("onStart"); + this.last_update_time = this.starttime = e.getTime(); + a = a || 0; + var b = this; + if (0 == a && "undefined" != typeof window && window.requestAnimationFrame) { + var d = function() { + if (-1 == b.execution_timer_id) { + window.requestAnimationFrame(d); + if (b.onBeforeStep) { + b.onBeforeStep(); + } + b.runStep(1, !b.catch_errors); + if (b.onAfterStep) { + b.onAfterStep(); + } + } + }; + this.execution_timer_id = -1; + d(); + } else { + this.execution_timer_id = setInterval(function() { + if (b.onBeforeStep) { + b.onBeforeStep(); + } + b.runStep(1, !b.catch_errors); + if (b.onAfterStep) { + b.onAfterStep(); + } + }, a); + } + } + }; + c.prototype.stop = function() { + if (this.status != c.STATUS_STOPPED) { + this.status = c.STATUS_STOPPED; + if (this.onStopEvent) { + this.onStopEvent(); + } + null != this.execution_timer_id && (-1 != this.execution_timer_id && clearInterval(this.execution_timer_id), this.execution_timer_id = null); + this.sendEventToAllNodes("onStop"); + } + }; + c.prototype.runStep = function(a, b, d) { + a = a || 1; + var g = e.getTime(); + this.globaltime = 0.001 * (g - this.starttime); + var f = this._nodes_executable ? this._nodes_executable : this._nodes; + if (f) { + d = d || f.length; + if (b) { + for (var C = 0; C < a; C++) { + for (var c = 0; c < d; ++c) { + var k = f[c]; + if (k.mode == e.ALWAYS && k.onExecute) { + k.onExecute(); + } + } + this.fixedtime += this.fixedtime_lapse; + if (this.onExecuteStep) { + this.onExecuteStep(); + } + } + if (this.onAfterExecute) { + this.onAfterExecute(); + } + } else { + try { + for (C = 0; C < a; C++) { + for (c = 0; c < d; ++c) { + if (k = f[c], k.mode == e.ALWAYS && k.onExecute) { + k.onExecute(); + } + } + this.fixedtime += this.fixedtime_lapse; + if (this.onExecuteStep) { + this.onExecuteStep(); + } + } + if (this.onAfterExecute) { + this.onAfterExecute(); + } + this.errors_in_execution = !1; + } catch (K) { + this.errors_in_execution = !0; + if (e.throw_errors) { + throw K; + } + e.debug && console.log("Error during execution: " + K); + this.stop(); + } + } + a = e.getTime(); + g = a - g; + 0 == g && (g = 1); + this.execution_time = 0.001 * g; + this.globaltime += 0.001 * g; + this.iteration += 1; + this.elapsed_time = 0.001 * (a - this.last_update_time); + this.last_update_time = a; + } + }; + c.prototype.updateExecutionOrder = function() { + this._nodes_in_order = this.computeExecutionOrder(!1); + this._nodes_executable = []; + for (var a = 0; a < this._nodes_in_order.length; ++a) { + this._nodes_in_order[a].onExecute && this._nodes_executable.push(this._nodes_in_order[a]); + } + }; + c.prototype.computeExecutionOrder = function(a, b) { + for (var d = [], g = [], f = {}, c = {}, w = {}, k = 0, n = this._nodes.length; k < n; ++k) { + var p = this._nodes[k]; + if (!a || p.onExecute) { + f[p.id] = p; + var l = 0; + if (p.inputs) { + for (var t = 0, h = p.inputs.length; t < h; t++) { + p.inputs[t] && null != p.inputs[t].link && (l += 1); + } + } + 0 == l ? (g.push(p), b && (p._level = 1)) : (b && (p._level = 0), w[p.id] = l); + } + } + for (; 0 != g.length;) { + if (p = g.shift(), d.push(p), delete f[p.id], p.outputs) { + for (k = 0; k < p.outputs.length; k++) { + if (a = p.outputs[k], null != a && null != a.links && 0 != a.links.length) { + for (t = 0; t < a.links.length; t++) { + (n = this.links[a.links[t]]) && !c[n.id] && (l = this.getNodeById(n.target_id), null == l ? c[n.id] = !0 : (b && (!l._level || l._level <= p._level) && (l._level = p._level + 1), c[n.id] = !0, --w[l.id], 0 == w[l.id] && g.push(l))); + } + } + } + } + } + for (k in f) { + d.push(f[k]); + } + d.length != this._nodes.length && e.debug && console.warn("something went wrong, nodes missing"); + n = d.length; + for (k = 0; k < n; ++k) { + d[k].order = k; + } + d = d.sort(function(a, b) { + var d = a.constructor.priority || a.priority || 0, f = b.constructor.priority || b.priority || 0; + return d == f ? a.order - b.order : d - f; + }); + for (k = 0; k < n; ++k) { + d[k].order = k; + } + return d; + }; + c.prototype.getAncestors = function(a) { + for (var b = [], d = [a], g = {}; d.length;) { + var f = d.shift(); + if (f.inputs) { + g[f.id] || f == a || (g[f.id] = !0, b.push(f)); + for (var e = 0; e < f.inputs.length; ++e) { + var c = f.getInputNode(e); + c && -1 == b.indexOf(c) && d.push(c); + } + } + } + b.sort(function(a, b) { + return a.order - b.order; + }); + return b; + }; + c.prototype.arrange = function(a) { + a = a || 100; + for (var b = this.computeExecutionOrder(!1, !0), d = [], g = 0; g < b.length; ++g) { + var f = b[g], c = f._level || 1; + d[c] || (d[c] = []); + d[c].push(f); + } + b = a; + for (g = 0; g < d.length; ++g) { + if (c = d[g]) { + for (var w = 100, k = a + e.NODE_TITLE_HEIGHT, n = 0; n < c.length; ++n) { + f = c[n], f.pos[0] = b, f.pos[1] = k, f.size[0] > w && (w = f.size[0]), k += f.size[1] + a + e.NODE_TITLE_HEIGHT; + } + b += w + a; + } + } + this.setDirtyCanvas(!0, !0); + }; + c.prototype.getTime = function() { + return this.globaltime; + }; + c.prototype.getFixedTime = function() { + return this.fixedtime; + }; + c.prototype.getElapsedTime = function() { + return this.elapsed_time; + }; + c.prototype.sendEventToAllNodes = function(a, b, d) { + d = d || e.ALWAYS; + var g = this._nodes_in_order ? this._nodes_in_order : this._nodes; + if (g) { + for (var f = 0, c = g.length; f < c; ++f) { + var w = g[f]; + if (w.constructor === e.Subgraph && "onExecute" != a) { + w.mode == d && w.sendEventToAllNodes(a, b, d); + } else { + if (w[a] && w.mode == d) { + if (void 0 === b) { + w[a](); + } else { + if (b && b.constructor === Array) { + w[a].apply(w, b); + } else { + w[a](b); + } + } + } + } + } + } + }; + c.prototype.sendActionToCanvas = function(a, b) { + if (this.list_of_graphcanvas) { + for (var d = 0; d < this.list_of_graphcanvas.length; ++d) { + var g = this.list_of_graphcanvas[d]; + g[a] && g[a].apply(g, b); + } + } + }; + c.prototype.add = function(a, b) { + if (a) { + if (a.constructor === h) { + this._groups.push(a), this.setDirtyCanvas(!0), this.change(), a.graph = this, this._version++; + } else { + -1 != a.id && null != this._nodes_by_id[a.id] && (console.warn("LiteGraph: there is already a node with this ID, changing it"), a.id = ++this.last_node_id); + if (this._nodes.length >= e.MAX_NUMBER_OF_NODES) { + throw "LiteGraph: max number of nodes in a graph reached"; + } + null == a.id || -1 == a.id ? a.id = ++this.last_node_id : this.last_node_id < a.id && (this.last_node_id = a.id); + a.graph = this; + this._version++; + this._nodes.push(a); + this._nodes_by_id[a.id] = a; + if (a.onAdded) { + a.onAdded(this); + } + this.config.align_to_grid && a.alignToGrid(); + b || this.updateExecutionOrder(); + if (this.onNodeAdded) { + this.onNodeAdded(a); + } + this.setDirtyCanvas(!0); + this.change(); + return a; + } + } + }; + c.prototype.remove = function(a) { + if (a.constructor === e.LGraphGroup) { + var b = this._groups.indexOf(a); + -1 != b && this._groups.splice(b, 1); + a.graph = null; + this._version++; + this.setDirtyCanvas(!0, !0); + this.change(); + } else { + if (null != this._nodes_by_id[a.id] && !a.ignore_remove) { + if (a.inputs) { + for (b = 0; b < a.inputs.length; b++) { + var d = a.inputs[b]; + null != d.link && a.disconnectInput(b); + } + } + if (a.outputs) { + for (b = 0; b < a.outputs.length; b++) { + d = a.outputs[b], null != d.links && d.links.length && a.disconnectOutput(b); + } + } + if (a.onRemoved) { + a.onRemoved(); + } + a.graph = null; + this._version++; + if (this.list_of_graphcanvas) { + for (b = 0; b < this.list_of_graphcanvas.length; ++b) { + d = this.list_of_graphcanvas[b], d.selected_nodes[a.id] && delete d.selected_nodes[a.id], d.node_dragged == a && (d.node_dragged = null); + } + } + b = this._nodes.indexOf(a); + -1 != b && this._nodes.splice(b, 1); + delete this._nodes_by_id[a.id]; + if (this.onNodeRemoved) { + this.onNodeRemoved(a); + } + this.setDirtyCanvas(!0, !0); + this.change(); + this.updateExecutionOrder(); + } + } + }; + c.prototype.getNodeById = function(a) { + return null == a ? null : this._nodes_by_id[a]; + }; + c.prototype.findNodesByClass = function(a, b) { + b = b || []; + for (var d = b.length = 0, g = this._nodes.length; d < g; ++d) { + this._nodes[d].constructor === a && b.push(this._nodes[d]); + } + return b; + }; + c.prototype.findNodesByType = function(a, b) { + a = a.toLowerCase(); + b = b || []; + for (var d = b.length = 0, g = this._nodes.length; d < g; ++d) { + this._nodes[d].type.toLowerCase() == a && b.push(this._nodes[d]); + } + return b; + }; + c.prototype.findNodeByTitle = function(a) { + for (var b = 0, d = this._nodes.length; b < d; ++b) { + if (this._nodes[b].title == a) { + return this._nodes[b]; + } + } + return null; + }; + c.prototype.findNodesByTitle = function(a) { + for (var b = [], d = 0, g = this._nodes.length; d < g; ++d) { + this._nodes[d].title == a && b.push(this._nodes[d]); + } + return b; + }; + c.prototype.getNodeOnPos = function(a, b, d, g) { + d = d || this._nodes; + for (var f = d.length - 1; 0 <= f; f--) { + var e = d[f]; + if (e.isPointInside(a, b, g)) { + return e; + } + } + return null; + }; + c.prototype.getGroupOnPos = function(a, b) { + for (var d = this._groups.length - 1; 0 <= d; d--) { + var g = this._groups[d]; + if (g.isPointInside(a, b, 2, !0)) { + return g; + } + } + return null; + }; + c.prototype.checkNodeTypes = function() { + for (var a = 0; a < this._nodes.length; a++) { + var b = this._nodes[a]; + if (b.constructor != e.registered_node_types[b.type]) { + console.log("node being replaced by newer version: " + b.type); + var d = e.createNode(b.type); + this._nodes[a] = d; + d.configure(b.serialize()); + d.graph = this; + this._nodes_by_id[d.id] = d; + b.inputs && (d.inputs = b.inputs.concat()); + b.outputs && (d.outputs = b.outputs.concat()); + } + } + this.updateExecutionOrder(); + }; + c.prototype.onAction = function(a, b) { + this._input_nodes = this.findNodesByClass(e.GraphInput, this._input_nodes); + for (var d = 0; d < this._input_nodes.length; ++d) { + var g = this._input_nodes[d]; + if (g.properties.name == a) { + g.onAction(a, b); + break; + } + } + }; + c.prototype.trigger = function(a, b) { + if (this.onTrigger) { + this.onTrigger(a, b); + } + }; + c.prototype.addInput = function(a, b, d) { + if (!this.inputs[a]) { + this.inputs[a] = {name:a, type:b, value:d}; + this._version++; + if (this.onInputAdded) { + this.onInputAdded(a, b); + } + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + } + }; + c.prototype.setInputData = function(a, b) { + if (a = this.inputs[a]) { + a.value = b; + } + }; + c.prototype.getInputData = function(a) { + return (a = this.inputs[a]) ? a.value : null; + }; + c.prototype.renameInput = function(a, b) { + if (b != a) { + if (!this.inputs[a]) { + return !1; + } + if (this.inputs[b]) { + return console.error("there is already one input with that name"), !1; + } + this.inputs[b] = this.inputs[a]; + delete this.inputs[a]; + this._version++; + if (this.onInputRenamed) { + this.onInputRenamed(a, b); + } + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + } + }; + c.prototype.changeInputType = function(a, b) { + if (!this.inputs[a]) { + return !1; + } + if (!this.inputs[a].type || String(this.inputs[a].type).toLowerCase() != String(b).toLowerCase()) { + if (this.inputs[a].type = b, this._version++, this.onInputTypeChanged) { + this.onInputTypeChanged(a, b); + } + } + }; + c.prototype.removeInput = function(a) { + if (!this.inputs[a]) { + return !1; + } + delete this.inputs[a]; + this._version++; + if (this.onInputRemoved) { + this.onInputRemoved(a); + } + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + return !0; + }; + c.prototype.addOutput = function(a, b, d) { + this.outputs[a] = {name:a, type:b, value:d}; + this._version++; + if (this.onOutputAdded) { + this.onOutputAdded(a, b); + } + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + }; + c.prototype.setOutputData = function(a, b) { + if (a = this.outputs[a]) { + a.value = b; + } + }; + c.prototype.getOutputData = function(a) { + return (a = this.outputs[a]) ? a.value : null; + }; + c.prototype.renameOutput = function(a, b) { + if (!this.outputs[a]) { + return !1; + } + if (this.outputs[b]) { + return console.error("there is already one output with that name"), !1; + } + this.outputs[b] = this.outputs[a]; + delete this.outputs[a]; + this._version++; + if (this.onOutputRenamed) { + this.onOutputRenamed(a, b); + } + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + }; + c.prototype.changeOutputType = function(a, b) { + if (!this.outputs[a]) { + return !1; + } + if (!this.outputs[a].type || String(this.outputs[a].type).toLowerCase() != String(b).toLowerCase()) { + if (this.outputs[a].type = b, this._version++, this.onOutputTypeChanged) { + this.onOutputTypeChanged(a, b); + } + } + }; + c.prototype.removeOutput = function(a) { + if (!this.outputs[a]) { + return !1; + } + delete this.outputs[a]; + this._version++; + if (this.onOutputRemoved) { + this.onOutputRemoved(a); + } + if (this.onInputsOutputsChange) { + this.onInputsOutputsChange(); + } + return !0; + }; + c.prototype.triggerInput = function(a, b) { + a = this.findNodesByTitle(a); + for (var d = 0; d < a.length; ++d) { + a[d].onTrigger(b); + } + }; + c.prototype.setCallback = function(a, b) { + a = this.findNodesByTitle(a); + for (var d = 0; d < a.length; ++d) { + a[d].setTrigger(b); + } + }; + c.prototype.connectionChange = function(a, b) { + this.updateExecutionOrder(); + if (this.onConnectionChange) { + this.onConnectionChange(a); + } + this._version++; + this.sendActionToCanvas("onConnectionChange"); + }; + c.prototype.isLive = function() { + if (!this.list_of_graphcanvas) { + return !1; + } + for (var a = 0; a < this.list_of_graphcanvas.length; ++a) { + if (this.list_of_graphcanvas[a].live_mode) { + return !0; + } + } + return !1; + }; + c.prototype.clearTriggeredSlots = function() { + for (var a in this.links) { + var b = this.links[a]; + b && b._last_time && (b._last_time = 0); + } + }; + c.prototype.change = function() { + e.debug && console.log("Graph changed"); + this.sendActionToCanvas("setDirty", [!0, !0]); + if (this.on_change) { + this.on_change(this); + } + }; + c.prototype.setDirtyCanvas = function(a, b) { + this.sendActionToCanvas("setDirty", [a, b]); + }; + c.prototype.removeLink = function(a) { + if (a = this.links[a]) { + var b = this.getNodeById(a.target_id); + b && b.disconnectInput(a.target_slot); + } + }; + c.prototype.serialize = function() { + for (var a = [], b = 0, d = this._nodes.length; b < d; ++b) { + a.push(this._nodes[b].serialize()); + } + d = []; + for (b in this.links) { + var g = this.links[b]; + if (!g.serialize) { + console.warn("weird LLink bug, link info is not a LLink but a regular object"); + var f = new r; + for (b in g) { + f[b] = g[b]; + } + g = this.links[b] = f; + } + d.push(g.serialize()); + } + g = []; + for (b = 0; b < this._groups.length; ++b) { + g.push(this._groups[b].serialize()); + } + return {last_node_id:this.last_node_id, last_link_id:this.last_link_id, nodes:a, links:d, groups:g, config:this.config, version:e.VERSION}; + }; + c.prototype.configure = function(a, b) { + if (a) { + b || this.clear(); + b = a.nodes; + if (a.links && a.links.constructor === Array) { + for (var d = [], g = 0; g < a.links.length; ++g) { + var f = a.links[g]; + if (f) { + var c = new r; + c.configure(f); + d[c.id] = c; + } else { + console.warn("serialized graph link data contains errors, skipping."); + } + } + a.links = d; + } + for (g in a) { + "nodes" != g && "groups" != g && (this[g] = a[g]); + } + d = !1; + this._nodes = []; + if (b) { + g = 0; + for (f = b.length; g < f; ++g) { + c = b[g]; + var w = e.createNode(c.type, c.title); + w || (e.debug && console.log("Node not found or has errors: " + c.type), w = new m, w.last_serialization = c, d = w.has_errors = !0); + w.id = c.id; + this.add(w, !0); + } + g = 0; + for (f = b.length; g < f; ++g) { + c = b[g], (w = this.getNodeById(c.id)) && w.configure(c); + } + } + this._groups.length = 0; + if (a.groups) { + for (g = 0; g < a.groups.length; ++g) { + b = new e.LGraphGroup, b.configure(a.groups[g]), this.add(b); + } + } + this.updateExecutionOrder(); + this._version++; + this.setDirtyCanvas(!0, !0); + return d; + } + }; + c.prototype.load = function(a) { + var b = this, d = new XMLHttpRequest; + d.open("GET", a, !0); + d.send(null); + d.onload = function(a) { + 200 !== d.status ? console.error("Error loading graph:", d.status, d.response) : (a = JSON.parse(d.response), b.configure(a)); + }; + d.onerror = function(a) { + console.error("Error loading graph:", a); + }; + }; + c.prototype.onNodeTrace = function(a, b, d) { + }; + r.prototype.configure = function(a) { + a.constructor === Array ? (this.id = a[0], this.origin_id = a[1], this.origin_slot = a[2], this.target_id = a[3], this.target_slot = a[4], this.type = a[5]) : (this.id = a.id, this.type = a.type, this.origin_id = a.origin_id, this.origin_slot = a.origin_slot, this.target_id = a.target_id, this.target_slot = a.target_slot); + }; + r.prototype.serialize = function() { + return [this.id, this.origin_id, this.origin_slot, this.target_id, this.target_slot, this.type]; + }; + e.LLink = r; + v.LGraphNode = e.LGraphNode = m; + m.prototype._ctor = function(a) { + this.title = a || "Unnamed"; + this.size = [e.NODE_WIDTH, 60]; + this.graph = null; + this._pos = new Float32Array(10, 10); + Object.defineProperty(this, "pos", {set:function(a) { + !a || 2 > a.length || (this._pos[0] = a[0], this._pos[1] = a[1]); + }, get:function() { + return this._pos; + }, enumerable:!0}); + this.id = -1; + this.type = null; + this.inputs = []; + this.outputs = []; + this.connections = []; + this.properties = {}; + this.properties_info = []; + this.flags = {}; + }; + m.prototype.configure = function(a) { + this.graph && this.graph._version++; + for (var b in a) { + if ("properties" == b) { + for (var d in a.properties) { + if (this.properties[d] = a.properties[d], this.onPropertyChanged) { + this.onPropertyChanged(d, a.properties[d]); + } + } + } else { + null != a[b] && ("object" == typeof a[b] ? this[b] && this[b].configure ? this[b].configure(a[b]) : this[b] = e.cloneObject(a[b], this[b]) : this[b] = a[b]); + } + } + a.title || (this.title = this.constructor.title); + if (this.onConnectionsChange) { + if (this.inputs) { + for (d = 0; d < this.inputs.length; ++d) { + b = this.inputs[d]; + var g = this.graph ? this.graph.links[b.link] : null; + this.onConnectionsChange(e.INPUT, d, !0, g, b); + } + } + if (this.outputs) { + for (d = 0; d < this.outputs.length; ++d) { + var f = this.outputs[d]; + if (f.links) { + for (b = 0; b < f.links.length; ++b) { + g = this.graph ? this.graph.links[f.links[b]] : null, this.onConnectionsChange(e.OUTPUT, d, !0, g, f); + } + } + } + } + } + if (this.widgets) { + for (d = 0; d < this.widgets.length; ++d) { + (b = this.widgets[d]) && b.options && b.options.property && this.properties[b.options.property] && (b.value = JSON.parse(JSON.stringify(this.properties[b.options.property]))); + } + if (a.widgets_values) { + for (d = 0; d < a.widgets_values.length; ++d) { + this.widgets[d] && (this.widgets[d].value = a.widgets_values[d]); + } + } + } + if (this.onConfigure) { + this.onConfigure(a); + } + }; + m.prototype.serialize = function() { + var a = {id:this.id, type:this.type, pos:this.pos, size:this.size, flags:e.cloneObject(this.flags), order:this.order, mode:this.mode}; + if (this.constructor === m && this.last_serialization) { + return this.last_serialization; + } + this.inputs && (a.inputs = this.inputs); + if (this.outputs) { + for (var b = 0; b < this.outputs.length; b++) { + delete this.outputs[b]._data; + } + a.outputs = this.outputs; + } + this.title && this.title != this.constructor.title && (a.title = this.title); + this.properties && (a.properties = e.cloneObject(this.properties)); + if (this.widgets && this.serialize_widgets) { + for (a.widgets_values = [], b = 0; b < this.widgets.length; ++b) { + a.widgets_values[b] = this.widgets[b] ? this.widgets[b].value : null; + } + } + a.type || (a.type = this.constructor.type); + this.color && (a.color = this.color); + this.bgcolor && (a.bgcolor = this.bgcolor); + this.boxcolor && (a.boxcolor = this.boxcolor); + this.shape && (a.shape = this.shape); + this.onSerialize && this.onSerialize(a) && console.warn("node onSerialize shouldnt return anything, data should be stored in the object pass in the first parameter"); + return a; + }; + m.prototype.clone = function() { + var a = e.createNode(this.type); + if (!a) { + return null; + } + var b = e.cloneObject(this.serialize()); + if (b.inputs) { + for (var d = 0; d < b.inputs.length; ++d) { + b.inputs[d].link = null; + } + } + if (b.outputs) { + for (d = 0; d < b.outputs.length; ++d) { + b.outputs[d].links && (b.outputs[d].links.length = 0); + } + } + delete b.id; + a.configure(b); + return a; + }; + m.prototype.toString = function() { + return JSON.stringify(this.serialize()); + }; + m.prototype.getTitle = function() { + return this.title || this.constructor.title; + }; + m.prototype.setProperty = function(a, b) { + this.properties || (this.properties = {}); + if (b !== this.properties[a]) { + var d = this.properties[a]; + this.properties[a] = b; + this.onPropertyChanged && !1 === this.onPropertyChanged(a, b, d) && (this.properties[a] = d); + if (this.widgets) { + for (d = 0; d < this.widgets.length; ++d) { + var g = this.widgets[d]; + if (g && g.options.property == a) { + g.value = b; + break; + } + } + } + } + }; + m.prototype.setOutputData = function(a, b) { + if (this.outputs && !(-1 == a || a >= this.outputs.length)) { + var d = this.outputs[a]; + if (d && (d._data = b, this.outputs[a].links)) { + for (d = 0; d < this.outputs[a].links.length; d++) { + var g = this.graph.links[this.outputs[a].links[d]]; + g && (g.data = b); + } + } + } + }; + m.prototype.setOutputDataType = function(a, b) { + if (this.outputs && !(-1 == a || a >= this.outputs.length)) { + var d = this.outputs[a]; + if (d && (d.type = b, this.outputs[a].links)) { + for (d = 0; d < this.outputs[a].links.length; d++) { + this.graph.links[this.outputs[a].links[d]].type = b; + } + } + } + }; + m.prototype.getInputData = function(a, b) { + if (this.inputs && !(a >= this.inputs.length || null == this.inputs[a].link)) { + a = this.graph.links[this.inputs[a].link]; + if (!a) { + return null; + } + if (!b) { + return a.data; + } + b = this.graph.getNodeById(a.origin_id); + if (!b) { + return a.data; + } + if (b.updateOutputData) { + b.updateOutputData(a.origin_slot); + } else { + if (b.onExecute) { + b.onExecute(); + } + } + return a.data; + } + }; + m.prototype.getInputDataType = function(a) { + if (!this.inputs || a >= this.inputs.length || null == this.inputs[a].link) { + return null; + } + a = this.graph.links[this.inputs[a].link]; + if (!a) { + return null; + } + var b = this.graph.getNodeById(a.origin_id); + return b ? (a = b.outputs[a.origin_slot]) ? a.type : null : a.type; + }; + m.prototype.getInputDataByName = function(a, b) { + a = this.findInputSlot(a); + return -1 == a ? null : this.getInputData(a, b); + }; + m.prototype.isInputConnected = function(a) { + return this.inputs ? a < this.inputs.length && null != this.inputs[a].link : !1; + }; + m.prototype.getInputInfo = function(a) { + return this.inputs ? a < this.inputs.length ? this.inputs[a] : null : null; + }; + m.prototype.getInputNode = function(a) { + if (!this.inputs || a >= this.inputs.length) { + return null; + } + a = this.inputs[a]; + return a && null !== a.link ? (a = this.graph.links[a.link]) ? this.graph.getNodeById(a.origin_id) : null : null; + }; + m.prototype.getInputOrProperty = function(a) { + if (!this.inputs || !this.inputs.length) { + return this.properties ? this.properties[a] : null; + } + for (var b = 0, d = this.inputs.length; b < d; ++b) { + var g = this.inputs[b]; + if (a == g.name && null != g.link && (g = this.graph.links[g.link])) { + return g.data; + } + } + return this.properties[a]; + }; + m.prototype.getOutputData = function(a) { + return !this.outputs || a >= this.outputs.length ? null : this.outputs[a]._data; + }; + m.prototype.getOutputInfo = function(a) { + return this.outputs ? a < this.outputs.length ? this.outputs[a] : null : null; + }; + m.prototype.isOutputConnected = function(a) { + return this.outputs ? a < this.outputs.length && this.outputs[a].links && this.outputs[a].links.length : !1; + }; + m.prototype.isAnyOutputConnected = function() { + if (!this.outputs) { + return !1; + } + for (var a = 0; a < this.outputs.length; ++a) { + if (this.outputs[a].links && this.outputs[a].links.length) { + return !0; + } + } + return !1; + }; + m.prototype.getOutputNodes = function(a) { + if (!this.outputs || 0 == this.outputs.length || a >= this.outputs.length) { + return null; + } + a = this.outputs[a]; + if (!a.links || 0 == a.links.length) { + return null; + } + for (var b = [], d = 0; d < a.links.length; d++) { + var g = this.graph.links[a.links[d]]; + g && (g = this.graph.getNodeById(g.target_id)) && b.push(g); + } + return b; + }; + m.prototype.trigger = function(a, b) { + if (this.outputs && this.outputs.length) { + this.graph && (this.graph._last_trigger_time = e.getTime()); + for (var d = 0; d < this.outputs.length; ++d) { + var g = this.outputs[d]; + !g || g.type !== e.EVENT || a && g.name != a || this.triggerSlot(d, b); + } + } + }; + m.prototype.triggerSlot = function(a, b, d) { + if (this.outputs && (a = this.outputs[a]) && (a = a.links) && a.length) { + this.graph && (this.graph._last_trigger_time = e.getTime()); + for (var g = 0; g < a.length; ++g) { + var f = a[g]; + if (null == d || d == f) { + var c = this.graph.links[a[g]]; + if (c && (c._last_time = e.getTime(), f = this.graph.getNodeById(c.target_id))) { + if (c = f.inputs[c.target_slot], f.onAction) { + f.onAction(c.name, b); + } else { + if (f.mode === e.ON_TRIGGER && f.onExecute) { + f.onExecute(b); + } + } + } + } + } + } + }; + m.prototype.clearTriggeredSlot = function(a, b) { + if (this.outputs && (a = this.outputs[a]) && (a = a.links) && a.length) { + for (var d = 0; d < a.length; ++d) { + var g = a[d]; + if (null == b || b == g) { + if (g = this.graph.links[a[d]]) { + g._last_time = 0; + } + } + } + } + }; + m.prototype.setSize = function(a) { + this.size = a; + if (this.onResize) { + this.onResize(this.size); + } + }; + m.prototype.addProperty = function(a, b, d, g) { + d = {name:a, type:d, default_value:b}; + if (g) { + for (var f in g) { + d[f] = g[f]; + } + } + this.properties_info || (this.properties_info = []); + this.properties_info.push(d); + this.properties || (this.properties = {}); + this.properties[a] = b; + return d; + }; + m.prototype.addOutput = function(a, b, d) { + a = {name:a, type:b, links:null}; + if (d) { + for (var g in d) { + a[g] = d[g]; + } + } + this.outputs || (this.outputs = []); + this.outputs.push(a); + if (this.onOutputAdded) { + this.onOutputAdded(a); + } + this.setSize(this.computeSize()); + this.setDirtyCanvas(!0, !0); + return a; + }; + m.prototype.addOutputs = function(a) { + for (var b = 0; b < a.length; ++b) { + var d = a[b], g = {name:d[0], type:d[1], link:null}; + if (a[2]) { + for (var f in d[2]) { + g[f] = d[2][f]; + } + } + this.outputs || (this.outputs = []); + this.outputs.push(g); + if (this.onOutputAdded) { + this.onOutputAdded(g); + } + } + this.setSize(this.computeSize()); + this.setDirtyCanvas(!0, !0); + }; + m.prototype.removeOutput = function(a) { + this.disconnectOutput(a); + this.outputs.splice(a, 1); + for (var b = a; b < this.outputs.length; ++b) { + if (this.outputs[b] && this.outputs[b].links) { + for (var d = this.outputs[b].links, g = 0; g < d.length; ++g) { + var f = this.graph.links[d[g]]; + f && --f.origin_slot; + } + } + } + this.setSize(this.computeSize()); + if (this.onOutputRemoved) { + this.onOutputRemoved(a); + } + this.setDirtyCanvas(!0, !0); + }; + m.prototype.addInput = function(a, b, d) { + a = {name:a, type:b || 0, link:null}; + if (d) { + for (var g in d) { + a[g] = d[g]; + } + } + this.inputs || (this.inputs = []); + this.inputs.push(a); + this.setSize(this.computeSize()); + if (this.onInputAdded) { + this.onInputAdded(a); + } + this.setDirtyCanvas(!0, !0); + return a; + }; + m.prototype.addInputs = function(a) { + for (var b = 0; b < a.length; ++b) { + var d = a[b], g = {name:d[0], type:d[1], link:null}; + if (a[2]) { + for (var f in d[2]) { + g[f] = d[2][f]; + } + } + this.inputs || (this.inputs = []); + this.inputs.push(g); + if (this.onInputAdded) { + this.onInputAdded(g); + } + } + this.setSize(this.computeSize()); + this.setDirtyCanvas(!0, !0); + }; + m.prototype.removeInput = function(a) { + this.disconnectInput(a); + this.inputs.splice(a, 1); + for (var b = a; b < this.inputs.length; ++b) { + if (this.inputs[b]) { + var d = this.graph.links[this.inputs[b].link]; + d && --d.target_slot; + } + } + this.setSize(this.computeSize()); + if (this.onInputRemoved) { + this.onInputRemoved(a); + } + this.setDirtyCanvas(!0, !0); + }; + m.prototype.addConnection = function(a, b, d, g) { + a = {name:a, type:b, pos:d, direction:g, links:null}; + this.connections.push(a); + return a; + }; + m.prototype.computeSize = function(a, b) { + function d(a) { + return a ? f * a.length * 0.6 : 0; + } + if (this.constructor.size) { + return this.constructor.size.concat(); + } + var g = Math.max(this.inputs ? this.inputs.length : 1, this.outputs ? this.outputs.length : 1); + b = b || new Float32Array([0, 0]); + g = Math.max(g, 1); + var f = e.NODE_TEXT_SIZE, c = d(this.title), w = 0, k = 0; + if (this.inputs) { + for (var n = 0, p = this.inputs.length; n < p; ++n) { + var l = this.inputs[n]; + l = l.label || l.name || ""; + l = d(l); + w < l && (w = l); + } + } + if (this.outputs) { + for (n = 0, p = this.outputs.length; n < p; ++n) { + l = this.outputs[n], l = l.label || l.name || "", l = d(l), k < l && (k = l); + } + } + b[0] = Math.max(w + k + 10, c); + b[0] = Math.max(b[0], e.NODE_WIDTH); + this.widgets && this.widgets.length && (b[0] = Math.max(b[0], 1.5 * e.NODE_WIDTH)); + b[1] = (this.constructor.slot_start_y || 0) + g * e.NODE_SLOT_HEIGHT; + g = 0; + if (this.widgets && this.widgets.length) { + n = 0; + for (p = this.widgets.length; n < p; ++n) { + g = this.widgets[n].computeSize ? g + (this.widgets[n].computeSize(Math.max(b[0], a || 0))[1] + 4) : g + (e.NODE_WIDGET_HEIGHT + 4); + } + g += 8; + } + b[1] = this.widgets_up ? Math.max(b[1], g) : null != this.widgets_start_y ? Math.max(b[1], g + this.widgets_start_y) : b[1] + g; + this.constructor.min_height && b[1] < this.constructor.min_height && (b[1] = this.constructor.min_height); + b[1] += 6; + return b; + }; + m.prototype.getPropertyInfo = function(a) { + var b = null; + if (this.properties_info) { + for (var d = 0; d < this.properties_info.length; ++d) { + if (this.properties_info[d].name == a) { + b = this.properties_info[d]; + break; + } + } + } + this.constructor["@" + a] && (b = this.constructor["@" + a]); + this.onGetPropertyInfo && (b = this.onGetPropertyInfo(a)); + b || (b = {}); + b.type || (b.type = typeof this.properties[a]); + return b; + }; + m.prototype.addWidget = function(a, b, d, g, f) { + this.widgets || (this.widgets = []); + !f && g && g.constructor === Object && (f = g, g = null); + f && f.constructor === String && (f = {property:f}); + g && g.constructor === String && (f || (f = {}), f.property = g, g = null); + g && g.constructor !== Function && (console.warn("addWidget: callback must be a function"), g = null); + b = {type:a.toLowerCase(), name:b, value:d, callback:g, options:f || {}}; + void 0 !== b.options.y && (b.y = b.options.y); + g || b.options.callback || b.options.property || console.warn("LiteGraph addWidget(...) without a callback or property assigned"); + if ("combo" == a && !b.options.values) { + throw "LiteGraph addWidget('combo',...) requires to pass values in options: { values:['red','blue'] }"; + } + this.widgets.push(b); + this.setSize(this.computeSize()); + return b; + }; + m.prototype.addCustomWidget = function(a) { + this.widgets || (this.widgets = []); + this.widgets.push(a); + return a; + }; + m.prototype.getBounding = function(a) { + a = a || new Float32Array(4); + a[0] = this.pos[0] - 4; + a[1] = this.pos[1] - e.NODE_TITLE_HEIGHT; + a[2] = this.size[0] + 4; + a[3] = this.size[1] + e.NODE_TITLE_HEIGHT; + if (this.onBounding) { + this.onBounding(a); + } + return a; + }; + m.prototype.isPointInside = function(a, b, d, g) { + d = d || 0; + var f = this.graph && this.graph.isLive() ? 0 : e.NODE_TITLE_HEIGHT; + g && (f = 0); + if (this.flags && this.flags.collapsed) { + if (y(a, b, this.pos[0] - d, this.pos[1] - e.NODE_TITLE_HEIGHT - d, (this._collapsed_width || e.NODE_COLLAPSED_WIDTH) + 2 * d, e.NODE_TITLE_HEIGHT + 2 * d)) { + return !0; + } + } else { + if (this.pos[0] - 4 - d < a && this.pos[0] + this.size[0] + 4 + d > a && this.pos[1] - f - d < b && this.pos[1] + this.size[1] + d > b) { + return !0; + } + } + return !1; + }; + m.prototype.getSlotInPosition = function(a, b) { + var d = new Float32Array(2); + if (this.inputs) { + for (var g = 0, f = this.inputs.length; g < f; ++g) { + var e = this.inputs[g]; + this.getConnectionPos(!0, g, d); + if (y(a, b, d[0] - 10, d[1] - 5, 20, 10)) { + return {input:e, slot:g, link_pos:d}; + } + } + } + if (this.outputs) { + for (g = 0, f = this.outputs.length; g < f; ++g) { + if (e = this.outputs[g], this.getConnectionPos(!1, g, d), y(a, b, d[0] - 10, d[1] - 5, 20, 10)) { + return {output:e, slot:g, link_pos:d}; + } + } + } + return null; + }; + m.prototype.findInputSlot = function(a) { + if (!this.inputs) { + return -1; + } + for (var b = 0, d = this.inputs.length; b < d; ++b) { + if (a == this.inputs[b].name) { + return b; + } + } + return -1; + }; + m.prototype.findOutputSlot = function(a) { + if (!this.outputs) { + return -1; + } + for (var b = 0, d = this.outputs.length; b < d; ++b) { + if (a == this.outputs[b].name) { + return b; + } + } + return -1; + }; + m.prototype.connect = function(a, b, d) { + d = d || 0; + if (!this.graph) { + return console.log("Connect: Error, node doesn't belong to any graph. Nodes must be added first to a graph before connecting them."), null; + } + if (a.constructor === String) { + if (a = this.findOutputSlot(a), -1 == a) { + return e.debug && console.log("Connect: Error, no slot of name " + a), null; + } + } else { + if (!this.outputs || a >= this.outputs.length) { + return e.debug && console.log("Connect: Error, slot number not found"), null; + } + } + b && b.constructor === Number && (b = this.graph.getNodeById(b)); + if (!b) { + throw "target node is null"; + } + if (b == this) { + return null; + } + if (d.constructor === String) { + if (d = b.findInputSlot(d), -1 == d) { + return e.debug && console.log("Connect: Error, no slot of name " + d), null; + } + } else { + if (d === e.EVENT) { + return null; + } + if (!b.inputs || d >= b.inputs.length) { + return e.debug && console.log("Connect: Error, slot number not found"), null; + } + } + null != b.inputs[d].link && b.disconnectInput(d); + var g = this.outputs[a]; + if (b.onConnectInput && !1 === b.onConnectInput(d, g.type, g, this, a)) { + return null; + } + var f = b.inputs[d], c = null; + if (e.isValidConnection(g.type, f.type)) { + c = new r(++this.graph.last_link_id, f.type, this.id, a, b.id, d); + this.graph.links[c.id] = c; + null == g.links && (g.links = []); + g.links.push(c.id); + b.inputs[d].link = c.id; + this.graph && this.graph._version++; + if (this.onConnectionsChange) { + this.onConnectionsChange(e.OUTPUT, a, !0, c, g); + } + if (b.onConnectionsChange) { + b.onConnectionsChange(e.INPUT, d, !0, c, f); + } + this.graph && this.graph.onNodeConnectionChange && (this.graph.onNodeConnectionChange(e.INPUT, b, d, this, a), this.graph.onNodeConnectionChange(e.OUTPUT, this, a, b, d)); + } + this.setDirtyCanvas(!1, !0); + this.graph.connectionChange(this, c); + return c; + }; + m.prototype.disconnectOutput = function(a, b) { + if (a.constructor === String) { + if (a = this.findOutputSlot(a), -1 == a) { + return e.debug && console.log("Connect: Error, no slot of name " + a), !1; + } + } else { + if (!this.outputs || a >= this.outputs.length) { + return e.debug && console.log("Connect: Error, slot number not found"), !1; + } + } + var d = this.outputs[a]; + if (!d || !d.links || 0 == d.links.length) { + return !1; + } + if (b) { + b.constructor === Number && (b = this.graph.getNodeById(b)); + if (!b) { + throw "Target Node not found"; + } + for (var g = 0, f = d.links.length; g < f; g++) { + var c = d.links[g], w = this.graph.links[c]; + if (w.target_id == b.id) { + d.links.splice(g, 1); + var k = b.inputs[w.target_slot]; + k.link = null; + delete this.graph.links[c]; + this.graph && this.graph._version++; + if (b.onConnectionsChange) { + b.onConnectionsChange(e.INPUT, w.target_slot, !1, w, k); + } + if (this.onConnectionsChange) { + this.onConnectionsChange(e.OUTPUT, a, !1, w, d); + } + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange(e.OUTPUT, this, a); + } + this.graph && this.graph.onNodeConnectionChange && (this.graph.onNodeConnectionChange(e.OUTPUT, this, a), this.graph.onNodeConnectionChange(e.INPUT, b, w.target_slot)); + break; + } + } + } else { + g = 0; + for (f = d.links.length; g < f; g++) { + if (c = d.links[g], w = this.graph.links[c]) { + b = this.graph.getNodeById(w.target_id); + this.graph && this.graph._version++; + if (b) { + k = b.inputs[w.target_slot]; + k.link = null; + if (b.onConnectionsChange) { + b.onConnectionsChange(e.INPUT, w.target_slot, !1, w, k); + } + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange(e.INPUT, b, w.target_slot); + } + } + delete this.graph.links[c]; + if (this.onConnectionsChange) { + this.onConnectionsChange(e.OUTPUT, a, !1, w, d); + } + this.graph && this.graph.onNodeConnectionChange && (this.graph.onNodeConnectionChange(e.OUTPUT, this, a), this.graph.onNodeConnectionChange(e.INPUT, b, w.target_slot)); + } + } + d.links = null; + } + this.setDirtyCanvas(!1, !0); + this.graph.connectionChange(this); + return !0; + }; + m.prototype.disconnectInput = function(a) { + if (a.constructor === String) { + if (a = this.findInputSlot(a), -1 == a) { + return e.debug && console.log("Connect: Error, no slot of name " + a), !1; + } + } else { + if (!this.inputs || a >= this.inputs.length) { + return e.debug && console.log("Connect: Error, slot number not found"), !1; + } + } + var b = this.inputs[a]; + if (!b) { + return !1; + } + var d = this.inputs[a].link; + this.inputs[a].link = null; + var g = this.graph.links[d]; + if (g) { + var f = this.graph.getNodeById(g.origin_id); + if (!f) { + return !1; + } + var c = f.outputs[g.origin_slot]; + if (!c || !c.links || 0 == c.links.length) { + return !1; + } + for (var k = 0, n = c.links.length; k < n; k++) { + if (c.links[k] == d) { + c.links.splice(k, 1); + break; + } + } + delete this.graph.links[d]; + this.graph && this.graph._version++; + if (this.onConnectionsChange) { + this.onConnectionsChange(e.INPUT, a, !1, g, b); + } + if (f.onConnectionsChange) { + f.onConnectionsChange(e.OUTPUT, k, !1, g, c); + } + this.graph && this.graph.onNodeConnectionChange && (this.graph.onNodeConnectionChange(e.OUTPUT, f, k), this.graph.onNodeConnectionChange(e.INPUT, this, a)); + } + this.setDirtyCanvas(!1, !0); + this.graph.connectionChange(this); + return !0; + }; + m.prototype.getConnectionPos = function(a, b, d) { + d = d || new Float32Array(2); + var g = 0; + a && this.inputs && (g = this.inputs.length); + !a && this.outputs && (g = this.outputs.length); + var f = 0.5 * e.NODE_SLOT_HEIGHT; + if (this.flags.collapsed) { + return b = this._collapsed_width || e.NODE_COLLAPSED_WIDTH, this.horizontal ? (d[0] = this.pos[0] + 0.5 * b, d[1] = a ? this.pos[1] - e.NODE_TITLE_HEIGHT : this.pos[1]) : (d[0] = a ? this.pos[0] : this.pos[0] + b, d[1] = this.pos[1] - 0.5 * e.NODE_TITLE_HEIGHT), d; + } + if (a && -1 == b) { + return d[0] = this.pos[0] + 0.5 * e.NODE_TITLE_HEIGHT, d[1] = this.pos[1] + 0.5 * e.NODE_TITLE_HEIGHT, d; + } + if (a && g > b && this.inputs[b].pos) { + return d[0] = this.pos[0] + this.inputs[b].pos[0], d[1] = this.pos[1] + this.inputs[b].pos[1], d; + } + if (!a && g > b && this.outputs[b].pos) { + return d[0] = this.pos[0] + this.outputs[b].pos[0], d[1] = this.pos[1] + this.outputs[b].pos[1], d; + } + if (this.horizontal) { + return d[0] = this.pos[0] + this.size[0] / g * (b + 0.5), d[1] = a ? this.pos[1] - e.NODE_TITLE_HEIGHT : this.pos[1] + this.size[1], d; + } + d[0] = a ? this.pos[0] + f : this.pos[0] + this.size[0] + 1 - f; + d[1] = this.pos[1] + (b + 0.7) * e.NODE_SLOT_HEIGHT + (this.constructor.slot_start_y || 0); + return d; + }; + m.prototype.alignToGrid = function() { + this.pos[0] = e.CANVAS_GRID_SIZE * Math.round(this.pos[0] / e.CANVAS_GRID_SIZE); + this.pos[1] = e.CANVAS_GRID_SIZE * Math.round(this.pos[1] / e.CANVAS_GRID_SIZE); + }; + m.prototype.trace = function(a) { + this.console || (this.console = []); + this.console.push(a); + this.console.length > m.MAX_CONSOLE && this.console.shift(); + this.graph.onNodeTrace(this, a); + }; + m.prototype.setDirtyCanvas = function(a, b) { + this.graph && this.graph.sendActionToCanvas("setDirty", [a, b]); + }; + m.prototype.loadImage = function(a) { + var b = new Image; + b.src = e.node_images_path + a; + b.ready = !1; + var d = this; + b.onload = function() { + this.ready = !0; + d.setDirtyCanvas(!0); + }; + return b; + }; + m.prototype.captureInput = function(a) { + if (this.graph && this.graph.list_of_graphcanvas) { + for (var b = this.graph.list_of_graphcanvas, d = 0; d < b.length; ++d) { + var g = b[d]; + if (a || g.node_capturing_input == this) { + g.node_capturing_input = a ? this : null; + } + } + } + }; + m.prototype.collapse = function(a) { + this.graph._version++; + if (!1 !== this.constructor.collapsable || a) { + this.flags.collapsed = this.flags.collapsed ? !1 : !0, this.setDirtyCanvas(!0, !0); + } + }; + m.prototype.pin = function(a) { + this.graph._version++; + this.flags.pinned = void 0 === a ? !this.flags.pinned : a; + }; + m.prototype.localToScreen = function(a, b, d) { + return [(a + this.pos[0]) * d.scale + d.offset[0], (b + this.pos[1]) * d.scale + d.offset[1]]; + }; + v.LGraphGroup = e.LGraphGroup = h; + h.prototype._ctor = function(a) { + this.title = a || "Group"; + this.font_size = 24; + this.color = l.node_colors.pale_blue ? l.node_colors.pale_blue.groupcolor : "#AAA"; + this._bounding = new Float32Array([10, 10, 140, 80]); + this._pos = this._bounding.subarray(0, 2); + this._size = this._bounding.subarray(2, 4); + this._nodes = []; + this.graph = null; + Object.defineProperty(this, "pos", {set:function(a) { + !a || 2 > a.length || (this._pos[0] = a[0], this._pos[1] = a[1]); + }, get:function() { + return this._pos; + }, enumerable:!0}); + Object.defineProperty(this, "size", {set:function(a) { + !a || 2 > a.length || (this._size[0] = Math.max(140, a[0]), this._size[1] = Math.max(80, a[1])); + }, get:function() { + return this._size; + }, enumerable:!0}); + }; + h.prototype.configure = function(a) { + this.title = a.title; + this._bounding.set(a.bounding); + this.color = a.color; + this.font = a.font; + }; + h.prototype.serialize = function() { + var a = this._bounding; + return {title:this.title, bounding:[Math.round(a[0]), Math.round(a[1]), Math.round(a[2]), Math.round(a[3])], color:this.color, font:this.font}; + }; + h.prototype.move = function(a, b, d) { + this._pos[0] += a; + this._pos[1] += b; + if (!d) { + for (d = 0; d < this._nodes.length; ++d) { + var g = this._nodes[d]; + g.pos[0] += a; + g.pos[1] += b; + } + } + }; + h.prototype.recomputeInsideNodes = function() { + this._nodes.length = 0; + for (var a = this.graph._nodes, b = new Float32Array(4), d = 0; d < a.length; ++d) { + var g = a[d]; + g.getBounding(b); + x(this._bounding, b) && this._nodes.push(g); + } + }; + h.prototype.isPointInside = m.prototype.isPointInside; + h.prototype.setDirtyCanvas = m.prototype.setDirtyCanvas; + e.DragAndScale = q; + q.prototype.bindEvents = function(a) { + this.last_mouse = new Float32Array(2); + this._binded_mouse_callback = this.onMouse.bind(this); + a.addEventListener("mousedown", this._binded_mouse_callback); + a.addEventListener("mousemove", this._binded_mouse_callback); + a.addEventListener("mousewheel", this._binded_mouse_callback, !1); + a.addEventListener("wheel", this._binded_mouse_callback, !1); + }; + q.prototype.computeVisibleArea = function() { + if (this.element) { + var a = -this.offset[0], b = -this.offset[1], d = a + this.element.width / this.scale, g = b + this.element.height / this.scale; + this.visible_area[0] = a; + this.visible_area[1] = b; + this.visible_area[2] = d - a; + this.visible_area[3] = g - b; + } else { + this.visible_area[0] = this.visible_area[1] = this.visible_area[2] = this.visible_area[3] = 0; + } + }; + q.prototype.onMouse = function(a) { + if (this.enabled) { + var b = this.element, d = b.getBoundingClientRect(), g = a.clientX - d.left; + d = a.clientY - d.top; + a.canvasx = g; + a.canvasy = d; + a.dragging = this.dragging; + var f = !1; + this.onmouse && (f = this.onmouse(a)); + if ("mousedown" == a.type) { + this.dragging = !0, b.removeEventListener("mousemove", this._binded_mouse_callback), document.body.addEventListener("mousemove", this._binded_mouse_callback), document.body.addEventListener("mouseup", this._binded_mouse_callback); + } else { + if ("mousemove" == a.type) { + f || (b = g - this.last_mouse[0], f = d - this.last_mouse[1], this.dragging && this.mouseDrag(b, f)); + } else { + if ("mouseup" == a.type) { + this.dragging = !1, document.body.removeEventListener("mousemove", this._binded_mouse_callback), document.body.removeEventListener("mouseup", this._binded_mouse_callback), b.addEventListener("mousemove", this._binded_mouse_callback); + } else { + if ("mousewheel" == a.type || "wheel" == a.type || "DOMMouseScroll" == a.type) { + a.eventType = "mousewheel", a.wheel = "wheel" == a.type ? -a.deltaY : null != a.wheelDeltaY ? a.wheelDeltaY : -60 * a.detail, a.delta = a.wheelDelta ? a.wheelDelta / 40 : a.deltaY ? -a.deltaY / 3 : 0, this.changeDeltaScale(1.0 + 0.05 * a.delta); + } + } + } + } + this.last_mouse[0] = g; + this.last_mouse[1] = d; + a.preventDefault(); + a.stopPropagation(); + return !1; + } + }; + q.prototype.toCanvasContext = function(a) { + a.scale(this.scale, this.scale); + a.translate(this.offset[0], this.offset[1]); + }; + q.prototype.convertOffsetToCanvas = function(a) { + return [(a[0] + this.offset[0]) * this.scale, (a[1] + this.offset[1]) * this.scale]; + }; + q.prototype.convertCanvasToOffset = function(a, b) { + b = b || [0, 0]; + b[0] = a[0] / this.scale - this.offset[0]; + b[1] = a[1] / this.scale - this.offset[1]; + return b; + }; + q.prototype.mouseDrag = function(a, b) { + this.offset[0] += a / this.scale; + this.offset[1] += b / this.scale; + if (this.onredraw) { + this.onredraw(this); + } + }; + q.prototype.changeScale = function(a, b) { + a < this.min_scale ? a = this.min_scale : a > this.max_scale && (a = this.max_scale); + if (a != this.scale && this.element) { + var d = this.element.getBoundingClientRect(); + if (d && (b = b || [0.5 * d.width, 0.5 * d.height], d = this.convertCanvasToOffset(b), this.scale = a, 0.01 > Math.abs(this.scale - 1) && (this.scale = 1), a = this.convertCanvasToOffset(b), a = [a[0] - d[0], a[1] - d[1]], this.offset[0] += a[0], this.offset[1] += a[1], this.onredraw)) { + this.onredraw(this); + } + } + }; + q.prototype.changeDeltaScale = function(a, b) { + this.changeScale(this.scale * a, b); + }; + q.prototype.reset = function() { + this.scale = 1; + this.offset[0] = 0; + this.offset[1] = 0; + }; + v.LGraphCanvas = e.LGraphCanvas = l; + l.link_type_colors = {"-1":e.EVENT_LINK_COLOR, number:"#AAA", node:"#DCA"}; + l.gradients = {}; + l.prototype.clear = function() { + this.fps = this.render_time = this.last_draw_time = this.frame = 0; + this.dragging_rectangle = null; + this.selected_nodes = {}; + this.selected_group = null; + this.visible_nodes = []; + this.connecting_node = this.node_capturing_input = this.node_over = this.node_dragged = null; + this.highlighted_links = {}; + this.dirty_bgcanvas = this.dirty_canvas = !0; + this.node_widget = this.node_in_panel = this.dirty_area = null; + this.last_mouse = [0, 0]; + this.last_mouseclick = 0; + this.visible_area.set([0, 0, 0, 0]); + if (this.onClear) { + this.onClear(); + } + }; + l.prototype.setGraph = function(a, b) { + this.graph != a && (b || this.clear(), !a && this.graph ? this.graph.detachCanvas(this) : (a.attachCanvas(this), this._graph_stack && (this._graph_stack = null), this.setDirty(!0, !0))); + }; + l.prototype.openSubgraph = function(a) { + if (!a) { + throw "graph cannot be null"; + } + if (this.graph == a) { + throw "graph cannot be the same"; + } + this.clear(); + this.graph && (this._graph_stack || (this._graph_stack = []), this._graph_stack.push(this.graph)); + a.attachCanvas(this); + this.setDirty(!0, !0); + }; + l.prototype.closeSubgraph = function() { + if (this._graph_stack && 0 != this._graph_stack.length) { + var a = this.graph._subgraph_node, b = this._graph_stack.pop(); + this.selected_nodes = {}; + this.highlighted_links = {}; + b.attachCanvas(this); + this.setDirty(!0, !0); + a && (this.centerOnNode(a), this.selectNodes([a])); + } + }; + l.prototype.getCurrentGraph = function() { + return this.graph; + }; + l.prototype.setCanvas = function(a, b) { + if (a && a.constructor === String && (a = document.getElementById(a), !a)) { + throw "Error creating LiteGraph canvas: Canvas not found"; + } + if (a !== this.canvas && (!a && this.canvas && (b || this.unbindEvents()), this.canvas = a, this.ds.element = a)) { + a.className += " lgraphcanvas"; + a.data = this; + a.tabindex = "1"; + this.bgcanvas = null; + this.bgcanvas || (this.bgcanvas = document.createElement("canvas"), this.bgcanvas.width = this.canvas.width, this.bgcanvas.height = this.canvas.height); + if (null == a.getContext) { + if ("canvas" != a.localName) { + throw "Element supplied for LGraphCanvas must be a element, you passed a " + a.localName; + } + throw "This browser doesn't support Canvas"; + } + null == (this.ctx = a.getContext("2d")) && (a.webgl_enabled || console.warn("This canvas seems to be WebGL, enabling WebGL renderer"), this.enableWebGL()); + this._mousemove_callback = this.processMouseMove.bind(this); + this._mouseup_callback = this.processMouseUp.bind(this); + b || this.bindEvents(); + } + }; + l.prototype._doNothing = function(a) { + a.preventDefault(); + return !1; + }; + l.prototype._doReturnTrue = function(a) { + a.preventDefault(); + return !0; + }; + l.prototype.bindEvents = function() { + if (this._events_binded) { + console.warn("LGraphCanvas: events already binded"); + } else { + var a = this.canvas, b = this.getCanvasWindow().document; + this._mousedown_callback = this.processMouseDown.bind(this); + this._mousewheel_callback = this.processMouseWheel.bind(this); + a.addEventListener("mousedown", this._mousedown_callback, !0); + a.addEventListener("mousemove", this._mousemove_callback); + a.addEventListener("mousewheel", this._mousewheel_callback, !1); + a.addEventListener("contextmenu", this._doNothing); + a.addEventListener("DOMMouseScroll", this._mousewheel_callback, !1); + a.addEventListener("touchstart", this.touchHandler, !0); + a.addEventListener("touchmove", this.touchHandler, !0); + a.addEventListener("touchend", this.touchHandler, !0); + a.addEventListener("touchcancel", this.touchHandler, !0); + this._key_callback = this.processKey.bind(this); + a.addEventListener("keydown", this._key_callback, !0); + b.addEventListener("keyup", this._key_callback, !0); + this._ondrop_callback = this.processDrop.bind(this); + a.addEventListener("dragover", this._doNothing, !1); + a.addEventListener("dragend", this._doNothing, !1); + a.addEventListener("drop", this._ondrop_callback, !1); + a.addEventListener("dragenter", this._doReturnTrue, !1); + this._events_binded = !0; + } + }; + l.prototype.unbindEvents = function() { + if (this._events_binded) { + var a = this.getCanvasWindow().document; + this.canvas.removeEventListener("mousedown", this._mousedown_callback); + this.canvas.removeEventListener("mousewheel", this._mousewheel_callback); + this.canvas.removeEventListener("DOMMouseScroll", this._mousewheel_callback); + this.canvas.removeEventListener("keydown", this._key_callback); + a.removeEventListener("keyup", this._key_callback); + this.canvas.removeEventListener("contextmenu", this._doNothing); + this.canvas.removeEventListener("drop", this._ondrop_callback); + this.canvas.removeEventListener("dragenter", this._doReturnTrue); + this.canvas.removeEventListener("touchstart", this.touchHandler); + this.canvas.removeEventListener("touchmove", this.touchHandler); + this.canvas.removeEventListener("touchend", this.touchHandler); + this.canvas.removeEventListener("touchcancel", this.touchHandler); + this._ondrop_callback = this._key_callback = this._mousewheel_callback = this._mousedown_callback = null; + this._events_binded = !1; + } else { + console.warn("LGraphCanvas: no events binded"); + } + }; + l.getFileExtension = function(a) { + var b = a.indexOf("?"); + -1 != b && (a = a.substr(0, b)); + b = a.lastIndexOf("."); + return -1 == b ? "" : a.substr(b + 1).toLowerCase(); + }; + l.prototype.enableWebGL = function() { + this.gl = this.ctx = enableWebGLCanvas(this.canvas); + this.ctx.webgl = !0; + this.bgcanvas = this.canvas; + this.bgctx = this.gl; + this.canvas.webgl_enabled = !0; + }; + l.prototype.setDirty = function(a, b) { + a && (this.dirty_canvas = !0); + b && (this.dirty_bgcanvas = !0); + }; + l.prototype.getCanvasWindow = function() { + if (!this.canvas) { + return window; + } + var a = this.canvas.ownerDocument; + return a.defaultView || a.parentWindow; + }; + l.prototype.startRendering = function() { + function a() { + this.pause_rendering || this.draw(); + var b = this.getCanvasWindow(); + this.is_rendering && b.requestAnimationFrame(a.bind(this)); + } + this.is_rendering || (this.is_rendering = !0, a.call(this)); + }; + l.prototype.stopRendering = function() { + this.is_rendering = !1; + }; + l.prototype.processMouseDown = function(a) { + if (this.graph) { + this.adjustMouseEvent(a); + var b = this.getCanvasWindow(); + l.active_canvas = this; + this.canvas.removeEventListener("mousemove", this._mousemove_callback); + b.document.addEventListener("mousemove", this._mousemove_callback, !0); + b.document.addEventListener("mouseup", this._mouseup_callback, !0); + var d = this.graph.getNodeOnPos(a.canvasX, a.canvasY, this.visible_nodes, 5), g = !1, f = 300 > e.getTime() - this.last_mouseclick; + this.canvas_mouse[0] = a.canvasX; + this.canvas_mouse[1] = a.canvasY; + this.canvas.focus(); + e.closeAllContextMenus(b); + if (!this.onMouse || 1 != this.onMouse(a)) { + if (1 == a.which) { + a.ctrlKey && (this.dragging_rectangle = new Float32Array(4), this.dragging_rectangle[0] = a.canvasX, this.dragging_rectangle[1] = a.canvasY, this.dragging_rectangle[2] = 1, this.dragging_rectangle[3] = 1, g = !0); + var c = !1; + if (d && this.allow_interaction && !g && !this.read_only) { + this.live_mode || d.flags.pinned || this.bringToFront(d); + if (!this.connecting_node && !d.flags.collapsed && !this.live_mode) { + if (!g && !1 !== d.resizable && y(a.canvasX, a.canvasY, d.pos[0] + d.size[0] - 5, d.pos[1] + d.size[1] - 5, 10, 10)) { + this.resizing_node = d, this.canvas.style.cursor = "se-resize", g = !0; + } else { + if (d.outputs) { + for (var k = 0, n = d.outputs.length; k < n; ++k) { + var p = d.outputs[k], h = d.getConnectionPos(!1, k); + if (y(a.canvasX, a.canvasY, h[0] - 15, h[1] - 10, 30, 20)) { + this.connecting_node = d; + this.connecting_output = p; + this.connecting_pos = d.getConnectionPos(!1, k); + this.connecting_slot = k; + a.shiftKey && d.disconnectOutput(k); + if (f) { + if (d.onOutputDblClick) { + d.onOutputDblClick(k, a); + } + } else { + if (d.onOutputClick) { + d.onOutputClick(k, a); + } + } + g = !0; + break; + } + } + } + if (d.inputs) { + for (k = 0, n = d.inputs.length; k < n; ++k) { + if (p = d.inputs[k], h = d.getConnectionPos(!0, k), y(a.canvasX, a.canvasY, h[0] - 15, h[1] - 10, 30, 20)) { + if (f) { + if (d.onInputDblClick) { + d.onInputDblClick(k, a); + } + } else { + if (d.onInputClick) { + d.onInputClick(k, a); + } + } + if (null !== p.link) { + g = this.graph.links[p.link]; + d.disconnectInput(k); + if (this.allow_reconnect_links || a.shiftKey) { + this.connecting_node = this.graph._nodes_by_id[g.origin_id], this.connecting_slot = g.origin_slot, this.connecting_output = this.connecting_node.outputs[this.connecting_slot], this.connecting_pos = this.connecting_node.getConnectionPos(!1, this.connecting_slot); + } + g = this.dirty_bgcanvas = !0; + } + } + } + } + } + } + if (!g) { + k = !1; + if (n = this.processNodeWidgets(d, this.canvas_mouse, a)) { + k = !0, this.node_widget = [d, n]; + } + if (f && this.selected_nodes[d.id]) { + if (d.onDblClick) { + d.onDblClick(a, [a.canvasX - d.pos[0], a.canvasY - d.pos[1]], this); + } + this.processNodeDblClicked(d); + k = !0; + } + d.onMouseDown && d.onMouseDown(a, [a.canvasX - d.pos[0], a.canvasY - d.pos[1]], this) ? k = !0 : this.live_mode && (k = c = !0); + k || (this.allow_dragnodes && (this.node_dragged = d), this.selected_nodes[d.id] || this.processNodeSelected(d, a)); + this.dirty_canvas = !0; + } + } else { + if (!this.read_only) { + for (k = 0; k < this.visible_links.length; ++k) { + if (d = this.visible_links[k], c = d._pos, !(!c || a.canvasX < c[0] - 4 || a.canvasX > c[0] + 4 || a.canvasY < c[1] - 4 || a.canvasY > c[1] + 4)) { + this.showLinkMenu(d, a); + this.over_link_center = null; + break; + } + } + } + this.selected_group = this.graph.getGroupOnPos(a.canvasX, a.canvasY); + this.selected_group_resizing = !1; + this.selected_group && !this.read_only && (a.ctrlKey && (this.dragging_rectangle = null), 10 > A([a.canvasX, a.canvasY], [this.selected_group.pos[0] + this.selected_group.size[0], this.selected_group.pos[1] + this.selected_group.size[1]]) * this.ds.scale ? this.selected_group_resizing = !0 : this.selected_group.recomputeInsideNodes()); + f && !this.read_only && this.allow_searchbox && this.showSearchBox(a); + c = !0; + } + !g && c && this.allow_dragcanvas && (this.dragging_canvas = !0); + } else { + 2 != a.which && 3 == a.which && (this.read_only || this.processContextMenu(d, a)); + } + this.last_mouse[0] = a.localX; + this.last_mouse[1] = a.localY; + this.last_mouseclick = e.getTime(); + this.last_mouse_dragging = !0; + this.graph.change(); + (!b.document.activeElement || "input" != b.document.activeElement.nodeName.toLowerCase() && "textarea" != b.document.activeElement.nodeName.toLowerCase()) && a.preventDefault(); + a.stopPropagation(); + if (this.onMouseDown) { + this.onMouseDown(a); + } + return !1; + } + } + }; + l.prototype.processMouseMove = function(a) { + this.autoresize && this.resize(); + if (this.graph) { + l.active_canvas = this; + this.adjustMouseEvent(a); + var b = [a.localX, a.localY], d = [b[0] - this.last_mouse[0], b[1] - this.last_mouse[1]]; + this.last_mouse = b; + this.canvas_mouse[0] = a.canvasX; + this.canvas_mouse[1] = a.canvasY; + a.dragging = this.last_mouse_dragging; + this.node_widget && (this.processNodeWidgets(this.node_widget[0], this.canvas_mouse, a, this.node_widget[1]), this.dirty_canvas = !0); + if (this.dragging_rectangle) { + this.dragging_rectangle[2] = a.canvasX - this.dragging_rectangle[0], this.dragging_rectangle[3] = a.canvasY - this.dragging_rectangle[1], this.dirty_canvas = !0; + } else { + if (this.selected_group && !this.read_only) { + this.selected_group_resizing ? this.selected_group.size = [a.canvasX - this.selected_group.pos[0], a.canvasY - this.selected_group.pos[1]] : (this.selected_group.move(d[0] / this.ds.scale, d[1] / this.ds.scale, a.ctrlKey), this.selected_group._nodes.length && (this.dirty_canvas = !0)), this.dirty_bgcanvas = !0; + } else { + if (this.dragging_canvas) { + this.ds.offset[0] += d[0] / this.ds.scale, this.ds.offset[1] += d[1] / this.ds.scale, this.dirty_bgcanvas = this.dirty_canvas = !0; + } else { + if (this.allow_interaction && !this.read_only) { + this.connecting_node && (this.dirty_canvas = !0); + var g = this.graph.getNodeOnPos(a.canvasX, a.canvasY, this.visible_nodes); + b = 0; + for (var f = this.graph._nodes.length; b < f; ++b) { + if (this.graph._nodes[b].mouseOver && g != this.graph._nodes[b]) { + this.graph._nodes[b].mouseOver = !1; + if (this.node_over && this.node_over.onMouseLeave) { + this.node_over.onMouseLeave(a); + } + this.node_over = null; + this.dirty_canvas = !0; + } + } + if (g) { + if (!g.mouseOver && (g.mouseOver = !0, this.node_over = g, this.dirty_canvas = !0, g.onMouseEnter)) { + g.onMouseEnter(a); + } + if (g.onMouseMove) { + g.onMouseMove(a, [a.canvasX - g.pos[0], a.canvasY - g.pos[1]], this); + } + if (this.connecting_node && (f = this._highlight_input || [0, 0], !this.isOverNodeBox(g, a.canvasX, a.canvasY))) { + var c = this.isOverNodeInput(g, a.canvasX, a.canvasY, f); + -1 != c && g.inputs[c] ? e.isValidConnection(this.connecting_output.type, g.inputs[c].type) && (this._highlight_input = f) : this._highlight_input = null; + } + this.canvas && (y(a.canvasX, a.canvasY, g.pos[0] + g.size[0] - 5, g.pos[1] + g.size[1] - 5, 5, 5) ? this.canvas.style.cursor = "se-resize" : this.canvas.style.cursor = "crosshair"); + } else { + f = null; + for (b = 0; b < this.visible_links.length; ++b) { + c = this.visible_links[b]; + var k = c._pos; + if (!(!k || a.canvasX < k[0] - 4 || a.canvasX > k[0] + 4 || a.canvasY < k[1] - 4 || a.canvasY > k[1] + 4)) { + f = c; + break; + } + } + f != this.over_link_center && (this.over_link_center = f, this.dirty_canvas = !0); + this.canvas && (this.canvas.style.cursor = ""); + } + if (this.node_capturing_input && this.node_capturing_input != g && this.node_capturing_input.onMouseMove) { + this.node_capturing_input.onMouseMove(a, [a.canvasX - this.node_capturing_input.pos[0], a.canvasY - this.node_capturing_input.pos[1]], this); + } + if (this.node_dragged && !this.live_mode) { + for (b in this.selected_nodes) { + g = this.selected_nodes[b], g.pos[0] += d[0] / this.ds.scale, g.pos[1] += d[1] / this.ds.scale; + } + this.dirty_bgcanvas = this.dirty_canvas = !0; + } + this.resizing_node && !this.live_mode && (d = [a.canvasX - this.resizing_node.pos[0], a.canvasY - this.resizing_node.pos[1]], b = this.resizing_node.computeSize(), d[0] = Math.max(b[0], d[0]), d[1] = Math.max(b[1], d[1]), this.resizing_node.setSize(d), this.canvas.style.cursor = "se-resize", this.dirty_bgcanvas = this.dirty_canvas = !0); + } + } + } + } + a.preventDefault(); + return !1; + } + }; + l.prototype.processMouseUp = function(a) { + if (this.graph) { + var b = this.getCanvasWindow().document; + l.active_canvas = this; + b.removeEventListener("mousemove", this._mousemove_callback, !0); + this.canvas.addEventListener("mousemove", this._mousemove_callback, !0); + b.removeEventListener("mouseup", this._mouseup_callback, !0); + this.adjustMouseEvent(a); + b = e.getTime(); + a.click_time = b - this.last_mouseclick; + this.last_mouse_dragging = !1; + if (1 == a.which) { + if (this.node_widget && this.processNodeWidgets(this.node_widget[0], this.canvas_mouse, a), this.node_widget = null, this.selected_group && (this.selected_group.move(this.selected_group.pos[0] - Math.round(this.selected_group.pos[0]), this.selected_group.pos[1] - Math.round(this.selected_group.pos[1]), a.ctrlKey), this.selected_group.pos[0] = Math.round(this.selected_group.pos[0]), this.selected_group.pos[1] = Math.round(this.selected_group.pos[1]), this.selected_group._nodes.length && (this.dirty_canvas = + !0), this.selected_group = null), this.selected_group_resizing = !1, this.dragging_rectangle) { + if (this.graph) { + b = this.graph._nodes; + var d = new Float32Array(4); + this.deselectAllNodes(); + var g = Math.abs(this.dragging_rectangle[2]), f = Math.abs(this.dragging_rectangle[3]), c = 0 > this.dragging_rectangle[3] ? this.dragging_rectangle[1] - f : this.dragging_rectangle[1]; + this.dragging_rectangle[0] = 0 > this.dragging_rectangle[2] ? this.dragging_rectangle[0] - g : this.dragging_rectangle[0]; + this.dragging_rectangle[1] = c; + this.dragging_rectangle[2] = g; + this.dragging_rectangle[3] = f; + f = []; + for (c = 0; c < b.length; ++c) { + g = b[c], g.getBounding(d), x(this.dragging_rectangle, d) && f.push(g); + } + f.length && this.selectNodes(f); + } + this.dragging_rectangle = null; + } else { + if (this.connecting_node) { + this.dirty_bgcanvas = this.dirty_canvas = !0; + if (g = this.graph.getNodeOnPos(a.canvasX, a.canvasY, this.visible_nodes)) { + this.connecting_output.type == e.EVENT && this.isOverNodeBox(g, a.canvasX, a.canvasY) ? this.connecting_node.connect(this.connecting_slot, g, e.EVENT) : (b = this.isOverNodeInput(g, a.canvasX, a.canvasY), -1 != b ? this.connecting_node.connect(this.connecting_slot, g, b) : (b = g.getInputInfo(0), this.connecting_output.type == e.EVENT ? this.connecting_node.connect(this.connecting_slot, g, e.EVENT) : b && !b.link && e.isValidConnection(b.type && this.connecting_output.type) && this.connecting_node.connect(this.connecting_slot, + g, 0))); + } + this.connecting_node = this.connecting_pos = this.connecting_output = null; + this.connecting_slot = -1; + } else { + if (this.resizing_node) { + this.dirty_bgcanvas = this.dirty_canvas = !0, this.resizing_node = null; + } else { + if (this.node_dragged) { + (g = this.node_dragged) && 300 > a.click_time && y(a.canvasX, a.canvasY, g.pos[0], g.pos[1] - e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT) && g.collapse(); + this.dirty_bgcanvas = this.dirty_canvas = !0; + this.node_dragged.pos[0] = Math.round(this.node_dragged.pos[0]); + this.node_dragged.pos[1] = Math.round(this.node_dragged.pos[1]); + this.graph.config.align_to_grid && this.node_dragged.alignToGrid(); + if (this.onNodeMoved) { + this.onNodeMoved(this.node_dragged); + } + this.node_dragged = null; + } else { + g = this.graph.getNodeOnPos(a.canvasX, a.canvasY, this.visible_nodes); + !g && 300 > a.click_time && this.deselectAllNodes(); + this.dirty_canvas = !0; + this.dragging_canvas = !1; + if (this.node_over && this.node_over.onMouseUp) { + this.node_over.onMouseUp(a, [a.canvasX - this.node_over.pos[0], a.canvasY - this.node_over.pos[1]], this); + } + if (this.node_capturing_input && this.node_capturing_input.onMouseUp) { + this.node_capturing_input.onMouseUp(a, [a.canvasX - this.node_capturing_input.pos[0], a.canvasY - this.node_capturing_input.pos[1]]); + } + } + } + } + } + } else { + 2 == a.which ? (this.dirty_canvas = !0, this.dragging_canvas = !1) : 3 == a.which && (this.dirty_canvas = !0, this.dragging_canvas = !1); + } + this.graph.change(); + a.stopPropagation(); + a.preventDefault(); + return !1; + } + }; + l.prototype.processMouseWheel = function(a) { + if (this.graph && this.allow_dragcanvas) { + var b = null != a.wheelDeltaY ? a.wheelDeltaY : -60 * a.detail; + this.adjustMouseEvent(a); + var d = this.ds.scale; + 0 < b ? d *= 1.1 : 0 > b && (d *= 1 / 1.1); + this.ds.changeScale(d, [a.localX, a.localY]); + this.graph.change(); + a.preventDefault(); + return !1; + } + }; + l.prototype.isOverNodeBox = function(a, b, d) { + var g = e.NODE_TITLE_HEIGHT; + return y(b, d, a.pos[0] + 2, a.pos[1] + 2 - g, g - 4, g - 4) ? !0 : !1; + }; + l.prototype.isOverNodeInput = function(a, b, d, g) { + if (a.inputs) { + for (var f = 0, e = a.inputs.length; f < e; ++f) { + var c = a.getConnectionPos(!0, f); + if (a.horizontal ? y(b, d, c[0] - 5, c[1] - 10, 10, 20) : y(b, d, c[0] - 10, c[1] - 5, 40, 10)) { + return g && (g[0] = c[0], g[1] = c[1]), f; + } + } + } + return -1; + }; + l.prototype.processKey = function(a) { + if (this.graph) { + var b = !1; + if ("input" != a.target.localName) { + if ("keydown" == a.type) { + if (32 == a.keyCode && (b = this.dragging_canvas = !0), 65 == a.keyCode && a.ctrlKey && (this.selectNodes(), b = !0), "KeyC" == a.code && (a.metaKey || a.ctrlKey) && !a.shiftKey && this.selected_nodes && (this.copyToClipboard(), b = !0), "KeyV" != a.code || !a.metaKey && !a.ctrlKey || a.shiftKey || this.pasteFromClipboard(), 46 != a.keyCode && 8 != a.keyCode || "input" == a.target.localName || "textarea" == a.target.localName || (this.deleteSelectedNodes(), b = !0), this.selected_nodes) { + for (var d in this.selected_nodes) { + if (this.selected_nodes[d].onKeyDown) { + this.selected_nodes[d].onKeyDown(a); + } + } + } + } else { + if ("keyup" == a.type && (32 == a.keyCode && (this.dragging_canvas = !1), this.selected_nodes)) { + for (d in this.selected_nodes) { + if (this.selected_nodes[d].onKeyUp) { + this.selected_nodes[d].onKeyUp(a); + } + } + } + } + this.graph.change(); + if (b) { + return a.preventDefault(), a.stopImmediatePropagation(), !1; + } + } + } + }; + l.prototype.copyToClipboard = function() { + var a = {nodes:[], links:[]}, b = 0, d = [], g; + for (g in this.selected_nodes) { + var f = this.selected_nodes[g]; + f._relative_id = b; + d.push(f); + b += 1; + } + for (g = 0; g < d.length; ++g) { + if (f = d[g], b = f.clone()) { + if (a.nodes.push(b.serialize()), f.inputs && f.inputs.length) { + for (b = 0; b < f.inputs.length; ++b) { + var e = f.inputs[b]; + if (e && null != e.link && (e = this.graph.links[e.link])) { + var c = this.graph.getNodeById(e.origin_id); + c && this.selected_nodes[c.id] && a.links.push([c._relative_id, e.origin_slot, f._relative_id, e.target_slot]); + } + } + } + } else { + console.warn("node type not found: " + f.type); + } + } + localStorage.setItem("litegrapheditor_clipboard", JSON.stringify(a)); + }; + l.prototype.pasteFromClipboard = function() { + var a = localStorage.getItem("litegrapheditor_clipboard"); + if (a) { + a = JSON.parse(a); + for (var b = [], d = 0; d < a.nodes.length; ++d) { + var g = a.nodes[d], f = e.createNode(g.type); + f && (f.configure(g), f.pos[0] += 5, f.pos[1] += 5, this.graph.add(f), b.push(f)); + } + for (d = 0; d < a.links.length; ++d) { + g = a.links[d]; + f = b[g[0]]; + var c = b[g[2]]; + f && c ? f.connect(g[1], c, g[3]) : console.warn("Warning, nodes missing on pasting"); + } + this.selectNodes(b); + } + }; + l.prototype.processDrop = function(a) { + a.preventDefault(); + this.adjustMouseEvent(a); + var b = [a.canvasX, a.canvasY], d = this.graph ? this.graph.getNodeOnPos(b[0], b[1]) : null; + if (d) { + if ((d.onDropFile || d.onDropData) && (b = a.dataTransfer.files) && b.length) { + for (var g = 0; g < b.length; g++) { + var f = a.dataTransfer.files[0], e = f.name; + l.getFileExtension(e); + if (d.onDropFile) { + d.onDropFile(f); + } + if (d.onDropData) { + var c = new FileReader; + c.onload = function(a) { + d.onDropData(a.target.result, e, f); + }; + var k = f.type.split("/")[0]; + "text" == k || "" == k ? c.readAsText(f) : "image" == k ? c.readAsDataURL(f) : c.readAsArrayBuffer(f); + } + } + } + return d.onDropItem && d.onDropItem(event) ? !0 : this.onDropItem ? this.onDropItem(event) : !1; + } + b = null; + this.onDropItem && (b = this.onDropItem(event)); + b || this.checkDropItem(a); + }; + l.prototype.checkDropItem = function(a) { + if (a.dataTransfer.files.length) { + var b = a.dataTransfer.files[0], d = l.getFileExtension(b.name).toLowerCase(); + if (d = e.node_types_by_file_extension[d]) { + if (d = e.createNode(d.type), d.pos = [a.canvasX, a.canvasY], this.graph.add(d), d.onDropFile) { + d.onDropFile(b); + } + } + } + }; + l.prototype.processNodeDblClicked = function(a) { + if (this.onShowNodePanel) { + this.onShowNodePanel(a); + } + if (this.onNodeDblClicked) { + this.onNodeDblClicked(a); + } + this.setDirty(!0); + }; + l.prototype.processNodeSelected = function(a, b) { + this.selectNode(a, b && b.shiftKey); + if (this.onNodeSelected) { + this.onNodeSelected(a); + } + }; + l.prototype.selectNode = function(a, b) { + null == a ? this.deselectAllNodes() : this.selectNodes([a], b); + }; + l.prototype.selectNodes = function(a, b) { + b || this.deselectAllNodes(); + a = a || this.graph._nodes; + for (b = 0; b < a.length; ++b) { + var d = a[b]; + if (!d.is_selected) { + if (!d.is_selected && d.onSelected) { + d.onSelected(); + } + d.is_selected = !0; + this.selected_nodes[d.id] = d; + if (d.inputs) { + for (var g = 0; g < d.inputs.length; ++g) { + this.highlighted_links[d.inputs[g].link] = !0; + } + } + if (d.outputs) { + for (g = 0; g < d.outputs.length; ++g) { + var f = d.outputs[g]; + if (f.links) { + for (var e = 0; e < f.links.length; ++e) { + this.highlighted_links[f.links[e]] = !0; + } + } + } + } + } + } + if (this.onSelectionChange) { + this.onSelectionChange(this.selected_nodes); + } + this.setDirty(!0); + }; + l.prototype.deselectNode = function(a) { + if (a.is_selected) { + if (a.onDeselected) { + a.onDeselected(); + } + a.is_selected = !1; + if (this.onNodeDeselected) { + this.onNodeDeselected(a); + } + if (a.inputs) { + for (var b = 0; b < a.inputs.length; ++b) { + delete this.highlighted_links[a.inputs[b].link]; + } + } + if (a.outputs) { + for (b = 0; b < a.outputs.length; ++b) { + var d = a.outputs[b]; + if (d.links) { + for (var g = 0; g < d.links.length; ++g) { + delete this.highlighted_links[d.links[g]]; + } + } + } + } + } + }; + l.prototype.deselectAllNodes = function() { + if (this.graph) { + for (var a = this.graph._nodes, b = 0, d = a.length; b < d; ++b) { + var g = a[b]; + if (g.is_selected) { + if (g.onDeselected) { + g.onDeselected(); + } + g.is_selected = !1; + if (this.onNodeDeselected) { + this.onNodeDeselected(g); + } + } + } + this.selected_nodes = {}; + this.current_node = null; + this.highlighted_links = {}; + if (this.onSelectionChange) { + this.onSelectionChange(this.selected_nodes); + } + this.setDirty(!0); + } + }; + l.prototype.deleteSelectedNodes = function() { + for (var a in this.selected_nodes) { + var b = this.selected_nodes[a]; + if (b.inputs && b.inputs.length && b.outputs && b.outputs.length && e.isValidConnection(b.inputs[0].type, b.outputs[0].type) && b.inputs[0].link && b.outputs[0].links && b.outputs[0].links.length) { + var d = b.graph.links[b.inputs[0].link], g = b.graph.links[b.outputs[0].links[0]], f = b.getInputNode(0), c = b.getOutputNodes(0)[0]; + f && c && f.connect(d.origin_slot, c, g.target_slot); + } + this.graph.remove(b); + if (this.onNodeDeselected) { + this.onNodeDeselected(b); + } + } + this.selected_nodes = {}; + this.current_node = null; + this.highlighted_links = {}; + this.setDirty(!0); + }; + l.prototype.centerOnNode = function(a) { + this.ds.offset[0] = -a.pos[0] - 0.5 * a.size[0] + 0.5 * this.canvas.width / this.ds.scale; + this.ds.offset[1] = -a.pos[1] - 0.5 * a.size[1] + 0.5 * this.canvas.height / this.ds.scale; + this.setDirty(!0, !0); + }; + l.prototype.adjustMouseEvent = function(a) { + if (this.canvas) { + var b = this.canvas.getBoundingClientRect(); + a.localX = a.clientX - b.left; + a.localY = a.clientY - b.top; + } else { + a.localX = a.clientX, a.localY = a.clientY; + } + a.deltaX = a.localX - this.last_mouse_position[0]; + a.deltaY = a.localY - this.last_mouse_position[1]; + this.last_mouse_position[0] = a.localX; + this.last_mouse_position[1] = a.localY; + a.canvasX = a.localX / this.ds.scale - this.ds.offset[0]; + a.canvasY = a.localY / this.ds.scale - this.ds.offset[1]; + }; + l.prototype.setZoom = function(a, b) { + this.ds.changeScale(a, b); + this.dirty_bgcanvas = this.dirty_canvas = !0; + }; + l.prototype.convertOffsetToCanvas = function(a, b) { + return this.ds.convertOffsetToCanvas(a, b); + }; + l.prototype.convertCanvasToOffset = function(a, b) { + return this.ds.convertCanvasToOffset(a, b); + }; + l.prototype.convertEventToCanvasOffset = function(a) { + var b = this.canvas.getBoundingClientRect(); + return this.convertCanvasToOffset([a.clientX - b.left, a.clientY - b.top]); + }; + l.prototype.bringToFront = function(a) { + var b = this.graph._nodes.indexOf(a); + -1 != b && (this.graph._nodes.splice(b, 1), this.graph._nodes.push(a)); + }; + l.prototype.sendToBack = function(a) { + var b = this.graph._nodes.indexOf(a); + -1 != b && (this.graph._nodes.splice(b, 1), this.graph._nodes.unshift(a)); + }; + var B = new Float32Array(4); + l.prototype.computeVisibleNodes = function(a, b) { + b = b || []; + b.length = 0; + a = a || this.graph._nodes; + for (var d = 0, g = a.length; d < g; ++d) { + var f = a[d]; + (!this.live_mode || f.onDrawBackground || f.onDrawForeground) && x(this.visible_area, f.getBounding(B)) && b.push(f); + } + return b; + }; + l.prototype.draw = function(a, b) { + if (this.canvas) { + var d = e.getTime(); + this.render_time = 0.001 * (d - this.last_draw_time); + this.last_draw_time = d; + this.graph && this.ds.computeVisibleArea(); + (this.dirty_bgcanvas || b || this.always_render_background || this.graph && this.graph._last_trigger_time && 1000 > d - this.graph._last_trigger_time) && this.drawBackCanvas(); + (this.dirty_canvas || a) && this.drawFrontCanvas(); + this.fps = this.render_time ? 1.0 / this.render_time : 0; + this.frame += 1; + } + }; + l.prototype.drawFrontCanvas = function() { + this.dirty_canvas = !1; + this.ctx || (this.ctx = this.bgcanvas.getContext("2d")); + var a = this.ctx; + if (a) { + a.start2D && a.start2D(); + var b = this.canvas; + a.restore(); + a.setTransform(1, 0, 0, 1, 0, 0); + this.dirty_area && (a.save(), a.beginPath(), a.rect(this.dirty_area[0], this.dirty_area[1], this.dirty_area[2], this.dirty_area[3]), a.clip()); + this.clear_background && a.clearRect(0, 0, b.width, b.height); + this.bgcanvas == this.canvas ? this.drawBackCanvas() : a.drawImage(this.bgcanvas, 0, 0); + if (this.onRender) { + this.onRender(b, a); + } + this.show_info && this.renderInfo(a); + if (this.graph) { + a.save(); + this.ds.toCanvasContext(a); + b = this.computeVisibleNodes(null, this.visible_nodes); + for (var d = 0; d < b.length; ++d) { + var g = b[d]; + a.save(); + a.translate(g.pos[0], g.pos[1]); + this.drawNode(g, a); + a.restore(); + } + this.render_execution_order && this.drawExecutionOrder(a); + this.graph.config.links_ontop && (this.live_mode || this.drawConnections(a)); + if (null != this.connecting_pos) { + a.lineWidth = this.connections_width; + switch(this.connecting_output.type) { + case e.EVENT: + b = e.EVENT_LINK_COLOR; + break; + default: + b = e.CONNECTING_LINK_COLOR; + } + this.renderLink(a, this.connecting_pos, [this.canvas_mouse[0], this.canvas_mouse[1]], null, !1, null, b, this.connecting_output.dir || (this.connecting_node.horizontal ? e.DOWN : e.RIGHT), e.CENTER); + a.beginPath(); + this.connecting_output.type === e.EVENT || this.connecting_output.shape === e.BOX_SHAPE ? a.rect(this.connecting_pos[0] - 6 + 0.5, this.connecting_pos[1] - 5 + 0.5, 14, 10) : a.arc(this.connecting_pos[0], this.connecting_pos[1], 4, 0, 2 * Math.PI); + a.fill(); + a.fillStyle = "#ffcc00"; + this._highlight_input && (a.beginPath(), a.arc(this._highlight_input[0], this._highlight_input[1], 6, 0, 2 * Math.PI), a.fill()); + } + this.dragging_rectangle && (a.strokeStyle = "#FFF", a.strokeRect(this.dragging_rectangle[0], this.dragging_rectangle[1], this.dragging_rectangle[2], this.dragging_rectangle[3])); + if (this.over_link_center && this.render_link_tooltip) { + this.drawLinkTooltip(a, this.over_link_center); + } else { + if (this.onDrawLinkTooltip) { + this.onDrawLinkTooltip(a, null); + } + } + if (this.onDrawForeground) { + this.onDrawForeground(a, this.visible_rect); + } + a.restore(); + } + if (this.onDrawOverlay) { + this.onDrawOverlay(a); + } + this.dirty_area && a.restore(); + a.finish2D && a.finish2D(); + } + }; + l.prototype.renderInfo = function(a, b, d) { + b = b || 0; + d = d || 0; + a.save(); + a.translate(b, d); + a.font = "10px Arial"; + a.fillStyle = "#888"; + this.graph ? (a.fillText("T: " + this.graph.globaltime.toFixed(2) + "s", 5, 13), a.fillText("I: " + this.graph.iteration, 5, 26), a.fillText("N: " + this.graph._nodes.length + " [" + this.visible_nodes.length + "]", 5, 39), a.fillText("V: " + this.graph._version, 5, 52), a.fillText("FPS:" + this.fps.toFixed(2), 5, 65)) : a.fillText("No graph selected", 5, 13); + a.restore(); + }; + l.prototype.drawBackCanvas = function() { + var a = this.bgcanvas; + if (a.width != this.canvas.width || a.height != this.canvas.height) { + a.width = this.canvas.width, a.height = this.canvas.height; + } + this.bgctx || (this.bgctx = this.bgcanvas.getContext("2d")); + var b = this.bgctx; + b.start && b.start(); + this.clear_background && b.clearRect(0, 0, a.width, a.height); + if (this._graph_stack && this._graph_stack.length) { + b.save(); + var d = this.graph._subgraph_node; + b.strokeStyle = d.bgcolor; + b.lineWidth = 10; + b.strokeRect(1, 1, a.width - 2, a.height - 2); + b.lineWidth = 1; + b.font = "40px Arial"; + b.textAlign = "center"; + b.fillStyle = d.bgcolor || "#AAA"; + for (var g = "", f = 1; f < this._graph_stack.length; ++f) { + g += this._graph_stack[f]._subgraph_node.getTitle() + " >> "; + } + b.fillText(g + d.getTitle(), 0.5 * a.width, 40); + b.restore(); + } + d = !1; + this.onRenderBackground && (d = this.onRenderBackground(a, b)); + b.restore(); + b.setTransform(1, 0, 0, 1, 0, 0); + this.visible_links.length = 0; + if (this.graph) { + b.save(); + this.ds.toCanvasContext(b); + if (this.background_image && 0.5 < this.ds.scale && !d) { + b.globalAlpha = this.zoom_modify_alpha ? (1.0 - 0.5 / this.ds.scale) * this.editor_alpha : this.editor_alpha; + b.imageSmoothingEnabled = b.mozImageSmoothingEnabled = b.imageSmoothingEnabled = !1; + if (!this._bg_img || this._bg_img.name != this.background_image) { + this._bg_img = new Image; + this._bg_img.name = this.background_image; + this._bg_img.src = this.background_image; + var e = this; + this._bg_img.onload = function() { + e.draw(!0, !0); + }; + } + d = null; + null == this._pattern && 0 < this._bg_img.width ? (d = b.createPattern(this._bg_img, "repeat"), this._pattern_img = this._bg_img, this._pattern = d) : d = this._pattern; + d && (b.fillStyle = d, b.fillRect(this.visible_area[0], this.visible_area[1], this.visible_area[2], this.visible_area[3]), b.fillStyle = "transparent"); + b.globalAlpha = 1.0; + b.imageSmoothingEnabled = b.mozImageSmoothingEnabled = b.imageSmoothingEnabled = !0; + } + this.graph._groups.length && !this.live_mode && this.drawGroups(a, b); + if (this.onDrawBackground) { + this.onDrawBackground(b, this.visible_area); + } + this.onBackgroundRender && (console.error("WARNING! onBackgroundRender deprecated, now is named onDrawBackground "), this.onBackgroundRender = null); + this.render_canvas_border && (b.strokeStyle = "#235", b.strokeRect(0, 0, a.width, a.height)); + this.render_connections_shadows ? (b.shadowColor = "#000", b.shadowOffsetX = 0, b.shadowOffsetY = 0, b.shadowBlur = 6) : b.shadowColor = "rgba(0,0,0,0)"; + this.live_mode || this.drawConnections(b); + b.shadowColor = "rgba(0,0,0,0)"; + b.restore(); + } + b.finish && b.finish(); + this.dirty_bgcanvas = !1; + this.dirty_canvas = !0; + }; + var E = new Float32Array(2); + l.prototype.drawNode = function(a, b) { + this.current_node = a; + var d = a.color || a.constructor.color || e.NODE_DEFAULT_COLOR, g = a.bgcolor || a.constructor.bgcolor || e.NODE_DEFAULT_BGCOLOR, f = 0.6 > this.ds.scale; + if (this.live_mode) { + if (!a.flags.collapsed && (b.shadowColor = "transparent", a.onDrawForeground)) { + a.onDrawForeground(b, this, this.canvas); + } + } else { + var c = this.editor_alpha; + b.globalAlpha = c; + this.render_shadows && !f ? (b.shadowColor = e.DEFAULT_SHADOW_COLOR, b.shadowOffsetX = 2 * this.ds.scale, b.shadowOffsetY = 2 * this.ds.scale, b.shadowBlur = 3 * this.ds.scale) : b.shadowColor = "transparent"; + if (!a.flags.collapsed || !a.onDrawCollapsed || 1 != a.onDrawCollapsed(b, this)) { + var k = a._shape || e.BOX_SHAPE; + E.set(a.size); + var n = a.horizontal; + if (a.flags.collapsed) { + b.font = this.inner_text_font; + var p = a.getTitle ? a.getTitle() : a.title; + null != p && (a._collapsed_width = Math.min(a.size[0], b.measureText(p).width + 2 * e.NODE_TITLE_HEIGHT), E[0] = a._collapsed_width, E[1] = 0); + } + a.clip_area && (b.save(), b.beginPath(), k == e.BOX_SHAPE ? b.rect(0, 0, E[0], E[1]) : k == e.ROUND_SHAPE ? b.roundRect(0, 0, E[0], E[1], 10) : k == e.CIRCLE_SHAPE && b.arc(0.5 * E[0], 0.5 * E[1], 0.5 * E[0], 0, 2 * Math.PI), b.clip()); + a.has_errors && (g = "red"); + this.drawNodeShape(a, b, E, d, g, a.is_selected, a.mouseOver); + b.shadowColor = "transparent"; + if (a.onDrawForeground) { + a.onDrawForeground(b, this, this.canvas); + } + b.textAlign = n ? "center" : "left"; + b.font = this.inner_text_font; + g = !f; + k = this.connecting_output; + b.lineWidth = 1; + p = 0; + var l = new Float32Array(2); + if (!a.flags.collapsed) { + if (a.inputs) { + for (d = 0; d < a.inputs.length; d++) { + var h = a.inputs[d]; + b.globalAlpha = c; + this.connecting_node && !e.isValidConnection(h.type, k.type) && (b.globalAlpha = 0.4 * c); + b.fillStyle = null != h.link ? h.color_on || this.default_connection_color.input_on : h.color_off || this.default_connection_color.input_off; + var t = a.getConnectionPos(!0, d, l); + t[0] -= a.pos[0]; + t[1] -= a.pos[1]; + p < t[1] + 0.5 * e.NODE_SLOT_HEIGHT && (p = t[1] + 0.5 * e.NODE_SLOT_HEIGHT); + b.beginPath(); + h.type === e.EVENT || h.shape === e.BOX_SHAPE ? n ? b.rect(t[0] - 5 + 0.5, t[1] - 8 + 0.5, 10, 14) : b.rect(t[0] - 6 + 0.5, t[1] - 5 + 0.5, 14, 10) : h.shape === e.ARROW_SHAPE ? (b.moveTo(t[0] + 8, t[1] + 0.5), b.lineTo(t[0] - 4, t[1] + 6 + 0.5), b.lineTo(t[0] - 4, t[1] - 6 + 0.5), b.closePath()) : f ? b.rect(t[0] - 4, t[1] - 4, 8, 8) : b.arc(t[0], t[1], 4, 0, 2 * Math.PI); + b.fill(); + if (g) { + var m = null != h.label ? h.label : h.name; + m && (b.fillStyle = e.NODE_TEXT_COLOR, n || h.dir == e.UP ? b.fillText(m, t[0], t[1] - 10) : b.fillText(m, t[0] + 10, t[1] + 5)); + } + } + } + this.connecting_node && (b.globalAlpha = 0.4 * c); + b.textAlign = n ? "center" : "right"; + b.strokeStyle = "black"; + if (a.outputs) { + for (d = 0; d < a.outputs.length; d++) { + if (h = a.outputs[d], t = a.getConnectionPos(!1, d, l), t[0] -= a.pos[0], t[1] -= a.pos[1], p < t[1] + 0.5 * e.NODE_SLOT_HEIGHT && (p = t[1] + 0.5 * e.NODE_SLOT_HEIGHT), b.fillStyle = h.links && h.links.length ? h.color_on || this.default_connection_color.output_on : h.color_off || this.default_connection_color.output_off, b.beginPath(), h.type === e.EVENT || h.shape === e.BOX_SHAPE ? n ? b.rect(t[0] - 5 + 0.5, t[1] - 8 + 0.5, 10, 14) : b.rect(t[0] - 6 + 0.5, t[1] - 5 + 0.5, 14, 10) : + h.shape === e.ARROW_SHAPE ? (b.moveTo(t[0] + 8, t[1] + 0.5), b.lineTo(t[0] - 4, t[1] + 6 + 0.5), b.lineTo(t[0] - 4, t[1] - 6 + 0.5), b.closePath()) : f ? b.rect(t[0] - 4, t[1] - 4, 8, 8) : b.arc(t[0], t[1], 4, 0, 2 * Math.PI), b.fill(), f || b.stroke(), g && (m = null != h.label ? h.label : h.name)) { + b.fillStyle = e.NODE_TEXT_COLOR, n || h.dir == e.DOWN ? b.fillText(m, t[0], t[1] - 8) : b.fillText(m, t[0] - 10, t[1] + 5); + } + } + } + b.textAlign = "left"; + b.globalAlpha = 1; + if (a.widgets) { + h = p; + if (n || a.widgets_up) { + h = 2; + } + null != a.widgets_start_y && (h = a.widgets_start_y); + this.drawNodeWidgets(a, h, b, this.node_widget && this.node_widget[0] == a ? this.node_widget[1] : null); + } + } else { + if (this.render_collapsed_slots) { + f = c = null; + if (a.inputs) { + for (d = 0; d < a.inputs.length; d++) { + if (h = a.inputs[d], null != h.link) { + c = h; + break; + } + } + } + if (a.outputs) { + for (d = 0; d < a.outputs.length; d++) { + h = a.outputs[d], h.links && h.links.length && (f = h); + } + } + c && (c = 0, d = -0.5 * e.NODE_TITLE_HEIGHT, n && (c = 0.5 * a._collapsed_width, d = -e.NODE_TITLE_HEIGHT), b.fillStyle = "#686", b.beginPath(), h.type === e.EVENT || h.shape === e.BOX_SHAPE ? b.rect(c - 7 + 0.5, d - 4, 14, 8) : h.shape === e.ARROW_SHAPE ? (b.moveTo(c + 8, d), b.lineTo(c + -4, d - 4), b.lineTo(c + -4, d + 4), b.closePath()) : b.arc(c, d, 4, 0, 2 * Math.PI), b.fill()); + f && (c = a._collapsed_width, d = -0.5 * e.NODE_TITLE_HEIGHT, n && (c = 0.5 * a._collapsed_width, d = 0), b.fillStyle = "#686", b.strokeStyle = "black", b.beginPath(), h.type === e.EVENT || h.shape === e.BOX_SHAPE ? b.rect(c - 7 + 0.5, d - 4, 14, 8) : h.shape === e.ARROW_SHAPE ? (b.moveTo(c + 6, d), b.lineTo(c - 6, d - 4), b.lineTo(c - 6, d + 4), b.closePath()) : b.arc(c, d, 4, 0, 2 * Math.PI), b.fill()); + } + } + a.clip_area && b.restore(); + b.globalAlpha = 1.0; + } + } + }; + l.prototype.drawLinkTooltip = function(a, b) { + var d = b._pos; + a.fillStyle = "black"; + a.beginPath(); + a.arc(d[0], d[1], 3, 0, 2 * Math.PI); + a.fill(); + if (null != b.data && (!this.onDrawLinkTooltip || 1 != this.onDrawLinkTooltip(a, b, this)) && (b = b.data, b = b.constructor === Number ? b.toFixed(2) : b.constructor === String ? '"' + b + '"' : b.constructor === Boolean ? String(b) : b.toToolTip ? b.toToolTip() : "[" + b.constructor.name + "]", null != b)) { + b = b.substr(0, 30); + a.font = "14px Courier New"; + var g = a.measureText(b).width + 20; + a.shadowColor = "black"; + a.shadowOffsetX = 2; + a.shadowOffsetY = 2; + a.shadowBlur = 3; + a.fillStyle = "#454"; + a.beginPath(); + a.roundRect(d[0] - 0.5 * g, d[1] - 15 - 24, g, 24, 3, 3); + a.moveTo(d[0] - 10, d[1] - 15); + a.lineTo(d[0] + 10, d[1] - 15); + a.lineTo(d[0], d[1] - 5); + a.fill(); + a.shadowColor = "transparent"; + a.textAlign = "center"; + a.fillStyle = "#CEC"; + a.fillText(b, d[0], d[1] - 15 - 24 * 0.3); + } + }; + var u = new Float32Array(4); + l.prototype.drawNodeShape = function(a, b, d, g, f, c, k) { + b.strokeStyle = g; + b.fillStyle = f; + f = e.NODE_TITLE_HEIGHT; + var n = 0.5 > this.ds.scale, p = a._shape || a.constructor.shape || e.ROUND_SHAPE, w = a.constructor.title_mode, C = !0; + w == e.TRANSPARENT_TITLE ? C = !1 : w == e.AUTOHIDE_TITLE && k && (C = !0); + u[0] = 0; + u[1] = C ? -f : 0; + u[2] = d[0] + 1; + u[3] = C ? d[1] + f : d[1]; + k = b.globalAlpha; + b.beginPath(); + p == e.BOX_SHAPE || n ? b.fillRect(u[0], u[1], u[2], u[3]) : p == e.ROUND_SHAPE || p == e.CARD_SHAPE ? b.roundRect(u[0], u[1], u[2], u[3], this.round_radius, p == e.CARD_SHAPE ? 0 : this.round_radius) : p == e.CIRCLE_SHAPE && b.arc(0.5 * d[0], 0.5 * d[1], 0.5 * d[0], 0, 2 * Math.PI); + b.fill(); + a.flags.collapsed || (b.shadowColor = "transparent", b.fillStyle = "rgba(0,0,0,0.2)", b.fillRect(0, -1, u[2], 2)); + b.shadowColor = "transparent"; + if (a.onDrawBackground) { + a.onDrawBackground(b, this, this.canvas); + } + if (C || w == e.TRANSPARENT_TITLE) { + if (a.onDrawTitleBar) { + a.onDrawTitleBar(b, f, d, this.ds.scale, g); + } else { + if (w != e.TRANSPARENT_TITLE && (a.constructor.title_color || this.render_title_colored)) { + C = a.constructor.title_color || g; + a.flags.collapsed && (b.shadowColor = e.DEFAULT_SHADOW_COLOR); + if (this.use_gradients) { + var t = l.gradients[C]; + t || (t = l.gradients[C] = b.createLinearGradient(0, 0, 400, 0), t.addColorStop(0, C), t.addColorStop(1, "#000")); + b.fillStyle = t; + } else { + b.fillStyle = C; + } + b.beginPath(); + p == e.BOX_SHAPE || n ? b.rect(0, -f, d[0] + 1, f) : (p == e.ROUND_SHAPE || p == e.CARD_SHAPE) && b.roundRect(0, -f, d[0] + 1, f, this.round_radius, a.flags.collapsed ? this.round_radius : 0); + b.fill(); + b.shadowColor = "transparent"; + } + } + if (a.onDrawTitleBox) { + a.onDrawTitleBox(b, f, d, this.ds.scale); + } else { + p == e.ROUND_SHAPE || p == e.CIRCLE_SHAPE || p == e.CARD_SHAPE ? (n && (b.fillStyle = "black", b.beginPath(), b.arc(0.5 * f, -0.5 * f, 6, 0, 2 * Math.PI), b.fill()), b.fillStyle = a.boxcolor || e.NODE_DEFAULT_BOXCOLOR, n ? b.fillRect(0.5 * f - 5, -0.5 * f - 5, 10, 10) : (b.beginPath(), b.arc(0.5 * f, -0.5 * f, 5, 0, 2 * Math.PI), b.fill())) : (n && (b.fillStyle = "black", b.fillRect(0.5 * (f - 10) - 1, -0.5 * (f + 10) - 1, 12, 12)), b.fillStyle = a.boxcolor || e.NODE_DEFAULT_BOXCOLOR, b.fillRect(0.5 * + (f - 10), -0.5 * (f + 10), 10, 10)); + } + b.globalAlpha = k; + if (a.onDrawTitleText) { + a.onDrawTitleText(b, f, d, this.ds.scale, this.title_text_font, c); + } + !n && (b.font = this.title_text_font, n = String(a.getTitle())) && (b.fillStyle = c ? "white" : a.constructor.title_text_color || this.node_title_color, a.flags.collapsed ? (b.textAlign = "left", b.measureText(n), b.fillText(n.substr(0, 20), f, e.NODE_TITLE_TEXT_Y - f), b.textAlign = "left") : (b.textAlign = "left", b.fillText(n, f, e.NODE_TITLE_TEXT_Y - f))); + if (a.onDrawTitle) { + a.onDrawTitle(b); + } + } + if (c) { + if (a.onBounding) { + a.onBounding(u); + } + w == e.TRANSPARENT_TITLE && (u[1] -= f, u[3] += f); + b.lineWidth = 1; + b.globalAlpha = 0.8; + b.beginPath(); + p == e.BOX_SHAPE ? b.rect(-6 + u[0], -6 + u[1], 12 + u[2], 12 + u[3]) : p == e.ROUND_SHAPE || p == e.CARD_SHAPE && a.flags.collapsed ? b.roundRect(-6 + u[0], -6 + u[1], 12 + u[2], 12 + u[3], 2 * this.round_radius) : p == e.CARD_SHAPE ? b.roundRect(-6 + u[0], -6 + u[1], 12 + u[2], 12 + u[3], 2 * this.round_radius, 2) : p == e.CIRCLE_SHAPE && b.arc(0.5 * d[0], 0.5 * d[1], 0.5 * d[0] + 6, 0, 2 * Math.PI); + b.strokeStyle = "#FFF"; + b.stroke(); + b.strokeStyle = g; + b.globalAlpha = 1; + } + }; + var H = new Float32Array(4), n = new Float32Array(4), p = new Float32Array(2), k = new Float32Array(2); + l.prototype.drawConnections = function(a) { + var b = e.getTime(), d = this.visible_area; + H[0] = d[0] - 20; + H[1] = d[1] - 20; + H[2] = d[2] + 40; + H[3] = d[3] + 40; + a.lineWidth = this.connections_width; + a.fillStyle = "#AAA"; + a.strokeStyle = "#AAA"; + a.globalAlpha = this.editor_alpha; + d = this.graph._nodes; + for (var g = 0, f = d.length; g < f; ++g) { + var c = d[g]; + if (c.inputs && c.inputs.length) { + for (var w = 0; w < c.inputs.length; ++w) { + var l = c.inputs[w]; + if (l && null != l.link && (l = this.graph.links[l.link])) { + var h = this.graph.getNodeById(l.origin_id); + if (null != h) { + var m = l.origin_slot; + var q = -1 == m ? [h.pos[0] + 10, h.pos[1] + 10] : h.getConnectionPos(!1, m, p); + var t = c.getConnectionPos(!0, w, k); + n[0] = q[0]; + n[1] = q[1]; + n[2] = t[0] - q[0]; + n[3] = t[1] - q[1]; + 0 > n[2] && (n[0] += n[2], n[2] = Math.abs(n[2])); + 0 > n[3] && (n[1] += n[3], n[3] = Math.abs(n[3])); + if (x(n, H)) { + var J = h.outputs[m]; + m = c.inputs[w]; + if (J && m && (h = J.dir || (h.horizontal ? e.DOWN : e.RIGHT), m = m.dir || (c.horizontal ? e.UP : e.LEFT), this.renderLink(a, q, t, l, !1, 0, null, h, m), l && l._last_time && 1000 > b - l._last_time)) { + J = 2.0 - 0.002 * (b - l._last_time); + var N = a.globalAlpha; + a.globalAlpha = N * J; + this.renderLink(a, q, t, l, !0, J, "white", h, m); + a.globalAlpha = N; + } + } + } + } + } + } + } + a.globalAlpha = 1; + }; + l.prototype.renderLink = function(a, b, d, g, f, c, k, n, p, h) { + g && this.visible_links.push(g); + !k && g && (k = g.color || l.link_type_colors[g.type]); + k || (k = this.default_link_color); + null != g && this.highlighted_links[g.id] && (k = "#FFF"); + n = n || e.RIGHT; + p = p || e.LEFT; + var w = A(b, d); + this.render_connections_border && 0.6 < this.ds.scale && (a.lineWidth = this.connections_width + 4); + a.lineJoin = "round"; + h = h || 1; + 1 < h && (a.lineWidth = 0.5); + a.beginPath(); + for (var t = 0; t < h; t += 1) { + var C = 5 * (t - 0.5 * (h - 1)); + if (this.links_render_mode == e.SPLINE_LINK) { + a.moveTo(b[0], b[1] + C); + var m = 0, q = 0, I = 0, r = 0; + switch(n) { + case e.LEFT: + m = -0.25 * w; + break; + case e.RIGHT: + m = 0.25 * w; + break; + case e.UP: + q = -0.25 * w; + break; + case e.DOWN: + q = 0.25 * w; + } + switch(p) { + case e.LEFT: + I = -0.25 * w; + break; + case e.RIGHT: + I = 0.25 * w; + break; + case e.UP: + r = -0.25 * w; + break; + case e.DOWN: + r = 0.25 * w; + } + a.bezierCurveTo(b[0] + m, b[1] + q + C, d[0] + I, d[1] + r + C, d[0], d[1] + C); + } else { + if (this.links_render_mode == e.LINEAR_LINK) { + a.moveTo(b[0], b[1] + C); + r = I = q = m = 0; + switch(n) { + case e.LEFT: + m = -1; + break; + case e.RIGHT: + m = 1; + break; + case e.UP: + q = -1; + break; + case e.DOWN: + q = 1; + } + switch(p) { + case e.LEFT: + I = -1; + break; + case e.RIGHT: + I = 1; + break; + case e.UP: + r = -1; + break; + case e.DOWN: + r = 1; + } + a.lineTo(b[0] + 15 * m, b[1] + 15 * q + C); + a.lineTo(d[0] + 15 * I, d[1] + 15 * r + C); + a.lineTo(d[0], d[1] + C); + } else { + if (this.links_render_mode == e.STRAIGHT_LINK) { + a.moveTo(b[0], b[1]), C = b[0], m = b[1], q = d[0], I = d[1], n == e.RIGHT ? C += 10 : m += 10, p == e.LEFT ? q -= 10 : I -= 10, a.lineTo(C, m), a.lineTo(0.5 * (C + q), m), a.lineTo(0.5 * (C + q), I), a.lineTo(q, I), a.lineTo(d[0], d[1]); + } else { + return; + } + } + } + } + this.render_connections_border && 0.6 < this.ds.scale && !f && (a.strokeStyle = "rgba(0,0,0,0.5)", a.stroke()); + a.lineWidth = this.connections_width; + a.fillStyle = a.strokeStyle = k; + a.stroke(); + f = this.computeConnectionPoint(b, d, 0.5, n, p); + g && g._pos && (g._pos[0] = f[0], g._pos[1] = f[1]); + 0.6 <= this.ds.scale && this.highquality_render && p != e.CENTER && (this.render_connection_arrows && (t = this.computeConnectionPoint(b, d, 0.25, n, p), w = this.computeConnectionPoint(b, d, 0.26, n, p), g = this.computeConnectionPoint(b, d, 0.75, n, p), h = this.computeConnectionPoint(b, d, 0.76, n, p), this.render_curved_connections ? (w = -Math.atan2(w[0] - t[0], w[1] - t[1]), h = -Math.atan2(h[0] - g[0], h[1] - g[1])) : h = w = d[1] > b[1] ? 0 : Math.PI, a.save(), a.translate(t[0], t[1]), + a.rotate(w), a.beginPath(), a.moveTo(-5, -3), a.lineTo(0, 7), a.lineTo(5, -3), a.fill(), a.restore(), a.save(), a.translate(g[0], g[1]), a.rotate(h), a.beginPath(), a.moveTo(-5, -3), a.lineTo(0, 7), a.lineTo(5, -3), a.fill(), a.restore()), a.beginPath(), a.arc(f[0], f[1], 5, 0, 2 * Math.PI), a.fill()); + if (c) { + for (a.fillStyle = k, t = 0; 5 > t; ++t) { + c = (0.001 * e.getTime() + 0.2 * t) % 1, f = this.computeConnectionPoint(b, d, c, n, p), a.beginPath(), a.arc(f[0], f[1], 5, 0, 2 * Math.PI), a.fill(); + } + } + }; + l.prototype.computeConnectionPoint = function(a, b, d, g, f) { + g = g || e.RIGHT; + f = f || e.LEFT; + var c = A(a, b), k = [a[0], a[1]], n = [b[0], b[1]]; + switch(g) { + case e.LEFT: + k[0] += -0.25 * c; + break; + case e.RIGHT: + k[0] += 0.25 * c; + break; + case e.UP: + k[1] += -0.25 * c; + break; + case e.DOWN: + k[1] += 0.25 * c; + } + switch(f) { + case e.LEFT: + n[0] += -0.25 * c; + break; + case e.RIGHT: + n[0] += 0.25 * c; + break; + case e.UP: + n[1] += -0.25 * c; + break; + case e.DOWN: + n[1] += 0.25 * c; + } + g = (1 - d) * (1 - d) * (1 - d); + f = 3 * (1 - d) * (1 - d) * d; + c = 3 * (1 - d) * d * d; + d *= d * d; + return [g * a[0] + f * k[0] + c * n[0] + d * b[0], g * a[1] + f * k[1] + c * n[1] + d * b[1]]; + }; + l.prototype.drawExecutionOrder = function(a) { + a.shadowColor = "transparent"; + a.globalAlpha = 0.25; + a.textAlign = "center"; + a.strokeStyle = "white"; + a.globalAlpha = 0.75; + for (var b = this.visible_nodes, d = 0; d < b.length; ++d) { + var g = b[d]; + a.fillStyle = "black"; + a.fillRect(g.pos[0] - e.NODE_TITLE_HEIGHT, g.pos[1] - e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT); + 0 == g.order && a.strokeRect(g.pos[0] - e.NODE_TITLE_HEIGHT + 0.5, g.pos[1] - e.NODE_TITLE_HEIGHT + 0.5, e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT); + a.fillStyle = "#FFF"; + a.fillText(g.order, g.pos[0] + -0.5 * e.NODE_TITLE_HEIGHT, g.pos[1] - 6); + } + a.globalAlpha = 1; + }; + l.prototype.drawNodeWidgets = function(a, b, d, g) { + if (!a.widgets || !a.widgets.length) { + return 0; + } + var f = a.size[0], c = a.widgets; + b += 2; + var k = e.NODE_WIDGET_HEIGHT, n = 0.5 < this.ds.scale; + d.save(); + d.globalAlpha = this.editor_alpha; + for (var p = e.WIDGET_OUTLINE_COLOR, l = e.WIDGET_BGCOLOR, h = e.WIDGET_TEXT_COLOR, t = e.WIDGET_SECONDARY_TEXT_COLOR, m = 0; m < c.length; ++m) { + var q = c[m], r = b; + q.y && (r = q.y); + q.last_y = r; + d.strokeStyle = p; + d.fillStyle = "#222"; + d.textAlign = "left"; + q.disabled && (d.globalAlpha *= 0.5); + switch(q.type) { + case "button": + q.clicked && (d.fillStyle = "#AAA", q.clicked = !1, this.dirty_canvas = !0); + d.fillRect(15, r, f - 30, k); + n && !q.disabled && d.strokeRect(15, r, f - 30, k); + n && (d.textAlign = "center", d.fillStyle = h, d.fillText(q.name, 0.5 * f, r + 0.7 * k)); + break; + case "toggle": + d.textAlign = "left"; + d.strokeStyle = p; + d.fillStyle = l; + d.beginPath(); + n ? d.roundRect(15, b, f - 30, k, 0.5 * k) : d.rect(15, b, f - 30, k); + d.fill(); + n && !q.disabled && d.stroke(); + d.fillStyle = q.value ? "#89A" : "#333"; + d.beginPath(); + d.arc(f - 30, r + 0.5 * k, 0.36 * k, 0, 2 * Math.PI); + d.fill(); + n && (d.fillStyle = t, null != q.name && d.fillText(q.name, 30, r + 0.7 * k), d.fillStyle = q.value ? h : t, d.textAlign = "right", d.fillText(q.value ? q.options.on || "true" : q.options.off || "false", f - 40, r + 0.7 * k)); + break; + case "slider": + d.fillStyle = l; + d.fillRect(15, r, f - 30, k); + var u = q.options.max - q.options.min, B = (q.value - q.options.min) / u; + d.fillStyle = g == q ? "#89A" : "#678"; + d.fillRect(15, r, B * (f - 30), k); + n && !q.disabled && d.strokeRect(15, r, f - 30, k); + q.marker && (u = (q.marker - q.options.min) / u, d.fillStyle = "#AA9", d.fillRect(15 + u * (f - 30), r, 2, k)); + n && (d.textAlign = "center", d.fillStyle = h, d.fillText(q.name + " " + Number(q.value).toFixed(3), 0.5 * f, r + 0.7 * k)); + break; + case "number": + case "combo": + d.textAlign = "left"; + d.strokeStyle = p; + d.fillStyle = l; + d.beginPath(); + n ? d.roundRect(15, b, f - 30, k, 0.5 * k) : d.rect(15, b, f - 30, k); + d.fill(); + n && (q.disabled || d.stroke(), d.fillStyle = h, q.disabled || (d.beginPath(), d.moveTo(31, b + 5), d.lineTo(21, b + 0.5 * k), d.lineTo(31, b + k - 5), d.fill(), d.beginPath(), d.moveTo(f - 15 - 16, b + 5), d.lineTo(f - 15 - 6, b + 0.5 * k), d.lineTo(f - 15 - 16, b + k - 5), d.fill()), d.fillStyle = t, d.fillText(q.name, 35, r + 0.7 * k), d.fillStyle = h, d.textAlign = "right", "number" == q.type ? d.fillText(Number(q.value).toFixed(void 0 !== q.options.precision ? q.options.precision : + 3), f - 30 - 20, r + 0.7 * k) : (u = q.value, q.options.values && (B = q.options.values, B.constructor === Function && (B = B()), B && B.constructor !== Array && (u = B[q.value])), d.fillText(u, f - 30 - 20, r + 0.7 * k))); + break; + case "string": + case "text": + d.textAlign = "left"; + d.strokeStyle = p; + d.fillStyle = l; + d.beginPath(); + n ? d.roundRect(15, b, f - 30, k, 0.5 * k) : d.rect(15, b, f - 30, k); + d.fill(); + n && (d.save(), d.beginPath(), d.rect(15, b, f - 30, k), d.clip(), d.stroke(), d.fillStyle = t, null != q.name && d.fillText(q.name, 30, r + 0.7 * k), d.fillStyle = h, d.textAlign = "right", d.fillText(String(q.value).substr(0, 30), f - 30, r + 0.7 * k), d.restore()); + break; + default: + q.draw && q.draw(d, a, f, r, k); + } + b += (q.computeSize ? q.computeSize(f)[1] : k) + 4; + d.globalAlpha = this.editor_alpha; + } + d.restore(); + d.textAlign = "left"; + }; + l.prototype.processNodeWidgets = function(a, b, d, g) { + function f(f, c) { + f.value = c; + f.options && f.options.property && void 0 !== a.properties[f.options.property] && a.setProperty(f.options.property, c); + f.callback && f.callback(f.value, p, a, b, d); + } + if (!a.widgets || !a.widgets.length) { + return null; + } + for (var c = b[0] - a.pos[0], k = b[1] - a.pos[1], n = a.size[0], p = this, l = this.getCanvasWindow(), h = 0; h < a.widgets.length; ++h) { + var t = a.widgets[h]; + if (t && !t.disabled) { + var m = t.computeSize ? t.computeSize(n)[1] : e.NODE_WIDGET_HEIGHT; + if (t == g || 6 < c && c < n - 12 && k > t.last_y && k < t.last_y + m) { + switch(t.type) { + case "button": + if ("mousemove" === d.type) { + break; + } + t.callback && setTimeout(function() { + t.callback(t, p, a, b, d); + }, 20); + this.dirty_canvas = t.clicked = !0; + break; + case "slider": + l = Math.clamp((c - 10) / (n - 20), 0, 1); + t.value = t.options.min + (t.options.max - t.options.min) * l; + t.callback && setTimeout(function() { + f(t, t.value); + }, 20); + this.dirty_canvas = !0; + break; + case "number": + case "combo": + g = t.value; + if ("mousemove" == d.type && "number" == t.type) { + t.value += 0.1 * d.deltaX * (t.options.step || 1), null != t.options.min && t.value < t.options.min && (t.value = t.options.min), null != t.options.max && t.value > t.options.max && (t.value = t.options.max); + } else { + if ("mousedown" == d.type) { + var q = t.options.values; + q && q.constructor === Function && (q = t.options.values(t, a)); + var r = null; + "number" != t.type && (r = q.constructor === Array ? q : Object.keys(q)); + c = 40 > c ? -1 : c > n - 40 ? 1 : 0; + if ("number" == t.type) { + t.value += 0.1 * c * (t.options.step || 1), null != t.options.min && t.value < t.options.min && (t.value = t.options.min), null != t.options.max && t.value > t.options.max && (t.value = t.options.max); + } else { + if (c) { + l = -1, l = q.constructor === Object ? r.indexOf(String(t.value)) + c : r.indexOf(t.value) + c, l >= r.length && (l = r.length - 1), 0 > l && (l = 0), t.value = q.constructor === Array ? q[l] : l; + } else { + var u = q != r ? Object.values(q) : q; + new e.ContextMenu(u, {scale:Math.max(1, this.ds.scale), event:d, className:"dark", callback:function(a, b, d) { + q != r && (a = u.indexOf(a)); + this.value = a; + f(this, a); + p.dirty_canvas = !0; + return !1; + }.bind(t)}, l); + } + } + } else { + "mouseup" == d.type && "number" == t.type && (c = 40 > c ? -1 : c > n - 40 ? 1 : 0, 200 > d.click_time && 0 == c && this.prompt("Value", t.value, function(a) { + this.value = Number(a); + f(this, this.value); + }.bind(t), d)); + } + } + g != t.value && setTimeout(function() { + f(this, this.value); + }.bind(t), 20); + this.dirty_canvas = !0; + break; + case "toggle": + "mousedown" == d.type && (t.value = !t.value, setTimeout(function() { + f(t, t.value); + }, 20)); + break; + case "string": + case "text": + "mousedown" == d.type && this.prompt("Value", t.value, function(a) { + this.value = a; + f(this, a); + }.bind(t), d); + break; + default: + t.mouse && (this.dirty_canvas = t.mouse(d, [c, k], a)); + } + return t; + } + } + } + return null; + }; + l.prototype.drawGroups = function(a, b) { + if (this.graph) { + a = this.graph._groups; + b.save(); + b.globalAlpha = 0.5 * this.editor_alpha; + for (var d = 0; d < a.length; ++d) { + var c = a[d]; + if (x(this.visible_area, c._bounding)) { + b.fillStyle = c.color || "#335"; + b.strokeStyle = c.color || "#335"; + var f = c._pos, k = c._size; + b.globalAlpha = 0.25 * this.editor_alpha; + b.beginPath(); + b.rect(f[0] + 0.5, f[1] + 0.5, k[0], k[1]); + b.fill(); + b.globalAlpha = this.editor_alpha; + b.stroke(); + b.beginPath(); + b.moveTo(f[0] + k[0], f[1] + k[1]); + b.lineTo(f[0] + k[0] - 10, f[1] + k[1]); + b.lineTo(f[0] + k[0], f[1] + k[1] - 10); + b.fill(); + k = c.font_size || e.DEFAULT_GROUP_FONT_SIZE; + b.font = k + "px Arial"; + b.fillText(c.title, f[0] + 4, f[1] + k); + } + } + b.restore(); + } + }; + l.prototype.adjustNodesSize = function() { + for (var a = this.graph._nodes, b = 0; b < a.length; ++b) { + a[b].size = a[b].computeSize(); + } + this.setDirty(!0, !0); + }; + l.prototype.resize = function(a, b) { + a || b || (b = this.canvas.parentNode, a = b.offsetWidth, b = b.offsetHeight); + if (this.canvas.width != a || this.canvas.height != b) { + this.canvas.width = a, this.canvas.height = b, this.bgcanvas.width = this.canvas.width, this.bgcanvas.height = this.canvas.height, this.setDirty(!0, !0); + } + }; + l.prototype.switchLiveMode = function(a) { + if (a) { + var b = this, d = this.live_mode ? 1.1 : 0.9; + this.live_mode && (this.live_mode = !1, this.editor_alpha = 0.1); + var c = setInterval(function() { + b.editor_alpha *= d; + b.dirty_canvas = !0; + b.dirty_bgcanvas = !0; + 1 > d && 0.01 > b.editor_alpha && (clearInterval(c), 1 > d && (b.live_mode = !0)); + 1 < d && 0.99 < b.editor_alpha && (clearInterval(c), b.editor_alpha = 1); + }, 1); + } else { + this.live_mode = !this.live_mode, this.dirty_bgcanvas = this.dirty_canvas = !0; + } + }; + l.prototype.onNodeSelectionChange = function(a) { + }; + l.prototype.touchHandler = function(a) { + var b = a.changedTouches[0]; + switch(a.type) { + case "touchstart": + var d = "mousedown"; + break; + case "touchmove": + d = "mousemove"; + break; + case "touchend": + d = "mouseup"; + break; + default: + return; + } + var c = this.getCanvasWindow(), f = c.document.createEvent("MouseEvent"); + f.initMouseEvent(d, !0, !0, c, 1, b.screenX, b.screenY, b.clientX, b.clientY, !1, !1, !1, !1, 0, null); + b.target.dispatchEvent(f); + a.preventDefault(); + }; + l.onGroupAdd = function(a, b, d) { + a = l.active_canvas; + a.getCanvasWindow(); + b = new e.LGraphGroup; + b.pos = a.convertEventToCanvasOffset(d); + a.graph.add(b); + }; + l.onMenuAdd = function(a, b, d, c, f) { + function g(a, b) { + b = c.getFirstEvent(); + if (a = e.createNode(a.value)) { + a.pos = k.convertEventToCanvasOffset(b), k.graph.add(a); + } + f && f(a); + } + var k = l.active_canvas, n = k.getCanvasWindow(); + a = e.getNodeTypesCategories(k.filter); + b = []; + for (var p in a) { + a[p] && b.push({value:a[p], content:a[p], has_submenu:!0}); + } + var h = new e.ContextMenu(b, {event:d, callback:function(a, b, d) { + a = e.getNodeTypesInCategory(a.value, k.filter); + b = []; + for (var f in a) { + a[f].skip_list || b.push({content:a[f].title, value:a[f].type}); + } + new e.ContextMenu(b, {event:d, callback:g, parentMenu:h}, n); + return !1; + }, parentMenu:c}, n); + return !1; + }; + l.onMenuCollapseAll = function() { + }; + l.onMenuNodeEdit = function() { + }; + l.showMenuNodeOptionalInputs = function(a, b, d, c, f) { + if (f) { + var g = this; + a = l.active_canvas.getCanvasWindow(); + b = f.optional_inputs; + f.onGetInputs && (b = f.onGetInputs()); + var k = []; + if (b) { + for (var n in b) { + var p = b[n]; + if (p) { + var h = p[0]; + p[2] && p[2].label && (h = p[2].label); + h = {content:h, value:p}; + p[1] == e.ACTION && (h.className = "event"); + k.push(h); + } else { + k.push(null); + } + } + } + this.onMenuNodeInputs && (k = this.onMenuNodeInputs(k)); + if (k.length) { + return new e.ContextMenu(k, {event:d, callback:function(a, b, d) { + f && (a.callback && a.callback.call(g, f, a, b, d), a.value && (f.addInput(a.value[0], a.value[1], a.value[2]), f.setDirtyCanvas(!0, !0))); + }, parentMenu:c, node:f}, a), !1; + } + } + }; + l.showMenuNodeOptionalOutputs = function(a, b, d, c, f) { + function g(a, b, d) { + if (f && (a.callback && a.callback.call(k, f, a, b, d), a.value)) { + if (d = a.value[1], !d || d.constructor !== Object && d.constructor !== Array) { + f.addOutput(a.value[0], a.value[1], a.value[2]), f.setDirtyCanvas(!0, !0); + } else { + a = []; + for (var n in d) { + a.push({content:n, value:d[n]}); + } + new e.ContextMenu(a, {event:b, callback:g, parentMenu:c, node:f}); + return !1; + } + } + } + if (f) { + var k = this; + a = l.active_canvas.getCanvasWindow(); + b = f.optional_outputs; + f.onGetOutputs && (b = f.onGetOutputs()); + var n = []; + if (b) { + for (var p in b) { + var h = b[p]; + if (!h) { + n.push(null); + } else { + if (!f.flags || !f.flags.skip_repeated_outputs || -1 == f.findOutputSlot(h[0])) { + var m = h[0]; + h[2] && h[2].label && (m = h[2].label); + m = {content:m, value:h}; + h[1] == e.EVENT && (m.className = "event"); + n.push(m); + } + } + } + } + this.onMenuNodeOutputs && (n = this.onMenuNodeOutputs(n)); + if (n.length) { + return new e.ContextMenu(n, {event:d, callback:g, parentMenu:c, node:f}, a), !1; + } + } + }; + l.onShowMenuNodeProperties = function(a, b, d, c, f) { + if (f && f.properties) { + var g = l.active_canvas; + b = g.getCanvasWindow(); + var k = [], n; + for (n in f.properties) { + a = void 0 !== f.properties[n] ? f.properties[n] : " ", "object" == typeof a && (a = JSON.stringify(a)), a = l.decodeHTML(a), k.push({content:"" + n + "" + a + "", value:n}); + } + if (k.length) { + return new e.ContextMenu(k, {event:d, callback:function(a, b, d, c) { + f && (b = this.getBoundingClientRect(), g.showEditPropertyValue(f, a.value, {position:[b.left, b.top]})); + }, parentMenu:c, allow_html:!0, node:f}, b), !1; + } + } + }; + l.decodeHTML = function(a) { + var b = document.createElement("div"); + b.innerText = a; + return b.innerHTML; + }; + l.onResizeNode = function(a, b, d, c, f) { + if (f) { + f.size = f.computeSize(); + if (f.onResize) { + f.onResize(f.size); + } + f.setDirtyCanvas(!0, !0); + } + }; + l.prototype.showLinkMenu = function(a, b) { + var d = this; + console.log(a); + var c = new e.ContextMenu(["Add Node", null, "Delete"], {event:b, title:null != a.data ? a.data.constructor.name : null, callback:function(b, g, e) { + switch(b) { + case "Add Node": + l.onMenuAdd(null, null, e, c, function(b) { + console.log("node autoconnect"); + var f = d.graph.getNodeById(a.origin_id), c = d.graph.getNodeById(a.target_id); + b.inputs && b.inputs.length && b.outputs && b.outputs.length && f.outputs[a.origin_slot].type == b.inputs[0].type && b.outputs[0].type == c.inputs[0].type && (f.connect(a.origin_slot, b, 0), b.connect(0, c, a.target_slot), b.pos[0] -= 0.5 * b.size[0]); + }); + break; + case "Delete": + d.graph.removeLink(a.id); + } + }}); + return !1; + }; + l.onShowPropertyEditor = function(a, b, d, c, f) { + function e() { + var b = n.value; + "Number" == a.type ? b = Number(b) : "Boolean" == a.type && (b = !!b); + f[g] = b; + k.parentNode && k.parentNode.removeChild(k); + f.setDirtyCanvas(!0, !0); + } + var g = a.property || "title"; + b = f[g]; + var k = document.createElement("div"); + k.className = "graphdialog"; + k.innerHTML = ""; + k.querySelector(".name").innerText = g; + var n = k.querySelector("input"); + n && (n.value = b, n.addEventListener("blur", function(a) { + this.focus(); + }), n.addEventListener("keydown", function(a) { + 13 == a.keyCode && (e(), a.preventDefault(), a.stopPropagation()); + })); + b = l.active_canvas.canvas; + d = b.getBoundingClientRect(); + var p = c = -20; + d && (c -= d.left, p -= d.top); + event ? (k.style.left = event.clientX + c + "px", k.style.top = event.clientY + p + "px") : (k.style.left = 0.5 * b.width + c + "px", k.style.top = 0.5 * b.height + p + "px"); + k.querySelector("button").addEventListener("click", e); + b.parentNode.appendChild(k); + }; + l.prototype.prompt = function(a, b, d, c) { + var f = this; + a = a || ""; + var e = !1, g = document.createElement("div"); + g.className = "graphdialog rounded"; + g.innerHTML = " "; + g.close = function() { + f.prompt_box = null; + g.parentNode && g.parentNode.removeChild(g); + }; + 1 < this.ds.scale && (g.style.transform = "scale(" + this.ds.scale + ")"); + g.addEventListener("mouseleave", function(a) { + e || g.close(); + }); + f.prompt_box && f.prompt_box.close(); + f.prompt_box = g; + g.querySelector(".name").innerText = a; + g.querySelector(".value").value = b; + var k = g.querySelector("input"); + k.addEventListener("keydown", function(a) { + e = !0; + if (27 == a.keyCode) { + g.close(); + } else { + if (13 == a.keyCode) { + d && d(this.value), g.close(); + } else { + return; + } + } + a.preventDefault(); + a.stopPropagation(); + }); + g.querySelector("button").addEventListener("click", function(a) { + d && d(k.value); + f.setDirty(!0); + g.close(); + }); + a = l.active_canvas.canvas; + b = a.getBoundingClientRect(); + var n = -20, p = -20; + b && (n -= b.left, p -= b.top); + c ? (g.style.left = c.clientX + n + "px", g.style.top = c.clientY + p + "px") : (g.style.left = 0.5 * a.width + n + "px", g.style.top = 0.5 * a.height + p + "px"); + a.parentNode.appendChild(g); + setTimeout(function() { + k.focus(); + }, 10); + return g; + }; + l.search_limit = -1; + l.prototype.showSearchBox = function(a) { + function b(b) { + if (b) { + if (f.onSearchBoxSelection) { + f.onSearchBoxSelection(b, a, k); + } else { + var d = e.searchbox_extras[b.toLowerCase()]; + d && (b = d.type); + if (b = e.createNode(b)) { + b.pos = k.convertEventToCanvasOffset(a), k.graph.add(b); + } + if (d && d.data) { + if (d.data.properties) { + for (var c in d.data.properties) { + b.addProperty(c, d.data.properties[c]); + } + } + if (d.data.inputs) { + for (c in b.inputs = [], d.data.inputs) { + b.addOutput(d.data.inputs[c][0], d.data.inputs[c][1]); + } + } + if (d.data.outputs) { + for (c in b.outputs = [], d.data.outputs) { + b.addOutput(d.data.outputs[c][0], d.data.outputs[c][1]); + } + } + d.data.title && (b.title = d.data.title); + d.data.json && b.configure(d.data.json); + } + } + } + h.close(); + } + function d(a) { + var b = u; + u && u.classList.remove("selected"); + u ? (u = a ? u.nextSibling : u.previousSibling) || (u = b) : u = a ? q.childNodes[0] : q.childNodes[q.childNodes.length]; + u && (u.classList.add("selected"), u.scrollIntoView({block:"end", behavior:"smooth"})); + } + function c() { + function a(a, d) { + var f = document.createElement("div"); + t || (t = a); + f.innerText = a; + f.dataset.type = escape(a); + f.className = "litegraph lite-search-item"; + d && (f.className += " " + d); + f.addEventListener("click", function(a) { + b(unescape(this.dataset.type)); + }); + q.appendChild(f); + } + r = null; + var d = B.value; + t = null; + q.innerHTML = ""; + if (d) { + if (f.onSearchBox) { + var c = f.onSearchBox(q, d, k); + if (c) { + for (var g = 0; g < c.length; ++g) { + a(c[g]); + } + } + } else { + c = function(a) { + var b = e.registered_node_types[a]; + return S && b.filter != S ? !1 : -1 !== a.toLowerCase().indexOf(d); + }; + var n = 0; + d = d.toLowerCase(); + var S = k.filter || k.graph.filter; + for (g in e.searchbox_extras) { + var p = e.searchbox_extras[g]; + if (-1 !== p.desc.toLowerCase().indexOf(d)) { + var h = e.registered_node_types[p.type]; + if (!h || !h.filter || h.filter == S) { + if (a(p.desc, "searchbox_extra"), -1 !== l.search_limit && n++ > l.search_limit) { + break; + } + } + } + } + p = null; + if (Array.prototype.filter) { + p = Object.keys(e.registered_node_types).filter(c); + } else { + for (g in p = [], e.registered_node_types) { + c(g) && p.push(g); + } + } + for (g = 0; g < p.length && !(a(p[g]), -1 !== l.search_limit && n++ > l.search_limit); g++) { + } + } + } + } + var f = this, k = l.active_canvas, n = k.canvas, p = n.ownerDocument || document, h = document.createElement("div"); + h.className = "litegraph litesearchbox graphdialog rounded"; + h.innerHTML = "Search
"; + h.close = function() { + f.search_box = null; + p.body.focus(); + p.body.style.overflow = ""; + setTimeout(function() { + f.canvas.focus(); + }, 20); + h.parentNode && h.parentNode.removeChild(h); + }; + var m = null; + 1 < this.ds.scale && (h.style.transform = "scale(" + this.ds.scale + ")"); + h.addEventListener("mouseenter", function(a) { + m && (clearTimeout(m), m = null); + }); + h.addEventListener("mouseleave", function(a) { + m = setTimeout(function() { + h.close(); + }, 500); + }); + f.search_box && f.search_box.close(); + f.search_box = h; + var q = h.querySelector(".helper"), t = null, r = null, u = null, B = h.querySelector("input"); + B && (B.addEventListener("blur", function(a) { + this.focus(); + }), B.addEventListener("keydown", function(a) { + if (38 == a.keyCode) { + d(!1); + } else { + if (40 == a.keyCode) { + d(!0); + } else { + if (27 == a.keyCode) { + h.close(); + } else { + if (13 == a.keyCode) { + u ? b(u.innerHTML) : t ? b(t) : h.close(); + } else { + r && clearInterval(r); + r = setTimeout(c, 10); + return; + } + } + } + } + a.preventDefault(); + a.stopPropagation(); + a.stopImmediatePropagation(); + return !0; + })); + p.fullscreenElement ? p.fullscreenElement.appendChild(h) : (p.body.appendChild(h), p.body.style.overflow = "hidden"); + n = n.getBoundingClientRect(); + var x = (a ? a.clientY : n.top + 0.5 * n.height) - 20; + h.style.left = (a ? a.clientX : n.left + 0.5 * n.width) - 80 + "px"; + h.style.top = x + "px"; + a.layerY > n.height - 200 && (q.style.maxHeight = n.height - a.layerY - 20 + "px"); + B.focus(); + return h; + }; + l.prototype.showEditPropertyValue = function(a, b, d) { + function c() { + f(t.value); + } + function f(f) { + "number" == typeof a.properties[b] && (f = Number(f)); + if ("array" == k || "object" == k) { + f = JSON.parse(f); + } + a.properties[b] = f; + a._graph && a._graph._version++; + if (a.onPropertyChanged) { + a.onPropertyChanged(b, f); + } + if (d.onclose) { + d.onclose(); + } + l.close(); + a.setDirtyCanvas(!0, !0); + } + if (a && void 0 !== a.properties[b]) { + d = d || {}; + var e = a.getPropertyInfo(b), k = e.type, n = ""; + if ("string" == k || "number" == k || "array" == k || "object" == k) { + n = ""; + } else { + if ("enum" == k && e.values) { + n = ""; + } else { + if ("boolean" == k) { + n = ""; + } else { + console.warn("unknown type: " + k); + return; + } + } + } + var l = this.createDialog("" + b + "" + n + "", d); + if ("enum" == k && e.values) { + var t = l.querySelector("select"); + t.addEventListener("change", function(a) { + f(a.target.value); + }); + } else { + if ("boolean" == k) { + (t = l.querySelector("input")) && t.addEventListener("click", function(a) { + f(!!t.checked); + }); + } else { + if (t = l.querySelector("input")) { + t.addEventListener("blur", function(a) { + this.focus(); + }), h = void 0 !== a.properties[b] ? a.properties[b] : "", h = JSON.stringify(h), t.value = h, t.addEventListener("keydown", function(a) { + 13 == a.keyCode && (c(), a.preventDefault(), a.stopPropagation()); + }); + } + } + } + l.querySelector("button").addEventListener("click", c); + return l; + } + }; + l.prototype.createDialog = function(a, b) { + b = b || {}; + var d = document.createElement("div"); + d.className = "graphdialog"; + d.innerHTML = a; + a = this.canvas.getBoundingClientRect(); + var c = -20, f = -20; + a && (c -= a.left, f -= a.top); + b.position ? (c += b.position[0], f += b.position[1]) : b.event ? (c += b.event.clientX, f += b.event.clientY) : (c += 0.5 * this.canvas.width, f += 0.5 * this.canvas.height); + d.style.left = c + "px"; + d.style.top = f + "px"; + this.canvas.parentNode.appendChild(d); + d.close = function() { + this.parentNode && this.parentNode.removeChild(this); + }; + return d; + }; + l.onMenuNodeCollapse = function(a, b, d, c, f) { + f.collapse(); + }; + l.onMenuNodePin = function(a, b, d, c, f) { + f.pin(); + }; + l.onMenuNodeMode = function(a, b, d, c, f) { + new e.ContextMenu(["Always", "On Event", "On Trigger", "Never"], {event:d, callback:function(a) { + if (f) { + switch(a) { + case "On Event": + f.mode = e.ON_EVENT; + break; + case "On Trigger": + f.mode = e.ON_TRIGGER; + break; + case "Never": + f.mode = e.NEVER; + break; + default: + f.mode = e.ALWAYS; + } + } + }, parentMenu:c, node:f}); + return !1; + }; + l.onMenuNodeColors = function(a, b, d, c, f) { + if (!f) { + throw "no node for color"; + } + b = []; + b.push({value:null, content:"No color"}); + for (var g in l.node_colors) { + a = l.node_colors[g], a = {value:g, content:"" + g + ""}, b.push(a); + } + new e.ContextMenu(b, {event:d, callback:function(a) { + f && ((a = a.value ? l.node_colors[a.value] : null) ? f.constructor === e.LGraphGroup ? f.color = a.groupcolor : (f.color = a.color, f.bgcolor = a.bgcolor) : (delete f.color, delete f.bgcolor), f.setDirtyCanvas(!0, !0)); + }, parentMenu:c, node:f}); + return !1; + }; + l.onMenuNodeShapes = function(a, b, d, c, f) { + if (!f) { + throw "no node passed"; + } + new e.ContextMenu(e.VALID_SHAPES, {event:d, callback:function(a) { + f && (f.shape = a, f.setDirtyCanvas(!0)); + }, parentMenu:c, node:f}); + return !1; + }; + l.onMenuNodeRemove = function(a, b, d, c, f) { + if (!f) { + throw "no node passed"; + } + !1 !== f.removable && (f.graph.remove(f), f.setDirtyCanvas(!0, !0)); + }; + l.onMenuNodeToSubgraph = function(a, b, d, c, f) { + a = f.graph; + if (b = l.active_canvas) { + d = Object.values(b.selected_nodes || {}), d.length || (d = [f]), c = e.createNode("graph/subgraph"), c.pos = f.pos.concat(), a.add(c), c.buildFromNodes(d), b.deselectAllNodes(), f.setDirtyCanvas(!0, !0); + } + }; + l.onMenuNodeClone = function(a, b, d, c, f) { + 0 != f.clonable && (a = f.clone()) && (a.pos = [f.pos[0] + 5, f.pos[1] + 5], f.graph.add(a), f.setDirtyCanvas(!0, !0)); + }; + l.node_colors = {red:{color:"#322", bgcolor:"#533", groupcolor:"#A88"}, brown:{color:"#332922", bgcolor:"#593930", groupcolor:"#b06634"}, green:{color:"#232", bgcolor:"#353", groupcolor:"#8A8"}, blue:{color:"#223", bgcolor:"#335", groupcolor:"#88A"}, pale_blue:{color:"#2a363b", bgcolor:"#3f5159", groupcolor:"#3f789e"}, cyan:{color:"#233", bgcolor:"#355", groupcolor:"#8AA"}, purple:{color:"#323", bgcolor:"#535", groupcolor:"#a1309b"}, yellow:{color:"#432", bgcolor:"#653", groupcolor:"#b58b2a"}, + black:{color:"#222", bgcolor:"#000", groupcolor:"#444"}}; + l.prototype.getCanvasMenuOptions = function() { + if (this.getMenuOptions) { + var a = this.getMenuOptions(); + } else { + a = [{content:"Add Node", has_submenu:!0, callback:l.onMenuAdd}, {content:"Add Group", callback:l.onGroupAdd}], this._graph_stack && 0 < this._graph_stack.length && a.push(null, {content:"Close subgraph", callback:this.closeSubgraph.bind(this)}); + } + if (this.getExtraMenuOptions) { + var b = this.getExtraMenuOptions(this, a); + b && (a = a.concat(b)); + } + return a; + }; + l.prototype.getNodeMenuOptions = function(a) { + var b = a.getMenuOptions ? a.getMenuOptions(this) : [{content:"Inputs", has_submenu:!0, disabled:!0, callback:l.showMenuNodeOptionalInputs}, {content:"Outputs", has_submenu:!0, disabled:!0, callback:l.showMenuNodeOptionalOutputs}, null, {content:"Properties", has_submenu:!0, callback:l.onShowMenuNodeProperties}, null, {content:"Title", callback:l.onShowPropertyEditor}, {content:"Mode", has_submenu:!0, callback:l.onMenuNodeMode}, {content:"Resize", callback:l.onResizeNode}, {content:"Collapse", + callback:l.onMenuNodeCollapse}, {content:"Pin", callback:l.onMenuNodePin}, {content:"Colors", has_submenu:!0, callback:l.onMenuNodeColors}, {content:"Shapes", has_submenu:!0, callback:l.onMenuNodeShapes}, null]; + if (a.onGetInputs) { + var d = a.onGetInputs(); + d && d.length && (b[0].disabled = !1); + } + a.onGetOutputs && (d = a.onGetOutputs()) && d.length && (b[1].disabled = !1); + a.getExtraMenuOptions && (d = a.getExtraMenuOptions(this)) && (d.push(null), b = d.concat(b)); + !1 !== a.clonable && b.push({content:"Clone", callback:l.onMenuNodeClone}); + !1 !== a.removable && b.push(null, {content:"Remove", callback:l.onMenuNodeRemove}); + if (a.graph && a.graph.onGetNodeMenuOptions) { + a.graph.onGetNodeMenuOptions(b, a); + } + return b; + }; + l.prototype.getGroupMenuOptions = function(a) { + return [{content:"Title", callback:l.onShowPropertyEditor}, {content:"Color", has_submenu:!0, callback:l.onMenuNodeColors}, {content:"Font size", property:"font_size", type:"Number", callback:l.onShowPropertyEditor}, null, {content:"Remove", callback:l.onMenuNodeRemove}]; + }; + l.prototype.processContextMenu = function(a, b) { + var d = this, c = l.active_canvas.getCanvasWindow(), f = null, k = {event:b, callback:function(b, f, c) { + if (b) { + if ("Remove Slot" == b.content) { + b = b.slot, b.input ? a.removeInput(b.slot) : b.output && a.removeOutput(b.slot); + } else { + if ("Disconnect Links" == b.content) { + b = b.slot, b.output ? a.disconnectOutput(b.slot) : b.input && a.disconnectInput(b.slot); + } else { + if ("Rename Slot" == b.content) { + b = b.slot; + var e = b.input ? a.getInputInfo(b.slot) : a.getOutputInfo(b.slot), g = d.createDialog("Name", f), k = g.querySelector("input"); + k && e && (k.value = e.label || ""); + g.querySelector("button").addEventListener("click", function(a) { + k.value && (e && (e.label = k.value), d.setDirty(!0)); + g.close(); + }); + } + } + } + } + }, extra:a}; + a && (k.title = a.type); + var n = null; + a && (n = a.getSlotInPosition(b.canvasX, b.canvasY), l.active_node = a); + n ? (f = [], a.getSlotMenuOptions ? f = a.getSlotMenuOptions(n) : (n && n.output && n.output.links && n.output.links.length && f.push({content:"Disconnect Links", slot:n}), b = n.input || n.output, f.push(b.locked ? "Cannot remove" : {content:"Remove Slot", slot:n}), f.push(b.nameLocked ? "Cannot rename" : {content:"Rename Slot", slot:n})), k.title = (n.input ? n.input.type : n.output.type) || "*", n.input && n.input.type == e.ACTION && (k.title = "Action"), n.output && n.output.type == e.EVENT && + (k.title = "Event")) : a ? f = this.getNodeMenuOptions(a) : (f = this.getCanvasMenuOptions(), (n = this.graph.getGroupOnPos(b.canvasX, b.canvasY)) && f.push(null, {content:"Edit Group", has_submenu:!0, submenu:{title:"Group", extra:n, options:this.getGroupMenuOptions(n)}})); + f && new e.ContextMenu(f, k, c); + }; + "undefined" != typeof window && window.CanvasRenderingContext2D && (window.CanvasRenderingContext2D.prototype.roundRect = function(a, b, d, c, f, e) { + void 0 === f && (f = 5); + void 0 === e && (e = f); + this.moveTo(a + f, b); + this.lineTo(a + d - f, b); + this.quadraticCurveTo(a + d, b, a + d, b + f); + this.lineTo(a + d, b + c - e); + this.quadraticCurveTo(a + d, b + c, a + d - e, b + c); + this.lineTo(a + e, b + c); + this.quadraticCurveTo(a, b + c, a, b + c - e); + this.lineTo(a, b + f); + this.quadraticCurveTo(a, b, a + f, b); + }); + e.compareObjects = function(a, b) { + for (var d in a) { + if (a[d] != b[d]) { + return !1; + } + } + return !0; + }; + e.distance = A; + e.colorToString = function(a) { + return "rgba(" + Math.round(255 * a[0]).toFixed() + "," + Math.round(255 * a[1]).toFixed() + "," + Math.round(255 * a[2]).toFixed() + "," + (4 == a.length ? a[3].toFixed(2) : "1.0") + ")"; + }; + e.isInsideRectangle = y; + e.growBounding = function(a, b, d) { + b < a[0] ? a[0] = b : b > a[2] && (a[2] = b); + d < a[1] ? a[1] = d : d > a[3] && (a[3] = d); + }; + e.isInsideBounding = function(a, b) { + return a[0] < b[0][0] || a[1] < b[0][1] || a[0] > b[1][0] || a[1] > b[1][1] ? !1 : !0; + }; + e.overlapBounding = x; + e.hex2num = function(a) { + "#" == a.charAt(0) && (a = a.slice(1)); + a = a.toUpperCase(); + for (var b = Array(3), d = 0, c, f, e = 0; 6 > e; e += 2) { + c = "0123456789ABCDEF".indexOf(a.charAt(e)), f = "0123456789ABCDEF".indexOf(a.charAt(e + 1)), b[d] = 16 * c + f, d++; + } + return b; + }; + e.num2hex = function(a) { + for (var b = "#", d, c, f = 0; 3 > f; f++) { + d = a[f] / 16, c = a[f] % 16, b += "0123456789ABCDEF".charAt(d) + "0123456789ABCDEF".charAt(c); + } + return b; + }; + F.prototype.addItem = function(a, b, d) { + function c(a) { + var b = this.value; + b && b.has_submenu && f.call(this, a); + } + function f(a) { + var b = this.value, f = !0; + e.current_submenu && e.current_submenu.close(a); + if (d.callback) { + var c = d.callback.call(this, b, d, a, e, d.node); + !0 === c && (f = !1); + } + if (b && (b.callback && !d.ignore_item_callbacks && !0 !== b.disabled && (c = b.callback.call(this, b, d, a, e, d.extra), !0 === c && (f = !1)), b.submenu)) { + if (!b.submenu.options) { + throw "ContextMenu submenu needs options"; + } + new e.constructor(b.submenu.options, {callback:b.submenu.callback, event:a, parentMenu:e, ignore_item_callbacks:b.submenu.ignore_item_callbacks, title:b.submenu.title, extra:b.submenu.extra, autoopen:d.autoopen}); + f = !1; + } + f && !e.lock && e.close(); + } + var e = this; + d = d || {}; + var k = document.createElement("div"); + k.className = "litemenu-entry submenu"; + var n = !1; + if (null === b) { + k.classList.add("separator"); + } else { + k.innerHTML = b && b.title ? b.title : a; + if (k.value = b) { + b.disabled && (n = !0, k.classList.add("disabled")), (b.submenu || b.has_submenu) && k.classList.add("has_submenu"); + } + "function" == typeof b ? (k.dataset.value = a, k.onclick_callback = b) : k.dataset.value = b; + b.className && (k.className += " " + b.className); + } + this.root.appendChild(k); + n || k.addEventListener("click", f); + d.autoopen && k.addEventListener("mouseenter", c); + return k; + }; + F.prototype.close = function(a, b) { + this.root.parentNode && this.root.parentNode.removeChild(this.root); + this.parentMenu && !b && (this.parentMenu.lock = !1, this.parentMenu.current_submenu = null, void 0 === a ? this.parentMenu.close() : a && !F.isCursorOverElement(a, this.parentMenu.root) && F.trigger(this.parentMenu.root, "mouseleave", a)); + this.current_submenu && this.current_submenu.close(a, !0); + this.root.closing_timer && clearTimeout(this.root.closing_timer); + }; + F.trigger = function(a, b, d, c) { + var f = document.createEvent("CustomEvent"); + f.initCustomEvent(b, !0, !0, d); + f.srcElement = c; + a.dispatchEvent ? a.dispatchEvent(f) : a.__events && a.__events.dispatchEvent(f); + return f; + }; + F.prototype.getTopMenu = function() { + return this.options.parentMenu ? this.options.parentMenu.getTopMenu() : this; + }; + F.prototype.getFirstEvent = function() { + return this.options.parentMenu ? this.options.parentMenu.getFirstEvent() : this.options.event; + }; + F.isCursorOverElement = function(a, b) { + var d = a.clientX; + a = a.clientY; + return (b = b.getBoundingClientRect()) ? a > b.top && a < b.top + b.height && d > b.left && d < b.left + b.width ? !0 : !1 : !1; + }; + e.ContextMenu = F; + e.closeAllContextMenus = function(a) { + a = a || window; + a = a.document.querySelectorAll(".litecontextmenu"); + if (a.length) { + for (var b = [], d = 0; d < a.length; d++) { + b.push(a[d]); + } + for (d in b) { + b[d].close ? b[d].close() : b[d].parentNode && b[d].parentNode.removeChild(b[d]); + } + } + }; + e.extendClass = function(a, b) { + for (var d in b) { + a.hasOwnProperty(d) || (a[d] = b[d]); + } + if (b.prototype) { + for (d in b.prototype) { + b.prototype.hasOwnProperty(d) && !a.prototype.hasOwnProperty(d) && (b.prototype.__lookupGetter__(d) ? a.prototype.__defineGetter__(d, b.prototype.__lookupGetter__(d)) : a.prototype[d] = b.prototype[d], b.prototype.__lookupSetter__(d) && a.prototype.__defineSetter__(d, b.prototype.__lookupSetter__(d))); + } + } + }; + z.sampleCurve = function(a, b) { + if (b) { + for (var d = 0; d < b.length - 1; ++d) { + var c = b[d], f = b[d + 1]; + if (!(f[0] < a)) { + b = f[0] - c[0]; + if (0.00001 > Math.abs(b)) { + return c[1]; + } + a = (a - c[0]) / b; + return c[1] * (1.0 - a) + f[1] * a; + } + } + return 0; + } + }; + z.prototype.draw = function(a, b, d, c, f, e) { + if (d = this.points) { + this.size = b; + var g = b[0] - 2 * this.margin; + b = b[1] - 2 * this.margin; + f = f || "#666"; + a.save(); + a.translate(this.margin, this.margin); + c && (a.fillStyle = "#111", a.fillRect(0, 0, g, b), a.fillStyle = "#222", a.fillRect(0.5 * g, 0, 1, b), a.strokeStyle = "#333", a.strokeRect(0, 0, g, b)); + a.strokeStyle = f; + e && (a.globalAlpha = 0.5); + a.beginPath(); + for (c = 0; c < d.length; ++c) { + f = d[c], a.lineTo(f[0] * g, (1.0 - f[1]) * b); + } + a.stroke(); + a.globalAlpha = 1; + if (!e) { + for (c = 0; c < d.length; ++c) { + f = d[c], a.fillStyle = this.selected == c ? "#FFF" : this.nearest == c ? "#DDD" : "#AAA", a.beginPath(), a.arc(f[0] * g, (1.0 - f[1]) * b, 2, 0, 2 * Math.PI), a.fill(); + } + } + a.restore(); + } + }; + z.prototype.onMouseDown = function(a, b) { + var d = this.points; + if (d && !(0 > a[1])) { + var c = this.size[0] - 2 * this.margin, f = this.size[1] - 2 * this.margin, e = a[0] - this.margin; + a = a[1] - this.margin; + this.selected = this.getCloserPoint([e, a], 30 / b.ds.scale); + -1 == this.selected && (b = [e / c, 1 - a / f], d.push(b), d.sort(function(a, b) { + return a[0] - b[0]; + }), this.selected = d.indexOf(b), this.must_update = !0); + if (-1 != this.selected) { + return !0; + } + } + }; + z.prototype.onMouseMove = function(a, b) { + var d = this.points; + if (d) { + var c = this.selected; + if (!(0 > c)) { + var f = (a[0] - this.margin) / (this.size[0] - 2 * this.margin), e = (a[1] - this.margin) / (this.size[1] - 2 * this.margin); + this._nearest = this.getCloserPoint([a[0] - this.margin, a[1] - this.margin], 30 / b.ds.scale); + if (b = d[c]) { + var k = 0 == c || c == d.length - 1; + !k && (-10 > a[0] || a[0] > this.size[0] + 10 || -10 > a[1] || a[1] > this.size[1] + 10) ? (d.splice(c, 1), this.selected = -1) : (b[0] = k ? 0 == c ? 0 : 1 : Math.clamp(f, 0, 1), b[1] = 1.0 - Math.clamp(e, 0, 1), d.sort(function(a, b) { + return a[0] - b[0]; + }), this.selected = d.indexOf(b), this.must_update = !0); + } + } + } + }; + z.prototype.onMouseUp = function(a, b) { + this.selected = -1; + return !1; + }; + z.prototype.getCloserPoint = function(a, b) { + var d = this.points; + if (!d) { + return -1; + } + b = b || 30; + for (var c = this.size[0] - 2 * this.margin, f = this.size[1] - 2 * this.margin, e = d.length, k = [0, 0], n = 1000000, p = -1, h = 0; h < e; ++h) { + var l = d[h]; + k[0] = l[0] * c; + k[1] = (1.0 - l[1]) * f; + l = vec2.distance(a, k); + l > n || l > b || (p = h, n = l); + } + return p; + }; + e.CurveEditor = z; + e.getParameterNames = function(a) { + return (a + "").replace(/[/][/].*$/gm, "").replace(/\s+/g, "").replace(/[/][*][^/*]*[*][/]/g, "").split("){", 1)[0].replace(/^[^(]*[(]/, "").replace(/=[^,]+/g, "").split(",").filter(Boolean); + }; + Math.clamp = function(a, b, d) { + return b > a ? b : d < a ? d : a; + }; + "undefined" == typeof window || window.requestAnimationFrame || (window.requestAnimationFrame = window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(a) { + window.setTimeout(a, 1000 / 60); + }); +})(this); +"undefined" != typeof exports && (exports.LiteGraph = this.LiteGraph); +(function(v) { + function c() { + this.addOutput("in ms", "number"); + this.addOutput("in sec", "number"); + } + function r() { + this.size = [140, 80]; + this.properties = {enabled:!0}; + this.enabled = !0; + this.subgraph = new g.LGraph; + this.subgraph._subgraph_node = this; + this.subgraph._is_subgraph = !0; + this.subgraph.onTrigger = this.onSubgraphTrigger.bind(this); + this.subgraph.onInputAdded = this.onSubgraphNewInput.bind(this); + this.subgraph.onInputRenamed = this.onSubgraphRenamedInput.bind(this); + this.subgraph.onInputTypeChanged = this.onSubgraphTypeChangeInput.bind(this); + this.subgraph.onInputRemoved = this.onSubgraphRemovedInput.bind(this); + this.subgraph.onOutputAdded = this.onSubgraphNewOutput.bind(this); + this.subgraph.onOutputRenamed = this.onSubgraphRenamedOutput.bind(this); + this.subgraph.onOutputTypeChanged = this.onSubgraphTypeChangeOutput.bind(this); + this.subgraph.onOutputRemoved = this.onSubgraphRemovedOutput.bind(this); + } + function m() { + this.addOutput("", "number"); + this.name_in_graph = ""; + this.properties = {name:"", type:"number", value:0}; + var a = this; + this.name_widget = this.addWidget("text", "Name", this.properties.name, function(b) { + b && a.setProperty("name", b); + }); + this.type_widget = this.addWidget("text", "Type", this.properties.type, function(b) { + a.setProperty("type", b); + }); + this.value_widget = this.addWidget("number", "Value", this.properties.value, function(b) { + a.setProperty("value", b); + }); + this.widgets_up = !0; + this.size = [180, 90]; + } + function h() { + this.addInput("", ""); + this.name_in_graph = ""; + this.properties = {}; + var a = this; + Object.defineProperty(this.properties, "name", {get:function() { + return a.name_in_graph; + }, set:function(b) { + "" != b && b != a.name_in_graph && (a.name_in_graph ? a.graph.renameOutput(a.name_in_graph, b) : a.graph.addOutput(b, a.properties.type), a.name_widget.value = b, a.name_in_graph = b); + }, enumerable:!0}); + Object.defineProperty(this.properties, "type", {get:function() { + return a.inputs[0].type; + }, set:function(b) { + if ("action" == b || "event" == b) { + b = g.ACTION; + } + a.inputs[0].type = b; + a.name_in_graph && a.graph.changeOutputType(a.name_in_graph, a.inputs[0].type); + a.type_widget.value = b || ""; + }, enumerable:!0}); + this.name_widget = this.addWidget("text", "Name", this.properties.name, "name"); + this.type_widget = this.addWidget("text", "Type", this.properties.type, "type"); + this.widgets_up = !0; + this.size = [180, 60]; + } + function q() { + this.addOutput("value", "number"); + this.addProperty("value", 1.0); + this.widget = this.addWidget("number", "value", 1, "value"); + this.widgets_up = !0; + this.size = [180, 30]; + } + function l() { + this.addOutput("", "boolean"); + this.addProperty("value", !0); + this.widget = this.addWidget("toggle", "value", !0, "value"); + this.widgets_up = !0; + this.size = [140, 30]; + } + function A() { + this.addOutput("", "string"); + this.addProperty("value", ""); + this.widget = this.addWidget("text", "value", "", "value"); + this.widgets_up = !0; + this.size = [180, 30]; + } + function y() { + this.addInput("url", ""); + this.addOutput("", ""); + this.addProperty("url", ""); + this.addProperty("type", "text"); + this.widget = this.addWidget("text", "url", "", "url"); + this._data = null; + } + function x() { + this.addOutput("", ""); + this.addProperty("value", ""); + this.widget = this.addWidget("text", "json", "", "value"); + this.widgets_up = !0; + this.size = [140, 30]; + this._value = null; + } + function F() { + this.addInput("", ""); + this.addOutput("", "array"); + this.addOutput("length", "number"); + this.addProperty("value", ""); + this.widget = this.addWidget("text", "array", "", "value"); + this.widgets_up = !0; + this.size = [140, 50]; + this._value = null; + } + function z() { + this.addInput("array", "array,table,string"); + this.addInput("index", "number"); + this.addOutput("value", ""); + this.addProperty("index", 0); + } + function e() { + this.addInput("table", "table"); + this.addInput("row", "number"); + this.addInput("col", "number"); + this.addOutput("value", ""); + this.addProperty("row", 0); + this.addProperty("column", 0); + } + function B() { + this.addInput("obj", ""); + this.addOutput("", ""); + this.addProperty("value", ""); + this.widget = this.addWidget("text", "prop.", "", this.setValue.bind(this)); + this.widgets_up = !0; + this.size = [140, 30]; + this._value = null; + } + function E() { + this.addInput("obj", ""); + this.addOutput("keys", "array"); + this.size = [140, 30]; + } + function u() { + this.addInput("A", "object"); + this.addInput("B", "object"); + this.addOutput("", "object"); + this._result = {}; + var a = this; + this.addWidget("button", "clear", "", function() { + a._result = {}; + }); + this.size = this.computeSize(); + } + function H() { + this.size = [60, 30]; + this.addInput("in"); + this.addOutput("out"); + this.properties = {varname:"myname", global:!1}; + this.value = null; + } + function n() { + this.size = [60, 30]; + this.addInput("data", 0); + this.addInput("download", g.ACTION); + this.properties = {filename:"data.json"}; + this.value = null; + var a = this; + this.addWidget("button", "Download", "", function(b) { + a.value && a.downloadAsFile(); + }); + } + function p() { + this.size = [60, 30]; + this.addInput("value", 0, {label:""}); + this.value = 0; + } + function k() { + this.addInput("in", 0); + this.addOutput("out", 0); + this.size = [40, 30]; + } + function a() { + this.mode = g.ON_EVENT; + this.size = [80, 30]; + this.addProperty("msg", ""); + this.addInput("log", g.EVENT); + this.addInput("msg", 0); + } + function b() { + this.mode = g.ON_EVENT; + this.addProperty("msg", ""); + this.addInput("", g.EVENT); + var a = this; + this.widget = this.addWidget("text", "Text", "", function(b) { + a.properties.msg = b; + }); + this.widgets_up = !0; + this.size = [200, 30]; + } + function d() { + this.size = [60, 30]; + this.addProperty("onExecute", "return A;"); + this.addInput("A", ""); + this.addInput("B", ""); + this.addOutput("out", ""); + this._func = null; + this.data = {}; + } + var g = v.LiteGraph; + c.title = "Time"; + c.desc = "Time"; + c.prototype.onExecute = function() { + this.setOutputData(0, 1000 * this.graph.globaltime); + this.setOutputData(1, this.graph.globaltime); + }; + g.registerNodeType("basic/time", c); + r.title = "Subgraph"; + r.desc = "Graph inside a node"; + r.title_color = "#334"; + r.prototype.onGetInputs = function() { + return [["enabled", "boolean"]]; + }; + r.prototype.onDrawTitle = function(a) { + if (!this.flags.collapsed) { + a.fillStyle = "#555"; + var b = g.NODE_TITLE_HEIGHT, d = this.size[0] - b; + a.fillRect(d, -b, b, b); + a.fillStyle = "#333"; + a.beginPath(); + a.moveTo(d + 0.2 * b, 0.6 * -b); + a.lineTo(d + 0.8 * b, 0.6 * -b); + a.lineTo(d + 0.5 * b, 0.3 * -b); + a.fill(); + } + }; + r.prototype.onDblClick = function(a, b, d) { + var f = this; + setTimeout(function() { + d.openSubgraph(f.subgraph); + }, 10); + }; + r.prototype.onMouseDown = function(a, b, d) { + if (!this.flags.collapsed && b[0] > this.size[0] - g.NODE_TITLE_HEIGHT && 0 > b[1]) { + var f = this; + setTimeout(function() { + d.openSubgraph(f.subgraph); + }, 10); + } + }; + r.prototype.onAction = function(a, b) { + this.subgraph.onAction(a, b); + }; + r.prototype.onExecute = function() { + if (this.enabled = this.getInputOrProperty("enabled")) { + if (this.inputs) { + for (var a = 0; a < this.inputs.length; a++) { + var b = this.inputs[a], d = this.getInputData(a); + this.subgraph.setInputData(b.name, d); + } + } + this.subgraph.runStep(); + if (this.outputs) { + for (a = 0; a < this.outputs.length; a++) { + d = this.subgraph.getOutputData(this.outputs[a].name), this.setOutputData(a, d); + } + } + } + }; + r.prototype.sendEventToAllNodes = function(a, b, d) { + this.enabled && this.subgraph.sendEventToAllNodes(a, b, d); + }; + r.prototype.onSubgraphTrigger = function(a, b) { + a = this.findOutputSlot(a); + -1 != a && this.triggerSlot(a); + }; + r.prototype.onSubgraphNewInput = function(a, b) { + -1 == this.findInputSlot(a) && this.addInput(a, b); + }; + r.prototype.onSubgraphRenamedInput = function(a, b) { + a = this.findInputSlot(a); + -1 != a && (this.getInputInfo(a).name = b); + }; + r.prototype.onSubgraphTypeChangeInput = function(a, b) { + a = this.findInputSlot(a); + -1 != a && (this.getInputInfo(a).type = b); + }; + r.prototype.onSubgraphRemovedInput = function(a) { + a = this.findInputSlot(a); + -1 != a && this.removeInput(a); + }; + r.prototype.onSubgraphNewOutput = function(a, b) { + -1 == this.findOutputSlot(a) && this.addOutput(a, b); + }; + r.prototype.onSubgraphRenamedOutput = function(a, b) { + a = this.findOutputSlot(a); + -1 != a && (this.getOutputInfo(a).name = b); + }; + r.prototype.onSubgraphTypeChangeOutput = function(a, b) { + a = this.findOutputSlot(a); + -1 != a && (this.getOutputInfo(a).type = b); + }; + r.prototype.onSubgraphRemovedOutput = function(a) { + a = this.findInputSlot(a); + -1 != a && this.removeOutput(a); + }; + r.prototype.getExtraMenuOptions = function(a) { + var b = this; + return [{content:"Open", callback:function() { + a.openSubgraph(b.subgraph); + }}]; + }; + r.prototype.onResize = function(a) { + a[1] += 20; + }; + r.prototype.serialize = function() { + var a = g.LGraphNode.prototype.serialize.call(this); + a.subgraph = this.subgraph.serialize(); + return a; + }; + r.prototype.clone = function() { + var a = g.createNode(this.type), b = this.serialize(); + delete b.id; + delete b.inputs; + delete b.outputs; + a.configure(b); + return a; + }; + r.prototype.buildFromNodes = function(a) { + for (var b = {}, d = 0; d < a.length; ++d) { + var c = a[d]; + b[c.id] = c; + } + for (d = 0; d < a.length; ++d) { + c = a[d]; + if (c.inputs) { + for (var f = 0; f < c.inputs.length; ++f) { + var e = c.inputs[f]; + if (e && e.link) { + var k = c.graph.links[e.link]; + k && (b[k.origin_id] || this.subgraph.addInput(e.name, k.type)); + } + } + } + if (c.outputs) { + for (f = 0; f < c.outputs.length; ++f) { + if ((e = c.outputs[f]) && e.links && e.links.length) { + for (var g = 0; g < e.links.length && (!(k = c.graph.links[e.links[g]]) || b[k.target_id]); ++g) { + } + } + } + } + } + }; + g.Subgraph = r; + g.registerNodeType("graph/subgraph", r); + m.title = "Input"; + m.desc = "Input of the graph"; + m.prototype.onConfigure = function() { + this.updateType(); + }; + m.prototype.updateType = function() { + var a = this.properties.type; + this.type_widget.value = a; + this.outputs[0].type != a && (this.outputs[0].type = a, this.disconnectOutput(0)); + "number" == a ? (this.value_widget.type = "number", this.value_widget.value = 0) : "boolean" == a ? (this.value_widget.type = "toggle", this.value_widget.value = !0) : "string" == a ? (this.value_widget.type = "text", this.value_widget.value = "") : (this.value_widget.type = null, this.value_widget.value = null); + this.properties.value = this.value_widget.value; + }; + m.prototype.onPropertyChanged = function(a, b) { + if ("name" == a) { + if ("" == b || b == this.name_in_graph || "enabled" == b) { + return !1; + } + this.graph && (this.name_in_graph ? this.graph.renameInput(this.name_in_graph, b) : this.graph.addInput(b, this.properties.type)); + this.name_in_graph = this.name_widget.value = b; + } else { + "type" == a && this.updateType(b || ""); + } + }; + m.prototype.getTitle = function() { + return this.flags.collapsed ? this.properties.name : this.title; + }; + m.prototype.onAction = function(a, b) { + this.properties.type == g.EVENT && this.triggerSlot(0, b); + }; + m.prototype.onExecute = function() { + var a = this.graph.inputs[this.properties.name]; + a ? this.setOutputData(0, void 0 !== a.value ? a.value : this.properties.value) : this.setOutputData(0, this.properties.value); + }; + m.prototype.onRemoved = function() { + this.name_in_graph && this.graph.removeInput(this.name_in_graph); + }; + g.GraphInput = m; + g.registerNodeType("graph/input", m); + h.title = "Output"; + h.desc = "Output of the graph"; + h.prototype.onExecute = function() { + this._value = this.getInputData(0); + this.graph.setOutputData(this.properties.name, this._value); + }; + h.prototype.onAction = function(a, b) { + this.properties.type == g.ACTION && this.graph.trigger(this.properties.name, b); + }; + h.prototype.onRemoved = function() { + this.name_in_graph && this.graph.removeOutput(this.name_in_graph); + }; + h.prototype.getTitle = function() { + return this.flags.collapsed ? this.properties.name : this.title; + }; + g.GraphOutput = h; + g.registerNodeType("graph/output", h); + q.title = "Const Number"; + q.desc = "Constant number"; + q.prototype.onExecute = function() { + this.setOutputData(0, parseFloat(this.properties.value)); + }; + q.prototype.getTitle = function() { + return this.flags.collapsed ? this.properties.value : this.title; + }; + q.prototype.setValue = function(a) { + this.setProperty("value", a); + }; + q.prototype.onDrawBackground = function(a) { + this.outputs[0].label = this.properties.value.toFixed(3); + }; + g.registerNodeType("basic/const", q); + l.title = "Const Boolean"; + l.desc = "Constant boolean"; + l.prototype.getTitle = q.prototype.getTitle; + l.prototype.onExecute = function() { + this.setOutputData(0, this.properties.value); + }; + l.prototype.setValue = q.prototype.setValue; + l.prototype.onGetInputs = function() { + return [["toggle", g.ACTION]]; + }; + l.prototype.onAction = function(a) { + this.setValue(!this.properties.value); + }; + g.registerNodeType("basic/boolean", l); + A.title = "Const String"; + A.desc = "Constant string"; + A.prototype.getTitle = q.prototype.getTitle; + A.prototype.onExecute = function() { + this.setOutputData(0, this.properties.value); + }; + A.prototype.setValue = q.prototype.setValue; + A.prototype.onDropFile = function(a) { + var b = this, d = new FileReader; + d.onload = function(a) { + b.setProperty("value", a.target.result); + }; + d.readAsText(a); + }; + g.registerNodeType("basic/string", A); + y.title = "Const File"; + y.desc = "Fetches a file from an url"; + y["@type"] = {type:"enum", values:["text", "arraybuffer", "blob", "json"]}; + y.prototype.onPropertyChanged = function(a, b) { + "url" == a && (null == b || "" == b ? this._data = null : this.fetchFile(b)); + }; + y.prototype.onExecute = function() { + var a = this.getInputData(0) || this.properties.url; + !a || a == this._url && this._type == this.properties.type || this.fetchFile(a); + this.setOutputData(0, this._data); + }; + y.prototype.setValue = q.prototype.setValue; + y.prototype.fetchFile = function(a) { + var b = this; + a && a.constructor === String ? (this._url = a, this._type = this.properties.type, "http" == a.substr(0, 4) && g.proxy && (a = g.proxy + a.substr(a.indexOf(":") + 3)), fetch(a).then(function(a) { + if (!a.ok) { + throw Error("File not found"); + } + if ("arraybuffer" == b.properties.type) { + return a.arrayBuffer(); + } + if ("text" == b.properties.type) { + return a.text(); + } + if ("json" == b.properties.type) { + return a.json(); + } + if ("blob" == b.properties.type) { + return a.blob(); + } + }).then(function(a) { + b._data = a; + b.boxcolor = "#AEA"; + }).catch(function(d) { + b._data = null; + b.boxcolor = "red"; + console.error("error fetching file:", a); + })) : (b._data = null, b.boxcolor = null); + }; + y.prototype.onDropFile = function(a) { + var b = this; + this._url = a.name; + this._type = this.properties.type; + this.properties.url = a.name; + var d = new FileReader; + d.onload = function(a) { + b.boxcolor = "#AEA"; + a = a.target.result; + "json" == b.properties.type && (a = JSON.parse(a)); + b._data = a; + }; + if ("arraybuffer" == b.properties.type) { + d.readAsArrayBuffer(a); + } else { + if ("text" == b.properties.type || "json" == b.properties.type) { + d.readAsText(a); + } else { + if ("blob" == b.properties.type) { + return d.readAsBinaryString(a); + } + } + } + }; + g.registerNodeType("basic/file", y); + x.title = "Const Data"; + x.desc = "Constant Data"; + x.prototype.onPropertyChanged = function(a, b) { + this.widget.value = b; + if (null != b && "" != b) { + try { + this._value = JSON.parse(b), this.boxcolor = "#AEA"; + } catch (w) { + this.boxcolor = "red"; + } + } + }; + x.prototype.onExecute = function() { + this.setOutputData(0, this._value); + }; + x.prototype.setValue = q.prototype.setValue; + g.registerNodeType("basic/data", x); + F.title = "Const Array"; + F.desc = "Constant Array"; + F.prototype.onPropertyChanged = function(a, b) { + this.widget.value = b; + if (null != b && "" != b) { + try { + this._value = "[" != b[0] ? JSON.parse("[" + b + "]") : JSON.parse(b), this.boxcolor = "#AEA"; + } catch (w) { + this.boxcolor = "red"; + } + } + }; + F.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a && a.length) { + this._value || (this._value = []); + this._value.length = a.length; + for (var b = 0; b < a.length; ++b) { + this._value[b] = a[b]; + } + } + this.setOutputData(0, this._value); + this.setOutputData(1, this._value ? this._value.length || 0 : 0); + }; + F.prototype.setValue = q.prototype.setValue; + g.registerNodeType("basic/array", F); + z.title = "Array[i]"; + z.desc = "Returns an element from an array"; + z.prototype.onExecute = function() { + var a = this.getInputData(0), b = this.getInputData(1); + null == b && (b = this.properties.index); + null != a && null != b && this.setOutputData(0, a[Math.floor(Number(b))]); + }; + g.registerNodeType("basic/array[]", z); + e.title = "Table[row][col]"; + e.desc = "Returns an element from a table"; + e.prototype.onExecute = function() { + var a = this.getInputData(0), b = this.getInputData(1), d = this.getInputData(2); + null == b && (b = this.properties.row); + null == d && (d = this.properties.column); + null != a && null != b && null != d && ((b = a[Math.floor(Number(b))]) ? this.setOutputData(0, b[Math.floor(Number(d))]) : this.setOutputData(0, null)); + }; + g.registerNodeType("basic/table[][]", e); + B.title = "Object property"; + B.desc = "Outputs the property of an object"; + B.prototype.setValue = function(a) { + this.properties.value = a; + this.widget.value = a; + }; + B.prototype.getTitle = function() { + return this.flags.collapsed ? "in." + this.properties.value : this.title; + }; + B.prototype.onPropertyChanged = function(a, b) { + this.widget.value = b; + }; + B.prototype.onExecute = function() { + var a = this.getInputData(0); + null != a && this.setOutputData(0, a[this.properties.value]); + }; + g.registerNodeType("basic/object_property", B); + E.title = "Object keys"; + E.desc = "Outputs an array with the keys of an object"; + E.prototype.onExecute = function() { + var a = this.getInputData(0); + null != a && this.setOutputData(0, Object.keys(a)); + }; + g.registerNodeType("basic/object_keys", E); + u.title = "Merge Objects"; + u.desc = "Creates an object copying properties from others"; + u.prototype.onExecute = function() { + var a = this.getInputData(0), b = this.getInputData(1), d = this._result; + if (a) { + for (var c in a) { + d[c] = a[c]; + } + } + if (b) { + for (c in b) { + d[c] = b[c]; + } + } + this.setOutputData(0, d); + }; + g.registerNodeType("basic/merge_objects", u); + H.title = "Variable"; + H.desc = "store/read variable value"; + H.prototype.onExecute = function() { + this.value = this.getInputData(0); + this.graph && (this.graph.vars[this.properties.varname] = this.value); + this.properties.global && (v[this.properties.varname] = this.value); + this.setOutputData(0, this.value); + }; + H.prototype.getTitle = function() { + return this.properties.varname; + }; + g.registerNodeType("basic/variable", H); + g.wrapFunctionAsNode("basic/length", function(a) { + return a && null != a.length ? Number(a.length) : 0; + }, [""], "number"); + n.title = "Download"; + n.desc = "Download some data"; + n.prototype.downloadAsFile = function() { + if (null != this.value) { + var a = null; + a = this.value.constructor === String ? this.value : JSON.stringify(this.value); + a = new Blob([a]); + var b = URL.createObjectURL(a); + a = document.createElement("a"); + a.setAttribute("href", b); + a.setAttribute("download", this.properties.filename); + a.style.display = "none"; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + setTimeout(function() { + URL.revokeObjectURL(b); + }, 6E4); + } + }; + n.prototype.onAction = function(a, b) { + var d = this; + setTimeout(function() { + d.downloadAsFile(); + }, 100); + }; + n.prototype.onExecute = function() { + this.inputs[0] && (this.value = this.getInputData(0)); + }; + n.prototype.getTitle = function() { + return this.flags.collapsed ? this.properties.filename : this.title; + }; + g.registerNodeType("basic/download", n); + p.title = "Watch"; + p.desc = "Show value of input"; + p.prototype.onExecute = function() { + this.inputs[0] && (this.value = this.getInputData(0)); + }; + p.prototype.getTitle = function() { + return this.flags.collapsed ? this.inputs[0].label : this.title; + }; + p.toString = function(a) { + if (null == a) { + return "null"; + } + if (a.constructor === Number) { + return a.toFixed(3); + } + if (a.constructor === Array) { + for (var b = "[", d = 0; d < a.length; ++d) { + b += p.toString(a[d]) + (d + 1 != a.length ? "," : ""); + } + return b + "]"; + } + return String(a); + }; + p.prototype.onDrawBackground = function(a) { + this.inputs[0].label = p.toString(this.value); + }; + g.registerNodeType("basic/watch", p); + k.title = "Cast"; + k.desc = "Allows to connect different types"; + k.prototype.onExecute = function() { + this.setOutputData(0, this.getInputData(0)); + }; + g.registerNodeType("basic/cast", k); + a.title = "Console"; + a.desc = "Show value inside the console"; + a.prototype.onAction = function(a, b) { + "log" == a ? console.log(b) : "warn" == a ? console.warn(b) : "error" == a && console.error(b); + }; + a.prototype.onExecute = function() { + var a = this.getInputData(1); + null !== a && (this.properties.msg = a); + console.log(a); + }; + a.prototype.onGetInputs = function() { + return [["log", g.ACTION], ["warn", g.ACTION], ["error", g.ACTION]]; + }; + g.registerNodeType("basic/console", a); + b.title = "Alert"; + b.desc = "Show an alert window"; + b.color = "#510"; + b.prototype.onConfigure = function(a) { + this.widget.value = a.properties.msg; + }; + b.prototype.onAction = function(a, b) { + var d = this.properties.msg; + setTimeout(function() { + alert(d); + }, 10); + }; + g.registerNodeType("basic/alert", b); + d.prototype.onConfigure = function(a) { + a.properties.onExecute && g.allow_scripts ? this.compileCode(a.properties.onExecute) : console.warn("Script not compiled, LiteGraph.allow_scripts is false"); + }; + d.title = "Script"; + d.desc = "executes a code (max 100 characters)"; + d.widgets_info = {onExecute:{type:"code"}}; + d.prototype.onPropertyChanged = function(a, b) { + "onExecute" == a && g.allow_scripts ? this.compileCode(b) : console.warn("Script not compiled, LiteGraph.allow_scripts is false"); + }; + d.prototype.compileCode = function(a) { + this._func = null; + if (256 < a.length) { + console.warn("Script too long, max 256 chars"); + } else { + for (var b = a.toLowerCase(), d = "script body document eval nodescript function".split(" "), c = 0; c < d.length; ++c) { + if (-1 != b.indexOf(d[c])) { + console.warn("invalid script"); + return; + } + } + try { + this._func = new Function("A", "B", "C", "DATA", "node", a); + } catch (K) { + console.error("Error parsing script"), console.error(K); + } + } + }; + d.prototype.onExecute = function() { + if (this._func) { + try { + var a = this.getInputData(0), b = this.getInputData(1), d = this.getInputData(2); + this.setOutputData(0, this._func(a, b, d, this.data, this)); + } catch (I) { + console.error("Error in script"), console.error(I); + } + } + }; + d.prototype.onGetOutputs = function() { + return [["C", ""]]; + }; + g.registerNodeType("basic/script", d); +})(this); +(function(v) { + function c() { + this.size = [60, 30]; + this.addInput("event", x.ACTION); + } + function r() { + this.size = [60, 30]; + this.addInput("if", ""); + this.addOutput("true", x.EVENT); + this.addOutput("change", x.EVENT); + this.addOutput("false", x.EVENT); + this.properties = {only_on_change:!0}; + this.prev = 0; + } + function m() { + this.addInput("", x.ACTION); + this.addInput("", x.ACTION); + this.addInput("", x.ACTION); + this.addInput("", x.ACTION); + this.addInput("", x.ACTION); + this.addInput("", x.ACTION); + this.addOutput("", x.EVENT); + this.addOutput("", x.EVENT); + this.addOutput("", x.EVENT); + this.addOutput("", x.EVENT); + this.addOutput("", x.EVENT); + this.addOutput("", x.EVENT); + this.size = [120, 30]; + this.flags = {horizontal:!0, render_box:!1}; + } + function h() { + this.size = [60, 30]; + this.addInput("event", x.ACTION); + this.addOutput("event", x.EVENT); + this.properties = {equal_to:"", has_property:"", property_equal_to:""}; + } + function q() { + this.addInput("inc", x.ACTION); + this.addInput("dec", x.ACTION); + this.addInput("reset", x.ACTION); + this.addOutput("change", x.EVENT); + this.addOutput("num", "number"); + this.num = 0; + } + function l() { + this.size = [60, 30]; + this.addProperty("time_in_ms", 1000); + this.addInput("event", x.ACTION); + this.addOutput("on_time", x.EVENT); + this._pending = []; + } + function A() { + this.addProperty("interval", 1000); + this.addProperty("event", "tick"); + this.addOutput("on_tick", x.EVENT); + this.time = 0; + this.last_interval = 1000; + this.triggered = !1; + } + function y() { + this.addInput("data", ""); + this.addInput("assign", x.ACTION); + this.addOutput("data", ""); + this._last_value = null; + this.properties = {data:null, serialize:!0}; + var c = this; + this.addWidget("button", "store", "", function() { + c.properties.data = c._last_value; + }); + } + var x = v.LiteGraph; + c.title = "Log Event"; + c.desc = "Log event in console"; + c.prototype.onAction = function(c, h) { + console.log(c, h); + }; + x.registerNodeType("events/log", c); + r.title = "TriggerEvent"; + r.desc = "Triggers event if input evaluates to true"; + r.prototype.onExecute = function(c, h) { + c = this.getInputData(0); + var e = c != this.prev; + 0 === this.prev && (e = !1); + var l = e && this.properties.only_on_change || !e && !this.properties.only_on_change; + c && l && this.triggerSlot(0, h); + !c && l && this.triggerSlot(2, h); + e && this.triggerSlot(1, h); + this.prev = c; + }; + x.registerNodeType("events/trigger", r); + m.title = "Sequencer"; + m.desc = "Trigger events when an event arrives"; + m.prototype.getTitle = function() { + return ""; + }; + m.prototype.onAction = function(c, h) { + if (this.outputs) { + for (c = 0; c < this.outputs.length; ++c) { + this.triggerSlot(c, h); + } + } + }; + x.registerNodeType("events/sequencer", m); + h.title = "Filter Event"; + h.desc = "Blocks events that do not match the filter"; + h.prototype.onAction = function(c, h) { + if (null != h && (!this.properties.equal_to || this.properties.equal_to == h)) { + if (this.properties.has_property && (c = h[this.properties.has_property], null == c || this.properties.property_equal_to && this.properties.property_equal_to != c)) { + return; + } + this.triggerSlot(0, h); + } + }; + x.registerNodeType("events/filter", h); + q.title = "Counter"; + q.desc = "Counts events"; + q.prototype.getTitle = function() { + return this.flags.collapsed ? String(this.num) : this.title; + }; + q.prototype.onAction = function(c, h) { + h = this.num; + "inc" == c ? this.num += 1 : "dec" == c ? --this.num : "reset" == c && (this.num = 0); + this.num != h && this.trigger("change", this.num); + }; + q.prototype.onDrawBackground = function(c) { + this.flags.collapsed || (c.fillStyle = "#AAA", c.font = "20px Arial", c.textAlign = "center", c.fillText(this.num, 0.5 * this.size[0], 0.5 * this.size[1])); + }; + q.prototype.onExecute = function() { + this.setOutputData(1, this.num); + }; + x.registerNodeType("events/counter", q); + l.title = "Delay"; + l.desc = "Delays one event"; + l.prototype.onAction = function(c, h) { + c = this.properties.time_in_ms; + 0 >= c ? this.trigger(null, h) : this._pending.push([c, h]); + }; + l.prototype.onExecute = function() { + var c = 1000 * this.graph.elapsed_time; + this.isInputConnected(1) && (this.properties.time_in_ms = this.getInputData(1)); + for (var h = 0; h < this._pending.length; ++h) { + var e = this._pending[h]; + e[0] -= c; + 0 < e[0] || (this._pending.splice(h, 1), --h, this.trigger(null, e[1])); + } + }; + l.prototype.onGetInputs = function() { + return [["event", x.ACTION], ["time_in_ms", "number"]]; + }; + x.registerNodeType("events/delay", l); + A.title = "Timer"; + A.desc = "Sends an event every N milliseconds"; + A.prototype.onStart = function() { + this.time = 0; + }; + A.prototype.getTitle = function() { + return "Timer: " + this.last_interval.toString() + "ms"; + }; + A.on_color = "#AAA"; + A.off_color = "#222"; + A.prototype.onDrawBackground = function() { + this.boxcolor = this.triggered ? A.on_color : A.off_color; + this.triggered = !1; + }; + A.prototype.onExecute = function() { + var c = 0 == this.time; + this.time += 1000 * this.graph.elapsed_time; + this.last_interval = Math.max(1, this.getInputOrProperty("interval") | 0); + !c && (this.time < this.last_interval || isNaN(this.last_interval)) ? this.inputs && 1 < this.inputs.length && this.inputs[1] && this.setOutputData(1, !1) : (this.triggered = !0, this.time %= this.last_interval, this.trigger("on_tick", this.properties.event), this.inputs && 1 < this.inputs.length && this.inputs[1] && this.setOutputData(1, !0)); + }; + A.prototype.onGetInputs = function() { + return [["interval", "number"]]; + }; + A.prototype.onGetOutputs = function() { + return [["tick", "boolean"]]; + }; + x.registerNodeType("events/timer", A); + y.title = "Data Store"; + y.desc = "Stores data and only changes when event is received"; + y.prototype.onExecute = function() { + this._last_value = this.getInputData(0); + this.setOutputData(0, this.properties.data); + }; + y.prototype.onAction = function(c, h) { + this.properties.data = this._last_value; + }; + y.prototype.onSerialize = function(c) { + null != c.data && (0 == this.properties.serialize || c.data.constructor !== String && c.data.constructor !== Number && c.data.constructor !== Boolean && c.data.constructor !== Array && c.data.constructor !== Object) && (c.data = null); + }; + x.registerNodeType("basic/data_store", y); +})(this); +(function(v) { + function c() { + this.addOutput("", z.EVENT); + this.addOutput("", "boolean"); + this.addProperty("text", "click me"); + this.addProperty("font_size", 30); + this.addProperty("message", ""); + this.size = [164, 84]; + this.clicked = !1; + } + function r() { + this.addInput("", "boolean"); + this.addInput("e", z.ACTION); + this.addOutput("v", "boolean"); + this.addOutput("e", z.EVENT); + this.properties = {font:"", value:!1}; + this.size = [160, 44]; + } + function m() { + this.addOutput("", "number"); + this.size = [80, 60]; + this.properties = {min:-1000, max:1000, value:1, step:1}; + this.old_y = -1; + this._precision = this._remainder = 0; + this.mouse_captured = !1; + } + function h() { + this.addOutput("", "string"); + this.addOutput("change", z.EVENT); + this.size = [80, 60]; + this.properties = {value:"A", values:"A;B;C"}; + this.old_y = -1; + this.mouse_captured = !1; + this._values = this.properties.values.split(";"); + var c = this; + this.widgets_up = !0; + this.widget = this.addWidget("combo", "", this.properties.value, function(e) { + c.properties.value = e; + c.triggerSlot(1, e); + }, {property:"value", values:this._values}); + } + function q() { + this.addOutput("", "number"); + this.size = [64, 84]; + this.properties = {min:0, max:1, value:0.5, color:"#7AF", precision:2}; + this.value = -1; + } + function l() { + this.addOutput("", "number"); + this.properties = {value:0.5, min:0, max:1, text:"V"}; + var c = this; + this.size = [140, 40]; + this.slider = this.addWidget("slider", "V", this.properties.value, function(e) { + c.properties.value = e; + }, this.properties); + this.widgets_up = !0; + } + function A() { + this.size = [160, 26]; + this.addOutput("", "number"); + this.properties = {color:"#7AF", min:0, max:1, value:0.5}; + this.value = -1; + } + function y() { + this.size = [160, 26]; + this.addInput("", "number"); + this.properties = {min:0, max:1, value:0, color:"#AAF"}; + } + function x() { + this.addInputs("", 0); + this.properties = {value:"...", font:"Arial", fontsize:18, color:"#AAA", align:"left", glowSize:0, decimals:1}; + } + function F() { + this.size = [200, 100]; + this.properties = {borderColor:"#ffffff", bgcolorTop:"#f0f0f0", bgcolorBottom:"#e0e0e0", shadowSize:2, borderRadius:3}; + } + var z = v.LiteGraph; + c.title = "Button"; + c.desc = "Triggers an event"; + c.font = "Arial"; + c.prototype.onDrawForeground = function(e) { + if (!this.flags.collapsed && (e.fillStyle = "black", e.fillRect(11, 11, this.size[0] - 20, this.size[1] - 20), e.fillStyle = "#AAF", e.fillRect(9, 9, this.size[0] - 20, this.size[1] - 20), e.fillStyle = this.clicked ? "white" : this.mouseOver ? "#668" : "#334", e.fillRect(10, 10, this.size[0] - 20, this.size[1] - 20), this.properties.text || 0 === this.properties.text)) { + var h = this.properties.font_size || 30; + e.textAlign = "center"; + e.fillStyle = this.clicked ? "black" : "white"; + e.font = h + "px " + c.font; + e.fillText(this.properties.text, 0.5 * this.size[0], 0.5 * this.size[1] + 0.3 * h); + e.textAlign = "left"; + } + }; + c.prototype.onMouseDown = function(c, h) { + if (1 < h[0] && 1 < h[1] && h[0] < this.size[0] - 2 && h[1] < this.size[1] - 2) { + return this.clicked = !0, this.triggerSlot(0, this.properties.message), !0; + } + }; + c.prototype.onExecute = function() { + this.setOutputData(1, this.clicked); + }; + c.prototype.onMouseUp = function(c) { + this.clicked = !1; + }; + z.registerNodeType("widget/button", c); + r.title = "Toggle"; + r.desc = "Toggles between true or false"; + r.prototype.onDrawForeground = function(c) { + if (!this.flags.collapsed) { + var e = 0.5 * this.size[1], h = 0.8 * this.size[1]; + c.font = this.properties.font || (0.8 * e).toFixed(0) + "px Arial"; + var l = c.measureText(this.title).width; + l = 0.5 * (this.size[0] - (l + e)); + c.fillStyle = "#AAA"; + c.fillRect(l, h - e, e, e); + c.fillStyle = this.properties.value ? "#AEF" : "#000"; + c.fillRect(l + 0.25 * e, h - e + 0.25 * e, .5 * e, .5 * e); + c.textAlign = "left"; + c.fillStyle = "#AAA"; + c.fillText(this.title, 1.2 * e + l, 0.85 * h); + c.textAlign = "left"; + } + }; + r.prototype.onAction = function(c) { + this.properties.value = !this.properties.value; + this.trigger("e", this.properties.value); + }; + r.prototype.onExecute = function() { + var c = this.getInputData(0); + null != c && (this.properties.value = c); + this.setOutputData(0, this.properties.value); + }; + r.prototype.onMouseDown = function(c, h) { + if (1 < h[0] && 1 < h[1] && h[0] < this.size[0] - 2 && h[1] < this.size[1] - 2) { + return this.properties.value = !this.properties.value, this.graph._version++, this.trigger("e", this.properties.value), !0; + } + }; + z.registerNodeType("widget/toggle", r); + m.title = "Number"; + m.desc = "Widget to select number value"; + m.pixels_threshold = 10; + m.markers_color = "#666"; + m.prototype.onDrawForeground = function(c) { + var e = 0.5 * this.size[0], h = this.size[1]; + 30 < h ? (c.fillStyle = m.markers_color, c.beginPath(), c.moveTo(e, 0.1 * h), c.lineTo(e + 0.1 * h, 0.2 * h), c.lineTo(e + -0.1 * h, 0.2 * h), c.fill(), c.beginPath(), c.moveTo(e, 0.9 * h), c.lineTo(e + 0.1 * h, 0.8 * h), c.lineTo(e + -0.1 * h, 0.8 * h), c.fill(), c.font = (0.7 * h).toFixed(1) + "px Arial") : c.font = (0.8 * h).toFixed(1) + "px Arial"; + c.textAlign = "center"; + c.font = (0.7 * h).toFixed(1) + "px Arial"; + c.fillStyle = "#EEE"; + c.fillText(this.properties.value.toFixed(this._precision), e, 0.75 * h); + }; + m.prototype.onExecute = function() { + this.setOutputData(0, this.properties.value); + }; + m.prototype.onPropertyChanged = function(c, h) { + c = (this.properties.step + "").split("."); + this._precision = 1 < c.length ? c[1].length : 0; + }; + m.prototype.onMouseDown = function(c, h) { + if (!(0 > h[1])) { + return this.old_y = c.canvasY, this.captureInput(!0), this.mouse_captured = !0; + } + }; + m.prototype.onMouseMove = function(c) { + if (this.mouse_captured) { + var e = this.old_y - c.canvasY; + c.shiftKey && (e *= 10); + if (c.metaKey || c.altKey) { + e *= 0.1; + } + this.old_y = c.canvasY; + c = this._remainder + e / m.pixels_threshold; + this._remainder = c % 1; + c = Math.clamp(this.properties.value + (c | 0) * this.properties.step, this.properties.min, this.properties.max); + this.properties.value = c; + this.graph._version++; + this.setDirtyCanvas(!0); + } + }; + m.prototype.onMouseUp = function(c, h) { + 200 > c.click_time && (this.properties.value = Math.clamp(this.properties.value + (h[1] > 0.5 * this.size[1] ? -1 : 1) * this.properties.step, this.properties.min, this.properties.max), this.graph._version++, this.setDirtyCanvas(!0)); + this.mouse_captured && (this.mouse_captured = !1, this.captureInput(!1)); + }; + z.registerNodeType("widget/number", m); + h.title = "Combo"; + h.desc = "Widget to select from a list"; + h.prototype.onExecute = function() { + this.setOutputData(0, this.properties.value); + }; + h.prototype.onPropertyChanged = function(c, h) { + "values" == c ? (this._values = h.split(";"), this.widget.options.values = this._values) : "value" == c && (this.widget.value = h); + }; + z.registerNodeType("widget/combo", h); + q.title = "Knob"; + q.desc = "Circular controller"; + q.size = [80, 100]; + q.prototype.onDrawForeground = function(c) { + if (!this.flags.collapsed) { + -1 == this.value && (this.value = (this.properties.value - this.properties.min) / (this.properties.max - this.properties.min)); + var e = 0.5 * this.size[0], h = 0.5 * this.size[1], l = 0.5 * Math.min(this.size[0], this.size[1]) - 5; + c.globalAlpha = 1; + c.save(); + c.translate(e, h); + c.rotate(0.75 * Math.PI); + c.fillStyle = "rgba(0,0,0,0.5)"; + c.beginPath(); + c.moveTo(0, 0); + c.arc(0, 0, l, 0, 1.5 * Math.PI); + c.fill(); + c.strokeStyle = "black"; + c.fillStyle = this.properties.color; + c.lineWidth = 2; + c.beginPath(); + c.moveTo(0, 0); + c.arc(0, 0, l - 4, 0, 1.5 * Math.PI * Math.max(0.01, this.value)); + c.closePath(); + c.fill(); + c.lineWidth = 1; + c.globalAlpha = 1; + c.restore(); + c.fillStyle = "black"; + c.beginPath(); + c.arc(e, h, 0.75 * l, 0, 2 * Math.PI, !0); + c.fill(); + c.fillStyle = this.mouseOver ? "white" : this.properties.color; + c.beginPath(); + var m = this.value * Math.PI * 1.5 + 0.75 * Math.PI; + c.arc(e + Math.cos(m) * l * 0.65, h + Math.sin(m) * l * 0.65, 0.05 * l, 0, 2 * Math.PI, !0); + c.fill(); + c.fillStyle = this.mouseOver ? "white" : "#AAA"; + c.font = Math.floor(0.5 * l) + "px Arial"; + c.textAlign = "center"; + c.fillText(this.properties.value.toFixed(this.properties.precision), e, h + 0.15 * l); + } + }; + q.prototype.onExecute = function() { + this.setOutputData(0, this.properties.value); + this.boxcolor = z.colorToString([this.value, this.value, this.value]); + }; + q.prototype.onMouseDown = function(c) { + this.center = [0.5 * this.size[0], 0.5 * this.size[1] + 20]; + this.radius = 0.5 * this.size[0]; + if (20 > c.canvasY - this.pos[1] || z.distance([c.canvasX, c.canvasY], [this.pos[0] + this.center[0], this.pos[1] + this.center[1]]) > this.radius) { + return !1; + } + this.oldmouse = [c.canvasX - this.pos[0], c.canvasY - this.pos[1]]; + this.captureInput(!0); + return !0; + }; + q.prototype.onMouseMove = function(c) { + if (this.oldmouse) { + c = [c.canvasX - this.pos[0], c.canvasY - this.pos[1]]; + var e = this.value; + e -= 0.01 * (c[1] - this.oldmouse[1]); + 1.0 < e ? e = 1.0 : 0.0 > e && (e = 0.0); + this.value = e; + this.properties.value = this.properties.min + (this.properties.max - this.properties.min) * this.value; + this.oldmouse = c; + this.setDirtyCanvas(!0); + } + }; + q.prototype.onMouseUp = function(c) { + this.oldmouse && (this.oldmouse = null, this.captureInput(!1)); + }; + q.prototype.onPropertyChanged = function(c, h) { + if ("min" == c || "max" == c || "value" == c) { + return this.properties[c] = parseFloat(h), !0; + } + }; + z.registerNodeType("widget/knob", q); + l.title = "Inner Slider"; + l.prototype.onPropertyChanged = function(c, h) { + "value" == c && (this.slider.value = h); + }; + l.prototype.onExecute = function() { + this.setOutputData(0, this.properties.value); + }; + z.registerNodeType("widget/internal_slider", l); + A.title = "H.Slider"; + A.desc = "Linear slider controller"; + A.prototype.onDrawForeground = function(c) { + -1 == this.value && (this.value = (this.properties.value - this.properties.min) / (this.properties.max - this.properties.min)); + c.globalAlpha = 1; + c.lineWidth = 1; + c.fillStyle = "#000"; + c.fillRect(2, 2, this.size[0] - 4, this.size[1] - 4); + c.fillStyle = this.properties.color; + c.beginPath(); + c.rect(4, 4, (this.size[0] - 8) * this.value, this.size[1] - 8); + c.fill(); + }; + A.prototype.onExecute = function() { + this.properties.value = this.properties.min + (this.properties.max - this.properties.min) * this.value; + this.setOutputData(0, this.properties.value); + this.boxcolor = z.colorToString([this.value, this.value, this.value]); + }; + A.prototype.onMouseDown = function(c) { + if (0 > c.canvasY - this.pos[1]) { + return !1; + } + this.oldmouse = [c.canvasX - this.pos[0], c.canvasY - this.pos[1]]; + this.captureInput(!0); + return !0; + }; + A.prototype.onMouseMove = function(c) { + if (this.oldmouse) { + c = [c.canvasX - this.pos[0], c.canvasY - this.pos[1]]; + var e = this.value; + e += (c[0] - this.oldmouse[0]) / this.size[0]; + 1.0 < e ? e = 1.0 : 0.0 > e && (e = 0.0); + this.value = e; + this.oldmouse = c; + this.setDirtyCanvas(!0); + } + }; + A.prototype.onMouseUp = function(c) { + this.oldmouse = null; + this.captureInput(!1); + }; + A.prototype.onMouseLeave = function(c) { + }; + z.registerNodeType("widget/hslider", A); + y.title = "Progress"; + y.desc = "Shows data in linear progress"; + y.prototype.onExecute = function() { + var c = this.getInputData(0); + void 0 != c && (this.properties.value = c); + }; + y.prototype.onDrawForeground = function(c) { + c.lineWidth = 1; + c.fillStyle = this.properties.color; + var e = (this.properties.value - this.properties.min) / (this.properties.max - this.properties.min); + e = Math.min(1, e); + e = Math.max(0, e); + c.fillRect(2, 2, (this.size[0] - 4) * e, this.size[1] - 4); + }; + z.registerNodeType("widget/progress", y); + x.title = "Text"; + x.desc = "Shows the input value"; + x.widgets = [{name:"resize", text:"Resize box", type:"button"}, {name:"led_text", text:"LED", type:"minibutton"}, {name:"normal_text", text:"Normal", type:"minibutton"}]; + x.prototype.onDrawForeground = function(c) { + c.fillStyle = this.properties.color; + var e = this.properties.value; + this.properties.glowSize ? (c.shadowColor = this.properties.color, c.shadowOffsetX = 0, c.shadowOffsetY = 0, c.shadowBlur = this.properties.glowSize) : c.shadowColor = "transparent"; + var h = this.properties.fontsize; + c.textAlign = this.properties.align; + c.font = h.toString() + "px " + this.properties.font; + this.str = "number" == typeof e ? e.toFixed(this.properties.decimals) : e; + if ("string" == typeof this.str) { + e = this.str.split("\\n"); + for (var l in e) { + c.fillText(e[l], "left" == this.properties.align ? 15 : this.size[0] - 15, -0.15 * h + h * (parseInt(l) + 1)); + } + } + c.shadowColor = "transparent"; + this.last_ctx = c; + c.textAlign = "left"; + }; + x.prototype.onExecute = function() { + var c = this.getInputData(0); + null != c && (this.properties.value = c); + }; + x.prototype.resize = function() { + if (this.last_ctx) { + var c = this.str.split("\\n"); + this.last_ctx.font = this.properties.fontsize + "px " + this.properties.font; + var h = 0, l; + for (l in c) { + var m = this.last_ctx.measureText(c[l]).width; + h < m && (h = m); + } + this.size[0] = h + 20; + this.size[1] = 4 + c.length * this.properties.fontsize; + this.setDirtyCanvas(!0); + } + }; + x.prototype.onPropertyChanged = function(c, h) { + this.properties[c] = h; + this.str = "number" == typeof h ? h.toFixed(3) : h; + return !0; + }; + z.registerNodeType("widget/text", x); + F.title = "Panel"; + F.desc = "Non interactive panel"; + F.widgets = [{name:"update", text:"Update", type:"button"}]; + F.prototype.createGradient = function(c) { + "" == this.properties.bgcolorTop || "" == this.properties.bgcolorBottom ? this.lineargradient = 0 : (this.lineargradient = c.createLinearGradient(0, 0, 0, this.size[1]), this.lineargradient.addColorStop(0, this.properties.bgcolorTop), this.lineargradient.addColorStop(1, this.properties.bgcolorBottom)); + }; + F.prototype.onDrawForeground = function(c) { + this.flags.collapsed || (null == this.lineargradient && this.createGradient(c), this.lineargradient && (c.lineWidth = 1, c.strokeStyle = this.properties.borderColor, c.fillStyle = this.lineargradient, this.properties.shadowSize ? (c.shadowColor = "#000", c.shadowOffsetX = 0, c.shadowOffsetY = 0, c.shadowBlur = this.properties.shadowSize) : c.shadowColor = "transparent", c.roundRect(0, 0, this.size[0] - 1, this.size[1] - 1, this.properties.shadowSize), c.fill(), c.shadowColor = "transparent", + c.stroke())); + }; + z.registerNodeType("widget/panel", F); +})(this); +(function(v) { + function c() { + this.addOutput("left_x_axis", "number"); + this.addOutput("left_y_axis", "number"); + this.addOutput("button_pressed", r.EVENT); + this.properties = {gamepad_index:0, threshold:0.1}; + this._left_axis = new Float32Array(2); + this._right_axis = new Float32Array(2); + this._triggers = new Float32Array(2); + this._previous_buttons = new Uint8Array(17); + this._current_buttons = new Uint8Array(17); + } + var r = v.LiteGraph; + c.title = "Gamepad"; + c.desc = "gets the input of the gamepad"; + c.CENTER = 0; + c.LEFT = 1; + c.RIGHT = 2; + c.UP = 4; + c.DOWN = 8; + c.zero = new Float32Array(2); + c.buttons = "a b x y lb rb lt rt back start ls rs home".split(" "); + c.prototype.onExecute = function() { + var m = this.getGamepad(), h = this.properties.threshold || 0.0; + m && (this._left_axis[0] = Math.abs(m.xbox.axes.lx) > h ? m.xbox.axes.lx : 0, this._left_axis[1] = Math.abs(m.xbox.axes.ly) > h ? m.xbox.axes.ly : 0, this._right_axis[0] = Math.abs(m.xbox.axes.rx) > h ? m.xbox.axes.rx : 0, this._right_axis[1] = Math.abs(m.xbox.axes.ry) > h ? m.xbox.axes.ry : 0, this._triggers[0] = Math.abs(m.xbox.axes.ltrigger) > h ? m.xbox.axes.ltrigger : 0, this._triggers[1] = Math.abs(m.xbox.axes.rtrigger) > h ? m.xbox.axes.rtrigger : 0); + if (this.outputs) { + for (h = 0; h < this.outputs.length; h++) { + var q = this.outputs[h]; + if (q.links && q.links.length) { + var l = null; + if (m) { + switch(q.name) { + case "left_axis": + l = this._left_axis; + break; + case "right_axis": + l = this._right_axis; + break; + case "left_x_axis": + l = this._left_axis[0]; + break; + case "left_y_axis": + l = this._left_axis[1]; + break; + case "right_x_axis": + l = this._right_axis[0]; + break; + case "right_y_axis": + l = this._right_axis[1]; + break; + case "trigger_left": + l = this._triggers[0]; + break; + case "trigger_right": + l = this._triggers[1]; + break; + case "a_button": + l = m.xbox.buttons.a ? 1 : 0; + break; + case "b_button": + l = m.xbox.buttons.b ? 1 : 0; + break; + case "x_button": + l = m.xbox.buttons.x ? 1 : 0; + break; + case "y_button": + l = m.xbox.buttons.y ? 1 : 0; + break; + case "lb_button": + l = m.xbox.buttons.lb ? 1 : 0; + break; + case "rb_button": + l = m.xbox.buttons.rb ? 1 : 0; + break; + case "ls_button": + l = m.xbox.buttons.ls ? 1 : 0; + break; + case "rs_button": + l = m.xbox.buttons.rs ? 1 : 0; + break; + case "hat_left": + l = m.xbox.hatmap & c.LEFT; + break; + case "hat_right": + l = m.xbox.hatmap & c.RIGHT; + break; + case "hat_up": + l = m.xbox.hatmap & c.UP; + break; + case "hat_down": + l = m.xbox.hatmap & c.DOWN; + break; + case "hat": + l = m.xbox.hatmap; + break; + case "start_button": + l = m.xbox.buttons.start ? 1 : 0; + break; + case "back_button": + l = m.xbox.buttons.back ? 1 : 0; + break; + case "button_pressed": + for (q = 0; q < this._current_buttons.length; ++q) { + this._current_buttons[q] && !this._previous_buttons[q] && this.triggerSlot(h, c.buttons[q]); + } + } + } else { + switch(q.name) { + case "button_pressed": + break; + case "left_axis": + case "right_axis": + l = c.zero; + break; + default: + l = 0; + } + } + this.setOutputData(h, l); + } + } + } + }; + c.mapping = {a:0, b:1, x:2, y:3, lb:4, rb:5, lt:6, rt:7, back:8, start:9, ls:10, rs:11}; + c.mapping_array = "a b x y lb rb lt rt back start ls rs".split(" "); + c.prototype.getGamepad = function() { + var m = navigator.getGamepads || navigator.webkitGetGamepads || navigator.mozGetGamepads; + if (!m) { + return null; + } + m = m.call(navigator); + this._previous_buttons.set(this._current_buttons); + for (var h = this.properties.gamepad_index; 4 > h; h++) { + if (m[h]) { + m = m[h]; + h = this.xbox_mapping; + h || (h = this.xbox_mapping = {axes:[], buttons:{}, hat:"", hatmap:c.CENTER}); + h.axes.lx = m.axes[0]; + h.axes.ly = m.axes[1]; + h.axes.rx = m.axes[2]; + h.axes.ry = m.axes[3]; + h.axes.ltrigger = m.buttons[6].value; + h.axes.rtrigger = m.buttons[7].value; + h.hat = ""; + h.hatmap = c.CENTER; + for (var q = 0; q < m.buttons.length; q++) { + if (this._current_buttons[q] = m.buttons[q].pressed, 12 > q) { + h.buttons[c.mapping_array[q]] = m.buttons[q].pressed, m.buttons[q].was_pressed && this.trigger(c.mapping_array[q] + "_button_event"); + } else { + switch(q) { + case 12: + m.buttons[q].pressed && (h.hat += "up", h.hatmap |= c.UP); + break; + case 13: + m.buttons[q].pressed && (h.hat += "down", h.hatmap |= c.DOWN); + break; + case 14: + m.buttons[q].pressed && (h.hat += "left", h.hatmap |= c.LEFT); + break; + case 15: + m.buttons[q].pressed && (h.hat += "right", h.hatmap |= c.RIGHT); + break; + case 16: + h.buttons.home = m.buttons[q].pressed; + } + } + } + m.xbox = h; + return m; + } + } + }; + c.prototype.onDrawBackground = function(c) { + if (!this.flags.collapsed) { + var h = this._left_axis, m = this._right_axis; + c.strokeStyle = "#88A"; + c.strokeRect(0.5 * (h[0] + 1) * this.size[0] - 4, 0.5 * (h[1] + 1) * this.size[1] - 4, 8, 8); + c.strokeStyle = "#8A8"; + c.strokeRect(0.5 * (m[0] + 1) * this.size[0] - 4, 0.5 * (m[1] + 1) * this.size[1] - 4, 8, 8); + h = this.size[1] / this._current_buttons.length; + c.fillStyle = "#AEB"; + for (m = 0; m < this._current_buttons.length; ++m) { + this._current_buttons[m] && c.fillRect(0, h * m, 6, h); + } + } + }; + c.prototype.onGetOutputs = function() { + return [["left_axis", "vec2"], ["right_axis", "vec2"], ["left_x_axis", "number"], ["left_y_axis", "number"], ["right_x_axis", "number"], ["right_y_axis", "number"], ["trigger_left", "number"], ["trigger_right", "number"], ["a_button", "number"], ["b_button", "number"], ["x_button", "number"], ["y_button", "number"], ["lb_button", "number"], ["rb_button", "number"], ["ls_button", "number"], ["rs_button", "number"], ["start_button", "number"], ["back_button", "number"], ["a_button_event", r.EVENT], + ["b_button_event", r.EVENT], ["x_button_event", r.EVENT], ["y_button_event", r.EVENT], ["lb_button_event", r.EVENT], ["rb_button_event", r.EVENT], ["ls_button_event", r.EVENT], ["rs_button_event", r.EVENT], ["start_button_event", r.EVENT], ["back_button_event", r.EVENT], ["hat_left", "number"], ["hat_right", "number"], ["hat_up", "number"], ["hat_down", "number"], ["hat", "number"], ["button_pressed", r.EVENT]]; + }; + r.registerNodeType("input/gamepad", c); +})(this); +(function(v) { + function c() { + this.addInput("in", "*"); + this.size = [80, 30]; + } + function r() { + this.addInput("in"); + this.addOutput("out"); + this.size = [80, 30]; + } + function m() { + this.addInput("in"); + this.addOutput("out"); + } + function h() { + this.addInput("in", "number", {locked:!0}); + this.addOutput("out", "number", {locked:!0}); + this.addOutput("clamped", "number", {locked:!0}); + this.addProperty("in", 0); + this.addProperty("in_min", 0); + this.addProperty("in_max", 1); + this.addProperty("out_min", 0); + this.addProperty("out_max", 1); + this.size = [120, 50]; + } + function q() { + this.addOutput("value", "number"); + this.addProperty("min", 0); + this.addProperty("max", 1); + this.size = [80, 30]; + } + function l() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.addProperty("min", 0); + this.addProperty("max", 1); + this.addProperty("smooth", !0); + this.addProperty("seed", 0); + this.addProperty("octaves", 1); + this.addProperty("persistence", 0.8); + this.addProperty("speed", 1); + this.size = [90, 30]; + } + function A() { + this.addOutput("out", "number"); + this.addProperty("min_time", 1); + this.addProperty("max_time", 2); + this.addProperty("duration", 0.2); + this.size = [90, 30]; + this._blink_time = this._remaining_time = 0; + } + function y() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.size = [80, 30]; + this.addProperty("min", 0); + this.addProperty("max", 1); + } + function x() { + this.properties = {f:0.5}; + this.addInput("A", "number"); + this.addInput("B", "number"); + this.addOutput("out", "number"); + } + function F() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.size = [80, 30]; + } + function z() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.size = [80, 30]; + } + function e() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.size = [80, 30]; + } + function B() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.size = [80, 30]; + this.properties = {A:0, B:1}; + } + function E() { + this.addInput("in", "number", {label:""}); + this.addOutput("out", "number", {label:""}); + this.size = [80, 30]; + this.addProperty("factor", 1); + } + function u() { + this.addInput("v", "boolean"); + this.addInput("A"); + this.addInput("B"); + this.addOutput("out"); + } + function H() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.size = [80, 30]; + this.addProperty("samples", 10); + this._values = new Float32Array(10); + this._current = 0; + } + function n() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.addProperty("factor", 0.1); + this.size = [80, 30]; + this._value = null; + } + function p() { + this.addInput("A", "number,array,object"); + this.addInput("B", "number"); + this.addOutput("=", "number"); + this.addProperty("A", 1); + this.addProperty("B", 1); + this.addProperty("OP", "+", "enum", {values:p.values}); + this._func = function(a, b) { + return a + b; + }; + this._result = []; + } + function k() { + this.addInput("A", "number"); + this.addInput("B", "number"); + this.addOutput("A==B", "boolean"); + this.addOutput("A!=B", "boolean"); + this.addProperty("A", 0); + this.addProperty("B", 0); + } + function a() { + this.addInput("A", "number"); + this.addInput("B", "number"); + this.addOutput("true", "boolean"); + this.addOutput("false", "boolean"); + this.addProperty("A", 1); + this.addProperty("B", 1); + this.addProperty("OP", ">", "enum", {values:a.values}); + this.size = [80, 60]; + } + function b() { + this.addInput("inc", "number"); + this.addOutput("total", "number"); + this.addProperty("increment", 1); + this.addProperty("value", 0); + } + function d() { + this.addInput("v", "number"); + this.addOutput("sin", "number"); + this.addProperty("amplitude", 1); + this.addProperty("offset", 0); + this.bgImageUrl = "nodes/imgs/icon-sin.png"; + } + function g() { + this.addInput("x", "number"); + this.addInput("y", "number"); + this.addOutput("", "number"); + this.properties = {x:1.0, y:1.0, formula:"x+y"}; + this.code_widget = this.addWidget("text", "F(x,y)", this.properties.formula, function(a, b, d) { + d.properties.formula = a; + }); + this.addWidget("toggle", "allow", D.allow_scripts, function(a) { + D.allow_scripts = a; + }); + this._func = null; + } + function f() { + this.addInput("vec2", "vec2"); + this.addOutput("x", "number"); + this.addOutput("y", "number"); + } + function C() { + this.addInputs([["x", "number"], ["y", "number"]]); + this.addOutput("vec2", "vec2"); + this.properties = {x:0, y:0}; + this._data = new Float32Array(2); + } + function w() { + this.addInput("vec3", "vec3"); + this.addOutput("x", "number"); + this.addOutput("y", "number"); + this.addOutput("z", "number"); + } + function I() { + this.addInputs([["x", "number"], ["y", "number"], ["z", "number"]]); + this.addOutput("vec3", "vec3"); + this.properties = {x:0, y:0, z:0}; + this._data = new Float32Array(3); + } + function K() { + this.addInput("vec4", "vec4"); + this.addOutput("x", "number"); + this.addOutput("y", "number"); + this.addOutput("z", "number"); + this.addOutput("w", "number"); + } + function L() { + this.addInputs([["x", "number"], ["y", "number"], ["z", "number"], ["w", "number"]]); + this.addOutput("vec4", "vec4"); + this.properties = {x:0, y:0, z:0, w:0}; + this._data = new Float32Array(4); + } + var D = v.LiteGraph; + c.title = "Converter"; + c.desc = "type A to type B"; + c.prototype.onExecute = function() { + var a = this.getInputData(0); + if (null != a && this.outputs) { + for (var b = 0; b < this.outputs.length; b++) { + var d = this.outputs[b]; + if (d.links && d.links.length) { + var c = null; + switch(d.name) { + case "number": + c = a.length ? a[0] : parseFloat(a); + break; + case "vec2": + case "vec3": + case "vec4": + c = 1; + switch(d.name) { + case "vec2": + c = 2; + break; + case "vec3": + c = 3; + break; + case "vec4": + c = 4; + }c = new Float32Array(c); + if (a.length) { + for (d = 0; d < a.length && d < c.length; d++) { + c[d] = a[d]; + } + } else { + c[0] = parseFloat(a); + } + } + this.setOutputData(b, c); + } + } + } + }; + c.prototype.onGetOutputs = function() { + return [["number", "number"], ["vec2", "vec2"], ["vec3", "vec3"], ["vec4", "vec4"]]; + }; + D.registerNodeType("math/converter", c); + r.title = "Bypass"; + r.desc = "removes the type"; + r.prototype.onExecute = function() { + var a = this.getInputData(0); + this.setOutputData(0, a); + }; + D.registerNodeType("math/bypass", r); + m.title = "to Number"; + m.desc = "Cast to number"; + m.prototype.onExecute = function() { + var a = this.getInputData(0); + this.setOutputData(0, Number(a)); + }; + D.registerNodeType("math/to_number", m); + h.title = "Range"; + h.desc = "Convert a number from one range to another"; + h.prototype.getTitle = function() { + return this.flags.collapsed ? (this._last_v || 0).toFixed(2) : this.title; + }; + h.prototype.onExecute = function() { + if (this.inputs) { + for (var a = 0; a < this.inputs.length; a++) { + var b = this.inputs[a], d = this.getInputData(a); + void 0 !== d && (this.properties[b.name] = d); + } + } + d = this.properties["in"]; + if (void 0 === d || null === d || d.constructor !== Number) { + d = 0; + } + a = this.properties.in_min; + b = this.properties.out_min; + var c = this.properties.out_max; + this._last_v = (d - a) / (this.properties.in_max - a) * (c - b) + b; + this.setOutputData(0, this._last_v); + this.setOutputData(1, Math.clamp(this._last_v, b, c)); + }; + h.prototype.onDrawBackground = function(a) { + this.outputs[0].label = this._last_v ? this._last_v.toFixed(3) : "?"; + }; + h.prototype.onGetInputs = function() { + return [["in_min", "number"], ["in_max", "number"], ["out_min", "number"], ["out_max", "number"]]; + }; + D.registerNodeType("math/range", h); + q.title = "Rand"; + q.desc = "Random number"; + q.prototype.onExecute = function() { + if (this.inputs) { + for (var a = 0; a < this.inputs.length; a++) { + var b = this.inputs[a], d = this.getInputData(a); + void 0 !== d && (this.properties[b.name] = d); + } + } + a = this.properties.min; + this._last_v = Math.random() * (this.properties.max - a) + a; + this.setOutputData(0, this._last_v); + }; + q.prototype.onDrawBackground = function(a) { + this.outputs[0].label = (this._last_v || 0).toFixed(3); + }; + q.prototype.onGetInputs = function() { + return [["min", "number"], ["max", "number"]]; + }; + D.registerNodeType("math/rand", q); + l.title = "Noise"; + l.desc = "Random number with temporal continuity"; + l.data = null; + l.getValue = function(a, b) { + if (!l.data) { + l.data = new Float32Array(1024); + for (var d = 0; d < l.data.length; ++d) { + l.data[d] = Math.random(); + } + } + a %= 1024; + 0 > a && (a += 1024); + var c = Math.floor(a); + a -= c; + d = l.data[c]; + c = l.data[1023 == c ? 0 : c + 1]; + b && (a = a * a * a * (a * (6.0 * a - 15.0) + 10.0)); + return d * (1 - a) + c * a; + }; + l.prototype.onExecute = function() { + var a = this.getInputData(0) || 0, b = this.properties.octaves || 1, d = 0, c = 1; + a += this.properties.seed || 0; + for (var k = this.properties.speed || 1, f = 0, g = 0; g < b && !(d += l.getValue(a * (1 + g) * k, this.properties.smooth) * c, f += c, c *= this.properties.persistence, 0.001 > c); ++g) { + } + a = this.properties.min; + this._last_v = d / f * (this.properties.max - a) + a; + this.setOutputData(0, this._last_v); + }; + l.prototype.onDrawBackground = function(a) { + this.outputs[0].label = (this._last_v || 0).toFixed(3); + }; + D.registerNodeType("math/noise", l); + A.title = "Spikes"; + A.desc = "spike every random time"; + A.prototype.onExecute = function() { + var a = this.graph.elapsed_time; + this._remaining_time -= a; + this._blink_time -= a; + a = 0; + 0 < this._blink_time && (a = 1 / (Math.pow(this._blink_time / this.properties.duration * 8 - 4, 4) + 1)); + 0 > this._remaining_time ? (this._remaining_time = Math.random() * (this.properties.max_time - this.properties.min_time) + this.properties.min_time, this._blink_time = this.properties.duration, this.boxcolor = "#FFF") : this.boxcolor = "#000"; + this.setOutputData(0, a); + }; + D.registerNodeType("math/spikes", A); + y.title = "Clamp"; + y.desc = "Clamp number between min and max"; + y.prototype.onExecute = function() { + var a = this.getInputData(0); + null != a && (a = Math.max(this.properties.min, a), a = Math.min(this.properties.max, a), this.setOutputData(0, a)); + }; + y.prototype.getCode = function(a) { + a = ""; + this.isInputConnected(0) && (a += "clamp({{0}}," + this.properties.min + "," + this.properties.max + ")"); + return a; + }; + D.registerNodeType("math/clamp", y); + x.title = "Lerp"; + x.desc = "Linear Interpolation"; + x.prototype.onExecute = function() { + var a = this.getInputData(0); + null == a && (a = 0); + var b = this.getInputData(1); + null == b && (b = 0); + var d = this.properties.f, c = this.getInputData(2); + void 0 !== c && (d = c); + this.setOutputData(0, a * (1 - d) + b * d); + }; + x.prototype.onGetInputs = function() { + return [["f", "number"]]; + }; + D.registerNodeType("math/lerp", x); + F.title = "Abs"; + F.desc = "Absolute"; + F.prototype.onExecute = function() { + var a = this.getInputData(0); + null != a && this.setOutputData(0, Math.abs(a)); + }; + D.registerNodeType("math/abs", F); + z.title = "Floor"; + z.desc = "Floor number to remove fractional part"; + z.prototype.onExecute = function() { + var a = this.getInputData(0); + null != a && this.setOutputData(0, Math.floor(a)); + }; + D.registerNodeType("math/floor", z); + e.title = "Frac"; + e.desc = "Returns fractional part"; + e.prototype.onExecute = function() { + var a = this.getInputData(0); + null != a && this.setOutputData(0, a % 1); + }; + D.registerNodeType("math/frac", e); + B.title = "Smoothstep"; + B.desc = "Smoothstep"; + B.prototype.onExecute = function() { + var a = this.getInputData(0); + if (void 0 !== a) { + var b = this.properties.A; + a = Math.clamp((a - b) / (this.properties.B - b), 0.0, 1.0); + this.setOutputData(0, a * a * (3 - 2 * a)); + } + }; + D.registerNodeType("math/smoothstep", B); + E.title = "Scale"; + E.desc = "v * factor"; + E.prototype.onExecute = function() { + var a = this.getInputData(0); + null != a && this.setOutputData(0, a * this.properties.factor); + }; + D.registerNodeType("math/scale", E); + u.title = "Gate"; + u.desc = "if v is true, then outputs A, otherwise B"; + u.prototype.onExecute = function() { + var a = this.getInputData(0); + this.setOutputData(0, this.getInputData(a ? 1 : 2)); + }; + D.registerNodeType("math/gate", u); + H.title = "Average"; + H.desc = "Average Filter"; + H.prototype.onExecute = function() { + var a = this.getInputData(0); + null == a && (a = 0); + var b = this._values.length; + this._values[this._current % b] = a; + this._current += 1; + this._current > b && (this._current = 0); + for (var d = a = 0; d < b; ++d) { + a += this._values[d]; + } + this.setOutputData(0, a / b); + }; + H.prototype.onPropertyChanged = function(a, b) { + 1 > b && (b = 1); + this.properties.samples = Math.round(b); + a = this._values; + this._values = new Float32Array(this.properties.samples); + a.length <= this._values.length ? this._values.set(a) : this._values.set(a.subarray(0, this._values.length)); + }; + D.registerNodeType("math/average", H); + n.title = "TendTo"; + n.desc = "moves the output value always closer to the input"; + n.prototype.onExecute = function() { + var a = this.getInputData(0); + null == a && (a = 0); + var b = this.properties.factor; + this._value = null == this._value ? a : this._value * (1 - b) + a * b; + this.setOutputData(0, this._value); + }; + D.registerNodeType("math/tendTo", n); + p.values = "+ - * / % ^ max min".split(" "); + p.title = "Operation"; + p.desc = "Easy math operators"; + p["@OP"] = {type:"enum", title:"operation", values:p.values}; + p.size = [100, 60]; + p.prototype.getTitle = function() { + return "max" == this.properties.OP || "min" == this.properties.OP ? this.properties.OP + "(A,B)" : "A " + this.properties.OP + " B"; + }; + p.prototype.setValue = function(a) { + "string" == typeof a && (a = parseFloat(a)); + this.properties.value = a; + }; + p.prototype.onPropertyChanged = function(a, b) { + if ("OP" == a) { + switch(this.properties.OP) { + case "+": + this._func = function(a, b) { + return a + b; + }; + break; + case "-": + this._func = function(a, b) { + return a - b; + }; + break; + case "x": + case "X": + case "*": + this._func = function(a, b) { + return a * b; + }; + break; + case "/": + this._func = function(a, b) { + return a / b; + }; + break; + case "%": + this._func = function(a, b) { + return a % b; + }; + break; + case "^": + this._func = function(a, b) { + return Math.pow(a, b); + }; + break; + case "max": + this._func = function(a, b) { + return Math.max(a, b); + }; + break; + case "min": + this._func = function(a, b) { + return Math.min(a, b); + }; + break; + default: + console.warn("Unknown operation: " + this.properties.OP), this._func = function(a) { + return a; + }; + } + } + }; + p.prototype.onExecute = function() { + var a = this.getInputData(0), b = this.getInputData(1); + null != a ? a.constructor === Number && (this.properties.A = a) : a = this.properties.A; + null != b ? this.properties.B = b : b = this.properties.B; + if (a.constructor === Number) { + var d = this._func(a, b); + } else { + if (a.constructor === Array) { + d = this._result; + d.length = a.length; + for (var c = 0; c < a.length; ++c) { + d[c] = this._func(a[c], b); + } + } else { + for (c in d = {}, a) { + d[c] = this._func(a[c], b); + } + } + } + this.setOutputData(0, d); + }; + p.prototype.onDrawBackground = function(a) { + this.flags.collapsed || (a.font = "40px Arial", a.fillStyle = "#666", a.textAlign = "center", a.fillText(this.properties.OP, 0.5 * this.size[0], 0.5 * (this.size[1] + D.NODE_TITLE_HEIGHT)), a.textAlign = "left"); + }; + D.registerNodeType("math/operation", p); + D.registerSearchboxExtra("math/operation", "MAX", {properties:{OP:"max"}, title:"MAX()"}); + D.registerSearchboxExtra("math/operation", "MIN", {properties:{OP:"min"}, title:"MIN()"}); + k.title = "Compare"; + k.desc = "compares between two values"; + k.prototype.onExecute = function() { + var a = this.getInputData(0), b = this.getInputData(1); + void 0 !== a ? this.properties.A = a : a = this.properties.A; + void 0 !== b ? this.properties.B = b : b = this.properties.B; + for (var d = 0, c = this.outputs.length; d < c; ++d) { + var k = this.outputs[d]; + if (k.links && k.links.length) { + switch(k.name) { + case "A==B": + var f = a == b; + break; + case "A!=B": + f = a != b; + break; + case "A>B": + f = a > b; + break; + case "A=B": + f = a >= b; + } + this.setOutputData(d, f); + } + } + }; + k.prototype.onGetOutputs = function() { + return [["A==B", "boolean"], ["A!=B", "boolean"], ["A>B", "boolean"], ["A=B", "boolean"], ["A<=B", "boolean"]]; + }; + D.registerNodeType("math/compare", k); + D.registerSearchboxExtra("math/compare", "==", {outputs:[["A==B", "boolean"]], title:"A==B"}); + D.registerSearchboxExtra("math/compare", "!=", {outputs:[["A!=B", "boolean"]], title:"A!=B"}); + D.registerSearchboxExtra("math/compare", ">", {outputs:[["A>B", "boolean"]], title:"A>B"}); + D.registerSearchboxExtra("math/compare", "<", {outputs:[["A=", {outputs:[["A>=B", "boolean"]], title:"A>=B"}); + D.registerSearchboxExtra("math/compare", "<=", {outputs:[["A<=B", "boolean"]], title:"A<=B"}); + a.values = "> < == != <= >= || &&".split(" "); + a["@OP"] = {type:"enum", title:"operation", values:a.values}; + a.title = "Condition"; + a.desc = "evaluates condition between A and B"; + a.prototype.getTitle = function() { + return "A " + this.properties.OP + " B"; + }; + a.prototype.onExecute = function() { + var a = this.getInputData(0); + void 0 === a ? a = this.properties.A : this.properties.A = a; + var b = this.getInputData(1); + void 0 === b ? b = this.properties.B : this.properties.B = b; + var d = !0; + switch(this.properties.OP) { + case ">": + d = a > b; + break; + case "<": + d = a < b; + break; + case "==": + d = a == b; + break; + case "!=": + d = a != b; + break; + case "<=": + d = a <= b; + break; + case ">=": + d = a >= b; + break; + case "||": + d = a || b; + break; + case "&&": + d = a && b; + } + this.setOutputData(0, d); + this.setOutputData(1, !d); + }; + D.registerNodeType("math/condition", a); + b.title = "Accumulate"; + b.desc = "Increments a value every time"; + b.prototype.onExecute = function() { + null === this.properties.value && (this.properties.value = 0); + var a = this.getInputData(0); + this.properties.value = null !== a ? this.properties.value + a : this.properties.value + this.properties.increment; + this.setOutputData(0, this.properties.value); + }; + D.registerNodeType("math/accumulate", b); + d.title = "Trigonometry"; + d.desc = "Sin Cos Tan"; + d.prototype.onExecute = function() { + var a = this.getInputData(0); + null == a && (a = 0); + var b = this.properties.amplitude, d = this.findInputSlot("amplitude"); + -1 != d && (b = this.getInputData(d)); + var c = this.properties.offset; + d = this.findInputSlot("offset"); + -1 != d && (c = this.getInputData(d)); + d = 0; + for (var f = this.outputs.length; d < f; ++d) { + switch(this.outputs[d].name) { + case "sin": + var k = Math.sin(a); + break; + case "cos": + k = Math.cos(a); + break; + case "tan": + k = Math.tan(a); + break; + case "asin": + k = Math.asin(a); + break; + case "acos": + k = Math.acos(a); + break; + case "atan": + k = Math.atan(a); + } + this.setOutputData(d, b * k + c); + } + }; + d.prototype.onGetInputs = function() { + return [["v", "number"], ["amplitude", "number"], ["offset", "number"]]; + }; + d.prototype.onGetOutputs = function() { + return [["sin", "number"], ["cos", "number"], ["tan", "number"], ["asin", "number"], ["acos", "number"], ["atan", "number"]]; + }; + D.registerNodeType("math/trigonometry", d); + D.registerSearchboxExtra("math/trigonometry", "SIN()", {outputs:[["sin", "number"]], title:"SIN()"}); + D.registerSearchboxExtra("math/trigonometry", "COS()", {outputs:[["cos", "number"]], title:"COS()"}); + D.registerSearchboxExtra("math/trigonometry", "TAN()", {outputs:[["tan", "number"]], title:"TAN()"}); + g.title = "Formula"; + g.desc = "Compute formula"; + g.size = [160, 100]; + H.prototype.onPropertyChanged = function(a, b) { + "formula" == a && (this.code_widget.value = b); + }; + g.prototype.onExecute = function() { + if (D.allow_scripts) { + var a = this.getInputData(0), b = this.getInputData(1); + null != a ? this.properties.x = a : a = this.properties.x; + null != b ? this.properties.y = b : b = this.properties.y; + try { + this._func && this._func_code == this.properties.formula || (this._func = new Function("x", "y", "TIME", "return " + this.properties.formula), this._func_code = this.properties.formula); + var d = this._func(a, b, this.graph.globaltime); + this.boxcolor = null; + } catch (M) { + this.boxcolor = "red"; + } + this.setOutputData(0, d); + } + }; + g.prototype.getTitle = function() { + return this._func_code || "Formula"; + }; + g.prototype.onDrawBackground = function() { + var a = this.properties.formula; + this.outputs && this.outputs.length && (this.outputs[0].label = a); + }; + D.registerNodeType("math/formula", g); + f.title = "Vec2->XY"; + f.desc = "vector 2 to components"; + f.prototype.onExecute = function() { + var a = this.getInputData(0); + null != a && (this.setOutputData(0, a[0]), this.setOutputData(1, a[1])); + }; + D.registerNodeType("math3d/vec2-to-xy", f); + C.title = "XY->Vec2"; + C.desc = "components to vector2"; + C.prototype.onExecute = function() { + var a = this.getInputData(0); + null == a && (a = this.properties.x); + var b = this.getInputData(1); + null == b && (b = this.properties.y); + var d = this._data; + d[0] = a; + d[1] = b; + this.setOutputData(0, d); + }; + D.registerNodeType("math3d/xy-to-vec2", C); + w.title = "Vec3->XYZ"; + w.desc = "vector 3 to components"; + w.prototype.onExecute = function() { + var a = this.getInputData(0); + null != a && (this.setOutputData(0, a[0]), this.setOutputData(1, a[1]), this.setOutputData(2, a[2])); + }; + D.registerNodeType("math3d/vec3-to-xyz", w); + I.title = "XYZ->Vec3"; + I.desc = "components to vector3"; + I.prototype.onExecute = function() { + var a = this.getInputData(0); + null == a && (a = this.properties.x); + var b = this.getInputData(1); + null == b && (b = this.properties.y); + var d = this.getInputData(2); + null == d && (d = this.properties.z); + var c = this._data; + c[0] = a; + c[1] = b; + c[2] = d; + this.setOutputData(0, c); + }; + D.registerNodeType("math3d/xyz-to-vec3", I); + K.title = "Vec4->XYZW"; + K.desc = "vector 4 to components"; + K.prototype.onExecute = function() { + var a = this.getInputData(0); + null != a && (this.setOutputData(0, a[0]), this.setOutputData(1, a[1]), this.setOutputData(2, a[2]), this.setOutputData(3, a[3])); + }; + D.registerNodeType("math3d/vec4-to-xyzw", K); + L.title = "XYZW->Vec4"; + L.desc = "components to vector4"; + L.prototype.onExecute = function() { + var a = this.getInputData(0); + null == a && (a = this.properties.x); + var b = this.getInputData(1); + null == b && (b = this.properties.y); + var d = this.getInputData(2); + null == d && (d = this.properties.z); + var c = this.getInputData(3); + null == c && (c = this.properties.w); + var k = this._data; + k[0] = a; + k[1] = b; + k[2] = d; + k[3] = c; + this.setOutputData(0, k); + }; + D.registerNodeType("math3d/xyzw-to-vec4", L); +})(this); +(function(v) { + function c() { + this.addInput("sel", "number"); + this.addInput("A"); + this.addInput("B"); + this.addInput("C"); + this.addInput("D"); + this.addOutput("out"); + this.selected = 0; + } + function r() { + this.properties = {sequence:"A,B,C"}; + this.addInput("index", "number"); + this.addInput("seq"); + this.addOutput("out"); + this.index = 0; + this.values = this.properties.sequence.split(","); + } + var m = v.LiteGraph; + c.title = "Selector"; + c.desc = "selects an output"; + c.prototype.onDrawBackground = function(c) { + if (!this.flags.collapsed) { + c.fillStyle = "#AFB"; + var h = (this.selected + 1) * m.NODE_SLOT_HEIGHT + 6; + c.beginPath(); + c.moveTo(50, h); + c.lineTo(50, h + m.NODE_SLOT_HEIGHT); + c.lineTo(34, h + 0.5 * m.NODE_SLOT_HEIGHT); + c.fill(); + } + }; + c.prototype.onExecute = function() { + var c = this.getInputData(0); + if (null == c || c.constructor !== Number) { + c = 0; + } + this.selected = c = Math.round(c) % (this.inputs.length - 1); + c = this.getInputData(c + 1); + void 0 !== c && this.setOutputData(0, c); + }; + c.prototype.onGetInputs = function() { + return [["E", 0], ["F", 0], ["G", 0], ["H", 0]]; + }; + m.registerNodeType("logic/selector", c); + r.title = "Sequence"; + r.desc = "select one element from a sequence from a string"; + r.prototype.onPropertyChanged = function(c, m) { + "sequence" == c && (this.values = m.split(",")); + }; + r.prototype.onExecute = function() { + var c = this.getInputData(1); + c && c != this.current_sequence && (this.values = c.split(","), this.current_sequence = c); + c = this.getInputData(0); + null == c && (c = 0); + this.index = c = Math.round(c) % this.values.length; + this.setOutputData(0, this.values[c]); + }; + m.registerNodeType("logic/sequence", r); +})(this); +(function(v) { + function c() { + this.addOutput("tex", "Texture"); + this.addOutput("name", "string"); + this.properties = {name:"", filter:!0}; + this.size = [c.image_preview_size, c.image_preview_size]; + } + function r() { + this.addInput("Texture", "Texture"); + this.properties = {flipY:!1}; + this.size = [c.image_preview_size, c.image_preview_size]; + } + function m() { + this.addInput("Texture", "Texture"); + this.addOutput("tex", "Texture"); + this.addOutput("name", "string"); + this.properties = {name:"", generate_mipmaps:!1}; + } + function h() { + this.addInput("Texture", "Texture"); + this.addInput("TextureB", "Texture"); + this.addInput("value", "number"); + this.addOutput("Texture", "Texture"); + this.help = "

pixelcode must be vec3, uvcode must be vec2, is optional

\r\n\t\t

uv: tex. coords

color: texture colorB: textureB

time: scene time value: input value

For multiline you must type: result = ...

"; + this.properties = {value:1, pixelcode:"color + colorB * value", uvcode:"", precision:c.DEFAULT}; + this.has_error = !1; + } + function q() { + this.addOutput("out", "Texture"); + this.properties = {code:"", u_value:1, u_color:[1, 1, 1, 1], width:512, height:512, precision:c.DEFAULT}; + this.properties.code = q.pixel_shader; + this._uniforms = {u_value:1, u_color:vec4.create(), in_texture:0, texSize:vec2.create(), time:0}; + } + function l() { + this.addInput("in", "Texture"); + this.addInput("scale", "vec2"); + this.addInput("offset", "vec2"); + this.addOutput("out", "Texture"); + this.properties = {offset:vec2.fromValues(0, 0), scale:vec2.fromValues(1, 1), precision:c.DEFAULT}; + } + function A() { + this.addInput("in", "Texture"); + this.addInput("warp", "Texture"); + this.addInput("factor", "number"); + this.addOutput("out", "Texture"); + this.properties = {factor:0.01, scale:[1, 1], offset:[0, 0], precision:c.DEFAULT}; + this._uniforms = {u_texture:0, u_textureB:1, u_factor:1, u_scale:vec2.create(), u_offset:vec2.create()}; + } + function y() { + this.addInput("Texture", "Texture"); + this.properties = {additive:!1, antialiasing:!1, filter:!0, disable_alpha:!1, gamma:1.0, viewport:[0, 0, 1, 1]}; + this.size[0] = 130; + } + function x() { + this.addInput("Texture", "Texture"); + this.addOutput("", "Texture"); + this.properties = {size:0, generate_mipmaps:!1, precision:c.DEFAULT}; + } + function F() { + this.addInput("Texture", "Texture"); + this.addOutput("", "Texture"); + this.properties = {iterations:1, generate_mipmaps:!1, precision:c.DEFAULT}; + } + function z() { + this.addInput("Texture", "Texture"); + this.addOutput("tex", "Texture"); + this.addOutput("avg", "vec4"); + this.addOutput("lum", "number"); + this.properties = {use_previous_frame:!0, high_quality:!1}; + this._uniforms = {u_texture:0, u_mipmap_offset:0}; + this._luminance = new Float32Array(4); + } + function e() { + this.addInput("in", "Texture"); + this.addInput("factor", "Number"); + this.addOutput("out", "Texture"); + this.properties = {factor:0.5}; + this._uniforms = {u_texture:0, u_textureB:1, u_factor:this.properties.factor}; + } + function B() { + this.addInput("in", "Texture"); + this.addOutput("avg", "Texture"); + this.addOutput("array", "Texture"); + this.properties = {samples:64, frames_interval:1}; + this._uniforms = {u_texture:0, u_textureB:1, u_samples:this.properties.samples, u_isamples:1 / this.properties.samples}; + this.frame = 0; + } + function E() { + this.addInput("Image", "image"); + this.addOutput("", "Texture"); + this.properties = {}; + } + function u() { + this.addInput("Texture", "Texture"); + this.addInput("LUT", "Texture"); + this.addInput("Intensity", "number"); + this.addOutput("", "Texture"); + this.properties = {enabled:!0, intensity:1, precision:c.DEFAULT, texture:null}; + u._shader || (u._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, u.pixel_shader)); + } + function H() { + this.addInput("Texture", "Texture"); + this.addInput("Atlas", "Texture"); + this.addOutput("", "Texture"); + this.properties = {enabled:!0, num_row_symbols:4, symbol_size:16, brightness:1, colorize:!1, filter:!1, invert:!1, precision:c.DEFAULT, texture:null}; + H._shader || (H._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, H.pixel_shader)); + this._uniforms = {u_texture:0, u_textureB:1, u_row_simbols:4, u_simbol_size:16, u_res:vec2.create()}; + } + function n() { + this.addInput("Texture", "Texture"); + this.addOutput("R", "Texture"); + this.addOutput("G", "Texture"); + this.addOutput("B", "Texture"); + this.addOutput("A", "Texture"); + n._shader || (n._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, n.pixel_shader)); + } + function p() { + this.addInput("R", "Texture"); + this.addInput("G", "Texture"); + this.addInput("B", "Texture"); + this.addInput("A", "Texture"); + this.addOutput("Texture", "Texture"); + this.properties = {precision:c.DEFAULT, R:1, G:1, B:1, A:1}; + this._color = vec4.create(); + this._uniforms = {u_textureR:0, u_textureG:1, u_textureB:2, u_textureA:3, u_color:this._color}; + } + function k() { + this.addOutput("Texture", "Texture"); + this._tex_color = vec4.create(); + this.properties = {color:vec4.create(), precision:c.DEFAULT}; + } + function a() { + this.addInput("A", "color"); + this.addInput("B", "color"); + this.addOutput("Texture", "Texture"); + this.properties = {angle:0, scale:1, A:[0, 0, 0], B:[1, 1, 1], texture_size:32}; + a._shader || (a._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, a.pixel_shader)); + this._uniforms = {u_angle:0, u_colorA:vec3.create(), u_colorB:vec3.create()}; + } + function b() { + this.addInput("A", "Texture"); + this.addInput("B", "Texture"); + this.addInput("Mixer", "Texture"); + this.addOutput("Texture", "Texture"); + this.properties = {factor:0.5, size_from_biggest:!0, invert:!1, precision:c.DEFAULT}; + this._uniforms = {u_textureA:0, u_textureB:1, u_textureMix:2, u_mix:vec4.create()}; + } + function d() { + this.addInput("Tex.", "Texture"); + this.addOutput("Edges", "Texture"); + this.properties = {invert:!0, threshold:!1, factor:1, precision:c.DEFAULT}; + d._shader || (d._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, d.pixel_shader)); + } + function g() { + this.addInput("Texture", "Texture"); + this.addInput("Distance", "number"); + this.addInput("Range", "number"); + this.addOutput("Texture", "Texture"); + this.properties = {distance:100, range:50, only_depth:!1, high_precision:!1}; + this._uniforms = {u_texture:0, u_distance:100, u_range:50, u_camera_planes:null}; + } + function f() { + this.addInput("Texture", "Texture"); + this.addOutput("Texture", "Texture"); + this.properties = {precision:c.DEFAULT, invert:!1}; + this._uniforms = {u_texture:0, u_camera_planes:null, u_ires:vec2.create()}; + } + function C() { + this.addInput("Texture", "Texture"); + this.addInput("Iterations", "number"); + this.addInput("Intensity", "number"); + this.addOutput("Blurred", "Texture"); + this.properties = {intensity:1, iterations:1, preserve_aspect:!1, scale:[1, 1], precision:c.DEFAULT}; + } + function w() { + this.addInput("in", "Texture"); + this.addInput("dirt", "Texture"); + this.addOutput("out", "Texture"); + this.addOutput("glow", "Texture"); + this.properties = {enabled:!0, intensity:1, persistence:0.99, iterations:16, threshold:0, scale:1, dirt_factor:0.5, precision:c.DEFAULT}; + this._textures = []; + this._uniforms = {u_intensity:1, u_texture:0, u_glow_texture:1, u_threshold:0, u_texel_size:vec2.create()}; + } + function I() { + this.addInput("Texture", "Texture"); + this.addOutput("Filtered", "Texture"); + this.properties = {intensity:1, radius:5}; + } + function K() { + this.addInput("Texture", "Texture"); + this.addOutput("Filtered", "Texture"); + this.properties = {sigma:1.4, k:1.6, p:21.7, epsilon:79, phi:0.017}; + } + function L() { + this.addOutput("Webcam", "Texture"); + this.properties = {texture_name:"", facingMode:"user"}; + this.boxcolor = "black"; + this.version = 0; + } + function D() { + this.addInput("in", "Texture"); + this.addInput("f", "number"); + this.addOutput("out", "Texture"); + this.properties = {enabled:!0, factor:1, precision:c.LOW}; + this._uniforms = {u_texture:0, u_factor:1}; + } + function t() { + this.addInput("in", ""); + this.properties = {precision:c.LOW, width:0, height:0, channels:1}; + this.addOutput("out", "Texture"); + } + function J() { + this.addInput("in", "Texture"); + this.addOutput("out", "Texture"); + this.properties = {precision:c.LOW, split_channels:!1}; + this._values = new Uint8Array(1024); + this._values.fill(255); + this._curve_texture = null; + this._uniforms = {u_texture:0, u_curve:1, u_range:1.0}; + this._must_update = !0; + this._points = {RGB:[[0, 0], [1, 1]], R:[[0, 0], [1, 1]], G:[[0, 0], [1, 1]], B:[[0, 0], [1, 1]]}; + this.curve_editor = null; + this.addWidget("toggle", "Split Channels", !1, "split_channels"); + this.addWidget("combo", "Channel", "RGB", {values:["RGB", "R", "G", "B"]}); + this.curve_offset = 68; + this.size = [240, 160]; + } + function N() { + this.addInput("in", "Texture"); + this.addInput("exp", "number"); + this.addOutput("out", "Texture"); + this.properties = {exposition:1, precision:c.LOW}; + this._uniforms = {u_texture:0, u_exposition:1}; + } + function M() { + this.addInput("in", "Texture"); + this.addInput("avg", "number,Texture"); + this.addOutput("out", "Texture"); + this.properties = {enabled:!0, scale:1, gamma:1, average_lum:1, lum_white:1, precision:c.LOW}; + this._uniforms = {u_texture:0, u_lumwhite2:1, u_igamma:1, u_scale:1, u_average_lum:1}; + } + function P() { + this.addOutput("out", "Texture"); + this.properties = {width:512, height:512, seed:0, persistence:0.1, octaves:8, scale:1, offset:[0, 0], amplitude:1, precision:c.DEFAULT}; + this._key = 0; + this._texture = null; + this._uniforms = {u_persistence:0.1, u_seed:0, u_offset:vec2.create(), u_scale:1, u_viewport:vec2.create()}; + } + function O() { + this.addInput("v"); + this.addOutput("out", "Texture"); + this.properties = {code:O.default_code, width:512, height:512, clear:!0, precision:c.DEFAULT, use_html_canvas:!1}; + this._temp_texture = this._func = null; + this.compileCode(); + } + function Q() { + this.addInput("in", "Texture"); + this.addOutput("out", "Texture"); + this.properties = {key_color:vec3.fromValues(0, 1, 0), threshold:0.8, slope:0.2, precision:c.DEFAULT}; + } + function R() { + this.addInput("in", "texture"); + this.addInput("yaw", "number"); + this.addOutput("out", "texture"); + this.properties = {yaw:0}; + } + var G = v.LiteGraph, V = v.LGraphCanvas; + v.LGraphTexture = null; + "undefined" != typeof GL && (V.link_type_colors.Texture = "#987", v.LGraphTexture = c, c.title = "Texture", c.desc = "Texture", c.widgets_info = {name:{widget:"texture"}, filter:{widget:"checkbox"}}, c.loadTextureCallback = null, c.image_preview_size = 256, c.PASS_THROUGH = 1, c.COPY = 2, c.LOW = 3, c.HIGH = 4, c.REUSE = 5, c.DEFAULT = 2, c.MODE_VALUES = {"pass through":c.PASS_THROUGH, copy:c.COPY, low:c.LOW, high:c.HIGH, reuse:c.REUSE, default:c.DEFAULT}, c.getTexturesContainer = function() { + return gl.textures; + }, c.loadTexture = function(a, b) { + b = b || {}; + var d = a; + "http://" == d.substr(0, 7) && G.proxy && (d = G.proxy + d.substr(7)); + return c.getTexturesContainer()[a] = GL.Texture.fromURL(d, b); + }, c.getTexture = function(a) { + var b = this.getTexturesContainer(); + if (!b) { + throw "Cannot load texture, container of textures not found"; + } + b = b[a]; + return !b && a && ":" != a[0] ? this.loadTexture(a) : b; + }, c.getTargetTexture = function(a, b, d) { + if (!a) { + throw "LGraphTexture.getTargetTexture expects a reference texture"; + } + switch(d) { + case c.LOW: + d = gl.UNSIGNED_BYTE; + break; + case c.HIGH: + d = gl.HIGH_PRECISION_FORMAT; + break; + case c.REUSE: + return a; + default: + d = a ? a.type : gl.UNSIGNED_BYTE; + } + b && b.width == a.width && b.height == a.height && b.type == d || (b = new GL.Texture(a.width, a.height, {type:d, format:gl.RGBA, filter:gl.LINEAR})); + return b; + }, c.getTextureType = function(a, b) { + b = b ? b.type : gl.UNSIGNED_BYTE; + switch(a) { + case c.HIGH: + b = gl.HIGH_PRECISION_FORMAT; + break; + case c.LOW: + b = gl.UNSIGNED_BYTE; + } + return b; + }, c.getWhiteTexture = function() { + return this._white_texture ? this._white_texture : this._white_texture = GL.Texture.fromMemory(1, 1, [255, 255, 255, 255], {format:gl.RGBA, wrap:gl.REPEAT, filter:gl.NEAREST}); + }, c.getNoiseTexture = function() { + if (this._noise_texture) { + return this._noise_texture; + } + for (var a = new Uint8Array(1048576), b = 0; 1048576 > b; ++b) { + a[b] = 255 * Math.random(); + } + return this._noise_texture = a = GL.Texture.fromMemory(512, 512, a, {format:gl.RGBA, wrap:gl.REPEAT, filter:gl.NEAREST}); + }, c.prototype.onDropFile = function(a, b, d) { + a ? ("string" == typeof a ? a = GL.Texture.fromURL(a) : -1 != b.toLowerCase().indexOf(".dds") ? a = GL.Texture.fromDDSInMemory(a) : (a = new Blob([d]), a = URL.createObjectURL(a), a = GL.Texture.fromURL(a)), this._drop_texture = a, this.properties.name = b) : (this._drop_texture = null, this.properties.name = ""); + }, c.prototype.getExtraMenuOptions = function(a) { + var b = this; + if (this._drop_texture) { + return [{content:"Clear", callback:function() { + b._drop_texture = null; + b.properties.name = ""; + }}]; + } + }, c.prototype.onExecute = function() { + var a = null; + this.isOutputConnected(1) && (a = this.getInputData(0)); + !a && this._drop_texture && (a = this._drop_texture); + !a && this.properties.name && (a = c.getTexture(this.properties.name)); + if (a) { + this._last_tex = a; + !1 === this.properties.filter ? a.setParameter(gl.TEXTURE_MAG_FILTER, gl.NEAREST) : a.setParameter(gl.TEXTURE_MAG_FILTER, gl.LINEAR); + this.setOutputData(0, a); + this.setOutputData(1, a.fullpath || a.filename); + for (var b = 2; b < this.outputs.length; b++) { + var d = this.outputs[b]; + if (d) { + var k = null; + "width" == d.name ? k = a.width : "height" == d.name ? k = a.height : "aspect" == d.name && (k = a.width / a.height); + this.setOutputData(b, k); + } + } + } else { + this.setOutputData(0, null), this.setOutputData(1, ""); + } + }, c.prototype.onResourceRenamed = function(a, b) { + this.properties.name == a && (this.properties.name = b); + }, c.prototype.onDrawBackground = function(a) { + if (!(this.flags.collapsed || 20 >= this.size[1])) { + if (this._drop_texture && a.webgl) { + a.drawImage(this._drop_texture, 0, 0, this.size[0], this.size[1]); + } else { + if (this._last_preview_tex != this._last_tex) { + if (a.webgl) { + this._canvas = this._last_tex; + } else { + var b = c.generateLowResTexturePreview(this._last_tex); + if (!b) { + return; + } + this._last_preview_tex = this._last_tex; + this._canvas = cloneCanvas(b); + } + } + this._canvas && (a.save(), a.webgl || (a.translate(0, this.size[1]), a.scale(1, -1)), a.drawImage(this._canvas, 0, 0, this.size[0], this.size[1]), a.restore()); + } + } + }, c.generateLowResTexturePreview = function(a) { + if (!a) { + return null; + } + var b = c.image_preview_size, d = a; + if (a.format == gl.DEPTH_COMPONENT) { + return null; + } + if (a.width > b || a.height > b) { + d = this._preview_temp_tex, this._preview_temp_tex || (this._preview_temp_tex = d = new GL.Texture(b, b, {minFilter:gl.NEAREST})), a.copyTo(d); + } + a = this._preview_canvas; + a || (this._preview_canvas = a = createCanvas(b, b)); + d && d.toCanvas(a); + return a; + }, c.prototype.getResources = function(a) { + this.properties.name && (a[this.properties.name] = GL.Texture); + return a; + }, c.prototype.onGetInputs = function() { + return [["in", "Texture"]]; + }, c.prototype.onGetOutputs = function() { + return [["width", "number"], ["height", "number"], ["aspect", "number"]]; + }, c.replaceCode = function(a, b) { + return a.replace(/\{\{[a-zA-Z0-9_]*\}\}/g, function(a) { + a = a.replace(/[\{\}]/g, ""); + return b[a] || ""; + }); + }, G.registerNodeType("texture/texture", c), r.title = "Preview", r.desc = "Show a texture in the graph canvas", r.allow_preview = !1, r.prototype.onDrawBackground = function(a) { + if (!this.flags.collapsed && (a.webgl || r.allow_preview)) { + var b = this.getInputData(0); + b && (b = !b.handle && a.webgl ? b : c.generateLowResTexturePreview(b), a.save(), this.properties.flipY && (a.translate(0, this.size[1]), a.scale(1, -1)), a.drawImage(b, 0, 0, this.size[0], this.size[1]), a.restore()); + } + }, G.registerNodeType("texture/preview", r), m.title = "Save", m.desc = "Save a texture in the repository", m.prototype.getPreviewTexture = function() { + return this._texture; + }, m.prototype.onExecute = function() { + var a = this.getInputData(0); + a && (this.properties.generate_mipmaps && (a.bind(0), a.setParameter(gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR), gl.generateMipmap(a.texture_type), a.unbind(0)), this.properties.name && (c.storeTexture ? c.storeTexture(this.properties.name, a) : c.getTexturesContainer()[this.properties.name] = a), this._texture = a, this.setOutputData(0, a), this.setOutputData(1, this.properties.name)); + }, G.registerNodeType("texture/save", m), h.widgets_info = {uvcode:{widget:"code"}, pixelcode:{widget:"code"}, precision:{widget:"combo", values:c.MODE_VALUES}}, h.title = "Operation", h.desc = "Texture shader operation", h.presets = {}, h.prototype.getExtraMenuOptions = function(a) { + var b = this; + return [{content:b.properties.show ? "Hide Texture" : "Show Texture", callback:function() { + b.properties.show = !b.properties.show; + }}]; + }, h.prototype.onPropertyChanged = function() { + this.has_error = !1; + }, h.prototype.onDrawBackground = function(a) { + this.flags.collapsed || 20 >= this.size[1] || !this.properties.show || !this._tex || this._tex.gl != a || (a.save(), a.drawImage(this._tex, 0, 0, this.size[0], this.size[1]), a.restore()); + }, h.prototype.onExecute = function() { + var a = this.getInputData(0); + if (this.isOutputConnected(0)) { + if (this.properties.precision === c.PASS_THROUGH) { + this.setOutputData(0, a); + } else { + var b = this.getInputData(1); + if (this.properties.uvcode || this.properties.pixelcode) { + var d = 512, k = 512; + a ? (d = a.width, k = a.height) : b && (d = b.width, k = b.height); + b || (b = GL.Texture.getWhiteTexture()); + var f = c.getTextureType(this.properties.precision, a); + this._tex = a || this._tex ? c.getTargetTexture(a || this._tex, this._tex, this.properties.precision) : new GL.Texture(d, k, {type:f, format:gl.RGBA, filter:gl.LINEAR}); + f = ""; + this.properties.uvcode && (f = "uv = " + this.properties.uvcode, -1 != this.properties.uvcode.indexOf(";") && (f = this.properties.uvcode)); + var g = ""; + this.properties.pixelcode && (g = "result = " + this.properties.pixelcode, -1 != this.properties.pixelcode.indexOf(";") && (g = this.properties.pixelcode)); + var e = this._shader; + if (!(this.has_error || e && this._shader_code == f + "|" + g)) { + var n = c.replaceCode(h.pixel_shader, {UV_CODE:f, PIXEL_CODE:g}); + try { + e = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, n), this.boxcolor = "#00FF00"; + } catch (X) { + GL.Shader.dumpErrorToConsole(X, Shader.SCREEN_VERTEX_SHADER, n); + this.boxcolor = "#FF0000"; + this.has_error = !0; + return; + } + this._shader = e; + this._shader_code = f + "|" + g; + } + if (this._shader) { + var p = this.getInputData(2); + null != p ? this.properties.value = p : p = parseFloat(this.properties.value); + var l = this.graph.getTime(); + this._tex.drawTo(function() { + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + gl.disable(gl.BLEND); + a && a.bind(0); + b && b.bind(1); + var c = Mesh.getScreenQuad(); + e.uniforms({u_texture:0, u_textureB:1, value:p, texSize:[d, k], time:l}).draw(c); + }); + this.setOutputData(0, this._tex); + } + } + } + } + }, h.pixel_shader = "precision highp float;\n\r\n\t\t\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform vec2 texSize;\n\r\n\t\tuniform float time;\n\r\n\t\tuniform float value;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec2 uv = v_coord;\n\r\n\t\t\t{{UV_CODE}};\n\r\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\r\n\t\t\tvec3 color = color4.rgb;\n\r\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\r\n\t\t\tvec3 colorB = color4B.rgb;\n\r\n\t\t\tvec3 result = color;\n\r\n\t\t\tfloat alpha = 1.0;\n\r\n\t\t\t{{PIXEL_CODE}};\n\r\n\t\t\tgl_FragColor = vec4(result, alpha);\n\r\n\t\t}\n\r\n\t\t", + h.registerPreset = function(a, b) { + h.presets[a] = b; + }, h.registerPreset("", ""), h.registerPreset("bypass", "color"), h.registerPreset("add", "color + colorB * value"), h.registerPreset("substract", "(color - colorB) * value"), h.registerPreset("mate", "mix( color, colorB, color4B.a * value)"), h.registerPreset("invert", "vec3(1.0) - color"), h.registerPreset("multiply", "color * colorB * value"), h.registerPreset("divide", "(color / colorB) / value"), h.registerPreset("difference", "abs(color - colorB) * value"), h.registerPreset("max", "max(color, colorB) * value"), + h.registerPreset("min", "min(color, colorB) * value"), h.registerPreset("displace", "texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"), h.registerPreset("grayscale", "vec3(color.x + color.y + color.z) * value / 3.0"), h.registerPreset("saturation", "mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"), h.registerPreset("threshold", "vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"), h.prototype.onInspect = + function(a) { + var b = this; + a.addCombo("Presets", "", {values:Object.keys(h.presets), callback:function(d) { + var c = h.presets[d]; + c && (b.setProperty("pixelcode", c), b.title = d, a.refresh()); + }}); + }, G.registerNodeType("texture/operation", h), q.title = "Shader", q.desc = "Texture shader", q.widgets_info = {code:{type:"code", lang:"glsl"}, precision:{widget:"combo", values:c.MODE_VALUES}}, q.prototype.onPropertyChanged = function(a, b) { + if ("code" == a && (a = this.getShader())) { + b = a.uniformInfo; + if (this.inputs) { + for (var d = {}, c = 0; c < this.inputs.length; ++c) { + var k = this.getInputInfo(c); + k && (b[k.name] && !d[k.name] ? d[k.name] = !0 : (this.removeInput(c), c--)); + } + } + for (c in b) { + if (k = a.uniformInfo[c], null !== k.loc && "time" != c) { + if (this._shader.samplers[c]) { + b = "texture"; + } else { + switch(k.size) { + case 1: + b = "number"; + break; + case 2: + b = "vec2"; + break; + case 3: + b = "vec3"; + break; + case 4: + b = "vec4"; + break; + case 9: + b = "mat3"; + break; + case 16: + b = "mat4"; + break; + default: + continue; + } + } + d = this.findInputSlot(c); + if (-1 != d && (k = this.getInputInfo(d))) { + if (k.type == b) { + continue; + } + this.removeInput(d, b); + } + this.addInput(c, b); + } + } + } + }, q.prototype.getShader = function() { + if (this._shader && this._shader_code == this.properties.code) { + return this._shader; + } + this._shader_code = this.properties.code; + this._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, this.properties.code), this.boxcolor = "green"; + return this._shader; + }, q.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var a = this.getShader(); + if (a) { + var b = 0, d = null; + if (this.inputs) { + for (var k = 0; k < this.inputs.length; ++k) { + var f = this.getInputInfo(k), g = this.getInputData(k); + null != g && (g.constructor === GL.Texture && (g.bind(b), d || (d = g), g = b, b++), a.setUniform(f.name, g)); + } + } + var e = this._uniforms; + b = c.getTextureType(this.properties.precision, d); + k = this.properties.width | 0; + f = this.properties.height | 0; + 0 == k && (k = d ? d.width : gl.canvas.width); + 0 == f && (f = d ? d.height : gl.canvas.height); + e.texSize[0] = k; + e.texSize[1] = f; + e.time = this.graph.getTime(); + e.u_value = this.properties.u_value; + e.u_color.set(this.properties.u_color); + this._tex && this._tex.type == b && this._tex.width == k && this._tex.height == f || (this._tex = new GL.Texture(k, f, {type:b, format:gl.RGBA, filter:gl.LINEAR})); + this._tex.drawTo(function() { + a.uniforms(e).draw(GL.Mesh.getScreenQuad()); + }); + this.setOutputData(0, this._tex); + } + } + }, q.pixel_shader = "precision highp float;\n\r\n\n\r\nvarying vec2 v_coord;\n\r\nuniform float time; //time in seconds\n\r\nuniform vec2 texSize; //tex resolution\n\r\nuniform float u_value;\n\r\nuniform vec4 u_color;\n\n\r\nvoid main() {\n\r\n\tvec2 uv = v_coord;\n\r\n\tvec3 color = vec3(0.0);\n\r\n\t//your code here\n\r\n\tcolor.xy=uv;\n\n\r\n\tgl_FragColor = vec4(color, 1.0);\n\r\n}\n\r\n", G.registerNodeType("texture/shader", q), l.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, + l.title = "Scale/Offset", l.desc = "Applies an scaling and offseting", l.prototype.onExecute = function() { + var a = this.getInputData(0); + if (this.isOutputConnected(0) && a) { + if (this.properties.precision === c.PASS_THROUGH) { + this.setOutputData(0, a); + } else { + var b = a.width, d = a.height, k = this.precision === c.LOW ? gl.UNSIGNED_BYTE : gl.HIGH_PRECISION_FORMAT; + this.precision === c.DEFAULT && (k = a.type); + this._tex && this._tex.width == b && this._tex.height == d && this._tex.type == k || (this._tex = new GL.Texture(b, d, {type:k, format:gl.RGBA, filter:gl.LINEAR})); + var f = this._shader; + f || (f = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, l.pixel_shader)); + var g = this.getInputData(1); + g ? (this.properties.scale[0] = g[0], this.properties.scale[1] = g[1]) : g = this.properties.scale; + var e = this.getInputData(2); + e ? (this.properties.offset[0] = e[0], this.properties.offset[1] = e[1]) : e = this.properties.offset; + this._tex.drawTo(function() { + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + gl.disable(gl.BLEND); + a.bind(0); + var b = Mesh.getScreenQuad(); + f.uniforms({u_texture:0, u_scale:g, u_offset:e}).draw(b); + }); + this.setOutputData(0, this._tex); + } + } + }, l.pixel_shader = "precision highp float;\n\r\n\t\t\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform vec2 u_scale;\n\r\n\t\tuniform vec2 u_offset;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec2 uv = v_coord;\n\r\n\t\t\tuv = uv / u_scale - u_offset;\n\r\n\t\t\tgl_FragColor = texture2D(u_texture, uv);\n\r\n\t\t}\n\r\n\t\t", G.registerNodeType("texture/scaleOffset", l), A.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, + A.title = "Warp", A.desc = "Texture warp operation", A.prototype.onExecute = function() { + var a = this.getInputData(0); + if (this.isOutputConnected(0)) { + if (this.properties.precision === c.PASS_THROUGH) { + this.setOutputData(0, a); + } else { + var b = this.getInputData(1), d = 512, k = 512; + a ? (d = a.width, k = a.height) : b && (d = b.width, k = b.height); + this._tex = a || this._tex ? c.getTargetTexture(a || this._tex, this._tex, this.properties.precision) : new GL.Texture(d, k, {type:this.precision === c.LOW ? gl.UNSIGNED_BYTE : gl.HIGH_PRECISION_FORMAT, format:gl.RGBA, filter:gl.LINEAR}); + var f = this._shader; + f || (f = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, A.pixel_shader)); + d = this.getInputData(2); + null != d ? this.properties.factor = d : d = parseFloat(this.properties.factor); + var g = this._uniforms; + g.u_factor = d; + g.u_scale.set(this.properties.scale); + g.u_offset.set(this.properties.offset); + this._tex.drawTo(function() { + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + gl.disable(gl.BLEND); + a && a.bind(0); + b && b.bind(1); + var d = Mesh.getScreenQuad(); + f.uniforms(g).draw(d); + }); + this.setOutputData(0, this._tex); + } + } + }, A.pixel_shader = "precision highp float;\n\r\n\t\t\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform float u_factor;\n\r\n\t\tuniform vec2 u_scale;\n\r\n\t\tuniform vec2 u_offset;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec2 uv = v_coord;\n\r\n\t\t\tuv += ( texture2D(u_textureB, uv).rg - vec2(0.5)) * u_factor * u_scale + u_offset;\n\r\n\t\t\tgl_FragColor = texture2D(u_texture, uv);\n\r\n\t\t}\n\r\n\t\t", G.registerNodeType("texture/warp", + A), y.title = "to Viewport", y.desc = "Texture to viewport", y._prev_viewport = new Float32Array(4), y.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a) { + this.properties.disable_alpha ? gl.disable(gl.BLEND) : (gl.enable(gl.BLEND), this.properties.additive ? gl.blendFunc(gl.SRC_ALPHA, gl.ONE) : gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)); + gl.disable(gl.DEPTH_TEST); + var b = this.properties.gamma || 1.0; + this.isInputConnected(1) && (b = this.getInputData(1)); + a.setParameter(gl.TEXTURE_MAG_FILTER, this.properties.filter ? gl.LINEAR : gl.NEAREST); + var d = y._prev_viewport; + d.set(gl.viewport_data); + var c = this.properties.viewport; + gl.viewport(d[0] + d[2] * c[0], d[1] + d[3] * c[1], d[2] * c[2], d[3] * c[3]); + gl.getViewport(); + this.properties.antialiasing ? (y._shader || (y._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, y.aa_pixel_shader)), c = Mesh.getScreenQuad(), a.bind(0), y._shader.uniforms({u_texture:0, uViewportSize:[a.width, a.height], u_igamma:1 / b, inverseVP:[1 / a.width, 1 / a.height]}).draw(c)) : 1.0 != b ? (y._gamma_shader || (y._gamma_shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, y.gamma_pixel_shader)), a.toViewport(y._gamma_shader, {u_texture:0, u_igamma:1 / b})) : a.toViewport(); + gl.viewport(d[0], d[1], d[2], d[3]); + } + }, y.prototype.onGetInputs = function() { + return [["gamma", "number"]]; + }, y.aa_pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec2 uViewportSize;\n\r\n\t\tuniform vec2 inverseVP;\n\r\n\t\tuniform float u_igamma;\n\r\n\t\t#define FXAA_REDUCE_MIN (1.0/ 128.0)\n\r\n\t\t#define FXAA_REDUCE_MUL (1.0 / 8.0)\n\r\n\t\t#define FXAA_SPAN_MAX 8.0\n\r\n\t\t\n\r\n\t\t/* from mitsuhiko/webgl-meincraft based on the code on geeks3d.com */\n\r\n\t\tvec4 applyFXAA(sampler2D tex, vec2 fragCoord)\n\r\n\t\t{\n\r\n\t\t\tvec4 color = vec4(0.0);\n\r\n\t\t\t/*vec2 inverseVP = vec2(1.0 / uViewportSize.x, 1.0 / uViewportSize.y);*/\n\r\n\t\t\tvec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * inverseVP).xyz;\n\r\n\t\t\tvec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * inverseVP).xyz;\n\r\n\t\t\tvec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * inverseVP).xyz;\n\r\n\t\t\tvec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * inverseVP).xyz;\n\r\n\t\t\tvec3 rgbM = texture2D(tex, fragCoord * inverseVP).xyz;\n\r\n\t\t\tvec3 luma = vec3(0.299, 0.587, 0.114);\n\r\n\t\t\tfloat lumaNW = dot(rgbNW, luma);\n\r\n\t\t\tfloat lumaNE = dot(rgbNE, luma);\n\r\n\t\t\tfloat lumaSW = dot(rgbSW, luma);\n\r\n\t\t\tfloat lumaSE = dot(rgbSE, luma);\n\r\n\t\t\tfloat lumaM = dot(rgbM, luma);\n\r\n\t\t\tfloat lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n\r\n\t\t\tfloat lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\r\n\t\t\t\n\r\n\t\t\tvec2 dir;\n\r\n\t\t\tdir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n\r\n\t\t\tdir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\r\n\t\t\t\n\r\n\t\t\tfloat dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\r\n\t\t\t\n\r\n\t\t\tfloat rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n\r\n\t\t\tdir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * inverseVP;\n\r\n\t\t\t\n\r\n\t\t\tvec3 rgbA = 0.5 * (texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + \n\r\n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz);\n\r\n\t\t\tvec3 rgbB = rgbA * 0.5 + 0.25 * (texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + \n\r\n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz);\n\r\n\t\t\t\n\r\n\t\t\t//return vec4(rgbA,1.0);\n\r\n\t\t\tfloat lumaB = dot(rgbB, luma);\n\r\n\t\t\tif ((lumaB < lumaMin) || (lumaB > lumaMax))\n\r\n\t\t\t\tcolor = vec4(rgbA, 1.0);\n\r\n\t\t\telse\n\r\n\t\t\t\tcolor = vec4(rgbB, 1.0);\n\r\n\t\t\tif(u_igamma != 1.0)\n\r\n\t\t\t\tcolor.xyz = pow( color.xyz, vec3(u_igamma) );\n\r\n\t\t\treturn color;\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\r\n\t\t}\n\r\n\t\t", + y.gamma_pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_igamma;\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\r\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\r\n\t\t gl_FragColor = color;\n\r\n\t\t}\n\r\n\t\t", G.registerNodeType("texture/toviewport", y), x.title = "Copy", x.desc = "Copy Texture", x.widgets_info = {size:{widget:"combo", + values:[0, 32, 64, 128, 256, 512, 1024, 2048]}, precision:{widget:"combo", values:c.MODE_VALUES}}, x.prototype.onExecute = function() { + var a = this.getInputData(0); + if ((a || this._temp_texture) && this.isOutputConnected(0)) { + if (a) { + var b = a.width, d = a.height; + 0 != this.properties.size && (d = b = this.properties.size); + var k = this._temp_texture, f = a.type; + this.properties.precision === c.LOW ? f = gl.UNSIGNED_BYTE : this.properties.precision === c.HIGH && (f = gl.HIGH_PRECISION_FORMAT); + k && k.width == b && k.height == d && k.type == f || (k = gl.LINEAR, this.properties.generate_mipmaps && isPowerOfTwo(b) && isPowerOfTwo(d) && (k = gl.LINEAR_MIPMAP_LINEAR), this._temp_texture = new GL.Texture(b, d, {type:f, format:gl.RGBA, minFilter:k, magFilter:gl.LINEAR})); + a.copyTo(this._temp_texture); + this.properties.generate_mipmaps && (this._temp_texture.bind(0), gl.generateMipmap(this._temp_texture.texture_type), this._temp_texture.unbind(0)); + } + this.setOutputData(0, this._temp_texture); + } + }, G.registerNodeType("texture/copy", x), F.title = "Downsample", F.desc = "Downsample Texture", F.widgets_info = {iterations:{type:"number", step:1, precision:0, min:0}, precision:{widget:"combo", values:c.MODE_VALUES}}, F.prototype.onExecute = function() { + var a = this.getInputData(0); + if ((a || this._temp_texture) && this.isOutputConnected(0) && a && a.texture_type === GL.TEXTURE_2D) { + if (1 > this.properties.iterations) { + this.setOutputData(0, a); + } else { + var b = F._shader; + b || (F._shader = b = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, F.pixel_shader)); + var d = a.width | 0, k = a.height | 0, f = a.type; + this.properties.precision === c.LOW ? f = gl.UNSIGNED_BYTE : this.properties.precision === c.HIGH && (f = gl.HIGH_PRECISION_FORMAT); + var g = this.properties.iterations || 1, e = a, n = []; + f = {type:f, format:a.format}; + var p = vec2.create(), h = {u_offset:p}; + this._texture && GL.Texture.releaseTemporary(this._texture); + for (var l = 0; l < g; ++l) { + p[0] = 1 / d; + p[1] = 1 / k; + d = d >> 1 || 0; + k = k >> 1 || 0; + a = GL.Texture.getTemporary(d, k, f); + n.push(a); + e.setParameter(GL.TEXTURE_MAG_FILTER, GL.NEAREST); + e.copyTo(a, b, h); + if (1 == d && 1 == k) { + break; + } + e = a; + } + this._texture = n.pop(); + for (l = 0; l < n.length; ++l) { + GL.Texture.releaseTemporary(n[l]); + } + this.properties.generate_mipmaps && (this._texture.bind(0), gl.generateMipmap(this._texture.texture_type), this._texture.unbind(0)); + this.setOutputData(0, this._texture); + } + } + }, F.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec2 u_offset;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = texture2D(u_texture, v_coord );\n\r\n\t\t\tcolor += texture2D(u_texture, v_coord + vec2( u_offset.x, 0.0 ) );\n\r\n\t\t\tcolor += texture2D(u_texture, v_coord + vec2( 0.0, u_offset.y ) );\n\r\n\t\t\tcolor += texture2D(u_texture, v_coord + vec2( u_offset.x, u_offset.y ) );\n\r\n\t\t gl_FragColor = color * 0.25;\n\r\n\t\t}\n\r\n\t\t", + G.registerNodeType("texture/downsample", F), z.title = "Average", z.desc = "Compute a partial average (32 random samples) of a texture and stores it as a 1x1 pixel texture.\n If high_quality is true, then it generates the mipmaps first and reads from the lower one.", z.prototype.onExecute = function() { + this.properties.use_previous_frame || this.updateAverage(); + var a = this._luminance; + this.setOutputData(0, this._temp_texture); + this.setOutputData(1, a); + this.setOutputData(2, (a[0] + a[1] + a[2]) / 3); + }, z.prototype.onPreRenderExecute = function() { + this.updateAverage(); + }, z.prototype.updateAverage = function() { + var a = this.getInputData(0); + if (a && (this.isOutputConnected(0) || this.isOutputConnected(1) || this.isOutputConnected(2))) { + if (!z._shader) { + z._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, z.pixel_shader); + for (var b = new Float32Array(16), d = 0; d < b.length; ++d) { + b[d] = Math.random(); + } + z._shader.uniforms({u_samples_a:b.subarray(0, 16), u_samples_b:b.subarray(16, 32)}); + } + d = this._temp_texture; + b = gl.UNSIGNED_BYTE; + a.type != b && (b = gl.FLOAT); + d && d.type == b || (this._temp_texture = new GL.Texture(1, 1, {type:b, format:gl.RGBA, filter:gl.NEAREST})); + this._uniforms.u_mipmap_offset = 0; + this.properties.high_quality && (this._temp_pot2_texture && this._temp_pot2_texture.type == b || (this._temp_pot2_texture = new GL.Texture(512, 512, {type:b, format:gl.RGBA, minFilter:gl.LINEAR_MIPMAP_LINEAR, magFilter:gl.LINEAR})), a.copyTo(this._temp_pot2_texture), a = this._temp_pot2_texture, a.bind(0), gl.generateMipmap(GL.TEXTURE_2D), this._uniforms.u_mipmap_offset = 9); + var c = z._shader, k = this._uniforms; + k.u_mipmap_offset = this.properties.mipmap_offset; + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.BLEND); + this._temp_texture.drawTo(function() { + a.toViewport(c, k); + }); + if (this.isOutputConnected(1) || this.isOutputConnected(2)) { + if (d = this._temp_texture.getPixels()) { + var f = this._luminance; + b = this._temp_texture.type; + f.set(d); + b == gl.UNSIGNED_BYTE && vec4.scale(f, f, 1 / 255); + } + } + } + }, z.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tuniform mat4 u_samples_a;\n\r\n\t\tuniform mat4 u_samples_b;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_mipmap_offset;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = vec4(0.0);\n\r\n\t\t\t//random average\n\r\n\t\t\tfor(int i = 0; i < 4; ++i)\n\r\n\t\t\t\tfor(int j = 0; j < 4; ++j)\n\r\n\t\t\t\t{\n\r\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\r\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\r\n\t\t\t\t}\n\r\n\t\t gl_FragColor = color * 0.03125;\n\r\n\t\t}\n\r\n\t\t", + G.registerNodeType("texture/average", z), e.title = "Smooth", e.desc = "Smooth texture over time", e.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a && this.isOutputConnected(0)) { + e._shader || (e._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, e.pixel_shader)); + var b = this._temp_texture; + b && b.type == a.type && b.width == a.width && b.height == a.height || (b = {type:a.type, format:gl.RGBA, filter:gl.NEAREST}, this._temp_texture = new GL.Texture(a.width, a.height, b), this._temp_texture2 = new GL.Texture(a.width, a.height, b), a.copyTo(this._temp_texture2)); + b = this._temp_texture; + var d = this._temp_texture2, c = e._shader, k = this._uniforms; + k.u_factor = 1.0 - this.getInputOrProperty("factor"); + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + b.drawTo(function() { + d.bind(1); + a.toViewport(c, k); + }); + this.setOutputData(0, b); + this._temp_texture = d; + this._temp_texture2 = b; + } + }, e.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tuniform float u_factor;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tgl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\r\n\t\t}\n\r\n\t\t", G.registerNodeType("texture/temporal_smooth", e), B.title = "Lineal Avg Smooth", B.desc = "Smooth texture linearly over time", + B["@samples"] = {type:"number", min:1, max:64, step:1, precision:1}, B.prototype.getPreviewTexture = function() { + return this._temp_texture2; + }, B.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a && this.isOutputConnected(0)) { + B._shader || (B._shader_copy = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, B.pixel_shader_copy), B._shader_avg = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, B.pixel_shader_avg)); + var b = Math.clamp(this.properties.samples, 0, 64), d = this.frame, c = this.properties.frames_interval; + if (0 == c || 0 == d % c) { + d = this._temp_texture; + d && d.type == a.type && d.width == b || (d = {type:a.type, format:gl.RGBA, filter:gl.NEAREST}, this._temp_texture = new GL.Texture(b, 1, d), this._temp_texture2 = new GL.Texture(b, 1, d), this._temp_texture_out = new GL.Texture(1, 1, d)); + var k = this._temp_texture, f = this._temp_texture2, g = B._shader_copy, e = B._shader_avg, n = this._uniforms; + n.u_samples = b; + n.u_isamples = 1.0 / b; + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + k.drawTo(function() { + f.bind(1); + a.toViewport(g, n); + }); + this._temp_texture_out.drawTo(function() { + k.toViewport(e, n); + }); + this.setOutputData(0, this._temp_texture_out); + this._temp_texture = f; + this._temp_texture2 = k; + } else { + this.setOutputData(0, this._temp_texture_out); + } + this.setOutputData(1, this._temp_texture2); + this.frame++; + } + }, B.pixel_shader_copy = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tuniform float u_isamples;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tif( v_coord.x <= u_isamples )\n\r\n\t\t\t\tgl_FragColor = texture2D( u_texture, vec2(0.5) );\n\r\n\t\t\telse\n\r\n\t\t\t\tgl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\r\n\t\t}\n\r\n\t\t", B.pixel_shader_avg = + "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform int u_samples;\n\r\n\t\tuniform float u_isamples;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = vec4(0.0);\n\r\n\t\t\tfor(int i = 0; i < 64; ++i)\n\r\n\t\t\t{\n\r\n\t\t\t\tcolor += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\r\n\t\t\t\tif(i == (u_samples - 1))\n\r\n\t\t\t\t\tbreak;\n\r\n\t\t\t}\n\r\n\t\t\tgl_FragColor = color * u_isamples;\n\r\n\t\t}\n\r\n\t\t", + G.registerNodeType("texture/linear_avg_smooth", B), E.title = "Image to Texture", E.desc = "Uploads an image to the GPU", E.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a) { + var b = a.videoWidth || a.width, d = a.videoHeight || a.height; + if (a.gltexture) { + this.setOutputData(0, a.gltexture); + } else { + var c = this._temp_texture; + c && c.width == b && c.height == d || (this._temp_texture = new GL.Texture(b, d, {format:gl.RGBA, filter:gl.LINEAR})); + try { + this._temp_texture.uploadImage(a); + } catch (U) { + console.error("image comes from an unsafe location, cannot be uploaded to webgl: " + U); + return; + } + this.setOutputData(0, this._temp_texture); + } + } + }, G.registerNodeType("texture/imageToTexture", E), u.widgets_info = {texture:{widget:"texture"}, precision:{widget:"combo", values:c.MODE_VALUES}}, u.title = "LUT", u.desc = "Apply LUT to Texture", u.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var a = this.getInputData(0); + if (this.properties.precision === c.PASS_THROUGH || !1 === this.properties.enabled) { + this.setOutputData(0, a); + } else { + if (a) { + var b = this.getInputData(1); + b || (b = c.getTexture(this.properties.texture)); + if (b) { + b.bind(0); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.bindTexture(gl.TEXTURE_2D, null); + var d = this.properties.intensity; + this.isInputConnected(2) && (this.properties.intensity = d = this.getInputData(2)); + this._tex = c.getTargetTexture(a, this._tex, this.properties.precision); + this._tex.drawTo(function() { + b.bind(1); + a.toViewport(u._shader, {u_texture:0, u_textureB:1, u_amount:d}); + }); + this.setOutputData(0, this._tex); + } else { + this.setOutputData(0, a); + } + } + } + } + }, u.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tuniform float u_amount;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\r\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\r\n\t\t\t mediump vec2 quad1;\n\r\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\r\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\r\n\t\t\t mediump vec2 quad2;\n\r\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\r\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\r\n\t\t\t highp vec2 texPos1;\n\r\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\r\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\r\n\t\t\t highp vec2 texPos2;\n\r\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\r\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\r\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\r\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\r\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\r\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\r\n\t\t}\n\r\n\t\t", + G.registerNodeType("texture/LUT", u), H.widgets_info = {texture:{widget:"texture"}, precision:{widget:"combo", values:c.MODE_VALUES}}, H.title = "Encode", H.desc = "Apply a texture atlas to encode a texture", H.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var a = this.getInputData(0); + if (this.properties.precision === c.PASS_THROUGH || !1 === this.properties.enabled) { + this.setOutputData(0, a); + } else { + if (a) { + var b = this.getInputData(1); + b || (b = c.getTexture(this.properties.texture)); + if (b) { + b.bind(0); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, this.properties.filter ? gl.LINEAR : gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, this.properties.filter ? gl.LINEAR : gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.bindTexture(gl.TEXTURE_2D, null); + var d = this._uniforms; + d.u_row_simbols = Math.floor(this.properties.num_row_symbols); + d.u_symbol_size = this.properties.symbol_size; + d.u_brightness = this.properties.brightness; + d.u_invert = this.properties.invert ? 1 : 0; + d.u_colorize = this.properties.colorize ? 1 : 0; + this._tex = c.getTargetTexture(a, this._tex, this.properties.precision); + d.u_res[0] = this._tex.width; + d.u_res[1] = this._tex.height; + this._tex.bind(0); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + this._tex.drawTo(function() { + b.bind(1); + a.toViewport(H._shader, d); + }); + this.setOutputData(0, this._tex); + } else { + this.setOutputData(0, a); + } + } + } + } + }, H.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tuniform float u_row_simbols;\n\r\n\t\tuniform float u_symbol_size;\n\r\n\t\tuniform float u_brightness;\n\r\n\t\tuniform float u_invert;\n\r\n\t\tuniform float u_colorize;\n\r\n\t\tuniform vec2 u_res;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec2 total_symbols = u_res / u_symbol_size;\n\r\n\t\t\tvec2 uv = floor(v_coord * total_symbols) / total_symbols; //pixelate \n\r\n\t\t\tvec2 local_uv = mod(v_coord * u_res, u_symbol_size) / u_symbol_size;\n\r\n\t\t\tlowp vec4 textureColor = texture2D(u_texture, uv );\n\r\n\t\t\tfloat lum = clamp(u_brightness * (textureColor.x + textureColor.y + textureColor.z)/3.0,0.0,1.0);\n\r\n\t\t\tif( u_invert == 1.0 ) lum = 1.0 - lum;\n\r\n\t\t\tfloat index = floor( lum * (u_row_simbols * u_row_simbols - 1.0));\n\r\n\t\t\tfloat col = mod( index, u_row_simbols );\n\r\n\t\t\tfloat row = u_row_simbols - floor( index / u_row_simbols ) - 1.0;\n\r\n\t\t\tvec2 simbol_uv = ( vec2( col, row ) + local_uv ) / u_row_simbols;\n\r\n\t\t\tvec4 color = texture2D( u_textureB, simbol_uv );\n\r\n\t\t\tif(u_colorize == 1.0)\n\r\n\t\t\t\tcolor *= textureColor;\n\r\n\t\t\tgl_FragColor = color;\n\r\n\t\t}\n\r\n\t\t", + G.registerNodeType("texture/encode", H), n.title = "Texture to Channels", n.desc = "Split texture channels", n.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a) { + this._channels || (this._channels = Array(4)); + for (var b = gl.RGB, d = 0, c = 0; 4 > c; c++) { + this.isOutputConnected(c) ? (this._channels[c] && this._channels[c].width == a.width && this._channels[c].height == a.height && this._channels[c].type == a.type && this._channels[c].format == b || (this._channels[c] = new GL.Texture(a.width, a.height, {type:a.type, format:b, filter:gl.LINEAR})), d++) : this._channels[c] = null; + } + if (d) { + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var k = Mesh.getScreenQuad(), f = n._shader, g = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]; + for (c = 0; 4 > c; c++) { + this._channels[c] && (this._channels[c].drawTo(function() { + a.bind(0); + f.uniforms({u_texture:0, u_mask:g[c]}).draw(k); + }), this.setOutputData(c, this._channels[c])); + } + } + } + }, n.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec4 u_mask;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\r\n\t\t}\n\r\n\t\t", G.registerNodeType("texture/textureChannels", n), p.title = "Channels to Texture", p.desc = "Split texture channels", p.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, + p.prototype.onExecute = function() { + var a = c.getWhiteTexture(), b = this.getInputData(0) || a, d = this.getInputData(1) || a, k = this.getInputData(2) || a, f = this.getInputData(3) || a; + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var g = Mesh.getScreenQuad(); + p._shader || (p._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, p.pixel_shader)); + var e = p._shader; + a = Math.max(b.width, d.width, k.width, f.width); + var n = Math.max(b.height, d.height, k.height, f.height), h = this.properties.precision == c.HIGH ? c.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE; + this._texture && this._texture.width == a && this._texture.height == n && this._texture.type == h || (this._texture = new GL.Texture(a, n, {type:h, format:gl.RGBA, filter:gl.LINEAR})); + a = this._color; + a[0] = this.properties.R; + a[1] = this.properties.G; + a[2] = this.properties.B; + a[3] = this.properties.A; + var l = this._uniforms; + this._texture.drawTo(function() { + b.bind(0); + d.bind(1); + k.bind(2); + f.bind(3); + e.uniforms(l).draw(g); + }); + this.setOutputData(0, this._texture); + }, p.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_textureR;\n\r\n\t\tuniform sampler2D u_textureG;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tuniform sampler2D u_textureA;\n\r\n\t\tuniform vec4 u_color;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t gl_FragColor = u_color * vec4( \r\n\t\t\t\t\ttexture2D(u_textureR, v_coord).r,\r\n\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\r\n\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\r\n\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\r\n\t\t}\n\r\n\t\t", + G.registerNodeType("texture/channelsTexture", p), k.title = "Color", k.desc = "Generates a 1x1 texture with a constant color", k.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, k.prototype.onDrawBackground = function(a) { + var b = this.properties.color; + a.fillStyle = "rgb(" + Math.floor(255 * Math.clamp(b[0], 0, 1)) + "," + Math.floor(255 * Math.clamp(b[1], 0, 1)) + "," + Math.floor(255 * Math.clamp(b[2], 0, 1)) + ")"; + this.flags.collapsed ? this.boxcolor = a.fillStyle : a.fillRect(0, 0, this.size[0], this.size[1]); + }, k.prototype.onExecute = function() { + var a = this.properties.precision == c.HIGH ? c.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE; + this._tex && this._tex.type == a || (this._tex = new GL.Texture(1, 1, {format:gl.RGBA, type:a, minFilter:gl.NEAREST})); + a = this.properties.color; + if (this.inputs) { + for (var b = 0; b < this.inputs.length; b++) { + var d = this.inputs[b], k = this.getInputData(b); + if (void 0 !== k) { + switch(d.name) { + case "RGB": + case "RGBA": + a.set(k); + break; + case "R": + a[0] = k; + break; + case "G": + a[1] = k; + break; + case "B": + a[2] = k; + break; + case "A": + a[3] = k; + } + } + } + } + 0.001 < vec4.sqrDist(this._tex_color, a) && (this._tex_color.set(a), this._tex.fill(a)); + this.setOutputData(0, this._tex); + }, k.prototype.onGetInputs = function() { + return [["RGB", "vec3"], ["RGBA", "vec4"], ["R", "number"], ["G", "number"], ["B", "number"], ["A", "number"]]; + }, G.registerNodeType("texture/color", k), a.title = "Gradient", a.desc = "Generates a gradient", a["@A"] = {type:"color"}, a["@B"] = {type:"color"}, a["@texture_size"] = {type:"enum", values:[32, 64, 128, 256, 512]}, a.prototype.onExecute = function() { + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var b = GL.Mesh.getScreenQuad(), d = a._shader, c = this.getInputData(0); + c || (c = this.properties.A); + var k = this.getInputData(1); + k || (k = this.properties.B); + for (var f = 2; f < this.inputs.length; f++) { + var g = this.inputs[f], e = this.getInputData(f); + void 0 !== e && (this.properties[g.name] = e); + } + var n = this._uniforms; + this._uniforms.u_angle = this.properties.angle * DEG2RAD; + this._uniforms.u_scale = this.properties.scale; + vec3.copy(n.u_colorA, c); + vec3.copy(n.u_colorB, k); + c = parseInt(this.properties.texture_size); + this._tex && this._tex.width == c || (this._tex = new GL.Texture(c, c, {format:gl.RGB, filter:gl.LINEAR})); + this._tex.drawTo(function() { + d.uniforms(n).draw(b); + }); + this.setOutputData(0, this._tex); + }, a.prototype.onGetInputs = function() { + return [["angle", "number"], ["scale", "number"]]; + }, a.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform float u_angle;\n\r\n\t\tuniform float u_scale;\n\r\n\t\tuniform vec3 u_colorA;\n\r\n\t\tuniform vec3 u_colorB;\n\r\n\t\t\n\r\n\t\tvec2 rotate(vec2 v, float angle)\n\r\n\t\t{\n\r\n\t\t\tvec2 result;\n\r\n\t\t\tfloat _cos = cos(angle);\n\r\n\t\t\tfloat _sin = sin(angle);\n\r\n\t\t\tresult.x = v.x * _cos - v.y * _sin;\n\r\n\t\t\tresult.y = v.x * _sin + v.y * _cos;\n\r\n\t\t\treturn result;\n\r\n\t\t}\n\r\n\t\tvoid main() {\n\r\n\t\t\tfloat f = (rotate(u_scale * (v_coord - vec2(0.5)), u_angle) + vec2(0.5)).x;\n\r\n\t\t\tvec3 color = mix(u_colorA,u_colorB,clamp(f,0.0,1.0));\n\r\n\t\t gl_FragColor = vec4(color,1.0);\n\r\n\t\t}\n\r\n\t\t", + G.registerNodeType("texture/gradient", a), b.title = "Mix", b.desc = "Generates a texture mixing two textures", b.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, b.prototype.onExecute = function() { + var a = this.getInputData(0); + if (this.isOutputConnected(0)) { + if (this.properties.precision === c.PASS_THROUGH) { + this.setOutputData(0, a); + } else { + var d = this.getInputData(1); + if (a && d) { + var k = this.getInputData(2), f = this.getInputData(3); + this._tex = c.getTargetTexture(this.properties.size_from_biggest && d.width > a.width ? d : a, this._tex, this.properties.precision); + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var g = Mesh.getScreenQuad(), e = null, n = this._uniforms; + k ? (e = b._shader_tex, e || (e = b._shader_tex = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, b.pixel_shader, {MIX_TEX:""}))) : (e = b._shader_factor, e || (e = b._shader_factor = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, b.pixel_shader)), f = null == f ? this.properties.factor : f, n.u_mix.set([f, f, f, f])); + var p = this.properties.invert; + this._tex.drawTo(function() { + a.bind(p ? 1 : 0); + d.bind(p ? 0 : 1); + k && k.bind(2); + e.uniforms(n).draw(g); + }); + this.setOutputData(0, this._tex); + } + } + } + }, b.prototype.onGetInputs = function() { + return [["factor", "number"]]; + }, b.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_textureA;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\t#ifdef MIX_TEX\n\r\n\t\t\tuniform sampler2D u_textureMix;\n\r\n\t\t#else\n\r\n\t\t\tuniform vec4 u_mix;\n\r\n\t\t#endif\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\t#ifdef MIX_TEX\n\r\n\t\t\t vec4 f = texture2D(u_textureMix, v_coord);\n\r\n\t\t\t#else\n\r\n\t\t\t vec4 f = u_mix;\n\r\n\t\t\t#endif\n\r\n\t\t gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\r\n\t\t}\n\r\n\t\t", + G.registerNodeType("texture/mix", b), d.title = "Edges", d.desc = "Detects edges", d.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, d.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var a = this.getInputData(0); + if (this.properties.precision === c.PASS_THROUGH) { + this.setOutputData(0, a); + } else { + if (a) { + this._tex = c.getTargetTexture(a, this._tex, this.properties.precision); + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var b = Mesh.getScreenQuad(), k = d._shader, f = this.properties.invert, g = this.properties.factor, e = this.properties.threshold ? 1 : 0; + this._tex.drawTo(function() { + a.bind(0); + k.uniforms({u_texture:0, u_isize:[1 / a.width, 1 / a.height], u_factor:g, u_threshold:e, u_invert:f ? 1 : 0}).draw(b); + }); + this.setOutputData(0, this._tex); + } + } + } + }, d.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec2 u_isize;\n\r\n\t\tuniform int u_invert;\n\r\n\t\tuniform float u_factor;\n\r\n\t\tuniform float u_threshold;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 center = texture2D(u_texture, v_coord);\n\r\n\t\t\tvec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\r\n\t\t\tvec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\r\n\t\t\tvec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\r\n\t\t\tvec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\r\n\t\t\tvec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\r\n\t\t\tdiff *= u_factor;\n\r\n\t\t\tif(u_invert == 1)\n\r\n\t\t\t\tdiff.xyz = vec3(1.0) - diff.xyz;\n\r\n\t\t\tif( u_threshold == 0.0 )\n\r\n\t\t\t\tgl_FragColor = vec4( diff.xyz, center.a );\n\r\n\t\t\telse\n\r\n\t\t\t\tgl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\r\n\t\t}\n\r\n\t\t", + G.registerNodeType("texture/edges", d), g.title = "Depth Range", g.desc = "Generates a texture with a depth range", g.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var a = this.getInputData(0); + if (a) { + var b = gl.UNSIGNED_BYTE; + this.properties.high_precision && (b = gl.half_float_ext ? gl.HALF_FLOAT_OES : gl.FLOAT); + this._temp_texture && this._temp_texture.type == b && this._temp_texture.width == a.width && this._temp_texture.height == a.height || (this._temp_texture = new GL.Texture(a.width, a.height, {type:b, format:gl.RGBA, filter:gl.LINEAR})); + var d = this._uniforms; + b = this.properties.distance; + this.isInputConnected(1) && (b = this.getInputData(1), this.properties.distance = b); + var c = this.properties.range; + this.isInputConnected(2) && (c = this.getInputData(2), this.properties.range = c); + d.u_distance = b; + d.u_range = c; + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var k = Mesh.getScreenQuad(); + g._shader || (g._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, g.pixel_shader), g._shader_onlydepth = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, g.pixel_shader, {ONLY_DEPTH:""})); + var f = this.properties.only_depth ? g._shader_onlydepth : g._shader; + b = null; + b = a.near_far_planes ? a.near_far_planes : window.LS && LS.Renderer._main_camera ? LS.Renderer._main_camera._uniforms.u_camera_planes : [0.1, 1000]; + d.u_camera_planes = b; + this._temp_texture.drawTo(function() { + a.bind(0); + f.uniforms(d).draw(k); + }); + this._temp_texture.near_far_planes = b; + this.setOutputData(0, this._temp_texture); + } + } + }, g.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec2 u_camera_planes;\n\r\n\t\tuniform float u_distance;\n\r\n\t\tuniform float u_range;\n\r\n\t\t\n\r\n\t\tfloat LinearDepth()\n\r\n\t\t{\n\r\n\t\t\tfloat zNear = u_camera_planes.x;\n\r\n\t\t\tfloat zFar = u_camera_planes.y;\n\r\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\r\n\t\t\tdepth = depth * 2.0 - 1.0;\n\r\n\t\t\treturn zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tfloat depth = LinearDepth();\n\r\n\t\t\t#ifdef ONLY_DEPTH\n\r\n\t\t\t gl_FragColor = vec4(depth);\n\r\n\t\t\t#else\n\r\n\t\t\t\tfloat diff = abs(depth * u_camera_planes.y - u_distance);\n\r\n\t\t\t\tfloat dof = 1.0;\n\r\n\t\t\t\tif(diff <= u_range)\n\r\n\t\t\t\t\tdof = diff / u_range;\n\r\n\t\t\t gl_FragColor = vec4(dof);\n\r\n\t\t\t#endif\n\r\n\t\t}\n\r\n\t\t", + G.registerNodeType("texture/depth_range", g), f.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, f.title = "Linear Depth", f.desc = "Creates a color texture with linear depth", f.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var a = this.getInputData(0); + if (a && (a.format == gl.DEPTH_COMPONENT || a.format == gl.DEPTH_STENCIL)) { + var b = this.properties.precision == c.HIGH ? gl.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE; + this._temp_texture && this._temp_texture.type == b && this._temp_texture.width == a.width && this._temp_texture.height == a.height || (this._temp_texture = new GL.Texture(a.width, a.height, {type:b, format:gl.RGB, filter:gl.LINEAR})); + var d = this._uniforms; + d.u_invert = this.properties.invert ? 1 : 0; + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var k = Mesh.getScreenQuad(); + f._shader || (f._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, f.pixel_shader)); + var g = f._shader; + b = null; + b = a.near_far_planes ? a.near_far_planes : window.LS && LS.Renderer._main_camera ? LS.Renderer._main_camera._uniforms.u_camera_planes : [0.1, 1000]; + d.u_camera_planes = b; + d.u_ires.set([0, 0]); + this._temp_texture.drawTo(function() { + a.bind(0); + g.uniforms(d).draw(k); + }); + this._temp_texture.near_far_planes = b; + this.setOutputData(0, this._temp_texture); + } + } + }, f.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec2 u_camera_planes;\n\r\n\t\tuniform int u_invert;\n\r\n\t\tuniform vec2 u_ires;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tfloat zNear = u_camera_planes.x;\n\r\n\t\t\tfloat zFar = u_camera_planes.y;\n\r\n\t\t\tfloat depth = texture2D(u_texture, v_coord + u_ires*0.5).x * 2.0 - 1.0;\n\r\n\t\t\tfloat f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\r\n\t\t\tif( u_invert == 1 )\n\r\n\t\t\t\tf = 1.0 - f;\n\r\n\t\t\tgl_FragColor = vec4(vec3(f),1.0);\n\r\n\t\t}\n\r\n\t\t", + G.registerNodeType("texture/linear_depth", f), C.title = "Blur", C.desc = "Blur a texture", C.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, C.max_iterations = 20, C.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a && this.isOutputConnected(0)) { + var b = this._final_texture; + b && b.width == a.width && b.height == a.height && b.type == a.type || (b = this._final_texture = new GL.Texture(a.width, a.height, {type:a.type, format:gl.RGBA, filter:gl.LINEAR})); + var d = this.properties.iterations; + this.isInputConnected(1) && (d = this.getInputData(1), this.properties.iterations = d); + d = Math.min(Math.floor(d), C.max_iterations); + if (0 == d) { + this.setOutputData(0, a); + } else { + var c = this.properties.intensity; + this.isInputConnected(2) && (c = this.getInputData(2), this.properties.intensity = c); + var k = G.camera_aspect; + k || void 0 === window.gl || (k = gl.canvas.height / gl.canvas.width); + k || (k = 1); + k = this.properties.preserve_aspect ? k : 1; + var f = this.properties.scale || [1, 1]; + a.applyBlur(k * f[0], f[1], c, b); + for (a = 1; a < d; ++a) { + b.applyBlur(k * f[0] * (a + 1), f[1] * (a + 1), c); + } + this.setOutputData(0, b); + } + } + }, G.registerNodeType("texture/blur", C), w.title = "Glow", w.desc = "Filters a texture giving it a glow effect", w.weights = new Float32Array([0.5, 0.4, 0.3, 0.2]), w.widgets_info = {iterations:{type:"number", min:0, max:16, step:1, precision:0}, threshold:{type:"number", min:0, max:10, step:0.01, precision:2}, precision:{widget:"combo", values:c.MODE_VALUES}}, w.prototype.onGetInputs = function() { + return [["enabled", "boolean"], ["threshold", "number"], ["intensity", "number"], ["persistence", "number"], ["iterations", "number"], ["dirt_factor", "number"]]; + }, w.prototype.onGetOutputs = function() { + return [["average", "Texture"]]; + }, w.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a && this.isAnyOutputConnected()) { + if (this.properties.precision === c.PASS_THROUGH || !1 === this.getInputOrProperty("enabled")) { + this.setOutputData(0, a); + } else { + var b = a.width, d = a.height, k = {format:a.format, type:a.type, minFilter:GL.LINEAR, magFilter:GL.LINEAR, wrap:gl.CLAMP_TO_EDGE}, f = c.getTextureType(this.properties.precision, a), g = this._uniforms, e = this._textures, n = w._cut_shader; + n || (n = w._cut_shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, w.cut_pixel_shader)); + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.BLEND); + g.u_threshold = this.getInputOrProperty("threshold"); + var p = e[0] = GL.Texture.getTemporary(b, d, k); + a.blit(p, n.uniforms(g)); + var h = p, l = this.getInputOrProperty("iterations"); + l = Math.clamp(l, 1, 16) | 0; + var m = g.u_texel_size, q = this.getInputOrProperty("intensity"); + g.u_intensity = 1; + g.u_delta = this.properties.scale; + n = w._shader; + n || (n = w._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, w.scale_pixel_shader)); + for (var r = 1; r < l; r++) { + b >>= 1; + 1 < (d | 0) && (d >>= 1); + if (2 > b) { + break; + } + p = e[r] = GL.Texture.getTemporary(b, d, k); + m[0] = 1 / h.width; + m[1] = 1 / h.height; + h.blit(p, n.uniforms(g)); + h = p; + } + this.isOutputConnected(2) && (b = this._average_texture, b && b.type == a.type && b.format == a.format || (b = this._average_texture = new GL.Texture(1, 1, {type:a.type, format:a.format, filter:gl.LINEAR})), m[0] = 1 / h.width, m[1] = 1 / h.height, g.u_intensity = q, g.u_delta = 1, h.blit(b, n.uniforms(g)), this.setOutputData(2, b)); + gl.enable(gl.BLEND); + gl.blendFunc(gl.ONE, gl.ONE); + g.u_intensity = this.getInputOrProperty("persistence"); + g.u_delta = 0.5; + for (r -= 2; 0 <= r; r--) { + p = e[r], e[r] = null, m[0] = 1 / h.width, m[1] = 1 / h.height, h.blit(p, n.uniforms(g)), GL.Texture.releaseTemporary(h), h = p; + } + gl.disable(gl.BLEND); + this.isOutputConnected(1) && (e = this._glow_texture, e && e.width == a.width && e.height == a.height && e.type == f && e.format == a.format || (e = this._glow_texture = new GL.Texture(a.width, a.height, {type:f, format:a.format, filter:gl.LINEAR})), h.blit(e), this.setOutputData(1, e)); + if (this.isOutputConnected(0)) { + e = this._final_texture; + e && e.width == a.width && e.height == a.height && e.type == f && e.format == a.format || (e = this._final_texture = new GL.Texture(a.width, a.height, {type:f, format:a.format, filter:gl.LINEAR})); + var t = this.getInputData(1), u = this.getInputOrProperty("dirt_factor"); + g.u_intensity = q; + n = t ? w._dirt_final_shader : w._final_shader; + n || (n = t ? w._dirt_final_shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, w.final_pixel_shader, {USE_DIRT:""}) : w._final_shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, w.final_pixel_shader)); + e.drawTo(function() { + a.bind(0); + h.bind(1); + t && (n.setUniform("u_dirt_factor", u), n.setUniform("u_dirt_texture", t.bind(2))); + n.toViewport(g); + }); + this.setOutputData(0, e); + } + GL.Texture.releaseTemporary(h); + } + } + }, w.cut_pixel_shader = "precision highp float;\n\r\n\tvarying vec2 v_coord;\n\r\n\tuniform sampler2D u_texture;\n\r\n\tuniform float u_threshold;\n\r\n\tvoid main() {\n\r\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\r\n\t}", w.scale_pixel_shader = "precision highp float;\n\r\n\tvarying vec2 v_coord;\n\r\n\tuniform sampler2D u_texture;\n\r\n\tuniform vec2 u_texel_size;\n\r\n\tuniform float u_delta;\n\r\n\tuniform float u_intensity;\n\r\n\t\n\r\n\tvec4 sampleBox(vec2 uv) {\n\r\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\r\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\r\n\t\treturn s * 0.25;\n\r\n\t}\n\r\n\tvoid main() {\n\r\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\r\n\t}", + w.final_pixel_shader = "precision highp float;\n\r\n\tvarying vec2 v_coord;\n\r\n\tuniform sampler2D u_texture;\n\r\n\tuniform sampler2D u_glow_texture;\n\r\n\t#ifdef USE_DIRT\n\r\n\t\tuniform sampler2D u_dirt_texture;\n\r\n\t#endif\n\r\n\tuniform vec2 u_texel_size;\n\r\n\tuniform float u_delta;\n\r\n\tuniform float u_intensity;\n\r\n\tuniform float u_dirt_factor;\n\r\n\t\n\r\n\tvec4 sampleBox(vec2 uv) {\n\r\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\r\n\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\r\n\t\treturn s * 0.25;\n\r\n\t}\n\r\n\tvoid main() {\n\r\n\t\tvec4 glow = sampleBox( v_coord );\n\r\n\t\t#ifdef USE_DIRT\n\r\n\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\r\n\t\t#endif\n\r\n\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\r\n\t}", + G.registerNodeType("texture/glow", w), I.title = "Kuwahara Filter", I.desc = "Filters a texture giving an artistic oil canvas painting", I.max_radius = 10, I._shaders = [], I.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a && this.isOutputConnected(0)) { + var b = this._temp_texture; + b && b.width == a.width && b.height == a.height && b.type == a.type || (this._temp_texture = new GL.Texture(a.width, a.height, {type:a.type, format:gl.RGBA, filter:gl.LINEAR})); + b = this.properties.radius; + b = Math.min(Math.floor(b), I.max_radius); + if (0 == b) { + this.setOutputData(0, a); + } else { + var d = this.properties.intensity, c = G.camera_aspect; + c || void 0 === window.gl || (c = gl.canvas.height / gl.canvas.width); + c || (c = 1); + c = this.properties.preserve_aspect ? c : 1; + I._shaders[b] || (I._shaders[b] = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, I.pixel_shader, {RADIUS:b.toFixed(0)})); + var k = I._shaders[b], f = GL.Mesh.getScreenQuad(); + a.bind(0); + this._temp_texture.drawTo(function() { + k.uniforms({u_texture:0, u_intensity:d, u_resolution:[a.width, a.height], u_iResolution:[1 / a.width, 1 / a.height]}).draw(f); + }); + this.setOutputData(0, this._temp_texture); + } + } + }, I.pixel_shader = "\n\r\nprecision highp float;\n\r\nvarying vec2 v_coord;\n\r\nuniform sampler2D u_texture;\n\r\nuniform float u_intensity;\n\r\nuniform vec2 u_resolution;\n\r\nuniform vec2 u_iResolution;\n\r\n#ifndef RADIUS\n\r\n\t#define RADIUS 7\n\r\n#endif\n\r\nvoid main() {\n\r\n\n\r\n\tconst int radius = RADIUS;\n\r\n\tvec2 fragCoord = v_coord;\n\r\n\tvec2 src_size = u_iResolution;\n\r\n\tvec2 uv = v_coord;\n\r\n\tfloat n = float((radius + 1) * (radius + 1));\n\r\n\tint i;\n\r\n\tint j;\n\r\n\tvec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\r\n\tvec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\r\n\tvec3 c;\n\r\n\t\n\r\n\tfor (int j = -radius; j <= 0; ++j) {\n\r\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\r\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\r\n\t\t\tm0 += c;\n\r\n\t\t\ts0 += c * c;\n\r\n\t\t}\n\r\n\t}\n\r\n\t\n\r\n\tfor (int j = -radius; j <= 0; ++j) {\n\r\n\t\tfor (int i = 0; i <= radius; ++i) {\n\r\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\r\n\t\t\tm1 += c;\n\r\n\t\t\ts1 += c * c;\n\r\n\t\t}\n\r\n\t}\n\r\n\t\n\r\n\tfor (int j = 0; j <= radius; ++j) {\n\r\n\t\tfor (int i = 0; i <= radius; ++i) {\n\r\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\r\n\t\t\tm2 += c;\n\r\n\t\t\ts2 += c * c;\n\r\n\t\t}\n\r\n\t}\n\r\n\t\n\r\n\tfor (int j = 0; j <= radius; ++j) {\n\r\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\r\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\r\n\t\t\tm3 += c;\n\r\n\t\t\ts3 += c * c;\n\r\n\t\t}\n\r\n\t}\n\r\n\t\n\r\n\tfloat min_sigma2 = 1e+2;\n\r\n\tm0 /= n;\n\r\n\ts0 = abs(s0 / n - m0 * m0);\n\r\n\t\n\r\n\tfloat sigma2 = s0.r + s0.g + s0.b;\n\r\n\tif (sigma2 < min_sigma2) {\n\r\n\t\tmin_sigma2 = sigma2;\n\r\n\t\tgl_FragColor = vec4(m0, 1.0);\n\r\n\t}\n\r\n\t\n\r\n\tm1 /= n;\n\r\n\ts1 = abs(s1 / n - m1 * m1);\n\r\n\t\n\r\n\tsigma2 = s1.r + s1.g + s1.b;\n\r\n\tif (sigma2 < min_sigma2) {\n\r\n\t\tmin_sigma2 = sigma2;\n\r\n\t\tgl_FragColor = vec4(m1, 1.0);\n\r\n\t}\n\r\n\t\n\r\n\tm2 /= n;\n\r\n\ts2 = abs(s2 / n - m2 * m2);\n\r\n\t\n\r\n\tsigma2 = s2.r + s2.g + s2.b;\n\r\n\tif (sigma2 < min_sigma2) {\n\r\n\t\tmin_sigma2 = sigma2;\n\r\n\t\tgl_FragColor = vec4(m2, 1.0);\n\r\n\t}\n\r\n\t\n\r\n\tm3 /= n;\n\r\n\ts3 = abs(s3 / n - m3 * m3);\n\r\n\t\n\r\n\tsigma2 = s3.r + s3.g + s3.b;\n\r\n\tif (sigma2 < min_sigma2) {\n\r\n\t\tmin_sigma2 = sigma2;\n\r\n\t\tgl_FragColor = vec4(m3, 1.0);\n\r\n\t}\n\r\n}\n\r\n", + G.registerNodeType("texture/kuwahara", I), K.title = "XDoG Filter", K.desc = "Filters a texture giving an artistic ink style", K.max_radius = 10, K._shaders = [], K.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a && this.isOutputConnected(0)) { + var b = this._temp_texture; + b && b.width == a.width && b.height == a.height && b.type == a.type || (this._temp_texture = new GL.Texture(a.width, a.height, {type:a.type, format:gl.RGBA, filter:gl.LINEAR})); + K._xdog_shader || (K._xdog_shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, K.xdog_pixel_shader)); + var d = K._xdog_shader, c = GL.Mesh.getScreenQuad(), k = this.properties.sigma, f = this.properties.k, g = this.properties.p, e = this.properties.epsilon, n = this.properties.phi; + a.bind(0); + this._temp_texture.drawTo(function() { + d.uniforms({src:0, sigma:k, k:f, p:g, epsilon:e, phi:n, cvsWidth:a.width, cvsHeight:a.height}).draw(c); + }); + this.setOutputData(0, this._temp_texture); + } + }, K.xdog_pixel_shader = "\n\r\nprecision highp float;\n\r\nuniform sampler2D src;\n\n\r\nuniform float cvsHeight;\n\r\nuniform float cvsWidth;\n\n\r\nuniform float sigma;\n\r\nuniform float k;\n\r\nuniform float p;\n\r\nuniform float epsilon;\n\r\nuniform float phi;\n\r\nvarying vec2 v_coord;\n\n\r\nfloat cosh(float val)\n\r\n{\n\r\n\tfloat tmp = exp(val);\n\r\n\tfloat cosH = (tmp + 1.0 / tmp) / 2.0;\n\r\n\treturn cosH;\n\r\n}\n\n\r\nfloat tanh(float val)\n\r\n{\n\r\n\tfloat tmp = exp(val);\n\r\n\tfloat tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\r\n\treturn tanH;\n\r\n}\n\n\r\nfloat sinh(float val)\n\r\n{\n\r\n\tfloat tmp = exp(val);\n\r\n\tfloat sinH = (tmp - 1.0 / tmp) / 2.0;\n\r\n\treturn sinH;\n\r\n}\n\n\r\nvoid main(void){\n\r\n\tvec3 destColor = vec3(0.0);\n\r\n\tfloat tFrag = 1.0 / cvsHeight;\n\r\n\tfloat sFrag = 1.0 / cvsWidth;\n\r\n\tvec2 Frag = vec2(sFrag,tFrag);\n\r\n\tvec2 uv = gl_FragCoord.st;\n\r\n\tfloat twoSigmaESquared = 2.0 * sigma * sigma;\n\r\n\tfloat twoSigmaRSquared = twoSigmaESquared * k * k;\n\r\n\tint halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\r\n\tconst int MAX_NUM_ITERATION = 99999;\n\r\n\tvec2 sum = vec2(0.0);\n\r\n\tvec2 norm = vec2(0.0);\n\n\r\n\tfor(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\r\n\t\tint i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\r\n\t\tint j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\r\n\t\tfloat d = length(vec2(i,j));\n\r\n\t\tvec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\r\n\t\t\t\t\t\t\texp( -d * d / twoSigmaRSquared ));\n\n\r\n\t\tvec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\r\n\t\tnorm += kernel;\n\r\n\t\tsum += kernel * L;\n\r\n\t}\n\n\r\n\tsum /= norm;\n\n\r\n\tfloat H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\r\n\tfloat edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\r\n\tdestColor = vec3(edge);\n\r\n\tgl_FragColor = vec4(destColor, 1.0);\n\r\n}", + G.registerNodeType("texture/xDoG", K), L.title = "Webcam", L.desc = "Webcam texture", L.is_webcam_open = !1, L.prototype.openStream = function() { + if (navigator.getUserMedia) { + this._waiting_confirmation = !0; + navigator.mediaDevices.getUserMedia({audio:!1, video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this)).catch(function(b) { + L.is_webcam_open = !1; + console.log("Webcam rejected", b); + a._webcam_stream = !1; + a.boxcolor = "red"; + a.trigger("stream_error"); + }); + var a = this; + } + }, L.prototype.closeStream = function() { + if (this._webcam_stream) { + var a = this._webcam_stream.getTracks(); + if (a.length) { + for (var b = 0; b < a.length; ++b) { + a[b].stop(); + } + } + L.is_webcam_open = !1; + this._video = this._webcam_stream = null; + this.boxcolor = "black"; + this.trigger("stream_closed"); + } + }, L.prototype.streamReady = function(a) { + this._webcam_stream = a; + this.boxcolor = "green"; + var b = this._video; + b || (b = document.createElement("video"), b.autoplay = !0, b.srcObject = a, this._video = b, b.onloadedmetadata = function(a) { + L.is_webcam_open = !0; + console.log(a); + }); + this.trigger("stream_ready", b); + }, L.prototype.onPropertyChanged = function(a, b) { + "facingMode" == a && (this.properties.facingMode = b, this.closeStream(), this.openStream()); + }, L.prototype.onRemoved = function() { + if (this._webcam_stream) { + var a = this._webcam_stream.getTracks(); + if (a.length) { + for (var b = 0; b < a.length; ++b) { + a[b].stop(); + } + } + this._video = this._webcam_stream = null; + } + }, L.prototype.onDrawBackground = function(a) { + this.flags.collapsed || 20 >= this.size[1] || !this._video || (a.save(), a.webgl ? this._video_texture && a.drawImage(this._video_texture, 0, 0, this.size[0], this.size[1]) : a.drawImage(this._video, 0, 0, this.size[0], this.size[1]), a.restore()); + }, L.prototype.onExecute = function() { + null != this._webcam_stream || this._waiting_confirmation || this.openStream(); + if (this._video && this._video.videoWidth) { + var a = this._video.videoWidth, b = this._video.videoHeight, d = this._video_texture; + d && d.width == a && d.height == b || (this._video_texture = new GL.Texture(a, b, {format:gl.RGB, filter:gl.LINEAR})); + this._video_texture.uploadImage(this._video); + this._video_texture.version = ++this.version; + this.properties.texture_name && (c.getTexturesContainer()[this.properties.texture_name] = this._video_texture); + this.setOutputData(0, this._video_texture); + for (a = 1; a < this.outputs.length; ++a) { + if (this.outputs[a]) { + switch(this.outputs[a].name) { + case "width": + this.setOutputData(a, this._video.videoWidth); + break; + case "height": + this.setOutputData(a, this._video.videoHeight); + } + } + } + } + }, L.prototype.onGetOutputs = function() { + return [["width", "number"], ["height", "number"], ["stream_ready", G.EVENT], ["stream_closed", G.EVENT], ["stream_error", G.EVENT]]; + }, G.registerNodeType("texture/webcam", L), D.title = "Lens FX", D.desc = "distortion and chromatic aberration", D.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, D.prototype.onGetInputs = function() { + return [["enabled", "boolean"]]; + }, D.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a && this.isOutputConnected(0)) { + if (this.properties.precision === c.PASS_THROUGH || !1 === this.getInputOrProperty("enabled")) { + this.setOutputData(0, a); + } else { + var b = this._temp_texture; + b && b.width == a.width && b.height == a.height && b.type == a.type || (b = this._temp_texture = new GL.Texture(a.width, a.height, {type:a.type, format:gl.RGBA, filter:gl.LINEAR})); + var d = D._shader; + d || (d = D._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, D.pixel_shader)); + var k = this.getInputData(1); + null == k && (k = this.properties.factor); + var f = this._uniforms; + f.u_factor = k; + gl.disable(gl.DEPTH_TEST); + b.drawTo(function() { + a.bind(0); + d.uniforms(f).draw(GL.Mesh.getScreenQuad()); + }); + this.setOutputData(0, b); + } + } + }, D.pixel_shader = "precision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_factor;\n\r\n\t\tvec2 barrelDistortion(vec2 coord, float amt) {\n\r\n\t\t\tvec2 cc = coord - 0.5;\n\r\n\t\t\tfloat dist = dot(cc, cc);\n\r\n\t\t\treturn coord + cc * dist * amt;\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tfloat sat( float t )\n\r\n\t\t{\n\r\n\t\t\treturn clamp( t, 0.0, 1.0 );\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tfloat linterp( float t ) {\n\r\n\t\t\treturn sat( 1.0 - abs( 2.0*t - 1.0 ) );\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tfloat remap( float t, float a, float b ) {\n\r\n\t\t\treturn sat( (t - a) / (b - a) );\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tvec4 spectrum_offset( float t ) {\n\r\n\t\t\tvec4 ret;\n\r\n\t\t\tfloat lo = step(t,0.5);\n\r\n\t\t\tfloat hi = 1.0-lo;\n\r\n\t\t\tfloat w = linterp( remap( t, 1.0/6.0, 5.0/6.0 ) );\n\r\n\t\t\tret = vec4(lo,1.0,hi, 1.) * vec4(1.0-w, w, 1.0-w, 1.);\n\r\n\t\t\n\r\n\t\t\treturn pow( ret, vec4(1.0/2.2) );\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tconst float max_distort = 2.2;\n\r\n\t\tconst int num_iter = 12;\n\r\n\t\tconst float reci_num_iter_f = 1.0 / float(num_iter);\n\r\n\t\t\n\r\n\t\tvoid main()\n\r\n\t\t{\t\n\r\n\t\t\tvec2 uv=v_coord;\n\r\n\t\t\tvec4 sumcol = vec4(0.0);\n\r\n\t\t\tvec4 sumw = vec4(0.0);\t\n\r\n\t\t\tfor ( int i=0; i Math.abs(b)) { + return c[1]; + } + a = (a - c[0]) / b; + return c[1] * (1.0 - a) + k[1] * a; + } + } + return 0; + } + }, J.prototype.updateCurve = function() { + for (var a = this._values, b = a.length / 4, d = this.properties.split_channels, c = 0; c < b; ++c) { + if (d) { + a[4 * c] = Math.clamp(255 * this.sampleCurve(c / b, this._points.R), 0, 255), a[4 * c + 1] = Math.clamp(255 * this.sampleCurve(c / b, this._points.G), 0, 255), a[4 * c + 2] = Math.clamp(255 * this.sampleCurve(c / b, this._points.B), 0, 255); + } else { + var k = this.sampleCurve(c / b); + a[4 * c] = a[4 * c + 1] = a[4 * c + 2] = Math.clamp(255 * k, 0, 255); + } + a[4 * c + 3] = 255; + } + this._curve_texture || (this._curve_texture = new GL.Texture(256, 1, {format:gl.RGBA, magFilter:gl.LINEAR, wrap:gl.CLAMP_TO_EDGE})); + this._curve_texture.uploadData(a, null, !0); + }, J.prototype.onSerialize = function(a) { + var b = {}, d; + for (d in this._points) { + b[d] = this._points[d].concat(); + } + a.curves = b; + }, J.prototype.onConfigure = function(a) { + this._points = a.curves; + this.curve_editor && (curve_editor.points = this._points); + this._must_update = !0; + }, J.prototype.onMouseDown = function(a, b, d) { + if (this.curve_editor) { + return (a = this.curve_editor.onMouseDown([b[0], b[1] - this.curve_offset], d)) && this.captureInput(!0), a; + } + }, J.prototype.onMouseMove = function(a, b, d) { + if (this.curve_editor) { + return this.curve_editor.onMouseMove([b[0], b[1] - this.curve_offset], d); + } + }, J.prototype.onMouseUp = function(a, b, d) { + if (this.curve_editor) { + return this.curve_editor.onMouseUp([b[0], b[1] - this.curve_offset], d); + } + this.captureInput(!1); + }, J.channel_line_colors = {RGB:"#666", R:"#F33", G:"#3F3", B:"#33F"}, J.prototype.onDrawBackground = function(a, b) { + if (!this.flags.collapsed) { + this.curve_editor || (this.curve_editor = new G.CurveEditor(this._points.R)); + a.save(); + a.translate(0, this.curve_offset); + var d = this.widgets[1].value; + this.properties.split_channels ? ("RGB" == d && (this.widgets[1].value = d = "R", this.widgets[1].disabled = !1), this.curve_editor.points = this._points.R, this.curve_editor.draw(a, [this.size[0], this.size[1] - this.curve_offset], b, "#111", J.channel_line_colors.R, !0), a.globalCompositeOperation = "lighten", this.curve_editor.points = this._points.G, this.curve_editor.draw(a, [this.size[0], this.size[1] - this.curve_offset], b, null, J.channel_line_colors.G, !0), this.curve_editor.points = + this._points.B, this.curve_editor.draw(a, [this.size[0], this.size[1] - this.curve_offset], b, null, J.channel_line_colors.B, !0), a.globalCompositeOperation = "source-over") : (this.widgets[1].value = d = "RGB", this.widgets[1].disabled = !0); + this.curve_editor.points = this._points[d]; + this.curve_editor.draw(a, [this.size[0], this.size[1] - this.curve_offset], b, this.properties.split_channels ? null : "#111", J.channel_line_colors[d]); + a.restore(); + } + }, J.pixel_shader = "precision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_curve;\n\r\n\t\tuniform float u_range;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = texture2D( u_texture, v_coord ) * u_range;\n\r\n\t\t\tcolor.x = texture2D( u_curve, vec2( color.x, 0.5 ) ).x;\n\r\n\t\t\tcolor.y = texture2D( u_curve, vec2( color.y, 0.5 ) ).y;\n\r\n\t\t\tcolor.z = texture2D( u_curve, vec2( color.z, 0.5 ) ).z;\n\r\n\t\t\t//color.w = texture2D( u_curve, vec2( color.w, 0.5 ) ).w;\n\r\n\t\t\tgl_FragColor = color;\n\r\n\t\t}", + G.registerNodeType("texture/curve", J), N.title = "Exposition", N.desc = "Controls texture exposition", N.widgets_info = {exposition:{widget:"slider", min:0, max:3}, precision:{widget:"combo", values:c.MODE_VALUES}}, N.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a && this.isOutputConnected(0)) { + var b = this._temp_texture; + b && b.width == a.width && b.height == a.height && b.type == a.type || (b = this._temp_texture = new GL.Texture(a.width, a.height, {type:a.type, format:gl.RGBA, filter:gl.LINEAR})); + var d = N._shader; + d || (d = N._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, N.pixel_shader)); + var c = this.getInputData(1); + null != c && (this.properties.exposition = c); + var k = this._uniforms; + b.drawTo(function() { + gl.disable(gl.DEPTH_TEST); + a.bind(0); + d.uniforms(k).draw(GL.Mesh.getScreenQuad()); + }); + this.setOutputData(0, b); + } + }, N.pixel_shader = "precision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_exposition;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = texture2D( u_texture, v_coord );\n\r\n\t\t\tgl_FragColor = vec4( color.xyz * u_exposition, color.a );\n\r\n\t\t}", G.registerNodeType("texture/exposition", N), M.title = "Tone Mapping", M.desc = "Applies Tone Mapping to convert from high to low", M.widgets_info = {precision:{widget:"combo", + values:c.MODE_VALUES}}, M.prototype.onGetInputs = function() { + return [["enabled", "boolean"]]; + }, M.prototype.onExecute = function() { + var a = this.getInputData(0); + if (a && this.isOutputConnected(0)) { + if (this.properties.precision === c.PASS_THROUGH || !1 === this.getInputOrProperty("enabled")) { + this.setOutputData(0, a); + } else { + var b = this._temp_texture; + b && b.width == a.width && b.height == a.height && b.type == a.type || (b = this._temp_texture = new GL.Texture(a.width, a.height, {type:a.type, format:gl.RGBA, filter:gl.LINEAR})); + var d = this.getInputData(1); + null == d && (d = this.properties.average_lum); + var k = this._uniforms, f = null; + d.constructor === Number ? (this.properties.average_lum = d, k.u_average_lum = this.properties.average_lum, f = M._shader, f || (f = M._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, M.pixel_shader))) : d.constructor === GL.Texture && (k.u_average_texture = d.bind(1), f = M._shader_texture, f || (f = M._shader_texture = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, M.pixel_shader, {AVG_TEXTURE:""}))); + k.u_lumwhite2 = this.properties.lum_white * this.properties.lum_white; + k.u_scale = this.properties.scale; + k.u_igamma = 1 / this.properties.gamma; + gl.disable(gl.DEPTH_TEST); + b.drawTo(function() { + a.bind(0); + f.uniforms(k).draw(GL.Mesh.getScreenQuad()); + }); + this.setOutputData(0, this._temp_texture); + } + } + }, M.pixel_shader = "precision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_scale;\n\r\n\t\t#ifdef AVG_TEXTURE\n\r\n\t\t\tuniform sampler2D u_average_texture;\n\r\n\t\t#else\n\r\n\t\t\tuniform float u_average_lum;\n\r\n\t\t#endif\n\r\n\t\tuniform float u_lumwhite2;\n\r\n\t\tuniform float u_igamma;\n\r\n\t\tvec3 RGB2xyY (vec3 rgb)\n\r\n\t\t{\n\r\n\t\t\t const mat3 RGB2XYZ = mat3(0.4124, 0.3576, 0.1805,\n\r\n\t\t\t\t\t\t\t\t\t 0.2126, 0.7152, 0.0722,\n\r\n\t\t\t\t\t\t\t\t\t 0.0193, 0.1192, 0.9505);\n\r\n\t\t\tvec3 XYZ = RGB2XYZ * rgb;\n\r\n\t\t\t\n\r\n\t\t\tfloat f = (XYZ.x + XYZ.y + XYZ.z);\n\r\n\t\t\treturn vec3(XYZ.x / f,\n\r\n\t\t\t\t\t\tXYZ.y / f,\n\r\n\t\t\t\t\t\tXYZ.y);\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = texture2D( u_texture, v_coord );\n\r\n\t\t\tvec3 rgb = color.xyz;\n\r\n\t\t\tfloat average_lum = 0.0;\n\r\n\t\t\t#ifdef AVG_TEXTURE\n\r\n\t\t\t\tvec3 pixel = texture2D(u_average_texture,vec2(0.5)).xyz;\n\r\n\t\t\t\taverage_lum = (pixel.x + pixel.y + pixel.z) / 3.0;\n\r\n\t\t\t#else\n\r\n\t\t\t\taverage_lum = u_average_lum;\n\r\n\t\t\t#endif\n\r\n\t\t\t//Ld - this part of the code is the same for both versions\n\r\n\t\t\tfloat lum = dot(rgb, vec3(0.2126, 0.7152, 0.0722));\n\r\n\t\t\tfloat L = (u_scale / average_lum) * lum;\n\r\n\t\t\tfloat Ld = (L * (1.0 + L / u_lumwhite2)) / (1.0 + L);\n\r\n\t\t\t//first\n\r\n\t\t\t//vec3 xyY = RGB2xyY(rgb);\n\r\n\t\t\t//xyY.z *= Ld;\n\r\n\t\t\t//rgb = xyYtoRGB(xyY);\n\r\n\t\t\t//second\n\r\n\t\t\trgb = (rgb / lum) * Ld;\n\r\n\t\t\trgb = max(rgb,vec3(0.001));\n\r\n\t\t\trgb = pow( rgb, vec3( u_igamma ) );\n\r\n\t\t\tgl_FragColor = vec4( rgb, color.a );\n\r\n\t\t}", + G.registerNodeType("texture/tonemapping", M), P.title = "Perlin", P.desc = "Generates a perlin noise texture", P.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}, width:{type:"Number", precision:0, step:1}, height:{type:"Number", precision:0, step:1}, octaves:{type:"Number", precision:0, step:1, min:1, max:50}}, P.prototype.onGetInputs = function() { + return [["seed", "Number"], ["persistence", "Number"], ["octaves", "Number"], ["scale", "Number"], ["amplitude", "Number"], ["offset", "vec2"]]; + }, P.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var a = this.properties.width | 0, b = this.properties.height | 0; + 0 == a && (a = gl.viewport_data[2]); + 0 == b && (b = gl.viewport_data[3]); + var d = c.getTextureType(this.properties.precision), k = this._texture; + k && k.width == a && k.height == b && k.type == d || (k = this._texture = new GL.Texture(a, b, {type:d, format:gl.RGB, filter:gl.LINEAR})); + var f = this.getInputOrProperty("persistence"), g = this.getInputOrProperty("octaves"), e = this.getInputOrProperty("offset"), n = this.getInputOrProperty("scale"), p = this.getInputOrProperty("amplitude"), h = this.getInputOrProperty("seed"); + d = "" + a + b + d + f + g + n + h + e[0] + e[1] + p; + if (d != this._key) { + this._key = d; + var l = this._uniforms; + l.u_persistence = f; + l.u_octaves = g; + l.u_offset.set(e); + l.u_scale = n; + l.u_amplitude = p; + l.u_seed = 128 * h; + l.u_viewport[0] = a; + l.u_viewport[1] = b; + var m = P._shader; + m || (m = P._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, P.pixel_shader)); + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + k.drawTo(function() { + m.uniforms(l).draw(GL.Mesh.getScreenQuad()); + }); + } + this.setOutputData(0, k); + } + }, P.pixel_shader = "precision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform vec2 u_offset;\n\r\n\t\tuniform float u_scale;\n\r\n\t\tuniform float u_persistence;\n\r\n\t\tuniform int u_octaves;\n\r\n\t\tuniform float u_amplitude;\n\r\n\t\tuniform vec2 u_viewport;\n\r\n\t\tuniform float u_seed;\n\r\n\t\t#define M_PI 3.14159265358979323846\n\r\n\t\t\n\r\n\t\tfloat rand(vec2 c){\treturn fract(sin(dot(c.xy ,vec2( 12.9898 + u_seed,78.233 + u_seed))) * 43758.5453); }\n\r\n\t\t\n\r\n\t\tfloat noise(vec2 p, float freq ){\n\r\n\t\t\tfloat unit = u_viewport.x/freq;\n\r\n\t\t\tvec2 ij = floor(p/unit);\n\r\n\t\t\tvec2 xy = mod(p,unit)/unit;\n\r\n\t\t\t//xy = 3.*xy*xy-2.*xy*xy*xy;\n\r\n\t\t\txy = .5*(1.-cos(M_PI*xy));\n\r\n\t\t\tfloat a = rand((ij+vec2(0.,0.)));\n\r\n\t\t\tfloat b = rand((ij+vec2(1.,0.)));\n\r\n\t\t\tfloat c = rand((ij+vec2(0.,1.)));\n\r\n\t\t\tfloat d = rand((ij+vec2(1.,1.)));\n\r\n\t\t\tfloat x1 = mix(a, b, xy.x);\n\r\n\t\t\tfloat x2 = mix(c, d, xy.x);\n\r\n\t\t\treturn mix(x1, x2, xy.y);\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tfloat pNoise(vec2 p, int res){\n\r\n\t\t\tfloat persistance = u_persistence;\n\r\n\t\t\tfloat n = 0.;\n\r\n\t\t\tfloat normK = 0.;\n\r\n\t\t\tfloat f = 4.;\n\r\n\t\t\tfloat amp = 1.0;\n\r\n\t\t\tint iCount = 0;\n\r\n\t\t\tfor (int i = 0; i<50; i++){\n\r\n\t\t\t\tn+=amp*noise(p, f);\n\r\n\t\t\t\tf*=2.;\n\r\n\t\t\t\tnormK+=amp;\n\r\n\t\t\t\tamp*=persistance;\n\r\n\t\t\t\tif (iCount >= res)\n\r\n\t\t\t\t\tbreak;\n\r\n\t\t\t\tiCount++;\n\r\n\t\t\t}\n\r\n\t\t\tfloat nf = n/normK;\n\r\n\t\t\treturn nf*nf*nf*nf;\n\r\n\t\t}\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec2 uv = v_coord * u_scale * u_viewport + u_offset * u_scale;\n\r\n\t\t\tvec4 color = vec4( pNoise( uv, u_octaves ) * u_amplitude );\n\r\n\t\t\tgl_FragColor = color;\n\r\n\t\t}", + G.registerNodeType("texture/perlin", P), O.title = "Canvas2D", O.desc = "Executes Canvas2D code inside a texture or the viewport.", O.help = "Set width and height to 0 to match viewport size.", O.default_code = "//vars: canvas,ctx,time\nctx.fillStyle='red';\nctx.fillRect(0,0,50,50);\n", O.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}, code:{type:"code"}, width:{type:"Number", precision:0, step:1}, height:{type:"Number", precision:0, step:1}}, O.prototype.onPropertyChanged = function(a, + b) { + "code" == a && this.compileCode(b); + }, O.prototype.compileCode = function(a) { + this._func = null; + if (G.allow_scripts) { + try { + this._func = new Function("canvas", "ctx", "time", "script", "v", a), this.boxcolor = "#00FF00"; + } catch (W) { + this.boxcolor = "#FF0000", console.error("Error parsing script"), console.error(W); + } + } + }, O.prototype.onExecute = function() { + var a = this._func; + a && this.isOutputConnected(0) && this.executeDraw(a); + }, O.prototype.executeDraw = function(a) { + var b = this.properties.width || gl.canvas.width, d = this.properties.height || gl.canvas.height, k = this._temp_texture, f = c.getTextureType(this.properties.precision); + k && k.width == b && k.height == d && k.type == f || (k = this._temp_texture = new GL.Texture(b, d, {format:gl.RGBA, filter:gl.LINEAR, type:f})); + var g = this.getInputData(0), e = this.properties, n = this, p = this.graph.getTime(), h = gl, l = gl.canvas; + if (this.properties.use_html_canvas || !v.enableWebGLCanvas) { + this._canvas ? (l = this._canvas, h = this._ctx) : (l = this._canvas = createCanvas(b.height), h = this._ctx = l.getContext("2d")), l.width = b, l.height = d; + } + if (h == gl) { + k.drawTo(function() { + gl.start2D(); + e.clear && (gl.clearColor(0, 0, 0, 0), gl.clear(gl.COLOR_BUFFER_BIT)); + try { + a.draw ? a.draw.call(n, l, h, p, a, g) : a.call(n, l, h, p, a, g), n.boxcolor = "#00FF00"; + } catch (T) { + n.boxcolor = "#FF0000", console.error("Error executing script"), console.error(T); + } + gl.finish2D(); + }); + } else { + e.clear && h.clearRect(0, 0, l.width, l.height); + try { + a.draw ? a.draw.call(this, l, h, p, a, g) : a.call(this, l, h, p, a, g), this.boxcolor = "#00FF00"; + } catch (T) { + this.boxcolor = "#FF0000", console.error("Error executing script"), console.error(T); + } + k.uploadImage(l); + } + this.setOutputData(0, k); + }, G.registerNodeType("texture/canvas2D", O), Q.title = "Matte", Q.desc = "Extracts background", Q.widgets_info = {key_color:{widget:"color"}, precision:{widget:"combo", values:c.MODE_VALUES}}, Q.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var a = this.getInputData(0); + if (this.properties.precision === c.PASS_THROUGH) { + this.setOutputData(0, a); + } else { + if (a) { + this._tex = c.getTargetTexture(a, this._tex, this.properties.precision); + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + this._uniforms || (this._uniforms = {u_texture:0, u_key_color:this.properties.key_color, u_threshold:1, u_slope:1}); + var b = this._uniforms, d = Mesh.getScreenQuad(), k = Q._shader; + k || (k = Q._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, Q.pixel_shader)); + b.u_key_color = this.properties.key_color; + b.u_threshold = this.properties.threshold; + b.u_slope = this.properties.slope; + this._tex.drawTo(function() { + a.bind(0); + k.uniforms(b).draw(d); + }); + this.setOutputData(0, this._tex); + } + } + } + }, Q.pixel_shader = "precision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec3 u_key_color;\n\r\n\t\tuniform float u_threshold;\n\r\n\t\tuniform float u_slope;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec3 color = texture2D( u_texture, v_coord ).xyz;\n\r\n\t\t\tfloat diff = length( normalize(color) - normalize(u_key_color) );\n\r\n\t\t\tfloat edge = u_threshold * (1.0 - u_slope);\n\r\n\t\t\tfloat alpha = smoothstep( edge, u_threshold, diff);\n\r\n\t\t\tgl_FragColor = vec4( color, alpha );\n\r\n\t\t}", + G.registerNodeType("texture/matte", Q), R.title = "CubemapToTexture2D", R.desc = "Transforms a CUBEMAP texture into a TEXTURE2D in Polar Representation", R.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var a = this.getInputData(0); + if (a && a.texture_type == GL.TEXTURE_CUBE_MAP) { + !this._last_tex || this._last_tex.height == a.height && this._last_tex.type == a.type || (this._last_tex = null); + var b = this.getInputOrProperty("yaw"); + this._last_tex = GL.Texture.cubemapToTexture2D(a, a.height, this._last_tex, !0, b); + this.setOutputData(0, this._last_tex); + } + } + }, G.registerNodeType("texture/cubemapToTexture2D", R)); +})(this); +(function(v) { + var c = v.LiteGraph, r = v.LGraphTexture; + if ("undefined" != typeof GL) { + var m = function() { + this.addInput("Tex.", "Texture"); + this.addInput("intensity", "number"); + this.addOutput("Texture", "Texture"); + this.properties = {intensity:1, invert:!1, precision:r.DEFAULT}; + m._shader || (m._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, m.pixel_shader)); + }, h = function() { + this.addInput("Texture", "Texture"); + this.addInput("value1", "number"); + this.addInput("value2", "number"); + this.addOutput("Texture", "Texture"); + this.properties = {fx:"halftone", value1:1, value2:1, precision:r.DEFAULT}; + }, q = function() { + this.addInput("Texture", "Texture"); + this.addInput("Blurred", "Texture"); + this.addInput("Mask", "Texture"); + this.addInput("Threshold", "number"); + this.addOutput("Texture", "Texture"); + this.properties = {shape:"", size:10, alpha:1.0, threshold:1.0, high_precision:!1}; + }, l = function() { + this.addInput("Texture", "Texture"); + this.addInput("Aberration", "number"); + this.addInput("Distortion", "number"); + this.addInput("Blur", "number"); + this.addOutput("Texture", "Texture"); + this.properties = {aberration:1.0, distortion:1.0, blur:1.0, precision:r.DEFAULT}; + l._shader || (l._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, l.pixel_shader), l._texture = new GL.Texture(3, 1, {format:gl.RGB, wrap:gl.CLAMP_TO_EDGE, magFilter:gl.LINEAR, minFilter:gl.LINEAR, pixel_data:[255, 0, 0, 0, 255, 0, 0, 0, 255]})); + }; + l.title = "Lens"; + l.desc = "Camera Lens distortion"; + l.widgets_info = {precision:{widget:"combo", values:r.MODE_VALUES}}; + l.prototype.onExecute = function() { + var c = this.getInputData(0); + if (this.properties.precision === r.PASS_THROUGH) { + this.setOutputData(0, c); + } else { + if (c) { + this._tex = r.getTargetTexture(c, this._tex, this.properties.precision); + var h = this.properties.aberration; + this.isInputConnected(1) && (h = this.getInputData(1), this.properties.aberration = h); + var m = this.properties.distortion; + this.isInputConnected(2) && (m = this.getInputData(2), this.properties.distortion = m); + var q = this.properties.blur; + this.isInputConnected(3) && (q = this.getInputData(3), this.properties.blur = q); + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var v = Mesh.getScreenQuad(), e = l._shader; + this._tex.drawTo(function() { + c.bind(0); + e.uniforms({u_texture:0, u_aberration:h, u_distortion:m, u_blur:q}).draw(v); + }); + this.setOutputData(0, this._tex); + } + } + }; + l.pixel_shader = "precision highp float;\n\r\n\t\t\tprecision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform vec2 u_camera_planes;\n\r\n\t\t\tuniform float u_aberration;\n\r\n\t\t\tuniform float u_distortion;\n\r\n\t\t\tuniform float u_blur;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec2 coord = v_coord;\n\r\n\t\t\t\tfloat dist = distance(vec2(0.5), coord);\n\r\n\t\t\t\tvec2 dist_coord = coord - vec2(0.5);\n\r\n\t\t\t\tfloat percent = 1.0 + ((0.5 - dist) / 0.5) * u_distortion;\n\r\n\t\t\t\tdist_coord *= percent;\n\r\n\t\t\t\tcoord = dist_coord + vec2(0.5);\n\r\n\t\t\t\tvec4 color = texture2D(u_texture,coord, u_blur * dist);\n\r\n\t\t\t\tcolor.r = texture2D(u_texture,vec2(0.5) + dist_coord * (1.0+0.01*u_aberration), u_blur * dist ).r;\n\r\n\t\t\t\tcolor.b = texture2D(u_texture,vec2(0.5) + dist_coord * (1.0-0.01*u_aberration), u_blur * dist ).b;\n\r\n\t\t\t\tgl_FragColor = color;\n\r\n\t\t\t}\n\r\n\t\t\t"; + c.registerNodeType("fx/lens", l); + v.LGraphFXLens = l; + q.title = "Bokeh"; + q.desc = "applies an Bokeh effect"; + q.widgets_info = {shape:{widget:"texture"}}; + q.prototype.onExecute = function() { + var c = this.getInputData(0), h = this.getInputData(1), l = this.getInputData(2); + if (c && l && this.properties.shape) { + h || (h = c); + var m = r.getTexture(this.properties.shape); + if (m) { + var v = this.properties.threshold; + this.isInputConnected(3) && (v = this.getInputData(3), this.properties.threshold = v); + var e = gl.UNSIGNED_BYTE; + this.properties.high_precision && (e = gl.half_float_ext ? gl.HALF_FLOAT_OES : gl.FLOAT); + this._temp_texture && this._temp_texture.type == e && this._temp_texture.width == c.width && this._temp_texture.height == c.height || (this._temp_texture = new GL.Texture(c.width, c.height, {type:e, format:gl.RGBA, filter:gl.LINEAR})); + var B = q._first_shader; + B || (B = q._first_shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, q._first_pixel_shader)); + var E = q._second_shader; + E || (E = q._second_shader = new GL.Shader(q._second_vertex_shader, q._second_pixel_shader)); + var u = this._points_mesh; + u && u._width == c.width && u._height == c.height && 2 == u._spacing || (u = this.createPointsMesh(c.width, c.height, 2)); + var H = Mesh.getScreenQuad(), n = this.properties.size, p = this.properties.alpha; + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.BLEND); + this._temp_texture.drawTo(function() { + c.bind(0); + h.bind(1); + l.bind(2); + B.uniforms({u_texture:0, u_texture_blur:1, u_mask:2, u_texsize:[c.width, c.height]}).draw(H); + }); + this._temp_texture.drawTo(function() { + gl.enable(gl.BLEND); + gl.blendFunc(gl.ONE, gl.ONE); + c.bind(0); + m.bind(3); + E.uniforms({u_texture:0, u_mask:2, u_shape:3, u_alpha:p, u_threshold:v, u_pointSize:n, u_itexsize:[1.0 / c.width, 1.0 / c.height]}).draw(u, gl.POINTS); + }); + this.setOutputData(0, this._temp_texture); + } + } else { + this.setOutputData(0, c); + } + }; + q.prototype.createPointsMesh = function(c, h, l) { + for (var m = Math.round(c / l), q = Math.round(h / l), e = new Float32Array(m * q * 2), r = -1, x = 2 / c * l, u = 2 / h * l, v = 0; v < q; ++v) { + for (var n = -1, p = 0; p < m; ++p) { + var k = v * m * 2 + 2 * p; + e[k] = n; + e[k + 1] = r; + n += x; + } + r += u; + } + this._points_mesh = GL.Mesh.load({vertices2D:e}); + this._points_mesh._width = c; + this._points_mesh._height = h; + this._points_mesh._spacing = l; + return this._points_mesh; + }; + q._first_pixel_shader = "precision highp float;\n\r\n\t\t\tprecision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform sampler2D u_texture_blur;\n\r\n\t\t\tuniform sampler2D u_mask;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, v_coord);\n\r\n\t\t\t\tvec4 blurred_color = texture2D(u_texture_blur, v_coord);\n\r\n\t\t\t\tfloat mask = texture2D(u_mask, v_coord).x;\n\r\n\t\t\t gl_FragColor = mix(color, blurred_color, mask);\n\r\n\t\t\t}\n\r\n\t\t\t"; + q._second_vertex_shader = "precision highp float;\n\r\n\t\t\tattribute vec2 a_vertex2D;\n\r\n\t\t\tvarying vec4 v_color;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform sampler2D u_mask;\n\r\n\t\t\tuniform vec2 u_itexsize;\n\r\n\t\t\tuniform float u_pointSize;\n\r\n\t\t\tuniform float u_threshold;\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec2 coord = a_vertex2D * 0.5 + 0.5;\n\r\n\t\t\t\tv_color = texture2D( u_texture, coord );\n\r\n\t\t\t\tv_color += texture2D( u_texture, coord + vec2(u_itexsize.x, 0.0) );\n\r\n\t\t\t\tv_color += texture2D( u_texture, coord + vec2(0.0, u_itexsize.y));\n\r\n\t\t\t\tv_color += texture2D( u_texture, coord + u_itexsize);\n\r\n\t\t\t\tv_color *= 0.25;\n\r\n\t\t\t\tfloat mask = texture2D(u_mask, coord).x;\n\r\n\t\t\t\tfloat luminance = length(v_color) * mask;\n\r\n\t\t\t\t/*luminance /= (u_pointSize*u_pointSize)*0.01 */;\n\r\n\t\t\t\tluminance -= u_threshold;\n\r\n\t\t\t\tif(luminance < 0.0)\n\r\n\t\t\t\t{\n\r\n\t\t\t\t\tgl_Position.x = -100.0;\n\r\n\t\t\t\t\treturn;\n\r\n\t\t\t\t}\n\r\n\t\t\t\tgl_PointSize = u_pointSize;\n\r\n\t\t\t\tgl_Position = vec4(a_vertex2D,0.0,1.0);\n\r\n\t\t\t}\n\r\n\t\t\t"; + q._second_pixel_shader = "precision highp float;\n\r\n\t\t\tvarying vec4 v_color;\n\r\n\t\t\tuniform sampler2D u_shape;\n\r\n\t\t\tuniform float u_alpha;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec4 color = texture2D( u_shape, gl_PointCoord );\n\r\n\t\t\t\tcolor *= v_color * u_alpha;\n\r\n\t\t\t\tgl_FragColor = color;\n\r\n\t\t\t}\n"; + c.registerNodeType("fx/bokeh", q); + v.LGraphFXBokeh = q; + h.title = "FX"; + h.desc = "applies an FX from a list"; + h.widgets_info = {fx:{widget:"combo", values:["halftone", "pixelate", "lowpalette", "noise", "gamma"]}, precision:{widget:"combo", values:r.MODE_VALUES}}; + h.shaders = {}; + h.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var c = this.getInputData(0); + if (this.properties.precision === r.PASS_THROUGH) { + this.setOutputData(0, c); + } else { + if (c) { + this._tex = r.getTargetTexture(c, this._tex, this.properties.precision); + var l = this.properties.value1; + this.isInputConnected(1) && (l = this.getInputData(1), this.properties.value1 = l); + var m = this.properties.value2; + this.isInputConnected(2) && (m = this.getInputData(2), this.properties.value2 = m); + var q = this.properties.fx, z = h.shaders[q]; + if (!z) { + var e = h["pixel_shader_" + q]; + if (!e) { + return; + } + z = h.shaders[q] = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, e); + } + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var B = Mesh.getScreenQuad(); + var E = v.LS && LS.Renderer._current_camera ? [LS.Renderer._current_camera.near, LS.Renderer._current_camera.far] : [1, 100]; + var u = null; + "noise" == q && (u = r.getNoiseTexture()); + this._tex.drawTo(function() { + c.bind(0); + "noise" == q && u.bind(1); + z.uniforms({u_texture:0, u_noise:1, u_size:[c.width, c.height], u_rand:[Math.random(), Math.random()], u_value1:l, u_value2:m, u_camera_planes:E}).draw(B); + }); + this.setOutputData(0, this._tex); + } + } + } + }; + h.pixel_shader_halftone = "precision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform vec2 u_camera_planes;\n\r\n\t\t\tuniform vec2 u_size;\n\r\n\t\t\tuniform float u_value1;\n\r\n\t\t\tuniform float u_value2;\n\r\n\t\t\t\n\r\n\t\t\tfloat pattern() {\n\r\n\t\t\t\tfloat s = sin(u_value1 * 3.1415), c = cos(u_value1 * 3.1415);\n\r\n\t\t\t\tvec2 tex = v_coord * u_size.xy;\n\r\n\t\t\t\tvec2 point = vec2(\n\r\n\t\t\t\t c * tex.x - s * tex.y ,\n\r\n\t\t\t\t s * tex.x + c * tex.y \n\r\n\t\t\t\t) * u_value2;\n\r\n\t\t\t\treturn (sin(point.x) * sin(point.y)) * 4.0;\n\r\n\t\t\t}\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, v_coord);\n\r\n\t\t\t\tfloat average = (color.r + color.g + color.b) / 3.0;\n\r\n\t\t\t\tgl_FragColor = vec4(vec3(average * 10.0 - 5.0 + pattern()), color.a);\n\r\n\t\t\t}\n"; + h.pixel_shader_pixelate = "precision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform vec2 u_camera_planes;\n\r\n\t\t\tuniform vec2 u_size;\n\r\n\t\t\tuniform float u_value1;\n\r\n\t\t\tuniform float u_value2;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec2 coord = vec2( floor(v_coord.x * u_value1) / u_value1, floor(v_coord.y * u_value2) / u_value2 );\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, coord);\n\r\n\t\t\t\tgl_FragColor = color;\n\r\n\t\t\t}\n"; + h.pixel_shader_lowpalette = "precision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform vec2 u_camera_planes;\n\r\n\t\t\tuniform vec2 u_size;\n\r\n\t\t\tuniform float u_value1;\n\r\n\t\t\tuniform float u_value2;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, v_coord);\n\r\n\t\t\t\tgl_FragColor = floor(color * u_value1) / u_value1;\n\r\n\t\t\t}\n"; + h.pixel_shader_noise = "precision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform sampler2D u_noise;\n\r\n\t\t\tuniform vec2 u_size;\n\r\n\t\t\tuniform float u_value1;\n\r\n\t\t\tuniform float u_value2;\n\r\n\t\t\tuniform vec2 u_rand;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, v_coord);\n\r\n\t\t\t\tvec3 noise = texture2D(u_noise, v_coord * vec2(u_size.x / 512.0, u_size.y / 512.0) + u_rand).xyz - vec3(0.5);\n\r\n\t\t\t\tgl_FragColor = vec4( color.xyz + noise * u_value1, color.a );\n\r\n\t\t\t}\n"; + h.pixel_shader_gamma = "precision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform float u_value1;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, v_coord);\n\r\n\t\t\t\tfloat gamma = 1.0 / u_value1;\n\r\n\t\t\t\tgl_FragColor = vec4( pow( color.xyz, vec3(gamma) ), color.a );\n\r\n\t\t\t}\n"; + c.registerNodeType("fx/generic", h); + v.LGraphFXGeneric = h; + m.title = "Vigneting"; + m.desc = "Vigneting"; + m.widgets_info = {precision:{widget:"combo", values:r.MODE_VALUES}}; + m.prototype.onExecute = function() { + var c = this.getInputData(0); + if (this.properties.precision === r.PASS_THROUGH) { + this.setOutputData(0, c); + } else { + if (c) { + this._tex = r.getTargetTexture(c, this._tex, this.properties.precision); + var h = this.properties.intensity; + this.isInputConnected(1) && (h = this.getInputData(1), this.properties.intensity = h); + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var l = Mesh.getScreenQuad(), q = m._shader, v = this.properties.invert; + this._tex.drawTo(function() { + c.bind(0); + q.uniforms({u_texture:0, u_intensity:h, u_isize:[1 / c.width, 1 / c.height], u_invert:v ? 1 : 0}).draw(l); + }); + this.setOutputData(0, this._tex); + } + } + }; + m.pixel_shader = "precision highp float;\n\r\n\t\t\tprecision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform float u_intensity;\n\r\n\t\t\tuniform int u_invert;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tfloat luminance = 1.0 - length( v_coord - vec2(0.5) ) * 1.414;\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, v_coord);\n\r\n\t\t\t\tif(u_invert == 1)\n\r\n\t\t\t\t\tluminance = 1.0 - luminance;\n\r\n\t\t\t\tluminance = mix(1.0, luminance, u_intensity);\n\r\n\t\t\t gl_FragColor = vec4( luminance * color.xyz, color.a);\n\r\n\t\t\t}\n\r\n\t\t\t"; + c.registerNodeType("fx/vigneting", m); + v.LGraphFXVigneting = m; + } +})(this); +(function(v) { + function c(c) { + this.cmd = this.channel = 0; + this.data = new Uint32Array(3); + c && this.setup(c); + } + function r(c, e) { + navigator.requestMIDIAccess ? (this.on_ready = c, this.state = {note:[], cc:[]}, this.input_ports = null, this.input_ports_info = [], this.output_ports = null, this.output_ports_info = [], navigator.requestMIDIAccess().then(this.onMIDISuccess.bind(this), this.onMIDIFailure.bind(this))) : (this.error = "not suppoorted", e ? e("Not supported") : console.error("MIDI NOT SUPPORTED, enable by chrome://flags")); + } + function m() { + this.addOutput("on_midi", u.EVENT); + this.addOutput("out", "midi"); + this.properties = {port:0}; + this._current_midi_event = this._last_midi_event = null; + this.boxcolor = "#AAA"; + this._last_time = 0; + var c = this; + new r(function(e) { + c._midi = e; + if (c._waiting) { + c.onStart(); + } + c._waiting = !1; + }); + } + function h() { + this.addInput("send", u.EVENT); + this.properties = {port:0}; + var c = this; + new r(function(e) { + c._midi = e; + c.widget.options.values = c.getMIDIOutputs(); + }); + this.widget = this.addWidget("combo", "Device", this.properties.port, {property:"port", values:this.getMIDIOutputs.bind(this)}); + this.size = [340, 60]; + } + function q() { + this.addInput("on_midi", u.EVENT); + this._str = ""; + this.size = [200, 40]; + } + function l() { + this.properties = {channel:-1, cmd:-1, min_value:-1, max_value:-1}; + var c = this; + this._learning = !1; + this.addWidget("button", "Learn", "", function() { + c._learning = !0; + c.boxcolor = "#FA3"; + }); + this.addInput("in", u.EVENT); + this.addOutput("on_midi", u.EVENT); + this.boxcolor = "#AAA"; + } + function A() { + this.properties = {channel:0, cmd:144, value1:1, value2:1}; + this.addInput("send", u.EVENT); + this.addInput("assign", u.EVENT); + this.addOutput("on_midi", u.EVENT); + this.midi_event = new c; + this.gate = !1; + } + function y() { + this.properties = {cc:1, value:0}; + this.addOutput("value", "number"); + } + function x() { + this.addInput("generate", u.ACTION); + this.addInput("scale", "string"); + this.addInput("octave", "number"); + this.addOutput("note", u.EVENT); + this.properties = {notes:"A,A#,B,C,C#,D,D#,E,F,F#,G,G#", octave:2, duration:0.5, mode:"sequence"}; + this.notes_pitches = x.processScale(this.properties.notes); + this.sequence_index = 0; + } + function F() { + this.properties = {amount:0}; + this.addInput("in", u.ACTION); + this.addInput("amount", "number"); + this.addOutput("out", u.EVENT); + this.midi_event = new c; + } + function z() { + this.properties = {scale:"A,A#,B,C,C#,D,D#,E,F,F#,G,G#"}; + this.addInput("note", u.ACTION); + this.addInput("scale", "string"); + this.addOutput("out", u.EVENT); + this.valid_notes = Array(12); + this.offset_notes = Array(12); + this.processScale(this.properties.scale); + } + function e() { + this.properties = {url:"", autoplay:!0}; + this.addInput("play", u.ACTION); + this.addInput("pause", u.ACTION); + this.addOutput("note", u.EVENT); + this._midi = null; + this._current_time = 0; + this._playing = !1; + "undefined" == typeof MidiParser && (console.error("midi-parser.js not included, LGMidiPlay requires that library: https://raw.githubusercontent.com/colxi/midi-parser-js/master/src/main.js"), this.boxcolor = "red"); + } + function B() { + this.properties = {volume:0.5, duration:1}; + this.addInput("note", u.ACTION); + this.addInput("volume", "number"); + this.addInput("duration", "number"); + this.addOutput("note", u.EVENT); + "undefined" == typeof AudioSynth ? (console.error("Audiosynth.js not included, LGMidiPlay requires that library"), this.boxcolor = "red") : this.instrument = (this.synth = new AudioSynth).createInstrument("piano"); + } + function E() { + this.properties = {num_octaves:2, start_octave:2}; + this.addInput("note", u.ACTION); + this.addInput("reset", u.ACTION); + this.addOutput("note", u.EVENT); + this.size = [400, 100]; + this.keys = []; + this._last_key = -1; + } + var u = v.LiteGraph; + u.MIDIEvent = c; + c.prototype.fromJSON = function(c) { + this.setup(c.data); + }; + c.prototype.setup = function(e) { + var n = e; + e.constructor === Object && (n = e.data); + this.data.set(n); + this.status = e = n[0]; + n = e & 240; + this.cmd = 240 <= e ? e : n; + this.cmd == c.NOTEON && 0 == this.velocity && (this.cmd = c.NOTEOFF); + this.cmd_str = c.commands[this.cmd] || ""; + if (n >= c.NOTEON || n <= c.NOTEOFF) { + this.channel = e & 15; + } + }; + Object.defineProperty(c.prototype, "velocity", {get:function() { + return this.cmd == c.NOTEON ? this.data[2] : -1; + }, set:function(c) { + this.data[2] = c; + }, enumerable:!0}); + c.notes = "A A# B C C# D D# E F F# G G#".split(" "); + c.note_to_index = {A:0, "A#":1, B:2, C:3, "C#":4, D:5, "D#":6, E:7, F:8, "F#":9, G:10, "G#":11}; + Object.defineProperty(c.prototype, "note", {get:function() { + return this.cmd != c.NOTEON ? -1 : c.toNoteString(this.data[1], !0); + }, set:function(c) { + throw "notes cannot be assigned this way, must modify the data[1]"; + }, enumerable:!0}); + Object.defineProperty(c.prototype, "octave", {get:function() { + return this.cmd != c.NOTEON ? -1 : Math.floor((this.data[1] - 24) / 12 + 1); + }, set:function(c) { + throw "octave cannot be assigned this way, must modify the data[1]"; + }, enumerable:!0}); + c.prototype.getPitch = function() { + return 440 * Math.pow(2, (this.data[1] - 69) / 12); + }; + c.computePitch = function(c) { + return 440 * Math.pow(2, (c - 69) / 12); + }; + c.prototype.getCC = function() { + return this.data[1]; + }; + c.prototype.getCCValue = function() { + return this.data[2]; + }; + c.prototype.getPitchBend = function() { + return this.data[1] + (this.data[2] << 7) - 8192; + }; + c.computePitchBend = function(c, e) { + return c + (e << 7) - 8192; + }; + c.prototype.setCommandFromString = function(e) { + this.cmd = c.computeCommandFromString(e); + }; + c.computeCommandFromString = function(e) { + if (!e) { + return 0; + } + if (e && e.constructor === Number) { + return e; + } + e = e.toUpperCase(); + switch(e) { + case "NOTE ON": + case "NOTEON": + return c.NOTEON; + case "NOTE OFF": + case "NOTEOFF": + return c.NOTEON; + case "KEY PRESSURE": + case "KEYPRESSURE": + return c.KEYPRESSURE; + case "CONTROLLER CHANGE": + case "CONTROLLERCHANGE": + case "CC": + return c.CONTROLLERCHANGE; + case "PROGRAM CHANGE": + case "PROGRAMCHANGE": + case "PC": + return c.PROGRAMCHANGE; + case "CHANNEL PRESSURE": + case "CHANNELPRESSURE": + return c.CHANNELPRESSURE; + case "PITCH BEND": + case "PITCHBEND": + return c.PITCHBEND; + case "TIME TICK": + case "TIMETICK": + return c.TIMETICK; + default: + return Number(e); + } + }; + c.toNoteString = function(e, h) { + e = Math.round(e); + var k = Math.floor((e - 24) / 12 + 1); + e = (e - 21) % 12; + 0 > e && (e = 12 + e); + return c.notes[e] + (h ? "" : k); + }; + c.NoteStringToPitch = function(e) { + e = e.toUpperCase(); + var n = e[0], k = 4; + "#" == e[1] ? (n += "#", 2 < e.length && (k = Number(e[2]))) : 1 < e.length && (k = Number(e[1])); + e = c.note_to_index[n]; + return null == e ? null : 12 * (k - 1) + e + 21; + }; + c.prototype.toString = function() { + var e = "" + this.channel + ". "; + switch(this.cmd) { + case c.NOTEON: + e += "NOTEON " + c.toNoteString(this.data[1]); + break; + case c.NOTEOFF: + e += "NOTEOFF " + c.toNoteString(this.data[1]); + break; + case c.CONTROLLERCHANGE: + e += "CC " + this.data[1] + " " + this.data[2]; + break; + case c.PROGRAMCHANGE: + e += "PC " + this.data[1]; + break; + case c.PITCHBEND: + e += "PITCHBEND " + this.getPitchBend(); + break; + case c.KEYPRESSURE: + e += "KEYPRESS " + this.data[1]; + } + return e; + }; + c.prototype.toHexString = function() { + for (var c = "", e = 0; e < this.data.length; e++) { + c += this.data[e].toString(16) + " "; + } + }; + c.prototype.toJSON = function() { + return {data:[this.data[0], this.data[1], this.data[2]], object_class:"MIDIEvent"}; + }; + c.NOTEOFF = 128; + c.NOTEON = 144; + c.KEYPRESSURE = 160; + c.CONTROLLERCHANGE = 176; + c.PROGRAMCHANGE = 192; + c.CHANNELPRESSURE = 208; + c.PITCHBEND = 224; + c.TIMETICK = 248; + c.commands = {128:"note off", 144:"note on", 160:"key pressure", 176:"controller change", 192:"program change", 208:"channel pressure", 224:"pitch bend", 240:"system", 242:"Song pos", 243:"Song select", 246:"Tune request", 248:"time tick", 250:"Start Song", 251:"Continue Song", 252:"Stop Song", 254:"Sensing", 255:"Reset"}; + c.commands_short = {128:"NOTEOFF", 144:"NOTEOFF", 160:"KEYP", 176:"CC", 192:"PC", 208:"CP", 224:"PB", 240:"SYS", 242:"POS", 243:"SELECT", 246:"TUNEREQ", 248:"TT", 250:"START", 251:"CONTINUE", 252:"STOP", 254:"SENS", 255:"RESET"}; + c.commands_reversed = {}; + for (var H in c.commands) { + c.commands_reversed[c.commands[H]] = H; + } + r.input = null; + r.MIDIEvent = c; + r.prototype.onMIDISuccess = function(c) { + console.log("MIDI ready!"); + console.log(c); + this.midi = c; + this.updatePorts(); + if (this.on_ready) { + this.on_ready(this); + } + }; + r.prototype.updatePorts = function() { + var c = this.midi; + this.input_ports = c.inputs; + this.input_ports_info = []; + this.output_ports = c.outputs; + this.output_ports_info = []; + c = 0; + for (var e = this.input_ports.values(), k = e.next(); k && !1 === k.done;) { + k = k.value, this.input_ports_info.push(k), console.log("Input port [type:'" + k.type + "'] id:'" + k.id + "' manufacturer:'" + k.manufacturer + "' name:'" + k.name + "' version:'" + k.version + "'"), c++, k = e.next(); + } + this.num_input_ports = c; + c = 0; + e = this.output_ports.values(); + for (k = e.next(); k && !1 === k.done;) { + k = k.value, this.output_ports_info.push(k), console.log("Output port [type:'" + k.type + "'] id:'" + k.id + "' manufacturer:'" + k.manufacturer + "' name:'" + k.name + "' version:'" + k.version + "'"), c++, k = e.next(); + } + this.num_output_ports = c; + }; + r.prototype.onMIDIFailure = function(c) { + console.error("Failed to get MIDI access - " + c); + }; + r.prototype.openInputPort = function(e, h) { + e = this.input_ports.get("input-" + e); + if (!e) { + return !1; + } + r.input = this; + var k = this; + e.onmidimessage = function(a) { + var b = new c(a.data); + k.updateState(b); + h && h(a.data, b); + if (r.on_message) { + r.on_message(a.data, b); + } + }; + console.log("port open: ", e); + return !0; + }; + r.parseMsg = function(c) { + }; + r.prototype.updateState = function(e) { + switch(e.cmd) { + case c.NOTEON: + this.state.note[e.value1 | 0] = e.value2; + break; + case c.NOTEOFF: + this.state.note[e.value1 | 0] = 0; + break; + case c.CONTROLLERCHANGE: + this.state.cc[e.getCC()] = e.getCCValue(); + } + }; + r.prototype.sendMIDI = function(e, h) { + h && (e = this.output_ports_info[e]) && (r.output = this, h.constructor === c ? e.send(h.data) : e.send(h)); + }; + m.MIDIInterface = r; + m.title = "MIDI Input"; + m.desc = "Reads MIDI from a input port"; + m.color = "#243"; + m.prototype.getPropertyInfo = function(c) { + if (this._midi && "port" == c) { + c = {}; + for (var e = 0; e < this._midi.input_ports_info.length; ++e) { + var k = this._midi.input_ports_info[e]; + c[e] = e + ".- " + k.name + " version:" + k.version; + } + return {type:"enum", values:c}; + } + }; + m.prototype.onStart = function() { + this._midi ? this._midi.openInputPort(this.properties.port, this.onMIDIEvent.bind(this)) : this._waiting = !0; + }; + m.prototype.onMIDIEvent = function(e, h) { + this._last_midi_event = h; + this.boxcolor = "#AFA"; + this._last_time = u.getTime(); + this.trigger("on_midi", h); + h.cmd == c.NOTEON ? this.trigger("on_noteon", h) : h.cmd == c.NOTEOFF ? this.trigger("on_noteoff", h) : h.cmd == c.CONTROLLERCHANGE ? this.trigger("on_cc", h) : h.cmd == c.PROGRAMCHANGE ? this.trigger("on_pc", h) : h.cmd == c.PITCHBEND && this.trigger("on_pitchbend", h); + }; + m.prototype.onDrawBackground = function(c) { + this.boxcolor = "#AAA"; + if (!this.flags.collapsed && this._last_midi_event) { + c.fillStyle = "white"; + var e = u.getTime(); + e = 1.0 - Math.max(0, 0.001 * (e - this._last_time)); + if (0 < e) { + var k = c.globalAlpha; + c.globalAlpha *= e; + c.font = "12px Tahoma"; + c.fillText(this._last_midi_event.toString(), 2, 0.5 * this.size[1] + 3); + c.globalAlpha = k; + } + } + }; + m.prototype.onExecute = function() { + if (this.outputs) { + for (var c = this._last_midi_event, e = 0; e < this.outputs.length; ++e) { + switch(this.outputs[e].name) { + case "midi": + var k = this._midi; + break; + case "last_midi": + k = c; + break; + default: + continue; + } + this.setOutputData(e, k); + } + } + }; + m.prototype.onGetOutputs = function() { + return [["last_midi", "midi"], ["on_midi", u.EVENT], ["on_noteon", u.EVENT], ["on_noteoff", u.EVENT], ["on_cc", u.EVENT], ["on_pc", u.EVENT], ["on_pitchbend", u.EVENT]]; + }; + u.registerNodeType("midi/input", m); + h.MIDIInterface = r; + h.title = "MIDI Output"; + h.desc = "Sends MIDI to output channel"; + h.color = "#243"; + h.prototype.onGetPropertyInfo = function(c) { + if (this._midi && "port" == c) { + return {type:"enum", values:this.getMIDIOutputs()}; + } + }; + h.default_ports = {0:"unknown"}; + h.prototype.getMIDIOutputs = function() { + var c = {}; + if (!this._midi) { + return h.default_ports; + } + if (this._midi.output_ports_info) { + for (var e = 0; e < this._midi.output_ports_info.length; ++e) { + var k = this._midi.output_ports_info[e]; + k && (c[e] = e + ".- " + k.name + " version:" + k.version); + } + } + return c; + }; + h.prototype.onAction = function(c, e) { + this._midi && ("send" == c && this._midi.sendMIDI(this.properties.port, e), this.trigger("midi", e)); + }; + h.prototype.onGetInputs = function() { + return [["send", u.ACTION]]; + }; + h.prototype.onGetOutputs = function() { + return [["on_midi", u.EVENT]]; + }; + u.registerNodeType("midi/output", h); + q.title = "MIDI Show"; + q.desc = "Shows MIDI in the graph"; + q.color = "#243"; + q.prototype.getTitle = function() { + return this.flags.collapsed ? this._str : this.title; + }; + q.prototype.onAction = function(e, h) { + h && (this._str = h.constructor === c ? h.toString() : "???"); + }; + q.prototype.onDrawForeground = function(c) { + this._str && !this.flags.collapsed && (c.font = "30px Arial", c.fillText(this._str, 10, 0.8 * this.size[1])); + }; + q.prototype.onGetInputs = function() { + return [["in", u.ACTION]]; + }; + q.prototype.onGetOutputs = function() { + return [["on_midi", u.EVENT]]; + }; + u.registerNodeType("midi/show", q); + l.title = "MIDI Filter"; + l.desc = "Filters MIDI messages"; + l.color = "#243"; + l["@cmd"] = {type:"enum", title:"Command", values:c.commands_reversed}; + l.prototype.getTitle = function() { + var e = -1 == this.properties.cmd ? "Nothing" : c.commands_short[this.properties.cmd] || "Unknown"; + -1 != this.properties.min_value && -1 != this.properties.max_value && (e += " " + (this.properties.min_value == this.properties.max_value ? this.properties.max_value : this.properties.min_value + ".." + this.properties.max_value)); + return "Filter: " + e; + }; + l.prototype.onPropertyChanged = function(e, h) { + "cmd" == e && (e = Number(h), isNaN(e) && (e = c.commands[h] || 0), this.properties.cmd = e); + }; + l.prototype.onAction = function(e, h) { + if (h && h.constructor === c) { + if (this._learning) { + this._learning = !1, this.boxcolor = "#AAA", this.properties.channel = h.channel, this.properties.cmd = h.cmd, this.properties.min_value = this.properties.max_value = h.data[1]; + } else { + if (-1 != this.properties.channel && h.channel != this.properties.channel || -1 != this.properties.cmd && h.cmd != this.properties.cmd || -1 != this.properties.min_value && h.data[1] < this.properties.min_value || -1 != this.properties.max_value && h.data[1] > this.properties.max_value) { + return; + } + } + this.trigger("on_midi", h); + } + }; + u.registerNodeType("midi/filter", l); + A.title = "MIDIEvent"; + A.desc = "Create a MIDI Event"; + A.color = "#243"; + A.prototype.onAction = function(e, h) { + "assign" == e ? (this.properties.channel = h.channel, this.properties.cmd = h.cmd, this.properties.value1 = h.data[1], this.properties.value2 = h.data[2], h.cmd == c.NOTEON ? this.gate = !0 : h.cmd == c.NOTEOFF && (this.gate = !1)) : (h = this.midi_event, h.channel = this.properties.channel, this.properties.cmd && this.properties.cmd.constructor === String ? h.setCommandFromString(this.properties.cmd) : h.cmd = this.properties.cmd, h.data[0] = h.cmd | h.channel, h.data[1] = Number(this.properties.value1), + h.data[2] = Number(this.properties.value2), this.trigger("on_midi", h)); + }; + A.prototype.onExecute = function() { + var e = this.properties; + if (this.inputs) { + for (var h = 0; h < this.inputs.length; ++h) { + var k = this.inputs[h]; + if (-1 != k.link) { + switch(k.name) { + case "note": + k = this.getInputData(h); + null != k && (k.constructor === String && (k = c.NoteStringToPitch(k)), this.properties.value1 = (k | 0) % 255); + break; + case "cmd": + k = this.getInputData(h); + null != k && (this.properties.cmd = k); + break; + case "value1": + k = this.getInputData(h); + null != k && (this.properties.value1 = Math.clamp(k | 0, 0, 127)); + break; + case "value2": + k = this.getInputData(h), null != k && (this.properties.value2 = Math.clamp(k | 0, 0, 127)); + } + } + } + } + if (this.outputs) { + for (h = 0; h < this.outputs.length; ++h) { + switch(this.outputs[h].name) { + case "midi": + k = new c; + k.setup([e.cmd, e.value1, e.value2]); + k.channel = e.channel; + break; + case "command": + k = e.cmd; + break; + case "cc": + k = e.value1; + break; + case "cc_value": + k = e.value2; + break; + case "note": + k = e.cmd == c.NOTEON || e.cmd == c.NOTEOFF ? e.value1 : null; + break; + case "velocity": + k = e.cmd == c.NOTEON ? e.value2 : null; + break; + case "pitch": + k = e.cmd == c.NOTEON ? c.computePitch(e.value1) : null; + break; + case "pitchbend": + k = e.cmd == c.PITCHBEND ? c.computePitchBend(e.value1, e.value2) : null; + break; + case "gate": + k = this.gate; + break; + default: + continue; + } + null !== k && this.setOutputData(h, k); + } + } + }; + A.prototype.onPropertyChanged = function(e, h) { + "cmd" == e && (this.properties.cmd = c.computeCommandFromString(h)); + }; + A.prototype.onGetInputs = function() { + return [["cmd", "number"], ["note", "number"], ["value1", "number"], ["value2", "number"]]; + }; + A.prototype.onGetOutputs = function() { + return [["midi", "midi"], ["on_midi", u.EVENT], ["command", "number"], ["note", "number"], ["velocity", "number"], ["cc", "number"], ["cc_value", "number"], ["pitch", "number"], ["gate", "bool"], ["pitchbend", "number"]]; + }; + u.registerNodeType("midi/event", A); + y.title = "MIDICC"; + y.desc = "gets a Controller Change"; + y.color = "#243"; + y.prototype.onExecute = function() { + r.input && (this.properties.value = r.input.state.cc[this.properties.cc]); + this.setOutputData(0, this.properties.value); + }; + u.registerNodeType("midi/cc", y); + x.title = "MIDI Generator"; + x.desc = "Generates a random MIDI note"; + x.color = "#243"; + x.processScale = function(e) { + e = e.split(","); + for (var h = 0; h < e.length; ++h) { + var k = e[h]; + e[h] = 2 == k.length && "#" != k[1] || 2 < k.length ? -u.MIDIEvent.NoteStringToPitch(k) : c.note_to_index[k] || 0; + } + return e; + }; + x.prototype.onPropertyChanged = function(c, e) { + "notes" == c && (this.notes_pitches = x.processScale(e)); + }; + x.prototype.onExecute = function() { + var c = this.getInputData(2); + null != c && (this.properties.octave = c); + if (c = this.getInputData(1)) { + this.notes_pitches = x.processScale(c); + } + }; + x.prototype.onAction = function(e, h) { + var k = 0; + h = this.notes_pitches.length; + e = 0; + "sequence" == this.properties.mode ? e = this.sequence_index = (this.sequence_index + 1) % h : "random" == this.properties.mode && (e = Math.floor(Math.random() * h)); + h = this.notes_pitches[e]; + k = 0 <= h ? h + 12 * (this.properties.octave - 1) + 33 : -h; + h = new c; + h.setup([c.NOTEON, k, 10]); + e = this.properties.duration || 1; + this.trigger("note", h); + setTimeout(function() { + var a = new c; + a.setup([c.NOTEOFF, k, 0]); + this.trigger("note", a); + }.bind(this), 1000 * e); + }; + u.registerNodeType("midi/generator", x); + F.title = "MIDI Transpose"; + F.desc = "Transpose a MIDI note"; + F.color = "#243"; + F.prototype.onAction = function(e, h) { + h && h.constructor === c && (h.data[0] == c.NOTEON || h.data[0] == c.NOTEOFF ? (this.midi_event = new c, this.midi_event.setup(h.data), this.midi_event.data[1] = Math.round(this.midi_event.data[1] + this.properties.amount), this.trigger("out", this.midi_event)) : this.trigger("out", h)); + }; + F.prototype.onExecute = function() { + var c = this.getInputData(1); + null != c && (this.properties.amount = c); + }; + u.registerNodeType("midi/transpose", F); + z.title = "MIDI Quantize Pitch"; + z.desc = "Transpose a MIDI note tp fit an scale"; + z.color = "#243"; + z.prototype.onPropertyChanged = function(c, e) { + "scale" == c && this.processScale(e); + }; + z.prototype.processScale = function(c) { + this._current_scale = c; + this.notes_pitches = x.processScale(c); + for (c = 0; 12 > c; ++c) { + this.valid_notes[c] = -1 != this.notes_pitches.indexOf(c); + } + for (c = 0; 12 > c; ++c) { + if (this.valid_notes[c]) { + this.offset_notes[c] = 0; + } else { + for (var e = 1; 12 > e; ++e) { + if (this.valid_notes[(c - e) % 12]) { + this.offset_notes[c] = -e; + break; + } + if (this.valid_notes[(c + e) % 12]) { + this.offset_notes[c] = e; + break; + } + } + } + } + }; + z.prototype.onAction = function(e, h) { + h && h.constructor === c && (h.data[0] == c.NOTEON || h.data[0] == c.NOTEOFF ? (this.midi_event = new c, this.midi_event.setup(h.data), this.midi_event.data[1] += this.offset_notes[c.note_to_index[h.note]], this.trigger("out", this.midi_event)) : this.trigger("out", h)); + }; + z.prototype.onExecute = function() { + var c = this.getInputData(1); + null != c && c != this._current_scale && this.processScale(c); + }; + u.registerNodeType("midi/quantize", z); + e.title = "MIDI fromFile"; + e.desc = "Plays a MIDI file"; + e.color = "#243"; + e.prototype.onAction = function(c) { + "play" == c ? this.play() : "pause" == c && (this._playing = !this._playing); + }; + e.prototype.onPropertyChanged = function(c, e) { + "url" == c && this.loadMIDIFile(e); + }; + e.prototype.onExecute = function() { + if (this._midi && this._playing) { + this._current_time += this.graph.elapsed_time; + for (var e = 100 * this._current_time, h = 0; h < this._midi.tracks; ++h) { + var k = this._midi.track[h]; + k._last_pos || (k._last_pos = 0, k._time = 0); + var a = k.event[k._last_pos]; + if (a && k._time + a.deltaTime <= e && (k._last_pos++, k._time += a.deltaTime, a.data)) { + k = a.type << 4 + a.channel; + var b = new c; + b.setup([k, a.data[0], a.data[1]]); + this.trigger("note", b); + } + } + } + }; + e.prototype.play = function() { + this._playing = !0; + this._current_time = 0; + if (this._midi) { + for (var c = 0; c < this._midi.tracks; ++c) { + var e = this._midi.track[c]; + e._last_pos = 0; + e._time = 0; + } + } + }; + e.prototype.loadMIDIFile = function(c) { + var e = this; + u.fetchFile(c, "arraybuffer", function(c) { + e.boxcolor = "#AFA"; + e._midi = MidiParser.parse(new Uint8Array(c)); + e.properties.autoplay && e.play(); + }, function(c) { + e.boxcolor = "#FAA"; + e._midi = null; + }); + }; + e.prototype.onDropFile = function(c) { + this.properties.url = ""; + this.loadMIDIFile(c); + }; + u.registerNodeType("midi/fromFile", e); + B.title = "MIDI Play"; + B.desc = "Plays a MIDI note"; + B.color = "#243"; + B.prototype.onAction = function(e, h) { + if (h && h.constructor === c) { + if (this.instrument && h.data[0] == c.NOTEON) { + e = h.note; + if (!e || "undefined" == e || e.constructor !== String) { + return; + } + this.instrument.play(e, h.octave, this.properties.duration, this.properties.volume); + } + this.trigger("note", h); + } + }; + B.prototype.onExecute = function() { + var c = this.getInputData(1); + null != c && (this.properties.volume = c); + c = this.getInputData(2); + null != c && (this.properties.duration = c); + }; + u.registerNodeType("midi/play", B); + E.title = "MIDI Keys"; + E.desc = "Keyboard to play notes"; + E.color = "#243"; + E.keys = [{x:0, w:1, h:1, t:0}, {x:0.75, w:0.5, h:0.6, t:1}, {x:1, w:1, h:1, t:0}, {x:1.75, w:0.5, h:0.6, t:1}, {x:2, w:1, h:1, t:0}, {x:2.75, w:0.5, h:0.6, t:1}, {x:3, w:1, h:1, t:0}, {x:4, w:1, h:1, t:0}, {x:4.75, w:0.5, h:0.6, t:1}, {x:5, w:1, h:1, t:0}, {x:5.75, w:0.5, h:0.6, t:1}, {x:6, w:1, h:1, t:0}]; + E.prototype.onDrawForeground = function(c) { + if (!this.flags.collapsed) { + var e = 12 * this.properties.num_octaves; + this.keys.length = e; + var k = this.size[0] / (7 * this.properties.num_octaves), a = this.size[1]; + c.globalAlpha = 1; + for (var b = 0; 2 > b; b++) { + for (var d = 0; d < e; ++d) { + var g = E.keys[d % 12]; + if (g.t == b) { + var f = 7 * Math.floor(d / 12) * k + g.x * k; + c.fillStyle = 0 == b ? this.keys[d] ? "#CCC" : "white" : this.keys[d] ? "#333" : "black"; + c.fillRect(f + 1, 0, k * g.w - 2, a * g.h); + } + } + } + } + }; + E.prototype.getKeyIndex = function(c) { + for (var e = this.size[0] / (7 * this.properties.num_octaves), k = this.size[1], a = 1; 0 <= a; a--) { + for (var b = 0; b < this.keys.length; ++b) { + var d = E.keys[b % 12]; + if (d.t == a) { + var g = 7 * Math.floor(b / 12) * e + d.x * e, f = e * d.w; + d = k * d.h; + if (!(c[0] < g || c[0] > g + f || c[1] > d)) { + return b; + } + } + } + } + return -1; + }; + E.prototype.onAction = function(e, h) { + if ("reset" == e) { + for (h = 0; h < this.keys.length; ++h) { + this.keys[h] = !1; + } + } else { + h && h.constructor === c && (e = h.data[1] - (12 * (this.properties.start_octave - 1) + 29), 0 <= e && e < this.keys.length && (h.data[0] == c.NOTEON ? this.keys[e] = !0 : h.data[0] == c.NOTEOFF && (this.keys[e] = !1)), this.trigger("note", h)); + } + }; + E.prototype.onMouseDown = function(e, h) { + if (!(0 > h[1])) { + return e = this.getKeyIndex(h), this.keys[e] = !0, this._last_key = e, e = 12 * (this.properties.start_octave - 1) + 29 + e, h = new c, h.setup([c.NOTEON, e, 100]), this.trigger("note", h), !0; + } + }; + E.prototype.onMouseMove = function(e, h) { + if (!(0 > h[1] || -1 == this._last_key)) { + this.setDirtyCanvas(!0); + e = this.getKeyIndex(h); + if (this._last_key == e) { + return !0; + } + this.keys[this._last_key] = !1; + h = 12 * (this.properties.start_octave - 1) + 29 + this._last_key; + var k = new c; + k.setup([c.NOTEOFF, h, 100]); + this.trigger("note", k); + this.keys[e] = !0; + h = 12 * (this.properties.start_octave - 1) + 29 + e; + k = new c; + k.setup([c.NOTEON, h, 100]); + this.trigger("note", k); + this._last_key = e; + return !0; + } + }; + E.prototype.onMouseUp = function(e, h) { + if (!(0 > h[1])) { + return e = this.getKeyIndex(h), this.keys[e] = !1, this._last_key = -1, e = 12 * (this.properties.start_octave - 1) + 29 + e, h = new c, h.setup([c.NOTEOFF, e, 100]), this.trigger("note", h), !0; + } + }; + u.registerNodeType("midi/keys", E); +})(this); +(function(v) { + function c() { + this.properties = {src:"", gain:0.5, loop:!0, autoplay:!0, playbackRate:1}; + this._loading_audio = !1; + this._audiobuffer = null; + this._audionodes = []; + this._last_sourcenode = null; + this.addOutput("out", "audio"); + this.addInput("gain", "number"); + this.audionode = p.getAudioContext().createGain(); + this.audionode.graphnode = this; + this.audionode.gain.value = this.properties.gain; + this.properties.src && this.loadSound(this.properties.src); + } + function r() { + this.properties = {gain:0.5}; + this._audionodes = []; + this._media_stream = null; + this.addOutput("out", "audio"); + this.addInput("gain", "number"); + this.audionode = p.getAudioContext().createGain(); + this.audionode.graphnode = this; + this.audionode.gain.value = this.properties.gain; + } + function m() { + this.properties = {fftSize:2048, minDecibels:-100, maxDecibels:-10, smoothingTimeConstant:0.5}; + this.audionode = p.getAudioContext().createAnalyser(); + this.audionode.graphnode = this; + this.audionode.fftSize = this.properties.fftSize; + this.audionode.minDecibels = this.properties.minDecibels; + this.audionode.maxDecibels = this.properties.maxDecibels; + this.audionode.smoothingTimeConstant = this.properties.smoothingTimeConstant; + this.addInput("in", "audio"); + this.addOutput("freqs", "array"); + this.addOutput("samples", "array"); + this._time_bin = this._freq_bin = null; + } + function h() { + this.properties = {gain:1}; + this.audionode = p.getAudioContext().createGain(); + this.addInput("in", "audio"); + this.addInput("gain", "number"); + this.addOutput("out", "audio"); + } + function q() { + this.properties = {impulse_src:"", normalize:!0}; + this.audionode = p.getAudioContext().createConvolver(); + this.addInput("in", "audio"); + this.addOutput("out", "audio"); + } + function l() { + this.properties = {threshold:-50, knee:40, ratio:12, reduction:-20, attack:0, release:0.25}; + this.audionode = p.getAudioContext().createDynamicsCompressor(); + this.addInput("in", "audio"); + this.addOutput("out", "audio"); + } + function A() { + this.properties = {}; + this.audionode = p.getAudioContext().createWaveShaper(); + this.addInput("in", "audio"); + this.addInput("shape", "waveshape"); + this.addOutput("out", "audio"); + } + function y() { + this.properties = {gain1:0.5, gain2:0.5}; + this.audionode = p.getAudioContext().createGain(); + this.audionode1 = p.getAudioContext().createGain(); + this.audionode1.gain.value = this.properties.gain1; + this.audionode2 = p.getAudioContext().createGain(); + this.audionode2.gain.value = this.properties.gain2; + this.audionode1.connect(this.audionode); + this.audionode2.connect(this.audionode); + this.addInput("in1", "audio"); + this.addInput("in1 gain", "number"); + this.addInput("in2", "audio"); + this.addInput("in2 gain", "number"); + this.addOutput("out", "audio"); + } + function x() { + this.properties = {A:0.1, D:0.1, S:0.1, R:0.1}; + this.audionode = p.getAudioContext().createGain(); + this.audionode.gain.value = 0; + this.addInput("in", "audio"); + this.addInput("gate", "bool"); + this.addOutput("out", "audio"); + this.gate = !1; + } + function F() { + this.properties = {delayTime:0.5}; + this.audionode = p.getAudioContext().createDelay(10); + this.audionode.delayTime.value = this.properties.delayTime; + this.addInput("in", "audio"); + this.addInput("time", "number"); + this.addOutput("out", "audio"); + } + function z() { + this.properties = {frequency:350, detune:0, Q:1}; + this.addProperty("type", "lowpass", "enum", {values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")}); + this.audionode = p.getAudioContext().createBiquadFilter(); + this.addInput("in", "audio"); + this.addOutput("out", "audio"); + } + function e() { + this.properties = {frequency:440, detune:0, type:"sine"}; + this.addProperty("type", "sine", "enum", {values:["sine", "square", "sawtooth", "triangle", "custom"]}); + this.audionode = p.getAudioContext().createOscillator(); + this.addOutput("out", "audio"); + } + function B() { + this.properties = {continuous:!0, mark:-1}; + this.addInput("data", "array"); + this.addInput("mark", "number"); + this.size = [300, 200]; + this._last_buffer = null; + } + function E() { + this.properties = {band:440, amplitude:1}; + this.addInput("freqs", "array"); + this.addOutput("signal", "number"); + } + function u() { + if (!u.default_code) { + var c = u.default_function.toString(), a = c.indexOf("{") + 1, b = c.lastIndexOf("}"); + u.default_code = c.substr(a, b - a); + } + this.properties = {code:u.default_code}; + c = p.getAudioContext(); + c.createScriptProcessor ? this.audionode = c.createScriptProcessor(4096, 1, 1) : (console.warn("ScriptProcessorNode deprecated"), this.audionode = c.createGain()); + this.processCode(); + u._bypass_function || (u._bypass_function = this.audionode.onaudioprocess); + this.addInput("in", "audio"); + this.addOutput("out", "audio"); + } + function H() { + this.audionode = p.getAudioContext().destination; + this.addInput("in", "audio"); + } + var n = v.LiteGraph, p = {}; + v.LGAudio = p; + p.getAudioContext = function() { + if (!this._audio_context) { + window.AudioContext = window.AudioContext || window.webkitAudioContext; + if (!window.AudioContext) { + return console.error("AudioContext not supported by browser"), null; + } + this._audio_context = new AudioContext; + this._audio_context.onmessage = function(c) { + console.log("msg", c); + }; + this._audio_context.onended = function(c) { + console.log("ended", c); + }; + this._audio_context.oncomplete = function(c) { + console.log("complete", c); + }; + } + return this._audio_context; + }; + p.connect = function(c, a) { + try { + c.connect(a); + } catch (b) { + console.warn("LGraphAudio:", b); + } + }; + p.disconnect = function(c, a) { + try { + c.disconnect(a); + } catch (b) { + console.warn("LGraphAudio:", b); + } + }; + p.changeAllAudiosConnections = function(c, a) { + if (c.inputs) { + for (var b = 0; b < c.inputs.length; ++b) { + var d = c.graph.links[c.inputs[b].link]; + if (d) { + var e = c.graph.getNodeById(d.origin_id); + e = e.getAudioNodeInOutputSlot ? e.getAudioNodeInOutputSlot(d.origin_slot) : e.audionode; + d = c.getAudioNodeInInputSlot ? c.getAudioNodeInInputSlot(b) : c.audionode; + a ? p.connect(e, d) : p.disconnect(e, d); + } + } + } + if (c.outputs) { + for (b = 0; b < c.outputs.length; ++b) { + for (var k = c.outputs[b], h = 0; h < k.links.length; ++h) { + if (d = c.graph.links[k.links[h]]) { + e = c.getAudioNodeInOutputSlot ? c.getAudioNodeInOutputSlot(b) : c.audionode; + var l = c.graph.getNodeById(d.target_id); + d = l.getAudioNodeInInputSlot ? l.getAudioNodeInInputSlot(d.target_slot) : l.audionode; + a ? p.connect(e, d) : p.disconnect(e, d); + } + } + } + } + }; + p.onConnectionsChange = function(c, a, b, d) { + c == n.OUTPUT && (c = null, d && (c = this.graph.getNodeById(d.target_id)), c && (a = this.getAudioNodeInOutputSlot ? this.getAudioNodeInOutputSlot(a) : this.audionode, d = c.getAudioNodeInInputSlot ? c.getAudioNodeInInputSlot(d.target_slot) : c.audionode, b ? p.connect(a, d) : p.disconnect(a, d))); + }; + p.createAudioNodeWrapper = function(c) { + var a = c.prototype.onPropertyChanged; + c.prototype.onPropertyChanged = function(b, d) { + a && a.call(this, b, d); + this.audionode && void 0 !== this.audionode[b] && (void 0 !== this.audionode[b].value ? this.audionode[b].value = d : this.audionode[b] = d); + }; + c.prototype.onConnectionsChange = p.onConnectionsChange; + }; + p.cached_audios = {}; + p.loadSound = function(c, a, b) { + function d(a) { + console.log("Audio loading sample error:", a); + b && b(a); + } + if (p.cached_audios[c] && -1 == c.indexOf("blob:")) { + a && a(p.cached_audios[c]); + } else { + p.onProcessAudioURL && (c = p.onProcessAudioURL(c)); + var e = new XMLHttpRequest; + e.open("GET", c, !0); + e.responseType = "arraybuffer"; + var k = p.getAudioContext(); + e.onload = function() { + console.log("AudioSource loaded"); + k.decodeAudioData(e.response, function(b) { + console.log("AudioSource decoded"); + p.cached_audios[c] = b; + a && a(b); + }, d); + }; + e.send(); + return e; + } + }; + c.desc = "Plays an audio file"; + c["@src"] = {widget:"resource"}; + c.supported_extensions = ["wav", "ogg", "mp3"]; + c.prototype.onAdded = function(c) { + if (c.status === LGraph.STATUS_RUNNING) { + this.onStart(); + } + }; + c.prototype.onStart = function() { + this._audiobuffer && this.properties.autoplay && this.playBuffer(this._audiobuffer); + }; + c.prototype.onStop = function() { + this.stopAllSounds(); + }; + c.prototype.onPause = function() { + this.pauseAllSounds(); + }; + c.prototype.onUnpause = function() { + this.unpauseAllSounds(); + }; + c.prototype.onRemoved = function() { + this.stopAllSounds(); + this._dropped_url && URL.revokeObjectURL(this._url); + }; + c.prototype.stopAllSounds = function() { + for (var c = 0; c < this._audionodes.length; ++c) { + this._audionodes[c].started && (this._audionodes[c].started = !1, this._audionodes[c].stop()); + } + this._audionodes.length = 0; + }; + c.prototype.pauseAllSounds = function() { + p.getAudioContext().suspend(); + }; + c.prototype.unpauseAllSounds = function() { + p.getAudioContext().resume(); + }; + c.prototype.onExecute = function() { + if (this.inputs) { + for (var c = 0; c < this.inputs.length; ++c) { + var a = this.inputs[c]; + if (null != a.link) { + var b = this.getInputData(c); + if (void 0 !== b) { + if ("gain" == a.name) { + this.audionode.gain.value = b; + } else { + if ("src" == a.name) { + this.setProperty("src", b); + } else { + if ("playbackRate" == a.name) { + for (this.properties.playbackRate = b, a = 0; a < this._audionodes.length; ++a) { + this._audionodes[a].playbackRate.value = b; + } + } + } + } + } + } + } + } + if (this.outputs) { + for (c = 0; c < this.outputs.length; ++c) { + "buffer" == this.outputs[c].name && this._audiobuffer && this.setOutputData(c, this._audiobuffer); + } + } + }; + c.prototype.onAction = function(c) { + this._audiobuffer && ("Play" == c ? this.playBuffer(this._audiobuffer) : "Stop" == c && this.stopAllSounds()); + }; + c.prototype.onPropertyChanged = function(c, a) { + if ("src" == c) { + this.loadSound(a); + } else { + if ("gain" == c) { + this.audionode.gain.value = a; + } else { + if ("playbackRate" == c) { + for (c = 0; c < this._audionodes.length; ++c) { + this._audionodes[c].playbackRate.value = a; + } + } + } + } + }; + c.prototype.playBuffer = function(c) { + var a = this, b = p.getAudioContext().createBufferSource(); + this._last_sourcenode = b; + b.graphnode = this; + b.buffer = c; + b.loop = this.properties.loop; + b.playbackRate.value = this.properties.playbackRate; + this._audionodes.push(b); + b.connect(this.audionode); + this._audionodes.push(b); + this.trigger("start"); + b.onended = function() { + a.trigger("ended"); + var c = a._audionodes.indexOf(b); + -1 != c && a._audionodes.splice(c, 1); + }; + b.started || (b.started = !0, b.start()); + return b; + }; + c.prototype.loadSound = function(c) { + var a = this; + this._request && (this._request.abort(), this._request = null); + this._audiobuffer = null; + this._loading_audio = !1; + c && (this._request = p.loadSound(c, function(b) { + this.boxcolor = n.NODE_DEFAULT_BOXCOLOR; + a._audiobuffer = b; + a._loading_audio = !1; + if (a.graph && a.graph.status === LGraph.STATUS_RUNNING) { + a.onStart(); + } + }), this._loading_audio = !0, this.boxcolor = "#AA4"); + }; + c.prototype.onConnectionsChange = p.onConnectionsChange; + c.prototype.onGetInputs = function() { + return [["playbackRate", "number"], ["src", "string"], ["Play", n.ACTION], ["Stop", n.ACTION]]; + }; + c.prototype.onGetOutputs = function() { + return [["buffer", "audiobuffer"], ["start", n.EVENT], ["ended", n.EVENT]]; + }; + c.prototype.onDropFile = function(c) { + this._dropped_url && URL.revokeObjectURL(this._dropped_url); + c = URL.createObjectURL(c); + this.properties.src = c; + this.loadSound(c); + this._dropped_url = c; + }; + c.title = "Source"; + c.desc = "Plays audio"; + n.registerNodeType("audio/source", c); + r.prototype.onAdded = function(c) { + if (c.status === LGraph.STATUS_RUNNING) { + this.onStart(); + } + }; + r.prototype.onStart = function() { + null != this._media_stream || this._waiting_confirmation || this.openStream(); + }; + r.prototype.onStop = function() { + this.audionode.gain.value = 0; + }; + r.prototype.onPause = function() { + this.audionode.gain.value = 0; + }; + r.prototype.onUnpause = function() { + this.audionode.gain.value = this.properties.gain; + }; + r.prototype.onRemoved = function() { + this.audionode.gain.value = 0; + this.audiosource_node && (this.audiosource_node.disconnect(this.audionode), this.audiosource_node = null); + if (this._media_stream) { + var c = this._media_stream.getTracks(); + c.length && c[0].stop(); + } + }; + r.prototype.openStream = function() { + if (navigator.mediaDevices) { + this._waiting_confirmation = !0; + navigator.mediaDevices.getUserMedia({audio:!0, video:!1}).then(this.streamReady.bind(this)).catch(function(a) { + console.log("Media rejected", a); + c._media_stream = !1; + c.boxcolor = "red"; + }); + var c = this; + } else { + console.log("getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags"); + } + }; + r.prototype.streamReady = function(c) { + this._media_stream = c; + this.audiosource_node && this.audiosource_node.disconnect(this.audionode); + this.audiosource_node = p.getAudioContext().createMediaStreamSource(c); + this.audiosource_node.graphnode = this; + this.audiosource_node.connect(this.audionode); + this.boxcolor = "white"; + }; + r.prototype.onExecute = function() { + null != this._media_stream || this._waiting_confirmation || this.openStream(); + if (this.inputs) { + for (var c = 0; c < this.inputs.length; ++c) { + var a = this.inputs[c]; + if (null != a.link) { + var b = this.getInputData(c); + void 0 !== b && "gain" == a.name && (this.audionode.gain.value = this.properties.gain = b); + } + } + } + }; + r.prototype.onAction = function(c) { + "Play" == c ? this.audionode.gain.value = this.properties.gain : "Stop" == c && (this.audionode.gain.value = 0); + }; + r.prototype.onPropertyChanged = function(c, a) { + "gain" == c && (this.audionode.gain.value = a); + }; + r.prototype.onConnectionsChange = p.onConnectionsChange; + r.prototype.onGetInputs = function() { + return [["playbackRate", "number"], ["Play", n.ACTION], ["Stop", n.ACTION]]; + }; + r.title = "MediaSource"; + r.desc = "Plays microphone"; + n.registerNodeType("audio/media_source", r); + m.prototype.onPropertyChanged = function(c, a) { + this.audionode[c] = a; + }; + m.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + var c = this.audionode.frequencyBinCount; + this._freq_bin && this._freq_bin.length == c || (this._freq_bin = new Uint8Array(c)); + this.audionode.getByteFrequencyData(this._freq_bin); + this.setOutputData(0, this._freq_bin); + } + this.isOutputConnected(1) && (c = this.audionode.frequencyBinCount, this._time_bin && this._time_bin.length == c || (this._time_bin = new Uint8Array(c)), this.audionode.getByteTimeDomainData(this._time_bin), this.setOutputData(1, this._time_bin)); + for (c = 1; c < this.inputs.length; ++c) { + var a = this.inputs[c]; + if (null != a.link) { + var b = this.getInputData(c); + void 0 !== b && (this.audionode[a.name].value = b); + } + } + }; + m.prototype.onGetInputs = function() { + return [["minDecibels", "number"], ["maxDecibels", "number"], ["smoothingTimeConstant", "number"]]; + }; + m.prototype.onGetOutputs = function() { + return [["freqs", "array"], ["samples", "array"]]; + }; + m.title = "Analyser"; + m.desc = "Audio Analyser"; + n.registerNodeType("audio/analyser", m); + h.prototype.onExecute = function() { + if (this.inputs && this.inputs.length) { + for (var c = 1; c < this.inputs.length; ++c) { + var a = this.inputs[c], b = this.getInputData(c); + void 0 !== b && (this.audionode[a.name].value = b); + } + } + }; + p.createAudioNodeWrapper(h); + h.title = "Gain"; + h.desc = "Audio gain"; + n.registerNodeType("audio/gain", h); + p.createAudioNodeWrapper(q); + q.prototype.onRemove = function() { + this._dropped_url && URL.revokeObjectURL(this._dropped_url); + }; + q.prototype.onPropertyChanged = function(c, a) { + "impulse_src" == c ? this.loadImpulse(a) : "normalize" == c && (this.audionode.normalize = a); + }; + q.prototype.onDropFile = function(c) { + this._dropped_url && URL.revokeObjectURL(this._dropped_url); + this._dropped_url = URL.createObjectURL(c); + this.properties.impulse_src = this._dropped_url; + this.loadImpulse(this._dropped_url); + }; + q.prototype.loadImpulse = function(c) { + var a = this; + this._request && (this._request.abort(), this._request = null); + this._impulse_buffer = null; + this._loading_impulse = !1; + c && (this._request = p.loadSound(c, function(b) { + a._impulse_buffer = b; + a.audionode.buffer = b; + console.log("Impulse signal set"); + a._loading_impulse = !1; + }), this._loading_impulse = !0); + }; + q.title = "Convolver"; + q.desc = "Convolves the signal (used for reverb)"; + n.registerNodeType("audio/convolver", q); + p.createAudioNodeWrapper(l); + l.prototype.onExecute = function() { + if (this.inputs && this.inputs.length) { + for (var c = 1; c < this.inputs.length; ++c) { + var a = this.inputs[c]; + if (null != a.link) { + var b = this.getInputData(c); + void 0 !== b && (this.audionode[a.name].value = b); + } + } + } + }; + l.prototype.onGetInputs = function() { + return [["threshold", "number"], ["knee", "number"], ["ratio", "number"], ["reduction", "number"], ["attack", "number"], ["release", "number"]]; + }; + l.title = "DynamicsCompressor"; + l.desc = "Dynamics Compressor"; + n.registerNodeType("audio/dynamicsCompressor", l); + A.prototype.onExecute = function() { + if (this.inputs && this.inputs.length) { + var c = this.getInputData(1); + void 0 !== c && (this.audionode.curve = c); + } + }; + A.prototype.setWaveShape = function(c) { + this.audionode.curve = c; + }; + p.createAudioNodeWrapper(A); + y.prototype.getAudioNodeInInputSlot = function(c) { + if (0 == c) { + return this.audionode1; + } + if (2 == c) { + return this.audionode2; + } + }; + y.prototype.onPropertyChanged = function(c, a) { + "gain1" == c ? this.audionode1.gain.value = a : "gain2" == c && (this.audionode2.gain.value = a); + }; + y.prototype.onExecute = function() { + if (this.inputs && this.inputs.length) { + for (var c = 1; c < this.inputs.length; ++c) { + var a = this.inputs[c]; + null != a.link && "audio" != a.type && (a = this.getInputData(c), void 0 !== a && (1 == c ? this.audionode1.gain.value = a : 3 == c && (this.audionode2.gain.value = a))); + } + } + }; + p.createAudioNodeWrapper(y); + y.title = "Mixer"; + y.desc = "Audio mixer"; + n.registerNodeType("audio/mixer", y); + x.prototype.onExecute = function() { + var c = p.getAudioContext().currentTime, a = this.audionode.gain, b = this.getInputData(1), d = this.getInputOrProperty("A"), e = this.getInputOrProperty("D"), f = this.getInputOrProperty("S"), h = this.getInputOrProperty("R"); + !this.gate && b ? (a.cancelScheduledValues(0), a.setValueAtTime(0, c), a.linearRampToValueAtTime(1, c + d), a.linearRampToValueAtTime(f, c + d + e)) : this.gate && !b && (a.cancelScheduledValues(0), a.setValueAtTime(a.value, c), a.linearRampToValueAtTime(0, c + h)); + this.gate = b; + }; + x.prototype.onGetInputs = function() { + return [["A", "number"], ["D", "number"], ["S", "number"], ["R", "number"]]; + }; + p.createAudioNodeWrapper(x); + x.title = "ADSR"; + x.desc = "Audio envelope"; + n.registerNodeType("audio/adsr", x); + p.createAudioNodeWrapper(F); + F.prototype.onExecute = function() { + var c = this.getInputData(1); + void 0 !== c && (this.audionode.delayTime.value = c); + }; + F.title = "Delay"; + F.desc = "Audio delay"; + n.registerNodeType("audio/delay", F); + z.prototype.onExecute = function() { + if (this.inputs && this.inputs.length) { + for (var c = 1; c < this.inputs.length; ++c) { + var a = this.inputs[c]; + if (null != a.link) { + var b = this.getInputData(c); + void 0 !== b && (this.audionode[a.name].value = b); + } + } + } + }; + z.prototype.onGetInputs = function() { + return [["frequency", "number"], ["detune", "number"], ["Q", "number"]]; + }; + p.createAudioNodeWrapper(z); + z.title = "BiquadFilter"; + z.desc = "Audio filter"; + n.registerNodeType("audio/biquadfilter", z); + e.prototype.onStart = function() { + if (!this.audionode.started) { + this.audionode.started = !0; + try { + this.audionode.start(); + } catch (k) { + } + } + }; + e.prototype.onStop = function() { + this.audionode.started && (this.audionode.started = !1, this.audionode.stop()); + }; + e.prototype.onPause = function() { + this.onStop(); + }; + e.prototype.onUnpause = function() { + this.onStart(); + }; + e.prototype.onExecute = function() { + if (this.inputs && this.inputs.length) { + for (var c = 0; c < this.inputs.length; ++c) { + var a = this.inputs[c]; + if (null != a.link) { + var b = this.getInputData(c); + void 0 !== b && (this.audionode[a.name].value = b); + } + } + } + }; + e.prototype.onGetInputs = function() { + return [["frequency", "number"], ["detune", "number"], ["type", "string"]]; + }; + p.createAudioNodeWrapper(e); + e.title = "Oscillator"; + e.desc = "Oscillator"; + n.registerNodeType("audio/oscillator", e); + B.prototype.onExecute = function() { + this._last_buffer = this.getInputData(0); + var c = this.getInputData(1); + void 0 !== c && (this.properties.mark = c); + this.setDirtyCanvas(!0, !1); + }; + B.prototype.onDrawForeground = function(c) { + if (this._last_buffer) { + var a = this._last_buffer, b = a.length / this.size[0], d = this.size[1]; + c.fillStyle = "black"; + c.fillRect(0, 0, this.size[0], this.size[1]); + c.strokeStyle = "white"; + c.beginPath(); + var e = 0; + if (this.properties.continuous) { + c.moveTo(e, d); + for (var f = 0; f < a.length; f += b) { + c.lineTo(e, d - a[f | 0] / 255 * d), e++; + } + } else { + for (f = 0; f < a.length; f += b) { + c.moveTo(e + 0.5, d), c.lineTo(e + 0.5, d - a[f | 0] / 255 * d), e++; + } + } + c.stroke(); + 0 <= this.properties.mark && (a = p.getAudioContext().sampleRate / a.length, e = this.properties.mark / a * 2 / b, e >= this.size[0] && (e = this.size[0] - 1), c.strokeStyle = "red", c.beginPath(), c.moveTo(e, d), c.lineTo(e, 0), c.stroke()); + } + }; + B.title = "Visualization"; + B.desc = "Audio Visualization"; + n.registerNodeType("audio/visualization", B); + E.prototype.onExecute = function() { + if (this._freqs = this.getInputData(0)) { + var c = this.properties.band, a = this.getInputData(1); + void 0 !== a && (c = a); + a = p.getAudioContext().sampleRate / this._freqs.length; + a = c / a * 2; + a >= this._freqs.length ? a = this._freqs[this._freqs.length - 1] : (c = a | 0, a -= c, a = this._freqs[c] * (1 - a) + this._freqs[c + 1] * a); + this.setOutputData(0, a / 255 * this.properties.amplitude); + } + }; + E.prototype.onGetInputs = function() { + return [["band", "number"]]; + }; + E.title = "Signal"; + E.desc = "extract the signal of some frequency"; + n.registerNodeType("audio/signal", E); + u.prototype.onAdded = function(c) { + c.status == LGraph.STATUS_RUNNING && (this.audionode.onaudioprocess = this._callback); + }; + u["@code"] = {widget:"code", type:"code"}; + u.prototype.onStart = function() { + this.audionode.onaudioprocess = this._callback; + }; + u.prototype.onStop = function() { + this.audionode.onaudioprocess = u._bypass_function; + }; + u.prototype.onPause = function() { + this.audionode.onaudioprocess = u._bypass_function; + }; + u.prototype.onUnpause = function() { + this.audionode.onaudioprocess = this._callback; + }; + u.prototype.onExecute = function() { + }; + u.prototype.onRemoved = function() { + this.audionode.onaudioprocess = u._bypass_function; + }; + u.prototype.processCode = function() { + try { + this._script = new (new Function("properties", this.properties.code))(this.properties), this._old_code = this.properties.code, this._callback = this._script.onaudioprocess; + } catch (k) { + console.error("Error in onaudioprocess code", k), this._callback = u._bypass_function, this.audionode.onaudioprocess = this._callback; + } + }; + u.prototype.onPropertyChanged = function(c, a) { + "code" == c && (this.properties.code = a, this.processCode(), this.graph && this.graph.status == LGraph.STATUS_RUNNING && (this.audionode.onaudioprocess = this._callback)); + }; + u.default_function = function() { + this.onaudioprocess = function(c) { + var a = c.inputBuffer; + c = c.outputBuffer; + for (var b = 0; b < c.numberOfChannels; b++) { + for (var d = a.getChannelData(b), e = c.getChannelData(b), f = 0; f < a.length; f++) { + e[f] = d[f]; + } + } + }; + }; + p.createAudioNodeWrapper(u); + u.title = "Script"; + u.desc = "apply script to signal"; + n.registerNodeType("audio/script", u); + H.title = "Destination"; + H.desc = "Audio output"; + n.registerNodeType("audio/destination", H); +})(this); +(function(v) { + function c() { + this.size = [60, 20]; + this.addInput("send", m.ACTION); + this.addOutput("received", m.EVENT); + this.addInput("in", 0); + this.addOutput("out", 0); + this.properties = {url:"", room:"lgraph", only_send_changes:!0}; + this._ws = null; + this._last_sent_data = []; + this._last_received_data = []; + } + function r() { + this.room_widget = this.addWidget("text", "Room", "lgraph", this.setRoom.bind(this)); + this.addWidget("button", "Reconnect", null, this.connectSocket.bind(this)); + this.addInput("send", m.ACTION); + this.addOutput("received", m.EVENT); + this.addInput("in", 0); + this.addOutput("out", 0); + this.properties = {url:"tamats.com:55000", room:"lgraph", only_send_changes:!0}; + this._server = null; + this.connectSocket(); + this._last_sent_data = []; + this._last_received_data = []; + "undefined" == typeof SillyClient && console.warn("remember to add SillyClient.js to your project: https://tamats.com/projects/sillyserver/src/sillyclient.js"); + } + var m = v.LiteGraph; + c.title = "WebSocket"; + c.desc = "Send data through a websocket"; + c.prototype.onPropertyChanged = function(c, m) { + "url" == c && this.connectSocket(); + }; + c.prototype.onExecute = function() { + !this._ws && this.properties.url && this.connectSocket(); + if (this._ws && this._ws.readyState == WebSocket.OPEN) { + for (var c = this.properties.room, m = this.properties.only_send_changes, l = 1; l < this.inputs.length; ++l) { + var r = this.getInputData(l); + if (null != r) { + try { + var v = JSON.stringify({type:0, room:c, channel:l, data:r}); + } catch (x) { + continue; + } + m && this._last_sent_data[l] == v || (this._last_sent_data[l] = v, this._ws.send(v)); + } + } + for (l = 1; l < this.outputs.length; ++l) { + this.setOutputData(l, this._last_received_data[l]); + } + "#AFA" == this.boxcolor && (this.boxcolor = "#6C6"); + } + }; + c.prototype.connectSocket = function() { + var c = this, q = this.properties.url; + "ws" != q.substr(0, 2) && (q = "ws://" + q); + this._ws = new WebSocket(q); + this._ws.onopen = function() { + console.log("ready"); + c.boxcolor = "#6C6"; + }; + this._ws.onmessage = function(h) { + c.boxcolor = "#AFA"; + h = JSON.parse(h.data); + if (!h.room || h.room == c.properties.room) { + if (1 == h.type) { + if (h.data.object_class && m[h.data.object_class]) { + var l = null; + try { + l = new m[h.data.object_class](h.data), c.triggerSlot(0, l); + } catch (y) { + } + } else { + c.triggerSlot(0, h.data); + } + } else { + c._last_received_data[h.channel || 0] = h.data; + } + } + }; + this._ws.onerror = function(h) { + console.log("couldnt connect to websocket"); + c.boxcolor = "#E88"; + }; + this._ws.onclose = function(h) { + console.log("connection closed"); + c.boxcolor = "#000"; + }; + }; + c.prototype.send = function(c) { + this._ws && this._ws.readyState == WebSocket.OPEN && this._ws.send(JSON.stringify({type:1, msg:c})); + }; + c.prototype.onAction = function(c, m) { + this._ws && this._ws.readyState == WebSocket.OPEN && this._ws.send({type:1, room:this.properties.room, action:c, data:m}); + }; + c.prototype.onGetInputs = function() { + return [["in", 0]]; + }; + c.prototype.onGetOutputs = function() { + return [["out", 0]]; + }; + m.registerNodeType("network/websocket", c); + r.title = "SillyClient"; + r.desc = "Connects to SillyServer to broadcast messages"; + r.prototype.onPropertyChanged = function(c, m) { + "room" == c && (this.room_widget.value = m); + this.connectSocket(); + }; + r.prototype.setRoom = function(c) { + this.properties.room = c; + this.room_widget.value = c; + this.connectSocket(); + }; + r.prototype.onDrawForeground = function() { + for (var c = 1; c < this.inputs.length; ++c) { + var m = this.inputs[c]; + m.label = "in_" + c; + } + for (c = 1; c < this.outputs.length; ++c) { + m = this.outputs[c], m.label = "out_" + c; + } + }; + r.prototype.onExecute = function() { + if (this._server && this._server.is_connected) { + for (var c = this.properties.only_send_changes, m = 1; m < this.inputs.length; ++m) { + var l = this.getInputData(m), r = this._last_sent_data[m]; + if (null != l) { + if (c) { + var v = !0; + if (l && l.length && r && r.length == l.length && l.constructor !== String) { + for (var x = 0; x < l.length; ++x) { + if (r[x] != l[x]) { + v = !1; + break; + } + } + } else { + this._last_sent_data[m] != l && (v = !1); + } + if (v) { + continue; + } + } + this._server.sendMessage({type:0, channel:m, data:l}); + if (l.length && l.constructor !== String) { + if (this._last_sent_data[m]) { + for (this._last_sent_data[m].length = l.length, x = 0; x < l.length; ++x) { + this._last_sent_data[m][x] = l[x]; + } + } else { + this._last_sent_data[m] = l.constructor === Array ? l.concat() : new l.constructor(l); + } + } else { + this._last_sent_data[m] = l; + } + } + } + for (m = 1; m < this.outputs.length; ++m) { + this.setOutputData(m, this._last_received_data[m]); + } + "#AFA" == this.boxcolor && (this.boxcolor = "#6C6"); + } + }; + r.prototype.connectSocket = function() { + var c = this; + if ("undefined" == typeof SillyClient) { + this._error || console.error("SillyClient node cannot be used, you must include SillyServer.js"), this._error = !0; + } else { + if (this._server = new SillyClient, this._server.on_ready = function() { + console.log("ready"); + c.boxcolor = "#6C6"; + }, this._server.on_message = function(h, l) { + h = null; + try { + h = JSON.parse(l); + } catch (A) { + return; + } + if (1 == h.type) { + if (h.data.object_class && m[h.data.object_class]) { + l = null; + try { + l = new m[h.data.object_class](h.data), c.triggerSlot(0, l); + } catch (A) { + return; + } + } else { + c.triggerSlot(0, h.data); + } + } else { + c._last_received_data[h.channel || 0] = h.data; + } + c.boxcolor = "#AFA"; + }, this._server.on_error = function(h) { + console.log("couldnt connect to websocket"); + c.boxcolor = "#E88"; + }, this._server.on_close = function(h) { + console.log("connection closed"); + c.boxcolor = "#000"; + }, this.properties.url && this.properties.room) { + try { + this._server.connect(this.properties.url, this.properties.room); + } catch (q) { + console.error("SillyServer error: " + q); + this._server = null; + return; + } + this._final_url = this.properties.url + "/" + this.properties.room; + } + } + }; + r.prototype.send = function(c) { + this._server && this._server.is_connected && this._server.sendMessage({type:1, data:c}); + }; + r.prototype.onAction = function(c, m) { + this._server && this._server.is_connected && this._server.sendMessage({type:1, action:c, data:m}); + }; + r.prototype.onGetInputs = function() { + return [["in", 0]]; + }; + r.prototype.onGetOutputs = function() { + return [["out", 0]]; + }; + m.registerNodeType("network/sillyclient", r); +})(this); + diff --git a/src/litegraph.js b/src/litegraph.js index b2fcda9fb..f97538428 100755 --- a/src/litegraph.js +++ b/src/litegraph.js @@ -3171,10 +3171,10 @@ /** * computes the minimum size of a node according to its inputs and output slots * @method computeSize - * @param {number} minHeight + * @param {number} the optional target width * @return {number} the total size */ - LGraphNode.prototype.computeSize = function(out) { + LGraphNode.prototype.computeSize = function(width, out) { if (this.constructor.size) { return this.constructor.size.concat(); } @@ -3226,7 +3226,7 @@ if (this.widgets && this.widgets.length) { for (var i = 0, l = this.widgets.length; i < l; ++i) { if (this.widgets[i].computeSize) - widgets_height += this.widgets[i].computeSize(size[0])[1] + 4; + widgets_height += this.widgets[i].computeSize(Math.max(size[0], width || 0))[1] + 4; else widgets_height += LiteGraph.NODE_WIDGET_HEIGHT + 4; } From 1763ff1a87d41e3c1c090f581329a041d2376778 Mon Sep 17 00:00:00 2001 From: altarfinch Date: Thu, 18 Jun 2020 15:33:56 +0200 Subject: [PATCH 47/63] Forgot to call the updated computeSize() function --- src/litegraph.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/litegraph.js b/src/litegraph.js index f97538428..0c0c1347a 100755 --- a/src/litegraph.js +++ b/src/litegraph.js @@ -5575,7 +5575,7 @@ LGraphNode.prototype.executeAction = function(action) if (this.resizing_node && !this.live_mode) { //convert mouse to node space var desired_size = [ e.canvasX - this.resizing_node.pos[0], e.canvasY - this.resizing_node.pos[1] ]; - var min_size = this.resizing_node.computeSize(); + var min_size = this.resizing_node.computeSize(desired_size[0]); desired_size[0] = Math.max( min_size[0], desired_size[0] ); desired_size[1] = Math.max( min_size[1], desired_size[1] ); this.resizing_node.setSize( desired_size ); From 6ac69486092a069bbd7495dfe43c89af8c21bfdc Mon Sep 17 00:00:00 2001 From: Javi Agenjo Date: Wed, 1 Jul 2020 09:51:41 +0200 Subject: [PATCH 48/63] Update README.md --- guides/README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/guides/README.md b/guides/README.md index 6f61a9fce..4743daf50 100644 --- a/guides/README.md +++ b/guides/README.md @@ -60,11 +60,14 @@ There are several settings that could be defined or modified per node: * **size**: ```[width,height]``` the size of the area inside the node (excluding title). Every row is LiteGraph.NODE_SLOT_HEIGHT pixels height. * **properties**: object containing the properties that could be configured by the user, and serialized when saving the graph * **shape**: the shape of the object (could be LiteGraph.BOX_SHAPE,LiteGraph.ROUND_SHAPE,LiteGraph.CARD_SHAPE) -* **flags**: several flags - * **resizable**: if it can be resized dragging the corner - * **horizontal**: if the slots should be placed horizontally on the top and bottom of the node - * **clip_area**: clips the content when rendering the node +* **flags**: flags that can be changed by the user and will be stored when serialized * **collapsed**: if it is shown collapsed (small) +* **redraw_on_mouse**: forces a redraw if the mouse passes over the widget +* **widgets_up**: widgets do not start after the slots +* **widgets_y**: widgets should start being drawn from this Y +* **clip_area**: clips the content when rendering the node +* **resizable**: if it can be resized dragging the corner +* **horizontal**: if the slots should be placed horizontally on the top and bottom of the node There are several callbacks that could be defined by the user: * **onAdded**: called when added to graph From 2f9c67295f476ad2a0e5554058ae1c82263783d0 Mon Sep 17 00:00:00 2001 From: tamat Date: Thu, 2 Jul 2020 00:00:53 +0200 Subject: [PATCH 49/63] shader graph support --- build/litegraph.js | 531 ++++++++++++++++++++++++++++++++++++--- css/litegraph-editor.css | 153 +---------- css/litegraph.css | 229 +++++++++++++++++ demo/js/code.js | 5 +- demo/style.css | 2 +- src/litegraph-editor.js | 255 +------------------ src/litegraph.js | 453 ++++++++++++++++++++++++++++++--- src/nodes/base.js | 4 + src/nodes/gltextures.js | 95 ++++++- src/nodes/shaders.js | 429 +++++++++++++++++++++++++++++++ 10 files changed, 1684 insertions(+), 472 deletions(-) create mode 100644 src/nodes/shaders.js diff --git a/build/litegraph.js b/build/litegraph.js index 048fedcef..4c125bf70 100644 --- a/build/litegraph.js +++ b/build/litegraph.js @@ -1382,6 +1382,9 @@ this.onNodeRemoved(node); } + //close panels + this.sendActionToCanvas("checkPanels"); + this.setDirtyCanvas(true, true); this.change(); @@ -3090,9 +3093,11 @@ } this.inputs.push(o); this.setSize( this.computeSize() ); + if (this.onInputAdded) { this.onInputAdded(o); } + this.setDirtyCanvas(true, true); return o; }; @@ -4710,6 +4715,7 @@ LGraphNode.prototype.executeAction = function(action) } graph.attachCanvas(this); + this.checkPanels(); this.setDirty(true, true); }; @@ -4841,6 +4847,7 @@ LGraphNode.prototype.executeAction = function(action) } var canvas = this.canvas; + var ref_window = this.getCanvasWindow(); var document = ref_window.document; //hack used when moving canvas between windows @@ -5243,6 +5250,7 @@ LGraphNode.prototype.executeAction = function(action) //it wasn't clicked on the links boxes if (!skip_action) { var block_drag_node = false; + var pos = [e.canvasX - node.pos[0], e.canvasY - node.pos[1]]; //widgets var widget = this.processNodeWidgets( node, this.canvas_mouse, e ); @@ -5255,18 +5263,31 @@ LGraphNode.prototype.executeAction = function(action) if (is_double_click && this.selected_nodes[node.id]) { //double click node if (node.onDblClick) { - node.onDblClick( e, [ e.canvasX - node.pos[0], e.canvasY - node.pos[1] ], this ); + node.onDblClick( e, pos, this ); } this.processNodeDblClicked(node); block_drag_node = true; } //if do not capture mouse - if ( node.onMouseDown && node.onMouseDown( e, [e.canvasX - node.pos[0], e.canvasY - node.pos[1]], this ) ) { - block_drag_node = true; - } else if (this.live_mode) { - clicking_canvas_bg = true; + if ( node.onMouseDown && node.onMouseDown( e, pos, this ) ) { block_drag_node = true; + } else { + //open subgraph button + if(node.subgraph && !node.skip_subgraph_button) + { + if ( !node.flags.collapsed && pos[0] > node.size[0] - LiteGraph.NODE_TITLE_HEIGHT && pos[1] < 0 ) { + var that = this; + setTimeout(function() { + that.openSubgraph(node.subgraph); + }, 10); + } + } + + if (this.live_mode) { + clicking_canvas_bg = true; + block_drag_node = true; + } } if (!block_drag_node) { @@ -5460,6 +5481,10 @@ LGraphNode.prototype.executeAction = function(action) //mouse over a node if (node) { + + if(node.redraw_on_mouse) + this.dirty_canvas = true; + //this.canvas.style.cursor = "move"; if (!node.mouseOver) { //mouse enter @@ -6200,6 +6225,10 @@ LGraphNode.prototype.executeAction = function(action) if (this.onShowNodePanel) { this.onShowNodePanel(n); } + else + { + this.showShowNodePanel(n); + } if (this.onNodeDblClicked) { this.onNodeDblClicked(n); @@ -6350,6 +6379,10 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.prototype.deleteSelectedNodes = function() { for (var i in this.selected_nodes) { var node = this.selected_nodes[i]; + + if(node.block_delete) + continue; + //autoconnect when possible (very basic, only takes into account first input-output) if(node.inputs && node.inputs.length && node.outputs && node.outputs.length && LiteGraph.isValidConnection( node.inputs[0].type, node.outputs[0].type ) && node.inputs[0].link && node.outputs[0].links && node.outputs[0].links.length ) { @@ -6972,14 +7005,8 @@ LGraphNode.prototype.executeAction = function(action) var glow = false; this.current_node = node; - var color = - node.color || - node.constructor.color || - LiteGraph.NODE_DEFAULT_COLOR; - var bgcolor = - node.bgcolor || - node.constructor.bgcolor || - LiteGraph.NODE_DEFAULT_BGCOLOR; + var color = node.color || node.constructor.color || LiteGraph.NODE_DEFAULT_COLOR; + var bgcolor = node.bgcolor || node.constructor.bgcolor || LiteGraph.NODE_DEFAULT_BGCOLOR; //shadow and glow if (node.mouseOver) { @@ -7493,20 +7520,14 @@ LGraphNode.prototype.executeAction = function(action) ctx.shadowColor = "transparent"; if (node.onDrawBackground) { - node.onDrawBackground(ctx, this, this.canvas); + node.onDrawBackground(ctx, this, this.canvas, this.canvas_mouse ); } //title bg (remember, it is rendered ABOVE the node) if (render_title || title_mode == LiteGraph.TRANSPARENT_TITLE) { //title bar if (node.onDrawTitleBar) { - node.onDrawTitleBar( - ctx, - title_height, - size, - this.ds.scale, - fgcolor - ); + node.onDrawTitleBar( ctx, title_height, size, this.ds.scale, fgcolor ); } else if ( title_mode != LiteGraph.TRANSPARENT_TITLE && (node.constructor.title_color || this.render_title_colored) @@ -7647,6 +7668,28 @@ LGraphNode.prototype.executeAction = function(action) } } + //subgraph box + if (!node.flags.collapsed && node.subgraph && !node.skip_subgraph_button) { + ctx.fillStyle = "#555"; + var w = LiteGraph.NODE_TITLE_HEIGHT; + var x = node.size[0] - w; + if( shape == LiteGraph.BOX_SHAPE || low_quality) + ctx.fillRect(x+2, -w+2, w-4, w-4); + else + { + ctx.beginPath(); + ctx.roundRect(x+2, -w+2, w-4, w-4,4); + ctx.fill(); + } + ctx.fillStyle = "#333"; + ctx.beginPath(); + ctx.moveTo(x + w * 0.2, -w * 0.6); + ctx.lineTo(x + w * 0.8, -w * 0.6); + ctx.lineTo(x + w * 0.5, -w * 0.3); + ctx.fill(); + } + + //custom title render if (node.onDrawTitle) { node.onDrawTitle(ctx); } @@ -8885,6 +8928,7 @@ LGraphNode.prototype.executeAction = function(action) } if (!entries.length) { + console.log("no input entries"); return; } @@ -9754,6 +9798,341 @@ LGraphNode.prototype.executeAction = function(action) return dialog; }; + LGraphCanvas.prototype.createPanel = function(title, options) { + options = options || {}; + + var ref_window = options.window || window; + var root = document.createElement("div"); + root.className = "litegraph dialog"; + root.innerHTML = "
"; + root.header = root.querySelector(".dialog-header"); + + if(options.width) + root.style.width = options.width + (options.width.constructor === Number ? "px" : ""); + if(options.height) + root.style.height = options.height + (options.height.constructor === Number ? "px" : ""); + if(options.closable) + { + var close = document.createElement("span"); + close.innerHTML = "✕"; + close.classList.add("close"); + close.addEventListener("click",function(){ + root.close(); + }); + root.header.appendChild(close); + } + root.title_element = root.querySelector(".dialog-title"); + root.title_element.innerText = title; + root.content = root.querySelector(".dialog-content"); + root.footer = root.querySelector(".dialog-footer"); + + root.close = function() + { + this.parentNode.removeChild(this); + } + + root.clear = function() + { + this.content.innerHTML = ""; + } + + root.addHTML = function(code, classname, on_footer) + { + var elem = document.createElement("div"); + if(classname) + elem.className = classname; + elem.innerHTML = code; + if(on_footer) + root.footer.appendChild(elem); + else + root.content.appendChild(elem); + return elem; + } + + root.addButton = function( name, callback, options ) + { + var elem = document.createElement("button"); + elem.innerText = name; + elem.options = options; + elem.classList.add("btn"); + elem.addEventListener("click",callback); + root.footer.appendChild(elem); + return elem; + } + + root.addSeparator = function() + { + var elem = document.createElement("div"); + elem.className = "separator"; + root.content.appendChild(elem); + } + + root.addWidget = function( type, name, value, options, callback ) + { + options = options || {}; + var str_value = String(value); + if(type == "number") + str_value = value.toFixed(3); + + var elem = document.createElement("div"); + elem.className = "property"; + elem.innerHTML = ""; + elem.querySelector(".property_name").innerText = name; + var value_element = elem.querySelector(".property_value"); + value_element.innerText = str_value; + elem.dataset["property"] = name; + elem.dataset["type"] = options.type || type; + elem.options = options; + elem.value = value; + + //if( type == "code" ) + // elem.addEventListener("click", function(){ inner_showCodePad( node, this.dataset["property"] ); }); + if (type == "boolean") + { + elem.classList.add("boolean"); + if(value) + elem.classList.add("bool-on"); + elem.addEventListener("click", function(){ + //var v = node.properties[this.dataset["property"]]; + //node.setProperty(this.dataset["property"],!v); this.innerText = v ? "true" : "false"; + var propname = this.dataset["property"]; + this.value = !this.value; + this.classList.toggle("bool-on"); + this.querySelector(".property_value").innerText = this.value ? "true" : "false"; + innerChange(propname, this.value ); + }); + } + else if (type == "string" || type == "number") + { + value_element.setAttribute("contenteditable",true); + value_element.addEventListener("keydown", function(e){ + if(e.code == "Enter") + { + e.preventDefault(); + this.blur(); + } + }); + value_element.addEventListener("blur", function(){ + var v = this.innerText; + var propname = this.parentNode.dataset["property"]; + var proptype = this.parentNode.dataset["type"]; + if( proptype == "number") + v = Number(v); + innerChange(propname, v); + }); + } + else if (type == "enum") + value_element.addEventListener("click", function(event){ + var values = options.values || []; + var propname = this.parentNode.dataset["property"]; + var elem_that = this; + var menu = new LiteGraph.ContextMenu(values,{ + event: event, + className: "dark", + callback: inner_clicked + }, + ref_window); + function inner_clicked(v, option, event) { + //node.setProperty(propname,v); + //graphcanvas.dirty_canvas = true; + elem_that.innerText = v; + innerChange(propname,v); + return false; + } + }); + + root.content.appendChild(elem); + + function innerChange(name, value) + { + console.log("change",name,value); + //that.dirty_canvas = true; + if(options.callback) + options.callback(name,value); + if(callback) + callback(name,value); + } + + return elem; + } + + return root; + }; + + LGraphCanvas.prototype.showShowNodePanel = function( node ) + { + window.SELECTED_NODE = node; + var panel = document.querySelector("#node-panel"); + if(panel) + panel.close(); + var ref_window = this.getCanvasWindow(); + panel = this.createPanel(node.title || "",{closable: true, window: ref_window }); + panel.id = "node-panel"; + panel.node = node; + panel.classList.add("settings"); + var that = this; + var graphcanvas = this; + + function inner_refresh() + { + panel.content.innerHTML = ""; //clear + panel.addHTML(""+node.type+""+(node.constructor.desc || "")+""); + + panel.addHTML("

Properties

"); + + for(var i in node.properties) + { + var value = node.properties[i]; + var info = node.getPropertyInfo(i); + var type = info.type || "string"; + + //in case the user wants control over the side panel widget + if( node.onAddPropertyToPanel && node.onAddPropertyToPanel(i,panel) ) + continue; + + panel.addWidget( info.widget || info.type, i, value, info, function(name,value){ + node.setProperty(name,value); + graphcanvas.dirty_canvas = true; + }); + } + + panel.addSeparator(); + + if(node.onShowCustomPanelInfo) + node.onShowCustomPanelInfo(panel); + + /* + panel.addHTML("

Connections

"); + var connection_containers = panel.addHTML("
","connections"); + var inputs = connection_containers.querySelector(".inputs"); + var outputs = connection_containers.querySelector(".outputs"); + */ + + panel.addButton("Delete",function(){ + if(node.block_delete) + return; + node.graph.remove(node); + panel.close(); + }).classList.add("delete"); + } + + function inner_showCodePad( node, propname ) + { + panel.style.top = "calc( 50% - 250px)"; + panel.style.left = "calc( 50% - 400px)"; + panel.style.width = "800px"; + panel.style.height = "500px"; + + if(window.CodeFlask) //disabled for now + { + panel.content.innerHTML = "
"; + var flask = new CodeFlask( "div.code", { language: 'js' }); + flask.updateCode(node.properties[propname]); + flask.onUpdate( function(code) { + node.setProperty(propname, code); + }); + } + else + { + panel.content.innerHTML = ""; + var textarea = panel.content.querySelector("textarea"); + textarea.value = node.properties[propname]; + textarea.addEventListener("keydown", function(e){ + //console.log(e); + if(e.code == "Enter" && e.ctrlKey ) + { + console.log("Assigned"); + node.setProperty(propname, textarea.value); + } + }); + textarea.style.height = "calc(100% - 40px)"; + } + var assign = that.createButton( "Assign", null, function(){ + node.setProperty(propname, textarea.value); + }); + panel.content.appendChild(assign); + var button = that.createButton( "Close", null, function(){ + panel.style.height = ""; + inner_refresh(); + }); + button.style.float = "right"; + panel.content.appendChild(button); + } + + inner_refresh(); + + this.canvas.parentNode.appendChild( panel ); + } + + LGraphCanvas.prototype.showSubgraphPropertiesDialog = function(node) + { + console.log("showing subgraph properties dialog"); + + var old_panel = this.canvas.parentNode.querySelector(".subgraph_dialog"); + if(old_panel) + old_panel.close(); + + var panel = this.createPanel("Subgraph Inputs",{closable:true, width: 500}); + panel.node = node; + panel.classList.add("subgraph_dialog"); + + function inner_refresh() + { + panel.clear(); + + //show currents + if(node.inputs) + for(var i = 0; i < node.inputs.length; ++i) + { + var input = node.inputs[i]; + var html = "NameType"; + var elem = panel.addHTML(html,"subgraph_property"); + elem.dataset["name"] = input.name; + elem.dataset["slot"] = i; + elem.querySelector(".name").value = input.name; + elem.querySelector(".type").value = input.type; + elem.querySelector("button").addEventListener("click",function(e){ + node.removeInput( this.parentNode.dataset["slot"] ); + inner_refresh(); + }); + } + } + + //add extra + var html = " + NameType"; + var elem = panel.addHTML(html,"subgraph_property extra", true); + elem.querySelector("button").addEventListener("click", function(e){ + var elem = this.parentNode; + var name = elem.querySelector(".name").value; + var type = elem.querySelector(".type").value; + if(!name || node.findInputSlot(name) != -1) + return; + node.addInput(name,type); + elem.querySelector(".name").value = ""; + elem.querySelector(".type").value = ""; + inner_refresh(); + }); + + inner_refresh(); + this.canvas.parentNode.appendChild(panel); + return panel; + } + + LGraphCanvas.prototype.checkPanels = function() + { + if(!this.canvas) + return; + var panels = this.canvas.parentNode.querySelectorAll(".litegraph.dialog"); + for(var i = 0; i < panels.length; ++i) + { + var panel = panels[i]; + if( !panel.node ) + continue; + if( !panel.node.graph || panel.graph != this.graph ) + panel.close(); + } + } + LGraphCanvas.onMenuNodeCollapse = function(value, options, e, menu, node) { node.collapse(); }; @@ -10058,12 +10437,11 @@ LGraphNode.prototype.executeAction = function(action) callback: LGraphCanvas.onMenuNodeToSubgraph }); - if (node.removable !== false) { - options.push(null, { - content: "Remove", - callback: LGraphCanvas.onMenuNodeRemove - }); - } + options.push(null, { + content: "Remove", + disabled: !(node.removable !== false && !node.block_delete ), + callback: LGraphCanvas.onMenuNodeRemove + }); if (node.graph && node.graph.onGetNodeMenuOptions) { node.graph.onGetNodeMenuOptions(options, node); @@ -11126,6 +11504,7 @@ if (typeof exports != "undefined") { return [["enabled", "boolean"]]; }; + /* Subgraph.prototype.onDrawTitle = function(ctx) { if (this.flags.collapsed) { return; @@ -11142,6 +11521,7 @@ if (typeof exports != "undefined") { ctx.lineTo(x + w * 0.5, -w * 0.3); ctx.fill(); }; + */ Subgraph.prototype.onDblClick = function(e, pos, graphcanvas) { var that = this; @@ -11150,6 +11530,7 @@ if (typeof exports != "undefined") { }, 10); }; + /* Subgraph.prototype.onMouseDown = function(e, pos, graphcanvas) { if ( !this.flags.collapsed && @@ -11162,6 +11543,7 @@ if (typeof exports != "undefined") { }, 10); } }; + */ Subgraph.prototype.onAction = function(action, param) { this.subgraph.onAction(action, param); @@ -17518,7 +17900,7 @@ if (typeof exports != "undefined") { u_texture: 0, u_textureB: 1, value: value, - texSize: [width, height], + texSize: [width, height,1/width,1/height], time: time }) .draw(mesh); @@ -17533,7 +17915,7 @@ if (typeof exports != "undefined") { uniform sampler2D u_texture;\n\ uniform sampler2D u_textureB;\n\ varying vec2 v_coord;\n\ - uniform vec2 texSize;\n\ + uniform vec4 texSize;\n\ uniform float time;\n\ uniform float value;\n\ \n\ @@ -17570,6 +17952,20 @@ if (typeof exports != "undefined") { LGraphTextureOperation.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"); LGraphTextureOperation.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"); LGraphTextureOperation.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"); + LGraphTextureOperation.registerPreset("normalmap","\n\ + float z0 = texture2D(u_texture, uv + vec2(-texSize.z, -texSize.w) ).x;\n\ + float z1 = texture2D(u_texture, uv + vec2(0.0, -texSize.w) ).x;\n\ + float z2 = texture2D(u_texture, uv + vec2(texSize.z, -texSize.w) ).x;\n\ + float z3 = texture2D(u_texture, uv + vec2(-texSize.z, 0.0) ).x;\n\ + float z4 = color.x;\n\ + float z5 = texture2D(u_texture, uv + vec2(texSize.z, 0.0) ).x;\n\ + float z6 = texture2D(u_texture, uv + vec2(-texSize.z, texSize.w) ).x;\n\ + float z7 = texture2D(u_texture, uv + vec2(0.0, texSize.w) ).x;\n\ + float z8 = texture2D(u_texture, uv + vec2(texSize.z, texSize.w) ).x;\n\ + vec3 normal = vec3( z2 + 2.0*z4 + z7 - z0 - 2.0*z3 - z5, z5 + 2.0*z6 + z7 -z0 - 2.0*z1 - z2, 1.0 );\n\ + normal.xy *= value;\n\ + result.xyz = normalize(normal) * 0.5 + vec3(0.5);\n\ + "); LGraphTextureOperation.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"); //webglstudio stuff... @@ -17602,7 +17998,7 @@ if (typeof exports != "undefined") { }; this.properties.code = LGraphTextureShader.pixel_shader; - this._uniforms = { u_value: 1, u_color: vec4.create(), in_texture: 0, texSize: vec2.create(), time: 0 }; + this._uniforms = { u_value: 1, u_color: vec4.create(), in_texture: 0, texSize: vec4.create(), time: 0 }; } LGraphTextureShader.title = "Shader"; @@ -17768,6 +18164,8 @@ if (typeof exports != "undefined") { } uniforms.texSize[0] = w; uniforms.texSize[1] = h; + uniforms.texSize[2] = 1/w; + uniforms.texSize[3] = 1/h; uniforms.time = this.graph.getTime(); uniforms.u_value = this.properties.u_value; uniforms.u_color.set( this.properties.u_color ); @@ -17788,7 +18186,7 @@ if (typeof exports != "undefined") { \n\ varying vec2 v_coord;\n\ uniform float time; //time in seconds\n\ -uniform vec2 texSize; //tex resolution\n\ +uniform vec4 texSize; //tex resolution\n\ uniform float u_value;\n\ uniform vec4 u_color;\n\n\ void main() {\n\ @@ -18438,6 +18836,69 @@ void main() {\n\ LGraphTextureDownsample ); + + + function LGraphTextureResize() { + this.addInput("Texture", "Texture"); + this.addOutput("", "Texture"); + this.properties = { + size: [512,512], + generate_mipmaps: false, + precision: LGraphTexture.DEFAULT + }; + } + + LGraphTextureResize.title = "Resize"; + LGraphTextureResize.desc = "Resize Texture"; + LGraphTextureResize.widgets_info = { + iterations: { type: "number", step: 1, precision: 0, min: 0 }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureResize.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex && !this._temp_texture) { + return; + } + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + //we do not allow any texture different than texture 2D + if (!tex || tex.texture_type !== GL.TEXTURE_2D) { + return; + } + + var width = this.properties.size[0] | 0; + var height = this.properties.size[1] | 0; + if(width == 0) + width = tex.width; + if(height == 0) + height = tex.height; + var type = tex.type; + if (this.properties.precision === LGraphTexture.LOW) { + type = gl.UNSIGNED_BYTE; + } else if (this.properties.precision === LGraphTexture.HIGH) { + type = gl.HIGH_PRECISION_FORMAT; + } + + if( !this._texture || this._texture.width != width || this._texture.height != height || this._texture.type != type ) + this._texture = new GL.Texture( width, height, { type: type } ); + + tex.copyTo( this._texture ); + + if (this.properties.generate_mipmaps) { + this._texture.bind(0); + gl.generateMipmap(this._texture.texture_type); + this._texture.unbind(0); + } + + this.setOutputData(0, this._texture); + }; + + LiteGraph.registerNodeType( "texture/resize", LGraphTextureResize ); + // Texture Average ***************************************** function LGraphTextureAverage() { this.addInput("Texture", "Texture"); @@ -19105,7 +19566,7 @@ void main() {\n\ this.addInput("Texture", "Texture"); this.addInput("Atlas", "Texture"); this.addOutput("", "Texture"); - this.properties = { enabled: true, num_row_symbols: 4, symbol_size: 16, brightness: 1, colorize: false, filter: false, invert: false, precision: LGraphTexture.DEFAULT, texture: null }; + this.properties = { enabled: true, num_row_symbols: 4, symbol_size: 16, brightness: 1, colorize: false, filter: false, invert: false, precision: LGraphTexture.DEFAULT, generate_mipmaps: false, texture: null }; if (!LGraphTextureEncode._shader) { LGraphTextureEncode._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, LGraphTextureEncode.pixel_shader ); @@ -19181,6 +19642,12 @@ void main() {\n\ tex.toViewport(LGraphTextureEncode._shader, uniforms); }); + if (this.properties.generate_mipmaps) { + this._tex.bind(0); + gl.generateMipmap(this._tex.texture_type); + this._tex.unbind(0); + } + this.setOutputData(0, this._tex); }; diff --git a/css/litegraph-editor.css b/css/litegraph-editor.css index acf575e1f..d286949b6 100755 --- a/css/litegraph-editor.css +++ b/css/litegraph-editor.css @@ -93,7 +93,7 @@ /* BUTTONS **********************/ -.litegraph-editor button { +.litegraph-editor .btn { /*font-family: "Metro Light";*/ color: #ccc; font-size: 20px; @@ -193,157 +193,6 @@ background-image: url("../demo/imgs/load-progress-full.png"); } -.litegraph-editor .dialog { - position: absolute; - top: 50%; - left: 50%; - margin-top: -150px; - margin-left: -200px; - - background-color: #222222; - - min-width: 400px; - min-height: 200px; - box-shadow: 0 0 4px #111; - border-radius: 6px; -} - -.litegraph-editor .dialog.settings { - left: 10px; - top: 10px; - height: calc( 100% - 20px ); - margin: auto; -} - -.litegraph-editor .dialog .close { - float: right; - margin: 4px; - margin-right: 10px; - cursor: pointer; - font-size: 1.4em; -} - -.litegraph-editor .dialog .close:hover { - color: white; -} - -.litegraph-editor .dialog .dialog-header { - color: #AAA; - border-bottom: 1px solid #161616; -} - -.litegraph-editor .dialog .dialog-header { height: 40px; } -.litegraph-editor .dialog .dialog-footer { height: 50px; padding: 10px; border-top: 1px solid #1a1a1a;} - -.litegraph-editor .dialog .dialog-header .dialog-title { - font: 20px "Arial"; - margin: 4px; - padding: 4px 10px; - display: inline-block; -} - -.litegraph-editor .dialog .dialog-content { - height: calc(100% - 90px); - width: calc(100% - 10px); - /*background-color: black;*/ - padding: 4px; - display: inline-block; - color: #AAA; -} - -.litegraph-editor .dialog .dialog-content h3 { - margin: 10px; -} - -.litegraph-editor .dialog .dialog-content .connections { - flex-direction: row; -} - -.litegraph-editor .dialog .dialog-content .connections .connections_side { - width: calc(50% - 5px); - min-height: 100px; - background-color: black; - display: flex; -} - -.litegraph-editor .dialog .node_type { - font-size: 1.2em; - display: block; - margin: 10px; -} - -.litegraph-editor .dialog .node_desc { - opacity: 0.5; - display: block; - margin: 10px; -} - -.litegraph-editor .dialog .separator { - display: block; - width: calc( 100% - 4px ); - height: 1px; - border-top: 1px solid #000; - border-bottom: 1px solid #333; - margin: 10px 2px; - padding: 0; -} - -.litegraph-editor .dialog .property { - margin-bottom: 2px; - padding: 0; -} - -.litegraph-editor .dialog .property_name { - color: #737373; - display: inline-block; - text-align: left; - vertical-align: top; - width: 120px; - padding-left: 4px; - overflow: hidden; -} - -.litegraph-editor .dialog .property_value { - display: inline-block; - text-align: right; - color: #AAA; - background-color: #1A1A1A; - width: calc( 100% - 122px ); - max-height: 300px; - padding: 4px; - padding-right: 12px; - overflow: hidden; - cursor: pointer; - border-radius: 3px; -} - -.litegraph-editor .dialog .property_value:hover { - color: white; -} - -.litegraph-editor .dialog .property.boolean .property_value { - padding-right: 30px; -} - -.litegraph-editor .dialog button { - border-radius: 4px; - padding: 4px 20px; - margin-left: 0px; - background-color: #060606; - color: #8e8e8e; -} - -.litegraph-editor .dialog button:hover { - background-color: #111; - color: #FFF; -} - -.litegraph-editor .dialog button.delete:hover { - background-color: #F33; - color: black; -} - - .litegraph-editor textarea.code, .litegraph-editor div.code { height: 100%; width: 100%; diff --git a/css/litegraph.css b/css/litegraph.css index 7b34e3e89..6f03567a4 100755 --- a/css/litegraph.css +++ b/css/litegraph.css @@ -5,6 +5,7 @@ user-select: none; -moz-user-select: none; -webkit-user-select: none; + outline: none; } .litegraph.litecontextmenu { @@ -207,6 +208,234 @@ color: black; } +/* DIALOGs ******/ + +.litegraph .dialog { + position: absolute; + top: 50%; + left: 50%; + margin-top: -150px; + margin-left: -200px; + + background-color: #2A2A2A; + + min-width: 400px; + min-height: 200px; + box-shadow: 0 0 4px #111; + border-radius: 6px; +} + +.litegraph .dialog.settings { + left: 10px; + top: 10px; + height: calc( 100% - 20px ); + margin: auto; +} + +.litegraph .dialog .close { + float: right; + margin: 4px; + margin-right: 10px; + cursor: pointer; + font-size: 1.4em; +} + +.litegraph .dialog .close:hover { + color: white; +} + +.litegraph .dialog .dialog-header { + color: #AAA; + border-bottom: 1px solid #161616; +} + +.litegraph .dialog .dialog-header { height: 40px; } +.litegraph .dialog .dialog-footer { height: 50px; padding: 10px; border-top: 1px solid #1a1a1a;} + +.litegraph .dialog .dialog-header .dialog-title { + font: 20px "Arial"; + margin: 4px; + padding: 4px 10px; + display: inline-block; +} + +.litegraph .dialog .dialog-content { + height: calc(100% - 90px); + width: calc(100% - 10px); + min-height: 100px; + /*background-color: black;*/ + padding: 4px; + display: inline-block; + color: #AAA; +} + +.litegraph .dialog .dialog-content h3 { + margin: 10px; +} + +.litegraph .dialog .dialog-content .connections { + flex-direction: row; +} + +.litegraph .dialog .dialog-content .connections .connections_side { + width: calc(50% - 5px); + min-height: 100px; + background-color: black; + display: flex; +} + +.litegraph .dialog .node_type { + font-size: 1.2em; + display: block; + margin: 10px; +} + +.litegraph .dialog .node_desc { + opacity: 0.5; + display: block; + margin: 10px; +} + +.litegraph .dialog .separator { + display: block; + width: calc( 100% - 4px ); + height: 1px; + border-top: 1px solid #000; + border-bottom: 1px solid #333; + margin: 10px 2px; + padding: 0; +} + +.litegraph .dialog .property { + margin-bottom: 2px; + padding: 0; +} + +.litegraph .dialog .property_name { + color: #737373; + display: inline-block; + text-align: left; + vertical-align: top; + width: 120px; + padding-left: 4px; + overflow: hidden; +} + +.litegraph .dialog .property_value { + display: inline-block; + text-align: right; + color: #AAA; + background-color: #1A1A1A; + width: calc( 100% - 122px ); + max-height: 300px; + padding: 4px; + padding-right: 12px; + overflow: hidden; + cursor: pointer; + border-radius: 3px; +} + +.litegraph .dialog .property_value:hover { + color: white; +} + +.litegraph .dialog .property.boolean .property_value { + padding-right: 30px; +} + +.litegraph .dialog .btn { + border-radius: 4px; + padding: 4px 20px; + margin-left: 0px; + background-color: #060606; + color: #8e8e8e; +} + +.litegraph .dialog .btn:hover { + background-color: #111; + color: #FFF; +} + +.litegraph .dialog .btn.delete:hover { + background-color: #F33; + color: black; +} + +.litegraph .subgraph_property { + padding-bottom: 4px; +} + +.litegraph .subgraph_property:hover { + background-color: #333; +} + +.litegraph .subgraph_property.extra { + margin-top: 8px; +} + +.litegraph .subgraph_property span.name { + font-size: 1.3em; + padding-left: 4px; +} + +.litegraph .subgraph_property span.type { + opacity: 0.5; + margin-right: 20px; + padding-left: 4px; +} + +.litegraph .subgraph_property span.label { + display: inline-block; + width: 60px; + padding: 0px 10px; +} + +.litegraph .subgraph_property input { + width: 140px; + color: #999; + background-color: #1A1A1A; + border-radius: 4px; + border: 0; + margin-right: 10px; + padding: 4px; + padding-left: 10px; +} + +.litegraph .subgraph_property button { + background-color: #1c1c1c; + color: #aaa; + border: 0; + border-radius: 2px; + padding: 4px 10px; + cursor: pointer; +} + +.litegraph .subgraph_property.extra { + color: #ccc; +} + +.litegraph .subgraph_property.extra input { + background-color: #111; +} + +.litegraph .bullet_icon { + margin-left: 10px; + border-radius: 10px; + width: 12px; + height: 12px; + background-color: #666; + display: inline-block; + margin-top: 2px; + margin-right: 4px; + transition: background-color 0.1s ease 0s; + -moz-transition: background-color 0.1s ease 0s; +} + +.litegraph .bullet_icon:hover { + background-color: #698; + cursor: pointer; +} + /* OLD */ .graphcontextmenu { diff --git a/demo/js/code.js b/demo/js/code.js index 7038567a6..4f6ecb712 100644 --- a/demo/js/code.js +++ b/demo/js/code.js @@ -1,7 +1,7 @@ var webgl_canvas = null; LiteGraph.node_images_path = "../nodes_data/"; -var editor = new LiteGraph.Editor("main",{miniwindow:true}); +var editor = new LiteGraph.Editor("main",{miniwindow:false}); window.graphcanvas = editor.graphcanvas; window.graph = editor.graph; window.addEventListener("resize", function() { editor.graphcanvas.resize(); } ); @@ -17,7 +17,7 @@ LiteGraph.allow_scripts = true; //create scene selector var elem = document.createElement("span"); elem.className = "selector"; -elem.innerHTML = "Demo | "; +elem.innerHTML = "Demo | "; editor.tools.appendChild(elem); var select = elem.querySelector("select"); select.addEventListener("change", function(e){ @@ -102,6 +102,7 @@ function enableWebGL() "js/libs/litegl.js", "../src/nodes/gltextures.js", "../src/nodes/glfx.js", + "../src/nodes/shaders.js", "../src/nodes/geometry.js" ]; diff --git a/demo/style.css b/demo/style.css index ecf79c0e7..02f642f79 100755 --- a/demo/style.css +++ b/demo/style.css @@ -123,7 +123,7 @@ label { color: #AAF; } -input,textarea { +.header input { color: #EEE; background-color: #555; font-size: 1.2em; diff --git a/src/litegraph-editor.js b/src/litegraph-editor.js index 25a40f7bf..e8ddb80b5 100755 --- a/src/litegraph-editor.js +++ b/src/litegraph-editor.js @@ -9,7 +9,7 @@ function Editor(container_id, options) { var root = document.createElement("div"); this.root = root; - root.className = "litegraph-editor"; + root.className = "litegraph litegraph-editor"; root.innerHTML = html; this.tools = root.querySelector(".tools"); @@ -27,7 +27,6 @@ function Editor(container_id, options) { }; graphcanvas.onDropItem = this.onDropItem.bind(this); - graphcanvas.onShowNodePanel = this.onShowNodePanel.bind(this); //add stuff //this.addToolsButton("loadsession_button","Load","imgs/icon-load.png", this.onLoadButton.bind(this), ".tools-left" ); @@ -115,157 +114,12 @@ Editor.prototype.addToolsButton = function( id, name, icon_url, callback, contai this.root.querySelector(container).appendChild(button); }; -Editor.prototype.createPanel = function(title, options) { - options = options || {}; - - var ref_window = options.window || window; - var root = document.createElement("div"); - root.className = "dialog"; - root.innerHTML = "
"; - root.header = root.querySelector(".dialog-header"); - if(options.closable) - { - var close = document.createElement("span"); - close.innerHTML = "✕"; - close.classList.add("close"); - close.addEventListener("click",function(){ - root.close(); - }); - root.header.appendChild(close); - } - root.title_element = root.querySelector(".dialog-title"); - root.title_element.innerText = title; - root.content = root.querySelector(".dialog-content"); - root.footer = root.querySelector(".dialog-footer"); - root.close = function() - { - this.parentNode.removeChild(this); - } - - root.addHTML = function(code, classname) - { - var elem = document.createElement("div"); - if(classname) - elem.className = classname; - elem.innerHTML = code; - root.content.appendChild(elem); - return elem; - } - - root.addButton = function( name, callback, options ) - { - var elem = document.createElement("button"); - elem.innerText = name; - elem.options = options; - elem.addEventListener("click",callback); - root.footer.appendChild(elem); - return elem; - } - - root.addSeparator = function() - { - var elem = document.createElement("div"); - elem.className = "separator"; - root.content.appendChild(elem); - } - - root.addWidget = function( type, name, value, options, callback ) - { - options = options || {}; - var str_value = String(value); - if(type == "number") - str_value = value.toFixed(3); - - var elem = document.createElement("div"); - elem.className = "property"; - elem.innerHTML = ""; - elem.querySelector(".property_name").innerText = name; - var value_element = elem.querySelector(".property_value"); - value_element.innerText = str_value; - elem.dataset["property"] = name; - elem.dataset["type"] = options.type || type; - elem.options = options; - elem.value = value; - - //if( type == "code" ) - // elem.addEventListener("click", function(){ inner_showCodePad( node, this.dataset["property"] ); }); - if (type == "boolean") - { - elem.classList.add("boolean"); - if(value) - elem.classList.add("bool-on"); - elem.addEventListener("click", function(){ - //var v = node.properties[this.dataset["property"]]; - //node.setProperty(this.dataset["property"],!v); this.innerText = v ? "true" : "false"; - var propname = this.dataset["property"]; - this.value = !this.value; - this.classList.toggle("bool-on"); - this.querySelector(".property_value").innerText = this.value ? "true" : "false"; - innerChange(propname, this.value ); - }); - } - else if (type == "string" || type == "number") - { - value_element.setAttribute("contenteditable",true); - value_element.addEventListener("keydown", function(e){ - if(e.code == "Enter") - { - e.preventDefault(); - this.blur(); - } - }); - value_element.addEventListener("blur", function(){ - var v = this.innerText; - var propname = this.parentNode.dataset["property"]; - var proptype = this.parentNode.dataset["type"]; - if( proptype == "number") - v = Number(v); - innerChange(propname, v); - }); - } - else if (type == "enum") - value_element.addEventListener("click", function(event){ - var values = options.values || []; - var propname = this.parentNode.dataset["property"]; - var elem_that = this; - var menu = new LiteGraph.ContextMenu(values,{ - event: event, - className: "dark", - callback: inner_clicked - }, - ref_window); - function inner_clicked(v, option, event) { - //node.setProperty(propname,v); - //graphcanvas.dirty_canvas = true; - elem_that.innerText = v; - innerChange(propname,v); - return false; - } - }); - - root.content.appendChild(elem); - - function innerChange(name, value) - { - console.log("change",name,value); - //that.dirty_canvas = true; - if(options.callback) - options.callback(name,value); - if(callback) - callback(name,value); - } - - return elem; - } - - return root; -}; - Editor.prototype.createButton = function(name, icon_url, callback) { var button = document.createElement("button"); if (icon_url) { button.innerHTML = " "; } + button.classList.add("btn"); button.innerHTML += name; if(callback) button.addEventListener("click", callback ); @@ -273,7 +127,7 @@ Editor.prototype.createButton = function(name, icon_url, callback) { }; Editor.prototype.onLoadButton = function() { - var panel = this.createPanel("Load session",{closable:true}); + var panel = this.graphcanvas.createPanel("Load session",{closable:true}); //TO DO this.root.appendChild(panel); @@ -332,107 +186,6 @@ Editor.prototype.onDropItem = function(e) } } -//shows the left side panel with the node info -Editor.prototype.onShowNodePanel = function(node) -{ - window.SELECTED_NODE = node; - var panel = document.querySelector("#node-panel"); - if(panel) - panel.close(); - var ref_window = this.graphcanvas.getCanvasWindow(); - panel = this.createPanel(node.title || "",{closable: true, window: ref_window }); - panel.id = "node-panel"; - panel.classList.add("settings"); - var that = this; - var graphcanvas = this.graphcanvas; - - function inner_refresh() - { - panel.content.innerHTML = ""; //clear - panel.addHTML(""+node.type+""+(node.constructor.desc || "")+""); - - panel.addHTML("

Properties

"); - - for(var i in node.properties) - { - var value = node.properties[i]; - var info = node.getPropertyInfo(i); - var type = info.type || "string"; - - //in case the user wants control over the side panel widget - if( node.onAddPropertyToPanel && node.onAddPropertyToPanel(i,panel) ) - continue; - - panel.addWidget( info.widget || info.type, i, value, info, function(name,value){ - node.setProperty(name,value); - graphcanvas.dirty_canvas = true; - }); - } - - panel.addSeparator(); - - /* - panel.addHTML("

Connections

"); - var connection_containers = panel.addHTML("
","connections"); - var inputs = connection_containers.querySelector(".inputs"); - var outputs = connection_containers.querySelector(".outputs"); - */ - - - panel.addButton("Delete",function(){ - node.graph.remove(node); - panel.close(); - }).classList.add("delete"); - } - - function inner_showCodePad( node, propname ) - { - panel.style.top = "calc( 50% - 250px)"; - panel.style.left = "calc( 50% - 400px)"; - panel.style.width = "800px"; - panel.style.height = "500px"; - - if(window.CodeFlask) //disabled for now - { - panel.content.innerHTML = "
"; - var flask = new CodeFlask( "div.code", { language: 'js' }); - flask.updateCode(node.properties[propname]); - flask.onUpdate( function(code) { - node.setProperty(propname, code); - }); - } - else - { - panel.content.innerHTML = ""; - var textarea = panel.content.querySelector("textarea"); - textarea.value = node.properties[propname]; - textarea.addEventListener("keydown", function(e){ - //console.log(e); - if(e.code == "Enter" && e.ctrlKey ) - { - console.log("Assigned"); - node.setProperty(propname, textarea.value); - } - }); - textarea.style.height = "calc(100% - 40px)"; - } - var assign = that.createButton( "Assign", null, function(){ - node.setProperty(propname, textarea.value); - }); - panel.content.appendChild(assign); - var button = that.createButton( "Close", null, function(){ - panel.style.height = ""; - inner_refresh(); - }); - button.style.float = "right"; - panel.content.appendChild(button); - } - - inner_refresh(); - - this.content.appendChild( panel ); -} - Editor.prototype.goFullscreen = function() { if (this.root.requestFullscreen) { this.root.requestFullscreen(Element.ALLOW_KEYBOARD_INPUT); @@ -466,7 +219,7 @@ Editor.prototype.addMiniWindow = function(w, h) { var canvas = miniwindow.querySelector("canvas"); var that = this; - var graphcanvas = new LGraphCanvas(canvas, this.graph); + var graphcanvas = new LGraphCanvas( canvas, this.graph ); graphcanvas.show_info = false; graphcanvas.background_image = "imgs/grid.png"; graphcanvas.scale = 0.25; diff --git a/src/litegraph.js b/src/litegraph.js index b2fcda9fb..6ca05a999 100755 --- a/src/litegraph.js +++ b/src/litegraph.js @@ -1380,6 +1380,9 @@ this.onNodeRemoved(node); } + //close panels + this.sendActionToCanvas("checkPanels"); + this.setDirtyCanvas(true, true); this.change(); @@ -2672,6 +2675,23 @@ return null; }; + /** + * Returns the link info in the connection of an input slot + * @method getInputLink + * @param {number} slot + * @return {LLink} object or null + */ + LGraphNode.prototype.getInputLink = function(slot) { + if (!this.inputs) { + return null; + } + if (slot < this.inputs.length) { + var slot_info = this.inputs[slot]; + return this.graph.links[ slot_info.link ]; + } + return null; + }; + /** * returns the node connected in the input slot * @method getInputNode @@ -3088,9 +3108,11 @@ } this.inputs.push(o); this.setSize( this.computeSize() ); + if (this.onInputAdded) { this.onInputAdded(o); } + this.setDirtyCanvas(true, true); return o; }; @@ -3130,7 +3152,7 @@ */ LGraphNode.prototype.removeInput = function(slot) { this.disconnectInput(slot); - this.inputs.splice(slot, 1); + var slot_info = this.inputs.splice(slot, 1); for (var i = slot; i < this.inputs.length; ++i) { if (!this.inputs[i]) { continue; @@ -3143,7 +3165,7 @@ } this.setSize( this.computeSize() ); if (this.onInputRemoved) { - this.onInputRemoved(slot); + this.onInputRemoved(slot, slot_info[0] ); } this.setDirtyCanvas(true, true); }; @@ -4708,6 +4730,7 @@ LGraphNode.prototype.executeAction = function(action) } graph.attachCanvas(this); + this.checkPanels(); this.setDirty(true, true); }; @@ -4839,6 +4862,7 @@ LGraphNode.prototype.executeAction = function(action) } var canvas = this.canvas; + var ref_window = this.getCanvasWindow(); var document = ref_window.document; //hack used when moving canvas between windows @@ -5241,6 +5265,7 @@ LGraphNode.prototype.executeAction = function(action) //it wasn't clicked on the links boxes if (!skip_action) { var block_drag_node = false; + var pos = [e.canvasX - node.pos[0], e.canvasY - node.pos[1]]; //widgets var widget = this.processNodeWidgets( node, this.canvas_mouse, e ); @@ -5253,18 +5278,31 @@ LGraphNode.prototype.executeAction = function(action) if (is_double_click && this.selected_nodes[node.id]) { //double click node if (node.onDblClick) { - node.onDblClick( e, [ e.canvasX - node.pos[0], e.canvasY - node.pos[1] ], this ); + node.onDblClick( e, pos, this ); } this.processNodeDblClicked(node); block_drag_node = true; } //if do not capture mouse - if ( node.onMouseDown && node.onMouseDown( e, [e.canvasX - node.pos[0], e.canvasY - node.pos[1]], this ) ) { - block_drag_node = true; - } else if (this.live_mode) { - clicking_canvas_bg = true; + if ( node.onMouseDown && node.onMouseDown( e, pos, this ) ) { block_drag_node = true; + } else { + //open subgraph button + if(node.subgraph && !node.skip_subgraph_button) + { + if ( !node.flags.collapsed && pos[0] > node.size[0] - LiteGraph.NODE_TITLE_HEIGHT && pos[1] < 0 ) { + var that = this; + setTimeout(function() { + that.openSubgraph(node.subgraph); + }, 10); + } + } + + if (this.live_mode) { + clicking_canvas_bg = true; + block_drag_node = true; + } } if (!block_drag_node) { @@ -5458,6 +5496,10 @@ LGraphNode.prototype.executeAction = function(action) //mouse over a node if (node) { + + if(node.redraw_on_mouse) + this.dirty_canvas = true; + //this.canvas.style.cursor = "move"; if (!node.mouseOver) { //mouse enter @@ -6198,6 +6240,10 @@ LGraphNode.prototype.executeAction = function(action) if (this.onShowNodePanel) { this.onShowNodePanel(n); } + else + { + this.showShowNodePanel(n); + } if (this.onNodeDblClicked) { this.onNodeDblClicked(n); @@ -6348,6 +6394,10 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.prototype.deleteSelectedNodes = function() { for (var i in this.selected_nodes) { var node = this.selected_nodes[i]; + + if(node.block_delete) + continue; + //autoconnect when possible (very basic, only takes into account first input-output) if(node.inputs && node.inputs.length && node.outputs && node.outputs.length && LiteGraph.isValidConnection( node.inputs[0].type, node.outputs[0].type ) && node.inputs[0].link && node.outputs[0].links && node.outputs[0].links.length ) { @@ -6970,14 +7020,8 @@ LGraphNode.prototype.executeAction = function(action) var glow = false; this.current_node = node; - var color = - node.color || - node.constructor.color || - LiteGraph.NODE_DEFAULT_COLOR; - var bgcolor = - node.bgcolor || - node.constructor.bgcolor || - LiteGraph.NODE_DEFAULT_BGCOLOR; + var color = node.color || node.constructor.color || LiteGraph.NODE_DEFAULT_COLOR; + var bgcolor = node.bgcolor || node.constructor.bgcolor || LiteGraph.NODE_DEFAULT_BGCOLOR; //shadow and glow if (node.mouseOver) { @@ -7491,20 +7535,14 @@ LGraphNode.prototype.executeAction = function(action) ctx.shadowColor = "transparent"; if (node.onDrawBackground) { - node.onDrawBackground(ctx, this, this.canvas); + node.onDrawBackground(ctx, this, this.canvas, this.canvas_mouse ); } //title bg (remember, it is rendered ABOVE the node) if (render_title || title_mode == LiteGraph.TRANSPARENT_TITLE) { //title bar if (node.onDrawTitleBar) { - node.onDrawTitleBar( - ctx, - title_height, - size, - this.ds.scale, - fgcolor - ); + node.onDrawTitleBar( ctx, title_height, size, this.ds.scale, fgcolor ); } else if ( title_mode != LiteGraph.TRANSPARENT_TITLE && (node.constructor.title_color || this.render_title_colored) @@ -7645,6 +7683,28 @@ LGraphNode.prototype.executeAction = function(action) } } + //subgraph box + if (!node.flags.collapsed && node.subgraph && !node.skip_subgraph_button) { + ctx.fillStyle = "#555"; + var w = LiteGraph.NODE_TITLE_HEIGHT; + var x = node.size[0] - w; + if( shape == LiteGraph.BOX_SHAPE || low_quality) + ctx.fillRect(x+2, -w+2, w-4, w-4); + else + { + ctx.beginPath(); + ctx.roundRect(x+2, -w+2, w-4, w-4,4); + ctx.fill(); + } + ctx.fillStyle = "#333"; + ctx.beginPath(); + ctx.moveTo(x + w * 0.2, -w * 0.6); + ctx.lineTo(x + w * 0.8, -w * 0.6); + ctx.lineTo(x + w * 0.5, -w * 0.3); + ctx.fill(); + } + + //custom title render if (node.onDrawTitle) { node.onDrawTitle(ctx); } @@ -8883,6 +8943,7 @@ LGraphNode.prototype.executeAction = function(action) } if (!entries.length) { + console.log("no input entries"); return; } @@ -9752,6 +9813,341 @@ LGraphNode.prototype.executeAction = function(action) return dialog; }; + LGraphCanvas.prototype.createPanel = function(title, options) { + options = options || {}; + + var ref_window = options.window || window; + var root = document.createElement("div"); + root.className = "litegraph dialog"; + root.innerHTML = "
"; + root.header = root.querySelector(".dialog-header"); + + if(options.width) + root.style.width = options.width + (options.width.constructor === Number ? "px" : ""); + if(options.height) + root.style.height = options.height + (options.height.constructor === Number ? "px" : ""); + if(options.closable) + { + var close = document.createElement("span"); + close.innerHTML = "✕"; + close.classList.add("close"); + close.addEventListener("click",function(){ + root.close(); + }); + root.header.appendChild(close); + } + root.title_element = root.querySelector(".dialog-title"); + root.title_element.innerText = title; + root.content = root.querySelector(".dialog-content"); + root.footer = root.querySelector(".dialog-footer"); + + root.close = function() + { + this.parentNode.removeChild(this); + } + + root.clear = function() + { + this.content.innerHTML = ""; + } + + root.addHTML = function(code, classname, on_footer) + { + var elem = document.createElement("div"); + if(classname) + elem.className = classname; + elem.innerHTML = code; + if(on_footer) + root.footer.appendChild(elem); + else + root.content.appendChild(elem); + return elem; + } + + root.addButton = function( name, callback, options ) + { + var elem = document.createElement("button"); + elem.innerText = name; + elem.options = options; + elem.classList.add("btn"); + elem.addEventListener("click",callback); + root.footer.appendChild(elem); + return elem; + } + + root.addSeparator = function() + { + var elem = document.createElement("div"); + elem.className = "separator"; + root.content.appendChild(elem); + } + + root.addWidget = function( type, name, value, options, callback ) + { + options = options || {}; + var str_value = String(value); + if(type == "number") + str_value = value.toFixed(3); + + var elem = document.createElement("div"); + elem.className = "property"; + elem.innerHTML = ""; + elem.querySelector(".property_name").innerText = name; + var value_element = elem.querySelector(".property_value"); + value_element.innerText = str_value; + elem.dataset["property"] = name; + elem.dataset["type"] = options.type || type; + elem.options = options; + elem.value = value; + + //if( type == "code" ) + // elem.addEventListener("click", function(){ inner_showCodePad( node, this.dataset["property"] ); }); + if (type == "boolean") + { + elem.classList.add("boolean"); + if(value) + elem.classList.add("bool-on"); + elem.addEventListener("click", function(){ + //var v = node.properties[this.dataset["property"]]; + //node.setProperty(this.dataset["property"],!v); this.innerText = v ? "true" : "false"; + var propname = this.dataset["property"]; + this.value = !this.value; + this.classList.toggle("bool-on"); + this.querySelector(".property_value").innerText = this.value ? "true" : "false"; + innerChange(propname, this.value ); + }); + } + else if (type == "string" || type == "number") + { + value_element.setAttribute("contenteditable",true); + value_element.addEventListener("keydown", function(e){ + if(e.code == "Enter") + { + e.preventDefault(); + this.blur(); + } + }); + value_element.addEventListener("blur", function(){ + var v = this.innerText; + var propname = this.parentNode.dataset["property"]; + var proptype = this.parentNode.dataset["type"]; + if( proptype == "number") + v = Number(v); + innerChange(propname, v); + }); + } + else if (type == "enum") + value_element.addEventListener("click", function(event){ + var values = options.values || []; + var propname = this.parentNode.dataset["property"]; + var elem_that = this; + var menu = new LiteGraph.ContextMenu(values,{ + event: event, + className: "dark", + callback: inner_clicked + }, + ref_window); + function inner_clicked(v, option, event) { + //node.setProperty(propname,v); + //graphcanvas.dirty_canvas = true; + elem_that.innerText = v; + innerChange(propname,v); + return false; + } + }); + + root.content.appendChild(elem); + + function innerChange(name, value) + { + console.log("change",name,value); + //that.dirty_canvas = true; + if(options.callback) + options.callback(name,value); + if(callback) + callback(name,value); + } + + return elem; + } + + return root; + }; + + LGraphCanvas.prototype.showShowNodePanel = function( node ) + { + window.SELECTED_NODE = node; + var panel = document.querySelector("#node-panel"); + if(panel) + panel.close(); + var ref_window = this.getCanvasWindow(); + panel = this.createPanel(node.title || "",{closable: true, window: ref_window }); + panel.id = "node-panel"; + panel.node = node; + panel.classList.add("settings"); + var that = this; + var graphcanvas = this; + + function inner_refresh() + { + panel.content.innerHTML = ""; //clear + panel.addHTML(""+node.type+""+(node.constructor.desc || "")+""); + + panel.addHTML("

Properties

"); + + for(var i in node.properties) + { + var value = node.properties[i]; + var info = node.getPropertyInfo(i); + var type = info.type || "string"; + + //in case the user wants control over the side panel widget + if( node.onAddPropertyToPanel && node.onAddPropertyToPanel(i,panel) ) + continue; + + panel.addWidget( info.widget || info.type, i, value, info, function(name,value){ + node.setProperty(name,value); + graphcanvas.dirty_canvas = true; + }); + } + + panel.addSeparator(); + + if(node.onShowCustomPanelInfo) + node.onShowCustomPanelInfo(panel); + + /* + panel.addHTML("

Connections

"); + var connection_containers = panel.addHTML("
","connections"); + var inputs = connection_containers.querySelector(".inputs"); + var outputs = connection_containers.querySelector(".outputs"); + */ + + panel.addButton("Delete",function(){ + if(node.block_delete) + return; + node.graph.remove(node); + panel.close(); + }).classList.add("delete"); + } + + function inner_showCodePad( node, propname ) + { + panel.style.top = "calc( 50% - 250px)"; + panel.style.left = "calc( 50% - 400px)"; + panel.style.width = "800px"; + panel.style.height = "500px"; + + if(window.CodeFlask) //disabled for now + { + panel.content.innerHTML = "
"; + var flask = new CodeFlask( "div.code", { language: 'js' }); + flask.updateCode(node.properties[propname]); + flask.onUpdate( function(code) { + node.setProperty(propname, code); + }); + } + else + { + panel.content.innerHTML = ""; + var textarea = panel.content.querySelector("textarea"); + textarea.value = node.properties[propname]; + textarea.addEventListener("keydown", function(e){ + //console.log(e); + if(e.code == "Enter" && e.ctrlKey ) + { + console.log("Assigned"); + node.setProperty(propname, textarea.value); + } + }); + textarea.style.height = "calc(100% - 40px)"; + } + var assign = that.createButton( "Assign", null, function(){ + node.setProperty(propname, textarea.value); + }); + panel.content.appendChild(assign); + var button = that.createButton( "Close", null, function(){ + panel.style.height = ""; + inner_refresh(); + }); + button.style.float = "right"; + panel.content.appendChild(button); + } + + inner_refresh(); + + this.canvas.parentNode.appendChild( panel ); + } + + LGraphCanvas.prototype.showSubgraphPropertiesDialog = function(node) + { + console.log("showing subgraph properties dialog"); + + var old_panel = this.canvas.parentNode.querySelector(".subgraph_dialog"); + if(old_panel) + old_panel.close(); + + var panel = this.createPanel("Subgraph Inputs",{closable:true, width: 500}); + panel.node = node; + panel.classList.add("subgraph_dialog"); + + function inner_refresh() + { + panel.clear(); + + //show currents + if(node.inputs) + for(var i = 0; i < node.inputs.length; ++i) + { + var input = node.inputs[i]; + var html = " "; + var elem = panel.addHTML(html,"subgraph_property"); + elem.dataset["name"] = input.name; + elem.dataset["slot"] = i; + elem.querySelector(".name").innerText = input.name; + elem.querySelector(".type").innerText = input.type; + elem.querySelector("button").addEventListener("click",function(e){ + node.removeInput( Number( this.parentNode.dataset["slot"] ) ); + inner_refresh(); + }); + } + } + + //add extra + var html = " + NameType"; + var elem = panel.addHTML(html,"subgraph_property extra", true); + elem.querySelector("button").addEventListener("click", function(e){ + var elem = this.parentNode; + var name = elem.querySelector(".name").value; + var type = elem.querySelector(".type").value; + if(!name || node.findInputSlot(name) != -1) + return; + node.addInput(name,type); + elem.querySelector(".name").value = ""; + elem.querySelector(".type").value = ""; + inner_refresh(); + }); + + inner_refresh(); + this.canvas.parentNode.appendChild(panel); + return panel; + } + + LGraphCanvas.prototype.checkPanels = function() + { + if(!this.canvas) + return; + var panels = this.canvas.parentNode.querySelectorAll(".litegraph.dialog"); + for(var i = 0; i < panels.length; ++i) + { + var panel = panels[i]; + if( !panel.node ) + continue; + if( !panel.node.graph || panel.graph != this.graph ) + panel.close(); + } + } + LGraphCanvas.onMenuNodeCollapse = function(value, options, e, menu, node) { node.collapse(); }; @@ -10056,12 +10452,11 @@ LGraphNode.prototype.executeAction = function(action) callback: LGraphCanvas.onMenuNodeToSubgraph }); - if (node.removable !== false) { - options.push(null, { - content: "Remove", - callback: LGraphCanvas.onMenuNodeRemove - }); - } + options.push(null, { + content: "Remove", + disabled: !(node.removable !== false && !node.block_delete ), + callback: LGraphCanvas.onMenuNodeRemove + }); if (node.graph && node.graph.onGetNodeMenuOptions) { node.graph.onGetNodeMenuOptions(options, node); diff --git a/src/nodes/base.js b/src/nodes/base.js index 5e4c429d4..e0f4050cd 100755 --- a/src/nodes/base.js +++ b/src/nodes/base.js @@ -52,6 +52,7 @@ return [["enabled", "boolean"]]; }; + /* Subgraph.prototype.onDrawTitle = function(ctx) { if (this.flags.collapsed) { return; @@ -68,6 +69,7 @@ ctx.lineTo(x + w * 0.5, -w * 0.3); ctx.fill(); }; + */ Subgraph.prototype.onDblClick = function(e, pos, graphcanvas) { var that = this; @@ -76,6 +78,7 @@ }, 10); }; + /* Subgraph.prototype.onMouseDown = function(e, pos, graphcanvas) { if ( !this.flags.collapsed && @@ -88,6 +91,7 @@ }, 10); } }; + */ Subgraph.prototype.onAction = function(action, param) { this.subgraph.onAction(action, param); diff --git a/src/nodes/gltextures.js b/src/nodes/gltextures.js index 725f9ad65..3c24ddf69 100755 --- a/src/nodes/gltextures.js +++ b/src/nodes/gltextures.js @@ -660,7 +660,7 @@ u_texture: 0, u_textureB: 1, value: value, - texSize: [width, height], + texSize: [width, height,1/width,1/height], time: time }) .draw(mesh); @@ -675,7 +675,7 @@ uniform sampler2D u_texture;\n\ uniform sampler2D u_textureB;\n\ varying vec2 v_coord;\n\ - uniform vec2 texSize;\n\ + uniform vec4 texSize;\n\ uniform float time;\n\ uniform float value;\n\ \n\ @@ -712,6 +712,20 @@ LGraphTextureOperation.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"); LGraphTextureOperation.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"); LGraphTextureOperation.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"); + LGraphTextureOperation.registerPreset("normalmap","\n\ + float z0 = texture2D(u_texture, uv + vec2(-texSize.z, -texSize.w) ).x;\n\ + float z1 = texture2D(u_texture, uv + vec2(0.0, -texSize.w) ).x;\n\ + float z2 = texture2D(u_texture, uv + vec2(texSize.z, -texSize.w) ).x;\n\ + float z3 = texture2D(u_texture, uv + vec2(-texSize.z, 0.0) ).x;\n\ + float z4 = color.x;\n\ + float z5 = texture2D(u_texture, uv + vec2(texSize.z, 0.0) ).x;\n\ + float z6 = texture2D(u_texture, uv + vec2(-texSize.z, texSize.w) ).x;\n\ + float z7 = texture2D(u_texture, uv + vec2(0.0, texSize.w) ).x;\n\ + float z8 = texture2D(u_texture, uv + vec2(texSize.z, texSize.w) ).x;\n\ + vec3 normal = vec3( z2 + 2.0*z4 + z7 - z0 - 2.0*z3 - z5, z5 + 2.0*z6 + z7 -z0 - 2.0*z1 - z2, 1.0 );\n\ + normal.xy *= value;\n\ + result.xyz = normalize(normal) * 0.5 + vec3(0.5);\n\ + "); LGraphTextureOperation.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"); //webglstudio stuff... @@ -744,7 +758,7 @@ }; this.properties.code = LGraphTextureShader.pixel_shader; - this._uniforms = { u_value: 1, u_color: vec4.create(), in_texture: 0, texSize: vec2.create(), time: 0 }; + this._uniforms = { u_value: 1, u_color: vec4.create(), in_texture: 0, texSize: vec4.create(), time: 0 }; } LGraphTextureShader.title = "Shader"; @@ -910,6 +924,8 @@ } uniforms.texSize[0] = w; uniforms.texSize[1] = h; + uniforms.texSize[2] = 1/w; + uniforms.texSize[3] = 1/h; uniforms.time = this.graph.getTime(); uniforms.u_value = this.properties.u_value; uniforms.u_color.set( this.properties.u_color ); @@ -930,7 +946,7 @@ \n\ varying vec2 v_coord;\n\ uniform float time; //time in seconds\n\ -uniform vec2 texSize; //tex resolution\n\ +uniform vec4 texSize; //tex resolution\n\ uniform float u_value;\n\ uniform vec4 u_color;\n\n\ void main() {\n\ @@ -1580,6 +1596,69 @@ void main() {\n\ LGraphTextureDownsample ); + + + function LGraphTextureResize() { + this.addInput("Texture", "Texture"); + this.addOutput("", "Texture"); + this.properties = { + size: [512,512], + generate_mipmaps: false, + precision: LGraphTexture.DEFAULT + }; + } + + LGraphTextureResize.title = "Resize"; + LGraphTextureResize.desc = "Resize Texture"; + LGraphTextureResize.widgets_info = { + iterations: { type: "number", step: 1, precision: 0, min: 0 }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphTextureResize.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex && !this._temp_texture) { + return; + } + + if (!this.isOutputConnected(0)) { + return; + } //saves work + + //we do not allow any texture different than texture 2D + if (!tex || tex.texture_type !== GL.TEXTURE_2D) { + return; + } + + var width = this.properties.size[0] | 0; + var height = this.properties.size[1] | 0; + if(width == 0) + width = tex.width; + if(height == 0) + height = tex.height; + var type = tex.type; + if (this.properties.precision === LGraphTexture.LOW) { + type = gl.UNSIGNED_BYTE; + } else if (this.properties.precision === LGraphTexture.HIGH) { + type = gl.HIGH_PRECISION_FORMAT; + } + + if( !this._texture || this._texture.width != width || this._texture.height != height || this._texture.type != type ) + this._texture = new GL.Texture( width, height, { type: type } ); + + tex.copyTo( this._texture ); + + if (this.properties.generate_mipmaps) { + this._texture.bind(0); + gl.generateMipmap(this._texture.texture_type); + this._texture.unbind(0); + } + + this.setOutputData(0, this._texture); + }; + + LiteGraph.registerNodeType( "texture/resize", LGraphTextureResize ); + // Texture Average ***************************************** function LGraphTextureAverage() { this.addInput("Texture", "Texture"); @@ -2247,7 +2326,7 @@ void main() {\n\ this.addInput("Texture", "Texture"); this.addInput("Atlas", "Texture"); this.addOutput("", "Texture"); - this.properties = { enabled: true, num_row_symbols: 4, symbol_size: 16, brightness: 1, colorize: false, filter: false, invert: false, precision: LGraphTexture.DEFAULT, texture: null }; + this.properties = { enabled: true, num_row_symbols: 4, symbol_size: 16, brightness: 1, colorize: false, filter: false, invert: false, precision: LGraphTexture.DEFAULT, generate_mipmaps: false, texture: null }; if (!LGraphTextureEncode._shader) { LGraphTextureEncode._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, LGraphTextureEncode.pixel_shader ); @@ -2323,6 +2402,12 @@ void main() {\n\ tex.toViewport(LGraphTextureEncode._shader, uniforms); }); + if (this.properties.generate_mipmaps) { + this._tex.bind(0); + gl.generateMipmap(this._tex.texture_type); + this._tex.unbind(0); + } + this.setOutputData(0, this._tex); }; diff --git a/src/nodes/shaders.js b/src/nodes/shaders.js new file mode 100644 index 000000000..f5b22969c --- /dev/null +++ b/src/nodes/shaders.js @@ -0,0 +1,429 @@ +(function(global) { + var LiteGraph = global.LiteGraph; + var LGraphCanvas = global.LGraphCanvas; + + if (typeof GL == "undefined") + return; + + + //common actions to all shader node classes + function setShaderNode( node_ctor ) + { + node_ctor.color = "#345"; + } + + function getShaderInputLinkName( node, slot ) + { + if(!node.inputs) + return null; + var link = node.getInputLink( slot ); + if( !link ) + return null; + var origin_node = node.graph.getNodeById( link.origin_id ); + if( !origin_node ) + return null; + if(origin_node.getOutputVarName) + return origin_node.getOutputVarName(link.origin_slot); + //generate + return "link_" + origin_node.id + "_" + link.origin_slot; + } + + function getShaderOutputLinkName( node, slot ) + { + return "link_" + node.id + "_" + slot; + } + + //used to host a shader body ******************* + function LGShaderContext() + { + this.vs_template = ""; + this.fs_template = ""; + this._uniforms = {}; + this._codeparts = {}; + } + + LGShaderContext.valid_types = ["float","vec2","vec3","vec4","sampler2D","mat3","mat4","int","boolean"]; + + LGShaderContext.prototype.clear = function() + { + this._uniforms = {}; + this._codeparts = {}; + } + + LGShaderContext.prototype.addUniform = function( name, type ) + { + this._uniforms[ name ] = type; + } + + LGShaderContext.prototype.addCode = function( hook, code ) + { + if(!this._codeparts[ hook ]) + this._codeparts[ hook ] = code + "\n"; + else + this._codeparts[ hook ] += code + "\n"; + } + + //generates the shader code from the template and the + LGShaderContext.prototype.computeShader = function( shader ) + { + var uniforms = ""; + for(var i in this._uniforms) + uniforms += "uniform " + this._uniforms[i] + " " + i + ";\n"; + + var parts = this._codeparts; + parts.uniforms = uniforms; + + var vs_code = GL.Shader.replaceCodeUsingContext( this.vs_template, parts ); + var fs_code = GL.Shader.replaceCodeUsingContext( this.fs_template, parts ); + + try + { + if(shader) + shader.updateShader( vs_code, fs_code ); + else + shader = new GL.Shader( vs_code, fs_code ); + return shader; + } + catch (err) + { + return null; + } + + return null;//never here + } + + // LGraphShaderGraph ***************************** + // applies a shader graph to texture, it can be uses as an example + + function LGraphShaderGraph() { + this.addOutput("out", "texture"); + this.properties = { width: 0, height: 0, alpha: false, precision: 0 }; + + this.subgraph = new LiteGraph.LGraph(); + this.subgraph._subgraph_node = this; + this.subgraph._is_subgraph = true; + + var subnode = LiteGraph.createNode("shader/fragcolor"); + this.subgraph.pos = [300,100]; + this.subgraph.add( subnode ); + + this.size = [180,60]; + this.redraw_on_mouse = true; //force redraw + + this._uniforms = {}; + this._shader = null; + this._context = new LGShaderContext(); + this._context.vs_template = GL.Shader.SCREEN_VERTEX_SHADER; + this._context.fs_template = LGraphShaderGraph.template; + } + + LGraphShaderGraph.template = "\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + {{varying}}\n\ + {{uniforms}}\n\ + void main() {\n\ + vec2 uv = v_coord;\n\ + vec4 color = vec4(0.0);\n\ + {{fs_code}}\n\ + gl_FragColor = color;\n\ + }\n\ + "; + + LGraphShaderGraph.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } + }; + + LGraphShaderGraph.title = "ShaderGraph"; + LGraphShaderGraph.desc = "Builds a shader using a graph"; + + LGraphShaderGraph.prototype.onSerialize = function(o) + { + o.subgraph = this.subgraph.serialize(); + } + + LGraphShaderGraph.prototype.onConfigure = function(o) + { + this.subgraph.configure(o.subgraph); + } + + LGraphShaderGraph.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) + return; + + var w = this.properties.width | 0; + var h = this.properties.height | 0; + if (w == 0) { + w = gl.viewport_data[2]; + } //0 means default + if (h == 0) { + h = gl.viewport_data[3]; + } //0 means default + var type = LGraphTexture.getTextureType(this.properties.precision); + + var texture = this._texture; + if ( !texture || texture.width != w || texture.height != h || texture.type != type ) { + texture = this._texture = new GL.Texture(w, h, { + type: type, + format: this.alpha ? gl.RGBA : gl.RGB, + filter: gl.LINEAR + }); + } + + var shader = this.getShader(); + if(!shader) + return; + + var uniforms = this._uniforms; + + var tex_slot = 0; + if(this.inputs) + for(var i = 0; i < this.inputs.length; ++i) + { + var input = this.inputs[i]; + var data = this.getInputData(i); + if(input.type == "texture") + { + if(!data) + data = GL.Texture.getWhiteTexture(); + data = data.bind(tex_slot++); + } + + if(data != null) + uniforms[ "u_" + input.name ] = data; + } + + var mesh = GL.Mesh.getScreenQuad(); + + gl.disable( gl.DEPTH_TEST ); + gl.disable( gl.BLEND ); + + texture.drawTo(function(){ + shader.uniforms( uniforms ); + shader.draw( mesh ); + }); + + //use subgraph output + this.setOutputData(0, texture ); + }; + + LGraphShaderGraph.prototype.onInputAdded = function( slot_info ) + { + var subnode = LiteGraph.createNode("shader/uniform"); + subnode.properties.name = slot_info.name; + subnode.properties.type = slot_info.type; + this.subgraph.add( subnode ); + } + + LGraphShaderGraph.prototype.onInputRemoved = function( slot, slot_info ) + { + var nodes = this.subgraph.findNodesByType("shader/uniform"); + for(var i = 0; i < nodes.length; ++i) + { + var node = nodes[i]; + if(node.properties.name == slot_info.name ) + this.subgraph.remove( node ); + } + } + + LGraphShaderGraph.prototype.computeSize = function() + { + var num_inputs = this.inputs ? this.inputs.length : 0; + var num_outputs = this.outputs ? this.outputs.length : 0; + return [ 200, Math.max(num_inputs,num_outputs) * LiteGraph.NODE_SLOT_HEIGHT + LiteGraph.NODE_TITLE_HEIGHT + 10]; + } + + LGraphShaderGraph.prototype.getShader = function() + { + //if subgraph not changed? + if(this._shader && this._shader._version == this.subgraph._version) + return this._shader; + + //prepare context + this._context.clear(); + + //gets code from graph + this.subgraph.sendEventToAllNodes("onGetCode", this._context); + + //compile shader + var shader = this._context.computeShader(); + if(!shader) + { + this.boxcolor = "red"; + return this._shader; + } + else + this.boxcolor = null; + + this._shader = shader; + shader._version = this.subgraph._version; + return shader; + } + + LGraphShaderGraph.prototype.onDrawBackground = function(ctx, graphcanvas, canvas, pos) + { + if(this.flags.collapsed) + return; + + var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5; + + //button + var over = LiteGraph.isInsideRectangle(pos[0],pos[1],this.pos[0],this.pos[1] + y,this.size[0],LiteGraph.NODE_TITLE_HEIGHT); + ctx.fillStyle = over ? "#555" : "#222"; + ctx.beginPath(); + ctx.roundRect( 0, y, this.size[0]+1, LiteGraph.NODE_TITLE_HEIGHT, 0, 8); + ctx.fill(); + + //button + ctx.textAlign = "center"; + ctx.font = "24px Arial"; + ctx.fillStyle = over ? "#DDD" : "#999"; + ctx.fillText( "+", this.size[0] * 0.5, y + 24 ); + } + + LGraphShaderGraph.prototype.onMouseDown = function(e, localpos, graphcanvas) + { + var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5; + if(localpos[1] > y) + { + graphcanvas.showSubgraphPropertiesDialog(this); + } + } + + LiteGraph.registerNodeType( "texture/shaderGraph", LGraphShaderGraph ); + + //Shader Nodes *************************** + + //applies a shader graph to a code + function LGraphShaderUniform() { + this.addOutput("out", ""); + this.properties = { name: "", type: "" }; + } + + LGraphShaderUniform.title = "Uniform"; + LGraphShaderUniform.desc = "Input data for the shader"; + + LGraphShaderUniform.prototype.getTitle = function() + { + return "uniform " + this.properties.name; + } + + LGraphShaderUniform.prototype.onGetCode = function( context ) + { + var type = this.properties.type; + if( !type ) + return; + if(type == "number") + type = "float"; + else if(type == "texture") + type = "sampler2D"; + if ( LGShaderContext.valid_types.indexOf(type) == -1 ) + return; + context.addUniform( "u_" + this.properties.name, type ); + } + + LGraphShaderUniform.prototype.getOutputVarName = function(slot) + { + return "u_" + this.properties.name; + } + + setShaderNode( LGraphShaderUniform ); + + LiteGraph.registerNodeType( "shader/uniform", LGraphShaderUniform ); + + + + function LGraphShaderAttribute() { + this.addOutput("out", "vec2"); + this.properties = { name: "coord", type: "vec2" }; + } + + LGraphShaderAttribute.title = "Attribute"; + LGraphShaderAttribute.desc = "Input data from mesh attribute"; + + LGraphShaderAttribute.prototype.getTitle = function() + { + return "att. " + this.properties.name; + } + + LGraphShaderAttribute.prototype.onGetCode = function( context ) + { + var type = this.properties.type; + if( !type || LGShaderContext.valid_types.indexOf(type) == -1 ) + return; + if(type == "number") + type = "float"; + if( this.properties.name != "coord") + context.addCode( "varying", " varying " + type +" v_" + this.properties.name ); + } + + LGraphShaderAttribute.prototype.getOutputVarName = function(slot) + { + return "v_" + this.properties.name; + } + + setShaderNode( LGraphShaderAttribute ); + + LiteGraph.registerNodeType( "shader/attribute", LGraphShaderAttribute ); + + + function LGraphShaderSampler2D() { + this.addInput("tex", "sampler2D"); + this.addInput("uv", "vec2"); + this.addOutput("rgba", "vec4"); + } + + LGraphShaderSampler2D.title = "Sampler2D"; + LGraphShaderSampler2D.desc = "Reads a pixel from a texture"; + + LGraphShaderSampler2D.prototype.onGetCode = function( context ) + { + var texname = getShaderInputLinkName( this, 0 ); + var code; + if(texname) + { + var uvname = getShaderInputLinkName( this, 1 ) || "v_coord"; + code = "vec4 " + getShaderOutputLinkName( this, 0 ) + " = texture2D("+texname+","+uvname+");"; + } + else + code = "vec4 " + getShaderOutputLinkName( this, 0 ) + " = vec4(0.0);"; + context.addCode( "fs_code", code ); + } + + setShaderNode( LGraphShaderSampler2D ); + + LiteGraph.registerNodeType( "shader/sampler2D", LGraphShaderSampler2D ); + + //********************************* + + function LGraphShaderFragColor() { + this.addInput("color", "float,vec2,vec3,vec4"); + this.block_delete = true; + } + + LGraphShaderFragColor.title = "FragColor"; + LGraphShaderFragColor.desc = "Pixel final color"; + + LGraphShaderFragColor.prototype.onGetCode = function( context ) + { + var link_name = getShaderInputLinkName( this, 0 ); + if(!link_name) + return; + + var code = link_name; + var type = this.getInputDataType(0); + if(type == "float") + code = "vec4(" + code + ");"; + else if(type == "vec2") + code = "vec4(" + code + ",0.0,1.0);"; + else if(type == "vec3") + code = "vec4(" + code + ",1.0);"; + + context.addCode("fs_code", "color = " + code + ";\n"); + } + + setShaderNode( LGraphShaderFragColor ); + + LiteGraph.registerNodeType( "shader/fragcolor", LGraphShaderFragColor ); + +})(this); \ No newline at end of file From 115b647bf6422c9a8fcf5cb5460bc713253d9e82 Mon Sep 17 00:00:00 2001 From: ilyabeskrovniy Date: Tue, 7 Jul 2020 10:27:22 +0300 Subject: [PATCH 50/63] Fixed typings and documentation --- guides/README.md | 2 +- src/litegraph.d.ts | 29 +++++++++++++++++++++-------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/guides/README.md b/guides/README.md index 4743daf50..db26a2c27 100644 --- a/guides/README.md +++ b/guides/README.md @@ -64,7 +64,7 @@ There are several settings that could be defined or modified per node: * **collapsed**: if it is shown collapsed (small) * **redraw_on_mouse**: forces a redraw if the mouse passes over the widget * **widgets_up**: widgets do not start after the slots -* **widgets_y**: widgets should start being drawn from this Y +* **widgets_start_y**: widgets should start being drawn from this Y * **clip_area**: clips the content when rendering the node * **resizable**: if it can be resized dragging the corner * **horizontal**: if the slots should be placed horizontally on the top and bottom of the node diff --git a/src/litegraph.d.ts b/src/litegraph.d.ts index ec4cbaf95..2a54667cb 100644 --- a/src/litegraph.d.ts +++ b/src/litegraph.d.ts @@ -15,7 +15,7 @@ export type widgetTypes = /** https://github.com/jagenjo/litegraph.js/tree/master/guides#node-slots */ export interface INodeSlot { name: string; - type: string; + type: string | -1; label?: string; dir?: | typeof LiteGraph.UP @@ -601,7 +601,9 @@ export declare class LGraphNode { properties: Record; properties_info: any[]; - flags: object; + flags: Partial<{ + collapsed: boolean + }>; color: string; bgcolor: string; @@ -623,6 +625,17 @@ export declare class LGraphNode { | typeof LiteGraph.NEVER | typeof LiteGraph.ALWAYS; + /** If set to true widgets do not start after the slots */ + widgets_up: boolean; + /** widgets start at y distance from the top of the node */ + widgets_start_y: number; + /** if you render outside the node, it will be clipped */ + clip_area: boolean; + /** if set to false it wont be resizable with the mouse */ + resizable: boolean; + /** slots are distributed horizontally */ + horizontal: boolean; + /** configure a node from an object containing the serialized info */ configure(info: SerializedLGraphNode): void; /** serialize the content */ @@ -715,7 +728,7 @@ export declare class LGraphNode { */ addOutput( name: string, - type: string, + type: string | -1, extra_info?: Partial ): void; /** @@ -723,7 +736,7 @@ export declare class LGraphNode { * @param array of triplets like [[name,type,extra_info],[...]] */ addOutputs( - array: [string, string, Partial | undefined][] + array: [string, string | -1, Partial | undefined][] ): void; /** remove an existing output slot */ removeOutput(slot: number): void; @@ -735,7 +748,7 @@ export declare class LGraphNode { */ addInput( name: string, - type: string, + type: string | -1, extra_info?: Partial ): void; /** @@ -743,7 +756,7 @@ export declare class LGraphNode { * @param array of triplets like [[name,type,extra_info],[...]] */ addInputs( - array: [string, string, Partial | undefined][] + array: [string, string | -1, Partial | undefined][] ): void; /** remove an existing input slot */ removeInput(slot: number): void; @@ -777,7 +790,7 @@ export declare class LGraphNode { type: T["type"], name: string, value: T["value"], - callback?: WidgetCallback, + callback?: WidgetCallback | string, options?: T["options"] ): T; @@ -1111,7 +1124,7 @@ export declare class LGraphCanvas { last_mouse_position: Vector2; /** Timestamp of last mouse click, defaults to 0 */ last_mouseclick: number; - link_render_mode: + links_render_mode: | typeof LiteGraph.STRAIGHT_LINK | typeof LiteGraph.LINEAR_LINK | typeof LiteGraph.SPLINE_LINK; From 44f58d03e1eb280131d220ec7645f1ed7f6a078c Mon Sep 17 00:00:00 2001 From: ilyabeskrovniy Date: Tue, 7 Jul 2020 10:32:28 +0300 Subject: [PATCH 51/63] Added function to clear all previously registered node types --- src/litegraph.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/litegraph.js b/src/litegraph.js index 0c0c1347a..d6e6c5dc5 100755 --- a/src/litegraph.js +++ b/src/litegraph.js @@ -94,6 +94,16 @@ searchbox_extras: {}, //used to add extra features to the search box + /** + * Removes all previously registered node's types + */ + clearRegisteredTypes: function() { + this.registered_node_types = {}; + this.node_types_by_file_extension = {}; + this.Nodes = {}; + this.searchbox_extras = {}; + }, + /** * Register a node class so it can be listed when the user wants to create a new one * @method registerNodeType From cfdfbf929b780416f6ac8c06d37a1c8cb576c788 Mon Sep 17 00:00:00 2001 From: ilya Date: Wed, 8 Jul 2020 18:57:22 +0300 Subject: [PATCH 52/63] Extracted fucntion to draw slot graphics and modified shape selection to allow custom action/event slot shape --- src/litegraph.js | 116 ++++++++++++++++++----------------------------- 1 file changed, 45 insertions(+), 71 deletions(-) diff --git a/src/litegraph.js b/src/litegraph.js index 0c0c1347a..182e311de 100755 --- a/src/litegraph.js +++ b/src/litegraph.js @@ -53,6 +53,7 @@ CIRCLE_SHAPE: 3, CARD_SHAPE: 4, ARROW_SHAPE: 5, + SQUARE_SHAPE: 6, //enums INPUT: 1, @@ -6962,6 +6963,44 @@ LGraphNode.prototype.executeAction = function(action) var temp_vec2 = new Float32Array(2); + function drawSlotGraphic(ctx, pos, shape, horizontal) { + ctx.beginPath(); + + switch (shape) { + case (LiteGraph.BOX_SHAPE): + if (horizontal) { + ctx.rect( + pos[0] - 5 + 0.5, + pos[1] - 8 + 0.5, + 10, + 14 + ); + } else { + ctx.rect( + pos[0] - 6 + 0.5, + pos[1] - 5 + 0.5, + 14, + 10 + ); + } + break; + case (LiteGraph.ARROW_SHAPE): + ctx.moveTo(pos[0] + 8, pos[1] + 0.5); + ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5); + ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5); + ctx.closePath(); + break; + case (LiteGraph.SQUARE_SHAPE): + ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8); //faster + break; + case (LiteGraph.CIRCLE_SHAPE): + default: + ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); + break; + } + ctx.fill(); + } + /** * draws the given node inside the canvas * @method drawNode @@ -7117,39 +7156,9 @@ LGraphNode.prototype.executeAction = function(action) max_y = pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5; } - ctx.beginPath(); - - if ( - slot.type === LiteGraph.EVENT || - slot.shape === LiteGraph.BOX_SHAPE - ) { - if (horizontal) { - ctx.rect( - pos[0] - 5 + 0.5, - pos[1] - 8 + 0.5, - 10, - 14 - ); - } else { - ctx.rect( - pos[0] - 6 + 0.5, - pos[1] - 5 + 0.5, - 14, - 10 - ); - } - } else if (slot.shape === LiteGraph.ARROW_SHAPE) { - ctx.moveTo(pos[0] + 8, pos[1] + 0.5); - ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5); - ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5); - ctx.closePath(); - } else { - if(low_quality) - ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); //faster - else - ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); - } - ctx.fill(); + var shape = slot.shape || (slot.type === LiteGraph.EVENT && LiteGraph.BOX_SHAPE) + || (low_quality && LiteGraph.SQUARE_SHAPE) || LiteGraph.CIRCLE_SHAPE; + drawSlotGraphic(ctx, pos, shape, horizontal); //render name if (render_text) { @@ -7190,46 +7199,11 @@ LGraphNode.prototype.executeAction = function(action) this.default_connection_color.output_on : slot.color_off || this.default_connection_color.output_off; - ctx.beginPath(); - //ctx.rect( node.size[0] - 14,i*14,10,10); - if ( - slot.type === LiteGraph.EVENT || - slot.shape === LiteGraph.BOX_SHAPE - ) { - if (horizontal) { - ctx.rect( - pos[0] - 5 + 0.5, - pos[1] - 8 + 0.5, - 10, - 14 - ); - } else { - ctx.rect( - pos[0] - 6 + 0.5, - pos[1] - 5 + 0.5, - 14, - 10 - ); - } - } else if (slot.shape === LiteGraph.ARROW_SHAPE) { - ctx.moveTo(pos[0] + 8, pos[1] + 0.5); - ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5); - ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5); - ctx.closePath(); - } else { - if(low_quality) - ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); - else - ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); - } + var shape = slot.shape || (slot.type === LiteGraph.EVENT && LiteGraph.BOX_SHAPE) + || (low_quality && LiteGraph.SQUARE_SHAPE) || LiteGraph.CIRCLE_SHAPE; + drawSlotGraphic(ctx, pos, shape, horizontal); - //trigger - //if(slot.node_id != null && slot.slot == -1) - // ctx.fillStyle = "#F85"; - - //if(slot.links != null && slot.links.length) - ctx.fill(); if(!low_quality) ctx.stroke(); From 0238a5dcaeed7a84a8fab24ca657f9e254bb0ece Mon Sep 17 00:00:00 2001 From: ilya Date: Wed, 8 Jul 2020 21:14:36 +0300 Subject: [PATCH 53/63] Chenges to allow ability to automatically create inputs if required. --- src/litegraph.d.ts | 11 +++++++++++ src/litegraph.js | 47 +++++++++++++++++++++++++--------------------- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/src/litegraph.d.ts b/src/litegraph.d.ts index ec4cbaf95..b35ce01c7 100644 --- a/src/litegraph.d.ts +++ b/src/litegraph.d.ts @@ -938,6 +938,17 @@ export declare class LGraphNode { _this: this, slotIndex: number ): boolean; + + /** + * Called just before connection (or disconnect - if input is linked). + * A convenient place to switch to another input, or create new one. + * This allow for ability to automatically add slots if needed + * @param inputIndex + * @return selected input slot index, can differ from parameter value + */ + onBeforeConnectInput?( + inputIndex: number + ): number; /** a connection changed (new one or removed) (LiteGraph.INPUT or LiteGraph.OUTPUT, slot, true if connected, link_info, input_info or output_info ) */ onConnectionsChange( diff --git a/src/litegraph.js b/src/litegraph.js index 0c0c1347a..bd4d74512 100755 --- a/src/litegraph.js +++ b/src/litegraph.js @@ -3592,28 +3592,33 @@ return null; } - //if there is something already plugged there, disconnect - if (target_node.inputs[target_slot].link != null) { - target_node.disconnectInput(target_slot); - } - - //why here?? - //this.setDirtyCanvas(false,true); - //this.graph.connectionChange( this ); - - var output = this.outputs[slot]; - - //allows nodes to block connection - if (target_node.onConnectInput) { - if ( target_node.onConnectInput(target_slot, output.type, output, this, slot) === false ) { - return null; - } - } - - var input = target_node.inputs[target_slot]; - var link_info = null; - if (LiteGraph.isValidConnection(output.type, input.type)) { + if (target_node.onBeforeConnectInput) { + // This way node can choose another slot (if selected is occupied) + target_slot = target_node.onBeforeConnectInput(target_slot); + } + + //if there is something already plugged there, disconnect + if (target_node.inputs[target_slot].link != null) { + target_node.disconnectInput(target_slot); + } + + //why here?? + //this.setDirtyCanvas(false,true); + //this.graph.connectionChange( this ); + + var output = this.outputs[slot]; + + //allows nodes to block connection + if (target_node.onConnectInput) { + if ( target_node.onConnectInput(target_slot, output.type, output, this, slot) === false ) { + return null; + } + } + + var input = target_node.inputs[target_slot]; + var link_info = null; + link_info = new LLink( ++this.graph.last_link_id, input.type, From ea63884c83ac0eda4d7b3dd827f67effa6d748bb Mon Sep 17 00:00:00 2001 From: ilya Date: Fri, 10 Jul 2020 12:23:43 +0300 Subject: [PATCH 54/63] Added typings for slot shape and replaced built files --- build/litegraph.js | 175 +- build/litegraph.min.js | 6362 ++++++++++++++++++++-------------------- src/litegraph.d.ts | 7 + 3 files changed, 3280 insertions(+), 3264 deletions(-) diff --git a/build/litegraph.js b/build/litegraph.js index fa2b81803..55f34c577 100644 --- a/build/litegraph.js +++ b/build/litegraph.js @@ -53,6 +53,7 @@ CIRCLE_SHAPE: 3, CARD_SHAPE: 4, ARROW_SHAPE: 5, + SQUARE_SHAPE: 6, //enums INPUT: 1, @@ -94,6 +95,16 @@ searchbox_extras: {}, //used to add extra features to the search box + /** + * Removes all previously registered node's types + */ + clearRegisteredTypes: function() { + this.registered_node_types = {}; + this.node_types_by_file_extension = {}; + this.Nodes = {}; + this.searchbox_extras = {}; + }, + /** * Register a node class so it can be listed when the user wants to create a new one * @method registerNodeType @@ -3592,28 +3603,33 @@ return null; } - //if there is something already plugged there, disconnect - if (target_node.inputs[target_slot].link != null) { - target_node.disconnectInput(target_slot); - } - - //why here?? - //this.setDirtyCanvas(false,true); - //this.graph.connectionChange( this ); - - var output = this.outputs[slot]; - - //allows nodes to block connection - if (target_node.onConnectInput) { - if ( target_node.onConnectInput(target_slot, output.type, output, this, slot) === false ) { - return null; - } - } - - var input = target_node.inputs[target_slot]; - var link_info = null; - if (LiteGraph.isValidConnection(output.type, input.type)) { + if (target_node.onBeforeConnectInput) { + // This way node can choose another slot (if selected is occupied) + target_slot = target_node.onBeforeConnectInput(target_slot); + } + + //if there is something already plugged there, disconnect + if (target_node.inputs[target_slot].link != null) { + target_node.disconnectInput(target_slot); + } + + //why here?? + //this.setDirtyCanvas(false,true); + //this.graph.connectionChange( this ); + + var output = this.outputs[slot]; + + //allows nodes to block connection + if (target_node.onConnectInput) { + if ( target_node.onConnectInput(target_slot, output.type, output, this, slot) === false ) { + return null; + } + } + + var input = target_node.inputs[target_slot]; + var link_info = null; + link_info = new LLink( ++this.graph.last_link_id, input.type, @@ -5575,7 +5591,7 @@ LGraphNode.prototype.executeAction = function(action) if (this.resizing_node && !this.live_mode) { //convert mouse to node space var desired_size = [ e.canvasX - this.resizing_node.pos[0], e.canvasY - this.resizing_node.pos[1] ]; - var min_size = this.resizing_node.computeSize(); + var min_size = this.resizing_node.computeSize(desired_size[0]); desired_size[0] = Math.max( min_size[0], desired_size[0] ); desired_size[1] = Math.max( min_size[1], desired_size[1] ); this.resizing_node.setSize( desired_size ); @@ -6962,6 +6978,44 @@ LGraphNode.prototype.executeAction = function(action) var temp_vec2 = new Float32Array(2); + function drawSlotGraphic(ctx, pos, shape, horizontal) { + ctx.beginPath(); + + switch (shape) { + case (LiteGraph.BOX_SHAPE): + if (horizontal) { + ctx.rect( + pos[0] - 5 + 0.5, + pos[1] - 8 + 0.5, + 10, + 14 + ); + } else { + ctx.rect( + pos[0] - 6 + 0.5, + pos[1] - 5 + 0.5, + 14, + 10 + ); + } + break; + case (LiteGraph.ARROW_SHAPE): + ctx.moveTo(pos[0] + 8, pos[1] + 0.5); + ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5); + ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5); + ctx.closePath(); + break; + case (LiteGraph.SQUARE_SHAPE): + ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8); //faster + break; + case (LiteGraph.CIRCLE_SHAPE): + default: + ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); + break; + } + ctx.fill(); + } + /** * draws the given node inside the canvas * @method drawNode @@ -7117,39 +7171,9 @@ LGraphNode.prototype.executeAction = function(action) max_y = pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5; } - ctx.beginPath(); - - if ( - slot.type === LiteGraph.EVENT || - slot.shape === LiteGraph.BOX_SHAPE - ) { - if (horizontal) { - ctx.rect( - pos[0] - 5 + 0.5, - pos[1] - 8 + 0.5, - 10, - 14 - ); - } else { - ctx.rect( - pos[0] - 6 + 0.5, - pos[1] - 5 + 0.5, - 14, - 10 - ); - } - } else if (slot.shape === LiteGraph.ARROW_SHAPE) { - ctx.moveTo(pos[0] + 8, pos[1] + 0.5); - ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5); - ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5); - ctx.closePath(); - } else { - if(low_quality) - ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); //faster - else - ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); - } - ctx.fill(); + var shape = slot.shape || (slot.type === LiteGraph.EVENT && LiteGraph.BOX_SHAPE) + || (low_quality && LiteGraph.SQUARE_SHAPE) || LiteGraph.CIRCLE_SHAPE; + drawSlotGraphic(ctx, pos, shape, horizontal); //render name if (render_text) { @@ -7190,46 +7214,11 @@ LGraphNode.prototype.executeAction = function(action) this.default_connection_color.output_on : slot.color_off || this.default_connection_color.output_off; - ctx.beginPath(); - //ctx.rect( node.size[0] - 14,i*14,10,10); - if ( - slot.type === LiteGraph.EVENT || - slot.shape === LiteGraph.BOX_SHAPE - ) { - if (horizontal) { - ctx.rect( - pos[0] - 5 + 0.5, - pos[1] - 8 + 0.5, - 10, - 14 - ); - } else { - ctx.rect( - pos[0] - 6 + 0.5, - pos[1] - 5 + 0.5, - 14, - 10 - ); - } - } else if (slot.shape === LiteGraph.ARROW_SHAPE) { - ctx.moveTo(pos[0] + 8, pos[1] + 0.5); - ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5); - ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5); - ctx.closePath(); - } else { - if(low_quality) - ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); - else - ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); - } + var shape = slot.shape || (slot.type === LiteGraph.EVENT && LiteGraph.BOX_SHAPE) + || (low_quality && LiteGraph.SQUARE_SHAPE) || LiteGraph.CIRCLE_SHAPE; + drawSlotGraphic(ctx, pos, shape, horizontal); - //trigger - //if(slot.node_id != null && slot.slot == -1) - // ctx.fillStyle = "#F85"; - - //if(slot.links != null && slot.links.length) - ctx.fill(); if(!low_quality) ctx.stroke(); diff --git a/build/litegraph.min.js b/build/litegraph.min.js index 9ff56320c..674a4b2d0 100755 --- a/build/litegraph.min.js +++ b/build/litegraph.min.js @@ -3,39 +3,39 @@ $jscomp.scope = {}; $jscomp.ASSUME_ES5 = !1; $jscomp.ASSUME_NO_NATIVE_MAP = !1; $jscomp.ASSUME_NO_NATIVE_SET = !1; -$jscomp.defineProperty = $jscomp.ASSUME_ES5 || "function" == typeof Object.defineProperties ? Object.defineProperty : function(v, c, r) { - v != Array.prototype && v != Object.prototype && (v[c] = r.value); +$jscomp.defineProperty = $jscomp.ASSUME_ES5 || "function" == typeof Object.defineProperties ? Object.defineProperty : function(w, c, t) { + w != Array.prototype && w != Object.prototype && (w[c] = t.value); }; -$jscomp.getGlobal = function(v) { - return "undefined" != typeof window && window === v ? v : "undefined" != typeof global && null != global ? global : v; +$jscomp.getGlobal = function(w) { + return "undefined" != typeof window && window === w ? w : "undefined" != typeof global && null != global ? global : w; }; $jscomp.global = $jscomp.getGlobal(this); -$jscomp.polyfill = function(v, c, r, m) { +$jscomp.polyfill = function(w, c, t, l) { if (c) { - r = $jscomp.global; - v = v.split("."); - for (m = 0; m < v.length - 1; m++) { - var h = v[m]; - h in r || (r[h] = {}); - r = r[h]; + t = $jscomp.global; + w = w.split("."); + for (l = 0; l < w.length - 1; l++) { + var p = w[l]; + p in t || (t[p] = {}); + t = t[p]; } - v = v[v.length - 1]; - m = r[v]; - c = c(m); - c != m && null != c && $jscomp.defineProperty(r, v, {configurable:!0, writable:!0, value:c}); + w = w[w.length - 1]; + l = t[w]; + c = c(l); + c != l && null != c && $jscomp.defineProperty(t, w, {configurable:!0, writable:!0, value:c}); } }; -$jscomp.polyfill("Array.prototype.fill", function(v) { - return v ? v : function(c, r, m) { - var h = this.length || 0; - 0 > r && (r = Math.max(0, h + r)); - if (null == m || m > h) { - m = h; +$jscomp.polyfill("Array.prototype.fill", function(w) { + return w ? w : function(c, t, l) { + var p = this.length || 0; + 0 > t && (t = Math.max(0, p + t)); + if (null == l || l > p) { + l = p; } - m = Number(m); - 0 > m && (m = Math.max(0, h + m)); - for (r = Number(r || 0); r < m; r++) { - this[r] = c; + l = Number(l); + 0 > l && (l = Math.max(0, p + l)); + for (t = Number(t || 0); t < l; t++) { + this[t] = c; } return this; }; @@ -47,100 +47,100 @@ $jscomp.initSymbol = function() { $jscomp.global.Symbol || ($jscomp.global.Symbol = $jscomp.Symbol); }; $jscomp.Symbol = function() { - var v = 0; + var w = 0; return function(c) { - return $jscomp.SYMBOL_PREFIX + (c || "") + v++; + return $jscomp.SYMBOL_PREFIX + (c || "") + w++; }; }(); $jscomp.initSymbolIterator = function() { $jscomp.initSymbol(); - var v = $jscomp.global.Symbol.iterator; - v || (v = $jscomp.global.Symbol.iterator = $jscomp.global.Symbol("iterator")); - "function" != typeof Array.prototype[v] && $jscomp.defineProperty(Array.prototype, v, {configurable:!0, writable:!0, value:function() { + var w = $jscomp.global.Symbol.iterator; + w || (w = $jscomp.global.Symbol.iterator = $jscomp.global.Symbol("iterator")); + "function" != typeof Array.prototype[w] && $jscomp.defineProperty(Array.prototype, w, {configurable:!0, writable:!0, value:function() { return $jscomp.arrayIterator(this); }}); $jscomp.initSymbolIterator = function() { }; }; -$jscomp.arrayIterator = function(v) { +$jscomp.arrayIterator = function(w) { var c = 0; return $jscomp.iteratorPrototype(function() { - return c < v.length ? {done:!1, value:v[c++]} : {done:!0}; + return c < w.length ? {done:!1, value:w[c++]} : {done:!0}; }); }; -$jscomp.iteratorPrototype = function(v) { +$jscomp.iteratorPrototype = function(w) { $jscomp.initSymbolIterator(); - v = {next:v}; - v[$jscomp.global.Symbol.iterator] = function() { + w = {next:w}; + w[$jscomp.global.Symbol.iterator] = function() { return this; }; - return v; + return w; }; -$jscomp.iteratorFromArray = function(v, c) { +$jscomp.iteratorFromArray = function(w, c) { $jscomp.initSymbolIterator(); - v instanceof String && (v += ""); - var r = 0, m = {next:function() { - if (r < v.length) { - var h = r++; - return {value:c(h, v[h]), done:!1}; + w instanceof String && (w += ""); + var t = 0, l = {next:function() { + if (t < w.length) { + var p = t++; + return {value:c(p, w[p]), done:!1}; } - m.next = function() { + l.next = function() { return {done:!0, value:void 0}; }; - return m.next(); + return l.next(); }}; - m[Symbol.iterator] = function() { - return m; + l[Symbol.iterator] = function() { + return l; }; - return m; + return l; }; -$jscomp.polyfill("Array.prototype.values", function(v) { - return v ? v : function() { - return $jscomp.iteratorFromArray(this, function(c, r) { - return r; +$jscomp.polyfill("Array.prototype.values", function(w) { + return w ? w : function() { + return $jscomp.iteratorFromArray(this, function(c, t) { + return t; }); }; }, "es8", "es3"); -$jscomp.polyfill("Array.prototype.keys", function(v) { - return v ? v : function() { +$jscomp.polyfill("Array.prototype.keys", function(w) { + return w ? w : function() { return $jscomp.iteratorFromArray(this, function(c) { return c; }); }; }, "es6", "es3"); -$jscomp.owns = function(v, c) { - return Object.prototype.hasOwnProperty.call(v, c); +$jscomp.owns = function(w, c) { + return Object.prototype.hasOwnProperty.call(w, c); }; -$jscomp.polyfill("Object.values", function(v) { - return v ? v : function(c) { - var r = [], m; - for (m in c) { - $jscomp.owns(c, m) && r.push(c[m]); +$jscomp.polyfill("Object.values", function(w) { + return w ? w : function(c) { + var t = [], l; + for (l in c) { + $jscomp.owns(c, l) && t.push(c[l]); } - return r; + return t; }; }, "es8", "es3"); -(function(v) { +(function(w) { function c(a) { e.debug && console.log("Graph created"); this.list_of_graphcanvas = null; this.clear(); a && this.configure(a); } - function r(a, b, d, g, f, e) { + function t(a, b, d, f, k, e) { this.id = a; this.type = b; this.origin_id = d; - this.origin_slot = g; - this.target_id = f; + this.origin_slot = f; + this.target_id = k; this.target_slot = e; this._data = null; this._pos = new Float32Array(2); } - function m(a) { + function l(a) { this._ctor(a); } - function h(a) { + function p(a) { this._ctor(a); } function q(a, b) { @@ -155,7 +155,7 @@ $jscomp.polyfill("Object.values", function(v) { this.visible_area = new Float32Array(4); a && (this.element = a, b || this.bindEvents(a)); } - function l(a, b, d) { + function g(a, b, d) { d = d || {}; this.background_image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; a && a.constructor === String && (a = document.querySelector(a)); @@ -200,17 +200,37 @@ $jscomp.polyfill("Object.values", function(v) { d.skip_render || this.startRendering(); this.autoresize = d.autoresize; } + function B(a, b, d, f) { + a.beginPath(); + switch(d) { + case e.BOX_SHAPE: + f ? a.rect(b[0] - 5 + 0.5, b[1] - 8 + 0.5, 10, 14) : a.rect(b[0] - 6 + 0.5, b[1] - 5 + 0.5, 14, 10); + break; + case e.ARROW_SHAPE: + a.moveTo(b[0] + 8, b[1] + 0.5); + a.lineTo(b[0] - 4, b[1] + 6 + 0.5); + a.lineTo(b[0] - 4, b[1] - 6 + 0.5); + a.closePath(); + break; + case e.SQUARE_SHAPE: + a.rect(b[0] - 4, b[1] - 4, 8, 8); + break; + default: + a.arc(b[0], b[1], 4, 0, 2 * Math.PI); + } + a.fill(); + } function A(a, b) { return Math.sqrt((b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1])); } - function y(a, b, d, g, f, e) { - return d < a && d + f > a && g < b && g + e > b ? !0 : !1; + function v(a, b, d, f, k, e) { + return d < a && d + k > a && f < b && f + e > b ? !0 : !1; } - function x(a, b) { - var d = a[0] + a[2], g = a[1] + a[3], f = b[1] + b[3]; - return a[0] > b[0] + b[2] || a[1] > f || d < b[0] || g < b[1] ? !1 : !0; + function G(a, b) { + var d = a[0] + a[2], f = a[1] + a[3], k = b[1] + b[3]; + return a[0] > b[0] + b[2] || a[1] > k || d < b[0] || f < b[1] ? !1 : !0; } - function F(a, b) { + function z(a, b) { function d(a) { var d = parseInt(e.style.top); e.style.top = (d + a.deltaY * b.scroll_speed).toFixed() + "px"; @@ -218,11 +238,11 @@ $jscomp.polyfill("Object.values", function(v) { return !0; } this.options = b = b || {}; - var g = this; + var f = this; b.parentMenu && (b.parentMenu.constructor !== this.constructor ? (console.error("parentMenu must be of class ContextMenu, ignoring it"), b.parentMenu = null) : (this.parentMenu = b.parentMenu, this.parentMenu.lock = !0, this.parentMenu.current_submenu = this)); - var f = null; - b.event && (f = b.event.constructor.name); - "MouseEvent" !== f && "CustomEvent" !== f && "PointerEvent" !== f && (console.error("Event passed to ContextMenu is not of type MouseEvent or CustomEvent. Ignoring it."), b.event = null); + var k = null; + b.event && (k = b.event.constructor.name); + "MouseEvent" !== k && "CustomEvent" !== k && "PointerEvent" !== k && (console.error("Event passed to ContextMenu is not of type MouseEvent or CustomEvent. Ignoring it."), b.event = null); var e = document.createElement("div"); e.className = "litegraph litecontextmenu litemenubar-panel"; b.className && (e.className += " " + b.className); @@ -245,23 +265,23 @@ $jscomp.polyfill("Object.values", function(v) { }, !0); e.addEventListener("mousedown", function(a) { if (2 == a.button) { - return g.close(), a.preventDefault(), !0; + return f.close(), a.preventDefault(), !0; } }, !0); b.scroll_speed || (b.scroll_speed = 0.1); e.addEventListener("wheel", d, !0); e.addEventListener("mousewheel", d, !0); this.root = e; - b.title && (f = document.createElement("div"), f.className = "litemenu-title", f.innerHTML = b.title, e.appendChild(f)); - f = 0; + b.title && (k = document.createElement("div"), k.className = "litemenu-title", k.innerHTML = b.title, e.appendChild(k)); + k = 0; for (var c in a) { - var k = a.constructor == Array ? a[c] : c; - null != k && k.constructor !== String && (k = void 0 === k.content ? String(k) : k.content); - this.addItem(k, a[c], b); - f++; + var h = a.constructor == Array ? a[c] : c; + null != h && h.constructor !== String && (h = void 0 === h.content ? String(h) : h.content); + this.addItem(h, a[c], b); + k++; } e.addEventListener("mouseleave", function(a) { - g.lock || (e.closing_timer && clearTimeout(e.closing_timer), e.closing_timer = setTimeout(g.close.bind(g, a), 500)); + f.lock || (e.closing_timer && clearTimeout(e.closing_timer), e.closing_timer = setTimeout(f.close.bind(f, a), 500)); }); e.addEventListener("mouseenter", function(a) { e.closing_timer && clearTimeout(e.closing_timer); @@ -272,37 +292,42 @@ $jscomp.polyfill("Object.values", function(v) { a.fullscreenElement ? a.fullscreenElement.appendChild(e) : a.body.appendChild(e); c = b.left || 0; a = b.top || 0; - b.event && (c = b.event.clientX - 10, a = b.event.clientY - 10, b.title && (a -= 20), b.parentMenu && (c = b.parentMenu.root.getBoundingClientRect(), c = c.left + c.width), f = document.body.getBoundingClientRect(), k = e.getBoundingClientRect(), 0 == f.height && console.error("document.body height is 0. That is dangerous, set html,body { height: 100%; }"), f.width && c > f.width - k.width - 10 && (c = f.width - k.width - 10), f.height && a > f.height - k.height - 10 && (a = f.height - k.height - + b.event && (c = b.event.clientX - 10, a = b.event.clientY - 10, b.title && (a -= 20), b.parentMenu && (c = b.parentMenu.root.getBoundingClientRect(), c = c.left + c.width), k = document.body.getBoundingClientRect(), h = e.getBoundingClientRect(), 0 == k.height && console.error("document.body height is 0. That is dangerous, set html,body { height: 100%; }"), k.width && c > k.width - h.width - 10 && (c = k.width - h.width - 10), k.height && a > k.height - h.height - 10 && (a = k.height - h.height - 10)); e.style.left = c + "px"; e.style.top = a + "px"; b.scale && (e.style.transform = "scale(" + b.scale + ")"); } - function z(a) { + function r(a) { this.points = a; this.nearest = this.selected = -1; this.size = null; this.must_update = !0; this.margin = 5; } - var e = v.LiteGraph = {VERSION:0.4, CANVAS_GRID_SIZE:10, NODE_TITLE_HEIGHT:30, NODE_TITLE_TEXT_Y:20, NODE_SLOT_HEIGHT:20, NODE_WIDGET_HEIGHT:20, NODE_WIDTH:140, NODE_MIN_WIDTH:50, NODE_COLLAPSED_RADIUS:10, NODE_COLLAPSED_WIDTH:80, NODE_TITLE_COLOR:"#999", NODE_TEXT_SIZE:14, NODE_TEXT_COLOR:"#AAA", NODE_SUBTEXT_SIZE:12, NODE_DEFAULT_COLOR:"#333", NODE_DEFAULT_BGCOLOR:"#353535", NODE_DEFAULT_BOXCOLOR:"#666", NODE_DEFAULT_SHAPE:"box", DEFAULT_SHADOW_COLOR:"rgba(0,0,0,0.5)", DEFAULT_GROUP_FONT:24, - WIDGET_BGCOLOR:"#222", WIDGET_OUTLINE_COLOR:"#666", WIDGET_TEXT_COLOR:"#DDD", WIDGET_SECONDARY_TEXT_COLOR:"#999", LINK_COLOR:"#9A9", EVENT_LINK_COLOR:"#A86", CONNECTING_LINK_COLOR:"#AFA", MAX_NUMBER_OF_NODES:1000, DEFAULT_POSITION:[100, 100], VALID_SHAPES:["default", "box", "round", "card"], BOX_SHAPE:1, ROUND_SHAPE:2, CIRCLE_SHAPE:3, CARD_SHAPE:4, ARROW_SHAPE:5, INPUT:1, OUTPUT:2, EVENT:-1, ACTION:-1, ALWAYS:0, ON_EVENT:1, NEVER:2, ON_TRIGGER:3, UP:1, DOWN:2, LEFT:3, RIGHT:4, CENTER:5, STRAIGHT_LINK:0, - LINEAR_LINK:1, SPLINE_LINK:2, NORMAL_TITLE:0, NO_TITLE:1, TRANSPARENT_TITLE:2, AUTOHIDE_TITLE:3, proxy:null, node_images_path:"", debug:!1, catch_exceptions:!0, throw_errors:!0, allow_scripts:!1, registered_node_types:{}, node_types_by_file_extension:{}, Nodes:{}, searchbox_extras:{}, registerNodeType:function(a, b) { + var e = w.LiteGraph = {VERSION:0.4, CANVAS_GRID_SIZE:10, NODE_TITLE_HEIGHT:30, NODE_TITLE_TEXT_Y:20, NODE_SLOT_HEIGHT:20, NODE_WIDGET_HEIGHT:20, NODE_WIDTH:140, NODE_MIN_WIDTH:50, NODE_COLLAPSED_RADIUS:10, NODE_COLLAPSED_WIDTH:80, NODE_TITLE_COLOR:"#999", NODE_TEXT_SIZE:14, NODE_TEXT_COLOR:"#AAA", NODE_SUBTEXT_SIZE:12, NODE_DEFAULT_COLOR:"#333", NODE_DEFAULT_BGCOLOR:"#353535", NODE_DEFAULT_BOXCOLOR:"#666", NODE_DEFAULT_SHAPE:"box", DEFAULT_SHADOW_COLOR:"rgba(0,0,0,0.5)", DEFAULT_GROUP_FONT:24, + WIDGET_BGCOLOR:"#222", WIDGET_OUTLINE_COLOR:"#666", WIDGET_TEXT_COLOR:"#DDD", WIDGET_SECONDARY_TEXT_COLOR:"#999", LINK_COLOR:"#9A9", EVENT_LINK_COLOR:"#A86", CONNECTING_LINK_COLOR:"#AFA", MAX_NUMBER_OF_NODES:1000, DEFAULT_POSITION:[100, 100], VALID_SHAPES:["default", "box", "round", "card"], BOX_SHAPE:1, ROUND_SHAPE:2, CIRCLE_SHAPE:3, CARD_SHAPE:4, ARROW_SHAPE:5, SQUARE_SHAPE:6, INPUT:1, OUTPUT:2, EVENT:-1, ACTION:-1, ALWAYS:0, ON_EVENT:1, NEVER:2, ON_TRIGGER:3, UP:1, DOWN:2, LEFT:3, RIGHT:4, CENTER:5, + STRAIGHT_LINK:0, LINEAR_LINK:1, SPLINE_LINK:2, NORMAL_TITLE:0, NO_TITLE:1, TRANSPARENT_TITLE:2, AUTOHIDE_TITLE:3, proxy:null, node_images_path:"", debug:!1, catch_exceptions:!0, throw_errors:!0, allow_scripts:!1, registered_node_types:{}, node_types_by_file_extension:{}, Nodes:{}, searchbox_extras:{}, clearRegisteredTypes:function() { + this.registered_node_types = {}; + this.node_types_by_file_extension = {}; + this.Nodes = {}; + this.searchbox_extras = {}; + }, registerNodeType:function(a, b) { if (!b.prototype) { throw "Cannot register a simple object, it must be a class with a prototype"; } b.type = a; e.debug && console.log("Node registered: " + a); a.split("/"); - var d = b.name, g = a.lastIndexOf("/"); - b.category = a.substr(0, g); + var d = b.name, f = a.lastIndexOf("/"); + b.category = a.substr(0, f); b.title || (b.title = d); if (b.prototype) { - for (var f in m.prototype) { - b.prototype[f] || (b.prototype[f] = m.prototype[f]); + for (var k in l.prototype) { + b.prototype[k] || (b.prototype[k] = l.prototype[k]); } } - if (g = this.registered_node_types[a]) { + if (f = this.registered_node_types[a]) { console.log("replacing node type: " + a); } else { if (Object.hasOwnProperty(b.prototype, "shape") || Object.defineProperty(b.prototype, "shape", {set:function(a) { @@ -328,9 +353,9 @@ $jscomp.polyfill("Object.values", function(v) { }, get:function(a) { return this._shape; }, enumerable:!0, configurable:!0}), b.prototype.onPropertyChange && console.warn("LiteGraph node class " + a + " has onPropertyChange method, it must be called onPropertyChanged with d at the end"), b.supported_extensions) { - for (f in b.supported_extensions) { - var C = b.supported_extensions[f]; - C && C.constructor === String && (this.node_types_by_file_extension[C.toLowerCase()] = b); + for (k in b.supported_extensions) { + var c = b.supported_extensions[k]; + c && c.constructor === String && (this.node_types_by_file_extension[c.toLowerCase()] = b); } } } @@ -339,8 +364,8 @@ $jscomp.polyfill("Object.values", function(v) { if (e.onNodeTypeRegistered) { e.onNodeTypeRegistered(a, b); } - if (g && e.onNodeTypeReplaced) { - e.onNodeTypeReplaced(a, b, g); + if (f && e.onNodeTypeReplaced) { + e.onNodeTypeReplaced(a, b, f); } }, unregisterNodeType:function(a) { var b = a.constructor === String ? this.registered_node_types[a] : a; @@ -349,74 +374,74 @@ $jscomp.polyfill("Object.values", function(v) { } delete this.registered_node_types[b.type]; b.constructor.name && delete this.Nodes[b.constructor.name]; - }, wrapFunctionAsNode:function(a, b, d, g, f) { - for (var C = Array(b.length), c = "", k = e.getParameterNames(b), n = 0; n < k.length; ++n) { - c += "this.addInput('" + k[n] + "'," + (d && d[n] ? "'" + d[n] + "'" : "0") + ");\n"; + }, wrapFunctionAsNode:function(a, b, d, f, k) { + for (var c = Array(b.length), F = "", h = e.getParameterNames(b), n = 0; n < h.length; ++n) { + F += "this.addInput('" + h[n] + "'," + (d && d[n] ? "'" + d[n] + "'" : "0") + ");\n"; } - c += "this.addOutput('out'," + (g ? "'" + g + "'" : 0) + ");\n"; - f && (c += "this.properties = " + JSON.stringify(f) + ";\n"); - d = Function(c); + F += "this.addOutput('out'," + (f ? "'" + f + "'" : 0) + ");\n"; + k && (F += "this.properties = " + JSON.stringify(k) + ";\n"); + d = Function(F); d.title = a.split("/").pop(); d.desc = "Generated from " + b.name; d.prototype.onExecute = function() { - for (var a = 0; a < C.length; ++a) { - C[a] = this.getInputData(a); + for (var a = 0; a < c.length; ++a) { + c[a] = this.getInputData(a); } - a = b.apply(this, C); + a = b.apply(this, c); this.setOutputData(0, a); }; this.registerNodeType(a, d); }, addNodeMethod:function(a, b) { - m.prototype[a] = b; + l.prototype[a] = b; for (var d in this.registered_node_types) { - var g = this.registered_node_types[d]; - g.prototype[a] && (g.prototype["_" + a] = g.prototype[a]); - g.prototype[a] = b; + var f = this.registered_node_types[d]; + f.prototype[a] && (f.prototype["_" + a] = f.prototype[a]); + f.prototype[a] = b; } }, createNode:function(a, b, d) { - var g = this.registered_node_types[a]; - if (!g) { + var f = this.registered_node_types[a]; + if (!f) { return e.debug && console.log('GraphNode type "' + a + '" not registered.'), null; } - b = b || g.title || a; - var f = null; + b = b || f.title || a; + var k = null; if (e.catch_exceptions) { try { - f = new g(b); - } catch (w) { - return console.error(w), null; + k = new f(b); + } catch (F) { + return console.error(F), null; } } else { - f = new g(b); + k = new f(b); } - f.type = a; - !f.title && b && (f.title = b); - f.properties || (f.properties = {}); - f.properties_info || (f.properties_info = []); - f.flags || (f.flags = {}); - f.size || (f.size = f.computeSize()); - f.pos || (f.pos = e.DEFAULT_POSITION.concat()); - f.mode || (f.mode = e.ALWAYS); + k.type = a; + !k.title && b && (k.title = b); + k.properties || (k.properties = {}); + k.properties_info || (k.properties_info = []); + k.flags || (k.flags = {}); + k.size || (k.size = k.computeSize()); + k.pos || (k.pos = e.DEFAULT_POSITION.concat()); + k.mode || (k.mode = e.ALWAYS); if (d) { - for (var C in d) { - f[C] = d[C]; + for (var c in d) { + k[c] = d[c]; } } - return f; + return k; }, getNodeType:function(a) { return this.registered_node_types[a]; }, getNodeTypesInCategory:function(a, b) { - var d = [], g; - for (g in this.registered_node_types) { - var f = this.registered_node_types[g]; - b && f.filter && f.filter != b || ("" == a ? null == f.category && d.push(f) : f.category == a && d.push(f)); + var d = [], f; + for (f in this.registered_node_types) { + var k = this.registered_node_types[f]; + b && k.filter && k.filter != b || ("" == a ? null == k.category && d.push(k) : k.category == a && d.push(k)); } return d; }, getNodeTypesCategories:function(a) { var b = {"":1}, d; for (d in this.registered_node_types) { - var g = this.registered_node_types[d]; - !g.category || g.skip_list || a && g.filter != a || (b[g.category] = 1); + var f = this.registered_node_types[d]; + !f.category || f.skip_list || a && f.filter != a || (b[f.category] = 1); } a = []; for (d in b) { @@ -424,27 +449,27 @@ $jscomp.polyfill("Object.values", function(v) { } return a; }, reloadNodes:function(a) { - var b = document.getElementsByTagName("script"), d = [], g; - for (g in b) { - d.push(b[g]); + var b = document.getElementsByTagName("script"), d = [], f; + for (f in b) { + d.push(b[f]); } b = document.getElementsByTagName("head")[0]; a = document.location.href + a; - for (g in d) { - var f = d[g].src; - if (f && f.substr(0, a.length) == a) { + for (f in d) { + var k = d[f].src; + if (k && k.substr(0, a.length) == a) { try { - e.debug && console.log("Reloading: " + f); - var C = document.createElement("script"); - C.type = "text/javascript"; - C.src = f; - b.appendChild(C); - b.removeChild(d[g]); - } catch (w) { + e.debug && console.log("Reloading: " + k); + var c = document.createElement("script"); + c.type = "text/javascript"; + c.src = k; + b.appendChild(c); + b.removeChild(d[f]); + } catch (F) { if (e.throw_errors) { - throw w; + throw F; } - e.debug && console.log("Error while reloading " + f); + e.debug && console.log("Error while reloading " + k); } } } @@ -475,8 +500,8 @@ $jscomp.polyfill("Object.values", function(v) { a = a.split(","); b = b.split(","); for (var d = 0; d < a.length; ++d) { - for (var g = 0; g < b.length; ++g) { - if (a[d] == b[g]) { + for (var f = 0; f < b.length; ++f) { + if (a[d] == b[f]) { return !0; } } @@ -484,7 +509,7 @@ $jscomp.polyfill("Object.values", function(v) { return !1; }, registerSearchboxExtra:function(a, b, d) { this.searchbox_extras[b.toLowerCase()] = {type:a, desc:b, data:d}; - }, fetchFile:function(a, b, d, g) { + }, fetchFile:function(a, b, d, f) { if (!a) { return null; } @@ -510,24 +535,24 @@ $jscomp.polyfill("Object.values", function(v) { d && d(a); }).catch(function(b) { console.error("error fetching file:", a); - g && g(b); + f && f(b); }); } if (a.constructor === File || a.constructor === Blob) { - var f = new FileReader; - f.onload = function(a) { + var k = new FileReader; + k.onload = function(a) { a = a.target.result; "json" == b && (a = JSON.parse(a)); d && d(a); }; if ("arraybuffer" == b) { - return f.readAsArrayBuffer(a); + return k.readAsArrayBuffer(a); } if ("text" == b || "json" == b) { - return f.readAsText(a); + return k.readAsText(a); } if ("blob" == b) { - return f.readAsBinaryString(a); + return k.readAsBinaryString(a); } } return null; @@ -538,7 +563,7 @@ $jscomp.polyfill("Object.values", function(v) { } : function() { return (new Date).getTime(); }; - v.LGraph = e.LGraph = c; + w.LGraph = e.LGraph = c; c.supported_types = ["number", "string", "boolean"]; c.prototype.getSupportedTypes = function() { return this.supported_types || c.supported_types; @@ -577,7 +602,7 @@ $jscomp.polyfill("Object.values", function(v) { this.sendActionToCanvas("clear"); }; c.prototype.attachCanvas = function(a) { - if (a.constructor != l) { + if (a.constructor != g) { throw "attachCanvas expects a LGraphCanvas instance"; } a.graph && a.graph != this && a.graph.detachCanvas(a); @@ -641,17 +666,17 @@ $jscomp.polyfill("Object.values", function(v) { }; c.prototype.runStep = function(a, b, d) { a = a || 1; - var g = e.getTime(); - this.globaltime = 0.001 * (g - this.starttime); - var f = this._nodes_executable ? this._nodes_executable : this._nodes; - if (f) { - d = d || f.length; + var f = e.getTime(); + this.globaltime = 0.001 * (f - this.starttime); + var k = this._nodes_executable ? this._nodes_executable : this._nodes; + if (k) { + d = d || k.length; if (b) { - for (var C = 0; C < a; C++) { - for (var c = 0; c < d; ++c) { - var k = f[c]; - if (k.mode == e.ALWAYS && k.onExecute) { - k.onExecute(); + for (var c = 0; c < a; c++) { + for (var F = 0; F < d; ++F) { + var h = k[F]; + if (h.mode == e.ALWAYS && h.onExecute) { + h.onExecute(); } } this.fixedtime += this.fixedtime_lapse; @@ -664,10 +689,10 @@ $jscomp.polyfill("Object.values", function(v) { } } else { try { - for (C = 0; C < a; C++) { - for (c = 0; c < d; ++c) { - if (k = f[c], k.mode == e.ALWAYS && k.onExecute) { - k.onExecute(); + for (c = 0; c < a; c++) { + for (F = 0; F < d; ++F) { + if (h = k[F], h.mode == e.ALWAYS && h.onExecute) { + h.onExecute(); } } this.fixedtime += this.fixedtime_lapse; @@ -689,10 +714,10 @@ $jscomp.polyfill("Object.values", function(v) { } } a = e.getTime(); - g = a - g; - 0 == g && (g = 1); - this.execution_time = 0.001 * g; - this.globaltime += 0.001 * g; + f = a - f; + 0 == f && (f = 1); + this.execution_time = 0.001 * f; + this.globaltime += 0.001 * f; this.iteration += 1; this.elapsed_time = 0.001 * (a - this.last_update_time); this.last_update_time = a; @@ -706,54 +731,54 @@ $jscomp.polyfill("Object.values", function(v) { } }; c.prototype.computeExecutionOrder = function(a, b) { - for (var d = [], g = [], f = {}, c = {}, w = {}, k = 0, n = this._nodes.length; k < n; ++k) { - var p = this._nodes[k]; - if (!a || p.onExecute) { - f[p.id] = p; - var l = 0; - if (p.inputs) { - for (var t = 0, h = p.inputs.length; t < h; t++) { - p.inputs[t] && null != p.inputs[t].link && (l += 1); + for (var d = [], f = [], k = {}, c = {}, F = {}, h = 0, n = this._nodes.length; h < n; ++h) { + var m = this._nodes[h]; + if (!a || m.onExecute) { + k[m.id] = m; + var x = 0; + if (m.inputs) { + for (var y = 0, g = m.inputs.length; y < g; y++) { + m.inputs[y] && null != m.inputs[y].link && (x += 1); } } - 0 == l ? (g.push(p), b && (p._level = 1)) : (b && (p._level = 0), w[p.id] = l); + 0 == x ? (f.push(m), b && (m._level = 1)) : (b && (m._level = 0), F[m.id] = x); } } - for (; 0 != g.length;) { - if (p = g.shift(), d.push(p), delete f[p.id], p.outputs) { - for (k = 0; k < p.outputs.length; k++) { - if (a = p.outputs[k], null != a && null != a.links && 0 != a.links.length) { - for (t = 0; t < a.links.length; t++) { - (n = this.links[a.links[t]]) && !c[n.id] && (l = this.getNodeById(n.target_id), null == l ? c[n.id] = !0 : (b && (!l._level || l._level <= p._level) && (l._level = p._level + 1), c[n.id] = !0, --w[l.id], 0 == w[l.id] && g.push(l))); + for (; 0 != f.length;) { + if (m = f.shift(), d.push(m), delete k[m.id], m.outputs) { + for (h = 0; h < m.outputs.length; h++) { + if (a = m.outputs[h], null != a && null != a.links && 0 != a.links.length) { + for (y = 0; y < a.links.length; y++) { + (n = this.links[a.links[y]]) && !c[n.id] && (x = this.getNodeById(n.target_id), null == x ? c[n.id] = !0 : (b && (!x._level || x._level <= m._level) && (x._level = m._level + 1), c[n.id] = !0, --F[x.id], 0 == F[x.id] && f.push(x))); } } } } } - for (k in f) { - d.push(f[k]); + for (h in k) { + d.push(k[h]); } d.length != this._nodes.length && e.debug && console.warn("something went wrong, nodes missing"); n = d.length; - for (k = 0; k < n; ++k) { - d[k].order = k; + for (h = 0; h < n; ++h) { + d[h].order = h; } d = d.sort(function(a, b) { var d = a.constructor.priority || a.priority || 0, f = b.constructor.priority || b.priority || 0; return d == f ? a.order - b.order : d - f; }); - for (k = 0; k < n; ++k) { - d[k].order = k; + for (h = 0; h < n; ++h) { + d[h].order = h; } return d; }; c.prototype.getAncestors = function(a) { - for (var b = [], d = [a], g = {}; d.length;) { - var f = d.shift(); - if (f.inputs) { - g[f.id] || f == a || (g[f.id] = !0, b.push(f)); - for (var e = 0; e < f.inputs.length; ++e) { - var c = f.getInputNode(e); + for (var b = [], d = [a], f = {}; d.length;) { + var k = d.shift(); + if (k.inputs) { + f[k.id] || k == a || (f[k.id] = !0, b.push(k)); + for (var e = 0; e < k.inputs.length; ++e) { + var c = k.getInputNode(e); c && -1 == b.indexOf(c) && d.push(c); } } @@ -765,18 +790,18 @@ $jscomp.polyfill("Object.values", function(v) { }; c.prototype.arrange = function(a) { a = a || 100; - for (var b = this.computeExecutionOrder(!1, !0), d = [], g = 0; g < b.length; ++g) { - var f = b[g], c = f._level || 1; + for (var b = this.computeExecutionOrder(!1, !0), d = [], f = 0; f < b.length; ++f) { + var k = b[f], c = k._level || 1; d[c] || (d[c] = []); - d[c].push(f); + d[c].push(k); } b = a; - for (g = 0; g < d.length; ++g) { - if (c = d[g]) { - for (var w = 100, k = a + e.NODE_TITLE_HEIGHT, n = 0; n < c.length; ++n) { - f = c[n], f.pos[0] = b, f.pos[1] = k, f.size[0] > w && (w = f.size[0]), k += f.size[1] + a + e.NODE_TITLE_HEIGHT; + for (f = 0; f < d.length; ++f) { + if (c = d[f]) { + for (var h = 100, n = a + e.NODE_TITLE_HEIGHT, m = 0; m < c.length; ++m) { + k = c[m], k.pos[0] = b, k.pos[1] = n, k.size[0] > h && (h = k.size[0]), n += k.size[1] + a + e.NODE_TITLE_HEIGHT; } - b += w + a; + b += h + a; } } this.setDirtyCanvas(!0, !0); @@ -792,21 +817,21 @@ $jscomp.polyfill("Object.values", function(v) { }; c.prototype.sendEventToAllNodes = function(a, b, d) { d = d || e.ALWAYS; - var g = this._nodes_in_order ? this._nodes_in_order : this._nodes; - if (g) { - for (var f = 0, c = g.length; f < c; ++f) { - var w = g[f]; - if (w.constructor === e.Subgraph && "onExecute" != a) { - w.mode == d && w.sendEventToAllNodes(a, b, d); + var f = this._nodes_in_order ? this._nodes_in_order : this._nodes; + if (f) { + for (var k = 0, c = f.length; k < c; ++k) { + var h = f[k]; + if (h.constructor === e.Subgraph && "onExecute" != a) { + h.mode == d && h.sendEventToAllNodes(a, b, d); } else { - if (w[a] && w.mode == d) { + if (h[a] && h.mode == d) { if (void 0 === b) { - w[a](); + h[a](); } else { if (b && b.constructor === Array) { - w[a].apply(w, b); + h[a].apply(h, b); } else { - w[a](b); + h[a](b); } } } @@ -817,14 +842,14 @@ $jscomp.polyfill("Object.values", function(v) { c.prototype.sendActionToCanvas = function(a, b) { if (this.list_of_graphcanvas) { for (var d = 0; d < this.list_of_graphcanvas.length; ++d) { - var g = this.list_of_graphcanvas[d]; - g[a] && g[a].apply(g, b); + var f = this.list_of_graphcanvas[d]; + f[a] && f[a].apply(f, b); } } }; c.prototype.add = function(a, b) { if (a) { - if (a.constructor === h) { + if (a.constructor === p) { this._groups.push(a), this.setDirtyCanvas(!0), this.change(), a.graph = this, this._version++; } else { -1 != a.id && null != this._nodes_by_id[a.id] && (console.warn("LiteGraph: there is already a node with this ID, changing it"), a.id = ++this.last_node_id); @@ -898,7 +923,7 @@ $jscomp.polyfill("Object.values", function(v) { }; c.prototype.findNodesByClass = function(a, b) { b = b || []; - for (var d = b.length = 0, g = this._nodes.length; d < g; ++d) { + for (var d = b.length = 0, f = this._nodes.length; d < f; ++d) { this._nodes[d].constructor === a && b.push(this._nodes[d]); } return b; @@ -906,7 +931,7 @@ $jscomp.polyfill("Object.values", function(v) { c.prototype.findNodesByType = function(a, b) { a = a.toLowerCase(); b = b || []; - for (var d = b.length = 0, g = this._nodes.length; d < g; ++d) { + for (var d = b.length = 0, f = this._nodes.length; d < f; ++d) { this._nodes[d].type.toLowerCase() == a && b.push(this._nodes[d]); } return b; @@ -920,26 +945,26 @@ $jscomp.polyfill("Object.values", function(v) { return null; }; c.prototype.findNodesByTitle = function(a) { - for (var b = [], d = 0, g = this._nodes.length; d < g; ++d) { + for (var b = [], d = 0, f = this._nodes.length; d < f; ++d) { this._nodes[d].title == a && b.push(this._nodes[d]); } return b; }; - c.prototype.getNodeOnPos = function(a, b, d, g) { + c.prototype.getNodeOnPos = function(a, b, d, f) { d = d || this._nodes; - for (var f = d.length - 1; 0 <= f; f--) { - var e = d[f]; - if (e.isPointInside(a, b, g)) { - return e; + for (var k = d.length - 1; 0 <= k; k--) { + var c = d[k]; + if (c.isPointInside(a, b, f)) { + return c; } } return null; }; c.prototype.getGroupOnPos = function(a, b) { for (var d = this._groups.length - 1; 0 <= d; d--) { - var g = this._groups[d]; - if (g.isPointInside(a, b, 2, !0)) { - return g; + var f = this._groups[d]; + if (f.isPointInside(a, b, 2, !0)) { + return f; } } return null; @@ -963,9 +988,9 @@ $jscomp.polyfill("Object.values", function(v) { c.prototype.onAction = function(a, b) { this._input_nodes = this.findNodesByClass(e.GraphInput, this._input_nodes); for (var d = 0; d < this._input_nodes.length; ++d) { - var g = this._input_nodes[d]; - if (g.properties.name == a) { - g.onAction(a, b); + var f = this._input_nodes[d]; + if (f.properties.name == a) { + f.onAction(a, b); break; } } @@ -1156,33 +1181,33 @@ $jscomp.polyfill("Object.values", function(v) { } d = []; for (b in this.links) { - var g = this.links[b]; - if (!g.serialize) { + var f = this.links[b]; + if (!f.serialize) { console.warn("weird LLink bug, link info is not a LLink but a regular object"); - var f = new r; - for (b in g) { - f[b] = g[b]; + var k = new t; + for (b in f) { + k[b] = f[b]; } - g = this.links[b] = f; + f = this.links[b] = k; } - d.push(g.serialize()); + d.push(f.serialize()); } - g = []; + f = []; for (b = 0; b < this._groups.length; ++b) { - g.push(this._groups[b].serialize()); + f.push(this._groups[b].serialize()); } - return {last_node_id:this.last_node_id, last_link_id:this.last_link_id, nodes:a, links:d, groups:g, config:this.config, version:e.VERSION}; + return {last_node_id:this.last_node_id, last_link_id:this.last_link_id, nodes:a, links:d, groups:f, config:this.config, version:e.VERSION}; }; c.prototype.configure = function(a, b) { if (a) { b || this.clear(); b = a.nodes; if (a.links && a.links.constructor === Array) { - for (var d = [], g = 0; g < a.links.length; ++g) { - var f = a.links[g]; - if (f) { - var c = new r; - c.configure(f); + for (var d = [], f = 0; f < a.links.length; ++f) { + var k = a.links[f]; + if (k) { + var c = new t; + c.configure(k); d[c.id] = c; } else { console.warn("serialized graph link data contains errors, skipping."); @@ -1190,29 +1215,29 @@ $jscomp.polyfill("Object.values", function(v) { } a.links = d; } - for (g in a) { - "nodes" != g && "groups" != g && (this[g] = a[g]); + for (f in a) { + "nodes" != f && "groups" != f && (this[f] = a[f]); } d = !1; this._nodes = []; if (b) { - g = 0; - for (f = b.length; g < f; ++g) { - c = b[g]; - var w = e.createNode(c.type, c.title); - w || (e.debug && console.log("Node not found or has errors: " + c.type), w = new m, w.last_serialization = c, d = w.has_errors = !0); - w.id = c.id; - this.add(w, !0); + f = 0; + for (k = b.length; f < k; ++f) { + c = b[f]; + var h = e.createNode(c.type, c.title); + h || (e.debug && console.log("Node not found or has errors: " + c.type), h = new l, h.last_serialization = c, d = h.has_errors = !0); + h.id = c.id; + this.add(h, !0); } - g = 0; - for (f = b.length; g < f; ++g) { - c = b[g], (w = this.getNodeById(c.id)) && w.configure(c); + f = 0; + for (k = b.length; f < k; ++f) { + c = b[f], (h = this.getNodeById(c.id)) && h.configure(c); } } this._groups.length = 0; if (a.groups) { - for (g = 0; g < a.groups.length; ++g) { - b = new e.LGraphGroup, b.configure(a.groups[g]), this.add(b); + for (f = 0; f < a.groups.length; ++f) { + b = new e.LGraphGroup, b.configure(a.groups[f]), this.add(b); } } this.updateExecutionOrder(); @@ -1234,15 +1259,15 @@ $jscomp.polyfill("Object.values", function(v) { }; c.prototype.onNodeTrace = function(a, b, d) { }; - r.prototype.configure = function(a) { + t.prototype.configure = function(a) { a.constructor === Array ? (this.id = a[0], this.origin_id = a[1], this.origin_slot = a[2], this.target_id = a[3], this.target_slot = a[4], this.type = a[5]) : (this.id = a.id, this.type = a.type, this.origin_id = a.origin_id, this.origin_slot = a.origin_slot, this.target_id = a.target_id, this.target_slot = a.target_slot); }; - r.prototype.serialize = function() { + t.prototype.serialize = function() { return [this.id, this.origin_id, this.origin_slot, this.target_id, this.target_slot, this.type]; }; - e.LLink = r; - v.LGraphNode = e.LGraphNode = m; - m.prototype._ctor = function(a) { + e.LLink = t; + w.LGraphNode = e.LGraphNode = l; + l.prototype._ctor = function(a) { this.title = a || "Unnamed"; this.size = [e.NODE_WIDTH, 60]; this.graph = null; @@ -1261,7 +1286,7 @@ $jscomp.polyfill("Object.values", function(v) { this.properties_info = []; this.flags = {}; }; - m.prototype.configure = function(a) { + l.prototype.configure = function(a) { this.graph && this.graph._version++; for (var b in a) { if ("properties" == b) { @@ -1279,16 +1304,16 @@ $jscomp.polyfill("Object.values", function(v) { if (this.inputs) { for (d = 0; d < this.inputs.length; ++d) { b = this.inputs[d]; - var g = this.graph ? this.graph.links[b.link] : null; - this.onConnectionsChange(e.INPUT, d, !0, g, b); + var f = this.graph ? this.graph.links[b.link] : null; + this.onConnectionsChange(e.INPUT, d, !0, f, b); } } if (this.outputs) { for (d = 0; d < this.outputs.length; ++d) { - var f = this.outputs[d]; - if (f.links) { - for (b = 0; b < f.links.length; ++b) { - g = this.graph ? this.graph.links[f.links[b]] : null, this.onConnectionsChange(e.OUTPUT, d, !0, g, f); + var k = this.outputs[d]; + if (k.links) { + for (b = 0; b < k.links.length; ++b) { + f = this.graph ? this.graph.links[k.links[b]] : null, this.onConnectionsChange(e.OUTPUT, d, !0, f, k); } } } @@ -1308,9 +1333,9 @@ $jscomp.polyfill("Object.values", function(v) { this.onConfigure(a); } }; - m.prototype.serialize = function() { + l.prototype.serialize = function() { var a = {id:this.id, type:this.type, pos:this.pos, size:this.size, flags:e.cloneObject(this.flags), order:this.order, mode:this.mode}; - if (this.constructor === m && this.last_serialization) { + if (this.constructor === l && this.last_serialization) { return this.last_serialization; } this.inputs && (a.inputs = this.inputs); @@ -1335,7 +1360,7 @@ $jscomp.polyfill("Object.values", function(v) { this.onSerialize && this.onSerialize(a) && console.warn("node onSerialize shouldnt return anything, data should be stored in the object pass in the first parameter"); return a; }; - m.prototype.clone = function() { + l.prototype.clone = function() { var a = e.createNode(this.type); if (!a) { return null; @@ -1355,13 +1380,13 @@ $jscomp.polyfill("Object.values", function(v) { a.configure(b); return a; }; - m.prototype.toString = function() { + l.prototype.toString = function() { return JSON.stringify(this.serialize()); }; - m.prototype.getTitle = function() { + l.prototype.getTitle = function() { return this.title || this.constructor.title; }; - m.prototype.setProperty = function(a, b) { + l.prototype.setProperty = function(a, b) { this.properties || (this.properties = {}); if (b !== this.properties[a]) { var d = this.properties[a]; @@ -1369,27 +1394,27 @@ $jscomp.polyfill("Object.values", function(v) { this.onPropertyChanged && !1 === this.onPropertyChanged(a, b, d) && (this.properties[a] = d); if (this.widgets) { for (d = 0; d < this.widgets.length; ++d) { - var g = this.widgets[d]; - if (g && g.options.property == a) { - g.value = b; + var f = this.widgets[d]; + if (f && f.options.property == a) { + f.value = b; break; } } } } }; - m.prototype.setOutputData = function(a, b) { + l.prototype.setOutputData = function(a, b) { if (this.outputs && !(-1 == a || a >= this.outputs.length)) { var d = this.outputs[a]; if (d && (d._data = b, this.outputs[a].links)) { for (d = 0; d < this.outputs[a].links.length; d++) { - var g = this.graph.links[this.outputs[a].links[d]]; - g && (g.data = b); + var f = this.graph.links[this.outputs[a].links[d]]; + f && (f.data = b); } } } }; - m.prototype.setOutputDataType = function(a, b) { + l.prototype.setOutputDataType = function(a, b) { if (this.outputs && !(-1 == a || a >= this.outputs.length)) { var d = this.outputs[a]; if (d && (d.type = b, this.outputs[a].links)) { @@ -1399,7 +1424,7 @@ $jscomp.polyfill("Object.values", function(v) { } } }; - m.prototype.getInputData = function(a, b) { + l.prototype.getInputData = function(a, b) { if (this.inputs && !(a >= this.inputs.length || null == this.inputs[a].link)) { a = this.graph.links[this.inputs[a].link]; if (!a) { @@ -1422,7 +1447,7 @@ $jscomp.polyfill("Object.values", function(v) { return a.data; } }; - m.prototype.getInputDataType = function(a) { + l.prototype.getInputDataType = function(a) { if (!this.inputs || a >= this.inputs.length || null == this.inputs[a].link) { return null; } @@ -1433,45 +1458,45 @@ $jscomp.polyfill("Object.values", function(v) { var b = this.graph.getNodeById(a.origin_id); return b ? (a = b.outputs[a.origin_slot]) ? a.type : null : a.type; }; - m.prototype.getInputDataByName = function(a, b) { + l.prototype.getInputDataByName = function(a, b) { a = this.findInputSlot(a); return -1 == a ? null : this.getInputData(a, b); }; - m.prototype.isInputConnected = function(a) { + l.prototype.isInputConnected = function(a) { return this.inputs ? a < this.inputs.length && null != this.inputs[a].link : !1; }; - m.prototype.getInputInfo = function(a) { + l.prototype.getInputInfo = function(a) { return this.inputs ? a < this.inputs.length ? this.inputs[a] : null : null; }; - m.prototype.getInputNode = function(a) { + l.prototype.getInputNode = function(a) { if (!this.inputs || a >= this.inputs.length) { return null; } a = this.inputs[a]; return a && null !== a.link ? (a = this.graph.links[a.link]) ? this.graph.getNodeById(a.origin_id) : null : null; }; - m.prototype.getInputOrProperty = function(a) { + l.prototype.getInputOrProperty = function(a) { if (!this.inputs || !this.inputs.length) { return this.properties ? this.properties[a] : null; } for (var b = 0, d = this.inputs.length; b < d; ++b) { - var g = this.inputs[b]; - if (a == g.name && null != g.link && (g = this.graph.links[g.link])) { - return g.data; + var f = this.inputs[b]; + if (a == f.name && null != f.link && (f = this.graph.links[f.link])) { + return f.data; } } return this.properties[a]; }; - m.prototype.getOutputData = function(a) { + l.prototype.getOutputData = function(a) { return !this.outputs || a >= this.outputs.length ? null : this.outputs[a]._data; }; - m.prototype.getOutputInfo = function(a) { + l.prototype.getOutputInfo = function(a) { return this.outputs ? a < this.outputs.length ? this.outputs[a] : null : null; }; - m.prototype.isOutputConnected = function(a) { + l.prototype.isOutputConnected = function(a) { return this.outputs ? a < this.outputs.length && this.outputs[a].links && this.outputs[a].links.length : !1; }; - m.prototype.isAnyOutputConnected = function() { + l.prototype.isAnyOutputConnected = function() { if (!this.outputs) { return !1; } @@ -1482,7 +1507,7 @@ $jscomp.polyfill("Object.values", function(v) { } return !1; }; - m.prototype.getOutputNodes = function(a) { + l.prototype.getOutputNodes = function(a) { if (!this.outputs || 0 == this.outputs.length || a >= this.outputs.length) { return null; } @@ -1491,33 +1516,33 @@ $jscomp.polyfill("Object.values", function(v) { return null; } for (var b = [], d = 0; d < a.links.length; d++) { - var g = this.graph.links[a.links[d]]; - g && (g = this.graph.getNodeById(g.target_id)) && b.push(g); + var f = this.graph.links[a.links[d]]; + f && (f = this.graph.getNodeById(f.target_id)) && b.push(f); } return b; }; - m.prototype.trigger = function(a, b) { + l.prototype.trigger = function(a, b) { if (this.outputs && this.outputs.length) { this.graph && (this.graph._last_trigger_time = e.getTime()); for (var d = 0; d < this.outputs.length; ++d) { - var g = this.outputs[d]; - !g || g.type !== e.EVENT || a && g.name != a || this.triggerSlot(d, b); + var f = this.outputs[d]; + !f || f.type !== e.EVENT || a && f.name != a || this.triggerSlot(d, b); } } }; - m.prototype.triggerSlot = function(a, b, d) { + l.prototype.triggerSlot = function(a, b, d) { if (this.outputs && (a = this.outputs[a]) && (a = a.links) && a.length) { this.graph && (this.graph._last_trigger_time = e.getTime()); - for (var g = 0; g < a.length; ++g) { - var f = a[g]; - if (null == d || d == f) { - var c = this.graph.links[a[g]]; - if (c && (c._last_time = e.getTime(), f = this.graph.getNodeById(c.target_id))) { - if (c = f.inputs[c.target_slot], f.onAction) { - f.onAction(c.name, b); + for (var f = 0; f < a.length; ++f) { + var k = a[f]; + if (null == d || d == k) { + var c = this.graph.links[a[f]]; + if (c && (c._last_time = e.getTime(), k = this.graph.getNodeById(c.target_id))) { + if (c = k.inputs[c.target_slot], k.onAction) { + k.onAction(c.name, b); } else { - if (f.mode === e.ON_TRIGGER && f.onExecute) { - f.onExecute(b); + if (k.mode === e.ON_TRIGGER && k.onExecute) { + k.onExecute(b); } } } @@ -1525,29 +1550,29 @@ $jscomp.polyfill("Object.values", function(v) { } } }; - m.prototype.clearTriggeredSlot = function(a, b) { + l.prototype.clearTriggeredSlot = function(a, b) { if (this.outputs && (a = this.outputs[a]) && (a = a.links) && a.length) { for (var d = 0; d < a.length; ++d) { - var g = a[d]; - if (null == b || b == g) { - if (g = this.graph.links[a[d]]) { - g._last_time = 0; + var f = a[d]; + if (null == b || b == f) { + if (f = this.graph.links[a[d]]) { + f._last_time = 0; } } } } }; - m.prototype.setSize = function(a) { + l.prototype.setSize = function(a) { this.size = a; if (this.onResize) { this.onResize(this.size); } }; - m.prototype.addProperty = function(a, b, d, g) { + l.prototype.addProperty = function(a, b, d, f) { d = {name:a, type:d, default_value:b}; - if (g) { - for (var f in g) { - d[f] = g[f]; + if (f) { + for (var k in f) { + d[k] = f[k]; } } this.properties_info || (this.properties_info = []); @@ -1556,11 +1581,11 @@ $jscomp.polyfill("Object.values", function(v) { this.properties[a] = b; return d; }; - m.prototype.addOutput = function(a, b, d) { + l.prototype.addOutput = function(a, b, d) { a = {name:a, type:b, links:null}; if (d) { - for (var g in d) { - a[g] = d[g]; + for (var f in d) { + a[f] = d[f]; } } this.outputs || (this.outputs = []); @@ -1572,31 +1597,31 @@ $jscomp.polyfill("Object.values", function(v) { this.setDirtyCanvas(!0, !0); return a; }; - m.prototype.addOutputs = function(a) { + l.prototype.addOutputs = function(a) { for (var b = 0; b < a.length; ++b) { - var d = a[b], g = {name:d[0], type:d[1], link:null}; + var d = a[b], f = {name:d[0], type:d[1], link:null}; if (a[2]) { - for (var f in d[2]) { - g[f] = d[2][f]; + for (var k in d[2]) { + f[k] = d[2][k]; } } this.outputs || (this.outputs = []); - this.outputs.push(g); + this.outputs.push(f); if (this.onOutputAdded) { - this.onOutputAdded(g); + this.onOutputAdded(f); } } this.setSize(this.computeSize()); this.setDirtyCanvas(!0, !0); }; - m.prototype.removeOutput = function(a) { + l.prototype.removeOutput = function(a) { this.disconnectOutput(a); this.outputs.splice(a, 1); for (var b = a; b < this.outputs.length; ++b) { if (this.outputs[b] && this.outputs[b].links) { - for (var d = this.outputs[b].links, g = 0; g < d.length; ++g) { - var f = this.graph.links[d[g]]; - f && --f.origin_slot; + for (var d = this.outputs[b].links, f = 0; f < d.length; ++f) { + var k = this.graph.links[d[f]]; + k && --k.origin_slot; } } } @@ -1606,11 +1631,11 @@ $jscomp.polyfill("Object.values", function(v) { } this.setDirtyCanvas(!0, !0); }; - m.prototype.addInput = function(a, b, d) { + l.prototype.addInput = function(a, b, d) { a = {name:a, type:b || 0, link:null}; if (d) { - for (var g in d) { - a[g] = d[g]; + for (var f in d) { + a[f] = d[f]; } } this.inputs || (this.inputs = []); @@ -1622,24 +1647,24 @@ $jscomp.polyfill("Object.values", function(v) { this.setDirtyCanvas(!0, !0); return a; }; - m.prototype.addInputs = function(a) { + l.prototype.addInputs = function(a) { for (var b = 0; b < a.length; ++b) { - var d = a[b], g = {name:d[0], type:d[1], link:null}; + var d = a[b], f = {name:d[0], type:d[1], link:null}; if (a[2]) { - for (var f in d[2]) { - g[f] = d[2][f]; + for (var k in d[2]) { + f[k] = d[2][k]; } } this.inputs || (this.inputs = []); - this.inputs.push(g); + this.inputs.push(f); if (this.onInputAdded) { - this.onInputAdded(g); + this.onInputAdded(f); } } this.setSize(this.computeSize()); this.setDirtyCanvas(!0, !0); }; - m.prototype.removeInput = function(a) { + l.prototype.removeInput = function(a) { this.disconnectInput(a); this.inputs.splice(a, 1); for (var b = a; b < this.inputs.length; ++b) { @@ -1654,53 +1679,53 @@ $jscomp.polyfill("Object.values", function(v) { } this.setDirtyCanvas(!0, !0); }; - m.prototype.addConnection = function(a, b, d, g) { - a = {name:a, type:b, pos:d, direction:g, links:null}; + l.prototype.addConnection = function(a, b, d, f) { + a = {name:a, type:b, pos:d, direction:f, links:null}; this.connections.push(a); return a; }; - m.prototype.computeSize = function(a, b) { + l.prototype.computeSize = function(a, b) { function d(a) { - return a ? f * a.length * 0.6 : 0; + return a ? k * a.length * 0.6 : 0; } if (this.constructor.size) { return this.constructor.size.concat(); } - var g = Math.max(this.inputs ? this.inputs.length : 1, this.outputs ? this.outputs.length : 1); + var f = Math.max(this.inputs ? this.inputs.length : 1, this.outputs ? this.outputs.length : 1); b = b || new Float32Array([0, 0]); - g = Math.max(g, 1); - var f = e.NODE_TEXT_SIZE, c = d(this.title), w = 0, k = 0; + f = Math.max(f, 1); + var k = e.NODE_TEXT_SIZE, c = d(this.title), h = 0, n = 0; if (this.inputs) { - for (var n = 0, p = this.inputs.length; n < p; ++n) { - var l = this.inputs[n]; - l = l.label || l.name || ""; - l = d(l); - w < l && (w = l); + for (var m = 0, g = this.inputs.length; m < g; ++m) { + var x = this.inputs[m]; + x = x.label || x.name || ""; + x = d(x); + h < x && (h = x); } } if (this.outputs) { - for (n = 0, p = this.outputs.length; n < p; ++n) { - l = this.outputs[n], l = l.label || l.name || "", l = d(l), k < l && (k = l); + for (m = 0, g = this.outputs.length; m < g; ++m) { + x = this.outputs[m], x = x.label || x.name || "", x = d(x), n < x && (n = x); } } - b[0] = Math.max(w + k + 10, c); + b[0] = Math.max(h + n + 10, c); b[0] = Math.max(b[0], e.NODE_WIDTH); this.widgets && this.widgets.length && (b[0] = Math.max(b[0], 1.5 * e.NODE_WIDTH)); - b[1] = (this.constructor.slot_start_y || 0) + g * e.NODE_SLOT_HEIGHT; - g = 0; + b[1] = (this.constructor.slot_start_y || 0) + f * e.NODE_SLOT_HEIGHT; + f = 0; if (this.widgets && this.widgets.length) { - n = 0; - for (p = this.widgets.length; n < p; ++n) { - g = this.widgets[n].computeSize ? g + (this.widgets[n].computeSize(Math.max(b[0], a || 0))[1] + 4) : g + (e.NODE_WIDGET_HEIGHT + 4); + m = 0; + for (g = this.widgets.length; m < g; ++m) { + f = this.widgets[m].computeSize ? f + (this.widgets[m].computeSize(Math.max(b[0], a || 0))[1] + 4) : f + (e.NODE_WIDGET_HEIGHT + 4); } - g += 8; + f += 8; } - b[1] = this.widgets_up ? Math.max(b[1], g) : null != this.widgets_start_y ? Math.max(b[1], g + this.widgets_start_y) : b[1] + g; + b[1] = this.widgets_up ? Math.max(b[1], f) : null != this.widgets_start_y ? Math.max(b[1], f + this.widgets_start_y) : b[1] + f; this.constructor.min_height && b[1] < this.constructor.min_height && (b[1] = this.constructor.min_height); b[1] += 6; return b; }; - m.prototype.getPropertyInfo = function(a) { + l.prototype.getPropertyInfo = function(a) { var b = null; if (this.properties_info) { for (var d = 0; d < this.properties_info.length; ++d) { @@ -1716,15 +1741,15 @@ $jscomp.polyfill("Object.values", function(v) { b.type || (b.type = typeof this.properties[a]); return b; }; - m.prototype.addWidget = function(a, b, d, g, f) { + l.prototype.addWidget = function(a, b, d, f, k) { this.widgets || (this.widgets = []); - !f && g && g.constructor === Object && (f = g, g = null); - f && f.constructor === String && (f = {property:f}); - g && g.constructor === String && (f || (f = {}), f.property = g, g = null); - g && g.constructor !== Function && (console.warn("addWidget: callback must be a function"), g = null); - b = {type:a.toLowerCase(), name:b, value:d, callback:g, options:f || {}}; + !k && f && f.constructor === Object && (k = f, f = null); + k && k.constructor === String && (k = {property:k}); + f && f.constructor === String && (k || (k = {}), k.property = f, f = null); + f && f.constructor !== Function && (console.warn("addWidget: callback must be a function"), f = null); + b = {type:a.toLowerCase(), name:b, value:d, callback:f, options:k || {}}; void 0 !== b.options.y && (b.y = b.options.y); - g || b.options.callback || b.options.property || console.warn("LiteGraph addWidget(...) without a callback or property assigned"); + f || b.options.callback || b.options.property || console.warn("LiteGraph addWidget(...) without a callback or property assigned"); if ("combo" == a && !b.options.values) { throw "LiteGraph addWidget('combo',...) requires to pass values in options: { values:['red','blue'] }"; } @@ -1732,12 +1757,12 @@ $jscomp.polyfill("Object.values", function(v) { this.setSize(this.computeSize()); return b; }; - m.prototype.addCustomWidget = function(a) { + l.prototype.addCustomWidget = function(a) { this.widgets || (this.widgets = []); this.widgets.push(a); return a; }; - m.prototype.getBounding = function(a) { + l.prototype.getBounding = function(a) { a = a || new Float32Array(4); a[0] = this.pos[0] - 4; a[1] = this.pos[1] - e.NODE_TITLE_HEIGHT; @@ -1748,42 +1773,42 @@ $jscomp.polyfill("Object.values", function(v) { } return a; }; - m.prototype.isPointInside = function(a, b, d, g) { + l.prototype.isPointInside = function(a, b, d, f) { d = d || 0; - var f = this.graph && this.graph.isLive() ? 0 : e.NODE_TITLE_HEIGHT; - g && (f = 0); + var k = this.graph && this.graph.isLive() ? 0 : e.NODE_TITLE_HEIGHT; + f && (k = 0); if (this.flags && this.flags.collapsed) { - if (y(a, b, this.pos[0] - d, this.pos[1] - e.NODE_TITLE_HEIGHT - d, (this._collapsed_width || e.NODE_COLLAPSED_WIDTH) + 2 * d, e.NODE_TITLE_HEIGHT + 2 * d)) { + if (v(a, b, this.pos[0] - d, this.pos[1] - e.NODE_TITLE_HEIGHT - d, (this._collapsed_width || e.NODE_COLLAPSED_WIDTH) + 2 * d, e.NODE_TITLE_HEIGHT + 2 * d)) { return !0; } } else { - if (this.pos[0] - 4 - d < a && this.pos[0] + this.size[0] + 4 + d > a && this.pos[1] - f - d < b && this.pos[1] + this.size[1] + d > b) { + if (this.pos[0] - 4 - d < a && this.pos[0] + this.size[0] + 4 + d > a && this.pos[1] - k - d < b && this.pos[1] + this.size[1] + d > b) { return !0; } } return !1; }; - m.prototype.getSlotInPosition = function(a, b) { + l.prototype.getSlotInPosition = function(a, b) { var d = new Float32Array(2); if (this.inputs) { - for (var g = 0, f = this.inputs.length; g < f; ++g) { - var e = this.inputs[g]; - this.getConnectionPos(!0, g, d); - if (y(a, b, d[0] - 10, d[1] - 5, 20, 10)) { - return {input:e, slot:g, link_pos:d}; + for (var f = 0, k = this.inputs.length; f < k; ++f) { + var c = this.inputs[f]; + this.getConnectionPos(!0, f, d); + if (v(a, b, d[0] - 10, d[1] - 5, 20, 10)) { + return {input:c, slot:f, link_pos:d}; } } } if (this.outputs) { - for (g = 0, f = this.outputs.length; g < f; ++g) { - if (e = this.outputs[g], this.getConnectionPos(!1, g, d), y(a, b, d[0] - 10, d[1] - 5, 20, 10)) { - return {output:e, slot:g, link_pos:d}; + for (f = 0, k = this.outputs.length; f < k; ++f) { + if (c = this.outputs[f], this.getConnectionPos(!1, f, d), v(a, b, d[0] - 10, d[1] - 5, 20, 10)) { + return {output:c, slot:f, link_pos:d}; } } } return null; }; - m.prototype.findInputSlot = function(a) { + l.prototype.findInputSlot = function(a) { if (!this.inputs) { return -1; } @@ -1794,7 +1819,7 @@ $jscomp.polyfill("Object.values", function(v) { } return -1; }; - m.prototype.findOutputSlot = function(a) { + l.prototype.findOutputSlot = function(a) { if (!this.outputs) { return -1; } @@ -1805,7 +1830,7 @@ $jscomp.polyfill("Object.values", function(v) { } return -1; }; - m.prototype.connect = function(a, b, d) { + l.prototype.connect = function(a, b, d) { d = d || 0; if (!this.graph) { return console.log("Connect: Error, node doesn't belong to any graph. Nodes must be added first to a graph before connecting them."), null; @@ -1838,24 +1863,25 @@ $jscomp.polyfill("Object.values", function(v) { return e.debug && console.log("Connect: Error, slot number not found"), null; } } - null != b.inputs[d].link && b.disconnectInput(d); - var g = this.outputs[a]; - if (b.onConnectInput && !1 === b.onConnectInput(d, g.type, g, this, a)) { - return null; - } - var f = b.inputs[d], c = null; - if (e.isValidConnection(g.type, f.type)) { - c = new r(++this.graph.last_link_id, f.type, this.id, a, b.id, d); + if (e.isValidConnection(f.type, k.type)) { + b.onBeforeConnectInput && (d = b.onBeforeConnectInput(d)); + null != b.inputs[d].link && b.disconnectInput(d); + var f = this.outputs[a]; + if (b.onConnectInput && !1 === b.onConnectInput(d, f.type, f, this, a)) { + return null; + } + var k = b.inputs[d]; + var c = new t(++this.graph.last_link_id, k.type, this.id, a, b.id, d); this.graph.links[c.id] = c; - null == g.links && (g.links = []); - g.links.push(c.id); + null == f.links && (f.links = []); + f.links.push(c.id); b.inputs[d].link = c.id; this.graph && this.graph._version++; if (this.onConnectionsChange) { - this.onConnectionsChange(e.OUTPUT, a, !0, c, g); + this.onConnectionsChange(e.OUTPUT, a, !0, c, f); } if (b.onConnectionsChange) { - b.onConnectionsChange(e.INPUT, d, !0, c, f); + b.onConnectionsChange(e.INPUT, d, !0, c, k); } this.graph && this.graph.onNodeConnectionChange && (this.graph.onNodeConnectionChange(e.INPUT, b, d, this, a), this.graph.onNodeConnectionChange(e.OUTPUT, this, a, b, d)); } @@ -1863,7 +1889,7 @@ $jscomp.polyfill("Object.values", function(v) { this.graph.connectionChange(this, c); return c; }; - m.prototype.disconnectOutput = function(a, b) { + l.prototype.disconnectOutput = function(a, b) { if (a.constructor === String) { if (a = this.findOutputSlot(a), -1 == a) { return e.debug && console.log("Connect: Error, no slot of name " + a), !1; @@ -1882,48 +1908,48 @@ $jscomp.polyfill("Object.values", function(v) { if (!b) { throw "Target Node not found"; } - for (var g = 0, f = d.links.length; g < f; g++) { - var c = d.links[g], w = this.graph.links[c]; - if (w.target_id == b.id) { - d.links.splice(g, 1); - var k = b.inputs[w.target_slot]; - k.link = null; + for (var f = 0, k = d.links.length; f < k; f++) { + var c = d.links[f], h = this.graph.links[c]; + if (h.target_id == b.id) { + d.links.splice(f, 1); + var m = b.inputs[h.target_slot]; + m.link = null; delete this.graph.links[c]; this.graph && this.graph._version++; if (b.onConnectionsChange) { - b.onConnectionsChange(e.INPUT, w.target_slot, !1, w, k); + b.onConnectionsChange(e.INPUT, h.target_slot, !1, h, m); } if (this.onConnectionsChange) { - this.onConnectionsChange(e.OUTPUT, a, !1, w, d); + this.onConnectionsChange(e.OUTPUT, a, !1, h, d); } if (this.graph && this.graph.onNodeConnectionChange) { this.graph.onNodeConnectionChange(e.OUTPUT, this, a); } - this.graph && this.graph.onNodeConnectionChange && (this.graph.onNodeConnectionChange(e.OUTPUT, this, a), this.graph.onNodeConnectionChange(e.INPUT, b, w.target_slot)); + this.graph && this.graph.onNodeConnectionChange && (this.graph.onNodeConnectionChange(e.OUTPUT, this, a), this.graph.onNodeConnectionChange(e.INPUT, b, h.target_slot)); break; } } } else { - g = 0; - for (f = d.links.length; g < f; g++) { - if (c = d.links[g], w = this.graph.links[c]) { - b = this.graph.getNodeById(w.target_id); + f = 0; + for (k = d.links.length; f < k; f++) { + if (c = d.links[f], h = this.graph.links[c]) { + b = this.graph.getNodeById(h.target_id); this.graph && this.graph._version++; if (b) { - k = b.inputs[w.target_slot]; - k.link = null; + m = b.inputs[h.target_slot]; + m.link = null; if (b.onConnectionsChange) { - b.onConnectionsChange(e.INPUT, w.target_slot, !1, w, k); + b.onConnectionsChange(e.INPUT, h.target_slot, !1, h, m); } if (this.graph && this.graph.onNodeConnectionChange) { - this.graph.onNodeConnectionChange(e.INPUT, b, w.target_slot); + this.graph.onNodeConnectionChange(e.INPUT, b, h.target_slot); } } delete this.graph.links[c]; if (this.onConnectionsChange) { - this.onConnectionsChange(e.OUTPUT, a, !1, w, d); + this.onConnectionsChange(e.OUTPUT, a, !1, h, d); } - this.graph && this.graph.onNodeConnectionChange && (this.graph.onNodeConnectionChange(e.OUTPUT, this, a), this.graph.onNodeConnectionChange(e.INPUT, b, w.target_slot)); + this.graph && this.graph.onNodeConnectionChange && (this.graph.onNodeConnectionChange(e.OUTPUT, this, a), this.graph.onNodeConnectionChange(e.INPUT, b, h.target_slot)); } } d.links = null; @@ -1932,7 +1958,7 @@ $jscomp.polyfill("Object.values", function(v) { this.graph.connectionChange(this); return !0; }; - m.prototype.disconnectInput = function(a) { + l.prototype.disconnectInput = function(a) { if (a.constructor === String) { if (a = this.findInputSlot(a), -1 == a) { return e.debug && console.log("Connect: Error, no slot of name " + a), !1; @@ -1948,75 +1974,75 @@ $jscomp.polyfill("Object.values", function(v) { } var d = this.inputs[a].link; this.inputs[a].link = null; - var g = this.graph.links[d]; - if (g) { - var f = this.graph.getNodeById(g.origin_id); - if (!f) { + var f = this.graph.links[d]; + if (f) { + var k = this.graph.getNodeById(f.origin_id); + if (!k) { return !1; } - var c = f.outputs[g.origin_slot]; + var c = k.outputs[f.origin_slot]; if (!c || !c.links || 0 == c.links.length) { return !1; } - for (var k = 0, n = c.links.length; k < n; k++) { - if (c.links[k] == d) { - c.links.splice(k, 1); + for (var h = 0, m = c.links.length; h < m; h++) { + if (c.links[h] == d) { + c.links.splice(h, 1); break; } } delete this.graph.links[d]; this.graph && this.graph._version++; if (this.onConnectionsChange) { - this.onConnectionsChange(e.INPUT, a, !1, g, b); + this.onConnectionsChange(e.INPUT, a, !1, f, b); } - if (f.onConnectionsChange) { - f.onConnectionsChange(e.OUTPUT, k, !1, g, c); + if (k.onConnectionsChange) { + k.onConnectionsChange(e.OUTPUT, h, !1, f, c); } - this.graph && this.graph.onNodeConnectionChange && (this.graph.onNodeConnectionChange(e.OUTPUT, f, k), this.graph.onNodeConnectionChange(e.INPUT, this, a)); + this.graph && this.graph.onNodeConnectionChange && (this.graph.onNodeConnectionChange(e.OUTPUT, k, h), this.graph.onNodeConnectionChange(e.INPUT, this, a)); } this.setDirtyCanvas(!1, !0); this.graph.connectionChange(this); return !0; }; - m.prototype.getConnectionPos = function(a, b, d) { + l.prototype.getConnectionPos = function(a, b, d) { d = d || new Float32Array(2); - var g = 0; - a && this.inputs && (g = this.inputs.length); - !a && this.outputs && (g = this.outputs.length); - var f = 0.5 * e.NODE_SLOT_HEIGHT; + var f = 0; + a && this.inputs && (f = this.inputs.length); + !a && this.outputs && (f = this.outputs.length); + var k = 0.5 * e.NODE_SLOT_HEIGHT; if (this.flags.collapsed) { return b = this._collapsed_width || e.NODE_COLLAPSED_WIDTH, this.horizontal ? (d[0] = this.pos[0] + 0.5 * b, d[1] = a ? this.pos[1] - e.NODE_TITLE_HEIGHT : this.pos[1]) : (d[0] = a ? this.pos[0] : this.pos[0] + b, d[1] = this.pos[1] - 0.5 * e.NODE_TITLE_HEIGHT), d; } if (a && -1 == b) { return d[0] = this.pos[0] + 0.5 * e.NODE_TITLE_HEIGHT, d[1] = this.pos[1] + 0.5 * e.NODE_TITLE_HEIGHT, d; } - if (a && g > b && this.inputs[b].pos) { + if (a && f > b && this.inputs[b].pos) { return d[0] = this.pos[0] + this.inputs[b].pos[0], d[1] = this.pos[1] + this.inputs[b].pos[1], d; } - if (!a && g > b && this.outputs[b].pos) { + if (!a && f > b && this.outputs[b].pos) { return d[0] = this.pos[0] + this.outputs[b].pos[0], d[1] = this.pos[1] + this.outputs[b].pos[1], d; } if (this.horizontal) { - return d[0] = this.pos[0] + this.size[0] / g * (b + 0.5), d[1] = a ? this.pos[1] - e.NODE_TITLE_HEIGHT : this.pos[1] + this.size[1], d; + return d[0] = this.pos[0] + this.size[0] / f * (b + 0.5), d[1] = a ? this.pos[1] - e.NODE_TITLE_HEIGHT : this.pos[1] + this.size[1], d; } - d[0] = a ? this.pos[0] + f : this.pos[0] + this.size[0] + 1 - f; + d[0] = a ? this.pos[0] + k : this.pos[0] + this.size[0] + 1 - k; d[1] = this.pos[1] + (b + 0.7) * e.NODE_SLOT_HEIGHT + (this.constructor.slot_start_y || 0); return d; }; - m.prototype.alignToGrid = function() { + l.prototype.alignToGrid = function() { this.pos[0] = e.CANVAS_GRID_SIZE * Math.round(this.pos[0] / e.CANVAS_GRID_SIZE); this.pos[1] = e.CANVAS_GRID_SIZE * Math.round(this.pos[1] / e.CANVAS_GRID_SIZE); }; - m.prototype.trace = function(a) { + l.prototype.trace = function(a) { this.console || (this.console = []); this.console.push(a); - this.console.length > m.MAX_CONSOLE && this.console.shift(); + this.console.length > l.MAX_CONSOLE && this.console.shift(); this.graph.onNodeTrace(this, a); }; - m.prototype.setDirtyCanvas = function(a, b) { + l.prototype.setDirtyCanvas = function(a, b) { this.graph && this.graph.sendActionToCanvas("setDirty", [a, b]); }; - m.prototype.loadImage = function(a) { + l.prototype.loadImage = function(a) { var b = new Image; b.src = e.node_images_path + a; b.ready = !1; @@ -2027,34 +2053,34 @@ $jscomp.polyfill("Object.values", function(v) { }; return b; }; - m.prototype.captureInput = function(a) { + l.prototype.captureInput = function(a) { if (this.graph && this.graph.list_of_graphcanvas) { for (var b = this.graph.list_of_graphcanvas, d = 0; d < b.length; ++d) { - var g = b[d]; - if (a || g.node_capturing_input == this) { - g.node_capturing_input = a ? this : null; + var f = b[d]; + if (a || f.node_capturing_input == this) { + f.node_capturing_input = a ? this : null; } } } }; - m.prototype.collapse = function(a) { + l.prototype.collapse = function(a) { this.graph._version++; if (!1 !== this.constructor.collapsable || a) { this.flags.collapsed = this.flags.collapsed ? !1 : !0, this.setDirtyCanvas(!0, !0); } }; - m.prototype.pin = function(a) { + l.prototype.pin = function(a) { this.graph._version++; this.flags.pinned = void 0 === a ? !this.flags.pinned : a; }; - m.prototype.localToScreen = function(a, b, d) { + l.prototype.localToScreen = function(a, b, d) { return [(a + this.pos[0]) * d.scale + d.offset[0], (b + this.pos[1]) * d.scale + d.offset[1]]; }; - v.LGraphGroup = e.LGraphGroup = h; - h.prototype._ctor = function(a) { + w.LGraphGroup = e.LGraphGroup = p; + p.prototype._ctor = function(a) { this.title = a || "Group"; this.font_size = 24; - this.color = l.node_colors.pale_blue ? l.node_colors.pale_blue.groupcolor : "#AAA"; + this.color = g.node_colors.pale_blue ? g.node_colors.pale_blue.groupcolor : "#AAA"; this._bounding = new Float32Array([10, 10, 140, 80]); this._pos = this._bounding.subarray(0, 2); this._size = this._bounding.subarray(2, 4); @@ -2071,37 +2097,37 @@ $jscomp.polyfill("Object.values", function(v) { return this._size; }, enumerable:!0}); }; - h.prototype.configure = function(a) { + p.prototype.configure = function(a) { this.title = a.title; this._bounding.set(a.bounding); this.color = a.color; this.font = a.font; }; - h.prototype.serialize = function() { + p.prototype.serialize = function() { var a = this._bounding; return {title:this.title, bounding:[Math.round(a[0]), Math.round(a[1]), Math.round(a[2]), Math.round(a[3])], color:this.color, font:this.font}; }; - h.prototype.move = function(a, b, d) { + p.prototype.move = function(a, b, d) { this._pos[0] += a; this._pos[1] += b; if (!d) { for (d = 0; d < this._nodes.length; ++d) { - var g = this._nodes[d]; - g.pos[0] += a; - g.pos[1] += b; + var f = this._nodes[d]; + f.pos[0] += a; + f.pos[1] += b; } } }; - h.prototype.recomputeInsideNodes = function() { + p.prototype.recomputeInsideNodes = function() { this._nodes.length = 0; for (var a = this.graph._nodes, b = new Float32Array(4), d = 0; d < a.length; ++d) { - var g = a[d]; - g.getBounding(b); - x(this._bounding, b) && this._nodes.push(g); + var f = a[d]; + f.getBounding(b); + G(this._bounding, b) && this._nodes.push(f); } }; - h.prototype.isPointInside = m.prototype.isPointInside; - h.prototype.setDirtyCanvas = m.prototype.setDirtyCanvas; + p.prototype.isPointInside = l.prototype.isPointInside; + p.prototype.setDirtyCanvas = l.prototype.setDirtyCanvas; e.DragAndScale = q; q.prototype.bindEvents = function(a) { this.last_mouse = new Float32Array(2); @@ -2113,29 +2139,29 @@ $jscomp.polyfill("Object.values", function(v) { }; q.prototype.computeVisibleArea = function() { if (this.element) { - var a = -this.offset[0], b = -this.offset[1], d = a + this.element.width / this.scale, g = b + this.element.height / this.scale; + var a = -this.offset[0], b = -this.offset[1], d = a + this.element.width / this.scale, f = b + this.element.height / this.scale; this.visible_area[0] = a; this.visible_area[1] = b; this.visible_area[2] = d - a; - this.visible_area[3] = g - b; + this.visible_area[3] = f - b; } else { this.visible_area[0] = this.visible_area[1] = this.visible_area[2] = this.visible_area[3] = 0; } }; q.prototype.onMouse = function(a) { if (this.enabled) { - var b = this.element, d = b.getBoundingClientRect(), g = a.clientX - d.left; + var b = this.element, d = b.getBoundingClientRect(), f = a.clientX - d.left; d = a.clientY - d.top; - a.canvasx = g; + a.canvasx = f; a.canvasy = d; a.dragging = this.dragging; - var f = !1; - this.onmouse && (f = this.onmouse(a)); + var k = !1; + this.onmouse && (k = this.onmouse(a)); if ("mousedown" == a.type) { this.dragging = !0, b.removeEventListener("mousemove", this._binded_mouse_callback), document.body.addEventListener("mousemove", this._binded_mouse_callback), document.body.addEventListener("mouseup", this._binded_mouse_callback); } else { if ("mousemove" == a.type) { - f || (b = g - this.last_mouse[0], f = d - this.last_mouse[1], this.dragging && this.mouseDrag(b, f)); + k || (b = f - this.last_mouse[0], k = d - this.last_mouse[1], this.dragging && this.mouseDrag(b, k)); } else { if ("mouseup" == a.type) { this.dragging = !1, document.body.removeEventListener("mousemove", this._binded_mouse_callback), document.body.removeEventListener("mouseup", this._binded_mouse_callback), b.addEventListener("mousemove", this._binded_mouse_callback); @@ -2146,7 +2172,7 @@ $jscomp.polyfill("Object.values", function(v) { } } } - this.last_mouse[0] = g; + this.last_mouse[0] = f; this.last_mouse[1] = d; a.preventDefault(); a.stopPropagation(); @@ -2190,10 +2216,10 @@ $jscomp.polyfill("Object.values", function(v) { this.offset[0] = 0; this.offset[1] = 0; }; - v.LGraphCanvas = e.LGraphCanvas = l; - l.link_type_colors = {"-1":e.EVENT_LINK_COLOR, number:"#AAA", node:"#DCA"}; - l.gradients = {}; - l.prototype.clear = function() { + w.LGraphCanvas = e.LGraphCanvas = g; + g.link_type_colors = {"-1":e.EVENT_LINK_COLOR, number:"#AAA", node:"#DCA"}; + g.gradients = {}; + g.prototype.clear = function() { this.fps = this.render_time = this.last_draw_time = this.frame = 0; this.dragging_rectangle = null; this.selected_nodes = {}; @@ -2210,10 +2236,10 @@ $jscomp.polyfill("Object.values", function(v) { this.onClear(); } }; - l.prototype.setGraph = function(a, b) { + g.prototype.setGraph = function(a, b) { this.graph != a && (b || this.clear(), !a && this.graph ? this.graph.detachCanvas(this) : (a.attachCanvas(this), this._graph_stack && (this._graph_stack = null), this.setDirty(!0, !0))); }; - l.prototype.openSubgraph = function(a) { + g.prototype.openSubgraph = function(a) { if (!a) { throw "graph cannot be null"; } @@ -2225,7 +2251,7 @@ $jscomp.polyfill("Object.values", function(v) { a.attachCanvas(this); this.setDirty(!0, !0); }; - l.prototype.closeSubgraph = function() { + g.prototype.closeSubgraph = function() { if (this._graph_stack && 0 != this._graph_stack.length) { var a = this.graph._subgraph_node, b = this._graph_stack.pop(); this.selected_nodes = {}; @@ -2235,10 +2261,10 @@ $jscomp.polyfill("Object.values", function(v) { a && (this.centerOnNode(a), this.selectNodes([a])); } }; - l.prototype.getCurrentGraph = function() { + g.prototype.getCurrentGraph = function() { return this.graph; }; - l.prototype.setCanvas = function(a, b) { + g.prototype.setCanvas = function(a, b) { if (a && a.constructor === String && (a = document.getElementById(a), !a)) { throw "Error creating LiteGraph canvas: Canvas not found"; } @@ -2260,15 +2286,15 @@ $jscomp.polyfill("Object.values", function(v) { b || this.bindEvents(); } }; - l.prototype._doNothing = function(a) { + g.prototype._doNothing = function(a) { a.preventDefault(); return !1; }; - l.prototype._doReturnTrue = function(a) { + g.prototype._doReturnTrue = function(a) { a.preventDefault(); return !0; }; - l.prototype.bindEvents = function() { + g.prototype.bindEvents = function() { if (this._events_binded) { console.warn("LGraphCanvas: events already binded"); } else { @@ -2295,7 +2321,7 @@ $jscomp.polyfill("Object.values", function(v) { this._events_binded = !0; } }; - l.prototype.unbindEvents = function() { + g.prototype.unbindEvents = function() { if (this._events_binded) { var a = this.getCanvasWindow().document; this.canvas.removeEventListener("mousedown", this._mousedown_callback); @@ -2316,31 +2342,31 @@ $jscomp.polyfill("Object.values", function(v) { console.warn("LGraphCanvas: no events binded"); } }; - l.getFileExtension = function(a) { + g.getFileExtension = function(a) { var b = a.indexOf("?"); -1 != b && (a = a.substr(0, b)); b = a.lastIndexOf("."); return -1 == b ? "" : a.substr(b + 1).toLowerCase(); }; - l.prototype.enableWebGL = function() { + g.prototype.enableWebGL = function() { this.gl = this.ctx = enableWebGLCanvas(this.canvas); this.ctx.webgl = !0; this.bgcanvas = this.canvas; this.bgctx = this.gl; this.canvas.webgl_enabled = !0; }; - l.prototype.setDirty = function(a, b) { + g.prototype.setDirty = function(a, b) { a && (this.dirty_canvas = !0); b && (this.dirty_bgcanvas = !0); }; - l.prototype.getCanvasWindow = function() { + g.prototype.getCanvasWindow = function() { if (!this.canvas) { return window; } var a = this.canvas.ownerDocument; return a.defaultView || a.parentWindow; }; - l.prototype.startRendering = function() { + g.prototype.startRendering = function() { function a() { this.pause_rendering || this.draw(); var b = this.getCanvasWindow(); @@ -2348,100 +2374,100 @@ $jscomp.polyfill("Object.values", function(v) { } this.is_rendering || (this.is_rendering = !0, a.call(this)); }; - l.prototype.stopRendering = function() { + g.prototype.stopRendering = function() { this.is_rendering = !1; }; - l.prototype.processMouseDown = function(a) { + g.prototype.processMouseDown = function(a) { if (this.graph) { this.adjustMouseEvent(a); var b = this.getCanvasWindow(); - l.active_canvas = this; + g.active_canvas = this; this.canvas.removeEventListener("mousemove", this._mousemove_callback); b.document.addEventListener("mousemove", this._mousemove_callback, !0); b.document.addEventListener("mouseup", this._mouseup_callback, !0); - var d = this.graph.getNodeOnPos(a.canvasX, a.canvasY, this.visible_nodes, 5), g = !1, f = 300 > e.getTime() - this.last_mouseclick; + var d = this.graph.getNodeOnPos(a.canvasX, a.canvasY, this.visible_nodes, 5), f = !1, k = 300 > e.getTime() - this.last_mouseclick; this.canvas_mouse[0] = a.canvasX; this.canvas_mouse[1] = a.canvasY; this.canvas.focus(); e.closeAllContextMenus(b); if (!this.onMouse || 1 != this.onMouse(a)) { if (1 == a.which) { - a.ctrlKey && (this.dragging_rectangle = new Float32Array(4), this.dragging_rectangle[0] = a.canvasX, this.dragging_rectangle[1] = a.canvasY, this.dragging_rectangle[2] = 1, this.dragging_rectangle[3] = 1, g = !0); + a.ctrlKey && (this.dragging_rectangle = new Float32Array(4), this.dragging_rectangle[0] = a.canvasX, this.dragging_rectangle[1] = a.canvasY, this.dragging_rectangle[2] = 1, this.dragging_rectangle[3] = 1, f = !0); var c = !1; - if (d && this.allow_interaction && !g && !this.read_only) { + if (d && this.allow_interaction && !f && !this.read_only) { this.live_mode || d.flags.pinned || this.bringToFront(d); if (!this.connecting_node && !d.flags.collapsed && !this.live_mode) { - if (!g && !1 !== d.resizable && y(a.canvasX, a.canvasY, d.pos[0] + d.size[0] - 5, d.pos[1] + d.size[1] - 5, 10, 10)) { - this.resizing_node = d, this.canvas.style.cursor = "se-resize", g = !0; + if (!f && !1 !== d.resizable && v(a.canvasX, a.canvasY, d.pos[0] + d.size[0] - 5, d.pos[1] + d.size[1] - 5, 10, 10)) { + this.resizing_node = d, this.canvas.style.cursor = "se-resize", f = !0; } else { if (d.outputs) { - for (var k = 0, n = d.outputs.length; k < n; ++k) { - var p = d.outputs[k], h = d.getConnectionPos(!1, k); - if (y(a.canvasX, a.canvasY, h[0] - 15, h[1] - 10, 30, 20)) { + for (var h = 0, m = d.outputs.length; h < m; ++h) { + var n = d.outputs[h], r = d.getConnectionPos(!1, h); + if (v(a.canvasX, a.canvasY, r[0] - 15, r[1] - 10, 30, 20)) { this.connecting_node = d; - this.connecting_output = p; - this.connecting_pos = d.getConnectionPos(!1, k); - this.connecting_slot = k; - a.shiftKey && d.disconnectOutput(k); - if (f) { + this.connecting_output = n; + this.connecting_pos = d.getConnectionPos(!1, h); + this.connecting_slot = h; + a.shiftKey && d.disconnectOutput(h); + if (k) { if (d.onOutputDblClick) { - d.onOutputDblClick(k, a); + d.onOutputDblClick(h, a); } } else { if (d.onOutputClick) { - d.onOutputClick(k, a); + d.onOutputClick(h, a); } } - g = !0; + f = !0; break; } } } if (d.inputs) { - for (k = 0, n = d.inputs.length; k < n; ++k) { - if (p = d.inputs[k], h = d.getConnectionPos(!0, k), y(a.canvasX, a.canvasY, h[0] - 15, h[1] - 10, 30, 20)) { - if (f) { + for (h = 0, m = d.inputs.length; h < m; ++h) { + if (n = d.inputs[h], r = d.getConnectionPos(!0, h), v(a.canvasX, a.canvasY, r[0] - 15, r[1] - 10, 30, 20)) { + if (k) { if (d.onInputDblClick) { - d.onInputDblClick(k, a); + d.onInputDblClick(h, a); } } else { if (d.onInputClick) { - d.onInputClick(k, a); + d.onInputClick(h, a); } } - if (null !== p.link) { - g = this.graph.links[p.link]; - d.disconnectInput(k); + if (null !== n.link) { + f = this.graph.links[n.link]; + d.disconnectInput(h); if (this.allow_reconnect_links || a.shiftKey) { - this.connecting_node = this.graph._nodes_by_id[g.origin_id], this.connecting_slot = g.origin_slot, this.connecting_output = this.connecting_node.outputs[this.connecting_slot], this.connecting_pos = this.connecting_node.getConnectionPos(!1, this.connecting_slot); + this.connecting_node = this.graph._nodes_by_id[f.origin_id], this.connecting_slot = f.origin_slot, this.connecting_output = this.connecting_node.outputs[this.connecting_slot], this.connecting_pos = this.connecting_node.getConnectionPos(!1, this.connecting_slot); } - g = this.dirty_bgcanvas = !0; + f = this.dirty_bgcanvas = !0; } } } } } } - if (!g) { - k = !1; - if (n = this.processNodeWidgets(d, this.canvas_mouse, a)) { - k = !0, this.node_widget = [d, n]; + if (!f) { + h = !1; + if (m = this.processNodeWidgets(d, this.canvas_mouse, a)) { + h = !0, this.node_widget = [d, m]; } - if (f && this.selected_nodes[d.id]) { + if (k && this.selected_nodes[d.id]) { if (d.onDblClick) { d.onDblClick(a, [a.canvasX - d.pos[0], a.canvasY - d.pos[1]], this); } this.processNodeDblClicked(d); - k = !0; + h = !0; } - d.onMouseDown && d.onMouseDown(a, [a.canvasX - d.pos[0], a.canvasY - d.pos[1]], this) ? k = !0 : this.live_mode && (k = c = !0); - k || (this.allow_dragnodes && (this.node_dragged = d), this.selected_nodes[d.id] || this.processNodeSelected(d, a)); + d.onMouseDown && d.onMouseDown(a, [a.canvasX - d.pos[0], a.canvasY - d.pos[1]], this) ? h = !0 : this.live_mode && (h = c = !0); + h || (this.allow_dragnodes && (this.node_dragged = d), this.selected_nodes[d.id] || this.processNodeSelected(d, a)); this.dirty_canvas = !0; } } else { if (!this.read_only) { - for (k = 0; k < this.visible_links.length; ++k) { - if (d = this.visible_links[k], c = d._pos, !(!c || a.canvasX < c[0] - 4 || a.canvasX > c[0] + 4 || a.canvasY < c[1] - 4 || a.canvasY > c[1] + 4)) { + for (h = 0; h < this.visible_links.length; ++h) { + if (d = this.visible_links[h], c = d._pos, !(!c || a.canvasX < c[0] - 4 || a.canvasX > c[0] + 4 || a.canvasY < c[1] - 4 || a.canvasY > c[1] + 4)) { this.showLinkMenu(d, a); this.over_link_center = null; break; @@ -2451,10 +2477,10 @@ $jscomp.polyfill("Object.values", function(v) { this.selected_group = this.graph.getGroupOnPos(a.canvasX, a.canvasY); this.selected_group_resizing = !1; this.selected_group && !this.read_only && (a.ctrlKey && (this.dragging_rectangle = null), 10 > A([a.canvasX, a.canvasY], [this.selected_group.pos[0] + this.selected_group.size[0], this.selected_group.pos[1] + this.selected_group.size[1]]) * this.ds.scale ? this.selected_group_resizing = !0 : this.selected_group.recomputeInsideNodes()); - f && !this.read_only && this.allow_searchbox && this.showSearchBox(a); + k && !this.read_only && this.allow_searchbox && this.showSearchBox(a); c = !0; } - !g && c && this.allow_dragcanvas && (this.dragging_canvas = !0); + !f && c && this.allow_dragcanvas && (this.dragging_canvas = !0); } else { 2 != a.which && 3 == a.which && (this.read_only || this.processContextMenu(d, a)); } @@ -2472,10 +2498,10 @@ $jscomp.polyfill("Object.values", function(v) { } } }; - l.prototype.processMouseMove = function(a) { + g.prototype.processMouseMove = function(a) { this.autoresize && this.resize(); if (this.graph) { - l.active_canvas = this; + g.active_canvas = this; this.adjustMouseEvent(a); var b = [a.localX, a.localY], d = [b[0] - this.last_mouse[0], b[1] - this.last_mouse[1]]; this.last_mouse = b; @@ -2494,10 +2520,10 @@ $jscomp.polyfill("Object.values", function(v) { } else { if (this.allow_interaction && !this.read_only) { this.connecting_node && (this.dirty_canvas = !0); - var g = this.graph.getNodeOnPos(a.canvasX, a.canvasY, this.visible_nodes); + var f = this.graph.getNodeOnPos(a.canvasX, a.canvasY, this.visible_nodes); b = 0; - for (var f = this.graph._nodes.length; b < f; ++b) { - if (this.graph._nodes[b].mouseOver && g != this.graph._nodes[b]) { + for (var k = this.graph._nodes.length; b < k; ++b) { + if (this.graph._nodes[b].mouseOver && f != this.graph._nodes[b]) { this.graph._nodes[b].mouseOver = !1; if (this.node_over && this.node_over.onMouseLeave) { this.node_over.onMouseLeave(a); @@ -2506,41 +2532,41 @@ $jscomp.polyfill("Object.values", function(v) { this.dirty_canvas = !0; } } - if (g) { - if (!g.mouseOver && (g.mouseOver = !0, this.node_over = g, this.dirty_canvas = !0, g.onMouseEnter)) { - g.onMouseEnter(a); + if (f) { + if (!f.mouseOver && (f.mouseOver = !0, this.node_over = f, this.dirty_canvas = !0, f.onMouseEnter)) { + f.onMouseEnter(a); } - if (g.onMouseMove) { - g.onMouseMove(a, [a.canvasX - g.pos[0], a.canvasY - g.pos[1]], this); + if (f.onMouseMove) { + f.onMouseMove(a, [a.canvasX - f.pos[0], a.canvasY - f.pos[1]], this); } - if (this.connecting_node && (f = this._highlight_input || [0, 0], !this.isOverNodeBox(g, a.canvasX, a.canvasY))) { - var c = this.isOverNodeInput(g, a.canvasX, a.canvasY, f); - -1 != c && g.inputs[c] ? e.isValidConnection(this.connecting_output.type, g.inputs[c].type) && (this._highlight_input = f) : this._highlight_input = null; + if (this.connecting_node && (k = this._highlight_input || [0, 0], !this.isOverNodeBox(f, a.canvasX, a.canvasY))) { + var c = this.isOverNodeInput(f, a.canvasX, a.canvasY, k); + -1 != c && f.inputs[c] ? e.isValidConnection(this.connecting_output.type, f.inputs[c].type) && (this._highlight_input = k) : this._highlight_input = null; } - this.canvas && (y(a.canvasX, a.canvasY, g.pos[0] + g.size[0] - 5, g.pos[1] + g.size[1] - 5, 5, 5) ? this.canvas.style.cursor = "se-resize" : this.canvas.style.cursor = "crosshair"); + this.canvas && (v(a.canvasX, a.canvasY, f.pos[0] + f.size[0] - 5, f.pos[1] + f.size[1] - 5, 5, 5) ? this.canvas.style.cursor = "se-resize" : this.canvas.style.cursor = "crosshair"); } else { - f = null; + k = null; for (b = 0; b < this.visible_links.length; ++b) { c = this.visible_links[b]; - var k = c._pos; - if (!(!k || a.canvasX < k[0] - 4 || a.canvasX > k[0] + 4 || a.canvasY < k[1] - 4 || a.canvasY > k[1] + 4)) { - f = c; + var h = c._pos; + if (!(!h || a.canvasX < h[0] - 4 || a.canvasX > h[0] + 4 || a.canvasY < h[1] - 4 || a.canvasY > h[1] + 4)) { + k = c; break; } } - f != this.over_link_center && (this.over_link_center = f, this.dirty_canvas = !0); + k != this.over_link_center && (this.over_link_center = k, this.dirty_canvas = !0); this.canvas && (this.canvas.style.cursor = ""); } - if (this.node_capturing_input && this.node_capturing_input != g && this.node_capturing_input.onMouseMove) { + if (this.node_capturing_input && this.node_capturing_input != f && this.node_capturing_input.onMouseMove) { this.node_capturing_input.onMouseMove(a, [a.canvasX - this.node_capturing_input.pos[0], a.canvasY - this.node_capturing_input.pos[1]], this); } if (this.node_dragged && !this.live_mode) { for (b in this.selected_nodes) { - g = this.selected_nodes[b], g.pos[0] += d[0] / this.ds.scale, g.pos[1] += d[1] / this.ds.scale; + f = this.selected_nodes[b], f.pos[0] += d[0] / this.ds.scale, f.pos[1] += d[1] / this.ds.scale; } this.dirty_bgcanvas = this.dirty_canvas = !0; } - this.resizing_node && !this.live_mode && (d = [a.canvasX - this.resizing_node.pos[0], a.canvasY - this.resizing_node.pos[1]], b = this.resizing_node.computeSize(), d[0] = Math.max(b[0], d[0]), d[1] = Math.max(b[1], d[1]), this.resizing_node.setSize(d), this.canvas.style.cursor = "se-resize", this.dirty_bgcanvas = this.dirty_canvas = !0); + this.resizing_node && !this.live_mode && (d = [a.canvasX - this.resizing_node.pos[0], a.canvasY - this.resizing_node.pos[1]], b = this.resizing_node.computeSize(d[0]), d[0] = Math.max(b[0], d[0]), d[1] = Math.max(b[1], d[1]), this.resizing_node.setSize(d), this.canvas.style.cursor = "se-resize", this.dirty_bgcanvas = this.dirty_canvas = !0); } } } @@ -2549,10 +2575,10 @@ $jscomp.polyfill("Object.values", function(v) { return !1; } }; - l.prototype.processMouseUp = function(a) { + g.prototype.processMouseUp = function(a) { if (this.graph) { var b = this.getCanvasWindow().document; - l.active_canvas = this; + g.active_canvas = this; b.removeEventListener("mousemove", this._mousemove_callback, !0); this.canvas.addEventListener("mousemove", this._mousemove_callback, !0); b.removeEventListener("mouseup", this._mouseup_callback, !0); @@ -2567,24 +2593,24 @@ $jscomp.polyfill("Object.values", function(v) { b = this.graph._nodes; var d = new Float32Array(4); this.deselectAllNodes(); - var g = Math.abs(this.dragging_rectangle[2]), f = Math.abs(this.dragging_rectangle[3]), c = 0 > this.dragging_rectangle[3] ? this.dragging_rectangle[1] - f : this.dragging_rectangle[1]; - this.dragging_rectangle[0] = 0 > this.dragging_rectangle[2] ? this.dragging_rectangle[0] - g : this.dragging_rectangle[0]; + var f = Math.abs(this.dragging_rectangle[2]), k = Math.abs(this.dragging_rectangle[3]), c = 0 > this.dragging_rectangle[3] ? this.dragging_rectangle[1] - k : this.dragging_rectangle[1]; + this.dragging_rectangle[0] = 0 > this.dragging_rectangle[2] ? this.dragging_rectangle[0] - f : this.dragging_rectangle[0]; this.dragging_rectangle[1] = c; - this.dragging_rectangle[2] = g; - this.dragging_rectangle[3] = f; - f = []; + this.dragging_rectangle[2] = f; + this.dragging_rectangle[3] = k; + k = []; for (c = 0; c < b.length; ++c) { - g = b[c], g.getBounding(d), x(this.dragging_rectangle, d) && f.push(g); + f = b[c], f.getBounding(d), G(this.dragging_rectangle, d) && k.push(f); } - f.length && this.selectNodes(f); + k.length && this.selectNodes(k); } this.dragging_rectangle = null; } else { if (this.connecting_node) { this.dirty_bgcanvas = this.dirty_canvas = !0; - if (g = this.graph.getNodeOnPos(a.canvasX, a.canvasY, this.visible_nodes)) { - this.connecting_output.type == e.EVENT && this.isOverNodeBox(g, a.canvasX, a.canvasY) ? this.connecting_node.connect(this.connecting_slot, g, e.EVENT) : (b = this.isOverNodeInput(g, a.canvasX, a.canvasY), -1 != b ? this.connecting_node.connect(this.connecting_slot, g, b) : (b = g.getInputInfo(0), this.connecting_output.type == e.EVENT ? this.connecting_node.connect(this.connecting_slot, g, e.EVENT) : b && !b.link && e.isValidConnection(b.type && this.connecting_output.type) && this.connecting_node.connect(this.connecting_slot, - g, 0))); + if (f = this.graph.getNodeOnPos(a.canvasX, a.canvasY, this.visible_nodes)) { + this.connecting_output.type == e.EVENT && this.isOverNodeBox(f, a.canvasX, a.canvasY) ? this.connecting_node.connect(this.connecting_slot, f, e.EVENT) : (b = this.isOverNodeInput(f, a.canvasX, a.canvasY), -1 != b ? this.connecting_node.connect(this.connecting_slot, f, b) : (b = f.getInputInfo(0), this.connecting_output.type == e.EVENT ? this.connecting_node.connect(this.connecting_slot, f, e.EVENT) : b && !b.link && e.isValidConnection(b.type && this.connecting_output.type) && this.connecting_node.connect(this.connecting_slot, + f, 0))); } this.connecting_node = this.connecting_pos = this.connecting_output = null; this.connecting_slot = -1; @@ -2593,7 +2619,7 @@ $jscomp.polyfill("Object.values", function(v) { this.dirty_bgcanvas = this.dirty_canvas = !0, this.resizing_node = null; } else { if (this.node_dragged) { - (g = this.node_dragged) && 300 > a.click_time && y(a.canvasX, a.canvasY, g.pos[0], g.pos[1] - e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT) && g.collapse(); + (f = this.node_dragged) && 300 > a.click_time && v(a.canvasX, a.canvasY, f.pos[0], f.pos[1] - e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT) && f.collapse(); this.dirty_bgcanvas = this.dirty_canvas = !0; this.node_dragged.pos[0] = Math.round(this.node_dragged.pos[0]); this.node_dragged.pos[1] = Math.round(this.node_dragged.pos[1]); @@ -2603,8 +2629,8 @@ $jscomp.polyfill("Object.values", function(v) { } this.node_dragged = null; } else { - g = this.graph.getNodeOnPos(a.canvasX, a.canvasY, this.visible_nodes); - !g && 300 > a.click_time && this.deselectAllNodes(); + f = this.graph.getNodeOnPos(a.canvasX, a.canvasY, this.visible_nodes); + !f && 300 > a.click_time && this.deselectAllNodes(); this.dirty_canvas = !0; this.dragging_canvas = !1; if (this.node_over && this.node_over.onMouseUp) { @@ -2626,7 +2652,7 @@ $jscomp.polyfill("Object.values", function(v) { return !1; } }; - l.prototype.processMouseWheel = function(a) { + g.prototype.processMouseWheel = function(a) { if (this.graph && this.allow_dragcanvas) { var b = null != a.wheelDeltaY ? a.wheelDeltaY : -60 * a.detail; this.adjustMouseEvent(a); @@ -2638,22 +2664,22 @@ $jscomp.polyfill("Object.values", function(v) { return !1; } }; - l.prototype.isOverNodeBox = function(a, b, d) { - var g = e.NODE_TITLE_HEIGHT; - return y(b, d, a.pos[0] + 2, a.pos[1] + 2 - g, g - 4, g - 4) ? !0 : !1; + g.prototype.isOverNodeBox = function(a, b, d) { + var f = e.NODE_TITLE_HEIGHT; + return v(b, d, a.pos[0] + 2, a.pos[1] + 2 - f, f - 4, f - 4) ? !0 : !1; }; - l.prototype.isOverNodeInput = function(a, b, d, g) { + g.prototype.isOverNodeInput = function(a, b, d, f) { if (a.inputs) { - for (var f = 0, e = a.inputs.length; f < e; ++f) { - var c = a.getConnectionPos(!0, f); - if (a.horizontal ? y(b, d, c[0] - 5, c[1] - 10, 10, 20) : y(b, d, c[0] - 10, c[1] - 5, 40, 10)) { - return g && (g[0] = c[0], g[1] = c[1]), f; + for (var k = 0, c = a.inputs.length; k < c; ++k) { + var e = a.getConnectionPos(!0, k); + if (a.horizontal ? v(b, d, e[0] - 5, e[1] - 10, 10, 20) : v(b, d, e[0] - 10, e[1] - 5, 40, 10)) { + return f && (f[0] = e[0], f[1] = e[1]), k; } } } return -1; }; - l.prototype.processKey = function(a) { + g.prototype.processKey = function(a) { if (this.graph) { var b = !1; if ("input" != a.target.localName) { @@ -2681,67 +2707,67 @@ $jscomp.polyfill("Object.values", function(v) { } } }; - l.prototype.copyToClipboard = function() { - var a = {nodes:[], links:[]}, b = 0, d = [], g; - for (g in this.selected_nodes) { - var f = this.selected_nodes[g]; - f._relative_id = b; - d.push(f); + g.prototype.copyToClipboard = function() { + var a = {nodes:[], links:[]}, b = 0, d = [], f; + for (f in this.selected_nodes) { + var k = this.selected_nodes[f]; + k._relative_id = b; + d.push(k); b += 1; } - for (g = 0; g < d.length; ++g) { - if (f = d[g], b = f.clone()) { - if (a.nodes.push(b.serialize()), f.inputs && f.inputs.length) { - for (b = 0; b < f.inputs.length; ++b) { - var e = f.inputs[b]; - if (e && null != e.link && (e = this.graph.links[e.link])) { - var c = this.graph.getNodeById(e.origin_id); - c && this.selected_nodes[c.id] && a.links.push([c._relative_id, e.origin_slot, f._relative_id, e.target_slot]); + for (f = 0; f < d.length; ++f) { + if (k = d[f], b = k.clone()) { + if (a.nodes.push(b.serialize()), k.inputs && k.inputs.length) { + for (b = 0; b < k.inputs.length; ++b) { + var c = k.inputs[b]; + if (c && null != c.link && (c = this.graph.links[c.link])) { + var e = this.graph.getNodeById(c.origin_id); + e && this.selected_nodes[e.id] && a.links.push([e._relative_id, c.origin_slot, k._relative_id, c.target_slot]); } } } } else { - console.warn("node type not found: " + f.type); + console.warn("node type not found: " + k.type); } } localStorage.setItem("litegrapheditor_clipboard", JSON.stringify(a)); }; - l.prototype.pasteFromClipboard = function() { + g.prototype.pasteFromClipboard = function() { var a = localStorage.getItem("litegrapheditor_clipboard"); if (a) { a = JSON.parse(a); for (var b = [], d = 0; d < a.nodes.length; ++d) { - var g = a.nodes[d], f = e.createNode(g.type); - f && (f.configure(g), f.pos[0] += 5, f.pos[1] += 5, this.graph.add(f), b.push(f)); + var f = a.nodes[d], k = e.createNode(f.type); + k && (k.configure(f), k.pos[0] += 5, k.pos[1] += 5, this.graph.add(k), b.push(k)); } for (d = 0; d < a.links.length; ++d) { - g = a.links[d]; - f = b[g[0]]; - var c = b[g[2]]; - f && c ? f.connect(g[1], c, g[3]) : console.warn("Warning, nodes missing on pasting"); + f = a.links[d]; + k = b[f[0]]; + var c = b[f[2]]; + k && c ? k.connect(f[1], c, f[3]) : console.warn("Warning, nodes missing on pasting"); } this.selectNodes(b); } }; - l.prototype.processDrop = function(a) { + g.prototype.processDrop = function(a) { a.preventDefault(); this.adjustMouseEvent(a); var b = [a.canvasX, a.canvasY], d = this.graph ? this.graph.getNodeOnPos(b[0], b[1]) : null; if (d) { if ((d.onDropFile || d.onDropData) && (b = a.dataTransfer.files) && b.length) { - for (var g = 0; g < b.length; g++) { - var f = a.dataTransfer.files[0], e = f.name; - l.getFileExtension(e); + for (var f = 0; f < b.length; f++) { + var k = a.dataTransfer.files[0], c = k.name; + g.getFileExtension(c); if (d.onDropFile) { - d.onDropFile(f); + d.onDropFile(k); } if (d.onDropData) { - var c = new FileReader; - c.onload = function(a) { - d.onDropData(a.target.result, e, f); + var e = new FileReader; + e.onload = function(a) { + d.onDropData(a.target.result, c, k); }; - var k = f.type.split("/")[0]; - "text" == k || "" == k ? c.readAsText(f) : "image" == k ? c.readAsDataURL(f) : c.readAsArrayBuffer(f); + var h = k.type.split("/")[0]; + "text" == h || "" == h ? e.readAsText(k) : "image" == h ? e.readAsDataURL(k) : e.readAsArrayBuffer(k); } } } @@ -2751,9 +2777,9 @@ $jscomp.polyfill("Object.values", function(v) { this.onDropItem && (b = this.onDropItem(event)); b || this.checkDropItem(a); }; - l.prototype.checkDropItem = function(a) { + g.prototype.checkDropItem = function(a) { if (a.dataTransfer.files.length) { - var b = a.dataTransfer.files[0], d = l.getFileExtension(b.name).toLowerCase(); + var b = a.dataTransfer.files[0], d = g.getFileExtension(b.name).toLowerCase(); if (d = e.node_types_by_file_extension[d]) { if (d = e.createNode(d.type), d.pos = [a.canvasX, a.canvasY], this.graph.add(d), d.onDropFile) { d.onDropFile(b); @@ -2761,7 +2787,7 @@ $jscomp.polyfill("Object.values", function(v) { } } }; - l.prototype.processNodeDblClicked = function(a) { + g.prototype.processNodeDblClicked = function(a) { if (this.onShowNodePanel) { this.onShowNodePanel(a); } @@ -2770,16 +2796,16 @@ $jscomp.polyfill("Object.values", function(v) { } this.setDirty(!0); }; - l.prototype.processNodeSelected = function(a, b) { + g.prototype.processNodeSelected = function(a, b) { this.selectNode(a, b && b.shiftKey); if (this.onNodeSelected) { this.onNodeSelected(a); } }; - l.prototype.selectNode = function(a, b) { + g.prototype.selectNode = function(a, b) { null == a ? this.deselectAllNodes() : this.selectNodes([a], b); }; - l.prototype.selectNodes = function(a, b) { + g.prototype.selectNodes = function(a, b) { b || this.deselectAllNodes(); a = a || this.graph._nodes; for (b = 0; b < a.length; ++b) { @@ -2791,16 +2817,16 @@ $jscomp.polyfill("Object.values", function(v) { d.is_selected = !0; this.selected_nodes[d.id] = d; if (d.inputs) { - for (var g = 0; g < d.inputs.length; ++g) { - this.highlighted_links[d.inputs[g].link] = !0; + for (var f = 0; f < d.inputs.length; ++f) { + this.highlighted_links[d.inputs[f].link] = !0; } } if (d.outputs) { - for (g = 0; g < d.outputs.length; ++g) { - var f = d.outputs[g]; - if (f.links) { - for (var e = 0; e < f.links.length; ++e) { - this.highlighted_links[f.links[e]] = !0; + for (f = 0; f < d.outputs.length; ++f) { + var k = d.outputs[f]; + if (k.links) { + for (var c = 0; c < k.links.length; ++c) { + this.highlighted_links[k.links[c]] = !0; } } } @@ -2812,7 +2838,7 @@ $jscomp.polyfill("Object.values", function(v) { } this.setDirty(!0); }; - l.prototype.deselectNode = function(a) { + g.prototype.deselectNode = function(a) { if (a.is_selected) { if (a.onDeselected) { a.onDeselected(); @@ -2830,25 +2856,25 @@ $jscomp.polyfill("Object.values", function(v) { for (b = 0; b < a.outputs.length; ++b) { var d = a.outputs[b]; if (d.links) { - for (var g = 0; g < d.links.length; ++g) { - delete this.highlighted_links[d.links[g]]; + for (var f = 0; f < d.links.length; ++f) { + delete this.highlighted_links[d.links[f]]; } } } } } }; - l.prototype.deselectAllNodes = function() { + g.prototype.deselectAllNodes = function() { if (this.graph) { for (var a = this.graph._nodes, b = 0, d = a.length; b < d; ++b) { - var g = a[b]; - if (g.is_selected) { - if (g.onDeselected) { - g.onDeselected(); + var f = a[b]; + if (f.is_selected) { + if (f.onDeselected) { + f.onDeselected(); } - g.is_selected = !1; + f.is_selected = !1; if (this.onNodeDeselected) { - this.onNodeDeselected(g); + this.onNodeDeselected(f); } } } @@ -2861,12 +2887,12 @@ $jscomp.polyfill("Object.values", function(v) { this.setDirty(!0); } }; - l.prototype.deleteSelectedNodes = function() { + g.prototype.deleteSelectedNodes = function() { for (var a in this.selected_nodes) { var b = this.selected_nodes[a]; if (b.inputs && b.inputs.length && b.outputs && b.outputs.length && e.isValidConnection(b.inputs[0].type, b.outputs[0].type) && b.inputs[0].link && b.outputs[0].links && b.outputs[0].links.length) { - var d = b.graph.links[b.inputs[0].link], g = b.graph.links[b.outputs[0].links[0]], f = b.getInputNode(0), c = b.getOutputNodes(0)[0]; - f && c && f.connect(d.origin_slot, c, g.target_slot); + var d = b.graph.links[b.inputs[0].link], f = b.graph.links[b.outputs[0].links[0]], k = b.getInputNode(0), c = b.getOutputNodes(0)[0]; + k && c && k.connect(d.origin_slot, c, f.target_slot); } this.graph.remove(b); if (this.onNodeDeselected) { @@ -2878,12 +2904,12 @@ $jscomp.polyfill("Object.values", function(v) { this.highlighted_links = {}; this.setDirty(!0); }; - l.prototype.centerOnNode = function(a) { + g.prototype.centerOnNode = function(a) { this.ds.offset[0] = -a.pos[0] - 0.5 * a.size[0] + 0.5 * this.canvas.width / this.ds.scale; this.ds.offset[1] = -a.pos[1] - 0.5 * a.size[1] + 0.5 * this.canvas.height / this.ds.scale; this.setDirty(!0, !0); }; - l.prototype.adjustMouseEvent = function(a) { + g.prototype.adjustMouseEvent = function(a) { if (this.canvas) { var b = this.canvas.getBoundingClientRect(); a.localX = a.clientX - b.left; @@ -2898,40 +2924,40 @@ $jscomp.polyfill("Object.values", function(v) { a.canvasX = a.localX / this.ds.scale - this.ds.offset[0]; a.canvasY = a.localY / this.ds.scale - this.ds.offset[1]; }; - l.prototype.setZoom = function(a, b) { + g.prototype.setZoom = function(a, b) { this.ds.changeScale(a, b); this.dirty_bgcanvas = this.dirty_canvas = !0; }; - l.prototype.convertOffsetToCanvas = function(a, b) { + g.prototype.convertOffsetToCanvas = function(a, b) { return this.ds.convertOffsetToCanvas(a, b); }; - l.prototype.convertCanvasToOffset = function(a, b) { + g.prototype.convertCanvasToOffset = function(a, b) { return this.ds.convertCanvasToOffset(a, b); }; - l.prototype.convertEventToCanvasOffset = function(a) { + g.prototype.convertEventToCanvasOffset = function(a) { var b = this.canvas.getBoundingClientRect(); return this.convertCanvasToOffset([a.clientX - b.left, a.clientY - b.top]); }; - l.prototype.bringToFront = function(a) { + g.prototype.bringToFront = function(a) { var b = this.graph._nodes.indexOf(a); -1 != b && (this.graph._nodes.splice(b, 1), this.graph._nodes.push(a)); }; - l.prototype.sendToBack = function(a) { + g.prototype.sendToBack = function(a) { var b = this.graph._nodes.indexOf(a); -1 != b && (this.graph._nodes.splice(b, 1), this.graph._nodes.unshift(a)); }; - var B = new Float32Array(4); - l.prototype.computeVisibleNodes = function(a, b) { + var J = new Float32Array(4); + g.prototype.computeVisibleNodes = function(a, b) { b = b || []; b.length = 0; a = a || this.graph._nodes; - for (var d = 0, g = a.length; d < g; ++d) { - var f = a[d]; - (!this.live_mode || f.onDrawBackground || f.onDrawForeground) && x(this.visible_area, f.getBounding(B)) && b.push(f); + for (var d = 0, f = a.length; d < f; ++d) { + var k = a[d]; + (!this.live_mode || k.onDrawBackground || k.onDrawForeground) && G(this.visible_area, k.getBounding(J)) && b.push(k); } return b; }; - l.prototype.draw = function(a, b) { + g.prototype.draw = function(a, b) { if (this.canvas) { var d = e.getTime(); this.render_time = 0.001 * (d - this.last_draw_time); @@ -2943,7 +2969,7 @@ $jscomp.polyfill("Object.values", function(v) { this.frame += 1; } }; - l.prototype.drawFrontCanvas = function() { + g.prototype.drawFrontCanvas = function() { this.dirty_canvas = !1; this.ctx || (this.ctx = this.bgcanvas.getContext("2d")); var a = this.ctx; @@ -2964,10 +2990,10 @@ $jscomp.polyfill("Object.values", function(v) { this.ds.toCanvasContext(a); b = this.computeVisibleNodes(null, this.visible_nodes); for (var d = 0; d < b.length; ++d) { - var g = b[d]; + var f = b[d]; a.save(); - a.translate(g.pos[0], g.pos[1]); - this.drawNode(g, a); + a.translate(f.pos[0], f.pos[1]); + this.drawNode(f, a); a.restore(); } this.render_execution_order && this.drawExecutionOrder(a); @@ -3008,7 +3034,7 @@ $jscomp.polyfill("Object.values", function(v) { a.finish2D && a.finish2D(); } }; - l.prototype.renderInfo = function(a, b, d) { + g.prototype.renderInfo = function(a, b, d) { b = b || 0; d = d || 0; a.save(); @@ -3018,7 +3044,7 @@ $jscomp.polyfill("Object.values", function(v) { this.graph ? (a.fillText("T: " + this.graph.globaltime.toFixed(2) + "s", 5, 13), a.fillText("I: " + this.graph.iteration, 5, 26), a.fillText("N: " + this.graph._nodes.length + " [" + this.visible_nodes.length + "]", 5, 39), a.fillText("V: " + this.graph._version, 5, 52), a.fillText("FPS:" + this.fps.toFixed(2), 5, 65)) : a.fillText("No graph selected", 5, 13); a.restore(); }; - l.prototype.drawBackCanvas = function() { + g.prototype.drawBackCanvas = function() { var a = this.bgcanvas; if (a.width != this.canvas.width || a.height != this.canvas.height) { a.width = this.canvas.width, a.height = this.canvas.height; @@ -3037,10 +3063,10 @@ $jscomp.polyfill("Object.values", function(v) { b.font = "40px Arial"; b.textAlign = "center"; b.fillStyle = d.bgcolor || "#AAA"; - for (var g = "", f = 1; f < this._graph_stack.length; ++f) { - g += this._graph_stack[f]._subgraph_node.getTitle() + " >> "; + for (var f = "", k = 1; k < this._graph_stack.length; ++k) { + f += this._graph_stack[k]._subgraph_node.getTitle() + " >> "; } - b.fillText(g + d.getTitle(), 0.5 * a.width, 40); + b.fillText(f + d.getTitle(), 0.5 * a.width, 40); b.restore(); } d = !1; @@ -3058,9 +3084,9 @@ $jscomp.polyfill("Object.values", function(v) { this._bg_img = new Image; this._bg_img.name = this.background_image; this._bg_img.src = this.background_image; - var e = this; + var c = this; this._bg_img.onload = function() { - e.draw(!0, !0); + c.draw(!0, !0); }; } d = null; @@ -3084,10 +3110,10 @@ $jscomp.polyfill("Object.values", function(v) { this.dirty_bgcanvas = !1; this.dirty_canvas = !0; }; - var E = new Float32Array(2); - l.prototype.drawNode = function(a, b) { + var u = new Float32Array(2); + g.prototype.drawNode = function(a, b) { this.current_node = a; - var d = a.color || a.constructor.color || e.NODE_DEFAULT_COLOR, g = a.bgcolor || a.constructor.bgcolor || e.NODE_DEFAULT_BGCOLOR, f = 0.6 > this.ds.scale; + var d = a.color || a.constructor.color || e.NODE_DEFAULT_COLOR, f = a.bgcolor || a.constructor.bgcolor || e.NODE_DEFAULT_BGCOLOR, k = 0.6 > this.ds.scale; if (this.live_mode) { if (!a.flags.collapsed && (b.shadowColor = "transparent", a.onDrawForeground)) { a.onDrawForeground(b, this, this.canvas); @@ -3095,89 +3121,83 @@ $jscomp.polyfill("Object.values", function(v) { } else { var c = this.editor_alpha; b.globalAlpha = c; - this.render_shadows && !f ? (b.shadowColor = e.DEFAULT_SHADOW_COLOR, b.shadowOffsetX = 2 * this.ds.scale, b.shadowOffsetY = 2 * this.ds.scale, b.shadowBlur = 3 * this.ds.scale) : b.shadowColor = "transparent"; + this.render_shadows && !k ? (b.shadowColor = e.DEFAULT_SHADOW_COLOR, b.shadowOffsetX = 2 * this.ds.scale, b.shadowOffsetY = 2 * this.ds.scale, b.shadowBlur = 3 * this.ds.scale) : b.shadowColor = "transparent"; if (!a.flags.collapsed || !a.onDrawCollapsed || 1 != a.onDrawCollapsed(b, this)) { - var k = a._shape || e.BOX_SHAPE; - E.set(a.size); - var n = a.horizontal; + var h = a._shape || e.BOX_SHAPE; + u.set(a.size); + var m = a.horizontal; if (a.flags.collapsed) { b.font = this.inner_text_font; - var p = a.getTitle ? a.getTitle() : a.title; - null != p && (a._collapsed_width = Math.min(a.size[0], b.measureText(p).width + 2 * e.NODE_TITLE_HEIGHT), E[0] = a._collapsed_width, E[1] = 0); + var n = a.getTitle ? a.getTitle() : a.title; + null != n && (a._collapsed_width = Math.min(a.size[0], b.measureText(n).width + 2 * e.NODE_TITLE_HEIGHT), u[0] = a._collapsed_width, u[1] = 0); } - a.clip_area && (b.save(), b.beginPath(), k == e.BOX_SHAPE ? b.rect(0, 0, E[0], E[1]) : k == e.ROUND_SHAPE ? b.roundRect(0, 0, E[0], E[1], 10) : k == e.CIRCLE_SHAPE && b.arc(0.5 * E[0], 0.5 * E[1], 0.5 * E[0], 0, 2 * Math.PI), b.clip()); - a.has_errors && (g = "red"); - this.drawNodeShape(a, b, E, d, g, a.is_selected, a.mouseOver); + a.clip_area && (b.save(), b.beginPath(), h == e.BOX_SHAPE ? b.rect(0, 0, u[0], u[1]) : h == e.ROUND_SHAPE ? b.roundRect(0, 0, u[0], u[1], 10) : h == e.CIRCLE_SHAPE && b.arc(0.5 * u[0], 0.5 * u[1], 0.5 * u[0], 0, 2 * Math.PI), b.clip()); + a.has_errors && (f = "red"); + this.drawNodeShape(a, b, u, d, f, a.is_selected, a.mouseOver); b.shadowColor = "transparent"; if (a.onDrawForeground) { a.onDrawForeground(b, this, this.canvas); } - b.textAlign = n ? "center" : "left"; + b.textAlign = m ? "center" : "left"; b.font = this.inner_text_font; - g = !f; - k = this.connecting_output; + f = !k; + n = this.connecting_output; b.lineWidth = 1; - p = 0; - var l = new Float32Array(2); + var g = 0, x = new Float32Array(2); if (!a.flags.collapsed) { if (a.inputs) { for (d = 0; d < a.inputs.length; d++) { - var h = a.inputs[d]; + var y = a.inputs[d]; b.globalAlpha = c; - this.connecting_node && !e.isValidConnection(h.type, k.type) && (b.globalAlpha = 0.4 * c); - b.fillStyle = null != h.link ? h.color_on || this.default_connection_color.input_on : h.color_off || this.default_connection_color.input_off; - var t = a.getConnectionPos(!0, d, l); - t[0] -= a.pos[0]; - t[1] -= a.pos[1]; - p < t[1] + 0.5 * e.NODE_SLOT_HEIGHT && (p = t[1] + 0.5 * e.NODE_SLOT_HEIGHT); - b.beginPath(); - h.type === e.EVENT || h.shape === e.BOX_SHAPE ? n ? b.rect(t[0] - 5 + 0.5, t[1] - 8 + 0.5, 10, 14) : b.rect(t[0] - 6 + 0.5, t[1] - 5 + 0.5, 14, 10) : h.shape === e.ARROW_SHAPE ? (b.moveTo(t[0] + 8, t[1] + 0.5), b.lineTo(t[0] - 4, t[1] + 6 + 0.5), b.lineTo(t[0] - 4, t[1] - 6 + 0.5), b.closePath()) : f ? b.rect(t[0] - 4, t[1] - 4, 8, 8) : b.arc(t[0], t[1], 4, 0, 2 * Math.PI); - b.fill(); - if (g) { - var m = null != h.label ? h.label : h.name; - m && (b.fillStyle = e.NODE_TEXT_COLOR, n || h.dir == e.UP ? b.fillText(m, t[0], t[1] - 10) : b.fillText(m, t[0] + 10, t[1] + 5)); - } + this.connecting_node && !e.isValidConnection(y.type, n.type) && (b.globalAlpha = 0.4 * c); + b.fillStyle = null != y.link ? y.color_on || this.default_connection_color.input_on : y.color_off || this.default_connection_color.input_off; + var r = a.getConnectionPos(!0, d, x); + r[0] -= a.pos[0]; + r[1] -= a.pos[1]; + g < r[1] + 0.5 * e.NODE_SLOT_HEIGHT && (g = r[1] + 0.5 * e.NODE_SLOT_HEIGHT); + h = y.shape || y.type === e.EVENT && e.BOX_SHAPE || k && e.SQUARE_SHAPE || e.CIRCLE_SHAPE; + B(b, r, h, m); + f && (h = null != y.label ? y.label : y.name) && (b.fillStyle = e.NODE_TEXT_COLOR, m || y.dir == e.UP ? b.fillText(h, r[0], r[1] - 10) : b.fillText(h, r[0] + 10, r[1] + 5)); } } this.connecting_node && (b.globalAlpha = 0.4 * c); - b.textAlign = n ? "center" : "right"; + b.textAlign = m ? "center" : "right"; b.strokeStyle = "black"; if (a.outputs) { for (d = 0; d < a.outputs.length; d++) { - if (h = a.outputs[d], t = a.getConnectionPos(!1, d, l), t[0] -= a.pos[0], t[1] -= a.pos[1], p < t[1] + 0.5 * e.NODE_SLOT_HEIGHT && (p = t[1] + 0.5 * e.NODE_SLOT_HEIGHT), b.fillStyle = h.links && h.links.length ? h.color_on || this.default_connection_color.output_on : h.color_off || this.default_connection_color.output_off, b.beginPath(), h.type === e.EVENT || h.shape === e.BOX_SHAPE ? n ? b.rect(t[0] - 5 + 0.5, t[1] - 8 + 0.5, 10, 14) : b.rect(t[0] - 6 + 0.5, t[1] - 5 + 0.5, 14, 10) : - h.shape === e.ARROW_SHAPE ? (b.moveTo(t[0] + 8, t[1] + 0.5), b.lineTo(t[0] - 4, t[1] + 6 + 0.5), b.lineTo(t[0] - 4, t[1] - 6 + 0.5), b.closePath()) : f ? b.rect(t[0] - 4, t[1] - 4, 8, 8) : b.arc(t[0], t[1], 4, 0, 2 * Math.PI), b.fill(), f || b.stroke(), g && (m = null != h.label ? h.label : h.name)) { - b.fillStyle = e.NODE_TEXT_COLOR, n || h.dir == e.DOWN ? b.fillText(m, t[0], t[1] - 8) : b.fillText(m, t[0] - 10, t[1] + 5); + if (y = a.outputs[d], r = a.getConnectionPos(!1, d, x), r[0] -= a.pos[0], r[1] -= a.pos[1], g < r[1] + 0.5 * e.NODE_SLOT_HEIGHT && (g = r[1] + 0.5 * e.NODE_SLOT_HEIGHT), b.fillStyle = y.links && y.links.length ? y.color_on || this.default_connection_color.output_on : y.color_off || this.default_connection_color.output_off, h = y.shape || y.type === e.EVENT && e.BOX_SHAPE || k && e.SQUARE_SHAPE || e.CIRCLE_SHAPE, B(b, r, h, m), k || b.stroke(), f && (h = null != y.label ? y.label : y.name)) { + b.fillStyle = e.NODE_TEXT_COLOR, m || y.dir == e.DOWN ? b.fillText(h, r[0], r[1] - 8) : b.fillText(h, r[0] - 10, r[1] + 5); } } } b.textAlign = "left"; b.globalAlpha = 1; if (a.widgets) { - h = p; - if (n || a.widgets_up) { - h = 2; + y = g; + if (m || a.widgets_up) { + y = 2; } - null != a.widgets_start_y && (h = a.widgets_start_y); - this.drawNodeWidgets(a, h, b, this.node_widget && this.node_widget[0] == a ? this.node_widget[1] : null); + null != a.widgets_start_y && (y = a.widgets_start_y); + this.drawNodeWidgets(a, y, b, this.node_widget && this.node_widget[0] == a ? this.node_widget[1] : null); } } else { if (this.render_collapsed_slots) { - f = c = null; + k = c = null; if (a.inputs) { for (d = 0; d < a.inputs.length; d++) { - if (h = a.inputs[d], null != h.link) { - c = h; + if (y = a.inputs[d], null != y.link) { + c = y; break; } } } if (a.outputs) { for (d = 0; d < a.outputs.length; d++) { - h = a.outputs[d], h.links && h.links.length && (f = h); + y = a.outputs[d], y.links && y.links.length && (k = y); } } - c && (c = 0, d = -0.5 * e.NODE_TITLE_HEIGHT, n && (c = 0.5 * a._collapsed_width, d = -e.NODE_TITLE_HEIGHT), b.fillStyle = "#686", b.beginPath(), h.type === e.EVENT || h.shape === e.BOX_SHAPE ? b.rect(c - 7 + 0.5, d - 4, 14, 8) : h.shape === e.ARROW_SHAPE ? (b.moveTo(c + 8, d), b.lineTo(c + -4, d - 4), b.lineTo(c + -4, d + 4), b.closePath()) : b.arc(c, d, 4, 0, 2 * Math.PI), b.fill()); - f && (c = a._collapsed_width, d = -0.5 * e.NODE_TITLE_HEIGHT, n && (c = 0.5 * a._collapsed_width, d = 0), b.fillStyle = "#686", b.strokeStyle = "black", b.beginPath(), h.type === e.EVENT || h.shape === e.BOX_SHAPE ? b.rect(c - 7 + 0.5, d - 4, 14, 8) : h.shape === e.ARROW_SHAPE ? (b.moveTo(c + 6, d), b.lineTo(c - 6, d - 4), b.lineTo(c - 6, d + 4), b.closePath()) : b.arc(c, d, 4, 0, 2 * Math.PI), b.fill()); + c && (c = 0, h = -0.5 * e.NODE_TITLE_HEIGHT, m && (c = 0.5 * a._collapsed_width, h = -e.NODE_TITLE_HEIGHT), b.fillStyle = "#686", b.beginPath(), y.type === e.EVENT || y.shape === e.BOX_SHAPE ? b.rect(c - 7 + 0.5, h - 4, 14, 8) : y.shape === e.ARROW_SHAPE ? (b.moveTo(c + 8, h), b.lineTo(c + -4, h - 4), b.lineTo(c + -4, h + 4), b.closePath()) : b.arc(c, h, 4, 0, 2 * Math.PI), b.fill()); + k && (c = a._collapsed_width, h = -0.5 * e.NODE_TITLE_HEIGHT, m && (c = 0.5 * a._collapsed_width, h = 0), b.fillStyle = "#686", b.strokeStyle = "black", b.beginPath(), y.type === e.EVENT || y.shape === e.BOX_SHAPE ? b.rect(c - 7 + 0.5, h - 4, 14, 8) : y.shape === e.ARROW_SHAPE ? (b.moveTo(c + 6, h), b.lineTo(c - 6, h - 4), b.lineTo(c - 6, h + 4), b.closePath()) : b.arc(c, h, 4, 0, 2 * Math.PI), b.fill()); } } a.clip_area && b.restore(); @@ -3185,7 +3205,7 @@ $jscomp.polyfill("Object.values", function(v) { } } }; - l.prototype.drawLinkTooltip = function(a, b) { + g.prototype.drawLinkTooltip = function(a, b) { var d = b._pos; a.fillStyle = "black"; a.beginPath(); @@ -3194,14 +3214,14 @@ $jscomp.polyfill("Object.values", function(v) { if (null != b.data && (!this.onDrawLinkTooltip || 1 != this.onDrawLinkTooltip(a, b, this)) && (b = b.data, b = b.constructor === Number ? b.toFixed(2) : b.constructor === String ? '"' + b + '"' : b.constructor === Boolean ? String(b) : b.toToolTip ? b.toToolTip() : "[" + b.constructor.name + "]", null != b)) { b = b.substr(0, 30); a.font = "14px Courier New"; - var g = a.measureText(b).width + 20; + var f = a.measureText(b).width + 20; a.shadowColor = "black"; a.shadowOffsetX = 2; a.shadowOffsetY = 2; a.shadowBlur = 3; a.fillStyle = "#454"; a.beginPath(); - a.roundRect(d[0] - 0.5 * g, d[1] - 15 - 24, g, 24, 3, 3); + a.roundRect(d[0] - 0.5 * f, d[1] - 15 - 24, f, 24, 3, 3); a.moveTo(d[0] - 10, d[1] - 15); a.lineTo(d[0] + 10, d[1] - 15); a.lineTo(d[0], d[1] - 5); @@ -3212,114 +3232,114 @@ $jscomp.polyfill("Object.values", function(v) { a.fillText(b, d[0], d[1] - 15 - 24 * 0.3); } }; - var u = new Float32Array(4); - l.prototype.drawNodeShape = function(a, b, d, g, f, c, k) { - b.strokeStyle = g; - b.fillStyle = f; - f = e.NODE_TITLE_HEIGHT; - var n = 0.5 > this.ds.scale, p = a._shape || a.constructor.shape || e.ROUND_SHAPE, w = a.constructor.title_mode, C = !0; - w == e.TRANSPARENT_TITLE ? C = !1 : w == e.AUTOHIDE_TITLE && k && (C = !0); - u[0] = 0; - u[1] = C ? -f : 0; - u[2] = d[0] + 1; - u[3] = C ? d[1] + f : d[1]; - k = b.globalAlpha; + var C = new Float32Array(4); + g.prototype.drawNodeShape = function(a, b, d, f, k, c, h) { + b.strokeStyle = f; + b.fillStyle = k; + k = e.NODE_TITLE_HEIGHT; + var m = 0.5 > this.ds.scale, n = a._shape || a.constructor.shape || e.ROUND_SHAPE, r = a.constructor.title_mode, x = !0; + r == e.TRANSPARENT_TITLE ? x = !1 : r == e.AUTOHIDE_TITLE && h && (x = !0); + C[0] = 0; + C[1] = x ? -k : 0; + C[2] = d[0] + 1; + C[3] = x ? d[1] + k : d[1]; + h = b.globalAlpha; b.beginPath(); - p == e.BOX_SHAPE || n ? b.fillRect(u[0], u[1], u[2], u[3]) : p == e.ROUND_SHAPE || p == e.CARD_SHAPE ? b.roundRect(u[0], u[1], u[2], u[3], this.round_radius, p == e.CARD_SHAPE ? 0 : this.round_radius) : p == e.CIRCLE_SHAPE && b.arc(0.5 * d[0], 0.5 * d[1], 0.5 * d[0], 0, 2 * Math.PI); + n == e.BOX_SHAPE || m ? b.fillRect(C[0], C[1], C[2], C[3]) : n == e.ROUND_SHAPE || n == e.CARD_SHAPE ? b.roundRect(C[0], C[1], C[2], C[3], this.round_radius, n == e.CARD_SHAPE ? 0 : this.round_radius) : n == e.CIRCLE_SHAPE && b.arc(0.5 * d[0], 0.5 * d[1], 0.5 * d[0], 0, 2 * Math.PI); b.fill(); - a.flags.collapsed || (b.shadowColor = "transparent", b.fillStyle = "rgba(0,0,0,0.2)", b.fillRect(0, -1, u[2], 2)); + a.flags.collapsed || (b.shadowColor = "transparent", b.fillStyle = "rgba(0,0,0,0.2)", b.fillRect(0, -1, C[2], 2)); b.shadowColor = "transparent"; if (a.onDrawBackground) { a.onDrawBackground(b, this, this.canvas); } - if (C || w == e.TRANSPARENT_TITLE) { + if (x || r == e.TRANSPARENT_TITLE) { if (a.onDrawTitleBar) { - a.onDrawTitleBar(b, f, d, this.ds.scale, g); + a.onDrawTitleBar(b, k, d, this.ds.scale, f); } else { - if (w != e.TRANSPARENT_TITLE && (a.constructor.title_color || this.render_title_colored)) { - C = a.constructor.title_color || g; + if (r != e.TRANSPARENT_TITLE && (a.constructor.title_color || this.render_title_colored)) { + x = a.constructor.title_color || f; a.flags.collapsed && (b.shadowColor = e.DEFAULT_SHADOW_COLOR); if (this.use_gradients) { - var t = l.gradients[C]; - t || (t = l.gradients[C] = b.createLinearGradient(0, 0, 400, 0), t.addColorStop(0, C), t.addColorStop(1, "#000")); - b.fillStyle = t; + var y = g.gradients[x]; + y || (y = g.gradients[x] = b.createLinearGradient(0, 0, 400, 0), y.addColorStop(0, x), y.addColorStop(1, "#000")); + b.fillStyle = y; } else { - b.fillStyle = C; + b.fillStyle = x; } b.beginPath(); - p == e.BOX_SHAPE || n ? b.rect(0, -f, d[0] + 1, f) : (p == e.ROUND_SHAPE || p == e.CARD_SHAPE) && b.roundRect(0, -f, d[0] + 1, f, this.round_radius, a.flags.collapsed ? this.round_radius : 0); + n == e.BOX_SHAPE || m ? b.rect(0, -k, d[0] + 1, k) : (n == e.ROUND_SHAPE || n == e.CARD_SHAPE) && b.roundRect(0, -k, d[0] + 1, k, this.round_radius, a.flags.collapsed ? this.round_radius : 0); b.fill(); b.shadowColor = "transparent"; } } if (a.onDrawTitleBox) { - a.onDrawTitleBox(b, f, d, this.ds.scale); + a.onDrawTitleBox(b, k, d, this.ds.scale); } else { - p == e.ROUND_SHAPE || p == e.CIRCLE_SHAPE || p == e.CARD_SHAPE ? (n && (b.fillStyle = "black", b.beginPath(), b.arc(0.5 * f, -0.5 * f, 6, 0, 2 * Math.PI), b.fill()), b.fillStyle = a.boxcolor || e.NODE_DEFAULT_BOXCOLOR, n ? b.fillRect(0.5 * f - 5, -0.5 * f - 5, 10, 10) : (b.beginPath(), b.arc(0.5 * f, -0.5 * f, 5, 0, 2 * Math.PI), b.fill())) : (n && (b.fillStyle = "black", b.fillRect(0.5 * (f - 10) - 1, -0.5 * (f + 10) - 1, 12, 12)), b.fillStyle = a.boxcolor || e.NODE_DEFAULT_BOXCOLOR, b.fillRect(0.5 * - (f - 10), -0.5 * (f + 10), 10, 10)); + n == e.ROUND_SHAPE || n == e.CIRCLE_SHAPE || n == e.CARD_SHAPE ? (m && (b.fillStyle = "black", b.beginPath(), b.arc(0.5 * k, -0.5 * k, 6, 0, 2 * Math.PI), b.fill()), b.fillStyle = a.boxcolor || e.NODE_DEFAULT_BOXCOLOR, m ? b.fillRect(0.5 * k - 5, -0.5 * k - 5, 10, 10) : (b.beginPath(), b.arc(0.5 * k, -0.5 * k, 5, 0, 2 * Math.PI), b.fill())) : (m && (b.fillStyle = "black", b.fillRect(0.5 * (k - 10) - 1, -0.5 * (k + 10) - 1, 12, 12)), b.fillStyle = a.boxcolor || e.NODE_DEFAULT_BOXCOLOR, b.fillRect(0.5 * + (k - 10), -0.5 * (k + 10), 10, 10)); } - b.globalAlpha = k; + b.globalAlpha = h; if (a.onDrawTitleText) { - a.onDrawTitleText(b, f, d, this.ds.scale, this.title_text_font, c); + a.onDrawTitleText(b, k, d, this.ds.scale, this.title_text_font, c); } - !n && (b.font = this.title_text_font, n = String(a.getTitle())) && (b.fillStyle = c ? "white" : a.constructor.title_text_color || this.node_title_color, a.flags.collapsed ? (b.textAlign = "left", b.measureText(n), b.fillText(n.substr(0, 20), f, e.NODE_TITLE_TEXT_Y - f), b.textAlign = "left") : (b.textAlign = "left", b.fillText(n, f, e.NODE_TITLE_TEXT_Y - f))); + !m && (b.font = this.title_text_font, m = String(a.getTitle())) && (b.fillStyle = c ? "white" : a.constructor.title_text_color || this.node_title_color, a.flags.collapsed ? (b.textAlign = "left", b.measureText(m), b.fillText(m.substr(0, 20), k, e.NODE_TITLE_TEXT_Y - k), b.textAlign = "left") : (b.textAlign = "left", b.fillText(m, k, e.NODE_TITLE_TEXT_Y - k))); if (a.onDrawTitle) { a.onDrawTitle(b); } } if (c) { if (a.onBounding) { - a.onBounding(u); + a.onBounding(C); } - w == e.TRANSPARENT_TITLE && (u[1] -= f, u[3] += f); + r == e.TRANSPARENT_TITLE && (C[1] -= k, C[3] += k); b.lineWidth = 1; b.globalAlpha = 0.8; b.beginPath(); - p == e.BOX_SHAPE ? b.rect(-6 + u[0], -6 + u[1], 12 + u[2], 12 + u[3]) : p == e.ROUND_SHAPE || p == e.CARD_SHAPE && a.flags.collapsed ? b.roundRect(-6 + u[0], -6 + u[1], 12 + u[2], 12 + u[3], 2 * this.round_radius) : p == e.CARD_SHAPE ? b.roundRect(-6 + u[0], -6 + u[1], 12 + u[2], 12 + u[3], 2 * this.round_radius, 2) : p == e.CIRCLE_SHAPE && b.arc(0.5 * d[0], 0.5 * d[1], 0.5 * d[0] + 6, 0, 2 * Math.PI); + n == e.BOX_SHAPE ? b.rect(-6 + C[0], -6 + C[1], 12 + C[2], 12 + C[3]) : n == e.ROUND_SHAPE || n == e.CARD_SHAPE && a.flags.collapsed ? b.roundRect(-6 + C[0], -6 + C[1], 12 + C[2], 12 + C[3], 2 * this.round_radius) : n == e.CARD_SHAPE ? b.roundRect(-6 + C[0], -6 + C[1], 12 + C[2], 12 + C[3], 2 * this.round_radius, 2) : n == e.CIRCLE_SHAPE && b.arc(0.5 * d[0], 0.5 * d[1], 0.5 * d[0] + 6, 0, 2 * Math.PI); b.strokeStyle = "#FFF"; b.stroke(); - b.strokeStyle = g; + b.strokeStyle = f; b.globalAlpha = 1; } }; - var H = new Float32Array(4), n = new Float32Array(4), p = new Float32Array(2), k = new Float32Array(2); - l.prototype.drawConnections = function(a) { + var m = new Float32Array(4), n = new Float32Array(4), h = new Float32Array(2), E = new Float32Array(2); + g.prototype.drawConnections = function(a) { var b = e.getTime(), d = this.visible_area; - H[0] = d[0] - 20; - H[1] = d[1] - 20; - H[2] = d[2] + 40; - H[3] = d[3] + 40; + m[0] = d[0] - 20; + m[1] = d[1] - 20; + m[2] = d[2] + 40; + m[3] = d[3] + 40; a.lineWidth = this.connections_width; a.fillStyle = "#AAA"; a.strokeStyle = "#AAA"; a.globalAlpha = this.editor_alpha; d = this.graph._nodes; - for (var g = 0, f = d.length; g < f; ++g) { - var c = d[g]; + for (var f = 0, k = d.length; f < k; ++f) { + var c = d[f]; if (c.inputs && c.inputs.length) { - for (var w = 0; w < c.inputs.length; ++w) { - var l = c.inputs[w]; - if (l && null != l.link && (l = this.graph.links[l.link])) { - var h = this.graph.getNodeById(l.origin_id); - if (null != h) { - var m = l.origin_slot; - var q = -1 == m ? [h.pos[0] + 10, h.pos[1] + 10] : h.getConnectionPos(!1, m, p); - var t = c.getConnectionPos(!0, w, k); - n[0] = q[0]; - n[1] = q[1]; - n[2] = t[0] - q[0]; - n[3] = t[1] - q[1]; + for (var g = 0; g < c.inputs.length; ++g) { + var r = c.inputs[g]; + if (r && null != r.link && (r = this.graph.links[r.link])) { + var p = this.graph.getNodeById(r.origin_id); + if (null != p) { + var l = r.origin_slot; + var x = -1 == l ? [p.pos[0] + 10, p.pos[1] + 10] : p.getConnectionPos(!1, l, h); + var y = c.getConnectionPos(!0, g, E); + n[0] = x[0]; + n[1] = x[1]; + n[2] = y[0] - x[0]; + n[3] = y[1] - x[1]; 0 > n[2] && (n[0] += n[2], n[2] = Math.abs(n[2])); 0 > n[3] && (n[1] += n[3], n[3] = Math.abs(n[3])); - if (x(n, H)) { - var J = h.outputs[m]; - m = c.inputs[w]; - if (J && m && (h = J.dir || (h.horizontal ? e.DOWN : e.RIGHT), m = m.dir || (c.horizontal ? e.UP : e.LEFT), this.renderLink(a, q, t, l, !1, 0, null, h, m), l && l._last_time && 1000 > b - l._last_time)) { - J = 2.0 - 0.002 * (b - l._last_time); - var N = a.globalAlpha; - a.globalAlpha = N * J; - this.renderLink(a, q, t, l, !0, J, "white", h, m); - a.globalAlpha = N; + if (G(n, m)) { + var q = p.outputs[l]; + l = c.inputs[g]; + if (q && l && (p = q.dir || (p.horizontal ? e.DOWN : e.RIGHT), l = l.dir || (c.horizontal ? e.UP : e.LEFT), this.renderLink(a, x, y, r, !1, 0, null, p, l), r && r._last_time && 1000 > b - r._last_time)) { + q = 2.0 - 0.002 * (b - r._last_time); + var M = a.globalAlpha; + a.globalAlpha = M * q; + this.renderLink(a, x, y, r, !0, q, "white", p, l); + a.globalAlpha = M; } } } @@ -3329,69 +3349,69 @@ $jscomp.polyfill("Object.values", function(v) { } a.globalAlpha = 1; }; - l.prototype.renderLink = function(a, b, d, g, f, c, k, n, p, h) { - g && this.visible_links.push(g); - !k && g && (k = g.color || l.link_type_colors[g.type]); - k || (k = this.default_link_color); - null != g && this.highlighted_links[g.id] && (k = "#FFF"); - n = n || e.RIGHT; - p = p || e.LEFT; - var w = A(b, d); + g.prototype.renderLink = function(a, b, d, f, k, c, h, m, n, r) { + f && this.visible_links.push(f); + !h && f && (h = f.color || g.link_type_colors[f.type]); + h || (h = this.default_link_color); + null != f && this.highlighted_links[f.id] && (h = "#FFF"); + m = m || e.RIGHT; + n = n || e.LEFT; + var x = A(b, d); this.render_connections_border && 0.6 < this.ds.scale && (a.lineWidth = this.connections_width + 4); a.lineJoin = "round"; - h = h || 1; - 1 < h && (a.lineWidth = 0.5); + r = r || 1; + 1 < r && (a.lineWidth = 0.5); a.beginPath(); - for (var t = 0; t < h; t += 1) { - var C = 5 * (t - 0.5 * (h - 1)); + for (var y = 0; y < r; y += 1) { + var p = 5 * (y - 0.5 * (r - 1)); if (this.links_render_mode == e.SPLINE_LINK) { - a.moveTo(b[0], b[1] + C); - var m = 0, q = 0, I = 0, r = 0; + a.moveTo(b[0], b[1] + p); + var F = 0, l = 0, I = 0, q = 0; + switch(m) { + case e.LEFT: + F = -0.25 * x; + break; + case e.RIGHT: + F = 0.25 * x; + break; + case e.UP: + l = -0.25 * x; + break; + case e.DOWN: + l = 0.25 * x; + } switch(n) { case e.LEFT: - m = -0.25 * w; + I = -0.25 * x; break; case e.RIGHT: - m = 0.25 * w; + I = 0.25 * x; break; case e.UP: - q = -0.25 * w; + q = -0.25 * x; break; case e.DOWN: - q = 0.25 * w; + q = 0.25 * x; } - switch(p) { - case e.LEFT: - I = -0.25 * w; - break; - case e.RIGHT: - I = 0.25 * w; - break; - case e.UP: - r = -0.25 * w; - break; - case e.DOWN: - r = 0.25 * w; - } - a.bezierCurveTo(b[0] + m, b[1] + q + C, d[0] + I, d[1] + r + C, d[0], d[1] + C); + a.bezierCurveTo(b[0] + F, b[1] + l + p, d[0] + I, d[1] + q + p, d[0], d[1] + p); } else { if (this.links_render_mode == e.LINEAR_LINK) { - a.moveTo(b[0], b[1] + C); - r = I = q = m = 0; - switch(n) { + a.moveTo(b[0], b[1] + p); + q = I = l = F = 0; + switch(m) { case e.LEFT: - m = -1; + F = -1; break; case e.RIGHT: - m = 1; + F = 1; break; case e.UP: - q = -1; + l = -1; break; case e.DOWN: - q = 1; + l = 1; } - switch(p) { + switch(n) { case e.LEFT: I = -1; break; @@ -3399,320 +3419,320 @@ $jscomp.polyfill("Object.values", function(v) { I = 1; break; case e.UP: - r = -1; + q = -1; break; case e.DOWN: - r = 1; + q = 1; } - a.lineTo(b[0] + 15 * m, b[1] + 15 * q + C); - a.lineTo(d[0] + 15 * I, d[1] + 15 * r + C); - a.lineTo(d[0], d[1] + C); + a.lineTo(b[0] + 15 * F, b[1] + 15 * l + p); + a.lineTo(d[0] + 15 * I, d[1] + 15 * q + p); + a.lineTo(d[0], d[1] + p); } else { if (this.links_render_mode == e.STRAIGHT_LINK) { - a.moveTo(b[0], b[1]), C = b[0], m = b[1], q = d[0], I = d[1], n == e.RIGHT ? C += 10 : m += 10, p == e.LEFT ? q -= 10 : I -= 10, a.lineTo(C, m), a.lineTo(0.5 * (C + q), m), a.lineTo(0.5 * (C + q), I), a.lineTo(q, I), a.lineTo(d[0], d[1]); + a.moveTo(b[0], b[1]), p = b[0], F = b[1], l = d[0], I = d[1], m == e.RIGHT ? p += 10 : F += 10, n == e.LEFT ? l -= 10 : I -= 10, a.lineTo(p, F), a.lineTo(0.5 * (p + l), F), a.lineTo(0.5 * (p + l), I), a.lineTo(l, I), a.lineTo(d[0], d[1]); } else { return; } } } } - this.render_connections_border && 0.6 < this.ds.scale && !f && (a.strokeStyle = "rgba(0,0,0,0.5)", a.stroke()); + this.render_connections_border && 0.6 < this.ds.scale && !k && (a.strokeStyle = "rgba(0,0,0,0.5)", a.stroke()); a.lineWidth = this.connections_width; - a.fillStyle = a.strokeStyle = k; + a.fillStyle = a.strokeStyle = h; a.stroke(); - f = this.computeConnectionPoint(b, d, 0.5, n, p); - g && g._pos && (g._pos[0] = f[0], g._pos[1] = f[1]); - 0.6 <= this.ds.scale && this.highquality_render && p != e.CENTER && (this.render_connection_arrows && (t = this.computeConnectionPoint(b, d, 0.25, n, p), w = this.computeConnectionPoint(b, d, 0.26, n, p), g = this.computeConnectionPoint(b, d, 0.75, n, p), h = this.computeConnectionPoint(b, d, 0.76, n, p), this.render_curved_connections ? (w = -Math.atan2(w[0] - t[0], w[1] - t[1]), h = -Math.atan2(h[0] - g[0], h[1] - g[1])) : h = w = d[1] > b[1] ? 0 : Math.PI, a.save(), a.translate(t[0], t[1]), - a.rotate(w), a.beginPath(), a.moveTo(-5, -3), a.lineTo(0, 7), a.lineTo(5, -3), a.fill(), a.restore(), a.save(), a.translate(g[0], g[1]), a.rotate(h), a.beginPath(), a.moveTo(-5, -3), a.lineTo(0, 7), a.lineTo(5, -3), a.fill(), a.restore()), a.beginPath(), a.arc(f[0], f[1], 5, 0, 2 * Math.PI), a.fill()); + k = this.computeConnectionPoint(b, d, 0.5, m, n); + f && f._pos && (f._pos[0] = k[0], f._pos[1] = k[1]); + 0.6 <= this.ds.scale && this.highquality_render && n != e.CENTER && (this.render_connection_arrows && (y = this.computeConnectionPoint(b, d, 0.25, m, n), x = this.computeConnectionPoint(b, d, 0.26, m, n), f = this.computeConnectionPoint(b, d, 0.75, m, n), r = this.computeConnectionPoint(b, d, 0.76, m, n), this.render_curved_connections ? (x = -Math.atan2(x[0] - y[0], x[1] - y[1]), r = -Math.atan2(r[0] - f[0], r[1] - f[1])) : r = x = d[1] > b[1] ? 0 : Math.PI, a.save(), a.translate(y[0], y[1]), + a.rotate(x), a.beginPath(), a.moveTo(-5, -3), a.lineTo(0, 7), a.lineTo(5, -3), a.fill(), a.restore(), a.save(), a.translate(f[0], f[1]), a.rotate(r), a.beginPath(), a.moveTo(-5, -3), a.lineTo(0, 7), a.lineTo(5, -3), a.fill(), a.restore()), a.beginPath(), a.arc(k[0], k[1], 5, 0, 2 * Math.PI), a.fill()); if (c) { - for (a.fillStyle = k, t = 0; 5 > t; ++t) { - c = (0.001 * e.getTime() + 0.2 * t) % 1, f = this.computeConnectionPoint(b, d, c, n, p), a.beginPath(), a.arc(f[0], f[1], 5, 0, 2 * Math.PI), a.fill(); + for (a.fillStyle = h, y = 0; 5 > y; ++y) { + c = (0.001 * e.getTime() + 0.2 * y) % 1, k = this.computeConnectionPoint(b, d, c, m, n), a.beginPath(), a.arc(k[0], k[1], 5, 0, 2 * Math.PI), a.fill(); } } }; - l.prototype.computeConnectionPoint = function(a, b, d, g, f) { - g = g || e.RIGHT; - f = f || e.LEFT; - var c = A(a, b), k = [a[0], a[1]], n = [b[0], b[1]]; - switch(g) { - case e.LEFT: - k[0] += -0.25 * c; - break; - case e.RIGHT: - k[0] += 0.25 * c; - break; - case e.UP: - k[1] += -0.25 * c; - break; - case e.DOWN: - k[1] += 0.25 * c; - } + g.prototype.computeConnectionPoint = function(a, b, d, f, k) { + f = f || e.RIGHT; + k = k || e.LEFT; + var c = A(a, b), h = [a[0], a[1]], m = [b[0], b[1]]; switch(f) { case e.LEFT: - n[0] += -0.25 * c; + h[0] += -0.25 * c; break; case e.RIGHT: - n[0] += 0.25 * c; + h[0] += 0.25 * c; break; case e.UP: - n[1] += -0.25 * c; + h[1] += -0.25 * c; break; case e.DOWN: - n[1] += 0.25 * c; + h[1] += 0.25 * c; } - g = (1 - d) * (1 - d) * (1 - d); - f = 3 * (1 - d) * (1 - d) * d; + switch(k) { + case e.LEFT: + m[0] += -0.25 * c; + break; + case e.RIGHT: + m[0] += 0.25 * c; + break; + case e.UP: + m[1] += -0.25 * c; + break; + case e.DOWN: + m[1] += 0.25 * c; + } + f = (1 - d) * (1 - d) * (1 - d); + k = 3 * (1 - d) * (1 - d) * d; c = 3 * (1 - d) * d * d; d *= d * d; - return [g * a[0] + f * k[0] + c * n[0] + d * b[0], g * a[1] + f * k[1] + c * n[1] + d * b[1]]; + return [f * a[0] + k * h[0] + c * m[0] + d * b[0], f * a[1] + k * h[1] + c * m[1] + d * b[1]]; }; - l.prototype.drawExecutionOrder = function(a) { + g.prototype.drawExecutionOrder = function(a) { a.shadowColor = "transparent"; a.globalAlpha = 0.25; a.textAlign = "center"; a.strokeStyle = "white"; a.globalAlpha = 0.75; for (var b = this.visible_nodes, d = 0; d < b.length; ++d) { - var g = b[d]; + var f = b[d]; a.fillStyle = "black"; - a.fillRect(g.pos[0] - e.NODE_TITLE_HEIGHT, g.pos[1] - e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT); - 0 == g.order && a.strokeRect(g.pos[0] - e.NODE_TITLE_HEIGHT + 0.5, g.pos[1] - e.NODE_TITLE_HEIGHT + 0.5, e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT); + a.fillRect(f.pos[0] - e.NODE_TITLE_HEIGHT, f.pos[1] - e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT); + 0 == f.order && a.strokeRect(f.pos[0] - e.NODE_TITLE_HEIGHT + 0.5, f.pos[1] - e.NODE_TITLE_HEIGHT + 0.5, e.NODE_TITLE_HEIGHT, e.NODE_TITLE_HEIGHT); a.fillStyle = "#FFF"; - a.fillText(g.order, g.pos[0] + -0.5 * e.NODE_TITLE_HEIGHT, g.pos[1] - 6); + a.fillText(f.order, f.pos[0] + -0.5 * e.NODE_TITLE_HEIGHT, f.pos[1] - 6); } a.globalAlpha = 1; }; - l.prototype.drawNodeWidgets = function(a, b, d, g) { + g.prototype.drawNodeWidgets = function(a, b, d, f) { if (!a.widgets || !a.widgets.length) { return 0; } - var f = a.size[0], c = a.widgets; + var k = a.size[0], c = a.widgets; b += 2; - var k = e.NODE_WIDGET_HEIGHT, n = 0.5 < this.ds.scale; + var h = e.NODE_WIDGET_HEIGHT, m = 0.5 < this.ds.scale; d.save(); d.globalAlpha = this.editor_alpha; - for (var p = e.WIDGET_OUTLINE_COLOR, l = e.WIDGET_BGCOLOR, h = e.WIDGET_TEXT_COLOR, t = e.WIDGET_SECONDARY_TEXT_COLOR, m = 0; m < c.length; ++m) { - var q = c[m], r = b; - q.y && (r = q.y); - q.last_y = r; - d.strokeStyle = p; + for (var n = e.WIDGET_OUTLINE_COLOR, r = e.WIDGET_BGCOLOR, x = e.WIDGET_TEXT_COLOR, g = e.WIDGET_SECONDARY_TEXT_COLOR, p = 0; p < c.length; ++p) { + var l = c[p], q = b; + l.y && (q = l.y); + l.last_y = q; + d.strokeStyle = n; d.fillStyle = "#222"; d.textAlign = "left"; - q.disabled && (d.globalAlpha *= 0.5); - switch(q.type) { + l.disabled && (d.globalAlpha *= 0.5); + switch(l.type) { case "button": - q.clicked && (d.fillStyle = "#AAA", q.clicked = !1, this.dirty_canvas = !0); - d.fillRect(15, r, f - 30, k); - n && !q.disabled && d.strokeRect(15, r, f - 30, k); - n && (d.textAlign = "center", d.fillStyle = h, d.fillText(q.name, 0.5 * f, r + 0.7 * k)); + l.clicked && (d.fillStyle = "#AAA", l.clicked = !1, this.dirty_canvas = !0); + d.fillRect(15, q, k - 30, h); + m && !l.disabled && d.strokeRect(15, q, k - 30, h); + m && (d.textAlign = "center", d.fillStyle = x, d.fillText(l.name, 0.5 * k, q + 0.7 * h)); break; case "toggle": d.textAlign = "left"; - d.strokeStyle = p; - d.fillStyle = l; + d.strokeStyle = n; + d.fillStyle = r; d.beginPath(); - n ? d.roundRect(15, b, f - 30, k, 0.5 * k) : d.rect(15, b, f - 30, k); + m ? d.roundRect(15, b, k - 30, h, 0.5 * h) : d.rect(15, b, k - 30, h); d.fill(); - n && !q.disabled && d.stroke(); - d.fillStyle = q.value ? "#89A" : "#333"; + m && !l.disabled && d.stroke(); + d.fillStyle = l.value ? "#89A" : "#333"; d.beginPath(); - d.arc(f - 30, r + 0.5 * k, 0.36 * k, 0, 2 * Math.PI); + d.arc(k - 30, q + 0.5 * h, 0.36 * h, 0, 2 * Math.PI); d.fill(); - n && (d.fillStyle = t, null != q.name && d.fillText(q.name, 30, r + 0.7 * k), d.fillStyle = q.value ? h : t, d.textAlign = "right", d.fillText(q.value ? q.options.on || "true" : q.options.off || "false", f - 40, r + 0.7 * k)); + m && (d.fillStyle = g, null != l.name && d.fillText(l.name, 30, q + 0.7 * h), d.fillStyle = l.value ? x : g, d.textAlign = "right", d.fillText(l.value ? l.options.on || "true" : l.options.off || "false", k - 40, q + 0.7 * h)); break; case "slider": - d.fillStyle = l; - d.fillRect(15, r, f - 30, k); - var u = q.options.max - q.options.min, B = (q.value - q.options.min) / u; - d.fillStyle = g == q ? "#89A" : "#678"; - d.fillRect(15, r, B * (f - 30), k); - n && !q.disabled && d.strokeRect(15, r, f - 30, k); - q.marker && (u = (q.marker - q.options.min) / u, d.fillStyle = "#AA9", d.fillRect(15 + u * (f - 30), r, 2, k)); - n && (d.textAlign = "center", d.fillStyle = h, d.fillText(q.name + " " + Number(q.value).toFixed(3), 0.5 * f, r + 0.7 * k)); + d.fillStyle = r; + d.fillRect(15, q, k - 30, h); + var E = l.options.max - l.options.min, t = (l.value - l.options.min) / E; + d.fillStyle = f == l ? "#89A" : "#678"; + d.fillRect(15, q, t * (k - 30), h); + m && !l.disabled && d.strokeRect(15, q, k - 30, h); + l.marker && (E = (l.marker - l.options.min) / E, d.fillStyle = "#AA9", d.fillRect(15 + E * (k - 30), q, 2, h)); + m && (d.textAlign = "center", d.fillStyle = x, d.fillText(l.name + " " + Number(l.value).toFixed(3), 0.5 * k, q + 0.7 * h)); break; case "number": case "combo": d.textAlign = "left"; - d.strokeStyle = p; - d.fillStyle = l; + d.strokeStyle = n; + d.fillStyle = r; d.beginPath(); - n ? d.roundRect(15, b, f - 30, k, 0.5 * k) : d.rect(15, b, f - 30, k); + m ? d.roundRect(15, b, k - 30, h, 0.5 * h) : d.rect(15, b, k - 30, h); d.fill(); - n && (q.disabled || d.stroke(), d.fillStyle = h, q.disabled || (d.beginPath(), d.moveTo(31, b + 5), d.lineTo(21, b + 0.5 * k), d.lineTo(31, b + k - 5), d.fill(), d.beginPath(), d.moveTo(f - 15 - 16, b + 5), d.lineTo(f - 15 - 6, b + 0.5 * k), d.lineTo(f - 15 - 16, b + k - 5), d.fill()), d.fillStyle = t, d.fillText(q.name, 35, r + 0.7 * k), d.fillStyle = h, d.textAlign = "right", "number" == q.type ? d.fillText(Number(q.value).toFixed(void 0 !== q.options.precision ? q.options.precision : - 3), f - 30 - 20, r + 0.7 * k) : (u = q.value, q.options.values && (B = q.options.values, B.constructor === Function && (B = B()), B && B.constructor !== Array && (u = B[q.value])), d.fillText(u, f - 30 - 20, r + 0.7 * k))); + m && (l.disabled || d.stroke(), d.fillStyle = x, l.disabled || (d.beginPath(), d.moveTo(31, b + 5), d.lineTo(21, b + 0.5 * h), d.lineTo(31, b + h - 5), d.fill(), d.beginPath(), d.moveTo(k - 15 - 16, b + 5), d.lineTo(k - 15 - 6, b + 0.5 * h), d.lineTo(k - 15 - 16, b + h - 5), d.fill()), d.fillStyle = g, d.fillText(l.name, 35, q + 0.7 * h), d.fillStyle = x, d.textAlign = "right", "number" == l.type ? d.fillText(Number(l.value).toFixed(void 0 !== l.options.precision ? l.options.precision : + 3), k - 30 - 20, q + 0.7 * h) : (E = l.value, l.options.values && (t = l.options.values, t.constructor === Function && (t = t()), t && t.constructor !== Array && (E = t[l.value])), d.fillText(E, k - 30 - 20, q + 0.7 * h))); break; case "string": case "text": d.textAlign = "left"; - d.strokeStyle = p; - d.fillStyle = l; + d.strokeStyle = n; + d.fillStyle = r; d.beginPath(); - n ? d.roundRect(15, b, f - 30, k, 0.5 * k) : d.rect(15, b, f - 30, k); + m ? d.roundRect(15, b, k - 30, h, 0.5 * h) : d.rect(15, b, k - 30, h); d.fill(); - n && (d.save(), d.beginPath(), d.rect(15, b, f - 30, k), d.clip(), d.stroke(), d.fillStyle = t, null != q.name && d.fillText(q.name, 30, r + 0.7 * k), d.fillStyle = h, d.textAlign = "right", d.fillText(String(q.value).substr(0, 30), f - 30, r + 0.7 * k), d.restore()); + m && (d.save(), d.beginPath(), d.rect(15, b, k - 30, h), d.clip(), d.stroke(), d.fillStyle = g, null != l.name && d.fillText(l.name, 30, q + 0.7 * h), d.fillStyle = x, d.textAlign = "right", d.fillText(String(l.value).substr(0, 30), k - 30, q + 0.7 * h), d.restore()); break; default: - q.draw && q.draw(d, a, f, r, k); + l.draw && l.draw(d, a, k, q, h); } - b += (q.computeSize ? q.computeSize(f)[1] : k) + 4; + b += (l.computeSize ? l.computeSize(k)[1] : h) + 4; d.globalAlpha = this.editor_alpha; } d.restore(); d.textAlign = "left"; }; - l.prototype.processNodeWidgets = function(a, b, d, g) { - function f(f, c) { - f.value = c; - f.options && f.options.property && void 0 !== a.properties[f.options.property] && a.setProperty(f.options.property, c); - f.callback && f.callback(f.value, p, a, b, d); + g.prototype.processNodeWidgets = function(a, b, d, f) { + function k(f, k) { + f.value = k; + f.options && f.options.property && void 0 !== a.properties[f.options.property] && a.setProperty(f.options.property, k); + f.callback && f.callback(f.value, n, a, b, d); } if (!a.widgets || !a.widgets.length) { return null; } - for (var c = b[0] - a.pos[0], k = b[1] - a.pos[1], n = a.size[0], p = this, l = this.getCanvasWindow(), h = 0; h < a.widgets.length; ++h) { - var t = a.widgets[h]; - if (t && !t.disabled) { - var m = t.computeSize ? t.computeSize(n)[1] : e.NODE_WIDGET_HEIGHT; - if (t == g || 6 < c && c < n - 12 && k > t.last_y && k < t.last_y + m) { - switch(t.type) { + for (var c = b[0] - a.pos[0], h = b[1] - a.pos[1], m = a.size[0], n = this, r = this.getCanvasWindow(), x = 0; x < a.widgets.length; ++x) { + var g = a.widgets[x]; + if (g && !g.disabled) { + var l = g.computeSize ? g.computeSize(m)[1] : e.NODE_WIDGET_HEIGHT; + if (g == f || 6 < c && c < m - 12 && h > g.last_y && h < g.last_y + l) { + switch(g.type) { case "button": if ("mousemove" === d.type) { break; } - t.callback && setTimeout(function() { - t.callback(t, p, a, b, d); + g.callback && setTimeout(function() { + g.callback(g, n, a, b, d); }, 20); - this.dirty_canvas = t.clicked = !0; + this.dirty_canvas = g.clicked = !0; break; case "slider": - l = Math.clamp((c - 10) / (n - 20), 0, 1); - t.value = t.options.min + (t.options.max - t.options.min) * l; - t.callback && setTimeout(function() { - f(t, t.value); + r = Math.clamp((c - 10) / (m - 20), 0, 1); + g.value = g.options.min + (g.options.max - g.options.min) * r; + g.callback && setTimeout(function() { + k(g, g.value); }, 20); this.dirty_canvas = !0; break; case "number": case "combo": - g = t.value; - if ("mousemove" == d.type && "number" == t.type) { - t.value += 0.1 * d.deltaX * (t.options.step || 1), null != t.options.min && t.value < t.options.min && (t.value = t.options.min), null != t.options.max && t.value > t.options.max && (t.value = t.options.max); + f = g.value; + if ("mousemove" == d.type && "number" == g.type) { + g.value += 0.1 * d.deltaX * (g.options.step || 1), null != g.options.min && g.value < g.options.min && (g.value = g.options.min), null != g.options.max && g.value > g.options.max && (g.value = g.options.max); } else { if ("mousedown" == d.type) { - var q = t.options.values; - q && q.constructor === Function && (q = t.options.values(t, a)); - var r = null; - "number" != t.type && (r = q.constructor === Array ? q : Object.keys(q)); - c = 40 > c ? -1 : c > n - 40 ? 1 : 0; - if ("number" == t.type) { - t.value += 0.1 * c * (t.options.step || 1), null != t.options.min && t.value < t.options.min && (t.value = t.options.min), null != t.options.max && t.value > t.options.max && (t.value = t.options.max); + var p = g.options.values; + p && p.constructor === Function && (p = g.options.values(g, a)); + var q = null; + "number" != g.type && (q = p.constructor === Array ? p : Object.keys(p)); + c = 40 > c ? -1 : c > m - 40 ? 1 : 0; + if ("number" == g.type) { + g.value += 0.1 * c * (g.options.step || 1), null != g.options.min && g.value < g.options.min && (g.value = g.options.min), null != g.options.max && g.value > g.options.max && (g.value = g.options.max); } else { if (c) { - l = -1, l = q.constructor === Object ? r.indexOf(String(t.value)) + c : r.indexOf(t.value) + c, l >= r.length && (l = r.length - 1), 0 > l && (l = 0), t.value = q.constructor === Array ? q[l] : l; + r = -1, r = p.constructor === Object ? q.indexOf(String(g.value)) + c : q.indexOf(g.value) + c, r >= q.length && (r = q.length - 1), 0 > r && (r = 0), g.value = p.constructor === Array ? p[r] : r; } else { - var u = q != r ? Object.values(q) : q; - new e.ContextMenu(u, {scale:Math.max(1, this.ds.scale), event:d, className:"dark", callback:function(a, b, d) { - q != r && (a = u.indexOf(a)); + var E = p != q ? Object.values(p) : p; + new e.ContextMenu(E, {scale:Math.max(1, this.ds.scale), event:d, className:"dark", callback:function(a, b, d) { + p != q && (a = E.indexOf(a)); this.value = a; - f(this, a); - p.dirty_canvas = !0; + k(this, a); + n.dirty_canvas = !0; return !1; - }.bind(t)}, l); + }.bind(g)}, r); } } } else { - "mouseup" == d.type && "number" == t.type && (c = 40 > c ? -1 : c > n - 40 ? 1 : 0, 200 > d.click_time && 0 == c && this.prompt("Value", t.value, function(a) { + "mouseup" == d.type && "number" == g.type && (c = 40 > c ? -1 : c > m - 40 ? 1 : 0, 200 > d.click_time && 0 == c && this.prompt("Value", g.value, function(a) { this.value = Number(a); - f(this, this.value); - }.bind(t), d)); + k(this, this.value); + }.bind(g), d)); } } - g != t.value && setTimeout(function() { - f(this, this.value); - }.bind(t), 20); + f != g.value && setTimeout(function() { + k(this, this.value); + }.bind(g), 20); this.dirty_canvas = !0; break; case "toggle": - "mousedown" == d.type && (t.value = !t.value, setTimeout(function() { - f(t, t.value); + "mousedown" == d.type && (g.value = !g.value, setTimeout(function() { + k(g, g.value); }, 20)); break; case "string": case "text": - "mousedown" == d.type && this.prompt("Value", t.value, function(a) { + "mousedown" == d.type && this.prompt("Value", g.value, function(a) { this.value = a; - f(this, a); - }.bind(t), d); + k(this, a); + }.bind(g), d); break; default: - t.mouse && (this.dirty_canvas = t.mouse(d, [c, k], a)); + g.mouse && (this.dirty_canvas = g.mouse(d, [c, h], a)); } - return t; + return g; } } } return null; }; - l.prototype.drawGroups = function(a, b) { + g.prototype.drawGroups = function(a, b) { if (this.graph) { a = this.graph._groups; b.save(); b.globalAlpha = 0.5 * this.editor_alpha; for (var d = 0; d < a.length; ++d) { - var c = a[d]; - if (x(this.visible_area, c._bounding)) { - b.fillStyle = c.color || "#335"; - b.strokeStyle = c.color || "#335"; - var f = c._pos, k = c._size; + var f = a[d]; + if (G(this.visible_area, f._bounding)) { + b.fillStyle = f.color || "#335"; + b.strokeStyle = f.color || "#335"; + var k = f._pos, c = f._size; b.globalAlpha = 0.25 * this.editor_alpha; b.beginPath(); - b.rect(f[0] + 0.5, f[1] + 0.5, k[0], k[1]); + b.rect(k[0] + 0.5, k[1] + 0.5, c[0], c[1]); b.fill(); b.globalAlpha = this.editor_alpha; b.stroke(); b.beginPath(); - b.moveTo(f[0] + k[0], f[1] + k[1]); - b.lineTo(f[0] + k[0] - 10, f[1] + k[1]); - b.lineTo(f[0] + k[0], f[1] + k[1] - 10); + b.moveTo(k[0] + c[0], k[1] + c[1]); + b.lineTo(k[0] + c[0] - 10, k[1] + c[1]); + b.lineTo(k[0] + c[0], k[1] + c[1] - 10); b.fill(); - k = c.font_size || e.DEFAULT_GROUP_FONT_SIZE; - b.font = k + "px Arial"; - b.fillText(c.title, f[0] + 4, f[1] + k); + c = f.font_size || e.DEFAULT_GROUP_FONT_SIZE; + b.font = c + "px Arial"; + b.fillText(f.title, k[0] + 4, k[1] + c); } } b.restore(); } }; - l.prototype.adjustNodesSize = function() { + g.prototype.adjustNodesSize = function() { for (var a = this.graph._nodes, b = 0; b < a.length; ++b) { a[b].size = a[b].computeSize(); } this.setDirty(!0, !0); }; - l.prototype.resize = function(a, b) { + g.prototype.resize = function(a, b) { a || b || (b = this.canvas.parentNode, a = b.offsetWidth, b = b.offsetHeight); if (this.canvas.width != a || this.canvas.height != b) { this.canvas.width = a, this.canvas.height = b, this.bgcanvas.width = this.canvas.width, this.bgcanvas.height = this.canvas.height, this.setDirty(!0, !0); } }; - l.prototype.switchLiveMode = function(a) { + g.prototype.switchLiveMode = function(a) { if (a) { var b = this, d = this.live_mode ? 1.1 : 0.9; this.live_mode && (this.live_mode = !1, this.editor_alpha = 0.1); - var c = setInterval(function() { + var f = setInterval(function() { b.editor_alpha *= d; b.dirty_canvas = !0; b.dirty_bgcanvas = !0; - 1 > d && 0.01 > b.editor_alpha && (clearInterval(c), 1 > d && (b.live_mode = !0)); - 1 < d && 0.99 < b.editor_alpha && (clearInterval(c), b.editor_alpha = 1); + 1 > d && 0.01 > b.editor_alpha && (clearInterval(f), 1 > d && (b.live_mode = !0)); + 1 < d && 0.99 < b.editor_alpha && (clearInterval(f), b.editor_alpha = 1); }, 1); } else { this.live_mode = !this.live_mode, this.dirty_bgcanvas = this.dirty_canvas = !0; } }; - l.prototype.onNodeSelectionChange = function(a) { + g.prototype.onNodeSelectionChange = function(a) { }; - l.prototype.touchHandler = function(a) { + g.prototype.touchHandler = function(a) { var b = a.changedTouches[0]; switch(a.type) { case "touchstart": @@ -3727,158 +3747,158 @@ $jscomp.polyfill("Object.values", function(v) { default: return; } - var c = this.getCanvasWindow(), f = c.document.createEvent("MouseEvent"); - f.initMouseEvent(d, !0, !0, c, 1, b.screenX, b.screenY, b.clientX, b.clientY, !1, !1, !1, !1, 0, null); - b.target.dispatchEvent(f); + var f = this.getCanvasWindow(), k = f.document.createEvent("MouseEvent"); + k.initMouseEvent(d, !0, !0, f, 1, b.screenX, b.screenY, b.clientX, b.clientY, !1, !1, !1, !1, 0, null); + b.target.dispatchEvent(k); a.preventDefault(); }; - l.onGroupAdd = function(a, b, d) { - a = l.active_canvas; + g.onGroupAdd = function(a, b, d) { + a = g.active_canvas; a.getCanvasWindow(); b = new e.LGraphGroup; b.pos = a.convertEventToCanvasOffset(d); a.graph.add(b); }; - l.onMenuAdd = function(a, b, d, c, f) { - function g(a, b) { - b = c.getFirstEvent(); + g.onMenuAdd = function(a, b, d, f, k) { + function c(a, b) { + b = f.getFirstEvent(); if (a = e.createNode(a.value)) { - a.pos = k.convertEventToCanvasOffset(b), k.graph.add(a); + a.pos = h.convertEventToCanvasOffset(b), h.graph.add(a); } - f && f(a); + k && k(a); } - var k = l.active_canvas, n = k.getCanvasWindow(); - a = e.getNodeTypesCategories(k.filter); + var h = g.active_canvas, m = h.getCanvasWindow(); + a = e.getNodeTypesCategories(h.filter); b = []; - for (var p in a) { - a[p] && b.push({value:a[p], content:a[p], has_submenu:!0}); + for (var n in a) { + a[n] && b.push({value:a[n], content:a[n], has_submenu:!0}); } - var h = new e.ContextMenu(b, {event:d, callback:function(a, b, d) { - a = e.getNodeTypesInCategory(a.value, k.filter); + var r = new e.ContextMenu(b, {event:d, callback:function(a, b, d) { + a = e.getNodeTypesInCategory(a.value, h.filter); b = []; for (var f in a) { a[f].skip_list || b.push({content:a[f].title, value:a[f].type}); } - new e.ContextMenu(b, {event:d, callback:g, parentMenu:h}, n); + new e.ContextMenu(b, {event:d, callback:c, parentMenu:r}, m); return !1; - }, parentMenu:c}, n); + }, parentMenu:f}, m); return !1; }; - l.onMenuCollapseAll = function() { + g.onMenuCollapseAll = function() { }; - l.onMenuNodeEdit = function() { + g.onMenuNodeEdit = function() { }; - l.showMenuNodeOptionalInputs = function(a, b, d, c, f) { - if (f) { - var g = this; - a = l.active_canvas.getCanvasWindow(); - b = f.optional_inputs; - f.onGetInputs && (b = f.onGetInputs()); - var k = []; + g.showMenuNodeOptionalInputs = function(a, b, d, f, k) { + if (k) { + var c = this; + a = g.active_canvas.getCanvasWindow(); + b = k.optional_inputs; + k.onGetInputs && (b = k.onGetInputs()); + var h = []; if (b) { - for (var n in b) { - var p = b[n]; - if (p) { - var h = p[0]; - p[2] && p[2].label && (h = p[2].label); - h = {content:h, value:p}; - p[1] == e.ACTION && (h.className = "event"); - k.push(h); + for (var m in b) { + var n = b[m]; + if (n) { + var r = n[0]; + n[2] && n[2].label && (r = n[2].label); + r = {content:r, value:n}; + n[1] == e.ACTION && (r.className = "event"); + h.push(r); } else { - k.push(null); + h.push(null); } } } - this.onMenuNodeInputs && (k = this.onMenuNodeInputs(k)); - if (k.length) { - return new e.ContextMenu(k, {event:d, callback:function(a, b, d) { - f && (a.callback && a.callback.call(g, f, a, b, d), a.value && (f.addInput(a.value[0], a.value[1], a.value[2]), f.setDirtyCanvas(!0, !0))); - }, parentMenu:c, node:f}, a), !1; + this.onMenuNodeInputs && (h = this.onMenuNodeInputs(h)); + if (h.length) { + return new e.ContextMenu(h, {event:d, callback:function(a, b, d) { + k && (a.callback && a.callback.call(c, k, a, b, d), a.value && (k.addInput(a.value[0], a.value[1], a.value[2]), k.setDirtyCanvas(!0, !0))); + }, parentMenu:f, node:k}, a), !1; } } }; - l.showMenuNodeOptionalOutputs = function(a, b, d, c, f) { - function g(a, b, d) { - if (f && (a.callback && a.callback.call(k, f, a, b, d), a.value)) { + g.showMenuNodeOptionalOutputs = function(a, b, d, f, k) { + function c(a, b, d) { + if (k && (a.callback && a.callback.call(h, k, a, b, d), a.value)) { if (d = a.value[1], !d || d.constructor !== Object && d.constructor !== Array) { - f.addOutput(a.value[0], a.value[1], a.value[2]), f.setDirtyCanvas(!0, !0); + k.addOutput(a.value[0], a.value[1], a.value[2]), k.setDirtyCanvas(!0, !0); } else { a = []; - for (var n in d) { - a.push({content:n, value:d[n]}); + for (var m in d) { + a.push({content:m, value:d[m]}); } - new e.ContextMenu(a, {event:b, callback:g, parentMenu:c, node:f}); + new e.ContextMenu(a, {event:b, callback:c, parentMenu:f, node:k}); return !1; } } } - if (f) { - var k = this; - a = l.active_canvas.getCanvasWindow(); - b = f.optional_outputs; - f.onGetOutputs && (b = f.onGetOutputs()); - var n = []; + if (k) { + var h = this; + a = g.active_canvas.getCanvasWindow(); + b = k.optional_outputs; + k.onGetOutputs && (b = k.onGetOutputs()); + var m = []; if (b) { - for (var p in b) { - var h = b[p]; - if (!h) { - n.push(null); + for (var n in b) { + var r = b[n]; + if (!r) { + m.push(null); } else { - if (!f.flags || !f.flags.skip_repeated_outputs || -1 == f.findOutputSlot(h[0])) { - var m = h[0]; - h[2] && h[2].label && (m = h[2].label); - m = {content:m, value:h}; - h[1] == e.EVENT && (m.className = "event"); - n.push(m); + if (!k.flags || !k.flags.skip_repeated_outputs || -1 == k.findOutputSlot(r[0])) { + var x = r[0]; + r[2] && r[2].label && (x = r[2].label); + x = {content:x, value:r}; + r[1] == e.EVENT && (x.className = "event"); + m.push(x); } } } } - this.onMenuNodeOutputs && (n = this.onMenuNodeOutputs(n)); - if (n.length) { - return new e.ContextMenu(n, {event:d, callback:g, parentMenu:c, node:f}, a), !1; + this.onMenuNodeOutputs && (m = this.onMenuNodeOutputs(m)); + if (m.length) { + return new e.ContextMenu(m, {event:d, callback:c, parentMenu:f, node:k}, a), !1; } } }; - l.onShowMenuNodeProperties = function(a, b, d, c, f) { - if (f && f.properties) { - var g = l.active_canvas; - b = g.getCanvasWindow(); - var k = [], n; - for (n in f.properties) { - a = void 0 !== f.properties[n] ? f.properties[n] : " ", "object" == typeof a && (a = JSON.stringify(a)), a = l.decodeHTML(a), k.push({content:"" + n + "" + a + "", value:n}); + g.onShowMenuNodeProperties = function(a, b, d, f, k) { + if (k && k.properties) { + var c = g.active_canvas; + b = c.getCanvasWindow(); + var h = [], m; + for (m in k.properties) { + a = void 0 !== k.properties[m] ? k.properties[m] : " ", "object" == typeof a && (a = JSON.stringify(a)), a = g.decodeHTML(a), h.push({content:"" + m + "" + a + "", value:m}); } - if (k.length) { - return new e.ContextMenu(k, {event:d, callback:function(a, b, d, c) { - f && (b = this.getBoundingClientRect(), g.showEditPropertyValue(f, a.value, {position:[b.left, b.top]})); - }, parentMenu:c, allow_html:!0, node:f}, b), !1; + if (h.length) { + return new e.ContextMenu(h, {event:d, callback:function(a, b, d, f) { + k && (b = this.getBoundingClientRect(), c.showEditPropertyValue(k, a.value, {position:[b.left, b.top]})); + }, parentMenu:f, allow_html:!0, node:k}, b), !1; } } }; - l.decodeHTML = function(a) { + g.decodeHTML = function(a) { var b = document.createElement("div"); b.innerText = a; return b.innerHTML; }; - l.onResizeNode = function(a, b, d, c, f) { - if (f) { - f.size = f.computeSize(); - if (f.onResize) { - f.onResize(f.size); + g.onResizeNode = function(a, b, d, f, k) { + if (k) { + k.size = k.computeSize(); + if (k.onResize) { + k.onResize(k.size); } - f.setDirtyCanvas(!0, !0); + k.setDirtyCanvas(!0, !0); } }; - l.prototype.showLinkMenu = function(a, b) { + g.prototype.showLinkMenu = function(a, b) { var d = this; console.log(a); - var c = new e.ContextMenu(["Add Node", null, "Delete"], {event:b, title:null != a.data ? a.data.constructor.name : null, callback:function(b, g, e) { + var f = new e.ContextMenu(["Add Node", null, "Delete"], {event:b, title:null != a.data ? a.data.constructor.name : null, callback:function(b, c, h) { switch(b) { case "Add Node": - l.onMenuAdd(null, null, e, c, function(b) { + g.onMenuAdd(null, null, h, f, function(b) { console.log("node autoconnect"); - var f = d.graph.getNodeById(a.origin_id), c = d.graph.getNodeById(a.target_id); - b.inputs && b.inputs.length && b.outputs && b.outputs.length && f.outputs[a.origin_slot].type == b.inputs[0].type && b.outputs[0].type == c.inputs[0].type && (f.connect(a.origin_slot, b, 0), b.connect(0, c, a.target_slot), b.pos[0] -= 0.5 * b.size[0]); + var f = d.graph.getNodeById(a.origin_id), k = d.graph.getNodeById(a.target_id); + b.inputs && b.inputs.length && b.outputs && b.outputs.length && f.outputs[a.origin_slot].type == b.inputs[0].type && b.outputs[0].type == k.inputs[0].type && (f.connect(a.origin_slot, b, 0), b.connect(0, k, a.target_slot), b.pos[0] -= 0.5 * b.size[0]); }); break; case "Delete": @@ -3887,60 +3907,60 @@ $jscomp.polyfill("Object.values", function(v) { }}); return !1; }; - l.onShowPropertyEditor = function(a, b, d, c, f) { - function e() { - var b = n.value; + g.onShowPropertyEditor = function(a, b, d, f, k) { + function c() { + var b = m.value; "Number" == a.type ? b = Number(b) : "Boolean" == a.type && (b = !!b); - f[g] = b; - k.parentNode && k.parentNode.removeChild(k); - f.setDirtyCanvas(!0, !0); + k[h] = b; + e.parentNode && e.parentNode.removeChild(e); + k.setDirtyCanvas(!0, !0); } - var g = a.property || "title"; - b = f[g]; - var k = document.createElement("div"); - k.className = "graphdialog"; - k.innerHTML = ""; - k.querySelector(".name").innerText = g; - var n = k.querySelector("input"); - n && (n.value = b, n.addEventListener("blur", function(a) { + var h = a.property || "title"; + b = k[h]; + var e = document.createElement("div"); + e.className = "graphdialog"; + e.innerHTML = ""; + e.querySelector(".name").innerText = h; + var m = e.querySelector("input"); + m && (m.value = b, m.addEventListener("blur", function(a) { this.focus(); - }), n.addEventListener("keydown", function(a) { - 13 == a.keyCode && (e(), a.preventDefault(), a.stopPropagation()); + }), m.addEventListener("keydown", function(a) { + 13 == a.keyCode && (c(), a.preventDefault(), a.stopPropagation()); })); - b = l.active_canvas.canvas; + b = g.active_canvas.canvas; d = b.getBoundingClientRect(); - var p = c = -20; - d && (c -= d.left, p -= d.top); - event ? (k.style.left = event.clientX + c + "px", k.style.top = event.clientY + p + "px") : (k.style.left = 0.5 * b.width + c + "px", k.style.top = 0.5 * b.height + p + "px"); - k.querySelector("button").addEventListener("click", e); - b.parentNode.appendChild(k); + var n = f = -20; + d && (f -= d.left, n -= d.top); + event ? (e.style.left = event.clientX + f + "px", e.style.top = event.clientY + n + "px") : (e.style.left = 0.5 * b.width + f + "px", e.style.top = 0.5 * b.height + n + "px"); + e.querySelector("button").addEventListener("click", c); + b.parentNode.appendChild(e); }; - l.prototype.prompt = function(a, b, d, c) { - var f = this; + g.prototype.prompt = function(a, b, d, f) { + var k = this; a = a || ""; - var e = !1, g = document.createElement("div"); - g.className = "graphdialog rounded"; - g.innerHTML = " "; - g.close = function() { - f.prompt_box = null; - g.parentNode && g.parentNode.removeChild(g); + var c = !1, h = document.createElement("div"); + h.className = "graphdialog rounded"; + h.innerHTML = " "; + h.close = function() { + k.prompt_box = null; + h.parentNode && h.parentNode.removeChild(h); }; - 1 < this.ds.scale && (g.style.transform = "scale(" + this.ds.scale + ")"); - g.addEventListener("mouseleave", function(a) { - e || g.close(); + 1 < this.ds.scale && (h.style.transform = "scale(" + this.ds.scale + ")"); + h.addEventListener("mouseleave", function(a) { + c || h.close(); }); - f.prompt_box && f.prompt_box.close(); - f.prompt_box = g; - g.querySelector(".name").innerText = a; - g.querySelector(".value").value = b; - var k = g.querySelector("input"); - k.addEventListener("keydown", function(a) { - e = !0; + k.prompt_box && k.prompt_box.close(); + k.prompt_box = h; + h.querySelector(".name").innerText = a; + h.querySelector(".value").value = b; + var e = h.querySelector("input"); + e.addEventListener("keydown", function(a) { + c = !0; if (27 == a.keyCode) { - g.close(); + h.close(); } else { if (13 == a.keyCode) { - d && d(this.value), g.close(); + d && d(this.value), h.close(); } else { return; } @@ -3948,48 +3968,48 @@ $jscomp.polyfill("Object.values", function(v) { a.preventDefault(); a.stopPropagation(); }); - g.querySelector("button").addEventListener("click", function(a) { - d && d(k.value); - f.setDirty(!0); - g.close(); + h.querySelector("button").addEventListener("click", function(a) { + d && d(e.value); + k.setDirty(!0); + h.close(); }); - a = l.active_canvas.canvas; + a = g.active_canvas.canvas; b = a.getBoundingClientRect(); - var n = -20, p = -20; - b && (n -= b.left, p -= b.top); - c ? (g.style.left = c.clientX + n + "px", g.style.top = c.clientY + p + "px") : (g.style.left = 0.5 * a.width + n + "px", g.style.top = 0.5 * a.height + p + "px"); - a.parentNode.appendChild(g); + var m = -20, n = -20; + b && (m -= b.left, n -= b.top); + f ? (h.style.left = f.clientX + m + "px", h.style.top = f.clientY + n + "px") : (h.style.left = 0.5 * a.width + m + "px", h.style.top = 0.5 * a.height + n + "px"); + a.parentNode.appendChild(h); setTimeout(function() { - k.focus(); + e.focus(); }, 10); - return g; + return h; }; - l.search_limit = -1; - l.prototype.showSearchBox = function(a) { + g.search_limit = -1; + g.prototype.showSearchBox = function(a) { function b(b) { if (b) { - if (f.onSearchBoxSelection) { - f.onSearchBoxSelection(b, a, k); + if (k.onSearchBoxSelection) { + k.onSearchBoxSelection(b, a, c); } else { var d = e.searchbox_extras[b.toLowerCase()]; d && (b = d.type); if (b = e.createNode(b)) { - b.pos = k.convertEventToCanvasOffset(a), k.graph.add(b); + b.pos = c.convertEventToCanvasOffset(a), c.graph.add(b); } if (d && d.data) { if (d.data.properties) { - for (var c in d.data.properties) { - b.addProperty(c, d.data.properties[c]); + for (var f in d.data.properties) { + b.addProperty(f, d.data.properties[f]); } } if (d.data.inputs) { - for (c in b.inputs = [], d.data.inputs) { - b.addOutput(d.data.inputs[c][0], d.data.inputs[c][1]); + for (f in b.inputs = [], d.data.inputs) { + b.addOutput(d.data.inputs[f][0], d.data.inputs[f][1]); } } if (d.data.outputs) { - for (c in b.outputs = [], d.data.outputs) { - b.addOutput(d.data.outputs[c][0], d.data.outputs[c][1]); + for (f in b.outputs = [], d.data.outputs) { + b.addOutput(d.data.outputs[f][0], d.data.outputs[f][1]); } } d.data.title && (b.title = d.data.title); @@ -3997,18 +4017,18 @@ $jscomp.polyfill("Object.values", function(v) { } } } - h.close(); + n.close(); } function d(a) { - var b = u; - u && u.classList.remove("selected"); - u ? (u = a ? u.nextSibling : u.previousSibling) || (u = b) : u = a ? q.childNodes[0] : q.childNodes[q.childNodes.length]; - u && (u.classList.add("selected"), u.scrollIntoView({block:"end", behavior:"smooth"})); + var b = q; + q && q.classList.remove("selected"); + q ? (q = a ? q.nextSibling : q.previousSibling) || (q = b) : q = a ? x.childNodes[0] : x.childNodes[x.childNodes.length]; + q && (q.classList.add("selected"), q.scrollIntoView({block:"end", behavior:"smooth"})); } - function c() { + function f() { function a(a, d) { var f = document.createElement("div"); - t || (t = a); + l || (l = a); f.innerText = a; f.dataset.type = escape(a); f.className = "litegraph lite-search-item"; @@ -4016,80 +4036,80 @@ $jscomp.polyfill("Object.values", function(v) { f.addEventListener("click", function(a) { b(unescape(this.dataset.type)); }); - q.appendChild(f); + x.appendChild(f); } - r = null; - var d = B.value; - t = null; - q.innerHTML = ""; + p = null; + var d = E.value; + l = null; + x.innerHTML = ""; if (d) { - if (f.onSearchBox) { - var c = f.onSearchBox(q, d, k); - if (c) { - for (var g = 0; g < c.length; ++g) { - a(c[g]); + if (k.onSearchBox) { + var f = k.onSearchBox(x, d, c); + if (f) { + for (var h = 0; h < f.length; ++h) { + a(f[h]); } } } else { - c = function(a) { + f = function(a) { var b = e.registered_node_types[a]; - return S && b.filter != S ? !1 : -1 !== a.toLowerCase().indexOf(d); + return m && b.filter != m ? !1 : -1 !== a.toLowerCase().indexOf(d); }; - var n = 0; + var T = 0; d = d.toLowerCase(); - var S = k.filter || k.graph.filter; - for (g in e.searchbox_extras) { - var p = e.searchbox_extras[g]; - if (-1 !== p.desc.toLowerCase().indexOf(d)) { - var h = e.registered_node_types[p.type]; - if (!h || !h.filter || h.filter == S) { - if (a(p.desc, "searchbox_extra"), -1 !== l.search_limit && n++ > l.search_limit) { + var m = c.filter || c.graph.filter; + for (h in e.searchbox_extras) { + var n = e.searchbox_extras[h]; + if (-1 !== n.desc.toLowerCase().indexOf(d)) { + var r = e.registered_node_types[n.type]; + if (!r || !r.filter || r.filter == m) { + if (a(n.desc, "searchbox_extra"), -1 !== g.search_limit && T++ > g.search_limit) { break; } } } } - p = null; + n = null; if (Array.prototype.filter) { - p = Object.keys(e.registered_node_types).filter(c); + n = Object.keys(e.registered_node_types).filter(f); } else { - for (g in p = [], e.registered_node_types) { - c(g) && p.push(g); + for (h in n = [], e.registered_node_types) { + f(h) && n.push(h); } } - for (g = 0; g < p.length && !(a(p[g]), -1 !== l.search_limit && n++ > l.search_limit); g++) { + for (h = 0; h < n.length && !(a(n[h]), -1 !== g.search_limit && T++ > g.search_limit); h++) { } } } } - var f = this, k = l.active_canvas, n = k.canvas, p = n.ownerDocument || document, h = document.createElement("div"); - h.className = "litegraph litesearchbox graphdialog rounded"; - h.innerHTML = "Search
"; - h.close = function() { - f.search_box = null; - p.body.focus(); - p.body.style.overflow = ""; + var k = this, c = g.active_canvas, h = c.canvas, m = h.ownerDocument || document, n = document.createElement("div"); + n.className = "litegraph litesearchbox graphdialog rounded"; + n.innerHTML = "Search
"; + n.close = function() { + k.search_box = null; + m.body.focus(); + m.body.style.overflow = ""; setTimeout(function() { - f.canvas.focus(); + k.canvas.focus(); }, 20); - h.parentNode && h.parentNode.removeChild(h); + n.parentNode && n.parentNode.removeChild(n); }; - var m = null; - 1 < this.ds.scale && (h.style.transform = "scale(" + this.ds.scale + ")"); - h.addEventListener("mouseenter", function(a) { - m && (clearTimeout(m), m = null); + var r = null; + 1 < this.ds.scale && (n.style.transform = "scale(" + this.ds.scale + ")"); + n.addEventListener("mouseenter", function(a) { + r && (clearTimeout(r), r = null); }); - h.addEventListener("mouseleave", function(a) { - m = setTimeout(function() { - h.close(); + n.addEventListener("mouseleave", function(a) { + r = setTimeout(function() { + n.close(); }, 500); }); - f.search_box && f.search_box.close(); - f.search_box = h; - var q = h.querySelector(".helper"), t = null, r = null, u = null, B = h.querySelector("input"); - B && (B.addEventListener("blur", function(a) { + k.search_box && k.search_box.close(); + k.search_box = n; + var x = n.querySelector(".helper"), l = null, p = null, q = null, E = n.querySelector("input"); + E && (E.addEventListener("blur", function(a) { this.focus(); - }), B.addEventListener("keydown", function(a) { + }), E.addEventListener("keydown", function(a) { if (38 == a.keyCode) { d(!1); } else { @@ -4097,13 +4117,13 @@ $jscomp.polyfill("Object.values", function(v) { d(!0); } else { if (27 == a.keyCode) { - h.close(); + n.close(); } else { if (13 == a.keyCode) { - u ? b(u.innerHTML) : t ? b(t) : h.close(); + q ? b(q.innerHTML) : l ? b(l) : n.close(); } else { - r && clearInterval(r); - r = setTimeout(c, 10); + p && clearInterval(p); + p = setTimeout(f, 10); return; } } @@ -4114,22 +4134,22 @@ $jscomp.polyfill("Object.values", function(v) { a.stopImmediatePropagation(); return !0; })); - p.fullscreenElement ? p.fullscreenElement.appendChild(h) : (p.body.appendChild(h), p.body.style.overflow = "hidden"); - n = n.getBoundingClientRect(); - var x = (a ? a.clientY : n.top + 0.5 * n.height) - 20; - h.style.left = (a ? a.clientX : n.left + 0.5 * n.width) - 80 + "px"; - h.style.top = x + "px"; - a.layerY > n.height - 200 && (q.style.maxHeight = n.height - a.layerY - 20 + "px"); - B.focus(); - return h; + m.fullscreenElement ? m.fullscreenElement.appendChild(n) : (m.body.appendChild(n), m.body.style.overflow = "hidden"); + h = h.getBoundingClientRect(); + var t = (a ? a.clientY : h.top + 0.5 * h.height) - 20; + n.style.left = (a ? a.clientX : h.left + 0.5 * h.width) - 80 + "px"; + n.style.top = t + "px"; + a.layerY > h.height - 200 && (x.style.maxHeight = h.height - a.layerY - 20 + "px"); + E.focus(); + return n; }; - l.prototype.showEditPropertyValue = function(a, b, d) { - function c() { - f(t.value); + g.prototype.showEditPropertyValue = function(a, b, d) { + function f() { + k(r.value); } - function f(f) { + function k(f) { "number" == typeof a.properties[b] && (f = Number(f)); - if ("array" == k || "object" == k) { + if ("array" == h || "object" == h) { f = JSON.parse(f); } a.properties[b] = f; @@ -4140,144 +4160,144 @@ $jscomp.polyfill("Object.values", function(v) { if (d.onclose) { d.onclose(); } - l.close(); + g.close(); a.setDirtyCanvas(!0, !0); } if (a && void 0 !== a.properties[b]) { d = d || {}; - var e = a.getPropertyInfo(b), k = e.type, n = ""; - if ("string" == k || "number" == k || "array" == k || "object" == k) { - n = ""; + var c = a.getPropertyInfo(b), h = c.type, e = ""; + if ("string" == h || "number" == h || "array" == h || "object" == h) { + e = ""; } else { - if ("enum" == k && e.values) { - n = ""; + for (var m in c.values) { + var n = c.values.constructor === Array ? c.values[m] : m; + e += ""; } - n += ""; + e += ""; } else { - if ("boolean" == k) { - n = ""; + if ("boolean" == h) { + e = ""; } else { - console.warn("unknown type: " + k); + console.warn("unknown type: " + h); return; } } } - var l = this.createDialog("" + b + "" + n + "", d); - if ("enum" == k && e.values) { - var t = l.querySelector("select"); - t.addEventListener("change", function(a) { - f(a.target.value); + var g = this.createDialog("" + b + "" + e + "", d); + if ("enum" == h && c.values) { + var r = g.querySelector("select"); + r.addEventListener("change", function(a) { + k(a.target.value); }); } else { - if ("boolean" == k) { - (t = l.querySelector("input")) && t.addEventListener("click", function(a) { - f(!!t.checked); + if ("boolean" == h) { + (r = g.querySelector("input")) && r.addEventListener("click", function(a) { + k(!!r.checked); }); } else { - if (t = l.querySelector("input")) { - t.addEventListener("blur", function(a) { + if (r = g.querySelector("input")) { + r.addEventListener("blur", function(a) { this.focus(); - }), h = void 0 !== a.properties[b] ? a.properties[b] : "", h = JSON.stringify(h), t.value = h, t.addEventListener("keydown", function(a) { - 13 == a.keyCode && (c(), a.preventDefault(), a.stopPropagation()); + }), n = void 0 !== a.properties[b] ? a.properties[b] : "", n = JSON.stringify(n), r.value = n, r.addEventListener("keydown", function(a) { + 13 == a.keyCode && (f(), a.preventDefault(), a.stopPropagation()); }); } } } - l.querySelector("button").addEventListener("click", c); - return l; + g.querySelector("button").addEventListener("click", f); + return g; } }; - l.prototype.createDialog = function(a, b) { + g.prototype.createDialog = function(a, b) { b = b || {}; var d = document.createElement("div"); d.className = "graphdialog"; d.innerHTML = a; a = this.canvas.getBoundingClientRect(); - var c = -20, f = -20; - a && (c -= a.left, f -= a.top); - b.position ? (c += b.position[0], f += b.position[1]) : b.event ? (c += b.event.clientX, f += b.event.clientY) : (c += 0.5 * this.canvas.width, f += 0.5 * this.canvas.height); - d.style.left = c + "px"; - d.style.top = f + "px"; + var f = -20, k = -20; + a && (f -= a.left, k -= a.top); + b.position ? (f += b.position[0], k += b.position[1]) : b.event ? (f += b.event.clientX, k += b.event.clientY) : (f += 0.5 * this.canvas.width, k += 0.5 * this.canvas.height); + d.style.left = f + "px"; + d.style.top = k + "px"; this.canvas.parentNode.appendChild(d); d.close = function() { this.parentNode && this.parentNode.removeChild(this); }; return d; }; - l.onMenuNodeCollapse = function(a, b, d, c, f) { - f.collapse(); + g.onMenuNodeCollapse = function(a, b, d, f, k) { + k.collapse(); }; - l.onMenuNodePin = function(a, b, d, c, f) { - f.pin(); + g.onMenuNodePin = function(a, b, d, f, k) { + k.pin(); }; - l.onMenuNodeMode = function(a, b, d, c, f) { + g.onMenuNodeMode = function(a, b, d, f, k) { new e.ContextMenu(["Always", "On Event", "On Trigger", "Never"], {event:d, callback:function(a) { - if (f) { + if (k) { switch(a) { case "On Event": - f.mode = e.ON_EVENT; + k.mode = e.ON_EVENT; break; case "On Trigger": - f.mode = e.ON_TRIGGER; + k.mode = e.ON_TRIGGER; break; case "Never": - f.mode = e.NEVER; + k.mode = e.NEVER; break; default: - f.mode = e.ALWAYS; + k.mode = e.ALWAYS; } } - }, parentMenu:c, node:f}); + }, parentMenu:f, node:k}); return !1; }; - l.onMenuNodeColors = function(a, b, d, c, f) { - if (!f) { + g.onMenuNodeColors = function(a, b, d, f, k) { + if (!k) { throw "no node for color"; } b = []; b.push({value:null, content:"No color"}); - for (var g in l.node_colors) { - a = l.node_colors[g], a = {value:g, content:"" + g + ""}, b.push(a); + for (var c in g.node_colors) { + a = g.node_colors[c], a = {value:c, content:"" + c + ""}, b.push(a); } new e.ContextMenu(b, {event:d, callback:function(a) { - f && ((a = a.value ? l.node_colors[a.value] : null) ? f.constructor === e.LGraphGroup ? f.color = a.groupcolor : (f.color = a.color, f.bgcolor = a.bgcolor) : (delete f.color, delete f.bgcolor), f.setDirtyCanvas(!0, !0)); - }, parentMenu:c, node:f}); + k && ((a = a.value ? g.node_colors[a.value] : null) ? k.constructor === e.LGraphGroup ? k.color = a.groupcolor : (k.color = a.color, k.bgcolor = a.bgcolor) : (delete k.color, delete k.bgcolor), k.setDirtyCanvas(!0, !0)); + }, parentMenu:f, node:k}); return !1; }; - l.onMenuNodeShapes = function(a, b, d, c, f) { - if (!f) { + g.onMenuNodeShapes = function(a, b, d, f, c) { + if (!c) { throw "no node passed"; } new e.ContextMenu(e.VALID_SHAPES, {event:d, callback:function(a) { - f && (f.shape = a, f.setDirtyCanvas(!0)); - }, parentMenu:c, node:f}); + c && (c.shape = a, c.setDirtyCanvas(!0)); + }, parentMenu:f, node:c}); return !1; }; - l.onMenuNodeRemove = function(a, b, d, c, f) { - if (!f) { + g.onMenuNodeRemove = function(a, b, d, f, c) { + if (!c) { throw "no node passed"; } - !1 !== f.removable && (f.graph.remove(f), f.setDirtyCanvas(!0, !0)); + !1 !== c.removable && (c.graph.remove(c), c.setDirtyCanvas(!0, !0)); }; - l.onMenuNodeToSubgraph = function(a, b, d, c, f) { - a = f.graph; - if (b = l.active_canvas) { - d = Object.values(b.selected_nodes || {}), d.length || (d = [f]), c = e.createNode("graph/subgraph"), c.pos = f.pos.concat(), a.add(c), c.buildFromNodes(d), b.deselectAllNodes(), f.setDirtyCanvas(!0, !0); + g.onMenuNodeToSubgraph = function(a, b, d, f, c) { + a = c.graph; + if (b = g.active_canvas) { + d = Object.values(b.selected_nodes || {}), d.length || (d = [c]), f = e.createNode("graph/subgraph"), f.pos = c.pos.concat(), a.add(f), f.buildFromNodes(d), b.deselectAllNodes(), c.setDirtyCanvas(!0, !0); } }; - l.onMenuNodeClone = function(a, b, d, c, f) { - 0 != f.clonable && (a = f.clone()) && (a.pos = [f.pos[0] + 5, f.pos[1] + 5], f.graph.add(a), f.setDirtyCanvas(!0, !0)); + g.onMenuNodeClone = function(a, b, d, f, c) { + 0 != c.clonable && (a = c.clone()) && (a.pos = [c.pos[0] + 5, c.pos[1] + 5], c.graph.add(a), c.setDirtyCanvas(!0, !0)); }; - l.node_colors = {red:{color:"#322", bgcolor:"#533", groupcolor:"#A88"}, brown:{color:"#332922", bgcolor:"#593930", groupcolor:"#b06634"}, green:{color:"#232", bgcolor:"#353", groupcolor:"#8A8"}, blue:{color:"#223", bgcolor:"#335", groupcolor:"#88A"}, pale_blue:{color:"#2a363b", bgcolor:"#3f5159", groupcolor:"#3f789e"}, cyan:{color:"#233", bgcolor:"#355", groupcolor:"#8AA"}, purple:{color:"#323", bgcolor:"#535", groupcolor:"#a1309b"}, yellow:{color:"#432", bgcolor:"#653", groupcolor:"#b58b2a"}, + g.node_colors = {red:{color:"#322", bgcolor:"#533", groupcolor:"#A88"}, brown:{color:"#332922", bgcolor:"#593930", groupcolor:"#b06634"}, green:{color:"#232", bgcolor:"#353", groupcolor:"#8A8"}, blue:{color:"#223", bgcolor:"#335", groupcolor:"#88A"}, pale_blue:{color:"#2a363b", bgcolor:"#3f5159", groupcolor:"#3f789e"}, cyan:{color:"#233", bgcolor:"#355", groupcolor:"#8AA"}, purple:{color:"#323", bgcolor:"#535", groupcolor:"#a1309b"}, yellow:{color:"#432", bgcolor:"#653", groupcolor:"#b58b2a"}, black:{color:"#222", bgcolor:"#000", groupcolor:"#444"}}; - l.prototype.getCanvasMenuOptions = function() { + g.prototype.getCanvasMenuOptions = function() { if (this.getMenuOptions) { var a = this.getMenuOptions(); } else { - a = [{content:"Add Node", has_submenu:!0, callback:l.onMenuAdd}, {content:"Add Group", callback:l.onGroupAdd}], this._graph_stack && 0 < this._graph_stack.length && a.push(null, {content:"Close subgraph", callback:this.closeSubgraph.bind(this)}); + a = [{content:"Add Node", has_submenu:!0, callback:g.onMenuAdd}, {content:"Add Group", callback:g.onGroupAdd}], this._graph_stack && 0 < this._graph_stack.length && a.push(null, {content:"Close subgraph", callback:this.closeSubgraph.bind(this)}); } if (this.getExtraMenuOptions) { var b = this.getExtraMenuOptions(this, a); @@ -4285,27 +4305,27 @@ $jscomp.polyfill("Object.values", function(v) { } return a; }; - l.prototype.getNodeMenuOptions = function(a) { - var b = a.getMenuOptions ? a.getMenuOptions(this) : [{content:"Inputs", has_submenu:!0, disabled:!0, callback:l.showMenuNodeOptionalInputs}, {content:"Outputs", has_submenu:!0, disabled:!0, callback:l.showMenuNodeOptionalOutputs}, null, {content:"Properties", has_submenu:!0, callback:l.onShowMenuNodeProperties}, null, {content:"Title", callback:l.onShowPropertyEditor}, {content:"Mode", has_submenu:!0, callback:l.onMenuNodeMode}, {content:"Resize", callback:l.onResizeNode}, {content:"Collapse", - callback:l.onMenuNodeCollapse}, {content:"Pin", callback:l.onMenuNodePin}, {content:"Colors", has_submenu:!0, callback:l.onMenuNodeColors}, {content:"Shapes", has_submenu:!0, callback:l.onMenuNodeShapes}, null]; + g.prototype.getNodeMenuOptions = function(a) { + var b = a.getMenuOptions ? a.getMenuOptions(this) : [{content:"Inputs", has_submenu:!0, disabled:!0, callback:g.showMenuNodeOptionalInputs}, {content:"Outputs", has_submenu:!0, disabled:!0, callback:g.showMenuNodeOptionalOutputs}, null, {content:"Properties", has_submenu:!0, callback:g.onShowMenuNodeProperties}, null, {content:"Title", callback:g.onShowPropertyEditor}, {content:"Mode", has_submenu:!0, callback:g.onMenuNodeMode}, {content:"Resize", callback:g.onResizeNode}, {content:"Collapse", + callback:g.onMenuNodeCollapse}, {content:"Pin", callback:g.onMenuNodePin}, {content:"Colors", has_submenu:!0, callback:g.onMenuNodeColors}, {content:"Shapes", has_submenu:!0, callback:g.onMenuNodeShapes}, null]; if (a.onGetInputs) { var d = a.onGetInputs(); d && d.length && (b[0].disabled = !1); } a.onGetOutputs && (d = a.onGetOutputs()) && d.length && (b[1].disabled = !1); a.getExtraMenuOptions && (d = a.getExtraMenuOptions(this)) && (d.push(null), b = d.concat(b)); - !1 !== a.clonable && b.push({content:"Clone", callback:l.onMenuNodeClone}); - !1 !== a.removable && b.push(null, {content:"Remove", callback:l.onMenuNodeRemove}); + !1 !== a.clonable && b.push({content:"Clone", callback:g.onMenuNodeClone}); + !1 !== a.removable && b.push(null, {content:"Remove", callback:g.onMenuNodeRemove}); if (a.graph && a.graph.onGetNodeMenuOptions) { a.graph.onGetNodeMenuOptions(b, a); } return b; }; - l.prototype.getGroupMenuOptions = function(a) { - return [{content:"Title", callback:l.onShowPropertyEditor}, {content:"Color", has_submenu:!0, callback:l.onMenuNodeColors}, {content:"Font size", property:"font_size", type:"Number", callback:l.onShowPropertyEditor}, null, {content:"Remove", callback:l.onMenuNodeRemove}]; + g.prototype.getGroupMenuOptions = function(a) { + return [{content:"Title", callback:g.onShowPropertyEditor}, {content:"Color", has_submenu:!0, callback:g.onMenuNodeColors}, {content:"Font size", property:"font_size", type:"Number", callback:g.onShowPropertyEditor}, null, {content:"Remove", callback:g.onMenuNodeRemove}]; }; - l.prototype.processContextMenu = function(a, b) { - var d = this, c = l.active_canvas.getCanvasWindow(), f = null, k = {event:b, callback:function(b, f, c) { + g.prototype.processContextMenu = function(a, b) { + var d = this, f = g.active_canvas.getCanvasWindow(), c = null, h = {event:b, callback:function(b, f, c) { if (b) { if ("Remove Slot" == b.content) { b = b.slot, b.input ? a.removeInput(b.slot) : b.output && a.removeOutput(b.slot); @@ -4315,36 +4335,36 @@ $jscomp.polyfill("Object.values", function(v) { } else { if ("Rename Slot" == b.content) { b = b.slot; - var e = b.input ? a.getInputInfo(b.slot) : a.getOutputInfo(b.slot), g = d.createDialog("Name", f), k = g.querySelector("input"); - k && e && (k.value = e.label || ""); - g.querySelector("button").addEventListener("click", function(a) { - k.value && (e && (e.label = k.value), d.setDirty(!0)); - g.close(); + var k = b.input ? a.getInputInfo(b.slot) : a.getOutputInfo(b.slot), h = d.createDialog("Name", f), e = h.querySelector("input"); + e && k && (e.value = k.label || ""); + h.querySelector("button").addEventListener("click", function(a) { + e.value && (k && (k.label = e.value), d.setDirty(!0)); + h.close(); }); } } } } }, extra:a}; - a && (k.title = a.type); - var n = null; - a && (n = a.getSlotInPosition(b.canvasX, b.canvasY), l.active_node = a); - n ? (f = [], a.getSlotMenuOptions ? f = a.getSlotMenuOptions(n) : (n && n.output && n.output.links && n.output.links.length && f.push({content:"Disconnect Links", slot:n}), b = n.input || n.output, f.push(b.locked ? "Cannot remove" : {content:"Remove Slot", slot:n}), f.push(b.nameLocked ? "Cannot rename" : {content:"Rename Slot", slot:n})), k.title = (n.input ? n.input.type : n.output.type) || "*", n.input && n.input.type == e.ACTION && (k.title = "Action"), n.output && n.output.type == e.EVENT && - (k.title = "Event")) : a ? f = this.getNodeMenuOptions(a) : (f = this.getCanvasMenuOptions(), (n = this.graph.getGroupOnPos(b.canvasX, b.canvasY)) && f.push(null, {content:"Edit Group", has_submenu:!0, submenu:{title:"Group", extra:n, options:this.getGroupMenuOptions(n)}})); - f && new e.ContextMenu(f, k, c); + a && (h.title = a.type); + var m = null; + a && (m = a.getSlotInPosition(b.canvasX, b.canvasY), g.active_node = a); + m ? (c = [], a.getSlotMenuOptions ? c = a.getSlotMenuOptions(m) : (m && m.output && m.output.links && m.output.links.length && c.push({content:"Disconnect Links", slot:m}), b = m.input || m.output, c.push(b.locked ? "Cannot remove" : {content:"Remove Slot", slot:m}), c.push(b.nameLocked ? "Cannot rename" : {content:"Rename Slot", slot:m})), h.title = (m.input ? m.input.type : m.output.type) || "*", m.input && m.input.type == e.ACTION && (h.title = "Action"), m.output && m.output.type == e.EVENT && + (h.title = "Event")) : a ? c = this.getNodeMenuOptions(a) : (c = this.getCanvasMenuOptions(), (m = this.graph.getGroupOnPos(b.canvasX, b.canvasY)) && c.push(null, {content:"Edit Group", has_submenu:!0, submenu:{title:"Group", extra:m, options:this.getGroupMenuOptions(m)}})); + c && new e.ContextMenu(c, h, f); }; - "undefined" != typeof window && window.CanvasRenderingContext2D && (window.CanvasRenderingContext2D.prototype.roundRect = function(a, b, d, c, f, e) { - void 0 === f && (f = 5); - void 0 === e && (e = f); - this.moveTo(a + f, b); - this.lineTo(a + d - f, b); - this.quadraticCurveTo(a + d, b, a + d, b + f); - this.lineTo(a + d, b + c - e); - this.quadraticCurveTo(a + d, b + c, a + d - e, b + c); - this.lineTo(a + e, b + c); - this.quadraticCurveTo(a, b + c, a, b + c - e); - this.lineTo(a, b + f); - this.quadraticCurveTo(a, b, a + f, b); + "undefined" != typeof window && window.CanvasRenderingContext2D && (window.CanvasRenderingContext2D.prototype.roundRect = function(a, b, d, f, c, h) { + void 0 === c && (c = 5); + void 0 === h && (h = c); + this.moveTo(a + c, b); + this.lineTo(a + d - c, b); + this.quadraticCurveTo(a + d, b, a + d, b + c); + this.lineTo(a + d, b + f - h); + this.quadraticCurveTo(a + d, b + f, a + d - h, b + f); + this.lineTo(a + h, b + f); + this.quadraticCurveTo(a, b + f, a, b + f - h); + this.lineTo(a, b + c); + this.quadraticCurveTo(a, b, a + c, b); }); e.compareObjects = function(a, b) { for (var d in a) { @@ -4358,7 +4378,7 @@ $jscomp.polyfill("Object.values", function(v) { e.colorToString = function(a) { return "rgba(" + Math.round(255 * a[0]).toFixed() + "," + Math.round(255 * a[1]).toFixed() + "," + Math.round(255 * a[2]).toFixed() + "," + (4 == a.length ? a[3].toFixed(2) : "1.0") + ")"; }; - e.isInsideRectangle = y; + e.isInsideRectangle = v; e.growBounding = function(a, b, d) { b < a[0] ? a[0] = b : b > a[2] && (a[2] = b); d < a[1] ? a[1] = d : d > a[3] && (a[3] = d); @@ -4366,87 +4386,87 @@ $jscomp.polyfill("Object.values", function(v) { e.isInsideBounding = function(a, b) { return a[0] < b[0][0] || a[1] < b[0][1] || a[0] > b[1][0] || a[1] > b[1][1] ? !1 : !0; }; - e.overlapBounding = x; + e.overlapBounding = G; e.hex2num = function(a) { "#" == a.charAt(0) && (a = a.slice(1)); a = a.toUpperCase(); - for (var b = Array(3), d = 0, c, f, e = 0; 6 > e; e += 2) { - c = "0123456789ABCDEF".indexOf(a.charAt(e)), f = "0123456789ABCDEF".indexOf(a.charAt(e + 1)), b[d] = 16 * c + f, d++; + for (var b = Array(3), d = 0, f, c, h = 0; 6 > h; h += 2) { + f = "0123456789ABCDEF".indexOf(a.charAt(h)), c = "0123456789ABCDEF".indexOf(a.charAt(h + 1)), b[d] = 16 * f + c, d++; } return b; }; e.num2hex = function(a) { - for (var b = "#", d, c, f = 0; 3 > f; f++) { - d = a[f] / 16, c = a[f] % 16, b += "0123456789ABCDEF".charAt(d) + "0123456789ABCDEF".charAt(c); + for (var b = "#", d, f, c = 0; 3 > c; c++) { + d = a[c] / 16, f = a[c] % 16, b += "0123456789ABCDEF".charAt(d) + "0123456789ABCDEF".charAt(f); } return b; }; - F.prototype.addItem = function(a, b, d) { - function c(a) { - var b = this.value; - b && b.has_submenu && f.call(this, a); - } + z.prototype.addItem = function(a, b, d) { function f(a) { + var b = this.value; + b && b.has_submenu && c.call(this, a); + } + function c(a) { var b = this.value, f = !0; - e.current_submenu && e.current_submenu.close(a); + h.current_submenu && h.current_submenu.close(a); if (d.callback) { - var c = d.callback.call(this, b, d, a, e, d.node); + var c = d.callback.call(this, b, d, a, h, d.node); !0 === c && (f = !1); } - if (b && (b.callback && !d.ignore_item_callbacks && !0 !== b.disabled && (c = b.callback.call(this, b, d, a, e, d.extra), !0 === c && (f = !1)), b.submenu)) { + if (b && (b.callback && !d.ignore_item_callbacks && !0 !== b.disabled && (c = b.callback.call(this, b, d, a, h, d.extra), !0 === c && (f = !1)), b.submenu)) { if (!b.submenu.options) { throw "ContextMenu submenu needs options"; } - new e.constructor(b.submenu.options, {callback:b.submenu.callback, event:a, parentMenu:e, ignore_item_callbacks:b.submenu.ignore_item_callbacks, title:b.submenu.title, extra:b.submenu.extra, autoopen:d.autoopen}); + new h.constructor(b.submenu.options, {callback:b.submenu.callback, event:a, parentMenu:h, ignore_item_callbacks:b.submenu.ignore_item_callbacks, title:b.submenu.title, extra:b.submenu.extra, autoopen:d.autoopen}); f = !1; } - f && !e.lock && e.close(); + f && !h.lock && h.close(); } - var e = this; + var h = this; d = d || {}; - var k = document.createElement("div"); - k.className = "litemenu-entry submenu"; - var n = !1; + var e = document.createElement("div"); + e.className = "litemenu-entry submenu"; + var m = !1; if (null === b) { - k.classList.add("separator"); + e.classList.add("separator"); } else { - k.innerHTML = b && b.title ? b.title : a; - if (k.value = b) { - b.disabled && (n = !0, k.classList.add("disabled")), (b.submenu || b.has_submenu) && k.classList.add("has_submenu"); + e.innerHTML = b && b.title ? b.title : a; + if (e.value = b) { + b.disabled && (m = !0, e.classList.add("disabled")), (b.submenu || b.has_submenu) && e.classList.add("has_submenu"); } - "function" == typeof b ? (k.dataset.value = a, k.onclick_callback = b) : k.dataset.value = b; - b.className && (k.className += " " + b.className); + "function" == typeof b ? (e.dataset.value = a, e.onclick_callback = b) : e.dataset.value = b; + b.className && (e.className += " " + b.className); } - this.root.appendChild(k); - n || k.addEventListener("click", f); - d.autoopen && k.addEventListener("mouseenter", c); - return k; + this.root.appendChild(e); + m || e.addEventListener("click", c); + d.autoopen && e.addEventListener("mouseenter", f); + return e; }; - F.prototype.close = function(a, b) { + z.prototype.close = function(a, b) { this.root.parentNode && this.root.parentNode.removeChild(this.root); - this.parentMenu && !b && (this.parentMenu.lock = !1, this.parentMenu.current_submenu = null, void 0 === a ? this.parentMenu.close() : a && !F.isCursorOverElement(a, this.parentMenu.root) && F.trigger(this.parentMenu.root, "mouseleave", a)); + this.parentMenu && !b && (this.parentMenu.lock = !1, this.parentMenu.current_submenu = null, void 0 === a ? this.parentMenu.close() : a && !z.isCursorOverElement(a, this.parentMenu.root) && z.trigger(this.parentMenu.root, "mouseleave", a)); this.current_submenu && this.current_submenu.close(a, !0); this.root.closing_timer && clearTimeout(this.root.closing_timer); }; - F.trigger = function(a, b, d, c) { - var f = document.createEvent("CustomEvent"); - f.initCustomEvent(b, !0, !0, d); - f.srcElement = c; - a.dispatchEvent ? a.dispatchEvent(f) : a.__events && a.__events.dispatchEvent(f); - return f; + z.trigger = function(a, b, d, f) { + var c = document.createEvent("CustomEvent"); + c.initCustomEvent(b, !0, !0, d); + c.srcElement = f; + a.dispatchEvent ? a.dispatchEvent(c) : a.__events && a.__events.dispatchEvent(c); + return c; }; - F.prototype.getTopMenu = function() { + z.prototype.getTopMenu = function() { return this.options.parentMenu ? this.options.parentMenu.getTopMenu() : this; }; - F.prototype.getFirstEvent = function() { + z.prototype.getFirstEvent = function() { return this.options.parentMenu ? this.options.parentMenu.getFirstEvent() : this.options.event; }; - F.isCursorOverElement = function(a, b) { + z.isCursorOverElement = function(a, b) { var d = a.clientX; a = a.clientY; return (b = b.getBoundingClientRect()) ? a > b.top && a < b.top + b.height && d > b.left && d < b.left + b.width ? !0 : !1 : !1; }; - e.ContextMenu = F; + e.ContextMenu = z; e.closeAllContextMenus = function(a) { a = a || window; a = a.document.querySelectorAll(".litecontextmenu"); @@ -4469,54 +4489,54 @@ $jscomp.polyfill("Object.values", function(v) { } } }; - z.sampleCurve = function(a, b) { + r.sampleCurve = function(a, b) { if (b) { for (var d = 0; d < b.length - 1; ++d) { - var c = b[d], f = b[d + 1]; - if (!(f[0] < a)) { - b = f[0] - c[0]; + var f = b[d], c = b[d + 1]; + if (!(c[0] < a)) { + b = c[0] - f[0]; if (0.00001 > Math.abs(b)) { - return c[1]; + return f[1]; } - a = (a - c[0]) / b; - return c[1] * (1.0 - a) + f[1] * a; + a = (a - f[0]) / b; + return f[1] * (1.0 - a) + c[1] * a; } } return 0; } }; - z.prototype.draw = function(a, b, d, c, f, e) { + r.prototype.draw = function(a, b, d, f, c, h) { if (d = this.points) { this.size = b; - var g = b[0] - 2 * this.margin; + var k = b[0] - 2 * this.margin; b = b[1] - 2 * this.margin; - f = f || "#666"; + c = c || "#666"; a.save(); a.translate(this.margin, this.margin); - c && (a.fillStyle = "#111", a.fillRect(0, 0, g, b), a.fillStyle = "#222", a.fillRect(0.5 * g, 0, 1, b), a.strokeStyle = "#333", a.strokeRect(0, 0, g, b)); - a.strokeStyle = f; - e && (a.globalAlpha = 0.5); + f && (a.fillStyle = "#111", a.fillRect(0, 0, k, b), a.fillStyle = "#222", a.fillRect(0.5 * k, 0, 1, b), a.strokeStyle = "#333", a.strokeRect(0, 0, k, b)); + a.strokeStyle = c; + h && (a.globalAlpha = 0.5); a.beginPath(); - for (c = 0; c < d.length; ++c) { - f = d[c], a.lineTo(f[0] * g, (1.0 - f[1]) * b); + for (f = 0; f < d.length; ++f) { + c = d[f], a.lineTo(c[0] * k, (1.0 - c[1]) * b); } a.stroke(); a.globalAlpha = 1; - if (!e) { - for (c = 0; c < d.length; ++c) { - f = d[c], a.fillStyle = this.selected == c ? "#FFF" : this.nearest == c ? "#DDD" : "#AAA", a.beginPath(), a.arc(f[0] * g, (1.0 - f[1]) * b, 2, 0, 2 * Math.PI), a.fill(); + if (!h) { + for (f = 0; f < d.length; ++f) { + c = d[f], a.fillStyle = this.selected == f ? "#FFF" : this.nearest == f ? "#DDD" : "#AAA", a.beginPath(), a.arc(c[0] * k, (1.0 - c[1]) * b, 2, 0, 2 * Math.PI), a.fill(); } } a.restore(); } }; - z.prototype.onMouseDown = function(a, b) { + r.prototype.onMouseDown = function(a, b) { var d = this.points; if (d && !(0 > a[1])) { - var c = this.size[0] - 2 * this.margin, f = this.size[1] - 2 * this.margin, e = a[0] - this.margin; + var f = this.size[0] - 2 * this.margin, c = this.size[1] - 2 * this.margin, h = a[0] - this.margin; a = a[1] - this.margin; - this.selected = this.getCloserPoint([e, a], 30 / b.ds.scale); - -1 == this.selected && (b = [e / c, 1 - a / f], d.push(b), d.sort(function(a, b) { + this.selected = this.getCloserPoint([h, a], 30 / b.ds.scale); + -1 == this.selected && (b = [h / f, 1 - a / c], d.push(b), d.sort(function(a, b) { return a[0] - b[0]; }), this.selected = d.indexOf(b), this.must_update = !0); if (-1 != this.selected) { @@ -4524,42 +4544,42 @@ $jscomp.polyfill("Object.values", function(v) { } } }; - z.prototype.onMouseMove = function(a, b) { + r.prototype.onMouseMove = function(a, b) { var d = this.points; if (d) { - var c = this.selected; - if (!(0 > c)) { - var f = (a[0] - this.margin) / (this.size[0] - 2 * this.margin), e = (a[1] - this.margin) / (this.size[1] - 2 * this.margin); + var f = this.selected; + if (!(0 > f)) { + var c = (a[0] - this.margin) / (this.size[0] - 2 * this.margin), h = (a[1] - this.margin) / (this.size[1] - 2 * this.margin); this._nearest = this.getCloserPoint([a[0] - this.margin, a[1] - this.margin], 30 / b.ds.scale); - if (b = d[c]) { - var k = 0 == c || c == d.length - 1; - !k && (-10 > a[0] || a[0] > this.size[0] + 10 || -10 > a[1] || a[1] > this.size[1] + 10) ? (d.splice(c, 1), this.selected = -1) : (b[0] = k ? 0 == c ? 0 : 1 : Math.clamp(f, 0, 1), b[1] = 1.0 - Math.clamp(e, 0, 1), d.sort(function(a, b) { + if (b = d[f]) { + var e = 0 == f || f == d.length - 1; + !e && (-10 > a[0] || a[0] > this.size[0] + 10 || -10 > a[1] || a[1] > this.size[1] + 10) ? (d.splice(f, 1), this.selected = -1) : (b[0] = e ? 0 == f ? 0 : 1 : Math.clamp(c, 0, 1), b[1] = 1.0 - Math.clamp(h, 0, 1), d.sort(function(a, b) { return a[0] - b[0]; }), this.selected = d.indexOf(b), this.must_update = !0); } } } }; - z.prototype.onMouseUp = function(a, b) { + r.prototype.onMouseUp = function(a, b) { this.selected = -1; return !1; }; - z.prototype.getCloserPoint = function(a, b) { + r.prototype.getCloserPoint = function(a, b) { var d = this.points; if (!d) { return -1; } b = b || 30; - for (var c = this.size[0] - 2 * this.margin, f = this.size[1] - 2 * this.margin, e = d.length, k = [0, 0], n = 1000000, p = -1, h = 0; h < e; ++h) { - var l = d[h]; - k[0] = l[0] * c; - k[1] = (1.0 - l[1]) * f; - l = vec2.distance(a, k); - l > n || l > b || (p = h, n = l); + for (var f = this.size[0] - 2 * this.margin, c = this.size[1] - 2 * this.margin, h = d.length, e = [0, 0], m = 1000000, n = -1, g = 0; g < h; ++g) { + var r = d[g]; + e[0] = r[0] * f; + e[1] = (1.0 - r[1]) * c; + r = vec2.distance(a, e); + r > m || r > b || (n = g, m = r); } - return p; + return n; }; - e.CurveEditor = z; + e.CurveEditor = r; e.getParameterNames = function(a) { return (a + "").replace(/[/][/].*$/gm, "").replace(/\s+/g, "").replace(/[/][*][^/*]*[*][/]/g, "").split("){", 1)[0].replace(/^[^(]*[(]/, "").replace(/=[^,]+/g, "").split(",").filter(Boolean); }; @@ -4571,16 +4591,16 @@ $jscomp.polyfill("Object.values", function(v) { }); })(this); "undefined" != typeof exports && (exports.LiteGraph = this.LiteGraph); -(function(v) { +(function(w) { function c() { this.addOutput("in ms", "number"); this.addOutput("in sec", "number"); } - function r() { + function t() { this.size = [140, 80]; this.properties = {enabled:!0}; this.enabled = !0; - this.subgraph = new g.LGraph; + this.subgraph = new d.LGraph; this.subgraph._subgraph_node = this; this.subgraph._is_subgraph = !0; this.subgraph.onTrigger = this.onSubgraphTrigger.bind(this); @@ -4593,7 +4613,7 @@ $jscomp.polyfill("Object.values", function(v) { this.subgraph.onOutputTypeChanged = this.onSubgraphTypeChangeOutput.bind(this); this.subgraph.onOutputRemoved = this.onSubgraphRemovedOutput.bind(this); } - function m() { + function l() { this.addOutput("", "number"); this.name_in_graph = ""; this.properties = {name:"", type:"number", value:0}; @@ -4610,7 +4630,7 @@ $jscomp.polyfill("Object.values", function(v) { this.widgets_up = !0; this.size = [180, 90]; } - function h() { + function p() { this.addInput("", ""); this.name_in_graph = ""; this.properties = {}; @@ -4624,7 +4644,7 @@ $jscomp.polyfill("Object.values", function(v) { return a.inputs[0].type; }, set:function(b) { if ("action" == b || "event" == b) { - b = g.ACTION; + b = d.ACTION; } a.inputs[0].type = b; a.name_in_graph && a.graph.changeOutputType(a.name_in_graph, a.inputs[0].type); @@ -4642,21 +4662,21 @@ $jscomp.polyfill("Object.values", function(v) { this.widgets_up = !0; this.size = [180, 30]; } - function l() { + function g() { this.addOutput("", "boolean"); this.addProperty("value", !0); this.widget = this.addWidget("toggle", "value", !0, "value"); this.widgets_up = !0; this.size = [140, 30]; } - function A() { + function B() { this.addOutput("", "string"); this.addProperty("value", ""); this.widget = this.addWidget("text", "value", "", "value"); this.widgets_up = !0; this.size = [180, 30]; } - function y() { + function A() { this.addInput("url", ""); this.addOutput("", ""); this.addProperty("url", ""); @@ -4664,7 +4684,7 @@ $jscomp.polyfill("Object.values", function(v) { this.widget = this.addWidget("text", "url", "", "url"); this._data = null; } - function x() { + function v() { this.addOutput("", ""); this.addProperty("value", ""); this.widget = this.addWidget("text", "json", "", "value"); @@ -4672,7 +4692,7 @@ $jscomp.polyfill("Object.values", function(v) { this.size = [140, 30]; this._value = null; } - function F() { + function G() { this.addInput("", ""); this.addOutput("", "array"); this.addOutput("length", "number"); @@ -4688,7 +4708,7 @@ $jscomp.polyfill("Object.values", function(v) { this.addOutput("value", ""); this.addProperty("index", 0); } - function e() { + function r() { this.addInput("table", "table"); this.addInput("row", "number"); this.addInput("col", "number"); @@ -4696,7 +4716,7 @@ $jscomp.polyfill("Object.values", function(v) { this.addProperty("row", 0); this.addProperty("column", 0); } - function B() { + function e() { this.addInput("obj", ""); this.addOutput("", ""); this.addProperty("value", ""); @@ -4705,7 +4725,7 @@ $jscomp.polyfill("Object.values", function(v) { this.size = [140, 30]; this._value = null; } - function E() { + function J() { this.addInput("obj", ""); this.addOutput("keys", "array"); this.size = [140, 30]; @@ -4721,17 +4741,17 @@ $jscomp.polyfill("Object.values", function(v) { }); this.size = this.computeSize(); } - function H() { + function C() { this.size = [60, 30]; this.addInput("in"); this.addOutput("out"); this.properties = {varname:"myname", global:!1}; this.value = null; } - function n() { + function m() { this.size = [60, 30]; this.addInput("data", 0); - this.addInput("download", g.ACTION); + this.addInput("download", d.ACTION); this.properties = {filename:"data.json"}; this.value = null; var a = this; @@ -4739,27 +4759,27 @@ $jscomp.polyfill("Object.values", function(v) { a.value && a.downloadAsFile(); }); } - function p() { + function n() { this.size = [60, 30]; this.addInput("value", 0, {label:""}); this.value = 0; } - function k() { + function h() { this.addInput("in", 0); this.addOutput("out", 0); this.size = [40, 30]; } - function a() { - this.mode = g.ON_EVENT; + function E() { + this.mode = d.ON_EVENT; this.size = [80, 30]; this.addProperty("msg", ""); - this.addInput("log", g.EVENT); + this.addInput("log", d.EVENT); this.addInput("msg", 0); } - function b() { - this.mode = g.ON_EVENT; + function a() { + this.mode = d.ON_EVENT; this.addProperty("msg", ""); - this.addInput("", g.EVENT); + this.addInput("", d.EVENT); var a = this; this.widget = this.addWidget("text", "Text", "", function(b) { a.properties.msg = b; @@ -4767,7 +4787,7 @@ $jscomp.polyfill("Object.values", function(v) { this.widgets_up = !0; this.size = [200, 30]; } - function d() { + function b() { this.size = [60, 30]; this.addProperty("onExecute", "return A;"); this.addInput("A", ""); @@ -4776,51 +4796,51 @@ $jscomp.polyfill("Object.values", function(v) { this._func = null; this.data = {}; } - var g = v.LiteGraph; + var d = w.LiteGraph; c.title = "Time"; c.desc = "Time"; c.prototype.onExecute = function() { this.setOutputData(0, 1000 * this.graph.globaltime); this.setOutputData(1, this.graph.globaltime); }; - g.registerNodeType("basic/time", c); - r.title = "Subgraph"; - r.desc = "Graph inside a node"; - r.title_color = "#334"; - r.prototype.onGetInputs = function() { + d.registerNodeType("basic/time", c); + t.title = "Subgraph"; + t.desc = "Graph inside a node"; + t.title_color = "#334"; + t.prototype.onGetInputs = function() { return [["enabled", "boolean"]]; }; - r.prototype.onDrawTitle = function(a) { + t.prototype.onDrawTitle = function(a) { if (!this.flags.collapsed) { a.fillStyle = "#555"; - var b = g.NODE_TITLE_HEIGHT, d = this.size[0] - b; - a.fillRect(d, -b, b, b); + var b = d.NODE_TITLE_HEIGHT, f = this.size[0] - b; + a.fillRect(f, -b, b, b); a.fillStyle = "#333"; a.beginPath(); - a.moveTo(d + 0.2 * b, 0.6 * -b); - a.lineTo(d + 0.8 * b, 0.6 * -b); - a.lineTo(d + 0.5 * b, 0.3 * -b); + a.moveTo(f + 0.2 * b, 0.6 * -b); + a.lineTo(f + 0.8 * b, 0.6 * -b); + a.lineTo(f + 0.5 * b, 0.3 * -b); a.fill(); } }; - r.prototype.onDblClick = function(a, b, d) { + t.prototype.onDblClick = function(a, b, d) { var f = this; setTimeout(function() { d.openSubgraph(f.subgraph); }, 10); }; - r.prototype.onMouseDown = function(a, b, d) { - if (!this.flags.collapsed && b[0] > this.size[0] - g.NODE_TITLE_HEIGHT && 0 > b[1]) { + t.prototype.onMouseDown = function(a, b, c) { + if (!this.flags.collapsed && b[0] > this.size[0] - d.NODE_TITLE_HEIGHT && 0 > b[1]) { var f = this; setTimeout(function() { - d.openSubgraph(f.subgraph); + c.openSubgraph(f.subgraph); }, 10); } }; - r.prototype.onAction = function(a, b) { + t.prototype.onAction = function(a, b) { this.subgraph.onAction(a, b); }; - r.prototype.onExecute = function() { + t.prototype.onExecute = function() { if (this.enabled = this.getInputOrProperty("enabled")) { if (this.inputs) { for (var a = 0; a < this.inputs.length; a++) { @@ -4836,66 +4856,66 @@ $jscomp.polyfill("Object.values", function(v) { } } }; - r.prototype.sendEventToAllNodes = function(a, b, d) { + t.prototype.sendEventToAllNodes = function(a, b, d) { this.enabled && this.subgraph.sendEventToAllNodes(a, b, d); }; - r.prototype.onSubgraphTrigger = function(a, b) { + t.prototype.onSubgraphTrigger = function(a, b) { a = this.findOutputSlot(a); -1 != a && this.triggerSlot(a); }; - r.prototype.onSubgraphNewInput = function(a, b) { + t.prototype.onSubgraphNewInput = function(a, b) { -1 == this.findInputSlot(a) && this.addInput(a, b); }; - r.prototype.onSubgraphRenamedInput = function(a, b) { + t.prototype.onSubgraphRenamedInput = function(a, b) { a = this.findInputSlot(a); -1 != a && (this.getInputInfo(a).name = b); }; - r.prototype.onSubgraphTypeChangeInput = function(a, b) { + t.prototype.onSubgraphTypeChangeInput = function(a, b) { a = this.findInputSlot(a); -1 != a && (this.getInputInfo(a).type = b); }; - r.prototype.onSubgraphRemovedInput = function(a) { + t.prototype.onSubgraphRemovedInput = function(a) { a = this.findInputSlot(a); -1 != a && this.removeInput(a); }; - r.prototype.onSubgraphNewOutput = function(a, b) { + t.prototype.onSubgraphNewOutput = function(a, b) { -1 == this.findOutputSlot(a) && this.addOutput(a, b); }; - r.prototype.onSubgraphRenamedOutput = function(a, b) { + t.prototype.onSubgraphRenamedOutput = function(a, b) { a = this.findOutputSlot(a); -1 != a && (this.getOutputInfo(a).name = b); }; - r.prototype.onSubgraphTypeChangeOutput = function(a, b) { + t.prototype.onSubgraphTypeChangeOutput = function(a, b) { a = this.findOutputSlot(a); -1 != a && (this.getOutputInfo(a).type = b); }; - r.prototype.onSubgraphRemovedOutput = function(a) { + t.prototype.onSubgraphRemovedOutput = function(a) { a = this.findInputSlot(a); -1 != a && this.removeOutput(a); }; - r.prototype.getExtraMenuOptions = function(a) { + t.prototype.getExtraMenuOptions = function(a) { var b = this; return [{content:"Open", callback:function() { a.openSubgraph(b.subgraph); }}]; }; - r.prototype.onResize = function(a) { + t.prototype.onResize = function(a) { a[1] += 20; }; - r.prototype.serialize = function() { - var a = g.LGraphNode.prototype.serialize.call(this); + t.prototype.serialize = function() { + var a = d.LGraphNode.prototype.serialize.call(this); a.subgraph = this.subgraph.serialize(); return a; }; - r.prototype.clone = function() { - var a = g.createNode(this.type), b = this.serialize(); + t.prototype.clone = function() { + var a = d.createNode(this.type), b = this.serialize(); delete b.id; delete b.inputs; delete b.outputs; a.configure(b); return a; }; - r.prototype.buildFromNodes = function(a) { + t.prototype.buildFromNodes = function(a) { for (var b = {}, d = 0; d < a.length; ++d) { var c = a[d]; b[c.id] = c; @@ -4904,38 +4924,38 @@ $jscomp.polyfill("Object.values", function(v) { c = a[d]; if (c.inputs) { for (var f = 0; f < c.inputs.length; ++f) { - var e = c.inputs[f]; - if (e && e.link) { - var k = c.graph.links[e.link]; - k && (b[k.origin_id] || this.subgraph.addInput(e.name, k.type)); + var h = c.inputs[f]; + if (h && h.link) { + var e = c.graph.links[h.link]; + e && (b[e.origin_id] || this.subgraph.addInput(h.name, e.type)); } } } if (c.outputs) { for (f = 0; f < c.outputs.length; ++f) { - if ((e = c.outputs[f]) && e.links && e.links.length) { - for (var g = 0; g < e.links.length && (!(k = c.graph.links[e.links[g]]) || b[k.target_id]); ++g) { + if ((h = c.outputs[f]) && h.links && h.links.length) { + for (var m = 0; m < h.links.length && (!(e = c.graph.links[h.links[m]]) || b[e.target_id]); ++m) { } } } } } }; - g.Subgraph = r; - g.registerNodeType("graph/subgraph", r); - m.title = "Input"; - m.desc = "Input of the graph"; - m.prototype.onConfigure = function() { + d.Subgraph = t; + d.registerNodeType("graph/subgraph", t); + l.title = "Input"; + l.desc = "Input of the graph"; + l.prototype.onConfigure = function() { this.updateType(); }; - m.prototype.updateType = function() { + l.prototype.updateType = function() { var a = this.properties.type; this.type_widget.value = a; this.outputs[0].type != a && (this.outputs[0].type = a, this.disconnectOutput(0)); "number" == a ? (this.value_widget.type = "number", this.value_widget.value = 0) : "boolean" == a ? (this.value_widget.type = "toggle", this.value_widget.value = !0) : "string" == a ? (this.value_widget.type = "text", this.value_widget.value = "") : (this.value_widget.type = null, this.value_widget.value = null); this.properties.value = this.value_widget.value; }; - m.prototype.onPropertyChanged = function(a, b) { + l.prototype.onPropertyChanged = function(a, b) { if ("name" == a) { if ("" == b || b == this.name_in_graph || "enabled" == b) { return !1; @@ -4946,38 +4966,38 @@ $jscomp.polyfill("Object.values", function(v) { "type" == a && this.updateType(b || ""); } }; - m.prototype.getTitle = function() { + l.prototype.getTitle = function() { return this.flags.collapsed ? this.properties.name : this.title; }; - m.prototype.onAction = function(a, b) { - this.properties.type == g.EVENT && this.triggerSlot(0, b); + l.prototype.onAction = function(a, b) { + this.properties.type == d.EVENT && this.triggerSlot(0, b); }; - m.prototype.onExecute = function() { + l.prototype.onExecute = function() { var a = this.graph.inputs[this.properties.name]; a ? this.setOutputData(0, void 0 !== a.value ? a.value : this.properties.value) : this.setOutputData(0, this.properties.value); }; - m.prototype.onRemoved = function() { + l.prototype.onRemoved = function() { this.name_in_graph && this.graph.removeInput(this.name_in_graph); }; - g.GraphInput = m; - g.registerNodeType("graph/input", m); - h.title = "Output"; - h.desc = "Output of the graph"; - h.prototype.onExecute = function() { + d.GraphInput = l; + d.registerNodeType("graph/input", l); + p.title = "Output"; + p.desc = "Output of the graph"; + p.prototype.onExecute = function() { this._value = this.getInputData(0); this.graph.setOutputData(this.properties.name, this._value); }; - h.prototype.onAction = function(a, b) { - this.properties.type == g.ACTION && this.graph.trigger(this.properties.name, b); + p.prototype.onAction = function(a, b) { + this.properties.type == d.ACTION && this.graph.trigger(this.properties.name, b); }; - h.prototype.onRemoved = function() { + p.prototype.onRemoved = function() { this.name_in_graph && this.graph.removeOutput(this.name_in_graph); }; - h.prototype.getTitle = function() { + p.prototype.getTitle = function() { return this.flags.collapsed ? this.properties.name : this.title; }; - g.GraphOutput = h; - g.registerNodeType("graph/output", h); + d.GraphOutput = p; + d.registerNodeType("graph/output", p); q.title = "Const Number"; q.desc = "Constant number"; q.prototype.onExecute = function() { @@ -4992,51 +5012,51 @@ $jscomp.polyfill("Object.values", function(v) { q.prototype.onDrawBackground = function(a) { this.outputs[0].label = this.properties.value.toFixed(3); }; - g.registerNodeType("basic/const", q); - l.title = "Const Boolean"; - l.desc = "Constant boolean"; - l.prototype.getTitle = q.prototype.getTitle; - l.prototype.onExecute = function() { + d.registerNodeType("basic/const", q); + g.title = "Const Boolean"; + g.desc = "Constant boolean"; + g.prototype.getTitle = q.prototype.getTitle; + g.prototype.onExecute = function() { this.setOutputData(0, this.properties.value); }; - l.prototype.setValue = q.prototype.setValue; - l.prototype.onGetInputs = function() { - return [["toggle", g.ACTION]]; + g.prototype.setValue = q.prototype.setValue; + g.prototype.onGetInputs = function() { + return [["toggle", d.ACTION]]; }; - l.prototype.onAction = function(a) { + g.prototype.onAction = function(a) { this.setValue(!this.properties.value); }; - g.registerNodeType("basic/boolean", l); - A.title = "Const String"; - A.desc = "Constant string"; - A.prototype.getTitle = q.prototype.getTitle; - A.prototype.onExecute = function() { + d.registerNodeType("basic/boolean", g); + B.title = "Const String"; + B.desc = "Constant string"; + B.prototype.getTitle = q.prototype.getTitle; + B.prototype.onExecute = function() { this.setOutputData(0, this.properties.value); }; - A.prototype.setValue = q.prototype.setValue; - A.prototype.onDropFile = function(a) { + B.prototype.setValue = q.prototype.setValue; + B.prototype.onDropFile = function(a) { var b = this, d = new FileReader; d.onload = function(a) { b.setProperty("value", a.target.result); }; d.readAsText(a); }; - g.registerNodeType("basic/string", A); - y.title = "Const File"; - y.desc = "Fetches a file from an url"; - y["@type"] = {type:"enum", values:["text", "arraybuffer", "blob", "json"]}; - y.prototype.onPropertyChanged = function(a, b) { + d.registerNodeType("basic/string", B); + A.title = "Const File"; + A.desc = "Fetches a file from an url"; + A["@type"] = {type:"enum", values:["text", "arraybuffer", "blob", "json"]}; + A.prototype.onPropertyChanged = function(a, b) { "url" == a && (null == b || "" == b ? this._data = null : this.fetchFile(b)); }; - y.prototype.onExecute = function() { + A.prototype.onExecute = function() { var a = this.getInputData(0) || this.properties.url; !a || a == this._url && this._type == this.properties.type || this.fetchFile(a); this.setOutputData(0, this._data); }; - y.prototype.setValue = q.prototype.setValue; - y.prototype.fetchFile = function(a) { + A.prototype.setValue = q.prototype.setValue; + A.prototype.fetchFile = function(a) { var b = this; - a && a.constructor === String ? (this._url = a, this._type = this.properties.type, "http" == a.substr(0, 4) && g.proxy && (a = g.proxy + a.substr(a.indexOf(":") + 3)), fetch(a).then(function(a) { + a && a.constructor === String ? (this._url = a, this._type = this.properties.type, "http" == a.substr(0, 4) && d.proxy && (a = d.proxy + a.substr(a.indexOf(":") + 3)), fetch(a).then(function(a) { if (!a.ok) { throw Error("File not found"); } @@ -5061,7 +5081,7 @@ $jscomp.polyfill("Object.values", function(v) { console.error("error fetching file:", a); })) : (b._data = null, b.boxcolor = null); }; - y.prototype.onDropFile = function(a) { + A.prototype.onDropFile = function(a) { var b = this; this._url = a.name; this._type = this.properties.type; @@ -5085,37 +5105,37 @@ $jscomp.polyfill("Object.values", function(v) { } } }; - g.registerNodeType("basic/file", y); - x.title = "Const Data"; - x.desc = "Constant Data"; - x.prototype.onPropertyChanged = function(a, b) { + d.registerNodeType("basic/file", A); + v.title = "Const Data"; + v.desc = "Constant Data"; + v.prototype.onPropertyChanged = function(a, b) { this.widget.value = b; if (null != b && "" != b) { try { this._value = JSON.parse(b), this.boxcolor = "#AEA"; - } catch (w) { + } catch (I) { this.boxcolor = "red"; } } }; - x.prototype.onExecute = function() { + v.prototype.onExecute = function() { this.setOutputData(0, this._value); }; - x.prototype.setValue = q.prototype.setValue; - g.registerNodeType("basic/data", x); - F.title = "Const Array"; - F.desc = "Constant Array"; - F.prototype.onPropertyChanged = function(a, b) { + v.prototype.setValue = q.prototype.setValue; + d.registerNodeType("basic/data", v); + G.title = "Const Array"; + G.desc = "Constant Array"; + G.prototype.onPropertyChanged = function(a, b) { this.widget.value = b; if (null != b && "" != b) { try { this._value = "[" != b[0] ? JSON.parse("[" + b + "]") : JSON.parse(b), this.boxcolor = "#AEA"; - } catch (w) { + } catch (I) { this.boxcolor = "red"; } } }; - F.prototype.onExecute = function() { + G.prototype.onExecute = function() { var a = this.getInputData(0); if (a && a.length) { this._value || (this._value = []); @@ -5127,8 +5147,8 @@ $jscomp.polyfill("Object.values", function(v) { this.setOutputData(0, this._value); this.setOutputData(1, this._value ? this._value.length || 0 : 0); }; - F.prototype.setValue = q.prototype.setValue; - g.registerNodeType("basic/array", F); + G.prototype.setValue = q.prototype.setValue; + d.registerNodeType("basic/array", G); z.title = "Array[i]"; z.desc = "Returns an element from an array"; z.prototype.onExecute = function() { @@ -5136,40 +5156,40 @@ $jscomp.polyfill("Object.values", function(v) { null == b && (b = this.properties.index); null != a && null != b && this.setOutputData(0, a[Math.floor(Number(b))]); }; - g.registerNodeType("basic/array[]", z); - e.title = "Table[row][col]"; - e.desc = "Returns an element from a table"; - e.prototype.onExecute = function() { + d.registerNodeType("basic/array[]", z); + r.title = "Table[row][col]"; + r.desc = "Returns an element from a table"; + r.prototype.onExecute = function() { var a = this.getInputData(0), b = this.getInputData(1), d = this.getInputData(2); null == b && (b = this.properties.row); null == d && (d = this.properties.column); null != a && null != b && null != d && ((b = a[Math.floor(Number(b))]) ? this.setOutputData(0, b[Math.floor(Number(d))]) : this.setOutputData(0, null)); }; - g.registerNodeType("basic/table[][]", e); - B.title = "Object property"; - B.desc = "Outputs the property of an object"; - B.prototype.setValue = function(a) { + d.registerNodeType("basic/table[][]", r); + e.title = "Object property"; + e.desc = "Outputs the property of an object"; + e.prototype.setValue = function(a) { this.properties.value = a; this.widget.value = a; }; - B.prototype.getTitle = function() { + e.prototype.getTitle = function() { return this.flags.collapsed ? "in." + this.properties.value : this.title; }; - B.prototype.onPropertyChanged = function(a, b) { + e.prototype.onPropertyChanged = function(a, b) { this.widget.value = b; }; - B.prototype.onExecute = function() { + e.prototype.onExecute = function() { var a = this.getInputData(0); null != a && this.setOutputData(0, a[this.properties.value]); }; - g.registerNodeType("basic/object_property", B); - E.title = "Object keys"; - E.desc = "Outputs an array with the keys of an object"; - E.prototype.onExecute = function() { + d.registerNodeType("basic/object_property", e); + J.title = "Object keys"; + J.desc = "Outputs an array with the keys of an object"; + J.prototype.onExecute = function() { var a = this.getInputData(0); null != a && this.setOutputData(0, Object.keys(a)); }; - g.registerNodeType("basic/object_keys", E); + d.registerNodeType("basic/object_keys", J); u.title = "Merge Objects"; u.desc = "Creates an object copying properties from others"; u.prototype.onExecute = function() { @@ -5186,25 +5206,25 @@ $jscomp.polyfill("Object.values", function(v) { } this.setOutputData(0, d); }; - g.registerNodeType("basic/merge_objects", u); - H.title = "Variable"; - H.desc = "store/read variable value"; - H.prototype.onExecute = function() { + d.registerNodeType("basic/merge_objects", u); + C.title = "Variable"; + C.desc = "store/read variable value"; + C.prototype.onExecute = function() { this.value = this.getInputData(0); this.graph && (this.graph.vars[this.properties.varname] = this.value); - this.properties.global && (v[this.properties.varname] = this.value); + this.properties.global && (w[this.properties.varname] = this.value); this.setOutputData(0, this.value); }; - H.prototype.getTitle = function() { + C.prototype.getTitle = function() { return this.properties.varname; }; - g.registerNodeType("basic/variable", H); - g.wrapFunctionAsNode("basic/length", function(a) { + d.registerNodeType("basic/variable", C); + d.wrapFunctionAsNode("basic/length", function(a) { return a && null != a.length ? Number(a.length) : 0; }, [""], "number"); - n.title = "Download"; - n.desc = "Download some data"; - n.prototype.downloadAsFile = function() { + m.title = "Download"; + m.desc = "Download some data"; + m.prototype.downloadAsFile = function() { if (null != this.value) { var a = null; a = this.value.constructor === String ? this.value : JSON.stringify(this.value); @@ -5222,28 +5242,28 @@ $jscomp.polyfill("Object.values", function(v) { }, 6E4); } }; - n.prototype.onAction = function(a, b) { + m.prototype.onAction = function(a, b) { var d = this; setTimeout(function() { d.downloadAsFile(); }, 100); }; + m.prototype.onExecute = function() { + this.inputs[0] && (this.value = this.getInputData(0)); + }; + m.prototype.getTitle = function() { + return this.flags.collapsed ? this.properties.filename : this.title; + }; + d.registerNodeType("basic/download", m); + n.title = "Watch"; + n.desc = "Show value of input"; n.prototype.onExecute = function() { this.inputs[0] && (this.value = this.getInputData(0)); }; n.prototype.getTitle = function() { - return this.flags.collapsed ? this.properties.filename : this.title; - }; - g.registerNodeType("basic/download", n); - p.title = "Watch"; - p.desc = "Show value of input"; - p.prototype.onExecute = function() { - this.inputs[0] && (this.value = this.getInputData(0)); - }; - p.prototype.getTitle = function() { return this.flags.collapsed ? this.inputs[0].label : this.title; }; - p.toString = function(a) { + n.toString = function(a) { if (null == a) { return "null"; } @@ -5252,59 +5272,59 @@ $jscomp.polyfill("Object.values", function(v) { } if (a.constructor === Array) { for (var b = "[", d = 0; d < a.length; ++d) { - b += p.toString(a[d]) + (d + 1 != a.length ? "," : ""); + b += n.toString(a[d]) + (d + 1 != a.length ? "," : ""); } return b + "]"; } return String(a); }; - p.prototype.onDrawBackground = function(a) { - this.inputs[0].label = p.toString(this.value); + n.prototype.onDrawBackground = function(a) { + this.inputs[0].label = n.toString(this.value); }; - g.registerNodeType("basic/watch", p); - k.title = "Cast"; - k.desc = "Allows to connect different types"; - k.prototype.onExecute = function() { + d.registerNodeType("basic/watch", n); + h.title = "Cast"; + h.desc = "Allows to connect different types"; + h.prototype.onExecute = function() { this.setOutputData(0, this.getInputData(0)); }; - g.registerNodeType("basic/cast", k); - a.title = "Console"; - a.desc = "Show value inside the console"; - a.prototype.onAction = function(a, b) { + d.registerNodeType("basic/cast", h); + E.title = "Console"; + E.desc = "Show value inside the console"; + E.prototype.onAction = function(a, b) { "log" == a ? console.log(b) : "warn" == a ? console.warn(b) : "error" == a && console.error(b); }; - a.prototype.onExecute = function() { + E.prototype.onExecute = function() { var a = this.getInputData(1); null !== a && (this.properties.msg = a); console.log(a); }; - a.prototype.onGetInputs = function() { - return [["log", g.ACTION], ["warn", g.ACTION], ["error", g.ACTION]]; + E.prototype.onGetInputs = function() { + return [["log", d.ACTION], ["warn", d.ACTION], ["error", d.ACTION]]; }; - g.registerNodeType("basic/console", a); - b.title = "Alert"; - b.desc = "Show an alert window"; - b.color = "#510"; - b.prototype.onConfigure = function(a) { + d.registerNodeType("basic/console", E); + a.title = "Alert"; + a.desc = "Show an alert window"; + a.color = "#510"; + a.prototype.onConfigure = function(a) { this.widget.value = a.properties.msg; }; - b.prototype.onAction = function(a, b) { + a.prototype.onAction = function(a, b) { var d = this.properties.msg; setTimeout(function() { alert(d); }, 10); }; - g.registerNodeType("basic/alert", b); - d.prototype.onConfigure = function(a) { - a.properties.onExecute && g.allow_scripts ? this.compileCode(a.properties.onExecute) : console.warn("Script not compiled, LiteGraph.allow_scripts is false"); + d.registerNodeType("basic/alert", a); + b.prototype.onConfigure = function(a) { + a.properties.onExecute && d.allow_scripts ? this.compileCode(a.properties.onExecute) : console.warn("Script not compiled, LiteGraph.allow_scripts is false"); }; - d.title = "Script"; - d.desc = "executes a code (max 100 characters)"; - d.widgets_info = {onExecute:{type:"code"}}; - d.prototype.onPropertyChanged = function(a, b) { - "onExecute" == a && g.allow_scripts ? this.compileCode(b) : console.warn("Script not compiled, LiteGraph.allow_scripts is false"); + b.title = "Script"; + b.desc = "executes a code (max 100 characters)"; + b.widgets_info = {onExecute:{type:"code"}}; + b.prototype.onPropertyChanged = function(a, b) { + "onExecute" == a && d.allow_scripts ? this.compileCode(b) : console.warn("Script not compiled, LiteGraph.allow_scripts is false"); }; - d.prototype.compileCode = function(a) { + b.prototype.compileCode = function(a) { this._func = null; if (256 < a.length) { console.warn("Script too long, max 256 chars"); @@ -5317,88 +5337,88 @@ $jscomp.polyfill("Object.values", function(v) { } try { this._func = new Function("A", "B", "C", "DATA", "node", a); - } catch (K) { - console.error("Error parsing script"), console.error(K); + } catch (L) { + console.error("Error parsing script"), console.error(L); } } }; - d.prototype.onExecute = function() { + b.prototype.onExecute = function() { if (this._func) { try { var a = this.getInputData(0), b = this.getInputData(1), d = this.getInputData(2); this.setOutputData(0, this._func(a, b, d, this.data, this)); - } catch (I) { - console.error("Error in script"), console.error(I); + } catch (F) { + console.error("Error in script"), console.error(F); } } }; - d.prototype.onGetOutputs = function() { + b.prototype.onGetOutputs = function() { return [["C", ""]]; }; - g.registerNodeType("basic/script", d); + d.registerNodeType("basic/script", b); })(this); -(function(v) { +(function(w) { function c() { this.size = [60, 30]; - this.addInput("event", x.ACTION); + this.addInput("event", v.ACTION); } - function r() { + function t() { this.size = [60, 30]; this.addInput("if", ""); - this.addOutput("true", x.EVENT); - this.addOutput("change", x.EVENT); - this.addOutput("false", x.EVENT); + this.addOutput("true", v.EVENT); + this.addOutput("change", v.EVENT); + this.addOutput("false", v.EVENT); this.properties = {only_on_change:!0}; this.prev = 0; } - function m() { - this.addInput("", x.ACTION); - this.addInput("", x.ACTION); - this.addInput("", x.ACTION); - this.addInput("", x.ACTION); - this.addInput("", x.ACTION); - this.addInput("", x.ACTION); - this.addOutput("", x.EVENT); - this.addOutput("", x.EVENT); - this.addOutput("", x.EVENT); - this.addOutput("", x.EVENT); - this.addOutput("", x.EVENT); - this.addOutput("", x.EVENT); + function l() { + this.addInput("", v.ACTION); + this.addInput("", v.ACTION); + this.addInput("", v.ACTION); + this.addInput("", v.ACTION); + this.addInput("", v.ACTION); + this.addInput("", v.ACTION); + this.addOutput("", v.EVENT); + this.addOutput("", v.EVENT); + this.addOutput("", v.EVENT); + this.addOutput("", v.EVENT); + this.addOutput("", v.EVENT); + this.addOutput("", v.EVENT); this.size = [120, 30]; this.flags = {horizontal:!0, render_box:!1}; } - function h() { + function p() { this.size = [60, 30]; - this.addInput("event", x.ACTION); - this.addOutput("event", x.EVENT); + this.addInput("event", v.ACTION); + this.addOutput("event", v.EVENT); this.properties = {equal_to:"", has_property:"", property_equal_to:""}; } function q() { - this.addInput("inc", x.ACTION); - this.addInput("dec", x.ACTION); - this.addInput("reset", x.ACTION); - this.addOutput("change", x.EVENT); + this.addInput("inc", v.ACTION); + this.addInput("dec", v.ACTION); + this.addInput("reset", v.ACTION); + this.addOutput("change", v.EVENT); this.addOutput("num", "number"); this.num = 0; } - function l() { + function g() { this.size = [60, 30]; this.addProperty("time_in_ms", 1000); - this.addInput("event", x.ACTION); - this.addOutput("on_time", x.EVENT); + this.addInput("event", v.ACTION); + this.addOutput("on_time", v.EVENT); this._pending = []; } - function A() { + function B() { this.addProperty("interval", 1000); this.addProperty("event", "tick"); - this.addOutput("on_tick", x.EVENT); + this.addOutput("on_tick", v.EVENT); this.time = 0; this.last_interval = 1000; this.triggered = !1; } - function y() { + function A() { this.addInput("data", ""); - this.addInput("assign", x.ACTION); + this.addInput("assign", v.ACTION); this.addOutput("data", ""); this._last_value = null; this.properties = {data:null, serialize:!0}; @@ -5407,59 +5427,59 @@ $jscomp.polyfill("Object.values", function(v) { c.properties.data = c._last_value; }); } - var x = v.LiteGraph; + var v = w.LiteGraph; c.title = "Log Event"; c.desc = "Log event in console"; - c.prototype.onAction = function(c, h) { - console.log(c, h); + c.prototype.onAction = function(c, g) { + console.log(c, g); }; - x.registerNodeType("events/log", c); - r.title = "TriggerEvent"; - r.desc = "Triggers event if input evaluates to true"; - r.prototype.onExecute = function(c, h) { + v.registerNodeType("events/log", c); + t.title = "TriggerEvent"; + t.desc = "Triggers event if input evaluates to true"; + t.prototype.onExecute = function(c, g) { c = this.getInputData(0); - var e = c != this.prev; - 0 === this.prev && (e = !1); - var l = e && this.properties.only_on_change || !e && !this.properties.only_on_change; - c && l && this.triggerSlot(0, h); - !c && l && this.triggerSlot(2, h); - e && this.triggerSlot(1, h); + var r = c != this.prev; + 0 === this.prev && (r = !1); + var e = r && this.properties.only_on_change || !r && !this.properties.only_on_change; + c && e && this.triggerSlot(0, g); + !c && e && this.triggerSlot(2, g); + r && this.triggerSlot(1, g); this.prev = c; }; - x.registerNodeType("events/trigger", r); - m.title = "Sequencer"; - m.desc = "Trigger events when an event arrives"; - m.prototype.getTitle = function() { + v.registerNodeType("events/trigger", t); + l.title = "Sequencer"; + l.desc = "Trigger events when an event arrives"; + l.prototype.getTitle = function() { return ""; }; - m.prototype.onAction = function(c, h) { + l.prototype.onAction = function(c, g) { if (this.outputs) { for (c = 0; c < this.outputs.length; ++c) { - this.triggerSlot(c, h); + this.triggerSlot(c, g); } } }; - x.registerNodeType("events/sequencer", m); - h.title = "Filter Event"; - h.desc = "Blocks events that do not match the filter"; - h.prototype.onAction = function(c, h) { - if (null != h && (!this.properties.equal_to || this.properties.equal_to == h)) { - if (this.properties.has_property && (c = h[this.properties.has_property], null == c || this.properties.property_equal_to && this.properties.property_equal_to != c)) { + v.registerNodeType("events/sequencer", l); + p.title = "Filter Event"; + p.desc = "Blocks events that do not match the filter"; + p.prototype.onAction = function(c, g) { + if (null != g && (!this.properties.equal_to || this.properties.equal_to == g)) { + if (this.properties.has_property && (c = g[this.properties.has_property], null == c || this.properties.property_equal_to && this.properties.property_equal_to != c)) { return; } - this.triggerSlot(0, h); + this.triggerSlot(0, g); } }; - x.registerNodeType("events/filter", h); + v.registerNodeType("events/filter", p); q.title = "Counter"; q.desc = "Counts events"; q.prototype.getTitle = function() { return this.flags.collapsed ? String(this.num) : this.title; }; - q.prototype.onAction = function(c, h) { - h = this.num; + q.prototype.onAction = function(c, g) { + g = this.num; "inc" == c ? this.num += 1 : "dec" == c ? --this.num : "reset" == c && (this.num = 0); - this.num != h && this.trigger("change", this.num); + this.num != g && this.trigger("change", this.num); }; q.prototype.onDrawBackground = function(c) { this.flags.collapsed || (c.fillStyle = "#AAA", c.font = "20px Arial", c.textAlign = "center", c.fillText(this.num, 0.5 * this.size[0], 0.5 * this.size[1])); @@ -5467,68 +5487,68 @@ $jscomp.polyfill("Object.values", function(v) { q.prototype.onExecute = function() { this.setOutputData(1, this.num); }; - x.registerNodeType("events/counter", q); - l.title = "Delay"; - l.desc = "Delays one event"; - l.prototype.onAction = function(c, h) { + v.registerNodeType("events/counter", q); + g.title = "Delay"; + g.desc = "Delays one event"; + g.prototype.onAction = function(c, g) { c = this.properties.time_in_ms; - 0 >= c ? this.trigger(null, h) : this._pending.push([c, h]); + 0 >= c ? this.trigger(null, g) : this._pending.push([c, g]); }; - l.prototype.onExecute = function() { + g.prototype.onExecute = function() { var c = 1000 * this.graph.elapsed_time; this.isInputConnected(1) && (this.properties.time_in_ms = this.getInputData(1)); - for (var h = 0; h < this._pending.length; ++h) { - var e = this._pending[h]; - e[0] -= c; - 0 < e[0] || (this._pending.splice(h, 1), --h, this.trigger(null, e[1])); + for (var g = 0; g < this._pending.length; ++g) { + var r = this._pending[g]; + r[0] -= c; + 0 < r[0] || (this._pending.splice(g, 1), --g, this.trigger(null, r[1])); } }; - l.prototype.onGetInputs = function() { - return [["event", x.ACTION], ["time_in_ms", "number"]]; + g.prototype.onGetInputs = function() { + return [["event", v.ACTION], ["time_in_ms", "number"]]; }; - x.registerNodeType("events/delay", l); - A.title = "Timer"; - A.desc = "Sends an event every N milliseconds"; - A.prototype.onStart = function() { + v.registerNodeType("events/delay", g); + B.title = "Timer"; + B.desc = "Sends an event every N milliseconds"; + B.prototype.onStart = function() { this.time = 0; }; - A.prototype.getTitle = function() { + B.prototype.getTitle = function() { return "Timer: " + this.last_interval.toString() + "ms"; }; - A.on_color = "#AAA"; - A.off_color = "#222"; - A.prototype.onDrawBackground = function() { - this.boxcolor = this.triggered ? A.on_color : A.off_color; + B.on_color = "#AAA"; + B.off_color = "#222"; + B.prototype.onDrawBackground = function() { + this.boxcolor = this.triggered ? B.on_color : B.off_color; this.triggered = !1; }; - A.prototype.onExecute = function() { + B.prototype.onExecute = function() { var c = 0 == this.time; this.time += 1000 * this.graph.elapsed_time; this.last_interval = Math.max(1, this.getInputOrProperty("interval") | 0); !c && (this.time < this.last_interval || isNaN(this.last_interval)) ? this.inputs && 1 < this.inputs.length && this.inputs[1] && this.setOutputData(1, !1) : (this.triggered = !0, this.time %= this.last_interval, this.trigger("on_tick", this.properties.event), this.inputs && 1 < this.inputs.length && this.inputs[1] && this.setOutputData(1, !0)); }; - A.prototype.onGetInputs = function() { + B.prototype.onGetInputs = function() { return [["interval", "number"]]; }; - A.prototype.onGetOutputs = function() { + B.prototype.onGetOutputs = function() { return [["tick", "boolean"]]; }; - x.registerNodeType("events/timer", A); - y.title = "Data Store"; - y.desc = "Stores data and only changes when event is received"; - y.prototype.onExecute = function() { + v.registerNodeType("events/timer", B); + A.title = "Data Store"; + A.desc = "Stores data and only changes when event is received"; + A.prototype.onExecute = function() { this._last_value = this.getInputData(0); this.setOutputData(0, this.properties.data); }; - y.prototype.onAction = function(c, h) { + A.prototype.onAction = function(c, g) { this.properties.data = this._last_value; }; - y.prototype.onSerialize = function(c) { + A.prototype.onSerialize = function(c) { null != c.data && (0 == this.properties.serialize || c.data.constructor !== String && c.data.constructor !== Number && c.data.constructor !== Boolean && c.data.constructor !== Array && c.data.constructor !== Object) && (c.data = null); }; - x.registerNodeType("basic/data_store", y); + v.registerNodeType("basic/data_store", A); })(this); -(function(v) { +(function(w) { function c() { this.addOutput("", z.EVENT); this.addOutput("", "boolean"); @@ -5538,7 +5558,7 @@ $jscomp.polyfill("Object.values", function(v) { this.size = [164, 84]; this.clicked = !1; } - function r() { + function t() { this.addInput("", "boolean"); this.addInput("e", z.ACTION); this.addOutput("v", "boolean"); @@ -5546,7 +5566,7 @@ $jscomp.polyfill("Object.values", function(v) { this.properties = {font:"", value:!1}; this.size = [160, 44]; } - function m() { + function l() { this.addOutput("", "number"); this.size = [80, 60]; this.properties = {min:-1000, max:1000, value:1, step:1}; @@ -5554,7 +5574,7 @@ $jscomp.polyfill("Object.values", function(v) { this._precision = this._remainder = 0; this.mouse_captured = !1; } - function h() { + function p() { this.addOutput("", "string"); this.addOutput("change", z.EVENT); this.size = [80, 60]; @@ -5575,7 +5595,7 @@ $jscomp.polyfill("Object.values", function(v) { this.properties = {min:0, max:1, value:0.5, color:"#7AF", precision:2}; this.value = -1; } - function l() { + function g() { this.addOutput("", "number"); this.properties = {value:0.5, min:0, max:1, text:"V"}; var c = this; @@ -5585,41 +5605,41 @@ $jscomp.polyfill("Object.values", function(v) { }, this.properties); this.widgets_up = !0; } - function A() { + function B() { this.size = [160, 26]; this.addOutput("", "number"); this.properties = {color:"#7AF", min:0, max:1, value:0.5}; this.value = -1; } - function y() { + function A() { this.size = [160, 26]; this.addInput("", "number"); this.properties = {min:0, max:1, value:0, color:"#AAF"}; } - function x() { + function v() { this.addInputs("", 0); this.properties = {value:"...", font:"Arial", fontsize:18, color:"#AAA", align:"left", glowSize:0, decimals:1}; } - function F() { + function G() { this.size = [200, 100]; this.properties = {borderColor:"#ffffff", bgcolorTop:"#f0f0f0", bgcolorBottom:"#e0e0e0", shadowSize:2, borderRadius:3}; } - var z = v.LiteGraph; + var z = w.LiteGraph; c.title = "Button"; c.desc = "Triggers an event"; c.font = "Arial"; - c.prototype.onDrawForeground = function(e) { - if (!this.flags.collapsed && (e.fillStyle = "black", e.fillRect(11, 11, this.size[0] - 20, this.size[1] - 20), e.fillStyle = "#AAF", e.fillRect(9, 9, this.size[0] - 20, this.size[1] - 20), e.fillStyle = this.clicked ? "white" : this.mouseOver ? "#668" : "#334", e.fillRect(10, 10, this.size[0] - 20, this.size[1] - 20), this.properties.text || 0 === this.properties.text)) { - var h = this.properties.font_size || 30; - e.textAlign = "center"; - e.fillStyle = this.clicked ? "black" : "white"; - e.font = h + "px " + c.font; - e.fillText(this.properties.text, 0.5 * this.size[0], 0.5 * this.size[1] + 0.3 * h); - e.textAlign = "left"; + c.prototype.onDrawForeground = function(g) { + if (!this.flags.collapsed && (g.fillStyle = "black", g.fillRect(11, 11, this.size[0] - 20, this.size[1] - 20), g.fillStyle = "#AAF", g.fillRect(9, 9, this.size[0] - 20, this.size[1] - 20), g.fillStyle = this.clicked ? "white" : this.mouseOver ? "#668" : "#334", g.fillRect(10, 10, this.size[0] - 20, this.size[1] - 20), this.properties.text || 0 === this.properties.text)) { + var e = this.properties.font_size || 30; + g.textAlign = "center"; + g.fillStyle = this.clicked ? "black" : "white"; + g.font = e + "px " + c.font; + g.fillText(this.properties.text, 0.5 * this.size[0], 0.5 * this.size[1] + 0.3 * e); + g.textAlign = "left"; } }; - c.prototype.onMouseDown = function(c, h) { - if (1 < h[0] && 1 < h[1] && h[0] < this.size[0] - 2 && h[1] < this.size[1] - 2) { + c.prototype.onMouseDown = function(c, e) { + if (1 < e[0] && 1 < e[1] && e[0] < this.size[0] - 2 && e[1] < this.size[1] - 2) { return this.clicked = !0, this.triggerSlot(0, this.properties.message), !0; } }; @@ -5630,64 +5650,64 @@ $jscomp.polyfill("Object.values", function(v) { this.clicked = !1; }; z.registerNodeType("widget/button", c); - r.title = "Toggle"; - r.desc = "Toggles between true or false"; - r.prototype.onDrawForeground = function(c) { + t.title = "Toggle"; + t.desc = "Toggles between true or false"; + t.prototype.onDrawForeground = function(c) { if (!this.flags.collapsed) { - var e = 0.5 * this.size[1], h = 0.8 * this.size[1]; + var e = 0.5 * this.size[1], g = 0.8 * this.size[1]; c.font = this.properties.font || (0.8 * e).toFixed(0) + "px Arial"; - var l = c.measureText(this.title).width; - l = 0.5 * (this.size[0] - (l + e)); + var r = c.measureText(this.title).width; + r = 0.5 * (this.size[0] - (r + e)); c.fillStyle = "#AAA"; - c.fillRect(l, h - e, e, e); + c.fillRect(r, g - e, e, e); c.fillStyle = this.properties.value ? "#AEF" : "#000"; - c.fillRect(l + 0.25 * e, h - e + 0.25 * e, .5 * e, .5 * e); + c.fillRect(r + 0.25 * e, g - e + 0.25 * e, .5 * e, .5 * e); c.textAlign = "left"; c.fillStyle = "#AAA"; - c.fillText(this.title, 1.2 * e + l, 0.85 * h); + c.fillText(this.title, 1.2 * e + r, 0.85 * g); c.textAlign = "left"; } }; - r.prototype.onAction = function(c) { + t.prototype.onAction = function(c) { this.properties.value = !this.properties.value; this.trigger("e", this.properties.value); }; - r.prototype.onExecute = function() { + t.prototype.onExecute = function() { var c = this.getInputData(0); null != c && (this.properties.value = c); this.setOutputData(0, this.properties.value); }; - r.prototype.onMouseDown = function(c, h) { - if (1 < h[0] && 1 < h[1] && h[0] < this.size[0] - 2 && h[1] < this.size[1] - 2) { + t.prototype.onMouseDown = function(c, e) { + if (1 < e[0] && 1 < e[1] && e[0] < this.size[0] - 2 && e[1] < this.size[1] - 2) { return this.properties.value = !this.properties.value, this.graph._version++, this.trigger("e", this.properties.value), !0; } }; - z.registerNodeType("widget/toggle", r); - m.title = "Number"; - m.desc = "Widget to select number value"; - m.pixels_threshold = 10; - m.markers_color = "#666"; - m.prototype.onDrawForeground = function(c) { - var e = 0.5 * this.size[0], h = this.size[1]; - 30 < h ? (c.fillStyle = m.markers_color, c.beginPath(), c.moveTo(e, 0.1 * h), c.lineTo(e + 0.1 * h, 0.2 * h), c.lineTo(e + -0.1 * h, 0.2 * h), c.fill(), c.beginPath(), c.moveTo(e, 0.9 * h), c.lineTo(e + 0.1 * h, 0.8 * h), c.lineTo(e + -0.1 * h, 0.8 * h), c.fill(), c.font = (0.7 * h).toFixed(1) + "px Arial") : c.font = (0.8 * h).toFixed(1) + "px Arial"; + z.registerNodeType("widget/toggle", t); + l.title = "Number"; + l.desc = "Widget to select number value"; + l.pixels_threshold = 10; + l.markers_color = "#666"; + l.prototype.onDrawForeground = function(c) { + var e = 0.5 * this.size[0], g = this.size[1]; + 30 < g ? (c.fillStyle = l.markers_color, c.beginPath(), c.moveTo(e, 0.1 * g), c.lineTo(e + 0.1 * g, 0.2 * g), c.lineTo(e + -0.1 * g, 0.2 * g), c.fill(), c.beginPath(), c.moveTo(e, 0.9 * g), c.lineTo(e + 0.1 * g, 0.8 * g), c.lineTo(e + -0.1 * g, 0.8 * g), c.fill(), c.font = (0.7 * g).toFixed(1) + "px Arial") : c.font = (0.8 * g).toFixed(1) + "px Arial"; c.textAlign = "center"; - c.font = (0.7 * h).toFixed(1) + "px Arial"; + c.font = (0.7 * g).toFixed(1) + "px Arial"; c.fillStyle = "#EEE"; - c.fillText(this.properties.value.toFixed(this._precision), e, 0.75 * h); + c.fillText(this.properties.value.toFixed(this._precision), e, 0.75 * g); }; - m.prototype.onExecute = function() { + l.prototype.onExecute = function() { this.setOutputData(0, this.properties.value); }; - m.prototype.onPropertyChanged = function(c, h) { + l.prototype.onPropertyChanged = function(c, e) { c = (this.properties.step + "").split("."); this._precision = 1 < c.length ? c[1].length : 0; }; - m.prototype.onMouseDown = function(c, h) { - if (!(0 > h[1])) { + l.prototype.onMouseDown = function(c, e) { + if (!(0 > e[1])) { return this.old_y = c.canvasY, this.captureInput(!0), this.mouse_captured = !0; } }; - m.prototype.onMouseMove = function(c) { + l.prototype.onMouseMove = function(c) { if (this.mouse_captured) { var e = this.old_y - c.canvasY; c.shiftKey && (e *= 10); @@ -5695,7 +5715,7 @@ $jscomp.polyfill("Object.values", function(v) { e *= 0.1; } this.old_y = c.canvasY; - c = this._remainder + e / m.pixels_threshold; + c = this._remainder + e / l.pixels_threshold; this._remainder = c % 1; c = Math.clamp(this.properties.value + (c | 0) * this.properties.step, this.properties.min, this.properties.max); this.properties.value = c; @@ -5703,30 +5723,30 @@ $jscomp.polyfill("Object.values", function(v) { this.setDirtyCanvas(!0); } }; - m.prototype.onMouseUp = function(c, h) { - 200 > c.click_time && (this.properties.value = Math.clamp(this.properties.value + (h[1] > 0.5 * this.size[1] ? -1 : 1) * this.properties.step, this.properties.min, this.properties.max), this.graph._version++, this.setDirtyCanvas(!0)); + l.prototype.onMouseUp = function(c, e) { + 200 > c.click_time && (this.properties.value = Math.clamp(this.properties.value + (e[1] > 0.5 * this.size[1] ? -1 : 1) * this.properties.step, this.properties.min, this.properties.max), this.graph._version++, this.setDirtyCanvas(!0)); this.mouse_captured && (this.mouse_captured = !1, this.captureInput(!1)); }; - z.registerNodeType("widget/number", m); - h.title = "Combo"; - h.desc = "Widget to select from a list"; - h.prototype.onExecute = function() { + z.registerNodeType("widget/number", l); + p.title = "Combo"; + p.desc = "Widget to select from a list"; + p.prototype.onExecute = function() { this.setOutputData(0, this.properties.value); }; - h.prototype.onPropertyChanged = function(c, h) { - "values" == c ? (this._values = h.split(";"), this.widget.options.values = this._values) : "value" == c && (this.widget.value = h); + p.prototype.onPropertyChanged = function(c, e) { + "values" == c ? (this._values = e.split(";"), this.widget.options.values = this._values) : "value" == c && (this.widget.value = e); }; - z.registerNodeType("widget/combo", h); + z.registerNodeType("widget/combo", p); q.title = "Knob"; q.desc = "Circular controller"; q.size = [80, 100]; q.prototype.onDrawForeground = function(c) { if (!this.flags.collapsed) { -1 == this.value && (this.value = (this.properties.value - this.properties.min) / (this.properties.max - this.properties.min)); - var e = 0.5 * this.size[0], h = 0.5 * this.size[1], l = 0.5 * Math.min(this.size[0], this.size[1]) - 5; + var e = 0.5 * this.size[0], g = 0.5 * this.size[1], l = 0.5 * Math.min(this.size[0], this.size[1]) - 5; c.globalAlpha = 1; c.save(); - c.translate(e, h); + c.translate(e, g); c.rotate(0.75 * Math.PI); c.fillStyle = "rgba(0,0,0,0.5)"; c.beginPath(); @@ -5746,17 +5766,17 @@ $jscomp.polyfill("Object.values", function(v) { c.restore(); c.fillStyle = "black"; c.beginPath(); - c.arc(e, h, 0.75 * l, 0, 2 * Math.PI, !0); + c.arc(e, g, 0.75 * l, 0, 2 * Math.PI, !0); c.fill(); c.fillStyle = this.mouseOver ? "white" : this.properties.color; c.beginPath(); - var m = this.value * Math.PI * 1.5 + 0.75 * Math.PI; - c.arc(e + Math.cos(m) * l * 0.65, h + Math.sin(m) * l * 0.65, 0.05 * l, 0, 2 * Math.PI, !0); + var p = this.value * Math.PI * 1.5 + 0.75 * Math.PI; + c.arc(e + Math.cos(p) * l * 0.65, g + Math.sin(p) * l * 0.65, 0.05 * l, 0, 2 * Math.PI, !0); c.fill(); c.fillStyle = this.mouseOver ? "white" : "#AAA"; c.font = Math.floor(0.5 * l) + "px Arial"; c.textAlign = "center"; - c.fillText(this.properties.value.toFixed(this.properties.precision), e, h + 0.15 * l); + c.fillText(this.properties.value.toFixed(this.properties.precision), e, g + 0.15 * l); } }; q.prototype.onExecute = function() { @@ -5788,23 +5808,23 @@ $jscomp.polyfill("Object.values", function(v) { q.prototype.onMouseUp = function(c) { this.oldmouse && (this.oldmouse = null, this.captureInput(!1)); }; - q.prototype.onPropertyChanged = function(c, h) { + q.prototype.onPropertyChanged = function(c, e) { if ("min" == c || "max" == c || "value" == c) { - return this.properties[c] = parseFloat(h), !0; + return this.properties[c] = parseFloat(e), !0; } }; z.registerNodeType("widget/knob", q); - l.title = "Inner Slider"; - l.prototype.onPropertyChanged = function(c, h) { - "value" == c && (this.slider.value = h); + g.title = "Inner Slider"; + g.prototype.onPropertyChanged = function(c, e) { + "value" == c && (this.slider.value = e); }; - l.prototype.onExecute = function() { + g.prototype.onExecute = function() { this.setOutputData(0, this.properties.value); }; - z.registerNodeType("widget/internal_slider", l); - A.title = "H.Slider"; - A.desc = "Linear slider controller"; - A.prototype.onDrawForeground = function(c) { + z.registerNodeType("widget/internal_slider", g); + B.title = "H.Slider"; + B.desc = "Linear slider controller"; + B.prototype.onDrawForeground = function(c) { -1 == this.value && (this.value = (this.properties.value - this.properties.min) / (this.properties.max - this.properties.min)); c.globalAlpha = 1; c.lineWidth = 1; @@ -5815,12 +5835,12 @@ $jscomp.polyfill("Object.values", function(v) { c.rect(4, 4, (this.size[0] - 8) * this.value, this.size[1] - 8); c.fill(); }; - A.prototype.onExecute = function() { + B.prototype.onExecute = function() { this.properties.value = this.properties.min + (this.properties.max - this.properties.min) * this.value; this.setOutputData(0, this.properties.value); this.boxcolor = z.colorToString([this.value, this.value, this.value]); }; - A.prototype.onMouseDown = function(c) { + B.prototype.onMouseDown = function(c) { if (0 > c.canvasY - this.pos[1]) { return !1; } @@ -5828,7 +5848,7 @@ $jscomp.polyfill("Object.values", function(v) { this.captureInput(!0); return !0; }; - A.prototype.onMouseMove = function(c) { + B.prototype.onMouseMove = function(c) { if (this.oldmouse) { c = [c.canvasX - this.pos[0], c.canvasY - this.pos[1]]; var e = this.value; @@ -5839,20 +5859,20 @@ $jscomp.polyfill("Object.values", function(v) { this.setDirtyCanvas(!0); } }; - A.prototype.onMouseUp = function(c) { + B.prototype.onMouseUp = function(c) { this.oldmouse = null; this.captureInput(!1); }; - A.prototype.onMouseLeave = function(c) { + B.prototype.onMouseLeave = function(c) { }; - z.registerNodeType("widget/hslider", A); - y.title = "Progress"; - y.desc = "Shows data in linear progress"; - y.prototype.onExecute = function() { + z.registerNodeType("widget/hslider", B); + A.title = "Progress"; + A.desc = "Shows data in linear progress"; + A.prototype.onExecute = function() { var c = this.getInputData(0); void 0 != c && (this.properties.value = c); }; - y.prototype.onDrawForeground = function(c) { + A.prototype.onDrawForeground = function(c) { c.lineWidth = 1; c.fillStyle = this.properties.color; var e = (this.properties.value - this.properties.min) / (this.properties.max - this.properties.min); @@ -5860,69 +5880,69 @@ $jscomp.polyfill("Object.values", function(v) { e = Math.max(0, e); c.fillRect(2, 2, (this.size[0] - 4) * e, this.size[1] - 4); }; - z.registerNodeType("widget/progress", y); - x.title = "Text"; - x.desc = "Shows the input value"; - x.widgets = [{name:"resize", text:"Resize box", type:"button"}, {name:"led_text", text:"LED", type:"minibutton"}, {name:"normal_text", text:"Normal", type:"minibutton"}]; - x.prototype.onDrawForeground = function(c) { + z.registerNodeType("widget/progress", A); + v.title = "Text"; + v.desc = "Shows the input value"; + v.widgets = [{name:"resize", text:"Resize box", type:"button"}, {name:"led_text", text:"LED", type:"minibutton"}, {name:"normal_text", text:"Normal", type:"minibutton"}]; + v.prototype.onDrawForeground = function(c) { c.fillStyle = this.properties.color; var e = this.properties.value; this.properties.glowSize ? (c.shadowColor = this.properties.color, c.shadowOffsetX = 0, c.shadowOffsetY = 0, c.shadowBlur = this.properties.glowSize) : c.shadowColor = "transparent"; - var h = this.properties.fontsize; + var g = this.properties.fontsize; c.textAlign = this.properties.align; - c.font = h.toString() + "px " + this.properties.font; + c.font = g.toString() + "px " + this.properties.font; this.str = "number" == typeof e ? e.toFixed(this.properties.decimals) : e; if ("string" == typeof this.str) { e = this.str.split("\\n"); for (var l in e) { - c.fillText(e[l], "left" == this.properties.align ? 15 : this.size[0] - 15, -0.15 * h + h * (parseInt(l) + 1)); + c.fillText(e[l], "left" == this.properties.align ? 15 : this.size[0] - 15, -0.15 * g + g * (parseInt(l) + 1)); } } c.shadowColor = "transparent"; this.last_ctx = c; c.textAlign = "left"; }; - x.prototype.onExecute = function() { + v.prototype.onExecute = function() { var c = this.getInputData(0); null != c && (this.properties.value = c); }; - x.prototype.resize = function() { + v.prototype.resize = function() { if (this.last_ctx) { var c = this.str.split("\\n"); this.last_ctx.font = this.properties.fontsize + "px " + this.properties.font; - var h = 0, l; - for (l in c) { - var m = this.last_ctx.measureText(c[l]).width; - h < m && (h = m); + var e = 0, g; + for (g in c) { + var l = this.last_ctx.measureText(c[g]).width; + e < l && (e = l); } - this.size[0] = h + 20; + this.size[0] = e + 20; this.size[1] = 4 + c.length * this.properties.fontsize; this.setDirtyCanvas(!0); } }; - x.prototype.onPropertyChanged = function(c, h) { - this.properties[c] = h; - this.str = "number" == typeof h ? h.toFixed(3) : h; + v.prototype.onPropertyChanged = function(c, e) { + this.properties[c] = e; + this.str = "number" == typeof e ? e.toFixed(3) : e; return !0; }; - z.registerNodeType("widget/text", x); - F.title = "Panel"; - F.desc = "Non interactive panel"; - F.widgets = [{name:"update", text:"Update", type:"button"}]; - F.prototype.createGradient = function(c) { + z.registerNodeType("widget/text", v); + G.title = "Panel"; + G.desc = "Non interactive panel"; + G.widgets = [{name:"update", text:"Update", type:"button"}]; + G.prototype.createGradient = function(c) { "" == this.properties.bgcolorTop || "" == this.properties.bgcolorBottom ? this.lineargradient = 0 : (this.lineargradient = c.createLinearGradient(0, 0, 0, this.size[1]), this.lineargradient.addColorStop(0, this.properties.bgcolorTop), this.lineargradient.addColorStop(1, this.properties.bgcolorBottom)); }; - F.prototype.onDrawForeground = function(c) { + G.prototype.onDrawForeground = function(c) { this.flags.collapsed || (null == this.lineargradient && this.createGradient(c), this.lineargradient && (c.lineWidth = 1, c.strokeStyle = this.properties.borderColor, c.fillStyle = this.lineargradient, this.properties.shadowSize ? (c.shadowColor = "#000", c.shadowOffsetX = 0, c.shadowOffsetY = 0, c.shadowBlur = this.properties.shadowSize) : c.shadowColor = "transparent", c.roundRect(0, 0, this.size[0] - 1, this.size[1] - 1, this.properties.shadowSize), c.fill(), c.shadowColor = "transparent", c.stroke())); }; - z.registerNodeType("widget/panel", F); + z.registerNodeType("widget/panel", G); })(this); -(function(v) { +(function(w) { function c() { this.addOutput("left_x_axis", "number"); this.addOutput("left_y_axis", "number"); - this.addOutput("button_pressed", r.EVENT); + this.addOutput("button_pressed", t.EVENT); this.properties = {gamepad_index:0, threshold:0.1}; this._left_axis = new Float32Array(2); this._right_axis = new Float32Array(2); @@ -5930,7 +5950,7 @@ $jscomp.polyfill("Object.values", function(v) { this._previous_buttons = new Uint8Array(17); this._current_buttons = new Uint8Array(17); } - var r = v.LiteGraph; + var t = w.LiteGraph; c.title = "Gamepad"; c.desc = "gets the input of the gamepad"; c.CENTER = 0; @@ -5941,87 +5961,87 @@ $jscomp.polyfill("Object.values", function(v) { c.zero = new Float32Array(2); c.buttons = "a b x y lb rb lt rt back start ls rs home".split(" "); c.prototype.onExecute = function() { - var m = this.getGamepad(), h = this.properties.threshold || 0.0; - m && (this._left_axis[0] = Math.abs(m.xbox.axes.lx) > h ? m.xbox.axes.lx : 0, this._left_axis[1] = Math.abs(m.xbox.axes.ly) > h ? m.xbox.axes.ly : 0, this._right_axis[0] = Math.abs(m.xbox.axes.rx) > h ? m.xbox.axes.rx : 0, this._right_axis[1] = Math.abs(m.xbox.axes.ry) > h ? m.xbox.axes.ry : 0, this._triggers[0] = Math.abs(m.xbox.axes.ltrigger) > h ? m.xbox.axes.ltrigger : 0, this._triggers[1] = Math.abs(m.xbox.axes.rtrigger) > h ? m.xbox.axes.rtrigger : 0); + var l = this.getGamepad(), p = this.properties.threshold || 0.0; + l && (this._left_axis[0] = Math.abs(l.xbox.axes.lx) > p ? l.xbox.axes.lx : 0, this._left_axis[1] = Math.abs(l.xbox.axes.ly) > p ? l.xbox.axes.ly : 0, this._right_axis[0] = Math.abs(l.xbox.axes.rx) > p ? l.xbox.axes.rx : 0, this._right_axis[1] = Math.abs(l.xbox.axes.ry) > p ? l.xbox.axes.ry : 0, this._triggers[0] = Math.abs(l.xbox.axes.ltrigger) > p ? l.xbox.axes.ltrigger : 0, this._triggers[1] = Math.abs(l.xbox.axes.rtrigger) > p ? l.xbox.axes.rtrigger : 0); if (this.outputs) { - for (h = 0; h < this.outputs.length; h++) { - var q = this.outputs[h]; + for (p = 0; p < this.outputs.length; p++) { + var q = this.outputs[p]; if (q.links && q.links.length) { - var l = null; - if (m) { + var g = null; + if (l) { switch(q.name) { case "left_axis": - l = this._left_axis; + g = this._left_axis; break; case "right_axis": - l = this._right_axis; + g = this._right_axis; break; case "left_x_axis": - l = this._left_axis[0]; + g = this._left_axis[0]; break; case "left_y_axis": - l = this._left_axis[1]; + g = this._left_axis[1]; break; case "right_x_axis": - l = this._right_axis[0]; + g = this._right_axis[0]; break; case "right_y_axis": - l = this._right_axis[1]; + g = this._right_axis[1]; break; case "trigger_left": - l = this._triggers[0]; + g = this._triggers[0]; break; case "trigger_right": - l = this._triggers[1]; + g = this._triggers[1]; break; case "a_button": - l = m.xbox.buttons.a ? 1 : 0; + g = l.xbox.buttons.a ? 1 : 0; break; case "b_button": - l = m.xbox.buttons.b ? 1 : 0; + g = l.xbox.buttons.b ? 1 : 0; break; case "x_button": - l = m.xbox.buttons.x ? 1 : 0; + g = l.xbox.buttons.x ? 1 : 0; break; case "y_button": - l = m.xbox.buttons.y ? 1 : 0; + g = l.xbox.buttons.y ? 1 : 0; break; case "lb_button": - l = m.xbox.buttons.lb ? 1 : 0; + g = l.xbox.buttons.lb ? 1 : 0; break; case "rb_button": - l = m.xbox.buttons.rb ? 1 : 0; + g = l.xbox.buttons.rb ? 1 : 0; break; case "ls_button": - l = m.xbox.buttons.ls ? 1 : 0; + g = l.xbox.buttons.ls ? 1 : 0; break; case "rs_button": - l = m.xbox.buttons.rs ? 1 : 0; + g = l.xbox.buttons.rs ? 1 : 0; break; case "hat_left": - l = m.xbox.hatmap & c.LEFT; + g = l.xbox.hatmap & c.LEFT; break; case "hat_right": - l = m.xbox.hatmap & c.RIGHT; + g = l.xbox.hatmap & c.RIGHT; break; case "hat_up": - l = m.xbox.hatmap & c.UP; + g = l.xbox.hatmap & c.UP; break; case "hat_down": - l = m.xbox.hatmap & c.DOWN; + g = l.xbox.hatmap & c.DOWN; break; case "hat": - l = m.xbox.hatmap; + g = l.xbox.hatmap; break; case "start_button": - l = m.xbox.buttons.start ? 1 : 0; + g = l.xbox.buttons.start ? 1 : 0; break; case "back_button": - l = m.xbox.buttons.back ? 1 : 0; + g = l.xbox.buttons.back ? 1 : 0; break; case "button_pressed": for (q = 0; q < this._current_buttons.length; ++q) { - this._current_buttons[q] && !this._previous_buttons[q] && this.triggerSlot(h, c.buttons[q]); + this._current_buttons[q] && !this._previous_buttons[q] && this.triggerSlot(p, c.buttons[q]); } } } else { @@ -6030,13 +6050,13 @@ $jscomp.polyfill("Object.values", function(v) { break; case "left_axis": case "right_axis": - l = c.zero; + g = c.zero; break; default: - l = 0; + g = 0; } } - this.setOutputData(h, l); + this.setOutputData(p, g); } } } @@ -6044,87 +6064,87 @@ $jscomp.polyfill("Object.values", function(v) { c.mapping = {a:0, b:1, x:2, y:3, lb:4, rb:5, lt:6, rt:7, back:8, start:9, ls:10, rs:11}; c.mapping_array = "a b x y lb rb lt rt back start ls rs".split(" "); c.prototype.getGamepad = function() { - var m = navigator.getGamepads || navigator.webkitGetGamepads || navigator.mozGetGamepads; - if (!m) { + var l = navigator.getGamepads || navigator.webkitGetGamepads || navigator.mozGetGamepads; + if (!l) { return null; } - m = m.call(navigator); + l = l.call(navigator); this._previous_buttons.set(this._current_buttons); - for (var h = this.properties.gamepad_index; 4 > h; h++) { - if (m[h]) { - m = m[h]; - h = this.xbox_mapping; - h || (h = this.xbox_mapping = {axes:[], buttons:{}, hat:"", hatmap:c.CENTER}); - h.axes.lx = m.axes[0]; - h.axes.ly = m.axes[1]; - h.axes.rx = m.axes[2]; - h.axes.ry = m.axes[3]; - h.axes.ltrigger = m.buttons[6].value; - h.axes.rtrigger = m.buttons[7].value; - h.hat = ""; - h.hatmap = c.CENTER; - for (var q = 0; q < m.buttons.length; q++) { - if (this._current_buttons[q] = m.buttons[q].pressed, 12 > q) { - h.buttons[c.mapping_array[q]] = m.buttons[q].pressed, m.buttons[q].was_pressed && this.trigger(c.mapping_array[q] + "_button_event"); + for (var p = this.properties.gamepad_index; 4 > p; p++) { + if (l[p]) { + l = l[p]; + p = this.xbox_mapping; + p || (p = this.xbox_mapping = {axes:[], buttons:{}, hat:"", hatmap:c.CENTER}); + p.axes.lx = l.axes[0]; + p.axes.ly = l.axes[1]; + p.axes.rx = l.axes[2]; + p.axes.ry = l.axes[3]; + p.axes.ltrigger = l.buttons[6].value; + p.axes.rtrigger = l.buttons[7].value; + p.hat = ""; + p.hatmap = c.CENTER; + for (var q = 0; q < l.buttons.length; q++) { + if (this._current_buttons[q] = l.buttons[q].pressed, 12 > q) { + p.buttons[c.mapping_array[q]] = l.buttons[q].pressed, l.buttons[q].was_pressed && this.trigger(c.mapping_array[q] + "_button_event"); } else { switch(q) { case 12: - m.buttons[q].pressed && (h.hat += "up", h.hatmap |= c.UP); + l.buttons[q].pressed && (p.hat += "up", p.hatmap |= c.UP); break; case 13: - m.buttons[q].pressed && (h.hat += "down", h.hatmap |= c.DOWN); + l.buttons[q].pressed && (p.hat += "down", p.hatmap |= c.DOWN); break; case 14: - m.buttons[q].pressed && (h.hat += "left", h.hatmap |= c.LEFT); + l.buttons[q].pressed && (p.hat += "left", p.hatmap |= c.LEFT); break; case 15: - m.buttons[q].pressed && (h.hat += "right", h.hatmap |= c.RIGHT); + l.buttons[q].pressed && (p.hat += "right", p.hatmap |= c.RIGHT); break; case 16: - h.buttons.home = m.buttons[q].pressed; + p.buttons.home = l.buttons[q].pressed; } } } - m.xbox = h; - return m; + l.xbox = p; + return l; } } }; c.prototype.onDrawBackground = function(c) { if (!this.flags.collapsed) { - var h = this._left_axis, m = this._right_axis; + var l = this._left_axis, q = this._right_axis; c.strokeStyle = "#88A"; - c.strokeRect(0.5 * (h[0] + 1) * this.size[0] - 4, 0.5 * (h[1] + 1) * this.size[1] - 4, 8, 8); + c.strokeRect(0.5 * (l[0] + 1) * this.size[0] - 4, 0.5 * (l[1] + 1) * this.size[1] - 4, 8, 8); c.strokeStyle = "#8A8"; - c.strokeRect(0.5 * (m[0] + 1) * this.size[0] - 4, 0.5 * (m[1] + 1) * this.size[1] - 4, 8, 8); - h = this.size[1] / this._current_buttons.length; + c.strokeRect(0.5 * (q[0] + 1) * this.size[0] - 4, 0.5 * (q[1] + 1) * this.size[1] - 4, 8, 8); + l = this.size[1] / this._current_buttons.length; c.fillStyle = "#AEB"; - for (m = 0; m < this._current_buttons.length; ++m) { - this._current_buttons[m] && c.fillRect(0, h * m, 6, h); + for (q = 0; q < this._current_buttons.length; ++q) { + this._current_buttons[q] && c.fillRect(0, l * q, 6, l); } } }; c.prototype.onGetOutputs = function() { - return [["left_axis", "vec2"], ["right_axis", "vec2"], ["left_x_axis", "number"], ["left_y_axis", "number"], ["right_x_axis", "number"], ["right_y_axis", "number"], ["trigger_left", "number"], ["trigger_right", "number"], ["a_button", "number"], ["b_button", "number"], ["x_button", "number"], ["y_button", "number"], ["lb_button", "number"], ["rb_button", "number"], ["ls_button", "number"], ["rs_button", "number"], ["start_button", "number"], ["back_button", "number"], ["a_button_event", r.EVENT], - ["b_button_event", r.EVENT], ["x_button_event", r.EVENT], ["y_button_event", r.EVENT], ["lb_button_event", r.EVENT], ["rb_button_event", r.EVENT], ["ls_button_event", r.EVENT], ["rs_button_event", r.EVENT], ["start_button_event", r.EVENT], ["back_button_event", r.EVENT], ["hat_left", "number"], ["hat_right", "number"], ["hat_up", "number"], ["hat_down", "number"], ["hat", "number"], ["button_pressed", r.EVENT]]; + return [["left_axis", "vec2"], ["right_axis", "vec2"], ["left_x_axis", "number"], ["left_y_axis", "number"], ["right_x_axis", "number"], ["right_y_axis", "number"], ["trigger_left", "number"], ["trigger_right", "number"], ["a_button", "number"], ["b_button", "number"], ["x_button", "number"], ["y_button", "number"], ["lb_button", "number"], ["rb_button", "number"], ["ls_button", "number"], ["rs_button", "number"], ["start_button", "number"], ["back_button", "number"], ["a_button_event", t.EVENT], + ["b_button_event", t.EVENT], ["x_button_event", t.EVENT], ["y_button_event", t.EVENT], ["lb_button_event", t.EVENT], ["rb_button_event", t.EVENT], ["ls_button_event", t.EVENT], ["rs_button_event", t.EVENT], ["start_button_event", t.EVENT], ["back_button_event", t.EVENT], ["hat_left", "number"], ["hat_right", "number"], ["hat_up", "number"], ["hat_down", "number"], ["hat", "number"], ["button_pressed", t.EVENT]]; }; - r.registerNodeType("input/gamepad", c); + t.registerNodeType("input/gamepad", c); })(this); -(function(v) { +(function(w) { function c() { this.addInput("in", "*"); this.size = [80, 30]; } - function r() { + function t() { this.addInput("in"); this.addOutput("out"); this.size = [80, 30]; } - function m() { + function l() { this.addInput("in"); this.addOutput("out"); } - function h() { + function p() { this.addInput("in", "number", {locked:!0}); this.addOutput("out", "number", {locked:!0}); this.addOutput("clamped", "number", {locked:!0}); @@ -6141,7 +6161,7 @@ $jscomp.polyfill("Object.values", function(v) { this.addProperty("max", 1); this.size = [80, 30]; } - function l() { + function g() { this.addInput("in", "number"); this.addOutput("out", "number"); this.addProperty("min", 0); @@ -6153,7 +6173,7 @@ $jscomp.polyfill("Object.values", function(v) { this.addProperty("speed", 1); this.size = [90, 30]; } - function A() { + function B() { this.addOutput("out", "number"); this.addProperty("min_time", 1); this.addProperty("max_time", 2); @@ -6161,20 +6181,20 @@ $jscomp.polyfill("Object.values", function(v) { this.size = [90, 30]; this._blink_time = this._remaining_time = 0; } - function y() { + function A() { this.addInput("in", "number"); this.addOutput("out", "number"); this.size = [80, 30]; this.addProperty("min", 0); this.addProperty("max", 1); } - function x() { + function v() { this.properties = {f:0.5}; this.addInput("A", "number"); this.addInput("B", "number"); this.addOutput("out", "number"); } - function F() { + function G() { this.addInput("in", "number"); this.addOutput("out", "number"); this.size = [80, 30]; @@ -6184,18 +6204,18 @@ $jscomp.polyfill("Object.values", function(v) { this.addOutput("out", "number"); this.size = [80, 30]; } - function e() { + function r() { this.addInput("in", "number"); this.addOutput("out", "number"); this.size = [80, 30]; } - function B() { + function e() { this.addInput("in", "number"); this.addOutput("out", "number"); this.size = [80, 30]; this.properties = {A:0, B:1}; } - function E() { + function J() { this.addInput("in", "number", {label:""}); this.addOutput("out", "number", {label:""}); this.size = [80, 30]; @@ -6207,7 +6227,7 @@ $jscomp.polyfill("Object.values", function(v) { this.addInput("B"); this.addOutput("out"); } - function H() { + function C() { this.addInput("in", "number"); this.addOutput("out", "number"); this.size = [80, 30]; @@ -6215,26 +6235,26 @@ $jscomp.polyfill("Object.values", function(v) { this._values = new Float32Array(10); this._current = 0; } - function n() { + function m() { this.addInput("in", "number"); this.addOutput("out", "number"); this.addProperty("factor", 0.1); this.size = [80, 30]; this._value = null; } - function p() { + function n() { this.addInput("A", "number,array,object"); this.addInput("B", "number"); this.addOutput("=", "number"); this.addProperty("A", 1); this.addProperty("B", 1); - this.addProperty("OP", "+", "enum", {values:p.values}); + this.addProperty("OP", "+", "enum", {values:n.values}); this._func = function(a, b) { return a + b; }; this._result = []; } - function k() { + function h() { this.addInput("A", "number"); this.addInput("B", "number"); this.addOutput("A==B", "boolean"); @@ -6242,30 +6262,30 @@ $jscomp.polyfill("Object.values", function(v) { this.addProperty("A", 0); this.addProperty("B", 0); } - function a() { + function E() { this.addInput("A", "number"); this.addInput("B", "number"); this.addOutput("true", "boolean"); this.addOutput("false", "boolean"); this.addProperty("A", 1); this.addProperty("B", 1); - this.addProperty("OP", ">", "enum", {values:a.values}); + this.addProperty("OP", ">", "enum", {values:E.values}); this.size = [80, 60]; } - function b() { + function a() { this.addInput("inc", "number"); this.addOutput("total", "number"); this.addProperty("increment", 1); this.addProperty("value", 0); } - function d() { + function b() { this.addInput("v", "number"); this.addOutput("sin", "number"); this.addProperty("amplitude", 1); this.addProperty("offset", 0); this.bgImageUrl = "nodes/imgs/icon-sin.png"; } - function g() { + function d() { this.addInput("x", "number"); this.addInput("y", "number"); this.addOutput("", "number"); @@ -6283,38 +6303,38 @@ $jscomp.polyfill("Object.values", function(v) { this.addOutput("x", "number"); this.addOutput("y", "number"); } - function C() { + function k() { this.addInputs([["x", "number"], ["y", "number"]]); this.addOutput("vec2", "vec2"); this.properties = {x:0, y:0}; this._data = new Float32Array(2); } - function w() { + function I() { this.addInput("vec3", "vec3"); this.addOutput("x", "number"); this.addOutput("y", "number"); this.addOutput("z", "number"); } - function I() { + function F() { this.addInputs([["x", "number"], ["y", "number"], ["z", "number"]]); this.addOutput("vec3", "vec3"); this.properties = {x:0, y:0, z:0}; this._data = new Float32Array(3); } - function K() { + function L() { this.addInput("vec4", "vec4"); this.addOutput("x", "number"); this.addOutput("y", "number"); this.addOutput("z", "number"); this.addOutput("w", "number"); } - function L() { + function K() { this.addInputs([["x", "number"], ["y", "number"], ["z", "number"], ["w", "number"]]); this.addOutput("vec4", "vec4"); this.properties = {x:0, y:0, z:0, w:0}; this._data = new Float32Array(4); } - var D = v.LiteGraph; + var D = w.LiteGraph; c.title = "Converter"; c.desc = "type A to type B"; c.prototype.onExecute = function() { @@ -6359,26 +6379,26 @@ $jscomp.polyfill("Object.values", function(v) { return [["number", "number"], ["vec2", "vec2"], ["vec3", "vec3"], ["vec4", "vec4"]]; }; D.registerNodeType("math/converter", c); - r.title = "Bypass"; - r.desc = "removes the type"; - r.prototype.onExecute = function() { + t.title = "Bypass"; + t.desc = "removes the type"; + t.prototype.onExecute = function() { var a = this.getInputData(0); this.setOutputData(0, a); }; - D.registerNodeType("math/bypass", r); - m.title = "to Number"; - m.desc = "Cast to number"; - m.prototype.onExecute = function() { + D.registerNodeType("math/bypass", t); + l.title = "to Number"; + l.desc = "Cast to number"; + l.prototype.onExecute = function() { var a = this.getInputData(0); this.setOutputData(0, Number(a)); }; - D.registerNodeType("math/to_number", m); - h.title = "Range"; - h.desc = "Convert a number from one range to another"; - h.prototype.getTitle = function() { + D.registerNodeType("math/to_number", l); + p.title = "Range"; + p.desc = "Convert a number from one range to another"; + p.prototype.getTitle = function() { return this.flags.collapsed ? (this._last_v || 0).toFixed(2) : this.title; }; - h.prototype.onExecute = function() { + p.prototype.onExecute = function() { if (this.inputs) { for (var a = 0; a < this.inputs.length; a++) { var b = this.inputs[a], d = this.getInputData(a); @@ -6396,13 +6416,13 @@ $jscomp.polyfill("Object.values", function(v) { this.setOutputData(0, this._last_v); this.setOutputData(1, Math.clamp(this._last_v, b, c)); }; - h.prototype.onDrawBackground = function(a) { + p.prototype.onDrawBackground = function(a) { this.outputs[0].label = this._last_v ? this._last_v.toFixed(3) : "?"; }; - h.prototype.onGetInputs = function() { + p.prototype.onGetInputs = function() { return [["in_min", "number"], ["in_max", "number"], ["out_min", "number"], ["out_max", "number"]]; }; - D.registerNodeType("math/range", h); + D.registerNodeType("math/range", p); q.title = "Rand"; q.desc = "Random number"; q.prototype.onExecute = function() { @@ -6423,41 +6443,41 @@ $jscomp.polyfill("Object.values", function(v) { return [["min", "number"], ["max", "number"]]; }; D.registerNodeType("math/rand", q); - l.title = "Noise"; - l.desc = "Random number with temporal continuity"; - l.data = null; - l.getValue = function(a, b) { - if (!l.data) { - l.data = new Float32Array(1024); - for (var d = 0; d < l.data.length; ++d) { - l.data[d] = Math.random(); + g.title = "Noise"; + g.desc = "Random number with temporal continuity"; + g.data = null; + g.getValue = function(a, b) { + if (!g.data) { + g.data = new Float32Array(1024); + for (var d = 0; d < g.data.length; ++d) { + g.data[d] = Math.random(); } } a %= 1024; 0 > a && (a += 1024); var c = Math.floor(a); a -= c; - d = l.data[c]; - c = l.data[1023 == c ? 0 : c + 1]; + d = g.data[c]; + c = g.data[1023 == c ? 0 : c + 1]; b && (a = a * a * a * (a * (6.0 * a - 15.0) + 10.0)); return d * (1 - a) + c * a; }; - l.prototype.onExecute = function() { + g.prototype.onExecute = function() { var a = this.getInputData(0) || 0, b = this.properties.octaves || 1, d = 0, c = 1; a += this.properties.seed || 0; - for (var k = this.properties.speed || 1, f = 0, g = 0; g < b && !(d += l.getValue(a * (1 + g) * k, this.properties.smooth) * c, f += c, c *= this.properties.persistence, 0.001 > c); ++g) { + for (var h = this.properties.speed || 1, f = 0, e = 0; e < b && !(d += g.getValue(a * (1 + e) * h, this.properties.smooth) * c, f += c, c *= this.properties.persistence, 0.001 > c); ++e) { } a = this.properties.min; this._last_v = d / f * (this.properties.max - a) + a; this.setOutputData(0, this._last_v); }; - l.prototype.onDrawBackground = function(a) { + g.prototype.onDrawBackground = function(a) { this.outputs[0].label = (this._last_v || 0).toFixed(3); }; - D.registerNodeType("math/noise", l); - A.title = "Spikes"; - A.desc = "spike every random time"; - A.prototype.onExecute = function() { + D.registerNodeType("math/noise", g); + B.title = "Spikes"; + B.desc = "spike every random time"; + B.prototype.onExecute = function() { var a = this.graph.elapsed_time; this._remaining_time -= a; this._blink_time -= a; @@ -6466,22 +6486,22 @@ $jscomp.polyfill("Object.values", function(v) { 0 > this._remaining_time ? (this._remaining_time = Math.random() * (this.properties.max_time - this.properties.min_time) + this.properties.min_time, this._blink_time = this.properties.duration, this.boxcolor = "#FFF") : this.boxcolor = "#000"; this.setOutputData(0, a); }; - D.registerNodeType("math/spikes", A); - y.title = "Clamp"; - y.desc = "Clamp number between min and max"; - y.prototype.onExecute = function() { + D.registerNodeType("math/spikes", B); + A.title = "Clamp"; + A.desc = "Clamp number between min and max"; + A.prototype.onExecute = function() { var a = this.getInputData(0); null != a && (a = Math.max(this.properties.min, a), a = Math.min(this.properties.max, a), this.setOutputData(0, a)); }; - y.prototype.getCode = function(a) { + A.prototype.getCode = function(a) { a = ""; this.isInputConnected(0) && (a += "clamp({{0}}," + this.properties.min + "," + this.properties.max + ")"); return a; }; - D.registerNodeType("math/clamp", y); - x.title = "Lerp"; - x.desc = "Linear Interpolation"; - x.prototype.onExecute = function() { + D.registerNodeType("math/clamp", A); + v.title = "Lerp"; + v.desc = "Linear Interpolation"; + v.prototype.onExecute = function() { var a = this.getInputData(0); null == a && (a = 0); var b = this.getInputData(1); @@ -6490,17 +6510,17 @@ $jscomp.polyfill("Object.values", function(v) { void 0 !== c && (d = c); this.setOutputData(0, a * (1 - d) + b * d); }; - x.prototype.onGetInputs = function() { + v.prototype.onGetInputs = function() { return [["f", "number"]]; }; - D.registerNodeType("math/lerp", x); - F.title = "Abs"; - F.desc = "Absolute"; - F.prototype.onExecute = function() { + D.registerNodeType("math/lerp", v); + G.title = "Abs"; + G.desc = "Absolute"; + G.prototype.onExecute = function() { var a = this.getInputData(0); null != a && this.setOutputData(0, Math.abs(a)); }; - D.registerNodeType("math/abs", F); + D.registerNodeType("math/abs", G); z.title = "Floor"; z.desc = "Floor number to remove fractional part"; z.prototype.onExecute = function() { @@ -6508,16 +6528,16 @@ $jscomp.polyfill("Object.values", function(v) { null != a && this.setOutputData(0, Math.floor(a)); }; D.registerNodeType("math/floor", z); - e.title = "Frac"; - e.desc = "Returns fractional part"; - e.prototype.onExecute = function() { + r.title = "Frac"; + r.desc = "Returns fractional part"; + r.prototype.onExecute = function() { var a = this.getInputData(0); null != a && this.setOutputData(0, a % 1); }; - D.registerNodeType("math/frac", e); - B.title = "Smoothstep"; - B.desc = "Smoothstep"; - B.prototype.onExecute = function() { + D.registerNodeType("math/frac", r); + e.title = "Smoothstep"; + e.desc = "Smoothstep"; + e.prototype.onExecute = function() { var a = this.getInputData(0); if (void 0 !== a) { var b = this.properties.A; @@ -6525,14 +6545,14 @@ $jscomp.polyfill("Object.values", function(v) { this.setOutputData(0, a * a * (3 - 2 * a)); } }; - D.registerNodeType("math/smoothstep", B); - E.title = "Scale"; - E.desc = "v * factor"; - E.prototype.onExecute = function() { + D.registerNodeType("math/smoothstep", e); + J.title = "Scale"; + J.desc = "v * factor"; + J.prototype.onExecute = function() { var a = this.getInputData(0); null != a && this.setOutputData(0, a * this.properties.factor); }; - D.registerNodeType("math/scale", E); + D.registerNodeType("math/scale", J); u.title = "Gate"; u.desc = "if v is true, then outputs A, otherwise B"; u.prototype.onExecute = function() { @@ -6540,9 +6560,9 @@ $jscomp.polyfill("Object.values", function(v) { this.setOutputData(0, this.getInputData(a ? 1 : 2)); }; D.registerNodeType("math/gate", u); - H.title = "Average"; - H.desc = "Average Filter"; - H.prototype.onExecute = function() { + C.title = "Average"; + C.desc = "Average Filter"; + C.prototype.onExecute = function() { var a = this.getInputData(0); null == a && (a = 0); var b = this._values.length; @@ -6554,37 +6574,37 @@ $jscomp.polyfill("Object.values", function(v) { } this.setOutputData(0, a / b); }; - H.prototype.onPropertyChanged = function(a, b) { + C.prototype.onPropertyChanged = function(a, b) { 1 > b && (b = 1); this.properties.samples = Math.round(b); a = this._values; this._values = new Float32Array(this.properties.samples); a.length <= this._values.length ? this._values.set(a) : this._values.set(a.subarray(0, this._values.length)); }; - D.registerNodeType("math/average", H); - n.title = "TendTo"; - n.desc = "moves the output value always closer to the input"; - n.prototype.onExecute = function() { + D.registerNodeType("math/average", C); + m.title = "TendTo"; + m.desc = "moves the output value always closer to the input"; + m.prototype.onExecute = function() { var a = this.getInputData(0); null == a && (a = 0); var b = this.properties.factor; this._value = null == this._value ? a : this._value * (1 - b) + a * b; this.setOutputData(0, this._value); }; - D.registerNodeType("math/tendTo", n); - p.values = "+ - * / % ^ max min".split(" "); - p.title = "Operation"; - p.desc = "Easy math operators"; - p["@OP"] = {type:"enum", title:"operation", values:p.values}; - p.size = [100, 60]; - p.prototype.getTitle = function() { + D.registerNodeType("math/tendTo", m); + n.values = "+ - * / % ^ max min".split(" "); + n.title = "Operation"; + n.desc = "Easy math operators"; + n["@OP"] = {type:"enum", title:"operation", values:n.values}; + n.size = [100, 60]; + n.prototype.getTitle = function() { return "max" == this.properties.OP || "min" == this.properties.OP ? this.properties.OP + "(A,B)" : "A " + this.properties.OP + " B"; }; - p.prototype.setValue = function(a) { + n.prototype.setValue = function(a) { "string" == typeof a && (a = parseFloat(a)); this.properties.value = a; }; - p.prototype.onPropertyChanged = function(a, b) { + n.prototype.onPropertyChanged = function(a, b) { if ("OP" == a) { switch(this.properties.OP) { case "+": @@ -6636,7 +6656,7 @@ $jscomp.polyfill("Object.values", function(v) { } } }; - p.prototype.onExecute = function() { + n.prototype.onExecute = function() { var a = this.getInputData(0), b = this.getInputData(1); null != a ? a.constructor === Number && (this.properties.A = a) : a = this.properties.A; null != b ? this.properties.B = b : b = this.properties.B; @@ -6657,22 +6677,22 @@ $jscomp.polyfill("Object.values", function(v) { } this.setOutputData(0, d); }; - p.prototype.onDrawBackground = function(a) { + n.prototype.onDrawBackground = function(a) { this.flags.collapsed || (a.font = "40px Arial", a.fillStyle = "#666", a.textAlign = "center", a.fillText(this.properties.OP, 0.5 * this.size[0], 0.5 * (this.size[1] + D.NODE_TITLE_HEIGHT)), a.textAlign = "left"); }; - D.registerNodeType("math/operation", p); + D.registerNodeType("math/operation", n); D.registerSearchboxExtra("math/operation", "MAX", {properties:{OP:"max"}, title:"MAX()"}); D.registerSearchboxExtra("math/operation", "MIN", {properties:{OP:"min"}, title:"MIN()"}); - k.title = "Compare"; - k.desc = "compares between two values"; - k.prototype.onExecute = function() { + h.title = "Compare"; + h.desc = "compares between two values"; + h.prototype.onExecute = function() { var a = this.getInputData(0), b = this.getInputData(1); void 0 !== a ? this.properties.A = a : a = this.properties.A; void 0 !== b ? this.properties.B = b : b = this.properties.B; for (var d = 0, c = this.outputs.length; d < c; ++d) { - var k = this.outputs[d]; - if (k.links && k.links.length) { - switch(k.name) { + var h = this.outputs[d]; + if (h.links && h.links.length) { + switch(h.name) { case "A==B": var f = a == b; break; @@ -6695,24 +6715,24 @@ $jscomp.polyfill("Object.values", function(v) { } } }; - k.prototype.onGetOutputs = function() { + h.prototype.onGetOutputs = function() { return [["A==B", "boolean"], ["A!=B", "boolean"], ["A>B", "boolean"], ["A=B", "boolean"], ["A<=B", "boolean"]]; }; - D.registerNodeType("math/compare", k); + D.registerNodeType("math/compare", h); D.registerSearchboxExtra("math/compare", "==", {outputs:[["A==B", "boolean"]], title:"A==B"}); D.registerSearchboxExtra("math/compare", "!=", {outputs:[["A!=B", "boolean"]], title:"A!=B"}); D.registerSearchboxExtra("math/compare", ">", {outputs:[["A>B", "boolean"]], title:"A>B"}); D.registerSearchboxExtra("math/compare", "<", {outputs:[["A=", {outputs:[["A>=B", "boolean"]], title:"A>=B"}); D.registerSearchboxExtra("math/compare", "<=", {outputs:[["A<=B", "boolean"]], title:"A<=B"}); - a.values = "> < == != <= >= || &&".split(" "); - a["@OP"] = {type:"enum", title:"operation", values:a.values}; - a.title = "Condition"; - a.desc = "evaluates condition between A and B"; - a.prototype.getTitle = function() { + E.values = "> < == != <= >= || &&".split(" "); + E["@OP"] = {type:"enum", title:"operation", values:E.values}; + E.title = "Condition"; + E.desc = "evaluates condition between A and B"; + E.prototype.getTitle = function() { return "A " + this.properties.OP + " B"; }; - a.prototype.onExecute = function() { + E.prototype.onExecute = function() { var a = this.getInputData(0); void 0 === a ? a = this.properties.A : this.properties.A = a; var b = this.getInputData(1); @@ -6746,19 +6766,19 @@ $jscomp.polyfill("Object.values", function(v) { this.setOutputData(0, d); this.setOutputData(1, !d); }; - D.registerNodeType("math/condition", a); - b.title = "Accumulate"; - b.desc = "Increments a value every time"; - b.prototype.onExecute = function() { + D.registerNodeType("math/condition", E); + a.title = "Accumulate"; + a.desc = "Increments a value every time"; + a.prototype.onExecute = function() { null === this.properties.value && (this.properties.value = 0); var a = this.getInputData(0); this.properties.value = null !== a ? this.properties.value + a : this.properties.value + this.properties.increment; this.setOutputData(0, this.properties.value); }; - D.registerNodeType("math/accumulate", b); - d.title = "Trigonometry"; - d.desc = "Sin Cos Tan"; - d.prototype.onExecute = function() { + D.registerNodeType("math/accumulate", a); + b.title = "Trigonometry"; + b.desc = "Sin Cos Tan"; + b.prototype.onExecute = function() { var a = this.getInputData(0); null == a && (a = 0); var b = this.properties.amplitude, d = this.findInputSlot("amplitude"); @@ -6767,46 +6787,46 @@ $jscomp.polyfill("Object.values", function(v) { d = this.findInputSlot("offset"); -1 != d && (c = this.getInputData(d)); d = 0; - for (var f = this.outputs.length; d < f; ++d) { + for (var h = this.outputs.length; d < h; ++d) { switch(this.outputs[d].name) { case "sin": - var k = Math.sin(a); + var f = Math.sin(a); break; case "cos": - k = Math.cos(a); + f = Math.cos(a); break; case "tan": - k = Math.tan(a); + f = Math.tan(a); break; case "asin": - k = Math.asin(a); + f = Math.asin(a); break; case "acos": - k = Math.acos(a); + f = Math.acos(a); break; case "atan": - k = Math.atan(a); + f = Math.atan(a); } - this.setOutputData(d, b * k + c); + this.setOutputData(d, b * f + c); } }; - d.prototype.onGetInputs = function() { + b.prototype.onGetInputs = function() { return [["v", "number"], ["amplitude", "number"], ["offset", "number"]]; }; - d.prototype.onGetOutputs = function() { + b.prototype.onGetOutputs = function() { return [["sin", "number"], ["cos", "number"], ["tan", "number"], ["asin", "number"], ["acos", "number"], ["atan", "number"]]; }; - D.registerNodeType("math/trigonometry", d); + D.registerNodeType("math/trigonometry", b); D.registerSearchboxExtra("math/trigonometry", "SIN()", {outputs:[["sin", "number"]], title:"SIN()"}); D.registerSearchboxExtra("math/trigonometry", "COS()", {outputs:[["cos", "number"]], title:"COS()"}); D.registerSearchboxExtra("math/trigonometry", "TAN()", {outputs:[["tan", "number"]], title:"TAN()"}); - g.title = "Formula"; - g.desc = "Compute formula"; - g.size = [160, 100]; - H.prototype.onPropertyChanged = function(a, b) { + d.title = "Formula"; + d.desc = "Compute formula"; + d.size = [160, 100]; + C.prototype.onPropertyChanged = function(a, b) { "formula" == a && (this.code_widget.value = b); }; - g.prototype.onExecute = function() { + d.prototype.onExecute = function() { if (D.allow_scripts) { var a = this.getInputData(0), b = this.getInputData(1); null != a ? this.properties.x = a : a = this.properties.x; @@ -6821,14 +6841,14 @@ $jscomp.polyfill("Object.values", function(v) { this.setOutputData(0, d); } }; - g.prototype.getTitle = function() { + d.prototype.getTitle = function() { return this._func_code || "Formula"; }; - g.prototype.onDrawBackground = function() { + d.prototype.onDrawBackground = function() { var a = this.properties.formula; this.outputs && this.outputs.length && (this.outputs[0].label = a); }; - D.registerNodeType("math/formula", g); + D.registerNodeType("math/formula", d); f.title = "Vec2->XY"; f.desc = "vector 2 to components"; f.prototype.onExecute = function() { @@ -6836,9 +6856,9 @@ $jscomp.polyfill("Object.values", function(v) { null != a && (this.setOutputData(0, a[0]), this.setOutputData(1, a[1])); }; D.registerNodeType("math3d/vec2-to-xy", f); - C.title = "XY->Vec2"; - C.desc = "components to vector2"; - C.prototype.onExecute = function() { + k.title = "XY->Vec2"; + k.desc = "components to vector2"; + k.prototype.onExecute = function() { var a = this.getInputData(0); null == a && (a = this.properties.x); var b = this.getInputData(1); @@ -6848,17 +6868,17 @@ $jscomp.polyfill("Object.values", function(v) { d[1] = b; this.setOutputData(0, d); }; - D.registerNodeType("math3d/xy-to-vec2", C); - w.title = "Vec3->XYZ"; - w.desc = "vector 3 to components"; - w.prototype.onExecute = function() { + D.registerNodeType("math3d/xy-to-vec2", k); + I.title = "Vec3->XYZ"; + I.desc = "vector 3 to components"; + I.prototype.onExecute = function() { var a = this.getInputData(0); null != a && (this.setOutputData(0, a[0]), this.setOutputData(1, a[1]), this.setOutputData(2, a[2])); }; - D.registerNodeType("math3d/vec3-to-xyz", w); - I.title = "XYZ->Vec3"; - I.desc = "components to vector3"; - I.prototype.onExecute = function() { + D.registerNodeType("math3d/vec3-to-xyz", I); + F.title = "XYZ->Vec3"; + F.desc = "components to vector3"; + F.prototype.onExecute = function() { var a = this.getInputData(0); null == a && (a = this.properties.x); var b = this.getInputData(1); @@ -6871,17 +6891,17 @@ $jscomp.polyfill("Object.values", function(v) { c[2] = d; this.setOutputData(0, c); }; - D.registerNodeType("math3d/xyz-to-vec3", I); - K.title = "Vec4->XYZW"; - K.desc = "vector 4 to components"; - K.prototype.onExecute = function() { + D.registerNodeType("math3d/xyz-to-vec3", F); + L.title = "Vec4->XYZW"; + L.desc = "vector 4 to components"; + L.prototype.onExecute = function() { var a = this.getInputData(0); null != a && (this.setOutputData(0, a[0]), this.setOutputData(1, a[1]), this.setOutputData(2, a[2]), this.setOutputData(3, a[3])); }; - D.registerNodeType("math3d/vec4-to-xyzw", K); - L.title = "XYZW->Vec4"; - L.desc = "components to vector4"; - L.prototype.onExecute = function() { + D.registerNodeType("math3d/vec4-to-xyzw", L); + K.title = "XYZW->Vec4"; + K.desc = "components to vector4"; + K.prototype.onExecute = function() { var a = this.getInputData(0); null == a && (a = this.properties.x); var b = this.getInputData(1); @@ -6890,16 +6910,16 @@ $jscomp.polyfill("Object.values", function(v) { null == d && (d = this.properties.z); var c = this.getInputData(3); null == c && (c = this.properties.w); - var k = this._data; - k[0] = a; - k[1] = b; - k[2] = d; - k[3] = c; - this.setOutputData(0, k); + var h = this._data; + h[0] = a; + h[1] = b; + h[2] = d; + h[3] = c; + this.setOutputData(0, h); }; - D.registerNodeType("math3d/xyzw-to-vec4", L); + D.registerNodeType("math3d/xyzw-to-vec4", K); })(this); -(function(v) { +(function(w) { function c() { this.addInput("sel", "number"); this.addInput("A"); @@ -6909,7 +6929,7 @@ $jscomp.polyfill("Object.values", function(v) { this.addOutput("out"); this.selected = 0; } - function r() { + function t() { this.properties = {sequence:"A,B,C"}; this.addInput("index", "number"); this.addInput("seq"); @@ -6917,17 +6937,17 @@ $jscomp.polyfill("Object.values", function(v) { this.index = 0; this.values = this.properties.sequence.split(","); } - var m = v.LiteGraph; + var l = w.LiteGraph; c.title = "Selector"; c.desc = "selects an output"; c.prototype.onDrawBackground = function(c) { if (!this.flags.collapsed) { c.fillStyle = "#AFB"; - var h = (this.selected + 1) * m.NODE_SLOT_HEIGHT + 6; + var p = (this.selected + 1) * l.NODE_SLOT_HEIGHT + 6; c.beginPath(); - c.moveTo(50, h); - c.lineTo(50, h + m.NODE_SLOT_HEIGHT); - c.lineTo(34, h + 0.5 * m.NODE_SLOT_HEIGHT); + c.moveTo(50, p); + c.lineTo(50, p + l.NODE_SLOT_HEIGHT); + c.lineTo(34, p + 0.5 * l.NODE_SLOT_HEIGHT); c.fill(); } }; @@ -6943,13 +6963,13 @@ $jscomp.polyfill("Object.values", function(v) { c.prototype.onGetInputs = function() { return [["E", 0], ["F", 0], ["G", 0], ["H", 0]]; }; - m.registerNodeType("logic/selector", c); - r.title = "Sequence"; - r.desc = "select one element from a sequence from a string"; - r.prototype.onPropertyChanged = function(c, m) { - "sequence" == c && (this.values = m.split(",")); + l.registerNodeType("logic/selector", c); + t.title = "Sequence"; + t.desc = "select one element from a sequence from a string"; + t.prototype.onPropertyChanged = function(c, l) { + "sequence" == c && (this.values = l.split(",")); }; - r.prototype.onExecute = function() { + t.prototype.onExecute = function() { var c = this.getInputData(1); c && c != this.current_sequence && (this.values = c.split(","), this.current_sequence = c); c = this.getInputData(0); @@ -6957,27 +6977,27 @@ $jscomp.polyfill("Object.values", function(v) { this.index = c = Math.round(c) % this.values.length; this.setOutputData(0, this.values[c]); }; - m.registerNodeType("logic/sequence", r); + l.registerNodeType("logic/sequence", t); })(this); -(function(v) { +(function(w) { function c() { this.addOutput("tex", "Texture"); this.addOutput("name", "string"); this.properties = {name:"", filter:!0}; this.size = [c.image_preview_size, c.image_preview_size]; } - function r() { + function t() { this.addInput("Texture", "Texture"); this.properties = {flipY:!1}; this.size = [c.image_preview_size, c.image_preview_size]; } - function m() { + function l() { this.addInput("Texture", "Texture"); this.addOutput("tex", "Texture"); this.addOutput("name", "string"); this.properties = {name:"", generate_mipmaps:!1}; } - function h() { + function p() { this.addInput("Texture", "Texture"); this.addInput("TextureB", "Texture"); this.addInput("value", "number"); @@ -6992,14 +7012,14 @@ $jscomp.polyfill("Object.values", function(v) { this.properties.code = q.pixel_shader; this._uniforms = {u_value:1, u_color:vec4.create(), in_texture:0, texSize:vec2.create(), time:0}; } - function l() { + function g() { this.addInput("in", "Texture"); this.addInput("scale", "vec2"); this.addInput("offset", "vec2"); this.addOutput("out", "Texture"); this.properties = {offset:vec2.fromValues(0, 0), scale:vec2.fromValues(1, 1), precision:c.DEFAULT}; } - function A() { + function B() { this.addInput("in", "Texture"); this.addInput("warp", "Texture"); this.addInput("factor", "number"); @@ -7007,17 +7027,17 @@ $jscomp.polyfill("Object.values", function(v) { this.properties = {factor:0.01, scale:[1, 1], offset:[0, 0], precision:c.DEFAULT}; this._uniforms = {u_texture:0, u_textureB:1, u_factor:1, u_scale:vec2.create(), u_offset:vec2.create()}; } - function y() { + function A() { this.addInput("Texture", "Texture"); this.properties = {additive:!1, antialiasing:!1, filter:!0, disable_alpha:!1, gamma:1.0, viewport:[0, 0, 1, 1]}; this.size[0] = 130; } - function x() { + function v() { this.addInput("Texture", "Texture"); this.addOutput("", "Texture"); this.properties = {size:0, generate_mipmaps:!1, precision:c.DEFAULT}; } - function F() { + function G() { this.addInput("Texture", "Texture"); this.addOutput("", "Texture"); this.properties = {iterations:1, generate_mipmaps:!1, precision:c.DEFAULT}; @@ -7031,14 +7051,14 @@ $jscomp.polyfill("Object.values", function(v) { this._uniforms = {u_texture:0, u_mipmap_offset:0}; this._luminance = new Float32Array(4); } - function e() { + function r() { this.addInput("in", "Texture"); this.addInput("factor", "Number"); this.addOutput("out", "Texture"); this.properties = {factor:0.5}; this._uniforms = {u_texture:0, u_textureB:1, u_factor:this.properties.factor}; } - function B() { + function e() { this.addInput("in", "Texture"); this.addOutput("avg", "Texture"); this.addOutput("array", "Texture"); @@ -7046,7 +7066,7 @@ $jscomp.polyfill("Object.values", function(v) { this._uniforms = {u_texture:0, u_textureB:1, u_samples:this.properties.samples, u_isamples:1 / this.properties.samples}; this.frame = 0; } - function E() { + function J() { this.addInput("Image", "image"); this.addOutput("", "Texture"); this.properties = {}; @@ -7059,23 +7079,23 @@ $jscomp.polyfill("Object.values", function(v) { this.properties = {enabled:!0, intensity:1, precision:c.DEFAULT, texture:null}; u._shader || (u._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, u.pixel_shader)); } - function H() { + function C() { this.addInput("Texture", "Texture"); this.addInput("Atlas", "Texture"); this.addOutput("", "Texture"); this.properties = {enabled:!0, num_row_symbols:4, symbol_size:16, brightness:1, colorize:!1, filter:!1, invert:!1, precision:c.DEFAULT, texture:null}; - H._shader || (H._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, H.pixel_shader)); + C._shader || (C._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, C.pixel_shader)); this._uniforms = {u_texture:0, u_textureB:1, u_row_simbols:4, u_simbol_size:16, u_res:vec2.create()}; } - function n() { + function m() { this.addInput("Texture", "Texture"); this.addOutput("R", "Texture"); this.addOutput("G", "Texture"); this.addOutput("B", "Texture"); this.addOutput("A", "Texture"); - n._shader || (n._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, n.pixel_shader)); + m._shader || (m._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, m.pixel_shader)); } - function p() { + function n() { this.addInput("R", "Texture"); this.addInput("G", "Texture"); this.addInput("B", "Texture"); @@ -7085,20 +7105,20 @@ $jscomp.polyfill("Object.values", function(v) { this._color = vec4.create(); this._uniforms = {u_textureR:0, u_textureG:1, u_textureB:2, u_textureA:3, u_color:this._color}; } - function k() { + function h() { this.addOutput("Texture", "Texture"); this._tex_color = vec4.create(); this.properties = {color:vec4.create(), precision:c.DEFAULT}; } - function a() { + function E() { this.addInput("A", "color"); this.addInput("B", "color"); this.addOutput("Texture", "Texture"); this.properties = {angle:0, scale:1, A:[0, 0, 0], B:[1, 1, 1], texture_size:32}; - a._shader || (a._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, a.pixel_shader)); + E._shader || (E._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, E.pixel_shader)); this._uniforms = {u_angle:0, u_colorA:vec3.create(), u_colorB:vec3.create()}; } - function b() { + function a() { this.addInput("A", "Texture"); this.addInput("B", "Texture"); this.addInput("Mixer", "Texture"); @@ -7106,13 +7126,13 @@ $jscomp.polyfill("Object.values", function(v) { this.properties = {factor:0.5, size_from_biggest:!0, invert:!1, precision:c.DEFAULT}; this._uniforms = {u_textureA:0, u_textureB:1, u_textureMix:2, u_mix:vec4.create()}; } - function d() { + function b() { this.addInput("Tex.", "Texture"); this.addOutput("Edges", "Texture"); this.properties = {invert:!0, threshold:!1, factor:1, precision:c.DEFAULT}; - d._shader || (d._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, d.pixel_shader)); + b._shader || (b._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, b.pixel_shader)); } - function g() { + function d() { this.addInput("Texture", "Texture"); this.addInput("Distance", "number"); this.addInput("Range", "number"); @@ -7126,14 +7146,14 @@ $jscomp.polyfill("Object.values", function(v) { this.properties = {precision:c.DEFAULT, invert:!1}; this._uniforms = {u_texture:0, u_camera_planes:null, u_ires:vec2.create()}; } - function C() { + function k() { this.addInput("Texture", "Texture"); this.addInput("Iterations", "number"); this.addInput("Intensity", "number"); this.addOutput("Blurred", "Texture"); this.properties = {intensity:1, iterations:1, preserve_aspect:!1, scale:[1, 1], precision:c.DEFAULT}; } - function w() { + function I() { this.addInput("in", "Texture"); this.addInput("dirt", "Texture"); this.addOutput("out", "Texture"); @@ -7142,17 +7162,17 @@ $jscomp.polyfill("Object.values", function(v) { this._textures = []; this._uniforms = {u_intensity:1, u_texture:0, u_glow_texture:1, u_threshold:0, u_texel_size:vec2.create()}; } - function I() { + function F() { this.addInput("Texture", "Texture"); this.addOutput("Filtered", "Texture"); this.properties = {intensity:1, radius:5}; } - function K() { + function L() { this.addInput("Texture", "Texture"); this.addOutput("Filtered", "Texture"); this.properties = {sigma:1.4, k:1.6, p:21.7, epsilon:79, phi:0.017}; } - function L() { + function K() { this.addOutput("Webcam", "Texture"); this.properties = {texture_name:"", facingMode:"user"}; this.boxcolor = "black"; @@ -7165,12 +7185,12 @@ $jscomp.polyfill("Object.values", function(v) { this.properties = {enabled:!0, factor:1, precision:c.LOW}; this._uniforms = {u_texture:0, u_factor:1}; } - function t() { + function x() { this.addInput("in", ""); this.properties = {precision:c.LOW, width:0, height:0, channels:1}; this.addOutput("out", "Texture"); } - function J() { + function y() { this.addInput("in", "Texture"); this.addOutput("out", "Texture"); this.properties = {precision:c.LOW, split_channels:!1}; @@ -7186,7 +7206,7 @@ $jscomp.polyfill("Object.values", function(v) { this.curve_offset = 68; this.size = [240, 160]; } - function N() { + function P() { this.addInput("in", "Texture"); this.addInput("exp", "number"); this.addOutput("out", "Texture"); @@ -7200,17 +7220,17 @@ $jscomp.polyfill("Object.values", function(v) { this.properties = {enabled:!0, scale:1, gamma:1, average_lum:1, lum_white:1, precision:c.LOW}; this._uniforms = {u_texture:0, u_lumwhite2:1, u_igamma:1, u_scale:1, u_average_lum:1}; } - function P() { + function O() { this.addOutput("out", "Texture"); this.properties = {width:512, height:512, seed:0, persistence:0.1, octaves:8, scale:1, offset:[0, 0], amplitude:1, precision:c.DEFAULT}; this._key = 0; this._texture = null; this._uniforms = {u_persistence:0.1, u_seed:0, u_offset:vec2.create(), u_scale:1, u_viewport:vec2.create()}; } - function O() { + function N() { this.addInput("v"); this.addOutput("out", "Texture"); - this.properties = {code:O.default_code, width:512, height:512, clear:!0, precision:c.DEFAULT, use_html_canvas:!1}; + this.properties = {code:N.default_code, width:512, height:512, clear:!0, precision:c.DEFAULT, use_html_canvas:!1}; this._temp_texture = this._func = null; this.compileCode(); } @@ -7225,14 +7245,14 @@ $jscomp.polyfill("Object.values", function(v) { this.addOutput("out", "texture"); this.properties = {yaw:0}; } - var G = v.LiteGraph, V = v.LGraphCanvas; - v.LGraphTexture = null; - "undefined" != typeof GL && (V.link_type_colors.Texture = "#987", v.LGraphTexture = c, c.title = "Texture", c.desc = "Texture", c.widgets_info = {name:{widget:"texture"}, filter:{widget:"checkbox"}}, c.loadTextureCallback = null, c.image_preview_size = 256, c.PASS_THROUGH = 1, c.COPY = 2, c.LOW = 3, c.HIGH = 4, c.REUSE = 5, c.DEFAULT = 2, c.MODE_VALUES = {"pass through":c.PASS_THROUGH, copy:c.COPY, low:c.LOW, high:c.HIGH, reuse:c.REUSE, default:c.DEFAULT}, c.getTexturesContainer = function() { + var H = w.LiteGraph, V = w.LGraphCanvas; + w.LGraphTexture = null; + "undefined" != typeof GL && (V.link_type_colors.Texture = "#987", w.LGraphTexture = c, c.title = "Texture", c.desc = "Texture", c.widgets_info = {name:{widget:"texture"}, filter:{widget:"checkbox"}}, c.loadTextureCallback = null, c.image_preview_size = 256, c.PASS_THROUGH = 1, c.COPY = 2, c.LOW = 3, c.HIGH = 4, c.REUSE = 5, c.DEFAULT = 2, c.MODE_VALUES = {"pass through":c.PASS_THROUGH, copy:c.COPY, low:c.LOW, high:c.HIGH, reuse:c.REUSE, default:c.DEFAULT}, c.getTexturesContainer = function() { return gl.textures; }, c.loadTexture = function(a, b) { b = b || {}; var d = a; - "http://" == d.substr(0, 7) && G.proxy && (d = G.proxy + d.substr(7)); + "http://" == d.substr(0, 7) && H.proxy && (d = H.proxy + d.substr(7)); return c.getTexturesContainer()[a] = GL.Texture.fromURL(d, b); }, c.getTexture = function(a) { var b = this.getTexturesContainer(); @@ -7302,9 +7322,9 @@ $jscomp.polyfill("Object.values", function(v) { for (var b = 2; b < this.outputs.length; b++) { var d = this.outputs[b]; if (d) { - var k = null; - "width" == d.name ? k = a.width : "height" == d.name ? k = a.height : "aspect" == d.name && (k = a.width / a.height); - this.setOutputData(b, k); + var h = null; + "width" == d.name ? h = a.width : "height" == d.name ? h = a.height : "aspect" == d.name && (h = a.width / a.height); + this.setOutputData(b, h); } } } else { @@ -7359,26 +7379,26 @@ $jscomp.polyfill("Object.values", function(v) { a = a.replace(/[\{\}]/g, ""); return b[a] || ""; }); - }, G.registerNodeType("texture/texture", c), r.title = "Preview", r.desc = "Show a texture in the graph canvas", r.allow_preview = !1, r.prototype.onDrawBackground = function(a) { - if (!this.flags.collapsed && (a.webgl || r.allow_preview)) { + }, H.registerNodeType("texture/texture", c), t.title = "Preview", t.desc = "Show a texture in the graph canvas", t.allow_preview = !1, t.prototype.onDrawBackground = function(a) { + if (!this.flags.collapsed && (a.webgl || t.allow_preview)) { var b = this.getInputData(0); b && (b = !b.handle && a.webgl ? b : c.generateLowResTexturePreview(b), a.save(), this.properties.flipY && (a.translate(0, this.size[1]), a.scale(1, -1)), a.drawImage(b, 0, 0, this.size[0], this.size[1]), a.restore()); } - }, G.registerNodeType("texture/preview", r), m.title = "Save", m.desc = "Save a texture in the repository", m.prototype.getPreviewTexture = function() { + }, H.registerNodeType("texture/preview", t), l.title = "Save", l.desc = "Save a texture in the repository", l.prototype.getPreviewTexture = function() { return this._texture; - }, m.prototype.onExecute = function() { + }, l.prototype.onExecute = function() { var a = this.getInputData(0); a && (this.properties.generate_mipmaps && (a.bind(0), a.setParameter(gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR), gl.generateMipmap(a.texture_type), a.unbind(0)), this.properties.name && (c.storeTexture ? c.storeTexture(this.properties.name, a) : c.getTexturesContainer()[this.properties.name] = a), this._texture = a, this.setOutputData(0, a), this.setOutputData(1, this.properties.name)); - }, G.registerNodeType("texture/save", m), h.widgets_info = {uvcode:{widget:"code"}, pixelcode:{widget:"code"}, precision:{widget:"combo", values:c.MODE_VALUES}}, h.title = "Operation", h.desc = "Texture shader operation", h.presets = {}, h.prototype.getExtraMenuOptions = function(a) { + }, H.registerNodeType("texture/save", l), p.widgets_info = {uvcode:{widget:"code"}, pixelcode:{widget:"code"}, precision:{widget:"combo", values:c.MODE_VALUES}}, p.title = "Operation", p.desc = "Texture shader operation", p.presets = {}, p.prototype.getExtraMenuOptions = function(a) { var b = this; return [{content:b.properties.show ? "Hide Texture" : "Show Texture", callback:function() { b.properties.show = !b.properties.show; }}]; - }, h.prototype.onPropertyChanged = function() { + }, p.prototype.onPropertyChanged = function() { this.has_error = !1; - }, h.prototype.onDrawBackground = function(a) { + }, p.prototype.onDrawBackground = function(a) { this.flags.collapsed || 20 >= this.size[1] || !this.properties.show || !this._tex || this._tex.gl != a || (a.save(), a.drawImage(this._tex, 0, 0, this.size[0], this.size[1]), a.restore()); - }, h.prototype.onExecute = function() { + }, p.prototype.onExecute = function() { var a = this.getInputData(0); if (this.isOutputConnected(0)) { if (this.properties.precision === c.PASS_THROUGH) { @@ -7386,33 +7406,33 @@ $jscomp.polyfill("Object.values", function(v) { } else { var b = this.getInputData(1); if (this.properties.uvcode || this.properties.pixelcode) { - var d = 512, k = 512; - a ? (d = a.width, k = a.height) : b && (d = b.width, k = b.height); + var d = 512, h = 512; + a ? (d = a.width, h = a.height) : b && (d = b.width, h = b.height); b || (b = GL.Texture.getWhiteTexture()); var f = c.getTextureType(this.properties.precision, a); - this._tex = a || this._tex ? c.getTargetTexture(a || this._tex, this._tex, this.properties.precision) : new GL.Texture(d, k, {type:f, format:gl.RGBA, filter:gl.LINEAR}); + this._tex = a || this._tex ? c.getTargetTexture(a || this._tex, this._tex, this.properties.precision) : new GL.Texture(d, h, {type:f, format:gl.RGBA, filter:gl.LINEAR}); f = ""; this.properties.uvcode && (f = "uv = " + this.properties.uvcode, -1 != this.properties.uvcode.indexOf(";") && (f = this.properties.uvcode)); - var g = ""; - this.properties.pixelcode && (g = "result = " + this.properties.pixelcode, -1 != this.properties.pixelcode.indexOf(";") && (g = this.properties.pixelcode)); - var e = this._shader; - if (!(this.has_error || e && this._shader_code == f + "|" + g)) { - var n = c.replaceCode(h.pixel_shader, {UV_CODE:f, PIXEL_CODE:g}); + var e = ""; + this.properties.pixelcode && (e = "result = " + this.properties.pixelcode, -1 != this.properties.pixelcode.indexOf(";") && (e = this.properties.pixelcode)); + var m = this._shader; + if (!(this.has_error || m && this._shader_code == f + "|" + e)) { + var k = c.replaceCode(p.pixel_shader, {UV_CODE:f, PIXEL_CODE:e}); try { - e = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, n), this.boxcolor = "#00FF00"; + m = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, k), this.boxcolor = "#00FF00"; } catch (X) { - GL.Shader.dumpErrorToConsole(X, Shader.SCREEN_VERTEX_SHADER, n); + GL.Shader.dumpErrorToConsole(X, Shader.SCREEN_VERTEX_SHADER, k); this.boxcolor = "#FF0000"; this.has_error = !0; return; } - this._shader = e; - this._shader_code = f + "|" + g; + this._shader = m; + this._shader_code = f + "|" + e; } if (this._shader) { - var p = this.getInputData(2); - null != p ? this.properties.value = p : p = parseFloat(this.properties.value); - var l = this.graph.getTime(); + var n = this.getInputData(2); + null != n ? this.properties.value = n : n = parseFloat(this.properties.value); + var g = this.graph.getTime(); this._tex.drawTo(function() { gl.disable(gl.DEPTH_TEST); gl.disable(gl.CULL_FACE); @@ -7420,39 +7440,39 @@ $jscomp.polyfill("Object.values", function(v) { a && a.bind(0); b && b.bind(1); var c = Mesh.getScreenQuad(); - e.uniforms({u_texture:0, u_textureB:1, value:p, texSize:[d, k], time:l}).draw(c); + m.uniforms({u_texture:0, u_textureB:1, value:n, texSize:[d, h], time:g}).draw(c); }); this.setOutputData(0, this._tex); } } } } - }, h.pixel_shader = "precision highp float;\n\r\n\t\t\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform vec2 texSize;\n\r\n\t\tuniform float time;\n\r\n\t\tuniform float value;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec2 uv = v_coord;\n\r\n\t\t\t{{UV_CODE}};\n\r\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\r\n\t\t\tvec3 color = color4.rgb;\n\r\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\r\n\t\t\tvec3 colorB = color4B.rgb;\n\r\n\t\t\tvec3 result = color;\n\r\n\t\t\tfloat alpha = 1.0;\n\r\n\t\t\t{{PIXEL_CODE}};\n\r\n\t\t\tgl_FragColor = vec4(result, alpha);\n\r\n\t\t}\n\r\n\t\t", - h.registerPreset = function(a, b) { - h.presets[a] = b; - }, h.registerPreset("", ""), h.registerPreset("bypass", "color"), h.registerPreset("add", "color + colorB * value"), h.registerPreset("substract", "(color - colorB) * value"), h.registerPreset("mate", "mix( color, colorB, color4B.a * value)"), h.registerPreset("invert", "vec3(1.0) - color"), h.registerPreset("multiply", "color * colorB * value"), h.registerPreset("divide", "(color / colorB) / value"), h.registerPreset("difference", "abs(color - colorB) * value"), h.registerPreset("max", "max(color, colorB) * value"), - h.registerPreset("min", "min(color, colorB) * value"), h.registerPreset("displace", "texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"), h.registerPreset("grayscale", "vec3(color.x + color.y + color.z) * value / 3.0"), h.registerPreset("saturation", "mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"), h.registerPreset("threshold", "vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"), h.prototype.onInspect = + }, p.pixel_shader = "precision highp float;\n\r\n\t\t\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform vec2 texSize;\n\r\n\t\tuniform float time;\n\r\n\t\tuniform float value;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec2 uv = v_coord;\n\r\n\t\t\t{{UV_CODE}};\n\r\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\r\n\t\t\tvec3 color = color4.rgb;\n\r\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\r\n\t\t\tvec3 colorB = color4B.rgb;\n\r\n\t\t\tvec3 result = color;\n\r\n\t\t\tfloat alpha = 1.0;\n\r\n\t\t\t{{PIXEL_CODE}};\n\r\n\t\t\tgl_FragColor = vec4(result, alpha);\n\r\n\t\t}\n\r\n\t\t", + p.registerPreset = function(a, b) { + p.presets[a] = b; + }, p.registerPreset("", ""), p.registerPreset("bypass", "color"), p.registerPreset("add", "color + colorB * value"), p.registerPreset("substract", "(color - colorB) * value"), p.registerPreset("mate", "mix( color, colorB, color4B.a * value)"), p.registerPreset("invert", "vec3(1.0) - color"), p.registerPreset("multiply", "color * colorB * value"), p.registerPreset("divide", "(color / colorB) / value"), p.registerPreset("difference", "abs(color - colorB) * value"), p.registerPreset("max", "max(color, colorB) * value"), + p.registerPreset("min", "min(color, colorB) * value"), p.registerPreset("displace", "texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"), p.registerPreset("grayscale", "vec3(color.x + color.y + color.z) * value / 3.0"), p.registerPreset("saturation", "mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"), p.registerPreset("threshold", "vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"), p.prototype.onInspect = function(a) { var b = this; - a.addCombo("Presets", "", {values:Object.keys(h.presets), callback:function(d) { - var c = h.presets[d]; + a.addCombo("Presets", "", {values:Object.keys(p.presets), callback:function(d) { + var c = p.presets[d]; c && (b.setProperty("pixelcode", c), b.title = d, a.refresh()); }}); - }, G.registerNodeType("texture/operation", h), q.title = "Shader", q.desc = "Texture shader", q.widgets_info = {code:{type:"code", lang:"glsl"}, precision:{widget:"combo", values:c.MODE_VALUES}}, q.prototype.onPropertyChanged = function(a, b) { + }, H.registerNodeType("texture/operation", p), q.title = "Shader", q.desc = "Texture shader", q.widgets_info = {code:{type:"code", lang:"glsl"}, precision:{widget:"combo", values:c.MODE_VALUES}}, q.prototype.onPropertyChanged = function(a, b) { if ("code" == a && (a = this.getShader())) { b = a.uniformInfo; if (this.inputs) { for (var d = {}, c = 0; c < this.inputs.length; ++c) { - var k = this.getInputInfo(c); - k && (b[k.name] && !d[k.name] ? d[k.name] = !0 : (this.removeInput(c), c--)); + var h = this.getInputInfo(c); + h && (b[h.name] && !d[h.name] ? d[h.name] = !0 : (this.removeInput(c), c--)); } } for (c in b) { - if (k = a.uniformInfo[c], null !== k.loc && "time" != c) { + if (h = a.uniformInfo[c], null !== h.loc && "time" != c) { if (this._shader.samplers[c]) { b = "texture"; } else { - switch(k.size) { + switch(h.size) { case 1: b = "number"; break; @@ -7476,8 +7496,8 @@ $jscomp.polyfill("Object.values", function(v) { } } d = this.findInputSlot(c); - if (-1 != d && (k = this.getInputInfo(d))) { - if (k.type == b) { + if (-1 != d && (h = this.getInputInfo(d))) { + if (h.type == b) { continue; } this.removeInput(d, b); @@ -7499,74 +7519,74 @@ $jscomp.polyfill("Object.values", function(v) { if (a) { var b = 0, d = null; if (this.inputs) { - for (var k = 0; k < this.inputs.length; ++k) { - var f = this.getInputInfo(k), g = this.getInputData(k); - null != g && (g.constructor === GL.Texture && (g.bind(b), d || (d = g), g = b, b++), a.setUniform(f.name, g)); + for (var h = 0; h < this.inputs.length; ++h) { + var f = this.getInputInfo(h), e = this.getInputData(h); + null != e && (e.constructor === GL.Texture && (e.bind(b), d || (d = e), e = b, b++), a.setUniform(f.name, e)); } } - var e = this._uniforms; + var m = this._uniforms; b = c.getTextureType(this.properties.precision, d); - k = this.properties.width | 0; + h = this.properties.width | 0; f = this.properties.height | 0; - 0 == k && (k = d ? d.width : gl.canvas.width); + 0 == h && (h = d ? d.width : gl.canvas.width); 0 == f && (f = d ? d.height : gl.canvas.height); - e.texSize[0] = k; - e.texSize[1] = f; - e.time = this.graph.getTime(); - e.u_value = this.properties.u_value; - e.u_color.set(this.properties.u_color); - this._tex && this._tex.type == b && this._tex.width == k && this._tex.height == f || (this._tex = new GL.Texture(k, f, {type:b, format:gl.RGBA, filter:gl.LINEAR})); + m.texSize[0] = h; + m.texSize[1] = f; + m.time = this.graph.getTime(); + m.u_value = this.properties.u_value; + m.u_color.set(this.properties.u_color); + this._tex && this._tex.type == b && this._tex.width == h && this._tex.height == f || (this._tex = new GL.Texture(h, f, {type:b, format:gl.RGBA, filter:gl.LINEAR})); this._tex.drawTo(function() { - a.uniforms(e).draw(GL.Mesh.getScreenQuad()); + a.uniforms(m).draw(GL.Mesh.getScreenQuad()); }); this.setOutputData(0, this._tex); } } - }, q.pixel_shader = "precision highp float;\n\r\n\n\r\nvarying vec2 v_coord;\n\r\nuniform float time; //time in seconds\n\r\nuniform vec2 texSize; //tex resolution\n\r\nuniform float u_value;\n\r\nuniform vec4 u_color;\n\n\r\nvoid main() {\n\r\n\tvec2 uv = v_coord;\n\r\n\tvec3 color = vec3(0.0);\n\r\n\t//your code here\n\r\n\tcolor.xy=uv;\n\n\r\n\tgl_FragColor = vec4(color, 1.0);\n\r\n}\n\r\n", G.registerNodeType("texture/shader", q), l.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, - l.title = "Scale/Offset", l.desc = "Applies an scaling and offseting", l.prototype.onExecute = function() { + }, q.pixel_shader = "precision highp float;\n\r\n\n\r\nvarying vec2 v_coord;\n\r\nuniform float time; //time in seconds\n\r\nuniform vec2 texSize; //tex resolution\n\r\nuniform float u_value;\n\r\nuniform vec4 u_color;\n\n\r\nvoid main() {\n\r\n\tvec2 uv = v_coord;\n\r\n\tvec3 color = vec3(0.0);\n\r\n\t//your code here\n\r\n\tcolor.xy=uv;\n\n\r\n\tgl_FragColor = vec4(color, 1.0);\n\r\n}\n\r\n", H.registerNodeType("texture/shader", q), g.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, + g.title = "Scale/Offset", g.desc = "Applies an scaling and offseting", g.prototype.onExecute = function() { var a = this.getInputData(0); if (this.isOutputConnected(0) && a) { if (this.properties.precision === c.PASS_THROUGH) { this.setOutputData(0, a); } else { - var b = a.width, d = a.height, k = this.precision === c.LOW ? gl.UNSIGNED_BYTE : gl.HIGH_PRECISION_FORMAT; - this.precision === c.DEFAULT && (k = a.type); - this._tex && this._tex.width == b && this._tex.height == d && this._tex.type == k || (this._tex = new GL.Texture(b, d, {type:k, format:gl.RGBA, filter:gl.LINEAR})); + var b = a.width, d = a.height, h = this.precision === c.LOW ? gl.UNSIGNED_BYTE : gl.HIGH_PRECISION_FORMAT; + this.precision === c.DEFAULT && (h = a.type); + this._tex && this._tex.width == b && this._tex.height == d && this._tex.type == h || (this._tex = new GL.Texture(b, d, {type:h, format:gl.RGBA, filter:gl.LINEAR})); var f = this._shader; - f || (f = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, l.pixel_shader)); - var g = this.getInputData(1); - g ? (this.properties.scale[0] = g[0], this.properties.scale[1] = g[1]) : g = this.properties.scale; - var e = this.getInputData(2); - e ? (this.properties.offset[0] = e[0], this.properties.offset[1] = e[1]) : e = this.properties.offset; + f || (f = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, g.pixel_shader)); + var e = this.getInputData(1); + e ? (this.properties.scale[0] = e[0], this.properties.scale[1] = e[1]) : e = this.properties.scale; + var m = this.getInputData(2); + m ? (this.properties.offset[0] = m[0], this.properties.offset[1] = m[1]) : m = this.properties.offset; this._tex.drawTo(function() { gl.disable(gl.DEPTH_TEST); gl.disable(gl.CULL_FACE); gl.disable(gl.BLEND); a.bind(0); var b = Mesh.getScreenQuad(); - f.uniforms({u_texture:0, u_scale:g, u_offset:e}).draw(b); + f.uniforms({u_texture:0, u_scale:e, u_offset:m}).draw(b); }); this.setOutputData(0, this._tex); } } - }, l.pixel_shader = "precision highp float;\n\r\n\t\t\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform vec2 u_scale;\n\r\n\t\tuniform vec2 u_offset;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec2 uv = v_coord;\n\r\n\t\t\tuv = uv / u_scale - u_offset;\n\r\n\t\t\tgl_FragColor = texture2D(u_texture, uv);\n\r\n\t\t}\n\r\n\t\t", G.registerNodeType("texture/scaleOffset", l), A.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, - A.title = "Warp", A.desc = "Texture warp operation", A.prototype.onExecute = function() { + }, g.pixel_shader = "precision highp float;\n\r\n\t\t\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform vec2 u_scale;\n\r\n\t\tuniform vec2 u_offset;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec2 uv = v_coord;\n\r\n\t\t\tuv = uv / u_scale - u_offset;\n\r\n\t\t\tgl_FragColor = texture2D(u_texture, uv);\n\r\n\t\t}\n\r\n\t\t", H.registerNodeType("texture/scaleOffset", g), B.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, + B.title = "Warp", B.desc = "Texture warp operation", B.prototype.onExecute = function() { var a = this.getInputData(0); if (this.isOutputConnected(0)) { if (this.properties.precision === c.PASS_THROUGH) { this.setOutputData(0, a); } else { - var b = this.getInputData(1), d = 512, k = 512; - a ? (d = a.width, k = a.height) : b && (d = b.width, k = b.height); - this._tex = a || this._tex ? c.getTargetTexture(a || this._tex, this._tex, this.properties.precision) : new GL.Texture(d, k, {type:this.precision === c.LOW ? gl.UNSIGNED_BYTE : gl.HIGH_PRECISION_FORMAT, format:gl.RGBA, filter:gl.LINEAR}); + var b = this.getInputData(1), d = 512, h = 512; + a ? (d = a.width, h = a.height) : b && (d = b.width, h = b.height); + this._tex = a || this._tex ? c.getTargetTexture(a || this._tex, this._tex, this.properties.precision) : new GL.Texture(d, h, {type:this.precision === c.LOW ? gl.UNSIGNED_BYTE : gl.HIGH_PRECISION_FORMAT, format:gl.RGBA, filter:gl.LINEAR}); var f = this._shader; - f || (f = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, A.pixel_shader)); + f || (f = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, B.pixel_shader)); d = this.getInputData(2); null != d ? this.properties.factor = d : d = parseFloat(this.properties.factor); - var g = this._uniforms; - g.u_factor = d; - g.u_scale.set(this.properties.scale); - g.u_offset.set(this.properties.offset); + var e = this._uniforms; + e.u_factor = d; + e.u_scale.set(this.properties.scale); + e.u_offset.set(this.properties.offset); this._tex.drawTo(function() { gl.disable(gl.DEPTH_TEST); gl.disable(gl.CULL_FACE); @@ -7574,13 +7594,13 @@ $jscomp.polyfill("Object.values", function(v) { a && a.bind(0); b && b.bind(1); var d = Mesh.getScreenQuad(); - f.uniforms(g).draw(d); + f.uniforms(e).draw(d); }); this.setOutputData(0, this._tex); } } - }, A.pixel_shader = "precision highp float;\n\r\n\t\t\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform float u_factor;\n\r\n\t\tuniform vec2 u_scale;\n\r\n\t\tuniform vec2 u_offset;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec2 uv = v_coord;\n\r\n\t\t\tuv += ( texture2D(u_textureB, uv).rg - vec2(0.5)) * u_factor * u_scale + u_offset;\n\r\n\t\t\tgl_FragColor = texture2D(u_texture, uv);\n\r\n\t\t}\n\r\n\t\t", G.registerNodeType("texture/warp", - A), y.title = "to Viewport", y.desc = "Texture to viewport", y._prev_viewport = new Float32Array(4), y.prototype.onExecute = function() { + }, B.pixel_shader = "precision highp float;\n\r\n\t\t\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform float u_factor;\n\r\n\t\tuniform vec2 u_scale;\n\r\n\t\tuniform vec2 u_offset;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec2 uv = v_coord;\n\r\n\t\t\tuv += ( texture2D(u_textureB, uv).rg - vec2(0.5)) * u_factor * u_scale + u_offset;\n\r\n\t\t\tgl_FragColor = texture2D(u_texture, uv);\n\r\n\t\t}\n\r\n\t\t", H.registerNodeType("texture/warp", + B), A.title = "to Viewport", A.desc = "Texture to viewport", A._prev_viewport = new Float32Array(4), A.prototype.onExecute = function() { var a = this.getInputData(0); if (a) { this.properties.disable_alpha ? gl.disable(gl.BLEND) : (gl.enable(gl.BLEND), this.properties.additive ? gl.blendFunc(gl.SRC_ALPHA, gl.ONE) : gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)); @@ -7588,70 +7608,70 @@ $jscomp.polyfill("Object.values", function(v) { var b = this.properties.gamma || 1.0; this.isInputConnected(1) && (b = this.getInputData(1)); a.setParameter(gl.TEXTURE_MAG_FILTER, this.properties.filter ? gl.LINEAR : gl.NEAREST); - var d = y._prev_viewport; + var d = A._prev_viewport; d.set(gl.viewport_data); var c = this.properties.viewport; gl.viewport(d[0] + d[2] * c[0], d[1] + d[3] * c[1], d[2] * c[2], d[3] * c[3]); gl.getViewport(); - this.properties.antialiasing ? (y._shader || (y._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, y.aa_pixel_shader)), c = Mesh.getScreenQuad(), a.bind(0), y._shader.uniforms({u_texture:0, uViewportSize:[a.width, a.height], u_igamma:1 / b, inverseVP:[1 / a.width, 1 / a.height]}).draw(c)) : 1.0 != b ? (y._gamma_shader || (y._gamma_shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, y.gamma_pixel_shader)), a.toViewport(y._gamma_shader, {u_texture:0, u_igamma:1 / b})) : a.toViewport(); + this.properties.antialiasing ? (A._shader || (A._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, A.aa_pixel_shader)), c = Mesh.getScreenQuad(), a.bind(0), A._shader.uniforms({u_texture:0, uViewportSize:[a.width, a.height], u_igamma:1 / b, inverseVP:[1 / a.width, 1 / a.height]}).draw(c)) : 1.0 != b ? (A._gamma_shader || (A._gamma_shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, A.gamma_pixel_shader)), a.toViewport(A._gamma_shader, {u_texture:0, u_igamma:1 / b})) : a.toViewport(); gl.viewport(d[0], d[1], d[2], d[3]); } - }, y.prototype.onGetInputs = function() { + }, A.prototype.onGetInputs = function() { return [["gamma", "number"]]; - }, y.aa_pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec2 uViewportSize;\n\r\n\t\tuniform vec2 inverseVP;\n\r\n\t\tuniform float u_igamma;\n\r\n\t\t#define FXAA_REDUCE_MIN (1.0/ 128.0)\n\r\n\t\t#define FXAA_REDUCE_MUL (1.0 / 8.0)\n\r\n\t\t#define FXAA_SPAN_MAX 8.0\n\r\n\t\t\n\r\n\t\t/* from mitsuhiko/webgl-meincraft based on the code on geeks3d.com */\n\r\n\t\tvec4 applyFXAA(sampler2D tex, vec2 fragCoord)\n\r\n\t\t{\n\r\n\t\t\tvec4 color = vec4(0.0);\n\r\n\t\t\t/*vec2 inverseVP = vec2(1.0 / uViewportSize.x, 1.0 / uViewportSize.y);*/\n\r\n\t\t\tvec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * inverseVP).xyz;\n\r\n\t\t\tvec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * inverseVP).xyz;\n\r\n\t\t\tvec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * inverseVP).xyz;\n\r\n\t\t\tvec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * inverseVP).xyz;\n\r\n\t\t\tvec3 rgbM = texture2D(tex, fragCoord * inverseVP).xyz;\n\r\n\t\t\tvec3 luma = vec3(0.299, 0.587, 0.114);\n\r\n\t\t\tfloat lumaNW = dot(rgbNW, luma);\n\r\n\t\t\tfloat lumaNE = dot(rgbNE, luma);\n\r\n\t\t\tfloat lumaSW = dot(rgbSW, luma);\n\r\n\t\t\tfloat lumaSE = dot(rgbSE, luma);\n\r\n\t\t\tfloat lumaM = dot(rgbM, luma);\n\r\n\t\t\tfloat lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n\r\n\t\t\tfloat lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\r\n\t\t\t\n\r\n\t\t\tvec2 dir;\n\r\n\t\t\tdir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n\r\n\t\t\tdir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\r\n\t\t\t\n\r\n\t\t\tfloat dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\r\n\t\t\t\n\r\n\t\t\tfloat rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n\r\n\t\t\tdir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * inverseVP;\n\r\n\t\t\t\n\r\n\t\t\tvec3 rgbA = 0.5 * (texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + \n\r\n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz);\n\r\n\t\t\tvec3 rgbB = rgbA * 0.5 + 0.25 * (texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + \n\r\n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz);\n\r\n\t\t\t\n\r\n\t\t\t//return vec4(rgbA,1.0);\n\r\n\t\t\tfloat lumaB = dot(rgbB, luma);\n\r\n\t\t\tif ((lumaB < lumaMin) || (lumaB > lumaMax))\n\r\n\t\t\t\tcolor = vec4(rgbA, 1.0);\n\r\n\t\t\telse\n\r\n\t\t\t\tcolor = vec4(rgbB, 1.0);\n\r\n\t\t\tif(u_igamma != 1.0)\n\r\n\t\t\t\tcolor.xyz = pow( color.xyz, vec3(u_igamma) );\n\r\n\t\t\treturn color;\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\r\n\t\t}\n\r\n\t\t", - y.gamma_pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_igamma;\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\r\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\r\n\t\t gl_FragColor = color;\n\r\n\t\t}\n\r\n\t\t", G.registerNodeType("texture/toviewport", y), x.title = "Copy", x.desc = "Copy Texture", x.widgets_info = {size:{widget:"combo", - values:[0, 32, 64, 128, 256, 512, 1024, 2048]}, precision:{widget:"combo", values:c.MODE_VALUES}}, x.prototype.onExecute = function() { + }, A.aa_pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec2 uViewportSize;\n\r\n\t\tuniform vec2 inverseVP;\n\r\n\t\tuniform float u_igamma;\n\r\n\t\t#define FXAA_REDUCE_MIN (1.0/ 128.0)\n\r\n\t\t#define FXAA_REDUCE_MUL (1.0 / 8.0)\n\r\n\t\t#define FXAA_SPAN_MAX 8.0\n\r\n\t\t\n\r\n\t\t/* from mitsuhiko/webgl-meincraft based on the code on geeks3d.com */\n\r\n\t\tvec4 applyFXAA(sampler2D tex, vec2 fragCoord)\n\r\n\t\t{\n\r\n\t\t\tvec4 color = vec4(0.0);\n\r\n\t\t\t/*vec2 inverseVP = vec2(1.0 / uViewportSize.x, 1.0 / uViewportSize.y);*/\n\r\n\t\t\tvec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * inverseVP).xyz;\n\r\n\t\t\tvec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * inverseVP).xyz;\n\r\n\t\t\tvec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * inverseVP).xyz;\n\r\n\t\t\tvec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * inverseVP).xyz;\n\r\n\t\t\tvec3 rgbM = texture2D(tex, fragCoord * inverseVP).xyz;\n\r\n\t\t\tvec3 luma = vec3(0.299, 0.587, 0.114);\n\r\n\t\t\tfloat lumaNW = dot(rgbNW, luma);\n\r\n\t\t\tfloat lumaNE = dot(rgbNE, luma);\n\r\n\t\t\tfloat lumaSW = dot(rgbSW, luma);\n\r\n\t\t\tfloat lumaSE = dot(rgbSE, luma);\n\r\n\t\t\tfloat lumaM = dot(rgbM, luma);\n\r\n\t\t\tfloat lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n\r\n\t\t\tfloat lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\r\n\t\t\t\n\r\n\t\t\tvec2 dir;\n\r\n\t\t\tdir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n\r\n\t\t\tdir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\r\n\t\t\t\n\r\n\t\t\tfloat dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\r\n\t\t\t\n\r\n\t\t\tfloat rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n\r\n\t\t\tdir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * inverseVP;\n\r\n\t\t\t\n\r\n\t\t\tvec3 rgbA = 0.5 * (texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + \n\r\n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz);\n\r\n\t\t\tvec3 rgbB = rgbA * 0.5 + 0.25 * (texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + \n\r\n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz);\n\r\n\t\t\t\n\r\n\t\t\t//return vec4(rgbA,1.0);\n\r\n\t\t\tfloat lumaB = dot(rgbB, luma);\n\r\n\t\t\tif ((lumaB < lumaMin) || (lumaB > lumaMax))\n\r\n\t\t\t\tcolor = vec4(rgbA, 1.0);\n\r\n\t\t\telse\n\r\n\t\t\t\tcolor = vec4(rgbB, 1.0);\n\r\n\t\t\tif(u_igamma != 1.0)\n\r\n\t\t\t\tcolor.xyz = pow( color.xyz, vec3(u_igamma) );\n\r\n\t\t\treturn color;\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\r\n\t\t}\n\r\n\t\t", + A.gamma_pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_igamma;\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\r\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\r\n\t\t gl_FragColor = color;\n\r\n\t\t}\n\r\n\t\t", H.registerNodeType("texture/toviewport", A), v.title = "Copy", v.desc = "Copy Texture", v.widgets_info = {size:{widget:"combo", + values:[0, 32, 64, 128, 256, 512, 1024, 2048]}, precision:{widget:"combo", values:c.MODE_VALUES}}, v.prototype.onExecute = function() { var a = this.getInputData(0); if ((a || this._temp_texture) && this.isOutputConnected(0)) { if (a) { var b = a.width, d = a.height; 0 != this.properties.size && (d = b = this.properties.size); - var k = this._temp_texture, f = a.type; + var h = this._temp_texture, f = a.type; this.properties.precision === c.LOW ? f = gl.UNSIGNED_BYTE : this.properties.precision === c.HIGH && (f = gl.HIGH_PRECISION_FORMAT); - k && k.width == b && k.height == d && k.type == f || (k = gl.LINEAR, this.properties.generate_mipmaps && isPowerOfTwo(b) && isPowerOfTwo(d) && (k = gl.LINEAR_MIPMAP_LINEAR), this._temp_texture = new GL.Texture(b, d, {type:f, format:gl.RGBA, minFilter:k, magFilter:gl.LINEAR})); + h && h.width == b && h.height == d && h.type == f || (h = gl.LINEAR, this.properties.generate_mipmaps && isPowerOfTwo(b) && isPowerOfTwo(d) && (h = gl.LINEAR_MIPMAP_LINEAR), this._temp_texture = new GL.Texture(b, d, {type:f, format:gl.RGBA, minFilter:h, magFilter:gl.LINEAR})); a.copyTo(this._temp_texture); this.properties.generate_mipmaps && (this._temp_texture.bind(0), gl.generateMipmap(this._temp_texture.texture_type), this._temp_texture.unbind(0)); } this.setOutputData(0, this._temp_texture); } - }, G.registerNodeType("texture/copy", x), F.title = "Downsample", F.desc = "Downsample Texture", F.widgets_info = {iterations:{type:"number", step:1, precision:0, min:0}, precision:{widget:"combo", values:c.MODE_VALUES}}, F.prototype.onExecute = function() { + }, H.registerNodeType("texture/copy", v), G.title = "Downsample", G.desc = "Downsample Texture", G.widgets_info = {iterations:{type:"number", step:1, precision:0, min:0}, precision:{widget:"combo", values:c.MODE_VALUES}}, G.prototype.onExecute = function() { var a = this.getInputData(0); if ((a || this._temp_texture) && this.isOutputConnected(0) && a && a.texture_type === GL.TEXTURE_2D) { if (1 > this.properties.iterations) { this.setOutputData(0, a); } else { - var b = F._shader; - b || (F._shader = b = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, F.pixel_shader)); - var d = a.width | 0, k = a.height | 0, f = a.type; + var b = G._shader; + b || (G._shader = b = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, G.pixel_shader)); + var d = a.width | 0, h = a.height | 0, f = a.type; this.properties.precision === c.LOW ? f = gl.UNSIGNED_BYTE : this.properties.precision === c.HIGH && (f = gl.HIGH_PRECISION_FORMAT); - var g = this.properties.iterations || 1, e = a, n = []; + var e = this.properties.iterations || 1, m = a, k = []; f = {type:f, format:a.format}; - var p = vec2.create(), h = {u_offset:p}; + var n = vec2.create(), g = {u_offset:n}; this._texture && GL.Texture.releaseTemporary(this._texture); - for (var l = 0; l < g; ++l) { - p[0] = 1 / d; - p[1] = 1 / k; + for (var l = 0; l < e; ++l) { + n[0] = 1 / d; + n[1] = 1 / h; d = d >> 1 || 0; - k = k >> 1 || 0; - a = GL.Texture.getTemporary(d, k, f); - n.push(a); - e.setParameter(GL.TEXTURE_MAG_FILTER, GL.NEAREST); - e.copyTo(a, b, h); - if (1 == d && 1 == k) { + h = h >> 1 || 0; + a = GL.Texture.getTemporary(d, h, f); + k.push(a); + m.setParameter(GL.TEXTURE_MAG_FILTER, GL.NEAREST); + m.copyTo(a, b, g); + if (1 == d && 1 == h) { break; } - e = a; + m = a; } - this._texture = n.pop(); - for (l = 0; l < n.length; ++l) { - GL.Texture.releaseTemporary(n[l]); + this._texture = k.pop(); + for (l = 0; l < k.length; ++l) { + GL.Texture.releaseTemporary(k[l]); } this.properties.generate_mipmaps && (this._texture.bind(0), gl.generateMipmap(this._texture.texture_type), this._texture.unbind(0)); this.setOutputData(0, this._texture); } } - }, F.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec2 u_offset;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = texture2D(u_texture, v_coord );\n\r\n\t\t\tcolor += texture2D(u_texture, v_coord + vec2( u_offset.x, 0.0 ) );\n\r\n\t\t\tcolor += texture2D(u_texture, v_coord + vec2( 0.0, u_offset.y ) );\n\r\n\t\t\tcolor += texture2D(u_texture, v_coord + vec2( u_offset.x, u_offset.y ) );\n\r\n\t\t gl_FragColor = color * 0.25;\n\r\n\t\t}\n\r\n\t\t", - G.registerNodeType("texture/downsample", F), z.title = "Average", z.desc = "Compute a partial average (32 random samples) of a texture and stores it as a 1x1 pixel texture.\n If high_quality is true, then it generates the mipmaps first and reads from the lower one.", z.prototype.onExecute = function() { + }, G.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec2 u_offset;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = texture2D(u_texture, v_coord );\n\r\n\t\t\tcolor += texture2D(u_texture, v_coord + vec2( u_offset.x, 0.0 ) );\n\r\n\t\t\tcolor += texture2D(u_texture, v_coord + vec2( 0.0, u_offset.y ) );\n\r\n\t\t\tcolor += texture2D(u_texture, v_coord + vec2( u_offset.x, u_offset.y ) );\n\r\n\t\t gl_FragColor = color * 0.25;\n\r\n\t\t}\n\r\n\t\t", + H.registerNodeType("texture/downsample", G), z.title = "Average", z.desc = "Compute a partial average (32 random samples) of a texture and stores it as a 1x1 pixel texture.\n If high_quality is true, then it generates the mipmaps first and reads from the lower one.", z.prototype.onExecute = function() { this.properties.use_previous_frame || this.updateAverage(); var a = this._luminance; this.setOutputData(0, this._temp_texture); @@ -7675,12 +7695,12 @@ $jscomp.polyfill("Object.values", function(v) { d && d.type == b || (this._temp_texture = new GL.Texture(1, 1, {type:b, format:gl.RGBA, filter:gl.NEAREST})); this._uniforms.u_mipmap_offset = 0; this.properties.high_quality && (this._temp_pot2_texture && this._temp_pot2_texture.type == b || (this._temp_pot2_texture = new GL.Texture(512, 512, {type:b, format:gl.RGBA, minFilter:gl.LINEAR_MIPMAP_LINEAR, magFilter:gl.LINEAR})), a.copyTo(this._temp_pot2_texture), a = this._temp_pot2_texture, a.bind(0), gl.generateMipmap(GL.TEXTURE_2D), this._uniforms.u_mipmap_offset = 9); - var c = z._shader, k = this._uniforms; - k.u_mipmap_offset = this.properties.mipmap_offset; + var c = z._shader, h = this._uniforms; + h.u_mipmap_offset = this.properties.mipmap_offset; gl.disable(gl.DEPTH_TEST); gl.disable(gl.BLEND); this._temp_texture.drawTo(function() { - a.toViewport(c, k); + a.toViewport(c, h); }); if (this.isOutputConnected(1) || this.isOutputConnected(2)) { if (d = this._temp_texture.getPixels()) { @@ -7692,60 +7712,60 @@ $jscomp.polyfill("Object.values", function(v) { } } }, z.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tuniform mat4 u_samples_a;\n\r\n\t\tuniform mat4 u_samples_b;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_mipmap_offset;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = vec4(0.0);\n\r\n\t\t\t//random average\n\r\n\t\t\tfor(int i = 0; i < 4; ++i)\n\r\n\t\t\t\tfor(int j = 0; j < 4; ++j)\n\r\n\t\t\t\t{\n\r\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\r\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\r\n\t\t\t\t}\n\r\n\t\t gl_FragColor = color * 0.03125;\n\r\n\t\t}\n\r\n\t\t", - G.registerNodeType("texture/average", z), e.title = "Smooth", e.desc = "Smooth texture over time", e.prototype.onExecute = function() { + H.registerNodeType("texture/average", z), r.title = "Smooth", r.desc = "Smooth texture over time", r.prototype.onExecute = function() { var a = this.getInputData(0); if (a && this.isOutputConnected(0)) { - e._shader || (e._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, e.pixel_shader)); + r._shader || (r._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, r.pixel_shader)); var b = this._temp_texture; b && b.type == a.type && b.width == a.width && b.height == a.height || (b = {type:a.type, format:gl.RGBA, filter:gl.NEAREST}, this._temp_texture = new GL.Texture(a.width, a.height, b), this._temp_texture2 = new GL.Texture(a.width, a.height, b), a.copyTo(this._temp_texture2)); b = this._temp_texture; - var d = this._temp_texture2, c = e._shader, k = this._uniforms; - k.u_factor = 1.0 - this.getInputOrProperty("factor"); + var d = this._temp_texture2, c = r._shader, h = this._uniforms; + h.u_factor = 1.0 - this.getInputOrProperty("factor"); gl.disable(gl.BLEND); gl.disable(gl.DEPTH_TEST); b.drawTo(function() { d.bind(1); - a.toViewport(c, k); + a.toViewport(c, h); }); this.setOutputData(0, b); this._temp_texture = d; this._temp_texture2 = b; } - }, e.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tuniform float u_factor;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tgl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\r\n\t\t}\n\r\n\t\t", G.registerNodeType("texture/temporal_smooth", e), B.title = "Lineal Avg Smooth", B.desc = "Smooth texture linearly over time", - B["@samples"] = {type:"number", min:1, max:64, step:1, precision:1}, B.prototype.getPreviewTexture = function() { + }, r.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tuniform float u_factor;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tgl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\r\n\t\t}\n\r\n\t\t", H.registerNodeType("texture/temporal_smooth", r), e.title = "Lineal Avg Smooth", e.desc = "Smooth texture linearly over time", + e["@samples"] = {type:"number", min:1, max:64, step:1, precision:1}, e.prototype.getPreviewTexture = function() { return this._temp_texture2; - }, B.prototype.onExecute = function() { + }, e.prototype.onExecute = function() { var a = this.getInputData(0); if (a && this.isOutputConnected(0)) { - B._shader || (B._shader_copy = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, B.pixel_shader_copy), B._shader_avg = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, B.pixel_shader_avg)); + e._shader || (e._shader_copy = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, e.pixel_shader_copy), e._shader_avg = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, e.pixel_shader_avg)); var b = Math.clamp(this.properties.samples, 0, 64), d = this.frame, c = this.properties.frames_interval; if (0 == c || 0 == d % c) { d = this._temp_texture; d && d.type == a.type && d.width == b || (d = {type:a.type, format:gl.RGBA, filter:gl.NEAREST}, this._temp_texture = new GL.Texture(b, 1, d), this._temp_texture2 = new GL.Texture(b, 1, d), this._temp_texture_out = new GL.Texture(1, 1, d)); - var k = this._temp_texture, f = this._temp_texture2, g = B._shader_copy, e = B._shader_avg, n = this._uniforms; + var h = this._temp_texture, f = this._temp_texture2, m = e._shader_copy, k = e._shader_avg, n = this._uniforms; n.u_samples = b; n.u_isamples = 1.0 / b; gl.disable(gl.BLEND); gl.disable(gl.DEPTH_TEST); - k.drawTo(function() { + h.drawTo(function() { f.bind(1); - a.toViewport(g, n); + a.toViewport(m, n); }); this._temp_texture_out.drawTo(function() { - k.toViewport(e, n); + h.toViewport(k, n); }); this.setOutputData(0, this._temp_texture_out); this._temp_texture = f; - this._temp_texture2 = k; + this._temp_texture2 = h; } else { this.setOutputData(0, this._temp_texture_out); } this.setOutputData(1, this._temp_texture2); this.frame++; } - }, B.pixel_shader_copy = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tuniform float u_isamples;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tif( v_coord.x <= u_isamples )\n\r\n\t\t\t\tgl_FragColor = texture2D( u_texture, vec2(0.5) );\n\r\n\t\t\telse\n\r\n\t\t\t\tgl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\r\n\t\t}\n\r\n\t\t", B.pixel_shader_avg = + }, e.pixel_shader_copy = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tuniform float u_isamples;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tif( v_coord.x <= u_isamples )\n\r\n\t\t\t\tgl_FragColor = texture2D( u_texture, vec2(0.5) );\n\r\n\t\t\telse\n\r\n\t\t\t\tgl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\r\n\t\t}\n\r\n\t\t", e.pixel_shader_avg = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform int u_samples;\n\r\n\t\tuniform float u_isamples;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = vec4(0.0);\n\r\n\t\t\tfor(int i = 0; i < 64; ++i)\n\r\n\t\t\t{\n\r\n\t\t\t\tcolor += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\r\n\t\t\t\tif(i == (u_samples - 1))\n\r\n\t\t\t\t\tbreak;\n\r\n\t\t\t}\n\r\n\t\t\tgl_FragColor = color * u_isamples;\n\r\n\t\t}\n\r\n\t\t", - G.registerNodeType("texture/linear_avg_smooth", B), E.title = "Image to Texture", E.desc = "Uploads an image to the GPU", E.prototype.onExecute = function() { + H.registerNodeType("texture/linear_avg_smooth", e), J.title = "Image to Texture", J.desc = "Uploads an image to the GPU", J.prototype.onExecute = function() { var a = this.getInputData(0); if (a) { var b = a.videoWidth || a.width, d = a.videoHeight || a.height; @@ -7763,7 +7783,7 @@ $jscomp.polyfill("Object.values", function(v) { this.setOutputData(0, this._temp_texture); } } - }, G.registerNodeType("texture/imageToTexture", E), u.widgets_info = {texture:{widget:"texture"}, precision:{widget:"combo", values:c.MODE_VALUES}}, u.title = "LUT", u.desc = "Apply LUT to Texture", u.prototype.onExecute = function() { + }, H.registerNodeType("texture/imageToTexture", J), u.widgets_info = {texture:{widget:"texture"}, precision:{widget:"combo", values:c.MODE_VALUES}}, u.title = "LUT", u.desc = "Apply LUT to Texture", u.prototype.onExecute = function() { if (this.isOutputConnected(0)) { var a = this.getInputData(0); if (this.properties.precision === c.PASS_THROUGH || !1 === this.properties.enabled) { @@ -7793,7 +7813,7 @@ $jscomp.polyfill("Object.values", function(v) { } } }, u.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tuniform float u_amount;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\r\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\r\n\t\t\t mediump vec2 quad1;\n\r\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\r\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\r\n\t\t\t mediump vec2 quad2;\n\r\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\r\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\r\n\t\t\t highp vec2 texPos1;\n\r\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\r\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\r\n\t\t\t highp vec2 texPos2;\n\r\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\r\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\r\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\r\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\r\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\r\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\r\n\t\t}\n\r\n\t\t", - G.registerNodeType("texture/LUT", u), H.widgets_info = {texture:{widget:"texture"}, precision:{widget:"combo", values:c.MODE_VALUES}}, H.title = "Encode", H.desc = "Apply a texture atlas to encode a texture", H.prototype.onExecute = function() { + H.registerNodeType("texture/LUT", u), C.widgets_info = {texture:{widget:"texture"}, precision:{widget:"combo", values:c.MODE_VALUES}}, C.title = "Encode", C.desc = "Apply a texture atlas to encode a texture", C.prototype.onExecute = function() { if (this.isOutputConnected(0)) { var a = this.getInputData(0); if (this.properties.precision === c.PASS_THROUGH || !1 === this.properties.enabled) { @@ -7823,7 +7843,7 @@ $jscomp.polyfill("Object.values", function(v) { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); this._tex.drawTo(function() { b.bind(1); - a.toViewport(H._shader, d); + a.toViewport(C._shader, d); }); this.setOutputData(0, this._tex); } else { @@ -7832,8 +7852,8 @@ $jscomp.polyfill("Object.values", function(v) { } } } - }, H.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tuniform float u_row_simbols;\n\r\n\t\tuniform float u_symbol_size;\n\r\n\t\tuniform float u_brightness;\n\r\n\t\tuniform float u_invert;\n\r\n\t\tuniform float u_colorize;\n\r\n\t\tuniform vec2 u_res;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec2 total_symbols = u_res / u_symbol_size;\n\r\n\t\t\tvec2 uv = floor(v_coord * total_symbols) / total_symbols; //pixelate \n\r\n\t\t\tvec2 local_uv = mod(v_coord * u_res, u_symbol_size) / u_symbol_size;\n\r\n\t\t\tlowp vec4 textureColor = texture2D(u_texture, uv );\n\r\n\t\t\tfloat lum = clamp(u_brightness * (textureColor.x + textureColor.y + textureColor.z)/3.0,0.0,1.0);\n\r\n\t\t\tif( u_invert == 1.0 ) lum = 1.0 - lum;\n\r\n\t\t\tfloat index = floor( lum * (u_row_simbols * u_row_simbols - 1.0));\n\r\n\t\t\tfloat col = mod( index, u_row_simbols );\n\r\n\t\t\tfloat row = u_row_simbols - floor( index / u_row_simbols ) - 1.0;\n\r\n\t\t\tvec2 simbol_uv = ( vec2( col, row ) + local_uv ) / u_row_simbols;\n\r\n\t\t\tvec4 color = texture2D( u_textureB, simbol_uv );\n\r\n\t\t\tif(u_colorize == 1.0)\n\r\n\t\t\t\tcolor *= textureColor;\n\r\n\t\t\tgl_FragColor = color;\n\r\n\t\t}\n\r\n\t\t", - G.registerNodeType("texture/encode", H), n.title = "Texture to Channels", n.desc = "Split texture channels", n.prototype.onExecute = function() { + }, C.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tuniform float u_row_simbols;\n\r\n\t\tuniform float u_symbol_size;\n\r\n\t\tuniform float u_brightness;\n\r\n\t\tuniform float u_invert;\n\r\n\t\tuniform float u_colorize;\n\r\n\t\tuniform vec2 u_res;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec2 total_symbols = u_res / u_symbol_size;\n\r\n\t\t\tvec2 uv = floor(v_coord * total_symbols) / total_symbols; //pixelate \n\r\n\t\t\tvec2 local_uv = mod(v_coord * u_res, u_symbol_size) / u_symbol_size;\n\r\n\t\t\tlowp vec4 textureColor = texture2D(u_texture, uv );\n\r\n\t\t\tfloat lum = clamp(u_brightness * (textureColor.x + textureColor.y + textureColor.z)/3.0,0.0,1.0);\n\r\n\t\t\tif( u_invert == 1.0 ) lum = 1.0 - lum;\n\r\n\t\t\tfloat index = floor( lum * (u_row_simbols * u_row_simbols - 1.0));\n\r\n\t\t\tfloat col = mod( index, u_row_simbols );\n\r\n\t\t\tfloat row = u_row_simbols - floor( index / u_row_simbols ) - 1.0;\n\r\n\t\t\tvec2 simbol_uv = ( vec2( col, row ) + local_uv ) / u_row_simbols;\n\r\n\t\t\tvec4 color = texture2D( u_textureB, simbol_uv );\n\r\n\t\t\tif(u_colorize == 1.0)\n\r\n\t\t\t\tcolor *= textureColor;\n\r\n\t\t\tgl_FragColor = color;\n\r\n\t\t}\n\r\n\t\t", + H.registerNodeType("texture/encode", C), m.title = "Texture to Channels", m.desc = "Split texture channels", m.prototype.onExecute = function() { var a = this.getInputData(0); if (a) { this._channels || (this._channels = Array(4)); @@ -7843,26 +7863,26 @@ $jscomp.polyfill("Object.values", function(v) { if (d) { gl.disable(gl.BLEND); gl.disable(gl.DEPTH_TEST); - var k = Mesh.getScreenQuad(), f = n._shader, g = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]; + var h = Mesh.getScreenQuad(), f = m._shader, e = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]; for (c = 0; 4 > c; c++) { this._channels[c] && (this._channels[c].drawTo(function() { a.bind(0); - f.uniforms({u_texture:0, u_mask:g[c]}).draw(k); + f.uniforms({u_texture:0, u_mask:e[c]}).draw(h); }), this.setOutputData(c, this._channels[c])); } } } - }, n.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec4 u_mask;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\r\n\t\t}\n\r\n\t\t", G.registerNodeType("texture/textureChannels", n), p.title = "Channels to Texture", p.desc = "Split texture channels", p.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, - p.prototype.onExecute = function() { - var a = c.getWhiteTexture(), b = this.getInputData(0) || a, d = this.getInputData(1) || a, k = this.getInputData(2) || a, f = this.getInputData(3) || a; + }, m.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec4 u_mask;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\r\n\t\t}\n\r\n\t\t", H.registerNodeType("texture/textureChannels", m), n.title = "Channels to Texture", n.desc = "Split texture channels", n.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, + n.prototype.onExecute = function() { + var a = c.getWhiteTexture(), b = this.getInputData(0) || a, d = this.getInputData(1) || a, h = this.getInputData(2) || a, f = this.getInputData(3) || a; gl.disable(gl.BLEND); gl.disable(gl.DEPTH_TEST); - var g = Mesh.getScreenQuad(); - p._shader || (p._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, p.pixel_shader)); - var e = p._shader; - a = Math.max(b.width, d.width, k.width, f.width); - var n = Math.max(b.height, d.height, k.height, f.height), h = this.properties.precision == c.HIGH ? c.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE; - this._texture && this._texture.width == a && this._texture.height == n && this._texture.type == h || (this._texture = new GL.Texture(a, n, {type:h, format:gl.RGBA, filter:gl.LINEAR})); + var e = Mesh.getScreenQuad(); + n._shader || (n._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, n.pixel_shader)); + var m = n._shader; + a = Math.max(b.width, d.width, h.width, f.width); + var k = Math.max(b.height, d.height, h.height, f.height), g = this.properties.precision == c.HIGH ? c.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE; + this._texture && this._texture.width == a && this._texture.height == k && this._texture.type == g || (this._texture = new GL.Texture(a, k, {type:g, format:gl.RGBA, filter:gl.LINEAR})); a = this._color; a[0] = this.properties.R; a[1] = this.properties.G; @@ -7872,102 +7892,102 @@ $jscomp.polyfill("Object.values", function(v) { this._texture.drawTo(function() { b.bind(0); d.bind(1); - k.bind(2); + h.bind(2); f.bind(3); - e.uniforms(l).draw(g); + m.uniforms(l).draw(e); }); this.setOutputData(0, this._texture); - }, p.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_textureR;\n\r\n\t\tuniform sampler2D u_textureG;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tuniform sampler2D u_textureA;\n\r\n\t\tuniform vec4 u_color;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t gl_FragColor = u_color * vec4( \r\n\t\t\t\t\ttexture2D(u_textureR, v_coord).r,\r\n\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\r\n\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\r\n\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\r\n\t\t}\n\r\n\t\t", - G.registerNodeType("texture/channelsTexture", p), k.title = "Color", k.desc = "Generates a 1x1 texture with a constant color", k.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, k.prototype.onDrawBackground = function(a) { + }, n.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_textureR;\n\r\n\t\tuniform sampler2D u_textureG;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\tuniform sampler2D u_textureA;\n\r\n\t\tuniform vec4 u_color;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t gl_FragColor = u_color * vec4( \r\n\t\t\t\t\ttexture2D(u_textureR, v_coord).r,\r\n\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\r\n\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\r\n\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\r\n\t\t}\n\r\n\t\t", + H.registerNodeType("texture/channelsTexture", n), h.title = "Color", h.desc = "Generates a 1x1 texture with a constant color", h.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, h.prototype.onDrawBackground = function(a) { var b = this.properties.color; a.fillStyle = "rgb(" + Math.floor(255 * Math.clamp(b[0], 0, 1)) + "," + Math.floor(255 * Math.clamp(b[1], 0, 1)) + "," + Math.floor(255 * Math.clamp(b[2], 0, 1)) + ")"; this.flags.collapsed ? this.boxcolor = a.fillStyle : a.fillRect(0, 0, this.size[0], this.size[1]); - }, k.prototype.onExecute = function() { + }, h.prototype.onExecute = function() { var a = this.properties.precision == c.HIGH ? c.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE; this._tex && this._tex.type == a || (this._tex = new GL.Texture(1, 1, {format:gl.RGBA, type:a, minFilter:gl.NEAREST})); a = this.properties.color; if (this.inputs) { for (var b = 0; b < this.inputs.length; b++) { - var d = this.inputs[b], k = this.getInputData(b); - if (void 0 !== k) { + var d = this.inputs[b], h = this.getInputData(b); + if (void 0 !== h) { switch(d.name) { case "RGB": case "RGBA": - a.set(k); + a.set(h); break; case "R": - a[0] = k; + a[0] = h; break; case "G": - a[1] = k; + a[1] = h; break; case "B": - a[2] = k; + a[2] = h; break; case "A": - a[3] = k; + a[3] = h; } } } } 0.001 < vec4.sqrDist(this._tex_color, a) && (this._tex_color.set(a), this._tex.fill(a)); this.setOutputData(0, this._tex); - }, k.prototype.onGetInputs = function() { + }, h.prototype.onGetInputs = function() { return [["RGB", "vec3"], ["RGBA", "vec4"], ["R", "number"], ["G", "number"], ["B", "number"], ["A", "number"]]; - }, G.registerNodeType("texture/color", k), a.title = "Gradient", a.desc = "Generates a gradient", a["@A"] = {type:"color"}, a["@B"] = {type:"color"}, a["@texture_size"] = {type:"enum", values:[32, 64, 128, 256, 512]}, a.prototype.onExecute = function() { + }, H.registerNodeType("texture/color", h), E.title = "Gradient", E.desc = "Generates a gradient", E["@A"] = {type:"color"}, E["@B"] = {type:"color"}, E["@texture_size"] = {type:"enum", values:[32, 64, 128, 256, 512]}, E.prototype.onExecute = function() { gl.disable(gl.BLEND); gl.disable(gl.DEPTH_TEST); - var b = GL.Mesh.getScreenQuad(), d = a._shader, c = this.getInputData(0); - c || (c = this.properties.A); - var k = this.getInputData(1); - k || (k = this.properties.B); - for (var f = 2; f < this.inputs.length; f++) { - var g = this.inputs[f], e = this.getInputData(f); - void 0 !== e && (this.properties[g.name] = e); + var a = GL.Mesh.getScreenQuad(), b = E._shader, d = this.getInputData(0); + d || (d = this.properties.A); + var c = this.getInputData(1); + c || (c = this.properties.B); + for (var h = 2; h < this.inputs.length; h++) { + var f = this.inputs[h], e = this.getInputData(h); + void 0 !== e && (this.properties[f.name] = e); } - var n = this._uniforms; + var m = this._uniforms; this._uniforms.u_angle = this.properties.angle * DEG2RAD; this._uniforms.u_scale = this.properties.scale; - vec3.copy(n.u_colorA, c); - vec3.copy(n.u_colorB, k); - c = parseInt(this.properties.texture_size); - this._tex && this._tex.width == c || (this._tex = new GL.Texture(c, c, {format:gl.RGB, filter:gl.LINEAR})); + vec3.copy(m.u_colorA, d); + vec3.copy(m.u_colorB, c); + d = parseInt(this.properties.texture_size); + this._tex && this._tex.width == d || (this._tex = new GL.Texture(d, d, {format:gl.RGB, filter:gl.LINEAR})); this._tex.drawTo(function() { - d.uniforms(n).draw(b); + b.uniforms(m).draw(a); }); this.setOutputData(0, this._tex); - }, a.prototype.onGetInputs = function() { + }, E.prototype.onGetInputs = function() { return [["angle", "number"], ["scale", "number"]]; - }, a.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform float u_angle;\n\r\n\t\tuniform float u_scale;\n\r\n\t\tuniform vec3 u_colorA;\n\r\n\t\tuniform vec3 u_colorB;\n\r\n\t\t\n\r\n\t\tvec2 rotate(vec2 v, float angle)\n\r\n\t\t{\n\r\n\t\t\tvec2 result;\n\r\n\t\t\tfloat _cos = cos(angle);\n\r\n\t\t\tfloat _sin = sin(angle);\n\r\n\t\t\tresult.x = v.x * _cos - v.y * _sin;\n\r\n\t\t\tresult.y = v.x * _sin + v.y * _cos;\n\r\n\t\t\treturn result;\n\r\n\t\t}\n\r\n\t\tvoid main() {\n\r\n\t\t\tfloat f = (rotate(u_scale * (v_coord - vec2(0.5)), u_angle) + vec2(0.5)).x;\n\r\n\t\t\tvec3 color = mix(u_colorA,u_colorB,clamp(f,0.0,1.0));\n\r\n\t\t gl_FragColor = vec4(color,1.0);\n\r\n\t\t}\n\r\n\t\t", - G.registerNodeType("texture/gradient", a), b.title = "Mix", b.desc = "Generates a texture mixing two textures", b.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, b.prototype.onExecute = function() { - var a = this.getInputData(0); + }, E.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform float u_angle;\n\r\n\t\tuniform float u_scale;\n\r\n\t\tuniform vec3 u_colorA;\n\r\n\t\tuniform vec3 u_colorB;\n\r\n\t\t\n\r\n\t\tvec2 rotate(vec2 v, float angle)\n\r\n\t\t{\n\r\n\t\t\tvec2 result;\n\r\n\t\t\tfloat _cos = cos(angle);\n\r\n\t\t\tfloat _sin = sin(angle);\n\r\n\t\t\tresult.x = v.x * _cos - v.y * _sin;\n\r\n\t\t\tresult.y = v.x * _sin + v.y * _cos;\n\r\n\t\t\treturn result;\n\r\n\t\t}\n\r\n\t\tvoid main() {\n\r\n\t\t\tfloat f = (rotate(u_scale * (v_coord - vec2(0.5)), u_angle) + vec2(0.5)).x;\n\r\n\t\t\tvec3 color = mix(u_colorA,u_colorB,clamp(f,0.0,1.0));\n\r\n\t\t gl_FragColor = vec4(color,1.0);\n\r\n\t\t}\n\r\n\t\t", + H.registerNodeType("texture/gradient", E), a.title = "Mix", a.desc = "Generates a texture mixing two textures", a.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, a.prototype.onExecute = function() { + var b = this.getInputData(0); if (this.isOutputConnected(0)) { if (this.properties.precision === c.PASS_THROUGH) { - this.setOutputData(0, a); + this.setOutputData(0, b); } else { var d = this.getInputData(1); - if (a && d) { - var k = this.getInputData(2), f = this.getInputData(3); - this._tex = c.getTargetTexture(this.properties.size_from_biggest && d.width > a.width ? d : a, this._tex, this.properties.precision); + if (b && d) { + var h = this.getInputData(2), f = this.getInputData(3); + this._tex = c.getTargetTexture(this.properties.size_from_biggest && d.width > b.width ? d : b, this._tex, this.properties.precision); gl.disable(gl.BLEND); gl.disable(gl.DEPTH_TEST); - var g = Mesh.getScreenQuad(), e = null, n = this._uniforms; - k ? (e = b._shader_tex, e || (e = b._shader_tex = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, b.pixel_shader, {MIX_TEX:""}))) : (e = b._shader_factor, e || (e = b._shader_factor = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, b.pixel_shader)), f = null == f ? this.properties.factor : f, n.u_mix.set([f, f, f, f])); - var p = this.properties.invert; + var e = Mesh.getScreenQuad(), m = null, k = this._uniforms; + h ? (m = a._shader_tex, m || (m = a._shader_tex = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, a.pixel_shader, {MIX_TEX:""}))) : (m = a._shader_factor, m || (m = a._shader_factor = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, a.pixel_shader)), f = null == f ? this.properties.factor : f, k.u_mix.set([f, f, f, f])); + var n = this.properties.invert; this._tex.drawTo(function() { - a.bind(p ? 1 : 0); - d.bind(p ? 0 : 1); - k && k.bind(2); - e.uniforms(n).draw(g); + b.bind(n ? 1 : 0); + d.bind(n ? 0 : 1); + h && h.bind(2); + m.uniforms(k).draw(e); }); this.setOutputData(0, this._tex); } } } - }, b.prototype.onGetInputs = function() { + }, a.prototype.onGetInputs = function() { return [["factor", "number"]]; - }, b.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_textureA;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\t#ifdef MIX_TEX\n\r\n\t\t\tuniform sampler2D u_textureMix;\n\r\n\t\t#else\n\r\n\t\t\tuniform vec4 u_mix;\n\r\n\t\t#endif\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\t#ifdef MIX_TEX\n\r\n\t\t\t vec4 f = texture2D(u_textureMix, v_coord);\n\r\n\t\t\t#else\n\r\n\t\t\t vec4 f = u_mix;\n\r\n\t\t\t#endif\n\r\n\t\t gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\r\n\t\t}\n\r\n\t\t", - G.registerNodeType("texture/mix", b), d.title = "Edges", d.desc = "Detects edges", d.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, d.prototype.onExecute = function() { + }, a.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_textureA;\n\r\n\t\tuniform sampler2D u_textureB;\n\r\n\t\t#ifdef MIX_TEX\n\r\n\t\t\tuniform sampler2D u_textureMix;\n\r\n\t\t#else\n\r\n\t\t\tuniform vec4 u_mix;\n\r\n\t\t#endif\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\t#ifdef MIX_TEX\n\r\n\t\t\t vec4 f = texture2D(u_textureMix, v_coord);\n\r\n\t\t\t#else\n\r\n\t\t\t vec4 f = u_mix;\n\r\n\t\t\t#endif\n\r\n\t\t gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\r\n\t\t}\n\r\n\t\t", + H.registerNodeType("texture/mix", a), b.title = "Edges", b.desc = "Detects edges", b.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, b.prototype.onExecute = function() { if (this.isOutputConnected(0)) { var a = this.getInputData(0); if (this.properties.precision === c.PASS_THROUGH) { @@ -7977,48 +7997,48 @@ $jscomp.polyfill("Object.values", function(v) { this._tex = c.getTargetTexture(a, this._tex, this.properties.precision); gl.disable(gl.BLEND); gl.disable(gl.DEPTH_TEST); - var b = Mesh.getScreenQuad(), k = d._shader, f = this.properties.invert, g = this.properties.factor, e = this.properties.threshold ? 1 : 0; + var d = Mesh.getScreenQuad(), h = b._shader, f = this.properties.invert, e = this.properties.factor, m = this.properties.threshold ? 1 : 0; this._tex.drawTo(function() { a.bind(0); - k.uniforms({u_texture:0, u_isize:[1 / a.width, 1 / a.height], u_factor:g, u_threshold:e, u_invert:f ? 1 : 0}).draw(b); + h.uniforms({u_texture:0, u_isize:[1 / a.width, 1 / a.height], u_factor:e, u_threshold:m, u_invert:f ? 1 : 0}).draw(d); }); this.setOutputData(0, this._tex); } } } - }, d.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec2 u_isize;\n\r\n\t\tuniform int u_invert;\n\r\n\t\tuniform float u_factor;\n\r\n\t\tuniform float u_threshold;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 center = texture2D(u_texture, v_coord);\n\r\n\t\t\tvec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\r\n\t\t\tvec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\r\n\t\t\tvec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\r\n\t\t\tvec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\r\n\t\t\tvec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\r\n\t\t\tdiff *= u_factor;\n\r\n\t\t\tif(u_invert == 1)\n\r\n\t\t\t\tdiff.xyz = vec3(1.0) - diff.xyz;\n\r\n\t\t\tif( u_threshold == 0.0 )\n\r\n\t\t\t\tgl_FragColor = vec4( diff.xyz, center.a );\n\r\n\t\t\telse\n\r\n\t\t\t\tgl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\r\n\t\t}\n\r\n\t\t", - G.registerNodeType("texture/edges", d), g.title = "Depth Range", g.desc = "Generates a texture with a depth range", g.prototype.onExecute = function() { + }, b.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec2 u_isize;\n\r\n\t\tuniform int u_invert;\n\r\n\t\tuniform float u_factor;\n\r\n\t\tuniform float u_threshold;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 center = texture2D(u_texture, v_coord);\n\r\n\t\t\tvec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\r\n\t\t\tvec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\r\n\t\t\tvec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\r\n\t\t\tvec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\r\n\t\t\tvec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\r\n\t\t\tdiff *= u_factor;\n\r\n\t\t\tif(u_invert == 1)\n\r\n\t\t\t\tdiff.xyz = vec3(1.0) - diff.xyz;\n\r\n\t\t\tif( u_threshold == 0.0 )\n\r\n\t\t\t\tgl_FragColor = vec4( diff.xyz, center.a );\n\r\n\t\t\telse\n\r\n\t\t\t\tgl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\r\n\t\t}\n\r\n\t\t", + H.registerNodeType("texture/edges", b), d.title = "Depth Range", d.desc = "Generates a texture with a depth range", d.prototype.onExecute = function() { if (this.isOutputConnected(0)) { var a = this.getInputData(0); if (a) { var b = gl.UNSIGNED_BYTE; this.properties.high_precision && (b = gl.half_float_ext ? gl.HALF_FLOAT_OES : gl.FLOAT); this._temp_texture && this._temp_texture.type == b && this._temp_texture.width == a.width && this._temp_texture.height == a.height || (this._temp_texture = new GL.Texture(a.width, a.height, {type:b, format:gl.RGBA, filter:gl.LINEAR})); - var d = this._uniforms; + var c = this._uniforms; b = this.properties.distance; this.isInputConnected(1) && (b = this.getInputData(1), this.properties.distance = b); - var c = this.properties.range; - this.isInputConnected(2) && (c = this.getInputData(2), this.properties.range = c); - d.u_distance = b; - d.u_range = c; + var h = this.properties.range; + this.isInputConnected(2) && (h = this.getInputData(2), this.properties.range = h); + c.u_distance = b; + c.u_range = h; gl.disable(gl.BLEND); gl.disable(gl.DEPTH_TEST); - var k = Mesh.getScreenQuad(); - g._shader || (g._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, g.pixel_shader), g._shader_onlydepth = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, g.pixel_shader, {ONLY_DEPTH:""})); - var f = this.properties.only_depth ? g._shader_onlydepth : g._shader; + var f = Mesh.getScreenQuad(); + d._shader || (d._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, d.pixel_shader), d._shader_onlydepth = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, d.pixel_shader, {ONLY_DEPTH:""})); + var e = this.properties.only_depth ? d._shader_onlydepth : d._shader; b = null; b = a.near_far_planes ? a.near_far_planes : window.LS && LS.Renderer._main_camera ? LS.Renderer._main_camera._uniforms.u_camera_planes : [0.1, 1000]; - d.u_camera_planes = b; + c.u_camera_planes = b; this._temp_texture.drawTo(function() { a.bind(0); - f.uniforms(d).draw(k); + e.uniforms(c).draw(f); }); this._temp_texture.near_far_planes = b; this.setOutputData(0, this._temp_texture); } } - }, g.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec2 u_camera_planes;\n\r\n\t\tuniform float u_distance;\n\r\n\t\tuniform float u_range;\n\r\n\t\t\n\r\n\t\tfloat LinearDepth()\n\r\n\t\t{\n\r\n\t\t\tfloat zNear = u_camera_planes.x;\n\r\n\t\t\tfloat zFar = u_camera_planes.y;\n\r\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\r\n\t\t\tdepth = depth * 2.0 - 1.0;\n\r\n\t\t\treturn zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tfloat depth = LinearDepth();\n\r\n\t\t\t#ifdef ONLY_DEPTH\n\r\n\t\t\t gl_FragColor = vec4(depth);\n\r\n\t\t\t#else\n\r\n\t\t\t\tfloat diff = abs(depth * u_camera_planes.y - u_distance);\n\r\n\t\t\t\tfloat dof = 1.0;\n\r\n\t\t\t\tif(diff <= u_range)\n\r\n\t\t\t\t\tdof = diff / u_range;\n\r\n\t\t\t gl_FragColor = vec4(dof);\n\r\n\t\t\t#endif\n\r\n\t\t}\n\r\n\t\t", - G.registerNodeType("texture/depth_range", g), f.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, f.title = "Linear Depth", f.desc = "Creates a color texture with linear depth", f.prototype.onExecute = function() { + }, d.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec2 u_camera_planes;\n\r\n\t\tuniform float u_distance;\n\r\n\t\tuniform float u_range;\n\r\n\t\t\n\r\n\t\tfloat LinearDepth()\n\r\n\t\t{\n\r\n\t\t\tfloat zNear = u_camera_planes.x;\n\r\n\t\t\tfloat zFar = u_camera_planes.y;\n\r\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\r\n\t\t\tdepth = depth * 2.0 - 1.0;\n\r\n\t\t\treturn zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tfloat depth = LinearDepth();\n\r\n\t\t\t#ifdef ONLY_DEPTH\n\r\n\t\t\t gl_FragColor = vec4(depth);\n\r\n\t\t\t#else\n\r\n\t\t\t\tfloat diff = abs(depth * u_camera_planes.y - u_distance);\n\r\n\t\t\t\tfloat dof = 1.0;\n\r\n\t\t\t\tif(diff <= u_range)\n\r\n\t\t\t\t\tdof = diff / u_range;\n\r\n\t\t\t gl_FragColor = vec4(dof);\n\r\n\t\t\t#endif\n\r\n\t\t}\n\r\n\t\t", + H.registerNodeType("texture/depth_range", d), f.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, f.title = "Linear Depth", f.desc = "Creates a color texture with linear depth", f.prototype.onExecute = function() { if (this.isOutputConnected(0)) { var a = this.getInputData(0); if (a && (a.format == gl.DEPTH_COMPONENT || a.format == gl.DEPTH_STENCIL)) { @@ -8028,156 +8048,156 @@ $jscomp.polyfill("Object.values", function(v) { d.u_invert = this.properties.invert ? 1 : 0; gl.disable(gl.BLEND); gl.disable(gl.DEPTH_TEST); - var k = Mesh.getScreenQuad(); + var h = Mesh.getScreenQuad(); f._shader || (f._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, f.pixel_shader)); - var g = f._shader; + var e = f._shader; b = null; b = a.near_far_planes ? a.near_far_planes : window.LS && LS.Renderer._main_camera ? LS.Renderer._main_camera._uniforms.u_camera_planes : [0.1, 1000]; d.u_camera_planes = b; d.u_ires.set([0, 0]); this._temp_texture.drawTo(function() { a.bind(0); - g.uniforms(d).draw(k); + e.uniforms(d).draw(h); }); this._temp_texture.near_far_planes = b; this.setOutputData(0, this._temp_texture); } } }, f.pixel_shader = "precision highp float;\n\r\n\t\tprecision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec2 u_camera_planes;\n\r\n\t\tuniform int u_invert;\n\r\n\t\tuniform vec2 u_ires;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tfloat zNear = u_camera_planes.x;\n\r\n\t\t\tfloat zFar = u_camera_planes.y;\n\r\n\t\t\tfloat depth = texture2D(u_texture, v_coord + u_ires*0.5).x * 2.0 - 1.0;\n\r\n\t\t\tfloat f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\r\n\t\t\tif( u_invert == 1 )\n\r\n\t\t\t\tf = 1.0 - f;\n\r\n\t\t\tgl_FragColor = vec4(vec3(f),1.0);\n\r\n\t\t}\n\r\n\t\t", - G.registerNodeType("texture/linear_depth", f), C.title = "Blur", C.desc = "Blur a texture", C.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, C.max_iterations = 20, C.prototype.onExecute = function() { + H.registerNodeType("texture/linear_depth", f), k.title = "Blur", k.desc = "Blur a texture", k.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, k.max_iterations = 20, k.prototype.onExecute = function() { var a = this.getInputData(0); if (a && this.isOutputConnected(0)) { var b = this._final_texture; b && b.width == a.width && b.height == a.height && b.type == a.type || (b = this._final_texture = new GL.Texture(a.width, a.height, {type:a.type, format:gl.RGBA, filter:gl.LINEAR})); var d = this.properties.iterations; this.isInputConnected(1) && (d = this.getInputData(1), this.properties.iterations = d); - d = Math.min(Math.floor(d), C.max_iterations); + d = Math.min(Math.floor(d), k.max_iterations); if (0 == d) { this.setOutputData(0, a); } else { var c = this.properties.intensity; this.isInputConnected(2) && (c = this.getInputData(2), this.properties.intensity = c); - var k = G.camera_aspect; - k || void 0 === window.gl || (k = gl.canvas.height / gl.canvas.width); - k || (k = 1); - k = this.properties.preserve_aspect ? k : 1; + var h = H.camera_aspect; + h || void 0 === window.gl || (h = gl.canvas.height / gl.canvas.width); + h || (h = 1); + h = this.properties.preserve_aspect ? h : 1; var f = this.properties.scale || [1, 1]; - a.applyBlur(k * f[0], f[1], c, b); + a.applyBlur(h * f[0], f[1], c, b); for (a = 1; a < d; ++a) { - b.applyBlur(k * f[0] * (a + 1), f[1] * (a + 1), c); + b.applyBlur(h * f[0] * (a + 1), f[1] * (a + 1), c); } this.setOutputData(0, b); } } - }, G.registerNodeType("texture/blur", C), w.title = "Glow", w.desc = "Filters a texture giving it a glow effect", w.weights = new Float32Array([0.5, 0.4, 0.3, 0.2]), w.widgets_info = {iterations:{type:"number", min:0, max:16, step:1, precision:0}, threshold:{type:"number", min:0, max:10, step:0.01, precision:2}, precision:{widget:"combo", values:c.MODE_VALUES}}, w.prototype.onGetInputs = function() { + }, H.registerNodeType("texture/blur", k), I.title = "Glow", I.desc = "Filters a texture giving it a glow effect", I.weights = new Float32Array([0.5, 0.4, 0.3, 0.2]), I.widgets_info = {iterations:{type:"number", min:0, max:16, step:1, precision:0}, threshold:{type:"number", min:0, max:10, step:0.01, precision:2}, precision:{widget:"combo", values:c.MODE_VALUES}}, I.prototype.onGetInputs = function() { return [["enabled", "boolean"], ["threshold", "number"], ["intensity", "number"], ["persistence", "number"], ["iterations", "number"], ["dirt_factor", "number"]]; - }, w.prototype.onGetOutputs = function() { + }, I.prototype.onGetOutputs = function() { return [["average", "Texture"]]; - }, w.prototype.onExecute = function() { + }, I.prototype.onExecute = function() { var a = this.getInputData(0); if (a && this.isAnyOutputConnected()) { if (this.properties.precision === c.PASS_THROUGH || !1 === this.getInputOrProperty("enabled")) { this.setOutputData(0, a); } else { - var b = a.width, d = a.height, k = {format:a.format, type:a.type, minFilter:GL.LINEAR, magFilter:GL.LINEAR, wrap:gl.CLAMP_TO_EDGE}, f = c.getTextureType(this.properties.precision, a), g = this._uniforms, e = this._textures, n = w._cut_shader; - n || (n = w._cut_shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, w.cut_pixel_shader)); + var b = a.width, d = a.height, h = {format:a.format, type:a.type, minFilter:GL.LINEAR, magFilter:GL.LINEAR, wrap:gl.CLAMP_TO_EDGE}, f = c.getTextureType(this.properties.precision, a), e = this._uniforms, m = this._textures, k = I._cut_shader; + k || (k = I._cut_shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, I.cut_pixel_shader)); gl.disable(gl.DEPTH_TEST); gl.disable(gl.BLEND); - g.u_threshold = this.getInputOrProperty("threshold"); - var p = e[0] = GL.Texture.getTemporary(b, d, k); - a.blit(p, n.uniforms(g)); - var h = p, l = this.getInputOrProperty("iterations"); + e.u_threshold = this.getInputOrProperty("threshold"); + var n = m[0] = GL.Texture.getTemporary(b, d, h); + a.blit(n, k.uniforms(e)); + var g = n, l = this.getInputOrProperty("iterations"); l = Math.clamp(l, 1, 16) | 0; - var m = g.u_texel_size, q = this.getInputOrProperty("intensity"); - g.u_intensity = 1; - g.u_delta = this.properties.scale; - n = w._shader; - n || (n = w._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, w.scale_pixel_shader)); + var p = e.u_texel_size, q = this.getInputOrProperty("intensity"); + e.u_intensity = 1; + e.u_delta = this.properties.scale; + k = I._shader; + k || (k = I._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, I.scale_pixel_shader)); for (var r = 1; r < l; r++) { b >>= 1; 1 < (d | 0) && (d >>= 1); if (2 > b) { break; } - p = e[r] = GL.Texture.getTemporary(b, d, k); - m[0] = 1 / h.width; - m[1] = 1 / h.height; - h.blit(p, n.uniforms(g)); - h = p; + n = m[r] = GL.Texture.getTemporary(b, d, h); + p[0] = 1 / g.width; + p[1] = 1 / g.height; + g.blit(n, k.uniforms(e)); + g = n; } - this.isOutputConnected(2) && (b = this._average_texture, b && b.type == a.type && b.format == a.format || (b = this._average_texture = new GL.Texture(1, 1, {type:a.type, format:a.format, filter:gl.LINEAR})), m[0] = 1 / h.width, m[1] = 1 / h.height, g.u_intensity = q, g.u_delta = 1, h.blit(b, n.uniforms(g)), this.setOutputData(2, b)); + this.isOutputConnected(2) && (b = this._average_texture, b && b.type == a.type && b.format == a.format || (b = this._average_texture = new GL.Texture(1, 1, {type:a.type, format:a.format, filter:gl.LINEAR})), p[0] = 1 / g.width, p[1] = 1 / g.height, e.u_intensity = q, e.u_delta = 1, g.blit(b, k.uniforms(e)), this.setOutputData(2, b)); gl.enable(gl.BLEND); gl.blendFunc(gl.ONE, gl.ONE); - g.u_intensity = this.getInputOrProperty("persistence"); - g.u_delta = 0.5; + e.u_intensity = this.getInputOrProperty("persistence"); + e.u_delta = 0.5; for (r -= 2; 0 <= r; r--) { - p = e[r], e[r] = null, m[0] = 1 / h.width, m[1] = 1 / h.height, h.blit(p, n.uniforms(g)), GL.Texture.releaseTemporary(h), h = p; + n = m[r], m[r] = null, p[0] = 1 / g.width, p[1] = 1 / g.height, g.blit(n, k.uniforms(e)), GL.Texture.releaseTemporary(g), g = n; } gl.disable(gl.BLEND); - this.isOutputConnected(1) && (e = this._glow_texture, e && e.width == a.width && e.height == a.height && e.type == f && e.format == a.format || (e = this._glow_texture = new GL.Texture(a.width, a.height, {type:f, format:a.format, filter:gl.LINEAR})), h.blit(e), this.setOutputData(1, e)); + this.isOutputConnected(1) && (m = this._glow_texture, m && m.width == a.width && m.height == a.height && m.type == f && m.format == a.format || (m = this._glow_texture = new GL.Texture(a.width, a.height, {type:f, format:a.format, filter:gl.LINEAR})), g.blit(m), this.setOutputData(1, m)); if (this.isOutputConnected(0)) { - e = this._final_texture; - e && e.width == a.width && e.height == a.height && e.type == f && e.format == a.format || (e = this._final_texture = new GL.Texture(a.width, a.height, {type:f, format:a.format, filter:gl.LINEAR})); - var t = this.getInputData(1), u = this.getInputOrProperty("dirt_factor"); - g.u_intensity = q; - n = t ? w._dirt_final_shader : w._final_shader; - n || (n = t ? w._dirt_final_shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, w.final_pixel_shader, {USE_DIRT:""}) : w._final_shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, w.final_pixel_shader)); - e.drawTo(function() { + m = this._final_texture; + m && m.width == a.width && m.height == a.height && m.type == f && m.format == a.format || (m = this._final_texture = new GL.Texture(a.width, a.height, {type:f, format:a.format, filter:gl.LINEAR})); + var E = this.getInputData(1), t = this.getInputOrProperty("dirt_factor"); + e.u_intensity = q; + k = E ? I._dirt_final_shader : I._final_shader; + k || (k = E ? I._dirt_final_shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, I.final_pixel_shader, {USE_DIRT:""}) : I._final_shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, I.final_pixel_shader)); + m.drawTo(function() { a.bind(0); - h.bind(1); - t && (n.setUniform("u_dirt_factor", u), n.setUniform("u_dirt_texture", t.bind(2))); - n.toViewport(g); + g.bind(1); + E && (k.setUniform("u_dirt_factor", t), k.setUniform("u_dirt_texture", E.bind(2))); + k.toViewport(e); }); - this.setOutputData(0, e); + this.setOutputData(0, m); } - GL.Texture.releaseTemporary(h); + GL.Texture.releaseTemporary(g); } } - }, w.cut_pixel_shader = "precision highp float;\n\r\n\tvarying vec2 v_coord;\n\r\n\tuniform sampler2D u_texture;\n\r\n\tuniform float u_threshold;\n\r\n\tvoid main() {\n\r\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\r\n\t}", w.scale_pixel_shader = "precision highp float;\n\r\n\tvarying vec2 v_coord;\n\r\n\tuniform sampler2D u_texture;\n\r\n\tuniform vec2 u_texel_size;\n\r\n\tuniform float u_delta;\n\r\n\tuniform float u_intensity;\n\r\n\t\n\r\n\tvec4 sampleBox(vec2 uv) {\n\r\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\r\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\r\n\t\treturn s * 0.25;\n\r\n\t}\n\r\n\tvoid main() {\n\r\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\r\n\t}", - w.final_pixel_shader = "precision highp float;\n\r\n\tvarying vec2 v_coord;\n\r\n\tuniform sampler2D u_texture;\n\r\n\tuniform sampler2D u_glow_texture;\n\r\n\t#ifdef USE_DIRT\n\r\n\t\tuniform sampler2D u_dirt_texture;\n\r\n\t#endif\n\r\n\tuniform vec2 u_texel_size;\n\r\n\tuniform float u_delta;\n\r\n\tuniform float u_intensity;\n\r\n\tuniform float u_dirt_factor;\n\r\n\t\n\r\n\tvec4 sampleBox(vec2 uv) {\n\r\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\r\n\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\r\n\t\treturn s * 0.25;\n\r\n\t}\n\r\n\tvoid main() {\n\r\n\t\tvec4 glow = sampleBox( v_coord );\n\r\n\t\t#ifdef USE_DIRT\n\r\n\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\r\n\t\t#endif\n\r\n\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\r\n\t}", - G.registerNodeType("texture/glow", w), I.title = "Kuwahara Filter", I.desc = "Filters a texture giving an artistic oil canvas painting", I.max_radius = 10, I._shaders = [], I.prototype.onExecute = function() { + }, I.cut_pixel_shader = "precision highp float;\n\r\n\tvarying vec2 v_coord;\n\r\n\tuniform sampler2D u_texture;\n\r\n\tuniform float u_threshold;\n\r\n\tvoid main() {\n\r\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\r\n\t}", I.scale_pixel_shader = "precision highp float;\n\r\n\tvarying vec2 v_coord;\n\r\n\tuniform sampler2D u_texture;\n\r\n\tuniform vec2 u_texel_size;\n\r\n\tuniform float u_delta;\n\r\n\tuniform float u_intensity;\n\r\n\t\n\r\n\tvec4 sampleBox(vec2 uv) {\n\r\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\r\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\r\n\t\treturn s * 0.25;\n\r\n\t}\n\r\n\tvoid main() {\n\r\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\r\n\t}", + I.final_pixel_shader = "precision highp float;\n\r\n\tvarying vec2 v_coord;\n\r\n\tuniform sampler2D u_texture;\n\r\n\tuniform sampler2D u_glow_texture;\n\r\n\t#ifdef USE_DIRT\n\r\n\t\tuniform sampler2D u_dirt_texture;\n\r\n\t#endif\n\r\n\tuniform vec2 u_texel_size;\n\r\n\tuniform float u_delta;\n\r\n\tuniform float u_intensity;\n\r\n\tuniform float u_dirt_factor;\n\r\n\t\n\r\n\tvec4 sampleBox(vec2 uv) {\n\r\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\r\n\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\r\n\t\treturn s * 0.25;\n\r\n\t}\n\r\n\tvoid main() {\n\r\n\t\tvec4 glow = sampleBox( v_coord );\n\r\n\t\t#ifdef USE_DIRT\n\r\n\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\r\n\t\t#endif\n\r\n\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\r\n\t}", + H.registerNodeType("texture/glow", I), F.title = "Kuwahara Filter", F.desc = "Filters a texture giving an artistic oil canvas painting", F.max_radius = 10, F._shaders = [], F.prototype.onExecute = function() { var a = this.getInputData(0); if (a && this.isOutputConnected(0)) { var b = this._temp_texture; b && b.width == a.width && b.height == a.height && b.type == a.type || (this._temp_texture = new GL.Texture(a.width, a.height, {type:a.type, format:gl.RGBA, filter:gl.LINEAR})); b = this.properties.radius; - b = Math.min(Math.floor(b), I.max_radius); + b = Math.min(Math.floor(b), F.max_radius); if (0 == b) { this.setOutputData(0, a); } else { - var d = this.properties.intensity, c = G.camera_aspect; + var d = this.properties.intensity, c = H.camera_aspect; c || void 0 === window.gl || (c = gl.canvas.height / gl.canvas.width); c || (c = 1); c = this.properties.preserve_aspect ? c : 1; - I._shaders[b] || (I._shaders[b] = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, I.pixel_shader, {RADIUS:b.toFixed(0)})); - var k = I._shaders[b], f = GL.Mesh.getScreenQuad(); + F._shaders[b] || (F._shaders[b] = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, F.pixel_shader, {RADIUS:b.toFixed(0)})); + var h = F._shaders[b], f = GL.Mesh.getScreenQuad(); a.bind(0); this._temp_texture.drawTo(function() { - k.uniforms({u_texture:0, u_intensity:d, u_resolution:[a.width, a.height], u_iResolution:[1 / a.width, 1 / a.height]}).draw(f); + h.uniforms({u_texture:0, u_intensity:d, u_resolution:[a.width, a.height], u_iResolution:[1 / a.width, 1 / a.height]}).draw(f); }); this.setOutputData(0, this._temp_texture); } } - }, I.pixel_shader = "\n\r\nprecision highp float;\n\r\nvarying vec2 v_coord;\n\r\nuniform sampler2D u_texture;\n\r\nuniform float u_intensity;\n\r\nuniform vec2 u_resolution;\n\r\nuniform vec2 u_iResolution;\n\r\n#ifndef RADIUS\n\r\n\t#define RADIUS 7\n\r\n#endif\n\r\nvoid main() {\n\r\n\n\r\n\tconst int radius = RADIUS;\n\r\n\tvec2 fragCoord = v_coord;\n\r\n\tvec2 src_size = u_iResolution;\n\r\n\tvec2 uv = v_coord;\n\r\n\tfloat n = float((radius + 1) * (radius + 1));\n\r\n\tint i;\n\r\n\tint j;\n\r\n\tvec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\r\n\tvec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\r\n\tvec3 c;\n\r\n\t\n\r\n\tfor (int j = -radius; j <= 0; ++j) {\n\r\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\r\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\r\n\t\t\tm0 += c;\n\r\n\t\t\ts0 += c * c;\n\r\n\t\t}\n\r\n\t}\n\r\n\t\n\r\n\tfor (int j = -radius; j <= 0; ++j) {\n\r\n\t\tfor (int i = 0; i <= radius; ++i) {\n\r\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\r\n\t\t\tm1 += c;\n\r\n\t\t\ts1 += c * c;\n\r\n\t\t}\n\r\n\t}\n\r\n\t\n\r\n\tfor (int j = 0; j <= radius; ++j) {\n\r\n\t\tfor (int i = 0; i <= radius; ++i) {\n\r\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\r\n\t\t\tm2 += c;\n\r\n\t\t\ts2 += c * c;\n\r\n\t\t}\n\r\n\t}\n\r\n\t\n\r\n\tfor (int j = 0; j <= radius; ++j) {\n\r\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\r\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\r\n\t\t\tm3 += c;\n\r\n\t\t\ts3 += c * c;\n\r\n\t\t}\n\r\n\t}\n\r\n\t\n\r\n\tfloat min_sigma2 = 1e+2;\n\r\n\tm0 /= n;\n\r\n\ts0 = abs(s0 / n - m0 * m0);\n\r\n\t\n\r\n\tfloat sigma2 = s0.r + s0.g + s0.b;\n\r\n\tif (sigma2 < min_sigma2) {\n\r\n\t\tmin_sigma2 = sigma2;\n\r\n\t\tgl_FragColor = vec4(m0, 1.0);\n\r\n\t}\n\r\n\t\n\r\n\tm1 /= n;\n\r\n\ts1 = abs(s1 / n - m1 * m1);\n\r\n\t\n\r\n\tsigma2 = s1.r + s1.g + s1.b;\n\r\n\tif (sigma2 < min_sigma2) {\n\r\n\t\tmin_sigma2 = sigma2;\n\r\n\t\tgl_FragColor = vec4(m1, 1.0);\n\r\n\t}\n\r\n\t\n\r\n\tm2 /= n;\n\r\n\ts2 = abs(s2 / n - m2 * m2);\n\r\n\t\n\r\n\tsigma2 = s2.r + s2.g + s2.b;\n\r\n\tif (sigma2 < min_sigma2) {\n\r\n\t\tmin_sigma2 = sigma2;\n\r\n\t\tgl_FragColor = vec4(m2, 1.0);\n\r\n\t}\n\r\n\t\n\r\n\tm3 /= n;\n\r\n\ts3 = abs(s3 / n - m3 * m3);\n\r\n\t\n\r\n\tsigma2 = s3.r + s3.g + s3.b;\n\r\n\tif (sigma2 < min_sigma2) {\n\r\n\t\tmin_sigma2 = sigma2;\n\r\n\t\tgl_FragColor = vec4(m3, 1.0);\n\r\n\t}\n\r\n}\n\r\n", - G.registerNodeType("texture/kuwahara", I), K.title = "XDoG Filter", K.desc = "Filters a texture giving an artistic ink style", K.max_radius = 10, K._shaders = [], K.prototype.onExecute = function() { + }, F.pixel_shader = "\n\r\nprecision highp float;\n\r\nvarying vec2 v_coord;\n\r\nuniform sampler2D u_texture;\n\r\nuniform float u_intensity;\n\r\nuniform vec2 u_resolution;\n\r\nuniform vec2 u_iResolution;\n\r\n#ifndef RADIUS\n\r\n\t#define RADIUS 7\n\r\n#endif\n\r\nvoid main() {\n\r\n\n\r\n\tconst int radius = RADIUS;\n\r\n\tvec2 fragCoord = v_coord;\n\r\n\tvec2 src_size = u_iResolution;\n\r\n\tvec2 uv = v_coord;\n\r\n\tfloat n = float((radius + 1) * (radius + 1));\n\r\n\tint i;\n\r\n\tint j;\n\r\n\tvec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\r\n\tvec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\r\n\tvec3 c;\n\r\n\t\n\r\n\tfor (int j = -radius; j <= 0; ++j) {\n\r\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\r\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\r\n\t\t\tm0 += c;\n\r\n\t\t\ts0 += c * c;\n\r\n\t\t}\n\r\n\t}\n\r\n\t\n\r\n\tfor (int j = -radius; j <= 0; ++j) {\n\r\n\t\tfor (int i = 0; i <= radius; ++i) {\n\r\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\r\n\t\t\tm1 += c;\n\r\n\t\t\ts1 += c * c;\n\r\n\t\t}\n\r\n\t}\n\r\n\t\n\r\n\tfor (int j = 0; j <= radius; ++j) {\n\r\n\t\tfor (int i = 0; i <= radius; ++i) {\n\r\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\r\n\t\t\tm2 += c;\n\r\n\t\t\ts2 += c * c;\n\r\n\t\t}\n\r\n\t}\n\r\n\t\n\r\n\tfor (int j = 0; j <= radius; ++j) {\n\r\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\r\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\r\n\t\t\tm3 += c;\n\r\n\t\t\ts3 += c * c;\n\r\n\t\t}\n\r\n\t}\n\r\n\t\n\r\n\tfloat min_sigma2 = 1e+2;\n\r\n\tm0 /= n;\n\r\n\ts0 = abs(s0 / n - m0 * m0);\n\r\n\t\n\r\n\tfloat sigma2 = s0.r + s0.g + s0.b;\n\r\n\tif (sigma2 < min_sigma2) {\n\r\n\t\tmin_sigma2 = sigma2;\n\r\n\t\tgl_FragColor = vec4(m0, 1.0);\n\r\n\t}\n\r\n\t\n\r\n\tm1 /= n;\n\r\n\ts1 = abs(s1 / n - m1 * m1);\n\r\n\t\n\r\n\tsigma2 = s1.r + s1.g + s1.b;\n\r\n\tif (sigma2 < min_sigma2) {\n\r\n\t\tmin_sigma2 = sigma2;\n\r\n\t\tgl_FragColor = vec4(m1, 1.0);\n\r\n\t}\n\r\n\t\n\r\n\tm2 /= n;\n\r\n\ts2 = abs(s2 / n - m2 * m2);\n\r\n\t\n\r\n\tsigma2 = s2.r + s2.g + s2.b;\n\r\n\tif (sigma2 < min_sigma2) {\n\r\n\t\tmin_sigma2 = sigma2;\n\r\n\t\tgl_FragColor = vec4(m2, 1.0);\n\r\n\t}\n\r\n\t\n\r\n\tm3 /= n;\n\r\n\ts3 = abs(s3 / n - m3 * m3);\n\r\n\t\n\r\n\tsigma2 = s3.r + s3.g + s3.b;\n\r\n\tif (sigma2 < min_sigma2) {\n\r\n\t\tmin_sigma2 = sigma2;\n\r\n\t\tgl_FragColor = vec4(m3, 1.0);\n\r\n\t}\n\r\n}\n\r\n", + H.registerNodeType("texture/kuwahara", F), L.title = "XDoG Filter", L.desc = "Filters a texture giving an artistic ink style", L.max_radius = 10, L._shaders = [], L.prototype.onExecute = function() { var a = this.getInputData(0); if (a && this.isOutputConnected(0)) { var b = this._temp_texture; b && b.width == a.width && b.height == a.height && b.type == a.type || (this._temp_texture = new GL.Texture(a.width, a.height, {type:a.type, format:gl.RGBA, filter:gl.LINEAR})); - K._xdog_shader || (K._xdog_shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, K.xdog_pixel_shader)); - var d = K._xdog_shader, c = GL.Mesh.getScreenQuad(), k = this.properties.sigma, f = this.properties.k, g = this.properties.p, e = this.properties.epsilon, n = this.properties.phi; + L._xdog_shader || (L._xdog_shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, L.xdog_pixel_shader)); + var d = L._xdog_shader, c = GL.Mesh.getScreenQuad(), h = this.properties.sigma, f = this.properties.k, e = this.properties.p, m = this.properties.epsilon, k = this.properties.phi; a.bind(0); this._temp_texture.drawTo(function() { - d.uniforms({src:0, sigma:k, k:f, p:g, epsilon:e, phi:n, cvsWidth:a.width, cvsHeight:a.height}).draw(c); + d.uniforms({src:0, sigma:h, k:f, p:e, epsilon:m, phi:k, cvsWidth:a.width, cvsHeight:a.height}).draw(c); }); this.setOutputData(0, this._temp_texture); } - }, K.xdog_pixel_shader = "\n\r\nprecision highp float;\n\r\nuniform sampler2D src;\n\n\r\nuniform float cvsHeight;\n\r\nuniform float cvsWidth;\n\n\r\nuniform float sigma;\n\r\nuniform float k;\n\r\nuniform float p;\n\r\nuniform float epsilon;\n\r\nuniform float phi;\n\r\nvarying vec2 v_coord;\n\n\r\nfloat cosh(float val)\n\r\n{\n\r\n\tfloat tmp = exp(val);\n\r\n\tfloat cosH = (tmp + 1.0 / tmp) / 2.0;\n\r\n\treturn cosH;\n\r\n}\n\n\r\nfloat tanh(float val)\n\r\n{\n\r\n\tfloat tmp = exp(val);\n\r\n\tfloat tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\r\n\treturn tanH;\n\r\n}\n\n\r\nfloat sinh(float val)\n\r\n{\n\r\n\tfloat tmp = exp(val);\n\r\n\tfloat sinH = (tmp - 1.0 / tmp) / 2.0;\n\r\n\treturn sinH;\n\r\n}\n\n\r\nvoid main(void){\n\r\n\tvec3 destColor = vec3(0.0);\n\r\n\tfloat tFrag = 1.0 / cvsHeight;\n\r\n\tfloat sFrag = 1.0 / cvsWidth;\n\r\n\tvec2 Frag = vec2(sFrag,tFrag);\n\r\n\tvec2 uv = gl_FragCoord.st;\n\r\n\tfloat twoSigmaESquared = 2.0 * sigma * sigma;\n\r\n\tfloat twoSigmaRSquared = twoSigmaESquared * k * k;\n\r\n\tint halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\r\n\tconst int MAX_NUM_ITERATION = 99999;\n\r\n\tvec2 sum = vec2(0.0);\n\r\n\tvec2 norm = vec2(0.0);\n\n\r\n\tfor(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\r\n\t\tint i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\r\n\t\tint j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\r\n\t\tfloat d = length(vec2(i,j));\n\r\n\t\tvec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\r\n\t\t\t\t\t\t\texp( -d * d / twoSigmaRSquared ));\n\n\r\n\t\tvec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\r\n\t\tnorm += kernel;\n\r\n\t\tsum += kernel * L;\n\r\n\t}\n\n\r\n\tsum /= norm;\n\n\r\n\tfloat H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\r\n\tfloat edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\r\n\tdestColor = vec3(edge);\n\r\n\tgl_FragColor = vec4(destColor, 1.0);\n\r\n}", - G.registerNodeType("texture/xDoG", K), L.title = "Webcam", L.desc = "Webcam texture", L.is_webcam_open = !1, L.prototype.openStream = function() { + }, L.xdog_pixel_shader = "\n\r\nprecision highp float;\n\r\nuniform sampler2D src;\n\n\r\nuniform float cvsHeight;\n\r\nuniform float cvsWidth;\n\n\r\nuniform float sigma;\n\r\nuniform float k;\n\r\nuniform float p;\n\r\nuniform float epsilon;\n\r\nuniform float phi;\n\r\nvarying vec2 v_coord;\n\n\r\nfloat cosh(float val)\n\r\n{\n\r\n\tfloat tmp = exp(val);\n\r\n\tfloat cosH = (tmp + 1.0 / tmp) / 2.0;\n\r\n\treturn cosH;\n\r\n}\n\n\r\nfloat tanh(float val)\n\r\n{\n\r\n\tfloat tmp = exp(val);\n\r\n\tfloat tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\r\n\treturn tanH;\n\r\n}\n\n\r\nfloat sinh(float val)\n\r\n{\n\r\n\tfloat tmp = exp(val);\n\r\n\tfloat sinH = (tmp - 1.0 / tmp) / 2.0;\n\r\n\treturn sinH;\n\r\n}\n\n\r\nvoid main(void){\n\r\n\tvec3 destColor = vec3(0.0);\n\r\n\tfloat tFrag = 1.0 / cvsHeight;\n\r\n\tfloat sFrag = 1.0 / cvsWidth;\n\r\n\tvec2 Frag = vec2(sFrag,tFrag);\n\r\n\tvec2 uv = gl_FragCoord.st;\n\r\n\tfloat twoSigmaESquared = 2.0 * sigma * sigma;\n\r\n\tfloat twoSigmaRSquared = twoSigmaESquared * k * k;\n\r\n\tint halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\r\n\tconst int MAX_NUM_ITERATION = 99999;\n\r\n\tvec2 sum = vec2(0.0);\n\r\n\tvec2 norm = vec2(0.0);\n\n\r\n\tfor(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\r\n\t\tint i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\r\n\t\tint j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\r\n\t\tfloat d = length(vec2(i,j));\n\r\n\t\tvec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\r\n\t\t\t\t\t\t\texp( -d * d / twoSigmaRSquared ));\n\n\r\n\t\tvec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\r\n\t\tnorm += kernel;\n\r\n\t\tsum += kernel * L;\n\r\n\t}\n\n\r\n\tsum /= norm;\n\n\r\n\tfloat H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\r\n\tfloat edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\r\n\tdestColor = vec3(edge);\n\r\n\tgl_FragColor = vec4(destColor, 1.0);\n\r\n}", + H.registerNodeType("texture/xDoG", L), K.title = "Webcam", K.desc = "Webcam texture", K.is_webcam_open = !1, K.prototype.openStream = function() { if (navigator.getUserMedia) { this._waiting_confirmation = !0; navigator.mediaDevices.getUserMedia({audio:!1, video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this)).catch(function(b) { - L.is_webcam_open = !1; + K.is_webcam_open = !1; console.log("Webcam rejected", b); a._webcam_stream = !1; a.boxcolor = "red"; @@ -8185,7 +8205,7 @@ $jscomp.polyfill("Object.values", function(v) { }); var a = this; } - }, L.prototype.closeStream = function() { + }, K.prototype.closeStream = function() { if (this._webcam_stream) { var a = this._webcam_stream.getTracks(); if (a.length) { @@ -8193,23 +8213,23 @@ $jscomp.polyfill("Object.values", function(v) { a[b].stop(); } } - L.is_webcam_open = !1; + K.is_webcam_open = !1; this._video = this._webcam_stream = null; this.boxcolor = "black"; this.trigger("stream_closed"); } - }, L.prototype.streamReady = function(a) { + }, K.prototype.streamReady = function(a) { this._webcam_stream = a; this.boxcolor = "green"; var b = this._video; b || (b = document.createElement("video"), b.autoplay = !0, b.srcObject = a, this._video = b, b.onloadedmetadata = function(a) { - L.is_webcam_open = !0; + K.is_webcam_open = !0; console.log(a); }); this.trigger("stream_ready", b); - }, L.prototype.onPropertyChanged = function(a, b) { + }, K.prototype.onPropertyChanged = function(a, b) { "facingMode" == a && (this.properties.facingMode = b, this.closeStream(), this.openStream()); - }, L.prototype.onRemoved = function() { + }, K.prototype.onRemoved = function() { if (this._webcam_stream) { var a = this._webcam_stream.getTracks(); if (a.length) { @@ -8219,9 +8239,9 @@ $jscomp.polyfill("Object.values", function(v) { } this._video = this._webcam_stream = null; } - }, L.prototype.onDrawBackground = function(a) { + }, K.prototype.onDrawBackground = function(a) { this.flags.collapsed || 20 >= this.size[1] || !this._video || (a.save(), a.webgl ? this._video_texture && a.drawImage(this._video_texture, 0, 0, this.size[0], this.size[1]) : a.drawImage(this._video, 0, 0, this.size[0], this.size[1]), a.restore()); - }, L.prototype.onExecute = function() { + }, K.prototype.onExecute = function() { null != this._webcam_stream || this._waiting_confirmation || this.openStream(); if (this._video && this._video.videoWidth) { var a = this._video.videoWidth, b = this._video.videoHeight, d = this._video_texture; @@ -8242,9 +8262,9 @@ $jscomp.polyfill("Object.values", function(v) { } } } - }, L.prototype.onGetOutputs = function() { - return [["width", "number"], ["height", "number"], ["stream_ready", G.EVENT], ["stream_closed", G.EVENT], ["stream_error", G.EVENT]]; - }, G.registerNodeType("texture/webcam", L), D.title = "Lens FX", D.desc = "distortion and chromatic aberration", D.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, D.prototype.onGetInputs = function() { + }, K.prototype.onGetOutputs = function() { + return [["width", "number"], ["height", "number"], ["stream_ready", H.EVENT], ["stream_closed", H.EVENT], ["stream_error", H.EVENT]]; + }, H.registerNodeType("texture/webcam", K), D.title = "Lens FX", D.desc = "distortion and chromatic aberration", D.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, D.prototype.onGetInputs = function() { return [["enabled", "boolean"]]; }, D.prototype.onExecute = function() { var a = this.getInputData(0); @@ -8256,10 +8276,10 @@ $jscomp.polyfill("Object.values", function(v) { b && b.width == a.width && b.height == a.height && b.type == a.type || (b = this._temp_texture = new GL.Texture(a.width, a.height, {type:a.type, format:gl.RGBA, filter:gl.LINEAR})); var d = D._shader; d || (d = D._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, D.pixel_shader)); - var k = this.getInputData(1); - null == k && (k = this.properties.factor); + var h = this.getInputData(1); + null == h && (h = this.properties.factor); var f = this._uniforms; - f.u_factor = k; + f.u_factor = h; gl.disable(gl.DEPTH_TEST); b.drawTo(function() { a.bind(0); @@ -8269,123 +8289,123 @@ $jscomp.polyfill("Object.values", function(v) { } } }, D.pixel_shader = "precision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_factor;\n\r\n\t\tvec2 barrelDistortion(vec2 coord, float amt) {\n\r\n\t\t\tvec2 cc = coord - 0.5;\n\r\n\t\t\tfloat dist = dot(cc, cc);\n\r\n\t\t\treturn coord + cc * dist * amt;\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tfloat sat( float t )\n\r\n\t\t{\n\r\n\t\t\treturn clamp( t, 0.0, 1.0 );\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tfloat linterp( float t ) {\n\r\n\t\t\treturn sat( 1.0 - abs( 2.0*t - 1.0 ) );\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tfloat remap( float t, float a, float b ) {\n\r\n\t\t\treturn sat( (t - a) / (b - a) );\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tvec4 spectrum_offset( float t ) {\n\r\n\t\t\tvec4 ret;\n\r\n\t\t\tfloat lo = step(t,0.5);\n\r\n\t\t\tfloat hi = 1.0-lo;\n\r\n\t\t\tfloat w = linterp( remap( t, 1.0/6.0, 5.0/6.0 ) );\n\r\n\t\t\tret = vec4(lo,1.0,hi, 1.) * vec4(1.0-w, w, 1.0-w, 1.);\n\r\n\t\t\n\r\n\t\t\treturn pow( ret, vec4(1.0/2.2) );\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tconst float max_distort = 2.2;\n\r\n\t\tconst int num_iter = 12;\n\r\n\t\tconst float reci_num_iter_f = 1.0 / float(num_iter);\n\r\n\t\t\n\r\n\t\tvoid main()\n\r\n\t\t{\t\n\r\n\t\t\tvec2 uv=v_coord;\n\r\n\t\t\tvec4 sumcol = vec4(0.0);\n\r\n\t\t\tvec4 sumw = vec4(0.0);\t\n\r\n\t\t\tfor ( int i=0; i Math.abs(b)) { return c[1]; } a = (a - c[0]) / b; - return c[1] * (1.0 - a) + k[1] * a; + return c[1] * (1.0 - a) + h[1] * a; } } return 0; } - }, J.prototype.updateCurve = function() { + }, y.prototype.updateCurve = function() { for (var a = this._values, b = a.length / 4, d = this.properties.split_channels, c = 0; c < b; ++c) { if (d) { a[4 * c] = Math.clamp(255 * this.sampleCurve(c / b, this._points.R), 0, 255), a[4 * c + 1] = Math.clamp(255 * this.sampleCurve(c / b, this._points.G), 0, 255), a[4 * c + 2] = Math.clamp(255 * this.sampleCurve(c / b, this._points.B), 0, 255); } else { - var k = this.sampleCurve(c / b); - a[4 * c] = a[4 * c + 1] = a[4 * c + 2] = Math.clamp(255 * k, 0, 255); + var h = this.sampleCurve(c / b); + a[4 * c] = a[4 * c + 1] = a[4 * c + 2] = Math.clamp(255 * h, 0, 255); } a[4 * c + 3] = 255; } this._curve_texture || (this._curve_texture = new GL.Texture(256, 1, {format:gl.RGBA, magFilter:gl.LINEAR, wrap:gl.CLAMP_TO_EDGE})); this._curve_texture.uploadData(a, null, !0); - }, J.prototype.onSerialize = function(a) { + }, y.prototype.onSerialize = function(a) { var b = {}, d; for (d in this._points) { b[d] = this._points[d].concat(); } a.curves = b; - }, J.prototype.onConfigure = function(a) { + }, y.prototype.onConfigure = function(a) { this._points = a.curves; this.curve_editor && (curve_editor.points = this._points); this._must_update = !0; - }, J.prototype.onMouseDown = function(a, b, d) { + }, y.prototype.onMouseDown = function(a, b, d) { if (this.curve_editor) { return (a = this.curve_editor.onMouseDown([b[0], b[1] - this.curve_offset], d)) && this.captureInput(!0), a; } - }, J.prototype.onMouseMove = function(a, b, d) { + }, y.prototype.onMouseMove = function(a, b, d) { if (this.curve_editor) { return this.curve_editor.onMouseMove([b[0], b[1] - this.curve_offset], d); } - }, J.prototype.onMouseUp = function(a, b, d) { + }, y.prototype.onMouseUp = function(a, b, d) { if (this.curve_editor) { return this.curve_editor.onMouseUp([b[0], b[1] - this.curve_offset], d); } this.captureInput(!1); - }, J.channel_line_colors = {RGB:"#666", R:"#F33", G:"#3F3", B:"#33F"}, J.prototype.onDrawBackground = function(a, b) { + }, y.channel_line_colors = {RGB:"#666", R:"#F33", G:"#3F3", B:"#33F"}, y.prototype.onDrawBackground = function(a, b) { if (!this.flags.collapsed) { - this.curve_editor || (this.curve_editor = new G.CurveEditor(this._points.R)); + this.curve_editor || (this.curve_editor = new H.CurveEditor(this._points.R)); a.save(); a.translate(0, this.curve_offset); var d = this.widgets[1].value; - this.properties.split_channels ? ("RGB" == d && (this.widgets[1].value = d = "R", this.widgets[1].disabled = !1), this.curve_editor.points = this._points.R, this.curve_editor.draw(a, [this.size[0], this.size[1] - this.curve_offset], b, "#111", J.channel_line_colors.R, !0), a.globalCompositeOperation = "lighten", this.curve_editor.points = this._points.G, this.curve_editor.draw(a, [this.size[0], this.size[1] - this.curve_offset], b, null, J.channel_line_colors.G, !0), this.curve_editor.points = - this._points.B, this.curve_editor.draw(a, [this.size[0], this.size[1] - this.curve_offset], b, null, J.channel_line_colors.B, !0), a.globalCompositeOperation = "source-over") : (this.widgets[1].value = d = "RGB", this.widgets[1].disabled = !0); + this.properties.split_channels ? ("RGB" == d && (this.widgets[1].value = d = "R", this.widgets[1].disabled = !1), this.curve_editor.points = this._points.R, this.curve_editor.draw(a, [this.size[0], this.size[1] - this.curve_offset], b, "#111", y.channel_line_colors.R, !0), a.globalCompositeOperation = "lighten", this.curve_editor.points = this._points.G, this.curve_editor.draw(a, [this.size[0], this.size[1] - this.curve_offset], b, null, y.channel_line_colors.G, !0), this.curve_editor.points = + this._points.B, this.curve_editor.draw(a, [this.size[0], this.size[1] - this.curve_offset], b, null, y.channel_line_colors.B, !0), a.globalCompositeOperation = "source-over") : (this.widgets[1].value = d = "RGB", this.widgets[1].disabled = !0); this.curve_editor.points = this._points[d]; - this.curve_editor.draw(a, [this.size[0], this.size[1] - this.curve_offset], b, this.properties.split_channels ? null : "#111", J.channel_line_colors[d]); + this.curve_editor.draw(a, [this.size[0], this.size[1] - this.curve_offset], b, this.properties.split_channels ? null : "#111", y.channel_line_colors[d]); a.restore(); } - }, J.pixel_shader = "precision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_curve;\n\r\n\t\tuniform float u_range;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = texture2D( u_texture, v_coord ) * u_range;\n\r\n\t\t\tcolor.x = texture2D( u_curve, vec2( color.x, 0.5 ) ).x;\n\r\n\t\t\tcolor.y = texture2D( u_curve, vec2( color.y, 0.5 ) ).y;\n\r\n\t\t\tcolor.z = texture2D( u_curve, vec2( color.z, 0.5 ) ).z;\n\r\n\t\t\t//color.w = texture2D( u_curve, vec2( color.w, 0.5 ) ).w;\n\r\n\t\t\tgl_FragColor = color;\n\r\n\t\t}", - G.registerNodeType("texture/curve", J), N.title = "Exposition", N.desc = "Controls texture exposition", N.widgets_info = {exposition:{widget:"slider", min:0, max:3}, precision:{widget:"combo", values:c.MODE_VALUES}}, N.prototype.onExecute = function() { + }, y.pixel_shader = "precision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform sampler2D u_curve;\n\r\n\t\tuniform float u_range;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = texture2D( u_texture, v_coord ) * u_range;\n\r\n\t\t\tcolor.x = texture2D( u_curve, vec2( color.x, 0.5 ) ).x;\n\r\n\t\t\tcolor.y = texture2D( u_curve, vec2( color.y, 0.5 ) ).y;\n\r\n\t\t\tcolor.z = texture2D( u_curve, vec2( color.z, 0.5 ) ).z;\n\r\n\t\t\t//color.w = texture2D( u_curve, vec2( color.w, 0.5 ) ).w;\n\r\n\t\t\tgl_FragColor = color;\n\r\n\t\t}", + H.registerNodeType("texture/curve", y), P.title = "Exposition", P.desc = "Controls texture exposition", P.widgets_info = {exposition:{widget:"slider", min:0, max:3}, precision:{widget:"combo", values:c.MODE_VALUES}}, P.prototype.onExecute = function() { var a = this.getInputData(0); if (a && this.isOutputConnected(0)) { var b = this._temp_texture; b && b.width == a.width && b.height == a.height && b.type == a.type || (b = this._temp_texture = new GL.Texture(a.width, a.height, {type:a.type, format:gl.RGBA, filter:gl.LINEAR})); - var d = N._shader; - d || (d = N._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, N.pixel_shader)); + var d = P._shader; + d || (d = P._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, P.pixel_shader)); var c = this.getInputData(1); null != c && (this.properties.exposition = c); - var k = this._uniforms; + var h = this._uniforms; b.drawTo(function() { gl.disable(gl.DEPTH_TEST); a.bind(0); - d.uniforms(k).draw(GL.Mesh.getScreenQuad()); + d.uniforms(h).draw(GL.Mesh.getScreenQuad()); }); this.setOutputData(0, b); } - }, N.pixel_shader = "precision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_exposition;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = texture2D( u_texture, v_coord );\n\r\n\t\t\tgl_FragColor = vec4( color.xyz * u_exposition, color.a );\n\r\n\t\t}", G.registerNodeType("texture/exposition", N), M.title = "Tone Mapping", M.desc = "Applies Tone Mapping to convert from high to low", M.widgets_info = {precision:{widget:"combo", + }, P.pixel_shader = "precision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_exposition;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = texture2D( u_texture, v_coord );\n\r\n\t\t\tgl_FragColor = vec4( color.xyz * u_exposition, color.a );\n\r\n\t\t}", H.registerNodeType("texture/exposition", P), M.title = "Tone Mapping", M.desc = "Applies Tone Mapping to convert from high to low", M.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}}, M.prototype.onGetInputs = function() { return [["enabled", "boolean"]]; }, M.prototype.onExecute = function() { @@ -8398,97 +8418,97 @@ $jscomp.polyfill("Object.values", function(v) { b && b.width == a.width && b.height == a.height && b.type == a.type || (b = this._temp_texture = new GL.Texture(a.width, a.height, {type:a.type, format:gl.RGBA, filter:gl.LINEAR})); var d = this.getInputData(1); null == d && (d = this.properties.average_lum); - var k = this._uniforms, f = null; - d.constructor === Number ? (this.properties.average_lum = d, k.u_average_lum = this.properties.average_lum, f = M._shader, f || (f = M._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, M.pixel_shader))) : d.constructor === GL.Texture && (k.u_average_texture = d.bind(1), f = M._shader_texture, f || (f = M._shader_texture = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, M.pixel_shader, {AVG_TEXTURE:""}))); - k.u_lumwhite2 = this.properties.lum_white * this.properties.lum_white; - k.u_scale = this.properties.scale; - k.u_igamma = 1 / this.properties.gamma; + var h = this._uniforms, f = null; + d.constructor === Number ? (this.properties.average_lum = d, h.u_average_lum = this.properties.average_lum, f = M._shader, f || (f = M._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, M.pixel_shader))) : d.constructor === GL.Texture && (h.u_average_texture = d.bind(1), f = M._shader_texture, f || (f = M._shader_texture = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, M.pixel_shader, {AVG_TEXTURE:""}))); + h.u_lumwhite2 = this.properties.lum_white * this.properties.lum_white; + h.u_scale = this.properties.scale; + h.u_igamma = 1 / this.properties.gamma; gl.disable(gl.DEPTH_TEST); b.drawTo(function() { a.bind(0); - f.uniforms(k).draw(GL.Mesh.getScreenQuad()); + f.uniforms(h).draw(GL.Mesh.getScreenQuad()); }); this.setOutputData(0, this._temp_texture); } } }, M.pixel_shader = "precision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform float u_scale;\n\r\n\t\t#ifdef AVG_TEXTURE\n\r\n\t\t\tuniform sampler2D u_average_texture;\n\r\n\t\t#else\n\r\n\t\t\tuniform float u_average_lum;\n\r\n\t\t#endif\n\r\n\t\tuniform float u_lumwhite2;\n\r\n\t\tuniform float u_igamma;\n\r\n\t\tvec3 RGB2xyY (vec3 rgb)\n\r\n\t\t{\n\r\n\t\t\t const mat3 RGB2XYZ = mat3(0.4124, 0.3576, 0.1805,\n\r\n\t\t\t\t\t\t\t\t\t 0.2126, 0.7152, 0.0722,\n\r\n\t\t\t\t\t\t\t\t\t 0.0193, 0.1192, 0.9505);\n\r\n\t\t\tvec3 XYZ = RGB2XYZ * rgb;\n\r\n\t\t\t\n\r\n\t\t\tfloat f = (XYZ.x + XYZ.y + XYZ.z);\n\r\n\t\t\treturn vec3(XYZ.x / f,\n\r\n\t\t\t\t\t\tXYZ.y / f,\n\r\n\t\t\t\t\t\tXYZ.y);\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec4 color = texture2D( u_texture, v_coord );\n\r\n\t\t\tvec3 rgb = color.xyz;\n\r\n\t\t\tfloat average_lum = 0.0;\n\r\n\t\t\t#ifdef AVG_TEXTURE\n\r\n\t\t\t\tvec3 pixel = texture2D(u_average_texture,vec2(0.5)).xyz;\n\r\n\t\t\t\taverage_lum = (pixel.x + pixel.y + pixel.z) / 3.0;\n\r\n\t\t\t#else\n\r\n\t\t\t\taverage_lum = u_average_lum;\n\r\n\t\t\t#endif\n\r\n\t\t\t//Ld - this part of the code is the same for both versions\n\r\n\t\t\tfloat lum = dot(rgb, vec3(0.2126, 0.7152, 0.0722));\n\r\n\t\t\tfloat L = (u_scale / average_lum) * lum;\n\r\n\t\t\tfloat Ld = (L * (1.0 + L / u_lumwhite2)) / (1.0 + L);\n\r\n\t\t\t//first\n\r\n\t\t\t//vec3 xyY = RGB2xyY(rgb);\n\r\n\t\t\t//xyY.z *= Ld;\n\r\n\t\t\t//rgb = xyYtoRGB(xyY);\n\r\n\t\t\t//second\n\r\n\t\t\trgb = (rgb / lum) * Ld;\n\r\n\t\t\trgb = max(rgb,vec3(0.001));\n\r\n\t\t\trgb = pow( rgb, vec3( u_igamma ) );\n\r\n\t\t\tgl_FragColor = vec4( rgb, color.a );\n\r\n\t\t}", - G.registerNodeType("texture/tonemapping", M), P.title = "Perlin", P.desc = "Generates a perlin noise texture", P.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}, width:{type:"Number", precision:0, step:1}, height:{type:"Number", precision:0, step:1}, octaves:{type:"Number", precision:0, step:1, min:1, max:50}}, P.prototype.onGetInputs = function() { + H.registerNodeType("texture/tonemapping", M), O.title = "Perlin", O.desc = "Generates a perlin noise texture", O.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}, width:{type:"Number", precision:0, step:1}, height:{type:"Number", precision:0, step:1}, octaves:{type:"Number", precision:0, step:1, min:1, max:50}}, O.prototype.onGetInputs = function() { return [["seed", "Number"], ["persistence", "Number"], ["octaves", "Number"], ["scale", "Number"], ["amplitude", "Number"], ["offset", "vec2"]]; - }, P.prototype.onExecute = function() { + }, O.prototype.onExecute = function() { if (this.isOutputConnected(0)) { var a = this.properties.width | 0, b = this.properties.height | 0; 0 == a && (a = gl.viewport_data[2]); 0 == b && (b = gl.viewport_data[3]); - var d = c.getTextureType(this.properties.precision), k = this._texture; - k && k.width == a && k.height == b && k.type == d || (k = this._texture = new GL.Texture(a, b, {type:d, format:gl.RGB, filter:gl.LINEAR})); - var f = this.getInputOrProperty("persistence"), g = this.getInputOrProperty("octaves"), e = this.getInputOrProperty("offset"), n = this.getInputOrProperty("scale"), p = this.getInputOrProperty("amplitude"), h = this.getInputOrProperty("seed"); - d = "" + a + b + d + f + g + n + h + e[0] + e[1] + p; + var d = c.getTextureType(this.properties.precision), h = this._texture; + h && h.width == a && h.height == b && h.type == d || (h = this._texture = new GL.Texture(a, b, {type:d, format:gl.RGB, filter:gl.LINEAR})); + var f = this.getInputOrProperty("persistence"), e = this.getInputOrProperty("octaves"), m = this.getInputOrProperty("offset"), k = this.getInputOrProperty("scale"), n = this.getInputOrProperty("amplitude"), g = this.getInputOrProperty("seed"); + d = "" + a + b + d + f + e + k + g + m[0] + m[1] + n; if (d != this._key) { this._key = d; var l = this._uniforms; l.u_persistence = f; - l.u_octaves = g; - l.u_offset.set(e); - l.u_scale = n; - l.u_amplitude = p; - l.u_seed = 128 * h; + l.u_octaves = e; + l.u_offset.set(m); + l.u_scale = k; + l.u_amplitude = n; + l.u_seed = 128 * g; l.u_viewport[0] = a; l.u_viewport[1] = b; - var m = P._shader; - m || (m = P._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, P.pixel_shader)); + var p = O._shader; + p || (p = O._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, O.pixel_shader)); gl.disable(gl.BLEND); gl.disable(gl.DEPTH_TEST); - k.drawTo(function() { - m.uniforms(l).draw(GL.Mesh.getScreenQuad()); + h.drawTo(function() { + p.uniforms(l).draw(GL.Mesh.getScreenQuad()); }); } - this.setOutputData(0, k); + this.setOutputData(0, h); } - }, P.pixel_shader = "precision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform vec2 u_offset;\n\r\n\t\tuniform float u_scale;\n\r\n\t\tuniform float u_persistence;\n\r\n\t\tuniform int u_octaves;\n\r\n\t\tuniform float u_amplitude;\n\r\n\t\tuniform vec2 u_viewport;\n\r\n\t\tuniform float u_seed;\n\r\n\t\t#define M_PI 3.14159265358979323846\n\r\n\t\t\n\r\n\t\tfloat rand(vec2 c){\treturn fract(sin(dot(c.xy ,vec2( 12.9898 + u_seed,78.233 + u_seed))) * 43758.5453); }\n\r\n\t\t\n\r\n\t\tfloat noise(vec2 p, float freq ){\n\r\n\t\t\tfloat unit = u_viewport.x/freq;\n\r\n\t\t\tvec2 ij = floor(p/unit);\n\r\n\t\t\tvec2 xy = mod(p,unit)/unit;\n\r\n\t\t\t//xy = 3.*xy*xy-2.*xy*xy*xy;\n\r\n\t\t\txy = .5*(1.-cos(M_PI*xy));\n\r\n\t\t\tfloat a = rand((ij+vec2(0.,0.)));\n\r\n\t\t\tfloat b = rand((ij+vec2(1.,0.)));\n\r\n\t\t\tfloat c = rand((ij+vec2(0.,1.)));\n\r\n\t\t\tfloat d = rand((ij+vec2(1.,1.)));\n\r\n\t\t\tfloat x1 = mix(a, b, xy.x);\n\r\n\t\t\tfloat x2 = mix(c, d, xy.x);\n\r\n\t\t\treturn mix(x1, x2, xy.y);\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tfloat pNoise(vec2 p, int res){\n\r\n\t\t\tfloat persistance = u_persistence;\n\r\n\t\t\tfloat n = 0.;\n\r\n\t\t\tfloat normK = 0.;\n\r\n\t\t\tfloat f = 4.;\n\r\n\t\t\tfloat amp = 1.0;\n\r\n\t\t\tint iCount = 0;\n\r\n\t\t\tfor (int i = 0; i<50; i++){\n\r\n\t\t\t\tn+=amp*noise(p, f);\n\r\n\t\t\t\tf*=2.;\n\r\n\t\t\t\tnormK+=amp;\n\r\n\t\t\t\tamp*=persistance;\n\r\n\t\t\t\tif (iCount >= res)\n\r\n\t\t\t\t\tbreak;\n\r\n\t\t\t\tiCount++;\n\r\n\t\t\t}\n\r\n\t\t\tfloat nf = n/normK;\n\r\n\t\t\treturn nf*nf*nf*nf;\n\r\n\t\t}\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec2 uv = v_coord * u_scale * u_viewport + u_offset * u_scale;\n\r\n\t\t\tvec4 color = vec4( pNoise( uv, u_octaves ) * u_amplitude );\n\r\n\t\t\tgl_FragColor = color;\n\r\n\t\t}", - G.registerNodeType("texture/perlin", P), O.title = "Canvas2D", O.desc = "Executes Canvas2D code inside a texture or the viewport.", O.help = "Set width and height to 0 to match viewport size.", O.default_code = "//vars: canvas,ctx,time\nctx.fillStyle='red';\nctx.fillRect(0,0,50,50);\n", O.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}, code:{type:"code"}, width:{type:"Number", precision:0, step:1}, height:{type:"Number", precision:0, step:1}}, O.prototype.onPropertyChanged = function(a, + }, O.pixel_shader = "precision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform vec2 u_offset;\n\r\n\t\tuniform float u_scale;\n\r\n\t\tuniform float u_persistence;\n\r\n\t\tuniform int u_octaves;\n\r\n\t\tuniform float u_amplitude;\n\r\n\t\tuniform vec2 u_viewport;\n\r\n\t\tuniform float u_seed;\n\r\n\t\t#define M_PI 3.14159265358979323846\n\r\n\t\t\n\r\n\t\tfloat rand(vec2 c){\treturn fract(sin(dot(c.xy ,vec2( 12.9898 + u_seed,78.233 + u_seed))) * 43758.5453); }\n\r\n\t\t\n\r\n\t\tfloat noise(vec2 p, float freq ){\n\r\n\t\t\tfloat unit = u_viewport.x/freq;\n\r\n\t\t\tvec2 ij = floor(p/unit);\n\r\n\t\t\tvec2 xy = mod(p,unit)/unit;\n\r\n\t\t\t//xy = 3.*xy*xy-2.*xy*xy*xy;\n\r\n\t\t\txy = .5*(1.-cos(M_PI*xy));\n\r\n\t\t\tfloat a = rand((ij+vec2(0.,0.)));\n\r\n\t\t\tfloat b = rand((ij+vec2(1.,0.)));\n\r\n\t\t\tfloat c = rand((ij+vec2(0.,1.)));\n\r\n\t\t\tfloat d = rand((ij+vec2(1.,1.)));\n\r\n\t\t\tfloat x1 = mix(a, b, xy.x);\n\r\n\t\t\tfloat x2 = mix(c, d, xy.x);\n\r\n\t\t\treturn mix(x1, x2, xy.y);\n\r\n\t\t}\n\r\n\t\t\n\r\n\t\tfloat pNoise(vec2 p, int res){\n\r\n\t\t\tfloat persistance = u_persistence;\n\r\n\t\t\tfloat n = 0.;\n\r\n\t\t\tfloat normK = 0.;\n\r\n\t\t\tfloat f = 4.;\n\r\n\t\t\tfloat amp = 1.0;\n\r\n\t\t\tint iCount = 0;\n\r\n\t\t\tfor (int i = 0; i<50; i++){\n\r\n\t\t\t\tn+=amp*noise(p, f);\n\r\n\t\t\t\tf*=2.;\n\r\n\t\t\t\tnormK+=amp;\n\r\n\t\t\t\tamp*=persistance;\n\r\n\t\t\t\tif (iCount >= res)\n\r\n\t\t\t\t\tbreak;\n\r\n\t\t\t\tiCount++;\n\r\n\t\t\t}\n\r\n\t\t\tfloat nf = n/normK;\n\r\n\t\t\treturn nf*nf*nf*nf;\n\r\n\t\t}\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec2 uv = v_coord * u_scale * u_viewport + u_offset * u_scale;\n\r\n\t\t\tvec4 color = vec4( pNoise( uv, u_octaves ) * u_amplitude );\n\r\n\t\t\tgl_FragColor = color;\n\r\n\t\t}", + H.registerNodeType("texture/perlin", O), N.title = "Canvas2D", N.desc = "Executes Canvas2D code inside a texture or the viewport.", N.help = "Set width and height to 0 to match viewport size.", N.default_code = "//vars: canvas,ctx,time\nctx.fillStyle='red';\nctx.fillRect(0,0,50,50);\n", N.widgets_info = {precision:{widget:"combo", values:c.MODE_VALUES}, code:{type:"code"}, width:{type:"Number", precision:0, step:1}, height:{type:"Number", precision:0, step:1}}, N.prototype.onPropertyChanged = function(a, b) { "code" == a && this.compileCode(b); - }, O.prototype.compileCode = function(a) { + }, N.prototype.compileCode = function(a) { this._func = null; - if (G.allow_scripts) { + if (H.allow_scripts) { try { this._func = new Function("canvas", "ctx", "time", "script", "v", a), this.boxcolor = "#00FF00"; } catch (W) { this.boxcolor = "#FF0000", console.error("Error parsing script"), console.error(W); } } - }, O.prototype.onExecute = function() { + }, N.prototype.onExecute = function() { var a = this._func; a && this.isOutputConnected(0) && this.executeDraw(a); - }, O.prototype.executeDraw = function(a) { - var b = this.properties.width || gl.canvas.width, d = this.properties.height || gl.canvas.height, k = this._temp_texture, f = c.getTextureType(this.properties.precision); - k && k.width == b && k.height == d && k.type == f || (k = this._temp_texture = new GL.Texture(b, d, {format:gl.RGBA, filter:gl.LINEAR, type:f})); - var g = this.getInputData(0), e = this.properties, n = this, p = this.graph.getTime(), h = gl, l = gl.canvas; - if (this.properties.use_html_canvas || !v.enableWebGLCanvas) { - this._canvas ? (l = this._canvas, h = this._ctx) : (l = this._canvas = createCanvas(b.height), h = this._ctx = l.getContext("2d")), l.width = b, l.height = d; + }, N.prototype.executeDraw = function(a) { + var b = this.properties.width || gl.canvas.width, d = this.properties.height || gl.canvas.height, h = this._temp_texture, f = c.getTextureType(this.properties.precision); + h && h.width == b && h.height == d && h.type == f || (h = this._temp_texture = new GL.Texture(b, d, {format:gl.RGBA, filter:gl.LINEAR, type:f})); + var e = this.getInputData(0), m = this.properties, k = this, n = this.graph.getTime(), g = gl, l = gl.canvas; + if (this.properties.use_html_canvas || !w.enableWebGLCanvas) { + this._canvas ? (l = this._canvas, g = this._ctx) : (l = this._canvas = createCanvas(b.height), g = this._ctx = l.getContext("2d")), l.width = b, l.height = d; } - if (h == gl) { - k.drawTo(function() { + if (g == gl) { + h.drawTo(function() { gl.start2D(); - e.clear && (gl.clearColor(0, 0, 0, 0), gl.clear(gl.COLOR_BUFFER_BIT)); + m.clear && (gl.clearColor(0, 0, 0, 0), gl.clear(gl.COLOR_BUFFER_BIT)); try { - a.draw ? a.draw.call(n, l, h, p, a, g) : a.call(n, l, h, p, a, g), n.boxcolor = "#00FF00"; - } catch (T) { - n.boxcolor = "#FF0000", console.error("Error executing script"), console.error(T); + a.draw ? a.draw.call(k, l, g, n, a, e) : a.call(k, l, g, n, a, e), k.boxcolor = "#00FF00"; + } catch (S) { + k.boxcolor = "#FF0000", console.error("Error executing script"), console.error(S); } gl.finish2D(); }); } else { - e.clear && h.clearRect(0, 0, l.width, l.height); + m.clear && g.clearRect(0, 0, l.width, l.height); try { - a.draw ? a.draw.call(this, l, h, p, a, g) : a.call(this, l, h, p, a, g), this.boxcolor = "#00FF00"; - } catch (T) { - this.boxcolor = "#FF0000", console.error("Error executing script"), console.error(T); + a.draw ? a.draw.call(this, l, g, n, a, e) : a.call(this, l, g, n, a, e), this.boxcolor = "#00FF00"; + } catch (S) { + this.boxcolor = "#FF0000", console.error("Error executing script"), console.error(S); } - k.uploadImage(l); + h.uploadImage(l); } - this.setOutputData(0, k); - }, G.registerNodeType("texture/canvas2D", O), Q.title = "Matte", Q.desc = "Extracts background", Q.widgets_info = {key_color:{widget:"color"}, precision:{widget:"combo", values:c.MODE_VALUES}}, Q.prototype.onExecute = function() { + this.setOutputData(0, h); + }, H.registerNodeType("texture/canvas2D", N), Q.title = "Matte", Q.desc = "Extracts background", Q.widgets_info = {key_color:{widget:"color"}, precision:{widget:"combo", values:c.MODE_VALUES}}, Q.prototype.onExecute = function() { if (this.isOutputConnected(0)) { var a = this.getInputData(0); if (this.properties.precision === c.PASS_THROUGH) { @@ -8499,21 +8519,21 @@ $jscomp.polyfill("Object.values", function(v) { gl.disable(gl.BLEND); gl.disable(gl.DEPTH_TEST); this._uniforms || (this._uniforms = {u_texture:0, u_key_color:this.properties.key_color, u_threshold:1, u_slope:1}); - var b = this._uniforms, d = Mesh.getScreenQuad(), k = Q._shader; - k || (k = Q._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, Q.pixel_shader)); + var b = this._uniforms, d = Mesh.getScreenQuad(), h = Q._shader; + h || (h = Q._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, Q.pixel_shader)); b.u_key_color = this.properties.key_color; b.u_threshold = this.properties.threshold; b.u_slope = this.properties.slope; this._tex.drawTo(function() { a.bind(0); - k.uniforms(b).draw(d); + h.uniforms(b).draw(d); }); this.setOutputData(0, this._tex); } } } }, Q.pixel_shader = "precision highp float;\n\r\n\t\tvarying vec2 v_coord;\n\r\n\t\tuniform sampler2D u_texture;\n\r\n\t\tuniform vec3 u_key_color;\n\r\n\t\tuniform float u_threshold;\n\r\n\t\tuniform float u_slope;\n\r\n\t\t\n\r\n\t\tvoid main() {\n\r\n\t\t\tvec3 color = texture2D( u_texture, v_coord ).xyz;\n\r\n\t\t\tfloat diff = length( normalize(color) - normalize(u_key_color) );\n\r\n\t\t\tfloat edge = u_threshold * (1.0 - u_slope);\n\r\n\t\t\tfloat alpha = smoothstep( edge, u_threshold, diff);\n\r\n\t\t\tgl_FragColor = vec4( color, alpha );\n\r\n\t\t}", - G.registerNodeType("texture/matte", Q), R.title = "CubemapToTexture2D", R.desc = "Transforms a CUBEMAP texture into a TEXTURE2D in Polar Representation", R.prototype.onExecute = function() { + H.registerNodeType("texture/matte", Q), R.title = "CubemapToTexture2D", R.desc = "Transforms a CUBEMAP texture into a TEXTURE2D in Polar Representation", R.prototype.onExecute = function() { if (this.isOutputConnected(0)) { var a = this.getInputData(0); if (a && a.texture_type == GL.TEXTURE_CUBE_MAP) { @@ -8523,23 +8543,23 @@ $jscomp.polyfill("Object.values", function(v) { this.setOutputData(0, this._last_tex); } } - }, G.registerNodeType("texture/cubemapToTexture2D", R)); + }, H.registerNodeType("texture/cubemapToTexture2D", R)); })(this); -(function(v) { - var c = v.LiteGraph, r = v.LGraphTexture; +(function(w) { + var c = w.LiteGraph, t = w.LGraphTexture; if ("undefined" != typeof GL) { - var m = function() { + var l = function() { this.addInput("Tex.", "Texture"); this.addInput("intensity", "number"); this.addOutput("Texture", "Texture"); - this.properties = {intensity:1, invert:!1, precision:r.DEFAULT}; - m._shader || (m._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, m.pixel_shader)); - }, h = function() { + this.properties = {intensity:1, invert:!1, precision:t.DEFAULT}; + l._shader || (l._shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, l.pixel_shader)); + }, p = function() { this.addInput("Texture", "Texture"); this.addInput("value1", "number"); this.addInput("value2", "number"); this.addOutput("Texture", "Texture"); - this.properties = {fx:"halftone", value1:1, value2:1, precision:r.DEFAULT}; + this.properties = {fx:"halftone", value1:1, value2:1, precision:t.DEFAULT}; }, q = function() { this.addInput("Texture", "Texture"); this.addInput("Blurred", "Texture"); @@ -8547,80 +8567,80 @@ $jscomp.polyfill("Object.values", function(v) { this.addInput("Threshold", "number"); this.addOutput("Texture", "Texture"); this.properties = {shape:"", size:10, alpha:1.0, threshold:1.0, high_precision:!1}; - }, l = function() { + }, g = function() { this.addInput("Texture", "Texture"); this.addInput("Aberration", "number"); this.addInput("Distortion", "number"); this.addInput("Blur", "number"); this.addOutput("Texture", "Texture"); - this.properties = {aberration:1.0, distortion:1.0, blur:1.0, precision:r.DEFAULT}; - l._shader || (l._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, l.pixel_shader), l._texture = new GL.Texture(3, 1, {format:gl.RGB, wrap:gl.CLAMP_TO_EDGE, magFilter:gl.LINEAR, minFilter:gl.LINEAR, pixel_data:[255, 0, 0, 0, 255, 0, 0, 0, 255]})); + this.properties = {aberration:1.0, distortion:1.0, blur:1.0, precision:t.DEFAULT}; + g._shader || (g._shader = new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, g.pixel_shader), g._texture = new GL.Texture(3, 1, {format:gl.RGB, wrap:gl.CLAMP_TO_EDGE, magFilter:gl.LINEAR, minFilter:gl.LINEAR, pixel_data:[255, 0, 0, 0, 255, 0, 0, 0, 255]})); }; - l.title = "Lens"; - l.desc = "Camera Lens distortion"; - l.widgets_info = {precision:{widget:"combo", values:r.MODE_VALUES}}; - l.prototype.onExecute = function() { + g.title = "Lens"; + g.desc = "Camera Lens distortion"; + g.widgets_info = {precision:{widget:"combo", values:t.MODE_VALUES}}; + g.prototype.onExecute = function() { var c = this.getInputData(0); - if (this.properties.precision === r.PASS_THROUGH) { + if (this.properties.precision === t.PASS_THROUGH) { this.setOutputData(0, c); } else { if (c) { - this._tex = r.getTargetTexture(c, this._tex, this.properties.precision); - var h = this.properties.aberration; - this.isInputConnected(1) && (h = this.getInputData(1), this.properties.aberration = h); - var m = this.properties.distortion; - this.isInputConnected(2) && (m = this.getInputData(2), this.properties.distortion = m); + this._tex = t.getTargetTexture(c, this._tex, this.properties.precision); + var l = this.properties.aberration; + this.isInputConnected(1) && (l = this.getInputData(1), this.properties.aberration = l); + var p = this.properties.distortion; + this.isInputConnected(2) && (p = this.getInputData(2), this.properties.distortion = p); var q = this.properties.blur; this.isInputConnected(3) && (q = this.getInputData(3), this.properties.blur = q); gl.disable(gl.BLEND); gl.disable(gl.DEPTH_TEST); - var v = Mesh.getScreenQuad(), e = l._shader; + var w = Mesh.getScreenQuad(), r = g._shader; this._tex.drawTo(function() { c.bind(0); - e.uniforms({u_texture:0, u_aberration:h, u_distortion:m, u_blur:q}).draw(v); + r.uniforms({u_texture:0, u_aberration:l, u_distortion:p, u_blur:q}).draw(w); }); this.setOutputData(0, this._tex); } } }; - l.pixel_shader = "precision highp float;\n\r\n\t\t\tprecision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform vec2 u_camera_planes;\n\r\n\t\t\tuniform float u_aberration;\n\r\n\t\t\tuniform float u_distortion;\n\r\n\t\t\tuniform float u_blur;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec2 coord = v_coord;\n\r\n\t\t\t\tfloat dist = distance(vec2(0.5), coord);\n\r\n\t\t\t\tvec2 dist_coord = coord - vec2(0.5);\n\r\n\t\t\t\tfloat percent = 1.0 + ((0.5 - dist) / 0.5) * u_distortion;\n\r\n\t\t\t\tdist_coord *= percent;\n\r\n\t\t\t\tcoord = dist_coord + vec2(0.5);\n\r\n\t\t\t\tvec4 color = texture2D(u_texture,coord, u_blur * dist);\n\r\n\t\t\t\tcolor.r = texture2D(u_texture,vec2(0.5) + dist_coord * (1.0+0.01*u_aberration), u_blur * dist ).r;\n\r\n\t\t\t\tcolor.b = texture2D(u_texture,vec2(0.5) + dist_coord * (1.0-0.01*u_aberration), u_blur * dist ).b;\n\r\n\t\t\t\tgl_FragColor = color;\n\r\n\t\t\t}\n\r\n\t\t\t"; - c.registerNodeType("fx/lens", l); - v.LGraphFXLens = l; + g.pixel_shader = "precision highp float;\n\r\n\t\t\tprecision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform vec2 u_camera_planes;\n\r\n\t\t\tuniform float u_aberration;\n\r\n\t\t\tuniform float u_distortion;\n\r\n\t\t\tuniform float u_blur;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec2 coord = v_coord;\n\r\n\t\t\t\tfloat dist = distance(vec2(0.5), coord);\n\r\n\t\t\t\tvec2 dist_coord = coord - vec2(0.5);\n\r\n\t\t\t\tfloat percent = 1.0 + ((0.5 - dist) / 0.5) * u_distortion;\n\r\n\t\t\t\tdist_coord *= percent;\n\r\n\t\t\t\tcoord = dist_coord + vec2(0.5);\n\r\n\t\t\t\tvec4 color = texture2D(u_texture,coord, u_blur * dist);\n\r\n\t\t\t\tcolor.r = texture2D(u_texture,vec2(0.5) + dist_coord * (1.0+0.01*u_aberration), u_blur * dist ).r;\n\r\n\t\t\t\tcolor.b = texture2D(u_texture,vec2(0.5) + dist_coord * (1.0-0.01*u_aberration), u_blur * dist ).b;\n\r\n\t\t\t\tgl_FragColor = color;\n\r\n\t\t\t}\n\r\n\t\t\t"; + c.registerNodeType("fx/lens", g); + w.LGraphFXLens = g; q.title = "Bokeh"; q.desc = "applies an Bokeh effect"; q.widgets_info = {shape:{widget:"texture"}}; q.prototype.onExecute = function() { - var c = this.getInputData(0), h = this.getInputData(1), l = this.getInputData(2); + var c = this.getInputData(0), g = this.getInputData(1), l = this.getInputData(2); if (c && l && this.properties.shape) { - h || (h = c); - var m = r.getTexture(this.properties.shape); - if (m) { - var v = this.properties.threshold; - this.isInputConnected(3) && (v = this.getInputData(3), this.properties.threshold = v); - var e = gl.UNSIGNED_BYTE; - this.properties.high_precision && (e = gl.half_float_ext ? gl.HALF_FLOAT_OES : gl.FLOAT); - this._temp_texture && this._temp_texture.type == e && this._temp_texture.width == c.width && this._temp_texture.height == c.height || (this._temp_texture = new GL.Texture(c.width, c.height, {type:e, format:gl.RGBA, filter:gl.LINEAR})); - var B = q._first_shader; - B || (B = q._first_shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, q._first_pixel_shader)); - var E = q._second_shader; - E || (E = q._second_shader = new GL.Shader(q._second_vertex_shader, q._second_pixel_shader)); + g || (g = c); + var p = t.getTexture(this.properties.shape); + if (p) { + var w = this.properties.threshold; + this.isInputConnected(3) && (w = this.getInputData(3), this.properties.threshold = w); + var r = gl.UNSIGNED_BYTE; + this.properties.high_precision && (r = gl.half_float_ext ? gl.HALF_FLOAT_OES : gl.FLOAT); + this._temp_texture && this._temp_texture.type == r && this._temp_texture.width == c.width && this._temp_texture.height == c.height || (this._temp_texture = new GL.Texture(c.width, c.height, {type:r, format:gl.RGBA, filter:gl.LINEAR})); + var e = q._first_shader; + e || (e = q._first_shader = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, q._first_pixel_shader)); + var J = q._second_shader; + J || (J = q._second_shader = new GL.Shader(q._second_vertex_shader, q._second_pixel_shader)); var u = this._points_mesh; u && u._width == c.width && u._height == c.height && 2 == u._spacing || (u = this.createPointsMesh(c.width, c.height, 2)); - var H = Mesh.getScreenQuad(), n = this.properties.size, p = this.properties.alpha; + var C = Mesh.getScreenQuad(), m = this.properties.size, n = this.properties.alpha; gl.disable(gl.DEPTH_TEST); gl.disable(gl.BLEND); this._temp_texture.drawTo(function() { c.bind(0); - h.bind(1); + g.bind(1); l.bind(2); - B.uniforms({u_texture:0, u_texture_blur:1, u_mask:2, u_texsize:[c.width, c.height]}).draw(H); + e.uniforms({u_texture:0, u_texture_blur:1, u_mask:2, u_texsize:[c.width, c.height]}).draw(C); }); this._temp_texture.drawTo(function() { gl.enable(gl.BLEND); gl.blendFunc(gl.ONE, gl.ONE); c.bind(0); - m.bind(3); - E.uniforms({u_texture:0, u_mask:2, u_shape:3, u_alpha:p, u_threshold:v, u_pointSize:n, u_itexsize:[1.0 / c.width, 1.0 / c.height]}).draw(u, gl.POINTS); + p.bind(3); + J.uniforms({u_texture:0, u_mask:2, u_shape:3, u_alpha:n, u_threshold:w, u_pointSize:m, u_itexsize:[1.0 / c.width, 1.0 / c.height]}).draw(u, gl.POINTS); }); this.setOutputData(0, this._temp_texture); } @@ -8628,19 +8648,19 @@ $jscomp.polyfill("Object.values", function(v) { this.setOutputData(0, c); } }; - q.prototype.createPointsMesh = function(c, h, l) { - for (var m = Math.round(c / l), q = Math.round(h / l), e = new Float32Array(m * q * 2), r = -1, x = 2 / c * l, u = 2 / h * l, v = 0; v < q; ++v) { - for (var n = -1, p = 0; p < m; ++p) { - var k = v * m * 2 + 2 * p; - e[k] = n; - e[k + 1] = r; - n += x; + q.prototype.createPointsMesh = function(c, g, l) { + for (var p = Math.round(c / l), q = Math.round(g / l), r = new Float32Array(p * q * 2), e = -1, t = 2 / c * l, u = 2 / g * l, v = 0; v < q; ++v) { + for (var m = -1, n = 0; n < p; ++n) { + var h = v * p * 2 + 2 * n; + r[h] = m; + r[h + 1] = e; + m += t; } - r += u; + e += u; } - this._points_mesh = GL.Mesh.load({vertices2D:e}); + this._points_mesh = GL.Mesh.load({vertices2D:r}); this._points_mesh._width = c; - this._points_mesh._height = h; + this._points_mesh._height = g; this._points_mesh._spacing = l; return this._points_mesh; }; @@ -8648,92 +8668,92 @@ $jscomp.polyfill("Object.values", function(v) { q._second_vertex_shader = "precision highp float;\n\r\n\t\t\tattribute vec2 a_vertex2D;\n\r\n\t\t\tvarying vec4 v_color;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform sampler2D u_mask;\n\r\n\t\t\tuniform vec2 u_itexsize;\n\r\n\t\t\tuniform float u_pointSize;\n\r\n\t\t\tuniform float u_threshold;\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec2 coord = a_vertex2D * 0.5 + 0.5;\n\r\n\t\t\t\tv_color = texture2D( u_texture, coord );\n\r\n\t\t\t\tv_color += texture2D( u_texture, coord + vec2(u_itexsize.x, 0.0) );\n\r\n\t\t\t\tv_color += texture2D( u_texture, coord + vec2(0.0, u_itexsize.y));\n\r\n\t\t\t\tv_color += texture2D( u_texture, coord + u_itexsize);\n\r\n\t\t\t\tv_color *= 0.25;\n\r\n\t\t\t\tfloat mask = texture2D(u_mask, coord).x;\n\r\n\t\t\t\tfloat luminance = length(v_color) * mask;\n\r\n\t\t\t\t/*luminance /= (u_pointSize*u_pointSize)*0.01 */;\n\r\n\t\t\t\tluminance -= u_threshold;\n\r\n\t\t\t\tif(luminance < 0.0)\n\r\n\t\t\t\t{\n\r\n\t\t\t\t\tgl_Position.x = -100.0;\n\r\n\t\t\t\t\treturn;\n\r\n\t\t\t\t}\n\r\n\t\t\t\tgl_PointSize = u_pointSize;\n\r\n\t\t\t\tgl_Position = vec4(a_vertex2D,0.0,1.0);\n\r\n\t\t\t}\n\r\n\t\t\t"; q._second_pixel_shader = "precision highp float;\n\r\n\t\t\tvarying vec4 v_color;\n\r\n\t\t\tuniform sampler2D u_shape;\n\r\n\t\t\tuniform float u_alpha;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec4 color = texture2D( u_shape, gl_PointCoord );\n\r\n\t\t\t\tcolor *= v_color * u_alpha;\n\r\n\t\t\t\tgl_FragColor = color;\n\r\n\t\t\t}\n"; c.registerNodeType("fx/bokeh", q); - v.LGraphFXBokeh = q; - h.title = "FX"; - h.desc = "applies an FX from a list"; - h.widgets_info = {fx:{widget:"combo", values:["halftone", "pixelate", "lowpalette", "noise", "gamma"]}, precision:{widget:"combo", values:r.MODE_VALUES}}; - h.shaders = {}; - h.prototype.onExecute = function() { + w.LGraphFXBokeh = q; + p.title = "FX"; + p.desc = "applies an FX from a list"; + p.widgets_info = {fx:{widget:"combo", values:["halftone", "pixelate", "lowpalette", "noise", "gamma"]}, precision:{widget:"combo", values:t.MODE_VALUES}}; + p.shaders = {}; + p.prototype.onExecute = function() { if (this.isOutputConnected(0)) { var c = this.getInputData(0); - if (this.properties.precision === r.PASS_THROUGH) { + if (this.properties.precision === t.PASS_THROUGH) { this.setOutputData(0, c); } else { if (c) { - this._tex = r.getTargetTexture(c, this._tex, this.properties.precision); - var l = this.properties.value1; - this.isInputConnected(1) && (l = this.getInputData(1), this.properties.value1 = l); - var m = this.properties.value2; - this.isInputConnected(2) && (m = this.getInputData(2), this.properties.value2 = m); - var q = this.properties.fx, z = h.shaders[q]; + this._tex = t.getTargetTexture(c, this._tex, this.properties.precision); + var g = this.properties.value1; + this.isInputConnected(1) && (g = this.getInputData(1), this.properties.value1 = g); + var l = this.properties.value2; + this.isInputConnected(2) && (l = this.getInputData(2), this.properties.value2 = l); + var q = this.properties.fx, z = p.shaders[q]; if (!z) { - var e = h["pixel_shader_" + q]; - if (!e) { + var r = p["pixel_shader_" + q]; + if (!r) { return; } - z = h.shaders[q] = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, e); + z = p.shaders[q] = new GL.Shader(Shader.SCREEN_VERTEX_SHADER, r); } gl.disable(gl.BLEND); gl.disable(gl.DEPTH_TEST); - var B = Mesh.getScreenQuad(); - var E = v.LS && LS.Renderer._current_camera ? [LS.Renderer._current_camera.near, LS.Renderer._current_camera.far] : [1, 100]; + var e = Mesh.getScreenQuad(); + var J = w.LS && LS.Renderer._current_camera ? [LS.Renderer._current_camera.near, LS.Renderer._current_camera.far] : [1, 100]; var u = null; - "noise" == q && (u = r.getNoiseTexture()); + "noise" == q && (u = t.getNoiseTexture()); this._tex.drawTo(function() { c.bind(0); "noise" == q && u.bind(1); - z.uniforms({u_texture:0, u_noise:1, u_size:[c.width, c.height], u_rand:[Math.random(), Math.random()], u_value1:l, u_value2:m, u_camera_planes:E}).draw(B); + z.uniforms({u_texture:0, u_noise:1, u_size:[c.width, c.height], u_rand:[Math.random(), Math.random()], u_value1:g, u_value2:l, u_camera_planes:J}).draw(e); }); this.setOutputData(0, this._tex); } } } }; - h.pixel_shader_halftone = "precision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform vec2 u_camera_planes;\n\r\n\t\t\tuniform vec2 u_size;\n\r\n\t\t\tuniform float u_value1;\n\r\n\t\t\tuniform float u_value2;\n\r\n\t\t\t\n\r\n\t\t\tfloat pattern() {\n\r\n\t\t\t\tfloat s = sin(u_value1 * 3.1415), c = cos(u_value1 * 3.1415);\n\r\n\t\t\t\tvec2 tex = v_coord * u_size.xy;\n\r\n\t\t\t\tvec2 point = vec2(\n\r\n\t\t\t\t c * tex.x - s * tex.y ,\n\r\n\t\t\t\t s * tex.x + c * tex.y \n\r\n\t\t\t\t) * u_value2;\n\r\n\t\t\t\treturn (sin(point.x) * sin(point.y)) * 4.0;\n\r\n\t\t\t}\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, v_coord);\n\r\n\t\t\t\tfloat average = (color.r + color.g + color.b) / 3.0;\n\r\n\t\t\t\tgl_FragColor = vec4(vec3(average * 10.0 - 5.0 + pattern()), color.a);\n\r\n\t\t\t}\n"; - h.pixel_shader_pixelate = "precision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform vec2 u_camera_planes;\n\r\n\t\t\tuniform vec2 u_size;\n\r\n\t\t\tuniform float u_value1;\n\r\n\t\t\tuniform float u_value2;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec2 coord = vec2( floor(v_coord.x * u_value1) / u_value1, floor(v_coord.y * u_value2) / u_value2 );\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, coord);\n\r\n\t\t\t\tgl_FragColor = color;\n\r\n\t\t\t}\n"; - h.pixel_shader_lowpalette = "precision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform vec2 u_camera_planes;\n\r\n\t\t\tuniform vec2 u_size;\n\r\n\t\t\tuniform float u_value1;\n\r\n\t\t\tuniform float u_value2;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, v_coord);\n\r\n\t\t\t\tgl_FragColor = floor(color * u_value1) / u_value1;\n\r\n\t\t\t}\n"; - h.pixel_shader_noise = "precision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform sampler2D u_noise;\n\r\n\t\t\tuniform vec2 u_size;\n\r\n\t\t\tuniform float u_value1;\n\r\n\t\t\tuniform float u_value2;\n\r\n\t\t\tuniform vec2 u_rand;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, v_coord);\n\r\n\t\t\t\tvec3 noise = texture2D(u_noise, v_coord * vec2(u_size.x / 512.0, u_size.y / 512.0) + u_rand).xyz - vec3(0.5);\n\r\n\t\t\t\tgl_FragColor = vec4( color.xyz + noise * u_value1, color.a );\n\r\n\t\t\t}\n"; - h.pixel_shader_gamma = "precision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform float u_value1;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, v_coord);\n\r\n\t\t\t\tfloat gamma = 1.0 / u_value1;\n\r\n\t\t\t\tgl_FragColor = vec4( pow( color.xyz, vec3(gamma) ), color.a );\n\r\n\t\t\t}\n"; - c.registerNodeType("fx/generic", h); - v.LGraphFXGeneric = h; - m.title = "Vigneting"; - m.desc = "Vigneting"; - m.widgets_info = {precision:{widget:"combo", values:r.MODE_VALUES}}; - m.prototype.onExecute = function() { + p.pixel_shader_halftone = "precision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform vec2 u_camera_planes;\n\r\n\t\t\tuniform vec2 u_size;\n\r\n\t\t\tuniform float u_value1;\n\r\n\t\t\tuniform float u_value2;\n\r\n\t\t\t\n\r\n\t\t\tfloat pattern() {\n\r\n\t\t\t\tfloat s = sin(u_value1 * 3.1415), c = cos(u_value1 * 3.1415);\n\r\n\t\t\t\tvec2 tex = v_coord * u_size.xy;\n\r\n\t\t\t\tvec2 point = vec2(\n\r\n\t\t\t\t c * tex.x - s * tex.y ,\n\r\n\t\t\t\t s * tex.x + c * tex.y \n\r\n\t\t\t\t) * u_value2;\n\r\n\t\t\t\treturn (sin(point.x) * sin(point.y)) * 4.0;\n\r\n\t\t\t}\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, v_coord);\n\r\n\t\t\t\tfloat average = (color.r + color.g + color.b) / 3.0;\n\r\n\t\t\t\tgl_FragColor = vec4(vec3(average * 10.0 - 5.0 + pattern()), color.a);\n\r\n\t\t\t}\n"; + p.pixel_shader_pixelate = "precision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform vec2 u_camera_planes;\n\r\n\t\t\tuniform vec2 u_size;\n\r\n\t\t\tuniform float u_value1;\n\r\n\t\t\tuniform float u_value2;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec2 coord = vec2( floor(v_coord.x * u_value1) / u_value1, floor(v_coord.y * u_value2) / u_value2 );\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, coord);\n\r\n\t\t\t\tgl_FragColor = color;\n\r\n\t\t\t}\n"; + p.pixel_shader_lowpalette = "precision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform vec2 u_camera_planes;\n\r\n\t\t\tuniform vec2 u_size;\n\r\n\t\t\tuniform float u_value1;\n\r\n\t\t\tuniform float u_value2;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, v_coord);\n\r\n\t\t\t\tgl_FragColor = floor(color * u_value1) / u_value1;\n\r\n\t\t\t}\n"; + p.pixel_shader_noise = "precision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform sampler2D u_noise;\n\r\n\t\t\tuniform vec2 u_size;\n\r\n\t\t\tuniform float u_value1;\n\r\n\t\t\tuniform float u_value2;\n\r\n\t\t\tuniform vec2 u_rand;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, v_coord);\n\r\n\t\t\t\tvec3 noise = texture2D(u_noise, v_coord * vec2(u_size.x / 512.0, u_size.y / 512.0) + u_rand).xyz - vec3(0.5);\n\r\n\t\t\t\tgl_FragColor = vec4( color.xyz + noise * u_value1, color.a );\n\r\n\t\t\t}\n"; + p.pixel_shader_gamma = "precision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform float u_value1;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, v_coord);\n\r\n\t\t\t\tfloat gamma = 1.0 / u_value1;\n\r\n\t\t\t\tgl_FragColor = vec4( pow( color.xyz, vec3(gamma) ), color.a );\n\r\n\t\t\t}\n"; + c.registerNodeType("fx/generic", p); + w.LGraphFXGeneric = p; + l.title = "Vigneting"; + l.desc = "Vigneting"; + l.widgets_info = {precision:{widget:"combo", values:t.MODE_VALUES}}; + l.prototype.onExecute = function() { var c = this.getInputData(0); - if (this.properties.precision === r.PASS_THROUGH) { + if (this.properties.precision === t.PASS_THROUGH) { this.setOutputData(0, c); } else { if (c) { - this._tex = r.getTargetTexture(c, this._tex, this.properties.precision); - var h = this.properties.intensity; - this.isInputConnected(1) && (h = this.getInputData(1), this.properties.intensity = h); + this._tex = t.getTargetTexture(c, this._tex, this.properties.precision); + var g = this.properties.intensity; + this.isInputConnected(1) && (g = this.getInputData(1), this.properties.intensity = g); gl.disable(gl.BLEND); gl.disable(gl.DEPTH_TEST); - var l = Mesh.getScreenQuad(), q = m._shader, v = this.properties.invert; + var p = Mesh.getScreenQuad(), q = l._shader, w = this.properties.invert; this._tex.drawTo(function() { c.bind(0); - q.uniforms({u_texture:0, u_intensity:h, u_isize:[1 / c.width, 1 / c.height], u_invert:v ? 1 : 0}).draw(l); + q.uniforms({u_texture:0, u_intensity:g, u_isize:[1 / c.width, 1 / c.height], u_invert:w ? 1 : 0}).draw(p); }); this.setOutputData(0, this._tex); } } }; - m.pixel_shader = "precision highp float;\n\r\n\t\t\tprecision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform float u_intensity;\n\r\n\t\t\tuniform int u_invert;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tfloat luminance = 1.0 - length( v_coord - vec2(0.5) ) * 1.414;\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, v_coord);\n\r\n\t\t\t\tif(u_invert == 1)\n\r\n\t\t\t\t\tluminance = 1.0 - luminance;\n\r\n\t\t\t\tluminance = mix(1.0, luminance, u_intensity);\n\r\n\t\t\t gl_FragColor = vec4( luminance * color.xyz, color.a);\n\r\n\t\t\t}\n\r\n\t\t\t"; - c.registerNodeType("fx/vigneting", m); - v.LGraphFXVigneting = m; + l.pixel_shader = "precision highp float;\n\r\n\t\t\tprecision highp float;\n\r\n\t\t\tvarying vec2 v_coord;\n\r\n\t\t\tuniform sampler2D u_texture;\n\r\n\t\t\tuniform float u_intensity;\n\r\n\t\t\tuniform int u_invert;\n\r\n\t\t\t\n\r\n\t\t\tvoid main() {\n\r\n\t\t\t\tfloat luminance = 1.0 - length( v_coord - vec2(0.5) ) * 1.414;\n\r\n\t\t\t\tvec4 color = texture2D(u_texture, v_coord);\n\r\n\t\t\t\tif(u_invert == 1)\n\r\n\t\t\t\t\tluminance = 1.0 - luminance;\n\r\n\t\t\t\tluminance = mix(1.0, luminance, u_intensity);\n\r\n\t\t\t gl_FragColor = vec4( luminance * color.xyz, color.a);\n\r\n\t\t\t}\n\r\n\t\t\t"; + c.registerNodeType("fx/vigneting", l); + w.LGraphFXVigneting = l; } })(this); -(function(v) { +(function(w) { function c(c) { this.cmd = this.channel = 0; this.data = new Uint32Array(3); c && this.setup(c); } - function r(c, e) { + function t(c, e) { navigator.requestMIDIAccess ? (this.on_ready = c, this.state = {note:[], cc:[]}, this.input_ports = null, this.input_ports_info = [], this.output_ports = null, this.output_ports_info = [], navigator.requestMIDIAccess().then(this.onMIDISuccess.bind(this), this.onMIDIFailure.bind(this))) : (this.error = "not suppoorted", e ? e("Not supported") : console.error("MIDI NOT SUPPORTED, enable by chrome://flags")); } - function m() { + function l() { this.addOutput("on_midi", u.EVENT); this.addOutput("out", "midi"); this.properties = {port:0}; @@ -8741,7 +8761,7 @@ $jscomp.polyfill("Object.values", function(v) { this.boxcolor = "#AAA"; this._last_time = 0; var c = this; - new r(function(e) { + new t(function(e) { c._midi = e; if (c._waiting) { c.onStart(); @@ -8749,11 +8769,11 @@ $jscomp.polyfill("Object.values", function(v) { c._waiting = !1; }); } - function h() { + function p() { this.addInput("send", u.EVENT); this.properties = {port:0}; var c = this; - new r(function(e) { + new t(function(e) { c._midi = e; c.widget.options.values = c.getMIDIOutputs(); }); @@ -8765,7 +8785,7 @@ $jscomp.polyfill("Object.values", function(v) { this._str = ""; this.size = [200, 40]; } - function l() { + function g() { this.properties = {channel:-1, cmd:-1, min_value:-1, max_value:-1}; var c = this; this._learning = !1; @@ -8777,7 +8797,7 @@ $jscomp.polyfill("Object.values", function(v) { this.addOutput("on_midi", u.EVENT); this.boxcolor = "#AAA"; } - function A() { + function B() { this.properties = {channel:0, cmd:144, value1:1, value2:1}; this.addInput("send", u.EVENT); this.addInput("assign", u.EVENT); @@ -8785,20 +8805,20 @@ $jscomp.polyfill("Object.values", function(v) { this.midi_event = new c; this.gate = !1; } - function y() { + function A() { this.properties = {cc:1, value:0}; this.addOutput("value", "number"); } - function x() { + function v() { this.addInput("generate", u.ACTION); this.addInput("scale", "string"); this.addInput("octave", "number"); this.addOutput("note", u.EVENT); this.properties = {notes:"A,A#,B,C,C#,D,D#,E,F,F#,G,G#", octave:2, duration:0.5, mode:"sequence"}; - this.notes_pitches = x.processScale(this.properties.notes); + this.notes_pitches = v.processScale(this.properties.notes); this.sequence_index = 0; } - function F() { + function G() { this.properties = {amount:0}; this.addInput("in", u.ACTION); this.addInput("amount", "number"); @@ -8814,7 +8834,7 @@ $jscomp.polyfill("Object.values", function(v) { this.offset_notes = Array(12); this.processScale(this.properties.scale); } - function e() { + function r() { this.properties = {url:"", autoplay:!0}; this.addInput("play", u.ACTION); this.addInput("pause", u.ACTION); @@ -8824,7 +8844,7 @@ $jscomp.polyfill("Object.values", function(v) { this._playing = !1; "undefined" == typeof MidiParser && (console.error("midi-parser.js not included, LGMidiPlay requires that library: https://raw.githubusercontent.com/colxi/midi-parser-js/master/src/main.js"), this.boxcolor = "red"); } - function B() { + function e() { this.properties = {volume:0.5, duration:1}; this.addInput("note", u.ACTION); this.addInput("volume", "number"); @@ -8832,7 +8852,7 @@ $jscomp.polyfill("Object.values", function(v) { this.addOutput("note", u.EVENT); "undefined" == typeof AudioSynth ? (console.error("Audiosynth.js not included, LGMidiPlay requires that library"), this.boxcolor = "red") : this.instrument = (this.synth = new AudioSynth).createInstrument("piano"); } - function E() { + function J() { this.properties = {num_octaves:2, start_octave:2}; this.addInput("note", u.ACTION); this.addInput("reset", u.ACTION); @@ -8841,21 +8861,21 @@ $jscomp.polyfill("Object.values", function(v) { this.keys = []; this._last_key = -1; } - var u = v.LiteGraph; + var u = w.LiteGraph; u.MIDIEvent = c; c.prototype.fromJSON = function(c) { this.setup(c.data); }; c.prototype.setup = function(e) { - var n = e; - e.constructor === Object && (n = e.data); - this.data.set(n); - this.status = e = n[0]; - n = e & 240; - this.cmd = 240 <= e ? e : n; + var m = e; + e.constructor === Object && (m = e.data); + this.data.set(m); + this.status = e = m[0]; + m = e & 240; + this.cmd = 240 <= e ? e : m; this.cmd == c.NOTEON && 0 == this.velocity && (this.cmd = c.NOTEOFF); this.cmd_str = c.commands[this.cmd] || ""; - if (n >= c.NOTEON || n <= c.NOTEOFF) { + if (m >= c.NOTEON || m <= c.NOTEOFF) { this.channel = e & 15; } }; @@ -8936,19 +8956,19 @@ $jscomp.polyfill("Object.values", function(v) { return Number(e); } }; - c.toNoteString = function(e, h) { + c.toNoteString = function(e, g) { e = Math.round(e); - var k = Math.floor((e - 24) / 12 + 1); + var h = Math.floor((e - 24) / 12 + 1); e = (e - 21) % 12; 0 > e && (e = 12 + e); - return c.notes[e] + (h ? "" : k); + return c.notes[e] + (g ? "" : h); }; c.NoteStringToPitch = function(e) { e = e.toUpperCase(); - var n = e[0], k = 4; - "#" == e[1] ? (n += "#", 2 < e.length && (k = Number(e[2]))) : 1 < e.length && (k = Number(e[1])); - e = c.note_to_index[n]; - return null == e ? null : 12 * (k - 1) + e + 21; + var m = e[0], h = 4; + "#" == e[1] ? (m += "#", 2 < e.length && (h = Number(e[2]))) : 1 < e.length && (h = Number(e[1])); + e = c.note_to_index[m]; + return null == e ? null : 12 * (h - 1) + e + 21; }; c.prototype.toString = function() { var e = "" + this.channel + ". "; @@ -8992,12 +9012,12 @@ $jscomp.polyfill("Object.values", function(v) { c.commands = {128:"note off", 144:"note on", 160:"key pressure", 176:"controller change", 192:"program change", 208:"channel pressure", 224:"pitch bend", 240:"system", 242:"Song pos", 243:"Song select", 246:"Tune request", 248:"time tick", 250:"Start Song", 251:"Continue Song", 252:"Stop Song", 254:"Sensing", 255:"Reset"}; c.commands_short = {128:"NOTEOFF", 144:"NOTEOFF", 160:"KEYP", 176:"CC", 192:"PC", 208:"CP", 224:"PB", 240:"SYS", 242:"POS", 243:"SELECT", 246:"TUNEREQ", 248:"TT", 250:"START", 251:"CONTINUE", 252:"STOP", 254:"SENS", 255:"RESET"}; c.commands_reversed = {}; - for (var H in c.commands) { - c.commands_reversed[c.commands[H]] = H; + for (var C in c.commands) { + c.commands_reversed[c.commands[C]] = C; } - r.input = null; - r.MIDIEvent = c; - r.prototype.onMIDISuccess = function(c) { + t.input = null; + t.MIDIEvent = c; + t.prototype.onMIDISuccess = function(c) { console.log("MIDI ready!"); console.log(c); this.midi = c; @@ -9006,48 +9026,48 @@ $jscomp.polyfill("Object.values", function(v) { this.on_ready(this); } }; - r.prototype.updatePorts = function() { + t.prototype.updatePorts = function() { var c = this.midi; this.input_ports = c.inputs; this.input_ports_info = []; this.output_ports = c.outputs; this.output_ports_info = []; c = 0; - for (var e = this.input_ports.values(), k = e.next(); k && !1 === k.done;) { - k = k.value, this.input_ports_info.push(k), console.log("Input port [type:'" + k.type + "'] id:'" + k.id + "' manufacturer:'" + k.manufacturer + "' name:'" + k.name + "' version:'" + k.version + "'"), c++, k = e.next(); + for (var e = this.input_ports.values(), h = e.next(); h && !1 === h.done;) { + h = h.value, this.input_ports_info.push(h), console.log("Input port [type:'" + h.type + "'] id:'" + h.id + "' manufacturer:'" + h.manufacturer + "' name:'" + h.name + "' version:'" + h.version + "'"), c++, h = e.next(); } this.num_input_ports = c; c = 0; e = this.output_ports.values(); - for (k = e.next(); k && !1 === k.done;) { - k = k.value, this.output_ports_info.push(k), console.log("Output port [type:'" + k.type + "'] id:'" + k.id + "' manufacturer:'" + k.manufacturer + "' name:'" + k.name + "' version:'" + k.version + "'"), c++, k = e.next(); + for (h = e.next(); h && !1 === h.done;) { + h = h.value, this.output_ports_info.push(h), console.log("Output port [type:'" + h.type + "'] id:'" + h.id + "' manufacturer:'" + h.manufacturer + "' name:'" + h.name + "' version:'" + h.version + "'"), c++, h = e.next(); } this.num_output_ports = c; }; - r.prototype.onMIDIFailure = function(c) { + t.prototype.onMIDIFailure = function(c) { console.error("Failed to get MIDI access - " + c); }; - r.prototype.openInputPort = function(e, h) { + t.prototype.openInputPort = function(e, g) { e = this.input_ports.get("input-" + e); if (!e) { return !1; } - r.input = this; - var k = this; - e.onmidimessage = function(a) { - var b = new c(a.data); - k.updateState(b); - h && h(a.data, b); - if (r.on_message) { - r.on_message(a.data, b); + t.input = this; + var h = this; + e.onmidimessage = function(e) { + var a = new c(e.data); + h.updateState(a); + g && g(e.data, a); + if (t.on_message) { + t.on_message(e.data, a); } }; console.log("port open: ", e); return !0; }; - r.parseMsg = function(c) { + t.parseMsg = function(c) { }; - r.prototype.updateState = function(e) { + t.prototype.updateState = function(e) { switch(e.cmd) { case c.NOTEON: this.state.note[e.value1 | 0] = e.value2; @@ -9059,110 +9079,110 @@ $jscomp.polyfill("Object.values", function(v) { this.state.cc[e.getCC()] = e.getCCValue(); } }; - r.prototype.sendMIDI = function(e, h) { - h && (e = this.output_ports_info[e]) && (r.output = this, h.constructor === c ? e.send(h.data) : e.send(h)); + t.prototype.sendMIDI = function(e, g) { + g && (e = this.output_ports_info[e]) && (t.output = this, g.constructor === c ? e.send(g.data) : e.send(g)); }; - m.MIDIInterface = r; - m.title = "MIDI Input"; - m.desc = "Reads MIDI from a input port"; - m.color = "#243"; - m.prototype.getPropertyInfo = function(c) { + l.MIDIInterface = t; + l.title = "MIDI Input"; + l.desc = "Reads MIDI from a input port"; + l.color = "#243"; + l.prototype.getPropertyInfo = function(c) { if (this._midi && "port" == c) { c = {}; for (var e = 0; e < this._midi.input_ports_info.length; ++e) { - var k = this._midi.input_ports_info[e]; - c[e] = e + ".- " + k.name + " version:" + k.version; + var h = this._midi.input_ports_info[e]; + c[e] = e + ".- " + h.name + " version:" + h.version; } return {type:"enum", values:c}; } }; - m.prototype.onStart = function() { + l.prototype.onStart = function() { this._midi ? this._midi.openInputPort(this.properties.port, this.onMIDIEvent.bind(this)) : this._waiting = !0; }; - m.prototype.onMIDIEvent = function(e, h) { - this._last_midi_event = h; + l.prototype.onMIDIEvent = function(e, g) { + this._last_midi_event = g; this.boxcolor = "#AFA"; this._last_time = u.getTime(); - this.trigger("on_midi", h); - h.cmd == c.NOTEON ? this.trigger("on_noteon", h) : h.cmd == c.NOTEOFF ? this.trigger("on_noteoff", h) : h.cmd == c.CONTROLLERCHANGE ? this.trigger("on_cc", h) : h.cmd == c.PROGRAMCHANGE ? this.trigger("on_pc", h) : h.cmd == c.PITCHBEND && this.trigger("on_pitchbend", h); + this.trigger("on_midi", g); + g.cmd == c.NOTEON ? this.trigger("on_noteon", g) : g.cmd == c.NOTEOFF ? this.trigger("on_noteoff", g) : g.cmd == c.CONTROLLERCHANGE ? this.trigger("on_cc", g) : g.cmd == c.PROGRAMCHANGE ? this.trigger("on_pc", g) : g.cmd == c.PITCHBEND && this.trigger("on_pitchbend", g); }; - m.prototype.onDrawBackground = function(c) { + l.prototype.onDrawBackground = function(c) { this.boxcolor = "#AAA"; if (!this.flags.collapsed && this._last_midi_event) { c.fillStyle = "white"; var e = u.getTime(); e = 1.0 - Math.max(0, 0.001 * (e - this._last_time)); if (0 < e) { - var k = c.globalAlpha; + var h = c.globalAlpha; c.globalAlpha *= e; c.font = "12px Tahoma"; c.fillText(this._last_midi_event.toString(), 2, 0.5 * this.size[1] + 3); - c.globalAlpha = k; + c.globalAlpha = h; } } }; - m.prototype.onExecute = function() { + l.prototype.onExecute = function() { if (this.outputs) { for (var c = this._last_midi_event, e = 0; e < this.outputs.length; ++e) { switch(this.outputs[e].name) { case "midi": - var k = this._midi; + var h = this._midi; break; case "last_midi": - k = c; + h = c; break; default: continue; } - this.setOutputData(e, k); + this.setOutputData(e, h); } } }; - m.prototype.onGetOutputs = function() { + l.prototype.onGetOutputs = function() { return [["last_midi", "midi"], ["on_midi", u.EVENT], ["on_noteon", u.EVENT], ["on_noteoff", u.EVENT], ["on_cc", u.EVENT], ["on_pc", u.EVENT], ["on_pitchbend", u.EVENT]]; }; - u.registerNodeType("midi/input", m); - h.MIDIInterface = r; - h.title = "MIDI Output"; - h.desc = "Sends MIDI to output channel"; - h.color = "#243"; - h.prototype.onGetPropertyInfo = function(c) { + u.registerNodeType("midi/input", l); + p.MIDIInterface = t; + p.title = "MIDI Output"; + p.desc = "Sends MIDI to output channel"; + p.color = "#243"; + p.prototype.onGetPropertyInfo = function(c) { if (this._midi && "port" == c) { return {type:"enum", values:this.getMIDIOutputs()}; } }; - h.default_ports = {0:"unknown"}; - h.prototype.getMIDIOutputs = function() { + p.default_ports = {0:"unknown"}; + p.prototype.getMIDIOutputs = function() { var c = {}; if (!this._midi) { - return h.default_ports; + return p.default_ports; } if (this._midi.output_ports_info) { for (var e = 0; e < this._midi.output_ports_info.length; ++e) { - var k = this._midi.output_ports_info[e]; - k && (c[e] = e + ".- " + k.name + " version:" + k.version); + var h = this._midi.output_ports_info[e]; + h && (c[e] = e + ".- " + h.name + " version:" + h.version); } } return c; }; - h.prototype.onAction = function(c, e) { + p.prototype.onAction = function(c, e) { this._midi && ("send" == c && this._midi.sendMIDI(this.properties.port, e), this.trigger("midi", e)); }; - h.prototype.onGetInputs = function() { + p.prototype.onGetInputs = function() { return [["send", u.ACTION]]; }; - h.prototype.onGetOutputs = function() { + p.prototype.onGetOutputs = function() { return [["on_midi", u.EVENT]]; }; - u.registerNodeType("midi/output", h); + u.registerNodeType("midi/output", p); q.title = "MIDI Show"; q.desc = "Shows MIDI in the graph"; q.color = "#243"; q.prototype.getTitle = function() { return this.flags.collapsed ? this._str : this.title; }; - q.prototype.onAction = function(e, h) { - h && (this._str = h.constructor === c ? h.toString() : "???"); + q.prototype.onAction = function(e, g) { + g && (this._str = g.constructor === c ? g.toString() : "???"); }; q.prototype.onDrawForeground = function(c) { this._str && !this.flags.collapsed && (c.font = "30px Arial", c.fillText(this._str, 10, 0.8 * this.size[1])); @@ -9174,170 +9194,170 @@ $jscomp.polyfill("Object.values", function(v) { return [["on_midi", u.EVENT]]; }; u.registerNodeType("midi/show", q); - l.title = "MIDI Filter"; - l.desc = "Filters MIDI messages"; - l.color = "#243"; - l["@cmd"] = {type:"enum", title:"Command", values:c.commands_reversed}; - l.prototype.getTitle = function() { + g.title = "MIDI Filter"; + g.desc = "Filters MIDI messages"; + g.color = "#243"; + g["@cmd"] = {type:"enum", title:"Command", values:c.commands_reversed}; + g.prototype.getTitle = function() { var e = -1 == this.properties.cmd ? "Nothing" : c.commands_short[this.properties.cmd] || "Unknown"; -1 != this.properties.min_value && -1 != this.properties.max_value && (e += " " + (this.properties.min_value == this.properties.max_value ? this.properties.max_value : this.properties.min_value + ".." + this.properties.max_value)); return "Filter: " + e; }; - l.prototype.onPropertyChanged = function(e, h) { - "cmd" == e && (e = Number(h), isNaN(e) && (e = c.commands[h] || 0), this.properties.cmd = e); + g.prototype.onPropertyChanged = function(e, g) { + "cmd" == e && (e = Number(g), isNaN(e) && (e = c.commands[g] || 0), this.properties.cmd = e); }; - l.prototype.onAction = function(e, h) { - if (h && h.constructor === c) { + g.prototype.onAction = function(e, g) { + if (g && g.constructor === c) { if (this._learning) { - this._learning = !1, this.boxcolor = "#AAA", this.properties.channel = h.channel, this.properties.cmd = h.cmd, this.properties.min_value = this.properties.max_value = h.data[1]; + this._learning = !1, this.boxcolor = "#AAA", this.properties.channel = g.channel, this.properties.cmd = g.cmd, this.properties.min_value = this.properties.max_value = g.data[1]; } else { - if (-1 != this.properties.channel && h.channel != this.properties.channel || -1 != this.properties.cmd && h.cmd != this.properties.cmd || -1 != this.properties.min_value && h.data[1] < this.properties.min_value || -1 != this.properties.max_value && h.data[1] > this.properties.max_value) { + if (-1 != this.properties.channel && g.channel != this.properties.channel || -1 != this.properties.cmd && g.cmd != this.properties.cmd || -1 != this.properties.min_value && g.data[1] < this.properties.min_value || -1 != this.properties.max_value && g.data[1] > this.properties.max_value) { return; } } - this.trigger("on_midi", h); + this.trigger("on_midi", g); } }; - u.registerNodeType("midi/filter", l); - A.title = "MIDIEvent"; - A.desc = "Create a MIDI Event"; - A.color = "#243"; - A.prototype.onAction = function(e, h) { - "assign" == e ? (this.properties.channel = h.channel, this.properties.cmd = h.cmd, this.properties.value1 = h.data[1], this.properties.value2 = h.data[2], h.cmd == c.NOTEON ? this.gate = !0 : h.cmd == c.NOTEOFF && (this.gate = !1)) : (h = this.midi_event, h.channel = this.properties.channel, this.properties.cmd && this.properties.cmd.constructor === String ? h.setCommandFromString(this.properties.cmd) : h.cmd = this.properties.cmd, h.data[0] = h.cmd | h.channel, h.data[1] = Number(this.properties.value1), - h.data[2] = Number(this.properties.value2), this.trigger("on_midi", h)); + u.registerNodeType("midi/filter", g); + B.title = "MIDIEvent"; + B.desc = "Create a MIDI Event"; + B.color = "#243"; + B.prototype.onAction = function(e, g) { + "assign" == e ? (this.properties.channel = g.channel, this.properties.cmd = g.cmd, this.properties.value1 = g.data[1], this.properties.value2 = g.data[2], g.cmd == c.NOTEON ? this.gate = !0 : g.cmd == c.NOTEOFF && (this.gate = !1)) : (g = this.midi_event, g.channel = this.properties.channel, this.properties.cmd && this.properties.cmd.constructor === String ? g.setCommandFromString(this.properties.cmd) : g.cmd = this.properties.cmd, g.data[0] = g.cmd | g.channel, g.data[1] = Number(this.properties.value1), + g.data[2] = Number(this.properties.value2), this.trigger("on_midi", g)); }; - A.prototype.onExecute = function() { + B.prototype.onExecute = function() { var e = this.properties; if (this.inputs) { - for (var h = 0; h < this.inputs.length; ++h) { - var k = this.inputs[h]; - if (-1 != k.link) { - switch(k.name) { + for (var g = 0; g < this.inputs.length; ++g) { + var h = this.inputs[g]; + if (-1 != h.link) { + switch(h.name) { case "note": - k = this.getInputData(h); - null != k && (k.constructor === String && (k = c.NoteStringToPitch(k)), this.properties.value1 = (k | 0) % 255); + h = this.getInputData(g); + null != h && (h.constructor === String && (h = c.NoteStringToPitch(h)), this.properties.value1 = (h | 0) % 255); break; case "cmd": - k = this.getInputData(h); - null != k && (this.properties.cmd = k); + h = this.getInputData(g); + null != h && (this.properties.cmd = h); break; case "value1": - k = this.getInputData(h); - null != k && (this.properties.value1 = Math.clamp(k | 0, 0, 127)); + h = this.getInputData(g); + null != h && (this.properties.value1 = Math.clamp(h | 0, 0, 127)); break; case "value2": - k = this.getInputData(h), null != k && (this.properties.value2 = Math.clamp(k | 0, 0, 127)); + h = this.getInputData(g), null != h && (this.properties.value2 = Math.clamp(h | 0, 0, 127)); } } } } if (this.outputs) { - for (h = 0; h < this.outputs.length; ++h) { - switch(this.outputs[h].name) { + for (g = 0; g < this.outputs.length; ++g) { + switch(this.outputs[g].name) { case "midi": - k = new c; - k.setup([e.cmd, e.value1, e.value2]); - k.channel = e.channel; + h = new c; + h.setup([e.cmd, e.value1, e.value2]); + h.channel = e.channel; break; case "command": - k = e.cmd; + h = e.cmd; break; case "cc": - k = e.value1; + h = e.value1; break; case "cc_value": - k = e.value2; + h = e.value2; break; case "note": - k = e.cmd == c.NOTEON || e.cmd == c.NOTEOFF ? e.value1 : null; + h = e.cmd == c.NOTEON || e.cmd == c.NOTEOFF ? e.value1 : null; break; case "velocity": - k = e.cmd == c.NOTEON ? e.value2 : null; + h = e.cmd == c.NOTEON ? e.value2 : null; break; case "pitch": - k = e.cmd == c.NOTEON ? c.computePitch(e.value1) : null; + h = e.cmd == c.NOTEON ? c.computePitch(e.value1) : null; break; case "pitchbend": - k = e.cmd == c.PITCHBEND ? c.computePitchBend(e.value1, e.value2) : null; + h = e.cmd == c.PITCHBEND ? c.computePitchBend(e.value1, e.value2) : null; break; case "gate": - k = this.gate; + h = this.gate; break; default: continue; } - null !== k && this.setOutputData(h, k); + null !== h && this.setOutputData(g, h); } } }; - A.prototype.onPropertyChanged = function(e, h) { - "cmd" == e && (this.properties.cmd = c.computeCommandFromString(h)); + B.prototype.onPropertyChanged = function(e, g) { + "cmd" == e && (this.properties.cmd = c.computeCommandFromString(g)); }; - A.prototype.onGetInputs = function() { + B.prototype.onGetInputs = function() { return [["cmd", "number"], ["note", "number"], ["value1", "number"], ["value2", "number"]]; }; - A.prototype.onGetOutputs = function() { + B.prototype.onGetOutputs = function() { return [["midi", "midi"], ["on_midi", u.EVENT], ["command", "number"], ["note", "number"], ["velocity", "number"], ["cc", "number"], ["cc_value", "number"], ["pitch", "number"], ["gate", "bool"], ["pitchbend", "number"]]; }; - u.registerNodeType("midi/event", A); - y.title = "MIDICC"; - y.desc = "gets a Controller Change"; - y.color = "#243"; - y.prototype.onExecute = function() { - r.input && (this.properties.value = r.input.state.cc[this.properties.cc]); + u.registerNodeType("midi/event", B); + A.title = "MIDICC"; + A.desc = "gets a Controller Change"; + A.color = "#243"; + A.prototype.onExecute = function() { + t.input && (this.properties.value = t.input.state.cc[this.properties.cc]); this.setOutputData(0, this.properties.value); }; - u.registerNodeType("midi/cc", y); - x.title = "MIDI Generator"; - x.desc = "Generates a random MIDI note"; - x.color = "#243"; - x.processScale = function(e) { + u.registerNodeType("midi/cc", A); + v.title = "MIDI Generator"; + v.desc = "Generates a random MIDI note"; + v.color = "#243"; + v.processScale = function(e) { e = e.split(","); - for (var h = 0; h < e.length; ++h) { - var k = e[h]; - e[h] = 2 == k.length && "#" != k[1] || 2 < k.length ? -u.MIDIEvent.NoteStringToPitch(k) : c.note_to_index[k] || 0; + for (var g = 0; g < e.length; ++g) { + var h = e[g]; + e[g] = 2 == h.length && "#" != h[1] || 2 < h.length ? -u.MIDIEvent.NoteStringToPitch(h) : c.note_to_index[h] || 0; } return e; }; - x.prototype.onPropertyChanged = function(c, e) { - "notes" == c && (this.notes_pitches = x.processScale(e)); + v.prototype.onPropertyChanged = function(c, e) { + "notes" == c && (this.notes_pitches = v.processScale(e)); }; - x.prototype.onExecute = function() { + v.prototype.onExecute = function() { var c = this.getInputData(2); null != c && (this.properties.octave = c); if (c = this.getInputData(1)) { - this.notes_pitches = x.processScale(c); + this.notes_pitches = v.processScale(c); } }; - x.prototype.onAction = function(e, h) { - var k = 0; - h = this.notes_pitches.length; + v.prototype.onAction = function(e, g) { + var h = 0; + g = this.notes_pitches.length; e = 0; - "sequence" == this.properties.mode ? e = this.sequence_index = (this.sequence_index + 1) % h : "random" == this.properties.mode && (e = Math.floor(Math.random() * h)); - h = this.notes_pitches[e]; - k = 0 <= h ? h + 12 * (this.properties.octave - 1) + 33 : -h; - h = new c; - h.setup([c.NOTEON, k, 10]); + "sequence" == this.properties.mode ? e = this.sequence_index = (this.sequence_index + 1) % g : "random" == this.properties.mode && (e = Math.floor(Math.random() * g)); + g = this.notes_pitches[e]; + h = 0 <= g ? g + 12 * (this.properties.octave - 1) + 33 : -g; + g = new c; + g.setup([c.NOTEON, h, 10]); e = this.properties.duration || 1; - this.trigger("note", h); + this.trigger("note", g); setTimeout(function() { - var a = new c; - a.setup([c.NOTEOFF, k, 0]); - this.trigger("note", a); + var e = new c; + e.setup([c.NOTEOFF, h, 0]); + this.trigger("note", e); }.bind(this), 1000 * e); }; - u.registerNodeType("midi/generator", x); - F.title = "MIDI Transpose"; - F.desc = "Transpose a MIDI note"; - F.color = "#243"; - F.prototype.onAction = function(e, h) { - h && h.constructor === c && (h.data[0] == c.NOTEON || h.data[0] == c.NOTEOFF ? (this.midi_event = new c, this.midi_event.setup(h.data), this.midi_event.data[1] = Math.round(this.midi_event.data[1] + this.properties.amount), this.trigger("out", this.midi_event)) : this.trigger("out", h)); + u.registerNodeType("midi/generator", v); + G.title = "MIDI Transpose"; + G.desc = "Transpose a MIDI note"; + G.color = "#243"; + G.prototype.onAction = function(e, g) { + g && g.constructor === c && (g.data[0] == c.NOTEON || g.data[0] == c.NOTEOFF ? (this.midi_event = new c, this.midi_event.setup(g.data), this.midi_event.data[1] = Math.round(this.midi_event.data[1] + this.properties.amount), this.trigger("out", this.midi_event)) : this.trigger("out", g)); }; - F.prototype.onExecute = function() { + G.prototype.onExecute = function() { var c = this.getInputData(1); null != c && (this.properties.amount = c); }; - u.registerNodeType("midi/transpose", F); + u.registerNodeType("midi/transpose", G); z.title = "MIDI Quantize Pitch"; z.desc = "Transpose a MIDI note tp fit an scale"; z.color = "#243"; @@ -9346,7 +9366,7 @@ $jscomp.polyfill("Object.values", function(v) { }; z.prototype.processScale = function(c) { this._current_scale = c; - this.notes_pitches = x.processScale(c); + this.notes_pitches = v.processScale(c); for (c = 0; 12 > c; ++c) { this.valid_notes[c] = -1 != this.notes_pitches.indexOf(c); } @@ -9367,40 +9387,40 @@ $jscomp.polyfill("Object.values", function(v) { } } }; - z.prototype.onAction = function(e, h) { - h && h.constructor === c && (h.data[0] == c.NOTEON || h.data[0] == c.NOTEOFF ? (this.midi_event = new c, this.midi_event.setup(h.data), this.midi_event.data[1] += this.offset_notes[c.note_to_index[h.note]], this.trigger("out", this.midi_event)) : this.trigger("out", h)); + z.prototype.onAction = function(e, g) { + g && g.constructor === c && (g.data[0] == c.NOTEON || g.data[0] == c.NOTEOFF ? (this.midi_event = new c, this.midi_event.setup(g.data), this.midi_event.data[1] += this.offset_notes[c.note_to_index[g.note]], this.trigger("out", this.midi_event)) : this.trigger("out", g)); }; z.prototype.onExecute = function() { var c = this.getInputData(1); null != c && c != this._current_scale && this.processScale(c); }; u.registerNodeType("midi/quantize", z); - e.title = "MIDI fromFile"; - e.desc = "Plays a MIDI file"; - e.color = "#243"; - e.prototype.onAction = function(c) { + r.title = "MIDI fromFile"; + r.desc = "Plays a MIDI file"; + r.color = "#243"; + r.prototype.onAction = function(c) { "play" == c ? this.play() : "pause" == c && (this._playing = !this._playing); }; - e.prototype.onPropertyChanged = function(c, e) { + r.prototype.onPropertyChanged = function(c, e) { "url" == c && this.loadMIDIFile(e); }; - e.prototype.onExecute = function() { + r.prototype.onExecute = function() { if (this._midi && this._playing) { this._current_time += this.graph.elapsed_time; - for (var e = 100 * this._current_time, h = 0; h < this._midi.tracks; ++h) { - var k = this._midi.track[h]; - k._last_pos || (k._last_pos = 0, k._time = 0); - var a = k.event[k._last_pos]; - if (a && k._time + a.deltaTime <= e && (k._last_pos++, k._time += a.deltaTime, a.data)) { - k = a.type << 4 + a.channel; - var b = new c; - b.setup([k, a.data[0], a.data[1]]); - this.trigger("note", b); + for (var e = 100 * this._current_time, g = 0; g < this._midi.tracks; ++g) { + var h = this._midi.track[g]; + h._last_pos || (h._last_pos = 0, h._time = 0); + var l = h.event[h._last_pos]; + if (l && h._time + l.deltaTime <= e && (h._last_pos++, h._time += l.deltaTime, l.data)) { + h = l.type << 4 + l.channel; + var a = new c; + a.setup([h, l.data[0], l.data[1]]); + this.trigger("note", a); } } } }; - e.prototype.play = function() { + r.prototype.play = function() { this._playing = !0; this._current_time = 0; if (this._midi) { @@ -9411,7 +9431,7 @@ $jscomp.polyfill("Object.values", function(v) { } } }; - e.prototype.loadMIDIFile = function(c) { + r.prototype.loadMIDIFile = function(c) { var e = this; u.fetchFile(c, "arraybuffer", function(c) { e.boxcolor = "#AFA"; @@ -9422,113 +9442,113 @@ $jscomp.polyfill("Object.values", function(v) { e._midi = null; }); }; - e.prototype.onDropFile = function(c) { + r.prototype.onDropFile = function(c) { this.properties.url = ""; this.loadMIDIFile(c); }; - u.registerNodeType("midi/fromFile", e); - B.title = "MIDI Play"; - B.desc = "Plays a MIDI note"; - B.color = "#243"; - B.prototype.onAction = function(e, h) { - if (h && h.constructor === c) { - if (this.instrument && h.data[0] == c.NOTEON) { - e = h.note; + u.registerNodeType("midi/fromFile", r); + e.title = "MIDI Play"; + e.desc = "Plays a MIDI note"; + e.color = "#243"; + e.prototype.onAction = function(e, g) { + if (g && g.constructor === c) { + if (this.instrument && g.data[0] == c.NOTEON) { + e = g.note; if (!e || "undefined" == e || e.constructor !== String) { return; } - this.instrument.play(e, h.octave, this.properties.duration, this.properties.volume); + this.instrument.play(e, g.octave, this.properties.duration, this.properties.volume); } - this.trigger("note", h); + this.trigger("note", g); } }; - B.prototype.onExecute = function() { + e.prototype.onExecute = function() { var c = this.getInputData(1); null != c && (this.properties.volume = c); c = this.getInputData(2); null != c && (this.properties.duration = c); }; - u.registerNodeType("midi/play", B); - E.title = "MIDI Keys"; - E.desc = "Keyboard to play notes"; - E.color = "#243"; - E.keys = [{x:0, w:1, h:1, t:0}, {x:0.75, w:0.5, h:0.6, t:1}, {x:1, w:1, h:1, t:0}, {x:1.75, w:0.5, h:0.6, t:1}, {x:2, w:1, h:1, t:0}, {x:2.75, w:0.5, h:0.6, t:1}, {x:3, w:1, h:1, t:0}, {x:4, w:1, h:1, t:0}, {x:4.75, w:0.5, h:0.6, t:1}, {x:5, w:1, h:1, t:0}, {x:5.75, w:0.5, h:0.6, t:1}, {x:6, w:1, h:1, t:0}]; - E.prototype.onDrawForeground = function(c) { + u.registerNodeType("midi/play", e); + J.title = "MIDI Keys"; + J.desc = "Keyboard to play notes"; + J.color = "#243"; + J.keys = [{x:0, w:1, h:1, t:0}, {x:0.75, w:0.5, h:0.6, t:1}, {x:1, w:1, h:1, t:0}, {x:1.75, w:0.5, h:0.6, t:1}, {x:2, w:1, h:1, t:0}, {x:2.75, w:0.5, h:0.6, t:1}, {x:3, w:1, h:1, t:0}, {x:4, w:1, h:1, t:0}, {x:4.75, w:0.5, h:0.6, t:1}, {x:5, w:1, h:1, t:0}, {x:5.75, w:0.5, h:0.6, t:1}, {x:6, w:1, h:1, t:0}]; + J.prototype.onDrawForeground = function(c) { if (!this.flags.collapsed) { var e = 12 * this.properties.num_octaves; this.keys.length = e; - var k = this.size[0] / (7 * this.properties.num_octaves), a = this.size[1]; + var h = this.size[0] / (7 * this.properties.num_octaves), g = this.size[1]; c.globalAlpha = 1; - for (var b = 0; 2 > b; b++) { - for (var d = 0; d < e; ++d) { - var g = E.keys[d % 12]; - if (g.t == b) { - var f = 7 * Math.floor(d / 12) * k + g.x * k; - c.fillStyle = 0 == b ? this.keys[d] ? "#CCC" : "white" : this.keys[d] ? "#333" : "black"; - c.fillRect(f + 1, 0, k * g.w - 2, a * g.h); + for (var a = 0; 2 > a; a++) { + for (var b = 0; b < e; ++b) { + var d = J.keys[b % 12]; + if (d.t == a) { + var f = 7 * Math.floor(b / 12) * h + d.x * h; + c.fillStyle = 0 == a ? this.keys[b] ? "#CCC" : "white" : this.keys[b] ? "#333" : "black"; + c.fillRect(f + 1, 0, h * d.w - 2, g * d.h); } } } } }; - E.prototype.getKeyIndex = function(c) { - for (var e = this.size[0] / (7 * this.properties.num_octaves), k = this.size[1], a = 1; 0 <= a; a--) { - for (var b = 0; b < this.keys.length; ++b) { - var d = E.keys[b % 12]; - if (d.t == a) { - var g = 7 * Math.floor(b / 12) * e + d.x * e, f = e * d.w; - d = k * d.h; - if (!(c[0] < g || c[0] > g + f || c[1] > d)) { - return b; + J.prototype.getKeyIndex = function(c) { + for (var e = this.size[0] / (7 * this.properties.num_octaves), h = this.size[1], g = 1; 0 <= g; g--) { + for (var a = 0; a < this.keys.length; ++a) { + var b = J.keys[a % 12]; + if (b.t == g) { + var d = 7 * Math.floor(a / 12) * e + b.x * e, f = e * b.w; + b = h * b.h; + if (!(c[0] < d || c[0] > d + f || c[1] > b)) { + return a; } } } } return -1; }; - E.prototype.onAction = function(e, h) { + J.prototype.onAction = function(e, g) { if ("reset" == e) { - for (h = 0; h < this.keys.length; ++h) { - this.keys[h] = !1; + for (g = 0; g < this.keys.length; ++g) { + this.keys[g] = !1; } } else { - h && h.constructor === c && (e = h.data[1] - (12 * (this.properties.start_octave - 1) + 29), 0 <= e && e < this.keys.length && (h.data[0] == c.NOTEON ? this.keys[e] = !0 : h.data[0] == c.NOTEOFF && (this.keys[e] = !1)), this.trigger("note", h)); + g && g.constructor === c && (e = g.data[1] - (12 * (this.properties.start_octave - 1) + 29), 0 <= e && e < this.keys.length && (g.data[0] == c.NOTEON ? this.keys[e] = !0 : g.data[0] == c.NOTEOFF && (this.keys[e] = !1)), this.trigger("note", g)); } }; - E.prototype.onMouseDown = function(e, h) { - if (!(0 > h[1])) { - return e = this.getKeyIndex(h), this.keys[e] = !0, this._last_key = e, e = 12 * (this.properties.start_octave - 1) + 29 + e, h = new c, h.setup([c.NOTEON, e, 100]), this.trigger("note", h), !0; + J.prototype.onMouseDown = function(e, g) { + if (!(0 > g[1])) { + return e = this.getKeyIndex(g), this.keys[e] = !0, this._last_key = e, e = 12 * (this.properties.start_octave - 1) + 29 + e, g = new c, g.setup([c.NOTEON, e, 100]), this.trigger("note", g), !0; } }; - E.prototype.onMouseMove = function(e, h) { - if (!(0 > h[1] || -1 == this._last_key)) { + J.prototype.onMouseMove = function(e, g) { + if (!(0 > g[1] || -1 == this._last_key)) { this.setDirtyCanvas(!0); - e = this.getKeyIndex(h); + e = this.getKeyIndex(g); if (this._last_key == e) { return !0; } this.keys[this._last_key] = !1; - h = 12 * (this.properties.start_octave - 1) + 29 + this._last_key; - var k = new c; - k.setup([c.NOTEOFF, h, 100]); - this.trigger("note", k); + g = 12 * (this.properties.start_octave - 1) + 29 + this._last_key; + var h = new c; + h.setup([c.NOTEOFF, g, 100]); + this.trigger("note", h); this.keys[e] = !0; - h = 12 * (this.properties.start_octave - 1) + 29 + e; - k = new c; - k.setup([c.NOTEON, h, 100]); - this.trigger("note", k); + g = 12 * (this.properties.start_octave - 1) + 29 + e; + h = new c; + h.setup([c.NOTEON, g, 100]); + this.trigger("note", h); this._last_key = e; return !0; } }; - E.prototype.onMouseUp = function(e, h) { - if (!(0 > h[1])) { - return e = this.getKeyIndex(h), this.keys[e] = !1, this._last_key = -1, e = 12 * (this.properties.start_octave - 1) + 29 + e, h = new c, h.setup([c.NOTEOFF, e, 100]), this.trigger("note", h), !0; + J.prototype.onMouseUp = function(e, g) { + if (!(0 > g[1])) { + return e = this.getKeyIndex(g), this.keys[e] = !1, this._last_key = -1, e = 12 * (this.properties.start_octave - 1) + 29 + e, g = new c, g.setup([c.NOTEOFF, e, 100]), this.trigger("note", g), !0; } }; - u.registerNodeType("midi/keys", E); + u.registerNodeType("midi/keys", J); })(this); -(function(v) { +(function(w) { function c() { this.properties = {src:"", gain:0.5, loop:!0, autoplay:!0, playbackRate:1}; this._loading_audio = !1; @@ -9537,24 +9557,24 @@ $jscomp.polyfill("Object.values", function(v) { this._last_sourcenode = null; this.addOutput("out", "audio"); this.addInput("gain", "number"); - this.audionode = p.getAudioContext().createGain(); + this.audionode = n.getAudioContext().createGain(); this.audionode.graphnode = this; this.audionode.gain.value = this.properties.gain; this.properties.src && this.loadSound(this.properties.src); } - function r() { + function t() { this.properties = {gain:0.5}; this._audionodes = []; this._media_stream = null; this.addOutput("out", "audio"); this.addInput("gain", "number"); - this.audionode = p.getAudioContext().createGain(); + this.audionode = n.getAudioContext().createGain(); this.audionode.graphnode = this; this.audionode.gain.value = this.properties.gain; } - function m() { + function l() { this.properties = {fftSize:2048, minDecibels:-100, maxDecibels:-10, smoothingTimeConstant:0.5}; - this.audionode = p.getAudioContext().createAnalyser(); + this.audionode = n.getAudioContext().createAnalyser(); this.audionode.graphnode = this; this.audionode.fftSize = this.properties.fftSize; this.audionode.minDecibels = this.properties.minDecibels; @@ -9565,38 +9585,38 @@ $jscomp.polyfill("Object.values", function(v) { this.addOutput("samples", "array"); this._time_bin = this._freq_bin = null; } - function h() { + function p() { this.properties = {gain:1}; - this.audionode = p.getAudioContext().createGain(); + this.audionode = n.getAudioContext().createGain(); this.addInput("in", "audio"); this.addInput("gain", "number"); this.addOutput("out", "audio"); } function q() { this.properties = {impulse_src:"", normalize:!0}; - this.audionode = p.getAudioContext().createConvolver(); + this.audionode = n.getAudioContext().createConvolver(); this.addInput("in", "audio"); this.addOutput("out", "audio"); } - function l() { + function g() { this.properties = {threshold:-50, knee:40, ratio:12, reduction:-20, attack:0, release:0.25}; - this.audionode = p.getAudioContext().createDynamicsCompressor(); + this.audionode = n.getAudioContext().createDynamicsCompressor(); this.addInput("in", "audio"); this.addOutput("out", "audio"); } - function A() { + function B() { this.properties = {}; - this.audionode = p.getAudioContext().createWaveShaper(); + this.audionode = n.getAudioContext().createWaveShaper(); this.addInput("in", "audio"); this.addInput("shape", "waveshape"); this.addOutput("out", "audio"); } - function y() { + function A() { this.properties = {gain1:0.5, gain2:0.5}; - this.audionode = p.getAudioContext().createGain(); - this.audionode1 = p.getAudioContext().createGain(); + this.audionode = n.getAudioContext().createGain(); + this.audionode1 = n.getAudioContext().createGain(); this.audionode1.gain.value = this.properties.gain1; - this.audionode2 = p.getAudioContext().createGain(); + this.audionode2 = n.getAudioContext().createGain(); this.audionode2.gain.value = this.properties.gain2; this.audionode1.connect(this.audionode); this.audionode2.connect(this.audionode); @@ -9606,18 +9626,18 @@ $jscomp.polyfill("Object.values", function(v) { this.addInput("in2 gain", "number"); this.addOutput("out", "audio"); } - function x() { + function v() { this.properties = {A:0.1, D:0.1, S:0.1, R:0.1}; - this.audionode = p.getAudioContext().createGain(); + this.audionode = n.getAudioContext().createGain(); this.audionode.gain.value = 0; this.addInput("in", "audio"); this.addInput("gate", "bool"); this.addOutput("out", "audio"); this.gate = !1; } - function F() { + function G() { this.properties = {delayTime:0.5}; - this.audionode = p.getAudioContext().createDelay(10); + this.audionode = n.getAudioContext().createDelay(10); this.audionode.delayTime.value = this.properties.delayTime; this.addInput("in", "audio"); this.addInput("time", "number"); @@ -9626,48 +9646,48 @@ $jscomp.polyfill("Object.values", function(v) { function z() { this.properties = {frequency:350, detune:0, Q:1}; this.addProperty("type", "lowpass", "enum", {values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")}); - this.audionode = p.getAudioContext().createBiquadFilter(); + this.audionode = n.getAudioContext().createBiquadFilter(); this.addInput("in", "audio"); this.addOutput("out", "audio"); } - function e() { + function r() { this.properties = {frequency:440, detune:0, type:"sine"}; this.addProperty("type", "sine", "enum", {values:["sine", "square", "sawtooth", "triangle", "custom"]}); - this.audionode = p.getAudioContext().createOscillator(); + this.audionode = n.getAudioContext().createOscillator(); this.addOutput("out", "audio"); } - function B() { + function e() { this.properties = {continuous:!0, mark:-1}; this.addInput("data", "array"); this.addInput("mark", "number"); this.size = [300, 200]; this._last_buffer = null; } - function E() { + function J() { this.properties = {band:440, amplitude:1}; this.addInput("freqs", "array"); this.addOutput("signal", "number"); } function u() { if (!u.default_code) { - var c = u.default_function.toString(), a = c.indexOf("{") + 1, b = c.lastIndexOf("}"); - u.default_code = c.substr(a, b - a); + var c = u.default_function.toString(), e = c.indexOf("{") + 1, a = c.lastIndexOf("}"); + u.default_code = c.substr(e, a - e); } this.properties = {code:u.default_code}; - c = p.getAudioContext(); + c = n.getAudioContext(); c.createScriptProcessor ? this.audionode = c.createScriptProcessor(4096, 1, 1) : (console.warn("ScriptProcessorNode deprecated"), this.audionode = c.createGain()); this.processCode(); u._bypass_function || (u._bypass_function = this.audionode.onaudioprocess); this.addInput("in", "audio"); this.addOutput("out", "audio"); } - function H() { - this.audionode = p.getAudioContext().destination; + function C() { + this.audionode = n.getAudioContext().destination; this.addInput("in", "audio"); } - var n = v.LiteGraph, p = {}; - v.LGAudio = p; - p.getAudioContext = function() { + var m = w.LiteGraph, n = {}; + w.LGAudio = n; + n.getAudioContext = function() { if (!this._audio_context) { window.AudioContext = window.AudioContext || window.webkitAudioContext; if (!window.AudioContext) { @@ -9686,80 +9706,80 @@ $jscomp.polyfill("Object.values", function(v) { } return this._audio_context; }; - p.connect = function(c, a) { + n.connect = function(c, e) { try { - c.connect(a); - } catch (b) { - console.warn("LGraphAudio:", b); + c.connect(e); + } catch (a) { + console.warn("LGraphAudio:", a); } }; - p.disconnect = function(c, a) { + n.disconnect = function(c, e) { try { - c.disconnect(a); - } catch (b) { - console.warn("LGraphAudio:", b); + c.disconnect(e); + } catch (a) { + console.warn("LGraphAudio:", a); } }; - p.changeAllAudiosConnections = function(c, a) { + n.changeAllAudiosConnections = function(c, e) { if (c.inputs) { - for (var b = 0; b < c.inputs.length; ++b) { - var d = c.graph.links[c.inputs[b].link]; - if (d) { - var e = c.graph.getNodeById(d.origin_id); - e = e.getAudioNodeInOutputSlot ? e.getAudioNodeInOutputSlot(d.origin_slot) : e.audionode; - d = c.getAudioNodeInInputSlot ? c.getAudioNodeInInputSlot(b) : c.audionode; - a ? p.connect(e, d) : p.disconnect(e, d); + for (var a = 0; a < c.inputs.length; ++a) { + var b = c.graph.links[c.inputs[a].link]; + if (b) { + var d = c.graph.getNodeById(b.origin_id); + d = d.getAudioNodeInOutputSlot ? d.getAudioNodeInOutputSlot(b.origin_slot) : d.audionode; + b = c.getAudioNodeInInputSlot ? c.getAudioNodeInInputSlot(a) : c.audionode; + e ? n.connect(d, b) : n.disconnect(d, b); } } } if (c.outputs) { - for (b = 0; b < c.outputs.length; ++b) { - for (var k = c.outputs[b], h = 0; h < k.links.length; ++h) { - if (d = c.graph.links[k.links[h]]) { - e = c.getAudioNodeInOutputSlot ? c.getAudioNodeInOutputSlot(b) : c.audionode; - var l = c.graph.getNodeById(d.target_id); - d = l.getAudioNodeInInputSlot ? l.getAudioNodeInInputSlot(d.target_slot) : l.audionode; - a ? p.connect(e, d) : p.disconnect(e, d); + for (a = 0; a < c.outputs.length; ++a) { + for (var h = c.outputs[a], g = 0; g < h.links.length; ++g) { + if (b = c.graph.links[h.links[g]]) { + d = c.getAudioNodeInOutputSlot ? c.getAudioNodeInOutputSlot(a) : c.audionode; + var l = c.graph.getNodeById(b.target_id); + b = l.getAudioNodeInInputSlot ? l.getAudioNodeInInputSlot(b.target_slot) : l.audionode; + e ? n.connect(d, b) : n.disconnect(d, b); } } } } }; - p.onConnectionsChange = function(c, a, b, d) { - c == n.OUTPUT && (c = null, d && (c = this.graph.getNodeById(d.target_id)), c && (a = this.getAudioNodeInOutputSlot ? this.getAudioNodeInOutputSlot(a) : this.audionode, d = c.getAudioNodeInInputSlot ? c.getAudioNodeInInputSlot(d.target_slot) : c.audionode, b ? p.connect(a, d) : p.disconnect(a, d))); + n.onConnectionsChange = function(c, e, a, b) { + c == m.OUTPUT && (c = null, b && (c = this.graph.getNodeById(b.target_id)), c && (e = this.getAudioNodeInOutputSlot ? this.getAudioNodeInOutputSlot(e) : this.audionode, b = c.getAudioNodeInInputSlot ? c.getAudioNodeInInputSlot(b.target_slot) : c.audionode, a ? n.connect(e, b) : n.disconnect(e, b))); }; - p.createAudioNodeWrapper = function(c) { - var a = c.prototype.onPropertyChanged; - c.prototype.onPropertyChanged = function(b, d) { - a && a.call(this, b, d); - this.audionode && void 0 !== this.audionode[b] && (void 0 !== this.audionode[b].value ? this.audionode[b].value = d : this.audionode[b] = d); + n.createAudioNodeWrapper = function(c) { + var e = c.prototype.onPropertyChanged; + c.prototype.onPropertyChanged = function(a, b) { + e && e.call(this, a, b); + this.audionode && void 0 !== this.audionode[a] && (void 0 !== this.audionode[a].value ? this.audionode[a].value = b : this.audionode[a] = b); }; - c.prototype.onConnectionsChange = p.onConnectionsChange; + c.prototype.onConnectionsChange = n.onConnectionsChange; }; - p.cached_audios = {}; - p.loadSound = function(c, a, b) { - function d(a) { - console.log("Audio loading sample error:", a); - b && b(a); + n.cached_audios = {}; + n.loadSound = function(c, e, a) { + function b(b) { + console.log("Audio loading sample error:", b); + a && a(b); } - if (p.cached_audios[c] && -1 == c.indexOf("blob:")) { - a && a(p.cached_audios[c]); + if (n.cached_audios[c] && -1 == c.indexOf("blob:")) { + e && e(n.cached_audios[c]); } else { - p.onProcessAudioURL && (c = p.onProcessAudioURL(c)); - var e = new XMLHttpRequest; - e.open("GET", c, !0); - e.responseType = "arraybuffer"; - var k = p.getAudioContext(); - e.onload = function() { + n.onProcessAudioURL && (c = n.onProcessAudioURL(c)); + var d = new XMLHttpRequest; + d.open("GET", c, !0); + d.responseType = "arraybuffer"; + var h = n.getAudioContext(); + d.onload = function() { console.log("AudioSource loaded"); - k.decodeAudioData(e.response, function(b) { + h.decodeAudioData(d.response, function(a) { console.log("AudioSource decoded"); - p.cached_audios[c] = b; - a && a(b); - }, d); + n.cached_audios[c] = a; + e && e(a); + }, b); }; - e.send(); - return e; + d.send(); + return d; } }; c.desc = "Plays an audio file"; @@ -9793,27 +9813,27 @@ $jscomp.polyfill("Object.values", function(v) { this._audionodes.length = 0; }; c.prototype.pauseAllSounds = function() { - p.getAudioContext().suspend(); + n.getAudioContext().suspend(); }; c.prototype.unpauseAllSounds = function() { - p.getAudioContext().resume(); + n.getAudioContext().resume(); }; c.prototype.onExecute = function() { if (this.inputs) { for (var c = 0; c < this.inputs.length; ++c) { - var a = this.inputs[c]; - if (null != a.link) { - var b = this.getInputData(c); - if (void 0 !== b) { - if ("gain" == a.name) { - this.audionode.gain.value = b; + var e = this.inputs[c]; + if (null != e.link) { + var a = this.getInputData(c); + if (void 0 !== a) { + if ("gain" == e.name) { + this.audionode.gain.value = a; } else { - if ("src" == a.name) { - this.setProperty("src", b); + if ("src" == e.name) { + this.setProperty("src", a); } else { - if ("playbackRate" == a.name) { - for (this.properties.playbackRate = b, a = 0; a < this._audionodes.length; ++a) { - this._audionodes[a].playbackRate.value = b; + if ("playbackRate" == e.name) { + for (this.properties.playbackRate = a, e = 0; e < this._audionodes.length; ++e) { + this._audionodes[e].playbackRate.value = a; } } } @@ -9831,60 +9851,60 @@ $jscomp.polyfill("Object.values", function(v) { c.prototype.onAction = function(c) { this._audiobuffer && ("Play" == c ? this.playBuffer(this._audiobuffer) : "Stop" == c && this.stopAllSounds()); }; - c.prototype.onPropertyChanged = function(c, a) { + c.prototype.onPropertyChanged = function(c, e) { if ("src" == c) { - this.loadSound(a); + this.loadSound(e); } else { if ("gain" == c) { - this.audionode.gain.value = a; + this.audionode.gain.value = e; } else { if ("playbackRate" == c) { for (c = 0; c < this._audionodes.length; ++c) { - this._audionodes[c].playbackRate.value = a; + this._audionodes[c].playbackRate.value = e; } } } } }; c.prototype.playBuffer = function(c) { - var a = this, b = p.getAudioContext().createBufferSource(); - this._last_sourcenode = b; - b.graphnode = this; - b.buffer = c; - b.loop = this.properties.loop; - b.playbackRate.value = this.properties.playbackRate; - this._audionodes.push(b); - b.connect(this.audionode); - this._audionodes.push(b); + var e = this, a = n.getAudioContext().createBufferSource(); + this._last_sourcenode = a; + a.graphnode = this; + a.buffer = c; + a.loop = this.properties.loop; + a.playbackRate.value = this.properties.playbackRate; + this._audionodes.push(a); + a.connect(this.audionode); + this._audionodes.push(a); this.trigger("start"); - b.onended = function() { - a.trigger("ended"); - var c = a._audionodes.indexOf(b); - -1 != c && a._audionodes.splice(c, 1); + a.onended = function() { + e.trigger("ended"); + var b = e._audionodes.indexOf(a); + -1 != b && e._audionodes.splice(b, 1); }; - b.started || (b.started = !0, b.start()); - return b; + a.started || (a.started = !0, a.start()); + return a; }; c.prototype.loadSound = function(c) { - var a = this; + var e = this; this._request && (this._request.abort(), this._request = null); this._audiobuffer = null; this._loading_audio = !1; - c && (this._request = p.loadSound(c, function(b) { - this.boxcolor = n.NODE_DEFAULT_BOXCOLOR; - a._audiobuffer = b; - a._loading_audio = !1; - if (a.graph && a.graph.status === LGraph.STATUS_RUNNING) { - a.onStart(); + c && (this._request = n.loadSound(c, function(a) { + this.boxcolor = m.NODE_DEFAULT_BOXCOLOR; + e._audiobuffer = a; + e._loading_audio = !1; + if (e.graph && e.graph.status === LGraph.STATUS_RUNNING) { + e.onStart(); } }), this._loading_audio = !0, this.boxcolor = "#AA4"); }; - c.prototype.onConnectionsChange = p.onConnectionsChange; + c.prototype.onConnectionsChange = n.onConnectionsChange; c.prototype.onGetInputs = function() { - return [["playbackRate", "number"], ["src", "string"], ["Play", n.ACTION], ["Stop", n.ACTION]]; + return [["playbackRate", "number"], ["src", "string"], ["Play", m.ACTION], ["Stop", m.ACTION]]; }; c.prototype.onGetOutputs = function() { - return [["buffer", "audiobuffer"], ["start", n.EVENT], ["ended", n.EVENT]]; + return [["buffer", "audiobuffer"], ["start", m.EVENT], ["ended", m.EVENT]]; }; c.prototype.onDropFile = function(c) { this._dropped_url && URL.revokeObjectURL(this._dropped_url); @@ -9895,25 +9915,25 @@ $jscomp.polyfill("Object.values", function(v) { }; c.title = "Source"; c.desc = "Plays audio"; - n.registerNodeType("audio/source", c); - r.prototype.onAdded = function(c) { + m.registerNodeType("audio/source", c); + t.prototype.onAdded = function(c) { if (c.status === LGraph.STATUS_RUNNING) { this.onStart(); } }; - r.prototype.onStart = function() { + t.prototype.onStart = function() { null != this._media_stream || this._waiting_confirmation || this.openStream(); }; - r.prototype.onStop = function() { + t.prototype.onStop = function() { this.audionode.gain.value = 0; }; - r.prototype.onPause = function() { + t.prototype.onPause = function() { this.audionode.gain.value = 0; }; - r.prototype.onUnpause = function() { + t.prototype.onUnpause = function() { this.audionode.gain.value = this.properties.gain; }; - r.prototype.onRemoved = function() { + t.prototype.onRemoved = function() { this.audionode.gain.value = 0; this.audiosource_node && (this.audiosource_node.disconnect(this.audionode), this.audiosource_node = null); if (this._media_stream) { @@ -9921,11 +9941,11 @@ $jscomp.polyfill("Object.values", function(v) { c.length && c[0].stop(); } }; - r.prototype.openStream = function() { + t.prototype.openStream = function() { if (navigator.mediaDevices) { this._waiting_confirmation = !0; - navigator.mediaDevices.getUserMedia({audio:!0, video:!1}).then(this.streamReady.bind(this)).catch(function(a) { - console.log("Media rejected", a); + navigator.mediaDevices.getUserMedia({audio:!0, video:!1}).then(this.streamReady.bind(this)).catch(function(e) { + console.log("Media rejected", e); c._media_stream = !1; c.boxcolor = "red"; }); @@ -9934,43 +9954,43 @@ $jscomp.polyfill("Object.values", function(v) { console.log("getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags"); } }; - r.prototype.streamReady = function(c) { + t.prototype.streamReady = function(c) { this._media_stream = c; this.audiosource_node && this.audiosource_node.disconnect(this.audionode); - this.audiosource_node = p.getAudioContext().createMediaStreamSource(c); + this.audiosource_node = n.getAudioContext().createMediaStreamSource(c); this.audiosource_node.graphnode = this; this.audiosource_node.connect(this.audionode); this.boxcolor = "white"; }; - r.prototype.onExecute = function() { + t.prototype.onExecute = function() { null != this._media_stream || this._waiting_confirmation || this.openStream(); if (this.inputs) { for (var c = 0; c < this.inputs.length; ++c) { - var a = this.inputs[c]; - if (null != a.link) { - var b = this.getInputData(c); - void 0 !== b && "gain" == a.name && (this.audionode.gain.value = this.properties.gain = b); + var e = this.inputs[c]; + if (null != e.link) { + var a = this.getInputData(c); + void 0 !== a && "gain" == e.name && (this.audionode.gain.value = this.properties.gain = a); } } } }; - r.prototype.onAction = function(c) { + t.prototype.onAction = function(c) { "Play" == c ? this.audionode.gain.value = this.properties.gain : "Stop" == c && (this.audionode.gain.value = 0); }; - r.prototype.onPropertyChanged = function(c, a) { - "gain" == c && (this.audionode.gain.value = a); + t.prototype.onPropertyChanged = function(c, e) { + "gain" == c && (this.audionode.gain.value = e); }; - r.prototype.onConnectionsChange = p.onConnectionsChange; - r.prototype.onGetInputs = function() { - return [["playbackRate", "number"], ["Play", n.ACTION], ["Stop", n.ACTION]]; + t.prototype.onConnectionsChange = n.onConnectionsChange; + t.prototype.onGetInputs = function() { + return [["playbackRate", "number"], ["Play", m.ACTION], ["Stop", m.ACTION]]; }; - r.title = "MediaSource"; - r.desc = "Plays microphone"; - n.registerNodeType("audio/media_source", r); - m.prototype.onPropertyChanged = function(c, a) { - this.audionode[c] = a; + t.title = "MediaSource"; + t.desc = "Plays microphone"; + m.registerNodeType("audio/media_source", t); + l.prototype.onPropertyChanged = function(c, e) { + this.audionode[c] = e; }; - m.prototype.onExecute = function() { + l.prototype.onExecute = function() { if (this.isOutputConnected(0)) { var c = this.audionode.frequencyBinCount; this._freq_bin && this._freq_bin.length == c || (this._freq_bin = new Uint8Array(c)); @@ -9979,40 +9999,40 @@ $jscomp.polyfill("Object.values", function(v) { } this.isOutputConnected(1) && (c = this.audionode.frequencyBinCount, this._time_bin && this._time_bin.length == c || (this._time_bin = new Uint8Array(c)), this.audionode.getByteTimeDomainData(this._time_bin), this.setOutputData(1, this._time_bin)); for (c = 1; c < this.inputs.length; ++c) { - var a = this.inputs[c]; - if (null != a.link) { - var b = this.getInputData(c); - void 0 !== b && (this.audionode[a.name].value = b); + var e = this.inputs[c]; + if (null != e.link) { + var a = this.getInputData(c); + void 0 !== a && (this.audionode[e.name].value = a); } } }; - m.prototype.onGetInputs = function() { + l.prototype.onGetInputs = function() { return [["minDecibels", "number"], ["maxDecibels", "number"], ["smoothingTimeConstant", "number"]]; }; - m.prototype.onGetOutputs = function() { + l.prototype.onGetOutputs = function() { return [["freqs", "array"], ["samples", "array"]]; }; - m.title = "Analyser"; - m.desc = "Audio Analyser"; - n.registerNodeType("audio/analyser", m); - h.prototype.onExecute = function() { + l.title = "Analyser"; + l.desc = "Audio Analyser"; + m.registerNodeType("audio/analyser", l); + p.prototype.onExecute = function() { if (this.inputs && this.inputs.length) { for (var c = 1; c < this.inputs.length; ++c) { - var a = this.inputs[c], b = this.getInputData(c); - void 0 !== b && (this.audionode[a.name].value = b); + var e = this.inputs[c], a = this.getInputData(c); + void 0 !== a && (this.audionode[e.name].value = a); } } }; - p.createAudioNodeWrapper(h); - h.title = "Gain"; - h.desc = "Audio gain"; - n.registerNodeType("audio/gain", h); - p.createAudioNodeWrapper(q); + n.createAudioNodeWrapper(p); + p.title = "Gain"; + p.desc = "Audio gain"; + m.registerNodeType("audio/gain", p); + n.createAudioNodeWrapper(q); q.prototype.onRemove = function() { this._dropped_url && URL.revokeObjectURL(this._dropped_url); }; - q.prototype.onPropertyChanged = function(c, a) { - "impulse_src" == c ? this.loadImpulse(a) : "normalize" == c && (this.audionode.normalize = a); + q.prototype.onPropertyChanged = function(c, e) { + "impulse_src" == c ? this.loadImpulse(e) : "normalize" == c && (this.audionode.normalize = e); }; q.prototype.onDropFile = function(c) { this._dropped_url && URL.revokeObjectURL(this._dropped_url); @@ -10021,49 +10041,49 @@ $jscomp.polyfill("Object.values", function(v) { this.loadImpulse(this._dropped_url); }; q.prototype.loadImpulse = function(c) { - var a = this; + var e = this; this._request && (this._request.abort(), this._request = null); this._impulse_buffer = null; this._loading_impulse = !1; - c && (this._request = p.loadSound(c, function(b) { - a._impulse_buffer = b; - a.audionode.buffer = b; + c && (this._request = n.loadSound(c, function(a) { + e._impulse_buffer = a; + e.audionode.buffer = a; console.log("Impulse signal set"); - a._loading_impulse = !1; + e._loading_impulse = !1; }), this._loading_impulse = !0); }; q.title = "Convolver"; q.desc = "Convolves the signal (used for reverb)"; - n.registerNodeType("audio/convolver", q); - p.createAudioNodeWrapper(l); - l.prototype.onExecute = function() { + m.registerNodeType("audio/convolver", q); + n.createAudioNodeWrapper(g); + g.prototype.onExecute = function() { if (this.inputs && this.inputs.length) { for (var c = 1; c < this.inputs.length; ++c) { - var a = this.inputs[c]; - if (null != a.link) { - var b = this.getInputData(c); - void 0 !== b && (this.audionode[a.name].value = b); + var e = this.inputs[c]; + if (null != e.link) { + var a = this.getInputData(c); + void 0 !== a && (this.audionode[e.name].value = a); } } } }; - l.prototype.onGetInputs = function() { + g.prototype.onGetInputs = function() { return [["threshold", "number"], ["knee", "number"], ["ratio", "number"], ["reduction", "number"], ["attack", "number"], ["release", "number"]]; }; - l.title = "DynamicsCompressor"; - l.desc = "Dynamics Compressor"; - n.registerNodeType("audio/dynamicsCompressor", l); - A.prototype.onExecute = function() { + g.title = "DynamicsCompressor"; + g.desc = "Dynamics Compressor"; + m.registerNodeType("audio/dynamicsCompressor", g); + B.prototype.onExecute = function() { if (this.inputs && this.inputs.length) { var c = this.getInputData(1); void 0 !== c && (this.audionode.curve = c); } }; - A.prototype.setWaveShape = function(c) { + B.prototype.setWaveShape = function(c) { this.audionode.curve = c; }; - p.createAudioNodeWrapper(A); - y.prototype.getAudioNodeInInputSlot = function(c) { + n.createAudioNodeWrapper(B); + A.prototype.getAudioNodeInInputSlot = function(c) { if (0 == c) { return this.audionode1; } @@ -10071,48 +10091,48 @@ $jscomp.polyfill("Object.values", function(v) { return this.audionode2; } }; - y.prototype.onPropertyChanged = function(c, a) { - "gain1" == c ? this.audionode1.gain.value = a : "gain2" == c && (this.audionode2.gain.value = a); + A.prototype.onPropertyChanged = function(c, e) { + "gain1" == c ? this.audionode1.gain.value = e : "gain2" == c && (this.audionode2.gain.value = e); }; - y.prototype.onExecute = function() { + A.prototype.onExecute = function() { if (this.inputs && this.inputs.length) { for (var c = 1; c < this.inputs.length; ++c) { - var a = this.inputs[c]; - null != a.link && "audio" != a.type && (a = this.getInputData(c), void 0 !== a && (1 == c ? this.audionode1.gain.value = a : 3 == c && (this.audionode2.gain.value = a))); + var e = this.inputs[c]; + null != e.link && "audio" != e.type && (e = this.getInputData(c), void 0 !== e && (1 == c ? this.audionode1.gain.value = e : 3 == c && (this.audionode2.gain.value = e))); } } }; - p.createAudioNodeWrapper(y); - y.title = "Mixer"; - y.desc = "Audio mixer"; - n.registerNodeType("audio/mixer", y); - x.prototype.onExecute = function() { - var c = p.getAudioContext().currentTime, a = this.audionode.gain, b = this.getInputData(1), d = this.getInputOrProperty("A"), e = this.getInputOrProperty("D"), f = this.getInputOrProperty("S"), h = this.getInputOrProperty("R"); - !this.gate && b ? (a.cancelScheduledValues(0), a.setValueAtTime(0, c), a.linearRampToValueAtTime(1, c + d), a.linearRampToValueAtTime(f, c + d + e)) : this.gate && !b && (a.cancelScheduledValues(0), a.setValueAtTime(a.value, c), a.linearRampToValueAtTime(0, c + h)); - this.gate = b; + n.createAudioNodeWrapper(A); + A.title = "Mixer"; + A.desc = "Audio mixer"; + m.registerNodeType("audio/mixer", A); + v.prototype.onExecute = function() { + var c = n.getAudioContext().currentTime, e = this.audionode.gain, a = this.getInputData(1), b = this.getInputOrProperty("A"), d = this.getInputOrProperty("D"), f = this.getInputOrProperty("S"), g = this.getInputOrProperty("R"); + !this.gate && a ? (e.cancelScheduledValues(0), e.setValueAtTime(0, c), e.linearRampToValueAtTime(1, c + b), e.linearRampToValueAtTime(f, c + b + d)) : this.gate && !a && (e.cancelScheduledValues(0), e.setValueAtTime(e.value, c), e.linearRampToValueAtTime(0, c + g)); + this.gate = a; }; - x.prototype.onGetInputs = function() { + v.prototype.onGetInputs = function() { return [["A", "number"], ["D", "number"], ["S", "number"], ["R", "number"]]; }; - p.createAudioNodeWrapper(x); - x.title = "ADSR"; - x.desc = "Audio envelope"; - n.registerNodeType("audio/adsr", x); - p.createAudioNodeWrapper(F); - F.prototype.onExecute = function() { + n.createAudioNodeWrapper(v); + v.title = "ADSR"; + v.desc = "Audio envelope"; + m.registerNodeType("audio/adsr", v); + n.createAudioNodeWrapper(G); + G.prototype.onExecute = function() { var c = this.getInputData(1); void 0 !== c && (this.audionode.delayTime.value = c); }; - F.title = "Delay"; - F.desc = "Audio delay"; - n.registerNodeType("audio/delay", F); + G.title = "Delay"; + G.desc = "Audio delay"; + m.registerNodeType("audio/delay", G); z.prototype.onExecute = function() { if (this.inputs && this.inputs.length) { for (var c = 1; c < this.inputs.length; ++c) { - var a = this.inputs[c]; - if (null != a.link) { - var b = this.getInputData(c); - void 0 !== b && (this.audionode[a.name].value = b); + var e = this.inputs[c]; + if (null != e.link) { + var a = this.getInputData(c); + void 0 !== a && (this.audionode[e.name].value = a); } } } @@ -10120,93 +10140,93 @@ $jscomp.polyfill("Object.values", function(v) { z.prototype.onGetInputs = function() { return [["frequency", "number"], ["detune", "number"], ["Q", "number"]]; }; - p.createAudioNodeWrapper(z); + n.createAudioNodeWrapper(z); z.title = "BiquadFilter"; z.desc = "Audio filter"; - n.registerNodeType("audio/biquadfilter", z); - e.prototype.onStart = function() { + m.registerNodeType("audio/biquadfilter", z); + r.prototype.onStart = function() { if (!this.audionode.started) { this.audionode.started = !0; try { this.audionode.start(); - } catch (k) { + } catch (h) { } } }; - e.prototype.onStop = function() { + r.prototype.onStop = function() { this.audionode.started && (this.audionode.started = !1, this.audionode.stop()); }; - e.prototype.onPause = function() { + r.prototype.onPause = function() { this.onStop(); }; - e.prototype.onUnpause = function() { + r.prototype.onUnpause = function() { this.onStart(); }; - e.prototype.onExecute = function() { + r.prototype.onExecute = function() { if (this.inputs && this.inputs.length) { for (var c = 0; c < this.inputs.length; ++c) { - var a = this.inputs[c]; - if (null != a.link) { - var b = this.getInputData(c); - void 0 !== b && (this.audionode[a.name].value = b); + var e = this.inputs[c]; + if (null != e.link) { + var a = this.getInputData(c); + void 0 !== a && (this.audionode[e.name].value = a); } } } }; - e.prototype.onGetInputs = function() { + r.prototype.onGetInputs = function() { return [["frequency", "number"], ["detune", "number"], ["type", "string"]]; }; - p.createAudioNodeWrapper(e); - e.title = "Oscillator"; - e.desc = "Oscillator"; - n.registerNodeType("audio/oscillator", e); - B.prototype.onExecute = function() { + n.createAudioNodeWrapper(r); + r.title = "Oscillator"; + r.desc = "Oscillator"; + m.registerNodeType("audio/oscillator", r); + e.prototype.onExecute = function() { this._last_buffer = this.getInputData(0); var c = this.getInputData(1); void 0 !== c && (this.properties.mark = c); this.setDirtyCanvas(!0, !1); }; - B.prototype.onDrawForeground = function(c) { + e.prototype.onDrawForeground = function(c) { if (this._last_buffer) { - var a = this._last_buffer, b = a.length / this.size[0], d = this.size[1]; + var e = this._last_buffer, a = e.length / this.size[0], b = this.size[1]; c.fillStyle = "black"; c.fillRect(0, 0, this.size[0], this.size[1]); c.strokeStyle = "white"; c.beginPath(); - var e = 0; + var d = 0; if (this.properties.continuous) { - c.moveTo(e, d); - for (var f = 0; f < a.length; f += b) { - c.lineTo(e, d - a[f | 0] / 255 * d), e++; + c.moveTo(d, b); + for (var f = 0; f < e.length; f += a) { + c.lineTo(d, b - e[f | 0] / 255 * b), d++; } } else { - for (f = 0; f < a.length; f += b) { - c.moveTo(e + 0.5, d), c.lineTo(e + 0.5, d - a[f | 0] / 255 * d), e++; + for (f = 0; f < e.length; f += a) { + c.moveTo(d + 0.5, b), c.lineTo(d + 0.5, b - e[f | 0] / 255 * b), d++; } } c.stroke(); - 0 <= this.properties.mark && (a = p.getAudioContext().sampleRate / a.length, e = this.properties.mark / a * 2 / b, e >= this.size[0] && (e = this.size[0] - 1), c.strokeStyle = "red", c.beginPath(), c.moveTo(e, d), c.lineTo(e, 0), c.stroke()); + 0 <= this.properties.mark && (e = n.getAudioContext().sampleRate / e.length, d = this.properties.mark / e * 2 / a, d >= this.size[0] && (d = this.size[0] - 1), c.strokeStyle = "red", c.beginPath(), c.moveTo(d, b), c.lineTo(d, 0), c.stroke()); } }; - B.title = "Visualization"; - B.desc = "Audio Visualization"; - n.registerNodeType("audio/visualization", B); - E.prototype.onExecute = function() { + e.title = "Visualization"; + e.desc = "Audio Visualization"; + m.registerNodeType("audio/visualization", e); + J.prototype.onExecute = function() { if (this._freqs = this.getInputData(0)) { - var c = this.properties.band, a = this.getInputData(1); - void 0 !== a && (c = a); - a = p.getAudioContext().sampleRate / this._freqs.length; - a = c / a * 2; - a >= this._freqs.length ? a = this._freqs[this._freqs.length - 1] : (c = a | 0, a -= c, a = this._freqs[c] * (1 - a) + this._freqs[c + 1] * a); - this.setOutputData(0, a / 255 * this.properties.amplitude); + var c = this.properties.band, e = this.getInputData(1); + void 0 !== e && (c = e); + e = n.getAudioContext().sampleRate / this._freqs.length; + e = c / e * 2; + e >= this._freqs.length ? e = this._freqs[this._freqs.length - 1] : (c = e | 0, e -= c, e = this._freqs[c] * (1 - e) + this._freqs[c + 1] * e); + this.setOutputData(0, e / 255 * this.properties.amplitude); } }; - E.prototype.onGetInputs = function() { + J.prototype.onGetInputs = function() { return [["band", "number"]]; }; - E.title = "Signal"; - E.desc = "extract the signal of some frequency"; - n.registerNodeType("audio/signal", E); + J.title = "Signal"; + J.desc = "extract the signal of some frequency"; + m.registerNodeType("audio/signal", J); u.prototype.onAdded = function(c) { c.status == LGraph.STATUS_RUNNING && (this.audionode.onaudioprocess = this._callback); }; @@ -10231,37 +10251,37 @@ $jscomp.polyfill("Object.values", function(v) { u.prototype.processCode = function() { try { this._script = new (new Function("properties", this.properties.code))(this.properties), this._old_code = this.properties.code, this._callback = this._script.onaudioprocess; - } catch (k) { - console.error("Error in onaudioprocess code", k), this._callback = u._bypass_function, this.audionode.onaudioprocess = this._callback; + } catch (h) { + console.error("Error in onaudioprocess code", h), this._callback = u._bypass_function, this.audionode.onaudioprocess = this._callback; } }; - u.prototype.onPropertyChanged = function(c, a) { - "code" == c && (this.properties.code = a, this.processCode(), this.graph && this.graph.status == LGraph.STATUS_RUNNING && (this.audionode.onaudioprocess = this._callback)); + u.prototype.onPropertyChanged = function(c, e) { + "code" == c && (this.properties.code = e, this.processCode(), this.graph && this.graph.status == LGraph.STATUS_RUNNING && (this.audionode.onaudioprocess = this._callback)); }; u.default_function = function() { this.onaudioprocess = function(c) { - var a = c.inputBuffer; + var e = c.inputBuffer; c = c.outputBuffer; - for (var b = 0; b < c.numberOfChannels; b++) { - for (var d = a.getChannelData(b), e = c.getChannelData(b), f = 0; f < a.length; f++) { - e[f] = d[f]; + for (var a = 0; a < c.numberOfChannels; a++) { + for (var b = e.getChannelData(a), d = c.getChannelData(a), f = 0; f < e.length; f++) { + d[f] = b[f]; } } }; }; - p.createAudioNodeWrapper(u); + n.createAudioNodeWrapper(u); u.title = "Script"; u.desc = "apply script to signal"; - n.registerNodeType("audio/script", u); - H.title = "Destination"; - H.desc = "Audio output"; - n.registerNodeType("audio/destination", H); + m.registerNodeType("audio/script", u); + C.title = "Destination"; + C.desc = "Audio output"; + m.registerNodeType("audio/destination", C); })(this); -(function(v) { +(function(w) { function c() { this.size = [60, 20]; - this.addInput("send", m.ACTION); - this.addOutput("received", m.EVENT); + this.addInput("send", l.ACTION); + this.addOutput("received", l.EVENT); this.addInput("in", 0); this.addOutput("out", 0); this.properties = {url:"", room:"lgraph", only_send_changes:!0}; @@ -10269,11 +10289,11 @@ $jscomp.polyfill("Object.values", function(v) { this._last_sent_data = []; this._last_received_data = []; } - function r() { + function t() { this.room_widget = this.addWidget("text", "Room", "lgraph", this.setRoom.bind(this)); this.addWidget("button", "Reconnect", null, this.connectSocket.bind(this)); - this.addInput("send", m.ACTION); - this.addOutput("received", m.EVENT); + this.addInput("send", l.ACTION); + this.addOutput("received", l.EVENT); this.addInput("in", 0); this.addOutput("out", 0); this.properties = {url:"tamats.com:55000", room:"lgraph", only_send_changes:!0}; @@ -10283,28 +10303,28 @@ $jscomp.polyfill("Object.values", function(v) { this._last_received_data = []; "undefined" == typeof SillyClient && console.warn("remember to add SillyClient.js to your project: https://tamats.com/projects/sillyserver/src/sillyclient.js"); } - var m = v.LiteGraph; + var l = w.LiteGraph; c.title = "WebSocket"; c.desc = "Send data through a websocket"; - c.prototype.onPropertyChanged = function(c, m) { + c.prototype.onPropertyChanged = function(c, l) { "url" == c && this.connectSocket(); }; c.prototype.onExecute = function() { !this._ws && this.properties.url && this.connectSocket(); if (this._ws && this._ws.readyState == WebSocket.OPEN) { - for (var c = this.properties.room, m = this.properties.only_send_changes, l = 1; l < this.inputs.length; ++l) { - var r = this.getInputData(l); - if (null != r) { + for (var c = this.properties.room, l = this.properties.only_send_changes, g = 1; g < this.inputs.length; ++g) { + var t = this.getInputData(g); + if (null != t) { try { - var v = JSON.stringify({type:0, room:c, channel:l, data:r}); - } catch (x) { + var w = JSON.stringify({type:0, room:c, channel:g, data:t}); + } catch (v) { continue; } - m && this._last_sent_data[l] == v || (this._last_sent_data[l] = v, this._ws.send(v)); + l && this._last_sent_data[g] == w || (this._last_sent_data[g] = w, this._ws.send(w)); } } - for (l = 1; l < this.outputs.length; ++l) { - this.setOutputData(l, this._last_received_data[l]); + for (g = 1; g < this.outputs.length; ++g) { + this.setOutputData(g, this._last_received_data[g]); } "#AFA" == this.boxcolor && (this.boxcolor = "#6C6"); } @@ -10317,30 +10337,30 @@ $jscomp.polyfill("Object.values", function(v) { console.log("ready"); c.boxcolor = "#6C6"; }; - this._ws.onmessage = function(h) { + this._ws.onmessage = function(g) { c.boxcolor = "#AFA"; - h = JSON.parse(h.data); - if (!h.room || h.room == c.properties.room) { - if (1 == h.type) { - if (h.data.object_class && m[h.data.object_class]) { - var l = null; + g = JSON.parse(g.data); + if (!g.room || g.room == c.properties.room) { + if (1 == g.type) { + if (g.data.object_class && l[g.data.object_class]) { + var p = null; try { - l = new m[h.data.object_class](h.data), c.triggerSlot(0, l); - } catch (y) { + p = new l[g.data.object_class](g.data), c.triggerSlot(0, p); + } catch (A) { } } else { - c.triggerSlot(0, h.data); + c.triggerSlot(0, g.data); } } else { - c._last_received_data[h.channel || 0] = h.data; + c._last_received_data[g.channel || 0] = g.data; } } }; - this._ws.onerror = function(h) { + this._ws.onerror = function(g) { console.log("couldnt connect to websocket"); c.boxcolor = "#E88"; }; - this._ws.onclose = function(h) { + this._ws.onclose = function(g) { console.log("connection closed"); c.boxcolor = "#000"; }; @@ -10348,8 +10368,8 @@ $jscomp.polyfill("Object.values", function(v) { c.prototype.send = function(c) { this._ws && this._ws.readyState == WebSocket.OPEN && this._ws.send(JSON.stringify({type:1, msg:c})); }; - c.prototype.onAction = function(c, m) { - this._ws && this._ws.readyState == WebSocket.OPEN && this._ws.send({type:1, room:this.properties.room, action:c, data:m}); + c.prototype.onAction = function(c, l) { + this._ws && this._ws.readyState == WebSocket.OPEN && this._ws.send({type:1, room:this.properties.room, action:c, data:l}); }; c.prototype.onGetInputs = function() { return [["in", 0]]; @@ -10357,69 +10377,69 @@ $jscomp.polyfill("Object.values", function(v) { c.prototype.onGetOutputs = function() { return [["out", 0]]; }; - m.registerNodeType("network/websocket", c); - r.title = "SillyClient"; - r.desc = "Connects to SillyServer to broadcast messages"; - r.prototype.onPropertyChanged = function(c, m) { - "room" == c && (this.room_widget.value = m); + l.registerNodeType("network/websocket", c); + t.title = "SillyClient"; + t.desc = "Connects to SillyServer to broadcast messages"; + t.prototype.onPropertyChanged = function(c, l) { + "room" == c && (this.room_widget.value = l); this.connectSocket(); }; - r.prototype.setRoom = function(c) { + t.prototype.setRoom = function(c) { this.properties.room = c; this.room_widget.value = c; this.connectSocket(); }; - r.prototype.onDrawForeground = function() { + t.prototype.onDrawForeground = function() { for (var c = 1; c < this.inputs.length; ++c) { - var m = this.inputs[c]; - m.label = "in_" + c; + var l = this.inputs[c]; + l.label = "in_" + c; } for (c = 1; c < this.outputs.length; ++c) { - m = this.outputs[c], m.label = "out_" + c; + l = this.outputs[c], l.label = "out_" + c; } }; - r.prototype.onExecute = function() { + t.prototype.onExecute = function() { if (this._server && this._server.is_connected) { - for (var c = this.properties.only_send_changes, m = 1; m < this.inputs.length; ++m) { - var l = this.getInputData(m), r = this._last_sent_data[m]; - if (null != l) { + for (var c = this.properties.only_send_changes, l = 1; l < this.inputs.length; ++l) { + var g = this.getInputData(l), t = this._last_sent_data[l]; + if (null != g) { if (c) { - var v = !0; - if (l && l.length && r && r.length == l.length && l.constructor !== String) { - for (var x = 0; x < l.length; ++x) { - if (r[x] != l[x]) { - v = !1; + var w = !0; + if (g && g.length && t && t.length == g.length && g.constructor !== String) { + for (var v = 0; v < g.length; ++v) { + if (t[v] != g[v]) { + w = !1; break; } } } else { - this._last_sent_data[m] != l && (v = !1); + this._last_sent_data[l] != g && (w = !1); } - if (v) { + if (w) { continue; } } - this._server.sendMessage({type:0, channel:m, data:l}); - if (l.length && l.constructor !== String) { - if (this._last_sent_data[m]) { - for (this._last_sent_data[m].length = l.length, x = 0; x < l.length; ++x) { - this._last_sent_data[m][x] = l[x]; + this._server.sendMessage({type:0, channel:l, data:g}); + if (g.length && g.constructor !== String) { + if (this._last_sent_data[l]) { + for (this._last_sent_data[l].length = g.length, v = 0; v < g.length; ++v) { + this._last_sent_data[l][v] = g[v]; } } else { - this._last_sent_data[m] = l.constructor === Array ? l.concat() : new l.constructor(l); + this._last_sent_data[l] = g.constructor === Array ? g.concat() : new g.constructor(g); } } else { - this._last_sent_data[m] = l; + this._last_sent_data[l] = g; } } } - for (m = 1; m < this.outputs.length; ++m) { - this.setOutputData(m, this._last_received_data[m]); + for (l = 1; l < this.outputs.length; ++l) { + this.setOutputData(l, this._last_received_data[l]); } "#AFA" == this.boxcolor && (this.boxcolor = "#6C6"); } }; - r.prototype.connectSocket = function() { + t.prototype.connectSocket = function() { var c = this; if ("undefined" == typeof SillyClient) { this._error || console.error("SillyClient node cannot be used, you must include SillyServer.js"), this._error = !0; @@ -10427,32 +10447,32 @@ $jscomp.polyfill("Object.values", function(v) { if (this._server = new SillyClient, this._server.on_ready = function() { console.log("ready"); c.boxcolor = "#6C6"; - }, this._server.on_message = function(h, l) { - h = null; + }, this._server.on_message = function(p, g) { + p = null; try { - h = JSON.parse(l); - } catch (A) { + p = JSON.parse(g); + } catch (B) { return; } - if (1 == h.type) { - if (h.data.object_class && m[h.data.object_class]) { - l = null; + if (1 == p.type) { + if (p.data.object_class && l[p.data.object_class]) { + g = null; try { - l = new m[h.data.object_class](h.data), c.triggerSlot(0, l); - } catch (A) { + g = new l[p.data.object_class](p.data), c.triggerSlot(0, g); + } catch (B) { return; } } else { - c.triggerSlot(0, h.data); + c.triggerSlot(0, p.data); } } else { - c._last_received_data[h.channel || 0] = h.data; + c._last_received_data[p.channel || 0] = p.data; } c.boxcolor = "#AFA"; - }, this._server.on_error = function(h) { + }, this._server.on_error = function(l) { console.log("couldnt connect to websocket"); c.boxcolor = "#E88"; - }, this._server.on_close = function(h) { + }, this._server.on_close = function(l) { console.log("connection closed"); c.boxcolor = "#000"; }, this.properties.url && this.properties.room) { @@ -10467,18 +10487,18 @@ $jscomp.polyfill("Object.values", function(v) { } } }; - r.prototype.send = function(c) { + t.prototype.send = function(c) { this._server && this._server.is_connected && this._server.sendMessage({type:1, data:c}); }; - r.prototype.onAction = function(c, m) { - this._server && this._server.is_connected && this._server.sendMessage({type:1, action:c, data:m}); + t.prototype.onAction = function(c, l) { + this._server && this._server.is_connected && this._server.sendMessage({type:1, action:c, data:l}); }; - r.prototype.onGetInputs = function() { + t.prototype.onGetInputs = function() { return [["in", 0]]; }; - r.prototype.onGetOutputs = function() { + t.prototype.onGetOutputs = function() { return [["out", 0]]; }; - m.registerNodeType("network/sillyclient", r); + l.registerNodeType("network/sillyclient", t); })(this); diff --git a/src/litegraph.d.ts b/src/litegraph.d.ts index 740d46a3c..b524fd0d2 100644 --- a/src/litegraph.d.ts +++ b/src/litegraph.d.ts @@ -11,6 +11,11 @@ export type widgetTypes = | "text" | "toggle" | "button"; +export type SlotShape = + | typeof LiteGraph.BOX_SHAPE + | typeof LiteGraph.CIRCLE_SHAPE + | typeof LiteGraph.ARROW_SHAPE + | typeof LiteGraph.SQUARE_SHAPE /** https://github.com/jagenjo/litegraph.js/tree/master/guides#node-slots */ export interface INodeSlot { @@ -24,6 +29,7 @@ export interface INodeSlot { | typeof LiteGraph.LEFT; color_on?: string; color_off?: string; + shape?: SlotShape; locked?: boolean; nameLocked?: boolean; } @@ -174,6 +180,7 @@ export const LiteGraph: { CIRCLE_SHAPE: 3; CARD_SHAPE: 4; ARROW_SHAPE: 5; + SQUARE_SHAPE: 6; //enums INPUT: 1; From 802156e5a018d1efdfd58e085fcec16e87365c62 Mon Sep 17 00:00:00 2001 From: ilya Date: Mon, 13 Jul 2020 19:54:24 +0300 Subject: [PATCH 55/63] TypeDef updates and fix of method declaration --- src/litegraph.d.ts | 5 +++++ src/litegraph.js | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/litegraph.d.ts b/src/litegraph.d.ts index b524fd0d2..4f2a875d4 100644 --- a/src/litegraph.d.ts +++ b/src/litegraph.d.ts @@ -16,6 +16,7 @@ export type SlotShape = | typeof LiteGraph.CIRCLE_SHAPE | typeof LiteGraph.ARROW_SHAPE | typeof LiteGraph.SQUARE_SHAPE + | number; // For custom shapes /** https://github.com/jagenjo/litegraph.js/tree/master/guides#node-slots */ export interface INodeSlot { @@ -238,6 +239,8 @@ export const LiteGraph: { registerNodeType(type: string, base: { new (): LGraphNode }): void; /** removes a node type from the system */ unregisterNodeType(type: string): void; + /** Removes all previously registered node's types. */ + clearRegisteredTypes(): void; /** * Create a new node type by passing a function, it wraps it with a proper class and generates inputs according to the parameters of the function. * Useful to wrap simple methods that do not require properties, and that only process some input to generate an output. @@ -1308,6 +1311,8 @@ export declare class LGraphCanvas { drawBackCanvas(): void; /** draws the given node inside the canvas */ drawNode(node: LGraphNode, ctx: CanvasRenderingContext2D): void; + /** draws graphic for node's slot */ + drawSlotGraphic(ctx: CanvasRenderingContext2D, pos: number[], shape: SlotShape, horizontal: boolean): void; /** draws the shape of the given node in the canvas */ drawNodeShape( node: LGraphNode, diff --git a/src/litegraph.js b/src/litegraph.js index b3aed4324..0b5b3792c 100755 --- a/src/litegraph.js +++ b/src/litegraph.js @@ -6978,7 +6978,7 @@ LGraphNode.prototype.executeAction = function(action) var temp_vec2 = new Float32Array(2); - function drawSlotGraphic(ctx, pos, shape, horizontal) { + LGraphCanvas.prototype.drawSlotGraphic = function(ctx, pos, shape, horizontal) { ctx.beginPath(); switch (shape) { @@ -7173,7 +7173,7 @@ LGraphNode.prototype.executeAction = function(action) var shape = slot.shape || (slot.type === LiteGraph.EVENT && LiteGraph.BOX_SHAPE) || (low_quality && LiteGraph.SQUARE_SHAPE) || LiteGraph.CIRCLE_SHAPE; - drawSlotGraphic(ctx, pos, shape, horizontal); + this.drawSlotGraphic(ctx, pos, shape, horizontal); //render name if (render_text) { @@ -7217,7 +7217,7 @@ LGraphNode.prototype.executeAction = function(action) var shape = slot.shape || (slot.type === LiteGraph.EVENT && LiteGraph.BOX_SHAPE) || (low_quality && LiteGraph.SQUARE_SHAPE) || LiteGraph.CIRCLE_SHAPE; - drawSlotGraphic(ctx, pos, shape, horizontal); + this.drawSlotGraphic(ctx, pos, shape, horizontal); if(!low_quality) ctx.stroke(); From 5fdce48481a937ecd2a5d927d2befde8214813b6 Mon Sep 17 00:00:00 2001 From: ilya Date: Mon, 13 Jul 2020 20:10:30 +0300 Subject: [PATCH 56/63] Fixed broken connect method --- src/litegraph.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/litegraph.js b/src/litegraph.js index 0b5b3792c..65117d8f1 100755 --- a/src/litegraph.js +++ b/src/litegraph.js @@ -3603,10 +3603,14 @@ return null; } + var output = this.outputs[slot]; + var input = target_node.inputs[target_slot]; + if (LiteGraph.isValidConnection(output.type, input.type)) { if (target_node.onBeforeConnectInput) { // This way node can choose another slot (if selected is occupied) target_slot = target_node.onBeforeConnectInput(target_slot); + input = target_node.inputs[target_slot]; } //if there is something already plugged there, disconnect @@ -3618,8 +3622,6 @@ //this.setDirtyCanvas(false,true); //this.graph.connectionChange( this ); - var output = this.outputs[slot]; - //allows nodes to block connection if (target_node.onConnectInput) { if ( target_node.onConnectInput(target_slot, output.type, output, this, slot) === false ) { @@ -3627,7 +3629,6 @@ } } - var input = target_node.inputs[target_slot]; var link_info = null; link_info = new LLink( From d0b448996604eee212596f4f9b0afed35ff6cad6 Mon Sep 17 00:00:00 2001 From: ilya Date: Tue, 14 Jul 2020 02:16:22 +0300 Subject: [PATCH 57/63] Create CONTRIBUTING.md --- CONTRIBUTING.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..ef80047fc --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,7 @@ +# Contribution Rules +There are some simple rules that everyone should follow: + +### Do not commit files from bulid folder +> I usually have horrible merge conflicts when I upload the build version that take me too much time to solve, but I want to keep the build version in the repo, so I guess it would be better if only one of us does the built, which would be me. +> https://github.com/jagenjo/litegraph.js/pull/155#issuecomment-656602861 +Those files will be updated by owner. From eb953a0a4e7bfce7819e96ea6421afe8a8032d55 Mon Sep 17 00:00:00 2001 From: Javi Agenjo Date: Wed, 15 Jul 2020 21:06:24 +0200 Subject: [PATCH 58/63] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index d30e078ac..33b36f31a 100755 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ Try it in the [demo site](https://tamats.com/projects/litegraph/demo). - Graphs can be executed in NodeJS - Highly customizable nodes (color, shape, slots vertical or horizontal, widgets, custom rendering) - Easy to integrate in any JS application (one single file, no dependencies) +- Typescript support ## Nodes provided Although it is easy to create new node types, LiteGraph comes with some default nodes that could be useful for many cases: @@ -180,6 +181,7 @@ You can write any feedback to javi.agenjo@gmail.com - InventivetalentDev - NateScarlet - coderofsalvation +- ilyabesk From b6c28399f89c8f92b016f121e5d8bd1ece91f7fc Mon Sep 17 00:00:00 2001 From: tamat Date: Wed, 15 Jul 2020 21:22:32 +0200 Subject: [PATCH 59/63] shader nodes --- build/litegraph.js | 405 ++++++-- css/litegraph-editor.css | 4 +- css/litegraph.css | 9 +- {demo => editor}/demodata/audio.wav | Bin {demo => editor}/demodata/impulse.wav | Bin {demo => editor}/demodata/video.webm | Bin {demo => editor}/examples/audio.json | 0 {demo => editor}/examples/audio_delay.json | 0 {demo => editor}/examples/audio_reverb.json | 0 {demo => editor}/examples/benchmark.json | 0 {demo => editor}/examples/features.json | 0 .../examples/midi_generation.json | 0 {demo => editor}/examples/subgraph.json | 0 {demo => editor}/imgs/grid.png | Bin {demo => editor}/imgs/icon-edit.png | Bin {demo => editor}/imgs/icon-gear.png | Bin {demo => editor}/imgs/icon-load.png | Bin {demo => editor}/imgs/icon-maximize.png | Bin {demo => editor}/imgs/icon-play.png | Bin {demo => editor}/imgs/icon-playstep.png | Bin {demo => editor}/imgs/icon-record.png | Bin {demo => editor}/imgs/icon-save.png | Bin {demo => editor}/imgs/icon-stop.png | Bin {demo => editor}/imgs/load-progress-empty.png | Bin {demo => editor}/imgs/load-progress-full.png | Bin {demo => editor}/imgs/load-progress-grey.png | Bin .../imgs/play-icons-light-alpha.png | Bin {demo => editor}/imgs/play-icons-light.png | Bin {demo => editor}/imgs/play-icons.png | Bin {demo => editor}/index.html | 0 {demo => editor}/js/code.js | 2 - {demo => editor}/js/demos.js | 0 {demo => editor}/js/libs/audiosynth.js | 0 {demo => editor}/js/libs/gl-matrix-min.js | 0 {demo => editor}/js/libs/litegl.js | 0 {demo => editor}/js/libs/midi-parser.js | 0 {demo => editor}/style.css | 0 src/litegraph.js | 325 +++++-- src/nodes/base.js | 37 + src/nodes/gltextures.js | 14 + src/nodes/shaders.js | 911 ++++++++++++++++-- utils/deploy_files.txt | 1 + 42 files changed, 1513 insertions(+), 195 deletions(-) rename {demo => editor}/demodata/audio.wav (100%) rename {demo => editor}/demodata/impulse.wav (100%) rename {demo => editor}/demodata/video.webm (100%) rename {demo => editor}/examples/audio.json (100%) rename {demo => editor}/examples/audio_delay.json (100%) rename {demo => editor}/examples/audio_reverb.json (100%) rename {demo => editor}/examples/benchmark.json (100%) rename {demo => editor}/examples/features.json (100%) rename {demo => editor}/examples/midi_generation.json (100%) rename {demo => editor}/examples/subgraph.json (100%) rename {demo => editor}/imgs/grid.png (100%) rename {demo => editor}/imgs/icon-edit.png (100%) rename {demo => editor}/imgs/icon-gear.png (100%) rename {demo => editor}/imgs/icon-load.png (100%) rename {demo => editor}/imgs/icon-maximize.png (100%) rename {demo => editor}/imgs/icon-play.png (100%) rename {demo => editor}/imgs/icon-playstep.png (100%) rename {demo => editor}/imgs/icon-record.png (100%) rename {demo => editor}/imgs/icon-save.png (100%) rename {demo => editor}/imgs/icon-stop.png (100%) rename {demo => editor}/imgs/load-progress-empty.png (100%) rename {demo => editor}/imgs/load-progress-full.png (100%) rename {demo => editor}/imgs/load-progress-grey.png (100%) rename {demo => editor}/imgs/play-icons-light-alpha.png (100%) rename {demo => editor}/imgs/play-icons-light.png (100%) rename {demo => editor}/imgs/play-icons.png (100%) rename {demo => editor}/index.html (100%) rename {demo => editor}/js/code.js (99%) rename {demo => editor}/js/demos.js (100%) rename {demo => editor}/js/libs/audiosynth.js (100%) rename {demo => editor}/js/libs/gl-matrix-min.js (100%) rename {demo => editor}/js/libs/litegl.js (100%) rename {demo => editor}/js/libs/midi-parser.js (100%) rename {demo => editor}/style.css (100%) diff --git a/build/litegraph.js b/build/litegraph.js index 4c125bf70..30b576438 100644 --- a/build/litegraph.js +++ b/build/litegraph.js @@ -374,7 +374,7 @@ var r = []; for (var i in this.registered_node_types) { var type = this.registered_node_types[i]; - if (filter && type.filter && type.filter != filter) { + if (type.filter != filter) { continue; } @@ -393,6 +393,7 @@ /** * Returns a list with all the node type categories * @method getNodeTypesCategories + * @param {String} filter only nodes with ctor.filter equal can be shown * @return {Array} array with all the names of the categories */ getNodeTypesCategories: function( filter ) { @@ -401,7 +402,7 @@ var type = this.registered_node_types[i]; if ( type.category && !type.skip_list ) { - if(filter && type.filter != filter) + if(type.filter != filter) continue; categories[type.category] = 1; } @@ -619,6 +620,10 @@ /** * LGraph is the class that contain a full graph. We instantiate one and add nodes to it, and then we can run the execution loop. + * supported callbacks: + + onNodeAdded: when a new node is added to the graph + + onNodeRemoved: when a node inside this graph is removed + + onNodeConnectionChange: some connection has changed in the graph (connected or disconnected) * * @class LGraph * @constructor @@ -677,8 +682,8 @@ //nodes this._nodes = []; this._nodes_by_id = {}; - this._nodes_in_order = []; //nodes that are executable sorted in execution order - this._nodes_executable = null; //nodes that contain onExecute + this._nodes_in_order = []; //nodes sorted in execution order + this._nodes_executable = null; //nodes that contain onExecute sorted in execution order //other scene stuff this._groups = []; @@ -692,6 +697,7 @@ //custom data this.config = {}; this.vars = {}; + this.extra = {}; //to store custom data //timing this.globaltime = 0; @@ -729,6 +735,7 @@ } graphcanvas.graph = this; + if (!this.list_of_graphcanvas) { this.list_of_graphcanvas = []; } @@ -1955,9 +1962,13 @@ links: links, groups: groups_info, config: this.config, + extra: this.extra, version: LiteGraph.VERSION }; + if(this.onSerialize) + this.onSerialize(data); + return data; }; @@ -2050,6 +2061,12 @@ } this.updateExecutionOrder(); + + this.extra = data.extra || {}; + + if(this.onConfigure) + this.onConfigure(data); + this._version++; this.setDirtyCanvas(true, true); return error; @@ -2677,6 +2694,23 @@ return null; }; + /** + * Returns the link info in the connection of an input slot + * @method getInputLink + * @param {number} slot + * @return {LLink} object or null + */ + LGraphNode.prototype.getInputLink = function(slot) { + if (!this.inputs) { + return null; + } + if (slot < this.inputs.length) { + var slot_info = this.inputs[slot]; + return this.graph.links[ slot_info.link ]; + } + return null; + }; + /** * returns the node connected in the input slot * @method getInputNode @@ -3137,7 +3171,7 @@ */ LGraphNode.prototype.removeInput = function(slot) { this.disconnectInput(slot); - this.inputs.splice(slot, 1); + var slot_info = this.inputs.splice(slot, 1); for (var i = slot; i < this.inputs.length; ++i) { if (!this.inputs[i]) { continue; @@ -3150,7 +3184,7 @@ } this.setSize( this.computeSize() ); if (this.onInputRemoved) { - this.onInputRemoved(slot); + this.onInputRemoved(slot, slot_info[0] ); } this.setDirtyCanvas(true, true); }; @@ -4041,12 +4075,14 @@ if (!this.console) { this.console = []; } + this.console.push(msg); if (this.console.length > LGraphNode.MAX_CONSOLE) { this.console.shift(); } - this.graph.onNodeTrace(this, msg); + if(this.graph.onNodeTrace) + this.graph.onNodeTrace(this, msg); }; /* Forces to redraw or the main canvas (LGraphNode) or the bg canvas (links) */ @@ -4556,6 +4592,7 @@ LGraphNode.prototype.executeAction = function(action) this.filter = null; //allows to filter to only accept some type of nodes in a graph + this.set_canvas_dirty_on_mouse_event = true; //forces to redraw the canvas if the mouse does anything this.always_render_background = false; this.render_shadows = true; this.render_canvas_border = true; @@ -4570,7 +4607,9 @@ LGraphNode.prototype.executeAction = function(action) this.links_render_mode = LiteGraph.SPLINE_LINK; - this.canvas_mouse = [0, 0]; //mouse in canvas graph coordinates, where 0,0 is the top-left corner of the blue rectangle + this.mouse = [0, 0]; //mouse in canvas coordinates, where 0,0 is the top-left corner of the blue rectangle + this.graph_mouse = [0, 0]; //mouse in graph coordinates, where 0,0 is the top-left corner of the blue rectangle + this.canvas_mouse = this.graph_mouse; //LEGACY: REMOVE THIS, USE GRAPH_MOUSE INSTEAD //to personalize the search box this.onSearchBox = null; @@ -4645,6 +4684,8 @@ LGraphNode.prototype.executeAction = function(action) this.connecting_node = null; this.highlighted_links = {}; + this.dragging_canvas = false; + this.dirty_canvas = true; this.dirty_bgcanvas = true; this.dirty_area = null; @@ -4690,6 +4731,19 @@ LGraphNode.prototype.executeAction = function(action) this.setDirty(true, true); }; + /** + * returns the top level graph (in case there are subgraphs open on the canvas) + * + * @method getTopGraph + * @return {LGraph} graph + */ + LGraphCanvas.prototype.getTopGraph = function() + { + if(this._graph_stack.length) + return this._graph_stack[0]; + return this.graph; + } + /** * opens a graph contained inside a node in the current graph * @@ -5044,8 +5098,19 @@ LGraphNode.prototype.executeAction = function(action) /* LiteGraphCanvas input */ + //used to block future mouse events (because of im gui) + LGraphCanvas.prototype.blockClick = function() + { + this.block_click = true; + this.last_mouseclick = 0; + } + LGraphCanvas.prototype.processMouseDown = function(e) { - if (!this.graph) { + + if( this.set_canvas_dirty_on_mouse_event ) + this.dirty_canvas = true; + + if (!this.graph) { return; } @@ -5079,9 +5144,12 @@ LGraphNode.prototype.executeAction = function(action) var skip_action = false; var now = LiteGraph.getTime(); var is_double_click = now - this.last_mouseclick < 300; + this.mouse[0] = e.localX; + this.mouse[1] = e.localY; + this.graph_mouse[0] = e.canvasX; + this.graph_mouse[1] = e.canvasY; + this.last_click_position = [this.mouse[0],this.mouse[1]]; - this.canvas_mouse[0] = e.canvasX; - this.canvas_mouse[1] = e.canvasY; this.canvas.focus(); LiteGraph.closeAllContextMenus(ref_window); @@ -5253,7 +5321,7 @@ LGraphNode.prototype.executeAction = function(action) var pos = [e.canvasX - node.pos[0], e.canvasY - node.pos[1]]; //widgets - var widget = this.processNodeWidgets( node, this.canvas_mouse, e ); + var widget = this.processNodeWidgets( node, this.graph_mouse, e ); if (widget) { block_drag_node = true; this.node_widget = [node, widget]; @@ -5400,6 +5468,9 @@ LGraphNode.prototype.executeAction = function(action) this.resize(); } + if( this.set_canvas_dirty_on_mouse_event ) + this.dirty_canvas = true; + if (!this.graph) { return; } @@ -5407,30 +5478,42 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.active_canvas = this; this.adjustMouseEvent(e); var mouse = [e.localX, e.localY]; + this.mouse[0] = mouse[0]; + this.mouse[1] = mouse[1]; var delta = [ mouse[0] - this.last_mouse[0], mouse[1] - this.last_mouse[1] ]; this.last_mouse = mouse; - this.canvas_mouse[0] = e.canvasX; - this.canvas_mouse[1] = e.canvasY; + this.graph_mouse[0] = e.canvasX; + this.graph_mouse[1] = e.canvasY; + + if(this.block_click) + { + e.preventDefault(); + return false; + } + e.dragging = this.last_mouse_dragging; if (this.node_widget) { this.processNodeWidgets( this.node_widget[0], - this.canvas_mouse, + this.graph_mouse, e, this.node_widget[1] ); this.dirty_canvas = true; } - if (this.dragging_rectangle) { + if (this.dragging_rectangle) + { this.dragging_rectangle[2] = e.canvasX - this.dragging_rectangle[0]; this.dragging_rectangle[3] = e.canvasY - this.dragging_rectangle[1]; this.dirty_canvas = true; - } else if (this.selected_group && !this.read_only) { + } + else if (this.selected_group && !this.read_only) + { //moving/resizing a group if (this.selected_group_resizing) { this.selected_group.size = [ @@ -5457,18 +5540,11 @@ LGraphNode.prototype.executeAction = function(action) } //get node over - var node = this.graph.getNodeOnPos( - e.canvasX, - e.canvasY, - this.visible_nodes - ); + var node = this.graph.getNodeOnPos(e.canvasX,e.canvasY,this.visible_nodes); //remove mouseover flag for (var i = 0, l = this.graph._nodes.length; i < l; ++i) { - if ( - this.graph._nodes[i].mouseOver && - node != this.graph._nodes[i] - ) { + if (this.graph._nodes[i].mouseOver && node != this.graph._nodes[i] ) { //mouse leave this.graph._nodes[i].mouseOver = false; if (this.node_over && this.node_over.onMouseLeave) { @@ -5499,11 +5575,7 @@ LGraphNode.prototype.executeAction = function(action) //in case the node wants to do something if (node.onMouseMove) { - node.onMouseMove( - e, - [e.canvasX - node.pos[0], e.canvasY - node.pos[1]], - this - ); + node.onMouseMove( e, [e.canvasX - node.pos[0], e.canvasY - node.pos[1]], this ); } //if dragging a link @@ -5515,20 +5587,10 @@ LGraphNode.prototype.executeAction = function(action) //mouse on top of the corner box, don't know what to do } else { //check if I have a slot below de mouse - var slot = this.isOverNodeInput( - node, - e.canvasX, - e.canvasY, - pos - ); + var slot = this.isOverNodeInput( node, e.canvasX, e.canvasY, pos ); if (slot != -1 && node.inputs[slot]) { var slot_type = node.inputs[slot].type; - if ( - LiteGraph.isValidConnection( - this.connecting_output.type, - slot_type - ) - ) { + if ( LiteGraph.isValidConnection( this.connecting_output.type, slot_type ) ) { this._highlight_input = pos; } } else { @@ -5554,7 +5616,7 @@ LGraphNode.prototype.executeAction = function(action) this.canvas.style.cursor = "crosshair"; } } - } else { //outside + } else { //not over a node //search for link connector var over_link = null; @@ -5582,13 +5644,16 @@ LGraphNode.prototype.executeAction = function(action) if (this.canvas) { this.canvas.style.cursor = ""; } - } + } //end + //send event to node if capturing input (used with widgets that allow drag outside of the area of the node) if ( this.node_capturing_input && this.node_capturing_input != node && this.node_capturing_input.onMouseMove ) { this.node_capturing_input.onMouseMove(e,[e.canvasX - this.node_capturing_input.pos[0],e.canvasY - this.node_capturing_input.pos[1]], this); } + //node being dragged if (this.node_dragged && !this.live_mode) { + //console.log("draggin!",this.selected_nodes); for (var i in this.selected_nodes) { var n = this.selected_nodes[i]; n.pos[0] += delta[0] / this.ds.scale; @@ -5622,9 +5687,12 @@ LGraphNode.prototype.executeAction = function(action) * @method processMouseUp **/ LGraphCanvas.prototype.processMouseUp = function(e) { - if (!this.graph) { + + if( this.set_canvas_dirty_on_mouse_event ) + this.dirty_canvas = true; + + if (!this.graph) return; - } var window = this.getCanvasWindow(); var document = window.document; @@ -5639,12 +5707,19 @@ LGraphNode.prototype.executeAction = function(action) var now = LiteGraph.getTime(); e.click_time = now - this.last_mouseclick; this.last_mouse_dragging = false; + this.last_click_position = null; + + if(this.block_click) + { + console.log("foo"); + this.block_click = false; //used to avoid sending twice a click in a immediate button + } if (e.which == 1) { if( this.node_widget ) { - this.processNodeWidgets( this.node_widget[0], this.canvas_mouse, e ); + this.processNodeWidgets( this.node_widget[0], this.graph_mouse, e ); } //left button @@ -6263,10 +6338,8 @@ LGraphNode.prototype.executeAction = function(action) * selects several nodes (or adds them to the current selection) * @method selectNodes **/ - LGraphCanvas.prototype.selectNodes = function( - nodes, - add_to_current_selection - ) { + LGraphCanvas.prototype.selectNodes = function( nodes, add_to_current_selection ) + { if (!add_to_current_selection) { this.deselectAllNodes(); } @@ -6562,7 +6635,7 @@ LGraphNode.prototype.executeAction = function(action) * @method draw **/ LGraphCanvas.prototype.draw = function(force_canvas, force_bgcanvas) { - if (!this.canvas) { + if (!this.canvas || this.canvas.width == 0 || this.canvas.height == 0) { return; } @@ -6643,7 +6716,7 @@ LGraphNode.prototype.executeAction = function(action) if (this.bgcanvas == this.canvas) { this.drawBackCanvas(); } else { - ctx.drawImage(this.bgcanvas, 0, 0); + ctx.drawImage( this.bgcanvas, 0, 0 ); } //rendering @@ -6712,7 +6785,7 @@ LGraphNode.prototype.executeAction = function(action) this.renderLink( ctx, this.connecting_pos, - [this.canvas_mouse[0], this.canvas_mouse[1]], + [this.graph_mouse[0], this.graph_mouse[1]], null, false, null, @@ -6786,6 +6859,12 @@ LGraphNode.prototype.executeAction = function(action) ctx.restore(); } + //draws panel in the corner + if (this._graph_stack && this._graph_stack.length) { + this.drawSubgraphPanel( ctx ); + } + + if (this.onDrawOverlay) { this.onDrawOverlay(ctx); } @@ -6801,13 +6880,151 @@ LGraphNode.prototype.executeAction = function(action) } }; + /** + * draws the panel in the corner that shows subgraph properties + * @method drawSubgraphPanel + **/ + LGraphCanvas.prototype.drawSubgraphPanel = function(ctx) { + var subgraph = this.graph; + var subnode = subgraph._subgraph_node; + if(!subnode) + { + console.warn("subgraph without subnode"); + return; + } + + var num = subnode.inputs ? subnode.inputs.length : 0; + var w = 300; + var h = Math.floor(LiteGraph.NODE_SLOT_HEIGHT * 1.6); + + ctx.fillStyle = "#111"; + ctx.globalAlpha = 0.8; + ctx.beginPath(); + ctx.roundRect(10,10,w, (num + 1) * h + 50,8 ); + ctx.fill(); + ctx.globalAlpha = 1; + + ctx.fillStyle = "#888"; + ctx.font = "14px Arial"; + ctx.textAlign = "left"; + ctx.fillText( "Graph Inputs", 20, 34 ); + var pos = this.mouse; + + if( this.drawButton( w - 20, 20,20,20, "X", "#151515" ) ) + { + this.closeSubgraph(); + return; + } + + var y = 50; + ctx.font = "20px Arial"; + if(subnode.inputs) + for(var i = 0; i < subnode.inputs.length; ++i) + { + var input = subnode.inputs[i]; + if(input.not_subgraph_input) + continue; + + //input button clicked + if( this.drawButton( 20,y+2,w - 20, h - 2 ) ) + { + var type = subnode.constructor.input_node_type || "graph/input"; + var newnode = LiteGraph.createNode( type ); + if(newnode) + { + subgraph.add( newnode ); + this.block_click = false; + this.last_click_position = null; + this.selectNodes([newnode]); + this.node_dragged = newnode; + this.dragging_canvas = false; + newnode.setProperty("name",input.name); + newnode.setProperty("type",input.type); + this.node_dragged.pos[0] = this.graph_mouse[0] - 5; + this.node_dragged.pos[1] = this.graph_mouse[1] - 5; + } + else + console.error("graph input node not found:",type); + } + + ctx.fillStyle = "#9C9"; + ctx.beginPath(); + ctx.arc(w - 16,y + h * 0.5,5,0,2*Math.PI); + ctx.fill(); + + ctx.fillStyle = "#AAA"; + ctx.fillText( input.name, 50, y + h*0.75 ); + var tw = ctx.measureText( input.name ); + ctx.fillStyle = "#777"; + ctx.fillText( input.type, 50 + tw.width + 10, y + h*0.75 ); + + y += h; + } + + //add + button + if( this.drawButton( 20,y+2,w - 20, h - 2, "+", "#151515", "#222" ) ) + { + this.showSubgraphPropertiesDialog( subnode ); + } + } + + //Draws a button into the canvas overlay and computes if it was clicked using the immediate gui paradigm + LGraphCanvas.prototype.drawButton = function( x,y,w,h, text, bgcolor, hovercolor, textcolor ) + { + var ctx = this.ctx; + bgcolor = bgcolor || LiteGraph.NODE_DEFAULT_COLOR; + hovercolor = hovercolor || "#555"; + textcolor = textcolor || LiteGraph.NODE_TEXT_COLOR; + + var pos = this.mouse; + var hover = LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); + pos = this.last_click_position; + var clicked = pos && LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); + + ctx.fillStyle = hover ? hovercolor : bgcolor; + if(clicked) + ctx.fillStyle = "#AAA"; + ctx.beginPath(); + ctx.roundRect(x,y,w,h,4 ); + ctx.fill(); + + if(text != null) + { + if(text.constructor == String) + { + ctx.fillStyle = textcolor; + ctx.textAlign = "center"; + ctx.font = ((h * 0.65)|0) + "px Arial"; + ctx.fillText( text, x + w * 0.5,y + h * 0.75 ); + ctx.textAlign = "left"; + } + } + + var was_clicked = clicked && !this.block_click; + if(clicked) + this.blockClick(); + return was_clicked; + } + + LGraphCanvas.prototype.isAreaClicked = function( x,y,w,h, hold_click ) + { + var pos = this.mouse; + var hover = LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); + pos = this.last_click_position; + var clicked = pos && LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); + var was_clicked = clicked && !this.block_click; + if(clicked && hold_click) + this.blockClick(); + return was_clicked; + } + /** * draws some useful stats in the corner of the canvas * @method renderInfo **/ LGraphCanvas.prototype.renderInfo = function(ctx, x, y) { - x = x || 0; - y = y || 0; + x = x || 10; + y = y || this.canvas.height - 80; ctx.save(); ctx.translate(x, y); @@ -7520,7 +7737,7 @@ LGraphNode.prototype.executeAction = function(action) ctx.shadowColor = "transparent"; if (node.onDrawBackground) { - node.onDrawBackground(ctx, this, this.canvas, this.canvas_mouse ); + node.onDrawBackground(ctx, this, this.canvas, this.graph_mouse ); } //title bg (remember, it is rendered ABOVE the node) @@ -7670,9 +7887,10 @@ LGraphNode.prototype.executeAction = function(action) //subgraph box if (!node.flags.collapsed && node.subgraph && !node.skip_subgraph_button) { - ctx.fillStyle = "#555"; var w = LiteGraph.NODE_TITLE_HEIGHT; var x = node.size[0] - w; + var over = LiteGraph.isInsideRectangle( this.graph_mouse[0] - node.pos[0], this.graph_mouse[1] - node.pos[1], x+2, -w+2, w-4, w-4 ); + ctx.fillStyle = over ? "#888" : "#555"; if( shape == LiteGraph.BOX_SHAPE || low_quality) ctx.fillRect(x+2, -w+2, w-4, w-4); else @@ -8558,6 +8776,7 @@ LGraphNode.prototype.executeAction = function(action) } } else if (delta) { //clicked in arrow, used for combos var index = -1; + this.last_mouseclick = 0; //avoids dobl click event if(values.constructor === Object) index = values_list.indexOf( String( w.value ) ) + delta; else @@ -8836,8 +9055,11 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.onMenuAdd = function(node, options, e, prev_menu, callback) { var canvas = LGraphCanvas.active_canvas; var ref_window = canvas.getCanvasWindow(); + var graph = canvas.graph; + if(!graph) + return; - var values = LiteGraph.getNodeTypesCategories( canvas.filter ); + var values = LiteGraph.getNodeTypesCategories( canvas.filter || graph.filter ); var entries = []; for (var i in values) { if (values[i]) { @@ -8850,7 +9072,7 @@ LGraphNode.prototype.executeAction = function(action) function inner_clicked(v, option, e) { var category = v.value; - var node_types = LiteGraph.getNodeTypesInCategory( category, canvas.filter ); + var node_types = LiteGraph.getNodeTypesInCategory( category, canvas.filter || graph.filter ); var values = []; for (var i in node_types) { if (!node_types[i].skip_list) { @@ -10085,14 +10307,16 @@ LGraphNode.prototype.executeAction = function(action) for(var i = 0; i < node.inputs.length; ++i) { var input = node.inputs[i]; - var html = "NameType"; + if(input.not_subgraph_input) + continue; + var html = " "; var elem = panel.addHTML(html,"subgraph_property"); elem.dataset["name"] = input.name; elem.dataset["slot"] = i; - elem.querySelector(".name").value = input.name; - elem.querySelector(".type").value = input.type; + elem.querySelector(".name").innerText = input.name; + elem.querySelector(".type").innerText = input.type; elem.querySelector("button").addEventListener("click",function(e){ - node.removeInput( this.parentNode.dataset["slot"] ); + node.removeInput( Number( this.parentNode.dataset["slot"] ) ); inner_refresh(); }); } @@ -11583,6 +11807,43 @@ if (typeof exports != "undefined") { } }; + Subgraph.prototype.onDrawBackground = function(ctx, graphcanvas, canvas, pos) + { + if(this.flags.collapsed) + return; + + var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5; + + //button + var over = LiteGraph.isInsideRectangle(pos[0],pos[1],this.pos[0],this.pos[1] + y,this.size[0],LiteGraph.NODE_TITLE_HEIGHT); + ctx.fillStyle = over ? "#555" : "#222"; + ctx.beginPath(); + ctx.roundRect( 0, y, this.size[0]+1, LiteGraph.NODE_TITLE_HEIGHT, 0, 8); + ctx.fill(); + + //button + ctx.textAlign = "center"; + ctx.font = "24px Arial"; + ctx.fillStyle = over ? "#DDD" : "#999"; + ctx.fillText( "+", this.size[0] * 0.5, y + 24 ); + } + + Subgraph.prototype.onMouseDown = function(e, localpos, graphcanvas) + { + var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5; + if(localpos[1] > y) + { + graphcanvas.showSubgraphPropertiesDialog(this); + } + } + + Subgraph.prototype.computeSize = function() + { + var num_inputs = this.inputs ? this.inputs.length : 0; + var num_outputs = this.outputs ? this.outputs.length : 0; + return [ 200, Math.max(num_inputs,num_outputs) * LiteGraph.NODE_SLOT_HEIGHT + LiteGraph.NODE_TITLE_HEIGHT ]; + } + //**** INPUTS *********************************** Subgraph.prototype.onSubgraphTrigger = function(event, param) { var slot = this.findOutputSlot(event); @@ -18469,6 +18730,20 @@ void main() {\n\ LGraphTextureToViewport.desc = "Texture to viewport"; LGraphTextureToViewport._prev_viewport = new Float32Array(4); + + LGraphTextureToViewport.prototype.onDrawBackground = function( ctx ) + { + if ( this.flags.collapsed || this.size[1] <= 40 ) + return; + + var tex = this.getInputData(0); + if (!tex) { + return; + } + + ctx.drawImage( ctx == gl ? tex : gl.canvas, 10,30, this.size[0] -20, this.size[1] -40); + } + LGraphTextureToViewport.prototype.onExecute = function() { var tex = this.getInputData(0); if (!tex) { diff --git a/css/litegraph-editor.css b/css/litegraph-editor.css index d286949b6..4b1239602 100755 --- a/css/litegraph-editor.css +++ b/css/litegraph-editor.css @@ -181,7 +181,7 @@ display: inline-block; width: 90px; height: 15px; - background-image: url("../demo/imgs/load-progress-empty.png"); + background-image: url("../editor/imgs/load-progress-empty.png"); } .litegraph-editor .cpuload .fgload, @@ -190,7 +190,7 @@ width: 4px; height: 15px; max-width: 90px; - background-image: url("../demo/imgs/load-progress-full.png"); + background-image: url("../editor/imgs/load-progress-full.png"); } .litegraph-editor textarea.code, .litegraph-editor div.code { diff --git a/css/litegraph.css b/css/litegraph.css index 6f03567a4..80a203ad4 100755 --- a/css/litegraph.css +++ b/css/litegraph.css @@ -261,12 +261,11 @@ .litegraph .dialog .dialog-content { height: calc(100% - 90px); - width: calc(100% - 10px); + width: 100%; min-height: 100px; - /*background-color: black;*/ - padding: 4px; display: inline-block; color: #AAA; + /*background-color: black;*/ } .litegraph .dialog .dialog-content h3 { @@ -308,7 +307,7 @@ .litegraph .dialog .property { margin-bottom: 2px; - padding: 0; + padding: 4px; } .litegraph .dialog .property_name { @@ -362,7 +361,7 @@ } .litegraph .subgraph_property { - padding-bottom: 4px; + padding: 4px; } .litegraph .subgraph_property:hover { diff --git a/demo/demodata/audio.wav b/editor/demodata/audio.wav similarity index 100% rename from demo/demodata/audio.wav rename to editor/demodata/audio.wav diff --git a/demo/demodata/impulse.wav b/editor/demodata/impulse.wav similarity index 100% rename from demo/demodata/impulse.wav rename to editor/demodata/impulse.wav diff --git a/demo/demodata/video.webm b/editor/demodata/video.webm similarity index 100% rename from demo/demodata/video.webm rename to editor/demodata/video.webm diff --git a/demo/examples/audio.json b/editor/examples/audio.json similarity index 100% rename from demo/examples/audio.json rename to editor/examples/audio.json diff --git a/demo/examples/audio_delay.json b/editor/examples/audio_delay.json similarity index 100% rename from demo/examples/audio_delay.json rename to editor/examples/audio_delay.json diff --git a/demo/examples/audio_reverb.json b/editor/examples/audio_reverb.json similarity index 100% rename from demo/examples/audio_reverb.json rename to editor/examples/audio_reverb.json diff --git a/demo/examples/benchmark.json b/editor/examples/benchmark.json similarity index 100% rename from demo/examples/benchmark.json rename to editor/examples/benchmark.json diff --git a/demo/examples/features.json b/editor/examples/features.json similarity index 100% rename from demo/examples/features.json rename to editor/examples/features.json diff --git a/demo/examples/midi_generation.json b/editor/examples/midi_generation.json similarity index 100% rename from demo/examples/midi_generation.json rename to editor/examples/midi_generation.json diff --git a/demo/examples/subgraph.json b/editor/examples/subgraph.json similarity index 100% rename from demo/examples/subgraph.json rename to editor/examples/subgraph.json diff --git a/demo/imgs/grid.png b/editor/imgs/grid.png similarity index 100% rename from demo/imgs/grid.png rename to editor/imgs/grid.png diff --git a/demo/imgs/icon-edit.png b/editor/imgs/icon-edit.png similarity index 100% rename from demo/imgs/icon-edit.png rename to editor/imgs/icon-edit.png diff --git a/demo/imgs/icon-gear.png b/editor/imgs/icon-gear.png similarity index 100% rename from demo/imgs/icon-gear.png rename to editor/imgs/icon-gear.png diff --git a/demo/imgs/icon-load.png b/editor/imgs/icon-load.png similarity index 100% rename from demo/imgs/icon-load.png rename to editor/imgs/icon-load.png diff --git a/demo/imgs/icon-maximize.png b/editor/imgs/icon-maximize.png similarity index 100% rename from demo/imgs/icon-maximize.png rename to editor/imgs/icon-maximize.png diff --git a/demo/imgs/icon-play.png b/editor/imgs/icon-play.png similarity index 100% rename from demo/imgs/icon-play.png rename to editor/imgs/icon-play.png diff --git a/demo/imgs/icon-playstep.png b/editor/imgs/icon-playstep.png similarity index 100% rename from demo/imgs/icon-playstep.png rename to editor/imgs/icon-playstep.png diff --git a/demo/imgs/icon-record.png b/editor/imgs/icon-record.png similarity index 100% rename from demo/imgs/icon-record.png rename to editor/imgs/icon-record.png diff --git a/demo/imgs/icon-save.png b/editor/imgs/icon-save.png similarity index 100% rename from demo/imgs/icon-save.png rename to editor/imgs/icon-save.png diff --git a/demo/imgs/icon-stop.png b/editor/imgs/icon-stop.png similarity index 100% rename from demo/imgs/icon-stop.png rename to editor/imgs/icon-stop.png diff --git a/demo/imgs/load-progress-empty.png b/editor/imgs/load-progress-empty.png similarity index 100% rename from demo/imgs/load-progress-empty.png rename to editor/imgs/load-progress-empty.png diff --git a/demo/imgs/load-progress-full.png b/editor/imgs/load-progress-full.png similarity index 100% rename from demo/imgs/load-progress-full.png rename to editor/imgs/load-progress-full.png diff --git a/demo/imgs/load-progress-grey.png b/editor/imgs/load-progress-grey.png similarity index 100% rename from demo/imgs/load-progress-grey.png rename to editor/imgs/load-progress-grey.png diff --git a/demo/imgs/play-icons-light-alpha.png b/editor/imgs/play-icons-light-alpha.png similarity index 100% rename from demo/imgs/play-icons-light-alpha.png rename to editor/imgs/play-icons-light-alpha.png diff --git a/demo/imgs/play-icons-light.png b/editor/imgs/play-icons-light.png similarity index 100% rename from demo/imgs/play-icons-light.png rename to editor/imgs/play-icons-light.png diff --git a/demo/imgs/play-icons.png b/editor/imgs/play-icons.png similarity index 100% rename from demo/imgs/play-icons.png rename to editor/imgs/play-icons.png diff --git a/demo/index.html b/editor/index.html similarity index 100% rename from demo/index.html rename to editor/index.html diff --git a/demo/js/code.js b/editor/js/code.js similarity index 99% rename from demo/js/code.js rename to editor/js/code.js index 4f6ecb712..4ff061cca 100644 --- a/demo/js/code.js +++ b/editor/js/code.js @@ -162,7 +162,5 @@ function enableWebGL() gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT ); gl.viewport(0,0,gl.canvas.width, gl.canvas.height ); } - - } } diff --git a/demo/js/demos.js b/editor/js/demos.js similarity index 100% rename from demo/js/demos.js rename to editor/js/demos.js diff --git a/demo/js/libs/audiosynth.js b/editor/js/libs/audiosynth.js similarity index 100% rename from demo/js/libs/audiosynth.js rename to editor/js/libs/audiosynth.js diff --git a/demo/js/libs/gl-matrix-min.js b/editor/js/libs/gl-matrix-min.js similarity index 100% rename from demo/js/libs/gl-matrix-min.js rename to editor/js/libs/gl-matrix-min.js diff --git a/demo/js/libs/litegl.js b/editor/js/libs/litegl.js similarity index 100% rename from demo/js/libs/litegl.js rename to editor/js/libs/litegl.js diff --git a/demo/js/libs/midi-parser.js b/editor/js/libs/midi-parser.js similarity index 100% rename from demo/js/libs/midi-parser.js rename to editor/js/libs/midi-parser.js diff --git a/demo/style.css b/editor/style.css similarity index 100% rename from demo/style.css rename to editor/style.css diff --git a/src/litegraph.js b/src/litegraph.js index 6ca05a999..eaea5ab5f 100755 --- a/src/litegraph.js +++ b/src/litegraph.js @@ -372,7 +372,7 @@ var r = []; for (var i in this.registered_node_types) { var type = this.registered_node_types[i]; - if (filter && type.filter && type.filter != filter) { + if (type.filter != filter) { continue; } @@ -391,6 +391,7 @@ /** * Returns a list with all the node type categories * @method getNodeTypesCategories + * @param {String} filter only nodes with ctor.filter equal can be shown * @return {Array} array with all the names of the categories */ getNodeTypesCategories: function( filter ) { @@ -399,7 +400,7 @@ var type = this.registered_node_types[i]; if ( type.category && !type.skip_list ) { - if(filter && type.filter != filter) + if(type.filter != filter) continue; categories[type.category] = 1; } @@ -617,6 +618,10 @@ /** * LGraph is the class that contain a full graph. We instantiate one and add nodes to it, and then we can run the execution loop. + * supported callbacks: + + onNodeAdded: when a new node is added to the graph + + onNodeRemoved: when a node inside this graph is removed + + onNodeConnectionChange: some connection has changed in the graph (connected or disconnected) * * @class LGraph * @constructor @@ -675,8 +680,8 @@ //nodes this._nodes = []; this._nodes_by_id = {}; - this._nodes_in_order = []; //nodes that are executable sorted in execution order - this._nodes_executable = null; //nodes that contain onExecute + this._nodes_in_order = []; //nodes sorted in execution order + this._nodes_executable = null; //nodes that contain onExecute sorted in execution order //other scene stuff this._groups = []; @@ -690,6 +695,7 @@ //custom data this.config = {}; this.vars = {}; + this.extra = {}; //to store custom data //timing this.globaltime = 0; @@ -727,6 +733,7 @@ } graphcanvas.graph = this; + if (!this.list_of_graphcanvas) { this.list_of_graphcanvas = []; } @@ -1953,9 +1960,13 @@ links: links, groups: groups_info, config: this.config, + extra: this.extra, version: LiteGraph.VERSION }; + if(this.onSerialize) + this.onSerialize(data); + return data; }; @@ -2048,6 +2059,12 @@ } this.updateExecutionOrder(); + + this.extra = data.extra || {}; + + if(this.onConfigure) + this.onConfigure(data); + this._version++; this.setDirtyCanvas(true, true); return error; @@ -4056,12 +4073,14 @@ if (!this.console) { this.console = []; } + this.console.push(msg); if (this.console.length > LGraphNode.MAX_CONSOLE) { this.console.shift(); } - this.graph.onNodeTrace(this, msg); + if(this.graph.onNodeTrace) + this.graph.onNodeTrace(this, msg); }; /* Forces to redraw or the main canvas (LGraphNode) or the bg canvas (links) */ @@ -4571,6 +4590,7 @@ LGraphNode.prototype.executeAction = function(action) this.filter = null; //allows to filter to only accept some type of nodes in a graph + this.set_canvas_dirty_on_mouse_event = true; //forces to redraw the canvas if the mouse does anything this.always_render_background = false; this.render_shadows = true; this.render_canvas_border = true; @@ -4585,7 +4605,9 @@ LGraphNode.prototype.executeAction = function(action) this.links_render_mode = LiteGraph.SPLINE_LINK; - this.canvas_mouse = [0, 0]; //mouse in canvas graph coordinates, where 0,0 is the top-left corner of the blue rectangle + this.mouse = [0, 0]; //mouse in canvas coordinates, where 0,0 is the top-left corner of the blue rectangle + this.graph_mouse = [0, 0]; //mouse in graph coordinates, where 0,0 is the top-left corner of the blue rectangle + this.canvas_mouse = this.graph_mouse; //LEGACY: REMOVE THIS, USE GRAPH_MOUSE INSTEAD //to personalize the search box this.onSearchBox = null; @@ -4660,6 +4682,8 @@ LGraphNode.prototype.executeAction = function(action) this.connecting_node = null; this.highlighted_links = {}; + this.dragging_canvas = false; + this.dirty_canvas = true; this.dirty_bgcanvas = true; this.dirty_area = null; @@ -4705,6 +4729,19 @@ LGraphNode.prototype.executeAction = function(action) this.setDirty(true, true); }; + /** + * returns the top level graph (in case there are subgraphs open on the canvas) + * + * @method getTopGraph + * @return {LGraph} graph + */ + LGraphCanvas.prototype.getTopGraph = function() + { + if(this._graph_stack.length) + return this._graph_stack[0]; + return this.graph; + } + /** * opens a graph contained inside a node in the current graph * @@ -5059,8 +5096,19 @@ LGraphNode.prototype.executeAction = function(action) /* LiteGraphCanvas input */ + //used to block future mouse events (because of im gui) + LGraphCanvas.prototype.blockClick = function() + { + this.block_click = true; + this.last_mouseclick = 0; + } + LGraphCanvas.prototype.processMouseDown = function(e) { - if (!this.graph) { + + if( this.set_canvas_dirty_on_mouse_event ) + this.dirty_canvas = true; + + if (!this.graph) { return; } @@ -5094,9 +5142,12 @@ LGraphNode.prototype.executeAction = function(action) var skip_action = false; var now = LiteGraph.getTime(); var is_double_click = now - this.last_mouseclick < 300; + this.mouse[0] = e.localX; + this.mouse[1] = e.localY; + this.graph_mouse[0] = e.canvasX; + this.graph_mouse[1] = e.canvasY; + this.last_click_position = [this.mouse[0],this.mouse[1]]; - this.canvas_mouse[0] = e.canvasX; - this.canvas_mouse[1] = e.canvasY; this.canvas.focus(); LiteGraph.closeAllContextMenus(ref_window); @@ -5268,7 +5319,7 @@ LGraphNode.prototype.executeAction = function(action) var pos = [e.canvasX - node.pos[0], e.canvasY - node.pos[1]]; //widgets - var widget = this.processNodeWidgets( node, this.canvas_mouse, e ); + var widget = this.processNodeWidgets( node, this.graph_mouse, e ); if (widget) { block_drag_node = true; this.node_widget = [node, widget]; @@ -5415,6 +5466,9 @@ LGraphNode.prototype.executeAction = function(action) this.resize(); } + if( this.set_canvas_dirty_on_mouse_event ) + this.dirty_canvas = true; + if (!this.graph) { return; } @@ -5422,30 +5476,42 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.active_canvas = this; this.adjustMouseEvent(e); var mouse = [e.localX, e.localY]; + this.mouse[0] = mouse[0]; + this.mouse[1] = mouse[1]; var delta = [ mouse[0] - this.last_mouse[0], mouse[1] - this.last_mouse[1] ]; this.last_mouse = mouse; - this.canvas_mouse[0] = e.canvasX; - this.canvas_mouse[1] = e.canvasY; + this.graph_mouse[0] = e.canvasX; + this.graph_mouse[1] = e.canvasY; + + if(this.block_click) + { + e.preventDefault(); + return false; + } + e.dragging = this.last_mouse_dragging; if (this.node_widget) { this.processNodeWidgets( this.node_widget[0], - this.canvas_mouse, + this.graph_mouse, e, this.node_widget[1] ); this.dirty_canvas = true; } - if (this.dragging_rectangle) { + if (this.dragging_rectangle) + { this.dragging_rectangle[2] = e.canvasX - this.dragging_rectangle[0]; this.dragging_rectangle[3] = e.canvasY - this.dragging_rectangle[1]; this.dirty_canvas = true; - } else if (this.selected_group && !this.read_only) { + } + else if (this.selected_group && !this.read_only) + { //moving/resizing a group if (this.selected_group_resizing) { this.selected_group.size = [ @@ -5472,18 +5538,11 @@ LGraphNode.prototype.executeAction = function(action) } //get node over - var node = this.graph.getNodeOnPos( - e.canvasX, - e.canvasY, - this.visible_nodes - ); + var node = this.graph.getNodeOnPos(e.canvasX,e.canvasY,this.visible_nodes); //remove mouseover flag for (var i = 0, l = this.graph._nodes.length; i < l; ++i) { - if ( - this.graph._nodes[i].mouseOver && - node != this.graph._nodes[i] - ) { + if (this.graph._nodes[i].mouseOver && node != this.graph._nodes[i] ) { //mouse leave this.graph._nodes[i].mouseOver = false; if (this.node_over && this.node_over.onMouseLeave) { @@ -5514,11 +5573,7 @@ LGraphNode.prototype.executeAction = function(action) //in case the node wants to do something if (node.onMouseMove) { - node.onMouseMove( - e, - [e.canvasX - node.pos[0], e.canvasY - node.pos[1]], - this - ); + node.onMouseMove( e, [e.canvasX - node.pos[0], e.canvasY - node.pos[1]], this ); } //if dragging a link @@ -5530,20 +5585,10 @@ LGraphNode.prototype.executeAction = function(action) //mouse on top of the corner box, don't know what to do } else { //check if I have a slot below de mouse - var slot = this.isOverNodeInput( - node, - e.canvasX, - e.canvasY, - pos - ); + var slot = this.isOverNodeInput( node, e.canvasX, e.canvasY, pos ); if (slot != -1 && node.inputs[slot]) { var slot_type = node.inputs[slot].type; - if ( - LiteGraph.isValidConnection( - this.connecting_output.type, - slot_type - ) - ) { + if ( LiteGraph.isValidConnection( this.connecting_output.type, slot_type ) ) { this._highlight_input = pos; } } else { @@ -5569,7 +5614,7 @@ LGraphNode.prototype.executeAction = function(action) this.canvas.style.cursor = "crosshair"; } } - } else { //outside + } else { //not over a node //search for link connector var over_link = null; @@ -5597,13 +5642,16 @@ LGraphNode.prototype.executeAction = function(action) if (this.canvas) { this.canvas.style.cursor = ""; } - } + } //end + //send event to node if capturing input (used with widgets that allow drag outside of the area of the node) if ( this.node_capturing_input && this.node_capturing_input != node && this.node_capturing_input.onMouseMove ) { this.node_capturing_input.onMouseMove(e,[e.canvasX - this.node_capturing_input.pos[0],e.canvasY - this.node_capturing_input.pos[1]], this); } + //node being dragged if (this.node_dragged && !this.live_mode) { + //console.log("draggin!",this.selected_nodes); for (var i in this.selected_nodes) { var n = this.selected_nodes[i]; n.pos[0] += delta[0] / this.ds.scale; @@ -5637,9 +5685,12 @@ LGraphNode.prototype.executeAction = function(action) * @method processMouseUp **/ LGraphCanvas.prototype.processMouseUp = function(e) { - if (!this.graph) { + + if( this.set_canvas_dirty_on_mouse_event ) + this.dirty_canvas = true; + + if (!this.graph) return; - } var window = this.getCanvasWindow(); var document = window.document; @@ -5654,12 +5705,19 @@ LGraphNode.prototype.executeAction = function(action) var now = LiteGraph.getTime(); e.click_time = now - this.last_mouseclick; this.last_mouse_dragging = false; + this.last_click_position = null; + + if(this.block_click) + { + console.log("foo"); + this.block_click = false; //used to avoid sending twice a click in a immediate button + } if (e.which == 1) { if( this.node_widget ) { - this.processNodeWidgets( this.node_widget[0], this.canvas_mouse, e ); + this.processNodeWidgets( this.node_widget[0], this.graph_mouse, e ); } //left button @@ -6278,10 +6336,8 @@ LGraphNode.prototype.executeAction = function(action) * selects several nodes (or adds them to the current selection) * @method selectNodes **/ - LGraphCanvas.prototype.selectNodes = function( - nodes, - add_to_current_selection - ) { + LGraphCanvas.prototype.selectNodes = function( nodes, add_to_current_selection ) + { if (!add_to_current_selection) { this.deselectAllNodes(); } @@ -6577,7 +6633,7 @@ LGraphNode.prototype.executeAction = function(action) * @method draw **/ LGraphCanvas.prototype.draw = function(force_canvas, force_bgcanvas) { - if (!this.canvas) { + if (!this.canvas || this.canvas.width == 0 || this.canvas.height == 0) { return; } @@ -6658,7 +6714,7 @@ LGraphNode.prototype.executeAction = function(action) if (this.bgcanvas == this.canvas) { this.drawBackCanvas(); } else { - ctx.drawImage(this.bgcanvas, 0, 0); + ctx.drawImage( this.bgcanvas, 0, 0 ); } //rendering @@ -6727,7 +6783,7 @@ LGraphNode.prototype.executeAction = function(action) this.renderLink( ctx, this.connecting_pos, - [this.canvas_mouse[0], this.canvas_mouse[1]], + [this.graph_mouse[0], this.graph_mouse[1]], null, false, null, @@ -6801,6 +6857,12 @@ LGraphNode.prototype.executeAction = function(action) ctx.restore(); } + //draws panel in the corner + if (this._graph_stack && this._graph_stack.length) { + this.drawSubgraphPanel( ctx ); + } + + if (this.onDrawOverlay) { this.onDrawOverlay(ctx); } @@ -6816,13 +6878,151 @@ LGraphNode.prototype.executeAction = function(action) } }; + /** + * draws the panel in the corner that shows subgraph properties + * @method drawSubgraphPanel + **/ + LGraphCanvas.prototype.drawSubgraphPanel = function(ctx) { + var subgraph = this.graph; + var subnode = subgraph._subgraph_node; + if(!subnode) + { + console.warn("subgraph without subnode"); + return; + } + + var num = subnode.inputs ? subnode.inputs.length : 0; + var w = 300; + var h = Math.floor(LiteGraph.NODE_SLOT_HEIGHT * 1.6); + + ctx.fillStyle = "#111"; + ctx.globalAlpha = 0.8; + ctx.beginPath(); + ctx.roundRect(10,10,w, (num + 1) * h + 50,8 ); + ctx.fill(); + ctx.globalAlpha = 1; + + ctx.fillStyle = "#888"; + ctx.font = "14px Arial"; + ctx.textAlign = "left"; + ctx.fillText( "Graph Inputs", 20, 34 ); + var pos = this.mouse; + + if( this.drawButton( w - 20, 20,20,20, "X", "#151515" ) ) + { + this.closeSubgraph(); + return; + } + + var y = 50; + ctx.font = "20px Arial"; + if(subnode.inputs) + for(var i = 0; i < subnode.inputs.length; ++i) + { + var input = subnode.inputs[i]; + if(input.not_subgraph_input) + continue; + + //input button clicked + if( this.drawButton( 20,y+2,w - 20, h - 2 ) ) + { + var type = subnode.constructor.input_node_type || "graph/input"; + var newnode = LiteGraph.createNode( type ); + if(newnode) + { + subgraph.add( newnode ); + this.block_click = false; + this.last_click_position = null; + this.selectNodes([newnode]); + this.node_dragged = newnode; + this.dragging_canvas = false; + newnode.setProperty("name",input.name); + newnode.setProperty("type",input.type); + this.node_dragged.pos[0] = this.graph_mouse[0] - 5; + this.node_dragged.pos[1] = this.graph_mouse[1] - 5; + } + else + console.error("graph input node not found:",type); + } + + ctx.fillStyle = "#9C9"; + ctx.beginPath(); + ctx.arc(w - 16,y + h * 0.5,5,0,2*Math.PI); + ctx.fill(); + + ctx.fillStyle = "#AAA"; + ctx.fillText( input.name, 50, y + h*0.75 ); + var tw = ctx.measureText( input.name ); + ctx.fillStyle = "#777"; + ctx.fillText( input.type, 50 + tw.width + 10, y + h*0.75 ); + + y += h; + } + + //add + button + if( this.drawButton( 20,y+2,w - 20, h - 2, "+", "#151515", "#222" ) ) + { + this.showSubgraphPropertiesDialog( subnode ); + } + } + + //Draws a button into the canvas overlay and computes if it was clicked using the immediate gui paradigm + LGraphCanvas.prototype.drawButton = function( x,y,w,h, text, bgcolor, hovercolor, textcolor ) + { + var ctx = this.ctx; + bgcolor = bgcolor || LiteGraph.NODE_DEFAULT_COLOR; + hovercolor = hovercolor || "#555"; + textcolor = textcolor || LiteGraph.NODE_TEXT_COLOR; + + var pos = this.mouse; + var hover = LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); + pos = this.last_click_position; + var clicked = pos && LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); + + ctx.fillStyle = hover ? hovercolor : bgcolor; + if(clicked) + ctx.fillStyle = "#AAA"; + ctx.beginPath(); + ctx.roundRect(x,y,w,h,4 ); + ctx.fill(); + + if(text != null) + { + if(text.constructor == String) + { + ctx.fillStyle = textcolor; + ctx.textAlign = "center"; + ctx.font = ((h * 0.65)|0) + "px Arial"; + ctx.fillText( text, x + w * 0.5,y + h * 0.75 ); + ctx.textAlign = "left"; + } + } + + var was_clicked = clicked && !this.block_click; + if(clicked) + this.blockClick(); + return was_clicked; + } + + LGraphCanvas.prototype.isAreaClicked = function( x,y,w,h, hold_click ) + { + var pos = this.mouse; + var hover = LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); + pos = this.last_click_position; + var clicked = pos && LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); + var was_clicked = clicked && !this.block_click; + if(clicked && hold_click) + this.blockClick(); + return was_clicked; + } + /** * draws some useful stats in the corner of the canvas * @method renderInfo **/ LGraphCanvas.prototype.renderInfo = function(ctx, x, y) { - x = x || 0; - y = y || 0; + x = x || 10; + y = y || this.canvas.height - 80; ctx.save(); ctx.translate(x, y); @@ -7535,7 +7735,7 @@ LGraphNode.prototype.executeAction = function(action) ctx.shadowColor = "transparent"; if (node.onDrawBackground) { - node.onDrawBackground(ctx, this, this.canvas, this.canvas_mouse ); + node.onDrawBackground(ctx, this, this.canvas, this.graph_mouse ); } //title bg (remember, it is rendered ABOVE the node) @@ -7685,9 +7885,10 @@ LGraphNode.prototype.executeAction = function(action) //subgraph box if (!node.flags.collapsed && node.subgraph && !node.skip_subgraph_button) { - ctx.fillStyle = "#555"; var w = LiteGraph.NODE_TITLE_HEIGHT; var x = node.size[0] - w; + var over = LiteGraph.isInsideRectangle( this.graph_mouse[0] - node.pos[0], this.graph_mouse[1] - node.pos[1], x+2, -w+2, w-4, w-4 ); + ctx.fillStyle = over ? "#888" : "#555"; if( shape == LiteGraph.BOX_SHAPE || low_quality) ctx.fillRect(x+2, -w+2, w-4, w-4); else @@ -8573,6 +8774,7 @@ LGraphNode.prototype.executeAction = function(action) } } else if (delta) { //clicked in arrow, used for combos var index = -1; + this.last_mouseclick = 0; //avoids dobl click event if(values.constructor === Object) index = values_list.indexOf( String( w.value ) ) + delta; else @@ -8851,8 +9053,11 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.onMenuAdd = function(node, options, e, prev_menu, callback) { var canvas = LGraphCanvas.active_canvas; var ref_window = canvas.getCanvasWindow(); + var graph = canvas.graph; + if(!graph) + return; - var values = LiteGraph.getNodeTypesCategories( canvas.filter ); + var values = LiteGraph.getNodeTypesCategories( canvas.filter || graph.filter ); var entries = []; for (var i in values) { if (values[i]) { @@ -8865,7 +9070,7 @@ LGraphNode.prototype.executeAction = function(action) function inner_clicked(v, option, e) { var category = v.value; - var node_types = LiteGraph.getNodeTypesInCategory( category, canvas.filter ); + var node_types = LiteGraph.getNodeTypesInCategory( category, canvas.filter || graph.filter ); var values = []; for (var i in node_types) { if (!node_types[i].skip_list) { @@ -10100,6 +10305,8 @@ LGraphNode.prototype.executeAction = function(action) for(var i = 0; i < node.inputs.length; ++i) { var input = node.inputs[i]; + if(input.not_subgraph_input) + continue; var html = " "; var elem = panel.addHTML(html,"subgraph_property"); elem.dataset["name"] = input.name; diff --git a/src/nodes/base.js b/src/nodes/base.js index e0f4050cd..9f82b1636 100755 --- a/src/nodes/base.js +++ b/src/nodes/base.js @@ -131,6 +131,43 @@ } }; + Subgraph.prototype.onDrawBackground = function(ctx, graphcanvas, canvas, pos) + { + if(this.flags.collapsed) + return; + + var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5; + + //button + var over = LiteGraph.isInsideRectangle(pos[0],pos[1],this.pos[0],this.pos[1] + y,this.size[0],LiteGraph.NODE_TITLE_HEIGHT); + ctx.fillStyle = over ? "#555" : "#222"; + ctx.beginPath(); + ctx.roundRect( 0, y, this.size[0]+1, LiteGraph.NODE_TITLE_HEIGHT, 0, 8); + ctx.fill(); + + //button + ctx.textAlign = "center"; + ctx.font = "24px Arial"; + ctx.fillStyle = over ? "#DDD" : "#999"; + ctx.fillText( "+", this.size[0] * 0.5, y + 24 ); + } + + Subgraph.prototype.onMouseDown = function(e, localpos, graphcanvas) + { + var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5; + if(localpos[1] > y) + { + graphcanvas.showSubgraphPropertiesDialog(this); + } + } + + Subgraph.prototype.computeSize = function() + { + var num_inputs = this.inputs ? this.inputs.length : 0; + var num_outputs = this.outputs ? this.outputs.length : 0; + return [ 200, Math.max(num_inputs,num_outputs) * LiteGraph.NODE_SLOT_HEIGHT + LiteGraph.NODE_TITLE_HEIGHT ]; + } + //**** INPUTS *********************************** Subgraph.prototype.onSubgraphTrigger = function(event, param) { var slot = this.findOutputSlot(event); diff --git a/src/nodes/gltextures.js b/src/nodes/gltextures.js index 3c24ddf69..0ea30c449 100755 --- a/src/nodes/gltextures.js +++ b/src/nodes/gltextures.js @@ -1229,6 +1229,20 @@ void main() {\n\ LGraphTextureToViewport.desc = "Texture to viewport"; LGraphTextureToViewport._prev_viewport = new Float32Array(4); + + LGraphTextureToViewport.prototype.onDrawBackground = function( ctx ) + { + if ( this.flags.collapsed || this.size[1] <= 40 ) + return; + + var tex = this.getInputData(0); + if (!tex) { + return; + } + + ctx.drawImage( ctx == gl ? tex : gl.canvas, 10,30, this.size[0] -20, this.size[1] -40); + } + LGraphTextureToViewport.prototype.onExecute = function() { var tex = this.getInputData(0); if (!tex) { diff --git a/src/nodes/shaders.js b/src/nodes/shaders.js index f5b22969c..bf03cfdac 100644 --- a/src/nodes/shaders.js +++ b/src/nodes/shaders.js @@ -1,18 +1,112 @@ (function(global) { - var LiteGraph = global.LiteGraph; - var LGraphCanvas = global.LGraphCanvas; if (typeof GL == "undefined") return; + var LiteGraph = global.LiteGraph; + var LGraphCanvas = global.LGraphCanvas; - //common actions to all shader node classes - function setShaderNode( node_ctor ) + var SHADERNODES_COLOR = "#345"; + + var LGShaders = LiteGraph.Shaders = {}; + + var GLSL_types = LGShaders.GLSL_types = ["float","vec2","vec3","vec4","mat3","mat4","sampler2D","samplerCube"]; + var GLSL_types_const = LGShaders.GLSL_types_const = ["float","vec2","vec3","vec4"]; + + var GLSL_functions_desc = { + "radians": "T radians(T degrees)", + "degrees": "T degrees(T radians)", + "sin": "T sin(T angle)", + "cos": "T cos(T angle)", + "tan": "T tan(T angle)", + "asin": "T asin(T x)", + "acos": "T acos(T x)", + "atan": "T atan(T x)", + "atan2": "T atan(T x,T y)", + "pow": "T pow(T x,T y)", + "exp": "T exp(T x)", + "log": "T log(T x)", + "exp2": "T exp2(T x)", + "log2": "T log2(T x)", + "sqrt": "T sqrt(T x)", + "inversesqrt": "T inversesqrt(T x)", + "abs": "T abs(T x)", + "sign": "T sign(T x)", + "floor": "T floor(T x)", + "ceil": "T ceil(T x)", + "fract": "T fract(T x)", + "mod": "T mod(T x,T y)", //"T mod(T x,float y)" + "min": "T min(T x,T y)", + "max": "T max(T x,T y)", + "clamp": "T clamp(T x,T minVal,T maxVal)", + "mix": "T mix(T x,T y,T a)", //"T mix(T x,T y,float a)" + "step": "T step(T edge, T x)", //"T step(float edge, T x)" + "smoothstep": "T smoothstep(T edge, T x)", //"T smoothstep(float edge, T x)" + "length":"float length(T x)", + "distance":"float distance(T p0, T p1)", + "normalize":"T normalize(T x)", + "dot": "float dot(T x,T y)", + "cross": "vec3 cross(vec3 x,vec3 y)" + }; + + //parse them + var GLSL_functions = {}; + var GLSL_functions_name = []; + parseGLSLDescriptions(); + + function parseGLSLDescriptions() { - node_ctor.color = "#345"; + GLSL_functions_name.length = 0; + + for(var i in GLSL_functions_desc) + { + var op = GLSL_functions_desc[i]; + var index = op.indexOf(" "); + var return_type = op.substr(0,index); + var index2 = op.indexOf("(",index); + var func_name = op.substr(index,index2-index).trim(); + var params = op.substr(index2 + 1, op.length - index2 - 2).split(","); + for(var j in params) + { + var p = params[j].split(" "); + params[j] = { type: p[0], name: p[1] }; + } + GLSL_functions[i] = { return_type: return_type, func: func_name, params: params }; + GLSL_functions_name.push( func_name ); + //console.log( GLSL_functions[i] ); + } } - function getShaderInputLinkName( node, slot ) + //common actions to all shader node classes + function registerShaderNode( type, node_ctor ) + { + //static attributes + node_ctor.color = SHADERNODES_COLOR; + node_ctor.filter = "shader"; + + //common methods + node_ctor.prototype.clearDestination = function(){ this.shader_destination = {}; } + node_ctor.prototype.propagateDestination = function propagateDestination( dest_name ) + { + this.shader_destination[ dest_name ] = true; + if(this.inputs) + for(var i = 0; i < this.inputs.length; ++i) + { + var origin_node = this.getInputNode(i); + if(origin_node) + origin_node.propagateDestination( dest_name ); + } + } + + LiteGraph.registerNodeType( type, node_ctor ); + } + + function getShaderNodeVarName( node, name ) + { + return "VAR_" + (name || "TEMP") + "_" + node.id; + } + + function getInputLinkID( node, slot ) { if(!node.inputs) return null; @@ -28,12 +122,60 @@ return "link_" + origin_node.id + "_" + link.origin_slot; } - function getShaderOutputLinkName( node, slot ) + function getOutputLinkID( node, slot ) { + if (!node.isOutputConnected(0)) + return null; return "link_" + node.id + "_" + slot; } - //used to host a shader body ******************* + LGShaders.registerShaderNode = registerShaderNode; + LGShaders.getInputLinkID = getInputLinkID; + LGShaders.getOutputLinkID = getOutputLinkID; + LGShaders.getShaderNodeVarName = getShaderNodeVarName; + LGShaders.parseGLSLDescriptions = parseGLSLDescriptions; + + var valueToGLSL = LiteGraph.valueToGLSL = function valueToGLSL( v, type ) + { + var n = 5; //num decimals + if(!type) + { + if(v.constructor === Number) + type = "float"; + else if(v.length) + { + switch(v.length) + { + case 2: type = "vec2"; break; + case 3: type = "vec3"; break; + case 4: type = "vec4"; break; + case 9: type = "mat3"; break; + case 16: type = "mat4"; break; + default: + throw("unknown type for glsl value size"); + } + } + else + throw("unknown type for glsl value: " + v.constructor); + } + switch(type) + { + case 'float': return v.toFixed(n); break; + case 'vec2': return "vec2(" + v[0].toFixed(n) + "," + v[1].toFixed(n) + ")"; break; + case 'color3': + case 'vec3': return "vec3(" + v[0].toFixed(n) + "," + v[1].toFixed(n) + "," + v[2].toFixed(n) + ")"; break; + case 'color4': + case 'vec4': return "vec4(" + v[0].toFixed(n) + "," + v[1].toFixed(n) + "," + v[2].toFixed(n) + "," + v[3].toFixed(n) + ")"; break; + case 'mat3': return "mat3(1.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,1.0)"; break; //not fully supported yet + case 'mat4': return "mat4(1.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,1.0)"; break;//not fully supported yet + default: + throw("unknown glsl type in valueToGLSL:", type); + } + + return ""; + } + + //used to host a shader body ************************************** function LGShaderContext() { this.vs_template = ""; @@ -42,8 +184,6 @@ this._codeparts = {}; } - LGShaderContext.valid_types = ["float","vec2","vec3","vec4","sampler2D","mat3","mat4","int","boolean"]; - LGShaderContext.prototype.clear = function() { this._uniforms = {}; @@ -55,16 +195,20 @@ this._uniforms[ name ] = type; } - LGShaderContext.prototype.addCode = function( hook, code ) + LGShaderContext.prototype.addCode = function( hook, code, destinations ) { - if(!this._codeparts[ hook ]) - this._codeparts[ hook ] = code + "\n"; - else - this._codeparts[ hook ] += code + "\n"; + destinations = destinations || {"":""}; + for(var i in destinations) + { + var h = i ? i + "_" + hook : hook; + if(!this._codeparts[ h ]) + this._codeparts[ h ] = code + "\n"; + else + this._codeparts[ h ] += code + "\n"; + } } - //generates the shader code from the template and the - LGShaderContext.prototype.computeShader = function( shader ) + LGShaderContext.prototype.computeShaderCode = function() { var uniforms = ""; for(var i in this._uniforms) @@ -75,23 +219,77 @@ var vs_code = GL.Shader.replaceCodeUsingContext( this.vs_template, parts ); var fs_code = GL.Shader.replaceCodeUsingContext( this.fs_template, parts ); + return { + vs_code: vs_code, + fs_code: fs_code + }; + } + + //generates the shader code from the template and the + LGShaderContext.prototype.computeShader = function( shader ) + { + var finalcode = this.computeShaderCode(); + console.log( finalcode.vs_code, finalcode.fs_code ); try { if(shader) - shader.updateShader( vs_code, fs_code ); + shader.updateShader( finalcode.vs_code, finalcode.fs_code ); else - shader = new GL.Shader( vs_code, fs_code ); + shader = new GL.Shader( finalcode.vs_code, finalcode.fs_code ); + this._shader_error = false; return shader; } catch (err) { + if(!this._shader_error) + { + console.error(err); + if(err.indexOf("Fragment shader") != -1) + console.log( finalcode.fs_code ); + else + console.log( finalcode.vs_code ); + } + this._shader_error = true; return null; } return null;//never here } + //represents a fragment of code exported by a node + /* + function LGShaderCodeBlock( node, code, uniforms ) + { + this.node = node || null; + this.uniforms = uniforms || null; + this.parts = {}; + if(code) + { + if(code.constructor === String) + this.parts.code = code; + else + this.parts = code; + } + } + + LGShaderCodeBlock.prototype.addUniform = function( name, type ) + { + if(!this.uniforms) + this.uniforms = {}; + this.uniforms[ name ] = type; + } + + LGShaderCodeBlock.prototype.addCode = function( hook, code ) + { + if(!this.parts[ hook ]) + this.parts[ hook ] = code + "\n"; + else + this.parts[ hook ] += code + "\n"; + } + */ + + // LGraphShaderGraph ***************************** // applies a shader graph to texture, it can be uses as an example @@ -102,9 +300,10 @@ this.subgraph = new LiteGraph.LGraph(); this.subgraph._subgraph_node = this; this.subgraph._is_subgraph = true; + this.subgraph.filter = "shader"; - var subnode = LiteGraph.createNode("shader/fragcolor"); - this.subgraph.pos = [300,100]; + var subnode = LiteGraph.createNode("output/fragcolor"); + subnode.pos = [300,100]; this.subgraph.add( subnode ); this.size = [180,60]; @@ -118,16 +317,17 @@ } LGraphShaderGraph.template = "\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - {{varying}}\n\ - {{uniforms}}\n\ - void main() {\n\ - vec2 uv = v_coord;\n\ - vec4 color = vec4(0.0);\n\ - {{fs_code}}\n\ - gl_FragColor = color;\n\ - }\n\ +precision highp float;\n\ +varying vec2 v_coord;\n\ +{{varying}}\n\ +{{uniforms}}\n\ +{{fs_functions}}\n\ +void main() {\n\n\ +vec2 uv = v_coord;\n\ +vec4 color = vec4(0.0);\n\ +{{fs_code}}\n\ +gl_FragColor = color;\n\ +}\n\ "; LGraphShaderGraph.widgets_info = { @@ -136,6 +336,8 @@ LGraphShaderGraph.title = "ShaderGraph"; LGraphShaderGraph.desc = "Builds a shader using a graph"; + LGraphShaderGraph.input_node_type = "input/uniform"; + LGraphShaderGraph.title_color = SHADERNODES_COLOR; LGraphShaderGraph.prototype.onSerialize = function(o) { @@ -207,17 +409,19 @@ this.setOutputData(0, texture ); }; + //add input node inside subgraph LGraphShaderGraph.prototype.onInputAdded = function( slot_info ) { - var subnode = LiteGraph.createNode("shader/uniform"); - subnode.properties.name = slot_info.name; - subnode.properties.type = slot_info.type; + var subnode = LiteGraph.createNode("input/uniform"); + subnode.setProperty("name",slot_info.name); + subnode.setProperty("type",slot_info.type); this.subgraph.add( subnode ); } + //remove all LGraphShaderGraph.prototype.onInputRemoved = function( slot, slot_info ) { - var nodes = this.subgraph.findNodesByType("shader/uniform"); + var nodes = this.subgraph.findNodesByType("input/uniform"); for(var i = 0; i < nodes.length; ++i) { var node = nodes[i]; @@ -242,8 +446,25 @@ //prepare context this._context.clear(); + //grab output nodes + var vertexout = this.subgraph.findNodesByType("output/vertex"); + vertexout = vertexout && vertexout.length ? vertexout[0] : null; + var fragmentout = this.subgraph.findNodesByType("output/fragcolor"); + fragmentout = fragmentout && fragmentout.length ? fragmentout[0] : null; + + if(!fragmentout) //?? + return null; + + this.subgraph.sendEventToAllNodes( "clearDestination" ); + + //propagate back destinations + if(vertexout) + vertexout.propagateDestination("vs"); + if(fragmentout) + fragmentout.propagateDestination("fs"); + //gets code from graph - this.subgraph.sendEventToAllNodes("onGetCode", this._context); + this.subgraph.sendEventToAllNodes("onGetCode", this._context ); //compile shader var shader = this._context.computeShader(); @@ -265,6 +486,13 @@ if(this.flags.collapsed) return; + //allows to preview the node if the canvas is a webgl canvas + var tex = this.getOutputData(0); + var inputs_y = this.inputs ? this.inputs.length * LiteGraph.NODE_SLOT_HEIGHT : 0; + if (tex && ctx == tex.gl && this.size[1] > inputs_y + LiteGraph.NODE_TITLE_HEIGHT ) { + ctx.drawImage( tex, 10,y, this.size[0] - 20, this.size[1] - inputs_y - LiteGraph.NODE_TITLE_HEIGHT ); + } + var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5; //button @@ -290,6 +518,17 @@ } } + LGraphShaderGraph.prototype.getExtraMenuOptions = function(graphcanvas) + { + var that = this; + var options = [{ content: "Print Code", callback: function(){ + var code = that._context.computeShaderCode(); + console.log( code.vs_code, code.fs_code ); + }}]; + + return options; + } + LiteGraph.registerNodeType( "texture/shaderGraph", LGraphShaderGraph ); //Shader Nodes *************************** @@ -305,7 +544,14 @@ LGraphShaderUniform.prototype.getTitle = function() { - return "uniform " + this.properties.name; + if( this.properties.name && this.flags.collapsed) + return this.properties.type + " " + this.properties.name; + return "Uniform"; + } + + LGraphShaderUniform.prototype.onPropertyChanged = function(name,value) + { + this.outputs[0].name = this.properties.type + " " + this.properties.name; } LGraphShaderUniform.prototype.onGetCode = function( context ) @@ -317,9 +563,11 @@ type = "float"; else if(type == "texture") type = "sampler2D"; - if ( LGShaderContext.valid_types.indexOf(type) == -1 ) + if ( LGShaders.GLSL_types.indexOf(type) == -1 ) return; + context.addUniform( "u_" + this.properties.name, type ); + this.setOutputData( 0, type ); } LGraphShaderUniform.prototype.getOutputVarName = function(slot) @@ -327,10 +575,7 @@ return "u_" + this.properties.name; } - setShaderNode( LGraphShaderUniform ); - - LiteGraph.registerNodeType( "shader/uniform", LGraphShaderUniform ); - + registerShaderNode( "input/uniform", LGraphShaderUniform ); function LGraphShaderAttribute() { @@ -349,12 +594,17 @@ LGraphShaderAttribute.prototype.onGetCode = function( context ) { var type = this.properties.type; - if( !type || LGShaderContext.valid_types.indexOf(type) == -1 ) + if( !type || LGShaders.GLSL_types.indexOf(type) == -1 ) return; if(type == "number") type = "float"; if( this.properties.name != "coord") - context.addCode( "varying", " varying " + type +" v_" + this.properties.name ); + { + context.addCode( "varying", " varying " + type +" v_" + this.properties.name + ";" ); + //if( !context.varyings[ this.properties.name ] ) + //context.addCode( "vs_code", "v_" + this.properties.name + " = " + input_name + ";" ); + } + this.setOutputData( 0, type ); } LGraphShaderAttribute.prototype.getOutputVarName = function(slot) @@ -362,15 +612,13 @@ return "v_" + this.properties.name; } - setShaderNode( LGraphShaderAttribute ); - - LiteGraph.registerNodeType( "shader/attribute", LGraphShaderAttribute ); - + registerShaderNode( "input/attribute", LGraphShaderAttribute ); function LGraphShaderSampler2D() { this.addInput("tex", "sampler2D"); this.addInput("uv", "vec2"); this.addOutput("rgba", "vec4"); + this.addOutput("rgb", "vec3"); } LGraphShaderSampler2D.title = "Sampler2D"; @@ -378,22 +626,301 @@ LGraphShaderSampler2D.prototype.onGetCode = function( context ) { - var texname = getShaderInputLinkName( this, 0 ); - var code; + var texname = getInputLinkID( this, 0 ); + var varname = getShaderNodeVarName(this); + var code = "vec4 " + varname + " = vec4(0.0);\n"; if(texname) { - var uvname = getShaderInputLinkName( this, 1 ) || "v_coord"; - code = "vec4 " + getShaderOutputLinkName( this, 0 ) + " = texture2D("+texname+","+uvname+");"; + var uvname = getInputLinkID( this, 1 ) || "v_coord"; + code += varname + " = texture2D("+texname+","+uvname+");\n"; } - else - code = "vec4 " + getShaderOutputLinkName( this, 0 ) + " = vec4(0.0);"; - context.addCode( "fs_code", code ); + + var link0 = getOutputLinkID( this, 0 ); + if(link0) + code += "vec4 " + getOutputLinkID( this, 0 ) + " = "+varname+";\n"; + + var link1 = getOutputLinkID( this, 1 ); + if(link1) + code += "vec3 " + getOutputLinkID( this, 1 ) + " = "+varname+".xyz;\n"; + + context.addCode( "code", code, this.shader_destination ); + this.setOutputData( 0, "vec4" ); + this.setOutputData( 1, "vec3" ); } - setShaderNode( LGraphShaderSampler2D ); + registerShaderNode( "texture/sampler2D", LGraphShaderSampler2D ); - LiteGraph.registerNodeType( "shader/sampler2D", LGraphShaderSampler2D ); + //********************************* + function LGraphShaderConstant() + { + this.addOutput("","float"); + + this.properties = { + type: "float", + value: 0 + }; + + this.addWidget("combo","type","float",null, { values: GLSL_types_const, property: "type" } ); + this.updateWidgets(); + } + + LGraphShaderConstant.title = "const"; + + LGraphShaderConstant.prototype.getTitle = function() + { + if(this.flags.collapsed) + return valueToGLSL( this.properties.value, this.properties.type ); + return "Const"; + } + + LGraphShaderConstant.prototype.onPropertyChanged = function(name,value) + { + var that = this; + if(name == "type") + { + if(this.outputs[0].type != value) + { + this.disconnectOutput(0); + this.outputs[0].type = value; + this.widgets.length = 1; //remove extra widgets + this.updateWidgets(); + } + } + } + + LGraphShaderConstant.prototype.updateWidgets = function( old_value ) + { + var that = this; + var old_value = this.properties.value; + var options = { step: 0.01 }; + switch(this.properties.type) + { + case 'float': + this.properties.value = 0; + this.addWidget("number","v",0,{ step:0.01, property: "value" }); + break; + case 'vec2': + this.properties.value = old_value && old_value.length == 2 ? [old_value[0],old_value[1]] : [0,0,0]; + this.addWidget("number","x",0,options, function(v){ that.properties.value[0] = v; }); + this.addWidget("number","y",0,options, function(v){ that.properties.value[1] = v; }); + break; + case 'vec3': + this.properties.value = old_value && old_value.length == 3 ? [old_value[0],old_value[1],old_value[2]] : [0,0,0]; + this.addWidget("number","x",0,options, function(v){ that.properties.value[0] = v; }); + this.addWidget("number","y",0,options, function(v){ that.properties.value[1] = v; }); + this.addWidget("number","z",0,options, function(v){ that.properties.value[2] = v; }); + break; + case 'vec4': + this.properties.value = old_value && old_value.length == 4 ? [old_value[0],old_value[1],old_value[2],old_value[3]] : [0,0,0,0]; + this.addWidget("number","x",0,options, function(v){ that.properties.value[0] = v; }); + this.addWidget("number","y",0,options, function(v){ that.properties.value[1] = v; }); + this.addWidget("number","z",0,options, function(v){ that.properties.value[2] = v; }); + this.addWidget("number","w",0,options, function(v){ that.properties.value[3] = v; }); + break; + default: + console.error("unknown type for constant"); + } + } + + LGraphShaderConstant.prototype.onGetCode = function( context ) + { + var value = valueToGLSL( this.properties.value, this.properties.type ); + var link_name = getOutputLinkID(this,0); + if(!link_name) //not connected + return; + + var code = " " + this.properties.type + " " + link_name + " = " + value + ";"; + context.addCode( "code", code, this.shader_destination ); + + this.setOutputData( 0, this.properties.type ); + } + + registerShaderNode( "const/const", LGraphShaderConstant ); + + function LGraphShaderVec2() + { + this.addInput("xy","vec2"); + this.addInput("x","float"); + this.addInput("y","float"); + this.addOutput("xy","vec2"); + this.addOutput("x","float"); + this.addOutput("y","float"); + + this.properties = { x: 0, y: 0 }; + } + + LGraphShaderVec2.title = "vec2"; + LGraphShaderVec2.varmodes = ["xy","x","y"]; + + LGraphShaderVec2.prototype.onPropertyChanged = function() + { + this.graph._version++; + } + + LGraphShaderVec2.prototype.onGetCode = function( context ) + { + var props = this.properties; + + var varname = getShaderNodeVarName(this); + var code = " vec2 " + varname + " = " + valueToGLSL([props.x,props.y]) + ";\n"; + + for(var i = 0;i < LGraphShaderVec2.varmodes.length; ++i) + { + var varmode = LGraphShaderVec2.varmodes[i]; + var inlink = getInputLinkID(this,i); + if(!inlink) + continue; + code += " " + varname + "."+varmode+" = " + inlink + ";\n"; + } + + for(var i = 0;i < LGraphShaderVec2.varmodes.length; ++i) + { + var varmode = LGraphShaderVec2.varmodes[i]; + var outlink = getOutputLinkID(this,i); + if(!outlink) + continue; + var type = GLSL_types_const[varmode.length - 1]; + code += " "+type+" " + outlink + " = " + varname + "." + varmode + ";\n"; + this.setOutputData( i, type ); + } + + context.addCode( "code", code, this.shader_destination ); + } + + registerShaderNode( "const/vec2", LGraphShaderVec2 ); + + function LGraphShaderVec3() + { + this.addInput("xyz","vec3"); + this.addInput("x","float"); + this.addInput("y","float"); + this.addInput("z","float"); + this.addInput("xy","vec2"); + this.addInput("xz","vec2"); + this.addInput("yz","vec2"); + this.addOutput("xyz","vec3"); + this.addOutput("x","float"); + this.addOutput("y","float"); + this.addOutput("z","float"); + this.addOutput("xy","vec2"); + this.addOutput("xz","vec2"); + this.addOutput("yz","vec2"); + + this.properties = { x:0, y: 0, z: 0 }; + } + + LGraphShaderVec3.title = "vec3"; + LGraphShaderVec3.varmodes = ["xyz","x","y","z","xy","xz","yz"]; + + LGraphShaderVec3.prototype.onPropertyChanged = function() + { + this.graph._version++; + } + + + LGraphShaderVec3.prototype.onPropertyChanged = function() + { + this.graph._version++; + } + + LGraphShaderVec3.prototype.onGetCode = function( context ) + { + var props = this.properties; + + var varname = getShaderNodeVarName(this); + var code = "vec3 " + varname + " = " + valueToGLSL([props.x,props.y,props.z]) + ";\n"; + + for(var i = 0;i < LGraphShaderVec3.varmodes.length; ++i) + { + var varmode = LGraphShaderVec3.varmodes[i]; + var inlink = getInputLinkID(this,i); + if(!inlink) + continue; + code += " " + varname + "."+varmode+" = " + inlink + ";\n"; + } + + for(var i = 0; i < LGraphShaderVec3.varmodes.length; ++i) + { + var varmode = LGraphShaderVec3.varmodes[i]; + var outlink = getOutputLinkID(this,i); + if(!outlink) + continue; + var type = GLSL_types_const[varmode.length - 1]; + code += " "+type+" " + outlink + " = " + varname + "." + varmode + ";\n"; + this.setOutputData( i, type ); + } + + context.addCode( "code", code, this.shader_destination ); + } + + registerShaderNode( "const/vec3", LGraphShaderVec3 ); + + + function LGraphShaderVec4() + { + this.addInput("xyzw","vec4"); + this.addInput("xyz","vec3"); + this.addInput("x","float"); + this.addInput("y","float"); + this.addInput("z","float"); + this.addInput("w","float"); + this.addInput("xy","vec2"); + this.addInput("yz","vec2"); + this.addInput("zw","vec2"); + this.addOutput("xyzw","vec4"); + this.addOutput("xyz","vec3"); + this.addOutput("x","float"); + this.addOutput("y","float"); + this.addOutput("z","float"); + this.addOutput("xy","vec2"); + this.addOutput("yz","vec2"); + this.addOutput("zw","vec2"); + + this.properties = { x:0, y: 0, z: 0, w: 0 }; + } + + LGraphShaderVec4.title = "vec4"; + LGraphShaderVec4.varmodes = ["xyzw","xyz","x","y","z","w","xy","yz","zw"]; + + LGraphShaderVec4.prototype.onPropertyChanged = function() + { + this.graph._version++; + } + + LGraphShaderVec4.prototype.onGetCode = function( context ) + { + var props = this.properties; + + var varname = getShaderNodeVarName(this); + var code = "vec4 " + varname + " = " + valueToGLSL([props.x,props.y,props.z,props.w]) + ";\n"; + + for(var i = 0;i < LGraphShaderVec4.varmodes.length; ++i) + { + var varmode = LGraphShaderVec4.varmodes[i]; + var inlink = getInputLinkID(this,i); + if(!inlink) + continue; + code += " " + varname + "."+varmode+" = " + inlink + ";\n"; + } + + for(var i = 0;i < LGraphShaderVec4.varmodes.length; ++i) + { + var varmode = LGraphShaderVec4.varmodes[i]; + var outlink = getOutputLinkID(this,i); + if(!outlink) + continue; + var type = GLSL_types_const[varmode.length - 1]; + code += " "+type+" " + outlink + " = " + varname + "." + varmode + ";\n"; + this.setOutputData( i, type ); + } + + context.addCode( "code", code, this.shader_destination ); + + } + + registerShaderNode( "const/vec4", LGraphShaderVec4 ); + //********************************* function LGraphShaderFragColor() { @@ -406,7 +933,7 @@ LGraphShaderFragColor.prototype.onGetCode = function( context ) { - var link_name = getShaderInputLinkName( this, 0 ); + var link_name = getInputLinkID( this, 0 ); if(!link_name) return; @@ -419,11 +946,271 @@ else if(type == "vec3") code = "vec4(" + code + ",1.0);"; - context.addCode("fs_code", "color = " + code + ";\n"); + context.addCode("fs_code", "color = " + code + ";"); } - setShaderNode( LGraphShaderFragColor ); + registerShaderNode( "output/fragcolor", LGraphShaderFragColor ); - LiteGraph.registerNodeType( "shader/fragcolor", LGraphShaderFragColor ); -})(this); \ No newline at end of file + + // ************************************************* + + function LGraphShaderMath() + { + this.addInput("A","float,vec2,vec3,vec4"); + this.addInput("B","float,vec2,vec3,vec4"); + this.addOutput("out",""); + this.properties = { + func: "floor" + }; + this._current = "floor"; + this.addWidget("combo","func",this.properties.func,{ property: "func", values: GLSL_functions_name }); + } + + LGraphShaderMath.title = "Math"; + + LGraphShaderMath.prototype.onPropertyChanged = function(name,value) + { + this.graph._version++; + + if(name == "func") + { + var func_desc = GLSL_functions[ value ]; + if(!func_desc) + return; + + //remove extra inputs + for(var i = func_desc.params.length; i < this.inputs.length; ++i) + this.removeInput(i); + + //add and update inputs + for(var i = 0; i < func_desc.params.length; ++i) + if( this.inputs[i] ) + this.inputs[i].name = func_desc.params[i].name; + else + this.addInput( func_desc.params[i].name, "float,vec2,vec3,vec4" ); + } + } + + LGraphShaderMath.prototype.getTitle = function() + { + if(this.flags.collapsed) + return this.properties.func; + else + return "Func"; + } + + LGraphShaderMath.prototype.onGetCode = function( context ) + { + if(!this.isOutputConnected(0)) + return; + + var inlinks = []; + for(var i = 0; i < 3; ++i) + inlinks.push( { name: getInputLinkID(this,i), type: this.getInputData(i) || "float" } ); + + var outlink = getOutputLinkID(this,0); + if(!outlink) //not connected + return; + + var func_desc = GLSL_functions[ this.properties.func ]; + if(!func_desc) + return; + + //func_desc + var base_type = inlinks[0].type; + var return_type = func_desc.return_type; + if( return_type == "T" ) + return_type = base_type; + + var params = []; + for(var i = 0; i < func_desc.params.length; ++i) + { + var p = func_desc.params[i]; + //if( p.type == "T" && inlinks[i].type != base_type ) + params.push( inlinks[i].name ); + } + + context.addCode("code", return_type + " " + outlink + " = "+func_desc.func+"("+params.join(",")+");", this.shader_destination ); + + this.setOutputData( 0, return_type ); + } + + registerShaderNode( "math/func", LGraphShaderMath ); + + + + function LGraphShaderSnippet() + { + this.addInput("A","float,vec2,vec3,vec4"); + this.addInput("B","float,vec2,vec3,vec4"); + this.addOutput("C","vec4"); + this.properties = { + code:"C = A+B", + type: "vec4" + } + this.addWidget("text","code",this.properties.code,{ property: "code" }); + this.addWidget("combo","type",this.properties.type,{ values:["float","vec2","vec3","vec4"], property: "type" }); + } + + LGraphShaderSnippet.title = "Snippet"; + + LGraphShaderSnippet.prototype.onPropertyChanged = function(name,value) + { + this.graph._version++; + + if(name == "type"&& this.outputs[0].type != value) + { + this.disconnectOutput(0); + this.outputs[0].type = value; + } + } + + LGraphShaderSnippet.prototype.getTitle = function() + { + if(this.flags.collapsed) + return this.properties.code; + else + return "Snippet"; + } + + LGraphShaderSnippet.prototype.onGetCode = function( context ) + { + if(!this.isOutputConnected(0)) + return; + + var inlinkA = getInputLinkID(this,0); + if(!inlinkA) + inlinkA = "1.0"; + var inlinkB = getInputLinkID(this,1); + if(!inlinkB) + inlinkB = "1.0"; + var outlink = getOutputLinkID(this,0); + if(!outlink) //not connected + return; + + var inA_type = this.getInputData(0) || "float"; + var inB_type = this.getInputData(1) || "float"; + var return_type = this.properties.type; + + //cannot resolve input + if(inA_type == "T" || inB_type == "T") + { + return null; + } + + var funcname = "funcSnippet" + this.id; + + var func_code = "\n" + return_type + " " + funcname + "( " + inA_type + " A, " + inB_type + " B) {\n"; + func_code += " " + return_type + " C = " + return_type + "(0.0);\n"; + func_code += " " + this.properties.code + ";\n"; + func_code += " return C;\n}\n"; + + context.addCode("functions", func_code, this.shader_destination ); + context.addCode("code", return_type + " " + outlink + " = "+funcname+"("+inlinkA+","+inlinkB+");", this.shader_destination ); + + this.setOutputData( 0, return_type ); + } + + registerShaderNode( "utils/snippet", LGraphShaderSnippet ); + + + //************************************ + function LGraphShaderRemap() + { + this.addInput("","T,float,vec2,vec3,vec4"); + this.addOutput("","T"); + this.properties = { + min_value: 0, + max_value: 1, + min_value2: 0, + max_value2: 1 + }; + this.addWidget("number","min",0,{ step: 0.1, property: "min_value" }); + this.addWidget("number","max",1,{ step: 0.1, property: "max_value" }); + this.addWidget("number","min2",0,{ step: 0.1, property: "min_value2"}); + this.addWidget("number","max2",1,{ step: 0.1, property: "max_value2"}); + } + + LGraphShaderRemap.title = "Remap"; + + LGraphShaderRemap.prototype.onPropertyChanged = function() + { + this.graph._version++; + } + + LGraphShaderRemap.prototype.onConnectionsChange = function() + { + var return_type = this.getInputDataType(0); + this.outputs[0].type = return_type || "T"; + } + + LGraphShaderRemap.prototype.onGetCode = function( context ) + { + if(!this.isOutputConnected(0)) + return; + + var inlink = getInputLinkID(this,0); + var outlink = getOutputLinkID(this,0); + if(!inlink && !outlink) //not connected + return; + + var return_type = this.getInputDataType(0); + this.outputs[0].type = return_type; + if(return_type == "T") + { + console.warn("node type is T and cannot be resolved"); + return; + } + + if(!inlink) + { + context.addCode("code"," " + return_type + " " + outlink + " = " + return_type + "(0.0);\n"); + return; + } + + var minv = valueToGLSL( this.properties.min_value ); + var maxv = valueToGLSL( this.properties.max_value ); + var minv2 = valueToGLSL( this.properties.min_value2 ); + var maxv2 = valueToGLSL( this.properties.max_value2 ); + + context.addCode("code", return_type + " " + outlink + " = ( (" + inlink + " - "+minv+") / ("+ maxv+" - "+minv+") ) * ("+ maxv2+" - "+minv2+") + " + minv2 + ";", this.shader_destination ); + this.setOutputData( 0, return_type ); + } + + registerShaderNode( "math/remap", LGraphShaderRemap ); + +})(this); + + +/* +// https://blog.undefinist.com/writing-a-shader-graph/ + +\sin +f,Out +float->float +{1} = sin({0}); + +\mul +A,B,Out +T,T->T +T,float->T +{2} = {0} * {1}; + +\clamp +f,min,max,Out +float,float=0,float=1->float +vec2,vec2=vec2(0.0),vec2=vec2(1.0)->vec2 +vec3,vec3=vec3(0.0),vec3=vec3(1.0)->vec3 +vec4,vec4=vec4(0.0),vec4=vec4(1.0)->vec4 +{3}=clamp({0},{1},{2}); + +\mix +A,B,f,Out +float,float,float->float +vec2,vec2,float->vec2 +vec3,vec3,float->vec3 +vec4,vec4,float->vec4 +{3} = mix({0},{1},{2}); + +*/ \ No newline at end of file diff --git a/utils/deploy_files.txt b/utils/deploy_files.txt index 03665ddf7..ce998b646 100755 --- a/utils/deploy_files.txt +++ b/utils/deploy_files.txt @@ -9,6 +9,7 @@ ../src/nodes/logic.js ../src/nodes/graphics.js ../src/nodes/gltextures.js +../src/nodes/shaders.js ../src/nodes/geometry.js ../src/nodes/glfx.js ../src/nodes/midi.js From c74d8760b11dddfc46c17b569cbca1d46eebdb5a Mon Sep 17 00:00:00 2001 From: tamat Date: Fri, 17 Jul 2020 19:21:07 +0200 Subject: [PATCH 60/63] variable class works better\nFixes in panels\nBetter shader generation. --- CONTRIBUTING.md | 2 + README.md | 2 +- build/litegraph.js | 864 +++++++++++++++++++++++++--------------- src/litegraph.js | 742 ++++++++++++++++++++-------------- src/nodes/base.js | 44 +- src/nodes/gltextures.js | 4 +- src/nodes/shaders.js | 74 +++- 7 files changed, 1089 insertions(+), 643 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ef80047fc..60cb91d05 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,3 +5,5 @@ There are some simple rules that everyone should follow: > I usually have horrible merge conflicts when I upload the build version that take me too much time to solve, but I want to keep the build version in the repo, so I guess it would be better if only one of us does the built, which would be me. > https://github.com/jagenjo/litegraph.js/pull/155#issuecomment-656602861 Those files will be updated by owner. + + diff --git a/README.md b/README.md index 33b36f31a..e34d7bef1 100755 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ A library in Javascript to create graphs in the browser similar to Unreal Bluepr It can be integrated easily in any existing web applications and graphs can be run without the need of the editor. -Try it in the [demo site](https://tamats.com/projects/litegraph/demo). +Try it in the [demo site](https://tamats.com/projects/litegraph/editor). ![Node Graph](imgs/node_graph_example.png "WebGLStudio") diff --git a/build/litegraph.js b/build/litegraph.js index bab975888..166f1f20c 100644 --- a/build/litegraph.js +++ b/build/litegraph.js @@ -55,7 +55,6 @@ CIRCLE_SHAPE: 3, CARD_SHAPE: 4, ARROW_SHAPE: 5, - SQUARE_SHAPE: 6, //enums INPUT: 1, @@ -94,19 +93,10 @@ registered_node_types: {}, //nodetypes by string node_types_by_file_extension: {}, //used for dropping files in the canvas Nodes: {}, //node types by classname + Globals: {}, //used to store vars between graphs searchbox_extras: {}, //used to add extra features to the search box - /** - * Removes all previously registered node's types - */ - clearRegisteredTypes: function() { - this.registered_node_types = {}; - this.node_types_by_file_extension = {}; - this.Nodes = {}; - this.searchbox_extras = {}; - }, - /** * Register a node class so it can be listed when the user wants to create a new one * @method registerNodeType @@ -1591,8 +1581,10 @@ return; } + node.graph.beforeChange(); this.inputs[name] = { name: name, type: type, value: value }; this._version++; + node.graph.afterChange(); if (this.onInputAdded) { this.onInputAdded(name, type); @@ -1853,6 +1845,22 @@ } }; + //used for undo, called before any change is made to the graph + LGraph.prototype.beforeChange = function(info) { + if (this.onBeforeChange) { + this.onBeforeChange(this,info); + } + this.sendActionToCanvas("onBeforeChange", this); + }; + + //used to resend actions, called after any change is made to the graph + LGraph.prototype.afterChange = function(info) { + if (this.onAfterChange) { + this.onAfterChange(this,info); + } + this.sendActionToCanvas("onAfterChange", this); + }; + LGraph.prototype.connectionChange = function(node, link_info) { this.updateExecutionOrder(); if (this.onConnectionChange) { @@ -3136,6 +3144,7 @@ if (!this.inputs) { this.inputs = []; } + this.inputs.push(o); this.setSize( this.computeSize() ); @@ -3223,10 +3232,10 @@ /** * computes the minimum size of a node according to its inputs and output slots * @method computeSize - * @param {number} the optional target width + * @param {number} minHeight * @return {number} the total size */ - LGraphNode.prototype.computeSize = function(width, out) { + LGraphNode.prototype.computeSize = function(out) { if (this.constructor.size) { return this.constructor.size.concat(); } @@ -3278,7 +3287,7 @@ if (this.widgets && this.widgets.length) { for (var i = 0, l = this.widgets.length; i < l; ++i) { if (this.widgets[i].computeSize) - widgets_height += this.widgets[i].computeSize(Math.max(size[0], width || 0))[1] + 4; + widgets_height += this.widgets[i].computeSize(size[0])[1] + 4; else widgets_height += LiteGraph.NODE_WIDGET_HEIGHT + 4; } @@ -3337,8 +3346,11 @@ if(this.constructor["@" + property]) info = this.constructor["@" + property]; + if(this.constructor.widgets_info && this.constructor.widgets_info[property]) + info = this.constructor.widgets_info[property]; + //litescene mode using the constructor - if (this.onGetPropertyInfo) { + if (!info && this.onGetPropertyInfo) { info = this.onGetPropertyInfo(property); } @@ -3346,6 +3358,8 @@ info = {}; if(!info.type) info.type = typeof this.properties[property]; + if(info.widget == "combo") + info.type = "enum"; return info; } @@ -3644,94 +3658,104 @@ return null; } + var changed = false; + + //if there is something already plugged there, disconnect + if (target_node.inputs[target_slot].link != null) { + this.graph.beforeChange(); + target_node.disconnectInput(target_slot); + changed = true; + } + + //why here?? + //this.setDirtyCanvas(false,true); + //this.graph.connectionChange( this ); + var output = this.outputs[slot]; - var input = target_node.inputs[target_slot]; - if (LiteGraph.isValidConnection(output.type, input.type)) { - if (target_node.onBeforeConnectInput) { - // This way node can choose another slot (if selected is occupied) - target_slot = target_node.onBeforeConnectInput(target_slot); - input = target_node.inputs[target_slot]; - } - - //if there is something already plugged there, disconnect - if (target_node.inputs[target_slot].link != null) { - target_node.disconnectInput(target_slot); - } - - //why here?? - //this.setDirtyCanvas(false,true); - //this.graph.connectionChange( this ); - - //allows nodes to block connection - if (target_node.onConnectInput) { - if ( target_node.onConnectInput(target_slot, output.type, output, this, slot) === false ) { - return null; - } - } - - var link_info = null; - - link_info = new LLink( - ++this.graph.last_link_id, - input.type, - this.id, - slot, - target_node.id, - target_slot - ); - - //add to graph links list - this.graph.links[link_info.id] = link_info; - - //connect in output - if (output.links == null) { - output.links = []; - } - output.links.push(link_info.id); - //connect in input - target_node.inputs[target_slot].link = link_info.id; - if (this.graph) { - this.graph._version++; - } - if (this.onConnectionsChange) { - this.onConnectionsChange( - LiteGraph.OUTPUT, - slot, - true, - link_info, - output - ); - } //link_info has been created now, so its updated - if (target_node.onConnectionsChange) { - target_node.onConnectionsChange( - LiteGraph.INPUT, - target_slot, - true, - link_info, - input - ); - } - if (this.graph && this.graph.onNodeConnectionChange) { - this.graph.onNodeConnectionChange( - LiteGraph.INPUT, - target_node, - target_slot, - this, - slot - ); - this.graph.onNodeConnectionChange( - LiteGraph.OUTPUT, - this, - slot, - target_node, - target_slot - ); + //allows nodes to block connection + if (target_node.onConnectInput) { + if ( target_node.onConnectInput(target_slot, output.type, output, this, slot) === false ) { + return null; } } + var input = target_node.inputs[target_slot]; + var link_info = null; + + //this slots cannot be connected (different types) + if (!LiteGraph.isValidConnection(output.type, input.type)) + { + this.setDirtyCanvas(false, true); + if(changed) + this.graph.connectionChange(this, link_info); + return null; + } + + if(!changed) + this.graph.beforeChange(); + + //create link class + link_info = new LLink( + ++this.graph.last_link_id, + input.type, + this.id, + slot, + target_node.id, + target_slot + ); + + //add to graph links list + this.graph.links[link_info.id] = link_info; + + //connect in output + if (output.links == null) { + output.links = []; + } + output.links.push(link_info.id); + //connect in input + target_node.inputs[target_slot].link = link_info.id; + if (this.graph) { + this.graph._version++; + } + if (this.onConnectionsChange) { + this.onConnectionsChange( + LiteGraph.OUTPUT, + slot, + true, + link_info, + output + ); + } //link_info has been created now, so its updated + if (target_node.onConnectionsChange) { + target_node.onConnectionsChange( + LiteGraph.INPUT, + target_slot, + true, + link_info, + input + ); + } + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange( + LiteGraph.INPUT, + target_node, + target_slot, + this, + slot + ); + this.graph.onNodeConnectionChange( + LiteGraph.OUTPUT, + this, + slot, + target_node, + target_slot + ); + } + this.setDirtyCanvas(false, true); - this.graph.connectionChange(this, link_info); + this.graph.afterChange(); + this.graph.connectionChange(this, link_info); return link_info; }; @@ -4566,8 +4590,7 @@ LGraphNode.prototype.executeAction = function(action) //if(graph === undefined) // throw ("No graph assigned"); - this.background_image = - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; + this.background_image = LGraphCanvas.DEFAULT_BACKGROUND_IMAGE; if (canvas && canvas.constructor === String) { canvas = document.querySelector(canvas); @@ -4640,6 +4663,9 @@ LGraphNode.prototype.executeAction = function(action) this.onDrawLinkTooltip = null; //called when rendering a tooltip this.onNodeMoved = null; //called after moving a node this.onSelectionChange = null; //called if the selection changes + this.onConnectingChange = null; //called before any link changes + this.onBeforeChange = null; //called before modifying the graph + this.onAfterChange = null; //called after modifying the graph this.connections_width = 3; this.round_radius = 8; @@ -4668,6 +4694,8 @@ LGraphNode.prototype.executeAction = function(action) global.LGraphCanvas = LiteGraph.LGraphCanvas = LGraphCanvas; + LGraphCanvas.DEFAULT_BACKGROUND_IMAGE = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; + LGraphCanvas.link_type_colors = { "-1": LiteGraph.EVENT_LINK_COLOR, number: "#AAA", @@ -5171,15 +5199,17 @@ LGraphNode.prototype.executeAction = function(action) LiteGraph.closeAllContextMenus(ref_window); - if (this.onMouse) { - if (this.onMouse(e) == true) { + if (this.onMouse) + { + if (this.onMouse(e) == true) return; - } } - if (e.which == 1) { - //left button mouse - if (e.ctrlKey) { + //left button mouse + if (e.which == 1) + { + if (e.ctrlKey) + { this.dragging_rectangle = new Float32Array(4); this.dragging_rectangle[0] = e.canvasX; this.dragging_rectangle[1] = e.canvasY; @@ -5216,6 +5246,7 @@ LGraphNode.prototype.executeAction = function(action) 10 ) ) { + this.graph.beforeChange(); this.resizing_node = node; this.canvas.style.cursor = "se-resize"; skip_action = true; @@ -5323,15 +5354,6 @@ LGraphNode.prototype.executeAction = function(action) } //not resizing } - //Search for corner for collapsing - /* - if( !skip_action && isInsideRectangle( e.canvasX, e.canvasY, node.pos[0], node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT )) - { - node.collapse(); - skip_action = true; - } - */ - //it wasn't clicked on the links boxes if (!skip_action) { var block_drag_node = false; @@ -5377,6 +5399,7 @@ LGraphNode.prototype.executeAction = function(action) if (!block_drag_node) { if (this.allow_dragnodes) { + this.graph.beforeChange(); this.node_dragged = node; } if (!this.selected_nodes[node.id]) { @@ -5684,7 +5707,7 @@ LGraphNode.prototype.executeAction = function(action) if (this.resizing_node && !this.live_mode) { //convert mouse to node space var desired_size = [ e.canvasX - this.resizing_node.pos[0], e.canvasY - this.resizing_node.pos[1] ]; - var min_size = this.resizing_node.computeSize(desired_size[0]); + var min_size = this.resizing_node.computeSize(); desired_size[0] = Math.max( min_size[0], desired_size[0] ); desired_size[1] = Math.max( min_size[1], desired_size[1] ); this.resizing_node.setSize( desired_size ); @@ -5876,6 +5899,7 @@ LGraphNode.prototype.executeAction = function(action) else if (this.resizing_node) { this.dirty_canvas = true; this.dirty_bgcanvas = true; + this.graph.afterChange(this.resizing_node); this.resizing_node = null; } else if (this.node_dragged) { //node being dragged? @@ -5897,6 +5921,7 @@ LGraphNode.prototype.executeAction = function(action) } if( this.onNodeMoved ) this.onNodeMoved( this.node_dragged ); + this.graph.afterChange(this.node_dragged); this.node_dragged = null; } //no node being dragged else { @@ -6196,6 +6221,8 @@ LGraphNode.prototype.executeAction = function(action) return; } + this.graph.beforeChange(); + //create nodes var clipboard_info = JSON.parse(data); var nodes = []; @@ -6223,6 +6250,8 @@ LGraphNode.prototype.executeAction = function(action) } this.selectNodes(nodes); + + this.graph.afterChange(); }; /** @@ -6303,12 +6332,14 @@ LGraphNode.prototype.executeAction = function(action) var ext = LGraphCanvas.getFileExtension(file.name).toLowerCase(); var nodetype = LiteGraph.node_types_by_file_extension[ext]; if (nodetype) { + this.graph.beforeChange(); var node = LiteGraph.createNode(nodetype.type); node.pos = [e.canvasX, e.canvasY]; this.graph.add(node); if (node.onDropFile) { node.onDropFile(file); } + this.graph.afterChange(); } } }; @@ -6467,6 +6498,9 @@ LGraphNode.prototype.executeAction = function(action) * @method deleteSelectedNodes **/ LGraphCanvas.prototype.deleteSelectedNodes = function() { + + this.graph.beforeChange(); + for (var i in this.selected_nodes) { var node = this.selected_nodes[i]; @@ -6492,6 +6526,7 @@ LGraphNode.prototype.executeAction = function(action) this.current_node = null; this.highlighted_links = {}; this.setDirty(true); + this.graph.afterChange(); }; /** @@ -6946,6 +6981,7 @@ LGraphNode.prototype.executeAction = function(action) if( this.drawButton( 20,y+2,w - 20, h - 2 ) ) { var type = subnode.constructor.input_node_type || "graph/input"; + this.graph.beforeChange(); var newnode = LiteGraph.createNode( type ); if(newnode) { @@ -6959,6 +6995,7 @@ LGraphNode.prototype.executeAction = function(action) newnode.setProperty("type",input.type); this.node_dragged.pos[0] = this.graph_mouse[0] - 5; this.node_dragged.pos[1] = this.graph_mouse[1] - 5; + this.graph.afterChange(); } else console.error("graph input node not found:",type); @@ -7231,44 +7268,6 @@ LGraphNode.prototype.executeAction = function(action) var temp_vec2 = new Float32Array(2); - LGraphCanvas.prototype.drawSlotGraphic = function(ctx, pos, shape, horizontal) { - ctx.beginPath(); - - switch (shape) { - case (LiteGraph.BOX_SHAPE): - if (horizontal) { - ctx.rect( - pos[0] - 5 + 0.5, - pos[1] - 8 + 0.5, - 10, - 14 - ); - } else { - ctx.rect( - pos[0] - 6 + 0.5, - pos[1] - 5 + 0.5, - 14, - 10 - ); - } - break; - case (LiteGraph.ARROW_SHAPE): - ctx.moveTo(pos[0] + 8, pos[1] + 0.5); - ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5); - ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5); - ctx.closePath(); - break; - case (LiteGraph.SQUARE_SHAPE): - ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8); //faster - break; - case (LiteGraph.CIRCLE_SHAPE): - default: - ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); - break; - } - ctx.fill(); - } - /** * draws the given node inside the canvas * @method drawNode @@ -7418,9 +7417,39 @@ LGraphNode.prototype.executeAction = function(action) max_y = pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5; } - var shape = slot.shape || (slot.type === LiteGraph.EVENT && LiteGraph.BOX_SHAPE) - || (low_quality && LiteGraph.SQUARE_SHAPE) || LiteGraph.CIRCLE_SHAPE; - this.drawSlotGraphic(ctx, pos, shape, horizontal); + ctx.beginPath(); + + if ( + slot.type === LiteGraph.EVENT || + slot.shape === LiteGraph.BOX_SHAPE + ) { + if (horizontal) { + ctx.rect( + pos[0] - 5 + 0.5, + pos[1] - 8 + 0.5, + 10, + 14 + ); + } else { + ctx.rect( + pos[0] - 6 + 0.5, + pos[1] - 5 + 0.5, + 14, + 10 + ); + } + } else if (slot.shape === LiteGraph.ARROW_SHAPE) { + ctx.moveTo(pos[0] + 8, pos[1] + 0.5); + ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5); + ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5); + ctx.closePath(); + } else { + if(low_quality) + ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); //faster + else + ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); + } + ctx.fill(); //render name if (render_text) { @@ -7461,11 +7490,46 @@ LGraphNode.prototype.executeAction = function(action) this.default_connection_color.output_on : slot.color_off || this.default_connection_color.output_off; + ctx.beginPath(); + //ctx.rect( node.size[0] - 14,i*14,10,10); - var shape = slot.shape || (slot.type === LiteGraph.EVENT && LiteGraph.BOX_SHAPE) - || (low_quality && LiteGraph.SQUARE_SHAPE) || LiteGraph.CIRCLE_SHAPE; - this.drawSlotGraphic(ctx, pos, shape, horizontal); + if ( + slot.type === LiteGraph.EVENT || + slot.shape === LiteGraph.BOX_SHAPE + ) { + if (horizontal) { + ctx.rect( + pos[0] - 5 + 0.5, + pos[1] - 8 + 0.5, + 10, + 14 + ); + } else { + ctx.rect( + pos[0] - 6 + 0.5, + pos[1] - 5 + 0.5, + 14, + 10 + ); + } + } else if (slot.shape === LiteGraph.ARROW_SHAPE) { + ctx.moveTo(pos[0] + 8, pos[1] + 0.5); + ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5); + ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5); + ctx.closePath(); + } else { + if(low_quality) + ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); + else + ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); + } + //trigger + //if(slot.node_id != null && slot.slot == -1) + // ctx.fillStyle = "#F85"; + + //if(slot.links != null && slot.links.length) + ctx.fill(); if(!low_quality) ctx.stroke(); @@ -8706,149 +8770,161 @@ LGraphNode.prototype.executeAction = function(action) if(!w || w.disabled) continue; var widget_height = w.computeSize ? w.computeSize(width)[1] : LiteGraph.NODE_WIDGET_HEIGHT; - if ( w == active_widget || (x > 6 && x < width - 12 && y > w.last_y && y < w.last_y + widget_height) ) { - //inside widget - switch (w.type) { - case "button": - if (event.type === "mousemove") { - break; - } - if (w.callback) { - setTimeout(function() { - w.callback(w, that, node, pos, event); - }, 20); - } - w.clicked = true; - this.dirty_canvas = true; - break; - case "slider": - var range = w.options.max - w.options.min; - var nvalue = Math.clamp((x - 10) / (width - 20), 0, 1); - w.value = - w.options.min + - (w.options.max - w.options.min) * nvalue; - if (w.callback) { - setTimeout(function() { - inner_value_change(w, w.value); - }, 20); - } - this.dirty_canvas = true; - break; - case "number": - case "combo": - var old_value = w.value; - if (event.type == "mousemove" && w.type == "number") { - w.value += event.deltaX * 0.1 * (w.options.step || 1); - if ( w.options.min != null && w.value < w.options.min ) { - w.value = w.options.min; - } - if ( w.options.max != null && w.value > w.options.max ) { - w.value = w.options.max; - } - } else if (event.type == "mousedown") { - var values = w.options.values; - if (values && values.constructor === Function) { - values = w.options.values(w, node); - } - var values_list = null; - - if( w.type != "number") - values_list = values.constructor === Array ? values : Object.keys(values); + //outside + if ( w != active_widget && + (x < 6 || x > width - 12 || y < w.last_y || y > w.last_y + widget_height) ) + continue; - var delta = x < 40 ? -1 : x > width - 40 ? 1 : 0; - if (w.type == "number") { - w.value += delta * 0.1 * (w.options.step || 1); - if ( w.options.min != null && w.value < w.options.min ) { - w.value = w.options.min; - } - if ( w.options.max != null && w.value > w.options.max ) { - w.value = w.options.max; - } - } else if (delta) { //clicked in arrow, used for combos - var index = -1; - this.last_mouseclick = 0; //avoids dobl click event - if(values.constructor === Object) - index = values_list.indexOf( String( w.value ) ) + delta; - else - index = values_list.indexOf( w.value ) + delta; - if (index >= values_list.length) { - index = values_list.length - 1; - } - if (index < 0) { - index = 0; - } - if( values.constructor === Array ) - w.value = values[index]; - else - w.value = index; - } else { //combo clicked - var text_values = values != values_list ? Object.values(values) : values; - var menu = new LiteGraph.ContextMenu(text_values, { - scale: Math.max(1, this.ds.scale), - event: event, - className: "dark", - callback: inner_clicked.bind(w) - }, - ref_window); - function inner_clicked(v, option, event) { - if(values != values_list) - v = text_values.indexOf(v); - this.value = v; - inner_value_change(this, v); - that.dirty_canvas = true; - return false; - } - } - } //end mousedown - else if(event.type == "mouseup" && w.type == "number") - { - var delta = x < 40 ? -1 : x > width - 40 ? 1 : 0; - if (event.click_time < 200 && delta == 0) { - this.prompt("Value",w.value,function(v) { - this.value = Number(v); - inner_value_change(this, this.value); - }.bind(w), - event); + var old_value = w.value; + + //if ( w == active_widget || (x > 6 && x < width - 12 && y > w.last_y && y < w.last_y + widget_height) ) { + //inside widget + switch (w.type) { + case "button": + if (event.type === "mousemove") { + break; + } + if (w.callback) { + setTimeout(function() { + w.callback(w, that, node, pos, event); + }, 20); + } + w.clicked = true; + this.dirty_canvas = true; + break; + case "slider": + var range = w.options.max - w.options.min; + var nvalue = Math.clamp((x - 10) / (width - 20), 0, 1); + w.value = + w.options.min + + (w.options.max - w.options.min) * nvalue; + if (w.callback) { + setTimeout(function() { + inner_value_change(w, w.value); + }, 20); + } + this.dirty_canvas = true; + break; + case "number": + case "combo": + var old_value = w.value; + if (event.type == "mousemove" && w.type == "number") { + w.value += event.deltaX * 0.1 * (w.options.step || 1); + if ( w.options.min != null && w.value < w.options.min ) { + w.value = w.options.min; + } + if ( w.options.max != null && w.value > w.options.max ) { + w.value = w.options.max; + } + } else if (event.type == "mousedown") { + var values = w.options.values; + if (values && values.constructor === Function) { + values = w.options.values(w, node); + } + var values_list = null; + + if( w.type != "number") + values_list = values.constructor === Array ? values : Object.keys(values); + + var delta = x < 40 ? -1 : x > width - 40 ? 1 : 0; + if (w.type == "number") { + w.value += delta * 0.1 * (w.options.step || 1); + if ( w.options.min != null && w.value < w.options.min ) { + w.value = w.options.min; + } + if ( w.options.max != null && w.value > w.options.max ) { + w.value = w.options.max; + } + } else if (delta) { //clicked in arrow, used for combos + var index = -1; + this.last_mouseclick = 0; //avoids dobl click event + if(values.constructor === Object) + index = values_list.indexOf( String( w.value ) ) + delta; + else + index = values_list.indexOf( w.value ) + delta; + if (index >= values_list.length) { + index = values_list.length - 1; + } + if (index < 0) { + index = 0; + } + if( values.constructor === Array ) + w.value = values[index]; + else + w.value = index; + } else { //combo clicked + var text_values = values != values_list ? Object.values(values) : values; + var menu = new LiteGraph.ContextMenu(text_values, { + scale: Math.max(1, this.ds.scale), + event: event, + className: "dark", + callback: inner_clicked.bind(w) + }, + ref_window); + function inner_clicked(v, option, event) { + if(values != values_list) + v = text_values.indexOf(v); + this.value = v; + inner_value_change(this, v); + that.dirty_canvas = true; + return false; } } - - if( old_value != w.value ) - setTimeout( - function() { + } //end mousedown + else if(event.type == "mouseup" && w.type == "number") + { + var delta = x < 40 ? -1 : x > width - 40 ? 1 : 0; + if (event.click_time < 200 && delta == 0) { + this.prompt("Value",w.value,function(v) { + this.value = Number(v); inner_value_change(this, this.value); }.bind(w), - 20 - ); - this.dirty_canvas = true; - break; - case "toggle": - if (event.type == "mousedown") { - w.value = !w.value; - setTimeout(function() { - inner_value_change(w, w.value); - }, 20); - } - break; - case "string": - case "text": - if (event.type == "mousedown") { - this.prompt("Value",w.value,function(v) { - this.value = v; - inner_value_change(this, v); - }.bind(w), - event); - } - break; - default: - if (w.mouse) { - this.dirty_canvas = w.mouse(event, [x, y], node); - } - break; - } //end switch + event); + } + } - return w; - } - } + if( old_value != w.value ) + setTimeout( + function() { + inner_value_change(this, this.value); + }.bind(w), + 20 + ); + this.dirty_canvas = true; + break; + case "toggle": + if (event.type == "mousedown") { + w.value = !w.value; + setTimeout(function() { + inner_value_change(w, w.value); + }, 20); + } + break; + case "string": + case "text": + if (event.type == "mousedown") { + this.prompt("Value",w.value,function(v) { + this.value = v; + inner_value_change(this, v); + }.bind(w), + event); + } + break; + default: + if (w.mouse) { + this.dirty_canvas = w.mouse(event, [x, y], node); + } + break; + } //end switch + + //value changed + if( old_value != w.value ) + { + node.graph._version++; + } + + return w; + }//end for function inner_value_change(widget, value) { widget.value = value; @@ -9079,6 +9155,7 @@ LGraphNode.prototype.executeAction = function(action) function inner_create(v, e) { var first_event = prev_menu.getFirstEvent(); + canvas.graph.beforeChange(); var node = LiteGraph.createNode(v.value); if (node) { node.pos = canvas.convertEventToCanvasOffset(first_event); @@ -9086,6 +9163,7 @@ LGraphNode.prototype.executeAction = function(action) } if(callback) callback(node); + canvas.graph.afterChange(); } return false; @@ -9165,8 +9243,10 @@ LGraphNode.prototype.executeAction = function(action) } if (v.value) { + node.graph.beforeChange(); node.addInput(v.value[0], v.value[1], v.value[2]); node.setDirtyCanvas(true, true); + node.graph.afterChange(); } } @@ -9273,8 +9353,10 @@ LGraphNode.prototype.executeAction = function(action) }); return false; } else { + node.graph.beforeChange(); node.addOutput(v.value[0], v.value[1], v.value[2]); node.setDirtyCanvas(true, true); + node.graph.afterChange(); } } @@ -9301,6 +9383,10 @@ LGraphNode.prototype.executeAction = function(action) var value = node.properties[i] !== undefined ? node.properties[i] : " "; if( typeof value == "object" ) value = JSON.stringify(value); + var info = node.getPropertyInfo(i); + if(info.type == "enum" || info.type == "combo") + value = LGraphCanvas.getPropertyPrintableValue( value, info.values ); + //value could contain invalid html characters, clean that value = LGraphCanvas.decodeHTML(value); entries.push({ @@ -9703,6 +9789,7 @@ LGraphNode.prototype.executeAction = function(action) name = extra.type; } + graphcanvas.graph.beforeChange(); var node = LiteGraph.createNode(name); if (node) { node.pos = graphcanvas.convertEventToCanvasOffset( @@ -9741,6 +9828,8 @@ LGraphNode.prototype.executeAction = function(action) if (extra.data.json) { node.configure(extra.data.json); } + + graphcanvas.graph.afterChange(); } } } @@ -9872,10 +9961,13 @@ LGraphNode.prototype.executeAction = function(action) if (type == "string" || type == "number" || type == "array" || type == "object") { input_html = ""; - } else if (type == "enum" && info.values) { + } else if ( (type == "enum" || type == "combo") && info.values) { input_html = ""; - } else if (type == "enum" && info.values) { + } else if ( (type == "enum" || type == "combo") && info.values) { input_html = "";l.querySelector(".name").innerText=c;var t=l.querySelector("input");t&&(t.value=b,t.addEventListener("blur",function(a){this.focus()}),t.addEventListener("keydown",function(a){13==a.keyCode&&(e(),a.preventDefault(),a.stopPropagation())})); -b=h.active_canvas.canvas;d=b.getBoundingClientRect();var p=f=-20;d&&(f-=d.left,p-=d.top);event?(l.style.left=event.clientX+f+"px",l.style.top=event.clientY+p+"px"):(l.style.left=0.5*b.width+f+"px",l.style.top=0.5*b.height+p+"px");l.querySelector("button").addEventListener("click",e);b.parentNode.appendChild(l)};h.prototype.prompt=function(a,b,d,f){var g=this;a=a||"";var e=!1,c=document.createElement("div");c.className="graphdialog rounded";c.innerHTML=" "; -c.close=function(){g.prompt_box=null;c.parentNode&&c.parentNode.removeChild(c)};1h.search_limit)break}}t=null;if(Array.prototype.filter)t=Object.keys(e.registered_node_types).filter(T); -else for(q in t=[],e.registered_node_types)T(q)&&t.push(q);for(q=0;qh.search_limit);q++);var T=function(a){var b=e.registered_node_types[a];return l&&b.filter!=l?!1:-1!==a.toLowerCase().indexOf(d)}}}var g=this,c=h.active_canvas,q=c.canvas,l=q.ownerDocument||document,t=document.createElement("div");t.className="litegraph litesearchbox graphdialog rounded";t.innerHTML="Search
"; -t.close=function(){g.search_box=null;l.body.focus();l.body.style.overflow="";setTimeout(function(){g.canvas.focus()},20);t.parentNode&&t.parentNode.removeChild(t)};var p=null;1q.height-200&&(r.style.maxHeight=q.height-a.layerY-20+"px");n.focus();return t};h.prototype.showEditPropertyValue=function(a,b,d){function f(){g(v.value)}function g(g){"number"==typeof a.properties[b]&&(g=Number(g));if("array"==c||"object"==c)g=JSON.parse(g);a.properties[b]=g;a._graph&&a._graph._version++;if(a.onPropertyChanged)a.onPropertyChanged(b, -g);if(d.onclose)d.onclose();r.close();a.setDirtyCanvas(!0,!0)}if(a&&void 0!==a.properties[b]){d=d||{};var e=a.getPropertyInfo(b),c=e.type,l="";if("string"==c||"number"==c||"array"==c||"object"==c)l="";else if("enum"==c&&e.values){var l=""}else if("boolean"== -c)l="";else{console.warn("unknown type: "+c);return}var r=this.createDialog(""+b+""+l+"",d);if("enum"==c&&e.values){var v=r.querySelector("select");v.addEventListener("change",function(a){g(a.target.value)})}else if("boolean"==c)(v=r.querySelector("input"))&&v.addEventListener("click",function(a){g(!!v.checked)});else if(v=r.querySelector("input"))v.addEventListener("blur", -function(a){this.focus()}),p=void 0!==a.properties[b]?a.properties[b]:"",p=JSON.stringify(p),v.value=p,v.addEventListener("keydown",function(a){13==a.keyCode&&(f(),a.preventDefault(),a.stopPropagation())});r.querySelector("button").addEventListener("click",f);return r}};h.prototype.createDialog=function(a,b){b=b||{};var d=document.createElement("div");d.className="graphdialog";d.innerHTML=a;var f=this.canvas.getBoundingClientRect(),g=-20,e=-20;f&&(g-=f.left,e-=f.top);b.position?(g+=b.position[0], -e+=b.position[1]):b.event?(g+=b.event.clientX,e+=b.event.clientY):(g+=0.5*this.canvas.width,e+=0.5*this.canvas.height);d.style.left=g+"px";d.style.top=e+"px";this.canvas.parentNode.appendChild(d);d.close=function(){this.parentNode&&this.parentNode.removeChild(this)};return d};h.prototype.createPanel=function(a,b){b=b||{};var d=b.window||window,f=document.createElement("div");f.className="litegraph dialog";f.innerHTML="
"; +a.type||"DOMMouseScroll"==a.type)a.eventType="mousewheel",a.wheel="wheel"==a.type?-a.deltaY:null!=a.wheelDeltaY?a.wheelDeltaY:-60*a.detail,a.delta=a.wheelDelta?a.wheelDelta/40:a.deltaY?-a.deltaY/3:0,this.changeDeltaScale(1+0.05*a.delta);this.last_mouse[0]=f;this.last_mouse[1]=d;a.preventDefault();a.stopPropagation();return!1}};v.prototype.toCanvasContext=function(a){a.scale(this.scale,this.scale);a.translate(this.offset[0],this.offset[1])};v.prototype.convertOffsetToCanvas=function(a){return[(a[0]+ +this.offset[0])*this.scale,(a[1]+this.offset[1])*this.scale]};v.prototype.convertCanvasToOffset=function(a,b){b=b||[0,0];b[0]=a[0]/this.scale-this.offset[0];b[1]=a[1]/this.scale-this.offset[1];return b};v.prototype.mouseDrag=function(a,b){this.offset[0]+=a/this.scale;this.offset[1]+=b/this.scale;if(this.onredraw)this.onredraw(this)};v.prototype.changeScale=function(a,b){athis.max_scale&&(a=this.max_scale);if(a!=this.scale&&this.element){var d=this.element.getBoundingClientRect(); +if(d){b=b||[0.5*d.width,0.5*d.height];d=this.convertCanvasToOffset(b);this.scale=a;0.01>Math.abs(this.scale-1)&&(this.scale=1);var f=this.convertCanvasToOffset(b),d=[f[0]-d[0],f[1]-d[1]];this.offset[0]+=d[0];this.offset[1]+=d[1];if(this.onredraw)this.onredraw(this)}}};v.prototype.changeDeltaScale=function(a,b){this.changeScale(this.scale*a,b)};v.prototype.reset=function(){this.scale=1;this.offset[0]=0;this.offset[1]=0};D.LGraphCanvas=e.LGraphCanvas=h;h.DEFAULT_BACKGROUND_IMAGE="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; +h.link_type_colors={"-1":e.EVENT_LINK_COLOR,number:"#AAA",node:"#DCA"};h.gradients={};h.prototype.clear=function(){this.fps=this.render_time=this.last_draw_time=this.frame=0;this.dragging_rectangle=null;this.selected_nodes={};this.selected_group=null;this.visible_nodes=[];this.connecting_node=this.node_capturing_input=this.node_over=this.node_dragged=null;this.highlighted_links={};this.dragging_canvas=!1;this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_widget=this.node_in_panel=this.dirty_area= +null;this.last_mouse=[0,0];this.last_mouseclick=0;this.visible_area.set([0,0,0,0]);if(this.onClear)this.onClear()};h.prototype.setGraph=function(a,b){this.graph!=a&&(b||this.clear(),!a&&this.graph?this.graph.detachCanvas(this):(a.attachCanvas(this),this._graph_stack&&(this._graph_stack=null),this.setDirty(!0,!0)))};h.prototype.getTopGraph=function(){return this._graph_stack.length?this._graph_stack[0]:this.graph};h.prototype.openSubgraph=function(a){if(!a)throw"graph cannot be null";if(this.graph== +a)throw"graph cannot be the same";this.clear();this.graph&&(this._graph_stack||(this._graph_stack=[]),this._graph_stack.push(this.graph));a.attachCanvas(this);this.checkPanels();this.setDirty(!0,!0)};h.prototype.closeSubgraph=function(){if(this._graph_stack&&0!=this._graph_stack.length){var a=this.graph._subgraph_node,b=this._graph_stack.pop();this.selected_nodes={};this.highlighted_links={};b.attachCanvas(this);this.setDirty(!0,!0);a&&(this.centerOnNode(a),this.selectNodes([a]))}};h.prototype.getCurrentGraph= +function(){return this.graph};h.prototype.setCanvas=function(a,b){if(a&&a.constructor===String&&(a=document.getElementById(a),!a))throw"Error creating LiteGraph canvas: Canvas not found";if(a!==this.canvas&&(!a&&this.canvas&&(b||this.unbindEvents()),this.canvas=a,this.ds.element=a)){a.className+=" lgraphcanvas";a.data=this;a.tabindex="1";this.bgcanvas=null;this.bgcanvas||(this.bgcanvas=document.createElement("canvas"),this.bgcanvas.width=this.canvas.width,this.bgcanvas.height=this.canvas.height); +if(null==a.getContext){if("canvas"!=a.localName)throw"Element supplied for LGraphCanvas must be a element, you passed a "+a.localName;throw"This browser doesn't support Canvas";}null==(this.ctx=a.getContext("2d"))&&(a.webgl_enabled||console.warn("This canvas seems to be WebGL, enabling WebGL renderer"),this.enableWebGL());this._mousemove_callback=this.processMouseMove.bind(this);this._mouseup_callback=this.processMouseUp.bind(this);b||this.bindEvents()}};h.prototype._doNothing=function(a){a.preventDefault(); +return!1};h.prototype._doReturnTrue=function(a){a.preventDefault();return!0};h.prototype.bindEvents=function(){if(this._events_binded)console.warn("LGraphCanvas: events already binded");else{var a=this.canvas,b=this.getCanvasWindow().document;this._mousedown_callback=this.processMouseDown.bind(this);this._mousewheel_callback=this.processMouseWheel.bind(this);a.addEventListener("mousedown",this._mousedown_callback,!0);a.addEventListener("mousemove",this._mousemove_callback);a.addEventListener("mousewheel", +this._mousewheel_callback,!1);a.addEventListener("contextmenu",this._doNothing);a.addEventListener("DOMMouseScroll",this._mousewheel_callback,!1);a.addEventListener("touchstart",this.touchHandler,!0);a.addEventListener("touchmove",this.touchHandler,!0);a.addEventListener("touchend",this.touchHandler,!0);a.addEventListener("touchcancel",this.touchHandler,!0);this._key_callback=this.processKey.bind(this);a.addEventListener("keydown",this._key_callback,!0);b.addEventListener("keyup",this._key_callback, +!0);this._ondrop_callback=this.processDrop.bind(this);a.addEventListener("dragover",this._doNothing,!1);a.addEventListener("dragend",this._doNothing,!1);a.addEventListener("drop",this._ondrop_callback,!1);a.addEventListener("dragenter",this._doReturnTrue,!1);this._events_binded=!0}};h.prototype.unbindEvents=function(){if(this._events_binded){var a=this.getCanvasWindow().document;this.canvas.removeEventListener("mousedown",this._mousedown_callback);this.canvas.removeEventListener("mousewheel",this._mousewheel_callback); +this.canvas.removeEventListener("DOMMouseScroll",this._mousewheel_callback);this.canvas.removeEventListener("keydown",this._key_callback);a.removeEventListener("keyup",this._key_callback);this.canvas.removeEventListener("contextmenu",this._doNothing);this.canvas.removeEventListener("drop",this._ondrop_callback);this.canvas.removeEventListener("dragenter",this._doReturnTrue);this.canvas.removeEventListener("touchstart",this.touchHandler);this.canvas.removeEventListener("touchmove",this.touchHandler); +this.canvas.removeEventListener("touchend",this.touchHandler);this.canvas.removeEventListener("touchcancel",this.touchHandler);this._ondrop_callback=this._key_callback=this._mousewheel_callback=this._mousedown_callback=null;this._events_binded=!1}else console.warn("LGraphCanvas: no events binded")};h.getFileExtension=function(a){var b=a.indexOf("?");-1!=b&&(a=a.substr(0,b));b=a.lastIndexOf(".");return-1==b?"":a.substr(b+1).toLowerCase()};h.prototype.enableWebGL=function(){if(void 0===typeof GL)throw"litegl.js must be included to use a WebGL canvas"; +if(void 0===typeof enableWebGLCanvas)throw"webglCanvas.js must be included to use this feature";this.gl=this.ctx=enableWebGLCanvas(this.canvas);this.ctx.webgl=!0;this.bgcanvas=this.canvas;this.bgctx=this.gl;this.canvas.webgl_enabled=!0};h.prototype.setDirty=function(a,b){a&&(this.dirty_canvas=!0);b&&(this.dirty_bgcanvas=!0)};h.prototype.getCanvasWindow=function(){if(!this.canvas)return window;var a=this.canvas.ownerDocument;return a.defaultView||a.parentWindow};h.prototype.startRendering=function(){function a(){this.pause_rendering|| +this.draw();var b=this.getCanvasWindow();this.is_rendering&&b.requestAnimationFrame(a.bind(this))}this.is_rendering||(this.is_rendering=!0,a.call(this))};h.prototype.stopRendering=function(){this.is_rendering=!1};h.prototype.blockClick=function(){this.block_click=!0;this.last_mouseclick=0};h.prototype.processMouseDown=function(a){this.set_canvas_dirty_on_mouse_event&&(this.dirty_canvas=!0);if(this.graph){this.adjustMouseEvent(a);var b=this.getCanvasWindow();h.active_canvas=this;var d=this;this.canvas.removeEventListener("mousemove", +this._mousemove_callback);b.document.addEventListener("mousemove",this._mousemove_callback,!0);b.document.addEventListener("mouseup",this._mouseup_callback,!0);var f=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes,5),g=!1,r=300>e.getTime()-this.last_mouseclick;this.mouse[0]=a.localX;this.mouse[1]=a.localY;this.graph_mouse[0]=a.canvasX;this.graph_mouse[1]=a.canvasY;this.last_click_position=[this.mouse[0],this.mouse[1]];this.canvas.focus();e.closeAllContextMenus(b);if(!this.onMouse|| +!0!=this.onMouse(a)){if(1==a.which){a.ctrlKey&&(this.dragging_rectangle=new Float32Array(4),this.dragging_rectangle[0]=a.canvasX,this.dragging_rectangle[1]=a.canvasY,this.dragging_rectangle[2]=1,this.dragging_rectangle[3]=1,g=!0);var x=!1;if(f&&this.allow_interaction&&!g&&!this.read_only){this.live_mode||f.flags.pinned||this.bringToFront(f);if(!this.connecting_node&&!f.flags.collapsed&&!this.live_mode)if(!g&&!1!==f.resizable&&y(a.canvasX,a.canvasY,f.pos[0]+f.size[0]-5,f.pos[1]+f.size[1]-5,10,10))this.graph.beforeChange(), +this.resizing_node=f,this.canvas.style.cursor="se-resize",g=!0;else{if(f.outputs)for(var c=0,q=f.outputs.length;cf.size[0]-e.NODE_TITLE_HEIGHT&&0>q[1]&&(d=this,setTimeout(function(){d.openSubgraph(f.subgraph)},10)),this.live_mode&& +(c=x=!0));c||(this.allow_dragnodes&&(this.graph.beforeChange(),this.node_dragged=f),this.selected_nodes[f.id]||this.processNodeSelected(f,a));this.dirty_canvas=!0}}else{if(!this.read_only)for(c=0;cq[0]+4||a.canvasYq[1]+4)){this.showLinkMenu(x,a);this.over_link_center=null;break}this.selected_group=this.graph.getGroupOnPos(a.canvasX,a.canvasY);this.selected_group_resizing=!1;this.selected_group&& +!this.read_only&&(a.ctrlKey&&(this.dragging_rectangle=null),10>B([a.canvasX,a.canvasY],[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])*this.ds.scale?this.selected_group_resizing=!0:this.selected_group.recomputeInsideNodes());r&&!this.read_only&&this.allow_searchbox&&this.showSearchBox(a);x=!0}!g&&x&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}else 2!=a.which&&3==a.which&&(this.read_only||this.processContextMenu(f,a));this.last_mouse[0]= +a.localX;this.last_mouse[1]=a.localY;this.last_mouseclick=e.getTime();this.last_mouse_dragging=!0;this.graph.change();(!b.document.activeElement||"input"!=b.document.activeElement.nodeName.toLowerCase()&&"textarea"!=b.document.activeElement.nodeName.toLowerCase())&&a.preventDefault();a.stopPropagation();if(this.onMouseDown)this.onMouseDown(a);return!1}}};h.prototype.processMouseMove=function(a){this.autoresize&&this.resize();this.set_canvas_dirty_on_mouse_event&&(this.dirty_canvas=!0);if(this.graph){h.active_canvas= +this;this.adjustMouseEvent(a);var b=[a.localX,a.localY];this.mouse[0]=b[0];this.mouse[1]=b[1];var d=[b[0]-this.last_mouse[0],b[1]-this.last_mouse[1]];this.last_mouse=b;this.graph_mouse[0]=a.canvasX;this.graph_mouse[1]=a.canvasY;if(this.block_click)return a.preventDefault(),!1;a.dragging=this.last_mouse_dragging;this.node_widget&&(this.processNodeWidgets(this.node_widget[0],this.graph_mouse,a,this.node_widget[1]),this.dirty_canvas=!0);if(this.dragging_rectangle)this.dragging_rectangle[2]=a.canvasX- +this.dragging_rectangle[0],this.dragging_rectangle[3]=a.canvasY-this.dragging_rectangle[1],this.dirty_canvas=!0;else if(this.selected_group&&!this.read_only)this.selected_group_resizing?this.selected_group.size=[a.canvasX-this.selected_group.pos[0],a.canvasY-this.selected_group.pos[1]]:(this.selected_group.move(d[0]/this.ds.scale,d[1]/this.ds.scale,a.ctrlKey),this.selected_group._nodes.length&&(this.dirty_canvas=!0)),this.dirty_bgcanvas=!0;else if(this.dragging_canvas)this.ds.offset[0]+=d[0]/this.ds.scale, +this.ds.offset[1]+=d[1]/this.ds.scale,this.dirty_bgcanvas=this.dirty_canvas=!0;else if(this.allow_interaction&&!this.read_only){this.connecting_node&&(this.dirty_canvas=!0);for(var f=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes),b=0,g=this.graph._nodes.length;bx[0]+4||a.canvasYx[1]+4)){g=r;break}}g!=this.over_link_center&&(this.over_link_center=g,this.dirty_canvas=!0);this.canvas&&(this.canvas.style.cursor="")}if(this.node_capturing_input&&this.node_capturing_input!=f&& +this.node_capturing_input.onMouseMove)this.node_capturing_input.onMouseMove(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]],this);if(this.node_dragged&&!this.live_mode){for(b in this.selected_nodes)f=this.selected_nodes[b],f.pos[0]+=d[0]/this.ds.scale,f.pos[1]+=d[1]/this.ds.scale;this.dirty_bgcanvas=this.dirty_canvas=!0}this.resizing_node&&!this.live_mode&&(d=[a.canvasX-this.resizing_node.pos[0],a.canvasY-this.resizing_node.pos[1]],b=this.resizing_node.computeSize(), +d[0]=Math.max(b[0],d[0]),d[1]=Math.max(b[1],d[1]),this.resizing_node.setSize(d),this.canvas.style.cursor="se-resize",this.dirty_bgcanvas=this.dirty_canvas=!0)}a.preventDefault();return!1}};h.prototype.processMouseUp=function(a){this.set_canvas_dirty_on_mouse_event&&(this.dirty_canvas=!0);if(this.graph){var b=this.getCanvasWindow().document;h.active_canvas=this;b.removeEventListener("mousemove",this._mousemove_callback,!0);this.canvas.addEventListener("mousemove",this._mousemove_callback,!0);b.removeEventListener("mouseup", +this._mouseup_callback,!0);this.adjustMouseEvent(a);b=e.getTime();a.click_time=b-this.last_mouseclick;this.last_mouse_dragging=!1;this.last_click_position=null;this.block_click&&(console.log("foo"),this.block_click=!1);if(1==a.which){this.node_widget&&this.processNodeWidgets(this.node_widget[0],this.graph_mouse,a);this.node_widget=null;if(this.selected_group){var b=this.selected_group.pos[0]-Math.round(this.selected_group.pos[0]),d=this.selected_group.pos[1]-Math.round(this.selected_group.pos[1]); +this.selected_group.move(b,d,a.ctrlKey);this.selected_group.pos[0]=Math.round(this.selected_group.pos[0]);this.selected_group.pos[1]=Math.round(this.selected_group.pos[1]);this.selected_group._nodes.length&&(this.dirty_canvas=!0);this.selected_group=null}this.selected_group_resizing=!1;if(this.dragging_rectangle){if(this.graph){b=this.graph._nodes;d=new Float32Array(4);this.deselectAllNodes();var f=Math.abs(this.dragging_rectangle[2]),g=Math.abs(this.dragging_rectangle[3]),r=0>this.dragging_rectangle[3]? +this.dragging_rectangle[1]-g:this.dragging_rectangle[1];this.dragging_rectangle[0]=0>this.dragging_rectangle[2]?this.dragging_rectangle[0]-f:this.dragging_rectangle[0];this.dragging_rectangle[1]=r;this.dragging_rectangle[2]=f;this.dragging_rectangle[3]=g;g=[];for(r=0;ra.click_time&&y(a.canvasX,a.canvasY,f.pos[0],f.pos[1]-e.NODE_TITLE_HEIGHT,e.NODE_TITLE_HEIGHT,e.NODE_TITLE_HEIGHT)&&f.collapse();this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_dragged.pos[0]=Math.round(this.node_dragged.pos[0]); +this.node_dragged.pos[1]=Math.round(this.node_dragged.pos[1]);this.graph.config.align_to_grid&&this.node_dragged.alignToGrid();if(this.onNodeMoved)this.onNodeMoved(this.node_dragged);this.graph.afterChange(this.node_dragged);this.node_dragged=null}else{f=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes);!f&&300>a.click_time&&this.deselectAllNodes();this.dirty_canvas=!0;this.dragging_canvas=!1;if(this.node_over&&this.node_over.onMouseUp)this.node_over.onMouseUp(a,[a.canvasX-this.node_over.pos[0], +a.canvasY-this.node_over.pos[1]],this);if(this.node_capturing_input&&this.node_capturing_input.onMouseUp)this.node_capturing_input.onMouseUp(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]])}}else 2==a.which?(this.dirty_canvas=!0,this.dragging_canvas=!1):3==a.which&&(this.dirty_canvas=!0,this.dragging_canvas=!1);this.graph.change();a.stopPropagation();a.preventDefault();return!1}};h.prototype.processMouseWheel=function(a){if(this.graph&&this.allow_dragcanvas){var b= +null!=a.wheelDeltaY?a.wheelDeltaY:-60*a.detail;this.adjustMouseEvent(a);var d=this.ds.scale;0b&&(d*=1/1.1);this.ds.changeScale(d,[a.localX,a.localY]);this.graph.change();a.preventDefault();return!1}};h.prototype.isOverNodeBox=function(a,b,d){var f=e.NODE_TITLE_HEIGHT;return y(b,d,a.pos[0]+2,a.pos[1]+2-f,f-4,f-4)?!0:!1};h.prototype.isOverNodeInput=function(a,b,d,f){if(a.inputs)for(var g=0,r=a.inputs.length;gd-this.graph._last_trigger_time)&&this.drawBackCanvas(); +(this.dirty_canvas||a)&&this.drawFrontCanvas();this.fps=this.render_time?1/this.render_time:0;this.frame+=1}};h.prototype.drawFrontCanvas=function(){this.dirty_canvas=!1;this.ctx||(this.ctx=this.bgcanvas.getContext("2d"));var a=this.ctx;if(a){a.start2D&&a.start2D();var b=this.canvas;a.restore();a.setTransform(1,0,0,1,0,0);this.dirty_area&&(a.save(),a.beginPath(),a.rect(this.dirty_area[0],this.dirty_area[1],this.dirty_area[2],this.dirty_area[3]),a.clip());this.clear_background&&a.clearRect(0,0,b.width, +b.height);this.bgcanvas==this.canvas?this.drawBackCanvas():a.drawImage(this.bgcanvas,0,0);if(this.onRender)this.onRender(b,a);this.show_info&&this.renderInfo(a);if(this.graph){a.save();this.ds.toCanvasContext(a);for(var b=this.computeVisibleNodes(null,this.visible_nodes),d=0;d> ";b.fillText(f+d.getTitle(),0.5*a.width,40);b.restore()}d=!1;this.onRenderBackground&&(d=this.onRenderBackground(a,b));b.restore();b.setTransform(1,0,0,1,0,0);this.visible_links.length=0;if(this.graph){b.save();this.ds.toCanvasContext(b);if(this.background_image&&0.5this.ds.scale;if(this.live_mode){if(!a.flags.collapsed&&(b.shadowColor="transparent",a.onDrawForeground))a.onDrawForeground(b, +this,this.canvas)}else{var r=this.editor_alpha;b.globalAlpha=r;this.render_shadows&&!g?(b.shadowColor=e.DEFAULT_SHADOW_COLOR,b.shadowOffsetX=2*this.ds.scale,b.shadowOffsetY=2*this.ds.scale,b.shadowBlur=3*this.ds.scale):b.shadowColor="transparent";if(!a.flags.collapsed||!a.onDrawCollapsed||!0!=a.onDrawCollapsed(b,this)){var c=a._shape||e.BOX_SHAPE;F.set(a.size);var l=a.horizontal;if(a.flags.collapsed){b.font=this.inner_text_font;var q=a.getTitle?a.getTitle():a.title;null!=q&&(a._collapsed_width=Math.min(a.size[0], +b.measureText(q).width+2*e.NODE_TITLE_HEIGHT),F[0]=a._collapsed_width,F[1]=0)}a.clip_area&&(b.save(),b.beginPath(),c==e.BOX_SHAPE?b.rect(0,0,F[0],F[1]):c==e.ROUND_SHAPE?b.roundRect(0,0,F[0],F[1],10):c==e.CIRCLE_SHAPE&&b.arc(0.5*F[0],0.5*F[1],0.5*F[0],0,2*Math.PI),b.clip());a.has_errors&&(f="red");this.drawNodeShape(a,b,F,d,f,a.is_selected,a.mouseOver);b.shadowColor="transparent";if(a.onDrawForeground)a.onDrawForeground(b,this,this.canvas);b.textAlign=l?"center":"left";b.font=this.inner_text_font; +f=!g;c=this.connecting_output;b.lineWidth=1;var q=0,z=new Float32Array(2);if(!a.flags.collapsed){if(a.inputs)for(d=0;dthis.ds.scale,q=a._shape||a.constructor.shape||e.ROUND_SHAPE,z=a.constructor.title_mode,w=!0;z== +e.TRANSPARENT_TITLE?w=!1:z==e.AUTOHIDE_TITLE&&c&&(w=!0);t[0]=0;t[1]=w?-g:0;t[2]=d[0]+1;t[3]=w?d[1]+g:d[1];c=b.globalAlpha;b.beginPath();q==e.BOX_SHAPE||l?b.fillRect(t[0],t[1],t[2],t[3]):q==e.ROUND_SHAPE||q==e.CARD_SHAPE?b.roundRect(t[0],t[1],t[2],t[3],this.round_radius,q==e.CARD_SHAPE?0:this.round_radius):q==e.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0],0,2*Math.PI);b.fill();a.flags.collapsed||(b.shadowColor="transparent",b.fillStyle="rgba(0,0,0,0.2)",b.fillRect(0,-1,t[2],2));b.shadowColor="transparent"; +if(a.onDrawBackground)a.onDrawBackground(b,this,this.canvas,this.graph_mouse);if(w||z==e.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,g,d,this.ds.scale,f);else if(z!=e.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){w=a.constructor.title_color||f;a.flags.collapsed&&(b.shadowColor=e.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var u=h.gradients[w];u||(u=h.gradients[w]=b.createLinearGradient(0,0,400,0),u.addColorStop(0,w),u.addColorStop(1,"#000"));b.fillStyle= +u}else b.fillStyle=w;b.beginPath();q==e.BOX_SHAPE||l?b.rect(0,-g,d[0]+1,g):q!=e.ROUND_SHAPE&&q!=e.CARD_SHAPE||b.roundRect(0,-g,d[0]+1,g,this.round_radius,a.flags.collapsed?this.round_radius:0);b.fill();b.shadowColor="transparent"}if(a.onDrawTitleBox)a.onDrawTitleBox(b,g,d,this.ds.scale);else q==e.ROUND_SHAPE||q==e.CIRCLE_SHAPE||q==e.CARD_SHAPE?(l&&(b.fillStyle="black",b.beginPath(),b.arc(0.5*g,-0.5*g,6,0,2*Math.PI),b.fill()),b.fillStyle=a.boxcolor||e.NODE_DEFAULT_BOXCOLOR,l?b.fillRect(0.5*g-5,-0.5* +g-5,10,10):(b.beginPath(),b.arc(0.5*g,-0.5*g,5,0,2*Math.PI),b.fill())):(l&&(b.fillStyle="black",b.fillRect(0.5*(g-10)-1,-0.5*(g+10)-1,12,12)),b.fillStyle=a.boxcolor||e.NODE_DEFAULT_BOXCOLOR,b.fillRect(0.5*(g-10),-0.5*(g+10),10,10));b.globalAlpha=c;if(a.onDrawTitleText)a.onDrawTitleText(b,g,d,this.ds.scale,this.title_text_font,r);!l&&(b.font=this.title_text_font,c=String(a.getTitle()))&&(b.fillStyle=r?"white":a.constructor.title_text_color||this.node_title_color,a.flags.collapsed?(b.textAlign="left", +b.measureText(c),b.fillText(c.substr(0,20),g,e.NODE_TITLE_TEXT_Y-g),b.textAlign="left"):(b.textAlign="left",b.fillText(c,g,e.NODE_TITLE_TEXT_Y-g)));a.flags.collapsed||!a.subgraph||a.skip_subgraph_button||(c=e.NODE_TITLE_HEIGHT,w=a.size[0]-c,u=e.isInsideRectangle(this.graph_mouse[0]-a.pos[0],this.graph_mouse[1]-a.pos[1],w+2,-c+2,c-4,c-4),b.fillStyle=u?"#888":"#555",q==e.BOX_SHAPE||l?b.fillRect(w+2,-c+2,c-4,c-4):(b.beginPath(),b.roundRect(w+2,-c+2,c-4,c-4,4),b.fill()),b.fillStyle="#333",b.beginPath(), +b.moveTo(w+0.2*c,0.6*-c),b.lineTo(w+0.8*c,0.6*-c),b.lineTo(w+0.5*c,0.3*-c),b.fill());if(a.onDrawTitle)a.onDrawTitle(b)}if(r){if(a.onBounding)a.onBounding(t);z==e.TRANSPARENT_TITLE&&(t[1]-=g,t[3]+=g);b.lineWidth=1;b.globalAlpha=0.8;b.beginPath();q==e.BOX_SHAPE?b.rect(-6+t[0],-6+t[1],12+t[2],12+t[3]):q==e.ROUND_SHAPE||q==e.CARD_SHAPE&&a.flags.collapsed?b.roundRect(-6+t[0],-6+t[1],12+t[2],12+t[3],2*this.round_radius):q==e.CARD_SHAPE?b.roundRect(-6+t[0],-6+t[1],12+t[2],12+t[3],2*this.round_radius,2): +q==e.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0]+6,0,2*Math.PI);b.strokeStyle="#FFF";b.stroke();b.strokeStyle=f;b.globalAlpha=1}};var H=new Float32Array(4),p=new Float32Array(4),s=new Float32Array(2),l=new Float32Array(2);h.prototype.drawConnections=function(a){var b=e.getTime(),d=this.visible_area;H[0]=d[0]-20;H[1]=d[1]-20;H[2]=d[2]+40;H[3]=d[3]+40;a.lineWidth=this.connections_width;a.fillStyle="#AAA";a.strokeStyle="#AAA";a.globalAlpha=this.editor_alpha;for(var d=this.graph._nodes,f=0,g=d.length;f< +g;++f){var r=d[f];if(r.inputs&&r.inputs.length)for(var c=0;cp[2]&&(p[0]+=p[2],p[2]=Math.abs(p[2]));0>p[3]&&(p[1]+=p[3],p[3]=Math.abs(p[3]));if(C(p,H)){var A=q.outputs[z],z=r.inputs[c];if(A&& +z&&(q=A.dir||(q.horizontal?e.DOWN:e.RIGHT),z=z.dir||(r.horizontal?e.UP:e.LEFT),this.renderLink(a,w,u,h,!1,0,null,q,z),h&&h._last_time&&1E3>b-h._last_time)){var A=2-0.002*(b-h._last_time),R=a.globalAlpha;a.globalAlpha=R*A;this.renderLink(a,w,u,h,!0,A,"white",q,z);a.globalAlpha=R}}}}}}a.globalAlpha=1};h.prototype.renderLink=function(a,b,d,f,g,r,c,l,q,z){f&&this.visible_links.push(f);!c&&f&&(c=f.color||h.link_type_colors[f.type]);c||(c=this.default_link_color);null!=f&&this.highlighted_links[f.id]&& +(c="#FFF");l=l||e.RIGHT;q=q||e.LEFT;var w=B(b,d);this.render_connections_border&&0.6b[1]?0:Math.PI,a.save(),a.translate(u[0],u[1]),a.rotate(p),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore(),a.save(),a.translate(z[0],z[1]),a.rotate(A), +a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(g[0],g[1],5,0,2*Math.PI),a.fill());if(r)for(a.fillStyle=c,u=0;5>u;++u)r=(0.001*e.getTime()+0.2*u)%1,g=this.computeConnectionPoint(b,d,r,l,q),a.beginPath(),a.arc(g[0],g[1],5,0,2*Math.PI),a.fill()};h.prototype.computeConnectionPoint=function(a,b,d,f,g){f=f||e.RIGHT;g=g||e.LEFT;var r=B(a,b),c=[a[0],a[1]],l=[b[0],b[1]];switch(f){case e.LEFT:c[0]+=-0.25*r;break;case e.RIGHT:c[0]+=0.25*r;break;case e.UP:c[1]+= +-0.25*r;break;case e.DOWN:c[1]+=0.25*r}switch(g){case e.LEFT:l[0]+=-0.25*r;break;case e.RIGHT:l[0]+=0.25*r;break;case e.UP:l[1]+=-0.25*r;break;case e.DOWN:l[1]+=0.25*r}f=(1-d)*(1-d)*(1-d);g=3*(1-d)*(1-d)*d;r=3*(1-d)*d*d;d*=d*d;return[f*a[0]+g*c[0]+r*l[0]+d*b[0],f*a[1]+g*c[1]+r*l[1]+d*b[1]]};h.prototype.drawExecutionOrder=function(a){a.shadowColor="transparent";a.globalAlpha=0.25;a.textAlign="center";a.strokeStyle="white";a.globalAlpha=0.75;for(var b=this.visible_nodes,d=0;dc||c>l-12||xu.last_y+h)){f=u.value;switch(u.type){case "button":if("mousemove"===d.type)break;u.callback&&setTimeout(function(){u.callback(u,q,a,b,d)},20);this.dirty_canvas=u.clicked=!0;break;case "slider":z=Math.clamp((c-10)/(l-20),0,1);u.value=u.options.min+(u.options.max-u.options.min)*z;u.callback&&setTimeout(function(){g(u,u.value)},20);this.dirty_canvas= +!0;break;case "number":case "combo":f=u.value;if("mousemove"==d.type&&"number"==u.type)u.value+=0.1*d.deltaX*(u.options.step||1),null!=u.options.min&&u.valueu.options.max&&(u.value=u.options.max);else if("mousedown"==d.type){var p=u.options.values;p&&p.constructor===Function&&(p=u.options.values(u,a));var A=null;"number"!=u.type&&(A=p.constructor===Array?p:Object.keys(p));c=40>c?-1:c>l-40?1:0;if("number"==u.type)u.value+=0.1*c*(u.options.step|| +1),null!=u.options.min&&u.valueu.options.max&&(u.value=u.options.max);else if(c)z=-1,this.last_mouseclick=0,z=p.constructor===Object?A.indexOf(String(u.value))+c:A.indexOf(u.value)+c,z>=A.length&&(z=A.length-1),0>z&&(z=0),u.value=p.constructor===Array?p[z]:z;else{var s=p!=A?Object.values(p):p;new e.ContextMenu(s,{scale:Math.max(1,this.ds.scale),event:d,className:"dark",callback:k.bind(u)},z);var k=function(a,b,d){p!=A&&(a=s.indexOf(a)); +this.value=a;g(this,a);q.dirty_canvas=!0;return!1}}}else"mouseup"==d.type&&"number"==u.type&&(c=40>c?-1:c>l-40?1:0,200>d.click_time&&0==c&&this.prompt("Value",u.value,function(a){this.value=Number(a);g(this,this.value)}.bind(u),d));f!=u.value&&setTimeout(function(){g(this,this.value)}.bind(u),20);this.dirty_canvas=!0;break;case "toggle":"mousedown"==d.type&&(u.value=!u.value,setTimeout(function(){g(u,u.value)},20));break;case "string":case "text":"mousedown"==d.type&&this.prompt("Value",u.value,function(a){this.value= +a;g(this,a)}.bind(u),d);break;default:u.mouse&&(this.dirty_canvas=u.mouse(d,[c,x],a))}f!=u.value&&a.graph._version++;return u}}}return null};h.prototype.drawGroups=function(a,b){if(this.graph){var d=this.graph._groups;b.save();b.globalAlpha=0.5*this.editor_alpha;for(var f=0;fd&&0.01>b.editor_alpha&&(clearInterval(f),1>d&&(b.live_mode=!0));1"+q+""+a+"",value:q})}if(p.length)return new e.ContextMenu(p,{event:d,callback:c,parentMenu:f,allow_html:!0,node:g},b),!1}};h.decodeHTML=function(a){var b=document.createElement("div");b.innerText=a;return b.innerHTML};h.onResizeNode=function(a,b,d,f,g){if(g){g.size=g.computeSize();if(g.onResize)g.onResize(g.size);g.setDirtyCanvas(!0,!0)}};h.prototype.showLinkMenu=function(a,b){var d=this;console.log(a); +var f=new e.ContextMenu(["Add Node",null,"Delete"],{event:b,title:null!=a.data?a.data.constructor.name:null,callback:function(b,c,e){switch(b){case "Add Node":h.onMenuAdd(null,null,e,f,function(b){console.log("node autoconnect");var g=d.graph.getNodeById(a.origin_id),f=d.graph.getNodeById(a.target_id);b.inputs&&b.inputs.length&&b.outputs&&b.outputs.length&&g.outputs[a.origin_slot].type==b.inputs[0].type&&b.outputs[0].type==f.inputs[0].type&&(g.connect(a.origin_slot,b,0),b.connect(0,f,a.target_slot), +b.pos[0]-=0.5*b.size[0])});break;case "Delete":d.graph.removeLink(a.id)}}});return!1};h.onShowPropertyEditor=function(a,b,d,f,g){function c(){var b=q.value;"Number"==a.type?b=Number(b):"Boolean"==a.type&&(b=Boolean(b));g[e]=b;l.parentNode&&l.parentNode.removeChild(l);g.setDirtyCanvas(!0,!0)}var e=a.property||"title";b=g[e];var l=document.createElement("div");l.className="graphdialog";l.innerHTML="";l.querySelector(".name").innerText= +e;var q=l.querySelector("input");q&&(q.value=b,q.addEventListener("blur",function(a){this.focus()}),q.addEventListener("keydown",function(a){13==a.keyCode&&(c(),a.preventDefault(),a.stopPropagation())}));b=h.active_canvas.canvas;d=b.getBoundingClientRect();var z=f=-20;d&&(f-=d.left,z-=d.top);event?(l.style.left=event.clientX+f+"px",l.style.top=event.clientY+z+"px"):(l.style.left=0.5*b.width+f+"px",l.style.top=0.5*b.height+z+"px");l.querySelector("button").addEventListener("click",c);b.parentNode.appendChild(l)}; +h.prototype.prompt=function(a,b,d,f){var g=this;a=a||"";var c=!1,e=document.createElement("div");e.className="graphdialog rounded";e.innerHTML=" ";e.close=function(){g.prompt_box=null;e.parentNode&&e.parentNode.removeChild(e)};1h.search_limit)break}}x=null;if(Array.prototype.filter)x=Object.keys(e.registered_node_types).filter(T);else for(l in x=[],e.registered_node_types)T(l)&&x.push(l);for(l=0;lh.search_limit);l++);var T=function(a){var b= +e.registered_node_types[a];return q&&b.filter!=q?!1:-1!==a.toLowerCase().indexOf(d)}}}var g=this,c=h.active_canvas,l=c.canvas,p=l.ownerDocument||document,q=document.createElement("div");q.className="litegraph litesearchbox graphdialog rounded";q.innerHTML="Search
";q.close=function(){g.search_box=null;p.body.focus();p.body.style.overflow="";setTimeout(function(){g.canvas.focus()},20);q.parentNode&& +q.parentNode.removeChild(q)};var z=null;1 +l.height-200&&(A.style.maxHeight=l.height-a.layerY-20+"px");n.focus();return q};h.prototype.showEditPropertyValue=function(a,b,d){function f(){g(u.value)}function g(g){c&&c.values&&c.values.constructor===Object&&void 0!=c.values[g]&&(g=c.values[g]);"number"==typeof a.properties[b]&&(g=Number(g));if("array"==e||"object"==e)g=JSON.parse(g);a.properties[b]=g;a.graph&&a.graph._version++;if(a.onPropertyChanged)a.onPropertyChanged(b,g);if(d.onclose)d.onclose();h.close();a.setDirtyCanvas(!0,!0)}if(a&&void 0!== +a.properties[b]){d=d||{};var c=a.getPropertyInfo(b),e=c.type,l="";if("string"==e||"number"==e||"array"==e||"object"==e)l="";else if("enum"!=e&&"combo"!=e||!c.values)if("boolean"==e)l="";else{console.warn("unknown type: "+e);return}else{var l=""}var h=this.createDialog(""+b+""+l+"",d);if("enum"!=e&&"combo"!=e||!c.values)if("boolean"==e)(u=h.querySelector("input"))&&u.addEventListener("click",function(a){g(!!u.checked)});else{if(u=h.querySelector("input"))u.addEventListener("blur",function(a){this.focus()}),p=void 0!==a.properties[b]?a.properties[b]:"",p=JSON.stringify(p),u.value=p,u.addEventListener("keydown", +function(a){13==a.keyCode&&(f(),a.preventDefault(),a.stopPropagation())})}else{var u=h.querySelector("select");u.addEventListener("change",function(a){g(a.target.value)})}h.querySelector("button").addEventListener("click",f);return h}};h.prototype.createDialog=function(a,b){b=b||{};var d=document.createElement("div");d.className="graphdialog";d.innerHTML=a;var f=this.canvas.getBoundingClientRect(),g=-20,c=-20;f&&(g-=f.left,c-=f.top);b.position?(g+=b.position[0],c+=b.position[1]):b.event?(g+=b.event.clientX, +c+=b.event.clientY):(g+=0.5*this.canvas.width,c+=0.5*this.canvas.height);d.style.left=g+"px";d.style.top=c+"px";this.canvas.parentNode.appendChild(d);d.close=function(){this.parentNode&&this.parentNode.removeChild(this)};return d};h.prototype.createPanel=function(a,b){b=b||{};var d=b.window||window,f=document.createElement("div");f.className="litegraph dialog";f.innerHTML="
"; f.header=f.querySelector(".dialog-header");b.width&&(f.style.width=b.width+(b.width.constructor===Number?"px":""));b.height&&(f.style.height=b.height+(b.height.constructor===Number?"px":""));if(b.closable){var g=document.createElement("span");g.innerHTML="✕";g.classList.add("close");g.addEventListener("click",function(){f.close()});f.header.appendChild(g)}f.title_element=f.querySelector(".dialog-title");f.title_element.innerText=a;f.content=f.querySelector(".dialog-content");f.footer=f.querySelector(".dialog-footer"); f.close=function(){this.parentNode.removeChild(this)};f.clear=function(){this.content.innerHTML=""};f.addHTML=function(a,b,d){var g=document.createElement("div");b&&(g.className=b);g.innerHTML=a;d?f.footer.appendChild(g):f.content.appendChild(g);return g};f.addButton=function(a,b,d){var g=document.createElement("button");g.innerText=a;g.options=d;g.classList.add("btn");g.addEventListener("click",b);f.footer.appendChild(g);return g};f.addSeparator=function(){var a=document.createElement("div");a.className= -"separator";f.content.appendChild(a)};f.addWidget=function(a,b,g,c,l){function p(a,b){console.log("change",a,b);c.callback&&c.callback(a,b);l&&l(a,b)}c=c||{};var v=String(g);"number"==a&&(v=g.toFixed(3));var r=document.createElement("div");r.className="property";r.innerHTML="";r.querySelector(".property_name").innerText=b;var h=r.querySelector(".property_value");h.innerText=v;r.dataset.property=b;r.dataset.type=c.type||a;r.options= -c;r.value=g;"boolean"==a?(r.classList.add("boolean"),g&&r.classList.add("bool-on"),r.addEventListener("click",function(){var a=this.dataset.property;this.value=!this.value;this.classList.toggle("bool-on");this.querySelector(".property_value").innerText=this.value?"true":"false";p(a,this.value)})):"string"==a||"number"==a?(h.setAttribute("contenteditable",!0),h.addEventListener("keydown",function(a){"Enter"==a.code&&(a.preventDefault(),this.blur())}),h.addEventListener("blur",function(){var a=this.innerText, -b=this.parentNode.dataset.property;"number"==this.parentNode.dataset.type&&(a=Number(a));p(b,a)})):"enum"==a&&h.addEventListener("click",function(a){var b=this.parentNode.dataset.property,g=this;new e.ContextMenu(c.values||[],{event:a,className:"dark",callback:function(a,d,f){g.innerText=a;p(b,a);return!1}},d)});f.content.appendChild(r);return r};return f};h.prototype.showShowNodePanel=function(a){window.SELECTED_NODE=a;var b=document.querySelector("#node-panel");b&&b.close();var d=this.getCanvasWindow(), -b=this.createPanel(a.title||"",{closable:!0,window:d});b.id="node-panel";b.node=a;b.classList.add("settings");var f=this;(function(){b.content.innerHTML="";b.addHTML(""+a.type+""+(a.constructor.desc||"")+"");b.addHTML("

Properties

");for(var d in a.properties){var e=a.properties[d],c=a.getPropertyInfo(d);a.onAddPropertyToPanel&&a.onAddPropertyToPanel(d,b)||b.addWidget(c.widget||c.type,d,e,c,function(b, -d){a.setProperty(b,d);f.dirty_canvas=!0})}b.addSeparator();if(a.onShowCustomPanelInfo)a.onShowCustomPanelInfo(b);b.addButton("Delete",function(){a.block_delete||(a.graph.remove(a),b.close())}).classList.add("delete")})();this.canvas.parentNode.appendChild(b)};h.prototype.showSubgraphPropertiesDialog=function(a){function b(){f.clear();if(a.inputs)for(var d=0;d", -"subgraph_property");c.dataset.name=e.name;c.dataset.slot=d;c.querySelector(".name").innerText=e.name;c.querySelector(".type").innerText=e.type;c.querySelector("button").addEventListener("click",function(d){a.removeInput(Number(this.parentNode.dataset.slot));b()})}}}console.log("showing subgraph properties dialog");var d=this.canvas.parentNode.querySelector(".subgraph_dialog");d&&d.close();var f=this.createPanel("Subgraph Inputs",{closable:!0,width:500});f.node=a;f.classList.add("subgraph_dialog"); -f.addHTML(" + NameType","subgraph_property extra",!0).querySelector("button").addEventListener("click",function(d){d=this.parentNode;var f=d.querySelector(".name").value,e=d.querySelector(".type").value;f&&-1==a.findInputSlot(f)&&(a.addInput(f,e),d.querySelector(".name").value="",d.querySelector(".type").value="",b())});b();this.canvas.parentNode.appendChild(f);return f};h.prototype.checkPanels= -function(){if(this.canvas)for(var a=this.canvas.parentNode.querySelectorAll(".litegraph.dialog"),b=0;bNo color"});for(var c in h.node_colors)a=h.node_colors[c],a={value:c,content:""+c+""},b.push(a);new e.ContextMenu(b, -{event:d,callback:function(a){g&&((a=a.value?h.node_colors[a.value]:null)?g.constructor===e.LGraphGroup?g.color=a.groupcolor:(g.color=a.color,g.bgcolor=a.bgcolor):(delete g.color,delete g.bgcolor),g.setDirtyCanvas(!0,!0))},parentMenu:f,node:g});return!1};h.onMenuNodeShapes=function(a,b,d,f,g){if(!g)throw"no node passed";new e.ContextMenu(e.VALID_SHAPES,{event:d,callback:function(a){g&&(g.shape=a,g.setDirtyCanvas(!0))},parentMenu:f,node:g});return!1};h.onMenuNodeRemove=function(a,b,d,f,g){if(!g)throw"no node passed"; -!1!==g.removable&&(g.graph.remove(g),g.setDirtyCanvas(!0,!0))};h.onMenuNodeToSubgraph=function(a,b,d,f,g){a=g.graph;if(b=h.active_canvas)d=Object.values(b.selected_nodes||{}),d.length||(d=[g]),f=e.createNode("graph/subgraph"),f.pos=g.pos.concat(),a.add(f),f.buildFromNodes(d),b.deselectAllNodes(),g.setDirtyCanvas(!0,!0)};h.onMenuNodeClone=function(a,b,d,f,g){!1!=g.clonable&&(a=g.clone())&&(a.pos=[g.pos[0]+5,g.pos[1]+5],g.graph.add(a),g.setDirtyCanvas(!0,!0))};h.node_colors={red:{color:"#322",bgcolor:"#533", -groupcolor:"#A88"},brown:{color:"#332922",bgcolor:"#593930",groupcolor:"#b06634"},green:{color:"#232",bgcolor:"#353",groupcolor:"#8A8"},blue:{color:"#223",bgcolor:"#335",groupcolor:"#88A"},pale_blue:{color:"#2a363b",bgcolor:"#3f5159",groupcolor:"#3f789e"},cyan:{color:"#233",bgcolor:"#355",groupcolor:"#8AA"},purple:{color:"#323",bgcolor:"#535",groupcolor:"#a1309b"},yellow:{color:"#432",bgcolor:"#653",groupcolor:"#b58b2a"},black:{color:"#222",bgcolor:"#000",groupcolor:"#444"}};h.prototype.getCanvasMenuOptions= -function(){var a=null;this.getMenuOptions?a=this.getMenuOptions():(a=[{content:"Add Node",has_submenu:!0,callback:h.onMenuAdd},{content:"Add Group",callback:h.onGroupAdd}],this._graph_stack&&0Name",g),s=c.querySelector("input");s&&e&&(s.value=e.label||"");c.querySelector("button").addEventListener("click", -function(a){s.value&&(e&&(e.label=s.value),d.setDirty(!0));c.close()})}},extra:a};a&&(c.title=a.type);var l=null;a&&(l=a.getSlotInPosition(b.canvasX,b.canvasY),h.active_node=a);if(l){g=[];if(a.getSlotMenuOptions)g=a.getSlotMenuOptions(l);else{l&&l.output&&l.output.links&&l.output.links.length&&g.push({content:"Disconnect Links",slot:l});var p=l.input||l.output;g.push(p.locked?"Cannot remove":{content:"Remove Slot",slot:l});g.push(p.nameLocked?"Cannot rename":{content:"Rename Slot",slot:l})}c.title= -(l.input?l.input.type:l.output.type)||"*";l.input&&l.input.type==e.ACTION&&(c.title="Action");l.output&&l.output.type==e.EVENT&&(c.title="Event")}else a?g=this.getNodeMenuOptions(a):(g=this.getCanvasMenuOptions(),(l=this.graph.getGroupOnPos(b.canvasX,b.canvasY))&&g.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:l,options:this.getGroupMenuOptions(l)}}));g&&new e.ContextMenu(g,c,f)};"undefined"!=typeof window&&window.CanvasRenderingContext2D&&(window.CanvasRenderingContext2D.prototype.roundRect= -function(a,b,d,f,g,e){void 0===g&&(g=5);void 0===e&&(e=g);this.moveTo(a+g,b);this.lineTo(a+d-g,b);this.quadraticCurveTo(a+d,b,a+d,b+g);this.lineTo(a+d,b+f-e);this.quadraticCurveTo(a+d,b+f,a+d-e,b+f);this.lineTo(a+e,b+f);this.quadraticCurveTo(a,b+f,a,b+f-e);this.lineTo(a,b+g);this.quadraticCurveTo(a,b,a+g,b)});e.compareObjects=function(a,b){for(var d in a)if(a[d]!=b[d])return!1;return!0};e.distance=z;e.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+ -","+Math.round(255*a[2]).toFixed()+","+(4==a.length?a[3].toFixed(2):"1.0")+")"};e.isInsideRectangle=x;e.growBounding=function(a,b,d){ba[2]&&(a[2]=b);da[3]&&(a[3]=d)};e.isInsideBounding=function(a,b){return a[0]b[1][0]||a[1]>b[1][1]?!1:!0};e.overlapBounding=y;e.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),d=0,f,g,e=0;6>e;e+=2)f="0123456789ABCDEF".indexOf(a.charAt(e)),g="0123456789ABCDEF".indexOf(a.charAt(e+ -1)),b[d]=16*f+g,d++;return b};e.num2hex=function(a){for(var b="#",d,f,g=0;3>g;g++)d=a[g]/16,f=a[g]%16,b+="0123456789ABCDEF".charAt(d)+"0123456789ABCDEF".charAt(f);return b};F.prototype.addItem=function(a,b,d){function f(a){var b=this.value;b&&b.has_submenu&&g.call(this,a)}function g(a){var b=this.value,g=!0;e.current_submenu&&e.current_submenu.close(a);if(d.callback){var f=d.callback.call(this,b,d,a,e,d.node);!0===f&&(g=!1)}if(b&&(b.callback&&!d.ignore_item_callbacks&&!0!==b.disabled&&(f=b.callback.call(this, -b,d,a,e,d.extra),!0===f&&(g=!1)),b.submenu)){if(!b.submenu.options)throw"ContextMenu submenu needs options";new e.constructor(b.submenu.options,{callback:b.submenu.callback,event:a,parentMenu:e,ignore_item_callbacks:b.submenu.ignore_item_callbacks,title:b.submenu.title,extra:b.submenu.extra,autoopen:d.autoopen});g=!1}g&&!e.lock&&e.close()}var e=this;d=d||{};var c=document.createElement("div");c.className="litemenu-entry submenu";var l=!1;if(null===b)c.classList.add("separator");else{c.innerHTML=b&& -b.title?b.title:a;if(c.value=b)b.disabled&&(l=!0,c.classList.add("disabled")),(b.submenu||b.has_submenu)&&c.classList.add("has_submenu");"function"==typeof b?(c.dataset.value=a,c.onclick_callback=b):c.dataset.value=b;b.className&&(c.className+=" "+b.className)}this.root.appendChild(c);l||c.addEventListener("click",g);d.autoopen&&c.addEventListener("mouseenter",f);return c};F.prototype.close=function(a,b){this.root.parentNode&&this.root.parentNode.removeChild(this.root);this.parentMenu&&!b&&(this.parentMenu.lock= -!1,this.parentMenu.current_submenu=null,void 0===a?this.parentMenu.close():a&&!F.isCursorOverElement(a,this.parentMenu.root)&&F.trigger(this.parentMenu.root,"mouseleave",a));this.current_submenu&&this.current_submenu.close(a,!0);this.root.closing_timer&&clearTimeout(this.root.closing_timer)};F.trigger=function(a,b,d,f){var g=document.createEvent("CustomEvent");g.initCustomEvent(b,!0,!0,d);g.srcElement=f;a.dispatchEvent?a.dispatchEvent(g):a.__events&&a.__events.dispatchEvent(g);return g};F.prototype.getTopMenu= -function(){return this.options.parentMenu?this.options.parentMenu.getTopMenu():this};F.prototype.getFirstEvent=function(){return this.options.parentMenu?this.options.parentMenu.getFirstEvent():this.options.event};F.isCursorOverElement=function(a,b){var d=a.clientX,f=a.clientY,g=b.getBoundingClientRect();return g?f>g.top&&fg.left&&d";A.querySelector(".property_name").innerText=b;var s=A.querySelector(".property_value");s.innerText=u;A.dataset.property=b;A.dataset.type=c.type||a;A.options= +c;A.value=g;if("boolean"==a)A.classList.add("boolean"),g&&A.classList.add("bool-on"),A.addEventListener("click",function(){var a=this.dataset.property;this.value=!this.value;this.classList.toggle("bool-on");this.querySelector(".property_value").innerText=this.value?"true":"false";p(a,this.value)});else if("string"==a||"number"==a)s.setAttribute("contenteditable",!0),s.addEventListener("keydown",function(a){"Enter"==a.code&&(a.preventDefault(),this.blur())}),s.addEventListener("blur",function(){var a= +this.innerText,b=this.parentNode.dataset.property;"number"==this.parentNode.dataset.type&&(a=Number(a));p(b,a)});else if("enum"==a||"combo"==a)u=h.getPropertyPrintableValue(g,c.values);s.innerText=u;s.addEventListener("click",function(a){var b=this.parentNode.dataset.property,g=this;new e.ContextMenu(c.values||[],{event:a,className:"dark",callback:function(a,d,f){g.innerText=a;p(b,a);return!1}},d)});f.content.appendChild(A);return A};return f};h.getPropertyPrintableValue=function(a,b){if(!b||b.constructor=== +Array)return String(a);if(b.constructor===Object){var d="",f;for(f in b)if(b[f]==a){d=f;break}return String(a)+" ("+d+")"}};h.prototype.showShowNodePanel=function(a){window.SELECTED_NODE=a;var b=document.querySelector("#node-panel");b&&b.close();var d=this.getCanvasWindow(),b=this.createPanel(a.title||"",{closable:!0,window:d});b.id="node-panel";b.node=a;b.classList.add("settings");var f=this;(function(){b.content.innerHTML="";b.addHTML(""+a.type+""+ +(a.constructor.desc||"")+"");b.addHTML("

Properties

");for(var d in a.properties){var c=a.properties[d],e=a.getPropertyInfo(d);a.onAddPropertyToPanel&&a.onAddPropertyToPanel(d,b)||b.addWidget(e.widget||e.type,d,c,e,function(b,d){f.graph.beforeChange(a);a.setProperty(b,d);f.graph.afterChange();f.dirty_canvas=!0})}b.addSeparator();if(a.onShowCustomPanelInfo)a.onShowCustomPanelInfo(b);b.addButton("Delete",function(){a.block_delete||(a.graph.remove(a),b.close())}).classList.add("delete")})(); +this.canvas.parentNode.appendChild(b)};h.prototype.showSubgraphPropertiesDialog=function(a){function b(){f.clear();if(a.inputs)for(var d=0;d","subgraph_property");e.dataset.name=c.name;e.dataset.slot=d;e.querySelector(".name").innerText=c.name;e.querySelector(".type").innerText=c.type;e.querySelector("button").addEventListener("click", +function(d){a.removeInput(Number(this.parentNode.dataset.slot));b()})}}}console.log("showing subgraph properties dialog");var d=this.canvas.parentNode.querySelector(".subgraph_dialog");d&&d.close();var f=this.createPanel("Subgraph Inputs",{closable:!0,width:500});f.node=a;f.classList.add("subgraph_dialog");f.addHTML(" + NameType","subgraph_property extra",!0).querySelector("button").addEventListener("click", +function(d){d=this.parentNode;var f=d.querySelector(".name").value,c=d.querySelector(".type").value;f&&-1==a.findInputSlot(f)&&(a.addInput(f,c),d.querySelector(".name").value="",d.querySelector(".type").value="",b())});b();this.canvas.parentNode.appendChild(f);return f};h.prototype.checkPanels=function(){if(this.canvas)for(var a=this.canvas.parentNode.querySelectorAll(".litegraph.dialog"),b=0;bNo color"});for(var c in h.node_colors)a=h.node_colors[c],a={value:c,content:""+c+""},b.push(a);new e.ContextMenu(b,{event:d,callback:function(a){g&&((a=a.value?h.node_colors[a.value]:null)?g.constructor===e.LGraphGroup?g.color=a.groupcolor:(g.color=a.color,g.bgcolor=a.bgcolor): +(delete g.color,delete g.bgcolor),g.setDirtyCanvas(!0,!0))},parentMenu:f,node:g});return!1};h.onMenuNodeShapes=function(a,b,d,f,g){if(!g)throw"no node passed";new e.ContextMenu(e.VALID_SHAPES,{event:d,callback:function(a){g&&(g.graph.beforeChange(g),g.shape=a,g.graph.afterChange(g),g.setDirtyCanvas(!0))},parentMenu:f,node:g});return!1};h.onMenuNodeRemove=function(a,b,d,f,g){if(!g)throw"no node passed";!1!==g.removable&&(a=g.graph,a.beforeChange(),a.remove(g),a.afterChange(),g.setDirtyCanvas(!0,!0))}; +h.onMenuNodeToSubgraph=function(a,b,d,f,g){a=g.graph;if(b=h.active_canvas)d=Object.values(b.selected_nodes||{}),d.length||(d=[g]),f=e.createNode("graph/subgraph"),f.pos=g.pos.concat(),a.add(f),f.buildFromNodes(d),b.deselectAllNodes(),g.setDirtyCanvas(!0,!0)};h.onMenuNodeClone=function(a,b,d,f,g){!1!=g.clonable&&(a=g.clone())&&(a.pos=[g.pos[0]+5,g.pos[1]+5],g.graph.beforeChange(),g.graph.add(a),g.graph.afterChange(),g.setDirtyCanvas(!0,!0))};h.node_colors={red:{color:"#322",bgcolor:"#533",groupcolor:"#A88"}, +brown:{color:"#332922",bgcolor:"#593930",groupcolor:"#b06634"},green:{color:"#232",bgcolor:"#353",groupcolor:"#8A8"},blue:{color:"#223",bgcolor:"#335",groupcolor:"#88A"},pale_blue:{color:"#2a363b",bgcolor:"#3f5159",groupcolor:"#3f789e"},cyan:{color:"#233",bgcolor:"#355",groupcolor:"#8AA"},purple:{color:"#323",bgcolor:"#535",groupcolor:"#a1309b"},yellow:{color:"#432",bgcolor:"#653",groupcolor:"#b58b2a"},black:{color:"#222",bgcolor:"#000",groupcolor:"#444"}};h.prototype.getCanvasMenuOptions=function(){var a= +null;this.getMenuOptions?a=this.getMenuOptions():(a=[{content:"Add Node",has_submenu:!0,callback:h.onMenuAdd},{content:"Add Group",callback:h.onGroupAdd}],this._graph_stack&&0Name",g),l=e.querySelector("input");l&&c&&(l.value=c.label||"");e.querySelector("button").addEventListener("click",function(a){l.value&& +(c&&(c.label=l.value),d.setDirty(!0));e.close()})}},extra:a};a&&(c.title=a.type);var l=null;a&&(l=a.getSlotInPosition(b.canvasX,b.canvasY),h.active_node=a);if(l){g=[];if(a.getSlotMenuOptions)g=a.getSlotMenuOptions(l);else{l&&l.output&&l.output.links&&l.output.links.length&&g.push({content:"Disconnect Links",slot:l});var p=l.input||l.output;g.push(p.locked?"Cannot remove":{content:"Remove Slot",slot:l});g.push(p.nameLocked?"Cannot rename":{content:"Rename Slot",slot:l})}c.title=(l.input?l.input.type: +l.output.type)||"*";l.input&&l.input.type==e.ACTION&&(c.title="Action");l.output&&l.output.type==e.EVENT&&(c.title="Event")}else a?g=this.getNodeMenuOptions(a):(g=this.getCanvasMenuOptions(),(l=this.graph.getGroupOnPos(b.canvasX,b.canvasY))&&g.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:l,options:this.getGroupMenuOptions(l)}}));g&&new e.ContextMenu(g,c,f)};"undefined"!=typeof window&&window.CanvasRenderingContext2D&&(window.CanvasRenderingContext2D.prototype.roundRect= +function(a,b,d,f,g,c){void 0===g&&(g=5);void 0===c&&(c=g);this.moveTo(a+g,b);this.lineTo(a+d-g,b);this.quadraticCurveTo(a+d,b,a+d,b+g);this.lineTo(a+d,b+f-c);this.quadraticCurveTo(a+d,b+f,a+d-c,b+f);this.lineTo(a+c,b+f);this.quadraticCurveTo(a,b+f,a,b+f-c);this.lineTo(a,b+g);this.quadraticCurveTo(a,b,a+g,b)});e.compareObjects=function(a,b){for(var d in a)if(a[d]!=b[d])return!1;return!0};e.distance=B;e.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+ +","+Math.round(255*a[2]).toFixed()+","+(4==a.length?a[3].toFixed(2):"1.0")+")"};e.isInsideRectangle=y;e.growBounding=function(a,b,d){ba[2]&&(a[2]=b);da[3]&&(a[3]=d)};e.isInsideBounding=function(a,b){return a[0]b[1][0]||a[1]>b[1][1]?!1:!0};e.overlapBounding=C;e.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),d=0,f,g,c=0;6>c;c+=2)f="0123456789ABCDEF".indexOf(a.charAt(c)),g="0123456789ABCDEF".indexOf(a.charAt(c+ +1)),b[d]=16*f+g,d++;return b};e.num2hex=function(a){for(var b="#",d,f,g=0;3>g;g++)d=a[g]/16,f=a[g]%16,b+="0123456789ABCDEF".charAt(d)+"0123456789ABCDEF".charAt(f);return b};G.prototype.addItem=function(a,b,d){function f(a){var b=this.value;b&&b.has_submenu&&g.call(this,a)}function g(a){var b=this.value,g=!0;c.current_submenu&&c.current_submenu.close(a);if(d.callback){var f=d.callback.call(this,b,d,a,c,d.node);!0===f&&(g=!1)}if(b&&(b.callback&&!d.ignore_item_callbacks&&!0!==b.disabled&&(f=b.callback.call(this, +b,d,a,c,d.extra),!0===f&&(g=!1)),b.submenu)){if(!b.submenu.options)throw"ContextMenu submenu needs options";new c.constructor(b.submenu.options,{callback:b.submenu.callback,event:a,parentMenu:c,ignore_item_callbacks:b.submenu.ignore_item_callbacks,title:b.submenu.title,extra:b.submenu.extra,autoopen:d.autoopen});g=!1}g&&!c.lock&&c.close()}var c=this;d=d||{};var e=document.createElement("div");e.className="litemenu-entry submenu";var l=!1;if(null===b)e.classList.add("separator");else{e.innerHTML=b&& +b.title?b.title:a;if(e.value=b)b.disabled&&(l=!0,e.classList.add("disabled")),(b.submenu||b.has_submenu)&&e.classList.add("has_submenu");"function"==typeof b?(e.dataset.value=a,e.onclick_callback=b):e.dataset.value=b;b.className&&(e.className+=" "+b.className)}this.root.appendChild(e);l||e.addEventListener("click",g);d.autoopen&&e.addEventListener("mouseenter",f);return e};G.prototype.close=function(a,b){this.root.parentNode&&this.root.parentNode.removeChild(this.root);this.parentMenu&&!b&&(this.parentMenu.lock= +!1,this.parentMenu.current_submenu=null,void 0===a?this.parentMenu.close():a&&!G.isCursorOverElement(a,this.parentMenu.root)&&G.trigger(this.parentMenu.root,"mouseleave",a));this.current_submenu&&this.current_submenu.close(a,!0);this.root.closing_timer&&clearTimeout(this.root.closing_timer)};G.trigger=function(a,b,d,f){var g=document.createEvent("CustomEvent");g.initCustomEvent(b,!0,!0,d);g.srcElement=f;a.dispatchEvent?a.dispatchEvent(g):a.__events&&a.__events.dispatchEvent(g);return g};G.prototype.getTopMenu= +function(){return this.options.parentMenu?this.options.parentMenu.getTopMenu():this};G.prototype.getFirstEvent=function(){return this.options.parentMenu?this.options.parentMenu.getFirstEvent():this.options.event};G.isCursorOverElement=function(a,b){var d=a.clientX,f=a.clientY,g=b.getBoundingClientRect();return g?f>g.top&&fg.left&&dMath.abs(d))return f[1];d=(a-f[0])/d;return f[1]*(1-d)+g[1]*d}}return 0}};D.prototype.draw=function(a,b,d,f,g,e){if(d=this.points){this.size=b;var c=b[0]-2*this.margin;b=b[1]-2*this.margin;g=g||"#666";a.save();a.translate(this.margin,this.margin);f&&(a.fillStyle="#111",a.fillRect(0,0,c,b),a.fillStyle="#222",a.fillRect(0.5*c,0,1,b),a.strokeStyle="#333",a.strokeRect(0,0,c,b));a.strokeStyle= -g;e&&(a.globalAlpha=0.5);a.beginPath();for(f=0;fa[1])){var f=this.size[0]-2*this.margin,g=this.size[1]-2*this.margin,e=a[0]-this.margin,c=a[1]-this.margin;this.selected=this.getCloserPoint([e, -c],30/b.ds.scale);-1==this.selected&&(f=[e/f,1-c/g],d.push(f),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(f),this.must_update=!0);if(-1!=this.selected)return!0}};D.prototype.onMouseMove=function(a,b){var d=this.points;if(d){var f=this.selected;if(!(0>f)){var g=(a[0]-this.margin)/(this.size[0]-2*this.margin),e=(a[1]-this.margin)/(this.size[1]-2*this.margin);this._nearest=this.getCloserPoint([a[0]-this.margin,a[1]-this.margin],30/b.ds.scale);var c=d[f];if(c){var l=0==f||f==d.length- -1;!l&&(-10>a[0]||a[0]>this.size[0]+10||-10>a[1]||a[1]>this.size[1]+10)?(d.splice(f,1),this.selected=-1):(c[0]=l?0==f?0:1:Math.clamp(g,0,1),c[1]=1-Math.clamp(e,0,1),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(c),this.must_update=!0)}}}};D.prototype.onMouseUp=function(a,b){this.selected=-1;return!1};D.prototype.getCloserPoint=function(a,b){var d=this.points;if(!d)return-1;b=b||30;for(var f=this.size[0]-2*this.margin,g=this.size[1]-2*this.margin,e=d.length,c=[0,0],l=1E6,t=-1,p=0;p< -e;++p){var r=d[p];c[0]=r[0]*f;c[1]=(1-r[1])*g;r=vec2.distance(a,c);r>l||r>b||(t=p,l=r)}return t};e.CurveEditor=D;e.getParameterNames=function(a){return(a+"").replace(/[/][/].*$/gm,"").replace(/\s+/g,"").replace(/[/][*][^/*]*[*][/]/g,"").split("){",1)[0].replace(/^[^(]*[(]/,"").replace(/=[^,]+/g,"").split(",").filter(Boolean)};Math.clamp=function(a,b,d){return b>a?b:dMath.abs(d))return f[1];d=(a-f[0])/d;return f[1]*(1-d)+g[1]*d}}return 0}};E.prototype.draw=function(a,b,d,f,g,c){if(d=this.points){this.size=b;var e=b[0]-2*this.margin;b=b[1]-2*this.margin;g=g||"#666";a.save();a.translate(this.margin,this.margin);f&&(a.fillStyle="#111",a.fillRect(0,0,e,b),a.fillStyle="#222",a.fillRect(0.5*e,0,1,b),a.strokeStyle="#333",a.strokeRect(0,0,e,b));a.strokeStyle= +g;c&&(a.globalAlpha=0.5);a.beginPath();for(f=0;fa[1])){var f=this.size[0]-2*this.margin,g=this.size[1]-2*this.margin,c=a[0]-this.margin,e=a[1]-this.margin;this.selected=this.getCloserPoint([c, +e],30/b.ds.scale);-1==this.selected&&(f=[c/f,1-e/g],d.push(f),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(f),this.must_update=!0);if(-1!=this.selected)return!0}};E.prototype.onMouseMove=function(a,b){var d=this.points;if(d){var f=this.selected;if(!(0>f)){var g=(a[0]-this.margin)/(this.size[0]-2*this.margin),c=(a[1]-this.margin)/(this.size[1]-2*this.margin);this._nearest=this.getCloserPoint([a[0]-this.margin,a[1]-this.margin],30/b.ds.scale);var e=d[f];if(e){var l=0==f||f==d.length- +1;!l&&(-10>a[0]||a[0]>this.size[0]+10||-10>a[1]||a[1]>this.size[1]+10)?(d.splice(f,1),this.selected=-1):(e[0]=l?0==f?0:1:Math.clamp(g,0,1),e[1]=1-Math.clamp(c,0,1),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(e),this.must_update=!0)}}}};E.prototype.onMouseUp=function(a,b){this.selected=-1;return!1};E.prototype.getCloserPoint=function(a,b){var d=this.points;if(!d)return-1;b=b||30;for(var f=this.size[0]-2*this.margin,g=this.size[1]-2*this.margin,c=d.length,e=[0,0],l=1E6,q=-1,p=0;p< +c;++p){var h=d[p];e[0]=h[0]*f;e[1]=(1-h[1])*g;h=vec2.distance(a,e);h>l||h>b||(q=p,l=h)}return q};e.CurveEditor=E;e.getParameterNames=function(a){return(a+"").replace(/[/][/].*$/gm,"").replace(/\s+/g,"").replace(/[/][*][^/*]*[*][/]/g,"").split("){",1)[0].replace(/^[^(]*[(]/,"").replace(/=[^,]+/g,"").split(",").filter(Boolean)};Math.clamp=function(a,b,d){return b>a?b:d +this.subgraph.sendEventToAllNodes(a,b,d)};m.prototype.onDrawBackground=function(a,b,d,c){this.flags.collapsed||(b=this.size[1]-f.NODE_TITLE_HEIGHT+0.5,c=f.isInsideRectangle(c[0],c[1],this.pos[0],this.pos[1]+b,this.size[0],f.NODE_TITLE_HEIGHT),a.fillStyle=c?"#555":"#222",a.beginPath(),a.roundRect(0,b,this.size[0]+1,f.NODE_TITLE_HEIGHT,0,8),a.fill(),a.textAlign="center",a.font="24px Arial",a.fillStyle=c?"#DDD":"#999",a.fillText("+",0.5*this.size[0],b+24))};m.prototype.onMouseDown=function(a,b,d){b[1]> this.size[1]-f.NODE_TITLE_HEIGHT+0.5&&d.showSubgraphPropertiesDialog(this)};m.prototype.computeSize=function(){return[200,Math.max(this.inputs?this.inputs.length:0,this.outputs?this.outputs.length:0)*f.NODE_SLOT_HEIGHT+f.NODE_TITLE_HEIGHT]};m.prototype.onSubgraphTrigger=function(a,b){var d=this.findOutputSlot(a);-1!=d&&this.triggerSlot(d)};m.prototype.onSubgraphNewInput=function(a,b){-1==this.findInputSlot(a)&&this.addInput(a,b)};m.prototype.onSubgraphRenamedInput=function(a,b){var d=this.findInputSlot(a); -1!=d&&(this.getInputInfo(d).name=b)};m.prototype.onSubgraphTypeChangeInput=function(a,b){var d=this.findInputSlot(a);-1!=d&&(this.getInputInfo(d).type=b)};m.prototype.onSubgraphRemovedInput=function(a){a=this.findInputSlot(a);-1!=a&&this.removeInput(a)};m.prototype.onSubgraphNewOutput=function(a,b){-1==this.findOutputSlot(a)&&this.addOutput(a,b)};m.prototype.onSubgraphRenamedOutput=function(a,b){var d=this.findOutputSlot(a);-1!=d&&(this.getOutputInfo(d).name=b)};m.prototype.onSubgraphTypeChangeOutput= function(a,b){var d=this.findOutputSlot(a);-1!=d&&(this.getOutputInfo(d).type=b)};m.prototype.onSubgraphRemovedOutput=function(a){a=this.findInputSlot(a);-1!=a&&this.removeOutput(a)};m.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:"Open",callback:function(){a.openSubgraph(b.subgraph)}}]};m.prototype.onResize=function(a){a[1]+=20};m.prototype.serialize=function(){var a=f.LGraphNode.prototype.serialize.call(this);a.subgraph=this.subgraph.serialize();return a};m.prototype.clone= -function(){var a=f.createNode(this.type),b=this.serialize();delete b.id;delete b.inputs;delete b.outputs;a.configure(b);return a};m.prototype.buildFromNodes=function(a){for(var b={},d=0,f=0;f=e?this.trigger(null,h):this._pending.push([e,h])};h.prototype.onExecute=function(){var c=1E3* -this.graph.elapsed_time;this.isInputConnected(1)&&(this.properties.time_in_ms=this.getInputData(1));for(var h=0;h=e?this.trigger(null,h):this._pending.push([e,h])};h.prototype.onExecute=function(){var c=1E3* +this.graph.elapsed_time;this.isInputConnected(1)&&(this.properties.time_in_ms=this.getInputData(1));for(var h=0;hh[1]))return this.old_y=c.canvasY,this.captureInput(!0),this.mouse_captured=!0};n.prototype.onMouseMove=function(c){if(this.mouse_captured){var h=this.old_y-c.canvasY;c.shiftKey&&(h*=10);if(c.metaKey||c.altKey)h*=0.1;this.old_y=c.canvasY;c=this._remainder+h/n.pixels_threshold;this._remainder=c%1;c=Math.clamp(this.properties.value+ -(c|0)*this.properties.step,this.properties.min,this.properties.max);this.properties.value=c;this.graph._version++;this.setDirtyCanvas(!0)}};n.prototype.onMouseUp=function(c,h){200>c.click_time&&(this.properties.value=Math.clamp(this.properties.value+(h[1]>0.5*this.size[1]?-1:1)*this.properties.step,this.properties.min,this.properties.max),this.graph._version++,this.setDirtyCanvas(!0));this.mouse_captured&&(this.mouse_captured=!1,this.captureInput(!1))};D.registerNodeType("widget/number",n);k.title= -"Combo";k.desc="Widget to select from a list";k.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};k.prototype.onPropertyChanged=function(c,h){"values"==c?(this._values=h.split(";"),this.widget.options.values=this._values):"value"==c&&(this.widget.value=h)};D.registerNodeType("widget/combo",k);w.title="Knob";w.desc="Circular controller";w.size=[80,100];w.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/ +(c|0)*this.properties.step,this.properties.min,this.properties.max);this.properties.value=c;this.graph._version++;this.setDirtyCanvas(!0)}};n.prototype.onMouseUp=function(c,h){200>c.click_time&&(this.properties.value=Math.clamp(this.properties.value+(h[1]>0.5*this.size[1]?-1:1)*this.properties.step,this.properties.min,this.properties.max),this.graph._version++,this.setDirtyCanvas(!0));this.mouse_captured&&(this.mouse_captured=!1,this.captureInput(!1))};E.registerNodeType("widget/number",n);k.title= +"Combo";k.desc="Widget to select from a list";k.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};k.prototype.onPropertyChanged=function(c,h){"values"==c?(this._values=h.split(";"),this.widget.options.values=this._values):"value"==c&&(this.widget.value=h)};E.registerNodeType("widget/combo",k);v.title="Knob";v.desc="Circular controller";v.size=[80,100];v.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/ (this.properties.max-this.properties.min));var h=0.5*this.size[0],k=0.5*this.size[1],n=0.5*Math.min(this.size[0],this.size[1])-5;c.globalAlpha=1;c.save();c.translate(h,k);c.rotate(0.75*Math.PI);c.fillStyle="rgba(0,0,0,0.5)";c.beginPath();c.moveTo(0,0);c.arc(0,0,n,0,1.5*Math.PI);c.fill();c.strokeStyle="black";c.fillStyle=this.properties.color;c.lineWidth=2;c.beginPath();c.moveTo(0,0);c.arc(0,0,n-4,0,1.5*Math.PI*Math.max(0.01,this.value));c.closePath();c.fill();c.lineWidth=1;c.globalAlpha=1;c.restore(); -c.fillStyle="black";c.beginPath();c.arc(h,k,0.75*n,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":this.properties.color;c.beginPath();var y=this.value*Math.PI*1.5+0.75*Math.PI;c.arc(h+Math.cos(y)*n*0.65,k+Math.sin(y)*n*0.65,0.05*n,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":"#AAA";c.font=Math.floor(0.5*n)+"px Arial";c.textAlign="center";c.fillText(this.properties.value.toFixed(this.properties.precision),h,k+0.15*n)}};w.prototype.onExecute=function(){this.setOutputData(0, -this.properties.value);this.boxcolor=D.colorToString([this.value,this.value,this.value])};w.prototype.onMouseDown=function(c){this.center=[0.5*this.size[0],0.5*this.size[1]+20];this.radius=0.5*this.size[0];if(20>c.canvasY-this.pos[1]||D.distance([c.canvasX,c.canvasY],[this.pos[0]+this.center[0],this.pos[1]+this.center[1]])>this.radius)return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];this.captureInput(!0);return!0};w.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX- -this.pos[0],c.canvasY-this.pos[1]];var h=this.value,h=h-0.01*(c[1]-this.oldmouse[1]);1h&&(h=0);this.value=h;this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.oldmouse=c;this.setDirtyCanvas(!0)}};w.prototype.onMouseUp=function(c){this.oldmouse&&(this.oldmouse=null,this.captureInput(!1))};w.prototype.onPropertyChanged=function(c,h){if("min"==c||"max"==c||"value"==c)return this.properties[c]=parseFloat(h),!0};D.registerNodeType("widget/knob", -w);h.title="Inner Slider";h.prototype.onPropertyChanged=function(c,h){"value"==c&&(this.slider.value=h)};h.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};D.registerNodeType("widget/internal_slider",h);z.title="H.Slider";z.desc="Linear slider controller";z.prototype.onDrawForeground=function(c){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min));c.globalAlpha=1;c.lineWidth=1;c.fillStyle="#000";c.fillRect(2, -2,this.size[0]-4,this.size[1]-4);c.fillStyle=this.properties.color;c.beginPath();c.rect(4,4,(this.size[0]-8)*this.value,this.size[1]-8);c.fill()};z.prototype.onExecute=function(){this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.setOutputData(0,this.properties.value);this.boxcolor=D.colorToString([this.value,this.value,this.value])};z.prototype.onMouseDown=function(c){if(0>c.canvasY-this.pos[1])return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY- -this.pos[1]];this.captureInput(!0);return!0};z.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];var h=this.value,h=h+(c[0]-this.oldmouse[0])/this.size[0];1h&&(h=0);this.value=h;this.oldmouse=c;this.setDirtyCanvas(!0)}};z.prototype.onMouseUp=function(c){this.oldmouse=null;this.captureInput(!1)};z.prototype.onMouseLeave=function(c){};D.registerNodeType("widget/hslider",z);x.title="Progress";x.desc="Shows data in linear progress";x.prototype.onExecute= -function(){var c=this.getInputData(0);void 0!=c&&(this.properties.value=c)};x.prototype.onDrawForeground=function(c){c.lineWidth=1;c.fillStyle=this.properties.color;var h=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min),h=Math.min(1,h),h=Math.max(0,h);c.fillRect(2,2,(this.size[0]-4)*h,this.size[1]-4)};D.registerNodeType("widget/progress",x);y.title="Text";y.desc="Shows the input value";y.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"led_text", -text:"LED",type:"minibutton"},{name:"normal_text",text:"Normal",type:"minibutton"}];y.prototype.onDrawForeground=function(c){c.fillStyle=this.properties.color;var h=this.properties.value;this.properties.glowSize?(c.shadowColor=this.properties.color,c.shadowOffsetX=0,c.shadowOffsetY=0,c.shadowBlur=this.properties.glowSize):c.shadowColor="transparent";var k=this.properties.fontsize;c.textAlign=this.properties.align;c.font=k.toString()+"px "+this.properties.font;this.str="number"==typeof h?h.toFixed(this.properties.decimals): -h;if("string"==typeof this.str){var h=this.str.split("\\n"),n;for(n in h)c.fillText(h[n],"left"==this.properties.align?15:this.size[0]-15,-0.15*k+k*(parseInt(n)+1))}c.shadowColor="transparent";this.last_ctx=c;c.textAlign="left"};y.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(this.properties.value=c)};y.prototype.resize=function(){if(this.last_ctx){var c=this.str.split("\\n");this.last_ctx.font=this.properties.fontsize+"px "+this.properties.font;var h=0,k;for(k in c){var n=this.last_ctx.measureText(c[k]).width; -hc.canvasY-this.pos[1]||E.distance([c.canvasX,c.canvasY],[this.pos[0]+this.center[0],this.pos[1]+this.center[1]])>this.radius)return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];this.captureInput(!0);return!0};v.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX- +this.pos[0],c.canvasY-this.pos[1]];var h=this.value,h=h-0.01*(c[1]-this.oldmouse[1]);1h&&(h=0);this.value=h;this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.oldmouse=c;this.setDirtyCanvas(!0)}};v.prototype.onMouseUp=function(c){this.oldmouse&&(this.oldmouse=null,this.captureInput(!1))};v.prototype.onPropertyChanged=function(c,h){if("min"==c||"max"==c||"value"==c)return this.properties[c]=parseFloat(h),!0};E.registerNodeType("widget/knob", +v);h.title="Inner Slider";h.prototype.onPropertyChanged=function(c,h){"value"==c&&(this.slider.value=h)};h.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};E.registerNodeType("widget/internal_slider",h);B.title="H.Slider";B.desc="Linear slider controller";B.prototype.onDrawForeground=function(c){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min));c.globalAlpha=1;c.lineWidth=1;c.fillStyle="#000";c.fillRect(2, +2,this.size[0]-4,this.size[1]-4);c.fillStyle=this.properties.color;c.beginPath();c.rect(4,4,(this.size[0]-8)*this.value,this.size[1]-8);c.fill()};B.prototype.onExecute=function(){this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.setOutputData(0,this.properties.value);this.boxcolor=E.colorToString([this.value,this.value,this.value])};B.prototype.onMouseDown=function(c){if(0>c.canvasY-this.pos[1])return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY- +this.pos[1]];this.captureInput(!0);return!0};B.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];var h=this.value,h=h+(c[0]-this.oldmouse[0])/this.size[0];1h&&(h=0);this.value=h;this.oldmouse=c;this.setDirtyCanvas(!0)}};B.prototype.onMouseUp=function(c){this.oldmouse=null;this.captureInput(!1)};B.prototype.onMouseLeave=function(c){};E.registerNodeType("widget/hslider",B);y.title="Progress";y.desc="Shows data in linear progress";y.prototype.onExecute= +function(){var c=this.getInputData(0);void 0!=c&&(this.properties.value=c)};y.prototype.onDrawForeground=function(c){c.lineWidth=1;c.fillStyle=this.properties.color;var h=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min),h=Math.min(1,h),h=Math.max(0,h);c.fillRect(2,2,(this.size[0]-4)*h,this.size[1]-4)};E.registerNodeType("widget/progress",y);C.title="Text";C.desc="Shows the input value";C.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"led_text", +text:"LED",type:"minibutton"},{name:"normal_text",text:"Normal",type:"minibutton"}];C.prototype.onDrawForeground=function(c){c.fillStyle=this.properties.color;var h=this.properties.value;this.properties.glowSize?(c.shadowColor=this.properties.color,c.shadowOffsetX=0,c.shadowOffsetY=0,c.shadowBlur=this.properties.glowSize):c.shadowColor="transparent";var k=this.properties.fontsize;c.textAlign=this.properties.align;c.font=k.toString()+"px "+this.properties.font;this.str="number"==typeof h?h.toFixed(this.properties.decimals): +h;if("string"==typeof this.str){var h=this.str.split("\\n"),n;for(n in h)c.fillText(h[n],"left"==this.properties.align?15:this.size[0]-15,-0.15*k+k*(parseInt(n)+1))}c.shadowColor="transparent";this.last_ctx=c;c.textAlign="left"};C.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(this.properties.value=c)};C.prototype.resize=function(){if(this.last_ctx){var c=this.str.split("\\n");this.last_ctx.font=this.properties.fontsize+"px "+this.properties.font;var h=0,k;for(k in c){var n=this.last_ctx.measureText(c[k]).width; +hk?n.xbox.axes.lx:0,this._left_axis[1]=Math.abs(n.xbox.axes.ly)>k?n.xbox.axes.ly:0,this._right_axis[0]=Math.abs(n.xbox.axes.rx)>k?n.xbox.axes.rx:0,this._right_axis[1]=Math.abs(n.xbox.axes.ry)>k?n.xbox.axes.ry:0,this._triggers[0]=Math.abs(n.xbox.axes.ltrigger)>k?n.xbox.axes.ltrigger: 0,this._triggers[1]=Math.abs(n.xbox.axes.rtrigger)>k?n.xbox.axes.rtrigger:0);if(this.outputs)for(k=0;k","enum",{values:a.values});this.size=[80,60]}function b(){this.addInput("inc", "number");this.addOutput("total","number");this.addProperty("increment",1);this.addProperty("value",0)}function d(){this.addInput("v","number");this.addOutput("sin","number");this.addProperty("amplitude",1);this.addProperty("offset",0);this.bgImageUrl="nodes/imgs/icon-sin.png"}function f(){this.addInput("x","number");this.addInput("y","number");this.addOutput("","number");this.properties={x:1,y:1,formula:"x+y"};this.code_widget=this.addWidget("text","F(x,y)",this.properties.formula,function(a,b,d){d.properties.formula= -a});this.addWidget("toggle","allow",G.allow_scripts,function(a){G.allow_scripts=a});this._func=null}function g(){this.addInput("vec2","vec2");this.addOutput("x","number");this.addOutput("y","number")}function s(){this.addInputs([["x","number"],["y","number"]]);this.addOutput("vec2","vec2");this.properties={x:0,y:0};this._data=new Float32Array(2)}function q(){this.addInput("vec3","vec3");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number")}function A(){this.addInputs([["x", -"number"],["y","number"],["z","number"]]);this.addOutput("vec3","vec3");this.properties={x:0,y:0,z:0};this._data=new Float32Array(3)}function t(){this.addInput("vec4","vec4");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number");this.addOutput("w","number")}function K(){this.addInputs([["x","number"],["y","number"],["z","number"],["w","number"]]);this.addOutput("vec4","vec4");this.properties={x:0,y:0,z:0,w:0};this._data=new Float32Array(4)}var G=C.LiteGraph;c.title= -"Converter";c.desc="type A to type B";c.prototype.onExecute=function(){var a=this.getInputData(0);if(null!=a&&this.outputs)for(var b=0;ba&&(a+=1024);var f=Math.floor(a);a-=f;d=h.data[f];f=h.data[1023==f?0:f+1];b&&(a=a*a*a*(a*(6*a-15)+10));return d*(1-a)+f*a};h.prototype.onExecute=function(){for(var a=this.getInputData(0)||0,b=this.properties.octaves||1,d=0,f=1,a=a+(this.properties.seed||0),c=this.properties.speed||1,g=0,e=0;ef);++e);a=this.properties.min; -this._last_v=d/g*(this.properties.max-a)+a;this.setOutputData(0,this._last_v)};h.prototype.onDrawBackground=function(a){this.outputs[0].label=(this._last_v||0).toFixed(3)};G.registerNodeType("math/noise",h);z.title="Spikes";z.desc="spike every random time";z.prototype.onExecute=function(){var a=this.graph.elapsed_time;this._remaining_time-=a;this._blink_time-=a;a=0;0this._remaining_time?(this._remaining_time= -Math.random()*(this.properties.max_time-this.properties.min_time)+this.properties.min_time,this._blink_time=this.properties.duration,this.boxcolor="#FFF"):this.boxcolor="#000";this.setOutputData(0,a)};G.registerNodeType("math/spikes",z);x.title="Clamp";x.desc="Clamp number between min and max";x.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(a=Math.max(this.properties.min,a),a=Math.min(this.properties.max,a),this.setOutputData(0,a))};x.prototype.getCode=function(a){a="";this.isInputConnected(0)&& -(a+="clamp({{0}},"+this.properties.min+","+this.properties.max+")");return a};G.registerNodeType("math/clamp",x);y.title="Lerp";y.desc="Linear Interpolation";y.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.getInputData(1);null==b&&(b=0);var d=this.properties.f,f=this.getInputData(2);void 0!==f&&(d=f);this.setOutputData(0,a*(1-d)+b*d)};y.prototype.onGetInputs=function(){return[["f","number"]]};G.registerNodeType("math/lerp",y);F.title="Abs";F.desc="Absolute";F.prototype.onExecute= -function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.abs(a))};G.registerNodeType("math/abs",F);D.title="Floor";D.desc="Floor number to remove fractional part";D.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.floor(a))};G.registerNodeType("math/floor",D);e.title="Frac";e.desc="Returns fractional part";e.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a%1)};G.registerNodeType("math/frac",e);B.title= -"Smoothstep";B.desc="Smoothstep";B.prototype.onExecute=function(){var a=this.getInputData(0);if(void 0!==a){var b=this.properties.A,a=Math.clamp((a-b)/(this.properties.B-b),0,1);this.setOutputData(0,a*a*(3-2*a))}};G.registerNodeType("math/smoothstep",B);E.title="Scale";E.desc="v * factor";E.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a*this.properties.factor)};G.registerNodeType("math/scale",E);u.title="Gate";u.desc="if v is true, then outputs A, otherwise B"; -u.prototype.onExecute=function(){var a=this.getInputData(0);this.setOutputData(0,this.getInputData(a?1:2))};G.registerNodeType("math/gate",u);I.title="Average";I.desc="Average Filter";I.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this._values.length;this._values[this._current%b]=a;this._current+=1;this._current>b&&(this._current=0);for(var d=a=0;db&&(b=1);this.properties.samples= -Math.round(b);var d=this._values;this._values=new Float32Array(this.properties.samples);d.length<=this._values.length?this._values.set(d):this._values.set(d.subarray(0,this._values.length))};G.registerNodeType("math/average",I);p.title="TendTo";p.desc="moves the output value always closer to the input";p.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.factor;this._value=null==this._value?a:this._value*(1-b)+a*b;this.setOutputData(0,this._value)};G.registerNodeType("math/tendTo", -p);r.values="+ - * / % ^ max min".split(" ");r.title="Operation";r.desc="Easy math operators";r["@OP"]={type:"enum",title:"operation",values:r.values};r.size=[100,60];r.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"};r.prototype.setValue=function(a){"string"==typeof a&&(a=parseFloat(a));this.properties.value=a};r.prototype.onPropertyChanged=function(a,b){if("OP"==a)switch(this.properties.OP){case "+":this._func= +a});this.addWidget("toggle","allow",w.allow_scripts,function(a){w.allow_scripts=a});this._func=null}function g(){this.addInput("vec2","vec2");this.addOutput("x","number");this.addOutput("y","number")}function r(){this.addInputs([["x","number"],["y","number"]]);this.addOutput("vec2","vec2");this.properties={x:0,y:0};this._data=new Float32Array(2)}function x(){this.addInput("vec3","vec3");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number")}function K(){this.addInputs([["x", +"number"],["y","number"],["z","number"]]);this.addOutput("vec3","vec3");this.properties={x:0,y:0,z:0};this._data=new Float32Array(3)}function q(){this.addInput("vec4","vec4");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number");this.addOutput("w","number")}function z(){this.addInputs([["x","number"],["y","number"],["z","number"],["w","number"]]);this.addOutput("vec4","vec4");this.properties={x:0,y:0,z:0,w:0};this._data=new Float32Array(4)}var w=D.LiteGraph;c.title= +"Converter";c.desc="type A to type B";c.prototype.onExecute=function(){var a=this.getInputData(0);if(null!=a&&this.outputs)for(var b=0;ba&&(a+=1024);var c=Math.floor(a);a-=c;d=h.data[c];c=h.data[1023==c?0:c+1];b&&(a=a*a*a*(a*(6*a-15)+10));return d*(1-a)+c*a};h.prototype.onExecute=function(){for(var a=this.getInputData(0)||0,b=this.properties.octaves||1,d=0,c=1,a=a+(this.properties.seed||0),f=this.properties.speed||1,g=0,e=0;ec);++e);a=this.properties.min; +this._last_v=d/g*(this.properties.max-a)+a;this.setOutputData(0,this._last_v)};h.prototype.onDrawBackground=function(a){this.outputs[0].label=(this._last_v||0).toFixed(3)};w.registerNodeType("math/noise",h);B.title="Spikes";B.desc="spike every random time";B.prototype.onExecute=function(){var a=this.graph.elapsed_time;this._remaining_time-=a;this._blink_time-=a;a=0;0this._remaining_time?(this._remaining_time= +Math.random()*(this.properties.max_time-this.properties.min_time)+this.properties.min_time,this._blink_time=this.properties.duration,this.boxcolor="#FFF"):this.boxcolor="#000";this.setOutputData(0,a)};w.registerNodeType("math/spikes",B);y.title="Clamp";y.desc="Clamp number between min and max";y.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(a=Math.max(this.properties.min,a),a=Math.min(this.properties.max,a),this.setOutputData(0,a))};y.prototype.getCode=function(a){a="";this.isInputConnected(0)&& +(a+="clamp({{0}},"+this.properties.min+","+this.properties.max+")");return a};w.registerNodeType("math/clamp",y);C.title="Lerp";C.desc="Linear Interpolation";C.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.getInputData(1);null==b&&(b=0);var d=this.properties.f,c=this.getInputData(2);void 0!==c&&(d=c);this.setOutputData(0,a*(1-d)+b*d)};C.prototype.onGetInputs=function(){return[["f","number"]]};w.registerNodeType("math/lerp",C);G.title="Abs";G.desc="Absolute";G.prototype.onExecute= +function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.abs(a))};w.registerNodeType("math/abs",G);E.title="Floor";E.desc="Floor number to remove fractional part";E.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.floor(a))};w.registerNodeType("math/floor",E);e.title="Frac";e.desc="Returns fractional part";e.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a%1)};w.registerNodeType("math/frac",e);A.title= +"Smoothstep";A.desc="Smoothstep";A.prototype.onExecute=function(){var a=this.getInputData(0);if(void 0!==a){var b=this.properties.A,a=Math.clamp((a-b)/(this.properties.B-b),0,1);this.setOutputData(0,a*a*(3-2*a))}};w.registerNodeType("math/smoothstep",A);F.title="Scale";F.desc="v * factor";F.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a*this.properties.factor)};w.registerNodeType("math/scale",F);t.title="Gate";t.desc="if v is true, then outputs A, otherwise B"; +t.prototype.onExecute=function(){var a=this.getInputData(0);this.setOutputData(0,this.getInputData(a?1:2))};w.registerNodeType("math/gate",t);H.title="Average";H.desc="Average Filter";H.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this._values.length;this._values[this._current%b]=a;this._current+=1;this._current>b&&(this._current=0);for(var d=a=0;db&&(b=1);this.properties.samples= +Math.round(b);var d=this._values;this._values=new Float32Array(this.properties.samples);d.length<=this._values.length?this._values.set(d):this._values.set(d.subarray(0,this._values.length))};w.registerNodeType("math/average",H);p.title="TendTo";p.desc="moves the output value always closer to the input";p.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.factor;this._value=null==this._value?a:this._value*(1-b)+a*b;this.setOutputData(0,this._value)};w.registerNodeType("math/tendTo", +p);s.values="+ - * / % ^ max min".split(" ");s.title="Operation";s.desc="Easy math operators";s["@OP"]={type:"enum",title:"operation",values:s.values};s.size=[100,60];s.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"};s.prototype.setValue=function(a){"string"==typeof a&&(a=parseFloat(a));this.properties.value=a};s.prototype.onPropertyChanged=function(a,b){if("OP"==a)switch(this.properties.OP){case "+":this._func= function(a,b){return a+b};break;case "-":this._func=function(a,b){return a-b};break;case "x":case "X":case "*":this._func=function(a,b){return a*b};break;case "/":this._func=function(a,b){return a/b};break;case "%":this._func=function(a,b){return a%b};break;case "^":this._func=function(a,b){return Math.pow(a,b)};break;case "max":this._func=function(a,b){return Math.max(a,b)};break;case "min":this._func=function(a,b){return Math.min(a,b)};break;default:console.warn("Unknown operation: "+this.properties.OP), -this._func=function(a){return a}}};r.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);null!=a?a.constructor===Number&&(this.properties.A=a):a=this.properties.A;null!=b?this.properties.B=b:b=this.properties.B;var d;if(a.constructor===Number)d=this._func(a,b);else if(a.constructor===Array){d=this._result;d.length=a.length;for(var f=0;fB":g=a>b;break;case "A=B":g=a>=b}this.setOutputData(d,g)}}};l.prototype.onGetOutputs=function(){return[["A==B","boolean"],["A!=B","boolean"], -["A>B","boolean"],["A=B","boolean"],["A<=B","boolean"]]};G.registerNodeType("math/compare",l);G.registerSearchboxExtra("math/compare","==",{outputs:[["A==B","boolean"]],title:"A==B"});G.registerSearchboxExtra("math/compare","!=",{outputs:[["A!=B","boolean"]],title:"A!=B"});G.registerSearchboxExtra("math/compare",">",{outputs:[["A>B","boolean"]],title:"A>B"});G.registerSearchboxExtra("math/compare","<",{outputs:[["A=",{outputs:[["A>=B","boolean"]],title:"A>=B"});G.registerSearchboxExtra("math/compare","<=",{outputs:[["A<=B","boolean"]],title:"A<=B"});a.values="> < == != <= >= || &&".split(" ");a["@OP"]={type:"enum",title:"operation",values:a.values};a.title="Condition";a.desc="evaluates condition between A and B";a.prototype.getTitle=function(){return"A "+this.properties.OP+" B"};a.prototype.onExecute=function(){var a=this.getInputData(0);void 0===a?a=this.properties.A:this.properties.A=a;var b=this.getInputData(1); -void 0===b?b=this.properties.B:this.properties.B=b;var d=!0;switch(this.properties.OP){case ">":d=a>b;break;case "<":d=a=":d=a>=b;break;case "||":d=a||b;break;case "&&":d=a&&b}this.setOutputData(0,d);this.setOutputData(1,!d)};G.registerNodeType("math/condition",a);b.title="Accumulate";b.desc="Increments a value every time";b.prototype.onExecute=function(){null===this.properties.value&&(this.properties.value=0);var a= -this.getInputData(0);this.properties.value=null!==a?this.properties.value+a:this.properties.value+this.properties.increment;this.setOutputData(0,this.properties.value)};G.registerNodeType("math/accumulate",b);d.title="Trigonometry";d.desc="Sin Cos Tan";d.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.amplitude,d=this.findInputSlot("amplitude");-1!=d&&(b=this.getInputData(d));var f=this.properties.offset,d=this.findInputSlot("offset");-1!=d&&(f=this.getInputData(d)); -for(var d=0,c=this.outputs.length;dXY";g.desc="vector 2 to components";g.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]))};G.registerNodeType("math3d/vec2-to-xy",g);s.title="XY->Vec2";s.desc="components to vector2";s.prototype.onExecute= -function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this._data;d[0]=a;d[1]=b;this.setOutputData(0,d)};G.registerNodeType("math3d/xy-to-vec2",s);q.title="Vec3->XYZ";q.desc="vector 3 to components";q.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]))};G.registerNodeType("math3d/vec3-to-xyz",q);A.title="XYZ->Vec3";A.desc="components to vector3"; -A.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var f=this._data;f[0]=a;f[1]=b;f[2]=d;this.setOutputData(0,f)};G.registerNodeType("math3d/xyz-to-vec3",A);t.title="Vec4->XYZW";t.desc="vector 4 to components";t.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2, -a[2]),this.setOutputData(3,a[3]))};G.registerNodeType("math3d/vec4-to-xyzw",t);K.title="XYZW->Vec4";K.desc="components to vector4";K.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var f=this.getInputData(3);null==f&&(f=this.properties.w);var c=this._data;c[0]=a;c[1]=b;c[2]=d;c[3]=f;this.setOutputData(0,c)};G.registerNodeType("math3d/xyzw-to-vec4", -K)})(this); -(function(C){function c(){this.addInput("T","vec3");this.addInput("R","vec3");this.addInput("S","vec3");this.addOutput("mat4","mat4");this.properties={T:[0,0,0],R:[0,0,0],S:[1,1,1],R_in_degrees:!0};this._result=mat4.create();this._must_update=!0}function m(){this.addInput("A","number,vec3");this.addInput("B","number,vec3");this.addOutput("=","number,vec3");this.addProperty("OP","+","enum",{values:m.values});this._result=vec3.create()}function n(){this.addInput("in","vec3");this.addInput("f","number"); -this.addOutput("out","vec3");this.properties={f:1};this._data=new Float32Array(3)}function k(){this.addInput("in","vec3");this.addOutput("out","number")}function w(){this.addInput("in","vec3");this.addOutput("out","vec3");this._data=new Float32Array(3)}function h(){this.addInput("A","vec3");this.addInput("B","vec3");this.addInput("f","vec3");this.addOutput("out","vec3");this.properties={f:0.5};this._data=new Float32Array(3)}function z(){this.addInput("A","vec3");this.addInput("B","vec3");this.addOutput("out", -"number")}var x=C.LiteGraph;c.title="mat4";c.temp_quat=new Float32Array([0,0,0,1]);c.temp_mat4=new Float32Array(16);c.temp_vec3=new Float32Array(3);c.prototype.onPropertyChanged=function(c,h){this._must_update=!0};c.prototype.onExecute=function(){var h=this._result,k=c.temp_quat,n=c.temp_mat4,e=c.temp_vec3,m=this.getInputData(0),E=this.getInputData(1),u=this.getInputData(2);if(this._must_update||m||E||u)m=m||this.properties.T,E=E||this.properties.R,u=u||this.properties.S,mat4.identity(h),mat4.translate(h, -h,m),this.properties.R_in_degrees?(e.set(E),vec3.scale(e,e,DEG2RAD),quat.fromEuler(k,e)):quat.fromEuler(k,E),mat4.fromQuat(n,k),mat4.multiply(h,h,n),mat4.scale(h,h,u);this.setOutputData(0,h)};x.registerNodeType("math3d/mat4",c);m.values="+ - * / % ^ max min dot cross".split(" ");x.registerSearchboxExtra("math3d/operation","CROSS()",{properties:{OP:"cross"},title:"CROSS()"});x.registerSearchboxExtra("math3d/operation","DOT()",{properties:{OP:"dot"},title:"DOT()"});m.title="Operation";m.desc="Easy math 3D operators"; +this._func=function(a){return a}}};s.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);null!=a?a.constructor===Number&&(this.properties.A=a):a=this.properties.A;null!=b?this.properties.B=b:b=this.properties.B;var d;if(a.constructor===Number)d=this._func(a,b);else if(a.constructor===Array){d=this._result;d.length=a.length;for(var c=0;cB":g=a>b;break;case "A=B":g=a>=b}this.setOutputData(d,g)}}};l.prototype.onGetOutputs=function(){return[["A==B","boolean"],["A!=B","boolean"], +["A>B","boolean"],["A=B","boolean"],["A<=B","boolean"]]};w.registerNodeType("math/compare",l);w.registerSearchboxExtra("math/compare","==",{outputs:[["A==B","boolean"]],title:"A==B"});w.registerSearchboxExtra("math/compare","!=",{outputs:[["A!=B","boolean"]],title:"A!=B"});w.registerSearchboxExtra("math/compare",">",{outputs:[["A>B","boolean"]],title:"A>B"});w.registerSearchboxExtra("math/compare","<",{outputs:[["A=",{outputs:[["A>=B","boolean"]],title:"A>=B"});w.registerSearchboxExtra("math/compare","<=",{outputs:[["A<=B","boolean"]],title:"A<=B"});a.values="> < == != <= >= || &&".split(" ");a["@OP"]={type:"enum",title:"operation",values:a.values};a.title="Condition";a.desc="evaluates condition between A and B";a.prototype.getTitle=function(){return"A "+this.properties.OP+" B"};a.prototype.onExecute=function(){var a=this.getInputData(0);void 0===a?a=this.properties.A:this.properties.A=a;var b=this.getInputData(1); +void 0===b?b=this.properties.B:this.properties.B=b;var d=!0;switch(this.properties.OP){case ">":d=a>b;break;case "<":d=a=":d=a>=b;break;case "||":d=a||b;break;case "&&":d=a&&b}this.setOutputData(0,d);this.setOutputData(1,!d)};w.registerNodeType("math/condition",a);b.title="Accumulate";b.desc="Increments a value every time";b.prototype.onExecute=function(){null===this.properties.value&&(this.properties.value=0);var a= +this.getInputData(0);this.properties.value=null!==a?this.properties.value+a:this.properties.value+this.properties.increment;this.setOutputData(0,this.properties.value)};w.registerNodeType("math/accumulate",b);d.title="Trigonometry";d.desc="Sin Cos Tan";d.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.amplitude,d=this.findInputSlot("amplitude");-1!=d&&(b=this.getInputData(d));var c=this.properties.offset,d=this.findInputSlot("offset");-1!=d&&(c=this.getInputData(d)); +for(var d=0,f=this.outputs.length;dXY";g.desc="vector 2 to components";g.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]))};w.registerNodeType("math3d/vec2-to-xy",g);r.title="XY->Vec2";r.desc="components to vector2";r.prototype.onExecute= +function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this._data;d[0]=a;d[1]=b;this.setOutputData(0,d)};w.registerNodeType("math3d/xy-to-vec2",r);x.title="Vec3->XYZ";x.desc="vector 3 to components";x.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]))};w.registerNodeType("math3d/vec3-to-xyz",x);K.title="XYZ->Vec3";K.desc="components to vector3"; +K.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this._data;c[0]=a;c[1]=b;c[2]=d;this.setOutputData(0,c)};w.registerNodeType("math3d/xyz-to-vec3",K);q.title="Vec4->XYZW";q.desc="vector 4 to components";q.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2, +a[2]),this.setOutputData(3,a[3]))};w.registerNodeType("math3d/vec4-to-xyzw",q);z.title="XYZW->Vec4";z.desc="components to vector4";z.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this.getInputData(3);null==c&&(c=this.properties.w);var f=this._data;f[0]=a;f[1]=b;f[2]=d;f[3]=c;this.setOutputData(0,f)};w.registerNodeType("math3d/xyzw-to-vec4", +z)})(this); +(function(D){function c(){this.addInput("T","vec3");this.addInput("R","vec3");this.addInput("S","vec3");this.addOutput("mat4","mat4");this.properties={T:[0,0,0],R:[0,0,0],S:[1,1,1],R_in_degrees:!0};this._result=mat4.create();this._must_update=!0}function m(){this.addInput("A","number,vec3");this.addInput("B","number,vec3");this.addOutput("=","number,vec3");this.addProperty("OP","+","enum",{values:m.values});this._result=vec3.create()}function n(){this.addInput("in","vec3");this.addInput("f","number"); +this.addOutput("out","vec3");this.properties={f:1};this._data=new Float32Array(3)}function k(){this.addInput("in","vec3");this.addOutput("out","number")}function v(){this.addInput("in","vec3");this.addOutput("out","vec3");this._data=new Float32Array(3)}function h(){this.addInput("A","vec3");this.addInput("B","vec3");this.addInput("f","vec3");this.addOutput("out","vec3");this.properties={f:0.5};this._data=new Float32Array(3)}function B(){this.addInput("A","vec3");this.addInput("B","vec3");this.addOutput("out", +"number")}var y=D.LiteGraph;c.title="mat4";c.temp_quat=new Float32Array([0,0,0,1]);c.temp_mat4=new Float32Array(16);c.temp_vec3=new Float32Array(3);c.prototype.onPropertyChanged=function(c,h){this._must_update=!0};c.prototype.onExecute=function(){var h=this._result,k=c.temp_quat,n=c.temp_mat4,e=c.temp_vec3,A=this.getInputData(0),m=this.getInputData(1),t=this.getInputData(2);if(this._must_update||A||m||t)A=A||this.properties.T,m=m||this.properties.R,t=t||this.properties.S,mat4.identity(h),mat4.translate(h, +h,A),this.properties.R_in_degrees?(e.set(m),vec3.scale(e,e,DEG2RAD),quat.fromEuler(k,e)):quat.fromEuler(k,m),mat4.fromQuat(n,k),mat4.multiply(h,h,n),mat4.scale(h,h,t);this.setOutputData(0,h)};y.registerNodeType("math3d/mat4",c);m.values="+ - * / % ^ max min dot cross".split(" ");y.registerSearchboxExtra("math3d/operation","CROSS()",{properties:{OP:"cross"},title:"CROSS()"});y.registerSearchboxExtra("math3d/operation","DOT()",{properties:{OP:"dot"},title:"DOT()"});m.title="Operation";m.desc="Easy math 3D operators"; m["@OP"]={type:"enum",title:"operation",values:m.values};m.size=[100,60];m.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"};m.prototype.onExecute=function(){var c=this.getInputData(0),h=this.getInputData(1);if(null!=c&&null!=h){c.constructor===Number&&(c=[c,c,c]);h.constructor===Number&&(h=[h,h,h]);var k=this._result;switch(this.properties.OP){case "+":k=vec3.add(k,c,h);break;case "-":k=vec3.sub(k,c,h); break;case "x":case "X":case "*":k=vec3.mul(k,c,h);break;case "/":k=vec3.div(k,c,h);break;case "%":k[0]=c[0]%h[0];k[1]=c[1]%h[1];k[2]=c[2]%h[2];break;case "^":k[0]=Math.pow(c[0],h[0]);k[1]=Math.pow(c[1],h[1]);k[2]=Math.pow(c[2],h[2]);break;case "max":k[0]=Math.max(c[0],h[0]);k[1]=Math.max(c[1],h[1]);k[2]=Math.max(c[2],h[2]);break;case "min":k[0]=Math.min(c[0],h[0]),k[1]=Math.min(c[1],h[1]),k[2]=Math.min(c[2],h[2]);case "dot":k=vec3.dot(c,h);break;case "cross":vec3.cross(k,c,h);break;default:console.warn("Unknown operation: "+ -this.properties.OP)}this.setOutputData(0,k)}};m.prototype.onDrawBackground=function(c){this.flags.collapsed||(c.font="40px Arial",c.fillStyle="#666",c.textAlign="center",c.fillText(this.properties.OP,0.5*this.size[0],0.5*(this.size[1]+x.NODE_TITLE_HEIGHT)),c.textAlign="left")};x.registerNodeType("math3d/operation",m);n.title="vec3_scale";n.desc="scales the components of a vec3";n.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);null==h&&(h=this.properties.f); -var k=this._data;k[0]=c[0]*h;k[1]=c[1]*h;k[2]=c[2]*h;this.setOutputData(0,k)}};x.registerNodeType("math3d/vec3-scale",n);k.title="vec3_length";k.desc="returns the module of a vector";k.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(c=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),this.setOutputData(0,c))};x.registerNodeType("math3d/vec3-length",k);w.title="vec3_normalize";w.desc="returns the vector normalized";w.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h= -Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),k=this._data;k[0]=c[0]/h;k[1]=c[1]/h;k[2]=c[2]/h;this.setOutputData(0,k)}};x.registerNodeType("math3d/vec3-normalize",w);h.title="vec3_lerp";h.desc="returns the interpolated vector";h.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);if(null!=h){var k=this.getInputOrProperty("f"),e=this._data;e[0]=c[0]*(1-k)+h[0]*k;e[1]=c[1]*(1-k)+h[1]*k;e[2]=c[2]*(1-k)+h[2]*k;this.setOutputData(0,e)}}};x.registerNodeType("math3d/vec3-lerp", -h);z.title="vec3_dot";z.desc="returns the dot product";z.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);null!=h&&this.setOutputData(0,c[0]*h[0]+c[1]*h[1]+c[2]*h[2])}};x.registerNodeType("math3d/vec3-dot",z);C.glMatrix?(C=function(){this.addOutput("quat","quat");this.properties={x:0,y:0,z:0,w:1,normalize:!1};this._value=quat.create()},C.title="Quaternion",C.desc="quaternion",C.prototype.onExecute=function(){this._value[0]=this.getInputOrProperty("x"); -this._value[1]=this.getInputOrProperty("y");this._value[2]=this.getInputOrProperty("z");this._value[3]=this.getInputOrProperty("w");this.properties.normalize&&quat.normalize(this._value,this._value);this.setOutputData(0,this._value)},C.prototype.onGetInputs=function(){return[["x","number"],["y","number"],["z","number"],["w","number"]]},x.registerNodeType("math3d/quaternion",C),C=function(){this.addInputs([["degrees","number"],["axis","vec3"]]);this.addOutput("quat","quat");this.properties={angle:90, -axis:vec3.fromValues(0,1,0)};this._value=quat.create()},C.title="Rotation",C.desc="quaternion rotation",C.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.angle);var h=this.getInputData(1);null==h&&(h=this.properties.axis);c=quat.setAxisAngle(this._value,h,0.0174532925*c);this.setOutputData(0,c)},x.registerNodeType("math3d/rotation",C),C=function(){this.addInput("euler","vec3");this.addOutput("quat","quat");this.properties={euler:[0,0,0],use_yaw_pitch_roll:!1}; -this._degs=vec3.create();this._value=quat.create()},C.title="Euler->Quat",C.desc="Converts euler angles (in degrees) to quaternion",C.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.euler);vec3.scale(this._degs,c,DEG2RAD);this.properties.use_yaw_pitch_roll&&(this._degs=[this._degs[2],this._degs[0],this._degs[1]]);c=quat.fromEuler(this._value,this._degs);this.setOutputData(0,c)},x.registerNodeType("math3d/euler_to_quat",C),C=function(){this.addInput(["quat","quat"]); -this.addOutput("euler","vec3");this._value=vec3.create()},C.title="Euler->Quat",C.desc="Converts rotX,rotY,rotZ in degrees to quat",C.prototype.onExecute=function(){var c=this.getInputData(0);c&&(quat.toEuler(this._value,c),vec3.scale(this._value,this._value,DEG2RAD),this.setOutputData(0,this._value))},x.registerNodeType("math3d/quat_to_euler",C),C=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},C.title="Rot. Vec3",C.desc= -"rotate a point",C.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.vec);var h=this.getInputData(1);null==h?this.setOutputData(c):this.setOutputData(0,vec3.transformQuat(vec3.create(),c,h))},x.registerNodeType("math3d/rotate_vec3",C),C=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},C.title="Mult. Quat",C.desc="rotate quaternion",C.prototype.onExecute=function(){var c=this.getInputData(0);if(null!= -c){var h=this.getInputData(1);null!=h&&(c=quat.multiply(this._value,c,h),this.setOutputData(0,c))}},x.registerNodeType("math3d/mult-quat",C),C=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp","quat");this.addProperty("factor",0.5);this._value=quat.create()},C.title="Quat Slerp",C.desc="quaternion spherical interpolation",C.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);if(null!=h){var k=this.properties.factor; -null!=this.getInputData(2)&&(k=this.getInputData(2));c=quat.slerp(this._value,c,h,k);this.setOutputData(0,c)}}},x.registerNodeType("math3d/quat-slerp",C),C=function(){this.addInput("vec3","vec3");this.addOutput("remap","vec3");this.addOutput("clamped","vec3");this.properties={clamp:!0,range_min:[-1,-1,0],range_max:[1,1,0],target_min:[-1,-1,0],target_max:[1,1,0]};this._value=vec3.create();this._clamped=vec3.create()},C.title="Remap Range",C.desc="remap a 3D range",C.prototype.onExecute=function(){var c= -this.getInputData(0);c&&this._value.set(c);for(var c=this.properties.range_min,h=this.properties.range_max,k=this.properties.target_min,e=this.properties.target_max,n=0;3>n;++n){var m=h[n]-c[n];this._clamped[n]=Math.clamp(this._value[n],c[n],h[n]);0==m?this._value[n]=0.5*(k[n]+e[n]):(m=(this._value[n]-c[n])/m,this.properties.clamp&&(m=Math.clamp(m,0,1)),this._value[n]=k[n]+m*(e[n]-k[n]))}this.setOutputData(0,this._value);this.setOutputData(1,this._clamped)},x.registerNodeType("math3d/remap_range", -C)):console.warn("No glmatrix found, some Math3D nodes may not work")})(this); -(function(C){function c(c,k){return c==k}function m(){this.addInput("","string");this.addOutput("table","table");this.addOutput("rows","number");this.addProperty("value","");this.addProperty("separator",",");this._table=null}C=C.LiteGraph;C.wrapFunctionAsNode("string/toString",c,[""],"String");C.wrapFunctionAsNode("string/compare",c,["string","string"],"boolean");C.wrapFunctionAsNode("string/concatenate",function(c,k){return void 0===c?k:void 0===k?c:c+k},["string","string"],"string");C.wrapFunctionAsNode("string/contains", -function(c,k){return void 0===c||void 0===k?!1:-1!=c.indexOf(k)},["string","string"],"boolean");C.wrapFunctionAsNode("string/toUpperCase",function(c){return null!=c&&c.constructor===String?c.toUpperCase():c},["string"],"string");C.wrapFunctionAsNode("string/split",function(c,k){null==k&&(k=this.properties.separator);if(null==c)return[];if(c.constructor===String)return c.split(k||" ");if(c.constructor===Array){for(var m=[],h=0;hQuat",D.desc="Converts euler angles (in degrees) to quaternion",D.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.euler);vec3.scale(this._degs,c,DEG2RAD);this.properties.use_yaw_pitch_roll&&(this._degs=[this._degs[2],this._degs[0],this._degs[1]]);c=quat.fromEuler(this._value,this._degs);this.setOutputData(0,c)},y.registerNodeType("math3d/euler_to_quat",D),D=function(){this.addInput(["quat","quat"]); +this.addOutput("euler","vec3");this._value=vec3.create()},D.title="Euler->Quat",D.desc="Converts rotX,rotY,rotZ in degrees to quat",D.prototype.onExecute=function(){var c=this.getInputData(0);c&&(quat.toEuler(this._value,c),vec3.scale(this._value,this._value,DEG2RAD),this.setOutputData(0,this._value))},y.registerNodeType("math3d/quat_to_euler",D),D=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},D.title="Rot. Vec3",D.desc= +"rotate a point",D.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.vec);var h=this.getInputData(1);null==h?this.setOutputData(c):this.setOutputData(0,vec3.transformQuat(vec3.create(),c,h))},y.registerNodeType("math3d/rotate_vec3",D),D=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},D.title="Mult. Quat",D.desc="rotate quaternion",D.prototype.onExecute=function(){var c=this.getInputData(0);if(null!= +c){var h=this.getInputData(1);null!=h&&(c=quat.multiply(this._value,c,h),this.setOutputData(0,c))}},y.registerNodeType("math3d/mult-quat",D),D=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp","quat");this.addProperty("factor",0.5);this._value=quat.create()},D.title="Quat Slerp",D.desc="quaternion spherical interpolation",D.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);if(null!=h){var k=this.properties.factor; +null!=this.getInputData(2)&&(k=this.getInputData(2));c=quat.slerp(this._value,c,h,k);this.setOutputData(0,c)}}},y.registerNodeType("math3d/quat-slerp",D),D=function(){this.addInput("vec3","vec3");this.addOutput("remap","vec3");this.addOutput("clamped","vec3");this.properties={clamp:!0,range_min:[-1,-1,0],range_max:[1,1,0],target_min:[-1,-1,0],target_max:[1,1,0]};this._value=vec3.create();this._clamped=vec3.create()},D.title="Remap Range",D.desc="remap a 3D range",D.prototype.onExecute=function(){var c= +this.getInputData(0);c&&this._value.set(c);for(var c=this.properties.range_min,h=this.properties.range_max,k=this.properties.target_min,e=this.properties.target_max,n=0;3>n;++n){var m=h[n]-c[n];this._clamped[n]=Math.clamp(this._value[n],c[n],h[n]);0==m?this._value[n]=0.5*(k[n]+e[n]):(m=(this._value[n]-c[n])/m,this.properties.clamp&&(m=Math.clamp(m,0,1)),this._value[n]=k[n]+m*(e[n]-k[n]))}this.setOutputData(0,this._value);this.setOutputData(1,this._clamped)},y.registerNodeType("math3d/remap_range", +D)):console.warn("No glmatrix found, some Math3D nodes may not work")})(this); +(function(D){function c(c,k){return c==k}function m(){this.addInput("","string");this.addOutput("table","table");this.addOutput("rows","number");this.addProperty("value","");this.addProperty("separator",",");this._table=null}D=D.LiteGraph;D.wrapFunctionAsNode("string/toString",c,[""],"String");D.wrapFunctionAsNode("string/compare",c,["string","string"],"boolean");D.wrapFunctionAsNode("string/concatenate",function(c,k){return void 0===c?k:void 0===k?c:c+k},["string","string"],"string");D.wrapFunctionAsNode("string/contains", +function(c,k){return void 0===c||void 0===k?!1:-1!=c.indexOf(k)},["string","string"],"boolean");D.wrapFunctionAsNode("string/toUpperCase",function(c){return null!=c&&c.constructor===String?c.toUpperCase():c},["string"],"string");D.wrapFunctionAsNode("string/split",function(c,k){null==k&&(k=this.properties.separator);if(null==c)return[];if(c.constructor===String)return c.split(k||" ");if(c.constructor===Array){for(var m=[],h=0;he;++e){var h=this.getInputData(e);if(null!=h){var k=this.values[e];k.push(h);k.length>c[0]&&k.shift()}}}};c.prototype.onDrawBackground=function(e){if(!this.flags.collapsed){var h=this.size,k=0.5*h[1]/ -this.properties.scale,m=c.colors,p=0.5*h[1];e.fillStyle="#000";e.fillRect(0,0,h[0],h[1]);e.strokeStyle="#555";e.beginPath();e.moveTo(0,p);e.lineTo(h[0],p);e.stroke();if(this.inputs)for(var r=0;4>r;++r){var l=this.values[r];if(this.inputs[r]&&this.inputs[r].link){e.strokeStyle=m[r];e.beginPath();var a=l[0]*k*-1+p;e.moveTo(0,Math.clamp(a,0,h[1]));for(var b=1;be;++e){var h=this.getInputData(e);if(null!=h){var k=this.values[e];k.push(h);k.length>c[0]&&k.shift()}}}};c.prototype.onDrawBackground=function(e){if(!this.flags.collapsed){var h=this.size,k=0.5*h[1]/ +this.properties.scale,m=c.colors,p=0.5*h[1];e.fillStyle="#000";e.fillRect(0,0,h[0],h[1]);e.strokeStyle="#555";e.beginPath();e.moveTo(0,p);e.lineTo(h[0],p);e.stroke();if(this.inputs)for(var s=0;4>s;++s){var l=this.values[s];if(this.inputs[s]&&this.inputs[s].link){e.strokeStyle=m[s];e.beginPath();var a=l[0]*k*-1+p;e.moveTo(0,Math.clamp(a,0,h[1]));for(var b=1;be&&(e=0);if(0!=c.length){var h=[0,0,0];if(0==e)h=c[0];else if(1==e)h=c[c.length-1];else{var k= (c.length-1)*e,e=c[Math.floor(k)],c=c[Math.floor(k)+1],k=k-Math.floor(k);h[0]=e[0]*(1-k)+c[0]*k;h[1]=e[1]*(1-k)+c[1]*k;h[2]=e[2]*(1-k)+c[2]*k}for(var p in h)h[p]/=255;this.boxcolor=colorToString(h);this.setOutputData(0,h)}};e.registerNodeType("color/palette",n);k.title="Frame";k.desc="Frame viewerew";k.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}];k.prototype.onDrawBackground=function(c){this.frame&&!this.flags.collapsed&&c.drawImage(this.frame, 0,0,this.size[0],this.size[1])};k.prototype.onExecute=function(){this.frame=this.getInputData(0);this.setDirtyCanvas(!0)};k.prototype.onWidget=function(c,e){if("resize"==e.name&&this.frame){var h=this.frame.width,k=this.frame.height;h||null==this.frame.videoWidth||(h=this.frame.videoWidth,k=this.frame.videoHeight);h&&k&&(this.size=[h,k]);this.setDirtyCanvas(!0,!0)}else"view"==e.name&&this.show()};k.prototype.show=function(){showElement&&this.frame&&showElement(this.frame)};e.registerNodeType("graphics/frame", -k);w.title="Image fade";w.desc="Fades between images";w.widgets=[{name:"resizeA",text:"Resize to A",type:"button"},{name:"resizeB",text:"Resize to B",type:"button"}];w.prototype.onAdded=function(){this.createCanvas();var c=this.canvas.getContext("2d");c.fillStyle="#000";c.fillRect(0,0,this.properties.width,this.properties.height)};w.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};w.prototype.onExecute= -function(){var c=this.canvas.getContext("2d");this.canvas.width=this.canvas.width;var e=this.getInputData(0);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);e=this.getInputData(2);null==e?e=this.properties.fade:this.properties.fade=e;c.globalAlpha=e;e=this.getInputData(1);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);c.globalAlpha=1;this.setOutputData(0,this.canvas);this.setDirtyCanvas(!0)};e.registerNodeType("graphics/imagefade",w);h.title="Crop";h.desc="Crop Image"; +k);v.title="Image fade";v.desc="Fades between images";v.widgets=[{name:"resizeA",text:"Resize to A",type:"button"},{name:"resizeB",text:"Resize to B",type:"button"}];v.prototype.onAdded=function(){this.createCanvas();var c=this.canvas.getContext("2d");c.fillStyle="#000";c.fillRect(0,0,this.properties.width,this.properties.height)};v.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};v.prototype.onExecute= +function(){var c=this.canvas.getContext("2d");this.canvas.width=this.canvas.width;var e=this.getInputData(0);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);e=this.getInputData(2);null==e?e=this.properties.fade:this.properties.fade=e;c.globalAlpha=e;e=this.getInputData(1);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);c.globalAlpha=1;this.setOutputData(0,this.canvas);this.setDirtyCanvas(!0)};e.registerNodeType("graphics/imagefade",v);h.title="Crop";h.desc="Crop Image"; h.prototype.onAdded=function(){this.createCanvas()};h.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};h.prototype.onExecute=function(){var c=this.getInputData(0);c&&(c.width?(this.canvas.getContext("2d").drawImage(c,-this.properties.x,-this.properties.y,c.width*this.properties.scale,c.height*this.properties.scale),this.setOutputData(0,this.canvas)):this.setOutputData(0,null))};h.prototype.onDrawBackground= -function(c){this.flags.collapsed||this.canvas&&c.drawImage(this.canvas,0,0,this.canvas.width,this.canvas.height,0,0,this.size[0],this.size[1])};h.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"scale"==c?(this.properties[c]=parseFloat(e),0==this.properties[c]&&(console.error("Error in scale"),this.properties[c]=1)):this.properties[c]=parseInt(e);this.createCanvas();return!0};e.registerNodeType("graphics/cropImage",h);z.title="Canvas";z.desc="Canvas to render stuff";z.prototype.onExecute= -function(){var c=this.canvas,e=this.properties.width|0,h=this.properties.height|0;c.width!=e&&(c.width=e);c.height!=h&&(c.height=h);this.properties.autoclear&&this.ctx.clearRect(0,0,c.width,c.height);this.setOutputData(0,c)};z.prototype.onAction=function(c,e){"clear"==c&&this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)};e.registerNodeType("graphics/canvas",z);x.title="DrawImage";x.desc="Draws image into a canvas";x.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e= -this.getInputOrProperty("img");if(e){var h=this.getInputOrProperty("x"),k=this.getInputOrProperty("y");c.getContext("2d").drawImage(e,h,k)}}};e.registerNodeType("graphics/drawImage",x);y.title="DrawRectangle";y.desc="Draws rectangle in canvas";y.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e=this.getInputOrProperty("x"),h=this.getInputOrProperty("y"),k=this.getInputOrProperty("w"),p=this.getInputOrProperty("h");c.getContext("2d").fillRect(e,h,k,p)}};e.registerNodeType("graphics/drawRectangle", -y);F.title="Video";F.desc="Video playback";F.widgets=[{name:"play",text:"PLAY",type:"minibutton"},{name:"stop",text:"STOP",type:"minibutton"},{name:"demo",text:"Demo video",type:"button"},{name:"mute",text:"Mute video",type:"button"}];F.prototype.onExecute=function(){if(this.properties.url&&(this.properties.url!=this._video_url&&this.loadVideo(this.properties.url),this._video&&0!=this._video.width)){var c=this.getInputData(0);c&&0<=c&&1>=c&&(this._video.currentTime=c*this._video.duration,this._video.pause()); -this._video.dirty=!0;this.setOutputData(0,this._video);this.setOutputData(1,this._video.currentTime);this.setOutputData(2,this._video.duration);this.setDirtyCanvas(!0)}};F.prototype.onStart=function(){this.play()};F.prototype.onStop=function(){this.stop()};F.prototype.loadVideo=function(c){this._video_url=c;var h=c.substr(0,10).indexOf(":"),k="";-1!=h&&(k=c.substr(0,h));h="";k&&(h=c.substr(0,c.indexOf("/",k.length+3)),h=h.substr(k.length+3));this.properties.use_proxy&&k&&e.proxy&&h!=location.host&& +function(c){this.flags.collapsed||this.canvas&&c.drawImage(this.canvas,0,0,this.canvas.width,this.canvas.height,0,0,this.size[0],this.size[1])};h.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"scale"==c?(this.properties[c]=parseFloat(e),0==this.properties[c]&&(console.error("Error in scale"),this.properties[c]=1)):this.properties[c]=parseInt(e);this.createCanvas();return!0};e.registerNodeType("graphics/cropImage",h);B.title="Canvas";B.desc="Canvas to render stuff";B.prototype.onExecute= +function(){var c=this.canvas,e=this.properties.width|0,h=this.properties.height|0;c.width!=e&&(c.width=e);c.height!=h&&(c.height=h);this.properties.autoclear&&this.ctx.clearRect(0,0,c.width,c.height);this.setOutputData(0,c)};B.prototype.onAction=function(c,e){"clear"==c&&this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)};e.registerNodeType("graphics/canvas",B);y.title="DrawImage";y.desc="Draws image into a canvas";y.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e= +this.getInputOrProperty("img");if(e){var h=this.getInputOrProperty("x"),k=this.getInputOrProperty("y");c.getContext("2d").drawImage(e,h,k)}}};e.registerNodeType("graphics/drawImage",y);C.title="DrawRectangle";C.desc="Draws rectangle in canvas";C.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e=this.getInputOrProperty("x"),h=this.getInputOrProperty("y"),k=this.getInputOrProperty("w"),p=this.getInputOrProperty("h");c.getContext("2d").fillRect(e,h,k,p)}};e.registerNodeType("graphics/drawRectangle", +C);G.title="Video";G.desc="Video playback";G.widgets=[{name:"play",text:"PLAY",type:"minibutton"},{name:"stop",text:"STOP",type:"minibutton"},{name:"demo",text:"Demo video",type:"button"},{name:"mute",text:"Mute video",type:"button"}];G.prototype.onExecute=function(){if(this.properties.url&&(this.properties.url!=this._video_url&&this.loadVideo(this.properties.url),this._video&&0!=this._video.width)){var c=this.getInputData(0);c&&0<=c&&1>=c&&(this._video.currentTime=c*this._video.duration,this._video.pause()); +this._video.dirty=!0;this.setOutputData(0,this._video);this.setOutputData(1,this._video.currentTime);this.setOutputData(2,this._video.duration);this.setDirtyCanvas(!0)}};G.prototype.onStart=function(){this.play()};G.prototype.onStop=function(){this.stop()};G.prototype.loadVideo=function(c){this._video_url=c;var h=c.substr(0,10).indexOf(":"),k="";-1!=h&&(k=c.substr(0,h));h="";k&&(h=c.substr(0,c.indexOf("/",k.length+3)),h=h.substr(k.length+3));this.properties.use_proxy&&k&&e.proxy&&h!=location.host&& (c=e.proxy+c.substr(c.indexOf(":")+3));this._video=document.createElement("video");this._video.src=c;this._video.type="type=video/mp4";this._video.muted=!0;this._video.autoplay=!0;var m=this;this._video.addEventListener("loadedmetadata",function(c){console.log("Duration: "+this.duration+" seconds");console.log("Size: "+this.videoWidth+","+this.videoHeight);m.setDirtyCanvas(!0);this.width=this.videoWidth;this.height=this.videoHeight});this._video.addEventListener("progress",function(c){console.log("video loading...")}); this._video.addEventListener("error",function(c){console.error("Error loading video: "+this.src);if(this.error)switch(this.error.code){case this.error.MEDIA_ERR_ABORTED:console.error("You stopped the video.");break;case this.error.MEDIA_ERR_NETWORK:console.error("Network error - please try again later.");break;case this.error.MEDIA_ERR_DECODE:console.error("Video is broken..");break;case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED:console.error("Sorry, your browser can't play this video.")}});this._video.addEventListener("ended", -function(c){console.log("Video Ended.");this.play()})};F.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"url"==c&&""!=e&&this.loadVideo(e);return!0};F.prototype.play=function(){this._video&&this._video.videoWidth&&this._video.play()};F.prototype.playPause=function(){this._video&&(this._video.paused?this.play():this.pause())};F.prototype.stop=function(){this._video&&(this._video.pause(),this._video.currentTime=0)};F.prototype.pause=function(){this._video&&(console.log("Video paused"), -this._video.pause())};F.prototype.onWidget=function(c,e){};e.registerNodeType("graphics/video",F);D.title="Webcam";D.desc="Webcam image";D.is_webcam_open=!1;D.prototype.openStream=function(){function c(h){console.log("Webcam rejected",h);e._webcam_stream=!1;D.is_webcam_open=!1;e.boxcolor="red";e.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](c); -var e=this}};D.prototype.closeStream=function(){if(this._webcam_stream){var c=this._webcam_stream.getTracks();if(c.length)for(var e=0;e=this.size[1]||!this.properties.show|| -!this._video||(c.save(),c.drawImage(this._video,0,0,this.size[0],this.size[1]),c.restore())};D.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["stream_ready",e.EVENT],["stream_closed",e.EVENT],["stream_error",e.EVENT]]};e.registerNodeType("graphics/webcam",D)})(this); -(function(C){function c(){this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",filter:!0};this.size=[c.image_preview_size,c.image_preview_size]}function m(){this.addInput("Texture","Texture");this.properties={flipY:!1};this.size=[c.image_preview_size,c.image_preview_size]}function n(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",generate_mipmaps:!1}}function k(){this.addInput("Texture", +function(c){console.log("Video Ended.");this.play()})};G.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"url"==c&&""!=e&&this.loadVideo(e);return!0};G.prototype.play=function(){this._video&&this._video.videoWidth&&this._video.play()};G.prototype.playPause=function(){this._video&&(this._video.paused?this.play():this.pause())};G.prototype.stop=function(){this._video&&(this._video.pause(),this._video.currentTime=0)};G.prototype.pause=function(){this._video&&(console.log("Video paused"), +this._video.pause())};G.prototype.onWidget=function(c,e){};e.registerNodeType("graphics/video",G);E.title="Webcam";E.desc="Webcam image";E.is_webcam_open=!1;E.prototype.openStream=function(){function c(h){console.log("Webcam rejected",h);e._webcam_stream=!1;E.is_webcam_open=!1;e.boxcolor="red";e.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](c); +var e=this}};E.prototype.closeStream=function(){if(this._webcam_stream){var c=this._webcam_stream.getTracks();if(c.length)for(var e=0;e=this.size[1]||!this.properties.show|| +!this._video||(c.save(),c.drawImage(this._video,0,0,this.size[0],this.size[1]),c.restore())};E.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["stream_ready",e.EVENT],["stream_closed",e.EVENT],["stream_error",e.EVENT]]};e.registerNodeType("graphics/webcam",E)})(this); +(function(D){function c(){this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",filter:!0};this.size=[c.image_preview_size,c.image_preview_size]}function m(){this.addInput("Texture","Texture");this.properties={flipY:!1};this.size=[c.image_preview_size,c.image_preview_size]}function n(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",generate_mipmaps:!1}}function k(){this.addInput("Texture", "Texture");this.addInput("TextureB","Texture");this.addInput("value","number");this.addOutput("Texture","Texture");this.help="

pixelcode must be vec3, uvcode must be vec2, is optional

\t\t

uv: tex. coords

color: texture colorB: textureB

time: scene time value: input value

For multiline you must type: result = ...

";this.properties={value:1,pixelcode:"color + colorB * value",uvcode:"",precision:c.DEFAULT}; -this.has_error=!1}function w(){this.addOutput("out","Texture");this.properties={code:"",u_value:1,u_color:[1,1,1,1],width:512,height:512,precision:c.DEFAULT};this.properties.code=w.pixel_shader;this._uniforms={u_value:1,u_color:vec4.create(),in_texture:0,texSize:vec4.create(),time:0}}function h(){this.addInput("in","Texture");this.addInput("scale","vec2");this.addInput("offset","vec2");this.addOutput("out","Texture");this.properties={offset:vec2.fromValues(0,0),scale:vec2.fromValues(1,1),precision:c.DEFAULT}} -function z(){this.addInput("in","Texture");this.addInput("warp","Texture");this.addInput("factor","number");this.addOutput("out","Texture");this.properties={factor:0.01,scale:[1,1],offset:[0,0],precision:c.DEFAULT};this._uniforms={u_texture:0,u_textureB:1,u_factor:1,u_scale:vec2.create(),u_offset:vec2.create()}}function x(){this.addInput("Texture","Texture");this.properties={additive:!1,antialiasing:!1,filter:!0,disable_alpha:!1,gamma:1,viewport:[0,0,1,1]};this.size[0]=130}function y(){this.addInput("Texture", -"Texture");this.addOutput("","Texture");this.properties={size:0,generate_mipmaps:!1,precision:c.DEFAULT}}function F(){this.addInput("Texture","Texture");this.addOutput("","Texture");this.properties={iterations:1,generate_mipmaps:!1,precision:c.DEFAULT}}function D(){this.addInput("Texture","Texture");this.addOutput("","Texture");this.properties={size:[512,512],generate_mipmaps:!1,precision:c.DEFAULT}}function e(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("avg", -"vec4");this.addOutput("lum","number");this.properties={use_previous_frame:!0,high_quality:!1};this._uniforms={u_texture:0,u_mipmap_offset:0};this._luminance=new Float32Array(4)}function B(){this.addInput("Texture","Texture");this.addOutput("min_t","Texture");this.addOutput("max_t","Texture");this.addOutput("min","vec4");this.addOutput("max","vec4");this.properties={mode:"max",use_previous_frame:!0};this._uniforms={u_texture:0};this._max=new Float32Array(4);this._min=new Float32Array(4);this._textures_chain= -[]}function E(){this.addInput("in","Texture");this.addInput("factor","Number");this.addOutput("out","Texture");this.properties={factor:0.5};this._uniforms={u_texture:0,u_textureB:1,u_factor:this.properties.factor}}function u(){this.addInput("in","Texture");this.addOutput("avg","Texture");this.addOutput("array","Texture");this.properties={samples:64,frames_interval:1};this._uniforms={u_texture:0,u_textureB:1,u_samples:this.properties.samples,u_isamples:1/this.properties.samples};this.frame=0}function I(){this.addInput("Image", -"image");this.addOutput("","Texture");this.properties={}}function p(){this.addInput("Texture","Texture");this.addInput("LUT","Texture");this.addInput("Intensity","number");this.addOutput("","Texture");this.properties={enabled:!0,intensity:1,precision:c.DEFAULT,texture:null};p._shader||(p._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,p.pixel_shader))}function r(){this.addInput("Texture","Texture");this.addInput("Atlas","Texture");this.addOutput("","Texture");this.properties={enabled:!0,num_row_symbols:4, -symbol_size:16,brightness:1,colorize:!1,filter:!1,invert:!1,precision:c.DEFAULT,generate_mipmaps:!1,texture:null};r._shader||(r._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,r.pixel_shader));this._uniforms={u_texture:0,u_textureB:1,u_row_simbols:4,u_simbol_size:16,u_res:vec2.create()}}function l(){this.addInput("Texture","Texture");this.addOutput("R","Texture");this.addOutput("G","Texture");this.addOutput("B","Texture");this.addOutput("A","Texture");l._shader||(l._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER, +this.has_error=!1}function v(){this.addOutput("out","Texture");this.properties={code:"",u_value:1,u_color:[1,1,1,1],width:512,height:512,precision:c.DEFAULT};this.properties.code=v.pixel_shader;this._uniforms={u_value:1,u_color:vec4.create(),in_texture:0,texSize:vec4.create(),time:0}}function h(){this.addInput("in","Texture");this.addInput("scale","vec2");this.addInput("offset","vec2");this.addOutput("out","Texture");this.properties={offset:vec2.fromValues(0,0),scale:vec2.fromValues(1,1),precision:c.DEFAULT}} +function B(){this.addInput("in","Texture");this.addInput("warp","Texture");this.addInput("factor","number");this.addOutput("out","Texture");this.properties={factor:0.01,scale:[1,1],offset:[0,0],precision:c.DEFAULT};this._uniforms={u_texture:0,u_textureB:1,u_factor:1,u_scale:vec2.create(),u_offset:vec2.create()}}function y(){this.addInput("Texture","Texture");this.properties={additive:!1,antialiasing:!1,filter:!0,disable_alpha:!1,gamma:1,viewport:[0,0,1,1]};this.size[0]=130}function C(){this.addInput("Texture", +"Texture");this.addOutput("","Texture");this.properties={size:0,generate_mipmaps:!1,precision:c.DEFAULT}}function G(){this.addInput("Texture","Texture");this.addOutput("","Texture");this.properties={iterations:1,generate_mipmaps:!1,precision:c.DEFAULT}}function E(){this.addInput("Texture","Texture");this.addOutput("","Texture");this.properties={size:[512,512],generate_mipmaps:!1,precision:c.DEFAULT}}function e(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("avg", +"vec4");this.addOutput("lum","number");this.properties={use_previous_frame:!0,high_quality:!1};this._uniforms={u_texture:0,u_mipmap_offset:0};this._luminance=new Float32Array(4)}function A(){this.addInput("Texture","Texture");this.addOutput("min_t","Texture");this.addOutput("max_t","Texture");this.addOutput("min","vec4");this.addOutput("max","vec4");this.properties={mode:"max",use_previous_frame:!0};this._uniforms={u_texture:0};this._max=new Float32Array(4);this._min=new Float32Array(4);this._textures_chain= +[]}function F(){this.addInput("in","Texture");this.addInput("factor","Number");this.addOutput("out","Texture");this.properties={factor:0.5};this._uniforms={u_texture:0,u_textureB:1,u_factor:this.properties.factor}}function t(){this.addInput("in","Texture");this.addOutput("avg","Texture");this.addOutput("array","Texture");this.properties={samples:64,frames_interval:1};this._uniforms={u_texture:0,u_textureB:1,u_samples:this.properties.samples,u_isamples:1/this.properties.samples};this.frame=0}function H(){this.addInput("Image", +"image");this.addOutput("","Texture");this.properties={}}function p(){this.addInput("Texture","Texture");this.addInput("LUT","Texture");this.addInput("Intensity","number");this.addOutput("","Texture");this.properties={enabled:!0,intensity:1,precision:c.DEFAULT,texture:null};p._shader||(p._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,p.pixel_shader))}function s(){this.addInput("Texture","Texture");this.addInput("Atlas","Texture");this.addOutput("","Texture");this.properties={enabled:!0,num_row_symbols:4, +symbol_size:16,brightness:1,colorize:!1,filter:!1,invert:!1,precision:c.DEFAULT,generate_mipmaps:!1,texture:null};s._shader||(s._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,s.pixel_shader));this._uniforms={u_texture:0,u_textureB:1,u_row_simbols:4,u_simbol_size:16,u_res:vec2.create()}}function l(){this.addInput("Texture","Texture");this.addOutput("R","Texture");this.addOutput("G","Texture");this.addOutput("B","Texture");this.addOutput("A","Texture");l._shader||(l._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER, l.pixel_shader))}function a(){this.addInput("R","Texture");this.addInput("G","Texture");this.addInput("B","Texture");this.addInput("A","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,R:1,G:1,B:1,A:1};this._color=vec4.create();this._uniforms={u_textureR:0,u_textureG:1,u_textureB:2,u_textureA:3,u_color:this._color}}function b(){this.addOutput("Texture","Texture");this._tex_color=vec4.create();this.properties={color:vec4.create(),precision:c.DEFAULT}}function d(){this.addInput("A", "color");this.addInput("B","color");this.addOutput("Texture","Texture");this.properties={angle:0,scale:1,A:[0,0,0],B:[1,1,1],texture_size:32};d._shader||(d._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,d.pixel_shader));this._uniforms={u_angle:0,u_colorA:vec3.create(),u_colorB:vec3.create()}}function f(){this.addInput("A","Texture");this.addInput("B","Texture");this.addInput("Mixer","Texture");this.addOutput("Texture","Texture");this.properties={factor:0.5,size_from_biggest:!0,invert:!1,precision:c.DEFAULT}; -this._uniforms={u_textureA:0,u_textureB:1,u_textureMix:2,u_mix:vec4.create()}}function g(){this.addInput("Tex.","Texture");this.addOutput("Edges","Texture");this.properties={invert:!0,threshold:!1,factor:1,precision:c.DEFAULT};g._shader||(g._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,g.pixel_shader))}function s(){this.addInput("Texture","Texture");this.addInput("Distance","number");this.addInput("Range","number");this.addOutput("Texture","Texture");this.properties={distance:100,range:50,only_depth:!1, -high_precision:!1};this._uniforms={u_texture:0,u_distance:100,u_range:50,u_camera_planes:null}}function q(){this.addInput("Texture","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,invert:!1};this._uniforms={u_texture:0,u_camera_planes:null,u_ires:vec2.create()}}function A(){this.addInput("Texture","Texture");this.addInput("Iterations","number");this.addInput("Intensity","number");this.addOutput("Blurred","Texture");this.properties={intensity:1,iterations:1,preserve_aspect:!1, -scale:[1,1],precision:c.DEFAULT}}function t(){this.addInput("in","Texture");this.addInput("dirt","Texture");this.addOutput("out","Texture");this.addOutput("glow","Texture");this.properties={enabled:!0,intensity:1,persistence:0.99,iterations:16,threshold:0,scale:1,dirt_factor:0.5,precision:c.DEFAULT};this._textures=[];this._uniforms={u_intensity:1,u_texture:0,u_glow_texture:1,u_threshold:0,u_texel_size:vec2.create()}}function K(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture"); -this.properties={intensity:1,radius:5}}function G(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={sigma:1.4,k:1.6,p:21.7,epsilon:79,phi:0.017}}function v(){this.addOutput("Webcam","Texture");this.properties={texture_name:"",facingMode:"user"};this.boxcolor="black";this.version=0}function N(){this.addInput("in","Texture");this.addInput("f","number");this.addOutput("out","Texture");this.properties={enabled:!0,factor:1,precision:c.LOW};this._uniforms={u_texture:0, +this._uniforms={u_textureA:0,u_textureB:1,u_textureMix:2,u_mix:vec4.create()}}function g(){this.addInput("Tex.","Texture");this.addOutput("Edges","Texture");this.properties={invert:!0,threshold:!1,factor:1,precision:c.DEFAULT};g._shader||(g._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,g.pixel_shader))}function r(){this.addInput("Texture","Texture");this.addInput("Distance","number");this.addInput("Range","number");this.addOutput("Texture","Texture");this.properties={distance:100,range:50,only_depth:!1, +high_precision:!1};this._uniforms={u_texture:0,u_distance:100,u_range:50,u_camera_planes:null}}function x(){this.addInput("Texture","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,invert:!1};this._uniforms={u_texture:0,u_camera_planes:null,u_ires:vec2.create()}}function K(){this.addInput("Texture","Texture");this.addInput("Iterations","number");this.addInput("Intensity","number");this.addOutput("Blurred","Texture");this.properties={intensity:1,iterations:1,preserve_aspect:!1, +scale:[1,1],precision:c.DEFAULT}}function q(){this.addInput("in","Texture");this.addInput("dirt","Texture");this.addOutput("out","Texture");this.addOutput("glow","Texture");this.properties={enabled:!0,intensity:1,persistence:0.99,iterations:16,threshold:0,scale:1,dirt_factor:0.5,precision:c.DEFAULT};this._textures=[];this._uniforms={u_intensity:1,u_texture:0,u_glow_texture:1,u_threshold:0,u_texel_size:vec2.create()}}function z(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture"); +this.properties={intensity:1,radius:5}}function w(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={sigma:1.4,k:1.6,p:21.7,epsilon:79,phi:0.017}}function u(){this.addOutput("Webcam","Texture");this.properties={texture_name:"",facingMode:"user"};this.boxcolor="black";this.version=0}function N(){this.addInput("in","Texture");this.addInput("f","number");this.addOutput("out","Texture");this.properties={enabled:!0,factor:1,precision:c.LOW};this._uniforms={u_texture:0, u_factor:1}}function R(){this.addInput("in","");this.properties={precision:c.LOW,width:0,height:0,channels:1};this.addOutput("out","Texture")}function J(){this.addInput("in","Texture");this.addOutput("out","Texture");this.properties={precision:c.LOW,split_channels:!1};this._values=new Uint8Array(1024);this._values.fill(255);this._curve_texture=null;this._uniforms={u_texture:0,u_curve:1,u_range:1};this._must_update=!0;this._points={RGB:[[0,0],[1,1]],R:[[0,0],[1,1]],G:[[0,0],[1,1]],B:[[0,0],[1,1]]}; this.curve_editor=null;this.addWidget("toggle","Split Channels",!1,"split_channels");this.addWidget("combo","Channel","RGB",{values:["RGB","R","G","B"]});this.curve_offset=68;this.size=[240,160]}function P(){this.addInput("in","Texture");this.addInput("exp","number");this.addOutput("out","Texture");this.properties={exposition:1,precision:c.LOW};this._uniforms={u_texture:0,u_exposition:1}}function L(){this.addInput("in","Texture");this.addInput("avg","number,Texture");this.addOutput("out","Texture"); this.properties={enabled:!0,scale:1,gamma:1,average_lum:1,lum_white:1,precision:c.LOW};this._uniforms={u_texture:0,u_lumwhite2:1,u_igamma:1,u_scale:1,u_average_lum:1}}function O(){this.addOutput("out","Texture");this.properties={width:512,height:512,seed:0,persistence:0.1,octaves:8,scale:1,offset:[0,0],amplitude:1,precision:c.DEFAULT};this._key=0;this._texture=null;this._uniforms={u_persistence:0.1,u_seed:0,u_offset:vec2.create(),u_scale:1,u_viewport:vec2.create()}}function M(){this.addInput("v"); -this.addOutput("out","Texture");this.properties={code:M.default_code,width:512,height:512,clear:!0,precision:c.DEFAULT,use_html_canvas:!1};this._temp_texture=this._func=null;this.compileCode()}function Q(){this.addInput("in","Texture");this.addOutput("out","Texture");this.properties={key_color:vec3.fromValues(0,1,0),threshold:0.8,slope:0.2,precision:c.DEFAULT}}function S(){this.addInput("in","texture");this.addInput("yaw","number");this.addOutput("out","texture");this.properties={yaw:0}}var H=C.LiteGraph, -U=C.LGraphCanvas;C.LGraphTexture=null;"undefined"!=typeof GL&&(U.link_type_colors.Texture="#987",C.LGraphTexture=c,c.title="Texture",c.desc="Texture",c.widgets_info={name:{widget:"texture"},filter:{widget:"checkbox"}},c.loadTextureCallback=null,c.image_preview_size=256,c.PASS_THROUGH=1,c.COPY=2,c.LOW=3,c.HIGH=4,c.REUSE=5,c.DEFAULT=2,c.MODE_VALUES={"pass through":c.PASS_THROUGH,copy:c.COPY,low:c.LOW,high:c.HIGH,reuse:c.REUSE,"default":c.DEFAULT},c.getTexturesContainer=function(){return gl.textures}, -c.loadTexture=function(a,b){b=b||{};var d=a;"http://"==d.substr(0,7)&&H.proxy&&(d=H.proxy+d.substr(7));return c.getTexturesContainer()[a]=GL.Texture.fromURL(d,b)},c.getTexture=function(a){var b=this.getTexturesContainer();if(!b)throw"Cannot load texture, container of textures not found";b=b[a];return!b&&a&&":"!=a[0]?this.loadTexture(a):b},c.getTargetTexture=function(a,b,d){if(!a)throw"LGraphTexture.getTargetTexture expects a reference texture";var f=null;switch(d){case c.LOW:f=gl.UNSIGNED_BYTE;break; -case c.HIGH:f=gl.HIGH_PRECISION_FORMAT;break;case c.REUSE:return a;default:f=a?a.type:gl.UNSIGNED_BYTE}b&&b.width==a.width&&b.height==a.height&&b.type==f||(b=new GL.Texture(a.width,a.height,{type:f,format:gl.RGBA,filter:gl.LINEAR}));return b},c.getTextureType=function(a,b){var d=b?b.type:gl.UNSIGNED_BYTE;switch(a){case c.HIGH:d=gl.HIGH_PRECISION_FORMAT;break;case c.LOW:d=gl.UNSIGNED_BYTE}return d},c.getWhiteTexture=function(){return this._white_texture?this._white_texture:this._white_texture=GL.Texture.fromMemory(1, -1,[255,255,255,255],{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.getNoiseTexture=function(){if(this._noise_texture)return this._noise_texture;for(var a=new Uint8Array(1048576),b=0;1048576>b;++b)a[b]=255*Math.random();return this._noise_texture=a=GL.Texture.fromMemory(512,512,a,{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.prototype.onDropFile=function(a,b,d){if(a){var c=null;"string"==typeof a?c=GL.Texture.fromURL(a):-1!=b.toLowerCase().indexOf(".dds")?c=GL.Texture.fromDDSInMemory(a): +this.addOutput("out","Texture");this.properties={code:M.default_code,width:512,height:512,clear:!0,precision:c.DEFAULT,use_html_canvas:!1};this._temp_texture=this._func=null;this.compileCode()}function Q(){this.addInput("in","Texture");this.addOutput("out","Texture");this.properties={key_color:vec3.fromValues(0,1,0),threshold:0.8,slope:0.2,precision:c.DEFAULT}}function S(){this.addInput("in","texture");this.addInput("yaw","number");this.addOutput("out","texture");this.properties={yaw:0}}var I=D.LiteGraph, +U=D.LGraphCanvas;D.LGraphTexture=null;"undefined"!=typeof GL&&(U.link_type_colors.Texture="#987",D.LGraphTexture=c,c.title="Texture",c.desc="Texture",c.widgets_info={name:{widget:"texture"},filter:{widget:"checkbox"}},c.loadTextureCallback=null,c.image_preview_size=256,c.UNDEFINED=0,c.PASS_THROUGH=1,c.COPY=2,c.LOW=3,c.HIGH=4,c.REUSE=5,c.DEFAULT=2,c.MODE_VALUES={undefined:c.UNDEFINED,"pass through":c.PASS_THROUGH,copy:c.COPY,low:c.LOW,high:c.HIGH,reuse:c.REUSE,"default":c.DEFAULT},c.getTexturesContainer= +function(){return gl.textures},c.loadTexture=function(a,b){b=b||{};var d=a;"http://"==d.substr(0,7)&&I.proxy&&(d=I.proxy+d.substr(7));return c.getTexturesContainer()[a]=GL.Texture.fromURL(d,b)},c.getTexture=function(a){var b=this.getTexturesContainer();if(!b)throw"Cannot load texture, container of textures not found";b=b[a];return!b&&a&&":"!=a[0]?this.loadTexture(a):b},c.getTargetTexture=function(a,b,d){if(!a)throw"LGraphTexture.getTargetTexture expects a reference texture";var f=null;switch(d){case c.LOW:f= +gl.UNSIGNED_BYTE;break;case c.HIGH:f=gl.HIGH_PRECISION_FORMAT;break;case c.REUSE:return a;default:f=a?a.type:gl.UNSIGNED_BYTE}b&&b.width==a.width&&b.height==a.height&&b.type==f||(b=new GL.Texture(a.width,a.height,{type:f,format:gl.RGBA,filter:gl.LINEAR}));return b},c.getTextureType=function(a,b){var d=b?b.type:gl.UNSIGNED_BYTE;switch(a){case c.HIGH:d=gl.HIGH_PRECISION_FORMAT;break;case c.LOW:d=gl.UNSIGNED_BYTE}return d},c.getWhiteTexture=function(){return this._white_texture?this._white_texture:this._white_texture= +GL.Texture.fromMemory(1,1,[255,255,255,255],{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.getNoiseTexture=function(){if(this._noise_texture)return this._noise_texture;for(var a=new Uint8Array(1048576),b=0;1048576>b;++b)a[b]=255*Math.random();return this._noise_texture=a=GL.Texture.fromMemory(512,512,a,{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.prototype.onDropFile=function(a,b,d){if(a){var c=null;"string"==typeof a?c=GL.Texture.fromURL(a):-1!=b.toLowerCase().indexOf(".dds")?c=GL.Texture.fromDDSInMemory(a): (a=new Blob([d]),a=URL.createObjectURL(a),c=GL.Texture.fromURL(a));this._drop_texture=c;this.properties.name=b}else this._drop_texture=null,this.properties.name=""},c.prototype.getExtraMenuOptions=function(a){var b=this;if(this._drop_texture)return[{content:"Clear",callback:function(){b._drop_texture=null;b.properties.name=""}}]},c.prototype.onExecute=function(){var a=null;this.isOutputConnected(1)&&(a=this.getInputData(0));!a&&this._drop_texture&&(a=this._drop_texture);!a&&this.properties.name&& (a=c.getTexture(this.properties.name));if(a){this._last_tex=a;!1===this.properties.filter?a.setParameter(gl.TEXTURE_MAG_FILTER,gl.NEAREST):a.setParameter(gl.TEXTURE_MAG_FILTER,gl.LINEAR);this.setOutputData(0,a);this.setOutputData(1,a.fullpath||a.filename);for(var b=2;b=this.size[1]))if(this._drop_texture&&a.webgl)a.drawImage(this._drop_texture,0,0,this.size[0],this.size[1]);else{if(this._last_preview_tex!=this._last_tex)if(a.webgl)this._canvas=this._last_tex;else{var b=c.generateLowResTexturePreview(this._last_tex);if(!b)return;this._last_preview_tex=this._last_tex;this._canvas=cloneCanvas(b)}this._canvas&& (a.save(),a.webgl||(a.translate(0,this.size[1]),a.scale(1,-1)),a.drawImage(this._canvas,0,0,this.size[0],this.size[1]),a.restore())}},c.generateLowResTexturePreview=function(a){if(!a)return null;var b=c.image_preview_size,d=a;if(a.format==gl.DEPTH_COMPONENT)return null;if(a.width>b||a.height>b)d=this._preview_temp_tex,this._preview_temp_tex||(this._preview_temp_tex=d=new GL.Texture(b,b,{minFilter:gl.NEAREST})),a.copyTo(d);a=this._preview_canvas;a||(this._preview_canvas=a=createCanvas(b,b));d&&d.toCanvas(a); -return a},c.prototype.getResources=function(a){this.properties.name&&(a[this.properties.name]=GL.Texture);return a},c.prototype.onGetInputs=function(){return[["in","Texture"]]},c.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["aspect","number"]]},c.replaceCode=function(a,b){return a.replace(/\{\{[a-zA-Z0-9_]*\}\}/g,function(a){a=a.replace(/[\{\}]/g,"");return b[a]||""})},H.registerNodeType("texture/texture",c),m.title="Preview",m.desc="Show a texture in the graph canvas", -m.allow_preview=!1,m.prototype.onDrawBackground=function(a){if(!this.flags.collapsed&&(a.webgl||m.allow_preview)){var b=this.getInputData(0);if(b){var d=null,d=!b.handle&&a.webgl?b:c.generateLowResTexturePreview(b);a.save();this.properties.flipY&&(a.translate(0,this.size[1]),a.scale(1,-1));a.drawImage(d,0,0,this.size[0],this.size[1]);a.restore()}}},H.registerNodeType("texture/preview",m),n.title="Save",n.desc="Save a texture in the repository",n.prototype.getPreviewTexture=function(){return this._texture}, -n.prototype.onExecute=function(){var a=this.getInputData(0);a&&(this.properties.generate_mipmaps&&(a.bind(0),a.setParameter(gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_LINEAR),gl.generateMipmap(a.texture_type),a.unbind(0)),this.properties.name&&(c.storeTexture?c.storeTexture(this.properties.name,a):c.getTexturesContainer()[this.properties.name]=a),this._texture=a,this.setOutputData(0,a),this.setOutputData(1,this.properties.name))},H.registerNodeType("texture/save",n),k.widgets_info={uvcode:{widget:"code"}, +return a},c.prototype.getResources=function(a){this.properties.name&&(a[this.properties.name]=GL.Texture);return a},c.prototype.onGetInputs=function(){return[["in","Texture"]]},c.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["aspect","number"]]},c.replaceCode=function(a,b){return a.replace(/\{\{[a-zA-Z0-9_]*\}\}/g,function(a){a=a.replace(/[\{\}]/g,"");return b[a]||""})},I.registerNodeType("texture/texture",c),m.title="Preview",m.desc="Show a texture in the graph canvas", +m.allow_preview=!1,m.prototype.onDrawBackground=function(a){if(!this.flags.collapsed&&(a.webgl||m.allow_preview)){var b=this.getInputData(0);if(b){var d=null,d=!b.handle&&a.webgl?b:c.generateLowResTexturePreview(b);a.save();this.properties.flipY&&(a.translate(0,this.size[1]),a.scale(1,-1));a.drawImage(d,0,0,this.size[0],this.size[1]);a.restore()}}},I.registerNodeType("texture/preview",m),n.title="Save",n.desc="Save a texture in the repository",n.prototype.getPreviewTexture=function(){return this._texture}, +n.prototype.onExecute=function(){var a=this.getInputData(0);a&&(this.properties.generate_mipmaps&&(a.bind(0),a.setParameter(gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_LINEAR),gl.generateMipmap(a.texture_type),a.unbind(0)),this.properties.name&&(c.storeTexture?c.storeTexture(this.properties.name,a):c.getTexturesContainer()[this.properties.name]=a),this._texture=a,this.setOutputData(0,a),this.setOutputData(1,this.properties.name))},I.registerNodeType("texture/save",n),k.widgets_info={uvcode:{widget:"code"}, pixelcode:{widget:"code"},precision:{widget:"combo",values:c.MODE_VALUES}},k.title="Operation",k.desc="Texture shader operation",k.presets={},k.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:b.properties.show?"Hide Texture":"Show Texture",callback:function(){b.properties.show=!b.properties.show}}]},k.prototype.onPropertyChanged=function(){this.has_error=!1},k.prototype.onDrawBackground=function(a){this.flags.collapsed||20>=this.size[1]||!this.properties.show||!this._tex||this._tex.gl!= a||(a.save(),a.drawImage(this._tex,0,0,this.size[0],this.size[1]),a.restore())},k.prototype.onExecute=function(){var a=this.getInputData(0);if(this.isOutputConnected(0))if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else{var b=this.getInputData(1);if(this.properties.uvcode||this.properties.pixelcode){var d=512,f=512;a?(d=a.width,f=a.height):b&&(d=b.width,f=b.height);b||(b=GL.Texture.getWhiteTexture());var g=c.getTextureType(this.properties.precision,a);this._tex=a||this._tex? c.getTargetTexture(a||this._tex,this._tex,this.properties.precision):new GL.Texture(d,f,{type:g,format:gl.RGBA,filter:gl.LINEAR});g="";this.properties.uvcode&&(g="uv = "+this.properties.uvcode,-1!=this.properties.uvcode.indexOf(";")&&(g=this.properties.uvcode));var e="";this.properties.pixelcode&&(e="result = "+this.properties.pixelcode,-1!=this.properties.pixelcode.indexOf(";")&&(e=this.properties.pixelcode));var h=this._shader;if(!(this.has_error||h&&this._shader_code==g+"|"+e)){var l=c.replaceCode(k.pixel_shader, -{UV_CODE:g,PIXEL_CODE:e});try{h=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,l),this.boxcolor="#00FF00"}catch(p){GL.Shader.dumpErrorToConsole(p,Shader.SCREEN_VERTEX_SHADER,l);this.boxcolor="#FF0000";this.has_error=!0;return}this._shader=h;this._shader_code=g+"|"+e}if(this._shader){var t=this.getInputData(2);null!=t?this.properties.value=t:t=parseFloat(this.properties.value);var r=this.graph.getTime();this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);gl.disable(gl.BLEND); -a&&a.bind(0);b&&b.bind(1);var c=Mesh.getScreenQuad();h.uniforms({u_texture:0,u_textureB:1,value:t,texSize:[d,f,1/d,1/f],time:r}).draw(c)});this.setOutputData(0,this._tex)}}}},k.pixel_shader="precision highp float;\n\t\t\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec4 texSize;\n\t\tuniform float time;\n\t\tuniform float value;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord;\n\t\t\t{{UV_CODE}};\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\tvec3 color = color4.rgb;\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\tvec3 colorB = color4B.rgb;\n\t\t\tvec3 result = color;\n\t\t\tfloat alpha = 1.0;\n\t\t\t{{PIXEL_CODE}};\n\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t}\n\t\t", +{UV_CODE:g,PIXEL_CODE:e});try{h=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,l),this.boxcolor="#00FF00"}catch(p){GL.Shader.dumpErrorToConsole(p,Shader.SCREEN_VERTEX_SHADER,l);this.boxcolor="#FF0000";this.has_error=!0;return}this._shader=h;this._shader_code=g+"|"+e}if(this._shader){var q=this.getInputData(2);null!=q?this.properties.value=q:q=parseFloat(this.properties.value);var z=this.graph.getTime();this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);gl.disable(gl.BLEND); +a&&a.bind(0);b&&b.bind(1);var c=Mesh.getScreenQuad();h.uniforms({u_texture:0,u_textureB:1,value:q,texSize:[d,f,1/d,1/f],time:z}).draw(c)});this.setOutputData(0,this._tex)}}}},k.pixel_shader="precision highp float;\n\t\t\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec4 texSize;\n\t\tuniform float time;\n\t\tuniform float value;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord;\n\t\t\t{{UV_CODE}};\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\tvec3 color = color4.rgb;\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\tvec3 colorB = color4B.rgb;\n\t\t\tvec3 result = color;\n\t\t\tfloat alpha = 1.0;\n\t\t\t{{PIXEL_CODE}};\n\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t}\n\t\t", k.registerPreset=function(a,b){k.presets[a]=b},k.registerPreset("",""),k.registerPreset("bypass","color"),k.registerPreset("add","color + colorB * value"),k.registerPreset("substract","(color - colorB) * value"),k.registerPreset("mate","mix( color, colorB, color4B.a * value)"),k.registerPreset("invert","vec3(1.0) - color"),k.registerPreset("multiply","color * colorB * value"),k.registerPreset("divide","(color / colorB) / value"),k.registerPreset("difference","abs(color - colorB) * value"),k.registerPreset("max", "max(color, colorB) * value"),k.registerPreset("min","min(color, colorB) * value"),k.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"),k.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"),k.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"),k.registerPreset("normalmap","\n\t\tfloat z0 = texture2D(u_texture, uv + vec2(-texSize.z, -texSize.w) ).x;\n\t\tfloat z1 = texture2D(u_texture, uv + vec2(0.0, -texSize.w) ).x;\n\t\tfloat z2 = texture2D(u_texture, uv + vec2(texSize.z, -texSize.w) ).x;\n\t\tfloat z3 = texture2D(u_texture, uv + vec2(-texSize.z, 0.0) ).x;\n\t\tfloat z4 = color.x;\n\t\tfloat z5 = texture2D(u_texture, uv + vec2(texSize.z, 0.0) ).x;\n\t\tfloat z6 = texture2D(u_texture, uv + vec2(-texSize.z, texSize.w) ).x;\n\t\tfloat z7 = texture2D(u_texture, uv + vec2(0.0, texSize.w) ).x;\n\t\tfloat z8 = texture2D(u_texture, uv + vec2(texSize.z, texSize.w) ).x;\n\t\tvec3 normal = vec3( z2 + 2.0*z4 + z7 - z0 - 2.0*z3 - z5, z5 + 2.0*z6 + z7 -z0 - 2.0*z1 - z2, 1.0 );\n\t\tnormal.xy *= value;\n\t\tresult.xyz = normalize(normal) * 0.5 + vec3(0.5);\n\t"), -k.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"),k.prototype.onInspect=function(a){var b=this;a.addCombo("Presets","",{values:Object.keys(k.presets),callback:function(d){var c=k.presets[d];c&&(b.setProperty("pixelcode",c),b.title=d,a.refresh())}})},H.registerNodeType("texture/operation",k),w.title="Shader",w.desc="Texture shader",w.widgets_info={code:{type:"code",lang:"glsl"},precision:{widget:"combo", -values:c.MODE_VALUES}},w.prototype.onPropertyChanged=function(a,b){if("code"==a){var d=this.getShader();if(d){var c=d.uniformInfo;if(this.inputs)for(var f={},g=0;g colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"),k.prototype.onInspect=function(a){var b=this;a.addCombo("Presets","",{values:Object.keys(k.presets),callback:function(d){var c=k.presets[d];c&&(b.setProperty("pixelcode",c),b.title=d,a.refresh())}})},I.registerNodeType("texture/operation",k),v.title="Shader",v.desc="Texture shader",v.widgets_info={code:{type:"code",lang:"glsl"},precision:{widget:"combo", +values:c.MODE_VALUES}},v.prototype.onPropertyChanged=function(a,b){if("code"==a){var d=this.getShader();if(d){var c=d.uniformInfo;if(this.inputs)for(var f={},g=0;g=this.size[1])){var b=this.getInputData(0);b&&a.drawImage(a==gl?b:gl.canvas,10,30,this.size[0]-20,this.size[1]-40)}},x.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this.properties.disable_alpha?gl.disable(gl.BLEND):(gl.enable(gl.BLEND),this.properties.additive?gl.blendFunc(gl.SRC_ALPHA, -gl.ONE):gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA));gl.disable(gl.DEPTH_TEST);var b=this.properties.gamma||1;this.isInputConnected(1)&&(b=this.getInputData(1));a.setParameter(gl.TEXTURE_MAG_FILTER,this.properties.filter?gl.LINEAR:gl.NEAREST);var d=x._prev_viewport;d.set(gl.viewport_data);var c=this.properties.viewport;gl.viewport(d[0]+d[2]*c[0],d[1]+d[3]*c[1],d[2]*c[2],d[3]*c[3]);gl.getViewport();this.properties.antialiasing?(x._shader||(x._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, -x.aa_pixel_shader)),c=Mesh.getScreenQuad(),a.bind(0),x._shader.uniforms({u_texture:0,uViewportSize:[a.width,a.height],u_igamma:1/b,inverseVP:[1/a.width,1/a.height]}).draw(c)):1!=b?(x._gamma_shader||(x._gamma_shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,x.gamma_pixel_shader)),a.toViewport(x._gamma_shader,{u_texture:0,u_igamma:1/b})):a.toViewport();gl.viewport(d[0],d[1],d[2],d[3])}},x.prototype.onGetInputs=function(){return[["gamma","number"]]},x.aa_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 uViewportSize;\n\t\tuniform vec2 inverseVP;\n\t\tuniform float u_igamma;\n\t\t#define FXAA_REDUCE_MIN (1.0/ 128.0)\n\t\t#define FXAA_REDUCE_MUL (1.0 / 8.0)\n\t\t#define FXAA_SPAN_MAX 8.0\n\t\t\n\t\t/* from mitsuhiko/webgl-meincraft based on the code on geeks3d.com */\n\t\tvec4 applyFXAA(sampler2D tex, vec2 fragCoord)\n\t\t{\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t/*vec2 inverseVP = vec2(1.0 / uViewportSize.x, 1.0 / uViewportSize.y);*/\n\t\t\tvec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbM = texture2D(tex, fragCoord * inverseVP).xyz;\n\t\t\tvec3 luma = vec3(0.299, 0.587, 0.114);\n\t\t\tfloat lumaNW = dot(rgbNW, luma);\n\t\t\tfloat lumaNE = dot(rgbNE, luma);\n\t\t\tfloat lumaSW = dot(rgbSW, luma);\n\t\t\tfloat lumaSE = dot(rgbSE, luma);\n\t\t\tfloat lumaM = dot(rgbM, luma);\n\t\t\tfloat lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n\t\t\tfloat lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\t\t\t\n\t\t\tvec2 dir;\n\t\t\tdir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n\t\t\tdir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\t\t\t\n\t\t\tfloat dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\t\t\t\n\t\t\tfloat rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n\t\t\tdir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * inverseVP;\n\t\t\t\n\t\t\tvec3 rgbA = 0.5 * (texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + \n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz);\n\t\t\tvec3 rgbB = rgbA * 0.5 + 0.25 * (texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + \n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz);\n\t\t\t\n\t\t\t//return vec4(rgbA,1.0);\n\t\t\tfloat lumaB = dot(rgbB, luma);\n\t\t\tif ((lumaB < lumaMin) || (lumaB > lumaMax))\n\t\t\t\tcolor = vec4(rgbA, 1.0);\n\t\t\telse\n\t\t\t\tcolor = vec4(rgbB, 1.0);\n\t\t\tif(u_igamma != 1.0)\n\t\t\t\tcolor.xyz = pow( color.xyz, vec3(u_igamma) );\n\t\t\treturn color;\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\t\t}\n\t\t", -x.gamma_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_igamma;\n\t\tvoid main() {\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\t\t gl_FragColor = color;\n\t\t}\n\t\t",H.registerNodeType("texture/toviewport",x),y.title="Copy",y.desc="Copy Texture",y.widgets_info={size:{widget:"combo",values:[0,32,64,128,256,512,1024,2048]},precision:{widget:"combo", -values:c.MODE_VALUES}},y.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)){if(a){var b=a.width,d=a.height;0!=this.properties.size&&(d=b=this.properties.size);var f=this._temp_texture,g=a.type;this.properties.precision===c.LOW?g=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(g=gl.HIGH_PRECISION_FORMAT);f&&f.width==b&&f.height==d&&f.type==g||(f=gl.LINEAR,this.properties.generate_mipmaps&&isPowerOfTwo(b)&&isPowerOfTwo(d)&&(f=gl.LINEAR_MIPMAP_LINEAR), -this._temp_texture=new GL.Texture(b,d,{type:g,format:gl.RGBA,minFilter:f,magFilter:gl.LINEAR}));a.copyTo(this._temp_texture);this.properties.generate_mipmaps&&(this._temp_texture.bind(0),gl.generateMipmap(this._temp_texture.texture_type),this._temp_texture.unbind(0))}this.setOutputData(0,this._temp_texture)}},H.registerNodeType("texture/copy",y),F.title="Downsample",F.desc="Downsample Texture",F.widgets_info={iterations:{type:"number",step:1,precision:0,min:0},precision:{widget:"combo",values:c.MODE_VALUES}}, -F.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)&&a&&a.texture_type===GL.TEXTURE_2D)if(1>this.properties.iterations)this.setOutputData(0,a);else{var b=F._shader;b||(F._shader=b=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,F.pixel_shader));var d=a.width|0,f=a.height|0,g=a.type;this.properties.precision===c.LOW?g=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(g=gl.HIGH_PRECISION_FORMAT);var e=this.properties.iterations||1,h=a,l= -null,t=[],a={type:g,format:a.format},g=vec2.create(),p={u_offset:g};this._texture&&GL.Texture.releaseTemporary(this._texture);for(var r=0;r>1||0;f=f>>1||0;l=GL.Texture.getTemporary(d,f,a);t.push(l);h.setParameter(GL.TEXTURE_MAG_FILTER,GL.NEAREST);h.copyTo(l,b,p);if(1==d&&1==f)break;h=l}this._texture=t.pop();for(r=0;r=this.size[1])){var b=this.getInputData(0);b&&a.drawImage(a==gl?b:gl.canvas,10,30,this.size[0]-20,this.size[1]-40)}},y.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this.properties.disable_alpha?gl.disable(gl.BLEND):(gl.enable(gl.BLEND),this.properties.additive?gl.blendFunc(gl.SRC_ALPHA, +gl.ONE):gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA));gl.disable(gl.DEPTH_TEST);var b=this.properties.gamma||1;this.isInputConnected(1)&&(b=this.getInputData(1));a.setParameter(gl.TEXTURE_MAG_FILTER,this.properties.filter?gl.LINEAR:gl.NEAREST);var d=y._prev_viewport;d.set(gl.viewport_data);var c=this.properties.viewport;gl.viewport(d[0]+d[2]*c[0],d[1]+d[3]*c[1],d[2]*c[2],d[3]*c[3]);gl.getViewport();this.properties.antialiasing?(y._shader||(y._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, +y.aa_pixel_shader)),c=Mesh.getScreenQuad(),a.bind(0),y._shader.uniforms({u_texture:0,uViewportSize:[a.width,a.height],u_igamma:1/b,inverseVP:[1/a.width,1/a.height]}).draw(c)):1!=b?(y._gamma_shader||(y._gamma_shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,y.gamma_pixel_shader)),a.toViewport(y._gamma_shader,{u_texture:0,u_igamma:1/b})):a.toViewport();gl.viewport(d[0],d[1],d[2],d[3])}},y.prototype.onGetInputs=function(){return[["gamma","number"]]},y.aa_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 uViewportSize;\n\t\tuniform vec2 inverseVP;\n\t\tuniform float u_igamma;\n\t\t#define FXAA_REDUCE_MIN (1.0/ 128.0)\n\t\t#define FXAA_REDUCE_MUL (1.0 / 8.0)\n\t\t#define FXAA_SPAN_MAX 8.0\n\t\t\n\t\t/* from mitsuhiko/webgl-meincraft based on the code on geeks3d.com */\n\t\tvec4 applyFXAA(sampler2D tex, vec2 fragCoord)\n\t\t{\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t/*vec2 inverseVP = vec2(1.0 / uViewportSize.x, 1.0 / uViewportSize.y);*/\n\t\t\tvec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbM = texture2D(tex, fragCoord * inverseVP).xyz;\n\t\t\tvec3 luma = vec3(0.299, 0.587, 0.114);\n\t\t\tfloat lumaNW = dot(rgbNW, luma);\n\t\t\tfloat lumaNE = dot(rgbNE, luma);\n\t\t\tfloat lumaSW = dot(rgbSW, luma);\n\t\t\tfloat lumaSE = dot(rgbSE, luma);\n\t\t\tfloat lumaM = dot(rgbM, luma);\n\t\t\tfloat lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n\t\t\tfloat lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\t\t\t\n\t\t\tvec2 dir;\n\t\t\tdir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n\t\t\tdir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\t\t\t\n\t\t\tfloat dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\t\t\t\n\t\t\tfloat rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n\t\t\tdir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * inverseVP;\n\t\t\t\n\t\t\tvec3 rgbA = 0.5 * (texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + \n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz);\n\t\t\tvec3 rgbB = rgbA * 0.5 + 0.25 * (texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + \n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz);\n\t\t\t\n\t\t\t//return vec4(rgbA,1.0);\n\t\t\tfloat lumaB = dot(rgbB, luma);\n\t\t\tif ((lumaB < lumaMin) || (lumaB > lumaMax))\n\t\t\t\tcolor = vec4(rgbA, 1.0);\n\t\t\telse\n\t\t\t\tcolor = vec4(rgbB, 1.0);\n\t\t\tif(u_igamma != 1.0)\n\t\t\t\tcolor.xyz = pow( color.xyz, vec3(u_igamma) );\n\t\t\treturn color;\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\t\t}\n\t\t", +y.gamma_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_igamma;\n\t\tvoid main() {\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\t\t gl_FragColor = color;\n\t\t}\n\t\t",I.registerNodeType("texture/toviewport",y),C.title="Copy",C.desc="Copy Texture",C.widgets_info={size:{widget:"combo",values:[0,32,64,128,256,512,1024,2048]},precision:{widget:"combo", +values:c.MODE_VALUES}},C.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)){if(a){var b=a.width,d=a.height;0!=this.properties.size&&(d=b=this.properties.size);var f=this._temp_texture,g=a.type;this.properties.precision===c.LOW?g=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(g=gl.HIGH_PRECISION_FORMAT);f&&f.width==b&&f.height==d&&f.type==g||(f=gl.LINEAR,this.properties.generate_mipmaps&&isPowerOfTwo(b)&&isPowerOfTwo(d)&&(f=gl.LINEAR_MIPMAP_LINEAR), +this._temp_texture=new GL.Texture(b,d,{type:g,format:gl.RGBA,minFilter:f,magFilter:gl.LINEAR}));a.copyTo(this._temp_texture);this.properties.generate_mipmaps&&(this._temp_texture.bind(0),gl.generateMipmap(this._temp_texture.texture_type),this._temp_texture.unbind(0))}this.setOutputData(0,this._temp_texture)}},I.registerNodeType("texture/copy",C),G.title="Downsample",G.desc="Downsample Texture",G.widgets_info={iterations:{type:"number",step:1,precision:0,min:0},precision:{widget:"combo",values:c.MODE_VALUES}}, +G.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)&&a&&a.texture_type===GL.TEXTURE_2D)if(1>this.properties.iterations)this.setOutputData(0,a);else{var b=G._shader;b||(G._shader=b=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,G.pixel_shader));var d=a.width|0,f=a.height|0,g=a.type;this.properties.precision===c.LOW?g=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(g=gl.HIGH_PRECISION_FORMAT);var e=this.properties.iterations||1,h=a,l= +null,p=[],a={type:g,format:a.format},g=vec2.create(),q={u_offset:g};this._texture&&GL.Texture.releaseTemporary(this._texture);for(var k=0;k>1||0;f=f>>1||0;l=GL.Texture.getTemporary(d,f,a);p.push(l);h.setParameter(GL.TEXTURE_MAG_FILTER,GL.NEAREST);h.copyTo(l,b,q);if(1==d&&1==f)break;h=l}this._texture=p.pop();for(k=0;k>=2,c++,1!=d););a.copyTo(this._textures_chain[0]);for(var c=1;c<=this._textures_chain.length;++c)a=this._textures_chain[c];var f=B._shader,g=this._uniforms;g.u_mipmap_offset=this.properties.mipmap_offset;gl.disable(gl.DEPTH_TEST); -gl.disable(gl.BLEND);this._temp_texture.drawTo(function(){a.toViewport(f,g)})}},B.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform mat4 u_samples_a;\n\t\tuniform mat4 u_samples_b;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_mipmap_offset;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t//random average\n\t\t\tfor(int i = 0; i < 4; ++i)\n\t\t\t\tfor(int j = 0; j < 4; ++j)\n\t\t\t\t{\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t}\n\t\t gl_FragColor = color * 0.03125;\n\t\t}\n\t\t", -E.title="Smooth",E.desc="Smooth texture over time",E.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){E._shader||(E._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,E.pixel_shader));var b=this._temp_texture;b&&b.type==a.type&&b.width==a.width&&b.height==a.height||(b={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(a.width,a.height,b),this._temp_texture2=new GL.Texture(a.width,a.height,b),a.copyTo(this._temp_texture2));var b= -this._temp_texture,d=this._temp_texture2,c=E._shader,f=this._uniforms;f.u_factor=1-this.getInputOrProperty("factor");gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);b.drawTo(function(){d.bind(1);a.toViewport(c,f)});this.setOutputData(0,b);this._temp_texture=d;this._temp_texture2=b}},E.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_factor;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tgl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\t\t}\n\t\t", -H.registerNodeType("texture/temporal_smooth",E),u.title="Lineal Avg Smooth",u.desc="Smooth texture linearly over time",u["@samples"]={type:"number",min:1,max:64,step:1,precision:1},u.prototype.getPreviewTexture=function(){return this._temp_texture2},u.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){u._shader||(u._shader_copy=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,u.pixel_shader_copy),u._shader_avg=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,u.pixel_shader_avg)); -var b=Math.clamp(this.properties.samples,0,64),d=this.frame,c=this.properties.frames_interval;if(0==c||0==d%c){d=this._temp_texture;d&&d.type==a.type&&d.width==b||(d={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(b,1,d),this._temp_texture2=new GL.Texture(b,1,d),this._temp_texture_out=new GL.Texture(1,1,d));var f=this._temp_texture,g=this._temp_texture2,e=u._shader_copy,h=u._shader_avg,l=this._uniforms;l.u_samples=b;l.u_isamples=1/b;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST); -f.drawTo(function(){g.bind(1);a.toViewport(e,l)});this._temp_texture_out.drawTo(function(){f.toViewport(h,l)});this.setOutputData(0,this._temp_texture_out);this._temp_texture=g;this._temp_texture2=f}else this.setOutputData(0,this._temp_texture_out);this.setOutputData(1,this._temp_texture2);this.frame++}},u.pixel_shader_copy="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tif( v_coord.x <= u_isamples )\n\t\t\t\tgl_FragColor = texture2D( u_texture, vec2(0.5) );\n\t\t\telse\n\t\t\t\tgl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\t\t}\n\t\t", -u.pixel_shader_avg="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform int u_samples;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\tfor(int i = 0; i < 64; ++i)\n\t\t\t{\n\t\t\t\tcolor += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\t\t\t\tif(i == (u_samples - 1))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tgl_FragColor = color * u_isamples;\n\t\t}\n\t\t",H.registerNodeType("texture/linear_avg_smooth", -u),I.title="Image to Texture",I.desc="Uploads an image to the GPU",I.prototype.onExecute=function(){var a=this.getInputData(0);if(a){var b=a.videoWidth||a.width,d=a.videoHeight||a.height;if(a.gltexture)this.setOutputData(0,a.gltexture);else{var c=this._temp_texture;c&&c.width==b&&c.height==d||(this._temp_texture=new GL.Texture(b,d,{format:gl.RGBA,filter:gl.LINEAR}));try{this._temp_texture.uploadImage(a)}catch(f){console.error("image comes from an unsafe location, cannot be uploaded to webgl: "+f); -return}this.setOutputData(0,this._temp_texture)}}},H.registerNodeType("texture/imageToTexture",I),p.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},p.title="LUT",p.desc="Apply LUT to Texture",p.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture)); +I.registerNodeType("texture/average",e),A.widgets_info={mode:{widget:"combo",values:["min","max","avg"]}},A.title="MinMax",A.desc="Compute the scene min max",A.prototype.onExecute=function(){this.properties.use_previous_frame||this.update();this.setOutputData(0,this._temp_texture);this.setOutputData(1,this._luminance)},A.prototype.onPreRenderExecute=function(){this.update()},A.prototype.update=function(){var a=this.getInputData(0);if(a&&(this.isOutputConnected(0)||this.isOutputConnected(1))){A._shader|| +(A._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,A.pixel_shader));var b=gl.UNSIGNED_BYTE;a.type!=b&&(b=gl.FLOAT);var d=512;if(!this._textures_chain.length||this._textures_chain[0].type!=b)for(;c&&(this._textures_chain[c]=new GL.Texture(d,d,{type:b,format:gl.RGBA,filter:gl.NEAREST}),d>>=2,c++,1!=d););a.copyTo(this._textures_chain[0]);for(var c=1;c<=this._textures_chain.length;++c)a=this._textures_chain[c];var f=A._shader,g=this._uniforms;g.u_mipmap_offset=this.properties.mipmap_offset;gl.disable(gl.DEPTH_TEST); +gl.disable(gl.BLEND);this._temp_texture.drawTo(function(){a.toViewport(f,g)})}},A.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform mat4 u_samples_a;\n\t\tuniform mat4 u_samples_b;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_mipmap_offset;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t//random average\n\t\t\tfor(int i = 0; i < 4; ++i)\n\t\t\t\tfor(int j = 0; j < 4; ++j)\n\t\t\t\t{\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t}\n\t\t gl_FragColor = color * 0.03125;\n\t\t}\n\t\t", +F.title="Smooth",F.desc="Smooth texture over time",F.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){F._shader||(F._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,F.pixel_shader));var b=this._temp_texture;b&&b.type==a.type&&b.width==a.width&&b.height==a.height||(b={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(a.width,a.height,b),this._temp_texture2=new GL.Texture(a.width,a.height,b),a.copyTo(this._temp_texture2));var b= +this._temp_texture,d=this._temp_texture2,c=F._shader,f=this._uniforms;f.u_factor=1-this.getInputOrProperty("factor");gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);b.drawTo(function(){d.bind(1);a.toViewport(c,f)});this.setOutputData(0,b);this._temp_texture=d;this._temp_texture2=b}},F.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_factor;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tgl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\t\t}\n\t\t", +I.registerNodeType("texture/temporal_smooth",F),t.title="Lineal Avg Smooth",t.desc="Smooth texture linearly over time",t["@samples"]={type:"number",min:1,max:64,step:1,precision:1},t.prototype.getPreviewTexture=function(){return this._temp_texture2},t.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){t._shader||(t._shader_copy=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,t.pixel_shader_copy),t._shader_avg=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,t.pixel_shader_avg)); +var b=Math.clamp(this.properties.samples,0,64),d=this.frame,c=this.properties.frames_interval;if(0==c||0==d%c){d=this._temp_texture;d&&d.type==a.type&&d.width==b||(d={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(b,1,d),this._temp_texture2=new GL.Texture(b,1,d),this._temp_texture_out=new GL.Texture(1,1,d));var f=this._temp_texture,g=this._temp_texture2,e=t._shader_copy,h=t._shader_avg,l=this._uniforms;l.u_samples=b;l.u_isamples=1/b;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST); +f.drawTo(function(){g.bind(1);a.toViewport(e,l)});this._temp_texture_out.drawTo(function(){f.toViewport(h,l)});this.setOutputData(0,this._temp_texture_out);this._temp_texture=g;this._temp_texture2=f}else this.setOutputData(0,this._temp_texture_out);this.setOutputData(1,this._temp_texture2);this.frame++}},t.pixel_shader_copy="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tif( v_coord.x <= u_isamples )\n\t\t\t\tgl_FragColor = texture2D( u_texture, vec2(0.5) );\n\t\t\telse\n\t\t\t\tgl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\t\t}\n\t\t", +t.pixel_shader_avg="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform int u_samples;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\tfor(int i = 0; i < 64; ++i)\n\t\t\t{\n\t\t\t\tcolor += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\t\t\t\tif(i == (u_samples - 1))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tgl_FragColor = color * u_isamples;\n\t\t}\n\t\t",I.registerNodeType("texture/linear_avg_smooth", +t),H.title="Image to Texture",H.desc="Uploads an image to the GPU",H.prototype.onExecute=function(){var a=this.getInputData(0);if(a){var b=a.videoWidth||a.width,d=a.videoHeight||a.height;if(a.gltexture)this.setOutputData(0,a.gltexture);else{var c=this._temp_texture;c&&c.width==b&&c.height==d||(this._temp_texture=new GL.Texture(b,d,{format:gl.RGBA,filter:gl.LINEAR}));try{this._temp_texture.uploadImage(a)}catch(f){console.error("image comes from an unsafe location, cannot be uploaded to webgl: "+f); +return}this.setOutputData(0,this._temp_texture)}}},I.registerNodeType("texture/imageToTexture",H),p.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},p.title="LUT",p.desc="Apply LUT to Texture",p.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture)); if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.bindTexture(gl.TEXTURE_2D,null);var d=this.properties.intensity;this.isInputConnected(2)&&(this.properties.intensity=d=this.getInputData(2));this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);this._tex.drawTo(function(){b.bind(1);a.toViewport(p._shader,{u_texture:0,u_textureB:1, u_amount:d})});this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}},p.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_amount;\n\t\t\n\t\tvoid main() {\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\t\t\t mediump vec2 quad1;\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\t\t\t mediump vec2 quad2;\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\t\t\t highp vec2 texPos1;\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t highp vec2 texPos2;\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\t\t}\n\t\t", -H.registerNodeType("texture/LUT",p),r.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},r.title="Encode",r.desc="Apply a texture atlas to encode a texture",r.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture));if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D, +I.registerNodeType("texture/LUT",p),s.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},s.title="Encode",s.desc="Apply a texture atlas to encode a texture",s.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture));if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER,this.properties.filter?gl.LINEAR:gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,this.properties.filter?gl.LINEAR:gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.bindTexture(gl.TEXTURE_2D,null);var d=this._uniforms;d.u_row_simbols=Math.floor(this.properties.num_row_symbols);d.u_symbol_size=this.properties.symbol_size;d.u_brightness=this.properties.brightness; -d.u_invert=this.properties.invert?1:0;d.u_colorize=this.properties.colorize?1:0;this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);d.u_res[0]=this._tex.width;d.u_res[1]=this._tex.height;this._tex.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.NEAREST);this._tex.drawTo(function(){b.bind(1);a.toViewport(r._shader,d)});this.properties.generate_mipmaps&&(this._tex.bind(0),gl.generateMipmap(this._tex.texture_type), -this._tex.unbind(0));this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}},r.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_row_simbols;\n\t\tuniform float u_symbol_size;\n\t\tuniform float u_brightness;\n\t\tuniform float u_invert;\n\t\tuniform float u_colorize;\n\t\tuniform vec2 u_res;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 total_symbols = u_res / u_symbol_size;\n\t\t\tvec2 uv = floor(v_coord * total_symbols) / total_symbols; //pixelate \n\t\t\tvec2 local_uv = mod(v_coord * u_res, u_symbol_size) / u_symbol_size;\n\t\t\tlowp vec4 textureColor = texture2D(u_texture, uv );\n\t\t\tfloat lum = clamp(u_brightness * (textureColor.x + textureColor.y + textureColor.z)/3.0,0.0,1.0);\n\t\t\tif( u_invert == 1.0 ) lum = 1.0 - lum;\n\t\t\tfloat index = floor( lum * (u_row_simbols * u_row_simbols - 1.0));\n\t\t\tfloat col = mod( index, u_row_simbols );\n\t\t\tfloat row = u_row_simbols - floor( index / u_row_simbols ) - 1.0;\n\t\t\tvec2 simbol_uv = ( vec2( col, row ) + local_uv ) / u_row_simbols;\n\t\t\tvec4 color = texture2D( u_textureB, simbol_uv );\n\t\t\tif(u_colorize == 1.0)\n\t\t\t\tcolor *= textureColor;\n\t\t\tgl_FragColor = color;\n\t\t}\n\t\t", -H.registerNodeType("texture/encode",r),l.title="Texture to Channels",l.desc="Split texture channels",l.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this._channels||(this._channels=Array(4));for(var b=gl.RGB,d=0,c=0;4>c;c++)this.isOutputConnected(c)?(this._channels[c]&&this._channels[c].width==a.width&&this._channels[c].height==a.height&&this._channels[c].type==a.type&&this._channels[c].format==b||(this._channels[c]=new GL.Texture(a.width,a.height,{type:a.type,format:b,filter:gl.LINEAR})), +d.u_invert=this.properties.invert?1:0;d.u_colorize=this.properties.colorize?1:0;this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);d.u_res[0]=this._tex.width;d.u_res[1]=this._tex.height;this._tex.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.NEAREST);this._tex.drawTo(function(){b.bind(1);a.toViewport(s._shader,d)});this.properties.generate_mipmaps&&(this._tex.bind(0),gl.generateMipmap(this._tex.texture_type), +this._tex.unbind(0));this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}},s.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_row_simbols;\n\t\tuniform float u_symbol_size;\n\t\tuniform float u_brightness;\n\t\tuniform float u_invert;\n\t\tuniform float u_colorize;\n\t\tuniform vec2 u_res;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 total_symbols = u_res / u_symbol_size;\n\t\t\tvec2 uv = floor(v_coord * total_symbols) / total_symbols; //pixelate \n\t\t\tvec2 local_uv = mod(v_coord * u_res, u_symbol_size) / u_symbol_size;\n\t\t\tlowp vec4 textureColor = texture2D(u_texture, uv );\n\t\t\tfloat lum = clamp(u_brightness * (textureColor.x + textureColor.y + textureColor.z)/3.0,0.0,1.0);\n\t\t\tif( u_invert == 1.0 ) lum = 1.0 - lum;\n\t\t\tfloat index = floor( lum * (u_row_simbols * u_row_simbols - 1.0));\n\t\t\tfloat col = mod( index, u_row_simbols );\n\t\t\tfloat row = u_row_simbols - floor( index / u_row_simbols ) - 1.0;\n\t\t\tvec2 simbol_uv = ( vec2( col, row ) + local_uv ) / u_row_simbols;\n\t\t\tvec4 color = texture2D( u_textureB, simbol_uv );\n\t\t\tif(u_colorize == 1.0)\n\t\t\t\tcolor *= textureColor;\n\t\t\tgl_FragColor = color;\n\t\t}\n\t\t", +I.registerNodeType("texture/encode",s),l.title="Texture to Channels",l.desc="Split texture channels",l.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this._channels||(this._channels=Array(4));for(var b=gl.RGB,d=0,c=0;4>c;c++)this.isOutputConnected(c)?(this._channels[c]&&this._channels[c].width==a.width&&this._channels[c].height==a.height&&this._channels[c].type==a.type&&this._channels[c].format==b||(this._channels[c]=new GL.Texture(a.width,a.height,{type:a.type,format:b,filter:gl.LINEAR})), d++):this._channels[c]=null;if(d){gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);for(var f=Mesh.getScreenQuad(),g=l._shader,e=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],c=0;4>c;c++)this._channels[c]&&(this._channels[c].drawTo(function(){a.bind(0);g.uniforms({u_texture:0,u_mask:e[c]}).draw(f)}),this.setOutputData(c,this._channels[c]))}}},l.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec4 u_mask;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\t\t}\n\t\t", -H.registerNodeType("texture/textureChannels",l),a.title="Channels to Texture",a.desc="Split texture channels",a.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},a.prototype.onExecute=function(){var b=c.getWhiteTexture(),d=this.getInputData(0)||b,f=this.getInputData(1)||b,g=this.getInputData(2)||b,e=this.getInputData(3)||b;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var h=Mesh.getScreenQuad();a._shader||(a._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,a.pixel_shader));var l=a._shader, -b=Math.max(d.width,f.width,g.width,e.width),t=Math.max(d.height,f.height,g.height,e.height),p=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._texture&&this._texture.width==b&&this._texture.height==t&&this._texture.type==p||(this._texture=new GL.Texture(b,t,{type:p,format:gl.RGBA,filter:gl.LINEAR}));b=this._color;b[0]=this.properties.R;b[1]=this.properties.G;b[2]=this.properties.B;b[3]=this.properties.A;var r=this._uniforms;this._texture.drawTo(function(){d.bind(0); -f.bind(1);g.bind(2);e.bind(3);l.uniforms(r).draw(h)});this.setOutputData(0,this._texture)},a.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureR;\n\t\tuniform sampler2D u_textureG;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform vec4 u_color;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = u_color * vec4( \t\t\t\t\ttexture2D(u_textureR, v_coord).r,\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\t\t}\n\t\t", -H.registerNodeType("texture/channelsTexture",a),b.title="Color",b.desc="Generates a 1x1 texture with a constant color",b.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},b.prototype.onDrawBackground=function(a){var b=this.properties.color;a.fillStyle="rgb("+Math.floor(255*Math.clamp(b[0],0,1))+","+Math.floor(255*Math.clamp(b[1],0,1))+","+Math.floor(255*Math.clamp(b[2],0,1))+")";this.flags.collapsed?this.boxcolor=a.fillStyle:a.fillRect(0,0,this.size[0],this.size[1])},b.prototype.onExecute= +I.registerNodeType("texture/textureChannels",l),a.title="Channels to Texture",a.desc="Split texture channels",a.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},a.prototype.onExecute=function(){var b=c.getWhiteTexture(),d=this.getInputData(0)||b,f=this.getInputData(1)||b,g=this.getInputData(2)||b,e=this.getInputData(3)||b;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var h=Mesh.getScreenQuad();a._shader||(a._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,a.pixel_shader));var l=a._shader, +b=Math.max(d.width,f.width,g.width,e.width),p=Math.max(d.height,f.height,g.height,e.height),q=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._texture&&this._texture.width==b&&this._texture.height==p&&this._texture.type==q||(this._texture=new GL.Texture(b,p,{type:q,format:gl.RGBA,filter:gl.LINEAR}));b=this._color;b[0]=this.properties.R;b[1]=this.properties.G;b[2]=this.properties.B;b[3]=this.properties.A;var k=this._uniforms;this._texture.drawTo(function(){d.bind(0); +f.bind(1);g.bind(2);e.bind(3);l.uniforms(k).draw(h)});this.setOutputData(0,this._texture)},a.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureR;\n\t\tuniform sampler2D u_textureG;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform vec4 u_color;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = u_color * vec4( \t\t\t\t\ttexture2D(u_textureR, v_coord).r,\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\t\t}\n\t\t", +I.registerNodeType("texture/channelsTexture",a),b.title="Color",b.desc="Generates a 1x1 texture with a constant color",b.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},b.prototype.onDrawBackground=function(a){var b=this.properties.color;a.fillStyle="rgb("+Math.floor(255*Math.clamp(b[0],0,1))+","+Math.floor(255*Math.clamp(b[1],0,1))+","+Math.floor(255*Math.clamp(b[2],0,1))+")";this.flags.collapsed?this.boxcolor=a.fillStyle:a.fillRect(0,0,this.size[0],this.size[1])},b.prototype.onExecute= function(){var a=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._tex&&this._tex.type==a||(this._tex=new GL.Texture(1,1,{format:gl.RGBA,type:a,minFilter:gl.NEAREST}));a=this.properties.color;if(this.inputs)for(var b=0;ba.width?b: -a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad(),h=null,l=this._uniforms;d?(h=f._shader_tex,h||(h=f._shader_tex=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,f.pixel_shader,{MIX_TEX:""}))):(h=f._shader_factor,h||(h=f._shader_factor=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,f.pixel_shader)),g=null==g?this.properties.factor:g,l.u_mix.set([g,g,g,g]));var t=this.properties.invert;this._tex.drawTo(function(){a.bind(t?1:0);b.bind(t?0:1);d&&d.bind(2); +I.registerNodeType("texture/gradient",d),f.title="Mix",f.desc="Generates a texture mixing two textures",f.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},f.prototype.onExecute=function(){var a=this.getInputData(0);if(this.isOutputConnected(0))if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else{var b=this.getInputData(1);if(a&&b){var d=this.getInputData(2),g=this.getInputData(3);this._tex=c.getTargetTexture(this.properties.size_from_biggest&&b.width>a.width?b: +a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad(),h=null,l=this._uniforms;d?(h=f._shader_tex,h||(h=f._shader_tex=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,f.pixel_shader,{MIX_TEX:""}))):(h=f._shader_factor,h||(h=f._shader_factor=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,f.pixel_shader)),g=null==g?this.properties.factor:g,l.u_mix.set([g,g,g,g]));var p=this.properties.invert;this._tex.drawTo(function(){a.bind(p?1:0);b.bind(p?0:1);d&&d.bind(2); h.uniforms(l).draw(e)});this.setOutputData(0,this._tex)}}},f.prototype.onGetInputs=function(){return[["factor","number"]]},f.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform sampler2D u_textureB;\n\t\t#ifdef MIX_TEX\n\t\t\tuniform sampler2D u_textureMix;\n\t\t#else\n\t\t\tuniform vec4 u_mix;\n\t\t#endif\n\t\t\n\t\tvoid main() {\n\t\t\t#ifdef MIX_TEX\n\t\t\t vec4 f = texture2D(u_textureMix, v_coord);\n\t\t\t#else\n\t\t\t vec4 f = u_mix;\n\t\t\t#endif\n\t\t gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\t\t}\n\t\t", -H.registerNodeType("texture/mix",f),g.title="Edges",g.desc="Detects edges",g.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},g.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else if(a){this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var b=Mesh.getScreenQuad(),d=g._shader,f=this.properties.invert,e=this.properties.factor, +I.registerNodeType("texture/mix",f),g.title="Edges",g.desc="Detects edges",g.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},g.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else if(a){this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var b=Mesh.getScreenQuad(),d=g._shader,f=this.properties.invert,e=this.properties.factor, h=this.properties.threshold?1:0;this._tex.drawTo(function(){a.bind(0);d.uniforms({u_texture:0,u_isize:[1/a.width,1/a.height],u_factor:e,u_threshold:h,u_invert:f?1:0}).draw(b)});this.setOutputData(0,this._tex)}}},g.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_isize;\n\t\tuniform int u_invert;\n\t\tuniform float u_factor;\n\t\tuniform float u_threshold;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 center = texture2D(u_texture, v_coord);\n\t\t\tvec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\t\t\tvec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\t\t\tvec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\t\t\tvec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\t\t\tvec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\t\t\tdiff *= u_factor;\n\t\t\tif(u_invert == 1)\n\t\t\t\tdiff.xyz = vec3(1.0) - diff.xyz;\n\t\t\tif( u_threshold == 0.0 )\n\t\t\t\tgl_FragColor = vec4( diff.xyz, center.a );\n\t\t\telse\n\t\t\t\tgl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\t\t}\n\t\t", -H.registerNodeType("texture/edges",g),s.title="Depth Range",s.desc="Generates a texture with a depth range",s.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a){var b=gl.UNSIGNED_BYTE;this.properties.high_precision&&(b=gl.half_float_ext?gl.HALF_FLOAT_OES:gl.FLOAT);this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&&this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGBA, -filter:gl.LINEAR}));var d=this._uniforms,b=this.properties.distance;this.isInputConnected(1)&&(b=this.getInputData(1),this.properties.distance=b);var c=this.properties.range;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.range=c);d.u_distance=b;d.u_range=c;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var f=Mesh.getScreenQuad();s._shader||(s._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,s.pixel_shader),s._shader_onlydepth=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,s.pixel_shader, -{ONLY_DEPTH:""}));var g=this.properties.only_depth?s._shader_onlydepth:s._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1,1E3];d.u_camera_planes=b;this._temp_texture.drawTo(function(){a.bind(0);g.uniforms(d).draw(f)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},s.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform float u_distance;\n\t\tuniform float u_range;\n\t\t\n\t\tfloat LinearDepth()\n\t\t{\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\t\t\tdepth = depth * 2.0 - 1.0;\n\t\t\treturn zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t\tfloat depth = LinearDepth();\n\t\t\t#ifdef ONLY_DEPTH\n\t\t\t gl_FragColor = vec4(depth);\n\t\t\t#else\n\t\t\t\tfloat diff = abs(depth * u_camera_planes.y - u_distance);\n\t\t\t\tfloat dof = 1.0;\n\t\t\t\tif(diff <= u_range)\n\t\t\t\t\tdof = diff / u_range;\n\t\t\t gl_FragColor = vec4(dof);\n\t\t\t#endif\n\t\t}\n\t\t", -H.registerNodeType("texture/depth_range",s),q.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},q.title="Linear Depth",q.desc="Creates a color texture with linear depth",q.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a&&(a.format==gl.DEPTH_COMPONENT||a.format==gl.DEPTH_STENCIL)){var b=this.properties.precision==c.HIGH?gl.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&& -this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGB,filter:gl.LINEAR}));var d=this._uniforms;d.u_invert=this.properties.invert?1:0;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var f=Mesh.getScreenQuad();q._shader||(q._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,q.pixel_shader));var g=q._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1, -1E3];d.u_camera_planes=b;d.u_ires.set([0,0]);this._temp_texture.drawTo(function(){a.bind(0);g.uniforms(d).draw(f)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},q.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform int u_invert;\n\t\tuniform vec2 u_ires;\n\t\t\n\t\tvoid main() {\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord + u_ires*0.5).x * 2.0 - 1.0;\n\t\t\tfloat f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t\tif( u_invert == 1 )\n\t\t\t\tf = 1.0 - f;\n\t\t\tgl_FragColor = vec4(vec3(f),1.0);\n\t\t}\n\t\t", -H.registerNodeType("texture/linear_depth",q),A.title="Blur",A.desc="Blur a texture",A.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},A.max_iterations=20,A.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._final_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(b=this._final_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));var d=this.properties.iterations;this.isInputConnected(1)&& -(d=this.getInputData(1),this.properties.iterations=d);d=Math.min(Math.floor(d),A.max_iterations);if(0==d)this.setOutputData(0,a);else{var c=this.properties.intensity;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.intensity=c);var f=H.camera_aspect;f||void 0===window.gl||(f=gl.canvas.height/gl.canvas.width);f||(f=1);var f=this.properties.preserve_aspect?f:1,g=this.properties.scale||[1,1];a.applyBlur(f*g[0],g[1],c,b);for(a=1;a>=1;1<(d|0)&&(d>>=1);if(2>b)break;p=h[m]=GL.Texture.getTemporary(b,d,f);q[0]=1/r.width;q[1]=1/r.height;r.blit(p,l.uniforms(e));r=p}this.isOutputConnected(2)&&(b=this._average_texture,b&&b.type==a.type&&b.format==a.format||(b=this._average_texture=new GL.Texture(1,1,{type:a.type,format:a.format,filter:gl.LINEAR})),q[0]=1/r.width,q[1]=1/r.height,e.u_intensity=s,e.u_delta=1,r.blit(b,l.uniforms(e)),this.setOutputData(2,b));gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE);e.u_intensity= -this.getInputOrProperty("persistence");e.u_delta=0.5;for(m-=2;0<=m;m--)p=h[m],h[m]=null,q[0]=1/r.width,q[1]=1/r.height,r.blit(p,l.uniforms(e)),GL.Texture.releaseTemporary(r),r=p;gl.disable(gl.BLEND);this.isOutputConnected(1)&&(h=this._glow_texture,h&&h.width==a.width&&h.height==a.height&&h.type==g&&h.format==a.format||(h=this._glow_texture=new GL.Texture(a.width,a.height,{type:g,format:a.format,filter:gl.LINEAR})),r.blit(h),this.setOutputData(1,h));if(this.isOutputConnected(0)){h=this._final_texture; -h&&h.width==a.width&&h.height==a.height&&h.type==g&&h.format==a.format||(h=this._final_texture=new GL.Texture(a.width,a.height,{type:g,format:a.format,filter:gl.LINEAR}));var A=this.getInputData(1),v=this.getInputOrProperty("dirt_factor");e.u_intensity=s;l=A?t._dirt_final_shader:t._final_shader;l||(l=A?t._dirt_final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,t.final_pixel_shader,{USE_DIRT:""}):t._final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,t.final_pixel_shader));h.drawTo(function(){a.bind(0); -r.bind(1);A&&(l.setUniform("u_dirt_factor",v),l.setUniform("u_dirt_texture",A.bind(2)));l.toViewport(e)});this.setOutputData(0,h)}GL.Texture.releaseTemporary(r)}},t.cut_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform float u_threshold;\n\tvoid main() {\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\t}",t.scale_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\t}", -t.final_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform sampler2D u_glow_texture;\n\t#ifdef USE_DIRT\n\t\tuniform sampler2D u_dirt_texture;\n\t#endif\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\tuniform float u_dirt_factor;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tvec4 glow = sampleBox( v_coord );\n\t\t#ifdef USE_DIRT\n\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\t\t#endif\n\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\t}", -H.registerNodeType("texture/glow",t),K.title="Kuwahara Filter",K.desc="Filters a texture giving an artistic oil canvas painting",K.max_radius=10,K._shaders=[],K.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));b=this.properties.radius;b=Math.min(Math.floor(b),K.max_radius);if(0== -b)this.setOutputData(0,a);else{var d=this.properties.intensity,c=H.camera_aspect;c||void 0===window.gl||(c=gl.canvas.height/gl.canvas.width);c||(c=1);c=this.properties.preserve_aspect?c:1;K._shaders[b]||(K._shaders[b]=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,K.pixel_shader,{RADIUS:b.toFixed(0)}));var f=K._shaders[b],g=GL.Mesh.getScreenQuad();a.bind(0);this._temp_texture.drawTo(function(){f.uniforms({u_texture:0,u_intensity:d,u_resolution:[a.width,a.height],u_iResolution:[1/a.width,1/a.height]}).draw(g)}); -this.setOutputData(0,this._temp_texture)}}},K.pixel_shader="\nprecision highp float;\nvarying vec2 v_coord;\nuniform sampler2D u_texture;\nuniform float u_intensity;\nuniform vec2 u_resolution;\nuniform vec2 u_iResolution;\n#ifndef RADIUS\n\t#define RADIUS 7\n#endif\nvoid main() {\n\n\tconst int radius = RADIUS;\n\tvec2 fragCoord = v_coord;\n\tvec2 src_size = u_iResolution;\n\tvec2 uv = v_coord;\n\tfloat n = float((radius + 1) * (radius + 1));\n\tint i;\n\tint j;\n\tvec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\tvec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\tvec3 c;\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm0 += c;\n\t\t\ts0 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm1 += c;\n\t\t\ts1 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm2 += c;\n\t\t\ts2 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm3 += c;\n\t\t\ts3 += c * c;\n\t\t}\n\t}\n\t\n\tfloat min_sigma2 = 1e+2;\n\tm0 /= n;\n\ts0 = abs(s0 / n - m0 * m0);\n\t\n\tfloat sigma2 = s0.r + s0.g + s0.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m0, 1.0);\n\t}\n\t\n\tm1 /= n;\n\ts1 = abs(s1 / n - m1 * m1);\n\t\n\tsigma2 = s1.r + s1.g + s1.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m1, 1.0);\n\t}\n\t\n\tm2 /= n;\n\ts2 = abs(s2 / n - m2 * m2);\n\t\n\tsigma2 = s2.r + s2.g + s2.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m2, 1.0);\n\t}\n\t\n\tm3 /= n;\n\ts3 = abs(s3 / n - m3 * m3);\n\t\n\tsigma2 = s3.r + s3.g + s3.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m3, 1.0);\n\t}\n}\n", -H.registerNodeType("texture/kuwahara",K),G.title="XDoG Filter",G.desc="Filters a texture giving an artistic ink style",G.max_radius=10,G._shaders=[],G.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));G._xdog_shader||(G._xdog_shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,G.xdog_pixel_shader)); -var d=G._xdog_shader,c=GL.Mesh.getScreenQuad(),f=this.properties.sigma,g=this.properties.k,e=this.properties.p,h=this.properties.epsilon,l=this.properties.phi;a.bind(0);this._temp_texture.drawTo(function(){d.uniforms({src:0,sigma:f,k:g,p:e,epsilon:h,phi:l,cvsWidth:a.width,cvsHeight:a.height}).draw(c)});this.setOutputData(0,this._temp_texture)}},G.xdog_pixel_shader="\nprecision highp float;\nuniform sampler2D src;\n\nuniform float cvsHeight;\nuniform float cvsWidth;\n\nuniform float sigma;\nuniform float k;\nuniform float p;\nuniform float epsilon;\nuniform float phi;\nvarying vec2 v_coord;\n\nfloat cosh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat cosH = (tmp + 1.0 / tmp) / 2.0;\n\treturn cosH;\n}\n\nfloat tanh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\treturn tanH;\n}\n\nfloat sinh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat sinH = (tmp - 1.0 / tmp) / 2.0;\n\treturn sinH;\n}\n\nvoid main(void){\n\tvec3 destColor = vec3(0.0);\n\tfloat tFrag = 1.0 / cvsHeight;\n\tfloat sFrag = 1.0 / cvsWidth;\n\tvec2 Frag = vec2(sFrag,tFrag);\n\tvec2 uv = gl_FragCoord.st;\n\tfloat twoSigmaESquared = 2.0 * sigma * sigma;\n\tfloat twoSigmaRSquared = twoSigmaESquared * k * k;\n\tint halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\tconst int MAX_NUM_ITERATION = 99999;\n\tvec2 sum = vec2(0.0);\n\tvec2 norm = vec2(0.0);\n\n\tfor(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\t\tint i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\t\tint j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\t\tfloat d = length(vec2(i,j));\n\t\tvec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\t\t\t\t\t\t\texp( -d * d / twoSigmaRSquared ));\n\n\t\tvec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\t\tnorm += kernel;\n\t\tsum += kernel * L;\n\t}\n\n\tsum /= norm;\n\n\tfloat H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\tfloat edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\tdestColor = vec3(edge);\n\tgl_FragColor = vec4(destColor, 1.0);\n}", -H.registerNodeType("texture/xDoG",G),v.title="Webcam",v.desc="Webcam texture",v.is_webcam_open=!1,v.prototype.openStream=function(){function a(d){v.is_webcam_open=!1;console.log("Webcam rejected",d);b._webcam_stream=!1;b.boxcolor="red";b.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](a);var b=this}},v.prototype.closeStream=function(){if(this._webcam_stream){var a= -this._webcam_stream.getTracks();if(a.length)for(var b=0;b=this.size[1]||!this._video||(a.save(),a.webgl?this._video_texture&&a.drawImage(this._video_texture,0,0,this.size[0],this.size[1]):a.drawImage(this._video, -0,0,this.size[0],this.size[1]),a.restore())},v.prototype.onExecute=function(){null!=this._webcam_stream||this._waiting_confirmation||this.openStream();if(this._video&&this._video.videoWidth){var a=this._video.videoWidth,b=this._video.videoHeight,d=this._video_texture;d&&d.width==a&&d.height==b||(this._video_texture=new GL.Texture(a,b,{format:gl.RGB,filter:gl.LINEAR}));this._video_texture.uploadImage(this._video);this._video_texture.version=++this.version;this.properties.texture_name&&(c.getTexturesContainer()[this.properties.texture_name]= -this._video_texture);this.setOutputData(0,this._video_texture);for(a=1;a>=1;1<(d|0)&&(d>>=1);if(2>b)break;p=h[r]=GL.Texture.getTemporary(b,d,f);s[0]=1/k.width;s[1]=1/k.height;k.blit(p,l.uniforms(e));k=p}this.isOutputConnected(2)&&(b=this._average_texture,b&&b.type==a.type&&b.format==a.format||(b=this._average_texture=new GL.Texture(1,1,{type:a.type,format:a.format,filter:gl.LINEAR})),s[0]=1/k.width,s[1]=1/k.height,e.u_intensity=u,e.u_delta=1,k.blit(b,l.uniforms(e)),this.setOutputData(2,b));gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE);e.u_intensity= +this.getInputOrProperty("persistence");e.u_delta=0.5;for(r-=2;0<=r;r--)p=h[r],h[r]=null,s[0]=1/k.width,s[1]=1/k.height,k.blit(p,l.uniforms(e)),GL.Texture.releaseTemporary(k),k=p;gl.disable(gl.BLEND);this.isOutputConnected(1)&&(h=this._glow_texture,h&&h.width==a.width&&h.height==a.height&&h.type==g&&h.format==a.format||(h=this._glow_texture=new GL.Texture(a.width,a.height,{type:g,format:a.format,filter:gl.LINEAR})),k.blit(h),this.setOutputData(1,h));if(this.isOutputConnected(0)){h=this._final_texture; +h&&h.width==a.width&&h.height==a.height&&h.type==g&&h.format==a.format||(h=this._final_texture=new GL.Texture(a.width,a.height,{type:g,format:a.format,filter:gl.LINEAR}));var w=this.getInputData(1),m=this.getInputOrProperty("dirt_factor");e.u_intensity=u;l=w?q._dirt_final_shader:q._final_shader;l||(l=w?q._dirt_final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,q.final_pixel_shader,{USE_DIRT:""}):q._final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,q.final_pixel_shader));h.drawTo(function(){a.bind(0); +k.bind(1);w&&(l.setUniform("u_dirt_factor",m),l.setUniform("u_dirt_texture",w.bind(2)));l.toViewport(e)});this.setOutputData(0,h)}GL.Texture.releaseTemporary(k)}},q.cut_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform float u_threshold;\n\tvoid main() {\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\t}",q.scale_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\t}", +q.final_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform sampler2D u_glow_texture;\n\t#ifdef USE_DIRT\n\t\tuniform sampler2D u_dirt_texture;\n\t#endif\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\tuniform float u_dirt_factor;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tvec4 glow = sampleBox( v_coord );\n\t\t#ifdef USE_DIRT\n\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\t\t#endif\n\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\t}", +I.registerNodeType("texture/glow",q),z.title="Kuwahara Filter",z.desc="Filters a texture giving an artistic oil canvas painting",z.max_radius=10,z._shaders=[],z.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));b=this.properties.radius;b=Math.min(Math.floor(b),z.max_radius);if(0== +b)this.setOutputData(0,a);else{var d=this.properties.intensity,c=I.camera_aspect;c||void 0===window.gl||(c=gl.canvas.height/gl.canvas.width);c||(c=1);c=this.properties.preserve_aspect?c:1;z._shaders[b]||(z._shaders[b]=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,z.pixel_shader,{RADIUS:b.toFixed(0)}));var f=z._shaders[b],g=GL.Mesh.getScreenQuad();a.bind(0);this._temp_texture.drawTo(function(){f.uniforms({u_texture:0,u_intensity:d,u_resolution:[a.width,a.height],u_iResolution:[1/a.width,1/a.height]}).draw(g)}); +this.setOutputData(0,this._temp_texture)}}},z.pixel_shader="\nprecision highp float;\nvarying vec2 v_coord;\nuniform sampler2D u_texture;\nuniform float u_intensity;\nuniform vec2 u_resolution;\nuniform vec2 u_iResolution;\n#ifndef RADIUS\n\t#define RADIUS 7\n#endif\nvoid main() {\n\n\tconst int radius = RADIUS;\n\tvec2 fragCoord = v_coord;\n\tvec2 src_size = u_iResolution;\n\tvec2 uv = v_coord;\n\tfloat n = float((radius + 1) * (radius + 1));\n\tint i;\n\tint j;\n\tvec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\tvec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\tvec3 c;\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm0 += c;\n\t\t\ts0 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm1 += c;\n\t\t\ts1 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm2 += c;\n\t\t\ts2 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm3 += c;\n\t\t\ts3 += c * c;\n\t\t}\n\t}\n\t\n\tfloat min_sigma2 = 1e+2;\n\tm0 /= n;\n\ts0 = abs(s0 / n - m0 * m0);\n\t\n\tfloat sigma2 = s0.r + s0.g + s0.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m0, 1.0);\n\t}\n\t\n\tm1 /= n;\n\ts1 = abs(s1 / n - m1 * m1);\n\t\n\tsigma2 = s1.r + s1.g + s1.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m1, 1.0);\n\t}\n\t\n\tm2 /= n;\n\ts2 = abs(s2 / n - m2 * m2);\n\t\n\tsigma2 = s2.r + s2.g + s2.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m2, 1.0);\n\t}\n\t\n\tm3 /= n;\n\ts3 = abs(s3 / n - m3 * m3);\n\t\n\tsigma2 = s3.r + s3.g + s3.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m3, 1.0);\n\t}\n}\n", +I.registerNodeType("texture/kuwahara",z),w.title="XDoG Filter",w.desc="Filters a texture giving an artistic ink style",w.max_radius=10,w._shaders=[],w.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));w._xdog_shader||(w._xdog_shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,w.xdog_pixel_shader)); +var d=w._xdog_shader,c=GL.Mesh.getScreenQuad(),f=this.properties.sigma,g=this.properties.k,e=this.properties.p,h=this.properties.epsilon,l=this.properties.phi;a.bind(0);this._temp_texture.drawTo(function(){d.uniforms({src:0,sigma:f,k:g,p:e,epsilon:h,phi:l,cvsWidth:a.width,cvsHeight:a.height}).draw(c)});this.setOutputData(0,this._temp_texture)}},w.xdog_pixel_shader="\nprecision highp float;\nuniform sampler2D src;\n\nuniform float cvsHeight;\nuniform float cvsWidth;\n\nuniform float sigma;\nuniform float k;\nuniform float p;\nuniform float epsilon;\nuniform float phi;\nvarying vec2 v_coord;\n\nfloat cosh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat cosH = (tmp + 1.0 / tmp) / 2.0;\n\treturn cosH;\n}\n\nfloat tanh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\treturn tanH;\n}\n\nfloat sinh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat sinH = (tmp - 1.0 / tmp) / 2.0;\n\treturn sinH;\n}\n\nvoid main(void){\n\tvec3 destColor = vec3(0.0);\n\tfloat tFrag = 1.0 / cvsHeight;\n\tfloat sFrag = 1.0 / cvsWidth;\n\tvec2 Frag = vec2(sFrag,tFrag);\n\tvec2 uv = gl_FragCoord.st;\n\tfloat twoSigmaESquared = 2.0 * sigma * sigma;\n\tfloat twoSigmaRSquared = twoSigmaESquared * k * k;\n\tint halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\tconst int MAX_NUM_ITERATION = 99999;\n\tvec2 sum = vec2(0.0);\n\tvec2 norm = vec2(0.0);\n\n\tfor(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\t\tint i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\t\tint j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\t\tfloat d = length(vec2(i,j));\n\t\tvec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\t\t\t\t\t\t\texp( -d * d / twoSigmaRSquared ));\n\n\t\tvec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\t\tnorm += kernel;\n\t\tsum += kernel * L;\n\t}\n\n\tsum /= norm;\n\n\tfloat H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\tfloat edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\tdestColor = vec3(edge);\n\tgl_FragColor = vec4(destColor, 1.0);\n}", +I.registerNodeType("texture/xDoG",w),u.title="Webcam",u.desc="Webcam texture",u.is_webcam_open=!1,u.prototype.openStream=function(){function a(d){u.is_webcam_open=!1;console.log("Webcam rejected",d);b._webcam_stream=!1;b.boxcolor="red";b.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](a);var b=this}},u.prototype.closeStream=function(){if(this._webcam_stream){var a= +this._webcam_stream.getTracks();if(a.length)for(var b=0;b=this.size[1]||!this._video||(a.save(),a.webgl?this._video_texture&&a.drawImage(this._video_texture,0,0,this.size[0],this.size[1]):a.drawImage(this._video, +0,0,this.size[0],this.size[1]),a.restore())},u.prototype.onExecute=function(){null!=this._webcam_stream||this._waiting_confirmation||this.openStream();if(this._video&&this._video.videoWidth){var a=this._video.videoWidth,b=this._video.videoHeight,d=this._video_texture;d&&d.width==a&&d.height==b||(this._video_texture=new GL.Texture(a,b,{format:gl.RGB,filter:gl.LINEAR}));this._video_texture.uploadImage(this._video);this._video_texture.version=++this.version;this.properties.texture_name&&(c.getTexturesContainer()[this.properties.texture_name]= +this._video_texture);this.setOutputData(0,this._video_texture);for(a=1;aMath.abs(d))return c[1];d=(a-c[0])/d;return c[1]*(1-d)+f[1]*d}}return 0}},J.prototype.updateCurve=function(){for(var a=this._values,b=a.length/4,d=this.properties.split_channels,c=0;cd+l.NODE_TITLE_HEIGHT&&a.drawImage(b,10,f,this.size[0]-20,this.size[1]-d-l.NODE_TITLE_HEIGHT);var f=this.size[1]- -l.NODE_TITLE_HEIGHT+0.5;c=l.isInsideRectangle(c[0],c[1],this.pos[0],this.pos[1]+f,this.size[0],l.NODE_TITLE_HEIGHT);a.fillStyle=c?"#555":"#222";a.beginPath();a.roundRect(0,f,this.size[0]+1,l.NODE_TITLE_HEIGHT,0,8);a.fill();a.textAlign="center";a.font="24px Arial";a.fillStyle=c?"#DDD":"#999";a.fillText("+",0.5*this.size[0],f+24)}};z.prototype.onMouseDown=function(a,b,d){b[1]>this.size[1]-l.NODE_TITLE_HEIGHT+0.5&&d.showSubgraphPropertiesDialog(this)};z.prototype.getExtraMenuOptions=function(a){var b= -this;return[{content:"Print Code",callback:function(){var a=b._context.computeShaderCode();console.log(a.vs_code,a.fs_code)}}]};l.registerNodeType("texture/shaderGraph",z);x.title="Uniform";x.desc="Input data for the shader";x.prototype.getTitle=function(){return this.properties.name&&this.flags.collapsed?this.properties.type+" "+this.properties.name:"Uniform"};x.prototype.onPropertyChanged=function(a,b){this.outputs[0].name=this.properties.type+" "+this.properties.name};x.prototype.onGetCode=function(a){var d= -this.properties.type;d&&("number"==d?d="float":"texture"==d&&(d="sampler2D"),-1!=b.GLSL_types.indexOf(d)&&(a.addUniform("u_"+this.properties.name,d),this.setOutputData(0,d)))};x.prototype.getOutputVarName=function(a){return"u_"+this.properties.name};m("input/uniform",x);y.title="Attribute";y.desc="Input data from mesh attribute";y.prototype.getTitle=function(){return"att. "+this.properties.name};y.prototype.onGetCode=function(a){var d=this.properties.type;d&&-1!=b.GLSL_types.indexOf(d)&&("number"== -d&&(d="float"),"coord"!=this.properties.name&&a.addCode("varying"," varying "+d+" v_"+this.properties.name+";"),this.setOutputData(0,d))};y.prototype.getOutputVarName=function(a){return"v_"+this.properties.name};m("input/attribute",y);F.title="Sampler2D";F.desc="Reads a pixel from a texture";F.prototype.onGetCode=function(a){var b=k(this,0),d=n(this),c="vec4 "+d+" = vec4(0.0);\n";if(b)var f=k(this,1)||"v_coord",c=c+(d+" = texture2D("+b+","+f+");\n");w(this,0)&&(c+="vec4 "+w(this,0)+" = "+d+";\n"); -w(this,1)&&(c+="vec3 "+w(this,1)+" = "+d+".xyz;\n");a.addCode("code",c,this.shader_destination);this.setOutputData(0,"vec4");this.setOutputData(1,"vec3")};m("texture/sampler2D",F);D.title="const";D.prototype.getTitle=function(){return this.flags.collapsed?q(this.properties.value,this.properties.type):"Const"};D.prototype.onPropertyChanged=function(a,b){"type"==a&&this.outputs[0].type!=b&&(this.disconnectOutput(0),this.outputs[0].type=b,this.widgets.length=1,this.updateWidgets())};D.prototype.updateWidgets= -function(a){var b=this;a=this.properties.value;var d={step:0.01};switch(this.properties.type){case "float":this.properties.value=0;this.addWidget("number","v",0,{step:0.01,property:"value"});break;case "vec2":this.properties.value=a&&2==a.length?[a[0],a[1]]:[0,0,0];this.addWidget("number","x",0,d,function(a){b.properties.value[0]=a});this.addWidget("number","y",0,d,function(a){b.properties.value[1]=a});break;case "vec3":this.properties.value=a&&3==a.length?[a[0],a[1],a[2]]:[0,0,0];this.addWidget("number", -"x",0,d,function(a){b.properties.value[0]=a});this.addWidget("number","y",0,d,function(a){b.properties.value[1]=a});this.addWidget("number","z",0,d,function(a){b.properties.value[2]=a});break;case "vec4":this.properties.value=a&&4==a.length?[a[0],a[1],a[2],a[3]]:[0,0,0,0];this.addWidget("number","x",0,d,function(a){b.properties.value[0]=a});this.addWidget("number","y",0,d,function(a){b.properties.value[1]=a});this.addWidget("number","z",0,d,function(a){b.properties.value[2]=a});this.addWidget("number", -"w",0,d,function(a){b.properties.value[3]=a});break;default:console.error("unknown type for constant")}};D.prototype.onGetCode=function(a){var b=q(this.properties.value,this.properties.type),d=w(this,0);d&&(a.addCode("code","\t"+this.properties.type+" "+d+" = "+b+";",this.shader_destination),this.setOutputData(0,this.properties.type))};m("const/const",D);e.title="vec2";e.varmodes=["xy","x","y"];e.prototype.onPropertyChanged=function(){this.graph._version++};e.prototype.onGetCode=function(a){for(var b= -this.properties,c=n(this),b="\tvec2 "+c+" = "+q([b.x,b.y])+";\n",f=0;fd;++d)b.push({name:k(this,d),type:this.getInputData(d)||"float"});var c=w(this,0);if(c){var f=g[this.properties.func];if(f){var d=b[0].type,e=f.return_type;"T"==e&&(e=d);for(var h=[],d=0;d=g;){e=0.5*(h+g)|0;c=a[e];if(c==d)break;if(g==h-1)return g;cd+l.NODE_TITLE_HEIGHT&&a.drawImage(b,10,f,this.size[0]-20,this.size[1]-d-l.NODE_TITLE_HEIGHT);var f=this.size[1]-l.NODE_TITLE_HEIGHT+0.5;c=l.isInsideRectangle(c[0],c[1], +this.pos[0],this.pos[1]+f,this.size[0],l.NODE_TITLE_HEIGHT);a.fillStyle=c?"#555":"#222";a.beginPath();a.roundRect(0,f,this.size[0]+1,l.NODE_TITLE_HEIGHT,0,8);a.fill();a.textAlign="center";a.font="24px Arial";a.fillStyle=c?"#DDD":"#999";a.fillText("+",0.5*this.size[0],f+24)}};B.prototype.onMouseDown=function(a,b,d){b[1]>this.size[1]-l.NODE_TITLE_HEIGHT+0.5&&d.showSubgraphPropertiesDialog(this)};B.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:"Print Code",callback:function(){var a= +b._context.computeShaderCode();console.log(a.vs_code,a.fs_code)}}]};l.registerNodeType("texture/shaderGraph",B);y.title="Uniform";y.desc="Input data for the shader";y.prototype.getTitle=function(){return this.properties.name&&this.flags.collapsed?this.properties.type+" "+this.properties.name:"Uniform"};y.prototype.onPropertyChanged=function(a,b){this.outputs[0].name=this.properties.type+" "+this.properties.name};y.prototype.onGetCode=function(a){var d=this.properties.type;d&&("number"==d?d="float": +"texture"==d&&(d="sampler2D"),-1!=b.GLSL_types.indexOf(d)&&(a.addUniform("u_"+this.properties.name,d),this.setOutputData(0,d)))};y.prototype.getOutputVarName=function(a){return"u_"+this.properties.name};m("input/uniform",y);C.title="Attribute";C.desc="Input data from mesh attribute";C.prototype.getTitle=function(){return"att. "+this.properties.name};C.prototype.onGetCode=function(a){var d=this.properties.type;d&&-1!=b.GLSL_types.indexOf(d)&&("number"==d&&(d="float"),"coord"!=this.properties.name&& +a.addCode("varying"," varying "+d+" v_"+this.properties.name+";"),this.setOutputData(0,d))};C.prototype.getOutputVarName=function(a){return"v_"+this.properties.name};m("input/attribute",C);G.title="Sampler2D";G.desc="Reads a pixel from a texture";G.prototype.onGetCode=function(a){var b=k(this,0),d=n(this),c="vec4 "+d+" = vec4(0.0);\n";if(b)var f=k(this,1)||"v_coord",c=c+(d+" = texture2D("+b+","+f+");\n");v(this,0)&&(c+="vec4 "+v(this,0)+" = "+d+";\n");v(this,1)&&(c+="vec3 "+v(this,1)+" = "+d+".xyz;\n"); +a.addCode("code",c,this.shader_destination);this.setOutputData(0,"vec4");this.setOutputData(1,"vec3")};m("texture/sampler2D",G);E.title="const";E.prototype.getTitle=function(){return this.flags.collapsed?x(this.properties.value,this.properties.type):"Const"};E.prototype.onPropertyChanged=function(a,b){"type"==a&&this.outputs[0].type!=b&&(this.disconnectOutput(0),this.outputs[0].type=b,this.widgets.length=1,this.updateWidgets())};E.prototype.updateWidgets=function(a){var b=this;a=this.properties.value; +var d={step:0.01};switch(this.properties.type){case "float":this.properties.value=0;this.addWidget("number","v",0,{step:0.01,property:"value"});break;case "vec2":this.properties.value=a&&2==a.length?[a[0],a[1]]:[0,0,0];this.addWidget("number","x",0,d,function(a){b.properties.value[0]=a});this.addWidget("number","y",0,d,function(a){b.properties.value[1]=a});break;case "vec3":this.properties.value=a&&3==a.length?[a[0],a[1],a[2]]:[0,0,0];this.addWidget("number","x",0,d,function(a){b.properties.value[0]= +a});this.addWidget("number","y",0,d,function(a){b.properties.value[1]=a});this.addWidget("number","z",0,d,function(a){b.properties.value[2]=a});break;case "vec4":this.properties.value=a&&4==a.length?[a[0],a[1],a[2],a[3]]:[0,0,0,0];this.addWidget("number","x",0,d,function(a){b.properties.value[0]=a});this.addWidget("number","y",0,d,function(a){b.properties.value[1]=a});this.addWidget("number","z",0,d,function(a){b.properties.value[2]=a});this.addWidget("number","w",0,d,function(a){b.properties.value[3]= +a});break;default:console.error("unknown type for constant")}};E.prototype.onGetCode=function(a){var b=x(this.properties.value,this.properties.type),d=v(this,0);d&&(a.addCode("code","\t"+this.properties.type+" "+d+" = "+b+";",this.shader_destination),this.setOutputData(0,this.properties.type))};m("const/const",E);e.title="vec2";e.varmodes=["xy","x","y"];e.prototype.onPropertyChanged=function(){this.graph._version++};e.prototype.onGetCode=function(a){for(var b=this.properties,c=n(this),b="\tvec2 "+ +c+" = "+x([b.x,b.y])+";\n",f=0;fd;++d)b.push({name:k(this,d),type:this.getInputData(d)||"float"});var c=v(this,0);if(c){var f=g[this.properties.func];if(f){var e=b[0].type,h=f.return_type;"T"==h&&(h=e);for(var l=[],d=0;d=g;){e=0.5*(h+g)|0;c=a[e];if(c==d)break;if(g==h-1)return g;ca&&(a=1);this.points&&this.points.length==3*a||(this.points=new Float32Array(3*a));this.properties.generate_normals?this.normals&&this.normals.length==this.points.length||(this.normals=new Float32Array(this.points.length)):this.normals=null;var d=this._last_radius||this.properties.radius,c=this.properties.mode,g=this.getInputData(0);this._old_obj_version=g?g._version:null;this.points=m.generatePoints(d,a,c,this.points, -this.normals,this.properties.regular,g);this.version++};m.generatePoints=function(a,d,c,g,e,h,l){var p=3*d;g&&g.length==p||(g=new Float32Array(p));var r=new Float32Array(3),k=new Float32Array([0,1,0]);if(h)if(c==m.RECTANGLE){p=Math.floor(Math.sqrt(d));for(d=0;de||wh&&hl))break}this.geometry.indices=this.indices=new Uint32Array(p)}this.indices&&this.indices.length?(this.geometry.indices=this.indices,this.setOutputData(0,this.geometry)):this.setOutputData(0,null)}};u.registerNodeType("geometry/connectPoints",y);"undefined"!=typeof GL&&(F.title="to geometry",F.desc="converts a mesh to geometry",F.prototype.onExecute= -function(){var a=this.getInputData(0);if(a){if(a!=this.last_mesh){this.last_mesh=a;for(i in a.vertexBuffers)this.geometry[i]=a.vertexBuffers[i].data;a.indexBuffers.triangles&&(this.geometry.indices=a.indexBuffers.triangles.data);this.geometry._id=c();this.geometry._version=0}this.setOutputData(0,this.geometry);this.geometry&&this.setOutputData(1,this.geometry.vertices)}},u.registerNodeType("geometry/toGeometry",F),D.title="Geo to Mesh",D.prototype.updateMesh=function(a){this.mesh||(this.mesh=new GL.Mesh); +h=this.vertices;h&&this.vertices.length==a.vertices.length?h.set(a.vertices):h=this.vertices=new Float32Array(a.vertices);for(g=0;ge||vh&&hl))break}this.geometry.indices=this.indices=new Uint32Array(p)}this.indices&&this.indices.length?(this.geometry.indices=this.indices,this.setOutputData(0,this.geometry)):this.setOutputData(0,null)}};t.registerNodeType("geometry/connectPoints",C);"undefined"!=typeof GL&&(G.title="to geometry",G.desc="converts a mesh to geometry",G.prototype.onExecute= +function(){var a=this.getInputData(0);if(a){if(a!=this.last_mesh){this.last_mesh=a;for(i in a.vertexBuffers)this.geometry[i]=a.vertexBuffers[i].data;a.indexBuffers.triangles&&(this.geometry.indices=a.indexBuffers.triangles.data);this.geometry._id=c();this.geometry._version=0}this.setOutputData(0,this.geometry);this.geometry&&this.setOutputData(1,this.geometry.vertices)}},t.registerNodeType("geometry/toGeometry",G),E.title="Geo to Mesh",E.prototype.updateMesh=function(a){this.mesh||(this.mesh=new GL.Mesh); for(var d in a)if("_"!=d[0]){var c=a[d],g=GL.Mesh.common_buffers[d];if(g||"indices"==d){var g=g?g.spacing:3,e=this.mesh.vertexBuffers[d];e&&e.data.length==c.length?(e.data.set(c),e.upload(GL.DYNAMIC_DRAW)):e=new GL.Buffer("indices"==d?GL.ELEMENT_ARRAY_BUFFER:GL.ARRAY_BUFFER,c,g,GL.DYNAMIC_DRAW);this.mesh.addBuffer(d,e)}}if(this.mesh.vertexBuffers.normals&&this.mesh.vertexBuffers.normals.data.length!=this.mesh.vertexBuffers.vertices.data.length){c=new Float32Array([0,1,0]);g=new Float32Array(this.mesh.vertexBuffers.vertices.data.length); -for(d=0;d=c.NOTEON||h<=c.NOTEOFF)this.channel=e&15};Object.defineProperty(c.prototype,"velocity",{get:function(){return this.cmd==c.NOTEON?this.data[2]:-1},set:function(c){this.data[2]=c},enumerable:!0});c.notes="A A# B C C# D D# E F F# G G#".split(" ");c.note_to_index={A:0,"A#":1,B:2,C:3,"C#":4,D:5,"D#":6,E:7,F:8,"F#":9,G:10,"G#":11};Object.defineProperty(c.prototype,"note",{get:function(){return this.cmd!= c.NOTEON?-1:c.toNoteString(this.data[1],!0)},set:function(c){throw"notes cannot be assigned this way, must modify the data[1]";},enumerable:!0});Object.defineProperty(c.prototype,"octave",{get:function(){return this.cmd!=c.NOTEON?-1:Math.floor((this.data[1]-24)/12+1)},set:function(c){throw"octave cannot be assigned this way, must modify the data[1]";},enumerable:!0});c.prototype.getPitch=function(){return 440*Math.pow(2,(this.data[1]-69)/12)};c.computePitch=function(c){return 440*Math.pow(2,(c-69)/ 12)};c.prototype.getCC=function(){return this.data[1]};c.prototype.getCCValue=function(){return this.data[2]};c.prototype.getPitchBend=function(){return this.data[1]+(this.data[2]<<7)-8192};c.computePitchBend=function(c,e){return c+(e<<7)-8192};c.prototype.setCommandFromString=function(e){this.cmd=c.computeCommandFromString(e)};c.computeCommandFromString=function(e){if(!e)return 0;if(e&&e.constructor===Number)return e;e=e.toUpperCase();switch(e){case "NOTE ON":case "NOTEON":return c.NOTEON;case "NOTE OFF":case "NOTEOFF":return c.NOTEON; @@ -706,85 +712,85 @@ case "KEY PRESSURE":case "KEYPRESSURE":return c.KEYPRESSURE;case "CONTROLLER CHA l=(e-21)%12;0>l&&(l=12+l);return c.notes[l]+(h?"":a)};c.NoteStringToPitch=function(e){e=e.toUpperCase();var h=e[0],l=4;"#"==e[1]?(h+="#",2this.properties.max_value)return;this.trigger("on_midi",h)}};u.registerNodeType("midi/filter",h);z.title="MIDIEvent";z.desc="Create a MIDI Event";z.color="#243";z.prototype.onAction=function(e,h){"assign"==e?(this.properties.channel=h.channel,this.properties.cmd=h.cmd,this.properties.value1=h.data[1],this.properties.value2=h.data[2],h.cmd== -c.NOTEON?this.gate=!0:h.cmd==c.NOTEOFF&&(this.gate=!1)):(h=this.midi_event,h.channel=this.properties.channel,this.properties.cmd&&this.properties.cmd.constructor===String?h.setCommandFromString(this.properties.cmd):h.cmd=this.properties.cmd,h.data[0]=h.cmd|h.channel,h.data[1]=Number(this.properties.value1),h.data[2]=Number(this.properties.value2),this.trigger("on_midi",h))};z.prototype.onExecute=function(){var e=this.properties;if(this.inputs)for(var h=0;hthis.properties.max_value)return;this.trigger("on_midi",h)}};t.registerNodeType("midi/filter",h);B.title="MIDIEvent";B.desc="Create a MIDI Event";B.color="#243";B.prototype.onAction=function(e,h){"assign"==e?(this.properties.channel=h.channel,this.properties.cmd=h.cmd,this.properties.value1=h.data[1],this.properties.value2=h.data[2],h.cmd== +c.NOTEON?this.gate=!0:h.cmd==c.NOTEOFF&&(this.gate=!1)):(h=this.midi_event,h.channel=this.properties.channel,this.properties.cmd&&this.properties.cmd.constructor===String?h.setCommandFromString(this.properties.cmd):h.cmd=this.properties.cmd,h.data[0]=h.cmd|h.channel,h.data[1]=Number(this.properties.value1),h.data[2]=Number(this.properties.value2),this.trigger("on_midi",h))};B.prototype.onExecute=function(){var e=this.properties;if(this.inputs)for(var h=0;hc;++c)this.valid_notes[c]=-1!=this.notes_pitches.indexOf(c);for(c=0;12>c;++c)if(this.valid_notes[c])this.offset_notes[c]=0;else for(var e=1;12>e;++e){if(this.valid_notes[(c-e)%12]){this.offset_notes[c]=-e;break}if(this.valid_notes[(c+e)%12]){this.offset_notes[c]=e;break}}};D.prototype.onAction=function(e,h){h&&h.constructor===c&&(h.data[0]==c.NOTEON||h.data[0]==c.NOTEOFF?(this.midi_event=new c,this.midi_event.setup(h.data),this.midi_event.data[1]+=this.offset_notes[c.note_to_index[h.note]], -this.trigger("out",this.midi_event)):this.trigger("out",h))};D.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&c!=this._current_scale&&this.processScale(c)};u.registerNodeType("midi/quantize",D);e.title="MIDI fromFile";e.desc="Plays a MIDI file";e.color="#243";e.prototype.onAction=function(c){"play"==c?this.play():"pause"==c&&(this._playing=!this._playing)};e.prototype.onPropertyChanged=function(c,e){"url"==c&&this.loadMIDIFile(e)};e.prototype.onExecute=function(){if(this._midi&& +l)}};B.prototype.onPropertyChanged=function(e,h){"cmd"==e&&(this.properties.cmd=c.computeCommandFromString(h))};B.prototype.onGetInputs=function(){return[["cmd","number"],["note","number"],["value1","number"],["value2","number"]]};B.prototype.onGetOutputs=function(){return[["midi","midi"],["on_midi",t.EVENT],["command","number"],["note","number"],["velocity","number"],["cc","number"],["cc_value","number"],["pitch","number"],["gate","bool"],["pitchbend","number"]]};t.registerNodeType("midi/event", +B);y.title="MIDICC";y.desc="gets a Controller Change";y.color="#243";y.prototype.onExecute=function(){m.input&&(this.properties.value=m.input.state.cc[this.properties.cc]);this.setOutputData(0,this.properties.value)};t.registerNodeType("midi/cc",y);C.title="MIDI Generator";C.desc="Generates a random MIDI note";C.color="#243";C.processScale=function(e){e=e.split(",");for(var h=0;hc;++c)this.valid_notes[c]=-1!=this.notes_pitches.indexOf(c);for(c=0;12>c;++c)if(this.valid_notes[c])this.offset_notes[c]=0;else for(var e=1;12>e;++e){if(this.valid_notes[(c-e)%12]){this.offset_notes[c]=-e;break}if(this.valid_notes[(c+e)%12]){this.offset_notes[c]=e;break}}};E.prototype.onAction=function(e,h){h&&h.constructor===c&&(h.data[0]==c.NOTEON||h.data[0]==c.NOTEOFF?(this.midi_event=new c,this.midi_event.setup(h.data),this.midi_event.data[1]+=this.offset_notes[c.note_to_index[h.note]], +this.trigger("out",this.midi_event)):this.trigger("out",h))};E.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&c!=this._current_scale&&this.processScale(c)};t.registerNodeType("midi/quantize",E);e.title="MIDI fromFile";e.desc="Plays a MIDI file";e.color="#243";e.prototype.onAction=function(c){"play"==c?this.play():"pause"==c&&(this._playing=!this._playing)};e.prototype.onPropertyChanged=function(c,e){"url"==c&&this.loadMIDIFile(e)};e.prototype.onExecute=function(){if(this._midi&& this._playing){this._current_time+=this.graph.elapsed_time;for(var e=100*this._current_time,h=0;hb;b++)for(var d=0;df+g||c[1]>d))return b}}return-1};E.prototype.onAction=function(e,h){if("reset"==e)for(var l=0;lh[1])){var l=this.getKeyIndex(h);this.keys[l]=!0;this._last_key=l;var l=12*(this.properties.start_octave-1)+29+l,a=new c;a.setup([c.NOTEON,l,100]);this.trigger("note",a);return!0}};E.prototype.onMouseMove=function(e,h){if(!(0>h[1]||-1==this._last_key)){this.setDirtyCanvas(!0); -var l=this.getKeyIndex(h);if(this._last_key==l)return!0;this.keys[this._last_key]=!1;var a=12*(this.properties.start_octave-1)+29+this._last_key,b=new c;b.setup([c.NOTEOFF,a,100]);this.trigger("note",b);this.keys[l]=!0;a=12*(this.properties.start_octave-1)+29+l;b=new c;b.setup([c.NOTEON,a,100]);this.trigger("note",b);this._last_key=l;return!0}};E.prototype.onMouseUp=function(e,h){if(!(0>h[1])){var l=this.getKeyIndex(h);this.keys[l]=!1;this._last_key=-1;var l=12*(this.properties.start_octave-1)+29+ -l,a=new c;a.setup([c.NOTEOFF,l,100]);this.trigger("note",a);return!0}};u.registerNodeType("midi/keys",E)})(this); -(function(C){function c(){this.properties={src:"",gain:0.5,loop:!0,autoplay:!0,playbackRate:1};this._loading_audio=!1;this._audiobuffer=null;this._audionodes=[];this._last_sourcenode=null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=r.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain;this.properties.src&&this.loadSound(this.properties.src)}function m(){this.properties={gain:0.5};this._audionodes=[];this._media_stream= -null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=r.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain}function n(){this.properties={fftSize:2048,minDecibels:-100,maxDecibels:-10,smoothingTimeConstant:0.5};this.audionode=r.getAudioContext().createAnalyser();this.audionode.graphnode=this;this.audionode.fftSize=this.properties.fftSize;this.audionode.minDecibels=this.properties.minDecibels;this.audionode.maxDecibels= -this.properties.maxDecibels;this.audionode.smoothingTimeConstant=this.properties.smoothingTimeConstant;this.addInput("in","audio");this.addOutput("freqs","array");this.addOutput("samples","array");this._time_bin=this._freq_bin=null}function k(){this.properties={gain:1};this.audionode=r.getAudioContext().createGain();this.addInput("in","audio");this.addInput("gain","number");this.addOutput("out","audio")}function w(){this.properties={impulse_src:"",normalize:!0};this.audionode=r.getAudioContext().createConvolver(); -this.addInput("in","audio");this.addOutput("out","audio")}function h(){this.properties={threshold:-50,knee:40,ratio:12,reduction:-20,attack:0,release:0.25};this.audionode=r.getAudioContext().createDynamicsCompressor();this.addInput("in","audio");this.addOutput("out","audio")}function z(){this.properties={};this.audionode=r.getAudioContext().createWaveShaper();this.addInput("in","audio");this.addInput("shape","waveshape");this.addOutput("out","audio")}function x(){this.properties={gain1:0.5,gain2:0.5}; -this.audionode=r.getAudioContext().createGain();this.audionode1=r.getAudioContext().createGain();this.audionode1.gain.value=this.properties.gain1;this.audionode2=r.getAudioContext().createGain();this.audionode2.gain.value=this.properties.gain2;this.audionode1.connect(this.audionode);this.audionode2.connect(this.audionode);this.addInput("in1","audio");this.addInput("in1 gain","number");this.addInput("in2","audio");this.addInput("in2 gain","number");this.addOutput("out","audio")}function y(){this.properties= -{A:0.1,D:0.1,S:0.1,R:0.1};this.audionode=r.getAudioContext().createGain();this.audionode.gain.value=0;this.addInput("in","audio");this.addInput("gate","bool");this.addOutput("out","audio");this.gate=!1}function F(){this.properties={delayTime:0.5};this.audionode=r.getAudioContext().createDelay(10);this.audionode.delayTime.value=this.properties.delayTime;this.addInput("in","audio");this.addInput("time","number");this.addOutput("out","audio")}function D(){this.properties={frequency:350,detune:0,Q:1}; -this.addProperty("type","lowpass","enum",{values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")});this.audionode=r.getAudioContext().createBiquadFilter();this.addInput("in","audio");this.addOutput("out","audio")}function e(){this.properties={frequency:440,detune:0,type:"sine"};this.addProperty("type","sine","enum",{values:["sine","square","sawtooth","triangle","custom"]});this.audionode=r.getAudioContext().createOscillator();this.addOutput("out","audio")}function B(){this.properties= -{continuous:!0,mark:-1};this.addInput("data","array");this.addInput("mark","number");this.size=[300,200];this._last_buffer=null}function E(){this.properties={band:440,amplitude:1};this.addInput("freqs","array");this.addOutput("signal","number")}function u(){if(!u.default_code){var c=u.default_function.toString(),a=c.indexOf("{")+1,b=c.lastIndexOf("}");u.default_code=c.substr(a,b-a)}this.properties={code:u.default_code};c=r.getAudioContext();c.createScriptProcessor?this.audionode=c.createScriptProcessor(4096, -1,1):(console.warn("ScriptProcessorNode deprecated"),this.audionode=c.createGain());this.processCode();u._bypass_function||(u._bypass_function=this.audionode.onaudioprocess);this.addInput("in","audio");this.addOutput("out","audio")}function I(){this.audionode=r.getAudioContext().destination;this.addInput("in","audio")}var p=C.LiteGraph,r={};C.LGAudio=r;r.getAudioContext=function(){if(!this._audio_context){window.AudioContext=window.AudioContext||window.webkitAudioContext;if(!window.AudioContext)return console.error("AudioContext not supported by browser"), -null;this._audio_context=new AudioContext;this._audio_context.onmessage=function(c){console.log("msg",c)};this._audio_context.onended=function(c){console.log("ended",c)};this._audio_context.oncomplete=function(c){console.log("complete",c)}}return this._audio_context};r.connect=function(c,a){try{c.connect(a)}catch(b){console.warn("LGraphAudio:",b)}};r.disconnect=function(c,a){try{c.disconnect(a)}catch(b){console.warn("LGraphAudio:",b)}};r.changeAllAudiosConnections=function(c,a){if(c.inputs)for(var b= -0;bb;b++)for(var d=0;df+g||c[1]>d))return b}}return-1};F.prototype.onAction=function(e,h){if("reset"==e)for(var l=0;lh[1])){var l=this.getKeyIndex(h);this.keys[l]=!0;this._last_key=l;var l=12*(this.properties.start_octave-1)+29+l,a=new c;a.setup([c.NOTEON,l,100]);this.trigger("note",a);return!0}};F.prototype.onMouseMove=function(e,h){if(!(0>h[1]||-1==this._last_key)){this.setDirtyCanvas(!0); +var l=this.getKeyIndex(h);if(this._last_key==l)return!0;this.keys[this._last_key]=!1;var a=12*(this.properties.start_octave-1)+29+this._last_key,b=new c;b.setup([c.NOTEOFF,a,100]);this.trigger("note",b);this.keys[l]=!0;a=12*(this.properties.start_octave-1)+29+l;b=new c;b.setup([c.NOTEON,a,100]);this.trigger("note",b);this._last_key=l;return!0}};F.prototype.onMouseUp=function(e,h){if(!(0>h[1])){var l=this.getKeyIndex(h);this.keys[l]=!1;this._last_key=-1;var l=12*(this.properties.start_octave-1)+29+ +l,a=new c;a.setup([c.NOTEOFF,l,100]);this.trigger("note",a);return!0}};t.registerNodeType("midi/keys",F)})(this); +(function(D){function c(){this.properties={src:"",gain:0.5,loop:!0,autoplay:!0,playbackRate:1};this._loading_audio=!1;this._audiobuffer=null;this._audionodes=[];this._last_sourcenode=null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=s.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain;this.properties.src&&this.loadSound(this.properties.src)}function m(){this.properties={gain:0.5};this._audionodes=[];this._media_stream= +null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=s.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain}function n(){this.properties={fftSize:2048,minDecibels:-100,maxDecibels:-10,smoothingTimeConstant:0.5};this.audionode=s.getAudioContext().createAnalyser();this.audionode.graphnode=this;this.audionode.fftSize=this.properties.fftSize;this.audionode.minDecibels=this.properties.minDecibels;this.audionode.maxDecibels= +this.properties.maxDecibels;this.audionode.smoothingTimeConstant=this.properties.smoothingTimeConstant;this.addInput("in","audio");this.addOutput("freqs","array");this.addOutput("samples","array");this._time_bin=this._freq_bin=null}function k(){this.properties={gain:1};this.audionode=s.getAudioContext().createGain();this.addInput("in","audio");this.addInput("gain","number");this.addOutput("out","audio")}function v(){this.properties={impulse_src:"",normalize:!0};this.audionode=s.getAudioContext().createConvolver(); +this.addInput("in","audio");this.addOutput("out","audio")}function h(){this.properties={threshold:-50,knee:40,ratio:12,reduction:-20,attack:0,release:0.25};this.audionode=s.getAudioContext().createDynamicsCompressor();this.addInput("in","audio");this.addOutput("out","audio")}function B(){this.properties={};this.audionode=s.getAudioContext().createWaveShaper();this.addInput("in","audio");this.addInput("shape","waveshape");this.addOutput("out","audio")}function y(){this.properties={gain1:0.5,gain2:0.5}; +this.audionode=s.getAudioContext().createGain();this.audionode1=s.getAudioContext().createGain();this.audionode1.gain.value=this.properties.gain1;this.audionode2=s.getAudioContext().createGain();this.audionode2.gain.value=this.properties.gain2;this.audionode1.connect(this.audionode);this.audionode2.connect(this.audionode);this.addInput("in1","audio");this.addInput("in1 gain","number");this.addInput("in2","audio");this.addInput("in2 gain","number");this.addOutput("out","audio")}function C(){this.properties= +{A:0.1,D:0.1,S:0.1,R:0.1};this.audionode=s.getAudioContext().createGain();this.audionode.gain.value=0;this.addInput("in","audio");this.addInput("gate","bool");this.addOutput("out","audio");this.gate=!1}function G(){this.properties={delayTime:0.5};this.audionode=s.getAudioContext().createDelay(10);this.audionode.delayTime.value=this.properties.delayTime;this.addInput("in","audio");this.addInput("time","number");this.addOutput("out","audio")}function E(){this.properties={frequency:350,detune:0,Q:1}; +this.addProperty("type","lowpass","enum",{values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")});this.audionode=s.getAudioContext().createBiquadFilter();this.addInput("in","audio");this.addOutput("out","audio")}function e(){this.properties={frequency:440,detune:0,type:"sine"};this.addProperty("type","sine","enum",{values:["sine","square","sawtooth","triangle","custom"]});this.audionode=s.getAudioContext().createOscillator();this.addOutput("out","audio")}function A(){this.properties= +{continuous:!0,mark:-1};this.addInput("data","array");this.addInput("mark","number");this.size=[300,200];this._last_buffer=null}function F(){this.properties={band:440,amplitude:1};this.addInput("freqs","array");this.addOutput("signal","number")}function t(){if(!t.default_code){var c=t.default_function.toString(),a=c.indexOf("{")+1,b=c.lastIndexOf("}");t.default_code=c.substr(a,b-a)}this.properties={code:t.default_code};c=s.getAudioContext();c.createScriptProcessor?this.audionode=c.createScriptProcessor(4096, +1,1):(console.warn("ScriptProcessorNode deprecated"),this.audionode=c.createGain());this.processCode();t._bypass_function||(t._bypass_function=this.audionode.onaudioprocess);this.addInput("in","audio");this.addOutput("out","audio")}function H(){this.audionode=s.getAudioContext().destination;this.addInput("in","audio")}var p=D.LiteGraph,s={};D.LGAudio=s;s.getAudioContext=function(){if(!this._audio_context){window.AudioContext=window.AudioContext||window.webkitAudioContext;if(!window.AudioContext)return console.error("AudioContext not supported by browser"), +null;this._audio_context=new AudioContext;this._audio_context.onmessage=function(c){console.log("msg",c)};this._audio_context.onended=function(c){console.log("ended",c)};this._audio_context.oncomplete=function(c){console.log("complete",c)}}return this._audio_context};s.connect=function(c,a){try{c.connect(a)}catch(b){console.warn("LGraphAudio:",b)}};s.disconnect=function(c,a){try{c.disconnect(a)}catch(b){console.warn("LGraphAudio:",b)}};s.changeAllAudiosConnections=function(c,a){if(c.inputs)for(var b= +0;b=this.size[0]&&(e=this.size[0]-1),c.strokeStyle="red",c.beginPath(),c.moveTo(e,d),c.lineTo(e,0),c.stroke())}};B.title="Visualization";B.desc="Audio Visualization";p.registerNodeType("audio/visualization",B);E.prototype.onExecute=function(){if(this._freqs=this.getInputData(0)){var c=this.properties.band,a=this.getInputData(1); -void 0!==a&&(c=a);a=r.getAudioContext().sampleRate/this._freqs.length;a=c/a*2;a>=this._freqs.length?a=this._freqs[this._freqs.length-1]:(c=a|0,a-=c,a=this._freqs[c]*(1-a)+this._freqs[c+1]*a);this.setOutputData(0,a/255*this.properties.amplitude)}};E.prototype.onGetInputs=function(){return[["band","number"]]};E.title="Signal";E.desc="extract the signal of some frequency";p.registerNodeType("audio/signal",E);u.prototype.onAdded=function(c){c.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess= -this._callback)};u["@code"]={widget:"code",type:"code"};u.prototype.onStart=function(){this.audionode.onaudioprocess=this._callback};u.prototype.onStop=function(){this.audionode.onaudioprocess=u._bypass_function};u.prototype.onPause=function(){this.audionode.onaudioprocess=u._bypass_function};u.prototype.onUnpause=function(){this.audionode.onaudioprocess=this._callback};u.prototype.onExecute=function(){};u.prototype.onRemoved=function(){this.audionode.onaudioprocess=u._bypass_function};u.prototype.processCode= -function(){try{this._script=new new Function("properties",this.properties.code)(this.properties),this._old_code=this.properties.code,this._callback=this._script.onaudioprocess}catch(c){console.error("Error in onaudioprocess code",c),this._callback=u._bypass_function,this.audionode.onaudioprocess=this._callback}};u.prototype.onPropertyChanged=function(c,a){"code"==c&&(this.properties.code=a,this.processCode(),this.graph&&this.graph.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback))}; -u.default_function=function(){this.onaudioprocess=function(c){var a=c.inputBuffer;c=c.outputBuffer;for(var b=0;b=this.size[0]&&(e=this.size[0]-1),c.strokeStyle="red",c.beginPath(),c.moveTo(e,d),c.lineTo(e,0),c.stroke())}};A.title="Visualization";A.desc="Audio Visualization";p.registerNodeType("audio/visualization",A);F.prototype.onExecute=function(){if(this._freqs=this.getInputData(0)){var c=this.properties.band,a=this.getInputData(1); +void 0!==a&&(c=a);a=s.getAudioContext().sampleRate/this._freqs.length;a=c/a*2;a>=this._freqs.length?a=this._freqs[this._freqs.length-1]:(c=a|0,a-=c,a=this._freqs[c]*(1-a)+this._freqs[c+1]*a);this.setOutputData(0,a/255*this.properties.amplitude)}};F.prototype.onGetInputs=function(){return[["band","number"]]};F.title="Signal";F.desc="extract the signal of some frequency";p.registerNodeType("audio/signal",F);t.prototype.onAdded=function(c){c.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess= +this._callback)};t["@code"]={widget:"code",type:"code"};t.prototype.onStart=function(){this.audionode.onaudioprocess=this._callback};t.prototype.onStop=function(){this.audionode.onaudioprocess=t._bypass_function};t.prototype.onPause=function(){this.audionode.onaudioprocess=t._bypass_function};t.prototype.onUnpause=function(){this.audionode.onaudioprocess=this._callback};t.prototype.onExecute=function(){};t.prototype.onRemoved=function(){this.audionode.onaudioprocess=t._bypass_function};t.prototype.processCode= +function(){try{this._script=new new Function("properties",this.properties.code)(this.properties),this._old_code=this.properties.code,this._callback=this._script.onaudioprocess}catch(c){console.error("Error in onaudioprocess code",c),this._callback=t._bypass_function,this.audionode.onaudioprocess=this._callback}};t.prototype.onPropertyChanged=function(c,a){"code"==c&&(this.properties.code=a,this.processCode(),this.graph&&this.graph.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback))}; +t.default_function=function(){this.onaudioprocess=function(c){var a=c.inputBuffer;c=c.outputBuffer;for(var b=0;b Date: Mon, 20 Jul 2020 15:32:39 +0200 Subject: [PATCH 62/63] fixes in subgraph --- build/litegraph.js | 93 ++-- build/litegraph.min.js | 1109 ++++++++++++++++++++-------------------- src/litegraph.js | 14 +- src/nodes/base.js | 24 +- src/nodes/shaders.js | 54 +- 5 files changed, 656 insertions(+), 638 deletions(-) diff --git a/build/litegraph.js b/build/litegraph.js index 166f1f20c..4cf7040ed 100644 --- a/build/litegraph.js +++ b/build/litegraph.js @@ -266,6 +266,16 @@ this.registerNodeType(name, classobj); }, + /** + * Removes all previously registered node's types + */ + clearRegisteredTypes: function() { + this.registered_node_types = {}; + this.node_types_by_file_extension = {}; + this.Nodes = {}; + this.searchbox_extras = {}; + }, + /** * Adds this method to all nodetypes, existing and to be created * (You can add it to LGraphNode.prototype but then existing node types wont have it) @@ -1581,10 +1591,10 @@ return; } - node.graph.beforeChange(); + this.beforeChange(); this.inputs[name] = { name: name, type: type, value: value }; this._version++; - node.graph.afterChange(); + this.afterChange(); if (this.onInputAdded) { this.onInputAdded(name, type); @@ -11944,7 +11954,10 @@ if (typeof exports != "undefined") { var over = LiteGraph.isInsideRectangle(pos[0],pos[1],this.pos[0],this.pos[1] + y,this.size[0],LiteGraph.NODE_TITLE_HEIGHT); ctx.fillStyle = over ? "#555" : "#222"; ctx.beginPath(); - ctx.roundRect( 0, y, this.size[0]+1, LiteGraph.NODE_TITLE_HEIGHT, 0, 8); + if (this._shape == LiteGraph.BOX_SHAPE) + ctx.rect(0, y, this.size[0]+1, LiteGraph.NODE_TITLE_HEIGHT); + else + ctx.roundRect( 0, y, this.size[0]+1, LiteGraph.NODE_TITLE_HEIGHT, 0, 8); ctx.fill(); //button @@ -12226,15 +12239,21 @@ if (typeof exports != "undefined") { this.updateType(); } + //ensures the type in the node output and the type in the associated graph input are the same GraphInput.prototype.updateType = function() { var type = this.properties.type; this.type_widget.value = type; + + //update output if(this.outputs[0].type != type) { + if (!LiteGraph.isValidConnection(this.outputs[0].type,type)) + this.disconnectOutput(0); this.outputs[0].type = type; - this.disconnectOutput(0); } + + //update widget if(type == "number") { this.value_widget.type = "number"; @@ -12256,8 +12275,14 @@ if (typeof exports != "undefined") { this.value_widget.value = null; } this.properties.value = this.value_widget.value; + + //update graph + if (this.graph && this.name_in_graph) { + this.graph.changeInputType(this.name_in_graph, type); + } } + //this is executed AFTER the property has changed GraphInput.prototype.onPropertyChanged = function(name,v) { if( name == "name" ) @@ -12279,8 +12304,7 @@ if (typeof exports != "undefined") { } else if( name == "type" ) { - v = v || ""; - this.updateType(v); + this.updateType(); } else if( name == "value" ) { @@ -12357,6 +12381,8 @@ if (typeof exports != "undefined") { if (v == "action" || v == "event") { v = LiteGraph.ACTION; } + if (!LiteGraph.isValidConnection(that.inputs[0].type,v)) + that.disconnectInput(0); that.inputs[0].type = v; if (that.name_in_graph) { //already added @@ -23135,7 +23161,7 @@ void main(void){\n\ "mod": "T mod(T x,T y)", //"T mod(T x,float y)" "min": "T min(T x,T y)", "max": "T max(T x,T y)", - "clamp": "T clamp(T x,T minVal,T maxVal)", + "clamp": "T clamp(T x,T minVal = 0.0,T maxVal = 1.0)", "mix": "T mix(T x,T y,T a)", //"T mix(T x,T y,float a)" "step": "T step(T edge, T x)", //"T step(float edge, T x)" "smoothstep": "T smoothstep(T edge, T x)", //"T smoothstep(float edge, T x)" @@ -23165,8 +23191,13 @@ void main(void){\n\ var params = op.substr(index2 + 1, op.length - index2 - 2).split(","); for(var j in params) { - var p = params[j].split(" "); - params[j] = { type: p[0], name: p[1] }; + var p = params[j].split(" ").filter(function(a){ return a; }); + params[j] = { type: p[0].trim(), name: p[1].trim() }; + if(params[j].name.indexOf("=") != -1) + { + params[j].name.split("="); + } + } GLSL_functions[i] = { return_type: return_type, func: func_name, params: params }; GLSL_functions_name.push( func_name ); @@ -23642,7 +23673,10 @@ gl_FragColor = color;\n\ var over = LiteGraph.isInsideRectangle(pos[0],pos[1],this.pos[0],this.pos[1] + y,this.size[0],LiteGraph.NODE_TITLE_HEIGHT); ctx.fillStyle = over ? "#555" : "#222"; ctx.beginPath(); - ctx.roundRect( 0, y, this.size[0]+1, LiteGraph.NODE_TITLE_HEIGHT, 0, 8); + if (this._shape == LiteGraph.BOX_SHAPE) + ctx.rect(0, y, this.size[0]+1, LiteGraph.NODE_TITLE_HEIGHT); + else + ctx.roundRect( 0, y, this.size[0]+1, LiteGraph.NODE_TITLE_HEIGHT, 0, 8); ctx.fill(); //button @@ -23674,7 +23708,12 @@ gl_FragColor = color;\n\ LiteGraph.registerNodeType( "texture/shaderGraph", LGraphShaderGraph ); - //Shader Nodes *************************** + function shaderNodeFromFunction( classname, params, return_type, code ) + { + //TODO + } + + //Shader Nodes *********************************************************** //applies a shader graph to a code function LGraphShaderUniform() { @@ -24334,37 +24373,7 @@ gl_FragColor = color;\n\ })(this); -/* -// https://blog.undefinist.com/writing-a-shader-graph/ - -\sin -f,Out -float->float -{1} = sin({0}); - -\mul -A,B,Out -T,T->T -T,float->T -{2} = {0} * {1}; - -\clamp -f,min,max,Out -float,float=0,float=1->float -vec2,vec2=vec2(0.0),vec2=vec2(1.0)->vec2 -vec3,vec3=vec3(0.0),vec3=vec3(1.0)->vec3 -vec4,vec4=vec4(0.0),vec4=vec4(1.0)->vec4 -{3}=clamp({0},{1},{2}); - -\mix -A,B,f,Out -float,float,float->float -vec2,vec2,float->vec2 -vec3,vec3,float->vec3 -vec4,vec4,float->vec4 -{3} = mix({0},{1},{2}); - -*/ + (function(global) { var LiteGraph = global.LiteGraph; diff --git a/build/litegraph.min.js b/build/litegraph.min.js index 0685fb16f..5381398b1 100755 --- a/build/litegraph.min.js +++ b/build/litegraph.min.js @@ -1,105 +1,106 @@ -(function(D){function c(a){e.debug&&console.log("Graph created");this.list_of_graphcanvas=null;this.clear();a&&this.configure(a)}function m(a,b,d,f,g,r){this.id=a;this.type=b;this.origin_id=d;this.origin_slot=f;this.target_id=g;this.target_slot=r;this._data=null;this._pos=new Float32Array(2)}function n(a){this._ctor(a)}function k(a){this._ctor(a)}function v(a,b){this.offset=new Float32Array([0,0]);this.scale=1;this.max_scale=10;this.min_scale=0.1;this.onredraw=null;this.enabled=!0;this.last_mouse= -[0,0];this.element=null;this.visible_area=new Float32Array(4);a&&(this.element=a,b||this.bindEvents(a))}function h(a,b,d){d=d||{};this.background_image=h.DEFAULT_BACKGROUND_IMAGE;a&&a.constructor===String&&(a=document.querySelector(a));this.ds=new v;this.zoom_modify_alpha=!0;this.title_text_font=""+e.NODE_TEXT_SIZE+"px Arial";this.inner_text_font="normal "+e.NODE_SUBTEXT_SIZE+"px Arial";this.node_title_color=e.NODE_TITLE_COLOR;this.default_link_color=e.LINK_COLOR;this.default_connection_color={input_off:"#778", +(function(C){function c(a){e.debug&&console.log("Graph created");this.list_of_graphcanvas=null;this.clear();a&&this.configure(a)}function m(a,b,d,f,g,q){this.id=a;this.type=b;this.origin_id=d;this.origin_slot=f;this.target_id=g;this.target_slot=q;this._data=null;this._pos=new Float32Array(2)}function n(a){this._ctor(a)}function k(a){this._ctor(a)}function y(a,b){this.offset=new Float32Array([0,0]);this.scale=1;this.max_scale=10;this.min_scale=0.1;this.onredraw=null;this.enabled=!0;this.last_mouse= +[0,0];this.element=null;this.visible_area=new Float32Array(4);a&&(this.element=a,b||this.bindEvents(a))}function h(a,b,d){d=d||{};this.background_image=h.DEFAULT_BACKGROUND_IMAGE;a&&a.constructor===String&&(a=document.querySelector(a));this.ds=new y;this.zoom_modify_alpha=!0;this.title_text_font=""+e.NODE_TEXT_SIZE+"px Arial";this.inner_text_font="normal "+e.NODE_SUBTEXT_SIZE+"px Arial";this.node_title_color=e.NODE_TITLE_COLOR;this.default_link_color=e.LINK_COLOR;this.default_connection_color={input_off:"#778", input_on:"#7F7",output_off:"#778",output_on:"#7F7"};this.highquality_render=!0;this.use_gradients=!1;this.editor_alpha=1;this.pause_rendering=!1;this.clear_background=!0;this.read_only=!1;this.render_only_selected=!0;this.live_mode=!1;this.allow_searchbox=this.allow_interaction=this.allow_dragnodes=this.allow_dragcanvas=this.show_info=!0;this.drag_mode=this.allow_reconnect_links=!1;this.filter=this.dragging_rectangle=null;this.set_canvas_dirty_on_mouse_event=!0;this.always_render_background=!1;this.render_canvas_border= this.render_shadows=!0;this.render_connections_shadows=!1;this.render_connections_border=!0;this.render_connection_arrows=this.render_curved_connections=!1;this.render_collapsed_slots=!0;this.render_execution_order=!1;this.render_link_tooltip=this.render_title_colored=!0;this.links_render_mode=e.SPLINE_LINK;this.mouse=[0,0];this.canvas_mouse=this.graph_mouse=[0,0];this.onAfterChange=this.onBeforeChange=this.onConnectingChange=this.onSelectionChange=this.onNodeMoved=this.onDrawLinkTooltip=this.onDrawOverlay= -this.onDrawForeground=this.onDrawBackground=this.onMouse=this.onSearchBoxSelection=this.onSearchBox=null;this.connections_width=3;this.round_radius=8;this.over_link_center=this.node_widget=this.current_node=null;this.last_mouse_position=[0,0];this.visible_area=this.ds.visible_area;this.visible_links=[];b&&b.attachCanvas(this);this.setCanvas(a);this.clear();d.skip_render||this.startRendering();this.autoresize=d.autoresize}function B(a,b){return Math.sqrt((b[0]-a[0])*(b[0]-a[0])+(b[1]-a[1])*(b[1]-a[1]))} -function y(a,b,d,f,g,r){return da&&fb?!0:!1}function C(a,b){var d=a[0]+a[2],f=a[1]+a[3],g=b[1]+b[3];return a[0]>b[0]+b[2]||a[1]>g||dc.width-q.width-10&&(g=c.width-q.width-10);c.height&& -e>c.height-q.height-10&&(e=c.height-q.height-10)}r.style.left=g+"px";r.style.top=e+"px";b.scale&&(r.style.transform="scale("+b.scale+")")}function E(a){this.points=a;this.nearest=this.selected=-1;this.size=null;this.must_update=!0;this.margin=5}var e=D.LiteGraph={VERSION:0.4,CANVAS_GRID_SIZE:10,NODE_TITLE_HEIGHT:30,NODE_TITLE_TEXT_Y:20,NODE_SLOT_HEIGHT:20,NODE_WIDGET_HEIGHT:20,NODE_WIDTH:140,NODE_MIN_WIDTH:50,NODE_COLLAPSED_RADIUS:10,NODE_COLLAPSED_WIDTH:80,NODE_TITLE_COLOR:"#999",NODE_TEXT_SIZE:14, +this.onDrawForeground=this.onDrawBackground=this.onMouse=this.onSearchBoxSelection=this.onSearchBox=null;this.connections_width=3;this.round_radius=8;this.over_link_center=this.node_widget=this.current_node=null;this.last_mouse_position=[0,0];this.visible_area=this.ds.visible_area;this.visible_links=[];b&&b.attachCanvas(this);this.setCanvas(a);this.clear();d.skip_render||this.startRendering();this.autoresize=d.autoresize}function D(a,b){return Math.sqrt((b[0]-a[0])*(b[0]-a[0])+(b[1]-a[1])*(b[1]-a[1]))} +function x(a,b,d,f,g,q){return da&&fb?!0:!1}function B(a,b){var d=a[0]+a[2],f=a[1]+a[3],g=b[1]+b[3];return a[0]>b[0]+b[2]||a[1]>g||dc.width-r.width-10&&(g=c.width-r.width-10);c.height&& +e>c.height-r.height-10&&(e=c.height-r.height-10)}q.style.left=g+"px";q.style.top=e+"px";b.scale&&(q.style.transform="scale("+b.scale+")")}function E(a){this.points=a;this.nearest=this.selected=-1;this.size=null;this.must_update=!0;this.margin=5}var e=C.LiteGraph={VERSION:0.4,CANVAS_GRID_SIZE:10,NODE_TITLE_HEIGHT:30,NODE_TITLE_TEXT_Y:20,NODE_SLOT_HEIGHT:20,NODE_WIDGET_HEIGHT:20,NODE_WIDTH:140,NODE_MIN_WIDTH:50,NODE_COLLAPSED_RADIUS:10,NODE_COLLAPSED_WIDTH:80,NODE_TITLE_COLOR:"#999",NODE_TEXT_SIZE:14, NODE_TEXT_COLOR:"#AAA",NODE_SUBTEXT_SIZE:12,NODE_DEFAULT_COLOR:"#333",NODE_DEFAULT_BGCOLOR:"#353535",NODE_DEFAULT_BOXCOLOR:"#666",NODE_DEFAULT_SHAPE:"box",DEFAULT_SHADOW_COLOR:"rgba(0,0,0,0.5)",DEFAULT_GROUP_FONT:24,WIDGET_BGCOLOR:"#222",WIDGET_OUTLINE_COLOR:"#666",WIDGET_TEXT_COLOR:"#DDD",WIDGET_SECONDARY_TEXT_COLOR:"#999",LINK_COLOR:"#9A9",EVENT_LINK_COLOR:"#A86",CONNECTING_LINK_COLOR:"#AFA",MAX_NUMBER_OF_NODES:1E3,DEFAULT_POSITION:[100,100],VALID_SHAPES:["default","box","round","card"],BOX_SHAPE:1, ROUND_SHAPE:2,CIRCLE_SHAPE:3,CARD_SHAPE:4,ARROW_SHAPE:5,INPUT:1,OUTPUT:2,EVENT:-1,ACTION:-1,ALWAYS:0,ON_EVENT:1,NEVER:2,ON_TRIGGER:3,UP:1,DOWN:2,LEFT:3,RIGHT:4,CENTER:5,STRAIGHT_LINK:0,LINEAR_LINK:1,SPLINE_LINK:2,NORMAL_TITLE:0,NO_TITLE:1,TRANSPARENT_TITLE:2,AUTOHIDE_TITLE:3,proxy:null,node_images_path:"",debug:!1,catch_exceptions:!0,throw_errors:!0,allow_scripts:!1,registered_node_types:{},node_types_by_file_extension:{},Nodes:{},Globals:{},searchbox_extras:{},registerNodeType:function(a,b){if(!b.prototype)throw"Cannot register a simple object, it must be a class with a prototype"; b.type=a;e.debug&&console.log("Node registered: "+a);a.split("/");var d=b.name,f=a.lastIndexOf("/");b.category=a.substr(0,f);b.title||(b.title=d);if(b.prototype)for(var g in n.prototype)b.prototype[g]||(b.prototype[g]=n.prototype[g]);if(f=this.registered_node_types[a])console.log("replacing node type: "+a);else if(Object.hasOwnProperty(b.prototype,"shape")||Object.defineProperty(b.prototype,"shape",{set:function(a){switch(a){case "default":delete this._shape;break;case "box":this._shape=e.BOX_SHAPE; -break;case "round":this._shape=e.ROUND_SHAPE;break;case "circle":this._shape=e.CIRCLE_SHAPE;break;case "card":this._shape=e.CARD_SHAPE;break;default:this._shape=a}},get:function(a){return this._shape},enumerable:!0,configurable:!0}),b.prototype.onPropertyChange&&console.warn("LiteGraph node class "+a+" has onPropertyChange method, it must be called onPropertyChanged with d at the end"),b.supported_extensions)for(g in b.supported_extensions){var r=b.supported_extensions[g];r&&r.constructor===String&& -(this.node_types_by_file_extension[r.toLowerCase()]=b)}this.registered_node_types[a]=b;b.constructor.name&&(this.Nodes[d]=b);if(e.onNodeTypeRegistered)e.onNodeTypeRegistered(a,b);if(f&&e.onNodeTypeReplaced)e.onNodeTypeReplaced(a,b,f)},unregisterNodeType:function(a){var b=a.constructor===String?this.registered_node_types[a]:a;if(!b)throw"node type not found: "+a;delete this.registered_node_types[b.type];b.constructor.name&&delete this.Nodes[b.constructor.name]},wrapFunctionAsNode:function(a,b,d,f, -g){for(var r=Array(b.length),x="",c=e.getParameterNames(b),q=0;qx&&(x=g.size[0]),c+=g.size[1]+a+e.NODE_TITLE_HEIGHT;b+=x+a}this.setDirtyCanvas(!0,!0)};c.prototype.getTime=function(){return this.globaltime};c.prototype.getFixedTime=function(){return this.fixedtime};c.prototype.getElapsedTime=function(){return this.elapsed_time};c.prototype.sendEventToAllNodes=function(a,b,d){d=d||e.ALWAYS;var f=this._nodes_in_order? -this._nodes_in_order:this._nodes;if(f)for(var g=0,r=f.length;g=e.MAX_NUMBER_OF_NODES)throw"LiteGraph: max number of nodes in a graph reached";null==a.id||-1==a.id?a.id=++this.last_node_id:this.last_node_ida.length||(this._pos[0]=a[0],this._pos[1]= -a[1])},get:function(){return this._pos},enumerable:!0});this.id=-1;this.type=null;this.inputs=[];this.outputs=[];this.connections=[];this.properties={};this.properties_info=[];this.flags={}};n.prototype.configure=function(a){this.graph&&this.graph._version++;for(var b in a)if("properties"==b)for(var d in a.properties){if(this.properties[d]=a.properties[d],this.onPropertyChanged)this.onPropertyChanged(d,a.properties[d])}else null!=a[b]&&("object"==typeof a[b]?this[b]&&this[b].configure?this[b].configure(a[b]): -this[b]=e.cloneObject(a[b],this[b]):this[b]=a[b]);a.title||(this.title=this.constructor.title);if(this.onConnectionsChange){if(this.inputs)for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d._data=b,this.outputs[a].links))for(d=0;d=this.outputs.length)){var d= -this.outputs[a];if(d&&(d.type=b,this.outputs[a].links))for(d=0;d=this.inputs.length||null==this.inputs[a].link)){var d=this.graph.links[this.inputs[a].link];if(!d)return null;if(!b)return d.data;var f=this.graph.getNodeById(d.origin_id);if(!f)return d.data;if(f.updateOutputData)f.updateOutputData(d.origin_slot);else if(f.onExecute)f.onExecute();return d.data}}; -n.prototype.getInputDataType=function(a){if(!this.inputs||a>=this.inputs.length||null==this.inputs[a].link)return null;a=this.graph.links[this.inputs[a].link];if(!a)return null;var b=this.graph.getNodeById(a.origin_id);return b?(a=b.outputs[a.origin_slot])?a.type:null:a.type};n.prototype.getInputDataByName=function(a,b){var d=this.findInputSlot(a);return-1==d?null:this.getInputData(d,b)};n.prototype.isInputConnected=function(a){return this.inputs?a=this.inputs.length)return null;a=this.inputs[a];return a&&null!==a.link?(a=this.graph.links[a.link])?this.graph.getNodeById(a.origin_id):null:null};n.prototype.getInputOrProperty=function(a){if(!this.inputs||!this.inputs.length)return this.properties? -this.properties[a]:null;for(var b=0,d=this.inputs.length;b=this.outputs.length?null:this.outputs[a]._data};n.prototype.getOutputInfo=function(a){return this.outputs?a=this.outputs.length)return null;a=this.outputs[a];if(!a.links||0==a.links.length)return null;for(var b=[],d=0;da&&this.pos[1]-g-db)return!0;return!1};n.prototype.getSlotInPosition=function(a,b){var d=new Float32Array(2);if(this.inputs)for(var f=0,g=this.inputs.length;f=this.outputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),null;b&&b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"target node is null";if(b==this)return null;if(d.constructor===String){if(d=b.findInputSlot(d),-1==d)return e.debug&&console.log("Connect: Error, no slot of name "+d),null}else{if(d=== -e.EVENT)return null;if(!b.inputs||d>=b.inputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),null}var f=!1;null!=b.inputs[d].link&&(this.graph.beforeChange(),b.disconnectInput(d),f=!0);var g=this.outputs[a];if(b.onConnectInput&&!1===b.onConnectInput(d,g.type,g,this,a))return null;var r=b.inputs[d],x=null;if(!e.isValidConnection(g.type,r.type))return this.setDirtyCanvas(!1,!0),f&&this.graph.connectionChange(this,x),null;f||this.graph.beforeChange();x=new m(++this.graph.last_link_id, -r.type,this.id,a,b.id,d);this.graph.links[x.id]=x;null==g.links&&(g.links=[]);g.links.push(x.id);b.inputs[d].link=x.id;this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(e.OUTPUT,a,!0,x,g);if(b.onConnectionsChange)b.onConnectionsChange(e.INPUT,d,!0,x,r);this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(e.INPUT,b,d,this,a),this.graph.onNodeConnectionChange(e.OUTPUT,this,a,b,d));this.setDirtyCanvas(!1,!0);this.graph.afterChange();this.graph.connectionChange(this, -x);return x};n.prototype.disconnectOutput=function(a,b){if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return e.debug&&console.log("Connect: Error, no slot of name "+a),!1}else if(!this.outputs||a>=this.outputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),!1;var d=this.outputs[a];if(!d||!d.links||0==d.links.length)return!1;if(b){b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"Target Node not found";for(var f=0,g=d.links.length;f=this.inputs.length)return e.debug&&console.log("Connect: Error, slot number not found"), -!1;var b=this.inputs[a];if(!b)return!1;var d=this.inputs[a].link;this.inputs[a].link=null;var f=this.graph.links[d];if(f){var g=this.graph.getNodeById(f.origin_id);if(!g)return!1;var r=g.outputs[f.origin_slot];if(!r||!r.links||0==r.links.length)return!1;for(var x=0,c=r.links.length;xb&&this.inputs[b].pos)return d[0]=this.pos[0]+this.inputs[b].pos[0],d[1]=this.pos[1]+this.inputs[b].pos[1],d;if(!a&&f>b&&this.outputs[b].pos)return d[0]=this.pos[0]+this.outputs[b].pos[0],d[1]=this.pos[1]+this.outputs[b].pos[1],d;if(this.horizontal)return d[0]= -this.pos[0]+this.size[0]/f*(b+0.5),d[1]=a?this.pos[1]-e.NODE_TITLE_HEIGHT:this.pos[1]+this.size[1],d;d[0]=a?this.pos[0]+g:this.pos[0]+this.size[0]+1-g;d[1]=this.pos[1]+(b+0.7)*e.NODE_SLOT_HEIGHT+(this.constructor.slot_start_y||0);return d};n.prototype.alignToGrid=function(){this.pos[0]=e.CANVAS_GRID_SIZE*Math.round(this.pos[0]/e.CANVAS_GRID_SIZE);this.pos[1]=e.CANVAS_GRID_SIZE*Math.round(this.pos[1]/e.CANVAS_GRID_SIZE)};n.prototype.trace=function(a){this.console||(this.console=[]);this.console.push(a); -this.console.length>n.MAX_CONSOLE&&this.console.shift();if(this.graph.onNodeTrace)this.graph.onNodeTrace(this,a)};n.prototype.setDirtyCanvas=function(a,b){this.graph&&this.graph.sendActionToCanvas("setDirty",[a,b])};n.prototype.loadImage=function(a){var b=new Image;b.src=e.node_images_path+a;b.ready=!1;var d=this;b.onload=function(){this.ready=!0;d.setDirtyCanvas(!0)};return b};n.prototype.captureInput=function(a){if(this.graph&&this.graph.list_of_graphcanvas)for(var b=this.graph.list_of_graphcanvas, -d=0;da.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});Object.defineProperty(this, -"size",{set:function(a){!a||2>a.length||(this._size[0]=Math.max(140,a[0]),this._size[1]=Math.max(80,a[1]))},get:function(){return this._size},enumerable:!0})};k.prototype.configure=function(a){this.title=a.title;this._bounding.set(a.bounding);this.color=a.color;this.font=a.font};k.prototype.serialize=function(){var a=this._bounding;return{title:this.title,bounding:[Math.round(a[0]),Math.round(a[1]),Math.round(a[2]),Math.round(a[3])],color:this.color,font:this.font}};k.prototype.move=function(a,b, -d){this._pos[0]+=a;this._pos[1]+=b;if(!d)for(d=0;dthis.max_scale&&(a=this.max_scale);if(a!=this.scale&&this.element){var d=this.element.getBoundingClientRect(); -if(d){b=b||[0.5*d.width,0.5*d.height];d=this.convertCanvasToOffset(b);this.scale=a;0.01>Math.abs(this.scale-1)&&(this.scale=1);var f=this.convertCanvasToOffset(b),d=[f[0]-d[0],f[1]-d[1]];this.offset[0]+=d[0];this.offset[1]+=d[1];if(this.onredraw)this.onredraw(this)}}};v.prototype.changeDeltaScale=function(a,b){this.changeScale(this.scale*a,b)};v.prototype.reset=function(){this.scale=1;this.offset[0]=0;this.offset[1]=0};D.LGraphCanvas=e.LGraphCanvas=h;h.DEFAULT_BACKGROUND_IMAGE="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; +break;case "round":this._shape=e.ROUND_SHAPE;break;case "circle":this._shape=e.CIRCLE_SHAPE;break;case "card":this._shape=e.CARD_SHAPE;break;default:this._shape=a}},get:function(a){return this._shape},enumerable:!0,configurable:!0}),b.prototype.onPropertyChange&&console.warn("LiteGraph node class "+a+" has onPropertyChange method, it must be called onPropertyChanged with d at the end"),b.supported_extensions)for(g in b.supported_extensions){var q=b.supported_extensions[g];q&&q.constructor===String&& +(this.node_types_by_file_extension[q.toLowerCase()]=b)}this.registered_node_types[a]=b;b.constructor.name&&(this.Nodes[d]=b);if(e.onNodeTypeRegistered)e.onNodeTypeRegistered(a,b);if(f&&e.onNodeTypeReplaced)e.onNodeTypeReplaced(a,b,f)},unregisterNodeType:function(a){var b=a.constructor===String?this.registered_node_types[a]:a;if(!b)throw"node type not found: "+a;delete this.registered_node_types[b.type];b.constructor.name&&delete this.Nodes[b.constructor.name]},wrapFunctionAsNode:function(a,b,d,f, +g){for(var q=Array(b.length),A="",c=e.getParameterNames(b),r=0;rA&&(A=g.size[0]),c+=g.size[1]+a+e.NODE_TITLE_HEIGHT;b+=A+a}this.setDirtyCanvas(!0,!0)};c.prototype.getTime=function(){return this.globaltime};c.prototype.getFixedTime=function(){return this.fixedtime};c.prototype.getElapsedTime=function(){return this.elapsed_time}; +c.prototype.sendEventToAllNodes=function(a,b,d){d=d||e.ALWAYS;var f=this._nodes_in_order?this._nodes_in_order:this._nodes;if(f)for(var g=0,q=f.length;g=e.MAX_NUMBER_OF_NODES)throw"LiteGraph: max number of nodes in a graph reached";null==a.id||-1==a.id?a.id=++this.last_node_id:this.last_node_ida.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});this.id=-1;this.type=null;this.inputs=[];this.outputs=[];this.connections=[];this.properties={};this.properties_info=[];this.flags={}};n.prototype.configure=function(a){this.graph&&this.graph._version++;for(var b in a)if("properties"==b)for(var d in a.properties){if(this.properties[d]=a.properties[d],this.onPropertyChanged)this.onPropertyChanged(d, +a.properties[d])}else null!=a[b]&&("object"==typeof a[b]?this[b]&&this[b].configure?this[b].configure(a[b]):this[b]=e.cloneObject(a[b],this[b]):this[b]=a[b]);a.title||(this.title=this.constructor.title);if(this.onConnectionsChange){if(this.inputs)for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d._data=b,this.outputs[a].links))for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d.type=b,this.outputs[a].links))for(d=0;d=this.inputs.length||null==this.inputs[a].link)){var d=this.graph.links[this.inputs[a].link];if(!d)return null;if(!b)return d.data;var f=this.graph.getNodeById(d.origin_id);if(!f)return d.data; +if(f.updateOutputData)f.updateOutputData(d.origin_slot);else if(f.onExecute)f.onExecute();return d.data}};n.prototype.getInputDataType=function(a){if(!this.inputs||a>=this.inputs.length||null==this.inputs[a].link)return null;a=this.graph.links[this.inputs[a].link];if(!a)return null;var b=this.graph.getNodeById(a.origin_id);return b?(a=b.outputs[a.origin_slot])?a.type:null:a.type};n.prototype.getInputDataByName=function(a,b){var d=this.findInputSlot(a);return-1==d?null:this.getInputData(d,b)};n.prototype.isInputConnected= +function(a){return this.inputs?a=this.inputs.length)return null;a=this.inputs[a];return a&&null!==a.link?(a=this.graph.links[a.link])?this.graph.getNodeById(a.origin_id): +null:null};n.prototype.getInputOrProperty=function(a){if(!this.inputs||!this.inputs.length)return this.properties?this.properties[a]:null;for(var b=0,d=this.inputs.length;b=this.outputs.length?null:this.outputs[a]._data};n.prototype.getOutputInfo=function(a){return this.outputs?a=this.outputs.length)return null;a=this.outputs[a];if(!a.links||0==a.links.length)return null; +for(var b=[],d=0;da&&this.pos[1]-g-db)return!0;return!1};n.prototype.getSlotInPosition=function(a,b){var d=new Float32Array(2);if(this.inputs)for(var f=0,g=this.inputs.length;f=this.outputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),null;b&&b.constructor===Number&&(b=this.graph.getNodeById(b)); +if(!b)throw"target node is null";if(b==this)return null;if(d.constructor===String){if(d=b.findInputSlot(d),-1==d)return e.debug&&console.log("Connect: Error, no slot of name "+d),null}else{if(d===e.EVENT)return null;if(!b.inputs||d>=b.inputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),null}var f=!1;null!=b.inputs[d].link&&(this.graph.beforeChange(),b.disconnectInput(d),f=!0);var g=this.outputs[a];if(b.onConnectInput&&!1===b.onConnectInput(d,g.type,g,this,a))return null; +var q=b.inputs[d],A=null;if(!e.isValidConnection(g.type,q.type))return this.setDirtyCanvas(!1,!0),f&&this.graph.connectionChange(this,A),null;f||this.graph.beforeChange();A=new m(++this.graph.last_link_id,q.type,this.id,a,b.id,d);this.graph.links[A.id]=A;null==g.links&&(g.links=[]);g.links.push(A.id);b.inputs[d].link=A.id;this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(e.OUTPUT,a,!0,A,g);if(b.onConnectionsChange)b.onConnectionsChange(e.INPUT,d,!0,A,q);this.graph&& +this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(e.INPUT,b,d,this,a),this.graph.onNodeConnectionChange(e.OUTPUT,this,a,b,d));this.setDirtyCanvas(!1,!0);this.graph.afterChange();this.graph.connectionChange(this,A);return A};n.prototype.disconnectOutput=function(a,b){if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return e.debug&&console.log("Connect: Error, no slot of name "+a),!1}else if(!this.outputs||a>=this.outputs.length)return e.debug&&console.log("Connect: Error, slot number not found"), +!1;var d=this.outputs[a];if(!d||!d.links||0==d.links.length)return!1;if(b){b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"Target Node not found";for(var f=0,g=d.links.length;f=this.inputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),!1;var b=this.inputs[a];if(!b)return!1;var d=this.inputs[a].link;this.inputs[a].link=null;var f=this.graph.links[d];if(f){var g=this.graph.getNodeById(f.origin_id);if(!g)return!1;var q=g.outputs[f.origin_slot];if(!q||!q.links||0==q.links.length)return!1;for(var A=0,c=q.links.length;A< +c;A++)if(q.links[A]==d){q.links.splice(A,1);break}delete this.graph.links[d];this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(e.INPUT,a,!1,f,b);if(g.onConnectionsChange)g.onConnectionsChange(e.OUTPUT,A,!1,f,q);this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(e.OUTPUT,g,A),this.graph.onNodeConnectionChange(e.INPUT,this,a))}this.setDirtyCanvas(!1,!0);this.graph.connectionChange(this);return!0};n.prototype.getConnectionPos=function(a, +b,d){d=d||new Float32Array(2);var f=0;a&&this.inputs&&(f=this.inputs.length);!a&&this.outputs&&(f=this.outputs.length);var g=0.5*e.NODE_SLOT_HEIGHT;if(this.flags.collapsed)return b=this._collapsed_width||e.NODE_COLLAPSED_WIDTH,this.horizontal?(d[0]=this.pos[0]+0.5*b,d[1]=a?this.pos[1]-e.NODE_TITLE_HEIGHT:this.pos[1]):(d[0]=a?this.pos[0]:this.pos[0]+b,d[1]=this.pos[1]-0.5*e.NODE_TITLE_HEIGHT),d;if(a&&-1==b)return d[0]=this.pos[0]+0.5*e.NODE_TITLE_HEIGHT,d[1]=this.pos[1]+0.5*e.NODE_TITLE_HEIGHT,d;if(a&& +f>b&&this.inputs[b].pos)return d[0]=this.pos[0]+this.inputs[b].pos[0],d[1]=this.pos[1]+this.inputs[b].pos[1],d;if(!a&&f>b&&this.outputs[b].pos)return d[0]=this.pos[0]+this.outputs[b].pos[0],d[1]=this.pos[1]+this.outputs[b].pos[1],d;if(this.horizontal)return d[0]=this.pos[0]+this.size[0]/f*(b+0.5),d[1]=a?this.pos[1]-e.NODE_TITLE_HEIGHT:this.pos[1]+this.size[1],d;d[0]=a?this.pos[0]+g:this.pos[0]+this.size[0]+1-g;d[1]=this.pos[1]+(b+0.7)*e.NODE_SLOT_HEIGHT+(this.constructor.slot_start_y||0);return d}; +n.prototype.alignToGrid=function(){this.pos[0]=e.CANVAS_GRID_SIZE*Math.round(this.pos[0]/e.CANVAS_GRID_SIZE);this.pos[1]=e.CANVAS_GRID_SIZE*Math.round(this.pos[1]/e.CANVAS_GRID_SIZE)};n.prototype.trace=function(a){this.console||(this.console=[]);this.console.push(a);this.console.length>n.MAX_CONSOLE&&this.console.shift();if(this.graph.onNodeTrace)this.graph.onNodeTrace(this,a)};n.prototype.setDirtyCanvas=function(a,b){this.graph&&this.graph.sendActionToCanvas("setDirty",[a,b])};n.prototype.loadImage= +function(a){var b=new Image;b.src=e.node_images_path+a;b.ready=!1;var d=this;b.onload=function(){this.ready=!0;d.setDirtyCanvas(!0)};return b};n.prototype.captureInput=function(a){if(this.graph&&this.graph.list_of_graphcanvas)for(var b=this.graph.list_of_graphcanvas,d=0;da.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});Object.defineProperty(this,"size",{set:function(a){!a||2>a.length||(this._size[0]=Math.max(140,a[0]),this._size[1]=Math.max(80,a[1]))},get:function(){return this._size},enumerable:!0})};k.prototype.configure=function(a){this.title=a.title;this._bounding.set(a.bounding);this.color= +a.color;this.font=a.font};k.prototype.serialize=function(){var a=this._bounding;return{title:this.title,bounding:[Math.round(a[0]),Math.round(a[1]),Math.round(a[2]),Math.round(a[3])],color:this.color,font:this.font}};k.prototype.move=function(a,b,d){this._pos[0]+=a;this._pos[1]+=b;if(!d)for(d=0;dthis.max_scale&&(a=this.max_scale);if(a!=this.scale&&this.element){var d=this.element.getBoundingClientRect();if(d){b=b||[0.5*d.width,0.5*d.height];d=this.convertCanvasToOffset(b);this.scale=a;0.01>Math.abs(this.scale-1)&&(this.scale=1);var f=this.convertCanvasToOffset(b),d=[f[0]-d[0],f[1]-d[1]];this.offset[0]+=d[0];this.offset[1]+=d[1];if(this.onredraw)this.onredraw(this)}}}; +y.prototype.changeDeltaScale=function(a,b){this.changeScale(this.scale*a,b)};y.prototype.reset=function(){this.scale=1;this.offset[0]=0;this.offset[1]=0};C.LGraphCanvas=e.LGraphCanvas=h;h.DEFAULT_BACKGROUND_IMAGE="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; h.link_type_colors={"-1":e.EVENT_LINK_COLOR,number:"#AAA",node:"#DCA"};h.gradients={};h.prototype.clear=function(){this.fps=this.render_time=this.last_draw_time=this.frame=0;this.dragging_rectangle=null;this.selected_nodes={};this.selected_group=null;this.visible_nodes=[];this.connecting_node=this.node_capturing_input=this.node_over=this.node_dragged=null;this.highlighted_links={};this.dragging_canvas=!1;this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_widget=this.node_in_panel=this.dirty_area= null;this.last_mouse=[0,0];this.last_mouseclick=0;this.visible_area.set([0,0,0,0]);if(this.onClear)this.onClear()};h.prototype.setGraph=function(a,b){this.graph!=a&&(b||this.clear(),!a&&this.graph?this.graph.detachCanvas(this):(a.attachCanvas(this),this._graph_stack&&(this._graph_stack=null),this.setDirty(!0,!0)))};h.prototype.getTopGraph=function(){return this._graph_stack.length?this._graph_stack[0]:this.graph};h.prototype.openSubgraph=function(a){if(!a)throw"graph cannot be null";if(this.graph== a)throw"graph cannot be the same";this.clear();this.graph&&(this._graph_stack||(this._graph_stack=[]),this._graph_stack.push(this.graph));a.attachCanvas(this);this.checkPanels();this.setDirty(!0,!0)};h.prototype.closeSubgraph=function(){if(this._graph_stack&&0!=this._graph_stack.length){var a=this.graph._subgraph_node,b=this._graph_stack.pop();this.selected_nodes={};this.highlighted_links={};b.attachCanvas(this);this.setDirty(!0,!0);a&&(this.centerOnNode(a),this.selectNodes([a]))}};h.prototype.getCurrentGraph= @@ -112,128 +113,128 @@ this.canvas.removeEventListener("DOMMouseScroll",this._mousewheel_callback);this this.canvas.removeEventListener("touchend",this.touchHandler);this.canvas.removeEventListener("touchcancel",this.touchHandler);this._ondrop_callback=this._key_callback=this._mousewheel_callback=this._mousedown_callback=null;this._events_binded=!1}else console.warn("LGraphCanvas: no events binded")};h.getFileExtension=function(a){var b=a.indexOf("?");-1!=b&&(a=a.substr(0,b));b=a.lastIndexOf(".");return-1==b?"":a.substr(b+1).toLowerCase()};h.prototype.enableWebGL=function(){if(void 0===typeof GL)throw"litegl.js must be included to use a WebGL canvas"; if(void 0===typeof enableWebGLCanvas)throw"webglCanvas.js must be included to use this feature";this.gl=this.ctx=enableWebGLCanvas(this.canvas);this.ctx.webgl=!0;this.bgcanvas=this.canvas;this.bgctx=this.gl;this.canvas.webgl_enabled=!0};h.prototype.setDirty=function(a,b){a&&(this.dirty_canvas=!0);b&&(this.dirty_bgcanvas=!0)};h.prototype.getCanvasWindow=function(){if(!this.canvas)return window;var a=this.canvas.ownerDocument;return a.defaultView||a.parentWindow};h.prototype.startRendering=function(){function a(){this.pause_rendering|| this.draw();var b=this.getCanvasWindow();this.is_rendering&&b.requestAnimationFrame(a.bind(this))}this.is_rendering||(this.is_rendering=!0,a.call(this))};h.prototype.stopRendering=function(){this.is_rendering=!1};h.prototype.blockClick=function(){this.block_click=!0;this.last_mouseclick=0};h.prototype.processMouseDown=function(a){this.set_canvas_dirty_on_mouse_event&&(this.dirty_canvas=!0);if(this.graph){this.adjustMouseEvent(a);var b=this.getCanvasWindow();h.active_canvas=this;var d=this;this.canvas.removeEventListener("mousemove", -this._mousemove_callback);b.document.addEventListener("mousemove",this._mousemove_callback,!0);b.document.addEventListener("mouseup",this._mouseup_callback,!0);var f=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes,5),g=!1,r=300>e.getTime()-this.last_mouseclick;this.mouse[0]=a.localX;this.mouse[1]=a.localY;this.graph_mouse[0]=a.canvasX;this.graph_mouse[1]=a.canvasY;this.last_click_position=[this.mouse[0],this.mouse[1]];this.canvas.focus();e.closeAllContextMenus(b);if(!this.onMouse|| -!0!=this.onMouse(a)){if(1==a.which){a.ctrlKey&&(this.dragging_rectangle=new Float32Array(4),this.dragging_rectangle[0]=a.canvasX,this.dragging_rectangle[1]=a.canvasY,this.dragging_rectangle[2]=1,this.dragging_rectangle[3]=1,g=!0);var x=!1;if(f&&this.allow_interaction&&!g&&!this.read_only){this.live_mode||f.flags.pinned||this.bringToFront(f);if(!this.connecting_node&&!f.flags.collapsed&&!this.live_mode)if(!g&&!1!==f.resizable&&y(a.canvasX,a.canvasY,f.pos[0]+f.size[0]-5,f.pos[1]+f.size[1]-5,10,10))this.graph.beforeChange(), -this.resizing_node=f,this.canvas.style.cursor="se-resize",g=!0;else{if(f.outputs)for(var c=0,q=f.outputs.length;cf.size[0]-e.NODE_TITLE_HEIGHT&&0>q[1]&&(d=this,setTimeout(function(){d.openSubgraph(f.subgraph)},10)),this.live_mode&& -(c=x=!0));c||(this.allow_dragnodes&&(this.graph.beforeChange(),this.node_dragged=f),this.selected_nodes[f.id]||this.processNodeSelected(f,a));this.dirty_canvas=!0}}else{if(!this.read_only)for(c=0;cq[0]+4||a.canvasYq[1]+4)){this.showLinkMenu(x,a);this.over_link_center=null;break}this.selected_group=this.graph.getGroupOnPos(a.canvasX,a.canvasY);this.selected_group_resizing=!1;this.selected_group&& -!this.read_only&&(a.ctrlKey&&(this.dragging_rectangle=null),10>B([a.canvasX,a.canvasY],[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])*this.ds.scale?this.selected_group_resizing=!0:this.selected_group.recomputeInsideNodes());r&&!this.read_only&&this.allow_searchbox&&this.showSearchBox(a);x=!0}!g&&x&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}else 2!=a.which&&3==a.which&&(this.read_only||this.processContextMenu(f,a));this.last_mouse[0]= +this._mousemove_callback);b.document.addEventListener("mousemove",this._mousemove_callback,!0);b.document.addEventListener("mouseup",this._mouseup_callback,!0);var f=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes,5),g=!1,q=300>e.getTime()-this.last_mouseclick;this.mouse[0]=a.localX;this.mouse[1]=a.localY;this.graph_mouse[0]=a.canvasX;this.graph_mouse[1]=a.canvasY;this.last_click_position=[this.mouse[0],this.mouse[1]];this.canvas.focus();e.closeAllContextMenus(b);if(!this.onMouse|| +!0!=this.onMouse(a)){if(1==a.which){a.ctrlKey&&(this.dragging_rectangle=new Float32Array(4),this.dragging_rectangle[0]=a.canvasX,this.dragging_rectangle[1]=a.canvasY,this.dragging_rectangle[2]=1,this.dragging_rectangle[3]=1,g=!0);var A=!1;if(f&&this.allow_interaction&&!g&&!this.read_only){this.live_mode||f.flags.pinned||this.bringToFront(f);if(!this.connecting_node&&!f.flags.collapsed&&!this.live_mode)if(!g&&!1!==f.resizable&&x(a.canvasX,a.canvasY,f.pos[0]+f.size[0]-5,f.pos[1]+f.size[1]-5,10,10))this.graph.beforeChange(), +this.resizing_node=f,this.canvas.style.cursor="se-resize",g=!0;else{if(f.outputs)for(var c=0,r=f.outputs.length;cf.size[0]-e.NODE_TITLE_HEIGHT&&0>r[1]&&(d=this,setTimeout(function(){d.openSubgraph(f.subgraph)},10)),this.live_mode&& +(c=A=!0));c||(this.allow_dragnodes&&(this.graph.beforeChange(),this.node_dragged=f),this.selected_nodes[f.id]||this.processNodeSelected(f,a));this.dirty_canvas=!0}}else{if(!this.read_only)for(c=0;cr[0]+4||a.canvasYr[1]+4)){this.showLinkMenu(A,a);this.over_link_center=null;break}this.selected_group=this.graph.getGroupOnPos(a.canvasX,a.canvasY);this.selected_group_resizing=!1;this.selected_group&& +!this.read_only&&(a.ctrlKey&&(this.dragging_rectangle=null),10>D([a.canvasX,a.canvasY],[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])*this.ds.scale?this.selected_group_resizing=!0:this.selected_group.recomputeInsideNodes());q&&!this.read_only&&this.allow_searchbox&&this.showSearchBox(a);A=!0}!g&&A&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}else 2!=a.which&&3==a.which&&(this.read_only||this.processContextMenu(f,a));this.last_mouse[0]= a.localX;this.last_mouse[1]=a.localY;this.last_mouseclick=e.getTime();this.last_mouse_dragging=!0;this.graph.change();(!b.document.activeElement||"input"!=b.document.activeElement.nodeName.toLowerCase()&&"textarea"!=b.document.activeElement.nodeName.toLowerCase())&&a.preventDefault();a.stopPropagation();if(this.onMouseDown)this.onMouseDown(a);return!1}}};h.prototype.processMouseMove=function(a){this.autoresize&&this.resize();this.set_canvas_dirty_on_mouse_event&&(this.dirty_canvas=!0);if(this.graph){h.active_canvas= this;this.adjustMouseEvent(a);var b=[a.localX,a.localY];this.mouse[0]=b[0];this.mouse[1]=b[1];var d=[b[0]-this.last_mouse[0],b[1]-this.last_mouse[1]];this.last_mouse=b;this.graph_mouse[0]=a.canvasX;this.graph_mouse[1]=a.canvasY;if(this.block_click)return a.preventDefault(),!1;a.dragging=this.last_mouse_dragging;this.node_widget&&(this.processNodeWidgets(this.node_widget[0],this.graph_mouse,a,this.node_widget[1]),this.dirty_canvas=!0);if(this.dragging_rectangle)this.dragging_rectangle[2]=a.canvasX- this.dragging_rectangle[0],this.dragging_rectangle[3]=a.canvasY-this.dragging_rectangle[1],this.dirty_canvas=!0;else if(this.selected_group&&!this.read_only)this.selected_group_resizing?this.selected_group.size=[a.canvasX-this.selected_group.pos[0],a.canvasY-this.selected_group.pos[1]]:(this.selected_group.move(d[0]/this.ds.scale,d[1]/this.ds.scale,a.ctrlKey),this.selected_group._nodes.length&&(this.dirty_canvas=!0)),this.dirty_bgcanvas=!0;else if(this.dragging_canvas)this.ds.offset[0]+=d[0]/this.ds.scale, this.ds.offset[1]+=d[1]/this.ds.scale,this.dirty_bgcanvas=this.dirty_canvas=!0;else if(this.allow_interaction&&!this.read_only){this.connecting_node&&(this.dirty_canvas=!0);for(var f=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes),b=0,g=this.graph._nodes.length;bx[0]+4||a.canvasYx[1]+4)){g=r;break}}g!=this.over_link_center&&(this.over_link_center=g,this.dirty_canvas=!0);this.canvas&&(this.canvas.style.cursor="")}if(this.node_capturing_input&&this.node_capturing_input!=f&& +(this.dirty_canvas=!0);if(!f.mouseOver&&(f.mouseOver=!0,this.node_over=f,this.dirty_canvas=!0,f.onMouseEnter))f.onMouseEnter(a);if(f.onMouseMove)f.onMouseMove(a,[a.canvasX-f.pos[0],a.canvasY-f.pos[1]],this);if(this.connecting_node&&(g=this._highlight_input||[0,0],!this.isOverNodeBox(f,a.canvasX,a.canvasY))){var q=this.isOverNodeInput(f,a.canvasX,a.canvasY,g);-1!=q&&f.inputs[q]?e.isValidConnection(this.connecting_output.type,f.inputs[q].type)&&(this._highlight_input=g):this._highlight_input=null}this.canvas&& +(x(a.canvasX,a.canvasY,f.pos[0]+f.size[0]-5,f.pos[1]+f.size[1]-5,5,5)?this.canvas.style.cursor="se-resize":this.canvas.style.cursor="crosshair")}else{g=null;for(b=0;bA[0]+4||a.canvasYA[1]+4)){g=q;break}}g!=this.over_link_center&&(this.over_link_center=g,this.dirty_canvas=!0);this.canvas&&(this.canvas.style.cursor="")}if(this.node_capturing_input&&this.node_capturing_input!=f&& this.node_capturing_input.onMouseMove)this.node_capturing_input.onMouseMove(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]],this);if(this.node_dragged&&!this.live_mode){for(b in this.selected_nodes)f=this.selected_nodes[b],f.pos[0]+=d[0]/this.ds.scale,f.pos[1]+=d[1]/this.ds.scale;this.dirty_bgcanvas=this.dirty_canvas=!0}this.resizing_node&&!this.live_mode&&(d=[a.canvasX-this.resizing_node.pos[0],a.canvasY-this.resizing_node.pos[1]],b=this.resizing_node.computeSize(), d[0]=Math.max(b[0],d[0]),d[1]=Math.max(b[1],d[1]),this.resizing_node.setSize(d),this.canvas.style.cursor="se-resize",this.dirty_bgcanvas=this.dirty_canvas=!0)}a.preventDefault();return!1}};h.prototype.processMouseUp=function(a){this.set_canvas_dirty_on_mouse_event&&(this.dirty_canvas=!0);if(this.graph){var b=this.getCanvasWindow().document;h.active_canvas=this;b.removeEventListener("mousemove",this._mousemove_callback,!0);this.canvas.addEventListener("mousemove",this._mousemove_callback,!0);b.removeEventListener("mouseup", this._mouseup_callback,!0);this.adjustMouseEvent(a);b=e.getTime();a.click_time=b-this.last_mouseclick;this.last_mouse_dragging=!1;this.last_click_position=null;this.block_click&&(console.log("foo"),this.block_click=!1);if(1==a.which){this.node_widget&&this.processNodeWidgets(this.node_widget[0],this.graph_mouse,a);this.node_widget=null;if(this.selected_group){var b=this.selected_group.pos[0]-Math.round(this.selected_group.pos[0]),d=this.selected_group.pos[1]-Math.round(this.selected_group.pos[1]); -this.selected_group.move(b,d,a.ctrlKey);this.selected_group.pos[0]=Math.round(this.selected_group.pos[0]);this.selected_group.pos[1]=Math.round(this.selected_group.pos[1]);this.selected_group._nodes.length&&(this.dirty_canvas=!0);this.selected_group=null}this.selected_group_resizing=!1;if(this.dragging_rectangle){if(this.graph){b=this.graph._nodes;d=new Float32Array(4);this.deselectAllNodes();var f=Math.abs(this.dragging_rectangle[2]),g=Math.abs(this.dragging_rectangle[3]),r=0>this.dragging_rectangle[3]? -this.dragging_rectangle[1]-g:this.dragging_rectangle[1];this.dragging_rectangle[0]=0>this.dragging_rectangle[2]?this.dragging_rectangle[0]-f:this.dragging_rectangle[0];this.dragging_rectangle[1]=r;this.dragging_rectangle[2]=f;this.dragging_rectangle[3]=g;g=[];for(r=0;rthis.dragging_rectangle[3]? +this.dragging_rectangle[1]-g:this.dragging_rectangle[1];this.dragging_rectangle[0]=0>this.dragging_rectangle[2]?this.dragging_rectangle[0]-f:this.dragging_rectangle[0];this.dragging_rectangle[1]=q;this.dragging_rectangle[2]=f;this.dragging_rectangle[3]=g;g=[];for(q=0;qa.click_time&&y(a.canvasX,a.canvasY,f.pos[0],f.pos[1]-e.NODE_TITLE_HEIGHT,e.NODE_TITLE_HEIGHT,e.NODE_TITLE_HEIGHT)&&f.collapse();this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_dragged.pos[0]=Math.round(this.node_dragged.pos[0]); +f,0)));this.connecting_node=this.connecting_pos=this.connecting_output=null;this.connecting_slot=-1}else if(this.resizing_node)this.dirty_bgcanvas=this.dirty_canvas=!0,this.graph.afterChange(this.resizing_node),this.resizing_node=null;else if(this.node_dragged){(f=this.node_dragged)&&300>a.click_time&&x(a.canvasX,a.canvasY,f.pos[0],f.pos[1]-e.NODE_TITLE_HEIGHT,e.NODE_TITLE_HEIGHT,e.NODE_TITLE_HEIGHT)&&f.collapse();this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_dragged.pos[0]=Math.round(this.node_dragged.pos[0]); this.node_dragged.pos[1]=Math.round(this.node_dragged.pos[1]);this.graph.config.align_to_grid&&this.node_dragged.alignToGrid();if(this.onNodeMoved)this.onNodeMoved(this.node_dragged);this.graph.afterChange(this.node_dragged);this.node_dragged=null}else{f=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes);!f&&300>a.click_time&&this.deselectAllNodes();this.dirty_canvas=!0;this.dragging_canvas=!1;if(this.node_over&&this.node_over.onMouseUp)this.node_over.onMouseUp(a,[a.canvasX-this.node_over.pos[0], a.canvasY-this.node_over.pos[1]],this);if(this.node_capturing_input&&this.node_capturing_input.onMouseUp)this.node_capturing_input.onMouseUp(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]])}}else 2==a.which?(this.dirty_canvas=!0,this.dragging_canvas=!1):3==a.which&&(this.dirty_canvas=!0,this.dragging_canvas=!1);this.graph.change();a.stopPropagation();a.preventDefault();return!1}};h.prototype.processMouseWheel=function(a){if(this.graph&&this.allow_dragcanvas){var b= -null!=a.wheelDeltaY?a.wheelDeltaY:-60*a.detail;this.adjustMouseEvent(a);var d=this.ds.scale;0b&&(d*=1/1.1);this.ds.changeScale(d,[a.localX,a.localY]);this.graph.change();a.preventDefault();return!1}};h.prototype.isOverNodeBox=function(a,b,d){var f=e.NODE_TITLE_HEIGHT;return y(b,d,a.pos[0]+2,a.pos[1]+2-f,f-4,f-4)?!0:!1};h.prototype.isOverNodeInput=function(a,b,d,f){if(a.inputs)for(var g=0,r=a.inputs.length;gb&&(d*=1/1.1);this.ds.changeScale(d,[a.localX,a.localY]);this.graph.change();a.preventDefault();return!1}};h.prototype.isOverNodeBox=function(a,b,d){var f=e.NODE_TITLE_HEIGHT;return x(b,d,a.pos[0]+2,a.pos[1]+2-f,f-4,f-4)?!0:!1};h.prototype.isOverNodeInput=function(a,b,d,f){if(a.inputs)for(var g=0,q=a.inputs.length;gd-this.graph._last_trigger_time)&&this.drawBackCanvas(); +return this.convertCanvasToOffset([a.clientX-b.left,a.clientY-b.top])};h.prototype.bringToFront=function(a){var b=this.graph._nodes.indexOf(a);-1!=b&&(this.graph._nodes.splice(b,1),this.graph._nodes.push(a))};h.prototype.sendToBack=function(a){var b=this.graph._nodes.indexOf(a);-1!=b&&(this.graph._nodes.splice(b,1),this.graph._nodes.unshift(a))};var p=new Float32Array(4);h.prototype.computeVisibleNodes=function(a,b){var d=b||[];d.length=0;a=a||this.graph._nodes;for(var f=0,g=a.length;fd-this.graph._last_trigger_time)&&this.drawBackCanvas(); (this.dirty_canvas||a)&&this.drawFrontCanvas();this.fps=this.render_time?1/this.render_time:0;this.frame+=1}};h.prototype.drawFrontCanvas=function(){this.dirty_canvas=!1;this.ctx||(this.ctx=this.bgcanvas.getContext("2d"));var a=this.ctx;if(a){a.start2D&&a.start2D();var b=this.canvas;a.restore();a.setTransform(1,0,0,1,0,0);this.dirty_area&&(a.save(),a.beginPath(),a.rect(this.dirty_area[0],this.dirty_area[1],this.dirty_area[2],this.dirty_area[3]),a.clip());this.clear_background&&a.clearRect(0,0,b.width, b.height);this.bgcanvas==this.canvas?this.drawBackCanvas():a.drawImage(this.bgcanvas,0,0);if(this.onRender)this.onRender(b,a);this.show_info&&this.renderInfo(a);if(this.graph){a.save();this.ds.toCanvasContext(a);for(var b=this.computeVisibleNodes(null,this.visible_nodes),d=0;d> ";b.fillText(f+d.getTitle(),0.5*a.width,40);b.restore()}d=!1;this.onRenderBackground&&(d=this.onRenderBackground(a,b));b.restore();b.setTransform(1,0,0,1,0,0);this.visible_links.length=0;if(this.graph){b.save();this.ds.toCanvasContext(b);if(this.background_image&&0.5this.ds.scale;if(this.live_mode){if(!a.flags.collapsed&&(b.shadowColor="transparent",a.onDrawForeground))a.onDrawForeground(b, -this,this.canvas)}else{var r=this.editor_alpha;b.globalAlpha=r;this.render_shadows&&!g?(b.shadowColor=e.DEFAULT_SHADOW_COLOR,b.shadowOffsetX=2*this.ds.scale,b.shadowOffsetY=2*this.ds.scale,b.shadowBlur=3*this.ds.scale):b.shadowColor="transparent";if(!a.flags.collapsed||!a.onDrawCollapsed||!0!=a.onDrawCollapsed(b,this)){var c=a._shape||e.BOX_SHAPE;F.set(a.size);var l=a.horizontal;if(a.flags.collapsed){b.font=this.inner_text_font;var q=a.getTitle?a.getTitle():a.title;null!=q&&(a._collapsed_width=Math.min(a.size[0], -b.measureText(q).width+2*e.NODE_TITLE_HEIGHT),F[0]=a._collapsed_width,F[1]=0)}a.clip_area&&(b.save(),b.beginPath(),c==e.BOX_SHAPE?b.rect(0,0,F[0],F[1]):c==e.ROUND_SHAPE?b.roundRect(0,0,F[0],F[1],10):c==e.CIRCLE_SHAPE&&b.arc(0.5*F[0],0.5*F[1],0.5*F[0],0,2*Math.PI),b.clip());a.has_errors&&(f="red");this.drawNodeShape(a,b,F,d,f,a.is_selected,a.mouseOver);b.shadowColor="transparent";if(a.onDrawForeground)a.onDrawForeground(b,this,this.canvas);b.textAlign=l?"center":"left";b.font=this.inner_text_font; -f=!g;c=this.connecting_output;b.lineWidth=1;var q=0,z=new Float32Array(2);if(!a.flags.collapsed){if(a.inputs)for(d=0;dthis.ds.scale,q=a._shape||a.constructor.shape||e.ROUND_SHAPE,z=a.constructor.title_mode,w=!0;z== -e.TRANSPARENT_TITLE?w=!1:z==e.AUTOHIDE_TITLE&&c&&(w=!0);t[0]=0;t[1]=w?-g:0;t[2]=d[0]+1;t[3]=w?d[1]+g:d[1];c=b.globalAlpha;b.beginPath();q==e.BOX_SHAPE||l?b.fillRect(t[0],t[1],t[2],t[3]):q==e.ROUND_SHAPE||q==e.CARD_SHAPE?b.roundRect(t[0],t[1],t[2],t[3],this.round_radius,q==e.CARD_SHAPE?0:this.round_radius):q==e.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0],0,2*Math.PI);b.fill();a.flags.collapsed||(b.shadowColor="transparent",b.fillStyle="rgba(0,0,0,0.2)",b.fillRect(0,-1,t[2],2));b.shadowColor="transparent"; -if(a.onDrawBackground)a.onDrawBackground(b,this,this.canvas,this.graph_mouse);if(w||z==e.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,g,d,this.ds.scale,f);else if(z!=e.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){w=a.constructor.title_color||f;a.flags.collapsed&&(b.shadowColor=e.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var u=h.gradients[w];u||(u=h.gradients[w]=b.createLinearGradient(0,0,400,0),u.addColorStop(0,w),u.addColorStop(1,"#000"));b.fillStyle= -u}else b.fillStyle=w;b.beginPath();q==e.BOX_SHAPE||l?b.rect(0,-g,d[0]+1,g):q!=e.ROUND_SHAPE&&q!=e.CARD_SHAPE||b.roundRect(0,-g,d[0]+1,g,this.round_radius,a.flags.collapsed?this.round_radius:0);b.fill();b.shadowColor="transparent"}if(a.onDrawTitleBox)a.onDrawTitleBox(b,g,d,this.ds.scale);else q==e.ROUND_SHAPE||q==e.CIRCLE_SHAPE||q==e.CARD_SHAPE?(l&&(b.fillStyle="black",b.beginPath(),b.arc(0.5*g,-0.5*g,6,0,2*Math.PI),b.fill()),b.fillStyle=a.boxcolor||e.NODE_DEFAULT_BOXCOLOR,l?b.fillRect(0.5*g-5,-0.5* -g-5,10,10):(b.beginPath(),b.arc(0.5*g,-0.5*g,5,0,2*Math.PI),b.fill())):(l&&(b.fillStyle="black",b.fillRect(0.5*(g-10)-1,-0.5*(g+10)-1,12,12)),b.fillStyle=a.boxcolor||e.NODE_DEFAULT_BOXCOLOR,b.fillRect(0.5*(g-10),-0.5*(g+10),10,10));b.globalAlpha=c;if(a.onDrawTitleText)a.onDrawTitleText(b,g,d,this.ds.scale,this.title_text_font,r);!l&&(b.font=this.title_text_font,c=String(a.getTitle()))&&(b.fillStyle=r?"white":a.constructor.title_text_color||this.node_title_color,a.flags.collapsed?(b.textAlign="left", -b.measureText(c),b.fillText(c.substr(0,20),g,e.NODE_TITLE_TEXT_Y-g),b.textAlign="left"):(b.textAlign="left",b.fillText(c,g,e.NODE_TITLE_TEXT_Y-g)));a.flags.collapsed||!a.subgraph||a.skip_subgraph_button||(c=e.NODE_TITLE_HEIGHT,w=a.size[0]-c,u=e.isInsideRectangle(this.graph_mouse[0]-a.pos[0],this.graph_mouse[1]-a.pos[1],w+2,-c+2,c-4,c-4),b.fillStyle=u?"#888":"#555",q==e.BOX_SHAPE||l?b.fillRect(w+2,-c+2,c-4,c-4):(b.beginPath(),b.roundRect(w+2,-c+2,c-4,c-4,4),b.fill()),b.fillStyle="#333",b.beginPath(), -b.moveTo(w+0.2*c,0.6*-c),b.lineTo(w+0.8*c,0.6*-c),b.lineTo(w+0.5*c,0.3*-c),b.fill());if(a.onDrawTitle)a.onDrawTitle(b)}if(r){if(a.onBounding)a.onBounding(t);z==e.TRANSPARENT_TITLE&&(t[1]-=g,t[3]+=g);b.lineWidth=1;b.globalAlpha=0.8;b.beginPath();q==e.BOX_SHAPE?b.rect(-6+t[0],-6+t[1],12+t[2],12+t[3]):q==e.ROUND_SHAPE||q==e.CARD_SHAPE&&a.flags.collapsed?b.roundRect(-6+t[0],-6+t[1],12+t[2],12+t[3],2*this.round_radius):q==e.CARD_SHAPE?b.roundRect(-6+t[0],-6+t[1],12+t[2],12+t[3],2*this.round_radius,2): -q==e.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0]+6,0,2*Math.PI);b.strokeStyle="#FFF";b.stroke();b.strokeStyle=f;b.globalAlpha=1}};var H=new Float32Array(4),p=new Float32Array(4),s=new Float32Array(2),l=new Float32Array(2);h.prototype.drawConnections=function(a){var b=e.getTime(),d=this.visible_area;H[0]=d[0]-20;H[1]=d[1]-20;H[2]=d[2]+40;H[3]=d[3]+40;a.lineWidth=this.connections_width;a.fillStyle="#AAA";a.strokeStyle="#AAA";a.globalAlpha=this.editor_alpha;for(var d=this.graph._nodes,f=0,g=d.length;f< -g;++f){var r=d[f];if(r.inputs&&r.inputs.length)for(var c=0;cp[2]&&(p[0]+=p[2],p[2]=Math.abs(p[2]));0>p[3]&&(p[1]+=p[3],p[3]=Math.abs(p[3]));if(C(p,H)){var A=q.outputs[z],z=r.inputs[c];if(A&& -z&&(q=A.dir||(q.horizontal?e.DOWN:e.RIGHT),z=z.dir||(r.horizontal?e.UP:e.LEFT),this.renderLink(a,w,u,h,!1,0,null,q,z),h&&h._last_time&&1E3>b-h._last_time)){var A=2-0.002*(b-h._last_time),R=a.globalAlpha;a.globalAlpha=R*A;this.renderLink(a,w,u,h,!0,A,"white",q,z);a.globalAlpha=R}}}}}}a.globalAlpha=1};h.prototype.renderLink=function(a,b,d,f,g,r,c,l,q,z){f&&this.visible_links.push(f);!c&&f&&(c=f.color||h.link_type_colors[f.type]);c||(c=this.default_link_color);null!=f&&this.highlighted_links[f.id]&& -(c="#FFF");l=l||e.RIGHT;q=q||e.LEFT;var w=B(b,d);this.render_connections_border&&0.6b[1]?0:Math.PI,a.save(),a.translate(u[0],u[1]),a.rotate(p),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore(),a.save(),a.translate(z[0],z[1]),a.rotate(A), -a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(g[0],g[1],5,0,2*Math.PI),a.fill());if(r)for(a.fillStyle=c,u=0;5>u;++u)r=(0.001*e.getTime()+0.2*u)%1,g=this.computeConnectionPoint(b,d,r,l,q),a.beginPath(),a.arc(g[0],g[1],5,0,2*Math.PI),a.fill()};h.prototype.computeConnectionPoint=function(a,b,d,f,g){f=f||e.RIGHT;g=g||e.LEFT;var r=B(a,b),c=[a[0],a[1]],l=[b[0],b[1]];switch(f){case e.LEFT:c[0]+=-0.25*r;break;case e.RIGHT:c[0]+=0.25*r;break;case e.UP:c[1]+= --0.25*r;break;case e.DOWN:c[1]+=0.25*r}switch(g){case e.LEFT:l[0]+=-0.25*r;break;case e.RIGHT:l[0]+=0.25*r;break;case e.UP:l[1]+=-0.25*r;break;case e.DOWN:l[1]+=0.25*r}f=(1-d)*(1-d)*(1-d);g=3*(1-d)*(1-d)*d;r=3*(1-d)*d*d;d*=d*d;return[f*a[0]+g*c[0]+r*l[0]+d*b[0],f*a[1]+g*c[1]+r*l[1]+d*b[1]]};h.prototype.drawExecutionOrder=function(a){a.shadowColor="transparent";a.globalAlpha=0.25;a.textAlign="center";a.strokeStyle="white";a.globalAlpha=0.75;for(var b=this.visible_nodes,d=0;dc||c>l-12||xu.last_y+h)){f=u.value;switch(u.type){case "button":if("mousemove"===d.type)break;u.callback&&setTimeout(function(){u.callback(u,q,a,b,d)},20);this.dirty_canvas=u.clicked=!0;break;case "slider":z=Math.clamp((c-10)/(l-20),0,1);u.value=u.options.min+(u.options.max-u.options.min)*z;u.callback&&setTimeout(function(){g(u,u.value)},20);this.dirty_canvas= -!0;break;case "number":case "combo":f=u.value;if("mousemove"==d.type&&"number"==u.type)u.value+=0.1*d.deltaX*(u.options.step||1),null!=u.options.min&&u.valueu.options.max&&(u.value=u.options.max);else if("mousedown"==d.type){var p=u.options.values;p&&p.constructor===Function&&(p=u.options.values(u,a));var A=null;"number"!=u.type&&(A=p.constructor===Array?p:Object.keys(p));c=40>c?-1:c>l-40?1:0;if("number"==u.type)u.value+=0.1*c*(u.options.step|| -1),null!=u.options.min&&u.valueu.options.max&&(u.value=u.options.max);else if(c)z=-1,this.last_mouseclick=0,z=p.constructor===Object?A.indexOf(String(u.value))+c:A.indexOf(u.value)+c,z>=A.length&&(z=A.length-1),0>z&&(z=0),u.value=p.constructor===Array?p[z]:z;else{var s=p!=A?Object.values(p):p;new e.ContextMenu(s,{scale:Math.max(1,this.ds.scale),event:d,className:"dark",callback:k.bind(u)},z);var k=function(a,b,d){p!=A&&(a=s.indexOf(a)); -this.value=a;g(this,a);q.dirty_canvas=!0;return!1}}}else"mouseup"==d.type&&"number"==u.type&&(c=40>c?-1:c>l-40?1:0,200>d.click_time&&0==c&&this.prompt("Value",u.value,function(a){this.value=Number(a);g(this,this.value)}.bind(u),d));f!=u.value&&setTimeout(function(){g(this,this.value)}.bind(u),20);this.dirty_canvas=!0;break;case "toggle":"mousedown"==d.type&&(u.value=!u.value,setTimeout(function(){g(u,u.value)},20));break;case "string":case "text":"mousedown"==d.type&&this.prompt("Value",u.value,function(a){this.value= -a;g(this,a)}.bind(u),d);break;default:u.mouse&&(this.dirty_canvas=u.mouse(d,[c,x],a))}f!=u.value&&a.graph._version++;return u}}}return null};h.prototype.drawGroups=function(a,b){if(this.graph){var d=this.graph._groups;b.save();b.globalAlpha=0.5*this.editor_alpha;for(var f=0;fthis.ds.scale,r=a._shape||a.constructor.shape||e.ROUND_SHAPE,s=a.constructor.title_mode,z=!0;s== +e.TRANSPARENT_TITLE?z=!1:s==e.AUTOHIDE_TITLE&&c&&(z=!0);v[0]=0;v[1]=z?-g:0;v[2]=d[0]+1;v[3]=z?d[1]+g:d[1];c=b.globalAlpha;b.beginPath();r==e.BOX_SHAPE||l?b.fillRect(v[0],v[1],v[2],v[3]):r==e.ROUND_SHAPE||r==e.CARD_SHAPE?b.roundRect(v[0],v[1],v[2],v[3],this.round_radius,r==e.CARD_SHAPE?0:this.round_radius):r==e.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0],0,2*Math.PI);b.fill();a.flags.collapsed||(b.shadowColor="transparent",b.fillStyle="rgba(0,0,0,0.2)",b.fillRect(0,-1,v[2],2));b.shadowColor="transparent"; +if(a.onDrawBackground)a.onDrawBackground(b,this,this.canvas,this.graph_mouse);if(z||s==e.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,g,d,this.ds.scale,f);else if(s!=e.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){z=a.constructor.title_color||f;a.flags.collapsed&&(b.shadowColor=e.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var u=h.gradients[z];u||(u=h.gradients[z]=b.createLinearGradient(0,0,400,0),u.addColorStop(0,z),u.addColorStop(1,"#000"));b.fillStyle= +u}else b.fillStyle=z;b.beginPath();r==e.BOX_SHAPE||l?b.rect(0,-g,d[0]+1,g):r!=e.ROUND_SHAPE&&r!=e.CARD_SHAPE||b.roundRect(0,-g,d[0]+1,g,this.round_radius,a.flags.collapsed?this.round_radius:0);b.fill();b.shadowColor="transparent"}if(a.onDrawTitleBox)a.onDrawTitleBox(b,g,d,this.ds.scale);else r==e.ROUND_SHAPE||r==e.CIRCLE_SHAPE||r==e.CARD_SHAPE?(l&&(b.fillStyle="black",b.beginPath(),b.arc(0.5*g,-0.5*g,6,0,2*Math.PI),b.fill()),b.fillStyle=a.boxcolor||e.NODE_DEFAULT_BOXCOLOR,l?b.fillRect(0.5*g-5,-0.5* +g-5,10,10):(b.beginPath(),b.arc(0.5*g,-0.5*g,5,0,2*Math.PI),b.fill())):(l&&(b.fillStyle="black",b.fillRect(0.5*(g-10)-1,-0.5*(g+10)-1,12,12)),b.fillStyle=a.boxcolor||e.NODE_DEFAULT_BOXCOLOR,b.fillRect(0.5*(g-10),-0.5*(g+10),10,10));b.globalAlpha=c;if(a.onDrawTitleText)a.onDrawTitleText(b,g,d,this.ds.scale,this.title_text_font,q);!l&&(b.font=this.title_text_font,c=String(a.getTitle()))&&(b.fillStyle=q?"white":a.constructor.title_text_color||this.node_title_color,a.flags.collapsed?(b.textAlign="left", +b.measureText(c),b.fillText(c.substr(0,20),g,e.NODE_TITLE_TEXT_Y-g),b.textAlign="left"):(b.textAlign="left",b.fillText(c,g,e.NODE_TITLE_TEXT_Y-g)));a.flags.collapsed||!a.subgraph||a.skip_subgraph_button||(c=e.NODE_TITLE_HEIGHT,z=a.size[0]-c,u=e.isInsideRectangle(this.graph_mouse[0]-a.pos[0],this.graph_mouse[1]-a.pos[1],z+2,-c+2,c-4,c-4),b.fillStyle=u?"#888":"#555",r==e.BOX_SHAPE||l?b.fillRect(z+2,-c+2,c-4,c-4):(b.beginPath(),b.roundRect(z+2,-c+2,c-4,c-4,4),b.fill()),b.fillStyle="#333",b.beginPath(), +b.moveTo(z+0.2*c,0.6*-c),b.lineTo(z+0.8*c,0.6*-c),b.lineTo(z+0.5*c,0.3*-c),b.fill());if(a.onDrawTitle)a.onDrawTitle(b)}if(q){if(a.onBounding)a.onBounding(v);s==e.TRANSPARENT_TITLE&&(v[1]-=g,v[3]+=g);b.lineWidth=1;b.globalAlpha=0.8;b.beginPath();r==e.BOX_SHAPE?b.rect(-6+v[0],-6+v[1],12+v[2],12+v[3]):r==e.ROUND_SHAPE||r==e.CARD_SHAPE&&a.flags.collapsed?b.roundRect(-6+v[0],-6+v[1],12+v[2],12+v[3],2*this.round_radius):r==e.CARD_SHAPE?b.roundRect(-6+v[0],-6+v[1],12+v[2],12+v[3],2*this.round_radius,2): +r==e.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0]+6,0,2*Math.PI);b.strokeStyle="#FFF";b.stroke();b.strokeStyle=f;b.globalAlpha=1}};var H=new Float32Array(4),w=new Float32Array(4),t=new Float32Array(2),l=new Float32Array(2);h.prototype.drawConnections=function(a){var b=e.getTime(),d=this.visible_area;H[0]=d[0]-20;H[1]=d[1]-20;H[2]=d[2]+40;H[3]=d[3]+40;a.lineWidth=this.connections_width;a.fillStyle="#AAA";a.strokeStyle="#AAA";a.globalAlpha=this.editor_alpha;for(var d=this.graph._nodes,f=0,g=d.length;f< +g;++f){var q=d[f];if(q.inputs&&q.inputs.length)for(var c=0;cw[2]&&(w[0]+=w[2],w[2]=Math.abs(w[2]));0>w[3]&&(w[1]+=w[3],w[3]=Math.abs(w[3]));if(B(w,H)){var p=r.outputs[s],s=q.inputs[c];if(p&& +s&&(r=p.dir||(r.horizontal?e.DOWN:e.RIGHT),s=s.dir||(q.horizontal?e.UP:e.LEFT),this.renderLink(a,z,u,h,!1,0,null,r,s),h&&h._last_time&&1E3>b-h._last_time)){var p=2-0.002*(b-h._last_time),R=a.globalAlpha;a.globalAlpha=R*p;this.renderLink(a,z,u,h,!0,p,"white",r,s);a.globalAlpha=R}}}}}}a.globalAlpha=1};h.prototype.renderLink=function(a,b,d,f,g,q,c,l,r,s){f&&this.visible_links.push(f);!c&&f&&(c=f.color||h.link_type_colors[f.type]);c||(c=this.default_link_color);null!=f&&this.highlighted_links[f.id]&& +(c="#FFF");l=l||e.RIGHT;r=r||e.LEFT;var z=D(b,d);this.render_connections_border&&0.6b[1]?0:Math.PI,a.save(),a.translate(u[0],u[1]),a.rotate(w),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore(),a.save(),a.translate(s[0],s[1]),a.rotate(p), +a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(g[0],g[1],5,0,2*Math.PI),a.fill());if(q)for(a.fillStyle=c,u=0;5>u;++u)q=(0.001*e.getTime()+0.2*u)%1,g=this.computeConnectionPoint(b,d,q,l,r),a.beginPath(),a.arc(g[0],g[1],5,0,2*Math.PI),a.fill()};h.prototype.computeConnectionPoint=function(a,b,d,f,g){f=f||e.RIGHT;g=g||e.LEFT;var q=D(a,b),c=[a[0],a[1]],l=[b[0],b[1]];switch(f){case e.LEFT:c[0]+=-0.25*q;break;case e.RIGHT:c[0]+=0.25*q;break;case e.UP:c[1]+= +-0.25*q;break;case e.DOWN:c[1]+=0.25*q}switch(g){case e.LEFT:l[0]+=-0.25*q;break;case e.RIGHT:l[0]+=0.25*q;break;case e.UP:l[1]+=-0.25*q;break;case e.DOWN:l[1]+=0.25*q}f=(1-d)*(1-d)*(1-d);g=3*(1-d)*(1-d)*d;q=3*(1-d)*d*d;d*=d*d;return[f*a[0]+g*c[0]+q*l[0]+d*b[0],f*a[1]+g*c[1]+q*l[1]+d*b[1]]};h.prototype.drawExecutionOrder=function(a){a.shadowColor="transparent";a.globalAlpha=0.25;a.textAlign="center";a.strokeStyle="white";a.globalAlpha=0.75;for(var b=this.visible_nodes,d=0;dc||c>h-12||lu.last_y+w)){f=u.value;switch(u.type){case "button":if("mousemove"===d.type)break;u.callback&&setTimeout(function(){u.callback(u,r,a,b,d)},20);this.dirty_canvas=u.clicked=!0;break;case "slider":s=Math.clamp((c-10)/(h-20),0,1);u.value=u.options.min+(u.options.max-u.options.min)*s;u.callback&&setTimeout(function(){g(u,u.value)},20);this.dirty_canvas= +!0;break;case "number":case "combo":f=u.value;if("mousemove"==d.type&&"number"==u.type)u.value+=0.1*d.deltaX*(u.options.step||1),null!=u.options.min&&u.valueu.options.max&&(u.value=u.options.max);else if("mousedown"==d.type){var p=u.options.values;p&&p.constructor===Function&&(p=u.options.values(u,a));var t=null;"number"!=u.type&&(t=p.constructor===Array?p:Object.keys(p));c=40>c?-1:c>h-40?1:0;if("number"==u.type)u.value+=0.1*c*(u.options.step|| +1),null!=u.options.min&&u.valueu.options.max&&(u.value=u.options.max);else if(c)s=-1,this.last_mouseclick=0,s=p.constructor===Object?t.indexOf(String(u.value))+c:t.indexOf(u.value)+c,s>=t.length&&(s=t.length-1),0>s&&(s=0),u.value=p.constructor===Array?p[s]:s;else{var k=p!=t?Object.values(p):p;new e.ContextMenu(k,{scale:Math.max(1,this.ds.scale),event:d,className:"dark",callback:n.bind(u)},s);var n=function(a,b,d){p!=t&&(a=k.indexOf(a)); +this.value=a;g(this,a);r.dirty_canvas=!0;return!1}}}else"mouseup"==d.type&&"number"==u.type&&(c=40>c?-1:c>h-40?1:0,200>d.click_time&&0==c&&this.prompt("Value",u.value,function(a){this.value=Number(a);g(this,this.value)}.bind(u),d));f!=u.value&&setTimeout(function(){g(this,this.value)}.bind(u),20);this.dirty_canvas=!0;break;case "toggle":"mousedown"==d.type&&(u.value=!u.value,setTimeout(function(){g(u,u.value)},20));break;case "string":case "text":"mousedown"==d.type&&this.prompt("Value",u.value,function(a){this.value= +a;g(this,a)}.bind(u),d);break;default:u.mouse&&(this.dirty_canvas=u.mouse(d,[c,l],a))}f!=u.value&&a.graph._version++;return u}}}return null};h.prototype.drawGroups=function(a,b){if(this.graph){var d=this.graph._groups;b.save();b.globalAlpha=0.5*this.editor_alpha;for(var f=0;fd&&0.01>b.editor_alpha&&(clearInterval(f),1>d&&(b.live_mode=!0));1"+q+""+a+"",value:q})}if(p.length)return new e.ContextMenu(p,{event:d,callback:c,parentMenu:f,allow_html:!0,node:g},b),!1}};h.decodeHTML=function(a){var b=document.createElement("div");b.innerText=a;return b.innerHTML};h.onResizeNode=function(a,b,d,f,g){if(g){g.size=g.computeSize();if(g.onResize)g.onResize(g.size);g.setDirtyCanvas(!0,!0)}};h.prototype.showLinkMenu=function(a,b){var d=this;console.log(a); +b.screenY,b.clientX,b.clientY,!1,!1,!1,!1,0,null);b.target.dispatchEvent(g);a.preventDefault()};h.onGroupAdd=function(a,b,d){a=h.active_canvas;a.getCanvasWindow();b=new e.LGraphGroup;b.pos=a.convertEventToCanvasOffset(d);a.graph.add(b)};h.onMenuAdd=function(a,b,d,f,g){function c(a,b,d){a=e.getNodeTypesInCategory(a.value,p.filter||s.filter);b=[];for(var g in a)a[g].skip_list||b.push({content:a[g].title,value:a[g].type});new e.ContextMenu(b,{event:d,callback:l,parentMenu:u},r);return!1}function l(a, +b){var d=f.getFirstEvent();p.graph.beforeChange();var c=e.createNode(a.value);c&&(c.pos=p.convertEventToCanvasOffset(d),p.graph.add(c));g&&g(c);p.graph.afterChange()}var p=h.active_canvas,r=p.getCanvasWindow(),s=p.graph;if(s){a=e.getNodeTypesCategories(p.filter||s.filter);b=[];for(var z in a)a[z]&&b.push({value:a[z],content:a[z],has_submenu:!0});var u=new e.ContextMenu(b,{event:d,callback:c,parentMenu:f},r);return!1}};h.onMenuCollapseAll=function(){};h.onMenuNodeEdit=function(){};h.showMenuNodeOptionalInputs= +function(a,b,d,f,g){function c(a,b,d){g&&(a.callback&&a.callback.call(l,g,a,b,d),a.value&&(g.graph.beforeChange(),g.addInput(a.value[0],a.value[1],a.value[2]),g.setDirtyCanvas(!0,!0),g.graph.afterChange()))}if(g){var l=this;a=h.active_canvas.getCanvasWindow();b=g.optional_inputs;g.onGetInputs&&(b=g.onGetInputs());var p=[];if(b)for(var r in b){var s=b[r];if(s){var z=s[0];s[2]&&s[2].label&&(z=s[2].label);z={content:z,value:s};s[1]==e.ACTION&&(z.className="event");p.push(z)}else p.push(null)}this.onMenuNodeInputs&& +(p=this.onMenuNodeInputs(p));if(p.length)return new e.ContextMenu(p,{event:d,callback:c,parentMenu:f,node:g},a),!1;console.log("no input entries")}};h.showMenuNodeOptionalOutputs=function(a,b,d,f,g){function c(a,b,d){if(g&&(a.callback&&a.callback.call(l,g,a,b,d),a.value))if(d=a.value[1],!d||d.constructor!==Object&&d.constructor!==Array)g.graph.beforeChange(),g.addOutput(a.value[0],a.value[1],a.value[2]),g.setDirtyCanvas(!0,!0),g.graph.afterChange();else{a=[];for(var r in d)a.push({content:r,value:d[r]}); +new e.ContextMenu(a,{event:b,callback:c,parentMenu:f,node:g});return!1}}if(g){var l=this;a=h.active_canvas.getCanvasWindow();b=g.optional_outputs;g.onGetOutputs&&(b=g.onGetOutputs());var p=[];if(b)for(var r in b){var s=b[r];if(!s)p.push(null);else if(!g.flags||!g.flags.skip_repeated_outputs||-1==g.findOutputSlot(s[0])){var z=s[0];s[2]&&s[2].label&&(z=s[2].label);z={content:z,value:s};s[1]==e.EVENT&&(z.className="event");p.push(z)}}this.onMenuNodeOutputs&&(p=this.onMenuNodeOutputs(p));if(p.length)return new e.ContextMenu(p, +{event:d,callback:c,parentMenu:f,node:g},a),!1}};h.onShowMenuNodeProperties=function(a,b,d,f,g){function c(a,b,d,f){g&&(b=this.getBoundingClientRect(),l.showEditPropertyValue(g,a.value,{position:[b.left,b.top]}))}if(g&&g.properties){var l=h.active_canvas;b=l.getCanvasWindow();var p=[],r;for(r in g.properties){a=void 0!==g.properties[r]?g.properties[r]:" ";"object"==typeof a&&(a=JSON.stringify(a));var s=g.getPropertyInfo(r);if("enum"==s.type||"combo"==s.type)a=h.getPropertyPrintableValue(a,s.values); +a=h.decodeHTML(a);p.push({content:""+r+""+a+"",value:r})}if(p.length)return new e.ContextMenu(p,{event:d,callback:c,parentMenu:f,allow_html:!0,node:g},b),!1}};h.decodeHTML=function(a){var b=document.createElement("div");b.innerText=a;return b.innerHTML};h.onResizeNode=function(a,b,d,f,g){if(g){g.size=g.computeSize();if(g.onResize)g.onResize(g.size);g.setDirtyCanvas(!0,!0)}};h.prototype.showLinkMenu=function(a,b){var d=this;console.log(a); var f=new e.ContextMenu(["Add Node",null,"Delete"],{event:b,title:null!=a.data?a.data.constructor.name:null,callback:function(b,c,e){switch(b){case "Add Node":h.onMenuAdd(null,null,e,f,function(b){console.log("node autoconnect");var g=d.graph.getNodeById(a.origin_id),f=d.graph.getNodeById(a.target_id);b.inputs&&b.inputs.length&&b.outputs&&b.outputs.length&&g.outputs[a.origin_slot].type==b.inputs[0].type&&b.outputs[0].type==f.inputs[0].type&&(g.connect(a.origin_slot,b,0),b.connect(0,f,a.target_slot), -b.pos[0]-=0.5*b.size[0])});break;case "Delete":d.graph.removeLink(a.id)}}});return!1};h.onShowPropertyEditor=function(a,b,d,f,g){function c(){var b=q.value;"Number"==a.type?b=Number(b):"Boolean"==a.type&&(b=Boolean(b));g[e]=b;l.parentNode&&l.parentNode.removeChild(l);g.setDirtyCanvas(!0,!0)}var e=a.property||"title";b=g[e];var l=document.createElement("div");l.className="graphdialog";l.innerHTML="";l.querySelector(".name").innerText= -e;var q=l.querySelector("input");q&&(q.value=b,q.addEventListener("blur",function(a){this.focus()}),q.addEventListener("keydown",function(a){13==a.keyCode&&(c(),a.preventDefault(),a.stopPropagation())}));b=h.active_canvas.canvas;d=b.getBoundingClientRect();var z=f=-20;d&&(f-=d.left,z-=d.top);event?(l.style.left=event.clientX+f+"px",l.style.top=event.clientY+z+"px"):(l.style.left=0.5*b.width+f+"px",l.style.top=0.5*b.height+z+"px");l.querySelector("button").addEventListener("click",c);b.parentNode.appendChild(l)}; +b.pos[0]-=0.5*b.size[0])});break;case "Delete":d.graph.removeLink(a.id)}}});return!1};h.onShowPropertyEditor=function(a,b,d,f,g){function c(){var b=r.value;"Number"==a.type?b=Number(b):"Boolean"==a.type&&(b=Boolean(b));g[e]=b;l.parentNode&&l.parentNode.removeChild(l);g.setDirtyCanvas(!0,!0)}var e=a.property||"title";b=g[e];var l=document.createElement("div");l.className="graphdialog";l.innerHTML="";l.querySelector(".name").innerText= +e;var r=l.querySelector("input");r&&(r.value=b,r.addEventListener("blur",function(a){this.focus()}),r.addEventListener("keydown",function(a){13==a.keyCode&&(c(),a.preventDefault(),a.stopPropagation())}));b=h.active_canvas.canvas;d=b.getBoundingClientRect();var s=f=-20;d&&(f-=d.left,s-=d.top);event?(l.style.left=event.clientX+f+"px",l.style.top=event.clientY+s+"px"):(l.style.left=0.5*b.width+f+"px",l.style.top=0.5*b.height+s+"px");l.querySelector("button").addEventListener("click",c);b.parentNode.appendChild(l)}; h.prototype.prompt=function(a,b,d,f){var g=this;a=a||"";var c=!1,e=document.createElement("div");e.className="graphdialog rounded";e.innerHTML=" ";e.close=function(){g.prompt_box=null;e.parentNode&&e.parentNode.removeChild(e)};1h.search_limit)break}}x=null;if(Array.prototype.filter)x=Object.keys(e.registered_node_types).filter(T);else for(l in x=[],e.registered_node_types)T(l)&&x.push(l);for(l=0;lh.search_limit);l++);var T=function(a){var b= -e.registered_node_types[a];return q&&b.filter!=q?!1:-1!==a.toLowerCase().indexOf(d)}}}var g=this,c=h.active_canvas,l=c.canvas,p=l.ownerDocument||document,q=document.createElement("div");q.className="litegraph litesearchbox graphdialog rounded";q.innerHTML="Search
";q.close=function(){g.search_box=null;p.body.focus();p.body.style.overflow="";setTimeout(function(){g.canvas.focus()},20);q.parentNode&& -q.parentNode.removeChild(q)};var z=null;1 -l.height-200&&(A.style.maxHeight=l.height-a.layerY-20+"px");n.focus();return q};h.prototype.showEditPropertyValue=function(a,b,d){function f(){g(u.value)}function g(g){c&&c.values&&c.values.constructor===Object&&void 0!=c.values[g]&&(g=c.values[g]);"number"==typeof a.properties[b]&&(g=Number(g));if("array"==e||"object"==e)g=JSON.parse(g);a.properties[b]=g;a.graph&&a.graph._version++;if(a.onPropertyChanged)a.onPropertyChanged(b,g);if(d.onclose)d.onclose();h.close();a.setDirtyCanvas(!0,!0)}if(a&&void 0!== -a.properties[b]){d=d||{};var c=a.getPropertyInfo(b),e=c.type,l="";if("string"==e||"number"==e||"array"==e||"object"==e)l="";else if("enum"!=e&&"combo"!=e||!c.values)if("boolean"==e)l="";else{console.warn("unknown type: "+e);return}else{var l=""}var h=this.createDialog(""+b+""+l+"",d);if("enum"!=e&&"combo"!=e||!c.values)if("boolean"==e)(u=h.querySelector("input"))&&u.addEventListener("click",function(a){g(!!u.checked)});else{if(u=h.querySelector("input"))u.addEventListener("blur",function(a){this.focus()}),p=void 0!==a.properties[b]?a.properties[b]:"",p=JSON.stringify(p),u.value=p,u.addEventListener("keydown", -function(a){13==a.keyCode&&(f(),a.preventDefault(),a.stopPropagation())})}else{var u=h.querySelector("select");u.addEventListener("change",function(a){g(a.target.value)})}h.querySelector("button").addEventListener("click",f);return h}};h.prototype.createDialog=function(a,b){b=b||{};var d=document.createElement("div");d.className="graphdialog";d.innerHTML=a;var f=this.canvas.getBoundingClientRect(),g=-20,c=-20;f&&(g-=f.left,c-=f.top);b.position?(g+=b.position[0],c+=b.position[1]):b.event?(g+=b.event.clientX, +e;e.querySelector(".name").innerText=a;e.querySelector(".value").value=b;var l=e.querySelector("input");l.addEventListener("keydown",function(a){c=!0;if(27==a.keyCode)e.close();else if(13==a.keyCode)d&&d(this.value),e.close();else return;a.preventDefault();a.stopPropagation()});e.querySelector("button").addEventListener("click",function(a){d&&d(l.value);g.setDirty(!0);e.close()});a=h.active_canvas.canvas;b=a.getBoundingClientRect();var r=-20,s=-20;b&&(r-=b.left,s-=b.top);f?(e.style.left=f.clientX+ +r+"px",e.style.top=f.clientY+s+"px"):(e.style.left=0.5*a.width+r+"px",e.style.top=0.5*a.height+s+"px");a.parentNode.appendChild(e);setTimeout(function(){l.focus()},10);return e};h.search_limit=-1;h.prototype.showSearchBox=function(a){function b(b){if(b)if(g.onSearchBoxSelection)g.onSearchBoxSelection(b,a,c);else{var d=e.searchbox_extras[b.toLowerCase()];d&&(b=d.type);c.graph.beforeChange();if(b=e.createNode(b))b.pos=c.convertEventToCanvasOffset(a),c.graph.add(b);if(d&&d.data){if(d.data.properties)for(var f in d.data.properties)b.addProperty(f, +d.data.properties[f]);if(d.data.inputs)for(f in b.inputs=[],d.data.inputs)b.addOutput(d.data.inputs[f][0],d.data.inputs[f][1]);if(d.data.outputs)for(f in b.outputs=[],d.data.outputs)b.addOutput(d.data.outputs[f][0],d.data.outputs[f][1]);d.data.title&&(b.title=d.data.title);d.data.json&&b.configure(d.data.json);c.graph.afterChange()}}r.close()}function d(a){var b=k;k&&k.classList.remove("selected");k?(k=a?k.nextSibling:k.previousSibling)||(k=b):k=a?w.childNodes[0]:w.childNodes[w.childNodes.length]; +k&&(k.classList.add("selected"),k.scrollIntoView({block:"end",behavior:"smooth"}))}function f(){function a(d,g){var f=document.createElement("div");u||(u=d);f.innerText=d;f.dataset.type=escape(d);f.className="litegraph lite-search-item";g&&(f.className+=" "+g);f.addEventListener("click",function(a){b(unescape(this.dataset.type))});w.appendChild(f)}t=null;var d=n.value;u=null;w.innerHTML="";if(d)if(g.onSearchBox){var f=g.onSearchBox(w,d,c);if(f)for(var l=0;lh.search_limit)break}}s=null;if(Array.prototype.filter)s=Object.keys(e.registered_node_types).filter(T);else for(l in s=[],e.registered_node_types)T(l)&&s.push(l);for(l=0;lh.search_limit);l++);var T=function(a){var b= +e.registered_node_types[a];return r&&b.filter!=r?!1:-1!==a.toLowerCase().indexOf(d)}}}var g=this,c=h.active_canvas,l=c.canvas,p=l.ownerDocument||document,r=document.createElement("div");r.className="litegraph litesearchbox graphdialog rounded";r.innerHTML="Search
";r.close=function(){g.search_box=null;p.body.focus();p.body.style.overflow="";setTimeout(function(){g.canvas.focus()},20);r.parentNode&& +r.parentNode.removeChild(r)};var s=null;1 +l.height-200&&(w.style.maxHeight=l.height-a.layerY-20+"px");n.focus();return r};h.prototype.showEditPropertyValue=function(a,b,d){function f(){g(u.value)}function g(g){c&&c.values&&c.values.constructor===Object&&void 0!=c.values[g]&&(g=c.values[g]);"number"==typeof a.properties[b]&&(g=Number(g));if("array"==e||"object"==e)g=JSON.parse(g);a.properties[b]=g;a.graph&&a.graph._version++;if(a.onPropertyChanged)a.onPropertyChanged(b,g);if(d.onclose)d.onclose();p.close();a.setDirtyCanvas(!0,!0)}if(a&&void 0!== +a.properties[b]){d=d||{};var c=a.getPropertyInfo(b),e=c.type,l="";if("string"==e||"number"==e||"array"==e||"object"==e)l="";else if("enum"!=e&&"combo"!=e||!c.values)if("boolean"==e)l="";else{console.warn("unknown type: "+e);return}else{var l=""}var p=this.createDialog(""+b+""+l+"",d);if("enum"!=e&&"combo"!=e||!c.values)if("boolean"==e)(u=p.querySelector("input"))&&u.addEventListener("click",function(a){g(!!u.checked)});else{if(u=p.querySelector("input"))u.addEventListener("blur",function(a){this.focus()}),s=void 0!==a.properties[b]?a.properties[b]:"",s=JSON.stringify(s),u.value=s,u.addEventListener("keydown", +function(a){13==a.keyCode&&(f(),a.preventDefault(),a.stopPropagation())})}else{var u=p.querySelector("select");u.addEventListener("change",function(a){g(a.target.value)})}p.querySelector("button").addEventListener("click",f);return p}};h.prototype.createDialog=function(a,b){b=b||{};var d=document.createElement("div");d.className="graphdialog";d.innerHTML=a;var f=this.canvas.getBoundingClientRect(),g=-20,c=-20;f&&(g-=f.left,c-=f.top);b.position?(g+=b.position[0],c+=b.position[1]):b.event?(g+=b.event.clientX, c+=b.event.clientY):(g+=0.5*this.canvas.width,c+=0.5*this.canvas.height);d.style.left=g+"px";d.style.top=c+"px";this.canvas.parentNode.appendChild(d);d.close=function(){this.parentNode&&this.parentNode.removeChild(this)};return d};h.prototype.createPanel=function(a,b){b=b||{};var d=b.window||window,f=document.createElement("div");f.className="litegraph dialog";f.innerHTML="
"; f.header=f.querySelector(".dialog-header");b.width&&(f.style.width=b.width+(b.width.constructor===Number?"px":""));b.height&&(f.style.height=b.height+(b.height.constructor===Number?"px":""));if(b.closable){var g=document.createElement("span");g.innerHTML="✕";g.classList.add("close");g.addEventListener("click",function(){f.close()});f.header.appendChild(g)}f.title_element=f.querySelector(".dialog-title");f.title_element.innerText=a;f.content=f.querySelector(".dialog-content");f.footer=f.querySelector(".dialog-footer"); f.close=function(){this.parentNode.removeChild(this)};f.clear=function(){this.content.innerHTML=""};f.addHTML=function(a,b,d){var g=document.createElement("div");b&&(g.className=b);g.innerHTML=a;d?f.footer.appendChild(g):f.content.appendChild(g);return g};f.addButton=function(a,b,d){var g=document.createElement("button");g.innerText=a;g.options=d;g.classList.add("btn");g.addEventListener("click",b);f.footer.appendChild(g);return g};f.addSeparator=function(){var a=document.createElement("div");a.className= -"separator";f.content.appendChild(a)};f.addWidget=function(a,b,g,c,l){function p(a,b){console.log("change",a,b);c.callback&&c.callback(a,b);l&&l(a,b)}c=c||{};var u=String(g);"number"==a&&(u=g.toFixed(3));var A=document.createElement("div");A.className="property";A.innerHTML="";A.querySelector(".property_name").innerText=b;var s=A.querySelector(".property_value");s.innerText=u;A.dataset.property=b;A.dataset.type=c.type||a;A.options= -c;A.value=g;if("boolean"==a)A.classList.add("boolean"),g&&A.classList.add("bool-on"),A.addEventListener("click",function(){var a=this.dataset.property;this.value=!this.value;this.classList.toggle("bool-on");this.querySelector(".property_value").innerText=this.value?"true":"false";p(a,this.value)});else if("string"==a||"number"==a)s.setAttribute("contenteditable",!0),s.addEventListener("keydown",function(a){"Enter"==a.code&&(a.preventDefault(),this.blur())}),s.addEventListener("blur",function(){var a= -this.innerText,b=this.parentNode.dataset.property;"number"==this.parentNode.dataset.type&&(a=Number(a));p(b,a)});else if("enum"==a||"combo"==a)u=h.getPropertyPrintableValue(g,c.values);s.innerText=u;s.addEventListener("click",function(a){var b=this.parentNode.dataset.property,g=this;new e.ContextMenu(c.values||[],{event:a,className:"dark",callback:function(a,d,f){g.innerText=a;p(b,a);return!1}},d)});f.content.appendChild(A);return A};return f};h.getPropertyPrintableValue=function(a,b){if(!b||b.constructor=== +"separator";f.content.appendChild(a)};f.addWidget=function(a,b,g,c,l){function p(a,b){console.log("change",a,b);c.callback&&c.callback(a,b);l&&l(a,b)}c=c||{};var u=String(g);"number"==a&&(u=g.toFixed(3));var w=document.createElement("div");w.className="property";w.innerHTML="";w.querySelector(".property_name").innerText=b;var t=w.querySelector(".property_value");t.innerText=u;w.dataset.property=b;w.dataset.type=c.type||a;w.options= +c;w.value=g;if("boolean"==a)w.classList.add("boolean"),g&&w.classList.add("bool-on"),w.addEventListener("click",function(){var a=this.dataset.property;this.value=!this.value;this.classList.toggle("bool-on");this.querySelector(".property_value").innerText=this.value?"true":"false";p(a,this.value)});else if("string"==a||"number"==a)t.setAttribute("contenteditable",!0),t.addEventListener("keydown",function(a){"Enter"==a.code&&(a.preventDefault(),this.blur())}),t.addEventListener("blur",function(){var a= +this.innerText,b=this.parentNode.dataset.property;"number"==this.parentNode.dataset.type&&(a=Number(a));p(b,a)});else if("enum"==a||"combo"==a)u=h.getPropertyPrintableValue(g,c.values);t.innerText=u;t.addEventListener("click",function(a){var b=this.parentNode.dataset.property,g=this;new e.ContextMenu(c.values||[],{event:a,className:"dark",callback:function(a,d,f){g.innerText=a;p(b,a);return!1}},d)});f.content.appendChild(w);return w};return f};h.getPropertyPrintableValue=function(a,b){if(!b||b.constructor=== Array)return String(a);if(b.constructor===Object){var d="",f;for(f in b)if(b[f]==a){d=f;break}return String(a)+" ("+d+")"}};h.prototype.showShowNodePanel=function(a){window.SELECTED_NODE=a;var b=document.querySelector("#node-panel");b&&b.close();var d=this.getCanvasWindow(),b=this.createPanel(a.title||"",{closable:!0,window:d});b.id="node-panel";b.node=a;b.classList.add("settings");var f=this;(function(){b.content.innerHTML="";b.addHTML(""+a.type+""+ (a.constructor.desc||"")+"");b.addHTML("

Properties

");for(var d in a.properties){var c=a.properties[d],e=a.getPropertyInfo(d);a.onAddPropertyToPanel&&a.onAddPropertyToPanel(d,b)||b.addWidget(e.widget||e.type,d,c,e,function(b,d){f.graph.beforeChange(a);a.setProperty(b,d);f.graph.afterChange();f.dirty_canvas=!0})}b.addSeparator();if(a.onShowCustomPanelInfo)a.onShowCustomPanelInfo(b);b.addButton("Delete",function(){a.block_delete||(a.graph.remove(a),b.close())}).classList.add("delete")})(); this.canvas.parentNode.appendChild(b)};h.prototype.showSubgraphPropertiesDialog=function(a){function b(){f.clear();if(a.inputs)for(var d=0;d","subgraph_property");e.dataset.name=c.name;e.dataset.slot=d;e.querySelector(".name").innerText=c.name;e.querySelector(".type").innerText=c.type;e.querySelector("button").addEventListener("click", @@ -251,8 +252,8 @@ a);return b};h.prototype.getGroupMenuOptions=function(a){return[{content:"Title" a.removeInput(b.slot):b.output&&a.removeOutput(b.slot);else if("Disconnect Links"==b.content)b=b.slot,b.output?a.disconnectOutput(b.slot):b.input&&a.disconnectInput(b.slot);else if("Rename Slot"==b.content){b=b.slot;var c=b.input?a.getInputInfo(b.slot):a.getOutputInfo(b.slot),e=d.createDialog("Name",g),l=e.querySelector("input");l&&c&&(l.value=c.label||"");e.querySelector("button").addEventListener("click",function(a){l.value&& (c&&(c.label=l.value),d.setDirty(!0));e.close()})}},extra:a};a&&(c.title=a.type);var l=null;a&&(l=a.getSlotInPosition(b.canvasX,b.canvasY),h.active_node=a);if(l){g=[];if(a.getSlotMenuOptions)g=a.getSlotMenuOptions(l);else{l&&l.output&&l.output.links&&l.output.links.length&&g.push({content:"Disconnect Links",slot:l});var p=l.input||l.output;g.push(p.locked?"Cannot remove":{content:"Remove Slot",slot:l});g.push(p.nameLocked?"Cannot rename":{content:"Rename Slot",slot:l})}c.title=(l.input?l.input.type: l.output.type)||"*";l.input&&l.input.type==e.ACTION&&(c.title="Action");l.output&&l.output.type==e.EVENT&&(c.title="Event")}else a?g=this.getNodeMenuOptions(a):(g=this.getCanvasMenuOptions(),(l=this.graph.getGroupOnPos(b.canvasX,b.canvasY))&&g.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:l,options:this.getGroupMenuOptions(l)}}));g&&new e.ContextMenu(g,c,f)};"undefined"!=typeof window&&window.CanvasRenderingContext2D&&(window.CanvasRenderingContext2D.prototype.roundRect= -function(a,b,d,f,g,c){void 0===g&&(g=5);void 0===c&&(c=g);this.moveTo(a+g,b);this.lineTo(a+d-g,b);this.quadraticCurveTo(a+d,b,a+d,b+g);this.lineTo(a+d,b+f-c);this.quadraticCurveTo(a+d,b+f,a+d-c,b+f);this.lineTo(a+c,b+f);this.quadraticCurveTo(a,b+f,a,b+f-c);this.lineTo(a,b+g);this.quadraticCurveTo(a,b,a+g,b)});e.compareObjects=function(a,b){for(var d in a)if(a[d]!=b[d])return!1;return!0};e.distance=B;e.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+ -","+Math.round(255*a[2]).toFixed()+","+(4==a.length?a[3].toFixed(2):"1.0")+")"};e.isInsideRectangle=y;e.growBounding=function(a,b,d){ba[2]&&(a[2]=b);da[3]&&(a[3]=d)};e.isInsideBounding=function(a,b){return a[0]b[1][0]||a[1]>b[1][1]?!1:!0};e.overlapBounding=C;e.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),d=0,f,g,c=0;6>c;c+=2)f="0123456789ABCDEF".indexOf(a.charAt(c)),g="0123456789ABCDEF".indexOf(a.charAt(c+ +function(a,b,d,f,g,c){void 0===g&&(g=5);void 0===c&&(c=g);this.moveTo(a+g,b);this.lineTo(a+d-g,b);this.quadraticCurveTo(a+d,b,a+d,b+g);this.lineTo(a+d,b+f-c);this.quadraticCurveTo(a+d,b+f,a+d-c,b+f);this.lineTo(a+c,b+f);this.quadraticCurveTo(a,b+f,a,b+f-c);this.lineTo(a,b+g);this.quadraticCurveTo(a,b,a+g,b)});e.compareObjects=function(a,b){for(var d in a)if(a[d]!=b[d])return!1;return!0};e.distance=D;e.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+ +","+Math.round(255*a[2]).toFixed()+","+(4==a.length?a[3].toFixed(2):"1.0")+")"};e.isInsideRectangle=x;e.growBounding=function(a,b,d){ba[2]&&(a[2]=b);da[3]&&(a[3]=d)};e.isInsideBounding=function(a,b){return a[0]b[1][0]||a[1]>b[1][1]?!1:!0};e.overlapBounding=B;e.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),d=0,f,g,c=0;6>c;c+=2)f="0123456789ABCDEF".indexOf(a.charAt(c)),g="0123456789ABCDEF".indexOf(a.charAt(c+ 1)),b[d]=16*f+g,d++;return b};e.num2hex=function(a){for(var b="#",d,f,g=0;3>g;g++)d=a[g]/16,f=a[g]%16,b+="0123456789ABCDEF".charAt(d)+"0123456789ABCDEF".charAt(f);return b};G.prototype.addItem=function(a,b,d){function f(a){var b=this.value;b&&b.has_submenu&&g.call(this,a)}function g(a){var b=this.value,g=!0;c.current_submenu&&c.current_submenu.close(a);if(d.callback){var f=d.callback.call(this,b,d,a,c,d.node);!0===f&&(g=!1)}if(b&&(b.callback&&!d.ignore_item_callbacks&&!0!==b.disabled&&(f=b.callback.call(this, b,d,a,c,d.extra),!0===f&&(g=!1)),b.submenu)){if(!b.submenu.options)throw"ContextMenu submenu needs options";new c.constructor(b.submenu.options,{callback:b.submenu.callback,event:a,parentMenu:c,ignore_item_callbacks:b.submenu.ignore_item_callbacks,title:b.submenu.title,extra:b.submenu.extra,autoopen:d.autoopen});g=!1}g&&!c.lock&&c.close()}var c=this;d=d||{};var e=document.createElement("div");e.className="litemenu-entry submenu";var l=!1;if(null===b)e.classList.add("separator");else{e.innerHTML=b&& b.title?b.title:a;if(e.value=b)b.disabled&&(l=!0,e.classList.add("disabled")),(b.submenu||b.has_submenu)&&e.classList.add("has_submenu");"function"==typeof b?(e.dataset.value=a,e.onclick_callback=b):e.dataset.value=b;b.className&&(e.className+=" "+b.className)}this.root.appendChild(e);l||e.addEventListener("click",g);d.autoopen&&e.addEventListener("mouseenter",f);return e};G.prototype.close=function(a,b){this.root.parentNode&&this.root.parentNode.removeChild(this.root);this.parentMenu&&!b&&(this.parentMenu.lock= @@ -262,87 +263,87 @@ function(){return this.options.parentMenu?this.options.parentMenu.getTopMenu():t E.sampleCurve=function(a,b){if(b){for(var d=0;dMath.abs(d))return f[1];d=(a-f[0])/d;return f[1]*(1-d)+g[1]*d}}return 0}};E.prototype.draw=function(a,b,d,f,g,c){if(d=this.points){this.size=b;var e=b[0]-2*this.margin;b=b[1]-2*this.margin;g=g||"#666";a.save();a.translate(this.margin,this.margin);f&&(a.fillStyle="#111",a.fillRect(0,0,e,b),a.fillStyle="#222",a.fillRect(0.5*e,0,1,b),a.strokeStyle="#333",a.strokeRect(0,0,e,b));a.strokeStyle= g;c&&(a.globalAlpha=0.5);a.beginPath();for(f=0;fa[1])){var f=this.size[0]-2*this.margin,g=this.size[1]-2*this.margin,c=a[0]-this.margin,e=a[1]-this.margin;this.selected=this.getCloserPoint([c, e],30/b.ds.scale);-1==this.selected&&(f=[c/f,1-e/g],d.push(f),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(f),this.must_update=!0);if(-1!=this.selected)return!0}};E.prototype.onMouseMove=function(a,b){var d=this.points;if(d){var f=this.selected;if(!(0>f)){var g=(a[0]-this.margin)/(this.size[0]-2*this.margin),c=(a[1]-this.margin)/(this.size[1]-2*this.margin);this._nearest=this.getCloserPoint([a[0]-this.margin,a[1]-this.margin],30/b.ds.scale);var e=d[f];if(e){var l=0==f||f==d.length- -1;!l&&(-10>a[0]||a[0]>this.size[0]+10||-10>a[1]||a[1]>this.size[1]+10)?(d.splice(f,1),this.selected=-1):(e[0]=l?0==f?0:1:Math.clamp(g,0,1),e[1]=1-Math.clamp(c,0,1),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(e),this.must_update=!0)}}}};E.prototype.onMouseUp=function(a,b){this.selected=-1;return!1};E.prototype.getCloserPoint=function(a,b){var d=this.points;if(!d)return-1;b=b||30;for(var f=this.size[0]-2*this.margin,g=this.size[1]-2*this.margin,c=d.length,e=[0,0],l=1E6,q=-1,p=0;p< -c;++p){var h=d[p];e[0]=h[0]*f;e[1]=(1-h[1])*g;h=vec2.distance(a,e);h>l||h>b||(q=p,l=h)}return q};e.CurveEditor=E;e.getParameterNames=function(a){return(a+"").replace(/[/][/].*$/gm,"").replace(/\s+/g,"").replace(/[/][*][^/*]*[*][/]/g,"").split("){",1)[0].replace(/^[^(]*[(]/,"").replace(/=[^,]+/g,"").split(",").filter(Boolean)};Math.clamp=function(a,b,d){return b>a?b:da[0]||a[0]>this.size[0]+10||-10>a[1]||a[1]>this.size[1]+10)?(d.splice(f,1),this.selected=-1):(e[0]=l?0==f?0:1:Math.clamp(g,0,1),e[1]=1-Math.clamp(c,0,1),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(e),this.must_update=!0)}}}};E.prototype.onMouseUp=function(a,b){this.selected=-1;return!1};E.prototype.getCloserPoint=function(a,b){var d=this.points;if(!d)return-1;b=b||30;for(var f=this.size[0]-2*this.margin,g=this.size[1]-2*this.margin,c=d.length,e=[0,0],l=1E6,r=-1,p=0;p< +c;++p){var h=d[p];e[0]=h[0]*f;e[1]=(1-h[1])*g;h=vec2.distance(a,e);h>l||h>b||(r=p,l=h)}return r};e.CurveEditor=E;e.getParameterNames=function(a){return(a+"").replace(/[/][/].*$/gm,"").replace(/\s+/g,"").replace(/[/][*][^/*]*[*][/]/g,"").split("){",1)[0].replace(/^[^(]*[(]/,"").replace(/=[^,]+/g,"").split(",").filter(Boolean)};Math.clamp=function(a,b,d){return b>a?b:d -this.size[1]-f.NODE_TITLE_HEIGHT+0.5&&d.showSubgraphPropertiesDialog(this)};m.prototype.computeSize=function(){return[200,Math.max(this.inputs?this.inputs.length:0,this.outputs?this.outputs.length:0)*f.NODE_SLOT_HEIGHT+f.NODE_TITLE_HEIGHT]};m.prototype.onSubgraphTrigger=function(a,b){var d=this.findOutputSlot(a);-1!=d&&this.triggerSlot(d)};m.prototype.onSubgraphNewInput=function(a,b){-1==this.findInputSlot(a)&&this.addInput(a,b)};m.prototype.onSubgraphRenamedInput=function(a,b){var d=this.findInputSlot(a); --1!=d&&(this.getInputInfo(d).name=b)};m.prototype.onSubgraphTypeChangeInput=function(a,b){var d=this.findInputSlot(a);-1!=d&&(this.getInputInfo(d).type=b)};m.prototype.onSubgraphRemovedInput=function(a){a=this.findInputSlot(a);-1!=a&&this.removeInput(a)};m.prototype.onSubgraphNewOutput=function(a,b){-1==this.findOutputSlot(a)&&this.addOutput(a,b)};m.prototype.onSubgraphRenamedOutput=function(a,b){var d=this.findOutputSlot(a);-1!=d&&(this.getOutputInfo(d).name=b)};m.prototype.onSubgraphTypeChangeOutput= -function(a,b){var d=this.findOutputSlot(a);-1!=d&&(this.getOutputInfo(d).type=b)};m.prototype.onSubgraphRemovedOutput=function(a){a=this.findInputSlot(a);-1!=a&&this.removeOutput(a)};m.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:"Open",callback:function(){a.openSubgraph(b.subgraph)}}]};m.prototype.onResize=function(a){a[1]+=20};m.prototype.serialize=function(){var a=f.LGraphNode.prototype.serialize.call(this);a.subgraph=this.subgraph.serialize();return a};m.prototype.clone= -function(){var a=f.createNode(this.type),b=this.serialize();delete b.id;delete b.inputs;delete b.outputs;a.configure(b);return a};m.prototype.buildFromNodes=function(a){for(var b={},d=0,f=0;f=e?this.trigger(null,h):this._pending.push([e,h])};h.prototype.onExecute=function(){var c=1E3* -this.graph.elapsed_time;this.isInputConnected(1)&&(this.properties.time_in_ms=this.getInputData(1));for(var h=0;hthis.size[1]-f.NODE_TITLE_HEIGHT+0.5&&d.showSubgraphPropertiesDialog(this)};m.prototype.computeSize=function(){return[200,Math.max(this.inputs?this.inputs.length:0,this.outputs?this.outputs.length:0)*f.NODE_SLOT_HEIGHT+f.NODE_TITLE_HEIGHT]};m.prototype.onSubgraphTrigger=function(a,b){var d=this.findOutputSlot(a);-1!=d&&this.triggerSlot(d)};m.prototype.onSubgraphNewInput= +function(a,b){-1==this.findInputSlot(a)&&this.addInput(a,b)};m.prototype.onSubgraphRenamedInput=function(a,b){var d=this.findInputSlot(a);-1!=d&&(this.getInputInfo(d).name=b)};m.prototype.onSubgraphTypeChangeInput=function(a,b){var d=this.findInputSlot(a);-1!=d&&(this.getInputInfo(d).type=b)};m.prototype.onSubgraphRemovedInput=function(a){a=this.findInputSlot(a);-1!=a&&this.removeInput(a)};m.prototype.onSubgraphNewOutput=function(a,b){-1==this.findOutputSlot(a)&&this.addOutput(a,b)};m.prototype.onSubgraphRenamedOutput= +function(a,b){var d=this.findOutputSlot(a);-1!=d&&(this.getOutputInfo(d).name=b)};m.prototype.onSubgraphTypeChangeOutput=function(a,b){var d=this.findOutputSlot(a);-1!=d&&(this.getOutputInfo(d).type=b)};m.prototype.onSubgraphRemovedOutput=function(a){a=this.findInputSlot(a);-1!=a&&this.removeOutput(a)};m.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:"Open",callback:function(){a.openSubgraph(b.subgraph)}}]};m.prototype.onResize=function(a){a[1]+=20};m.prototype.serialize=function(){var a= +f.LGraphNode.prototype.serialize.call(this);a.subgraph=this.subgraph.serialize();return a};m.prototype.clone=function(){var a=f.createNode(this.type),b=this.serialize();delete b.id;delete b.inputs;delete b.outputs;a.configure(b);return a};m.prototype.buildFromNodes=function(a){for(var b={},d=0,f=0;f=e?this.trigger(null,h):this._pending.push([e,h])};h.prototype.onExecute=function(){var c=1E3* +this.graph.elapsed_time;this.isInputConnected(1)&&(this.properties.time_in_ms=this.getInputData(1));for(var h=0;hh[1]))return this.old_y=c.canvasY,this.captureInput(!0),this.mouse_captured=!0};n.prototype.onMouseMove=function(c){if(this.mouse_captured){var h=this.old_y-c.canvasY;c.shiftKey&&(h*=10);if(c.metaKey||c.altKey)h*=0.1;this.old_y=c.canvasY;c=this._remainder+h/n.pixels_threshold;this._remainder=c%1;c=Math.clamp(this.properties.value+ (c|0)*this.properties.step,this.properties.min,this.properties.max);this.properties.value=c;this.graph._version++;this.setDirtyCanvas(!0)}};n.prototype.onMouseUp=function(c,h){200>c.click_time&&(this.properties.value=Math.clamp(this.properties.value+(h[1]>0.5*this.size[1]?-1:1)*this.properties.step,this.properties.min,this.properties.max),this.graph._version++,this.setDirtyCanvas(!0));this.mouse_captured&&(this.mouse_captured=!1,this.captureInput(!1))};E.registerNodeType("widget/number",n);k.title= -"Combo";k.desc="Widget to select from a list";k.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};k.prototype.onPropertyChanged=function(c,h){"values"==c?(this._values=h.split(";"),this.widget.options.values=this._values):"value"==c&&(this.widget.value=h)};E.registerNodeType("widget/combo",k);v.title="Knob";v.desc="Circular controller";v.size=[80,100];v.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/ +"Combo";k.desc="Widget to select from a list";k.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};k.prototype.onPropertyChanged=function(c,h){"values"==c?(this._values=h.split(";"),this.widget.options.values=this._values):"value"==c&&(this.widget.value=h)};E.registerNodeType("widget/combo",k);y.title="Knob";y.desc="Circular controller";y.size=[80,100];y.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/ (this.properties.max-this.properties.min));var h=0.5*this.size[0],k=0.5*this.size[1],n=0.5*Math.min(this.size[0],this.size[1])-5;c.globalAlpha=1;c.save();c.translate(h,k);c.rotate(0.75*Math.PI);c.fillStyle="rgba(0,0,0,0.5)";c.beginPath();c.moveTo(0,0);c.arc(0,0,n,0,1.5*Math.PI);c.fill();c.strokeStyle="black";c.fillStyle=this.properties.color;c.lineWidth=2;c.beginPath();c.moveTo(0,0);c.arc(0,0,n-4,0,1.5*Math.PI*Math.max(0.01,this.value));c.closePath();c.fill();c.lineWidth=1;c.globalAlpha=1;c.restore(); -c.fillStyle="black";c.beginPath();c.arc(h,k,0.75*n,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":this.properties.color;c.beginPath();var C=this.value*Math.PI*1.5+0.75*Math.PI;c.arc(h+Math.cos(C)*n*0.65,k+Math.sin(C)*n*0.65,0.05*n,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":"#AAA";c.font=Math.floor(0.5*n)+"px Arial";c.textAlign="center";c.fillText(this.properties.value.toFixed(this.properties.precision),h,k+0.15*n)}};v.prototype.onExecute=function(){this.setOutputData(0, -this.properties.value);this.boxcolor=E.colorToString([this.value,this.value,this.value])};v.prototype.onMouseDown=function(c){this.center=[0.5*this.size[0],0.5*this.size[1]+20];this.radius=0.5*this.size[0];if(20>c.canvasY-this.pos[1]||E.distance([c.canvasX,c.canvasY],[this.pos[0]+this.center[0],this.pos[1]+this.center[1]])>this.radius)return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];this.captureInput(!0);return!0};v.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX- -this.pos[0],c.canvasY-this.pos[1]];var h=this.value,h=h-0.01*(c[1]-this.oldmouse[1]);1h&&(h=0);this.value=h;this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.oldmouse=c;this.setDirtyCanvas(!0)}};v.prototype.onMouseUp=function(c){this.oldmouse&&(this.oldmouse=null,this.captureInput(!1))};v.prototype.onPropertyChanged=function(c,h){if("min"==c||"max"==c||"value"==c)return this.properties[c]=parseFloat(h),!0};E.registerNodeType("widget/knob", -v);h.title="Inner Slider";h.prototype.onPropertyChanged=function(c,h){"value"==c&&(this.slider.value=h)};h.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};E.registerNodeType("widget/internal_slider",h);B.title="H.Slider";B.desc="Linear slider controller";B.prototype.onDrawForeground=function(c){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min));c.globalAlpha=1;c.lineWidth=1;c.fillStyle="#000";c.fillRect(2, -2,this.size[0]-4,this.size[1]-4);c.fillStyle=this.properties.color;c.beginPath();c.rect(4,4,(this.size[0]-8)*this.value,this.size[1]-8);c.fill()};B.prototype.onExecute=function(){this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.setOutputData(0,this.properties.value);this.boxcolor=E.colorToString([this.value,this.value,this.value])};B.prototype.onMouseDown=function(c){if(0>c.canvasY-this.pos[1])return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY- -this.pos[1]];this.captureInput(!0);return!0};B.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];var h=this.value,h=h+(c[0]-this.oldmouse[0])/this.size[0];1h&&(h=0);this.value=h;this.oldmouse=c;this.setDirtyCanvas(!0)}};B.prototype.onMouseUp=function(c){this.oldmouse=null;this.captureInput(!1)};B.prototype.onMouseLeave=function(c){};E.registerNodeType("widget/hslider",B);y.title="Progress";y.desc="Shows data in linear progress";y.prototype.onExecute= -function(){var c=this.getInputData(0);void 0!=c&&(this.properties.value=c)};y.prototype.onDrawForeground=function(c){c.lineWidth=1;c.fillStyle=this.properties.color;var h=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min),h=Math.min(1,h),h=Math.max(0,h);c.fillRect(2,2,(this.size[0]-4)*h,this.size[1]-4)};E.registerNodeType("widget/progress",y);C.title="Text";C.desc="Shows the input value";C.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"led_text", -text:"LED",type:"minibutton"},{name:"normal_text",text:"Normal",type:"minibutton"}];C.prototype.onDrawForeground=function(c){c.fillStyle=this.properties.color;var h=this.properties.value;this.properties.glowSize?(c.shadowColor=this.properties.color,c.shadowOffsetX=0,c.shadowOffsetY=0,c.shadowBlur=this.properties.glowSize):c.shadowColor="transparent";var k=this.properties.fontsize;c.textAlign=this.properties.align;c.font=k.toString()+"px "+this.properties.font;this.str="number"==typeof h?h.toFixed(this.properties.decimals): -h;if("string"==typeof this.str){var h=this.str.split("\\n"),n;for(n in h)c.fillText(h[n],"left"==this.properties.align?15:this.size[0]-15,-0.15*k+k*(parseInt(n)+1))}c.shadowColor="transparent";this.last_ctx=c;c.textAlign="left"};C.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(this.properties.value=c)};C.prototype.resize=function(){if(this.last_ctx){var c=this.str.split("\\n");this.last_ctx.font=this.properties.fontsize+"px "+this.properties.font;var h=0,k;for(k in c){var n=this.last_ctx.measureText(c[k]).width; -hc.canvasY-this.pos[1]||E.distance([c.canvasX,c.canvasY],[this.pos[0]+this.center[0],this.pos[1]+this.center[1]])>this.radius)return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];this.captureInput(!0);return!0};y.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX- +this.pos[0],c.canvasY-this.pos[1]];var h=this.value,h=h-0.01*(c[1]-this.oldmouse[1]);1h&&(h=0);this.value=h;this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.oldmouse=c;this.setDirtyCanvas(!0)}};y.prototype.onMouseUp=function(c){this.oldmouse&&(this.oldmouse=null,this.captureInput(!1))};y.prototype.onPropertyChanged=function(c,h){if("min"==c||"max"==c||"value"==c)return this.properties[c]=parseFloat(h),!0};E.registerNodeType("widget/knob", +y);h.title="Inner Slider";h.prototype.onPropertyChanged=function(c,h){"value"==c&&(this.slider.value=h)};h.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};E.registerNodeType("widget/internal_slider",h);D.title="H.Slider";D.desc="Linear slider controller";D.prototype.onDrawForeground=function(c){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min));c.globalAlpha=1;c.lineWidth=1;c.fillStyle="#000";c.fillRect(2, +2,this.size[0]-4,this.size[1]-4);c.fillStyle=this.properties.color;c.beginPath();c.rect(4,4,(this.size[0]-8)*this.value,this.size[1]-8);c.fill()};D.prototype.onExecute=function(){this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.setOutputData(0,this.properties.value);this.boxcolor=E.colorToString([this.value,this.value,this.value])};D.prototype.onMouseDown=function(c){if(0>c.canvasY-this.pos[1])return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY- +this.pos[1]];this.captureInput(!0);return!0};D.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];var h=this.value,h=h+(c[0]-this.oldmouse[0])/this.size[0];1h&&(h=0);this.value=h;this.oldmouse=c;this.setDirtyCanvas(!0)}};D.prototype.onMouseUp=function(c){this.oldmouse=null;this.captureInput(!1)};D.prototype.onMouseLeave=function(c){};E.registerNodeType("widget/hslider",D);x.title="Progress";x.desc="Shows data in linear progress";x.prototype.onExecute= +function(){var c=this.getInputData(0);void 0!=c&&(this.properties.value=c)};x.prototype.onDrawForeground=function(c){c.lineWidth=1;c.fillStyle=this.properties.color;var h=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min),h=Math.min(1,h),h=Math.max(0,h);c.fillRect(2,2,(this.size[0]-4)*h,this.size[1]-4)};E.registerNodeType("widget/progress",x);B.title="Text";B.desc="Shows the input value";B.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"led_text", +text:"LED",type:"minibutton"},{name:"normal_text",text:"Normal",type:"minibutton"}];B.prototype.onDrawForeground=function(c){c.fillStyle=this.properties.color;var h=this.properties.value;this.properties.glowSize?(c.shadowColor=this.properties.color,c.shadowOffsetX=0,c.shadowOffsetY=0,c.shadowBlur=this.properties.glowSize):c.shadowColor="transparent";var k=this.properties.fontsize;c.textAlign=this.properties.align;c.font=k.toString()+"px "+this.properties.font;this.str="number"==typeof h?h.toFixed(this.properties.decimals): +h;if("string"==typeof this.str){var h=this.str.split("\\n"),n;for(n in h)c.fillText(h[n],"left"==this.properties.align?15:this.size[0]-15,-0.15*k+k*(parseInt(n)+1))}c.shadowColor="transparent";this.last_ctx=c;c.textAlign="left"};B.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(this.properties.value=c)};B.prototype.resize=function(){if(this.last_ctx){var c=this.str.split("\\n");this.last_ctx.font=this.properties.fontsize+"px "+this.properties.font;var h=0,k;for(k in c){var n=this.last_ctx.measureText(c[k]).width; +hk?n.xbox.axes.lx:0,this._left_axis[1]=Math.abs(n.xbox.axes.ly)>k?n.xbox.axes.ly:0,this._right_axis[0]=Math.abs(n.xbox.axes.rx)>k?n.xbox.axes.rx:0,this._right_axis[1]=Math.abs(n.xbox.axes.ry)>k?n.xbox.axes.ry:0,this._triggers[0]=Math.abs(n.xbox.axes.ltrigger)>k?n.xbox.axes.ltrigger: 0,this._triggers[1]=Math.abs(n.xbox.axes.rtrigger)>k?n.xbox.axes.rtrigger:0);if(this.outputs)for(k=0;k","enum",{values:a.values});this.size=[80,60]}function b(){this.addInput("inc", "number");this.addOutput("total","number");this.addProperty("increment",1);this.addProperty("value",0)}function d(){this.addInput("v","number");this.addOutput("sin","number");this.addProperty("amplitude",1);this.addProperty("offset",0);this.bgImageUrl="nodes/imgs/icon-sin.png"}function f(){this.addInput("x","number");this.addInput("y","number");this.addOutput("","number");this.properties={x:1,y:1,formula:"x+y"};this.code_widget=this.addWidget("text","F(x,y)",this.properties.formula,function(a,b,d){d.properties.formula= -a});this.addWidget("toggle","allow",w.allow_scripts,function(a){w.allow_scripts=a});this._func=null}function g(){this.addInput("vec2","vec2");this.addOutput("x","number");this.addOutput("y","number")}function r(){this.addInputs([["x","number"],["y","number"]]);this.addOutput("vec2","vec2");this.properties={x:0,y:0};this._data=new Float32Array(2)}function x(){this.addInput("vec3","vec3");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number")}function K(){this.addInputs([["x", -"number"],["y","number"],["z","number"]]);this.addOutput("vec3","vec3");this.properties={x:0,y:0,z:0};this._data=new Float32Array(3)}function q(){this.addInput("vec4","vec4");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number");this.addOutput("w","number")}function z(){this.addInputs([["x","number"],["y","number"],["z","number"],["w","number"]]);this.addOutput("vec4","vec4");this.properties={x:0,y:0,z:0,w:0};this._data=new Float32Array(4)}var w=D.LiteGraph;c.title= +a});this.addWidget("toggle","allow",z.allow_scripts,function(a){z.allow_scripts=a});this._func=null}function g(){this.addInput("vec2","vec2");this.addOutput("x","number");this.addOutput("y","number")}function q(){this.addInputs([["x","number"],["y","number"]]);this.addOutput("vec2","vec2");this.properties={x:0,y:0};this._data=new Float32Array(2)}function A(){this.addInput("vec3","vec3");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number")}function K(){this.addInputs([["x", +"number"],["y","number"],["z","number"]]);this.addOutput("vec3","vec3");this.properties={x:0,y:0,z:0};this._data=new Float32Array(3)}function r(){this.addInput("vec4","vec4");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number");this.addOutput("w","number")}function s(){this.addInputs([["x","number"],["y","number"],["z","number"],["w","number"]]);this.addOutput("vec4","vec4");this.properties={x:0,y:0,z:0,w:0};this._data=new Float32Array(4)}var z=C.LiteGraph;c.title= "Converter";c.desc="type A to type B";c.prototype.onExecute=function(){var a=this.getInputData(0);if(null!=a&&this.outputs)for(var b=0;ba&&(a+=1024);var c=Math.floor(a);a-=c;d=h.data[c];c=h.data[1023==c?0:c+1];b&&(a=a*a*a*(a*(6*a-15)+10));return d*(1-a)+c*a};h.prototype.onExecute=function(){for(var a=this.getInputData(0)||0,b=this.properties.octaves||1,d=0,c=1,a=a+(this.properties.seed||0),f=this.properties.speed||1,g=0,e=0;ec);++e);a=this.properties.min; -this._last_v=d/g*(this.properties.max-a)+a;this.setOutputData(0,this._last_v)};h.prototype.onDrawBackground=function(a){this.outputs[0].label=(this._last_v||0).toFixed(3)};w.registerNodeType("math/noise",h);B.title="Spikes";B.desc="spike every random time";B.prototype.onExecute=function(){var a=this.graph.elapsed_time;this._remaining_time-=a;this._blink_time-=a;a=0;0this._remaining_time?(this._remaining_time= -Math.random()*(this.properties.max_time-this.properties.min_time)+this.properties.min_time,this._blink_time=this.properties.duration,this.boxcolor="#FFF"):this.boxcolor="#000";this.setOutputData(0,a)};w.registerNodeType("math/spikes",B);y.title="Clamp";y.desc="Clamp number between min and max";y.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(a=Math.max(this.properties.min,a),a=Math.min(this.properties.max,a),this.setOutputData(0,a))};y.prototype.getCode=function(a){a="";this.isInputConnected(0)&& -(a+="clamp({{0}},"+this.properties.min+","+this.properties.max+")");return a};w.registerNodeType("math/clamp",y);C.title="Lerp";C.desc="Linear Interpolation";C.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.getInputData(1);null==b&&(b=0);var d=this.properties.f,c=this.getInputData(2);void 0!==c&&(d=c);this.setOutputData(0,a*(1-d)+b*d)};C.prototype.onGetInputs=function(){return[["f","number"]]};w.registerNodeType("math/lerp",C);G.title="Abs";G.desc="Absolute";G.prototype.onExecute= -function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.abs(a))};w.registerNodeType("math/abs",G);E.title="Floor";E.desc="Floor number to remove fractional part";E.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.floor(a))};w.registerNodeType("math/floor",E);e.title="Frac";e.desc="Returns fractional part";e.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a%1)};w.registerNodeType("math/frac",e);A.title= -"Smoothstep";A.desc="Smoothstep";A.prototype.onExecute=function(){var a=this.getInputData(0);if(void 0!==a){var b=this.properties.A,a=Math.clamp((a-b)/(this.properties.B-b),0,1);this.setOutputData(0,a*a*(3-2*a))}};w.registerNodeType("math/smoothstep",A);F.title="Scale";F.desc="v * factor";F.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a*this.properties.factor)};w.registerNodeType("math/scale",F);t.title="Gate";t.desc="if v is true, then outputs A, otherwise B"; -t.prototype.onExecute=function(){var a=this.getInputData(0);this.setOutputData(0,this.getInputData(a?1:2))};w.registerNodeType("math/gate",t);H.title="Average";H.desc="Average Filter";H.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this._values.length;this._values[this._current%b]=a;this._current+=1;this._current>b&&(this._current=0);for(var d=a=0;db&&(b=1);this.properties.samples= -Math.round(b);var d=this._values;this._values=new Float32Array(this.properties.samples);d.length<=this._values.length?this._values.set(d):this._values.set(d.subarray(0,this._values.length))};w.registerNodeType("math/average",H);p.title="TendTo";p.desc="moves the output value always closer to the input";p.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.factor;this._value=null==this._value?a:this._value*(1-b)+a*b;this.setOutputData(0,this._value)};w.registerNodeType("math/tendTo", -p);s.values="+ - * / % ^ max min".split(" ");s.title="Operation";s.desc="Easy math operators";s["@OP"]={type:"enum",title:"operation",values:s.values};s.size=[100,60];s.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"};s.prototype.setValue=function(a){"string"==typeof a&&(a=parseFloat(a));this.properties.value=a};s.prototype.onPropertyChanged=function(a,b){if("OP"==a)switch(this.properties.OP){case "+":this._func= +this._last_v=d/g*(this.properties.max-a)+a;this.setOutputData(0,this._last_v)};h.prototype.onDrawBackground=function(a){this.outputs[0].label=(this._last_v||0).toFixed(3)};z.registerNodeType("math/noise",h);D.title="Spikes";D.desc="spike every random time";D.prototype.onExecute=function(){var a=this.graph.elapsed_time;this._remaining_time-=a;this._blink_time-=a;a=0;0this._remaining_time?(this._remaining_time= +Math.random()*(this.properties.max_time-this.properties.min_time)+this.properties.min_time,this._blink_time=this.properties.duration,this.boxcolor="#FFF"):this.boxcolor="#000";this.setOutputData(0,a)};z.registerNodeType("math/spikes",D);x.title="Clamp";x.desc="Clamp number between min and max";x.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(a=Math.max(this.properties.min,a),a=Math.min(this.properties.max,a),this.setOutputData(0,a))};x.prototype.getCode=function(a){a="";this.isInputConnected(0)&& +(a+="clamp({{0}},"+this.properties.min+","+this.properties.max+")");return a};z.registerNodeType("math/clamp",x);B.title="Lerp";B.desc="Linear Interpolation";B.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.getInputData(1);null==b&&(b=0);var d=this.properties.f,c=this.getInputData(2);void 0!==c&&(d=c);this.setOutputData(0,a*(1-d)+b*d)};B.prototype.onGetInputs=function(){return[["f","number"]]};z.registerNodeType("math/lerp",B);G.title="Abs";G.desc="Absolute";G.prototype.onExecute= +function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.abs(a))};z.registerNodeType("math/abs",G);E.title="Floor";E.desc="Floor number to remove fractional part";E.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.floor(a))};z.registerNodeType("math/floor",E);e.title="Frac";e.desc="Returns fractional part";e.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a%1)};z.registerNodeType("math/frac",e);p.title= +"Smoothstep";p.desc="Smoothstep";p.prototype.onExecute=function(){var a=this.getInputData(0);if(void 0!==a){var b=this.properties.A,a=Math.clamp((a-b)/(this.properties.B-b),0,1);this.setOutputData(0,a*a*(3-2*a))}};z.registerNodeType("math/smoothstep",p);F.title="Scale";F.desc="v * factor";F.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a*this.properties.factor)};z.registerNodeType("math/scale",F);v.title="Gate";v.desc="if v is true, then outputs A, otherwise B"; +v.prototype.onExecute=function(){var a=this.getInputData(0);this.setOutputData(0,this.getInputData(a?1:2))};z.registerNodeType("math/gate",v);H.title="Average";H.desc="Average Filter";H.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this._values.length;this._values[this._current%b]=a;this._current+=1;this._current>b&&(this._current=0);for(var d=a=0;db&&(b=1);this.properties.samples= +Math.round(b);var d=this._values;this._values=new Float32Array(this.properties.samples);d.length<=this._values.length?this._values.set(d):this._values.set(d.subarray(0,this._values.length))};z.registerNodeType("math/average",H);w.title="TendTo";w.desc="moves the output value always closer to the input";w.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.factor;this._value=null==this._value?a:this._value*(1-b)+a*b;this.setOutputData(0,this._value)};z.registerNodeType("math/tendTo", +w);t.values="+ - * / % ^ max min".split(" ");t.title="Operation";t.desc="Easy math operators";t["@OP"]={type:"enum",title:"operation",values:t.values};t.size=[100,60];t.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"};t.prototype.setValue=function(a){"string"==typeof a&&(a=parseFloat(a));this.properties.value=a};t.prototype.onPropertyChanged=function(a,b){if("OP"==a)switch(this.properties.OP){case "+":this._func= function(a,b){return a+b};break;case "-":this._func=function(a,b){return a-b};break;case "x":case "X":case "*":this._func=function(a,b){return a*b};break;case "/":this._func=function(a,b){return a/b};break;case "%":this._func=function(a,b){return a%b};break;case "^":this._func=function(a,b){return Math.pow(a,b)};break;case "max":this._func=function(a,b){return Math.max(a,b)};break;case "min":this._func=function(a,b){return Math.min(a,b)};break;default:console.warn("Unknown operation: "+this.properties.OP), -this._func=function(a){return a}}};s.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);null!=a?a.constructor===Number&&(this.properties.A=a):a=this.properties.A;null!=b?this.properties.B=b:b=this.properties.B;var d;if(a.constructor===Number)d=this._func(a,b);else if(a.constructor===Array){d=this._result;d.length=a.length;for(var c=0;cB":g=a>b;break;case "A=B":g=a>=b}this.setOutputData(d,g)}}};l.prototype.onGetOutputs=function(){return[["A==B","boolean"],["A!=B","boolean"], -["A>B","boolean"],["A=B","boolean"],["A<=B","boolean"]]};w.registerNodeType("math/compare",l);w.registerSearchboxExtra("math/compare","==",{outputs:[["A==B","boolean"]],title:"A==B"});w.registerSearchboxExtra("math/compare","!=",{outputs:[["A!=B","boolean"]],title:"A!=B"});w.registerSearchboxExtra("math/compare",">",{outputs:[["A>B","boolean"]],title:"A>B"});w.registerSearchboxExtra("math/compare","<",{outputs:[["A=",{outputs:[["A>=B","boolean"]],title:"A>=B"});w.registerSearchboxExtra("math/compare","<=",{outputs:[["A<=B","boolean"]],title:"A<=B"});a.values="> < == != <= >= || &&".split(" ");a["@OP"]={type:"enum",title:"operation",values:a.values};a.title="Condition";a.desc="evaluates condition between A and B";a.prototype.getTitle=function(){return"A "+this.properties.OP+" B"};a.prototype.onExecute=function(){var a=this.getInputData(0);void 0===a?a=this.properties.A:this.properties.A=a;var b=this.getInputData(1); -void 0===b?b=this.properties.B:this.properties.B=b;var d=!0;switch(this.properties.OP){case ">":d=a>b;break;case "<":d=a=":d=a>=b;break;case "||":d=a||b;break;case "&&":d=a&&b}this.setOutputData(0,d);this.setOutputData(1,!d)};w.registerNodeType("math/condition",a);b.title="Accumulate";b.desc="Increments a value every time";b.prototype.onExecute=function(){null===this.properties.value&&(this.properties.value=0);var a= -this.getInputData(0);this.properties.value=null!==a?this.properties.value+a:this.properties.value+this.properties.increment;this.setOutputData(0,this.properties.value)};w.registerNodeType("math/accumulate",b);d.title="Trigonometry";d.desc="Sin Cos Tan";d.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.amplitude,d=this.findInputSlot("amplitude");-1!=d&&(b=this.getInputData(d));var c=this.properties.offset,d=this.findInputSlot("offset");-1!=d&&(c=this.getInputData(d)); +["A>B","boolean"],["A=B","boolean"],["A<=B","boolean"]]};z.registerNodeType("math/compare",l);z.registerSearchboxExtra("math/compare","==",{outputs:[["A==B","boolean"]],title:"A==B"});z.registerSearchboxExtra("math/compare","!=",{outputs:[["A!=B","boolean"]],title:"A!=B"});z.registerSearchboxExtra("math/compare",">",{outputs:[["A>B","boolean"]],title:"A>B"});z.registerSearchboxExtra("math/compare","<",{outputs:[["A=",{outputs:[["A>=B","boolean"]],title:"A>=B"});z.registerSearchboxExtra("math/compare","<=",{outputs:[["A<=B","boolean"]],title:"A<=B"});a.values="> < == != <= >= || &&".split(" ");a["@OP"]={type:"enum",title:"operation",values:a.values};a.title="Condition";a.desc="evaluates condition between A and B";a.prototype.getTitle=function(){return"A "+this.properties.OP+" B"};a.prototype.onExecute=function(){var a=this.getInputData(0);void 0===a?a=this.properties.A:this.properties.A=a;var b=this.getInputData(1); +void 0===b?b=this.properties.B:this.properties.B=b;var d=!0;switch(this.properties.OP){case ">":d=a>b;break;case "<":d=a=":d=a>=b;break;case "||":d=a||b;break;case "&&":d=a&&b}this.setOutputData(0,d);this.setOutputData(1,!d)};z.registerNodeType("math/condition",a);b.title="Accumulate";b.desc="Increments a value every time";b.prototype.onExecute=function(){null===this.properties.value&&(this.properties.value=0);var a= +this.getInputData(0);this.properties.value=null!==a?this.properties.value+a:this.properties.value+this.properties.increment;this.setOutputData(0,this.properties.value)};z.registerNodeType("math/accumulate",b);d.title="Trigonometry";d.desc="Sin Cos Tan";d.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.amplitude,d=this.findInputSlot("amplitude");-1!=d&&(b=this.getInputData(d));var c=this.properties.offset,d=this.findInputSlot("offset");-1!=d&&(c=this.getInputData(d)); for(var d=0,f=this.outputs.length;dXY";g.desc="vector 2 to components";g.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]))};w.registerNodeType("math3d/vec2-to-xy",g);r.title="XY->Vec2";r.desc="components to vector2";r.prototype.onExecute= -function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this._data;d[0]=a;d[1]=b;this.setOutputData(0,d)};w.registerNodeType("math3d/xy-to-vec2",r);x.title="Vec3->XYZ";x.desc="vector 3 to components";x.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]))};w.registerNodeType("math3d/vec3-to-xyz",x);K.title="XYZ->Vec3";K.desc="components to vector3"; -K.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this._data;c[0]=a;c[1]=b;c[2]=d;this.setOutputData(0,c)};w.registerNodeType("math3d/xyz-to-vec3",K);q.title="Vec4->XYZW";q.desc="vector 4 to components";q.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2, -a[2]),this.setOutputData(3,a[3]))};w.registerNodeType("math3d/vec4-to-xyzw",q);z.title="XYZW->Vec4";z.desc="components to vector4";z.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this.getInputData(3);null==c&&(c=this.properties.w);var f=this._data;f[0]=a;f[1]=b;f[2]=d;f[3]=c;this.setOutputData(0,f)};w.registerNodeType("math3d/xyzw-to-vec4", -z)})(this); -(function(D){function c(){this.addInput("T","vec3");this.addInput("R","vec3");this.addInput("S","vec3");this.addOutput("mat4","mat4");this.properties={T:[0,0,0],R:[0,0,0],S:[1,1,1],R_in_degrees:!0};this._result=mat4.create();this._must_update=!0}function m(){this.addInput("A","number,vec3");this.addInput("B","number,vec3");this.addOutput("=","number,vec3");this.addProperty("OP","+","enum",{values:m.values});this._result=vec3.create()}function n(){this.addInput("in","vec3");this.addInput("f","number"); -this.addOutput("out","vec3");this.properties={f:1};this._data=new Float32Array(3)}function k(){this.addInput("in","vec3");this.addOutput("out","number")}function v(){this.addInput("in","vec3");this.addOutput("out","vec3");this._data=new Float32Array(3)}function h(){this.addInput("A","vec3");this.addInput("B","vec3");this.addInput("f","vec3");this.addOutput("out","vec3");this.properties={f:0.5};this._data=new Float32Array(3)}function B(){this.addInput("A","vec3");this.addInput("B","vec3");this.addOutput("out", -"number")}var y=D.LiteGraph;c.title="mat4";c.temp_quat=new Float32Array([0,0,0,1]);c.temp_mat4=new Float32Array(16);c.temp_vec3=new Float32Array(3);c.prototype.onPropertyChanged=function(c,h){this._must_update=!0};c.prototype.onExecute=function(){var h=this._result,k=c.temp_quat,n=c.temp_mat4,e=c.temp_vec3,A=this.getInputData(0),m=this.getInputData(1),t=this.getInputData(2);if(this._must_update||A||m||t)A=A||this.properties.T,m=m||this.properties.R,t=t||this.properties.S,mat4.identity(h),mat4.translate(h, -h,A),this.properties.R_in_degrees?(e.set(m),vec3.scale(e,e,DEG2RAD),quat.fromEuler(k,e)):quat.fromEuler(k,m),mat4.fromQuat(n,k),mat4.multiply(h,h,n),mat4.scale(h,h,t);this.setOutputData(0,h)};y.registerNodeType("math3d/mat4",c);m.values="+ - * / % ^ max min dot cross".split(" ");y.registerSearchboxExtra("math3d/operation","CROSS()",{properties:{OP:"cross"},title:"CROSS()"});y.registerSearchboxExtra("math3d/operation","DOT()",{properties:{OP:"dot"},title:"DOT()"});m.title="Operation";m.desc="Easy math 3D operators"; +["acos","number"],["atan","number"]]};z.registerNodeType("math/trigonometry",d);z.registerSearchboxExtra("math/trigonometry","SIN()",{outputs:[["sin","number"]],title:"SIN()"});z.registerSearchboxExtra("math/trigonometry","COS()",{outputs:[["cos","number"]],title:"COS()"});z.registerSearchboxExtra("math/trigonometry","TAN()",{outputs:[["tan","number"]],title:"TAN()"});f.title="Formula";f.desc="Compute formula";f.size=[160,100];H.prototype.onPropertyChanged=function(a,b){"formula"==a&&(this.code_widget.value= +b)};f.prototype.onExecute=function(){if(z.allow_scripts){var a=this.getInputData(0),b=this.getInputData(1);null!=a?this.properties.x=a:a=this.properties.x;null!=b?this.properties.y=b:b=this.properties.y;var d;try{this._func&&this._func_code==this.properties.formula||(this._func=new Function("x","y","TIME","return "+this.properties.formula),this._func_code=this.properties.formula),d=this._func(a,b,this.graph.globaltime),this.boxcolor=null}catch(c){this.boxcolor="red"}this.setOutputData(0,d)}};f.prototype.getTitle= +function(){return this._func_code||"Formula"};f.prototype.onDrawBackground=function(){var a=this.properties.formula;this.outputs&&this.outputs.length&&(this.outputs[0].label=a)};z.registerNodeType("math/formula",f);g.title="Vec2->XY";g.desc="vector 2 to components";g.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]))};z.registerNodeType("math3d/vec2-to-xy",g);q.title="XY->Vec2";q.desc="components to vector2";q.prototype.onExecute= +function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this._data;d[0]=a;d[1]=b;this.setOutputData(0,d)};z.registerNodeType("math3d/xy-to-vec2",q);A.title="Vec3->XYZ";A.desc="vector 3 to components";A.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]))};z.registerNodeType("math3d/vec3-to-xyz",A);K.title="XYZ->Vec3";K.desc="components to vector3"; +K.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this._data;c[0]=a;c[1]=b;c[2]=d;this.setOutputData(0,c)};z.registerNodeType("math3d/xyz-to-vec3",K);r.title="Vec4->XYZW";r.desc="vector 4 to components";r.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2, +a[2]),this.setOutputData(3,a[3]))};z.registerNodeType("math3d/vec4-to-xyzw",r);s.title="XYZW->Vec4";s.desc="components to vector4";s.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this.getInputData(3);null==c&&(c=this.properties.w);var f=this._data;f[0]=a;f[1]=b;f[2]=d;f[3]=c;this.setOutputData(0,f)};z.registerNodeType("math3d/xyzw-to-vec4", +s)})(this); +(function(C){function c(){this.addInput("T","vec3");this.addInput("R","vec3");this.addInput("S","vec3");this.addOutput("mat4","mat4");this.properties={T:[0,0,0],R:[0,0,0],S:[1,1,1],R_in_degrees:!0};this._result=mat4.create();this._must_update=!0}function m(){this.addInput("A","number,vec3");this.addInput("B","number,vec3");this.addOutput("=","number,vec3");this.addProperty("OP","+","enum",{values:m.values});this._result=vec3.create()}function n(){this.addInput("in","vec3");this.addInput("f","number"); +this.addOutput("out","vec3");this.properties={f:1};this._data=new Float32Array(3)}function k(){this.addInput("in","vec3");this.addOutput("out","number")}function y(){this.addInput("in","vec3");this.addOutput("out","vec3");this._data=new Float32Array(3)}function h(){this.addInput("A","vec3");this.addInput("B","vec3");this.addInput("f","vec3");this.addOutput("out","vec3");this.properties={f:0.5};this._data=new Float32Array(3)}function D(){this.addInput("A","vec3");this.addInput("B","vec3");this.addOutput("out", +"number")}var x=C.LiteGraph;c.title="mat4";c.temp_quat=new Float32Array([0,0,0,1]);c.temp_mat4=new Float32Array(16);c.temp_vec3=new Float32Array(3);c.prototype.onPropertyChanged=function(c,h){this._must_update=!0};c.prototype.onExecute=function(){var h=this._result,k=c.temp_quat,n=c.temp_mat4,e=c.temp_vec3,p=this.getInputData(0),m=this.getInputData(1),v=this.getInputData(2);if(this._must_update||p||m||v)p=p||this.properties.T,m=m||this.properties.R,v=v||this.properties.S,mat4.identity(h),mat4.translate(h, +h,p),this.properties.R_in_degrees?(e.set(m),vec3.scale(e,e,DEG2RAD),quat.fromEuler(k,e)):quat.fromEuler(k,m),mat4.fromQuat(n,k),mat4.multiply(h,h,n),mat4.scale(h,h,v);this.setOutputData(0,h)};x.registerNodeType("math3d/mat4",c);m.values="+ - * / % ^ max min dot cross".split(" ");x.registerSearchboxExtra("math3d/operation","CROSS()",{properties:{OP:"cross"},title:"CROSS()"});x.registerSearchboxExtra("math3d/operation","DOT()",{properties:{OP:"dot"},title:"DOT()"});m.title="Operation";m.desc="Easy math 3D operators"; m["@OP"]={type:"enum",title:"operation",values:m.values};m.size=[100,60];m.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"};m.prototype.onExecute=function(){var c=this.getInputData(0),h=this.getInputData(1);if(null!=c&&null!=h){c.constructor===Number&&(c=[c,c,c]);h.constructor===Number&&(h=[h,h,h]);var k=this._result;switch(this.properties.OP){case "+":k=vec3.add(k,c,h);break;case "-":k=vec3.sub(k,c,h); break;case "x":case "X":case "*":k=vec3.mul(k,c,h);break;case "/":k=vec3.div(k,c,h);break;case "%":k[0]=c[0]%h[0];k[1]=c[1]%h[1];k[2]=c[2]%h[2];break;case "^":k[0]=Math.pow(c[0],h[0]);k[1]=Math.pow(c[1],h[1]);k[2]=Math.pow(c[2],h[2]);break;case "max":k[0]=Math.max(c[0],h[0]);k[1]=Math.max(c[1],h[1]);k[2]=Math.max(c[2],h[2]);break;case "min":k[0]=Math.min(c[0],h[0]),k[1]=Math.min(c[1],h[1]),k[2]=Math.min(c[2],h[2]);case "dot":k=vec3.dot(c,h);break;case "cross":vec3.cross(k,c,h);break;default:console.warn("Unknown operation: "+ -this.properties.OP)}this.setOutputData(0,k)}};m.prototype.onDrawBackground=function(c){this.flags.collapsed||(c.font="40px Arial",c.fillStyle="#666",c.textAlign="center",c.fillText(this.properties.OP,0.5*this.size[0],0.5*(this.size[1]+y.NODE_TITLE_HEIGHT)),c.textAlign="left")};y.registerNodeType("math3d/operation",m);n.title="vec3_scale";n.desc="scales the components of a vec3";n.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);null==h&&(h=this.properties.f); -var k=this._data;k[0]=c[0]*h;k[1]=c[1]*h;k[2]=c[2]*h;this.setOutputData(0,k)}};y.registerNodeType("math3d/vec3-scale",n);k.title="vec3_length";k.desc="returns the module of a vector";k.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(c=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),this.setOutputData(0,c))};y.registerNodeType("math3d/vec3-length",k);v.title="vec3_normalize";v.desc="returns the vector normalized";v.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h= -Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),k=this._data;k[0]=c[0]/h;k[1]=c[1]/h;k[2]=c[2]/h;this.setOutputData(0,k)}};y.registerNodeType("math3d/vec3-normalize",v);h.title="vec3_lerp";h.desc="returns the interpolated vector";h.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);if(null!=h){var k=this.getInputOrProperty("f"),e=this._data;e[0]=c[0]*(1-k)+h[0]*k;e[1]=c[1]*(1-k)+h[1]*k;e[2]=c[2]*(1-k)+h[2]*k;this.setOutputData(0,e)}}};y.registerNodeType("math3d/vec3-lerp", -h);B.title="vec3_dot";B.desc="returns the dot product";B.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);null!=h&&this.setOutputData(0,c[0]*h[0]+c[1]*h[1]+c[2]*h[2])}};y.registerNodeType("math3d/vec3-dot",B);D.glMatrix?(D=function(){this.addOutput("quat","quat");this.properties={x:0,y:0,z:0,w:1,normalize:!1};this._value=quat.create()},D.title="Quaternion",D.desc="quaternion",D.prototype.onExecute=function(){this._value[0]=this.getInputOrProperty("x"); -this._value[1]=this.getInputOrProperty("y");this._value[2]=this.getInputOrProperty("z");this._value[3]=this.getInputOrProperty("w");this.properties.normalize&&quat.normalize(this._value,this._value);this.setOutputData(0,this._value)},D.prototype.onGetInputs=function(){return[["x","number"],["y","number"],["z","number"],["w","number"]]},y.registerNodeType("math3d/quaternion",D),D=function(){this.addInputs([["degrees","number"],["axis","vec3"]]);this.addOutput("quat","quat");this.properties={angle:90, -axis:vec3.fromValues(0,1,0)};this._value=quat.create()},D.title="Rotation",D.desc="quaternion rotation",D.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.angle);var h=this.getInputData(1);null==h&&(h=this.properties.axis);c=quat.setAxisAngle(this._value,h,0.0174532925*c);this.setOutputData(0,c)},y.registerNodeType("math3d/rotation",D),D=function(){this.addInput("euler","vec3");this.addOutput("quat","quat");this.properties={euler:[0,0,0],use_yaw_pitch_roll:!1}; -this._degs=vec3.create();this._value=quat.create()},D.title="Euler->Quat",D.desc="Converts euler angles (in degrees) to quaternion",D.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.euler);vec3.scale(this._degs,c,DEG2RAD);this.properties.use_yaw_pitch_roll&&(this._degs=[this._degs[2],this._degs[0],this._degs[1]]);c=quat.fromEuler(this._value,this._degs);this.setOutputData(0,c)},y.registerNodeType("math3d/euler_to_quat",D),D=function(){this.addInput(["quat","quat"]); -this.addOutput("euler","vec3");this._value=vec3.create()},D.title="Euler->Quat",D.desc="Converts rotX,rotY,rotZ in degrees to quat",D.prototype.onExecute=function(){var c=this.getInputData(0);c&&(quat.toEuler(this._value,c),vec3.scale(this._value,this._value,DEG2RAD),this.setOutputData(0,this._value))},y.registerNodeType("math3d/quat_to_euler",D),D=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},D.title="Rot. Vec3",D.desc= -"rotate a point",D.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.vec);var h=this.getInputData(1);null==h?this.setOutputData(c):this.setOutputData(0,vec3.transformQuat(vec3.create(),c,h))},y.registerNodeType("math3d/rotate_vec3",D),D=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},D.title="Mult. Quat",D.desc="rotate quaternion",D.prototype.onExecute=function(){var c=this.getInputData(0);if(null!= -c){var h=this.getInputData(1);null!=h&&(c=quat.multiply(this._value,c,h),this.setOutputData(0,c))}},y.registerNodeType("math3d/mult-quat",D),D=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp","quat");this.addProperty("factor",0.5);this._value=quat.create()},D.title="Quat Slerp",D.desc="quaternion spherical interpolation",D.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);if(null!=h){var k=this.properties.factor; -null!=this.getInputData(2)&&(k=this.getInputData(2));c=quat.slerp(this._value,c,h,k);this.setOutputData(0,c)}}},y.registerNodeType("math3d/quat-slerp",D),D=function(){this.addInput("vec3","vec3");this.addOutput("remap","vec3");this.addOutput("clamped","vec3");this.properties={clamp:!0,range_min:[-1,-1,0],range_max:[1,1,0],target_min:[-1,-1,0],target_max:[1,1,0]};this._value=vec3.create();this._clamped=vec3.create()},D.title="Remap Range",D.desc="remap a 3D range",D.prototype.onExecute=function(){var c= -this.getInputData(0);c&&this._value.set(c);for(var c=this.properties.range_min,h=this.properties.range_max,k=this.properties.target_min,e=this.properties.target_max,n=0;3>n;++n){var m=h[n]-c[n];this._clamped[n]=Math.clamp(this._value[n],c[n],h[n]);0==m?this._value[n]=0.5*(k[n]+e[n]):(m=(this._value[n]-c[n])/m,this.properties.clamp&&(m=Math.clamp(m,0,1)),this._value[n]=k[n]+m*(e[n]-k[n]))}this.setOutputData(0,this._value);this.setOutputData(1,this._clamped)},y.registerNodeType("math3d/remap_range", -D)):console.warn("No glmatrix found, some Math3D nodes may not work")})(this); -(function(D){function c(c,k){return c==k}function m(){this.addInput("","string");this.addOutput("table","table");this.addOutput("rows","number");this.addProperty("value","");this.addProperty("separator",",");this._table=null}D=D.LiteGraph;D.wrapFunctionAsNode("string/toString",c,[""],"String");D.wrapFunctionAsNode("string/compare",c,["string","string"],"boolean");D.wrapFunctionAsNode("string/concatenate",function(c,k){return void 0===c?k:void 0===k?c:c+k},["string","string"],"string");D.wrapFunctionAsNode("string/contains", -function(c,k){return void 0===c||void 0===k?!1:-1!=c.indexOf(k)},["string","string"],"boolean");D.wrapFunctionAsNode("string/toUpperCase",function(c){return null!=c&&c.constructor===String?c.toUpperCase():c},["string"],"string");D.wrapFunctionAsNode("string/split",function(c,k){null==k&&(k=this.properties.separator);if(null==c)return[];if(c.constructor===String)return c.split(k||" ");if(c.constructor===Array){for(var m=[],h=0;hQuat",C.desc="Converts euler angles (in degrees) to quaternion",C.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.euler);vec3.scale(this._degs,c,DEG2RAD);this.properties.use_yaw_pitch_roll&&(this._degs=[this._degs[2],this._degs[0],this._degs[1]]);c=quat.fromEuler(this._value,this._degs);this.setOutputData(0,c)},x.registerNodeType("math3d/euler_to_quat",C),C=function(){this.addInput(["quat","quat"]); +this.addOutput("euler","vec3");this._value=vec3.create()},C.title="Euler->Quat",C.desc="Converts rotX,rotY,rotZ in degrees to quat",C.prototype.onExecute=function(){var c=this.getInputData(0);c&&(quat.toEuler(this._value,c),vec3.scale(this._value,this._value,DEG2RAD),this.setOutputData(0,this._value))},x.registerNodeType("math3d/quat_to_euler",C),C=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},C.title="Rot. Vec3",C.desc= +"rotate a point",C.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.vec);var h=this.getInputData(1);null==h?this.setOutputData(c):this.setOutputData(0,vec3.transformQuat(vec3.create(),c,h))},x.registerNodeType("math3d/rotate_vec3",C),C=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},C.title="Mult. Quat",C.desc="rotate quaternion",C.prototype.onExecute=function(){var c=this.getInputData(0);if(null!= +c){var h=this.getInputData(1);null!=h&&(c=quat.multiply(this._value,c,h),this.setOutputData(0,c))}},x.registerNodeType("math3d/mult-quat",C),C=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp","quat");this.addProperty("factor",0.5);this._value=quat.create()},C.title="Quat Slerp",C.desc="quaternion spherical interpolation",C.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);if(null!=h){var k=this.properties.factor; +null!=this.getInputData(2)&&(k=this.getInputData(2));c=quat.slerp(this._value,c,h,k);this.setOutputData(0,c)}}},x.registerNodeType("math3d/quat-slerp",C),C=function(){this.addInput("vec3","vec3");this.addOutput("remap","vec3");this.addOutput("clamped","vec3");this.properties={clamp:!0,range_min:[-1,-1,0],range_max:[1,1,0],target_min:[-1,-1,0],target_max:[1,1,0]};this._value=vec3.create();this._clamped=vec3.create()},C.title="Remap Range",C.desc="remap a 3D range",C.prototype.onExecute=function(){var c= +this.getInputData(0);c&&this._value.set(c);for(var c=this.properties.range_min,h=this.properties.range_max,k=this.properties.target_min,e=this.properties.target_max,p=0;3>p;++p){var n=h[p]-c[p];this._clamped[p]=Math.clamp(this._value[p],c[p],h[p]);0==n?this._value[p]=0.5*(k[p]+e[p]):(n=(this._value[p]-c[p])/n,this.properties.clamp&&(n=Math.clamp(n,0,1)),this._value[p]=k[p]+n*(e[p]-k[p]))}this.setOutputData(0,this._value);this.setOutputData(1,this._clamped)},x.registerNodeType("math3d/remap_range", +C)):console.warn("No glmatrix found, some Math3D nodes may not work")})(this); +(function(C){function c(c,k){return c==k}function m(){this.addInput("","string");this.addOutput("table","table");this.addOutput("rows","number");this.addProperty("value","");this.addProperty("separator",",");this._table=null}C=C.LiteGraph;C.wrapFunctionAsNode("string/toString",c,[""],"String");C.wrapFunctionAsNode("string/compare",c,["string","string"],"boolean");C.wrapFunctionAsNode("string/concatenate",function(c,k){return void 0===c?k:void 0===k?c:c+k},["string","string"],"string");C.wrapFunctionAsNode("string/contains", +function(c,k){return void 0===c||void 0===k?!1:-1!=c.indexOf(k)},["string","string"],"boolean");C.wrapFunctionAsNode("string/toUpperCase",function(c){return null!=c&&c.constructor===String?c.toUpperCase():c},["string"],"string");C.wrapFunctionAsNode("string/split",function(c,k){null==k&&(k=this.properties.separator);if(null==c)return[];if(c.constructor===String)return c.split(k||" ");if(c.constructor===Array){for(var m=[],h=0;he;++e){var h=this.getInputData(e);if(null!=h){var k=this.values[e];k.push(h);k.length>c[0]&&k.shift()}}}};c.prototype.onDrawBackground=function(e){if(!this.flags.collapsed){var h=this.size,k=0.5*h[1]/ -this.properties.scale,m=c.colors,p=0.5*h[1];e.fillStyle="#000";e.fillRect(0,0,h[0],h[1]);e.strokeStyle="#555";e.beginPath();e.moveTo(0,p);e.lineTo(h[0],p);e.stroke();if(this.inputs)for(var s=0;4>s;++s){var l=this.values[s];if(this.inputs[s]&&this.inputs[s].link){e.strokeStyle=m[s];e.beginPath();var a=l[0]*k*-1+p;e.moveTo(0,Math.clamp(a,0,h[1]));for(var b=1;be;++e){var h=this.getInputData(e);if(null!=h){var k=this.values[e];k.push(h);k.length>c[0]&&k.shift()}}}};c.prototype.onDrawBackground=function(e){if(!this.flags.collapsed){var h=this.size,k=0.5*h[1]/ +this.properties.scale,m=c.colors,w=0.5*h[1];e.fillStyle="#000";e.fillRect(0,0,h[0],h[1]);e.strokeStyle="#555";e.beginPath();e.moveTo(0,w);e.lineTo(h[0],w);e.stroke();if(this.inputs)for(var t=0;4>t;++t){var l=this.values[t];if(this.inputs[t]&&this.inputs[t].link){e.strokeStyle=m[t];e.beginPath();var a=l[0]*k*-1+w;e.moveTo(0,Math.clamp(a,0,h[1]));for(var b=1;be&&(e=0);if(0!=c.length){var h=[0,0,0];if(0==e)h=c[0];else if(1==e)h=c[c.length-1];else{var k= -(c.length-1)*e,e=c[Math.floor(k)],c=c[Math.floor(k)+1],k=k-Math.floor(k);h[0]=e[0]*(1-k)+c[0]*k;h[1]=e[1]*(1-k)+c[1]*k;h[2]=e[2]*(1-k)+c[2]*k}for(var p in h)h[p]/=255;this.boxcolor=colorToString(h);this.setOutputData(0,h)}};e.registerNodeType("color/palette",n);k.title="Frame";k.desc="Frame viewerew";k.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}];k.prototype.onDrawBackground=function(c){this.frame&&!this.flags.collapsed&&c.drawImage(this.frame, +(c.length-1)*e,e=c[Math.floor(k)],c=c[Math.floor(k)+1],k=k-Math.floor(k);h[0]=e[0]*(1-k)+c[0]*k;h[1]=e[1]*(1-k)+c[1]*k;h[2]=e[2]*(1-k)+c[2]*k}for(var w in h)h[w]/=255;this.boxcolor=colorToString(h);this.setOutputData(0,h)}};e.registerNodeType("color/palette",n);k.title="Frame";k.desc="Frame viewerew";k.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}];k.prototype.onDrawBackground=function(c){this.frame&&!this.flags.collapsed&&c.drawImage(this.frame, 0,0,this.size[0],this.size[1])};k.prototype.onExecute=function(){this.frame=this.getInputData(0);this.setDirtyCanvas(!0)};k.prototype.onWidget=function(c,e){if("resize"==e.name&&this.frame){var h=this.frame.width,k=this.frame.height;h||null==this.frame.videoWidth||(h=this.frame.videoWidth,k=this.frame.videoHeight);h&&k&&(this.size=[h,k]);this.setDirtyCanvas(!0,!0)}else"view"==e.name&&this.show()};k.prototype.show=function(){showElement&&this.frame&&showElement(this.frame)};e.registerNodeType("graphics/frame", -k);v.title="Image fade";v.desc="Fades between images";v.widgets=[{name:"resizeA",text:"Resize to A",type:"button"},{name:"resizeB",text:"Resize to B",type:"button"}];v.prototype.onAdded=function(){this.createCanvas();var c=this.canvas.getContext("2d");c.fillStyle="#000";c.fillRect(0,0,this.properties.width,this.properties.height)};v.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};v.prototype.onExecute= -function(){var c=this.canvas.getContext("2d");this.canvas.width=this.canvas.width;var e=this.getInputData(0);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);e=this.getInputData(2);null==e?e=this.properties.fade:this.properties.fade=e;c.globalAlpha=e;e=this.getInputData(1);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);c.globalAlpha=1;this.setOutputData(0,this.canvas);this.setDirtyCanvas(!0)};e.registerNodeType("graphics/imagefade",v);h.title="Crop";h.desc="Crop Image"; +k);y.title="Image fade";y.desc="Fades between images";y.widgets=[{name:"resizeA",text:"Resize to A",type:"button"},{name:"resizeB",text:"Resize to B",type:"button"}];y.prototype.onAdded=function(){this.createCanvas();var c=this.canvas.getContext("2d");c.fillStyle="#000";c.fillRect(0,0,this.properties.width,this.properties.height)};y.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};y.prototype.onExecute= +function(){var c=this.canvas.getContext("2d");this.canvas.width=this.canvas.width;var e=this.getInputData(0);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);e=this.getInputData(2);null==e?e=this.properties.fade:this.properties.fade=e;c.globalAlpha=e;e=this.getInputData(1);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);c.globalAlpha=1;this.setOutputData(0,this.canvas);this.setDirtyCanvas(!0)};e.registerNodeType("graphics/imagefade",y);h.title="Crop";h.desc="Crop Image"; h.prototype.onAdded=function(){this.createCanvas()};h.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};h.prototype.onExecute=function(){var c=this.getInputData(0);c&&(c.width?(this.canvas.getContext("2d").drawImage(c,-this.properties.x,-this.properties.y,c.width*this.properties.scale,c.height*this.properties.scale),this.setOutputData(0,this.canvas)):this.setOutputData(0,null))};h.prototype.onDrawBackground= -function(c){this.flags.collapsed||this.canvas&&c.drawImage(this.canvas,0,0,this.canvas.width,this.canvas.height,0,0,this.size[0],this.size[1])};h.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"scale"==c?(this.properties[c]=parseFloat(e),0==this.properties[c]&&(console.error("Error in scale"),this.properties[c]=1)):this.properties[c]=parseInt(e);this.createCanvas();return!0};e.registerNodeType("graphics/cropImage",h);B.title="Canvas";B.desc="Canvas to render stuff";B.prototype.onExecute= -function(){var c=this.canvas,e=this.properties.width|0,h=this.properties.height|0;c.width!=e&&(c.width=e);c.height!=h&&(c.height=h);this.properties.autoclear&&this.ctx.clearRect(0,0,c.width,c.height);this.setOutputData(0,c)};B.prototype.onAction=function(c,e){"clear"==c&&this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)};e.registerNodeType("graphics/canvas",B);y.title="DrawImage";y.desc="Draws image into a canvas";y.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e= -this.getInputOrProperty("img");if(e){var h=this.getInputOrProperty("x"),k=this.getInputOrProperty("y");c.getContext("2d").drawImage(e,h,k)}}};e.registerNodeType("graphics/drawImage",y);C.title="DrawRectangle";C.desc="Draws rectangle in canvas";C.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e=this.getInputOrProperty("x"),h=this.getInputOrProperty("y"),k=this.getInputOrProperty("w"),p=this.getInputOrProperty("h");c.getContext("2d").fillRect(e,h,k,p)}};e.registerNodeType("graphics/drawRectangle", -C);G.title="Video";G.desc="Video playback";G.widgets=[{name:"play",text:"PLAY",type:"minibutton"},{name:"stop",text:"STOP",type:"minibutton"},{name:"demo",text:"Demo video",type:"button"},{name:"mute",text:"Mute video",type:"button"}];G.prototype.onExecute=function(){if(this.properties.url&&(this.properties.url!=this._video_url&&this.loadVideo(this.properties.url),this._video&&0!=this._video.width)){var c=this.getInputData(0);c&&0<=c&&1>=c&&(this._video.currentTime=c*this._video.duration,this._video.pause()); +function(c){this.flags.collapsed||this.canvas&&c.drawImage(this.canvas,0,0,this.canvas.width,this.canvas.height,0,0,this.size[0],this.size[1])};h.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"scale"==c?(this.properties[c]=parseFloat(e),0==this.properties[c]&&(console.error("Error in scale"),this.properties[c]=1)):this.properties[c]=parseInt(e);this.createCanvas();return!0};e.registerNodeType("graphics/cropImage",h);D.title="Canvas";D.desc="Canvas to render stuff";D.prototype.onExecute= +function(){var c=this.canvas,e=this.properties.width|0,h=this.properties.height|0;c.width!=e&&(c.width=e);c.height!=h&&(c.height=h);this.properties.autoclear&&this.ctx.clearRect(0,0,c.width,c.height);this.setOutputData(0,c)};D.prototype.onAction=function(c,e){"clear"==c&&this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)};e.registerNodeType("graphics/canvas",D);x.title="DrawImage";x.desc="Draws image into a canvas";x.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e= +this.getInputOrProperty("img");if(e){var h=this.getInputOrProperty("x"),k=this.getInputOrProperty("y");c.getContext("2d").drawImage(e,h,k)}}};e.registerNodeType("graphics/drawImage",x);B.title="DrawRectangle";B.desc="Draws rectangle in canvas";B.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e=this.getInputOrProperty("x"),h=this.getInputOrProperty("y"),k=this.getInputOrProperty("w"),w=this.getInputOrProperty("h");c.getContext("2d").fillRect(e,h,k,w)}};e.registerNodeType("graphics/drawRectangle", +B);G.title="Video";G.desc="Video playback";G.widgets=[{name:"play",text:"PLAY",type:"minibutton"},{name:"stop",text:"STOP",type:"minibutton"},{name:"demo",text:"Demo video",type:"button"},{name:"mute",text:"Mute video",type:"button"}];G.prototype.onExecute=function(){if(this.properties.url&&(this.properties.url!=this._video_url&&this.loadVideo(this.properties.url),this._video&&0!=this._video.width)){var c=this.getInputData(0);c&&0<=c&&1>=c&&(this._video.currentTime=c*this._video.duration,this._video.pause()); this._video.dirty=!0;this.setOutputData(0,this._video);this.setOutputData(1,this._video.currentTime);this.setOutputData(2,this._video.duration);this.setDirtyCanvas(!0)}};G.prototype.onStart=function(){this.play()};G.prototype.onStop=function(){this.stop()};G.prototype.loadVideo=function(c){this._video_url=c;var h=c.substr(0,10).indexOf(":"),k="";-1!=h&&(k=c.substr(0,h));h="";k&&(h=c.substr(0,c.indexOf("/",k.length+3)),h=h.substr(k.length+3));this.properties.use_proxy&&k&&e.proxy&&h!=location.host&& (c=e.proxy+c.substr(c.indexOf(":")+3));this._video=document.createElement("video");this._video.src=c;this._video.type="type=video/mp4";this._video.muted=!0;this._video.autoplay=!0;var m=this;this._video.addEventListener("loadedmetadata",function(c){console.log("Duration: "+this.duration+" seconds");console.log("Size: "+this.videoWidth+","+this.videoHeight);m.setDirtyCanvas(!0);this.width=this.videoWidth;this.height=this.videoHeight});this._video.addEventListener("progress",function(c){console.log("video loading...")}); this._video.addEventListener("error",function(c){console.error("Error loading video: "+this.src);if(this.error)switch(this.error.code){case this.error.MEDIA_ERR_ABORTED:console.error("You stopped the video.");break;case this.error.MEDIA_ERR_NETWORK:console.error("Network error - please try again later.");break;case this.error.MEDIA_ERR_DECODE:console.error("Video is broken..");break;case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED:console.error("Sorry, your browser can't play this video.")}});this._video.addEventListener("ended", @@ -445,26 +446,26 @@ var e=this}};E.prototype.closeStream=function(){if(this._webcam_stream){var c=th c;this.boxcolor="green";var e=this._video;e||(e=document.createElement("video"),e.autoplay=!0,e.srcObject=c,this._video=e,e.onloadedmetadata=function(c){console.log(c);E.is_webcam_open=!0});this.trigger("stream_ready",e)};E.prototype.onExecute=function(){null!=this._webcam_stream||this._waiting_confirmation||this.openStream();if(this._video&&this._video.videoWidth){this._video.frame=++this.frame;this._video.width=this._video.videoWidth;this._video.height=this._video.videoHeight;this.setOutputData(0, this._video);for(var c=1;c=this.size[1]||!this.properties.show|| !this._video||(c.save(),c.drawImage(this._video,0,0,this.size[0],this.size[1]),c.restore())};E.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["stream_ready",e.EVENT],["stream_closed",e.EVENT],["stream_error",e.EVENT]]};e.registerNodeType("graphics/webcam",E)})(this); -(function(D){function c(){this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",filter:!0};this.size=[c.image_preview_size,c.image_preview_size]}function m(){this.addInput("Texture","Texture");this.properties={flipY:!1};this.size=[c.image_preview_size,c.image_preview_size]}function n(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",generate_mipmaps:!1}}function k(){this.addInput("Texture", +(function(C){function c(){this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",filter:!0};this.size=[c.image_preview_size,c.image_preview_size]}function m(){this.addInput("Texture","Texture");this.properties={flipY:!1};this.size=[c.image_preview_size,c.image_preview_size]}function n(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",generate_mipmaps:!1}}function k(){this.addInput("Texture", "Texture");this.addInput("TextureB","Texture");this.addInput("value","number");this.addOutput("Texture","Texture");this.help="

pixelcode must be vec3, uvcode must be vec2, is optional

\t\t

uv: tex. coords

color: texture colorB: textureB

time: scene time value: input value

For multiline you must type: result = ...

";this.properties={value:1,pixelcode:"color + colorB * value",uvcode:"",precision:c.DEFAULT}; -this.has_error=!1}function v(){this.addOutput("out","Texture");this.properties={code:"",u_value:1,u_color:[1,1,1,1],width:512,height:512,precision:c.DEFAULT};this.properties.code=v.pixel_shader;this._uniforms={u_value:1,u_color:vec4.create(),in_texture:0,texSize:vec4.create(),time:0}}function h(){this.addInput("in","Texture");this.addInput("scale","vec2");this.addInput("offset","vec2");this.addOutput("out","Texture");this.properties={offset:vec2.fromValues(0,0),scale:vec2.fromValues(1,1),precision:c.DEFAULT}} -function B(){this.addInput("in","Texture");this.addInput("warp","Texture");this.addInput("factor","number");this.addOutput("out","Texture");this.properties={factor:0.01,scale:[1,1],offset:[0,0],precision:c.DEFAULT};this._uniforms={u_texture:0,u_textureB:1,u_factor:1,u_scale:vec2.create(),u_offset:vec2.create()}}function y(){this.addInput("Texture","Texture");this.properties={additive:!1,antialiasing:!1,filter:!0,disable_alpha:!1,gamma:1,viewport:[0,0,1,1]};this.size[0]=130}function C(){this.addInput("Texture", +this.has_error=!1}function y(){this.addOutput("out","Texture");this.properties={code:"",u_value:1,u_color:[1,1,1,1],width:512,height:512,precision:c.DEFAULT};this.properties.code=y.pixel_shader;this._uniforms={u_value:1,u_color:vec4.create(),in_texture:0,texSize:vec4.create(),time:0}}function h(){this.addInput("in","Texture");this.addInput("scale","vec2");this.addInput("offset","vec2");this.addOutput("out","Texture");this.properties={offset:vec2.fromValues(0,0),scale:vec2.fromValues(1,1),precision:c.DEFAULT}} +function D(){this.addInput("in","Texture");this.addInput("warp","Texture");this.addInput("factor","number");this.addOutput("out","Texture");this.properties={factor:0.01,scale:[1,1],offset:[0,0],precision:c.DEFAULT};this._uniforms={u_texture:0,u_textureB:1,u_factor:1,u_scale:vec2.create(),u_offset:vec2.create()}}function x(){this.addInput("Texture","Texture");this.properties={additive:!1,antialiasing:!1,filter:!0,disable_alpha:!1,gamma:1,viewport:[0,0,1,1]};this.size[0]=130}function B(){this.addInput("Texture", "Texture");this.addOutput("","Texture");this.properties={size:0,generate_mipmaps:!1,precision:c.DEFAULT}}function G(){this.addInput("Texture","Texture");this.addOutput("","Texture");this.properties={iterations:1,generate_mipmaps:!1,precision:c.DEFAULT}}function E(){this.addInput("Texture","Texture");this.addOutput("","Texture");this.properties={size:[512,512],generate_mipmaps:!1,precision:c.DEFAULT}}function e(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("avg", -"vec4");this.addOutput("lum","number");this.properties={use_previous_frame:!0,high_quality:!1};this._uniforms={u_texture:0,u_mipmap_offset:0};this._luminance=new Float32Array(4)}function A(){this.addInput("Texture","Texture");this.addOutput("min_t","Texture");this.addOutput("max_t","Texture");this.addOutput("min","vec4");this.addOutput("max","vec4");this.properties={mode:"max",use_previous_frame:!0};this._uniforms={u_texture:0};this._max=new Float32Array(4);this._min=new Float32Array(4);this._textures_chain= -[]}function F(){this.addInput("in","Texture");this.addInput("factor","Number");this.addOutput("out","Texture");this.properties={factor:0.5};this._uniforms={u_texture:0,u_textureB:1,u_factor:this.properties.factor}}function t(){this.addInput("in","Texture");this.addOutput("avg","Texture");this.addOutput("array","Texture");this.properties={samples:64,frames_interval:1};this._uniforms={u_texture:0,u_textureB:1,u_samples:this.properties.samples,u_isamples:1/this.properties.samples};this.frame=0}function H(){this.addInput("Image", -"image");this.addOutput("","Texture");this.properties={}}function p(){this.addInput("Texture","Texture");this.addInput("LUT","Texture");this.addInput("Intensity","number");this.addOutput("","Texture");this.properties={enabled:!0,intensity:1,precision:c.DEFAULT,texture:null};p._shader||(p._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,p.pixel_shader))}function s(){this.addInput("Texture","Texture");this.addInput("Atlas","Texture");this.addOutput("","Texture");this.properties={enabled:!0,num_row_symbols:4, -symbol_size:16,brightness:1,colorize:!1,filter:!1,invert:!1,precision:c.DEFAULT,generate_mipmaps:!1,texture:null};s._shader||(s._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,s.pixel_shader));this._uniforms={u_texture:0,u_textureB:1,u_row_simbols:4,u_simbol_size:16,u_res:vec2.create()}}function l(){this.addInput("Texture","Texture");this.addOutput("R","Texture");this.addOutput("G","Texture");this.addOutput("B","Texture");this.addOutput("A","Texture");l._shader||(l._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER, +"vec4");this.addOutput("lum","number");this.properties={use_previous_frame:!0,high_quality:!1};this._uniforms={u_texture:0,u_mipmap_offset:0};this._luminance=new Float32Array(4)}function p(){this.addInput("Texture","Texture");this.addOutput("min_t","Texture");this.addOutput("max_t","Texture");this.addOutput("min","vec4");this.addOutput("max","vec4");this.properties={mode:"max",use_previous_frame:!0};this._uniforms={u_texture:0};this._max=new Float32Array(4);this._min=new Float32Array(4);this._textures_chain= +[]}function F(){this.addInput("in","Texture");this.addInput("factor","Number");this.addOutput("out","Texture");this.properties={factor:0.5};this._uniforms={u_texture:0,u_textureB:1,u_factor:this.properties.factor}}function v(){this.addInput("in","Texture");this.addOutput("avg","Texture");this.addOutput("array","Texture");this.properties={samples:64,frames_interval:1};this._uniforms={u_texture:0,u_textureB:1,u_samples:this.properties.samples,u_isamples:1/this.properties.samples};this.frame=0}function H(){this.addInput("Image", +"image");this.addOutput("","Texture");this.properties={}}function w(){this.addInput("Texture","Texture");this.addInput("LUT","Texture");this.addInput("Intensity","number");this.addOutput("","Texture");this.properties={enabled:!0,intensity:1,precision:c.DEFAULT,texture:null};w._shader||(w._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,w.pixel_shader))}function t(){this.addInput("Texture","Texture");this.addInput("Atlas","Texture");this.addOutput("","Texture");this.properties={enabled:!0,num_row_symbols:4, +symbol_size:16,brightness:1,colorize:!1,filter:!1,invert:!1,precision:c.DEFAULT,generate_mipmaps:!1,texture:null};t._shader||(t._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,t.pixel_shader));this._uniforms={u_texture:0,u_textureB:1,u_row_simbols:4,u_simbol_size:16,u_res:vec2.create()}}function l(){this.addInput("Texture","Texture");this.addOutput("R","Texture");this.addOutput("G","Texture");this.addOutput("B","Texture");this.addOutput("A","Texture");l._shader||(l._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER, l.pixel_shader))}function a(){this.addInput("R","Texture");this.addInput("G","Texture");this.addInput("B","Texture");this.addInput("A","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,R:1,G:1,B:1,A:1};this._color=vec4.create();this._uniforms={u_textureR:0,u_textureG:1,u_textureB:2,u_textureA:3,u_color:this._color}}function b(){this.addOutput("Texture","Texture");this._tex_color=vec4.create();this.properties={color:vec4.create(),precision:c.DEFAULT}}function d(){this.addInput("A", "color");this.addInput("B","color");this.addOutput("Texture","Texture");this.properties={angle:0,scale:1,A:[0,0,0],B:[1,1,1],texture_size:32};d._shader||(d._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,d.pixel_shader));this._uniforms={u_angle:0,u_colorA:vec3.create(),u_colorB:vec3.create()}}function f(){this.addInput("A","Texture");this.addInput("B","Texture");this.addInput("Mixer","Texture");this.addOutput("Texture","Texture");this.properties={factor:0.5,size_from_biggest:!0,invert:!1,precision:c.DEFAULT}; -this._uniforms={u_textureA:0,u_textureB:1,u_textureMix:2,u_mix:vec4.create()}}function g(){this.addInput("Tex.","Texture");this.addOutput("Edges","Texture");this.properties={invert:!0,threshold:!1,factor:1,precision:c.DEFAULT};g._shader||(g._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,g.pixel_shader))}function r(){this.addInput("Texture","Texture");this.addInput("Distance","number");this.addInput("Range","number");this.addOutput("Texture","Texture");this.properties={distance:100,range:50,only_depth:!1, -high_precision:!1};this._uniforms={u_texture:0,u_distance:100,u_range:50,u_camera_planes:null}}function x(){this.addInput("Texture","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,invert:!1};this._uniforms={u_texture:0,u_camera_planes:null,u_ires:vec2.create()}}function K(){this.addInput("Texture","Texture");this.addInput("Iterations","number");this.addInput("Intensity","number");this.addOutput("Blurred","Texture");this.properties={intensity:1,iterations:1,preserve_aspect:!1, -scale:[1,1],precision:c.DEFAULT}}function q(){this.addInput("in","Texture");this.addInput("dirt","Texture");this.addOutput("out","Texture");this.addOutput("glow","Texture");this.properties={enabled:!0,intensity:1,persistence:0.99,iterations:16,threshold:0,scale:1,dirt_factor:0.5,precision:c.DEFAULT};this._textures=[];this._uniforms={u_intensity:1,u_texture:0,u_glow_texture:1,u_threshold:0,u_texel_size:vec2.create()}}function z(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture"); -this.properties={intensity:1,radius:5}}function w(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={sigma:1.4,k:1.6,p:21.7,epsilon:79,phi:0.017}}function u(){this.addOutput("Webcam","Texture");this.properties={texture_name:"",facingMode:"user"};this.boxcolor="black";this.version=0}function N(){this.addInput("in","Texture");this.addInput("f","number");this.addOutput("out","Texture");this.properties={enabled:!0,factor:1,precision:c.LOW};this._uniforms={u_texture:0, +this._uniforms={u_textureA:0,u_textureB:1,u_textureMix:2,u_mix:vec4.create()}}function g(){this.addInput("Tex.","Texture");this.addOutput("Edges","Texture");this.properties={invert:!0,threshold:!1,factor:1,precision:c.DEFAULT};g._shader||(g._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,g.pixel_shader))}function q(){this.addInput("Texture","Texture");this.addInput("Distance","number");this.addInput("Range","number");this.addOutput("Texture","Texture");this.properties={distance:100,range:50,only_depth:!1, +high_precision:!1};this._uniforms={u_texture:0,u_distance:100,u_range:50,u_camera_planes:null}}function A(){this.addInput("Texture","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,invert:!1};this._uniforms={u_texture:0,u_camera_planes:null,u_ires:vec2.create()}}function K(){this.addInput("Texture","Texture");this.addInput("Iterations","number");this.addInput("Intensity","number");this.addOutput("Blurred","Texture");this.properties={intensity:1,iterations:1,preserve_aspect:!1, +scale:[1,1],precision:c.DEFAULT}}function r(){this.addInput("in","Texture");this.addInput("dirt","Texture");this.addOutput("out","Texture");this.addOutput("glow","Texture");this.properties={enabled:!0,intensity:1,persistence:0.99,iterations:16,threshold:0,scale:1,dirt_factor:0.5,precision:c.DEFAULT};this._textures=[];this._uniforms={u_intensity:1,u_texture:0,u_glow_texture:1,u_threshold:0,u_texel_size:vec2.create()}}function s(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture"); +this.properties={intensity:1,radius:5}}function z(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={sigma:1.4,k:1.6,p:21.7,epsilon:79,phi:0.017}}function u(){this.addOutput("Webcam","Texture");this.properties={texture_name:"",facingMode:"user"};this.boxcolor="black";this.version=0}function N(){this.addInput("in","Texture");this.addInput("f","number");this.addOutput("out","Texture");this.properties={enabled:!0,factor:1,precision:c.LOW};this._uniforms={u_texture:0, u_factor:1}}function R(){this.addInput("in","");this.properties={precision:c.LOW,width:0,height:0,channels:1};this.addOutput("out","Texture")}function J(){this.addInput("in","Texture");this.addOutput("out","Texture");this.properties={precision:c.LOW,split_channels:!1};this._values=new Uint8Array(1024);this._values.fill(255);this._curve_texture=null;this._uniforms={u_texture:0,u_curve:1,u_range:1};this._must_update=!0;this._points={RGB:[[0,0],[1,1]],R:[[0,0],[1,1]],G:[[0,0],[1,1]],B:[[0,0],[1,1]]}; this.curve_editor=null;this.addWidget("toggle","Split Channels",!1,"split_channels");this.addWidget("combo","Channel","RGB",{values:["RGB","R","G","B"]});this.curve_offset=68;this.size=[240,160]}function P(){this.addInput("in","Texture");this.addInput("exp","number");this.addOutput("out","Texture");this.properties={exposition:1,precision:c.LOW};this._uniforms={u_texture:0,u_exposition:1}}function L(){this.addInput("in","Texture");this.addInput("avg","number,Texture");this.addOutput("out","Texture"); this.properties={enabled:!0,scale:1,gamma:1,average_lum:1,lum_white:1,precision:c.LOW};this._uniforms={u_texture:0,u_lumwhite2:1,u_igamma:1,u_scale:1,u_average_lum:1}}function O(){this.addOutput("out","Texture");this.properties={width:512,height:512,seed:0,persistence:0.1,octaves:8,scale:1,offset:[0,0],amplitude:1,precision:c.DEFAULT};this._key=0;this._texture=null;this._uniforms={u_persistence:0.1,u_seed:0,u_offset:vec2.create(),u_scale:1,u_viewport:vec2.create()}}function M(){this.addInput("v"); -this.addOutput("out","Texture");this.properties={code:M.default_code,width:512,height:512,clear:!0,precision:c.DEFAULT,use_html_canvas:!1};this._temp_texture=this._func=null;this.compileCode()}function Q(){this.addInput("in","Texture");this.addOutput("out","Texture");this.properties={key_color:vec3.fromValues(0,1,0),threshold:0.8,slope:0.2,precision:c.DEFAULT}}function S(){this.addInput("in","texture");this.addInput("yaw","number");this.addOutput("out","texture");this.properties={yaw:0}}var I=D.LiteGraph, -U=D.LGraphCanvas;D.LGraphTexture=null;"undefined"!=typeof GL&&(U.link_type_colors.Texture="#987",D.LGraphTexture=c,c.title="Texture",c.desc="Texture",c.widgets_info={name:{widget:"texture"},filter:{widget:"checkbox"}},c.loadTextureCallback=null,c.image_preview_size=256,c.UNDEFINED=0,c.PASS_THROUGH=1,c.COPY=2,c.LOW=3,c.HIGH=4,c.REUSE=5,c.DEFAULT=2,c.MODE_VALUES={undefined:c.UNDEFINED,"pass through":c.PASS_THROUGH,copy:c.COPY,low:c.LOW,high:c.HIGH,reuse:c.REUSE,"default":c.DEFAULT},c.getTexturesContainer= +this.addOutput("out","Texture");this.properties={code:M.default_code,width:512,height:512,clear:!0,precision:c.DEFAULT,use_html_canvas:!1};this._temp_texture=this._func=null;this.compileCode()}function Q(){this.addInput("in","Texture");this.addOutput("out","Texture");this.properties={key_color:vec3.fromValues(0,1,0),threshold:0.8,slope:0.2,precision:c.DEFAULT}}function S(){this.addInput("in","texture");this.addInput("yaw","number");this.addOutput("out","texture");this.properties={yaw:0}}var I=C.LiteGraph, +U=C.LGraphCanvas;C.LGraphTexture=null;"undefined"!=typeof GL&&(U.link_type_colors.Texture="#987",C.LGraphTexture=c,c.title="Texture",c.desc="Texture",c.widgets_info={name:{widget:"texture"},filter:{widget:"checkbox"}},c.loadTextureCallback=null,c.image_preview_size=256,c.UNDEFINED=0,c.PASS_THROUGH=1,c.COPY=2,c.LOW=3,c.HIGH=4,c.REUSE=5,c.DEFAULT=2,c.MODE_VALUES={undefined:c.UNDEFINED,"pass through":c.PASS_THROUGH,copy:c.COPY,low:c.LOW,high:c.HIGH,reuse:c.REUSE,"default":c.DEFAULT},c.getTexturesContainer= function(){return gl.textures},c.loadTexture=function(a,b){b=b||{};var d=a;"http://"==d.substr(0,7)&&I.proxy&&(d=I.proxy+d.substr(7));return c.getTexturesContainer()[a]=GL.Texture.fromURL(d,b)},c.getTexture=function(a){var b=this.getTexturesContainer();if(!b)throw"Cannot load texture, container of textures not found";b=b[a];return!b&&a&&":"!=a[0]?this.loadTexture(a):b},c.getTargetTexture=function(a,b,d){if(!a)throw"LGraphTexture.getTargetTexture expects a reference texture";var f=null;switch(d){case c.LOW:f= gl.UNSIGNED_BYTE;break;case c.HIGH:f=gl.HIGH_PRECISION_FORMAT;break;case c.REUSE:return a;default:f=a?a.type:gl.UNSIGNED_BYTE}b&&b.width==a.width&&b.height==a.height&&b.type==f||(b=new GL.Texture(a.width,a.height,{type:f,format:gl.RGBA,filter:gl.LINEAR}));return b},c.getTextureType=function(a,b){var d=b?b.type:gl.UNSIGNED_BYTE;switch(a){case c.HIGH:d=gl.HIGH_PRECISION_FORMAT;break;case c.LOW:d=gl.UNSIGNED_BYTE}return d},c.getWhiteTexture=function(){return this._white_texture?this._white_texture:this._white_texture= GL.Texture.fromMemory(1,1,[255,255,255,255],{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.getNoiseTexture=function(){if(this._noise_texture)return this._noise_texture;for(var a=new Uint8Array(1048576),b=0;1048576>b;++b)a[b]=255*Math.random();return this._noise_texture=a=GL.Texture.fromMemory(512,512,a,{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.prototype.onDropFile=function(a,b,d){if(a){var c=null;"string"==typeof a?c=GL.Texture.fromURL(a):-1!=b.toLowerCase().indexOf(".dds")?c=GL.Texture.fromDDSInMemory(a): @@ -478,29 +479,29 @@ n.prototype.onExecute=function(){var a=this.getInputData(0);a&&(this.properties. pixelcode:{widget:"code"},precision:{widget:"combo",values:c.MODE_VALUES}},k.title="Operation",k.desc="Texture shader operation",k.presets={},k.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:b.properties.show?"Hide Texture":"Show Texture",callback:function(){b.properties.show=!b.properties.show}}]},k.prototype.onPropertyChanged=function(){this.has_error=!1},k.prototype.onDrawBackground=function(a){this.flags.collapsed||20>=this.size[1]||!this.properties.show||!this._tex||this._tex.gl!= a||(a.save(),a.drawImage(this._tex,0,0,this.size[0],this.size[1]),a.restore())},k.prototype.onExecute=function(){var a=this.getInputData(0);if(this.isOutputConnected(0))if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else{var b=this.getInputData(1);if(this.properties.uvcode||this.properties.pixelcode){var d=512,f=512;a?(d=a.width,f=a.height):b&&(d=b.width,f=b.height);b||(b=GL.Texture.getWhiteTexture());var g=c.getTextureType(this.properties.precision,a);this._tex=a||this._tex? c.getTargetTexture(a||this._tex,this._tex,this.properties.precision):new GL.Texture(d,f,{type:g,format:gl.RGBA,filter:gl.LINEAR});g="";this.properties.uvcode&&(g="uv = "+this.properties.uvcode,-1!=this.properties.uvcode.indexOf(";")&&(g=this.properties.uvcode));var e="";this.properties.pixelcode&&(e="result = "+this.properties.pixelcode,-1!=this.properties.pixelcode.indexOf(";")&&(e=this.properties.pixelcode));var h=this._shader;if(!(this.has_error||h&&this._shader_code==g+"|"+e)){var l=c.replaceCode(k.pixel_shader, -{UV_CODE:g,PIXEL_CODE:e});try{h=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,l),this.boxcolor="#00FF00"}catch(p){GL.Shader.dumpErrorToConsole(p,Shader.SCREEN_VERTEX_SHADER,l);this.boxcolor="#FF0000";this.has_error=!0;return}this._shader=h;this._shader_code=g+"|"+e}if(this._shader){var q=this.getInputData(2);null!=q?this.properties.value=q:q=parseFloat(this.properties.value);var z=this.graph.getTime();this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);gl.disable(gl.BLEND); -a&&a.bind(0);b&&b.bind(1);var c=Mesh.getScreenQuad();h.uniforms({u_texture:0,u_textureB:1,value:q,texSize:[d,f,1/d,1/f],time:z}).draw(c)});this.setOutputData(0,this._tex)}}}},k.pixel_shader="precision highp float;\n\t\t\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec4 texSize;\n\t\tuniform float time;\n\t\tuniform float value;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord;\n\t\t\t{{UV_CODE}};\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\tvec3 color = color4.rgb;\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\tvec3 colorB = color4B.rgb;\n\t\t\tvec3 result = color;\n\t\t\tfloat alpha = 1.0;\n\t\t\t{{PIXEL_CODE}};\n\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t}\n\t\t", +{UV_CODE:g,PIXEL_CODE:e});try{h=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,l),this.boxcolor="#00FF00"}catch(r){GL.Shader.dumpErrorToConsole(r,Shader.SCREEN_VERTEX_SHADER,l);this.boxcolor="#FF0000";this.has_error=!0;return}this._shader=h;this._shader_code=g+"|"+e}if(this._shader){var s=this.getInputData(2);null!=s?this.properties.value=s:s=parseFloat(this.properties.value);var w=this.graph.getTime();this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);gl.disable(gl.BLEND); +a&&a.bind(0);b&&b.bind(1);var c=Mesh.getScreenQuad();h.uniforms({u_texture:0,u_textureB:1,value:s,texSize:[d,f,1/d,1/f],time:w}).draw(c)});this.setOutputData(0,this._tex)}}}},k.pixel_shader="precision highp float;\n\t\t\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec4 texSize;\n\t\tuniform float time;\n\t\tuniform float value;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord;\n\t\t\t{{UV_CODE}};\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\tvec3 color = color4.rgb;\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\tvec3 colorB = color4B.rgb;\n\t\t\tvec3 result = color;\n\t\t\tfloat alpha = 1.0;\n\t\t\t{{PIXEL_CODE}};\n\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t}\n\t\t", k.registerPreset=function(a,b){k.presets[a]=b},k.registerPreset("",""),k.registerPreset("bypass","color"),k.registerPreset("add","color + colorB * value"),k.registerPreset("substract","(color - colorB) * value"),k.registerPreset("mate","mix( color, colorB, color4B.a * value)"),k.registerPreset("invert","vec3(1.0) - color"),k.registerPreset("multiply","color * colorB * value"),k.registerPreset("divide","(color / colorB) / value"),k.registerPreset("difference","abs(color - colorB) * value"),k.registerPreset("max", "max(color, colorB) * value"),k.registerPreset("min","min(color, colorB) * value"),k.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"),k.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"),k.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"),k.registerPreset("normalmap","\n\t\tfloat z0 = texture2D(u_texture, uv + vec2(-texSize.z, -texSize.w) ).x;\n\t\tfloat z1 = texture2D(u_texture, uv + vec2(0.0, -texSize.w) ).x;\n\t\tfloat z2 = texture2D(u_texture, uv + vec2(texSize.z, -texSize.w) ).x;\n\t\tfloat z3 = texture2D(u_texture, uv + vec2(-texSize.z, 0.0) ).x;\n\t\tfloat z4 = color.x;\n\t\tfloat z5 = texture2D(u_texture, uv + vec2(texSize.z, 0.0) ).x;\n\t\tfloat z6 = texture2D(u_texture, uv + vec2(-texSize.z, texSize.w) ).x;\n\t\tfloat z7 = texture2D(u_texture, uv + vec2(0.0, texSize.w) ).x;\n\t\tfloat z8 = texture2D(u_texture, uv + vec2(texSize.z, texSize.w) ).x;\n\t\tvec3 normal = vec3( z2 + 2.0*z4 + z7 - z0 - 2.0*z3 - z5, z5 + 2.0*z6 + z7 -z0 - 2.0*z1 - z2, 1.0 );\n\t\tnormal.xy *= value;\n\t\tresult.xyz = normalize(normal) * 0.5 + vec3(0.5);\n\t"), -k.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"),k.prototype.onInspect=function(a){var b=this;a.addCombo("Presets","",{values:Object.keys(k.presets),callback:function(d){var c=k.presets[d];c&&(b.setProperty("pixelcode",c),b.title=d,a.refresh())}})},I.registerNodeType("texture/operation",k),v.title="Shader",v.desc="Texture shader",v.widgets_info={code:{type:"code",lang:"glsl"},precision:{widget:"combo", -values:c.MODE_VALUES}},v.prototype.onPropertyChanged=function(a,b){if("code"==a){var d=this.getShader();if(d){var c=d.uniformInfo;if(this.inputs)for(var f={},g=0;g colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"),k.prototype.onInspect=function(a){var b=this;a.addCombo("Presets","",{values:Object.keys(k.presets),callback:function(d){var c=k.presets[d];c&&(b.setProperty("pixelcode",c),b.title=d,a.refresh())}})},I.registerNodeType("texture/operation",k),y.title="Shader",y.desc="Texture shader",y.widgets_info={code:{type:"code",lang:"glsl"},precision:{widget:"combo", +values:c.MODE_VALUES}},y.prototype.onPropertyChanged=function(a,b){if("code"==a){var d=this.getShader();if(d){var c=d.uniformInfo;if(this.inputs)for(var f={},g=0;g=this.size[1])){var b=this.getInputData(0);b&&a.drawImage(a==gl?b:gl.canvas,10,30,this.size[0]-20,this.size[1]-40)}},y.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this.properties.disable_alpha?gl.disable(gl.BLEND):(gl.enable(gl.BLEND),this.properties.additive?gl.blendFunc(gl.SRC_ALPHA, -gl.ONE):gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA));gl.disable(gl.DEPTH_TEST);var b=this.properties.gamma||1;this.isInputConnected(1)&&(b=this.getInputData(1));a.setParameter(gl.TEXTURE_MAG_FILTER,this.properties.filter?gl.LINEAR:gl.NEAREST);var d=y._prev_viewport;d.set(gl.viewport_data);var c=this.properties.viewport;gl.viewport(d[0]+d[2]*c[0],d[1]+d[3]*c[1],d[2]*c[2],d[3]*c[3]);gl.getViewport();this.properties.antialiasing?(y._shader||(y._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, -y.aa_pixel_shader)),c=Mesh.getScreenQuad(),a.bind(0),y._shader.uniforms({u_texture:0,uViewportSize:[a.width,a.height],u_igamma:1/b,inverseVP:[1/a.width,1/a.height]}).draw(c)):1!=b?(y._gamma_shader||(y._gamma_shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,y.gamma_pixel_shader)),a.toViewport(y._gamma_shader,{u_texture:0,u_igamma:1/b})):a.toViewport();gl.viewport(d[0],d[1],d[2],d[3])}},y.prototype.onGetInputs=function(){return[["gamma","number"]]},y.aa_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 uViewportSize;\n\t\tuniform vec2 inverseVP;\n\t\tuniform float u_igamma;\n\t\t#define FXAA_REDUCE_MIN (1.0/ 128.0)\n\t\t#define FXAA_REDUCE_MUL (1.0 / 8.0)\n\t\t#define FXAA_SPAN_MAX 8.0\n\t\t\n\t\t/* from mitsuhiko/webgl-meincraft based on the code on geeks3d.com */\n\t\tvec4 applyFXAA(sampler2D tex, vec2 fragCoord)\n\t\t{\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t/*vec2 inverseVP = vec2(1.0 / uViewportSize.x, 1.0 / uViewportSize.y);*/\n\t\t\tvec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbM = texture2D(tex, fragCoord * inverseVP).xyz;\n\t\t\tvec3 luma = vec3(0.299, 0.587, 0.114);\n\t\t\tfloat lumaNW = dot(rgbNW, luma);\n\t\t\tfloat lumaNE = dot(rgbNE, luma);\n\t\t\tfloat lumaSW = dot(rgbSW, luma);\n\t\t\tfloat lumaSE = dot(rgbSE, luma);\n\t\t\tfloat lumaM = dot(rgbM, luma);\n\t\t\tfloat lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n\t\t\tfloat lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\t\t\t\n\t\t\tvec2 dir;\n\t\t\tdir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n\t\t\tdir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\t\t\t\n\t\t\tfloat dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\t\t\t\n\t\t\tfloat rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n\t\t\tdir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * inverseVP;\n\t\t\t\n\t\t\tvec3 rgbA = 0.5 * (texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + \n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz);\n\t\t\tvec3 rgbB = rgbA * 0.5 + 0.25 * (texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + \n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz);\n\t\t\t\n\t\t\t//return vec4(rgbA,1.0);\n\t\t\tfloat lumaB = dot(rgbB, luma);\n\t\t\tif ((lumaB < lumaMin) || (lumaB > lumaMax))\n\t\t\t\tcolor = vec4(rgbA, 1.0);\n\t\t\telse\n\t\t\t\tcolor = vec4(rgbB, 1.0);\n\t\t\tif(u_igamma != 1.0)\n\t\t\t\tcolor.xyz = pow( color.xyz, vec3(u_igamma) );\n\t\t\treturn color;\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\t\t}\n\t\t", -y.gamma_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_igamma;\n\t\tvoid main() {\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\t\t gl_FragColor = color;\n\t\t}\n\t\t",I.registerNodeType("texture/toviewport",y),C.title="Copy",C.desc="Copy Texture",C.widgets_info={size:{widget:"combo",values:[0,32,64,128,256,512,1024,2048]},precision:{widget:"combo", -values:c.MODE_VALUES}},C.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)){if(a){var b=a.width,d=a.height;0!=this.properties.size&&(d=b=this.properties.size);var f=this._temp_texture,g=a.type;this.properties.precision===c.LOW?g=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(g=gl.HIGH_PRECISION_FORMAT);f&&f.width==b&&f.height==d&&f.type==g||(f=gl.LINEAR,this.properties.generate_mipmaps&&isPowerOfTwo(b)&&isPowerOfTwo(d)&&(f=gl.LINEAR_MIPMAP_LINEAR), -this._temp_texture=new GL.Texture(b,d,{type:g,format:gl.RGBA,minFilter:f,magFilter:gl.LINEAR}));a.copyTo(this._temp_texture);this.properties.generate_mipmaps&&(this._temp_texture.bind(0),gl.generateMipmap(this._temp_texture.texture_type),this._temp_texture.unbind(0))}this.setOutputData(0,this._temp_texture)}},I.registerNodeType("texture/copy",C),G.title="Downsample",G.desc="Downsample Texture",G.widgets_info={iterations:{type:"number",step:1,precision:0,min:0},precision:{widget:"combo",values:c.MODE_VALUES}}, +h),D.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},D.title="Warp",D.desc="Texture warp operation",D.prototype.onExecute=function(){var a=this.getInputData(0);if(this.isOutputConnected(0))if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else{var b=this.getInputData(1),d=512,f=512;a?(d=a.width,f=a.height):b&&(d=b.width,f=b.height);this._tex=a||this._tex?c.getTargetTexture(a||this._tex,this._tex,this.properties.precision):new GL.Texture(d,f,{type:this.precision=== +c.LOW?gl.UNSIGNED_BYTE:gl.HIGH_PRECISION_FORMAT,format:gl.RGBA,filter:gl.LINEAR});var g=this._shader;g||(g=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,D.pixel_shader));d=this.getInputData(2);null!=d?this.properties.factor=d:d=parseFloat(this.properties.factor);var e=this._uniforms;e.u_factor=d;e.u_scale.set(this.properties.scale);e.u_offset.set(this.properties.offset);this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);gl.disable(gl.BLEND);a&&a.bind(0);b&&b.bind(1);var d= +Mesh.getScreenQuad();g.uniforms(e).draw(d)});this.setOutputData(0,this._tex)}},D.pixel_shader="precision highp float;\n\t\t\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tvarying vec2 v_coord;\n\t\tuniform float u_factor;\n\t\tuniform vec2 u_scale;\n\t\tuniform vec2 u_offset;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord;\n\t\t\tuv += ( texture2D(u_textureB, uv).rg - vec2(0.5)) * u_factor * u_scale + u_offset;\n\t\t\tgl_FragColor = texture2D(u_texture, uv);\n\t\t}\n\t\t", +I.registerNodeType("texture/warp",D),x.title="to Viewport",x.desc="Texture to viewport",x._prev_viewport=new Float32Array(4),x.prototype.onDrawBackground=function(a){if(!(this.flags.collapsed||40>=this.size[1])){var b=this.getInputData(0);b&&a.drawImage(a==gl?b:gl.canvas,10,30,this.size[0]-20,this.size[1]-40)}},x.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this.properties.disable_alpha?gl.disable(gl.BLEND):(gl.enable(gl.BLEND),this.properties.additive?gl.blendFunc(gl.SRC_ALPHA, +gl.ONE):gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA));gl.disable(gl.DEPTH_TEST);var b=this.properties.gamma||1;this.isInputConnected(1)&&(b=this.getInputData(1));a.setParameter(gl.TEXTURE_MAG_FILTER,this.properties.filter?gl.LINEAR:gl.NEAREST);var d=x._prev_viewport;d.set(gl.viewport_data);var c=this.properties.viewport;gl.viewport(d[0]+d[2]*c[0],d[1]+d[3]*c[1],d[2]*c[2],d[3]*c[3]);gl.getViewport();this.properties.antialiasing?(x._shader||(x._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, +x.aa_pixel_shader)),c=Mesh.getScreenQuad(),a.bind(0),x._shader.uniforms({u_texture:0,uViewportSize:[a.width,a.height],u_igamma:1/b,inverseVP:[1/a.width,1/a.height]}).draw(c)):1!=b?(x._gamma_shader||(x._gamma_shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,x.gamma_pixel_shader)),a.toViewport(x._gamma_shader,{u_texture:0,u_igamma:1/b})):a.toViewport();gl.viewport(d[0],d[1],d[2],d[3])}},x.prototype.onGetInputs=function(){return[["gamma","number"]]},x.aa_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 uViewportSize;\n\t\tuniform vec2 inverseVP;\n\t\tuniform float u_igamma;\n\t\t#define FXAA_REDUCE_MIN (1.0/ 128.0)\n\t\t#define FXAA_REDUCE_MUL (1.0 / 8.0)\n\t\t#define FXAA_SPAN_MAX 8.0\n\t\t\n\t\t/* from mitsuhiko/webgl-meincraft based on the code on geeks3d.com */\n\t\tvec4 applyFXAA(sampler2D tex, vec2 fragCoord)\n\t\t{\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t/*vec2 inverseVP = vec2(1.0 / uViewportSize.x, 1.0 / uViewportSize.y);*/\n\t\t\tvec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbM = texture2D(tex, fragCoord * inverseVP).xyz;\n\t\t\tvec3 luma = vec3(0.299, 0.587, 0.114);\n\t\t\tfloat lumaNW = dot(rgbNW, luma);\n\t\t\tfloat lumaNE = dot(rgbNE, luma);\n\t\t\tfloat lumaSW = dot(rgbSW, luma);\n\t\t\tfloat lumaSE = dot(rgbSE, luma);\n\t\t\tfloat lumaM = dot(rgbM, luma);\n\t\t\tfloat lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n\t\t\tfloat lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\t\t\t\n\t\t\tvec2 dir;\n\t\t\tdir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n\t\t\tdir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\t\t\t\n\t\t\tfloat dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\t\t\t\n\t\t\tfloat rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n\t\t\tdir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * inverseVP;\n\t\t\t\n\t\t\tvec3 rgbA = 0.5 * (texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + \n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz);\n\t\t\tvec3 rgbB = rgbA * 0.5 + 0.25 * (texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + \n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz);\n\t\t\t\n\t\t\t//return vec4(rgbA,1.0);\n\t\t\tfloat lumaB = dot(rgbB, luma);\n\t\t\tif ((lumaB < lumaMin) || (lumaB > lumaMax))\n\t\t\t\tcolor = vec4(rgbA, 1.0);\n\t\t\telse\n\t\t\t\tcolor = vec4(rgbB, 1.0);\n\t\t\tif(u_igamma != 1.0)\n\t\t\t\tcolor.xyz = pow( color.xyz, vec3(u_igamma) );\n\t\t\treturn color;\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\t\t}\n\t\t", +x.gamma_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_igamma;\n\t\tvoid main() {\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\t\t gl_FragColor = color;\n\t\t}\n\t\t",I.registerNodeType("texture/toviewport",x),B.title="Copy",B.desc="Copy Texture",B.widgets_info={size:{widget:"combo",values:[0,32,64,128,256,512,1024,2048]},precision:{widget:"combo", +values:c.MODE_VALUES}},B.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)){if(a){var b=a.width,d=a.height;0!=this.properties.size&&(d=b=this.properties.size);var f=this._temp_texture,g=a.type;this.properties.precision===c.LOW?g=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(g=gl.HIGH_PRECISION_FORMAT);f&&f.width==b&&f.height==d&&f.type==g||(f=gl.LINEAR,this.properties.generate_mipmaps&&isPowerOfTwo(b)&&isPowerOfTwo(d)&&(f=gl.LINEAR_MIPMAP_LINEAR), +this._temp_texture=new GL.Texture(b,d,{type:g,format:gl.RGBA,minFilter:f,magFilter:gl.LINEAR}));a.copyTo(this._temp_texture);this.properties.generate_mipmaps&&(this._temp_texture.bind(0),gl.generateMipmap(this._temp_texture.texture_type),this._temp_texture.unbind(0))}this.setOutputData(0,this._temp_texture)}},I.registerNodeType("texture/copy",B),G.title="Downsample",G.desc="Downsample Texture",G.widgets_info={iterations:{type:"number",step:1,precision:0,min:0},precision:{widget:"combo",values:c.MODE_VALUES}}, G.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)&&a&&a.texture_type===GL.TEXTURE_2D)if(1>this.properties.iterations)this.setOutputData(0,a);else{var b=G._shader;b||(G._shader=b=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,G.pixel_shader));var d=a.width|0,f=a.height|0,g=a.type;this.properties.precision===c.LOW?g=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(g=gl.HIGH_PRECISION_FORMAT);var e=this.properties.iterations||1,h=a,l= -null,p=[],a={type:g,format:a.format},g=vec2.create(),q={u_offset:g};this._texture&&GL.Texture.releaseTemporary(this._texture);for(var k=0;k>1||0;f=f>>1||0;l=GL.Texture.getTemporary(d,f,a);p.push(l);h.setParameter(GL.TEXTURE_MAG_FILTER,GL.NEAREST);h.copyTo(l,b,q);if(1==d&&1==f)break;h=l}this._texture=p.pop();for(k=0;k>1||0;f=f>>1||0;l=GL.Texture.getTemporary(d,f,a);r.push(l);h.setParameter(GL.TEXTURE_MAG_FILTER,GL.NEAREST);h.copyTo(l,b,k);if(1==d&&1==f)break;h=l}this._texture=r.pop();for(s=0;s>=2,c++,1!=d););a.copyTo(this._textures_chain[0]);for(var c=1;c<=this._textures_chain.length;++c)a=this._textures_chain[c];var f=A._shader,g=this._uniforms;g.u_mipmap_offset=this.properties.mipmap_offset;gl.disable(gl.DEPTH_TEST); -gl.disable(gl.BLEND);this._temp_texture.drawTo(function(){a.toViewport(f,g)})}},A.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform mat4 u_samples_a;\n\t\tuniform mat4 u_samples_b;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_mipmap_offset;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t//random average\n\t\t\tfor(int i = 0; i < 4; ++i)\n\t\t\t\tfor(int j = 0; j < 4; ++j)\n\t\t\t\t{\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t}\n\t\t gl_FragColor = color * 0.03125;\n\t\t}\n\t\t", +I.registerNodeType("texture/average",e),p.widgets_info={mode:{widget:"combo",values:["min","max","avg"]}},p.title="MinMax",p.desc="Compute the scene min max",p.prototype.onExecute=function(){this.properties.use_previous_frame||this.update();this.setOutputData(0,this._temp_texture);this.setOutputData(1,this._luminance)},p.prototype.onPreRenderExecute=function(){this.update()},p.prototype.update=function(){var a=this.getInputData(0);if(a&&(this.isOutputConnected(0)||this.isOutputConnected(1))){p._shader|| +(p._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,p.pixel_shader));var b=gl.UNSIGNED_BYTE;a.type!=b&&(b=gl.FLOAT);var d=512;if(!this._textures_chain.length||this._textures_chain[0].type!=b)for(;c&&(this._textures_chain[c]=new GL.Texture(d,d,{type:b,format:gl.RGBA,filter:gl.NEAREST}),d>>=2,c++,1!=d););a.copyTo(this._textures_chain[0]);for(var c=1;c<=this._textures_chain.length;++c)a=this._textures_chain[c];var f=p._shader,g=this._uniforms;g.u_mipmap_offset=this.properties.mipmap_offset;gl.disable(gl.DEPTH_TEST); +gl.disable(gl.BLEND);this._temp_texture.drawTo(function(){a.toViewport(f,g)})}},p.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform mat4 u_samples_a;\n\t\tuniform mat4 u_samples_b;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_mipmap_offset;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t//random average\n\t\t\tfor(int i = 0; i < 4; ++i)\n\t\t\t\tfor(int j = 0; j < 4; ++j)\n\t\t\t\t{\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t}\n\t\t gl_FragColor = color * 0.03125;\n\t\t}\n\t\t", F.title="Smooth",F.desc="Smooth texture over time",F.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){F._shader||(F._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,F.pixel_shader));var b=this._temp_texture;b&&b.type==a.type&&b.width==a.width&&b.height==a.height||(b={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(a.width,a.height,b),this._temp_texture2=new GL.Texture(a.width,a.height,b),a.copyTo(this._temp_texture2));var b= this._temp_texture,d=this._temp_texture2,c=F._shader,f=this._uniforms;f.u_factor=1-this.getInputOrProperty("factor");gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);b.drawTo(function(){d.bind(1);a.toViewport(c,f)});this.setOutputData(0,b);this._temp_texture=d;this._temp_texture2=b}},F.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_factor;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tgl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\t\t}\n\t\t", -I.registerNodeType("texture/temporal_smooth",F),t.title="Lineal Avg Smooth",t.desc="Smooth texture linearly over time",t["@samples"]={type:"number",min:1,max:64,step:1,precision:1},t.prototype.getPreviewTexture=function(){return this._temp_texture2},t.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){t._shader||(t._shader_copy=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,t.pixel_shader_copy),t._shader_avg=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,t.pixel_shader_avg)); -var b=Math.clamp(this.properties.samples,0,64),d=this.frame,c=this.properties.frames_interval;if(0==c||0==d%c){d=this._temp_texture;d&&d.type==a.type&&d.width==b||(d={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(b,1,d),this._temp_texture2=new GL.Texture(b,1,d),this._temp_texture_out=new GL.Texture(1,1,d));var f=this._temp_texture,g=this._temp_texture2,e=t._shader_copy,h=t._shader_avg,l=this._uniforms;l.u_samples=b;l.u_isamples=1/b;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST); -f.drawTo(function(){g.bind(1);a.toViewport(e,l)});this._temp_texture_out.drawTo(function(){f.toViewport(h,l)});this.setOutputData(0,this._temp_texture_out);this._temp_texture=g;this._temp_texture2=f}else this.setOutputData(0,this._temp_texture_out);this.setOutputData(1,this._temp_texture2);this.frame++}},t.pixel_shader_copy="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tif( v_coord.x <= u_isamples )\n\t\t\t\tgl_FragColor = texture2D( u_texture, vec2(0.5) );\n\t\t\telse\n\t\t\t\tgl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\t\t}\n\t\t", -t.pixel_shader_avg="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform int u_samples;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\tfor(int i = 0; i < 64; ++i)\n\t\t\t{\n\t\t\t\tcolor += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\t\t\t\tif(i == (u_samples - 1))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tgl_FragColor = color * u_isamples;\n\t\t}\n\t\t",I.registerNodeType("texture/linear_avg_smooth", -t),H.title="Image to Texture",H.desc="Uploads an image to the GPU",H.prototype.onExecute=function(){var a=this.getInputData(0);if(a){var b=a.videoWidth||a.width,d=a.videoHeight||a.height;if(a.gltexture)this.setOutputData(0,a.gltexture);else{var c=this._temp_texture;c&&c.width==b&&c.height==d||(this._temp_texture=new GL.Texture(b,d,{format:gl.RGBA,filter:gl.LINEAR}));try{this._temp_texture.uploadImage(a)}catch(f){console.error("image comes from an unsafe location, cannot be uploaded to webgl: "+f); -return}this.setOutputData(0,this._temp_texture)}}},I.registerNodeType("texture/imageToTexture",H),p.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},p.title="LUT",p.desc="Apply LUT to Texture",p.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture)); -if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.bindTexture(gl.TEXTURE_2D,null);var d=this.properties.intensity;this.isInputConnected(2)&&(this.properties.intensity=d=this.getInputData(2));this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);this._tex.drawTo(function(){b.bind(1);a.toViewport(p._shader,{u_texture:0,u_textureB:1, -u_amount:d})});this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}},p.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_amount;\n\t\t\n\t\tvoid main() {\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\t\t\t mediump vec2 quad1;\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\t\t\t mediump vec2 quad2;\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\t\t\t highp vec2 texPos1;\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t highp vec2 texPos2;\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\t\t}\n\t\t", -I.registerNodeType("texture/LUT",p),s.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},s.title="Encode",s.desc="Apply a texture atlas to encode a texture",s.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture));if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D, +I.registerNodeType("texture/temporal_smooth",F),v.title="Lineal Avg Smooth",v.desc="Smooth texture linearly over time",v["@samples"]={type:"number",min:1,max:64,step:1,precision:1},v.prototype.getPreviewTexture=function(){return this._temp_texture2},v.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){v._shader||(v._shader_copy=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,v.pixel_shader_copy),v._shader_avg=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,v.pixel_shader_avg)); +var b=Math.clamp(this.properties.samples,0,64),d=this.frame,c=this.properties.frames_interval;if(0==c||0==d%c){d=this._temp_texture;d&&d.type==a.type&&d.width==b||(d={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(b,1,d),this._temp_texture2=new GL.Texture(b,1,d),this._temp_texture_out=new GL.Texture(1,1,d));var f=this._temp_texture,g=this._temp_texture2,e=v._shader_copy,h=v._shader_avg,l=this._uniforms;l.u_samples=b;l.u_isamples=1/b;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST); +f.drawTo(function(){g.bind(1);a.toViewport(e,l)});this._temp_texture_out.drawTo(function(){f.toViewport(h,l)});this.setOutputData(0,this._temp_texture_out);this._temp_texture=g;this._temp_texture2=f}else this.setOutputData(0,this._temp_texture_out);this.setOutputData(1,this._temp_texture2);this.frame++}},v.pixel_shader_copy="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tif( v_coord.x <= u_isamples )\n\t\t\t\tgl_FragColor = texture2D( u_texture, vec2(0.5) );\n\t\t\telse\n\t\t\t\tgl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\t\t}\n\t\t", +v.pixel_shader_avg="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform int u_samples;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\tfor(int i = 0; i < 64; ++i)\n\t\t\t{\n\t\t\t\tcolor += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\t\t\t\tif(i == (u_samples - 1))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tgl_FragColor = color * u_isamples;\n\t\t}\n\t\t",I.registerNodeType("texture/linear_avg_smooth", +v),H.title="Image to Texture",H.desc="Uploads an image to the GPU",H.prototype.onExecute=function(){var a=this.getInputData(0);if(a){var b=a.videoWidth||a.width,d=a.videoHeight||a.height;if(a.gltexture)this.setOutputData(0,a.gltexture);else{var c=this._temp_texture;c&&c.width==b&&c.height==d||(this._temp_texture=new GL.Texture(b,d,{format:gl.RGBA,filter:gl.LINEAR}));try{this._temp_texture.uploadImage(a)}catch(f){console.error("image comes from an unsafe location, cannot be uploaded to webgl: "+f); +return}this.setOutputData(0,this._temp_texture)}}},I.registerNodeType("texture/imageToTexture",H),w.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},w.title="LUT",w.desc="Apply LUT to Texture",w.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture)); +if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.bindTexture(gl.TEXTURE_2D,null);var d=this.properties.intensity;this.isInputConnected(2)&&(this.properties.intensity=d=this.getInputData(2));this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);this._tex.drawTo(function(){b.bind(1);a.toViewport(w._shader,{u_texture:0,u_textureB:1, +u_amount:d})});this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}},w.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_amount;\n\t\t\n\t\tvoid main() {\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\t\t\t mediump vec2 quad1;\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\t\t\t mediump vec2 quad2;\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\t\t\t highp vec2 texPos1;\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t highp vec2 texPos2;\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\t\t}\n\t\t", +I.registerNodeType("texture/LUT",w),t.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},t.title="Encode",t.desc="Apply a texture atlas to encode a texture",t.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture));if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER,this.properties.filter?gl.LINEAR:gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,this.properties.filter?gl.LINEAR:gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.bindTexture(gl.TEXTURE_2D,null);var d=this._uniforms;d.u_row_simbols=Math.floor(this.properties.num_row_symbols);d.u_symbol_size=this.properties.symbol_size;d.u_brightness=this.properties.brightness; -d.u_invert=this.properties.invert?1:0;d.u_colorize=this.properties.colorize?1:0;this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);d.u_res[0]=this._tex.width;d.u_res[1]=this._tex.height;this._tex.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.NEAREST);this._tex.drawTo(function(){b.bind(1);a.toViewport(s._shader,d)});this.properties.generate_mipmaps&&(this._tex.bind(0),gl.generateMipmap(this._tex.texture_type), -this._tex.unbind(0));this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}},s.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_row_simbols;\n\t\tuniform float u_symbol_size;\n\t\tuniform float u_brightness;\n\t\tuniform float u_invert;\n\t\tuniform float u_colorize;\n\t\tuniform vec2 u_res;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 total_symbols = u_res / u_symbol_size;\n\t\t\tvec2 uv = floor(v_coord * total_symbols) / total_symbols; //pixelate \n\t\t\tvec2 local_uv = mod(v_coord * u_res, u_symbol_size) / u_symbol_size;\n\t\t\tlowp vec4 textureColor = texture2D(u_texture, uv );\n\t\t\tfloat lum = clamp(u_brightness * (textureColor.x + textureColor.y + textureColor.z)/3.0,0.0,1.0);\n\t\t\tif( u_invert == 1.0 ) lum = 1.0 - lum;\n\t\t\tfloat index = floor( lum * (u_row_simbols * u_row_simbols - 1.0));\n\t\t\tfloat col = mod( index, u_row_simbols );\n\t\t\tfloat row = u_row_simbols - floor( index / u_row_simbols ) - 1.0;\n\t\t\tvec2 simbol_uv = ( vec2( col, row ) + local_uv ) / u_row_simbols;\n\t\t\tvec4 color = texture2D( u_textureB, simbol_uv );\n\t\t\tif(u_colorize == 1.0)\n\t\t\t\tcolor *= textureColor;\n\t\t\tgl_FragColor = color;\n\t\t}\n\t\t", -I.registerNodeType("texture/encode",s),l.title="Texture to Channels",l.desc="Split texture channels",l.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this._channels||(this._channels=Array(4));for(var b=gl.RGB,d=0,c=0;4>c;c++)this.isOutputConnected(c)?(this._channels[c]&&this._channels[c].width==a.width&&this._channels[c].height==a.height&&this._channels[c].type==a.type&&this._channels[c].format==b||(this._channels[c]=new GL.Texture(a.width,a.height,{type:a.type,format:b,filter:gl.LINEAR})), +d.u_invert=this.properties.invert?1:0;d.u_colorize=this.properties.colorize?1:0;this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);d.u_res[0]=this._tex.width;d.u_res[1]=this._tex.height;this._tex.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.NEAREST);this._tex.drawTo(function(){b.bind(1);a.toViewport(t._shader,d)});this.properties.generate_mipmaps&&(this._tex.bind(0),gl.generateMipmap(this._tex.texture_type), +this._tex.unbind(0));this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}},t.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_row_simbols;\n\t\tuniform float u_symbol_size;\n\t\tuniform float u_brightness;\n\t\tuniform float u_invert;\n\t\tuniform float u_colorize;\n\t\tuniform vec2 u_res;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 total_symbols = u_res / u_symbol_size;\n\t\t\tvec2 uv = floor(v_coord * total_symbols) / total_symbols; //pixelate \n\t\t\tvec2 local_uv = mod(v_coord * u_res, u_symbol_size) / u_symbol_size;\n\t\t\tlowp vec4 textureColor = texture2D(u_texture, uv );\n\t\t\tfloat lum = clamp(u_brightness * (textureColor.x + textureColor.y + textureColor.z)/3.0,0.0,1.0);\n\t\t\tif( u_invert == 1.0 ) lum = 1.0 - lum;\n\t\t\tfloat index = floor( lum * (u_row_simbols * u_row_simbols - 1.0));\n\t\t\tfloat col = mod( index, u_row_simbols );\n\t\t\tfloat row = u_row_simbols - floor( index / u_row_simbols ) - 1.0;\n\t\t\tvec2 simbol_uv = ( vec2( col, row ) + local_uv ) / u_row_simbols;\n\t\t\tvec4 color = texture2D( u_textureB, simbol_uv );\n\t\t\tif(u_colorize == 1.0)\n\t\t\t\tcolor *= textureColor;\n\t\t\tgl_FragColor = color;\n\t\t}\n\t\t", +I.registerNodeType("texture/encode",t),l.title="Texture to Channels",l.desc="Split texture channels",l.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this._channels||(this._channels=Array(4));for(var b=gl.RGB,d=0,c=0;4>c;c++)this.isOutputConnected(c)?(this._channels[c]&&this._channels[c].width==a.width&&this._channels[c].height==a.height&&this._channels[c].type==a.type&&this._channels[c].format==b||(this._channels[c]=new GL.Texture(a.width,a.height,{type:a.type,format:b,filter:gl.LINEAR})), d++):this._channels[c]=null;if(d){gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);for(var f=Mesh.getScreenQuad(),g=l._shader,e=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],c=0;4>c;c++)this._channels[c]&&(this._channels[c].drawTo(function(){a.bind(0);g.uniforms({u_texture:0,u_mask:e[c]}).draw(f)}),this.setOutputData(c,this._channels[c]))}}},l.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec4 u_mask;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\t\t}\n\t\t", I.registerNodeType("texture/textureChannels",l),a.title="Channels to Texture",a.desc="Split texture channels",a.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},a.prototype.onExecute=function(){var b=c.getWhiteTexture(),d=this.getInputData(0)||b,f=this.getInputData(1)||b,g=this.getInputData(2)||b,e=this.getInputData(3)||b;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var h=Mesh.getScreenQuad();a._shader||(a._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,a.pixel_shader));var l=a._shader, -b=Math.max(d.width,f.width,g.width,e.width),p=Math.max(d.height,f.height,g.height,e.height),q=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._texture&&this._texture.width==b&&this._texture.height==p&&this._texture.type==q||(this._texture=new GL.Texture(b,p,{type:q,format:gl.RGBA,filter:gl.LINEAR}));b=this._color;b[0]=this.properties.R;b[1]=this.properties.G;b[2]=this.properties.B;b[3]=this.properties.A;var k=this._uniforms;this._texture.drawTo(function(){d.bind(0); -f.bind(1);g.bind(2);e.bind(3);l.uniforms(k).draw(h)});this.setOutputData(0,this._texture)},a.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureR;\n\t\tuniform sampler2D u_textureG;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform vec4 u_color;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = u_color * vec4( \t\t\t\t\ttexture2D(u_textureR, v_coord).r,\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\t\t}\n\t\t", +b=Math.max(d.width,f.width,g.width,e.width),r=Math.max(d.height,f.height,g.height,e.height),k=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._texture&&this._texture.width==b&&this._texture.height==r&&this._texture.type==k||(this._texture=new GL.Texture(b,r,{type:k,format:gl.RGBA,filter:gl.LINEAR}));b=this._color;b[0]=this.properties.R;b[1]=this.properties.G;b[2]=this.properties.B;b[3]=this.properties.A;var s=this._uniforms;this._texture.drawTo(function(){d.bind(0); +f.bind(1);g.bind(2);e.bind(3);l.uniforms(s).draw(h)});this.setOutputData(0,this._texture)},a.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureR;\n\t\tuniform sampler2D u_textureG;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform vec4 u_color;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = u_color * vec4( \t\t\t\t\ttexture2D(u_textureR, v_coord).r,\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\t\t}\n\t\t", I.registerNodeType("texture/channelsTexture",a),b.title="Color",b.desc="Generates a 1x1 texture with a constant color",b.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},b.prototype.onDrawBackground=function(a){var b=this.properties.color;a.fillStyle="rgb("+Math.floor(255*Math.clamp(b[0],0,1))+","+Math.floor(255*Math.clamp(b[1],0,1))+","+Math.floor(255*Math.clamp(b[2],0,1))+")";this.flags.collapsed?this.boxcolor=a.fillStyle:a.fillRect(0,0,this.size[0],this.size[1])},b.prototype.onExecute= function(){var a=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._tex&&this._tex.type==a||(this._tex=new GL.Texture(1,1,{format:gl.RGBA,type:a,minFilter:gl.NEAREST}));a=this.properties.color;if(this.inputs)for(var b=0;ba.width?b: -a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad(),h=null,l=this._uniforms;d?(h=f._shader_tex,h||(h=f._shader_tex=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,f.pixel_shader,{MIX_TEX:""}))):(h=f._shader_factor,h||(h=f._shader_factor=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,f.pixel_shader)),g=null==g?this.properties.factor:g,l.u_mix.set([g,g,g,g]));var p=this.properties.invert;this._tex.drawTo(function(){a.bind(p?1:0);b.bind(p?0:1);d&&d.bind(2); +a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad(),h=null,l=this._uniforms;d?(h=f._shader_tex,h||(h=f._shader_tex=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,f.pixel_shader,{MIX_TEX:""}))):(h=f._shader_factor,h||(h=f._shader_factor=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,f.pixel_shader)),g=null==g?this.properties.factor:g,l.u_mix.set([g,g,g,g]));var r=this.properties.invert;this._tex.drawTo(function(){a.bind(r?1:0);b.bind(r?0:1);d&&d.bind(2); h.uniforms(l).draw(e)});this.setOutputData(0,this._tex)}}},f.prototype.onGetInputs=function(){return[["factor","number"]]},f.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform sampler2D u_textureB;\n\t\t#ifdef MIX_TEX\n\t\t\tuniform sampler2D u_textureMix;\n\t\t#else\n\t\t\tuniform vec4 u_mix;\n\t\t#endif\n\t\t\n\t\tvoid main() {\n\t\t\t#ifdef MIX_TEX\n\t\t\t vec4 f = texture2D(u_textureMix, v_coord);\n\t\t\t#else\n\t\t\t vec4 f = u_mix;\n\t\t\t#endif\n\t\t gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\t\t}\n\t\t", I.registerNodeType("texture/mix",f),g.title="Edges",g.desc="Detects edges",g.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},g.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else if(a){this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var b=Mesh.getScreenQuad(),d=g._shader,f=this.properties.invert,e=this.properties.factor, h=this.properties.threshold?1:0;this._tex.drawTo(function(){a.bind(0);d.uniforms({u_texture:0,u_isize:[1/a.width,1/a.height],u_factor:e,u_threshold:h,u_invert:f?1:0}).draw(b)});this.setOutputData(0,this._tex)}}},g.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_isize;\n\t\tuniform int u_invert;\n\t\tuniform float u_factor;\n\t\tuniform float u_threshold;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 center = texture2D(u_texture, v_coord);\n\t\t\tvec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\t\t\tvec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\t\t\tvec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\t\t\tvec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\t\t\tvec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\t\t\tdiff *= u_factor;\n\t\t\tif(u_invert == 1)\n\t\t\t\tdiff.xyz = vec3(1.0) - diff.xyz;\n\t\t\tif( u_threshold == 0.0 )\n\t\t\t\tgl_FragColor = vec4( diff.xyz, center.a );\n\t\t\telse\n\t\t\t\tgl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\t\t}\n\t\t", -I.registerNodeType("texture/edges",g),r.title="Depth Range",r.desc="Generates a texture with a depth range",r.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a){var b=gl.UNSIGNED_BYTE;this.properties.high_precision&&(b=gl.half_float_ext?gl.HALF_FLOAT_OES:gl.FLOAT);this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&&this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGBA, -filter:gl.LINEAR}));var d=this._uniforms,b=this.properties.distance;this.isInputConnected(1)&&(b=this.getInputData(1),this.properties.distance=b);var c=this.properties.range;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.range=c);d.u_distance=b;d.u_range=c;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var f=Mesh.getScreenQuad();r._shader||(r._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,r.pixel_shader),r._shader_onlydepth=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,r.pixel_shader, -{ONLY_DEPTH:""}));var g=this.properties.only_depth?r._shader_onlydepth:r._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1,1E3];d.u_camera_planes=b;this._temp_texture.drawTo(function(){a.bind(0);g.uniforms(d).draw(f)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},r.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform float u_distance;\n\t\tuniform float u_range;\n\t\t\n\t\tfloat LinearDepth()\n\t\t{\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\t\t\tdepth = depth * 2.0 - 1.0;\n\t\t\treturn zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t\tfloat depth = LinearDepth();\n\t\t\t#ifdef ONLY_DEPTH\n\t\t\t gl_FragColor = vec4(depth);\n\t\t\t#else\n\t\t\t\tfloat diff = abs(depth * u_camera_planes.y - u_distance);\n\t\t\t\tfloat dof = 1.0;\n\t\t\t\tif(diff <= u_range)\n\t\t\t\t\tdof = diff / u_range;\n\t\t\t gl_FragColor = vec4(dof);\n\t\t\t#endif\n\t\t}\n\t\t", -I.registerNodeType("texture/depth_range",r),x.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},x.title="Linear Depth",x.desc="Creates a color texture with linear depth",x.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a&&(a.format==gl.DEPTH_COMPONENT||a.format==gl.DEPTH_STENCIL)){var b=this.properties.precision==c.HIGH?gl.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&& -this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGB,filter:gl.LINEAR}));var d=this._uniforms;d.u_invert=this.properties.invert?1:0;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var f=Mesh.getScreenQuad();x._shader||(x._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,x.pixel_shader));var g=x._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1, -1E3];d.u_camera_planes=b;d.u_ires.set([0,0]);this._temp_texture.drawTo(function(){a.bind(0);g.uniforms(d).draw(f)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},x.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform int u_invert;\n\t\tuniform vec2 u_ires;\n\t\t\n\t\tvoid main() {\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord + u_ires*0.5).x * 2.0 - 1.0;\n\t\t\tfloat f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t\tif( u_invert == 1 )\n\t\t\t\tf = 1.0 - f;\n\t\t\tgl_FragColor = vec4(vec3(f),1.0);\n\t\t}\n\t\t", -I.registerNodeType("texture/linear_depth",x),K.title="Blur",K.desc="Blur a texture",K.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},K.max_iterations=20,K.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._final_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(b=this._final_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));var d=this.properties.iterations;this.isInputConnected(1)&& +I.registerNodeType("texture/edges",g),q.title="Depth Range",q.desc="Generates a texture with a depth range",q.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a){var b=gl.UNSIGNED_BYTE;this.properties.high_precision&&(b=gl.half_float_ext?gl.HALF_FLOAT_OES:gl.FLOAT);this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&&this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGBA, +filter:gl.LINEAR}));var d=this._uniforms,b=this.properties.distance;this.isInputConnected(1)&&(b=this.getInputData(1),this.properties.distance=b);var c=this.properties.range;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.range=c);d.u_distance=b;d.u_range=c;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var f=Mesh.getScreenQuad();q._shader||(q._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,q.pixel_shader),q._shader_onlydepth=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,q.pixel_shader, +{ONLY_DEPTH:""}));var g=this.properties.only_depth?q._shader_onlydepth:q._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1,1E3];d.u_camera_planes=b;this._temp_texture.drawTo(function(){a.bind(0);g.uniforms(d).draw(f)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},q.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform float u_distance;\n\t\tuniform float u_range;\n\t\t\n\t\tfloat LinearDepth()\n\t\t{\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\t\t\tdepth = depth * 2.0 - 1.0;\n\t\t\treturn zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t\tfloat depth = LinearDepth();\n\t\t\t#ifdef ONLY_DEPTH\n\t\t\t gl_FragColor = vec4(depth);\n\t\t\t#else\n\t\t\t\tfloat diff = abs(depth * u_camera_planes.y - u_distance);\n\t\t\t\tfloat dof = 1.0;\n\t\t\t\tif(diff <= u_range)\n\t\t\t\t\tdof = diff / u_range;\n\t\t\t gl_FragColor = vec4(dof);\n\t\t\t#endif\n\t\t}\n\t\t", +I.registerNodeType("texture/depth_range",q),A.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},A.title="Linear Depth",A.desc="Creates a color texture with linear depth",A.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a&&(a.format==gl.DEPTH_COMPONENT||a.format==gl.DEPTH_STENCIL)){var b=this.properties.precision==c.HIGH?gl.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&& +this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGB,filter:gl.LINEAR}));var d=this._uniforms;d.u_invert=this.properties.invert?1:0;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var f=Mesh.getScreenQuad();A._shader||(A._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,A.pixel_shader));var g=A._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1, +1E3];d.u_camera_planes=b;d.u_ires.set([0,0]);this._temp_texture.drawTo(function(){a.bind(0);g.uniforms(d).draw(f)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},A.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform int u_invert;\n\t\tuniform vec2 u_ires;\n\t\t\n\t\tvoid main() {\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord + u_ires*0.5).x * 2.0 - 1.0;\n\t\t\tfloat f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t\tif( u_invert == 1 )\n\t\t\t\tf = 1.0 - f;\n\t\t\tgl_FragColor = vec4(vec3(f),1.0);\n\t\t}\n\t\t", +I.registerNodeType("texture/linear_depth",A),K.title="Blur",K.desc="Blur a texture",K.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},K.max_iterations=20,K.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._final_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(b=this._final_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));var d=this.properties.iterations;this.isInputConnected(1)&& (d=this.getInputData(1),this.properties.iterations=d);d=Math.min(Math.floor(d),K.max_iterations);if(0==d)this.setOutputData(0,a);else{var c=this.properties.intensity;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.intensity=c);var f=I.camera_aspect;f||void 0===window.gl||(f=gl.canvas.height/gl.canvas.width);f||(f=1);var f=this.properties.preserve_aspect?f:1,g=this.properties.scale||[1,1];a.applyBlur(f*g[0],g[1],c,b);for(a=1;a>=1;1<(d|0)&&(d>>=1);if(2>b)break;p=h[r]=GL.Texture.getTemporary(b,d,f);s[0]=1/k.width;s[1]=1/k.height;k.blit(p,l.uniforms(e));k=p}this.isOutputConnected(2)&&(b=this._average_texture,b&&b.type==a.type&&b.format==a.format||(b=this._average_texture=new GL.Texture(1,1,{type:a.type,format:a.format,filter:gl.LINEAR})),s[0]=1/k.width,s[1]=1/k.height,e.u_intensity=u,e.u_delta=1,k.blit(b,l.uniforms(e)),this.setOutputData(2,b));gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE);e.u_intensity= -this.getInputOrProperty("persistence");e.u_delta=0.5;for(r-=2;0<=r;r--)p=h[r],h[r]=null,s[0]=1/k.width,s[1]=1/k.height,k.blit(p,l.uniforms(e)),GL.Texture.releaseTemporary(k),k=p;gl.disable(gl.BLEND);this.isOutputConnected(1)&&(h=this._glow_texture,h&&h.width==a.width&&h.height==a.height&&h.type==g&&h.format==a.format||(h=this._glow_texture=new GL.Texture(a.width,a.height,{type:g,format:a.format,filter:gl.LINEAR})),k.blit(h),this.setOutputData(1,h));if(this.isOutputConnected(0)){h=this._final_texture; -h&&h.width==a.width&&h.height==a.height&&h.type==g&&h.format==a.format||(h=this._final_texture=new GL.Texture(a.width,a.height,{type:g,format:a.format,filter:gl.LINEAR}));var w=this.getInputData(1),m=this.getInputOrProperty("dirt_factor");e.u_intensity=u;l=w?q._dirt_final_shader:q._final_shader;l||(l=w?q._dirt_final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,q.final_pixel_shader,{USE_DIRT:""}):q._final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,q.final_pixel_shader));h.drawTo(function(){a.bind(0); -k.bind(1);w&&(l.setUniform("u_dirt_factor",m),l.setUniform("u_dirt_texture",w.bind(2)));l.toViewport(e)});this.setOutputData(0,h)}GL.Texture.releaseTemporary(k)}},q.cut_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform float u_threshold;\n\tvoid main() {\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\t}",q.scale_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\t}", -q.final_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform sampler2D u_glow_texture;\n\t#ifdef USE_DIRT\n\t\tuniform sampler2D u_dirt_texture;\n\t#endif\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\tuniform float u_dirt_factor;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tvec4 glow = sampleBox( v_coord );\n\t\t#ifdef USE_DIRT\n\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\t\t#endif\n\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\t}", -I.registerNodeType("texture/glow",q),z.title="Kuwahara Filter",z.desc="Filters a texture giving an artistic oil canvas painting",z.max_radius=10,z._shaders=[],z.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));b=this.properties.radius;b=Math.min(Math.floor(b),z.max_radius);if(0== -b)this.setOutputData(0,a);else{var d=this.properties.intensity,c=I.camera_aspect;c||void 0===window.gl||(c=gl.canvas.height/gl.canvas.width);c||(c=1);c=this.properties.preserve_aspect?c:1;z._shaders[b]||(z._shaders[b]=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,z.pixel_shader,{RADIUS:b.toFixed(0)}));var f=z._shaders[b],g=GL.Mesh.getScreenQuad();a.bind(0);this._temp_texture.drawTo(function(){f.uniforms({u_texture:0,u_intensity:d,u_resolution:[a.width,a.height],u_iResolution:[1/a.width,1/a.height]}).draw(g)}); -this.setOutputData(0,this._temp_texture)}}},z.pixel_shader="\nprecision highp float;\nvarying vec2 v_coord;\nuniform sampler2D u_texture;\nuniform float u_intensity;\nuniform vec2 u_resolution;\nuniform vec2 u_iResolution;\n#ifndef RADIUS\n\t#define RADIUS 7\n#endif\nvoid main() {\n\n\tconst int radius = RADIUS;\n\tvec2 fragCoord = v_coord;\n\tvec2 src_size = u_iResolution;\n\tvec2 uv = v_coord;\n\tfloat n = float((radius + 1) * (radius + 1));\n\tint i;\n\tint j;\n\tvec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\tvec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\tvec3 c;\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm0 += c;\n\t\t\ts0 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm1 += c;\n\t\t\ts1 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm2 += c;\n\t\t\ts2 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm3 += c;\n\t\t\ts3 += c * c;\n\t\t}\n\t}\n\t\n\tfloat min_sigma2 = 1e+2;\n\tm0 /= n;\n\ts0 = abs(s0 / n - m0 * m0);\n\t\n\tfloat sigma2 = s0.r + s0.g + s0.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m0, 1.0);\n\t}\n\t\n\tm1 /= n;\n\ts1 = abs(s1 / n - m1 * m1);\n\t\n\tsigma2 = s1.r + s1.g + s1.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m1, 1.0);\n\t}\n\t\n\tm2 /= n;\n\ts2 = abs(s2 / n - m2 * m2);\n\t\n\tsigma2 = s2.r + s2.g + s2.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m2, 1.0);\n\t}\n\t\n\tm3 /= n;\n\ts3 = abs(s3 / n - m3 * m3);\n\t\n\tsigma2 = s3.r + s3.g + s3.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m3, 1.0);\n\t}\n}\n", -I.registerNodeType("texture/kuwahara",z),w.title="XDoG Filter",w.desc="Filters a texture giving an artistic ink style",w.max_radius=10,w._shaders=[],w.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));w._xdog_shader||(w._xdog_shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,w.xdog_pixel_shader)); -var d=w._xdog_shader,c=GL.Mesh.getScreenQuad(),f=this.properties.sigma,g=this.properties.k,e=this.properties.p,h=this.properties.epsilon,l=this.properties.phi;a.bind(0);this._temp_texture.drawTo(function(){d.uniforms({src:0,sigma:f,k:g,p:e,epsilon:h,phi:l,cvsWidth:a.width,cvsHeight:a.height}).draw(c)});this.setOutputData(0,this._temp_texture)}},w.xdog_pixel_shader="\nprecision highp float;\nuniform sampler2D src;\n\nuniform float cvsHeight;\nuniform float cvsWidth;\n\nuniform float sigma;\nuniform float k;\nuniform float p;\nuniform float epsilon;\nuniform float phi;\nvarying vec2 v_coord;\n\nfloat cosh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat cosH = (tmp + 1.0 / tmp) / 2.0;\n\treturn cosH;\n}\n\nfloat tanh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\treturn tanH;\n}\n\nfloat sinh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat sinH = (tmp - 1.0 / tmp) / 2.0;\n\treturn sinH;\n}\n\nvoid main(void){\n\tvec3 destColor = vec3(0.0);\n\tfloat tFrag = 1.0 / cvsHeight;\n\tfloat sFrag = 1.0 / cvsWidth;\n\tvec2 Frag = vec2(sFrag,tFrag);\n\tvec2 uv = gl_FragCoord.st;\n\tfloat twoSigmaESquared = 2.0 * sigma * sigma;\n\tfloat twoSigmaRSquared = twoSigmaESquared * k * k;\n\tint halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\tconst int MAX_NUM_ITERATION = 99999;\n\tvec2 sum = vec2(0.0);\n\tvec2 norm = vec2(0.0);\n\n\tfor(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\t\tint i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\t\tint j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\t\tfloat d = length(vec2(i,j));\n\t\tvec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\t\t\t\t\t\t\texp( -d * d / twoSigmaRSquared ));\n\n\t\tvec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\t\tnorm += kernel;\n\t\tsum += kernel * L;\n\t}\n\n\tsum /= norm;\n\n\tfloat H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\tfloat edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\tdestColor = vec3(edge);\n\tgl_FragColor = vec4(destColor, 1.0);\n}", -I.registerNodeType("texture/xDoG",w),u.title="Webcam",u.desc="Webcam texture",u.is_webcam_open=!1,u.prototype.openStream=function(){function a(d){u.is_webcam_open=!1;console.log("Webcam rejected",d);b._webcam_stream=!1;b.boxcolor="red";b.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](a);var b=this}},u.prototype.closeStream=function(){if(this._webcam_stream){var a= +b)}}},I.registerNodeType("texture/blur",K),r.title="Glow",r.desc="Filters a texture giving it a glow effect",r.weights=new Float32Array([0.5,0.4,0.3,0.2]),r.widgets_info={iterations:{type:"number",min:0,max:16,step:1,precision:0},threshold:{type:"number",min:0,max:10,step:0.01,precision:2},precision:{widget:"combo",values:c.MODE_VALUES}},r.prototype.onGetInputs=function(){return[["enabled","boolean"],["threshold","number"],["intensity","number"],["persistence","number"],["iterations","number"],["dirt_factor", +"number"]]},r.prototype.onGetOutputs=function(){return[["average","Texture"]]},r.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isAnyOutputConnected())if(this.properties.precision===c.PASS_THROUGH||!1===this.getInputOrProperty("enabled"))this.setOutputData(0,a);else{var b=a.width,d=a.height,f={format:a.format,type:a.type,minFilter:GL.LINEAR,magFilter:GL.LINEAR,wrap:gl.CLAMP_TO_EDGE},g=c.getTextureType(this.properties.precision,a),e=this._uniforms,h=this._textures,l=r._cut_shader; +l||(l=r._cut_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,r.cut_pixel_shader));gl.disable(gl.DEPTH_TEST);gl.disable(gl.BLEND);e.u_threshold=this.getInputOrProperty("threshold");var k=h[0]=GL.Texture.getTemporary(b,d,f);a.blit(k,l.uniforms(e));var s=k,w=this.getInputOrProperty("iterations"),w=Math.clamp(w,1,16)|0,t=e.u_texel_size,q=this.getInputOrProperty("intensity");e.u_intensity=1;e.u_delta=this.properties.scale;l=r._shader;l||(l=r._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,r.scale_pixel_shader)); +for(var u=1;u>=1;1<(d|0)&&(d>>=1);if(2>b)break;k=h[u]=GL.Texture.getTemporary(b,d,f);t[0]=1/s.width;t[1]=1/s.height;s.blit(k,l.uniforms(e));s=k}this.isOutputConnected(2)&&(b=this._average_texture,b&&b.type==a.type&&b.format==a.format||(b=this._average_texture=new GL.Texture(1,1,{type:a.type,format:a.format,filter:gl.LINEAR})),t[0]=1/s.width,t[1]=1/s.height,e.u_intensity=q,e.u_delta=1,s.blit(b,l.uniforms(e)),this.setOutputData(2,b));gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE);e.u_intensity= +this.getInputOrProperty("persistence");e.u_delta=0.5;for(u-=2;0<=u;u--)k=h[u],h[u]=null,t[0]=1/s.width,t[1]=1/s.height,s.blit(k,l.uniforms(e)),GL.Texture.releaseTemporary(s),s=k;gl.disable(gl.BLEND);this.isOutputConnected(1)&&(h=this._glow_texture,h&&h.width==a.width&&h.height==a.height&&h.type==g&&h.format==a.format||(h=this._glow_texture=new GL.Texture(a.width,a.height,{type:g,format:a.format,filter:gl.LINEAR})),s.blit(h),this.setOutputData(1,h));if(this.isOutputConnected(0)){h=this._final_texture; +h&&h.width==a.width&&h.height==a.height&&h.type==g&&h.format==a.format||(h=this._final_texture=new GL.Texture(a.width,a.height,{type:g,format:a.format,filter:gl.LINEAR}));var z=this.getInputData(1),m=this.getInputOrProperty("dirt_factor");e.u_intensity=q;l=z?r._dirt_final_shader:r._final_shader;l||(l=z?r._dirt_final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,r.final_pixel_shader,{USE_DIRT:""}):r._final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,r.final_pixel_shader));h.drawTo(function(){a.bind(0); +s.bind(1);z&&(l.setUniform("u_dirt_factor",m),l.setUniform("u_dirt_texture",z.bind(2)));l.toViewport(e)});this.setOutputData(0,h)}GL.Texture.releaseTemporary(s)}},r.cut_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform float u_threshold;\n\tvoid main() {\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\t}",r.scale_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\t}", +r.final_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform sampler2D u_glow_texture;\n\t#ifdef USE_DIRT\n\t\tuniform sampler2D u_dirt_texture;\n\t#endif\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\tuniform float u_dirt_factor;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tvec4 glow = sampleBox( v_coord );\n\t\t#ifdef USE_DIRT\n\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\t\t#endif\n\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\t}", +I.registerNodeType("texture/glow",r),s.title="Kuwahara Filter",s.desc="Filters a texture giving an artistic oil canvas painting",s.max_radius=10,s._shaders=[],s.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));b=this.properties.radius;b=Math.min(Math.floor(b),s.max_radius);if(0== +b)this.setOutputData(0,a);else{var d=this.properties.intensity,c=I.camera_aspect;c||void 0===window.gl||(c=gl.canvas.height/gl.canvas.width);c||(c=1);c=this.properties.preserve_aspect?c:1;s._shaders[b]||(s._shaders[b]=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,s.pixel_shader,{RADIUS:b.toFixed(0)}));var f=s._shaders[b],g=GL.Mesh.getScreenQuad();a.bind(0);this._temp_texture.drawTo(function(){f.uniforms({u_texture:0,u_intensity:d,u_resolution:[a.width,a.height],u_iResolution:[1/a.width,1/a.height]}).draw(g)}); +this.setOutputData(0,this._temp_texture)}}},s.pixel_shader="\nprecision highp float;\nvarying vec2 v_coord;\nuniform sampler2D u_texture;\nuniform float u_intensity;\nuniform vec2 u_resolution;\nuniform vec2 u_iResolution;\n#ifndef RADIUS\n\t#define RADIUS 7\n#endif\nvoid main() {\n\n\tconst int radius = RADIUS;\n\tvec2 fragCoord = v_coord;\n\tvec2 src_size = u_iResolution;\n\tvec2 uv = v_coord;\n\tfloat n = float((radius + 1) * (radius + 1));\n\tint i;\n\tint j;\n\tvec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\tvec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\tvec3 c;\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm0 += c;\n\t\t\ts0 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm1 += c;\n\t\t\ts1 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm2 += c;\n\t\t\ts2 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm3 += c;\n\t\t\ts3 += c * c;\n\t\t}\n\t}\n\t\n\tfloat min_sigma2 = 1e+2;\n\tm0 /= n;\n\ts0 = abs(s0 / n - m0 * m0);\n\t\n\tfloat sigma2 = s0.r + s0.g + s0.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m0, 1.0);\n\t}\n\t\n\tm1 /= n;\n\ts1 = abs(s1 / n - m1 * m1);\n\t\n\tsigma2 = s1.r + s1.g + s1.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m1, 1.0);\n\t}\n\t\n\tm2 /= n;\n\ts2 = abs(s2 / n - m2 * m2);\n\t\n\tsigma2 = s2.r + s2.g + s2.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m2, 1.0);\n\t}\n\t\n\tm3 /= n;\n\ts3 = abs(s3 / n - m3 * m3);\n\t\n\tsigma2 = s3.r + s3.g + s3.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m3, 1.0);\n\t}\n}\n", +I.registerNodeType("texture/kuwahara",s),z.title="XDoG Filter",z.desc="Filters a texture giving an artistic ink style",z.max_radius=10,z._shaders=[],z.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));z._xdog_shader||(z._xdog_shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,z.xdog_pixel_shader)); +var d=z._xdog_shader,c=GL.Mesh.getScreenQuad(),f=this.properties.sigma,g=this.properties.k,e=this.properties.p,h=this.properties.epsilon,l=this.properties.phi;a.bind(0);this._temp_texture.drawTo(function(){d.uniforms({src:0,sigma:f,k:g,p:e,epsilon:h,phi:l,cvsWidth:a.width,cvsHeight:a.height}).draw(c)});this.setOutputData(0,this._temp_texture)}},z.xdog_pixel_shader="\nprecision highp float;\nuniform sampler2D src;\n\nuniform float cvsHeight;\nuniform float cvsWidth;\n\nuniform float sigma;\nuniform float k;\nuniform float p;\nuniform float epsilon;\nuniform float phi;\nvarying vec2 v_coord;\n\nfloat cosh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat cosH = (tmp + 1.0 / tmp) / 2.0;\n\treturn cosH;\n}\n\nfloat tanh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\treturn tanH;\n}\n\nfloat sinh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat sinH = (tmp - 1.0 / tmp) / 2.0;\n\treturn sinH;\n}\n\nvoid main(void){\n\tvec3 destColor = vec3(0.0);\n\tfloat tFrag = 1.0 / cvsHeight;\n\tfloat sFrag = 1.0 / cvsWidth;\n\tvec2 Frag = vec2(sFrag,tFrag);\n\tvec2 uv = gl_FragCoord.st;\n\tfloat twoSigmaESquared = 2.0 * sigma * sigma;\n\tfloat twoSigmaRSquared = twoSigmaESquared * k * k;\n\tint halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\tconst int MAX_NUM_ITERATION = 99999;\n\tvec2 sum = vec2(0.0);\n\tvec2 norm = vec2(0.0);\n\n\tfor(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\t\tint i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\t\tint j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\t\tfloat d = length(vec2(i,j));\n\t\tvec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\t\t\t\t\t\t\texp( -d * d / twoSigmaRSquared ));\n\n\t\tvec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\t\tnorm += kernel;\n\t\tsum += kernel * L;\n\t}\n\n\tsum /= norm;\n\n\tfloat H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\tfloat edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\tdestColor = vec3(edge);\n\tgl_FragColor = vec4(destColor, 1.0);\n}", +I.registerNodeType("texture/xDoG",z),u.title="Webcam",u.desc="Webcam texture",u.is_webcam_open=!1,u.prototype.openStream=function(){function a(d){u.is_webcam_open=!1;console.log("Webcam rejected",d);b._webcam_stream=!1;b.boxcolor="red";b.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](a);var b=this}},u.prototype.closeStream=function(){if(this._webcam_stream){var a= this._webcam_stream.getTracks();if(a.length)for(var b=0;b=this.size[1]||!this._video||(a.save(),a.webgl?this._video_texture&&a.drawImage(this._video_texture,0,0,this.size[0],this.size[1]):a.drawImage(this._video, 0,0,this.size[0],this.size[1]),a.restore())},u.prototype.onExecute=function(){null!=this._webcam_stream||this._waiting_confirmation||this.openStream();if(this._video&&this._video.videoWidth){var a=this._video.videoWidth,b=this._video.videoHeight,d=this._video_texture;d&&d.width==a&&d.height==b||(this._video_texture=new GL.Texture(a,b,{format:gl.RGB,filter:gl.LINEAR}));this._video_texture.uploadImage(this._video);this._video_texture.version=++this.version;this.properties.texture_name&&(c.getTexturesContainer()[this.properties.texture_name]= @@ -583,128 +584,128 @@ I.registerNodeType("texture/exposition",P),L.title="Tone Mapping",L.desc="Applie b.height==a.height&&b.type==a.type||(b=this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));var d=this.getInputData(1);null==d&&(d=this.properties.average_lum);var f=this._uniforms,g=null;d.constructor===Number?(this.properties.average_lum=d,f.u_average_lum=this.properties.average_lum,g=L._shader,g||(g=L._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,L.pixel_shader))):d.constructor===GL.Texture&&(f.u_average_texture=d.bind(1),g=L._shader_texture, g||(g=L._shader_texture=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,L.pixel_shader,{AVG_TEXTURE:""})));f.u_lumwhite2=this.properties.lum_white*this.properties.lum_white;f.u_scale=this.properties.scale;f.u_igamma=1/this.properties.gamma;gl.disable(gl.DEPTH_TEST);b.drawTo(function(){a.bind(0);g.uniforms(f).draw(GL.Mesh.getScreenQuad())});this.setOutputData(0,this._temp_texture)}},L.pixel_shader="precision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_scale;\n\t\t#ifdef AVG_TEXTURE\n\t\t\tuniform sampler2D u_average_texture;\n\t\t#else\n\t\t\tuniform float u_average_lum;\n\t\t#endif\n\t\tuniform float u_lumwhite2;\n\t\tuniform float u_igamma;\n\t\tvec3 RGB2xyY (vec3 rgb)\n\t\t{\n\t\t\t const mat3 RGB2XYZ = mat3(0.4124, 0.3576, 0.1805,\n\t\t\t\t\t\t\t\t\t 0.2126, 0.7152, 0.0722,\n\t\t\t\t\t\t\t\t\t 0.0193, 0.1192, 0.9505);\n\t\t\tvec3 XYZ = RGB2XYZ * rgb;\n\t\t\t\n\t\t\tfloat f = (XYZ.x + XYZ.y + XYZ.z);\n\t\t\treturn vec3(XYZ.x / f,\n\t\t\t\t\t\tXYZ.y / f,\n\t\t\t\t\t\tXYZ.y);\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = texture2D( u_texture, v_coord );\n\t\t\tvec3 rgb = color.xyz;\n\t\t\tfloat average_lum = 0.0;\n\t\t\t#ifdef AVG_TEXTURE\n\t\t\t\tvec3 pixel = texture2D(u_average_texture,vec2(0.5)).xyz;\n\t\t\t\taverage_lum = (pixel.x + pixel.y + pixel.z) / 3.0;\n\t\t\t#else\n\t\t\t\taverage_lum = u_average_lum;\n\t\t\t#endif\n\t\t\t//Ld - this part of the code is the same for both versions\n\t\t\tfloat lum = dot(rgb, vec3(0.2126, 0.7152, 0.0722));\n\t\t\tfloat L = (u_scale / average_lum) * lum;\n\t\t\tfloat Ld = (L * (1.0 + L / u_lumwhite2)) / (1.0 + L);\n\t\t\t//first\n\t\t\t//vec3 xyY = RGB2xyY(rgb);\n\t\t\t//xyY.z *= Ld;\n\t\t\t//rgb = xyYtoRGB(xyY);\n\t\t\t//second\n\t\t\trgb = (rgb / lum) * Ld;\n\t\t\trgb = max(rgb,vec3(0.001));\n\t\t\trgb = pow( rgb, vec3( u_igamma ) );\n\t\t\tgl_FragColor = vec4( rgb, color.a );\n\t\t}", I.registerNodeType("texture/tonemapping",L),O.title="Perlin",O.desc="Generates a perlin noise texture",O.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES},width:{type:"Number",precision:0,step:1},height:{type:"Number",precision:0,step:1},octaves:{type:"Number",precision:0,step:1,min:1,max:50}},O.prototype.onGetInputs=function(){return[["seed","Number"],["persistence","Number"],["octaves","Number"],["scale","Number"],["amplitude","Number"],["offset","vec2"]]},O.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a= -this.properties.width|0,b=this.properties.height|0;0==a&&(a=gl.viewport_data[2]);0==b&&(b=gl.viewport_data[3]);var d=c.getTextureType(this.properties.precision),f=this._texture;f&&f.width==a&&f.height==b&&f.type==d||(f=this._texture=new GL.Texture(a,b,{type:d,format:gl.RGB,filter:gl.LINEAR}));var g=this.getInputOrProperty("persistence"),e=this.getInputOrProperty("octaves"),h=this.getInputOrProperty("offset"),l=this.getInputOrProperty("scale"),p=this.getInputOrProperty("amplitude"),k=this.getInputOrProperty("seed"), -d=""+a+b+d+g+e+l+k+h[0]+h[1]+p;if(d!=this._key){this._key=d;var q=this._uniforms;q.u_persistence=g;q.u_octaves=e;q.u_offset.set(h);q.u_scale=l;q.u_amplitude=p;q.u_seed=128*k;q.u_viewport[0]=a;q.u_viewport[1]=b;var z=O._shader;z||(z=O._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,O.pixel_shader));gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);f.drawTo(function(){z.uniforms(q).draw(GL.Mesh.getScreenQuad())})}this.setOutputData(0,f)}},O.pixel_shader="precision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec2 u_offset;\n\t\tuniform float u_scale;\n\t\tuniform float u_persistence;\n\t\tuniform int u_octaves;\n\t\tuniform float u_amplitude;\n\t\tuniform vec2 u_viewport;\n\t\tuniform float u_seed;\n\t\t#define M_PI 3.14159265358979323846\n\t\t\n\t\tfloat rand(vec2 c){\treturn fract(sin(dot(c.xy ,vec2( 12.9898 + u_seed,78.233 + u_seed))) * 43758.5453); }\n\t\t\n\t\tfloat noise(vec2 p, float freq ){\n\t\t\tfloat unit = u_viewport.x/freq;\n\t\t\tvec2 ij = floor(p/unit);\n\t\t\tvec2 xy = mod(p,unit)/unit;\n\t\t\t//xy = 3.*xy*xy-2.*xy*xy*xy;\n\t\t\txy = .5*(1.-cos(M_PI*xy));\n\t\t\tfloat a = rand((ij+vec2(0.,0.)));\n\t\t\tfloat b = rand((ij+vec2(1.,0.)));\n\t\t\tfloat c = rand((ij+vec2(0.,1.)));\n\t\t\tfloat d = rand((ij+vec2(1.,1.)));\n\t\t\tfloat x1 = mix(a, b, xy.x);\n\t\t\tfloat x2 = mix(c, d, xy.x);\n\t\t\treturn mix(x1, x2, xy.y);\n\t\t}\n\t\t\n\t\tfloat pNoise(vec2 p, int res){\n\t\t\tfloat persistance = u_persistence;\n\t\t\tfloat n = 0.;\n\t\t\tfloat normK = 0.;\n\t\t\tfloat f = 4.;\n\t\t\tfloat amp = 1.0;\n\t\t\tint iCount = 0;\n\t\t\tfor (int i = 0; i<50; i++){\n\t\t\t\tn+=amp*noise(p, f);\n\t\t\t\tf*=2.;\n\t\t\t\tnormK+=amp;\n\t\t\t\tamp*=persistance;\n\t\t\t\tif (iCount >= res)\n\t\t\t\t\tbreak;\n\t\t\t\tiCount++;\n\t\t\t}\n\t\t\tfloat nf = n/normK;\n\t\t\treturn nf*nf*nf*nf;\n\t\t}\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord * u_scale * u_viewport + u_offset * u_scale;\n\t\t\tvec4 color = vec4( pNoise( uv, u_octaves ) * u_amplitude );\n\t\t\tgl_FragColor = color;\n\t\t}", +this.properties.width|0,b=this.properties.height|0;0==a&&(a=gl.viewport_data[2]);0==b&&(b=gl.viewport_data[3]);var d=c.getTextureType(this.properties.precision),f=this._texture;f&&f.width==a&&f.height==b&&f.type==d||(f=this._texture=new GL.Texture(a,b,{type:d,format:gl.RGB,filter:gl.LINEAR}));var g=this.getInputOrProperty("persistence"),e=this.getInputOrProperty("octaves"),h=this.getInputOrProperty("offset"),l=this.getInputOrProperty("scale"),r=this.getInputOrProperty("amplitude"),k=this.getInputOrProperty("seed"), +d=""+a+b+d+g+e+l+k+h[0]+h[1]+r;if(d!=this._key){this._key=d;var s=this._uniforms;s.u_persistence=g;s.u_octaves=e;s.u_offset.set(h);s.u_scale=l;s.u_amplitude=r;s.u_seed=128*k;s.u_viewport[0]=a;s.u_viewport[1]=b;var w=O._shader;w||(w=O._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,O.pixel_shader));gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);f.drawTo(function(){w.uniforms(s).draw(GL.Mesh.getScreenQuad())})}this.setOutputData(0,f)}},O.pixel_shader="precision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec2 u_offset;\n\t\tuniform float u_scale;\n\t\tuniform float u_persistence;\n\t\tuniform int u_octaves;\n\t\tuniform float u_amplitude;\n\t\tuniform vec2 u_viewport;\n\t\tuniform float u_seed;\n\t\t#define M_PI 3.14159265358979323846\n\t\t\n\t\tfloat rand(vec2 c){\treturn fract(sin(dot(c.xy ,vec2( 12.9898 + u_seed,78.233 + u_seed))) * 43758.5453); }\n\t\t\n\t\tfloat noise(vec2 p, float freq ){\n\t\t\tfloat unit = u_viewport.x/freq;\n\t\t\tvec2 ij = floor(p/unit);\n\t\t\tvec2 xy = mod(p,unit)/unit;\n\t\t\t//xy = 3.*xy*xy-2.*xy*xy*xy;\n\t\t\txy = .5*(1.-cos(M_PI*xy));\n\t\t\tfloat a = rand((ij+vec2(0.,0.)));\n\t\t\tfloat b = rand((ij+vec2(1.,0.)));\n\t\t\tfloat c = rand((ij+vec2(0.,1.)));\n\t\t\tfloat d = rand((ij+vec2(1.,1.)));\n\t\t\tfloat x1 = mix(a, b, xy.x);\n\t\t\tfloat x2 = mix(c, d, xy.x);\n\t\t\treturn mix(x1, x2, xy.y);\n\t\t}\n\t\t\n\t\tfloat pNoise(vec2 p, int res){\n\t\t\tfloat persistance = u_persistence;\n\t\t\tfloat n = 0.;\n\t\t\tfloat normK = 0.;\n\t\t\tfloat f = 4.;\n\t\t\tfloat amp = 1.0;\n\t\t\tint iCount = 0;\n\t\t\tfor (int i = 0; i<50; i++){\n\t\t\t\tn+=amp*noise(p, f);\n\t\t\t\tf*=2.;\n\t\t\t\tnormK+=amp;\n\t\t\t\tamp*=persistance;\n\t\t\t\tif (iCount >= res)\n\t\t\t\t\tbreak;\n\t\t\t\tiCount++;\n\t\t\t}\n\t\t\tfloat nf = n/normK;\n\t\t\treturn nf*nf*nf*nf;\n\t\t}\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord * u_scale * u_viewport + u_offset * u_scale;\n\t\t\tvec4 color = vec4( pNoise( uv, u_octaves ) * u_amplitude );\n\t\t\tgl_FragColor = color;\n\t\t}", I.registerNodeType("texture/perlin",O),M.title="Canvas2D",M.desc="Executes Canvas2D code inside a texture or the viewport.",M.help="Set width and height to 0 to match viewport size.",M.default_code="//vars: canvas,ctx,time\nctx.fillStyle='red';\nctx.fillRect(0,0,50,50);\n",M.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES},code:{type:"code"},width:{type:"Number",precision:0,step:1},height:{type:"Number",precision:0,step:1}},M.prototype.onPropertyChanged=function(a,b){"code"==a&&this.compileCode(b)}, M.prototype.compileCode=function(a){this._func=null;if(I.allow_scripts)try{this._func=new Function("canvas","ctx","time","script","v",a),this.boxcolor="#00FF00"}catch(b){this.boxcolor="#FF0000",console.error("Error parsing script"),console.error(b)}},M.prototype.onExecute=function(){var a=this._func;a&&this.isOutputConnected(0)&&this.executeDraw(a)},M.prototype.executeDraw=function(a){var b=this.properties.width||gl.canvas.width,d=this.properties.height||gl.canvas.height,f=this._temp_texture,g=c.getTextureType(this.properties.precision); -f&&f.width==b&&f.height==d&&f.type==g||(f=this._temp_texture=new GL.Texture(b,d,{format:gl.RGBA,filter:gl.LINEAR,type:g}));var e=this.getInputData(0),h=this.properties,l=this,p=this.graph.getTime(),k=gl,q=gl.canvas;if(this.properties.use_html_canvas||!D.enableWebGLCanvas)this._canvas?(q=this._canvas,k=this._ctx):(q=this._canvas=createCanvas(b.height),k=this._ctx=q.getContext("2d")),q.width=b,q.height=d;if(k==gl)f.drawTo(function(){gl.start2D();h.clear&&(gl.clearColor(0,0,0,0),gl.clear(gl.COLOR_BUFFER_BIT)); -try{a.draw?a.draw.call(l,q,k,p,a,e):a.call(l,q,k,p,a,e),l.boxcolor="#00FF00"}catch(b){l.boxcolor="#FF0000",console.error("Error executing script"),console.error(b)}gl.finish2D()});else{h.clear&&k.clearRect(0,0,q.width,q.height);try{a.draw?a.draw.call(this,q,k,p,a,e):a.call(this,q,k,p,a,e),this.boxcolor="#00FF00"}catch(z){this.boxcolor="#FF0000",console.error("Error executing script"),console.error(z)}f.uploadImage(q)}this.setOutputData(0,f)},I.registerNodeType("texture/canvas2D",M),Q.title="Matte", +f&&f.width==b&&f.height==d&&f.type==g||(f=this._temp_texture=new GL.Texture(b,d,{format:gl.RGBA,filter:gl.LINEAR,type:g}));var e=this.getInputData(0),h=this.properties,l=this,r=this.graph.getTime(),k=gl,s=gl.canvas;if(this.properties.use_html_canvas||!C.enableWebGLCanvas)this._canvas?(s=this._canvas,k=this._ctx):(s=this._canvas=createCanvas(b.height),k=this._ctx=s.getContext("2d")),s.width=b,s.height=d;if(k==gl)f.drawTo(function(){gl.start2D();h.clear&&(gl.clearColor(0,0,0,0),gl.clear(gl.COLOR_BUFFER_BIT)); +try{a.draw?a.draw.call(l,s,k,r,a,e):a.call(l,s,k,r,a,e),l.boxcolor="#00FF00"}catch(b){l.boxcolor="#FF0000",console.error("Error executing script"),console.error(b)}gl.finish2D()});else{h.clear&&k.clearRect(0,0,s.width,s.height);try{a.draw?a.draw.call(this,s,k,r,a,e):a.call(this,s,k,r,a,e),this.boxcolor="#00FF00"}catch(w){this.boxcolor="#FF0000",console.error("Error executing script"),console.error(w)}f.uploadImage(s)}this.setOutputData(0,f)},I.registerNodeType("texture/canvas2D",M),Q.title="Matte", Q.desc="Extracts background",Q.widgets_info={key_color:{widget:"color"},precision:{widget:"combo",values:c.MODE_VALUES}},Q.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else if(a){this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);this._uniforms||(this._uniforms={u_texture:0,u_key_color:this.properties.key_color,u_threshold:1, u_slope:1});var b=this._uniforms,d=Mesh.getScreenQuad(),f=Q._shader;f||(f=Q._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,Q.pixel_shader));b.u_key_color=this.properties.key_color;b.u_threshold=this.properties.threshold;b.u_slope=this.properties.slope;this._tex.drawTo(function(){a.bind(0);f.uniforms(b).draw(d)});this.setOutputData(0,this._tex)}}},Q.pixel_shader="precision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec3 u_key_color;\n\t\tuniform float u_threshold;\n\t\tuniform float u_slope;\n\t\t\n\t\tvoid main() {\n\t\t\tvec3 color = texture2D( u_texture, v_coord ).xyz;\n\t\t\tfloat diff = length( normalize(color) - normalize(u_key_color) );\n\t\t\tfloat edge = u_threshold * (1.0 - u_slope);\n\t\t\tfloat alpha = smoothstep( edge, u_threshold, diff);\n\t\t\tgl_FragColor = vec4( color, alpha );\n\t\t}", I.registerNodeType("texture/matte",Q),S.title="CubemapToTexture2D",S.desc="Transforms a CUBEMAP texture into a TEXTURE2D in Polar Representation",S.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a&&a.texture_type==GL.TEXTURE_CUBE_MAP){!this._last_tex||this._last_tex.height==a.height&&this._last_tex.type==a.type||(this._last_tex=null);var b=this.getInputOrProperty("yaw");this._last_tex=GL.Texture.cubemapToTexture2D(a,a.height,this._last_tex,!0,b);this.setOutputData(0, this._last_tex)}}},I.registerNodeType("texture/cubemapToTexture2D",S))})(this); -(function(D){function c(){r.length=0;for(var a in f){var b=f[a],d=b.indexOf(" "),c=b.substr(0,d),e=b.indexOf("(",d),d=b.substr(d,e-d).trim(),b=b.substr(e+1,b.length-e-2).split(","),h;for(h in b)e=b[h].split(" "),b[h]={type:e[0],name:e[1]};g[a]={return_type:c,func:d,params:b};r.push(d)}}function m(b,d){d.color=a;d.filter="shader";d.prototype.clearDestination=function(){this.shader_destination={}};d.prototype.propagateDestination=function(a){this.shader_destination[a]=!0;if(this.inputs)for(var b=0;b< -this.inputs.length;++b){var d=this.getInputNode(b);d&&d.propagateDestination(a)}};l.registerNodeType(b,d)}function n(a,b){return"VAR_"+(b||"TEMP")+"_"+a.id}function k(a,b){if(!a.inputs)return null;var d=a.getInputLink(b);if(!d)return null;var c=a.graph.getNodeById(d.origin_id);return c?c.getOutputVarName?c.getOutputVarName(d.origin_slot):"link_"+c.id+"_"+d.origin_slot:null}function v(a,b){return a.isOutputConnected(0)?"link_"+a.id+"_"+b:null}function h(){this.fs_template=this.vs_template="";this._uniforms= -{};this._codeparts={}}function B(){this.subgraph=new l.LGraph;this.subgraph._subgraph_node=this;this.subgraph._is_subgraph=!0;this.subgraph.filter="shader";this.addInput("in","texture");this.addOutput("out","texture");this.properties={width:0,height:0,alpha:!1,precision:"undefined"!=typeof LGraphTexture?LGraphTexture.DEFAULT:2};var a=this.subgraph.findNodesByType("input/uniform")[0];a.pos=[200,300];var b=l.createNode("texture/sampler2D");b.pos=[400,300];this.subgraph.add(b);var d=l.createNode("output/fragcolor"); -d.pos=[600,300];this.subgraph.add(d);a.connect(0,b);b.connect(0,d);this.size=[180,60];this.redraw_on_mouse=!0;this._uniforms={};this._shader=null;this._context=new h;this._context.vs_template=GL.Shader.SCREEN_VERTEX_SHADER;this._context.fs_template=B.template}function y(){this.addOutput("out","");this.properties={name:"",type:""}}function C(){this.addOutput("out","vec2");this.properties={name:"coord",type:"vec2"}}function G(){this.addInput("tex","sampler2D");this.addInput("uv","vec2");this.addOutput("rgba", -"vec4");this.addOutput("rgb","vec3")}function E(){this.addOutput("","float");this.properties={type:"float",value:0};this.addWidget("combo","type","float",null,{values:d,property:"type"});this.updateWidgets()}function e(){this.addInput("xy","vec2");this.addInput("x","float");this.addInput("y","float");this.addOutput("xy","vec2");this.addOutput("x","float");this.addOutput("y","float");this.properties={x:0,y:0}}function A(){this.addInput("xyz","vec3");this.addInput("x","float");this.addInput("y","float"); -this.addInput("z","float");this.addInput("xy","vec2");this.addInput("xz","vec2");this.addInput("yz","vec2");this.addOutput("xyz","vec3");this.addOutput("x","float");this.addOutput("y","float");this.addOutput("z","float");this.addOutput("xy","vec2");this.addOutput("xz","vec2");this.addOutput("yz","vec2");this.properties={x:0,y:0,z:0}}function F(){this.addInput("xyzw","vec4");this.addInput("xyz","vec3");this.addInput("x","float");this.addInput("y","float");this.addInput("z","float");this.addInput("w", -"float");this.addInput("xy","vec2");this.addInput("yz","vec2");this.addInput("zw","vec2");this.addOutput("xyzw","vec4");this.addOutput("xyz","vec3");this.addOutput("x","float");this.addOutput("y","float");this.addOutput("z","float");this.addOutput("xy","vec2");this.addOutput("yz","vec2");this.addOutput("zw","vec2");this.properties={x:0,y:0,z:0,w:0}}function t(){this.addInput("color","float,vec2,vec3,vec4");this.block_delete=!0}function H(){this.addInput("A","float,vec2,vec3,vec4");this.addInput("B", -"float,vec2,vec3,vec4");this.addOutput("out","");this.properties={func:"floor"};this._current="floor";this.addWidget("combo","func",this.properties.func,{property:"func",values:r})}function p(){this.addInput("A","float,vec2,vec3,vec4");this.addInput("B","float,vec2,vec3,vec4");this.addOutput("C","vec4");this.properties={code:"C = A+B",type:"vec4"};this.addWidget("text","code",this.properties.code,{property:"code"});this.addWidget("combo","type",this.properties.type,{values:["float","vec2","vec3", -"vec4"],property:"type"})}function s(){this.addInput("","T,float,vec2,vec3,vec4");this.addOutput("","T");this.properties={min_value:0,max_value:1,min_value2:0,max_value2:1};this.addWidget("number","min",0,{step:0.1,property:"min_value"});this.addWidget("number","max",1,{step:0.1,property:"max_value"});this.addWidget("number","min2",0,{step:0.1,property:"min_value2"});this.addWidget("number","max2",1,{step:0.1,property:"max_value2"})}if("undefined"!=typeof GL){var l=D.LiteGraph,a="#345",b=l.Shaders= -{};b.GLSL_types="float vec2 vec3 vec4 mat3 mat4 sampler2D samplerCube".split(" ");var d=b.GLSL_types_const=["float","vec2","vec3","vec4"],f={radians:"T radians(T degrees)",degrees:"T degrees(T radians)",sin:"T sin(T angle)",cos:"T cos(T angle)",tan:"T tan(T angle)",asin:"T asin(T x)",acos:"T acos(T x)",atan:"T atan(T x)",atan2:"T atan(T x,T y)",pow:"T pow(T x,T y)",exp:"T exp(T x)",log:"T log(T x)",exp2:"T exp2(T x)",log2:"T log2(T x)",sqrt:"T sqrt(T x)",inversesqrt:"T inversesqrt(T x)",abs:"T abs(T x)", -sign:"T sign(T x)",floor:"T floor(T x)",ceil:"T ceil(T x)",fract:"T fract(T x)",mod:"T mod(T x,T y)",min:"T min(T x,T y)",max:"T max(T x,T y)",clamp:"T clamp(T x,T minVal,T maxVal)",mix:"T mix(T x,T y,T a)",step:"T step(T edge, T x)",smoothstep:"T smoothstep(T edge, T x)",length:"float length(T x)",distance:"float distance(T p0, T p1)",normalize:"T normalize(T x)",dot:"float dot(T x,T y)",cross:"vec3 cross(vec3 x,vec3 y)"},g={},r=[];c();b.registerShaderNode=m;b.getInputLinkID=k;b.getOutputLinkID= -v;b.getShaderNodeVarName=n;b.parseGLSLDescriptions=c;var x=l.valueToGLSL=function(a,b){if(!b)if(a.constructor===Number)b="float";else if(a.length)switch(a.length){case 2:b="vec2";break;case 3:b="vec3";break;case 4:b="vec4";break;case 9:b="mat3";break;case 16:b="mat4";break;default:throw"unknown type for glsl value size";}else throw"unknown type for glsl value: "+a.constructor;switch(b){case "float":return a.toFixed(5);case "vec2":return"vec2("+a[0].toFixed(5)+","+a[1].toFixed(5)+")";case "color3":case "vec3":return"vec3("+ -a[0].toFixed(5)+","+a[1].toFixed(5)+","+a[2].toFixed(5)+")";case "color4":case "vec4":return"vec4("+a[0].toFixed(5)+","+a[1].toFixed(5)+","+a[2].toFixed(5)+","+a[3].toFixed(5)+")";case "mat3":return"mat3(1.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,1.0)";case "mat4":return"mat4(1.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,1.0)";default:throw"unknown glsl type in valueToGLSL:",b;}},K=l.convertVarToGLSLType=function(a,b,d){if(b==d)return a;if("float"==b)return d+"("+a+")";if("vec2"==d)return"vec2("+ -a+".xy)";if("vec3"==d){if("vec2"==b)return"vec3("+a+",0.0)";if("vec4"==b)return"vec4("+a+".xyz)"}if("vec4"==d){if("vec2"==b)return"vec4("+a+",0.0,0.0)";if("vec3"==d)return"vec4("+a+",1.0)"}return null};h.prototype.clear=function(){this._uniforms={};this._codeparts={}};h.prototype.addUniform=function(a,b){this._uniforms[a]=b};h.prototype.addCode=function(a,b,d){d=d||{"":""};for(var c in d)d=c?c+"_"+a:a,this._codeparts[d]=this._codeparts[d]?this._codeparts[d]+(b+"\n"):b+"\n"};h.prototype.computeShaderCode= -function(){var a="",b;for(b in this._uniforms)a+="uniform "+this._uniforms[b]+" "+b+";\n";b=this._codeparts;b.uniforms=a;a=GL.Shader.replaceCodeUsingContext(this.vs_template,b);b=GL.Shader.replaceCodeUsingContext(this.fs_template,b);return{vs_code:a,fs_code:b}};h.prototype.computeShader=function(a){var b=this.computeShaderCode();console.log(b.vs_code,b.fs_code);try{return a?a.updateShader(b.vs_code,b.fs_code):a=new GL.Shader(b.vs_code,b.fs_code),this._shader_error=!1,a}catch(d){return this._shader_error|| -(console.error(d),-1!=d.indexOf("Fragment shader")?console.log(b.fs_code):console.log(b.vs_code)),this._shader_error=!0,null}};B.template="\nprecision highp float;\nvarying vec2 v_coord;\n{{varying}}\n{{uniforms}}\n{{fs_functions}}\nvoid main() {\n\nvec2 uv = v_coord;\nvec4 color = vec4(0.0);\n{{fs_code}}\ngl_FragColor = color;\n}\n\t";B.widgets_info={precision:{widget:"combo",values:LGraphTexture.MODE_VALUES}};B.title="ShaderGraph";B.desc="Builds a shader using a graph";B.input_node_type="input/uniform"; -B.output_node_type="output/fragcolor";B.title_color=a;B.prototype.onSerialize=function(a){a.subgraph=this.subgraph.serialize()};B.prototype.onConfigure=function(a){this.subgraph.configure(a.subgraph)};B.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);a&&a.constructor!=GL.Texture&&(a=null);var b=this.properties.width|0,d=this.properties.height|0;0==b&&(b=a?a.width:gl.viewport_data[2]);0==d&&(d=a?a.height:gl.viewport_data[3]);var c=LGraphTexture.getTextureType(this.properties.precision, -a),a=this._texture;a&&a.width==b&&a.height==d&&a.type==c||(a=this._texture=new GL.Texture(b,d,{type:c,format:this.alpha?gl.RGBA:gl.RGB,filter:gl.LINEAR}));var f=this.getShader();if(f){var g=this._uniforms,b=0;if(this.inputs)for(d=0;dd+l.NODE_TITLE_HEIGHT&&a.drawImage(b,10,f,this.size[0]-20,this.size[1]-d-l.NODE_TITLE_HEIGHT);var f=this.size[1]-l.NODE_TITLE_HEIGHT+0.5;c=l.isInsideRectangle(c[0],c[1], -this.pos[0],this.pos[1]+f,this.size[0],l.NODE_TITLE_HEIGHT);a.fillStyle=c?"#555":"#222";a.beginPath();a.roundRect(0,f,this.size[0]+1,l.NODE_TITLE_HEIGHT,0,8);a.fill();a.textAlign="center";a.font="24px Arial";a.fillStyle=c?"#DDD":"#999";a.fillText("+",0.5*this.size[0],f+24)}};B.prototype.onMouseDown=function(a,b,d){b[1]>this.size[1]-l.NODE_TITLE_HEIGHT+0.5&&d.showSubgraphPropertiesDialog(this)};B.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:"Print Code",callback:function(){var a= -b._context.computeShaderCode();console.log(a.vs_code,a.fs_code)}}]};l.registerNodeType("texture/shaderGraph",B);y.title="Uniform";y.desc="Input data for the shader";y.prototype.getTitle=function(){return this.properties.name&&this.flags.collapsed?this.properties.type+" "+this.properties.name:"Uniform"};y.prototype.onPropertyChanged=function(a,b){this.outputs[0].name=this.properties.type+" "+this.properties.name};y.prototype.onGetCode=function(a){var d=this.properties.type;d&&("number"==d?d="float": -"texture"==d&&(d="sampler2D"),-1!=b.GLSL_types.indexOf(d)&&(a.addUniform("u_"+this.properties.name,d),this.setOutputData(0,d)))};y.prototype.getOutputVarName=function(a){return"u_"+this.properties.name};m("input/uniform",y);C.title="Attribute";C.desc="Input data from mesh attribute";C.prototype.getTitle=function(){return"att. "+this.properties.name};C.prototype.onGetCode=function(a){var d=this.properties.type;d&&-1!=b.GLSL_types.indexOf(d)&&("number"==d&&(d="float"),"coord"!=this.properties.name&& -a.addCode("varying"," varying "+d+" v_"+this.properties.name+";"),this.setOutputData(0,d))};C.prototype.getOutputVarName=function(a){return"v_"+this.properties.name};m("input/attribute",C);G.title="Sampler2D";G.desc="Reads a pixel from a texture";G.prototype.onGetCode=function(a){var b=k(this,0),d=n(this),c="vec4 "+d+" = vec4(0.0);\n";if(b)var f=k(this,1)||"v_coord",c=c+(d+" = texture2D("+b+","+f+");\n");v(this,0)&&(c+="vec4 "+v(this,0)+" = "+d+";\n");v(this,1)&&(c+="vec3 "+v(this,1)+" = "+d+".xyz;\n"); -a.addCode("code",c,this.shader_destination);this.setOutputData(0,"vec4");this.setOutputData(1,"vec3")};m("texture/sampler2D",G);E.title="const";E.prototype.getTitle=function(){return this.flags.collapsed?x(this.properties.value,this.properties.type):"Const"};E.prototype.onPropertyChanged=function(a,b){"type"==a&&this.outputs[0].type!=b&&(this.disconnectOutput(0),this.outputs[0].type=b,this.widgets.length=1,this.updateWidgets())};E.prototype.updateWidgets=function(a){var b=this;a=this.properties.value; -var d={step:0.01};switch(this.properties.type){case "float":this.properties.value=0;this.addWidget("number","v",0,{step:0.01,property:"value"});break;case "vec2":this.properties.value=a&&2==a.length?[a[0],a[1]]:[0,0,0];this.addWidget("number","x",0,d,function(a){b.properties.value[0]=a});this.addWidget("number","y",0,d,function(a){b.properties.value[1]=a});break;case "vec3":this.properties.value=a&&3==a.length?[a[0],a[1],a[2]]:[0,0,0];this.addWidget("number","x",0,d,function(a){b.properties.value[0]= -a});this.addWidget("number","y",0,d,function(a){b.properties.value[1]=a});this.addWidget("number","z",0,d,function(a){b.properties.value[2]=a});break;case "vec4":this.properties.value=a&&4==a.length?[a[0],a[1],a[2],a[3]]:[0,0,0,0];this.addWidget("number","x",0,d,function(a){b.properties.value[0]=a});this.addWidget("number","y",0,d,function(a){b.properties.value[1]=a});this.addWidget("number","z",0,d,function(a){b.properties.value[2]=a});this.addWidget("number","w",0,d,function(a){b.properties.value[3]= -a});break;default:console.error("unknown type for constant")}};E.prototype.onGetCode=function(a){var b=x(this.properties.value,this.properties.type),d=v(this,0);d&&(a.addCode("code","\t"+this.properties.type+" "+d+" = "+b+";",this.shader_destination),this.setOutputData(0,this.properties.type))};m("const/const",E);e.title="vec2";e.varmodes=["xy","x","y"];e.prototype.onPropertyChanged=function(){this.graph._version++};e.prototype.onGetCode=function(a){for(var b=this.properties,c=n(this),b="\tvec2 "+ -c+" = "+x([b.x,b.y])+";\n",f=0;fd;++d)b.push({name:k(this,d),type:this.getInputData(d)||"float"});var c=v(this,0);if(c){var f=g[this.properties.func];if(f){var e=b[0].type,h=f.return_type;"T"==h&&(h=e);for(var l=[],d=0;d=g;){e=0.5*(h+g)|0;c=a[e];if(c==d)break;if(g==h-1)return g;cd+l.NODE_TITLE_HEIGHT&&a.drawImage(b,10,f,this.size[0]-20,this.size[1]-d-l.NODE_TITLE_HEIGHT);var f=this.size[1]- +l.NODE_TITLE_HEIGHT+0.5;c=l.isInsideRectangle(c[0],c[1],this.pos[0],this.pos[1]+f,this.size[0],l.NODE_TITLE_HEIGHT);a.fillStyle=c?"#555":"#222";a.beginPath();this._shape==l.BOX_SHAPE?a.rect(0,f,this.size[0]+1,l.NODE_TITLE_HEIGHT):a.roundRect(0,f,this.size[0]+1,l.NODE_TITLE_HEIGHT,0,8);a.fill();a.textAlign="center";a.font="24px Arial";a.fillStyle=c?"#DDD":"#999";a.fillText("+",0.5*this.size[0],f+24)}};D.prototype.onMouseDown=function(a,b,d){b[1]>this.size[1]-l.NODE_TITLE_HEIGHT+0.5&&d.showSubgraphPropertiesDialog(this)}; +D.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:"Print Code",callback:function(){var a=b._context.computeShaderCode();console.log(a.vs_code,a.fs_code)}}]};l.registerNodeType("texture/shaderGraph",D);x.title="Uniform";x.desc="Input data for the shader";x.prototype.getTitle=function(){return this.properties.name&&this.flags.collapsed?this.properties.type+" "+this.properties.name:"Uniform"};x.prototype.onPropertyChanged=function(a,b){this.outputs[0].name=this.properties.type+" "+ +this.properties.name};x.prototype.onGetCode=function(a){var d=this.properties.type;d&&("number"==d?d="float":"texture"==d&&(d="sampler2D"),-1!=b.GLSL_types.indexOf(d)&&(a.addUniform("u_"+this.properties.name,d),this.setOutputData(0,d)))};x.prototype.getOutputVarName=function(a){return"u_"+this.properties.name};m("input/uniform",x);B.title="Attribute";B.desc="Input data from mesh attribute";B.prototype.getTitle=function(){return"att. "+this.properties.name};B.prototype.onGetCode=function(a){var d= +this.properties.type;d&&-1!=b.GLSL_types.indexOf(d)&&("number"==d&&(d="float"),"coord"!=this.properties.name&&a.addCode("varying"," varying "+d+" v_"+this.properties.name+";"),this.setOutputData(0,d))};B.prototype.getOutputVarName=function(a){return"v_"+this.properties.name};m("input/attribute",B);G.title="Sampler2D";G.desc="Reads a pixel from a texture";G.prototype.onGetCode=function(a){var b=k(this,0),d=n(this),c="vec4 "+d+" = vec4(0.0);\n";if(b)var f=k(this,1)||"v_coord",c=c+(d+" = texture2D("+ +b+","+f+");\n");y(this,0)&&(c+="vec4 "+y(this,0)+" = "+d+";\n");y(this,1)&&(c+="vec3 "+y(this,1)+" = "+d+".xyz;\n");a.addCode("code",c,this.shader_destination);this.setOutputData(0,"vec4");this.setOutputData(1,"vec3")};m("texture/sampler2D",G);E.title="const";E.prototype.getTitle=function(){return this.flags.collapsed?A(this.properties.value,this.properties.type):"Const"};E.prototype.onPropertyChanged=function(a,b){"type"==a&&this.outputs[0].type!=b&&(this.disconnectOutput(0),this.outputs[0].type= +b,this.widgets.length=1,this.updateWidgets())};E.prototype.updateWidgets=function(a){var b=this;a=this.properties.value;var d={step:0.01};switch(this.properties.type){case "float":this.properties.value=0;this.addWidget("number","v",0,{step:0.01,property:"value"});break;case "vec2":this.properties.value=a&&2==a.length?[a[0],a[1]]:[0,0,0];this.addWidget("number","x",0,d,function(a){b.properties.value[0]=a});this.addWidget("number","y",0,d,function(a){b.properties.value[1]=a});break;case "vec3":this.properties.value= +a&&3==a.length?[a[0],a[1],a[2]]:[0,0,0];this.addWidget("number","x",0,d,function(a){b.properties.value[0]=a});this.addWidget("number","y",0,d,function(a){b.properties.value[1]=a});this.addWidget("number","z",0,d,function(a){b.properties.value[2]=a});break;case "vec4":this.properties.value=a&&4==a.length?[a[0],a[1],a[2],a[3]]:[0,0,0,0];this.addWidget("number","x",0,d,function(a){b.properties.value[0]=a});this.addWidget("number","y",0,d,function(a){b.properties.value[1]=a});this.addWidget("number", +"z",0,d,function(a){b.properties.value[2]=a});this.addWidget("number","w",0,d,function(a){b.properties.value[3]=a});break;default:console.error("unknown type for constant")}};E.prototype.onGetCode=function(a){var b=A(this.properties.value,this.properties.type),d=y(this,0);d&&(a.addCode("code","\t"+this.properties.type+" "+d+" = "+b+";",this.shader_destination),this.setOutputData(0,this.properties.type))};m("const/const",E);e.title="vec2";e.varmodes=["xy","x","y"];e.prototype.onPropertyChanged=function(){this.graph._version++}; +e.prototype.onGetCode=function(a){for(var b=this.properties,c=n(this),b="\tvec2 "+c+" = "+A([b.x,b.y])+";\n",f=0;fd;++d)b.push({name:k(this,d),type:this.getInputData(d)||"float"});var c=y(this,0);if(c){var f=g[this.properties.func];if(f){var e=b[0].type,h=f.return_type;"T"==h&&(h=e);for(var l=[],d=0;d< +f.params.length;++d){var w=f.params[d],t=b[d].name;null==t&&(t="(1.0)",b[d].type="float");if("T"==w.type&&b[d].type!=e||"T"!=w.type&&b[d].type!=e)t=K(b[d].name,b[d].type,e);l.push(t)}a.addCode("code",h+" "+c+" = "+f.func+"("+l.join(",")+");",this.shader_destination);this.setOutputData(0,h)}}}};m("math/func",H);w.title="Snippet";w.prototype.onPropertyChanged=function(a,b){this.graph._version++;"type"==a&&this.outputs[0].type!=b&&(this.disconnectOutput(0),this.outputs[0].type=b)};w.prototype.getTitle= +function(){return this.flags.collapsed?this.properties.code:"Snippet"};w.prototype.onGetCode=function(a){if(this.isOutputConnected(0)){var b=k(this,0);b||(b="1.0");var d=k(this,1);d||(d="1.0");var c=y(this,0);if(c){var f=this.getInputData(0)||"float",g=this.getInputData(1)||"float",e=this.properties.type;if("T"==f||"T"==g)return null;var h="funcSnippet"+this.id,f="\n"+e+" "+h+"( "+f+" A, "+g+" B) {\n"+("\t"+e+" C = "+e+"(0.0);\n"),f=f+("\t"+this.properties.code+";\n"),f=f+"\treturn C;\n}\n";a.addCode("functions", +f,this.shader_destination);a.addCode("code",e+" "+c+" = "+h+"("+b+","+d+");",this.shader_destination);this.setOutputData(0,e)}}};m("utils/snippet",w);t.title="Remap";t.prototype.onPropertyChanged=function(){this.graph._version++};t.prototype.onConnectionsChange=function(){var a=this.getInputDataType(0);this.outputs[0].type=a||"T"};t.prototype.onGetCode=function(a){if(this.isOutputConnected(0)){var b=k(this,0),d=y(this,0);if(b||d){var c=this.getInputDataType(0);this.outputs[0].type=c;if("T"==c)console.warn("node type is T and cannot be resolved"); +else if(b){var f=A(this.properties.min_value),g=A(this.properties.max_value),e=A(this.properties.min_value2),h=A(this.properties.max_value2);a.addCode("code",c+" "+d+" = ( ("+b+" - "+f+") / ("+g+" - "+f+") ) * ("+h+" - "+e+") + "+e+";",this.shader_destination);this.setOutputData(0,c)}else a.addCode("code","\t"+c+" "+d+" = "+c+"(0.0);\n")}}};m("math/remap",t)}})(this); +(function(C){function c(){return 1E5*Math.random()|0}function m(){this.addInput("obj","");this.addInput("radius","number");this.addOutput("out","geometry");this.addOutput("points","[vec3]");this.properties={radius:1,num_points:4096,generate_normals:!0,regular:!1,mode:m.SPHERE,force_update:!1};this.points=new Float32Array(3*this.properties.num_points);this.normals=new Float32Array(3*this.properties.num_points);this.must_update=!0;this.version=0;var a=this;this.addWidget("button","update",null,function(){a.must_update= +!0});this.geometry={vertices:null,_id:c()};this._last_radius=this._old_obj=null}function n(a,d){var c=a.length,g=0,e=0,h=c;if(0==c)return-1;if(1==c)return 0;for(;h>=g;){e=0.5*(h+g)|0;c=a[e];if(c==d)break;if(g==h-1)return g;ca&&(a=1);this.points&&this.points.length==3*a||(this.points=new Float32Array(3*a));this.properties.generate_normals?this.normals&&this.normals.length==this.points.length||(this.normals=new Float32Array(this.points.length)):this.normals=null;var d=this._last_radius||this.properties.radius,c=this.properties.mode,g=this.getInputData(0);this._old_obj_version=g?g._version:null;this.points=m.generatePoints(d,a,c,this.points, -this.normals,this.properties.regular,g);this.version++};m.generatePoints=function(a,d,c,g,e,h,l){var p=3*d;g&&g.length==p||(g=new Float32Array(p));var k=new Float32Array(3),s=new Float32Array([0,1,0]);if(h)if(c==m.RECTANGLE){p=Math.floor(Math.sqrt(d));for(d=0;de||vh&&hl))break}this.geometry.indices=this.indices=new Uint32Array(p)}this.indices&&this.indices.length?(this.geometry.indices=this.indices,this.setOutputData(0,this.geometry)):this.setOutputData(0,null)}};t.registerNodeType("geometry/connectPoints",C);"undefined"!=typeof GL&&(G.title="to geometry",G.desc="converts a mesh to geometry",G.prototype.onExecute= -function(){var a=this.getInputData(0);if(a){if(a!=this.last_mesh){this.last_mesh=a;for(i in a.vertexBuffers)this.geometry[i]=a.vertexBuffers[i].data;a.indexBuffers.triangles&&(this.geometry.indices=a.indexBuffers.triangles.data);this.geometry._id=c();this.geometry._version=0}this.setOutputData(0,this.geometry);this.geometry&&this.setOutputData(1,this.geometry.vertices)}},t.registerNodeType("geometry/toGeometry",G),E.title="Geo to Mesh",E.prototype.updateMesh=function(a){this.mesh||(this.mesh=new GL.Mesh); +h=this.vertices;h&&this.vertices.length==a.vertices.length?h.set(a.vertices):h=this.vertices=new Float32Array(a.vertices);for(g=0;ge||vh&&hl))break}this.geometry.indices=this.indices=new Uint32Array(k)}this.indices&&this.indices.length?(this.geometry.indices=this.indices,this.setOutputData(0,this.geometry)):this.setOutputData(0,null)}};v.registerNodeType("geometry/connectPoints",B);"undefined"!=typeof GL&&(G.title="to geometry",G.desc="converts a mesh to geometry",G.prototype.onExecute= +function(){var a=this.getInputData(0);if(a){if(a!=this.last_mesh){this.last_mesh=a;for(i in a.vertexBuffers)this.geometry[i]=a.vertexBuffers[i].data;a.indexBuffers.triangles&&(this.geometry.indices=a.indexBuffers.triangles.data);this.geometry._id=c();this.geometry._version=0}this.setOutputData(0,this.geometry);this.geometry&&this.setOutputData(1,this.geometry.vertices)}},v.registerNodeType("geometry/toGeometry",G),E.title="Geo to Mesh",E.prototype.updateMesh=function(a){this.mesh||(this.mesh=new GL.Mesh); for(var d in a)if("_"!=d[0]){var c=a[d],g=GL.Mesh.common_buffers[d];if(g||"indices"==d){var g=g?g.spacing:3,e=this.mesh.vertexBuffers[d];e&&e.data.length==c.length?(e.data.set(c),e.upload(GL.DYNAMIC_DRAW)):e=new GL.Buffer("indices"==d?GL.ELEMENT_ARRAY_BUFFER:GL.ARRAY_BUFFER,c,g,GL.DYNAMIC_DRAW);this.mesh.addBuffer(d,e)}}if(this.mesh.vertexBuffers.normals&&this.mesh.vertexBuffers.normals.data.length!=this.mesh.vertexBuffers.vertices.data.length){c=new Float32Array([0,1,0]);g=new Float32Array(this.mesh.vertexBuffers.vertices.data.length); -for(d=0;d=c.NOTEON||h<=c.NOTEOFF)this.channel=e&15};Object.defineProperty(c.prototype,"velocity",{get:function(){return this.cmd==c.NOTEON?this.data[2]:-1},set:function(c){this.data[2]=c},enumerable:!0});c.notes="A A# B C C# D D# E F F# G G#".split(" ");c.note_to_index={A:0,"A#":1,B:2,C:3,"C#":4,D:5,"D#":6,E:7,F:8,"F#":9,G:10,"G#":11};Object.defineProperty(c.prototype,"note",{get:function(){return this.cmd!= c.NOTEON?-1:c.toNoteString(this.data[1],!0)},set:function(c){throw"notes cannot be assigned this way, must modify the data[1]";},enumerable:!0});Object.defineProperty(c.prototype,"octave",{get:function(){return this.cmd!=c.NOTEON?-1:Math.floor((this.data[1]-24)/12+1)},set:function(c){throw"octave cannot be assigned this way, must modify the data[1]";},enumerable:!0});c.prototype.getPitch=function(){return 440*Math.pow(2,(this.data[1]-69)/12)};c.computePitch=function(c){return 440*Math.pow(2,(c-69)/ 12)};c.prototype.getCC=function(){return this.data[1]};c.prototype.getCCValue=function(){return this.data[2]};c.prototype.getPitchBend=function(){return this.data[1]+(this.data[2]<<7)-8192};c.computePitchBend=function(c,e){return c+(e<<7)-8192};c.prototype.setCommandFromString=function(e){this.cmd=c.computeCommandFromString(e)};c.computeCommandFromString=function(e){if(!e)return 0;if(e&&e.constructor===Number)return e;e=e.toUpperCase();switch(e){case "NOTE ON":case "NOTEON":return c.NOTEON;case "NOTE OFF":case "NOTEOFF":return c.NOTEON; @@ -716,81 +717,81 @@ this.data[2];break;case c.PROGRAMCHANGE:e+="PC "+this.data[1];break;case c.PITCH h=e.next();h&&!1===h.done;)h=h.value,this.input_ports_info.push(h),console.log("Input port [type:'"+h.type+"'] id:'"+h.id+"' manufacturer:'"+h.manufacturer+"' name:'"+h.name+"' version:'"+h.version+"'"),c++,h=e.next();this.num_input_ports=c;c=0;e=this.output_ports.values();for(h=e.next();h&&!1===h.done;)h=h.value,this.output_ports_info.push(h),console.log("Output port [type:'"+h.type+"'] id:'"+h.id+"' manufacturer:'"+h.manufacturer+"' name:'"+h.name+"' version:'"+h.version+"'"),c++,h=e.next();this.num_output_ports= c};m.prototype.onMIDIFailure=function(c){console.error("Failed to get MIDI access - "+c)};m.prototype.openInputPort=function(e,h){var l=this.input_ports.get("input-"+e);if(!l)return!1;m.input=this;var a=this;l.onmidimessage=function(b){var d=new c(b.data);a.updateState(d);h&&h(b.data,d);if(m.on_message)m.on_message(b.data,d)};console.log("port open: ",l);return!0};m.parseMsg=function(c){};m.prototype.updateState=function(e){switch(e.cmd){case c.NOTEON:this.state.note[e.value1|0]=e.value2;break;case c.NOTEOFF:this.state.note[e.value1| 0]=0;break;case c.CONTROLLERCHANGE:this.state.cc[e.getCC()]=e.getCCValue()}};m.prototype.sendMIDI=function(e,h){if(h){var l=this.output_ports_info[e];l&&(m.output=this,h.constructor===c?l.send(h.data):l.send(h))}};n.MIDIInterface=m;n.title="MIDI Input";n.desc="Reads MIDI from a input port";n.color="#243";n.prototype.getPropertyInfo=function(c){if(this._midi&&"port"==c){c={};for(var e=0;ethis.properties.max_value)return;this.trigger("on_midi",h)}};t.registerNodeType("midi/filter",h);B.title="MIDIEvent";B.desc="Create a MIDI Event";B.color="#243";B.prototype.onAction=function(e,h){"assign"==e?(this.properties.channel=h.channel,this.properties.cmd=h.cmd,this.properties.value1=h.data[1],this.properties.value2=h.data[2],h.cmd== -c.NOTEON?this.gate=!0:h.cmd==c.NOTEOFF&&(this.gate=!1)):(h=this.midi_event,h.channel=this.properties.channel,this.properties.cmd&&this.properties.cmd.constructor===String?h.setCommandFromString(this.properties.cmd):h.cmd=this.properties.cmd,h.data[0]=h.cmd|h.channel,h.data[1]=Number(this.properties.value1),h.data[2]=Number(this.properties.value2),this.trigger("on_midi",h))};B.prototype.onExecute=function(){var e=this.properties;if(this.inputs)for(var h=0;hthis.properties.max_value)return;this.trigger("on_midi",h)}};v.registerNodeType("midi/filter",h);D.title="MIDIEvent";D.desc="Create a MIDI Event";D.color="#243";D.prototype.onAction=function(e,h){"assign"==e?(this.properties.channel=h.channel,this.properties.cmd=h.cmd,this.properties.value1=h.data[1],this.properties.value2=h.data[2],h.cmd== +c.NOTEON?this.gate=!0:h.cmd==c.NOTEOFF&&(this.gate=!1)):(h=this.midi_event,h.channel=this.properties.channel,this.properties.cmd&&this.properties.cmd.constructor===String?h.setCommandFromString(this.properties.cmd):h.cmd=this.properties.cmd,h.data[0]=h.cmd|h.channel,h.data[1]=Number(this.properties.value1),h.data[2]=Number(this.properties.value2),this.trigger("on_midi",h))};D.prototype.onExecute=function(){var e=this.properties;if(this.inputs)for(var h=0;hc;++c)this.valid_notes[c]=-1!=this.notes_pitches.indexOf(c);for(c=0;12>c;++c)if(this.valid_notes[c])this.offset_notes[c]=0;else for(var e=1;12>e;++e){if(this.valid_notes[(c-e)%12]){this.offset_notes[c]=-e;break}if(this.valid_notes[(c+e)%12]){this.offset_notes[c]=e;break}}};E.prototype.onAction=function(e,h){h&&h.constructor===c&&(h.data[0]==c.NOTEON||h.data[0]==c.NOTEOFF?(this.midi_event=new c,this.midi_event.setup(h.data),this.midi_event.data[1]+=this.offset_notes[c.note_to_index[h.note]], -this.trigger("out",this.midi_event)):this.trigger("out",h))};E.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&c!=this._current_scale&&this.processScale(c)};t.registerNodeType("midi/quantize",E);e.title="MIDI fromFile";e.desc="Plays a MIDI file";e.color="#243";e.prototype.onAction=function(c){"play"==c?this.play():"pause"==c&&(this._playing=!this._playing)};e.prototype.onPropertyChanged=function(c,e){"url"==c&&this.loadMIDIFile(e)};e.prototype.onExecute=function(){if(this._midi&& +l)}};D.prototype.onPropertyChanged=function(e,h){"cmd"==e&&(this.properties.cmd=c.computeCommandFromString(h))};D.prototype.onGetInputs=function(){return[["cmd","number"],["note","number"],["value1","number"],["value2","number"]]};D.prototype.onGetOutputs=function(){return[["midi","midi"],["on_midi",v.EVENT],["command","number"],["note","number"],["velocity","number"],["cc","number"],["cc_value","number"],["pitch","number"],["gate","bool"],["pitchbend","number"]]};v.registerNodeType("midi/event", +D);x.title="MIDICC";x.desc="gets a Controller Change";x.color="#243";x.prototype.onExecute=function(){m.input&&(this.properties.value=m.input.state.cc[this.properties.cc]);this.setOutputData(0,this.properties.value)};v.registerNodeType("midi/cc",x);B.title="MIDI Generator";B.desc="Generates a random MIDI note";B.color="#243";B.processScale=function(e){e=e.split(",");for(var h=0;hc;++c)this.valid_notes[c]=-1!=this.notes_pitches.indexOf(c);for(c=0;12>c;++c)if(this.valid_notes[c])this.offset_notes[c]=0;else for(var e=1;12>e;++e){if(this.valid_notes[(c-e)%12]){this.offset_notes[c]=-e;break}if(this.valid_notes[(c+e)%12]){this.offset_notes[c]=e;break}}};E.prototype.onAction=function(e,h){h&&h.constructor===c&&(h.data[0]==c.NOTEON||h.data[0]==c.NOTEOFF?(this.midi_event=new c,this.midi_event.setup(h.data),this.midi_event.data[1]+=this.offset_notes[c.note_to_index[h.note]], +this.trigger("out",this.midi_event)):this.trigger("out",h))};E.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&c!=this._current_scale&&this.processScale(c)};v.registerNodeType("midi/quantize",E);e.title="MIDI fromFile";e.desc="Plays a MIDI file";e.color="#243";e.prototype.onAction=function(c){"play"==c?this.play():"pause"==c&&(this._playing=!this._playing)};e.prototype.onPropertyChanged=function(c,e){"url"==c&&this.loadMIDIFile(e)};e.prototype.onExecute=function(){if(this._midi&& this._playing){this._current_time+=this.graph.elapsed_time;for(var e=100*this._current_time,h=0;hb;b++)for(var d=0;df+g||c[1]>d))return b}}return-1};F.prototype.onAction=function(e,h){if("reset"==e)for(var l=0;lh[1])){var l=this.getKeyIndex(h);this.keys[l]=!0;this._last_key=l;var l=12*(this.properties.start_octave-1)+29+l,a=new c;a.setup([c.NOTEON,l,100]);this.trigger("note",a);return!0}};F.prototype.onMouseMove=function(e,h){if(!(0>h[1]||-1==this._last_key)){this.setDirtyCanvas(!0); var l=this.getKeyIndex(h);if(this._last_key==l)return!0;this.keys[this._last_key]=!1;var a=12*(this.properties.start_octave-1)+29+this._last_key,b=new c;b.setup([c.NOTEOFF,a,100]);this.trigger("note",b);this.keys[l]=!0;a=12*(this.properties.start_octave-1)+29+l;b=new c;b.setup([c.NOTEON,a,100]);this.trigger("note",b);this._last_key=l;return!0}};F.prototype.onMouseUp=function(e,h){if(!(0>h[1])){var l=this.getKeyIndex(h);this.keys[l]=!1;this._last_key=-1;var l=12*(this.properties.start_octave-1)+29+ -l,a=new c;a.setup([c.NOTEOFF,l,100]);this.trigger("note",a);return!0}};t.registerNodeType("midi/keys",F)})(this); -(function(D){function c(){this.properties={src:"",gain:0.5,loop:!0,autoplay:!0,playbackRate:1};this._loading_audio=!1;this._audiobuffer=null;this._audionodes=[];this._last_sourcenode=null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=s.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain;this.properties.src&&this.loadSound(this.properties.src)}function m(){this.properties={gain:0.5};this._audionodes=[];this._media_stream= -null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=s.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain}function n(){this.properties={fftSize:2048,minDecibels:-100,maxDecibels:-10,smoothingTimeConstant:0.5};this.audionode=s.getAudioContext().createAnalyser();this.audionode.graphnode=this;this.audionode.fftSize=this.properties.fftSize;this.audionode.minDecibels=this.properties.minDecibels;this.audionode.maxDecibels= -this.properties.maxDecibels;this.audionode.smoothingTimeConstant=this.properties.smoothingTimeConstant;this.addInput("in","audio");this.addOutput("freqs","array");this.addOutput("samples","array");this._time_bin=this._freq_bin=null}function k(){this.properties={gain:1};this.audionode=s.getAudioContext().createGain();this.addInput("in","audio");this.addInput("gain","number");this.addOutput("out","audio")}function v(){this.properties={impulse_src:"",normalize:!0};this.audionode=s.getAudioContext().createConvolver(); -this.addInput("in","audio");this.addOutput("out","audio")}function h(){this.properties={threshold:-50,knee:40,ratio:12,reduction:-20,attack:0,release:0.25};this.audionode=s.getAudioContext().createDynamicsCompressor();this.addInput("in","audio");this.addOutput("out","audio")}function B(){this.properties={};this.audionode=s.getAudioContext().createWaveShaper();this.addInput("in","audio");this.addInput("shape","waveshape");this.addOutput("out","audio")}function y(){this.properties={gain1:0.5,gain2:0.5}; -this.audionode=s.getAudioContext().createGain();this.audionode1=s.getAudioContext().createGain();this.audionode1.gain.value=this.properties.gain1;this.audionode2=s.getAudioContext().createGain();this.audionode2.gain.value=this.properties.gain2;this.audionode1.connect(this.audionode);this.audionode2.connect(this.audionode);this.addInput("in1","audio");this.addInput("in1 gain","number");this.addInput("in2","audio");this.addInput("in2 gain","number");this.addOutput("out","audio")}function C(){this.properties= -{A:0.1,D:0.1,S:0.1,R:0.1};this.audionode=s.getAudioContext().createGain();this.audionode.gain.value=0;this.addInput("in","audio");this.addInput("gate","bool");this.addOutput("out","audio");this.gate=!1}function G(){this.properties={delayTime:0.5};this.audionode=s.getAudioContext().createDelay(10);this.audionode.delayTime.value=this.properties.delayTime;this.addInput("in","audio");this.addInput("time","number");this.addOutput("out","audio")}function E(){this.properties={frequency:350,detune:0,Q:1}; -this.addProperty("type","lowpass","enum",{values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")});this.audionode=s.getAudioContext().createBiquadFilter();this.addInput("in","audio");this.addOutput("out","audio")}function e(){this.properties={frequency:440,detune:0,type:"sine"};this.addProperty("type","sine","enum",{values:["sine","square","sawtooth","triangle","custom"]});this.audionode=s.getAudioContext().createOscillator();this.addOutput("out","audio")}function A(){this.properties= -{continuous:!0,mark:-1};this.addInput("data","array");this.addInput("mark","number");this.size=[300,200];this._last_buffer=null}function F(){this.properties={band:440,amplitude:1};this.addInput("freqs","array");this.addOutput("signal","number")}function t(){if(!t.default_code){var c=t.default_function.toString(),a=c.indexOf("{")+1,b=c.lastIndexOf("}");t.default_code=c.substr(a,b-a)}this.properties={code:t.default_code};c=s.getAudioContext();c.createScriptProcessor?this.audionode=c.createScriptProcessor(4096, -1,1):(console.warn("ScriptProcessorNode deprecated"),this.audionode=c.createGain());this.processCode();t._bypass_function||(t._bypass_function=this.audionode.onaudioprocess);this.addInput("in","audio");this.addOutput("out","audio")}function H(){this.audionode=s.getAudioContext().destination;this.addInput("in","audio")}var p=D.LiteGraph,s={};D.LGAudio=s;s.getAudioContext=function(){if(!this._audio_context){window.AudioContext=window.AudioContext||window.webkitAudioContext;if(!window.AudioContext)return console.error("AudioContext not supported by browser"), -null;this._audio_context=new AudioContext;this._audio_context.onmessage=function(c){console.log("msg",c)};this._audio_context.onended=function(c){console.log("ended",c)};this._audio_context.oncomplete=function(c){console.log("complete",c)}}return this._audio_context};s.connect=function(c,a){try{c.connect(a)}catch(b){console.warn("LGraphAudio:",b)}};s.disconnect=function(c,a){try{c.disconnect(a)}catch(b){console.warn("LGraphAudio:",b)}};s.changeAllAudiosConnections=function(c,a){if(c.inputs)for(var b= -0;b=this.size[0]&&(e=this.size[0]-1),c.strokeStyle="red",c.beginPath(),c.moveTo(e,d),c.lineTo(e,0),c.stroke())}};A.title="Visualization";A.desc="Audio Visualization";p.registerNodeType("audio/visualization",A);F.prototype.onExecute=function(){if(this._freqs=this.getInputData(0)){var c=this.properties.band,a=this.getInputData(1); -void 0!==a&&(c=a);a=s.getAudioContext().sampleRate/this._freqs.length;a=c/a*2;a>=this._freqs.length?a=this._freqs[this._freqs.length-1]:(c=a|0,a-=c,a=this._freqs[c]*(1-a)+this._freqs[c+1]*a);this.setOutputData(0,a/255*this.properties.amplitude)}};F.prototype.onGetInputs=function(){return[["band","number"]]};F.title="Signal";F.desc="extract the signal of some frequency";p.registerNodeType("audio/signal",F);t.prototype.onAdded=function(c){c.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess= -this._callback)};t["@code"]={widget:"code",type:"code"};t.prototype.onStart=function(){this.audionode.onaudioprocess=this._callback};t.prototype.onStop=function(){this.audionode.onaudioprocess=t._bypass_function};t.prototype.onPause=function(){this.audionode.onaudioprocess=t._bypass_function};t.prototype.onUnpause=function(){this.audionode.onaudioprocess=this._callback};t.prototype.onExecute=function(){};t.prototype.onRemoved=function(){this.audionode.onaudioprocess=t._bypass_function};t.prototype.processCode= -function(){try{this._script=new new Function("properties",this.properties.code)(this.properties),this._old_code=this.properties.code,this._callback=this._script.onaudioprocess}catch(c){console.error("Error in onaudioprocess code",c),this._callback=t._bypass_function,this.audionode.onaudioprocess=this._callback}};t.prototype.onPropertyChanged=function(c,a){"code"==c&&(this.properties.code=a,this.processCode(),this.graph&&this.graph.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback))}; -t.default_function=function(){this.onaudioprocess=function(c){var a=c.inputBuffer;c=c.outputBuffer;for(var b=0;b=this.size[0]&&(e=this.size[0]-1),c.strokeStyle="red",c.beginPath(),c.moveTo(e,d),c.lineTo(e,0),c.stroke())}};p.title="Visualization";p.desc="Audio Visualization";w.registerNodeType("audio/visualization",p);F.prototype.onExecute=function(){if(this._freqs=this.getInputData(0)){var c=this.properties.band,a=this.getInputData(1); +void 0!==a&&(c=a);a=t.getAudioContext().sampleRate/this._freqs.length;a=c/a*2;a>=this._freqs.length?a=this._freqs[this._freqs.length-1]:(c=a|0,a-=c,a=this._freqs[c]*(1-a)+this._freqs[c+1]*a);this.setOutputData(0,a/255*this.properties.amplitude)}};F.prototype.onGetInputs=function(){return[["band","number"]]};F.title="Signal";F.desc="extract the signal of some frequency";w.registerNodeType("audio/signal",F);v.prototype.onAdded=function(c){c.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess= +this._callback)};v["@code"]={widget:"code",type:"code"};v.prototype.onStart=function(){this.audionode.onaudioprocess=this._callback};v.prototype.onStop=function(){this.audionode.onaudioprocess=v._bypass_function};v.prototype.onPause=function(){this.audionode.onaudioprocess=v._bypass_function};v.prototype.onUnpause=function(){this.audionode.onaudioprocess=this._callback};v.prototype.onExecute=function(){};v.prototype.onRemoved=function(){this.audionode.onaudioprocess=v._bypass_function};v.prototype.processCode= +function(){try{this._script=new new Function("properties",this.properties.code)(this.properties),this._old_code=this.properties.code,this._callback=this._script.onaudioprocess}catch(c){console.error("Error in onaudioprocess code",c),this._callback=v._bypass_function,this.audionode.onaudioprocess=this._callback}};v.prototype.onPropertyChanged=function(c,a){"code"==c&&(this.properties.code=a,this.processCode(),this.graph&&this.graph.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback))}; +v.default_function=function(){this.onaudioprocess=function(c){var a=c.inputBuffer;c=c.outputBuffer;for(var b=0;bfloat -{1} = sin({0}); - -\mul -A,B,Out -T,T->T -T,float->T -{2} = {0} * {1}; - -\clamp -f,min,max,Out -float,float=0,float=1->float -vec2,vec2=vec2(0.0),vec2=vec2(1.0)->vec2 -vec3,vec3=vec3(0.0),vec3=vec3(1.0)->vec3 -vec4,vec4=vec4(0.0),vec4=vec4(1.0)->vec4 -{3}=clamp({0},{1},{2}); - -\mix -A,B,f,Out -float,float,float->float -vec2,vec2,float->vec2 -vec3,vec3,float->vec3 -vec4,vec4,float->vec4 -{3} = mix({0},{1},{2}); - -*/ \ No newline at end of file From baed06fa3706af7e78c708d27da0bdd9b0054f12 Mon Sep 17 00:00:00 2001 From: tamat Date: Sat, 25 Jul 2020 01:09:14 +0200 Subject: [PATCH 63/63] minor fixes --- build/litegraph.js | 680 ++++++++--- build/litegraph.min.js | 1445 ++++++++++++------------ editor/js/code.js | 2 +- src/litegraph.js | 10 +- src/nodes/events.js | 23 + src/nodes/{shaders.js => glshaders.js} | 588 +++++++--- src/nodes/gltextures.js | 27 +- src/nodes/math.js | 32 + utils/deploy_files.txt | 2 +- 9 files changed, 1809 insertions(+), 1000 deletions(-) rename src/nodes/{shaders.js => glshaders.js} (66%) diff --git a/build/litegraph.js b/build/litegraph.js index 4cf7040ed..c070ca683 100644 --- a/build/litegraph.js +++ b/build/litegraph.js @@ -8930,6 +8930,8 @@ LGraphNode.prototype.executeAction = function(action) //value changed if( old_value != w.value ) { + if(node.onWidgetChanged) + node.onWidgetChanged( w.name,w.value,old_value,w ); node.graph._version++; } @@ -9139,7 +9141,10 @@ LGraphNode.prototype.executeAction = function(action) var entries = []; for (var i in values) { if (values[i]) { - entries.push({ value: values[i], content: values[i], has_submenu: true }); + var name = values[i]; + if(name.indexOf("::") != -1) //in case it has a namespace like "shader::math/rand" it hides the namespace + name = name.split("::")[1]; + entries.push({ value: values[i], content: name, has_submenu: true }); } } @@ -10189,6 +10194,7 @@ LGraphNode.prototype.executeAction = function(action) { options = options || {}; var str_value = String(value); + type = type.toLowerCase(); if(type == "number") str_value = value.toFixed(3); @@ -10777,7 +10783,7 @@ LGraphNode.prototype.executeAction = function(action) } if (node.getExtraMenuOptions) { - var extra = node.getExtraMenuOptions(this); + var extra = node.getExtraMenuOptions(this, options); if (extra) { extra.push(null); options = extra.concat(options); @@ -13330,6 +13336,29 @@ if (typeof exports != "undefined") { LiteGraph.registerNodeType("events/filter", FilterEvent); + + function EventBranch() { + this.addInput("in", LiteGraph.ACTION); + this.addInput("cond", "boolean"); + this.addOutput("true", LiteGraph.EVENT); + this.addOutput("false", LiteGraph.EVENT); + this.size = [120, 60]; + this._value = false; + } + + EventBranch.title = "Branch"; + EventBranch.desc = "If condition is true, outputs triggers true, otherwise false"; + + EventBranch.prototype.onExecute = function() { + this._value = this.getInputData(1); + } + + EventBranch.prototype.onAction = function(action, param) { + this.triggerSlot(this._value ? 0 : 1); + } + + LiteGraph.registerNodeType("events/branch", EventBranch); + //Show value inside the debug console function EventCounter() { this.addInput("inc", LiteGraph.ACTION); @@ -15545,6 +15574,7 @@ if (typeof exports != "undefined") { this.addProperty("A", 1); this.addProperty("B", 1); this.addProperty("OP", ">", "enum", { values: MathCondition.values }); + this.addWidget("combo","Cond.",this.properties.OP,{ property: "OP", values: MathCondition.values } ); this.size = [80, 60]; } @@ -15612,6 +15642,37 @@ if (typeof exports != "undefined") { LiteGraph.registerNodeType("math/condition", MathCondition); + + function MathBranch() { + this.addInput("in", ""); + this.addInput("cond", "boolean"); + this.addOutput("true", ""); + this.addOutput("false", ""); + this.size = [80, 60]; + } + + MathBranch.title = "Branch"; + MathBranch.desc = "If condition is true, outputs IN in true, otherwise in false"; + + MathBranch.prototype.onExecute = function() { + var V = this.getInputData(0); + var cond = this.getInputData(1); + + if(cond) + { + this.setOutputData(0, V); + this.setOutputData(1, null); + } + else + { + this.setOutputData(0, null); + this.setOutputData(1, V); + } + } + + LiteGraph.registerNodeType("math/branch", MathBranch); + + function MathAccumulate() { this.addInput("inc", "number"); this.addOutput("total", "number"); @@ -17718,7 +17779,7 @@ if (typeof exports != "undefined") { //flags to choose output texture type LGraphTexture.UNDEFINED = 0; //not specified - LGraphTexture.PASS_THROUGH = 1; //do not apply FX + LGraphTexture.PASS_THROUGH = 1; //do not apply FX (like disable but passing the in to the out) LGraphTexture.COPY = 2; //create new texture with the same properties as the origin texture LGraphTexture.LOW = 3; //create new texture with low precision (byte) LGraphTexture.HIGH = 4; //create new texture with high precision (half-float) @@ -17799,11 +17860,12 @@ if (typeof exports != "undefined") { !target || target.width != origin.width || target.height != origin.height || - target.type != tex_type + target.type != tex_type || + target.format != origin.format ) { target = new GL.Texture(origin.width, origin.height, { type: tex_type, - format: gl.RGBA, + format: origin.format, filter: gl.LINEAR }); } @@ -22698,18 +22760,18 @@ void main(void){\n\ LGraphTexturePerlin.widgets_info = { precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }, - width: { type: "Number", precision: 0, step: 1 }, - height: { type: "Number", precision: 0, step: 1 }, - octaves: { type: "Number", precision: 0, step: 1, min: 1, max: 50 } + width: { type: "number", precision: 0, step: 1 }, + height: { type: "number", precision: 0, step: 1 }, + octaves: { type: "number", precision: 0, step: 1, min: 1, max: 50 } }; LGraphTexturePerlin.prototype.onGetInputs = function() { return [ - ["seed", "Number"], - ["persistence", "Number"], - ["octaves", "Number"], - ["scale", "Number"], - ["amplitude", "Number"], + ["seed", "number"], + ["persistence", "number"], + ["octaves", "number"], + ["scale", "number"], + ["amplitude", "number"], ["offset", "vec2"] ]; }; @@ -22880,8 +22942,8 @@ void main(void){\n\ LGraphTextureCanvas2D.widgets_info = { precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }, code: { type: "code" }, - width: { type: "Number", precision: 0, step: 1 }, - height: { type: "Number", precision: 0, step: 1 } + width: { type: "number", precision: 0, step: 1 }, + height: { type: "number", precision: 0, step: 1 } }; LGraphTextureCanvas2D.prototype.onPropertyChanged = function( name, value ) { @@ -23163,8 +23225,8 @@ void main(void){\n\ "max": "T max(T x,T y)", "clamp": "T clamp(T x,T minVal = 0.0,T maxVal = 1.0)", "mix": "T mix(T x,T y,T a)", //"T mix(T x,T y,float a)" - "step": "T step(T edge, T x)", //"T step(float edge, T x)" - "smoothstep": "T smoothstep(T edge, T x)", //"T smoothstep(float edge, T x)" + "step": "T step(T edge, T edge2, T x)", //"T step(float edge, T x)" + "smoothstep": "T smoothstep(T edge, T edge2, T x)", //"T smoothstep(float edge, T x)" "length":"float length(T x)", "distance":"float distance(T p0, T p1)", "normalize":"T normalize(T x)", @@ -23193,11 +23255,8 @@ void main(void){\n\ { var p = params[j].split(" ").filter(function(a){ return a; }); params[j] = { type: p[0].trim(), name: p[1].trim() }; - if(params[j].name.indexOf("=") != -1) - { - params[j].name.split("="); - } - + if(p[2] == "=") + params[j].value = p[3].trim(); } GLSL_functions[i] = { return_type: return_type, func: func_name, params: params }; GLSL_functions_name.push( func_name ); @@ -23225,8 +23284,13 @@ void main(void){\n\ origin_node.propagateDestination( dest_name ); } } + if(!node_ctor.prototype.onPropertyChanged) + node_ctor.prototype.onPropertyChanged = function() + { + this.graph._version++; + } - LiteGraph.registerNodeType( type, node_ctor ); + LiteGraph.registerNodeType( "shader::" + type, node_ctor ); } function getShaderNodeVarName( node, name ) @@ -23252,7 +23316,7 @@ void main(void){\n\ function getOutputLinkID( node, slot ) { - if (!node.isOutputConnected(0)) + if (!node.isOutputConnected(slot)) return null; return "link_" + node.id + "_" + slot; } @@ -23263,9 +23327,12 @@ void main(void){\n\ LGShaders.getShaderNodeVarName = getShaderNodeVarName; LGShaders.parseGLSLDescriptions = parseGLSLDescriptions; - var valueToGLSL = LiteGraph.valueToGLSL = function valueToGLSL( v, type ) + //given a const number, it transform it to a string that matches a type + var valueToGLSL = LiteGraph.valueToGLSL = function valueToGLSL( v, type, precision ) { var n = 5; //num decimals + if(precision != null) + n = precision; if(!type) { if(v.constructor === Number) @@ -23303,6 +23370,86 @@ void main(void){\n\ return ""; } + //makes sure that a var is of a type, and if not, it converts it + var varToTypeGLSL = LiteGraph.varToTypeGLSL = function varToTypeGLSL( v, input_type, output_type ) + { + if(input_type == output_type) + return v; + if(v == null) + switch(output_type) + { + case "float": return "0.0"; + case "vec2": return "vec2(0.0)"; + case "vec3": return "vec3(0.0)"; + case "vec4": return "vec4(0.0,0.0,0.0,1.0)"; + default: //null + return null; + } + + if(!output_type) + throw("error: no output type specified"); + if(output_type == "float") + { + switch(input_type) + { + //case "float": + case "vec2": + case "vec3": + case "vec4": + return v + ".x"; + break; + default: //null + return "0.0"; + break; + } + } + else if(output_type == "vec2") + { + switch(input_type) + { + case "float": + return "vec2("+v+")"; + //case "vec2": + case "vec3": + case "vec4": + return v + ".xy"; + default: //null + return "vec2(0.0)"; + } + } + else if(output_type == "vec3") + { + switch(input_type) + { + case "float": + return "vec3("+v+")"; + case "vec2": + return "vec3(" + v + ",0.0)"; + //case "vec3": + case "vec4": + return v + ".xyz"; + default: //null + return "vec3(0.0)"; + } + } + else if(output_type == "vec4") + { + switch(input_type) + { + case "float": + return "vec4("+v+")"; + case "vec2": + return "vec4(" + v + ",0.0,1.0)"; + case "vec3": + return "vec4(" + v + ",1.0)"; + default: //null + return "vec4(0.0,0.0,0.0,1.0)"; + } + } + throw("type cannot be converted"); + } + + //used to plug incompatible stuff var convertVarToGLSLType = LiteGraph.convertVarToGLSLType = function convertVarToGLSLType( varname, type, target_type ) { @@ -23332,21 +23479,47 @@ void main(void){\n\ //used to host a shader body ************************************** function LGShaderContext() { + //to store the code template this.vs_template = ""; this.fs_template = ""; + + //required so nodes now where to fetch the input data + this.buffer_names = { + uvs: "v_coord" + }; + + this.extra = {}; //to store custom info from the nodes (like if this shader supports a feature, etc) + + this._functions = {}; this._uniforms = {}; this._codeparts = {}; + this._uniform_value = null; } LGShaderContext.prototype.clear = function() { this._uniforms = {}; + this._functions = {}; this._codeparts = {}; + this._uniform_value = null; + + this.extra = {}; } - LGShaderContext.prototype.addUniform = function( name, type ) + LGShaderContext.prototype.addUniform = function( name, type, value ) { this._uniforms[ name ] = type; + if(value != null) + { + if(!this._uniform_value) + this._uniform_value = {}; + this._uniform_value[name] = value; + } + } + + LGShaderContext.prototype.addFunction = function( name, code ) + { + this._functions[name] = code; } LGShaderContext.prototype.addCode = function( hook, code, destinations ) @@ -23362,17 +23535,53 @@ void main(void){\n\ } } - LGShaderContext.prototype.computeShaderCode = function() + //the system works by grabbing code fragments from every node and concatenating them in blocks depending on where must they be attached + LGShaderContext.prototype.computeCodeBlocks = function( graph, extra_uniforms ) { + //prepare context + this.clear(); + + //grab output nodes + var vertexout = graph.findNodesByType("shader::output/vertex"); + vertexout = vertexout && vertexout.length ? vertexout[0] : null; + var fragmentout = graph.findNodesByType("shader::output/fragcolor"); + fragmentout = fragmentout && fragmentout.length ? fragmentout[0] : null; + if(!fragmentout) //?? + return null; + + //propagate back destinations + graph.sendEventToAllNodes( "clearDestination" ); + if(vertexout) + vertexout.propagateDestination("vs"); + if(fragmentout) + fragmentout.propagateDestination("fs"); + + //gets code from graph + graph.sendEventToAllNodes("onGetCode", this ); + var uniforms = ""; for(var i in this._uniforms) uniforms += "uniform " + this._uniforms[i] + " " + i + ";\n"; + if(extra_uniforms) + for(var i in extra_uniforms) + uniforms += "uniform " + extra_uniforms[i] + " " + i + ";\n"; - var parts = this._codeparts; - parts.uniforms = uniforms; + var functions = ""; + for(var i in this._functions) + functions += "//" + i + "\n" + this._functions[i] + "\n"; - var vs_code = GL.Shader.replaceCodeUsingContext( this.vs_template, parts ); - var fs_code = GL.Shader.replaceCodeUsingContext( this.fs_template, parts ); + var blocks = this._codeparts; + blocks.uniforms = uniforms; + blocks.functions = functions; + return blocks; + } + + //replaces blocks using the vs and fs template and returns the final codes + LGShaderContext.prototype.computeShaderCode = function( graph ) + { + var blocks = this.computeCodeBlocks( graph ); + var vs_code = GL.Shader.replaceCodeUsingContext( this.vs_template, blocks ); + var fs_code = GL.Shader.replaceCodeUsingContext( this.fs_template, blocks ); return { vs_code: vs_code, fs_code: fs_code @@ -23380,11 +23589,22 @@ void main(void){\n\ } //generates the shader code from the template and the - LGShaderContext.prototype.computeShader = function( shader ) + LGShaderContext.prototype.computeShader = function( graph, shader ) { - var finalcode = this.computeShaderCode(); + var finalcode = this.computeShaderCode( graph ); console.log( finalcode.vs_code, finalcode.fs_code ); + if(!LiteGraph.catch_exceptions) + { + this._shader_error = true; + if(shader) + shader.updateShader( finalcode.vs_code, finalcode.fs_code ); + else + shader = new GL.Shader( finalcode.vs_code, finalcode.fs_code ); + this._shader_error = false; + return shader; + } + try { if(shader) @@ -23400,7 +23620,7 @@ void main(void){\n\ { console.error(err); if(err.indexOf("Fragment shader") != -1) - console.log( finalcode.fs_code ); + console.log( finalcode.fs_code.split("\n").map(function(v,i){ return i + ".- " + v; }).join("\n") ); else console.log( finalcode.vs_code ); } @@ -23411,38 +23631,45 @@ void main(void){\n\ return null;//never here } - //represents a fragment of code exported by a node - /* - function LGShaderCodeBlock( node, code, uniforms ) + LGShaderContext.prototype.getShader = function( graph ) { - this.node = node || null; - this.uniforms = uniforms || null; - this.parts = {}; - if(code) + //if graph not changed? + if(this._shader && this._shader._version == graph._version) + return this._shader; + + //compile shader + var shader = this.computeShader( graph, this._shader ); + if(!shader) + return null; + + this._shader = shader; + shader._version = graph._version; + return shader; + } + + //some shader nodes could require to fill the box with some uniforms + LGShaderContext.prototype.fillUniforms = function( uniforms, param ) + { + if(!this._uniform_value) + return; + + for(var i in this._uniform_value) { - if(code.constructor === String) - this.parts.code = code; + var v = this._uniform_value[i]; + if(v == null) + continue; + if(v.constructor === Function) + uniforms[i] = v.call( this, param ); + else if(v.constructor === GL.Texture) + { + //todo... + } else - this.parts = code; + uniforms[i] = v; } } - LGShaderCodeBlock.prototype.addUniform = function( name, type ) - { - if(!this.uniforms) - this.uniforms = {}; - this.uniforms[ name ] = type; - } - - LGShaderCodeBlock.prototype.addCode = function( hook, code ) - { - if(!this.parts[ hook ]) - this.parts[ hook ] = code + "\n"; - else - this.parts[ hook ] += code + "\n"; - } - */ - + LiteGraph.ShaderContext = LiteGraph.Shaders.Context = LGShaderContext; // LGraphShaderGraph ***************************** // applies a shader graph to texture, it can be uses as an example @@ -23459,14 +23686,14 @@ void main(void){\n\ this.addOutput("out", "texture"); this.properties = { width: 0, height: 0, alpha: false, precision: typeof(LGraphTexture) != "undefined" ? LGraphTexture.DEFAULT : 2 }; - var inputNode = this.subgraph.findNodesByType("input/uniform")[0]; + var inputNode = this.subgraph.findNodesByType("shader::input/uniform")[0]; inputNode.pos = [200,300]; - var sampler = LiteGraph.createNode("texture/sampler2D"); + var sampler = LiteGraph.createNode("shader::texture/sampler2D"); sampler.pos = [400,300]; this.subgraph.add( sampler ); - var outnode = LiteGraph.createNode("output/fragcolor"); + var outnode = LiteGraph.createNode("shader::output/fragcolor"); outnode.pos = [600,300]; this.subgraph.add( outnode ); @@ -23479,21 +23706,24 @@ void main(void){\n\ this._uniforms = {}; this._shader = null; this._context = new LGShaderContext(); - this._context.vs_template = GL.Shader.SCREEN_VERTEX_SHADER; + this._context.vs_template = "#define VERTEX\n" + GL.Shader.SCREEN_VERTEX_SHADER; this._context.fs_template = LGraphShaderGraph.template; } LGraphShaderGraph.template = "\n\ +#define FRAGMENT\n\ precision highp float;\n\ varying vec2 v_coord;\n\ {{varying}}\n\ {{uniforms}}\n\ +{{functions}}\n\ {{fs_functions}}\n\ void main() {\n\n\ vec2 uv = v_coord;\n\ -vec4 color = vec4(0.0);\n\ +vec4 fragcolor = vec4(0.0);\n\ +vec4 fragcolor1 = vec4(0.0);\n\ {{fs_code}}\n\ -gl_FragColor = color;\n\ +gl_FragColor = fragcolor;\n\ }\n\ "; @@ -23546,11 +23776,12 @@ gl_FragColor = color;\n\ }); } - var shader = this.getShader(); + var shader = this.getShader( this.subgraph ); if(!shader) return; var uniforms = this._uniforms; + this._context.fillUniforms( uniforms ); var tex_slot = 0; if(this.inputs) @@ -23586,7 +23817,7 @@ gl_FragColor = color;\n\ //add input node inside subgraph LGraphShaderGraph.prototype.onInputAdded = function( slot_info ) { - var subnode = LiteGraph.createNode("input/uniform"); + var subnode = LiteGraph.createNode("shader::input/uniform"); subnode.setProperty("name",slot_info.name); subnode.setProperty("type",slot_info.type); this.subgraph.add( subnode ); @@ -23595,7 +23826,7 @@ gl_FragColor = color;\n\ //remove all LGraphShaderGraph.prototype.onInputRemoved = function( slot, slot_info ) { - var nodes = this.subgraph.findNodesByType("input/uniform"); + var nodes = this.subgraph.findNodesByType("shader::input/uniform"); for(var i = 0; i < nodes.length; ++i) { var node = nodes[i]; @@ -23613,45 +23844,11 @@ gl_FragColor = color;\n\ LGraphShaderGraph.prototype.getShader = function() { - //if subgraph not changed? - if(this._shader && this._shader._version == this.subgraph._version) - return this._shader; - - //prepare context - this._context.clear(); - - //grab output nodes - var vertexout = this.subgraph.findNodesByType("output/vertex"); - vertexout = vertexout && vertexout.length ? vertexout[0] : null; - var fragmentout = this.subgraph.findNodesByType("output/fragcolor"); - fragmentout = fragmentout && fragmentout.length ? fragmentout[0] : null; - - if(!fragmentout) //?? - return null; - - this.subgraph.sendEventToAllNodes( "clearDestination" ); - - //propagate back destinations - if(vertexout) - vertexout.propagateDestination("vs"); - if(fragmentout) - fragmentout.propagateDestination("fs"); - - //gets code from graph - this.subgraph.sendEventToAllNodes("onGetCode", this._context ); - - //compile shader - var shader = this._context.computeShader(); + var shader = this._context.getShader( this.subgraph ); if(!shader) - { this.boxcolor = "red"; - return this._shader; - } else this.boxcolor = null; - - this._shader = shader; - shader._version = this.subgraph._version; return shader; } @@ -23695,6 +23892,11 @@ gl_FragColor = color;\n\ } } + LGraphShaderGraph.prototype.onDrawSubgraphBackground = function(graphcanvas) + { + //TODO + } + LGraphShaderGraph.prototype.getExtraMenuOptions = function(graphcanvas) { var that = this; @@ -23813,7 +24015,7 @@ gl_FragColor = color;\n\ var code = "vec4 " + varname + " = vec4(0.0);\n"; if(texname) { - var uvname = getInputLinkID( this, 1 ) || "v_coord"; + var uvname = getInputLinkID( this, 1 ) || context.buffer_names.uvs; code += varname + " = texture2D("+texname+","+uvname+");\n"; } @@ -23852,7 +24054,7 @@ gl_FragColor = color;\n\ LGraphShaderConstant.prototype.getTitle = function() { if(this.flags.collapsed) - return valueToGLSL( this.properties.value, this.properties.type ); + return valueToGLSL( this.properties.value, this.properties.type, 2 ); return "Const"; } @@ -23865,8 +24067,21 @@ gl_FragColor = color;\n\ { this.disconnectOutput(0); this.outputs[0].type = value; - this.widgets.length = 1; //remove extra widgets - this.updateWidgets(); + } + this.widgets.length = 1; //remove extra widgets + this.updateWidgets(); + } + if(name == "value") + { + if(!value.length) + this.widgets[1].value = value; + else + { + this.widgets[1].value = value[1]; + if(value.length > 2) + this.widgets[2].value = value[2]; + if(value.length > 3) + this.widgets[3].value = value[3]; } } } @@ -23884,21 +24099,21 @@ gl_FragColor = color;\n\ break; case 'vec2': this.properties.value = old_value && old_value.length == 2 ? [old_value[0],old_value[1]] : [0,0,0]; - this.addWidget("number","x",0,options, function(v){ that.properties.value[0] = v; }); - this.addWidget("number","y",0,options, function(v){ that.properties.value[1] = v; }); + this.addWidget("number","x",this.properties.value[0], function(v){ that.properties.value[0] = v; },options); + this.addWidget("number","y",this.properties.value[1], function(v){ that.properties.value[1] = v; },options); break; case 'vec3': this.properties.value = old_value && old_value.length == 3 ? [old_value[0],old_value[1],old_value[2]] : [0,0,0]; - this.addWidget("number","x",0,options, function(v){ that.properties.value[0] = v; }); - this.addWidget("number","y",0,options, function(v){ that.properties.value[1] = v; }); - this.addWidget("number","z",0,options, function(v){ that.properties.value[2] = v; }); + this.addWidget("number","x",this.properties.value[0], function(v){ that.properties.value[0] = v; },options); + this.addWidget("number","y",this.properties.value[1], function(v){ that.properties.value[1] = v; },options); + this.addWidget("number","z",this.properties.value[2], function(v){ that.properties.value[2] = v; },options); break; case 'vec4': this.properties.value = old_value && old_value.length == 4 ? [old_value[0],old_value[1],old_value[2],old_value[3]] : [0,0,0,0]; - this.addWidget("number","x",0,options, function(v){ that.properties.value[0] = v; }); - this.addWidget("number","y",0,options, function(v){ that.properties.value[1] = v; }); - this.addWidget("number","z",0,options, function(v){ that.properties.value[2] = v; }); - this.addWidget("number","w",0,options, function(v){ that.properties.value[3] = v; }); + this.addWidget("number","x",this.properties.value[0], function(v){ that.properties.value[0] = v; },options); + this.addWidget("number","y",this.properties.value[1], function(v){ that.properties.value[1] = v; },options); + this.addWidget("number","z",this.properties.value[2], function(v){ that.properties.value[2] = v; },options); + this.addWidget("number","w",this.properties.value[3], function(v){ that.properties.value[3] = v; },options); break; default: console.error("unknown type for constant"); @@ -24118,26 +24333,116 @@ gl_FragColor = color;\n\ var link_name = getInputLinkID( this, 0 ); if(!link_name) return; - - var code = link_name; - var type = this.getInputDataType(0); - if(type == "float") - code = "vec4(" + code + ");"; - else if(type == "vec2") - code = "vec4(" + code + ",0.0,1.0);"; - else if(type == "vec3") - code = "vec4(" + code + ",1.0);"; - - context.addCode("fs_code", "color = " + code + ";"); + var type = this.getInputData(0); + var code = varToTypeGLSL( link_name, type, "vec4" ); + context.addCode("fs_code", "fragcolor = " + code + ";"); } registerShaderNode( "output/fragcolor", LGraphShaderFragColor ); + /* + function LGraphShaderDiscard() + { + this.addInput("v","T"); + this.addInput("min","T"); + this.properties = { min_value: 0.0 }; + this.addWidget("number","min",0,{ step: 0.01, property: "min_value" }); + } + + LGraphShaderDiscard.title = "Discard"; + + LGraphShaderDiscard.prototype.onGetCode = function( context ) + { + if(!this.isOutputConnected(0)) + return; + + var inlink = getInputLinkID(this,0); + var inlink1 = getInputLinkID(this,1); + + if(!inlink && !inlink1) //not connected + return; + context.addCode("code", return_type + " " + outlink + " = ( (" + inlink + " - "+minv+") / ("+ maxv+" - "+minv+") ) * ("+ maxv2+" - "+minv2+") + " + minv2 + ";", this.shader_destination ); + this.setOutputData( 0, return_type ); + } + + registerShaderNode( "output/discard", LGraphShaderDiscard ); + */ + // ************************************************* - function LGraphShaderMath() + function LGraphShaderOperation() + { + this.addInput("A","float,vec2,vec3,vec4"); + this.addInput("B","float,vec2,vec3,vec4"); + this.addOutput("out",""); + this.properties = { + operation: "*" + }; + this.addWidget("combo","op.",this.properties.operation,{ property: "operation", values: LGraphShaderOperation.operations }); + } + + LGraphShaderOperation.title = "Operation"; + LGraphShaderOperation.operations = ["+","-","*","/"]; + + LGraphShaderOperation.prototype.getTitle = function() + { + if(this.flags.collapsed) + return "A" + this.properties.operation + "B"; + else + return "Operation"; + } + + LGraphShaderOperation.prototype.onGetCode = function( context ) + { + if(!this.isOutputConnected(0)) + return; + + var inlinks = []; + for(var i = 0; i < 3; ++i) + inlinks.push( { name: getInputLinkID(this,i), type: this.getInputData(i) || "float" } ); + + var outlink = getOutputLinkID(this,0); + if(!outlink) //not connected + return; + + //func_desc + var base_type = inlinks[0].type; + var return_type = base_type; + var op = this.properties.operation; + + var params = []; + for(var i = 0; i < 2; ++i) + { + var param_code = inlinks[i].name; + if(param_code == null) //not plugged + { + param_code = p.value != null ? p.value : "(1.0)"; + inlinks[i].type = "float"; + } + + //convert + if( inlinks[i].type != base_type ) + { + if( inlinks[i].type == "float" && (op == "*" || op == "/") ) + { + //I find hard to create the opposite condition now, so I prefeer an else + } + else + param_code = convertVarToGLSLType( param_code, inlinks[i].type, base_type ); + } + params.push( param_code ); + } + + context.addCode("code", return_type + " " + outlink + " = "+ params[0] + op + params[1] + ";", this.shader_destination ); + this.setOutputData( 0, return_type ); + } + + registerShaderNode( "math/operation", LGraphShaderOperation ); + + + function LGraphShaderFunc() { this.addInput("A","float,vec2,vec3,vec4"); this.addInput("B","float,vec2,vec3,vec4"); @@ -24149,9 +24454,9 @@ gl_FragColor = color;\n\ this.addWidget("combo","func",this.properties.func,{ property: "func", values: GLSL_functions_name }); } - LGraphShaderMath.title = "Math"; + LGraphShaderFunc.title = "Func"; - LGraphShaderMath.prototype.onPropertyChanged = function(name,value) + LGraphShaderFunc.prototype.onPropertyChanged = function(name,value) { this.graph._version++; @@ -24167,14 +24472,17 @@ gl_FragColor = color;\n\ //add and update inputs for(var i = 0; i < func_desc.params.length; ++i) + { + var p = func_desc.params[i]; if( this.inputs[i] ) - this.inputs[i].name = func_desc.params[i].name; + this.inputs[i].name = p.name + (p.value ? " (" + p.value + ")" : ""); else - this.addInput( func_desc.params[i].name, "float,vec2,vec3,vec4" ); + this.addInput( p.name, "float,vec2,vec3,vec4" ); + } } } - LGraphShaderMath.prototype.getTitle = function() + LGraphShaderFunc.prototype.getTitle = function() { if(this.flags.collapsed) return this.properties.func; @@ -24182,7 +24490,7 @@ gl_FragColor = color;\n\ return "Func"; } - LGraphShaderMath.prototype.onGetCode = function( context ) + LGraphShaderFunc.prototype.onGetCode = function( context ) { if(!this.isOutputConnected(0)) return; @@ -24212,12 +24520,12 @@ gl_FragColor = color;\n\ var param_code = inlinks[i].name; if(param_code == null) //not plugged { - param_code = "(1.0)"; + param_code = p.value != null ? p.value : "(1.0)"; inlinks[i].type = "float"; } if( (p.type == "T" && inlinks[i].type != base_type) || (p.type != "T" && inlinks[i].type != base_type) ) - param_code = convertVarToGLSLType( inlinks[i].name, inlinks[i].type, base_type ); + param_code = convertVarToGLSLType( param_code, inlinks[i].type, base_type ); params.push( param_code ); } @@ -24226,7 +24534,7 @@ gl_FragColor = color;\n\ this.setOutputData( 0, return_type ); } - registerShaderNode( "math/func", LGraphShaderMath ); + registerShaderNode( "math/func", LGraphShaderFunc ); @@ -24304,12 +24612,102 @@ gl_FragColor = color;\n\ registerShaderNode( "utils/snippet", LGraphShaderSnippet ); - //************************************ + + function LGraphShaderRand() + { + this.addOutput("out","float"); + } + + LGraphShaderRand.title = "Rand"; + + LGraphShaderRand.prototype.onGetCode = function( context ) + { + if(!this.isOutputConnected(0)) + return; + + var outlink = getOutputLinkID(this,0); + + context.addUniform( "u_rand" + this.id, "float", function(){ return Math.random(); }); + context.addCode("code", "float " + outlink + " = u_rand" + this.id +";", this.shader_destination ); + this.setOutputData( 0, "float" ); + } + + registerShaderNode( "input/rand", LGraphShaderRand ); + + //noise? + //https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83 + + + function LGraphShaderTime() + { + this.addOutput("out","float"); + } + + LGraphShaderTime.title = "Time"; + + LGraphShaderTime.prototype.onGetCode = function( context ) + { + if(!this.isOutputConnected(0)) + return; + + var outlink = getOutputLinkID(this,0); + + context.addUniform( "u_time" + this.id, "float", function(){ return getTime() * 0.001; }); + context.addCode("code", "float " + outlink + " = u_time" + this.id +";", this.shader_destination ); + this.setOutputData( 0, "float" ); + } + + registerShaderNode( "input/time", LGraphShaderTime ); + + + function LGraphShaderDither() + { + this.addInput("in","T"); + this.addOutput("out","float"); + } + + LGraphShaderDither.title = "Dither"; + + LGraphShaderDither.prototype.onGetCode = function( context ) + { + if(!this.isOutputConnected(0)) + return; + var inlink = getInputLinkID(this,0); + var return_type = "float"; + var outlink = getOutputLinkID(this,0); + var intype = this.getInputData(0); + inlink = varToTypeGLSL( inlink, intype, "float" ); + context.addFunction("dither8x8", LGraphShaderDither.dither_func); + context.addCode("code", return_type + " " + outlink + " = dither8x8("+ inlink +");", this.shader_destination ); + this.setOutputData( 0, return_type ); + } + + LGraphShaderDither.dither_values = [0.515625,0.140625,0.640625,0.046875,0.546875,0.171875,0.671875,0.765625,0.265625,0.890625,0.390625,0.796875,0.296875,0.921875,0.421875,0.203125,0.703125,0.078125,0.578125,0.234375,0.734375,0.109375,0.609375,0.953125,0.453125,0.828125,0.328125,0.984375,0.484375,0.859375,0.359375,0.0625,0.5625,0.1875,0.6875,0.03125,0.53125,0.15625,0.65625,0.8125,0.3125,0.9375,0.4375,0.78125,0.28125,0.90625,0.40625,0.25,0.75,0.125,0.625,0.21875,0.71875,0.09375,0.59375,1.0001,0.5,0.875,0.375,0.96875,0.46875,0.84375,0.34375]; + + LGraphShaderDither.dither_func = "\n\ + float dither8x8(float brightness) {\n\ + vec2 position = vec2(0.0);\n\ + #ifdef FRAGMENT\n\ + position = gl_FragCoord.xy;\n\ + #endif\n\ + int x = int(mod(position.x, 8.0));\n\ + int y = int(mod(position.y, 8.0));\n\ + int index = x + y * 8;\n\ + float limit = 0.0;\n\ + if (x < 8) {\n\ + if(index==0) limit = 0.015625;\n\ + "+(LGraphShaderDither.dither_values.map( function(v,i){ return "else if(index== "+(i+1)+") limit = " + v + ";"}).join("\n"))+"\n\ + }\n\ + return brightness < limit ? 0.0 : 1.0;\n\ + }\n", + + registerShaderNode( "math/dither", LGraphShaderDither ); + function LGraphShaderRemap() { - this.addInput("","T,float,vec2,vec3,vec4"); - this.addOutput("","T"); + this.addInput("","float,vec2,vec3,vec4"); + this.addOutput("",""); this.properties = { min_value: 0, max_value: 1, diff --git a/build/litegraph.min.js b/build/litegraph.min.js index 5381398b1..881d76f93 100755 --- a/build/litegraph.min.js +++ b/build/litegraph.min.js @@ -1,44 +1,44 @@ -(function(C){function c(a){e.debug&&console.log("Graph created");this.list_of_graphcanvas=null;this.clear();a&&this.configure(a)}function m(a,b,d,f,g,q){this.id=a;this.type=b;this.origin_id=d;this.origin_slot=f;this.target_id=g;this.target_slot=q;this._data=null;this._pos=new Float32Array(2)}function n(a){this._ctor(a)}function k(a){this._ctor(a)}function y(a,b){this.offset=new Float32Array([0,0]);this.scale=1;this.max_scale=10;this.min_scale=0.1;this.onredraw=null;this.enabled=!0;this.last_mouse= -[0,0];this.element=null;this.visible_area=new Float32Array(4);a&&(this.element=a,b||this.bindEvents(a))}function h(a,b,d){d=d||{};this.background_image=h.DEFAULT_BACKGROUND_IMAGE;a&&a.constructor===String&&(a=document.querySelector(a));this.ds=new y;this.zoom_modify_alpha=!0;this.title_text_font=""+e.NODE_TEXT_SIZE+"px Arial";this.inner_text_font="normal "+e.NODE_SUBTEXT_SIZE+"px Arial";this.node_title_color=e.NODE_TITLE_COLOR;this.default_link_color=e.LINK_COLOR;this.default_connection_color={input_off:"#778", +(function(y){function c(a){e.debug&&console.log("Graph created");this.list_of_graphcanvas=null;this.clear();a&&this.configure(a)}function n(a,b,d,f,g,t){this.id=a;this.type=b;this.origin_id=d;this.origin_slot=f;this.target_id=g;this.target_slot=t;this._data=null;this._pos=new Float32Array(2)}function s(a){this._ctor(a)}function h(a){this._ctor(a)}function w(a,b){this.offset=new Float32Array([0,0]);this.scale=1;this.max_scale=10;this.min_scale=0.1;this.onredraw=null;this.enabled=!0;this.last_mouse= +[0,0];this.element=null;this.visible_area=new Float32Array(4);a&&(this.element=a,b||this.bindEvents(a))}function k(a,b,d){d=d||{};this.background_image=k.DEFAULT_BACKGROUND_IMAGE;a&&a.constructor===String&&(a=document.querySelector(a));this.ds=new w;this.zoom_modify_alpha=!0;this.title_text_font=""+e.NODE_TEXT_SIZE+"px Arial";this.inner_text_font="normal "+e.NODE_SUBTEXT_SIZE+"px Arial";this.node_title_color=e.NODE_TITLE_COLOR;this.default_link_color=e.LINK_COLOR;this.default_connection_color={input_off:"#778", input_on:"#7F7",output_off:"#778",output_on:"#7F7"};this.highquality_render=!0;this.use_gradients=!1;this.editor_alpha=1;this.pause_rendering=!1;this.clear_background=!0;this.read_only=!1;this.render_only_selected=!0;this.live_mode=!1;this.allow_searchbox=this.allow_interaction=this.allow_dragnodes=this.allow_dragcanvas=this.show_info=!0;this.drag_mode=this.allow_reconnect_links=!1;this.filter=this.dragging_rectangle=null;this.set_canvas_dirty_on_mouse_event=!0;this.always_render_background=!1;this.render_canvas_border= this.render_shadows=!0;this.render_connections_shadows=!1;this.render_connections_border=!0;this.render_connection_arrows=this.render_curved_connections=!1;this.render_collapsed_slots=!0;this.render_execution_order=!1;this.render_link_tooltip=this.render_title_colored=!0;this.links_render_mode=e.SPLINE_LINK;this.mouse=[0,0];this.canvas_mouse=this.graph_mouse=[0,0];this.onAfterChange=this.onBeforeChange=this.onConnectingChange=this.onSelectionChange=this.onNodeMoved=this.onDrawLinkTooltip=this.onDrawOverlay= -this.onDrawForeground=this.onDrawBackground=this.onMouse=this.onSearchBoxSelection=this.onSearchBox=null;this.connections_width=3;this.round_radius=8;this.over_link_center=this.node_widget=this.current_node=null;this.last_mouse_position=[0,0];this.visible_area=this.ds.visible_area;this.visible_links=[];b&&b.attachCanvas(this);this.setCanvas(a);this.clear();d.skip_render||this.startRendering();this.autoresize=d.autoresize}function D(a,b){return Math.sqrt((b[0]-a[0])*(b[0]-a[0])+(b[1]-a[1])*(b[1]-a[1]))} -function x(a,b,d,f,g,q){return da&&fb?!0:!1}function B(a,b){var d=a[0]+a[2],f=a[1]+a[3],g=b[1]+b[3];return a[0]>b[0]+b[2]||a[1]>g||dc.width-r.width-10&&(g=c.width-r.width-10);c.height&& -e>c.height-r.height-10&&(e=c.height-r.height-10)}q.style.left=g+"px";q.style.top=e+"px";b.scale&&(q.style.transform="scale("+b.scale+")")}function E(a){this.points=a;this.nearest=this.selected=-1;this.size=null;this.must_update=!0;this.margin=5}var e=C.LiteGraph={VERSION:0.4,CANVAS_GRID_SIZE:10,NODE_TITLE_HEIGHT:30,NODE_TITLE_TEXT_Y:20,NODE_SLOT_HEIGHT:20,NODE_WIDGET_HEIGHT:20,NODE_WIDTH:140,NODE_MIN_WIDTH:50,NODE_COLLAPSED_RADIUS:10,NODE_COLLAPSED_WIDTH:80,NODE_TITLE_COLOR:"#999",NODE_TEXT_SIZE:14, +this.onDrawForeground=this.onDrawBackground=this.onMouse=this.onSearchBoxSelection=this.onSearchBox=null;this.connections_width=3;this.round_radius=8;this.over_link_center=this.node_widget=this.current_node=null;this.last_mouse_position=[0,0];this.visible_area=this.ds.visible_area;this.visible_links=[];b&&b.attachCanvas(this);this.setCanvas(a);this.clear();d.skip_render||this.startRendering();this.autoresize=d.autoresize}function B(a,b){return Math.sqrt((b[0]-a[0])*(b[0]-a[0])+(b[1]-a[1])*(b[1]-a[1]))} +function v(a,b,d,f,g,t){return da&&fb?!0:!1}function F(a,b){var d=a[0]+a[2],f=a[1]+a[3],g=b[1]+b[3];return a[0]>b[0]+b[2]||a[1]>g||dc.width-l.width-10&&(g=c.width-l.width-10);c.height&& +e>c.height-l.height-10&&(e=c.height-l.height-10)}t.style.left=g+"px";t.style.top=e+"px";b.scale&&(t.style.transform="scale("+b.scale+")")}function C(a){this.points=a;this.nearest=this.selected=-1;this.size=null;this.must_update=!0;this.margin=5}var e=y.LiteGraph={VERSION:0.4,CANVAS_GRID_SIZE:10,NODE_TITLE_HEIGHT:30,NODE_TITLE_TEXT_Y:20,NODE_SLOT_HEIGHT:20,NODE_WIDGET_HEIGHT:20,NODE_WIDTH:140,NODE_MIN_WIDTH:50,NODE_COLLAPSED_RADIUS:10,NODE_COLLAPSED_WIDTH:80,NODE_TITLE_COLOR:"#999",NODE_TEXT_SIZE:14, NODE_TEXT_COLOR:"#AAA",NODE_SUBTEXT_SIZE:12,NODE_DEFAULT_COLOR:"#333",NODE_DEFAULT_BGCOLOR:"#353535",NODE_DEFAULT_BOXCOLOR:"#666",NODE_DEFAULT_SHAPE:"box",DEFAULT_SHADOW_COLOR:"rgba(0,0,0,0.5)",DEFAULT_GROUP_FONT:24,WIDGET_BGCOLOR:"#222",WIDGET_OUTLINE_COLOR:"#666",WIDGET_TEXT_COLOR:"#DDD",WIDGET_SECONDARY_TEXT_COLOR:"#999",LINK_COLOR:"#9A9",EVENT_LINK_COLOR:"#A86",CONNECTING_LINK_COLOR:"#AFA",MAX_NUMBER_OF_NODES:1E3,DEFAULT_POSITION:[100,100],VALID_SHAPES:["default","box","round","card"],BOX_SHAPE:1, ROUND_SHAPE:2,CIRCLE_SHAPE:3,CARD_SHAPE:4,ARROW_SHAPE:5,INPUT:1,OUTPUT:2,EVENT:-1,ACTION:-1,ALWAYS:0,ON_EVENT:1,NEVER:2,ON_TRIGGER:3,UP:1,DOWN:2,LEFT:3,RIGHT:4,CENTER:5,STRAIGHT_LINK:0,LINEAR_LINK:1,SPLINE_LINK:2,NORMAL_TITLE:0,NO_TITLE:1,TRANSPARENT_TITLE:2,AUTOHIDE_TITLE:3,proxy:null,node_images_path:"",debug:!1,catch_exceptions:!0,throw_errors:!0,allow_scripts:!1,registered_node_types:{},node_types_by_file_extension:{},Nodes:{},Globals:{},searchbox_extras:{},registerNodeType:function(a,b){if(!b.prototype)throw"Cannot register a simple object, it must be a class with a prototype"; -b.type=a;e.debug&&console.log("Node registered: "+a);a.split("/");var d=b.name,f=a.lastIndexOf("/");b.category=a.substr(0,f);b.title||(b.title=d);if(b.prototype)for(var g in n.prototype)b.prototype[g]||(b.prototype[g]=n.prototype[g]);if(f=this.registered_node_types[a])console.log("replacing node type: "+a);else if(Object.hasOwnProperty(b.prototype,"shape")||Object.defineProperty(b.prototype,"shape",{set:function(a){switch(a){case "default":delete this._shape;break;case "box":this._shape=e.BOX_SHAPE; -break;case "round":this._shape=e.ROUND_SHAPE;break;case "circle":this._shape=e.CIRCLE_SHAPE;break;case "card":this._shape=e.CARD_SHAPE;break;default:this._shape=a}},get:function(a){return this._shape},enumerable:!0,configurable:!0}),b.prototype.onPropertyChange&&console.warn("LiteGraph node class "+a+" has onPropertyChange method, it must be called onPropertyChanged with d at the end"),b.supported_extensions)for(g in b.supported_extensions){var q=b.supported_extensions[g];q&&q.constructor===String&& -(this.node_types_by_file_extension[q.toLowerCase()]=b)}this.registered_node_types[a]=b;b.constructor.name&&(this.Nodes[d]=b);if(e.onNodeTypeRegistered)e.onNodeTypeRegistered(a,b);if(f&&e.onNodeTypeReplaced)e.onNodeTypeReplaced(a,b,f)},unregisterNodeType:function(a){var b=a.constructor===String?this.registered_node_types[a]:a;if(!b)throw"node type not found: "+a;delete this.registered_node_types[b.type];b.constructor.name&&delete this.Nodes[b.constructor.name]},wrapFunctionAsNode:function(a,b,d,f, -g){for(var q=Array(b.length),A="",c=e.getParameterNames(b),r=0;rA&&(A=g.size[0]),c+=g.size[1]+a+e.NODE_TITLE_HEIGHT;b+=A+a}this.setDirtyCanvas(!0,!0)};c.prototype.getTime=function(){return this.globaltime};c.prototype.getFixedTime=function(){return this.fixedtime};c.prototype.getElapsedTime=function(){return this.elapsed_time}; -c.prototype.sendEventToAllNodes=function(a,b,d){d=d||e.ALWAYS;var f=this._nodes_in_order?this._nodes_in_order:this._nodes;if(f)for(var g=0,q=f.length;g=e.MAX_NUMBER_OF_NODES)throw"LiteGraph: max number of nodes in a graph reached";null==a.id||-1==a.id?a.id=++this.last_node_id:this.last_node_idz&&(z=g.size[0]),c+=g.size[1]+a+e.NODE_TITLE_HEIGHT;b+=z+a}this.setDirtyCanvas(!0,!0)};c.prototype.getTime=function(){return this.globaltime};c.prototype.getFixedTime=function(){return this.fixedtime};c.prototype.getElapsedTime=function(){return this.elapsed_time}; +c.prototype.sendEventToAllNodes=function(a,b,d){d=d||e.ALWAYS;var f=this._nodes_in_order?this._nodes_in_order:this._nodes;if(f)for(var g=0,t=f.length;g=e.MAX_NUMBER_OF_NODES)throw"LiteGraph: max number of nodes in a graph reached";null==a.id||-1==a.id?a.id=++this.last_node_id:this.last_node_ida.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});this.id=-1;this.type=null;this.inputs=[];this.outputs=[];this.connections=[];this.properties={};this.properties_info=[];this.flags={}};n.prototype.configure=function(a){this.graph&&this.graph._version++;for(var b in a)if("properties"==b)for(var d in a.properties){if(this.properties[d]=a.properties[d],this.onPropertyChanged)this.onPropertyChanged(d, +this.links[a]){var b=this.getNodeById(a.target_id);b&&b.disconnectInput(a.target_slot)}};c.prototype.serialize=function(){for(var a=[],b=0,d=this._nodes.length;ba.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});this.id=-1;this.type=null;this.inputs=[];this.outputs=[];this.connections=[];this.properties={};this.properties_info=[];this.flags={}};s.prototype.configure=function(a){this.graph&&this.graph._version++;for(var b in a)if("properties"==b)for(var d in a.properties){if(this.properties[d]=a.properties[d],this.onPropertyChanged)this.onPropertyChanged(d, a.properties[d])}else null!=a[b]&&("object"==typeof a[b]?this[b]&&this[b].configure?this[b].configure(a[b]):this[b]=e.cloneObject(a[b],this[b]):this[b]=a[b]);a.title||(this.title=this.constructor.title);if(this.onConnectionsChange){if(this.inputs)for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d._data=b,this.outputs[a].links))for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d.type=b,this.outputs[a].links))for(d=0;d=this.inputs.length||null==this.inputs[a].link)){var d=this.graph.links[this.inputs[a].link];if(!d)return null;if(!b)return d.data;var f=this.graph.getNodeById(d.origin_id);if(!f)return d.data; -if(f.updateOutputData)f.updateOutputData(d.origin_slot);else if(f.onExecute)f.onExecute();return d.data}};n.prototype.getInputDataType=function(a){if(!this.inputs||a>=this.inputs.length||null==this.inputs[a].link)return null;a=this.graph.links[this.inputs[a].link];if(!a)return null;var b=this.graph.getNodeById(a.origin_id);return b?(a=b.outputs[a.origin_slot])?a.type:null:a.type};n.prototype.getInputDataByName=function(a,b){var d=this.findInputSlot(a);return-1==d?null:this.getInputData(d,b)};n.prototype.isInputConnected= -function(a){return this.inputs?a=this.inputs.length)return null;a=this.inputs[a];return a&&null!==a.link?(a=this.graph.links[a.link])?this.graph.getNodeById(a.origin_id): -null:null};n.prototype.getInputOrProperty=function(a){if(!this.inputs||!this.inputs.length)return this.properties?this.properties[a]:null;for(var b=0,d=this.inputs.length;b=this.outputs.length?null:this.outputs[a]._data};n.prototype.getOutputInfo=function(a){return this.outputs?a=this.outputs.length)return null;a=this.outputs[a];if(!a.links||0==a.links.length)return null; -for(var b=[],d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d._data=b,this.outputs[a].links))for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d.type=b,this.outputs[a].links))for(d=0;d=this.inputs.length||null==this.inputs[a].link)){var d=this.graph.links[this.inputs[a].link];if(!d)return null;if(!b)return d.data;var f=this.graph.getNodeById(d.origin_id);if(!f)return d.data; +if(f.updateOutputData)f.updateOutputData(d.origin_slot);else if(f.onExecute)f.onExecute();return d.data}};s.prototype.getInputDataType=function(a){if(!this.inputs||a>=this.inputs.length||null==this.inputs[a].link)return null;a=this.graph.links[this.inputs[a].link];if(!a)return null;var b=this.graph.getNodeById(a.origin_id);return b?(a=b.outputs[a.origin_slot])?a.type:null:a.type};s.prototype.getInputDataByName=function(a,b){var d=this.findInputSlot(a);return-1==d?null:this.getInputData(d,b)};s.prototype.isInputConnected= +function(a){return this.inputs?a=this.inputs.length)return null;a=this.inputs[a];return a&&null!==a.link?(a=this.graph.links[a.link])?this.graph.getNodeById(a.origin_id): +null:null};s.prototype.getInputOrProperty=function(a){if(!this.inputs||!this.inputs.length)return this.properties?this.properties[a]:null;for(var b=0,d=this.inputs.length;b=this.outputs.length?null:this.outputs[a]._data};s.prototype.getOutputInfo=function(a){return this.outputs?a=this.outputs.length)return null;a=this.outputs[a];if(!a.links||0==a.links.length)return null; +for(var b=[],d=0;da&&this.pos[1]-g-db)return!0;return!1};n.prototype.getSlotInPosition=function(a,b){var d=new Float32Array(2);if(this.inputs)for(var f=0,g=this.inputs.length;f=this.outputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),null;b&&b.constructor===Number&&(b=this.graph.getNodeById(b)); +this.setSize(this.computeSize());return b};s.prototype.addCustomWidget=function(a){this.widgets||(this.widgets=[]);this.widgets.push(a);return a};s.prototype.getBounding=function(a){a=a||new Float32Array(4);a[0]=this.pos[0]-4;a[1]=this.pos[1]-e.NODE_TITLE_HEIGHT;a[2]=this.size[0]+4;a[3]=this.size[1]+e.NODE_TITLE_HEIGHT;if(this.onBounding)this.onBounding(a);return a};s.prototype.isPointInside=function(a,b,d,f){d=d||0;var g=this.graph&&this.graph.isLive()?0:e.NODE_TITLE_HEIGHT;f&&(g=0);if(this.flags&& +this.flags.collapsed){if(v(a,b,this.pos[0]-d,this.pos[1]-e.NODE_TITLE_HEIGHT-d,(this._collapsed_width||e.NODE_COLLAPSED_WIDTH)+2*d,e.NODE_TITLE_HEIGHT+2*d))return!0}else if(this.pos[0]-4-da&&this.pos[1]-g-db)return!0;return!1};s.prototype.getSlotInPosition=function(a,b){var d=new Float32Array(2);if(this.inputs)for(var f=0,g=this.inputs.length;f=this.outputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),null;b&&b.constructor===Number&&(b=this.graph.getNodeById(b)); if(!b)throw"target node is null";if(b==this)return null;if(d.constructor===String){if(d=b.findInputSlot(d),-1==d)return e.debug&&console.log("Connect: Error, no slot of name "+d),null}else{if(d===e.EVENT)return null;if(!b.inputs||d>=b.inputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),null}var f=!1;null!=b.inputs[d].link&&(this.graph.beforeChange(),b.disconnectInput(d),f=!0);var g=this.outputs[a];if(b.onConnectInput&&!1===b.onConnectInput(d,g.type,g,this,a))return null; -var q=b.inputs[d],A=null;if(!e.isValidConnection(g.type,q.type))return this.setDirtyCanvas(!1,!0),f&&this.graph.connectionChange(this,A),null;f||this.graph.beforeChange();A=new m(++this.graph.last_link_id,q.type,this.id,a,b.id,d);this.graph.links[A.id]=A;null==g.links&&(g.links=[]);g.links.push(A.id);b.inputs[d].link=A.id;this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(e.OUTPUT,a,!0,A,g);if(b.onConnectionsChange)b.onConnectionsChange(e.INPUT,d,!0,A,q);this.graph&& -this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(e.INPUT,b,d,this,a),this.graph.onNodeConnectionChange(e.OUTPUT,this,a,b,d));this.setDirtyCanvas(!1,!0);this.graph.afterChange();this.graph.connectionChange(this,A);return A};n.prototype.disconnectOutput=function(a,b){if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return e.debug&&console.log("Connect: Error, no slot of name "+a),!1}else if(!this.outputs||a>=this.outputs.length)return e.debug&&console.log("Connect: Error, slot number not found"), -!1;var d=this.outputs[a];if(!d||!d.links||0==d.links.length)return!1;if(b){b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"Target Node not found";for(var f=0,g=d.links.length;f=this.inputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),!1;var b=this.inputs[a];if(!b)return!1;var d=this.inputs[a].link;this.inputs[a].link=null;var f=this.graph.links[d];if(f){var g=this.graph.getNodeById(f.origin_id);if(!g)return!1;var q=g.outputs[f.origin_slot];if(!q||!q.links||0==q.links.length)return!1;for(var A=0,c=q.links.length;A< -c;A++)if(q.links[A]==d){q.links.splice(A,1);break}delete this.graph.links[d];this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(e.INPUT,a,!1,f,b);if(g.onConnectionsChange)g.onConnectionsChange(e.OUTPUT,A,!1,f,q);this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(e.OUTPUT,g,A),this.graph.onNodeConnectionChange(e.INPUT,this,a))}this.setDirtyCanvas(!1,!0);this.graph.connectionChange(this);return!0};n.prototype.getConnectionPos=function(a, +var t=b.inputs[d],z=null;if(!e.isValidConnection(g.type,t.type))return this.setDirtyCanvas(!1,!0),f&&this.graph.connectionChange(this,z),null;f||this.graph.beforeChange();z=new n(++this.graph.last_link_id,t.type,this.id,a,b.id,d);this.graph.links[z.id]=z;null==g.links&&(g.links=[]);g.links.push(z.id);b.inputs[d].link=z.id;this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(e.OUTPUT,a,!0,z,g);if(b.onConnectionsChange)b.onConnectionsChange(e.INPUT,d,!0,z,t);this.graph&& +this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(e.INPUT,b,d,this,a),this.graph.onNodeConnectionChange(e.OUTPUT,this,a,b,d));this.setDirtyCanvas(!1,!0);this.graph.afterChange();this.graph.connectionChange(this,z);return z};s.prototype.disconnectOutput=function(a,b){if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return e.debug&&console.log("Connect: Error, no slot of name "+a),!1}else if(!this.outputs||a>=this.outputs.length)return e.debug&&console.log("Connect: Error, slot number not found"), +!1;var d=this.outputs[a];if(!d||!d.links||0==d.links.length)return!1;if(b){b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"Target Node not found";for(var f=0,g=d.links.length;f=this.inputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),!1;var b=this.inputs[a];if(!b)return!1;var d=this.inputs[a].link;this.inputs[a].link=null;var f=this.graph.links[d];if(f){var g=this.graph.getNodeById(f.origin_id);if(!g)return!1;var t=g.outputs[f.origin_slot];if(!t||!t.links||0==t.links.length)return!1;for(var z=0,c=t.links.length;z< +c;z++)if(t.links[z]==d){t.links.splice(z,1);break}delete this.graph.links[d];this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(e.INPUT,a,!1,f,b);if(g.onConnectionsChange)g.onConnectionsChange(e.OUTPUT,z,!1,f,t);this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(e.OUTPUT,g,z),this.graph.onNodeConnectionChange(e.INPUT,this,a))}this.setDirtyCanvas(!1,!0);this.graph.connectionChange(this);return!0};s.prototype.getConnectionPos=function(a, b,d){d=d||new Float32Array(2);var f=0;a&&this.inputs&&(f=this.inputs.length);!a&&this.outputs&&(f=this.outputs.length);var g=0.5*e.NODE_SLOT_HEIGHT;if(this.flags.collapsed)return b=this._collapsed_width||e.NODE_COLLAPSED_WIDTH,this.horizontal?(d[0]=this.pos[0]+0.5*b,d[1]=a?this.pos[1]-e.NODE_TITLE_HEIGHT:this.pos[1]):(d[0]=a?this.pos[0]:this.pos[0]+b,d[1]=this.pos[1]-0.5*e.NODE_TITLE_HEIGHT),d;if(a&&-1==b)return d[0]=this.pos[0]+0.5*e.NODE_TITLE_HEIGHT,d[1]=this.pos[1]+0.5*e.NODE_TITLE_HEIGHT,d;if(a&& f>b&&this.inputs[b].pos)return d[0]=this.pos[0]+this.inputs[b].pos[0],d[1]=this.pos[1]+this.inputs[b].pos[1],d;if(!a&&f>b&&this.outputs[b].pos)return d[0]=this.pos[0]+this.outputs[b].pos[0],d[1]=this.pos[1]+this.outputs[b].pos[1],d;if(this.horizontal)return d[0]=this.pos[0]+this.size[0]/f*(b+0.5),d[1]=a?this.pos[1]-e.NODE_TITLE_HEIGHT:this.pos[1]+this.size[1],d;d[0]=a?this.pos[0]+g:this.pos[0]+this.size[0]+1-g;d[1]=this.pos[1]+(b+0.7)*e.NODE_SLOT_HEIGHT+(this.constructor.slot_start_y||0);return d}; -n.prototype.alignToGrid=function(){this.pos[0]=e.CANVAS_GRID_SIZE*Math.round(this.pos[0]/e.CANVAS_GRID_SIZE);this.pos[1]=e.CANVAS_GRID_SIZE*Math.round(this.pos[1]/e.CANVAS_GRID_SIZE)};n.prototype.trace=function(a){this.console||(this.console=[]);this.console.push(a);this.console.length>n.MAX_CONSOLE&&this.console.shift();if(this.graph.onNodeTrace)this.graph.onNodeTrace(this,a)};n.prototype.setDirtyCanvas=function(a,b){this.graph&&this.graph.sendActionToCanvas("setDirty",[a,b])};n.prototype.loadImage= -function(a){var b=new Image;b.src=e.node_images_path+a;b.ready=!1;var d=this;b.onload=function(){this.ready=!0;d.setDirtyCanvas(!0)};return b};n.prototype.captureInput=function(a){if(this.graph&&this.graph.list_of_graphcanvas)for(var b=this.graph.list_of_graphcanvas,d=0;da.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});Object.defineProperty(this,"size",{set:function(a){!a||2>a.length||(this._size[0]=Math.max(140,a[0]),this._size[1]=Math.max(80,a[1]))},get:function(){return this._size},enumerable:!0})};k.prototype.configure=function(a){this.title=a.title;this._bounding.set(a.bounding);this.color= -a.color;this.font=a.font};k.prototype.serialize=function(){var a=this._bounding;return{title:this.title,bounding:[Math.round(a[0]),Math.round(a[1]),Math.round(a[2]),Math.round(a[3])],color:this.color,font:this.font}};k.prototype.move=function(a,b,d){this._pos[0]+=a;this._pos[1]+=b;if(!d)for(d=0;ds.MAX_CONSOLE&&this.console.shift();if(this.graph.onNodeTrace)this.graph.onNodeTrace(this,a)};s.prototype.setDirtyCanvas=function(a,b){this.graph&&this.graph.sendActionToCanvas("setDirty",[a,b])};s.prototype.loadImage= +function(a){var b=new Image;b.src=e.node_images_path+a;b.ready=!1;var d=this;b.onload=function(){this.ready=!0;d.setDirtyCanvas(!0)};return b};s.prototype.captureInput=function(a){if(this.graph&&this.graph.list_of_graphcanvas)for(var b=this.graph.list_of_graphcanvas,d=0;da.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});Object.defineProperty(this,"size",{set:function(a){!a||2>a.length||(this._size[0]=Math.max(140,a[0]),this._size[1]=Math.max(80,a[1]))},get:function(){return this._size},enumerable:!0})};h.prototype.configure=function(a){this.title=a.title;this._bounding.set(a.bounding);this.color= +a.color;this.font=a.font};h.prototype.serialize=function(){var a=this._bounding;return{title:this.title,bounding:[Math.round(a[0]),Math.round(a[1]),Math.round(a[2]),Math.round(a[3])],color:this.color,font:this.font}};h.prototype.move=function(a,b,d){this._pos[0]+=a;this._pos[1]+=b;if(!d)for(d=0;dthis.max_scale&&(a=this.max_scale);if(a!=this.scale&&this.element){var d=this.element.getBoundingClientRect();if(d){b=b||[0.5*d.width,0.5*d.height];d=this.convertCanvasToOffset(b);this.scale=a;0.01>Math.abs(this.scale-1)&&(this.scale=1);var f=this.convertCanvasToOffset(b),d=[f[0]-d[0],f[1]-d[1]];this.offset[0]+=d[0];this.offset[1]+=d[1];if(this.onredraw)this.onredraw(this)}}}; -y.prototype.changeDeltaScale=function(a,b){this.changeScale(this.scale*a,b)};y.prototype.reset=function(){this.scale=1;this.offset[0]=0;this.offset[1]=0};C.LGraphCanvas=e.LGraphCanvas=h;h.DEFAULT_BACKGROUND_IMAGE="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; -h.link_type_colors={"-1":e.EVENT_LINK_COLOR,number:"#AAA",node:"#DCA"};h.gradients={};h.prototype.clear=function(){this.fps=this.render_time=this.last_draw_time=this.frame=0;this.dragging_rectangle=null;this.selected_nodes={};this.selected_group=null;this.visible_nodes=[];this.connecting_node=this.node_capturing_input=this.node_over=this.node_dragged=null;this.highlighted_links={};this.dragging_canvas=!1;this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_widget=this.node_in_panel=this.dirty_area= -null;this.last_mouse=[0,0];this.last_mouseclick=0;this.visible_area.set([0,0,0,0]);if(this.onClear)this.onClear()};h.prototype.setGraph=function(a,b){this.graph!=a&&(b||this.clear(),!a&&this.graph?this.graph.detachCanvas(this):(a.attachCanvas(this),this._graph_stack&&(this._graph_stack=null),this.setDirty(!0,!0)))};h.prototype.getTopGraph=function(){return this._graph_stack.length?this._graph_stack[0]:this.graph};h.prototype.openSubgraph=function(a){if(!a)throw"graph cannot be null";if(this.graph== -a)throw"graph cannot be the same";this.clear();this.graph&&(this._graph_stack||(this._graph_stack=[]),this._graph_stack.push(this.graph));a.attachCanvas(this);this.checkPanels();this.setDirty(!0,!0)};h.prototype.closeSubgraph=function(){if(this._graph_stack&&0!=this._graph_stack.length){var a=this.graph._subgraph_node,b=this._graph_stack.pop();this.selected_nodes={};this.highlighted_links={};b.attachCanvas(this);this.setDirty(!0,!0);a&&(this.centerOnNode(a),this.selectNodes([a]))}};h.prototype.getCurrentGraph= -function(){return this.graph};h.prototype.setCanvas=function(a,b){if(a&&a.constructor===String&&(a=document.getElementById(a),!a))throw"Error creating LiteGraph canvas: Canvas not found";if(a!==this.canvas&&(!a&&this.canvas&&(b||this.unbindEvents()),this.canvas=a,this.ds.element=a)){a.className+=" lgraphcanvas";a.data=this;a.tabindex="1";this.bgcanvas=null;this.bgcanvas||(this.bgcanvas=document.createElement("canvas"),this.bgcanvas.width=this.canvas.width,this.bgcanvas.height=this.canvas.height); -if(null==a.getContext){if("canvas"!=a.localName)throw"Element supplied for LGraphCanvas must be a element, you passed a "+a.localName;throw"This browser doesn't support Canvas";}null==(this.ctx=a.getContext("2d"))&&(a.webgl_enabled||console.warn("This canvas seems to be WebGL, enabling WebGL renderer"),this.enableWebGL());this._mousemove_callback=this.processMouseMove.bind(this);this._mouseup_callback=this.processMouseUp.bind(this);b||this.bindEvents()}};h.prototype._doNothing=function(a){a.preventDefault(); -return!1};h.prototype._doReturnTrue=function(a){a.preventDefault();return!0};h.prototype.bindEvents=function(){if(this._events_binded)console.warn("LGraphCanvas: events already binded");else{var a=this.canvas,b=this.getCanvasWindow().document;this._mousedown_callback=this.processMouseDown.bind(this);this._mousewheel_callback=this.processMouseWheel.bind(this);a.addEventListener("mousedown",this._mousedown_callback,!0);a.addEventListener("mousemove",this._mousemove_callback);a.addEventListener("mousewheel", +f;this.last_mouse[1]=d;a.preventDefault();a.stopPropagation();return!1}};w.prototype.toCanvasContext=function(a){a.scale(this.scale,this.scale);a.translate(this.offset[0],this.offset[1])};w.prototype.convertOffsetToCanvas=function(a){return[(a[0]+this.offset[0])*this.scale,(a[1]+this.offset[1])*this.scale]};w.prototype.convertCanvasToOffset=function(a,b){b=b||[0,0];b[0]=a[0]/this.scale-this.offset[0];b[1]=a[1]/this.scale-this.offset[1];return b};w.prototype.mouseDrag=function(a,b){this.offset[0]+= +a/this.scale;this.offset[1]+=b/this.scale;if(this.onredraw)this.onredraw(this)};w.prototype.changeScale=function(a,b){athis.max_scale&&(a=this.max_scale);if(a!=this.scale&&this.element){var d=this.element.getBoundingClientRect();if(d){b=b||[0.5*d.width,0.5*d.height];d=this.convertCanvasToOffset(b);this.scale=a;0.01>Math.abs(this.scale-1)&&(this.scale=1);var f=this.convertCanvasToOffset(b),d=[f[0]-d[0],f[1]-d[1]];this.offset[0]+=d[0];this.offset[1]+=d[1];if(this.onredraw)this.onredraw(this)}}}; +w.prototype.changeDeltaScale=function(a,b){this.changeScale(this.scale*a,b)};w.prototype.reset=function(){this.scale=1;this.offset[0]=0;this.offset[1]=0};y.LGraphCanvas=e.LGraphCanvas=k;k.DEFAULT_BACKGROUND_IMAGE="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; +k.link_type_colors={"-1":e.EVENT_LINK_COLOR,number:"#AAA",node:"#DCA"};k.gradients={};k.prototype.clear=function(){this.fps=this.render_time=this.last_draw_time=this.frame=0;this.dragging_rectangle=null;this.selected_nodes={};this.selected_group=null;this.visible_nodes=[];this.connecting_node=this.node_capturing_input=this.node_over=this.node_dragged=null;this.highlighted_links={};this.dragging_canvas=!1;this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_widget=this.node_in_panel=this.dirty_area= +null;this.last_mouse=[0,0];this.last_mouseclick=0;this.visible_area.set([0,0,0,0]);if(this.onClear)this.onClear()};k.prototype.setGraph=function(a,b){this.graph!=a&&(b||this.clear(),!a&&this.graph?this.graph.detachCanvas(this):(a.attachCanvas(this),this._graph_stack&&(this._graph_stack=null),this.setDirty(!0,!0)))};k.prototype.getTopGraph=function(){return this._graph_stack.length?this._graph_stack[0]:this.graph};k.prototype.openSubgraph=function(a){if(!a)throw"graph cannot be null";if(this.graph== +a)throw"graph cannot be the same";this.clear();this.graph&&(this._graph_stack||(this._graph_stack=[]),this._graph_stack.push(this.graph));a.attachCanvas(this);this.checkPanels();this.setDirty(!0,!0)};k.prototype.closeSubgraph=function(){if(this._graph_stack&&0!=this._graph_stack.length){var a=this.graph._subgraph_node,b=this._graph_stack.pop();this.selected_nodes={};this.highlighted_links={};b.attachCanvas(this);this.setDirty(!0,!0);a&&(this.centerOnNode(a),this.selectNodes([a]))}};k.prototype.getCurrentGraph= +function(){return this.graph};k.prototype.setCanvas=function(a,b){if(a&&a.constructor===String&&(a=document.getElementById(a),!a))throw"Error creating LiteGraph canvas: Canvas not found";if(a!==this.canvas&&(!a&&this.canvas&&(b||this.unbindEvents()),this.canvas=a,this.ds.element=a)){a.className+=" lgraphcanvas";a.data=this;a.tabindex="1";this.bgcanvas=null;this.bgcanvas||(this.bgcanvas=document.createElement("canvas"),this.bgcanvas.width=this.canvas.width,this.bgcanvas.height=this.canvas.height); +if(null==a.getContext){if("canvas"!=a.localName)throw"Element supplied for LGraphCanvas must be a element, you passed a "+a.localName;throw"This browser doesn't support Canvas";}null==(this.ctx=a.getContext("2d"))&&(a.webgl_enabled||console.warn("This canvas seems to be WebGL, enabling WebGL renderer"),this.enableWebGL());this._mousemove_callback=this.processMouseMove.bind(this);this._mouseup_callback=this.processMouseUp.bind(this);b||this.bindEvents()}};k.prototype._doNothing=function(a){a.preventDefault(); +return!1};k.prototype._doReturnTrue=function(a){a.preventDefault();return!0};k.prototype.bindEvents=function(){if(this._events_binded)console.warn("LGraphCanvas: events already binded");else{var a=this.canvas,b=this.getCanvasWindow().document;this._mousedown_callback=this.processMouseDown.bind(this);this._mousewheel_callback=this.processMouseWheel.bind(this);a.addEventListener("mousedown",this._mousedown_callback,!0);a.addEventListener("mousemove",this._mousemove_callback);a.addEventListener("mousewheel", this._mousewheel_callback,!1);a.addEventListener("contextmenu",this._doNothing);a.addEventListener("DOMMouseScroll",this._mousewheel_callback,!1);a.addEventListener("touchstart",this.touchHandler,!0);a.addEventListener("touchmove",this.touchHandler,!0);a.addEventListener("touchend",this.touchHandler,!0);a.addEventListener("touchcancel",this.touchHandler,!0);this._key_callback=this.processKey.bind(this);a.addEventListener("keydown",this._key_callback,!0);b.addEventListener("keyup",this._key_callback, -!0);this._ondrop_callback=this.processDrop.bind(this);a.addEventListener("dragover",this._doNothing,!1);a.addEventListener("dragend",this._doNothing,!1);a.addEventListener("drop",this._ondrop_callback,!1);a.addEventListener("dragenter",this._doReturnTrue,!1);this._events_binded=!0}};h.prototype.unbindEvents=function(){if(this._events_binded){var a=this.getCanvasWindow().document;this.canvas.removeEventListener("mousedown",this._mousedown_callback);this.canvas.removeEventListener("mousewheel",this._mousewheel_callback); +!0);this._ondrop_callback=this.processDrop.bind(this);a.addEventListener("dragover",this._doNothing,!1);a.addEventListener("dragend",this._doNothing,!1);a.addEventListener("drop",this._ondrop_callback,!1);a.addEventListener("dragenter",this._doReturnTrue,!1);this._events_binded=!0}};k.prototype.unbindEvents=function(){if(this._events_binded){var a=this.getCanvasWindow().document;this.canvas.removeEventListener("mousedown",this._mousedown_callback);this.canvas.removeEventListener("mousewheel",this._mousewheel_callback); this.canvas.removeEventListener("DOMMouseScroll",this._mousewheel_callback);this.canvas.removeEventListener("keydown",this._key_callback);a.removeEventListener("keyup",this._key_callback);this.canvas.removeEventListener("contextmenu",this._doNothing);this.canvas.removeEventListener("drop",this._ondrop_callback);this.canvas.removeEventListener("dragenter",this._doReturnTrue);this.canvas.removeEventListener("touchstart",this.touchHandler);this.canvas.removeEventListener("touchmove",this.touchHandler); -this.canvas.removeEventListener("touchend",this.touchHandler);this.canvas.removeEventListener("touchcancel",this.touchHandler);this._ondrop_callback=this._key_callback=this._mousewheel_callback=this._mousedown_callback=null;this._events_binded=!1}else console.warn("LGraphCanvas: no events binded")};h.getFileExtension=function(a){var b=a.indexOf("?");-1!=b&&(a=a.substr(0,b));b=a.lastIndexOf(".");return-1==b?"":a.substr(b+1).toLowerCase()};h.prototype.enableWebGL=function(){if(void 0===typeof GL)throw"litegl.js must be included to use a WebGL canvas"; -if(void 0===typeof enableWebGLCanvas)throw"webglCanvas.js must be included to use this feature";this.gl=this.ctx=enableWebGLCanvas(this.canvas);this.ctx.webgl=!0;this.bgcanvas=this.canvas;this.bgctx=this.gl;this.canvas.webgl_enabled=!0};h.prototype.setDirty=function(a,b){a&&(this.dirty_canvas=!0);b&&(this.dirty_bgcanvas=!0)};h.prototype.getCanvasWindow=function(){if(!this.canvas)return window;var a=this.canvas.ownerDocument;return a.defaultView||a.parentWindow};h.prototype.startRendering=function(){function a(){this.pause_rendering|| -this.draw();var b=this.getCanvasWindow();this.is_rendering&&b.requestAnimationFrame(a.bind(this))}this.is_rendering||(this.is_rendering=!0,a.call(this))};h.prototype.stopRendering=function(){this.is_rendering=!1};h.prototype.blockClick=function(){this.block_click=!0;this.last_mouseclick=0};h.prototype.processMouseDown=function(a){this.set_canvas_dirty_on_mouse_event&&(this.dirty_canvas=!0);if(this.graph){this.adjustMouseEvent(a);var b=this.getCanvasWindow();h.active_canvas=this;var d=this;this.canvas.removeEventListener("mousemove", -this._mousemove_callback);b.document.addEventListener("mousemove",this._mousemove_callback,!0);b.document.addEventListener("mouseup",this._mouseup_callback,!0);var f=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes,5),g=!1,q=300>e.getTime()-this.last_mouseclick;this.mouse[0]=a.localX;this.mouse[1]=a.localY;this.graph_mouse[0]=a.canvasX;this.graph_mouse[1]=a.canvasY;this.last_click_position=[this.mouse[0],this.mouse[1]];this.canvas.focus();e.closeAllContextMenus(b);if(!this.onMouse|| -!0!=this.onMouse(a)){if(1==a.which){a.ctrlKey&&(this.dragging_rectangle=new Float32Array(4),this.dragging_rectangle[0]=a.canvasX,this.dragging_rectangle[1]=a.canvasY,this.dragging_rectangle[2]=1,this.dragging_rectangle[3]=1,g=!0);var A=!1;if(f&&this.allow_interaction&&!g&&!this.read_only){this.live_mode||f.flags.pinned||this.bringToFront(f);if(!this.connecting_node&&!f.flags.collapsed&&!this.live_mode)if(!g&&!1!==f.resizable&&x(a.canvasX,a.canvasY,f.pos[0]+f.size[0]-5,f.pos[1]+f.size[1]-5,10,10))this.graph.beforeChange(), -this.resizing_node=f,this.canvas.style.cursor="se-resize",g=!0;else{if(f.outputs)for(var c=0,r=f.outputs.length;cf.size[0]-e.NODE_TITLE_HEIGHT&&0>r[1]&&(d=this,setTimeout(function(){d.openSubgraph(f.subgraph)},10)),this.live_mode&& -(c=A=!0));c||(this.allow_dragnodes&&(this.graph.beforeChange(),this.node_dragged=f),this.selected_nodes[f.id]||this.processNodeSelected(f,a));this.dirty_canvas=!0}}else{if(!this.read_only)for(c=0;cr[0]+4||a.canvasYr[1]+4)){this.showLinkMenu(A,a);this.over_link_center=null;break}this.selected_group=this.graph.getGroupOnPos(a.canvasX,a.canvasY);this.selected_group_resizing=!1;this.selected_group&& -!this.read_only&&(a.ctrlKey&&(this.dragging_rectangle=null),10>D([a.canvasX,a.canvasY],[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])*this.ds.scale?this.selected_group_resizing=!0:this.selected_group.recomputeInsideNodes());q&&!this.read_only&&this.allow_searchbox&&this.showSearchBox(a);A=!0}!g&&A&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}else 2!=a.which&&3==a.which&&(this.read_only||this.processContextMenu(f,a));this.last_mouse[0]= -a.localX;this.last_mouse[1]=a.localY;this.last_mouseclick=e.getTime();this.last_mouse_dragging=!0;this.graph.change();(!b.document.activeElement||"input"!=b.document.activeElement.nodeName.toLowerCase()&&"textarea"!=b.document.activeElement.nodeName.toLowerCase())&&a.preventDefault();a.stopPropagation();if(this.onMouseDown)this.onMouseDown(a);return!1}}};h.prototype.processMouseMove=function(a){this.autoresize&&this.resize();this.set_canvas_dirty_on_mouse_event&&(this.dirty_canvas=!0);if(this.graph){h.active_canvas= +this.canvas.removeEventListener("touchend",this.touchHandler);this.canvas.removeEventListener("touchcancel",this.touchHandler);this._ondrop_callback=this._key_callback=this._mousewheel_callback=this._mousedown_callback=null;this._events_binded=!1}else console.warn("LGraphCanvas: no events binded")};k.getFileExtension=function(a){var b=a.indexOf("?");-1!=b&&(a=a.substr(0,b));b=a.lastIndexOf(".");return-1==b?"":a.substr(b+1).toLowerCase()};k.prototype.enableWebGL=function(){if(void 0===typeof GL)throw"litegl.js must be included to use a WebGL canvas"; +if(void 0===typeof enableWebGLCanvas)throw"webglCanvas.js must be included to use this feature";this.gl=this.ctx=enableWebGLCanvas(this.canvas);this.ctx.webgl=!0;this.bgcanvas=this.canvas;this.bgctx=this.gl;this.canvas.webgl_enabled=!0};k.prototype.setDirty=function(a,b){a&&(this.dirty_canvas=!0);b&&(this.dirty_bgcanvas=!0)};k.prototype.getCanvasWindow=function(){if(!this.canvas)return window;var a=this.canvas.ownerDocument;return a.defaultView||a.parentWindow};k.prototype.startRendering=function(){function a(){this.pause_rendering|| +this.draw();var b=this.getCanvasWindow();this.is_rendering&&b.requestAnimationFrame(a.bind(this))}this.is_rendering||(this.is_rendering=!0,a.call(this))};k.prototype.stopRendering=function(){this.is_rendering=!1};k.prototype.blockClick=function(){this.block_click=!0;this.last_mouseclick=0};k.prototype.processMouseDown=function(a){this.set_canvas_dirty_on_mouse_event&&(this.dirty_canvas=!0);if(this.graph){this.adjustMouseEvent(a);var b=this.getCanvasWindow();k.active_canvas=this;var d=this;this.canvas.removeEventListener("mousemove", +this._mousemove_callback);b.document.addEventListener("mousemove",this._mousemove_callback,!0);b.document.addEventListener("mouseup",this._mouseup_callback,!0);var f=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes,5),g=!1,t=300>e.getTime()-this.last_mouseclick;this.mouse[0]=a.localX;this.mouse[1]=a.localY;this.graph_mouse[0]=a.canvasX;this.graph_mouse[1]=a.canvasY;this.last_click_position=[this.mouse[0],this.mouse[1]];this.canvas.focus();e.closeAllContextMenus(b);if(!this.onMouse|| +!0!=this.onMouse(a)){if(1==a.which){a.ctrlKey&&(this.dragging_rectangle=new Float32Array(4),this.dragging_rectangle[0]=a.canvasX,this.dragging_rectangle[1]=a.canvasY,this.dragging_rectangle[2]=1,this.dragging_rectangle[3]=1,g=!0);var z=!1;if(f&&this.allow_interaction&&!g&&!this.read_only){this.live_mode||f.flags.pinned||this.bringToFront(f);if(!this.connecting_node&&!f.flags.collapsed&&!this.live_mode)if(!g&&!1!==f.resizable&&v(a.canvasX,a.canvasY,f.pos[0]+f.size[0]-5,f.pos[1]+f.size[1]-5,10,10))this.graph.beforeChange(), +this.resizing_node=f,this.canvas.style.cursor="se-resize",g=!0;else{if(f.outputs)for(var c=0,l=f.outputs.length;cf.size[0]-e.NODE_TITLE_HEIGHT&&0>l[1]&&(d=this,setTimeout(function(){d.openSubgraph(f.subgraph)},10)),this.live_mode&& +(c=z=!0));c||(this.allow_dragnodes&&(this.graph.beforeChange(),this.node_dragged=f),this.selected_nodes[f.id]||this.processNodeSelected(f,a));this.dirty_canvas=!0}}else{if(!this.read_only)for(c=0;cl[0]+4||a.canvasYl[1]+4)){this.showLinkMenu(z,a);this.over_link_center=null;break}this.selected_group=this.graph.getGroupOnPos(a.canvasX,a.canvasY);this.selected_group_resizing=!1;this.selected_group&& +!this.read_only&&(a.ctrlKey&&(this.dragging_rectangle=null),10>B([a.canvasX,a.canvasY],[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])*this.ds.scale?this.selected_group_resizing=!0:this.selected_group.recomputeInsideNodes());t&&!this.read_only&&this.allow_searchbox&&this.showSearchBox(a);z=!0}!g&&z&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}else 2!=a.which&&3==a.which&&(this.read_only||this.processContextMenu(f,a));this.last_mouse[0]= +a.localX;this.last_mouse[1]=a.localY;this.last_mouseclick=e.getTime();this.last_mouse_dragging=!0;this.graph.change();(!b.document.activeElement||"input"!=b.document.activeElement.nodeName.toLowerCase()&&"textarea"!=b.document.activeElement.nodeName.toLowerCase())&&a.preventDefault();a.stopPropagation();if(this.onMouseDown)this.onMouseDown(a);return!1}}};k.prototype.processMouseMove=function(a){this.autoresize&&this.resize();this.set_canvas_dirty_on_mouse_event&&(this.dirty_canvas=!0);if(this.graph){k.active_canvas= this;this.adjustMouseEvent(a);var b=[a.localX,a.localY];this.mouse[0]=b[0];this.mouse[1]=b[1];var d=[b[0]-this.last_mouse[0],b[1]-this.last_mouse[1]];this.last_mouse=b;this.graph_mouse[0]=a.canvasX;this.graph_mouse[1]=a.canvasY;if(this.block_click)return a.preventDefault(),!1;a.dragging=this.last_mouse_dragging;this.node_widget&&(this.processNodeWidgets(this.node_widget[0],this.graph_mouse,a,this.node_widget[1]),this.dirty_canvas=!0);if(this.dragging_rectangle)this.dragging_rectangle[2]=a.canvasX- this.dragging_rectangle[0],this.dragging_rectangle[3]=a.canvasY-this.dragging_rectangle[1],this.dirty_canvas=!0;else if(this.selected_group&&!this.read_only)this.selected_group_resizing?this.selected_group.size=[a.canvasX-this.selected_group.pos[0],a.canvasY-this.selected_group.pos[1]]:(this.selected_group.move(d[0]/this.ds.scale,d[1]/this.ds.scale,a.ctrlKey),this.selected_group._nodes.length&&(this.dirty_canvas=!0)),this.dirty_bgcanvas=!0;else if(this.dragging_canvas)this.ds.offset[0]+=d[0]/this.ds.scale, this.ds.offset[1]+=d[1]/this.ds.scale,this.dirty_bgcanvas=this.dirty_canvas=!0;else if(this.allow_interaction&&!this.read_only){this.connecting_node&&(this.dirty_canvas=!0);for(var f=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes),b=0,g=this.graph._nodes.length;bA[0]+4||a.canvasYA[1]+4)){g=q;break}}g!=this.over_link_center&&(this.over_link_center=g,this.dirty_canvas=!0);this.canvas&&(this.canvas.style.cursor="")}if(this.node_capturing_input&&this.node_capturing_input!=f&& +(this.dirty_canvas=!0);if(!f.mouseOver&&(f.mouseOver=!0,this.node_over=f,this.dirty_canvas=!0,f.onMouseEnter))f.onMouseEnter(a);if(f.onMouseMove)f.onMouseMove(a,[a.canvasX-f.pos[0],a.canvasY-f.pos[1]],this);if(this.connecting_node&&(g=this._highlight_input||[0,0],!this.isOverNodeBox(f,a.canvasX,a.canvasY))){var t=this.isOverNodeInput(f,a.canvasX,a.canvasY,g);-1!=t&&f.inputs[t]?e.isValidConnection(this.connecting_output.type,f.inputs[t].type)&&(this._highlight_input=g):this._highlight_input=null}this.canvas&& +(v(a.canvasX,a.canvasY,f.pos[0]+f.size[0]-5,f.pos[1]+f.size[1]-5,5,5)?this.canvas.style.cursor="se-resize":this.canvas.style.cursor="crosshair")}else{g=null;for(b=0;bz[0]+4||a.canvasYz[1]+4)){g=t;break}}g!=this.over_link_center&&(this.over_link_center=g,this.dirty_canvas=!0);this.canvas&&(this.canvas.style.cursor="")}if(this.node_capturing_input&&this.node_capturing_input!=f&& this.node_capturing_input.onMouseMove)this.node_capturing_input.onMouseMove(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]],this);if(this.node_dragged&&!this.live_mode){for(b in this.selected_nodes)f=this.selected_nodes[b],f.pos[0]+=d[0]/this.ds.scale,f.pos[1]+=d[1]/this.ds.scale;this.dirty_bgcanvas=this.dirty_canvas=!0}this.resizing_node&&!this.live_mode&&(d=[a.canvasX-this.resizing_node.pos[0],a.canvasY-this.resizing_node.pos[1]],b=this.resizing_node.computeSize(), -d[0]=Math.max(b[0],d[0]),d[1]=Math.max(b[1],d[1]),this.resizing_node.setSize(d),this.canvas.style.cursor="se-resize",this.dirty_bgcanvas=this.dirty_canvas=!0)}a.preventDefault();return!1}};h.prototype.processMouseUp=function(a){this.set_canvas_dirty_on_mouse_event&&(this.dirty_canvas=!0);if(this.graph){var b=this.getCanvasWindow().document;h.active_canvas=this;b.removeEventListener("mousemove",this._mousemove_callback,!0);this.canvas.addEventListener("mousemove",this._mousemove_callback,!0);b.removeEventListener("mouseup", +d[0]=Math.max(b[0],d[0]),d[1]=Math.max(b[1],d[1]),this.resizing_node.setSize(d),this.canvas.style.cursor="se-resize",this.dirty_bgcanvas=this.dirty_canvas=!0)}a.preventDefault();return!1}};k.prototype.processMouseUp=function(a){this.set_canvas_dirty_on_mouse_event&&(this.dirty_canvas=!0);if(this.graph){var b=this.getCanvasWindow().document;k.active_canvas=this;b.removeEventListener("mousemove",this._mousemove_callback,!0);this.canvas.addEventListener("mousemove",this._mousemove_callback,!0);b.removeEventListener("mouseup", this._mouseup_callback,!0);this.adjustMouseEvent(a);b=e.getTime();a.click_time=b-this.last_mouseclick;this.last_mouse_dragging=!1;this.last_click_position=null;this.block_click&&(console.log("foo"),this.block_click=!1);if(1==a.which){this.node_widget&&this.processNodeWidgets(this.node_widget[0],this.graph_mouse,a);this.node_widget=null;if(this.selected_group){var b=this.selected_group.pos[0]-Math.round(this.selected_group.pos[0]),d=this.selected_group.pos[1]-Math.round(this.selected_group.pos[1]); -this.selected_group.move(b,d,a.ctrlKey);this.selected_group.pos[0]=Math.round(this.selected_group.pos[0]);this.selected_group.pos[1]=Math.round(this.selected_group.pos[1]);this.selected_group._nodes.length&&(this.dirty_canvas=!0);this.selected_group=null}this.selected_group_resizing=!1;if(this.dragging_rectangle){if(this.graph){b=this.graph._nodes;d=new Float32Array(4);this.deselectAllNodes();var f=Math.abs(this.dragging_rectangle[2]),g=Math.abs(this.dragging_rectangle[3]),q=0>this.dragging_rectangle[3]? -this.dragging_rectangle[1]-g:this.dragging_rectangle[1];this.dragging_rectangle[0]=0>this.dragging_rectangle[2]?this.dragging_rectangle[0]-f:this.dragging_rectangle[0];this.dragging_rectangle[1]=q;this.dragging_rectangle[2]=f;this.dragging_rectangle[3]=g;g=[];for(q=0;qthis.dragging_rectangle[3]? +this.dragging_rectangle[1]-g:this.dragging_rectangle[1];this.dragging_rectangle[0]=0>this.dragging_rectangle[2]?this.dragging_rectangle[0]-f:this.dragging_rectangle[0];this.dragging_rectangle[1]=t;this.dragging_rectangle[2]=f;this.dragging_rectangle[3]=g;g=[];for(t=0;ta.click_time&&x(a.canvasX,a.canvasY,f.pos[0],f.pos[1]-e.NODE_TITLE_HEIGHT,e.NODE_TITLE_HEIGHT,e.NODE_TITLE_HEIGHT)&&f.collapse();this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_dragged.pos[0]=Math.round(this.node_dragged.pos[0]); +f,0)));this.connecting_node=this.connecting_pos=this.connecting_output=null;this.connecting_slot=-1}else if(this.resizing_node)this.dirty_bgcanvas=this.dirty_canvas=!0,this.graph.afterChange(this.resizing_node),this.resizing_node=null;else if(this.node_dragged){(f=this.node_dragged)&&300>a.click_time&&v(a.canvasX,a.canvasY,f.pos[0],f.pos[1]-e.NODE_TITLE_HEIGHT,e.NODE_TITLE_HEIGHT,e.NODE_TITLE_HEIGHT)&&f.collapse();this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_dragged.pos[0]=Math.round(this.node_dragged.pos[0]); this.node_dragged.pos[1]=Math.round(this.node_dragged.pos[1]);this.graph.config.align_to_grid&&this.node_dragged.alignToGrid();if(this.onNodeMoved)this.onNodeMoved(this.node_dragged);this.graph.afterChange(this.node_dragged);this.node_dragged=null}else{f=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes);!f&&300>a.click_time&&this.deselectAllNodes();this.dirty_canvas=!0;this.dragging_canvas=!1;if(this.node_over&&this.node_over.onMouseUp)this.node_over.onMouseUp(a,[a.canvasX-this.node_over.pos[0], -a.canvasY-this.node_over.pos[1]],this);if(this.node_capturing_input&&this.node_capturing_input.onMouseUp)this.node_capturing_input.onMouseUp(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]])}}else 2==a.which?(this.dirty_canvas=!0,this.dragging_canvas=!1):3==a.which&&(this.dirty_canvas=!0,this.dragging_canvas=!1);this.graph.change();a.stopPropagation();a.preventDefault();return!1}};h.prototype.processMouseWheel=function(a){if(this.graph&&this.allow_dragcanvas){var b= -null!=a.wheelDeltaY?a.wheelDeltaY:-60*a.detail;this.adjustMouseEvent(a);var d=this.ds.scale;0b&&(d*=1/1.1);this.ds.changeScale(d,[a.localX,a.localY]);this.graph.change();a.preventDefault();return!1}};h.prototype.isOverNodeBox=function(a,b,d){var f=e.NODE_TITLE_HEIGHT;return x(b,d,a.pos[0]+2,a.pos[1]+2-f,f-4,f-4)?!0:!1};h.prototype.isOverNodeInput=function(a,b,d,f){if(a.inputs)for(var g=0,q=a.inputs.length;gd-this.graph._last_trigger_time)&&this.drawBackCanvas(); -(this.dirty_canvas||a)&&this.drawFrontCanvas();this.fps=this.render_time?1/this.render_time:0;this.frame+=1}};h.prototype.drawFrontCanvas=function(){this.dirty_canvas=!1;this.ctx||(this.ctx=this.bgcanvas.getContext("2d"));var a=this.ctx;if(a){a.start2D&&a.start2D();var b=this.canvas;a.restore();a.setTransform(1,0,0,1,0,0);this.dirty_area&&(a.save(),a.beginPath(),a.rect(this.dirty_area[0],this.dirty_area[1],this.dirty_area[2],this.dirty_area[3]),a.clip());this.clear_background&&a.clearRect(0,0,b.width, +a.canvasY-this.node_over.pos[1]],this);if(this.node_capturing_input&&this.node_capturing_input.onMouseUp)this.node_capturing_input.onMouseUp(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]])}}else 2==a.which?(this.dirty_canvas=!0,this.dragging_canvas=!1):3==a.which&&(this.dirty_canvas=!0,this.dragging_canvas=!1);this.graph.change();a.stopPropagation();a.preventDefault();return!1}};k.prototype.processMouseWheel=function(a){if(this.graph&&this.allow_dragcanvas){var b= +null!=a.wheelDeltaY?a.wheelDeltaY:-60*a.detail;this.adjustMouseEvent(a);var d=this.ds.scale;0b&&(d*=1/1.1);this.ds.changeScale(d,[a.localX,a.localY]);this.graph.change();a.preventDefault();return!1}};k.prototype.isOverNodeBox=function(a,b,d){var f=e.NODE_TITLE_HEIGHT;return v(b,d,a.pos[0]+2,a.pos[1]+2-f,f-4,f-4)?!0:!1};k.prototype.isOverNodeInput=function(a,b,d,f){if(a.inputs)for(var g=0,t=a.inputs.length;gd-this.graph._last_trigger_time)&&this.drawBackCanvas(); +(this.dirty_canvas||a)&&this.drawFrontCanvas();this.fps=this.render_time?1/this.render_time:0;this.frame+=1}};k.prototype.drawFrontCanvas=function(){this.dirty_canvas=!1;this.ctx||(this.ctx=this.bgcanvas.getContext("2d"));var a=this.ctx;if(a){a.start2D&&a.start2D();var b=this.canvas;a.restore();a.setTransform(1,0,0,1,0,0);this.dirty_area&&(a.save(),a.beginPath(),a.rect(this.dirty_area[0],this.dirty_area[1],this.dirty_area[2],this.dirty_area[3]),a.clip());this.clear_background&&a.clearRect(0,0,b.width, b.height);this.bgcanvas==this.canvas?this.drawBackCanvas():a.drawImage(this.bgcanvas,0,0);if(this.onRender)this.onRender(b,a);this.show_info&&this.renderInfo(a);if(this.graph){a.save();this.ds.toCanvasContext(a);for(var b=this.computeVisibleNodes(null,this.visible_nodes),d=0;d> ";b.fillText(f+d.getTitle(),0.5*a.width,40);b.restore()}d=!1;this.onRenderBackground&&(d=this.onRenderBackground(a,b));b.restore();b.setTransform(1,0,0,1,0,0);this.visible_links.length=0;if(this.graph){b.save();this.ds.toCanvasContext(b);if(this.background_image&&0.5this.ds.scale;if(this.live_mode){if(!a.flags.collapsed&&(b.shadowColor="transparent",a.onDrawForeground))a.onDrawForeground(b, -this,this.canvas)}else{var q=this.editor_alpha;b.globalAlpha=q;this.render_shadows&&!g?(b.shadowColor=e.DEFAULT_SHADOW_COLOR,b.shadowOffsetX=2*this.ds.scale,b.shadowOffsetY=2*this.ds.scale,b.shadowBlur=3*this.ds.scale):b.shadowColor="transparent";if(!a.flags.collapsed||!a.onDrawCollapsed||!0!=a.onDrawCollapsed(b,this)){var c=a._shape||e.BOX_SHAPE;F.set(a.size);var l=a.horizontal;if(a.flags.collapsed){b.font=this.inner_text_font;var r=a.getTitle?a.getTitle():a.title;null!=r&&(a._collapsed_width=Math.min(a.size[0], -b.measureText(r).width+2*e.NODE_TITLE_HEIGHT),F[0]=a._collapsed_width,F[1]=0)}a.clip_area&&(b.save(),b.beginPath(),c==e.BOX_SHAPE?b.rect(0,0,F[0],F[1]):c==e.ROUND_SHAPE?b.roundRect(0,0,F[0],F[1],10):c==e.CIRCLE_SHAPE&&b.arc(0.5*F[0],0.5*F[1],0.5*F[0],0,2*Math.PI),b.clip());a.has_errors&&(f="red");this.drawNodeShape(a,b,F,d,f,a.is_selected,a.mouseOver);b.shadowColor="transparent";if(a.onDrawForeground)a.onDrawForeground(b,this,this.canvas);b.textAlign=l?"center":"left";b.font=this.inner_text_font; -f=!g;c=this.connecting_output;b.lineWidth=1;var r=0,s=new Float32Array(2);if(!a.flags.collapsed){if(a.inputs)for(d=0;dthis.ds.scale;if(this.live_mode){if(!a.flags.collapsed&&(b.shadowColor="transparent",a.onDrawForeground))a.onDrawForeground(b, +this,this.canvas)}else{var t=this.editor_alpha;b.globalAlpha=t;this.render_shadows&&!g?(b.shadowColor=e.DEFAULT_SHADOW_COLOR,b.shadowOffsetX=2*this.ds.scale,b.shadowOffsetY=2*this.ds.scale,b.shadowBlur=3*this.ds.scale):b.shadowColor="transparent";if(!a.flags.collapsed||!a.onDrawCollapsed||!0!=a.onDrawCollapsed(b,this)){var c=a._shape||e.BOX_SHAPE;D.set(a.size);var l=a.horizontal;if(a.flags.collapsed){b.font=this.inner_text_font;var m=a.getTitle?a.getTitle():a.title;null!=m&&(a._collapsed_width=Math.min(a.size[0], +b.measureText(m).width+2*e.NODE_TITLE_HEIGHT),D[0]=a._collapsed_width,D[1]=0)}a.clip_area&&(b.save(),b.beginPath(),c==e.BOX_SHAPE?b.rect(0,0,D[0],D[1]):c==e.ROUND_SHAPE?b.roundRect(0,0,D[0],D[1],10):c==e.CIRCLE_SHAPE&&b.arc(0.5*D[0],0.5*D[1],0.5*D[0],0,2*Math.PI),b.clip());a.has_errors&&(f="red");this.drawNodeShape(a,b,D,d,f,a.is_selected,a.mouseOver);b.shadowColor="transparent";if(a.onDrawForeground)a.onDrawForeground(b,this,this.canvas);b.textAlign=l?"center":"left";b.font=this.inner_text_font; +f=!g;c=this.connecting_output;b.lineWidth=1;var m=0,q=new Float32Array(2);if(!a.flags.collapsed){if(a.inputs)for(d=0;dthis.ds.scale,r=a._shape||a.constructor.shape||e.ROUND_SHAPE,s=a.constructor.title_mode,z=!0;s== -e.TRANSPARENT_TITLE?z=!1:s==e.AUTOHIDE_TITLE&&c&&(z=!0);v[0]=0;v[1]=z?-g:0;v[2]=d[0]+1;v[3]=z?d[1]+g:d[1];c=b.globalAlpha;b.beginPath();r==e.BOX_SHAPE||l?b.fillRect(v[0],v[1],v[2],v[3]):r==e.ROUND_SHAPE||r==e.CARD_SHAPE?b.roundRect(v[0],v[1],v[2],v[3],this.round_radius,r==e.CARD_SHAPE?0:this.round_radius):r==e.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0],0,2*Math.PI);b.fill();a.flags.collapsed||(b.shadowColor="transparent",b.fillStyle="rgba(0,0,0,0.2)",b.fillRect(0,-1,v[2],2));b.shadowColor="transparent"; -if(a.onDrawBackground)a.onDrawBackground(b,this,this.canvas,this.graph_mouse);if(z||s==e.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,g,d,this.ds.scale,f);else if(s!=e.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){z=a.constructor.title_color||f;a.flags.collapsed&&(b.shadowColor=e.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var u=h.gradients[z];u||(u=h.gradients[z]=b.createLinearGradient(0,0,400,0),u.addColorStop(0,z),u.addColorStop(1,"#000"));b.fillStyle= -u}else b.fillStyle=z;b.beginPath();r==e.BOX_SHAPE||l?b.rect(0,-g,d[0]+1,g):r!=e.ROUND_SHAPE&&r!=e.CARD_SHAPE||b.roundRect(0,-g,d[0]+1,g,this.round_radius,a.flags.collapsed?this.round_radius:0);b.fill();b.shadowColor="transparent"}if(a.onDrawTitleBox)a.onDrawTitleBox(b,g,d,this.ds.scale);else r==e.ROUND_SHAPE||r==e.CIRCLE_SHAPE||r==e.CARD_SHAPE?(l&&(b.fillStyle="black",b.beginPath(),b.arc(0.5*g,-0.5*g,6,0,2*Math.PI),b.fill()),b.fillStyle=a.boxcolor||e.NODE_DEFAULT_BOXCOLOR,l?b.fillRect(0.5*g-5,-0.5* -g-5,10,10):(b.beginPath(),b.arc(0.5*g,-0.5*g,5,0,2*Math.PI),b.fill())):(l&&(b.fillStyle="black",b.fillRect(0.5*(g-10)-1,-0.5*(g+10)-1,12,12)),b.fillStyle=a.boxcolor||e.NODE_DEFAULT_BOXCOLOR,b.fillRect(0.5*(g-10),-0.5*(g+10),10,10));b.globalAlpha=c;if(a.onDrawTitleText)a.onDrawTitleText(b,g,d,this.ds.scale,this.title_text_font,q);!l&&(b.font=this.title_text_font,c=String(a.getTitle()))&&(b.fillStyle=q?"white":a.constructor.title_text_color||this.node_title_color,a.flags.collapsed?(b.textAlign="left", -b.measureText(c),b.fillText(c.substr(0,20),g,e.NODE_TITLE_TEXT_Y-g),b.textAlign="left"):(b.textAlign="left",b.fillText(c,g,e.NODE_TITLE_TEXT_Y-g)));a.flags.collapsed||!a.subgraph||a.skip_subgraph_button||(c=e.NODE_TITLE_HEIGHT,z=a.size[0]-c,u=e.isInsideRectangle(this.graph_mouse[0]-a.pos[0],this.graph_mouse[1]-a.pos[1],z+2,-c+2,c-4,c-4),b.fillStyle=u?"#888":"#555",r==e.BOX_SHAPE||l?b.fillRect(z+2,-c+2,c-4,c-4):(b.beginPath(),b.roundRect(z+2,-c+2,c-4,c-4,4),b.fill()),b.fillStyle="#333",b.beginPath(), -b.moveTo(z+0.2*c,0.6*-c),b.lineTo(z+0.8*c,0.6*-c),b.lineTo(z+0.5*c,0.3*-c),b.fill());if(a.onDrawTitle)a.onDrawTitle(b)}if(q){if(a.onBounding)a.onBounding(v);s==e.TRANSPARENT_TITLE&&(v[1]-=g,v[3]+=g);b.lineWidth=1;b.globalAlpha=0.8;b.beginPath();r==e.BOX_SHAPE?b.rect(-6+v[0],-6+v[1],12+v[2],12+v[3]):r==e.ROUND_SHAPE||r==e.CARD_SHAPE&&a.flags.collapsed?b.roundRect(-6+v[0],-6+v[1],12+v[2],12+v[3],2*this.round_radius):r==e.CARD_SHAPE?b.roundRect(-6+v[0],-6+v[1],12+v[2],12+v[3],2*this.round_radius,2): -r==e.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0]+6,0,2*Math.PI);b.strokeStyle="#FFF";b.stroke();b.strokeStyle=f;b.globalAlpha=1}};var H=new Float32Array(4),w=new Float32Array(4),t=new Float32Array(2),l=new Float32Array(2);h.prototype.drawConnections=function(a){var b=e.getTime(),d=this.visible_area;H[0]=d[0]-20;H[1]=d[1]-20;H[2]=d[2]+40;H[3]=d[3]+40;a.lineWidth=this.connections_width;a.fillStyle="#AAA";a.strokeStyle="#AAA";a.globalAlpha=this.editor_alpha;for(var d=this.graph._nodes,f=0,g=d.length;f< -g;++f){var q=d[f];if(q.inputs&&q.inputs.length)for(var c=0;cw[2]&&(w[0]+=w[2],w[2]=Math.abs(w[2]));0>w[3]&&(w[1]+=w[3],w[3]=Math.abs(w[3]));if(B(w,H)){var p=r.outputs[s],s=q.inputs[c];if(p&& -s&&(r=p.dir||(r.horizontal?e.DOWN:e.RIGHT),s=s.dir||(q.horizontal?e.UP:e.LEFT),this.renderLink(a,z,u,h,!1,0,null,r,s),h&&h._last_time&&1E3>b-h._last_time)){var p=2-0.002*(b-h._last_time),R=a.globalAlpha;a.globalAlpha=R*p;this.renderLink(a,z,u,h,!0,p,"white",r,s);a.globalAlpha=R}}}}}}a.globalAlpha=1};h.prototype.renderLink=function(a,b,d,f,g,q,c,l,r,s){f&&this.visible_links.push(f);!c&&f&&(c=f.color||h.link_type_colors[f.type]);c||(c=this.default_link_color);null!=f&&this.highlighted_links[f.id]&& -(c="#FFF");l=l||e.RIGHT;r=r||e.LEFT;var z=D(b,d);this.render_connections_border&&0.6b[1]?0:Math.PI,a.save(),a.translate(u[0],u[1]),a.rotate(w),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore(),a.save(),a.translate(s[0],s[1]),a.rotate(p), -a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(g[0],g[1],5,0,2*Math.PI),a.fill());if(q)for(a.fillStyle=c,u=0;5>u;++u)q=(0.001*e.getTime()+0.2*u)%1,g=this.computeConnectionPoint(b,d,q,l,r),a.beginPath(),a.arc(g[0],g[1],5,0,2*Math.PI),a.fill()};h.prototype.computeConnectionPoint=function(a,b,d,f,g){f=f||e.RIGHT;g=g||e.LEFT;var q=D(a,b),c=[a[0],a[1]],l=[b[0],b[1]];switch(f){case e.LEFT:c[0]+=-0.25*q;break;case e.RIGHT:c[0]+=0.25*q;break;case e.UP:c[1]+= --0.25*q;break;case e.DOWN:c[1]+=0.25*q}switch(g){case e.LEFT:l[0]+=-0.25*q;break;case e.RIGHT:l[0]+=0.25*q;break;case e.UP:l[1]+=-0.25*q;break;case e.DOWN:l[1]+=0.25*q}f=(1-d)*(1-d)*(1-d);g=3*(1-d)*(1-d)*d;q=3*(1-d)*d*d;d*=d*d;return[f*a[0]+g*c[0]+q*l[0]+d*b[0],f*a[1]+g*c[1]+q*l[1]+d*b[1]]};h.prototype.drawExecutionOrder=function(a){a.shadowColor="transparent";a.globalAlpha=0.25;a.textAlign="center";a.strokeStyle="white";a.globalAlpha=0.75;for(var b=this.visible_nodes,d=0;dc||c>h-12||lu.last_y+w)){f=u.value;switch(u.type){case "button":if("mousemove"===d.type)break;u.callback&&setTimeout(function(){u.callback(u,r,a,b,d)},20);this.dirty_canvas=u.clicked=!0;break;case "slider":s=Math.clamp((c-10)/(h-20),0,1);u.value=u.options.min+(u.options.max-u.options.min)*s;u.callback&&setTimeout(function(){g(u,u.value)},20);this.dirty_canvas= -!0;break;case "number":case "combo":f=u.value;if("mousemove"==d.type&&"number"==u.type)u.value+=0.1*d.deltaX*(u.options.step||1),null!=u.options.min&&u.valueu.options.max&&(u.value=u.options.max);else if("mousedown"==d.type){var p=u.options.values;p&&p.constructor===Function&&(p=u.options.values(u,a));var t=null;"number"!=u.type&&(t=p.constructor===Array?p:Object.keys(p));c=40>c?-1:c>h-40?1:0;if("number"==u.type)u.value+=0.1*c*(u.options.step|| -1),null!=u.options.min&&u.valueu.options.max&&(u.value=u.options.max);else if(c)s=-1,this.last_mouseclick=0,s=p.constructor===Object?t.indexOf(String(u.value))+c:t.indexOf(u.value)+c,s>=t.length&&(s=t.length-1),0>s&&(s=0),u.value=p.constructor===Array?p[s]:s;else{var k=p!=t?Object.values(p):p;new e.ContextMenu(k,{scale:Math.max(1,this.ds.scale),event:d,className:"dark",callback:n.bind(u)},s);var n=function(a,b,d){p!=t&&(a=k.indexOf(a)); -this.value=a;g(this,a);r.dirty_canvas=!0;return!1}}}else"mouseup"==d.type&&"number"==u.type&&(c=40>c?-1:c>h-40?1:0,200>d.click_time&&0==c&&this.prompt("Value",u.value,function(a){this.value=Number(a);g(this,this.value)}.bind(u),d));f!=u.value&&setTimeout(function(){g(this,this.value)}.bind(u),20);this.dirty_canvas=!0;break;case "toggle":"mousedown"==d.type&&(u.value=!u.value,setTimeout(function(){g(u,u.value)},20));break;case "string":case "text":"mousedown"==d.type&&this.prompt("Value",u.value,function(a){this.value= -a;g(this,a)}.bind(u),d);break;default:u.mouse&&(this.dirty_canvas=u.mouse(d,[c,l],a))}f!=u.value&&a.graph._version++;return u}}}return null};h.prototype.drawGroups=function(a,b){if(this.graph){var d=this.graph._groups;b.save();b.globalAlpha=0.5*this.editor_alpha;for(var f=0;fd&&0.01>b.editor_alpha&&(clearInterval(f),1>d&&(b.live_mode=!0));1"+r+""+a+"",value:r})}if(p.length)return new e.ContextMenu(p,{event:d,callback:c,parentMenu:f,allow_html:!0,node:g},b),!1}};h.decodeHTML=function(a){var b=document.createElement("div");b.innerText=a;return b.innerHTML};h.onResizeNode=function(a,b,d,f,g){if(g){g.size=g.computeSize();if(g.onResize)g.onResize(g.size);g.setDirtyCanvas(!0,!0)}};h.prototype.showLinkMenu=function(a,b){var d=this;console.log(a); -var f=new e.ContextMenu(["Add Node",null,"Delete"],{event:b,title:null!=a.data?a.data.constructor.name:null,callback:function(b,c,e){switch(b){case "Add Node":h.onMenuAdd(null,null,e,f,function(b){console.log("node autoconnect");var g=d.graph.getNodeById(a.origin_id),f=d.graph.getNodeById(a.target_id);b.inputs&&b.inputs.length&&b.outputs&&b.outputs.length&&g.outputs[a.origin_slot].type==b.inputs[0].type&&b.outputs[0].type==f.inputs[0].type&&(g.connect(a.origin_slot,b,0),b.connect(0,f,a.target_slot), -b.pos[0]-=0.5*b.size[0])});break;case "Delete":d.graph.removeLink(a.id)}}});return!1};h.onShowPropertyEditor=function(a,b,d,f,g){function c(){var b=r.value;"Number"==a.type?b=Number(b):"Boolean"==a.type&&(b=Boolean(b));g[e]=b;l.parentNode&&l.parentNode.removeChild(l);g.setDirtyCanvas(!0,!0)}var e=a.property||"title";b=g[e];var l=document.createElement("div");l.className="graphdialog";l.innerHTML="";l.querySelector(".name").innerText= -e;var r=l.querySelector("input");r&&(r.value=b,r.addEventListener("blur",function(a){this.focus()}),r.addEventListener("keydown",function(a){13==a.keyCode&&(c(),a.preventDefault(),a.stopPropagation())}));b=h.active_canvas.canvas;d=b.getBoundingClientRect();var s=f=-20;d&&(f-=d.left,s-=d.top);event?(l.style.left=event.clientX+f+"px",l.style.top=event.clientY+s+"px"):(l.style.left=0.5*b.width+f+"px",l.style.top=0.5*b.height+s+"px");l.querySelector("button").addEventListener("click",c);b.parentNode.appendChild(l)}; -h.prototype.prompt=function(a,b,d,f){var g=this;a=a||"";var c=!1,e=document.createElement("div");e.className="graphdialog rounded";e.innerHTML=" ";e.close=function(){g.prompt_box=null;e.parentNode&&e.parentNode.removeChild(e)};1h.search_limit)break}}s=null;if(Array.prototype.filter)s=Object.keys(e.registered_node_types).filter(T);else for(l in s=[],e.registered_node_types)T(l)&&s.push(l);for(l=0;lh.search_limit);l++);var T=function(a){var b= -e.registered_node_types[a];return r&&b.filter!=r?!1:-1!==a.toLowerCase().indexOf(d)}}}var g=this,c=h.active_canvas,l=c.canvas,p=l.ownerDocument||document,r=document.createElement("div");r.className="litegraph litesearchbox graphdialog rounded";r.innerHTML="Search
";r.close=function(){g.search_box=null;p.body.focus();p.body.style.overflow="";setTimeout(function(){g.canvas.focus()},20);r.parentNode&& -r.parentNode.removeChild(r)};var s=null;1 -l.height-200&&(w.style.maxHeight=l.height-a.layerY-20+"px");n.focus();return r};h.prototype.showEditPropertyValue=function(a,b,d){function f(){g(u.value)}function g(g){c&&c.values&&c.values.constructor===Object&&void 0!=c.values[g]&&(g=c.values[g]);"number"==typeof a.properties[b]&&(g=Number(g));if("array"==e||"object"==e)g=JSON.parse(g);a.properties[b]=g;a.graph&&a.graph._version++;if(a.onPropertyChanged)a.onPropertyChanged(b,g);if(d.onclose)d.onclose();p.close();a.setDirtyCanvas(!0,!0)}if(a&&void 0!== -a.properties[b]){d=d||{};var c=a.getPropertyInfo(b),e=c.type,l="";if("string"==e||"number"==e||"array"==e||"object"==e)l="";else if("enum"!=e&&"combo"!=e||!c.values)if("boolean"==e)l="";else{console.warn("unknown type: "+e);return}else{var l=""}var p=this.createDialog(""+b+""+l+"",d);if("enum"!=e&&"combo"!=e||!c.values)if("boolean"==e)(u=p.querySelector("input"))&&u.addEventListener("click",function(a){g(!!u.checked)});else{if(u=p.querySelector("input"))u.addEventListener("blur",function(a){this.focus()}),s=void 0!==a.properties[b]?a.properties[b]:"",s=JSON.stringify(s),u.value=s,u.addEventListener("keydown", -function(a){13==a.keyCode&&(f(),a.preventDefault(),a.stopPropagation())})}else{var u=p.querySelector("select");u.addEventListener("change",function(a){g(a.target.value)})}p.querySelector("button").addEventListener("click",f);return p}};h.prototype.createDialog=function(a,b){b=b||{};var d=document.createElement("div");d.className="graphdialog";d.innerHTML=a;var f=this.canvas.getBoundingClientRect(),g=-20,c=-20;f&&(g-=f.left,c-=f.top);b.position?(g+=b.position[0],c+=b.position[1]):b.event?(g+=b.event.clientX, -c+=b.event.clientY):(g+=0.5*this.canvas.width,c+=0.5*this.canvas.height);d.style.left=g+"px";d.style.top=c+"px";this.canvas.parentNode.appendChild(d);d.close=function(){this.parentNode&&this.parentNode.removeChild(this)};return d};h.prototype.createPanel=function(a,b){b=b||{};var d=b.window||window,f=document.createElement("div");f.className="litegraph dialog";f.innerHTML="
"; +3,a.fillStyle="#454",a.beginPath(),a.roundRect(d[0]-0.5*f,d[1]-15-24,f,24,3,3),a.moveTo(d[0]-10,d[1]-15),a.lineTo(d[0]+10,d[1]-15),a.lineTo(d[0],d[1]-5),a.fill(),a.shadowColor="transparent",a.textAlign="center",a.fillStyle="#CEC",a.fillText(g,d[0],d[1]-15-24*0.3))}};var u=new Float32Array(4);k.prototype.drawNodeShape=function(a,b,d,f,g,t,c){b.strokeStyle=f;b.fillStyle=g;g=e.NODE_TITLE_HEIGHT;var l=0.5>this.ds.scale,m=a._shape||a.constructor.shape||e.ROUND_SHAPE,r=a.constructor.title_mode,q=!0;r== +e.TRANSPARENT_TITLE?q=!1:r==e.AUTOHIDE_TITLE&&c&&(q=!0);u[0]=0;u[1]=q?-g:0;u[2]=d[0]+1;u[3]=q?d[1]+g:d[1];c=b.globalAlpha;b.beginPath();m==e.BOX_SHAPE||l?b.fillRect(u[0],u[1],u[2],u[3]):m==e.ROUND_SHAPE||m==e.CARD_SHAPE?b.roundRect(u[0],u[1],u[2],u[3],this.round_radius,m==e.CARD_SHAPE?0:this.round_radius):m==e.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0],0,2*Math.PI);b.fill();a.flags.collapsed||(b.shadowColor="transparent",b.fillStyle="rgba(0,0,0,0.2)",b.fillRect(0,-1,u[2],2));b.shadowColor="transparent"; +if(a.onDrawBackground)a.onDrawBackground(b,this,this.canvas,this.graph_mouse);if(q||r==e.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,g,d,this.ds.scale,f);else if(r!=e.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){q=a.constructor.title_color||f;a.flags.collapsed&&(b.shadowColor=e.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var h=k.gradients[q];h||(h=k.gradients[q]=b.createLinearGradient(0,0,400,0),h.addColorStop(0,q),h.addColorStop(1,"#000"));b.fillStyle= +h}else b.fillStyle=q;b.beginPath();m==e.BOX_SHAPE||l?b.rect(0,-g,d[0]+1,g):m!=e.ROUND_SHAPE&&m!=e.CARD_SHAPE||b.roundRect(0,-g,d[0]+1,g,this.round_radius,a.flags.collapsed?this.round_radius:0);b.fill();b.shadowColor="transparent"}if(a.onDrawTitleBox)a.onDrawTitleBox(b,g,d,this.ds.scale);else m==e.ROUND_SHAPE||m==e.CIRCLE_SHAPE||m==e.CARD_SHAPE?(l&&(b.fillStyle="black",b.beginPath(),b.arc(0.5*g,-0.5*g,6,0,2*Math.PI),b.fill()),b.fillStyle=a.boxcolor||e.NODE_DEFAULT_BOXCOLOR,l?b.fillRect(0.5*g-5,-0.5* +g-5,10,10):(b.beginPath(),b.arc(0.5*g,-0.5*g,5,0,2*Math.PI),b.fill())):(l&&(b.fillStyle="black",b.fillRect(0.5*(g-10)-1,-0.5*(g+10)-1,12,12)),b.fillStyle=a.boxcolor||e.NODE_DEFAULT_BOXCOLOR,b.fillRect(0.5*(g-10),-0.5*(g+10),10,10));b.globalAlpha=c;if(a.onDrawTitleText)a.onDrawTitleText(b,g,d,this.ds.scale,this.title_text_font,t);!l&&(b.font=this.title_text_font,c=String(a.getTitle()))&&(b.fillStyle=t?"white":a.constructor.title_text_color||this.node_title_color,a.flags.collapsed?(b.textAlign="left", +b.measureText(c),b.fillText(c.substr(0,20),g,e.NODE_TITLE_TEXT_Y-g),b.textAlign="left"):(b.textAlign="left",b.fillText(c,g,e.NODE_TITLE_TEXT_Y-g)));a.flags.collapsed||!a.subgraph||a.skip_subgraph_button||(c=e.NODE_TITLE_HEIGHT,q=a.size[0]-c,h=e.isInsideRectangle(this.graph_mouse[0]-a.pos[0],this.graph_mouse[1]-a.pos[1],q+2,-c+2,c-4,c-4),b.fillStyle=h?"#888":"#555",m==e.BOX_SHAPE||l?b.fillRect(q+2,-c+2,c-4,c-4):(b.beginPath(),b.roundRect(q+2,-c+2,c-4,c-4,4),b.fill()),b.fillStyle="#333",b.beginPath(), +b.moveTo(q+0.2*c,0.6*-c),b.lineTo(q+0.8*c,0.6*-c),b.lineTo(q+0.5*c,0.3*-c),b.fill());if(a.onDrawTitle)a.onDrawTitle(b)}if(t){if(a.onBounding)a.onBounding(u);r==e.TRANSPARENT_TITLE&&(u[1]-=g,u[3]+=g);b.lineWidth=1;b.globalAlpha=0.8;b.beginPath();m==e.BOX_SHAPE?b.rect(-6+u[0],-6+u[1],12+u[2],12+u[3]):m==e.ROUND_SHAPE||m==e.CARD_SHAPE&&a.flags.collapsed?b.roundRect(-6+u[0],-6+u[1],12+u[2],12+u[3],2*this.round_radius):m==e.CARD_SHAPE?b.roundRect(-6+u[0],-6+u[1],12+u[2],12+u[3],2*this.round_radius,2): +m==e.CIRCLE_SHAPE&&b.arc(0.5*d[0],0.5*d[1],0.5*d[0]+6,0,2*Math.PI);b.strokeStyle="#FFF";b.stroke();b.strokeStyle=f;b.globalAlpha=1}};var I=new Float32Array(4),q=new Float32Array(4),r=new Float32Array(2),l=new Float32Array(2);k.prototype.drawConnections=function(a){var b=e.getTime(),d=this.visible_area;I[0]=d[0]-20;I[1]=d[1]-20;I[2]=d[2]+40;I[3]=d[3]+40;a.lineWidth=this.connections_width;a.fillStyle="#AAA";a.strokeStyle="#AAA";a.globalAlpha=this.editor_alpha;for(var d=this.graph._nodes,f=0,g=d.length;f< +g;++f){var t=d[f];if(t.inputs&&t.inputs.length)for(var c=0;cq[2]&&(q[0]+=q[2],q[2]=Math.abs(q[2]));0>q[3]&&(q[1]+=q[3],q[3]=Math.abs(q[3]));if(F(q,I)){var H=k.outputs[h],h=t.inputs[c];if(H&& +h&&(k=H.dir||(k.horizontal?e.DOWN:e.RIGHT),h=h.dir||(t.horizontal?e.UP:e.LEFT),this.renderLink(a,s,G,m,!1,0,null,k,h),m&&m._last_time&&1E3>b-m._last_time)){var H=2-0.002*(b-m._last_time),A=a.globalAlpha;a.globalAlpha=A*H;this.renderLink(a,s,G,m,!0,H,"white",k,h);a.globalAlpha=A}}}}}}a.globalAlpha=1};k.prototype.renderLink=function(a,b,d,f,g,t,c,l,m,q){f&&this.visible_links.push(f);!c&&f&&(c=f.color||k.link_type_colors[f.type]);c||(c=this.default_link_color);null!=f&&this.highlighted_links[f.id]&& +(c="#FFF");l=l||e.RIGHT;m=m||e.LEFT;var r=B(b,d);this.render_connections_border&&0.6b[1]?0:Math.PI,a.save(),a.translate(h[0],h[1]),a.rotate(H),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore(),a.save(),a.translate(q[0],q[1]),a.rotate(A), +a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(g[0],g[1],5,0,2*Math.PI),a.fill());if(t)for(a.fillStyle=c,h=0;5>h;++h)t=(0.001*e.getTime()+0.2*h)%1,g=this.computeConnectionPoint(b,d,t,l,m),a.beginPath(),a.arc(g[0],g[1],5,0,2*Math.PI),a.fill()};k.prototype.computeConnectionPoint=function(a,b,d,f,g){f=f||e.RIGHT;g=g||e.LEFT;var t=B(a,b),c=[a[0],a[1]],l=[b[0],b[1]];switch(f){case e.LEFT:c[0]+=-0.25*t;break;case e.RIGHT:c[0]+=0.25*t;break;case e.UP:c[1]+= +-0.25*t;break;case e.DOWN:c[1]+=0.25*t}switch(g){case e.LEFT:l[0]+=-0.25*t;break;case e.RIGHT:l[0]+=0.25*t;break;case e.UP:l[1]+=-0.25*t;break;case e.DOWN:l[1]+=0.25*t}f=(1-d)*(1-d)*(1-d);g=3*(1-d)*(1-d)*d;t=3*(1-d)*d*d;d*=d*d;return[f*a[0]+g*c[0]+t*l[0]+d*b[0],f*a[1]+g*c[1]+t*l[1]+d*b[1]]};k.prototype.drawExecutionOrder=function(a){a.shadowColor="transparent";a.globalAlpha=0.25;a.textAlign="center";a.strokeStyle="white";a.globalAlpha=0.75;for(var b=this.visible_nodes,d=0;dc||c>m-12||lh.last_y+H)){f=h.value;switch(h.type){case "button":if("mousemove"===d.type)break;h.callback&&setTimeout(function(){h.callback(h,k,a,b,d)},20);this.dirty_canvas=h.clicked=!0;break;case "slider":q=Math.clamp((c-10)/(m-20),0,1);h.value=h.options.min+(h.options.max-h.options.min)*q;h.callback&&setTimeout(function(){g(h,h.value)},20);this.dirty_canvas= +!0;break;case "number":case "combo":f=h.value;if("mousemove"==d.type&&"number"==h.type)h.value+=0.1*d.deltaX*(h.options.step||1),null!=h.options.min&&h.valueh.options.max&&(h.value=h.options.max);else if("mousedown"==d.type){var A=h.options.values;A&&A.constructor===Function&&(A=h.options.values(h,a));var E=null;"number"!=h.type&&(E=A.constructor===Array?A:Object.keys(A));c=40>c?-1:c>m-40?1:0;if("number"==h.type)h.value+=0.1*c*(h.options.step|| +1),null!=h.options.min&&h.valueh.options.max&&(h.value=h.options.max);else if(c)q=-1,this.last_mouseclick=0,q=A.constructor===Object?E.indexOf(String(h.value))+c:E.indexOf(h.value)+c,q>=E.length&&(q=E.length-1),0>q&&(q=0),h.value=A.constructor===Array?A[q]:q;else{var K=A!=E?Object.values(A):A;new e.ContextMenu(K,{scale:Math.max(1,this.ds.scale),event:d,className:"dark",callback:s.bind(h)},q);var s=function(a,b,d){A!=E&&(a=K.indexOf(a)); +this.value=a;g(this,a);k.dirty_canvas=!0;return!1}}}else"mouseup"==d.type&&"number"==h.type&&(c=40>c?-1:c>m-40?1:0,200>d.click_time&&0==c&&this.prompt("Value",h.value,function(a){this.value=Number(a);g(this,this.value)}.bind(h),d));f!=h.value&&setTimeout(function(){g(this,this.value)}.bind(h),20);this.dirty_canvas=!0;break;case "toggle":"mousedown"==d.type&&(h.value=!h.value,setTimeout(function(){g(h,h.value)},20));break;case "string":case "text":"mousedown"==d.type&&this.prompt("Value",h.value,function(a){this.value= +a;g(this,a)}.bind(h),d);break;default:h.mouse&&(this.dirty_canvas=h.mouse(d,[c,l],a))}if(f!=h.value){if(a.onWidgetChanged)a.onWidgetChanged(h.name,h.value,f,h);a.graph._version++}return h}}}return null};k.prototype.drawGroups=function(a,b){if(this.graph){var d=this.graph._groups;b.save();b.globalAlpha=0.5*this.editor_alpha;for(var f=0;f +d&&0.01>b.editor_alpha&&(clearInterval(f),1>d&&(b.live_mode=!0));1"+q+""+a+"",value:q})}if(m.length)return new e.ContextMenu(m,{event:d,callback:c,parentMenu:f,allow_html:!0,node:g},b),!1}};k.decodeHTML=function(a){var b=document.createElement("div");b.innerText=a;return b.innerHTML};k.onResizeNode=function(a,b,d,f,g){if(g){g.size= +g.computeSize();if(g.onResize)g.onResize(g.size);g.setDirtyCanvas(!0,!0)}};k.prototype.showLinkMenu=function(a,b){var d=this;console.log(a);var f=new e.ContextMenu(["Add Node",null,"Delete"],{event:b,title:null!=a.data?a.data.constructor.name:null,callback:function(b,c,e){switch(b){case "Add Node":k.onMenuAdd(null,null,e,f,function(b){console.log("node autoconnect");var g=d.graph.getNodeById(a.origin_id),f=d.graph.getNodeById(a.target_id);b.inputs&&b.inputs.length&&b.outputs&&b.outputs.length&&g.outputs[a.origin_slot].type== +b.inputs[0].type&&b.outputs[0].type==f.inputs[0].type&&(g.connect(a.origin_slot,b,0),b.connect(0,f,a.target_slot),b.pos[0]-=0.5*b.size[0])});break;case "Delete":d.graph.removeLink(a.id)}}});return!1};k.onShowPropertyEditor=function(a,b,d,f,g){function c(){var b=m.value;"Number"==a.type?b=Number(b):"Boolean"==a.type&&(b=Boolean(b));g[e]=b;l.parentNode&&l.parentNode.removeChild(l);g.setDirtyCanvas(!0,!0)}var e=a.property||"title";b=g[e];var l=document.createElement("div");l.className="graphdialog"; +l.innerHTML="";l.querySelector(".name").innerText=e;var m=l.querySelector("input");m&&(m.value=b,m.addEventListener("blur",function(a){this.focus()}),m.addEventListener("keydown",function(a){13==a.keyCode&&(c(),a.preventDefault(),a.stopPropagation())}));b=k.active_canvas.canvas;d=b.getBoundingClientRect();var q=f=-20;d&&(f-=d.left,q-=d.top);event?(l.style.left=event.clientX+f+"px",l.style.top=event.clientY+q+ +"px"):(l.style.left=0.5*b.width+f+"px",l.style.top=0.5*b.height+q+"px");l.querySelector("button").addEventListener("click",c);b.parentNode.appendChild(l)};k.prototype.prompt=function(a,b,d,f){var g=this;a=a||"";var c=!1,e=document.createElement("div");e.className="graphdialog rounded";e.innerHTML=" ";e.close=function(){g.prompt_box=null;e.parentNode&&e.parentNode.removeChild(e)};1k.search_limit)break}}q=null;if(Array.prototype.filter)q=Object.keys(e.registered_node_types).filter(U); +else for(l in q=[],e.registered_node_types)U(l)&&q.push(l);for(l=0;lk.search_limit);l++);var U=function(a){var b=e.registered_node_types[a];return m&&b.filter!=m?!1:-1!==a.toLowerCase().indexOf(d)}}}var g=this,c=k.active_canvas,l=c.canvas,m=l.ownerDocument||document,q=document.createElement("div");q.className="litegraph litesearchbox graphdialog rounded";q.innerHTML="Search
"; +q.close=function(){g.search_box=null;m.body.focus();m.body.style.overflow="";setTimeout(function(){g.canvas.focus()},20);q.parentNode&&q.parentNode.removeChild(q)};var h=null;1l.height-200&&(r.style.maxHeight=l.height-a.layerY-20+"px");E.focus();return q};k.prototype.showEditPropertyValue=function(a,b,d){function f(){g(r.value)}function g(g){c&&c.values&&c.values.constructor===Object&&void 0!=c.values[g]&&(g=c.values[g]);"number"==typeof a.properties[b]&&(g=Number(g));if("array"==e||"object"==e)g=JSON.parse(g);a.properties[b]= +g;a.graph&&a.graph._version++;if(a.onPropertyChanged)a.onPropertyChanged(b,g);if(d.onclose)d.onclose();h.close();a.setDirtyCanvas(!0,!0)}if(a&&void 0!==a.properties[b]){d=d||{};var c=a.getPropertyInfo(b),e=c.type,l="";if("string"==e||"number"==e||"array"==e||"object"==e)l="";else if("enum"!=e&&"combo"!=e||!c.values)if("boolean"==e)l="";else{console.warn("unknown type: "+e); +return}else{var l=""}var h=this.createDialog(""+b+""+l+"",d);if("enum"!=e&&"combo"!=e||!c.values)if("boolean"==e)(r=h.querySelector("input"))&&r.addEventListener("click",function(a){g(!!r.checked)});else{if(r=h.querySelector("input"))r.addEventListener("blur", +function(a){this.focus()}),q=void 0!==a.properties[b]?a.properties[b]:"",q=JSON.stringify(q),r.value=q,r.addEventListener("keydown",function(a){13==a.keyCode&&(f(),a.preventDefault(),a.stopPropagation())})}else{var r=h.querySelector("select");r.addEventListener("change",function(a){g(a.target.value)})}h.querySelector("button").addEventListener("click",f);return h}};k.prototype.createDialog=function(a,b){b=b||{};var d=document.createElement("div");d.className="graphdialog";d.innerHTML=a;var f=this.canvas.getBoundingClientRect(), +g=-20,c=-20;f&&(g-=f.left,c-=f.top);b.position?(g+=b.position[0],c+=b.position[1]):b.event?(g+=b.event.clientX,c+=b.event.clientY):(g+=0.5*this.canvas.width,c+=0.5*this.canvas.height);d.style.left=g+"px";d.style.top=c+"px";this.canvas.parentNode.appendChild(d);d.close=function(){this.parentNode&&this.parentNode.removeChild(this)};return d};k.prototype.createPanel=function(a,b){b=b||{};var d=b.window||window,f=document.createElement("div");f.className="litegraph dialog";f.innerHTML="
"; f.header=f.querySelector(".dialog-header");b.width&&(f.style.width=b.width+(b.width.constructor===Number?"px":""));b.height&&(f.style.height=b.height+(b.height.constructor===Number?"px":""));if(b.closable){var g=document.createElement("span");g.innerHTML="✕";g.classList.add("close");g.addEventListener("click",function(){f.close()});f.header.appendChild(g)}f.title_element=f.querySelector(".dialog-title");f.title_element.innerText=a;f.content=f.querySelector(".dialog-content");f.footer=f.querySelector(".dialog-footer"); f.close=function(){this.parentNode.removeChild(this)};f.clear=function(){this.content.innerHTML=""};f.addHTML=function(a,b,d){var g=document.createElement("div");b&&(g.className=b);g.innerHTML=a;d?f.footer.appendChild(g):f.content.appendChild(g);return g};f.addButton=function(a,b,d){var g=document.createElement("button");g.innerText=a;g.options=d;g.classList.add("btn");g.addEventListener("click",b);f.footer.appendChild(g);return g};f.addSeparator=function(){var a=document.createElement("div");a.className= -"separator";f.content.appendChild(a)};f.addWidget=function(a,b,g,c,l){function p(a,b){console.log("change",a,b);c.callback&&c.callback(a,b);l&&l(a,b)}c=c||{};var u=String(g);"number"==a&&(u=g.toFixed(3));var w=document.createElement("div");w.className="property";w.innerHTML="";w.querySelector(".property_name").innerText=b;var t=w.querySelector(".property_value");t.innerText=u;w.dataset.property=b;w.dataset.type=c.type||a;w.options= -c;w.value=g;if("boolean"==a)w.classList.add("boolean"),g&&w.classList.add("bool-on"),w.addEventListener("click",function(){var a=this.dataset.property;this.value=!this.value;this.classList.toggle("bool-on");this.querySelector(".property_value").innerText=this.value?"true":"false";p(a,this.value)});else if("string"==a||"number"==a)t.setAttribute("contenteditable",!0),t.addEventListener("keydown",function(a){"Enter"==a.code&&(a.preventDefault(),this.blur())}),t.addEventListener("blur",function(){var a= -this.innerText,b=this.parentNode.dataset.property;"number"==this.parentNode.dataset.type&&(a=Number(a));p(b,a)});else if("enum"==a||"combo"==a)u=h.getPropertyPrintableValue(g,c.values);t.innerText=u;t.addEventListener("click",function(a){var b=this.parentNode.dataset.property,g=this;new e.ContextMenu(c.values||[],{event:a,className:"dark",callback:function(a,d,f){g.innerText=a;p(b,a);return!1}},d)});f.content.appendChild(w);return w};return f};h.getPropertyPrintableValue=function(a,b){if(!b||b.constructor=== -Array)return String(a);if(b.constructor===Object){var d="",f;for(f in b)if(b[f]==a){d=f;break}return String(a)+" ("+d+")"}};h.prototype.showShowNodePanel=function(a){window.SELECTED_NODE=a;var b=document.querySelector("#node-panel");b&&b.close();var d=this.getCanvasWindow(),b=this.createPanel(a.title||"",{closable:!0,window:d});b.id="node-panel";b.node=a;b.classList.add("settings");var f=this;(function(){b.content.innerHTML="";b.addHTML(""+a.type+""+ -(a.constructor.desc||"")+"");b.addHTML("

Properties

");for(var d in a.properties){var c=a.properties[d],e=a.getPropertyInfo(d);a.onAddPropertyToPanel&&a.onAddPropertyToPanel(d,b)||b.addWidget(e.widget||e.type,d,c,e,function(b,d){f.graph.beforeChange(a);a.setProperty(b,d);f.graph.afterChange();f.dirty_canvas=!0})}b.addSeparator();if(a.onShowCustomPanelInfo)a.onShowCustomPanelInfo(b);b.addButton("Delete",function(){a.block_delete||(a.graph.remove(a),b.close())}).classList.add("delete")})(); -this.canvas.parentNode.appendChild(b)};h.prototype.showSubgraphPropertiesDialog=function(a){function b(){f.clear();if(a.inputs)for(var d=0;d","subgraph_property");e.dataset.name=c.name;e.dataset.slot=d;e.querySelector(".name").innerText=c.name;e.querySelector(".type").innerText=c.type;e.querySelector("button").addEventListener("click", -function(d){a.removeInput(Number(this.parentNode.dataset.slot));b()})}}}console.log("showing subgraph properties dialog");var d=this.canvas.parentNode.querySelector(".subgraph_dialog");d&&d.close();var f=this.createPanel("Subgraph Inputs",{closable:!0,width:500});f.node=a;f.classList.add("subgraph_dialog");f.addHTML(" + NameType","subgraph_property extra",!0).querySelector("button").addEventListener("click", -function(d){d=this.parentNode;var f=d.querySelector(".name").value,c=d.querySelector(".type").value;f&&-1==a.findInputSlot(f)&&(a.addInput(f,c),d.querySelector(".name").value="",d.querySelector(".type").value="",b())});b();this.canvas.parentNode.appendChild(f);return f};h.prototype.checkPanels=function(){if(this.canvas)for(var a=this.canvas.parentNode.querySelectorAll(".litegraph.dialog"),b=0;bNo color"});for(var c in h.node_colors)a=h.node_colors[c],a={value:c,content:""+c+""},b.push(a);new e.ContextMenu(b,{event:d,callback:function(a){g&&((a=a.value?h.node_colors[a.value]:null)?g.constructor===e.LGraphGroup?g.color=a.groupcolor:(g.color=a.color,g.bgcolor=a.bgcolor): -(delete g.color,delete g.bgcolor),g.setDirtyCanvas(!0,!0))},parentMenu:f,node:g});return!1};h.onMenuNodeShapes=function(a,b,d,f,g){if(!g)throw"no node passed";new e.ContextMenu(e.VALID_SHAPES,{event:d,callback:function(a){g&&(g.graph.beforeChange(g),g.shape=a,g.graph.afterChange(g),g.setDirtyCanvas(!0))},parentMenu:f,node:g});return!1};h.onMenuNodeRemove=function(a,b,d,f,g){if(!g)throw"no node passed";!1!==g.removable&&(a=g.graph,a.beforeChange(),a.remove(g),a.afterChange(),g.setDirtyCanvas(!0,!0))}; -h.onMenuNodeToSubgraph=function(a,b,d,f,g){a=g.graph;if(b=h.active_canvas)d=Object.values(b.selected_nodes||{}),d.length||(d=[g]),f=e.createNode("graph/subgraph"),f.pos=g.pos.concat(),a.add(f),f.buildFromNodes(d),b.deselectAllNodes(),g.setDirtyCanvas(!0,!0)};h.onMenuNodeClone=function(a,b,d,f,g){!1!=g.clonable&&(a=g.clone())&&(a.pos=[g.pos[0]+5,g.pos[1]+5],g.graph.beforeChange(),g.graph.add(a),g.graph.afterChange(),g.setDirtyCanvas(!0,!0))};h.node_colors={red:{color:"#322",bgcolor:"#533",groupcolor:"#A88"}, -brown:{color:"#332922",bgcolor:"#593930",groupcolor:"#b06634"},green:{color:"#232",bgcolor:"#353",groupcolor:"#8A8"},blue:{color:"#223",bgcolor:"#335",groupcolor:"#88A"},pale_blue:{color:"#2a363b",bgcolor:"#3f5159",groupcolor:"#3f789e"},cyan:{color:"#233",bgcolor:"#355",groupcolor:"#8AA"},purple:{color:"#323",bgcolor:"#535",groupcolor:"#a1309b"},yellow:{color:"#432",bgcolor:"#653",groupcolor:"#b58b2a"},black:{color:"#222",bgcolor:"#000",groupcolor:"#444"}};h.prototype.getCanvasMenuOptions=function(){var a= -null;this.getMenuOptions?a=this.getMenuOptions():(a=[{content:"Add Node",has_submenu:!0,callback:h.onMenuAdd},{content:"Add Group",callback:h.onGroupAdd}],this._graph_stack&&0Name",g),l=e.querySelector("input");l&&c&&(l.value=c.label||"");e.querySelector("button").addEventListener("click",function(a){l.value&& -(c&&(c.label=l.value),d.setDirty(!0));e.close()})}},extra:a};a&&(c.title=a.type);var l=null;a&&(l=a.getSlotInPosition(b.canvasX,b.canvasY),h.active_node=a);if(l){g=[];if(a.getSlotMenuOptions)g=a.getSlotMenuOptions(l);else{l&&l.output&&l.output.links&&l.output.links.length&&g.push({content:"Disconnect Links",slot:l});var p=l.input||l.output;g.push(p.locked?"Cannot remove":{content:"Remove Slot",slot:l});g.push(p.nameLocked?"Cannot rename":{content:"Rename Slot",slot:l})}c.title=(l.input?l.input.type: -l.output.type)||"*";l.input&&l.input.type==e.ACTION&&(c.title="Action");l.output&&l.output.type==e.EVENT&&(c.title="Event")}else a?g=this.getNodeMenuOptions(a):(g=this.getCanvasMenuOptions(),(l=this.graph.getGroupOnPos(b.canvasX,b.canvasY))&&g.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:l,options:this.getGroupMenuOptions(l)}}));g&&new e.ContextMenu(g,c,f)};"undefined"!=typeof window&&window.CanvasRenderingContext2D&&(window.CanvasRenderingContext2D.prototype.roundRect= -function(a,b,d,f,g,c){void 0===g&&(g=5);void 0===c&&(c=g);this.moveTo(a+g,b);this.lineTo(a+d-g,b);this.quadraticCurveTo(a+d,b,a+d,b+g);this.lineTo(a+d,b+f-c);this.quadraticCurveTo(a+d,b+f,a+d-c,b+f);this.lineTo(a+c,b+f);this.quadraticCurveTo(a,b+f,a,b+f-c);this.lineTo(a,b+g);this.quadraticCurveTo(a,b,a+g,b)});e.compareObjects=function(a,b){for(var d in a)if(a[d]!=b[d])return!1;return!0};e.distance=D;e.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+ -","+Math.round(255*a[2]).toFixed()+","+(4==a.length?a[3].toFixed(2):"1.0")+")"};e.isInsideRectangle=x;e.growBounding=function(a,b,d){ba[2]&&(a[2]=b);da[3]&&(a[3]=d)};e.isInsideBounding=function(a,b){return a[0]b[1][0]||a[1]>b[1][1]?!1:!0};e.overlapBounding=B;e.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),d=0,f,g,c=0;6>c;c+=2)f="0123456789ABCDEF".indexOf(a.charAt(c)),g="0123456789ABCDEF".indexOf(a.charAt(c+ -1)),b[d]=16*f+g,d++;return b};e.num2hex=function(a){for(var b="#",d,f,g=0;3>g;g++)d=a[g]/16,f=a[g]%16,b+="0123456789ABCDEF".charAt(d)+"0123456789ABCDEF".charAt(f);return b};G.prototype.addItem=function(a,b,d){function f(a){var b=this.value;b&&b.has_submenu&&g.call(this,a)}function g(a){var b=this.value,g=!0;c.current_submenu&&c.current_submenu.close(a);if(d.callback){var f=d.callback.call(this,b,d,a,c,d.node);!0===f&&(g=!1)}if(b&&(b.callback&&!d.ignore_item_callbacks&&!0!==b.disabled&&(f=b.callback.call(this, -b,d,a,c,d.extra),!0===f&&(g=!1)),b.submenu)){if(!b.submenu.options)throw"ContextMenu submenu needs options";new c.constructor(b.submenu.options,{callback:b.submenu.callback,event:a,parentMenu:c,ignore_item_callbacks:b.submenu.ignore_item_callbacks,title:b.submenu.title,extra:b.submenu.extra,autoopen:d.autoopen});g=!1}g&&!c.lock&&c.close()}var c=this;d=d||{};var e=document.createElement("div");e.className="litemenu-entry submenu";var l=!1;if(null===b)e.classList.add("separator");else{e.innerHTML=b&& -b.title?b.title:a;if(e.value=b)b.disabled&&(l=!0,e.classList.add("disabled")),(b.submenu||b.has_submenu)&&e.classList.add("has_submenu");"function"==typeof b?(e.dataset.value=a,e.onclick_callback=b):e.dataset.value=b;b.className&&(e.className+=" "+b.className)}this.root.appendChild(e);l||e.addEventListener("click",g);d.autoopen&&e.addEventListener("mouseenter",f);return e};G.prototype.close=function(a,b){this.root.parentNode&&this.root.parentNode.removeChild(this.root);this.parentMenu&&!b&&(this.parentMenu.lock= -!1,this.parentMenu.current_submenu=null,void 0===a?this.parentMenu.close():a&&!G.isCursorOverElement(a,this.parentMenu.root)&&G.trigger(this.parentMenu.root,"mouseleave",a));this.current_submenu&&this.current_submenu.close(a,!0);this.root.closing_timer&&clearTimeout(this.root.closing_timer)};G.trigger=function(a,b,d,f){var g=document.createEvent("CustomEvent");g.initCustomEvent(b,!0,!0,d);g.srcElement=f;a.dispatchEvent?a.dispatchEvent(g):a.__events&&a.__events.dispatchEvent(g);return g};G.prototype.getTopMenu= -function(){return this.options.parentMenu?this.options.parentMenu.getTopMenu():this};G.prototype.getFirstEvent=function(){return this.options.parentMenu?this.options.parentMenu.getFirstEvent():this.options.event};G.isCursorOverElement=function(a,b){var d=a.clientX,f=a.clientY,g=b.getBoundingClientRect();return g?f>g.top&&fg.left&&dMath.abs(d))return f[1];d=(a-f[0])/d;return f[1]*(1-d)+g[1]*d}}return 0}};E.prototype.draw=function(a,b,d,f,g,c){if(d=this.points){this.size=b;var e=b[0]-2*this.margin;b=b[1]-2*this.margin;g=g||"#666";a.save();a.translate(this.margin,this.margin);f&&(a.fillStyle="#111",a.fillRect(0,0,e,b),a.fillStyle="#222",a.fillRect(0.5*e,0,1,b),a.strokeStyle="#333",a.strokeRect(0,0,e,b));a.strokeStyle= -g;c&&(a.globalAlpha=0.5);a.beginPath();for(f=0;fa[1])){var f=this.size[0]-2*this.margin,g=this.size[1]-2*this.margin,c=a[0]-this.margin,e=a[1]-this.margin;this.selected=this.getCloserPoint([c, -e],30/b.ds.scale);-1==this.selected&&(f=[c/f,1-e/g],d.push(f),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(f),this.must_update=!0);if(-1!=this.selected)return!0}};E.prototype.onMouseMove=function(a,b){var d=this.points;if(d){var f=this.selected;if(!(0>f)){var g=(a[0]-this.margin)/(this.size[0]-2*this.margin),c=(a[1]-this.margin)/(this.size[1]-2*this.margin);this._nearest=this.getCloserPoint([a[0]-this.margin,a[1]-this.margin],30/b.ds.scale);var e=d[f];if(e){var l=0==f||f==d.length- -1;!l&&(-10>a[0]||a[0]>this.size[0]+10||-10>a[1]||a[1]>this.size[1]+10)?(d.splice(f,1),this.selected=-1):(e[0]=l?0==f?0:1:Math.clamp(g,0,1),e[1]=1-Math.clamp(c,0,1),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(e),this.must_update=!0)}}}};E.prototype.onMouseUp=function(a,b){this.selected=-1;return!1};E.prototype.getCloserPoint=function(a,b){var d=this.points;if(!d)return-1;b=b||30;for(var f=this.size[0]-2*this.margin,g=this.size[1]-2*this.margin,c=d.length,e=[0,0],l=1E6,r=-1,p=0;p< -c;++p){var h=d[p];e[0]=h[0]*f;e[1]=(1-h[1])*g;h=vec2.distance(a,e);h>l||h>b||(r=p,l=h)}return r};e.CurveEditor=E;e.getParameterNames=function(a){return(a+"").replace(/[/][/].*$/gm,"").replace(/\s+/g,"").replace(/[/][*][^/*]*[*][/]/g,"").split("){",1)[0].replace(/^[^(]*[(]/,"").replace(/=[^,]+/g,"").split(",").filter(Boolean)};Math.clamp=function(a,b,d){return b>a?b:d";h.querySelector(".property_name").innerText=b;var r=h.querySelector(".property_value");r.innerText=q;h.dataset.property=b;h.dataset.type= +c.type||a;h.options=c;h.value=g;if("boolean"==a)h.classList.add("boolean"),g&&h.classList.add("bool-on"),h.addEventListener("click",function(){var a=this.dataset.property;this.value=!this.value;this.classList.toggle("bool-on");this.querySelector(".property_value").innerText=this.value?"true":"false";m(a,this.value)});else if("string"==a||"number"==a)r.setAttribute("contenteditable",!0),r.addEventListener("keydown",function(a){"Enter"==a.code&&(a.preventDefault(),this.blur())}),r.addEventListener("blur", +function(){var a=this.innerText,b=this.parentNode.dataset.property;"number"==this.parentNode.dataset.type&&(a=Number(a));m(b,a)});else if("enum"==a||"combo"==a)q=k.getPropertyPrintableValue(g,c.values);r.innerText=q;r.addEventListener("click",function(a){var b=this.parentNode.dataset.property,g=this;new e.ContextMenu(c.values||[],{event:a,className:"dark",callback:function(a,d,f){g.innerText=a;m(b,a);return!1}},d)});f.content.appendChild(h);return h};return f};k.getPropertyPrintableValue=function(a, +b){if(!b||b.constructor===Array)return String(a);if(b.constructor===Object){var d="",f;for(f in b)if(b[f]==a){d=f;break}return String(a)+" ("+d+")"}};k.prototype.showShowNodePanel=function(a){window.SELECTED_NODE=a;var b=document.querySelector("#node-panel");b&&b.close();var d=this.getCanvasWindow(),b=this.createPanel(a.title||"",{closable:!0,window:d});b.id="node-panel";b.node=a;b.classList.add("settings");var f=this;(function(){b.content.innerHTML="";b.addHTML(""+a.type+ +""+(a.constructor.desc||"")+"");b.addHTML("

Properties

");for(var d in a.properties){var c=a.properties[d],e=a.getPropertyInfo(d);a.onAddPropertyToPanel&&a.onAddPropertyToPanel(d,b)||b.addWidget(e.widget||e.type,d,c,e,function(b,d){f.graph.beforeChange(a);a.setProperty(b,d);f.graph.afterChange();f.dirty_canvas=!0})}b.addSeparator();if(a.onShowCustomPanelInfo)a.onShowCustomPanelInfo(b);b.addButton("Delete",function(){a.block_delete|| +(a.graph.remove(a),b.close())}).classList.add("delete")})();this.canvas.parentNode.appendChild(b)};k.prototype.showSubgraphPropertiesDialog=function(a){function b(){f.clear();if(a.inputs)for(var d=0;d","subgraph_property");e.dataset.name=c.name;e.dataset.slot=d;e.querySelector(".name").innerText=c.name;e.querySelector(".type").innerText= +c.type;e.querySelector("button").addEventListener("click",function(d){a.removeInput(Number(this.parentNode.dataset.slot));b()})}}}console.log("showing subgraph properties dialog");var d=this.canvas.parentNode.querySelector(".subgraph_dialog");d&&d.close();var f=this.createPanel("Subgraph Inputs",{closable:!0,width:500});f.node=a;f.classList.add("subgraph_dialog");f.addHTML(" + NameType", +"subgraph_property extra",!0).querySelector("button").addEventListener("click",function(d){d=this.parentNode;var f=d.querySelector(".name").value,c=d.querySelector(".type").value;f&&-1==a.findInputSlot(f)&&(a.addInput(f,c),d.querySelector(".name").value="",d.querySelector(".type").value="",b())});b();this.canvas.parentNode.appendChild(f);return f};k.prototype.checkPanels=function(){if(this.canvas)for(var a=this.canvas.parentNode.querySelectorAll(".litegraph.dialog"),b=0;bNo color"});for(var c in k.node_colors)a=k.node_colors[c],a={value:c,content:""+c+""},b.push(a);new e.ContextMenu(b,{event:d,callback:function(a){g&&((a=a.value?k.node_colors[a.value]:null)? +g.constructor===e.LGraphGroup?g.color=a.groupcolor:(g.color=a.color,g.bgcolor=a.bgcolor):(delete g.color,delete g.bgcolor),g.setDirtyCanvas(!0,!0))},parentMenu:f,node:g});return!1};k.onMenuNodeShapes=function(a,b,d,f,g){if(!g)throw"no node passed";new e.ContextMenu(e.VALID_SHAPES,{event:d,callback:function(a){g&&(g.graph.beforeChange(g),g.shape=a,g.graph.afterChange(g),g.setDirtyCanvas(!0))},parentMenu:f,node:g});return!1};k.onMenuNodeRemove=function(a,b,d,f,g){if(!g)throw"no node passed";!1!==g.removable&& +(a=g.graph,a.beforeChange(),a.remove(g),a.afterChange(),g.setDirtyCanvas(!0,!0))};k.onMenuNodeToSubgraph=function(a,b,d,f,g){a=g.graph;if(b=k.active_canvas)d=Object.values(b.selected_nodes||{}),d.length||(d=[g]),f=e.createNode("graph/subgraph"),f.pos=g.pos.concat(),a.add(f),f.buildFromNodes(d),b.deselectAllNodes(),g.setDirtyCanvas(!0,!0)};k.onMenuNodeClone=function(a,b,d,f,g){!1!=g.clonable&&(a=g.clone())&&(a.pos=[g.pos[0]+5,g.pos[1]+5],g.graph.beforeChange(),g.graph.add(a),g.graph.afterChange(), +g.setDirtyCanvas(!0,!0))};k.node_colors={red:{color:"#322",bgcolor:"#533",groupcolor:"#A88"},brown:{color:"#332922",bgcolor:"#593930",groupcolor:"#b06634"},green:{color:"#232",bgcolor:"#353",groupcolor:"#8A8"},blue:{color:"#223",bgcolor:"#335",groupcolor:"#88A"},pale_blue:{color:"#2a363b",bgcolor:"#3f5159",groupcolor:"#3f789e"},cyan:{color:"#233",bgcolor:"#355",groupcolor:"#8AA"},purple:{color:"#323",bgcolor:"#535",groupcolor:"#a1309b"},yellow:{color:"#432",bgcolor:"#653",groupcolor:"#b58b2a"},black:{color:"#222", +bgcolor:"#000",groupcolor:"#444"}};k.prototype.getCanvasMenuOptions=function(){var a=null;this.getMenuOptions?a=this.getMenuOptions():(a=[{content:"Add Node",has_submenu:!0,callback:k.onMenuAdd},{content:"Add Group",callback:k.onGroupAdd}],this._graph_stack&&0Name", +g),l=e.querySelector("input");l&&c&&(l.value=c.label||"");e.querySelector("button").addEventListener("click",function(a){l.value&&(c&&(c.label=l.value),d.setDirty(!0));e.close()})}},extra:a};a&&(c.title=a.type);var l=null;a&&(l=a.getSlotInPosition(b.canvasX,b.canvasY),k.active_node=a);if(l){g=[];if(a.getSlotMenuOptions)g=a.getSlotMenuOptions(l);else{l&&l.output&&l.output.links&&l.output.links.length&&g.push({content:"Disconnect Links",slot:l});var m=l.input||l.output;g.push(m.locked?"Cannot remove": +{content:"Remove Slot",slot:l});g.push(m.nameLocked?"Cannot rename":{content:"Rename Slot",slot:l})}c.title=(l.input?l.input.type:l.output.type)||"*";l.input&&l.input.type==e.ACTION&&(c.title="Action");l.output&&l.output.type==e.EVENT&&(c.title="Event")}else a?g=this.getNodeMenuOptions(a):(g=this.getCanvasMenuOptions(),(l=this.graph.getGroupOnPos(b.canvasX,b.canvasY))&&g.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:l,options:this.getGroupMenuOptions(l)}}));g&&new e.ContextMenu(g, +c,f)};"undefined"!=typeof window&&window.CanvasRenderingContext2D&&(window.CanvasRenderingContext2D.prototype.roundRect=function(a,b,d,f,g,c){void 0===g&&(g=5);void 0===c&&(c=g);this.moveTo(a+g,b);this.lineTo(a+d-g,b);this.quadraticCurveTo(a+d,b,a+d,b+g);this.lineTo(a+d,b+f-c);this.quadraticCurveTo(a+d,b+f,a+d-c,b+f);this.lineTo(a+c,b+f);this.quadraticCurveTo(a,b+f,a,b+f-c);this.lineTo(a,b+g);this.quadraticCurveTo(a,b,a+g,b)});e.compareObjects=function(a,b){for(var d in a)if(a[d]!=b[d])return!1;return!0}; +e.distance=B;e.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+","+Math.round(255*a[2]).toFixed()+","+(4==a.length?a[3].toFixed(2):"1.0")+")"};e.isInsideRectangle=v;e.growBounding=function(a,b,d){ba[2]&&(a[2]=b);da[3]&&(a[3]=d)};e.isInsideBounding=function(a,b){return a[0]b[1][0]||a[1]>b[1][1]?!1:!0};e.overlapBounding=F;e.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase(); +for(var b=Array(3),d=0,f,g,c=0;6>c;c+=2)f="0123456789ABCDEF".indexOf(a.charAt(c)),g="0123456789ABCDEF".indexOf(a.charAt(c+1)),b[d]=16*f+g,d++;return b};e.num2hex=function(a){for(var b="#",d,f,g=0;3>g;g++)d=a[g]/16,f=a[g]%16,b+="0123456789ABCDEF".charAt(d)+"0123456789ABCDEF".charAt(f);return b};x.prototype.addItem=function(a,b,d){function f(a){var b=this.value;b&&b.has_submenu&&g.call(this,a)}function g(a){var b=this.value,g=!0;c.current_submenu&&c.current_submenu.close(a);if(d.callback){var f=d.callback.call(this, +b,d,a,c,d.node);!0===f&&(g=!1)}if(b&&(b.callback&&!d.ignore_item_callbacks&&!0!==b.disabled&&(f=b.callback.call(this,b,d,a,c,d.extra),!0===f&&(g=!1)),b.submenu)){if(!b.submenu.options)throw"ContextMenu submenu needs options";new c.constructor(b.submenu.options,{callback:b.submenu.callback,event:a,parentMenu:c,ignore_item_callbacks:b.submenu.ignore_item_callbacks,title:b.submenu.title,extra:b.submenu.extra,autoopen:d.autoopen});g=!1}g&&!c.lock&&c.close()}var c=this;d=d||{};var e=document.createElement("div"); +e.className="litemenu-entry submenu";var l=!1;if(null===b)e.classList.add("separator");else{e.innerHTML=b&&b.title?b.title:a;if(e.value=b)b.disabled&&(l=!0,e.classList.add("disabled")),(b.submenu||b.has_submenu)&&e.classList.add("has_submenu");"function"==typeof b?(e.dataset.value=a,e.onclick_callback=b):e.dataset.value=b;b.className&&(e.className+=" "+b.className)}this.root.appendChild(e);l||e.addEventListener("click",g);d.autoopen&&e.addEventListener("mouseenter",f);return e};x.prototype.close= +function(a,b){this.root.parentNode&&this.root.parentNode.removeChild(this.root);this.parentMenu&&!b&&(this.parentMenu.lock=!1,this.parentMenu.current_submenu=null,void 0===a?this.parentMenu.close():a&&!x.isCursorOverElement(a,this.parentMenu.root)&&x.trigger(this.parentMenu.root,"mouseleave",a));this.current_submenu&&this.current_submenu.close(a,!0);this.root.closing_timer&&clearTimeout(this.root.closing_timer)};x.trigger=function(a,b,d,f){var g=document.createEvent("CustomEvent");g.initCustomEvent(b, +!0,!0,d);g.srcElement=f;a.dispatchEvent?a.dispatchEvent(g):a.__events&&a.__events.dispatchEvent(g);return g};x.prototype.getTopMenu=function(){return this.options.parentMenu?this.options.parentMenu.getTopMenu():this};x.prototype.getFirstEvent=function(){return this.options.parentMenu?this.options.parentMenu.getFirstEvent():this.options.event};x.isCursorOverElement=function(a,b){var d=a.clientX,f=a.clientY,g=b.getBoundingClientRect();return g?f>g.top&&fg.left&&dMath.abs(d))return f[1];d=(a-f[0])/d;return f[1]*(1-d)+g[1]*d}}return 0}};C.prototype.draw=function(a,b,d,f,g,c){if(d=this.points){this.size=b;var e=b[0]-2*this.margin;b=b[1]-2*this.margin;g=g||"#666";a.save();a.translate(this.margin, +this.margin);f&&(a.fillStyle="#111",a.fillRect(0,0,e,b),a.fillStyle="#222",a.fillRect(0.5*e,0,1,b),a.strokeStyle="#333",a.strokeRect(0,0,e,b));a.strokeStyle=g;c&&(a.globalAlpha=0.5);a.beginPath();for(f=0;fa[1])){var f=this.size[0]-2*this.margin,g=this.size[1]-2*this.margin,c=a[0]-this.margin,e=a[1]-this.margin;this.selected=this.getCloserPoint([c,e],30/b.ds.scale);-1==this.selected&&(f=[c/f,1-e/g],d.push(f),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(f),this.must_update=!0);if(-1!=this.selected)return!0}};C.prototype.onMouseMove=function(a,b){var d=this.points;if(d){var f=this.selected;if(!(0>f)){var g=(a[0]-this.margin)/(this.size[0]-2*this.margin),c=(a[1]- +this.margin)/(this.size[1]-2*this.margin);this._nearest=this.getCloserPoint([a[0]-this.margin,a[1]-this.margin],30/b.ds.scale);var e=d[f];if(e){var l=0==f||f==d.length-1;!l&&(-10>a[0]||a[0]>this.size[0]+10||-10>a[1]||a[1]>this.size[1]+10)?(d.splice(f,1),this.selected=-1):(e[0]=l?0==f?0:1:Math.clamp(g,0,1),e[1]=1-Math.clamp(c,0,1),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(e),this.must_update=!0)}}}};C.prototype.onMouseUp=function(a,b){this.selected=-1;return!1};C.prototype.getCloserPoint= +function(a,b){var d=this.points;if(!d)return-1;b=b||30;for(var f=this.size[0]-2*this.margin,g=this.size[1]-2*this.margin,c=d.length,e=[0,0],l=1E6,m=-1,q=0;ql||h>b||(m=q,l=h)}return m};e.CurveEditor=C;e.getParameterNames=function(a){return(a+"").replace(/[/][/].*$/gm,"").replace(/\s+/g,"").replace(/[/][*][^/*]*[*][/]/g,"").split("){",1)[0].replace(/^[^(]*[(]/,"").replace(/=[^,]+/g,"").split(",").filter(Boolean)};Math.clamp=function(a, +b,d){return b>a?b:dthis.size[1]-f.NODE_TITLE_HEIGHT+0.5&&d.showSubgraphPropertiesDialog(this)};m.prototype.computeSize=function(){return[200,Math.max(this.inputs?this.inputs.length:0,this.outputs?this.outputs.length:0)*f.NODE_SLOT_HEIGHT+f.NODE_TITLE_HEIGHT]};m.prototype.onSubgraphTrigger=function(a,b){var d=this.findOutputSlot(a);-1!=d&&this.triggerSlot(d)};m.prototype.onSubgraphNewInput= -function(a,b){-1==this.findInputSlot(a)&&this.addInput(a,b)};m.prototype.onSubgraphRenamedInput=function(a,b){var d=this.findInputSlot(a);-1!=d&&(this.getInputInfo(d).name=b)};m.prototype.onSubgraphTypeChangeInput=function(a,b){var d=this.findInputSlot(a);-1!=d&&(this.getInputInfo(d).type=b)};m.prototype.onSubgraphRemovedInput=function(a){a=this.findInputSlot(a);-1!=a&&this.removeInput(a)};m.prototype.onSubgraphNewOutput=function(a,b){-1==this.findOutputSlot(a)&&this.addOutput(a,b)};m.prototype.onSubgraphRenamedOutput= -function(a,b){var d=this.findOutputSlot(a);-1!=d&&(this.getOutputInfo(d).name=b)};m.prototype.onSubgraphTypeChangeOutput=function(a,b){var d=this.findOutputSlot(a);-1!=d&&(this.getOutputInfo(d).type=b)};m.prototype.onSubgraphRemovedOutput=function(a){a=this.findInputSlot(a);-1!=a&&this.removeOutput(a)};m.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:"Open",callback:function(){a.openSubgraph(b.subgraph)}}]};m.prototype.onResize=function(a){a[1]+=20};m.prototype.serialize=function(){var a= -f.LGraphNode.prototype.serialize.call(this);a.subgraph=this.subgraph.serialize();return a};m.prototype.clone=function(){var a=f.createNode(this.type),b=this.serialize();delete b.id;delete b.inputs;delete b.outputs;a.configure(b);return a};m.prototype.buildFromNodes=function(a){for(var b={},d=0,f=0;fthis.size[1]-f.NODE_TITLE_HEIGHT+0.5&&d.showSubgraphPropertiesDialog(this)};n.prototype.computeSize=function(){return[200,Math.max(this.inputs?this.inputs.length:0,this.outputs?this.outputs.length:0)*f.NODE_SLOT_HEIGHT+f.NODE_TITLE_HEIGHT]};n.prototype.onSubgraphTrigger=function(a,b){var d=this.findOutputSlot(a);-1!=d&&this.triggerSlot(d)};n.prototype.onSubgraphNewInput= +function(a,b){-1==this.findInputSlot(a)&&this.addInput(a,b)};n.prototype.onSubgraphRenamedInput=function(a,b){var d=this.findInputSlot(a);-1!=d&&(this.getInputInfo(d).name=b)};n.prototype.onSubgraphTypeChangeInput=function(a,b){var d=this.findInputSlot(a);-1!=d&&(this.getInputInfo(d).type=b)};n.prototype.onSubgraphRemovedInput=function(a){a=this.findInputSlot(a);-1!=a&&this.removeInput(a)};n.prototype.onSubgraphNewOutput=function(a,b){-1==this.findOutputSlot(a)&&this.addOutput(a,b)};n.prototype.onSubgraphRenamedOutput= +function(a,b){var d=this.findOutputSlot(a);-1!=d&&(this.getOutputInfo(d).name=b)};n.prototype.onSubgraphTypeChangeOutput=function(a,b){var d=this.findOutputSlot(a);-1!=d&&(this.getOutputInfo(d).type=b)};n.prototype.onSubgraphRemovedOutput=function(a){a=this.findInputSlot(a);-1!=a&&this.removeOutput(a)};n.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:"Open",callback:function(){a.openSubgraph(b.subgraph)}}]};n.prototype.onResize=function(a){a[1]+=20};n.prototype.serialize=function(){var a= +f.LGraphNode.prototype.serialize.call(this);a.subgraph=this.subgraph.serialize();return a};n.prototype.clone=function(){var a=f.createNode(this.type),b=this.serialize();delete b.id;delete b.inputs;delete b.outputs;a.configure(b);return a};n.prototype.buildFromNodes=function(a){for(var b={},d=0,f=0;f=e?this.trigger(null,h):this._pending.push([e,h])};h.prototype.onExecute=function(){var c=1E3* -this.graph.elapsed_time;this.isInputConnected(1)&&(this.properties.time_in_ms=this.getInputData(1));for(var h=0;hh[1]))return this.old_y=c.canvasY,this.captureInput(!0),this.mouse_captured=!0};n.prototype.onMouseMove=function(c){if(this.mouse_captured){var h=this.old_y-c.canvasY;c.shiftKey&&(h*=10);if(c.metaKey||c.altKey)h*=0.1;this.old_y=c.canvasY;c=this._remainder+h/n.pixels_threshold;this._remainder=c%1;c=Math.clamp(this.properties.value+ -(c|0)*this.properties.step,this.properties.min,this.properties.max);this.properties.value=c;this.graph._version++;this.setDirtyCanvas(!0)}};n.prototype.onMouseUp=function(c,h){200>c.click_time&&(this.properties.value=Math.clamp(this.properties.value+(h[1]>0.5*this.size[1]?-1:1)*this.properties.step,this.properties.min,this.properties.max),this.graph._version++,this.setDirtyCanvas(!0));this.mouse_captured&&(this.mouse_captured=!1,this.captureInput(!1))};E.registerNodeType("widget/number",n);k.title= -"Combo";k.desc="Widget to select from a list";k.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};k.prototype.onPropertyChanged=function(c,h){"values"==c?(this._values=h.split(";"),this.widget.options.values=this._values):"value"==c&&(this.widget.value=h)};E.registerNodeType("widget/combo",k);y.title="Knob";y.desc="Circular controller";y.size=[80,100];y.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/ -(this.properties.max-this.properties.min));var h=0.5*this.size[0],k=0.5*this.size[1],n=0.5*Math.min(this.size[0],this.size[1])-5;c.globalAlpha=1;c.save();c.translate(h,k);c.rotate(0.75*Math.PI);c.fillStyle="rgba(0,0,0,0.5)";c.beginPath();c.moveTo(0,0);c.arc(0,0,n,0,1.5*Math.PI);c.fill();c.strokeStyle="black";c.fillStyle=this.properties.color;c.lineWidth=2;c.beginPath();c.moveTo(0,0);c.arc(0,0,n-4,0,1.5*Math.PI*Math.max(0.01,this.value));c.closePath();c.fill();c.lineWidth=1;c.globalAlpha=1;c.restore(); -c.fillStyle="black";c.beginPath();c.arc(h,k,0.75*n,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":this.properties.color;c.beginPath();var B=this.value*Math.PI*1.5+0.75*Math.PI;c.arc(h+Math.cos(B)*n*0.65,k+Math.sin(B)*n*0.65,0.05*n,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":"#AAA";c.font=Math.floor(0.5*n)+"px Arial";c.textAlign="center";c.fillText(this.properties.value.toFixed(this.properties.precision),h,k+0.15*n)}};y.prototype.onExecute=function(){this.setOutputData(0, -this.properties.value);this.boxcolor=E.colorToString([this.value,this.value,this.value])};y.prototype.onMouseDown=function(c){this.center=[0.5*this.size[0],0.5*this.size[1]+20];this.radius=0.5*this.size[0];if(20>c.canvasY-this.pos[1]||E.distance([c.canvasX,c.canvasY],[this.pos[0]+this.center[0],this.pos[1]+this.center[1]])>this.radius)return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];this.captureInput(!0);return!0};y.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX- -this.pos[0],c.canvasY-this.pos[1]];var h=this.value,h=h-0.01*(c[1]-this.oldmouse[1]);1h&&(h=0);this.value=h;this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.oldmouse=c;this.setDirtyCanvas(!0)}};y.prototype.onMouseUp=function(c){this.oldmouse&&(this.oldmouse=null,this.captureInput(!1))};y.prototype.onPropertyChanged=function(c,h){if("min"==c||"max"==c||"value"==c)return this.properties[c]=parseFloat(h),!0};E.registerNodeType("widget/knob", -y);h.title="Inner Slider";h.prototype.onPropertyChanged=function(c,h){"value"==c&&(this.slider.value=h)};h.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};E.registerNodeType("widget/internal_slider",h);D.title="H.Slider";D.desc="Linear slider controller";D.prototype.onDrawForeground=function(c){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min));c.globalAlpha=1;c.lineWidth=1;c.fillStyle="#000";c.fillRect(2, -2,this.size[0]-4,this.size[1]-4);c.fillStyle=this.properties.color;c.beginPath();c.rect(4,4,(this.size[0]-8)*this.value,this.size[1]-8);c.fill()};D.prototype.onExecute=function(){this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.setOutputData(0,this.properties.value);this.boxcolor=E.colorToString([this.value,this.value,this.value])};D.prototype.onMouseDown=function(c){if(0>c.canvasY-this.pos[1])return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY- -this.pos[1]];this.captureInput(!0);return!0};D.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];var h=this.value,h=h+(c[0]-this.oldmouse[0])/this.size[0];1h&&(h=0);this.value=h;this.oldmouse=c;this.setDirtyCanvas(!0)}};D.prototype.onMouseUp=function(c){this.oldmouse=null;this.captureInput(!1)};D.prototype.onMouseLeave=function(c){};E.registerNodeType("widget/hslider",D);x.title="Progress";x.desc="Shows data in linear progress";x.prototype.onExecute= -function(){var c=this.getInputData(0);void 0!=c&&(this.properties.value=c)};x.prototype.onDrawForeground=function(c){c.lineWidth=1;c.fillStyle=this.properties.color;var h=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min),h=Math.min(1,h),h=Math.max(0,h);c.fillRect(2,2,(this.size[0]-4)*h,this.size[1]-4)};E.registerNodeType("widget/progress",x);B.title="Text";B.desc="Shows the input value";B.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"led_text", -text:"LED",type:"minibutton"},{name:"normal_text",text:"Normal",type:"minibutton"}];B.prototype.onDrawForeground=function(c){c.fillStyle=this.properties.color;var h=this.properties.value;this.properties.glowSize?(c.shadowColor=this.properties.color,c.shadowOffsetX=0,c.shadowOffsetY=0,c.shadowBlur=this.properties.glowSize):c.shadowColor="transparent";var k=this.properties.fontsize;c.textAlign=this.properties.align;c.font=k.toString()+"px "+this.properties.font;this.str="number"==typeof h?h.toFixed(this.properties.decimals): -h;if("string"==typeof this.str){var h=this.str.split("\\n"),n;for(n in h)c.fillText(h[n],"left"==this.properties.align?15:this.size[0]-15,-0.15*k+k*(parseInt(n)+1))}c.shadowColor="transparent";this.last_ctx=c;c.textAlign="left"};B.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(this.properties.value=c)};B.prototype.resize=function(){if(this.last_ctx){var c=this.str.split("\\n");this.last_ctx.font=this.properties.fontsize+"px "+this.properties.font;var h=0,k;for(k in c){var n=this.last_ctx.measureText(c[k]).width; -hk?n.xbox.axes.lx:0,this._left_axis[1]=Math.abs(n.xbox.axes.ly)>k?n.xbox.axes.ly:0,this._right_axis[0]=Math.abs(n.xbox.axes.rx)>k?n.xbox.axes.rx:0,this._right_axis[1]=Math.abs(n.xbox.axes.ry)>k?n.xbox.axes.ry:0,this._triggers[0]=Math.abs(n.xbox.axes.ltrigger)>k?n.xbox.axes.ltrigger: -0,this._triggers[1]=Math.abs(n.xbox.axes.rtrigger)>k?n.xbox.axes.rtrigger:0);if(this.outputs)for(k=0;kn;n++)if(k[n]){n=k[n];k=this.xbox_mapping;k||(k=this.xbox_mapping={axes:[], -buttons:{},hat:"",hatmap:c.CENTER});k.axes.lx=n.axes[0];k.axes.ly=n.axes[1];k.axes.rx=n.axes[2];k.axes.ry=n.axes[3];k.axes.ltrigger=n.buttons[6].value;k.axes.rtrigger=n.buttons[7].value;k.hat="";k.hatmap=c.CENTER;for(var m=0;mm)k.buttons[c.mapping_array[m]]=n.buttons[m].pressed,n.buttons[m].was_pressed&&this.trigger(c.mapping_array[m]+"_button_event");else switch(m){case 12:n.buttons[m].pressed&&(k.hat+="up",k.hatmap|=c.UP); -break;case 13:n.buttons[m].pressed&&(k.hat+="down",k.hatmap|=c.DOWN);break;case 14:n.buttons[m].pressed&&(k.hat+="left",k.hatmap|=c.LEFT);break;case 15:n.buttons[m].pressed&&(k.hat+="right",k.hatmap|=c.RIGHT);break;case 16:k.buttons.home=n.buttons[m].pressed}n.xbox=k;return n}};c.prototype.onDrawBackground=function(c){if(!this.flags.collapsed){var k=this._left_axis,m=this._right_axis;c.strokeStyle="#88A";c.strokeRect(0.5*(k[0]+1)*this.size[0]-4,0.5*(k[1]+1)*this.size[1]-4,8,8);c.strokeStyle="#8A8"; -c.strokeRect(0.5*(m[0]+1)*this.size[0]-4,0.5*(m[1]+1)*this.size[1]-4,8,8);k=this.size[1]/this._current_buttons.length;c.fillStyle="#AEB";for(m=0;m","enum",{values:a.values});this.size=[80,60]}function b(){this.addInput("inc", -"number");this.addOutput("total","number");this.addProperty("increment",1);this.addProperty("value",0)}function d(){this.addInput("v","number");this.addOutput("sin","number");this.addProperty("amplitude",1);this.addProperty("offset",0);this.bgImageUrl="nodes/imgs/icon-sin.png"}function f(){this.addInput("x","number");this.addInput("y","number");this.addOutput("","number");this.properties={x:1,y:1,formula:"x+y"};this.code_widget=this.addWidget("text","F(x,y)",this.properties.formula,function(a,b,d){d.properties.formula= -a});this.addWidget("toggle","allow",z.allow_scripts,function(a){z.allow_scripts=a});this._func=null}function g(){this.addInput("vec2","vec2");this.addOutput("x","number");this.addOutput("y","number")}function q(){this.addInputs([["x","number"],["y","number"]]);this.addOutput("vec2","vec2");this.properties={x:0,y:0};this._data=new Float32Array(2)}function A(){this.addInput("vec3","vec3");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number")}function K(){this.addInputs([["x", -"number"],["y","number"],["z","number"]]);this.addOutput("vec3","vec3");this.properties={x:0,y:0,z:0};this._data=new Float32Array(3)}function r(){this.addInput("vec4","vec4");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number");this.addOutput("w","number")}function s(){this.addInputs([["x","number"],["y","number"],["z","number"],["w","number"]]);this.addOutput("vec4","vec4");this.properties={x:0,y:0,z:0,w:0};this._data=new Float32Array(4)}var z=C.LiteGraph;c.title= -"Converter";c.desc="type A to type B";c.prototype.onExecute=function(){var a=this.getInputData(0);if(null!=a&&this.outputs)for(var b=0;ba&&(a+=1024);var c=Math.floor(a);a-=c;d=h.data[c];c=h.data[1023==c?0:c+1];b&&(a=a*a*a*(a*(6*a-15)+10));return d*(1-a)+c*a};h.prototype.onExecute=function(){for(var a=this.getInputData(0)||0,b=this.properties.octaves||1,d=0,c=1,a=a+(this.properties.seed||0),f=this.properties.speed||1,g=0,e=0;ec);++e);a=this.properties.min; -this._last_v=d/g*(this.properties.max-a)+a;this.setOutputData(0,this._last_v)};h.prototype.onDrawBackground=function(a){this.outputs[0].label=(this._last_v||0).toFixed(3)};z.registerNodeType("math/noise",h);D.title="Spikes";D.desc="spike every random time";D.prototype.onExecute=function(){var a=this.graph.elapsed_time;this._remaining_time-=a;this._blink_time-=a;a=0;0this._remaining_time?(this._remaining_time= -Math.random()*(this.properties.max_time-this.properties.min_time)+this.properties.min_time,this._blink_time=this.properties.duration,this.boxcolor="#FFF"):this.boxcolor="#000";this.setOutputData(0,a)};z.registerNodeType("math/spikes",D);x.title="Clamp";x.desc="Clamp number between min and max";x.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(a=Math.max(this.properties.min,a),a=Math.min(this.properties.max,a),this.setOutputData(0,a))};x.prototype.getCode=function(a){a="";this.isInputConnected(0)&& -(a+="clamp({{0}},"+this.properties.min+","+this.properties.max+")");return a};z.registerNodeType("math/clamp",x);B.title="Lerp";B.desc="Linear Interpolation";B.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.getInputData(1);null==b&&(b=0);var d=this.properties.f,c=this.getInputData(2);void 0!==c&&(d=c);this.setOutputData(0,a*(1-d)+b*d)};B.prototype.onGetInputs=function(){return[["f","number"]]};z.registerNodeType("math/lerp",B);G.title="Abs";G.desc="Absolute";G.prototype.onExecute= -function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.abs(a))};z.registerNodeType("math/abs",G);E.title="Floor";E.desc="Floor number to remove fractional part";E.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.floor(a))};z.registerNodeType("math/floor",E);e.title="Frac";e.desc="Returns fractional part";e.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a%1)};z.registerNodeType("math/frac",e);p.title= -"Smoothstep";p.desc="Smoothstep";p.prototype.onExecute=function(){var a=this.getInputData(0);if(void 0!==a){var b=this.properties.A,a=Math.clamp((a-b)/(this.properties.B-b),0,1);this.setOutputData(0,a*a*(3-2*a))}};z.registerNodeType("math/smoothstep",p);F.title="Scale";F.desc="v * factor";F.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a*this.properties.factor)};z.registerNodeType("math/scale",F);v.title="Gate";v.desc="if v is true, then outputs A, otherwise B"; -v.prototype.onExecute=function(){var a=this.getInputData(0);this.setOutputData(0,this.getInputData(a?1:2))};z.registerNodeType("math/gate",v);H.title="Average";H.desc="Average Filter";H.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this._values.length;this._values[this._current%b]=a;this._current+=1;this._current>b&&(this._current=0);for(var d=a=0;db&&(b=1);this.properties.samples= -Math.round(b);var d=this._values;this._values=new Float32Array(this.properties.samples);d.length<=this._values.length?this._values.set(d):this._values.set(d.subarray(0,this._values.length))};z.registerNodeType("math/average",H);w.title="TendTo";w.desc="moves the output value always closer to the input";w.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.factor;this._value=null==this._value?a:this._value*(1-b)+a*b;this.setOutputData(0,this._value)};z.registerNodeType("math/tendTo", -w);t.values="+ - * / % ^ max min".split(" ");t.title="Operation";t.desc="Easy math operators";t["@OP"]={type:"enum",title:"operation",values:t.values};t.size=[100,60];t.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"};t.prototype.setValue=function(a){"string"==typeof a&&(a=parseFloat(a));this.properties.value=a};t.prototype.onPropertyChanged=function(a,b){if("OP"==a)switch(this.properties.OP){case "+":this._func= -function(a,b){return a+b};break;case "-":this._func=function(a,b){return a-b};break;case "x":case "X":case "*":this._func=function(a,b){return a*b};break;case "/":this._func=function(a,b){return a/b};break;case "%":this._func=function(a,b){return a%b};break;case "^":this._func=function(a,b){return Math.pow(a,b)};break;case "max":this._func=function(a,b){return Math.max(a,b)};break;case "min":this._func=function(a,b){return Math.min(a,b)};break;default:console.warn("Unknown operation: "+this.properties.OP), -this._func=function(a){return a}}};t.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);null!=a?a.constructor===Number&&(this.properties.A=a):a=this.properties.A;null!=b?this.properties.B=b:b=this.properties.B;var d;if(a.constructor===Number)d=this._func(a,b);else if(a.constructor===Array){d=this._result;d.length=a.length;for(var c=0;cB":g=a>b;break;case "A=B":g=a>=b}this.setOutputData(d,g)}}};l.prototype.onGetOutputs=function(){return[["A==B","boolean"],["A!=B","boolean"], -["A>B","boolean"],["A=B","boolean"],["A<=B","boolean"]]};z.registerNodeType("math/compare",l);z.registerSearchboxExtra("math/compare","==",{outputs:[["A==B","boolean"]],title:"A==B"});z.registerSearchboxExtra("math/compare","!=",{outputs:[["A!=B","boolean"]],title:"A!=B"});z.registerSearchboxExtra("math/compare",">",{outputs:[["A>B","boolean"]],title:"A>B"});z.registerSearchboxExtra("math/compare","<",{outputs:[["A=",{outputs:[["A>=B","boolean"]],title:"A>=B"});z.registerSearchboxExtra("math/compare","<=",{outputs:[["A<=B","boolean"]],title:"A<=B"});a.values="> < == != <= >= || &&".split(" ");a["@OP"]={type:"enum",title:"operation",values:a.values};a.title="Condition";a.desc="evaluates condition between A and B";a.prototype.getTitle=function(){return"A "+this.properties.OP+" B"};a.prototype.onExecute=function(){var a=this.getInputData(0);void 0===a?a=this.properties.A:this.properties.A=a;var b=this.getInputData(1); -void 0===b?b=this.properties.B:this.properties.B=b;var d=!0;switch(this.properties.OP){case ">":d=a>b;break;case "<":d=a=":d=a>=b;break;case "||":d=a||b;break;case "&&":d=a&&b}this.setOutputData(0,d);this.setOutputData(1,!d)};z.registerNodeType("math/condition",a);b.title="Accumulate";b.desc="Increments a value every time";b.prototype.onExecute=function(){null===this.properties.value&&(this.properties.value=0);var a= -this.getInputData(0);this.properties.value=null!==a?this.properties.value+a:this.properties.value+this.properties.increment;this.setOutputData(0,this.properties.value)};z.registerNodeType("math/accumulate",b);d.title="Trigonometry";d.desc="Sin Cos Tan";d.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.amplitude,d=this.findInputSlot("amplitude");-1!=d&&(b=this.getInputData(d));var c=this.properties.offset,d=this.findInputSlot("offset");-1!=d&&(c=this.getInputData(d)); -for(var d=0,f=this.outputs.length;dXY";g.desc="vector 2 to components";g.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]))};z.registerNodeType("math3d/vec2-to-xy",g);q.title="XY->Vec2";q.desc="components to vector2";q.prototype.onExecute= -function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this._data;d[0]=a;d[1]=b;this.setOutputData(0,d)};z.registerNodeType("math3d/xy-to-vec2",q);A.title="Vec3->XYZ";A.desc="vector 3 to components";A.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]))};z.registerNodeType("math3d/vec3-to-xyz",A);K.title="XYZ->Vec3";K.desc="components to vector3"; -K.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this._data;c[0]=a;c[1]=b;c[2]=d;this.setOutputData(0,c)};z.registerNodeType("math3d/xyz-to-vec3",K);r.title="Vec4->XYZW";r.desc="vector 4 to components";r.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2, -a[2]),this.setOutputData(3,a[3]))};z.registerNodeType("math3d/vec4-to-xyzw",r);s.title="XYZW->Vec4";s.desc="components to vector4";s.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this.getInputData(3);null==c&&(c=this.properties.w);var f=this._data;f[0]=a;f[1]=b;f[2]=d;f[3]=c;this.setOutputData(0,f)};z.registerNodeType("math3d/xyzw-to-vec4", -s)})(this); -(function(C){function c(){this.addInput("T","vec3");this.addInput("R","vec3");this.addInput("S","vec3");this.addOutput("mat4","mat4");this.properties={T:[0,0,0],R:[0,0,0],S:[1,1,1],R_in_degrees:!0};this._result=mat4.create();this._must_update=!0}function m(){this.addInput("A","number,vec3");this.addInput("B","number,vec3");this.addOutput("=","number,vec3");this.addProperty("OP","+","enum",{values:m.values});this._result=vec3.create()}function n(){this.addInput("in","vec3");this.addInput("f","number"); -this.addOutput("out","vec3");this.properties={f:1};this._data=new Float32Array(3)}function k(){this.addInput("in","vec3");this.addOutput("out","number")}function y(){this.addInput("in","vec3");this.addOutput("out","vec3");this._data=new Float32Array(3)}function h(){this.addInput("A","vec3");this.addInput("B","vec3");this.addInput("f","vec3");this.addOutput("out","vec3");this.properties={f:0.5};this._data=new Float32Array(3)}function D(){this.addInput("A","vec3");this.addInput("B","vec3");this.addOutput("out", -"number")}var x=C.LiteGraph;c.title="mat4";c.temp_quat=new Float32Array([0,0,0,1]);c.temp_mat4=new Float32Array(16);c.temp_vec3=new Float32Array(3);c.prototype.onPropertyChanged=function(c,h){this._must_update=!0};c.prototype.onExecute=function(){var h=this._result,k=c.temp_quat,n=c.temp_mat4,e=c.temp_vec3,p=this.getInputData(0),m=this.getInputData(1),v=this.getInputData(2);if(this._must_update||p||m||v)p=p||this.properties.T,m=m||this.properties.R,v=v||this.properties.S,mat4.identity(h),mat4.translate(h, -h,p),this.properties.R_in_degrees?(e.set(m),vec3.scale(e,e,DEG2RAD),quat.fromEuler(k,e)):quat.fromEuler(k,m),mat4.fromQuat(n,k),mat4.multiply(h,h,n),mat4.scale(h,h,v);this.setOutputData(0,h)};x.registerNodeType("math3d/mat4",c);m.values="+ - * / % ^ max min dot cross".split(" ");x.registerSearchboxExtra("math3d/operation","CROSS()",{properties:{OP:"cross"},title:"CROSS()"});x.registerSearchboxExtra("math3d/operation","DOT()",{properties:{OP:"dot"},title:"DOT()"});m.title="Operation";m.desc="Easy math 3D operators"; -m["@OP"]={type:"enum",title:"operation",values:m.values};m.size=[100,60];m.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"};m.prototype.onExecute=function(){var c=this.getInputData(0),h=this.getInputData(1);if(null!=c&&null!=h){c.constructor===Number&&(c=[c,c,c]);h.constructor===Number&&(h=[h,h,h]);var k=this._result;switch(this.properties.OP){case "+":k=vec3.add(k,c,h);break;case "-":k=vec3.sub(k,c,h); +(function(y){function c(){this.size=[60,30];this.addInput("event",x.ACTION)}function n(){this.size=[60,30];this.addInput("if","");this.addOutput("true",x.EVENT);this.addOutput("change",x.EVENT);this.addOutput("false",x.EVENT);this.properties={only_on_change:!0};this.prev=0}function s(){this.addInput("",x.ACTION);this.addInput("",x.ACTION);this.addInput("",x.ACTION);this.addInput("",x.ACTION);this.addInput("",x.ACTION);this.addInput("",x.ACTION);this.addOutput("",x.EVENT);this.addOutput("",x.EVENT); +this.addOutput("",x.EVENT);this.addOutput("",x.EVENT);this.addOutput("",x.EVENT);this.addOutput("",x.EVENT);this.size=[120,30];this.flags={horizontal:!0,render_box:!1}}function h(){this.size=[60,30];this.addInput("event",x.ACTION);this.addOutput("event",x.EVENT);this.properties={equal_to:"",has_property:"",property_equal_to:""}}function w(){this.addInput("in",x.ACTION);this.addInput("cond","boolean");this.addOutput("true",x.EVENT);this.addOutput("false",x.EVENT);this.size=[120,60];this._value=!1} +function k(){this.addInput("inc",x.ACTION);this.addInput("dec",x.ACTION);this.addInput("reset",x.ACTION);this.addOutput("change",x.EVENT);this.addOutput("num","number");this.num=0}function B(){this.size=[60,30];this.addProperty("time_in_ms",1E3);this.addInput("event",x.ACTION);this.addOutput("on_time",x.EVENT);this._pending=[]}function v(){this.addProperty("interval",1E3);this.addProperty("event","tick");this.addOutput("on_tick",x.EVENT);this.time=0;this.last_interval=1E3;this.triggered=!1}function F(){this.addInput("data", +"");this.addInput("assign",x.ACTION);this.addOutput("data","");this._last_value=null;this.properties={data:null,serialize:!0};var c=this;this.addWidget("button","store","",function(){c.properties.data=c._last_value})}var x=y.LiteGraph;c.title="Log Event";c.desc="Log event in console";c.prototype.onAction=function(c,e){console.log(c,e)};x.registerNodeType("events/log",c);n.title="TriggerEvent";n.desc="Triggers event if input evaluates to true";n.prototype.onExecute=function(c,e){var m=this.getInputData(0), +h=m!=this.prev;0===this.prev&&(h=!1);var k=h&&this.properties.only_on_change||!h&&!this.properties.only_on_change;m&&k&&this.triggerSlot(0,e);!m&&k&&this.triggerSlot(2,e);h&&this.triggerSlot(1,e);this.prev=m};x.registerNodeType("events/trigger",n);s.title="Sequencer";s.desc="Trigger events when an event arrives";s.prototype.getTitle=function(){return""};s.prototype.onAction=function(c,e){if(this.outputs)for(var m=0;m=m?this.trigger(null,e):this._pending.push([m,e])};B.prototype.onExecute=function(){var c=1E3*this.graph.elapsed_time;this.isInputConnected(1)&&(this.properties.time_in_ms= +this.getInputData(1));for(var e=0;em[1]))return this.old_y=c.canvasY,this.captureInput(!0),this.mouse_captured=!0};s.prototype.onMouseMove=function(c){if(this.mouse_captured){var m=this.old_y-c.canvasY;c.shiftKey&&(m*=10);if(c.metaKey||c.altKey)m*=0.1;this.old_y=c.canvasY;c=this._remainder+m/s.pixels_threshold;this._remainder=c%1;c=Math.clamp(this.properties.value+ +(c|0)*this.properties.step,this.properties.min,this.properties.max);this.properties.value=c;this.graph._version++;this.setDirtyCanvas(!0)}};s.prototype.onMouseUp=function(c,m){200>c.click_time&&(this.properties.value=Math.clamp(this.properties.value+(m[1]>0.5*this.size[1]?-1:1)*this.properties.step,this.properties.min,this.properties.max),this.graph._version++,this.setDirtyCanvas(!0));this.mouse_captured&&(this.mouse_captured=!1,this.captureInput(!1))};C.registerNodeType("widget/number",s);h.title= +"Combo";h.desc="Widget to select from a list";h.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};h.prototype.onPropertyChanged=function(c,m){"values"==c?(this._values=m.split(";"),this.widget.options.values=this._values):"value"==c&&(this.widget.value=m)};C.registerNodeType("widget/combo",h);w.title="Knob";w.desc="Circular controller";w.size=[80,100];w.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/ +(this.properties.max-this.properties.min));var m=0.5*this.size[0],h=0.5*this.size[1],k=0.5*Math.min(this.size[0],this.size[1])-5;c.globalAlpha=1;c.save();c.translate(m,h);c.rotate(0.75*Math.PI);c.fillStyle="rgba(0,0,0,0.5)";c.beginPath();c.moveTo(0,0);c.arc(0,0,k,0,1.5*Math.PI);c.fill();c.strokeStyle="black";c.fillStyle=this.properties.color;c.lineWidth=2;c.beginPath();c.moveTo(0,0);c.arc(0,0,k-4,0,1.5*Math.PI*Math.max(0.01,this.value));c.closePath();c.fill();c.lineWidth=1;c.globalAlpha=1;c.restore(); +c.fillStyle="black";c.beginPath();c.arc(m,h,0.75*k,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":this.properties.color;c.beginPath();var s=this.value*Math.PI*1.5+0.75*Math.PI;c.arc(m+Math.cos(s)*k*0.65,h+Math.sin(s)*k*0.65,0.05*k,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":"#AAA";c.font=Math.floor(0.5*k)+"px Arial";c.textAlign="center";c.fillText(this.properties.value.toFixed(this.properties.precision),m,h+0.15*k)}};w.prototype.onExecute=function(){this.setOutputData(0, +this.properties.value);this.boxcolor=C.colorToString([this.value,this.value,this.value])};w.prototype.onMouseDown=function(c){this.center=[0.5*this.size[0],0.5*this.size[1]+20];this.radius=0.5*this.size[0];if(20>c.canvasY-this.pos[1]||C.distance([c.canvasX,c.canvasY],[this.pos[0]+this.center[0],this.pos[1]+this.center[1]])>this.radius)return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];this.captureInput(!0);return!0};w.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX- +this.pos[0],c.canvasY-this.pos[1]];var m=this.value,m=m-0.01*(c[1]-this.oldmouse[1]);1m&&(m=0);this.value=m;this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.oldmouse=c;this.setDirtyCanvas(!0)}};w.prototype.onMouseUp=function(c){this.oldmouse&&(this.oldmouse=null,this.captureInput(!1))};w.prototype.onPropertyChanged=function(c,m){if("min"==c||"max"==c||"value"==c)return this.properties[c]=parseFloat(m),!0};C.registerNodeType("widget/knob", +w);k.title="Inner Slider";k.prototype.onPropertyChanged=function(c,m){"value"==c&&(this.slider.value=m)};k.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};C.registerNodeType("widget/internal_slider",k);B.title="H.Slider";B.desc="Linear slider controller";B.prototype.onDrawForeground=function(c){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min));c.globalAlpha=1;c.lineWidth=1;c.fillStyle="#000";c.fillRect(2, +2,this.size[0]-4,this.size[1]-4);c.fillStyle=this.properties.color;c.beginPath();c.rect(4,4,(this.size[0]-8)*this.value,this.size[1]-8);c.fill()};B.prototype.onExecute=function(){this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.setOutputData(0,this.properties.value);this.boxcolor=C.colorToString([this.value,this.value,this.value])};B.prototype.onMouseDown=function(c){if(0>c.canvasY-this.pos[1])return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY- +this.pos[1]];this.captureInput(!0);return!0};B.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];var m=this.value,m=m+(c[0]-this.oldmouse[0])/this.size[0];1m&&(m=0);this.value=m;this.oldmouse=c;this.setDirtyCanvas(!0)}};B.prototype.onMouseUp=function(c){this.oldmouse=null;this.captureInput(!1)};B.prototype.onMouseLeave=function(c){};C.registerNodeType("widget/hslider",B);v.title="Progress";v.desc="Shows data in linear progress";v.prototype.onExecute= +function(){var c=this.getInputData(0);void 0!=c&&(this.properties.value=c)};v.prototype.onDrawForeground=function(c){c.lineWidth=1;c.fillStyle=this.properties.color;var m=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min),m=Math.min(1,m),m=Math.max(0,m);c.fillRect(2,2,(this.size[0]-4)*m,this.size[1]-4)};C.registerNodeType("widget/progress",v);F.title="Text";F.desc="Shows the input value";F.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"led_text", +text:"LED",type:"minibutton"},{name:"normal_text",text:"Normal",type:"minibutton"}];F.prototype.onDrawForeground=function(c){c.fillStyle=this.properties.color;var m=this.properties.value;this.properties.glowSize?(c.shadowColor=this.properties.color,c.shadowOffsetX=0,c.shadowOffsetY=0,c.shadowBlur=this.properties.glowSize):c.shadowColor="transparent";var h=this.properties.fontsize;c.textAlign=this.properties.align;c.font=h.toString()+"px "+this.properties.font;this.str="number"==typeof m?m.toFixed(this.properties.decimals): +m;if("string"==typeof this.str){var m=this.str.split("\\n"),k;for(k in m)c.fillText(m[k],"left"==this.properties.align?15:this.size[0]-15,-0.15*h+h*(parseInt(k)+1))}c.shadowColor="transparent";this.last_ctx=c;c.textAlign="left"};F.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(this.properties.value=c)};F.prototype.resize=function(){if(this.last_ctx){var c=this.str.split("\\n");this.last_ctx.font=this.properties.fontsize+"px "+this.properties.font;var m=0,h;for(h in c){var k=this.last_ctx.measureText(c[h]).width; +mh?s.xbox.axes.lx:0,this._left_axis[1]=Math.abs(s.xbox.axes.ly)>h?s.xbox.axes.ly:0,this._right_axis[0]=Math.abs(s.xbox.axes.rx)>h?s.xbox.axes.rx:0,this._right_axis[1]=Math.abs(s.xbox.axes.ry)>h?s.xbox.axes.ry:0,this._triggers[0]=Math.abs(s.xbox.axes.ltrigger)>h?s.xbox.axes.ltrigger: +0,this._triggers[1]=Math.abs(s.xbox.axes.rtrigger)>h?s.xbox.axes.rtrigger:0);if(this.outputs)for(h=0;hs;s++)if(h[s]){s=h[s];h=this.xbox_mapping;h||(h=this.xbox_mapping={axes:[], +buttons:{},hat:"",hatmap:c.CENTER});h.axes.lx=s.axes[0];h.axes.ly=s.axes[1];h.axes.rx=s.axes[2];h.axes.ry=s.axes[3];h.axes.ltrigger=s.buttons[6].value;h.axes.rtrigger=s.buttons[7].value;h.hat="";h.hatmap=c.CENTER;for(var n=0;nn)h.buttons[c.mapping_array[n]]=s.buttons[n].pressed,s.buttons[n].was_pressed&&this.trigger(c.mapping_array[n]+"_button_event");else switch(n){case 12:s.buttons[n].pressed&&(h.hat+="up",h.hatmap|=c.UP); +break;case 13:s.buttons[n].pressed&&(h.hat+="down",h.hatmap|=c.DOWN);break;case 14:s.buttons[n].pressed&&(h.hat+="left",h.hatmap|=c.LEFT);break;case 15:s.buttons[n].pressed&&(h.hat+="right",h.hatmap|=c.RIGHT);break;case 16:h.buttons.home=s.buttons[n].pressed}s.xbox=h;return s}};c.prototype.onDrawBackground=function(c){if(!this.flags.collapsed){var h=this._left_axis,n=this._right_axis;c.strokeStyle="#88A";c.strokeRect(0.5*(h[0]+1)*this.size[0]-4,0.5*(h[1]+1)*this.size[1]-4,8,8);c.strokeStyle="#8A8"; +c.strokeRect(0.5*(n[0]+1)*this.size[0]-4,0.5*(n[1]+1)*this.size[1]-4,8,8);h=this.size[1]/this._current_buttons.length;c.fillStyle="#AEB";for(n=0;n","enum",{values:a.values});this.addWidget("combo","Cond.",this.properties.OP, +{property:"OP",values:a.values});this.size=[80,60]}function b(){this.addInput("in","");this.addInput("cond","boolean");this.addOutput("true","");this.addOutput("false","");this.size=[80,60]}function d(){this.addInput("inc","number");this.addOutput("total","number");this.addProperty("increment",1);this.addProperty("value",0)}function f(){this.addInput("v","number");this.addOutput("sin","number");this.addProperty("amplitude",1);this.addProperty("offset",0);this.bgImageUrl="nodes/imgs/icon-sin.png"} +function g(){this.addInput("x","number");this.addInput("y","number");this.addOutput("","number");this.properties={x:1,y:1,formula:"x+y"};this.code_widget=this.addWidget("text","F(x,y)",this.properties.formula,function(a,b,d){d.properties.formula=a});this.addWidget("toggle","allow",G.allow_scripts,function(a){G.allow_scripts=a});this._func=null}function t(){this.addInput("vec2","vec2");this.addOutput("x","number");this.addOutput("y","number")}function z(){this.addInputs([["x","number"],["y","number"]]); +this.addOutput("vec2","vec2");this.properties={x:0,y:0};this._data=new Float32Array(2)}function O(){this.addInput("vec3","vec3");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number")}function L(){this.addInputs([["x","number"],["y","number"],["z","number"]]);this.addOutput("vec3","vec3");this.properties={x:0,y:0,z:0};this._data=new Float32Array(3)}function N(){this.addInput("vec4","vec4");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z", +"number");this.addOutput("w","number")}function M(){this.addInputs([["x","number"],["y","number"],["z","number"],["w","number"]]);this.addOutput("vec4","vec4");this.properties={x:0,y:0,z:0,w:0};this._data=new Float32Array(4)}var G=y.LiteGraph;c.title="Converter";c.desc="type A to type B";c.prototype.onExecute=function(){var a=this.getInputData(0);if(null!=a&&this.outputs)for(var b=0;ba&&(a+=1024);var c=Math.floor(a);a-=c;d=k.data[c];c=k.data[1023==c?0:c+1];b&&(a=a*a*a*(a*(6*a-15)+10));return d*(1-a)+c*a};k.prototype.onExecute=function(){for(var a= +this.getInputData(0)||0,b=this.properties.octaves||1,d=0,c=1,a=a+(this.properties.seed||0),f=this.properties.speed||1,g=0,l=0;lc);++l);a=this.properties.min;this._last_v=d/g*(this.properties.max-a)+a;this.setOutputData(0,this._last_v)};k.prototype.onDrawBackground=function(a){this.outputs[0].label=(this._last_v||0).toFixed(3)};G.registerNodeType("math/noise",k);B.title="Spikes";B.desc="spike every random time"; +B.prototype.onExecute=function(){var a=this.graph.elapsed_time;this._remaining_time-=a;this._blink_time-=a;a=0;0this._remaining_time?(this._remaining_time=Math.random()*(this.properties.max_time-this.properties.min_time)+this.properties.min_time,this._blink_time=this.properties.duration,this.boxcolor="#FFF"):this.boxcolor="#000";this.setOutputData(0,a)};G.registerNodeType("math/spikes",B);v.title="Clamp";v.desc= +"Clamp number between min and max";v.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(a=Math.max(this.properties.min,a),a=Math.min(this.properties.max,a),this.setOutputData(0,a))};v.prototype.getCode=function(a){a="";this.isInputConnected(0)&&(a+="clamp({{0}},"+this.properties.min+","+this.properties.max+")");return a};G.registerNodeType("math/clamp",v);F.title="Lerp";F.desc="Linear Interpolation";F.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b= +this.getInputData(1);null==b&&(b=0);var d=this.properties.f,c=this.getInputData(2);void 0!==c&&(d=c);this.setOutputData(0,a*(1-d)+b*d)};F.prototype.onGetInputs=function(){return[["f","number"]]};G.registerNodeType("math/lerp",F);x.title="Abs";x.desc="Absolute";x.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.abs(a))};G.registerNodeType("math/abs",x);C.title="Floor";C.desc="Floor number to remove fractional part";C.prototype.onExecute=function(){var a= +this.getInputData(0);null!=a&&this.setOutputData(0,Math.floor(a))};G.registerNodeType("math/floor",C);e.title="Frac";e.desc="Returns fractional part";e.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a%1)};G.registerNodeType("math/frac",e);m.title="Smoothstep";m.desc="Smoothstep";m.prototype.onExecute=function(){var a=this.getInputData(0);if(void 0!==a){var b=this.properties.A,a=Math.clamp((a-b)/(this.properties.B-b),0,1);this.setOutputData(0,a*a*(3-2*a))}}; +G.registerNodeType("math/smoothstep",m);D.title="Scale";D.desc="v * factor";D.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a*this.properties.factor)};G.registerNodeType("math/scale",D);u.title="Gate";u.desc="if v is true, then outputs A, otherwise B";u.prototype.onExecute=function(){var a=this.getInputData(0);this.setOutputData(0,this.getInputData(a?1:2))};G.registerNodeType("math/gate",u);I.title="Average";I.desc="Average Filter";I.prototype.onExecute=function(){var a= +this.getInputData(0);null==a&&(a=0);var b=this._values.length;this._values[this._current%b]=a;this._current+=1;this._current>b&&(this._current=0);for(var d=a=0;db&&(b=1);this.properties.samples=Math.round(b);var d=this._values;this._values=new Float32Array(this.properties.samples);d.length<=this._values.length?this._values.set(d):this._values.set(d.subarray(0,this._values.length))};G.registerNodeType("math/average", +I);q.title="TendTo";q.desc="moves the output value always closer to the input";q.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.factor;this._value=null==this._value?a:this._value*(1-b)+a*b;this.setOutputData(0,this._value)};G.registerNodeType("math/tendTo",q);r.values="+ - * / % ^ max min".split(" ");r.title="Operation";r.desc="Easy math operators";r["@OP"]={type:"enum",title:"operation",values:r.values};r.size=[100,60];r.prototype.getTitle=function(){return"max"== +this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"};r.prototype.setValue=function(a){"string"==typeof a&&(a=parseFloat(a));this.properties.value=a};r.prototype.onPropertyChanged=function(a,b){if("OP"==a)switch(this.properties.OP){case "+":this._func=function(a,b){return a+b};break;case "-":this._func=function(a,b){return a-b};break;case "x":case "X":case "*":this._func=function(a,b){return a*b};break;case "/":this._func=function(a,b){return a/b}; +break;case "%":this._func=function(a,b){return a%b};break;case "^":this._func=function(a,b){return Math.pow(a,b)};break;case "max":this._func=function(a,b){return Math.max(a,b)};break;case "min":this._func=function(a,b){return Math.min(a,b)};break;default:console.warn("Unknown operation: "+this.properties.OP),this._func=function(a){return a}}};r.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);null!=a?a.constructor===Number&&(this.properties.A=a):a=this.properties.A; +null!=b?this.properties.B=b:b=this.properties.B;var d;if(a.constructor===Number)d=this._func(a,b);else if(a.constructor===Array){d=this._result;d.length=a.length;for(var c=0;cB":g=a>b;break;case "A=B":g=a>=b}this.setOutputData(d,g)}}};l.prototype.onGetOutputs=function(){return[["A==B","boolean"],["A!=B","boolean"],["A>B","boolean"],["A=B","boolean"],["A<=B","boolean"]]};G.registerNodeType("math/compare",l);G.registerSearchboxExtra("math/compare","==",{outputs:[["A==B","boolean"]], +title:"A==B"});G.registerSearchboxExtra("math/compare","!=",{outputs:[["A!=B","boolean"]],title:"A!=B"});G.registerSearchboxExtra("math/compare",">",{outputs:[["A>B","boolean"]],title:"A>B"});G.registerSearchboxExtra("math/compare","<",{outputs:[["A=",{outputs:[["A>=B","boolean"]],title:"A>=B"});G.registerSearchboxExtra("math/compare","<=",{outputs:[["A<=B","boolean"]],title:"A<=B"});a.values="> < == != <= >= || &&".split(" "); +a["@OP"]={type:"enum",title:"operation",values:a.values};a.title="Condition";a.desc="evaluates condition between A and B";a.prototype.getTitle=function(){return"A "+this.properties.OP+" B"};a.prototype.onExecute=function(){var a=this.getInputData(0);void 0===a?a=this.properties.A:this.properties.A=a;var b=this.getInputData(1);void 0===b?b=this.properties.B:this.properties.B=b;var d=!0;switch(this.properties.OP){case ">":d=a>b;break;case "<":d=a=":d=a>=b;break;case "||":d=a||b;break;case "&&":d=a&&b}this.setOutputData(0,d);this.setOutputData(1,!d)};G.registerNodeType("math/condition",a);b.title="Branch";b.desc="If condition is true, outputs IN in true, otherwise in false";b.prototype.onExecute=function(){var a=this.getInputData(0);this.getInputData(1)?(this.setOutputData(0,a),this.setOutputData(1,null)):(this.setOutputData(0,null),this.setOutputData(1,a))};G.registerNodeType("math/branch",b);d.title="Accumulate"; +d.desc="Increments a value every time";d.prototype.onExecute=function(){null===this.properties.value&&(this.properties.value=0);var a=this.getInputData(0);this.properties.value=null!==a?this.properties.value+a:this.properties.value+this.properties.increment;this.setOutputData(0,this.properties.value)};G.registerNodeType("math/accumulate",d);f.title="Trigonometry";f.desc="Sin Cos Tan";f.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.amplitude,d=this.findInputSlot("amplitude"); +-1!=d&&(b=this.getInputData(d));var c=this.properties.offset,d=this.findInputSlot("offset");-1!=d&&(c=this.getInputData(d));for(var d=0,f=this.outputs.length;dXY";t.desc="vector 2 to components";t.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1, +a[1]))};G.registerNodeType("math3d/vec2-to-xy",t);z.title="XY->Vec2";z.desc="components to vector2";z.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this._data;d[0]=a;d[1]=b;this.setOutputData(0,d)};G.registerNodeType("math3d/xy-to-vec2",z);O.title="Vec3->XYZ";O.desc="vector 3 to components";O.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1, +a[1]),this.setOutputData(2,a[2]))};G.registerNodeType("math3d/vec3-to-xyz",O);L.title="XYZ->Vec3";L.desc="components to vector3";L.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this._data;c[0]=a;c[1]=b;c[2]=d;this.setOutputData(0,c)};G.registerNodeType("math3d/xyz-to-vec3",L);N.title="Vec4->XYZW";N.desc="vector 4 to components";N.prototype.onExecute= +function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]),this.setOutputData(3,a[3]))};G.registerNodeType("math3d/vec4-to-xyzw",N);M.title="XYZW->Vec4";M.desc="components to vector4";M.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this.getInputData(3);null==c&&(c=this.properties.w); +var f=this._data;f[0]=a;f[1]=b;f[2]=d;f[3]=c;this.setOutputData(0,f)};G.registerNodeType("math3d/xyzw-to-vec4",M)})(this); +(function(y){function c(){this.addInput("T","vec3");this.addInput("R","vec3");this.addInput("S","vec3");this.addOutput("mat4","mat4");this.properties={T:[0,0,0],R:[0,0,0],S:[1,1,1],R_in_degrees:!0};this._result=mat4.create();this._must_update=!0}function n(){this.addInput("A","number,vec3");this.addInput("B","number,vec3");this.addOutput("=","number,vec3");this.addProperty("OP","+","enum",{values:n.values});this._result=vec3.create()}function s(){this.addInput("in","vec3");this.addInput("f","number"); +this.addOutput("out","vec3");this.properties={f:1};this._data=new Float32Array(3)}function h(){this.addInput("in","vec3");this.addOutput("out","number")}function w(){this.addInput("in","vec3");this.addOutput("out","vec3");this._data=new Float32Array(3)}function k(){this.addInput("A","vec3");this.addInput("B","vec3");this.addInput("f","vec3");this.addOutput("out","vec3");this.properties={f:0.5};this._data=new Float32Array(3)}function B(){this.addInput("A","vec3");this.addInput("B","vec3");this.addOutput("out", +"number")}var v=y.LiteGraph;c.title="mat4";c.temp_quat=new Float32Array([0,0,0,1]);c.temp_mat4=new Float32Array(16);c.temp_vec3=new Float32Array(3);c.prototype.onPropertyChanged=function(c,h){this._must_update=!0};c.prototype.onExecute=function(){var h=this._result,k=c.temp_quat,s=c.temp_mat4,e=c.temp_vec3,m=this.getInputData(0),n=this.getInputData(1),u=this.getInputData(2);if(this._must_update||m||n||u)m=m||this.properties.T,n=n||this.properties.R,u=u||this.properties.S,mat4.identity(h),mat4.translate(h, +h,m),this.properties.R_in_degrees?(e.set(n),vec3.scale(e,e,DEG2RAD),quat.fromEuler(k,e)):quat.fromEuler(k,n),mat4.fromQuat(s,k),mat4.multiply(h,h,s),mat4.scale(h,h,u);this.setOutputData(0,h)};v.registerNodeType("math3d/mat4",c);n.values="+ - * / % ^ max min dot cross".split(" ");v.registerSearchboxExtra("math3d/operation","CROSS()",{properties:{OP:"cross"},title:"CROSS()"});v.registerSearchboxExtra("math3d/operation","DOT()",{properties:{OP:"dot"},title:"DOT()"});n.title="Operation";n.desc="Easy math 3D operators"; +n["@OP"]={type:"enum",title:"operation",values:n.values};n.size=[100,60];n.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"};n.prototype.onExecute=function(){var c=this.getInputData(0),h=this.getInputData(1);if(null!=c&&null!=h){c.constructor===Number&&(c=[c,c,c]);h.constructor===Number&&(h=[h,h,h]);var k=this._result;switch(this.properties.OP){case "+":k=vec3.add(k,c,h);break;case "-":k=vec3.sub(k,c,h); break;case "x":case "X":case "*":k=vec3.mul(k,c,h);break;case "/":k=vec3.div(k,c,h);break;case "%":k[0]=c[0]%h[0];k[1]=c[1]%h[1];k[2]=c[2]%h[2];break;case "^":k[0]=Math.pow(c[0],h[0]);k[1]=Math.pow(c[1],h[1]);k[2]=Math.pow(c[2],h[2]);break;case "max":k[0]=Math.max(c[0],h[0]);k[1]=Math.max(c[1],h[1]);k[2]=Math.max(c[2],h[2]);break;case "min":k[0]=Math.min(c[0],h[0]),k[1]=Math.min(c[1],h[1]),k[2]=Math.min(c[2],h[2]);case "dot":k=vec3.dot(c,h);break;case "cross":vec3.cross(k,c,h);break;default:console.warn("Unknown operation: "+ -this.properties.OP)}this.setOutputData(0,k)}};m.prototype.onDrawBackground=function(c){this.flags.collapsed||(c.font="40px Arial",c.fillStyle="#666",c.textAlign="center",c.fillText(this.properties.OP,0.5*this.size[0],0.5*(this.size[1]+x.NODE_TITLE_HEIGHT)),c.textAlign="left")};x.registerNodeType("math3d/operation",m);n.title="vec3_scale";n.desc="scales the components of a vec3";n.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);null==h&&(h=this.properties.f); -var k=this._data;k[0]=c[0]*h;k[1]=c[1]*h;k[2]=c[2]*h;this.setOutputData(0,k)}};x.registerNodeType("math3d/vec3-scale",n);k.title="vec3_length";k.desc="returns the module of a vector";k.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(c=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),this.setOutputData(0,c))};x.registerNodeType("math3d/vec3-length",k);y.title="vec3_normalize";y.desc="returns the vector normalized";y.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h= -Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),k=this._data;k[0]=c[0]/h;k[1]=c[1]/h;k[2]=c[2]/h;this.setOutputData(0,k)}};x.registerNodeType("math3d/vec3-normalize",y);h.title="vec3_lerp";h.desc="returns the interpolated vector";h.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);if(null!=h){var k=this.getInputOrProperty("f"),e=this._data;e[0]=c[0]*(1-k)+h[0]*k;e[1]=c[1]*(1-k)+h[1]*k;e[2]=c[2]*(1-k)+h[2]*k;this.setOutputData(0,e)}}};x.registerNodeType("math3d/vec3-lerp", -h);D.title="vec3_dot";D.desc="returns the dot product";D.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);null!=h&&this.setOutputData(0,c[0]*h[0]+c[1]*h[1]+c[2]*h[2])}};x.registerNodeType("math3d/vec3-dot",D);C.glMatrix?(C=function(){this.addOutput("quat","quat");this.properties={x:0,y:0,z:0,w:1,normalize:!1};this._value=quat.create()},C.title="Quaternion",C.desc="quaternion",C.prototype.onExecute=function(){this._value[0]=this.getInputOrProperty("x"); -this._value[1]=this.getInputOrProperty("y");this._value[2]=this.getInputOrProperty("z");this._value[3]=this.getInputOrProperty("w");this.properties.normalize&&quat.normalize(this._value,this._value);this.setOutputData(0,this._value)},C.prototype.onGetInputs=function(){return[["x","number"],["y","number"],["z","number"],["w","number"]]},x.registerNodeType("math3d/quaternion",C),C=function(){this.addInputs([["degrees","number"],["axis","vec3"]]);this.addOutput("quat","quat");this.properties={angle:90, -axis:vec3.fromValues(0,1,0)};this._value=quat.create()},C.title="Rotation",C.desc="quaternion rotation",C.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.angle);var h=this.getInputData(1);null==h&&(h=this.properties.axis);c=quat.setAxisAngle(this._value,h,0.0174532925*c);this.setOutputData(0,c)},x.registerNodeType("math3d/rotation",C),C=function(){this.addInput("euler","vec3");this.addOutput("quat","quat");this.properties={euler:[0,0,0],use_yaw_pitch_roll:!1}; -this._degs=vec3.create();this._value=quat.create()},C.title="Euler->Quat",C.desc="Converts euler angles (in degrees) to quaternion",C.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.euler);vec3.scale(this._degs,c,DEG2RAD);this.properties.use_yaw_pitch_roll&&(this._degs=[this._degs[2],this._degs[0],this._degs[1]]);c=quat.fromEuler(this._value,this._degs);this.setOutputData(0,c)},x.registerNodeType("math3d/euler_to_quat",C),C=function(){this.addInput(["quat","quat"]); -this.addOutput("euler","vec3");this._value=vec3.create()},C.title="Euler->Quat",C.desc="Converts rotX,rotY,rotZ in degrees to quat",C.prototype.onExecute=function(){var c=this.getInputData(0);c&&(quat.toEuler(this._value,c),vec3.scale(this._value,this._value,DEG2RAD),this.setOutputData(0,this._value))},x.registerNodeType("math3d/quat_to_euler",C),C=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},C.title="Rot. Vec3",C.desc= -"rotate a point",C.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.vec);var h=this.getInputData(1);null==h?this.setOutputData(c):this.setOutputData(0,vec3.transformQuat(vec3.create(),c,h))},x.registerNodeType("math3d/rotate_vec3",C),C=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},C.title="Mult. Quat",C.desc="rotate quaternion",C.prototype.onExecute=function(){var c=this.getInputData(0);if(null!= -c){var h=this.getInputData(1);null!=h&&(c=quat.multiply(this._value,c,h),this.setOutputData(0,c))}},x.registerNodeType("math3d/mult-quat",C),C=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp","quat");this.addProperty("factor",0.5);this._value=quat.create()},C.title="Quat Slerp",C.desc="quaternion spherical interpolation",C.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);if(null!=h){var k=this.properties.factor; -null!=this.getInputData(2)&&(k=this.getInputData(2));c=quat.slerp(this._value,c,h,k);this.setOutputData(0,c)}}},x.registerNodeType("math3d/quat-slerp",C),C=function(){this.addInput("vec3","vec3");this.addOutput("remap","vec3");this.addOutput("clamped","vec3");this.properties={clamp:!0,range_min:[-1,-1,0],range_max:[1,1,0],target_min:[-1,-1,0],target_max:[1,1,0]};this._value=vec3.create();this._clamped=vec3.create()},C.title="Remap Range",C.desc="remap a 3D range",C.prototype.onExecute=function(){var c= -this.getInputData(0);c&&this._value.set(c);for(var c=this.properties.range_min,h=this.properties.range_max,k=this.properties.target_min,e=this.properties.target_max,p=0;3>p;++p){var n=h[p]-c[p];this._clamped[p]=Math.clamp(this._value[p],c[p],h[p]);0==n?this._value[p]=0.5*(k[p]+e[p]):(n=(this._value[p]-c[p])/n,this.properties.clamp&&(n=Math.clamp(n,0,1)),this._value[p]=k[p]+n*(e[p]-k[p]))}this.setOutputData(0,this._value);this.setOutputData(1,this._clamped)},x.registerNodeType("math3d/remap_range", -C)):console.warn("No glmatrix found, some Math3D nodes may not work")})(this); -(function(C){function c(c,k){return c==k}function m(){this.addInput("","string");this.addOutput("table","table");this.addOutput("rows","number");this.addProperty("value","");this.addProperty("separator",",");this._table=null}C=C.LiteGraph;C.wrapFunctionAsNode("string/toString",c,[""],"String");C.wrapFunctionAsNode("string/compare",c,["string","string"],"boolean");C.wrapFunctionAsNode("string/concatenate",function(c,k){return void 0===c?k:void 0===k?c:c+k},["string","string"],"string");C.wrapFunctionAsNode("string/contains", -function(c,k){return void 0===c||void 0===k?!1:-1!=c.indexOf(k)},["string","string"],"boolean");C.wrapFunctionAsNode("string/toUpperCase",function(c){return null!=c&&c.constructor===String?c.toUpperCase():c},["string"],"string");C.wrapFunctionAsNode("string/split",function(c,k){null==k&&(k=this.properties.separator);if(null==c)return[];if(c.constructor===String)return c.split(k||" ");if(c.constructor===Array){for(var m=[],h=0;he;++e){var h=this.getInputData(e);if(null!=h){var k=this.values[e];k.push(h);k.length>c[0]&&k.shift()}}}};c.prototype.onDrawBackground=function(e){if(!this.flags.collapsed){var h=this.size,k=0.5*h[1]/ -this.properties.scale,m=c.colors,w=0.5*h[1];e.fillStyle="#000";e.fillRect(0,0,h[0],h[1]);e.strokeStyle="#555";e.beginPath();e.moveTo(0,w);e.lineTo(h[0],w);e.stroke();if(this.inputs)for(var t=0;4>t;++t){var l=this.values[t];if(this.inputs[t]&&this.inputs[t].link){e.strokeStyle=m[t];e.beginPath();var a=l[0]*k*-1+w;e.moveTo(0,Math.clamp(a,0,h[1]));for(var b=1;be&&(e=0);if(0!=c.length){var h=[0,0,0];if(0==e)h=c[0];else if(1==e)h=c[c.length-1];else{var k= -(c.length-1)*e,e=c[Math.floor(k)],c=c[Math.floor(k)+1],k=k-Math.floor(k);h[0]=e[0]*(1-k)+c[0]*k;h[1]=e[1]*(1-k)+c[1]*k;h[2]=e[2]*(1-k)+c[2]*k}for(var w in h)h[w]/=255;this.boxcolor=colorToString(h);this.setOutputData(0,h)}};e.registerNodeType("color/palette",n);k.title="Frame";k.desc="Frame viewerew";k.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}];k.prototype.onDrawBackground=function(c){this.frame&&!this.flags.collapsed&&c.drawImage(this.frame, -0,0,this.size[0],this.size[1])};k.prototype.onExecute=function(){this.frame=this.getInputData(0);this.setDirtyCanvas(!0)};k.prototype.onWidget=function(c,e){if("resize"==e.name&&this.frame){var h=this.frame.width,k=this.frame.height;h||null==this.frame.videoWidth||(h=this.frame.videoWidth,k=this.frame.videoHeight);h&&k&&(this.size=[h,k]);this.setDirtyCanvas(!0,!0)}else"view"==e.name&&this.show()};k.prototype.show=function(){showElement&&this.frame&&showElement(this.frame)};e.registerNodeType("graphics/frame", -k);y.title="Image fade";y.desc="Fades between images";y.widgets=[{name:"resizeA",text:"Resize to A",type:"button"},{name:"resizeB",text:"Resize to B",type:"button"}];y.prototype.onAdded=function(){this.createCanvas();var c=this.canvas.getContext("2d");c.fillStyle="#000";c.fillRect(0,0,this.properties.width,this.properties.height)};y.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};y.prototype.onExecute= -function(){var c=this.canvas.getContext("2d");this.canvas.width=this.canvas.width;var e=this.getInputData(0);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);e=this.getInputData(2);null==e?e=this.properties.fade:this.properties.fade=e;c.globalAlpha=e;e=this.getInputData(1);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);c.globalAlpha=1;this.setOutputData(0,this.canvas);this.setDirtyCanvas(!0)};e.registerNodeType("graphics/imagefade",y);h.title="Crop";h.desc="Crop Image"; -h.prototype.onAdded=function(){this.createCanvas()};h.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};h.prototype.onExecute=function(){var c=this.getInputData(0);c&&(c.width?(this.canvas.getContext("2d").drawImage(c,-this.properties.x,-this.properties.y,c.width*this.properties.scale,c.height*this.properties.scale),this.setOutputData(0,this.canvas)):this.setOutputData(0,null))};h.prototype.onDrawBackground= -function(c){this.flags.collapsed||this.canvas&&c.drawImage(this.canvas,0,0,this.canvas.width,this.canvas.height,0,0,this.size[0],this.size[1])};h.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"scale"==c?(this.properties[c]=parseFloat(e),0==this.properties[c]&&(console.error("Error in scale"),this.properties[c]=1)):this.properties[c]=parseInt(e);this.createCanvas();return!0};e.registerNodeType("graphics/cropImage",h);D.title="Canvas";D.desc="Canvas to render stuff";D.prototype.onExecute= -function(){var c=this.canvas,e=this.properties.width|0,h=this.properties.height|0;c.width!=e&&(c.width=e);c.height!=h&&(c.height=h);this.properties.autoclear&&this.ctx.clearRect(0,0,c.width,c.height);this.setOutputData(0,c)};D.prototype.onAction=function(c,e){"clear"==c&&this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)};e.registerNodeType("graphics/canvas",D);x.title="DrawImage";x.desc="Draws image into a canvas";x.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e= -this.getInputOrProperty("img");if(e){var h=this.getInputOrProperty("x"),k=this.getInputOrProperty("y");c.getContext("2d").drawImage(e,h,k)}}};e.registerNodeType("graphics/drawImage",x);B.title="DrawRectangle";B.desc="Draws rectangle in canvas";B.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e=this.getInputOrProperty("x"),h=this.getInputOrProperty("y"),k=this.getInputOrProperty("w"),w=this.getInputOrProperty("h");c.getContext("2d").fillRect(e,h,k,w)}};e.registerNodeType("graphics/drawRectangle", -B);G.title="Video";G.desc="Video playback";G.widgets=[{name:"play",text:"PLAY",type:"minibutton"},{name:"stop",text:"STOP",type:"minibutton"},{name:"demo",text:"Demo video",type:"button"},{name:"mute",text:"Mute video",type:"button"}];G.prototype.onExecute=function(){if(this.properties.url&&(this.properties.url!=this._video_url&&this.loadVideo(this.properties.url),this._video&&0!=this._video.width)){var c=this.getInputData(0);c&&0<=c&&1>=c&&(this._video.currentTime=c*this._video.duration,this._video.pause()); -this._video.dirty=!0;this.setOutputData(0,this._video);this.setOutputData(1,this._video.currentTime);this.setOutputData(2,this._video.duration);this.setDirtyCanvas(!0)}};G.prototype.onStart=function(){this.play()};G.prototype.onStop=function(){this.stop()};G.prototype.loadVideo=function(c){this._video_url=c;var h=c.substr(0,10).indexOf(":"),k="";-1!=h&&(k=c.substr(0,h));h="";k&&(h=c.substr(0,c.indexOf("/",k.length+3)),h=h.substr(k.length+3));this.properties.use_proxy&&k&&e.proxy&&h!=location.host&& -(c=e.proxy+c.substr(c.indexOf(":")+3));this._video=document.createElement("video");this._video.src=c;this._video.type="type=video/mp4";this._video.muted=!0;this._video.autoplay=!0;var m=this;this._video.addEventListener("loadedmetadata",function(c){console.log("Duration: "+this.duration+" seconds");console.log("Size: "+this.videoWidth+","+this.videoHeight);m.setDirtyCanvas(!0);this.width=this.videoWidth;this.height=this.videoHeight});this._video.addEventListener("progress",function(c){console.log("video loading...")}); +this.properties.OP)}this.setOutputData(0,k)}};n.prototype.onDrawBackground=function(c){this.flags.collapsed||(c.font="40px Arial",c.fillStyle="#666",c.textAlign="center",c.fillText(this.properties.OP,0.5*this.size[0],0.5*(this.size[1]+v.NODE_TITLE_HEIGHT)),c.textAlign="left")};v.registerNodeType("math3d/operation",n);s.title="vec3_scale";s.desc="scales the components of a vec3";s.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);null==h&&(h=this.properties.f); +var k=this._data;k[0]=c[0]*h;k[1]=c[1]*h;k[2]=c[2]*h;this.setOutputData(0,k)}};v.registerNodeType("math3d/vec3-scale",s);h.title="vec3_length";h.desc="returns the module of a vector";h.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&(c=Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),this.setOutputData(0,c))};v.registerNodeType("math3d/vec3-length",h);w.title="vec3_normalize";w.desc="returns the vector normalized";w.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h= +Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),k=this._data;k[0]=c[0]/h;k[1]=c[1]/h;k[2]=c[2]/h;this.setOutputData(0,k)}};v.registerNodeType("math3d/vec3-normalize",w);k.title="vec3_lerp";k.desc="returns the interpolated vector";k.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);if(null!=h){var k=this.getInputOrProperty("f"),e=this._data;e[0]=c[0]*(1-k)+h[0]*k;e[1]=c[1]*(1-k)+h[1]*k;e[2]=c[2]*(1-k)+h[2]*k;this.setOutputData(0,e)}}};v.registerNodeType("math3d/vec3-lerp", +k);B.title="vec3_dot";B.desc="returns the dot product";B.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);null!=h&&this.setOutputData(0,c[0]*h[0]+c[1]*h[1]+c[2]*h[2])}};v.registerNodeType("math3d/vec3-dot",B);y.glMatrix?(y=function(){this.addOutput("quat","quat");this.properties={x:0,y:0,z:0,w:1,normalize:!1};this._value=quat.create()},y.title="Quaternion",y.desc="quaternion",y.prototype.onExecute=function(){this._value[0]=this.getInputOrProperty("x"); +this._value[1]=this.getInputOrProperty("y");this._value[2]=this.getInputOrProperty("z");this._value[3]=this.getInputOrProperty("w");this.properties.normalize&&quat.normalize(this._value,this._value);this.setOutputData(0,this._value)},y.prototype.onGetInputs=function(){return[["x","number"],["y","number"],["z","number"],["w","number"]]},v.registerNodeType("math3d/quaternion",y),y=function(){this.addInputs([["degrees","number"],["axis","vec3"]]);this.addOutput("quat","quat");this.properties={angle:90, +axis:vec3.fromValues(0,1,0)};this._value=quat.create()},y.title="Rotation",y.desc="quaternion rotation",y.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.angle);var h=this.getInputData(1);null==h&&(h=this.properties.axis);c=quat.setAxisAngle(this._value,h,0.0174532925*c);this.setOutputData(0,c)},v.registerNodeType("math3d/rotation",y),y=function(){this.addInput("euler","vec3");this.addOutput("quat","quat");this.properties={euler:[0,0,0],use_yaw_pitch_roll:!1}; +this._degs=vec3.create();this._value=quat.create()},y.title="Euler->Quat",y.desc="Converts euler angles (in degrees) to quaternion",y.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.euler);vec3.scale(this._degs,c,DEG2RAD);this.properties.use_yaw_pitch_roll&&(this._degs=[this._degs[2],this._degs[0],this._degs[1]]);c=quat.fromEuler(this._value,this._degs);this.setOutputData(0,c)},v.registerNodeType("math3d/euler_to_quat",y),y=function(){this.addInput(["quat","quat"]); +this.addOutput("euler","vec3");this._value=vec3.create()},y.title="Euler->Quat",y.desc="Converts rotX,rotY,rotZ in degrees to quat",y.prototype.onExecute=function(){var c=this.getInputData(0);c&&(quat.toEuler(this._value,c),vec3.scale(this._value,this._value,DEG2RAD),this.setOutputData(0,this._value))},v.registerNodeType("math3d/quat_to_euler",y),y=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},y.title="Rot. Vec3",y.desc= +"rotate a point",y.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.vec);var h=this.getInputData(1);null==h?this.setOutputData(c):this.setOutputData(0,vec3.transformQuat(vec3.create(),c,h))},v.registerNodeType("math3d/rotate_vec3",y),y=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},y.title="Mult. Quat",y.desc="rotate quaternion",y.prototype.onExecute=function(){var c=this.getInputData(0);if(null!= +c){var h=this.getInputData(1);null!=h&&(c=quat.multiply(this._value,c,h),this.setOutputData(0,c))}},v.registerNodeType("math3d/mult-quat",y),y=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp","quat");this.addProperty("factor",0.5);this._value=quat.create()},y.title="Quat Slerp",y.desc="quaternion spherical interpolation",y.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var h=this.getInputData(1);if(null!=h){var k=this.properties.factor; +null!=this.getInputData(2)&&(k=this.getInputData(2));c=quat.slerp(this._value,c,h,k);this.setOutputData(0,c)}}},v.registerNodeType("math3d/quat-slerp",y),y=function(){this.addInput("vec3","vec3");this.addOutput("remap","vec3");this.addOutput("clamped","vec3");this.properties={clamp:!0,range_min:[-1,-1,0],range_max:[1,1,0],target_min:[-1,-1,0],target_max:[1,1,0]};this._value=vec3.create();this._clamped=vec3.create()},y.title="Remap Range",y.desc="remap a 3D range",y.prototype.onExecute=function(){var c= +this.getInputData(0);c&&this._value.set(c);for(var c=this.properties.range_min,h=this.properties.range_max,k=this.properties.target_min,e=this.properties.target_max,m=0;3>m;++m){var s=h[m]-c[m];this._clamped[m]=Math.clamp(this._value[m],c[m],h[m]);0==s?this._value[m]=0.5*(k[m]+e[m]):(s=(this._value[m]-c[m])/s,this.properties.clamp&&(s=Math.clamp(s,0,1)),this._value[m]=k[m]+s*(e[m]-k[m]))}this.setOutputData(0,this._value);this.setOutputData(1,this._clamped)},v.registerNodeType("math3d/remap_range", +y)):console.warn("No glmatrix found, some Math3D nodes may not work")})(this); +(function(y){function c(c,h){return c==h}function n(){this.addInput("","string");this.addOutput("table","table");this.addOutput("rows","number");this.addProperty("value","");this.addProperty("separator",",");this._table=null}y=y.LiteGraph;y.wrapFunctionAsNode("string/toString",c,[""],"String");y.wrapFunctionAsNode("string/compare",c,["string","string"],"boolean");y.wrapFunctionAsNode("string/concatenate",function(c,h){return void 0===c?h:void 0===h?c:c+h},["string","string"],"string");y.wrapFunctionAsNode("string/contains", +function(c,h){return void 0===c||void 0===h?!1:-1!=c.indexOf(h)},["string","string"],"boolean");y.wrapFunctionAsNode("string/toUpperCase",function(c){return null!=c&&c.constructor===String?c.toUpperCase():c},["string"],"string");y.wrapFunctionAsNode("string/split",function(c,h){null==h&&(h=this.properties.separator);if(null==c)return[];if(c.constructor===String)return c.split(h||" ");if(c.constructor===Array){for(var n=[],k=0;ke;++e){var h=this.getInputData(e);if(null!=h){var k=this.values[e];k.push(h);k.length>c[0]&&k.shift()}}}};c.prototype.onDrawBackground=function(e){if(!this.flags.collapsed){var h=this.size,k=0.5*h[1]/ +this.properties.scale,n=c.colors,q=0.5*h[1];e.fillStyle="#000";e.fillRect(0,0,h[0],h[1]);e.strokeStyle="#555";e.beginPath();e.moveTo(0,q);e.lineTo(h[0],q);e.stroke();if(this.inputs)for(var r=0;4>r;++r){var l=this.values[r];if(this.inputs[r]&&this.inputs[r].link){e.strokeStyle=n[r];e.beginPath();var a=l[0]*k*-1+q;e.moveTo(0,Math.clamp(a,0,h[1]));for(var b=1;be&&(e=0);if(0!=c.length){var h=[0,0,0];if(0==e)h=c[0];else if(1==e)h=c[c.length-1];else{var k= +(c.length-1)*e,e=c[Math.floor(k)],c=c[Math.floor(k)+1],k=k-Math.floor(k);h[0]=e[0]*(1-k)+c[0]*k;h[1]=e[1]*(1-k)+c[1]*k;h[2]=e[2]*(1-k)+c[2]*k}for(var q in h)h[q]/=255;this.boxcolor=colorToString(h);this.setOutputData(0,h)}};e.registerNodeType("color/palette",s);h.title="Frame";h.desc="Frame viewerew";h.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}];h.prototype.onDrawBackground=function(c){this.frame&&!this.flags.collapsed&&c.drawImage(this.frame, +0,0,this.size[0],this.size[1])};h.prototype.onExecute=function(){this.frame=this.getInputData(0);this.setDirtyCanvas(!0)};h.prototype.onWidget=function(c,e){if("resize"==e.name&&this.frame){var h=this.frame.width,k=this.frame.height;h||null==this.frame.videoWidth||(h=this.frame.videoWidth,k=this.frame.videoHeight);h&&k&&(this.size=[h,k]);this.setDirtyCanvas(!0,!0)}else"view"==e.name&&this.show()};h.prototype.show=function(){showElement&&this.frame&&showElement(this.frame)};e.registerNodeType("graphics/frame", +h);w.title="Image fade";w.desc="Fades between images";w.widgets=[{name:"resizeA",text:"Resize to A",type:"button"},{name:"resizeB",text:"Resize to B",type:"button"}];w.prototype.onAdded=function(){this.createCanvas();var c=this.canvas.getContext("2d");c.fillStyle="#000";c.fillRect(0,0,this.properties.width,this.properties.height)};w.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};w.prototype.onExecute= +function(){var c=this.canvas.getContext("2d");this.canvas.width=this.canvas.width;var e=this.getInputData(0);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);e=this.getInputData(2);null==e?e=this.properties.fade:this.properties.fade=e;c.globalAlpha=e;e=this.getInputData(1);null!=e&&c.drawImage(e,0,0,this.canvas.width,this.canvas.height);c.globalAlpha=1;this.setOutputData(0,this.canvas);this.setDirtyCanvas(!0)};e.registerNodeType("graphics/imagefade",w);k.title="Crop";k.desc="Crop Image"; +k.prototype.onAdded=function(){this.createCanvas()};k.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};k.prototype.onExecute=function(){var c=this.getInputData(0);c&&(c.width?(this.canvas.getContext("2d").drawImage(c,-this.properties.x,-this.properties.y,c.width*this.properties.scale,c.height*this.properties.scale),this.setOutputData(0,this.canvas)):this.setOutputData(0,null))};k.prototype.onDrawBackground= +function(c){this.flags.collapsed||this.canvas&&c.drawImage(this.canvas,0,0,this.canvas.width,this.canvas.height,0,0,this.size[0],this.size[1])};k.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"scale"==c?(this.properties[c]=parseFloat(e),0==this.properties[c]&&(console.error("Error in scale"),this.properties[c]=1)):this.properties[c]=parseInt(e);this.createCanvas();return!0};e.registerNodeType("graphics/cropImage",k);B.title="Canvas";B.desc="Canvas to render stuff";B.prototype.onExecute= +function(){var c=this.canvas,e=this.properties.width|0,h=this.properties.height|0;c.width!=e&&(c.width=e);c.height!=h&&(c.height=h);this.properties.autoclear&&this.ctx.clearRect(0,0,c.width,c.height);this.setOutputData(0,c)};B.prototype.onAction=function(c,e){"clear"==c&&this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)};e.registerNodeType("graphics/canvas",B);v.title="DrawImage";v.desc="Draws image into a canvas";v.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e= +this.getInputOrProperty("img");if(e){var h=this.getInputOrProperty("x"),k=this.getInputOrProperty("y");c.getContext("2d").drawImage(e,h,k)}}};e.registerNodeType("graphics/drawImage",v);F.title="DrawRectangle";F.desc="Draws rectangle in canvas";F.prototype.onExecute=function(){var c=this.getInputData(0);if(c){var e=this.getInputOrProperty("x"),h=this.getInputOrProperty("y"),k=this.getInputOrProperty("w"),q=this.getInputOrProperty("h");c.getContext("2d").fillRect(e,h,k,q)}};e.registerNodeType("graphics/drawRectangle", +F);x.title="Video";x.desc="Video playback";x.widgets=[{name:"play",text:"PLAY",type:"minibutton"},{name:"stop",text:"STOP",type:"minibutton"},{name:"demo",text:"Demo video",type:"button"},{name:"mute",text:"Mute video",type:"button"}];x.prototype.onExecute=function(){if(this.properties.url&&(this.properties.url!=this._video_url&&this.loadVideo(this.properties.url),this._video&&0!=this._video.width)){var c=this.getInputData(0);c&&0<=c&&1>=c&&(this._video.currentTime=c*this._video.duration,this._video.pause()); +this._video.dirty=!0;this.setOutputData(0,this._video);this.setOutputData(1,this._video.currentTime);this.setOutputData(2,this._video.duration);this.setDirtyCanvas(!0)}};x.prototype.onStart=function(){this.play()};x.prototype.onStop=function(){this.stop()};x.prototype.loadVideo=function(c){this._video_url=c;var h=c.substr(0,10).indexOf(":"),k="";-1!=h&&(k=c.substr(0,h));h="";k&&(h=c.substr(0,c.indexOf("/",k.length+3)),h=h.substr(k.length+3));this.properties.use_proxy&&k&&e.proxy&&h!=location.host&& +(c=e.proxy+c.substr(c.indexOf(":")+3));this._video=document.createElement("video");this._video.src=c;this._video.type="type=video/mp4";this._video.muted=!0;this._video.autoplay=!0;var n=this;this._video.addEventListener("loadedmetadata",function(c){console.log("Duration: "+this.duration+" seconds");console.log("Size: "+this.videoWidth+","+this.videoHeight);n.setDirtyCanvas(!0);this.width=this.videoWidth;this.height=this.videoHeight});this._video.addEventListener("progress",function(c){console.log("video loading...")}); this._video.addEventListener("error",function(c){console.error("Error loading video: "+this.src);if(this.error)switch(this.error.code){case this.error.MEDIA_ERR_ABORTED:console.error("You stopped the video.");break;case this.error.MEDIA_ERR_NETWORK:console.error("Network error - please try again later.");break;case this.error.MEDIA_ERR_DECODE:console.error("Video is broken..");break;case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED:console.error("Sorry, your browser can't play this video.")}});this._video.addEventListener("ended", -function(c){console.log("Video Ended.");this.play()})};G.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"url"==c&&""!=e&&this.loadVideo(e);return!0};G.prototype.play=function(){this._video&&this._video.videoWidth&&this._video.play()};G.prototype.playPause=function(){this._video&&(this._video.paused?this.play():this.pause())};G.prototype.stop=function(){this._video&&(this._video.pause(),this._video.currentTime=0)};G.prototype.pause=function(){this._video&&(console.log("Video paused"), -this._video.pause())};G.prototype.onWidget=function(c,e){};e.registerNodeType("graphics/video",G);E.title="Webcam";E.desc="Webcam image";E.is_webcam_open=!1;E.prototype.openStream=function(){function c(h){console.log("Webcam rejected",h);e._webcam_stream=!1;E.is_webcam_open=!1;e.boxcolor="red";e.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](c); -var e=this}};E.prototype.closeStream=function(){if(this._webcam_stream){var c=this._webcam_stream.getTracks();if(c.length)for(var e=0;e=this.size[1]||!this.properties.show|| -!this._video||(c.save(),c.drawImage(this._video,0,0,this.size[0],this.size[1]),c.restore())};E.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["stream_ready",e.EVENT],["stream_closed",e.EVENT],["stream_error",e.EVENT]]};e.registerNodeType("graphics/webcam",E)})(this); -(function(C){function c(){this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",filter:!0};this.size=[c.image_preview_size,c.image_preview_size]}function m(){this.addInput("Texture","Texture");this.properties={flipY:!1};this.size=[c.image_preview_size,c.image_preview_size]}function n(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",generate_mipmaps:!1}}function k(){this.addInput("Texture", +function(c){console.log("Video Ended.");this.play()})};x.prototype.onPropertyChanged=function(c,e){this.properties[c]=e;"url"==c&&""!=e&&this.loadVideo(e);return!0};x.prototype.play=function(){this._video&&this._video.videoWidth&&this._video.play()};x.prototype.playPause=function(){this._video&&(this._video.paused?this.play():this.pause())};x.prototype.stop=function(){this._video&&(this._video.pause(),this._video.currentTime=0)};x.prototype.pause=function(){this._video&&(console.log("Video paused"), +this._video.pause())};x.prototype.onWidget=function(c,e){};e.registerNodeType("graphics/video",x);C.title="Webcam";C.desc="Webcam image";C.is_webcam_open=!1;C.prototype.openStream=function(){function c(h){console.log("Webcam rejected",h);e._webcam_stream=!1;C.is_webcam_open=!1;e.boxcolor="red";e.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](c); +var e=this}};C.prototype.closeStream=function(){if(this._webcam_stream){var c=this._webcam_stream.getTracks();if(c.length)for(var e=0;e=this.size[1]||!this.properties.show|| +!this._video||(c.save(),c.drawImage(this._video,0,0,this.size[0],this.size[1]),c.restore())};C.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["stream_ready",e.EVENT],["stream_closed",e.EVENT],["stream_error",e.EVENT]]};e.registerNodeType("graphics/webcam",C)})(this); +(function(y){function c(){this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",filter:!0};this.size=[c.image_preview_size,c.image_preview_size]}function n(){this.addInput("Texture","Texture");this.properties={flipY:!1};this.size=[c.image_preview_size,c.image_preview_size]}function s(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",generate_mipmaps:!1}}function h(){this.addInput("Texture", "Texture");this.addInput("TextureB","Texture");this.addInput("value","number");this.addOutput("Texture","Texture");this.help="

pixelcode must be vec3, uvcode must be vec2, is optional

\t\t

uv: tex. coords

color: texture colorB: textureB

time: scene time value: input value

For multiline you must type: result = ...

";this.properties={value:1,pixelcode:"color + colorB * value",uvcode:"",precision:c.DEFAULT}; -this.has_error=!1}function y(){this.addOutput("out","Texture");this.properties={code:"",u_value:1,u_color:[1,1,1,1],width:512,height:512,precision:c.DEFAULT};this.properties.code=y.pixel_shader;this._uniforms={u_value:1,u_color:vec4.create(),in_texture:0,texSize:vec4.create(),time:0}}function h(){this.addInput("in","Texture");this.addInput("scale","vec2");this.addInput("offset","vec2");this.addOutput("out","Texture");this.properties={offset:vec2.fromValues(0,0),scale:vec2.fromValues(1,1),precision:c.DEFAULT}} -function D(){this.addInput("in","Texture");this.addInput("warp","Texture");this.addInput("factor","number");this.addOutput("out","Texture");this.properties={factor:0.01,scale:[1,1],offset:[0,0],precision:c.DEFAULT};this._uniforms={u_texture:0,u_textureB:1,u_factor:1,u_scale:vec2.create(),u_offset:vec2.create()}}function x(){this.addInput("Texture","Texture");this.properties={additive:!1,antialiasing:!1,filter:!0,disable_alpha:!1,gamma:1,viewport:[0,0,1,1]};this.size[0]=130}function B(){this.addInput("Texture", -"Texture");this.addOutput("","Texture");this.properties={size:0,generate_mipmaps:!1,precision:c.DEFAULT}}function G(){this.addInput("Texture","Texture");this.addOutput("","Texture");this.properties={iterations:1,generate_mipmaps:!1,precision:c.DEFAULT}}function E(){this.addInput("Texture","Texture");this.addOutput("","Texture");this.properties={size:[512,512],generate_mipmaps:!1,precision:c.DEFAULT}}function e(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("avg", -"vec4");this.addOutput("lum","number");this.properties={use_previous_frame:!0,high_quality:!1};this._uniforms={u_texture:0,u_mipmap_offset:0};this._luminance=new Float32Array(4)}function p(){this.addInput("Texture","Texture");this.addOutput("min_t","Texture");this.addOutput("max_t","Texture");this.addOutput("min","vec4");this.addOutput("max","vec4");this.properties={mode:"max",use_previous_frame:!0};this._uniforms={u_texture:0};this._max=new Float32Array(4);this._min=new Float32Array(4);this._textures_chain= -[]}function F(){this.addInput("in","Texture");this.addInput("factor","Number");this.addOutput("out","Texture");this.properties={factor:0.5};this._uniforms={u_texture:0,u_textureB:1,u_factor:this.properties.factor}}function v(){this.addInput("in","Texture");this.addOutput("avg","Texture");this.addOutput("array","Texture");this.properties={samples:64,frames_interval:1};this._uniforms={u_texture:0,u_textureB:1,u_samples:this.properties.samples,u_isamples:1/this.properties.samples};this.frame=0}function H(){this.addInput("Image", -"image");this.addOutput("","Texture");this.properties={}}function w(){this.addInput("Texture","Texture");this.addInput("LUT","Texture");this.addInput("Intensity","number");this.addOutput("","Texture");this.properties={enabled:!0,intensity:1,precision:c.DEFAULT,texture:null};w._shader||(w._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,w.pixel_shader))}function t(){this.addInput("Texture","Texture");this.addInput("Atlas","Texture");this.addOutput("","Texture");this.properties={enabled:!0,num_row_symbols:4, -symbol_size:16,brightness:1,colorize:!1,filter:!1,invert:!1,precision:c.DEFAULT,generate_mipmaps:!1,texture:null};t._shader||(t._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,t.pixel_shader));this._uniforms={u_texture:0,u_textureB:1,u_row_simbols:4,u_simbol_size:16,u_res:vec2.create()}}function l(){this.addInput("Texture","Texture");this.addOutput("R","Texture");this.addOutput("G","Texture");this.addOutput("B","Texture");this.addOutput("A","Texture");l._shader||(l._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER, +this.has_error=!1}function w(){this.addOutput("out","Texture");this.properties={code:"",u_value:1,u_color:[1,1,1,1],width:512,height:512,precision:c.DEFAULT};this.properties.code=w.pixel_shader;this._uniforms={u_value:1,u_color:vec4.create(),in_texture:0,texSize:vec4.create(),time:0}}function k(){this.addInput("in","Texture");this.addInput("scale","vec2");this.addInput("offset","vec2");this.addOutput("out","Texture");this.properties={offset:vec2.fromValues(0,0),scale:vec2.fromValues(1,1),precision:c.DEFAULT}} +function B(){this.addInput("in","Texture");this.addInput("warp","Texture");this.addInput("factor","number");this.addOutput("out","Texture");this.properties={factor:0.01,scale:[1,1],offset:[0,0],precision:c.DEFAULT};this._uniforms={u_texture:0,u_textureB:1,u_factor:1,u_scale:vec2.create(),u_offset:vec2.create()}}function v(){this.addInput("Texture","Texture");this.properties={additive:!1,antialiasing:!1,filter:!0,disable_alpha:!1,gamma:1,viewport:[0,0,1,1]};this.size[0]=130}function F(){this.addInput("Texture", +"Texture");this.addOutput("","Texture");this.properties={size:0,generate_mipmaps:!1,precision:c.DEFAULT}}function x(){this.addInput("Texture","Texture");this.addOutput("","Texture");this.properties={iterations:1,generate_mipmaps:!1,precision:c.DEFAULT}}function C(){this.addInput("Texture","Texture");this.addOutput("","Texture");this.properties={size:[512,512],generate_mipmaps:!1,precision:c.DEFAULT}}function e(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("avg", +"vec4");this.addOutput("lum","number");this.properties={use_previous_frame:!0,high_quality:!1};this._uniforms={u_texture:0,u_mipmap_offset:0};this._luminance=new Float32Array(4)}function m(){this.addInput("Texture","Texture");this.addOutput("min_t","Texture");this.addOutput("max_t","Texture");this.addOutput("min","vec4");this.addOutput("max","vec4");this.properties={mode:"max",use_previous_frame:!0};this._uniforms={u_texture:0};this._max=new Float32Array(4);this._min=new Float32Array(4);this._textures_chain= +[]}function D(){this.addInput("in","Texture");this.addInput("factor","Number");this.addOutput("out","Texture");this.properties={factor:0.5};this._uniforms={u_texture:0,u_textureB:1,u_factor:this.properties.factor}}function u(){this.addInput("in","Texture");this.addOutput("avg","Texture");this.addOutput("array","Texture");this.properties={samples:64,frames_interval:1};this._uniforms={u_texture:0,u_textureB:1,u_samples:this.properties.samples,u_isamples:1/this.properties.samples};this.frame=0}function I(){this.addInput("Image", +"image");this.addOutput("","Texture");this.properties={}}function q(){this.addInput("Texture","Texture");this.addInput("LUT","Texture");this.addInput("Intensity","number");this.addOutput("","Texture");this.properties={enabled:!0,intensity:1,precision:c.DEFAULT,texture:null};q._shader||(q._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,q.pixel_shader))}function r(){this.addInput("Texture","Texture");this.addInput("Atlas","Texture");this.addOutput("","Texture");this.properties={enabled:!0,num_row_symbols:4, +symbol_size:16,brightness:1,colorize:!1,filter:!1,invert:!1,precision:c.DEFAULT,generate_mipmaps:!1,texture:null};r._shader||(r._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,r.pixel_shader));this._uniforms={u_texture:0,u_textureB:1,u_row_simbols:4,u_simbol_size:16,u_res:vec2.create()}}function l(){this.addInput("Texture","Texture");this.addOutput("R","Texture");this.addOutput("G","Texture");this.addOutput("B","Texture");this.addOutput("A","Texture");l._shader||(l._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER, l.pixel_shader))}function a(){this.addInput("R","Texture");this.addInput("G","Texture");this.addInput("B","Texture");this.addInput("A","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,R:1,G:1,B:1,A:1};this._color=vec4.create();this._uniforms={u_textureR:0,u_textureG:1,u_textureB:2,u_textureA:3,u_color:this._color}}function b(){this.addOutput("Texture","Texture");this._tex_color=vec4.create();this.properties={color:vec4.create(),precision:c.DEFAULT}}function d(){this.addInput("A", "color");this.addInput("B","color");this.addOutput("Texture","Texture");this.properties={angle:0,scale:1,A:[0,0,0],B:[1,1,1],texture_size:32};d._shader||(d._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,d.pixel_shader));this._uniforms={u_angle:0,u_colorA:vec3.create(),u_colorB:vec3.create()}}function f(){this.addInput("A","Texture");this.addInput("B","Texture");this.addInput("Mixer","Texture");this.addOutput("Texture","Texture");this.properties={factor:0.5,size_from_biggest:!0,invert:!1,precision:c.DEFAULT}; -this._uniforms={u_textureA:0,u_textureB:1,u_textureMix:2,u_mix:vec4.create()}}function g(){this.addInput("Tex.","Texture");this.addOutput("Edges","Texture");this.properties={invert:!0,threshold:!1,factor:1,precision:c.DEFAULT};g._shader||(g._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,g.pixel_shader))}function q(){this.addInput("Texture","Texture");this.addInput("Distance","number");this.addInput("Range","number");this.addOutput("Texture","Texture");this.properties={distance:100,range:50,only_depth:!1, -high_precision:!1};this._uniforms={u_texture:0,u_distance:100,u_range:50,u_camera_planes:null}}function A(){this.addInput("Texture","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,invert:!1};this._uniforms={u_texture:0,u_camera_planes:null,u_ires:vec2.create()}}function K(){this.addInput("Texture","Texture");this.addInput("Iterations","number");this.addInput("Intensity","number");this.addOutput("Blurred","Texture");this.properties={intensity:1,iterations:1,preserve_aspect:!1, -scale:[1,1],precision:c.DEFAULT}}function r(){this.addInput("in","Texture");this.addInput("dirt","Texture");this.addOutput("out","Texture");this.addOutput("glow","Texture");this.properties={enabled:!0,intensity:1,persistence:0.99,iterations:16,threshold:0,scale:1,dirt_factor:0.5,precision:c.DEFAULT};this._textures=[];this._uniforms={u_intensity:1,u_texture:0,u_glow_texture:1,u_threshold:0,u_texel_size:vec2.create()}}function s(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture"); -this.properties={intensity:1,radius:5}}function z(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={sigma:1.4,k:1.6,p:21.7,epsilon:79,phi:0.017}}function u(){this.addOutput("Webcam","Texture");this.properties={texture_name:"",facingMode:"user"};this.boxcolor="black";this.version=0}function N(){this.addInput("in","Texture");this.addInput("f","number");this.addOutput("out","Texture");this.properties={enabled:!0,factor:1,precision:c.LOW};this._uniforms={u_texture:0, -u_factor:1}}function R(){this.addInput("in","");this.properties={precision:c.LOW,width:0,height:0,channels:1};this.addOutput("out","Texture")}function J(){this.addInput("in","Texture");this.addOutput("out","Texture");this.properties={precision:c.LOW,split_channels:!1};this._values=new Uint8Array(1024);this._values.fill(255);this._curve_texture=null;this._uniforms={u_texture:0,u_curve:1,u_range:1};this._must_update=!0;this._points={RGB:[[0,0],[1,1]],R:[[0,0],[1,1]],G:[[0,0],[1,1]],B:[[0,0],[1,1]]}; -this.curve_editor=null;this.addWidget("toggle","Split Channels",!1,"split_channels");this.addWidget("combo","Channel","RGB",{values:["RGB","R","G","B"]});this.curve_offset=68;this.size=[240,160]}function P(){this.addInput("in","Texture");this.addInput("exp","number");this.addOutput("out","Texture");this.properties={exposition:1,precision:c.LOW};this._uniforms={u_texture:0,u_exposition:1}}function L(){this.addInput("in","Texture");this.addInput("avg","number,Texture");this.addOutput("out","Texture"); -this.properties={enabled:!0,scale:1,gamma:1,average_lum:1,lum_white:1,precision:c.LOW};this._uniforms={u_texture:0,u_lumwhite2:1,u_igamma:1,u_scale:1,u_average_lum:1}}function O(){this.addOutput("out","Texture");this.properties={width:512,height:512,seed:0,persistence:0.1,octaves:8,scale:1,offset:[0,0],amplitude:1,precision:c.DEFAULT};this._key=0;this._texture=null;this._uniforms={u_persistence:0.1,u_seed:0,u_offset:vec2.create(),u_scale:1,u_viewport:vec2.create()}}function M(){this.addInput("v"); -this.addOutput("out","Texture");this.properties={code:M.default_code,width:512,height:512,clear:!0,precision:c.DEFAULT,use_html_canvas:!1};this._temp_texture=this._func=null;this.compileCode()}function Q(){this.addInput("in","Texture");this.addOutput("out","Texture");this.properties={key_color:vec3.fromValues(0,1,0),threshold:0.8,slope:0.2,precision:c.DEFAULT}}function S(){this.addInput("in","texture");this.addInput("yaw","number");this.addOutput("out","texture");this.properties={yaw:0}}var I=C.LiteGraph, -U=C.LGraphCanvas;C.LGraphTexture=null;"undefined"!=typeof GL&&(U.link_type_colors.Texture="#987",C.LGraphTexture=c,c.title="Texture",c.desc="Texture",c.widgets_info={name:{widget:"texture"},filter:{widget:"checkbox"}},c.loadTextureCallback=null,c.image_preview_size=256,c.UNDEFINED=0,c.PASS_THROUGH=1,c.COPY=2,c.LOW=3,c.HIGH=4,c.REUSE=5,c.DEFAULT=2,c.MODE_VALUES={undefined:c.UNDEFINED,"pass through":c.PASS_THROUGH,copy:c.COPY,low:c.LOW,high:c.HIGH,reuse:c.REUSE,"default":c.DEFAULT},c.getTexturesContainer= -function(){return gl.textures},c.loadTexture=function(a,b){b=b||{};var d=a;"http://"==d.substr(0,7)&&I.proxy&&(d=I.proxy+d.substr(7));return c.getTexturesContainer()[a]=GL.Texture.fromURL(d,b)},c.getTexture=function(a){var b=this.getTexturesContainer();if(!b)throw"Cannot load texture, container of textures not found";b=b[a];return!b&&a&&":"!=a[0]?this.loadTexture(a):b},c.getTargetTexture=function(a,b,d){if(!a)throw"LGraphTexture.getTargetTexture expects a reference texture";var f=null;switch(d){case c.LOW:f= -gl.UNSIGNED_BYTE;break;case c.HIGH:f=gl.HIGH_PRECISION_FORMAT;break;case c.REUSE:return a;default:f=a?a.type:gl.UNSIGNED_BYTE}b&&b.width==a.width&&b.height==a.height&&b.type==f||(b=new GL.Texture(a.width,a.height,{type:f,format:gl.RGBA,filter:gl.LINEAR}));return b},c.getTextureType=function(a,b){var d=b?b.type:gl.UNSIGNED_BYTE;switch(a){case c.HIGH:d=gl.HIGH_PRECISION_FORMAT;break;case c.LOW:d=gl.UNSIGNED_BYTE}return d},c.getWhiteTexture=function(){return this._white_texture?this._white_texture:this._white_texture= -GL.Texture.fromMemory(1,1,[255,255,255,255],{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.getNoiseTexture=function(){if(this._noise_texture)return this._noise_texture;for(var a=new Uint8Array(1048576),b=0;1048576>b;++b)a[b]=255*Math.random();return this._noise_texture=a=GL.Texture.fromMemory(512,512,a,{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.prototype.onDropFile=function(a,b,d){if(a){var c=null;"string"==typeof a?c=GL.Texture.fromURL(a):-1!=b.toLowerCase().indexOf(".dds")?c=GL.Texture.fromDDSInMemory(a): -(a=new Blob([d]),a=URL.createObjectURL(a),c=GL.Texture.fromURL(a));this._drop_texture=c;this.properties.name=b}else this._drop_texture=null,this.properties.name=""},c.prototype.getExtraMenuOptions=function(a){var b=this;if(this._drop_texture)return[{content:"Clear",callback:function(){b._drop_texture=null;b.properties.name=""}}]},c.prototype.onExecute=function(){var a=null;this.isOutputConnected(1)&&(a=this.getInputData(0));!a&&this._drop_texture&&(a=this._drop_texture);!a&&this.properties.name&& -(a=c.getTexture(this.properties.name));if(a){this._last_tex=a;!1===this.properties.filter?a.setParameter(gl.TEXTURE_MAG_FILTER,gl.NEAREST):a.setParameter(gl.TEXTURE_MAG_FILTER,gl.LINEAR);this.setOutputData(0,a);this.setOutputData(1,a.fullpath||a.filename);for(var b=2;b=this.size[1]))if(this._drop_texture&&a.webgl)a.drawImage(this._drop_texture,0,0,this.size[0],this.size[1]);else{if(this._last_preview_tex!=this._last_tex)if(a.webgl)this._canvas=this._last_tex;else{var b=c.generateLowResTexturePreview(this._last_tex);if(!b)return;this._last_preview_tex=this._last_tex;this._canvas=cloneCanvas(b)}this._canvas&& -(a.save(),a.webgl||(a.translate(0,this.size[1]),a.scale(1,-1)),a.drawImage(this._canvas,0,0,this.size[0],this.size[1]),a.restore())}},c.generateLowResTexturePreview=function(a){if(!a)return null;var b=c.image_preview_size,d=a;if(a.format==gl.DEPTH_COMPONENT)return null;if(a.width>b||a.height>b)d=this._preview_temp_tex,this._preview_temp_tex||(this._preview_temp_tex=d=new GL.Texture(b,b,{minFilter:gl.NEAREST})),a.copyTo(d);a=this._preview_canvas;a||(this._preview_canvas=a=createCanvas(b,b));d&&d.toCanvas(a); -return a},c.prototype.getResources=function(a){this.properties.name&&(a[this.properties.name]=GL.Texture);return a},c.prototype.onGetInputs=function(){return[["in","Texture"]]},c.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["aspect","number"]]},c.replaceCode=function(a,b){return a.replace(/\{\{[a-zA-Z0-9_]*\}\}/g,function(a){a=a.replace(/[\{\}]/g,"");return b[a]||""})},I.registerNodeType("texture/texture",c),m.title="Preview",m.desc="Show a texture in the graph canvas", -m.allow_preview=!1,m.prototype.onDrawBackground=function(a){if(!this.flags.collapsed&&(a.webgl||m.allow_preview)){var b=this.getInputData(0);if(b){var d=null,d=!b.handle&&a.webgl?b:c.generateLowResTexturePreview(b);a.save();this.properties.flipY&&(a.translate(0,this.size[1]),a.scale(1,-1));a.drawImage(d,0,0,this.size[0],this.size[1]);a.restore()}}},I.registerNodeType("texture/preview",m),n.title="Save",n.desc="Save a texture in the repository",n.prototype.getPreviewTexture=function(){return this._texture}, -n.prototype.onExecute=function(){var a=this.getInputData(0);a&&(this.properties.generate_mipmaps&&(a.bind(0),a.setParameter(gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_LINEAR),gl.generateMipmap(a.texture_type),a.unbind(0)),this.properties.name&&(c.storeTexture?c.storeTexture(this.properties.name,a):c.getTexturesContainer()[this.properties.name]=a),this._texture=a,this.setOutputData(0,a),this.setOutputData(1,this.properties.name))},I.registerNodeType("texture/save",n),k.widgets_info={uvcode:{widget:"code"}, -pixelcode:{widget:"code"},precision:{widget:"combo",values:c.MODE_VALUES}},k.title="Operation",k.desc="Texture shader operation",k.presets={},k.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:b.properties.show?"Hide Texture":"Show Texture",callback:function(){b.properties.show=!b.properties.show}}]},k.prototype.onPropertyChanged=function(){this.has_error=!1},k.prototype.onDrawBackground=function(a){this.flags.collapsed||20>=this.size[1]||!this.properties.show||!this._tex||this._tex.gl!= -a||(a.save(),a.drawImage(this._tex,0,0,this.size[0],this.size[1]),a.restore())},k.prototype.onExecute=function(){var a=this.getInputData(0);if(this.isOutputConnected(0))if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else{var b=this.getInputData(1);if(this.properties.uvcode||this.properties.pixelcode){var d=512,f=512;a?(d=a.width,f=a.height):b&&(d=b.width,f=b.height);b||(b=GL.Texture.getWhiteTexture());var g=c.getTextureType(this.properties.precision,a);this._tex=a||this._tex? -c.getTargetTexture(a||this._tex,this._tex,this.properties.precision):new GL.Texture(d,f,{type:g,format:gl.RGBA,filter:gl.LINEAR});g="";this.properties.uvcode&&(g="uv = "+this.properties.uvcode,-1!=this.properties.uvcode.indexOf(";")&&(g=this.properties.uvcode));var e="";this.properties.pixelcode&&(e="result = "+this.properties.pixelcode,-1!=this.properties.pixelcode.indexOf(";")&&(e=this.properties.pixelcode));var h=this._shader;if(!(this.has_error||h&&this._shader_code==g+"|"+e)){var l=c.replaceCode(k.pixel_shader, -{UV_CODE:g,PIXEL_CODE:e});try{h=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,l),this.boxcolor="#00FF00"}catch(r){GL.Shader.dumpErrorToConsole(r,Shader.SCREEN_VERTEX_SHADER,l);this.boxcolor="#FF0000";this.has_error=!0;return}this._shader=h;this._shader_code=g+"|"+e}if(this._shader){var s=this.getInputData(2);null!=s?this.properties.value=s:s=parseFloat(this.properties.value);var w=this.graph.getTime();this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);gl.disable(gl.BLEND); -a&&a.bind(0);b&&b.bind(1);var c=Mesh.getScreenQuad();h.uniforms({u_texture:0,u_textureB:1,value:s,texSize:[d,f,1/d,1/f],time:w}).draw(c)});this.setOutputData(0,this._tex)}}}},k.pixel_shader="precision highp float;\n\t\t\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec4 texSize;\n\t\tuniform float time;\n\t\tuniform float value;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord;\n\t\t\t{{UV_CODE}};\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\tvec3 color = color4.rgb;\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\tvec3 colorB = color4B.rgb;\n\t\t\tvec3 result = color;\n\t\t\tfloat alpha = 1.0;\n\t\t\t{{PIXEL_CODE}};\n\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t}\n\t\t", -k.registerPreset=function(a,b){k.presets[a]=b},k.registerPreset("",""),k.registerPreset("bypass","color"),k.registerPreset("add","color + colorB * value"),k.registerPreset("substract","(color - colorB) * value"),k.registerPreset("mate","mix( color, colorB, color4B.a * value)"),k.registerPreset("invert","vec3(1.0) - color"),k.registerPreset("multiply","color * colorB * value"),k.registerPreset("divide","(color / colorB) / value"),k.registerPreset("difference","abs(color - colorB) * value"),k.registerPreset("max", -"max(color, colorB) * value"),k.registerPreset("min","min(color, colorB) * value"),k.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"),k.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"),k.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"),k.registerPreset("normalmap","\n\t\tfloat z0 = texture2D(u_texture, uv + vec2(-texSize.z, -texSize.w) ).x;\n\t\tfloat z1 = texture2D(u_texture, uv + vec2(0.0, -texSize.w) ).x;\n\t\tfloat z2 = texture2D(u_texture, uv + vec2(texSize.z, -texSize.w) ).x;\n\t\tfloat z3 = texture2D(u_texture, uv + vec2(-texSize.z, 0.0) ).x;\n\t\tfloat z4 = color.x;\n\t\tfloat z5 = texture2D(u_texture, uv + vec2(texSize.z, 0.0) ).x;\n\t\tfloat z6 = texture2D(u_texture, uv + vec2(-texSize.z, texSize.w) ).x;\n\t\tfloat z7 = texture2D(u_texture, uv + vec2(0.0, texSize.w) ).x;\n\t\tfloat z8 = texture2D(u_texture, uv + vec2(texSize.z, texSize.w) ).x;\n\t\tvec3 normal = vec3( z2 + 2.0*z4 + z7 - z0 - 2.0*z3 - z5, z5 + 2.0*z6 + z7 -z0 - 2.0*z1 - z2, 1.0 );\n\t\tnormal.xy *= value;\n\t\tresult.xyz = normalize(normal) * 0.5 + vec3(0.5);\n\t"), -k.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"),k.prototype.onInspect=function(a){var b=this;a.addCombo("Presets","",{values:Object.keys(k.presets),callback:function(d){var c=k.presets[d];c&&(b.setProperty("pixelcode",c),b.title=d,a.refresh())}})},I.registerNodeType("texture/operation",k),y.title="Shader",y.desc="Texture shader",y.widgets_info={code:{type:"code",lang:"glsl"},precision:{widget:"combo", -values:c.MODE_VALUES}},y.prototype.onPropertyChanged=function(a,b){if("code"==a){var d=this.getShader();if(d){var c=d.uniformInfo;if(this.inputs)for(var f={},g=0;g=this.size[1])){var b=this.getInputData(0);b&&a.drawImage(a==gl?b:gl.canvas,10,30,this.size[0]-20,this.size[1]-40)}},x.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this.properties.disable_alpha?gl.disable(gl.BLEND):(gl.enable(gl.BLEND),this.properties.additive?gl.blendFunc(gl.SRC_ALPHA, -gl.ONE):gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA));gl.disable(gl.DEPTH_TEST);var b=this.properties.gamma||1;this.isInputConnected(1)&&(b=this.getInputData(1));a.setParameter(gl.TEXTURE_MAG_FILTER,this.properties.filter?gl.LINEAR:gl.NEAREST);var d=x._prev_viewport;d.set(gl.viewport_data);var c=this.properties.viewport;gl.viewport(d[0]+d[2]*c[0],d[1]+d[3]*c[1],d[2]*c[2],d[3]*c[3]);gl.getViewport();this.properties.antialiasing?(x._shader||(x._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, -x.aa_pixel_shader)),c=Mesh.getScreenQuad(),a.bind(0),x._shader.uniforms({u_texture:0,uViewportSize:[a.width,a.height],u_igamma:1/b,inverseVP:[1/a.width,1/a.height]}).draw(c)):1!=b?(x._gamma_shader||(x._gamma_shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,x.gamma_pixel_shader)),a.toViewport(x._gamma_shader,{u_texture:0,u_igamma:1/b})):a.toViewport();gl.viewport(d[0],d[1],d[2],d[3])}},x.prototype.onGetInputs=function(){return[["gamma","number"]]},x.aa_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 uViewportSize;\n\t\tuniform vec2 inverseVP;\n\t\tuniform float u_igamma;\n\t\t#define FXAA_REDUCE_MIN (1.0/ 128.0)\n\t\t#define FXAA_REDUCE_MUL (1.0 / 8.0)\n\t\t#define FXAA_SPAN_MAX 8.0\n\t\t\n\t\t/* from mitsuhiko/webgl-meincraft based on the code on geeks3d.com */\n\t\tvec4 applyFXAA(sampler2D tex, vec2 fragCoord)\n\t\t{\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t/*vec2 inverseVP = vec2(1.0 / uViewportSize.x, 1.0 / uViewportSize.y);*/\n\t\t\tvec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbM = texture2D(tex, fragCoord * inverseVP).xyz;\n\t\t\tvec3 luma = vec3(0.299, 0.587, 0.114);\n\t\t\tfloat lumaNW = dot(rgbNW, luma);\n\t\t\tfloat lumaNE = dot(rgbNE, luma);\n\t\t\tfloat lumaSW = dot(rgbSW, luma);\n\t\t\tfloat lumaSE = dot(rgbSE, luma);\n\t\t\tfloat lumaM = dot(rgbM, luma);\n\t\t\tfloat lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n\t\t\tfloat lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\t\t\t\n\t\t\tvec2 dir;\n\t\t\tdir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n\t\t\tdir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\t\t\t\n\t\t\tfloat dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\t\t\t\n\t\t\tfloat rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n\t\t\tdir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * inverseVP;\n\t\t\t\n\t\t\tvec3 rgbA = 0.5 * (texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + \n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz);\n\t\t\tvec3 rgbB = rgbA * 0.5 + 0.25 * (texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + \n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz);\n\t\t\t\n\t\t\t//return vec4(rgbA,1.0);\n\t\t\tfloat lumaB = dot(rgbB, luma);\n\t\t\tif ((lumaB < lumaMin) || (lumaB > lumaMax))\n\t\t\t\tcolor = vec4(rgbA, 1.0);\n\t\t\telse\n\t\t\t\tcolor = vec4(rgbB, 1.0);\n\t\t\tif(u_igamma != 1.0)\n\t\t\t\tcolor.xyz = pow( color.xyz, vec3(u_igamma) );\n\t\t\treturn color;\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\t\t}\n\t\t", -x.gamma_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_igamma;\n\t\tvoid main() {\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\t\t gl_FragColor = color;\n\t\t}\n\t\t",I.registerNodeType("texture/toviewport",x),B.title="Copy",B.desc="Copy Texture",B.widgets_info={size:{widget:"combo",values:[0,32,64,128,256,512,1024,2048]},precision:{widget:"combo", -values:c.MODE_VALUES}},B.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)){if(a){var b=a.width,d=a.height;0!=this.properties.size&&(d=b=this.properties.size);var f=this._temp_texture,g=a.type;this.properties.precision===c.LOW?g=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(g=gl.HIGH_PRECISION_FORMAT);f&&f.width==b&&f.height==d&&f.type==g||(f=gl.LINEAR,this.properties.generate_mipmaps&&isPowerOfTwo(b)&&isPowerOfTwo(d)&&(f=gl.LINEAR_MIPMAP_LINEAR), -this._temp_texture=new GL.Texture(b,d,{type:g,format:gl.RGBA,minFilter:f,magFilter:gl.LINEAR}));a.copyTo(this._temp_texture);this.properties.generate_mipmaps&&(this._temp_texture.bind(0),gl.generateMipmap(this._temp_texture.texture_type),this._temp_texture.unbind(0))}this.setOutputData(0,this._temp_texture)}},I.registerNodeType("texture/copy",B),G.title="Downsample",G.desc="Downsample Texture",G.widgets_info={iterations:{type:"number",step:1,precision:0,min:0},precision:{widget:"combo",values:c.MODE_VALUES}}, -G.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)&&a&&a.texture_type===GL.TEXTURE_2D)if(1>this.properties.iterations)this.setOutputData(0,a);else{var b=G._shader;b||(G._shader=b=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,G.pixel_shader));var d=a.width|0,f=a.height|0,g=a.type;this.properties.precision===c.LOW?g=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(g=gl.HIGH_PRECISION_FORMAT);var e=this.properties.iterations||1,h=a,l= -null,r=[],a={type:g,format:a.format},g=vec2.create(),k={u_offset:g};this._texture&&GL.Texture.releaseTemporary(this._texture);for(var s=0;s>1||0;f=f>>1||0;l=GL.Texture.getTemporary(d,f,a);r.push(l);h.setParameter(GL.TEXTURE_MAG_FILTER,GL.NEAREST);h.copyTo(l,b,k);if(1==d&&1==f)break;h=l}this._texture=r.pop();for(s=0;sb;++b)a[b]=255*Math.random();return this._noise_texture=a=GL.Texture.fromMemory(512,512,a,{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.prototype.onDropFile=function(a,b,d){if(a){var c=null;"string"==typeof a?c=GL.Texture.fromURL(a): +-1!=b.toLowerCase().indexOf(".dds")?c=GL.Texture.fromDDSInMemory(a):(a=new Blob([d]),a=URL.createObjectURL(a),c=GL.Texture.fromURL(a));this._drop_texture=c;this.properties.name=b}else this._drop_texture=null,this.properties.name=""},c.prototype.getExtraMenuOptions=function(a){var b=this;if(this._drop_texture)return[{content:"Clear",callback:function(){b._drop_texture=null;b.properties.name=""}}]},c.prototype.onExecute=function(){var a=null;this.isOutputConnected(1)&&(a=this.getInputData(0));!a&&this._drop_texture&& +(a=this._drop_texture);!a&&this.properties.name&&(a=c.getTexture(this.properties.name));if(a){this._last_tex=a;!1===this.properties.filter?a.setParameter(gl.TEXTURE_MAG_FILTER,gl.NEAREST):a.setParameter(gl.TEXTURE_MAG_FILTER,gl.LINEAR);this.setOutputData(0,a);this.setOutputData(1,a.fullpath||a.filename);for(var b=2;b=this.size[1]))if(this._drop_texture&&a.webgl)a.drawImage(this._drop_texture,0,0,this.size[0],this.size[1]);else{if(this._last_preview_tex!=this._last_tex)if(a.webgl)this._canvas=this._last_tex;else{var b=c.generateLowResTexturePreview(this._last_tex);if(!b)return;this._last_preview_tex=this._last_tex; +this._canvas=cloneCanvas(b)}this._canvas&&(a.save(),a.webgl||(a.translate(0,this.size[1]),a.scale(1,-1)),a.drawImage(this._canvas,0,0,this.size[0],this.size[1]),a.restore())}},c.generateLowResTexturePreview=function(a){if(!a)return null;var b=c.image_preview_size,d=a;if(a.format==gl.DEPTH_COMPONENT)return null;if(a.width>b||a.height>b)d=this._preview_temp_tex,this._preview_temp_tex||(this._preview_temp_tex=d=new GL.Texture(b,b,{minFilter:gl.NEAREST})),a.copyTo(d);a=this._preview_canvas;a||(this._preview_canvas= +a=createCanvas(b,b));d&&d.toCanvas(a);return a},c.prototype.getResources=function(a){this.properties.name&&(a[this.properties.name]=GL.Texture);return a},c.prototype.onGetInputs=function(){return[["in","Texture"]]},c.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["aspect","number"]]},c.replaceCode=function(a,b){return a.replace(/\{\{[a-zA-Z0-9_]*\}\}/g,function(a){a=a.replace(/[\{\}]/g,"");return b[a]||""})},J.registerNodeType("texture/texture",c),n.title="Preview", +n.desc="Show a texture in the graph canvas",n.allow_preview=!1,n.prototype.onDrawBackground=function(a){if(!this.flags.collapsed&&(a.webgl||n.allow_preview)){var b=this.getInputData(0);if(b){var d=null,d=!b.handle&&a.webgl?b:c.generateLowResTexturePreview(b);a.save();this.properties.flipY&&(a.translate(0,this.size[1]),a.scale(1,-1));a.drawImage(d,0,0,this.size[0],this.size[1]);a.restore()}}},J.registerNodeType("texture/preview",n),s.title="Save",s.desc="Save a texture in the repository",s.prototype.getPreviewTexture= +function(){return this._texture},s.prototype.onExecute=function(){var a=this.getInputData(0);a&&(this.properties.generate_mipmaps&&(a.bind(0),a.setParameter(gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_LINEAR),gl.generateMipmap(a.texture_type),a.unbind(0)),this.properties.name&&(c.storeTexture?c.storeTexture(this.properties.name,a):c.getTexturesContainer()[this.properties.name]=a),this._texture=a,this.setOutputData(0,a),this.setOutputData(1,this.properties.name))},J.registerNodeType("texture/save",s),h.widgets_info= +{uvcode:{widget:"code"},pixelcode:{widget:"code"},precision:{widget:"combo",values:c.MODE_VALUES}},h.title="Operation",h.desc="Texture shader operation",h.presets={},h.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:b.properties.show?"Hide Texture":"Show Texture",callback:function(){b.properties.show=!b.properties.show}}]},h.prototype.onPropertyChanged=function(){this.has_error=!1},h.prototype.onDrawBackground=function(a){this.flags.collapsed||20>=this.size[1]||!this.properties.show|| +!this._tex||this._tex.gl!=a||(a.save(),a.drawImage(this._tex,0,0,this.size[0],this.size[1]),a.restore())},h.prototype.onExecute=function(){var a=this.getInputData(0);if(this.isOutputConnected(0))if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else{var b=this.getInputData(1);if(this.properties.uvcode||this.properties.pixelcode){var d=512,f=512;a?(d=a.width,f=a.height):b&&(d=b.width,f=b.height);b||(b=GL.Texture.getWhiteTexture());var g=c.getTextureType(this.properties.precision, +a);this._tex=a||this._tex?c.getTargetTexture(a||this._tex,this._tex,this.properties.precision):new GL.Texture(d,f,{type:g,format:gl.RGBA,filter:gl.LINEAR});g="";this.properties.uvcode&&(g="uv = "+this.properties.uvcode,-1!=this.properties.uvcode.indexOf(";")&&(g=this.properties.uvcode));var e="";this.properties.pixelcode&&(e="result = "+this.properties.pixelcode,-1!=this.properties.pixelcode.indexOf(";")&&(e=this.properties.pixelcode));var l=this._shader;if(!(this.has_error||l&&this._shader_code== +g+"|"+e)){var k=c.replaceCode(h.pixel_shader,{UV_CODE:g,PIXEL_CODE:e});try{l=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,k),this.boxcolor="#00FF00"}catch(q){GL.Shader.dumpErrorToConsole(q,Shader.SCREEN_VERTEX_SHADER,k);this.boxcolor="#FF0000";this.has_error=!0;return}this._shader=l;this._shader_code=g+"|"+e}if(this._shader){var r=this.getInputData(2);null!=r?this.properties.value=r:r=parseFloat(this.properties.value);var m=this.graph.getTime();this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE); +gl.disable(gl.BLEND);a&&a.bind(0);b&&b.bind(1);var c=Mesh.getScreenQuad();l.uniforms({u_texture:0,u_textureB:1,value:r,texSize:[d,f,1/d,1/f],time:m}).draw(c)});this.setOutputData(0,this._tex)}}}},h.pixel_shader="precision highp float;\n\t\t\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec4 texSize;\n\t\tuniform float time;\n\t\tuniform float value;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord;\n\t\t\t{{UV_CODE}};\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\tvec3 color = color4.rgb;\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\tvec3 colorB = color4B.rgb;\n\t\t\tvec3 result = color;\n\t\t\tfloat alpha = 1.0;\n\t\t\t{{PIXEL_CODE}};\n\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t}\n\t\t", +h.registerPreset=function(a,b){h.presets[a]=b},h.registerPreset("",""),h.registerPreset("bypass","color"),h.registerPreset("add","color + colorB * value"),h.registerPreset("substract","(color - colorB) * value"),h.registerPreset("mate","mix( color, colorB, color4B.a * value)"),h.registerPreset("invert","vec3(1.0) - color"),h.registerPreset("multiply","color * colorB * value"),h.registerPreset("divide","(color / colorB) / value"),h.registerPreset("difference","abs(color - colorB) * value"),h.registerPreset("max", +"max(color, colorB) * value"),h.registerPreset("min","min(color, colorB) * value"),h.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"),h.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"),h.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"),h.registerPreset("normalmap","\n\t\tfloat z0 = texture2D(u_texture, uv + vec2(-texSize.z, -texSize.w) ).x;\n\t\tfloat z1 = texture2D(u_texture, uv + vec2(0.0, -texSize.w) ).x;\n\t\tfloat z2 = texture2D(u_texture, uv + vec2(texSize.z, -texSize.w) ).x;\n\t\tfloat z3 = texture2D(u_texture, uv + vec2(-texSize.z, 0.0) ).x;\n\t\tfloat z4 = color.x;\n\t\tfloat z5 = texture2D(u_texture, uv + vec2(texSize.z, 0.0) ).x;\n\t\tfloat z6 = texture2D(u_texture, uv + vec2(-texSize.z, texSize.w) ).x;\n\t\tfloat z7 = texture2D(u_texture, uv + vec2(0.0, texSize.w) ).x;\n\t\tfloat z8 = texture2D(u_texture, uv + vec2(texSize.z, texSize.w) ).x;\n\t\tvec3 normal = vec3( z2 + 2.0*z4 + z7 - z0 - 2.0*z3 - z5, z5 + 2.0*z6 + z7 -z0 - 2.0*z1 - z2, 1.0 );\n\t\tnormal.xy *= value;\n\t\tresult.xyz = normalize(normal) * 0.5 + vec3(0.5);\n\t"), +h.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"),h.prototype.onInspect=function(a){var b=this;a.addCombo("Presets","",{values:Object.keys(h.presets),callback:function(d){var c=h.presets[d];c&&(b.setProperty("pixelcode",c),b.title=d,a.refresh())}})},J.registerNodeType("texture/operation",h),w.title="Shader",w.desc="Texture shader",w.widgets_info={code:{type:"code",lang:"glsl"},precision:{widget:"combo", +values:c.MODE_VALUES}},w.prototype.onPropertyChanged=function(a,b){if("code"==a){var d=this.getShader();if(d){var c=d.uniformInfo;if(this.inputs)for(var f={},g=0;g=this.size[1])){var b=this.getInputData(0);b&&a.drawImage(a==gl?b:gl.canvas,10,30,this.size[0]-20,this.size[1]-40)}},v.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this.properties.disable_alpha?gl.disable(gl.BLEND):(gl.enable(gl.BLEND),this.properties.additive?gl.blendFunc(gl.SRC_ALPHA, +gl.ONE):gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA));gl.disable(gl.DEPTH_TEST);var b=this.properties.gamma||1;this.isInputConnected(1)&&(b=this.getInputData(1));a.setParameter(gl.TEXTURE_MAG_FILTER,this.properties.filter?gl.LINEAR:gl.NEAREST);var d=v._prev_viewport;d.set(gl.viewport_data);var c=this.properties.viewport;gl.viewport(d[0]+d[2]*c[0],d[1]+d[3]*c[1],d[2]*c[2],d[3]*c[3]);gl.getViewport();this.properties.antialiasing?(v._shader||(v._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, +v.aa_pixel_shader)),c=Mesh.getScreenQuad(),a.bind(0),v._shader.uniforms({u_texture:0,uViewportSize:[a.width,a.height],u_igamma:1/b,inverseVP:[1/a.width,1/a.height]}).draw(c)):1!=b?(v._gamma_shader||(v._gamma_shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,v.gamma_pixel_shader)),a.toViewport(v._gamma_shader,{u_texture:0,u_igamma:1/b})):a.toViewport();gl.viewport(d[0],d[1],d[2],d[3])}},v.prototype.onGetInputs=function(){return[["gamma","number"]]},v.aa_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 uViewportSize;\n\t\tuniform vec2 inverseVP;\n\t\tuniform float u_igamma;\n\t\t#define FXAA_REDUCE_MIN (1.0/ 128.0)\n\t\t#define FXAA_REDUCE_MUL (1.0 / 8.0)\n\t\t#define FXAA_SPAN_MAX 8.0\n\t\t\n\t\t/* from mitsuhiko/webgl-meincraft based on the code on geeks3d.com */\n\t\tvec4 applyFXAA(sampler2D tex, vec2 fragCoord)\n\t\t{\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t/*vec2 inverseVP = vec2(1.0 / uViewportSize.x, 1.0 / uViewportSize.y);*/\n\t\t\tvec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbM = texture2D(tex, fragCoord * inverseVP).xyz;\n\t\t\tvec3 luma = vec3(0.299, 0.587, 0.114);\n\t\t\tfloat lumaNW = dot(rgbNW, luma);\n\t\t\tfloat lumaNE = dot(rgbNE, luma);\n\t\t\tfloat lumaSW = dot(rgbSW, luma);\n\t\t\tfloat lumaSE = dot(rgbSE, luma);\n\t\t\tfloat lumaM = dot(rgbM, luma);\n\t\t\tfloat lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n\t\t\tfloat lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\t\t\t\n\t\t\tvec2 dir;\n\t\t\tdir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n\t\t\tdir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\t\t\t\n\t\t\tfloat dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\t\t\t\n\t\t\tfloat rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n\t\t\tdir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * inverseVP;\n\t\t\t\n\t\t\tvec3 rgbA = 0.5 * (texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + \n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz);\n\t\t\tvec3 rgbB = rgbA * 0.5 + 0.25 * (texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + \n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz);\n\t\t\t\n\t\t\t//return vec4(rgbA,1.0);\n\t\t\tfloat lumaB = dot(rgbB, luma);\n\t\t\tif ((lumaB < lumaMin) || (lumaB > lumaMax))\n\t\t\t\tcolor = vec4(rgbA, 1.0);\n\t\t\telse\n\t\t\t\tcolor = vec4(rgbB, 1.0);\n\t\t\tif(u_igamma != 1.0)\n\t\t\t\tcolor.xyz = pow( color.xyz, vec3(u_igamma) );\n\t\t\treturn color;\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\t\t}\n\t\t", +v.gamma_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_igamma;\n\t\tvoid main() {\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\t\t gl_FragColor = color;\n\t\t}\n\t\t",J.registerNodeType("texture/toviewport",v),F.title="Copy",F.desc="Copy Texture",F.widgets_info={size:{widget:"combo",values:[0,32,64,128,256,512,1024,2048]},precision:{widget:"combo", +values:c.MODE_VALUES}},F.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)){if(a){var b=a.width,d=a.height;0!=this.properties.size&&(d=b=this.properties.size);var f=this._temp_texture,g=a.type;this.properties.precision===c.LOW?g=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(g=gl.HIGH_PRECISION_FORMAT);f&&f.width==b&&f.height==d&&f.type==g||(f=gl.LINEAR,this.properties.generate_mipmaps&&isPowerOfTwo(b)&&isPowerOfTwo(d)&&(f=gl.LINEAR_MIPMAP_LINEAR), +this._temp_texture=new GL.Texture(b,d,{type:g,format:gl.RGBA,minFilter:f,magFilter:gl.LINEAR}));a.copyTo(this._temp_texture);this.properties.generate_mipmaps&&(this._temp_texture.bind(0),gl.generateMipmap(this._temp_texture.texture_type),this._temp_texture.unbind(0))}this.setOutputData(0,this._temp_texture)}},J.registerNodeType("texture/copy",F),x.title="Downsample",x.desc="Downsample Texture",x.widgets_info={iterations:{type:"number",step:1,precision:0,min:0},precision:{widget:"combo",values:c.MODE_VALUES}}, +x.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)&&a&&a.texture_type===GL.TEXTURE_2D)if(1>this.properties.iterations)this.setOutputData(0,a);else{var b=x._shader;b||(x._shader=b=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,x.pixel_shader));var d=a.width|0,f=a.height|0,g=a.type;this.properties.precision===c.LOW?g=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(g=gl.HIGH_PRECISION_FORMAT);var e=this.properties.iterations||1,l=a,h= +null,k=[],a={type:g,format:a.format},g=vec2.create(),q={u_offset:g};this._texture&&GL.Texture.releaseTemporary(this._texture);for(var r=0;r>1||0;f=f>>1||0;h=GL.Texture.getTemporary(d,f,a);k.push(h);l.setParameter(GL.TEXTURE_MAG_FILTER,GL.NEAREST);l.copyTo(h,b,q);if(1==d&&1==f)break;l=h}this._texture=k.pop();for(r=0;r>=2,c++,1!=d););a.copyTo(this._textures_chain[0]);for(var c=1;c<=this._textures_chain.length;++c)a=this._textures_chain[c];var f=p._shader,g=this._uniforms;g.u_mipmap_offset=this.properties.mipmap_offset;gl.disable(gl.DEPTH_TEST); -gl.disable(gl.BLEND);this._temp_texture.drawTo(function(){a.toViewport(f,g)})}},p.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform mat4 u_samples_a;\n\t\tuniform mat4 u_samples_b;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_mipmap_offset;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t//random average\n\t\t\tfor(int i = 0; i < 4; ++i)\n\t\t\t\tfor(int j = 0; j < 4; ++j)\n\t\t\t\t{\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t}\n\t\t gl_FragColor = color * 0.03125;\n\t\t}\n\t\t", -F.title="Smooth",F.desc="Smooth texture over time",F.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){F._shader||(F._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,F.pixel_shader));var b=this._temp_texture;b&&b.type==a.type&&b.width==a.width&&b.height==a.height||(b={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(a.width,a.height,b),this._temp_texture2=new GL.Texture(a.width,a.height,b),a.copyTo(this._temp_texture2));var b= -this._temp_texture,d=this._temp_texture2,c=F._shader,f=this._uniforms;f.u_factor=1-this.getInputOrProperty("factor");gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);b.drawTo(function(){d.bind(1);a.toViewport(c,f)});this.setOutputData(0,b);this._temp_texture=d;this._temp_texture2=b}},F.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_factor;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tgl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\t\t}\n\t\t", -I.registerNodeType("texture/temporal_smooth",F),v.title="Lineal Avg Smooth",v.desc="Smooth texture linearly over time",v["@samples"]={type:"number",min:1,max:64,step:1,precision:1},v.prototype.getPreviewTexture=function(){return this._temp_texture2},v.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){v._shader||(v._shader_copy=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,v.pixel_shader_copy),v._shader_avg=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,v.pixel_shader_avg)); -var b=Math.clamp(this.properties.samples,0,64),d=this.frame,c=this.properties.frames_interval;if(0==c||0==d%c){d=this._temp_texture;d&&d.type==a.type&&d.width==b||(d={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(b,1,d),this._temp_texture2=new GL.Texture(b,1,d),this._temp_texture_out=new GL.Texture(1,1,d));var f=this._temp_texture,g=this._temp_texture2,e=v._shader_copy,h=v._shader_avg,l=this._uniforms;l.u_samples=b;l.u_isamples=1/b;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST); -f.drawTo(function(){g.bind(1);a.toViewport(e,l)});this._temp_texture_out.drawTo(function(){f.toViewport(h,l)});this.setOutputData(0,this._temp_texture_out);this._temp_texture=g;this._temp_texture2=f}else this.setOutputData(0,this._temp_texture_out);this.setOutputData(1,this._temp_texture2);this.frame++}},v.pixel_shader_copy="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tif( v_coord.x <= u_isamples )\n\t\t\t\tgl_FragColor = texture2D( u_texture, vec2(0.5) );\n\t\t\telse\n\t\t\t\tgl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\t\t}\n\t\t", -v.pixel_shader_avg="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform int u_samples;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\tfor(int i = 0; i < 64; ++i)\n\t\t\t{\n\t\t\t\tcolor += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\t\t\t\tif(i == (u_samples - 1))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tgl_FragColor = color * u_isamples;\n\t\t}\n\t\t",I.registerNodeType("texture/linear_avg_smooth", -v),H.title="Image to Texture",H.desc="Uploads an image to the GPU",H.prototype.onExecute=function(){var a=this.getInputData(0);if(a){var b=a.videoWidth||a.width,d=a.videoHeight||a.height;if(a.gltexture)this.setOutputData(0,a.gltexture);else{var c=this._temp_texture;c&&c.width==b&&c.height==d||(this._temp_texture=new GL.Texture(b,d,{format:gl.RGBA,filter:gl.LINEAR}));try{this._temp_texture.uploadImage(a)}catch(f){console.error("image comes from an unsafe location, cannot be uploaded to webgl: "+f); -return}this.setOutputData(0,this._temp_texture)}}},I.registerNodeType("texture/imageToTexture",H),w.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},w.title="LUT",w.desc="Apply LUT to Texture",w.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture)); -if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.bindTexture(gl.TEXTURE_2D,null);var d=this.properties.intensity;this.isInputConnected(2)&&(this.properties.intensity=d=this.getInputData(2));this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);this._tex.drawTo(function(){b.bind(1);a.toViewport(w._shader,{u_texture:0,u_textureB:1, -u_amount:d})});this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}},w.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_amount;\n\t\t\n\t\tvoid main() {\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\t\t\t mediump vec2 quad1;\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\t\t\t mediump vec2 quad2;\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\t\t\t highp vec2 texPos1;\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t highp vec2 texPos2;\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\t\t}\n\t\t", -I.registerNodeType("texture/LUT",w),t.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},t.title="Encode",t.desc="Apply a texture atlas to encode a texture",t.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture));if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D, +J.registerNodeType("texture/average",e),m.widgets_info={mode:{widget:"combo",values:["min","max","avg"]}},m.title="MinMax",m.desc="Compute the scene min max",m.prototype.onExecute=function(){this.properties.use_previous_frame||this.update();this.setOutputData(0,this._temp_texture);this.setOutputData(1,this._luminance)},m.prototype.onPreRenderExecute=function(){this.update()},m.prototype.update=function(){var a=this.getInputData(0);if(a&&(this.isOutputConnected(0)||this.isOutputConnected(1))){m._shader|| +(m._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,m.pixel_shader));var b=gl.UNSIGNED_BYTE;a.type!=b&&(b=gl.FLOAT);var d=512;if(!this._textures_chain.length||this._textures_chain[0].type!=b)for(;c&&(this._textures_chain[c]=new GL.Texture(d,d,{type:b,format:gl.RGBA,filter:gl.NEAREST}),d>>=2,c++,1!=d););a.copyTo(this._textures_chain[0]);for(var c=1;c<=this._textures_chain.length;++c)a=this._textures_chain[c];var f=m._shader,g=this._uniforms;g.u_mipmap_offset=this.properties.mipmap_offset;gl.disable(gl.DEPTH_TEST); +gl.disable(gl.BLEND);this._temp_texture.drawTo(function(){a.toViewport(f,g)})}},m.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform mat4 u_samples_a;\n\t\tuniform mat4 u_samples_b;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_mipmap_offset;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t//random average\n\t\t\tfor(int i = 0; i < 4; ++i)\n\t\t\t\tfor(int j = 0; j < 4; ++j)\n\t\t\t\t{\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\t\t\t\t}\n\t\t gl_FragColor = color * 0.03125;\n\t\t}\n\t\t", +D.title="Smooth",D.desc="Smooth texture over time",D.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){D._shader||(D._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,D.pixel_shader));var b=this._temp_texture;b&&b.type==a.type&&b.width==a.width&&b.height==a.height||(b={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(a.width,a.height,b),this._temp_texture2=new GL.Texture(a.width,a.height,b),a.copyTo(this._temp_texture2));var b= +this._temp_texture,d=this._temp_texture2,c=D._shader,f=this._uniforms;f.u_factor=1-this.getInputOrProperty("factor");gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);b.drawTo(function(){d.bind(1);a.toViewport(c,f)});this.setOutputData(0,b);this._temp_texture=d;this._temp_texture2=b}},D.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_factor;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tgl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\t\t}\n\t\t", +J.registerNodeType("texture/temporal_smooth",D),u.title="Lineal Avg Smooth",u.desc="Smooth texture linearly over time",u["@samples"]={type:"number",min:1,max:64,step:1,precision:1},u.prototype.getPreviewTexture=function(){return this._temp_texture2},u.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){u._shader||(u._shader_copy=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,u.pixel_shader_copy),u._shader_avg=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,u.pixel_shader_avg)); +var b=Math.clamp(this.properties.samples,0,64),d=this.frame,c=this.properties.frames_interval;if(0==c||0==d%c){d=this._temp_texture;d&&d.type==a.type&&d.width==b||(d={type:a.type,format:gl.RGBA,filter:gl.NEAREST},this._temp_texture=new GL.Texture(b,1,d),this._temp_texture2=new GL.Texture(b,1,d),this._temp_texture_out=new GL.Texture(1,1,d));var f=this._temp_texture,g=this._temp_texture2,e=u._shader_copy,l=u._shader_avg,h=this._uniforms;h.u_samples=b;h.u_isamples=1/b;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST); +f.drawTo(function(){g.bind(1);a.toViewport(e,h)});this._temp_texture_out.drawTo(function(){f.toViewport(l,h)});this.setOutputData(0,this._temp_texture_out);this._temp_texture=g;this._temp_texture2=f}else this.setOutputData(0,this._temp_texture_out);this.setOutputData(1,this._temp_texture2);this.frame++}},u.pixel_shader_copy="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tif( v_coord.x <= u_isamples )\n\t\t\t\tgl_FragColor = texture2D( u_texture, vec2(0.5) );\n\t\t\telse\n\t\t\t\tgl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\t\t}\n\t\t", +u.pixel_shader_avg="precision highp float;\n\t\tprecision highp float;\n\t\tuniform sampler2D u_texture;\n\t\tuniform int u_samples;\n\t\tuniform float u_isamples;\n\t\tvarying vec2 v_coord;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 color = vec4(0.0);\n\t\t\tfor(int i = 0; i < 64; ++i)\n\t\t\t{\n\t\t\t\tcolor += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\t\t\t\tif(i == (u_samples - 1))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tgl_FragColor = color * u_isamples;\n\t\t}\n\t\t",J.registerNodeType("texture/linear_avg_smooth", +u),I.title="Image to Texture",I.desc="Uploads an image to the GPU",I.prototype.onExecute=function(){var a=this.getInputData(0);if(a){var b=a.videoWidth||a.width,d=a.videoHeight||a.height;if(a.gltexture)this.setOutputData(0,a.gltexture);else{var c=this._temp_texture;c&&c.width==b&&c.height==d||(this._temp_texture=new GL.Texture(b,d,{format:gl.RGBA,filter:gl.LINEAR}));try{this._temp_texture.uploadImage(a)}catch(f){console.error("image comes from an unsafe location, cannot be uploaded to webgl: "+f); +return}this.setOutputData(0,this._temp_texture)}}},J.registerNodeType("texture/imageToTexture",I),q.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},q.title="LUT",q.desc="Apply LUT to Texture",q.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture)); +if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.bindTexture(gl.TEXTURE_2D,null);var d=this.properties.intensity;this.isInputConnected(2)&&(this.properties.intensity=d=this.getInputData(2));this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);this._tex.drawTo(function(){b.bind(1);a.toViewport(q._shader,{u_texture:0,u_textureB:1, +u_amount:d})});this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}},q.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_amount;\n\t\t\n\t\tvoid main() {\n\t\t\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\t\t\t mediump float blueColor = textureColor.b * 63.0;\n\t\t\t mediump vec2 quad1;\n\t\t\t quad1.y = floor(floor(blueColor) / 8.0);\n\t\t\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\t\t\t mediump vec2 quad2;\n\t\t\t quad2.y = floor(ceil(blueColor) / 8.0);\n\t\t\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\t\t\t highp vec2 texPos1;\n\t\t\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t highp vec2 texPos2;\n\t\t\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\t\t\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\t\t\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\t\t\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\t\t\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\t\t\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\t\t}\n\t\t", +J.registerNodeType("texture/LUT",q),r.widgets_info={texture:{widget:"texture"},precision:{widget:"combo",values:c.MODE_VALUES}},r.title="Encode",r.desc="Apply a texture atlas to encode a texture",r.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH||!1===this.properties.enabled)this.setOutputData(0,a);else if(a){var b=this.getInputData(1);b||(b=c.getTexture(this.properties.texture));if(b){b.bind(0);gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER,this.properties.filter?gl.LINEAR:gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,this.properties.filter?gl.LINEAR:gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);gl.bindTexture(gl.TEXTURE_2D,null);var d=this._uniforms;d.u_row_simbols=Math.floor(this.properties.num_row_symbols);d.u_symbol_size=this.properties.symbol_size;d.u_brightness=this.properties.brightness; -d.u_invert=this.properties.invert?1:0;d.u_colorize=this.properties.colorize?1:0;this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);d.u_res[0]=this._tex.width;d.u_res[1]=this._tex.height;this._tex.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.NEAREST);this._tex.drawTo(function(){b.bind(1);a.toViewport(t._shader,d)});this.properties.generate_mipmaps&&(this._tex.bind(0),gl.generateMipmap(this._tex.texture_type), -this._tex.unbind(0));this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}},t.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_row_simbols;\n\t\tuniform float u_symbol_size;\n\t\tuniform float u_brightness;\n\t\tuniform float u_invert;\n\t\tuniform float u_colorize;\n\t\tuniform vec2 u_res;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 total_symbols = u_res / u_symbol_size;\n\t\t\tvec2 uv = floor(v_coord * total_symbols) / total_symbols; //pixelate \n\t\t\tvec2 local_uv = mod(v_coord * u_res, u_symbol_size) / u_symbol_size;\n\t\t\tlowp vec4 textureColor = texture2D(u_texture, uv );\n\t\t\tfloat lum = clamp(u_brightness * (textureColor.x + textureColor.y + textureColor.z)/3.0,0.0,1.0);\n\t\t\tif( u_invert == 1.0 ) lum = 1.0 - lum;\n\t\t\tfloat index = floor( lum * (u_row_simbols * u_row_simbols - 1.0));\n\t\t\tfloat col = mod( index, u_row_simbols );\n\t\t\tfloat row = u_row_simbols - floor( index / u_row_simbols ) - 1.0;\n\t\t\tvec2 simbol_uv = ( vec2( col, row ) + local_uv ) / u_row_simbols;\n\t\t\tvec4 color = texture2D( u_textureB, simbol_uv );\n\t\t\tif(u_colorize == 1.0)\n\t\t\t\tcolor *= textureColor;\n\t\t\tgl_FragColor = color;\n\t\t}\n\t\t", -I.registerNodeType("texture/encode",t),l.title="Texture to Channels",l.desc="Split texture channels",l.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this._channels||(this._channels=Array(4));for(var b=gl.RGB,d=0,c=0;4>c;c++)this.isOutputConnected(c)?(this._channels[c]&&this._channels[c].width==a.width&&this._channels[c].height==a.height&&this._channels[c].type==a.type&&this._channels[c].format==b||(this._channels[c]=new GL.Texture(a.width,a.height,{type:a.type,format:b,filter:gl.LINEAR})), +d.u_invert=this.properties.invert?1:0;d.u_colorize=this.properties.colorize?1:0;this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);d.u_res[0]=this._tex.width;d.u_res[1]=this._tex.height;this._tex.bind(0);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.NEAREST);this._tex.drawTo(function(){b.bind(1);a.toViewport(r._shader,d)});this.properties.generate_mipmaps&&(this._tex.bind(0),gl.generateMipmap(this._tex.texture_type), +this._tex.unbind(0));this.setOutputData(0,this._tex)}else this.setOutputData(0,a)}}},r.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform float u_row_simbols;\n\t\tuniform float u_symbol_size;\n\t\tuniform float u_brightness;\n\t\tuniform float u_invert;\n\t\tuniform float u_colorize;\n\t\tuniform vec2 u_res;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 total_symbols = u_res / u_symbol_size;\n\t\t\tvec2 uv = floor(v_coord * total_symbols) / total_symbols; //pixelate \n\t\t\tvec2 local_uv = mod(v_coord * u_res, u_symbol_size) / u_symbol_size;\n\t\t\tlowp vec4 textureColor = texture2D(u_texture, uv );\n\t\t\tfloat lum = clamp(u_brightness * (textureColor.x + textureColor.y + textureColor.z)/3.0,0.0,1.0);\n\t\t\tif( u_invert == 1.0 ) lum = 1.0 - lum;\n\t\t\tfloat index = floor( lum * (u_row_simbols * u_row_simbols - 1.0));\n\t\t\tfloat col = mod( index, u_row_simbols );\n\t\t\tfloat row = u_row_simbols - floor( index / u_row_simbols ) - 1.0;\n\t\t\tvec2 simbol_uv = ( vec2( col, row ) + local_uv ) / u_row_simbols;\n\t\t\tvec4 color = texture2D( u_textureB, simbol_uv );\n\t\t\tif(u_colorize == 1.0)\n\t\t\t\tcolor *= textureColor;\n\t\t\tgl_FragColor = color;\n\t\t}\n\t\t", +J.registerNodeType("texture/encode",r),l.title="Texture to Channels",l.desc="Split texture channels",l.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this._channels||(this._channels=Array(4));for(var b=gl.RGB,d=0,c=0;4>c;c++)this.isOutputConnected(c)?(this._channels[c]&&this._channels[c].width==a.width&&this._channels[c].height==a.height&&this._channels[c].type==a.type&&this._channels[c].format==b||(this._channels[c]=new GL.Texture(a.width,a.height,{type:a.type,format:b,filter:gl.LINEAR})), d++):this._channels[c]=null;if(d){gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);for(var f=Mesh.getScreenQuad(),g=l._shader,e=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],c=0;4>c;c++)this._channels[c]&&(this._channels[c].drawTo(function(){a.bind(0);g.uniforms({u_texture:0,u_mask:e[c]}).draw(f)}),this.setOutputData(c,this._channels[c]))}}},l.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec4 u_mask;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\t\t}\n\t\t", -I.registerNodeType("texture/textureChannels",l),a.title="Channels to Texture",a.desc="Split texture channels",a.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},a.prototype.onExecute=function(){var b=c.getWhiteTexture(),d=this.getInputData(0)||b,f=this.getInputData(1)||b,g=this.getInputData(2)||b,e=this.getInputData(3)||b;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var h=Mesh.getScreenQuad();a._shader||(a._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,a.pixel_shader));var l=a._shader, -b=Math.max(d.width,f.width,g.width,e.width),r=Math.max(d.height,f.height,g.height,e.height),k=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._texture&&this._texture.width==b&&this._texture.height==r&&this._texture.type==k||(this._texture=new GL.Texture(b,r,{type:k,format:gl.RGBA,filter:gl.LINEAR}));b=this._color;b[0]=this.properties.R;b[1]=this.properties.G;b[2]=this.properties.B;b[3]=this.properties.A;var s=this._uniforms;this._texture.drawTo(function(){d.bind(0); -f.bind(1);g.bind(2);e.bind(3);l.uniforms(s).draw(h)});this.setOutputData(0,this._texture)},a.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureR;\n\t\tuniform sampler2D u_textureG;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform vec4 u_color;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = u_color * vec4( \t\t\t\t\ttexture2D(u_textureR, v_coord).r,\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\t\t}\n\t\t", -I.registerNodeType("texture/channelsTexture",a),b.title="Color",b.desc="Generates a 1x1 texture with a constant color",b.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},b.prototype.onDrawBackground=function(a){var b=this.properties.color;a.fillStyle="rgb("+Math.floor(255*Math.clamp(b[0],0,1))+","+Math.floor(255*Math.clamp(b[1],0,1))+","+Math.floor(255*Math.clamp(b[2],0,1))+")";this.flags.collapsed?this.boxcolor=a.fillStyle:a.fillRect(0,0,this.size[0],this.size[1])},b.prototype.onExecute= +J.registerNodeType("texture/textureChannels",l),a.title="Channels to Texture",a.desc="Split texture channels",a.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},a.prototype.onExecute=function(){var b=c.getWhiteTexture(),d=this.getInputData(0)||b,f=this.getInputData(1)||b,g=this.getInputData(2)||b,e=this.getInputData(3)||b;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var l=Mesh.getScreenQuad();a._shader||(a._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,a.pixel_shader));var h=a._shader, +b=Math.max(d.width,f.width,g.width,e.width),k=Math.max(d.height,f.height,g.height,e.height),q=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._texture&&this._texture.width==b&&this._texture.height==k&&this._texture.type==q||(this._texture=new GL.Texture(b,k,{type:q,format:gl.RGBA,filter:gl.LINEAR}));b=this._color;b[0]=this.properties.R;b[1]=this.properties.G;b[2]=this.properties.B;b[3]=this.properties.A;var r=this._uniforms;this._texture.drawTo(function(){d.bind(0); +f.bind(1);g.bind(2);e.bind(3);h.uniforms(r).draw(l)});this.setOutputData(0,this._texture)},a.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureR;\n\t\tuniform sampler2D u_textureG;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform vec4 u_color;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = u_color * vec4( \t\t\t\t\ttexture2D(u_textureR, v_coord).r,\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\t\t}\n\t\t", +J.registerNodeType("texture/channelsTexture",a),b.title="Color",b.desc="Generates a 1x1 texture with a constant color",b.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},b.prototype.onDrawBackground=function(a){var b=this.properties.color;a.fillStyle="rgb("+Math.floor(255*Math.clamp(b[0],0,1))+","+Math.floor(255*Math.clamp(b[1],0,1))+","+Math.floor(255*Math.clamp(b[2],0,1))+")";this.flags.collapsed?this.boxcolor=a.fillStyle:a.fillRect(0,0,this.size[0],this.size[1])},b.prototype.onExecute= function(){var a=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._tex&&this._tex.type==a||(this._tex=new GL.Texture(1,1,{format:gl.RGBA,type:a,minFilter:gl.NEAREST}));a=this.properties.color;if(this.inputs)for(var b=0;ba.width?b: -a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad(),h=null,l=this._uniforms;d?(h=f._shader_tex,h||(h=f._shader_tex=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,f.pixel_shader,{MIX_TEX:""}))):(h=f._shader_factor,h||(h=f._shader_factor=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,f.pixel_shader)),g=null==g?this.properties.factor:g,l.u_mix.set([g,g,g,g]));var r=this.properties.invert;this._tex.drawTo(function(){a.bind(r?1:0);b.bind(r?0:1);d&&d.bind(2); -h.uniforms(l).draw(e)});this.setOutputData(0,this._tex)}}},f.prototype.onGetInputs=function(){return[["factor","number"]]},f.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform sampler2D u_textureB;\n\t\t#ifdef MIX_TEX\n\t\t\tuniform sampler2D u_textureMix;\n\t\t#else\n\t\t\tuniform vec4 u_mix;\n\t\t#endif\n\t\t\n\t\tvoid main() {\n\t\t\t#ifdef MIX_TEX\n\t\t\t vec4 f = texture2D(u_textureMix, v_coord);\n\t\t\t#else\n\t\t\t vec4 f = u_mix;\n\t\t\t#endif\n\t\t gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\t\t}\n\t\t", -I.registerNodeType("texture/mix",f),g.title="Edges",g.desc="Detects edges",g.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},g.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else if(a){this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var b=Mesh.getScreenQuad(),d=g._shader,f=this.properties.invert,e=this.properties.factor, -h=this.properties.threshold?1:0;this._tex.drawTo(function(){a.bind(0);d.uniforms({u_texture:0,u_isize:[1/a.width,1/a.height],u_factor:e,u_threshold:h,u_invert:f?1:0}).draw(b)});this.setOutputData(0,this._tex)}}},g.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_isize;\n\t\tuniform int u_invert;\n\t\tuniform float u_factor;\n\t\tuniform float u_threshold;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 center = texture2D(u_texture, v_coord);\n\t\t\tvec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\t\t\tvec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\t\t\tvec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\t\t\tvec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\t\t\tvec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\t\t\tdiff *= u_factor;\n\t\t\tif(u_invert == 1)\n\t\t\t\tdiff.xyz = vec3(1.0) - diff.xyz;\n\t\t\tif( u_threshold == 0.0 )\n\t\t\t\tgl_FragColor = vec4( diff.xyz, center.a );\n\t\t\telse\n\t\t\t\tgl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\t\t}\n\t\t", -I.registerNodeType("texture/edges",g),q.title="Depth Range",q.desc="Generates a texture with a depth range",q.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a){var b=gl.UNSIGNED_BYTE;this.properties.high_precision&&(b=gl.half_float_ext?gl.HALF_FLOAT_OES:gl.FLOAT);this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&&this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGBA, -filter:gl.LINEAR}));var d=this._uniforms,b=this.properties.distance;this.isInputConnected(1)&&(b=this.getInputData(1),this.properties.distance=b);var c=this.properties.range;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.range=c);d.u_distance=b;d.u_range=c;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var f=Mesh.getScreenQuad();q._shader||(q._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,q.pixel_shader),q._shader_onlydepth=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,q.pixel_shader, -{ONLY_DEPTH:""}));var g=this.properties.only_depth?q._shader_onlydepth:q._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1,1E3];d.u_camera_planes=b;this._temp_texture.drawTo(function(){a.bind(0);g.uniforms(d).draw(f)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},q.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform float u_distance;\n\t\tuniform float u_range;\n\t\t\n\t\tfloat LinearDepth()\n\t\t{\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\t\t\tdepth = depth * 2.0 - 1.0;\n\t\t\treturn zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t\tfloat depth = LinearDepth();\n\t\t\t#ifdef ONLY_DEPTH\n\t\t\t gl_FragColor = vec4(depth);\n\t\t\t#else\n\t\t\t\tfloat diff = abs(depth * u_camera_planes.y - u_distance);\n\t\t\t\tfloat dof = 1.0;\n\t\t\t\tif(diff <= u_range)\n\t\t\t\t\tdof = diff / u_range;\n\t\t\t gl_FragColor = vec4(dof);\n\t\t\t#endif\n\t\t}\n\t\t", -I.registerNodeType("texture/depth_range",q),A.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},A.title="Linear Depth",A.desc="Creates a color texture with linear depth",A.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a&&(a.format==gl.DEPTH_COMPONENT||a.format==gl.DEPTH_STENCIL)){var b=this.properties.precision==c.HIGH?gl.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&& -this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGB,filter:gl.LINEAR}));var d=this._uniforms;d.u_invert=this.properties.invert?1:0;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var f=Mesh.getScreenQuad();A._shader||(A._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,A.pixel_shader));var g=A._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1, -1E3];d.u_camera_planes=b;d.u_ires.set([0,0]);this._temp_texture.drawTo(function(){a.bind(0);g.uniforms(d).draw(f)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},A.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform int u_invert;\n\t\tuniform vec2 u_ires;\n\t\t\n\t\tvoid main() {\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord + u_ires*0.5).x * 2.0 - 1.0;\n\t\t\tfloat f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t\tif( u_invert == 1 )\n\t\t\t\tf = 1.0 - f;\n\t\t\tgl_FragColor = vec4(vec3(f),1.0);\n\t\t}\n\t\t", -I.registerNodeType("texture/linear_depth",A),K.title="Blur",K.desc="Blur a texture",K.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},K.max_iterations=20,K.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._final_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(b=this._final_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));var d=this.properties.iterations;this.isInputConnected(1)&& -(d=this.getInputData(1),this.properties.iterations=d);d=Math.min(Math.floor(d),K.max_iterations);if(0==d)this.setOutputData(0,a);else{var c=this.properties.intensity;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.intensity=c);var f=I.camera_aspect;f||void 0===window.gl||(f=gl.canvas.height/gl.canvas.width);f||(f=1);var f=this.properties.preserve_aspect?f:1,g=this.properties.scale||[1,1];a.applyBlur(f*g[0],g[1],c,b);for(a=1;a>=1;1<(d|0)&&(d>>=1);if(2>b)break;k=h[u]=GL.Texture.getTemporary(b,d,f);t[0]=1/s.width;t[1]=1/s.height;s.blit(k,l.uniforms(e));s=k}this.isOutputConnected(2)&&(b=this._average_texture,b&&b.type==a.type&&b.format==a.format||(b=this._average_texture=new GL.Texture(1,1,{type:a.type,format:a.format,filter:gl.LINEAR})),t[0]=1/s.width,t[1]=1/s.height,e.u_intensity=q,e.u_delta=1,s.blit(b,l.uniforms(e)),this.setOutputData(2,b));gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE);e.u_intensity= -this.getInputOrProperty("persistence");e.u_delta=0.5;for(u-=2;0<=u;u--)k=h[u],h[u]=null,t[0]=1/s.width,t[1]=1/s.height,s.blit(k,l.uniforms(e)),GL.Texture.releaseTemporary(s),s=k;gl.disable(gl.BLEND);this.isOutputConnected(1)&&(h=this._glow_texture,h&&h.width==a.width&&h.height==a.height&&h.type==g&&h.format==a.format||(h=this._glow_texture=new GL.Texture(a.width,a.height,{type:g,format:a.format,filter:gl.LINEAR})),s.blit(h),this.setOutputData(1,h));if(this.isOutputConnected(0)){h=this._final_texture; -h&&h.width==a.width&&h.height==a.height&&h.type==g&&h.format==a.format||(h=this._final_texture=new GL.Texture(a.width,a.height,{type:g,format:a.format,filter:gl.LINEAR}));var z=this.getInputData(1),m=this.getInputOrProperty("dirt_factor");e.u_intensity=q;l=z?r._dirt_final_shader:r._final_shader;l||(l=z?r._dirt_final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,r.final_pixel_shader,{USE_DIRT:""}):r._final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,r.final_pixel_shader));h.drawTo(function(){a.bind(0); -s.bind(1);z&&(l.setUniform("u_dirt_factor",m),l.setUniform("u_dirt_texture",z.bind(2)));l.toViewport(e)});this.setOutputData(0,h)}GL.Texture.releaseTemporary(s)}},r.cut_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform float u_threshold;\n\tvoid main() {\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\t}",r.scale_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\t}", -r.final_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform sampler2D u_glow_texture;\n\t#ifdef USE_DIRT\n\t\tuniform sampler2D u_dirt_texture;\n\t#endif\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\tuniform float u_dirt_factor;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tvec4 glow = sampleBox( v_coord );\n\t\t#ifdef USE_DIRT\n\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\t\t#endif\n\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\t}", -I.registerNodeType("texture/glow",r),s.title="Kuwahara Filter",s.desc="Filters a texture giving an artistic oil canvas painting",s.max_radius=10,s._shaders=[],s.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));b=this.properties.radius;b=Math.min(Math.floor(b),s.max_radius);if(0== -b)this.setOutputData(0,a);else{var d=this.properties.intensity,c=I.camera_aspect;c||void 0===window.gl||(c=gl.canvas.height/gl.canvas.width);c||(c=1);c=this.properties.preserve_aspect?c:1;s._shaders[b]||(s._shaders[b]=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,s.pixel_shader,{RADIUS:b.toFixed(0)}));var f=s._shaders[b],g=GL.Mesh.getScreenQuad();a.bind(0);this._temp_texture.drawTo(function(){f.uniforms({u_texture:0,u_intensity:d,u_resolution:[a.width,a.height],u_iResolution:[1/a.width,1/a.height]}).draw(g)}); -this.setOutputData(0,this._temp_texture)}}},s.pixel_shader="\nprecision highp float;\nvarying vec2 v_coord;\nuniform sampler2D u_texture;\nuniform float u_intensity;\nuniform vec2 u_resolution;\nuniform vec2 u_iResolution;\n#ifndef RADIUS\n\t#define RADIUS 7\n#endif\nvoid main() {\n\n\tconst int radius = RADIUS;\n\tvec2 fragCoord = v_coord;\n\tvec2 src_size = u_iResolution;\n\tvec2 uv = v_coord;\n\tfloat n = float((radius + 1) * (radius + 1));\n\tint i;\n\tint j;\n\tvec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\tvec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\tvec3 c;\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm0 += c;\n\t\t\ts0 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm1 += c;\n\t\t\ts1 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm2 += c;\n\t\t\ts2 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm3 += c;\n\t\t\ts3 += c * c;\n\t\t}\n\t}\n\t\n\tfloat min_sigma2 = 1e+2;\n\tm0 /= n;\n\ts0 = abs(s0 / n - m0 * m0);\n\t\n\tfloat sigma2 = s0.r + s0.g + s0.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m0, 1.0);\n\t}\n\t\n\tm1 /= n;\n\ts1 = abs(s1 / n - m1 * m1);\n\t\n\tsigma2 = s1.r + s1.g + s1.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m1, 1.0);\n\t}\n\t\n\tm2 /= n;\n\ts2 = abs(s2 / n - m2 * m2);\n\t\n\tsigma2 = s2.r + s2.g + s2.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m2, 1.0);\n\t}\n\t\n\tm3 /= n;\n\ts3 = abs(s3 / n - m3 * m3);\n\t\n\tsigma2 = s3.r + s3.g + s3.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m3, 1.0);\n\t}\n}\n", -I.registerNodeType("texture/kuwahara",s),z.title="XDoG Filter",z.desc="Filters a texture giving an artistic ink style",z.max_radius=10,z._shaders=[],z.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));z._xdog_shader||(z._xdog_shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,z.xdog_pixel_shader)); -var d=z._xdog_shader,c=GL.Mesh.getScreenQuad(),f=this.properties.sigma,g=this.properties.k,e=this.properties.p,h=this.properties.epsilon,l=this.properties.phi;a.bind(0);this._temp_texture.drawTo(function(){d.uniforms({src:0,sigma:f,k:g,p:e,epsilon:h,phi:l,cvsWidth:a.width,cvsHeight:a.height}).draw(c)});this.setOutputData(0,this._temp_texture)}},z.xdog_pixel_shader="\nprecision highp float;\nuniform sampler2D src;\n\nuniform float cvsHeight;\nuniform float cvsWidth;\n\nuniform float sigma;\nuniform float k;\nuniform float p;\nuniform float epsilon;\nuniform float phi;\nvarying vec2 v_coord;\n\nfloat cosh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat cosH = (tmp + 1.0 / tmp) / 2.0;\n\treturn cosH;\n}\n\nfloat tanh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\treturn tanH;\n}\n\nfloat sinh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat sinH = (tmp - 1.0 / tmp) / 2.0;\n\treturn sinH;\n}\n\nvoid main(void){\n\tvec3 destColor = vec3(0.0);\n\tfloat tFrag = 1.0 / cvsHeight;\n\tfloat sFrag = 1.0 / cvsWidth;\n\tvec2 Frag = vec2(sFrag,tFrag);\n\tvec2 uv = gl_FragCoord.st;\n\tfloat twoSigmaESquared = 2.0 * sigma * sigma;\n\tfloat twoSigmaRSquared = twoSigmaESquared * k * k;\n\tint halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\tconst int MAX_NUM_ITERATION = 99999;\n\tvec2 sum = vec2(0.0);\n\tvec2 norm = vec2(0.0);\n\n\tfor(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\t\tint i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\t\tint j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\t\tfloat d = length(vec2(i,j));\n\t\tvec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\t\t\t\t\t\t\texp( -d * d / twoSigmaRSquared ));\n\n\t\tvec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\t\tnorm += kernel;\n\t\tsum += kernel * L;\n\t}\n\n\tsum /= norm;\n\n\tfloat H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\tfloat edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\tdestColor = vec3(edge);\n\tgl_FragColor = vec4(destColor, 1.0);\n}", -I.registerNodeType("texture/xDoG",z),u.title="Webcam",u.desc="Webcam texture",u.is_webcam_open=!1,u.prototype.openStream=function(){function a(d){u.is_webcam_open=!1;console.log("Webcam rejected",d);b._webcam_stream=!1;b.boxcolor="red";b.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](a);var b=this}},u.prototype.closeStream=function(){if(this._webcam_stream){var a= -this._webcam_stream.getTracks();if(a.length)for(var b=0;b=this.size[1]||!this._video||(a.save(),a.webgl?this._video_texture&&a.drawImage(this._video_texture,0,0,this.size[0],this.size[1]):a.drawImage(this._video, -0,0,this.size[0],this.size[1]),a.restore())},u.prototype.onExecute=function(){null!=this._webcam_stream||this._waiting_confirmation||this.openStream();if(this._video&&this._video.videoWidth){var a=this._video.videoWidth,b=this._video.videoHeight,d=this._video_texture;d&&d.width==a&&d.height==b||(this._video_texture=new GL.Texture(a,b,{format:gl.RGB,filter:gl.LINEAR}));this._video_texture.uploadImage(this._video);this._video_texture.version=++this.version;this.properties.texture_name&&(c.getTexturesContainer()[this.properties.texture_name]= -this._video_texture);this.setOutputData(0,this._video_texture);for(a=1;aMath.abs(d))return c[1];d=(a-c[0])/d;return c[1]*(1-d)+f[1]*d}}return 0}},J.prototype.updateCurve=function(){for(var a=this._values,b=a.length/4,d=this.properties.split_channels,c=0;cd+l.NODE_TITLE_HEIGHT&&a.drawImage(b,10,f,this.size[0]-20,this.size[1]-d-l.NODE_TITLE_HEIGHT);var f=this.size[1]- -l.NODE_TITLE_HEIGHT+0.5;c=l.isInsideRectangle(c[0],c[1],this.pos[0],this.pos[1]+f,this.size[0],l.NODE_TITLE_HEIGHT);a.fillStyle=c?"#555":"#222";a.beginPath();this._shape==l.BOX_SHAPE?a.rect(0,f,this.size[0]+1,l.NODE_TITLE_HEIGHT):a.roundRect(0,f,this.size[0]+1,l.NODE_TITLE_HEIGHT,0,8);a.fill();a.textAlign="center";a.font="24px Arial";a.fillStyle=c?"#DDD":"#999";a.fillText("+",0.5*this.size[0],f+24)}};D.prototype.onMouseDown=function(a,b,d){b[1]>this.size[1]-l.NODE_TITLE_HEIGHT+0.5&&d.showSubgraphPropertiesDialog(this)}; -D.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:"Print Code",callback:function(){var a=b._context.computeShaderCode();console.log(a.vs_code,a.fs_code)}}]};l.registerNodeType("texture/shaderGraph",D);x.title="Uniform";x.desc="Input data for the shader";x.prototype.getTitle=function(){return this.properties.name&&this.flags.collapsed?this.properties.type+" "+this.properties.name:"Uniform"};x.prototype.onPropertyChanged=function(a,b){this.outputs[0].name=this.properties.type+" "+ -this.properties.name};x.prototype.onGetCode=function(a){var d=this.properties.type;d&&("number"==d?d="float":"texture"==d&&(d="sampler2D"),-1!=b.GLSL_types.indexOf(d)&&(a.addUniform("u_"+this.properties.name,d),this.setOutputData(0,d)))};x.prototype.getOutputVarName=function(a){return"u_"+this.properties.name};m("input/uniform",x);B.title="Attribute";B.desc="Input data from mesh attribute";B.prototype.getTitle=function(){return"att. "+this.properties.name};B.prototype.onGetCode=function(a){var d= -this.properties.type;d&&-1!=b.GLSL_types.indexOf(d)&&("number"==d&&(d="float"),"coord"!=this.properties.name&&a.addCode("varying"," varying "+d+" v_"+this.properties.name+";"),this.setOutputData(0,d))};B.prototype.getOutputVarName=function(a){return"v_"+this.properties.name};m("input/attribute",B);G.title="Sampler2D";G.desc="Reads a pixel from a texture";G.prototype.onGetCode=function(a){var b=k(this,0),d=n(this),c="vec4 "+d+" = vec4(0.0);\n";if(b)var f=k(this,1)||"v_coord",c=c+(d+" = texture2D("+ -b+","+f+");\n");y(this,0)&&(c+="vec4 "+y(this,0)+" = "+d+";\n");y(this,1)&&(c+="vec3 "+y(this,1)+" = "+d+".xyz;\n");a.addCode("code",c,this.shader_destination);this.setOutputData(0,"vec4");this.setOutputData(1,"vec3")};m("texture/sampler2D",G);E.title="const";E.prototype.getTitle=function(){return this.flags.collapsed?A(this.properties.value,this.properties.type):"Const"};E.prototype.onPropertyChanged=function(a,b){"type"==a&&this.outputs[0].type!=b&&(this.disconnectOutput(0),this.outputs[0].type= -b,this.widgets.length=1,this.updateWidgets())};E.prototype.updateWidgets=function(a){var b=this;a=this.properties.value;var d={step:0.01};switch(this.properties.type){case "float":this.properties.value=0;this.addWidget("number","v",0,{step:0.01,property:"value"});break;case "vec2":this.properties.value=a&&2==a.length?[a[0],a[1]]:[0,0,0];this.addWidget("number","x",0,d,function(a){b.properties.value[0]=a});this.addWidget("number","y",0,d,function(a){b.properties.value[1]=a});break;case "vec3":this.properties.value= -a&&3==a.length?[a[0],a[1],a[2]]:[0,0,0];this.addWidget("number","x",0,d,function(a){b.properties.value[0]=a});this.addWidget("number","y",0,d,function(a){b.properties.value[1]=a});this.addWidget("number","z",0,d,function(a){b.properties.value[2]=a});break;case "vec4":this.properties.value=a&&4==a.length?[a[0],a[1],a[2],a[3]]:[0,0,0,0];this.addWidget("number","x",0,d,function(a){b.properties.value[0]=a});this.addWidget("number","y",0,d,function(a){b.properties.value[1]=a});this.addWidget("number", -"z",0,d,function(a){b.properties.value[2]=a});this.addWidget("number","w",0,d,function(a){b.properties.value[3]=a});break;default:console.error("unknown type for constant")}};E.prototype.onGetCode=function(a){var b=A(this.properties.value,this.properties.type),d=y(this,0);d&&(a.addCode("code","\t"+this.properties.type+" "+d+" = "+b+";",this.shader_destination),this.setOutputData(0,this.properties.type))};m("const/const",E);e.title="vec2";e.varmodes=["xy","x","y"];e.prototype.onPropertyChanged=function(){this.graph._version++}; -e.prototype.onGetCode=function(a){for(var b=this.properties,c=n(this),b="\tvec2 "+c+" = "+A([b.x,b.y])+";\n",f=0;fd;++d)b.push({name:k(this,d),type:this.getInputData(d)||"float"});var c=y(this,0);if(c){var f=g[this.properties.func];if(f){var e=b[0].type,h=f.return_type;"T"==h&&(h=e);for(var l=[],d=0;d< -f.params.length;++d){var w=f.params[d],t=b[d].name;null==t&&(t="(1.0)",b[d].type="float");if("T"==w.type&&b[d].type!=e||"T"!=w.type&&b[d].type!=e)t=K(b[d].name,b[d].type,e);l.push(t)}a.addCode("code",h+" "+c+" = "+f.func+"("+l.join(",")+");",this.shader_destination);this.setOutputData(0,h)}}}};m("math/func",H);w.title="Snippet";w.prototype.onPropertyChanged=function(a,b){this.graph._version++;"type"==a&&this.outputs[0].type!=b&&(this.disconnectOutput(0),this.outputs[0].type=b)};w.prototype.getTitle= -function(){return this.flags.collapsed?this.properties.code:"Snippet"};w.prototype.onGetCode=function(a){if(this.isOutputConnected(0)){var b=k(this,0);b||(b="1.0");var d=k(this,1);d||(d="1.0");var c=y(this,0);if(c){var f=this.getInputData(0)||"float",g=this.getInputData(1)||"float",e=this.properties.type;if("T"==f||"T"==g)return null;var h="funcSnippet"+this.id,f="\n"+e+" "+h+"( "+f+" A, "+g+" B) {\n"+("\t"+e+" C = "+e+"(0.0);\n"),f=f+("\t"+this.properties.code+";\n"),f=f+"\treturn C;\n}\n";a.addCode("functions", -f,this.shader_destination);a.addCode("code",e+" "+c+" = "+h+"("+b+","+d+");",this.shader_destination);this.setOutputData(0,e)}}};m("utils/snippet",w);t.title="Remap";t.prototype.onPropertyChanged=function(){this.graph._version++};t.prototype.onConnectionsChange=function(){var a=this.getInputDataType(0);this.outputs[0].type=a||"T"};t.prototype.onGetCode=function(a){if(this.isOutputConnected(0)){var b=k(this,0),d=y(this,0);if(b||d){var c=this.getInputDataType(0);this.outputs[0].type=c;if("T"==c)console.warn("node type is T and cannot be resolved"); -else if(b){var f=A(this.properties.min_value),g=A(this.properties.max_value),e=A(this.properties.min_value2),h=A(this.properties.max_value2);a.addCode("code",c+" "+d+" = ( ("+b+" - "+f+") / ("+g+" - "+f+") ) * ("+h+" - "+e+") + "+e+";",this.shader_destination);this.setOutputData(0,c)}else a.addCode("code","\t"+c+" "+d+" = "+c+"(0.0);\n")}}};m("math/remap",t)}})(this); -(function(C){function c(){return 1E5*Math.random()|0}function m(){this.addInput("obj","");this.addInput("radius","number");this.addOutput("out","geometry");this.addOutput("points","[vec3]");this.properties={radius:1,num_points:4096,generate_normals:!0,regular:!1,mode:m.SPHERE,force_update:!1};this.points=new Float32Array(3*this.properties.num_points);this.normals=new Float32Array(3*this.properties.num_points);this.must_update=!0;this.version=0;var a=this;this.addWidget("button","update",null,function(){a.must_update= -!0});this.geometry={vertices:null,_id:c()};this._last_radius=this._old_obj=null}function n(a,d){var c=a.length,g=0,e=0,h=c;if(0==c)return-1;if(1==c)return 0;for(;h>=g;){e=0.5*(h+g)|0;c=a[e];if(c==d)break;if(g==h-1)return g;ca&&(a=1);this.points&&this.points.length==3*a||(this.points=new Float32Array(3*a));this.properties.generate_normals?this.normals&&this.normals.length==this.points.length||(this.normals=new Float32Array(this.points.length)):this.normals=null;var d=this._last_radius||this.properties.radius,c=this.properties.mode,g=this.getInputData(0);this._old_obj_version=g?g._version:null;this.points=m.generatePoints(d,a,c,this.points, -this.normals,this.properties.regular,g);this.version++};m.generatePoints=function(a,d,c,g,e,h,l){var k=3*d;g&&g.length==k||(g=new Float32Array(k));var w=new Float32Array(3),t=new Float32Array([0,1,0]);if(h)if(c==m.RECTANGLE){k=Math.floor(Math.sqrt(d));for(d=0;da.width?b: +a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad(),l=null,h=this._uniforms;d?(l=f._shader_tex,l||(l=f._shader_tex=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,f.pixel_shader,{MIX_TEX:""}))):(l=f._shader_factor,l||(l=f._shader_factor=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,f.pixel_shader)),g=null==g?this.properties.factor:g,h.u_mix.set([g,g,g,g]));var k=this.properties.invert;this._tex.drawTo(function(){a.bind(k?1:0);b.bind(k?0:1);d&&d.bind(2); +l.uniforms(h).draw(e)});this.setOutputData(0,this._tex)}}},f.prototype.onGetInputs=function(){return[["factor","number"]]},f.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform sampler2D u_textureB;\n\t\t#ifdef MIX_TEX\n\t\t\tuniform sampler2D u_textureMix;\n\t\t#else\n\t\t\tuniform vec4 u_mix;\n\t\t#endif\n\t\t\n\t\tvoid main() {\n\t\t\t#ifdef MIX_TEX\n\t\t\t vec4 f = texture2D(u_textureMix, v_coord);\n\t\t\t#else\n\t\t\t vec4 f = u_mix;\n\t\t\t#endif\n\t\t gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\t\t}\n\t\t", +J.registerNodeType("texture/mix",f),g.title="Edges",g.desc="Detects edges",g.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},g.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else if(a){this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var b=Mesh.getScreenQuad(),d=g._shader,f=this.properties.invert,e=this.properties.factor, +l=this.properties.threshold?1:0;this._tex.drawTo(function(){a.bind(0);d.uniforms({u_texture:0,u_isize:[1/a.width,1/a.height],u_factor:e,u_threshold:l,u_invert:f?1:0}).draw(b)});this.setOutputData(0,this._tex)}}},g.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_isize;\n\t\tuniform int u_invert;\n\t\tuniform float u_factor;\n\t\tuniform float u_threshold;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 center = texture2D(u_texture, v_coord);\n\t\t\tvec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\t\t\tvec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\t\t\tvec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\t\t\tvec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\t\t\tvec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\t\t\tdiff *= u_factor;\n\t\t\tif(u_invert == 1)\n\t\t\t\tdiff.xyz = vec3(1.0) - diff.xyz;\n\t\t\tif( u_threshold == 0.0 )\n\t\t\t\tgl_FragColor = vec4( diff.xyz, center.a );\n\t\t\telse\n\t\t\t\tgl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\t\t}\n\t\t", +J.registerNodeType("texture/edges",g),t.title="Depth Range",t.desc="Generates a texture with a depth range",t.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a){var b=gl.UNSIGNED_BYTE;this.properties.high_precision&&(b=gl.half_float_ext?gl.HALF_FLOAT_OES:gl.FLOAT);this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&&this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGBA, +filter:gl.LINEAR}));var d=this._uniforms,b=this.properties.distance;this.isInputConnected(1)&&(b=this.getInputData(1),this.properties.distance=b);var c=this.properties.range;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.range=c);d.u_distance=b;d.u_range=c;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var f=Mesh.getScreenQuad();t._shader||(t._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,t.pixel_shader),t._shader_onlydepth=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,t.pixel_shader, +{ONLY_DEPTH:""}));var g=this.properties.only_depth?t._shader_onlydepth:t._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1,1E3];d.u_camera_planes=b;this._temp_texture.drawTo(function(){a.bind(0);g.uniforms(d).draw(f)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},t.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform float u_distance;\n\t\tuniform float u_range;\n\t\t\n\t\tfloat LinearDepth()\n\t\t{\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\t\t\tdepth = depth * 2.0 - 1.0;\n\t\t\treturn zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t\tfloat depth = LinearDepth();\n\t\t\t#ifdef ONLY_DEPTH\n\t\t\t gl_FragColor = vec4(depth);\n\t\t\t#else\n\t\t\t\tfloat diff = abs(depth * u_camera_planes.y - u_distance);\n\t\t\t\tfloat dof = 1.0;\n\t\t\t\tif(diff <= u_range)\n\t\t\t\t\tdof = diff / u_range;\n\t\t\t gl_FragColor = vec4(dof);\n\t\t\t#endif\n\t\t}\n\t\t", +J.registerNodeType("texture/depth_range",t),z.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},z.title="Linear Depth",z.desc="Creates a color texture with linear depth",z.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a&&(a.format==gl.DEPTH_COMPONENT||a.format==gl.DEPTH_STENCIL)){var b=this.properties.precision==c.HIGH?gl.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&& +this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGB,filter:gl.LINEAR}));var d=this._uniforms;d.u_invert=this.properties.invert?1:0;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var f=Mesh.getScreenQuad();z._shader||(z._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,z.pixel_shader));var g=z._shader,b=null,b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[0.1, +1E3];d.u_camera_planes=b;d.u_ires.set([0,0]);this._temp_texture.drawTo(function(){a.bind(0);g.uniforms(d).draw(f)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},z.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform int u_invert;\n\t\tuniform vec2 u_ires;\n\t\t\n\t\tvoid main() {\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord + u_ires*0.5).x * 2.0 - 1.0;\n\t\t\tfloat f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t\tif( u_invert == 1 )\n\t\t\t\tf = 1.0 - f;\n\t\t\tgl_FragColor = vec4(vec3(f),1.0);\n\t\t}\n\t\t", +J.registerNodeType("texture/linear_depth",z),O.title="Blur",O.desc="Blur a texture",O.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},O.max_iterations=20,O.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._final_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(b=this._final_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));var d=this.properties.iterations;this.isInputConnected(1)&& +(d=this.getInputData(1),this.properties.iterations=d);d=Math.min(Math.floor(d),O.max_iterations);if(0==d)this.setOutputData(0,a);else{var c=this.properties.intensity;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.intensity=c);var f=J.camera_aspect;f||void 0===window.gl||(f=gl.canvas.height/gl.canvas.width);f||(f=1);var f=this.properties.preserve_aspect?f:1,g=this.properties.scale||[1,1];a.applyBlur(f*g[0],g[1],c,b);for(a=1;a>=1;1<(d|0)&&(d>>=1);if(2>b)break;k=l[E]=GL.Texture.getTemporary(b,d,f);m[0]=1/q.width;m[1]=1/q.height;q.blit(k,h.uniforms(e));q=k}this.isOutputConnected(2)&&(b=this._average_texture,b&&b.type==a.type&&b.format==a.format||(b=this._average_texture=new GL.Texture(1,1,{type:a.type,format:a.format,filter:gl.LINEAR})),m[0]=1/q.width,m[1]=1/q.height,e.u_intensity=t,e.u_delta=1,q.blit(b,h.uniforms(e)),this.setOutputData(2,b));gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE);e.u_intensity= +this.getInputOrProperty("persistence");e.u_delta=0.5;for(E-=2;0<=E;E--)k=l[E],l[E]=null,m[0]=1/q.width,m[1]=1/q.height,q.blit(k,h.uniforms(e)),GL.Texture.releaseTemporary(q),q=k;gl.disable(gl.BLEND);this.isOutputConnected(1)&&(l=this._glow_texture,l&&l.width==a.width&&l.height==a.height&&l.type==g&&l.format==a.format||(l=this._glow_texture=new GL.Texture(a.width,a.height,{type:g,format:a.format,filter:gl.LINEAR})),q.blit(l),this.setOutputData(1,l));if(this.isOutputConnected(0)){l=this._final_texture; +l&&l.width==a.width&&l.height==a.height&&l.type==g&&l.format==a.format||(l=this._final_texture=new GL.Texture(a.width,a.height,{type:g,format:a.format,filter:gl.LINEAR}));var A=this.getInputData(1),n=this.getInputOrProperty("dirt_factor");e.u_intensity=t;h=A?L._dirt_final_shader:L._final_shader;h||(h=A?L._dirt_final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,L.final_pixel_shader,{USE_DIRT:""}):L._final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,L.final_pixel_shader));l.drawTo(function(){a.bind(0); +q.bind(1);A&&(h.setUniform("u_dirt_factor",n),h.setUniform("u_dirt_texture",A.bind(2)));h.toViewport(e)});this.setOutputData(0,l)}GL.Texture.releaseTemporary(q)}},L.cut_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform float u_threshold;\n\tvoid main() {\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\t}",L.scale_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\t}", +L.final_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform sampler2D u_glow_texture;\n\t#ifdef USE_DIRT\n\t\tuniform sampler2D u_dirt_texture;\n\t#endif\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\tuniform float u_dirt_factor;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tvec4 glow = sampleBox( v_coord );\n\t\t#ifdef USE_DIRT\n\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\t\t#endif\n\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\t}", +J.registerNodeType("texture/glow",L),N.title="Kuwahara Filter",N.desc="Filters a texture giving an artistic oil canvas painting",N.max_radius=10,N._shaders=[],N.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));b=this.properties.radius;b=Math.min(Math.floor(b),N.max_radius);if(0== +b)this.setOutputData(0,a);else{var d=this.properties.intensity,c=J.camera_aspect;c||void 0===window.gl||(c=gl.canvas.height/gl.canvas.width);c||(c=1);c=this.properties.preserve_aspect?c:1;N._shaders[b]||(N._shaders[b]=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,N.pixel_shader,{RADIUS:b.toFixed(0)}));var f=N._shaders[b],g=GL.Mesh.getScreenQuad();a.bind(0);this._temp_texture.drawTo(function(){f.uniforms({u_texture:0,u_intensity:d,u_resolution:[a.width,a.height],u_iResolution:[1/a.width,1/a.height]}).draw(g)}); +this.setOutputData(0,this._temp_texture)}}},N.pixel_shader="\nprecision highp float;\nvarying vec2 v_coord;\nuniform sampler2D u_texture;\nuniform float u_intensity;\nuniform vec2 u_resolution;\nuniform vec2 u_iResolution;\n#ifndef RADIUS\n\t#define RADIUS 7\n#endif\nvoid main() {\n\n\tconst int radius = RADIUS;\n\tvec2 fragCoord = v_coord;\n\tvec2 src_size = u_iResolution;\n\tvec2 uv = v_coord;\n\tfloat n = float((radius + 1) * (radius + 1));\n\tint i;\n\tint j;\n\tvec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\tvec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\tvec3 c;\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm0 += c;\n\t\t\ts0 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm1 += c;\n\t\t\ts1 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm2 += c;\n\t\t\ts2 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm3 += c;\n\t\t\ts3 += c * c;\n\t\t}\n\t}\n\t\n\tfloat min_sigma2 = 1e+2;\n\tm0 /= n;\n\ts0 = abs(s0 / n - m0 * m0);\n\t\n\tfloat sigma2 = s0.r + s0.g + s0.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m0, 1.0);\n\t}\n\t\n\tm1 /= n;\n\ts1 = abs(s1 / n - m1 * m1);\n\t\n\tsigma2 = s1.r + s1.g + s1.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m1, 1.0);\n\t}\n\t\n\tm2 /= n;\n\ts2 = abs(s2 / n - m2 * m2);\n\t\n\tsigma2 = s2.r + s2.g + s2.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m2, 1.0);\n\t}\n\t\n\tm3 /= n;\n\ts3 = abs(s3 / n - m3 * m3);\n\t\n\tsigma2 = s3.r + s3.g + s3.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m3, 1.0);\n\t}\n}\n", +J.registerNodeType("texture/kuwahara",N),M.title="XDoG Filter",M.desc="Filters a texture giving an artistic ink style",M.max_radius=10,M._shaders=[],M.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));M._xdog_shader||(M._xdog_shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,M.xdog_pixel_shader)); +var d=M._xdog_shader,c=GL.Mesh.getScreenQuad(),f=this.properties.sigma,g=this.properties.k,e=this.properties.p,l=this.properties.epsilon,h=this.properties.phi;a.bind(0);this._temp_texture.drawTo(function(){d.uniforms({src:0,sigma:f,k:g,p:e,epsilon:l,phi:h,cvsWidth:a.width,cvsHeight:a.height}).draw(c)});this.setOutputData(0,this._temp_texture)}},M.xdog_pixel_shader="\nprecision highp float;\nuniform sampler2D src;\n\nuniform float cvsHeight;\nuniform float cvsWidth;\n\nuniform float sigma;\nuniform float k;\nuniform float p;\nuniform float epsilon;\nuniform float phi;\nvarying vec2 v_coord;\n\nfloat cosh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat cosH = (tmp + 1.0 / tmp) / 2.0;\n\treturn cosH;\n}\n\nfloat tanh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\treturn tanH;\n}\n\nfloat sinh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat sinH = (tmp - 1.0 / tmp) / 2.0;\n\treturn sinH;\n}\n\nvoid main(void){\n\tvec3 destColor = vec3(0.0);\n\tfloat tFrag = 1.0 / cvsHeight;\n\tfloat sFrag = 1.0 / cvsWidth;\n\tvec2 Frag = vec2(sFrag,tFrag);\n\tvec2 uv = gl_FragCoord.st;\n\tfloat twoSigmaESquared = 2.0 * sigma * sigma;\n\tfloat twoSigmaRSquared = twoSigmaESquared * k * k;\n\tint halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\tconst int MAX_NUM_ITERATION = 99999;\n\tvec2 sum = vec2(0.0);\n\tvec2 norm = vec2(0.0);\n\n\tfor(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\t\tint i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\t\tint j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\t\tfloat d = length(vec2(i,j));\n\t\tvec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\t\t\t\t\t\t\texp( -d * d / twoSigmaRSquared ));\n\n\t\tvec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\t\tnorm += kernel;\n\t\tsum += kernel * L;\n\t}\n\n\tsum /= norm;\n\n\tfloat H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\tfloat edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\tdestColor = vec3(edge);\n\tgl_FragColor = vec4(destColor, 1.0);\n}", +J.registerNodeType("texture/xDoG",M),G.title="Webcam",G.desc="Webcam texture",G.is_webcam_open=!1,G.prototype.openStream=function(){function a(d){G.is_webcam_open=!1;console.log("Webcam rejected",d);b._webcam_stream=!1;b.boxcolor="red";b.trigger("stream_error")}if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this))["catch"](a);var b=this}},G.prototype.closeStream=function(){if(this._webcam_stream){var a= +this._webcam_stream.getTracks();if(a.length)for(var b=0;b=this.size[1]||!this._video||(a.save(),a.webgl?this._video_texture&&a.drawImage(this._video_texture,0,0,this.size[0],this.size[1]):a.drawImage(this._video, +0,0,this.size[0],this.size[1]),a.restore())},G.prototype.onExecute=function(){null!=this._webcam_stream||this._waiting_confirmation||this.openStream();if(this._video&&this._video.videoWidth){var a=this._video.videoWidth,b=this._video.videoHeight,d=this._video_texture;d&&d.width==a&&d.height==b||(this._video_texture=new GL.Texture(a,b,{format:gl.RGB,filter:gl.LINEAR}));this._video_texture.uploadImage(this._video);this._video_texture.version=++this.version;this.properties.texture_name&&(c.getTexturesContainer()[this.properties.texture_name]= +this._video_texture);this.setOutputData(0,this._video_texture);for(a=1;aMath.abs(d))return c[1];d=(a-c[0])/d;return c[1]*(1-d)+f[1]*d}}return 0}},E.prototype.updateCurve=function(){for(var a=this._values,b=a.length/4,d=this.properties.split_channels,c=0;cd+f.NODE_TITLE_HEIGHT&&a.drawImage(b,10,g,this.size[0]-20,this.size[1]-d-f.NODE_TITLE_HEIGHT);var g=this.size[1]-f.NODE_TITLE_HEIGHT+ +0.5;c=f.isInsideRectangle(c[0],c[1],this.pos[0],this.pos[1]+g,this.size[0],f.NODE_TITLE_HEIGHT);a.fillStyle=c?"#555":"#222";a.beginPath();this._shape==f.BOX_SHAPE?a.rect(0,g,this.size[0]+1,f.NODE_TITLE_HEIGHT):a.roundRect(0,g,this.size[0]+1,f.NODE_TITLE_HEIGHT,0,8);a.fill();a.textAlign="center";a.font="24px Arial";a.fillStyle=c?"#DDD":"#999";a.fillText("+",0.5*this.size[0],g+24)}};B.prototype.onMouseDown=function(a,b,d){b[1]>this.size[1]-f.NODE_TITLE_HEIGHT+0.5&&d.showSubgraphPropertiesDialog(this)}; +B.prototype.onDrawSubgraphBackground=function(a){};B.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:"Print Code",callback:function(){var a=b._context.computeShaderCode();console.log(a.vs_code,a.fs_code)}}]};f.registerNodeType("texture/shaderGraph",B);v.title="Uniform";v.desc="Input data for the shader";v.prototype.getTitle=function(){return this.properties.name&&this.flags.collapsed?this.properties.type+" "+this.properties.name:"Uniform"};v.prototype.onPropertyChanged=function(a, +b){this.outputs[0].name=this.properties.type+" "+this.properties.name};v.prototype.onGetCode=function(a){var b=this.properties.type;b&&("number"==b?b="float":"texture"==b&&(b="sampler2D"),-1!=t.GLSL_types.indexOf(b)&&(a.addUniform("u_"+this.properties.name,b),this.setOutputData(0,b)))};v.prototype.getOutputVarName=function(a){return"u_"+this.properties.name};n("input/uniform",v);F.title="Attribute";F.desc="Input data from mesh attribute";F.prototype.getTitle=function(){return"att. "+this.properties.name}; +F.prototype.onGetCode=function(a){var b=this.properties.type;b&&-1!=t.GLSL_types.indexOf(b)&&("number"==b&&(b="float"),"coord"!=this.properties.name&&a.addCode("varying"," varying "+b+" v_"+this.properties.name+";"),this.setOutputData(0,b))};F.prototype.getOutputVarName=function(a){return"v_"+this.properties.name};n("input/attribute",F);x.title="Sampler2D";x.desc="Reads a pixel from a texture";x.prototype.onGetCode=function(a){var b=h(this,0),d=s(this),c="vec4 "+d+" = vec4(0.0);\n";if(b)var f=h(this, +1)||a.buffer_names.uvs,c=c+(d+" = texture2D("+b+","+f+");\n");w(this,0)&&(c+="vec4 "+w(this,0)+" = "+d+";\n");w(this,1)&&(c+="vec3 "+w(this,1)+" = "+d+".xyz;\n");a.addCode("code",c,this.shader_destination);this.setOutputData(0,"vec4");this.setOutputData(1,"vec3")};n("texture/sampler2D",x);C.title="const";C.prototype.getTitle=function(){return this.flags.collapsed?M(this.properties.value,this.properties.type,2):"Const"};C.prototype.onPropertyChanged=function(a,b){"type"==a&&(this.outputs[0].type!= +b&&(this.disconnectOutput(0),this.outputs[0].type=b),this.widgets.length=1,this.updateWidgets());"value"==a&&(b.length?(this.widgets[1].value=b[1],2d;++d)b.push({name:h(this,d),type:this.getInputData(d)||"float"});var c=w(this,0);if(c){for(var f= +b[0].type,g=this.properties.operation,e=[],d=0;2>d;++d){var l=b[d].name;null==l&&(l=null!=p.value?p.value:"(1.0)",b[d].type="float");b[d].type!=f&&("float"!=b[d].type||"*"!=g&&"/"!=g)&&(l=H(l,b[d].type,f));e.push(l)}a.addCode("code",f+" "+c+" = "+e[0]+g+e[1]+";",this.shader_destination);this.setOutputData(0,f)}}};n("math/operation",I);q.title="Func";q.prototype.onPropertyChanged=function(a,b){this.graph._version++;if("func"==a){var d=L[b];if(d){for(var c=d.params.length;cd;++d)b.push({name:h(this,d),type:this.getInputData(d)||"float"});var c=w(this,0);if(c){var f=L[this.properties.func];if(f){var g=b[0].type,e=f.return_type;"T"== +e&&(e=g);for(var l=[],d=0;d=g;){e=0.5*(l+g)|0;c=a[e];if(c==d)break;if(g==l-1)return g;ca&&(a=1);this.points&&this.points.length==3*a||(this.points=new Float32Array(3*a));this.properties.generate_normals?this.normals&&this.normals.length==this.points.length||(this.normals=new Float32Array(this.points.length)):this.normals=null;var d=this._last_radius||this.properties.radius,c=this.properties.mode,g=this.getInputData(0);this._old_obj_version=g?g._version:null;this.points=n.generatePoints(d,a,c,this.points, +this.normals,this.properties.regular,g);this.version++};n.generatePoints=function(a,d,c,g,e,l,h){var k=3*d;g&&g.length==k||(g=new Float32Array(k));var q=new Float32Array(3),r=new Float32Array([0,1,0]);if(l)if(c==n.RECTANGLE){k=Math.floor(Math.sqrt(d));for(d=0;de||vh&&hl))break}this.geometry.indices=this.indices=new Uint32Array(k)}this.indices&&this.indices.length?(this.geometry.indices=this.indices,this.setOutputData(0,this.geometry)):this.setOutputData(0,null)}};v.registerNodeType("geometry/connectPoints",B);"undefined"!=typeof GL&&(G.title="to geometry",G.desc="converts a mesh to geometry",G.prototype.onExecute= -function(){var a=this.getInputData(0);if(a){if(a!=this.last_mesh){this.last_mesh=a;for(i in a.vertexBuffers)this.geometry[i]=a.vertexBuffers[i].data;a.indexBuffers.triangles&&(this.geometry.indices=a.indexBuffers.triangles.data);this.geometry._id=c();this.geometry._version=0}this.setOutputData(0,this.geometry);this.geometry&&this.setOutputData(1,this.geometry.vertices)}},v.registerNodeType("geometry/toGeometry",G),E.title="Geo to Mesh",E.prototype.updateMesh=function(a){this.mesh||(this.mesh=new GL.Mesh); +l=this.vertices;l&&this.vertices.length==a.vertices.length?l.set(a.vertices):l=this.vertices=new Float32Array(a.vertices);for(g=0;ge||vl&&lh))break}this.geometry.indices=this.indices=new Uint32Array(k)}this.indices&&this.indices.length?(this.geometry.indices=this.indices,this.setOutputData(0,this.geometry)):this.setOutputData(0,null)}};u.registerNodeType("geometry/connectPoints",F);"undefined"!=typeof GL&&(x.title="to geometry",x.desc="converts a mesh to geometry",x.prototype.onExecute= +function(){var a=this.getInputData(0);if(a){if(a!=this.last_mesh){this.last_mesh=a;for(i in a.vertexBuffers)this.geometry[i]=a.vertexBuffers[i].data;a.indexBuffers.triangles&&(this.geometry.indices=a.indexBuffers.triangles.data);this.geometry._id=c();this.geometry._version=0}this.setOutputData(0,this.geometry);this.geometry&&this.setOutputData(1,this.geometry.vertices)}},u.registerNodeType("geometry/toGeometry",x),C.title="Geo to Mesh",C.prototype.updateMesh=function(a){this.mesh||(this.mesh=new GL.Mesh); for(var d in a)if("_"!=d[0]){var c=a[d],g=GL.Mesh.common_buffers[d];if(g||"indices"==d){var g=g?g.spacing:3,e=this.mesh.vertexBuffers[d];e&&e.data.length==c.length?(e.data.set(c),e.upload(GL.DYNAMIC_DRAW)):e=new GL.Buffer("indices"==d?GL.ELEMENT_ARRAY_BUFFER:GL.ARRAY_BUFFER,c,g,GL.DYNAMIC_DRAW);this.mesh.addBuffer(d,e)}}if(this.mesh.vertexBuffers.normals&&this.mesh.vertexBuffers.normals.data.length!=this.mesh.vertexBuffers.vertices.data.length){c=new Float32Array([0,1,0]);g=new Float32Array(this.mesh.vertexBuffers.vertices.data.length); -for(d=0;d=c.NOTEON||h<=c.NOTEOFF)this.channel=e&15};Object.defineProperty(c.prototype,"velocity",{get:function(){return this.cmd==c.NOTEON?this.data[2]:-1},set:function(c){this.data[2]=c},enumerable:!0});c.notes="A A# B C C# D D# E F F# G G#".split(" ");c.note_to_index={A:0,"A#":1,B:2,C:3,"C#":4,D:5,"D#":6,E:7,F:8,"F#":9,G:10,"G#":11};Object.defineProperty(c.prototype,"note",{get:function(){return this.cmd!= c.NOTEON?-1:c.toNoteString(this.data[1],!0)},set:function(c){throw"notes cannot be assigned this way, must modify the data[1]";},enumerable:!0});Object.defineProperty(c.prototype,"octave",{get:function(){return this.cmd!=c.NOTEON?-1:Math.floor((this.data[1]-24)/12+1)},set:function(c){throw"octave cannot be assigned this way, must modify the data[1]";},enumerable:!0});c.prototype.getPitch=function(){return 440*Math.pow(2,(this.data[1]-69)/12)};c.computePitch=function(c){return 440*Math.pow(2,(c-69)/ 12)};c.prototype.getCC=function(){return this.data[1]};c.prototype.getCCValue=function(){return this.data[2]};c.prototype.getPitchBend=function(){return this.data[1]+(this.data[2]<<7)-8192};c.computePitchBend=function(c,e){return c+(e<<7)-8192};c.prototype.setCommandFromString=function(e){this.cmd=c.computeCommandFromString(e)};c.computeCommandFromString=function(e){if(!e)return 0;if(e&&e.constructor===Number)return e;e=e.toUpperCase();switch(e){case "NOTE ON":case "NOTEON":return c.NOTEON;case "NOTE OFF":case "NOTEOFF":return c.NOTEON; @@ -713,85 +726,85 @@ case "KEY PRESSURE":case "KEYPRESSURE":return c.KEYPRESSURE;case "CONTROLLER CHA l=(e-21)%12;0>l&&(l=12+l);return c.notes[l]+(h?"":a)};c.NoteStringToPitch=function(e){e=e.toUpperCase();var h=e[0],l=4;"#"==e[1]?(h+="#",2this.properties.max_value)return;this.trigger("on_midi",h)}};v.registerNodeType("midi/filter",h);D.title="MIDIEvent";D.desc="Create a MIDI Event";D.color="#243";D.prototype.onAction=function(e,h){"assign"==e?(this.properties.channel=h.channel,this.properties.cmd=h.cmd,this.properties.value1=h.data[1],this.properties.value2=h.data[2],h.cmd== -c.NOTEON?this.gate=!0:h.cmd==c.NOTEOFF&&(this.gate=!1)):(h=this.midi_event,h.channel=this.properties.channel,this.properties.cmd&&this.properties.cmd.constructor===String?h.setCommandFromString(this.properties.cmd):h.cmd=this.properties.cmd,h.data[0]=h.cmd|h.channel,h.data[1]=Number(this.properties.value1),h.data[2]=Number(this.properties.value2),this.trigger("on_midi",h))};D.prototype.onExecute=function(){var e=this.properties;if(this.inputs)for(var h=0;hthis.properties.max_value)return;this.trigger("on_midi",h)}};u.registerNodeType("midi/filter",k);B.title="MIDIEvent";B.desc="Create a MIDI Event";B.color="#243";B.prototype.onAction=function(e,h){"assign"==e?(this.properties.channel=h.channel,this.properties.cmd=h.cmd,this.properties.value1=h.data[1],this.properties.value2=h.data[2],h.cmd== +c.NOTEON?this.gate=!0:h.cmd==c.NOTEOFF&&(this.gate=!1)):(h=this.midi_event,h.channel=this.properties.channel,this.properties.cmd&&this.properties.cmd.constructor===String?h.setCommandFromString(this.properties.cmd):h.cmd=this.properties.cmd,h.data[0]=h.cmd|h.channel,h.data[1]=Number(this.properties.value1),h.data[2]=Number(this.properties.value2),this.trigger("on_midi",h))};B.prototype.onExecute=function(){var e=this.properties;if(this.inputs)for(var h=0;hc;++c)this.valid_notes[c]=-1!=this.notes_pitches.indexOf(c);for(c=0;12>c;++c)if(this.valid_notes[c])this.offset_notes[c]=0;else for(var e=1;12>e;++e){if(this.valid_notes[(c-e)%12]){this.offset_notes[c]=-e;break}if(this.valid_notes[(c+e)%12]){this.offset_notes[c]=e;break}}};E.prototype.onAction=function(e,h){h&&h.constructor===c&&(h.data[0]==c.NOTEON||h.data[0]==c.NOTEOFF?(this.midi_event=new c,this.midi_event.setup(h.data),this.midi_event.data[1]+=this.offset_notes[c.note_to_index[h.note]], -this.trigger("out",this.midi_event)):this.trigger("out",h))};E.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&c!=this._current_scale&&this.processScale(c)};v.registerNodeType("midi/quantize",E);e.title="MIDI fromFile";e.desc="Plays a MIDI file";e.color="#243";e.prototype.onAction=function(c){"play"==c?this.play():"pause"==c&&(this._playing=!this._playing)};e.prototype.onPropertyChanged=function(c,e){"url"==c&&this.loadMIDIFile(e)};e.prototype.onExecute=function(){if(this._midi&& +l)}};B.prototype.onPropertyChanged=function(e,h){"cmd"==e&&(this.properties.cmd=c.computeCommandFromString(h))};B.prototype.onGetInputs=function(){return[["cmd","number"],["note","number"],["value1","number"],["value2","number"]]};B.prototype.onGetOutputs=function(){return[["midi","midi"],["on_midi",u.EVENT],["command","number"],["note","number"],["velocity","number"],["cc","number"],["cc_value","number"],["pitch","number"],["gate","bool"],["pitchbend","number"]]};u.registerNodeType("midi/event", +B);v.title="MIDICC";v.desc="gets a Controller Change";v.color="#243";v.prototype.onExecute=function(){n.input&&(this.properties.value=n.input.state.cc[this.properties.cc]);this.setOutputData(0,this.properties.value)};u.registerNodeType("midi/cc",v);F.title="MIDI Generator";F.desc="Generates a random MIDI note";F.color="#243";F.processScale=function(e){e=e.split(",");for(var h=0;hc;++c)this.valid_notes[c]=-1!=this.notes_pitches.indexOf(c);for(c=0;12>c;++c)if(this.valid_notes[c])this.offset_notes[c]=0;else for(var e=1;12>e;++e){if(this.valid_notes[(c-e)%12]){this.offset_notes[c]=-e;break}if(this.valid_notes[(c+e)%12]){this.offset_notes[c]=e;break}}};C.prototype.onAction=function(e,h){h&&h.constructor===c&&(h.data[0]==c.NOTEON||h.data[0]==c.NOTEOFF?(this.midi_event=new c,this.midi_event.setup(h.data),this.midi_event.data[1]+=this.offset_notes[c.note_to_index[h.note]], +this.trigger("out",this.midi_event)):this.trigger("out",h))};C.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&c!=this._current_scale&&this.processScale(c)};u.registerNodeType("midi/quantize",C);e.title="MIDI fromFile";e.desc="Plays a MIDI file";e.color="#243";e.prototype.onAction=function(c){"play"==c?this.play():"pause"==c&&(this._playing=!this._playing)};e.prototype.onPropertyChanged=function(c,e){"url"==c&&this.loadMIDIFile(e)};e.prototype.onExecute=function(){if(this._midi&& this._playing){this._current_time+=this.graph.elapsed_time;for(var e=100*this._current_time,h=0;hb;b++)for(var d=0;df+g||c[1]>d))return b}}return-1};F.prototype.onAction=function(e,h){if("reset"==e)for(var l=0;lh[1])){var l=this.getKeyIndex(h);this.keys[l]=!0;this._last_key=l;var l=12*(this.properties.start_octave-1)+29+l,a=new c;a.setup([c.NOTEON,l,100]);this.trigger("note",a);return!0}};F.prototype.onMouseMove=function(e,h){if(!(0>h[1]||-1==this._last_key)){this.setDirtyCanvas(!0); -var l=this.getKeyIndex(h);if(this._last_key==l)return!0;this.keys[this._last_key]=!1;var a=12*(this.properties.start_octave-1)+29+this._last_key,b=new c;b.setup([c.NOTEOFF,a,100]);this.trigger("note",b);this.keys[l]=!0;a=12*(this.properties.start_octave-1)+29+l;b=new c;b.setup([c.NOTEON,a,100]);this.trigger("note",b);this._last_key=l;return!0}};F.prototype.onMouseUp=function(e,h){if(!(0>h[1])){var l=this.getKeyIndex(h);this.keys[l]=!1;this._last_key=-1;var l=12*(this.properties.start_octave-1)+29+ -l,a=new c;a.setup([c.NOTEOFF,l,100]);this.trigger("note",a);return!0}};v.registerNodeType("midi/keys",F)})(this); -(function(C){function c(){this.properties={src:"",gain:0.5,loop:!0,autoplay:!0,playbackRate:1};this._loading_audio=!1;this._audiobuffer=null;this._audionodes=[];this._last_sourcenode=null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=t.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain;this.properties.src&&this.loadSound(this.properties.src)}function m(){this.properties={gain:0.5};this._audionodes=[];this._media_stream= -null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=t.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain}function n(){this.properties={fftSize:2048,minDecibels:-100,maxDecibels:-10,smoothingTimeConstant:0.5};this.audionode=t.getAudioContext().createAnalyser();this.audionode.graphnode=this;this.audionode.fftSize=this.properties.fftSize;this.audionode.minDecibels=this.properties.minDecibels;this.audionode.maxDecibels= -this.properties.maxDecibels;this.audionode.smoothingTimeConstant=this.properties.smoothingTimeConstant;this.addInput("in","audio");this.addOutput("freqs","array");this.addOutput("samples","array");this._time_bin=this._freq_bin=null}function k(){this.properties={gain:1};this.audionode=t.getAudioContext().createGain();this.addInput("in","audio");this.addInput("gain","number");this.addOutput("out","audio")}function y(){this.properties={impulse_src:"",normalize:!0};this.audionode=t.getAudioContext().createConvolver(); -this.addInput("in","audio");this.addOutput("out","audio")}function h(){this.properties={threshold:-50,knee:40,ratio:12,reduction:-20,attack:0,release:0.25};this.audionode=t.getAudioContext().createDynamicsCompressor();this.addInput("in","audio");this.addOutput("out","audio")}function D(){this.properties={};this.audionode=t.getAudioContext().createWaveShaper();this.addInput("in","audio");this.addInput("shape","waveshape");this.addOutput("out","audio")}function x(){this.properties={gain1:0.5,gain2:0.5}; -this.audionode=t.getAudioContext().createGain();this.audionode1=t.getAudioContext().createGain();this.audionode1.gain.value=this.properties.gain1;this.audionode2=t.getAudioContext().createGain();this.audionode2.gain.value=this.properties.gain2;this.audionode1.connect(this.audionode);this.audionode2.connect(this.audionode);this.addInput("in1","audio");this.addInput("in1 gain","number");this.addInput("in2","audio");this.addInput("in2 gain","number");this.addOutput("out","audio")}function B(){this.properties= -{A:0.1,D:0.1,S:0.1,R:0.1};this.audionode=t.getAudioContext().createGain();this.audionode.gain.value=0;this.addInput("in","audio");this.addInput("gate","bool");this.addOutput("out","audio");this.gate=!1}function G(){this.properties={delayTime:0.5};this.audionode=t.getAudioContext().createDelay(10);this.audionode.delayTime.value=this.properties.delayTime;this.addInput("in","audio");this.addInput("time","number");this.addOutput("out","audio")}function E(){this.properties={frequency:350,detune:0,Q:1}; -this.addProperty("type","lowpass","enum",{values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")});this.audionode=t.getAudioContext().createBiquadFilter();this.addInput("in","audio");this.addOutput("out","audio")}function e(){this.properties={frequency:440,detune:0,type:"sine"};this.addProperty("type","sine","enum",{values:["sine","square","sawtooth","triangle","custom"]});this.audionode=t.getAudioContext().createOscillator();this.addOutput("out","audio")}function p(){this.properties= -{continuous:!0,mark:-1};this.addInput("data","array");this.addInput("mark","number");this.size=[300,200];this._last_buffer=null}function F(){this.properties={band:440,amplitude:1};this.addInput("freqs","array");this.addOutput("signal","number")}function v(){if(!v.default_code){var c=v.default_function.toString(),a=c.indexOf("{")+1,b=c.lastIndexOf("}");v.default_code=c.substr(a,b-a)}this.properties={code:v.default_code};c=t.getAudioContext();c.createScriptProcessor?this.audionode=c.createScriptProcessor(4096, -1,1):(console.warn("ScriptProcessorNode deprecated"),this.audionode=c.createGain());this.processCode();v._bypass_function||(v._bypass_function=this.audionode.onaudioprocess);this.addInput("in","audio");this.addOutput("out","audio")}function H(){this.audionode=t.getAudioContext().destination;this.addInput("in","audio")}var w=C.LiteGraph,t={};C.LGAudio=t;t.getAudioContext=function(){if(!this._audio_context){window.AudioContext=window.AudioContext||window.webkitAudioContext;if(!window.AudioContext)return console.error("AudioContext not supported by browser"), -null;this._audio_context=new AudioContext;this._audio_context.onmessage=function(c){console.log("msg",c)};this._audio_context.onended=function(c){console.log("ended",c)};this._audio_context.oncomplete=function(c){console.log("complete",c)}}return this._audio_context};t.connect=function(c,a){try{c.connect(a)}catch(b){console.warn("LGraphAudio:",b)}};t.disconnect=function(c,a){try{c.disconnect(a)}catch(b){console.warn("LGraphAudio:",b)}};t.changeAllAudiosConnections=function(c,a){if(c.inputs)for(var b= -0;bb;b++)for(var d=0;df+g||c[1]>d))return b}}return-1};D.prototype.onAction=function(e,h){if("reset"==e)for(var l=0;lh[1])){var l=this.getKeyIndex(h);this.keys[l]=!0;this._last_key=l;var l=12*(this.properties.start_octave-1)+29+l,a=new c;a.setup([c.NOTEON,l,100]);this.trigger("note",a);return!0}};D.prototype.onMouseMove=function(e,h){if(!(0>h[1]||-1==this._last_key)){this.setDirtyCanvas(!0); +var l=this.getKeyIndex(h);if(this._last_key==l)return!0;this.keys[this._last_key]=!1;var a=12*(this.properties.start_octave-1)+29+this._last_key,b=new c;b.setup([c.NOTEOFF,a,100]);this.trigger("note",b);this.keys[l]=!0;a=12*(this.properties.start_octave-1)+29+l;b=new c;b.setup([c.NOTEON,a,100]);this.trigger("note",b);this._last_key=l;return!0}};D.prototype.onMouseUp=function(e,h){if(!(0>h[1])){var l=this.getKeyIndex(h);this.keys[l]=!1;this._last_key=-1;var l=12*(this.properties.start_octave-1)+29+ +l,a=new c;a.setup([c.NOTEOFF,l,100]);this.trigger("note",a);return!0}};u.registerNodeType("midi/keys",D)})(this); +(function(y){function c(){this.properties={src:"",gain:0.5,loop:!0,autoplay:!0,playbackRate:1};this._loading_audio=!1;this._audiobuffer=null;this._audionodes=[];this._last_sourcenode=null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=r.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain;this.properties.src&&this.loadSound(this.properties.src)}function n(){this.properties={gain:0.5};this._audionodes=[];this._media_stream= +null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=r.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain}function s(){this.properties={fftSize:2048,minDecibels:-100,maxDecibels:-10,smoothingTimeConstant:0.5};this.audionode=r.getAudioContext().createAnalyser();this.audionode.graphnode=this;this.audionode.fftSize=this.properties.fftSize;this.audionode.minDecibels=this.properties.minDecibels;this.audionode.maxDecibels= +this.properties.maxDecibels;this.audionode.smoothingTimeConstant=this.properties.smoothingTimeConstant;this.addInput("in","audio");this.addOutput("freqs","array");this.addOutput("samples","array");this._time_bin=this._freq_bin=null}function h(){this.properties={gain:1};this.audionode=r.getAudioContext().createGain();this.addInput("in","audio");this.addInput("gain","number");this.addOutput("out","audio")}function w(){this.properties={impulse_src:"",normalize:!0};this.audionode=r.getAudioContext().createConvolver(); +this.addInput("in","audio");this.addOutput("out","audio")}function k(){this.properties={threshold:-50,knee:40,ratio:12,reduction:-20,attack:0,release:0.25};this.audionode=r.getAudioContext().createDynamicsCompressor();this.addInput("in","audio");this.addOutput("out","audio")}function B(){this.properties={};this.audionode=r.getAudioContext().createWaveShaper();this.addInput("in","audio");this.addInput("shape","waveshape");this.addOutput("out","audio")}function v(){this.properties={gain1:0.5,gain2:0.5}; +this.audionode=r.getAudioContext().createGain();this.audionode1=r.getAudioContext().createGain();this.audionode1.gain.value=this.properties.gain1;this.audionode2=r.getAudioContext().createGain();this.audionode2.gain.value=this.properties.gain2;this.audionode1.connect(this.audionode);this.audionode2.connect(this.audionode);this.addInput("in1","audio");this.addInput("in1 gain","number");this.addInput("in2","audio");this.addInput("in2 gain","number");this.addOutput("out","audio")}function F(){this.properties= +{A:0.1,D:0.1,S:0.1,R:0.1};this.audionode=r.getAudioContext().createGain();this.audionode.gain.value=0;this.addInput("in","audio");this.addInput("gate","bool");this.addOutput("out","audio");this.gate=!1}function x(){this.properties={delayTime:0.5};this.audionode=r.getAudioContext().createDelay(10);this.audionode.delayTime.value=this.properties.delayTime;this.addInput("in","audio");this.addInput("time","number");this.addOutput("out","audio")}function C(){this.properties={frequency:350,detune:0,Q:1}; +this.addProperty("type","lowpass","enum",{values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")});this.audionode=r.getAudioContext().createBiquadFilter();this.addInput("in","audio");this.addOutput("out","audio")}function e(){this.properties={frequency:440,detune:0,type:"sine"};this.addProperty("type","sine","enum",{values:["sine","square","sawtooth","triangle","custom"]});this.audionode=r.getAudioContext().createOscillator();this.addOutput("out","audio")}function m(){this.properties= +{continuous:!0,mark:-1};this.addInput("data","array");this.addInput("mark","number");this.size=[300,200];this._last_buffer=null}function D(){this.properties={band:440,amplitude:1};this.addInput("freqs","array");this.addOutput("signal","number")}function u(){if(!u.default_code){var c=u.default_function.toString(),a=c.indexOf("{")+1,b=c.lastIndexOf("}");u.default_code=c.substr(a,b-a)}this.properties={code:u.default_code};c=r.getAudioContext();c.createScriptProcessor?this.audionode=c.createScriptProcessor(4096, +1,1):(console.warn("ScriptProcessorNode deprecated"),this.audionode=c.createGain());this.processCode();u._bypass_function||(u._bypass_function=this.audionode.onaudioprocess);this.addInput("in","audio");this.addOutput("out","audio")}function I(){this.audionode=r.getAudioContext().destination;this.addInput("in","audio")}var q=y.LiteGraph,r={};y.LGAudio=r;r.getAudioContext=function(){if(!this._audio_context){window.AudioContext=window.AudioContext||window.webkitAudioContext;if(!window.AudioContext)return console.error("AudioContext not supported by browser"), +null;this._audio_context=new AudioContext;this._audio_context.onmessage=function(c){console.log("msg",c)};this._audio_context.onended=function(c){console.log("ended",c)};this._audio_context.oncomplete=function(c){console.log("complete",c)}}return this._audio_context};r.connect=function(c,a){try{c.connect(a)}catch(b){console.warn("LGraphAudio:",b)}};r.disconnect=function(c,a){try{c.disconnect(a)}catch(b){console.warn("LGraphAudio:",b)}};r.changeAllAudiosConnections=function(c,a){if(c.inputs)for(var b= +0;b=this.size[0]&&(e=this.size[0]-1),c.strokeStyle="red",c.beginPath(),c.moveTo(e,d),c.lineTo(e,0),c.stroke())}};p.title="Visualization";p.desc="Audio Visualization";w.registerNodeType("audio/visualization",p);F.prototype.onExecute=function(){if(this._freqs=this.getInputData(0)){var c=this.properties.band,a=this.getInputData(1); -void 0!==a&&(c=a);a=t.getAudioContext().sampleRate/this._freqs.length;a=c/a*2;a>=this._freqs.length?a=this._freqs[this._freqs.length-1]:(c=a|0,a-=c,a=this._freqs[c]*(1-a)+this._freqs[c+1]*a);this.setOutputData(0,a/255*this.properties.amplitude)}};F.prototype.onGetInputs=function(){return[["band","number"]]};F.title="Signal";F.desc="extract the signal of some frequency";w.registerNodeType("audio/signal",F);v.prototype.onAdded=function(c){c.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess= -this._callback)};v["@code"]={widget:"code",type:"code"};v.prototype.onStart=function(){this.audionode.onaudioprocess=this._callback};v.prototype.onStop=function(){this.audionode.onaudioprocess=v._bypass_function};v.prototype.onPause=function(){this.audionode.onaudioprocess=v._bypass_function};v.prototype.onUnpause=function(){this.audionode.onaudioprocess=this._callback};v.prototype.onExecute=function(){};v.prototype.onRemoved=function(){this.audionode.onaudioprocess=v._bypass_function};v.prototype.processCode= -function(){try{this._script=new new Function("properties",this.properties.code)(this.properties),this._old_code=this.properties.code,this._callback=this._script.onaudioprocess}catch(c){console.error("Error in onaudioprocess code",c),this._callback=v._bypass_function,this.audionode.onaudioprocess=this._callback}};v.prototype.onPropertyChanged=function(c,a){"code"==c&&(this.properties.code=a,this.processCode(),this.graph&&this.graph.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback))}; -v.default_function=function(){this.onaudioprocess=function(c){var a=c.inputBuffer;c=c.outputBuffer;for(var b=0;b=this.size[0]&&(f=this.size[0]-1),c.strokeStyle="red",c.beginPath(),c.moveTo(f,d),c.lineTo(f,0),c.stroke())}};m.title="Visualization";m.desc="Audio Visualization";q.registerNodeType("audio/visualization",m);D.prototype.onExecute=function(){if(this._freqs=this.getInputData(0)){var c=this.properties.band,a=this.getInputData(1); +void 0!==a&&(c=a);a=r.getAudioContext().sampleRate/this._freqs.length;a=c/a*2;a>=this._freqs.length?a=this._freqs[this._freqs.length-1]:(c=a|0,a-=c,a=this._freqs[c]*(1-a)+this._freqs[c+1]*a);this.setOutputData(0,a/255*this.properties.amplitude)}};D.prototype.onGetInputs=function(){return[["band","number"]]};D.title="Signal";D.desc="extract the signal of some frequency";q.registerNodeType("audio/signal",D);u.prototype.onAdded=function(c){c.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess= +this._callback)};u["@code"]={widget:"code",type:"code"};u.prototype.onStart=function(){this.audionode.onaudioprocess=this._callback};u.prototype.onStop=function(){this.audionode.onaudioprocess=u._bypass_function};u.prototype.onPause=function(){this.audionode.onaudioprocess=u._bypass_function};u.prototype.onUnpause=function(){this.audionode.onaudioprocess=this._callback};u.prototype.onExecute=function(){};u.prototype.onRemoved=function(){this.audionode.onaudioprocess=u._bypass_function};u.prototype.processCode= +function(){try{this._script=new new Function("properties",this.properties.code)(this.properties),this._old_code=this.properties.code,this._callback=this._script.onaudioprocess}catch(c){console.error("Error in onaudioprocess code",c),this._callback=u._bypass_function,this.audionode.onaudioprocess=this._callback}};u.prototype.onPropertyChanged=function(c,a){"code"==c&&(this.properties.code=a,this.processCode(),this.graph&&this.graph.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback))}; +u.default_function=function(){this.onaudioprocess=function(c){var a=c.inputBuffer;c=c.outputBuffer;for(var b=0;b 2) + this.widgets[2].value = value[2]; + if(value.length > 3) + this.widgets[3].value = value[3]; } } } @@ -761,21 +914,21 @@ gl_FragColor = color;\n\ break; case 'vec2': this.properties.value = old_value && old_value.length == 2 ? [old_value[0],old_value[1]] : [0,0,0]; - this.addWidget("number","x",0,options, function(v){ that.properties.value[0] = v; }); - this.addWidget("number","y",0,options, function(v){ that.properties.value[1] = v; }); + this.addWidget("number","x",this.properties.value[0], function(v){ that.properties.value[0] = v; },options); + this.addWidget("number","y",this.properties.value[1], function(v){ that.properties.value[1] = v; },options); break; case 'vec3': this.properties.value = old_value && old_value.length == 3 ? [old_value[0],old_value[1],old_value[2]] : [0,0,0]; - this.addWidget("number","x",0,options, function(v){ that.properties.value[0] = v; }); - this.addWidget("number","y",0,options, function(v){ that.properties.value[1] = v; }); - this.addWidget("number","z",0,options, function(v){ that.properties.value[2] = v; }); + this.addWidget("number","x",this.properties.value[0], function(v){ that.properties.value[0] = v; },options); + this.addWidget("number","y",this.properties.value[1], function(v){ that.properties.value[1] = v; },options); + this.addWidget("number","z",this.properties.value[2], function(v){ that.properties.value[2] = v; },options); break; case 'vec4': this.properties.value = old_value && old_value.length == 4 ? [old_value[0],old_value[1],old_value[2],old_value[3]] : [0,0,0,0]; - this.addWidget("number","x",0,options, function(v){ that.properties.value[0] = v; }); - this.addWidget("number","y",0,options, function(v){ that.properties.value[1] = v; }); - this.addWidget("number","z",0,options, function(v){ that.properties.value[2] = v; }); - this.addWidget("number","w",0,options, function(v){ that.properties.value[3] = v; }); + this.addWidget("number","x",this.properties.value[0], function(v){ that.properties.value[0] = v; },options); + this.addWidget("number","y",this.properties.value[1], function(v){ that.properties.value[1] = v; },options); + this.addWidget("number","z",this.properties.value[2], function(v){ that.properties.value[2] = v; },options); + this.addWidget("number","w",this.properties.value[3], function(v){ that.properties.value[3] = v; },options); break; default: console.error("unknown type for constant"); @@ -995,26 +1148,116 @@ gl_FragColor = color;\n\ var link_name = getInputLinkID( this, 0 ); if(!link_name) return; - - var code = link_name; - var type = this.getInputDataType(0); - if(type == "float") - code = "vec4(" + code + ");"; - else if(type == "vec2") - code = "vec4(" + code + ",0.0,1.0);"; - else if(type == "vec3") - code = "vec4(" + code + ",1.0);"; - - context.addCode("fs_code", "color = " + code + ";"); + var type = this.getInputData(0); + var code = varToTypeGLSL( link_name, type, "vec4" ); + context.addCode("fs_code", "fragcolor = " + code + ";"); } registerShaderNode( "output/fragcolor", LGraphShaderFragColor ); + /* + function LGraphShaderDiscard() + { + this.addInput("v","T"); + this.addInput("min","T"); + this.properties = { min_value: 0.0 }; + this.addWidget("number","min",0,{ step: 0.01, property: "min_value" }); + } + + LGraphShaderDiscard.title = "Discard"; + + LGraphShaderDiscard.prototype.onGetCode = function( context ) + { + if(!this.isOutputConnected(0)) + return; + + var inlink = getInputLinkID(this,0); + var inlink1 = getInputLinkID(this,1); + + if(!inlink && !inlink1) //not connected + return; + context.addCode("code", return_type + " " + outlink + " = ( (" + inlink + " - "+minv+") / ("+ maxv+" - "+minv+") ) * ("+ maxv2+" - "+minv2+") + " + minv2 + ";", this.shader_destination ); + this.setOutputData( 0, return_type ); + } + + registerShaderNode( "output/discard", LGraphShaderDiscard ); + */ + // ************************************************* - function LGraphShaderMath() + function LGraphShaderOperation() + { + this.addInput("A","float,vec2,vec3,vec4"); + this.addInput("B","float,vec2,vec3,vec4"); + this.addOutput("out",""); + this.properties = { + operation: "*" + }; + this.addWidget("combo","op.",this.properties.operation,{ property: "operation", values: LGraphShaderOperation.operations }); + } + + LGraphShaderOperation.title = "Operation"; + LGraphShaderOperation.operations = ["+","-","*","/"]; + + LGraphShaderOperation.prototype.getTitle = function() + { + if(this.flags.collapsed) + return "A" + this.properties.operation + "B"; + else + return "Operation"; + } + + LGraphShaderOperation.prototype.onGetCode = function( context ) + { + if(!this.isOutputConnected(0)) + return; + + var inlinks = []; + for(var i = 0; i < 3; ++i) + inlinks.push( { name: getInputLinkID(this,i), type: this.getInputData(i) || "float" } ); + + var outlink = getOutputLinkID(this,0); + if(!outlink) //not connected + return; + + //func_desc + var base_type = inlinks[0].type; + var return_type = base_type; + var op = this.properties.operation; + + var params = []; + for(var i = 0; i < 2; ++i) + { + var param_code = inlinks[i].name; + if(param_code == null) //not plugged + { + param_code = p.value != null ? p.value : "(1.0)"; + inlinks[i].type = "float"; + } + + //convert + if( inlinks[i].type != base_type ) + { + if( inlinks[i].type == "float" && (op == "*" || op == "/") ) + { + //I find hard to create the opposite condition now, so I prefeer an else + } + else + param_code = convertVarToGLSLType( param_code, inlinks[i].type, base_type ); + } + params.push( param_code ); + } + + context.addCode("code", return_type + " " + outlink + " = "+ params[0] + op + params[1] + ";", this.shader_destination ); + this.setOutputData( 0, return_type ); + } + + registerShaderNode( "math/operation", LGraphShaderOperation ); + + + function LGraphShaderFunc() { this.addInput("A","float,vec2,vec3,vec4"); this.addInput("B","float,vec2,vec3,vec4"); @@ -1026,9 +1269,9 @@ gl_FragColor = color;\n\ this.addWidget("combo","func",this.properties.func,{ property: "func", values: GLSL_functions_name }); } - LGraphShaderMath.title = "Math"; + LGraphShaderFunc.title = "Func"; - LGraphShaderMath.prototype.onPropertyChanged = function(name,value) + LGraphShaderFunc.prototype.onPropertyChanged = function(name,value) { this.graph._version++; @@ -1044,14 +1287,17 @@ gl_FragColor = color;\n\ //add and update inputs for(var i = 0; i < func_desc.params.length; ++i) + { + var p = func_desc.params[i]; if( this.inputs[i] ) - this.inputs[i].name = func_desc.params[i].name; + this.inputs[i].name = p.name + (p.value ? " (" + p.value + ")" : ""); else - this.addInput( func_desc.params[i].name, "float,vec2,vec3,vec4" ); + this.addInput( p.name, "float,vec2,vec3,vec4" ); + } } } - LGraphShaderMath.prototype.getTitle = function() + LGraphShaderFunc.prototype.getTitle = function() { if(this.flags.collapsed) return this.properties.func; @@ -1059,7 +1305,7 @@ gl_FragColor = color;\n\ return "Func"; } - LGraphShaderMath.prototype.onGetCode = function( context ) + LGraphShaderFunc.prototype.onGetCode = function( context ) { if(!this.isOutputConnected(0)) return; @@ -1089,12 +1335,12 @@ gl_FragColor = color;\n\ var param_code = inlinks[i].name; if(param_code == null) //not plugged { - param_code = "(1.0)"; + param_code = p.value != null ? p.value : "(1.0)"; inlinks[i].type = "float"; } if( (p.type == "T" && inlinks[i].type != base_type) || (p.type != "T" && inlinks[i].type != base_type) ) - param_code = convertVarToGLSLType( inlinks[i].name, inlinks[i].type, base_type ); + param_code = convertVarToGLSLType( param_code, inlinks[i].type, base_type ); params.push( param_code ); } @@ -1103,7 +1349,7 @@ gl_FragColor = color;\n\ this.setOutputData( 0, return_type ); } - registerShaderNode( "math/func", LGraphShaderMath ); + registerShaderNode( "math/func", LGraphShaderFunc ); @@ -1181,12 +1427,102 @@ gl_FragColor = color;\n\ registerShaderNode( "utils/snippet", LGraphShaderSnippet ); - //************************************ + + function LGraphShaderRand() + { + this.addOutput("out","float"); + } + + LGraphShaderRand.title = "Rand"; + + LGraphShaderRand.prototype.onGetCode = function( context ) + { + if(!this.isOutputConnected(0)) + return; + + var outlink = getOutputLinkID(this,0); + + context.addUniform( "u_rand" + this.id, "float", function(){ return Math.random(); }); + context.addCode("code", "float " + outlink + " = u_rand" + this.id +";", this.shader_destination ); + this.setOutputData( 0, "float" ); + } + + registerShaderNode( "input/rand", LGraphShaderRand ); + + //noise? + //https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83 + + + function LGraphShaderTime() + { + this.addOutput("out","float"); + } + + LGraphShaderTime.title = "Time"; + + LGraphShaderTime.prototype.onGetCode = function( context ) + { + if(!this.isOutputConnected(0)) + return; + + var outlink = getOutputLinkID(this,0); + + context.addUniform( "u_time" + this.id, "float", function(){ return getTime() * 0.001; }); + context.addCode("code", "float " + outlink + " = u_time" + this.id +";", this.shader_destination ); + this.setOutputData( 0, "float" ); + } + + registerShaderNode( "input/time", LGraphShaderTime ); + + + function LGraphShaderDither() + { + this.addInput("in","T"); + this.addOutput("out","float"); + } + + LGraphShaderDither.title = "Dither"; + + LGraphShaderDither.prototype.onGetCode = function( context ) + { + if(!this.isOutputConnected(0)) + return; + var inlink = getInputLinkID(this,0); + var return_type = "float"; + var outlink = getOutputLinkID(this,0); + var intype = this.getInputData(0); + inlink = varToTypeGLSL( inlink, intype, "float" ); + context.addFunction("dither8x8", LGraphShaderDither.dither_func); + context.addCode("code", return_type + " " + outlink + " = dither8x8("+ inlink +");", this.shader_destination ); + this.setOutputData( 0, return_type ); + } + + LGraphShaderDither.dither_values = [0.515625,0.140625,0.640625,0.046875,0.546875,0.171875,0.671875,0.765625,0.265625,0.890625,0.390625,0.796875,0.296875,0.921875,0.421875,0.203125,0.703125,0.078125,0.578125,0.234375,0.734375,0.109375,0.609375,0.953125,0.453125,0.828125,0.328125,0.984375,0.484375,0.859375,0.359375,0.0625,0.5625,0.1875,0.6875,0.03125,0.53125,0.15625,0.65625,0.8125,0.3125,0.9375,0.4375,0.78125,0.28125,0.90625,0.40625,0.25,0.75,0.125,0.625,0.21875,0.71875,0.09375,0.59375,1.0001,0.5,0.875,0.375,0.96875,0.46875,0.84375,0.34375]; + + LGraphShaderDither.dither_func = "\n\ + float dither8x8(float brightness) {\n\ + vec2 position = vec2(0.0);\n\ + #ifdef FRAGMENT\n\ + position = gl_FragCoord.xy;\n\ + #endif\n\ + int x = int(mod(position.x, 8.0));\n\ + int y = int(mod(position.y, 8.0));\n\ + int index = x + y * 8;\n\ + float limit = 0.0;\n\ + if (x < 8) {\n\ + if(index==0) limit = 0.015625;\n\ + "+(LGraphShaderDither.dither_values.map( function(v,i){ return "else if(index== "+(i+1)+") limit = " + v + ";"}).join("\n"))+"\n\ + }\n\ + return brightness < limit ? 0.0 : 1.0;\n\ + }\n", + + registerShaderNode( "math/dither", LGraphShaderDither ); + function LGraphShaderRemap() { - this.addInput("","T,float,vec2,vec3,vec4"); - this.addOutput("","T"); + this.addInput("","float,vec2,vec3,vec4"); + this.addOutput("",""); this.properties = { min_value: 0, max_value: 1, diff --git a/src/nodes/gltextures.js b/src/nodes/gltextures.js index eb3dc535d..064be4264 100755 --- a/src/nodes/gltextures.js +++ b/src/nodes/gltextures.js @@ -35,7 +35,7 @@ //flags to choose output texture type LGraphTexture.UNDEFINED = 0; //not specified - LGraphTexture.PASS_THROUGH = 1; //do not apply FX + LGraphTexture.PASS_THROUGH = 1; //do not apply FX (like disable but passing the in to the out) LGraphTexture.COPY = 2; //create new texture with the same properties as the origin texture LGraphTexture.LOW = 3; //create new texture with low precision (byte) LGraphTexture.HIGH = 4; //create new texture with high precision (half-float) @@ -116,11 +116,12 @@ !target || target.width != origin.width || target.height != origin.height || - target.type != tex_type + target.type != tex_type || + target.format != origin.format ) { target = new GL.Texture(origin.width, origin.height, { type: tex_type, - format: gl.RGBA, + format: origin.format, filter: gl.LINEAR }); } @@ -5015,18 +5016,18 @@ void main(void){\n\ LGraphTexturePerlin.widgets_info = { precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }, - width: { type: "Number", precision: 0, step: 1 }, - height: { type: "Number", precision: 0, step: 1 }, - octaves: { type: "Number", precision: 0, step: 1, min: 1, max: 50 } + width: { type: "number", precision: 0, step: 1 }, + height: { type: "number", precision: 0, step: 1 }, + octaves: { type: "number", precision: 0, step: 1, min: 1, max: 50 } }; LGraphTexturePerlin.prototype.onGetInputs = function() { return [ - ["seed", "Number"], - ["persistence", "Number"], - ["octaves", "Number"], - ["scale", "Number"], - ["amplitude", "Number"], + ["seed", "number"], + ["persistence", "number"], + ["octaves", "number"], + ["scale", "number"], + ["amplitude", "number"], ["offset", "vec2"] ]; }; @@ -5197,8 +5198,8 @@ void main(void){\n\ LGraphTextureCanvas2D.widgets_info = { precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }, code: { type: "code" }, - width: { type: "Number", precision: 0, step: 1 }, - height: { type: "Number", precision: 0, step: 1 } + width: { type: "number", precision: 0, step: 1 }, + height: { type: "number", precision: 0, step: 1 } }; LGraphTextureCanvas2D.prototype.onPropertyChanged = function( name, value ) { diff --git a/src/nodes/math.js b/src/nodes/math.js index 19e721aa8..601d8beba 100755 --- a/src/nodes/math.js +++ b/src/nodes/math.js @@ -854,6 +854,7 @@ this.addProperty("A", 1); this.addProperty("B", 1); this.addProperty("OP", ">", "enum", { values: MathCondition.values }); + this.addWidget("combo","Cond.",this.properties.OP,{ property: "OP", values: MathCondition.values } ); this.size = [80, 60]; } @@ -921,6 +922,37 @@ LiteGraph.registerNodeType("math/condition", MathCondition); + + function MathBranch() { + this.addInput("in", ""); + this.addInput("cond", "boolean"); + this.addOutput("true", ""); + this.addOutput("false", ""); + this.size = [80, 60]; + } + + MathBranch.title = "Branch"; + MathBranch.desc = "If condition is true, outputs IN in true, otherwise in false"; + + MathBranch.prototype.onExecute = function() { + var V = this.getInputData(0); + var cond = this.getInputData(1); + + if(cond) + { + this.setOutputData(0, V); + this.setOutputData(1, null); + } + else + { + this.setOutputData(0, null); + this.setOutputData(1, V); + } + } + + LiteGraph.registerNodeType("math/branch", MathBranch); + + function MathAccumulate() { this.addInput("inc", "number"); this.addOutput("total", "number"); diff --git a/utils/deploy_files.txt b/utils/deploy_files.txt index ce998b646..45163519b 100755 --- a/utils/deploy_files.txt +++ b/utils/deploy_files.txt @@ -9,7 +9,7 @@ ../src/nodes/logic.js ../src/nodes/graphics.js ../src/nodes/gltextures.js -../src/nodes/shaders.js +../src/nodes/glshaders.js ../src/nodes/geometry.js ../src/nodes/glfx.js ../src/nodes/midi.js